forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Mike Marshall <[email protected]>
- Loading branch information
Showing
6 changed files
with
3,196 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
/* | ||
* (C) 2001 Clemson University and The University of Chicago | ||
* | ||
* See COPYING in top-level directory. | ||
*/ | ||
|
||
#include "protocol.h" | ||
#include "pvfs2-kernel.h" | ||
#include "pvfs2-bufmap.h" | ||
#include <linux/posix_acl_xattr.h> | ||
#include <linux/fs_struct.h> | ||
|
||
struct posix_acl *pvfs2_get_acl(struct inode *inode, int type) | ||
{ | ||
struct posix_acl *acl; | ||
int ret; | ||
char *key = NULL, *value = NULL; | ||
|
||
switch (type) { | ||
case ACL_TYPE_ACCESS: | ||
key = PVFS2_XATTR_NAME_ACL_ACCESS; | ||
break; | ||
case ACL_TYPE_DEFAULT: | ||
key = PVFS2_XATTR_NAME_ACL_DEFAULT; | ||
break; | ||
default: | ||
gossip_err("pvfs2_get_acl: bogus value of type %d\n", type); | ||
return ERR_PTR(-EINVAL); | ||
} | ||
/* | ||
* Rather than incurring a network call just to determine the exact | ||
* length of the attribute, I just allocate a max length to save on | ||
* the network call. Conceivably, we could pass NULL to | ||
* pvfs2_inode_getxattr() to probe the length of the value, but | ||
* I don't do that for now. | ||
*/ | ||
value = kmalloc(PVFS_MAX_XATTR_VALUELEN, GFP_KERNEL); | ||
if (value == NULL) | ||
return ERR_PTR(-ENOMEM); | ||
|
||
gossip_debug(GOSSIP_ACL_DEBUG, | ||
"inode %pU, key %s, type %d\n", | ||
get_khandle_from_ino(inode), | ||
key, | ||
type); | ||
ret = pvfs2_inode_getxattr(inode, | ||
"", | ||
key, | ||
value, | ||
PVFS_MAX_XATTR_VALUELEN); | ||
/* if the key exists, convert it to an in-memory rep */ | ||
if (ret > 0) { | ||
acl = posix_acl_from_xattr(&init_user_ns, value, ret); | ||
} else if (ret == -ENODATA || ret == -ENOSYS) { | ||
acl = NULL; | ||
} else { | ||
gossip_err("inode %pU retrieving acl's failed with error %d\n", | ||
get_khandle_from_ino(inode), | ||
ret); | ||
acl = ERR_PTR(ret); | ||
} | ||
/* kfree(NULL) is safe, so don't worry if value ever got used */ | ||
kfree(value); | ||
return acl; | ||
} | ||
|
||
int pvfs2_set_acl(struct inode *inode, struct posix_acl *acl, int type) | ||
{ | ||
struct pvfs2_inode_s *pvfs2_inode = PVFS2_I(inode); | ||
int error = 0; | ||
void *value = NULL; | ||
size_t size = 0; | ||
const char *name = NULL; | ||
|
||
switch (type) { | ||
case ACL_TYPE_ACCESS: | ||
name = PVFS2_XATTR_NAME_ACL_ACCESS; | ||
if (acl) { | ||
umode_t mode = inode->i_mode; | ||
/* | ||
* can we represent this with the traditional file | ||
* mode permission bits? | ||
*/ | ||
error = posix_acl_equiv_mode(acl, &mode); | ||
if (error < 0) { | ||
gossip_err("%s: posix_acl_equiv_mode err: %d\n", | ||
__func__, | ||
error); | ||
return error; | ||
} | ||
|
||
if (inode->i_mode != mode) | ||
SetModeFlag(pvfs2_inode); | ||
inode->i_mode = mode; | ||
mark_inode_dirty_sync(inode); | ||
if (error == 0) | ||
acl = NULL; | ||
} | ||
break; | ||
case ACL_TYPE_DEFAULT: | ||
name = PVFS2_XATTR_NAME_ACL_DEFAULT; | ||
break; | ||
default: | ||
gossip_err("%s: invalid type %d!\n", __func__, type); | ||
return -EINVAL; | ||
} | ||
|
||
gossip_debug(GOSSIP_ACL_DEBUG, | ||
"%s: inode %pU, key %s type %d\n", | ||
__func__, get_khandle_from_ino(inode), | ||
name, | ||
type); | ||
|
||
if (acl) { | ||
size = posix_acl_xattr_size(acl->a_count); | ||
value = kmalloc(size, GFP_KERNEL); | ||
if (!value) | ||
return -ENOMEM; | ||
|
||
error = posix_acl_to_xattr(&init_user_ns, acl, value, size); | ||
if (error < 0) | ||
goto out; | ||
} | ||
|
||
gossip_debug(GOSSIP_ACL_DEBUG, | ||
"%s: name %s, value %p, size %zd, acl %p\n", | ||
__func__, name, value, size, acl); | ||
/* | ||
* Go ahead and set the extended attribute now. NOTE: Suppose acl | ||
* was NULL, then value will be NULL and size will be 0 and that | ||
* will xlate to a removexattr. However, we don't want removexattr | ||
* complain if attributes does not exist. | ||
*/ | ||
error = pvfs2_inode_setxattr(inode, "", name, value, size, 0); | ||
|
||
out: | ||
kfree(value); | ||
if (!error) | ||
set_cached_acl(inode, type, acl); | ||
return error; | ||
} | ||
|
||
int pvfs2_init_acl(struct inode *inode, struct inode *dir) | ||
{ | ||
struct pvfs2_inode_s *pvfs2_inode = PVFS2_I(inode); | ||
struct posix_acl *default_acl, *acl; | ||
umode_t mode = inode->i_mode; | ||
int error = 0; | ||
|
||
ClearModeFlag(pvfs2_inode); | ||
|
||
error = posix_acl_create(dir, &mode, &default_acl, &acl); | ||
if (error) | ||
return error; | ||
|
||
if (default_acl) { | ||
error = pvfs2_set_acl(inode, default_acl, ACL_TYPE_DEFAULT); | ||
posix_acl_release(default_acl); | ||
} | ||
|
||
if (acl) { | ||
if (!error) | ||
error = pvfs2_set_acl(inode, acl, ACL_TYPE_ACCESS); | ||
posix_acl_release(acl); | ||
} | ||
|
||
/* If mode of the inode was changed, then do a forcible ->setattr */ | ||
if (mode != inode->i_mode) { | ||
SetModeFlag(pvfs2_inode); | ||
inode->i_mode = mode; | ||
pvfs2_flush_inode(inode); | ||
} | ||
|
||
return error; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
/* | ||
* (C) 2001 Clemson University and The University of Chicago | ||
* | ||
* See COPYING in top-level directory. | ||
*/ | ||
|
||
/* | ||
* Implementation of dentry (directory cache) functions. | ||
*/ | ||
|
||
#include "protocol.h" | ||
#include "pvfs2-kernel.h" | ||
|
||
/* Returns 1 if dentry can still be trusted, else 0. */ | ||
static int pvfs2_revalidate_lookup(struct dentry *dentry) | ||
{ | ||
struct dentry *parent_dentry = dget_parent(dentry); | ||
struct inode *parent_inode = parent_dentry->d_inode; | ||
struct pvfs2_inode_s *parent = PVFS2_I(parent_inode); | ||
struct inode *inode = dentry->d_inode; | ||
struct pvfs2_kernel_op_s *new_op; | ||
int ret = 0; | ||
int err = 0; | ||
|
||
gossip_debug(GOSSIP_DCACHE_DEBUG, "%s: attempting lookup.\n", __func__); | ||
|
||
new_op = op_alloc(PVFS2_VFS_OP_LOOKUP); | ||
if (!new_op) | ||
goto out_put_parent; | ||
|
||
new_op->upcall.req.lookup.sym_follow = PVFS2_LOOKUP_LINK_NO_FOLLOW; | ||
new_op->upcall.req.lookup.parent_refn = parent->refn; | ||
strncpy(new_op->upcall.req.lookup.d_name, | ||
dentry->d_name.name, | ||
PVFS2_NAME_LEN); | ||
|
||
gossip_debug(GOSSIP_DCACHE_DEBUG, | ||
"%s:%s:%d interrupt flag [%d]\n", | ||
__FILE__, | ||
__func__, | ||
__LINE__, | ||
get_interruptible_flag(parent_inode)); | ||
|
||
err = service_operation(new_op, "pvfs2_lookup", | ||
get_interruptible_flag(parent_inode)); | ||
if (err) | ||
goto out_drop; | ||
|
||
if (new_op->downcall.status != 0 || | ||
!match_handle(new_op->downcall.resp.lookup.refn.khandle, inode)) { | ||
gossip_debug(GOSSIP_DCACHE_DEBUG, | ||
"%s:%s:%d " | ||
"lookup failure |%s| or no match |%s|.\n", | ||
__FILE__, | ||
__func__, | ||
__LINE__, | ||
new_op->downcall.status ? "true" : "false", | ||
match_handle(new_op->downcall.resp.lookup.refn.khandle, | ||
inode) ? "false" : "true"); | ||
gossip_debug(GOSSIP_DCACHE_DEBUG, | ||
"%s:%s:%d revalidate failed\n", | ||
__FILE__, __func__, __LINE__); | ||
goto out_drop; | ||
} | ||
|
||
ret = 1; | ||
out_release_op: | ||
op_release(new_op); | ||
out_put_parent: | ||
dput(parent_dentry); | ||
return ret; | ||
out_drop: | ||
d_drop(dentry); | ||
goto out_release_op; | ||
} | ||
|
||
/* | ||
* Verify that dentry is valid. | ||
* | ||
* Should return 1 if dentry can still be trusted, else 0 | ||
*/ | ||
static int pvfs2_d_revalidate(struct dentry *dentry, unsigned int flags) | ||
{ | ||
struct inode *inode; | ||
int ret = 0; | ||
|
||
if (flags & LOOKUP_RCU) | ||
return -ECHILD; | ||
|
||
gossip_debug(GOSSIP_DCACHE_DEBUG, "%s: called on dentry %p.\n", | ||
__func__, dentry); | ||
|
||
/* find inode from dentry */ | ||
if (!dentry->d_inode) { | ||
gossip_debug(GOSSIP_DCACHE_DEBUG, "%s: negative dentry.\n", | ||
__func__); | ||
goto invalid_exit; | ||
} | ||
|
||
gossip_debug(GOSSIP_DCACHE_DEBUG, "%s: inode valid.\n", __func__); | ||
inode = dentry->d_inode; | ||
|
||
/* | ||
* first perform a lookup to make sure that the object not only | ||
* exists, but is still in the expected place in the name space | ||
*/ | ||
if (!is_root_handle(inode)) { | ||
if (!pvfs2_revalidate_lookup(dentry)) | ||
goto invalid_exit; | ||
} else { | ||
gossip_debug(GOSSIP_DCACHE_DEBUG, | ||
"%s: root handle, lookup skipped.\n", | ||
__func__); | ||
} | ||
|
||
/* now perform getattr */ | ||
gossip_debug(GOSSIP_DCACHE_DEBUG, | ||
"%s: doing getattr: inode: %p, handle: %pU\n", | ||
__func__, | ||
inode, | ||
get_khandle_from_ino(inode)); | ||
ret = pvfs2_inode_getattr(inode, PVFS_ATTR_SYS_ALL_NOHINT); | ||
gossip_debug(GOSSIP_DCACHE_DEBUG, | ||
"%s: getattr %s (ret = %d), returning %s for dentry i_count=%d\n", | ||
__func__, | ||
(ret == 0 ? "succeeded" : "failed"), | ||
ret, | ||
(ret == 0 ? "valid" : "INVALID"), | ||
atomic_read(&inode->i_count)); | ||
if (ret != 0) | ||
goto invalid_exit; | ||
|
||
/* dentry is valid! */ | ||
return 1; | ||
|
||
invalid_exit: | ||
return 0; | ||
} | ||
|
||
const struct dentry_operations pvfs2_dentry_operations = { | ||
.d_revalidate = pvfs2_d_revalidate, | ||
}; |
Oops, something went wrong.