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 > 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 = vfs_parse_fs_string(fc, "source", buf, size);
 144                kunmap(page);
 145                put_page(page);
 146                if (ret < 0)
 147                        return ret;
 148        }
 149
 150        return 0;
 151}
 152
 153/*
 154 * create a vfsmount to be automounted
 155 */
 156static struct vfsmount *afs_mntpt_do_automount(struct dentry *mntpt)
 157{
 158        struct fs_context *fc;
 159        struct vfsmount *mnt;
 160        int ret;
 161
 162        BUG_ON(!d_inode(mntpt));
 163
 164        fc = fs_context_for_submount(&afs_fs_type, mntpt);
 165        if (IS_ERR(fc))
 166                return ERR_CAST(fc);
 167
 168        ret = afs_mntpt_set_params(fc, mntpt);
 169        if (!ret)
 170                mnt = fc_mount(fc);
 171        else
 172                mnt = ERR_PTR(ret);
 173
 174        put_fs_context(fc);
 175        return mnt;
 176}
 177
 178/*
 179 * handle an automount point
 180 */
 181struct vfsmount *afs_d_automount(struct path *path)
 182{
 183        struct vfsmount *newmnt;
 184
 185        _enter("{%pd}", path->dentry);
 186
 187        newmnt = afs_mntpt_do_automount(path->dentry);
 188        if (IS_ERR(newmnt))
 189                return newmnt;
 190
 191        mntget(newmnt); /* prevent immediate expiration */
 192        mnt_set_expiry(newmnt, &afs_vfsmounts);
 193        queue_delayed_work(afs_wq, &afs_mntpt_expiry_timer,
 194                           afs_mntpt_expiry_timeout * HZ);
 195        _leave(" = %p", newmnt);
 196        return newmnt;
 197}
 198
 199/*
 200 * handle mountpoint expiry timer going off
 201 */
 202static void afs_mntpt_expiry_timed_out(struct work_struct *work)
 203{
 204        _enter("");
 205
 206        if (!list_empty(&afs_vfsmounts)) {
 207                mark_mounts_for_expiry(&afs_vfsmounts);
 208                queue_delayed_work(afs_wq, &afs_mntpt_expiry_timer,
 209                                   afs_mntpt_expiry_timeout * HZ);
 210        }
 211
 212        _leave("");
 213}
 214
 215/*
 216 * kill the AFS mountpoint timer if it's still running
 217 */
 218void afs_mntpt_kill_timer(void)
 219{
 220        _enter("");
 221
 222        ASSERT(list_empty(&afs_vfsmounts));
 223        cancel_delayed_work_sync(&afs_mntpt_expiry_timer);
 224}
 225