afs: Make /afs/@cell and /afs/.@cell symlinks
authorDavid Howells <dhowells@redhat.com>
Tue, 7 Jan 2025 18:34:51 +0000 (18:34 +0000)
committerChristian Brauner <brauner@kernel.org>
Fri, 10 Jan 2025 13:54:08 +0000 (14:54 +0100)
Make /afs/@cell a symlink in the /afs dynamic root to match what other AFS
clients do rather than doing a substitution in the dentry name.  This has
the bonus of being tab-expandable also.

Further, provide a /afs/.@cell symlink to point to the dotted cell share.

Signed-off-by: David Howells <dhowells@redhat.com>
Link: https://lore.kernel.org/r/20250107183454.608451-4-dhowells@redhat.com
cc: Marc Dionne <marc.dionne@auristor.com>
cc: linux-afs@lists.infradead.org
Signed-off-by: Christian Brauner <brauner@kernel.org>
fs/afs/dynroot.c
include/trace/events/afs.h

index f80a4244b9d27424d11e35f02178752960c0b5ea..d8bf52f77d930228bd5def9cac32b80bf4a26d79 100644 (file)
@@ -185,50 +185,6 @@ out:
        return ret == -ENOENT ? NULL : ERR_PTR(ret);
 }
 
-/*
- * Look up @cell in a dynroot directory.  This is a substitution for the
- * local cell name for the net namespace.
- */
-static struct dentry *afs_lookup_atcell(struct dentry *dentry)
-{
-       struct afs_cell *cell;
-       struct afs_net *net = afs_d2net(dentry);
-       struct dentry *ret;
-       char *name;
-       int len;
-
-       if (!net->ws_cell)
-               return ERR_PTR(-ENOENT);
-
-       ret = ERR_PTR(-ENOMEM);
-       name = kmalloc(AFS_MAXCELLNAME + 1, GFP_KERNEL);
-       if (!name)
-               goto out_p;
-
-       down_read(&net->cells_lock);
-       cell = net->ws_cell;
-       if (cell) {
-               len = cell->name_len;
-               memcpy(name, cell->name, len + 1);
-       }
-       up_read(&net->cells_lock);
-
-       ret = ERR_PTR(-ENOENT);
-       if (!cell)
-               goto out_n;
-
-       ret = lookup_one_len(name, dentry->d_parent, len);
-
-       /* We don't want to d_add() the @cell dentry here as we don't want to
-        * the cached dentry to hide changes to the local cell name.
-        */
-
-out_n:
-       kfree(name);
-out_p:
-       return ret;
-}
-
 /*
  * Look up an entry in a dynroot directory.
  */
@@ -247,10 +203,6 @@ static struct dentry *afs_dynroot_lookup(struct inode *dir, struct dentry *dentr
                return ERR_PTR(-ENAMETOOLONG);
        }
 
-       if (dentry->d_name.len == 5 &&
-           memcmp(dentry->d_name.name, "@cell", 5) == 0)
-               return afs_lookup_atcell(dentry);
-
        return d_splice_alias(afs_try_auto_mntpt(dentry, dir), dentry);
 }
 
@@ -343,6 +295,131 @@ void afs_dynroot_rmdir(struct afs_net *net, struct afs_cell *cell)
        _leave("");
 }
 
