vfs_inode_dotl.c 24.8 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/*
 *  linux/fs/9p/vfs_inode_dotl.c
 *
 * This file contains vfs inode ops for the 9P2000.L protocol.
 *
 *  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
 *  it under the terms of the GNU General Public License version 2
 *  as published by the Free Software Foundation.
 *
 *  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>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/xattr.h>
#include <linux/posix_acl.h>
#include <net/9p/9p.h>
#include <net/9p/client.h>

#include "v9fs.h"
#include "v9fs_vfs.h"
#include "fid.h"
#include "cache.h"
#include "xattr.h"
#include "acl.h"

static int
Al Viro's avatar
Al Viro committed
51
v9fs_vfs_mknod_dotl(struct inode *dir, struct dentry *dentry, umode_t omode,
52
53
54
55
56
57
58
59
		    dev_t rdev);

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

60
static kgid_t v9fs_get_fsgid_for_create(struct inode *dir_inode)
61
62
63
64
65
66
67
68
69
70
{
	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();
}

71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
static int v9fs_test_inode_dotl(struct inode *inode, void *data)
{
	struct v9fs_inode *v9inode = V9FS_I(inode);
	struct p9_stat_dotl *st = (struct p9_stat_dotl *)data;

	/* don't match inode of different type */
	if ((inode->i_mode & S_IFMT) != (st->st_mode & S_IFMT))
		return 0;

	if (inode->i_generation != st->st_gen)
		return 0;

	/* compare qid details */
	if (memcmp(&v9inode->qid.version,
		   &st->qid.version, sizeof(v9inode->qid.version)))
		return 0;

	if (v9inode->qid.type != st->qid.type)
		return 0;
	return 1;
}

93
94
95
96
97
98
/* Always get a new inode */
static int v9fs_test_new_inode_dotl(struct inode *inode, void *data)
{
	return 0;
}

99
100
101
102
103
104
105
106
107
108
static int v9fs_set_inode_dotl(struct inode *inode,  void *data)
{
	struct v9fs_inode *v9inode = V9FS_I(inode);
	struct p9_stat_dotl *st = (struct p9_stat_dotl *)data;

	memcpy(&v9inode->qid, &st->qid, sizeof(st->qid));
	inode->i_generation = st->st_gen;
	return 0;
}

Aneesh Kumar K.V's avatar
Aneesh Kumar K.V committed
109
110
111
static struct inode *v9fs_qid_iget_dotl(struct super_block *sb,
					struct p9_qid *qid,
					struct p9_fid *fid,
112
113
					struct p9_stat_dotl *st,
					int new)
Aneesh Kumar K.V's avatar
Aneesh Kumar K.V committed
114
115
116
117
118
{
	int retval;
	unsigned long i_ino;
	struct inode *inode;
	struct v9fs_session_info *v9ses = sb->s_fs_info;
119
120
121
122
123
124
	int (*test)(struct inode *, void *);

	if (new)
		test = v9fs_test_new_inode_dotl;
	else
		test = v9fs_test_inode_dotl;
Aneesh Kumar K.V's avatar
Aneesh Kumar K.V committed
125
126

	i_ino = v9fs_qid2ino(qid);
127
	inode = iget5_locked(sb, i_ino, test, v9fs_set_inode_dotl, st);
Aneesh Kumar K.V's avatar
Aneesh Kumar K.V committed
128
129
130
131
132
133
134
135
136
	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.
	 */
137
	inode->i_ino = i_ino;
138
139
	retval = v9fs_init_inode(v9ses, inode,
				 st->st_mode, new_decode_dev(st->st_rdev));
Aneesh Kumar K.V's avatar
Aneesh Kumar K.V committed
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
	if (retval)
		goto error;

	v9fs_stat2inode_dotl(st, inode);
	v9fs_cache_inode_get_cookie(inode);
	retval = v9fs_get_acl(inode, fid);
	if (retval)
		goto error;

	unlock_new_inode(inode);
	return inode;
error:
	unlock_new_inode(inode);
	iput(inode);
	return ERR_PTR(retval);

}

158
struct inode *
Aneesh Kumar K.V's avatar
Aneesh Kumar K.V committed
159
v9fs_inode_from_fid_dotl(struct v9fs_session_info *v9ses, struct p9_fid *fid,
160
			 struct super_block *sb, int new)
161
162
{
	struct p9_stat_dotl *st;
Aneesh Kumar K.V's avatar
Aneesh Kumar K.V committed
163
	struct inode *inode = NULL;
164

165
	st = p9_client_getattr_dotl(fid, P9_STATS_BASIC | P9_STATS_GEN);
166
167
168
	if (IS_ERR(st))
		return ERR_CAST(st);

169
	inode = v9fs_qid_iget_dotl(sb, &st->qid, fid, st, new);
170
	kfree(st);
Aneesh Kumar K.V's avatar
Aneesh Kumar K.V committed
171
	return inode;
172
173
}

