 b6aa3714a1
			
		
	
	
		b6aa3714a1
		
	
	
	
	
		
			
			Now that we have enough room in the message, remove these types. Change-Id: Ib734c6f0209b259a14a1189b3886b9c8474e1b9a
		
			
				
	
	
		
			341 lines
		
	
	
		
			8.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			341 lines
		
	
	
		
			8.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* This file provides path-to-inode lookup functionality.
 | |
|  *
 | |
|  * The entry points into this file are:
 | |
|  *   do_lookup		perform the LOOKUP file system call
 | |
|  *
 | |
|  * Created:
 | |
|  *   April 2009 (D.C. van Moolenbroek)
 | |
|  */
 | |
| 
 | |
| #include "inc.h"
 | |
| 
 | |
| static int get_mask(vfs_ucred_t *ucred);
 | |
| 
 | |
| static int access_as_dir(struct inode *ino, struct sffs_attr *attr,
 | |
| 	int uid, int mask);
 | |
| 
 | |
| static int next_name(char **ptr, char **start, char name[NAME_MAX+1]);
 | |
| 
 | |
| static int go_up(char path[PATH_MAX], struct inode *ino,
 | |
| 	struct inode **res_ino, struct sffs_attr *attr);
 | |
| 
 | |
| static int go_down(char path[PATH_MAX], struct inode *ino, char *name,
 | |
| 	struct inode **res_ino, struct sffs_attr *attr);
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				get_mask				     *
 | |
|  *===========================================================================*/
 | |
| static int get_mask(
 | |
| 	vfs_ucred_t *ucred	/* credentials of the caller */
 | |
| )
 | |
