xref: /linux/fs/afs/mntpt.c (revision 092e0e7e520a1fca03e13c9f2d157432a8657ff2)
1 /* mountpoint management
2  *
3  * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
4  * Written by David Howells (dhowells@redhat.com)
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version
9  * 2 of the License, or (at your option) any later version.
10  */
11 
12 #include <linux/kernel.h>
13 #include <linux/module.h>
14 #include <linux/init.h>
15 #include <linux/fs.h>
16 #include <linux/pagemap.h>
17 #include <linux/mount.h>
18 #include <linux/namei.h>
19 #include <linux/gfp.h>
20 #include "internal.h"
21 
22 
23 static struct dentry *afs_mntpt_lookup(struct inode *dir,
24 				       struct dentry *dentry,
25 				       struct nameidata *nd);
26 static int afs_mntpt_open(struct inode *inode, struct file *file);
27 static void *afs_mntpt_follow_link(struct dentry *dentry, struct nameidata *nd);
28 static void afs_mntpt_expiry_timed_out(struct work_struct *work);
29 
30 const struct file_operations afs_mntpt_file_operations = {
31 	.open		= afs_mntpt_open,
32 	.llseek		= noop_llseek,
33 };
34 
35 const struct inode_operations afs_mntpt_inode_operations = {
36 	.lookup		= afs_mntpt_lookup,
37 	.follow_link	= afs_mntpt_follow_link,
38 	.readlink	= page_readlink,
39 	.getattr	= afs_getattr,
40 };
41 
42 const struct inode_operations afs_autocell_inode_operations = {
43 	.follow_link	= afs_mntpt_follow_link,
44 	.getattr	= afs_getattr,
45 };
46 
47 static LIST_HEAD(afs_vfsmounts);
48 static DECLARE_DELAYED_WORK(afs_mntpt_expiry_timer, afs_mntpt_expiry_timed_out);
49 
50 static unsigned long afs_mntpt_expiry_timeout = 10 * 60;
51 
52 /*
53  * check a symbolic link to see whether it actually encodes a mountpoint
54  * - sets the AFS_VNODE_MOUNTPOINT flag on the vnode appropriately
55  */
56 int afs_mntpt_check_symlink(struct afs_vnode *vnode, struct key *key)
57 {
58 	struct page *page;
59 	size_t size;
60 	char *buf;
61 	int ret;
62 
63 	_enter("{%x:%u,%u}",
64 	       vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique);
65 
66 	/* read the contents of the symlink into the pagecache */
67 	page = read_cache_page(AFS_VNODE_TO_I(vnode)->i_mapping, 0,
68 			       afs_page_filler, key);
69 	if (IS_ERR(page)) {
70 		ret = PTR_ERR(page);
71 		goto out;
72 	}
73 
74 	ret = -EIO;
75 	if (PageError(page))
76 		goto out_free;
77 
78 	buf = kmap(page);
79 
80 	/* examine the symlink's contents */
81 	size = vnode->status.size;
82 	_debug("symlink to %*.*s", (int) size, (int) size, buf);
83 
84 	if (size > 2 &&
85 	    (buf[0] == '%' || buf[0] == '#') &&
86 	    buf[size - 1] == '.'
87 	    ) {
88 		_debug("symlink is a mountpoint");
89 		spin_lock(&vnode->lock);
90 		set_bit(AFS_VNODE_MOUNTPOINT, &vnode->flags);
91 		spin_unlock(&vnode->lock);
92 	}
93 
94 	ret = 0;
95 
96 	kunmap(page);
97 out_free:
98 	page_cache_release(page);
99 out:
100 	_leave(" = %d", ret);
101 	return ret;
102 }
103 
104 /*
105  * no valid lookup procedure on this sort of dir
106  */
107 static struct dentry *afs_mntpt_lookup(struct inode *dir,
108 				       struct dentry *dentry,
109 				       struct nameidata *nd)
110 {
111 	_enter("%p,%p{%p{%s},%s}",
112 	       dir,
113 	       dentry,
114 	       dentry->d_parent,
115 	       dentry->d_parent ?
116 	       dentry->d_parent->d_name.name : (const unsigned char *) "",
117 	       dentry->d_name.name);
118 
119 	return ERR_PTR(-EREMOTE);
120 }
121 
122 /*
123  * no valid open procedure on this sort of dir
124  */
125 static int afs_mntpt_open(struct inode *inode, struct file *file)
126 {
127 	_enter("%p,%p{%p{%s},%s}",
128 	       inode, file,
129 	       file->f_path.dentry->d_parent,
130 	       file->f_path.dentry->d_parent ?
131 	       file->f_path.dentry->d_parent->d_name.name :
132 	       (const unsigned char *) "",
133 	       file->f_path.dentry->d_name.name);
134 
135 	return -EREMOTE;
136 }
137 
138 /*
139  * create a vfsmount to be automounted
140  */
141 static struct vfsmount *afs_mntpt_do_automount(struct dentry *mntpt)
142 {
143 	struct afs_super_info *super;
144 	struct vfsmount *mnt;
145 	struct afs_vnode *vnode;
146 	struct page *page;
147 	char *devname, *options;
148 	bool rwpath = false;
149 	int ret;
150 
151 	_enter("{%s}", mntpt->d_name.name);
152 
153 	BUG_ON(!mntpt->d_inode);
154 
155 	ret = -ENOMEM;
156 	devname = (char *) get_zeroed_page(GFP_KERNEL);
157 	if (!devname)
158 		goto error_no_devname;
159 
160 	options = (char *) get_zeroed_page(GFP_KERNEL);
161 	if (!options)
162 		goto error_no_options;
163 
164 	vnode = AFS_FS_I(mntpt->d_inode);
165 	if (test_bit(AFS_VNODE_PSEUDODIR, &vnode->flags)) {
166 		/* if the directory is a pseudo directory, use the d_name */
167 		static const char afs_root_cell[] = ":root.cell.";
168 		unsigned size = mntpt->d_name.len;
169 
170 		ret = -ENOENT;
171 		if (size < 2 || size > AFS_MAXCELLNAME)
172 			goto error_no_page;
173 
174 		if (mntpt->d_name.name[0] == '.') {
175 			devname[0] = '#';
176 			memcpy(devname + 1, mntpt->d_name.name, size - 1);
177 			memcpy(devname + size, afs_root_cell,
178 			       sizeof(afs_root_cell));
179 			rwpath = true;
180 		} else {
181 			devname[0] = '%';
182 			memcpy(devname + 1, mntpt->d_name.name, size);
183 			memcpy(devname + size + 1, afs_root_cell,
184 			       sizeof(afs_root_cell));
185 		}
186 	} else {
187 		/* read the contents of the AFS special symlink */
188 		loff_t size = i_size_read(mntpt->d_inode);
189 		char *buf;
190 
191 		ret = -EINVAL;
192 		if (size > PAGE_SIZE - 1)
193 			goto error_no_page;
194 
195 		page = read_mapping_page(mntpt->d_inode->i_mapping, 0, NULL);
196 		if (IS_ERR(page)) {
197 			ret = PTR_ERR(page);
198 			goto error_no_page;
199 		}
200 
201 		ret = -EIO;
202 		if (PageError(page))
203 			goto error;
204 
205 		buf = kmap_atomic(page, KM_USER0);
206 		memcpy(devname, buf, size);
207 		kunmap_atomic(buf, KM_USER0);
208 		page_cache_release(page);
209 		page = NULL;
210 	}
211 
212 	/* work out what options we want */
213 	super = AFS_FS_S(mntpt->d_sb);
214 	memcpy(options, "cell=", 5);
215 	strcpy(options + 5, super->volume->cell->name);
216 	if (super->volume->type == AFSVL_RWVOL || rwpath)
217 		strcat(options, ",rwpath");
218 
219 	/* try and do the mount */
220 	_debug("--- attempting mount %s -o %s ---", devname, options);
221 	mnt = vfs_kern_mount(&afs_fs_type, 0, devname, options);
222 	_debug("--- mount result %p ---", mnt);
223 
224 	free_page((unsigned long) devname);
225 	free_page((unsigned long) options);
226 	_leave(" = %p", mnt);
227 	return mnt;
228 
229 error:
230 	page_cache_release(page);
231 error_no_page:
232 	free_page((unsigned long) options);
233 error_no_options:
234 	free_page((unsigned long) devname);
235 error_no_devname:
236 	_leave(" = %d", ret);
237 	return ERR_PTR(ret);
238 }
239 
240 /*
241  * follow a link from a mountpoint directory, thus causing it to be mounted
242  */
243 static void *afs_mntpt_follow_link(struct dentry *dentry, struct nameidata *nd)
244 {
245 	struct vfsmount *newmnt;
246 	int err;
247 
248 	_enter("%p{%s},{%s:%p{%s},}",
249 	       dentry,
250 	       dentry->d_name.name,
251 	       nd->path.mnt->mnt_devname,
252 	       dentry,
253 	       nd->path.dentry->d_name.name);
254 
255 	dput(nd->path.dentry);
256 	nd->path.dentry = dget(dentry);
257 
258 	newmnt = afs_mntpt_do_automount(nd->path.dentry);
259 	if (IS_ERR(newmnt)) {
260 		path_put(&nd->path);
261 		return (void *)newmnt;
262 	}
263 
264 	mntget(newmnt);
265 	err = do_add_mount(newmnt, &nd->path, MNT_SHRINKABLE, &afs_vfsmounts);
266 	switch (err) {
267 	case 0:
268 		path_put(&nd->path);
269 		nd->path.mnt = newmnt;
270 		nd->path.dentry = dget(newmnt->mnt_root);
271 		schedule_delayed_work(&afs_mntpt_expiry_timer,
272 				      afs_mntpt_expiry_timeout * HZ);
273 		break;
274 	case -EBUSY:
275 		/* someone else made a mount here whilst we were busy */
276 		while (d_mountpoint(nd->path.dentry) &&
277 		       follow_down(&nd->path))
278 			;
279 		err = 0;
280 	default:
281 		mntput(newmnt);
282 		break;
283 	}
284 
285 	_leave(" = %d", err);
286 	return ERR_PTR(err);
287 }
288 
289 /*
290  * handle mountpoint expiry timer going off
291  */
292 static void afs_mntpt_expiry_timed_out(struct work_struct *work)
293 {
294 	_enter("");
295 
296 	if (!list_empty(&afs_vfsmounts)) {
297 		mark_mounts_for_expiry(&afs_vfsmounts);
298 		schedule_delayed_work(&afs_mntpt_expiry_timer,
299 				      afs_mntpt_expiry_timeout * HZ);
300 	}
301 
302 	_leave("");
303 }
304 
305 /*
306  * kill the AFS mountpoint timer if it's still running
307  */
308 void afs_mntpt_kill_timer(void)
309 {
310 	_enter("");
311 
312 	ASSERT(list_empty(&afs_vfsmounts));
313 	cancel_delayed_work(&afs_mntpt_expiry_timer);
314 	flush_scheduled_work();
315 }
316