174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
struct dotl_openflag_map {
	int open_flag;
	int dotl_flag;
};

static int v9fs_mapped_dotl_flags(int flags)
{
	int i;
	int rflags = 0;
	struct dotl_openflag_map dotl_oflag_map[] = {
		{ O_CREAT,	P9_DOTL_CREATE },
		{ O_EXCL,	P9_DOTL_EXCL },
		{ O_NOCTTY,	P9_DOTL_NOCTTY },
		{ O_APPEND,	P9_DOTL_APPEND },
		{ O_NONBLOCK,	P9_DOTL_NONBLOCK },
		{ O_DSYNC,	P9_DOTL_DSYNC },
		{ FASYNC,	P9_DOTL_FASYNC },
		{ O_DIRECT,	P9_DOTL_DIRECT },
		{ O_LARGEFILE,	P9_DOTL_LARGEFILE },
		{ O_DIRECTORY,	P9_DOTL_DIRECTORY },
		{ O_NOFOLLOW,	P9_DOTL_NOFOLLOW },
		{ O_NOATIME,	P9_DOTL_NOATIME },
		{ O_CLOEXEC,	P9_DOTL_CLOEXEC },
		{ O_SYNC,	P9_DOTL_SYNC},
	};
	for (i = 0; i < ARRAY_SIZE(dotl_oflag_map); i++) {
		if (flags & dotl_oflag_map[i].open_flag)
			rflags |= dotl_oflag_map[i].dotl_flag;
	}
	return rflags;
}

/**
 * v9fs_open_to_dotl_flags- convert Linux specific open flags to
 * plan 9 open flag.
 * @flags: flags to convert
 */
int v9fs_open_to_dotl_flags(int flags)
{
	int rflags = 0;

	/*
	 * We have same bits for P9_DOTL_READONLY, P9_DOTL_WRONLY
	 * and P9_DOTL_NOACCESS
	 */
	rflags |= flags & O_ACCMODE;
	rflags |= v9fs_mapped_dotl_flags(flags);

	return rflags;
}

225
226
227
228
/**
 * 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
Fabian Frederick's avatar
Fabian Frederick committed
229
 * @omode: create permissions
230
231
232
233
 *
 */

static int
Al Viro's avatar
Al Viro committed
234
v9fs_vfs_create_dotl(struct inode *dir, struct dentry *dentry, umode_t omode,
Al Viro's avatar
Al Viro committed
235
		bool excl)
236
237
238
239
{
	return v9fs_vfs_mknod_dotl(dir, dentry, omode, 0);
}

Al Viro's avatar
Al Viro committed
240
static int
241
v9fs_vfs_atomic_open_dotl(struct inode *dir, struct dentry *dentry,
Al Viro's avatar
Al Viro committed
242
			  struct file *file, unsigned flags, umode_t omode,
243
			  int *opened)
