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

#include "v9fs.h"
#include "v9fs_vfs.h"
#include "fid.h"
44
#include "cache.h"
45

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

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

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

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

	return res;
}

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

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

	res = mode & 0777;

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

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

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

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

	return res;
}

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

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

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

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

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

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

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

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

	return ret;
}

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

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

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
#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

238
239
240
241
242
243
244
245
246
/**
 * 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)
{
247
	int err;
248
	struct inode *inode;
249
	struct v9fs_session_info *v9ses = sb->s_fs_info;
250

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

	inode = new_inode(sb);
254
255
	if (!inode) {
		P9_EPRINTK(KERN_WARNING, "Problem allocating inode\n");
256
		return ERR_PTR(-ENOMEM);
257
258
	}

259
	inode_init_owner(inode, NULL, mode);
260
261
262
263
264
265
266
267
268
269
	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:
270
		if (!v9fs_proto_dotu(v9ses)) {
271
			P9_DPRINTK(P9_DEBUG_ERROR,
272
273
274
				   "special files without extended mode\n");
			err = -EINVAL;
			goto error;
275
		}
276
277
278
		init_special_inode(inode, inode->i_mode, inode->i_rdev);
		break;
	case S_IFREG:
279
280
281
282
283
284
285
286
		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;
		}

287
		break;
288

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

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

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

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

317
318
319
320
321
322
		break;
	default:
		P9_DPRINTK(P9_DEBUG_ERROR, "BAD mode 0x%x S_IFMT 0x%x\n",
			   mode, mode & S_IFMT);
		err = -EINVAL;
		goto error;
323
	}
324

325
	return inode;
326
327
328
329

error:
	iput(inode);
	return ERR_PTR(err);
330
331
}

332
/*
333
334
335
336
static struct v9fs_fid*
v9fs_clone_walk(struct v9fs_session_info *v9ses, u32 fid, struct dentry *dentry)
{
	int err;
337
	int nfid;
338
339
340
341
342
	struct v9fs_fid *ret;
	struct v9fs_fcall *fcall;

	nfid = v9fs_get_idpool(&v9ses->fidpool);
	if (nfid < 0) {
343
		eprintk(KERN_WARNING, "no free fids available\n");
344
		return ERR_PTR(-ENOSPC);
345
346
	}

347
348
349
350
	err = v9fs_t_walk(v9ses, fid, nfid, (char *) dentry->d_name.name,
		&fcall);

	if (err < 0) {
351
352
353
		if (fcall && fcall->id == RWALK)
			goto clunk_fid;

354
355
356
		PRINT_FCALL_ERROR("walk error", fcall);
		v9fs_put_idpool(nfid, &v9ses->fidpool);
		goto error;
357
	}
358

359
360
	kfree(fcall);
	fcall = NULL;
361
362
363
364
365
	ret = v9fs_fid_create(v9ses, nfid);
	if (!ret) {
		err = -ENOMEM;
		goto clunk_fid;
	}
366

367
368
369
370
	err = v9fs_fid_insert(ret, dentry);
	if (err < 0) {
		v9fs_fid_destroy(ret);
		goto clunk_fid;
371
	}
372

373
	return ret;
374

375
376
clunk_fid:
	v9fs_t_clunk(v9ses, nfid);
377

378
379
380
381
error:
	kfree(fcall);
	return ERR_PTR(err);
}
382
*/
383

384
385
386
387
388
389
390
391
392
393
394
395
396
397
398

/**
 * v9fs_clear_inode - release an inode
 * @inode: inode to release
 *
 */
void v9fs_clear_inode(struct inode *inode)
{
	filemap_fdatawrite(inode->i_mapping);

#ifdef CONFIG_9P_FSCACHE
	v9fs_cache_inode_put_cookie(inode);
#endif
}

399
400
401
402
403
404
405
406
/**
 * v9fs_inode_from_fid - 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
 *
 */

