Merge branch 'work.misc' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
authorLinus Torvalds <torvalds@linux-foundation.org>
Sat, 8 Jul 2017 17:50:54 +0000 (10:50 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 8 Jul 2017 17:50:54 +0000 (10:50 -0700)
Pull misc filesystem updates from Al Viro:
 "Assorted normal VFS / filesystems stuff..."

* 'work.misc' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
  dentry name snapshots
  Make statfs properly return read-only state after emergency remount
  fs/dcache: init in_lookup_hashtable
  minix: Deinline get_block, save 2691 bytes
  fs: Reorder inode_owner_or_capable() to avoid needless
  fs: warn in case userspace lied about modprobe return

fs/dcache.c
fs/debugfs/inode.c
fs/filesystems.c
fs/inode.c
fs/minix/itree_common.c
fs/namei.c
fs/notify/fsnotify.c
fs/statfs.c
include/linux/dcache.h
include/linux/fsnotify.h

index a140fe1dbb1acc26e9e452f06b8ca108c085ecca..7ece68d0d4dbd731295ee8eec1ab0e4d4d504065 100644 (file)
@@ -277,6 +277,33 @@ static inline int dname_external(const struct dentry *dentry)
        return dentry->d_name.name != dentry->d_iname;
 }
 
+void take_dentry_name_snapshot(struct name_snapshot *name, struct dentry *dentry)
+{
+       spin_lock(&dentry->d_lock);
+       if (unlikely(dname_external(dentry))) {
+               struct external_name *p = external_name(dentry);
+               atomic_inc(&p->u.count);
+               spin_unlock(&dentry->d_lock);
+               name->name = p->name;
+       } else {
+               memcpy(name->inline_name, dentry->d_iname, DNAME_INLINE_LEN);
+               spin_unlock(&dentry->d_lock);
+               name->name = name->inline_name;
+       }
+}
+EXPORT_SYMBOL(take_dentry_name_snapshot);
+
+void release_dentry_name_snapshot(struct name_snapshot *name)
+{
+       if (unlikely(name->name != name->inline_name)) {
+               struct external_name *p;
+               p = container_of(name->name, struct external_name, name[0]);
+               if (unlikely(atomic_dec_and_test(&p->u.count)))
+                       kfree_rcu(p, u.head);
+       }
+}
+EXPORT_SYMBOL(release_dentry_name_snapshot);
+
 static inline void __d_set_inode_and_type(struct dentry *dentry,
                                          struct inode *inode,
                                          unsigned type_flags)
@@ -3598,6 +3625,11 @@ EXPORT_SYMBOL(d_genocide);
 
 void __init vfs_caches_init_early(void)
 {
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(in_lookup_hashtable); i++)
+               INIT_HLIST_BL_HEAD(&in_lookup_hashtable[i]);
+
        dcache_init_early();
        inode_init_early();
 }
index 77440e4aa9d4eded846e3223efadb3bb076c0e68..a0e4e2f7e0befb47c22f9d275e90c039457e1b86 100644 (file)
@@ -766,7 +766,7 @@ struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry,
 {
        int error;
        struct dentry *dentry = NULL, *trap;
-       const char *old_name;
+       struct name_snapshot old_name;
 
        trap = lock_rename(new_dir, old_dir);
        /* Source or destination directories don't exist? */
@@ -781,19 +781,19 @@ struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry,
        if (IS_ERR(dentry) || dentry == trap || d_really_is_positive(dentry))
                goto exit;
 
-       old_name = fsnotify_oldname_init(old_dentry->d_name.name);
+       take_dentry_name_snapshot(&old_name, old_dentry);
 
        error = simple_rename(d_inode(old_dir), old_dentry, d_inode(new_dir),
                              dentry, 0);
        if (error) {
-               fsnotify_oldname_free(old_name);
+               release_dentry_name_snapshot(&old_name);
                goto exit;
        }
        d_move(old_dentry, dentry);
-       fsnotify_move(d_inode(old_dir), d_inode(new_dir), old_name,
+       fsnotify_move(d_inode(old_dir), d_inode(new_dir), old_name.name,
                d_is_dir(old_dentry),
                NULL, old_dentry);
-       fsnotify_oldname_free(old_name);
+       release_dentry_name_snapshot(&old_name);
        unlock_rename(new_dir, old_dir);
        dput(dentry);
        return old_dentry;
index cac75547d35ccb163a6ce0e94c786c1fd150e5db..8b99955e3504e2e8725ff4f4394b84898e88d155 100644 (file)
@@ -275,8 +275,10 @@ struct file_system_type *get_fs_type(const char *name)
        int len = dot ? dot - name : strlen(name);
 
        fs = __get_fs_type(name, len);
-       if (!fs && (request_module("fs-%.*s", len, name) == 0))
+       if (!fs && (request_module("fs-%.*s", len, name) == 0)) {
                fs = __get_fs_type(name, len);
+               WARN_ONCE(!fs, "request_module fs-%.*s succeeded, but still no fs?\n", len, name);
+       }
 
        if (dot && fs && !(fs->fs_flags & FS_HAS_SUBTYPE)) {
                put_filesystem(fs);
index 5cbc8e6e93905b1a8f880dfcf82065442e0933e9..50370599e37104708b95ede3a627052cc0d910d3 100644 (file)
@@ -2014,7 +2014,7 @@ bool inode_owner_or_capable(const struct inode *inode)
                return true;
 
        ns = current_user_ns();
-       if (ns_capable(ns, CAP_FOWNER) && kuid_has_mapping(ns, inode->i_uid))
+       if (kuid_has_mapping(ns, inode->i_uid) && ns_capable(ns, CAP_FOWNER))
                return true;
        return false;
 }
index 4c57c9af6946f0918bf689e42107343b4e09f164..2d1ca08870f7f6142082e880d78d0e9acc31c3dd 100644 (file)
@@ -142,7 +142,7 @@ changed:
        return -EAGAIN;
 }
 
-static inline int get_block(struct inode * inode, sector_t block,
+static int get_block(struct inode * inode, sector_t block,
                        struct buffer_head *bh, int create)
 {
        int err = -EIO;
index 8bacc390c51ed27bf3a680fcf6fd7f32d02e5c2d..e0b46eb0e2129ca337bba8e4634d96edf0683117 100644 (file)
@@ -1008,7 +1008,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 (inode_owner_or_capable(inode) || safe_hardlink_source(inode))
+       if (safe_hardlink_source(inode) || inode_owner_or_capable(inode))
                return 0;
 
        audit_log_link_denied("linkat", link);
@@ -4363,11 +4363,11 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
 {
        int error;
        bool is_dir = d_is_dir(old_dentry);
-       const unsigned char *old_name;
        struct inode *source = old_dentry->d_inode;
        struct inode *target = new_dentry->d_inode;
        bool new_is_dir = false;
        unsigned max_links = new_dir->i_sb->s_max_links;
+       struct name_snapshot old_name;
 
        if (source == target)
                return 0;
@@ -4414,7 +4414,7 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
        if (error)
                return error;
 
-       old_name = fsnotify_oldname_init(old_dentry->d_name.name);
+       take_dentry_name_snapshot(&old_name, old_dentry);
        dget(new_dentry);
        if (!is_dir || (flags & RENAME_EXCHANGE))
                lock_two_nondirectories(source, target);
@@ -4469,14 +4469,14 @@ out:
                inode_unlock(target);
        dput(new_dentry);
        if (!error) {
-               fsnotify_move(old_dir, new_dir, old_name, is_dir,
+               fsnotify_move(old_dir, new_dir, old_name.name, is_dir,
                              !(flags & RENAME_EXCHANGE) ? target : NULL, old_dentry);
                if (flags & RENAME_EXCHANGE) {
                        fsnotify_move(new_dir, old_dir, old_dentry->d_name.name,
                                      new_is_dir, NULL, new_dentry);
                }
        }
-       fsnotify_oldname_free(old_name);
+       release_dentry_name_snapshot(&old_name);
 
        return error;
 }
index 01a9f0f007d4518563fd9ad1f06d3b48e2aa872a..0c4583b61717646e491a28d879b5762faeb8956e 100644 (file)
@@ -161,16 +161,20 @@ int __fsnotify_parent(const struct path *path, struct dentry *dentry, __u32 mask
        if (unlikely(!fsnotify_inode_watches_children(p_inode)))
                __fsnotify_update_child_dentry_flags(p_inode);
        else if (p_inode->i_fsnotify_mask & mask) {
+               struct name_snapshot name;
+
                /* we are notifying a parent so come up with the new mask which
                 * specifies these are events which came from a child. */
                mask |= FS_EVENT_ON_CHILD;
 
+               take_dentry_name_snapshot(&name, dentry);
                if (path)
                        ret = fsnotify(p_inode, mask, path, FSNOTIFY_EVENT_PATH,
-                                      dentry->d_name.name, 0);
+                                      name.name, 0);
                else
                        ret = fsnotify(p_inode, mask, dentry->d_inode, FSNOTIFY_EVENT_INODE,
-                                      dentry->d_name.name, 0);
+                                      name.name, 0);
+               release_dentry_name_snapshot(&name);
        }
 
        dput(parent);
index 41a6a82da5e2614fd4e441a8c7a5341e1ee7273e..fab9b6a3c116c145115fa230a5848e6d9eb81bb1 100644 (file)
@@ -38,6 +38,8 @@ static int flags_by_sb(int s_flags)
                flags |= ST_SYNCHRONOUS;
        if (s_flags & MS_MANDLOCK)
                flags |= ST_MANDLOCK;
+       if (s_flags & MS_RDONLY)
+               flags |= ST_RDONLY;
        return flags;
 }
 
index d2e38dc6172c0670472e654103f2f0bfa0d1b2ba..025727bf679745e507bb427f14198bf1e607ae8e 100644 (file)
@@ -591,5 +591,11 @@ static inline struct inode *d_real_inode(const struct dentry *dentry)
        return d_backing_inode(d_real((struct dentry *) dentry, NULL, 0));
 }
 
+struct name_snapshot {
+       const char *name;
+       char inline_name[DNAME_INLINE_LEN];
+};
+void take_dentry_name_snapshot(struct name_snapshot *, struct dentry *);
+void release_dentry_name_snapshot(struct name_snapshot *);
 
 #endif /* __LINUX_DCACHE_H */
index b43d3f5bd9ead666e4e53894e8f98bdf25fb7ad8..b78aa7ac77ce1d6920aa196f4cd71d6d6a7aee7b 100644 (file)
@@ -293,35 +293,4 @@ static inline void fsnotify_change(struct dentry *dentry, unsigned int ia_valid)
        }
 }
 
-#if defined(CONFIG_FSNOTIFY)   /* notify helpers */
-
-/*
- * fsnotify_oldname_init - save off the old filename before we change it
- */
-static inline const unsigned char *fsnotify_oldname_init(const unsigned char *name)
-{
-       return kstrdup(name, GFP_KERNEL);
-}
-
-/*
- * fsnotify_oldname_free - free the name we got from fsnotify_oldname_init
- */
-static inline void fsnotify_oldname_free(const unsigned char *old_name)
-{
-       kfree(old_name);
-}
-
-#else  /* CONFIG_FSNOTIFY */
-
-static inline const char *fsnotify_oldname_init(const unsigned char *name)
-{
-       return NULL;
-}
-
-static inline void fsnotify_oldname_free(const unsigned char *old_name)
-{
-}
-
-#endif /*  CONFIG_FSNOTIFY */
-
 #endif /* _LINUX_FS_NOTIFY_H */