374 lines
		
	
	
		
			9.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			374 lines
		
	
	
		
			9.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
#include "inc.h"
 | 
						|
#include <string.h>
 | 
						|
#include <minix/com.h>
 | 
						|
#include <minix/vfsif.h>
 | 
						|
 | 
						|
#include "buf.h"
 | 
						|
 | 
						|
FORWARD _PROTOTYPE( char *get_name, (char *name, char string[NAME_MAX+1]) );
 | 
						|
FORWARD _PROTOTYPE( int parse_path, (ino_t dir_ino, ino_t root_ino, int flags,
 | 
						|
				     struct dir_record **res_inop,
 | 
						|
				     size_t *offsetp)			);
 | 
						|
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *                             fs_lookup				     *
 | 
						|
 *===========================================================================*/
 | 
						|
PUBLIC int fs_lookup() {
 | 
						|
  cp_grant_id_t grant;
 | 
						|
  int r, len, flags;
 | 
						|
  size_t offset;
 | 
						|
  ino_t dir_ino, root_ino;
 | 
						|
  struct dir_record *dir;
 | 
						|
 | 
						|
  grant		= fs_m_in.REQ_GRANT;
 | 
						|
  len		= fs_m_in.REQ_PATH_LEN;	/* including terminating nul */
 | 
						|
  dir_ino	= fs_m_in.REQ_DIR_INO;
 | 
						|
  root_ino	= fs_m_in.REQ_ROOT_INO;
 | 
						|
  flags		= fs_m_in.REQ_FLAGS;
 | 
						|
  caller_uid	= fs_m_in.REQ_UID;
 | 
						|
  caller_gid	= fs_m_in.REQ_GID;
 | 
						|
 | 
						|
  /* Check length. */
 | 
						|
  if(len > sizeof(user_path)) return(E2BIG);	/* too big for buffer */
 | 
						|
  if(len < 1) return(EINVAL);			/* too small */
 | 
						|
 | 
						|
  /* Copy the pathname and set up caller's user and group id */
 | 
						|
  r = sys_safecopyfrom(VFS_PROC_NR, grant, 0, (vir_bytes) user_path, 
 | 
						|
		       (phys_bytes) len, D);
 | 
						|
  if (r != OK) {
 | 
						|
	printf("ISOFS %s:%d sys_safecopyfrom failed: %d\n",
 | 
						|
		__FILE__, __LINE__, r);
 | 
						|
	return(r);
 | 
						|
  }
 | 
						|
 | 
						|
  /* Verify this is a null-terminated path. */
 | 
						|
  if(user_path[len-1] != '\0') return(EINVAL);
 | 
						|
 | 
						|
  /* Lookup inode */
 | 
						|
  dir = NULL;
 | 
						|
  offset = 0;
 | 
						|
  r = parse_path(dir_ino, root_ino, flags, &dir, &offset);
 | 
						|
 | 
						|
  if (r == ELEAVEMOUNT) {
 | 
						|
	/* Report offset and the error */
 | 
						|
	fs_m_out.RES_OFFSET = offset;
 | 
						|
	fs_m_out.RES_SYMLOOP = 0;
 | 
						|
	return(r);
 | 
						|
  }
 | 
						|
 | 
						|
  if (r != OK && r != EENTERMOUNT) return(r);
 | 
						|
 | 
						|
  fs_m_out.RES_INODE_NR     = ID_DIR_RECORD(dir);
 | 
						|
  fs_m_out.RES_MODE         = dir->d_mode;
 | 
						|
  fs_m_out.RES_FILE_SIZE_LO = dir->d_file_size; 
 | 
						|
  fs_m_out.RES_SYMLOOP      = 0;
 | 
						|
  fs_m_out.RES_UID          = SYS_UID; 	/* root */
 | 
						|
  fs_m_out.RES_GID          = SYS_GID;		/* operator */
 | 
						|
 | 
						|
 | 
						|
  if (r == EENTERMOUNT) { 
 | 
						|
  	fs_m_out.RES_OFFSET = offset;
 | 
						|
	release_dir_record(dir);
 | 
						|
  }
 | 
						|
 | 
						|
  return(r);
 | 
						|
}
 | 
						|
 | 
						|
/* The search dir actually performs the operation of searching for the
 | 
						|
 * compoent ``string" in ldir_ptr. It returns the response and the number of
 | 
						|
 * the inode in numb. */
 | 
						|
/*===========================================================================*
 | 
						|
 *				search_dir				     *
 | 
						|
 *===========================================================================*/
 | 
						|
PUBLIC int search_dir(ldir_ptr,string,numb)
 | 
						|
     register struct dir_record *ldir_ptr; /*  dir record parent */
 | 
						|
     char string[NAME_MAX];	      /* component to search for */
 | 
						|
     ino_t *numb;		      /* pointer to new dir record */
 | 
						|
{
 | 
						|
  struct dir_record *dir_tmp;
 | 
						|
  register struct buf *bp;
 | 
						|
  int pos;
 | 
						|
  char* comma_pos = NULL;
 | 
						|
  char tmp_string[NAME_MAX];
 | 
						|
 | 
						|
  /* This function search a particular element (in string) in a inode and
 | 
						|
   * return its number */
 | 
						|
 | 
						|
  /* Initialize the tmp array */
 | 
						|
  memset(tmp_string,'\0',NAME_MAX);
 | 
						|
 | 
						|
  if ((ldir_ptr->d_mode & I_TYPE) != I_DIRECTORY) {
 | 
						|
    return(ENOTDIR);
 | 
						|
  }
 | 
						|
  
 | 
						|
  if (strcmp(string,".") == 0) {
 | 
						|
    *numb = ID_DIR_RECORD(ldir_ptr);
 | 
						|
    return OK;
 | 
						|
  }
 | 
						|
 | 
						|
  if (strcmp(string,"..") == 0 && ldir_ptr->loc_extent_l == v_pri.dir_rec_root->loc_extent_l) {
 | 
						|
    *numb = ROOT_INO_NR;
 | 
						|
/*     *numb = ID_DIR_RECORD(ldir_ptr); */
 | 
						|
    return OK;
 | 
						|
  }
 | 
						|
 | 
						|
  /* Read the dir's content */
 | 
						|
  pos = ldir_ptr->ext_attr_rec_length;
 | 
						|
  bp = get_block(ldir_ptr->loc_extent_l);
 | 
						|
 | 
						|
  if (bp == NULL)
 | 
						|
    return EINVAL;
 | 
						|
 | 
						|
  while (pos < v_pri.logical_block_size_l) {
 | 
						|
    if ((dir_tmp = get_free_dir_record()) == NULL) {
 | 
						|
      put_block(bp);
 | 
						|
      return EINVAL;
 | 
						|
    }
 | 
						|
 | 
						|
    if (create_dir_record(dir_tmp,bp->b_data + pos,
 | 
						|
			  ldir_ptr->loc_extent_l*v_pri.logical_block_size_l + pos) == EINVAL)
 | 
						|
      return EINVAL;
 | 
						|
 | 
						|
    if (dir_tmp->length == 0) {
 | 
						|
      release_dir_record(dir_tmp);
 | 
						|
      put_block(bp);
 | 
						|
      return EINVAL;
 | 
						|
    }
 | 
						|
    
 | 
						|
    memcpy(tmp_string,dir_tmp->file_id,dir_tmp->length_file_id);
 | 
						|
    comma_pos = strchr(tmp_string,';');
 | 
						|
    if (comma_pos != NULL)
 | 
						|
      *comma_pos = 0;
 | 
						|
    else
 | 
						|
      tmp_string[dir_tmp->length_file_id] = 0;
 | 
						|
    if (tmp_string[strlen(tmp_string) - 1] == '.')
 | 
						|
      tmp_string[strlen(tmp_string) - 1] = '\0';
 | 
						|
    
 | 
						|
    if (strcmp(tmp_string,string) == 0 ||
 | 
						|
	(dir_tmp->file_id[0] == 1 && strcmp(string,"..") == 0)) {
 | 
						|
 | 
						|
      /* If the element is found or we are searchig for... */
 | 
						|
 | 
						|
      if (dir_tmp->loc_extent_l == dir_records->loc_extent_l) {
 | 
						|
	/* In this case the inode is a root because the parent
 | 
						|
	 * points to the same location than the inode. */
 | 
						|
	*numb = 1;
 | 
						|
 	release_dir_record(dir_tmp);
 | 
						|
	put_block(bp);
 | 
						|
	return OK;
 | 
						|
      }
 | 
						|
 | 
						|
      if (dir_tmp->ext_attr_rec_length != 0) {
 | 
						|
	dir_tmp->ext_attr = get_free_ext_attr();
 | 
						|
	create_ext_attr(dir_tmp->ext_attr,bp->b_data);
 | 
						|
      }
 | 
						|
 | 
						|
      *numb = ID_DIR_RECORD(dir_tmp);
 | 
						|
      release_dir_record(dir_tmp);
 | 
						|
      put_block(bp);
 | 
						|
      
 | 
						|
      return OK;
 | 
						|
    }
 | 
						|
 | 
						|
    pos += dir_tmp->length;
 | 
						|
    release_dir_record(dir_tmp);
 | 
						|
  }
 | 
						|
  
 | 
						|
  put_block(bp);
 | 
						|
  return EINVAL;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *                             parse_path				     *
 | 
						|
 *===========================================================================*/
 | 
						|
PRIVATE int parse_path(dir_ino, root_ino, flags, res_inop, offsetp)
 | 
						|
ino_t dir_ino;
 | 
						|
ino_t root_ino;
 | 
						|
int flags;
 | 
						|
struct dir_record **res_inop;
 | 
						|
size_t *offsetp;
 | 
						|
{
 | 
						|
  int r;
 | 
						|
  char string[NAME_MAX+1];
 | 
						|
  char *cp, *ncp;
 | 
						|
  struct dir_record *start_dir, *old_dir;
 | 
						|
 | 
						|
  /* Find starting inode inode according to the request message */
 | 
						|
  if ((start_dir = get_dir_record(dir_ino)) == NULL) {
 | 
						|
    printf("ISOFS: couldn't find starting inode %d\n", dir_ino);
 | 
						|
    return(ENOENT);
 | 
						|
  }
 | 
						|
  
 | 
						|
  cp = user_path;
 | 
						|
 | 
						|
  /* Scan the path component by component. */
 | 
						|
  while (TRUE) {
 | 
						|
    if (cp[0] == '\0') {
 | 
						|
      /* Empty path */
 | 
						|
      *res_inop= start_dir;
 | 
						|
      *offsetp += cp-user_path;
 | 
						|
 | 
						|
      /* Return EENTERMOUNT if we are at a mount point */
 | 
						|
      if (start_dir->d_mountpoint)
 | 
						|
       	return EENTERMOUNT;
 | 
						|
 | 
						|
      return OK;
 | 
						|
    }
 | 
						|
 | 
						|
    if (cp[0] == '/') {
 | 
						|
      /* Special case code. If the remaining path consists of just
 | 
						|
       * slashes, we need to look up '.'
 | 
						|
       */
 | 
						|
      while(cp[0] == '/')
 | 
						|
	cp++;
 | 
						|
      if (cp[0] == '\0') {
 | 
						|
	strcpy(string, ".");
 | 
						|
	ncp = cp;
 | 
						|
      }
 | 
						|
      else
 | 
						|
	ncp = get_name(cp, string);
 | 
						|
    } else
 | 
						|
      /* Just get the first component */
 | 
						|
      ncp = get_name(cp, string);
 | 
						|
  
 | 
						|
    /* Special code for '..'. A process is not allowed to leave a chrooted
 | 
						|
     * environment. A lookup of '..' at the root of a mounted filesystem
 | 
						|
     * has to return ELEAVEMOUNT.
 | 
						|
     */
 | 
						|
    if (strcmp(string, "..") == 0) {
 | 
						|
 | 
						|
      /* This condition is not necessary since it will never be the root filesystem */
 | 
						|
      /*       if (start_dir == dir_records) { */
 | 
						|
      /* 	cp = ncp; */
 | 
						|
      /* 	continue;	/\* Just ignore the '..' at a process' */
 | 
						|
      /* 			 * root. */
 | 
						|
      /* 			 *\/ */
 | 
						|
      /*       } */
 | 
						|
 | 
						|
      if (start_dir == dir_records) {
 | 
						|
	/* Climbing up mountpoint */
 | 
						|
	release_dir_record(start_dir);
 | 
						|
	*res_inop = NULL;
 | 
						|
	*offsetp += cp-user_path;
 | 
						|
	return ELEAVEMOUNT;
 | 
						|
      }
 | 
						|
    } else {
 | 
						|
      /* Only check for a mount point if we are not looking for '..'. */
 | 
						|
      if (start_dir->d_mountpoint) {
 | 
						|
	*res_inop= start_dir;
 | 
						|
	*offsetp += cp-user_path;
 | 
						|
	return EENTERMOUNT;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 
 | 
						|
    /* There is more path.  Keep parsing. */
 | 
						|
    old_dir = start_dir;
 | 
						|
 | 
						|
    r = advance(old_dir, string, &start_dir);
 | 
						|
 | 
						|
    if (r != OK) {
 | 
						|
      release_dir_record(old_dir);
 | 
						|
      return r;
 | 
						|
    }
 | 
						|
 | 
						|
    release_dir_record(old_dir);
 | 
						|
    cp = ncp;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *				advance					     *
 | 
						|
 *===========================================================================*/
 | 
						|
PUBLIC int advance(dirp, string, resp)
 | 
						|
struct dir_record *dirp;		/* inode for directory to be searched */
 | 
						|
char string[NAME_MAX];		        /* component name to look for */
 | 
						|
struct dir_record **resp;		/* resulting inode */
 | 
						|
{
 | 
						|
/* Given a directory and a component of a path, look up the component in
 | 
						|
 * the directory, find the inode, open it, and return a pointer to its inode
 | 
						|
 * slot.
 | 
						|
 */
 | 
						|
 | 
						|
  register struct dir_record *rip = NULL;
 | 
						|
  int r;
 | 
						|
  ino_t numb;
 | 
						|
 | 
						|
  /* If 'string' is empty, yield same inode straight away. */
 | 
						|
  if (string[0] == '\0') {
 | 
						|
    return ENOENT;
 | 
						|
  }
 | 
						|
 | 
						|
  /* Check for NULL. */
 | 
						|
  if (dirp == NULL) {
 | 
						|
    return EINVAL;
 | 
						|
  }
 | 
						|
 | 
						|
  /* If 'string' is not present in the directory, signal error. */
 | 
						|
  if ( (r = search_dir(dirp, string, &numb)) != OK) {
 | 
						|
    return r;
 | 
						|
  }
 | 
						|
 | 
						|
  /* The component has been found in the directory.  Get inode. */
 | 
						|
  if ( (rip = get_dir_record((int) numb)) == NULL)  {
 | 
						|
    return(err_code);
 | 
						|
  }
 | 
						|
 | 
						|
  *resp= rip;
 | 
						|
  return OK;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *				get_name				     *
 | 
						|
 *===========================================================================*/
 | 
						|
PRIVATE char *get_name(path_name, string)
 | 
						|
char *path_name;		/* path name to parse */
 | 
						|
char string[NAME_MAX+1];	/* component extracted from 'old_name' */
 | 
						|
{
 | 
						|
/* Given a pointer to a path name in fs space, 'path_name', copy the first
 | 
						|
 * component to 'string' (truncated if necessary, always nul terminated).
 | 
						|
 * A pointer to the string after the first component of the name as yet
 | 
						|
 * unparsed is returned.  Roughly speaking,
 | 
						|
 * 'get_name' = 'path_name' - 'string'.
 | 
						|
 *
 | 
						|
 * This routine follows the standard convention that /usr/ast, /usr//ast,
 | 
						|
 * //usr///ast and /usr/ast/ are all equivalent.
 | 
						|
 */
 | 
						|
  size_t len;
 | 
						|
  char *cp, *ep;
 | 
						|
 | 
						|
  cp= path_name;
 | 
						|
 | 
						|
  /* Skip leading slashes */
 | 
						|
  while (cp[0] == '/')
 | 
						|
	cp++;
 | 
						|
 | 
						|
  /* Find the end of the first component */
 | 
						|
  ep= cp;
 | 
						|
  while(ep[0] != '\0' && ep[0] != '/')
 | 
						|
	ep++;
 | 
						|
 | 
						|
  len= ep-cp;
 | 
						|
 | 
						|
  /* Truncate the amount to be copied if it exceeds NAME_MAX */
 | 
						|
  if (len > NAME_MAX)
 | 
						|
	len= NAME_MAX;
 | 
						|
 | 
						|
  /* Special case of the string at cp is empty */
 | 
						|
  if (len == 0)
 | 
						|
  {
 | 
						|
	/* Return "." */
 | 
						|
	strcpy(string, ".");
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
	memcpy(string, cp, len);
 | 
						|
	string[len]= '\0';
 | 
						|
  }
 | 
						|
 | 
						|
  return ep;
 | 
						|
}
 |