244
245
{
	int err = 0;
246
	kgid_t gid;
247
	umode_t mode;
248
	char *name = NULL;
249
250
	struct p9_qid qid;
	struct inode *inode;
251
252
253
254
	struct p9_fid *fid = NULL;
	struct v9fs_inode *v9inode;
	struct p9_fid *dfid, *ofid, *inode_fid;
	struct v9fs_session_info *v9ses;
255
	struct posix_acl *pacl = NULL, *dacl = NULL;
256
	struct dentry *res = NULL;
257

258
	if (d_unhashed(dentry)) {
Al Viro's avatar
Al Viro committed
259
		res = v9fs_vfs_lookup(dir, dentry, 0);
260
		if (IS_ERR(res))
Al Viro's avatar
Al Viro committed
261
			return PTR_ERR(res);
262
263
264
265
266
267

		if (res)
			dentry = res;
	}

	/* Only creates */
268
	if (!(flags & O_CREAT) || d_really_is_positive(dentry))
M. Mohan Kumar's avatar
M. Mohan Kumar committed
269
		return	finish_no_open(file, res);
270

271
272
	v9ses = v9fs_inode2v9ses(dir);

273
	name = (char *) dentry->d_name.name;
274
	p9_debug(P9_DEBUG_VFS, "name:%s flags:0x%x mode:0x%hx\n",
275
		 name, flags, omode);
276
277
278
279

	dfid = v9fs_fid_lookup(dentry->d_parent);
	if (IS_ERR(dfid)) {
		err = PTR_ERR(dfid);
280
		p9_debug(P9_DEBUG_VFS, "fid lookup failed %d\n", err);
Al Viro's avatar
Al Viro committed
281
		goto out;
282
283
284
285
286
287
	}

	/* clone a fid to use for creation */
	ofid = p9_client_walk(dfid, 0, NULL, 1);
	if (IS_ERR(ofid)) {
		err = PTR_ERR(ofid);
288
		p9_debug(P9_DEBUG_VFS, "p9_client_walk failed %d\n", err);
Al Viro's avatar
Al Viro committed
289
		goto out;
290
291
292
293
294
295
296
297
	}

	gid = v9fs_get_fsgid_for_create(dir);

	mode = omode;
	/* Update mode based on ACL value */
	err = v9fs_acl_mode(dir, &mode, &dacl, &pacl);
	if (err) {
298
299
		p9_debug(P9_DEBUG_VFS, "Failed to get acl values in creat %d\n",
			 err);
300
301
		goto error;
	}
302
303
	err = p9_client_create_dotl(ofid, name, v9fs_open_to_dotl_flags(flags),
				    mode, gid, &qid);
304
	if (err < 0) {
305
306
		p9_debug(P9_DEBUG_VFS, "p9_client_open_dotl failed in creat %d\n",
			 err);
307
308
		goto error;
	}
309
	v9fs_invalidate_inode_attr(dir);
310

311
312
313
314
	/* instantiate inode and assign the unopened fid to the dentry */
	fid = p9_client_walk(dfid, 1, &name, 1);
	if (IS_ERR(fid)) {
		err = PTR_ERR(fid);
315
		p9_debug(P9_DEBUG_VFS, "p9_client_walk failed %d\n", err);
316
		fid = NULL;
317
		goto error;
318
	}
319
	inode = v9fs_get_new_inode_from_fid(v9ses, fid, dir->i_sb);
320
321
	if (IS_ERR(inode)) {
		err = PTR_ERR(inode);
322
		p9_debug(P9_DEBUG_VFS, "inode creation failed %d\n", err);
323
324
		goto error;
	}
325
326
327
	/* Now set the ACL based on the default value */
	v9fs_set_create_acl(inode, fid, dacl, pacl);

Al Viro's avatar
Al Viro committed
328
	v9fs_fid_add(dentry, fid);
329
	d_instantiate(dentry, inode);
330

331
	v9inode = V9FS_I(inode);
332
	mutex_lock(&v9inode->v_mutex);
333
334
	if ((v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE) &&
	    !v9inode->writeback_fid &&
335
	    ((flags & O_ACCMODE) != O_RDONLY)) {
336
		/*
337
		 * clone a fid and add it to writeback_fid
338
339
340
341
342
343
344
345
		 * 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);
346
			mutex_unlock(&v9inode->v_mutex);
347
			goto err_clunk_old_fid;
348
		}
349
		v9inode->writeback_fid = (void *) inode_fid;
350
	}
351
	mutex_unlock(&v9inode->v_mutex);
352
	/* Since we are opening a file, assign the open fid to the file */
Al Viro's avatar
Al Viro committed
353
354
	err = finish_open(file, dentry, generic_file_open, opened);
	if (err)
355
		goto err_clunk_old_fid;
Al Viro's avatar
Al Viro committed
356
	file->private_data = ofid;
357
	if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE)
Al Viro's avatar
Al Viro committed
358
		v9fs_cache_inode_set_cookie(inode, file);
359
	*opened |= FILE_CREATED;
360
out:
361
	v9fs_put_acl(dacl, pacl);
362
	dput(res);
Al Viro's avatar
Al Viro committed
363
	return err;
364
365
366
367

error:
	if (fid)
		p9_client_clunk(fid);
368
369
370
err_clunk_old_fid:
	if (ofid)
		p9_client_clunk(ofid);
371
	goto out;
372
373
374
375
376
377
}

/**
 * v9fs_vfs_mkdir_dotl - VFS mkdir hook to create a directory
 * @dir:  inode that is being unlinked
 * @dentry: dentry that is being unlinked
Fabian Frederick's avatar
Fabian Frederick committed
378
 * @omode: mode for new directory
379
380
381
382
 *
 */

static int v9fs_vfs_mkdir_dotl(struct inode *dir,
383
			       struct dentry *dentry, umode_t omode)