407
static struct inode *
408
v9fs_inode_from_fid(struct v9fs_session_info *v9ses, struct p9_fid *fid,
409
410
411
412
	struct super_block *sb)
{
	int err, umode;
	struct inode *ret;
413
	struct p9_wstat *st;
414

415
	ret = NULL;
416
	st = p9_client_stat(fid);
417
418
	if (IS_ERR(st))
		return ERR_CAST(st);
419

420
	umode = p9mode2unixmode(v9ses, st->mode);
421
422
423
424
425
	ret = v9fs_get_inode(sb, umode);
	if (IS_ERR(ret)) {
		err = PTR_ERR(ret);
		goto error;
	}
426

427
428
	v9fs_stat2inode(st, ret, sb);
	ret->i_ino = v9fs_qid2ino(&st->qid);
429
430
431
432
433

#ifdef CONFIG_9P_FSCACHE
	v9fs_vcookie_set_qid(ret, &st->qid);
	v9fs_cache_inode_get_cookie(ret);
#endif
434
	p9stat_free(st);
435
	kfree(st);
436

437
	return ret;
438

439
error:
440
	p9stat_free(st);
441
	kfree(st);
442
	return ERR_PTR(err);
443
444
445
446
}

/**
 * v9fs_remove - helper function to remove files and directories
447
448
449
 * @dir: directory inode that is being deleted
 * @file:  dentry that is being deleted
 * @rmdir: removing a directory
450
451
452
453
454
 *
 */

static int v9fs_remove(struct inode *dir, struct dentry *file, int rmdir)
{
Aneesh Kumar K.V's avatar
Aneesh Kumar K.V committed
455
	int retval;
456
457
	struct inode *file_inode;
	struct p9_fid *v9fid;
458

459
	P9_DPRINTK(P9_DEBUG_VFS, "inode: %p dentry: %p rmdir: %d\n", dir, file,
460
461
462
		rmdir);

	file_inode = file->d_inode;
463
	v9fid = v9fs_fid_clone(file);
Latchesar Ionkov's avatar
Latchesar Ionkov committed
464
	if (IS_ERR(v9fid))
465
		return PTR_ERR(v9fid);
466

Aneesh Kumar K.V's avatar
Aneesh Kumar K.V committed
467
468
469
470
	retval = p9_client_remove(v9fid);
	if (!retval)
		drop_nlink(file_inode);
	return retval;
471
472
}

473
474
475
476
477
478
static int
v9fs_open_created(struct inode *inode, struct file *file)
{
	return 0;
}

479

480
/**
481
 * v9fs_create - Create a file
482
483
 * @v9ses: session information
 * @dir: directory that dentry is being created in
484
 * @dentry:  dentry that is being created
Abhishek Kulkarni's avatar
Abhishek Kulkarni committed
485
 * @extension: 9p2000.u extension string to support devices, etc.
486
487
 * @perm: create permissions
 * @mode: open mode
488
489
 *
 */
490
491
492
static struct p9_fid *
v9fs_create(struct v9fs_session_info *v9ses, struct inode *dir,
		struct dentry *dentry, char *extension, u32 perm, u8 mode)
