[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>
This commit is contained in:
parent
ff76e1dfc8
commit
da977b2c7e
4 changed files with 196 additions and 129 deletions
69
fs/9p/fid.c
69
fs/9p/fid.c
|
@ -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)
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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)
|
||||||
|
|
||||||
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 {
|
||||||
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 *);
|
||||||
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)
|
||||||
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)
|
||||||
|
|
||||||
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)
|
||||||
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)
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
|
|
||||||
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)
|
||||||
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)
|
||||||
|
|
||||||
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,
|
||||||
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,
|
||||||
|
|
||||||
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,
|
||||||
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,
|
||||||
|
|
||||||
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,
|
||||||
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,
|
||||||
|
|
||||||
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,
|
||||||
{
|
{
|
||||||
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,
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
||||||
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)
|
||||||
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)
|
||||||
|
|
||||||
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)
|
||||||
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)
|
||||||
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,
|
||||||
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,
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue