vfs_inode.c 45.6 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
40
#include <net/9p/9p.h>
#include <net/9p/client.h>
41
42
43
44

#include "v9fs.h"
#include "v9fs_vfs.h"
#include "fid.h"
45
#include "cache.h"
46
#include "xattr.h"
47

48
static const struct inode_operations v9fs_dir_inode_operations;
49
50
static const struct inode_operations v9fs_dir_inode_operations_dotu;
static const struct inode_operations v9fs_dir_inode_operations_dotl;
51
static const struct inode_operations v9fs_file_inode_operations;
52
static const struct inode_operations v9fs_file_inode_operations_dotl;
53
static const struct inode_operations v9fs_symlink_inode_operations;
54
static const struct inode_operations v9fs_symlink_inode_operations_dotl;
55
56
57
58
59
60
61
62

/**
 * unixmode2p9mode - convert unix mode bits to plan 9
 * @v9ses: v9fs session information
 * @mode: mode to convert
 *
 */

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

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

	return res;
}

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

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

	res = mode & 0777;

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

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

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

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

	return res;
}

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

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

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

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

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

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

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

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

	return ret;
}

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

185
void
186
v9fs_blank_wstat(struct p9_wstat *wstat)
187
{
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
	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;
205
206
}

207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
#ifdef CONFIG_9P_FSCACHE
/**
 * v9fs_alloc_inode - helper function to allocate an inode
 * This callback is executed before setting up the inode so that we
 * can associate a vcookie with each inode.
 *
 */

struct inode *v9fs_alloc_inode(struct super_block *sb)
{
	struct v9fs_cookie *vcookie;
	vcookie = (struct v9fs_cookie *)kmem_cache_alloc(vcookie_cache,
							 GFP_KERNEL);
	if (!vcookie)
		return NULL;

	vcookie->fscache = NULL;
	vcookie->qid = NULL;
	spin_lock_init(&vcookie->lock);
	return &vcookie->inode;
}

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

void v9fs_destroy_inode(struct inode *inode)
{
	kmem_cache_free(vcookie_cache, v9fs_inode2cookie(inode));
}
#endif

240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
/**
 * v9fs_get_fsgid_for_create - Helper function to get the gid for creating a
 * new file system object. This checks the S_ISGID to determine the owning
 * group of the new file system object.
 */

static gid_t v9fs_get_fsgid_for_create(struct inode *dir_inode)
{
	BUG_ON(dir_inode == NULL);

	if (dir_inode->i_mode & S_ISGID) {
		/* set_gid bit is set.*/
		return dir_inode->i_gid;
	}
	return current_fsgid();
}

/**
 * v9fs_dentry_from_dir_inode - helper function to get the dentry from
 * dir inode.
 *
 */

263
static struct dentry *v9fs_dentry_from_dir_inode(struct inode *inode)
264
265
266
267
268
269
270
271
272
273
274
{
	struct dentry *dentry;

	spin_lock(&dcache_lock);
	/* Directory should have only one entry. */
	BUG_ON(S_ISDIR(inode->i_mode) && !list_is_singular(&inode->i_dentry));
	dentry = list_entry(inode->i_dentry.next, struct dentry, d_alias);
	spin_unlock(&dcache_lock);
	return dentry;
}