493
{
494
	int err;
495
496
	char *name;
	struct p9_fid *dfid, *ofid, *fid;
497
498
	struct inode *inode;

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

501
502
503
504
	err = 0;
	ofid = NULL;
	fid = NULL;
	name = (char *) dentry->d_name.name;
505
	dfid = v9fs_fid_lookup(dentry->d_parent);
Latchesar Ionkov's avatar
Latchesar Ionkov committed
506
	if (IS_ERR(dfid)) {
507
		err = PTR_ERR(dfid);
508
509
		P9_DPRINTK(P9_DEBUG_VFS, "fid lookup failed %d\n", err);
		return ERR_PTR(err);
510
	}
511

512
513
514
515
	/* clone a fid to use for creation */
	ofid = p9_client_walk(dfid, 0, NULL, 1);
	if (IS_ERR(ofid)) {
		err = PTR_ERR(ofid);
516
		P9_DPRINTK(P9_DEBUG_VFS, "p9_client_walk failed %d\n", err);
517
		return ERR_PTR(err);
518
	}
519

520
	err = p9_client_fcreate(ofid, name, perm, mode, extension);
521
522
	if (err < 0) {
		P9_DPRINTK(P9_DEBUG_VFS, "p9_client_fcreate failed %d\n", err);
523
		goto error;
524
	}
525

526
	/* now walk from the parent so we can get unopened fid */
527
	fid = p9_client_walk(dfid, 1, &name, 1);
528
529
	if (IS_ERR(fid)) {
		err = PTR_ERR(fid);
530
		P9_DPRINTK(P9_DEBUG_VFS, "p9_client_walk failed %d\n", err);
531
		fid = NULL;
532
		goto error;
533
	}
534

535
536
	/* instantiate inode and assign the unopened fid to the dentry */
	inode = v9fs_inode_from_fid(v9ses, fid, dir->i_sb);
537
538
	if (IS_ERR(inode)) {
		err = PTR_ERR(inode);
539
		P9_DPRINTK(P9_DEBUG_VFS, "inode creation failed %d\n", err);
540
541
542
		goto error;
	}

Latchesar Ionkov's avatar
Latchesar Ionkov committed
543
	if (v9ses->cache)
544
545
546
		dentry->d_op = &v9fs_cached_dentry_operations;
	else
		dentry->d_op = &v9fs_dentry_operations;
547

548
	d_instantiate(dentry, inode);
549
550
551
552
	err = v9fs_fid_add(dentry, fid);
	if (err < 0)
		goto error;

553
	return ofid;
554

555
556
557
558
559
560
561
562
563
564
565
566
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
567
 * @dir: directory inode that is being created
568
569
570
571
572
 * @dentry:  dentry that is being deleted
 * @mode: create permissions
 * @nd: path information
 *
 */
573

574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
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,
595
596
				v9fs_uflags2omode(flags,
						v9fs_proto_dotu(v9ses)));
597
598
599
600
601
602
603
604
	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) {
605
606
		filp = lookup_instantiate_filp(nd, dentry, v9fs_open_created);
		if (IS_ERR(filp)) {
607
608
			err = PTR_ERR(filp);
			goto error;
609
610
		}

611
612
613
		filp->private_data = fid;
	} else
		p9_client_clunk(fid);
614
615
616
617

	return 0;

error:
618
619
	if (fid)
		p9_client_clunk(fid);
620
621

	return err;
622
623
624
625
}

/**
 * v9fs_vfs_mkdir - VFS mkdir hook to create a directory
626
 * @dir:  inode that is being unlinked
627
628
629
630
631
 * @dentry: dentry that is being unlinked
 * @mode: mode for new directory
 *
 */

632
static int v9fs_vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
633
{
634
	int err;
635
	u32 perm;
636
	struct v9fs_session_info *v9ses;
637
	struct p9_fid *fid;
638

639
640
	P9_DPRINTK(P9_DEBUG_VFS, "name %s\n", dentry->d_name.name);
	err = 0;
641
642
	v9ses = v9fs_inode2v9ses(dir);
	perm = unixmode2p9mode(v9ses, mode | S_IFDIR);
643
644
645
646
	fid = v9fs_create(v9ses, dir, dentry, NULL, perm, P9_OREAD);
	if (IS_ERR(fid)) {
		err = PTR_ERR(fid);
		fid = NULL;
647
648
	}

649
650
	if (fid)
		p9_client_clunk(fid);
651
652

	return err;
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
}

