Re: [PATCH v2] ceph: fix posix ACL hooks

Linus Torvalds
2014-02-03T21:03:37Z

[-- Attachment #1: Type: text/plain, Size: 2212 bytes --]

On Mon, Feb 3, 2014 at 2:29 AM, Christoph Hellwig <hch@infradead.org> wrote:
> On Thu, Jan 30, 2014 at 02:01:38PM -0800, Linus Torvalds wrote:
>> In the end, all the original call-sites should have a dentry, and none
>> of this is "fundamental". But you're right, it looks like an absolute
>> nightmare to add the dentry pointer through the whole chain. Damn.
>
> It's not just ceph. 9p fundamentally needs it and I really want to
> convert 9p to the new code so that we can get rid of the lower level
> interfaces entirely and eventually move ACL dispatching entirely
> into the VFS.

Hmm. I spent some time actually seeing how painful it would be, and I
think we can do it.

The attached patch seems to work for me - it does *not* make any
actual semantic changes, but it pushes the dentry pointer into the
filesystems for the permission checking, and the
vfs_{create,mkdir,mknot,symlink,link,rmdir,unlink,rename} helper
functions.

NOTE! It does *not* actually push them into the ACL functions, and in
fact it doesn't even push it down to "generic_permission()" yet. But
it would be a prerequisite for doing so.

And interestingly, replacing the inode pointer with a dentry pointer
ended up actually simplifying several of the call-sites, so while
doing this patch I got the feeling that this was the better interface
anyway, and we should have done this long ago.

Now, to be honest, pushing it down one more level (to
generic_permission()) will actually start causing some trouble. In
particular, gfs2_permission() fundamentally does not have a dentry for
several of the callers.

But aside from being pretty large, this patch looks pretty good to me.
And as I mentioned, I suspect it would actually be the right thing to
do even if we don't go any futher. And modulo gfs2, it would make it
trivial to push down the dentry pointer to generic_permission and then
to the acl lookup code.

What do you think? I guess this patch could be split up into two: one
that does the "vfs_xyz()" helper functions, and another that does the
inode_permission() change. I tied them together mainly because I
started with the inode_permission() change, and that required the
vfs_xyz() change.

Linus

[-- Attachment #2: patch.diff --]
[-- Type: text/plain, Size: 67416 bytes --]

drivers/base/devtmpfs.c | 8 +-
drivers/staging/lustre/lustre/llite/file.c | 6 +-
.../staging/lustre/lustre/llite/llite_internal.h | 2 +-
drivers/staging/lustre/lustre/lvfs/lvfs_linux.c | 4 +-
fs/afs/internal.h | 2 +-
fs/afs/security.c | 2 +-
fs/bad_inode.c | 2 +-
fs/btrfs/inode.c | 2 +-
fs/btrfs/ioctl.c | 16 +--
fs/cachefiles/namei.c | 11 +-
fs/ceph/inode.c | 2 +-
fs/ceph/super.h | 2 +-
fs/cifs/cifsfs.c | 2 +-
fs/coda/coda_linux.h | 2 +-
fs/coda/dir.c | 2 +-
fs/coda/pioctl.c | 4 +-
fs/ecryptfs/inode.c | 26 ++---
fs/exec.c | 2 +-
fs/fuse/dir.c | 2 +-
fs/gfs2/inode.c | 11 +-
fs/hostfs/hostfs_kern.c | 2 +-
fs/internal.h | 1 -
fs/kernfs/inode.c | 2 +-
fs/kernfs/kernfs-internal.h | 2 +-
fs/namei.c | 126 ++++++++++++---------
fs/ncpfs/ioctl.c | 6 +-
fs/nfs/dir.c | 2 +-
fs/nfsd/nfs4recover.c | 6 +-
fs/nfsd/nfsfh.c | 2 +-
fs/nfsd/vfs.c | 32 ++----
fs/nilfs2/inode.c | 2 +-
fs/nilfs2/nilfs.h | 2 +-
fs/notify/fanotify/fanotify_user.c | 2 +-
fs/notify/inotify/inotify_user.c | 2 +-
fs/ocfs2/file.c | 2 +-
fs/ocfs2/file.h | 2 +-
fs/ocfs2/refcounttree.c | 15 ++-
fs/open.c | 14 ++-
fs/proc/base.c | 2 +-
fs/proc/fd.c | 2 +-
fs/proc/fd.h | 2 +-
fs/proc/proc_sysctl.c | 2 +-
fs/reiserfs/xattr.c | 2 +-
fs/reiserfs/xattr.h | 2 +-
fs/udf/file.c | 2 +-
fs/utimes.c | 2 +-
fs/xattr.c | 12 +-
include/linux/fs.h | 20 ++--
include/linux/nfs_fs.h | 2 +-
ipc/mqueue.c | 10 +-
kernel/sys.c | 2 +-
mm/memcontrol.c | 2 +-
net/unix/af_unix.c | 4 +-
virt/kvm/assigned-dev.c | 2 +-
54 files changed, 210 insertions(+), 192 deletions(-)

diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c
index 25798db14553..fb81de9a78a3 100644
--- a/drivers/base/devtmpfs.c
+++ b/drivers/base/devtmpfs.c
@@ -157,7 +157,7 @@ static int dev_mkdir(const char *name, umode_t mode)
if (IS_ERR(dentry))
return PTR_ERR(dentry);

- err = vfs_mkdir(path.dentry->d_inode, dentry, mode);
+ err = vfs_mkdir(path.dentry, dentry, mode);
if (!err)
/* mark as kernel-created inode */
dentry->d_inode->i_private = &thread;
@@ -207,7 +207,7 @@ static int handle_create(const char *nodename, umode_t mode, kuid_t uid,
if (IS_ERR(dentry))
return PTR_ERR(dentry);

- err = vfs_mknod(path.dentry->d_inode, dentry, mode, dev->devt);
+ err = vfs_mknod(path.dentry, dentry, mode, dev->devt);
if (!err) {
struct iattr newattrs;

@@ -237,7 +237,7 @@ static int dev_rmdir(const char *name)
return PTR_ERR(dentry);
if (dentry->d_inode) {
if (dentry->d_inode->i_private == &thread)
- err = vfs_rmdir(parent.dentry->d_inode, dentry);
+ err = vfs_rmdir(parent.dentry, dentry);
else
err = -EPERM;
} else {
@@ -324,7 +324,7 @@ static int handle_remove(const char *nodename, struct device *dev)
mutex_lock(&dentry->d_inode->i_mutex);
notify_change(dentry, &newattrs, NULL);
mutex_unlock(&dentry->d_inode->i_mutex);
- err = vfs_unlink(parent.dentry->d_inode, dentry, NULL);
+ err = vfs_unlink(parent.dentry, dentry, NULL);
if (!err || err == -ENOENT)
deleted = 1;
}
diff --git a/drivers/staging/lustre/lustre/llite/file.c b/drivers/staging/lustre/lustre/llite/file.c
index c12821aedc2f..7a2e5f9b2e1d 100644
--- a/drivers/staging/lustre/lustre/llite/file.c
+++ b/drivers/staging/lustre/lustre/llite/file.c
@@ -1951,8 +1951,8 @@ static int ll_swap_layouts(struct file *file1, struct file *file2,
if (!S_ISREG(llss->inode2->i_mode))
GOTO(free, rc = -EINVAL);

- if (inode_permission(llss->inode1, MAY_WRITE) ||
- inode_permission(llss->inode2, MAY_WRITE))
+ if (inode_permission(file1->f_dentry, llss->inode1, MAY_WRITE) ||
+ inode_permission(file2->f_dentry, llss->inode2, MAY_WRITE))
GOTO(free, rc = -EPERM);

if (llss->inode2->i_sb != llss->inode1->i_sb)
@@ -3079,7 +3079,7 @@ struct posix_acl * ll_get_acl(struct inode *inode, int type)
}

-int ll_inode_permission(struct inode *inode, int mask)
+int ll_inode_permission(struct dentry *dentry, struct inode *inode, int mask)
{
int rc = 0;

diff --git a/drivers/staging/lustre/lustre/llite/llite_internal.h b/drivers/staging/lustre/lustre/llite/llite_internal.h
index 7ee5c02783f9..b767e484dfe9 100644
--- a/drivers/staging/lustre/lustre/llite/llite_internal.h
+++ b/drivers/staging/lustre/lustre/llite/llite_internal.h
@@ -799,7 +799,7 @@ int ll_getattr(struct vfsmount *mnt, struct dentry *de, struct kstat *stat);
struct ll_file_data *ll_file_data_get(void);
struct posix_acl * ll_get_acl(struct inode *inode, int type);

-int ll_inode_permission(struct inode *inode, int mask);
+int ll_inode_permission(struct dentry *dentry, struct inode *inode, int mask);

int ll_lov_setstripe_ea_info(struct inode *inode, struct file *file,
int flags, struct lov_user_md *lum,
diff --git a/drivers/staging/lustre/lustre/lvfs/lvfs_linux.c b/drivers/staging/lustre/lustre/lvfs/lvfs_linux.c
index 428ffd8c37b7..c4aa260def19 100644
--- a/drivers/staging/lustre/lustre/lvfs/lvfs_linux.c
+++ b/drivers/staging/lustre/lustre/lvfs/lvfs_linux.c
@@ -222,8 +222,8 @@ int lustre_rename(struct dentry *dir, struct vfsmount *mnt,
if (IS_ERR(dchild_new))
GOTO(put_old, err = PTR_ERR(dchild_new));

- err = ll_vfs_rename(dir->d_inode, dchild_old, mnt,
- dir->d_inode, dchild_new, mnt, NULL);
+ err = ll_vfs_rename(dir, dchild_old, mnt,
+ dir, dchild_new, mnt, NULL);

dput(dchild_new);
put_old:
diff --git a/fs/afs/internal.h b/fs/afs/internal.h
index 6621f8008122..63a03b555fd3 100644
--- a/fs/afs/internal.h
+++ b/fs/afs/internal.h
@@ -626,7 +626,7 @@ extern void afs_clear_permits(struct afs_vnode *);
extern void afs_cache_permit(struct afs_vnode *, struct key *, long);
extern void afs_zap_permits(struct rcu_head *);
extern struct key *afs_request_key(struct afs_cell *);
-extern int afs_permission(struct inode *, int);
+extern int afs_permission(struct dentry *, struct inode *, int);

/*
* server.c
diff --git a/fs/afs/security.c b/fs/afs/security.c
index 8d010422dc89..cc34a41bab04 100644
--- a/fs/afs/security.c
+++ b/fs/afs/security.c
@@ -285,7 +285,7 @@ static int afs_check_permit(struct afs_vnode *vnode, struct key *key,
* - AFS ACLs are attached to directories only, and a file is controlled by its
* parent directory's ACL
*/
-int afs_permission(struct inode *inode, int mask)
+int afs_permission(struct dentry *dentry, struct inode *inode, int mask)
{
struct afs_vnode *vnode = AFS_FS_I(inode);
afs_access_t uninitialized_var(access);
diff --git a/fs/bad_inode.c b/fs/bad_inode.c
index 7c93953030fb..cf6181b12add 100644
--- a/fs/bad_inode.c
+++ b/fs/bad_inode.c
@@ -230,7 +230,7 @@ static int bad_inode_readlink(struct dentry *dentry, char __user *buffer,
return -EIO;
}

-static int bad_inode_permission(struct inode *inode, int mask)
+static int bad_inode_permission(struct dentry *dentry, struct inode *inode, int mask)
{
return -EIO;
}
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 5c4ab9c18940..546a6872e841 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -8817,7 +8817,7 @@ static int btrfs_set_page_dirty(struct page *page)
return __set_page_dirty_nobuffers(page);
}

-static int btrfs_permission(struct inode *inode, int mask)
+static int btrfs_permission(struct dentry *dentry, struct inode *inode, int mask)
{
struct btrfs_root *root = BTRFS_I(inode)->root;
umode_t mode = inode->i_mode;
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index b0134892dc70..d07fdc9b8c9e 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -716,9 +716,10 @@ static inline int btrfs_check_sticky(struct inode *dir, struct inode *inode)
* nfs_async_unlink().
*/

-static int btrfs_may_delete(struct inode *dir, struct dentry *victim, int isdir)
+static int btrfs_may_delete(struct dentry *parent, struct dentry *victim, int isdir)
{
int error;
+ struct inode *dir = parent->d_inode;

if (!victim->d_inode)
return -ENOENT;
@@ -726,7 +727,7 @@ static int btrfs_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_permission(parent, dir, MAY_WRITE | MAY_EXEC);
if (error)
return error;
if (IS_APPEND(dir))
@@ -750,13 +751,14 @@ static int btrfs_may_delete(struct inode *dir, struct dentry *victim, int isdir)
}

/* copy of may_create in fs/namei.c() */
-static inline int btrfs_may_create(struct inode *dir, struct dentry *child)
+static inline int btrfs_may_create(struct dentry *parent, struct dentry *child)
{
+ struct inode *dir = parent->d_inode;
if (child->d_inode)
return -EEXIST;
if (IS_DEADDIR(dir))
return -ENOENT;
- return inode_permission(dir, MAY_WRITE | MAY_EXEC);
+ return inode_permission(parent, dir, MAY_WRITE | MAY_EXEC);
}

/*
@@ -787,7 +789,7 @@ static noinline int btrfs_mksubvol(struct path *parent,
if (dentry->d_inode)
goto out_dput;

- error = btrfs_may_create(dir, dentry);
+ error = btrfs_may_create(parent->dentry, dentry);
if (error)
goto out_dput;

@@ -2213,13 +2215,13 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,
if (root == dest)
goto out_dput;

- err = inode_permission(inode, MAY_WRITE | MAY_EXEC);
+ err = inode_permission(dentry, inode, MAY_WRITE | MAY_EXEC);
if (err)
goto out_dput;
}

/* check if subvolume may be deleted by a user */
- err = btrfs_may_delete(dir, dentry, 1);
+ err = btrfs_may_delete(parent, dentry, 1);
if (err)
goto out_dput;

diff --git a/fs/cachefiles/namei.c b/fs/cachefiles/namei.c
index ca65f39dc8dc..c4b6712909e3 100644
--- a/fs/cachefiles/namei.c
+++ b/fs/cachefiles/namei.c
@@ -294,7 +294,7 @@ static int cachefiles_bury_object(struct cachefiles_cache *cache,
if (ret < 0) {
cachefiles_io_error(cache, "Unlink security error");
} else {
- ret = vfs_unlink(dir->d_inode, rep, NULL);
+ ret = vfs_unlink(dir, rep, NULL);

if (preemptive)
cachefiles_mark_object_buried(cache, rep);
@@ -395,8 +395,7 @@ try_again:
if (ret < 0) {
cachefiles_io_error(cache, "Rename security error %d", ret);
} else {
- ret = vfs_rename(dir->d_inode, rep,
- cache->graveyard->d_inode, grave, NULL);
+ ret = vfs_rename(dir, rep, cache->graveyard, grave, NULL);
if (ret != 0 && ret != -ENOMEM)
cachefiles_io_error(cache,
"Rename failed with error %d", ret);
@@ -537,7 +536,7 @@ lookup_again:
if (ret < 0)
goto create_error;
start = jiffies;
- ret = vfs_mkdir(dir->d_inode, next, 0);
+ ret = vfs_mkdir(dir, next, 0);
cachefiles_hist(cachefiles_mkdir_histogram, start);
if (ret < 0)
goto create_error;
@@ -566,7 +565,7 @@ lookup_again:
if (ret < 0)
goto create_error;
start = jiffies;
- ret = vfs_create(dir->d_inode, next, S_IFREG, true);
+ ret = vfs_create(dir, next, S_IFREG, true);
cachefiles_hist(cachefiles_create_histogram, start);
if (ret < 0)
goto create_error;
@@ -755,7 +754,7 @@ struct dentry *cachefiles_get_directory(struct cachefiles_cache *cache,
ret = security_path_mkdir(&path, subdir, 0700);
if (ret < 0)
goto mkdir_error;
- ret = vfs_mkdir(dir->d_inode, subdir, 0700);
+ ret = vfs_mkdir(dir, subdir, 0700);
if (ret < 0)
goto mkdir_error;

diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
index 32d519d8a2e2..04db1774cb2d 100644
--- a/fs/ceph/inode.c
+++ b/fs/ceph/inode.c
@@ -1875,7 +1875,7 @@ int ceph_do_getattr(struct inode *inode, int mask)
* Check inode permissions. We verify we have a valid value for
* the AUTH cap, then call the generic handler.
*/
-int ceph_permission(struct inode *inode, int mask)
+int ceph_permission(struct dentry *dentry, struct inode *inode, int mask)
{
int err;

diff --git a/fs/ceph/super.h b/fs/ceph/super.h
index 19793b56d0a7..6215dc38b676 100644
--- a/fs/ceph/super.h
+++ b/fs/ceph/super.h
@@ -716,7 +716,7 @@ extern void ceph_queue_invalidate(struct inode *inode);
extern void ceph_queue_writeback(struct inode *inode);

extern int ceph_do_getattr(struct inode *inode, int mask);
-extern int ceph_permission(struct inode *inode, int mask);
+extern int ceph_permission(struct dentry *, struct inode *inode, int mask);
extern int ceph_setattr(struct dentry *dentry, struct iattr *attr);
extern int ceph_getattr(struct vfsmount *mnt, struct dentry *dentry,
struct kstat *stat);
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index 849f6132b327..ca304878ef7b 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -211,7 +211,7 @@ cifs_statfs(struct dentry *dentry, struct kstatfs *buf)
return 0;
}

-static int cifs_permission(struct inode *inode, int mask)
+static int cifs_permission(struct dentry *dentry, struct inode *inode, int mask)
{
struct cifs_sb_info *cifs_sb;

diff --git a/fs/coda/coda_linux.h b/fs/coda/coda_linux.h
index e7550cb9fb74..be666ad53717 100644
--- a/fs/coda/coda_linux.h
+++ b/fs/coda/coda_linux.h
@@ -39,7 +39,7 @@ extern const struct file_operations coda_ioctl_operations;
/* operations shared over more than one file */
int coda_open(struct inode *i, struct file *f);
int coda_release(struct inode *i, struct file *f);
-int coda_permission(struct inode *inode, int mask);
+int coda_permission(struct dentry *dentry, struct inode *inode, int mask);
int coda_revalidate_inode(struct inode *);
int coda_getattr(struct vfsmount *, struct dentry *, struct kstat *);
int coda_setattr(struct dentry *, struct iattr *);
diff --git a/fs/coda/dir.c b/fs/coda/dir.c
index 5efbb5ee0adc..c3ce50ad725e 100644
--- a/fs/coda/dir.c
+++ b/fs/coda/dir.c
@@ -128,7 +128,7 @@ static struct dentry *coda_lookup(struct inode *dir, struct dentry *entry, unsig
}

-int coda_permission(struct inode *inode, int mask)
+int coda_permission(struct dentry *dentry, struct inode *inode, int mask)
{
int error;

diff --git a/fs/coda/pioctl.c b/fs/coda/pioctl.c
index 3f5de96bbb58..2b760b97fe9c 100644
--- a/fs/coda/pioctl.c
+++ b/fs/coda/pioctl.c
@@ -24,7 +24,7 @@
#include "coda_linux.h"

/* pioctl ops */
-static int coda_ioctl_permission(struct inode *inode, int mask);
+static int coda_ioctl_permission(struct dentry *dentry, struct inode *inode, int mask);
static long coda_pioctl(struct file *filp, unsigned int cmd,
unsigned long user_data);

@@ -41,7 +41,7 @@ const struct file_operations coda_ioctl_operations = {
};

/* the coda pioctl inode ops */
-static int coda_ioctl_permission(struct inode *inode, int mask)
+static int coda_ioctl_permission(struct dentry *dentry, struct inode *inode, int mask)
{
return (mask & MAY_EXEC) ? -EACCES : 0;
}
diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
index b167ca48b8ee..f2dc516c626a 100644
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -153,7 +153,7 @@ static int ecryptfs_do_unlink(struct inode *dir, struct dentry *dentry,

dget(lower_dentry);
lower_dir_dentry = lock_parent(lower_dentry);
- rc = vfs_unlink(lower_dir_inode, lower_dentry, NULL);
+ rc = vfs_unlink(lower_dir_dentry, lower_dentry, NULL);
if (rc) {
printk(KERN_ERR "Error in vfs_unlink; rc = [%d]\n", rc);
goto out_unlock;
@@ -198,7 +198,7 @@ ecryptfs_do_create(struct inode *directory_inode,
inode = ERR_CAST(lower_dir_dentry);
goto out;
}
- rc = vfs_create(lower_dir_dentry->d_inode, lower_dentry, mode, true);
+ rc = vfs_create(lower_dir_dentry, lower_dentry, mode, true);
if (rc) {
printk(KERN_ERR "%s: Failure to create dentry in lower fs; "
"rc = [%d]\n", __func__, rc);
@@ -208,7 +208,7 @@ ecryptfs_do_create(struct inode *directory_inode,
inode = __ecryptfs_get_inode(lower_dentry->d_inode,
directory_inode->i_sb);
if (IS_ERR(inode)) {
- vfs_unlink(lower_dir_dentry->d_inode, lower_dentry, NULL);
+ vfs_unlink(lower_dir_dentry, lower_dentry, NULL);
goto out_lock;
}
fsstack_copy_attr_times(directory_inode, lower_dir_dentry->d_inode);
@@ -474,8 +474,7 @@ static int ecryptfs_link(struct dentry *old_dentry, struct inode *dir,
dget(lower_old_dentry);
dget(lower_new_dentry);
lower_dir_dentry = lock_parent(lower_new_dentry);
- rc = vfs_link(lower_old_dentry, lower_dir_dentry->d_inode,
- lower_new_dentry, NULL);
+ rc = vfs_link(lower_old_dentry, lower_dir_dentry, lower_new_dentry, NULL);
if (rc || !lower_new_dentry->d_inode)
goto out_lock;
rc = ecryptfs_interpose(lower_new_dentry, new_dentry, dir->i_sb);
@@ -520,8 +519,7 @@ static int ecryptfs_symlink(struct inode *dir, struct dentry *dentry,
strlen(symname));
if (rc)
goto out_lock;
- rc = vfs_symlink(lower_dir_dentry->d_inode, lower_dentry,
- encoded_symname);
+ rc = vfs_symlink(lower_dir_dentry, lower_dentry, encoded_symname);
kfree(encoded_symname);
if (rc || !lower_dentry->d_inode)
goto out_lock;
@@ -546,7 +544,7 @@ static int ecryptfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode

lower_dentry = ecryptfs_dentry_to_lower(dentry);
lower_dir_dentry = lock_parent(lower_dentry);
- rc = vfs_mkdir(lower_dir_dentry->d_inode, lower_dentry, mode);
+ rc = vfs_mkdir(lower_dir_dentry, lower_dentry, mode);
if (rc || !lower_dentry->d_inode)
goto out;
rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb);
@@ -572,7 +570,7 @@ static int ecryptfs_rmdir(struct inode *dir, struct dentry *dentry)
dget(dentry);
lower_dir_dentry = lock_parent(lower_dentry);
dget(lower_dentry);
- rc = vfs_rmdir(lower_dir_dentry->d_inode, lower_dentry);
+ rc = vfs_rmdir(lower_dir_dentry, lower_dentry);
dput(lower_dentry);
if (!rc && dentry->d_inode)
clear_nlink(dentry->d_inode);
@@ -594,7 +592,7 @@ ecryptfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev

lower_dentry = ecryptfs_dentry_to_lower(dentry);
lower_dir_dentry = lock_parent(lower_dentry);
- rc = vfs_mknod(lower_dir_dentry->d_inode, lower_dentry, mode, dev);
+ rc = vfs_mknod(lower_dir_dentry, lower_dentry, mode, dev);
if (rc || !lower_dentry->d_inode)
goto out;
rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb);
@@ -639,8 +637,8 @@ ecryptfs_rename(struct inode *old_dir, struct dentry *old_dentry,
rc = -ENOTEMPTY;
goto out_lock;
}
- rc = vfs_rename(lower_old_dir_dentry->d_inode, lower_old_dentry,
- lower_new_dir_dentry->d_inode, lower_new_dentry,
+ rc = vfs_rename(lower_old_dir_dentry, lower_old_dentry,
+ lower_new_dir_dentry, lower_new_dentry,
NULL);
if (rc)
goto out_lock;
@@ -884,9 +882,9 @@ int ecryptfs_truncate(struct dentry *dentry, loff_t new_length)
}

static int
-ecryptfs_permission(struct inode *inode, int mask)
+ecryptfs_permission(struct dentry *dentry, struct inode *inode, int mask)
{
- return inode_permission(ecryptfs_inode_to_lower(inode), mask);
+ return inode_permission(ecryptfs_dentry_to_lower(dentry), ecryptfs_inode_to_lower(inode), mask);
}

/**
diff --git a/fs/exec.c b/fs/exec.c
index e1529b4c79b1..3d86968c5ffb 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1100,7 +1100,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_permission(file->f_path.dentry, file_inode(file), MAY_READ) < 0)
bprm->interp_flags |= BINPRM_FLAGS_ENFORCE_NONDUMP;
}
EXPORT_SYMBOL(would_dump);
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 1d1292c581c3..dbb7ae00e66b 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -1095,7 +1095,7 @@ static int fuse_perm_getattr(struct inode *inode, int mask)
* access request is sent. Execute permission is still checked
* locally based on file mode.
*/
-static int fuse_permission(struct inode *inode, int mask)
+static int fuse_permission(struct dentry *dentry, struct inode *inode, int mask)
{
struct fuse_conn *fc = get_fuse_conn(inode);
bool refreshed = false;
diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c
index 5c524180c98e..79d162029cae 100644
--- a/fs/gfs2/inode.c
+++ b/fs/gfs2/inode.c
@@ -1890,8 +1890,13 @@ out:
return ret;
}

+static int gfs2_vfs_permission(struct dentry *dentry, struct inode *inode, int mask)
+{
+ return gfs2_permission(inode, mask);
+}
+
const struct inode_operations gfs2_file_iops = {
- .permission = gfs2_permission,
+ .permission = gfs2_vfs_permission,
.setattr = gfs2_setattr,
.getattr = gfs2_getattr,
.setxattr = gfs2_setxattr,
@@ -1913,7 +1918,7 @@ const struct inode_operations gfs2_dir_iops = {
.rmdir = gfs2_unlink,
.mknod = gfs2_mknod,
.rename = gfs2_rename,
- .permission = gfs2_permission,
+ .permission = gfs2_vfs_permission,
.setattr = gfs2_setattr,
.getattr = gfs2_getattr,
.setxattr = gfs2_setxattr,
@@ -1930,7 +1935,7 @@ const struct inode_operations gfs2_symlink_iops = {
.readlink = generic_readlink,
.follow_link = gfs2_follow_link,
.put_link = kfree_put_link,
- .permission = gfs2_permission,
+ .permission = gfs2_vfs_permission,
.setattr = gfs2_setattr,
.getattr = gfs2_getattr,
.setxattr = gfs2_setxattr,
diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c
index fe649d325b1f..ef1573189fd7 100644
--- a/fs/hostfs/hostfs_kern.c
+++ b/fs/hostfs/hostfs_kern.c
@@ -759,7 +759,7 @@ static int hostfs_rename(struct inode *from_ino, struct dentry *from,
return err;
}

-static int hostfs_permission(struct inode *ino, int desired)
+static int hostfs_permission(struct dentry *dentry, struct inode *ino, int desired)
{
char *name;
int r = 0, w = 0, x = 0, err;
diff --git a/fs/internal.h b/fs/internal.h
index 465742407466..52108c8f3919 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -42,7 +42,6 @@ extern void __init chrdev_init(void);
/*
* namei.c
*/
-extern int __inode_permission(struct inode *, int);
extern int user_path_mountpoint_at(int, const char __user *, unsigned int, struct path *);
extern int vfs_path_lookup(struct dentry *, struct vfsmount *,
const char *, unsigned int, struct path *);
diff --git a/fs/kernfs/inode.c b/fs/kernfs/inode.c
index e55126f85bd2..9f084fc16fcf 100644
--- a/fs/kernfs/inode.c
+++ b/fs/kernfs/inode.c
@@ -360,7 +360,7 @@ void kernfs_evict_inode(struct inode *inode)
kernfs_put(kn);
}

-int kernfs_iop_permission(struct inode *inode, int mask)
+int kernfs_iop_permission(struct dentry *dentry, struct inode *inode, int mask)
{
struct kernfs_node *kn;

diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h
index eb536b76374a..f41ba12978a9 100644
--- a/fs/kernfs/kernfs-internal.h
+++ b/fs/kernfs/kernfs-internal.h
@@ -78,7 +78,7 @@ extern struct kmem_cache *kernfs_node_cache;
*/
struct inode *kernfs_get_inode(struct super_block *sb, struct kernfs_node *kn);
void kernfs_evict_inode(struct inode *inode);
-int kernfs_iop_permission(struct inode *inode, int mask);
+int kernfs_iop_permission(struct dentry *dentry, struct inode *inode, int mask);
int kernfs_iop_setattr(struct dentry *dentry, struct iattr *iattr);
int kernfs_iop_getattr(struct vfsmount *mnt, struct dentry *dentry,
struct kstat *stat);
diff --git a/fs/namei.c b/fs/namei.c
index d580df2e6804..a885b9322637 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -335,11 +335,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 dentry *dentry, struct inode *inode, int mask)
{
if (unlikely(!(inode->i_opflags & IOP_FASTPERM))) {
if (likely(inode->i_op->permission))
- return inode->i_op->permission(inode, mask);
+ return inode->i_op->permission(dentry, inode, mask);

/* This gets set once for the inode lifetime */
spin_lock(&inode->i_lock);
@@ -361,7 +361,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)
+static int __inode_permission(struct dentry *dentry, struct inode *inode, int mask)
{
int retval;

@@ -373,7 +373,7 @@ int __inode_permission(struct inode *inode, int mask)
return -EACCES;
}

- retval = do_inode_permission(inode, mask);
+ retval = do_inode_permission(dentry, inode, mask);
if (retval)
return retval;

@@ -416,14 +416,14 @@ 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_permission(struct dentry *dentry, 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_permission(dentry, inode, mask);
}

/**
@@ -729,7 +729,7 @@ static inline int may_follow_link(struct path *link, struct nameidata *nd)
*
* Otherwise returns true.
*/
-static bool safe_hardlink_source(struct inode *inode)
+static bool safe_hardlink_source(struct dentry *dentry, struct inode *inode)
{
umode_t mode = inode->i_mode;

@@ -746,7 +746,7 @@ static bool safe_hardlink_source(struct inode *inode)
return false;

/* Hardlinking to unreadable or unwritable sources is dangerous. */
- if (inode_permission(inode, MAY_READ | MAY_WRITE))
+ if (inode_permission(dentry, inode, MAY_READ | MAY_WRITE))
return false;

return true;
@@ -778,7 +778,7 @@ static int may_linkat(struct path *link)
/* Source inode owner (or CAP_FOWNER) can hardlink all they like,
* otherwise, it must be a safe source.
*/
- if (uid_eq(cred->fsuid, inode->i_uid) || safe_hardlink_source(inode) ||
+ if (uid_eq(cred->fsuid, inode->i_uid) || safe_hardlink_source(link->dentry, inode) ||
capable(CAP_FOWNER))
return 0;

@@ -1442,13 +1442,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_permission(nd->path.dentry, 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_permission(nd->path.dentry, nd->inode, MAY_EXEC);
}

static inline int handle_dots(struct nameidata *nd, int type)
@@ -1792,7 +1792,7 @@ static int path_init(int dfd, const char *name, unsigned int flags,
if (*name) {
if (!d_is_directory(root))
return -ENOTDIR;
- retval = inode_permission(inode, MAY_EXEC);
+ retval = inode_permission(root, inode, MAY_EXEC);
if (retval)
return retval;
}
@@ -2078,7 +2078,7 @@ 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_permission(base, base->d_inode, MAY_EXEC);
if (err)
return ERR_PTR(err);

@@ -2365,8 +2365,9 @@ 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, bool isdir)
+static int may_delete(struct dentry *parent, struct dentry *victim, bool isdir)
{
+ struct inode *dir = parent->d_inode;
struct inode *inode = victim->d_inode;
int error;

@@ -2377,7 +2378,7 @@ static int may_delete(struct inode *dir, struct dentry *victim, bool 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_permission(parent, dir, MAY_WRITE | MAY_EXEC);
if (error)
return error;
if (IS_APPEND(dir))
@@ -2408,14 +2409,15 @@ static int may_delete(struct inode *dir, struct dentry *victim, bool 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 dentry *parent, struct dentry *child)
{
+ struct inode *dir = parent->d_inode;
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_permission(parent, dir, MAY_WRITE | MAY_EXEC);
}

/*
@@ -2460,10 +2462,11 @@ void unlock_rename(struct dentry *p1, struct dentry *p2)
}
}

-int vfs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
+int vfs_create(struct dentry *parent, struct dentry *dentry, umode_t mode,
bool want_excl)
{
- int error = may_create(dir, dentry);
+ struct inode *dir = parent->d_inode;
+ int error = may_create(parent, dentry);
if (error)
return error;

@@ -2511,7 +2514,7 @@ static int may_open(struct path *path, int acc_mode, int flag)
break;
}

- error = inode_permission(inode, acc_mode);
+ error = inode_permission(dentry, inode, acc_mode);
if (error)
return error;

@@ -2567,7 +2570,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_permission(dir->dentry, dir->dentry->d_inode, MAY_WRITE | MAY_EXEC);
if (error)
return error;

@@ -2812,8 +2815,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,
- nd->flags & LOOKUP_EXCL);
+ error = vfs_create(dir, dentry, mode, nd->flags & LOOKUP_EXCL);
if (error)
goto out_dput;
}
@@ -3077,10 +3079,10 @@ static int do_tmpfile(int dfd, struct filename *pathname,
if (unlikely(error))
goto out;
/* we want directory to be writable */
- error = inode_permission(nd->inode, MAY_WRITE | MAY_EXEC);
+ dentry = nd->path.dentry;
+ error = inode_permission(dentry, nd->inode, MAY_WRITE | MAY_EXEC);
if (error)
goto out2;
- dentry = nd->path.dentry;
dir = dentry->d_inode;
if (!dir->i_op->tmpfile) {
error = -EOPNOTSUPP;
@@ -3323,9 +3325,10 @@ 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_mknod(struct dentry *parent, struct dentry *dentry, umode_t mode, dev_t dev)
{
- int error = may_create(dir, dentry);
+ struct inode *dir;
+ int error = may_create(parent, dentry);

if (error)
return error;
@@ -3333,6 +3336,7 @@ int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
if ((S_ISCHR(mode) || S_ISBLK(mode)) && !capable(CAP_MKNOD))
return -EPERM;

+ dir = parent->d_inode;
if (!dir->i_op->mknod)
return -EPERM;

@@ -3390,14 +3394,14 @@ retry:
goto out;
switch (mode & S_IFMT) {
case 0: case S_IFREG:
- error = vfs_create(path.dentry->d_inode,dentry,mode,true);
+ error = vfs_create(path.dentry,dentry,mode,true);
break;
case S_IFCHR: case S_IFBLK:
- error = vfs_mknod(path.dentry->d_inode,dentry,mode,
+ error = vfs_mknod(path.dentry,dentry,mode,
new_decode_dev(dev));
break;
case S_IFIFO: case S_IFSOCK:
- error = vfs_mknod(path.dentry->d_inode,dentry,mode,0);
+ error = vfs_mknod(path.dentry,dentry,mode,0);
break;
}
out:
@@ -3414,14 +3418,16 @@ 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_mkdir(struct dentry *parent, struct dentry *dentry, umode_t mode)
{
- int error = may_create(dir, dentry);
- unsigned max_links = dir->i_sb->s_max_links;
+ struct inode *dir;
+ unsigned max_links;
+ int error = may_create(parent, dentry);

if (error)
return error;

+ dir = parent->d_inode;
if (!dir->i_op->mkdir)
return -EPERM;

@@ -3430,6 +3436,7 @@ int vfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
if (error)
return error;

+ max_links = dir->i_sb->s_max_links;
if (max_links && dir->i_nlink >= max_links)
return -EMLINK;

@@ -3455,7 +3462,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_mkdir(path.dentry, dentry, mode);
done_path_create(&path, dentry);
if (retry_estale(error, lookup_flags)) {
lookup_flags |= LOOKUP_REVAL;
@@ -3493,13 +3500,15 @@ void dentry_unhash(struct dentry *dentry)
spin_unlock(&dentry->d_lock);
}

-int vfs_rmdir(struct inode *dir, struct dentry *dentry)
+int vfs_rmdir(struct dentry *parent, struct dentry *dentry)
{
- int error = may_delete(dir, dentry, 1);
+ struct inode *dir;
+ int error = may_delete(parent, dentry, 1);

if (error)
return error;

+ dir = parent->d_inode;
if (!dir->i_op->rmdir)
return -EPERM;

@@ -3571,7 +3580,7 @@ retry:
error = security_path_rmdir(&nd.path, dentry);
if (error)
goto exit3;
- error = vfs_rmdir(nd.path.dentry->d_inode, dentry);
+ error = vfs_rmdir(nd.path.dentry, dentry);
exit3:
dput(dentry);
exit2:
@@ -3610,10 +3619,11 @@ SYSCALL_DEFINE1(rmdir, const char __user *, pathname)
* be appropriate for callers that expect the underlying filesystem not
* to be NFS exported.
*/
-int vfs_unlink(struct inode *dir, struct dentry *dentry, struct inode **delegated_inode)
+int vfs_unlink(struct dentry *parent, struct dentry *dentry, struct inode **delegated_inode)
{
+ struct inode *dir = parent->d_inode;
struct inode *target = dentry->d_inode;
- int error = may_delete(dir, dentry, 0);
+ int error = may_delete(parent, dentry, 0);

if (error)
return error;
@@ -3690,7 +3700,7 @@ retry_deleg:
error = security_path_unlink(&nd.path, dentry);
if (error)
goto exit2;
- error = vfs_unlink(nd.path.dentry->d_inode, dentry, &delegated_inode);
+ error = vfs_unlink(nd.path.dentry, dentry, &delegated_inode);
exit2:
dput(dentry);
}
@@ -3740,13 +3750,15 @@ 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_symlink(struct dentry *parent, struct dentry *dentry, const char *oldname)
{
- int error = may_create(dir, dentry);
+ struct inode *dir;
+ int error = may_create(parent, dentry);

if (error)
return error;

+ dir = parent->d_inode;
if (!dir->i_op->symlink)
return -EPERM;

@@ -3780,7 +3792,7 @@ retry:

error = security_path_symlink(&path, dentry, from->name);
if (!error)
- error = vfs_symlink(path.dentry->d_inode, dentry, from->name);
+ error = vfs_symlink(path.dentry, dentry, from->name);
done_path_create(&path, dentry);
if (retry_estale(error, lookup_flags)) {
lookup_flags |= LOOKUP_REVAL;
@@ -3815,8 +3827,9 @@ SYSCALL_DEFINE2(symlink, const char __user *, oldname, const char __user *, newn
* be appropriate for callers that expect the underlying filesystem not
* to be NFS exported.
*/
-int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry, struct inode **delegated_inode)
+int vfs_link(struct dentry *old_dentry, struct dentry *new_parent, struct dentry *new_dentry, struct inode **delegated_inode)
{
+ struct inode *dir = new_parent->d_inode;
struct inode *inode = old_dentry->d_inode;
unsigned max_links = dir->i_sb->s_max_links;
int error;
@@ -3824,7 +3837,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(new_parent, new_dentry);
if (error)
return error;

@@ -3921,7 +3934,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, &delegated_inode);
+ error = vfs_link(old_path.dentry, new_path.dentry, new_dentry, &delegated_inode);
out_dput:
done_path_create(&new_path, new_dentry);
if (delegated_inode) {
@@ -3987,7 +4000,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_permission(old_dentry, old_dentry->d_inode, MAY_WRITE);
if (error)
return error;
}
@@ -4091,28 +4104,31 @@ out:
* be appropriate for callers that expect the underlying filesystem not
* to be NFS exported.
*/
-int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
- struct inode *new_dir, struct dentry *new_dentry,
+int vfs_rename(struct dentry *old_parent, struct dentry *old_dentry,
+ struct dentry *new_parent, struct dentry *new_dentry,
struct inode **delegated_inode)
{
int error;
+ struct inode *old_dir, *new_dir;
int is_dir = d_is_directory(old_dentry) || d_is_autodir(old_dentry);
const unsigned char *old_name;

if (old_dentry->d_inode == new_dentry->d_inode)
return 0;

- error = may_delete(old_dir, old_dentry, is_dir);
+ error = may_delete(old_parent, old_dentry, is_dir);
if (error)
return error;

if (!new_dentry->d_inode)
- error = may_create(new_dir, new_dentry);
+ error = may_create(new_parent, new_dentry);
else
- error = may_delete(new_dir, new_dentry, is_dir);
+ error = may_delete(new_parent, new_dentry, is_dir);
if (error)
return error;

+ old_dir = old_parent->d_inode;
+ new_dir = new_parent->d_inode;
if (!old_dir->i_op->rename)
return -EPERM;

@@ -4213,9 +4229,9 @@ retry_deleg:
&newnd.path, new_dentry);
if (error)
goto exit5;
- error = vfs_rename(old_dir->d_inode, old_dentry,
- new_dir->d_inode, new_dentry,
- &delegated_inode);
+ error = vfs_rename(old_dir, old_dentry,
+ new_dir, new_dentry,
+ &delegated_inode);
exit5:
dput(new_dentry);
exit4:
diff --git a/fs/ncpfs/ioctl.c b/fs/ncpfs/ioctl.c
index 60426ccb3b65..1294ebb5b4ce 100644
--- a/fs/ncpfs/ioctl.c
+++ b/fs/ncpfs/ioctl.c
@@ -872,7 +872,8 @@ long ncp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
if (ret)
goto out;
need_drop_write = 1;
- ret = inode_permission(inode, MAY_WRITE);
+ // Why not just check f_mode & FMODE_WRITE?
+ ret = inode_permission(filp->f_path.dentry, inode, MAY_WRITE);
if (ret)
goto outDropWrite;
break;
@@ -884,7 +885,8 @@ long ncp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
case NCP_IOC_GETMOUNTUID64:
case NCP_IOC_GETROOT:
case NCP_IOC_SIGN_WANTED:
- ret = inode_permission(inode, MAY_READ);
+ // Why not just check f_mode & FMODE_READ?
+ ret = inode_permission(filp->f_path.dentry, inode, MAY_READ);
if (ret)
goto out;
break;
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index be38b573495a..d13216693d1a 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -2269,7 +2269,7 @@ int nfs_may_open(struct inode *inode, struct rpc_cred *cred, int openflags)
}
EXPORT_SYMBOL_GPL(nfs_may_open);

-int nfs_permission(struct inode *inode, int mask)
+int nfs_permission(struct dentry *dentry, struct inode *inode, int mask)
{
struct rpc_cred *cred;
int res = 0;
diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c
index 9c271f42604a..666223b0d00a 100644
--- a/fs/nfsd/nfs4recover.c
+++ b/fs/nfsd/nfs4recover.c
@@ -209,7 +209,7 @@ nfsd4_create_clid_dir(struct nfs4_client *clp)
* as well be forgiving and just succeed silently.
*/
goto out_put;
- status = vfs_mkdir(dir->d_inode, dentry, S_IRWXU);
+ status = vfs_mkdir(dir, dentry, S_IRWXU);
out_put:
dput(dentry);
out_unlock:
@@ -323,7 +323,7 @@ nfsd4_unlink_clid_dir(char *name, int namlen, struct nfsd_net *nn)
status = -ENOENT;
if (!dentry->d_inode)
goto out;
- status = vfs_rmdir(dir->d_inode, dentry);
+ status = vfs_rmdir(dir, dentry);
out:
dput(dentry);
out_unlock:
@@ -383,7 +383,7 @@ purge_old(struct dentry *parent, struct dentry *child, struct nfsd_net *nn)
if (nfs4_has_reclaimed_state(child->d_name.name, nn))
return 0;

- status = vfs_rmdir(parent->d_inode, child);
+ status = vfs_rmdir(parent, child);
if (status)
printk("failed to remove client recovery directory %pd\n",
child);
diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c
index 3c37b160dcad..979be190080f 100644
--- a/fs/nfsd/nfsfh.c
+++ b/fs/nfsd/nfsfh.c
@@ -38,7 +38,7 @@ static int nfsd_acceptable(void *expv, struct dentry *dentry)
/* make sure parents give x permission to user */
int err;
parent = dget_parent(tdentry);
- err = inode_permission(parent->d_inode, MAY_EXEC);
+ err = inode_permission(parent, parent->d_inode, MAY_EXEC);
if (err < 0) {
dput(parent);
break;
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 017d3cb5e99b..f045c435d49a 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -1220,18 +1220,18 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
host_err = 0;
switch (type) {
case S_IFREG:
- host_err = vfs_create(dirp, dchild, iap->ia_mode, true);
+ host_err = vfs_create(dentry, dchild, iap->ia_mode, true);
if (!host_err)
nfsd_check_ignore_resizing(iap);
break;
case S_IFDIR:
- host_err = vfs_mkdir(dirp, dchild, iap->ia_mode);
+ host_err = vfs_mkdir(dentry, dchild, iap->ia_mode);
break;
case S_IFCHR:
case S_IFBLK:
case S_IFIFO:
case S_IFSOCK:
- host_err = vfs_mknod(dirp, dchild, iap->ia_mode, rdev);
+ host_err = vfs_mknod(dentry, dchild, iap->ia_mode, rdev);
break;
}
if (host_err < 0)
@@ -1388,7 +1388,7 @@ do_nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
goto out;
}

- host_err = vfs_create(dirp, dchild, iap->ia_mode, true);
+ host_err = vfs_create(dentry, dchild, iap->ia_mode, true);
if (host_err < 0) {
fh_drop_write(fhp);
goto out_nfserr;
@@ -1528,11 +1528,11 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp,
else {
strncpy(path_alloced, path, plen);
path_alloced[plen] = 0;
- host_err = vfs_symlink(dentry->d_inode, dnew, path_alloced);
+ host_err = vfs_symlink(dentry, dnew, path_alloced);
kfree(path_alloced);
}
} else
- host_err = vfs_symlink(dentry->d_inode, dnew, path);
+ host_err = vfs_symlink(dentry, dnew, path);
err = nfserrno(host_err);
if (!err)
err = nfserrno(commit_metadata(fhp));
@@ -1560,7 +1560,6 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
char *name, int len, struct svc_fh *tfhp)
{
struct dentry *ddir, *dnew, *dold;
- struct inode *dirp;
__be32 err;
int host_err;

@@ -1588,7 +1587,6 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,

fh_lock_nested(ffhp, I_MUTEX_PARENT);
ddir = ffhp->fh_dentry;
- dirp = ddir->d_inode;

dnew = lookup_one_len(name, ddir, len);
host_err = PTR_ERR(dnew);
@@ -1600,7 +1598,7 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
err = nfserr_noent;
if (!dold->d_inode)
goto out_dput;
- host_err = vfs_link(dold, dirp, dnew, NULL);
+ host_err = vfs_link(dold, ddir, dnew, NULL);
if (!host_err) {
err = nfserrno(commit_metadata(ffhp));
if (!err)
@@ -1633,7 +1631,6 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
struct svc_fh *tfhp, char *tname, int tlen)
{
struct dentry *fdentry, *tdentry, *odentry, *ndentry, *trap;
- struct inode *fdir, *tdir;
__be32 err;
int host_err;

@@ -1645,10 +1642,7 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
goto out;

fdentry = ffhp->fh_dentry;
- fdir = fdentry->d_inode;
-
tdentry = tfhp->fh_dentry;
- tdir = tdentry->d_inode;

err = nfserr_perm;
if (!flen || isdotent(fname, flen) || !tlen || isdotent(tname, tlen))
@@ -1693,7 +1687,7 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
if (ffhp->fh_export->ex_path.dentry != tfhp->fh_export->ex_path.dentry)
goto out_dput_new;

- host_err = vfs_rename(fdir, odentry, tdir, ndentry, NULL);
+ host_err = vfs_rename(fdentry, odentry, tdentry, ndentry, NULL);
if (!host_err) {
host_err = commit_metadata(tfhp);
if (!host_err)
@@ -1729,7 +1723,6 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
char *fname, int flen)
{
struct dentry *dentry, *rdentry;
- struct inode *dirp;
__be32 err;
int host_err;

@@ -1746,7 +1739,6 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,

fh_lock_nested(fhp, I_MUTEX_PARENT);
dentry = fhp->fh_dentry;
- dirp = dentry->d_inode;

rdentry = lookup_one_len(fname, dentry, flen);
host_err = PTR_ERR(rdentry);
@@ -1763,9 +1755,9 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
type = rdentry->d_inode->i_mode & S_IFMT;

if (type != S_IFDIR)
- host_err = vfs_unlink(dirp, rdentry, NULL);
+ host_err = vfs_unlink(dentry, rdentry, NULL);
else
- host_err = vfs_rmdir(dirp, rdentry);
+ host_err = vfs_rmdir(dentry, rdentry);
if (!host_err)
host_err = commit_metadata(fhp);
dput(rdentry);
@@ -2036,13 +2028,13 @@ nfsd_permission(struct svc_rqst *rqstp, struct svc_export *exp,
return 0;

/* This assumes NFSD_MAY_{READ,WRITE,EXEC} == MAY_{READ,WRITE,EXEC} */
- err = inode_permission(inode, acc & (MAY_READ|MAY_WRITE|MAY_EXEC));
+ err = inode_permission(dentry, inode, acc & (MAY_READ|MAY_WRITE|MAY_EXEC));

/* Allow read access to binaries even when mode 111 */
if (err == -EACCES && S_ISREG(inode->i_mode) &&
(acc == (NFSD_MAY_READ | NFSD_MAY_OWNER_OVERRIDE) ||
acc == (NFSD_MAY_READ | NFSD_MAY_READ_IF_EXEC)))
- err = inode_permission(inode, MAY_EXEC);
+ err = inode_permission(dentry, inode, MAY_EXEC);

return err? nfserrno(err) : 0;
}
diff --git a/fs/nilfs2/inode.c b/fs/nilfs2/inode.c
index 7e350c562e0e..5348b2f14395 100644
--- a/fs/nilfs2/inode.c
+++ b/fs/nilfs2/inode.c
@@ -850,7 +850,7 @@ out_err:
return err;
}

-int nilfs_permission(struct inode *inode, int mask)
+int nilfs_permission(struct dentry *dentry, struct inode *inode, int mask)
{
struct nilfs_root *root = NILFS_I(inode)->i_root;
if ((mask & MAY_WRITE) && root &&
diff --git a/fs/nilfs2/nilfs.h b/fs/nilfs2/nilfs.h
index 9bc72dec3fa6..3d5abd228b8c 100644
--- a/fs/nilfs2/nilfs.h
+++ b/fs/nilfs2/nilfs.h
@@ -278,7 +278,7 @@ extern void nilfs_truncate(struct inode *);
extern void nilfs_evict_inode(struct inode *);
extern int nilfs_setattr(struct dentry *, struct iattr *);
extern void nilfs_write_failed(struct address_space *mapping, loff_t to);
-int nilfs_permission(struct inode *inode, int mask);
+int nilfs_permission(struct dentry *dentry, struct inode *inode, int mask);
int nilfs_load_inode_block(struct inode *inode, struct buffer_head **pbh);
extern int nilfs_inode_dirty(struct inode *);
int nilfs_set_file_dirty(struct inode *inode, unsigned nr_dirty);
diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
index b6175fa11bf8..5f151d788733 100644
--- a/fs/notify/fanotify/fanotify_user.c
+++ b/fs/notify/fanotify/fanotify_user.c
@@ -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_permission(path->dentry, path->dentry->d_inode, MAY_READ);
if (ret)
path_put(path);
out:
diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c
index 497395c8274b..4c022eb266c7 100644
--- a/fs/notify/inotify/inotify_user.c
+++ b/fs/notify/inotify/inotify_user.c
@@ -338,7 +338,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_permission(path->dentry, path->dentry->d_inode, MAY_READ);
if (error)
path_put(path);
return error;
diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c
index d77d71ead8d1..562d9d73433d 100644
--- a/fs/ocfs2/file.c
+++ b/fs/ocfs2/file.c
@@ -1269,7 +1269,7 @@ bail:
return err;
}

-int ocfs2_permission(struct inode *inode, int mask)
+int ocfs2_permission(struct dentry *dentry, struct inode *inode, int mask)
{
int ret;

diff --git a/fs/ocfs2/file.h b/fs/ocfs2/file.h
index 97bf761c9e7c..e2bdbcadd6f4 100644
--- a/fs/ocfs2/file.h
+++ b/fs/ocfs2/file.h
@@ -61,7 +61,7 @@ int ocfs2_zero_extend(struct inode *inode, struct buffer_head *di_bh,
int ocfs2_setattr(struct dentry *dentry, struct iattr *attr);
int ocfs2_getattr(struct vfsmount *mnt, struct dentry *dentry,
struct kstat *stat);
-int ocfs2_permission(struct inode *inode, int mask);
+int ocfs2_permission(struct dentry *dentry, struct inode *inode, int mask);

int ocfs2_should_update_atime(struct inode *inode,
struct vfsmount *vfsmnt);
diff --git a/fs/ocfs2/refcounttree.c b/fs/ocfs2/refcounttree.c
index 6ba4bcbc4796..a5d429b65bef 100644
--- a/fs/ocfs2/refcounttree.c
+++ b/fs/ocfs2/refcounttree.c
@@ -4350,13 +4350,15 @@ out:
*/

/* copied from may_create in VFS. */
-static inline int ocfs2_may_create(struct inode *dir, struct dentry *child)
+static inline int ocfs2_may_create(struct dentry *parent, struct dentry *child)
{
+ struct inode *dir = parent->d_inode;
+
if (child->d_inode)
return -EEXIST;
if (IS_DEADDIR(dir))
return -ENOENT;
- return inode_permission(dir, MAY_WRITE | MAY_EXEC);
+ return inode_permission(parent, dir, MAY_WRITE | MAY_EXEC);
}

/**
@@ -4367,16 +4369,17 @@ static inline int ocfs2_may_create(struct inode *dir, struct dentry *child)
* @new_dentry: target dentry
* @preserve: if true, preserve all file attributes
*/
-static int ocfs2_vfs_reflink(struct dentry *old_dentry, struct inode *dir,
+static int ocfs2_vfs_reflink(struct dentry *old_dentry, struct dentry *new_parent,
struct dentry *new_dentry, bool preserve)
{
+ struct inode *dir = new_parent->d_inode;
struct inode *inode = old_dentry->d_inode;
int error;

if (!inode)
return -ENOENT;

- error = ocfs2_may_create(dir, new_dentry);
+ error = ocfs2_may_create(new_parent, new_dentry);
if (error)
return error;

@@ -4410,7 +4413,7 @@ static int ocfs2_vfs_reflink(struct dentry *old_dentry, struct inode *dir,
* file.
*/
if (!preserve) {
- error = inode_permission(inode, MAY_READ);
+ error = inode_permission(old_dentry, inode, MAY_READ);
if (error)
return error;
}
@@ -4458,7 +4461,7 @@ int ocfs2_reflink_ioctl(struct inode *inode,
}

error = ocfs2_vfs_reflink(old_path.dentry,
- new_path.dentry->d_inode,
+ new_path.dentry,
new_dentry, preserve);
out_dput:
done_path_create(&new_path, new_dentry);
diff --git a/fs/open.c b/fs/open.c
index 4b3e1edf2fe4..788be4fb87f3 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -65,10 +65,12 @@ int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,

long vfs_truncate(struct path *path, loff_t length)
{
+ struct dentry *dentry;
struct inode *inode;
long error;

- inode = path->dentry->d_inode;
+ dentry = path->dentry;
+ inode = dentry->d_inode;

/* For directories it's -EISDIR, for other non-regulars - -EINVAL */
if (S_ISDIR(inode->i_mode))
@@ -80,7 +82,7 @@ long vfs_truncate(struct path *path, loff_t length)
if (error)
goto out;

- error = inode_permission(inode, MAY_WRITE);
+ error = inode_permission(dentry, inode, MAY_WRITE);
if (error)
goto mnt_drop_write_and_out;

@@ -344,7 +346,7 @@ retry:
goto out_path_release;
}

- res = inode_permission(inode, mode | MAY_ACCESS);
+ res = inode_permission(path.dentry, 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;
@@ -388,7 +390,7 @@ retry:
if (error)
goto out;

- error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_CHDIR);
+ error = inode_permission(path.dentry, path.dentry->d_inode, MAY_EXEC | MAY_CHDIR);
if (error)
goto dput_and_out;

@@ -420,7 +422,7 @@ SYSCALL_DEFINE1(fchdir, unsigned int, fd)
if (!S_ISDIR(inode->i_mode))
goto out_putf;

- error = inode_permission(inode, MAY_EXEC | MAY_CHDIR);
+ error = inode_permission(f.file->f_path.dentry, inode, MAY_EXEC | MAY_CHDIR);
if (!error)
set_fs_pwd(current->fs, &f.file->f_path);
out_putf:
@@ -439,7 +441,7 @@ retry:
if (error)
goto out;

- error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_CHDIR);
+ error = inode_permission(path.dentry, path.dentry->d_inode, MAY_EXEC | MAY_CHDIR);
if (error)
goto dput_and_out;

diff --git a/fs/proc/base.c b/fs/proc/base.c
index 51507065263b..eb4752863442 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -596,7 +596,7 @@ static bool has_pid_permissions(struct pid_namespace *pid,
}

-static int proc_pid_permission(struct inode *inode, int mask)
+static int proc_pid_permission(struct dentry *dentry, struct inode *inode, int mask)
{
struct pid_namespace *pid = inode->i_sb->s_fs_info;
struct task_struct *task;
diff --git a/fs/proc/fd.c b/fs/proc/fd.c
index 985ea881b5bc..026594d5c820 100644
--- a/fs/proc/fd.c
+++ b/fs/proc/fd.c
@@ -281,7 +281,7 @@ static struct dentry *proc_lookupfd(struct inode *dir, struct dentry *dentry,
* /proc/pid/fd needs a special permission handler so that a process can still
* access /proc/self/fd after it has executed a setuid().
*/
-int proc_fd_permission(struct inode *inode, int mask)
+int proc_fd_permission(struct dentry *dentry, struct inode *inode, int mask)
{
int rv = generic_permission(inode, mask);
if (rv == 0)
diff --git a/fs/proc/fd.h b/fs/proc/fd.h
index 7c047f256ae2..29a70c16fd67 100644
--- a/fs/proc/fd.h
+++ b/fs/proc/fd.h
@@ -9,7 +9,7 @@ extern const struct inode_operations proc_fd_inode_operations;
extern const struct file_operations proc_fdinfo_operations;
extern const struct inode_operations proc_fdinfo_inode_operations;

-extern int proc_fd_permission(struct inode *inode, int mask);
+extern int proc_fd_permission(struct dentry *dentry, struct inode *inode, int mask);

static inline int proc_fd(struct inode *inode)
{
diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c
index 71290463a1d3..afcf1e80ab97 100644
--- a/fs/proc/proc_sysctl.c
+++ b/fs/proc/proc_sysctl.c
@@ -680,7 +680,7 @@ static int proc_sys_readdir(struct file *file, struct dir_context *ctx)
return 0;
}

-static int proc_sys_permission(struct inode *inode, int mask)
+static int proc_sys_permission(struct dentry *dentry, struct inode *inode, int mask)
{
/*
* sysctl entries that are not writeable,
diff --git a/fs/reiserfs/xattr.c b/fs/reiserfs/xattr.c
index 5cdfbd638b5c..fbfed32cb2d7 100644
--- a/fs/reiserfs/xattr.c
+++ b/fs/reiserfs/xattr.c
@@ -930,7 +930,7 @@ static int xattr_mount_check(struct super_block *s)
return 0;
}

-int reiserfs_permission(struct inode *inode, int mask)
+int reiserfs_permission(struct dentry *dentry, struct inode *inode, int mask)
{
/*
* We don't do permission checks on the internal objects.
diff --git a/fs/reiserfs/xattr.h b/fs/reiserfs/xattr.h
index f59626c5d33b..8d29ed9a19d4 100644
--- a/fs/reiserfs/xattr.h
+++ b/fs/reiserfs/xattr.h
@@ -15,7 +15,7 @@ int reiserfs_xattr_init(struct super_block *sb, int mount_flags);
int reiserfs_lookup_privroot(struct super_block *sb);
int reiserfs_delete_xattrs(struct inode *inode);
int reiserfs_chown_xattrs(struct inode *inode, struct iattr *attrs);
-int reiserfs_permission(struct inode *inode, int mask);
+int reiserfs_permission(struct dentry *dentry, struct inode *inode, int mask);

#ifdef CONFIG_REISERFS_FS_XATTR
#define has_xattr_dir(inode) (REISERFS_I(inode)->i_flags & i_has_xattr_dir)
diff --git a/fs/udf/file.c b/fs/udf/file.c
index c02a27a19c6d..0da4aabe25d6 100644
--- a/fs/udf/file.c
+++ b/fs/udf/file.c
@@ -182,7 +182,7 @@ long udf_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
long old_block, new_block;
int result = -EINVAL;

- if (inode_permission(inode, MAY_READ) != 0) {
+ if (inode_permission(filp->f_path.dentry, inode, MAY_READ) != 0) {
udf_debug("no permission to access inode %lu\n", inode->i_ino);
result = -EPERM;
goto out;
diff --git a/fs/utimes.c b/fs/utimes.c
index aa138d64560a..b7f17db4f08c 100644
--- a/fs/utimes.c
+++ b/fs/utimes.c
@@ -97,7 +97,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_permission(path->dentry, inode, MAY_WRITE);
if (error)
goto mnt_drop_write_and_out;
}
diff --git a/fs/xattr.c b/fs/xattr.c
index 3377dff18404..00575e033549 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -29,7 +29,7 @@
* because different namespaces have very different rules.
*/
static int
-xattr_permission(struct inode *inode, const char *name, int mask)
+xattr_permission(struct dentry *dentry, struct inode *inode, const char *name, int mask)
{
/*
* We can never set or remove an extended attribute on a read-only
@@ -70,7 +70,7 @@ xattr_permission(struct inode *inode, const char *name, int mask)
return -EPERM;
}

- return inode_permission(inode, mask);
+ return inode_permission(dentry, inode, mask);
}

/**
@@ -125,7 +125,7 @@ vfs_setxattr(struct dentry *dentry, const char *name, const void *value,
struct inode *inode = dentry->d_inode;
int error;

- error = xattr_permission(inode, name, MAY_WRITE);
+ error = xattr_permission(dentry, inode, name, MAY_WRITE);
if (error)
return error;

@@ -185,7 +185,7 @@ vfs_getxattr_alloc(struct dentry *dentry, const char *name, char **xattr_value,
char *value = *xattr_value;
int error;

- error = xattr_permission(inode, name, MAY_READ);
+ error = xattr_permission(dentry, inode, name, MAY_READ);
if (error)
return error;

@@ -233,7 +233,7 @@ vfs_getxattr(struct dentry *dentry, const char *name, void *value, size_t size)
struct inode *inode = dentry->d_inode;
int error;

- error = xattr_permission(inode, name, MAY_READ);
+ error = xattr_permission(dentry, inode, name, MAY_READ);
if (error)
return error;

@@ -292,7 +292,7 @@ vfs_removexattr(struct dentry *dentry, const char *name)
if (!inode->i_op->removexattr)
return -EOPNOTSUPP;

- error = xattr_permission(inode, name, MAY_WRITE);
+ error = xattr_permission(dentry, inode, name, MAY_WRITE);
if (error)
return error;

diff --git a/include/linux/fs.h b/include/linux/fs.h
index 09f553c59813..2350d8b10c18 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1449,14 +1449,14 @@ 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_mkdir(struct inode *, struct dentry *, umode_t);
-extern int vfs_mknod(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 *, struct inode **);
-extern int vfs_rmdir(struct inode *, struct dentry *);
-extern int vfs_unlink(struct inode *, struct dentry *, struct inode **);
-extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *, struct inode **);
+extern int vfs_create(struct dentry *, struct dentry *, umode_t, bool);
+extern int vfs_mkdir(struct dentry *, struct dentry *, umode_t);
+extern int vfs_mknod(struct dentry *, struct dentry *, umode_t, dev_t);
+extern int vfs_symlink(struct dentry *, struct dentry *, const char *);
+extern int vfs_link(struct dentry *, struct dentry *, struct dentry *, struct inode **);
+extern int vfs_rmdir(struct dentry *, struct dentry *);
+extern int vfs_unlink(struct dentry *, struct dentry *, struct inode **);
+extern int vfs_rename(struct dentry *, struct dentry *, struct dentry *, struct dentry *, struct inode **);

/*
* VFS dentry helper functions.
@@ -1552,7 +1552,7 @@ struct file_operations {
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 (*permission) (struct dentry *, struct inode *, int);
struct posix_acl * (*get_acl)(struct inode *, int);

int (*readlink) (struct dentry *, char __user *,int);
@@ -2280,7 +2280,7 @@ extern void emergency_remount(void);
extern sector_t bmap(struct inode *, sector_t);
#endif
extern int notify_change(struct dentry *, struct iattr *, struct inode **);
-extern int inode_permission(struct inode *, int);
+extern int inode_permission(struct dentry *, struct inode *, int);
extern int generic_permission(struct inode *, int);

static inline bool execute_ok(struct inode *inode)
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
index 0ae5807480f4..f0e056cbe19e 100644
--- a/include/linux/nfs_fs.h
+++ b/include/linux/nfs_fs.h
@@ -345,7 +345,7 @@ extern int nfs_post_op_update_inode_force_wcc(struct inode *inode, struct nfs_fa
extern int nfs_getattr(struct vfsmount *, struct dentry *, struct kstat *);
extern void nfs_access_add_cache(struct inode *, struct nfs_access_entry *);
extern void nfs_access_set_mask(struct nfs_access_entry *, u32);
-extern int nfs_permission(struct inode *, int);
+extern int nfs_permission(struct dentry *, struct inode *, int);
extern int nfs_open(struct inode *, struct file *);
extern int nfs_release(struct inode *, struct file *);
extern int nfs_attribute_timeout(struct inode *inode);
diff --git a/ipc/mqueue.c b/ipc/mqueue.c
index ccf1f9fd263a..a1c751b5858e 100644
--- a/ipc/mqueue.c
+++ b/ipc/mqueue.c
@@ -728,7 +728,7 @@ static int mq_attr_ok(struct ipc_namespace *ipc_ns, struct mq_attr *attr)
/*
* Invoked when creating a new queue via sys_mq_open
*/
-static struct file *do_create(struct ipc_namespace *ipc_ns, struct inode *dir,
+static struct file *do_create(struct ipc_namespace *ipc_ns, struct dentry *parent,
struct path *path, int oflag, umode_t mode,
struct mq_attr *attr)
{
@@ -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_create(parent, 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_permission(path->dentry, path->dentry->d_inode, acc))
return ERR_PTR(-EACCES);
return dentry_open(path, oflag, current_cred());
}
@@ -824,7 +824,7 @@ SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, umode_t, mode,
goto out;
}
audit_inode_parent_hidden(name, root);
- filp = do_create(ipc_ns, root->d_inode,
+ filp = do_create(ipc_ns, root,
&path, oflag, mode,
u_attr ? &attr : NULL);
}
@@ -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, NULL);
+ err = vfs_unlink(dentry->d_parent, dentry, NULL);
}
dput(dentry);

diff --git a/kernel/sys.c b/kernel/sys.c
index c0a58be780a4..7346f9240571 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -1650,7 +1650,7 @@ static int prctl_set_mm_exe_file(struct mm_struct *mm, unsigned int fd)
exe.file->f_path.mnt->mnt_flags & MNT_NOEXEC)
goto exit;

- err = inode_permission(inode, MAY_EXEC);
+ err = inode_permission(exe.file->f_path.dentry, inode, MAY_EXEC);
if (err)
goto exit;

diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 53385cd4e6f0..597648160d73 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -6147,7 +6147,7 @@ static int memcg_write_event_control(struct cgroup_subsys_state *css,

/* the process need read permission on control file */
/* AV: shouldn't we check that it's been opened for read instead? */
- ret = inode_permission(file_inode(cfile.file), MAY_READ);
+ ret = inode_permission(cfile.file->f_path.dentry, file_inode(cfile.file), MAY_READ);
if (ret < 0)
goto out_put_cfile;

diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index 29fc8bee9702..9509fbf1d3df 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -785,7 +785,7 @@ static struct sock *unix_find_other(struct net *net,
if (err)
goto fail;
inode = path.dentry->d_inode;
- err = inode_permission(inode, MAY_WRITE);
+ err = inode_permission(path.dentry, inode, MAY_WRITE);
if (err)
goto put_fail;

@@ -845,7 +845,7 @@ static int unix_mknod(const char *sun_path, umode_t mode, struct path *res)
*/
err = security_path_mknod(&path, dentry, mode, 0);
if (!err) {
- err = vfs_mknod(path.dentry->d_inode, dentry, mode, 0);
+ err = vfs_mknod(path.dentry, dentry, mode, 0);
if (!err) {
res->mnt = mntget(path.mnt);
res->dentry = dget(dentry);
diff --git a/virt/kvm/assigned-dev.c b/virt/kvm/assigned-dev.c
index 8db43701016f..c12024692c0d 100644
--- a/virt/kvm/assigned-dev.c
+++ b/virt/kvm/assigned-dev.c
@@ -639,7 +639,7 @@ static int probe_sysfs_permissions(struct pci_dev *dev)

inode = path.dentry->d_inode;

- r = inode_permission(inode, MAY_READ | MAY_WRITE | MAY_ACCESS);
+ r = inode_permission(path.dentry, inode, MAY_READ | MAY_WRITE | MAY_ACCESS);
path_put(&path);
if (r)
return r;