linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [GIT PULL] Ceph updates for -rc1
@ 2014-01-28 18:40 Sage Weil
  2014-01-28 21:10 ` Dave Jones
  0 siblings, 1 reply; 37+ messages in thread
From: Sage Weil @ 2014-01-28 18:40 UTC (permalink / raw)
  To: torvalds; +Cc: linux-kernel, ceph-devel, linux-fsdevel

Hi Linus,

Please pull the following Ceph updates from

  git://git.kernel.org/pub/scm/linux/kernel/git/sage/ceph-client.git for-linus

This is a big batch.  From Ilya we have:

 - rbd support for more than ~250 mapped devices (now uses same scheme 
   that SCSI does for device major/minor numbering)
 - crush updates for new mapping behaviors (will be needed for coming 
   erasure coding support, among other things)
 - preliminary support for tiered storage pools

There is also a big series fixing a pile cephfs bugs with clustered MDSs 
from Yan Zheng, ACL support for cephfs from Guangliang Zhao, ceph fscache 
improvements from Li Wang, improved behavior when we get ENOSPC from Josh 
Durgin, some readv/writev improvements from Majianpeng, and the usual mix 
of small cleanups.

Thanks!
sage


----------------------------------------------------------------
Alex Elder (1):
      MAINTAINERS: update an e-mail address

Guangliang Zhao (1):
      ceph: add acl for cephfs

Ilya Dryomov (51):
      rbd: rbd_device::dev_id is an int, format it as such
      rbd: tweak "loaded" message and module description
      rbd: refactor rbd_init() a bit
      rbd: switch to ida for rbd id assignments
      rbd: add 'minor' sysfs rbd device attribute
      rbd: wire up is_visible() sysfs callback for rbd bus
      rbd: add support for single-major device number allocation scheme
      rbd: enable extended devt in single-major mode
      rbd: introduce rbd_dev_header_unwatch_sync() and switch to it
      rbd: tear down watch request if rbd_dev_device_setup() fails
      libceph: all features fields must be u64
      libceph: update ceph_features.h
      crush: pass weight vector size to map function
      crush: factor out (trivial) crush_destroy_rule()
      crush: reduce scope of some local variables
      crush: fix some comments
      crush: eliminate CRUSH_MAX_SET result size limitation
      crush: return CRUSH_ITEM_UNDEF for failed placements with indep
      crush: use breadth-first search for indep mode
      crush: add note about r in recursive choose
      crush: strip firstn conditionals out of crush_choose, rename
      crush: clarify numrep vs endpos
      crush: pass parent r value for indep call
      crush: new SET_CHOOSE_LEAF_TRIES command
      crush: apply chooseleaf_tries to firstn mode too
      crush: add SET_CHOOSE_TRIES rule step
      crush: CHOOSE_LEAF -> CHOOSELEAF throughout
      crush: generalize descend_once
      crush: add set_choose_local_[fallback_]tries steps
      crush: attempts -> tries
      crush: fix crush_choose_firstn comment
      crush: support new indep mode and SET_* steps (crush v2) by default
      libceph: use CEPH_MON_PORT when the specified port is 0
      libceph: rename ceph_msg::front_max to front_alloc_len
      libceph: rename front to front_len in get_reply()
      libceph: fix preallocation check in get_reply()
      libceph: add ceph_kv{malloc,free}() and switch to them
      libceph: dout() is missing a newline
      libceph: start using oloc abstraction
      libceph: move ceph_file_layout helpers to ceph_fs.h
      libceph: rename MAX_OBJ_NAME_SIZE to CEPH_MAX_OID_NAME_LEN
      libceph: introduce and start using oid abstraction
      libceph: replace ceph_calc_ceph_pg() with ceph_oloc_oid_to_pg()
      libceph: CEPH_OSD_FLAG_* enum update
      libceph: add ceph_pg_pool_by_id()
      libceph: follow {read,write}_tier fields on osd request submission
      libceph: rename ceph_osd_request::r_{oloc,oid} to r_base_{oloc,oid}
      libceph: follow redirect replies from osds
      libceph: support CEPH_FEATURE_OSD_CACHEPOOL feature
      ceph: fix dout() compile warnings in ceph_filemap_fault()
      ceph: cast PAGE_SIZE to size_t in ceph_sync_write()

J. Bruce Fields (1):
      ceph: trivial comment fix

Josh Durgin (2):
      libceph: block I/O when PAUSE or FULL osd map flags are set
      libceph: resend all writes after the osdmap loses the full flag

Li Wang (4):
      ceph: Clean up if error occurred in finish_read()
      ceph: Add necessary clean up if invalid reply received in handle_reply()
      ceph fscache: Introduce a routine for uncaching single no data page from fscache
      ceph fscache: Uncaching no data page from fscache in readpage()

Libo Chen (1):
      fs: ceph: new helper: file_inode(file)

Yan, Zheng (13):
      ceph: drop unconnected inodes
      ceph: check caps in filemap_fault and page_mkwrite
      ceph: handle cap export race in try_flush_caps()
      ceph: use ceph_seq_cmp() to compare migrate_seq
      ceph: fix cache revoke race
      ceph: fix trim caps
      ceph: handle -ESTALE reply
      ceph: check inode caps in ceph_d_revalidate
      ceph: handle session flush message
      ceph: remove exported caps when handling cap import message
      ceph: add open export target session helper
      ceph: add imported caps when handling cap export message
      libceph: support CEPH_FEATURE_EXPORT_PEER

majianpeng (2):
      ceph: Implement writev/pwritev for sync operation.
      ceph: implement readv/preadv for sync operation

 Documentation/ABI/testing/sysfs-bus-rbd |  26 ++
 MAINTAINERS                             |   2 +-
 drivers/block/rbd.c                     | 303 ++++++++++++++--------
 fs/ceph/Kconfig                         |  13 +
 fs/ceph/Makefile                        |   1 +
 fs/ceph/acl.c                           | 332 ++++++++++++++++++++++++
 fs/ceph/addr.c                          |  93 ++++++-
 fs/ceph/cache.h                         |  13 +
 fs/ceph/caps.c                          | 338 +++++++++++++++---------
 fs/ceph/dir.c                           |  16 +-
 fs/ceph/file.c                          | 437 ++++++++++++++++++++++----------
 fs/ceph/inode.c                         |  33 ++-
 fs/ceph/ioctl.c                         |   8 +-
 fs/ceph/mds_client.c                    | 132 ++++++----
 fs/ceph/mds_client.h                    |   2 +
 fs/ceph/strings.c                       |   2 +
 fs/ceph/super.c                         |   9 +-
 fs/ceph/super.h                         |  45 +++-
 fs/ceph/xattr.c                         |  60 ++++-
 include/linux/ceph/buffer.h             |   1 -
 include/linux/ceph/ceph_features.h      | 101 +++++---
 include/linux/ceph/ceph_fs.h            |  36 ++-
 include/linux/ceph/libceph.h            |  19 +-
 include/linux/ceph/messenger.h          |  13 +-
 include/linux/ceph/osd_client.h         |  19 +-
 include/linux/ceph/osdmap.h             |  66 +++--
 include/linux/ceph/rados.h              |   4 +
 include/linux/crush/crush.h             |  20 +-
 include/linux/crush/mapper.h            |   3 +-
 net/ceph/buffer.c                       |  22 +-
 net/ceph/ceph_common.c                  |  24 +-
 net/ceph/crush/crush.c                  |   7 +-
 net/ceph/crush/mapper.c                 | 336 +++++++++++++++++++-----
 net/ceph/debugfs.c                      |   3 +-
 net/ceph/messenger.c                    |  32 +--
 net/ceph/mon_client.c                   |   8 +-
 net/ceph/osd_client.c                   | 283 +++++++++++++++++++--
 net/ceph/osdmap.c                       |  78 ++++--
 38 files changed, 2261 insertions(+), 679 deletions(-)
 create mode 100644 fs/ceph/acl.c

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [GIT PULL] Ceph updates for -rc1
  2014-01-28 18:40 [GIT PULL] Ceph updates for -rc1 Sage Weil
@ 2014-01-28 21:10 ` Dave Jones
  2014-01-28 21:48   ` Linus Torvalds
  0 siblings, 1 reply; 37+ messages in thread
From: Dave Jones @ 2014-01-28 21:10 UTC (permalink / raw)
  To: Sage Weil; +Cc: torvalds, linux-kernel, ceph-devel, linux-fsdevel

On Tue, Jan 28, 2014 at 10:40:16AM -0800, Sage Weil wrote:
 > Hi Linus,
 > 
 > Please pull the following Ceph updates from
 > 
 >   git://git.kernel.org/pub/scm/linux/kernel/git/sage/ceph-client.git for-linus
 > 
 > This is a big batch.  From Ilya we have:
 > 
 >  - rbd support for more than ~250 mapped devices (now uses same scheme 
 >    that SCSI does for device major/minor numbering)
 >  - crush updates for new mapping behaviors (will be needed for coming 
 >    erasure coding support, among other things)
 >  - preliminary support for tiered storage pools
 > 
 > There is also a big series fixing a pile cephfs bugs with clustered MDSs 
 > from Yan Zheng, ACL support for cephfs from Guangliang Zhao, ceph fscache 
 > improvements from Li Wang, improved behavior when we get ENOSPC from Josh 
 > Durgin, some readv/writev improvements from Majianpeng, and the usual mix 
 > of small cleanups.

This breaks the build for me.

fs/ceph/acl.c: In function ‘ceph_init_acl’:
fs/ceph/acl.c:216:3: warning: passing argument 1 of ‘posix_acl_create’ from incompatible pointer type [enabled by default]
   ret = posix_acl_create(&acl, GFP_NOFS, &inode->i_mode);
   ^
In file included from include/linux/posix_acl_xattr.h:12:0,
                 from fs/ceph/acl.c:25:
include/linux/posix_acl.h:96:12: note: expected ‘struct inode *’ but argument is of type ‘struct posix_acl **’
 extern int posix_acl_create(struct inode *, umode_t *, struct posix_acl **,
            ^
fs/ceph/acl.c:216:3: warning: passing argument 2 of ‘posix_acl_create’ makes pointer from integer without a cast [enabled by default]
   ret = posix_acl_create(&acl, GFP_NOFS, &inode->i_mode);
   ^
In file included from include/linux/posix_acl_xattr.h:12:0,
                 from fs/ceph/acl.c:25:
include/linux/posix_acl.h:96:12: note: expected ‘umode_t *’ but argument is of type ‘unsigned int’
 extern int posix_acl_create(struct inode *, umode_t *, struct posix_acl **,
            ^
fs/ceph/acl.c:216:3: warning: passing argument 3 of ‘posix_acl_create’ from incompatible pointer type [enabled by default]
   ret = posix_acl_create(&acl, GFP_NOFS, &inode->i_mode);
   ^
In file included from include/linux/posix_acl_xattr.h:12:0,
                 from fs/ceph/acl.c:25:
include/linux/posix_acl.h:96:12: note: expected ‘struct posix_acl **’ but argument is of type ‘umode_t *’
 extern int posix_acl_create(struct inode *, umode_t *, struct posix_acl **,
            ^
fs/ceph/acl.c:216:3: error: too few arguments to function ‘posix_acl_create’
   ret = posix_acl_create(&acl, GFP_NOFS, &inode->i_mode);
   ^
In file included from include/linux/posix_acl_xattr.h:12:0,
                 from fs/ceph/acl.c:25:
include/linux/posix_acl.h:96:12: note: declared here
 extern int posix_acl_create(struct inode *, umode_t *, struct posix_acl **,
            ^
fs/ceph/acl.c: In function ‘ceph_acl_chmod’:
fs/ceph/acl.c:252:2: warning: passing argument 1 of ‘posix_acl_chmod’ from incompatible pointer type [enabled by default]
  ret = posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode);
  ^
In file included from include/linux/posix_acl_xattr.h:12:0,
                 from fs/ceph/acl.c:25:
include/linux/posix_acl.h:95:12: note: expected ‘struct inode *’ but argument is of type ‘struct posix_acl **’
 extern int posix_acl_chmod(struct inode *, umode_t);
            ^
fs/ceph/acl.c:252:2: error: too many arguments to function ‘posix_acl_chmod’
  ret = posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode);
  ^
In file included from include/linux/posix_acl_xattr.h:12:0,
                 from fs/ceph/acl.c:25:
include/linux/posix_acl.h:95:12: note: declared here
 extern int posix_acl_chmod(struct inode *, umode_t);


^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [GIT PULL] Ceph updates for -rc1
  2014-01-28 21:10 ` Dave Jones
@ 2014-01-28 21:48   ` Linus Torvalds
  2014-01-29  6:08     ` Sage Weil
  0 siblings, 1 reply; 37+ messages in thread
From: Linus Torvalds @ 2014-01-28 21:48 UTC (permalink / raw)
  To: Dave Jones, Sage Weil, Linus Torvalds, Linux Kernel Mailing List,
	ceph-devel, linux-fsdevel, Christoph Hellwig, Al Viro,
	Guangliang Zhao, Li Wang

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

On Tue, Jan 28, 2014 at 1:10 PM, Dave Jones <davej@redhat.com> wrote:
>
> This breaks the build for me.

It is my merge (Christoph's ACL changes came in today through the VFS
tree from Al).

I was doing the merges today on my laptop (I had jury duty yesterday
and today), and so I didn't do the allmodconfig build I would normally
do on my (much faster) desktop. Well, actually I did do the full fs
builds for the earlier pulls that actually had some conflicts, but not
for the ceph pull. The conflict was hidden by the fact that the whole
cifs ACL support is new, so there was no data conflict, just a silent
semantic conflict between the new smarter ACL helpers and the new ACL
use in CIFS.

I'm back home now (yay, all the afternoon cases got settled), and I
see the problem now. I should have done an allmodconfig build
immediately after coming home, but I never even thought of it.

Anyway, here's an *untested* conversion to the new posix acl helper
infrastructure. I do wonder if ceph_init_acl() could be converted to
posix_acl_create(), right now that part is a "non-conversion" - it's
just made to use __posix_acl_create() that implements the old
interface.

Al, Christoph, can you please check my conversion for sanity from a
generic posix-acl standpoint?

Sage, Guangliang, Li, can you check the actual cifs usage/sanity of
the attached patch?

Sorry about the messed-up merge. Although I also blame Al, because
he's horrible about having his changes in linux-next, so nobody was
ever really aware of this semantic conflict.

Al. Bad, bad boy. Consider yourself hit with a rolled-up newspaper.

                 Linus

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

 fs/ceph/acl.c   | 103 +-------------------------------------------------------
 fs/ceph/inode.c |   3 +-
 fs/ceph/super.h |   3 --
 fs/ceph/xattr.c |   5 +--
 4 files changed, 6 insertions(+), 108 deletions(-)

diff --git a/fs/ceph/acl.c b/fs/ceph/acl.c
index 64fddbc1d17b..f6911284c9bd 100644
--- a/fs/ceph/acl.c
+++ b/fs/ceph/acl.c
@@ -213,7 +213,7 @@ int ceph_init_acl(struct dentry *dentry, struct inode *inode, struct inode *dir)
 			if (ret)
 				goto out_release;
 		}
-		ret = posix_acl_create(&acl, GFP_NOFS, &inode->i_mode);
+		ret = __posix_acl_create(&acl, GFP_NOFS, &inode->i_mode);
 		if (ret < 0)
 			goto out;
 		else if (ret > 0)
