]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
exportfs: fix the fallback implementation of the get_name export operation
authorTrond Myklebust <trond.myklebust@hammerspace.com>
Thu, 28 Dec 2023 20:15:10 +0000 (15:15 -0500)
committerChuck Lever <chuck.lever@oracle.com>
Tue, 23 Jan 2024 15:58:44 +0000 (10:58 -0500)
The fallback implementation for the get_name export operation uses
readdir() to try to match the inode number to a filename. That filename
is then used together with lookup_one() to produce a dentry.
A problem arises when we match the '.' or '..' entries, since that
causes lookup_one() to fail. This has sometimes been seen to occur for
filesystems that violate POSIX requirements around uniqueness of inode
numbers, something that is common for snapshot directories.

This patch just ensures that we skip '.' and '..' rather than allowing a
match.

Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Acked-by: Amir Goldstein <amir73il@gmail.com>
Link: https://lore.kernel.org/linux-nfs/CAOQ4uxiOZobN76OKB-VBNXWeFKVwLW_eK5QtthGyYzWU9mjb7Q@mail.gmail.com/
Acked-by: Christian Brauner <brauner@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
fs/exportfs/expfs.c

index 3ae0154c5680b2c06771a3819d825a1953aa707f..dcf7d86c2ce4f7cce44858d52681b79a3428f4b7 100644 (file)
@@ -244,6 +244,16 @@ struct getdents_callback {
        int sequence;           /* sequence counter */
 };
 
+/* Copied from lookup_one_common() */
+static inline bool is_dot_dotdot(const char *name, size_t len)
+{
+       if (unlikely(name[0] == '.')) {
+               if (len < 2 || (len == 2 && name[1] == '.'))
+                       return true;
+       }
+       return false;
+}
+
 /*
  * A rather strange filldir function to capture
  * the name matching the specified inode number.
@@ -255,7 +265,7 @@ static bool filldir_one(struct dir_context *ctx, const char *name, int len,
                container_of(ctx, struct getdents_callback, ctx);
 
        buf->sequence++;
-       if (buf->ino == ino && len <= NAME_MAX) {
+       if (buf->ino == ino && len <= NAME_MAX && !is_dot_dotdot(name, len)) {
                memcpy(buf->name, name, len);
                buf->name[len] = '\0';
                buf->found = 1;