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