/**
 * 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;
668
	struct p9_fid *dfid, *fid;
669
	struct inode *inode;
670
	char *name;
671
672
	int result = 0;

673
	P9_DPRINTK(P9_DEBUG_VFS, "dir: %p dentry: (%s) %p nameidata: %p\n",
674
		dir, dentry->d_name.name, dentry, nameidata);
675

676
677
678
	if (dentry->d_name.len > NAME_MAX)
		return ERR_PTR(-ENAMETOOLONG);

679
680
	sb = dir->i_sb;
	v9ses = v9fs_inode2v9ses(dir);
681
682
	dfid = v9fs_fid_lookup(dentry->d_parent);
	if (IS_ERR(dfid))
683
		return ERR_CAST(dfid);
684
685
686
687
688

	name = (char *) dentry->d_name.name;
	fid = p9_client_walk(dfid, 1, &name, 1);
	if (IS_ERR(fid)) {
		result = PTR_ERR(fid);
689
		if (result == -ENOENT) {
690
691
			inode = NULL;
			goto inst_out;
692
693
		}

694
		return ERR_PTR(result);
695
696
	}

697
698
699
700
701
	inode = v9fs_inode_from_fid(v9ses, fid, dir->i_sb);
	if (IS_ERR(inode)) {
		result = PTR_ERR(inode);
		inode = NULL;
		goto error;
702
703
	}

704
	result = v9fs_fid_add(dentry, fid);
705
	if (result < 0)
706
		goto error;
707

708
709
inst_out:
	if (v9ses->cache)
710
711
712
		dentry->d_op = &v9fs_cached_dentry_operations;
	else
		dentry->d_op = &v9fs_dentry_operations;
713
714
715
716

	d_add(dentry, inode);
	return NULL;

717
error:
718
	p9_client_clunk(fid);
719

720
721
722
723
724
725
	return ERR_PTR(result);
}

/**
 * v9fs_vfs_unlink - VFS unlink hook to delete an inode
 * @i:  inode that is being unlinked
726
 * @d: dentry that is being unlinked
727
728
729
730
731
732
733
734
735
736
737
 *
 */

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
738
 * @d: dentry that is being unlinked
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
 *
 */

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)
{
760
761
762
763
764
765
766
	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;
767

768
769
770
771
772
	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
773
	if (IS_ERR(oldfid))
774
775
776
		return PTR_ERR(oldfid);

	olddirfid = v9fs_fid_clone(old_dentry->d_parent);
Latchesar Ionkov's avatar
Latchesar Ionkov committed
777
	if (IS_ERR(olddirfid)) {
778
		retval = PTR_ERR(olddirfid);
779
		goto done;
780
781
782
	}

	newdirfid = v9fs_fid_clone(new_dentry->d_parent);
Latchesar Ionkov's avatar
Latchesar Ionkov committed
783
	if (IS_ERR(newdirfid)) {
784
		retval = PTR_ERR(newdirfid);
785
		goto clunk_olddir;
786
787
	}

788
789
790
791
792
793
794
	if (v9fs_proto_dotl(v9ses)) {
		retval = p9_client_rename(oldfid, newdirfid,
					(char *) new_dentry->d_name.name);
		if (retval != -ENOSYS)
			goto clunk_newdir;
	}

795
796
	/* 9P can only handle file rename in the same directory */
	if (memcmp(&olddirfid->qid, &newdirfid->qid, sizeof(newdirfid->qid))) {
797
798
		P9_DPRINTK(P9_DEBUG_ERROR,
				"old dir and new dir are different\n");
799
		retval = -EXDEV;
800
		goto clunk_newdir;
801
802
	}

803
	v9fs_blank_wstat(&wstat);
Latchesar Ionkov's avatar
Latchesar Ionkov committed
804
	wstat.muid = v9ses->uname;
805
	wstat.name = (char *) new_dentry->d_name.name;
806
	retval = p9_client_wstat(oldfid, &wstat);
807

808
clunk_newdir:
809
	p9_client_clunk(newdirfid);
810

811
clunk_olddir:
812
	p9_client_clunk(olddirfid);
813

814
done:
815
816
817
818
	return retval;
}

/**
Adrian Bunk's avatar
Adrian Bunk committed
819
 * v9fs_vfs_getattr - retrieve file metadata
820
821
822
 * @mnt: mount information
 * @dentry: file to get attributes on
 * @stat: metadata structure to populate
823
824
825
826
827
828
829
 *
 */

static int
v9fs_vfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
		 struct kstat *stat)
{
830
831
832
	int err;
	struct v9fs_session_info *v9ses;
	struct p9_fid *fid;
833
	struct p9_wstat *st;
834
835
836
837

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

841
842
	fid = v9fs_fid_lookup(dentry);
	if (IS_ERR(fid))
843
		return PTR_ERR(fid);
844

845
846
847
	st = p9_client_stat(fid);
	if (IS_ERR(st))
		return PTR_ERR(st);
848

849
	v9fs_stat2inode(st, dentry->d_inode, dentry->d_inode->i_sb);
850
851
		generic_fillattr(dentry->d_inode, stat);

852
853
	kfree(st);
	return 0;
854
855
856
857
858
859
860
861
862
863
864
}

