Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ericvh/v9fs
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ericvh/v9fs: (28 commits) net/9p: Return error on read with NULL buffer 9p: Add datasync to client side TFSYNC/RFSYNC for dotl net/9p: Return error if we fail to encode protocol data fs/9p: Use generic_file_open with lookup_instantiate_filp fs/9p: Add missing iput in v9fs_vfs_lookup fs/9p: Use mknod 9p operation on create without open request net/9p: Add waitq to VirtIO transport. [net/9p]Serialize virtqueue operations to make VirtIO transport SMP safe. 9p: Implement TREADLINK operation for 9p2000.L 9p: Use V9FS_MAGIC in statfs 9p: Implement TGETLOCK 9p: Implement TLOCK [9p] Introduce client side TFSYNC/RFSYNC for dotl. [fs/9p] Add file_operations for cached mode in dotl protocol. fs/9p: Add access = client option to opt in acl evaluation. fs/9p: Implement create time inheritance fs/9p: Update ACL on chmod fs/9p: Implement setting posix acl fs/9p: Add xattr callbacks for POSIX ACL fs/9p: Implement POSIX ACL permission checking function ...
This commit is contained in:
commit
90a2b69c14
|
@ -111,7 +111,7 @@ OPTIONS
|
|||
This can be used to share devices/named pipes/sockets between
|
||||
hosts. This functionality will be expanded in later versions.
|
||||
|
||||
access there are three access modes.
|
||||
access there are four access modes.
|
||||
user = if a user tries to access a file on v9fs
|
||||
filesystem for the first time, v9fs sends an
|
||||
attach command (Tattach) for that user.
|
||||
|
@ -120,6 +120,8 @@ OPTIONS
|
|||
the files on the mounted filesystem
|
||||
any = v9fs does single attach and performs all
|
||||
operations as one user
|
||||
client = ACL based access check on the 9p client
|
||||
side for access validation
|
||||
|
||||
cachetag cache tag to use the specified persistent cache.
|
||||
cache tags for existing cache sessions can be listed at
|
||||
|
|
|
@ -17,3 +17,16 @@ config 9P_FSCACHE
|
|||
Choose Y here to enable persistent, read-only local
|
||||
caching support for 9p clients using FS-Cache
|
||||
|
||||
|
||||
config 9P_FS_POSIX_ACL
|
||||
bool "9P POSIX Access Control Lists"
|
||||
depends on 9P_FS
|
||||
select FS_POSIX_ACL
|
||||
help
|
||||
POSIX Access Control Lists (ACLs) support permissions for users and
|
||||
groups beyond the owner/group/world scheme.
|
||||
|
||||
To learn more about Access Control Lists, visit the POSIX ACLs for
|
||||
Linux website <http://acl.bestbits.at/>.
|
||||
|
||||
If you don't know what Access Control Lists are, say N
|
||||
|
|
|
@ -13,3 +13,4 @@ obj-$(CONFIG_9P_FS) := 9p.o
|
|||
xattr_user.o
|
||||
|
||||
9p-$(CONFIG_9P_FSCACHE) += cache.o
|
||||
9p-$(CONFIG_9P_FS_POSIX_ACL) += acl.o
|
||||
|
|
392
fs/9p/acl.c
Normal file
392
fs/9p/acl.c
Normal file
|
@ -0,0 +1,392 @@
|
|||
/*
|
||||
* Copyright IBM Corporation, 2010
|
||||
* Author Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of version 2.1 of the GNU Lesser General Public License
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it would be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/fs.h>
|
||||
#include <net/9p/9p.h>
|
||||
#include <net/9p/client.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/posix_acl_xattr.h>
|
||||
#include "xattr.h"
|
||||
#include "acl.h"
|
||||
#include "v9fs_vfs.h"
|
||||
#include "v9fs.h"
|
||||
|
||||
static struct posix_acl *__v9fs_get_acl(struct p9_fid *fid, char *name)
|
||||
{
|
||||
ssize_t size;
|
||||
void *value = NULL;
|
||||
struct posix_acl *acl = NULL;;
|
||||
|
||||
size = v9fs_fid_xattr_get(fid, name, NULL, 0);
|
||||
if (size > 0) {
|
||||
value = kzalloc(size, GFP_NOFS);
|
||||
if (!value)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
size = v9fs_fid_xattr_get(fid, name, value, size);
|
||||
if (size > 0) {
|
||||
acl = posix_acl_from_xattr(value, size);
|
||||
if (IS_ERR(acl))
|
||||
goto err_out;
|
||||
}
|
||||
} else if (size == -ENODATA || size == 0 ||
|
||||
size == -ENOSYS || size == -EOPNOTSUPP) {
|
||||
acl = NULL;
|
||||
} else
|
||||
acl = ERR_PTR(-EIO);
|
||||
|
||||
err_out:
|
||||
kfree(value);
|
||||
return acl;
|
||||
}
|
||||
|
||||
int v9fs_get_acl(struct inode *inode, struct p9_fid *fid)
|
||||
{
|
||||
int retval = 0;
|
||||
struct posix_acl *pacl, *dacl;
|
||||
struct v9fs_session_info *v9ses;
|
||||
|
||||
v9ses = v9fs_inode2v9ses(inode);
|
||||
if ((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT) {
|
||||
set_cached_acl(inode, ACL_TYPE_DEFAULT, NULL);
|
||||
set_cached_acl(inode, ACL_TYPE_ACCESS, NULL);
|
||||
return 0;
|
||||
}
|
||||
/* get the default/access acl values and cache them */
|
||||
dacl = __v9fs_get_acl(fid, POSIX_ACL_XATTR_DEFAULT);
|
||||
pacl = __v9fs_get_acl(fid, POSIX_ACL_XATTR_ACCESS);
|
||||
|
||||
if (!IS_ERR(dacl) && !IS_ERR(pacl)) {
|
||||
set_cached_acl(inode, ACL_TYPE_DEFAULT, dacl);
|
||||
set_cached_acl(inode, ACL_TYPE_ACCESS, pacl);
|
||||
posix_acl_release(dacl);
|
||||
posix_acl_release(pacl);
|
||||
} else
|
||||
retval = -EIO;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static struct posix_acl *v9fs_get_cached_acl(struct inode *inode, int type)
|
||||
{
|
||||
struct posix_acl *acl;
|
||||
/*
|
||||
* 9p Always cache the acl value when
|
||||
* instantiating the inode (v9fs_inode_from_fid)
|
||||
*/
|
||||
acl = get_cached_acl(inode, type);
|
||||
BUG_ON(acl == ACL_NOT_CACHED);
|
||||
return acl;
|
||||
}
|
||||
|
||||
int v9fs_check_acl(struct inode *inode, int mask)
|
||||
{
|
||||
struct posix_acl *acl;
|
||||
struct v9fs_session_info *v9ses;
|
||||
|
||||
v9ses = v9fs_inode2v9ses(inode);
|
||||
if ((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT) {
|
||||
/*
|
||||
* On access = client mode get the acl
|
||||
* values from the server
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
acl = v9fs_get_cached_acl(inode, ACL_TYPE_ACCESS);
|
||||
|
||||
if (IS_ERR(acl))
|
||||
return PTR_ERR(acl);
|
||||
if (acl) {
|
||||
int error = posix_acl_permission(inode, acl, mask);
|
||||
posix_acl_release(acl);
|
||||
return error;
|
||||
}
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
static int v9fs_set_acl(struct dentry *dentry, int type, struct posix_acl *acl)
|
||||
{
|
||||
int retval;
|
||||
char *name;
|
||||
size_t size;
|
||||
void *buffer;
|
||||
struct inode *inode = dentry->d_inode;
|
||||
|
||||
set_cached_acl(inode, type, acl);
|
||||
/* Set a setxattr request to server */
|
||||
size = posix_acl_xattr_size(acl->a_count);
|
||||
buffer = kmalloc(size, GFP_KERNEL);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
retval = posix_acl_to_xattr(acl, buffer, size);
|
||||
if (retval < 0)
|
||||
goto err_free_out;
|
||||
switch (type) {
|
||||
case ACL_TYPE_ACCESS:
|
||||
name = POSIX_ACL_XATTR_ACCESS;
|
||||
break;
|
||||
case ACL_TYPE_DEFAULT:
|
||||
name = POSIX_ACL_XATTR_DEFAULT;
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
retval = v9fs_xattr_set(dentry, name, buffer, size, 0);
|
||||
err_free_out:
|
||||
kfree(buffer);
|
||||
return retval;
|
||||
}
|
||||
|
||||
int v9fs_acl_chmod(struct dentry *dentry)
|
||||
{
|
||||
int retval = 0;
|
||||
struct posix_acl *acl, *clone;
|
||||
struct inode *inode = dentry->d_inode;
|
||||
|
||||
if (S_ISLNK(inode->i_mode))
|
||||
return -EOPNOTSUPP;
|
||||
acl = v9fs_get_cached_acl(inode, ACL_TYPE_ACCESS);
|
||||
if (acl) {
|
||||
clone = posix_acl_clone(acl, GFP_KERNEL);
|
||||
posix_acl_release(acl);
|
||||
if (!clone)
|
||||
return -ENOMEM;
|
||||
retval = posix_acl_chmod_masq(clone, inode->i_mode);
|
||||
if (!retval)
|
||||
retval = v9fs_set_acl(dentry, ACL_TYPE_ACCESS, clone);
|
||||
posix_acl_release(clone);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
int v9fs_set_create_acl(struct dentry *dentry,
|
||||
struct posix_acl *dpacl, struct posix_acl *pacl)
|
||||
{
|
||||
if (dpacl)
|
||||
v9fs_set_acl(dentry, ACL_TYPE_DEFAULT, dpacl);
|
||||
if (pacl)
|
||||
v9fs_set_acl(dentry, ACL_TYPE_ACCESS, pacl);
|
||||
posix_acl_release(dpacl);
|
||||
posix_acl_release(pacl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int v9fs_acl_mode(struct inode *dir, mode_t *modep,
|
||||
struct posix_acl **dpacl, struct posix_acl **pacl)
|
||||
{
|
||||
int retval = 0;
|
||||
mode_t mode = *modep;
|
||||
struct posix_acl *acl = NULL;
|
||||
|
||||
if (!S_ISLNK(mode)) {
|
||||
acl = v9fs_get_cached_acl(dir, ACL_TYPE_DEFAULT);
|
||||
if (IS_ERR(acl))
|
||||
return PTR_ERR(acl);
|
||||
if (!acl)
|
||||
mode &= ~current_umask();
|
||||
}
|
||||
if (acl) {
|
||||
struct posix_acl *clone;
|
||||
|
||||
if (S_ISDIR(mode))
|
||||
*dpacl = acl;
|
||||
clone = posix_acl_clone(acl, GFP_NOFS);
|
||||
retval = -ENOMEM;
|
||||
if (!clone)
|
||||
goto cleanup;
|
||||
|
||||
retval = posix_acl_create_masq(clone, &mode);
|
||||
if (retval < 0) {
|
||||
posix_acl_release(clone);
|
||||
goto cleanup;
|
||||
}
|
||||
if (retval > 0)
|
||||
*pacl = clone;
|
||||
}
|
||||
*modep = mode;
|
||||
return 0;
|
||||
cleanup:
|
||||
posix_acl_release(acl);
|
||||
return retval;
|
||||
|
||||
}
|
||||
|
||||
static int v9fs_remote_get_acl(struct dentry *dentry, const char *name,
|
||||
void *buffer, size_t size, int type)
|
||||
{
|
||||
char *full_name;
|
||||
|
||||
switch (type) {
|
||||
case ACL_TYPE_ACCESS:
|
||||
full_name = POSIX_ACL_XATTR_ACCESS;
|
||||
break;
|
||||
case ACL_TYPE_DEFAULT:
|
||||
full_name = POSIX_ACL_XATTR_DEFAULT;
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
return v9fs_xattr_get(dentry, full_name, buffer, size);
|
||||
}
|
||||
|
||||
static int v9fs_xattr_get_acl(struct dentry *dentry, const char *name,
|
||||
void *buffer, size_t size, int type)
|
||||
{
|
||||
struct v9fs_session_info *v9ses;
|
||||
struct posix_acl *acl;
|
||||
int error;
|
||||
|
||||
if (strcmp(name, "") != 0)
|
||||
return -EINVAL;
|
||||
|
||||
v9ses = v9fs_inode2v9ses(dentry->d_inode);
|
||||
/*
|
||||
* We allow set/get/list of acl when access=client is not specified
|
||||
*/
|
||||
if ((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT)
|
||||
return v9fs_remote_get_acl(dentry, name, buffer, size, type);
|
||||
|
||||
acl = v9fs_get_cached_acl(dentry->d_inode, type);
|
||||
if (IS_ERR(acl))
|
||||
return PTR_ERR(acl);
|
||||
if (acl == NULL)
|
||||
return -ENODATA;
|
||||
error = posix_acl_to_xattr(acl, buffer, size);
|
||||
posix_acl_release(acl);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int v9fs_remote_set_acl(struct dentry *dentry, const char *name,
|
||||
const void *value, size_t size,
|
||||
int flags, int type)
|
||||
{
|
||||
char *full_name;
|
||||
|
||||
switch (type) {
|
||||
case ACL_TYPE_ACCESS:
|
||||
full_name = POSIX_ACL_XATTR_ACCESS;
|
||||
break;
|
||||
case ACL_TYPE_DEFAULT:
|
||||
full_name = POSIX_ACL_XATTR_DEFAULT;
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
return v9fs_xattr_set(dentry, full_name, value, size, flags);
|
||||
}
|
||||
|
||||
|
||||
static int v9fs_xattr_set_acl(struct dentry *dentry, const char *name,
|
||||
const void *value, size_t size,
|
||||
int flags, int type)
|
||||
{
|
||||
int retval;
|
||||
struct posix_acl *acl;
|
||||
struct v9fs_session_info *v9ses;
|
||||
struct inode *inode = dentry->d_inode;
|
||||
|
||||
if (strcmp(name, "") != 0)
|
||||
return -EINVAL;
|
||||
|
||||
v9ses = v9fs_inode2v9ses(dentry->d_inode);
|
||||
/*
|
||||
* set the attribute on the remote. Without even looking at the
|
||||
* xattr value. We leave it to the server to validate
|
||||
*/
|
||||
if ((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT)
|
||||
return v9fs_remote_set_acl(dentry, name,
|
||||
value, size, flags, type);
|
||||
|
||||
if (S_ISLNK(inode->i_mode))
|
||||
return -EOPNOTSUPP;
|
||||
if (!is_owner_or_cap(inode))
|
||||
return -EPERM;
|
||||
if (value) {
|
||||
/* update the cached acl value */
|
||||
acl = posix_acl_from_xattr(value, size);
|
||||
if (IS_ERR(acl))
|
||||
return PTR_ERR(acl);
|
||||
else if (acl) {
|
||||
retval = posix_acl_valid(acl);
|
||||
if (retval)
|
||||
goto err_out;
|
||||
}
|
||||
} else
|
||||
acl = NULL;
|
||||
|
||||
switch (type) {
|
||||
case ACL_TYPE_ACCESS:
|
||||
name = POSIX_ACL_XATTR_ACCESS;
|
||||
if (acl) {
|
||||
mode_t mode = inode->i_mode;
|
||||
retval = posix_acl_equiv_mode(acl, &mode);
|
||||
if (retval < 0)
|
||||
goto err_out;
|
||||
else {
|
||||
struct iattr iattr;
|
||||
if (retval == 0) {
|
||||
/*
|
||||
* ACL can be represented
|
||||
* by the mode bits. So don't
|
||||
* update ACL.
|
||||
*/
|
||||
acl = NULL;
|
||||
value = NULL;
|
||||
size = 0;
|
||||
}
|
||||
/* Updte the mode bits */
|
||||
iattr.ia_mode = ((mode & S_IALLUGO) |
|
||||
(inode->i_mode & ~S_IALLUGO));
|
||||
iattr.ia_valid = ATTR_MODE;
|
||||
/* FIXME should we update ctime ?
|
||||
* What is the following setxattr update the
|
||||
* mode ?
|
||||
*/
|
||||
v9fs_vfs_setattr_dotl(dentry, &iattr);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ACL_TYPE_DEFAULT:
|
||||
name = POSIX_ACL_XATTR_DEFAULT;
|
||||
if (!S_ISDIR(inode->i_mode)) {
|
||||
retval = -EINVAL;
|
||||
goto err_out;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
retval = v9fs_xattr_set(dentry, name, value, size, flags);
|
||||
if (!retval)
|
||||
set_cached_acl(inode, type, acl);
|
||||
err_out:
|
||||
posix_acl_release(acl);
|
||||
return retval;
|
||||
}
|
||||
|
||||
const struct xattr_handler v9fs_xattr_acl_access_handler = {
|
||||
.prefix = POSIX_ACL_XATTR_ACCESS,
|
||||
.flags = ACL_TYPE_ACCESS,
|
||||
.get = v9fs_xattr_get_acl,
|
||||
.set = v9fs_xattr_set_acl,
|
||||
};
|
||||
|
||||
const struct xattr_handler v9fs_xattr_acl_default_handler = {
|
||||
.prefix = POSIX_ACL_XATTR_DEFAULT,
|
||||
.flags = ACL_TYPE_DEFAULT,
|
||||
.get = v9fs_xattr_get_acl,
|
||||
.set = v9fs_xattr_set_acl,
|
||||
};
|
49
fs/9p/acl.h
Normal file
49
fs/9p/acl.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright IBM Corporation, 2010
|
||||
* Author Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of version 2.1 of the GNU Lesser General Public License
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it would be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
*/
|
||||
#ifndef FS_9P_ACL_H
|
||||
#define FS_9P_ACL_H
|
||||
|
||||
#ifdef CONFIG_9P_FS_POSIX_ACL
|
||||
extern int v9fs_get_acl(struct inode *, struct p9_fid *);
|
||||
extern int v9fs_check_acl(struct inode *inode, int mask);
|
||||
extern int v9fs_acl_chmod(struct dentry *);
|
||||
extern int v9fs_set_create_acl(struct dentry *,
|
||||
struct posix_acl *, struct posix_acl *);
|
||||
extern int v9fs_acl_mode(struct inode *dir, mode_t *modep,
|
||||
struct posix_acl **dpacl, struct posix_acl **pacl);
|
||||
#else
|
||||
#define v9fs_check_acl NULL
|
||||
static inline int v9fs_get_acl(struct inode *inode, struct p9_fid *fid)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int v9fs_acl_chmod(struct dentry *dentry)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int v9fs_set_create_acl(struct dentry *dentry,
|
||||
struct posix_acl *dpacl,
|
||||
struct posix_acl *pacl)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int v9fs_acl_mode(struct inode *dir, mode_t *modep,
|
||||
struct posix_acl **dpacl,
|
||||
struct posix_acl **pacl)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif /* FS_9P_XATTR_H */
|
|
@ -149,6 +149,7 @@ struct p9_fid *v9fs_fid_lookup(struct dentry *dentry)
|
|||
switch (access) {
|
||||
case V9FS_ACCESS_SINGLE:
|
||||
case V9FS_ACCESS_USER:
|
||||
case V9FS_ACCESS_CLIENT:
|
||||
uid = current_fsuid();
|
||||
any = 0;
|
||||
break;
|
||||
|
|
22
fs/9p/v9fs.c
22
fs/9p/v9fs.c
|
@ -193,7 +193,17 @@ static int v9fs_parse_options(struct v9fs_session_info *v9ses, char *opts)
|
|||
v9ses->flags |= V9FS_ACCESS_USER;
|
||||
else if (strcmp(s, "any") == 0)
|
||||
v9ses->flags |= V9FS_ACCESS_ANY;
|
||||
else {
|
||||
else if (strcmp(s, "client") == 0) {
|
||||
#ifdef CONFIG_9P_FS_POSIX_ACL
|
||||
v9ses->flags |= V9FS_ACCESS_CLIENT;
|
||||
#else
|
||||
P9_DPRINTK(P9_DEBUG_ERROR,
|
||||
"access=client option not supported\n");
|
||||
kfree(s);
|
||||
ret = -EINVAL;
|
||||
goto free_and_return;
|
||||
#endif
|
||||
} else {
|
||||
v9ses->flags |= V9FS_ACCESS_SINGLE;
|
||||
v9ses->uid = simple_strtoul(s, &e, 10);
|
||||
if (*e != '\0')
|
||||
|
@ -278,6 +288,16 @@ struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses,
|
|||
|
||||
v9ses->maxdata = v9ses->clnt->msize - P9_IOHDRSZ;
|
||||
|
||||
if (!v9fs_proto_dotl(v9ses) &&
|
||||
((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_CLIENT)) {
|
||||
/*
|
||||
* We support ACCESS_CLIENT only for dotl.
|
||||
* Fall back to ACCESS_USER
|
||||
*/
|
||||
v9ses->flags &= ~V9FS_ACCESS_MASK;
|
||||
v9ses->flags |= V9FS_ACCESS_USER;
|
||||
}
|
||||
/*FIXME !! */
|
||||
/* for legacy mode, fall back to V9FS_ACCESS_ANY */
|
||||
if (!(v9fs_proto_dotu(v9ses) || v9fs_proto_dotl(v9ses)) &&
|
||||
((v9ses->flags&V9FS_ACCESS_MASK) == V9FS_ACCESS_USER)) {
|
||||
|
|
10
fs/9p/v9fs.h
10
fs/9p/v9fs.h
|
@ -33,13 +33,17 @@
|
|||
*
|
||||
* Session flags reflect options selected by users at mount time
|
||||
*/
|
||||
#define V9FS_ACCESS_ANY (V9FS_ACCESS_SINGLE | \
|
||||
V9FS_ACCESS_USER | \
|
||||
V9FS_ACCESS_CLIENT)
|
||||
#define V9FS_ACCESS_MASK V9FS_ACCESS_ANY
|
||||
|
||||
enum p9_session_flags {
|
||||
V9FS_PROTO_2000U = 0x01,
|
||||
V9FS_PROTO_2000L = 0x02,
|
||||
V9FS_ACCESS_SINGLE = 0x04,
|
||||
V9FS_ACCESS_USER = 0x08,
|
||||
V9FS_ACCESS_ANY = 0x0C,
|
||||
V9FS_ACCESS_MASK = 0x0C,
|
||||
V9FS_ACCESS_CLIENT = 0x10
|
||||
};
|
||||
|
||||
/* possible values of ->cache */
|
||||
|
@ -113,8 +117,6 @@ void v9fs_session_close(struct v9fs_session_info *v9ses);
|
|||
void v9fs_session_cancel(struct v9fs_session_info *v9ses);
|
||||
void v9fs_session_begin_cancel(struct v9fs_session_info *v9ses);
|
||||
|
||||
#define V9FS_MAGIC 0x01021997
|
||||
|
||||
/* other default globals */
|
||||
#define V9FS_PORT 564
|
||||
#define V9FS_DEFUSER "nobody"
|
||||
|
|
|
@ -64,3 +64,7 @@ int v9fs_uflags2omode(int uflags, int extended);
|
|||
|
||||
ssize_t v9fs_file_readn(struct file *, char *, char __user *, u32, u64);
|
||||
void v9fs_blank_wstat(struct p9_wstat *wstat);
|
||||
int v9fs_vfs_setattr_dotl(struct dentry *, struct iattr *);
|
||||
int v9fs_file_fsync_dotl(struct file *filp, int datasync);
|
||||
|
||||
#define P9_LOCK_TIMEOUT (30*HZ)
|
||||
|
|
|
@ -154,10 +154,40 @@ static int v9fs_launder_page(struct page *page)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_direct_IO - 9P address space operation for direct I/O
|
||||
* @rw: direction (read or write)
|
||||
* @iocb: target I/O control block
|
||||
* @iov: array of vectors that define I/O buffer
|
||||
* @pos: offset in file to begin the operation
|
||||
* @nr_segs: size of iovec array
|
||||
*
|
||||
* The presence of v9fs_direct_IO() in the address space ops vector
|
||||
* allowes open() O_DIRECT flags which would have failed otherwise.
|
||||
*
|
||||
* In the non-cached mode, we shunt off direct read and write requests before
|
||||
* the VFS gets them, so this method should never be called.
|
||||
*
|
||||
* Direct IO is not 'yet' supported in the cached mode. Hence when
|
||||
* this routine is called through generic_file_aio_read(), the read/write fails
|
||||
* with an error.
|
||||
*
|
||||
*/
|
||||
ssize_t v9fs_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,
|
||||
loff_t pos, unsigned long nr_segs)
|
||||
{
|
||||
P9_DPRINTK(P9_DEBUG_VFS, "v9fs_direct_IO: v9fs_direct_IO (%s) "
|
||||
"off/no(%lld/%lu) EINVAL\n",
|
||||
iocb->ki_filp->f_path.dentry->d_name.name,
|
||||
(long long) pos, nr_segs);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
const struct address_space_operations v9fs_addr_operations = {
|
||||
.readpage = v9fs_vfs_readpage,
|
||||
.readpages = v9fs_vfs_readpages,
|
||||
.releasepage = v9fs_release_page,
|
||||
.invalidatepage = v9fs_invalidate_page,
|
||||
.launder_page = v9fs_launder_page,
|
||||
.direct_IO = v9fs_direct_IO,
|
||||
};
|
||||
|
|
|
@ -242,7 +242,8 @@ static int v9fs_dir_readdir_dotl(struct file *filp, void *dirent,
|
|||
while (rdir->head < rdir->tail) {
|
||||
|
||||
err = p9dirent_read(rdir->buf + rdir->head,
|
||||
buflen - rdir->head, &curdirent,
|
||||
rdir->tail - rdir->head,
|
||||
&curdirent,
|
||||
fid->clnt->proto_version);
|
||||
if (err < 0) {
|
||||
P9_DPRINTK(P9_DEBUG_VFS, "returned %d\n", err);
|
||||
|
@ -314,4 +315,5 @@ const struct file_operations v9fs_dir_operations_dotl = {
|
|||
.readdir = v9fs_dir_readdir_dotl,
|
||||
.open = v9fs_file_open,
|
||||
.release = v9fs_dir_release,
|
||||
.fsync = v9fs_file_fsync_dotl,
|
||||
};
|
||||
|
|
265
fs/9p/vfs_file.c
265
fs/9p/vfs_file.c
|
@ -33,6 +33,7 @@
|
|||
#include <linux/inet.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/utsname.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <linux/idr.h>
|
||||
#include <net/9p/9p.h>
|
||||
|
@ -44,6 +45,7 @@
|
|||
#include "cache.h"
|
||||
|
||||
static const struct file_operations v9fs_cached_file_operations;
|
||||
static const struct file_operations v9fs_cached_file_operations_dotl;
|
||||
|
||||
/**
|
||||
* v9fs_file_open - open a file (or directory)
|
||||
|
@ -92,6 +94,8 @@ int v9fs_file_open(struct inode *inode, struct file *file)
|
|||
/* enable cached file options */
|
||||
if(file->f_op == &v9fs_file_operations)
|
||||
file->f_op = &v9fs_cached_file_operations;
|
||||
else if (file->f_op == &v9fs_file_operations_dotl)
|
||||
file->f_op = &v9fs_cached_file_operations_dotl;
|
||||
|
||||
#ifdef CONFIG_9P_FSCACHE
|
||||
v9fs_cache_inode_set_cookie(inode, file);
|
||||
|
@ -130,6 +134,206 @@ static int v9fs_file_lock(struct file *filp, int cmd, struct file_lock *fl)
|
|||
return res;
|
||||
}
|
||||
|
||||
static int v9fs_file_do_lock(struct file *filp, int cmd, struct file_lock *fl)
|
||||
{
|
||||
struct p9_flock flock;
|
||||
struct p9_fid *fid;
|
||||
uint8_t status;
|
||||
int res = 0;
|
||||
unsigned char fl_type;
|
||||
|
||||
fid = filp->private_data;
|
||||
BUG_ON(fid == NULL);
|
||||
|
||||
if ((fl->fl_flags & FL_POSIX) != FL_POSIX)
|
||||
BUG();
|
||||
|
||||
res = posix_lock_file_wait(filp, fl);
|
||||
if (res < 0)
|
||||
goto out;
|
||||
|
||||
/* convert posix lock to p9 tlock args */
|
||||
memset(&flock, 0, sizeof(flock));
|
||||
flock.type = fl->fl_type;
|
||||
flock.start = fl->fl_start;
|
||||
if (fl->fl_end == OFFSET_MAX)
|
||||
flock.length = 0;
|
||||
else
|
||||
flock.length = fl->fl_end - fl->fl_start + 1;
|
||||
flock.proc_id = fl->fl_pid;
|
||||
flock.client_id = utsname()->nodename;
|
||||
if (IS_SETLKW(cmd))
|
||||
flock.flags = P9_LOCK_FLAGS_BLOCK;
|
||||
|
||||
/*
|
||||
* if its a blocked request and we get P9_LOCK_BLOCKED as the status
|
||||
* for lock request, keep on trying
|
||||
*/
|
||||
for (;;) {
|
||||
res = p9_client_lock_dotl(fid, &flock, &status);
|
||||
if (res < 0)
|
||||
break;
|
||||
|
||||
if (status != P9_LOCK_BLOCKED)
|
||||
break;
|
||||
if (status == P9_LOCK_BLOCKED && !IS_SETLKW(cmd))
|
||||
break;
|
||||
schedule_timeout_interruptible(P9_LOCK_TIMEOUT);
|
||||
}
|
||||
|
||||
/* map 9p status to VFS status */
|
||||
switch (status) {
|
||||
case P9_LOCK_SUCCESS:
|
||||
res = 0;
|
||||
break;
|
||||
case P9_LOCK_BLOCKED:
|
||||
res = -EAGAIN;
|
||||
break;
|
||||
case P9_LOCK_ERROR:
|
||||
case P9_LOCK_GRACE:
|
||||
res = -ENOLCK;
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
|
||||
/*
|
||||
* incase server returned error for lock request, revert
|
||||
* it locally
|
||||
*/
|
||||
if (res < 0 && fl->fl_type != F_UNLCK) {
|
||||
fl_type = fl->fl_type;
|
||||
fl->fl_type = F_UNLCK;
|
||||
res = posix_lock_file_wait(filp, fl);
|
||||
fl->fl_type = fl_type;
|
||||
}
|
||||
out:
|
||||
return res;
|
||||
}
|
||||
|
||||
static int v9fs_file_getlock(struct file *filp, struct file_lock *fl)
|
||||
{
|
||||
struct p9_getlock glock;
|
||||
struct p9_fid *fid;
|
||||
int res = 0;
|
||||
|
||||
fid = filp->private_data;
|
||||
BUG_ON(fid == NULL);
|
||||
|
||||
posix_test_lock(filp, fl);
|
||||
/*
|
||||
* if we have a conflicting lock locally, no need to validate
|
||||
* with server
|
||||
*/
|
||||
if (fl->fl_type != F_UNLCK)
|
||||
return res;
|
||||
|
||||
/* convert posix lock to p9 tgetlock args */
|
||||
memset(&glock, 0, sizeof(glock));
|
||||
glock.type = fl->fl_type;
|
||||
glock.start = fl->fl_start;
|
||||
if (fl->fl_end == OFFSET_MAX)
|
||||
glock.length = 0;
|
||||
else
|
||||
glock.length = fl->fl_end - fl->fl_start + 1;
|
||||
glock.proc_id = fl->fl_pid;
|
||||
glock.client_id = utsname()->nodename;
|
||||
|
||||
res = p9_client_getlock_dotl(fid, &glock);
|
||||
if (res < 0)
|
||||
return res;
|
||||
if (glock.type != F_UNLCK) {
|
||||
fl->fl_type = glock.type;
|
||||
fl->fl_start = glock.start;
|
||||
if (glock.length == 0)
|
||||
fl->fl_end = OFFSET_MAX;
|
||||
else
|
||||
fl->fl_end = glock.start + glock.length - 1;
|
||||
fl->fl_pid = glock.proc_id;
|
||||
} else
|
||||
fl->fl_type = F_UNLCK;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_file_lock_dotl - lock a file (or directory)
|
||||
* @filp: file to be locked
|
||||
* @cmd: lock command
|
||||
* @fl: file lock structure
|
||||
*
|
||||
*/
|
||||
|
||||
static int v9fs_file_lock_dotl(struct file *filp, int cmd, struct file_lock *fl)
|
||||
{
|
||||
struct inode *inode = filp->f_path.dentry->d_inode;
|
||||
int ret = -ENOLCK;
|
||||
|
||||
P9_DPRINTK(P9_DEBUG_VFS, "filp: %p cmd:%d lock: %p name: %s\n", filp,
|
||||
cmd, fl, filp->f_path.dentry->d_name.name);
|
||||
|
||||
/* No mandatory locks */
|
||||
if (__mandatory_lock(inode) && fl->fl_type != F_UNLCK)
|
||||
goto out_err;
|
||||
|
||||
if ((IS_SETLK(cmd) || IS_SETLKW(cmd)) && fl->fl_type != F_UNLCK) {
|
||||
filemap_write_and_wait(inode->i_mapping);
|
||||
invalidate_mapping_pages(&inode->i_data, 0, -1);
|
||||
}
|
||||
|
||||
if (IS_SETLK(cmd) || IS_SETLKW(cmd))
|
||||
ret = v9fs_file_do_lock(filp, cmd, fl);
|
||||
else if (IS_GETLK(cmd))
|
||||
ret = v9fs_file_getlock(filp, fl);
|
||||
else
|
||||
ret = -EINVAL;
|
||||
out_err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_file_flock_dotl - lock a file
|
||||
* @filp: file to be locked
|
||||
* @cmd: lock command
|
||||
* @fl: file lock structure
|
||||
*
|
||||
*/
|
||||
|
||||
static int v9fs_file_flock_dotl(struct file *filp, int cmd,
|
||||
struct file_lock *fl)
|
||||
{
|
||||
struct inode *inode = filp->f_path.dentry->d_inode;
|
||||
int ret = -ENOLCK;
|
||||
|
||||
P9_DPRINTK(P9_DEBUG_VFS, "filp: %p cmd:%d lock: %p name: %s\n", filp,
|
||||
cmd, fl, filp->f_path.dentry->d_name.name);
|
||||
|
||||
/* No mandatory locks */
|
||||
if (__mandatory_lock(inode) && fl->fl_type != F_UNLCK)
|
||||
goto out_err;
|
||||
|
||||
if (!(fl->fl_flags & FL_FLOCK))
|
||||
goto out_err;
|
||||
|
||||
if ((IS_SETLK(cmd) || IS_SETLKW(cmd)) && fl->fl_type != F_UNLCK) {
|
||||
filemap_write_and_wait(inode->i_mapping);
|
||||
invalidate_mapping_pages(&inode->i_data, 0, -1);
|
||||
}
|
||||
/* Convert flock to posix lock */
|
||||
fl->fl_owner = (fl_owner_t)filp;
|
||||
fl->fl_start = 0;
|
||||
fl->fl_end = OFFSET_MAX;
|
||||
fl->fl_flags |= FL_POSIX;
|
||||
fl->fl_flags ^= FL_FLOCK;
|
||||
|
||||
if (IS_SETLK(cmd) | IS_SETLKW(cmd))
|
||||
ret = v9fs_file_do_lock(filp, cmd, fl);
|
||||
else
|
||||
ret = -EINVAL;
|
||||
out_err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_file_readn - read from a file
|
||||
* @filp: file pointer to read
|
||||
|
@ -219,7 +423,9 @@ static ssize_t
|
|||
v9fs_file_write(struct file *filp, const char __user * data,
|
||||
size_t count, loff_t * offset)
|
||||
{
|
||||
int n, rsize, total = 0;
|
||||
ssize_t retval;
|
||||
size_t total = 0;
|
||||
int n;
|
||||
struct p9_fid *fid;
|
||||
struct p9_client *clnt;
|
||||
struct inode *inode = filp->f_path.dentry->d_inode;
|
||||
|
@ -232,14 +438,19 @@ v9fs_file_write(struct file *filp, const char __user * data,
|
|||
fid = filp->private_data;
|
||||
clnt = fid->clnt;
|
||||
|
||||
rsize = fid->iounit ? fid->iounit : clnt->msize - P9_IOHDRSZ;
|
||||
retval = generic_write_checks(filp, &origin, &count, 0);
|
||||
if (retval)
|
||||
goto out;
|
||||
|
||||
retval = -EINVAL;
|
||||
if ((ssize_t) count < 0)
|
||||
goto out;
|
||||
retval = 0;
|
||||
if (!count)
|
||||
goto out;
|
||||
|
||||
do {
|
||||
if (count < rsize)
|
||||
rsize = count;
|
||||
|
||||
n = p9_client_write(fid, NULL, data+total, origin+total,
|
||||
rsize);
|
||||
n = p9_client_write(fid, NULL, data+total, origin+total, count);
|
||||
if (n <= 0)
|
||||
break;
|
||||
count -= n;
|
||||
|
@ -258,9 +469,11 @@ v9fs_file_write(struct file *filp, const char __user * data,
|
|||
}
|
||||
|
||||
if (n < 0)
|
||||
return n;
|
||||
|
||||
return total;
|
||||
retval = n;
|
||||
else
|
||||
retval = total;
|
||||
out:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int v9fs_file_fsync(struct file *filp, int datasync)
|
||||
|
@ -278,6 +491,20 @@ static int v9fs_file_fsync(struct file *filp, int datasync)
|
|||
return retval;
|
||||
}
|
||||
|
||||
int v9fs_file_fsync_dotl(struct file *filp, int datasync)
|
||||
{
|
||||
struct p9_fid *fid;
|
||||
int retval;
|
||||
|
||||
P9_DPRINTK(P9_DEBUG_VFS, "v9fs_file_fsync_dotl: filp %p datasync %x\n",
|
||||
filp, datasync);
|
||||
|
||||
fid = filp->private_data;
|
||||
|
||||
retval = p9_client_fsync(fid, datasync);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static const struct file_operations v9fs_cached_file_operations = {
|
||||
.llseek = generic_file_llseek,
|
||||
.read = do_sync_read,
|
||||
|
@ -290,6 +517,19 @@ static const struct file_operations v9fs_cached_file_operations = {
|
|||
.fsync = v9fs_file_fsync,
|
||||
};
|
||||
|
||||
static const struct file_operations v9fs_cached_file_operations_dotl = {
|
||||
.llseek = generic_file_llseek,
|
||||
.read = do_sync_read,
|
||||
.aio_read = generic_file_aio_read,
|
||||
.write = v9fs_file_write,
|
||||
.open = v9fs_file_open,
|
||||
.release = v9fs_dir_release,
|
||||
.lock = v9fs_file_lock_dotl,
|
||||
.flock = v9fs_file_flock_dotl,
|
||||
.mmap = generic_file_readonly_mmap,
|
||||
.fsync = v9fs_file_fsync_dotl,
|
||||
};
|
||||
|
||||
const struct file_operations v9fs_file_operations = {
|
||||
.llseek = generic_file_llseek,
|
||||
.read = v9fs_file_read,
|
||||
|
@ -307,7 +547,8 @@ const struct file_operations v9fs_file_operations_dotl = {
|
|||
.write = v9fs_file_write,
|
||||
.open = v9fs_file_open,
|
||||
.release = v9fs_dir_release,
|
||||
.lock = v9fs_file_lock,
|
||||
.lock = v9fs_file_lock_dotl,
|
||||
.flock = v9fs_file_flock_dotl,
|
||||
.mmap = generic_file_readonly_mmap,
|
||||
.fsync = v9fs_file_fsync,
|
||||
.fsync = v9fs_file_fsync_dotl,
|
||||
};
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/xattr.h>
|
||||
#include <linux/posix_acl.h>
|
||||
#include <net/9p/9p.h>
|
||||
#include <net/9p/client.h>
|
||||
|
||||
|
@ -44,6 +45,7 @@
|
|||
#include "fid.h"
|
||||
#include "cache.h"
|
||||
#include "xattr.h"
|
||||
#include "acl.h"
|
||||
|
||||
static const struct inode_operations v9fs_dir_inode_operations;
|
||||
static const struct inode_operations v9fs_dir_inode_operations_dotu;
|
||||
|
@ -53,6 +55,10 @@ static const struct inode_operations v9fs_file_inode_operations_dotl;
|
|||
static const struct inode_operations v9fs_symlink_inode_operations;
|
||||
static const struct inode_operations v9fs_symlink_inode_operations_dotl;
|
||||
|
||||
static int
|
||||
v9fs_vfs_mknod_dotl(struct inode *dir, struct dentry *dentry, int omode,
|
||||
dev_t rdev);
|
||||
|
||||
/**
|
||||
* unixmode2p9mode - convert unix mode bits to plan 9
|
||||
* @v9ses: v9fs session information
|
||||
|
@ -500,6 +506,11 @@ v9fs_inode_dotl(struct v9fs_session_info *v9ses, struct p9_fid *fid,
|
|||
v9fs_vcookie_set_qid(ret, &st->qid);
|
||||
v9fs_cache_inode_get_cookie(ret);
|
||||
#endif
|
||||
err = v9fs_get_acl(ret, fid);
|
||||
if (err) {
|
||||
iput(ret);
|
||||
goto error;
|
||||
}
|
||||
kfree(st);
|
||||
return ret;
|
||||
error:
|
||||
|
@ -553,13 +564,6 @@ static int v9fs_remove(struct inode *dir, struct dentry *file, int rmdir)
|
|||
return retval;
|
||||
}
|
||||
|
||||
static int
|
||||
v9fs_open_created(struct inode *inode, struct file *file)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* v9fs_create - Create a file
|
||||
* @v9ses: session information
|
||||
|
@ -655,29 +659,37 @@ error:
|
|||
*/
|
||||
|
||||
static int
|
||||
v9fs_vfs_create_dotl(struct inode *dir, struct dentry *dentry, int mode,
|
||||
v9fs_vfs_create_dotl(struct inode *dir, struct dentry *dentry, int omode,
|
||||
struct nameidata *nd)
|
||||
{
|
||||
int err = 0;
|
||||
char *name = NULL;
|
||||
gid_t gid;
|
||||
int flags;
|
||||
mode_t mode;
|
||||
struct v9fs_session_info *v9ses;
|
||||
struct p9_fid *fid = NULL;
|
||||
struct p9_fid *dfid, *ofid;
|
||||
struct file *filp;
|
||||
struct p9_qid qid;
|
||||
struct inode *inode;
|
||||
struct posix_acl *pacl = NULL, *dacl = NULL;
|
||||
|
||||
v9ses = v9fs_inode2v9ses(dir);
|
||||
if (nd && nd->flags & LOOKUP_OPEN)
|
||||
flags = nd->intent.open.flags - 1;
|
||||
else
|
||||
flags = O_RDWR;
|
||||
else {
|
||||
/*
|
||||
* create call without LOOKUP_OPEN is due
|
||||
* to mknod of regular files. So use mknod
|
||||
* operation.
|
||||
*/
|
||||
return v9fs_vfs_mknod_dotl(dir, dentry, omode, 0);
|
||||
}
|
||||
|
||||
name = (char *) dentry->d_name.name;
|
||||
P9_DPRINTK(P9_DEBUG_VFS, "v9fs_vfs_create_dotl: name:%s flags:0x%x "
|
||||
"mode:0x%x\n", name, flags, mode);
|
||||
"mode:0x%x\n", name, flags, omode);
|
||||
|
||||
dfid = v9fs_fid_lookup(dentry->d_parent);
|
||||
if (IS_ERR(dfid)) {
|
||||
|
@ -695,6 +707,15 @@ v9fs_vfs_create_dotl(struct inode *dir, struct dentry *dentry, int mode,
|
|||
}
|
||||
|
||||
gid = v9fs_get_fsgid_for_create(dir);
|
||||
|
||||
mode = omode;
|
||||
/* Update mode based on ACL value */
|
||||
err = v9fs_acl_mode(dir, &mode, &dacl, &pacl);
|
||||
if (err) {
|
||||
P9_DPRINTK(P9_DEBUG_VFS,
|
||||
"Failed to get acl values in creat %d\n", err);
|
||||
goto error;
|
||||
}
|
||||
err = p9_client_create_dotl(ofid, name, flags, mode, gid, &qid);
|
||||
if (err < 0) {
|
||||
P9_DPRINTK(P9_DEBUG_VFS,
|
||||
|
@ -702,46 +723,52 @@ v9fs_vfs_create_dotl(struct inode *dir, struct dentry *dentry, int mode,
|
|||
err);
|
||||
goto error;
|
||||
}
|
||||
/* instantiate inode and assign the unopened fid to the dentry */
|
||||
if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE ||
|
||||
(nd && nd->flags & LOOKUP_OPEN)) {
|
||||
fid = p9_client_walk(dfid, 1, &name, 1);
|
||||
if (IS_ERR(fid)) {
|
||||
err = PTR_ERR(fid);
|
||||
P9_DPRINTK(P9_DEBUG_VFS, "p9_client_walk failed %d\n",
|
||||
err);
|
||||
fid = NULL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* No need to populate the inode if we are not opening the file AND
|
||||
* not in cached mode.
|
||||
*/
|
||||
if (!v9ses->cache && !(nd && nd->flags & LOOKUP_OPEN)) {
|
||||
/* Not in cached mode. No need to populate inode with stat */
|
||||
dentry->d_op = &v9fs_dentry_operations;
|
||||
p9_client_clunk(ofid);
|
||||
d_instantiate(dentry, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Now walk from the parent so we can get an unopened fid. */
|
||||
fid = p9_client_walk(dfid, 1, &name, 1);
|
||||
if (IS_ERR(fid)) {
|
||||
err = PTR_ERR(fid);
|
||||
P9_DPRINTK(P9_DEBUG_VFS, "p9_client_walk failed %d\n", err);
|
||||
fid = NULL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* instantiate inode and assign the unopened fid to dentry */
|
||||
inode = v9fs_inode_from_fid(v9ses, fid, dir->i_sb);
|
||||
if (IS_ERR(inode)) {
|
||||
err = PTR_ERR(inode);
|
||||
P9_DPRINTK(P9_DEBUG_VFS, "inode creation failed %d\n", err);
|
||||
goto error;
|
||||
}
|
||||
if (v9ses->cache)
|
||||
inode = v9fs_inode_from_fid(v9ses, fid, dir->i_sb);
|
||||
if (IS_ERR(inode)) {
|
||||
err = PTR_ERR(inode);
|
||||
P9_DPRINTK(P9_DEBUG_VFS, "inode creation failed %d\n",
|
||||
err);
|
||||
goto error;
|
||||
}
|
||||
dentry->d_op = &v9fs_cached_dentry_operations;
|
||||
else
|
||||
d_instantiate(dentry, inode);
|
||||
err = v9fs_fid_add(dentry, fid);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
/* The fid would get clunked via a dput */
|
||||
fid = NULL;
|
||||
} else {
|
||||
/*
|
||||
* Not in cached mode. No need to populate
|
||||
* inode with stat. We need to get an inode
|
||||
* so that we can set the acl with dentry
|
||||
*/
|
||||
inode = v9fs_get_inode(dir->i_sb, mode);
|
||||
if (IS_ERR(inode)) {
|
||||
err = PTR_ERR(inode);
|
||||
goto error;
|
||||
}
|
||||
dentry->d_op = &v9fs_dentry_operations;
|
||||
d_instantiate(dentry, inode);
|
||||
err = v9fs_fid_add(dentry, fid);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
d_instantiate(dentry, inode);
|
||||
}
|
||||
/* Now set the ACL based on the default value */
|
||||
v9fs_set_create_acl(dentry, dacl, pacl);
|
||||
|
||||
/* if we are opening a file, assign the open fid to the file */
|
||||
if (nd && nd->flags & LOOKUP_OPEN) {
|
||||
filp = lookup_instantiate_filp(nd, dentry, v9fs_open_created);
|
||||
filp = lookup_instantiate_filp(nd, dentry, generic_file_open);
|
||||
if (IS_ERR(filp)) {
|
||||
p9_client_clunk(ofid);
|
||||
return PTR_ERR(filp);
|
||||
|
@ -800,7 +827,7 @@ v9fs_vfs_create(struct inode *dir, struct dentry *dentry, int mode,
|
|||
|
||||
/* if we are opening a file, assign the open fid to the file */
|
||||
if (nd && nd->flags & LOOKUP_OPEN) {
|
||||
filp = lookup_instantiate_filp(nd, dentry, v9fs_open_created);
|
||||
filp = lookup_instantiate_filp(nd, dentry, generic_file_open);
|
||||
if (IS_ERR(filp)) {
|
||||
err = PTR_ERR(filp);
|
||||
goto error;
|
||||
|
@ -859,23 +886,28 @@ static int v9fs_vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
|
|||
*
|
||||
*/
|
||||
|
||||
static int v9fs_vfs_mkdir_dotl(struct inode *dir, struct dentry *dentry,
|
||||
int mode)
|
||||
static int v9fs_vfs_mkdir_dotl(struct inode *dir,
|
||||
struct dentry *dentry, int omode)
|
||||
{
|
||||
int err;
|
||||
struct v9fs_session_info *v9ses;
|
||||
struct p9_fid *fid = NULL, *dfid = NULL;
|
||||
gid_t gid;
|
||||
char *name;
|
||||
mode_t mode;
|
||||
struct inode *inode;
|
||||
struct p9_qid qid;
|
||||
struct dentry *dir_dentry;
|
||||
struct posix_acl *dacl = NULL, *pacl = NULL;
|
||||
|
||||
P9_DPRINTK(P9_DEBUG_VFS, "name %s\n", dentry->d_name.name);
|
||||
err = 0;
|
||||
v9ses = v9fs_inode2v9ses(dir);
|
||||
|
||||
mode |= S_IFDIR;
|
||||
omode |= S_IFDIR;
|
||||
if (dir->i_mode & S_ISGID)
|
||||
omode |= S_ISGID;
|
||||
|
||||
dir_dentry = v9fs_dentry_from_dir_inode(dir);
|
||||
dfid = v9fs_fid_lookup(dir_dentry);
|
||||
if (IS_ERR(dfid)) {
|
||||
|
@ -886,11 +918,14 @@ static int v9fs_vfs_mkdir_dotl(struct inode *dir, struct dentry *dentry,
|
|||
}
|
||||
|
||||
gid = v9fs_get_fsgid_for_create(dir);
|
||||
if (gid < 0) {
|
||||
P9_DPRINTK(P9_DEBUG_VFS, "v9fs_get_fsgid_for_create failed\n");
|
||||
mode = omode;
|
||||
/* Update mode based on ACL value */
|
||||
err = v9fs_acl_mode(dir, &mode, &dacl, &pacl);
|
||||
if (err) {
|
||||
P9_DPRINTK(P9_DEBUG_VFS,
|
||||
"Failed to get acl values in mkdir %d\n", err);
|
||||
goto error;
|
||||
}
|
||||
|
||||
name = (char *) dentry->d_name.name;
|
||||
err = p9_client_mkdir_dotl(dfid, name, mode, gid, &qid);
|
||||
if (err < 0)
|
||||
|
@ -920,7 +955,23 @@ static int v9fs_vfs_mkdir_dotl(struct inode *dir, struct dentry *dentry,
|
|||
if (err < 0)
|
||||
goto error;
|
||||
fid = NULL;
|
||||
} else {
|
||||
/*
|
||||
* Not in cached mode. No need to populate
|
||||
* inode with stat. We need to get an inode
|
||||
* so that we can set the acl with dentry
|
||||
*/
|
||||
inode = v9fs_get_inode(dir->i_sb, mode);
|
||||
if (IS_ERR(inode)) {
|
||||
err = PTR_ERR(inode);
|
||||
goto error;
|
||||
}
|
||||
dentry->d_op = &v9fs_dentry_operations;
|
||||
d_instantiate(dentry, inode);
|
||||
}
|
||||
/* Now set the ACL based on the default value */
|
||||
v9fs_set_create_acl(dentry, dacl, pacl);
|
||||
|
||||
error:
|
||||
if (fid)
|
||||
p9_client_clunk(fid);
|
||||
|
@ -979,7 +1030,7 @@ static struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry,
|
|||
|
||||
result = v9fs_fid_add(dentry, fid);
|
||||
if (result < 0)
|
||||
goto error;
|
||||
goto error_iput;
|
||||
|
||||
inst_out:
|
||||
if (v9ses->cache)
|
||||
|
@ -990,6 +1041,8 @@ inst_out:
|
|||
d_add(dentry, inode);
|
||||
return NULL;
|
||||
|
||||
error_iput:
|
||||
iput(inode);
|
||||
error:
|
||||
p9_client_clunk(fid);
|
||||
|
||||
|
@ -1237,7 +1290,7 @@ static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr)
|
|||
*
|
||||
*/
|
||||
|
||||
static int v9fs_vfs_setattr_dotl(struct dentry *dentry, struct iattr *iattr)
|
||||
int v9fs_vfs_setattr_dotl(struct dentry *dentry, struct iattr *iattr)
|
||||
{
|
||||
int retval;
|
||||
struct v9fs_session_info *v9ses;
|
||||
|
@ -1279,6 +1332,12 @@ static int v9fs_vfs_setattr_dotl(struct dentry *dentry, struct iattr *iattr)
|
|||
|
||||
setattr_copy(dentry->d_inode, iattr);
|
||||
mark_inode_dirty(dentry->d_inode);
|
||||
if (iattr->ia_valid & ATTR_MODE) {
|
||||
/* We also want to update ACL when we update mode bits */
|
||||
retval = v9fs_acl_chmod(dentry);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1473,7 +1532,7 @@ static int v9fs_readlink(struct dentry *dentry, char *buffer, int buflen)
|
|||
if (IS_ERR(fid))
|
||||
return PTR_ERR(fid);
|
||||
|
||||
if (!v9fs_proto_dotu(v9ses) && !v9fs_proto_dotl(v9ses))
|
||||
if (!v9fs_proto_dotu(v9ses))
|
||||
return -EBADF;
|
||||
|
||||
st = p9_client_stat(fid);
|
||||
|
@ -1616,11 +1675,6 @@ v9fs_vfs_symlink_dotl(struct inode *dir, struct dentry *dentry,
|
|||
|
||||
gid = v9fs_get_fsgid_for_create(dir);
|
||||
|
||||
if (gid < 0) {
|
||||
P9_DPRINTK(P9_DEBUG_VFS, "v9fs_get_egid failed %d\n", gid);
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Server doesn't alter fid on TSYMLINK. Hence no need to clone it. */
|
||||
err = p9_client_symlink(dfid, name, (char *)symname, gid, &qid);
|
||||
|
||||
|
@ -1855,21 +1909,23 @@ v9fs_vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev)
|
|||
*
|
||||
*/
|
||||
static int
|
||||
v9fs_vfs_mknod_dotl(struct inode *dir, struct dentry *dentry, int mode,
|
||||
v9fs_vfs_mknod_dotl(struct inode *dir, struct dentry *dentry, int omode,
|
||||
dev_t rdev)
|
||||
{
|
||||
int err;
|
||||
char *name;
|
||||
mode_t mode;
|
||||
struct v9fs_session_info *v9ses;
|
||||
struct p9_fid *fid = NULL, *dfid = NULL;
|
||||
struct inode *inode;
|
||||
gid_t gid;
|
||||
struct p9_qid qid;
|
||||
struct dentry *dir_dentry;
|
||||
struct posix_acl *dacl = NULL, *pacl = NULL;
|
||||
|
||||
P9_DPRINTK(P9_DEBUG_VFS,
|
||||
" %lu,%s mode: %x MAJOR: %u MINOR: %u\n", dir->i_ino,
|
||||
dentry->d_name.name, mode, MAJOR(rdev), MINOR(rdev));
|
||||
dentry->d_name.name, omode, MAJOR(rdev), MINOR(rdev));
|
||||
|
||||
if (!new_valid_dev(rdev))
|
||||
return -EINVAL;
|
||||
|
@ -1885,11 +1941,14 @@ v9fs_vfs_mknod_dotl(struct inode *dir, struct dentry *dentry, int mode,
|
|||
}
|
||||
|
||||
gid = v9fs_get_fsgid_for_create(dir);
|
||||
if (gid < 0) {
|
||||
P9_DPRINTK(P9_DEBUG_VFS, "v9fs_get_fsgid_for_create failed\n");
|
||||
mode = omode;
|
||||
/* Update mode based on ACL value */
|
||||
err = v9fs_acl_mode(dir, &mode, &dacl, &pacl);
|
||||
if (err) {
|
||||
P9_DPRINTK(P9_DEBUG_VFS,
|
||||
"Failed to get acl values in mknod %d\n", err);
|
||||
goto error;
|
||||
}
|
||||
|
||||
name = (char *) dentry->d_name.name;
|
||||
|
||||
err = p9_client_mknod_dotl(dfid, name, mode, rdev, gid, &qid);
|
||||
|
@ -1933,13 +1992,68 @@ v9fs_vfs_mknod_dotl(struct inode *dir, struct dentry *dentry, int mode,
|
|||
dentry->d_op = &v9fs_dentry_operations;
|
||||
d_instantiate(dentry, inode);
|
||||
}
|
||||
|
||||
/* Now set the ACL based on the default value */
|
||||
v9fs_set_create_acl(dentry, dacl, pacl);
|
||||
error:
|
||||
if (fid)
|
||||
p9_client_clunk(fid);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int
|
||||
v9fs_vfs_readlink_dotl(struct dentry *dentry, char *buffer, int buflen)
|
||||
{
|
||||
int retval;
|
||||
struct p9_fid *fid;
|
||||
char *target = NULL;
|
||||
|
||||
P9_DPRINTK(P9_DEBUG_VFS, " %s\n", dentry->d_name.name);
|
||||
retval = -EPERM;
|
||||
fid = v9fs_fid_lookup(dentry);
|
||||
if (IS_ERR(fid))
|
||||
return PTR_ERR(fid);
|
||||
|
||||
retval = p9_client_readlink(fid, &target);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
|
||||
strncpy(buffer, target, buflen);
|
||||
P9_DPRINTK(P9_DEBUG_VFS, "%s -> %s\n", dentry->d_name.name, buffer);
|
||||
|
||||
retval = strnlen(buffer, buflen);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* v9fs_vfs_follow_link_dotl - follow a symlink path
|
||||
* @dentry: dentry for symlink
|
||||
* @nd: nameidata
|
||||
*
|
||||
*/
|
||||
|
||||
static void *
|
||||
v9fs_vfs_follow_link_dotl(struct dentry *dentry, struct nameidata *nd)
|
||||
{
|
||||
int len = 0;
|
||||
char *link = __getname();
|
||||
|
||||
P9_DPRINTK(P9_DEBUG_VFS, "%s n", dentry->d_name.name);
|
||||
|
||||
if (!link)
|
||||
link = ERR_PTR(-ENOMEM);
|
||||
else {
|
||||
len = v9fs_vfs_readlink_dotl(dentry, link, PATH_MAX);
|
||||
if (len < 0) {
|
||||
__putname(link);
|
||||
link = ERR_PTR(len);
|
||||
} else
|
||||
link[min(len, PATH_MAX-1)] = 0;
|
||||
}
|
||||
nd_set_link(nd, link);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const struct inode_operations v9fs_dir_inode_operations_dotu = {
|
||||
.create = v9fs_vfs_create,
|
||||
.lookup = v9fs_vfs_lookup,
|
||||
|
@ -1970,7 +2084,7 @@ static const struct inode_operations v9fs_dir_inode_operations_dotl = {
|
|||
.getxattr = generic_getxattr,
|
||||
.removexattr = generic_removexattr,
|
||||
.listxattr = v9fs_listxattr,
|
||||
|
||||
.check_acl = v9fs_check_acl,
|
||||
};
|
||||
|
||||
static const struct inode_operations v9fs_dir_inode_operations = {
|
||||
|
@ -1997,6 +2111,7 @@ static const struct inode_operations v9fs_file_inode_operations_dotl = {
|
|||
.getxattr = generic_getxattr,
|
||||
.removexattr = generic_removexattr,
|
||||
.listxattr = v9fs_listxattr,
|
||||
.check_acl = v9fs_check_acl,
|
||||
};
|
||||
|
||||
static const struct inode_operations v9fs_symlink_inode_operations = {
|
||||
|
@ -2008,8 +2123,8 @@ static const struct inode_operations v9fs_symlink_inode_operations = {
|
|||
};
|
||||
|
||||
static const struct inode_operations v9fs_symlink_inode_operations_dotl = {
|
||||
.readlink = generic_readlink,
|
||||
.follow_link = v9fs_vfs_follow_link,
|
||||
.readlink = v9fs_vfs_readlink_dotl,
|
||||
.follow_link = v9fs_vfs_follow_link_dotl,
|
||||
.put_link = v9fs_vfs_put_link,
|
||||
.getattr = v9fs_vfs_getattr_dotl,
|
||||
.setattr = v9fs_vfs_setattr_dotl,
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/statfs.h>
|
||||
#include <linux/magic.h>
|
||||
#include <net/9p/9p.h>
|
||||
#include <net/9p/client.h>
|
||||
|
||||
|
@ -46,6 +47,7 @@
|
|||
#include "v9fs_vfs.h"
|
||||
#include "fid.h"
|
||||
#include "xattr.h"
|
||||
#include "acl.h"
|
||||
|
||||
static const struct super_operations v9fs_super_ops, v9fs_super_ops_dotl;
|
||||
|
||||
|
@ -88,6 +90,11 @@ v9fs_fill_super(struct super_block *sb, struct v9fs_session_info *v9ses,
|
|||
sb->s_flags = flags | MS_ACTIVE | MS_SYNCHRONOUS | MS_DIRSYNC |
|
||||
MS_NOATIME;
|
||||
|
||||
#ifdef CONFIG_9P_FS_POSIX_ACL
|
||||
if ((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_CLIENT)
|
||||
sb->s_flags |= MS_POSIXACL;
|
||||
#endif
|
||||
|
||||
save_mount_options(sb, data);
|
||||
}
|
||||
|
||||
|
@ -149,7 +156,6 @@ static int v9fs_get_sb(struct file_system_type *fs_type, int flags,
|
|||
goto release_sb;
|
||||
}
|
||||
sb->s_root = root;
|
||||
|
||||
if (v9fs_proto_dotl(v9ses)) {
|
||||
struct p9_stat_dotl *st = NULL;
|
||||
st = p9_client_getattr_dotl(fid, P9_STATS_BASIC);
|
||||
|
@ -174,7 +180,9 @@ static int v9fs_get_sb(struct file_system_type *fs_type, int flags,
|
|||
p9stat_free(st);
|
||||
kfree(st);
|
||||
}
|
||||
|
||||
retval = v9fs_get_acl(inode, fid);
|
||||
if (retval)
|
||||
goto release_sb;
|
||||
v9fs_fid_add(root, fid);
|
||||
|
||||
P9_DPRINTK(P9_DEBUG_VFS, " simple set mount, return 0\n");
|
||||
|
@ -249,7 +257,7 @@ static int v9fs_statfs(struct dentry *dentry, struct kstatfs *buf)
|
|||
if (v9fs_proto_dotl(v9ses)) {
|
||||
res = p9_client_statfs(fid, &rs);
|
||||
if (res == 0) {
|
||||
buf->f_type = rs.type;
|
||||
buf->f_type = V9FS_MAGIC;
|
||||
buf->f_bsize = rs.bsize;
|
||||
buf->f_blocks = rs.blocks;
|
||||
buf->f_bfree = rs.bfree;
|
||||
|
|
|
@ -21,30 +21,13 @@
|
|||
#include "fid.h"
|
||||
#include "xattr.h"
|
||||
|
||||
/*
|
||||
* v9fs_xattr_get()
|
||||
*
|
||||
* Copy an extended attribute into the buffer
|
||||
* provided, or compute the buffer size required.
|
||||
* Buffer is NULL to compute the size of the buffer required.
|
||||
*
|
||||
* Returns a negative error number on failure, or the number of bytes
|
||||
* used / required on success.
|
||||
*/
|
||||
ssize_t v9fs_xattr_get(struct dentry *dentry, const char *name,
|
||||
void *buffer, size_t buffer_size)
|
||||
ssize_t v9fs_fid_xattr_get(struct p9_fid *fid, const char *name,
|
||||
void *buffer, size_t buffer_size)
|
||||
{
|
||||
ssize_t retval;
|
||||
int msize, read_count;
|
||||
u64 offset = 0, attr_size;
|
||||
struct p9_fid *fid, *attr_fid;
|
||||
|
||||
P9_DPRINTK(P9_DEBUG_VFS, "%s: name = %s value_len = %zu\n",
|
||||
__func__, name, buffer_size);
|
||||
|
||||
fid = v9fs_fid_lookup(dentry);
|
||||
if (IS_ERR(fid))
|
||||
return PTR_ERR(fid);
|
||||
struct p9_fid *attr_fid;
|
||||
|
||||
attr_fid = p9_client_xattrwalk(fid, name, &attr_size);
|
||||
if (IS_ERR(attr_fid)) {
|
||||
|
@ -88,6 +71,31 @@ error:
|
|||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* v9fs_xattr_get()
|
||||
*
|
||||
* Copy an extended attribute into the buffer
|
||||
* provided, or compute the buffer size required.
|
||||
* Buffer is NULL to compute the size of the buffer required.
|
||||
*
|
||||
* Returns a negative error number on failure, or the number of bytes
|
||||
* used / required on success.
|
||||
*/
|
||||
ssize_t v9fs_xattr_get(struct dentry *dentry, const char *name,
|
||||
void *buffer, size_t buffer_size)
|
||||
{
|
||||
struct p9_fid *fid;
|
||||
|
||||
P9_DPRINTK(P9_DEBUG_VFS, "%s: name = %s value_len = %zu\n",
|
||||
__func__, name, buffer_size);
|
||||
fid = v9fs_fid_lookup(dentry);
|
||||
if (IS_ERR(fid))
|
||||
return PTR_ERR(fid);
|
||||
|
||||
return v9fs_fid_xattr_get(fid, name, buffer, buffer_size);
|
||||
}
|
||||
|
||||
/*
|
||||
* v9fs_xattr_set()
|
||||
*
|
||||
|
@ -156,5 +164,9 @@ ssize_t v9fs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size)
|
|||
|
||||
const struct xattr_handler *v9fs_xattr_handlers[] = {
|
||||
&v9fs_xattr_user_handler,
|
||||
#ifdef CONFIG_9P_FS_POSIX_ACL
|
||||
&v9fs_xattr_acl_access_handler,
|
||||
&v9fs_xattr_acl_default_handler,
|
||||
#endif
|
||||
NULL
|
||||
};
|
||||
|
|
|
@ -15,10 +15,16 @@
|
|||
#define FS_9P_XATTR_H
|
||||
|
||||
#include <linux/xattr.h>
|
||||
#include <net/9p/9p.h>
|
||||
#include <net/9p/client.h>
|
||||
|
||||
extern const struct xattr_handler *v9fs_xattr_handlers[];
|
||||
extern struct xattr_handler v9fs_xattr_user_handler;
|
||||
extern const struct xattr_handler v9fs_xattr_acl_access_handler;
|
||||
extern const struct xattr_handler v9fs_xattr_acl_default_handler;
|
||||
|
||||
extern ssize_t v9fs_fid_xattr_get(struct p9_fid *, const char *,
|
||||
void *, size_t);
|
||||
extern ssize_t v9fs_xattr_get(struct dentry *, const char *,
|
||||
void *, size_t);
|
||||
extern int v9fs_xattr_set(struct dentry *, const char *,
|
||||
|
|
|
@ -57,5 +57,6 @@
|
|||
|
||||
#define DEVPTS_SUPER_MAGIC 0x1cd1
|
||||
#define SOCKFS_MAGIC 0x534F434B
|
||||
#define V9FS_MAGIC 0x01021997
|
||||
|
||||
#endif /* __LINUX_MAGIC_H__ */
|
||||
|
|
|
@ -86,6 +86,8 @@ do { \
|
|||
|
||||
/**
|
||||
* enum p9_msg_t - 9P message types
|
||||
* @P9_TLERROR: not used
|
||||
* @P9_RLERROR: response for any failed request for 9P2000.L
|
||||
* @P9_TSTATFS: file system status request
|
||||
* @P9_RSTATFS: file system status response
|
||||
* @P9_TSYMLINK: make symlink request
|
||||
|
@ -137,6 +139,8 @@ do { \
|
|||
*/
|
||||
|
||||
enum p9_msg_t {
|
||||
P9_TLERROR = 6,
|
||||
P9_RLERROR,
|
||||
P9_TSTATFS = 8,
|
||||
P9_RSTATFS,
|
||||
P9_TLOPEN = 12,
|
||||
|
@ -149,6 +153,8 @@ enum p9_msg_t {
|
|||
P9_RMKNOD,
|
||||
P9_TRENAME = 20,
|
||||
P9_RRENAME,
|
||||
P9_TREADLINK = 22,
|
||||
P9_RREADLINK,
|
||||
P9_TGETATTR = 24,
|
||||
P9_RGETATTR,
|
||||
P9_TSETATTR = 26,
|
||||
|
@ -159,6 +165,12 @@ enum p9_msg_t {
|
|||
P9_RXATTRCREATE,
|
||||
P9_TREADDIR = 40,
|
||||
P9_RREADDIR,
|
||||
P9_TFSYNC = 50,
|
||||
P9_RFSYNC,
|
||||
P9_TLOCK = 52,
|
||||
P9_RLOCK,
|
||||
P9_TGETLOCK = 54,
|
||||
P9_RGETLOCK,
|
||||
P9_TLINK = 70,
|
||||
P9_RLINK,
|
||||
P9_TMKDIR = 72,
|
||||
|
@ -458,6 +470,48 @@ struct p9_iattr_dotl {
|
|||
u64 mtime_nsec;
|
||||
};
|
||||
|
||||
#define P9_LOCK_SUCCESS 0
|
||||
#define P9_LOCK_BLOCKED 1
|
||||
#define P9_LOCK_ERROR 2
|
||||
#define P9_LOCK_GRACE 3
|
||||
|
||||
#define P9_LOCK_FLAGS_BLOCK 1
|
||||
#define P9_LOCK_FLAGS_RECLAIM 2
|
||||
|
||||
/* struct p9_flock: POSIX lock structure
|
||||
* @type - type of lock
|
||||
* @flags - lock flags
|
||||
* @start - starting offset of the lock
|
||||
* @length - number of bytes
|
||||
* @proc_id - process id which wants to take lock
|
||||
* @client_id - client id
|
||||
*/
|
||||
|
||||
struct p9_flock {
|
||||
u8 type;
|
||||
u32 flags;
|
||||
u64 start;
|
||||
u64 length;
|
||||
u32 proc_id;
|
||||
char *client_id;
|
||||
};
|
||||
|
||||
/* struct p9_getlock: getlock structure
|
||||
* @type - type of lock
|
||||
* @start - starting offset of the lock
|
||||
* @length - number of bytes
|
||||
* @proc_id - process id which wants to take lock
|
||||
* @client_id - client id
|
||||
*/
|
||||
|
||||
struct p9_getlock {
|
||||
u8 type;
|
||||
u64 start;
|
||||
u64 length;
|
||||
u32 proc_id;
|
||||
char *client_id;
|
||||
};
|
||||
|
||||
/* Structures for Protocol Operations */
|
||||
struct p9_tstatfs {
|
||||
u32 fid;
|
||||
|
|
|
@ -229,6 +229,7 @@ int p9_client_symlink(struct p9_fid *fid, char *name, char *symname, gid_t gid,
|
|||
int p9_client_create_dotl(struct p9_fid *ofid, char *name, u32 flags, u32 mode,
|
||||
gid_t gid, struct p9_qid *qid);
|
||||
int p9_client_clunk(struct p9_fid *fid);
|
||||
int p9_client_fsync(struct p9_fid *fid, int datasync);
|
||||
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);
|
||||
|
@ -248,6 +249,8 @@ int p9_client_mknod_dotl(struct p9_fid *oldfid, char *name, int mode,
|
|||
dev_t rdev, gid_t gid, struct p9_qid *);
|
||||
int p9_client_mkdir_dotl(struct p9_fid *fid, char *name, int mode,
|
||||
gid_t gid, struct p9_qid *);
|
||||
int p9_client_lock_dotl(struct p9_fid *fid, struct p9_flock *flock, u8 *status);
|
||||
int p9_client_getlock_dotl(struct p9_fid *fid, struct p9_getlock *fl);
|
||||
struct p9_req_t *p9_tag_lookup(struct p9_client *, u16);
|
||||
void p9_client_cb(struct p9_client *c, struct p9_req_t *req);
|
||||
|
||||
|
@ -259,5 +262,6 @@ int p9_is_proto_dotu(struct p9_client *clnt);
|
|||
int p9_is_proto_dotl(struct p9_client *clnt);
|
||||
struct p9_fid *p9_client_xattrwalk(struct p9_fid *, const char *, u64 *);
|
||||
int p9_client_xattrcreate(struct p9_fid *, const char *, u64, int);
|
||||
int p9_client_readlink(struct p9_fid *fid, char **target);
|
||||
|
||||
#endif /* NET_9P_CLIENT_H */
|
||||
|
|
178
net/9p/client.c
178
net/9p/client.c
|
@ -450,32 +450,43 @@ static int p9_check_errors(struct p9_client *c, struct p9_req_t *req)
|
|||
return err;
|
||||
}
|
||||
|
||||
if (type == P9_RERROR) {
|
||||
if (type == P9_RERROR || type == P9_RLERROR) {
|
||||
int ecode;
|
||||
char *ename;
|
||||
|
||||
err = p9pdu_readf(req->rc, c->proto_version, "s?d",
|
||||
&ename, &ecode);
|
||||
if (err) {
|
||||
P9_DPRINTK(P9_DEBUG_ERROR, "couldn't parse error%d\n",
|
||||
err);
|
||||
return err;
|
||||
}
|
||||
if (!p9_is_proto_dotl(c)) {
|
||||
char *ename;
|
||||
|
||||
if (p9_is_proto_dotu(c) ||
|
||||
p9_is_proto_dotl(c))
|
||||
err = p9pdu_readf(req->rc, c->proto_version, "s?d",
|
||||
&ename, &ecode);
|
||||
if (err)
|
||||
goto out_err;
|
||||
|
||||
if (p9_is_proto_dotu(c))
|
||||
err = -ecode;
|
||||
|
||||
if (!err || !IS_ERR_VALUE(err)) {
|
||||
err = p9_errstr2errno(ename, strlen(ename));
|
||||
|
||||
P9_DPRINTK(P9_DEBUG_9P, "<<< RERROR (%d) %s\n", -ecode, ename);
|
||||
|
||||
kfree(ename);
|
||||
}
|
||||
} else {
|
||||
err = p9pdu_readf(req->rc, c->proto_version, "d", &ecode);
|
||||
err = -ecode;
|
||||
|
||||
if (!err || !IS_ERR_VALUE(err))
|
||||
err = p9_errstr2errno(ename, strlen(ename));
|
||||
P9_DPRINTK(P9_DEBUG_9P, "<<< RLERROR (%d)\n", -ecode);
|
||||
}
|
||||
|
||||
P9_DPRINTK(P9_DEBUG_9P, "<<< RERROR (%d) %s\n", -ecode, ename);
|
||||
|
||||
kfree(ename);
|
||||
} else
|
||||
err = 0;
|
||||
|
||||
return err;
|
||||
|
||||
out_err:
|
||||
P9_DPRINTK(P9_DEBUG_ERROR, "couldn't parse error%d\n", err);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -568,11 +579,14 @@ p9_client_rpc(struct p9_client *c, int8_t type, const char *fmt, ...)
|
|||
va_start(ap, fmt);
|
||||
err = p9pdu_vwritef(req->tc, c->proto_version, fmt, ap);
|
||||
va_end(ap);
|
||||
if (err)
|
||||
goto reterr;
|
||||
p9pdu_finalize(req->tc);
|
||||
|
||||
err = c->trans_mod->request(c, req);
|
||||
if (err < 0) {
|
||||
c->status = Disconnected;
|
||||
if (err != -ERESTARTSYS)
|
||||
c->status = Disconnected;
|
||||
goto reterr;
|
||||
}
|
||||
|
||||
|
@ -1151,12 +1165,44 @@ int p9_client_link(struct p9_fid *dfid, struct p9_fid *oldfid, char *newname)
|
|||
}
|
||||
EXPORT_SYMBOL(p9_client_link);
|
||||
|
||||
int p9_client_fsync(struct p9_fid *fid, int datasync)
|
||||
{
|
||||
int err;
|
||||
struct p9_client *clnt;
|
||||
struct p9_req_t *req;
|
||||
|
||||
P9_DPRINTK(P9_DEBUG_9P, ">>> TFSYNC fid %d datasync:%d\n",
|
||||
fid->fid, datasync);
|
||||
err = 0;
|
||||
clnt = fid->clnt;
|
||||
|
||||
req = p9_client_rpc(clnt, P9_TFSYNC, "dd", fid->fid, datasync);
|
||||
if (IS_ERR(req)) {
|
||||
err = PTR_ERR(req);
|
||||
goto error;
|
||||
}
|
||||
|
||||
P9_DPRINTK(P9_DEBUG_9P, "<<< RFSYNC fid %d\n", fid->fid);
|
||||
|
||||
p9_free_req(clnt, req);
|
||||
|
||||
error:
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(p9_client_fsync);
|
||||
|
||||
int p9_client_clunk(struct p9_fid *fid)
|
||||
{
|
||||
int err;
|
||||
struct p9_client *clnt;
|
||||
struct p9_req_t *req;
|
||||
|
||||
if (!fid) {
|
||||
P9_EPRINTK(KERN_WARNING, "Trying to clunk with NULL fid\n");
|
||||
dump_stack();
|
||||
return 0;
|
||||
}
|
||||
|
||||
P9_DPRINTK(P9_DEBUG_9P, ">>> TCLUNK fid %d\n", fid->fid);
|
||||
err = 0;
|
||||
clnt = fid->clnt;
|
||||
|
@ -1240,16 +1286,13 @@ p9_client_read(struct p9_fid *fid, char *data, char __user *udata, u64 offset,
|
|||
|
||||
if (data) {
|
||||
memmove(data, dataptr, count);
|
||||
}
|
||||
|
||||
if (udata) {
|
||||
} else {
|
||||
err = copy_to_user(udata, dataptr, count);
|
||||
if (err) {
|
||||
err = -EFAULT;
|
||||
goto free_and_error;
|
||||
}
|
||||
}
|
||||
|
||||
p9_free_req(clnt, req);
|
||||
return count;
|
||||
|
||||
|
@ -1761,3 +1804,96 @@ error:
|
|||
|
||||
}
|
||||
EXPORT_SYMBOL(p9_client_mkdir_dotl);
|
||||
|
||||
int p9_client_lock_dotl(struct p9_fid *fid, struct p9_flock *flock, u8 *status)
|
||||
{
|
||||
int err;
|
||||
struct p9_client *clnt;
|
||||
struct p9_req_t *req;
|
||||
|
||||
err = 0;
|
||||
clnt = fid->clnt;
|
||||
P9_DPRINTK(P9_DEBUG_9P, ">>> TLOCK fid %d type %i flags %d "
|
||||
"start %lld length %lld proc_id %d client_id %s\n",
|
||||
fid->fid, flock->type, flock->flags, flock->start,
|
||||
flock->length, flock->proc_id, flock->client_id);
|
||||
|
||||
req = p9_client_rpc(clnt, P9_TLOCK, "dbdqqds", fid->fid, flock->type,
|
||||
flock->flags, flock->start, flock->length,
|
||||
flock->proc_id, flock->client_id);
|
||||
|
||||
if (IS_ERR(req))
|
||||
return PTR_ERR(req);
|
||||
|
||||
err = p9pdu_readf(req->rc, clnt->proto_version, "b", status);
|
||||
if (err) {
|
||||
p9pdu_dump(1, req->rc);
|
||||
goto error;
|
||||
}
|
||||
P9_DPRINTK(P9_DEBUG_9P, "<<< RLOCK status %i\n", *status);
|
||||
error:
|
||||
p9_free_req(clnt, req);
|
||||
return err;
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL(p9_client_lock_dotl);
|
||||
|
||||
int p9_client_getlock_dotl(struct p9_fid *fid, struct p9_getlock *glock)
|
||||
{
|
||||
int err;
|
||||
struct p9_client *clnt;
|
||||
struct p9_req_t *req;
|
||||
|
||||
err = 0;
|
||||
clnt = fid->clnt;
|
||||
P9_DPRINTK(P9_DEBUG_9P, ">>> TGETLOCK fid %d, type %i start %lld "
|
||||
"length %lld proc_id %d client_id %s\n", fid->fid, glock->type,
|
||||
glock->start, glock->length, glock->proc_id, glock->client_id);
|
||||
|
||||
req = p9_client_rpc(clnt, P9_TGETLOCK, "dbqqds", fid->fid, glock->type,
|
||||
glock->start, glock->length, glock->proc_id, glock->client_id);
|
||||
|
||||
if (IS_ERR(req))
|
||||
return PTR_ERR(req);
|
||||
|
||||
err = p9pdu_readf(req->rc, clnt->proto_version, "bqqds", &glock->type,
|
||||
&glock->start, &glock->length, &glock->proc_id,
|
||||
&glock->client_id);
|
||||
if (err) {
|
||||
p9pdu_dump(1, req->rc);
|
||||
goto error;
|
||||
}
|
||||
P9_DPRINTK(P9_DEBUG_9P, "<<< RGETLOCK type %i start %lld length %lld "
|
||||
"proc_id %d client_id %s\n", glock->type, glock->start,
|
||||
glock->length, glock->proc_id, glock->client_id);
|
||||
error:
|
||||
p9_free_req(clnt, req);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(p9_client_getlock_dotl);
|
||||
|
||||
int p9_client_readlink(struct p9_fid *fid, char **target)
|
||||
{
|
||||
int err;
|
||||
struct p9_client *clnt;
|
||||
struct p9_req_t *req;
|
||||
|
||||
err = 0;
|
||||
clnt = fid->clnt;
|
||||
P9_DPRINTK(P9_DEBUG_9P, ">>> TREADLINK fid %d\n", fid->fid);
|
||||
|
||||
req = p9_client_rpc(clnt, P9_TREADLINK, "d", fid->fid);
|
||||
if (IS_ERR(req))
|
||||
return PTR_ERR(req);
|
||||
|
||||
err = p9pdu_readf(req->rc, clnt->proto_version, "s", target);
|
||||
if (err) {
|
||||
p9pdu_dump(1, req->rc);
|
||||
goto error;
|
||||
}
|
||||
P9_DPRINTK(P9_DEBUG_9P, "<<< RREADLINK target %s\n", *target);
|
||||
error:
|
||||
p9_free_req(clnt, req);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(p9_client_readlink);
|
||||
|
|
|
@ -122,9 +122,8 @@ static size_t
|
|||
pdu_write_u(struct p9_fcall *pdu, const char __user *udata, size_t size)
|
||||
{
|
||||
size_t len = MIN(pdu->capacity - pdu->size, size);
|
||||
int err = copy_from_user(&pdu->sdata[pdu->size], udata, len);
|
||||
if (err)
|
||||
printk(KERN_WARNING "pdu_write_u returning: %d\n", err);
|
||||
if (copy_from_user(&pdu->sdata[pdu->size], udata, len))
|
||||
len = 0;
|
||||
|
||||
pdu->size += len;
|
||||
return size - len;
|
||||
|
|
|
@ -75,6 +75,8 @@ struct virtio_chan {
|
|||
struct p9_client *client;
|
||||
struct virtio_device *vdev;
|
||||
struct virtqueue *vq;
|
||||
int ring_bufs_avail;
|
||||
wait_queue_head_t *vc_wq;
|
||||
|
||||
/* Scatterlist: can be too big for stack. */
|
||||
struct scatterlist sg[VIRTQUEUE_NUM];
|
||||
|
@ -134,16 +136,30 @@ static void req_done(struct virtqueue *vq)
|
|||
struct p9_fcall *rc;
|
||||
unsigned int len;
|
||||
struct p9_req_t *req;
|
||||
unsigned long flags;
|
||||
|
||||
P9_DPRINTK(P9_DEBUG_TRANS, ": request done\n");
|
||||
|
||||
while ((rc = virtqueue_get_buf(chan->vq, &len)) != NULL) {
|
||||
P9_DPRINTK(P9_DEBUG_TRANS, ": rc %p\n", rc);
|
||||
P9_DPRINTK(P9_DEBUG_TRANS, ": lookup tag %d\n", rc->tag);
|
||||
req = p9_tag_lookup(chan->client, rc->tag);
|
||||
req->status = REQ_STATUS_RCVD;
|
||||
p9_client_cb(chan->client, req);
|
||||
}
|
||||
do {
|
||||
spin_lock_irqsave(&chan->lock, flags);
|
||||
rc = virtqueue_get_buf(chan->vq, &len);
|
||||
|
||||
if (rc != NULL) {
|
||||
if (!chan->ring_bufs_avail) {
|
||||
chan->ring_bufs_avail = 1;
|
||||
wake_up(chan->vc_wq);
|
||||
}
|
||||
spin_unlock_irqrestore(&chan->lock, flags);
|
||||
P9_DPRINTK(P9_DEBUG_TRANS, ": rc %p\n", rc);
|
||||
P9_DPRINTK(P9_DEBUG_TRANS, ": lookup tag %d\n",
|
||||
rc->tag);
|
||||
req = p9_tag_lookup(chan->client, rc->tag);
|
||||
req->status = REQ_STATUS_RCVD;
|
||||
p9_client_cb(chan->client, req);
|
||||
} else {
|
||||
spin_unlock_irqrestore(&chan->lock, flags);
|
||||
}
|
||||
} while (rc != NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -199,23 +215,43 @@ p9_virtio_request(struct p9_client *client, struct p9_req_t *req)
|
|||
int in, out;
|
||||
struct virtio_chan *chan = client->trans;
|
||||
char *rdata = (char *)req->rc+sizeof(struct p9_fcall);
|
||||
unsigned long flags;
|
||||
int err;
|
||||
|
||||
P9_DPRINTK(P9_DEBUG_TRANS, "9p debug: virtio request\n");
|
||||
|
||||
req_retry:
|
||||
req->status = REQ_STATUS_SENT;
|
||||
|
||||
spin_lock_irqsave(&chan->lock, flags);
|
||||
out = pack_sg_list(chan->sg, 0, VIRTQUEUE_NUM, req->tc->sdata,
|
||||
req->tc->size);
|
||||
in = pack_sg_list(chan->sg, out, VIRTQUEUE_NUM-out, rdata,
|
||||
client->msize);
|
||||
|
||||
req->status = REQ_STATUS_SENT;
|
||||
err = virtqueue_add_buf(chan->vq, chan->sg, out, in, req->tc);
|
||||
if (err < 0) {
|
||||
if (err == -ENOSPC) {
|
||||
chan->ring_bufs_avail = 0;
|
||||
spin_unlock_irqrestore(&chan->lock, flags);
|
||||
err = wait_event_interruptible(*chan->vc_wq,
|
||||
chan->ring_bufs_avail);
|
||||
if (err == -ERESTARTSYS)
|
||||
return err;
|
||||
|
||||
if (virtqueue_add_buf(chan->vq, chan->sg, out, in, req->tc) < 0) {
|
||||
P9_DPRINTK(P9_DEBUG_TRANS,
|
||||
"9p debug: virtio rpc add_buf returned failure");
|
||||
return -EIO;
|
||||
P9_DPRINTK(P9_DEBUG_TRANS, "9p:Retry virtio request\n");
|
||||
goto req_retry;
|
||||
} else {
|
||||
spin_unlock_irqrestore(&chan->lock, flags);
|
||||
P9_DPRINTK(P9_DEBUG_TRANS,
|
||||
"9p debug: "
|
||||
"virtio rpc add_buf returned failure");
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
virtqueue_kick(chan->vq);
|
||||
spin_unlock_irqrestore(&chan->lock, flags);
|
||||
|
||||
P9_DPRINTK(P9_DEBUG_TRANS, "9p debug: virtio request kicked\n");
|
||||
return 0;
|
||||
|
@ -290,14 +326,23 @@ static int p9_virtio_probe(struct virtio_device *vdev)
|
|||
chan->tag_len = tag_len;
|
||||
err = sysfs_create_file(&(vdev->dev.kobj), &dev_attr_mount_tag.attr);
|
||||
if (err) {
|
||||
kfree(tag);
|
||||
goto out_free_vq;
|
||||
goto out_free_tag;
|
||||
}
|
||||
chan->vc_wq = kmalloc(sizeof(wait_queue_head_t), GFP_KERNEL);
|
||||
if (!chan->vc_wq) {
|
||||
err = -ENOMEM;
|
||||
goto out_free_tag;
|
||||
}
|
||||
init_waitqueue_head(chan->vc_wq);
|
||||
chan->ring_bufs_avail = 1;
|
||||
|
||||
mutex_lock(&virtio_9p_lock);
|
||||
list_add_tail(&chan->chan_list, &virtio_chan_list);
|
||||
mutex_unlock(&virtio_9p_lock);
|
||||
return 0;
|
||||
|
||||
out_free_tag:
|
||||
kfree(tag);
|
||||
out_free_vq:
|
||||
vdev->config->del_vqs(vdev);
|
||||
kfree(chan);
|
||||
|
@ -371,6 +416,7 @@ static void p9_virtio_remove(struct virtio_device *vdev)
|
|||
mutex_unlock(&virtio_9p_lock);
|
||||
sysfs_remove_file(&(vdev->dev.kobj), &dev_attr_mount_tag.attr);
|
||||
kfree(chan->tag);
|
||||
kfree(chan->vc_wq);
|
||||
kfree(chan);
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue