xref: /openbmc/linux/fs/9p/vfs_inode.c (revision 87c2ce3b)
1 /*
2  *  linux/fs/9p/vfs_inode.c
3  *
4  * This file contains vfs inode ops for the 9P2000 protocol.
5  *
6  *  Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
7  *  Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
8  *
9  *  This program is free software; you can redistribute it and/or modify
10  *  it under the terms of the GNU General Public License as published by
11  *  the Free Software Foundation; either version 2 of the License, or
12  *  (at your option) any later version.
13  *
14  *  This program is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *  GNU General Public License for more details.
18  *
19  *  You should have received a copy of the GNU General Public License
20  *  along with this program; if not, write to:
21  *  Free Software Foundation
22  *  51 Franklin Street, Fifth Floor
23  *  Boston, MA  02111-1301  USA
24  *
25  */
26 
27 #include <linux/module.h>
28 #include <linux/errno.h>
29 #include <linux/fs.h>
30 #include <linux/file.h>
31 #include <linux/pagemap.h>
32 #include <linux/stat.h>
33 #include <linux/string.h>
34 #include <linux/smp_lock.h>
35 #include <linux/inet.h>
36 #include <linux/namei.h>
37 #include <linux/idr.h>
38 
39 #include "debug.h"
40 #include "v9fs.h"
41 #include "9p.h"
42 #include "v9fs_vfs.h"
43 #include "fid.h"
44 
45 static struct inode_operations v9fs_dir_inode_operations;
46 static struct inode_operations v9fs_dir_inode_operations_ext;
47 static struct inode_operations v9fs_file_inode_operations;
48 static struct inode_operations v9fs_symlink_inode_operations;
49 
50 /**
51  * unixmode2p9mode - convert unix mode bits to plan 9
52  * @v9ses: v9fs session information
53  * @mode: mode to convert
54  *
55  */
56 
57 static int unixmode2p9mode(struct v9fs_session_info *v9ses, int mode)
58 {
59 	int res;
60 	res = mode & 0777;
61 	if (S_ISDIR(mode))
62 		res |= V9FS_DMDIR;
63 	if (v9ses->extended) {
64 		if (S_ISLNK(mode))
65 			res |= V9FS_DMSYMLINK;
66 		if (v9ses->nodev == 0) {
67 			if (S_ISSOCK(mode))
68 				res |= V9FS_DMSOCKET;
69 			if (S_ISFIFO(mode))
70 				res |= V9FS_DMNAMEDPIPE;
71 			if (S_ISBLK(mode))
72 				res |= V9FS_DMDEVICE;
73 			if (S_ISCHR(mode))
74 				res |= V9FS_DMDEVICE;
75 		}
76 
77 		if ((mode & S_ISUID) == S_ISUID)
78 			res |= V9FS_DMSETUID;
79 		if ((mode & S_ISGID) == S_ISGID)
80 			res |= V9FS_DMSETGID;
81 		if ((mode & V9FS_DMLINK))
82 			res |= V9FS_DMLINK;
83 	}
84 
85 	return res;
86 }
87 
88 /**
89  * p9mode2unixmode- convert plan9 mode bits to unix mode bits
90  * @v9ses: v9fs session information
91  * @mode: mode to convert
92  *
93  */
94 
95 static int p9mode2unixmode(struct v9fs_session_info *v9ses, int mode)
96 {
97 	int res;
98 
99 	res = mode & 0777;
100 
101 	if ((mode & V9FS_DMDIR) == V9FS_DMDIR)
102 		res |= S_IFDIR;
103 	else if ((mode & V9FS_DMSYMLINK) && (v9ses->extended))
104 		res |= S_IFLNK;
105 	else if ((mode & V9FS_DMSOCKET) && (v9ses->extended)
106 		 && (v9ses->nodev == 0))
107 		res |= S_IFSOCK;
108 	else if ((mode & V9FS_DMNAMEDPIPE) && (v9ses->extended)
109 		 && (v9ses->nodev == 0))
110 		res |= S_IFIFO;
111 	else if ((mode & V9FS_DMDEVICE) && (v9ses->extended)
112 		 && (v9ses->nodev == 0))
113 		res |= S_IFBLK;
114 	else
115 		res |= S_IFREG;
116 
117 	if (v9ses->extended) {
118 		if ((mode & V9FS_DMSETUID) == V9FS_DMSETUID)
119 			res |= S_ISUID;
120 
121 		if ((mode & V9FS_DMSETGID) == V9FS_DMSETGID)
122 			res |= S_ISGID;
123 	}
124 
125 	return res;
126 }
127 
128 /**
129  * v9fs_blank_wstat - helper function to setup a 9P stat structure
130  * @v9ses: 9P session info (for determining extended mode)
131  * @wstat: structure to initialize
132  *
133  */
134 
135 static void
136 v9fs_blank_wstat(struct v9fs_wstat *wstat)
137 {
138 	wstat->type = ~0;
139 	wstat->dev = ~0;
140 	wstat->qid.type = ~0;
141 	wstat->qid.version = ~0;
142 	*((long long *)&wstat->qid.path) = ~0;
143 	wstat->mode = ~0;
144 	wstat->atime = ~0;
145 	wstat->mtime = ~0;
146 	wstat->length = ~0;
147 	wstat->name = NULL;
148 	wstat->uid = NULL;
149 	wstat->gid = NULL;
150 	wstat->muid = NULL;
151 	wstat->n_uid = ~0;
152 	wstat->n_gid = ~0;
153 	wstat->n_muid = ~0;
154 	wstat->extension = NULL;
155 }
156 
157 /**
158  * v9fs_get_inode - helper function to setup an inode
159  * @sb: superblock
160  * @mode: mode to setup inode with
161  *
162  */
163 
164 struct inode *v9fs_get_inode(struct super_block *sb, int mode)
165 {
166 	struct inode *inode = NULL;
167 	struct v9fs_session_info *v9ses = sb->s_fs_info;
168 
169 	dprintk(DEBUG_VFS, "super block: %p mode: %o\n", sb, mode);
170 
171 	inode = new_inode(sb);
172 	if (inode) {
173 		inode->i_mode = mode;
174 		inode->i_uid = current->fsuid;
175 		inode->i_gid = current->fsgid;
176 		inode->i_blksize = sb->s_blocksize;
177 		inode->i_blocks = 0;
178 		inode->i_rdev = 0;
179 		inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
180 
181 		switch (mode & S_IFMT) {
182 		case S_IFIFO:
183 		case S_IFBLK:
184 		case S_IFCHR:
185 		case S_IFSOCK:
186 			if(!v9ses->extended) {
187 				dprintk(DEBUG_ERROR, "special files without extended mode\n");
188 				return ERR_PTR(-EINVAL);
189 			}
190 			init_special_inode(inode, inode->i_mode,
191 					   inode->i_rdev);
192 			break;
193 		case S_IFREG:
194 			inode->i_op = &v9fs_file_inode_operations;
195 			inode->i_fop = &v9fs_file_operations;
196 			break;
197 		case S_IFLNK:
198 			if(!v9ses->extended) {
199 				dprintk(DEBUG_ERROR, "extended modes used w/o 9P2000.u\n");
200 				return ERR_PTR(-EINVAL);
201 			}
202 			inode->i_op = &v9fs_symlink_inode_operations;
203 			break;
204 		case S_IFDIR:
205 			inode->i_nlink++;
206 			if(v9ses->extended)
207 				inode->i_op = &v9fs_dir_inode_operations_ext;
208 			else
209 				inode->i_op = &v9fs_dir_inode_operations;
210 			inode->i_fop = &v9fs_dir_operations;
211 			break;
212 		default:
213 			dprintk(DEBUG_ERROR, "BAD mode 0x%x S_IFMT 0x%x\n",
214 				mode, mode & S_IFMT);
215 			return ERR_PTR(-EINVAL);
216 		}
217 	} else {
218 		eprintk(KERN_WARNING, "Problem allocating inode\n");
219 		return ERR_PTR(-ENOMEM);
220 	}
221 	return inode;
222 }
223 
224 /**
225  * v9fs_create - helper function to create files and directories
226  * @dir: directory inode file is being created in
227  * @file_dentry: dentry file is being created in
228  * @perm: permissions file is being created with
229  * @open_mode: resulting open mode for file
230  *
231  */
232 
233 static int
234 v9fs_create(struct inode *dir,
235 	    struct dentry *file_dentry,
236 	    unsigned int perm, unsigned int open_mode)
237 {
238 	struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dir);
239 	struct super_block *sb = dir->i_sb;
240 	struct v9fs_fid *dirfid =
241 	    v9fs_fid_lookup(file_dentry->d_parent);
242 	struct v9fs_fid *fid = NULL;
243 	struct inode *file_inode = NULL;
244 	struct v9fs_fcall *fcall = NULL;
245 	struct v9fs_qid qid;
246 	int dirfidnum = -1;
247 	long newfid = -1;
248 	int result = 0;
249 	unsigned int iounit = 0;
250 	int wfidno = -1;
251 	int err;
252 
253 	perm = unixmode2p9mode(v9ses, perm);
254 
255 	dprintk(DEBUG_VFS, "dir: %p dentry: %p perm: %o mode: %o\n", dir,
256 		file_dentry, perm, open_mode);
257 
258 	if (!dirfid)
259 		return -EBADF;
260 
261 	dirfidnum = dirfid->fid;
262 	if (dirfidnum < 0) {
263 		dprintk(DEBUG_ERROR, "No fid for the directory #%lu\n",
264 			dir->i_ino);
265 		return -EBADF;
266 	}
267 
268 	if (file_dentry->d_inode) {
269 		dprintk(DEBUG_ERROR,
270 			"Odd. There is an inode for dir %lu, name :%s:\n",
271 			dir->i_ino, file_dentry->d_name.name);
272 		return -EEXIST;
273 	}
274 
275 	newfid = v9fs_get_idpool(&v9ses->fidpool);
276 	if (newfid < 0) {
277 		eprintk(KERN_WARNING, "no free fids available\n");
278 		return -ENOSPC;
279 	}
280 
281 	result = v9fs_t_walk(v9ses, dirfidnum, newfid, NULL, &fcall);
282 	if (result < 0) {
283 		PRINT_FCALL_ERROR("clone error", fcall);
284 		v9fs_put_idpool(newfid, &v9ses->fidpool);
285 		newfid = -1;
286 		goto CleanUpFid;
287 	}
288 
289 	kfree(fcall);
290 	fcall = NULL;
291 
292 	result = v9fs_t_create(v9ses, newfid, (char *)file_dentry->d_name.name,
293 			       perm, open_mode, &fcall);
294 	if (result < 0) {
295 		PRINT_FCALL_ERROR("create fails", fcall);
296 		goto CleanUpFid;
297 	}
298 
299 	iounit = fcall->params.rcreate.iounit;
300 	qid = fcall->params.rcreate.qid;
301 	kfree(fcall);
302 	fcall = NULL;
303 
304 	if (!(perm&V9FS_DMDIR)) {
305 		fid = v9fs_fid_create(file_dentry, v9ses, newfid, 1);
306 		dprintk(DEBUG_VFS, "fid %p %d\n", fid, fid->fidcreate);
307 		if (!fid) {
308 			result = -ENOMEM;
309 			goto CleanUpFid;
310 		}
311 
312 		fid->qid = qid;
313 		fid->iounit = iounit;
314 	} else {
315 		err = v9fs_t_clunk(v9ses, newfid);
316 		newfid = -1;
317 		if (err < 0)
318 			dprintk(DEBUG_ERROR, "clunk for mkdir failed: %d\n", err);
319 	}
320 
321 	/* walk to the newly created file and put the fid in the dentry */
322 	wfidno = v9fs_get_idpool(&v9ses->fidpool);
323 	if (wfidno < 0) {
324 		eprintk(KERN_WARNING, "no free fids available\n");
325 		return -ENOSPC;
326 	}
327 
328 	result = v9fs_t_walk(v9ses, dirfidnum, wfidno,
329 		(char *) file_dentry->d_name.name, &fcall);
330 	if (result < 0) {
331 		PRINT_FCALL_ERROR("clone error", fcall);
332 		v9fs_put_idpool(wfidno, &v9ses->fidpool);
333 		wfidno = -1;
334 		goto CleanUpFid;
335 	}
336 	kfree(fcall);
337 	fcall = NULL;
338 
339 	if (!v9fs_fid_create(file_dentry, v9ses, wfidno, 0)) {
340 		v9fs_put_idpool(wfidno, &v9ses->fidpool);
341 
342 		goto CleanUpFid;
343 	}
344 
345 	if ((perm & V9FS_DMSYMLINK) || (perm & V9FS_DMLINK) ||
346 	    (perm & V9FS_DMNAMEDPIPE) || (perm & V9FS_DMSOCKET) ||
347 	    (perm & V9FS_DMDEVICE))
348 		return 0;
349 
350 	result = v9fs_t_stat(v9ses, wfidno, &fcall);
351 	if (result < 0) {
352 		PRINT_FCALL_ERROR("stat error", fcall);
353 		goto CleanUpFid;
354 	}
355 
356 
357 	file_inode = v9fs_get_inode(sb,
358 		p9mode2unixmode(v9ses, fcall->params.rstat.stat.mode));
359 
360 	if ((!file_inode) || IS_ERR(file_inode)) {
361 		dprintk(DEBUG_ERROR, "create inode failed\n");
362 		result = -EBADF;
363 		goto CleanUpFid;
364 	}
365 
366 	v9fs_stat2inode(&fcall->params.rstat.stat, file_inode, sb);
367 	kfree(fcall);
368 	fcall = NULL;
369 	file_dentry->d_op = &v9fs_dentry_operations;
370 	d_instantiate(file_dentry, file_inode);
371 
372 	return 0;
373 
374       CleanUpFid:
375 	kfree(fcall);
376 	fcall = NULL;
377 
378 	if (newfid >= 0) {
379  		err = v9fs_t_clunk(v9ses, newfid);
380  		if (err < 0)
381  			dprintk(DEBUG_ERROR, "clunk failed: %d\n", err);
382 	}
383 	if (wfidno >= 0) {
384  		err = v9fs_t_clunk(v9ses, wfidno);
385  		if (err < 0)
386  			dprintk(DEBUG_ERROR, "clunk failed: %d\n", err);
387 	}
388 	return result;
389 }
390 
391 /**
392  * v9fs_remove - helper function to remove files and directories
393  * @dir: directory inode that is being deleted
394  * @file:  dentry that is being deleted
395  * @rmdir: removing a directory
396  *
397  */
398 
399 static int v9fs_remove(struct inode *dir, struct dentry *file, int rmdir)
400 {
401 	struct v9fs_fcall *fcall = NULL;
402 	struct super_block *sb = NULL;
403 	struct v9fs_session_info *v9ses = NULL;
404 	struct v9fs_fid *v9fid = NULL;
405 	struct inode *file_inode = NULL;
406 	int fid = -1;
407 	int result = 0;
408 
409 	dprintk(DEBUG_VFS, "inode: %p dentry: %p rmdir: %d\n", dir, file,
410 		rmdir);
411 
412 	file_inode = file->d_inode;
413 	sb = file_inode->i_sb;
414 	v9ses = v9fs_inode2v9ses(file_inode);
415 	v9fid = v9fs_fid_lookup(file);
416 
417 	if (!v9fid) {
418 		dprintk(DEBUG_ERROR,
419 			"no v9fs_fid\n");
420 		return -EBADF;
421 	}
422 
423 	fid = v9fid->fid;
424 	if (fid < 0) {
425 		dprintk(DEBUG_ERROR, "inode #%lu, no fid!\n",
426 			file_inode->i_ino);
427 		return -EBADF;
428 	}
429 
430 	result = v9fs_t_remove(v9ses, fid, &fcall);
431 	if (result < 0) {
432 		PRINT_FCALL_ERROR("remove fails", fcall);
433 	} else {
434 		v9fs_put_idpool(fid, &v9ses->fidpool);
435 		v9fs_fid_destroy(v9fid);
436 	}
437 
438 	kfree(fcall);
439 	return result;
440 }
441 
442 /**
443  * v9fs_vfs_create - VFS hook to create files
444  * @inode: directory inode that is being deleted
445  * @dentry:  dentry that is being deleted
446  * @perm: create permissions
447  * @nd: path information
448  *
449  */
450 
451 static int
452 v9fs_vfs_create(struct inode *inode, struct dentry *dentry, int perm,
453 		struct nameidata *nd)
454 {
455 	return v9fs_create(inode, dentry, perm, O_RDWR);
456 }
457 
458 /**
459  * v9fs_vfs_mkdir - VFS mkdir hook to create a directory
460  * @inode:  inode that is being unlinked
461  * @dentry: dentry that is being unlinked
462  * @mode: mode for new directory
463  *
464  */
465 
466 static int v9fs_vfs_mkdir(struct inode *inode, struct dentry *dentry, int mode)
467 {
468 	return v9fs_create(inode, dentry, mode | S_IFDIR, O_RDONLY);
469 }
470 
471 /**
472  * v9fs_vfs_lookup - VFS lookup hook to "walk" to a new inode
473  * @dir:  inode that is being walked from
474  * @dentry: dentry that is being walked to?
475  * @nameidata: path data
476  *
477  */
478 
479 static struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry,
480 				      struct nameidata *nameidata)
481 {
482 	struct super_block *sb;
483 	struct v9fs_session_info *v9ses;
484 	struct v9fs_fid *dirfid;
485 	struct v9fs_fid *fid;
486 	struct inode *inode;
487 	struct v9fs_fcall *fcall = NULL;
488 	int dirfidnum = -1;
489 	int newfid = -1;
490 	int result = 0;
491 
492 	dprintk(DEBUG_VFS, "dir: %p dentry: (%s) %p nameidata: %p\n",
493 		dir, dentry->d_iname, dentry, nameidata);
494 
495 	sb = dir->i_sb;
496 	v9ses = v9fs_inode2v9ses(dir);
497 	dirfid = v9fs_fid_lookup(dentry->d_parent);
498 
499 	if (!dirfid) {
500 		dprintk(DEBUG_ERROR, "no dirfid\n");
501 		return ERR_PTR(-EINVAL);
502 	}
503 
504 	dirfidnum = dirfid->fid;
505 
506 	if (dirfidnum < 0) {
507 		dprintk(DEBUG_ERROR, "no dirfid for inode %p, #%lu\n",
508 			dir, dir->i_ino);
509 		return ERR_PTR(-EBADF);
510 	}
511 
512 	newfid = v9fs_get_idpool(&v9ses->fidpool);
513 	if (newfid < 0) {
514 		eprintk(KERN_WARNING, "newfid fails!\n");
515 		return ERR_PTR(-ENOSPC);
516 	}
517 
518 	result =
519 	    v9fs_t_walk(v9ses, dirfidnum, newfid, (char *)dentry->d_name.name,
520 			NULL);
521 	if (result < 0) {
522 		v9fs_put_idpool(newfid, &v9ses->fidpool);
523 		if (result == -ENOENT) {
524 			d_add(dentry, NULL);
525 			dprintk(DEBUG_VFS,
526 				"Return negative dentry %p count %d\n",
527 				dentry, atomic_read(&dentry->d_count));
528 			return NULL;
529 		}
530 		dprintk(DEBUG_ERROR, "walk error:%d\n", result);
531 		goto FreeFcall;
532 	}
533 
534 	result = v9fs_t_stat(v9ses, newfid, &fcall);
535 	if (result < 0) {
536 		dprintk(DEBUG_ERROR, "stat error\n");
537 		goto FreeFcall;
538 	}
539 
540 	inode = v9fs_get_inode(sb, p9mode2unixmode(v9ses,
541 		fcall->params.rstat.stat.mode));
542 
543 	if (IS_ERR(inode) && (PTR_ERR(inode) == -ENOSPC)) {
544 		eprintk(KERN_WARNING, "inode alloc failes, returns %ld\n",
545 			PTR_ERR(inode));
546 
547 		result = -ENOSPC;
548 		goto FreeFcall;
549 	}
550 
551 	inode->i_ino = v9fs_qid2ino(&fcall->params.rstat.stat.qid);
552 
553 	fid = v9fs_fid_create(dentry, v9ses, newfid, 0);
554 	if (fid == NULL) {
555 		dprintk(DEBUG_ERROR, "couldn't insert\n");
556 		result = -ENOMEM;
557 		goto FreeFcall;
558 	}
559 
560 	fid->qid = fcall->params.rstat.stat.qid;
561 
562 	dentry->d_op = &v9fs_dentry_operations;
563 	v9fs_stat2inode(&fcall->params.rstat.stat, inode, inode->i_sb);
564 
565 	d_add(dentry, inode);
566 	kfree(fcall);
567 
568 	return NULL;
569 
570       FreeFcall:
571 	kfree(fcall);
572 	return ERR_PTR(result);
573 }
574 
575 /**
576  * v9fs_vfs_unlink - VFS unlink hook to delete an inode
577  * @i:  inode that is being unlinked
578  * @d: dentry that is being unlinked
579  *
580  */
581 
582 static int v9fs_vfs_unlink(struct inode *i, struct dentry *d)
583 {
584 	return v9fs_remove(i, d, 0);
585 }
586 
587 /**
588  * v9fs_vfs_rmdir - VFS unlink hook to delete a directory
589  * @i:  inode that is being unlinked
590  * @d: dentry that is being unlinked
591  *
592  */
593 
594 static int v9fs_vfs_rmdir(struct inode *i, struct dentry *d)
595 {
596 	return v9fs_remove(i, d, 1);
597 }
598 
599 /**
600  * v9fs_vfs_rename - VFS hook to rename an inode
601  * @old_dir:  old dir inode
602  * @old_dentry: old dentry
603  * @new_dir: new dir inode
604  * @new_dentry: new dentry
605  *
606  */
607 
608 static int
609 v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
610 		struct inode *new_dir, struct dentry *new_dentry)
611 {
612 	struct inode *old_inode = old_dentry->d_inode;
613 	struct v9fs_session_info *v9ses = v9fs_inode2v9ses(old_inode);
614 	struct v9fs_fid *oldfid = v9fs_fid_lookup(old_dentry);
615 	struct v9fs_fid *olddirfid =
616 	    v9fs_fid_lookup(old_dentry->d_parent);
617 	struct v9fs_fid *newdirfid =
618 	    v9fs_fid_lookup(new_dentry->d_parent);
619 	struct v9fs_wstat wstat;
620 	struct v9fs_fcall *fcall = NULL;
621 	int fid = -1;
622 	int olddirfidnum = -1;
623 	int newdirfidnum = -1;
624 	int retval = 0;
625 
626 	dprintk(DEBUG_VFS, "\n");
627 
628 	if ((!oldfid) || (!olddirfid) || (!newdirfid)) {
629 		dprintk(DEBUG_ERROR, "problem with arguments\n");
630 		return -EBADF;
631 	}
632 
633 	/* 9P can only handle file rename in the same directory */
634 	if (memcmp(&olddirfid->qid, &newdirfid->qid, sizeof(newdirfid->qid))) {
635 		dprintk(DEBUG_ERROR, "old dir and new dir are different\n");
636 		retval = -EPERM;
637 		goto FreeFcallnBail;
638 	}
639 
640 	fid = oldfid->fid;
641 	olddirfidnum = olddirfid->fid;
642 	newdirfidnum = newdirfid->fid;
643 
644 	if (fid < 0) {
645 		dprintk(DEBUG_ERROR, "no fid for old file #%lu\n",
646 			old_inode->i_ino);
647 		retval = -EBADF;
648 		goto FreeFcallnBail;
649 	}
650 
651 	v9fs_blank_wstat(&wstat);
652 	wstat.muid = v9ses->name;
653 	wstat.name = (char *) new_dentry->d_name.name;
654 
655 	retval = v9fs_t_wstat(v9ses, fid, &wstat, &fcall);
656 
657       FreeFcallnBail:
658 	if (retval < 0)
659 		PRINT_FCALL_ERROR("wstat error", fcall);
660 
661 	kfree(fcall);
662 	return retval;
663 }
664 
665 /**
666  * v9fs_vfs_getattr - retrieve file metadata
667  * @mnt - mount information
668  * @dentry - file to get attributes on
669  * @stat - metadata structure to populate
670  *
671  */
672 
673 static int
674 v9fs_vfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
675 		 struct kstat *stat)
676 {
677 	struct v9fs_fcall *fcall = NULL;
678 	struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dentry->d_inode);
679 	struct v9fs_fid *fid = v9fs_fid_lookup(dentry);
680 	int err = -EPERM;
681 
682 	dprintk(DEBUG_VFS, "dentry: %p\n", dentry);
683 	if (!fid) {
684 		dprintk(DEBUG_ERROR,
685 			"couldn't find fid associated with dentry\n");
686 		return -EBADF;
687 	}
688 
689 	err = v9fs_t_stat(v9ses, fid->fid, &fcall);
690 
691 	if (err < 0)
692 		dprintk(DEBUG_ERROR, "stat error\n");
693 	else {
694 		v9fs_stat2inode(&fcall->params.rstat.stat, dentry->d_inode,
695 				  dentry->d_inode->i_sb);
696 		generic_fillattr(dentry->d_inode, stat);
697 	}
698 
699 	kfree(fcall);
700 	return err;
701 }
702 
703 /**
704  * v9fs_vfs_setattr - set file metadata
705  * @dentry: file whose metadata to set
706  * @iattr: metadata assignment structure
707  *
708  */
709 
710 static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr)
711 {
712 	struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dentry->d_inode);
713 	struct v9fs_fid *fid = v9fs_fid_lookup(dentry);
714 	struct v9fs_fcall *fcall = NULL;
715 	struct v9fs_wstat wstat;
716 	int res = -EPERM;
717 
718 	dprintk(DEBUG_VFS, "\n");
719 
720 	if (!fid) {
721 		dprintk(DEBUG_ERROR,
722 			"Couldn't find fid associated with dentry\n");
723 		return -EBADF;
724 	}
725 
726 	v9fs_blank_wstat(&wstat);
727 	if (iattr->ia_valid & ATTR_MODE)
728 		wstat.mode = unixmode2p9mode(v9ses, iattr->ia_mode);
729 
730 	if (iattr->ia_valid & ATTR_MTIME)
731 		wstat.mtime = iattr->ia_mtime.tv_sec;
732 
733 	if (iattr->ia_valid & ATTR_ATIME)
734 		wstat.atime = iattr->ia_atime.tv_sec;
735 
736 	if (iattr->ia_valid & ATTR_SIZE)
737 		wstat.length = iattr->ia_size;
738 
739 	if (v9ses->extended) {
740 		if (iattr->ia_valid & ATTR_UID)
741 			wstat.n_uid = iattr->ia_uid;
742 
743 		if (iattr->ia_valid & ATTR_GID)
744 			wstat.n_gid = iattr->ia_gid;
745 	}
746 
747 	res = v9fs_t_wstat(v9ses, fid->fid, &wstat, &fcall);
748 
749 	if (res < 0)
750 		PRINT_FCALL_ERROR("wstat error", fcall);
751 
752 	kfree(fcall);
753 	if (res >= 0)
754 		res = inode_setattr(dentry->d_inode, iattr);
755 
756 	return res;
757 }
758 
759 /**
760  * v9fs_stat2inode - populate an inode structure with mistat info
761  * @stat: Plan 9 metadata (mistat) structure
762  * @inode: inode to populate
763  * @sb: superblock of filesystem
764  *
765  */
766 
767 void
768 v9fs_stat2inode(struct v9fs_stat *stat, struct inode *inode,
769 	struct super_block *sb)
770 {
771 	int n;
772 	char ext[32];
773 	struct v9fs_session_info *v9ses = sb->s_fs_info;
774 
775 	inode->i_nlink = 1;
776 
777 	inode->i_atime.tv_sec = stat->atime;
778 	inode->i_mtime.tv_sec = stat->mtime;
779 	inode->i_ctime.tv_sec = stat->mtime;
780 
781 	inode->i_uid = v9ses->uid;
782 	inode->i_gid = v9ses->gid;
783 
784 	if (v9ses->extended) {
785 		inode->i_uid = stat->n_uid;
786 		inode->i_gid = stat->n_gid;
787 	}
788 
789 	inode->i_mode = p9mode2unixmode(v9ses, stat->mode);
790 	if ((S_ISBLK(inode->i_mode)) || (S_ISCHR(inode->i_mode))) {
791 		char type = 0;
792 		int major = -1;
793 		int minor = -1;
794 
795 		n = stat->extension.len;
796 		if (n > sizeof(ext)-1)
797 			n = sizeof(ext)-1;
798 		memmove(ext, stat->extension.str, n);
799 		ext[n] = 0;
800 		sscanf(ext, "%c %u %u", &type, &major, &minor);
801 		switch (type) {
802 		case 'c':
803 			inode->i_mode &= ~S_IFBLK;
804 			inode->i_mode |= S_IFCHR;
805 			break;
806 		case 'b':
807 			break;
808 		default:
809 			dprintk(DEBUG_ERROR, "Unknown special type %c (%.*s)\n",
810 				type, stat->extension.len, stat->extension.str);
811 		};
812 		inode->i_rdev = MKDEV(major, minor);
813 	} else
814 		inode->i_rdev = 0;
815 
816 	inode->i_size = stat->length;
817 
818 	inode->i_blksize = sb->s_blocksize;
819 	inode->i_blocks =
820 	    (inode->i_size + inode->i_blksize - 1) >> sb->s_blocksize_bits;
821 }
822 
823 /**
824  * v9fs_qid2ino - convert qid into inode number
825  * @qid: qid to hash
826  *
827  * BUG: potential for inode number collisions?
828  */
829 
830 ino_t v9fs_qid2ino(struct v9fs_qid *qid)
831 {
832 	u64 path = qid->path + 2;
833 	ino_t i = 0;
834 
835 	if (sizeof(ino_t) == sizeof(path))
836 		memcpy(&i, &path, sizeof(ino_t));
837 	else
838 		i = (ino_t) (path ^ (path >> 32));
839 
840 	return i;
841 }
842 
843 /**
844  * v9fs_readlink - read a symlink's location (internal version)
845  * @dentry: dentry for symlink
846  * @buffer: buffer to load symlink location into
847  * @buflen: length of buffer
848  *
849  */
850 
851 static int v9fs_readlink(struct dentry *dentry, char *buffer, int buflen)
852 {
853 	int retval = -EPERM;
854 
855 	struct v9fs_fcall *fcall = NULL;
856 	struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dentry->d_inode);
857 	struct v9fs_fid *fid = v9fs_fid_lookup(dentry);
858 
859 	if (!fid) {
860 		dprintk(DEBUG_ERROR, "could not resolve fid from dentry\n");
861 		retval = -EBADF;
862 		goto FreeFcall;
863 	}
864 
865 	if (!v9ses->extended) {
866 		retval = -EBADF;
867 		dprintk(DEBUG_ERROR, "not extended\n");
868 		goto FreeFcall;
869 	}
870 
871 	dprintk(DEBUG_VFS, " %s\n", dentry->d_name.name);
872 	retval = v9fs_t_stat(v9ses, fid->fid, &fcall);
873 
874 	if (retval < 0) {
875 		dprintk(DEBUG_ERROR, "stat error\n");
876 		goto FreeFcall;
877 	}
878 
879 	if (!fcall)
880 		return -EIO;
881 
882 	if (!(fcall->params.rstat.stat.mode & V9FS_DMSYMLINK)) {
883 		retval = -EINVAL;
884 		goto FreeFcall;
885 	}
886 
887 	/* copy extension buffer into buffer */
888 	if (fcall->params.rstat.stat.extension.len < buflen)
889 		buflen = fcall->params.rstat.stat.extension.len;
890 
891 	memcpy(buffer, fcall->params.rstat.stat.extension.str, buflen - 1);
892 	buffer[buflen-1] = 0;
893 
894 	retval = buflen;
895 
896       FreeFcall:
897 	kfree(fcall);
898 
899 	return retval;
900 }
901 
902 /**
903  * v9fs_vfs_readlink - read a symlink's location
904  * @dentry: dentry for symlink
905  * @buf: buffer to load symlink location into
906  * @buflen: length of buffer
907  *
908  */
909 
910 static int v9fs_vfs_readlink(struct dentry *dentry, char __user * buffer,
911 			     int buflen)
912 {
913 	int retval;
914 	int ret;
915 	char *link = __getname();
916 
917 	if (buflen > PATH_MAX)
918 		buflen = PATH_MAX;
919 
920 	dprintk(DEBUG_VFS, " dentry: %s (%p)\n", dentry->d_iname, dentry);
921 
922 	retval = v9fs_readlink(dentry, link, buflen);
923 
924 	if (retval > 0) {
925 		if ((ret = copy_to_user(buffer, link, retval)) != 0) {
926 			dprintk(DEBUG_ERROR, "problem copying to user: %d\n",
927 				ret);
928 			retval = ret;
929 		}
930 	}
931 
932 	__putname(link);
933 	return retval;
934 }
935 
936 /**
937  * v9fs_vfs_follow_link - follow a symlink path
938  * @dentry: dentry for symlink
939  * @nd: nameidata
940  *
941  */
942 
943 static void *v9fs_vfs_follow_link(struct dentry *dentry, struct nameidata *nd)
944 {
945 	int len = 0;
946 	char *link = __getname();
947 
948 	dprintk(DEBUG_VFS, "%s n", dentry->d_name.name);
949 
950 	if (!link)
951 		link = ERR_PTR(-ENOMEM);
952 	else {
953 		len = v9fs_readlink(dentry, link, strlen(link));
954 
955 		if (len < 0) {
956 			__putname(link);
957 			link = ERR_PTR(len);
958 		} else
959 			link[len] = 0;
960 	}
961 	nd_set_link(nd, link);
962 
963 	return NULL;
964 }
965 
966 /**
967  * v9fs_vfs_put_link - release a symlink path
968  * @dentry: dentry for symlink
969  * @nd: nameidata
970  *
971  */
972 
973 static void v9fs_vfs_put_link(struct dentry *dentry, struct nameidata *nd, void *p)
974 {
975 	char *s = nd_get_link(nd);
976 
977 	dprintk(DEBUG_VFS, " %s %s\n", dentry->d_name.name, s);
978 	if (!IS_ERR(s))
979 		__putname(s);
980 }
981 
982 static int v9fs_vfs_mkspecial(struct inode *dir, struct dentry *dentry,
983 	int mode, const char *extension)
984 {
985 	int err, retval;
986 	struct v9fs_session_info *v9ses;
987 	struct v9fs_fcall *fcall;
988 	struct v9fs_fid *fid;
989 	struct v9fs_wstat wstat;
990 
991 	v9ses = v9fs_inode2v9ses(dir);
992 	retval = -EPERM;
993 	fcall = NULL;
994 
995 	if (!v9ses->extended) {
996 		dprintk(DEBUG_ERROR, "not extended\n");
997 		goto free_mem;
998 	}
999 
1000 	/* issue a create */
1001 	retval = v9fs_create(dir, dentry, mode, 0);
1002 	if (retval != 0)
1003 		goto free_mem;
1004 
1005 	fid = v9fs_fid_get_created(dentry);
1006 	if (!fid) {
1007 		dprintk(DEBUG_ERROR, "couldn't resolve fid from dentry\n");
1008 		goto free_mem;
1009 	}
1010 
1011 	/* issue a Twstat */
1012 	v9fs_blank_wstat(&wstat);
1013 	wstat.muid = v9ses->name;
1014 	wstat.extension = (char *) extension;
1015 	retval = v9fs_t_wstat(v9ses, fid->fid, &wstat, &fcall);
1016 	if (retval < 0) {
1017 		PRINT_FCALL_ERROR("wstat error", fcall);
1018 		goto free_mem;
1019 	}
1020 
1021 	err = v9fs_t_clunk(v9ses, fid->fid);
1022 	if (err < 0) {
1023 		dprintk(DEBUG_ERROR, "clunk failed: %d\n", err);
1024 		goto free_mem;
1025 	}
1026 
1027 	d_drop(dentry);		/* FID - will this also clunk? */
1028 
1029 free_mem:
1030 	kfree(fcall);
1031 	return retval;
1032 }
1033 
1034 /**
1035  * v9fs_vfs_symlink - helper function to create symlinks
1036  * @dir: directory inode containing symlink
1037  * @dentry: dentry for symlink
1038  * @symname: symlink data
1039  *
1040  * See 9P2000.u RFC for more information
1041  *
1042  */
1043 
1044 static int
1045 v9fs_vfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
1046 {
1047 	dprintk(DEBUG_VFS, " %lu,%s,%s\n", dir->i_ino, dentry->d_name.name,
1048 		symname);
1049 
1050 	return v9fs_vfs_mkspecial(dir, dentry, S_IFLNK, symname);
1051 }
1052 
1053 /**
1054  * v9fs_vfs_link - create a hardlink
1055  * @old_dentry: dentry for file to link to
1056  * @dir: inode destination for new link
1057  * @dentry: dentry for link
1058  *
1059  */
1060 
1061 /* XXX - lots of code dup'd from symlink and creates,
1062  * figure out a better reuse strategy
1063  */
1064 
1065 static int
1066 v9fs_vfs_link(struct dentry *old_dentry, struct inode *dir,
1067 	      struct dentry *dentry)
1068 {
1069 	int retval;
1070 	struct v9fs_fid *oldfid;
1071 	char *name;
1072 
1073 	dprintk(DEBUG_VFS, " %lu,%s,%s\n", dir->i_ino, dentry->d_name.name,
1074 		old_dentry->d_name.name);
1075 
1076 	oldfid = v9fs_fid_lookup(old_dentry);
1077 	if (!oldfid) {
1078 		dprintk(DEBUG_ERROR, "can't find oldfid\n");
1079 		return -EPERM;
1080 	}
1081 
1082 	name = __getname();
1083 	sprintf(name, "hardlink(%d)\n", oldfid->fid);
1084 	retval = v9fs_vfs_mkspecial(dir, dentry, V9FS_DMLINK, name);
1085 	__putname(name);
1086 
1087 	return retval;
1088 }
1089 
1090 /**
1091  * v9fs_vfs_mknod - create a special file
1092  * @dir: inode destination for new link
1093  * @dentry: dentry for file
1094  * @mode: mode for creation
1095  * @dev_t: device associated with special file
1096  *
1097  */
1098 
1099 static int
1100 v9fs_vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev)
1101 {
1102 	int retval;
1103 	char *name;
1104 
1105 	dprintk(DEBUG_VFS, " %lu,%s mode: %x MAJOR: %u MINOR: %u\n", dir->i_ino,
1106 		dentry->d_name.name, mode, MAJOR(rdev), MINOR(rdev));
1107 
1108 	if (!new_valid_dev(rdev))
1109 		return -EINVAL;
1110 
1111 	name = __getname();
1112 	/* build extension */
1113 	if (S_ISBLK(mode))
1114 		sprintf(name, "b %u %u", MAJOR(rdev), MINOR(rdev));
1115 	else if (S_ISCHR(mode))
1116 		sprintf(name, "c %u %u", MAJOR(rdev), MINOR(rdev));
1117 	else if (S_ISFIFO(mode))
1118 		*name = 0;
1119 	else {
1120 		__putname(name);
1121 		return -EINVAL;
1122 	}
1123 
1124 	retval = v9fs_vfs_mkspecial(dir, dentry, mode, name);
1125 	__putname(name);
1126 
1127 	return retval;
1128 }
1129 
1130 static struct inode_operations v9fs_dir_inode_operations_ext = {
1131 	.create = v9fs_vfs_create,
1132 	.lookup = v9fs_vfs_lookup,
1133 	.symlink = v9fs_vfs_symlink,
1134 	.link = v9fs_vfs_link,
1135 	.unlink = v9fs_vfs_unlink,
1136 	.mkdir = v9fs_vfs_mkdir,
1137 	.rmdir = v9fs_vfs_rmdir,
1138 	.mknod = v9fs_vfs_mknod,
1139 	.rename = v9fs_vfs_rename,
1140 	.readlink = v9fs_vfs_readlink,
1141 	.getattr = v9fs_vfs_getattr,
1142 	.setattr = v9fs_vfs_setattr,
1143 };
1144 
1145 static struct inode_operations v9fs_dir_inode_operations = {
1146 	.create = v9fs_vfs_create,
1147 	.lookup = v9fs_vfs_lookup,
1148 	.unlink = v9fs_vfs_unlink,
1149 	.mkdir = v9fs_vfs_mkdir,
1150 	.rmdir = v9fs_vfs_rmdir,
1151 	.mknod = v9fs_vfs_mknod,
1152 	.rename = v9fs_vfs_rename,
1153 	.getattr = v9fs_vfs_getattr,
1154 	.setattr = v9fs_vfs_setattr,
1155 };
1156 
1157 static struct inode_operations v9fs_file_inode_operations = {
1158 	.getattr = v9fs_vfs_getattr,
1159 	.setattr = v9fs_vfs_setattr,
1160 };
1161 
1162 static struct inode_operations v9fs_symlink_inode_operations = {
1163 	.readlink = v9fs_vfs_readlink,
1164 	.follow_link = v9fs_vfs_follow_link,
1165 	.put_link = v9fs_vfs_put_link,
1166 	.getattr = v9fs_vfs_getattr,
1167 	.setattr = v9fs_vfs_setattr,
1168 };
1169