vfs_inode.c 30.9 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;
224
	mutex_init(&v9inode->v_mutex);
Aneesh Kumar K.V's avatar
Aneesh Kumar K.V committed
225
	return &v9inode->vfs_inode;
226
227
228
229
230
231
232
}

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

Nick Piggin's avatar
Nick Piggin committed
233
static void v9fs_i_callback(struct rcu_head *head)
234
{
Nick Piggin's avatar
Nick Piggin committed
235
236
	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
237
	kmem_cache_free(v9fs_inode_cache, V9FS_I(inode));
238
}
Nick Piggin's avatar
Nick Piggin committed
239
240
241
242
243

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

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

250
	inode_init_owner(inode, NULL, mode);
251
252
253
254
255
256
257
258
259
260
	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
261
262
263
264
265
266
267
		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 {
268
			P9_DPRINTK(P9_DEBUG_ERROR,
269
270
271
				   "special files without extended mode\n");
			err = -EINVAL;
			goto error;
272
		}
273
274
275
		init_special_inode(inode, inode->i_mode, inode->i_rdev);
		break;
	case S_IFREG:
276
277
		if (v9fs_proto_dotl(v9ses)) {
			inode->i_op = &v9fs_file_inode_operations_dotl;
278
279
280
281
282
			if (v9ses->cache)
				inode->i_fop =
					&v9fs_cached_file_operations_dotl;
			else
				inode->i_fop = &v9fs_file_operations_dotl;
283
284
		} else {
			inode->i_op = &v9fs_file_inode_operations;
285
286
287
288
			if (v9ses->cache)
				inode->i_fop = &v9fs_cached_file_operations;
			else
				inode->i_fop = &v9fs_file_operations;
289
290
		}

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

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

305
306
307
		break;
	case S_IFDIR:
		inc_nlink(inode);
308
309
310
311
		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;
312
313
		else
			inode->i_op = &v9fs_dir_inode_operations;
314
315
316
317
318
319

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

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

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

Aneesh Kumar K.V's avatar
Aneesh Kumar K.V committed
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
357
/**
 * 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;
358
359
}

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

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

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

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

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

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

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

401
	return ret;
402

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

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

412
413
414
415
416
417

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

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

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

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

Aneesh Kumar K.V's avatar
Aneesh Kumar K.V committed
445
446
447
448
449
450
451
452
453
454
455
	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.
	 */
456
	umode = p9mode2unixmode(v9ses, st->mode);
Aneesh Kumar K.V's avatar
Aneesh Kumar K.V committed
457
458
	retval = v9fs_init_inode(v9ses, inode, umode);
	if (retval)
459
		goto error;
460

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

}

struct inode *
Aneesh Kumar K.V's avatar
Aneesh Kumar K.V committed
476
477
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
478
479
480
481
482
483
484
485
486
{
	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);
487
	p9stat_free(st);
488
	kfree(st);
Aneesh Kumar K.V's avatar
Aneesh Kumar K.V committed
489
	return inode;
490
491
492
493
}

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

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

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

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

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

526
		v9fs_invalidate_inode_attr(file_inode);
527
		v9fs_invalidate_inode_attr(dir);
528
	}
Aneesh Kumar K.V's avatar
Aneesh Kumar K.V committed
529
	return retval;
530
531
532
}

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

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

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

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

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

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

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

599
	return ofid;
600

601
602
603
604
605
606
607
608
609
610
611
612
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
613
 * @dir: directory inode that is being created
614
615
616
617
618
 * @dentry:  dentry that is being deleted
 * @mode: create permissions
 * @nd: path information
 *
 */
619

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

	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,
642
643
				v9fs_uflags2omode(flags,
						v9fs_proto_dotu(v9ses)));
644
645
646
647
648
649
	if (IS_ERR(fid)) {
		err = PTR_ERR(fid);
		fid = NULL;
		goto error;
	}

650
	v9fs_invalidate_inode_attr(dir);
