vfs: Add permission2 for filesystems with per mount permissions

This allows filesystems to use their mount private data to
influence the permssions they return in permission2. It has
been separated into a new call to avoid disrupting current
permission users.

Change-Id: I9d416e3b8b6eca84ef3e336bd2af89ddd51df6ca
Signed-off-by: Daniel Rosenberg <drosen@google.com>
This commit is contained in:
Daniel Rosenberg 2016-10-26 16:27:45 -07:00 committed by Joel16
parent fbcc4e23d7
commit f0af1e12c5
10 changed files with 166 additions and 73 deletions

View File

@ -1120,7 +1120,7 @@ EXPORT_SYMBOL(flush_old_exec);
void would_dump(struct linux_binprm *bprm, struct file *file)
{
if (inode_permission(file_inode(file), MAY_READ) < 0)
if (inode_permission2(file->f_path.mnt, file_inode(file), MAY_READ) < 0)
bprm->interp_flags |= BINPRM_FLAGS_ENFORCE_NONDUMP;
}
EXPORT_SYMBOL(would_dump);

View File

@ -356,9 +356,11 @@ int generic_permission(struct inode *inode, int mask)
* flag in inode->i_opflags, that says "this has not special
* permission function, use the fast case".
*/
static inline int do_inode_permission(struct inode *inode, int mask)
static inline int do_inode_permission(struct vfsmount *mnt, struct inode *inode, int mask)
{
if (unlikely(!(inode->i_opflags & IOP_FASTPERM))) {
if (likely(mnt && inode->i_op->permission2))
return inode->i_op->permission2(mnt, inode, mask);
if (likely(inode->i_op->permission))
return inode->i_op->permission(inode, mask);
@ -382,7 +384,7 @@ static inline int do_inode_permission(struct inode *inode, int mask)
* This does not check for a read-only file system. You probably want
* inode_permission().
*/
int __inode_permission(struct inode *inode, int mask)
int __inode_permission2(struct vfsmount *mnt, struct inode *inode, int mask)
{
int retval;
@ -394,7 +396,7 @@ int __inode_permission(struct inode *inode, int mask)
return -EACCES;
}
retval = do_inode_permission(inode, mask);
retval = do_inode_permission(mnt, inode, mask);
if (retval)
return retval;
@ -404,6 +406,13 @@ int __inode_permission(struct inode *inode, int mask)
return security_inode_permission(inode, mask);
}
EXPORT_SYMBOL(__inode_permission2);
int __inode_permission(struct inode *inode, int mask)
{
return __inode_permission2(NULL, inode, mask);
}
EXPORT_SYMBOL(__inode_permission);
/**
* sb_permission - Check superblock-level permissions
@ -437,15 +446,22 @@ static int sb_permission(struct super_block *sb, struct inode *inode, int mask)
*
* When checking for MAY_APPEND, MAY_WRITE must also be set in @mask.
*/
int inode_permission(struct inode *inode, int mask)
int inode_permission2(struct vfsmount *mnt, struct inode *inode, int mask)
{
int retval;
retval = sb_permission(inode->i_sb, inode, mask);
if (retval)
return retval;
return __inode_permission(inode, mask);
return __inode_permission2(mnt, inode, mask);
}
EXPORT_SYMBOL(inode_permission2);
int inode_permission(struct inode *inode, int mask)
{
return inode_permission2(NULL, inode, mask);
}
EXPORT_SYMBOL(inode_permission);
/**
* path_get - get a reference to a path
@ -1460,13 +1476,13 @@ static int lookup_slow(struct nameidata *nd, struct path *path)
static inline int may_lookup(struct nameidata *nd)
{
if (nd->flags & LOOKUP_RCU) {
int err = inode_permission(nd->inode, MAY_EXEC|MAY_NOT_BLOCK);
int err = inode_permission2(nd->path.mnt, nd->inode, MAY_EXEC|MAY_NOT_BLOCK);
if (err != -ECHILD)
return err;
if (unlazy_walk(nd, NULL))
return -ECHILD;
}
return inode_permission(nd->inode, MAY_EXEC);
return inode_permission2(nd->path.mnt, nd->inode, MAY_EXEC);
}
static inline int handle_dots(struct nameidata *nd, int type)
@ -1841,10 +1857,11 @@ static int path_init(int dfd, const char *name, unsigned int flags,
nd->depth = 0;
if (flags & LOOKUP_ROOT) {
struct inode *inode = nd->root.dentry->d_inode;
struct vfsmount *mnt = nd->root.mnt;
if (*name) {
if (!can_lookup(inode))
return -ENOTDIR;
retval = inode_permission(inode, MAY_EXEC);
retval = inode_permission2(mnt, inode, MAY_EXEC);
if (retval)
return retval;
}
@ -2096,6 +2113,7 @@ static struct dentry *lookup_hash(struct nameidata *nd)
/**
* lookup_one_len - filesystem helper to lookup single pathname component
* @name: pathname component to lookup
* @mnt: mount we are looking up on
* @base: base directory to lookup from
* @len: maximum length @len should be interpreted to
*
@ -2104,7 +2122,7 @@ static struct dentry *lookup_hash(struct nameidata *nd)
* nameidata argument is passed to the filesystem methods and a filesystem
* using this helper needs to be prepared for that.
*/
struct dentry *lookup_one_len(const char *name, struct dentry *base, int len)
struct dentry *lookup_one_len2(const char *name, struct vfsmount *mnt, struct dentry *base, int len)
{
struct qstr this;
unsigned int c;
@ -2138,12 +2156,19 @@ struct dentry *lookup_one_len(const char *name, struct dentry *base, int len)
return ERR_PTR(err);
}
err = inode_permission(base->d_inode, MAY_EXEC);
err = inode_permission2(mnt, base->d_inode, MAY_EXEC);
if (err)
return ERR_PTR(err);
return __lookup_hash(&this, base, 0);
}
EXPORT_SYMBOL(lookup_one_len2);
struct dentry *lookup_one_len(const char *name, struct dentry *base, int len)
{
return lookup_one_len2(name, NULL, base, len);
}
EXPORT_SYMBOL(lookup_one_len);
int user_path_at_empty(int dfd, const char __user *name, unsigned flags,
struct path *path, int *empty)
@ -2233,7 +2258,7 @@ static inline int check_sticky(struct inode *dir, struct inode *inode)
* 10. We don't allow removal of NFS sillyrenamed files; it's handled by
* nfs_async_unlink().
*/
static int may_delete(struct inode *dir,struct dentry *victim,int isdir)
static int may_delete(struct vfsmount *mnt, struct inode *dir,struct dentry *victim,int isdir)
{
int error;
@ -2243,7 +2268,7 @@ static int may_delete(struct inode *dir,struct dentry *victim,int isdir)
BUG_ON(victim->d_parent->d_inode != dir);
audit_inode_child(dir, victim, AUDIT_TYPE_CHILD_DELETE);
error = inode_permission(dir, MAY_WRITE | MAY_EXEC);
error = inode_permission2(mnt, dir, MAY_WRITE | MAY_EXEC);
if (error)
return error;
if (IS_APPEND(dir))
@ -2273,14 +2298,14 @@ static int may_delete(struct inode *dir,struct dentry *victim,int isdir)
* 3. We should have write and exec permissions on dir
* 4. We can't do it if dir is immutable (done in permission())
*/
static inline int may_create(struct inode *dir, struct dentry *child)
static inline int may_create(struct vfsmount *mnt, struct inode *dir, struct dentry *child)
{
audit_inode_child(dir, child, AUDIT_TYPE_CHILD_CREATE);
if (child->d_inode)
return -EEXIST;
if (IS_DEADDIR(dir))
return -ENOENT;
return inode_permission(dir, MAY_WRITE | MAY_EXEC);
return inode_permission2(mnt, dir, MAY_WRITE | MAY_EXEC);
}
/*
@ -2325,10 +2350,10 @@ void unlock_rename(struct dentry *p1, struct dentry *p2)
}
}
int vfs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
bool want_excl)
int vfs_create2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry,
umode_t mode, bool want_excl)
{
int error = may_create(dir, dentry);
int error = may_create(mnt, dir, dentry);
if (error)
return error;
@ -2349,10 +2374,19 @@ int vfs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
fsnotify_create(dir, dentry);
return error;
}
EXPORT_SYMBOL(vfs_create2);
int vfs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
bool want_excl)
{
return vfs_create2(NULL, dir, dentry, mode, want_excl);
}
EXPORT_SYMBOL(vfs_create);
static int may_open(struct path *path, int acc_mode, int flag)
{
struct dentry *dentry = path->dentry;
struct vfsmount *mnt = path->mnt;
struct inode *inode = dentry->d_inode;
int error;
@ -2381,7 +2415,7 @@ static int may_open(struct path *path, int acc_mode, int flag)
break;
}
error = inode_permission(inode, acc_mode);
error = inode_permission2(mnt, inode, acc_mode);
if (error)
return error;
@ -2437,7 +2471,7 @@ static int may_o_create(struct path *dir, struct dentry *dentry, umode_t mode)
if (error)
return error;
error = inode_permission(dir->dentry->d_inode, MAY_WRITE | MAY_EXEC);
error = inode_permission2(dir->mnt, dir->dentry->d_inode, MAY_WRITE | MAY_EXEC);
if (error)
return error;
@ -2625,6 +2659,7 @@ static int lookup_open(struct nameidata *nd, struct path *path,
bool got_write, int *opened)
{
struct dentry *dir = nd->path.dentry;
struct vfsmount *mnt = nd->path.mnt;
struct inode *dir_inode = dir->d_inode;
struct dentry *dentry;
int error;
@ -2672,7 +2707,7 @@ static int lookup_open(struct nameidata *nd, struct path *path,
error = security_path_mknod(&nd->path, dentry, mode, 0);
if (error)
goto out_dput;
error = vfs_create(dir->d_inode, dentry, mode,
error = vfs_create2(mnt, dir->d_inode, dentry, mode,
nd->flags & LOOKUP_EXCL);
if (error)
goto out_dput;
@ -3134,9 +3169,9 @@ struct dentry *user_path_create(int dfd, const char __user *pathname,
}
EXPORT_SYMBOL(user_path_create);
int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
int vfs_mknod2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
{
int error = may_create(dir, dentry);
int error = may_create(mnt, dir, dentry);
if (error)
return error;
@ -3167,6 +3202,13 @@ int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
return 0;
}
EXPORT_SYMBOL(vfs_mknod2);
int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
{
return vfs_mknod2(NULL, dir, dentry, mode, dev);
}
EXPORT_SYMBOL(vfs_mknod);
static int may_mknod(umode_t mode)
{
@ -3208,10 +3250,10 @@ retry:
goto out;
switch (mode & S_IFMT) {
case 0: case S_IFREG:
error = vfs_create(path.dentry->d_inode,dentry,mode,true);
error = vfs_create2(path.mnt, path.dentry->d_inode,dentry,mode,true);
break;
case S_IFCHR: case S_IFBLK:
error = vfs_mknod(path.dentry->d_inode,dentry,mode,
error = vfs_mknod2(path.mnt, path.dentry->d_inode,dentry,mode,
new_decode_dev(dev));
break;
case S_IFIFO: case S_IFSOCK:
@ -3232,9 +3274,9 @@ SYSCALL_DEFINE3(mknod, const char __user *, filename, umode_t, mode, unsigned, d
return sys_mknodat(AT_FDCWD, filename, mode, dev);
}
int vfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
int vfs_mkdir2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry, umode_t mode)
{
int error = may_create(dir, dentry);
int error = may_create(mnt, dir, dentry);
unsigned max_links = dir->i_sb->s_max_links;
if (error)
@ -3256,6 +3298,13 @@ int vfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
fsnotify_mkdir(dir, dentry);
return error;
}
EXPORT_SYMBOL(vfs_mkdir2);
int vfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
{
return vfs_mkdir2(NULL, dir, dentry, mode);
}
EXPORT_SYMBOL(vfs_mkdir);
SYSCALL_DEFINE3(mkdirat, int, dfd, const char __user *, pathname, umode_t, mode)
{
@ -3273,7 +3322,7 @@ retry:
mode &= ~current_umask();
error = security_path_mkdir(&path, dentry, mode);
if (!error)
error = vfs_mkdir(path.dentry->d_inode, dentry, mode);
error = vfs_mkdir2(path.mnt, path.dentry->d_inode, dentry, mode);
done_path_create(&path, dentry);
if (retry_estale(error, lookup_flags)) {
lookup_flags |= LOOKUP_REVAL;
@ -3311,9 +3360,9 @@ void dentry_unhash(struct dentry *dentry)
spin_unlock(&dentry->d_lock);
}
int vfs_rmdir(struct inode *dir, struct dentry *dentry)
int vfs_rmdir2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry)
{
int error = may_delete(dir, dentry, 1);
int error = may_delete(mnt, dir, dentry, 1);
if (error)
return error;
@ -3347,6 +3396,13 @@ out:
d_delete(dentry);
return error;
}
EXPORT_SYMBOL(vfs_rmdir2);
int vfs_rmdir(struct inode *dir, struct dentry *dentry)
{
return vfs_rmdir2(NULL, dir, dentry);
}
EXPORT_SYMBOL(vfs_rmdir);
static long do_rmdir(int dfd, const char __user *pathname)
{
@ -3389,7 +3445,7 @@ retry:
error = security_path_rmdir(&nd.path, dentry);
if (error)
goto exit3;
error = vfs_rmdir(nd.path.dentry->d_inode, dentry);
error = vfs_rmdir2(nd.path.mnt, nd.path.dentry->d_inode, dentry);
exit3:
dput(dentry);
exit2:
@ -3410,9 +3466,9 @@ SYSCALL_DEFINE1(rmdir, const char __user *, pathname)
return do_rmdir(AT_FDCWD, pathname);
}
int vfs_unlink(struct inode *dir, struct dentry *dentry)
int vfs_unlink2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry)
{
int error = may_delete(dir, dentry, 0);
int error = may_delete(mnt, dir, dentry, 0);
if (error)
return error;
@ -3441,6 +3497,13 @@ int vfs_unlink(struct inode *dir, struct dentry *dentry)
return error;
}
EXPORT_SYMBOL(vfs_unlink2);
int vfs_unlink(struct inode *dir, struct dentry *dentry)
{
return vfs_unlink2(NULL, dir, dentry);
}
EXPORT_SYMBOL(vfs_unlink);
/*
* Make sure that the actual truncation of the file will occur outside its
@ -3484,7 +3547,7 @@ retry:
error = security_path_unlink(&nd.path, dentry);
if (error)
goto exit2;
error = vfs_unlink(nd.path.dentry->d_inode, dentry);
error = vfs_unlink2(nd.path.mnt, nd.path.dentry->d_inode, dentry);
exit2:
dput(dentry);
}
@ -3524,9 +3587,9 @@ SYSCALL_DEFINE1(unlink, const char __user *, pathname)
return do_unlinkat(AT_FDCWD, pathname);
}
int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname)
int vfs_symlink2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry, const char *oldname)
{
int error = may_create(dir, dentry);
int error = may_create(mnt, dir, dentry);
if (error)
return error;
@ -3543,6 +3606,13 @@ int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname)
fsnotify_create(dir, dentry);
return error;
}
EXPORT_SYMBOL(vfs_symlink2);
int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname)
{
return vfs_symlink2(NULL, dir, dentry, oldname);
}
EXPORT_SYMBOL(vfs_symlink);
SYSCALL_DEFINE3(symlinkat, const char __user *, oldname,
int, newdfd, const char __user *, newname)
@ -3564,7 +3634,7 @@ retry:
error = security_path_symlink(&path, dentry, from->name);
if (!error)
error = vfs_symlink(path.dentry->d_inode, dentry, from->name);
error = vfs_symlink2(path.mnt, path.dentry->d_inode, dentry, from->name);
done_path_create(&path, dentry);
if (retry_estale(error, lookup_flags)) {
lookup_flags |= LOOKUP_REVAL;
@ -3580,7 +3650,7 @@ SYSCALL_DEFINE2(symlink, const char __user *, oldname, const char __user *, newn
return sys_symlinkat(oldname, AT_FDCWD, newname);
}
int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry)
int vfs_link2(struct vfsmount *mnt, struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry)
{
struct inode *inode = old_dentry->d_inode;
unsigned max_links = dir->i_sb->s_max_links;
@ -3589,7 +3659,7 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de
if (!inode)
return -ENOENT;
error = may_create(dir, new_dentry);
error = may_create(mnt, dir, new_dentry);
if (error)
return error;
@ -3623,6 +3693,13 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de
fsnotify_link(dir, inode, new_dentry);
return error;
}
EXPORT_SYMBOL(vfs_link2);
int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry)
{
return vfs_link2(NULL, old_dentry, dir, new_dentry);
}
EXPORT_SYMBOL(vfs_link);
/*
* Hardlinks are often used in delicate situations. We avoid
@ -3676,7 +3753,7 @@ retry:
error = security_path_link(old_path.dentry, &new_path, new_dentry);
if (error)
goto out_dput;
error = vfs_link(old_path.dentry, new_path.dentry->d_inode, new_dentry);
error = vfs_link2(old_path.mnt, old_path.dentry, new_path.dentry->d_inode, new_dentry);
out_dput:
done_path_create(&new_path, new_dentry);
if (retry_estale(error, how)) {
@ -3722,8 +3799,9 @@ SYSCALL_DEFINE2(link, const char __user *, oldname, const char __user *, newname
* ->i_mutex on parents, which works but leads to some truly excessive
* locking].
*/
static int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry)
static int vfs_rename_dir(struct vfsmount *mnt,
struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry)
{
int error = 0;
struct inode *target = new_dentry->d_inode;
@ -3734,7 +3812,7 @@ static int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry,
* we'll need to flip '..'.
*/
if (new_dir != old_dir) {
error = inode_permission(old_dentry->d_inode, MAY_WRITE);
error = inode_permission2(mnt, old_dentry->d_inode, MAY_WRITE);
if (error)
return error;
}
@ -3809,7 +3887,8 @@ out:
return error;
}
int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
int vfs_rename2(struct vfsmount *mnt,
struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry)
{
int error;
@ -3819,14 +3898,14 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
if (old_dentry->d_inode == new_dentry->d_inode)
return 0;
error = may_delete(old_dir, old_dentry, is_dir);
error = may_delete(mnt, old_dir, old_dentry, is_dir);
if (error)
return error;
if (!new_dentry->d_inode)
error = may_create(new_dir, new_dentry);
error = may_create(mnt, new_dir, new_dentry);
else
error = may_delete(new_dir, new_dentry, is_dir);
error = may_delete(mnt, new_dir, new_dentry, is_dir);
if (error)
return error;
@ -3836,7 +3915,7 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
old_name = fsnotify_oldname_init(old_dentry->d_name.name);
if (is_dir)
error = vfs_rename_dir(old_dir,old_dentry,new_dir,new_dentry);
error = vfs_rename_dir(mnt, old_dir,old_dentry,new_dir,new_dentry);
else
error = vfs_rename_other(old_dir,old_dentry,new_dir,new_dentry);
if (!error)
@ -3846,6 +3925,14 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
return error;
}
EXPORT_SYMBOL(vfs_rename2);
int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry)
{
return vfs_rename2(NULL, old_dir, old_dentry, new_dir, new_dentry);
}
EXPORT_SYMBOL(vfs_rename);
SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname,
int, newdfd, const char __user *, newname)
@ -3928,7 +4015,7 @@ retry:
&newnd.path, new_dentry);
if (error)
goto exit5;
error = vfs_rename(old_dir->d_inode, old_dentry,
error = vfs_rename2(oldnd.path.mnt, old_dir->d_inode, old_dentry,
new_dir->d_inode, new_dentry);
exit5:
dput(new_dentry);
@ -4102,7 +4189,6 @@ EXPORT_SYMBOL(follow_down);
EXPORT_SYMBOL(follow_up);
EXPORT_SYMBOL(get_write_access); /* nfsd */
EXPORT_SYMBOL(lock_rename);
EXPORT_SYMBOL(lookup_one_len);
EXPORT_SYMBOL(page_follow_link_light);
EXPORT_SYMBOL(page_put_link);
EXPORT_SYMBOL(page_readlink);
@ -4111,18 +4197,9 @@ EXPORT_SYMBOL(page_symlink);
EXPORT_SYMBOL(page_symlink_inode_operations);
EXPORT_SYMBOL(kern_path);
EXPORT_SYMBOL(vfs_path_lookup);
EXPORT_SYMBOL(inode_permission);
EXPORT_SYMBOL(unlock_rename);
EXPORT_SYMBOL(vfs_create);
EXPORT_SYMBOL(vfs_follow_link);
EXPORT_SYMBOL(vfs_link);
EXPORT_SYMBOL(vfs_mkdir);
EXPORT_SYMBOL(vfs_mknod);
EXPORT_SYMBOL(generic_permission);
EXPORT_SYMBOL(vfs_readlink);
EXPORT_SYMBOL(vfs_rename);
EXPORT_SYMBOL(vfs_rmdir);
EXPORT_SYMBOL(vfs_symlink);
EXPORT_SYMBOL(vfs_unlink);
EXPORT_SYMBOL(dentry_unhash);
EXPORT_SYMBOL(generic_readlink);

View File

@ -490,7 +490,7 @@ static int fanotify_find_path(int dfd, const char __user *filename,
}
/* you can only watch an inode if you have read permissions on it */
ret = inode_permission(path->dentry->d_inode, MAY_READ);
ret = inode_permission2(path->mnt, path->dentry->d_inode, MAY_READ);
if (ret)
path_put(path);
out:

View File

@ -349,7 +349,7 @@ static int inotify_find_inode(const char __user *dirname, struct path *path, uns
if (error)
return error;
/* you can only watch an inode if you have read permissions on it */
error = inode_permission(path->dentry->d_inode, MAY_READ);
error = inode_permission2(path->mnt, path->dentry->d_inode, MAY_READ);
if (error)
path_put(path);
return error;

View File

@ -65,9 +65,11 @@ int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,
long vfs_truncate(struct path *path, loff_t length)
{
struct inode *inode;
struct vfsmount *mnt;
long error;
inode = path->dentry->d_inode;
mnt = path->mnt;
/* For directories it's -EISDIR, for other non-regulars - -EINVAL */
if (S_ISDIR(inode->i_mode))
@ -79,7 +81,7 @@ long vfs_truncate(struct path *path, loff_t length)
if (error)
goto out;
error = inode_permission(inode, MAY_WRITE);
error = inode_permission2(mnt, inode, MAY_WRITE);
if (error)
goto mnt_drop_write_and_out;
@ -302,6 +304,7 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
struct cred *override_cred;
struct path path;
struct inode *inode;
struct vfsmount *mnt;
int res;
unsigned int lookup_flags = LOOKUP_FOLLOW;
@ -332,6 +335,7 @@ retry:
goto out;
inode = path.dentry->d_inode;
mnt = path.mnt;
if ((mode & MAY_EXEC) && S_ISREG(inode->i_mode)) {
/*
@ -343,7 +347,7 @@ retry:
goto out_path_release;
}
res = inode_permission(inode, mode | MAY_ACCESS);
res = inode_permission2(mnt, inode, mode | MAY_ACCESS);
/* SuS v2 requires we report a read only fs too */
if (res || !(mode & S_IWOTH) || special_file(inode->i_mode))
goto out_path_release;
@ -387,7 +391,7 @@ retry:
if (error)
goto out;
error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_CHDIR);
error = inode_permission2(path.mnt, path.dentry->d_inode, MAY_EXEC | MAY_CHDIR);
if (error)
goto dput_and_out;
@ -407,6 +411,7 @@ SYSCALL_DEFINE1(fchdir, unsigned int, fd)
{
struct fd f = fdget_raw(fd);
struct inode *inode;
struct vfsmount *mnt;
int error = -EBADF;
error = -EBADF;
@ -414,12 +419,13 @@ SYSCALL_DEFINE1(fchdir, unsigned int, fd)
goto out;
inode = file_inode(f.file);
mnt = f.file->f_path.mnt;
error = -ENOTDIR;
if (!S_ISDIR(inode->i_mode))
goto out_putf;
error = inode_permission(inode, MAY_EXEC | MAY_CHDIR);
error = inode_permission2(mnt, inode, MAY_EXEC | MAY_CHDIR);
if (!error)
set_fs_pwd(current->fs, &f.file->f_path);
out_putf:
@ -438,7 +444,7 @@ retry:
if (error)
goto out;
error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_CHDIR);
error = inode_permission2(path.mnt, path.dentry->d_inode, MAY_EXEC | MAY_CHDIR);
if (error)
goto dput_and_out;

View File

@ -96,7 +96,7 @@ static int utimes_common(struct path *path, struct timespec *times)
goto mnt_drop_write_and_out;
if (!inode_owner_or_capable(inode)) {
error = inode_permission(inode, MAY_WRITE);
error = inode_permission2(path->mnt, inode, MAY_WRITE);
if (error)
goto mnt_drop_write_and_out;
}

View File

@ -1457,13 +1457,20 @@ extern bool inode_owner_or_capable(const struct inode *inode);
* VFS helper functions..
*/
extern int vfs_create(struct inode *, struct dentry *, umode_t, bool);
extern int vfs_create2(struct vfsmount *, struct inode *, struct dentry *, umode_t, bool);
extern int vfs_mkdir(struct inode *, struct dentry *, umode_t);
extern int vfs_mkdir2(struct vfsmount *, struct inode *, struct dentry *, umode_t);
extern int vfs_mknod(struct inode *, struct dentry *, umode_t, dev_t);
extern int vfs_mknod2(struct vfsmount *, struct inode *, struct dentry *, umode_t, dev_t);
extern int vfs_symlink(struct inode *, struct dentry *, const char *);
extern int vfs_link(struct dentry *, struct inode *, struct dentry *);
extern int vfs_link2(struct vfsmount *, struct dentry *, struct inode *, struct dentry *);
extern int vfs_rmdir(struct inode *, struct dentry *);
extern int vfs_rmdir2(struct vfsmount *, struct inode *, struct dentry *);
extern int vfs_unlink(struct inode *, struct dentry *);
extern int vfs_unlink2(struct vfsmount *, struct inode *, struct dentry *);
extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *);
extern int vfs_rename2(struct vfsmount *, struct inode *, struct dentry *, struct inode *, struct dentry *);
/*
* VFS dentry helper functions.
@ -1568,6 +1575,7 @@ struct inode_operations {
struct dentry * (*lookup) (struct inode *,struct dentry *, unsigned int);
void * (*follow_link) (struct dentry *, struct nameidata *);
int (*permission) (struct inode *, int);
int (*permission2) (struct vfsmount *, struct inode *, int);
struct posix_acl * (*get_acl)(struct inode *, int);
int (*readlink) (struct dentry *, char __user *,int);
@ -2234,6 +2242,7 @@ extern sector_t bmap(struct inode *, sector_t);
#endif
extern int notify_change(struct dentry *, struct iattr *);
extern int inode_permission(struct inode *, int);
extern int inode_permission2(struct vfsmount *, struct inode *, int);
extern int generic_permission(struct inode *, int);
static inline bool execute_ok(struct inode *inode)

View File

@ -74,6 +74,7 @@ extern int vfs_path_lookup(struct dentry *, struct vfsmount *,
const char *, unsigned int, struct path *);
extern struct dentry *lookup_one_len(const char *, struct dentry *, int);
extern struct dentry *lookup_one_len2(const char *, struct vfsmount *mnt, struct dentry *, int);
extern int follow_down_one(struct path *);
extern int follow_down(struct path *);

View File

@ -754,7 +754,7 @@ static struct file *do_create(struct ipc_namespace *ipc_ns, struct inode *dir,
}
mode &= ~current_umask();
ret = vfs_create(dir, path->dentry, mode, true);
ret = vfs_create2(path->mnt, dir, path->dentry, mode, true);
path->dentry->d_fsdata = NULL;
if (ret)
return ERR_PTR(ret);
@ -770,7 +770,7 @@ static struct file *do_open(struct path *path, int oflag)
if ((oflag & O_ACCMODE) == (O_RDWR | O_WRONLY))
return ERR_PTR(-EINVAL);
acc = oflag2acc[oflag & O_ACCMODE];
if (inode_permission(path->dentry->d_inode, acc))
if (inode_permission2(path->mnt, path->dentry->d_inode, acc))
return ERR_PTR(-EACCES);
return dentry_open(path, oflag, current_cred());
}
@ -803,7 +803,7 @@ SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, umode_t, mode,
ro = mnt_want_write(mnt); /* we'll drop it in any case */
error = 0;
mutex_lock(&root->d_inode->i_mutex);
path.dentry = lookup_one_len(name->name, root, strlen(name->name));
path.dentry = lookup_one_len2(name->name, mnt, root, strlen(name->name));
if (IS_ERR(path.dentry)) {
error = PTR_ERR(path.dentry);
goto out_putfd;
@ -874,7 +874,7 @@ SYSCALL_DEFINE1(mq_unlink, const char __user *, u_name)
if (err)
goto out_name;
mutex_lock_nested(&mnt->mnt_root->d_inode->i_mutex, I_MUTEX_PARENT);
dentry = lookup_one_len(name->name, mnt->mnt_root,
dentry = lookup_one_len2(name->name, mnt, mnt->mnt_root,
strlen(name->name));
if (IS_ERR(dentry)) {
err = PTR_ERR(dentry);
@ -886,7 +886,7 @@ SYSCALL_DEFINE1(mq_unlink, const char __user *, u_name)
err = -ENOENT;
} else {
ihold(inode);
err = vfs_unlink(dentry->d_parent->d_inode, dentry);
err = vfs_unlink2(mnt, dentry->d_parent->d_inode, dentry);
}
dput(dentry);

View File

@ -105,7 +105,7 @@ struct dentry *securityfs_create_file(const char *name, umode_t mode,
dir = parent->d_inode;
mutex_lock(&dir->i_mutex);
dentry = lookup_one_len(name, parent, strlen(name));
dentry = lookup_one_len2(name, mount, parent, strlen(name));
if (IS_ERR(dentry))
goto out;