225 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/* mountpoint management3 *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 dir50 */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 dir61 */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 buf = kmap(page);136 ret = -EINVAL;137 if (buf[size - 1] == '.')138 ret = vfs_parse_fs_string(fc, "source", buf, size - 1);139 kunmap(page);140 put_page(page);141 if (ret < 0)142 return ret;143 144 /* Don't cross a backup volume mountpoint from a backup volume */145 if (src_as->volume && src_as->volume->type == AFSVL_BACKVOL &&146 ctx->type == AFSVL_BACKVOL)147 return -ENODEV;148 }149 150 return 0;151}152 153/*154 * create a vfsmount to be automounted155 */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 else172 mnt = ERR_PTR(ret);173 174 put_fs_context(fc);175 return mnt;176}177 178/*179 * handle an automount point180 */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 off201 */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 running217 */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