1
linux/fs/xfs/linux-2.6/xfs_iops.c
Yingping Lu 3a69c7dc6f [XFS] Interim solution for attribute insertion failure during file
creation due to ENOSPC. The current solution removes the inode when the
attribute insertion fails. Long term solution would be to make the inode
creation and attribute insertion atomic.

SGI-PV: 947610
SGI-Modid: xfs-linux-melb:xfs-kern:205193a

Signed-off-by: Yingping Lu <yingping@sgi.com>
Signed-off-by: Nathan Scott <nathans@sgi.com>
2006-02-01 12:14:34 +11:00

859 lines
19 KiB
C

/*
* Copyright (c) 2000-2005 Silicon Graphics, Inc.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU 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. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "xfs.h"
#include "xfs_fs.h"
#include "xfs_bit.h"
#include "xfs_log.h"
#include "xfs_inum.h"
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
#include "xfs_dir.h"
#include "xfs_dir2.h"
#include "xfs_alloc.h"
#include "xfs_dmapi.h"
#include "xfs_quota.h"
#include "xfs_mount.h"
#include "xfs_bmap_btree.h"
#include "xfs_alloc_btree.h"
#include "xfs_ialloc_btree.h"
#include "xfs_dir_sf.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
#include "xfs_inode.h"
#include "xfs_bmap.h"
#include "xfs_btree.h"
#include "xfs_ialloc.h"
#include "xfs_rtalloc.h"
#include "xfs_error.h"
#include "xfs_itable.h"
#include "xfs_rw.h"
#include "xfs_acl.h"
#include "xfs_cap.h"
#include "xfs_mac.h"
#include "xfs_attr.h"
#include "xfs_buf_item.h"
#include "xfs_utils.h"
#include <linux/capability.h>
#include <linux/xattr.h>
#include <linux/namei.h>
#include <linux/security.h>
/*
* Get a XFS inode from a given vnode.
*/
xfs_inode_t *
xfs_vtoi(
struct vnode *vp)
{
bhv_desc_t *bdp;
bdp = bhv_lookup_range(VN_BHV_HEAD(vp),
VNODE_POSITION_XFS, VNODE_POSITION_XFS);
if (unlikely(bdp == NULL))
return NULL;
return XFS_BHVTOI(bdp);
}
/*
* Bring the atime in the XFS inode uptodate.
* Used before logging the inode to disk or when the Linux inode goes away.
*/
void
xfs_synchronize_atime(
xfs_inode_t *ip)
{
vnode_t *vp;
vp = XFS_ITOV_NULL(ip);
if (vp) {
struct inode *inode = &vp->v_inode;
ip->i_d.di_atime.t_sec = (__int32_t)inode->i_atime.tv_sec;
ip->i_d.di_atime.t_nsec = (__int32_t)inode->i_atime.tv_nsec;
}
}
/*
* Change the requested timestamp in the given inode.
* We don't lock across timestamp updates, and we don't log them but
* we do record the fact that there is dirty information in core.
*
* NOTE -- callers MUST combine XFS_ICHGTIME_MOD or XFS_ICHGTIME_CHG
* with XFS_ICHGTIME_ACC to be sure that access time
* update will take. Calling first with XFS_ICHGTIME_ACC
* and then XFS_ICHGTIME_MOD may fail to modify the access
* timestamp if the filesystem is mounted noacctm.
*/
void
xfs_ichgtime(
xfs_inode_t *ip,
int flags)
{
struct inode *inode = LINVFS_GET_IP(XFS_ITOV(ip));
timespec_t tv;
nanotime(&tv);
if (flags & XFS_ICHGTIME_MOD) {
inode->i_mtime = tv;
ip->i_d.di_mtime.t_sec = (__int32_t)tv.tv_sec;
ip->i_d.di_mtime.t_nsec = (__int32_t)tv.tv_nsec;
}
if (flags & XFS_ICHGTIME_ACC) {
inode->i_atime = tv;
ip->i_d.di_atime.t_sec = (__int32_t)tv.tv_sec;
ip->i_d.di_atime.t_nsec = (__int32_t)tv.tv_nsec;
}
if (flags & XFS_ICHGTIME_CHG) {
inode->i_ctime = tv;
ip->i_d.di_ctime.t_sec = (__int32_t)tv.tv_sec;
ip->i_d.di_ctime.t_nsec = (__int32_t)tv.tv_nsec;
}
/*
* We update the i_update_core field _after_ changing
* the timestamps in order to coordinate properly with
* xfs_iflush() so that we don't lose timestamp updates.
* This keeps us from having to hold the inode lock
* while doing this. We use the SYNCHRONIZE macro to
* ensure that the compiler does not reorder the update
* of i_update_core above the timestamp updates above.
*/
SYNCHRONIZE();
ip->i_update_core = 1;
if (!(inode->i_state & I_LOCK))
mark_inode_dirty_sync(inode);
}
/*
* Variant on the above which avoids querying the system clock
* in situations where we know the Linux inode timestamps have
* just been updated (and so we can update our inode cheaply).
*/
void
xfs_ichgtime_fast(
xfs_inode_t *ip,
struct inode *inode,
int flags)
{
timespec_t *tvp;
/*
* Atime updates for read() & friends are handled lazily now, and
* explicit updates must go through xfs_ichgtime()
*/
ASSERT((flags & XFS_ICHGTIME_ACC) == 0);
/*
* We're not supposed to change timestamps in readonly-mounted
* filesystems. Throw it away if anyone asks us.
*/
if (unlikely(IS_RDONLY(inode)))
return;
if (flags & XFS_ICHGTIME_MOD) {
tvp = &inode->i_mtime;
ip->i_d.di_mtime.t_sec = (__int32_t)tvp->tv_sec;
ip->i_d.di_mtime.t_nsec = (__int32_t)tvp->tv_nsec;
}
if (flags & XFS_ICHGTIME_CHG) {
tvp = &inode->i_ctime;
ip->i_d.di_ctime.t_sec = (__int32_t)tvp->tv_sec;
ip->i_d.di_ctime.t_nsec = (__int32_t)tvp->tv_nsec;
}
/*
* We update the i_update_core field _after_ changing
* the timestamps in order to coordinate properly with
* xfs_iflush() so that we don't lose timestamp updates.
* This keeps us from having to hold the inode lock
* while doing this. We use the SYNCHRONIZE macro to
* ensure that the compiler does not reorder the update
* of i_update_core above the timestamp updates above.
*/
SYNCHRONIZE();
ip->i_update_core = 1;
if (!(inode->i_state & I_LOCK))
mark_inode_dirty_sync(inode);
}
/*
* Pull the link count and size up from the xfs inode to the linux inode
*/
STATIC void
validate_fields(
struct inode *ip)
{
vnode_t *vp = LINVFS_GET_VP(ip);
vattr_t va;
int error;
va.va_mask = XFS_AT_NLINK|XFS_AT_SIZE|XFS_AT_NBLOCKS;
VOP_GETATTR(vp, &va, ATTR_LAZY, NULL, error);
if (likely(!error)) {
ip->i_nlink = va.va_nlink;
ip->i_blocks = va.va_nblocks;
/* we're under i_mutex so i_size can't change under us */
if (i_size_read(ip) != va.va_size)
i_size_write(ip, va.va_size);
}
}
/*
* Hook in SELinux. This is not quite correct yet, what we really need
* here (as we do for default ACLs) is a mechanism by which creation of
* these attrs can be journalled at inode creation time (along with the
* inode, of course, such that log replay can't cause these to be lost).
*/
STATIC int
linvfs_init_security(
struct vnode *vp,
struct inode *dir)
{
struct inode *ip = LINVFS_GET_IP(vp);
size_t length;
void *value;
char *name;
int error;
error = security_inode_init_security(ip, dir, &name, &value, &length);
if (error) {
if (error == -EOPNOTSUPP)
return 0;
return -error;
}
VOP_ATTR_SET(vp, name, value, length, ATTR_SECURE, NULL, error);
if (!error)
VMODIFY(vp);
kfree(name);
kfree(value);
return error;
}
/*
* Determine whether a process has a valid fs_struct (kernel daemons
* like knfsd don't have an fs_struct).
*
* XXX(hch): nfsd is broken, better fix it instead.
*/
STATIC inline int
has_fs_struct(struct task_struct *task)
{
return (task->fs != init_task.fs);
}
STATIC inline void
cleanup_inode(
vnode_t *dvp,
vnode_t *vp,
struct dentry *dentry,
int mode)
{
struct dentry teardown = {};
int err2;
/* Oh, the horror.
* If we can't add the ACL or we fail in
* linvfs_init_security we must back out.
* ENOSPC can hit here, among other things.
*/
teardown.d_inode = LINVFS_GET_IP(vp);
teardown.d_name = dentry->d_name;
if (S_ISDIR(mode))
VOP_RMDIR(dvp, &teardown, NULL, err2);
else
VOP_REMOVE(dvp, &teardown, NULL, err2);
VN_RELE(vp);
}
STATIC int
linvfs_mknod(
struct inode *dir,
struct dentry *dentry,
int mode,
dev_t rdev)
{
struct inode *ip;
vattr_t va;
vnode_t *vp = NULL, *dvp = LINVFS_GET_VP(dir);
xfs_acl_t *default_acl = NULL;
attrexists_t test_default_acl = _ACL_DEFAULT_EXISTS;
int error;
/*
* Irix uses Missed'em'V split, but doesn't want to see
* the upper 5 bits of (14bit) major.
*/
if (!sysv_valid_dev(rdev) || MAJOR(rdev) & ~0x1ff)
return -EINVAL;
if (test_default_acl && test_default_acl(dvp)) {
if (!_ACL_ALLOC(default_acl))
return -ENOMEM;
if (!_ACL_GET_DEFAULT(dvp, default_acl)) {
_ACL_FREE(default_acl);
default_acl = NULL;
}
}
if (IS_POSIXACL(dir) && !default_acl && has_fs_struct(current))
mode &= ~current->fs->umask;
memset(&va, 0, sizeof(va));
va.va_mask = XFS_AT_TYPE|XFS_AT_MODE;
va.va_mode = mode;
switch (mode & S_IFMT) {
case S_IFCHR: case S_IFBLK: case S_IFIFO: case S_IFSOCK:
va.va_rdev = sysv_encode_dev(rdev);
va.va_mask |= XFS_AT_RDEV;
/*FALLTHROUGH*/
case S_IFREG:
VOP_CREATE(dvp, dentry, &va, &vp, NULL, error);
break;
case S_IFDIR:
VOP_MKDIR(dvp, dentry, &va, &vp, NULL, error);
break;
default:
error = EINVAL;
break;
}
if (!error)
{
error = linvfs_init_security(vp, dir);
if (error)
cleanup_inode(dvp, vp, dentry, mode);
}
if (default_acl) {
if (!error) {
error = _ACL_INHERIT(vp, &va, default_acl);
if (!error)
VMODIFY(vp);
else
cleanup_inode(dvp, vp, dentry, mode);
}
_ACL_FREE(default_acl);
}
if (!error) {
ASSERT(vp);
ip = LINVFS_GET_IP(vp);
if (S_ISCHR(mode) || S_ISBLK(mode))
ip->i_rdev = rdev;
else if (S_ISDIR(mode))
validate_fields(ip);
d_instantiate(dentry, ip);
validate_fields(dir);
}
return -error;
}
STATIC int
linvfs_create(
struct inode *dir,
struct dentry *dentry,
int mode,
struct nameidata *nd)
{
return linvfs_mknod(dir, dentry, mode, 0);
}
STATIC int
linvfs_mkdir(
struct inode *dir,
struct dentry *dentry,
int mode)
{
return linvfs_mknod(dir, dentry, mode|S_IFDIR, 0);
}
STATIC struct dentry *
linvfs_lookup(
struct inode *dir,
struct dentry *dentry,
struct nameidata *nd)
{
struct vnode *vp = LINVFS_GET_VP(dir), *cvp;
int error;
if (dentry->d_name.len >= MAXNAMELEN)
return ERR_PTR(-ENAMETOOLONG);
VOP_LOOKUP(vp, dentry, &cvp, 0, NULL, NULL, error);
if (error) {
if (unlikely(error != ENOENT))
return ERR_PTR(-error);
d_add(dentry, NULL);
return NULL;
}
return d_splice_alias(LINVFS_GET_IP(cvp), dentry);
}
STATIC int
linvfs_link(
struct dentry *old_dentry,
struct inode *dir,
struct dentry *dentry)
{
struct inode *ip; /* inode of guy being linked to */
vnode_t *tdvp; /* target directory for new name/link */
vnode_t *vp; /* vp of name being linked */
int error;
ip = old_dentry->d_inode; /* inode being linked to */
if (S_ISDIR(ip->i_mode))
return -EPERM;
tdvp = LINVFS_GET_VP(dir);
vp = LINVFS_GET_VP(ip);
VOP_LINK(tdvp, vp, dentry, NULL, error);
if (!error) {
VMODIFY(tdvp);
VN_HOLD(vp);
validate_fields(ip);
d_instantiate(dentry, ip);
}
return -error;
}
STATIC int
linvfs_unlink(
struct inode *dir,
struct dentry *dentry)
{
struct inode *inode;
vnode_t *dvp; /* directory containing name to remove */
int error;
inode = dentry->d_inode;
dvp = LINVFS_GET_VP(dir);
VOP_REMOVE(dvp, dentry, NULL, error);
if (!error) {
validate_fields(dir); /* For size only */
validate_fields(inode);
}
return -error;
}
STATIC int
linvfs_symlink(
struct inode *dir,
struct dentry *dentry,
const char *symname)
{
struct inode *ip;
vattr_t va;
vnode_t *dvp; /* directory containing name of symlink */
vnode_t *cvp; /* used to lookup symlink to put in dentry */
int error;
dvp = LINVFS_GET_VP(dir);
cvp = NULL;
memset(&va, 0, sizeof(va));
va.va_mode = S_IFLNK |
(irix_symlink_mode ? 0777 & ~current->fs->umask : S_IRWXUGO);
va.va_mask = XFS_AT_TYPE|XFS_AT_MODE;
error = 0;
VOP_SYMLINK(dvp, dentry, &va, (char *)symname, &cvp, NULL, error);
if (likely(!error && cvp)) {
error = linvfs_init_security(cvp, dir);
if (likely(!error)) {
ip = LINVFS_GET_IP(cvp);
d_instantiate(dentry, ip);
validate_fields(dir);
validate_fields(ip);
}
}
return -error;
}
STATIC int
linvfs_rmdir(
struct inode *dir,
struct dentry *dentry)
{
struct inode *inode = dentry->d_inode;
vnode_t *dvp = LINVFS_GET_VP(dir);
int error;
VOP_RMDIR(dvp, dentry, NULL, error);
if (!error) {
validate_fields(inode);
validate_fields(dir);
}
return -error;
}
STATIC int
linvfs_rename(
struct inode *odir,
struct dentry *odentry,
struct inode *ndir,
struct dentry *ndentry)
{
struct inode *new_inode = ndentry->d_inode;
vnode_t *fvp; /* from directory */
vnode_t *tvp; /* target directory */
int error;
fvp = LINVFS_GET_VP(odir);
tvp = LINVFS_GET_VP(ndir);
VOP_RENAME(fvp, odentry, tvp, ndentry, NULL, error);
if (error)
return -error;
if (new_inode)
validate_fields(new_inode);
validate_fields(odir);
if (ndir != odir)
validate_fields(ndir);
return 0;
}
/*
* careful here - this function can get called recursively, so
* we need to be very careful about how much stack we use.
* uio is kmalloced for this reason...
*/
STATIC void *
linvfs_follow_link(
struct dentry *dentry,
struct nameidata *nd)
{
vnode_t *vp;
uio_t *uio;
iovec_t iov;
int error;
char *link;
ASSERT(dentry);
ASSERT(nd);
link = (char *)kmalloc(MAXPATHLEN+1, GFP_KERNEL);
if (!link) {
nd_set_link(nd, ERR_PTR(-ENOMEM));
return NULL;
}
uio = (uio_t *)kmalloc(sizeof(uio_t), GFP_KERNEL);
if (!uio) {
kfree(link);
nd_set_link(nd, ERR_PTR(-ENOMEM));
return NULL;
}
vp = LINVFS_GET_VP(dentry->d_inode);
iov.iov_base = link;
iov.iov_len = MAXPATHLEN;
uio->uio_iov = &iov;
uio->uio_offset = 0;
uio->uio_segflg = UIO_SYSSPACE;
uio->uio_resid = MAXPATHLEN;
uio->uio_iovcnt = 1;
VOP_READLINK(vp, uio, 0, NULL, error);
if (error) {
kfree(link);
link = ERR_PTR(-error);
} else {
link[MAXPATHLEN - uio->uio_resid] = '\0';
}
kfree(uio);
nd_set_link(nd, link);
return NULL;
}
STATIC void
linvfs_put_link(
struct dentry *dentry,
struct nameidata *nd,
void *p)
{
char *s = nd_get_link(nd);
if (!IS_ERR(s))
kfree(s);
}
#ifdef CONFIG_XFS_POSIX_ACL
STATIC int
linvfs_permission(
struct inode *inode,
int mode,
struct nameidata *nd)
{
vnode_t *vp = LINVFS_GET_VP(inode);
int error;
mode <<= 6; /* convert from linux to vnode access bits */
VOP_ACCESS(vp, mode, NULL, error);
return -error;
}
#else
#define linvfs_permission NULL
#endif
STATIC int
linvfs_getattr(
struct vfsmount *mnt,
struct dentry *dentry,
struct kstat *stat)
{
struct inode *inode = dentry->d_inode;
vnode_t *vp = LINVFS_GET_VP(inode);
int error = 0;
if (unlikely(vp->v_flag & VMODIFIED))
error = vn_revalidate(vp);
if (!error)
generic_fillattr(inode, stat);
return 0;
}
STATIC int
linvfs_setattr(
struct dentry *dentry,
struct iattr *attr)
{
struct inode *inode = dentry->d_inode;
unsigned int ia_valid = attr->ia_valid;
vnode_t *vp = LINVFS_GET_VP(inode);
vattr_t vattr;
int flags = 0;
int error;
memset(&vattr, 0, sizeof(vattr_t));
if (ia_valid & ATTR_UID) {
vattr.va_mask |= XFS_AT_UID;
vattr.va_uid = attr->ia_uid;
}
if (ia_valid & ATTR_GID) {
vattr.va_mask |= XFS_AT_GID;
vattr.va_gid = attr->ia_gid;
}
if (ia_valid & ATTR_SIZE) {
vattr.va_mask |= XFS_AT_SIZE;
vattr.va_size = attr->ia_size;
}
if (ia_valid & ATTR_ATIME) {
vattr.va_mask |= XFS_AT_ATIME;
vattr.va_atime = attr->ia_atime;
}
if (ia_valid & ATTR_MTIME) {
vattr.va_mask |= XFS_AT_MTIME;
vattr.va_mtime = attr->ia_mtime;
}
if (ia_valid & ATTR_CTIME) {
vattr.va_mask |= XFS_AT_CTIME;
vattr.va_ctime = attr->ia_ctime;
}
if (ia_valid & ATTR_MODE) {
vattr.va_mask |= XFS_AT_MODE;
vattr.va_mode = attr->ia_mode;
if (!in_group_p(inode->i_gid) && !capable(CAP_FSETID))
inode->i_mode &= ~S_ISGID;
}
if (ia_valid & (ATTR_MTIME_SET | ATTR_ATIME_SET))
flags |= ATTR_UTIME;
#ifdef ATTR_NO_BLOCK
if ((ia_valid & ATTR_NO_BLOCK))
flags |= ATTR_NONBLOCK;
#endif
VOP_SETATTR(vp, &vattr, flags, NULL, error);
if (error)
return -error;
vn_revalidate(vp);
return error;
}
STATIC void
linvfs_truncate(
struct inode *inode)
{
block_truncate_page(inode->i_mapping, inode->i_size, linvfs_get_block);
}
STATIC int
linvfs_setxattr(
struct dentry *dentry,
const char *name,
const void *data,
size_t size,
int flags)
{
vnode_t *vp = LINVFS_GET_VP(dentry->d_inode);
char *attr = (char *)name;
attrnames_t *namesp;
int xflags = 0;
int error;
namesp = attr_lookup_namespace(attr, attr_namespaces, ATTR_NAMECOUNT);
if (!namesp)
return -EOPNOTSUPP;
attr += namesp->attr_namelen;
error = namesp->attr_capable(vp, NULL);
if (error)
return error;
/* Convert Linux syscall to XFS internal ATTR flags */
if (flags & XATTR_CREATE)
xflags |= ATTR_CREATE;
if (flags & XATTR_REPLACE)
xflags |= ATTR_REPLACE;
xflags |= namesp->attr_flag;
return namesp->attr_set(vp, attr, (void *)data, size, xflags);
}
STATIC ssize_t
linvfs_getxattr(
struct dentry *dentry,
const char *name,
void *data,
size_t size)
{
vnode_t *vp = LINVFS_GET_VP(dentry->d_inode);
char *attr = (char *)name;
attrnames_t *namesp;
int xflags = 0;
ssize_t error;
namesp = attr_lookup_namespace(attr, attr_namespaces, ATTR_NAMECOUNT);
if (!namesp)
return -EOPNOTSUPP;
attr += namesp->attr_namelen;
error = namesp->attr_capable(vp, NULL);
if (error)
return error;
/* Convert Linux syscall to XFS internal ATTR flags */
if (!size) {
xflags |= ATTR_KERNOVAL;
data = NULL;
}
xflags |= namesp->attr_flag;
return namesp->attr_get(vp, attr, (void *)data, size, xflags);
}
STATIC ssize_t
linvfs_listxattr(
struct dentry *dentry,
char *data,
size_t size)
{
vnode_t *vp = LINVFS_GET_VP(dentry->d_inode);
int error, xflags = ATTR_KERNAMELS;
ssize_t result;
if (!size)
xflags |= ATTR_KERNOVAL;
xflags |= capable(CAP_SYS_ADMIN) ? ATTR_KERNFULLS : ATTR_KERNORMALS;
error = attr_generic_list(vp, data, size, xflags, &result);
if (error < 0)
return error;
return result;
}
STATIC int
linvfs_removexattr(
struct dentry *dentry,
const char *name)
{
vnode_t *vp = LINVFS_GET_VP(dentry->d_inode);
char *attr = (char *)name;
attrnames_t *namesp;
int xflags = 0;
int error;
namesp = attr_lookup_namespace(attr, attr_namespaces, ATTR_NAMECOUNT);
if (!namesp)
return -EOPNOTSUPP;
attr += namesp->attr_namelen;
error = namesp->attr_capable(vp, NULL);
if (error)
return error;
xflags |= namesp->attr_flag;
return namesp->attr_remove(vp, attr, xflags);
}
struct inode_operations linvfs_file_inode_operations = {
.permission = linvfs_permission,
.truncate = linvfs_truncate,
.getattr = linvfs_getattr,
.setattr = linvfs_setattr,
.setxattr = linvfs_setxattr,
.getxattr = linvfs_getxattr,
.listxattr = linvfs_listxattr,
.removexattr = linvfs_removexattr,
};
struct inode_operations linvfs_dir_inode_operations = {
.create = linvfs_create,
.lookup = linvfs_lookup,
.link = linvfs_link,
.unlink = linvfs_unlink,
.symlink = linvfs_symlink,
.mkdir = linvfs_mkdir,
.rmdir = linvfs_rmdir,
.mknod = linvfs_mknod,
.rename = linvfs_rename,
.permission = linvfs_permission,
.getattr = linvfs_getattr,
.setattr = linvfs_setattr,
.setxattr = linvfs_setxattr,
.getxattr = linvfs_getxattr,
.listxattr = linvfs_listxattr,
.removexattr = linvfs_removexattr,
};
struct inode_operations linvfs_symlink_inode_operations = {
.readlink = generic_readlink,
.follow_link = linvfs_follow_link,
.put_link = linvfs_put_link,
.permission = linvfs_permission,
.getattr = linvfs_getattr,
.setattr = linvfs_setattr,
.setxattr = linvfs_setxattr,
.getxattr = linvfs_getxattr,
.listxattr = linvfs_listxattr,
.removexattr = linvfs_removexattr,
};