You need to sign in or sign up before continuing.
v9fs.c 10.1 KB
Newer Older
1 2 3 4 5
/*
 *  linux/fs/9p/v9fs.c
 *
 *  This file contains functions assisting in mapping VFS to 9P2000
 *
6
 *  Copyright (C) 2004-2008 by Eric Van Hensbergen <ericvh@gmail.com>
7 8 9
 *  Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
 *
 *  This program is free software; you can redistribute it and/or modify
10 11
 *  it under the terms of the GNU General Public License version 2
 *  as published by the Free Software Foundation.
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to:
 *  Free Software Foundation
 *  51 Franklin Street, Fifth Floor
 *  Boston, MA  02111-1301  USA
 *
 */

#include <linux/module.h>
#include <linux/errno.h>
#include <linux/fs.h>
29
#include <linux/sched.h>
30 31
#include <linux/parser.h>
#include <linux/idr.h>
32 33
#include <net/9p/9p.h>
#include <net/9p/client.h>
34
#include <net/9p/transport.h>
35 36
#include "v9fs.h"
#include "v9fs_vfs.h"
37 38 39 40
#include "cache.h"

static DEFINE_SPINLOCK(v9fs_sessionlist_lock);
static LIST_HEAD(v9fs_sessionlist);
41 42

/*
43 44 45
 * Option Parsing (code inspired by NFS code)
 *  NOTE: each transport will parse its own options
 */
46 47 48

enum {
	/* Options that take integer arguments */
49
	Opt_debug, Opt_dfltuid, Opt_dfltgid, Opt_afid,
50
	/* String options */
51
	Opt_uname, Opt_remotename, Opt_trans, Opt_cache, Opt_cachetag,
52
	/* Options that take no arguments */
53
	Opt_nodevmap,
54
	/* Cache options */
55
	Opt_cache_loose, Opt_fscache,
L
Latchesar Ionkov 已提交
56 57
	/* Access options */
	Opt_access,
58 59 60 61
	/* Error token */
	Opt_err
};

62
static const match_table_t tokens = {
63
	{Opt_debug, "debug=%x"},
64 65
	{Opt_dfltuid, "dfltuid=%u"},
	{Opt_dfltgid, "dfltgid=%u"},
66
	{Opt_afid, "afid=%u"},
67
	{Opt_uname, "uname=%s"},
68 69
	{Opt_remotename, "aname=%s"},
	{Opt_nodevmap, "nodevmap"},
70
	{Opt_cache, "cache=%s"},
71
	{Opt_cache_loose, "loose"},
72 73
	{Opt_fscache, "fscache"},
	{Opt_cachetag, "cachetag=%s"},
L
Latchesar Ionkov 已提交
74
	{Opt_access, "access=%s"},
75 76 77 78 79 80 81
	{Opt_err, NULL}
};

/**
 * v9fs_parse_options - parse mount options into session structure
 * @v9ses: existing v9fs session information
 *
82
 * Return 0 upon success, -ERRNO upon failure.
83 84
 */

