Commit da977b2c authored by Eric Van Hensbergen's avatar Eric Van Hensbergen Committed by Linus Torvalds

[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: default avatarEric Van Hensbergen <ericvh@gmail.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent ff76e1df
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/idr.h> #include <linux/idr.h>
#include <asm/semaphore.h>
#include "debug.h" #include "debug.h"
#include "v9fs.h" #include "v9fs.h"
...@@ -84,6 +85,7 @@ struct v9fs_fid *v9fs_fid_create(struct v9fs_session_info *v9ses, int fid) ...@@ -84,6 +85,7 @@ struct v9fs_fid *v9fs_fid_create(struct v9fs_session_info *v9ses, int fid)
new->iounit = 0; new->iounit = 0;
new->rdir_pos = 0; new->rdir_pos = 0;
new->rdir_fcall = NULL; new->rdir_fcall = NULL;
init_MUTEX(&new->lock);
INIT_LIST_HEAD(&new->list); INIT_LIST_HEAD(&new->list);
return new; return new;
...@@ -102,11 +104,11 @@ void v9fs_fid_destroy(struct v9fs_fid *fid) ...@@ -102,11 +104,11 @@ void v9fs_fid_destroy(struct v9fs_fid *fid)
} }
/** /**
* v9fs_fid_lookup - retrieve the right fid from a particular dentry * v9fs_fid_lookup - return a locked fid from a dentry
* @dentry: dentry to look for fid in * @dentry: dentry to look for fid in
* @type: intent of lookup (operation or traversal)
* *
* find a fid in the dentry * find a fid in the dentry, obtain its semaphore and return a reference to it.
* code calling lookup is responsible for releasing lock
* *
* TODO: only match fids that have the same uid as current user * TODO: only match fids that have the same uid as current user
* *
...@@ -124,7 +126,68 @@ struct v9fs_fid *v9fs_fid_lookup(struct dentry *dentry) ...@@ -124,7 +126,68 @@ struct v9fs_fid *v9fs_fid_lookup(struct dentry *dentry)
if (!return_fid) { if (!return_fid) {
dprintk(DEBUG_ERROR, "Couldn't find a fid in dentry\n"); dprintk(DEBUG_ERROR, "Couldn't find a fid in dentry\n");
return_fid = ERR_PTR(-EBADF);
} }
if(down_interruptible(&return_fid->lock))
return ERR_PTR(-EINTR);
return return_fid; return return_fid;
} }
/**
* v9fs_fid_clone - lookup the fid for a dentry, clone a private copy and release it
* @dentry: dentry to look for fid in
*
* find a fid in the dentry and then clone to a new private fid
*
* TODO: only match fids that have the same uid as current user
*
*/
struct v9fs_fid *v9fs_fid_clone(struct dentry *dentry)
{
struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dentry->d_inode);
struct v9fs_fid *base_fid, *new_fid = ERR_PTR(-EBADF);
struct v9fs_fcall *fcall = NULL;
int fid, err;
base_fid = v9fs_fid_lookup(dentry);
if(IS_ERR(base_fid))
return base_fid;
if(base_fid) { /* clone fid */
fid = v9fs_get_idpool(&v9ses->fidpool);
if (fid < 0) {
eprintk(KERN_WARNING, "newfid fails!\n");
new_fid = ERR_PTR(-ENOSPC);
goto Release_Fid;
}
err = v9fs_t_walk(v9ses, base_fid->fid, fid, NULL, &fcall);
if (err < 0) {
dprintk(DEBUG_ERROR, "clone walk didn't work\n");
v9fs_put_idpool(fid, &v9ses->fidpool);
new_fid = ERR_PTR(err);
goto Free_Fcall;
}
new_fid = v9fs_fid_create(v9ses, fid);
if (new_fid == NULL) {
dprintk(DEBUG_ERROR, "out of memory\n");
new_fid = ERR_PTR(-ENOMEM);
}
Free_Fcall:
kfree(fcall);
}
Release_Fid:
up(&base_fid->lock);
return new_fid;
}
void v9fs_fid_clunk(struct v9fs_session_info *v9ses, struct v9fs_fid *fid)
{
v9fs_t_clunk(v9ses, fid->fid);
v9fs_fid_destroy(fid);
}
...@@ -30,6 +30,8 @@ struct v9fs_fid { ...@@ -30,6 +30,8 @@ struct v9fs_fid {
struct list_head list; /* list of fids associated with a dentry */ struct list_head list; /* list of fids associated with a dentry */
struct list_head active; /* XXX - debug */ struct list_head active; /* XXX - debug */
struct semaphore lock;
u32 fid; u32 fid;
unsigned char fidopen; /* set when fid is opened */ unsigned char fidopen; /* set when fid is opened */
unsigned char fidclunked; /* set when fid has already been clunked */ unsigned char fidclunked; /* set when fid has already been clunked */
...@@ -55,3 +57,6 @@ struct v9fs_fid *v9fs_fid_get_created(struct dentry *); ...@@ -55,3 +57,6 @@ struct v9fs_fid *v9fs_fid_get_created(struct dentry *);
void v9fs_fid_destroy(struct v9fs_fid *fid); void v9fs_fid_destroy(struct v9fs_fid *fid);
struct v9fs_fid *v9fs_fid_create(struct v9fs_session_info *, int fid); struct v9fs_fid *v9fs_fid_create(struct v9fs_session_info *, int fid);
int v9fs_fid_insert(struct v9fs_fid *fid, struct dentry *dentry); int v9fs_fid_insert(struct v9fs_fid *fid, struct dentry *dentry);
struct v9fs_fid *v9fs_fid_clone(struct dentry *dentry);
void v9fs_fid_clunk(struct v9fs_session_info *v9ses, struct v9fs_fid *fid);
...@@ -55,53 +55,22 @@ int v9fs_file_open(struct inode *inode, struct file *file) ...@@ -55,53 +55,22 @@ int v9fs_file_open(struct inode *inode, struct file *file)
struct v9fs_fid *vfid; struct v9fs_fid *vfid;
struct v9fs_fcall *fcall = NULL; struct v9fs_fcall *fcall = NULL;
int omode; int omode;
int fid = V9FS_NOFID;
int err; int err;
dprintk(DEBUG_VFS, "inode: %p file: %p \n", inode, file); dprintk(DEBUG_VFS, "inode: %p file: %p \n", inode, file);
vfid = v9fs_fid_lookup(file->f_path.dentry); vfid = v9fs_fid_clone(file->f_path.dentry);
if (!vfid) { if (IS_ERR(vfid))
dprintk(DEBUG_ERROR, "Couldn't resolve fid from dentry\n"); return PTR_ERR(vfid);
return -EBADF;
}
fid = v9fs_get_idpool(&v9ses->fidpool);
if (fid < 0) {
eprintk(KERN_WARNING, "newfid fails!\n");
return -ENOSPC;
}
err = v9fs_t_walk(v9ses, vfid->fid, fid, NULL, &fcall);
if (err < 0) {
dprintk(DEBUG_ERROR, "rewalk didn't work\n");
if (fcall && fcall->id == RWALK)
goto clunk_fid;
else {
v9fs_put_idpool(fid, &v9ses->fidpool);
goto free_fcall;
}
}
kfree(fcall);
/* TODO: do special things for O_EXCL, O_NOFOLLOW, O_SYNC */
/* translate open mode appropriately */
omode = v9fs_uflags2omode(file->f_flags); omode = v9fs_uflags2omode(file->f_flags);
err = v9fs_t_open(v9ses, fid, omode, &fcall); err = v9fs_t_open(v9ses, vfid->fid, omode, &fcall);
if (err < 0) { if (err < 0) {
PRINT_FCALL_ERROR("open failed", fcall); PRINT_FCALL_ERROR("open failed", fcall);
goto clunk_fid; goto Clunk_Fid;
}
vfid = kmalloc(sizeof(struct v9fs_fid), GFP_KERNEL);
if (vfid == NULL) {
dprintk(DEBUG_ERROR, "out of memory\n");
err = -ENOMEM;
goto clunk_fid;
} }
file->private_data = vfid; file->private_data = vfid;
vfid->fid = fid;
vfid->fidopen = 1; vfid->fidopen = 1;
vfid->fidclunked = 0; vfid->fidclunked = 0;
vfid->iounit = fcall->params.ropen.iounit; vfid->iounit = fcall->params.ropen.iounit;
...@@ -112,10 +81,8 @@ int v9fs_file_open(struct inode *inode, struct file *file) ...@@ -112,10 +81,8 @@ int v9fs_file_open(struct inode *inode, struct file *file)
return 0; return 0;
clunk_fid: Clunk_Fid:
v9fs_t_clunk(v9ses, fid); v9fs_fid_clunk(v9ses, vfid);
free_fcall:
kfree(fcall); kfree(fcall);
return err; return err;
......
...@@ -416,12 +416,8 @@ static int v9fs_remove(struct inode *dir, struct dentry *file, int rmdir) ...@@ -416,12 +416,8 @@ static int v9fs_remove(struct inode *dir, struct dentry *file, int rmdir)
sb = file_inode->i_sb; sb = file_inode->i_sb;
v9ses = v9fs_inode2v9ses(file_inode); v9ses = v9fs_inode2v9ses(file_inode);
v9fid = v9fs_fid_lookup(file); v9fid = v9fs_fid_lookup(file);
if(IS_ERR(v9fid))
if (!v9fid) { return PTR_ERR(v9fid);
dprintk(DEBUG_ERROR,
"no v9fs_fid\n");
return -EBADF;
}
fid = v9fid->fid; fid = v9fid->fid;
if (fid < 0) { if (fid < 0) {
...@@ -433,11 +429,13 @@ static int v9fs_remove(struct inode *dir, struct dentry *file, int rmdir) ...@@ -433,11 +429,13 @@ static int v9fs_remove(struct inode *dir, struct dentry *file, int rmdir)
result = v9fs_t_remove(v9ses, fid, &fcall); result = v9fs_t_remove(v9ses, fid, &fcall);
if (result < 0) { if (result < 0) {
PRINT_FCALL_ERROR("remove fails", fcall); PRINT_FCALL_ERROR("remove fails", fcall);
goto Error;
} }
v9fs_put_idpool(fid, &v9ses->fidpool); v9fs_put_idpool(fid, &v9ses->fidpool);
v9fs_fid_destroy(v9fid); v9fs_fid_destroy(v9fid);
Error:
kfree(fcall); kfree(fcall);
return result; return result;
} }
...@@ -473,9 +471,13 @@ v9fs_vfs_create(struct inode *dir, struct dentry *dentry, int mode, ...@@ -473,9 +471,13 @@ v9fs_vfs_create(struct inode *dir, struct dentry *dentry, int mode,
inode = NULL; inode = NULL;
vfid = NULL; vfid = NULL;
v9ses = v9fs_inode2v9ses(dir); v9ses = v9fs_inode2v9ses(dir);
dfid = v9fs_fid_lookup(dentry->d_parent); dfid = v9fs_fid_clone(dentry->d_parent);
perm = unixmode2p9mode(v9ses, mode); if(IS_ERR(dfid)) {
err = PTR_ERR(dfid);
goto error;
}
perm = unixmode2p9mode(v9ses, mode);
if (nd && nd->flags & LOOKUP_OPEN) if (nd && nd->flags & LOOKUP_OPEN)
flags = nd->intent.open.flags - 1; flags = nd->intent.open.flags - 1;
else else
...@@ -485,9 +487,10 @@ v9fs_vfs_create(struct inode *dir, struct dentry *dentry, int mode, ...@@ -485,9 +487,10 @@ v9fs_vfs_create(struct inode *dir, struct dentry *dentry, int mode,
perm, v9fs_uflags2omode(flags), NULL, &fid, &qid, &iounit); perm, v9fs_uflags2omode(flags), NULL, &fid, &qid, &iounit);
if (err) if (err)
goto error; goto clunk_dfid;
vfid = v9fs_clone_walk(v9ses, dfid->fid, dentry); vfid = v9fs_clone_walk(v9ses, dfid->fid, dentry);
v9fs_fid_clunk(v9ses, dfid);
if (IS_ERR(vfid)) { if (IS_ERR(vfid)) {
err = PTR_ERR(vfid); err = PTR_ERR(vfid);
vfid = NULL; vfid = NULL;
...@@ -525,6 +528,9 @@ v9fs_vfs_create(struct inode *dir, struct dentry *dentry, int mode, ...@@ -525,6 +528,9 @@ v9fs_vfs_create(struct inode *dir, struct dentry *dentry, int mode,
return 0; return 0;
clunk_dfid:
v9fs_fid_clunk(v9ses, dfid);
error: error:
if (vfid) if (vfid)
v9fs_fid_destroy(vfid); v9fs_fid_destroy(vfid);
...@@ -551,7 +557,12 @@ static int v9fs_vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) ...@@ -551,7 +557,12 @@ static int v9fs_vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
inode = NULL; inode = NULL;
vfid = NULL; vfid = NULL;
v9ses = v9fs_inode2v9ses(dir); v9ses = v9fs_inode2v9ses(dir);
dfid = v9fs_fid_lookup(dentry->d_parent); dfid = v9fs_fid_clone(dentry->d_parent);
if(IS_ERR(dfid)) {
err = PTR_ERR(dfid);
goto error;
}
perm = unixmode2p9mode(v9ses, mode | S_IFDIR); perm = unixmode2p9mode(v9ses, mode | S_IFDIR);
err = v9fs_create(v9ses, dfid->fid, (char *) dentry->d_name.name, 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,37 +570,36 @@ static int v9fs_vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
if (err) { if (err) {
dprintk(DEBUG_ERROR, "create error %d\n", err); dprintk(DEBUG_ERROR, "create error %d\n", err);
goto error; goto clean_up_dfid;
}
err = v9fs_t_clunk(v9ses, fid);
if (err) {
dprintk(DEBUG_ERROR, "clunk error %d\n", err);
goto error;
} }
vfid = v9fs_clone_walk(v9ses, dfid->fid, dentry); vfid = v9fs_clone_walk(v9ses, dfid->fid, dentry);
if (IS_ERR(vfid)) { if (IS_ERR(vfid)) {
err = PTR_ERR(vfid); err = PTR_ERR(vfid);
vfid = NULL; vfid = NULL;
goto error; goto clean_up_dfid;
} }
v9fs_fid_clunk(v9ses, dfid);
inode = v9fs_inode_from_fid(v9ses, vfid->fid, dir->i_sb); inode = v9fs_inode_from_fid(v9ses, vfid->fid, dir->i_sb);
if (IS_ERR(inode)) { if (IS_ERR(inode)) {
err = PTR_ERR(inode); err = PTR_ERR(inode);
inode = NULL; inode = NULL;
goto error; goto clean_up_fids;
} }
dentry->d_op = &v9fs_dentry_operations; dentry->d_op = &v9fs_dentry_operations;
d_instantiate(dentry, inode); d_instantiate(dentry, inode);
return 0; return 0;
error: clean_up_fids:
if (vfid) if (vfid)
v9fs_fid_destroy(vfid); v9fs_fid_destroy(vfid);
clean_up_dfid:
v9fs_fid_clunk(v9ses, dfid);
error:
return err; return err;
} }
...@@ -622,28 +632,23 @@ static struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry, ...@@ -622,28 +632,23 @@ static struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry,
dentry->d_op = &v9fs_dentry_operations; dentry->d_op = &v9fs_dentry_operations;
dirfid = v9fs_fid_lookup(dentry->d_parent); dirfid = v9fs_fid_lookup(dentry->d_parent);
if (!dirfid) { if(IS_ERR(dirfid))
dprintk(DEBUG_ERROR, "no dirfid\n"); return ERR_PTR(PTR_ERR(dirfid));
return ERR_PTR(-EINVAL);
}
dirfidnum = dirfid->fid; dirfidnum = dirfid->fid;
if (dirfidnum < 0) {
dprintk(DEBUG_ERROR, "no dirfid for inode %p, #%lu\n",
dir, dir->i_ino);
return ERR_PTR(-EBADF);
}
newfid = v9fs_get_idpool(&v9ses->fidpool); newfid = v9fs_get_idpool(&v9ses->fidpool);
if (newfid < 0) { if (newfid < 0) {
eprintk(KERN_WARNING, "newfid fails!\n"); eprintk(KERN_WARNING, "newfid fails!\n");
return ERR_PTR(-ENOSPC); result = -ENOSPC;
goto Release_Dirfid;
} }
result = v9fs_t_walk(v9ses, dirfidnum, newfid, result = v9fs_t_walk(v9ses, dirfidnum, newfid,
(char *)dentry->d_name.name, &fcall); (char *)dentry->d_name.name, &fcall);
up(&dirfid->lock);
if (result < 0) { if (result < 0) {
if (fcall && fcall->id == RWALK) if (fcall && fcall->id == RWALK)
v9fs_t_clunk(v9ses, newfid); v9fs_t_clunk(v9ses, newfid);
...@@ -701,8 +706,12 @@ static struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry, ...@@ -701,8 +706,12 @@ static struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry,
return NULL; return NULL;
FreeFcall: Release_Dirfid:
up(&dirfid->lock);
FreeFcall:
kfree(fcall); kfree(fcall);
return ERR_PTR(result); return ERR_PTR(result);
} }
...@@ -746,10 +755,8 @@ v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -746,10 +755,8 @@ v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
struct inode *old_inode = old_dentry->d_inode; struct inode *old_inode = old_dentry->d_inode;
struct v9fs_session_info *v9ses = v9fs_inode2v9ses(old_inode); struct v9fs_session_info *v9ses = v9fs_inode2v9ses(old_inode);
struct v9fs_fid *oldfid = v9fs_fid_lookup(old_dentry); struct v9fs_fid *oldfid = v9fs_fid_lookup(old_dentry);
struct v9fs_fid *olddirfid = struct v9fs_fid *olddirfid;
v9fs_fid_lookup(old_dentry->d_parent); struct v9fs_fid *newdirfid;
struct v9fs_fid *newdirfid =
v9fs_fid_lookup(new_dentry->d_parent);
struct v9fs_wstat wstat; struct v9fs_wstat wstat;
struct v9fs_fcall *fcall = NULL; struct v9fs_fcall *fcall = NULL;
int fid = -1; int fid = -1;
...@@ -759,16 +766,26 @@ v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -759,16 +766,26 @@ v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
dprintk(DEBUG_VFS, "\n"); dprintk(DEBUG_VFS, "\n");
if ((!oldfid) || (!olddirfid) || (!newdirfid)) { if(IS_ERR(oldfid))
dprintk(DEBUG_ERROR, "problem with arguments\n"); return PTR_ERR(oldfid);
return -EBADF;
olddirfid = v9fs_fid_clone(old_dentry->d_parent);
if(IS_ERR(olddirfid)) {
retval = PTR_ERR(olddirfid);
goto Release_lock;
}
newdirfid = v9fs_fid_clone(new_dentry->d_parent);
if(IS_ERR(newdirfid)) {
retval = PTR_ERR(newdirfid);
goto Clunk_olddir;
} }
/* 9P can only handle file rename in the same directory */ /* 9P can only handle file rename in the same directory */
if (memcmp(&olddirfid->qid, &newdirfid->qid, sizeof(newdirfid->qid))) { if (memcmp(&olddirfid->qid, &newdirfid->qid, sizeof(newdirfid->qid))) {
dprintk(DEBUG_ERROR, "old dir and new dir are different\n"); dprintk(DEBUG_ERROR, "old dir and new dir are different\n");
retval = -EXDEV; retval = -EXDEV;
goto FreeFcallnBail; goto Clunk_newdir;
} }
fid = oldfid->fid; fid = oldfid->fid;
...@@ -779,7 +796,7 @@ v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -779,7 +796,7 @@ v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
dprintk(DEBUG_ERROR, "no fid for old file #%lu\n", dprintk(DEBUG_ERROR, "no fid for old file #%lu\n",
old_inode->i_ino); old_inode->i_ino);
retval = -EBADF; retval = -EBADF;
goto FreeFcallnBail; goto Clunk_newdir;
} }
v9fs_blank_wstat(&wstat); v9fs_blank_wstat(&wstat);
...@@ -788,11 +805,20 @@ v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -788,11 +805,20 @@ v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
retval = v9fs_t_wstat(v9ses, fid, &wstat, &fcall); retval = v9fs_t_wstat(v9ses, fid, &wstat, &fcall);
FreeFcallnBail:
if (retval < 0) if (retval < 0)
PRINT_FCALL_ERROR("wstat error", fcall); PRINT_FCALL_ERROR("wstat error", fcall);
kfree(fcall); kfree(fcall);
Clunk_newdir:
v9fs_fid_clunk(v9ses, newdirfid);
Clunk_olddir:
v9fs_fid_clunk(v9ses, olddirfid);
Release_lock:
up(&oldfid->lock);
return retval; return retval;
} }
...@@ -810,15 +836,12 @@ v9fs_vfs_getattr(struct vfsmount *mnt, struct dentry *dentry, ...@@ -810,15 +836,12 @@ v9fs_vfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
{ {
struct v9fs_fcall *fcall = NULL; struct v9fs_fcall *fcall = NULL;
struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dentry->d_inode); struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dentry->d_inode);
struct v9fs_fid *fid = v9fs_fid_lookup(dentry); struct v9fs_fid *fid = v9fs_fid_clone(dentry);
int err = -EPERM; int err = -EPERM;
dprintk(DEBUG_VFS, "dentry: %p\n", dentry); dprintk(DEBUG_VFS, "dentry: %p\n", dentry);
if (!fid) { if(IS_ERR(fid))
dprintk(DEBUG_ERROR, return PTR_ERR(fid);
"couldn't find fid associated with dentry\n");
return -EBADF;
}
err = v9fs_t_stat(v9ses, fid->fid, &fcall); err = v9fs_t_stat(v9ses, fid->fid, &fcall);
...@@ -831,6 +854,7 @@ v9fs_vfs_getattr(struct vfsmount *mnt, struct dentry *dentry, ...@@ -831,6 +854,7 @@ v9fs_vfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
} }
kfree(fcall); kfree(fcall);
v9fs_fid_clunk(v9ses, fid);
return err; return err;
} }
...@@ -844,18 +868,14 @@ v9fs_vfs_getattr(struct vfsmount *mnt, struct dentry *dentry, ...@@ -844,18 +868,14 @@ v9fs_vfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr) static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr)
{ {
struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dentry->d_inode); struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dentry->d_inode);
struct v9fs_fid *fid = v9fs_fid_lookup(dentry); struct v9fs_fid *fid = v9fs_fid_clone(dentry);
struct v9fs_fcall *fcall = NULL; struct v9fs_fcall *fcall = NULL;
struct v9fs_wstat wstat; struct v9fs_wstat wstat;
int res = -EPERM; int res = -EPERM;
dprintk(DEBUG_VFS, "\n"); dprintk(DEBUG_VFS, "\n");
if(IS_ERR(fid))
if (!fid) { return PTR_ERR(fid);
dprintk(DEBUG_ERROR,
"Couldn't find fid associated with dentry\n");
return -EBADF;
}
v9fs_blank_wstat(&wstat); v9fs_blank_wstat(&wstat);
if (iattr->ia_valid & ATTR_MODE) if (iattr->ia_valid & ATTR_MODE)
...@@ -887,6 +907,7 @@ static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr) ...@@ -887,6 +907,7 @@ static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr)
if (res >= 0) if (res >= 0)
res = inode_setattr(dentry->d_inode, iattr); res = inode_setattr(dentry->d_inode, iattr);
v9fs_fid_clunk(v9ses, fid);
return res; return res;
} }
...@@ -987,18 +1008,15 @@ static int v9fs_readlink(struct dentry *dentry, char *buffer, int buflen) ...@@ -987,18 +1008,15 @@ static int v9fs_readlink(struct dentry *dentry, char *buffer, int buflen)
struct v9fs_fcall *fcall = NULL; struct v9fs_fcall *fcall = NULL;
struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dentry->d_inode); struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dentry->d_inode);
struct v9fs_fid *fid = v9fs_fid_lookup(dentry); struct v9fs_fid *fid = v9fs_fid_clone(dentry);
if (!fid) { if(IS_ERR(fid))
dprintk(DEBUG_ERROR, "could not resolve fid from dentry\n"); return PTR_ERR(fid);
retval = -EBADF;
goto FreeFcall;
}
if (!v9ses->extended) { if (!v9ses->extended) {
retval = -EBADF; retval = -EBADF;
dprintk(DEBUG_ERROR, "not extended\n"); dprintk(DEBUG_ERROR, "not extended\n");
goto FreeFcall; goto ClunkFid;
} }
dprintk(DEBUG_VFS, " %s\n", dentry->d_name.name); 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,8 +1027,10 @@ static int v9fs_readlink(struct dentry *dentry, char *buffer, int buflen)
goto FreeFcall; goto FreeFcall;
} }
if (!fcall) if (!fcall) {
return -EIO; retval = -EIO;
goto ClunkFid;
}
if (!(fcall->params.rstat.stat.mode & V9FS_DMSYMLINK)) { if (!(fcall->params.rstat.stat.mode & V9FS_DMSYMLINK)) {
retval = -EINVAL; retval = -EINVAL;
...@@ -1028,9 +1048,12 @@ static int v9fs_readlink(struct dentry *dentry, char *buffer, int buflen) ...@@ -1028,9 +1048,12 @@ static int v9fs_readlink(struct dentry *dentry, char *buffer, int buflen)
fcall->params.rstat.stat.extension.str, buffer); fcall->params.rstat.stat.extension.str, buffer);
retval = buflen; retval = buflen;
FreeFcall: FreeFcall:
kfree(fcall); kfree(fcall);
ClunkFid:
v9fs_fid_clunk(v9ses, fid);
return retval; return retval;
} }
...@@ -1123,52 +1146,58 @@ static int v9fs_vfs_mkspecial(struct inode *dir, struct dentry *dentry, ...@@ -1123,52 +1146,58 @@ static int v9fs_vfs_mkspecial(struct inode *dir, struct dentry *dentry,
int err; int err;
u32 fid, perm; u32 fid, perm;
struct v9fs_session_info *v9ses; struct v9fs_session_info *v9ses;
struct v9fs_fid *dfid, *vfid; struct v9fs_fid *dfid, *vfid = NULL;
struct inode *inode; struct inode *inode = NULL;
inode = NULL;
vfid = NULL;
v9ses = v9fs_inode2v9ses(dir); v9ses = v9fs_inode2v9ses(dir);
dfid = v9fs_fid_lookup(dentry->d_parent);
perm = unixmode2p9mode(v9ses, mode);
if (!v9ses->extended) { if (!v9ses->extended) {
dprintk(DEBUG_ERROR, "not extended\n"); dprintk(DEBUG_ERROR, "not extended\n");
return -EPERM; return -EPERM;
} }
dfid = v9fs_fid_clone(dentry->d_parent);
if(IS_ERR(dfid)) {
err = PTR_ERR(dfid);
goto error;
}
perm = unixmode2p9mode(v9ses, mode);
err = v9fs_create(v9ses, dfid->fid, (char *) dentry->d_name.name, err = v9fs_create(v9ses, dfid->fid, (char *) dentry->d_name.name,
perm, V9FS_OREAD, (char *) extension, &fid, NULL, NULL); perm, V9FS_OREAD, (char *) extension, &fid, NULL, NULL);
if (err) if (err)
goto error; goto clunk_dfid;
err = v9fs_t_clunk(v9ses, fid); err = v9fs_t_clunk(v9ses, fid);
if (err) if (err)
goto error; goto clunk_dfid;
vfid = v9fs_clone_walk(v9ses, dfid->fid, dentry); vfid = v9fs_clone_walk(v9ses, dfid->fid, dentry);
if (IS_ERR(vfid)) { if (IS_ERR(vfid)) {
err = PTR_ERR(vfid); err = PTR_ERR(vfid);
vfid = NULL; vfid = NULL;
goto error; goto clunk_dfid;
} }
inode = v9fs_inode_from_fid(v9ses, vfid->fid, dir->i_sb); inode = v9fs_inode_from_fid(v9ses, vfid->fid, dir->i_sb);
if (IS_ERR(inode)) { if (IS_ERR(inode)) {
err = PTR_ERR(inode); err = PTR_ERR(inode);
inode = NULL; inode = NULL;
goto error; goto free_vfid;
} }
dentry->d_op = &v9fs_dentry_operations; dentry->d_op = &v9fs_dentry_operations;
d_instantiate(dentry, inode); d_instantiate(dentry, inode);
return 0; return 0;
error: free_vfid:
if (vfid) v9fs_fid_destroy(vfid);
v9fs_fid_destroy(vfid);
clunk_dfid:
v9fs_fid_clunk(v9ses, dfid);
error:
return err; return err;
} }
...@@ -1209,26 +1238,29 @@ v9fs_vfs_link(struct dentry *old_dentry, struct inode *dir, ...@@ -1209,26 +1238,29 @@ v9fs_vfs_link(struct dentry *old_dentry, struct inode *dir,
struct dentry *dentry) struct dentry *dentry)
{ {
int retval; int retval;
struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dir);
struct v9fs_fid *oldfid; struct v9fs_fid *oldfid;
char *name; char *name;
dprintk(DEBUG_VFS, " %lu,%s,%s\n", dir->i_ino, dentry->d_name.name, dprintk(DEBUG_VFS, " %lu,%s,%s\n", dir->i_ino, dentry->d_name.name,
old_dentry->d_name.name); old_dentry->d_name.name);
oldfid = v9fs_fid_lookup(old_dentry); oldfid = v9fs_fid_clone(old_dentry);
if (!oldfid) { if(IS_ERR(oldfid))
dprintk(DEBUG_ERROR, "can't find oldfid\n"); return PTR_ERR(oldfid);
return -EPERM;
}
name = __getname(); name = __getname();
if (unlikely(!name)) if (unlikely(!name)) {
return -ENOMEM; retval = -ENOMEM;
goto clunk_fid;
}
sprintf(name, "%d\n", oldfid->fid); sprintf(name, "%d\n", oldfid->fid);
retval = v9fs_vfs_mkspecial(dir, dentry, V9FS_DMLINK, name); retval = v9fs_vfs_mkspecial(dir, dentry, V9FS_DMLINK, name);
__putname(name); __putname(name);
clunk_fid:
v9fs_fid_clunk(v9ses, oldfid);
return retval; return retval;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment