162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2000-2005 Silicon Graphics, Inc. 462306a36Sopenharmony_ci * All Rights Reserved. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci#include "xfs.h" 762306a36Sopenharmony_ci#include "xfs_fs.h" 862306a36Sopenharmony_ci#include "xfs_shared.h" 962306a36Sopenharmony_ci#include "xfs_format.h" 1062306a36Sopenharmony_ci#include "xfs_log_format.h" 1162306a36Sopenharmony_ci#include "xfs_trans_resv.h" 1262306a36Sopenharmony_ci#include "xfs_mount.h" 1362306a36Sopenharmony_ci#include "xfs_inode.h" 1462306a36Sopenharmony_ci#include "xfs_rtalloc.h" 1562306a36Sopenharmony_ci#include "xfs_iwalk.h" 1662306a36Sopenharmony_ci#include "xfs_itable.h" 1762306a36Sopenharmony_ci#include "xfs_error.h" 1862306a36Sopenharmony_ci#include "xfs_da_format.h" 1962306a36Sopenharmony_ci#include "xfs_da_btree.h" 2062306a36Sopenharmony_ci#include "xfs_attr.h" 2162306a36Sopenharmony_ci#include "xfs_bmap.h" 2262306a36Sopenharmony_ci#include "xfs_bmap_util.h" 2362306a36Sopenharmony_ci#include "xfs_fsops.h" 2462306a36Sopenharmony_ci#include "xfs_discard.h" 2562306a36Sopenharmony_ci#include "xfs_quota.h" 2662306a36Sopenharmony_ci#include "xfs_export.h" 2762306a36Sopenharmony_ci#include "xfs_trace.h" 2862306a36Sopenharmony_ci#include "xfs_icache.h" 2962306a36Sopenharmony_ci#include "xfs_trans.h" 3062306a36Sopenharmony_ci#include "xfs_acl.h" 3162306a36Sopenharmony_ci#include "xfs_btree.h" 3262306a36Sopenharmony_ci#include <linux/fsmap.h> 3362306a36Sopenharmony_ci#include "xfs_fsmap.h" 3462306a36Sopenharmony_ci#include "scrub/xfs_scrub.h" 3562306a36Sopenharmony_ci#include "xfs_sb.h" 3662306a36Sopenharmony_ci#include "xfs_ag.h" 3762306a36Sopenharmony_ci#include "xfs_health.h" 3862306a36Sopenharmony_ci#include "xfs_reflink.h" 3962306a36Sopenharmony_ci#include "xfs_ioctl.h" 4062306a36Sopenharmony_ci#include "xfs_xattr.h" 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#include <linux/mount.h> 4362306a36Sopenharmony_ci#include <linux/namei.h> 4462306a36Sopenharmony_ci#include <linux/fileattr.h> 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* 4762306a36Sopenharmony_ci * xfs_find_handle maps from userspace xfs_fsop_handlereq structure to 4862306a36Sopenharmony_ci * a file or fs handle. 4962306a36Sopenharmony_ci * 5062306a36Sopenharmony_ci * XFS_IOC_PATH_TO_FSHANDLE 5162306a36Sopenharmony_ci * returns fs handle for a mount point or path within that mount point 5262306a36Sopenharmony_ci * XFS_IOC_FD_TO_HANDLE 5362306a36Sopenharmony_ci * returns full handle for a FD opened in user space 5462306a36Sopenharmony_ci * XFS_IOC_PATH_TO_HANDLE 5562306a36Sopenharmony_ci * returns full handle for a path 5662306a36Sopenharmony_ci */ 5762306a36Sopenharmony_ciint 5862306a36Sopenharmony_cixfs_find_handle( 5962306a36Sopenharmony_ci unsigned int cmd, 6062306a36Sopenharmony_ci xfs_fsop_handlereq_t *hreq) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci int hsize; 6362306a36Sopenharmony_ci xfs_handle_t handle; 6462306a36Sopenharmony_ci struct inode *inode; 6562306a36Sopenharmony_ci struct fd f = {NULL}; 6662306a36Sopenharmony_ci struct path path; 6762306a36Sopenharmony_ci int error; 6862306a36Sopenharmony_ci struct xfs_inode *ip; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci if (cmd == XFS_IOC_FD_TO_HANDLE) { 7162306a36Sopenharmony_ci f = fdget(hreq->fd); 7262306a36Sopenharmony_ci if (!f.file) 7362306a36Sopenharmony_ci return -EBADF; 7462306a36Sopenharmony_ci inode = file_inode(f.file); 7562306a36Sopenharmony_ci } else { 7662306a36Sopenharmony_ci error = user_path_at(AT_FDCWD, hreq->path, 0, &path); 7762306a36Sopenharmony_ci if (error) 7862306a36Sopenharmony_ci return error; 7962306a36Sopenharmony_ci inode = d_inode(path.dentry); 8062306a36Sopenharmony_ci } 8162306a36Sopenharmony_ci ip = XFS_I(inode); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci /* 8462306a36Sopenharmony_ci * We can only generate handles for inodes residing on a XFS filesystem, 8562306a36Sopenharmony_ci * and only for regular files, directories or symbolic links. 8662306a36Sopenharmony_ci */ 8762306a36Sopenharmony_ci error = -EINVAL; 8862306a36Sopenharmony_ci if (inode->i_sb->s_magic != XFS_SB_MAGIC) 8962306a36Sopenharmony_ci goto out_put; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci error = -EBADF; 9262306a36Sopenharmony_ci if (!S_ISREG(inode->i_mode) && 9362306a36Sopenharmony_ci !S_ISDIR(inode->i_mode) && 9462306a36Sopenharmony_ci !S_ISLNK(inode->i_mode)) 9562306a36Sopenharmony_ci goto out_put; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci memcpy(&handle.ha_fsid, ip->i_mount->m_fixedfsid, sizeof(xfs_fsid_t)); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci if (cmd == XFS_IOC_PATH_TO_FSHANDLE) { 10162306a36Sopenharmony_ci /* 10262306a36Sopenharmony_ci * This handle only contains an fsid, zero the rest. 10362306a36Sopenharmony_ci */ 10462306a36Sopenharmony_ci memset(&handle.ha_fid, 0, sizeof(handle.ha_fid)); 10562306a36Sopenharmony_ci hsize = sizeof(xfs_fsid_t); 10662306a36Sopenharmony_ci } else { 10762306a36Sopenharmony_ci handle.ha_fid.fid_len = sizeof(xfs_fid_t) - 10862306a36Sopenharmony_ci sizeof(handle.ha_fid.fid_len); 10962306a36Sopenharmony_ci handle.ha_fid.fid_pad = 0; 11062306a36Sopenharmony_ci handle.ha_fid.fid_gen = inode->i_generation; 11162306a36Sopenharmony_ci handle.ha_fid.fid_ino = ip->i_ino; 11262306a36Sopenharmony_ci hsize = sizeof(xfs_handle_t); 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci error = -EFAULT; 11662306a36Sopenharmony_ci if (copy_to_user(hreq->ohandle, &handle, hsize) || 11762306a36Sopenharmony_ci copy_to_user(hreq->ohandlen, &hsize, sizeof(__s32))) 11862306a36Sopenharmony_ci goto out_put; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci error = 0; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci out_put: 12362306a36Sopenharmony_ci if (cmd == XFS_IOC_FD_TO_HANDLE) 12462306a36Sopenharmony_ci fdput(f); 12562306a36Sopenharmony_ci else 12662306a36Sopenharmony_ci path_put(&path); 12762306a36Sopenharmony_ci return error; 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci/* 13162306a36Sopenharmony_ci * No need to do permission checks on the various pathname components 13262306a36Sopenharmony_ci * as the handle operations are privileged. 13362306a36Sopenharmony_ci */ 13462306a36Sopenharmony_ciSTATIC int 13562306a36Sopenharmony_cixfs_handle_acceptable( 13662306a36Sopenharmony_ci void *context, 13762306a36Sopenharmony_ci struct dentry *dentry) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci return 1; 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci/* 14362306a36Sopenharmony_ci * Convert userspace handle data into a dentry. 14462306a36Sopenharmony_ci */ 14562306a36Sopenharmony_cistruct dentry * 14662306a36Sopenharmony_cixfs_handle_to_dentry( 14762306a36Sopenharmony_ci struct file *parfilp, 14862306a36Sopenharmony_ci void __user *uhandle, 14962306a36Sopenharmony_ci u32 hlen) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci xfs_handle_t handle; 15262306a36Sopenharmony_ci struct xfs_fid64 fid; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci /* 15562306a36Sopenharmony_ci * Only allow handle opens under a directory. 15662306a36Sopenharmony_ci */ 15762306a36Sopenharmony_ci if (!S_ISDIR(file_inode(parfilp)->i_mode)) 15862306a36Sopenharmony_ci return ERR_PTR(-ENOTDIR); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci if (hlen != sizeof(xfs_handle_t)) 16162306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 16262306a36Sopenharmony_ci if (copy_from_user(&handle, uhandle, hlen)) 16362306a36Sopenharmony_ci return ERR_PTR(-EFAULT); 16462306a36Sopenharmony_ci if (handle.ha_fid.fid_len != 16562306a36Sopenharmony_ci sizeof(handle.ha_fid) - sizeof(handle.ha_fid.fid_len)) 16662306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci memset(&fid, 0, sizeof(struct fid)); 16962306a36Sopenharmony_ci fid.ino = handle.ha_fid.fid_ino; 17062306a36Sopenharmony_ci fid.gen = handle.ha_fid.fid_gen; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci return exportfs_decode_fh(parfilp->f_path.mnt, (struct fid *)&fid, 3, 17362306a36Sopenharmony_ci FILEID_INO32_GEN | XFS_FILEID_TYPE_64FLAG, 17462306a36Sopenharmony_ci xfs_handle_acceptable, NULL); 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ciSTATIC struct dentry * 17862306a36Sopenharmony_cixfs_handlereq_to_dentry( 17962306a36Sopenharmony_ci struct file *parfilp, 18062306a36Sopenharmony_ci xfs_fsop_handlereq_t *hreq) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci return xfs_handle_to_dentry(parfilp, hreq->ihandle, hreq->ihandlen); 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ciint 18662306a36Sopenharmony_cixfs_open_by_handle( 18762306a36Sopenharmony_ci struct file *parfilp, 18862306a36Sopenharmony_ci xfs_fsop_handlereq_t *hreq) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci const struct cred *cred = current_cred(); 19162306a36Sopenharmony_ci int error; 19262306a36Sopenharmony_ci int fd; 19362306a36Sopenharmony_ci int permflag; 19462306a36Sopenharmony_ci struct file *filp; 19562306a36Sopenharmony_ci struct inode *inode; 19662306a36Sopenharmony_ci struct dentry *dentry; 19762306a36Sopenharmony_ci fmode_t fmode; 19862306a36Sopenharmony_ci struct path path; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 20162306a36Sopenharmony_ci return -EPERM; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci dentry = xfs_handlereq_to_dentry(parfilp, hreq); 20462306a36Sopenharmony_ci if (IS_ERR(dentry)) 20562306a36Sopenharmony_ci return PTR_ERR(dentry); 20662306a36Sopenharmony_ci inode = d_inode(dentry); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci /* Restrict xfs_open_by_handle to directories & regular files. */ 20962306a36Sopenharmony_ci if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode))) { 21062306a36Sopenharmony_ci error = -EPERM; 21162306a36Sopenharmony_ci goto out_dput; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci#if BITS_PER_LONG != 32 21562306a36Sopenharmony_ci hreq->oflags |= O_LARGEFILE; 21662306a36Sopenharmony_ci#endif 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci permflag = hreq->oflags; 21962306a36Sopenharmony_ci fmode = OPEN_FMODE(permflag); 22062306a36Sopenharmony_ci if ((!(permflag & O_APPEND) || (permflag & O_TRUNC)) && 22162306a36Sopenharmony_ci (fmode & FMODE_WRITE) && IS_APPEND(inode)) { 22262306a36Sopenharmony_ci error = -EPERM; 22362306a36Sopenharmony_ci goto out_dput; 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci if ((fmode & FMODE_WRITE) && IS_IMMUTABLE(inode)) { 22762306a36Sopenharmony_ci error = -EPERM; 22862306a36Sopenharmony_ci goto out_dput; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci /* Can't write directories. */ 23262306a36Sopenharmony_ci if (S_ISDIR(inode->i_mode) && (fmode & FMODE_WRITE)) { 23362306a36Sopenharmony_ci error = -EISDIR; 23462306a36Sopenharmony_ci goto out_dput; 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci fd = get_unused_fd_flags(0); 23862306a36Sopenharmony_ci if (fd < 0) { 23962306a36Sopenharmony_ci error = fd; 24062306a36Sopenharmony_ci goto out_dput; 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci path.mnt = parfilp->f_path.mnt; 24462306a36Sopenharmony_ci path.dentry = dentry; 24562306a36Sopenharmony_ci filp = dentry_open(&path, hreq->oflags, cred); 24662306a36Sopenharmony_ci dput(dentry); 24762306a36Sopenharmony_ci if (IS_ERR(filp)) { 24862306a36Sopenharmony_ci put_unused_fd(fd); 24962306a36Sopenharmony_ci return PTR_ERR(filp); 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci if (S_ISREG(inode->i_mode)) { 25362306a36Sopenharmony_ci filp->f_flags |= O_NOATIME; 25462306a36Sopenharmony_ci filp->f_mode |= FMODE_NOCMTIME; 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci fd_install(fd, filp); 25862306a36Sopenharmony_ci return fd; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci out_dput: 26162306a36Sopenharmony_ci dput(dentry); 26262306a36Sopenharmony_ci return error; 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ciint 26662306a36Sopenharmony_cixfs_readlink_by_handle( 26762306a36Sopenharmony_ci struct file *parfilp, 26862306a36Sopenharmony_ci xfs_fsop_handlereq_t *hreq) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci struct dentry *dentry; 27162306a36Sopenharmony_ci __u32 olen; 27262306a36Sopenharmony_ci int error; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 27562306a36Sopenharmony_ci return -EPERM; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci dentry = xfs_handlereq_to_dentry(parfilp, hreq); 27862306a36Sopenharmony_ci if (IS_ERR(dentry)) 27962306a36Sopenharmony_ci return PTR_ERR(dentry); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci /* Restrict this handle operation to symlinks only. */ 28262306a36Sopenharmony_ci if (!d_is_symlink(dentry)) { 28362306a36Sopenharmony_ci error = -EINVAL; 28462306a36Sopenharmony_ci goto out_dput; 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci if (copy_from_user(&olen, hreq->ohandlen, sizeof(__u32))) { 28862306a36Sopenharmony_ci error = -EFAULT; 28962306a36Sopenharmony_ci goto out_dput; 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci error = vfs_readlink(dentry, hreq->ohandle, olen); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci out_dput: 29562306a36Sopenharmony_ci dput(dentry); 29662306a36Sopenharmony_ci return error; 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci/* 30062306a36Sopenharmony_ci * Format an attribute and copy it out to the user's buffer. 30162306a36Sopenharmony_ci * Take care to check values and protect against them changing later, 30262306a36Sopenharmony_ci * we may be reading them directly out of a user buffer. 30362306a36Sopenharmony_ci */ 30462306a36Sopenharmony_cistatic void 30562306a36Sopenharmony_cixfs_ioc_attr_put_listent( 30662306a36Sopenharmony_ci struct xfs_attr_list_context *context, 30762306a36Sopenharmony_ci int flags, 30862306a36Sopenharmony_ci unsigned char *name, 30962306a36Sopenharmony_ci int namelen, 31062306a36Sopenharmony_ci int valuelen) 31162306a36Sopenharmony_ci{ 31262306a36Sopenharmony_ci struct xfs_attrlist *alist = context->buffer; 31362306a36Sopenharmony_ci struct xfs_attrlist_ent *aep; 31462306a36Sopenharmony_ci int arraytop; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci ASSERT(!context->seen_enough); 31762306a36Sopenharmony_ci ASSERT(context->count >= 0); 31862306a36Sopenharmony_ci ASSERT(context->count < (ATTR_MAX_VALUELEN/8)); 31962306a36Sopenharmony_ci ASSERT(context->firstu >= sizeof(*alist)); 32062306a36Sopenharmony_ci ASSERT(context->firstu <= context->bufsize); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci /* 32362306a36Sopenharmony_ci * Only list entries in the right namespace. 32462306a36Sopenharmony_ci */ 32562306a36Sopenharmony_ci if (context->attr_filter != (flags & XFS_ATTR_NSP_ONDISK_MASK)) 32662306a36Sopenharmony_ci return; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci arraytop = sizeof(*alist) + 32962306a36Sopenharmony_ci context->count * sizeof(alist->al_offset[0]); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci /* decrement by the actual bytes used by the attr */ 33262306a36Sopenharmony_ci context->firstu -= round_up(offsetof(struct xfs_attrlist_ent, a_name) + 33362306a36Sopenharmony_ci namelen + 1, sizeof(uint32_t)); 33462306a36Sopenharmony_ci if (context->firstu < arraytop) { 33562306a36Sopenharmony_ci trace_xfs_attr_list_full(context); 33662306a36Sopenharmony_ci alist->al_more = 1; 33762306a36Sopenharmony_ci context->seen_enough = 1; 33862306a36Sopenharmony_ci return; 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci aep = context->buffer + context->firstu; 34262306a36Sopenharmony_ci aep->a_valuelen = valuelen; 34362306a36Sopenharmony_ci memcpy(aep->a_name, name, namelen); 34462306a36Sopenharmony_ci aep->a_name[namelen] = 0; 34562306a36Sopenharmony_ci alist->al_offset[context->count++] = context->firstu; 34662306a36Sopenharmony_ci alist->al_count = context->count; 34762306a36Sopenharmony_ci trace_xfs_attr_list_add(context); 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_cistatic unsigned int 35162306a36Sopenharmony_cixfs_attr_filter( 35262306a36Sopenharmony_ci u32 ioc_flags) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci if (ioc_flags & XFS_IOC_ATTR_ROOT) 35562306a36Sopenharmony_ci return XFS_ATTR_ROOT; 35662306a36Sopenharmony_ci if (ioc_flags & XFS_IOC_ATTR_SECURE) 35762306a36Sopenharmony_ci return XFS_ATTR_SECURE; 35862306a36Sopenharmony_ci return 0; 35962306a36Sopenharmony_ci} 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_cistatic unsigned int 36262306a36Sopenharmony_cixfs_attr_flags( 36362306a36Sopenharmony_ci u32 ioc_flags) 36462306a36Sopenharmony_ci{ 36562306a36Sopenharmony_ci if (ioc_flags & XFS_IOC_ATTR_CREATE) 36662306a36Sopenharmony_ci return XATTR_CREATE; 36762306a36Sopenharmony_ci if (ioc_flags & XFS_IOC_ATTR_REPLACE) 36862306a36Sopenharmony_ci return XATTR_REPLACE; 36962306a36Sopenharmony_ci return 0; 37062306a36Sopenharmony_ci} 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ciint 37362306a36Sopenharmony_cixfs_ioc_attr_list( 37462306a36Sopenharmony_ci struct xfs_inode *dp, 37562306a36Sopenharmony_ci void __user *ubuf, 37662306a36Sopenharmony_ci size_t bufsize, 37762306a36Sopenharmony_ci int flags, 37862306a36Sopenharmony_ci struct xfs_attrlist_cursor __user *ucursor) 37962306a36Sopenharmony_ci{ 38062306a36Sopenharmony_ci struct xfs_attr_list_context context = { }; 38162306a36Sopenharmony_ci struct xfs_attrlist *alist; 38262306a36Sopenharmony_ci void *buffer; 38362306a36Sopenharmony_ci int error; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci if (bufsize < sizeof(struct xfs_attrlist) || 38662306a36Sopenharmony_ci bufsize > XFS_XATTR_LIST_MAX) 38762306a36Sopenharmony_ci return -EINVAL; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci /* 39062306a36Sopenharmony_ci * Reject flags, only allow namespaces. 39162306a36Sopenharmony_ci */ 39262306a36Sopenharmony_ci if (flags & ~(XFS_IOC_ATTR_ROOT | XFS_IOC_ATTR_SECURE)) 39362306a36Sopenharmony_ci return -EINVAL; 39462306a36Sopenharmony_ci if (flags == (XFS_IOC_ATTR_ROOT | XFS_IOC_ATTR_SECURE)) 39562306a36Sopenharmony_ci return -EINVAL; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci /* 39862306a36Sopenharmony_ci * Validate the cursor. 39962306a36Sopenharmony_ci */ 40062306a36Sopenharmony_ci if (copy_from_user(&context.cursor, ucursor, sizeof(context.cursor))) 40162306a36Sopenharmony_ci return -EFAULT; 40262306a36Sopenharmony_ci if (context.cursor.pad1 || context.cursor.pad2) 40362306a36Sopenharmony_ci return -EINVAL; 40462306a36Sopenharmony_ci if (!context.cursor.initted && 40562306a36Sopenharmony_ci (context.cursor.hashval || context.cursor.blkno || 40662306a36Sopenharmony_ci context.cursor.offset)) 40762306a36Sopenharmony_ci return -EINVAL; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci buffer = kvzalloc(bufsize, GFP_KERNEL); 41062306a36Sopenharmony_ci if (!buffer) 41162306a36Sopenharmony_ci return -ENOMEM; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci /* 41462306a36Sopenharmony_ci * Initialize the output buffer. 41562306a36Sopenharmony_ci */ 41662306a36Sopenharmony_ci context.dp = dp; 41762306a36Sopenharmony_ci context.resynch = 1; 41862306a36Sopenharmony_ci context.attr_filter = xfs_attr_filter(flags); 41962306a36Sopenharmony_ci context.buffer = buffer; 42062306a36Sopenharmony_ci context.bufsize = round_down(bufsize, sizeof(uint32_t)); 42162306a36Sopenharmony_ci context.firstu = context.bufsize; 42262306a36Sopenharmony_ci context.put_listent = xfs_ioc_attr_put_listent; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci alist = context.buffer; 42562306a36Sopenharmony_ci alist->al_count = 0; 42662306a36Sopenharmony_ci alist->al_more = 0; 42762306a36Sopenharmony_ci alist->al_offset[0] = context.bufsize; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci error = xfs_attr_list(&context); 43062306a36Sopenharmony_ci if (error) 43162306a36Sopenharmony_ci goto out_free; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci if (copy_to_user(ubuf, buffer, bufsize) || 43462306a36Sopenharmony_ci copy_to_user(ucursor, &context.cursor, sizeof(context.cursor))) 43562306a36Sopenharmony_ci error = -EFAULT; 43662306a36Sopenharmony_ciout_free: 43762306a36Sopenharmony_ci kmem_free(buffer); 43862306a36Sopenharmony_ci return error; 43962306a36Sopenharmony_ci} 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ciSTATIC int 44262306a36Sopenharmony_cixfs_attrlist_by_handle( 44362306a36Sopenharmony_ci struct file *parfilp, 44462306a36Sopenharmony_ci struct xfs_fsop_attrlist_handlereq __user *p) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci struct xfs_fsop_attrlist_handlereq al_hreq; 44762306a36Sopenharmony_ci struct dentry *dentry; 44862306a36Sopenharmony_ci int error = -ENOMEM; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 45162306a36Sopenharmony_ci return -EPERM; 45262306a36Sopenharmony_ci if (copy_from_user(&al_hreq, p, sizeof(al_hreq))) 45362306a36Sopenharmony_ci return -EFAULT; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci dentry = xfs_handlereq_to_dentry(parfilp, &al_hreq.hreq); 45662306a36Sopenharmony_ci if (IS_ERR(dentry)) 45762306a36Sopenharmony_ci return PTR_ERR(dentry); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci error = xfs_ioc_attr_list(XFS_I(d_inode(dentry)), al_hreq.buffer, 46062306a36Sopenharmony_ci al_hreq.buflen, al_hreq.flags, &p->pos); 46162306a36Sopenharmony_ci dput(dentry); 46262306a36Sopenharmony_ci return error; 46362306a36Sopenharmony_ci} 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_cistatic int 46662306a36Sopenharmony_cixfs_attrmulti_attr_get( 46762306a36Sopenharmony_ci struct inode *inode, 46862306a36Sopenharmony_ci unsigned char *name, 46962306a36Sopenharmony_ci unsigned char __user *ubuf, 47062306a36Sopenharmony_ci uint32_t *len, 47162306a36Sopenharmony_ci uint32_t flags) 47262306a36Sopenharmony_ci{ 47362306a36Sopenharmony_ci struct xfs_da_args args = { 47462306a36Sopenharmony_ci .dp = XFS_I(inode), 47562306a36Sopenharmony_ci .attr_filter = xfs_attr_filter(flags), 47662306a36Sopenharmony_ci .attr_flags = xfs_attr_flags(flags), 47762306a36Sopenharmony_ci .name = name, 47862306a36Sopenharmony_ci .namelen = strlen(name), 47962306a36Sopenharmony_ci .valuelen = *len, 48062306a36Sopenharmony_ci }; 48162306a36Sopenharmony_ci int error; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci if (*len > XFS_XATTR_SIZE_MAX) 48462306a36Sopenharmony_ci return -EINVAL; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci error = xfs_attr_get(&args); 48762306a36Sopenharmony_ci if (error) 48862306a36Sopenharmony_ci goto out_kfree; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci *len = args.valuelen; 49162306a36Sopenharmony_ci if (copy_to_user(ubuf, args.value, args.valuelen)) 49262306a36Sopenharmony_ci error = -EFAULT; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ciout_kfree: 49562306a36Sopenharmony_ci kmem_free(args.value); 49662306a36Sopenharmony_ci return error; 49762306a36Sopenharmony_ci} 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_cistatic int 50062306a36Sopenharmony_cixfs_attrmulti_attr_set( 50162306a36Sopenharmony_ci struct inode *inode, 50262306a36Sopenharmony_ci unsigned char *name, 50362306a36Sopenharmony_ci const unsigned char __user *ubuf, 50462306a36Sopenharmony_ci uint32_t len, 50562306a36Sopenharmony_ci uint32_t flags) 50662306a36Sopenharmony_ci{ 50762306a36Sopenharmony_ci struct xfs_da_args args = { 50862306a36Sopenharmony_ci .dp = XFS_I(inode), 50962306a36Sopenharmony_ci .attr_filter = xfs_attr_filter(flags), 51062306a36Sopenharmony_ci .attr_flags = xfs_attr_flags(flags), 51162306a36Sopenharmony_ci .name = name, 51262306a36Sopenharmony_ci .namelen = strlen(name), 51362306a36Sopenharmony_ci }; 51462306a36Sopenharmony_ci int error; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) 51762306a36Sopenharmony_ci return -EPERM; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci if (ubuf) { 52062306a36Sopenharmony_ci if (len > XFS_XATTR_SIZE_MAX) 52162306a36Sopenharmony_ci return -EINVAL; 52262306a36Sopenharmony_ci args.value = memdup_user(ubuf, len); 52362306a36Sopenharmony_ci if (IS_ERR(args.value)) 52462306a36Sopenharmony_ci return PTR_ERR(args.value); 52562306a36Sopenharmony_ci args.valuelen = len; 52662306a36Sopenharmony_ci } 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci error = xfs_attr_change(&args); 52962306a36Sopenharmony_ci if (!error && (flags & XFS_IOC_ATTR_ROOT)) 53062306a36Sopenharmony_ci xfs_forget_acl(inode, name); 53162306a36Sopenharmony_ci kfree(args.value); 53262306a36Sopenharmony_ci return error; 53362306a36Sopenharmony_ci} 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ciint 53662306a36Sopenharmony_cixfs_ioc_attrmulti_one( 53762306a36Sopenharmony_ci struct file *parfilp, 53862306a36Sopenharmony_ci struct inode *inode, 53962306a36Sopenharmony_ci uint32_t opcode, 54062306a36Sopenharmony_ci void __user *uname, 54162306a36Sopenharmony_ci void __user *value, 54262306a36Sopenharmony_ci uint32_t *len, 54362306a36Sopenharmony_ci uint32_t flags) 54462306a36Sopenharmony_ci{ 54562306a36Sopenharmony_ci unsigned char *name; 54662306a36Sopenharmony_ci int error; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci if ((flags & XFS_IOC_ATTR_ROOT) && (flags & XFS_IOC_ATTR_SECURE)) 54962306a36Sopenharmony_ci return -EINVAL; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci name = strndup_user(uname, MAXNAMELEN); 55262306a36Sopenharmony_ci if (IS_ERR(name)) 55362306a36Sopenharmony_ci return PTR_ERR(name); 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci switch (opcode) { 55662306a36Sopenharmony_ci case ATTR_OP_GET: 55762306a36Sopenharmony_ci error = xfs_attrmulti_attr_get(inode, name, value, len, flags); 55862306a36Sopenharmony_ci break; 55962306a36Sopenharmony_ci case ATTR_OP_REMOVE: 56062306a36Sopenharmony_ci value = NULL; 56162306a36Sopenharmony_ci *len = 0; 56262306a36Sopenharmony_ci fallthrough; 56362306a36Sopenharmony_ci case ATTR_OP_SET: 56462306a36Sopenharmony_ci error = mnt_want_write_file(parfilp); 56562306a36Sopenharmony_ci if (error) 56662306a36Sopenharmony_ci break; 56762306a36Sopenharmony_ci error = xfs_attrmulti_attr_set(inode, name, value, *len, flags); 56862306a36Sopenharmony_ci mnt_drop_write_file(parfilp); 56962306a36Sopenharmony_ci break; 57062306a36Sopenharmony_ci default: 57162306a36Sopenharmony_ci error = -EINVAL; 57262306a36Sopenharmony_ci break; 57362306a36Sopenharmony_ci } 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci kfree(name); 57662306a36Sopenharmony_ci return error; 57762306a36Sopenharmony_ci} 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ciSTATIC int 58062306a36Sopenharmony_cixfs_attrmulti_by_handle( 58162306a36Sopenharmony_ci struct file *parfilp, 58262306a36Sopenharmony_ci void __user *arg) 58362306a36Sopenharmony_ci{ 58462306a36Sopenharmony_ci int error; 58562306a36Sopenharmony_ci xfs_attr_multiop_t *ops; 58662306a36Sopenharmony_ci xfs_fsop_attrmulti_handlereq_t am_hreq; 58762306a36Sopenharmony_ci struct dentry *dentry; 58862306a36Sopenharmony_ci unsigned int i, size; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 59162306a36Sopenharmony_ci return -EPERM; 59262306a36Sopenharmony_ci if (copy_from_user(&am_hreq, arg, sizeof(xfs_fsop_attrmulti_handlereq_t))) 59362306a36Sopenharmony_ci return -EFAULT; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci /* overflow check */ 59662306a36Sopenharmony_ci if (am_hreq.opcount >= INT_MAX / sizeof(xfs_attr_multiop_t)) 59762306a36Sopenharmony_ci return -E2BIG; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci dentry = xfs_handlereq_to_dentry(parfilp, &am_hreq.hreq); 60062306a36Sopenharmony_ci if (IS_ERR(dentry)) 60162306a36Sopenharmony_ci return PTR_ERR(dentry); 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci error = -E2BIG; 60462306a36Sopenharmony_ci size = am_hreq.opcount * sizeof(xfs_attr_multiop_t); 60562306a36Sopenharmony_ci if (!size || size > 16 * PAGE_SIZE) 60662306a36Sopenharmony_ci goto out_dput; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci ops = memdup_user(am_hreq.ops, size); 60962306a36Sopenharmony_ci if (IS_ERR(ops)) { 61062306a36Sopenharmony_ci error = PTR_ERR(ops); 61162306a36Sopenharmony_ci goto out_dput; 61262306a36Sopenharmony_ci } 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci error = 0; 61562306a36Sopenharmony_ci for (i = 0; i < am_hreq.opcount; i++) { 61662306a36Sopenharmony_ci ops[i].am_error = xfs_ioc_attrmulti_one(parfilp, 61762306a36Sopenharmony_ci d_inode(dentry), ops[i].am_opcode, 61862306a36Sopenharmony_ci ops[i].am_attrname, ops[i].am_attrvalue, 61962306a36Sopenharmony_ci &ops[i].am_length, ops[i].am_flags); 62062306a36Sopenharmony_ci } 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci if (copy_to_user(am_hreq.ops, ops, size)) 62362306a36Sopenharmony_ci error = -EFAULT; 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci kfree(ops); 62662306a36Sopenharmony_ci out_dput: 62762306a36Sopenharmony_ci dput(dentry); 62862306a36Sopenharmony_ci return error; 62962306a36Sopenharmony_ci} 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci/* Return 0 on success or positive error */ 63262306a36Sopenharmony_ciint 63362306a36Sopenharmony_cixfs_fsbulkstat_one_fmt( 63462306a36Sopenharmony_ci struct xfs_ibulk *breq, 63562306a36Sopenharmony_ci const struct xfs_bulkstat *bstat) 63662306a36Sopenharmony_ci{ 63762306a36Sopenharmony_ci struct xfs_bstat bs1; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci xfs_bulkstat_to_bstat(breq->mp, &bs1, bstat); 64062306a36Sopenharmony_ci if (copy_to_user(breq->ubuffer, &bs1, sizeof(bs1))) 64162306a36Sopenharmony_ci return -EFAULT; 64262306a36Sopenharmony_ci return xfs_ibulk_advance(breq, sizeof(struct xfs_bstat)); 64362306a36Sopenharmony_ci} 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ciint 64662306a36Sopenharmony_cixfs_fsinumbers_fmt( 64762306a36Sopenharmony_ci struct xfs_ibulk *breq, 64862306a36Sopenharmony_ci const struct xfs_inumbers *igrp) 64962306a36Sopenharmony_ci{ 65062306a36Sopenharmony_ci struct xfs_inogrp ig1; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci xfs_inumbers_to_inogrp(&ig1, igrp); 65362306a36Sopenharmony_ci if (copy_to_user(breq->ubuffer, &ig1, sizeof(struct xfs_inogrp))) 65462306a36Sopenharmony_ci return -EFAULT; 65562306a36Sopenharmony_ci return xfs_ibulk_advance(breq, sizeof(struct xfs_inogrp)); 65662306a36Sopenharmony_ci} 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ciSTATIC int 65962306a36Sopenharmony_cixfs_ioc_fsbulkstat( 66062306a36Sopenharmony_ci struct file *file, 66162306a36Sopenharmony_ci unsigned int cmd, 66262306a36Sopenharmony_ci void __user *arg) 66362306a36Sopenharmony_ci{ 66462306a36Sopenharmony_ci struct xfs_mount *mp = XFS_I(file_inode(file))->i_mount; 66562306a36Sopenharmony_ci struct xfs_fsop_bulkreq bulkreq; 66662306a36Sopenharmony_ci struct xfs_ibulk breq = { 66762306a36Sopenharmony_ci .mp = mp, 66862306a36Sopenharmony_ci .idmap = file_mnt_idmap(file), 66962306a36Sopenharmony_ci .ocount = 0, 67062306a36Sopenharmony_ci }; 67162306a36Sopenharmony_ci xfs_ino_t lastino; 67262306a36Sopenharmony_ci int error; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci /* done = 1 if there are more stats to get and if bulkstat */ 67562306a36Sopenharmony_ci /* should be called again (unused here, but used in dmapi) */ 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 67862306a36Sopenharmony_ci return -EPERM; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci if (xfs_is_shutdown(mp)) 68162306a36Sopenharmony_ci return -EIO; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci if (copy_from_user(&bulkreq, arg, sizeof(struct xfs_fsop_bulkreq))) 68462306a36Sopenharmony_ci return -EFAULT; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci if (copy_from_user(&lastino, bulkreq.lastip, sizeof(__s64))) 68762306a36Sopenharmony_ci return -EFAULT; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci if (bulkreq.icount <= 0) 69062306a36Sopenharmony_ci return -EINVAL; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci if (bulkreq.ubuffer == NULL) 69362306a36Sopenharmony_ci return -EINVAL; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci breq.ubuffer = bulkreq.ubuffer; 69662306a36Sopenharmony_ci breq.icount = bulkreq.icount; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci /* 69962306a36Sopenharmony_ci * FSBULKSTAT_SINGLE expects that *lastip contains the inode number 70062306a36Sopenharmony_ci * that we want to stat. However, FSINUMBERS and FSBULKSTAT expect 70162306a36Sopenharmony_ci * that *lastip contains either zero or the number of the last inode to 70262306a36Sopenharmony_ci * be examined by the previous call and return results starting with 70362306a36Sopenharmony_ci * the next inode after that. The new bulk request back end functions 70462306a36Sopenharmony_ci * take the inode to start with, so we have to compute the startino 70562306a36Sopenharmony_ci * parameter from lastino to maintain correct function. lastino == 0 70662306a36Sopenharmony_ci * is a special case because it has traditionally meant "first inode 70762306a36Sopenharmony_ci * in filesystem". 70862306a36Sopenharmony_ci */ 70962306a36Sopenharmony_ci if (cmd == XFS_IOC_FSINUMBERS) { 71062306a36Sopenharmony_ci breq.startino = lastino ? lastino + 1 : 0; 71162306a36Sopenharmony_ci error = xfs_inumbers(&breq, xfs_fsinumbers_fmt); 71262306a36Sopenharmony_ci lastino = breq.startino - 1; 71362306a36Sopenharmony_ci } else if (cmd == XFS_IOC_FSBULKSTAT_SINGLE) { 71462306a36Sopenharmony_ci breq.startino = lastino; 71562306a36Sopenharmony_ci breq.icount = 1; 71662306a36Sopenharmony_ci error = xfs_bulkstat_one(&breq, xfs_fsbulkstat_one_fmt); 71762306a36Sopenharmony_ci } else { /* XFS_IOC_FSBULKSTAT */ 71862306a36Sopenharmony_ci breq.startino = lastino ? lastino + 1 : 0; 71962306a36Sopenharmony_ci error = xfs_bulkstat(&breq, xfs_fsbulkstat_one_fmt); 72062306a36Sopenharmony_ci lastino = breq.startino - 1; 72162306a36Sopenharmony_ci } 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci if (error) 72462306a36Sopenharmony_ci return error; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci if (bulkreq.lastip != NULL && 72762306a36Sopenharmony_ci copy_to_user(bulkreq.lastip, &lastino, sizeof(xfs_ino_t))) 72862306a36Sopenharmony_ci return -EFAULT; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci if (bulkreq.ocount != NULL && 73162306a36Sopenharmony_ci copy_to_user(bulkreq.ocount, &breq.ocount, sizeof(__s32))) 73262306a36Sopenharmony_ci return -EFAULT; 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci return 0; 73562306a36Sopenharmony_ci} 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci/* Return 0 on success or positive error */ 73862306a36Sopenharmony_cistatic int 73962306a36Sopenharmony_cixfs_bulkstat_fmt( 74062306a36Sopenharmony_ci struct xfs_ibulk *breq, 74162306a36Sopenharmony_ci const struct xfs_bulkstat *bstat) 74262306a36Sopenharmony_ci{ 74362306a36Sopenharmony_ci if (copy_to_user(breq->ubuffer, bstat, sizeof(struct xfs_bulkstat))) 74462306a36Sopenharmony_ci return -EFAULT; 74562306a36Sopenharmony_ci return xfs_ibulk_advance(breq, sizeof(struct xfs_bulkstat)); 74662306a36Sopenharmony_ci} 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci/* 74962306a36Sopenharmony_ci * Check the incoming bulk request @hdr from userspace and initialize the 75062306a36Sopenharmony_ci * internal @breq bulk request appropriately. Returns 0 if the bulk request 75162306a36Sopenharmony_ci * should proceed; -ECANCELED if there's nothing to do; or the usual 75262306a36Sopenharmony_ci * negative error code. 75362306a36Sopenharmony_ci */ 75462306a36Sopenharmony_cistatic int 75562306a36Sopenharmony_cixfs_bulk_ireq_setup( 75662306a36Sopenharmony_ci struct xfs_mount *mp, 75762306a36Sopenharmony_ci const struct xfs_bulk_ireq *hdr, 75862306a36Sopenharmony_ci struct xfs_ibulk *breq, 75962306a36Sopenharmony_ci void __user *ubuffer) 76062306a36Sopenharmony_ci{ 76162306a36Sopenharmony_ci if (hdr->icount == 0 || 76262306a36Sopenharmony_ci (hdr->flags & ~XFS_BULK_IREQ_FLAGS_ALL) || 76362306a36Sopenharmony_ci memchr_inv(hdr->reserved, 0, sizeof(hdr->reserved))) 76462306a36Sopenharmony_ci return -EINVAL; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci breq->startino = hdr->ino; 76762306a36Sopenharmony_ci breq->ubuffer = ubuffer; 76862306a36Sopenharmony_ci breq->icount = hdr->icount; 76962306a36Sopenharmony_ci breq->ocount = 0; 77062306a36Sopenharmony_ci breq->flags = 0; 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci /* 77362306a36Sopenharmony_ci * The @ino parameter is a special value, so we must look it up here. 77462306a36Sopenharmony_ci * We're not allowed to have IREQ_AGNO, and we only return one inode 77562306a36Sopenharmony_ci * worth of data. 77662306a36Sopenharmony_ci */ 77762306a36Sopenharmony_ci if (hdr->flags & XFS_BULK_IREQ_SPECIAL) { 77862306a36Sopenharmony_ci if (hdr->flags & XFS_BULK_IREQ_AGNO) 77962306a36Sopenharmony_ci return -EINVAL; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci switch (hdr->ino) { 78262306a36Sopenharmony_ci case XFS_BULK_IREQ_SPECIAL_ROOT: 78362306a36Sopenharmony_ci breq->startino = mp->m_sb.sb_rootino; 78462306a36Sopenharmony_ci break; 78562306a36Sopenharmony_ci default: 78662306a36Sopenharmony_ci return -EINVAL; 78762306a36Sopenharmony_ci } 78862306a36Sopenharmony_ci breq->icount = 1; 78962306a36Sopenharmony_ci } 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci /* 79262306a36Sopenharmony_ci * The IREQ_AGNO flag means that we only want results from a given AG. 79362306a36Sopenharmony_ci * If @hdr->ino is zero, we start iterating in that AG. If @hdr->ino is 79462306a36Sopenharmony_ci * beyond the specified AG then we return no results. 79562306a36Sopenharmony_ci */ 79662306a36Sopenharmony_ci if (hdr->flags & XFS_BULK_IREQ_AGNO) { 79762306a36Sopenharmony_ci if (hdr->agno >= mp->m_sb.sb_agcount) 79862306a36Sopenharmony_ci return -EINVAL; 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci if (breq->startino == 0) 80162306a36Sopenharmony_ci breq->startino = XFS_AGINO_TO_INO(mp, hdr->agno, 0); 80262306a36Sopenharmony_ci else if (XFS_INO_TO_AGNO(mp, breq->startino) < hdr->agno) 80362306a36Sopenharmony_ci return -EINVAL; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci breq->flags |= XFS_IBULK_SAME_AG; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci /* Asking for an inode past the end of the AG? We're done! */ 80862306a36Sopenharmony_ci if (XFS_INO_TO_AGNO(mp, breq->startino) > hdr->agno) 80962306a36Sopenharmony_ci return -ECANCELED; 81062306a36Sopenharmony_ci } else if (hdr->agno) 81162306a36Sopenharmony_ci return -EINVAL; 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci /* Asking for an inode past the end of the FS? We're done! */ 81462306a36Sopenharmony_ci if (XFS_INO_TO_AGNO(mp, breq->startino) >= mp->m_sb.sb_agcount) 81562306a36Sopenharmony_ci return -ECANCELED; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci if (hdr->flags & XFS_BULK_IREQ_NREXT64) 81862306a36Sopenharmony_ci breq->flags |= XFS_IBULK_NREXT64; 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci return 0; 82162306a36Sopenharmony_ci} 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci/* 82462306a36Sopenharmony_ci * Update the userspace bulk request @hdr to reflect the end state of the 82562306a36Sopenharmony_ci * internal bulk request @breq. 82662306a36Sopenharmony_ci */ 82762306a36Sopenharmony_cistatic void 82862306a36Sopenharmony_cixfs_bulk_ireq_teardown( 82962306a36Sopenharmony_ci struct xfs_bulk_ireq *hdr, 83062306a36Sopenharmony_ci struct xfs_ibulk *breq) 83162306a36Sopenharmony_ci{ 83262306a36Sopenharmony_ci hdr->ino = breq->startino; 83362306a36Sopenharmony_ci hdr->ocount = breq->ocount; 83462306a36Sopenharmony_ci} 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci/* Handle the v5 bulkstat ioctl. */ 83762306a36Sopenharmony_ciSTATIC int 83862306a36Sopenharmony_cixfs_ioc_bulkstat( 83962306a36Sopenharmony_ci struct file *file, 84062306a36Sopenharmony_ci unsigned int cmd, 84162306a36Sopenharmony_ci struct xfs_bulkstat_req __user *arg) 84262306a36Sopenharmony_ci{ 84362306a36Sopenharmony_ci struct xfs_mount *mp = XFS_I(file_inode(file))->i_mount; 84462306a36Sopenharmony_ci struct xfs_bulk_ireq hdr; 84562306a36Sopenharmony_ci struct xfs_ibulk breq = { 84662306a36Sopenharmony_ci .mp = mp, 84762306a36Sopenharmony_ci .idmap = file_mnt_idmap(file), 84862306a36Sopenharmony_ci }; 84962306a36Sopenharmony_ci int error; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 85262306a36Sopenharmony_ci return -EPERM; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci if (xfs_is_shutdown(mp)) 85562306a36Sopenharmony_ci return -EIO; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci if (copy_from_user(&hdr, &arg->hdr, sizeof(hdr))) 85862306a36Sopenharmony_ci return -EFAULT; 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci error = xfs_bulk_ireq_setup(mp, &hdr, &breq, arg->bulkstat); 86162306a36Sopenharmony_ci if (error == -ECANCELED) 86262306a36Sopenharmony_ci goto out_teardown; 86362306a36Sopenharmony_ci if (error < 0) 86462306a36Sopenharmony_ci return error; 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci error = xfs_bulkstat(&breq, xfs_bulkstat_fmt); 86762306a36Sopenharmony_ci if (error) 86862306a36Sopenharmony_ci return error; 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ciout_teardown: 87162306a36Sopenharmony_ci xfs_bulk_ireq_teardown(&hdr, &breq); 87262306a36Sopenharmony_ci if (copy_to_user(&arg->hdr, &hdr, sizeof(hdr))) 87362306a36Sopenharmony_ci return -EFAULT; 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci return 0; 87662306a36Sopenharmony_ci} 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ciSTATIC int 87962306a36Sopenharmony_cixfs_inumbers_fmt( 88062306a36Sopenharmony_ci struct xfs_ibulk *breq, 88162306a36Sopenharmony_ci const struct xfs_inumbers *igrp) 88262306a36Sopenharmony_ci{ 88362306a36Sopenharmony_ci if (copy_to_user(breq->ubuffer, igrp, sizeof(struct xfs_inumbers))) 88462306a36Sopenharmony_ci return -EFAULT; 88562306a36Sopenharmony_ci return xfs_ibulk_advance(breq, sizeof(struct xfs_inumbers)); 88662306a36Sopenharmony_ci} 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci/* Handle the v5 inumbers ioctl. */ 88962306a36Sopenharmony_ciSTATIC int 89062306a36Sopenharmony_cixfs_ioc_inumbers( 89162306a36Sopenharmony_ci struct xfs_mount *mp, 89262306a36Sopenharmony_ci unsigned int cmd, 89362306a36Sopenharmony_ci struct xfs_inumbers_req __user *arg) 89462306a36Sopenharmony_ci{ 89562306a36Sopenharmony_ci struct xfs_bulk_ireq hdr; 89662306a36Sopenharmony_ci struct xfs_ibulk breq = { 89762306a36Sopenharmony_ci .mp = mp, 89862306a36Sopenharmony_ci }; 89962306a36Sopenharmony_ci int error; 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 90262306a36Sopenharmony_ci return -EPERM; 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci if (xfs_is_shutdown(mp)) 90562306a36Sopenharmony_ci return -EIO; 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci if (copy_from_user(&hdr, &arg->hdr, sizeof(hdr))) 90862306a36Sopenharmony_ci return -EFAULT; 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci error = xfs_bulk_ireq_setup(mp, &hdr, &breq, arg->inumbers); 91162306a36Sopenharmony_ci if (error == -ECANCELED) 91262306a36Sopenharmony_ci goto out_teardown; 91362306a36Sopenharmony_ci if (error < 0) 91462306a36Sopenharmony_ci return error; 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci error = xfs_inumbers(&breq, xfs_inumbers_fmt); 91762306a36Sopenharmony_ci if (error) 91862306a36Sopenharmony_ci return error; 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ciout_teardown: 92162306a36Sopenharmony_ci xfs_bulk_ireq_teardown(&hdr, &breq); 92262306a36Sopenharmony_ci if (copy_to_user(&arg->hdr, &hdr, sizeof(hdr))) 92362306a36Sopenharmony_ci return -EFAULT; 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci return 0; 92662306a36Sopenharmony_ci} 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ciSTATIC int 92962306a36Sopenharmony_cixfs_ioc_fsgeometry( 93062306a36Sopenharmony_ci struct xfs_mount *mp, 93162306a36Sopenharmony_ci void __user *arg, 93262306a36Sopenharmony_ci int struct_version) 93362306a36Sopenharmony_ci{ 93462306a36Sopenharmony_ci struct xfs_fsop_geom fsgeo; 93562306a36Sopenharmony_ci size_t len; 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci xfs_fs_geometry(mp, &fsgeo, struct_version); 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci if (struct_version <= 3) 94062306a36Sopenharmony_ci len = sizeof(struct xfs_fsop_geom_v1); 94162306a36Sopenharmony_ci else if (struct_version == 4) 94262306a36Sopenharmony_ci len = sizeof(struct xfs_fsop_geom_v4); 94362306a36Sopenharmony_ci else { 94462306a36Sopenharmony_ci xfs_fsop_geom_health(mp, &fsgeo); 94562306a36Sopenharmony_ci len = sizeof(fsgeo); 94662306a36Sopenharmony_ci } 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci if (copy_to_user(arg, &fsgeo, len)) 94962306a36Sopenharmony_ci return -EFAULT; 95062306a36Sopenharmony_ci return 0; 95162306a36Sopenharmony_ci} 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ciSTATIC int 95462306a36Sopenharmony_cixfs_ioc_ag_geometry( 95562306a36Sopenharmony_ci struct xfs_mount *mp, 95662306a36Sopenharmony_ci void __user *arg) 95762306a36Sopenharmony_ci{ 95862306a36Sopenharmony_ci struct xfs_perag *pag; 95962306a36Sopenharmony_ci struct xfs_ag_geometry ageo; 96062306a36Sopenharmony_ci int error; 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci if (copy_from_user(&ageo, arg, sizeof(ageo))) 96362306a36Sopenharmony_ci return -EFAULT; 96462306a36Sopenharmony_ci if (ageo.ag_flags) 96562306a36Sopenharmony_ci return -EINVAL; 96662306a36Sopenharmony_ci if (memchr_inv(&ageo.ag_reserved, 0, sizeof(ageo.ag_reserved))) 96762306a36Sopenharmony_ci return -EINVAL; 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci pag = xfs_perag_get(mp, ageo.ag_number); 97062306a36Sopenharmony_ci if (!pag) 97162306a36Sopenharmony_ci return -EINVAL; 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci error = xfs_ag_get_geometry(pag, &ageo); 97462306a36Sopenharmony_ci xfs_perag_put(pag); 97562306a36Sopenharmony_ci if (error) 97662306a36Sopenharmony_ci return error; 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci if (copy_to_user(arg, &ageo, sizeof(ageo))) 97962306a36Sopenharmony_ci return -EFAULT; 98062306a36Sopenharmony_ci return 0; 98162306a36Sopenharmony_ci} 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci/* 98462306a36Sopenharmony_ci * Linux extended inode flags interface. 98562306a36Sopenharmony_ci */ 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_cistatic void 98862306a36Sopenharmony_cixfs_fill_fsxattr( 98962306a36Sopenharmony_ci struct xfs_inode *ip, 99062306a36Sopenharmony_ci int whichfork, 99162306a36Sopenharmony_ci struct fileattr *fa) 99262306a36Sopenharmony_ci{ 99362306a36Sopenharmony_ci struct xfs_mount *mp = ip->i_mount; 99462306a36Sopenharmony_ci struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci fileattr_fill_xflags(fa, xfs_ip2xflags(ip)); 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci if (ip->i_diflags & XFS_DIFLAG_EXTSIZE) { 99962306a36Sopenharmony_ci fa->fsx_extsize = XFS_FSB_TO_B(mp, ip->i_extsize); 100062306a36Sopenharmony_ci } else if (ip->i_diflags & XFS_DIFLAG_EXTSZINHERIT) { 100162306a36Sopenharmony_ci /* 100262306a36Sopenharmony_ci * Don't let a misaligned extent size hint on a directory 100362306a36Sopenharmony_ci * escape to userspace if it won't pass the setattr checks 100462306a36Sopenharmony_ci * later. 100562306a36Sopenharmony_ci */ 100662306a36Sopenharmony_ci if ((ip->i_diflags & XFS_DIFLAG_RTINHERIT) && 100762306a36Sopenharmony_ci ip->i_extsize % mp->m_sb.sb_rextsize > 0) { 100862306a36Sopenharmony_ci fa->fsx_xflags &= ~(FS_XFLAG_EXTSIZE | 100962306a36Sopenharmony_ci FS_XFLAG_EXTSZINHERIT); 101062306a36Sopenharmony_ci fa->fsx_extsize = 0; 101162306a36Sopenharmony_ci } else { 101262306a36Sopenharmony_ci fa->fsx_extsize = XFS_FSB_TO_B(mp, ip->i_extsize); 101362306a36Sopenharmony_ci } 101462306a36Sopenharmony_ci } 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci if (ip->i_diflags2 & XFS_DIFLAG2_COWEXTSIZE) 101762306a36Sopenharmony_ci fa->fsx_cowextsize = XFS_FSB_TO_B(mp, ip->i_cowextsize); 101862306a36Sopenharmony_ci fa->fsx_projid = ip->i_projid; 101962306a36Sopenharmony_ci if (ifp && !xfs_need_iread_extents(ifp)) 102062306a36Sopenharmony_ci fa->fsx_nextents = xfs_iext_count(ifp); 102162306a36Sopenharmony_ci else 102262306a36Sopenharmony_ci fa->fsx_nextents = xfs_ifork_nextents(ifp); 102362306a36Sopenharmony_ci} 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ciSTATIC int 102662306a36Sopenharmony_cixfs_ioc_fsgetxattra( 102762306a36Sopenharmony_ci xfs_inode_t *ip, 102862306a36Sopenharmony_ci void __user *arg) 102962306a36Sopenharmony_ci{ 103062306a36Sopenharmony_ci struct fileattr fa; 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci xfs_ilock(ip, XFS_ILOCK_SHARED); 103362306a36Sopenharmony_ci xfs_fill_fsxattr(ip, XFS_ATTR_FORK, &fa); 103462306a36Sopenharmony_ci xfs_iunlock(ip, XFS_ILOCK_SHARED); 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci return copy_fsxattr_to_user(&fa, arg); 103762306a36Sopenharmony_ci} 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ciint 104062306a36Sopenharmony_cixfs_fileattr_get( 104162306a36Sopenharmony_ci struct dentry *dentry, 104262306a36Sopenharmony_ci struct fileattr *fa) 104362306a36Sopenharmony_ci{ 104462306a36Sopenharmony_ci struct xfs_inode *ip = XFS_I(d_inode(dentry)); 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci if (d_is_special(dentry)) 104762306a36Sopenharmony_ci return -ENOTTY; 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci xfs_ilock(ip, XFS_ILOCK_SHARED); 105062306a36Sopenharmony_ci xfs_fill_fsxattr(ip, XFS_DATA_FORK, fa); 105162306a36Sopenharmony_ci xfs_iunlock(ip, XFS_ILOCK_SHARED); 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci return 0; 105462306a36Sopenharmony_ci} 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ciSTATIC uint16_t 105762306a36Sopenharmony_cixfs_flags2diflags( 105862306a36Sopenharmony_ci struct xfs_inode *ip, 105962306a36Sopenharmony_ci unsigned int xflags) 106062306a36Sopenharmony_ci{ 106162306a36Sopenharmony_ci /* can't set PREALLOC this way, just preserve it */ 106262306a36Sopenharmony_ci uint16_t di_flags = 106362306a36Sopenharmony_ci (ip->i_diflags & XFS_DIFLAG_PREALLOC); 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci if (xflags & FS_XFLAG_IMMUTABLE) 106662306a36Sopenharmony_ci di_flags |= XFS_DIFLAG_IMMUTABLE; 106762306a36Sopenharmony_ci if (xflags & FS_XFLAG_APPEND) 106862306a36Sopenharmony_ci di_flags |= XFS_DIFLAG_APPEND; 106962306a36Sopenharmony_ci if (xflags & FS_XFLAG_SYNC) 107062306a36Sopenharmony_ci di_flags |= XFS_DIFLAG_SYNC; 107162306a36Sopenharmony_ci if (xflags & FS_XFLAG_NOATIME) 107262306a36Sopenharmony_ci di_flags |= XFS_DIFLAG_NOATIME; 107362306a36Sopenharmony_ci if (xflags & FS_XFLAG_NODUMP) 107462306a36Sopenharmony_ci di_flags |= XFS_DIFLAG_NODUMP; 107562306a36Sopenharmony_ci if (xflags & FS_XFLAG_NODEFRAG) 107662306a36Sopenharmony_ci di_flags |= XFS_DIFLAG_NODEFRAG; 107762306a36Sopenharmony_ci if (xflags & FS_XFLAG_FILESTREAM) 107862306a36Sopenharmony_ci di_flags |= XFS_DIFLAG_FILESTREAM; 107962306a36Sopenharmony_ci if (S_ISDIR(VFS_I(ip)->i_mode)) { 108062306a36Sopenharmony_ci if (xflags & FS_XFLAG_RTINHERIT) 108162306a36Sopenharmony_ci di_flags |= XFS_DIFLAG_RTINHERIT; 108262306a36Sopenharmony_ci if (xflags & FS_XFLAG_NOSYMLINKS) 108362306a36Sopenharmony_ci di_flags |= XFS_DIFLAG_NOSYMLINKS; 108462306a36Sopenharmony_ci if (xflags & FS_XFLAG_EXTSZINHERIT) 108562306a36Sopenharmony_ci di_flags |= XFS_DIFLAG_EXTSZINHERIT; 108662306a36Sopenharmony_ci if (xflags & FS_XFLAG_PROJINHERIT) 108762306a36Sopenharmony_ci di_flags |= XFS_DIFLAG_PROJINHERIT; 108862306a36Sopenharmony_ci } else if (S_ISREG(VFS_I(ip)->i_mode)) { 108962306a36Sopenharmony_ci if (xflags & FS_XFLAG_REALTIME) 109062306a36Sopenharmony_ci di_flags |= XFS_DIFLAG_REALTIME; 109162306a36Sopenharmony_ci if (xflags & FS_XFLAG_EXTSIZE) 109262306a36Sopenharmony_ci di_flags |= XFS_DIFLAG_EXTSIZE; 109362306a36Sopenharmony_ci } 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci return di_flags; 109662306a36Sopenharmony_ci} 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ciSTATIC uint64_t 109962306a36Sopenharmony_cixfs_flags2diflags2( 110062306a36Sopenharmony_ci struct xfs_inode *ip, 110162306a36Sopenharmony_ci unsigned int xflags) 110262306a36Sopenharmony_ci{ 110362306a36Sopenharmony_ci uint64_t di_flags2 = 110462306a36Sopenharmony_ci (ip->i_diflags2 & (XFS_DIFLAG2_REFLINK | 110562306a36Sopenharmony_ci XFS_DIFLAG2_BIGTIME | 110662306a36Sopenharmony_ci XFS_DIFLAG2_NREXT64)); 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci if (xflags & FS_XFLAG_DAX) 110962306a36Sopenharmony_ci di_flags2 |= XFS_DIFLAG2_DAX; 111062306a36Sopenharmony_ci if (xflags & FS_XFLAG_COWEXTSIZE) 111162306a36Sopenharmony_ci di_flags2 |= XFS_DIFLAG2_COWEXTSIZE; 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci return di_flags2; 111462306a36Sopenharmony_ci} 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_cistatic int 111762306a36Sopenharmony_cixfs_ioctl_setattr_xflags( 111862306a36Sopenharmony_ci struct xfs_trans *tp, 111962306a36Sopenharmony_ci struct xfs_inode *ip, 112062306a36Sopenharmony_ci struct fileattr *fa) 112162306a36Sopenharmony_ci{ 112262306a36Sopenharmony_ci struct xfs_mount *mp = ip->i_mount; 112362306a36Sopenharmony_ci bool rtflag = (fa->fsx_xflags & FS_XFLAG_REALTIME); 112462306a36Sopenharmony_ci uint64_t i_flags2; 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci if (rtflag != XFS_IS_REALTIME_INODE(ip)) { 112762306a36Sopenharmony_ci /* Can't change realtime flag if any extents are allocated. */ 112862306a36Sopenharmony_ci if (ip->i_df.if_nextents || ip->i_delayed_blks) 112962306a36Sopenharmony_ci return -EINVAL; 113062306a36Sopenharmony_ci } 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci if (rtflag) { 113362306a36Sopenharmony_ci /* If realtime flag is set then must have realtime device */ 113462306a36Sopenharmony_ci if (mp->m_sb.sb_rblocks == 0 || mp->m_sb.sb_rextsize == 0 || 113562306a36Sopenharmony_ci (ip->i_extsize % mp->m_sb.sb_rextsize)) 113662306a36Sopenharmony_ci return -EINVAL; 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci /* Clear reflink if we are actually able to set the rt flag. */ 113962306a36Sopenharmony_ci if (xfs_is_reflink_inode(ip)) 114062306a36Sopenharmony_ci ip->i_diflags2 &= ~XFS_DIFLAG2_REFLINK; 114162306a36Sopenharmony_ci } 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci /* diflags2 only valid for v3 inodes. */ 114462306a36Sopenharmony_ci i_flags2 = xfs_flags2diflags2(ip, fa->fsx_xflags); 114562306a36Sopenharmony_ci if (i_flags2 && !xfs_has_v3inodes(mp)) 114662306a36Sopenharmony_ci return -EINVAL; 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_ci ip->i_diflags = xfs_flags2diflags(ip, fa->fsx_xflags); 114962306a36Sopenharmony_ci ip->i_diflags2 = i_flags2; 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci xfs_diflags_to_iflags(ip, false); 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci /* 115462306a36Sopenharmony_ci * Make the stable writes flag match that of the device the inode 115562306a36Sopenharmony_ci * resides on when flipping the RT flag. 115662306a36Sopenharmony_ci */ 115762306a36Sopenharmony_ci if (rtflag != XFS_IS_REALTIME_INODE(ip) && S_ISREG(VFS_I(ip)->i_mode)) 115862306a36Sopenharmony_ci xfs_update_stable_writes(ip); 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_CHG); 116162306a36Sopenharmony_ci xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); 116262306a36Sopenharmony_ci XFS_STATS_INC(mp, xs_ig_attrchg); 116362306a36Sopenharmony_ci return 0; 116462306a36Sopenharmony_ci} 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_cistatic void 116762306a36Sopenharmony_cixfs_ioctl_setattr_prepare_dax( 116862306a36Sopenharmony_ci struct xfs_inode *ip, 116962306a36Sopenharmony_ci struct fileattr *fa) 117062306a36Sopenharmony_ci{ 117162306a36Sopenharmony_ci struct xfs_mount *mp = ip->i_mount; 117262306a36Sopenharmony_ci struct inode *inode = VFS_I(ip); 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci if (S_ISDIR(inode->i_mode)) 117562306a36Sopenharmony_ci return; 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci if (xfs_has_dax_always(mp) || xfs_has_dax_never(mp)) 117862306a36Sopenharmony_ci return; 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci if (((fa->fsx_xflags & FS_XFLAG_DAX) && 118162306a36Sopenharmony_ci !(ip->i_diflags2 & XFS_DIFLAG2_DAX)) || 118262306a36Sopenharmony_ci (!(fa->fsx_xflags & FS_XFLAG_DAX) && 118362306a36Sopenharmony_ci (ip->i_diflags2 & XFS_DIFLAG2_DAX))) 118462306a36Sopenharmony_ci d_mark_dontcache(inode); 118562306a36Sopenharmony_ci} 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci/* 118862306a36Sopenharmony_ci * Set up the transaction structure for the setattr operation, checking that we 118962306a36Sopenharmony_ci * have permission to do so. On success, return a clean transaction and the 119062306a36Sopenharmony_ci * inode locked exclusively ready for further operation specific checks. On 119162306a36Sopenharmony_ci * failure, return an error without modifying or locking the inode. 119262306a36Sopenharmony_ci */ 119362306a36Sopenharmony_cistatic struct xfs_trans * 119462306a36Sopenharmony_cixfs_ioctl_setattr_get_trans( 119562306a36Sopenharmony_ci struct xfs_inode *ip, 119662306a36Sopenharmony_ci struct xfs_dquot *pdqp) 119762306a36Sopenharmony_ci{ 119862306a36Sopenharmony_ci struct xfs_mount *mp = ip->i_mount; 119962306a36Sopenharmony_ci struct xfs_trans *tp; 120062306a36Sopenharmony_ci int error = -EROFS; 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_ci if (xfs_is_readonly(mp)) 120362306a36Sopenharmony_ci goto out_error; 120462306a36Sopenharmony_ci error = -EIO; 120562306a36Sopenharmony_ci if (xfs_is_shutdown(mp)) 120662306a36Sopenharmony_ci goto out_error; 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci error = xfs_trans_alloc_ichange(ip, NULL, NULL, pdqp, 120962306a36Sopenharmony_ci has_capability_noaudit(current, CAP_FOWNER), &tp); 121062306a36Sopenharmony_ci if (error) 121162306a36Sopenharmony_ci goto out_error; 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci if (xfs_has_wsync(mp)) 121462306a36Sopenharmony_ci xfs_trans_set_sync(tp); 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci return tp; 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ciout_error: 121962306a36Sopenharmony_ci return ERR_PTR(error); 122062306a36Sopenharmony_ci} 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci/* 122362306a36Sopenharmony_ci * Validate a proposed extent size hint. For regular files, the hint can only 122462306a36Sopenharmony_ci * be changed if no extents are allocated. 122562306a36Sopenharmony_ci */ 122662306a36Sopenharmony_cistatic int 122762306a36Sopenharmony_cixfs_ioctl_setattr_check_extsize( 122862306a36Sopenharmony_ci struct xfs_inode *ip, 122962306a36Sopenharmony_ci struct fileattr *fa) 123062306a36Sopenharmony_ci{ 123162306a36Sopenharmony_ci struct xfs_mount *mp = ip->i_mount; 123262306a36Sopenharmony_ci xfs_failaddr_t failaddr; 123362306a36Sopenharmony_ci uint16_t new_diflags; 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci if (!fa->fsx_valid) 123662306a36Sopenharmony_ci return 0; 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci if (S_ISREG(VFS_I(ip)->i_mode) && ip->i_df.if_nextents && 123962306a36Sopenharmony_ci XFS_FSB_TO_B(mp, ip->i_extsize) != fa->fsx_extsize) 124062306a36Sopenharmony_ci return -EINVAL; 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci if (fa->fsx_extsize & mp->m_blockmask) 124362306a36Sopenharmony_ci return -EINVAL; 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci new_diflags = xfs_flags2diflags(ip, fa->fsx_xflags); 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_ci /* 124862306a36Sopenharmony_ci * Inode verifiers do not check that the extent size hint is an integer 124962306a36Sopenharmony_ci * multiple of the rt extent size on a directory with both rtinherit 125062306a36Sopenharmony_ci * and extszinherit flags set. Don't let sysadmins misconfigure 125162306a36Sopenharmony_ci * directories. 125262306a36Sopenharmony_ci */ 125362306a36Sopenharmony_ci if ((new_diflags & XFS_DIFLAG_RTINHERIT) && 125462306a36Sopenharmony_ci (new_diflags & XFS_DIFLAG_EXTSZINHERIT)) { 125562306a36Sopenharmony_ci unsigned int rtextsize_bytes; 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_ci rtextsize_bytes = XFS_FSB_TO_B(mp, mp->m_sb.sb_rextsize); 125862306a36Sopenharmony_ci if (fa->fsx_extsize % rtextsize_bytes) 125962306a36Sopenharmony_ci return -EINVAL; 126062306a36Sopenharmony_ci } 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci failaddr = xfs_inode_validate_extsize(ip->i_mount, 126362306a36Sopenharmony_ci XFS_B_TO_FSB(mp, fa->fsx_extsize), 126462306a36Sopenharmony_ci VFS_I(ip)->i_mode, new_diflags); 126562306a36Sopenharmony_ci return failaddr != NULL ? -EINVAL : 0; 126662306a36Sopenharmony_ci} 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_cistatic int 126962306a36Sopenharmony_cixfs_ioctl_setattr_check_cowextsize( 127062306a36Sopenharmony_ci struct xfs_inode *ip, 127162306a36Sopenharmony_ci struct fileattr *fa) 127262306a36Sopenharmony_ci{ 127362306a36Sopenharmony_ci struct xfs_mount *mp = ip->i_mount; 127462306a36Sopenharmony_ci xfs_failaddr_t failaddr; 127562306a36Sopenharmony_ci uint64_t new_diflags2; 127662306a36Sopenharmony_ci uint16_t new_diflags; 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci if (!fa->fsx_valid) 127962306a36Sopenharmony_ci return 0; 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_ci if (fa->fsx_cowextsize & mp->m_blockmask) 128262306a36Sopenharmony_ci return -EINVAL; 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci new_diflags = xfs_flags2diflags(ip, fa->fsx_xflags); 128562306a36Sopenharmony_ci new_diflags2 = xfs_flags2diflags2(ip, fa->fsx_xflags); 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci failaddr = xfs_inode_validate_cowextsize(ip->i_mount, 128862306a36Sopenharmony_ci XFS_B_TO_FSB(mp, fa->fsx_cowextsize), 128962306a36Sopenharmony_ci VFS_I(ip)->i_mode, new_diflags, new_diflags2); 129062306a36Sopenharmony_ci return failaddr != NULL ? -EINVAL : 0; 129162306a36Sopenharmony_ci} 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_cistatic int 129462306a36Sopenharmony_cixfs_ioctl_setattr_check_projid( 129562306a36Sopenharmony_ci struct xfs_inode *ip, 129662306a36Sopenharmony_ci struct fileattr *fa) 129762306a36Sopenharmony_ci{ 129862306a36Sopenharmony_ci if (!fa->fsx_valid) 129962306a36Sopenharmony_ci return 0; 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_ci /* Disallow 32bit project ids if 32bit IDs are not enabled. */ 130262306a36Sopenharmony_ci if (fa->fsx_projid > (uint16_t)-1 && 130362306a36Sopenharmony_ci !xfs_has_projid32(ip->i_mount)) 130462306a36Sopenharmony_ci return -EINVAL; 130562306a36Sopenharmony_ci return 0; 130662306a36Sopenharmony_ci} 130762306a36Sopenharmony_ci 130862306a36Sopenharmony_ciint 130962306a36Sopenharmony_cixfs_fileattr_set( 131062306a36Sopenharmony_ci struct mnt_idmap *idmap, 131162306a36Sopenharmony_ci struct dentry *dentry, 131262306a36Sopenharmony_ci struct fileattr *fa) 131362306a36Sopenharmony_ci{ 131462306a36Sopenharmony_ci struct xfs_inode *ip = XFS_I(d_inode(dentry)); 131562306a36Sopenharmony_ci struct xfs_mount *mp = ip->i_mount; 131662306a36Sopenharmony_ci struct xfs_trans *tp; 131762306a36Sopenharmony_ci struct xfs_dquot *pdqp = NULL; 131862306a36Sopenharmony_ci struct xfs_dquot *olddquot = NULL; 131962306a36Sopenharmony_ci int error; 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci trace_xfs_ioctl_setattr(ip); 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_ci if (d_is_special(dentry)) 132462306a36Sopenharmony_ci return -ENOTTY; 132562306a36Sopenharmony_ci 132662306a36Sopenharmony_ci if (!fa->fsx_valid) { 132762306a36Sopenharmony_ci if (fa->flags & ~(FS_IMMUTABLE_FL | FS_APPEND_FL | 132862306a36Sopenharmony_ci FS_NOATIME_FL | FS_NODUMP_FL | 132962306a36Sopenharmony_ci FS_SYNC_FL | FS_DAX_FL | FS_PROJINHERIT_FL)) 133062306a36Sopenharmony_ci return -EOPNOTSUPP; 133162306a36Sopenharmony_ci } 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_ci error = xfs_ioctl_setattr_check_projid(ip, fa); 133462306a36Sopenharmony_ci if (error) 133562306a36Sopenharmony_ci return error; 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ci /* 133862306a36Sopenharmony_ci * If disk quotas is on, we make sure that the dquots do exist on disk, 133962306a36Sopenharmony_ci * before we start any other transactions. Trying to do this later 134062306a36Sopenharmony_ci * is messy. We don't care to take a readlock to look at the ids 134162306a36Sopenharmony_ci * in inode here, because we can't hold it across the trans_reserve. 134262306a36Sopenharmony_ci * If the IDs do change before we take the ilock, we're covered 134362306a36Sopenharmony_ci * because the i_*dquot fields will get updated anyway. 134462306a36Sopenharmony_ci */ 134562306a36Sopenharmony_ci if (fa->fsx_valid && XFS_IS_QUOTA_ON(mp)) { 134662306a36Sopenharmony_ci error = xfs_qm_vop_dqalloc(ip, VFS_I(ip)->i_uid, 134762306a36Sopenharmony_ci VFS_I(ip)->i_gid, fa->fsx_projid, 134862306a36Sopenharmony_ci XFS_QMOPT_PQUOTA, NULL, NULL, &pdqp); 134962306a36Sopenharmony_ci if (error) 135062306a36Sopenharmony_ci return error; 135162306a36Sopenharmony_ci } 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci xfs_ioctl_setattr_prepare_dax(ip, fa); 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci tp = xfs_ioctl_setattr_get_trans(ip, pdqp); 135662306a36Sopenharmony_ci if (IS_ERR(tp)) { 135762306a36Sopenharmony_ci error = PTR_ERR(tp); 135862306a36Sopenharmony_ci goto error_free_dquots; 135962306a36Sopenharmony_ci } 136062306a36Sopenharmony_ci 136162306a36Sopenharmony_ci error = xfs_ioctl_setattr_check_extsize(ip, fa); 136262306a36Sopenharmony_ci if (error) 136362306a36Sopenharmony_ci goto error_trans_cancel; 136462306a36Sopenharmony_ci 136562306a36Sopenharmony_ci error = xfs_ioctl_setattr_check_cowextsize(ip, fa); 136662306a36Sopenharmony_ci if (error) 136762306a36Sopenharmony_ci goto error_trans_cancel; 136862306a36Sopenharmony_ci 136962306a36Sopenharmony_ci error = xfs_ioctl_setattr_xflags(tp, ip, fa); 137062306a36Sopenharmony_ci if (error) 137162306a36Sopenharmony_ci goto error_trans_cancel; 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_ci if (!fa->fsx_valid) 137462306a36Sopenharmony_ci goto skip_xattr; 137562306a36Sopenharmony_ci /* 137662306a36Sopenharmony_ci * Change file ownership. Must be the owner or privileged. CAP_FSETID 137762306a36Sopenharmony_ci * overrides the following restrictions: 137862306a36Sopenharmony_ci * 137962306a36Sopenharmony_ci * The set-user-ID and set-group-ID bits of a file will be cleared upon 138062306a36Sopenharmony_ci * successful return from chown() 138162306a36Sopenharmony_ci */ 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_ci if ((VFS_I(ip)->i_mode & (S_ISUID|S_ISGID)) && 138462306a36Sopenharmony_ci !capable_wrt_inode_uidgid(idmap, VFS_I(ip), CAP_FSETID)) 138562306a36Sopenharmony_ci VFS_I(ip)->i_mode &= ~(S_ISUID|S_ISGID); 138662306a36Sopenharmony_ci 138762306a36Sopenharmony_ci /* Change the ownerships and register project quota modifications */ 138862306a36Sopenharmony_ci if (ip->i_projid != fa->fsx_projid) { 138962306a36Sopenharmony_ci if (XFS_IS_PQUOTA_ON(mp)) { 139062306a36Sopenharmony_ci olddquot = xfs_qm_vop_chown(tp, ip, 139162306a36Sopenharmony_ci &ip->i_pdquot, pdqp); 139262306a36Sopenharmony_ci } 139362306a36Sopenharmony_ci ip->i_projid = fa->fsx_projid; 139462306a36Sopenharmony_ci } 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci /* 139762306a36Sopenharmony_ci * Only set the extent size hint if we've already determined that the 139862306a36Sopenharmony_ci * extent size hint should be set on the inode. If no extent size flags 139962306a36Sopenharmony_ci * are set on the inode then unconditionally clear the extent size hint. 140062306a36Sopenharmony_ci */ 140162306a36Sopenharmony_ci if (ip->i_diflags & (XFS_DIFLAG_EXTSIZE | XFS_DIFLAG_EXTSZINHERIT)) 140262306a36Sopenharmony_ci ip->i_extsize = XFS_B_TO_FSB(mp, fa->fsx_extsize); 140362306a36Sopenharmony_ci else 140462306a36Sopenharmony_ci ip->i_extsize = 0; 140562306a36Sopenharmony_ci 140662306a36Sopenharmony_ci if (xfs_has_v3inodes(mp)) { 140762306a36Sopenharmony_ci if (ip->i_diflags2 & XFS_DIFLAG2_COWEXTSIZE) 140862306a36Sopenharmony_ci ip->i_cowextsize = XFS_B_TO_FSB(mp, fa->fsx_cowextsize); 140962306a36Sopenharmony_ci else 141062306a36Sopenharmony_ci ip->i_cowextsize = 0; 141162306a36Sopenharmony_ci } 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_ciskip_xattr: 141462306a36Sopenharmony_ci error = xfs_trans_commit(tp); 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_ci /* 141762306a36Sopenharmony_ci * Release any dquot(s) the inode had kept before chown. 141862306a36Sopenharmony_ci */ 141962306a36Sopenharmony_ci xfs_qm_dqrele(olddquot); 142062306a36Sopenharmony_ci xfs_qm_dqrele(pdqp); 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_ci return error; 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_cierror_trans_cancel: 142562306a36Sopenharmony_ci xfs_trans_cancel(tp); 142662306a36Sopenharmony_cierror_free_dquots: 142762306a36Sopenharmony_ci xfs_qm_dqrele(pdqp); 142862306a36Sopenharmony_ci return error; 142962306a36Sopenharmony_ci} 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_cistatic bool 143262306a36Sopenharmony_cixfs_getbmap_format( 143362306a36Sopenharmony_ci struct kgetbmap *p, 143462306a36Sopenharmony_ci struct getbmapx __user *u, 143562306a36Sopenharmony_ci size_t recsize) 143662306a36Sopenharmony_ci{ 143762306a36Sopenharmony_ci if (put_user(p->bmv_offset, &u->bmv_offset) || 143862306a36Sopenharmony_ci put_user(p->bmv_block, &u->bmv_block) || 143962306a36Sopenharmony_ci put_user(p->bmv_length, &u->bmv_length) || 144062306a36Sopenharmony_ci put_user(0, &u->bmv_count) || 144162306a36Sopenharmony_ci put_user(0, &u->bmv_entries)) 144262306a36Sopenharmony_ci return false; 144362306a36Sopenharmony_ci if (recsize < sizeof(struct getbmapx)) 144462306a36Sopenharmony_ci return true; 144562306a36Sopenharmony_ci if (put_user(0, &u->bmv_iflags) || 144662306a36Sopenharmony_ci put_user(p->bmv_oflags, &u->bmv_oflags) || 144762306a36Sopenharmony_ci put_user(0, &u->bmv_unused1) || 144862306a36Sopenharmony_ci put_user(0, &u->bmv_unused2)) 144962306a36Sopenharmony_ci return false; 145062306a36Sopenharmony_ci return true; 145162306a36Sopenharmony_ci} 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_ciSTATIC int 145462306a36Sopenharmony_cixfs_ioc_getbmap( 145562306a36Sopenharmony_ci struct file *file, 145662306a36Sopenharmony_ci unsigned int cmd, 145762306a36Sopenharmony_ci void __user *arg) 145862306a36Sopenharmony_ci{ 145962306a36Sopenharmony_ci struct getbmapx bmx = { 0 }; 146062306a36Sopenharmony_ci struct kgetbmap *buf; 146162306a36Sopenharmony_ci size_t recsize; 146262306a36Sopenharmony_ci int error, i; 146362306a36Sopenharmony_ci 146462306a36Sopenharmony_ci switch (cmd) { 146562306a36Sopenharmony_ci case XFS_IOC_GETBMAPA: 146662306a36Sopenharmony_ci bmx.bmv_iflags = BMV_IF_ATTRFORK; 146762306a36Sopenharmony_ci fallthrough; 146862306a36Sopenharmony_ci case XFS_IOC_GETBMAP: 146962306a36Sopenharmony_ci /* struct getbmap is a strict subset of struct getbmapx. */ 147062306a36Sopenharmony_ci recsize = sizeof(struct getbmap); 147162306a36Sopenharmony_ci break; 147262306a36Sopenharmony_ci case XFS_IOC_GETBMAPX: 147362306a36Sopenharmony_ci recsize = sizeof(struct getbmapx); 147462306a36Sopenharmony_ci break; 147562306a36Sopenharmony_ci default: 147662306a36Sopenharmony_ci return -EINVAL; 147762306a36Sopenharmony_ci } 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_ci if (copy_from_user(&bmx, arg, recsize)) 148062306a36Sopenharmony_ci return -EFAULT; 148162306a36Sopenharmony_ci 148262306a36Sopenharmony_ci if (bmx.bmv_count < 2) 148362306a36Sopenharmony_ci return -EINVAL; 148462306a36Sopenharmony_ci if (bmx.bmv_count >= INT_MAX / recsize) 148562306a36Sopenharmony_ci return -ENOMEM; 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ci buf = kvcalloc(bmx.bmv_count, sizeof(*buf), GFP_KERNEL); 148862306a36Sopenharmony_ci if (!buf) 148962306a36Sopenharmony_ci return -ENOMEM; 149062306a36Sopenharmony_ci 149162306a36Sopenharmony_ci error = xfs_getbmap(XFS_I(file_inode(file)), &bmx, buf); 149262306a36Sopenharmony_ci if (error) 149362306a36Sopenharmony_ci goto out_free_buf; 149462306a36Sopenharmony_ci 149562306a36Sopenharmony_ci error = -EFAULT; 149662306a36Sopenharmony_ci if (copy_to_user(arg, &bmx, recsize)) 149762306a36Sopenharmony_ci goto out_free_buf; 149862306a36Sopenharmony_ci arg += recsize; 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_ci for (i = 0; i < bmx.bmv_entries; i++) { 150162306a36Sopenharmony_ci if (!xfs_getbmap_format(buf + i, arg, recsize)) 150262306a36Sopenharmony_ci goto out_free_buf; 150362306a36Sopenharmony_ci arg += recsize; 150462306a36Sopenharmony_ci } 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_ci error = 0; 150762306a36Sopenharmony_ciout_free_buf: 150862306a36Sopenharmony_ci kmem_free(buf); 150962306a36Sopenharmony_ci return error; 151062306a36Sopenharmony_ci} 151162306a36Sopenharmony_ci 151262306a36Sopenharmony_ciSTATIC int 151362306a36Sopenharmony_cixfs_ioc_getfsmap( 151462306a36Sopenharmony_ci struct xfs_inode *ip, 151562306a36Sopenharmony_ci struct fsmap_head __user *arg) 151662306a36Sopenharmony_ci{ 151762306a36Sopenharmony_ci struct xfs_fsmap_head xhead = {0}; 151862306a36Sopenharmony_ci struct fsmap_head head; 151962306a36Sopenharmony_ci struct fsmap *recs; 152062306a36Sopenharmony_ci unsigned int count; 152162306a36Sopenharmony_ci __u32 last_flags = 0; 152262306a36Sopenharmony_ci bool done = false; 152362306a36Sopenharmony_ci int error; 152462306a36Sopenharmony_ci 152562306a36Sopenharmony_ci if (copy_from_user(&head, arg, sizeof(struct fsmap_head))) 152662306a36Sopenharmony_ci return -EFAULT; 152762306a36Sopenharmony_ci if (memchr_inv(head.fmh_reserved, 0, sizeof(head.fmh_reserved)) || 152862306a36Sopenharmony_ci memchr_inv(head.fmh_keys[0].fmr_reserved, 0, 152962306a36Sopenharmony_ci sizeof(head.fmh_keys[0].fmr_reserved)) || 153062306a36Sopenharmony_ci memchr_inv(head.fmh_keys[1].fmr_reserved, 0, 153162306a36Sopenharmony_ci sizeof(head.fmh_keys[1].fmr_reserved))) 153262306a36Sopenharmony_ci return -EINVAL; 153362306a36Sopenharmony_ci 153462306a36Sopenharmony_ci /* 153562306a36Sopenharmony_ci * Use an internal memory buffer so that we don't have to copy fsmap 153662306a36Sopenharmony_ci * data to userspace while holding locks. Start by trying to allocate 153762306a36Sopenharmony_ci * up to 128k for the buffer, but fall back to a single page if needed. 153862306a36Sopenharmony_ci */ 153962306a36Sopenharmony_ci count = min_t(unsigned int, head.fmh_count, 154062306a36Sopenharmony_ci 131072 / sizeof(struct fsmap)); 154162306a36Sopenharmony_ci recs = kvcalloc(count, sizeof(struct fsmap), GFP_KERNEL); 154262306a36Sopenharmony_ci if (!recs) { 154362306a36Sopenharmony_ci count = min_t(unsigned int, head.fmh_count, 154462306a36Sopenharmony_ci PAGE_SIZE / sizeof(struct fsmap)); 154562306a36Sopenharmony_ci recs = kvcalloc(count, sizeof(struct fsmap), GFP_KERNEL); 154662306a36Sopenharmony_ci if (!recs) 154762306a36Sopenharmony_ci return -ENOMEM; 154862306a36Sopenharmony_ci } 154962306a36Sopenharmony_ci 155062306a36Sopenharmony_ci xhead.fmh_iflags = head.fmh_iflags; 155162306a36Sopenharmony_ci xfs_fsmap_to_internal(&xhead.fmh_keys[0], &head.fmh_keys[0]); 155262306a36Sopenharmony_ci xfs_fsmap_to_internal(&xhead.fmh_keys[1], &head.fmh_keys[1]); 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_ci trace_xfs_getfsmap_low_key(ip->i_mount, &xhead.fmh_keys[0]); 155562306a36Sopenharmony_ci trace_xfs_getfsmap_high_key(ip->i_mount, &xhead.fmh_keys[1]); 155662306a36Sopenharmony_ci 155762306a36Sopenharmony_ci head.fmh_entries = 0; 155862306a36Sopenharmony_ci do { 155962306a36Sopenharmony_ci struct fsmap __user *user_recs; 156062306a36Sopenharmony_ci struct fsmap *last_rec; 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_ci user_recs = &arg->fmh_recs[head.fmh_entries]; 156362306a36Sopenharmony_ci xhead.fmh_entries = 0; 156462306a36Sopenharmony_ci xhead.fmh_count = min_t(unsigned int, count, 156562306a36Sopenharmony_ci head.fmh_count - head.fmh_entries); 156662306a36Sopenharmony_ci 156762306a36Sopenharmony_ci /* Run query, record how many entries we got. */ 156862306a36Sopenharmony_ci error = xfs_getfsmap(ip->i_mount, &xhead, recs); 156962306a36Sopenharmony_ci switch (error) { 157062306a36Sopenharmony_ci case 0: 157162306a36Sopenharmony_ci /* 157262306a36Sopenharmony_ci * There are no more records in the result set. Copy 157362306a36Sopenharmony_ci * whatever we got to userspace and break out. 157462306a36Sopenharmony_ci */ 157562306a36Sopenharmony_ci done = true; 157662306a36Sopenharmony_ci break; 157762306a36Sopenharmony_ci case -ECANCELED: 157862306a36Sopenharmony_ci /* 157962306a36Sopenharmony_ci * The internal memory buffer is full. Copy whatever 158062306a36Sopenharmony_ci * records we got to userspace and go again if we have 158162306a36Sopenharmony_ci * not yet filled the userspace buffer. 158262306a36Sopenharmony_ci */ 158362306a36Sopenharmony_ci error = 0; 158462306a36Sopenharmony_ci break; 158562306a36Sopenharmony_ci default: 158662306a36Sopenharmony_ci goto out_free; 158762306a36Sopenharmony_ci } 158862306a36Sopenharmony_ci head.fmh_entries += xhead.fmh_entries; 158962306a36Sopenharmony_ci head.fmh_oflags = xhead.fmh_oflags; 159062306a36Sopenharmony_ci 159162306a36Sopenharmony_ci /* 159262306a36Sopenharmony_ci * If the caller wanted a record count or there aren't any 159362306a36Sopenharmony_ci * new records to return, we're done. 159462306a36Sopenharmony_ci */ 159562306a36Sopenharmony_ci if (head.fmh_count == 0 || xhead.fmh_entries == 0) 159662306a36Sopenharmony_ci break; 159762306a36Sopenharmony_ci 159862306a36Sopenharmony_ci /* Copy all the records we got out to userspace. */ 159962306a36Sopenharmony_ci if (copy_to_user(user_recs, recs, 160062306a36Sopenharmony_ci xhead.fmh_entries * sizeof(struct fsmap))) { 160162306a36Sopenharmony_ci error = -EFAULT; 160262306a36Sopenharmony_ci goto out_free; 160362306a36Sopenharmony_ci } 160462306a36Sopenharmony_ci 160562306a36Sopenharmony_ci /* Remember the last record flags we copied to userspace. */ 160662306a36Sopenharmony_ci last_rec = &recs[xhead.fmh_entries - 1]; 160762306a36Sopenharmony_ci last_flags = last_rec->fmr_flags; 160862306a36Sopenharmony_ci 160962306a36Sopenharmony_ci /* Set up the low key for the next iteration. */ 161062306a36Sopenharmony_ci xfs_fsmap_to_internal(&xhead.fmh_keys[0], last_rec); 161162306a36Sopenharmony_ci trace_xfs_getfsmap_low_key(ip->i_mount, &xhead.fmh_keys[0]); 161262306a36Sopenharmony_ci } while (!done && head.fmh_entries < head.fmh_count); 161362306a36Sopenharmony_ci 161462306a36Sopenharmony_ci /* 161562306a36Sopenharmony_ci * If there are no more records in the query result set and we're not 161662306a36Sopenharmony_ci * in counting mode, mark the last record returned with the LAST flag. 161762306a36Sopenharmony_ci */ 161862306a36Sopenharmony_ci if (done && head.fmh_count > 0 && head.fmh_entries > 0) { 161962306a36Sopenharmony_ci struct fsmap __user *user_rec; 162062306a36Sopenharmony_ci 162162306a36Sopenharmony_ci last_flags |= FMR_OF_LAST; 162262306a36Sopenharmony_ci user_rec = &arg->fmh_recs[head.fmh_entries - 1]; 162362306a36Sopenharmony_ci 162462306a36Sopenharmony_ci if (copy_to_user(&user_rec->fmr_flags, &last_flags, 162562306a36Sopenharmony_ci sizeof(last_flags))) { 162662306a36Sopenharmony_ci error = -EFAULT; 162762306a36Sopenharmony_ci goto out_free; 162862306a36Sopenharmony_ci } 162962306a36Sopenharmony_ci } 163062306a36Sopenharmony_ci 163162306a36Sopenharmony_ci /* copy back header */ 163262306a36Sopenharmony_ci if (copy_to_user(arg, &head, sizeof(struct fsmap_head))) { 163362306a36Sopenharmony_ci error = -EFAULT; 163462306a36Sopenharmony_ci goto out_free; 163562306a36Sopenharmony_ci } 163662306a36Sopenharmony_ci 163762306a36Sopenharmony_ciout_free: 163862306a36Sopenharmony_ci kmem_free(recs); 163962306a36Sopenharmony_ci return error; 164062306a36Sopenharmony_ci} 164162306a36Sopenharmony_ci 164262306a36Sopenharmony_ciSTATIC int 164362306a36Sopenharmony_cixfs_ioc_scrub_metadata( 164462306a36Sopenharmony_ci struct file *file, 164562306a36Sopenharmony_ci void __user *arg) 164662306a36Sopenharmony_ci{ 164762306a36Sopenharmony_ci struct xfs_scrub_metadata scrub; 164862306a36Sopenharmony_ci int error; 164962306a36Sopenharmony_ci 165062306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 165162306a36Sopenharmony_ci return -EPERM; 165262306a36Sopenharmony_ci 165362306a36Sopenharmony_ci if (copy_from_user(&scrub, arg, sizeof(scrub))) 165462306a36Sopenharmony_ci return -EFAULT; 165562306a36Sopenharmony_ci 165662306a36Sopenharmony_ci error = xfs_scrub_metadata(file, &scrub); 165762306a36Sopenharmony_ci if (error) 165862306a36Sopenharmony_ci return error; 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_ci if (copy_to_user(arg, &scrub, sizeof(scrub))) 166162306a36Sopenharmony_ci return -EFAULT; 166262306a36Sopenharmony_ci 166362306a36Sopenharmony_ci return 0; 166462306a36Sopenharmony_ci} 166562306a36Sopenharmony_ci 166662306a36Sopenharmony_ciint 166762306a36Sopenharmony_cixfs_ioc_swapext( 166862306a36Sopenharmony_ci xfs_swapext_t *sxp) 166962306a36Sopenharmony_ci{ 167062306a36Sopenharmony_ci xfs_inode_t *ip, *tip; 167162306a36Sopenharmony_ci struct fd f, tmp; 167262306a36Sopenharmony_ci int error = 0; 167362306a36Sopenharmony_ci 167462306a36Sopenharmony_ci /* Pull information for the target fd */ 167562306a36Sopenharmony_ci f = fdget((int)sxp->sx_fdtarget); 167662306a36Sopenharmony_ci if (!f.file) { 167762306a36Sopenharmony_ci error = -EINVAL; 167862306a36Sopenharmony_ci goto out; 167962306a36Sopenharmony_ci } 168062306a36Sopenharmony_ci 168162306a36Sopenharmony_ci if (!(f.file->f_mode & FMODE_WRITE) || 168262306a36Sopenharmony_ci !(f.file->f_mode & FMODE_READ) || 168362306a36Sopenharmony_ci (f.file->f_flags & O_APPEND)) { 168462306a36Sopenharmony_ci error = -EBADF; 168562306a36Sopenharmony_ci goto out_put_file; 168662306a36Sopenharmony_ci } 168762306a36Sopenharmony_ci 168862306a36Sopenharmony_ci tmp = fdget((int)sxp->sx_fdtmp); 168962306a36Sopenharmony_ci if (!tmp.file) { 169062306a36Sopenharmony_ci error = -EINVAL; 169162306a36Sopenharmony_ci goto out_put_file; 169262306a36Sopenharmony_ci } 169362306a36Sopenharmony_ci 169462306a36Sopenharmony_ci if (!(tmp.file->f_mode & FMODE_WRITE) || 169562306a36Sopenharmony_ci !(tmp.file->f_mode & FMODE_READ) || 169662306a36Sopenharmony_ci (tmp.file->f_flags & O_APPEND)) { 169762306a36Sopenharmony_ci error = -EBADF; 169862306a36Sopenharmony_ci goto out_put_tmp_file; 169962306a36Sopenharmony_ci } 170062306a36Sopenharmony_ci 170162306a36Sopenharmony_ci if (IS_SWAPFILE(file_inode(f.file)) || 170262306a36Sopenharmony_ci IS_SWAPFILE(file_inode(tmp.file))) { 170362306a36Sopenharmony_ci error = -EINVAL; 170462306a36Sopenharmony_ci goto out_put_tmp_file; 170562306a36Sopenharmony_ci } 170662306a36Sopenharmony_ci 170762306a36Sopenharmony_ci /* 170862306a36Sopenharmony_ci * We need to ensure that the fds passed in point to XFS inodes 170962306a36Sopenharmony_ci * before we cast and access them as XFS structures as we have no 171062306a36Sopenharmony_ci * control over what the user passes us here. 171162306a36Sopenharmony_ci */ 171262306a36Sopenharmony_ci if (f.file->f_op != &xfs_file_operations || 171362306a36Sopenharmony_ci tmp.file->f_op != &xfs_file_operations) { 171462306a36Sopenharmony_ci error = -EINVAL; 171562306a36Sopenharmony_ci goto out_put_tmp_file; 171662306a36Sopenharmony_ci } 171762306a36Sopenharmony_ci 171862306a36Sopenharmony_ci ip = XFS_I(file_inode(f.file)); 171962306a36Sopenharmony_ci tip = XFS_I(file_inode(tmp.file)); 172062306a36Sopenharmony_ci 172162306a36Sopenharmony_ci if (ip->i_mount != tip->i_mount) { 172262306a36Sopenharmony_ci error = -EINVAL; 172362306a36Sopenharmony_ci goto out_put_tmp_file; 172462306a36Sopenharmony_ci } 172562306a36Sopenharmony_ci 172662306a36Sopenharmony_ci if (ip->i_ino == tip->i_ino) { 172762306a36Sopenharmony_ci error = -EINVAL; 172862306a36Sopenharmony_ci goto out_put_tmp_file; 172962306a36Sopenharmony_ci } 173062306a36Sopenharmony_ci 173162306a36Sopenharmony_ci if (xfs_is_shutdown(ip->i_mount)) { 173262306a36Sopenharmony_ci error = -EIO; 173362306a36Sopenharmony_ci goto out_put_tmp_file; 173462306a36Sopenharmony_ci } 173562306a36Sopenharmony_ci 173662306a36Sopenharmony_ci error = xfs_swap_extents(ip, tip, sxp); 173762306a36Sopenharmony_ci 173862306a36Sopenharmony_ci out_put_tmp_file: 173962306a36Sopenharmony_ci fdput(tmp); 174062306a36Sopenharmony_ci out_put_file: 174162306a36Sopenharmony_ci fdput(f); 174262306a36Sopenharmony_ci out: 174362306a36Sopenharmony_ci return error; 174462306a36Sopenharmony_ci} 174562306a36Sopenharmony_ci 174662306a36Sopenharmony_cistatic int 174762306a36Sopenharmony_cixfs_ioc_getlabel( 174862306a36Sopenharmony_ci struct xfs_mount *mp, 174962306a36Sopenharmony_ci char __user *user_label) 175062306a36Sopenharmony_ci{ 175162306a36Sopenharmony_ci struct xfs_sb *sbp = &mp->m_sb; 175262306a36Sopenharmony_ci char label[XFSLABEL_MAX + 1]; 175362306a36Sopenharmony_ci 175462306a36Sopenharmony_ci /* Paranoia */ 175562306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(sbp->sb_fname) > FSLABEL_MAX); 175662306a36Sopenharmony_ci 175762306a36Sopenharmony_ci /* 1 larger than sb_fname, so this ensures a trailing NUL char */ 175862306a36Sopenharmony_ci memset(label, 0, sizeof(label)); 175962306a36Sopenharmony_ci spin_lock(&mp->m_sb_lock); 176062306a36Sopenharmony_ci strncpy(label, sbp->sb_fname, XFSLABEL_MAX); 176162306a36Sopenharmony_ci spin_unlock(&mp->m_sb_lock); 176262306a36Sopenharmony_ci 176362306a36Sopenharmony_ci if (copy_to_user(user_label, label, sizeof(label))) 176462306a36Sopenharmony_ci return -EFAULT; 176562306a36Sopenharmony_ci return 0; 176662306a36Sopenharmony_ci} 176762306a36Sopenharmony_ci 176862306a36Sopenharmony_cistatic int 176962306a36Sopenharmony_cixfs_ioc_setlabel( 177062306a36Sopenharmony_ci struct file *filp, 177162306a36Sopenharmony_ci struct xfs_mount *mp, 177262306a36Sopenharmony_ci char __user *newlabel) 177362306a36Sopenharmony_ci{ 177462306a36Sopenharmony_ci struct xfs_sb *sbp = &mp->m_sb; 177562306a36Sopenharmony_ci char label[XFSLABEL_MAX + 1]; 177662306a36Sopenharmony_ci size_t len; 177762306a36Sopenharmony_ci int error; 177862306a36Sopenharmony_ci 177962306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 178062306a36Sopenharmony_ci return -EPERM; 178162306a36Sopenharmony_ci /* 178262306a36Sopenharmony_ci * The generic ioctl allows up to FSLABEL_MAX chars, but XFS is much 178362306a36Sopenharmony_ci * smaller, at 12 bytes. We copy one more to be sure we find the 178462306a36Sopenharmony_ci * (required) NULL character to test the incoming label length. 178562306a36Sopenharmony_ci * NB: The on disk label doesn't need to be null terminated. 178662306a36Sopenharmony_ci */ 178762306a36Sopenharmony_ci if (copy_from_user(label, newlabel, XFSLABEL_MAX + 1)) 178862306a36Sopenharmony_ci return -EFAULT; 178962306a36Sopenharmony_ci len = strnlen(label, XFSLABEL_MAX + 1); 179062306a36Sopenharmony_ci if (len > sizeof(sbp->sb_fname)) 179162306a36Sopenharmony_ci return -EINVAL; 179262306a36Sopenharmony_ci 179362306a36Sopenharmony_ci error = mnt_want_write_file(filp); 179462306a36Sopenharmony_ci if (error) 179562306a36Sopenharmony_ci return error; 179662306a36Sopenharmony_ci 179762306a36Sopenharmony_ci spin_lock(&mp->m_sb_lock); 179862306a36Sopenharmony_ci memset(sbp->sb_fname, 0, sizeof(sbp->sb_fname)); 179962306a36Sopenharmony_ci memcpy(sbp->sb_fname, label, len); 180062306a36Sopenharmony_ci spin_unlock(&mp->m_sb_lock); 180162306a36Sopenharmony_ci 180262306a36Sopenharmony_ci /* 180362306a36Sopenharmony_ci * Now we do several things to satisfy userspace. 180462306a36Sopenharmony_ci * In addition to normal logging of the primary superblock, we also 180562306a36Sopenharmony_ci * immediately write these changes to sector zero for the primary, then 180662306a36Sopenharmony_ci * update all backup supers (as xfs_db does for a label change), then 180762306a36Sopenharmony_ci * invalidate the block device page cache. This is so that any prior 180862306a36Sopenharmony_ci * buffered reads from userspace (i.e. from blkid) are invalidated, 180962306a36Sopenharmony_ci * and userspace will see the newly-written label. 181062306a36Sopenharmony_ci */ 181162306a36Sopenharmony_ci error = xfs_sync_sb_buf(mp); 181262306a36Sopenharmony_ci if (error) 181362306a36Sopenharmony_ci goto out; 181462306a36Sopenharmony_ci /* 181562306a36Sopenharmony_ci * growfs also updates backup supers so lock against that. 181662306a36Sopenharmony_ci */ 181762306a36Sopenharmony_ci mutex_lock(&mp->m_growlock); 181862306a36Sopenharmony_ci error = xfs_update_secondary_sbs(mp); 181962306a36Sopenharmony_ci mutex_unlock(&mp->m_growlock); 182062306a36Sopenharmony_ci 182162306a36Sopenharmony_ci invalidate_bdev(mp->m_ddev_targp->bt_bdev); 182262306a36Sopenharmony_ci 182362306a36Sopenharmony_ciout: 182462306a36Sopenharmony_ci mnt_drop_write_file(filp); 182562306a36Sopenharmony_ci return error; 182662306a36Sopenharmony_ci} 182762306a36Sopenharmony_ci 182862306a36Sopenharmony_cistatic inline int 182962306a36Sopenharmony_cixfs_fs_eofblocks_from_user( 183062306a36Sopenharmony_ci struct xfs_fs_eofblocks *src, 183162306a36Sopenharmony_ci struct xfs_icwalk *dst) 183262306a36Sopenharmony_ci{ 183362306a36Sopenharmony_ci if (src->eof_version != XFS_EOFBLOCKS_VERSION) 183462306a36Sopenharmony_ci return -EINVAL; 183562306a36Sopenharmony_ci 183662306a36Sopenharmony_ci if (src->eof_flags & ~XFS_EOF_FLAGS_VALID) 183762306a36Sopenharmony_ci return -EINVAL; 183862306a36Sopenharmony_ci 183962306a36Sopenharmony_ci if (memchr_inv(&src->pad32, 0, sizeof(src->pad32)) || 184062306a36Sopenharmony_ci memchr_inv(src->pad64, 0, sizeof(src->pad64))) 184162306a36Sopenharmony_ci return -EINVAL; 184262306a36Sopenharmony_ci 184362306a36Sopenharmony_ci dst->icw_flags = 0; 184462306a36Sopenharmony_ci if (src->eof_flags & XFS_EOF_FLAGS_SYNC) 184562306a36Sopenharmony_ci dst->icw_flags |= XFS_ICWALK_FLAG_SYNC; 184662306a36Sopenharmony_ci if (src->eof_flags & XFS_EOF_FLAGS_UID) 184762306a36Sopenharmony_ci dst->icw_flags |= XFS_ICWALK_FLAG_UID; 184862306a36Sopenharmony_ci if (src->eof_flags & XFS_EOF_FLAGS_GID) 184962306a36Sopenharmony_ci dst->icw_flags |= XFS_ICWALK_FLAG_GID; 185062306a36Sopenharmony_ci if (src->eof_flags & XFS_EOF_FLAGS_PRID) 185162306a36Sopenharmony_ci dst->icw_flags |= XFS_ICWALK_FLAG_PRID; 185262306a36Sopenharmony_ci if (src->eof_flags & XFS_EOF_FLAGS_MINFILESIZE) 185362306a36Sopenharmony_ci dst->icw_flags |= XFS_ICWALK_FLAG_MINFILESIZE; 185462306a36Sopenharmony_ci 185562306a36Sopenharmony_ci dst->icw_prid = src->eof_prid; 185662306a36Sopenharmony_ci dst->icw_min_file_size = src->eof_min_file_size; 185762306a36Sopenharmony_ci 185862306a36Sopenharmony_ci dst->icw_uid = INVALID_UID; 185962306a36Sopenharmony_ci if (src->eof_flags & XFS_EOF_FLAGS_UID) { 186062306a36Sopenharmony_ci dst->icw_uid = make_kuid(current_user_ns(), src->eof_uid); 186162306a36Sopenharmony_ci if (!uid_valid(dst->icw_uid)) 186262306a36Sopenharmony_ci return -EINVAL; 186362306a36Sopenharmony_ci } 186462306a36Sopenharmony_ci 186562306a36Sopenharmony_ci dst->icw_gid = INVALID_GID; 186662306a36Sopenharmony_ci if (src->eof_flags & XFS_EOF_FLAGS_GID) { 186762306a36Sopenharmony_ci dst->icw_gid = make_kgid(current_user_ns(), src->eof_gid); 186862306a36Sopenharmony_ci if (!gid_valid(dst->icw_gid)) 186962306a36Sopenharmony_ci return -EINVAL; 187062306a36Sopenharmony_ci } 187162306a36Sopenharmony_ci return 0; 187262306a36Sopenharmony_ci} 187362306a36Sopenharmony_ci 187462306a36Sopenharmony_ci/* 187562306a36Sopenharmony_ci * These long-unused ioctls were removed from the official ioctl API in 5.17, 187662306a36Sopenharmony_ci * but retain these definitions so that we can log warnings about them. 187762306a36Sopenharmony_ci */ 187862306a36Sopenharmony_ci#define XFS_IOC_ALLOCSP _IOW ('X', 10, struct xfs_flock64) 187962306a36Sopenharmony_ci#define XFS_IOC_FREESP _IOW ('X', 11, struct xfs_flock64) 188062306a36Sopenharmony_ci#define XFS_IOC_ALLOCSP64 _IOW ('X', 36, struct xfs_flock64) 188162306a36Sopenharmony_ci#define XFS_IOC_FREESP64 _IOW ('X', 37, struct xfs_flock64) 188262306a36Sopenharmony_ci 188362306a36Sopenharmony_ci/* 188462306a36Sopenharmony_ci * Note: some of the ioctl's return positive numbers as a 188562306a36Sopenharmony_ci * byte count indicating success, such as readlink_by_handle. 188662306a36Sopenharmony_ci * So we don't "sign flip" like most other routines. This means 188762306a36Sopenharmony_ci * true errors need to be returned as a negative value. 188862306a36Sopenharmony_ci */ 188962306a36Sopenharmony_cilong 189062306a36Sopenharmony_cixfs_file_ioctl( 189162306a36Sopenharmony_ci struct file *filp, 189262306a36Sopenharmony_ci unsigned int cmd, 189362306a36Sopenharmony_ci unsigned long p) 189462306a36Sopenharmony_ci{ 189562306a36Sopenharmony_ci struct inode *inode = file_inode(filp); 189662306a36Sopenharmony_ci struct xfs_inode *ip = XFS_I(inode); 189762306a36Sopenharmony_ci struct xfs_mount *mp = ip->i_mount; 189862306a36Sopenharmony_ci void __user *arg = (void __user *)p; 189962306a36Sopenharmony_ci int error; 190062306a36Sopenharmony_ci 190162306a36Sopenharmony_ci trace_xfs_file_ioctl(ip); 190262306a36Sopenharmony_ci 190362306a36Sopenharmony_ci switch (cmd) { 190462306a36Sopenharmony_ci case FITRIM: 190562306a36Sopenharmony_ci return xfs_ioc_trim(mp, arg); 190662306a36Sopenharmony_ci case FS_IOC_GETFSLABEL: 190762306a36Sopenharmony_ci return xfs_ioc_getlabel(mp, arg); 190862306a36Sopenharmony_ci case FS_IOC_SETFSLABEL: 190962306a36Sopenharmony_ci return xfs_ioc_setlabel(filp, mp, arg); 191062306a36Sopenharmony_ci case XFS_IOC_ALLOCSP: 191162306a36Sopenharmony_ci case XFS_IOC_FREESP: 191262306a36Sopenharmony_ci case XFS_IOC_ALLOCSP64: 191362306a36Sopenharmony_ci case XFS_IOC_FREESP64: 191462306a36Sopenharmony_ci xfs_warn_once(mp, 191562306a36Sopenharmony_ci "%s should use fallocate; XFS_IOC_{ALLOC,FREE}SP ioctl unsupported", 191662306a36Sopenharmony_ci current->comm); 191762306a36Sopenharmony_ci return -ENOTTY; 191862306a36Sopenharmony_ci case XFS_IOC_DIOINFO: { 191962306a36Sopenharmony_ci struct xfs_buftarg *target = xfs_inode_buftarg(ip); 192062306a36Sopenharmony_ci struct dioattr da; 192162306a36Sopenharmony_ci 192262306a36Sopenharmony_ci da.d_mem = da.d_miniosz = target->bt_logical_sectorsize; 192362306a36Sopenharmony_ci da.d_maxiosz = INT_MAX & ~(da.d_miniosz - 1); 192462306a36Sopenharmony_ci 192562306a36Sopenharmony_ci if (copy_to_user(arg, &da, sizeof(da))) 192662306a36Sopenharmony_ci return -EFAULT; 192762306a36Sopenharmony_ci return 0; 192862306a36Sopenharmony_ci } 192962306a36Sopenharmony_ci 193062306a36Sopenharmony_ci case XFS_IOC_FSBULKSTAT_SINGLE: 193162306a36Sopenharmony_ci case XFS_IOC_FSBULKSTAT: 193262306a36Sopenharmony_ci case XFS_IOC_FSINUMBERS: 193362306a36Sopenharmony_ci return xfs_ioc_fsbulkstat(filp, cmd, arg); 193462306a36Sopenharmony_ci 193562306a36Sopenharmony_ci case XFS_IOC_BULKSTAT: 193662306a36Sopenharmony_ci return xfs_ioc_bulkstat(filp, cmd, arg); 193762306a36Sopenharmony_ci case XFS_IOC_INUMBERS: 193862306a36Sopenharmony_ci return xfs_ioc_inumbers(mp, cmd, arg); 193962306a36Sopenharmony_ci 194062306a36Sopenharmony_ci case XFS_IOC_FSGEOMETRY_V1: 194162306a36Sopenharmony_ci return xfs_ioc_fsgeometry(mp, arg, 3); 194262306a36Sopenharmony_ci case XFS_IOC_FSGEOMETRY_V4: 194362306a36Sopenharmony_ci return xfs_ioc_fsgeometry(mp, arg, 4); 194462306a36Sopenharmony_ci case XFS_IOC_FSGEOMETRY: 194562306a36Sopenharmony_ci return xfs_ioc_fsgeometry(mp, arg, 5); 194662306a36Sopenharmony_ci 194762306a36Sopenharmony_ci case XFS_IOC_AG_GEOMETRY: 194862306a36Sopenharmony_ci return xfs_ioc_ag_geometry(mp, arg); 194962306a36Sopenharmony_ci 195062306a36Sopenharmony_ci case XFS_IOC_GETVERSION: 195162306a36Sopenharmony_ci return put_user(inode->i_generation, (int __user *)arg); 195262306a36Sopenharmony_ci 195362306a36Sopenharmony_ci case XFS_IOC_FSGETXATTRA: 195462306a36Sopenharmony_ci return xfs_ioc_fsgetxattra(ip, arg); 195562306a36Sopenharmony_ci 195662306a36Sopenharmony_ci case XFS_IOC_GETBMAP: 195762306a36Sopenharmony_ci case XFS_IOC_GETBMAPA: 195862306a36Sopenharmony_ci case XFS_IOC_GETBMAPX: 195962306a36Sopenharmony_ci return xfs_ioc_getbmap(filp, cmd, arg); 196062306a36Sopenharmony_ci 196162306a36Sopenharmony_ci case FS_IOC_GETFSMAP: 196262306a36Sopenharmony_ci return xfs_ioc_getfsmap(ip, arg); 196362306a36Sopenharmony_ci 196462306a36Sopenharmony_ci case XFS_IOC_SCRUB_METADATA: 196562306a36Sopenharmony_ci return xfs_ioc_scrub_metadata(filp, arg); 196662306a36Sopenharmony_ci 196762306a36Sopenharmony_ci case XFS_IOC_FD_TO_HANDLE: 196862306a36Sopenharmony_ci case XFS_IOC_PATH_TO_HANDLE: 196962306a36Sopenharmony_ci case XFS_IOC_PATH_TO_FSHANDLE: { 197062306a36Sopenharmony_ci xfs_fsop_handlereq_t hreq; 197162306a36Sopenharmony_ci 197262306a36Sopenharmony_ci if (copy_from_user(&hreq, arg, sizeof(hreq))) 197362306a36Sopenharmony_ci return -EFAULT; 197462306a36Sopenharmony_ci return xfs_find_handle(cmd, &hreq); 197562306a36Sopenharmony_ci } 197662306a36Sopenharmony_ci case XFS_IOC_OPEN_BY_HANDLE: { 197762306a36Sopenharmony_ci xfs_fsop_handlereq_t hreq; 197862306a36Sopenharmony_ci 197962306a36Sopenharmony_ci if (copy_from_user(&hreq, arg, sizeof(xfs_fsop_handlereq_t))) 198062306a36Sopenharmony_ci return -EFAULT; 198162306a36Sopenharmony_ci return xfs_open_by_handle(filp, &hreq); 198262306a36Sopenharmony_ci } 198362306a36Sopenharmony_ci 198462306a36Sopenharmony_ci case XFS_IOC_READLINK_BY_HANDLE: { 198562306a36Sopenharmony_ci xfs_fsop_handlereq_t hreq; 198662306a36Sopenharmony_ci 198762306a36Sopenharmony_ci if (copy_from_user(&hreq, arg, sizeof(xfs_fsop_handlereq_t))) 198862306a36Sopenharmony_ci return -EFAULT; 198962306a36Sopenharmony_ci return xfs_readlink_by_handle(filp, &hreq); 199062306a36Sopenharmony_ci } 199162306a36Sopenharmony_ci case XFS_IOC_ATTRLIST_BY_HANDLE: 199262306a36Sopenharmony_ci return xfs_attrlist_by_handle(filp, arg); 199362306a36Sopenharmony_ci 199462306a36Sopenharmony_ci case XFS_IOC_ATTRMULTI_BY_HANDLE: 199562306a36Sopenharmony_ci return xfs_attrmulti_by_handle(filp, arg); 199662306a36Sopenharmony_ci 199762306a36Sopenharmony_ci case XFS_IOC_SWAPEXT: { 199862306a36Sopenharmony_ci struct xfs_swapext sxp; 199962306a36Sopenharmony_ci 200062306a36Sopenharmony_ci if (copy_from_user(&sxp, arg, sizeof(xfs_swapext_t))) 200162306a36Sopenharmony_ci return -EFAULT; 200262306a36Sopenharmony_ci error = mnt_want_write_file(filp); 200362306a36Sopenharmony_ci if (error) 200462306a36Sopenharmony_ci return error; 200562306a36Sopenharmony_ci error = xfs_ioc_swapext(&sxp); 200662306a36Sopenharmony_ci mnt_drop_write_file(filp); 200762306a36Sopenharmony_ci return error; 200862306a36Sopenharmony_ci } 200962306a36Sopenharmony_ci 201062306a36Sopenharmony_ci case XFS_IOC_FSCOUNTS: { 201162306a36Sopenharmony_ci xfs_fsop_counts_t out; 201262306a36Sopenharmony_ci 201362306a36Sopenharmony_ci xfs_fs_counts(mp, &out); 201462306a36Sopenharmony_ci 201562306a36Sopenharmony_ci if (copy_to_user(arg, &out, sizeof(out))) 201662306a36Sopenharmony_ci return -EFAULT; 201762306a36Sopenharmony_ci return 0; 201862306a36Sopenharmony_ci } 201962306a36Sopenharmony_ci 202062306a36Sopenharmony_ci case XFS_IOC_SET_RESBLKS: { 202162306a36Sopenharmony_ci xfs_fsop_resblks_t inout; 202262306a36Sopenharmony_ci uint64_t in; 202362306a36Sopenharmony_ci 202462306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 202562306a36Sopenharmony_ci return -EPERM; 202662306a36Sopenharmony_ci 202762306a36Sopenharmony_ci if (xfs_is_readonly(mp)) 202862306a36Sopenharmony_ci return -EROFS; 202962306a36Sopenharmony_ci 203062306a36Sopenharmony_ci if (copy_from_user(&inout, arg, sizeof(inout))) 203162306a36Sopenharmony_ci return -EFAULT; 203262306a36Sopenharmony_ci 203362306a36Sopenharmony_ci error = mnt_want_write_file(filp); 203462306a36Sopenharmony_ci if (error) 203562306a36Sopenharmony_ci return error; 203662306a36Sopenharmony_ci 203762306a36Sopenharmony_ci /* input parameter is passed in resblks field of structure */ 203862306a36Sopenharmony_ci in = inout.resblks; 203962306a36Sopenharmony_ci error = xfs_reserve_blocks(mp, &in, &inout); 204062306a36Sopenharmony_ci mnt_drop_write_file(filp); 204162306a36Sopenharmony_ci if (error) 204262306a36Sopenharmony_ci return error; 204362306a36Sopenharmony_ci 204462306a36Sopenharmony_ci if (copy_to_user(arg, &inout, sizeof(inout))) 204562306a36Sopenharmony_ci return -EFAULT; 204662306a36Sopenharmony_ci return 0; 204762306a36Sopenharmony_ci } 204862306a36Sopenharmony_ci 204962306a36Sopenharmony_ci case XFS_IOC_GET_RESBLKS: { 205062306a36Sopenharmony_ci xfs_fsop_resblks_t out; 205162306a36Sopenharmony_ci 205262306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 205362306a36Sopenharmony_ci return -EPERM; 205462306a36Sopenharmony_ci 205562306a36Sopenharmony_ci error = xfs_reserve_blocks(mp, NULL, &out); 205662306a36Sopenharmony_ci if (error) 205762306a36Sopenharmony_ci return error; 205862306a36Sopenharmony_ci 205962306a36Sopenharmony_ci if (copy_to_user(arg, &out, sizeof(out))) 206062306a36Sopenharmony_ci return -EFAULT; 206162306a36Sopenharmony_ci 206262306a36Sopenharmony_ci return 0; 206362306a36Sopenharmony_ci } 206462306a36Sopenharmony_ci 206562306a36Sopenharmony_ci case XFS_IOC_FSGROWFSDATA: { 206662306a36Sopenharmony_ci struct xfs_growfs_data in; 206762306a36Sopenharmony_ci 206862306a36Sopenharmony_ci if (copy_from_user(&in, arg, sizeof(in))) 206962306a36Sopenharmony_ci return -EFAULT; 207062306a36Sopenharmony_ci 207162306a36Sopenharmony_ci error = mnt_want_write_file(filp); 207262306a36Sopenharmony_ci if (error) 207362306a36Sopenharmony_ci return error; 207462306a36Sopenharmony_ci error = xfs_growfs_data(mp, &in); 207562306a36Sopenharmony_ci mnt_drop_write_file(filp); 207662306a36Sopenharmony_ci return error; 207762306a36Sopenharmony_ci } 207862306a36Sopenharmony_ci 207962306a36Sopenharmony_ci case XFS_IOC_FSGROWFSLOG: { 208062306a36Sopenharmony_ci struct xfs_growfs_log in; 208162306a36Sopenharmony_ci 208262306a36Sopenharmony_ci if (copy_from_user(&in, arg, sizeof(in))) 208362306a36Sopenharmony_ci return -EFAULT; 208462306a36Sopenharmony_ci 208562306a36Sopenharmony_ci error = mnt_want_write_file(filp); 208662306a36Sopenharmony_ci if (error) 208762306a36Sopenharmony_ci return error; 208862306a36Sopenharmony_ci error = xfs_growfs_log(mp, &in); 208962306a36Sopenharmony_ci mnt_drop_write_file(filp); 209062306a36Sopenharmony_ci return error; 209162306a36Sopenharmony_ci } 209262306a36Sopenharmony_ci 209362306a36Sopenharmony_ci case XFS_IOC_FSGROWFSRT: { 209462306a36Sopenharmony_ci xfs_growfs_rt_t in; 209562306a36Sopenharmony_ci 209662306a36Sopenharmony_ci if (copy_from_user(&in, arg, sizeof(in))) 209762306a36Sopenharmony_ci return -EFAULT; 209862306a36Sopenharmony_ci 209962306a36Sopenharmony_ci error = mnt_want_write_file(filp); 210062306a36Sopenharmony_ci if (error) 210162306a36Sopenharmony_ci return error; 210262306a36Sopenharmony_ci error = xfs_growfs_rt(mp, &in); 210362306a36Sopenharmony_ci mnt_drop_write_file(filp); 210462306a36Sopenharmony_ci return error; 210562306a36Sopenharmony_ci } 210662306a36Sopenharmony_ci 210762306a36Sopenharmony_ci case XFS_IOC_GOINGDOWN: { 210862306a36Sopenharmony_ci uint32_t in; 210962306a36Sopenharmony_ci 211062306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 211162306a36Sopenharmony_ci return -EPERM; 211262306a36Sopenharmony_ci 211362306a36Sopenharmony_ci if (get_user(in, (uint32_t __user *)arg)) 211462306a36Sopenharmony_ci return -EFAULT; 211562306a36Sopenharmony_ci 211662306a36Sopenharmony_ci return xfs_fs_goingdown(mp, in); 211762306a36Sopenharmony_ci } 211862306a36Sopenharmony_ci 211962306a36Sopenharmony_ci case XFS_IOC_ERROR_INJECTION: { 212062306a36Sopenharmony_ci xfs_error_injection_t in; 212162306a36Sopenharmony_ci 212262306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 212362306a36Sopenharmony_ci return -EPERM; 212462306a36Sopenharmony_ci 212562306a36Sopenharmony_ci if (copy_from_user(&in, arg, sizeof(in))) 212662306a36Sopenharmony_ci return -EFAULT; 212762306a36Sopenharmony_ci 212862306a36Sopenharmony_ci return xfs_errortag_add(mp, in.errtag); 212962306a36Sopenharmony_ci } 213062306a36Sopenharmony_ci 213162306a36Sopenharmony_ci case XFS_IOC_ERROR_CLEARALL: 213262306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 213362306a36Sopenharmony_ci return -EPERM; 213462306a36Sopenharmony_ci 213562306a36Sopenharmony_ci return xfs_errortag_clearall(mp); 213662306a36Sopenharmony_ci 213762306a36Sopenharmony_ci case XFS_IOC_FREE_EOFBLOCKS: { 213862306a36Sopenharmony_ci struct xfs_fs_eofblocks eofb; 213962306a36Sopenharmony_ci struct xfs_icwalk icw; 214062306a36Sopenharmony_ci 214162306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 214262306a36Sopenharmony_ci return -EPERM; 214362306a36Sopenharmony_ci 214462306a36Sopenharmony_ci if (xfs_is_readonly(mp)) 214562306a36Sopenharmony_ci return -EROFS; 214662306a36Sopenharmony_ci 214762306a36Sopenharmony_ci if (copy_from_user(&eofb, arg, sizeof(eofb))) 214862306a36Sopenharmony_ci return -EFAULT; 214962306a36Sopenharmony_ci 215062306a36Sopenharmony_ci error = xfs_fs_eofblocks_from_user(&eofb, &icw); 215162306a36Sopenharmony_ci if (error) 215262306a36Sopenharmony_ci return error; 215362306a36Sopenharmony_ci 215462306a36Sopenharmony_ci trace_xfs_ioc_free_eofblocks(mp, &icw, _RET_IP_); 215562306a36Sopenharmony_ci 215662306a36Sopenharmony_ci sb_start_write(mp->m_super); 215762306a36Sopenharmony_ci error = xfs_blockgc_free_space(mp, &icw); 215862306a36Sopenharmony_ci sb_end_write(mp->m_super); 215962306a36Sopenharmony_ci return error; 216062306a36Sopenharmony_ci } 216162306a36Sopenharmony_ci 216262306a36Sopenharmony_ci default: 216362306a36Sopenharmony_ci return -ENOTTY; 216462306a36Sopenharmony_ci } 216562306a36Sopenharmony_ci} 2166