Commit d3d90cc2 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull vfs d_revalidate updates from Al Viro:
 "Provide stable parent and name to ->d_revalidate() instances

  Most of the filesystem methods where we care about dentry name and
  parent have their stability guaranteed by the callers;
  ->d_revalidate() is the major exception.

  It's easy enough for callers to supply stable values for expected name
  and expected parent of the dentry being validated. That kills quite a
  bit of boilerplate in ->d_revalidate() instances, along with a bunch
  of races where they used to access ->d_name without sufficient
  precautions"

* tag 'pull-revalidate' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
  9p: fix ->rename_sem exclusion
  orangefs_d_revalidate(): use stable parent inode and name passed by caller
  ocfs2_dentry_revalidate(): use stable parent inode and name passed by caller
  nfs: fix ->d_revalidate() UAF on ->d_name accesses
  nfs{,4}_lookup_validate(): use stable parent inode passed by caller
  gfs2_drevalidate(): use stable parent inode and name passed by caller
  fuse_dentry_revalidate(): use stable parent inode and name passed by caller
  vfat_revalidate{,_ci}(): use stable parent inode passed by caller
  exfat_d_revalidate(): use stable parent inode passed by caller
  fscrypt_d_revalidate(): use stable parent inode passed by caller
  ceph_d_revalidate(): propagate stable name down into request encoding
  ceph_d_revalidate(): use stable parent inode passed by caller
  afs_d_revalidate(): use stable name and parent inode passed by caller
  Pass parent directory inode and expected name to ->d_revalidate()
  generic_ci_d_compare(): use shortname_storage
  ext4 fast_commit: make use of name_snapshot primitives
  dissolve external_name.u into separate members
  make take_dentry_name_snapshot() lockless
  dcache: back inline names with a struct-wrapped array of unsigned long
  make sure that DNAME_INLINE_LEN is a multiple of word size
parents ce335806 30d61efe
Loading
Loading
Loading
Loading
+6 −1
Original line number Diff line number Diff line
@@ -17,7 +17,8 @@ dentry_operations