@@ -229,104 +229,3 @@ out_release:
 out:
 	return ret;
 }
-
-int ceph_acl_chmod(struct dentry *dentry, struct inode *inode)
-{
-	struct posix_acl *acl;
-	int ret = 0;
-
-	if (S_ISLNK(inode->i_mode)) {
-		ret = -EOPNOTSUPP;
-		goto out;
-	}
-
-	if (!IS_POSIXACL(inode))
-		goto out;
-
-	acl = ceph_get_acl(inode, ACL_TYPE_ACCESS);
-	if (IS_ERR_OR_NULL(acl)) {
-		ret = PTR_ERR(acl);
-		goto out;
-	}
-
-	ret = posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode);
-	if (ret)
-		goto out;
-	ret = ceph_set_acl(dentry, inode, acl, ACL_TYPE_ACCESS);
-	posix_acl_release(acl);
-out:
-	return ret;
-}
-
-static int ceph_xattr_acl_get(struct dentry *dentry, const char *name,
-				void *value, size_t size, int type)
-{
-	struct posix_acl *acl;
-	int ret = 0;
-
-	if (!IS_POSIXACL(dentry->d_inode))
-		return -EOPNOTSUPP;
-
-	acl = ceph_get_acl(dentry->d_inode, type);
-	if (IS_ERR(acl))
-		return PTR_ERR(acl);
-	if (acl == NULL)
-		return -ENODATA;
-
-	ret = posix_acl_to_xattr(&init_user_ns, acl, value, size);
-	posix_acl_release(acl);
-
-	return ret;
-}
-
-static int ceph_xattr_acl_set(struct dentry *dentry, const char *name,
-			const void *value, size_t size, int flags, int type)
-{
-	int ret = 0;
-	struct posix_acl *acl = NULL;
-
-	if (!inode_owner_or_capable(dentry->d_inode)) {
-		ret = -EPERM;
-		goto out;
-	}
-
-	if (!IS_POSIXACL(dentry->d_inode)) {
-		ret = -EOPNOTSUPP;
-		goto out;
-	}
-
-	if (value) {
-		acl = posix_acl_from_xattr(&init_user_ns, value, size);
-		if (IS_ERR(acl)) {
-			ret = PTR_ERR(acl);
-			goto out;
-		}
-
-		if (acl) {
-			ret = posix_acl_valid(acl);
-			if (ret)
-				goto out_release;
-		}
-	}
-
-	ret = ceph_set_acl(dentry, dentry->d_inode, acl, type);
-
-out_release:
-	posix_acl_release(acl);
-out:
-	return ret;
-}
-
-const struct xattr_handler ceph_xattr_acl_default_handler = {
-	.prefix = POSIX_ACL_XATTR_DEFAULT,
-	.flags  = ACL_TYPE_DEFAULT,
-	.get    = ceph_xattr_acl_get,
-	.set    = ceph_xattr_acl_set,
-};
-
-const struct xattr_handler ceph_xattr_acl_access_handler = {
-	.prefix = POSIX_ACL_XATTR_ACCESS,
-	.flags  = ACL_TYPE_ACCESS,
-	.get    = ceph_xattr_acl_get,
-	.set    = ceph_xattr_acl_set,
-};
diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
index 6fc10a7d7c59..8b8b506636cc 100644
--- a/fs/ceph/inode.c
+++ b/fs/ceph/inode.c
@@ -9,6 +9,7 @@
 #include <linux/namei.h>
 #include <linux/writeback.h>
 #include <linux/vmalloc.h>
+#include <linux/posix_acl.h>
 
 #include "super.h"
 #include "mds_client.h"
@@ -1805,7 +1806,7 @@ int ceph_setattr(struct dentry *dentry, struct iattr *attr)
 		__mark_inode_dirty(inode, inode_dirty_flags);
 
 	if (ia_valid & ATTR_MODE) {
-		err = ceph_acl_chmod(dentry, inode);
+		err = posix_acl_chmod(inode, attr->ia_mode);
 		if (err)
 			goto out_put;
 	}
diff --git a/fs/ceph/super.h b/fs/ceph/super.h
index c299f7d19bf3..345933948b6e 100644
--- a/fs/ceph/super.h
+++ b/fs/ceph/super.h
@@ -736,15 +736,12 @@ extern void __init ceph_xattr_init(void);
 extern void ceph_xattr_exit(void);
 
 /* acl.c */
-extern const struct xattr_handler ceph_xattr_acl_access_handler;
-extern const struct xattr_handler ceph_xattr_acl_default_handler;
 extern const struct xattr_handler *ceph_xattr_handlers[];
 
 #ifdef CONFIG_CEPH_FS_POSIX_ACL
 
 struct posix_acl *ceph_get_acl(struct inode *, int);
 int ceph_init_acl(struct dentry *, struct inode *, struct inode *);
-int ceph_acl_chmod(struct dentry *, struct inode *);
 void ceph_forget_all_cached_acls(struct inode *inode);
 
 #else
diff --git a/fs/ceph/xattr.c b/fs/ceph/xattr.c
index c7581f3733c1..898b6565ad3e 100644
--- a/fs/ceph/xattr.c
+++ b/fs/ceph/xattr.c
@@ -6,6 +6,7 @@
 #include <linux/ceph/decode.h>
 
 #include <linux/xattr.h>
+#include <linux/posix_acl_xattr.h>
 #include <linux/slab.h>
 
 #define XATTR_CEPH_PREFIX "ceph."
@@ -17,8 +18,8 @@
  */
 const struct xattr_handler *ceph_xattr_handlers[] = {
 #ifdef CONFIG_CEPH_FS_POSIX_ACL
-	&ceph_xattr_acl_access_handler,
-	&ceph_xattr_acl_default_handler,
+	&posix_acl_access_xattr_handler,
+	&posix_acl_default_xattr_handler,
 #endif
 	NULL,
 };

^ permalink raw reply related	[flat|nested] 37+ messages in thread

* Re: [GIT PULL] Ceph updates for -rc1
  2014-01-28 21:48   ` Linus Torvalds
@ 2014-01-29  6:08     ` Sage Weil
  2014-01-29 14:30       ` Sage Weil
  0 siblings, 1 reply; 37+ messages in thread
From: Sage Weil @ 2014-01-29  6:08 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Dave Jones, Linux Kernel Mailing List, ceph-devel, linux-fsdevel,
	Christoph Hellwig, Al Viro, Guangliang Zhao, Li Wang,
	zheng.z.yan

Hi Linus,

On Tue, 28 Jan 2014, Linus Torvalds wrote:
> On Tue, Jan 28, 2014 at 1:10 PM, Dave Jones <davej@redhat.com> wrote:
> >
> > This breaks the build for me.
> 
> It is my merge (Christoph's ACL changes came in today through the VFS
> tree from Al).
> 
> I was doing the merges today on my laptop (I had jury duty yesterday
> and today), and so I didn't do the allmodconfig build I would normally
> do on my (much faster) desktop. Well, actually I did do the full fs
> builds for the earlier pulls that actually had some conflicts, but not
> for the ceph pull. The conflict was hidden by the fact that the whole
> cifs ACL support is new, so there was no data conflict, just a silent
> semantic conflict between the new smarter ACL helpers and the new ACL
> use in CIFS.

s/cifs/ceph/ :)

> I'm back home now (yay, all the afternoon cases got settled), and I
> see the problem now. I should have done an allmodconfig build
> immediately after coming home, but I never even thought of it.
>
> Anyway, here's an *untested* conversion to the new posix acl helper
> infrastructure. I do wonder if ceph_init_acl() could be converted to
> posix_acl_create(), right now that part is a "non-conversion" - it's
> just made to use __posix_acl_create() that implements the old
> interface.
> 
> Al, Christoph, can you please check my conversion for sanity from a
> generic posix-acl standpoint?
> 
> Sage, Guangliang, Li, can you check the actual cifs usage/sanity of
> the attached patch?

Superficially at least the conversion looks okay to me, but it's not 
passing my smoke test (it's giving me EOPNOTSUPP for chmod and setxattr 
when setting an ACL).  I'll look at it tomorrow if Guangliang, Li, or Yan 
don't get there first.

I should have caught this before--I knew the ACL changes were coming and 
forgot to check the merged build beforehand!

sage

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [GIT PULL] Ceph updates for -rc1
  2014-01-29  6:08     ` Sage Weil
@ 2014-01-29 14:30       ` Sage Weil
  2014-01-29 16:36         ` Ilya Dryomov
  2014-01-30 10:46         ` [GIT PULL] Ceph updates for -rc1 Christoph Hellwig
  0 siblings, 2 replies; 37+ messages in thread
From: Sage Weil @ 2014-01-29 14:30 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Dave Jones, Linux Kernel Mailing List, ceph-devel, linux-fsdevel,
	Christoph Hellwig, Al Viro, Guangliang Zhao, Li Wang,
	zheng.z.yan

On Tue, 28 Jan 2014, Sage Weil wrote:
> Hi Linus,
> 
> On Tue, 28 Jan 2014, Linus Torvalds wrote:
> > On Tue, Jan 28, 2014 at 1:10 PM, Dave Jones <davej@redhat.com> wrote:
> > >
> > > This breaks the build for me.
> > 
> > It is my merge (Christoph's ACL changes came in today through the VFS
> > tree from Al).
> > 
> > I was doing the merges today on my laptop (I had jury duty yesterday
> > and today), and so I didn't do the allmodconfig build I would normally
> > do on my (much faster) desktop. Well, actually I did do the full fs
> > builds for the earlier pulls that actually had some conflicts, but not
> > for the ceph pull. The conflict was hidden by the fact that the whole
> > cifs ACL support is new, so there was no data conflict, just a silent
> > semantic conflict between the new smarter ACL helpers and the new ACL
> > use in CIFS.
> 
> s/cifs/ceph/ :)
> 
> > I'm back home now (yay, all the afternoon cases got settled), and I
> > see the problem now. I should have done an allmodconfig build
> > immediately after coming home, but I never even thought of it.
> >
> > Anyway, here's an *untested* conversion to the new posix acl helper
> > infrastructure. I do wonder if ceph_init_acl() could be converted to
> > posix_acl_create(), right now that part is a "non-conversion" - it's
> > just made to use __posix_acl_create() that implements the old
> > interface.
> > 
> > Al, Christoph, can you please check my conversion for sanity from a
> > generic posix-acl standpoint?
> > 
> > Sage, Guangliang, Li, can you check the actual cifs usage/sanity of
> > the attached patch?
> 
> Superficially at least the conversion looks okay to me, but it's not 
> passing my smoke test (it's giving me EOPNOTSUPP for chmod and setxattr 
> when setting an ACL).  I'll look at it tomorrow if Guangliang, Li, or Yan 
> don't get there first.

The set_acl inode_operation wasn't getting set, and the prototype needed 
to be adjusted a bit (it doesn't take a dentry anymore).  All seems to be 
well with the below patch.

Thanks!
sage


>From 01baa54e113060eb9147548fe7beb572522a645a Mon Sep 17 00:00:00 2001
From: Sage Weil <sage@inktank.com>
Date: Wed, 29 Jan 2014 06:22:25 -0800
Subject: [PATCH] ceph: fix posix ACL hooks

The merge of 7221fe4c2 raced with upstream changes in the generic POSIX
ACL code (2aeccbe95).  Update Ceph to use the new helpers as well by
dropping the now-generic functions and setting the set_acl inode op.

Signed-off-by: Sage Weil <sage@inktank.com>
---
 fs/ceph/acl.c   | 112 +++-----------------------------------------------------
 fs/ceph/dir.c   |   1 +
 fs/ceph/inode.c |   5 ++-
 fs/ceph/super.h |   5 +--
 fs/ceph/xattr.c |   5 ++-
 5 files changed, 15 insertions(+), 113 deletions(-)

diff --git a/fs/ceph/acl.c b/fs/ceph/acl.c
index 64fddbc..66d377a 100644
--- a/fs/ceph/acl.c
+++ b/fs/ceph/acl.c
@@ -107,14 +107,14 @@ struct posix_acl *ceph_get_acl(struct inode *inode, int type)
 	return acl;
 }
 
-static int ceph_set_acl(struct dentry *dentry, struct inode *inode,
-				struct posix_acl *acl, int type)
+int ceph_set_acl(struct inode *inode, struct posix_acl *acl, int type)
 {
 	int ret = 0, size = 0;
 	const char *name = NULL;
 	char *value = NULL;
 	struct iattr newattrs;
 	umode_t new_mode = inode->i_mode, old_mode = inode->i_mode;
+	struct dentry *dentry = d_find_alias(inode);
 
 	if (acl) {
 		ret = posix_acl_valid(acl);
@@ -208,16 +208,15 @@ int ceph_init_acl(struct dentry *dentry, struct inode *inode, struct inode *dir)
 
 	if (IS_POSIXACL(dir) && acl) {
 		if (S_ISDIR(inode->i_mode)) {
-			ret = ceph_set_acl(dentry, inode, acl,
-						ACL_TYPE_DEFAULT);
+			ret = ceph_set_acl(inode, acl, ACL_TYPE_DEFAULT);
 			if (ret)
 				goto out_release;
 		}
-		ret = posix_acl_create(&acl, GFP_NOFS, &inode->i_mode);
+		ret = __posix_acl_create(&acl, GFP_NOFS, &inode->i_mode);
 		if (ret < 0)
 			goto out;
 		else if (ret > 0)
-			ret = ceph_set_acl(dentry, inode, acl, ACL_TYPE_ACCESS);
+			ret = ceph_set_acl(inode, acl, ACL_TYPE_ACCESS);
 		else
 			cache_no_acl(inode);
 	} else {
@@ -229,104 +228,3 @@ out_release:
 out:
 	return ret;
 }
-
-int ceph_acl_chmod(struct dentry *dentry, struct inode *inode)
-{
-	struct posix_acl *acl;
-	int ret = 0;
-
-	if (S_ISLNK(inode->i_mode)) {
-		ret = -EOPNOTSUPP;
-		goto out;
-	}
-
-	if (!IS_POSIXACL(inode))
-		goto out;
-
-	acl = ceph_get_acl(inode, ACL_TYPE_ACCESS);
-	if (IS_ERR_OR_NULL(acl)) {
-		ret = PTR_ERR(acl);
-		goto out;
-	}
-
-	ret = posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode);
-	if (ret)
-		goto out;
-	ret = ceph_set_acl(dentry, inode, acl, ACL_TYPE_ACCESS);
-	posix_acl_release(acl);
-out:
-	return ret;
-}
-
-static int ceph_xattr_acl_get(struct dentry *dentry, const char *name,
-				void *value, size_t size, int type)
-{
-	struct posix_acl *acl;
-	int ret = 0;
-
-	if (!IS_POSIXACL(dentry->d_inode))
-		return -EOPNOTSUPP;
-
-	acl = ceph_get_acl(dentry->d_inode, type);
-	if (IS_ERR(acl))
-		return PTR_ERR(acl);
-	if (acl == NULL)
-		return -ENODATA;
-
-	ret = posix_acl_to_xattr(&init_user_ns, acl, value, size);
-	posix_acl_release(acl);
-
-	return ret;
-}
-
-static int ceph_xattr_acl_set(struct dentry *dentry, const char *name,
-			const void *value, size_t size, int flags, int type)
-{
-	int ret = 0;
-	struct posix_acl *acl = NULL;
-
-	if (!inode_owner_or_capable(dentry->d_inode)) {
-		ret = -EPERM;
-		goto out;
-	}
-
-	if (!IS_POSIXACL(dentry->d_inode)) {
-		ret = -EOPNOTSUPP;
-		goto out;
-	}
-
-	if (value) {
-		acl = posix_acl_from_xattr(&init_user_ns, value, size);
-		if (IS_ERR(acl)) {
-			ret = PTR_ERR(acl);
-			goto out;
-		}
-
-		if (acl) {
-			ret = posix_acl_valid(acl);
-			if (ret)
-				goto out_release;
-		}
-	}
-
-	ret = ceph_set_acl(dentry, dentry->d_inode, acl, type);
-
-out_release:
-	posix_acl_release(acl);
-out:
-	return ret;
-}
-
-const struct xattr_handler ceph_xattr_acl_default_handler = {
-	.prefix = POSIX_ACL_XATTR_DEFAULT,
-	.flags  = ACL_TYPE_DEFAULT,
-	.get    = ceph_xattr_acl_get,
-	.set    = ceph_xattr_acl_set,
-};
-
-const struct xattr_handler ceph_xattr_acl_access_handler = {
-	.prefix = POSIX_ACL_XATTR_ACCESS,
-	.flags  = ACL_TYPE_ACCESS,
-	.get    = ceph_xattr_acl_get,
-	.set    = ceph_xattr_acl_set,
-};
diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c
index 619616d..6da4df8 100644
--- a/fs/ceph/dir.c
+++ b/fs/ceph/dir.c
@@ -1303,6 +1303,7 @@ const struct inode_operations ceph_dir_iops = {
 	.listxattr = ceph_listxattr,
 	.removexattr = ceph_removexattr,
 	.get_acl = ceph_get_acl,
+	.set_acl = ceph_set_acl,
 	.mknod = ceph_mknod,
 	.symlink = ceph_symlink,
 	.mkdir = ceph_mkdir,
diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
index 6fc10a7..32d519d 100644
--- a/fs/ceph/inode.c
+++ b/fs/ceph/inode.c
@@ -9,6 +9,7 @@
 #include <linux/namei.h>
 #include <linux/writeback.h>
 #include <linux/vmalloc.h>
+#include <linux/posix_acl.h>
 
 #include "super.h"
 #include "mds_client.h"
@@ -96,6 +97,7 @@ const struct inode_operations ceph_file_iops = {
 	.listxattr = ceph_listxattr,
 	.removexattr = ceph_removexattr,
 	.get_acl = ceph_get_acl,
+	.set_acl = ceph_set_acl,
 };
 
 
@@ -1615,6 +1617,7 @@ static const struct inode_operations ceph_symlink_iops = {
 	.listxattr = ceph_listxattr,
 	.removexattr = ceph_removexattr,
 	.get_acl = ceph_get_acl,
+	.set_acl = ceph_set_acl,
 };
 
 /*
@@ -1805,7 +1808,7 @@ int ceph_setattr(struct dentry *dentry, struct iattr *attr)
 		__mark_inode_dirty(inode, inode_dirty_flags);
 
 	if (ia_valid & ATTR_MODE) {
-		err = ceph_acl_chmod(dentry, inode);
+		err = posix_acl_chmod(inode, attr->ia_mode);
 		if (err)
 			goto out_put;
 	}
diff --git a/fs/ceph/super.h b/fs/ceph/super.h
index c299f7d..eabf601 100644
--- a/fs/ceph/super.h
+++ b/fs/ceph/super.h
@@ -718,6 +718,7 @@ 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_setattr(struct dentry *dentry, struct iattr *attr);
+extern int ceph_setattr(struct dentry *dentry, struct iattr *attr);
 extern int ceph_getattr(struct vfsmount *mnt, struct dentry *dentry,
 			struct kstat *stat);
 
@@ -736,15 +737,13 @@ extern void __init ceph_xattr_init(void);
 extern void ceph_xattr_exit(void);
 
 /* acl.c */
-extern const struct xattr_handler ceph_xattr_acl_access_handler;
-extern const struct xattr_handler ceph_xattr_acl_default_handler;
 extern const struct xattr_handler *ceph_xattr_handlers[];
 
 #ifdef CONFIG_CEPH_FS_POSIX_ACL
 
 struct posix_acl *ceph_get_acl(struct inode *, int);
+int ceph_set_acl(struct inode *inode, struct posix_acl *acl, int type);
 int ceph_init_acl(struct dentry *, struct inode *, struct inode *);
-int ceph_acl_chmod(struct dentry *, struct inode *);
 void ceph_forget_all_cached_acls(struct inode *inode);
 
 #else
diff --git a/fs/ceph/xattr.c b/fs/ceph/xattr.c
index c7581f37..898b656 100644
--- a/fs/ceph/xattr.c
+++ b/fs/ceph/xattr.c
@@ -6,6 +6,7 @@
 #include <linux/ceph/decode.h>
 
 #include <linux/xattr.h>
+#include <linux/posix_acl_xattr.h>
 #include <linux/slab.h>
 
 #define XATTR_CEPH_PREFIX "ceph."
@@ -17,8 +18,8 @@
  */
 const struct xattr_handler *ceph_xattr_handlers[] = {
 #ifdef CONFIG_CEPH_FS_POSIX_ACL
-	&ceph_xattr_acl_access_handler,
-	&ceph_xattr_acl_default_handler,
+	&posix_acl_access_xattr_handler,
+	&posix_acl_default_xattr_handler,
 #endif
 	NULL,
 };
-- 
1.8.5.1


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* Re: [GIT PULL] Ceph updates for -rc1
  2014-01-29 14:30       ` Sage Weil
