mntpt.c 5.19 KB
Newer Older
1
/* mountpoint management
Linus Torvalds's avatar
Linus Torvalds committed
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
 *
 * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
 * Written by David Howells (dhowells@redhat.com)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version
 * 2 of the License, or (at your option) any later version.
 */

#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>
19
#include <linux/gfp.h>
Linus Torvalds's avatar
Linus Torvalds committed
20 21 22 23 24
#include "internal.h"


static struct dentry *afs_mntpt_lookup(struct inode *dir,
				       struct dentry *dentry,
25
				       unsigned int flags);
Linus Torvalds's avatar
Linus Torvalds committed
26
static int afs_mntpt_open(struct inode *inode, struct file *file);
27
static void afs_mntpt_expiry_timed_out(struct work_struct *work);
Linus Torvalds's avatar
Linus Torvalds committed
28

29
const struct file_operations afs_mntpt_file_operations = {
Linus Torvalds's avatar
Linus Torvalds committed
30
	.open		= afs_mntpt_open,
31
	.llseek		= noop_llseek,
Linus Torvalds's avatar
Linus Torvalds committed
32 33
};

34
const struct inode_operations afs_mntpt_inode_operations = {
Linus Torvalds's avatar
Linus Torvalds committed
35 36
	.lookup		= afs_mntpt_lookup,
	.readlink	= page_readlink,
David Howells's avatar
David Howells committed
37
	.getattr	= afs_getattr,
David Howells's avatar
David Howells committed
38
	.listxattr	= afs_listxattr,
Linus Torvalds's avatar
Linus Torvalds committed
39 40
};

41 42 43 44
const struct inode_operations afs_autocell_inode_operations = {
	.getattr	= afs_getattr,
};

Linus Torvalds's avatar
Linus Torvalds committed
45
static LIST_HEAD(afs_vfsmounts);
46
static DECLARE_DELAYED_WORK(afs_mntpt_expiry_timer, afs_mntpt_expiry_timed_out);
Linus Torvalds's avatar
Linus Torvalds committed
47

Adrian Bunk's avatar
Adrian Bunk committed
48
static unsigned long afs_mntpt_expiry_timeout = 10 * 60;
Linus Torvalds's avatar
Linus Torvalds committed
49 50 51 52 53 54

/*
 * no valid lookup procedure on this sort of dir
 */
static struct dentry *afs_mntpt_lookup(struct inode *dir,
				       struct dentry *dentry,
55
				       unsigned int flags)
Linus Torvalds's avatar
Linus Torvalds committed
56
{
Al Viro's avatar
Al Viro committed
57
	_enter("%p,%p{%pd2}", dir, dentry, dentry);
Linus Torvalds's avatar
Linus Torvalds committed
58
	return ERR_PTR(-EREMOTE);
59
}
Linus Torvalds's avatar
Linus Torvalds committed
60 61 62 63 64 65

/*
 * no valid open procedure on this sort of dir
 */
static int afs_mntpt_open(struct inode *inode, struct file *file)
{
Al Viro's avatar
Al Viro committed
66
	_enter("%p,%p{%pD2}", inode, file, file);
Linus Torvalds's avatar
Linus Torvalds committed
67
	return -EREMOTE;
68
}
Linus Torvalds's avatar
Linus Torvalds committed
69 70 71 72 73 74 75 76

/*
 * create a vfsmount to be automounted
 */
static struct vfsmount *afs_mntpt_do_automount(struct dentry *mntpt)
{
	struct afs_super_info *super;
	struct vfsmount *mnt;
77
	struct afs_vnode *vnode;
78
	struct page *page;
79 80
	char *devname, *options;
	bool rwpath = false;
Linus Torvalds's avatar
Linus Torvalds committed
81 82
	int ret;

Al Viro's avatar
Al Viro committed
83
	_enter("{%pd}", mntpt);
Linus Torvalds's avatar
Linus Torvalds committed
84

85
	BUG_ON(!d_inode(mntpt));
Linus Torvalds's avatar
Linus Torvalds committed
86 87 88 89

	ret = -ENOMEM;
	devname = (char *) get_zeroed_page(GFP_KERNEL);
	if (!devname)
90
		goto error_no_devname;
Linus Torvalds's avatar
Linus Torvalds committed
91 92 93

