[PATCH] v9fs: zero copy implementation
[linux-2.6.git] / fs / 9p / vfs_inode.c
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 - retreive 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         char ext[32];
772         struct v9fs_session_info *v9ses = sb->s_fs_info;
773
774         inode->i_nlink = 1;
775
776         inode->i_atime.tv_sec = stat->atime;
777         inode->i_mtime.tv_sec = stat->mtime;
778         inode->i_ctime.tv_sec = stat->mtime;
779
780         inode->i_uid = v9ses->uid;
781         inode->i_gid = v9ses->gid;
782
783         if (v9ses->extended) {
784                 inode->i_uid = stat->n_uid;
785                 inode->i_gid = stat->n_gid;
786         }
787
788         inode->i_mode = p9mode2unixmode(v9ses, stat->mode);
789         if ((S_ISBLK(inode->i_mode)) || (S_ISCHR(inode->i_mode))) {
790                 char type = 0;
791                 int major = -1;
792                 int minor = -1;
793
794                 v9fs_str_copy(ext, sizeof(ext), &stat->extension);
795                 sscanf(ext, "%c %u %u", &type, &major, &minor);
796                 switch (type) {
797                 case 'c':
798                         inode->i_mode &= ~S_IFBLK;
799                         inode->i_mode |= S_IFCHR;
800                         break;
801                 case 'b':
802                         break;
803                 default:
804                         dprintk(DEBUG_ERROR, "Unknown special type %c (%.*s)\n",
805                                 type, stat->extension.len, stat->extension.str);
806                 };
807                 inode->i_rdev = MKDEV(major, minor);
808         } else
809                 inode->i_rdev = 0;
810
811         inode->i_size = stat->length;
812
813         inode->i_blksize = sb->s_blocksize;
814         inode->i_blocks =
815             (inode->i_size + inode->i_blksize - 1) >> sb->s_blocksize_bits;
816 }
817
818 /**
819  * v9fs_qid2ino - convert qid into inode number
820  * @qid: qid to hash
821  *
822  * BUG: potential for inode number collisions?
823  */
824
825 ino_t v9fs_qid2ino(struct v9fs_qid *qid)
826 {
827         u64 path = qid->path + 2;
828         ino_t i = 0;
829
830         if (sizeof(ino_t) == sizeof(path))
831                 memcpy(&i, &path, sizeof(ino_t));
832         else
833                 i = (ino_t) (path ^ (path >> 32));
834
835         return i;
836 }
837
838 /**
839  * v9fs_readlink - read a symlink's location (internal version)
840  * @dentry: dentry for symlink
841  * @buffer: buffer to load symlink location into
842  * @buflen: length of buffer
843  *
844  */
845
846 static int v9fs_readlink(struct dentry *dentry, char *buffer, int buflen)
847 {
848         int retval = -EPERM;
849
850         struct v9fs_fcall *fcall = NULL;
851         struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dentry->d_inode);
852         struct v9fs_fid *fid = v9fs_fid_lookup(dentry);
853
854         if (!fid) {
855                 dprintk(DEBUG_ERROR, "could not resolve fid from dentry\n");
856                 retval = -EBADF;
857                 goto FreeFcall;
858         }
859
860         if (!v9ses->extended) {
861                 retval = -EBADF;
862                 dprintk(DEBUG_ERROR, "not extended\n");
863                 goto FreeFcall;
864         }
865
866         dprintk(DEBUG_VFS, " %s\n", dentry->d_name.name);
867         retval = v9fs_t_stat(v9ses, fid->fid, &fcall);
868
869         if (retval < 0) {
870                 dprintk(DEBUG_ERROR, "stat error\n");
871                 goto FreeFcall;
872         }
873
874         if (!fcall)
875                 return -EIO;
876
877         if (!(fcall->params.rstat.stat.mode & V9FS_DMSYMLINK)) {
878                 retval = -EINVAL;
879                 goto FreeFcall;
880         }
881
882         /* copy extension buffer into buffer */
883         if (fcall->params.rstat.stat.extension.len < buflen)
884                 buflen = fcall->params.rstat.stat.extension.len;
885
886         memcpy(buffer, fcall->params.rstat.stat.extension.str, buflen - 1);
887         buffer[buflen-1] = 0;
888
889         retval = buflen;
890
891       FreeFcall:
892         kfree(fcall);
893
894         return retval;
895 }
896
897 /**
898  * v9fs_vfs_readlink - read a symlink's location
899  * @dentry: dentry for symlink
900  * @buf: buffer to load symlink location into
901  * @buflen: length of buffer
902  *
903  */
904
905 static int v9fs_vfs_readlink(struct dentry *dentry, char __user * buffer,
906                              int buflen)
907 {
908         int retval;
909         int ret;
910         char *link = __getname();
911
912         if (buflen > PATH_MAX)
913                 buflen = PATH_MAX;
914
915         dprintk(DEBUG_VFS, " dentry: %s (%p)\n", dentry->d_iname, dentry);
916
917         retval = v9fs_readlink(dentry, link, buflen);
918
919         if (retval > 0) {
920                 if ((ret = copy_to_user(buffer, link, retval)) != 0) {
921                         dprintk(DEBUG_ERROR, "problem copying to user: %d\n",
922                                 ret);
923                         retval = ret;
924                 }
925         }
926
927         __putname(link);
928         return retval;
929 }
930
931 /**
932  * v9fs_vfs_follow_link - follow a symlink path
933  * @dentry: dentry for symlink
934  * @nd: nameidata
935  *
936  */
937
938 static void *v9fs_vfs_follow_link(struct dentry *dentry, struct nameidata *nd)
939 {
940         int len = 0;
941         char *link = __getname();
942
943         dprintk(DEBUG_VFS, "%s n", dentry->d_name.name);
944
945         if (!link)
946                 link = ERR_PTR(-ENOMEM);
947         else {
948                 len = v9fs_readlink(dentry, link, strlen(link));
949
950                 if (len < 0) {
951                         __putname(link);
952                         link = ERR_PTR(len);
953                 } else
954                         link[len] = 0;
955         }
956         nd_set_link(nd, link);
957
958         return NULL;
959 }
960
961 /**
962  * v9fs_vfs_put_link - release a symlink path
963  * @dentry: dentry for symlink
964  * @nd: nameidata
965  *
966  */
967
968 static void v9fs_vfs_put_link(struct dentry *dentry, struct nameidata *nd, void *p)
969 {
970         char *s = nd_get_link(nd);
971
972         dprintk(DEBUG_VFS, " %s %s\n", dentry->d_name.name, s);
973         if (!IS_ERR(s))
974                 __putname(s);
975 }
976
977 static int v9fs_vfs_mkspecial(struct inode *dir, struct dentry *dentry,
978         int mode, const char *extension)
979 {
980         int err, retval;
981         struct v9fs_session_info *v9ses;
982         struct v9fs_fcall *fcall;
983         struct v9fs_fid *fid;
984         struct v9fs_wstat wstat;
985
986         v9ses = v9fs_inode2v9ses(dir);
987         retval = -EPERM;
988         fcall = NULL;
989
990         if (!v9ses->extended) {
991                 dprintk(DEBUG_ERROR, "not extended\n");
992                 goto free_mem;
993         }
994
995         /* issue a create */
996         retval = v9fs_create(dir, dentry, mode, 0);
997         if (retval != 0)
998                 goto free_mem;
999
1000         fid = v9fs_fid_get_created(dentry);
1001         if (!fid) {
1002                 dprintk(DEBUG_ERROR, "couldn't resolve fid from dentry\n");
1003                 goto free_mem;
1004         }
1005
1006         /* issue a Twstat */
1007         v9fs_blank_wstat(&wstat);
1008         wstat.muid = v9ses->name;
1009         wstat.extension = (char *) extension;
1010         retval = v9fs_t_wstat(v9ses, fid->fid, &wstat, &fcall);
1011         if (retval < 0) {
1012                 PRINT_FCALL_ERROR("wstat error", fcall);
1013                 goto free_mem;
1014         }
1015
1016         err = v9fs_t_clunk(v9ses, fid->fid);
1017         if (err < 0) {
1018                 dprintk(DEBUG_ERROR, "clunk failed: %d\n", err);
1019                 goto free_mem;
1020         }
1021
1022         d_drop(dentry);         /* FID - will this also clunk? */
1023
1024 free_mem:
1025         kfree(fcall);
1026         return retval;
1027 }
1028
1029 /**
1030  * v9fs_vfs_symlink - helper function to create symlinks
1031  * @dir: directory inode containing symlink
1032  * @dentry: dentry for symlink
1033  * @symname: symlink data
1034  *
1035  * See 9P2000.u RFC for more information
1036  *
1037  */
1038
1039 static int
1040 v9fs_vfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
1041 {
1042         dprintk(DEBUG_VFS, " %lu,%s,%s\n", dir->i_ino, dentry->d_name.name,
1043                 symname);
1044
1045         return v9fs_vfs_mkspecial(dir, dentry, S_IFLNK, symname);
1046 }
1047
1048 /**
1049  * v9fs_vfs_link - create a hardlink
1050  * @old_dentry: dentry for file to link to
1051  * @dir: inode destination for new link
1052  * @dentry: dentry for link
1053  *
1054  */
1055
1056 /* XXX - lots of code dup'd from symlink and creates,
1057  * figure out a better reuse strategy
1058  */
1059
1060 static int
1061 v9fs_vfs_link(struct dentry *old_dentry, struct inode *dir,
1062               struct dentry *dentry)
1063 {
1064         int retval;
1065         struct v9fs_fid *oldfid;
1066         char *name;
1067
1068         dprintk(DEBUG_VFS, " %lu,%s,%s\n", dir->i_ino, dentry->d_name.name,
1069                 old_dentry->d_name.name);
1070
1071         oldfid = v9fs_fid_lookup(old_dentry);
1072         if (!oldfid) {
1073                 dprintk(DEBUG_ERROR, "can't find oldfid\n");
1074                 return -EPERM;
1075         }
1076
1077         name = __getname();
1078         sprintf(name, "hardlink(%d)\n", oldfid->fid);
1079         retval = v9fs_vfs_mkspecial(dir, dentry, V9FS_DMLINK, name);
1080         __putname(name);
1081
1082         return retval;
1083 }
1084
1085 /**
1086  * v9fs_vfs_mknod - create a special file
1087  * @dir: inode destination for new link
1088  * @dentry: dentry for file
1089  * @mode: mode for creation
1090  * @dev_t: device associated with special file
1091  *
1092  */
1093
1094 static int
1095 v9fs_vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev)
1096 {
1097         int retval;
1098         char *name;
1099
1100         dprintk(DEBUG_VFS, " %lu,%s mode: %x MAJOR: %u MINOR: %u\n", dir->i_ino,
1101                 dentry->d_name.name, mode, MAJOR(rdev), MINOR(rdev));
1102
1103         if (!new_valid_dev(rdev))
1104                 return -EINVAL;
1105
1106         name = __getname();
1107         /* build extension */
1108         if (S_ISBLK(mode))
1109                 sprintf(name, "b %u %u", MAJOR(rdev), MINOR(rdev));
1110         else if (S_ISCHR(mode))
1111                 sprintf(name, "c %u %u", MAJOR(rdev), MINOR(rdev));
1112         else if (S_ISFIFO(mode))
1113                 *name = 0;
1114         else {
1115                 __putname(name);
1116                 return -EINVAL;
1117         }
1118
1119         retval = v9fs_vfs_mkspecial(dir, dentry, mode, name);
1120         __putname(name);
1121
1122         return retval;
1123 }
1124
1125 static struct inode_operations v9fs_dir_inode_operations_ext = {
1126         .create = v9fs_vfs_create,
1127         .lookup = v9fs_vfs_lookup,
1128         .symlink = v9fs_vfs_symlink,
1129         .link = v9fs_vfs_link,
1130         .unlink = v9fs_vfs_unlink,
1131         .mkdir = v9fs_vfs_mkdir,
1132         .rmdir = v9fs_vfs_rmdir,
1133         .mknod = v9fs_vfs_mknod,
1134         .rename = v9fs_vfs_rename,
1135         .readlink = v9fs_vfs_readlink,
1136         .getattr = v9fs_vfs_getattr,
1137         .setattr = v9fs_vfs_setattr,
1138 };
1139
1140 static struct inode_operations v9fs_dir_inode_operations = {
1141         .create = v9fs_vfs_create,
1142         .lookup = v9fs_vfs_lookup,
1143         .unlink = v9fs_vfs_unlink,
1144         .mkdir = v9fs_vfs_mkdir,
1145         .rmdir = v9fs_vfs_rmdir,
1146         .mknod = v9fs_vfs_mknod,
1147         .rename = v9fs_vfs_rename,
1148         .getattr = v9fs_vfs_getattr,
1149         .setattr = v9fs_vfs_setattr,
1150 };
1151
1152 static struct inode_operations v9fs_file_inode_operations = {
1153         .getattr = v9fs_vfs_getattr,
1154         .setattr = v9fs_vfs_setattr,
1155 };
1156
1157 static struct inode_operations v9fs_symlink_inode_operations = {
1158         .readlink = v9fs_vfs_readlink,
1159         .follow_link = v9fs_vfs_follow_link,
1160         .put_link = v9fs_vfs_put_link,
1161         .getattr = v9fs_vfs_getattr,
1162         .setattr = v9fs_vfs_setattr,
1163 };