vfs_inode.c 29.4 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;
Aneesh Kumar K.V's avatar
Aneesh Kumar K.V committed
223
	return &v9inode->vfs_inode;
224
225
226
227
228
229
230
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

399
	return ret;
400

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

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

410
411
412
413
414
415

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

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

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

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

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

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

}

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

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

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

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

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

Aneesh Kumar K.V's avatar
Aneesh Kumar K.V committed
512
	retval = p9_client_remove(v9fid);
513
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);
	}
Aneesh Kumar K.V's avatar
Aneesh Kumar K.V committed
524
	return retval;
525
526
527
}

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

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

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

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

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

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

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

594
	return ofid;
595

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

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

	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,
637
638
				v9fs_uflags2omode(flags,
						v9fs_proto_dotu(v9ses)));
639
640
641
642
643
644
645
646
	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) {
647
648
		v9inode = V9FS_I(dentry->d_inode);
		if (v9ses->cache && !v9inode->writeback_fid) {
649
			/*
650
			 * clone a fid and add it to writeback_fid
651
652
653
654
655
656
657
658
659
660
			 * 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;
			}
661
			v9inode->writeback_fid = (void *) inode_fid;
662
		}
663
		filp = lookup_instantiate_filp(nd, dentry, generic_file_open);
664
		if (IS_ERR(filp)) {
665
666
			err = PTR_ERR(filp);
			goto error;
667
668
		}

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

	return 0;

error:
680
681
	if (fid)
		p9_client_clunk(fid);
682
683

	return err;
684
685
686
687
}

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

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

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

712
713
	if (fid)
		p9_client_clunk(fid);
714
715

	return err;
716
717
718
719
720
721
722
723
724
725
}

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

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

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

739
740
741
	if (dentry->d_name.len > NAME_MAX)
		return ERR_PTR(-ENAMETOOLONG);

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

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

758
		return ERR_PTR(result);
759
760
	}

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

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

772
inst_out:
773
774
775
	d_add(dentry, inode);
	return NULL;

776
777
error_iput:
	iput(inode);
778
error:
779
	p9_client_clunk(fid);
780

781
782
783
784
785
786
	return ERR_PTR(result);
}

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

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

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

803
int v9fs_vfs_rmdir(struct inode *i, struct dentry *d)
804
805
806
807
808
809
810
811
812
813
814
815
816
{
	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
 *
 */

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

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

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

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

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

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

873
clunk_newdir:
874
875
876
877
878
879
880
881
882
883
884
885
	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);
		}
886
887
		/* successful rename */
		d_move(old_dentry, new_dentry);
888
	}
889
	up_write(&v9ses->rename_sem);
890
	p9_client_clunk(newdirfid);
891

892
clunk_olddir:
893
	p9_client_clunk(olddirfid);
894

895
done:
896
897
898
899
	return retval;
}

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

static int
v9fs_vfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
		 struct kstat *stat)
{
911
912
913
	int err;
	struct v9fs_session_info *v9ses;
	struct p9_fid *fid;
914
	struct p9_wstat *st;
915
916
917
918

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

927
928
929
	st = p9_client_stat(fid);
	if (IS_ERR(st))
		return PTR_ERR(st);
930

931
	v9fs_stat2inode(st, dentry->d_inode, dentry->d_inode->i_sb);
932
933
		generic_fillattr(dentry->d_inode, stat);

934
	p9stat_free(st);
935
936
	kfree(st);
	return 0;
937
938
939
940
941
942
943
944
945
946
947
}

/**
 * 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)
{
948
949
950
951
	int retval;
	struct v9fs_session_info *v9ses;
	struct p9_fid *fid;
	struct p9_wstat wstat;
952

953
954
955
956
	P9_DPRINTK(P9_DEBUG_VFS, "\n");
	retval = -EPERM;
	v9ses = v9fs_inode2v9ses(dentry->d_inode);
	fid = v9fs_fid_lookup(dentry);
957
958
	if(IS_ERR(fid))
		return PTR_ERR(fid);
959

960
	v9fs_blank_wstat(&wstat);
961
	if (iattr->ia_valid & ATTR_MODE)
962
		wstat.mode = unixmode2p9mode(v9ses, iattr->ia_mode);
963
964

	if (iattr->ia_valid & ATTR_MTIME)
965
		wstat.mtime = iattr->ia_mtime.tv_sec;
966
967

	if (iattr->ia_valid & ATTR_ATIME)
968
		wstat.atime = iattr->ia_atime.tv_sec;
969
970

	if (iattr->ia_valid & ATTR_SIZE)
971
		wstat.length = iattr->ia_size;
972

973
	if (v9fs_proto_dotu(v9ses)) {
974
975
		if (iattr->ia_valid & ATTR_UID)
			wstat.n_uid = iattr->ia_uid;
976

977
978
		if (iattr->ia_valid & ATTR_GID)
			wstat.n_gid = iattr->ia_gid;
979
980
	}

981
	retval = p9_client_wstat(fid, &wstat);
Christoph Hellwig's avatar
Christoph Hellwig committed
982
983
	if (retval < 0)
		return retval;
984

Christoph Hellwig's avatar
Christoph Hellwig committed
985
986
987
988
989
990
	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;
	}
991

Christoph Hellwig's avatar
Christoph Hellwig committed
992
993
994
	setattr_copy(dentry->d_inode, iattr);
	mark_inode_dirty(dentry->d_inode);
	return 0;
995
996
997
}

/**
998
999
 * v9fs_stat2inode - populate an inode structure with mistat info
 * @stat: Plan 9 metadata (mistat) structure
1000
1001
1002
1003
1004
1005
 * @inode: inode to populate
 * @sb: superblock of filesystem
 *
 */

void
1006
v9fs_stat2inode(struct p9_wstat *stat, struct inode *inode,
1007
	struct super_block *sb)
1008
{
1009
	char ext[32];
1010
1011
	char tag_name[14];
	unsigned int i_nlink;
1012
1013
1014
1015
	struct v9fs_session_info *v9ses = sb->s_fs_info;

	inode->i_nlink = 1;

1016
1017
1018
	inode->i_atime.tv_sec = stat->atime;
	inode->i_mtime.tv_sec = stat->mtime;
	inode->i_ctime.tv_sec = stat->mtime;
1019

1020
1021
	inode->i_uid = v9ses->dfltuid;
	inode->i_gid = v9ses->dfltgid;
1022

1023
	if (v9fs_proto_dotu(v9ses)) {
1024
1025
		inode->i_uid = stat->n_uid;
		inode->i_gid = stat->n_gid;
1026
	}
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
	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;
		}
	}
1043
	inode->i_mode = p9mode2unixmode(v9ses, stat->mode);
1044
1045
1046
1047
	if ((S_ISBLK(inode->i_mode)) || (S_ISCHR(inode->i_mode))) {
		char type = 0;
		int major = -1;
		int minor = -1;
1048

1049
		strncpy(ext, stat->extension, sizeof(ext));
1050
		sscanf(ext, "%c %u %u", &type, &major, &minor);
1051
1052
1053
1054
1055
1056
1057
1058
		switch (type) {
		case 'c':
			inode->i_mode &= ~S_IFBLK;
			inode->i_mode |= S_IFCHR;
			break;
		case 'b':
			break;
		default:
1059
			P9_DPRINTK(P9_DEBUG_ERROR,
1060
1061
				"Unknown special type %c %s\n", type,
				stat->extension);
1062
1063
		};
		inode->i_rdev = MKDEV(major, minor);