[PATCH] v9fs: zero copy implementation

Performance enhancement reducing the number of copies in the data and
stat paths.

Signed-off-by: Latchesar Ionkov <lucho@ionkov.net>
Cc: Eric Van Hensbergen <ericvh@ericvh.myip.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
Latchesar Ionkov
2006-01-08 01:05:00 -08:00
committed by Linus Torvalds
parent d8da097afb
commit 531b1094b7
18 changed files with 1079 additions and 1043 deletions

View File

@@ -40,7 +40,6 @@
#include "v9fs.h"
#include "9p.h"
#include "v9fs_vfs.h"
#include "conv.h"
#include "fid.h"
static struct inode_operations v9fs_dir_inode_operations;
@@ -127,100 +126,32 @@ static int p9mode2unixmode(struct v9fs_session_info *v9ses, int mode)
}
/**
* v9fs_blank_mistat - helper function to setup a 9P stat structure
* v9fs_blank_wstat - helper function to setup a 9P stat structure
* @v9ses: 9P session info (for determining extended mode)
* @mistat: structure to initialize
* @wstat: structure to initialize
*
*/
static void
v9fs_blank_mistat(struct v9fs_session_info *v9ses, struct v9fs_stat *mistat)
v9fs_blank_wstat(struct v9fs_wstat *wstat)
{
mistat->type = ~0;
mistat->dev = ~0;
mistat->qid.type = ~0;
mistat->qid.version = ~0;
*((long long *)&mistat->qid.path) = ~0;
mistat->mode = ~0;
mistat->atime = ~0;
mistat->mtime = ~0;
mistat->length = ~0;
mistat->name = mistat->data;
mistat->uid = mistat->data;
mistat->gid = mistat->data;
mistat->muid = mistat->data;
if (v9ses->extended) {
mistat->n_uid = ~0;
mistat->n_gid = ~0;
mistat->n_muid = ~0;
mistat->extension = mistat->data;
}
*mistat->data = 0;
}
/**
* v9fs_mistat2unix - convert mistat to unix stat
* @mistat: Plan 9 metadata (mistat) structure
* @buf: unix metadata (stat) structure to populate
* @sb: superblock
*
*/
static void
v9fs_mistat2unix(struct v9fs_stat *mistat, struct stat *buf,
struct super_block *sb)
{
struct v9fs_session_info *v9ses = sb ? sb->s_fs_info : NULL;
buf->st_nlink = 1;
buf->st_atime = mistat->atime;
buf->st_mtime = mistat->mtime;
buf->st_ctime = mistat->mtime;
buf->st_uid = (unsigned short)-1;
buf->st_gid = (unsigned short)-1;
if (v9ses && v9ses->extended) {
/* TODO: string to uid mapping via user-space daemon */
if (mistat->n_uid != -1)
sscanf(mistat->uid, "%x", (unsigned int *)&buf->st_uid);
if (mistat->n_gid != -1)
sscanf(mistat->gid, "%x", (unsigned int *)&buf->st_gid);
}
if (buf->st_uid == (unsigned short)-1)
buf->st_uid = v9ses->uid;
if (buf->st_gid == (unsigned short)-1)
buf->st_gid = v9ses->gid;
buf->st_mode = p9mode2unixmode(v9ses, mistat->mode);
if ((S_ISBLK(buf->st_mode)) || (S_ISCHR(buf->st_mode))) {
char type = 0;
int major = -1;
int minor = -1;
sscanf(mistat->extension, "%c %u %u", &type, &major, &minor);
switch (type) {
case 'c':
buf->st_mode &= ~S_IFBLK;
buf->st_mode |= S_IFCHR;
break;
case 'b':
break;
default:
dprintk(DEBUG_ERROR, "Unknown special type %c (%s)\n",
type, mistat->extension);
};
buf->st_rdev = MKDEV(major, minor);
} else
buf->st_rdev = 0;
buf->st_size = mistat->length;
buf->st_blksize = sb->s_blocksize;
buf->st_blocks =
(buf->st_size + buf->st_blksize - 1) >> sb->s_blocksize_bits;
wstat->type = ~0;
wstat->dev = ~0;
wstat->qid.type = ~0;
wstat->qid.version = ~0;
*((long long *)&wstat->qid.path) = ~0;
wstat->mode = ~0;
wstat->atime = ~0;
wstat->mtime = ~0;
wstat->length = ~0;
wstat->name = NULL;
wstat->uid = NULL;
wstat->gid = NULL;
wstat->muid = NULL;
wstat->n_uid = ~0;
wstat->n_gid = ~0;
wstat->n_muid = ~0;
wstat->extension = NULL;
}
/**
@@ -312,7 +243,6 @@ v9fs_create(struct inode *dir,
struct inode *file_inode = NULL;
struct v9fs_fcall *fcall = NULL;
struct v9fs_qid qid;
struct stat newstat;
int dirfidnum = -1;
long newfid = -1;
int result = 0;
@@ -350,7 +280,7 @@ v9fs_create(struct inode *dir,
result = v9fs_t_walk(v9ses, dirfidnum, newfid, NULL, &fcall);
if (result < 0) {
dprintk(DEBUG_ERROR, "clone error: %s\n", FCALL_ERROR(fcall));
PRINT_FCALL_ERROR("clone error", fcall);
v9fs_put_idpool(newfid, &v9ses->fidpool);
newfid = -1;
goto CleanUpFid;
@@ -362,9 +292,7 @@ v9fs_create(struct inode *dir,
result = v9fs_t_create(v9ses, newfid, (char *)file_dentry->d_name.name,
perm, open_mode, &fcall);
if (result < 0) {
dprintk(DEBUG_ERROR, "create fails: %s(%d)\n",
FCALL_ERROR(fcall), result);
PRINT_FCALL_ERROR("create fails", fcall);
goto CleanUpFid;
}
@@ -400,7 +328,7 @@ v9fs_create(struct inode *dir,
result = v9fs_t_walk(v9ses, dirfidnum, wfidno,
(char *) file_dentry->d_name.name, &fcall);
if (result < 0) {
dprintk(DEBUG_ERROR, "clone error: %s\n", FCALL_ERROR(fcall));
PRINT_FCALL_ERROR("clone error", fcall);
v9fs_put_idpool(wfidno, &v9ses->fidpool);
wfidno = -1;
goto CleanUpFid;
@@ -421,21 +349,21 @@ v9fs_create(struct inode *dir,
result = v9fs_t_stat(v9ses, wfidno, &fcall);
if (result < 0) {
dprintk(DEBUG_ERROR, "stat error: %s(%d)\n", FCALL_ERROR(fcall),
result);
PRINT_FCALL_ERROR("stat error", fcall);
goto CleanUpFid;
}
v9fs_mistat2unix(fcall->params.rstat.stat, &newstat, sb);
file_inode = v9fs_get_inode(sb, newstat.st_mode);
file_inode = v9fs_get_inode(sb,
p9mode2unixmode(v9ses, fcall->params.rstat.stat.mode));
if ((!file_inode) || IS_ERR(file_inode)) {
dprintk(DEBUG_ERROR, "create inode failed\n");
result = -EBADF;
goto CleanUpFid;
}
v9fs_mistat2inode(fcall->params.rstat.stat, file_inode, sb);
v9fs_stat2inode(&fcall->params.rstat.stat, file_inode, sb);
kfree(fcall);
fcall = NULL;
file_dentry->d_op = &v9fs_dentry_operations;
@@ -500,10 +428,9 @@ static int v9fs_remove(struct inode *dir, struct dentry *file, int rmdir)
}
result = v9fs_t_remove(v9ses, fid, &fcall);
if (result < 0)
dprintk(DEBUG_ERROR, "remove of file fails: %s(%d)\n",
FCALL_ERROR(fcall), result);
else {
if (result < 0) {
PRINT_FCALL_ERROR("remove fails", fcall);
} else {
v9fs_put_idpool(fid, &v9ses->fidpool);
v9fs_fid_destroy(v9fid);
}
@@ -558,7 +485,6 @@ static struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry,
struct v9fs_fid *fid;
struct inode *inode;
struct v9fs_fcall *fcall = NULL;
struct stat newstat;
int dirfidnum = -1;
int newfid = -1;
int result = 0;
@@ -611,8 +537,8 @@ static struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry,
goto FreeFcall;
}
v9fs_mistat2unix(fcall->params.rstat.stat, &newstat, sb);
inode = v9fs_get_inode(sb, newstat.st_mode);
inode = v9fs_get_inode(sb, p9mode2unixmode(v9ses,
fcall->params.rstat.stat.mode));
if (IS_ERR(inode) && (PTR_ERR(inode) == -ENOSPC)) {
eprintk(KERN_WARNING, "inode alloc failes, returns %ld\n",
@@ -622,7 +548,7 @@ static struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry,
goto FreeFcall;
}
inode->i_ino = v9fs_qid2ino(&fcall->params.rstat.stat->qid);
inode->i_ino = v9fs_qid2ino(&fcall->params.rstat.stat.qid);
fid = v9fs_fid_create(dentry, v9ses, newfid, 0);
if (fid == NULL) {
@@ -631,10 +557,10 @@ static struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry,
goto FreeFcall;
}
fid->qid = fcall->params.rstat.stat->qid;
fid->qid = fcall->params.rstat.stat.qid;
dentry->d_op = &v9fs_dentry_operations;
v9fs_mistat2inode(fcall->params.rstat.stat, inode, inode->i_sb);
v9fs_stat2inode(&fcall->params.rstat.stat, inode, inode->i_sb);
d_add(dentry, inode);
kfree(fcall);
@@ -690,7 +616,7 @@ v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
v9fs_fid_lookup(old_dentry->d_parent);
struct v9fs_fid *newdirfid =
v9fs_fid_lookup(new_dentry->d_parent);
struct v9fs_stat *mistat = kmalloc(v9ses->maxdata, GFP_KERNEL);
struct v9fs_wstat wstat;
struct v9fs_fcall *fcall = NULL;
int fid = -1;
int olddirfidnum = -1;
@@ -699,9 +625,6 @@ v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
dprintk(DEBUG_VFS, "\n");
if (!mistat)
return -ENOMEM;
if ((!oldfid) || (!olddirfid) || (!newdirfid)) {
dprintk(DEBUG_ERROR, "problem with arguments\n");
return -EBADF;
@@ -725,26 +648,15 @@ v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
goto FreeFcallnBail;
}
v9fs_blank_mistat(v9ses, mistat);
v9fs_blank_wstat(&wstat);
wstat.muid = v9ses->name;
wstat.name = (char *) new_dentry->d_name.name;
strcpy(mistat->data + 1, v9ses->name);
mistat->name = mistat->data + 1 + strlen(v9ses->name);
if (new_dentry->d_name.len >
(v9ses->maxdata - strlen(v9ses->name) - sizeof(struct v9fs_stat))) {
dprintk(DEBUG_ERROR, "new name too long\n");
goto FreeFcallnBail;
}
strcpy(mistat->name, new_dentry->d_name.name);
retval = v9fs_t_wstat(v9ses, fid, mistat, &fcall);
retval = v9fs_t_wstat(v9ses, fid, &wstat, &fcall);
FreeFcallnBail:
kfree(mistat);
if (retval < 0)
dprintk(DEBUG_ERROR, "v9fs_t_wstat error: %s\n",
FCALL_ERROR(fcall));
PRINT_FCALL_ERROR("wstat error", fcall);
kfree(fcall);
return retval;
@@ -779,7 +691,7 @@ v9fs_vfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
if (err < 0)
dprintk(DEBUG_ERROR, "stat error\n");
else {
v9fs_mistat2inode(fcall->params.rstat.stat, dentry->d_inode,
v9fs_stat2inode(&fcall->params.rstat.stat, dentry->d_inode,
dentry->d_inode->i_sb);
generic_fillattr(dentry->d_inode, stat);
}
@@ -800,57 +712,44 @@ static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr)
struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dentry->d_inode);
struct v9fs_fid *fid = v9fs_fid_lookup(dentry);
struct v9fs_fcall *fcall = NULL;
struct v9fs_stat *mistat = kmalloc(v9ses->maxdata, GFP_KERNEL);
struct v9fs_wstat wstat;
int res = -EPERM;
dprintk(DEBUG_VFS, "\n");
if (!mistat)
return -ENOMEM;
if (!fid) {
dprintk(DEBUG_ERROR,
"Couldn't find fid associated with dentry\n");
return -EBADF;
}
v9fs_blank_mistat(v9ses, mistat);
v9fs_blank_wstat(&wstat);
if (iattr->ia_valid & ATTR_MODE)
mistat->mode = unixmode2p9mode(v9ses, iattr->ia_mode);
wstat.mode = unixmode2p9mode(v9ses, iattr->ia_mode);
if (iattr->ia_valid & ATTR_MTIME)
mistat->mtime = iattr->ia_mtime.tv_sec;
wstat.mtime = iattr->ia_mtime.tv_sec;
if (iattr->ia_valid & ATTR_ATIME)
mistat->atime = iattr->ia_atime.tv_sec;
wstat.atime = iattr->ia_atime.tv_sec;
if (iattr->ia_valid & ATTR_SIZE)
mistat->length = iattr->ia_size;
wstat.length = iattr->ia_size;
if (v9ses->extended) {
char *ptr = mistat->data+1;
if (iattr->ia_valid & ATTR_UID)
wstat.n_uid = iattr->ia_uid;
if (iattr->ia_valid & ATTR_UID) {
mistat->uid = ptr;
ptr += 1+sprintf(ptr, "%08x", iattr->ia_uid);
mistat->n_uid = iattr->ia_uid;
}
if (iattr->ia_valid & ATTR_GID) {
mistat->gid = ptr;
ptr += 1+sprintf(ptr, "%08x", iattr->ia_gid);
mistat->n_gid = iattr->ia_gid;
}
if (iattr->ia_valid & ATTR_GID)
wstat.n_gid = iattr->ia_gid;
}
res = v9fs_t_wstat(v9ses, fid->fid, mistat, &fcall);
res = v9fs_t_wstat(v9ses, fid->fid, &wstat, &fcall);
if (res < 0)
dprintk(DEBUG_ERROR, "wstat error: %s\n", FCALL_ERROR(fcall));
PRINT_FCALL_ERROR("wstat error", fcall);
kfree(mistat);
kfree(fcall);
if (res >= 0)
res = inode_setattr(dentry->d_inode, iattr);
@@ -858,51 +757,42 @@ static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr)
}
/**
* v9fs_mistat2inode - populate an inode structure with mistat info
* @mistat: Plan 9 metadata (mistat) structure
* v9fs_stat2inode - populate an inode structure with mistat info
* @stat: Plan 9 metadata (mistat) structure
* @inode: inode to populate
* @sb: superblock of filesystem
*
*/
void
v9fs_mistat2inode(struct v9fs_stat *mistat, struct inode *inode,
struct super_block *sb)
v9fs_stat2inode(struct v9fs_stat *stat, struct inode *inode,
struct super_block *sb)
{
char ext[32];
struct v9fs_session_info *v9ses = sb->s_fs_info;
inode->i_nlink = 1;
inode->i_atime.tv_sec = mistat->atime;
inode->i_mtime.tv_sec = mistat->mtime;
inode->i_ctime.tv_sec = mistat->mtime;
inode->i_atime.tv_sec = stat->atime;
inode->i_mtime.tv_sec = stat->mtime;
inode->i_ctime.tv_sec = stat->mtime;
inode->i_uid = -1;
inode->i_gid = -1;
inode->i_uid = v9ses->uid;
inode->i_gid = v9ses->gid;
if (v9ses->extended) {
/* TODO: string to uid mapping via user-space daemon */
inode->i_uid = mistat->n_uid;
inode->i_gid = mistat->n_gid;
if (mistat->n_uid == -1)
sscanf(mistat->uid, "%x", &inode->i_uid);
if (mistat->n_gid == -1)
sscanf(mistat->gid, "%x", &inode->i_gid);
inode->i_uid = stat->n_uid;
inode->i_gid = stat->n_gid;
}
if (inode->i_uid == -1)
inode->i_uid = v9ses->uid;
if (inode->i_gid == -1)
inode->i_gid = v9ses->gid;
inode->i_mode = p9mode2unixmode(v9ses, mistat->mode);
inode->i_mode = p9mode2unixmode(v9ses, stat->mode);
if ((S_ISBLK(inode->i_mode)) || (S_ISCHR(inode->i_mode))) {
char type = 0;
int major = -1;
int minor = -1;
sscanf(mistat->extension, "%c %u %u", &type, &major, &minor);
v9fs_str_copy(ext, sizeof(ext), &stat->extension);
sscanf(ext, "%c %u %u", &type, &major, &minor);
switch (type) {
case 'c':
inode->i_mode &= ~S_IFBLK;
@@ -911,14 +801,14 @@ v9fs_mistat2inode(struct v9fs_stat *mistat, struct inode *inode,
case 'b':
break;
default:
dprintk(DEBUG_ERROR, "Unknown special type %c (%s)\n",
type, mistat->extension);
dprintk(DEBUG_ERROR, "Unknown special type %c (%.*s)\n",
type, stat->extension.len, stat->extension.str);
};
inode->i_rdev = MKDEV(major, minor);
} else
inode->i_rdev = 0;
inode->i_size = mistat->length;
inode->i_size = stat->length;
inode->i_blksize = sb->s_blocksize;
inode->i_blocks =
@@ -945,72 +835,6 @@ ino_t v9fs_qid2ino(struct v9fs_qid *qid)
return i;
}
/**
* v9fs_vfs_symlink - helper function to create symlinks
* @dir: directory inode containing symlink
* @dentry: dentry for symlink
* @symname: symlink data
*
* See 9P2000.u RFC for more information
*
*/
static int
v9fs_vfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
{
int retval = -EPERM;
struct v9fs_fid *newfid;
struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dir);
struct v9fs_fcall *fcall = NULL;
struct v9fs_stat *mistat = kmalloc(v9ses->maxdata, GFP_KERNEL);
int err;
dprintk(DEBUG_VFS, " %lu,%s,%s\n", dir->i_ino, dentry->d_name.name,
symname);
if (!mistat)
return -ENOMEM;
if (!v9ses->extended) {
dprintk(DEBUG_ERROR, "not extended\n");
goto FreeFcall;
}
/* issue a create */
retval = v9fs_create(dir, dentry, S_IFLNK, 0);
if (retval != 0)
goto FreeFcall;
newfid = v9fs_fid_lookup(dentry);
/* issue a twstat */
v9fs_blank_mistat(v9ses, mistat);
strcpy(mistat->data + 1, symname);
mistat->extension = mistat->data + 1;
retval = v9fs_t_wstat(v9ses, newfid->fid, mistat, &fcall);
if (retval < 0) {
dprintk(DEBUG_ERROR, "v9fs_t_wstat error: %s\n",
FCALL_ERROR(fcall));
goto FreeFcall;
}
kfree(fcall);
err = v9fs_t_clunk(v9ses, newfid->fid);
if (err < 0) {
dprintk(DEBUG_ERROR, "clunk for symlink failed: %d\n", err);
goto FreeFcall;
}
d_drop(dentry); /* FID - will this also clunk? */
FreeFcall:
kfree(mistat);
kfree(fcall);
return retval;
}
/**
* v9fs_readlink - read a symlink's location (internal version)
* @dentry: dentry for symlink
@@ -1050,16 +874,17 @@ static int v9fs_readlink(struct dentry *dentry, char *buffer, int buflen)
if (!fcall)
return -EIO;
if (!(fcall->params.rstat.stat->mode & V9FS_DMSYMLINK)) {
if (!(fcall->params.rstat.stat.mode & V9FS_DMSYMLINK)) {
retval = -EINVAL;
goto FreeFcall;
}
/* copy extension buffer into buffer */
if (strlen(fcall->params.rstat.stat->extension) < buflen)
buflen = strlen(fcall->params.rstat.stat->extension);
if (fcall->params.rstat.stat.extension.len < buflen)
buflen = fcall->params.rstat.stat.extension.len;
memcpy(buffer, fcall->params.rstat.stat->extension, buflen + 1);
memcpy(buffer, fcall->params.rstat.stat.extension.str, buflen - 1);
buffer[buflen-1] = 0;
retval = buflen;
@@ -1149,6 +974,77 @@ static void v9fs_vfs_put_link(struct dentry *dentry, struct nameidata *nd, void
__putname(s);
}
static int v9fs_vfs_mkspecial(struct inode *dir, struct dentry *dentry,
int mode, const char *extension)
{
int err, retval;
struct v9fs_session_info *v9ses;
struct v9fs_fcall *fcall;
struct v9fs_fid *fid;
struct v9fs_wstat wstat;
v9ses = v9fs_inode2v9ses(dir);
retval = -EPERM;
fcall = NULL;
if (!v9ses->extended) {
dprintk(DEBUG_ERROR, "not extended\n");
goto free_mem;
}
/* issue a create */
retval = v9fs_create(dir, dentry, mode, 0);
if (retval != 0)
goto free_mem;
fid = v9fs_fid_get_created(dentry);
if (!fid) {
dprintk(DEBUG_ERROR, "couldn't resolve fid from dentry\n");
goto free_mem;
}
/* issue a Twstat */
v9fs_blank_wstat(&wstat);
wstat.muid = v9ses->name;
wstat.extension = (char *) extension;
retval = v9fs_t_wstat(v9ses, fid->fid, &wstat, &fcall);
if (retval < 0) {
PRINT_FCALL_ERROR("wstat error", fcall);
goto free_mem;
}
err = v9fs_t_clunk(v9ses, fid->fid);
if (err < 0) {
dprintk(DEBUG_ERROR, "clunk failed: %d\n", err);
goto free_mem;
}
d_drop(dentry); /* FID - will this also clunk? */
free_mem:
kfree(fcall);
return retval;
}
/**
* v9fs_vfs_symlink - helper function to create symlinks
* @dir: directory inode containing symlink
* @dentry: dentry for symlink
* @symname: symlink data
*
* See 9P2000.u RFC for more information
*
*/
static int
v9fs_vfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
{
dprintk(DEBUG_VFS, " %lu,%s,%s\n", dir->i_ino, dentry->d_name.name,
symname);
return v9fs_vfs_mkspecial(dir, dentry, S_IFLNK, symname);
}
/**
* v9fs_vfs_link - create a hardlink
* @old_dentry: dentry for file to link to
@@ -1165,66 +1061,24 @@ static int
v9fs_vfs_link(struct dentry *old_dentry, struct inode *dir,
struct dentry *dentry)
{
int retval = -EPERM;
struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dir);
struct v9fs_fcall *fcall = NULL;
struct v9fs_stat *mistat = kmalloc(v9ses->maxdata, GFP_KERNEL);
struct v9fs_fid *oldfid = v9fs_fid_lookup(old_dentry);
struct v9fs_fid *newfid = NULL;
char *symname = __getname();
int err;
int retval;
struct v9fs_fid *oldfid;
char *name;
dprintk(DEBUG_VFS, " %lu,%s,%s\n", dir->i_ino, dentry->d_name.name,
old_dentry->d_name.name);
if (!v9ses->extended) {
dprintk(DEBUG_ERROR, "not extended\n");
goto FreeMem;
oldfid = v9fs_fid_lookup(old_dentry);
if (!oldfid) {
dprintk(DEBUG_ERROR, "can't find oldfid\n");
return -EPERM;
}
/* get fid of old_dentry */
sprintf(symname, "hardlink(%d)\n", oldfid->fid);
name = __getname();
sprintf(name, "hardlink(%d)\n", oldfid->fid);
retval = v9fs_vfs_mkspecial(dir, dentry, V9FS_DMLINK, name);
__putname(name);
/* issue a create */
retval = v9fs_create(dir, dentry, V9FS_DMLINK, 0);
if (retval != 0)
goto FreeMem;
newfid = v9fs_fid_lookup(dentry);
if (!newfid) {
dprintk(DEBUG_ERROR, "couldn't resolve fid from dentry\n");
goto FreeMem;
}
/* issue a twstat */
v9fs_blank_mistat(v9ses, mistat);
strcpy(mistat->data + 1, symname);
mistat->extension = mistat->data + 1;
retval = v9fs_t_wstat(v9ses, newfid->fid, mistat, &fcall);
if (retval < 0) {
dprintk(DEBUG_ERROR, "v9fs_t_wstat error: %s\n",
FCALL_ERROR(fcall));
goto FreeMem;
}
kfree(fcall);
err = v9fs_t_clunk(v9ses, newfid->fid);
if (err < 0) {
dprintk(DEBUG_ERROR, "clunk for symlink failed: %d\n", err);
goto FreeMem;
}
d_drop(dentry); /* FID - will this also clunk? */
kfree(fcall);
fcall = NULL;
FreeMem:
kfree(mistat);
kfree(fcall);
__putname(symname);
return retval;
}
@@ -1240,83 +1094,30 @@ v9fs_vfs_link(struct dentry *old_dentry, struct inode *dir,
static int
v9fs_vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev)
{
int retval = -EPERM;
struct v9fs_fid *newfid;
struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dir);
struct v9fs_fcall *fcall = NULL;
struct v9fs_stat *mistat = kmalloc(v9ses->maxdata, GFP_KERNEL);
char *symname = __getname();
int err;
int retval;
char *name;
dprintk(DEBUG_VFS, " %lu,%s mode: %x MAJOR: %u MINOR: %u\n", dir->i_ino,
dentry->d_name.name, mode, MAJOR(rdev), MINOR(rdev));
if (!mistat)
return -ENOMEM;
if (!new_valid_dev(rdev)) {
retval = -EINVAL;
goto FreeMem;
}
if (!v9ses->extended) {
dprintk(DEBUG_ERROR, "not extended\n");
goto FreeMem;
}
/* issue a create */
retval = v9fs_create(dir, dentry, mode, 0);
if (retval != 0)
goto FreeMem;
newfid = v9fs_fid_lookup(dentry);
if (!newfid) {
dprintk(DEBUG_ERROR, "coudn't resove fid from dentry\n");
retval = -EINVAL;
goto FreeMem;
}
if (!new_valid_dev(rdev))
return -EINVAL;
name = __getname();
/* build extension */
if (S_ISBLK(mode))
sprintf(symname, "b %u %u", MAJOR(rdev), MINOR(rdev));
sprintf(name, "b %u %u", MAJOR(rdev), MINOR(rdev));
else if (S_ISCHR(mode))
sprintf(symname, "c %u %u", MAJOR(rdev), MINOR(rdev));
sprintf(name, "c %u %u", MAJOR(rdev), MINOR(rdev));
else if (S_ISFIFO(mode))
; /* DO NOTHING */
*name = 0;
else {
retval = -EINVAL;
goto FreeMem;
__putname(name);
return -EINVAL;
}
if (!S_ISFIFO(mode)) {
/* issue a twstat */
v9fs_blank_mistat(v9ses, mistat);
strcpy(mistat->data + 1, symname);
mistat->extension = mistat->data + 1;
retval = v9fs_t_wstat(v9ses, newfid->fid, mistat, &fcall);
if (retval < 0) {
dprintk(DEBUG_ERROR, "v9fs_t_wstat error: %s\n",
FCALL_ERROR(fcall));
goto FreeMem;
}
}
/* need to update dcache so we show up */
kfree(fcall);
err = v9fs_t_clunk(v9ses, newfid->fid);
if (err < 0) {
dprintk(DEBUG_ERROR, "clunk for symlink failed: %d\n", err);
goto FreeMem;
}
d_drop(dentry); /* FID - will this also clunk? */
FreeMem:
kfree(mistat);
kfree(fcall);
__putname(symname);
retval = v9fs_vfs_mkspecial(dir, dentry, mode, name);
__putname(name);
return retval;
}