375 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			375 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
 | 
						|
/* lookup() is the main routine that controls the path name lookup. It  
 | 
						|
 * handles mountpoints and symbolic links. The actual lookup requests
 | 
						|
 * are sent through the req_lookup wrapper function.
 | 
						|
 *
 | 
						|
 *  Jul 2006 (Balazs Gerofi)
 | 
						|
 */
 | 
						|
 | 
						|
#include "fs.h"
 | 
						|
#include <string.h>
 | 
						|
#include <minix/callnr.h>
 | 
						|
#include <minix/com.h>
 | 
						|
#include <minix/keymap.h>
 | 
						|
#include <minix/const.h>
 | 
						|
#include <minix/endpoint.h>
 | 
						|
#include <unistd.h>
 | 
						|
 | 
						|
#include <minix/vfsif.h>
 | 
						|
#include "fproc.h"
 | 
						|
#include "vmnt.h"
 | 
						|
#include "vnode.h"
 | 
						|
#include "param.h"
 | 
						|
 | 
						|
/* Set to following define to 1 if you really want to use the POSIX definition
 | 
						|
 * (IEEE Std 1003.1, 2004) of pathname resolution. POSIX requires pathnames
 | 
						|
 * with a traling slash (and that do not entirely consist of slash characters)
 | 
						|
 * to be treated as if a single dot is appended. This means that for example
 | 
						|
 * mkdir("dir/", ...) and rmdir("dir/") will fail because the call tries to
 | 
						|
 * create or remove the directory '.'. Historically, Unix systems just ignore
 | 
						|
 * trailing slashes.
 | 
						|
 */
 | 
						|
#define DO_POSIX_PATHNAME_RES	0
 | 
						|
 | 
						|
FORWARD _PROTOTYPE( int lookup_rel, (struct vnode *start_node,
 | 
						|
		int flags, int use_realuid, node_details_t *node)	);
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *				lookup_rel_vp				     *
 | 
						|
 *===========================================================================*/
 | 
						|
PUBLIC int lookup_rel_vp(start_node, flags, use_realuid, vpp)
 | 
						|
struct vnode *start_node;
 | 
						|
int flags;
 | 
						|
int use_realuid;
 | 
						|
struct vnode **vpp;
 | 
						|
{
 | 
						|
  /* Resolve a pathname (in user_fullpath) starting at start_node to a vnode. */
 | 
						|
  int r, lookup_res;
 | 
						|
  struct vnode *new_vp, *vp;
 | 
						|
  struct vmnt *vmp;
 | 
						|
  struct node_details res;
 | 
						|
 | 
						|
  /* See if free vnode is available */
 | 
						|
  if ((new_vp = get_free_vnode(__FILE__, __LINE__)) == NIL_VNODE) {
 | 
						|
      printf("vfs:lookup_rel_vp: no free vnode available\n");
 | 
						|
      *vpp= NULL;
 | 
						|
      return EINVAL;
 | 
						|
  }
 | 
						|
 | 
						|
  lookup_res = lookup_rel(start_node, flags, use_realuid, &res);
 | 
						|
 | 
						|
  if (lookup_res != OK)
 | 
						|
  {
 | 
						|
#if 0
 | 
						|
	printf("vfs:lookup_rel_vp: lookup_rel failed with %d\n", lookup_res);
 | 
						|
#endif
 | 
						|
	return lookup_res;
 | 
						|
  }
 | 
						|
 | 
						|
  /* Check whether vnode is already in use or not */
 | 
						|
  if ((vp = find_vnode(res.fs_e, res.inode_nr)) != NIL_VNODE) {
 | 
						|
        vp->v_ref_count++;
 | 
						|
	vp->v_fs_count++;	/* We got a reference from the FS */
 | 
						|
	*vpp= vp;
 | 
						|
	return OK;
 | 
						|
  }
 | 
						|
 | 
						|
  /* Fill in the free vnode's fields */
 | 
						|
  new_vp->v_fs_e = res.fs_e;
 | 
						|
  new_vp->v_inode_nr = res.inode_nr;
 | 
						|
  new_vp->v_mode = res.fmode;
 | 
						|
  new_vp->v_size = res.fsize;
 | 
						|
  new_vp->v_uid = res.uid;
 | 
						|
  new_vp->v_gid = res.gid;
 | 
						|
  new_vp->v_sdev = res.dev;
 | 
						|
 | 
						|
  if ( (vmp = find_vmnt(new_vp->v_fs_e)) == NIL_VMNT)
 | 
						|
	panic(__FILE__, "vfs:lookup_rel_vp: vmnt not found", NO_NUM);
 | 
						|
 | 
						|
  new_vp->v_vmnt = vmp; 
 | 
						|
  new_vp->v_dev = vmp->m_dev;
 | 
						|
  new_vp->v_fs_count = 1;
 | 
						|
  new_vp->v_ref_count = 1;
 | 
						|
 | 
						|
  *vpp= new_vp;
 | 
						|
  return OK;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *				lookup_vp				     *
 | 
						|
 *===========================================================================*/
 | 
						|
PUBLIC int lookup_vp(flags, use_realuid, vpp)
 | 
						|
int flags;
 | 
						|
int use_realuid;
 | 
						|
struct vnode **vpp;
 | 
						|
{
 | 
						|
  /* Resolve a pathname (in user_fullpath) starting to a vnode. Call
 | 
						|
   * lookup_rel_vp to do the actual work.
 | 
						|
   */
 | 
						|
  struct vnode *vp;
 | 
						|
 | 
						|
  if(!fp->fp_rd || !fp->fp_wd) {
 | 
						|
	printf("VFS: lookup_vp %d: no rd/wd\n", fp->fp_endpoint);
 | 
						|
	return ENOENT;
 | 
						|
  }
 | 
						|
 | 
						|
  vp= (user_fullpath[0] == '/' ? fp->fp_rd : fp->fp_wd);
 | 
						|
 | 
						|
  return lookup_rel_vp(vp, flags, use_realuid, vpp);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *				lookup_lastdir_rel			     *
 | 
						|
 *===========================================================================*/
 | 
						|
PUBLIC int lookup_lastdir_rel(start_node, use_realuid, vpp)
 | 
						|
struct vnode *start_node;
 | 
						|
int use_realuid;
 | 
						|
struct vnode **vpp;
 | 
						|
{
 | 
						|
	/* This function is for calls that insert or delete entries from a
 | 
						|
	 * directory. The path name (implicitly taken from user_fullpath)
 | 
						|
	 * is split into to parts: the name of the directory and the
 | 
						|
	 * directory entry. The name of the directory is resolved to a 
 | 
						|
	 * vnode. The directory entry is copied back to user_fullpath.
 | 
						|
	 * The lookup starts at start_node.
 | 
						|
	 */
 | 
						|
	int r;
 | 
						|
	size_t len;
 | 
						|
	char *cp;
 | 
						|
	char dir_entry[PATH_MAX+1];
 | 
						|
 | 
						|
	len= strlen(user_fullpath);
 | 
						|
	if (len == 0)
 | 
						|
	{
 | 
						|
		/* Empty path, always fail */
 | 
						|
		return ENOENT;
 | 
						|
	}
 | 
						|
 | 
						|
#if !DO_POSIX_PATHNAME_RES
 | 
						|
	/* Remove trailing slashes */
 | 
						|
	while (len > 1 && user_fullpath[len-1] == '/')
 | 
						|
	{
 | 
						|
		len--;
 | 
						|
		user_fullpath[len]= '\0';
 | 
						|
	}
 | 
						|
#endif
 | 
						|
 | 
						|
	cp= strrchr(user_fullpath, '/');
 | 
						|
	if (cp == NULL)
 | 
						|
	{
 | 
						|
		/* Just one entry in the current working directory */
 | 
						|
		dup_vnode(start_node);
 | 
						|
		*vpp= start_node;
 | 
						|
 | 
						|
		return OK;
 | 
						|
	}
 | 
						|
	else if (cp[1] == '\0')
 | 
						|
	{
 | 
						|
		/* Path ends in a slash. The directory entry is '.' */
 | 
						|
		strcpy(dir_entry, ".");
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		/* A path name for the directory and a directory entry */
 | 
						|
		strcpy(dir_entry, cp+1);
 | 
						|
		cp[1]= '\0';
 | 
						|
	}
 | 
						|
 | 
						|
	/* Remove trailing slashes */
 | 
						|
	while(cp > user_fullpath && cp[0] == '/')
 | 
						|
	{
 | 
						|
		cp[0]= '\0';
 | 
						|
		cp--;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Request lookup */
 | 
						|
	r = lookup_rel_vp(start_node, 0 /*no flags*/, use_realuid, vpp);
 | 
						|
	if (r != OK)
 | 
						|
		return r;
 | 
						|
 | 
						|
	/* Copy the directory entry back to user_fullpath */
 | 
						|
	strcpy(user_fullpath, dir_entry);
 | 
						|
 | 
						|
	return OK;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *				lookup_lastdir				     *
 | 
						|
 *===========================================================================*/
 | 
						|
PUBLIC int lookup_lastdir(use_realuid, vpp)
 | 
						|
int use_realuid;
 | 
						|
struct vnode **vpp;
 | 
						|
{
 | 
						|
	/* This function is for calls that insert or delete entries from a
 | 
						|
	 * directory. The path name (implicitly taken from user_fullpath)
 | 
						|
	 * is split into to parts: the name of the directory and the
 | 
						|
	 * directory entry. The name of the directory is resolved to a 
 | 
						|
	 * vnode. The directory entry is copied back to user_fullpath.
 | 
						|
	 * Just call lookup_lastdir_rel with the appropriate starting vnode.
 | 
						|
	 */
 | 
						|
	struct vnode *vp;
 | 
						|
 | 
						|
  if(!fp->fp_rd || !fp->fp_wd) {
 | 
						|
	printf("VFS: lookup_lastdir %d: no rd/wd\n", fp->fp_endpoint);
 | 
						|
	return ENOENT;
 | 
						|
  }
 | 
						|
 | 
						|
	vp= (user_fullpath[0] == '/' ? fp->fp_rd : fp->fp_wd);
 | 
						|
	return lookup_lastdir_rel(vp, use_realuid, vpp);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *				lookup_rel				     *
 | 
						|
 *===========================================================================*/
 | 
						|
PRIVATE int lookup_rel(start_node, flags, use_realuid, node)
 | 
						|
struct vnode *start_node;
 | 
						|
int flags;
 | 
						|
int use_realuid;
 | 
						|
node_details_t *node;
 | 
						|
{
 | 
						|
  /* Resolve a pathname (in user_fullpath) relative to start_node. */
 | 
						|
  int r, symloop;
 | 
						|
  endpoint_t fs_e;
 | 
						|
  size_t path_off;
 | 
						|
  ino_t dir_ino, root_ino;
 | 
						|
  uid_t uid;
 | 
						|
  gid_t gid;
 | 
						|
  struct vnode *dir_vp;
 | 
						|
  struct vmnt *vmp;
 | 
						|
  struct lookup_res res;
 | 
						|
  
 | 
						|
  /* Empty (start) path? */
 | 
						|
  if (user_fullpath[0] == '\0') {
 | 
						|
	node->inode_nr = 0;
 | 
						|
#if 0
 | 
						|
	printf("vfs:lookup_rel: returning ENOENT\n");
 | 
						|
#endif
 | 
						|
	return ENOENT;
 | 
						|
  }
 | 
						|
 | 
						|
  if(!fp->fp_rd || !fp->fp_wd) {
 | 
						|
	printf("VFS: lookup_rel %d: no rd/wd\n", fp->fp_endpoint);
 | 
						|
	return ENOENT;
 | 
						|
  }
 | 
						|
 | 
						|
  fs_e = start_node->v_fs_e;
 | 
						|
  path_off = 0;
 | 
						|
  dir_ino = start_node->v_inode_nr;
 | 
						|
  /* Is the process' root directory on the same partition?,
 | 
						|
   * if so, set the chroot directory too. */
 | 
						|
  if (fp->fp_rd->v_dev == fp->fp_wd->v_dev)
 | 
						|
      root_ino = fp->fp_rd->v_inode_nr; 
 | 
						|
  else
 | 
						|
      root_ino = 0;
 | 
						|
 | 
						|
  /* Set user and group ids according to the system call */
 | 
						|
  uid = (use_realuid ? fp->fp_realuid : fp->fp_effuid); 
 | 
						|
  gid = (use_realuid ? fp->fp_realgid : fp->fp_effgid); 
 | 
						|
 | 
						|
  symloop= 0;	/* Number of symlinks seen so far */
 | 
						|
 | 
						|
  /* Issue the request */
 | 
						|
  r = req_lookup(fs_e, path_off, dir_ino, root_ino, uid, gid, flags, &res);
 | 
						|
 | 
						|
  if (r != OK && r != EENTERMOUNT && r != ELEAVEMOUNT && r != ESYMLINK)
 | 
						|
  {
 | 
						|
#if 0
 | 
						|
	printf("vfs:lookup_rel: req_lookup_s failed with %d\n", r);
 | 
						|
#endif
 | 
						|
	return r;
 | 
						|
  }
 | 
						|
 | 
						|
  /* While the response is related to mount control set the 
 | 
						|
   * new requests respectively */
 | 
						|
  while (r == EENTERMOUNT || r == ELEAVEMOUNT || r == ESYMLINK) {
 | 
						|
	/* Save the place in the (possibly updated) path where we have to
 | 
						|
	 * continue witht henext lookup request.
 | 
						|
	 */
 | 
						|
	path_off= res.char_processed;
 | 
						|
 | 
						|
	/* Update the current value of the symloop counter */
 | 
						|
	symloop += res.symloop;
 | 
						|
	if (symloop > SYMLOOP_MAX)
 | 
						|
	{
 | 
						|
		printf("vfs:lookup_rel: returning ELOOP\n");
 | 
						|
		return ELOOP;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Symlink encountered with absolute path */
 | 
						|
	if (r == ESYMLINK) {
 | 
						|
		dir_vp = fp->fp_rd;
 | 
						|
	}
 | 
						|
	else if (r == EENTERMOUNT) {
 | 
						|
		/* Entering a new partition */
 | 
						|
		dir_vp = 0;
 | 
						|
		/* Start node is now the mounted partition's root node */
 | 
						|
		for (vmp = &vmnt[0]; vmp != &vmnt[NR_MNTS]; ++vmp) {
 | 
						|
			if (vmp->m_mounted_on->v_inode_nr == res.inode_nr &&
 | 
						|
				vmp->m_mounted_on->v_fs_e == res.fs_e) {
 | 
						|
				dir_vp = vmp->m_root_node;
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if (!dir_vp) {
 | 
						|
			printf(
 | 
						|
			"vfs:lookup_rel: res.inode_nr = %d, res.fs_e = %d\n",
 | 
						|
				res.inode_nr, res.fs_e);
 | 
						|
			panic(__FILE__,
 | 
						|
			"vfs:lookup_s: mounted partition couldn't be found",
 | 
						|
				NO_NUM);
 | 
						|
		}
 | 
						|
 | 
						|
	}
 | 
						|
	else {
 | 
						|
		/* Climbing up mount */
 | 
						|
		/* Find the vmnt that represents the partition on
 | 
						|
		 * which we "climb up". */
 | 
						|
		if ((vmp = find_vmnt(res.fs_e)) == NIL_VMNT) {
 | 
						|
			panic(__FILE__,
 | 
						|
			"vfs:lookup_s: couldn't find vmnt during the climbup",
 | 
						|
				NO_NUM);
 | 
						|
		}	  
 | 
						|
		/* Start node is the vnode on which the partition is
 | 
						|
		 * mounted */
 | 
						|
		dir_vp = vmp->m_mounted_on;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Set the starting directories inode number and FS endpoint */
 | 
						|
	fs_e = dir_vp->v_fs_e;
 | 
						|
	dir_ino = dir_vp->v_inode_nr;
 | 
						|
	/* Is the process' root directory on the same partition?,
 | 
						|
	 * if so, set the chroot directory too. */
 | 
						|
	if (dir_vp->v_dev == fp->fp_rd->v_dev)
 | 
						|
		root_ino = fp->fp_rd->v_inode_nr; 
 | 
						|
	else
 | 
						|
		root_ino = 0;
 | 
						|
 | 
						|
	/* Issue the request */
 | 
						|
	r = req_lookup(fs_e, path_off, dir_ino, root_ino, uid, gid, flags,
 | 
						|
		&res);
 | 
						|
 | 
						|
	if (r != OK && r != EENTERMOUNT && r != ELEAVEMOUNT && r != ESYMLINK)
 | 
						|
	{
 | 
						|
#if 0
 | 
						|
		printf("vfs:lookup_rel: req_lookup_s failed with %d\n", r);
 | 
						|
#endif
 | 
						|
		return r;
 | 
						|
	}
 | 
						|
  }
 | 
						|
 | 
						|
  /* Fill in response fields */
 | 
						|
  node->inode_nr = res.inode_nr;
 | 
						|
  node->fmode = res.fmode;
 | 
						|
  node->fsize = res.fsize;
 | 
						|
  node->dev = res.dev;
 | 
						|
  node->fs_e = res.fs_e;
 | 
						|
  node->uid = res.uid;
 | 
						|
  node->gid = res.gid;
 | 
						|
  
 | 
						|
  return r;
 | 
						|
}
 |