diff options
Diffstat (limited to 'fs/namei.c')
| -rw-r--r-- | fs/namei.c | 216 | 
1 files changed, 143 insertions, 73 deletions
diff --git a/fs/namei.c b/fs/namei.c index aa30d19e9ed..d1895f30815 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -117,18 +117,70 @@   * POSIX.1 2.4: an empty pathname is invalid (ENOENT).   * PATH_MAX includes the nul terminator --RR.   */ -static char *getname_flags(const char __user *filename, int flags, int *empty) +void final_putname(struct filename *name)  { -	char *result = __getname(), *err; +	if (name->separate) { +		__putname(name->name); +		kfree(name); +	} else { +		__putname(name); +	} +} + +#define EMBEDDED_NAME_MAX	(PATH_MAX - sizeof(struct filename)) + +static struct filename * +getname_flags(const char __user *filename, int flags, int *empty) +{ +	struct filename *result, *err;  	int len; +	long max; +	char *kname; +	result = audit_reusename(filename); +	if (result) +		return result; + +	result = __getname();  	if (unlikely(!result))  		return ERR_PTR(-ENOMEM); -	len = strncpy_from_user(result, filename, PATH_MAX); -	err = ERR_PTR(len); -	if (unlikely(len < 0)) +	/* +	 * First, try to embed the struct filename inside the names_cache +	 * allocation +	 */ +	kname = (char *)result + sizeof(*result); +	result->name = kname; +	result->separate = false; +	max = EMBEDDED_NAME_MAX; + +recopy: +	len = strncpy_from_user(kname, filename, max); +	if (unlikely(len < 0)) { +		err = ERR_PTR(len);  		goto error; +	} + +	/* +	 * Uh-oh. We have a name that's approaching PATH_MAX. Allocate a +	 * separate struct filename so we can dedicate the entire +	 * names_cache allocation for the pathname, and re-do the copy from +	 * userland. +	 */ +	if (len == EMBEDDED_NAME_MAX && max == EMBEDDED_NAME_MAX) { +		kname = (char *)result; + +		result = kzalloc(sizeof(*result), GFP_KERNEL); +		if (!result) { +			err = ERR_PTR(-ENOMEM); +			result = (struct filename *)kname; +			goto error; +		} +		result->name = kname; +		result->separate = true; +		max = PATH_MAX; +		goto recopy; +	}  	/* The empty path is special. */  	if (unlikely(!len)) { @@ -140,30 +192,32 @@ static char *getname_flags(const char __user *filename, int flags, int *empty)  	}  	err = ERR_PTR(-ENAMETOOLONG); -	if (likely(len < PATH_MAX)) { -		audit_getname(result); -		return result; -	} +	if (unlikely(len >= PATH_MAX)) +		goto error; + +	result->uptr = filename; +	audit_getname(result); +	return result;  error: -	__putname(result); +	final_putname(result);  	return err;  } -char *getname(const char __user * filename) +struct filename * +getname(const char __user * filename)  {  	return getname_flags(filename, 0, NULL);  } +EXPORT_SYMBOL(getname);  #ifdef CONFIG_AUDITSYSCALL -void putname(const char *name) +void putname(struct filename *name)  {  	if (unlikely(!audit_dummy_context())) -		audit_putname(name); -	else -		__putname(name); +		return audit_putname(name); +	final_putname(name);  } -EXPORT_SYMBOL(putname);  #endif  static int check_acl(struct inode *inode, int mask) @@ -692,9 +746,9 @@ static inline int may_follow_link(struct path *link, struct nameidata *nd)  	if (uid_eq(parent->i_uid, inode->i_uid))  		return 0; +	audit_log_link_denied("follow_link", link);  	path_put_conditional(link, nd);  	path_put(&nd->path); -	audit_log_link_denied("follow_link", link);  	return -EACCES;  } @@ -810,6 +864,7 @@ follow_link(struct path *link, struct nameidata *nd, void **p)  	return error;  out_put_nd_path: +	*p = NULL;  	path_put(&nd->path);  	path_put(link);  	return error; @@ -1962,24 +2017,29 @@ static int path_lookupat(int dfd, const char *name,  	return err;  } -static int do_path_lookup(int dfd, const char *name, +static int filename_lookup(int dfd, struct filename *name,  				unsigned int flags, struct nameidata *nd)  { -	int retval = path_lookupat(dfd, name, flags | LOOKUP_RCU, nd); +	int retval = path_lookupat(dfd, name->name, flags | LOOKUP_RCU, nd);  	if (unlikely(retval == -ECHILD)) -		retval = path_lookupat(dfd, name, flags, nd); +		retval = path_lookupat(dfd, name->name, flags, nd);  	if (unlikely(retval == -ESTALE)) -		retval = path_lookupat(dfd, name, flags | LOOKUP_REVAL, nd); +		retval = path_lookupat(dfd, name->name, +						flags | LOOKUP_REVAL, nd); -	if (likely(!retval)) { -		if (unlikely(!audit_dummy_context())) { -			if (nd->path.dentry && nd->inode) -				audit_inode(name, nd->path.dentry); -		} -	} +	if (likely(!retval)) +		audit_inode(name, nd->path.dentry, flags & LOOKUP_PARENT);  	return retval;  } +static int do_path_lookup(int dfd, const char *name, +				unsigned int flags, struct nameidata *nd) +{ +	struct filename filename = { .name = name }; + +	return filename_lookup(dfd, &filename, flags, nd); +} +  /* does lookup, returns the object with parent locked */  struct dentry *kern_path_locked(const char *name, struct path *path)  { @@ -2097,13 +2157,13 @@ int user_path_at_empty(int dfd, const char __user *name, unsigned flags,  		 struct path *path, int *empty)  {  	struct nameidata nd; -	char *tmp = getname_flags(name, flags, empty); +	struct filename *tmp = getname_flags(name, flags, empty);  	int err = PTR_ERR(tmp);  	if (!IS_ERR(tmp)) {  		BUG_ON(flags & LOOKUP_PARENT); -		err = do_path_lookup(dfd, tmp, flags, &nd); +		err = filename_lookup(dfd, tmp, flags, &nd);  		putname(tmp);  		if (!err)  			*path = nd.path; @@ -2117,22 +2177,28 @@ int user_path_at(int dfd, const char __user *name, unsigned flags,  	return user_path_at_empty(dfd, name, flags, path, NULL);  } -static int user_path_parent(int dfd, const char __user *path, -			struct nameidata *nd, char **name) +/* + * NB: most callers don't do anything directly with the reference to the + *     to struct filename, but the nd->last pointer points into the name string + *     allocated by getname. So we must hold the reference to it until all + *     path-walking is complete. + */ +static struct filename * +user_path_parent(int dfd, const char __user *path, struct nameidata *nd)  { -	char *s = getname(path); +	struct filename *s = getname(path);  	int error;  	if (IS_ERR(s)) -		return PTR_ERR(s); +		return s; -	error = do_path_lookup(dfd, s, LOOKUP_PARENT, nd); -	if (error) +	error = filename_lookup(dfd, s, LOOKUP_PARENT, nd); +	if (error) {  		putname(s); -	else -		*name = s; +		return ERR_PTR(error); +	} -	return error; +	return s;  }  /* @@ -2179,7 +2245,7 @@ static int may_delete(struct inode *dir,struct dentry *victim,int isdir)  		return -ENOENT;  	BUG_ON(victim->d_parent->d_inode != dir); -	audit_inode_child(victim, dir); +	audit_inode_child(dir, victim, AUDIT_TYPE_CHILD_DELETE);  	error = inode_permission(dir, MAY_WRITE | MAY_EXEC);  	if (error) @@ -2624,7 +2690,7 @@ out_dput:   */  static int do_last(struct nameidata *nd, struct path *path,  		   struct file *file, const struct open_flags *op, -		   int *opened, const char *pathname) +		   int *opened, struct filename *name)  {  	struct dentry *dir = nd->path.dentry;  	int open_flag = op->open_flag; @@ -2651,7 +2717,7 @@ static int do_last(struct nameidata *nd, struct path *path,  		error = complete_walk(nd);  		if (error)  			return error; -		audit_inode(pathname, nd->path.dentry); +		audit_inode(name, nd->path.dentry, 0);  		if (open_flag & O_CREAT) {  			error = -EISDIR;  			goto out; @@ -2661,7 +2727,7 @@ static int do_last(struct nameidata *nd, struct path *path,  		error = complete_walk(nd);  		if (error)  			return error; -		audit_inode(pathname, dir); +		audit_inode(name, dir, 0);  		goto finish_open;  	} @@ -2690,7 +2756,7 @@ static int do_last(struct nameidata *nd, struct path *path,  		if (error)  			return error; -		audit_inode(pathname, dir); +		audit_inode(name, dir, 0);  		error = -EISDIR;  		/* trailing slashes? */  		if (nd->last.name[nd->last.len]) @@ -2720,7 +2786,7 @@ retry_lookup:  		    !S_ISREG(file->f_path.dentry->d_inode->i_mode))  			will_truncate = false; -		audit_inode(pathname, file->f_path.dentry); +		audit_inode(name, file->f_path.dentry, 0);  		goto opened;  	} @@ -2737,7 +2803,7 @@ retry_lookup:  	 * create/update audit record if it already exists.  	 */  	if (path->dentry->d_inode) -		audit_inode(pathname, path->dentry); +		audit_inode(name, path->dentry, 0);  	/*  	 * If atomic_open() acquired write access it is dropped now due to @@ -2802,7 +2868,7 @@ finish_lookup:  	error = -ENOTDIR;  	if ((nd->flags & LOOKUP_DIRECTORY) && !nd->inode->i_op->lookup)  		goto out; -	audit_inode(pathname, nd->path.dentry); +	audit_inode(name, nd->path.dentry, 0);  finish_open:  	if (!S_ISREG(nd->inode->i_mode))  		will_truncate = false; @@ -2870,7 +2936,7 @@ stale_open:  	goto retry_lookup;  } -static struct file *path_openat(int dfd, const char *pathname, +static struct file *path_openat(int dfd, struct filename *pathname,  		struct nameidata *nd, const struct open_flags *op, int flags)  {  	struct file *base = NULL; @@ -2885,12 +2951,12 @@ static struct file *path_openat(int dfd, const char *pathname,  	file->f_flags = op->open_flag; -	error = path_init(dfd, pathname, flags | LOOKUP_PARENT, nd, &base); +	error = path_init(dfd, pathname->name, flags | LOOKUP_PARENT, nd, &base);  	if (unlikely(error))  		goto out;  	current->total_link_count = 0; -	error = link_path_walk(pathname, nd); +	error = link_path_walk(pathname->name, nd);  	if (unlikely(error))  		goto out; @@ -2936,7 +3002,7 @@ out:  	return file;  } -struct file *do_filp_open(int dfd, const char *pathname, +struct file *do_filp_open(int dfd, struct filename *pathname,  		const struct open_flags *op, int flags)  {  	struct nameidata nd; @@ -2955,6 +3021,7 @@ struct file *do_file_open_root(struct dentry *dentry, struct vfsmount *mnt,  {  	struct nameidata nd;  	struct file *file; +	struct filename filename = { .name = name };  	nd.root.mnt = mnt;  	nd.root.dentry = dentry; @@ -2964,11 +3031,11 @@ struct file *do_file_open_root(struct dentry *dentry, struct vfsmount *mnt,  	if (dentry->d_inode->i_op->follow_link && op->intent & LOOKUP_OPEN)  		return ERR_PTR(-ELOOP); -	file = path_openat(-1, name, &nd, op, flags | LOOKUP_RCU); +	file = path_openat(-1, &filename, &nd, op, flags | LOOKUP_RCU);  	if (unlikely(file == ERR_PTR(-ECHILD))) -		file = path_openat(-1, name, &nd, op, flags); +		file = path_openat(-1, &filename, &nd, op, flags);  	if (unlikely(file == ERR_PTR(-ESTALE))) -		file = path_openat(-1, name, &nd, op, flags | LOOKUP_REVAL); +		file = path_openat(-1, &filename, &nd, op, flags | LOOKUP_REVAL);  	return file;  } @@ -3043,11 +3110,11 @@ EXPORT_SYMBOL(done_path_create);  struct dentry *user_path_create(int dfd, const char __user *pathname, struct path *path, int is_dir)  { -	char *tmp = getname(pathname); +	struct filename *tmp = getname(pathname);  	struct dentry *res;  	if (IS_ERR(tmp))  		return ERR_CAST(tmp); -	res = kern_path_create(dfd, tmp, path, is_dir); +	res = kern_path_create(dfd, tmp->name, path, is_dir);  	putname(tmp);  	return res;  } @@ -3252,13 +3319,13 @@ out:  static long do_rmdir(int dfd, const char __user *pathname)  {  	int error = 0; -	char * name; +	struct filename *name;  	struct dentry *dentry;  	struct nameidata nd; -	error = user_path_parent(dfd, pathname, &nd, &name); -	if (error) -		return error; +	name = user_path_parent(dfd, pathname, &nd); +	if (IS_ERR(name)) +		return PTR_ERR(name);  	switch(nd.last_type) {  	case LAST_DOTDOT: @@ -3347,14 +3414,14 @@ int vfs_unlink(struct inode *dir, struct dentry *dentry)  static long do_unlinkat(int dfd, const char __user *pathname)  {  	int error; -	char *name; +	struct filename *name;  	struct dentry *dentry;  	struct nameidata nd;  	struct inode *inode = NULL; -	error = user_path_parent(dfd, pathname, &nd, &name); -	if (error) -		return error; +	name = user_path_parent(dfd, pathname, &nd); +	if (IS_ERR(name)) +		return PTR_ERR(name);  	error = -EISDIR;  	if (nd.last_type != LAST_NORM) @@ -3438,7 +3505,7 @@ SYSCALL_DEFINE3(symlinkat, const char __user *, oldname,  		int, newdfd, const char __user *, newname)  {  	int error; -	char *from; +	struct filename *from;  	struct dentry *dentry;  	struct path path; @@ -3451,9 +3518,9 @@ SYSCALL_DEFINE3(symlinkat, const char __user *, oldname,  	if (IS_ERR(dentry))  		goto out_putname; -	error = security_path_symlink(&path, dentry, from); +	error = security_path_symlink(&path, dentry, from->name);  	if (!error) -		error = vfs_symlink(path.dentry->d_inode, dentry, from); +		error = vfs_symlink(path.dentry->d_inode, dentry, from->name);  	done_path_create(&path, dentry);  out_putname:  	putname(from); @@ -3733,17 +3800,21 @@ SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname,  	struct dentry *old_dentry, *new_dentry;  	struct dentry *trap;  	struct nameidata oldnd, newnd; -	char *from; -	char *to; +	struct filename *from; +	struct filename *to;  	int error; -	error = user_path_parent(olddfd, oldname, &oldnd, &from); -	if (error) +	from = user_path_parent(olddfd, oldname, &oldnd); +	if (IS_ERR(from)) { +		error = PTR_ERR(from);  		goto exit; +	} -	error = user_path_parent(newdfd, newname, &newnd, &to); -	if (error) +	to = user_path_parent(newdfd, newname, &newnd); +	if (IS_ERR(to)) { +		error = PTR_ERR(to);  		goto exit1; +	}  	error = -EXDEV;  	if (oldnd.path.mnt != newnd.path.mnt) @@ -3967,7 +4038,6 @@ EXPORT_SYMBOL(follow_down_one);  EXPORT_SYMBOL(follow_down);  EXPORT_SYMBOL(follow_up);  EXPORT_SYMBOL(get_write_access); /* nfsd */ -EXPORT_SYMBOL(getname);  EXPORT_SYMBOL(lock_rename);  EXPORT_SYMBOL(lookup_one_len);  EXPORT_SYMBOL(page_follow_link_light);  |