@ 2014-01-29 16:36         ` Ilya Dryomov
  2014-01-29 16:37           ` [PATCH v2] ceph: fix posix ACL hooks Ilya Dryomov
  2014-01-30 10:46         ` [GIT PULL] Ceph updates for -rc1 Christoph Hellwig
  1 sibling, 1 reply; 37+ messages in thread
From: Ilya Dryomov @ 2014-01-29 16:36 UTC (permalink / raw)
  To: Sage Weil
  Cc: Linus Torvalds, Dave Jones, Linux Kernel Mailing List,
	Ceph Development, linux-fsdevel, Christoph Hellwig, Al Viro,
	Guangliang Zhao, Li Wang, Yan, Zheng

On Wed, Jan 29, 2014 at 4:30 PM, Sage Weil <sage@inktank.com> wrote:
> On Tue, 28 Jan 2014, Sage Weil wrote:
>> Hi Linus,
>>
>> On Tue, 28 Jan 2014, Linus Torvalds wrote:
>> > On Tue, Jan 28, 2014 at 1:10 PM, Dave Jones <davej@redhat.com> wrote:
>> > >
>> > > This breaks the build for me.
>> >
>> > It is my merge (Christoph's ACL changes came in today through the VFS
>> > tree from Al).
>> >
>> > I was doing the merges today on my laptop (I had jury duty yesterday
>> > and today), and so I didn't do the allmodconfig build I would normally
>> > do on my (much faster) desktop. Well, actually I did do the full fs
>> > builds for the earlier pulls that actually had some conflicts, but not
>> > for the ceph pull. The conflict was hidden by the fact that the whole
>> > cifs ACL support is new, so there was no data conflict, just a silent
>> > semantic conflict between the new smarter ACL helpers and the new ACL
>> > use in CIFS.
>>
>> s/cifs/ceph/ :)
>>
>> > I'm back home now (yay, all the afternoon cases got settled), and I
>> > see the problem now. I should have done an allmodconfig build
>> > immediately after coming home, but I never even thought of it.
>> >
>> > Anyway, here's an *untested* conversion to the new posix acl helper
>> > infrastructure. I do wonder if ceph_init_acl() could be converted to
>> > posix_acl_create(), right now that part is a "non-conversion" - it's
>> > just made to use __posix_acl_create() that implements the old
>> > interface.
>> >
>> > Al, Christoph, can you please check my conversion for sanity from a
>> > generic posix-acl standpoint?
>> >
>> > Sage, Guangliang, Li, can you check the actual cifs usage/sanity of
>> > the attached patch?
>>
>> Superficially at least the conversion looks okay to me, but it's not
>> passing my smoke test (it's giving me EOPNOTSUPP for chmod and setxattr
>> when setting an ACL).  I'll look at it tomorrow if Guangliang, Li, or Yan
>> don't get there first.
>
> The set_acl inode_operation wasn't getting set, and the prototype needed
> to be adjusted a bit (it doesn't take a dentry anymore).  All seems to be
> well with the below patch.
>
> Thanks!
> sage
>
>
> From 01baa54e113060eb9147548fe7beb572522a645a Mon Sep 17 00:00:00 2001
> From: Sage Weil <sage@inktank.com>
> Date: Wed, 29 Jan 2014 06:22:25 -0800
> Subject: [PATCH] ceph: fix posix ACL hooks
>
> The merge of 7221fe4c2 raced with upstream changes in the generic POSIX
> ACL code (2aeccbe95).  Update Ceph to use the new helpers as well by
> dropping the now-generic functions and setting the set_acl inode op.
>
> Signed-off-by: Sage Weil <sage@inktank.com>
> ---
>  fs/ceph/acl.c   | 112 +++-----------------------------------------------------
>  fs/ceph/dir.c   |   1 +
>  fs/ceph/inode.c |   5 ++-
>  fs/ceph/super.h |   5 +--
>  fs/ceph/xattr.c |   5 ++-
>  5 files changed, 15 insertions(+), 113 deletions(-)
>
> diff --git a/fs/ceph/acl.c b/fs/ceph/acl.c
> index 64fddbc..66d377a 100644
> --- a/fs/ceph/acl.c
> +++ b/fs/ceph/acl.c
> @@ -107,14 +107,14 @@ struct posix_acl *ceph_get_acl(struct inode *inode, int type)
>         return acl;
>  }
>
> -static int ceph_set_acl(struct dentry *dentry, struct inode *inode,
> -                               struct posix_acl *acl, int type)
> +int ceph_set_acl(struct inode *inode, struct posix_acl *acl, int type)
>  {
>         int ret = 0, size = 0;
>         const char *name = NULL;
>         char *value = NULL;
>         struct iattr newattrs;
>         umode_t new_mode = inode->i_mode, old_mode = inode->i_mode;
> +       struct dentry *dentry = d_find_alias(inode);
>
>         if (acl) {
>                 ret = posix_acl_valid(acl);
> @@ -208,16 +208,15 @@ int ceph_init_acl(struct dentry *dentry, struct inode *inode, struct inode *dir)
>
>         if (IS_POSIXACL(dir) && acl) {
>                 if (S_ISDIR(inode->i_mode)) {
> -                       ret = ceph_set_acl(dentry, inode, acl,
> -                                               ACL_TYPE_DEFAULT);
> +                       ret = ceph_set_acl(inode, acl, ACL_TYPE_DEFAULT);
>                         if (ret)
>                                 goto out_release;
>                 }
> -               ret = posix_acl_create(&acl, GFP_NOFS, &inode->i_mode);
> +               ret = __posix_acl_create(&acl, GFP_NOFS, &inode->i_mode);
>                 if (ret < 0)
>                         goto out;
>                 else if (ret > 0)
> -                       ret = ceph_set_acl(dentry, inode, acl, ACL_TYPE_ACCESS);
> +                       ret = ceph_set_acl(inode, acl, ACL_TYPE_ACCESS);
>                 else
>                         cache_no_acl(inode);
>         } else {
> @@ -229,104 +228,3 @@ out_release:
>  out:
>         return ret;
>  }
> -
> -int ceph_acl_chmod(struct dentry *dentry, struct inode *inode)
> -{
> -       struct posix_acl *acl;
> -       int ret = 0;
> -
> -       if (S_ISLNK(inode->i_mode)) {
> -               ret = -EOPNOTSUPP;
> -               goto out;
> -       }
> -
> -       if (!IS_POSIXACL(inode))
> -               goto out;
> -
> -       acl = ceph_get_acl(inode, ACL_TYPE_ACCESS);
> -       if (IS_ERR_OR_NULL(acl)) {
> -               ret = PTR_ERR(acl);
> -               goto out;
> -       }
> -
> -       ret = posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode);
> -       if (ret)
> -               goto out;
> -       ret = ceph_set_acl(dentry, inode, acl, ACL_TYPE_ACCESS);
> -       posix_acl_release(acl);
> -out:
> -       return ret;
> -}
> -
> -static int ceph_xattr_acl_get(struct dentry *dentry, const char *name,
> -                               void *value, size_t size, int type)
> -{
> -       struct posix_acl *acl;
> -       int ret = 0;
> -
> -       if (!IS_POSIXACL(dentry->d_inode))
> -               return -EOPNOTSUPP;
> -
> -       acl = ceph_get_acl(dentry->d_inode, type);
> -       if (IS_ERR(acl))
> -               return PTR_ERR(acl);
> -       if (acl == NULL)
> -               return -ENODATA;
> -
> -       ret = posix_acl_to_xattr(&init_user_ns, acl, value, size);
> -       posix_acl_release(acl);
> -
> -       return ret;
> -}
> -
> -static int ceph_xattr_acl_set(struct dentry *dentry, const char *name,
> -                       const void *value, size_t size, int flags, int type)
> -{
> -       int ret = 0;
> -       struct posix_acl *acl = NULL;
> -
> -       if (!inode_owner_or_capable(dentry->d_inode)) {
> -               ret = -EPERM;
> -               goto out;
> -       }
> -
> -       if (!IS_POSIXACL(dentry->d_inode)) {
> -               ret = -EOPNOTSUPP;
> -               goto out;
> -       }
> -
> -       if (value) {
> -               acl = posix_acl_from_xattr(&init_user_ns, value, size);
> -               if (IS_ERR(acl)) {
> -                       ret = PTR_ERR(acl);
> -                       goto out;
> -               }
> -
> -               if (acl) {
> -                       ret = posix_acl_valid(acl);
> -                       if (ret)
> -                               goto out_release;
> -               }
> -       }
> -
> -       ret = ceph_set_acl(dentry, dentry->d_inode, acl, type);
> -
> -out_release:
> -       posix_acl_release(acl);
> -out:
> -       return ret;
> -}
> -
> -const struct xattr_handler ceph_xattr_acl_default_handler = {
> -       .prefix = POSIX_ACL_XATTR_DEFAULT,
> -       .flags  = ACL_TYPE_DEFAULT,
> -       .get    = ceph_xattr_acl_get,
> -       .set    = ceph_xattr_acl_set,
> -};
> -
> -const struct xattr_handler ceph_xattr_acl_access_handler = {
> -       .prefix = POSIX_ACL_XATTR_ACCESS,
> -       .flags  = ACL_TYPE_ACCESS,
> -       .get    = ceph_xattr_acl_get,
> -       .set    = ceph_xattr_acl_set,
> -};
> diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c
> index 619616d..6da4df8 100644
> --- a/fs/ceph/dir.c
> +++ b/fs/ceph/dir.c
> @@ -1303,6 +1303,7 @@ const struct inode_operations ceph_dir_iops = {
>         .listxattr = ceph_listxattr,
>         .removexattr = ceph_removexattr,
>         .get_acl = ceph_get_acl,
> +       .set_acl = ceph_set_acl,
>         .mknod = ceph_mknod,
>         .symlink = ceph_symlink,
>         .mkdir = ceph_mkdir,
> diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
> index 6fc10a7..32d519d 100644
> --- a/fs/ceph/inode.c
> +++ b/fs/ceph/inode.c
> @@ -9,6 +9,7 @@
>  #include <linux/namei.h>
>  #include <linux/writeback.h>
>  #include <linux/vmalloc.h>
> +#include <linux/posix_acl.h>
>
>  #include "super.h"
>  #include "mds_client.h"
> @@ -96,6 +97,7 @@ const struct inode_operations ceph_file_iops = {
>         .listxattr = ceph_listxattr,
>         .removexattr = ceph_removexattr,
>         .get_acl = ceph_get_acl,
> +       .set_acl = ceph_set_acl,
>  };
>
>
> @@ -1615,6 +1617,7 @@ static const struct inode_operations ceph_symlink_iops = {
>         .listxattr = ceph_listxattr,
>         .removexattr = ceph_removexattr,
>         .get_acl = ceph_get_acl,
> +       .set_acl = ceph_set_acl,
>  };
>
>  /*
> @@ -1805,7 +1808,7 @@ int ceph_setattr(struct dentry *dentry, struct iattr *attr)
>                 __mark_inode_dirty(inode, inode_dirty_flags);
>
>         if (ia_valid & ATTR_MODE) {
> -               err = ceph_acl_chmod(dentry, inode);
> +               err = posix_acl_chmod(inode, attr->ia_mode);
>                 if (err)
>                         goto out_put;
>         }
> diff --git a/fs/ceph/super.h b/fs/ceph/super.h
> index c299f7d..eabf601 100644
> --- a/fs/ceph/super.h
> +++ b/fs/ceph/super.h
> @@ -718,6 +718,7 @@ 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_setattr(struct dentry *dentry, struct iattr *attr);
> +extern int ceph_setattr(struct dentry *dentry, struct iattr *attr);
>  extern int ceph_getattr(struct vfsmount *mnt, struct dentry *dentry,
>                         struct kstat *stat);
>
> @@ -736,15 +737,13 @@ extern void __init ceph_xattr_init(void);
>  extern void ceph_xattr_exit(void);
>
>  /* acl.c */
> -extern const struct xattr_handler ceph_xattr_acl_access_handler;
> -extern const struct xattr_handler ceph_xattr_acl_default_handler;
>  extern const struct xattr_handler *ceph_xattr_handlers[];
>
>  #ifdef CONFIG_CEPH_FS_POSIX_ACL
>
>  struct posix_acl *ceph_get_acl(struct inode *, int);
> +int ceph_set_acl(struct inode *inode, struct posix_acl *acl, int type);
>  int ceph_init_acl(struct dentry *, struct inode *, struct inode *);
> -int ceph_acl_chmod(struct dentry *, struct inode *);
>  void ceph_forget_all_cached_acls(struct inode *inode);
>
>  #else

Sage missed the '#define ceph_set_acl NULL' for the !CEPH_FS_POSIX_ACL
case.  v2 should be in-reply-to this message.

Thanks,

                Ilya

^ permalink raw reply	[flat|nested] 37+ messages in thread

* [PATCH v2] ceph: fix posix ACL hooks
  2014-01-29 16:36         ` Ilya Dryomov
