Merge branch 'work.audit' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 22 Feb 2021 21:05:30 +0000 (13:05 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 22 Feb 2021 21:05:30 +0000 (13:05 -0800)
Pull RCU-safe common_lsm_audit() from Al Viro:
 "Make common_lsm_audit() non-blocking and usable from RCU pathwalk
  context.

  We don't really need to grab/drop dentry in there - rcu_read_lock() is
  enough. There's a couple of followups using that to simplify the
  logics in selinux, but those hadn't soaked in -next yet, so they'll
  have to go in next window"

* 'work.audit' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
  make dump_common_audit_data() safe to be called from RCU pathwalk
  new helper: d_find_alias_rcu()

1  2 
fs/dcache.c

diff --combined fs/dcache.c
@@@ -456,6 -456,23 +456,6 @@@ static void d_lru_shrink_move(struct li
        list_lru_isolate_move(lru, &dentry->d_lru, list);
  }
  
 -/**
 - * d_drop - drop a dentry
 - * @dentry: dentry to drop
 - *
 - * d_drop() unhashes the entry from the parent dentry hashes, so that it won't
 - * be found through a VFS lookup any more. Note that this is different from
 - * deleting the dentry - d_delete will try to mark the dentry negative if
 - * possible, giving a successful _negative_ lookup, while d_drop will
 - * just make the cache lookup fail.
 - *
 - * d_drop() is used mainly for stuff that wants to invalidate a dentry for some
 - * reason (NFS timeouts or autofs deletes).
 - *
 - * __d_drop requires dentry->d_lock
 - * ___d_drop doesn't mark dentry as "unhashed"
 - *   (dentry->d_hash.pprev will be LIST_POISON2, not NULL).
 - */
  static void ___d_drop(struct dentry *dentry)
  {
        struct hlist_bl_head *b;
@@@ -484,24 -501,6 +484,24 @@@ void __d_drop(struct dentry *dentry
  }
  EXPORT_SYMBOL(__d_drop);
  
 +/**
 + * d_drop - drop a dentry
 + * @dentry: dentry to drop
 + *
 + * d_drop() unhashes the entry from the parent dentry hashes, so that it won't
 + * be found through a VFS lookup any more. Note that this is different from
 + * deleting the dentry - d_delete will try to mark the dentry negative if
 + * possible, giving a successful _negative_ lookup, while d_drop will
 + * just make the cache lookup fail.
 + *
 + * d_drop() is used mainly for stuff that wants to invalidate a dentry for some
 + * reason (NFS timeouts or autofs deletes).
 + *
 + * __d_drop requires dentry->d_lock
 + *
 + * ___d_drop doesn't mark dentry as "unhashed"
 + * (dentry->d_hash.pprev will be LIST_POISON2, not NULL).
 + */
  void d_drop(struct dentry *dentry)
  {
        spin_lock(&dentry->d_lock);
@@@ -997,6 -996,20 +997,6 @@@ struct dentry *d_find_any_alias(struct 
  }
  EXPORT_SYMBOL(d_find_any_alias);
  
 -/**
 - * d_find_alias - grab a hashed alias of inode
 - * @inode: inode in question
 - *
 - * If inode has a hashed alias, or is a directory and has any alias,
 - * acquire the reference to alias and return it. Otherwise return NULL.
 - * Notice that if inode is a directory there can be only one alias and
 - * it can be unhashed only if it has no children, or if it is the root
 - * of a filesystem, or if the directory was renamed and d_revalidate
 - * was the first vfs operation to notice.
 - *
 - * If the inode has an IS_ROOT, DCACHE_DISCONNECTED alias, then prefer
 - * any other hashed alias over that one.
 - */
  static struct dentry *__d_find_alias(struct inode *inode)
  {
        struct dentry *alias;
        return NULL;
  }
  
 +/**
 + * d_find_alias - grab a hashed alias of inode
 + * @inode: inode in question
 + *
 + * If inode has a hashed alias, or is a directory and has any alias,
 + * acquire the reference to alias and return it. Otherwise return NULL.
 + * Notice that if inode is a directory there can be only one alias and
 + * it can be unhashed only if it has no children, or if it is the root
 + * of a filesystem, or if the directory was renamed and d_revalidate
 + * was the first vfs operation to notice.
 + *
 + * If the inode has an IS_ROOT, DCACHE_DISCONNECTED alias, then prefer
 + * any other hashed alias over that one.
 + */
  struct dentry *d_find_alias(struct inode *inode)
  {
        struct dentry *de = NULL;
  EXPORT_SYMBOL(d_find_alias);
  
  /*
+  *  Caller MUST be holding rcu_read_lock() and be guaranteed
+  *  that inode won't get freed until rcu_read_unlock().
+  */
+ struct dentry *d_find_alias_rcu(struct inode *inode)
+ {
+       struct hlist_head *l = &inode->i_dentry;
+       struct dentry *de = NULL;
+       spin_lock(&inode->i_lock);
+       // ->i_dentry and ->i_rcu are colocated, but the latter won't be
+       // used without having I_FREEING set, which means no aliases left
+       if (likely(!(inode->i_state & I_FREEING) && !hlist_empty(l))) {
+               if (S_ISDIR(inode->i_mode)) {
+                       de = hlist_entry(l->first, struct dentry, d_u.d_alias);
+               } else {
+                       hlist_for_each_entry(de, l, d_u.d_alias)
+                               if (!d_unhashed(de))
+                                       break;
+               }
+       }
+       spin_unlock(&inode->i_lock);
+       return de;
+ }
+ /*
   *    Try to kill dentries associated with this inode.
   * WARNING: you must own a reference to inode.
   */