vfs_inode.c 30.2 KB
Newer Older
1
2
3
/*
 *  linux/fs/9p/vfs_inode.c
 *
4
 * This file contains vfs inode ops for the 9P2000 protocol.
5
6
7
8
9
 *
 *  Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
 *  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
29
30
31
32
33
34
35
 *
 *  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>
#include <linux/file.h>
#include <linux/pagemap.h>
#include <linux/stat.h>
#include <linux/string.h>
#include <linux/inet.h>
#include <linux/namei.h>
#include <linux/idr.h>
Alexey Dobriyan's avatar
Alexey Dobriyan committed
36
#include <linux/sched.h>
37
#include <linux/slab.h>
38
#include <linux/xattr.h>
39
#include <linux/posix_acl.h>
40
41
#include <net/9p/9p.h>
#include <net/9p/client.h>
42
43
44
45

#include "v9fs.h"
#include "v9fs_vfs.h"
#include "fid.h"
46
#include "cache.h"
47
#include "xattr.h"
48
#include "acl.h"
49

50
static const struct inode_operations v9fs_dir_inode_operations;
51
static const struct inode_operations v9fs_dir_inode_operations_dotu;
52
53
static const struct inode_operations v9fs_file_inode_operations;
static const struct inode_operations v9fs_symlink_inode_operations;
54

55
56
57
58
59
60
61
/**
 * unixmode2p9mode - convert unix mode bits to plan 9
 * @v9ses: v9fs session information
 * @mode: mode to convert
 *
 */

62
static int unixmode2p9mode(struct v9fs_session_info *v9ses, int mode)
63
64
65
66
{
	int res;
	res = mode & 0777;
	if (S_ISDIR(mode))
67
		res |= P9_DMDIR;
68
	if (v9fs_proto_dotu(v9ses)) {
69
		if (S_ISLNK(mode))
70
			res |= P9_DMSYMLINK;
71
72
		if (v9ses->nodev == 0) {
			if (S_ISSOCK(mode))
73
				res |= P9_DMSOCKET;
74
			if (S_ISFIFO(mode))
75
				res |= P9_DMNAMEDPIPE;
76
			if (S_ISBLK(mode))
77
				res |= P9_DMDEVICE;
78
			if (S_ISCHR(mode))
79
				res |= P9_DMDEVICE;
80
81
82
		}

		if ((mode & S_ISUID) == S_ISUID)
83
			res |= P9_DMSETUID;
84
		if ((mode & S_ISGID) == S_ISGID)
85
			res |= P9_DMSETGID;
86
87
		if ((mode & S_ISVTX) == S_ISVTX)
			res |= P9_DMSETVTX;
88
89
		if ((mode & P9_DMLINK))
			res |= P9_DMLINK;
90
91
92
93
94
95
96
97
98
99
100
101
	}

	return res;
}

/**
 * p9mode2unixmode- convert plan9 mode bits to unix mode bits
 * @v9ses: v9fs session information
 * @mode: mode to convert
 *
 */

102
static int p9mode2unixmode(struct v9fs_session_info *v9ses, int mode)
103
104
105
106
107
{
	int res;

	res = mode & 0777;

108
	if ((mode & P9_DMDIR) == P9_DMDIR)
109
		res |= S_IFDIR;
110
	else if ((mode & P9_DMSYMLINK) && (v9fs_proto_dotu(v9ses)))
111
		res |= S_IFLNK;
112
	else if ((mode & P9_DMSOCKET) && (v9fs_proto_dotu(v9ses))
113
114
		 && (v9ses->nodev == 0))
		res |= S_IFSOCK;
115
	else if ((mode & P9_DMNAMEDPIPE) && (v9fs_proto_dotu(v9ses))
116
117
		 && (v9ses->nodev == 0))
		res |= S_IFIFO;
118
	else if ((mode & P9_DMDEVICE) && (v9fs_proto_dotu(v9ses))
119
120
121
122
123
		 && (v9ses->nodev == 0))
		res |= S_IFBLK;
	else
		res |= S_IFREG;

124
	if (v9fs_proto_dotu(v9ses)) {
125
		if ((mode & P9_DMSETUID) == P9_DMSETUID)
126
127
			res |= S_ISUID;

128
		if ((mode & P9_DMSETGID) == P9_DMSETGID)
129
			res |= S_ISGID;
130
131
132

		if ((mode & P9_DMSETVTX) == P9_DMSETVTX)
			res |= S_ISVTX;
133
134
135
136
137
	}

	return res;
}

138
139
140
/**
 * v9fs_uflags2omode- convert posix open flags to plan 9 mode bits
 * @uflags: flags to convert
141
 * @extended: if .u extensions are active
142
143
 */

144
int v9fs_uflags2omode(int uflags, int extended)
145
146
147
148
149
150
151
{
	int ret;

	ret = 0;
	switch (uflags&3) {
	default:
	case O_RDONLY:
152
		ret = P9_OREAD;
153
154
155
		break;

	case O_WRONLY:
156
		ret = P9_OWRITE;
157
158
159
		break;

	case O_RDWR:
160
		ret = P9_ORDWR;
161
162
163
164
		break;
	}

	if (uflags & O_TRUNC)
165
		ret |= P9_OTRUNC;
166

167
168
169
170
171
172
173
	if (extended) {
		if (uflags & O_EXCL)
			ret |= P9_OEXCL;

		if (uflags & O_APPEND)
			ret |= P9_OAPPEND;
	}
174
175
176
177

	return ret;
}

178
/**
179
180
 * v9fs_blank_wstat - helper function to setup a 9P stat structure
 * @wstat: structure to initialize
181
182
183
 *
 */

184
void
185
v9fs_blank_wstat(struct p9_wstat *wstat)
186
{
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
	wstat->type = ~0;
	wstat->dev = ~0;
	wstat->qid.type = ~0;
	wstat->qid.version = ~0;
	*((long long *)&wstat->qid.path) = ~0;
	wstat->mode = ~0;
	wstat->atime = ~0;
	wstat->mtime = ~0;
	wstat->length = ~0;
	wstat->name = NULL;
	wstat->uid = NULL;
	wstat->gid = NULL;
	wstat->muid = NULL;
	wstat->n_uid = ~0;
	wstat->n_gid = ~0;
	wstat->n_muid = ~0;
	wstat->extension = NULL;
204
205
}

206
207
208
209
210
211
/**
 * v9fs_alloc_inode - helper function to allocate an inode
 *
 */
struct inode *v9fs_alloc_inode(struct super_block *sb)
{
Aneesh Kumar K.V's avatar
Aneesh Kumar K.V committed
212
213
214
215
	struct v9fs_inode *v9inode;
	v9inode = (struct v9fs_inode *)kmem_cache_alloc(v9fs_inode_cache,
							GFP_KERNEL);
	if (!v9inode)
216
		return NULL;
Aneesh Kumar K.V's avatar
Aneesh Kumar K.V committed
217
218
219
220
221
#ifdef CONFIG_9P_FSCACHE
	v9inode->fscache = NULL;
	v9inode->fscache_key = NULL;
	spin_lock_init(&v9inode->fscache_lock);
#endif
222
	v9inode->writeback_fid = NULL;
223
	v9inode->cache_validity = 0;
Aneesh Kumar K.V's avatar
Aneesh Kumar K.V committed
224
	return &v9inode->vfs_inode;
225
226
227
228
229
230
231
}

/**
 * v9fs_destroy_inode - destroy an inode
 *
 */

Nick Piggin's avatar
Nick Piggin committed
232
static void v9fs_i_callback(struct rcu_head *head)
233
{
Nick Piggin's avatar
Nick Piggin committed
234
235
	struct inode *inode = container_of(head, struct inode, i_rcu);
	INIT_LIST_HEAD(&inode->i_dentry);
Aneesh Kumar K.V's avatar
Aneesh Kumar K.V committed
236
	kmem_cache_free(v9fs_inode_cache, V9FS_I(inode));
237
}
Nick Piggin's avatar
Nick Piggin committed
238
239
240
241
242

void v9fs_destroy_inode(struct inode *inode)
{
	call_rcu(&inode->i_rcu, v9fs_i_callback);
}
243

Aneesh Kumar K.V's avatar
Aneesh Kumar K.V committed
244
245
int v9fs_init_inode(struct v9fs_session_info *v9ses,
		    struct inode *inode, int mode)
