diff mbox

Orangefs: bug involving copy_attributes_to_inode

Message ID CAOg9mSQLMrzPJDhfWwdvMJB1zRJop+d6a5gw+8c_GXSKP6OGrA@mail.gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Mike Marshall Dec. 29, 2015, 8:06 p.m. UTC
All...

WRT: fs/orangefs/symlink.c...

 AV> Said that, there is an unpleasant bug in that area - link_target of a live
 AV> inode can be overwritten, right under the pathname resolution walking the
 AV> old contents of that thing.

 AV> copy_attributes_to_inode() is triggered by ->d_revalidate() and
 AV> by ->getattr() and it's really, really unsafe for a live inode.

I can see how that would be unsafe, but have had to look at a lot
of other people's code, and in the Documentation directory, and
think a lot (ouch it hurts when I do that) to see how to try and make
it safe.

./filesystems/vfs.txt

 d_revalidate: called when the VFS needs to revalidate a dentry. This
        is called whenever a name look-up finds a dentry in the
        dcache. Most local filesystems leave this as NULL, because all their
        dentries in the dcache are valid. Network filesystems are different
        since things can change on the server without the client necessarily
        being aware of it.

        This function should return a positive value if the dentry is still
        valid, and zero or a negative error code if it isn't.

Perhaps d_revalidate didn't have any business triggering
copy_attributes_to_inode? I've made this change, and have no
regressions with dbench or other silly tests, I'm about to leave the
office for the afternoon with xfstests running.

Do you think this change fixes the bug?

$ git diff
--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/fs/orangefs/dcache.c b/fs/orangefs/dcache.c
index 5dd9841..0419981 100644
--- a/fs/orangefs/dcache.c
+++ b/fs/orangefs/dcache.c
@@ -77,7 +77,7 @@  out_drop:
 /*
  * Verify that dentry is valid.
  *
- * Should return 1 if dentry can still be trusted, else 0
+ * Should return 1 if dentry can still be trusted, else 0.
  */
 static int orangefs_d_revalidate(struct dentry *dentry, unsigned int flags)
 {
@@ -92,49 +92,27 @@  static int orangefs_d_revalidate(struct dentry *dentry, unsi

        /* find inode from dentry */
        if (!dentry->d_inode) {
-               gossip_debug(GOSSIP_DCACHE_DEBUG, "%s: negative dentry.\n",
+               gossip_debug(GOSSIP_DCACHE_DEBUG,
+                            "%s: negative dentry.\n",
                             __func__);
-               goto invalid_exit;
+               goto out;
        }

        gossip_debug(GOSSIP_DCACHE_DEBUG, "%s: inode valid.\n", __func__);
        inode = dentry->d_inode;

-       /*
-        * first perform a lookup to make sure that the object not only
-        * exists, but is still in the expected place in the name space
-        */
-       if (!is_root_handle(inode)) {
-               if (!orangefs_revalidate_lookup(dentry))
-                       goto invalid_exit;
-       } else {
-               gossip_debug(GOSSIP_DCACHE_DEBUG,
-                            "%s: root handle, lookup skipped.\n",
-                            __func__);
+       /* skip root handle lookups. */
+       if (is_root_handle(inode)) {
+               ret = 1;
+               goto out;
        }

-       /* now perform getattr */
-       gossip_debug(GOSSIP_DCACHE_DEBUG,
-                    "%s: doing getattr: inode: %p, handle: %pU\n",
-                    __func__,
-                    inode,
-                    get_khandle_from_ino(inode));
-       ret = orangefs_inode_getattr(inode, ORANGEFS_ATTR_SYS_ALL_NOHINT);
-       gossip_debug(GOSSIP_DCACHE_DEBUG,
-                    "%s: getattr %s (ret = %d), returning %s for dentry i_count
-                    __func__,
-                    (ret == 0 ? "succeeded" : "failed"),
-                    ret,
-                    (ret == 0 ? "valid" : "INVALID"),
-                    atomic_read(&inode->i_count));
-       if (ret != 0)
-               goto invalid_exit;
-
-       /* dentry is valid! */
-       return 1;
-
-invalid_exit:
-       return 0;
+       /* lookup the object. */
+       if (orangefs_revalidate_lookup(dentry))
+               ret = 1;
+
+out:
+       return ret;
 }

 const struct dentry_operations orangefs_dentry_operations = {