275
276
277
278
279
280
281
282
283
/**
 * 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)
{
284
	int err;
285
	struct inode *inode;
286
	struct v9fs_session_info *v9ses = sb->s_fs_info;
287

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

	inode = new_inode(sb);
291
292
	if (!inode) {
		P9_EPRINTK(KERN_WARNING, "Problem allocating inode\n");
293
		return ERR_PTR(-ENOMEM);
294
295
	}

296
	inode_init_owner(inode, NULL, mode);
297
298
299
300
301
302
303
304
305
306
	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
307
308
309
310
311
312
313
		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 {
314
			P9_DPRINTK(P9_DEBUG_ERROR,
315
316
317
				   "special files without extended mode\n");
			err = -EINVAL;
			goto error;
318
		}
319
320
321
		init_special_inode(inode, inode->i_mode, inode->i_rdev);
		break;
	case S_IFREG:
322
323
324
325
326
327
328
329
		if (v9fs_proto_dotl(v9ses)) {
			inode->i_op = &v9fs_file_inode_operations_dotl;
			inode->i_fop = &v9fs_file_operations_dotl;
		} else {
			inode->i_op = &v9fs_file_inode_operations;
			inode->i_fop = &v9fs_file_operations;
		}

330
		break;
331

332
	case S_IFLNK:
333
334
335
		if (!v9fs_proto_dotu(v9ses) && !v9fs_proto_dotl(v9ses)) {
			P9_DPRINTK(P9_DEBUG_ERROR, "extended modes used with "
						"legacy protocol.\n");
336
337
338
			err = -EINVAL;
			goto error;
		}
339
340
341
342
343
344

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

345
346
347
		break;
	case S_IFDIR:
		inc_nlink(inode);
348
349
350
351
		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;
352
353
		else
			inode->i_op = &v9fs_dir_inode_operations;
354
355
356
357
358
359

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

360
361
362
363
364
365
		break;
	default:
		P9_DPRINTK(P9_DEBUG_ERROR, "BAD mode 0x%x S_IFMT 0x%x\n",
			   mode, mode & S_IFMT);
		err = -EINVAL;
		goto error;
366
	}
367

368
	return inode;
369
370
371
372

error:
	iput(inode);
	return ERR_PTR(err);
373
374
}

375
/*
376
377
378
379
static struct v9fs_fid*
v9fs_clone_walk(struct v9fs_session_info *v9ses, u32 fid, struct dentry *dentry)
{
	int err;
380
	int nfid;
381
382
383
384
385
	struct v9fs_fid *ret;
	struct v9fs_fcall *fcall;

	nfid = v9fs_get_idpool(&v9ses->fidpool);
	if (nfid < 0) {
386
		eprintk(KERN_WARNING, "no free fids available\n");
387
		return ERR_PTR(-ENOSPC);
388
389
	}

390
391
392
393
	err = v9fs_t_walk(v9ses, fid, nfid, (char *) dentry->d_name.name,
		&fcall);

	if (err < 0) {
394
395
396
		if (fcall && fcall->id == RWALK)
			goto clunk_fid;

397
398
399
		PRINT_FCALL_ERROR("walk error", fcall);
		v9fs_put_idpool(nfid, &v9ses->fidpool);
		goto error;
400
	}
401

402
403
	kfree(fcall);
	fcall = NULL;
404
405
406
407
408
	ret = v9fs_fid_create(v9ses, nfid);
	if (!ret) {
		err = -ENOMEM;
		goto clunk_fid;
	}
409

410
411
412
413
	err = v9fs_fid_insert(ret, dentry);
	if (err < 0) {
		v9fs_fid_destroy(ret);
		goto clunk_fid;
414
	}
415

416
	return ret;
417

418
419
clunk_fid:
	v9fs_t_clunk(v9ses, nfid);
420

421
422
423
424
error:
	kfree(fcall);
	return ERR_PTR(err);
}
425
*/
426

427
428
429
430
431
432

/**
 * v9fs_clear_inode - release an inode
 * @inode: inode to release
 *
 */
433
void v9fs_evict_inode(struct inode *inode)
434
{
435
436
	truncate_inode_pages(inode->i_mapping, 0);
	end_writeback(inode);
437
438
439
440
441
442
443
	filemap_fdatawrite(inode->i_mapping);

#ifdef CONFIG_9P_FSCACHE
	v9fs_cache_inode_put_cookie(inode);
#endif
}

444
static struct inode *
445
v9fs_inode(struct v9fs_session_info *v9ses, struct p9_fid *fid,
446
447
448
	struct super_block *sb)
{
	int err, umode;
449
	struct inode *ret = NULL;
450
	struct p9_wstat *st;
451

452
	st = p9_client_stat(fid);
453
454
	if (IS_ERR(st))
		return ERR_CAST(st);
455

456
	umode = p9mode2unixmode(v9ses, st->mode);
457
458
459
460
461
	ret = v9fs_get_inode(sb, umode);
	if (IS_ERR(ret)) {
		err = PTR_ERR(ret);
		goto error;
	}
462

463
464
	v9fs_stat2inode(st, ret, sb);
	ret->i_ino = v9fs_qid2ino(&st->qid);
465
466
467
468
469

#ifdef CONFIG_9P_FSCACHE
	v9fs_vcookie_set_qid(ret, &st->qid);
	v9fs_cache_inode_get_cookie(ret);
#endif
470
	p9stat_free(st);
471
	kfree(st);
472
473
	return ret;
error:
474
	p9stat_free(st);
475
	kfree(st);
476
	return ERR_PTR(err);
477
478
}

479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
static struct inode *
v9fs_inode_dotl(struct v9fs_session_info *v9ses, struct p9_fid *fid,
	struct super_block *sb)
{
	struct inode *ret = NULL;
	int err;
	struct p9_stat_dotl *st;