384
385
386
387
{
	int err;
	struct v9fs_session_info *v9ses;
	struct p9_fid *fid = NULL, *dfid = NULL;
388
	kgid_t gid;
389
	char *name;
390
	umode_t mode;
391
392
393
394
395
	struct inode *inode;
	struct p9_qid qid;
	struct dentry *dir_dentry;
	struct posix_acl *dacl = NULL, *pacl = NULL;

Al Viro's avatar
Al Viro committed
396
	p9_debug(P9_DEBUG_VFS, "name %pd\n", dentry);
397
398
399
400
401
402
403
	err = 0;
	v9ses = v9fs_inode2v9ses(dir);

	omode |= S_IFDIR;
	if (dir->i_mode & S_ISGID)
		omode |= S_ISGID;

Al Viro's avatar
Al Viro committed
404
	dir_dentry = dentry->d_parent;
405
406
407
	dfid = v9fs_fid_lookup(dir_dentry);
	if (IS_ERR(dfid)) {
		err = PTR_ERR(dfid);
408
		p9_debug(P9_DEBUG_VFS, "fid lookup failed %d\n", err);
409
410
411
412
413
414
415
416
417
		dfid = NULL;
		goto error;
	}

	gid = v9fs_get_fsgid_for_create(dir);
	mode = omode;
	/* Update mode based on ACL value */
	err = v9fs_acl_mode(dir, &mode, &dacl, &pacl);
	if (err) {
418
419
		p9_debug(P9_DEBUG_VFS, "Failed to get acl values in mkdir %d\n",
			 err);
420
421
422
423
424
425
426
		goto error;
	}
	name = (char *) dentry->d_name.name;
	err = p9_client_mkdir_dotl(dfid, name, mode, gid, &qid);
	if (err < 0)
		goto error;

427
428
429
430
431
432
433
434
435
	fid = p9_client_walk(dfid, 1, &name, 1);
	if (IS_ERR(fid)) {
		err = PTR_ERR(fid);
		p9_debug(P9_DEBUG_VFS, "p9_client_walk failed %d\n",
			 err);
		fid = NULL;
		goto error;
	}

436
437
	/* instantiate inode and assign the unopened fid to the dentry */
	if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE) {
438
		inode = v9fs_get_new_inode_from_fid(v9ses, fid, dir->i_sb);
439
440
		if (IS_ERR(inode)) {
			err = PTR_ERR(inode);
441
442
			p9_debug(P9_DEBUG_VFS, "inode creation failed %d\n",
				 err);
443
444
			goto error;
		}
Al Viro's avatar
Al Viro committed
445
		v9fs_fid_add(dentry, fid);
446
		v9fs_set_create_acl(inode, fid, dacl, pacl);
447
		d_instantiate(dentry, inode);
448
		fid = NULL;
Al Viro's avatar
Al Viro committed
449
		err = 0;
450
451
452
453
454
455
	} else {
		/*
		 * Not in cached mode. No need to populate
		 * inode with stat. We need to get an inode
		 * so that we can set the acl with dentry
		 */
456
		inode = v9fs_get_inode(dir->i_sb, mode, 0);
457
458
459
460
		if (IS_ERR(inode)) {
			err = PTR_ERR(inode);
			goto error;
		}
461
		v9fs_set_create_acl(inode, fid, dacl, pacl);
462
463
		d_instantiate(dentry, inode);
	}
464
	inc_nlink(dir);
465
	v9fs_invalidate_inode_attr(dir);
466
467
468
error:
	if (fid)
		p9_client_clunk(fid);
469
	v9fs_put_acl(dacl, pacl);
470
471
472
473
474
475
476
477
478
479
480
	return err;
}

static int
v9fs_vfs_getattr_dotl(struct vfsmount *mnt, struct dentry *dentry,
		 struct kstat *stat)
{
	struct v9fs_session_info *v9ses;
	struct p9_fid *fid;
	struct p9_stat_dotl *st;

481
	p9_debug(P9_DEBUG_VFS, "dentry: %p\n", dentry);
482
	v9ses = v9fs_dentry2v9ses(dentry);
483
	if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE) {
484
		generic_fillattr(d_inode(dentry), stat);
485
486
		return 0;
	}
487
488
489
490
491
492
493
494
495
496
497
498
	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);

499
500
	v9fs_stat2inode_dotl(st, d_inode(dentry));
	generic_fillattr(d_inode(dentry), stat);
501
502
503
504
505
506
507
	/* Change block size to what the server returned */
	stat->blksize = st->st_blksize;

	kfree(st);
	return 0;
}

508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
/*
 * Attribute flags.
 */
#define P9_ATTR_MODE		(1 << 0)
#define P9_ATTR_UID		(1 << 1)
#define P9_ATTR_GID		(1 << 2)
#define P9_ATTR_SIZE		(1 << 3)
#define P9_ATTR_ATIME		(1 << 4)
#define P9_ATTR_MTIME		(1 << 5)
#define P9_ATTR_CTIME		(1 << 6)
#define P9_ATTR_ATIME_SET	(1 << 7)
#define P9_ATTR_MTIME_SET	(1 << 8)

struct dotl_iattr_map {
	int iattr_valid;
	int p9_iattr_valid;
};