/**
 * 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)
{
865
866
867
868
	int retval;
	struct v9fs_session_info *v9ses;
	struct p9_fid *fid;
	struct p9_wstat wstat;
869

870
871
872
873
	P9_DPRINTK(P9_DEBUG_VFS, "\n");
	retval = -EPERM;
	v9ses = v9fs_inode2v9ses(dentry->d_inode);
	fid = v9fs_fid_lookup(dentry);
874
875
	if(IS_ERR(fid))
		return PTR_ERR(fid);
876

877
	v9fs_blank_wstat(&wstat);
878
	if (iattr->ia_valid & ATTR_MODE)
879
		wstat.mode = unixmode2p9mode(v9ses, iattr->ia_mode);
880
881

	if (iattr->ia_valid & ATTR_MTIME)
882
		wstat.mtime = iattr->ia_mtime.tv_sec;
883
884

	if (iattr->ia_valid & ATTR_ATIME)
885
		wstat.atime = iattr->ia_atime.tv_sec;
886
887

	if (iattr->ia_valid & ATTR_SIZE)
888
		wstat.length = iattr->ia_size;
889

890
	if (v9fs_proto_dotu(v9ses)) {
891
892
		if (iattr->ia_valid & ATTR_UID)
			wstat.n_uid = iattr->ia_uid;
893

894
895
		if (iattr->ia_valid & ATTR_GID)
			wstat.n_gid = iattr->ia_gid;
896
897
	}

898
	retval = p9_client_wstat(fid, &wstat);
Christoph Hellwig's avatar
Christoph Hellwig committed
899
900
901
902
903
904
905
906
907
	if (retval < 0)
		return retval;

	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;
	}
908

Christoph Hellwig's avatar
Christoph Hellwig committed
909
910
911
	setattr_copy(dentry->d_inode, iattr);
	mark_inode_dirty(dentry->d_inode);
	return 0;
912
913
914
}

/**
915
916
 * v9fs_stat2inode - populate an inode structure with mistat info
 * @stat: Plan 9 metadata (mistat) structure
917
918
919
920
921
922
 * @inode: inode to populate
 * @sb: superblock of filesystem
 *
 */

void
923
v9fs_stat2inode(struct p9_wstat *stat, struct inode *inode,
924
	struct super_block *sb)
925
{
926
	char ext[32];
927
928
	char tag_name[14];
	unsigned int i_nlink;
929
930
931
932
	struct v9fs_session_info *v9ses = sb->s_fs_info;

	inode->i_nlink = 1;

933
934
935
	inode->i_atime.tv_sec = stat->atime;
	inode->i_mtime.tv_sec = stat->mtime;
	inode->i_ctime.tv_sec = stat->mtime;
936

937
938
	inode->i_uid = v9ses->dfltuid;
	inode->i_gid = v9ses->dfltgid;
939

940
	if (v9fs_proto_dotu(v9ses)) {
941
942
		inode->i_uid = stat->n_uid;
		inode->i_gid = stat->n_gid;
943
	}
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
	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;
		}
	}
960
	inode->i_mode = p9mode2unixmode(v9ses, stat->mode);
961
962
963
964
	if ((S_ISBLK(inode->i_mode)) || (S_ISCHR(inode->i_mode))) {
		char type = 0;
		int major = -1;
		int minor = -1;
965

966
		strncpy(ext, stat->extension, sizeof(ext));
967
		sscanf(ext, "%c %u %u", &type, &major, &minor);
968
969
970
971
972
973
974
975
		switch (type) {
		case 'c':
			inode->i_mode &= ~S_IFBLK;
			inode->i_mode |= S_IFCHR;
			break;
		case 'b':
			break;
		default:
976
			P9_DPRINTK(P9_DEBUG_ERROR,
977
978
				"Unknown special type %c %s\n", type,
				stat->extension);
979
980
		};
		inode->i_rdev = MKDEV(major, minor);
981
		init_special_inode(inode, inode->i_mode, inode->i_rdev);
982
983
984
	} else
		inode->i_rdev = 0;