246
{
Aneesh Kumar K.V's avatar
Aneesh Kumar K.V committed
247
	int err = 0;
248

249
	inode_init_owner(inode, NULL, mode);
250
251
252
253
254
255
256
257
258
259
	inode->i_blocks = 0;
	inode->i_rdev = 0;
	inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
	inode->i_mapping->a_ops = &v9fs_addr_operations;

	switch (mode & S_IFMT) {
	case S_IFIFO:
	case S_IFBLK:
	case S_IFCHR:
	case S_IFSOCK:
M. Mohan Kumar's avatar
M. Mohan Kumar committed
260
261
262
263
264
265
266
		if (v9fs_proto_dotl(v9ses)) {
			inode->i_op = &v9fs_file_inode_operations_dotl;
			inode->i_fop = &v9fs_file_operations_dotl;
		} else if (v9fs_proto_dotu(v9ses)) {
			inode->i_op = &v9fs_file_inode_operations;
			inode->i_fop = &v9fs_file_operations;
		} else {
267
			P9_DPRINTK(P9_DEBUG_ERROR,
268
269
270
				   "special files without extended mode\n");
			err = -EINVAL;
			goto error;
271
		}
272
273
274
		init_special_inode(inode, inode->i_mode, inode->i_rdev);
		break;
	case S_IFREG:
275
276
		if (v9fs_proto_dotl(v9ses)) {
			inode->i_op = &v9fs_file_inode_operations_dotl;
277
278
279
280
281
			if (v9ses->cache)
				inode->i_fop =
					&v9fs_cached_file_operations_dotl;
			else
				inode->i_fop = &v9fs_file_operations_dotl;
282
283
		} else {
			inode->i_op = &v9fs_file_inode_operations;
284
285
286
287
			if (v9ses->cache)
				inode->i_fop = &v9fs_cached_file_operations;
			else
				inode->i_fop = &v9fs_file_operations;
288
289
		}

290
291
		break;
	case S_IFLNK:
292
293
294
		if (!v9fs_proto_dotu(v9ses) && !v9fs_proto_dotl(v9ses)) {
			P9_DPRINTK(P9_DEBUG_ERROR, "extended modes used with "
						"legacy protocol.\n");
295
296
297
			err = -EINVAL;
			goto error;
		}
298
299
300
301
302
303

		if (v9fs_proto_dotl(v9ses))
			inode->i_op = &v9fs_symlink_inode_operations_dotl;
		else
			inode->i_op = &v9fs_symlink_inode_operations;

304
305
306
		break;
	case S_IFDIR:
		inc_nlink(inode);
307
308
309
310
		if (v9fs_proto_dotl(v9ses))
			inode->i_op = &v9fs_dir_inode_operations_dotl;
		else if (v9fs_proto_dotu(v9ses))
			inode->i_op = &v9fs_dir_inode_operations_dotu;
311
312
		else
			inode->i_op = &v9fs_dir_inode_operations;
313
314
315
316
317
318

		if (v9fs_proto_dotl(v9ses))
			inode->i_fop = &v9fs_dir_operations_dotl;
		else
			inode->i_fop = &v9fs_dir_operations;

319
320
321
322
323
324
		break;
	default:
		P9_DPRINTK(P9_DEBUG_ERROR, "BAD mode 0x%x S_IFMT 0x%x\n",
			   mode, mode & S_IFMT);
		err = -EINVAL;
		goto error;
325
	}
Aneesh Kumar K.V's avatar
Aneesh Kumar K.V committed
326
327
error:
	return err;
328

Aneesh Kumar K.V's avatar
Aneesh Kumar K.V committed
329
}
330

Aneesh Kumar K.V's avatar
Aneesh Kumar K.V committed
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
/**
 * v9fs_get_inode - helper function to setup an inode
 * @sb: superblock
 * @mode: mode to setup inode with
 *
 */

struct inode *v9fs_get_inode(struct super_block *sb, int mode)
{
	int err;
	struct inode *inode;
	struct v9fs_session_info *v9ses = sb->s_fs_info;

	P9_DPRINTK(P9_DEBUG_VFS, "super block: %p mode: %o\n", sb, mode);

	inode = new_inode(sb);
	if (!inode) {
		P9_EPRINTK(KERN_WARNING, "Problem allocating inode\n");
		return ERR_PTR(-ENOMEM);
	}
	err = v9fs_init_inode(v9ses, inode, mode);
	if (err) {
		iput(inode);
		return ERR_PTR(err);
	}
	return inode;
357
358
}

359
/*
360
361
362
363
static struct v9fs_fid*
v9fs_clone_walk(struct v9fs_session_info *v9ses, u32 fid, struct dentry *dentry)
{
	int err;
364
	int nfid;
365
366
367
368
369
	struct v9fs_fid *ret;
	struct v9fs_fcall *fcall;

	nfid = v9fs_get_idpool(&v9ses->fidpool);
	if (nfid < 0) {
370
		eprintk(KERN_WARNING, "no free fids available\n");
371
		return ERR_PTR(-ENOSPC);
372
373
	}

374
375
376
377
	err = v9fs_t_walk(v9ses, fid, nfid, (char *) dentry->d_name.name,
		&fcall);

	if (err < 0) {
378
379
380
		if (fcall && fcall->id == RWALK)
			goto clunk_fid;

381
382
383
		PRINT_FCALL_ERROR("walk error", fcall);
		v9fs_put_idpool(nfid, &v9ses->fidpool);
		goto error;
384
	}
385

386
387
	kfree(fcall);
	fcall = NULL;
388
389
390
391
392
	ret = v9fs_fid_create(v9ses, nfid);
	if (!ret) {
		err = -ENOMEM;
		goto clunk_fid;
	}
393

394
395
396
397
	err = v9fs_fid_insert(ret, dentry);
	if (err < 0) {
		v9fs_fid_destroy(ret);
		goto clunk_fid;
398
	}
399

400
	return ret;
401

402
403
clunk_fid:
	v9fs_t_clunk(v9ses, nfid);
404

405
406
407
408
error:
	kfree(fcall);
	return ERR_PTR(err);
}
409
*/
410

411
412
413
414
415
416

/**
 * v9fs_clear_inode - release an inode
 * @inode: inode to release
 *
 */
