18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* mountpoint management 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. 58c2ecf20Sopenharmony_ci * Written by David Howells (dhowells@redhat.com) 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/kernel.h> 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/init.h> 118c2ecf20Sopenharmony_ci#include <linux/fs.h> 128c2ecf20Sopenharmony_ci#include <linux/pagemap.h> 138c2ecf20Sopenharmony_ci#include <linux/mount.h> 148c2ecf20Sopenharmony_ci#include <linux/namei.h> 158c2ecf20Sopenharmony_ci#include <linux/gfp.h> 168c2ecf20Sopenharmony_ci#include <linux/fs_context.h> 178c2ecf20Sopenharmony_ci#include "internal.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic struct dentry *afs_mntpt_lookup(struct inode *dir, 218c2ecf20Sopenharmony_ci struct dentry *dentry, 228c2ecf20Sopenharmony_ci unsigned int flags); 238c2ecf20Sopenharmony_cistatic int afs_mntpt_open(struct inode *inode, struct file *file); 248c2ecf20Sopenharmony_cistatic void afs_mntpt_expiry_timed_out(struct work_struct *work); 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ciconst struct file_operations afs_mntpt_file_operations = { 278c2ecf20Sopenharmony_ci .open = afs_mntpt_open, 288c2ecf20Sopenharmony_ci .llseek = noop_llseek, 298c2ecf20Sopenharmony_ci}; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ciconst struct inode_operations afs_mntpt_inode_operations = { 328c2ecf20Sopenharmony_ci .lookup = afs_mntpt_lookup, 338c2ecf20Sopenharmony_ci .readlink = page_readlink, 348c2ecf20Sopenharmony_ci .getattr = afs_getattr, 358c2ecf20Sopenharmony_ci}; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ciconst struct inode_operations afs_autocell_inode_operations = { 388c2ecf20Sopenharmony_ci .getattr = afs_getattr, 398c2ecf20Sopenharmony_ci}; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic LIST_HEAD(afs_vfsmounts); 428c2ecf20Sopenharmony_cistatic DECLARE_DELAYED_WORK(afs_mntpt_expiry_timer, afs_mntpt_expiry_timed_out); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic unsigned long afs_mntpt_expiry_timeout = 10 * 60; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic const char afs_root_volume[] = "root.cell"; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/* 498c2ecf20Sopenharmony_ci * no valid lookup procedure on this sort of dir 508c2ecf20Sopenharmony_ci */ 518c2ecf20Sopenharmony_cistatic struct dentry *afs_mntpt_lookup(struct inode *dir, 528c2ecf20Sopenharmony_ci struct dentry *dentry, 538c2ecf20Sopenharmony_ci unsigned int flags) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci _enter("%p,%p{%pd2}", dir, dentry, dentry); 568c2ecf20Sopenharmony_ci return ERR_PTR(-EREMOTE); 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci/* 608c2ecf20Sopenharmony_ci * no valid open procedure on this sort of dir 618c2ecf20Sopenharmony_ci */ 628c2ecf20Sopenharmony_cistatic int afs_mntpt_open(struct inode *inode, struct file *file) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci _enter("%p,%p{%pD2}", inode, file, file); 658c2ecf20Sopenharmony_ci return -EREMOTE; 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/* 698c2ecf20Sopenharmony_ci * Set the parameters for the proposed superblock. 708c2ecf20Sopenharmony_ci */ 718c2ecf20Sopenharmony_cistatic int afs_mntpt_set_params(struct fs_context *fc, struct dentry *mntpt) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci struct afs_fs_context *ctx = fc->fs_private; 748c2ecf20Sopenharmony_ci struct afs_super_info *src_as = AFS_FS_S(mntpt->d_sb); 758c2ecf20Sopenharmony_ci struct afs_vnode *vnode = AFS_FS_I(d_inode(mntpt)); 768c2ecf20Sopenharmony_ci struct afs_cell *cell; 778c2ecf20Sopenharmony_ci const char *p; 788c2ecf20Sopenharmony_ci int ret; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci if (fc->net_ns != src_as->net_ns) { 818c2ecf20Sopenharmony_ci put_net(fc->net_ns); 828c2ecf20Sopenharmony_ci fc->net_ns = get_net(src_as->net_ns); 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci if (src_as->volume && src_as->volume->type == AFSVL_RWVOL) { 868c2ecf20Sopenharmony_ci ctx->type = AFSVL_RWVOL; 878c2ecf20Sopenharmony_ci ctx->force = true; 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci if (ctx->cell) { 908c2ecf20Sopenharmony_ci afs_unuse_cell(ctx->net, ctx->cell, afs_cell_trace_unuse_mntpt); 918c2ecf20Sopenharmony_ci ctx->cell = NULL; 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci if (test_bit(AFS_VNODE_PSEUDODIR, &vnode->flags)) { 948c2ecf20Sopenharmony_ci /* if the directory is a pseudo directory, use the d_name */ 958c2ecf20Sopenharmony_ci unsigned size = mntpt->d_name.len; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci if (size < 2) 988c2ecf20Sopenharmony_ci return -ENOENT; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci p = mntpt->d_name.name; 1018c2ecf20Sopenharmony_ci if (mntpt->d_name.name[0] == '.') { 1028c2ecf20Sopenharmony_ci size--; 1038c2ecf20Sopenharmony_ci p++; 1048c2ecf20Sopenharmony_ci ctx->type = AFSVL_RWVOL; 1058c2ecf20Sopenharmony_ci ctx->force = true; 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci if (size > AFS_MAXCELLNAME) 1088c2ecf20Sopenharmony_ci return -ENAMETOOLONG; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci cell = afs_lookup_cell(ctx->net, p, size, NULL, false); 1118c2ecf20Sopenharmony_ci if (IS_ERR(cell)) { 1128c2ecf20Sopenharmony_ci pr_err("kAFS: unable to lookup cell '%pd'\n", mntpt); 1138c2ecf20Sopenharmony_ci return PTR_ERR(cell); 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci ctx->cell = cell; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci ctx->volname = afs_root_volume; 1188c2ecf20Sopenharmony_ci ctx->volnamesz = sizeof(afs_root_volume) - 1; 1198c2ecf20Sopenharmony_ci } else { 1208c2ecf20Sopenharmony_ci /* read the contents of the AFS special symlink */ 1218c2ecf20Sopenharmony_ci struct page *page; 1228c2ecf20Sopenharmony_ci loff_t size = i_size_read(d_inode(mntpt)); 1238c2ecf20Sopenharmony_ci char *buf; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci if (src_as->cell) 1268c2ecf20Sopenharmony_ci ctx->cell = afs_use_cell(src_as->cell, afs_cell_trace_use_mntpt); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci if (size < 2 || size > PAGE_SIZE - 1) 1298c2ecf20Sopenharmony_ci return -EINVAL; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci page = read_mapping_page(d_inode(mntpt)->i_mapping, 0, NULL); 1328c2ecf20Sopenharmony_ci if (IS_ERR(page)) 1338c2ecf20Sopenharmony_ci return PTR_ERR(page); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci if (PageError(page)) { 1368c2ecf20Sopenharmony_ci ret = afs_bad(AFS_FS_I(d_inode(mntpt)), afs_file_error_mntpt); 1378c2ecf20Sopenharmony_ci put_page(page); 1388c2ecf20Sopenharmony_ci return ret; 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci buf = kmap(page); 1428c2ecf20Sopenharmony_ci ret = -EINVAL; 1438c2ecf20Sopenharmony_ci if (buf[size - 1] == '.') 1448c2ecf20Sopenharmony_ci ret = vfs_parse_fs_string(fc, "source", buf, size - 1); 1458c2ecf20Sopenharmony_ci kunmap(page); 1468c2ecf20Sopenharmony_ci put_page(page); 1478c2ecf20Sopenharmony_ci if (ret < 0) 1488c2ecf20Sopenharmony_ci return ret; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci return 0; 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci/* 1558c2ecf20Sopenharmony_ci * create a vfsmount to be automounted 1568c2ecf20Sopenharmony_ci */ 1578c2ecf20Sopenharmony_cistatic struct vfsmount *afs_mntpt_do_automount(struct dentry *mntpt) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci struct fs_context *fc; 1608c2ecf20Sopenharmony_ci struct vfsmount *mnt; 1618c2ecf20Sopenharmony_ci int ret; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci BUG_ON(!d_inode(mntpt)); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci fc = fs_context_for_submount(&afs_fs_type, mntpt); 1668c2ecf20Sopenharmony_ci if (IS_ERR(fc)) 1678c2ecf20Sopenharmony_ci return ERR_CAST(fc); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci ret = afs_mntpt_set_params(fc, mntpt); 1708c2ecf20Sopenharmony_ci if (!ret) 1718c2ecf20Sopenharmony_ci mnt = fc_mount(fc); 1728c2ecf20Sopenharmony_ci else 1738c2ecf20Sopenharmony_ci mnt = ERR_PTR(ret); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci put_fs_context(fc); 1768c2ecf20Sopenharmony_ci return mnt; 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci/* 1808c2ecf20Sopenharmony_ci * handle an automount point 1818c2ecf20Sopenharmony_ci */ 1828c2ecf20Sopenharmony_cistruct vfsmount *afs_d_automount(struct path *path) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci struct vfsmount *newmnt; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci _enter("{%pd}", path->dentry); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci newmnt = afs_mntpt_do_automount(path->dentry); 1898c2ecf20Sopenharmony_ci if (IS_ERR(newmnt)) 1908c2ecf20Sopenharmony_ci return newmnt; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci mntget(newmnt); /* prevent immediate expiration */ 1938c2ecf20Sopenharmony_ci mnt_set_expiry(newmnt, &afs_vfsmounts); 1948c2ecf20Sopenharmony_ci queue_delayed_work(afs_wq, &afs_mntpt_expiry_timer, 1958c2ecf20Sopenharmony_ci afs_mntpt_expiry_timeout * HZ); 1968c2ecf20Sopenharmony_ci _leave(" = %p", newmnt); 1978c2ecf20Sopenharmony_ci return newmnt; 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci/* 2018c2ecf20Sopenharmony_ci * handle mountpoint expiry timer going off 2028c2ecf20Sopenharmony_ci */ 2038c2ecf20Sopenharmony_cistatic void afs_mntpt_expiry_timed_out(struct work_struct *work) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci _enter(""); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci if (!list_empty(&afs_vfsmounts)) { 2088c2ecf20Sopenharmony_ci mark_mounts_for_expiry(&afs_vfsmounts); 2098c2ecf20Sopenharmony_ci queue_delayed_work(afs_wq, &afs_mntpt_expiry_timer, 2108c2ecf20Sopenharmony_ci afs_mntpt_expiry_timeout * HZ); 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci _leave(""); 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci/* 2178c2ecf20Sopenharmony_ci * kill the AFS mountpoint timer if it's still running 2188c2ecf20Sopenharmony_ci */ 2198c2ecf20Sopenharmony_civoid afs_mntpt_kill_timer(void) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci _enter(""); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci ASSERT(list_empty(&afs_vfsmounts)); 2248c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&afs_mntpt_expiry_timer); 2258c2ecf20Sopenharmony_ci} 226