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