985
	i_size_write(inode, stat->length);
986

987
	/* not real number of blocks, but 512 byte ones ... */
988
	inode->i_blocks = (i_size_read(inode) + 512 - 1) >> 9;
989
990
991
992
993
994
995
996
997
}

/**
 * v9fs_qid2ino - convert qid into inode number
 * @qid: qid to hash
 *
 * BUG: potential for inode number collisions?
 */

998
ino_t v9fs_qid2ino(struct p9_qid *qid)
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
{
	u64 path = qid->path + 2;
	ino_t i = 0;

	if (sizeof(ino_t) == sizeof(path))
		memcpy(&i, &path, sizeof(ino_t));
	else
		i = (ino_t) (path ^ (path >> 32));

	return i;
}

/**
 * v9fs_readlink - read a symlink's location (internal version)
 * @dentry: dentry for symlink
1014
 * @buffer: buffer to load symlink location into
1015
1016
1017
1018
1019
1020
 * @buflen: length of buffer
 *
 */

static int v9fs_readlink(struct dentry *dentry, char *buffer, int buflen)
{
1021
	int retval;
1022

1023
1024
	struct v9fs_session_info *v9ses;
	struct p9_fid *fid;
1025
	struct p9_wstat *st;
1026

1027
1028
1029
1030
	P9_DPRINTK(P9_DEBUG_VFS, " %s\n", dentry->d_name.name);
	retval = -EPERM;
	v9ses = v9fs_inode2v9ses(dentry->d_inode);
	fid = v9fs_fid_lookup(dentry);
Latchesar Ionkov's avatar
Latchesar Ionkov committed
1031
	if (IS_ERR(fid))
1032
		return PTR_ERR(fid);
1033

1034
	if (!v9fs_proto_dotu(v9ses))
1035
		return -EBADF;
1036

1037
1038
1039
	st = p9_client_stat(fid);
	if (IS_ERR(st))
		return PTR_ERR(st);
1040

1041
	if (!(st->mode & P9_DMSYMLINK)) {
1042
		retval = -EINVAL;
1043
		goto done;
1044
1045
1046
	}

	/* copy extension buffer into buffer */
1047
	strncpy(buffer, st->extension, buflen);
1048

1049
	P9_DPRINTK(P9_DEBUG_VFS,
1050
		"%s -> %s (%s)\n", dentry->d_name.name, st->extension, buffer);
1051

Martin Stava's avatar
Martin Stava committed
1052
	retval = strnlen(buffer, buflen);
1053
1054
done:
	kfree(st);
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
	return retval;
}

/**
 * v9fs_vfs_follow_link - follow a symlink path
 * @dentry: dentry for symlink
 * @nd: nameidata
 *
 */

static void *v9fs_vfs_follow_link(struct dentry *dentry, struct nameidata *nd)
{
	int len = 0;
	char *link = __getname();

1070
	P9_DPRINTK(P9_DEBUG_VFS, "%s n", dentry->d_name.name);
1071
1072
1073
1074

	if (!link)
		link = ERR_PTR(-ENOMEM);
	else {
1075
		len = v9fs_readlink(dentry, link, PATH_MAX);
1076
1077

		if (len < 0) {
1078
			__putname(link);
1079
1080
			link = ERR_PTR(len);
		} else
Martin Stava's avatar
Martin Stava committed
1081
			link[min(len, PATH_MAX-1)] = 0;
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
	}
	nd_set_link(nd, link);

	return NULL;
}

/**
 * v9fs_vfs_put_link - release a symlink path
 * @dentry: dentry for symlink
 * @nd: nameidata
1092
 * @p: unused
1093
1094
1095
 *
 */

1096
1097
static void
v9fs_vfs_put_link(struct dentry *dentry, struct nameidata *nd, void *p)
1098
1099
1100
{
	char *s = nd_get_link(nd);