Merge branches 'work.misc' and 'work.dcache' of git://git.kernel.org/pub/scm/linux...
[sfrench/cifs-2.6.git] / fs / afs / dynroot.c
index 983f3946ab576927850bd62e84308cf565de456f..1cde710a80133bd4c9e2e88f48b1236fd1034992 100644 (file)
@@ -1,4 +1,4 @@
-/* dir.c: AFS dynamic root handling
+/* AFS dynamic root handling
  *
  * Copyright (C) 2018 Red Hat, Inc. All Rights Reserved.
  * Written by David Howells (dhowells@redhat.com)
@@ -46,7 +46,7 @@ static int afs_probe_cell_name(struct dentry *dentry)
                return 0;
        }
 
-       ret = dns_query("afsdb", name, len, "ipv4", NULL, NULL);
+       ret = dns_query("afsdb", name, len, "", NULL, NULL);
        if (ret == -ENODATA)
                ret = -EDESTADDRREQ;
        return ret;
@@ -83,7 +83,7 @@ struct inode *afs_try_auto_mntpt(struct dentry *dentry, struct inode *dir)
 
 out:
        _leave("= %d", ret);
-       return ERR_PTR(ret);
+       return ret == -ENOENT ? NULL : ERR_PTR(ret);
 }
 
 /*
@@ -141,12 +141,6 @@ out_p:
 static struct dentry *afs_dynroot_lookup(struct inode *dir, struct dentry *dentry,
                                         unsigned int flags)
 {
-       struct afs_vnode *vnode;
-       struct inode *inode;
-       int ret;
-
-       vnode = AFS_FS_I(dir);
-
        _enter("%pd", dentry);
 
        ASSERTCMP(d_inode(dentry), ==, NULL);
@@ -160,22 +154,7 @@ static struct dentry *afs_dynroot_lookup(struct inode *dir, struct dentry *dentr
            memcmp(dentry->d_name.name, "@cell", 5) == 0)
                return afs_lookup_atcell(dentry);
 
-       inode = afs_try_auto_mntpt(dentry, dir);
-       if (IS_ERR(inode)) {
-               ret = PTR_ERR(inode);
-               if (ret == -ENOENT) {
-                       d_add(dentry, NULL);
-                       _leave(" = NULL [negative]");
-                       return NULL;
-               }
-               _leave(" = %d [do]", ret);
-               return ERR_PTR(ret);
-       }
-
-       d_add(dentry, inode);
-       _leave(" = 0 { ino=%lu v=%u }",
-              d_inode(dentry)->i_ino, d_inode(dentry)->i_generation);
-       return NULL;
+       return d_splice_alias(afs_try_auto_mntpt(dentry, dir), dentry);
 }
 
 const struct inode_operations afs_dynroot_inode_operations = {
@@ -207,3 +186,125 @@ const struct dentry_operations afs_dynroot_dentry_operations = {
        .d_release      = afs_d_release,
        .d_automount    = afs_d_automount,
 };
+
+/*
+ * Create a manually added cell mount directory.
+ * - The caller must hold net->proc_cells_lock
+ */
+int afs_dynroot_mkdir(struct afs_net *net, struct afs_cell *cell)
+{
+       struct super_block *sb = net->dynroot_sb;
+       struct dentry *root, *subdir;
+       int ret;
+
+       if (!sb || atomic_read(&sb->s_active) == 0)
+               return 0;
+
+       /* Let the ->lookup op do the creation */
+       root = sb->s_root;
+       inode_lock(root->d_inode);
+       subdir = lookup_one_len(cell->name, root, cell->name_len);
+       if (IS_ERR(subdir)) {
+               ret = PTR_ERR(subdir);
+               goto unlock;
+       }
+
+       /* Note that we're retaining an extra ref on the dentry */
+       subdir->d_fsdata = (void *)1UL;
+       ret = 0;
+unlock:
+       inode_unlock(root->d_inode);
+       return ret;
+}
+
+/*
+ * Remove a manually added cell mount directory.
+ * - The caller must hold net->proc_cells_lock
+ */
+void afs_dynroot_rmdir(struct afs_net *net, struct afs_cell *cell)
+{
+       struct super_block *sb = net->dynroot_sb;
+       struct dentry *root, *subdir;
+
+       if (!sb || atomic_read(&sb->s_active) == 0)
+               return;
+
+       root = sb->s_root;
+       inode_lock(root->d_inode);
+
+       /* Don't want to trigger a lookup call, which will re-add the cell */
+       subdir = try_lookup_one_len(cell->name, root, cell->name_len);
+       if (IS_ERR_OR_NULL(subdir)) {
+               _debug("lookup %ld", PTR_ERR(subdir));
+               goto no_dentry;
+       }
+
+       _debug("rmdir %pd %u", subdir, d_count(subdir));
+
+       if (subdir->d_fsdata) {
+               _debug("unpin %u", d_count(subdir));
+               subdir->d_fsdata = NULL;
+               dput(subdir);
+       }
+       dput(subdir);
+no_dentry:
+       inode_unlock(root->d_inode);
+       _leave("");
+}
+
+/*
+ * Populate a newly created dynamic root with cell names.
+ */
+int afs_dynroot_populate(struct super_block *sb)
+{
+       struct afs_cell *cell;
+       struct afs_net *net = afs_sb2net(sb);
+       int ret;
+
+       if (mutex_lock_interruptible(&net->proc_cells_lock) < 0)
+               return -ERESTARTSYS;
+
+       net->dynroot_sb = sb;
+       list_for_each_entry(cell, &net->proc_cells, proc_link) {
+               ret = afs_dynroot_mkdir(net, cell);
+               if (ret < 0)
+                       goto error;
+       }
+
+       ret = 0;
+out:
+       mutex_unlock(&net->proc_cells_lock);
+       return ret;
+
+error:
+       net->dynroot_sb = NULL;
+       goto out;
+}
+
+/*
+ * When a dynamic root that's in the process of being destroyed, depopulate it
+ * of pinned directories.
+ */
+void afs_dynroot_depopulate(struct super_block *sb)
+{
+       struct afs_net *net = afs_sb2net(sb);
+       struct dentry *root = sb->s_root, *subdir, *tmp;
+
+       /* Prevent more subdirs from being created */
+       mutex_lock(&net->proc_cells_lock);
+       if (net->dynroot_sb == sb)
+               net->dynroot_sb = NULL;
+       mutex_unlock(&net->proc_cells_lock);
+
+       inode_lock(root->d_inode);
+
+       /* Remove all the pins for dirs created for manually added cells */
+       list_for_each_entry_safe(subdir, tmp, &root->d_subdirs, d_child) {
+               if (subdir->d_fsdata) {
+                       subdir->d_fsdata = NULL;
+                       dput(subdir);
+               }
+       }
+
+       inode_unlock(root->d_inode);
+}