162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* mountpoint management 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. 562306a36Sopenharmony_ci * Written by David Howells (dhowells@redhat.com) 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/init.h> 1162306a36Sopenharmony_ci#include <linux/fs.h> 1262306a36Sopenharmony_ci#include <linux/pagemap.h> 1362306a36Sopenharmony_ci#include <linux/mount.h> 1462306a36Sopenharmony_ci#include <linux/namei.h> 1562306a36Sopenharmony_ci#include <linux/gfp.h> 1662306a36Sopenharmony_ci#include <linux/fs_context.h> 1762306a36Sopenharmony_ci#include "internal.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistatic struct dentry *afs_mntpt_lookup(struct inode *dir, 2162306a36Sopenharmony_ci struct dentry *dentry, 2262306a36Sopenharmony_ci unsigned int flags); 2362306a36Sopenharmony_cistatic int afs_mntpt_open(struct inode *inode, struct file *file); 2462306a36Sopenharmony_cistatic void afs_mntpt_expiry_timed_out(struct work_struct *work); 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ciconst struct file_operations afs_mntpt_file_operations = { 2762306a36Sopenharmony_ci .open = afs_mntpt_open, 2862306a36Sopenharmony_ci .llseek = noop_llseek, 2962306a36Sopenharmony_ci}; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ciconst struct inode_operations afs_mntpt_inode_operations = { 3262306a36Sopenharmony_ci .lookup = afs_mntpt_lookup, 3362306a36Sopenharmony_ci .readlink = page_readlink, 3462306a36Sopenharmony_ci .getattr = afs_getattr, 3562306a36Sopenharmony_ci}; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ciconst struct inode_operations afs_autocell_inode_operations = { 3862306a36Sopenharmony_ci .getattr = afs_getattr, 3962306a36Sopenharmony_ci}; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic LIST_HEAD(afs_vfsmounts); 4262306a36Sopenharmony_cistatic DECLARE_DELAYED_WORK(afs_mntpt_expiry_timer, afs_mntpt_expiry_timed_out); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic unsigned long afs_mntpt_expiry_timeout = 10 * 60; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic const char afs_root_volume[] = "root.cell"; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci/* 4962306a36Sopenharmony_ci * no valid lookup procedure on this sort of dir 5062306a36Sopenharmony_ci */ 5162306a36Sopenharmony_cistatic struct dentry *afs_mntpt_lookup(struct inode *dir, 5262306a36Sopenharmony_ci struct dentry *dentry, 5362306a36Sopenharmony_ci unsigned int flags) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci _enter("%p,%p{%pd2}", dir, dentry, dentry); 5662306a36Sopenharmony_ci return ERR_PTR(-EREMOTE); 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci/* 6062306a36Sopenharmony_ci * no valid open procedure on this sort of dir 6162306a36Sopenharmony_ci */ 6262306a36Sopenharmony_cistatic int afs_mntpt_open(struct inode *inode, struct file *file) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci _enter("%p,%p{%pD2}", inode, file, file); 6562306a36Sopenharmony_ci return -EREMOTE; 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci/* 6962306a36Sopenharmony_ci * Set the parameters for the proposed superblock. 7062306a36Sopenharmony_ci */ 7162306a36Sopenharmony_cistatic int afs_mntpt_set_params(struct fs_context *fc, struct dentry *mntpt) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci struct afs_fs_context *ctx = fc->fs_private; 7462306a36Sopenharmony_ci struct afs_super_info *src_as = AFS_FS_S(mntpt->d_sb); 7562306a36Sopenharmony_ci struct afs_vnode *vnode = AFS_FS_I(d_inode(mntpt)); 7662306a36Sopenharmony_ci struct afs_cell *cell; 7762306a36Sopenharmony_ci const char *p; 7862306a36Sopenharmony_ci int ret; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci if (fc->net_ns != src_as->net_ns) { 8162306a36Sopenharmony_ci put_net(fc->net_ns); 8262306a36Sopenharmony_ci fc->net_ns = get_net(src_as->net_ns); 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci if (src_as->volume && src_as->volume->type == AFSVL_RWVOL) { 8662306a36Sopenharmony_ci ctx->type = AFSVL_RWVOL; 8762306a36Sopenharmony_ci ctx->force = true; 8862306a36Sopenharmony_ci } 8962306a36Sopenharmony_ci if (ctx->cell) { 9062306a36Sopenharmony_ci afs_unuse_cell(ctx->net, ctx->cell, afs_cell_trace_unuse_mntpt); 9162306a36Sopenharmony_ci ctx->cell = NULL; 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci if (test_bit(AFS_VNODE_PSEUDODIR, &vnode->flags)) { 9462306a36Sopenharmony_ci /* if the directory is a pseudo directory, use the d_name */ 9562306a36Sopenharmony_ci unsigned size = mntpt->d_name.len; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci if (size < 2) 9862306a36Sopenharmony_ci return -ENOENT; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci p = mntpt->d_name.name; 10162306a36Sopenharmony_ci if (mntpt->d_name.name[0] == '.') { 10262306a36Sopenharmony_ci size--; 10362306a36Sopenharmony_ci p++; 10462306a36Sopenharmony_ci ctx->type = AFSVL_RWVOL; 10562306a36Sopenharmony_ci ctx->force = true; 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci if (size > AFS_MAXCELLNAME) 10862306a36Sopenharmony_ci return -ENAMETOOLONG; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci cell = afs_lookup_cell(ctx->net, p, size, NULL, false); 11162306a36Sopenharmony_ci if (IS_ERR(cell)) { 11262306a36Sopenharmony_ci pr_err("kAFS: unable to lookup cell '%pd'\n", mntpt); 11362306a36Sopenharmony_ci return PTR_ERR(cell); 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci ctx->cell = cell; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci ctx->volname = afs_root_volume; 11862306a36Sopenharmony_ci ctx->volnamesz = sizeof(afs_root_volume) - 1; 11962306a36Sopenharmony_ci } else { 12062306a36Sopenharmony_ci /* read the contents of the AFS special symlink */ 12162306a36Sopenharmony_ci struct page *page; 12262306a36Sopenharmony_ci loff_t size = i_size_read(d_inode(mntpt)); 12362306a36Sopenharmony_ci char *buf; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci if (src_as->cell) 12662306a36Sopenharmony_ci ctx->cell = afs_use_cell(src_as->cell, afs_cell_trace_use_mntpt); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci if (size < 2 || size > PAGE_SIZE - 1) 12962306a36Sopenharmony_ci return -EINVAL; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci page = read_mapping_page(d_inode(mntpt)->i_mapping, 0, NULL); 13262306a36Sopenharmony_ci if (IS_ERR(page)) 13362306a36Sopenharmony_ci return PTR_ERR(page); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci buf = kmap(page); 13662306a36Sopenharmony_ci ret = -EINVAL; 13762306a36Sopenharmony_ci if (buf[size - 1] == '.') 13862306a36Sopenharmony_ci ret = vfs_parse_fs_string(fc, "source", buf, size - 1); 13962306a36Sopenharmony_ci kunmap(page); 14062306a36Sopenharmony_ci put_page(page); 14162306a36Sopenharmony_ci if (ret < 0) 14262306a36Sopenharmony_ci return ret; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci return 0; 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci/* 14962306a36Sopenharmony_ci * create a vfsmount to be automounted 15062306a36Sopenharmony_ci */ 15162306a36Sopenharmony_cistatic struct vfsmount *afs_mntpt_do_automount(struct dentry *mntpt) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci struct fs_context *fc; 15462306a36Sopenharmony_ci struct vfsmount *mnt; 15562306a36Sopenharmony_ci int ret; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci BUG_ON(!d_inode(mntpt)); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci fc = fs_context_for_submount(&afs_fs_type, mntpt); 16062306a36Sopenharmony_ci if (IS_ERR(fc)) 16162306a36Sopenharmony_ci return ERR_CAST(fc); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci ret = afs_mntpt_set_params(fc, mntpt); 16462306a36Sopenharmony_ci if (!ret) 16562306a36Sopenharmony_ci mnt = fc_mount(fc); 16662306a36Sopenharmony_ci else 16762306a36Sopenharmony_ci mnt = ERR_PTR(ret); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci put_fs_context(fc); 17062306a36Sopenharmony_ci return mnt; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci/* 17462306a36Sopenharmony_ci * handle an automount point 17562306a36Sopenharmony_ci */ 17662306a36Sopenharmony_cistruct vfsmount *afs_d_automount(struct path *path) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci struct vfsmount *newmnt; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci _enter("{%pd}", path->dentry); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci newmnt = afs_mntpt_do_automount(path->dentry); 18362306a36Sopenharmony_ci if (IS_ERR(newmnt)) 18462306a36Sopenharmony_ci return newmnt; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci mntget(newmnt); /* prevent immediate expiration */ 18762306a36Sopenharmony_ci mnt_set_expiry(newmnt, &afs_vfsmounts); 18862306a36Sopenharmony_ci queue_delayed_work(afs_wq, &afs_mntpt_expiry_timer, 18962306a36Sopenharmony_ci afs_mntpt_expiry_timeout * HZ); 19062306a36Sopenharmony_ci _leave(" = %p", newmnt); 19162306a36Sopenharmony_ci return newmnt; 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci/* 19562306a36Sopenharmony_ci * handle mountpoint expiry timer going off 19662306a36Sopenharmony_ci */ 19762306a36Sopenharmony_cistatic void afs_mntpt_expiry_timed_out(struct work_struct *work) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci _enter(""); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci if (!list_empty(&afs_vfsmounts)) { 20262306a36Sopenharmony_ci mark_mounts_for_expiry(&afs_vfsmounts); 20362306a36Sopenharmony_ci queue_delayed_work(afs_wq, &afs_mntpt_expiry_timer, 20462306a36Sopenharmony_ci afs_mntpt_expiry_timeout * HZ); 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci _leave(""); 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci/* 21162306a36Sopenharmony_ci * kill the AFS mountpoint timer if it's still running 21262306a36Sopenharmony_ci */ 21362306a36Sopenharmony_civoid afs_mntpt_kill_timer(void) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci _enter(""); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci ASSERT(list_empty(&afs_vfsmounts)); 21862306a36Sopenharmony_ci cancel_delayed_work_sync(&afs_mntpt_expiry_timer); 21962306a36Sopenharmony_ci} 220