85
static int v9fs_parse_options(struct v9fs_session_info *v9ses, char *opts)
86
{
E
Eric Van Hensbergen 已提交
87
	char *options, *tmp_options;
88
	substring_t args[MAX_OPT_ARGS];
E
Eric Van Hensbergen 已提交
89
	char *p;
90
	int option = 0;
L
Latchesar Ionkov 已提交
91
	char *s, *e;
92
	int ret = 0;
93 94 95 96

	/* setup defaults */
	v9ses->afid = ~0;
	v9ses->debug = 0;
97
	v9ses->cache = 0;
98 99 100
#ifdef CONFIG_9P_FSCACHE
	v9ses->cachetag = NULL;
#endif
101

102
	if (!opts)
103
		return 0;
104

E
Eric Van Hensbergen 已提交
105
	tmp_options = kstrdup(opts, GFP_KERNEL);
106 107
	if (!tmp_options) {
		ret = -ENOMEM;
108
		goto fail_option_alloc;
109
	}
E
Eric Van Hensbergen 已提交
110
	options = tmp_options;
111

112 113 114 115 116
	while ((p = strsep(&options, ",")) != NULL) {
		int token;
		if (!*p)
			continue;
		token = match_token(p, tokens, args);
117
		if (token < Opt_uname) {
118 119
			int r = match_int(&args[0], &option);
			if (r < 0) {
120
				P9_DPRINTK(P9_DEBUG_ERROR,
121
					"integer field, but no integer?\n");
122
				ret = r;
123 124 125 126
				continue;
			}
		}
		switch (token) {
127 128
		case Opt_debug:
			v9ses->debug = option;
129
#ifdef CONFIG_NET_9P_DEBUG
130
			p9_debug_level = option;
131
#endif
132
			break;
133

134 135
		case Opt_dfltuid:
			v9ses->dfltuid = option;
136
			break;
137 138
		case Opt_dfltgid:
			v9ses->dfltgid = option;
139 140 141 142
			break;
		case Opt_afid:
			v9ses->afid = option;
			break;
143
		case Opt_uname:
144
			match_strlcpy(v9ses->uname, &args[0], PATH_MAX);
145 146
			break;
		case Opt_remotename:
147
			match_strlcpy(v9ses->aname, &args[0], PATH_MAX);
148 149 150 151
			break;
		case Opt_nodevmap:
			v9ses->nodev = 1;
			break;
152 153 154
		case Opt_cache_loose:
			v9ses->cache = CACHE_LOOSE;
			break;
155 156 157 158 159 160 161 162 163 164
		case Opt_fscache:
			v9ses->cache = CACHE_FSCACHE;
			break;
		case Opt_cachetag:
#ifdef CONFIG_9P_FSCACHE
			v9ses->cachetag = match_strdup(&args[0]);
#endif
			break;
		case Opt_cache:
			s = match_strdup(&args[0]);
165 166 167 168 169 170
			if (!s) {
				ret = -ENOMEM;
				P9_DPRINTK(P9_DEBUG_ERROR,
				  "problem allocating copy of cache arg\n");
				goto free_and_return;
			}
171 172 173 174 175 176 177 178 179

			if (strcmp(s, "loose") == 0)
				v9ses->cache = CACHE_LOOSE;
			else if (strcmp(s, "fscache") == 0)
				v9ses->cache = CACHE_FSCACHE;
			else
				v9ses->cache = CACHE_NONE;
			kfree(s);
			break;
L
Latchesar Ionkov 已提交
180 181 182

		case Opt_access:
			s = match_strdup(&args[0]);
183 184 185 186 187 188
			if (!s) {
				ret = -ENOMEM;
				P9_DPRINTK(P9_DEBUG_ERROR,
				  "problem allocating copy of access arg\n");
				goto free_and_return;
			}
189

L
Latchesar Ionkov 已提交
190 191 192 193 194 195 196
			v9ses->flags &= ~V9FS_ACCESS_MASK;
			if (strcmp(s, "user") == 0)
				v9ses->flags |= V9FS_ACCESS_USER;
			else if (strcmp(s, "any") == 0)
				v9ses->flags |= V9FS_ACCESS_ANY;
			else {
				v9ses->flags |= V9FS_ACCESS_SINGLE;
197
				v9ses->uid = simple_strtoul(s, &e, 10);
L
Latchesar Ionkov 已提交
198 199 200
				if (*e != '\0')
					v9ses->uid = ~0;
			}
A
Adrian Bunk 已提交
201
			kfree(s);
L
Latchesar Ionkov 已提交
202 203
			break;

204 205 206 207
		default:
			continue;
		}
	}
E
Eric Van Hensbergen 已提交
208

209
free_and_return:
E
Eric Van Hensbergen 已提交
210
	kfree(tmp_options);
211
fail_option_alloc:
212
	return ret;
213 214 215 216 217 218 219 220 221 222
}

/**
 * v9fs_session_init - initialize session
 * @v9ses: session information structure
 * @dev_name: device being mounted
 * @data: options
 *
 */

