12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2ec26815aSDavid Howells /* mountpoint management
31da177e4SLinus Torvalds *
41da177e4SLinus Torvalds * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
51da177e4SLinus Torvalds * Written by David Howells (dhowells@redhat.com)
61da177e4SLinus Torvalds */
71da177e4SLinus Torvalds
81da177e4SLinus Torvalds #include <linux/kernel.h>
91da177e4SLinus Torvalds #include <linux/module.h>
101da177e4SLinus Torvalds #include <linux/init.h>
111da177e4SLinus Torvalds #include <linux/fs.h>
121da177e4SLinus Torvalds #include <linux/pagemap.h>
131da177e4SLinus Torvalds #include <linux/mount.h>
141da177e4SLinus Torvalds #include <linux/namei.h>
155a0e3ad6STejun Heo #include <linux/gfp.h>
1613fcc683SDavid Howells #include <linux/fs_context.h>
171da177e4SLinus Torvalds #include "internal.h"
181da177e4SLinus Torvalds
191da177e4SLinus Torvalds
201da177e4SLinus Torvalds static struct dentry *afs_mntpt_lookup(struct inode *dir,
211da177e4SLinus Torvalds struct dentry *dentry,
2200cd8dd3SAl Viro unsigned int flags);
231da177e4SLinus Torvalds static int afs_mntpt_open(struct inode *inode, struct file *file);
2408e0e7c8SDavid Howells static void afs_mntpt_expiry_timed_out(struct work_struct *work);
251da177e4SLinus Torvalds
264b6f5d20SArjan van de Ven const struct file_operations afs_mntpt_file_operations = {
271da177e4SLinus Torvalds .open = afs_mntpt_open,
286038f373SArnd Bergmann .llseek = noop_llseek,
291da177e4SLinus Torvalds };
301da177e4SLinus Torvalds
31754661f1SArjan van de Ven const struct inode_operations afs_mntpt_inode_operations = {
321da177e4SLinus Torvalds .lookup = afs_mntpt_lookup,
331da177e4SLinus Torvalds .readlink = page_readlink,
34416351f2SDavid Howells .getattr = afs_getattr,
351da177e4SLinus Torvalds };
361da177e4SLinus Torvalds
37bec5eb61Swanglei const struct inode_operations afs_autocell_inode_operations = {
38bec5eb61Swanglei .getattr = afs_getattr,
39bec5eb61Swanglei };
40bec5eb61Swanglei
411da177e4SLinus Torvalds static LIST_HEAD(afs_vfsmounts);
4208e0e7c8SDavid Howells static DECLARE_DELAYED_WORK(afs_mntpt_expiry_timer, afs_mntpt_expiry_timed_out);
431da177e4SLinus Torvalds
44c1206a2cSAdrian Bunk static unsigned long afs_mntpt_expiry_timeout = 10 * 60;
451da177e4SLinus Torvalds
46c99c2171SDavid Howells static const char afs_root_volume[] = "root.cell";
47c99c2171SDavid Howells
481da177e4SLinus Torvalds /*
491da177e4SLinus Torvalds * no valid lookup procedure on this sort of dir
501da177e4SLinus Torvalds */
afs_mntpt_lookup(struct inode * dir,struct dentry * dentry,unsigned int flags)511da177e4SLinus Torvalds static struct dentry *afs_mntpt_lookup(struct inode *dir,
521da177e4SLinus Torvalds struct dentry *dentry,
5300cd8dd3SAl Viro unsigned int flags)
541da177e4SLinus Torvalds {
55a455589fSAl Viro _enter("%p,%p{%pd2}", dir, dentry, dentry);
561da177e4SLinus Torvalds return ERR_PTR(-EREMOTE);
57ec26815aSDavid Howells }
581da177e4SLinus Torvalds
591da177e4SLinus Torvalds /*
601da177e4SLinus Torvalds * no valid open procedure on this sort of dir
611da177e4SLinus Torvalds */
afs_mntpt_open(struct inode * inode,struct file * file)621da177e4SLinus Torvalds static int afs_mntpt_open(struct inode *inode, struct file *file)
631da177e4SLinus Torvalds {
64a455589fSAl Viro _enter("%p,%p{%pD2}", inode, file, file);
651da177e4SLinus Torvalds return -EREMOTE;
66ec26815aSDavid Howells }
671da177e4SLinus Torvalds
681da177e4SLinus Torvalds /*
69c99c2171SDavid Howells * Set the parameters for the proposed superblock.
70c99c2171SDavid Howells */
afs_mntpt_set_params(struct fs_context * fc,struct dentry * mntpt)71c99c2171SDavid Howells static int afs_mntpt_set_params(struct fs_context *fc, struct dentry *mntpt)
72c99c2171SDavid Howells {
73c99c2171SDavid Howells struct afs_fs_context *ctx = fc->fs_private;
74c99c2171SDavid Howells struct afs_super_info *src_as = AFS_FS_S(mntpt->d_sb);
75c99c2171SDavid Howells struct afs_vnode *vnode = AFS_FS_I(d_inode(mntpt));
76c99c2171SDavid Howells struct afs_cell *cell;
77c99c2171SDavid Howells const char *p;
78c99c2171SDavid Howells int ret;
79c99c2171SDavid Howells
80c99c2171SDavid Howells if (fc->net_ns != src_as->net_ns) {
81c99c2171SDavid Howells put_net(fc->net_ns);
82c99c2171SDavid Howells fc->net_ns = get_net(src_as->net_ns);
83c99c2171SDavid Howells }
84c99c2171SDavid Howells
85c99c2171SDavid Howells if (src_as->volume && src_as->volume->type == AFSVL_RWVOL) {
86c99c2171SDavid Howells ctx->type = AFSVL_RWVOL;
87c99c2171SDavid Howells ctx->force = true;
88c99c2171SDavid Howells }
89c99c2171SDavid Howells if (ctx->cell) {
90dca54a7bSDavid Howells afs_unuse_cell(ctx->net, ctx->cell, afs_cell_trace_unuse_mntpt);
91c99c2171SDavid Howells ctx->cell = NULL;
92c99c2171SDavid Howells }
93c99c2171SDavid Howells if (test_bit(AFS_VNODE_PSEUDODIR, &vnode->flags)) {
94c99c2171SDavid Howells /* if the directory is a pseudo directory, use the d_name */
95c99c2171SDavid Howells unsigned size = mntpt->d_name.len;
96c99c2171SDavid Howells
97c99c2171SDavid Howells if (size < 2)
98c99c2171SDavid Howells return -ENOENT;
99c99c2171SDavid Howells
100c99c2171SDavid Howells p = mntpt->d_name.name;
101c99c2171SDavid Howells if (mntpt->d_name.name[0] == '.') {
102c99c2171SDavid Howells size--;
103c99c2171SDavid Howells p++;
104c99c2171SDavid Howells ctx->type = AFSVL_RWVOL;
105c99c2171SDavid Howells ctx->force = true;
106c99c2171SDavid Howells }
107c99c2171SDavid Howells if (size > AFS_MAXCELLNAME)
108c99c2171SDavid Howells return -ENAMETOOLONG;
109c99c2171SDavid Howells
110c99c2171SDavid Howells cell = afs_lookup_cell(ctx->net, p, size, NULL, false);
111c99c2171SDavid Howells if (IS_ERR(cell)) {
112c99c2171SDavid Howells pr_err("kAFS: unable to lookup cell '%pd'\n", mntpt);
113c99c2171SDavid Howells return PTR_ERR(cell);
114c99c2171SDavid Howells }
115c99c2171SDavid Howells ctx->cell = cell;
116c99c2171SDavid Howells
117c99c2171SDavid Howells ctx->volname = afs_root_volume;
118c99c2171SDavid Howells ctx->volnamesz = sizeof(afs_root_volume) - 1;
119c99c2171SDavid Howells } else {
120c99c2171SDavid Howells /* read the contents of the AFS special symlink */
121c99c2171SDavid Howells struct page *page;
122c99c2171SDavid Howells loff_t size = i_size_read(d_inode(mntpt));
123c99c2171SDavid Howells char *buf;
124c99c2171SDavid Howells
125c99c2171SDavid Howells if (src_as->cell)
126dca54a7bSDavid Howells ctx->cell = afs_use_cell(src_as->cell, afs_cell_trace_use_mntpt);
127c99c2171SDavid Howells
128158d5833SDavid Howells if (size < 2 || size > PAGE_SIZE - 1)
129c99c2171SDavid Howells return -EINVAL;
130c99c2171SDavid Howells
131c99c2171SDavid Howells page = read_mapping_page(d_inode(mntpt)->i_mapping, 0, NULL);
132c99c2171SDavid Howells if (IS_ERR(page))
133c99c2171SDavid Howells return PTR_ERR(page);
134c99c2171SDavid Howells
135c99c2171SDavid Howells buf = kmap(page);
136158d5833SDavid Howells ret = -EINVAL;
137158d5833SDavid Howells if (buf[size - 1] == '.')
138158d5833SDavid Howells ret = vfs_parse_fs_string(fc, "source", buf, size - 1);
139c99c2171SDavid Howells kunmap(page);
140c99c2171SDavid Howells put_page(page);
141c99c2171SDavid Howells if (ret < 0)
142c99c2171SDavid Howells return ret;
143*ed2d2eadSMarc Dionne
144*ed2d2eadSMarc Dionne /* Don't cross a backup volume mountpoint from a backup volume */
145*ed2d2eadSMarc Dionne if (src_as->volume && src_as->volume->type == AFSVL_BACKVOL &&
146*ed2d2eadSMarc Dionne ctx->type == AFSVL_BACKVOL)
147*ed2d2eadSMarc Dionne return -ENODEV;
148c99c2171SDavid Howells }
149c99c2171SDavid Howells
150c99c2171SDavid Howells return 0;
151c99c2171SDavid Howells }
152c99c2171SDavid Howells
153c99c2171SDavid Howells /*
1541da177e4SLinus Torvalds * create a vfsmount to be automounted
1551da177e4SLinus Torvalds */
afs_mntpt_do_automount(struct dentry * mntpt)1561da177e4SLinus Torvalds static struct vfsmount *afs_mntpt_do_automount(struct dentry *mntpt)
1571da177e4SLinus Torvalds {
158c99c2171SDavid Howells struct fs_context *fc;
1591da177e4SLinus Torvalds struct vfsmount *mnt;
1601da177e4SLinus Torvalds int ret;
1611da177e4SLinus Torvalds
1622b0143b5SDavid Howells BUG_ON(!d_inode(mntpt));
1631da177e4SLinus Torvalds
164c99c2171SDavid Howells fc = fs_context_for_submount(&afs_fs_type, mntpt);
165c99c2171SDavid Howells if (IS_ERR(fc))
166c99c2171SDavid Howells return ERR_CAST(fc);
1671da177e4SLinus Torvalds
168c99c2171SDavid Howells ret = afs_mntpt_set_params(fc, mntpt);
169c99c2171SDavid Howells if (!ret)
170c99c2171SDavid Howells mnt = fc_mount(fc);
171c99c2171SDavid Howells else
172c99c2171SDavid Howells mnt = ERR_PTR(ret);
1731da177e4SLinus Torvalds
174c99c2171SDavid Howells put_fs_context(fc);
1751da177e4SLinus Torvalds return mnt;
176ec26815aSDavid Howells }
1771da177e4SLinus Torvalds
1781da177e4SLinus Torvalds /*
179d18610b0SDavid Howells * handle an automount point
1801da177e4SLinus Torvalds */
afs_d_automount(struct path * path)181d18610b0SDavid Howells struct vfsmount *afs_d_automount(struct path *path)
1821da177e4SLinus Torvalds {
1831da177e4SLinus Torvalds struct vfsmount *newmnt;
1841da177e4SLinus Torvalds
185a455589fSAl Viro _enter("{%pd}", path->dentry);
1861da177e4SLinus Torvalds
187d18610b0SDavid Howells newmnt = afs_mntpt_do_automount(path->dentry);
188d18610b0SDavid Howells if (IS_ERR(newmnt))
189d18610b0SDavid Howells return newmnt;
1901da177e4SLinus Torvalds
191ea5b778aSDavid Howells mntget(newmnt); /* prevent immediate expiration */
192ea5b778aSDavid Howells mnt_set_expiry(newmnt, &afs_vfsmounts);
1930ad53eeeSTejun Heo queue_delayed_work(afs_wq, &afs_mntpt_expiry_timer,
19408e0e7c8SDavid Howells afs_mntpt_expiry_timeout * HZ);
1955ffc2836SAl Viro _leave(" = %p", newmnt);
196d18610b0SDavid Howells return newmnt;
197d18610b0SDavid Howells }
1981da177e4SLinus Torvalds
1991da177e4SLinus Torvalds /*
2001da177e4SLinus Torvalds * handle mountpoint expiry timer going off
2011da177e4SLinus Torvalds */
afs_mntpt_expiry_timed_out(struct work_struct * work)20208e0e7c8SDavid Howells static void afs_mntpt_expiry_timed_out(struct work_struct *work)
2031da177e4SLinus Torvalds {
20408e0e7c8SDavid Howells _enter("");
2051da177e4SLinus Torvalds
20608e0e7c8SDavid Howells if (!list_empty(&afs_vfsmounts)) {
2071da177e4SLinus Torvalds mark_mounts_for_expiry(&afs_vfsmounts);
2080ad53eeeSTejun Heo queue_delayed_work(afs_wq, &afs_mntpt_expiry_timer,
2091da177e4SLinus Torvalds afs_mntpt_expiry_timeout * HZ);
21008e0e7c8SDavid Howells }
2111da177e4SLinus Torvalds
21208e0e7c8SDavid Howells _leave("");
21308e0e7c8SDavid Howells }
21408e0e7c8SDavid Howells
21508e0e7c8SDavid Howells /*
21608e0e7c8SDavid Howells * kill the AFS mountpoint timer if it's still running
21708e0e7c8SDavid Howells */
afs_mntpt_kill_timer(void)21808e0e7c8SDavid Howells void afs_mntpt_kill_timer(void)
21908e0e7c8SDavid Howells {
22008e0e7c8SDavid Howells _enter("");
22108e0e7c8SDavid Howells
22208e0e7c8SDavid Howells ASSERT(list_empty(&afs_vfsmounts));
2230ad53eeeSTejun Heo cancel_delayed_work_sync(&afs_mntpt_expiry_timer);
22408e0e7c8SDavid Howells }
225