prototypes::

	int (*d_revalidate)(struct dentry *, unsigned int);
	int (*d_revalidate)(struct inode *, const struct qstr *,
			    struct dentry *, unsigned int);
	int (*d_weak_revalidate)(struct dentry *, unsigned int);
	int (*d_hash)(const struct dentry *, struct qstr *);
	int (*d_compare)(const struct dentry *,
@@ -30,6 +31,8 @@ prototypes::
	struct vfsmount *(*d_automount)(struct path *path);
	int (*d_manage)(const struct path *, bool);
	struct dentry *(*d_real)(struct dentry *, enum d_real_type type);
	bool (*d_unalias_trylock)(const struct dentry *);
	void (*d_unalias_unlock)(const struct dentry *);

locking rules:

@@ -49,6 +52,8 @@ d_dname: no no no no
d_automount:	   no		no		yes		no
d_manage:	   no		no		yes (ref-walk)	maybe
d_real		   no		no		yes 		no
d_unalias_trylock  yes		no		no 		no
d_unalias_unlock   yes		no		no 		no
================== ===========	========	==============	========

inode_operations
+16 −0
Original line number Diff line number Diff line
@@ -1141,3 +1141,19 @@ pointer are gone.

set_blocksize() takes opened struct file instead of struct block_device now
and it *must* be opened exclusive.

---

** mandatory**

->d_revalidate() gets two extra arguments - inode of parent directory and
name our dentry is expected to have.  Both are stable (dir is pinned in
non-RCU case and will stay around during the call in RCU case, and name
is guaranteed to stay unchanging).  Your instance doesn't have to use
either, but it often helps to avoid a lot of painful boilerplate.
Note that while name->name is stable and NUL-terminated, it may (and
often will) have name->name[name->len] equal to '/' rather than '\0' -
in normal case it points into the pathname being looked up.
NOTE: if you need something like full path from the root of filesystem,
you are still on your own - this assists with simple cases, but it's not
magic.
+23 −1
Original line number Diff line number Diff line
@@ -1251,7 +1251,8 @@ defined:
.. code-block:: c

	struct dentry_operations {
		int (*d_revalidate)(struct dentry *, unsigned int);
		int (*d_revalidate)(struct inode *, const struct qstr *,
				    struct dentry *, unsigned int);
		int (*d_weak_revalidate)(struct dentry *, unsigned int);
		int (*d_hash)(const struct dentry *, struct qstr *);
		int (*d_compare)(const struct dentry *,
@@ -1264,6 +1265,8 @@ defined:
		struct vfsmount *(*d_automount)(struct path *);
		int (*d_manage)(const struct path *, bool);
		struct dentry *(*d_real)(struct dentry *, enum d_real_type type);
		bool (*d_unalias_trylock)(const struct dentry *);
		void (*d_unalias_unlock)(const struct dentry *);
	};

``d_revalidate``
@@ -1427,6 +1430,25 @@ defined:

	For non-regular files, the 'dentry' argument is returned.

``d_unalias_trylock``
	if present, will be called by d_splice_alias() before moving a
	preexisting attached alias.  Returning false prevents __d_move(),
	making d_splice_alias() fail with -ESTALE.

	Rationale: setting FS_RENAME_DOES_D_MOVE will prevent d_move()
	and d_exchange() calls from the outside of filesystem methods;
	however, it does not guarantee that attached dentries won't
	be renamed or moved by d_splice_alias() finding a preexisting
	alias for a directory inode.  Normally we would not care;
	however, something that wants to stabilize the entire path to
	root over a blocking operation might need that.  See 9p for one
	(and hopefully only) example.

``d_unalias_unlock``
	should be paired with ``d_unalias_trylock``; that one is called after
	__d_move() call in __d_unalias().


Each dentry has a pointer to its parent dentry, as well as a hash list
of child dentries.  Child dentries are basically like files in a
directory.
+1 −1
Original line number Diff line number Diff line
@@ -202,7 +202,7 @@ static inline struct v9fs_session_info *v9fs_inode2v9ses(struct inode *inode)
	return inode->i_sb->s_fs_info;
}

static inline struct v9fs_session_info *v9fs_dentry2v9ses(struct dentry *dentry)
static inline struct v9fs_session_info *v9fs_dentry2v9ses(const struct dentry *dentry)
{
	return dentry->d_sb->s_fs_info;
}
+24 −2
Original line number Diff line number Diff line
@@ -61,7 +61,7 @@ static void v9fs_dentry_release(struct dentry *dentry)
		p9_fid_put(hlist_entry(p, struct p9_fid, dlist));
}

static int v9fs_lookup_revalidate(struct dentry *dentry, unsigned int flags)
static int __v9fs_lookup_revalidate(struct dentry *dentry, unsigned int flags)
{
	struct p9_fid *fid;
	struct inode *inode;
@@ -99,14 +99,36 @@ static int v9fs_lookup_revalidate(struct dentry *dentry, unsigned int flags)
	return 1;
}

static int v9fs_lookup_revalidate(struct inode *dir, const struct qstr *name,
				  struct dentry *dentry, unsigned int flags)
{
	return __v9fs_lookup_revalidate(dentry, flags);
}

static bool v9fs_dentry_unalias_trylock(const struct dentry *dentry)
{
	struct v9fs_session_info *v9ses = v9fs_dentry2v9ses(dentry);
	return down_write_trylock(&v9ses->rename_sem);
}

static void v9fs_dentry_unalias_unlock(const struct dentry *dentry)
{
	struct v9fs_session_info *v9ses = v9fs_dentry2v9ses(dentry);
	up_write(&v9ses->rename_sem);
}

const struct dentry_operations v9fs_cached_dentry_operations = {
	.d_revalidate = v9fs_lookup_revalidate,
	.d_weak_revalidate = v9fs_lookup_revalidate,
	.d_weak_revalidate = __v9fs_lookup_revalidate,
	.d_delete = v9fs_cached_dentry_delete,
	.d_release = v9fs_dentry_release,
	.d_unalias_trylock = v9fs_dentry_unalias_trylock,
	.d_unalias_unlock = v9fs_dentry_unalias_unlock,
};

const struct dentry_operations v9fs_dentry_operations = {
	.d_delete = always_delete_dentry,
	.d_release = v9fs_dentry_release,
	.d_unalias_trylock = v9fs_dentry_unalias_trylock,
	.d_unalias_unlock = v9fs_dentry_unalias_unlock,
};
Loading