	options = (char *) get_zeroed_page(GFP_KERNEL);
	if (!options)
94
		goto error_no_options;
Linus Torvalds's avatar
Linus Torvalds committed
95

96
	vnode = AFS_FS_I(d_inode(mntpt));
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
	if (test_bit(AFS_VNODE_PSEUDODIR, &vnode->flags)) {
		/* if the directory is a pseudo directory, use the d_name */
		static const char afs_root_cell[] = ":root.cell.";
		unsigned size = mntpt->d_name.len;

		ret = -ENOENT;
		if (size < 2 || size > AFS_MAXCELLNAME)
			goto error_no_page;

		if (mntpt->d_name.name[0] == '.') {
			devname[0] = '#';
			memcpy(devname + 1, mntpt->d_name.name, size - 1);
			memcpy(devname + size, afs_root_cell,
			       sizeof(afs_root_cell));
			rwpath = true;
		} else {
			devname[0] = '%';
			memcpy(devname + 1, mntpt->d_name.name, size);
			memcpy(devname + size + 1, afs_root_cell,
			       sizeof(afs_root_cell));
		}
	} else {
		/* read the contents of the AFS special symlink */
120
		loff_t size = i_size_read(d_inode(mntpt));
121 122 123 124 125 126
		char *buf;

		ret = -EINVAL;
		if (size > PAGE_SIZE - 1)
			goto error_no_page;

127
		page = read_mapping_page(d_inode(mntpt)->i_mapping, 0, NULL);
128 129 130 131 132 133 134 135 136
		if (IS_ERR(page)) {
			ret = PTR_ERR(page);
			goto error_no_page;
		}

		ret = -EIO;
		if (PageError(page))
			goto error;

137
		buf = kmap_atomic(page);
138
		memcpy(devname, buf, size);
139
		kunmap_atomic(buf);
140
		put_page(page);
141
		page = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
142 143 144 145 146 147
	}

	/* work out what options we want */
	super = AFS_FS_S(mntpt->d_sb);
	memcpy(options, "cell=", 5);
	strcpy(options + 5, super->volume->cell->name);
148
	if (super->volume->type == AFSVL_RWVOL || rwpath)
Linus Torvalds's avatar
Linus Torvalds committed
149 150 151
		strcat(options, ",rwpath");

	/* try and do the mount */
152
	_debug("--- attempting mount %s -o %s ---", devname, options);
153
	mnt = vfs_submount(mntpt, &afs_fs_type, devname, options);
154
	_debug("--- mount result %p ---", mnt);
Linus Torvalds's avatar
Linus Torvalds committed
155 156 157

	free_page((unsigned long) devname);
	free_page((unsigned long) options);
158
	_leave(" = %p", mnt);
Linus Torvalds's avatar
Linus Torvalds committed
159 160
	return mnt;

161
error:
162
	put_page(page);
163 164 165 166 167
error_no_page:
	free_page((unsigned long) options);
error_no_options:
	free_page((unsigned long) devname);
error_no_devname:
168
	_leave(" = %d", ret);
Linus Torvalds's avatar
Linus Torvalds committed
169
	return ERR_PTR(ret);
170
}
Linus Torvalds's avatar
Linus Torvalds committed
171 172

/*
173
 * handle an automount point
Linus Torvalds's avatar
Linus Torvalds committed
174
 */
175
struct vfsmount *afs_d_automount(struct path *path)
Linus Torvalds's avatar
Linus Torvalds committed
176 177 178
{
	struct vfsmount *newmnt;

Al Viro's avatar
Al Viro committed
179
	_enter("{%pd}", path->dentry);
180

181 182 183
	newmnt = afs_mntpt_do_automount(path->dentry);
	if (IS_ERR(newmnt))
		return newmnt;
Linus Torvalds's avatar
Linus Torvalds committed
184

185 186 187 188
	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);
189
	_leave(" = %p", newmnt);
190
	return newmnt;
191
}
Linus Torvalds's avatar
Linus Torvalds committed
192 193 194 195

/*
 * handle mountpoint expiry timer going off
 */
196
static void afs_mntpt_expiry_timed_out(struct work_struct *work)
Linus Torvalds's avatar
Linus Torvalds committed
197
{
198 199 200 201
	_enter("");

	if (!list_empty(&afs_vfsmounts)) {
		mark_mounts_for_expiry(&afs_vfsmounts);
202 203
		queue_delayed_work(afs_wq, &afs_mntpt_expiry_timer,
				   afs_mntpt_expiry_timeout * HZ);
204 205 206 207
	}

	_leave("");
}
Linus Torvalds's avatar
Linus Torvalds committed
208

209 210 211 212 213 214
/*
 * kill the AFS mountpoint timer if it's still running
 */
void afs_mntpt_kill_timer(void)
{
	_enter("");
Linus Torvalds's avatar
Linus Torvalds committed
215

216
	ASSERT(list_empty(&afs_vfsmounts));
217
	cancel_delayed_work_sync(&afs_mntpt_expiry_timer);
218
}