+static void afs_atcell_delayed_put_cell(void *arg)
+{
+       struct afs_cell *cell = arg;
+
+       afs_put_cell(cell, afs_cell_trace_put_atcell);
+}
+
+/*
+ * Read @cell or .@cell symlinks.
+ */
+static const char *afs_atcell_get_link(struct dentry *dentry, struct inode *inode,
+                                      struct delayed_call *done)
+{
+       struct afs_vnode *vnode = AFS_FS_I(inode);
+       struct afs_cell *cell;
+       struct afs_net *net = afs_i2net(inode);
+       const char *name;
+       bool dotted = vnode->fid.vnode == 3;
+
+       if (!net->ws_cell)
+               return ERR_PTR(-ENOENT);
+
+       down_read(&net->cells_lock);
+
+       cell = net->ws_cell;
+       if (dotted)
+               name = cell->name - 1;
+       else
+               name = cell->name;
+       afs_get_cell(cell, afs_cell_trace_get_atcell);
+       set_delayed_call(done, afs_atcell_delayed_put_cell, cell);
+
+       up_read(&net->cells_lock);
+       return name;
+}
+
+static const struct inode_operations afs_atcell_inode_operations = {
+       .get_link       = afs_atcell_get_link,
+};
+
+/*
+ * Look up @cell or .@cell in a dynroot directory.  This is a substitution for
+ * the local cell name for the net namespace.
+ */
+static struct dentry *afs_dynroot_create_symlink(struct dentry *root, const char *name)
+{
+       struct afs_vnode *vnode;
+       struct afs_fid fid = { .vnode = 2, .unique = 1, };
+       struct dentry *dentry;
+       struct inode *inode;
+
+       if (name[0] == '.')
+               fid.vnode = 3;
+
+       dentry = d_alloc_name(root, name);
+       if (!dentry)
+               return ERR_PTR(-ENOMEM);
+
+       inode = iget5_locked(dentry->d_sb, fid.vnode,
+                            afs_iget5_pseudo_test, afs_iget5_pseudo_set, &fid);
+       if (!inode) {
+               dput(dentry);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       vnode = AFS_FS_I(inode);
+
+       /* there shouldn't be an existing inode */
+       if (WARN_ON_ONCE(!(inode->i_state & I_NEW))) {
+               iput(inode);
+               dput(dentry);
+               return ERR_PTR(-EIO);
+       }
+
+       netfs_inode_init(&vnode->netfs, NULL, false);
+       simple_inode_init_ts(inode);
+       set_nlink(inode, 1);
+       inode->i_size           = 0;
+       inode->i_mode           = S_IFLNK | 0555;
+       inode->i_op             = &afs_atcell_inode_operations;
+       inode->i_uid            = GLOBAL_ROOT_UID;
+       inode->i_gid            = GLOBAL_ROOT_GID;
+       inode->i_blocks         = 0;
+       inode->i_generation     = 0;
+       inode->i_flags          |= S_NOATIME;
+
+       unlock_new_inode(inode);
+       d_splice_alias(inode, dentry);
+       return dentry;
+}
+
+/*
+ * Create @cell and .@cell symlinks.
+ */
+static int afs_dynroot_symlink(struct afs_net *net)
+{
+       struct super_block *sb = net->dynroot_sb;
+       struct dentry *root, *symlink, *dsymlink;
+       int ret;
+
+       /* Let the ->lookup op do the creation */
+       root = sb->s_root;
+       inode_lock(root->d_inode);
+       symlink = afs_dynroot_create_symlink(root, "@cell");
+       if (IS_ERR(symlink)) {
+               ret = PTR_ERR(symlink);
+               goto unlock;
+       }
+
+       dsymlink = afs_dynroot_create_symlink(root, ".@cell");
+       if (IS_ERR(dsymlink)) {
+               ret = PTR_ERR(dsymlink);
+               dput(symlink);
+               goto unlock;
+       }
+
+       /* Note that we're retaining extra refs on the dentries. */
+       symlink->d_fsdata = (void *)1UL;
+       dsymlink->d_fsdata = (void *)1UL;
+       ret = 0;
+unlock:
+       inode_unlock(root->d_inode);
+       return ret;
+}
+
 /*
  * Populate a newly created dynamic root with cell names.
  */
@@ -355,6 +432,10 @@ int afs_dynroot_populate(struct super_block *sb)
        mutex_lock(&net->proc_cells_lock);
 
        net->dynroot_sb = sb;
+       ret = afs_dynroot_symlink(net);
+       if (ret < 0)
+               goto error;
+
        hlist_for_each_entry(cell, &net->proc_cells, proc_link) {
                ret = afs_dynroot_mkdir(net, cell);
                if (ret < 0)
index a0aed1a428a18370140dcd29bebdcfc06ff2a994..de0e2301a03766d598b523e84edfb802d241f086 100644 (file)
@@ -168,12 +168,14 @@ enum yfs_cm_operation {
 #define afs_cell_traces \
        EM(afs_cell_trace_alloc,                "ALLOC     ") \
        EM(afs_cell_trace_free,                 "FREE      ") \
+       EM(afs_cell_trace_get_atcell,           "GET atcell") \
        EM(afs_cell_trace_get_queue_dns,        "GET q-dns ") \
        EM(afs_cell_trace_get_queue_manage,     "GET q-mng ") \
        EM(afs_cell_trace_get_queue_new,        "GET q-new ") \
        EM(afs_cell_trace_get_vol,              "GET vol   ") \
        EM(afs_cell_trace_insert,               "INSERT    ") \
        EM(afs_cell_trace_manage,               "MANAGE    ") \
+       EM(afs_cell_trace_put_atcell,           "PUT atcell") \
        EM(afs_cell_trace_put_candidate,        "PUT candid") \
        EM(afs_cell_trace_put_destroy,          "PUT destry") \
        EM(afs_cell_trace_put_queue_work,       "PUT q-work") \