	st = p9_client_getattr_dotl(fid, P9_STATS_BASIC);
	if (IS_ERR(st))
		return ERR_CAST(st);

	ret = v9fs_get_inode(sb, st->st_mode);
	if (IS_ERR(ret)) {
		err = PTR_ERR(ret);
		goto error;
	}

	v9fs_stat2inode_dotl(st, ret);
	ret->i_ino = v9fs_qid2ino(&st->qid);
#ifdef CONFIG_9P_FSCACHE
	v9fs_vcookie_set_qid(ret, &st->qid);
	v9fs_cache_inode_get_cookie(ret);
#endif
	kfree(st);
	return ret;
error:
	kfree(st);
	return ERR_PTR(err);
}

/**
 * v9fs_inode_from_fid - Helper routine to populate an inode by
 * issuing a attribute request
 * @v9ses: session information
 * @fid: fid to issue attribute request for
 * @sb: superblock on which to create inode
 *
 */
static inline struct inode *
v9fs_inode_from_fid(struct v9fs_session_info *v9ses, struct p9_fid *fid,
			struct super_block *sb)
{
	if (v9fs_proto_dotl(v9ses))
		return v9fs_inode_dotl(v9ses, fid, sb);
	else
		return v9fs_inode(v9ses, fid, sb);
}

528
529
/**
 * v9fs_remove - helper function to remove files and directories
530
531
532
 * @dir: directory inode that is being deleted
 * @file:  dentry that is being deleted
 * @rmdir: removing a directory
533
534
535
536
537
 *
 */

static int v9fs_remove(struct inode *dir, struct dentry *file, int rmdir)
{
Aneesh Kumar K.V's avatar
Aneesh Kumar K.V committed
538
	int retval;
539
540
	struct inode *file_inode;
	struct p9_fid *v9fid;
541

542
	P9_DPRINTK(P9_DEBUG_VFS, "inode: %p dentry: %p rmdir: %d\n", dir, file,
543
544
545
		rmdir);

	file_inode = file->d_inode;
546
	v9fid = v9fs_fid_clone(file);
Latchesar Ionkov's avatar
Latchesar Ionkov committed
547
	if (IS_ERR(v9fid))
548
		return PTR_ERR(v9fid);
549

Aneesh Kumar K.V's avatar
Aneesh Kumar K.V committed
550
551
552
553
	retval = p9_client_remove(v9fid);
	if (!retval)
		drop_nlink(file_inode);
	return retval;
554
555
}

556
557
558
559
560
561
static int
v9fs_open_created(struct inode *inode, struct file *file)
{
	return 0;
}

562

563
/**
564
 * v9fs_create - Create a file
565
566
 * @v9ses: session information
 * @dir: directory that dentry is being created in
567
 * @dentry:  dentry that is being created
Abhishek Kulkarni's avatar
Abhishek Kulkarni committed
568
 * @extension: 9p2000.u extension string to support devices, etc.
569
570
 * @perm: create permissions
 * @mode: open mode
571
572
 *
 */
573
574
575
static struct p9_fid *
v9fs_create(struct v9fs_session_info *v9ses, struct inode *dir,
		struct dentry *dentry, char *extension, u32 perm, u8 mode)