651
652
	/* if we are opening a file, assign the open fid to the file */
	if (nd && nd->flags & LOOKUP_OPEN) {
653
		v9inode = V9FS_I(dentry->d_inode);
654
		mutex_lock(&v9inode->v_mutex);
655
		if (v9ses->cache && !v9inode->writeback_fid) {
656
			/*
657
			 * clone a fid and add it to writeback_fid
658
659
660
661
662
663
664
665
			 * 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);
666
				mutex_unlock(&v9inode->v_mutex);
667
668
				goto error;
			}
669
			v9inode->writeback_fid = (void *) inode_fid;
670
		}
671
		mutex_unlock(&v9inode->v_mutex);
672
		filp = lookup_instantiate_filp(nd, dentry, generic_file_open);
673
		if (IS_ERR(filp)) {
674
675
			err = PTR_ERR(filp);
			goto error;
676
677
		}

678
		filp->private_data = fid;
679
680
681
682
#ifdef CONFIG_9P_FSCACHE
		if (v9ses->cache)
			v9fs_cache_inode_set_cookie(dentry->d_inode, filp);
#endif
683
684
	} else
		p9_client_clunk(fid);
685
686
687
688

	return 0;

error:
689
690
	if (fid)
		p9_client_clunk(fid);
691
692

	return err;
693
694
695
696
}

/**
 * v9fs_vfs_mkdir - VFS mkdir hook to create a directory
697
 * @dir:  inode that is being unlinked
698
699
700
701
702
 * @dentry: dentry that is being unlinked
 * @mode: mode for new directory
 *
 */

703
static int v9fs_vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
704
{
705
	int err;
706
707
	u32 perm;
	struct p9_fid *fid;
708
	struct v9fs_session_info *v9ses;
709

710
711
	P9_DPRINTK(P9_DEBUG_VFS, "name %s\n", dentry->d_name.name);
	err = 0;
712
713
	v9ses = v9fs_inode2v9ses(dir);
	perm = unixmode2p9mode(v9ses, mode | S_IFDIR);
714
715
716
717
	fid = v9fs_create(v9ses, dir, dentry, NULL, perm, P9_OREAD);
	if (IS_ERR(fid)) {
		err = PTR_ERR(fid);
		fid = NULL;
718
	} else {
719
		inc_nlink(dir);
720
721
		v9fs_invalidate_inode_attr(dir);
	}
722

723
724
	if (fid)
		p9_client_clunk(fid);
725
726

	return err;
727
728
729
730
731
732
733
734
735
736
}

/**
 * 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
 *
 */

737
struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry,
738
739
740
741
				      struct nameidata *nameidata)
{
	struct super_block *sb;
	struct v9fs_session_info *v9ses;
742
	struct p9_fid *dfid, *fid;
743
	struct inode *inode;
744
	char *name;
745
746
	int result = 0;

747
	P9_DPRINTK(P9_DEBUG_VFS, "dir: %p dentry: (%s) %p nameidata: %p\n",
748
		dir, dentry->d_name.name, dentry, nameidata);
749

750
751
752
	if (dentry->d_name.len > NAME_MAX)
		return ERR_PTR(-ENAMETOOLONG);

753
754
	sb = dir->i_sb;
	v9ses = v9fs_inode2v9ses(dir);
755
	/* We can walk d_parent because we hold the dir->i_mutex */
756
757
	dfid = v9fs_fid_lookup(dentry->d_parent);
	if (IS_ERR(dfid))
758
		return ERR_CAST(dfid);
759
760
761
762
763

	name = (char *) dentry->d_name.name;
	fid = p9_client_walk(dfid, 1, &name, 1);
	if (IS_ERR(fid)) {
		result = PTR_ERR(fid);
764
		if (result == -ENOENT) {
765
766
			inode = NULL;
			goto inst_out;
767
768
		}

769
		return ERR_PTR(result);
770
771
	}

Aneesh Kumar K.V's avatar
Aneesh Kumar K.V committed
772
	inode = v9fs_get_inode_from_fid(v9ses, fid, dir->i_sb);
773
774
775
776
	if (IS_ERR(inode)) {
		result = PTR_ERR(inode);
		inode = NULL;
		goto error;
777
778
	}

779
	result = v9fs_fid_add(dentry, fid);
780
	if (result < 0)
781
		goto error_iput;
782

783
inst_out:
784
785
786
	d_add(dentry, inode);
	return NULL;

787
788
error_iput:
	iput(inode);
789
error:
790
	p9_client_clunk(fid);
791

792
793
794
795
796
797
	return ERR_PTR(result);
}

/**
 * v9fs_vfs_unlink - VFS unlink hook to delete an inode
 * @i:  inode that is being unlinked
798
 * @d: dentry that is being unlinked
799
800
801
 *
 */

802
int v9fs_vfs_unlink(struct inode *i, struct dentry *d)
803
804
805
806
807
808
809
{
	return v9fs_remove(i, d, 0);
}

/**
 * v9fs_vfs_rmdir - VFS unlink hook to delete a directory
 * @i:  inode that is being unlinked
810
 * @d: dentry that is being unlinked
811
812
813
 *
 */

814
int v9fs_vfs_rmdir(struct inode *i, struct dentry *d)
815
816
817
818
819
820
821
822
823
824
825
826
827
{
	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
 *
 */

828
int
829
830
831
v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
		struct inode *new_dir, struct dentry *new_dentry)
{
832
	int retval;
833
	struct inode *old_inode;
834
	struct inode *new_inode;
835
836
837
838
839
	struct v9fs_session_info *v9ses;
	struct p9_fid *oldfid;
	struct p9_fid *olddirfid;
	struct p9_fid *newdirfid;
	struct p9_wstat wstat;
840

841
842
843
	P9_DPRINTK(P9_DEBUG_VFS, "\n");
	retval = 0;
	old_inode = old_dentry->d_inode;
844
	new_inode = new_dentry->d_inode;
845
846
	v9ses = v9fs_inode2v9ses(old_inode);
	oldfid = v9fs_fid_lookup(old_dentry);
Latchesar Ionkov's avatar
Latchesar Ionkov committed
847
	if (IS_ERR(oldfid))
848
849
850
		return PTR_ERR(oldfid);

	olddirfid = v9fs_fid_clone(old_dentry->d_parent);
Latchesar Ionkov's avatar
Latchesar Ionkov committed
851
	if (IS_ERR(olddirfid)) {
852
		retval = PTR_ERR(olddirfid);
853
		goto done;
854
855
856
	}

	newdirfid = v9fs_fid_clone(new_dentry->d_parent);
Latchesar Ionkov's avatar
Latchesar Ionkov committed
857
	if (IS_ERR(newdirfid)) {
858
		retval = PTR_ERR(newdirfid);
859
		goto clunk_olddir;
860
861
	}

862
	down_write(&v9ses->rename_sem);
863
864
865
866
867
868
	if (v9fs_proto_dotl(v9ses)) {
		retval = p9_client_rename(oldfid, newdirfid,
					(char *) new_dentry->d_name.name);
		if (retval != -ENOSYS)
			goto clunk_newdir;
	}
869
870
871
872
	if (old_dentry->d_parent != new_dentry->d_parent) {
		/*
		 * 9P .u can only handle file rename in the same directory
		 */
873

874
875
		P9_DPRINTK(P9_DEBUG_ERROR,
				"old dir and new dir are different\n");
876
		retval = -EXDEV;
877
		goto clunk_newdir;
878
	}
879
	v9fs_blank_wstat(&wstat);
Latchesar Ionkov's avatar
Latchesar Ionkov committed
880
	wstat.muid = v9ses->uname;
881
	wstat.name = (char *) new_dentry->d_name.name;
882
	retval = p9_client_wstat(oldfid, &wstat);
883

884
clunk_newdir:
885
886
887
888
889
890
	if (!retval) {
		if (new_inode) {
			if (S_ISDIR(new_inode->i_mode))
				clear_nlink(new_inode);
			else
				drop_nlink(new_inode);
891
892
893
894
895
			/*
			 * Work around vfs rename rehash bug with
			 * FS_RENAME_DOES_D_MOVE
			 */
			v9fs_invalidate_inode_attr(new_inode);
896
897
898
899
900
901
		}
		if (S_ISDIR(old_inode->i_mode)) {
			if (!new_inode)
				inc_nlink(new_dir);
			drop_nlink(old_dir);
		}
902
		v9fs_invalidate_inode_attr(old_inode);
903
904
		v9fs_invalidate_inode_attr(old_dir);
		v9fs_invalidate_inode_attr(new_dir);
905

906
907
		/* successful rename */
		d_move(old_dentry, new_dentry);
908
	}
909
	up_write(&v9ses->rename_sem);
910
	p9_client_clunk(newdirfid);
911

912
clunk_olddir:
913
	p9_client_clunk(olddirfid);
914

915
done:
916
917
918
919
	return retval;
}

/**
Adrian Bunk's avatar
Adrian Bunk committed
920
 * v9fs_vfs_getattr - retrieve file metadata
921
922
923
 * @mnt: mount information
 * @dentry: file to get attributes on
 * @stat: metadata structure to populate
924
925
926
927
928
929
930
 *
 */

static int
v9fs_vfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
		 struct kstat *stat)
{
931
932
933
	int err;
	struct v9fs_session_info *v9ses;
	struct p9_fid *fid;
934
	struct p9_wstat *st;
935
936
937
938

	P9_DPRINTK(P9_DEBUG_VFS, "dentry: %p\n", dentry);
	err = -EPERM;
	v9ses = v9fs_inode2v9ses(dentry->d_inode);
939
940
941
942
	if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE) {
		generic_fillattr(dentry->d_inode, stat);
		return 0;
	}
943
944
	fid = v9fs_fid_lookup(dentry);
	if (IS_ERR(fid))
945
		return PTR_ERR(fid);
946

947
948
949
	st = p9_client_stat(fid);
	if (IS_ERR(st))
		return PTR_ERR(st);
950

951
	v9fs_stat2inode(st, dentry->d_inode, dentry->d_inode->i_sb);
952
953
		generic_fillattr(dentry->d_inode, stat);

954
	p9stat_free(st);
955
956
	kfree(st);
	return 0;
957
958
959
960
961
962
963
964
965
966
967
}

/**
 * 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)
{
968
969
970
971
	int retval;
	struct v9fs_session_info *v9ses;
	struct p9_fid *fid;
	struct p9_wstat wstat;
972

973
974
975
976
	P9_DPRINTK(P9_DEBUG_VFS, "\n");
	retval = -EPERM;
	v9ses = v9fs_inode2v9ses(dentry->d_inode);
	fid = v9fs_fid_lookup(dentry);
977
978
	if(IS_ERR(fid))
		return PTR_ERR(fid);
979

980
	v9fs_blank_wstat(&wstat);
981
	if (iattr->ia_valid & ATTR_MODE)
982
		wstat.mode = unixmode2p9mode(v9ses, iattr->ia_mode);
983
984

	if (iattr->ia_valid & ATTR_MTIME)
985
		wstat.mtime = iattr->ia_mtime.tv_sec;
986
987

	if (iattr->ia_valid & ATTR_ATIME)
988
		wstat.atime = iattr->ia_atime.tv_sec;
989
990

	if (iattr->ia_valid & ATTR_SIZE)
991
		wstat.length = iattr->ia_size;
992

993
	if (v9fs_proto_dotu(v9ses)) {
994
995
		if (iattr->ia_valid & ATTR_UID)
			wstat.n_uid = iattr->ia_uid;
996

997
998
		if (iattr->ia_valid & ATTR_GID)
			wstat.n_gid = iattr->ia_gid;
999
	}
Christoph Hellwig's avatar
Christoph Hellwig committed
1000
1001
1002
1003
1004
1005
	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;
	}
1006
1007
1008
1009
	/* Write all dirty data */
	if (S_ISREG(dentry->d_inode->i_mode))
		filemap_write_and_wait(dentry->d_inode->i_mapping);

1010
1011
1012
1013
	retval = p9_client_wstat(fid, &wstat);
	if (retval < 0)
		return retval;
	v9fs_invalidate_inode_attr(dentry->d_inode);
1014

Christoph Hellwig's avatar
Christoph Hellwig committed
1015
1016
1017
	setattr_copy(dentry->d_inode, iattr);
	mark_inode_dirty(dentry->d_inode);
	return 0;
1018
1019
1020
}

/**
1021
1022
 * v9fs_stat2inode - populate an inode structure with mistat info
 * @stat: Plan 9 metadata (mistat) structure
1023
1024
1025
1026
1027
1028
 * @inode: inode to populate
 * @sb: superblock of filesystem
 *
 */

void
1029
v9fs_stat2inode(struct p9_wstat *stat, struct inode *inode,
1030
	struct super_block *sb)
1031
{
1032
	char ext[32];
1033
1034
	char tag_name[14];
	unsigned int i_nlink;
1035
	struct v9fs_session_info *v9ses = sb->s_fs_info;
1036
	struct v9fs_inode *v9inode = V9FS_I(inode);
1037
1038
1039

	inode->i_nlink = 1;