diff options
author | Stephen Rothwell <sfr@canb.auug.org.au> | 2010-06-09 10:20:46 +1000 |
---|---|---|
committer | Stephen Rothwell <sfr@canb.auug.org.au> | 2010-06-09 10:20:46 +1000 |
commit | bcccb2718c882a3ccb81195335d724ef01133890 (patch) | |
tree | e76017fa58e29dbf4c780abc56e4981ee1d26d00 | |
parent | 183ee1b158dbed7ef7e79f953ff286b18139808a (diff) | |
parent | 46f7f04b1920202d75495a12711299413406f522 (diff) |
Merge remote branch 'v9fs/for-next'
-rw-r--r-- | fs/9p/fid.c | 3 | ||||
-rw-r--r-- | fs/9p/v9fs.c | 2 | ||||
-rw-r--r-- | fs/9p/vfs_dir.c | 134 | ||||
-rw-r--r-- | fs/9p/vfs_file.c | 13 | ||||
-rw-r--r-- | fs/9p/vfs_inode.c | 79 | ||||
-rw-r--r-- | include/linux/virtio_9p.h | 1 | ||||
-rw-r--r-- | include/net/9p/9p.h | 19 | ||||
-rw-r--r-- | include/net/9p/client.h | 19 | ||||
-rw-r--r-- | net/9p/client.c | 71 | ||||
-rw-r--r-- | net/9p/protocol.c | 27 |
10 files changed, 342 insertions, 26 deletions
diff --git a/fs/9p/fid.c b/fs/9p/fid.c index 7317b39b2815..5d6cfcbf73e7 100644 --- a/fs/9p/fid.c +++ b/fs/9p/fid.c @@ -152,7 +152,8 @@ struct p9_fid *v9fs_fid_lookup(struct dentry *dentry) if (access == V9FS_ACCESS_SINGLE) return ERR_PTR(-EPERM); - if (v9fs_proto_dotu(v9ses)) + if (v9fs_proto_dotu(v9ses) || + v9fs_proto_dotl(v9ses)) uname = NULL; else uname = v9ses->uname; diff --git a/fs/9p/v9fs.c b/fs/9p/v9fs.c index f8b86e92cd66..3c492011221c 100644 --- a/fs/9p/v9fs.c +++ b/fs/9p/v9fs.c @@ -278,7 +278,7 @@ struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses, v9ses->maxdata = v9ses->clnt->msize - P9_IOHDRSZ; /* for legacy mode, fall back to V9FS_ACCESS_ANY */ - if (!v9fs_proto_dotu(v9ses) && + if (!(v9fs_proto_dotu(v9ses) || v9fs_proto_dotl(v9ses)) && ((v9ses->flags&V9FS_ACCESS_MASK) == V9FS_ACCESS_USER)) { v9ses->flags &= ~V9FS_ACCESS_MASK; diff --git a/fs/9p/vfs_dir.c b/fs/9p/vfs_dir.c index d61e3b28ce37..aa1852d7c98e 100644 --- a/fs/9p/vfs_dir.c +++ b/fs/9p/vfs_dir.c @@ -87,29 +87,19 @@ static void p9stat_init(struct p9_wstat *stbuf) } /** - * v9fs_dir_readdir - read a directory + * v9fs_alloc_rdir_buf - Allocate buffer used for read and readdir * @filp: opened file structure - * @dirent: directory structure ??? - * @filldir: function to populate directory structure ??? + * @buflen: Length in bytes of buffer to allocate * */ -static int v9fs_dir_readdir(struct file *filp, void *dirent, filldir_t filldir) +static int v9fs_alloc_rdir_buf(struct file *filp, int buflen) { - int over; - struct p9_wstat st; - int err = 0; - struct p9_fid *fid; - int buflen; - int reclen = 0; struct p9_rdir *rdir; + struct p9_fid *fid; + int err = 0; - P9_DPRINTK(P9_DEBUG_VFS, "name %s\n", filp->f_path.dentry->d_name.name); fid = filp->private_data; - - buflen = fid->clnt->msize - P9_IOHDRSZ; - - /* allocate rdir on demand */ if (!fid->rdir) { rdir = kmalloc(sizeof(struct p9_rdir) + buflen, GFP_KERNEL); @@ -128,6 +118,36 @@ static int v9fs_dir_readdir(struct file *filp, void *dirent, filldir_t filldir) spin_unlock(&filp->f_dentry->d_lock); kfree(rdir); } +exit: + return err; +} + +/** + * v9fs_dir_readdir - read a directory + * @filp: opened file structure + * @dirent: directory structure ??? + * @filldir: function to populate directory structure ??? + * + */ + +static int v9fs_dir_readdir(struct file *filp, void *dirent, filldir_t filldir) +{ + int over; + struct p9_wstat st; + int err = 0; + struct p9_fid *fid; + int buflen; + int reclen = 0; + struct p9_rdir *rdir; + + P9_DPRINTK(P9_DEBUG_VFS, "name %s\n", filp->f_path.dentry->d_name.name); + fid = filp->private_data; + + buflen = fid->clnt->msize - P9_IOHDRSZ; + + err = v9fs_alloc_rdir_buf(filp, buflen); + if (err) + goto exit; rdir = (struct p9_rdir *) fid->rdir; err = mutex_lock_interruptible(&rdir->mutex); @@ -176,6 +196,88 @@ exit: return err; } +/** + * v9fs_dir_readdir_dotl - read a directory + * @filp: opened file structure + * @dirent: buffer to fill dirent structures + * @filldir: function to populate dirent structures + * + */ +static int v9fs_dir_readdir_dotl(struct file *filp, void *dirent, + filldir_t filldir) +{ + int over; + int err = 0; + struct p9_fid *fid; + int buflen; + struct p9_rdir *rdir; + struct p9_dirent curdirent; + u64 oldoffset = 0; + + P9_DPRINTK(P9_DEBUG_VFS, "name %s\n", filp->f_path.dentry->d_name.name); + fid = filp->private_data; + + buflen = fid->clnt->msize - P9_READDIRHDRSZ; + + err = v9fs_alloc_rdir_buf(filp, buflen); + if (err) + goto exit; + rdir = (struct p9_rdir *) fid->rdir; + + err = mutex_lock_interruptible(&rdir->mutex); + if (err) + return err; + + while (err == 0) { + if (rdir->tail == rdir->head) { + err = p9_client_readdir(fid, rdir->buf, buflen, + filp->f_pos); + if (err <= 0) + goto unlock_and_exit; + + rdir->head = 0; + rdir->tail = err; + } + + while (rdir->head < rdir->tail) { + + err = p9dirent_read(rdir->buf + rdir->head, + buflen - rdir->head, &curdirent, + fid->clnt->proto_version); + if (err < 0) { + P9_DPRINTK(P9_DEBUG_VFS, "returned %d\n", err); + err = -EIO; + goto unlock_and_exit; + } + + /* d_off in dirent structure tracks the offset into + * the next dirent in the dir. However, filldir() + * expects offset into the current dirent. Hence + * while calling filldir send the offset from the + * previous dirent structure. + */ + over = filldir(dirent, curdirent.d_name, + strlen(curdirent.d_name), + oldoffset, v9fs_qid2ino(&curdirent.qid), + curdirent.d_type); + oldoffset = curdirent.d_off; + + if (over) { + err = 0; + goto unlock_and_exit; + } + + filp->f_pos = curdirent.d_off; + rdir->head += err; + } + } + +unlock_and_exit: + mutex_unlock(&rdir->mutex); +exit: + return err; +} + /** * v9fs_dir_release - close a directory @@ -207,7 +309,7 @@ const struct file_operations v9fs_dir_operations = { const struct file_operations v9fs_dir_operations_dotl = { .read = generic_read_dir, .llseek = generic_file_llseek, - .readdir = v9fs_dir_readdir, + .readdir = v9fs_dir_readdir_dotl, .open = v9fs_file_open, .release = v9fs_dir_release, }; diff --git a/fs/9p/vfs_file.c b/fs/9p/vfs_file.c index 2bedc6c94fc2..2d686ec322a0 100644 --- a/fs/9p/vfs_file.c +++ b/fs/9p/vfs_file.c @@ -139,7 +139,7 @@ ssize_t v9fs_file_readn(struct file *filp, char *data, char __user *udata, u32 count, u64 offset) { - int n, total; + int n, total, size; struct p9_fid *fid = filp->private_data; P9_DPRINTK(P9_DEBUG_VFS, "fid %d offset %llu count %d\n", fid->fid, @@ -147,6 +147,7 @@ v9fs_file_readn(struct file *filp, char *data, char __user *udata, u32 count, n = 0; total = 0; + size = fid->iounit ? fid->iounit : fid->clnt->msize - P9_IOHDRSZ; do { n = p9_client_read(fid, data, udata, offset, count); if (n <= 0) @@ -160,7 +161,7 @@ v9fs_file_readn(struct file *filp, char *data, char __user *udata, u32 count, offset += n; count -= n; total += n; - } while (count > 0 && n == (fid->clnt->msize - P9_IOHDRSZ)); + } while (count > 0 && n == size); if (n < 0) total = n; @@ -183,11 +184,13 @@ v9fs_file_read(struct file *filp, char __user *udata, size_t count, { int ret; struct p9_fid *fid; + size_t size; P9_DPRINTK(P9_DEBUG_VFS, "count %zu offset %lld\n", count, *offset); fid = filp->private_data; - if (count > (fid->clnt->msize - P9_IOHDRSZ)) + size = fid->iounit ? fid->iounit : fid->clnt->msize - P9_IOHDRSZ; + if (count > size) ret = v9fs_file_readn(filp, NULL, udata, count, *offset); else ret = p9_client_read(fid, NULL, udata, *offset, count); @@ -224,9 +227,7 @@ v9fs_file_write(struct file *filp, const char __user * data, fid = filp->private_data; clnt = fid->clnt; - rsize = fid->iounit; - if (!rsize || rsize > clnt->msize-P9_IOHDRSZ) - rsize = clnt->msize - P9_IOHDRSZ; + rsize = fid->iounit ? fid->iounit : clnt->msize - P9_IOHDRSZ; do { if (count < rsize) diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index 4331b3b5ee1c..a67f9f507333 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -236,6 +236,23 @@ void v9fs_destroy_inode(struct inode *inode) #endif /** + * v9fs_get_fsgid_for_create - Helper function to get the gid for creating a + * new file system object. This checks the S_ISGID to determine the owning + * group of the new file system object. + */ + +gid_t v9fs_get_fsgid_for_create(struct dentry *dir_dentry) +{ + BUG_ON(dir_dentry->d_inode == NULL); + + if (dir_dentry->d_inode->i_mode & S_ISGID) { + /* set_gid bit is set.*/ + return dir_dentry->d_inode->i_gid; + } + return current_fsgid(); +} + +/** * v9fs_get_inode - helper function to setup an inode * @sb: superblock * @mode: mode to setup inode with @@ -532,6 +549,23 @@ v9fs_create(struct v9fs_session_info *v9ses, struct inode *dir, goto error; } + /* Server grabs uid information from the fid but we need to fix + * gid as dotu doesn't support sending gid on the wire with Tcreate. + * hardlink is an exception as it inherits all credentials. + */ + if (v9fs_proto_dotu(v9ses) && !(perm & P9_DMLINK)) { + struct p9_wstat wstat; + + v9fs_blank_wstat(&wstat); + wstat.n_gid = v9fs_get_fsgid_for_create(dentry->d_parent); + err = p9_client_wstat(fid, &wstat); + if (err < 0) { + P9_DPRINTK(P9_DEBUG_VFS, "p9_client_wstat failed %d\n", + err); + goto error; + } + } + /* instantiate inode and assign the unopened fid to the dentry */ inode = v9fs_inode_from_fid(v9ses, fid, dir->i_sb); if (IS_ERR(inode)) { @@ -1186,6 +1220,49 @@ clunk_fid: } /** + * v9fs_vfs_link_dotl - create a hardlink for dotl + * @old_dentry: dentry for file to link to + * @dir: inode destination for new link + * @dentry: dentry for link + * + */ + +static int +v9fs_vfs_link_dotl(struct dentry *old_dentry, struct inode *dir, + struct dentry *dentry) +{ + int err; + struct p9_fid *dfid; + char *name; + struct v9fs_session_info *v9ses; + + P9_DPRINTK(P9_DEBUG_VFS, + " %lu,%s,%s\n", dir->i_ino, dentry->d_name.name, + old_dentry->d_name.name); + + v9ses = v9fs_inode2v9ses(dir); + dfid = v9fs_fid_lookup(dentry->d_parent); + if (IS_ERR(dfid)) + return PTR_ERR(dfid); + + name = (char *) dentry->d_name.name; + + err = p9_client_link(dfid, (char *)old_dentry->d_name.name, + (char *)dentry->d_name.name); + if (err < 0) { + P9_DPRINTK(P9_DEBUG_VFS, "p9_client_link failed %d\n", err); + return err; + } + + dentry->d_op = old_dentry->d_op; + /* Hard link, so we can use old_dentry's inode. Just i_count++ */ + atomic_inc(&old_dentry->d_inode->i_count); + d_instantiate(dentry, old_dentry->d_inode); + + return err; +} + +/** * v9fs_vfs_mknod - create a special file * @dir: inode destination for new link * @dentry: dentry for file @@ -1234,7 +1311,7 @@ static const struct inode_operations v9fs_dir_inode_operations_dotu = { .create = v9fs_vfs_create, .lookup = v9fs_vfs_lookup, .symlink = v9fs_vfs_symlink, - .link = v9fs_vfs_link, + .link = v9fs_vfs_link_dotl, .unlink = v9fs_vfs_unlink, .mkdir = v9fs_vfs_mkdir, .rmdir = v9fs_vfs_rmdir, diff --git a/include/linux/virtio_9p.h b/include/linux/virtio_9p.h index 5cf11765146b..395c38a47adb 100644 --- a/include/linux/virtio_9p.h +++ b/include/linux/virtio_9p.h @@ -4,6 +4,7 @@ * compatible drivers/servers. */ #include <linux/virtio_ids.h> #include <linux/virtio_config.h> +#include <linux/types.h> /* The feature bitmap for virtio 9P */ diff --git a/include/net/9p/9p.h b/include/net/9p/9p.h index 156c26bb8bd7..6df324ef278c 100644 --- a/include/net/9p/9p.h +++ b/include/net/9p/9p.h @@ -133,6 +133,10 @@ enum p9_msg_t { P9_RSTATFS, P9_TRENAME = 20, P9_RRENAME, + P9_TREADDIR = 40, + P9_RREADDIR, + P9_TLINK = 70, + P9_RLINK, P9_TVERSION = 100, P9_RVERSION, P9_TAUTH = 102, @@ -275,6 +279,9 @@ enum p9_qid_t { /* ample room for Twrite/Rread header */ #define P9_IOHDRSZ 24 +/* Room for readdir header */ +#define P9_READDIRHDRSZ 24 + /** * struct p9_str - length prefixed string type * @len: length of the string @@ -485,6 +492,18 @@ struct p9_rwrite { u32 count; }; +struct p9_treaddir { + u32 fid; + u64 offset; + u32 count; +}; + +struct p9_rreaddir { + u32 count; + u8 *data; +}; + + struct p9_tclunk { u32 fid; }; diff --git a/include/net/9p/client.h b/include/net/9p/client.h index 7dd3ed85c782..180a07ed07d7 100644 --- a/include/net/9p/client.h +++ b/include/net/9p/client.h @@ -195,6 +195,21 @@ struct p9_fid { struct list_head dlist; /* list of all fids attached to a dentry */ }; +/** + * struct p9_dirent - directory entry structure + * @qid: The p9 server qid for this dirent + * @d_off: offset to the next dirent + * @d_type: type of file + * @d_name: file name + */ + +struct p9_dirent { + struct p9_qid qid; + u64 d_off; + unsigned char d_type; + char d_name[256]; +}; + int p9_client_statfs(struct p9_fid *fid, struct p9_rstatfs *sb); int p9_client_rename(struct p9_fid *fid, struct p9_fid *newdirfid, char *name); int p9_client_version(struct p9_client *); @@ -211,12 +226,16 @@ struct p9_fid *p9_client_walk(struct p9_fid *oldfid, int nwname, char **wnames, int p9_client_open(struct p9_fid *fid, int mode); int p9_client_fcreate(struct p9_fid *fid, char *name, u32 perm, int mode, char *extension); +int p9_client_link(struct p9_fid *fid, char *name, char *linkname); int p9_client_clunk(struct p9_fid *fid); int p9_client_remove(struct p9_fid *fid); int p9_client_read(struct p9_fid *fid, char *data, char __user *udata, u64 offset, u32 count); int p9_client_write(struct p9_fid *fid, char *data, const char __user *udata, u64 offset, u32 count); +int p9_client_readdir(struct p9_fid *fid, char *data, u32 count, u64 offset); +int p9dirent_read(char *buf, int len, struct p9_dirent *dirent, + int proto_version); struct p9_wstat *p9_client_stat(struct p9_fid *fid); int p9_client_wstat(struct p9_fid *fid, struct p9_wstat *wst); diff --git a/net/9p/client.c b/net/9p/client.c index 37c8da07a80b..01000a84a006 100644 --- a/net/9p/client.c +++ b/net/9p/client.c @@ -460,7 +460,8 @@ static int p9_check_errors(struct p9_client *c, struct p9_req_t *req) return err; } - if (p9_is_proto_dotu(c)) + if (p9_is_proto_dotu(c) || + p9_is_proto_dotl(c)) err = -ecode; if (!err || !IS_ERR_VALUE(err)) @@ -1094,6 +1095,27 @@ error: } EXPORT_SYMBOL(p9_client_fcreate); +int p9_client_link(struct p9_fid *dfid, char *oldname, char *newname) +{ + int err = 0; + struct p9_client *clnt; + struct p9_req_t *req; + + P9_DPRINTK(P9_DEBUG_9P, ">>> TLINK fid %d oldname %s newname %s\n", + dfid->fid, oldname, newname); + clnt = dfid->clnt; + req = p9_client_rpc(clnt, P9_TLINK, "dss", dfid->fid, oldname, newname); + if (IS_ERR(req)) { + err = PTR_ERR(req); + goto error; + } + P9_DPRINTK(P9_DEBUG_9P, "<<< RLINK\n"); + p9_free_req(clnt, req); +error: + return err; +} +EXPORT_SYMBOL(p9_client_link); + int p9_client_clunk(struct p9_fid *fid) { int err; @@ -1432,3 +1454,50 @@ error: } EXPORT_SYMBOL(p9_client_rename); +int p9_client_readdir(struct p9_fid *fid, char *data, u32 count, u64 offset) +{ + int err, rsize, total; + struct p9_client *clnt; + struct p9_req_t *req; + char *dataptr; + + P9_DPRINTK(P9_DEBUG_9P, ">>> TREADDIR fid %d offset %llu count %d\n", + fid->fid, (long long unsigned) offset, count); + + err = 0; + clnt = fid->clnt; + total = 0; + + rsize = fid->iounit; + if (!rsize || rsize > clnt->msize-P9_READDIRHDRSZ) + rsize = clnt->msize - P9_READDIRHDRSZ; + + if (count < rsize) + rsize = count; + + req = p9_client_rpc(clnt, P9_TREADDIR, "dqd", fid->fid, offset, rsize); + if (IS_ERR(req)) { + err = PTR_ERR(req); + goto error; + } + + err = p9pdu_readf(req->rc, clnt->proto_version, "D", &count, &dataptr); + if (err) { + p9pdu_dump(1, req->rc); + goto free_and_error; + } + + P9_DPRINTK(P9_DEBUG_9P, "<<< RREADDIR count %d\n", count); + + if (data) + memmove(data, dataptr, count); + + p9_free_req(clnt, req); + return count; + +free_and_error: + p9_free_req(clnt, req); +error: + return err; +} +EXPORT_SYMBOL(p9_client_readdir); diff --git a/net/9p/protocol.c b/net/9p/protocol.c index 149f82160130..b645c8263538 100644 --- a/net/9p/protocol.c +++ b/net/9p/protocol.c @@ -580,3 +580,30 @@ void p9pdu_reset(struct p9_fcall *pdu) pdu->offset = 0; pdu->size = 0; } + +int p9dirent_read(char *buf, int len, struct p9_dirent *dirent, + int proto_version) +{ + struct p9_fcall fake_pdu; + int ret; + char *nameptr; + + fake_pdu.size = len; + fake_pdu.capacity = len; + fake_pdu.sdata = buf; + fake_pdu.offset = 0; + + ret = p9pdu_readf(&fake_pdu, proto_version, "Qqbs", &dirent->qid, + &dirent->d_off, &dirent->d_type, &nameptr); + if (ret) { + P9_DPRINTK(P9_DEBUG_9P, "<<< p9dirent_read failed: %d\n", ret); + p9pdu_dump(1, &fake_pdu); + goto out; + } + + strcpy(dirent->d_name, nameptr); + +out: + return fake_pdu.offset; +} +EXPORT_SYMBOL(p9dirent_read); |