417
void v9fs_evict_inode(struct inode *inode)
418
{
419
420
	struct v9fs_inode *v9inode = V9FS_I(inode);

421
422
	truncate_inode_pages(inode->i_mapping, 0);
	end_writeback(inode);
423
424
425
426
427
	filemap_fdatawrite(inode->i_mapping);

#ifdef CONFIG_9P_FSCACHE
	v9fs_cache_inode_put_cookie(inode);
#endif
428
429
430
431
	/* clunk the fid stashed in writeback_fid */
	if (v9inode->writeback_fid) {
		p9_client_clunk(v9inode->writeback_fid);
		v9inode->writeback_fid = NULL;
432
	}
433
434
}

Aneesh Kumar K.V's avatar
Aneesh Kumar K.V committed
435
436
437
static struct inode *v9fs_qid_iget(struct super_block *sb,
				   struct p9_qid *qid,
				   struct p9_wstat *st)
438
{
Aneesh Kumar K.V's avatar
Aneesh Kumar K.V committed
439
440
441
442
	int retval, umode;
	unsigned long i_ino;
	struct inode *inode;
	struct v9fs_session_info *v9ses = sb->s_fs_info;
443

Aneesh Kumar K.V's avatar
Aneesh Kumar K.V committed
444
445
446
447
448
449
450
451
452
453
454
	i_ino = v9fs_qid2ino(qid);
	inode = iget_locked(sb, i_ino);
	if (!inode)
		return ERR_PTR(-ENOMEM);
	if (!(inode->i_state & I_NEW))
		return inode;
	/*
	 * initialize the inode with the stat info
	 * FIXME!! we may need support for stale inodes
	 * later.
	 */
455
	umode = p9mode2unixmode(v9ses, st->mode);
Aneesh Kumar K.V's avatar
Aneesh Kumar K.V committed
456
457
	retval = v9fs_init_inode(v9ses, inode, umode);
	if (retval)
458
		goto error;
459

Aneesh Kumar K.V's avatar
Aneesh Kumar K.V committed
460
	v9fs_stat2inode(st, inode, sb);
461
#ifdef CONFIG_9P_FSCACHE
Aneesh Kumar K.V's avatar
Aneesh Kumar K.V committed
462
	v9fs_fscache_set_key(inode, &st->qid);
Aneesh Kumar K.V's avatar
Aneesh Kumar K.V committed
463
	v9fs_cache_inode_get_cookie(inode);
464
#endif
Aneesh Kumar K.V's avatar
Aneesh Kumar K.V committed
465
466
	unlock_new_inode(inode);
	return inode;
467
error:
Aneesh Kumar K.V's avatar
Aneesh Kumar K.V committed
468
469
470
471
472
473
474
	unlock_new_inode(inode);
	iput(inode);
	return ERR_PTR(retval);

}

struct inode *
Aneesh Kumar K.V's avatar
Aneesh Kumar K.V committed
475
476
v9fs_inode_from_fid(struct v9fs_session_info *v9ses, struct p9_fid *fid,
		    struct super_block *sb)
Aneesh Kumar K.V's avatar
Aneesh Kumar K.V committed
477
478
479
480
481
482
483
484
485
{
	struct p9_wstat *st;
	struct inode *inode = NULL;

	st = p9_client_stat(fid);
	if (IS_ERR(st))
		return ERR_CAST(st);

	inode = v9fs_qid_iget(sb, &st->qid, st);
486
	p9stat_free(st);
487
	kfree(st);
Aneesh Kumar K.V's avatar
Aneesh Kumar K.V committed
488
	return inode;
489
490
491
492
}

/**
 * v9fs_remove - helper function to remove files and directories
493
494
495
 * @dir: directory inode that is being deleted
 * @file:  dentry that is being deleted
 * @rmdir: removing a directory
496
497
498
499
500
 *
 */

static int v9fs_remove(struct inode *dir, struct dentry *file, int rmdir)
{
Aneesh Kumar K.V's avatar
Aneesh Kumar K.V committed
501
	int retval;
502
503
	struct inode *file_inode;
	struct p9_fid *v9fid;
504

505
	P9_DPRINTK(P9_DEBUG_VFS, "inode: %p dentry: %p rmdir: %d\n", dir, file,
506
507
508
		rmdir);

	file_inode = file->d_inode;
509
	v9fid = v9fs_fid_clone(file);
Latchesar Ionkov's avatar
Latchesar Ionkov committed
510
	if (IS_ERR(v9fid))
511
		return PTR_ERR(v9fid);
512

Aneesh Kumar K.V's avatar
Aneesh Kumar K.V committed
513
	retval = p9_client_remove(v9fid);
514
515
516
517
518
519
520
521
522
523
	if (!retval) {
		/*
		 * directories on unlink should have zero
		 * link count
		 */
		if (rmdir) {
			clear_nlink(file_inode);
			drop_nlink(dir);
		} else
			drop_nlink(file_inode);
524
		v9fs_invalidate_inode_attr(file_inode);
525
	}
Aneesh Kumar K.V's avatar
Aneesh Kumar K.V committed
526
	return retval;
527
528
529
}