@ 2014-01-29 16:37           ` Ilya Dryomov
  2014-01-29 19:09             ` Linus Torvalds
  2014-01-29 20:43             ` Ilya Dryomov
  0 siblings, 2 replies; 37+ messages in thread
From: Ilya Dryomov @ 2014-01-29 16:37 UTC (permalink / raw)
  To: Sage Weil
  Cc: Linus Torvalds, Dave Jones, Linux Kernel Mailing List,
	ceph-devel, linux-fsdevel, Christoph Hellwig, Al Viro,
	Guangliang Zhao, Li Wang, zheng.z.yan

From: Sage Weil <sage@inktank.com>

The merge of 7221fe4c2 raced with upstream changes in the generic POSIX
ACL code (2aeccbe95).  Update Ceph to use the new helpers as well by
dropping the now-generic functions and setting the set_acl inode op.

Signed-off-by: Sage Weil <sage@inktank.com>
---
v2: fixed the !CONFIG_CEPH_FS_POSIX_ACL case

 fs/ceph/acl.c   |  112 +++----------------------------------------------------
 fs/ceph/dir.c   |    1 +
 fs/ceph/inode.c |    5 ++-
 fs/ceph/super.h |    6 +--
 fs/ceph/xattr.c |    5 ++-
 5 files changed, 16 insertions(+), 113 deletions(-)

diff --git a/fs/ceph/acl.c b/fs/ceph/acl.c
index 64fddbc1d17b..66d377a12f7c 100644
--- a/fs/ceph/acl.c
+++ b/fs/ceph/acl.c
@@ -107,14 +107,14 @@ struct posix_acl *ceph_get_acl(struct inode *inode, int type)
 	return acl;
 }
 
-static int ceph_set_acl(struct dentry *dentry, struct inode *inode,
-				struct posix_acl *acl, int type)
+int ceph_set_acl(struct inode *inode, struct posix_acl *acl, int type)
 {
 	int ret = 0, size = 0;
 	const char *name = NULL;
 	char *value = NULL;
 	struct iattr newattrs;
 	umode_t new_mode = inode->i_mode, old_mode = inode->i_mode;
+	struct dentry *dentry = d_find_alias(inode);
 
 	if (acl) {
 		ret = posix_acl_valid(acl);
@@ -208,16 +208,15 @@ int ceph_init_acl(struct dentry *dentry, struct inode *inode, struct inode *dir)
 
 	if (IS_POSIXACL(dir) && acl) {
 		if (S_ISDIR(inode->i_mode)) {
-			ret = ceph_set_acl(dentry, inode, acl,
-						ACL_TYPE_DEFAULT);
+			ret = ceph_set_acl(inode, acl, ACL_TYPE_DEFAULT);
 			if (ret)
 				goto out_release;
 		}
-		ret = posix_acl_create(&acl, GFP_NOFS, &inode->i_mode);
+		ret = __posix_acl_create(&acl, GFP_NOFS, &inode->i_mode);
 		if (ret < 0)
 			goto out;
 		else if (ret > 0)
-			ret = ceph_set_acl(dentry, inode, acl, ACL_TYPE_ACCESS);
+			ret = ceph_set_acl(inode, acl, ACL_TYPE_ACCESS);
 		else
 			cache_no_acl(inode);
 	} else {
@@ -229,104 +228,3 @@ out_release:
 out:
 	return ret;
 }
-
-int ceph_acl_chmod(struct dentry *dentry, struct inode *inode)
-{
-	struct posix_acl *acl;
-	int ret = 0;
-
-	if (S_ISLNK(inode->i_mode)) {
-		ret = -EOPNOTSUPP;
-		goto out;
-	}
-
-	if (!IS_POSIXACL(inode))
-		goto out;
-
-	acl = ceph_get_acl(inode, ACL_TYPE_ACCESS);
-	if (IS_ERR_OR_NULL(acl)) {
-		ret = PTR_ERR(acl);
-		goto out;
-	}
-
-	ret = posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode);
-	if (ret)
-		goto out;
-	ret = ceph_set_acl(dentry, inode, acl, ACL_TYPE_ACCESS);
-	posix_acl_release(acl);
-out:
-	return ret;
-}
-
-static int ceph_xattr_acl_get(struct dentry *dentry, const char *name,
-				void *value, size_t size, int type)
-{
-	struct posix_acl *acl;
-	int ret = 0;
-
-	if (!IS_POSIXACL(dentry->d_inode))
-		return -EOPNOTSUPP;
-
-	acl = ceph_get_acl(dentry->d_inode, type);
-	if (IS_ERR(acl))
-		return PTR_ERR(acl);
-	if (acl == NULL)
-		return -ENODATA;
-
-	ret = posix_acl_to_xattr(&init_user_ns, acl, value, size);
-	posix_acl_release(acl);
-
-	return ret;
-}
-
-static int ceph_xattr_acl_set(struct dentry *dentry, const char *name,
-			const void *value, size_t size, int flags, int type)
-{
-	int ret = 0;
-	struct posix_acl *acl = NULL;
-
-	if (!inode_owner_or_capable(dentry->d_inode)) {
-		ret = -EPERM;
-		goto out;
-	}
-
-	if (!IS_POSIXACL(dentry->d_inode)) {
-		ret = -EOPNOTSUPP;
-		goto out;
-	}
-
-	if (value) {
-		acl = posix_acl_from_xattr(&init_user_ns, value, size);
-		if (IS_ERR(acl)) {
-			ret = PTR_ERR(acl);
-			goto out;
-		}
-
-		if (acl) {
-			ret = posix_acl_valid(acl);
-			if (ret)
-				goto out_release;
-		}
-	}
-
-	ret = ceph_set_acl(dentry, dentry->d_inode, acl, type);
-
-out_release:
-	posix_acl_release(acl);
-out:
-	return ret;
-}
-
-const struct xattr_handler ceph_xattr_acl_default_handler = {
-	.prefix = POSIX_ACL_XATTR_DEFAULT,
-	.flags  = ACL_TYPE_DEFAULT,
-	.get    = ceph_xattr_acl_get,
-	.set    = ceph_xattr_acl_set,
-};
-
-const struct xattr_handler ceph_xattr_acl_access_handler = {
-	.prefix = POSIX_ACL_XATTR_ACCESS,
-	.flags  = ACL_TYPE_ACCESS,
-	.get    = ceph_xattr_acl_get,
-	.set    = ceph_xattr_acl_set,
-};
diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c
index 619616d585b0..6da4df84ba30 100644
--- a/fs/ceph/dir.c
+++ b/fs/ceph/dir.c
@@ -1303,6 +1303,7 @@ const struct inode_operations ceph_dir_iops = {
 	.listxattr = ceph_listxattr,
 	.removexattr = ceph_removexattr,
 	.get_acl = ceph_get_acl,
+	.set_acl = ceph_set_acl,
 	.mknod = ceph_mknod,
 	.symlink = ceph_symlink,
 	.mkdir = ceph_mkdir,
diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
index 6fc10a7d7c59..32d519d8a2e2 100644
--- a/fs/ceph/inode.c
+++ b/fs/ceph/inode.c
@@ -9,6 +9,7 @@
 #include <linux/namei.h>
 #include <linux/writeback.h>
 #include <linux/vmalloc.h>
+#include <linux/posix_acl.h>
 
 #include "super.h"
 #include "mds_client.h"
@@ -96,6 +97,7 @@ const struct inode_operations ceph_file_iops = {
 	.listxattr = ceph_listxattr,
 	.removexattr = ceph_removexattr,
 	.get_acl = ceph_get_acl,
+	.set_acl = ceph_set_acl,
 };
 
 
@@ -1615,6 +1617,7 @@ static const struct inode_operations ceph_symlink_iops = {
 	.listxattr = ceph_listxattr,
 	.removexattr = ceph_removexattr,
 	.get_acl = ceph_get_acl,
+	.set_acl = ceph_set_acl,
 };
 
 /*
@@ -1805,7 +1808,7 @@ int ceph_setattr(struct dentry *dentry, struct iattr *attr)
 		__mark_inode_dirty(inode, inode_dirty_flags);
 
 	if (ia_valid & ATTR_MODE) {
-		err = ceph_acl_chmod(dentry, inode);
+		err = posix_acl_chmod(inode, attr->ia_mode);
 		if (err)
 			goto out_put;
 	}
diff --git a/fs/ceph/super.h b/fs/ceph/super.h
index c299f7d19bf3..aa260590f615 100644
--- a/fs/ceph/super.h
+++ b/fs/ceph/super.h
@@ -718,6 +718,7 @@ 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_setattr(struct dentry *dentry, struct iattr *attr);
+extern int ceph_setattr(struct dentry *dentry, struct iattr *attr);
 extern int ceph_getattr(struct vfsmount *mnt, struct dentry *dentry,
 			struct kstat *stat);
 
@@ -736,20 +737,19 @@ extern void __init ceph_xattr_init(void);
 extern void ceph_xattr_exit(void);
 
 /* acl.c */
-extern const struct xattr_handler ceph_xattr_acl_access_handler;
-extern const struct xattr_handler ceph_xattr_acl_default_handler;
 extern const struct xattr_handler *ceph_xattr_handlers[];
 
 #ifdef CONFIG_CEPH_FS_POSIX_ACL
 
 struct posix_acl *ceph_get_acl(struct inode *, int);
+int ceph_set_acl(struct inode *inode, struct posix_acl *acl, int type);
 int ceph_init_acl(struct dentry *, struct inode *, struct inode *);
-int ceph_acl_chmod(struct dentry *, struct inode *);
 void ceph_forget_all_cached_acls(struct inode *inode);
 
 #else
 
 #define ceph_get_acl NULL
+#define ceph_set_acl NULL
 
 static inline int ceph_init_acl(struct dentry *dentry, struct inode *inode,
 				struct inode *dir)
diff --git a/fs/ceph/xattr.c b/fs/ceph/xattr.c
index c7581f3733c1..898b6565ad3e 100644
--- a/fs/ceph/xattr.c
+++ b/fs/ceph/xattr.c
@@ -6,6 +6,7 @@
 #include <linux/ceph/decode.h>
 
 #include <linux/xattr.h>
+#include <linux/posix_acl_xattr.h>
 #include <linux/slab.h>
 
 #define XATTR_CEPH_PREFIX "ceph."
@@ -17,8 +18,8 @@
  */
 const struct xattr_handler *ceph_xattr_handlers[] = {
 #ifdef CONFIG_CEPH_FS_POSIX_ACL
-	&ceph_xattr_acl_access_handler,
-	&ceph_xattr_acl_default_handler,
+	&posix_acl_access_xattr_handler,
+	&posix_acl_default_xattr_handler,
 #endif
 	NULL,
 };
-- 
1.7.10.4


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* Re: [PATCH v2] ceph: fix posix ACL hooks
  2014-01-29 16:37           ` [PATCH v2] ceph: fix posix ACL hooks Ilya Dryomov