223
struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses,
224 225 226
		  const char *dev_name, char *data)
{
	int retval = -EINVAL;
227
	struct p9_fid *fid;
228
	int rc;
229

L
Latchesar Ionkov 已提交
230 231
	v9ses->uname = __getname();
	if (!v9ses->uname)
232
		return ERR_PTR(-ENOMEM);
233

L
Latchesar Ionkov 已提交
234 235 236
	v9ses->aname = __getname();
	if (!v9ses->aname) {
		__putname(v9ses->uname);
237
		return ERR_PTR(-ENOMEM);
238 239
	}

240 241 242 243
	spin_lock(&v9fs_sessionlist_lock);
	list_add(&v9ses->slist, &v9fs_sessionlist);
	spin_unlock(&v9fs_sessionlist_lock);

244
	v9ses->flags = V9FS_ACCESS_USER;
L
Latchesar Ionkov 已提交
245 246 247
	strcpy(v9ses->uname, V9FS_DEFUSER);
	strcpy(v9ses->aname, V9FS_DEFANAME);
	v9ses->uid = ~0;
248 249
	v9ses->dfltuid = V9FS_DEFUID;
	v9ses->dfltgid = V9FS_DEFGID;
250

251
	rc = v9fs_parse_options(v9ses, data);
252 253 254 255
	if (rc < 0) {
		retval = rc;
		goto error;
	}
E
Eric Van Hensbergen 已提交
256

257
	v9ses->clnt = p9_client_create(dev_name, data);
258 259 260 261 262
	if (IS_ERR(v9ses->clnt)) {
		retval = PTR_ERR(v9ses->clnt);
		v9ses->clnt = NULL;
		P9_DPRINTK(P9_DEBUG_ERROR, "problem initializing 9p client\n");
		goto error;
263 264
	}

265 266 267 268
	if (p9_is_proto_dotl(v9ses->clnt))
		v9ses->flags |= V9FS_PROTO_2000L;
	else if (p9_is_proto_dotu(v9ses->clnt))
		v9ses->flags |= V9FS_PROTO_2000U;
L
Latchesar Ionkov 已提交
269

270
	v9ses->maxdata = v9ses->clnt->msize - P9_IOHDRSZ;
271

L
Latchesar Ionkov 已提交
272
	/* for legacy mode, fall back to V9FS_ACCESS_ANY */
273
	if (!v9fs_proto_dotu(v9ses) &&
L
Latchesar Ionkov 已提交
274 275 276 277 278 279 280 281 282
		((v9ses->flags&V9FS_ACCESS_MASK) == V9FS_ACCESS_USER)) {

		v9ses->flags &= ~V9FS_ACCESS_MASK;
		v9ses->flags |= V9FS_ACCESS_ANY;
		v9ses->uid = ~0;
	}

	fid = p9_client_attach(v9ses->clnt, NULL, v9ses->uname, ~0,
							v9ses->aname);
283 284 285 286 287
	if (IS_ERR(fid)) {
		retval = PTR_ERR(fid);
		fid = NULL;
		P9_DPRINTK(P9_DEBUG_ERROR, "cannot attach\n");
		goto error;
288 289
	}

L
Latchesar Ionkov 已提交
290 291 292 293 294
	if ((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_SINGLE)
		fid->uid = v9ses->uid;
	else
		fid->uid = ~0;

295 296 297 298 299
#ifdef CONFIG_9P_FSCACHE
	/* register the session for caching */
	v9fs_cache_session_get_cookie(v9ses);
#endif

300
	return fid;
301

302 303
error:
	return ERR_PTR(retval);
304 305 306 307 308 309 310 311 312 313
}

/**
 * v9fs_session_close - shutdown a session
 * @v9ses: session information structure
 *
 */

void v9fs_session_close(struct v9fs_session_info *v9ses)
{
314 315 316
	if (v9ses->clnt) {
		p9_client_destroy(v9ses->clnt);
		v9ses->clnt = NULL;
317
	}
318

319 320 321 322 323 324
#ifdef CONFIG_9P_FSCACHE
	if (v9ses->fscache) {
		v9fs_cache_session_put_cookie(v9ses);
		kfree(v9ses->cachetag);
	}
#endif
L
Latchesar Ionkov 已提交
325 326
	__putname(v9ses->uname);
	__putname(v9ses->aname);
327 328 329 330

	spin_lock(&v9fs_sessionlist_lock);
	list_del(&v9ses->slist);
	spin_unlock(&v9fs_sessionlist_lock);
331 332
}

333
/**
E
Eric Van Hensbergen 已提交
334 335 336 337
 * v9fs_session_cancel - terminate a session
 * @v9ses: session to terminate
 *
 * mark transport as disconnected and cancel all pending requests.
338
 */
E
Eric Van Hensbergen 已提交
339

340
void v9fs_session_cancel(struct v9fs_session_info *v9ses) {
341 342
	P9_DPRINTK(P9_DEBUG_ERROR, "cancel session %p\n", v9ses);
	p9_client_disconnect(v9ses->clnt);
343 344
}

345 346
extern int v9fs_error_init(void);

347 348 349
static struct kobject *v9fs_kobj;

#ifdef CONFIG_9P_FSCACHE
350
/**
351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426
 * caches_show - list caches associated with a session
 *
 * Returns the size of buffer written.
 */

static ssize_t caches_show(struct kobject *kobj,
			   struct kobj_attribute *attr,
			   char *buf)
{
	ssize_t n = 0, count = 0, limit = PAGE_SIZE;
	struct v9fs_session_info *v9ses;

	spin_lock(&v9fs_sessionlist_lock);
	list_for_each_entry(v9ses, &v9fs_sessionlist, slist) {
		if (v9ses->cachetag) {
			n = snprintf(buf, limit, "%s\n", v9ses->cachetag);
			if (n < 0) {
				count = n;
				break;
			}

			count += n;
			limit -= n;
		}
	}

	spin_unlock(&v9fs_sessionlist_lock);
	return count;
}

static struct kobj_attribute v9fs_attr_cache = __ATTR_RO(caches);
#endif /* CONFIG_9P_FSCACHE */

static struct attribute *v9fs_attrs[] = {
#ifdef CONFIG_9P_FSCACHE
	&v9fs_attr_cache.attr,
#endif
	NULL,
};

static struct attribute_group v9fs_attr_group = {
	.attrs = v9fs_attrs,
};

/**
 * v9fs_sysfs_init - Initialize the v9fs sysfs interface
 *
 */

static int v9fs_sysfs_init(void)
{
	v9fs_kobj = kobject_create_and_add("9p", fs_kobj);
	if (!v9fs_kobj)
		return -ENOMEM;

	if (sysfs_create_group(v9fs_kobj, &v9fs_attr_group)) {
		kobject_put(v9fs_kobj);
		return -ENOMEM;
	}

	return 0;
}

/**
 * v9fs_sysfs_cleanup - Unregister the v9fs sysfs interface
 *
 */

static void v9fs_sysfs_cleanup(void)
{
	sysfs_remove_group(v9fs_kobj, &v9fs_attr_group);
	kobject_put(v9fs_kobj);
}

/**
 * init_v9fs - Initialize module
427 428 429 430 431
 *
 */

static int __init init_v9fs(void)
{
432
	int err;
433
	printk(KERN_INFO "Installing v9fs 9p2000 file system support\n");
E
Eric Van Hensbergen 已提交
434
	/* TODO: Setup list of registered trasnport modules */
435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461
	err = register_filesystem(&v9fs_fs_type);
	if (err < 0) {
		printk(KERN_ERR "Failed to register filesystem\n");
		return err;
	}

	err = v9fs_cache_register();
	if (err < 0) {
		printk(KERN_ERR "Failed to register v9fs for caching\n");
		goto out_fs_unreg;
	}

	err = v9fs_sysfs_init();
	if (err < 0) {
		printk(KERN_ERR "Failed to register with sysfs\n");
		goto out_sysfs_cleanup;
	}

	return 0;

out_sysfs_cleanup:
	v9fs_sysfs_cleanup();

out_fs_unreg:
	unregister_filesystem(&v9fs_fs_type);

	return err;
462 463 464
}

/**
465
 * exit_v9fs - shutdown module
466 467 468 469 470
 *
 */

static void __exit exit_v9fs(void)
{
471 472
	v9fs_sysfs_cleanup();
	v9fs_cache_unregister();
473 474 475 476 477 478
	unregister_filesystem(&v9fs_fs_type);
}

module_init(init_v9fs)
module_exit(exit_v9fs)

479
MODULE_AUTHOR("Latchesar Ionkov <lucho@ionkov.net>");
480 481 482
MODULE_AUTHOR("Eric Van Hensbergen <ericvh@gmail.com>");
MODULE_AUTHOR("Ron Minnich <rminnich@lanl.gov>");
MODULE_LICENSE("GPL");