static int v9fs_mapped_iattr_valid(int iattr_valid)
{
	int i;
	int p9_iattr_valid = 0;
	struct dotl_iattr_map dotl_iattr_map[] = {
		{ ATTR_MODE,		P9_ATTR_MODE },
		{ ATTR_UID,		P9_ATTR_UID },
		{ ATTR_GID,		P9_ATTR_GID },
		{ ATTR_SIZE,		P9_ATTR_SIZE },
		{ ATTR_ATIME,		P9_ATTR_ATIME },
		{ ATTR_MTIME,		P9_ATTR_MTIME },
		{ ATTR_CTIME,		P9_ATTR_CTIME },
		{ ATTR_ATIME_SET,	P9_ATTR_ATIME_SET },
		{ ATTR_MTIME_SET,	P9_ATTR_MTIME_SET },
	};
	for (i = 0; i < ARRAY_SIZE(dotl_iattr_map); i++) {
		if (iattr_valid & dotl_iattr_map[i].iattr_valid)
			p9_iattr_valid |= dotl_iattr_map[i].p9_iattr_valid;
	}
	return p9_iattr_valid;
}

548
549
550
551
552
553
554
555
556
557
558
559
/**
 * v9fs_vfs_setattr_dotl - set file metadata
 * @dentry: file whose metadata to set
 * @iattr: metadata assignment structure
 *
 */

int v9fs_vfs_setattr_dotl(struct dentry *dentry, struct iattr *iattr)
{
	int retval;
	struct p9_fid *fid;
	struct p9_iattr_dotl p9attr;
560
	struct inode *inode = d_inode(dentry);
561

562
	p9_debug(P9_DEBUG_VFS, "\n");
563

564
	retval = inode_change_ok(inode, iattr);
565
566
567
	if (retval)
		return retval;

568
	p9attr.valid = v9fs_mapped_iattr_valid(iattr->ia_valid);
569
570
571
572
573
574
575
576
577
578
579
580
581
	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;

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

582
	/* Write all dirty data */
583
584
	if (S_ISREG(inode->i_mode))
		filemap_write_and_wait(inode->i_mapping);
585

586
587
588
	retval = p9_client_setattr(fid, &p9attr);
	if (retval < 0)
		return retval;
589

590
	if ((iattr->ia_valid & ATTR_SIZE) &&
591
592
	    iattr->ia_size != i_size_read(inode))
		truncate_setsize(inode, iattr->ia_size);
593

594
595
596
	v9fs_invalidate_inode_attr(inode);
	setattr_copy(inode, iattr);
	mark_inode_dirty(inode);
597
598
	if (iattr->ia_valid & ATTR_MODE) {
		/* We also want to update ACL when we update mode bits */
599
		retval = v9fs_acl_chmod(inode, fid);
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
		if (retval < 0)
			return retval;
	}
	return 0;
}

/**
 * v9fs_stat2inode_dotl - populate an inode structure with stat info
 * @stat: stat structure
 * @inode: inode to populate
 *
 */

void
v9fs_stat2inode_dotl(struct p9_stat_dotl *stat, struct inode *inode)
{
Al Viro's avatar
Al Viro committed
616
	umode_t mode;
617
	struct v9fs_inode *v9inode = V9FS_I(inode);
618
619
620
621
622
623
624
625
626
627

	if ((stat->st_result_mask & P9_STATS_BASIC) == P9_STATS_BASIC) {
		inode->i_atime.tv_sec = stat->st_atime_sec;
		inode->i_atime.tv_nsec = stat->st_atime_nsec;
		inode->i_mtime.tv_sec = stat->st_mtime_sec;
		inode->i_mtime.tv_nsec = stat->st_mtime_nsec;
		inode->i_ctime.tv_sec = stat->st_ctime_sec;
		inode->i_ctime.tv_nsec = stat->st_ctime_nsec;
		inode->i_uid = stat->st_uid;
		inode->i_gid = stat->st_gid;
Miklos Szeredi's avatar
Miklos Szeredi committed
628
		set_nlink(inode, stat->st_nlink);
629

630
631
632
		mode = stat->st_mode & S_IALLUGO;
		mode |= inode->i_mode & ~S_IALLUGO;
		inode->i_mode = mode;
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653

		i_size_write(inode, stat->st_size);
		inode->i_blocks = stat->st_blocks;
	} else {
		if (stat->st_result_mask & P9_STATS_ATIME) {
			inode->i_atime.tv_sec = stat->st_atime_sec;
			inode->i_atime.tv_nsec = stat->st_atime_nsec;
		}
		if (stat->st_result_mask & P9_STATS_MTIME) {
			inode->i_mtime.tv_sec = stat->st_mtime_sec;
			inode->i_mtime.tv_nsec = stat->st_mtime_nsec;
		}
		if (stat->st_result_mask & P9_STATS_CTIME) {
			inode->i_ctime.tv_sec = stat->st_ctime_sec;
			inode->i_ctime.tv_nsec = stat->st_ctime_nsec;
		}
		if (stat->st_result_mask & P9_STATS_UID)
			inode->i_uid = stat->st_uid;
		if (stat->st_result_mask & P9_STATS_GID)
			inode->i_gid = stat->st_gid;
		if (stat->st_result_mask & P9_STATS_NLINK)
Miklos Szeredi's avatar
Miklos Szeredi committed
654
			set_nlink(inode, stat->st_nlink);
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
		if (stat->st_result_mask & P9_STATS_MODE) {
			inode->i_mode = stat->st_mode;
			if ((S_ISBLK(inode->i_mode)) ||
						(S_ISCHR(inode->i_mode)))
				init_special_inode(inode, inode->i_mode,
								inode->i_rdev);
		}
		if (stat->st_result_mask & P9_STATS_RDEV)
			inode->i_rdev = new_decode_dev(stat->st_rdev);
		if (stat->st_result_mask & P9_STATS_SIZE)
			i_size_write(inode, stat->st_size);
		if (stat->st_result_mask & P9_STATS_BLOCKS)
			inode->i_blocks = stat->st_blocks;
	}
	if (stat->st_result_mask & P9_STATS_GEN)
670
		inode->i_generation = stat->st_gen;
671
672
673
674

	/* Currently we don't support P9_STATS_BTIME and P9_STATS_DATA_VERSION
	 * because the inode structure does not have fields for them.
	 */
675
	v9inode->cache_validity &= ~V9FS_INO_INVALID_ATTR;
676
677
678
679
680
681
682
}