@ 2014-01-29 19:09             ` Linus Torvalds
  2014-01-30  7:54               ` Christoph Hellwig
  2014-01-29 20:43             ` Ilya Dryomov
  1 sibling, 1 reply; 37+ messages in thread
From: Linus Torvalds @ 2014-01-29 19:09 UTC (permalink / raw)
  To: Ilya Dryomov
  Cc: Sage Weil, Dave Jones, Linux Kernel Mailing List, ceph-devel,
	linux-fsdevel, Christoph Hellwig, Al Viro, Guangliang Zhao,
	Li Wang, zheng.z.yan

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

On Wed, Jan 29, 2014 at 8:37 AM, Ilya Dryomov <ilya.dryomov@inktank.com> wrote:
> From: Sage Weil <sage@inktank.com>
>
> The merge of 7221fe4c2 raced with upstream changes in the generic POSIX
> ACL code (2aeccbe95).  Update Ceph to use the new helpers as well by
> dropping the now-generic functions and setting the set_acl inode op.
>
> Signed-off-by: Sage Weil <sage@inktank.com>

Ok, I already committed my untested (and broken) patch yesterday,
because even a broken tree is better than a non-*compiling* tree for
testing.

I created a diff from my current tree to this, and will happily apply
it, but the sign-off chain is now broken (Ilya didn't sign off on his
changes to Sage's patch. Not that it really matters for what is really
a one-liner change, but I thought I'd mention it, since another issue
came up with this patch..

Ceph now does

        struct dentry *dentry = d_find_alias(inode);

in its ceph_set_acl() function, and that's because the VFS layer
doesn't pass down the dentry to the acl code any more.

Christoph - that sounds like a misfeature in the new interfaces. I
realize that for traditional unix filesystems, the path is irrelevant
for an inode operation, but the thing is, from a Linux VFS standpoint
and a conceptual standpoint, the dentry really is the more important
and unique one, and some filesystems want the dentry because they are
*correctly* designed to be about pathnames.

Network filesystems that are pass file descriptors around (ie NFS etc)
are not the "RightWay(tm)" of doing things, so I think that it is
quite reasonable for ceph to want the *path* to the inode and not just
the inode.

So attached is the incremental diff of the patch by Sage and Ilya, and
I'll apply it (delayed a bit to see if I can get the sign-off from
Ilya), but I also think we should fix the (non-cached) ACL functions
that call down to the filesystem layer to also get the dentry.

We already do that for the xattr functions, just not for
get_acl()/set_acl()/acl_chmod().

Christoph, Al? Yes, most filesystems will ignore the dentry pointer, but still..

               Linus

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

 fs/ceph/acl.c   | 9 ++++-----
 fs/ceph/dir.c   | 1 +
 fs/ceph/inode.c | 2 ++
 fs/ceph/super.h | 3 +++
 4 files changed, 10 insertions(+), 5 deletions(-)

diff --git a/fs/ceph/acl.c b/fs/ceph/acl.c
index f6911284c9bd..66d377a12f7c 100644
--- a/fs/ceph/acl.c
+++ b/fs/ceph/acl.c
@@ -107,14 +107,14 @@ struct posix_acl *ceph_get_acl(struct inode *inode, int type)
 	return acl;
 }
 
-static int ceph_set_acl(struct dentry *dentry, struct inode *inode,
-				struct posix_acl *acl, int type)
+int ceph_set_acl(struct inode *inode, struct posix_acl *acl, int type)
 {
 	int ret = 0, size = 0;
 	const char *name = NULL;
 	char *value = NULL;
 	struct iattr newattrs;
 	umode_t new_mode = inode->i_mode, old_mode = inode->i_mode;
+	struct dentry *dentry = d_find_alias(inode);
 
 	if (acl) {
 		ret = posix_acl_valid(acl);
@@ -208,8 +208,7 @@ int ceph_init_acl(struct dentry *dentry, struct inode *inode, struct inode *dir)
 
 	if (IS_POSIXACL(dir) && acl) {
 		if (S_ISDIR(inode->i_mode)) {
-			ret = ceph_set_acl(dentry, inode, acl,
-						ACL_TYPE_DEFAULT);
+			ret = ceph_set_acl(inode, acl, ACL_TYPE_DEFAULT);
 			if (ret)
 				goto out_release;
 		}
@@ -217,7 +216,7 @@ int ceph_init_acl(struct dentry *dentry, struct inode *inode, struct inode *dir)
 		if (ret < 0)
 			goto out;
 		else if (ret > 0)
-			ret = ceph_set_acl(dentry, inode, acl, ACL_TYPE_ACCESS);
+			ret = ceph_set_acl(inode, acl, ACL_TYPE_ACCESS);
 		else
 			cache_no_acl(inode);
 	} else {
diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c
index 619616d585b0..6da4df84ba30 100644
--- a/fs/ceph/dir.c
+++ b/fs/ceph/dir.c
@@ -1303,6 +1303,7 @@ const struct inode_operations ceph_dir_iops = {
 	.listxattr = ceph_listxattr,
 	.removexattr = ceph_removexattr,
 	.get_acl = ceph_get_acl,
+	.set_acl = ceph_set_acl,
 	.mknod = ceph_mknod,
 	.symlink = ceph_symlink,
 	.mkdir = ceph_mkdir,
diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
index 8b8b506636cc..32d519d8a2e2 100644
--- a/fs/ceph/inode.c
+++ b/fs/ceph/inode.c
@@ -97,6 +97,7 @@ const struct inode_operations ceph_file_iops = {
 	.listxattr = ceph_listxattr,
 	.removexattr = ceph_removexattr,
 	.get_acl = ceph_get_acl,
+	.set_acl = ceph_set_acl,
 };
 
 
@@ -1616,6 +1617,7 @@ static const struct inode_operations ceph_symlink_iops = {
 	.listxattr = ceph_listxattr,
 	.removexattr = ceph_removexattr,
 	.get_acl = ceph_get_acl,
+	.set_acl = ceph_set_acl,
 };
 
 /*
diff --git a/fs/ceph/super.h b/fs/ceph/super.h
index 345933948b6e..aa260590f615 100644
--- a/fs/ceph/super.h
+++ b/fs/ceph/super.h
@@ -718,6 +718,7 @@ 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_setattr(struct dentry *dentry, struct iattr *attr);
+extern int ceph_setattr(struct dentry *dentry, struct iattr *attr);
 extern int ceph_getattr(struct vfsmount *mnt, struct dentry *dentry,
 			struct kstat *stat);
 
@@ -741,12 +742,14 @@ extern const struct xattr_handler *ceph_xattr_handlers[];
 #ifdef CONFIG_CEPH_FS_POSIX_ACL
 
 struct posix_acl *ceph_get_acl(struct inode *, int);
+int ceph_set_acl(struct inode *inode, struct posix_acl *acl, int type);
 int ceph_init_acl(struct dentry *, struct inode *, struct inode *);
 void ceph_forget_all_cached_acls(struct inode *inode);
 
 #else
 
 #define ceph_get_acl NULL
+#define ceph_set_acl NULL
 
 static inline int ceph_init_acl(struct dentry *dentry, struct inode *inode,
 				struct inode *dir)

^ permalink raw reply related	[flat|nested] 37+ messages in thread

* Re: [PATCH v2] ceph: fix posix ACL hooks
  2014-01-29 16:37           ` [PATCH v2] ceph: fix posix ACL hooks Ilya Dryomov
  2014-01-29 19:09             ` Linus Torvalds
@ 2014-01-29 20:43             ` Ilya Dryomov
  1 sibling, 0 replies; 37+ messages in thread
From: Ilya Dryomov @ 2014-01-29 20:43 UTC (permalink / raw)
  To: Sage Weil
  Cc: Linus Torvalds, Dave Jones, Linux Kernel Mailing List,
	Ceph Development, linux-fsdevel, Christoph Hellwig, Al Viro,
	Guangliang Zhao, Li Wang, Yan, Zheng

On Wed, Jan 29, 2014 at 6:37 PM, Ilya Dryomov <ilya.dryomov@inktank.com> wrote:
> From: Sage Weil <sage@inktank.com>
>
> The merge of 7221fe4c2 raced with upstream changes in the generic POSIX
> ACL code (2aeccbe95).  Update Ceph to use the new helpers as well by
> dropping the now-generic functions and setting the set_acl inode op.
>
> Signed-off-by: Sage Weil <sage@inktank.com>

Signed-off-by: Ilya Dryomov <ilya.dryomov@inktank.com>

Thanks,

                Ilya

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v2] ceph: fix posix ACL hooks
  2014-01-29 19:09             ` Linus Torvalds
@ 2014-01-30  7:54               ` Christoph Hellwig
  2014-01-30 22:01                 ` Linus Torvalds
  0 siblings, 1 reply; 37+ messages in thread
From: Christoph Hellwig @ 2014-01-30  7:54 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Ilya Dryomov, Sage Weil, Dave Jones, Linux Kernel Mailing List,
	ceph-devel, linux-fsdevel, Christoph Hellwig, Al Viro,
	Guangliang Zhao, Li Wang, zheng.z.yan

On Wed, Jan 29, 2014 at 11:09:18AM -0800, Linus Torvalds wrote:
> So attached is the incremental diff of the patch by Sage and Ilya, and
> I'll apply it (delayed a bit to see if I can get the sign-off from
> Ilya), but I also think we should fix the (non-cached) ACL functions
> that call down to the filesystem layer to also get the dentry.

For ->set_acl that's fairly easily doable and I actually had a version
doing that to be able to convert 9p.  But for ->get_acl the path walking
caller didn't seem easily feasible.  ->get_acl actually is an invention
of yours, so if you got a good idea to get the dentry to it I'd love
to be able to pass it.


^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [GIT PULL] Ceph updates for -rc1
  2014-01-29 14:30       ` Sage Weil
  2014-01-29 16:36         ` Ilya Dryomov
@ 2014-01-30 10:46         ` Christoph Hellwig
  1 sibling, 0 replies; 37+ messages in thread
From: Christoph Hellwig @ 2014-01-30 10:46 UTC (permalink / raw)
  To: Sage Weil
  Cc: Linus Torvalds, Dave Jones, Linux Kernel Mailing List,
	ceph-devel, linux-fsdevel, Christoph Hellwig, Al Viro,
	Guangliang Zhao, Li Wang, zheng.z.yan

On Wed, Jan 29, 2014 at 06:30:00AM -0800, Sage Weil wrote:
> The set_acl inode_operation wasn't getting set, and the prototype needed 
> to be adjusted a bit (it doesn't take a dentry anymore).  All seems to be 
> well with the below patch.

Btw, there's a few minor bits that should go on top of yours:

 - ->get_acl only gets called after we checked for a cached ACL, so no
   need to call get_cached_acl again.
 - no need to check IS_POSIXACL in ->get_acl, without that it should
   never get set as all the callers that set it already have the check.
 - you should be able to use the full posix_acl_create in CEPH

Untested patch below:

diff --git a/fs/ceph/acl.c b/fs/ceph/acl.c
index 66d377a..9ab312e 100644
--- a/fs/ceph/acl.c
+++ b/fs/ceph/acl.c
@@ -66,13 +66,6 @@ struct posix_acl *ceph_get_acl(struct inode *inode, int type)
 	char *value = NULL;
 	struct posix_acl *acl;
 
-	if (!IS_POSIXACL(inode))
-		return NULL;
-
-	acl = ceph_get_cached_acl(inode, type);
-	if (acl != ACL_NOT_CACHED)
-		return acl;
-
 	switch (type) {
 	case ACL_TYPE_ACCESS:
 		name = POSIX_ACL_XATTR_ACCESS;
@@ -190,41 +183,24 @@ out:
 
 int ceph_init_acl(struct dentry *dentry, struct inode *inode, struct inode *dir)
 {
-	struct posix_acl *acl = NULL;
-	int ret = 0;
-
-	if (!S_ISLNK(inode->i_mode)) {
-		if (IS_POSIXACL(dir)) {
-			acl = ceph_get_acl(dir, ACL_TYPE_DEFAULT);
-			if (IS_ERR(acl)) {
-				ret = PTR_ERR(acl);
-				goto out;
-			}
-		}
+	struct posix_acl *default_acl, *acl;
+	int error;
 
-		if (!acl)
-			inode->i_mode &= ~current_umask();
-	}
+	error = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl);
+	if (error)
+		return error;
 
-	if (IS_POSIXACL(dir) && acl) {
-		if (S_ISDIR(inode->i_mode)) {
-			ret = ceph_set_acl(inode, acl, ACL_TYPE_DEFAULT);
-			if (ret)
-				goto out_release;
-		}
-		ret = __posix_acl_create(&acl, GFP_NOFS, &inode->i_mode);
-		if (ret < 0)
-			goto out;
-		else if (ret > 0)
-			ret = ceph_set_acl(inode, acl, ACL_TYPE_ACCESS);
-		else
-			cache_no_acl(inode);
-	} else {
+	if (!default_acl && !acl)
 		cache_no_acl(inode);
-	}
 
-out_release:
-	posix_acl_release(acl);
-out:
-	return ret;
+	if (default_acl) {
+		error = ceph_set_acl(inode, default_acl, ACL_TYPE_DEFAULT);
+		posix_acl_release(default_acl);
+	}
+	if (acl) {
+		if (!error)
+			error = ceph_set_acl(inode, acl, ACL_TYPE_ACCESS);
+		posix_acl_release(acl);
+	}
+	return error;
 }

^ permalink raw reply related	[flat|nested] 37+ messages in thread

* Re: [PATCH v2] ceph: fix posix ACL hooks
  2014-01-30  7:54               ` Christoph Hellwig
@ 2014-01-30 22:01                 ` Linus Torvalds
  2014-01-31  0:14                   ` Sage Weil
  2014-02-03 10:29                   ` Christoph Hellwig
  0 siblings, 2 replies; 37+ messages in thread
From: Linus Torvalds @ 2014-01-30 22:01 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Ilya Dryomov, Sage Weil, Dave Jones, Linux Kernel Mailing List,
	ceph-devel, linux-fsdevel, Al Viro, Guangliang Zhao, Li Wang,
	zheng.z.yan

On Wed, Jan 29, 2014 at 11:54 PM, Christoph Hellwig <hch@infradead.org> wrote:
>
> For ->set_acl that's fairly easily doable and I actually had a version
> doing that to be able to convert 9p.  But for ->get_acl the path walking
> caller didn't seem easily feasible.  ->get_acl actually is an invention
> of yours, so if you got a good idea to get the dentry to it I'd love
> to be able to pass it.

Yeah, that's pretty annoying, largely because that path is also
RCU-walk aware, which does *not* need this all (because it will never
call down into the filesystem - if the acl isn't found in the cached
acl's, we just abort).

And we're going through that very common "generic_permission()" thing
that in turn is also often called from the low-level filesystens, and
it's all fairly tightly integrated with __inode_permission() etc.

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.

So I'm not thrilled about it, but maybe that "d_find_alias(inode)" to
find the dentry is good enough in practice. It feels very much
incorrect (it could find a dentry with a path that you cannot actually
access on the server, and result in user-visible errors), but I
definitely see your argument. It may just not be worth the pain for
this odd ceph case.

That said, if the ceph people decide to try to bite the bullet and do
the required conversions to pass the dentry to the permissions
functions, I think I'd take it unless it ends up being *too* horribly
messy.

               Linus

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v2] ceph: fix posix ACL hooks
  2014-01-30 22:01                 ` Linus Torvalds
@ 2014-01-31  0:14                   ` Sage Weil
  2014-02-03 10:29                   ` Christoph Hellwig
  1 sibling, 0 replies; 37+ messages in thread
From: Sage Weil @ 2014-01-31  0:14 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Christoph Hellwig, Ilya Dryomov, Dave Jones,
	Linux Kernel Mailing List, ceph-devel, linux-fsdevel, Al Viro,
	Guangliang Zhao, Li Wang, zheng.z.yan

On Thu, 30 Jan 2014, Linus Torvalds wrote:
> On Wed, Jan 29, 2014 at 11:54 PM, Christoph Hellwig <hch@infradead.org> wrote:
> >
> > For ->set_acl that's fairly easily doable and I actually had a version
> > doing that to be able to convert 9p.  But for ->get_acl the path walking
> > caller didn't seem easily feasible.  ->get_acl actually is an invention
> > of yours, so if you got a good idea to get the dentry to it I'd love
> > to be able to pass it.
> 
> Yeah, that's pretty annoying, largely because that path is also
> RCU-walk aware, which does *not* need this all (because it will never
> call down into the filesystem - if the acl isn't found in the cached
> acl's, we just abort).
> 
> And we're going through that very common "generic_permission()" thing
> that in turn is also often called from the low-level filesystens, and
> it's all fairly tightly integrated with __inode_permission() etc.
> 
> 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.
> 
> So I'm not thrilled about it, but maybe that "d_find_alias(inode)" to
> find the dentry is good enough in practice. It feels very much
> incorrect (it could find a dentry with a path that you cannot actually
> access on the server, and result in user-visible errors), but I
> definitely see your argument. It may just not be worth the pain for
> this odd ceph case.
> 
> That said, if the ceph people decide to try to bite the bullet and do
> the required conversions to pass the dentry to the permissions
> functions, I think I'd take it unless it ends up being *too* horribly
> messy.

FWIW the dentry isn't useful in the get case; it's only on put that it is 
currently used.  And now that I look closely, it is only being used by 
ceph_setattr to associate the update with the parent directory for the 
purposes of fsync(dirfd)... which is, I think, incorrect anyway (that 
should only flush out/wait for namespace modifications, not inode attr 
updates).

So I think it's fine as is, and we'll clean this up later.

I do have a couple patches on top of what's in your tree, though, that 
clean up a couple duplicated lines in your fix and apply Christoph's 
cleanup:

 git://git.kernel.org/pub/scm/linux/kernel/git/sage/ceph-client.git for-linus

Thanks!
sage

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v2] ceph: fix posix ACL hooks
  2014-01-30 22:01                 ` Linus Torvalds
  2014-01-31  0:14                   ` Sage Weil
@ 2014-02-03 10:29                   ` Christoph Hellwig
  2014-02-03 11:13                     ` Al Viro
  2014-02-03 21:03                     ` Linus Torvalds
  1 sibling, 2 replies; 37+ messages in thread
From: Christoph Hellwig @ 2014-02-03 10:29 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Christoph Hellwig, Ilya Dryomov, Sage Weil, Dave Jones,
	Linux Kernel Mailing List, ceph-devel, linux-fsdevel, Al Viro,
	Guangliang Zhao, Li Wang, zheng.z.yan

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.
> 
> So I'm not thrilled about it, but maybe that "d_find_alias(inode)" to
> find the dentry is good enough in practice. It feels very much
> incorrect (it could find a dentry with a path that you cannot actually
> access on the server, and result in user-visible errors), but I
> definitely see your argument. It may just not be worth the pain for
> this odd ceph case.

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.  The same d_find_alias hack should work for 9p as well,
although spreading this even more gets uglier and uglier.  Similarly
for CIFS which pretends to understand the Posix ACL xattrs, but doesn't
use any of the infrastructure as it seems to rely on server side
enforcement.


^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v2] ceph: fix posix ACL hooks
  2014-02-03 10:29                   ` Christoph Hellwig
@ 2014-02-03 11:13                     ` Al Viro
  2014-02-03 21:03                     ` Linus Torvalds
  1 sibling, 0 replies; 37+ messages in thread
From: Al Viro @ 2014-02-03 11:13 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Linus Torvalds, Ilya Dryomov, Sage Weil, Dave Jones,
	Linux Kernel Mailing List, ceph-devel, linux-fsdevel,
	Guangliang Zhao, Li Wang, zheng.z.yan

On Mon, Feb 03, 2014 at 02:29:43AM -0800, Christoph Hellwig 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.
> > 
> > So I'm not thrilled about it, but maybe that "d_find_alias(inode)" to
> > find the dentry is good enough in practice. It feels very much
> > incorrect (it could find a dentry with a path that you cannot actually
> > access on the server, and result in user-visible errors), but I
> > definitely see your argument. It may just not be worth the pain for
> > this odd ceph case.
> 
> 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.  The same d_find_alias hack should work for 9p as well,
> although spreading this even more gets uglier and uglier.  Similarly
> for CIFS which pretends to understand the Posix ACL xattrs, but doesn't
> use any of the infrastructure as it seems to rely on server side
> enforcement.

9P is going to be fun to deal with; that's why I've ended up abandoning
vfs.git#experimental-xattr last year.  We probably want to move FIDs
from dentries to inodes there, and rely in ->getxattr() et.al. upon
having already done ->d_revalidate() on some dentry for that inode.

Another pile of fun is fsnotify_xattr() call in __vfs_setxattr_noperm()
and the whole misbegotten IMA/EVM mess ;-/  

See #experimental-xattr - a lot of stuff in that direction is sitting there;
might turn out to be useful.

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v2] ceph: fix posix ACL hooks
  2014-02-03 10:29                   ` Christoph Hellwig
  2014-02-03 11:13                     ` Al Viro
@ 2014-02-03 21:03                     ` Linus Torvalds
  2014-02-03 21:19                       ` Al Viro
                                         ` (3 more replies)
  1 sibling, 4 replies; 37+ messages in thread