/**
530
 * v9fs_create - Create a file
531
532
 * @v9ses: session information
 * @dir: directory that dentry is being created in
533
 * @dentry:  dentry that is being created
Abhishek Kulkarni's avatar
Abhishek Kulkarni committed
534
 * @extension: 9p2000.u extension string to support devices, etc.
535
536
 * @perm: create permissions
 * @mode: open mode
537
538
 *
 */
539
540
541
static struct p9_fid *
v9fs_create(struct v9fs_session_info *v9ses, struct inode *dir,
		struct dentry *dentry, char *extension, u32 perm, u8 mode)
542
{
543
	int err;
544
545
	char *name;
	struct p9_fid *dfid, *ofid, *fid;
546
547
	struct inode *inode;

548
549
	P9_DPRINTK(P9_DEBUG_VFS, "name %s\n", dentry->d_name.name);

550
551
552
553
	err = 0;
	ofid = NULL;
	fid = NULL;
	name = (char *) dentry->d_name.name;
554
	dfid = v9fs_fid_lookup(dentry->d_parent);
Latchesar Ionkov's avatar
Latchesar Ionkov committed
555
	if (IS_ERR(dfid)) {
556
		err = PTR_ERR(dfid);
557
558
		P9_DPRINTK(P9_DEBUG_VFS, "fid lookup failed %d\n", err);
		return ERR_PTR(err);
559
	}
560

561
562
563
564
	/* clone a fid to use for creation */
	ofid = p9_client_walk(dfid, 0, NULL, 1);
	if (IS_ERR(ofid)) {
		err = PTR_ERR(ofid);
565
		P9_DPRINTK(P9_DEBUG_VFS, "p9_client_walk failed %d\n", err);
566
		return ERR_PTR(err);
567
	}
568

569
	err = p9_client_fcreate(ofid, name, perm, mode, extension);
570
571
	if (err < 0) {
		P9_DPRINTK(P9_DEBUG_VFS, "p9_client_fcreate failed %d\n", err);
572
		goto error;
573
	}
574

575
	/* now walk from the parent so we can get unopened fid */
576
	fid = p9_client_walk(dfid, 1, &name, 1);
577
578
	if (IS_ERR(fid)) {
		err = PTR_ERR(fid);
579
		P9_DPRINTK(P9_DEBUG_VFS, "p9_client_walk failed %d\n", err);
580
		fid = NULL;
581
		goto error;
582
	}
583

584
	/* instantiate inode and assign the unopened fid to the dentry */
Aneesh Kumar K.V's avatar
Aneesh Kumar K.V committed
585
	inode = v9fs_get_inode_from_fid(v9ses, fid, dir->i_sb);
586
587
	if (IS_ERR(inode)) {
		err = PTR_ERR(inode);
588
		P9_DPRINTK(P9_DEBUG_VFS, "inode creation failed %d\n", err);
589
590
591
		goto error;
	}
	d_instantiate(dentry, inode);
592
593
594
595
	err = v9fs_fid_add(dentry, fid);
	if (err < 0)
		goto error;

596
	return ofid;
597

598
599
600
601
602
603
604
605
606
607
608
609
error:
	if (ofid)
		p9_client_clunk(ofid);

	if (fid)
		p9_client_clunk(fid);

	return ERR_PTR(err);
}

/**
 * v9fs_vfs_create - VFS hook to create files
610
 * @dir: directory inode that is being created
611
612
613
614
615
 * @dentry:  dentry that is being deleted
 * @mode: create permissions
 * @nd: path information
 *
 */
616

617
618
619
620
621
622
623
static int
v9fs_vfs_create(struct inode *dir, struct dentry *dentry, int mode,
		struct nameidata *nd)
{
	int err;
	u32 perm;
	int flags;
624
625
	struct file *filp;
	struct v9fs_inode *v9inode;
626
	struct v9fs_session_info *v9ses;
627
	struct p9_fid *fid, *inode_fid;
628
629
630
631
632
633
634
635
636
637
638

	err = 0;
	fid = NULL;
	v9ses = v9fs_inode2v9ses(dir);
	perm = unixmode2p9mode(v9ses, mode);
	if (nd && nd->flags & LOOKUP_OPEN)
		flags = nd->intent.open.flags - 1;
	else
		flags = O_RDWR;

	fid = v9fs_create(v9ses, dir, dentry, NULL, perm,
639
640
				v9fs_uflags2omode(flags,
						v9fs_proto_dotu(v9ses)));
641
642
643
644
645
646
647
648
	if (IS_ERR(fid)) {
		err = PTR_ERR(fid);
		fid = NULL;
		goto error;
	}