static int
v9fs_vfs_symlink_dotl(struct inode *dir, struct dentry *dentry,
		const char *symname)
{
	int err;
683
	kgid_t gid;
684
685
686
687
688
689
	char *name;
	struct p9_qid qid;
	struct inode *inode;
	struct p9_fid *dfid;
	struct p9_fid *fid = NULL;
	struct v9fs_session_info *v9ses;
690
691

	name = (char *) dentry->d_name.name;
692
	p9_debug(P9_DEBUG_VFS, "%lu,%s,%s\n", dir->i_ino, name, symname);
693
694
695
696
697
	v9ses = v9fs_inode2v9ses(dir);

	dfid = v9fs_fid_lookup(dentry->d_parent);
	if (IS_ERR(dfid)) {
		err = PTR_ERR(dfid);
698
		p9_debug(P9_DEBUG_VFS, "fid lookup failed %d\n", err);
699
700
701
702
703
704
705
706
707
		return err;
	}

	gid = v9fs_get_fsgid_for_create(dir);

	/* Server doesn't alter fid on TSYMLINK. Hence no need to clone it. */
	err = p9_client_symlink(dfid, name, (char *)symname, gid, &qid);

	if (err < 0) {
708
		p9_debug(P9_DEBUG_VFS, "p9_client_symlink failed %d\n", err);
709
710
711
		goto error;
	}

712
	v9fs_invalidate_inode_attr(dir);
713
	if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE) {
714
715
716
717
		/* 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);
718
719
			p9_debug(P9_DEBUG_VFS, "p9_client_walk failed %d\n",
				 err);
720
721
722
723
724
			fid = NULL;
			goto error;
		}

		/* instantiate inode and assign the unopened fid to dentry */
725
		inode = v9fs_get_new_inode_from_fid(v9ses, fid, dir->i_sb);
726
727
		if (IS_ERR(inode)) {
			err = PTR_ERR(inode);
728
729
			p9_debug(P9_DEBUG_VFS, "inode creation failed %d\n",
				 err);
730
731
			goto error;
		}
Al Viro's avatar
Al Viro committed
732
		v9fs_fid_add(dentry, fid);
733
		d_instantiate(dentry, inode);
734
		fid = NULL;
Al Viro's avatar
Al Viro committed
735
		err = 0;
736
737
	} else {
		/* Not in cached mode. No need to populate inode with stat */
738
		inode = v9fs_get_inode(dir->i_sb, S_IFLNK, 0);
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
		if (IS_ERR(inode)) {
			err = PTR_ERR(inode);
			goto error;
		}
		d_instantiate(dentry, inode);
	}

error:
	if (fid)
		p9_client_clunk(fid);

	return err;
}

/**
 * v9fs_vfs_link_dotl - create a hardlink for dotl
 * @old_dentry: dentry for file to link to
 * @dir: inode destination for new link
 * @dentry: dentry for link
 *
 */

static int
v9fs_vfs_link_dotl(struct dentry *old_dentry, struct inode *dir,
		struct dentry *dentry)
{
	int err;
	struct dentry *dir_dentry;
767
768
	struct p9_fid *dfid, *oldfid;
	struct v9fs_session_info *v9ses;
769

Al Viro's avatar
Al Viro committed
770
771
	p9_debug(P9_DEBUG_VFS, "dir ino: %lu, old_name: %pd, new_name: %pd\n",
		 dir->i_ino, old_dentry, dentry);
772
773