From: Linus Torvalds @ 2014-02-03 21:03 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Ilya Dryomov, Sage Weil, Dave Jones, Linux Kernel Mailing List,
	ceph-devel, linux-fsdevel, Al Viro, Guangliang Zhao, Li Wang,
	zheng.z.yan

[-- 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;

^ permalink raw reply related	[flat|nested] 37+ messages in thread

* Re: [PATCH v2] ceph: fix posix ACL hooks
  2014-02-03 21:03                     ` Linus Torvalds
@ 2014-02-03 21:19                       ` Al Viro
  2014-02-03 21:24                         ` Christoph Hellwig
  2014-02-03 21:31                         ` Linus Torvalds
  2014-02-03 21:23                       ` Christoph Hellwig
                                         ` (2 subsequent siblings)
  3 siblings, 2 replies; 37+ messages in thread
From: Al Viro @ 2014-02-03 21:19 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Christoph Hellwig, Ilya Dryomov, Sage Weil, Dave Jones,
	Linux Kernel Mailing List, ceph-devel, linux-fsdevel,
	Guangliang Zhao, Li Wang, zheng.z.yan

On Mon, Feb 03, 2014 at 01:03:32PM -0800, Linus Torvalds wrote:

> 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.

Please, don't do that.  Half of that is pointless (e.g. what you are
doing with vfs_rmdir() - if anything, we could get rid of the first
argument completely, it's always victim->d_parent->d_inode and we are
holding enough locks for that to be stable) and ->permission() is
just plain wrong.

Result *is* a function of inode alone; the problem with 9P is that we
are caching FIDs in the wrong place.

What really happens is that protocol refers to objects by 32bit tokens,
given by server out to client.  Many of those can correspond to the
same file; think of those as file descriptors.  We are associating them
with dentries, but if you have several links to the same file a FID
acquired for either of them will do.

What we ought to do, AFAICS, is moving these guys from dentry to inode;
sure, to *get* one in the first place we need some dentry.  But by the
time ->setxattr() and friends get to see the inode, the caller has already
done things that make sure that some FID of his exists.

IOW, NAK.

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v2] ceph: fix posix ACL hooks
  2014-02-03 21:03                     ` Linus Torvalds
  2014-02-03 21:19                       ` Al Viro
@ 2014-02-03 21:23                       ` Christoph Hellwig
  2014-02-03 21:59                       ` Al Viro
  2014-02-03 21:59                       ` Linus Torvalds
  3 siblings, 0 replies; 37+ messages in thread
From: Christoph Hellwig @ 2014-02-03 21:23 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Christoph Hellwig, Ilya Dryomov, Sage Weil, Dave Jones,
	Linux Kernel Mailing List, ceph-devel, linux-fsdevel, Al Viro,
	Guangliang Zhao, Li Wang, zheng.z.yan, Steven Whitehouse,
	cluster-devel

On Mon, Feb 03, 2014 at 01:03:32PM -0800, Linus Torvalds wrote:
> 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.

Looking over the gfs2 code the problem seems to be that it duplicates
permissions checks from the may_{lookup,create,linkat,delete}, most
likely because it needs cluster locking in place for them.  The right
fix seems to be to optionally call the filesystem from those.  That
being said I wonder how ocfs2 or network filesystems get away without
that.

> 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.

The changes look good to me, and yes I think they should be split.
I'll see if I can take this further, but doing something non-hacky
in GFS2 would be the first step here.


^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v2] ceph: fix posix ACL hooks
  2014-02-03 21:19                       ` Al Viro
@ 2014-02-03 21:24                         ` Christoph Hellwig
  2014-02-03 21:31                           ` Al Viro
  2014-02-03 21:31                         ` Linus Torvalds
  1 sibling, 1 reply; 37+ messages in thread
From: Christoph Hellwig @ 2014-02-03 21:24 UTC (permalink / raw)
  To: Al Viro
  Cc: Linus Torvalds, Christoph Hellwig, Ilya Dryomov, Sage Weil,
	Dave Jones, Linux Kernel Mailing List, ceph-devel, linux-fsdevel,
	Guangliang Zhao, Li Wang, zheng.z.yan

On Mon, Feb 03, 2014 at 09:19:55PM +0000, Al Viro wrote:
> Result *is* a function of inode alone; the problem with 9P is that we
> are caching FIDs in the wrong place.

I don't think that's true for CIFS unfortunately, which is path based.


^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v2] ceph: fix posix ACL hooks
  2014-02-03 21:19                       ` Al Viro
  2014-02-03 21:24                         ` Christoph Hellwig
@ 2014-02-03 21:31                         ` Linus Torvalds
  2014-02-03 21:39                           ` Al Viro
  1 sibling, 1 reply; 37+ messages in thread
From: Linus Torvalds @ 2014-02-03 21:31 UTC (permalink / raw)
  To: Al Viro
  Cc: Christoph Hellwig, Ilya Dryomov, Sage Weil, Dave Jones,
	Linux Kernel Mailing List, ceph-devel, linux-fsdevel,
	Guangliang Zhao, Li Wang, zheng.z.yan

On Mon, Feb 3, 2014 at 1:19 PM, Al Viro <viro@zeniv.linux.org.uk> wrote:
>
> Please, don't do that.  Half of that is pointless (e.g. what you are
> doing with vfs_rmdir() - if anything, we could get rid of the first
> argument completely, it's always victim->d_parent->d_inode and we are
> holding enough locks for that to be stable) and ->permission() is
> just plain wrong.
>
> Result *is* a function of inode alone; the problem with 9P is that we
> are caching FIDs in the wrong place.

No, Al. It's *not* just a function of the inode alone. That's the point.

Some network filesystems pass the *path* to the server. Any operation
that needs to check something on the server needs the *dentry*, not
the inode.

This whole "the inode describes the file" mentality comes from old
broken UNIX semantics. It's fundamentally true for local Unix
filesystems, but that's it. It's not true in general.

Sure, many network filesystems then emulate the local Unix filesystem
behavior, so in practice, you get the unix semantics quite often. But
it really is wrong.

If the protocol is path-based (and it happens, and it's actually the
*correct* thing to do for a network filesystem, rather than the
idiotic "file handle" crap that tries to emulate the unix inode
semantics in the protocol), then the inode is simply not sufficient.

And no, d_find_alias() is not correct or sufficient either. It can
work in practice (and probably does perfectly fine 99.9% of the time),
but it can equally well give the *wrong* dentry: yes, the dentry it
returns would have been a valid dentry for somebody at some time, but
it might be a stale dentry *now*, and it might be the wrong dentry for
the current user (because the current user may not have permissions to
that particular path, even if the user has permissions through his
*own* path).

So I really think you're *fundamentally* incorrect when you say
"result *is* a function of inode alone".

                  Linus

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v2] ceph: fix posix ACL hooks
  2014-02-03 21:24                         ` Christoph Hellwig
@ 2014-02-03 21:31                           ` Al Viro
  2014-02-03 21:36                             ` Christoph Hellwig
  2014-02-03 21:37                             ` Linus Torvalds
  0 siblings, 2 replies; 37+ messages in thread
From: Al Viro @ 2014-02-03 21:31 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Linus Torvalds, Ilya Dryomov, Sage Weil, Dave Jones,
	Linux Kernel Mailing List, ceph-devel, linux-fsdevel,
	Guangliang Zhao, Li Wang, zheng.z.yan

On Mon, Feb 03, 2014 at 01:24:47PM -0800, Christoph Hellwig wrote:
> On Mon, Feb 03, 2014 at 09:19:55PM +0000, Al Viro wrote:
> > Result *is* a function of inode alone; the problem with 9P is that we
> > are caching FIDs in the wrong place.
> 
> I don't think that's true for CIFS unfortunately, which is path based.

Yes, and...?  CIFS also doesn't have hardlinks, so _there_ d_find_alias()
is just fine.

9P is actually trickier; there we need some massage, but for CIFS it's
literally a matter of one function call.

The problem with 9P is that the way we do it right now, you might pick
the wrong dentry.  _Some_ alias already bears the FID you are after,
but if you end up picking the one that doesn't, you might very well
end up being unable to obtain one either.

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v2] ceph: fix posix ACL hooks
  2014-02-03 21:31                           ` Al Viro
@ 2014-02-03 21:36                             ` Christoph Hellwig
  2014-02-03 21:37                             ` Linus Torvalds
  1 sibling, 0 replies; 37+ messages in thread
From: Christoph Hellwig @ 2014-02-03 21:36 UTC (permalink / raw)
  To: Al Viro
  Cc: Christoph Hellwig, Linus Torvalds, Ilya Dryomov, Sage Weil,
	Dave Jones, Linux Kernel Mailing List, ceph-devel, linux-fsdevel,
	Guangliang Zhao, Li Wang, zheng.z.yan

On Mon, Feb 03, 2014 at 09:31:53PM +0000, Al Viro wrote:
> Yes, and...?  CIFS also doesn't have hardlinks, so _there_ d_find_alias()
> is just fine.

It does have hardlinks, look at cifs_hardlink and functions called from
it.


^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v2] ceph: fix posix ACL hooks
  2014-02-03 21:31                           ` Al Viro
  2014-02-03 21:36                             ` Christoph Hellwig
@ 2014-02-03 21:37                             ` Linus Torvalds
  2014-02-03 21:42                               ` Al Viro
  1 sibling, 1 reply; 37+ messages in thread
From: Linus Torvalds @ 2014-02-03 21:37 UTC (permalink / raw)
  To: Al Viro
  Cc: Christoph Hellwig, Ilya Dryomov, Sage Weil, Dave Jones,
	Linux Kernel Mailing List, ceph-devel, linux-fsdevel,
	Guangliang Zhao, zheng.z.yan

On Mon, Feb 3, 2014 at 1:31 PM, Al Viro <viro@zeniv.linux.org.uk> wrote:
>
> Yes, and...?  CIFS also doesn't have hardlinks, so _there_ d_find_alias()
> is just fine.

Hmm? I'm pretty sure cifs can actually have hardlinks.

             Linus

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v2] ceph: fix posix ACL hooks
  2014-02-03 21:31                         ` Linus Torvalds
@ 2014-02-03 21:39                           ` Al Viro
  2014-02-03 21:43                             ` Al Viro
  2014-02-03 21:44                             ` Linus Torvalds
  0 siblings, 2 replies; 37+ messages in thread
From: Al Viro @ 2014-02-03 21:39 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Christoph Hellwig, Ilya Dryomov, Sage Weil, Dave Jones,
	Linux Kernel Mailing List, ceph-devel, linux-fsdevel,
	Guangliang Zhao, Li Wang, zheng.z.yan

On Mon, Feb 03, 2014 at 01:31:19PM -0800, Linus Torvalds wrote:

> If the protocol is path-based (and it happens, and it's actually the
> *correct* thing to do for a network filesystem, rather than the
> idiotic "file handle" crap that tries to emulate the unix inode
> semantics in the protocol), then the inode is simply not sufficient.
> 
> And no, d_find_alias() is not correct or sufficient either. It can
> work in practice (and probably does perfectly fine 99.9% of the time),
> but it can equally well give the *wrong* dentry: yes, the dentry it
> returns would have been a valid dentry for somebody at some time, but
> it might be a stale dentry *now*, and it might be the wrong dentry for
> the current user (because the current user may not have permissions to
> that particular path, even if the user has permissions through his
> *own* path).
> 
> So I really think you're *fundamentally* incorrect when you say
> "result *is* a function of inode alone".

Which fs are you talking about?  For 9P it *is* a function of inode alone.
For CIFS there's no wrong dentry to pick - it doesn't have links to start
with.

If we really have hardlinks, the result of permission check would better
be a function of inode itself - as in, "if it gives different results
for two pathnames reachable for the same user, we have a bug".

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v2] ceph: fix posix ACL hooks
  2014-02-03 21:37                             ` Linus Torvalds
@ 2014-02-03 21:42                               ` Al Viro
  0 siblings, 0 replies; 37+ messages in thread
From: Al Viro @ 2014-02-03 21:42 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Christoph Hellwig, Ilya Dryomov, Sage Weil, Dave Jones,
	Linux Kernel Mailing List, ceph-devel, linux-fsdevel,
	Guangliang Zhao, zheng.z.yan

On Mon, Feb 03, 2014 at 01:37:03PM -0800, Linus Torvalds wrote:
> On Mon, Feb 3, 2014 at 1:31 PM, Al Viro <viro@zeniv.linux.org.uk> wrote:
> >
> > Yes, and...?  CIFS also doesn't have hardlinks, so _there_ d_find_alias()
> > is just fine.
> 
> Hmm? I'm pretty sure cifs can actually have hardlinks.

*UGH*

How the hell does it...  Oh, right - samba on Unix server.  I really wonder
how well do Windows clients deal with those...

Crap...  I reall hate that prototype change ;-/

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v2] ceph: fix posix ACL hooks
  2014-02-03 21:39                           ` Al Viro
@ 2014-02-03 21:43                             ` Al Viro
  2014-02-03 21:44                             ` Linus Torvalds
  1 sibling, 0 replies; 37+ messages in thread
From: Al Viro @ 2014-02-03 21:43 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Christoph Hellwig, Ilya Dryomov, Sage Weil, Dave Jones,
	Linux Kernel Mailing List, ceph-devel, linux-fsdevel,
	Guangliang Zhao, Li Wang, zheng.z.yan

On Mon, Feb 03, 2014 at 09:39:26PM +0000, Al Viro wrote:

> Which fs are you talking about?  For 9P it *is* a function of inode alone.
> For CIFS there's no wrong dentry to pick - it doesn't have links to start
> with.

Except that apparently it does ;-/  Bugger...

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v2] ceph: fix posix ACL hooks
  2014-02-03 21:39                           ` Al Viro
  2014-02-03 21:43                             ` Al Viro
@ 2014-02-03 21:44                             ` Linus Torvalds
  2014-02-03 22:31                               ` Al Viro
  1 sibling, 1 reply; 37+ messages in thread
From: Linus Torvalds @ 2014-02-03 21:44 UTC (permalink / raw)
  To: Al Viro
  Cc: Christoph Hellwig, Ilya Dryomov, Sage Weil, Dave Jones,
	Linux Kernel Mailing List, ceph-devel, linux-fsdevel,
	Guangliang Zhao, Li Wang, zheng.z.yan

On Mon, Feb 3, 2014 at 1:39 PM, Al Viro <viro@zeniv.linux.org.uk> wrote:
>
> If we really have hardlinks, the result of permission check would better
> be a function of inode itself - as in, "if it gives different results
> for two pathnames reachable for the same user, we have a bug".

No. You're wrong.

EVEN ON A UNIX FILESYSTEM THE PATH IS MEANINGFUL.

Do this: create a hardlink in two different directories. Make the
*directory* permissions for one of the directories be something you
cannot traverse. Now try to check the permissions of the *same* inode
through those two paths. Notice how you get *different* results.

Really.

Now, imagine that you are doing the same thing over a network. On the
server, there may be a single inode for the file, but when the client
gives the server a pathname, the two pathnames to that single inode
ARE NOT EQUIVALENT.

And the fact is, filesystems with hardlinks and path-name-based
operations do exist. cifs with the unix extensions is one of them.

Al, face it, you're wrong this time.

                      Linus

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v2] ceph: fix posix ACL hooks
  2014-02-03 21:03                     ` Linus Torvalds
  2014-02-03 21:19                       ` Al Viro
  2014-02-03 21:23                       ` Christoph Hellwig
@ 2014-02-03 21:59                       ` Al Viro
  2014-02-03 22:12                         ` Linus Torvalds
  2014-02-03 21:59                       ` Linus Torvalds
  3 siblings, 1 reply; 37+ messages in thread
From: Al Viro @ 2014-02-03 21:59 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Christoph Hellwig, Ilya Dryomov, Sage Weil, Dave Jones,
	Linux Kernel Mailing List, ceph-devel, linux-fsdevel,
	Guangliang Zhao, Li Wang, zheng.z.yan

On Mon, Feb 03, 2014 at 01:03:32PM -0800, Linus Torvalds wrote:

> -	err = vfs_mkdir(path.dentry->d_inode, dentry, mode);
> +	err = vfs_mkdir(path.dentry, dentry, mode);

Pointless - path.dentry == dentry->d_parent anyway.
 
> -	err = vfs_mknod(path.dentry->d_inode, dentry, mode, dev->devt);
> +	err = vfs_mknod(path.dentry, dentry, mode, dev->devt);

Ditto.

> @@ -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);
 
 Ditto, with s/path/parent/
> @@ -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;
>  		}
 
And here as well.
 
> +++ 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);


... and again, that's completely pointless.

> -int afs_permission(struct inode *inode, int mask)
> +int afs_permission(struct dentry *dentry, struct inode *inode, int mask)

Oh, _lovely_.  So not only do we pass dentry, the arguments are redundant
as well.

> -static inline int btrfs_may_create(struct inode *dir, struct dentry *child)
> +static inline int btrfs_may_create(struct dentry *parent, struct dentry *child)

I'm fairly sure that it's also pointless, because parent is going to be, well,
the parent.  Of child.

> +static int gfs2_vfs_permission(struct dentry *dentry, struct inode *inode, int mask)
> +{
> +	return gfs2_permission(inode, mask);
> +}

Er...  You do realize that callers of gfs2_permission() tend to have
the dentry in question, either directly or as ->d_parent of something
they have?


I really hate the whole thing... ;-/

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v2] ceph: fix posix ACL hooks
  2014-02-03 21:03                     ` Linus Torvalds
                                         ` (2 preceding siblings ...)
  2014-02-03 21:59                       ` Al Viro
@ 2014-02-03 21:59                       ` Linus Torvalds
  3 siblings, 0 replies; 37+ messages in thread
From: Linus Torvalds @ 2014-02-03 21:59 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Ilya Dryomov, Sage Weil, Dave Jones, Linux Kernel Mailing List,
	ceph-devel, linux-fsdevel, Al Viro, Guangliang Zhao, Li Wang,
	zheng.z.yan

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

On Mon, Feb 3, 2014 at 1:03 PM, Linus Torvalds
<torvalds@linux-foundation.org> wrote:
>
> 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.

Ok, this is the split-up. I haven't completed the "make allmodconfig"
for the first patch yet, so I might have split this wrong, but it was
fairly well separated and I'm pretty sure that this is fine.

I do actually agree that the second patch isn't exactly pretty.
Passing in both dentry and inode is redundant, and calling the
function "inode_permission()" is now a misnomer. But it makes it a lot
easier to see the differences this way in the diff (particularly
word-diff), so I think a renaming and/or dropping the inode parameter
would better be done as a separate patch anyway.

And no, as far as the first patch is concerned, I certainly wouldn't
hate dropping the first 'parent' argument from the vfs_xyzzy()
functions either, since that *should* be redundant, but quite frankly,
I didn't want to think too much about that, and this was the simple
conversions (and all callers were quite happy with dropping the inode
and replacing it with the dentry).

            Linus

[-- Attachment #2: vfs-function-cleanup --]
[-- Type: application/octet-stream, Size: 26901 bytes --]

commit ff4be0942bc54f636fce24b682caea2a9df00d08
Author: Linus Torvalds <torvalds@linux-foundation.org>
Date:   Mon Feb 3 13:17:29 2014 -0800

    Push dentry pointer down to vfs_xyzzy() helper functions
    
    This actually ends up being closer to what the callers want anyway, so
    it simplifies most call sites.  And we'll eventually want to push down
    the dentry pointer to the permission checking routines, which in turn
    requires this.
    
    Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
---
 drivers/base/devtmpfs.c                         |  8 +--
 drivers/staging/lustre/lustre/lvfs/lvfs_linux.c |  4 +-
 fs/cachefiles/namei.c                           | 11 ++--
 fs/ecryptfs/inode.c                             | 22 +++----
 fs/namei.c                                      | 86 +++++++++++++++----------
 fs/nfsd/nfs4recover.c                           |  6 +-
 fs/nfsd/vfs.c                                   | 28 +++-----
 include/linux/fs.h                              | 16 ++---
 ipc/mqueue.c                                    |  8 +--
 net/unix/af_unix.c                              |  2 +-
 10 files changed, 98 insertions(+), 93 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/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/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/ecryptfs/inode.c b/fs/ecryptfs/inode.c
index b167ca48b8ee..a7a823f980c5 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;
diff --git a/fs/namei.c b/fs/namei.c
index d580df2e6804..142741e1ac9e 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -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;
 
@@ -2408,8 +2409,9 @@ 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;
@@ -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;
 
@@ -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;
 	}
@@ -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) {
@@ -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/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/vfs.c b/fs/nfsd/vfs.c
index 017d3cb5e99b..6faef0e5e43b 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);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 09f553c59813..c9fd8fb28c92 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.
diff --git a/ipc/mqueue.c b/ipc/mqueue.c
index ccf1f9fd263a..9c254d1e077f 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);
@@ -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/net/unix/af_unix.c b/net/unix/af_unix.c
index 29fc8bee9702..93371f7b9f86 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -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);

[-- Attachment #3: inode_permission-patch --]
[-- Type: application/octet-stream, Size: 42543 bytes --]

commit 101b08479cf1b5baa0e7687281548ea351fa3a8b
Author: Linus Torvalds <torvalds@linux-foundation.org>
Date:   Mon Feb 3 13:47:08 2014 -0800

    Make 'inode_permission()' take the dentry pointer too
---
 drivers/staging/lustre/lustre/llite/file.c         |  6 ++--
 .../staging/lustre/lustre/llite/llite_internal.h   |  2 +-
 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/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                                |  4 +--
 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                                         | 40 +++++++++++-----------
 fs/ncpfs/ioctl.c                                   |  6 ++--
 fs/nfs/dir.c                                       |  2 +-
 fs/nfsd/nfsfh.c                                    |  2 +-
 fs/nfsd/vfs.c                                      |  4 +--
 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                                 |  4 +--
 include/linux/nfs_fs.h                             |  2 +-
 ipc/mqueue.c                                       |  2 +-
 kernel/sys.c                                       |  2 +-
 mm/memcontrol.c                                    |  2 +-
 net/unix/af_unix.c                                 |  2 +-
 virt/kvm/assigned-dev.c                            |  2 +-
 50 files changed, 112 insertions(+), 99 deletions(-)

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/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/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 a7a823f980c5..f2dc516c626a 100644
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -882,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 142741e1ac9e..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);
 
@@ -2378,7 +2378,7 @@ static int may_delete(struct dentry *parent, 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))
@@ -2417,7 +2417,7 @@ static inline int may_create(struct dentry *parent, struct dentry *child)
 		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);
 }
 
 /*
@@ -2514,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;
 
@@ -2570,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;
 
@@ -3079,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;
@@ -4000,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;
 	}
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/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 6faef0e5e43b..f045c435d49a 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -2028,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 c9fd8fb28c92..2350d8b10c18 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -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 9c254d1e077f..a1c751b5858e 100644
--- a/ipc/mqueue.c
+++ b/ipc/mqueue.c
@@ -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());
 }
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 93371f7b9f86..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;
 
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;

^ permalink raw reply related	[flat|nested] 37+ messages in thread

* Re: [PATCH v2] ceph: fix posix ACL hooks
  2014-02-03 21:59                       ` Al Viro
@ 2014-02-03 22:12                         ` Linus Torvalds
  2014-02-03 22:40                           ` Al Viro
  0 siblings, 1 reply; 37+ messages in thread
From: Linus Torvalds @ 2014-02-03 22:12 UTC (permalink / raw)
  To: Al Viro
  Cc: Christoph Hellwig, Ilya Dryomov, Sage Weil, Dave Jones,
	Linux Kernel Mailing List, ceph-devel, linux-fsdevel,
	Guangliang Zhao, Li Wang, zheng.z.yan

On Mon, Feb 3, 2014 at 1:59 PM, Al Viro <viro@zeniv.linux.org.uk> wrote:
> On Mon, Feb 03, 2014 at 01:03:32PM -0800, Linus Torvalds wrote:
>
>> -     err = vfs_mkdir(path.dentry->d_inode, dentry, mode);
>> +     err = vfs_mkdir(path.dentry, dentry, mode);
>
> Pointless - path.dentry == dentry->d_parent anyway.

Heh. It's no less redundant than it used to be.

But if you want to clean up the vfs_xyzzy() ones further, I'm
perfectly fine with that.

>> -     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);
>
>
> ... and again, that's completely pointless.

Minimal patch.. I really didn't want to check what the heck lustre
does with the insane ll_vfs thing.

>> -int afs_permission(struct inode *inode, int mask)
>> +int afs_permission(struct dentry *dentry, struct inode *inode, int mask)
>
> Oh, _lovely_.  So not only do we pass dentry, the arguments are redundant
> as well.

Note that *not* passing in inode would make the patch much bigger,
because now every filesystem would have to add the

       struct inode *inode = dentry->d_inode;

at the top.

Also, I'm not actually convinced it is redundant at all. Remember the
RCU lookup case? dentry->d_inode is not safe.

The RCU case actually does

  inode_permission(nd->path.dentry, nd->inode, ..)

and here the difference between nd->inode and dentry->d_inode are
relevant, I think.

>> +static int gfs2_vfs_permission(struct dentry *dentry, struct inode *inode, int mask)
>> +{
>> +     return gfs2_permission(inode, mask);
>> +}
>
> Er...  You do realize that callers of gfs2_permission() tend to have
> the dentry in question, either directly or as ->d_parent of something
> they have?

Not true. Look closer.

Look at gfs2_lookupi() in particular, and check how it is called.