576
{
577
	int err;
578
579
	char *name;
	struct p9_fid *dfid, *ofid, *fid;
580
581
	struct inode *inode;

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

584
585
586
587
	err = 0;
	ofid = NULL;
	fid = NULL;
	name = (char *) dentry->d_name.name;
588
	dfid = v9fs_fid_lookup(dentry->d_parent);
Latchesar Ionkov's avatar
Latchesar Ionkov committed
589
	if (IS_ERR(dfid)) {
590
		err = PTR_ERR(dfid);
591
592
		P9_DPRINTK(P9_DEBUG_VFS, "fid lookup failed %d\n", err);
		return ERR_PTR(err);
593
	}
594

595
596
597
598
	/* clone a fid to use for creation */
	ofid = p9_client_walk(dfid, 0, NULL, 1);
	if (IS_ERR(ofid)) {
		err = PTR_ERR(ofid);
599
		P9_DPRINTK(P9_DEBUG_VFS, "p9_client_walk failed %d\n", err);
600
		return ERR_PTR(err);
601
	}
602

603
	err = p9_client_fcreate(ofid, name, perm, mode, extension);
604
605
	if (err < 0) {
		P9_DPRINTK(P9_DEBUG_VFS, "p9_client_fcreate failed %d\n", err);
606
		goto error;
607
	}
608

609
	/* now walk from the parent so we can get unopened fid */
610
	fid = p9_client_walk(dfid, 1, &name, 1);
611
612
	if (IS_ERR(fid)) {
		err = PTR_ERR(fid);
613
		P9_DPRINTK(P9_DEBUG_VFS, "p9_client_walk failed %d\n", err);
614
		fid = NULL;
615
		goto error;
616
	}
617

618
619
	/* instantiate inode and assign the unopened fid to the dentry */
	inode = v9fs_inode_from_fid(v9ses, fid, dir->i_sb);
620
621
	if (IS_ERR(inode)) {
		err = PTR_ERR(inode);
622
		P9_DPRINTK(P9_DEBUG_VFS, "inode creation failed %d\n", err);
623
624
625
		goto error;
	}

Latchesar Ionkov's avatar
Latchesar Ionkov committed
626
	if (v9ses->cache)
627
628
629
		dentry->d_op = &v9fs_cached_dentry_operations;
	else
		dentry->d_op = &v9fs_dentry_operations;
630

631
	d_instantiate(dentry, inode);
632
633
634
635
	err = v9fs_fid_add(dentry, fid);
	if (err < 0)
		goto error;

636
	return ofid;
637

638
639
640
641
642
643
644
645
646
647
error:
	if (ofid)
		p9_client_clunk(ofid);

	if (fid)
		p9_client_clunk(fid);

	return ERR_PTR(err);
}

648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
/**
 * v9fs_vfs_create_dotl - VFS hook to create files for 9P2000.L protocol.
 * @dir: directory inode that is being created
 * @dentry:  dentry that is being deleted
 * @mode: create permissions
 * @nd: path information
 *
 */

static int
v9fs_vfs_create_dotl(struct inode *dir, struct dentry *dentry, int mode,
		struct nameidata *nd)
{
	int err = 0;
	char *name = NULL;
	gid_t gid;
	int flags;
	struct v9fs_session_info *v9ses;
	struct p9_fid *fid = NULL;
	struct p9_fid *dfid, *ofid;
	struct file *filp;
	struct p9_qid qid;
	struct inode *inode;

	v9ses = v9fs_inode2v9ses(dir);
	if (nd && nd->flags & LOOKUP_OPEN)
		flags = nd->intent.open.flags - 1;
	else
		flags = O_RDWR;

	name = (char *) dentry->d_name.name;
	P9_DPRINTK(P9_DEBUG_VFS, "v9fs_vfs_create_dotl: name:%s flags:0x%x "
			"mode:0x%x\n", name, flags, mode);

	dfid = v9fs_fid_lookup(dentry->d_parent);
	if (IS_ERR(dfid)) {
		err = PTR_ERR(dfid);
		P9_DPRINTK(P9_DEBUG_VFS, "fid lookup failed %d\n", err);
		return err;
	}

	/* clone a fid to use for creation */
	ofid = p9_client_walk(dfid, 0, NULL, 1);
	if (IS_ERR(ofid)) {
		err = PTR_ERR(ofid);
		P9_DPRINTK(P9_DEBUG_VFS, "p9_client_walk failed %d\n", err);
		return err;
	}

	gid = v9fs_get_fsgid_for_create(dir);
	err = p9_client_create_dotl(ofid, name, flags, mode, gid, &qid);
	if (err < 0) {
		P9_DPRINTK(P9_DEBUG_VFS,
				"p9_client_open_dotl failed in creat %d\n",
				err);
		goto error;
	}

	/* No need to populate the inode if we are not opening the file AND
	 * not in cached mode.
	 */
	if (!v9ses->cache && !(nd && nd->flags & LOOKUP_OPEN)) {
		/* Not in cached mode. No need to populate inode with stat */
		dentry->d_op = &v9fs_dentry_operations;
		p9_client_clunk(ofid);
		d_instantiate(dentry, NULL);
		return 0;
	}

	/* Now walk from the parent so we can get an unopened fid. */
	fid = p9_client_walk(dfid, 1, &name, 1);
	if (IS_ERR(fid)) {
		err = PTR_ERR(fid);
		P9_DPRINTK(P9_DEBUG_VFS, "p9_client_walk failed %d\n", err);
		fid = NULL;
		goto error;
	}