	/* if we are opening a file, assign the open fid to the file */
	if (nd && nd->flags & LOOKUP_OPEN) {
649
650
		v9inode = V9FS_I(dentry->d_inode);
		if (v9ses->cache && !v9inode->writeback_fid) {
651
			/*
652
			 * clone a fid and add it to writeback_fid
653
654
655
656
657
658
659
660
661
662
			 * we do it during open time instead of
			 * page dirty time via write_begin/page_mkwrite
			 * because we want write after unlink usecase
			 * to work.
			 */
			inode_fid = v9fs_writeback_fid(dentry);
			if (IS_ERR(inode_fid)) {
				err = PTR_ERR(inode_fid);
				goto error;
			}
663
			v9inode->writeback_fid = (void *) inode_fid;
664
		}
665
		filp = lookup_instantiate_filp(nd, dentry, generic_file_open);
666
		if (IS_ERR(filp)) {
667
668
			err = PTR_ERR(filp);
			goto error;
669
670
		}

671
		filp->private_data = fid;
672
673
674
675
#ifdef CONFIG_9P_FSCACHE
		if (v9ses->cache)
			v9fs_cache_inode_set_cookie(dentry->d_inode, filp);
#endif
676
677
	} else
		p9_client_clunk(fid);
678
679
680
681

	return 0;

error:
682
683
	if (fid)
		p9_client_clunk(fid);
684
685

	return err;
686
687
688
689
}

/**
 * v9fs_vfs_mkdir - VFS mkdir hook to create a directory
690
 * @dir:  inode that is being unlinked
691
692
693
694
695
 * @dentry: dentry that is being unlinked
 * @mode: mode for new directory
 *
 */

696
static int v9fs_vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
697
{
698
	int err;
699
	u32 perm;
700
	struct v9fs_session_info *v9ses;
701
	struct p9_fid *fid;
702

703
704
	P9_DPRINTK(P9_DEBUG_VFS, "name %s\n", dentry->d_name.name);
	err = 0;
705
706
	v9ses = v9fs_inode2v9ses(dir);
	perm = unixmode2p9mode(v9ses, mode | S_IFDIR);
707
708
709
710
	fid = v9fs_create(v9ses, dir, dentry, NULL, perm, P9_OREAD);
	if (IS_ERR(fid)) {
		err = PTR_ERR(fid);
		fid = NULL;
711
712
	} else
		inc_nlink(dir);
713

714
715
	if (fid)
		p9_client_clunk(fid);
716
717

	return err;
718
719
720
721
722
723
724
725
726
727
}

/**
 * v9fs_vfs_lookup - VFS lookup hook to "walk" to a new inode
 * @dir:  inode that is being walked from
 * @dentry: dentry that is being walked to?
 * @nameidata: path data
 *
 */

728
struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry,
729
730
731
732
				      struct nameidata *nameidata)
{
	struct super_block *sb;
	struct v9fs_session_info *v9ses;
733
	struct p9_fid *dfid, *fid;
734
	struct inode *inode;
735
	char *name;
736
737
	int result = 0;

738
	P9_DPRINTK(P9_DEBUG_VFS, "dir: %p dentry: (%s) %p nameidata: %p\n",
739
		dir, dentry->d_name.name, dentry, nameidata);
740

741
742
743
	if (dentry->d_name.len > NAME_MAX)
		return ERR_PTR(-ENAMETOOLONG);

744
745
	sb = dir->i_sb;
	v9ses = v9fs_inode2v9ses(dir);
746
	/* We can walk d_parent because we hold the dir->i_mutex */
747
748
	dfid = v9fs_fid_lookup(dentry->d_parent);
	if (IS_ERR(dfid))
749
		return ERR_CAST(dfid);
750
751
752
753
754

	name = (char *) dentry->d_name.name;
	fid = p9_client_walk(dfid, 1, &name, 1);
	if (IS_ERR(fid)) {
		result = PTR_ERR(fid);
755
		if (result == -ENOENT) {
756
757
			inode = NULL;
			goto inst_out;
758
759
		}

760
		return ERR_PTR(result);
761
762
	}

Aneesh Kumar K.V's avatar
Aneesh Kumar K.V committed
763
	inode = v9fs_get_inode_from_fid(v9ses, fid, dir->i_sb);
764
765
766
767
	if (IS_ERR(inode)) {
		result = PTR_ERR(inode);
		inode = NULL;
		goto error;
768
769
	}

770
	result = v9fs_fid_add(dentry, fid);
771
	if (result < 0)
772
		goto error_iput;
773

774
inst_out:
775
776
777
	d_add(dentry, inode);
	return NULL;

778
779
error_iput:
	iput(inode);
780
error:
781
	p9_client_clunk(fid);
782

783
784
785
786
787
788
	return ERR_PTR(result);
}

/**
 * v9fs_vfs_unlink - VFS unlink hook to delete an inode
 * @i:  inode that is being unlinked
789
 * @d: dentry that is being unlinked
790
791
792
 *
 */

793
int v9fs_vfs_unlink(struct inode *i, struct dentry *d)
794
795
796
797
798
799
800
{
	return v9fs_remove(i, d, 0);
}

/**
 * v9fs_vfs_rmdir - VFS unlink hook to delete a directory
 * @i:  inode that is being unlinked
801
 * @d: dentry that is being unlinked
802
803
804
 *
 */

