mirror of
https://github.com/torvalds/linux.git
synced 2024-11-13 23:51:39 +00:00
fd92000878
Currently attributes are modified directly across one or more transactions. But they are not logged or replayed in the event of an error. The goal of log attr replay is to enable logging and replaying of attribute operations using the existing delayed operations infrastructure. This will later enable the attributes to become part of larger multi part operations that also must first be recorded to the log. This is mostly of interest in the scheme of parent pointers which would need to maintain an attribute containing parent inode information any time an inode is moved, created, or removed. Parent pointers would then be of interest to any feature that would need to quickly derive an inode path from the mount point. Online scrub, nfs lookups and fs grow or shrink operations are all features that could take advantage of this. This patch adds two new log item types for setting or removing attributes as deferred operations. The xfs_attri_log_item will log an intent to set or remove an attribute. The corresponding xfs_attrd_log_item holds a reference to the xfs_attri_log_item and is freed once the transaction is done. Both log items use a generic xfs_attr_log_format structure that contains the attribute name, value, flags, inode, and an op_flag that indicates if the operations is a set or remove. [dchinner: added extra little bits needed for intent whiteouts] Signed-off-by: Allison Henderson <allison.henderson@oracle.com> Reviewed-by: Chandan Babu R <chandanrlinux@gmail.com> Reviewed-by: Darrick J. Wong <djwong@kernel.org> Signed-off-by: Dave Chinner <david@fromorbit.com>
518 lines
14 KiB
C
518 lines
14 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (c) 2004-2005 Silicon Graphics, Inc.
|
|
* All Rights Reserved.
|
|
*/
|
|
#include <linux/mount.h>
|
|
#include <linux/fsmap.h>
|
|
#include "xfs.h"
|
|
#include "xfs_fs.h"
|
|
#include "xfs_shared.h"
|
|
#include "xfs_format.h"
|
|
#include "xfs_log_format.h"
|
|
#include "xfs_trans_resv.h"
|
|
#include "xfs_mount.h"
|
|
#include "xfs_inode.h"
|
|
#include "xfs_iwalk.h"
|
|
#include "xfs_itable.h"
|
|
#include "xfs_fsops.h"
|
|
#include "xfs_rtalloc.h"
|
|
#include "xfs_da_format.h"
|
|
#include "xfs_da_btree.h"
|
|
#include "xfs_attr.h"
|
|
#include "xfs_ioctl.h"
|
|
#include "xfs_ioctl32.h"
|
|
#include "xfs_trace.h"
|
|
#include "xfs_sb.h"
|
|
|
|
#define _NATIVE_IOC(cmd, type) \
|
|
_IOC(_IOC_DIR(cmd), _IOC_TYPE(cmd), _IOC_NR(cmd), sizeof(type))
|
|
|
|
#ifdef BROKEN_X86_ALIGNMENT
|
|
STATIC int
|
|
xfs_compat_ioc_fsgeometry_v1(
|
|
struct xfs_mount *mp,
|
|
compat_xfs_fsop_geom_v1_t __user *arg32)
|
|
{
|
|
struct xfs_fsop_geom fsgeo;
|
|
|
|
xfs_fs_geometry(mp, &fsgeo, 3);
|
|
/* The 32-bit variant simply has some padding at the end */
|
|
if (copy_to_user(arg32, &fsgeo, sizeof(struct compat_xfs_fsop_geom_v1)))
|
|
return -EFAULT;
|
|
return 0;
|
|
}
|
|
|
|
STATIC int
|
|
xfs_compat_growfs_data_copyin(
|
|
struct xfs_growfs_data *in,
|
|
compat_xfs_growfs_data_t __user *arg32)
|
|
{
|
|
if (get_user(in->newblocks, &arg32->newblocks) ||
|
|
get_user(in->imaxpct, &arg32->imaxpct))
|
|
return -EFAULT;
|
|
return 0;
|
|
}
|
|
|
|
STATIC int
|
|
xfs_compat_growfs_rt_copyin(
|
|
struct xfs_growfs_rt *in,
|
|
compat_xfs_growfs_rt_t __user *arg32)
|
|
{
|
|
if (get_user(in->newblocks, &arg32->newblocks) ||
|
|
get_user(in->extsize, &arg32->extsize))
|
|
return -EFAULT;
|
|
return 0;
|
|
}
|
|
|
|
STATIC int
|
|
xfs_fsinumbers_fmt_compat(
|
|
struct xfs_ibulk *breq,
|
|
const struct xfs_inumbers *ig)
|
|
{
|
|
struct compat_xfs_inogrp __user *p32 = breq->ubuffer;
|
|
struct xfs_inogrp ig1;
|
|
struct xfs_inogrp *igrp = &ig1;
|
|
|
|
xfs_inumbers_to_inogrp(&ig1, ig);
|
|
|
|
if (put_user(igrp->xi_startino, &p32->xi_startino) ||
|
|
put_user(igrp->xi_alloccount, &p32->xi_alloccount) ||
|
|
put_user(igrp->xi_allocmask, &p32->xi_allocmask))
|
|
return -EFAULT;
|
|
|
|
return xfs_ibulk_advance(breq, sizeof(struct compat_xfs_inogrp));
|
|
}
|
|
|
|
#else
|
|
#define xfs_fsinumbers_fmt_compat xfs_fsinumbers_fmt
|
|
#endif /* BROKEN_X86_ALIGNMENT */
|
|
|
|
STATIC int
|
|
xfs_ioctl32_bstime_copyin(
|
|
xfs_bstime_t *bstime,
|
|
compat_xfs_bstime_t __user *bstime32)
|
|
{
|
|
old_time32_t sec32; /* tv_sec differs on 64 vs. 32 */
|
|
|
|
if (get_user(sec32, &bstime32->tv_sec) ||
|
|
get_user(bstime->tv_nsec, &bstime32->tv_nsec))
|
|
return -EFAULT;
|
|
bstime->tv_sec = sec32;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* struct xfs_bstat has differing alignment on intel, & bstime_t sizes
|
|
* everywhere
|
|
*/
|
|
STATIC int
|
|
xfs_ioctl32_bstat_copyin(
|
|
struct xfs_bstat *bstat,
|
|
struct compat_xfs_bstat __user *bstat32)
|
|
{
|
|
if (get_user(bstat->bs_ino, &bstat32->bs_ino) ||
|
|
get_user(bstat->bs_mode, &bstat32->bs_mode) ||
|
|
get_user(bstat->bs_nlink, &bstat32->bs_nlink) ||
|
|
get_user(bstat->bs_uid, &bstat32->bs_uid) ||
|
|
get_user(bstat->bs_gid, &bstat32->bs_gid) ||
|
|
get_user(bstat->bs_rdev, &bstat32->bs_rdev) ||
|
|
get_user(bstat->bs_blksize, &bstat32->bs_blksize) ||
|
|
get_user(bstat->bs_size, &bstat32->bs_size) ||
|
|
xfs_ioctl32_bstime_copyin(&bstat->bs_atime, &bstat32->bs_atime) ||
|
|
xfs_ioctl32_bstime_copyin(&bstat->bs_mtime, &bstat32->bs_mtime) ||
|
|
xfs_ioctl32_bstime_copyin(&bstat->bs_ctime, &bstat32->bs_ctime) ||
|
|
get_user(bstat->bs_blocks, &bstat32->bs_size) ||
|
|
get_user(bstat->bs_xflags, &bstat32->bs_size) ||
|
|
get_user(bstat->bs_extsize, &bstat32->bs_extsize) ||
|
|
get_user(bstat->bs_extents, &bstat32->bs_extents) ||
|
|
get_user(bstat->bs_gen, &bstat32->bs_gen) ||
|
|
get_user(bstat->bs_projid_lo, &bstat32->bs_projid_lo) ||
|
|
get_user(bstat->bs_projid_hi, &bstat32->bs_projid_hi) ||
|
|
get_user(bstat->bs_forkoff, &bstat32->bs_forkoff) ||
|
|
get_user(bstat->bs_dmevmask, &bstat32->bs_dmevmask) ||
|
|
get_user(bstat->bs_dmstate, &bstat32->bs_dmstate) ||
|
|
get_user(bstat->bs_aextents, &bstat32->bs_aextents))
|
|
return -EFAULT;
|
|
return 0;
|
|
}
|
|
|
|
/* XFS_IOC_FSBULKSTAT and friends */
|
|
|
|
STATIC int
|
|
xfs_bstime_store_compat(
|
|
compat_xfs_bstime_t __user *p32,
|
|
const xfs_bstime_t *p)
|
|
{
|
|
__s32 sec32;
|
|
|
|
sec32 = p->tv_sec;
|
|
if (put_user(sec32, &p32->tv_sec) ||
|
|
put_user(p->tv_nsec, &p32->tv_nsec))
|
|
return -EFAULT;
|
|
return 0;
|
|
}
|
|
|
|
/* Return 0 on success or positive error (to xfs_bulkstat()) */
|
|
STATIC int
|
|
xfs_fsbulkstat_one_fmt_compat(
|
|
struct xfs_ibulk *breq,
|
|
const struct xfs_bulkstat *bstat)
|
|
{
|
|
struct compat_xfs_bstat __user *p32 = breq->ubuffer;
|
|
struct xfs_bstat bs1;
|
|
struct xfs_bstat *buffer = &bs1;
|
|
|
|
xfs_bulkstat_to_bstat(breq->mp, &bs1, bstat);
|
|
|
|
if (put_user(buffer->bs_ino, &p32->bs_ino) ||
|
|
put_user(buffer->bs_mode, &p32->bs_mode) ||
|
|
put_user(buffer->bs_nlink, &p32->bs_nlink) ||
|
|
put_user(buffer->bs_uid, &p32->bs_uid) ||
|
|
put_user(buffer->bs_gid, &p32->bs_gid) ||
|
|
put_user(buffer->bs_rdev, &p32->bs_rdev) ||
|
|
put_user(buffer->bs_blksize, &p32->bs_blksize) ||
|
|
put_user(buffer->bs_size, &p32->bs_size) ||
|
|
xfs_bstime_store_compat(&p32->bs_atime, &buffer->bs_atime) ||
|
|
xfs_bstime_store_compat(&p32->bs_mtime, &buffer->bs_mtime) ||
|
|
xfs_bstime_store_compat(&p32->bs_ctime, &buffer->bs_ctime) ||
|
|
put_user(buffer->bs_blocks, &p32->bs_blocks) ||
|
|
put_user(buffer->bs_xflags, &p32->bs_xflags) ||
|
|
put_user(buffer->bs_extsize, &p32->bs_extsize) ||
|
|
put_user(buffer->bs_extents, &p32->bs_extents) ||
|
|
put_user(buffer->bs_gen, &p32->bs_gen) ||
|
|
put_user(buffer->bs_projid, &p32->bs_projid) ||
|
|
put_user(buffer->bs_projid_hi, &p32->bs_projid_hi) ||
|
|
put_user(buffer->bs_forkoff, &p32->bs_forkoff) ||
|
|
put_user(buffer->bs_dmevmask, &p32->bs_dmevmask) ||
|
|
put_user(buffer->bs_dmstate, &p32->bs_dmstate) ||
|
|
put_user(buffer->bs_aextents, &p32->bs_aextents))
|
|
return -EFAULT;
|
|
|
|
return xfs_ibulk_advance(breq, sizeof(struct compat_xfs_bstat));
|
|
}
|
|
|
|
/* copied from xfs_ioctl.c */
|
|
STATIC int
|
|
xfs_compat_ioc_fsbulkstat(
|
|
struct file *file,
|
|
unsigned int cmd,
|
|
struct compat_xfs_fsop_bulkreq __user *p32)
|
|
{
|
|
struct xfs_mount *mp = XFS_I(file_inode(file))->i_mount;
|
|
u32 addr;
|
|
struct xfs_fsop_bulkreq bulkreq;
|
|
struct xfs_ibulk breq = {
|
|
.mp = mp,
|
|
.mnt_userns = file_mnt_user_ns(file),
|
|
.ocount = 0,
|
|
};
|
|
xfs_ino_t lastino;
|
|
int error;
|
|
|
|
/*
|
|
* Output structure handling functions. Depending on the command,
|
|
* either the xfs_bstat and xfs_inogrp structures are written out
|
|
* to userpace memory via bulkreq.ubuffer. Normally the compat
|
|
* functions and structure size are the correct ones to use ...
|
|
*/
|
|
inumbers_fmt_pf inumbers_func = xfs_fsinumbers_fmt_compat;
|
|
bulkstat_one_fmt_pf bs_one_func = xfs_fsbulkstat_one_fmt_compat;
|
|
|
|
#ifdef CONFIG_X86_X32_ABI
|
|
if (in_x32_syscall()) {
|
|
/*
|
|
* ... but on x32 the input xfs_fsop_bulkreq has pointers
|
|
* which must be handled in the "compat" (32-bit) way, while
|
|
* the xfs_bstat and xfs_inogrp structures follow native 64-
|
|
* bit layout convention. So adjust accordingly, otherwise
|
|
* the data written out in compat layout will not match what
|
|
* x32 userspace expects.
|
|
*/
|
|
inumbers_func = xfs_fsinumbers_fmt;
|
|
bs_one_func = xfs_fsbulkstat_one_fmt;
|
|
}
|
|
#endif
|
|
|
|
/* done = 1 if there are more stats to get and if bulkstat */
|
|
/* should be called again (unused here, but used in dmapi) */
|
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
|
return -EPERM;
|
|
|
|
if (xfs_is_shutdown(mp))
|
|
return -EIO;
|
|
|
|
if (get_user(addr, &p32->lastip))
|
|
return -EFAULT;
|
|
bulkreq.lastip = compat_ptr(addr);
|
|
if (get_user(bulkreq.icount, &p32->icount) ||
|
|
get_user(addr, &p32->ubuffer))
|
|
return -EFAULT;
|
|
bulkreq.ubuffer = compat_ptr(addr);
|
|
if (get_user(addr, &p32->ocount))
|
|
return -EFAULT;
|
|
bulkreq.ocount = compat_ptr(addr);
|
|
|
|
if (copy_from_user(&lastino, bulkreq.lastip, sizeof(__s64)))
|
|
return -EFAULT;
|
|
|
|
if (bulkreq.icount <= 0)
|
|
return -EINVAL;
|
|
|
|
if (bulkreq.ubuffer == NULL)
|
|
return -EINVAL;
|
|
|
|
breq.ubuffer = bulkreq.ubuffer;
|
|
breq.icount = bulkreq.icount;
|
|
|
|
/*
|
|
* FSBULKSTAT_SINGLE expects that *lastip contains the inode number
|
|
* that we want to stat. However, FSINUMBERS and FSBULKSTAT expect
|
|
* that *lastip contains either zero or the number of the last inode to
|
|
* be examined by the previous call and return results starting with
|
|
* the next inode after that. The new bulk request back end functions
|
|
* take the inode to start with, so we have to compute the startino
|
|
* parameter from lastino to maintain correct function. lastino == 0
|
|
* is a special case because it has traditionally meant "first inode
|
|
* in filesystem".
|
|
*/
|
|
if (cmd == XFS_IOC_FSINUMBERS_32) {
|
|
breq.startino = lastino ? lastino + 1 : 0;
|
|
error = xfs_inumbers(&breq, inumbers_func);
|
|
lastino = breq.startino - 1;
|
|
} else if (cmd == XFS_IOC_FSBULKSTAT_SINGLE_32) {
|
|
breq.startino = lastino;
|
|
breq.icount = 1;
|
|
error = xfs_bulkstat_one(&breq, bs_one_func);
|
|
lastino = breq.startino;
|
|
} else if (cmd == XFS_IOC_FSBULKSTAT_32) {
|
|
breq.startino = lastino ? lastino + 1 : 0;
|
|
error = xfs_bulkstat(&breq, bs_one_func);
|
|
lastino = breq.startino - 1;
|
|
} else {
|
|
error = -EINVAL;
|
|
}
|
|
if (error)
|
|
return error;
|
|
|
|
if (bulkreq.lastip != NULL &&
|
|
copy_to_user(bulkreq.lastip, &lastino, sizeof(xfs_ino_t)))
|
|
return -EFAULT;
|
|
|
|
if (bulkreq.ocount != NULL &&
|
|
copy_to_user(bulkreq.ocount, &breq.ocount, sizeof(__s32)))
|
|
return -EFAULT;
|
|
|
|
return 0;
|
|
}
|
|
|
|
STATIC int
|
|
xfs_compat_handlereq_copyin(
|
|
xfs_fsop_handlereq_t *hreq,
|
|
compat_xfs_fsop_handlereq_t __user *arg32)
|
|
{
|
|
compat_xfs_fsop_handlereq_t hreq32;
|
|
|
|
if (copy_from_user(&hreq32, arg32, sizeof(compat_xfs_fsop_handlereq_t)))
|
|
return -EFAULT;
|
|
|
|
hreq->fd = hreq32.fd;
|
|
hreq->path = compat_ptr(hreq32.path);
|
|
hreq->oflags = hreq32.oflags;
|
|
hreq->ihandle = compat_ptr(hreq32.ihandle);
|
|
hreq->ihandlen = hreq32.ihandlen;
|
|
hreq->ohandle = compat_ptr(hreq32.ohandle);
|
|
hreq->ohandlen = compat_ptr(hreq32.ohandlen);
|
|
|
|
return 0;
|
|
}
|
|
|
|
STATIC struct dentry *
|
|
xfs_compat_handlereq_to_dentry(
|
|
struct file *parfilp,
|
|
compat_xfs_fsop_handlereq_t *hreq)
|
|
{
|
|
return xfs_handle_to_dentry(parfilp,
|
|
compat_ptr(hreq->ihandle), hreq->ihandlen);
|
|
}
|
|
|
|
STATIC int
|
|
xfs_compat_attrlist_by_handle(
|
|
struct file *parfilp,
|
|
compat_xfs_fsop_attrlist_handlereq_t __user *p)
|
|
{
|
|
compat_xfs_fsop_attrlist_handlereq_t al_hreq;
|
|
struct dentry *dentry;
|
|
int error;
|
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
|
return -EPERM;
|
|
if (copy_from_user(&al_hreq, p, sizeof(al_hreq)))
|
|
return -EFAULT;
|
|
|
|
dentry = xfs_compat_handlereq_to_dentry(parfilp, &al_hreq.hreq);
|
|
if (IS_ERR(dentry))
|
|
return PTR_ERR(dentry);
|
|
|
|
error = xfs_ioc_attr_list(XFS_I(d_inode(dentry)),
|
|
compat_ptr(al_hreq.buffer), al_hreq.buflen,
|
|
al_hreq.flags, &p->pos);
|
|
dput(dentry);
|
|
return error;
|
|
}
|
|
|
|
STATIC int
|
|
xfs_compat_attrmulti_by_handle(
|
|
struct file *parfilp,
|
|
void __user *arg)
|
|
{
|
|
int error;
|
|
compat_xfs_attr_multiop_t *ops;
|
|
compat_xfs_fsop_attrmulti_handlereq_t am_hreq;
|
|
struct dentry *dentry;
|
|
unsigned int i, size;
|
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
|
return -EPERM;
|
|
if (copy_from_user(&am_hreq, arg,
|
|
sizeof(compat_xfs_fsop_attrmulti_handlereq_t)))
|
|
return -EFAULT;
|
|
|
|
/* overflow check */
|
|
if (am_hreq.opcount >= INT_MAX / sizeof(compat_xfs_attr_multiop_t))
|
|
return -E2BIG;
|
|
|
|
dentry = xfs_compat_handlereq_to_dentry(parfilp, &am_hreq.hreq);
|
|
if (IS_ERR(dentry))
|
|
return PTR_ERR(dentry);
|
|
|
|
error = -E2BIG;
|
|
size = am_hreq.opcount * sizeof(compat_xfs_attr_multiop_t);
|
|
if (!size || size > 16 * PAGE_SIZE)
|
|
goto out_dput;
|
|
|
|
ops = memdup_user(compat_ptr(am_hreq.ops), size);
|
|
if (IS_ERR(ops)) {
|
|
error = PTR_ERR(ops);
|
|
goto out_dput;
|
|
}
|
|
|
|
error = 0;
|
|
for (i = 0; i < am_hreq.opcount; i++) {
|
|
ops[i].am_error = xfs_ioc_attrmulti_one(parfilp,
|
|
d_inode(dentry), ops[i].am_opcode,
|
|
compat_ptr(ops[i].am_attrname),
|
|
compat_ptr(ops[i].am_attrvalue),
|
|
&ops[i].am_length, ops[i].am_flags);
|
|
}
|
|
|
|
if (copy_to_user(compat_ptr(am_hreq.ops), ops, size))
|
|
error = -EFAULT;
|
|
|
|
kfree(ops);
|
|
out_dput:
|
|
dput(dentry);
|
|
return error;
|
|
}
|
|
|
|
long
|
|
xfs_file_compat_ioctl(
|
|
struct file *filp,
|
|
unsigned cmd,
|
|
unsigned long p)
|
|
{
|
|
struct inode *inode = file_inode(filp);
|
|
struct xfs_inode *ip = XFS_I(inode);
|
|
void __user *arg = compat_ptr(p);
|
|
int error;
|
|
|
|
trace_xfs_file_compat_ioctl(ip);
|
|
|
|
switch (cmd) {
|
|
#if defined(BROKEN_X86_ALIGNMENT)
|
|
case XFS_IOC_FSGEOMETRY_V1_32:
|
|
return xfs_compat_ioc_fsgeometry_v1(ip->i_mount, arg);
|
|
case XFS_IOC_FSGROWFSDATA_32: {
|
|
struct xfs_growfs_data in;
|
|
|
|
if (xfs_compat_growfs_data_copyin(&in, arg))
|
|
return -EFAULT;
|
|
error = mnt_want_write_file(filp);
|
|
if (error)
|
|
return error;
|
|
error = xfs_growfs_data(ip->i_mount, &in);
|
|
mnt_drop_write_file(filp);
|
|
return error;
|
|
}
|
|
case XFS_IOC_FSGROWFSRT_32: {
|
|
struct xfs_growfs_rt in;
|
|
|
|
if (xfs_compat_growfs_rt_copyin(&in, arg))
|
|
return -EFAULT;
|
|
error = mnt_want_write_file(filp);
|
|
if (error)
|
|
return error;
|
|
error = xfs_growfs_rt(ip->i_mount, &in);
|
|
mnt_drop_write_file(filp);
|
|
return error;
|
|
}
|
|
#endif
|
|
/* long changes size, but xfs only copiese out 32 bits */
|
|
case XFS_IOC_GETVERSION_32:
|
|
cmd = _NATIVE_IOC(cmd, long);
|
|
return xfs_file_ioctl(filp, cmd, p);
|
|
case XFS_IOC_SWAPEXT_32: {
|
|
struct xfs_swapext sxp;
|
|
struct compat_xfs_swapext __user *sxu = arg;
|
|
|
|
/* Bulk copy in up to the sx_stat field, then copy bstat */
|
|
if (copy_from_user(&sxp, sxu,
|
|
offsetof(struct xfs_swapext, sx_stat)) ||
|
|
xfs_ioctl32_bstat_copyin(&sxp.sx_stat, &sxu->sx_stat))
|
|
return -EFAULT;
|
|
error = mnt_want_write_file(filp);
|
|
if (error)
|
|
return error;
|
|
error = xfs_ioc_swapext(&sxp);
|
|
mnt_drop_write_file(filp);
|
|
return error;
|
|
}
|
|
case XFS_IOC_FSBULKSTAT_32:
|
|
case XFS_IOC_FSBULKSTAT_SINGLE_32:
|
|
case XFS_IOC_FSINUMBERS_32:
|
|
return xfs_compat_ioc_fsbulkstat(filp, cmd, arg);
|
|
case XFS_IOC_FD_TO_HANDLE_32:
|
|
case XFS_IOC_PATH_TO_HANDLE_32:
|
|
case XFS_IOC_PATH_TO_FSHANDLE_32: {
|
|
struct xfs_fsop_handlereq hreq;
|
|
|
|
if (xfs_compat_handlereq_copyin(&hreq, arg))
|
|
return -EFAULT;
|
|
cmd = _NATIVE_IOC(cmd, struct xfs_fsop_handlereq);
|
|
return xfs_find_handle(cmd, &hreq);
|
|
}
|
|
case XFS_IOC_OPEN_BY_HANDLE_32: {
|
|
struct xfs_fsop_handlereq hreq;
|
|
|
|
if (xfs_compat_handlereq_copyin(&hreq, arg))
|
|
return -EFAULT;
|
|
return xfs_open_by_handle(filp, &hreq);
|
|
}
|
|
case XFS_IOC_READLINK_BY_HANDLE_32: {
|
|
struct xfs_fsop_handlereq hreq;
|
|
|
|
if (xfs_compat_handlereq_copyin(&hreq, arg))
|
|
return -EFAULT;
|
|
return xfs_readlink_by_handle(filp, &hreq);
|
|
}
|
|
case XFS_IOC_ATTRLIST_BY_HANDLE_32:
|
|
return xfs_compat_attrlist_by_handle(filp, arg);
|
|
case XFS_IOC_ATTRMULTI_BY_HANDLE_32:
|
|
return xfs_compat_attrmulti_by_handle(filp, arg);
|
|
default:
|
|
/* try the native version */
|
|
return xfs_file_ioctl(filp, cmd, (unsigned long)arg);
|
|
}
|
|
}
|