linux/fs/afs/mntpt.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/* mountpoint management
   3 *
   4 * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
   5 * Written by David Howells (dhowells@redhat.com)
   6 */
   7
   8#include <linux/kernel.h>
   9#include <linux/module.h>
  10#include <linux/init.h>
  11#include <linux/fs.h>
  12#include <linux/pagemap.h>
  13#include <linux/mount.h>
  14#include <linux/namei.h>
  15#include <linux/gfp.h>
  16#include <linux/fs_context.h>
  17#include "internal.h"
  18
  19
  20static struct dentry *afs_mntpt_lookup(struct inode *dir,
  21                                       struct dentry *dentry,
  22                                       unsigned int flags);
  23static int afs_mntpt_open(struct inode *inode, struct file *file);
  24static void afs_mntpt_expiry_timed_out(struct work_struct *work);
  25
  26const struct file_operations afs_mntpt_file_operations = {
  27        .open           = afs_mntpt_open,
  28        .llseek         = noop_llseek,
  29};
  30
  31const struct inode_operations afs_mntpt_inode_operations = {
  32        .lookup         = afs_mntpt_lookup,
  33        .readlink       = page_readlink,
  34        .getattr        = afs_getattr,
  35};
  36
  37const struct inode_operations afs_autocell_inode_operations = {
  38        .getattr        = afs_getattr,
  39};
  40
  41static LIST_HEAD(afs_vfsmounts);
  42static DECLARE_DELAYED_WORK(afs_mntpt_expiry_timer, afs_mntpt_expiry_timed_out);
  43
  44static unsigned long afs_mntpt_expiry_timeout = 10 * 60;
  45
  46static const char afs_root_volume[] = "root.cell";
  47
  48/*
  49 * no valid lookup procedure on this sort of dir
  50 */
  51static struct dentry *afs_mntpt_lookup(struct inode *dir,
  52                                       struct dentry *dentry,
  53                                       unsigned int flags)
  54{
  55        _enter("%p,%p{%pd2}", dir, dentry, dentry);
  56        return ERR_PTR(-EREMOTE);
  57}
  58
  59/*
  60 * no valid open procedure on this sort of dir
  61 */
  62static int afs_mntpt_open(struct inode *inode, struct file *file)
  63{
  64        _enter("%p,%p{%pD2}", inode, file, file);
  65        return -EREMOTE;
  66}
  67
  68/*
  69 * Set the parameters for the proposed superblock.
  70 */
  71static int afs_mntpt_set_params(struct fs_context *fc, struct dentry *mntpt)
  72{
  73        struct afs_fs_context *ctx = fc->fs_private;
  74        struct afs_super_info *src_as = AFS_FS_S(mntpt->d_sb);
  75        struct afs_vnode *vnode = AFS_FS_I(d_inode(mntpt));
  76        struct afs_cell *cell;
  77        const char *p;
  78        int ret;
  79
  80        if (fc->net_ns != src_as->net_ns) {
  81                put_net(fc->net_ns);
  82                fc->net_ns = get_net(src_as->net_ns);
  83        }
  84
  85        if (src_as->volume && src_as->volume->type == AFSVL_RWVOL) {
  86                ctx->type = AFSVL_RWVOL;
  87                ctx->force = true;
  88        }
  89        if (ctx->cell) {
  90                afs_unuse_cell(ctx->net, ctx->cell, afs_cell_trace_unuse_mntpt);
  91                ctx->cell = NULL;
  92        }
  93        if (test_bit(AFS_VNODE_PSEUDODIR, &vnode->flags)) {
  94                /* if the directory is a pseudo directory, use the d_name */
  95                unsigned size = mntpt->d_name.len;
  96
  97                if (size < 2)
  98                        return -ENOENT;
  99
 100                p = mntpt->d_name.name;
 101                if (mntpt->d_name.name[0] == '.') {
 102                        size--;
 103                        p++;
 104                        ctx->type = AFSVL_RWVOL;
 105                        ctx->force = true;
 106                }
 107                if (size > AFS_MAXCELLNAME)
 108                        return -ENAMETOOLONG;
 109
 110                cell = afs_lookup_cell(ctx->net, p, size, NULL, false);
 111                if (IS_ERR(cell)) {
 112                        pr_err("kAFS: unable to lookup cell '%pd'\n", mntpt);
 113                        return PTR_ERR(cell);
 114                }
 115                ctx->cell = cell;
 116
 117                ctx->volname = afs_root_volume;
 118                ctx->volnamesz = sizeof(afs_root_volume) - 1;
 119        } else {
 120                /* read the contents of the AFS special symlink */
 121                struct page *page;
 122                loff_t size = i_size_read(d_inode(mntpt));
 123                char *buf;
 124
 125                if (src_as->cell)
 126                        ctx->cell = afs_use_cell(src_as->cell, afs_cell_trace_use_mntpt);
 127
 128                if (size < 2 || size > PAGE_SIZE - 1)
 129                        return -EINVAL;
 130
 131                page = read_mapping_page(d_inode(mntpt)->i_mapping, 0, NULL);
 132                if (IS_ERR(page))
 133                        return PTR_ERR(page);
 134
 135                if (PageError(page)) {
 136                        ret = afs_bad(AFS_FS_I(d_inode(mntpt)), afs_file_error_mntpt);
 137                        put_page(page);
 138                        return ret;
 139                }
 140
 141                buf = kmap(page);
 142                ret = -EINVAL;
 143                if (buf[size - 1] == '.')
 144                        ret = vfs_parse_fs_string(fc, "source", buf, size - 1);
 145                kunmap(page);
 146                put_page(page);
 147                if (ret < 0)
 148                        return ret;
 149        }
 150
 151        return 0;
 152}
 153
 154/*
 155 * create a vfsmount to be automounted
 156 */
 157static struct vfsmount *afs_mntpt_do_automount(struct dentry *mntpt)
 158{
 159        struct fs_context *fc;
 160        struct vfsmount *mnt;
 161        int ret;
 162
 163        BUG_ON(!d_inode(mntpt));
 164
 165        fc = fs_context_for_submount(&afs_fs_type, mntpt);
 166        if (IS_ERR(fc))
 167                return ERR_CAST(fc);
 168
 169        ret = afs_mntpt_set_params(fc, mntpt);
 170        if (!ret)
 171                mnt = fc_mount(fc);
 172        else
 173                mnt = ERR_PTR(ret);
 174
 175        put_fs_context(fc);
 176        return mnt;
 177}
 178
 179/*
 180 * handle an automount point
 181 */
 182struct vfsmount *afs_d_automount(struct path *path)
 183{
 184        struct vfsmount *newmnt;
 185
 186        _enter("{%pd}", path->dentry);
 187
 188        newmnt = afs_mntpt_do_automount(path->dentry);
 189        if (IS_ERR(newmnt))
 190                return newmnt;
 191
 192        mntget(newmnt); /* prevent immediate expiration */
 193        mnt_set_expiry(newmnt, &afs_vfsmounts);
 194        queue_delayed_work(afs_wq, &afs_mntpt_expiry_timer,
 195                           afs_mntpt_expiry_timeout * HZ);
 196        _leave(" = %p", newmnt);
 197        return newmnt;
 198}
 199
 200/*
 201 * handle mountpoint expiry timer going off
 202 */
 203static void afs_mntpt_expiry_timed_out(struct work_struct *work)
 204{
 205        _enter("");
 206
 207        if (!list_empty(&afs_vfsmounts)) {
 208                mark_mounts_for_expiry(&afs_vfsmounts);
 209                queue_delayed_work(afs_wq, &afs_mntpt_expiry_timer,
 210                                   afs_mntpt_expiry_timeout * HZ);
 211        }
 212
 213        _leave("");
 214}
 215
 216/*
 217 * kill the AFS mountpoint timer if it's still running
 218 */
 219void afs_mntpt_kill_timer(void)
 220{
 221        _enter("");
 222
 223        ASSERT(list_empty(&afs_vfsmounts));
 224        cancel_delayed_work_sync(&afs_mntpt_expiry_timer);
 225}
 226