diff options
author | Eric Van Hensbergen <ericvh@gmail.com> | 2007-01-26 03:57:06 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-01-26 16:51:00 -0500 |
commit | da977b2c7eb4d6312f063a7b486f2aad99809710 (patch) | |
tree | bb8a2afc766c16e3349e03dfb8a706dca6408395 /fs | |
parent | ff76e1dfc8728278ee231feeb93146f9c57c3ec3 (diff) |
[PATCH] 9p: fix segfault caused by race condition in meta-data operations
Running dbench multithreaded exposed a race condition where fid structures
were removed while in use. This patch adds semaphores to meta-data operations
to protect the fid structure. Some cleanup of error-case handling in the
inode operations is also included.
Signed-off-by: Eric Van Hensbergen <ericvh@gmail.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/9p/fid.c | 69 | ||||
-rw-r--r-- | fs/9p/fid.h | 5 | ||||
-rw-r--r-- | fs/9p/vfs_file.c | 47 | ||||
-rw-r--r-- | fs/9p/vfs_inode.c | 204 |
4 files changed, 196 insertions, 129 deletions
diff --git a/fs/9p/fid.c b/fs/9p/fid.c index 27507201f9e7..a9b6301a04fc 100644 --- a/fs/9p/fid.c +++ b/fs/9p/fid.c | |||
@@ -25,6 +25,7 @@ | |||
25 | #include <linux/fs.h> | 25 | #include <linux/fs.h> |
26 | #include <linux/sched.h> | 26 | #include <linux/sched.h> |
27 | #include <linux/idr.h> | 27 | #include <linux/idr.h> |
28 | #include <asm/semaphore.h> | ||
28 | 29 | ||
29 | #include "debug.h" | 30 | #include "debug.h" |
30 | #include "v9fs.h" | 31 | #include "v9fs.h" |
@@ -84,6 +85,7 @@ struct v9fs_fid *v9fs_fid_create(struct v9fs_session_info *v9ses, int fid) | |||
84 | new->iounit = 0; | 85 | new->iounit = 0; |
85 | new->rdir_pos = 0; | 86 | new->rdir_pos = 0; |
86 | new->rdir_fcall = NULL; | 87 | new->rdir_fcall = NULL; |
88 | init_MUTEX(&new->lock); | ||
87 | INIT_LIST_HEAD(&new->list); | 89 | INIT_LIST_HEAD(&new->list); |
88 | 90 | ||
89 | return new; | 91 | return new; |
@@ -102,11 +104,11 @@ void v9fs_fid_destroy(struct v9fs_fid *fid) | |||
102 | } | 104 | } |
103 | 105 | ||
104 | /** | 106 | /** |
105 | * v9fs_fid_lookup - retrieve the right fid from a particular dentry | 107 | * v9fs_fid_lookup - return a locked fid from a dentry |
106 | * @dentry: dentry to look for fid in | 108 | * @dentry: dentry to look for fid in |
107 | * @type: intent of lookup (operation or traversal) | ||
108 | * | 109 | * |
109 | * find a fid in the dentry | 110 | * find a fid in the dentry, obtain its semaphore and return a reference to it. |
111 | * code calling lookup is responsible for releasing lock | ||
110 | * | 112 | * |
111 | * TODO: only match fids that have the same uid as current user | 113 | * TODO: only match fids that have the same uid as current user |
112 | * | 114 | * |
@@ -124,7 +126,68 @@ struct v9fs_fid *v9fs_fid_lookup(struct dentry *dentry) | |||
124 | 126 | ||
125 | if (!return_fid) { | 127 | if (!return_fid) { |
126 | dprintk(DEBUG_ERROR, "Couldn't find a fid in dentry\n"); | 128 | dprintk(DEBUG_ERROR, "Couldn't find a fid in dentry\n"); |
129 | return_fid = ERR_PTR(-EBADF); | ||
127 | } | 130 | } |
128 | 131 | ||
132 | if(down_interruptible(&return_fid->lock)) | ||
133 | return ERR_PTR(-EINTR); | ||
134 | |||
129 | return return_fid; | 135 | return return_fid; |
130 | } | 136 | } |
137 | |||
138 | /** | ||
139 | * v9fs_fid_clone - lookup the fid for a dentry, clone a private copy and release it | ||
140 | * @dentry: dentry to look for fid in | ||
141 | * | ||
142 | * find a fid in the dentry and then clone to a new private fid | ||
143 | * | ||
144 | * TODO: only match fids that have the same uid as current user | ||
145 | * | ||
146 | */ | ||
147 | |||
148 | struct v9fs_fid *v9fs_fid_clone(struct dentry *dentry) | ||
149 | { | ||
150 | struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dentry->d_inode); | ||
151 | struct v9fs_fid *base_fid, *new_fid = ERR_PTR(-EBADF); | ||
152 | struct v9fs_fcall *fcall = NULL; | ||
153 | int fid, err; | ||
154 | |||
155 | base_fid = v9fs_fid_lookup(dentry); | ||
156 | |||
157 | if(IS_ERR(base_fid)) | ||
158 | return base_fid; | ||
159 | |||
160 | if(base_fid) { /* clone fid */ | ||
161 | fid = v9fs_get_idpool(&v9ses->fidpool); | ||
162 | if (fid < 0) { | ||
163 | eprintk(KERN_WARNING, "newfid fails!\n"); | ||
164 | new_fid = ERR_PTR(-ENOSPC); | ||
165 | goto Release_Fid; | ||
166 | } | ||
167 | |||
168 | err = v9fs_t_walk(v9ses, base_fid->fid, fid, NULL, &fcall); | ||
169 | if (err < 0) { | ||
170 | dprintk(DEBUG_ERROR, "clone walk didn't work\n"); | ||
171 | v9fs_put_idpool(fid, &v9ses->fidpool); | ||
172 | new_fid = ERR_PTR(err); | ||
173 | goto Free_Fcall; | ||
174 | } | ||
175 | new_fid = v9fs_fid_create(v9ses, fid); | ||
176 | if (new_fid == NULL) { | ||
177 | dprintk(DEBUG_ERROR, "out of memory\n"); | ||
178 | new_fid = ERR_PTR(-ENOMEM); | ||
179 | } | ||
180 | Free_Fcall: | ||
181 | kfree(fcall); | ||
182 | } | ||
183 | |||
184 | Release_Fid: | ||
185 | up(&base_fid->lock); | ||
186 | return new_fid; | ||
187 | } | ||
188 | |||
189 | void v9fs_fid_clunk(struct v9fs_session_info *v9ses, struct v9fs_fid *fid) | ||
190 | { | ||
191 | v9fs_t_clunk(v9ses, fid->fid); | ||
192 | v9fs_fid_destroy(fid); | ||
193 | } | ||
diff --git a/fs/9p/fid.h b/fs/9p/fid.h index aa974d6875c3..48fc170c26c8 100644 --- a/fs/9p/fid.h +++ b/fs/9p/fid.h | |||
@@ -30,6 +30,8 @@ struct v9fs_fid { | |||
30 | struct list_head list; /* list of fids associated with a dentry */ | 30 | struct list_head list; /* list of fids associated with a dentry */ |
31 | struct list_head active; /* XXX - debug */ | 31 | struct list_head active; /* XXX - debug */ |
32 | 32 | ||
33 | struct semaphore lock; | ||
34 | |||
33 | u32 fid; | 35 | u32 fid; |
34 | unsigned char fidopen; /* set when fid is opened */ | 36 | unsigned char fidopen; /* set when fid is opened */ |
35 | unsigned char fidclunked; /* set when fid has already been clunked */ | 37 | unsigned char fidclunked; /* set when fid has already been clunked */ |
@@ -55,3 +57,6 @@ struct v9fs_fid *v9fs_fid_get_created(struct dentry *); | |||
55 | void v9fs_fid_destroy(struct v9fs_fid *fid); | 57 | void v9fs_fid_destroy(struct v9fs_fid *fid); |
56 | struct v9fs_fid *v9fs_fid_create(struct v9fs_session_info *, int fid); | 58 | struct v9fs_fid *v9fs_fid_create(struct v9fs_session_info *, int fid); |
57 | int v9fs_fid_insert(struct v9fs_fid *fid, struct dentry *dentry); | 59 | int v9fs_fid_insert(struct v9fs_fid *fid, struct dentry *dentry); |
60 | struct v9fs_fid *v9fs_fid_clone(struct dentry *dentry); | ||
61 | void v9fs_fid_clunk(struct v9fs_session_info *v9ses, struct v9fs_fid *fid); | ||
62 | |||
diff --git a/fs/9p/vfs_file.c b/fs/9p/vfs_file.c index e86a07151280..9f17b0cacdd0 100644 --- a/fs/9p/vfs_file.c +++ b/fs/9p/vfs_file.c | |||
@@ -55,53 +55,22 @@ int v9fs_file_open(struct inode *inode, struct file *file) | |||
55 | struct v9fs_fid *vfid; | 55 | struct v9fs_fid *vfid; |
56 | struct v9fs_fcall *fcall = NULL; | 56 | struct v9fs_fcall *fcall = NULL; |
57 | int omode; | 57 | int omode; |
58 | int fid = V9FS_NOFID; | ||
59 | int err; | 58 | int err; |
60 | 59 | ||
61 | dprintk(DEBUG_VFS, "inode: %p file: %p \n", inode, file); | 60 | dprintk(DEBUG_VFS, "inode: %p file: %p \n", inode, file); |
62 | 61 | ||
63 | vfid = v9fs_fid_lookup(file->f_path.dentry); | 62 | vfid = v9fs_fid_clone(file->f_path.dentry); |
64 | if (!vfid) { | 63 | if (IS_ERR(vfid)) |
65 | dprintk(DEBUG_ERROR, "Couldn't resolve fid from dentry\n"); | 64 | return PTR_ERR(vfid); |
66 | return -EBADF; | ||
67 | } | ||
68 | |||
69 | fid = v9fs_get_idpool(&v9ses->fidpool); | ||
70 | if (fid < 0) { | ||
71 | eprintk(KERN_WARNING, "newfid fails!\n"); | ||
72 | return -ENOSPC; | ||
73 | } | ||
74 | 65 | ||
75 | err = v9fs_t_walk(v9ses, vfid->fid, fid, NULL, &fcall); | ||
76 | if (err < 0) { | ||
77 | dprintk(DEBUG_ERROR, "rewalk didn't work\n"); | ||
78 | if (fcall && fcall->id == RWALK) | ||
79 | goto clunk_fid; | ||
80 | else { | ||
81 | v9fs_put_idpool(fid, &v9ses->fidpool); | ||
82 | goto free_fcall; | ||
83 | } | ||
84 | } | ||
85 | kfree(fcall); | ||
86 | |||
87 | /* TODO: do special things for O_EXCL, O_NOFOLLOW, O_SYNC */ | ||
88 | /* translate open mode appropriately */ | ||
89 | omode = v9fs_uflags2omode(file->f_flags); | 66 | omode = v9fs_uflags2omode(file->f_flags); |
90 | err = v9fs_t_open(v9ses, fid, omode, &fcall); | 67 | err = v9fs_t_open(v9ses, vfid->fid, omode, &fcall); |
91 | if (err < 0) { | 68 | if (err < 0) { |
92 | PRINT_FCALL_ERROR("open failed", fcall); | 69 | PRINT_FCALL_ERROR("open failed", fcall); |
93 | goto clunk_fid; | 70 | goto Clunk_Fid; |
94 | } | ||
95 | |||
96 | vfid = kmalloc(sizeof(struct v9fs_fid), GFP_KERNEL); | ||
97 | if (vfid == NULL) { | ||
98 | dprintk(DEBUG_ERROR, "out of memory\n"); | ||
99 | err = -ENOMEM; | ||
100 | goto clunk_fid; | ||
101 | } | 71 | } |
102 | 72 | ||
103 | file->private_data = vfid; | 73 | file->private_data = vfid; |
104 | vfid->fid = fid; | ||
105 | vfid->fidopen = 1; | 74 | vfid->fidopen = 1; |
106 | vfid->fidclunked = 0; | 75 | vfid->fidclunked = 0; |
107 | vfid->iounit = fcall->params.ropen.iounit; | 76 | vfid->iounit = fcall->params.ropen.iounit; |
@@ -112,10 +81,8 @@ int v9fs_file_open(struct inode *inode, struct file *file) | |||
112 | 81 | ||
113 | return 0; | 82 | return 0; |
114 | 83 | ||
115 | clunk_fid: | 84 | Clunk_Fid: |
116 | v9fs_t_clunk(v9ses, fid); | 85 | v9fs_fid_clunk(v9ses, vfid); |
117 | |||
118 | free_fcall: | ||
119 | kfree(fcall); | 86 | kfree(fcall); |
120 | 87 | ||
121 | return err; | 88 | return err; |
diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index 05d30e89ba45..9109ba1d6969 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c | |||
@@ -416,12 +416,8 @@ static int v9fs_remove(struct inode *dir, struct dentry *file, int rmdir) | |||
416 | sb = file_inode->i_sb; | 416 | sb = file_inode->i_sb; |
417 | v9ses = v9fs_inode2v9ses(file_inode); | 417 | v9ses = v9fs_inode2v9ses(file_inode); |
418 | v9fid = v9fs_fid_lookup(file); | 418 | v9fid = v9fs_fid_lookup(file); |
419 | 419 | if(IS_ERR(v9fid)) | |
420 | if (!v9fid) { | 420 | return PTR_ERR(v9fid); |
421 | dprintk(DEBUG_ERROR, | ||
422 | "no v9fs_fid\n"); | ||
423 | return -EBADF; | ||
424 | } | ||
425 | 421 | ||
426 | fid = v9fid->fid; | 422 | fid = v9fid->fid; |
427 | if (fid < 0) { | 423 | if (fid < 0) { |
@@ -433,11 +429,13 @@ static int v9fs_remove(struct inode *dir, struct dentry *file, int rmdir) | |||
433 | result = v9fs_t_remove(v9ses, fid, &fcall); | 429 | result = v9fs_t_remove(v9ses, fid, &fcall); |
434 | if (result < 0) { | 430 | if (result < 0) { |
435 | PRINT_FCALL_ERROR("remove fails", fcall); | 431 | PRINT_FCALL_ERROR("remove fails", fcall); |
432 | goto Error; | ||
436 | } | 433 | } |
437 | 434 | ||
438 | v9fs_put_idpool(fid, &v9ses->fidpool); | 435 | v9fs_put_idpool(fid, &v9ses->fidpool); |
439 | v9fs_fid_destroy(v9fid); | 436 | v9fs_fid_destroy(v9fid); |
440 | 437 | ||
438 | Error: | ||
441 | kfree(fcall); | 439 | kfree(fcall); |
442 | return result; | 440 | return result; |
443 | } | 441 | } |
@@ -473,9 +471,13 @@ v9fs_vfs_create(struct inode *dir, struct dentry *dentry, int mode, | |||
473 | inode = NULL; | 471 | inode = NULL; |
474 | vfid = NULL; | 472 | vfid = NULL; |
475 | v9ses = v9fs_inode2v9ses(dir); | 473 | v9ses = v9fs_inode2v9ses(dir); |
476 | dfid = v9fs_fid_lookup(dentry->d_parent); | 474 | dfid = v9fs_fid_clone(dentry->d_parent); |
477 | perm = unixmode2p9mode(v9ses, mode); | 475 | if(IS_ERR(dfid)) { |
476 | err = PTR_ERR(dfid); | ||
477 | goto error; | ||
478 | } | ||
478 | 479 | ||
480 | perm = unixmode2p9mode(v9ses, mode); | ||
479 | if (nd && nd->flags & LOOKUP_OPEN) | 481 | if (nd && nd->flags & LOOKUP_OPEN) |
480 | flags = nd->intent.open.flags - 1; | 482 | flags = nd->intent.open.flags - 1; |
481 | else | 483 | else |
@@ -485,9 +487,10 @@ v9fs_vfs_create(struct inode *dir, struct dentry *dentry, int mode, | |||
485 | perm, v9fs_uflags2omode(flags), NULL, &fid, &qid, &iounit); | 487 | perm, v9fs_uflags2omode(flags), NULL, &fid, &qid, &iounit); |
486 | 488 | ||
487 | if (err) | 489 | if (err) |
488 | goto error; | 490 | goto clunk_dfid; |
489 | 491 | ||
490 | vfid = v9fs_clone_walk(v9ses, dfid->fid, dentry); | 492 | vfid = v9fs_clone_walk(v9ses, dfid->fid, dentry); |
493 | v9fs_fid_clunk(v9ses, dfid); | ||
491 | if (IS_ERR(vfid)) { | 494 | if (IS_ERR(vfid)) { |
492 | err = PTR_ERR(vfid); | 495 | err = PTR_ERR(vfid); |
493 | vfid = NULL; | 496 | vfid = NULL; |
@@ -525,6 +528,9 @@ v9fs_vfs_create(struct inode *dir, struct dentry *dentry, int mode, | |||
525 | 528 | ||
526 | return 0; | 529 | return 0; |
527 | 530 | ||
531 | clunk_dfid: | ||
532 | v9fs_fid_clunk(v9ses, dfid); | ||
533 | |||
528 | error: | 534 | error: |
529 | if (vfid) | 535 | if (vfid) |
530 | v9fs_fid_destroy(vfid); | 536 | v9fs_fid_destroy(vfid); |
@@ -551,7 +557,12 @@ static int v9fs_vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) | |||
551 | inode = NULL; | 557 | inode = NULL; |
552 | vfid = NULL; | 558 | vfid = NULL; |
553 | v9ses = v9fs_inode2v9ses(dir); | 559 | v9ses = v9fs_inode2v9ses(dir); |
554 | dfid = v9fs_fid_lookup(dentry->d_parent); | 560 | dfid = v9fs_fid_clone(dentry->d_parent); |
561 | if(IS_ERR(dfid)) { | ||
562 | err = PTR_ERR(dfid); | ||
563 | goto error; | ||
564 | } | ||
565 | |||
555 | perm = unixmode2p9mode(v9ses, mode | S_IFDIR); | 566 | perm = unixmode2p9mode(v9ses, mode | S_IFDIR); |
556 | 567 | ||
557 | err = v9fs_create(v9ses, dfid->fid, (char *) dentry->d_name.name, | 568 | err = v9fs_create(v9ses, dfid->fid, (char *) dentry->d_name.name, |
@@ -559,37 +570,36 @@ static int v9fs_vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) | |||
559 | 570 | ||
560 | if (err) { | 571 | if (err) { |
561 | dprintk(DEBUG_ERROR, "create error %d\n", err); | 572 | dprintk(DEBUG_ERROR, "create error %d\n", err); |
562 | goto error; | 573 | goto clean_up_dfid; |
563 | } | ||
564 | |||
565 | err = v9fs_t_clunk(v9ses, fid); | ||
566 | if (err) { | ||
567 | dprintk(DEBUG_ERROR, "clunk error %d\n", err); | ||
568 | goto error; | ||
569 | } | 574 | } |
570 | 575 | ||
571 | vfid = v9fs_clone_walk(v9ses, dfid->fid, dentry); | 576 | vfid = v9fs_clone_walk(v9ses, dfid->fid, dentry); |
572 | if (IS_ERR(vfid)) { | 577 | if (IS_ERR(vfid)) { |
573 | err = PTR_ERR(vfid); | 578 | err = PTR_ERR(vfid); |
574 | vfid = NULL; | 579 | vfid = NULL; |
575 | goto error; | 580 | goto clean_up_dfid; |
576 | } | 581 | } |
577 | 582 | ||
583 | v9fs_fid_clunk(v9ses, dfid); | ||
578 | inode = v9fs_inode_from_fid(v9ses, vfid->fid, dir->i_sb); | 584 | inode = v9fs_inode_from_fid(v9ses, vfid->fid, dir->i_sb); |
579 | if (IS_ERR(inode)) { | 585 | if (IS_ERR(inode)) { |
580 | err = PTR_ERR(inode); | 586 | err = PTR_ERR(inode); |
581 | inode = NULL; | 587 | inode = NULL; |
582 | goto error; | 588 | goto clean_up_fids; |
583 | } | 589 | } |
584 | 590 | ||
585 | dentry->d_op = &v9fs_dentry_operations; | 591 | dentry->d_op = &v9fs_dentry_operations; |
586 | d_instantiate(dentry, inode); | 592 | d_instantiate(dentry, inode); |
587 | return 0; | 593 | return 0; |
588 | 594 | ||
589 | error: | 595 | clean_up_fids: |
590 | if (vfid) | 596 | if (vfid) |
591 | v9fs_fid_destroy(vfid); | 597 | v9fs_fid_destroy(vfid); |
592 | 598 | ||
599 | clean_up_dfid: | ||
600 | v9fs_fid_clunk(v9ses, dfid); | ||
601 | |||
602 | error: | ||
593 | return err; | 603 | return err; |
594 | } | 604 | } |
595 | 605 | ||
@@ -622,28 +632,23 @@ static struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry, | |||
622 | dentry->d_op = &v9fs_dentry_operations; | 632 | dentry->d_op = &v9fs_dentry_operations; |
623 | dirfid = v9fs_fid_lookup(dentry->d_parent); | 633 | dirfid = v9fs_fid_lookup(dentry->d_parent); |
624 | 634 | ||
625 | if (!dirfid) { | 635 | if(IS_ERR(dirfid)) |
626 | dprintk(DEBUG_ERROR, "no dirfid\n"); | 636 | return ERR_PTR(PTR_ERR(dirfid)); |
627 | return ERR_PTR(-EINVAL); | ||
628 | } | ||
629 | 637 | ||
630 | dirfidnum = dirfid->fid; | 638 | dirfidnum = dirfid->fid; |
631 | 639 | ||
632 | if (dirfidnum < 0) { | ||
633 | dprintk(DEBUG_ERROR, "no dirfid for inode %p, #%lu\n", | ||
634 | dir, dir->i_ino); | ||
635 | return ERR_PTR(-EBADF); | ||
636 | } | ||
637 | |||
638 | newfid = v9fs_get_idpool(&v9ses->fidpool); | 640 | newfid = v9fs_get_idpool(&v9ses->fidpool); |
639 | if (newfid < 0) { | 641 | if (newfid < 0) { |
640 | eprintk(KERN_WARNING, "newfid fails!\n"); | 642 | eprintk(KERN_WARNING, "newfid fails!\n"); |
641 | return ERR_PTR(-ENOSPC); | 643 | result = -ENOSPC; |
644 | goto Release_Dirfid; | ||
642 | } | 645 | } |
643 | 646 | ||
644 | result = v9fs_t_walk(v9ses, dirfidnum, newfid, | 647 | result = v9fs_t_walk(v9ses, dirfidnum, newfid, |
645 | (char *)dentry->d_name.name, &fcall); | 648 | (char *)dentry->d_name.name, &fcall); |
646 | 649 | ||
650 | up(&dirfid->lock); | ||
651 | |||
647 | if (result < 0) { | 652 | if (result < 0) { |
648 | if (fcall && fcall->id == RWALK) | 653 | if (fcall && fcall->id == RWALK) |
649 | v9fs_t_clunk(v9ses, newfid); | 654 | v9fs_t_clunk(v9ses, newfid); |
@@ -701,8 +706,12 @@ static struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry, | |||
701 | 706 | ||
702 | return NULL; | 707 | return NULL; |
703 | 708 | ||
704 | FreeFcall: | 709 | Release_Dirfid: |
710 | up(&dirfid->lock); | ||
711 | |||
712 | FreeFcall: | ||
705 | kfree(fcall); | 713 | kfree(fcall); |
714 | |||
706 | return ERR_PTR(result); | 715 | return ERR_PTR(result); |
707 | } | 716 | } |
708 | 717 | ||
@@ -746,10 +755,8 @@ v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
746 | struct inode *old_inode = old_dentry->d_inode; | 755 | struct inode *old_inode = old_dentry->d_inode; |
747 | struct v9fs_session_info *v9ses = v9fs_inode2v9ses(old_inode); | 756 | struct v9fs_session_info *v9ses = v9fs_inode2v9ses(old_inode); |
748 | struct v9fs_fid *oldfid = v9fs_fid_lookup(old_dentry); | 757 | struct v9fs_fid *oldfid = v9fs_fid_lookup(old_dentry); |
749 | struct v9fs_fid *olddirfid = | 758 | struct v9fs_fid *olddirfid; |
750 | v9fs_fid_lookup(old_dentry->d_parent); | 759 | struct v9fs_fid *newdirfid; |
751 | struct v9fs_fid *newdirfid = | ||
752 | v9fs_fid_lookup(new_dentry->d_parent); | ||
753 | struct v9fs_wstat wstat; | 760 | struct v9fs_wstat wstat; |
754 | struct v9fs_fcall *fcall = NULL; | 761 | struct v9fs_fcall *fcall = NULL; |
755 | int fid = -1; | 762 | int fid = -1; |
@@ -759,16 +766,26 @@ v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
759 | 766 | ||
760 | dprintk(DEBUG_VFS, "\n"); | 767 | dprintk(DEBUG_VFS, "\n"); |
761 | 768 | ||
762 | if ((!oldfid) || (!olddirfid) || (!newdirfid)) { | 769 | if(IS_ERR(oldfid)) |
763 | dprintk(DEBUG_ERROR, "problem with arguments\n"); | 770 | return PTR_ERR(oldfid); |
764 | return -EBADF; | 771 | |
772 | olddirfid = v9fs_fid_clone(old_dentry->d_parent); | ||
773 | if(IS_ERR(olddirfid)) { | ||
774 | retval = PTR_ERR(olddirfid); | ||
775 | goto Release_lock; | ||
776 | } | ||
777 | |||
778 | newdirfid = v9fs_fid_clone(new_dentry->d_parent); | ||
779 | if(IS_ERR(newdirfid)) { | ||
780 | retval = PTR_ERR(newdirfid); | ||
781 | goto Clunk_olddir; | ||
765 | } | 782 | } |
766 | 783 | ||
767 | /* 9P can only handle file rename in the same directory */ | 784 | /* 9P can only handle file rename in the same directory */ |
768 | if (memcmp(&olddirfid->qid, &newdirfid->qid, sizeof(newdirfid->qid))) { | 785 | if (memcmp(&olddirfid->qid, &newdirfid->qid, sizeof(newdirfid->qid))) { |
769 | dprintk(DEBUG_ERROR, "old dir and new dir are different\n"); | 786 | dprintk(DEBUG_ERROR, "old dir and new dir are different\n"); |
770 | retval = -EXDEV; | 787 | retval = -EXDEV; |
771 | goto FreeFcallnBail; | 788 | goto Clunk_newdir; |
772 | } | 789 | } |
773 | 790 | ||
774 | fid = oldfid->fid; | 791 | fid = oldfid->fid; |
@@ -779,7 +796,7 @@ v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
779 | dprintk(DEBUG_ERROR, "no fid for old file #%lu\n", | 796 | dprintk(DEBUG_ERROR, "no fid for old file #%lu\n", |
780 | old_inode->i_ino); | 797 | old_inode->i_ino); |
781 | retval = -EBADF; | 798 | retval = -EBADF; |
782 | goto FreeFcallnBail; | 799 | goto Clunk_newdir; |
783 | } | 800 | } |
784 | 801 | ||
785 | v9fs_blank_wstat(&wstat); | 802 | v9fs_blank_wstat(&wstat); |
@@ -788,11 +805,20 @@ v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
788 | 805 | ||
789 | retval = v9fs_t_wstat(v9ses, fid, &wstat, &fcall); | 806 | retval = v9fs_t_wstat(v9ses, fid, &wstat, &fcall); |
790 | 807 | ||
791 | FreeFcallnBail: | ||
792 | if (retval < 0) | 808 | if (retval < 0) |
793 | PRINT_FCALL_ERROR("wstat error", fcall); | 809 | PRINT_FCALL_ERROR("wstat error", fcall); |
794 | 810 | ||
795 | kfree(fcall); | 811 | kfree(fcall); |
812 | |||
813 | Clunk_newdir: | ||
814 | v9fs_fid_clunk(v9ses, newdirfid); | ||
815 | |||
816 | Clunk_olddir: | ||
817 | v9fs_fid_clunk(v9ses, olddirfid); | ||
818 | |||
819 | Release_lock: | ||
820 | up(&oldfid->lock); | ||
821 | |||
796 | return retval; | 822 | return retval; |
797 | } | 823 | } |
798 | 824 | ||
@@ -810,15 +836,12 @@ v9fs_vfs_getattr(struct vfsmount *mnt, struct dentry *dentry, | |||
810 | { | 836 | { |
811 | struct v9fs_fcall *fcall = NULL; | 837 | struct v9fs_fcall *fcall = NULL; |
812 | struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dentry->d_inode); | 838 | struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dentry->d_inode); |
813 | struct v9fs_fid *fid = v9fs_fid_lookup(dentry); | 839 | struct v9fs_fid *fid = v9fs_fid_clone(dentry); |
814 | int err = -EPERM; | 840 | int err = -EPERM; |
815 | 841 | ||
816 | dprintk(DEBUG_VFS, "dentry: %p\n", dentry); | 842 | dprintk(DEBUG_VFS, "dentry: %p\n", dentry); |
817 | if (!fid) { | 843 | if(IS_ERR(fid)) |
818 | dprintk(DEBUG_ERROR, | 844 | return PTR_ERR(fid); |
819 | "couldn't find fid associated with dentry\n"); | ||
820 | return -EBADF; | ||
821 | } | ||
822 | 845 | ||
823 | err = v9fs_t_stat(v9ses, fid->fid, &fcall); | 846 | err = v9fs_t_stat(v9ses, fid->fid, &fcall); |
824 | 847 | ||
@@ -831,6 +854,7 @@ v9fs_vfs_getattr(struct vfsmount *mnt, struct dentry *dentry, | |||
831 | } | 854 | } |
832 | 855 | ||
833 | kfree(fcall); | 856 | kfree(fcall); |
857 | v9fs_fid_clunk(v9ses, fid); | ||
834 | return err; | 858 | return err; |
835 | } | 859 | } |
836 | 860 | ||
@@ -844,18 +868,14 @@ v9fs_vfs_getattr(struct vfsmount *mnt, struct dentry *dentry, | |||
844 | static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr) | 868 | static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr) |
845 | { | 869 | { |
846 | struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dentry->d_inode); | 870 | struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dentry->d_inode); |
847 | struct v9fs_fid *fid = v9fs_fid_lookup(dentry); | 871 | struct v9fs_fid *fid = v9fs_fid_clone(dentry); |
848 | struct v9fs_fcall *fcall = NULL; | 872 | struct v9fs_fcall *fcall = NULL; |
849 | struct v9fs_wstat wstat; | 873 | struct v9fs_wstat wstat; |
850 | int res = -EPERM; | 874 | int res = -EPERM; |
851 | 875 | ||
852 | dprintk(DEBUG_VFS, "\n"); | 876 | dprintk(DEBUG_VFS, "\n"); |
853 | 877 | if(IS_ERR(fid)) | |
854 | if (!fid) { | 878 | return PTR_ERR(fid); |
855 | dprintk(DEBUG_ERROR, | ||
856 | "Couldn't find fid associated with dentry\n"); | ||
857 | return -EBADF; | ||
858 | } | ||
859 | 879 | ||
860 | v9fs_blank_wstat(&wstat); | 880 | v9fs_blank_wstat(&wstat); |
861 | if (iattr->ia_valid & ATTR_MODE) | 881 | if (iattr->ia_valid & ATTR_MODE) |
@@ -887,6 +907,7 @@ static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr) | |||
887 | if (res >= 0) | 907 | if (res >= 0) |
888 | res = inode_setattr(dentry->d_inode, iattr); | 908 | res = inode_setattr(dentry->d_inode, iattr); |
889 | 909 | ||
910 | v9fs_fid_clunk(v9ses, fid); | ||
890 | return res; | 911 | return res; |
891 | } | 912 | } |
892 | 913 | ||
@@ -987,18 +1008,15 @@ static int v9fs_readlink(struct dentry *dentry, char *buffer, int buflen) | |||
987 | 1008 | ||
988 | struct v9fs_fcall *fcall = NULL; | 1009 | struct v9fs_fcall *fcall = NULL; |
989 | struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dentry->d_inode); | 1010 | struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dentry->d_inode); |
990 | struct v9fs_fid *fid = v9fs_fid_lookup(dentry); | 1011 | struct v9fs_fid *fid = v9fs_fid_clone(dentry); |
991 | 1012 | ||
992 | if (!fid) { | 1013 | if(IS_ERR(fid)) |
993 | dprintk(DEBUG_ERROR, "could not resolve fid from dentry\n"); | 1014 | return PTR_ERR(fid); |
994 | retval = -EBADF; | ||
995 | goto FreeFcall; | ||
996 | } | ||
997 | 1015 | ||
998 | if (!v9ses->extended) { | 1016 | if (!v9ses->extended) { |
999 | retval = -EBADF; | 1017 | retval = -EBADF; |
1000 | dprintk(DEBUG_ERROR, "not extended\n"); | 1018 | dprintk(DEBUG_ERROR, "not extended\n"); |
1001 | goto FreeFcall; | 1019 | goto ClunkFid; |
1002 | } | 1020 | } |
1003 | 1021 | ||
1004 | dprintk(DEBUG_VFS, " %s\n", dentry->d_name.name); | 1022 | dprintk(DEBUG_VFS, " %s\n", dentry->d_name.name); |
@@ -1009,8 +1027,10 @@ static int v9fs_readlink(struct dentry *dentry, char *buffer, int buflen) | |||
1009 | goto FreeFcall; | 1027 | goto FreeFcall; |
1010 | } | 1028 | } |
1011 | 1029 | ||
1012 | if (!fcall) | 1030 | if (!fcall) { |
1013 | return -EIO; | 1031 | retval = -EIO; |
1032 | goto ClunkFid; | ||
1033 | } | ||
1014 | 1034 | ||
1015 | if (!(fcall->params.rstat.stat.mode & V9FS_DMSYMLINK)) { | 1035 | if (!(fcall->params.rstat.stat.mode & V9FS_DMSYMLINK)) { |
1016 | retval = -EINVAL; | 1036 | retval = -EINVAL; |
@@ -1028,9 +1048,12 @@ static int v9fs_readlink(struct dentry *dentry, char *buffer, int buflen) | |||
1028 | fcall->params.rstat.stat.extension.str, buffer); | 1048 | fcall->params.rstat.stat.extension.str, buffer); |
1029 | retval = buflen; | 1049 | retval = buflen; |
1030 | 1050 | ||
1031 | FreeFcall: | 1051 | FreeFcall: |
1032 | kfree(fcall); | 1052 | kfree(fcall); |
1033 | 1053 | ||
1054 | ClunkFid: | ||
1055 | v9fs_fid_clunk(v9ses, fid); | ||
1056 | |||
1034 | return retval; | 1057 | return retval; |
1035 | } | 1058 | } |
1036 | 1059 | ||
@@ -1123,52 +1146,58 @@ static int v9fs_vfs_mkspecial(struct inode *dir, struct dentry *dentry, | |||
1123 | int err; | 1146 | int err; |
1124 | u32 fid, perm; | 1147 | u32 fid, perm; |
1125 | struct v9fs_session_info *v9ses; | 1148 | struct v9fs_session_info *v9ses; |
1126 | struct v9fs_fid *dfid, *vfid; | 1149 | struct v9fs_fid *dfid, *vfid = NULL; |
1127 | struct inode *inode; | 1150 | struct inode *inode = NULL; |
1128 | 1151 | ||
1129 | inode = NULL; | ||
1130 | vfid = NULL; | ||
1131 | v9ses = v9fs_inode2v9ses(dir); | 1152 | v9ses = v9fs_inode2v9ses(dir); |
1132 | dfid = v9fs_fid_lookup(dentry->d_parent); | ||
1133 | perm = unixmode2p9mode(v9ses, mode); | ||
1134 | |||
1135 | if (!v9ses->extended) { | 1153 | if (!v9ses->extended) { |
1136 | dprintk(DEBUG_ERROR, "not extended\n"); | 1154 | dprintk(DEBUG_ERROR, "not extended\n"); |
1137 | return -EPERM; | 1155 | return -EPERM; |
1138 | } | 1156 | } |
1139 | 1157 | ||
1158 | dfid = v9fs_fid_clone(dentry->d_parent); | ||
1159 | if(IS_ERR(dfid)) { | ||
1160 | err = PTR_ERR(dfid); | ||
1161 | goto error; | ||
1162 | } | ||
1163 | |||
1164 | perm = unixmode2p9mode(v9ses, mode); | ||
1165 | |||
1140 | err = v9fs_create(v9ses, dfid->fid, (char *) dentry->d_name.name, | 1166 | err = v9fs_create(v9ses, dfid->fid, (char *) dentry->d_name.name, |
1141 | perm, V9FS_OREAD, (char *) extension, &fid, NULL, NULL); | 1167 | perm, V9FS_OREAD, (char *) extension, &fid, NULL, NULL); |
1142 | 1168 | ||
1143 | if (err) | 1169 | if (err) |
1144 | goto error; | 1170 | goto clunk_dfid; |
1145 | 1171 | ||
1146 | err = v9fs_t_clunk(v9ses, fid); | 1172 | err = v9fs_t_clunk(v9ses, fid); |
1147 | if (err) | 1173 | if (err) |
1148 | goto error; | 1174 | goto clunk_dfid; |
1149 | 1175 | ||
1150 | vfid = v9fs_clone_walk(v9ses, dfid->fid, dentry); | 1176 | vfid = v9fs_clone_walk(v9ses, dfid->fid, dentry); |
1151 | if (IS_ERR(vfid)) { | 1177 | if (IS_ERR(vfid)) { |
1152 | err = PTR_ERR(vfid); | 1178 | err = PTR_ERR(vfid); |
1153 | vfid = NULL; | 1179 | vfid = NULL; |
1154 | goto error; | 1180 | goto clunk_dfid; |
1155 | } | 1181 | } |
1156 | 1182 | ||
1157 | inode = v9fs_inode_from_fid(v9ses, vfid->fid, dir->i_sb); | 1183 | inode = v9fs_inode_from_fid(v9ses, vfid->fid, dir->i_sb); |
1158 | if (IS_ERR(inode)) { | 1184 | if (IS_ERR(inode)) { |
1159 | err = PTR_ERR(inode); | 1185 | err = PTR_ERR(inode); |
1160 | inode = NULL; | 1186 | inode = NULL; |
1161 | goto error; | 1187 | goto free_vfid; |
1162 | } | 1188 | } |
1163 | 1189 | ||
1164 | dentry->d_op = &v9fs_dentry_operations; | 1190 | dentry->d_op = &v9fs_dentry_operations; |
1165 | d_instantiate(dentry, inode); | 1191 | d_instantiate(dentry, inode); |
1166 | return 0; | 1192 | return 0; |
1167 | 1193 | ||
1168 | error: | 1194 | free_vfid: |
1169 | if (vfid) | 1195 | v9fs_fid_destroy(vfid); |
1170 | v9fs_fid_destroy(vfid); | 1196 | |
1197 | clunk_dfid: | ||
1198 | v9fs_fid_clunk(v9ses, dfid); | ||
1171 | 1199 | ||
1200 | error: | ||
1172 | return err; | 1201 | return err; |
1173 | 1202 | ||
1174 | } | 1203 | } |
@@ -1209,26 +1238,29 @@ v9fs_vfs_link(struct dentry *old_dentry, struct inode *dir, | |||
1209 | struct dentry *dentry) | 1238 | struct dentry *dentry) |
1210 | { | 1239 | { |
1211 | int retval; | 1240 | int retval; |
1241 | struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dir); | ||
1212 | struct v9fs_fid *oldfid; | 1242 | struct v9fs_fid *oldfid; |
1213 | char *name; | 1243 | char *name; |
1214 | 1244 | ||
1215 | dprintk(DEBUG_VFS, " %lu,%s,%s\n", dir->i_ino, dentry->d_name.name, | 1245 | dprintk(DEBUG_VFS, " %lu,%s,%s\n", dir->i_ino, dentry->d_name.name, |
1216 | old_dentry->d_name.name); | 1246 | old_dentry->d_name.name); |
1217 | 1247 | ||
1218 | oldfid = v9fs_fid_lookup(old_dentry); | 1248 | oldfid = v9fs_fid_clone(old_dentry); |
1219 | if (!oldfid) { | 1249 | if(IS_ERR(oldfid)) |
1220 | dprintk(DEBUG_ERROR, "can't find oldfid\n"); | 1250 | return PTR_ERR(oldfid); |
1221 | return -EPERM; | ||
1222 | } | ||
1223 | 1251 | ||
1224 | name = __getname(); | 1252 | name = __getname(); |
1225 | if (unlikely(!name)) | 1253 | if (unlikely(!name)) { |
1226 | return -ENOMEM; | 1254 | retval = -ENOMEM; |
1255 | goto clunk_fid; | ||
1256 | } | ||
1227 | 1257 | ||
1228 | sprintf(name, "%d\n", oldfid->fid); | 1258 | sprintf(name, "%d\n", oldfid->fid); |
1229 | retval = v9fs_vfs_mkspecial(dir, dentry, V9FS_DMLINK, name); | 1259 | retval = v9fs_vfs_mkspecial(dir, dentry, V9FS_DMLINK, name); |
1230 | __putname(name); | 1260 | __putname(name); |
1231 | 1261 | ||
1262 | clunk_fid: | ||
1263 | v9fs_fid_clunk(v9ses, oldfid); | ||
1232 | return retval; | 1264 | return retval; |
1233 | } | 1265 | } |
1234 | 1266 | ||