	/* instantiate inode and assign the unopened fid to dentry */
	inode = v9fs_inode_from_fid(v9ses, fid, dir->i_sb);
	if (IS_ERR(inode)) {
		err = PTR_ERR(inode);
		P9_DPRINTK(P9_DEBUG_VFS, "inode creation failed %d\n", err);
		goto error;
	}
	dentry->d_op = &v9fs_cached_dentry_operations;
	d_instantiate(dentry, inode);
	err = v9fs_fid_add(dentry, fid);
	if (err < 0)
		goto error;

	/* if we are opening a file, assign the open fid to the file */
	if (nd && nd->flags & LOOKUP_OPEN) {
		filp = lookup_instantiate_filp(nd, dentry, v9fs_open_created);
		if (IS_ERR(filp)) {
			p9_client_clunk(ofid);
			return PTR_ERR(filp);
		}
		filp->private_data = ofid;
	} else
		p9_client_clunk(ofid);

	return 0;

error:
	if (ofid)
		p9_client_clunk(ofid);
	if (fid)
		p9_client_clunk(fid);
	return err;
}

760
761
/**
 * v9fs_vfs_create - VFS hook to create files
762
 * @dir: directory inode that is being created
763
764
765
766
767
 * @dentry:  dentry that is being deleted
 * @mode: create permissions
 * @nd: path information
 *
 */
768

769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
static int
v9fs_vfs_create(struct inode *dir, struct dentry *dentry, int mode,
		struct nameidata *nd)
{
	int err;
	u32 perm;
	int flags;
	struct v9fs_session_info *v9ses;
	struct p9_fid *fid;
	struct file *filp;

	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,
790
791
				v9fs_uflags2omode(flags,
						v9fs_proto_dotu(v9ses)));
792
793
794
795
796
797
798
799
	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) {
800
801
		filp = lookup_instantiate_filp(nd, dentry, v9fs_open_created);
		if (IS_ERR(filp)) {
802
803
			err = PTR_ERR(filp);
			goto error;
804
805
		}

806
807
808
		filp->private_data = fid;
	} else
		p9_client_clunk(fid);
809
810
811
812

	return 0;

error:
813
814
	if (fid)
		p9_client_clunk(fid);
815
816

	return err;
817
818
819
820
}

/**
 * v9fs_vfs_mkdir - VFS mkdir hook to create a directory
821
 * @dir:  inode that is being unlinked
822
823
824
825
826
 * @dentry: dentry that is being unlinked
 * @mode: mode for new directory
 *
 */

827
static int v9fs_vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
828
{
829
	int err;
830
	u32 perm;
831
	struct v9fs_session_info *v9ses;
832
	struct p9_fid *fid;
833

834
835
	P9_DPRINTK(P9_DEBUG_VFS, "name %s\n", dentry->d_name.name);
	err = 0;
836
837
	v9ses = v9fs_inode2v9ses(dir);
	perm = unixmode2p9mode(v9ses, mode | S_IFDIR);
838
839
840
841
	fid = v9fs_create(v9ses, dir, dentry, NULL, perm, P9_OREAD);
	if (IS_ERR(fid)) {
		err = PTR_ERR(fid);
		fid = NULL;
842
843
	}

844
845
	if (fid)
		p9_client_clunk(fid);
846
847

	return err;
848
849
}

M. Mohan Kumar's avatar
M. Mohan Kumar committed
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926

/**
 * v9fs_vfs_mkdir_dotl - VFS mkdir hook to create a directory
 * @dir:  inode that is being unlinked
 * @dentry: dentry that is being unlinked
 * @mode: mode for new directory
 *
 */

