linux/fs/afs/dynroot.c
<<
>>
Prefs
   1/* AFS dynamic root handling
   2 *
   3 * Copyright (C) 2018 Red Hat, Inc. All Rights Reserved.
   4 * Written by David Howells (dhowells@redhat.com)
   5 *
   6 * This program is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU General Public Licence
   8 * as published by the Free Software Foundation; either version
   9 * 2 of the Licence, or (at your option) any later version.
  10 */
  11
  12#include <linux/fs.h>
  13#include <linux/namei.h>
  14#include <linux/dns_resolver.h>
  15#include "internal.h"
  16
  17const struct file_operations afs_dynroot_file_operations = {
  18        .open           = dcache_dir_open,
  19        .release        = dcache_dir_close,
  20        .iterate_shared = dcache_readdir,
  21        .llseek         = dcache_dir_lseek,
  22};
  23
  24/*
  25 * Probe to see if a cell may exist.  This prevents positive dentries from
  26 * being created unnecessarily.
  27 */
  28static int afs_probe_cell_name(struct dentry *dentry)
  29{
  30        struct afs_cell *cell;
  31        const char *name = dentry->d_name.name;
  32        size_t len = dentry->d_name.len;
  33        int ret;
  34
  35        /* Names prefixed with a dot are R/W mounts. */
  36        if (name[0] == '.') {
  37                if (len == 1)
  38                        return -EINVAL;
  39                name++;
  40                len--;
  41        }
  42
  43        cell = afs_lookup_cell_rcu(afs_d2net(dentry), name, len);
  44        if (!IS_ERR(cell)) {
  45                afs_put_cell(afs_d2net(dentry), cell);
  46                return 0;
  47        }
  48
  49        ret = dns_query("afsdb", name, len, "", NULL, NULL);
  50        if (ret == -ENODATA)
  51                ret = -EDESTADDRREQ;
  52        return ret;
  53}
  54
  55/*
  56 * Try to auto mount the mountpoint with pseudo directory, if the autocell
  57 * operation is setted.
  58 */
  59struct inode *afs_try_auto_mntpt(struct dentry *dentry, struct inode *dir)
  60{
  61        struct afs_vnode *vnode = AFS_FS_I(dir);
  62        struct inode *inode;
  63        int ret = -ENOENT;
  64
  65        _enter("%p{%pd}, {%x:%u}",
  66               dentry, dentry, vnode->fid.vid, vnode->fid.vnode);
  67
  68        if (!test_bit(AFS_VNODE_AUTOCELL, &vnode->flags))
  69                goto out;
  70
  71        ret = afs_probe_cell_name(dentry);
  72        if (ret < 0)
  73                goto out;
  74
  75        inode = afs_iget_pseudo_dir(dir->i_sb, false);
  76        if (IS_ERR(inode)) {
  77                ret = PTR_ERR(inode);
  78                goto out;
  79        }
  80
  81        _leave("= %p", inode);
  82        return inode;
  83
  84out:
  85        _leave("= %d", ret);
  86        return ret == -ENOENT ? NULL : ERR_PTR(ret);
  87}
  88
  89/*
  90 * Look up @cell in a dynroot directory.  This is a substitution for the
  91 * local cell name for the net namespace.
  92 */
  93static struct dentry *afs_lookup_atcell(struct dentry *dentry)
  94{
  95        struct afs_cell *cell;
  96        struct afs_net *net = afs_d2net(dentry);
  97        struct dentry *ret;
  98        unsigned int seq = 0;
  99        char *name;
 100        int len;
 101
 102        if (!net->ws_cell)
 103                return ERR_PTR(-ENOENT);
 104
 105        ret = ERR_PTR(-ENOMEM);
 106        name = kmalloc(AFS_MAXCELLNAME + 1, GFP_KERNEL);
 107        if (!name)
 108                goto out_p;
 109
 110        rcu_read_lock();
 111        do {
 112                read_seqbegin_or_lock(&net->cells_lock, &seq);
 113                cell = rcu_dereference_raw(net->ws_cell);
 114                if (cell) {
 115                        len = cell->name_len;
 116                        memcpy(name, cell->name, len + 1);
 117                }
 118        } while (need_seqretry(&net->cells_lock, seq));
 119        done_seqretry(&net->cells_lock, seq);
 120        rcu_read_unlock();
 121
 122        ret = ERR_PTR(-ENOENT);
 123        if (!cell)
 124                goto out_n;
 125
 126        ret = lookup_one_len(name, dentry->d_parent, len);
 127
 128        /* We don't want to d_add() the @cell dentry here as we don't want to
 129         * the cached dentry to hide changes to the local cell name.
 130         */
 131
 132out_n:
 133        kfree(name);
 134out_p:
 135        return ret;
 136}
 137
 138/*
 139 * Look up an entry in a dynroot directory.
 140 */
 141static struct dentry *afs_dynroot_lookup(struct inode *dir, struct dentry *dentry,
 142                                         unsigned int flags)
 143{
 144        _enter("%pd", dentry);
 145
 146        ASSERTCMP(d_inode(dentry), ==, NULL);
 147
 148        if (dentry->d_name.len >= AFSNAMEMAX) {
 149                _leave(" = -ENAMETOOLONG");
 150                return ERR_PTR(-ENAMETOOLONG);
 151        }
 152
 153        if (dentry->d_name.len == 5 &&
 154            memcmp(dentry->d_name.name, "@cell", 5) == 0)
 155                return afs_lookup_atcell(dentry);
 156
 157        return d_splice_alias(afs_try_auto_mntpt(dentry, dir), dentry);
 158}
 159
 160const struct inode_operations afs_dynroot_inode_operations = {
 161        .lookup         = afs_dynroot_lookup,
 162};
 163
 164/*
 165 * Dirs in the dynamic root don't need revalidation.
 166 */
 167static int afs_dynroot_d_revalidate(struct dentry *dentry, unsigned int flags)
 168{
 169        return 1;
 170}
 171
 172/*
 173 * Allow the VFS to enquire as to whether a dentry should be unhashed (mustn't
 174 * sleep)
 175 * - called from dput() when d_count is going to 0.
 176 * - return 1 to request dentry be unhashed, 0 otherwise
 177 */
 178static int afs_dynroot_d_delete(const struct dentry *dentry)
 179{
 180        return d_really_is_positive(dentry);
 181}
 182
 183const struct dentry_operations afs_dynroot_dentry_operations = {
 184        .d_revalidate   = afs_dynroot_d_revalidate,
 185        .d_delete       = afs_dynroot_d_delete,
 186        .d_release      = afs_d_release,
 187        .d_automount    = afs_d_automount,
 188};
 189
 190/*
 191 * Create a manually added cell mount directory.
 192 * - The caller must hold net->proc_cells_lock
 193 */
 194int afs_dynroot_mkdir(struct afs_net *net, struct afs_cell *cell)
 195{
 196        struct super_block *sb = net->dynroot_sb;
 197        struct dentry *root, *subdir;
 198        int ret;
 199
 200        if (!sb || atomic_read(&sb->s_active) == 0)
 201                return 0;
 202
 203        /* Let the ->lookup op do the creation */
 204        root = sb->s_root;
 205        inode_lock(root->d_inode);
 206        subdir = lookup_one_len(cell->name, root, cell->name_len);
 207        if (IS_ERR(subdir)) {
 208                ret = PTR_ERR(subdir);
 209                goto unlock;
 210        }
 211
 212        /* Note that we're retaining an extra ref on the dentry */
 213        subdir->d_fsdata = (void *)1UL;
 214        ret = 0;
 215unlock:
 216        inode_unlock(root->d_inode);
 217        return ret;
 218}
 219
 220/*
 221 * Remove a manually added cell mount directory.
 222 * - The caller must hold net->proc_cells_lock
 223 */
 224void afs_dynroot_rmdir(struct afs_net *net, struct afs_cell *cell)
 225{
 226        struct super_block *sb = net->dynroot_sb;
 227        struct dentry *root, *subdir;
 228
 229        if (!sb || atomic_read(&sb->s_active) == 0)
 230                return;
 231
 232        root = sb->s_root;
 233        inode_lock(root->d_inode);
 234
 235        /* Don't want to trigger a lookup call, which will re-add the cell */
 236        subdir = try_lookup_one_len(cell->name, root, cell->name_len);
 237        if (IS_ERR_OR_NULL(subdir)) {
 238                _debug("lookup %ld", PTR_ERR(subdir));
 239                goto no_dentry;
 240        }
 241
 242        _debug("rmdir %pd %u", subdir, d_count(subdir));
 243
 244        if (subdir->d_fsdata) {
 245                _debug("unpin %u", d_count(subdir));
 246                subdir->d_fsdata = NULL;
 247                dput(subdir);
 248        }
 249        dput(subdir);
 250no_dentry:
 251        inode_unlock(root->d_inode);
 252        _leave("");
 253}
 254
 255/*
 256 * Populate a newly created dynamic root with cell names.
 257 */
 258int afs_dynroot_populate(struct super_block *sb)
 259{
 260        struct afs_cell *cell;
 261        struct afs_net *net = afs_sb2net(sb);
 262        int ret;
 263
 264        if (mutex_lock_interruptible(&net->proc_cells_lock) < 0)
 265                return -ERESTARTSYS;
 266
 267        net->dynroot_sb = sb;
 268        hlist_for_each_entry(cell, &net->proc_cells, proc_link) {
 269                ret = afs_dynroot_mkdir(net, cell);
 270                if (ret < 0)
 271                        goto error;
 272        }
 273
 274        ret = 0;
 275out:
 276        mutex_unlock(&net->proc_cells_lock);
 277        return ret;
 278
 279error:
 280        net->dynroot_sb = NULL;
 281        goto out;
 282}
 283
 284/*
 285 * When a dynamic root that's in the process of being destroyed, depopulate it
 286 * of pinned directories.
 287 */
 288void afs_dynroot_depopulate(struct super_block *sb)
 289{
 290        struct afs_net *net = afs_sb2net(sb);
 291        struct dentry *root = sb->s_root, *subdir, *tmp;
 292
 293        /* Prevent more subdirs from being created */
 294        mutex_lock(&net->proc_cells_lock);
 295        if (net->dynroot_sb == sb)
 296                net->dynroot_sb = NULL;
 297        mutex_unlock(&net->proc_cells_lock);
 298
 299        inode_lock(root->d_inode);
 300
 301        /* Remove all the pins for dirs created for manually added cells */
 302        list_for_each_entry_safe(subdir, tmp, &root->d_subdirs, d_child) {
 303                if (subdir->d_fsdata) {
 304                        subdir->d_fsdata = NULL;
 305                        dput(subdir);
 306                }
 307        }
 308
 309        inode_unlock(root->d_inode);
 310}
 311