519 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (c) 2004-2005 Silicon Graphics, Inc.4 * All Rights Reserved.5 */6#include <linux/mount.h>7#include <linux/fsmap.h>8#include "xfs.h"9#include "xfs_fs.h"10#include "xfs_shared.h"11#include "xfs_format.h"12#include "xfs_log_format.h"13#include "xfs_trans_resv.h"14#include "xfs_mount.h"15#include "xfs_inode.h"16#include "xfs_iwalk.h"17#include "xfs_itable.h"18#include "xfs_fsops.h"19#include "xfs_rtalloc.h"20#include "xfs_da_format.h"21#include "xfs_da_btree.h"22#include "xfs_attr.h"23#include "xfs_ioctl.h"24#include "xfs_ioctl32.h"25#include "xfs_trace.h"26#include "xfs_sb.h"27#include "xfs_handle.h"28 29#define _NATIVE_IOC(cmd, type) \30 _IOC(_IOC_DIR(cmd), _IOC_TYPE(cmd), _IOC_NR(cmd), sizeof(type))31 32#ifdef BROKEN_X86_ALIGNMENT33STATIC int34xfs_compat_ioc_fsgeometry_v1(35 struct xfs_mount *mp,36 compat_xfs_fsop_geom_v1_t __user *arg32)37{38 struct xfs_fsop_geom fsgeo;39 40 xfs_fs_geometry(mp, &fsgeo, 3);41 /* The 32-bit variant simply has some padding at the end */42 if (copy_to_user(arg32, &fsgeo, sizeof(struct compat_xfs_fsop_geom_v1)))43 return -EFAULT;44 return 0;45}46 47STATIC int48xfs_compat_growfs_data_copyin(49 struct xfs_growfs_data *in,50 compat_xfs_growfs_data_t __user *arg32)51{52 if (get_user(in->newblocks, &arg32->newblocks) ||53 get_user(in->imaxpct, &arg32->imaxpct))54 return -EFAULT;55 return 0;56}57 58STATIC int59xfs_compat_growfs_rt_copyin(60 struct xfs_growfs_rt *in,61 compat_xfs_growfs_rt_t __user *arg32)62{63 if (get_user(in->newblocks, &arg32->newblocks) ||64 get_user(in->extsize, &arg32->extsize))65 return -EFAULT;66 return 0;67}68 69STATIC int70xfs_fsinumbers_fmt_compat(71 struct xfs_ibulk *breq,72 const struct xfs_inumbers *ig)73{74 struct compat_xfs_inogrp __user *p32 = breq->ubuffer;75 struct xfs_inogrp ig1;76 struct xfs_inogrp *igrp = &ig1;77 78 xfs_inumbers_to_inogrp(&ig1, ig);79 80 if (put_user(igrp->xi_startino, &p32->xi_startino) ||81 put_user(igrp->xi_alloccount, &p32->xi_alloccount) ||82 put_user(igrp->xi_allocmask, &p32->xi_allocmask))83 return -EFAULT;84 85 return xfs_ibulk_advance(breq, sizeof(struct compat_xfs_inogrp));86}87 88#else89#define xfs_fsinumbers_fmt_compat xfs_fsinumbers_fmt90#endif /* BROKEN_X86_ALIGNMENT */91 92STATIC int93xfs_ioctl32_bstime_copyin(94 xfs_bstime_t *bstime,95 compat_xfs_bstime_t __user *bstime32)96{97 old_time32_t sec32; /* tv_sec differs on 64 vs. 32 */98 99 if (get_user(sec32, &bstime32->tv_sec) ||100 get_user(bstime->tv_nsec, &bstime32->tv_nsec))101 return -EFAULT;102 bstime->tv_sec = sec32;103 return 0;104}105 106/*107 * struct xfs_bstat has differing alignment on intel, & bstime_t sizes108 * everywhere109 */110STATIC int111xfs_ioctl32_bstat_copyin(112 struct xfs_bstat *bstat,113 struct compat_xfs_bstat __user *bstat32)114{115 if (get_user(bstat->bs_ino, &bstat32->bs_ino) ||116 get_user(bstat->bs_mode, &bstat32->bs_mode) ||117 get_user(bstat->bs_nlink, &bstat32->bs_nlink) ||118 get_user(bstat->bs_uid, &bstat32->bs_uid) ||119 get_user(bstat->bs_gid, &bstat32->bs_gid) ||120 get_user(bstat->bs_rdev, &bstat32->bs_rdev) ||121 get_user(bstat->bs_blksize, &bstat32->bs_blksize) ||122 get_user(bstat->bs_size, &bstat32->bs_size) ||123 xfs_ioctl32_bstime_copyin(&bstat->bs_atime, &bstat32->bs_atime) ||124 xfs_ioctl32_bstime_copyin(&bstat->bs_mtime, &bstat32->bs_mtime) ||125 xfs_ioctl32_bstime_copyin(&bstat->bs_ctime, &bstat32->bs_ctime) ||126 get_user(bstat->bs_blocks, &bstat32->bs_size) ||127 get_user(bstat->bs_xflags, &bstat32->bs_size) ||128 get_user(bstat->bs_extsize, &bstat32->bs_extsize) ||129 get_user(bstat->bs_extents, &bstat32->bs_extents) ||130 get_user(bstat->bs_gen, &bstat32->bs_gen) ||131 get_user(bstat->bs_projid_lo, &bstat32->bs_projid_lo) ||132 get_user(bstat->bs_projid_hi, &bstat32->bs_projid_hi) ||133 get_user(bstat->bs_forkoff, &bstat32->bs_forkoff) ||134 get_user(bstat->bs_dmevmask, &bstat32->bs_dmevmask) ||135 get_user(bstat->bs_dmstate, &bstat32->bs_dmstate) ||136 get_user(bstat->bs_aextents, &bstat32->bs_aextents))137 return -EFAULT;138 return 0;139}140 141/* XFS_IOC_FSBULKSTAT and friends */142 143STATIC int144xfs_bstime_store_compat(145 compat_xfs_bstime_t __user *p32,146 const xfs_bstime_t *p)147{148 __s32 sec32;149 150 sec32 = p->tv_sec;151 if (put_user(sec32, &p32->tv_sec) ||152 put_user(p->tv_nsec, &p32->tv_nsec))153 return -EFAULT;154 return 0;155}156 157/* Return 0 on success or positive error (to xfs_bulkstat()) */158STATIC int159xfs_fsbulkstat_one_fmt_compat(160 struct xfs_ibulk *breq,161 const struct xfs_bulkstat *bstat)162{163 struct compat_xfs_bstat __user *p32 = breq->ubuffer;164 struct xfs_bstat bs1;165 struct xfs_bstat *buffer = &bs1;166 167 xfs_bulkstat_to_bstat(breq->mp, &bs1, bstat);168 169 if (put_user(buffer->bs_ino, &p32->bs_ino) ||170 put_user(buffer->bs_mode, &p32->bs_mode) ||171 put_user(buffer->bs_nlink, &p32->bs_nlink) ||172 put_user(buffer->bs_uid, &p32->bs_uid) ||173 put_user(buffer->bs_gid, &p32->bs_gid) ||174 put_user(buffer->bs_rdev, &p32->bs_rdev) ||175 put_user(buffer->bs_blksize, &p32->bs_blksize) ||176 put_user(buffer->bs_size, &p32->bs_size) ||177 xfs_bstime_store_compat(&p32->bs_atime, &buffer->bs_atime) ||178 xfs_bstime_store_compat(&p32->bs_mtime, &buffer->bs_mtime) ||179 xfs_bstime_store_compat(&p32->bs_ctime, &buffer->bs_ctime) ||180 put_user(buffer->bs_blocks, &p32->bs_blocks) ||181 put_user(buffer->bs_xflags, &p32->bs_xflags) ||182 put_user(buffer->bs_extsize, &p32->bs_extsize) ||183 put_user(buffer->bs_extents, &p32->bs_extents) ||184 put_user(buffer->bs_gen, &p32->bs_gen) ||185 put_user(buffer->bs_projid, &p32->bs_projid) ||186 put_user(buffer->bs_projid_hi, &p32->bs_projid_hi) ||187 put_user(buffer->bs_forkoff, &p32->bs_forkoff) ||188 put_user(buffer->bs_dmevmask, &p32->bs_dmevmask) ||189 put_user(buffer->bs_dmstate, &p32->bs_dmstate) ||190 put_user(buffer->bs_aextents, &p32->bs_aextents))191 return -EFAULT;192 193 return xfs_ibulk_advance(breq, sizeof(struct compat_xfs_bstat));194}195 196/* copied from xfs_ioctl.c */197STATIC int198xfs_compat_ioc_fsbulkstat(199 struct file *file,200 unsigned int cmd,201 struct compat_xfs_fsop_bulkreq __user *p32)202{203 struct xfs_mount *mp = XFS_I(file_inode(file))->i_mount;204 u32 addr;205 struct xfs_fsop_bulkreq bulkreq;206 struct xfs_ibulk breq = {207 .mp = mp,208 .idmap = file_mnt_idmap(file),209 .ocount = 0,210 };211 xfs_ino_t lastino;212 int error;213 214 /*215 * Output structure handling functions. Depending on the command,216 * either the xfs_bstat and xfs_inogrp structures are written out217 * to userpace memory via bulkreq.ubuffer. Normally the compat218 * functions and structure size are the correct ones to use ...219 */220 inumbers_fmt_pf inumbers_func = xfs_fsinumbers_fmt_compat;221 bulkstat_one_fmt_pf bs_one_func = xfs_fsbulkstat_one_fmt_compat;222 223#ifdef CONFIG_X86_X32_ABI224 if (in_x32_syscall()) {225 /*226 * ... but on x32 the input xfs_fsop_bulkreq has pointers227 * which must be handled in the "compat" (32-bit) way, while228 * the xfs_bstat and xfs_inogrp structures follow native 64-229 * bit layout convention. So adjust accordingly, otherwise230 * the data written out in compat layout will not match what231 * x32 userspace expects.232 */233 inumbers_func = xfs_fsinumbers_fmt;234 bs_one_func = xfs_fsbulkstat_one_fmt;235 }236#endif237 238 /* done = 1 if there are more stats to get and if bulkstat */239 /* should be called again (unused here, but used in dmapi) */240 241 if (!capable(CAP_SYS_ADMIN))242 return -EPERM;243 244 if (xfs_is_shutdown(mp))245 return -EIO;246 247 if (get_user(addr, &p32->lastip))248 return -EFAULT;249 bulkreq.lastip = compat_ptr(addr);250 if (get_user(bulkreq.icount, &p32->icount) ||251 get_user(addr, &p32->ubuffer))252 return -EFAULT;253 bulkreq.ubuffer = compat_ptr(addr);254 if (get_user(addr, &p32->ocount))255 return -EFAULT;256 bulkreq.ocount = compat_ptr(addr);257 258 if (copy_from_user(&lastino, bulkreq.lastip, sizeof(__s64)))259 return -EFAULT;260 261 if (bulkreq.icount <= 0)262 return -EINVAL;263 264 if (bulkreq.ubuffer == NULL)265 return -EINVAL;266 267 breq.ubuffer = bulkreq.ubuffer;268 breq.icount = bulkreq.icount;269 270 /*271 * FSBULKSTAT_SINGLE expects that *lastip contains the inode number272 * that we want to stat. However, FSINUMBERS and FSBULKSTAT expect273 * that *lastip contains either zero or the number of the last inode to274 * be examined by the previous call and return results starting with275 * the next inode after that. The new bulk request back end functions276 * take the inode to start with, so we have to compute the startino277 * parameter from lastino to maintain correct function. lastino == 0278 * is a special case because it has traditionally meant "first inode279 * in filesystem".280 */281 if (cmd == XFS_IOC_FSINUMBERS_32) {282 breq.startino = lastino ? lastino + 1 : 0;283 error = xfs_inumbers(&breq, inumbers_func);284 lastino = breq.startino - 1;285 } else if (cmd == XFS_IOC_FSBULKSTAT_SINGLE_32) {286 breq.startino = lastino;287 breq.icount = 1;288 error = xfs_bulkstat_one(&breq, bs_one_func);289 lastino = breq.startino;290 } else if (cmd == XFS_IOC_FSBULKSTAT_32) {291 breq.startino = lastino ? lastino + 1 : 0;292 error = xfs_bulkstat(&breq, bs_one_func);293 lastino = breq.startino - 1;294 } else {295 error = -EINVAL;296 }297 if (error)298 return error;299 300 if (bulkreq.lastip != NULL &&301 copy_to_user(bulkreq.lastip, &lastino, sizeof(xfs_ino_t)))302 return -EFAULT;303 304 if (bulkreq.ocount != NULL &&305 copy_to_user(bulkreq.ocount, &breq.ocount, sizeof(__s32)))306 return -EFAULT;307 308 return 0;309}310 311STATIC int312xfs_compat_handlereq_copyin(313 xfs_fsop_handlereq_t *hreq,314 compat_xfs_fsop_handlereq_t __user *arg32)315{316 compat_xfs_fsop_handlereq_t hreq32;317 318 if (copy_from_user(&hreq32, arg32, sizeof(compat_xfs_fsop_handlereq_t)))319 return -EFAULT;320 321 hreq->fd = hreq32.fd;322 hreq->path = compat_ptr(hreq32.path);323 hreq->oflags = hreq32.oflags;324 hreq->ihandle = compat_ptr(hreq32.ihandle);325 hreq->ihandlen = hreq32.ihandlen;326 hreq->ohandle = compat_ptr(hreq32.ohandle);327 hreq->ohandlen = compat_ptr(hreq32.ohandlen);328 329 return 0;330}331 332STATIC struct dentry *333xfs_compat_handlereq_to_dentry(334 struct file *parfilp,335 compat_xfs_fsop_handlereq_t *hreq)336{337 return xfs_handle_to_dentry(parfilp,338 compat_ptr(hreq->ihandle), hreq->ihandlen);339}340 341STATIC int342xfs_compat_attrlist_by_handle(343 struct file *parfilp,344 compat_xfs_fsop_attrlist_handlereq_t __user *p)345{346 compat_xfs_fsop_attrlist_handlereq_t al_hreq;347 struct dentry *dentry;348 int error;349 350 if (!capable(CAP_SYS_ADMIN))351 return -EPERM;352 if (copy_from_user(&al_hreq, p, sizeof(al_hreq)))353 return -EFAULT;354 355 dentry = xfs_compat_handlereq_to_dentry(parfilp, &al_hreq.hreq);356 if (IS_ERR(dentry))357 return PTR_ERR(dentry);358 359 error = xfs_ioc_attr_list(XFS_I(d_inode(dentry)),360 compat_ptr(al_hreq.buffer), al_hreq.buflen,361 al_hreq.flags, &p->pos);362 dput(dentry);363 return error;364}365 366STATIC int367xfs_compat_attrmulti_by_handle(368 struct file *parfilp,369 void __user *arg)370{371 int error;372 compat_xfs_attr_multiop_t *ops;373 compat_xfs_fsop_attrmulti_handlereq_t am_hreq;374 struct dentry *dentry;375 unsigned int i, size;376 377 if (!capable(CAP_SYS_ADMIN))378 return -EPERM;379 if (copy_from_user(&am_hreq, arg,380 sizeof(compat_xfs_fsop_attrmulti_handlereq_t)))381 return -EFAULT;382 383 /* overflow check */384 if (am_hreq.opcount >= INT_MAX / sizeof(compat_xfs_attr_multiop_t))385 return -E2BIG;386 387 dentry = xfs_compat_handlereq_to_dentry(parfilp, &am_hreq.hreq);388 if (IS_ERR(dentry))389 return PTR_ERR(dentry);390 391 error = -E2BIG;392 size = am_hreq.opcount * sizeof(compat_xfs_attr_multiop_t);393 if (!size || size > 16 * PAGE_SIZE)394 goto out_dput;395 396 ops = memdup_user(compat_ptr(am_hreq.ops), size);397 if (IS_ERR(ops)) {398 error = PTR_ERR(ops);399 goto out_dput;400 }401 402 error = 0;403 for (i = 0; i < am_hreq.opcount; i++) {404 ops[i].am_error = xfs_ioc_attrmulti_one(parfilp,405 d_inode(dentry), ops[i].am_opcode,406 compat_ptr(ops[i].am_attrname),407 compat_ptr(ops[i].am_attrvalue),408 &ops[i].am_length, ops[i].am_flags);409 }410 411 if (copy_to_user(compat_ptr(am_hreq.ops), ops, size))412 error = -EFAULT;413 414 kfree(ops);415 out_dput:416 dput(dentry);417 return error;418}419 420long421xfs_file_compat_ioctl(422 struct file *filp,423 unsigned cmd,424 unsigned long p)425{426 struct inode *inode = file_inode(filp);427 struct xfs_inode *ip = XFS_I(inode);428 void __user *arg = compat_ptr(p);429 int error;430 431 trace_xfs_file_compat_ioctl(ip);432 433 switch (cmd) {434#if defined(BROKEN_X86_ALIGNMENT)435 case XFS_IOC_FSGEOMETRY_V1_32:436 return xfs_compat_ioc_fsgeometry_v1(ip->i_mount, arg);437 case XFS_IOC_FSGROWFSDATA_32: {438 struct xfs_growfs_data in;439 440 if (xfs_compat_growfs_data_copyin(&in, arg))441 return -EFAULT;442 error = mnt_want_write_file(filp);443 if (error)444 return error;445 error = xfs_growfs_data(ip->i_mount, &in);446 mnt_drop_write_file(filp);447 return error;448 }449 case XFS_IOC_FSGROWFSRT_32: {450 struct xfs_growfs_rt in;451 452 if (xfs_compat_growfs_rt_copyin(&in, arg))453 return -EFAULT;454 error = mnt_want_write_file(filp);455 if (error)456 return error;457 error = xfs_growfs_rt(ip->i_mount, &in);458 mnt_drop_write_file(filp);459 return error;460 }461#endif462 /* long changes size, but xfs only copiese out 32 bits */463 case XFS_IOC_GETVERSION_32:464 cmd = _NATIVE_IOC(cmd, long);465 return xfs_file_ioctl(filp, cmd, p);466 case XFS_IOC_SWAPEXT_32: {467 struct xfs_swapext sxp;468 struct compat_xfs_swapext __user *sxu = arg;469 470 /* Bulk copy in up to the sx_stat field, then copy bstat */471 if (copy_from_user(&sxp, sxu,472 offsetof(struct xfs_swapext, sx_stat)) ||473 xfs_ioctl32_bstat_copyin(&sxp.sx_stat, &sxu->sx_stat))474 return -EFAULT;475 error = mnt_want_write_file(filp);476 if (error)477 return error;478 error = xfs_ioc_swapext(&sxp);479 mnt_drop_write_file(filp);480 return error;481 }482 case XFS_IOC_FSBULKSTAT_32:483 case XFS_IOC_FSBULKSTAT_SINGLE_32:484 case XFS_IOC_FSINUMBERS_32:485 return xfs_compat_ioc_fsbulkstat(filp, cmd, arg);486 case XFS_IOC_FD_TO_HANDLE_32:487 case XFS_IOC_PATH_TO_HANDLE_32:488 case XFS_IOC_PATH_TO_FSHANDLE_32: {489 struct xfs_fsop_handlereq hreq;490 491 if (xfs_compat_handlereq_copyin(&hreq, arg))492 return -EFAULT;493 cmd = _NATIVE_IOC(cmd, struct xfs_fsop_handlereq);494 return xfs_find_handle(cmd, &hreq);495 }496 case XFS_IOC_OPEN_BY_HANDLE_32: {497 struct xfs_fsop_handlereq hreq;498 499 if (xfs_compat_handlereq_copyin(&hreq, arg))500 return -EFAULT;501 return xfs_open_by_handle(filp, &hreq);502 }503 case XFS_IOC_READLINK_BY_HANDLE_32: {504 struct xfs_fsop_handlereq hreq;505 506 if (xfs_compat_handlereq_copyin(&hreq, arg))507 return -EFAULT;508 return xfs_readlink_by_handle(filp, &hreq);509 }510 case XFS_IOC_ATTRLIST_BY_HANDLE_32:511 return xfs_compat_attrlist_by_handle(filp, arg);512 case XFS_IOC_ATTRMULTI_BY_HANDLE_32:513 return xfs_compat_attrmulti_by_handle(filp, arg);514 default:515 /* try the native version */516 return xfs_file_ioctl(filp, cmd, (unsigned long)arg);517 }518}519