static int v9fs_vfs_mkdir_dotl(struct inode *dir, struct dentry *dentry,
					int mode)
{
	int err;
	struct v9fs_session_info *v9ses;
	struct p9_fid *fid = NULL, *dfid = NULL;
	gid_t gid;
	char *name;
	struct inode *inode;
	struct p9_qid qid;
	struct dentry *dir_dentry;

	P9_DPRINTK(P9_DEBUG_VFS, "name %s\n", dentry->d_name.name);
	err = 0;
	v9ses = v9fs_inode2v9ses(dir);

	mode |= S_IFDIR;
	dir_dentry = v9fs_dentry_from_dir_inode(dir);
	dfid = v9fs_fid_lookup(dir_dentry);
	if (IS_ERR(dfid)) {
		err = PTR_ERR(dfid);
		P9_DPRINTK(P9_DEBUG_VFS, "fid lookup failed %d\n", err);
		dfid = NULL;
		goto error;
	}

	gid = v9fs_get_fsgid_for_create(dir);
	if (gid < 0) {
		P9_DPRINTK(P9_DEBUG_VFS, "v9fs_get_fsgid_for_create failed\n");
		goto error;
	}

	name = (char *) dentry->d_name.name;
	err = p9_client_mkdir_dotl(dfid, name, mode, gid, &qid);
	if (err < 0)
		goto error;

	/* instantiate inode and assign the unopened fid to the dentry */
	if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE) {
		fid = p9_client_walk(dfid, 1, &name, 1);
		if (IS_ERR(fid)) {
			err = PTR_ERR(fid);
			P9_DPRINTK(P9_DEBUG_VFS, "p9_client_walk failed %d\n",
				err);
			fid = NULL;
			goto error;
		}

		inode = v9fs_inode_from_fid(v9ses, fid, dir->i_sb);
		if (IS_ERR(inode)) {
			err = PTR_ERR(inode);
			P9_DPRINTK(P9_DEBUG_VFS, "inode creation failed %d\n",
				err);
			goto error;
		}
		dentry->d_op = &v9fs_cached_dentry_operations;
		d_instantiate(dentry, inode);
		err = v9fs_fid_add(dentry, fid);
		if (err < 0)
			goto error;
		fid = NULL;
	}
error:
	if (fid)
		p9_client_clunk(fid);
	return err;
}

927
928
929
930
931
932
933
934
935
936
937
938
939
/**
 * 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
 *
 */

static struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry,
				      struct nameidata *nameidata)
{
	struct super_block *sb;
	struct v9fs_session_info *v9ses;
940
	struct p9_fid *dfid, *fid;
941
	struct inode *inode;
942
	char *name;
943
944
	int result = 0;

945
	P9_DPRINTK(P9_DEBUG_VFS, "dir: %p dentry: (%s) %p nameidata: %p\n",
946
		dir, dentry->d_name.name, dentry, nameidata);
947

948
949
950
	if (dentry->d_name.len > NAME_MAX)
		return ERR_PTR(-ENAMETOOLONG);

951
952
	sb = dir->i_sb;
	v9ses = v9fs_inode2v9ses(dir);
953
	/* We can walk d_parent because we hold the dir->i_mutex */
954
955
	dfid = v9fs_fid_lookup(dentry->d_parent);
	if (IS_ERR(dfid))
956
		return ERR_CAST(dfid);
957
958
959
960
961

	name = (char *) dentry->d_name.name;
	fid = p9_client_walk(dfid, 1, &name, 1);
	if (IS_ERR(fid)) {
		result = PTR_ERR(fid);
962
		if (result == -ENOENT) {
963
964
			inode = NULL;
			goto inst_out;
965
966
		}

967
		return ERR_PTR(result);
968
969
	}

970
971
972
973
974
	inode = v9fs_inode_from_fid(v9ses, fid, dir->i_sb);
	if (IS_ERR(inode)) {
		result = PTR_ERR(inode);
		inode = NULL;
		goto error;
975
976
	}

977
	result = v9fs_fid_add(dentry, fid);
978
	if (result < 0)
979
		goto error;
980

981
982
inst_out:
	if (v9ses->cache)
983
984
985
		dentry->d_op = &v9fs_cached_dentry_operations;
	else
		dentry->d_op = &v9fs_dentry_operations;
986
987
988
989

	d_add(dentry, inode);
	return NULL;

990
error:
991
	p9_client_clunk(fid);
992

993
994
995
996
997
998
	return ERR_PTR(result);
}

/**
 * v9fs_vfs_unlink - VFS unlink hook to delete an inode
 * @i:  inode that is being unlinked
999
 * @d: dentry that is being unlinked
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
 *
 */

static int v9fs_vfs_unlink(struct inode *i, struct dentry *d)
{
	return v9fs_remove(i, d, 0);
}

/**
 * v9fs_vfs_rmdir - VFS unlink hook to delete a directory
 * @i:  inode that is being unlinked
1011
 * @d: dentry that is being unlinked
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
 *
 */

static int v9fs_vfs_rmdir(struct inode *i, struct dentry *d)
{
	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
 *
 */

static int
v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
		struct inode *new_dir, struct dentry *new_dentry)
{
1033
1034
1035
1036
1037
1038
1039
	struct inode *old_inode;
	struct v9fs_session_info *v9ses;
	struct p9_fid *oldfid;
	struct p9_fid *olddirfid;
	struct p9_fid *newdirfid;
	struct p9_wstat wstat;
	int retval;
1040

1041
1042
1043
1044
1045
	P9_DPRINTK(P9_DEBUG_VFS, "\n");
	retval = 0;
	old_inode = old_dentry->d_inode;
	v9ses = v9fs_inode2v9ses(old_inode);
	oldfid = v9fs_fid_lookup(old_dentry);
Latchesar Ionkov's avatar
Latchesar Ionkov committed
1046
	if (IS_ERR(oldfid))
1047
1048
1049
		return PTR_ERR(oldfid);