	v9ses = v9fs_inode2v9ses(dir);
Al Viro's avatar
Al Viro committed
774
	dir_dentry = dentry->d_parent;
775
776
777
778
779
780
781
782
783
784
785
	dfid = v9fs_fid_lookup(dir_dentry);
	if (IS_ERR(dfid))
		return PTR_ERR(dfid);

	oldfid = v9fs_fid_lookup(old_dentry);
	if (IS_ERR(oldfid))
		return PTR_ERR(oldfid);

	err = p9_client_link(dfid, oldfid, (char *)dentry->d_name.name);

	if (err < 0) {
786
		p9_debug(P9_DEBUG_VFS, "p9_client_link failed %d\n", err);
787
788
789
		return err;
	}

790
	v9fs_invalidate_inode_attr(dir);
791
792
793
794
795
796
797
	if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE) {
		/* Get the latest stat info from server. */
		struct p9_fid *fid;
		fid = v9fs_fid_lookup(old_dentry);
		if (IS_ERR(fid))
			return PTR_ERR(fid);

798
		v9fs_refresh_inode_dotl(fid, d_inode(old_dentry));
799
	}
800
801
	ihold(d_inode(old_dentry));
	d_instantiate(dentry, d_inode(old_dentry));
802
803
804
805
806
807
808
809

	return err;
}

/**
 * v9fs_vfs_mknod_dotl - create a special file
 * @dir: inode destination for new link
 * @dentry: dentry for file
Fabian Frederick's avatar
Fabian Frederick committed
810
 * @omode: mode for creation
811
812
813
814
 * @rdev: device associated with special file
 *
 */
static int
Al Viro's avatar
Al Viro committed
815
v9fs_vfs_mknod_dotl(struct inode *dir, struct dentry *dentry, umode_t omode,
816
817
818
		dev_t rdev)
{
	int err;
819
	kgid_t gid;
820
	char *name;
821
	umode_t mode;
822
823
824
825
826
827
828
	struct v9fs_session_info *v9ses;
	struct p9_fid *fid = NULL, *dfid = NULL;
	struct inode *inode;
	struct p9_qid qid;
	struct dentry *dir_dentry;
	struct posix_acl *dacl = NULL, *pacl = NULL;

Al Viro's avatar
Al Viro committed
829
830
	p9_debug(P9_DEBUG_VFS, " %lu,%pd mode: %hx MAJOR: %u MINOR: %u\n",
		 dir->i_ino, dentry, omode,
831
		 MAJOR(rdev), MINOR(rdev));
832
833
834
835
836

	if (!new_valid_dev(rdev))
		return -EINVAL;

	v9ses = v9fs_inode2v9ses(dir);
Al Viro's avatar
Al Viro committed
837
	dir_dentry = dentry->d_parent;
838
839
840
	dfid = v9fs_fid_lookup(dir_dentry);
	if (IS_ERR(dfid)) {
		err = PTR_ERR(dfid);
841
		p9_debug(P9_DEBUG_VFS, "fid lookup failed %d\n", err);
842
843
844
845
846
847
848
849
850
		dfid = NULL;
		goto error;
	}

	gid = v9fs_get_fsgid_for_create(dir);
	mode = omode;
	/* Update mode based on ACL value */
	err = v9fs_acl_mode(dir, &mode, &dacl, &pacl);
	if (err) {
851
852
		p9_debug(P9_DEBUG_VFS, "Failed to get acl values in mknod %d\n",
			 err);
853
854
855
856
857
858
859
860
		goto error;
	}
	name = (char *) dentry->d_name.name;

	err = p9_client_mknod_dotl(dfid, name, mode, rdev, gid, &qid);
	if (err < 0)
		goto error;

861
	v9fs_invalidate_inode_attr(dir);
862
863
864
865
866
867
868
869
870
	fid = p9_client_walk(dfid, 1, &name, 1);
	if (IS_ERR(fid)) {
		err = PTR_ERR(fid);
		p9_debug(P9_DEBUG_VFS, "p9_client_walk failed %d\n",
			 err);
		fid = NULL;
		goto error;
	}

871
872
	/* instantiate inode and assign the unopened fid to the dentry */
	if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE) {
873
		inode = v9fs_get_new_inode_from_fid(v9ses, fid, dir->i_sb);
874
875
		if (IS_ERR(inode)) {
			err = PTR_ERR(inode);
876
877
			p9_debug(P9_DEBUG_VFS, "inode creation failed %d\n",
				 err);
878
879
			goto error;
		}
880
		v9fs_set_create_acl(inode, fid, dacl, pacl);
Al Viro's avatar
Al Viro committed
881
		v9fs_fid_add(dentry, fid);
882
		d_instantiate(dentry, inode);
883
		fid = NULL;
Al Viro's avatar
Al Viro committed
884
		err = 0;
885
886
887
888
889
	} else {
		/*
		 * Not in cached mode. No need to populate inode with stat.
		 * socket syscall returns a fd, so we need instantiate
		 */
890
		inode = v9fs_get_inode(dir->i_sb, mode, rdev);
891
892
893
894
		if (IS_ERR(inode)) {
			err = PTR_ERR(inode);
			goto error;
		}
895
		v9fs_set_create_acl(inode, fid, dacl, pacl);
896
897
898
899
900
		d_instantiate(dentry, inode);
	}