805
int v9fs_vfs_rmdir(struct inode *i, struct dentry *d)
806
807
808
809
810
811
812
813
814
815
816
817
818
{
	return v9fs_remove(i, d, 1);
}

/**
 * v9fs_vfs_rename - VFS hook to rename an inode
 * @old_dir:  old dir inode
 * @old_dentry: old dentry
 * @new_dir: new dir inode
 * @new_dentry: new dentry
 *
 */

819
int
820
821
822
v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
		struct inode *new_dir, struct dentry *new_dentry)
{
823
	struct inode *old_inode;
824
	struct inode *new_inode;
825
826
827
828
829
830
	struct v9fs_session_info *v9ses;
	struct p9_fid *oldfid;
	struct p9_fid *olddirfid;
	struct p9_fid *newdirfid;
	struct p9_wstat wstat;
	int retval;
831

832
833
834
	P9_DPRINTK(P9_DEBUG_VFS, "\n");
	retval = 0;
	old_inode = old_dentry->d_inode;
835
	new_inode = new_dentry->d_inode;
836
837
	v9ses = v9fs_inode2v9ses(old_inode);
	oldfid = v9fs_fid_lookup(old_dentry);
Latchesar Ionkov's avatar
Latchesar Ionkov committed
838
	if (IS_ERR(oldfid))
839
840
841
		return PTR_ERR(oldfid);

	olddirfid = v9fs_fid_clone(old_dentry->d_parent);
Latchesar Ionkov's avatar
Latchesar Ionkov committed
842
	if (IS_ERR(olddirfid)) {
843
		retval = PTR_ERR(olddirfid);
844
		goto done;
845
846
847
	}

	newdirfid = v9fs_fid_clone(new_dentry->d_parent);
Latchesar Ionkov's avatar
Latchesar Ionkov committed
848
	if (IS_ERR(newdirfid)) {
849
		retval = PTR_ERR(newdirfid);
850
		goto clunk_olddir;
851
852
	}

853
	down_write(&v9ses->rename_sem);
854
855
856
857
858
859
	if (v9fs_proto_dotl(v9ses)) {
		retval = p9_client_rename(oldfid, newdirfid,
					(char *) new_dentry->d_name.name);
		if (retval != -ENOSYS)
			goto clunk_newdir;
	}
860
861
862
863
	if (old_dentry->d_parent != new_dentry->d_parent) {
		/*
		 * 9P .u can only handle file rename in the same directory
		 */
864

865
866
		P9_DPRINTK(P9_DEBUG_ERROR,
				"old dir and new dir are different\n");
867
		retval = -EXDEV;
868
		goto clunk_newdir;
869
	}
870
	v9fs_blank_wstat(&wstat);
Latchesar Ionkov's avatar
Latchesar Ionkov committed
871
	wstat.muid = v9ses->uname;
872
	wstat.name = (char *) new_dentry->d_name.name;
873
	retval = p9_client_wstat(oldfid, &wstat);
874

875
clunk_newdir:
876
877
878
879
880
881
882
883
884
885
886
887
	if (!retval) {
		if (new_inode) {
			if (S_ISDIR(new_inode->i_mode))
				clear_nlink(new_inode);
			else
				drop_nlink(new_inode);
		}
		if (S_ISDIR(old_inode->i_mode)) {
			if (!new_inode)
				inc_nlink(new_dir);
			drop_nlink(old_dir);
		}
888
889
		v9fs_invalidate_inode_attr(old_inode);

890
891
		/* successful rename */
		d_move(old_dentry, new_dentry);
892
	}
893
	up_write(&v9ses->rename_sem);
894
	p9_client_clunk(newdirfid);
895

896
clunk_olddir:
897
	p9_client_clunk(olddirfid);
898

899
done:
900
901
902
903
	return retval;
}

/**
Adrian Bunk's avatar
Adrian Bunk committed
904
 * v9fs_vfs_getattr - retrieve file metadata
905
906
907
 * @mnt: mount information
 * @dentry: file to get attributes on
 * @stat: metadata structure to populate
908
909
910
911
912
913
914
 *
 */

static int
v9fs_vfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
		 struct kstat *stat)
{
915
916
917
	int err;
	struct v9fs_session_info *v9ses;
	struct p9_fid *fid;
918
	struct p9_wstat *st;
919
920
921
922

	P9_DPRINTK(P9_DEBUG_VFS, "dentry: %p\n", dentry);
	err = -EPERM;
	v9ses = v9fs_inode2v9ses(dentry->d_inode);
923
924
925
926
	if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE) {
		generic_fillattr(dentry->d_inode, stat);
		return 0;
	}
927
928
	fid = v9fs_fid_lookup(dentry);
	if (IS_ERR(fid))
929
		return PTR_ERR(fid);
930

931
932
933
	st = p9_client_stat(fid);
	if (IS_ERR(st))
		return PTR_ERR(st);
934

