[PATCH] v9fs: handle kthread_create failure, minor bugfixes
[safe/jmp/linux-2.6] / 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         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 };