error:
	if (fid)
		p9_client_clunk(fid);
901
	v9fs_put_acl(dacl, pacl);
902
903
904
905
906
907
	return err;
}

/**
 * v9fs_vfs_follow_link_dotl - follow a symlink path
 * @dentry: dentry for symlink
908
 * @cookie: place to pass the data to put_link()
909
910
 */

911
static const char *
912
v9fs_vfs_follow_link_dotl(struct dentry *dentry, void **cookie)
913
{
914
	struct p9_fid *fid = v9fs_fid_lookup(dentry);
M. Mohan Kumar's avatar
M. Mohan Kumar committed
915
	char *target;
916
	int retval;
917

Al Viro's avatar
Al Viro committed
918
	p9_debug(P9_DEBUG_VFS, "%pd\n", dentry);
919

920
921
	if (IS_ERR(fid))
		return ERR_CAST(fid);
M. Mohan Kumar's avatar
M. Mohan Kumar committed
922
	retval = p9_client_readlink(fid, &target);
923
924
	if (retval)
		return ERR_PTR(retval);
925
	return *cookie = target;
926
927
}

928
929
930
931
932
933
934
935
936
937
int v9fs_refresh_inode_dotl(struct p9_fid *fid, struct inode *inode)
{
	loff_t i_size;
	struct p9_stat_dotl *st;
	struct v9fs_session_info *v9ses;

	v9ses = v9fs_inode2v9ses(inode);
	st = p9_client_getattr_dotl(fid, P9_STATS_ALL);
	if (IS_ERR(st))
		return PTR_ERR(st);
938
939
940
941
942
	/*
	 * Don't update inode if the file type is different
	 */
	if ((inode->i_mode & S_IFMT) != (st->st_mode & S_IFMT))
		goto out;
943
944
945
946
947
948
949
950

	spin_lock(&inode->i_lock);
	/*
	 * We don't want to refresh inode->i_size,
	 * because we may have cached data
	 */
	i_size = inode->i_size;
	v9fs_stat2inode_dotl(st, inode);
951
	if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE)
952
953
		inode->i_size = i_size;
	spin_unlock(&inode->i_lock);
954
out:
955
956
957
958
	kfree(st);
	return 0;
}

959
960
const struct inode_operations v9fs_dir_inode_operations_dotl = {
	.create = v9fs_vfs_create_dotl,
961
	.atomic_open = v9fs_vfs_atomic_open_dotl,
962
963
964
965
966
967
968
969
970
971
972
973
974
975
	.lookup = v9fs_vfs_lookup,
	.link = v9fs_vfs_link_dotl,
	.symlink = v9fs_vfs_symlink_dotl,
	.unlink = v9fs_vfs_unlink,
	.mkdir = v9fs_vfs_mkdir_dotl,
	.rmdir = v9fs_vfs_rmdir,
	.mknod = v9fs_vfs_mknod_dotl,
	.rename = v9fs_vfs_rename,
	.getattr = v9fs_vfs_getattr_dotl,
	.setattr = v9fs_vfs_setattr_dotl,
	.setxattr = generic_setxattr,
	.getxattr = generic_getxattr,
	.removexattr = generic_removexattr,
	.listxattr = v9fs_listxattr,
976
	.get_acl = v9fs_iop_get_acl,
977
978
979
980
981
982
983
984
985
};

const struct inode_operations v9fs_file_inode_operations_dotl = {
	.getattr = v9fs_vfs_getattr_dotl,
	.setattr = v9fs_vfs_setattr_dotl,
	.setxattr = generic_setxattr,
	.getxattr = generic_getxattr,
	.removexattr = generic_removexattr,
	.listxattr = v9fs_listxattr,
986
	.get_acl = v9fs_iop_get_acl,
987
988
989
};

const struct inode_operations v9fs_symlink_inode_operations_dotl = {
M. Mohan Kumar's avatar
M. Mohan Kumar committed
990
	.readlink = generic_readlink,
991
	.follow_link = v9fs_vfs_follow_link_dotl,
992
	.put_link = kfree_put_link,
993
994
995
996
997
998
999
	.getattr = v9fs_vfs_getattr_dotl,
	.setattr = v9fs_vfs_setattr_dotl,
	.setxattr = generic_setxattr,
	.getxattr = generic_getxattr,
	.removexattr = generic_removexattr,
	.listxattr = v9fs_listxattr,
};