	olddirfid = v9fs_fid_clone(old_dentry->d_parent);
Latchesar Ionkov's avatar
Latchesar Ionkov committed
1050
	if (IS_ERR(olddirfid)) {
1051
		retval = PTR_ERR(olddirfid);
1052
		goto done;
1053
1054
1055
	}

	newdirfid = v9fs_fid_clone(new_dentry->d_parent);
Latchesar Ionkov's avatar
Latchesar Ionkov committed
1056
	if (IS_ERR(newdirfid)) {
1057
		retval = PTR_ERR(newdirfid);
1058
		goto clunk_olddir;
1059
1060
	}

1061
	down_write(&v9ses->rename_sem);
1062
1063
1064
1065
1066
1067
	if (v9fs_proto_dotl(v9ses)) {
		retval = p9_client_rename(oldfid, newdirfid,
					(char *) new_dentry->d_name.name);
		if (retval != -ENOSYS)
			goto clunk_newdir;
	}
1068
1069
1070
1071
	if (old_dentry->d_parent != new_dentry->d_parent) {
		/*
		 * 9P .u can only handle file rename in the same directory
		 */
1072

1073
1074
		P9_DPRINTK(P9_DEBUG_ERROR,
				"old dir and new dir are different\n");
1075
		retval = -EXDEV;
1076
		goto clunk_newdir;
1077
	}
1078
	v9fs_blank_wstat(&wstat);
Latchesar Ionkov's avatar
Latchesar Ionkov committed
1079
	wstat.muid = v9ses->uname;
1080
	wstat.name = (char *) new_dentry->d_name.name;
1081
	retval = p9_client_wstat(oldfid, &wstat);
1082

1083
clunk_newdir:
1084
1085
1086
1087
	if (!retval)
		/* successful rename */
		d_move(old_dentry, new_dentry);
	up_write(&v9ses->rename_sem);
1088
	p9_client_clunk(newdirfid);
1089

1090
clunk_olddir:
1091
	p9_client_clunk(olddirfid);
1092

1093
done:
1094
1095
1096
1097
	return retval;
}

/**
Adrian Bunk's avatar
Adrian Bunk committed
1098
 * v9fs_vfs_getattr - retrieve file metadata
1099
1100
1101
 * @mnt: mount information
 * @dentry: file to get attributes on
 * @stat: metadata structure to populate
1102
1103
1104
1105
1106
1107
1108
 *
 */

static int
v9fs_vfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
		 struct kstat *stat)
{
1109
1110
1111
	int err;
	struct v9fs_session_info *v9ses;
	struct p9_fid *fid;
1112
	struct p9_wstat *st;
1113
1114
1115
1116

	P9_DPRINTK(P9_DEBUG_VFS, "dentry: %p\n", dentry);
	err = -EPERM;
	v9ses = v9fs_inode2v9ses(dentry->d_inode);
1117
	if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE)
1118
1119
		return simple_getattr(mnt, dentry, stat);

1120
1121
	fid = v9fs_fid_lookup(dentry);
	if (IS_ERR(fid))
1122
		return PTR_ERR(fid);
1123

1124
1125
1126
	st = p9_client_stat(fid);
	if (IS_ERR(st))
		return PTR_ERR(st);
1127

1128
	v9fs_stat2inode(st, dentry->d_inode, dentry->d_inode->i_sb);
1129
1130
		generic_fillattr(dentry->d_inode, stat);

1131
1132
	kfree(st);
	return 0;
1133
1134
}

1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
static int
v9fs_vfs_getattr_dotl(struct vfsmount *mnt, struct dentry *dentry,
		 struct kstat *stat)
{
	int err;
	struct v9fs_session_info *v9ses;
	struct p9_fid *fid;
	struct p9_stat_dotl *st;

