123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225 |
- // SPDX-License-Identifier: GPL-2.0-or-later
- /* mountpoint management
- *
- * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.com)
- */
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/init.h>
- #include <linux/fs.h>
- #include <linux/pagemap.h>
- #include <linux/mount.h>
- #include <linux/namei.h>
- #include <linux/gfp.h>
- #include <linux/fs_context.h>
- #include "internal.h"
- static struct dentry *afs_mntpt_lookup(struct inode *dir,
- struct dentry *dentry,
- unsigned int flags);
- static int afs_mntpt_open(struct inode *inode, struct file *file);
- static void afs_mntpt_expiry_timed_out(struct work_struct *work);
- const struct file_operations afs_mntpt_file_operations = {
- .open = afs_mntpt_open,
- .llseek = noop_llseek,
- };
- const struct inode_operations afs_mntpt_inode_operations = {
- .lookup = afs_mntpt_lookup,
- .readlink = page_readlink,
- .getattr = afs_getattr,
- };
- const struct inode_operations afs_autocell_inode_operations = {
- .getattr = afs_getattr,
- };
- static LIST_HEAD(afs_vfsmounts);
- static DECLARE_DELAYED_WORK(afs_mntpt_expiry_timer, afs_mntpt_expiry_timed_out);
- static unsigned long afs_mntpt_expiry_timeout = 10 * 60;
- static const char afs_root_volume[] = "root.cell";
- /*
- * no valid lookup procedure on this sort of dir
- */
- static struct dentry *afs_mntpt_lookup(struct inode *dir,
- struct dentry *dentry,
- unsigned int flags)
- {
- _enter("%p,%p{%pd2}", dir, dentry, dentry);
- return ERR_PTR(-EREMOTE);
- }
- /*
- * no valid open procedure on this sort of dir
- */
- static int afs_mntpt_open(struct inode *inode, struct file *file)
- {
- _enter("%p,%p{%pD2}", inode, file, file);
- return -EREMOTE;
- }
- /*
- * Set the parameters for the proposed superblock.
- */
- static int afs_mntpt_set_params(struct fs_context *fc, struct dentry *mntpt)
- {
- struct afs_fs_context *ctx = fc->fs_private;
- struct afs_super_info *src_as = AFS_FS_S(mntpt->d_sb);
- struct afs_vnode *vnode = AFS_FS_I(d_inode(mntpt));
- struct afs_cell *cell;
- const char *p;
- int ret;
- if (fc->net_ns != src_as->net_ns) {
- put_net(fc->net_ns);
- fc->net_ns = get_net(src_as->net_ns);
- }
- if (src_as->volume && src_as->volume->type == AFSVL_RWVOL) {
- ctx->type = AFSVL_RWVOL;
- ctx->force = true;
- }
- if (ctx->cell) {
- afs_unuse_cell(ctx->net, ctx->cell, afs_cell_trace_unuse_mntpt);
- ctx->cell = NULL;
- }
- if (test_bit(AFS_VNODE_PSEUDODIR, &vnode->flags)) {
- /* if the directory is a pseudo directory, use the d_name */
- unsigned size = mntpt->d_name.len;
- if (size < 2)
- return -ENOENT;
- p = mntpt->d_name.name;
- if (mntpt->d_name.name[0] == '.') {
- size--;
- p++;
- ctx->type = AFSVL_RWVOL;
- ctx->force = true;
- }
- if (size > AFS_MAXCELLNAME)
- return -ENAMETOOLONG;
- cell = afs_lookup_cell(ctx->net, p, size, NULL, false);
- if (IS_ERR(cell)) {
- pr_err("kAFS: unable to lookup cell '%pd'\n", mntpt);
- return PTR_ERR(cell);
- }
- ctx->cell = cell;
- ctx->volname = afs_root_volume;
- ctx->volnamesz = sizeof(afs_root_volume) - 1;
- } else {
- /* read the contents of the AFS special symlink */
- struct page *page;
- loff_t size = i_size_read(d_inode(mntpt));
- char *buf;
- if (src_as->cell)
- ctx->cell = afs_use_cell(src_as->cell, afs_cell_trace_use_mntpt);
- if (size < 2 || size > PAGE_SIZE - 1)
- return -EINVAL;
- page = read_mapping_page(d_inode(mntpt)->i_mapping, 0, NULL);
- if (IS_ERR(page))
- return PTR_ERR(page);
- if (PageError(page)) {
- ret = afs_bad(AFS_FS_I(d_inode(mntpt)), afs_file_error_mntpt);
- put_page(page);
- return ret;
- }
- buf = kmap(page);
- ret = -EINVAL;
- if (buf[size - 1] == '.')
- ret = vfs_parse_fs_string(fc, "source", buf, size - 1);
- kunmap(page);
- put_page(page);
- if (ret < 0)
- return ret;
- }
- return 0;
- }
- /*
- * create a vfsmount to be automounted
- */
- static struct vfsmount *afs_mntpt_do_automount(struct dentry *mntpt)
- {
- struct fs_context *fc;
- struct vfsmount *mnt;
- int ret;
- BUG_ON(!d_inode(mntpt));
- fc = fs_context_for_submount(&afs_fs_type, mntpt);
- if (IS_ERR(fc))
- return ERR_CAST(fc);
- ret = afs_mntpt_set_params(fc, mntpt);
- if (!ret)
- mnt = fc_mount(fc);
- else
- mnt = ERR_PTR(ret);
- put_fs_context(fc);
- return mnt;
- }
- /*
- * handle an automount point
- */
- struct vfsmount *afs_d_automount(struct path *path)
- {
- struct vfsmount *newmnt;
- _enter("{%pd}", path->dentry);
- newmnt = afs_mntpt_do_automount(path->dentry);
- if (IS_ERR(newmnt))
- return newmnt;
- mntget(newmnt); /* prevent immediate expiration */
- mnt_set_expiry(newmnt, &afs_vfsmounts);
- queue_delayed_work(afs_wq, &afs_mntpt_expiry_timer,
- afs_mntpt_expiry_timeout * HZ);
- _leave(" = %p", newmnt);
- return newmnt;
- }
- /*
- * handle mountpoint expiry timer going off
- */
- static void afs_mntpt_expiry_timed_out(struct work_struct *work)
- {
- _enter("");
- if (!list_empty(&afs_vfsmounts)) {
- mark_mounts_for_expiry(&afs_vfsmounts);
- queue_delayed_work(afs_wq, &afs_mntpt_expiry_timer,
- afs_mntpt_expiry_timeout * HZ);
- }
- _leave("");
- }
- /*
- * kill the AFS mountpoint timer if it's still running
- */
- void afs_mntpt_kill_timer(void)
- {
- _enter("");
- ASSERT(list_empty(&afs_vfsmounts));
- cancel_delayed_work_sync(&afs_mntpt_expiry_timer);
- }
|