| {
 | |
|   /* Given the caller's credentials, precompute a search access mask to test
 | |
|    * against directory modes.
 | |
|    */
 | |
|   int i;
 | |
| 
 | |
|   if (ucred->vu_uid == sffs_params->p_uid) return S_IXUSR;
 | |
| 
 | |
|   if (ucred->vu_gid == sffs_params->p_gid) return S_IXGRP;
 | |
| 
 | |
|   for (i = 0; i < ucred->vu_ngroups; i++)
 | |
| 	if (ucred->vu_sgroups[i] == sffs_params->p_gid) return S_IXGRP;
 | |
| 
 | |
|   return S_IXOTH;
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				access_as_dir				     *
 | |
|  *===========================================================================*/
 | |
| static int access_as_dir(
 | |
| 	struct inode *ino,      /* the inode to test */
 | |
| 	struct sffs_attr *attr, /* attributes of the inode */
 | |
| 	int uid,                /* UID of the caller */
 | |
| 	int mask                /* search access mask of the caller */
 | |
| )
 | |
| {
 | |
| /* Check whether the given inode may be accessed as directory.
 | |
|  * Return OK or an appropriate error code.
 | |
|  */
 | |
|   mode_t mode;
 | |
| 
 | |
|   assert(attr->a_mask & SFFS_ATTR_MODE);
 | |
| 
 | |
|   /* The inode must be a directory to begin with. */
 | |
|   if (!IS_DIR(ino)) return ENOTDIR;
 | |
| 
 | |
|   /* The caller must have search access to the directory. Root always does. */
 | |
|   if (uid == 0) return OK;
 | |
| 
 | |
|   mode = get_mode(ino, attr->a_mode);
 | |
| 
 | |
|   return (mode & mask) ? OK : EACCES;
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				next_name				     *
 | |
|  *===========================================================================*/
 | |
| static int next_name(
 | |
| 	char **ptr,            /* cursor pointer into path (in, out) */
 | |
| 	char **start,          /* place to store start of name */
 | |
| 	char name[NAME_MAX+1]  /* place to store name */
 | |
| )
 | |
| {
 | |
| /* Get the next path component from a path.
 | |
|  */
 | |
|   char *p;
 | |
|   int i;
 | |
| 
 | |
|   for (p = *ptr; *p == '/'; p++);
 | |
| 
 | |
|   *start = p;
 | |
| 
 | |
|   if (*p) {
 | |
| 	for (i = 0; *p && *p != '/' && i <= NAME_MAX; p++, i++)
 | |
| 		name[i] = *p;
 | |
| 
 | |
| 	if (i > NAME_MAX)
 | |
| 		return ENAMETOOLONG;
 | |
| 
 | |
| 	name[i] = 0;
 | |
|   } else {
 | |
| 	strcpy(name, ".");
 | |
|   }
 | |
| 
 | |
|   *ptr = p;
 | |
|   return OK;
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				go_up					     *
 | |
|  *===========================================================================*/
 | |
| static int go_up(
 | |
| 	char path[PATH_MAX],    /* path to take the last part from */
 | |
| 	struct inode *ino,      /* inode of the current directory */
 | |
| 	struct inode **res_ino, /* place to store resulting inode */
 | |
| 	struct sffs_attr *attr  /* place to store inode attributes */
 | |
| )
 | |
| {
 | |
| /* Given an inode, progress into the parent directory.
 | |
|  */
 | |
|   struct inode *parent;
 | |
|   int r;
 | |
| 
 | |
|   pop_path(path);
 | |
| 
 | |
|   parent = ino->i_parent;
 | |
|   assert(parent != NULL);
 | |
| 
 | |
|   if ((r = verify_path(path, parent, attr, NULL)) != OK)
 | |
| 	return r;
 | |
| 
 | |
|   get_inode(parent);
 | |
| 
 | |
|   *res_ino = parent;
 | |
| 
 | |
|   return r;
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				go_down					     *
 | |
|  *===========================================================================*/
 | |
| static int go_down(
 | |
| 	char path[PATH_MAX],    /* path to add the name to */
 | |
| 	struct inode *parent,   /* inode of the current directory */
 | |
| 	char *name,             /* name of the directory entry */
 | |
| 	struct inode **res_ino, /* place to store resulting inode */
 | |
| 	struct sffs_attr *attr  /* place to store inode attributes */
 | |
| )
 | |
| {
 | |
| /* Given a directory inode and a name, progress into a directory entry.
 | |
|  */
 | |
|   struct inode *ino;
 | |
|   int r, stale = 0;
 | |
| 
 | |
|   if ((r = push_path(path, name)) != OK)
 | |
| 	return r;
 | |
| 
 | |
|   dprintf(("%s: go_down: name '%s', path now '%s'\n", sffs_name, name, path));
 | |
| 
 | |
|   ino = lookup_dentry(parent, name);
 | |
| 
 | |
|   dprintf(("%s: lookup_dentry('%s') returned %p\n", sffs_name, name, ino));
 | |
| 
 | |
|   if (ino != NULL)
 | |
| 	r = verify_path(path, ino, attr, &stale);
 | |
|   else
 | |
| 	r = sffs_table->t_getattr(path, attr);
 | |
| 
 | |
|   dprintf(("%s: path query returned %d\n", sffs_name, r));
 | |
| 
 | |
|   if (r != OK) {
 | |
| 	if (ino != NULL) {
 | |
| 		put_inode(ino);
 | |
| 
 | |
| 		ino = NULL;
 | |
| 	}
 | |
| 
 | |
| 	if (!stale)
 | |
| 		return r;
 | |
|   }
 | |
| 
 | |
|   dprintf(("%s: name '%s'\n", sffs_name, name));
 | |
| 
 | |
|   if (ino == NULL) {
 | |
| 	if ((ino = get_free_inode()) == NULL)
 | |
| 		return ENFILE;
 | |
| 
 | |
| 	dprintf(("%s: inode %p ref %d\n", sffs_name, ino, ino->i_ref));
 | |
| 
 | |
| 	ino->i_flags = MODE_TO_DIRFLAG(attr->a_mode);
 | |
| 
 | |
| 	add_dentry(parent, name, ino);
 | |
|   }
 | |
| 
 | |
|   *res_ino = ino;
 | |
|   return OK;
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				do_lookup				     *
 | |
|  *===========================================================================*/
 | |
| int do_lookup(void)
 | |
| {
 | |
| /* Resolve a path string to an inode.
 | |
|  */
 | |
|   ino_t dir_ino_nr, root_ino_nr;
 | |
|   struct inode *cur_ino, *root_ino;
 | |
|   struct inode *next_ino = NULL;
 | |
|   struct sffs_attr attr;
 | |
|   char buf[PATH_MAX], path[PATH_MAX];
 | |
|   char name[NAME_MAX+1];
 | |
|   char *ptr, *last;
 | |
|   vfs_ucred_t ucred;
 | |
|   mode_t mask;
 | |
|   size_t len;
 | |
|   int r;
 | |
| 
 | |
|   dir_ino_nr = m_in.m_vfs_fs_lookup.dir_ino;
 | |
|   root_ino_nr = m_in.m_vfs_fs_lookup.root_ino;
 | |
|   len = m_in.m_vfs_fs_lookup.path_len;
 | |
| 
 | |
|   /* Fetch the path name. */
 | |
|   if (len < 1 || len > PATH_MAX)
 | |
| 	return EINVAL;
 | |
| 
 | |
|   r = sys_safecopyfrom(m_in.m_source, m_in.m_vfs_fs_lookup.grant_path, 0,
 | |
| 	(vir_bytes) buf, len);
 | |
| 
 | |
|   if (r != OK)
 | |
| 	return r;
 | |
| 
 | |
|   if (buf[len-1] != 0) {
 | |
| 	printf("%s: VFS did not zero-terminate path!\n", sffs_name);
 | |
| 
 | |
| 	return EINVAL;
 | |
|   }
 | |
| 
 | |
|   /* Fetch the credentials, and generate a search access mask to test against
 | |
|    * directory modes.
 | |
|    */
 | |
|   if (m_in.m_vfs_fs_lookup.flags & PATH_GET_UCRED) {
 | |
| 	if (m_in.m_vfs_fs_lookup.ucred_size != sizeof(ucred)) {
 | |
| 		printf("%s: bad credential structure size\n", sffs_name);
 | |
| 
 | |
| 		return EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	r = sys_safecopyfrom(m_in.m_source, m_in.m_vfs_fs_lookup.grant_ucred, 0,
 | |
| 		(vir_bytes) &ucred, m_in.m_vfs_fs_lookup.ucred_size);
 | |
| 
 | |
| 	if (r != OK)
 | |
| 		return r;
 | |
|   }
 | |
|   else {
 | |
| 	ucred.vu_uid = m_in.m_vfs_fs_lookup.uid;
 | |
| 	ucred.vu_gid = m_in.m_vfs_fs_lookup.gid;
 | |
| 	ucred.vu_ngroups = 0;
 | |
|   }
 | |
| 
 | |
|   mask = get_mask(&ucred);
 | |
| 
 | |
|   /* Start the actual lookup. */
 | |
|   dprintf(("%s: lookup: got query '%s'\n", sffs_name, buf));
 | |
| 
 | |
|   if ((cur_ino = find_inode(dir_ino_nr)) == NULL)
 | |
| 	return EINVAL;
 | |
| 
 | |
|   attr.a_mask = SFFS_ATTR_MODE | SFFS_ATTR_SIZE;
 | |
| 
 | |
|   if ((r = verify_inode(cur_ino, path, &attr)) != OK)
 | |
| 	return r;
 | |
| 
 | |
|   get_inode(cur_ino);
 | |
| 
 | |
|   if (root_ino_nr > 0)
 | |
| 	root_ino = find_inode(root_ino_nr);
 | |
|   else
 | |
| 	root_ino = NULL;
 | |
| 
 | |
|   /* One possible optimization would be to check a path only right before the
 | |
|    * first ".." in a row, and at the very end (if still necessary). This would
 | |
|    * have consequences for inode validation, though.
 | |
|    */
 | |
|   for (ptr = last = buf; *ptr != 0; ) {
 | |
| 	if ((r = access_as_dir(cur_ino, &attr, ucred.vu_uid, mask)) != OK)
 | |
| 		break;
 | |
| 
 | |
| 	if ((r = next_name(&ptr, &last, name)) != OK)
 | |
| 		break;
 | |
| 
 | |
| 	dprintf(("%s: lookup: next name '%s'\n", sffs_name, name));
 | |
| 
 | |
| 	if (!strcmp(name, ".") ||
 | |
| 			(cur_ino == root_ino && !strcmp(name, "..")))
 | |
| 		continue;
 | |
| 
 | |
| 	if (!strcmp(name, "..")) {
 | |
| 		if (IS_ROOT(cur_ino))
 | |
| 			r = ELEAVEMOUNT;
 | |
| 		else
 | |
| 			r = go_up(path, cur_ino, &next_ino, &attr);
 | |
| 	} else {
 | |
| 		r = go_down(path, cur_ino, name, &next_ino, &attr);
 | |
| 	}
 | |
| 
 | |
| 	if (r != OK)
 | |
| 		break;
 | |
| 
 | |
| 	assert(next_ino != NULL);
 | |
| 
 | |
| 	put_inode(cur_ino);
 | |
| 
 | |
| 	cur_ino = next_ino;
 | |
|   }
 | |
| 
 | |
|   dprintf(("%s: lookup: result %d\n", sffs_name, r));
 | |
| 
 | |
|   if (r != OK) {
 | |
| 	put_inode(cur_ino);
 | |
| 
 | |
| 	/* We'd need support for these here. We don't have such support. */
 | |
| 	assert(r != EENTERMOUNT && r != ESYMLINK);
 | |
| 
 | |
| 	if (r == ELEAVEMOUNT) {
 | |
| 		m_out.m_fs_vfs_lookup.offset = (last - buf);
 | |
| 		m_out.m_fs_vfs_lookup.symloop = 0;
 | |
| 	}
 | |
| 
 | |
| 	return r;
 | |
|   }
 | |
| 
 | |
|   m_out.m_fs_vfs_lookup.inode = INODE_NR(cur_ino);
 | |
|   m_out.m_fs_vfs_lookup.mode = get_mode(cur_ino, attr.a_mode);
 | |
|   m_out.m_fs_vfs_lookup.file_size = attr.a_size;
 | |
|   m_out.m_fs_vfs_lookup.uid = sffs_params->p_uid;
 | |
|   m_out.m_fs_vfs_lookup.gid = sffs_params->p_gid;
 | |
|   m_out.m_fs_vfs_lookup.device = NO_DEV;
 | |
| 
 | |
|   return OK;
 | |
| }
 |