vfs: be even more careful about dentry RCU name lookups
[linux-3.10.git] / fs / dcache.c
index 92099f6..a7675e0 100644 (file)
@@ -192,6 +192,7 @@ static inline int dentry_string_cmp(const unsigned char *cs, const unsigned char
 
 static inline int dentry_cmp(const struct dentry *dentry, const unsigned char *ct, unsigned tcount)
 {
+       const unsigned char *cs;
        /*
         * Be careful about RCU walk racing with rename:
         * use ACCESS_ONCE to fetch the name pointer.
@@ -208,7 +209,9 @@ static inline int dentry_cmp(const struct dentry *dentry, const unsigned char *c
         * early because the data cannot match (there can
         * be no NUL in the ct/tcount data)
         */
-       return dentry_string_cmp(ACCESS_ONCE(dentry->d_name.name), ct, tcount);
+       cs = ACCESS_ONCE(dentry->d_name.name);
+       smp_read_barrier_depends();
+       return dentry_string_cmp(cs, ct, tcount);
 }
 
 static void __d_free(struct rcu_head *head)
@@ -1271,6 +1274,13 @@ struct dentry *__d_alloc(struct super_block *sb, const struct qstr *name)
        if (!dentry)
                return NULL;
 
+       /*
+        * We guarantee that the inline name is always NUL-terminated.
+        * This way the memcpy() done by the name switching in rename
+        * will still always have a NUL at the end, even if we might
+        * be overwriting an internal NUL character
+        */
+       dentry->d_iname[DNAME_INLINE_LEN-1] = 0;
        if (name->len > DNAME_INLINE_LEN-1) {
                dname = kmalloc(name->len + 1, GFP_KERNEL);
                if (!dname) {
@@ -1280,13 +1290,16 @@ struct dentry *__d_alloc(struct super_block *sb, const struct qstr *name)
        } else  {
                dname = dentry->d_iname;
        }       
-       dentry->d_name.name = dname;
 
        dentry->d_name.len = name->len;
        dentry->d_name.hash = name->hash;
        memcpy(dname, name->name, name->len);
        dname[name->len] = 0;
 
+       /* Make sure we always see the terminating NUL character */
+       smp_wmb();
+       dentry->d_name.name = dname;
+
        dentry->d_count = 1;
        dentry->d_flags = 0;
        spin_lock_init(&dentry->d_lock);