935
	v9fs_stat2inode(st, dentry->d_inode, dentry->d_inode->i_sb);
936
937
		generic_fillattr(dentry->d_inode, stat);

938
	p9stat_free(st);
939
940
	kfree(st);
	return 0;
941
942
943
944
945
946
947
948
949
950
951
}

/**
 * v9fs_vfs_setattr - set file metadata
 * @dentry: file whose metadata to set
 * @iattr: metadata assignment structure
 *
 */

static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr)
{
952
953
954
955
	int retval;
	struct v9fs_session_info *v9ses;
	struct p9_fid *fid;
	struct p9_wstat wstat;
956

957
958
959
960
	P9_DPRINTK(P9_DEBUG_VFS, "\n");
	retval = -EPERM;
	v9ses = v9fs_inode2v9ses(dentry->d_inode);
	fid = v9fs_fid_lookup(dentry);
961
962
	if(IS_ERR(fid))
		return PTR_ERR(fid);
963

964
	v9fs_blank_wstat(&wstat);
965
	if (iattr->ia_valid & ATTR_MODE)
966
		wstat.mode = unixmode2p9mode(v9ses, iattr->ia_mode);
967
968

	if (iattr->ia_valid & ATTR_MTIME)
969
		wstat.mtime = iattr->ia_mtime.tv_sec;
970
971

	if (iattr->ia_valid & ATTR_ATIME)
972
		wstat.atime = iattr->ia_atime.tv_sec;
973
974

	if (iattr->ia_valid & ATTR_SIZE)
975
		wstat.length = iattr->ia_size;
976

977
	if (v9fs_proto_dotu(v9ses)) {
978
979
		if (iattr->ia_valid & ATTR_UID)
			wstat.n_uid = iattr->ia_uid;
980

981
982
		if (iattr->ia_valid & ATTR_GID)
			wstat.n_gid = iattr->ia_gid;
983
984
	}

985
	retval = p9_client_wstat(fid, &wstat);
Christoph Hellwig's avatar
Christoph Hellwig committed
986
987
	if (retval < 0)
		return retval;
988

989
	v9fs_invalidate_inode_attr(dentry->d_inode);
Christoph Hellwig's avatar
Christoph Hellwig committed
990
991
992
993
994
995
	if ((iattr->ia_valid & ATTR_SIZE) &&
	    iattr->ia_size != i_size_read(dentry->d_inode)) {
		retval = vmtruncate(dentry->d_inode, iattr->ia_size);
		if (retval)
			return retval;
	}
996

Christoph Hellwig's avatar
Christoph Hellwig committed
997
998
999
	setattr_copy(dentry->d_inode, iattr);
	mark_inode_dirty(dentry->d_inode);
	return 0;
1000
1001
1002
}

/**
1003
1004
 * v9fs_stat2inode - populate an inode structure with mistat info
 * @stat: Plan 9 metadata (mistat) structure
1005
1006
1007
1008
1009
1010
 * @inode: inode to populate
 * @sb: superblock of filesystem
 *
 */

void
1011
v9fs_stat2inode(struct p9_wstat *stat, struct inode *inode,
1012
	struct super_block *sb)
1013
{
1014
	char ext[32];
1015
1016
	char tag_name[14];
	unsigned int i_nlink;
1017
	struct v9fs_session_info *v9ses = sb->s_fs_info;
1018
	struct v9fs_inode *v9inode = V9FS_I(inode);
1019
1020
1021

	inode->i_nlink = 1;

1022
1023
1024
	inode->i_atime.tv_sec = stat->atime;
	inode->i_mtime.tv_sec = stat->mtime;
	inode->i_ctime.tv_sec = stat->mtime;
1025

1026
1027
	inode->i_uid = v9ses->dfltuid;
	inode->i_gid = v9ses->dfltgid;
1028

1029
	if (v9fs_proto_dotu(v9ses)) {
1030
1031
		inode->i_uid = stat->n_uid;
		inode->i_gid = stat->n_gid;
1032
	}
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
	if ((S_ISREG(inode->i_mode)) || (S_ISDIR(inode->i_mode))) {
		if (v9fs_proto_dotu(v9ses) && (stat->extension[0] != '\0')) {
			/*
			 * Hadlink support got added later to
			 * to the .u extension. So there can be
			 * server out there that doesn't support
			 * this even with .u extension. So check
			 * for non NULL stat->extension
			 */
			strncpy(ext, stat->extension, sizeof(ext));
			/* HARDLINKCOUNT %u */
			sscanf(ext, "%13s %u", tag_name, &i_nlink);
			if (!strncmp(tag_name, "HARDLINKCOUNT", 13))
				inode->i_nlink = i_nlink;
		}
	}
1049
	inode->i_mode = p9mode2unixmode(v9ses, stat->mode);
1050
1051
1052
1053
	if ((S_ISBLK(inode->i_mode)) || (S_ISCHR(inode->i_mode))) {
		char type = 0;
		int major = -1;
		int minor = -1;
1054