I did hate that part of the patch, and I did mention the kinds of
problems this will cause if the next phase passes in dentry to
"generic_permission()".

                Linus

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v2] ceph: fix posix ACL hooks
  2014-02-03 21:44                             ` Linus Torvalds
@ 2014-02-03 22:31                               ` Al Viro
  2014-02-06 20:51                                 ` Jeremy Allison
  0 siblings, 1 reply; 37+ messages in thread
From: Al Viro @ 2014-02-03 22:31 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Christoph Hellwig, Ilya Dryomov, Sage Weil, Dave Jones,
	Linux Kernel Mailing List, ceph-devel, linux-fsdevel,
	Guangliang Zhao, Li Wang, zheng.z.yan

On Mon, Feb 03, 2014 at 01:44:22PM -0800, Linus Torvalds wrote:
> On Mon, Feb 3, 2014 at 1:39 PM, Al Viro <viro@zeniv.linux.org.uk> wrote:
> >
> > If we really have hardlinks, the result of permission check would better
> > be a function of inode itself - as in, "if it gives different results
> > for two pathnames reachable for the same user, we have a bug".
		      ^^^^^^^^^
> No. You're wrong.
> 
> EVEN ON A UNIX FILESYSTEM THE PATH IS MEANINGFUL.
> 
> Do this: create a hardlink in two different directories. Make the
> *directory* permissions for one of the directories be something you
> cannot traverse. Now try to check the permissions of the *same* inode
> through those two paths. Notice how you get *different* results.
> 
> Really.

Yes.  In one case we won't get to looking at the permissions of that thing
at all.
 
> Now, imagine that you are doing the same thing over a network. On the
> server, there may be a single inode for the file, but when the client
> gives the server a pathname, the two pathnames to that single inode
> ARE NOT EQUIVALENT.

Why do we pretend that those are links, in the first place?

> And the fact is, filesystems with hardlinks and path-name-based
> operations do exist. cifs with the unix extensions is one of them.

Pox on Tridge...

I really, really hate that change; I can buy "->getxattr() has inconvenient
interface because of lousy protocol design", but spreading the same to
->permission(), with everything that will fall out of that... <shudder>

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v2] ceph: fix posix ACL hooks
  2014-02-03 22:12                         ` Linus Torvalds
@ 2014-02-03 22:40                           ` Al Viro
  2014-02-03 22:55                             ` Linus Torvalds
  2014-02-04 11:33                             ` Steven Whitehouse
  0 siblings, 2 replies; 37+ messages in thread
From: Al Viro @ 2014-02-03 22:40 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Christoph Hellwig, Ilya Dryomov, Sage Weil, Dave Jones,
	Linux Kernel Mailing List, ceph-devel, linux-fsdevel,
	Guangliang Zhao, Li Wang, zheng.z.yan

On Mon, Feb 03, 2014 at 02:12:00PM -0800, Linus Torvalds wrote:

> >> -int afs_permission(struct inode *inode, int mask)
> >> +int afs_permission(struct dentry *dentry, struct inode *inode, int mask)
> >
> > Oh, _lovely_.  So not only do we pass dentry, the arguments are redundant
> > as well.
> 
> Note that *not* passing in inode would make the patch much bigger,
> because now every filesystem would have to add the
> 
>        struct inode *inode = dentry->d_inode;
> 
> at the top.
> 
> Also, I'm not actually convinced it is redundant at all. Remember the
> RCU lookup case? dentry->d_inode is not safe.

Umm...  Point, but that actually means that we get an extra pitfall for
filesystem writers here.  foo_permission() passes dentry (now that it
has one) to foo_wank_a_lot(), with the latter using dentry->d_inode at
some point...

> >> +static int gfs2_vfs_permission(struct dentry *dentry, struct inode *inode, int mask)
> >> +{
> >> +     return gfs2_permission(inode, mask);
> >> +}
> >
> > Er...  You do realize that callers of gfs2_permission() tend to have
> > the dentry in question, either directly or as ->d_parent of something
> > they have?
> 
> Not true. Look closer.
> 
> Look at gfs2_lookupi() in particular, and check how it is called.

Yeowch...  gfs2_ok_to_move() is particulary nasty...  WTF do we need
it for and why is it not racy as hell?

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v2] ceph: fix posix ACL hooks
  2014-02-03 22:40                           ` Al Viro
@ 2014-02-03 22:55                             ` Linus Torvalds
  2014-02-04 11:33                             ` Steven Whitehouse
  1 sibling, 0 replies; 37+ messages in thread
From: Linus Torvalds @ 2014-02-03 22:55 UTC (permalink / raw)
  To: Al Viro
  Cc: Christoph Hellwig, Ilya Dryomov, Sage Weil, Dave Jones,
	Linux Kernel Mailing List, ceph-devel, linux-fsdevel,
	Guangliang Zhao, Li Wang, zheng.z.yan

On Mon, Feb 3, 2014 at 2:40 PM, Al Viro <viro@zeniv.linux.org.uk> wrote:
>
> Umm...  Point, but that actually means that we get an extra pitfall for
> filesystem writers here.  foo_permission() passes dentry (now that it
> has one) to foo_wank_a_lot(), with the latter using dentry->d_inode at
> some point...

I agree.

The good news, though, is that in the RCU lookup case, we have that
MAY_NOT_BLOCK thing, and most filesystems will have errored out for
any complex operations.

RCU lookup is special, and complicated, and sadly, the permissions
checking is very much part of that. But for the really complex cases,
at least we can punt.

>> Look at gfs2_lookupi() in particular, and check how it is called.
>
> Yeowch...  gfs2_ok_to_move() is particulary nasty...  WTF do we need
> it for and why is it not racy as hell?

I don't know. And I suspect that for things like the journal index
file lookup (which is actually worse - see gfs2_jindex_hold()) we
don't really about the permissions, since this is just done at
init_journal() time.

So I think all of this is quite solvable for gfs2, it just wasn't the
obvious kind of "we already have the dentry" case that every single
other case I looked at was.

                     Linus

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v2] ceph: fix posix ACL hooks
  2014-02-03 22:40                           ` Al Viro
  2014-02-03 22:55                             ` Linus Torvalds
@ 2014-02-04 11:33                             ` Steven Whitehouse
  2014-02-04 15:57                               ` Christoph Hellwig
  2014-02-04 16:17                               ` Linus Torvalds
  1 sibling, 2 replies; 37+ messages in thread
From: Steven Whitehouse @ 2014-02-04 11:33 UTC (permalink / raw)
  To: Al Viro
  Cc: Linus Torvalds, Christoph Hellwig, Ilya Dryomov, Sage Weil,
	Dave Jones, Linux Kernel Mailing List, ceph-devel, linux-fsdevel,
	Guangliang Zhao, Li Wang, zheng.z.yan

On Mon, 2014-02-03 at 22:40 +0000, Al Viro wrote:
> > >> +static int gfs2_vfs_permission(struct dentry *dentry, struct inode *inode, int mask)
> > >> +{
> > >> +     return gfs2_permission(inode, mask);
> > >> +}
> > >
> > > Er...  You do realize that callers of gfs2_permission() tend to have
> > > the dentry in question, either directly or as ->d_parent of something
> > > they have?
> > 
> > Not true. Look closer.
> > 
> > Look at gfs2_lookupi() in particular, and check how it is called.
> 
> Yeowch...  gfs2_ok_to_move() is particulary nasty...  WTF do we need
> it for and why is it not racy as hell?

Well, the original intent was to prevent us from moving a directory into
one of its subdirectories, and thus creating a loop. It is only called
when the rename is moving a directory, and when the parent directories
(source and destination) are different.

I know there is a problem there, recently reported by Bruce Fields which
he came across when looking at the d_splice_alias issue. The bug that
Bruce found only shows up in the clustered case, and not in the single
node case though.

Which particular race are you worried about? This check is covered by
the rename glock, which is basically a cluster wide version of the vfs
level ->s_vfs_rename_mutex.

To diverge from that topic for a moment, this thread has also brought
together some discussion on another issue which I've been pondering
recently.... that of whether the inode operations for get/set_xattr
should take a dentry or not. I had thought that we'd come to the
conclusion that 9p made it impossible to swap the current dentry
argument for an inode, and I was about to send a patch for selinux
support on clustered fs on that basis. However the discussion in this
thread has made me wonder whether that really is the case or not.... Al,
can you confirm whether your xattr-experimental patches are still under
active consideration?

The other question that I have relating to that side of things, is why
security_inode_permission() is called from __inode_permission() rather
than from generic_permission() ? Maybe there is a good reason, but I
can't immediately see what it is at the moment.

In response to the question elsewhere about GFS2 calling
gfs2_permission() after the vfs has already done its checks, that is
indeed down to needing to ensure that we have the cluster locks when
this check is called. More importantly to know that things haven't
changed since the VFS called the same function in case we've raced with
another node changing the permissions, for example. There are a number
of cases where we redo vfs level checks for this reason,

Steve.



^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v2] ceph: fix posix ACL hooks
  2014-02-04 11:33                             ` Steven Whitehouse
@ 2014-02-04 15:57                               ` Christoph Hellwig
  2014-02-04 16:17                               ` Linus Torvalds
  1 sibling, 0 replies; 37+ messages in thread
From: Christoph Hellwig @ 2014-02-04 15:57 UTC (permalink / raw)
  To: Steven Whitehouse
  Cc: Al Viro, Linus Torvalds, Christoph Hellwig, Ilya Dryomov,
	Sage Weil, Dave Jones, Linux Kernel Mailing List, ceph-devel,
	linux-fsdevel, Guangliang Zhao, Li Wang, zheng.z.yan

On Tue, Feb 04, 2014 at 11:33:35AM +0000, Steven Whitehouse wrote:
> To diverge from that topic for a moment, this thread has also brought
> together some discussion on another issue which I've been pondering
> recently.... that of whether the inode operations for get/set_xattr
> should take a dentry or not. I had thought that we'd come to the
> conclusion that 9p made it impossible to swap the current dentry
> argument for an inode, and I was about to send a patch for selinux
> support on clustered fs on that basis. However the discussion in this
> thread has made me wonder whether that really is the case or not.... Al,
> can you confirm whether your xattr-experimental patches are still under
> active consideration?

My plan was to work on the 9p and cifs conversions using the
d_find_alias hack we have in ceph right now.  That means the base work
could switch to passed in dentries or in case of 9p the per-inode fids
easily.

> The other question that I have relating to that side of things, is why
> security_inode_permission() is called from __inode_permission() rather
> than from generic_permission() ? Maybe there is a good reason, but I
> can't immediately see what it is at the moment.

Seems like almost everything of the security_* family is called from the
VFS instead of the filesystem.  There's also some very odd other
behaviour in there, e.g. for the xattrs sets are handed to the
filesystem first, and then the xattr layer calls into the security
layer, which for reads the filesystems is never reached at all.

> In response to the question elsewhere about GFS2 calling
> gfs2_permission() after the vfs has already done its checks, that is
> indeed down to needing to ensure that we have the cluster locks when
> this check is called. More importantly to know that things haven't
> changed since the VFS called the same function in case we've raced with
> another node changing the permissions, for example. There are a number
> of cases where we redo vfs level checks for this reason,

Seems like we should be able to grab a cluster lock where we grab
i_mutex in the namespace code to avoid having to redo all these checks.


^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v2] ceph: fix posix ACL hooks
  2014-02-04 11:33                             ` Steven Whitehouse
  2014-02-04 15:57                               ` Christoph Hellwig
@ 2014-02-04 16:17                               ` Linus Torvalds
  1 sibling, 0 replies; 37+ messages in thread
From: Linus Torvalds @ 2014-02-04 16:17 UTC (permalink / raw)
  To: Steven Whitehouse
  Cc: Al Viro, Christoph Hellwig, Ilya Dryomov, Sage Weil, Dave Jones,
	Linux Kernel Mailing List, ceph-devel, linux-fsdevel,
	Guangliang Zhao, Li Wang, zheng.z.yan

On Tue, Feb 4, 2014 at 3:33 AM, Steven Whitehouse <swhiteho@redhat.com> wrote:
>
> The other question that I have relating to that side of things, is why
> security_inode_permission() is called from __inode_permission() rather
> than from generic_permission() ? Maybe there is a good reason, but I
> can't immediately see what it is at the moment.

"generic_permission()" is just a helper that implements the default
UNIX permissions, and won't necessarily even be called. A filesystem
could decide not to call it at all, and in fact there are cases that
don't (eg coda or the bad_inode case).

The inode_permission() class of helpers, in contrast, is what gets
called by the VFS layer itself. So if you want to catch all permission
checks (and that would be security_inode_permission()) then you need
to catch it there.

                     Linus

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v2] ceph: fix posix ACL hooks
  2014-02-03 22:31                               ` Al Viro
@ 2014-02-06 20:51                                 ` Jeremy Allison
  0 siblings, 0 replies; 37+ messages in thread
From: Jeremy Allison @ 2014-02-06 20:51 UTC (permalink / raw)
  To: Al Viro
  Cc: Linus Torvalds, Christoph Hellwig, Ilya Dryomov, Sage Weil,
	Dave Jones, Linux Kernel Mailing List, ceph-devel, linux-fsdevel,
	Guangliang Zhao, Li Wang, zheng.z.yan

On Mon, Feb 03, 2014 at 10:31:27PM +0000, Al Viro wrote:
> 
> > And the fact is, filesystems with hardlinks and path-name-based
> > operations do exist. cifs with the unix extensions is one of them.
> 
> Pox on Tridge...

Actually you have to blame me for that. Tridge always
*HATED* the UNIX extensions :-).

^ permalink raw reply	[flat|nested] 37+ messages in thread

end of thread, other threads:[~2014-02-06 21:01 UTC | newest]

Thread overview: 37+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-01-28 18:40 [GIT PULL] Ceph updates for -rc1 Sage Weil
2014-01-28 21:10 ` Dave Jones
2014-01-28 21:48   ` Linus Torvalds
2014-01-29  6:08     ` Sage Weil
2014-01-29 14:30       ` Sage Weil
2014-01-29 16:36         ` Ilya Dryomov
2014-01-29 16:37           ` [PATCH v2] ceph: fix posix ACL hooks Ilya Dryomov
2014-01-29 19:09             ` Linus Torvalds
2014-01-30  7:54               ` Christoph Hellwig
2014-01-30 22:01                 ` Linus Torvalds
2014-01-31  0:14                   ` Sage Weil
2014-02-03 10:29                   ` Christoph Hellwig
2014-02-03 11:13                     ` Al Viro
2014-02-03 21:03                     ` Linus Torvalds
2014-02-03 21:19                       ` Al Viro
2014-02-03 21:24                         ` Christoph Hellwig
2014-02-03 21:31                           ` Al Viro
2014-02-03 21:36                             ` Christoph Hellwig
2014-02-03 21:37                             ` Linus Torvalds
2014-02-03 21:42                               ` Al Viro
2014-02-03 21:31                         ` Linus Torvalds
2014-02-03 21:39                           ` Al Viro
2014-02-03 21:43                             ` Al Viro
2014-02-03 21:44                             ` Linus Torvalds
2014-02-03 22:31                               ` Al Viro
2014-02-06 20:51                                 ` Jeremy Allison
2014-02-03 21:23                       ` Christoph Hellwig
2014-02-03 21:59                       ` Al Viro
2014-02-03 22:12                         ` Linus Torvalds
2014-02-03 22:40                           ` Al Viro
2014-02-03 22:55                             ` Linus Torvalds
2014-02-04 11:33                             ` Steven Whitehouse
2014-02-04 15:57                               ` Christoph Hellwig
2014-02-04 16:17                               ` Linus Torvalds
2014-02-03 21:59                       ` Linus Torvalds
2014-01-29 20:43             ` Ilya Dryomov
2014-01-30 10:46         ` [GIT PULL] Ceph updates for -rc1 Christoph Hellwig

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).