	P9_DPRINTK(P9_DEBUG_VFS, "dentry: %p\n", dentry);
	err = -EPERM;
	v9ses = v9fs_inode2v9ses(dentry->d_inode);
	if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE)
		return simple_getattr(mnt, dentry, stat);

	fid = v9fs_fid_lookup(dentry);
	if (IS_ERR(fid))
		return PTR_ERR(fid);

	/* Ask for all the fields in stat structure. Server will return
	 * whatever it supports
	 */

	st = p9_client_getattr_dotl(fid, P9_STATS_ALL);
	if (IS_ERR(st))
		return PTR_ERR(st);

	v9fs_stat2inode_dotl(st, dentry->d_inode);
	generic_fillattr(dentry->d_inode, stat);
	/* Change block size to what the server returned */
	stat->blksize = st->st_blksize;

	kfree(st);
	return 0;
}

1171
1172
1173
1174
1175
1176
1177
1178
1179
/**
 * 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)
{
1180
1181
1182
1183
	int retval;
	struct v9fs_session_info *v9ses;
	struct p9_fid *fid;
	struct p9_wstat wstat;
1184

1185
1186
1187
1188
	P9_DPRINTK(P9_DEBUG_VFS, "\n");
	retval = -EPERM;
	v9ses = v9fs_inode2v9ses(dentry->d_inode);
	fid = v9fs_fid_lookup(dentry);
1189
1190
	if(IS_ERR(fid))
		return PTR_ERR(fid);
1191

1192
	v9fs_blank_wstat(&wstat);
1193
	if (iattr->ia_valid & ATTR_MODE)
1194
		wstat.mode = unixmode2p9mode(v9ses, iattr->ia_mode);
1195
1196

	if (iattr->ia_valid & ATTR_MTIME)
1197
		wstat.mtime = iattr->ia_mtime.tv_sec;
1198
1199

	if (iattr->ia_valid & ATTR_ATIME)
1200
		wstat.atime = iattr->ia_atime.tv_sec;
1201
1202

	if (iattr->ia_valid & ATTR_SIZE)
1203
		wstat.length = iattr->ia_size;
1204

1205
	if (v9fs_proto_dotu(v9ses)) {
1206
1207
		if (iattr->ia_valid & ATTR_UID)
			wstat.n_uid = iattr->ia_uid;
1208

1209
1210
		if (iattr->ia_valid & ATTR_GID)
			wstat.n_gid = iattr->ia_gid;
1211
1212
	}

1213
	retval = p9_client_wstat(fid, &wstat);
Christoph Hellwig's avatar
Christoph Hellwig committed
1214
1215
	if (retval < 0)
		return retval;
1216

Christoph Hellwig's avatar
Christoph Hellwig committed
1217
1218
1219
1220
1221
1222
	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;
	}
1223

Christoph Hellwig's avatar
Christoph Hellwig committed
1224
1225
1226
	setattr_copy(dentry->d_inode, iattr);
	mark_inode_dirty(dentry->d_inode);
	return 0;
1227
1228
}

1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
/**
 * v9fs_vfs_setattr_dotl - set file metadata
 * @dentry: file whose metadata to set
 * @iattr: metadata assignment structure
 *
 */

static int v9fs_vfs_setattr_dotl(struct dentry *dentry, struct iattr *iattr)
{
	int retval;
	struct v9fs_session_info *v9ses;
	struct p9_fid *fid;
	struct p9_iattr_dotl p9attr;

	P9_DPRINTK(P9_DEBUG_VFS, "\n");

	retval = inode_change_ok(dentry->d_inode, iattr);
	if (retval)
		return retval;

	p9attr.valid = iattr->ia_valid;
	p9attr.mode = iattr->ia_mode;
	p9attr.uid = iattr->ia_uid;
	p9attr.gid = iattr->ia_gid;
	p9attr.size = iattr->ia_size;
	p9attr.atime_sec = iattr->ia_atime.tv_sec;
	p9attr.atime_nsec = iattr->ia_atime.tv_nsec;
	p9attr.mtime_sec = iattr->ia_mtime.tv_sec;
	p9attr.mtime_nsec = iattr->ia_mtime.tv_nsec;

	retval = -EPERM;
	v9ses = v9fs_inode2v9ses(dentry->d_inode);
	fid = v9fs_fid_lookup(dentry);
	if (IS_ERR(fid))
		return PTR_ERR(fid);

	retval = p9_client_setattr(fid, &p9attr);
1266
1267
	if (retval < 0)
		return retval;
1268

1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
	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;
	}

	setattr_copy(dentry->d_inode, iattr);
	mark_inode_dirty(dentry->d_inode);
	return 0;
1279
1280
}

1281
/**
1282
1283
 * v9fs_stat2inode - populate an inode structure with mistat info
 * @stat: Plan 9 metadata (mistat) structure