xref: /freebsd/sys/compat/linux/linux_stats.c (revision 535af610)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 1994-1995 Søren Schmidt
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31 
32 #include "opt_ktrace.h"
33 
34 #include <sys/param.h>
35 #include <sys/capsicum.h>
36 #include <sys/dirent.h>
37 #include <sys/lock.h>
38 #include <sys/malloc.h>
39 #include <sys/mutex.h>
40 #include <sys/namei.h>
41 #include <sys/proc.h>
42 #include <sys/stat.h>
43 #include <sys/syscallsubr.h>
44 #include <sys/tty.h>
45 #include <sys/vnode.h>
46 #ifdef KTRACE
47 #include <sys/ktrace.h>
48 #endif
49 
50 #include <security/audit/audit.h>
51 
52 #ifdef COMPAT_LINUX32
53 #include <machine/../linux32/linux.h>
54 #include <machine/../linux32/linux32_proto.h>
55 #else
56 #include <machine/../linux/linux.h>
57 #include <machine/../linux/linux_proto.h>
58 #endif
59 
60 #include <compat/linux/linux.h>
61 #include <compat/linux/linux_file.h>
62 #include <compat/linux/linux_util.h>
63 
64 
65 static int
66 linux_kern_fstat(struct thread *td, int fd, struct stat *sbp)
67 {
68 	struct vnode *vp;
69 	struct file *fp;
70 	int error;
71 
72 	AUDIT_ARG_FD(fd);
73 
74 	error = fget(td, fd, &cap_fstat_rights, &fp);
75 	if (__predict_false(error != 0))
76 		return (error);
77 
78 	AUDIT_ARG_FILE(td->td_proc, fp);
79 
80 	error = fo_stat(fp, sbp, td->td_ucred);
81 	if (error == 0 && (vp = fp->f_vnode) != NULL)
82 		translate_vnhook_major_minor(vp, sbp);
83 	fdrop(fp, td);
84 #ifdef KTRACE
85 	if (KTRPOINT(td, KTR_STRUCT))
86 		ktrstat_error(sbp, error);
87 #endif
88 	return (error);
89 }
90 
91 static int
92 linux_kern_statat(struct thread *td, int flag, int fd, const char *path,
93     enum uio_seg pathseg, struct stat *sbp)
94 {
95 	struct nameidata nd;
96 	int error;
97 
98 	if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_RESOLVE_BENEATH |
99 	    AT_EMPTY_PATH)) != 0)
100 		return (EINVAL);
101 
102 	NDINIT_ATRIGHTS(&nd, LOOKUP, at2cnpflags(flag, AT_RESOLVE_BENEATH |
103 	    AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH) | LOCKSHARED | LOCKLEAF |
104 	    AUDITVNODE1, pathseg, path, fd, &cap_fstat_rights);
105 
106 	if ((error = namei(&nd)) != 0) {
107 		if (error == ENOTDIR &&
108 		    (nd.ni_resflags & NIRES_EMPTYPATH) != 0)
109 			error = linux_kern_fstat(td, fd, sbp);
110 		return (error);
111 	}
112 	error = VOP_STAT(nd.ni_vp, sbp, td->td_ucred, NOCRED);
113 	if (error == 0)
114 		translate_vnhook_major_minor(nd.ni_vp, sbp);
115 	NDFREE_PNBUF(&nd);
116 	vput(nd.ni_vp);
117 #ifdef KTRACE
118 	if (KTRPOINT(td, KTR_STRUCT))
119 		ktrstat_error(sbp, error);
120 #endif
121 	return (error);
122 }
123 
124 #ifdef LINUX_LEGACY_SYSCALLS
125 static int
126 linux_kern_stat(struct thread *td, const char *path, enum uio_seg pathseg,
127     struct stat *sbp)
128 {
129 
130 	return (linux_kern_statat(td, 0, AT_FDCWD, path, pathseg, sbp));
131 }
132 
133 static int
134 linux_kern_lstat(struct thread *td, const char *path, enum uio_seg pathseg,
135     struct stat *sbp)
136 {
137 
138 	return (linux_kern_statat(td, AT_SYMLINK_NOFOLLOW, AT_FDCWD, path,
139 	    pathseg, sbp));
140 }
141 #endif
142 
143 static int
144 newstat_copyout(struct stat *buf, void *ubuf)
145 {
146 	struct l_newstat tbuf;
147 
148 	bzero(&tbuf, sizeof(tbuf));
149 	tbuf.st_dev = linux_new_encode_dev(buf->st_dev);
150 	tbuf.st_ino = buf->st_ino;
151 	tbuf.st_mode = buf->st_mode;
152 	tbuf.st_nlink = buf->st_nlink;
153 	tbuf.st_uid = buf->st_uid;
154 	tbuf.st_gid = buf->st_gid;
155 	tbuf.st_rdev = linux_new_encode_dev(buf->st_rdev);
156 	tbuf.st_size = buf->st_size;
157 	tbuf.st_atim.tv_sec = buf->st_atim.tv_sec;
158 	tbuf.st_atim.tv_nsec = buf->st_atim.tv_nsec;
159 	tbuf.st_mtim.tv_sec = buf->st_mtim.tv_sec;
160 	tbuf.st_mtim.tv_nsec = buf->st_mtim.tv_nsec;
161 	tbuf.st_ctim.tv_sec = buf->st_ctim.tv_sec;
162 	tbuf.st_ctim.tv_nsec = buf->st_ctim.tv_nsec;
163 	tbuf.st_blksize = buf->st_blksize;
164 	tbuf.st_blocks = buf->st_blocks;
165 
166 	return (copyout(&tbuf, ubuf, sizeof(tbuf)));
167 }
168 
169 
170 #ifdef LINUX_LEGACY_SYSCALLS
171 int
172 linux_newstat(struct thread *td, struct linux_newstat_args *args)
173 {
174 	struct stat buf;
175 	int error;
176 
177 	error = linux_kern_stat(td, args->path, UIO_USERSPACE, &buf);
178 	if (error)
179 		return (error);
180 	return (newstat_copyout(&buf, args->buf));
181 }
182 
183 int
184 linux_newlstat(struct thread *td, struct linux_newlstat_args *args)
185 {
186 	struct stat sb;
187 	int error;
188 
189 	error = linux_kern_lstat(td, args->path, UIO_USERSPACE, &sb);
190 	if (error)
191 		return (error);
192 	return (newstat_copyout(&sb, args->buf));
193 }
194 #endif
195 
196 int
197 linux_newfstat(struct thread *td, struct linux_newfstat_args *args)
198 {
199 	struct stat buf;
200 	int error;
201 
202 	error = linux_kern_fstat(td, args->fd, &buf);
203 	if (!error)
204 		error = newstat_copyout(&buf, args->buf);
205 
206 	return (error);
207 }
208 
209 #if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32))
210 
211 static __inline uint16_t
212 linux_old_encode_dev(dev_t _dev)
213 {
214 
215 	return (_dev == NODEV ? 0 : linux_encode_dev(major(_dev), minor(_dev)));
216 }
217 
218 static int
219 old_stat_copyout(struct stat *buf, void *ubuf)
220 {
221 	struct l_old_stat lbuf;
222 
223 	bzero(&lbuf, sizeof(lbuf));
224 	lbuf.st_dev = linux_old_encode_dev(buf->st_dev);
225 	lbuf.st_ino = buf->st_ino;
226 	lbuf.st_mode = buf->st_mode;
227 	lbuf.st_nlink = buf->st_nlink;
228 	lbuf.st_uid = buf->st_uid;
229 	lbuf.st_gid = buf->st_gid;
230 	lbuf.st_rdev = linux_old_encode_dev(buf->st_rdev);
231 	lbuf.st_size = MIN(buf->st_size, INT32_MAX);
232 	lbuf.st_atim.tv_sec = buf->st_atim.tv_sec;
233 	lbuf.st_atim.tv_nsec = buf->st_atim.tv_nsec;
234 	lbuf.st_mtim.tv_sec = buf->st_mtim.tv_sec;
235 	lbuf.st_mtim.tv_nsec = buf->st_mtim.tv_nsec;
236 	lbuf.st_ctim.tv_sec = buf->st_ctim.tv_sec;
237 	lbuf.st_ctim.tv_nsec = buf->st_ctim.tv_nsec;
238 	lbuf.st_blksize = buf->st_blksize;
239 	lbuf.st_blocks = buf->st_blocks;
240 	lbuf.st_flags = buf->st_flags;
241 	lbuf.st_gen = buf->st_gen;
242 
243 	return (copyout(&lbuf, ubuf, sizeof(lbuf)));
244 }
245 
246 int
247 linux_stat(struct thread *td, struct linux_stat_args *args)
248 {
249 	struct stat buf;
250 	int error;
251 
252 	error = linux_kern_stat(td, args->path, UIO_USERSPACE, &buf);
253 	if (error) {
254 		return (error);
255 	}
256 	return (old_stat_copyout(&buf, args->up));
257 }
258 
259 int
260 linux_lstat(struct thread *td, struct linux_lstat_args *args)
261 {
262 	struct stat buf;
263 	int error;
264 
265 	error = linux_kern_lstat(td, args->path, UIO_USERSPACE, &buf);
266 	if (error) {
267 		return (error);
268 	}
269 	return (old_stat_copyout(&buf, args->up));
270 }
271 #endif /* __i386__ || (__amd64__ && COMPAT_LINUX32) */
272 
273 struct l_statfs {
274 	l_long		f_type;
275 	l_long		f_bsize;
276 	l_long		f_blocks;
277 	l_long		f_bfree;
278 	l_long		f_bavail;
279 	l_long		f_files;
280 	l_long		f_ffree;
281 	l_fsid_t	f_fsid;
282 	l_long		f_namelen;
283 	l_long		f_frsize;
284 	l_long		f_flags;
285 	l_long		f_spare[4];
286 };
287 
288 #define	LINUX_CODA_SUPER_MAGIC	0x73757245L
289 #define	LINUX_EXT2_SUPER_MAGIC	0xEF53L
290 #define	LINUX_HPFS_SUPER_MAGIC	0xf995e849L
291 #define	LINUX_ISOFS_SUPER_MAGIC	0x9660L
292 #define	LINUX_MSDOS_SUPER_MAGIC	0x4d44L
293 #define	LINUX_NCP_SUPER_MAGIC	0x564cL
294 #define	LINUX_NFS_SUPER_MAGIC	0x6969L
295 #define	LINUX_NTFS_SUPER_MAGIC	0x5346544EL
296 #define	LINUX_PROC_SUPER_MAGIC	0x9fa0L
297 #define	LINUX_UFS_SUPER_MAGIC	0x00011954L	/* XXX - UFS_MAGIC in Linux */
298 #define	LINUX_ZFS_SUPER_MAGIC	0x2FC12FC1
299 #define	LINUX_DEVFS_SUPER_MAGIC	0x1373L
300 #define	LINUX_SHMFS_MAGIC	0x01021994
301 
302 static long
303 bsd_to_linux_ftype(const char *fstypename)
304 {
305 	int i;
306 	static struct {const char *bsd_name; long linux_type;} b2l_tbl[] = {
307 		{"ufs",     LINUX_UFS_SUPER_MAGIC},
308 		{"zfs",     LINUX_ZFS_SUPER_MAGIC},
309 		{"cd9660",  LINUX_ISOFS_SUPER_MAGIC},
310 		{"nfs",     LINUX_NFS_SUPER_MAGIC},
311 		{"ext2fs",  LINUX_EXT2_SUPER_MAGIC},
312 		{"procfs",  LINUX_PROC_SUPER_MAGIC},
313 		{"msdosfs", LINUX_MSDOS_SUPER_MAGIC},
314 		{"ntfs",    LINUX_NTFS_SUPER_MAGIC},
315 		{"nwfs",    LINUX_NCP_SUPER_MAGIC},
316 		{"hpfs",    LINUX_HPFS_SUPER_MAGIC},
317 		{"coda",    LINUX_CODA_SUPER_MAGIC},
318 		{"devfs",   LINUX_DEVFS_SUPER_MAGIC},
319 		{"tmpfs",   LINUX_SHMFS_MAGIC},
320 		{NULL,      0L}};
321 
322 	for (i = 0; b2l_tbl[i].bsd_name != NULL; i++)
323 		if (strcmp(b2l_tbl[i].bsd_name, fstypename) == 0)
324 			return (b2l_tbl[i].linux_type);
325 
326 	return (0L);
327 }
328 
329 static int
330 bsd_to_linux_mnt_flags(int f_flags)
331 {
332 	int flags = LINUX_ST_VALID;
333 
334 	if (f_flags & MNT_RDONLY)
335 		flags |= LINUX_ST_RDONLY;
336 	if (f_flags & MNT_NOEXEC)
337 		flags |= LINUX_ST_NOEXEC;
338 	if (f_flags & MNT_NOSUID)
339 		flags |= LINUX_ST_NOSUID;
340 	if (f_flags & MNT_NOATIME)
341 		flags |= LINUX_ST_NOATIME;
342 	if (f_flags & MNT_NOSYMFOLLOW)
343 		flags |= LINUX_ST_NOSYMFOLLOW;
344 	if (f_flags & MNT_SYNCHRONOUS)
345 		flags |= LINUX_ST_SYNCHRONOUS;
346 
347 	return (flags);
348 }
349 
350 static int
351 bsd_to_linux_statfs(struct statfs *bsd_statfs, struct l_statfs *linux_statfs)
352 {
353 
354 #if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32))
355 	statfs_scale_blocks(bsd_statfs, INT32_MAX);
356 #endif
357 	linux_statfs->f_type = bsd_to_linux_ftype(bsd_statfs->f_fstypename);
358 	linux_statfs->f_bsize = bsd_statfs->f_bsize;
359 	linux_statfs->f_blocks = bsd_statfs->f_blocks;
360 	linux_statfs->f_bfree = bsd_statfs->f_bfree;
361 	linux_statfs->f_bavail = bsd_statfs->f_bavail;
362 #if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32))
363 	linux_statfs->f_ffree = MIN(bsd_statfs->f_ffree, INT32_MAX);
364 	linux_statfs->f_files = MIN(bsd_statfs->f_files, INT32_MAX);
365 #else
366 	linux_statfs->f_ffree = bsd_statfs->f_ffree;
367 	linux_statfs->f_files = bsd_statfs->f_files;
368 #endif
369 	linux_statfs->f_fsid.val[0] = bsd_statfs->f_fsid.val[0];
370 	linux_statfs->f_fsid.val[1] = bsd_statfs->f_fsid.val[1];
371 	linux_statfs->f_namelen = MAXNAMLEN;
372 	linux_statfs->f_frsize = bsd_statfs->f_bsize;
373 	linux_statfs->f_flags = bsd_to_linux_mnt_flags(bsd_statfs->f_flags);
374 	memset(linux_statfs->f_spare, 0, sizeof(linux_statfs->f_spare));
375 
376 	return (0);
377 }
378 
379 int
380 linux_statfs(struct thread *td, struct linux_statfs_args *args)
381 {
382 	struct l_statfs linux_statfs;
383 	struct statfs *bsd_statfs;
384 	int error;
385 
386 	bsd_statfs = malloc(sizeof(struct statfs), M_STATFS, M_WAITOK);
387 	error = kern_statfs(td, args->path, UIO_USERSPACE, bsd_statfs);
388 	if (error == 0)
389 		error = bsd_to_linux_statfs(bsd_statfs, &linux_statfs);
390 	free(bsd_statfs, M_STATFS);
391 	if (error != 0)
392 		return (error);
393 	return (copyout(&linux_statfs, args->buf, sizeof(linux_statfs)));
394 }
395 
396 #if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32))
397 static void
398 bsd_to_linux_statfs64(struct statfs *bsd_statfs, struct l_statfs64 *linux_statfs)
399 {
400 
401 	linux_statfs->f_type = bsd_to_linux_ftype(bsd_statfs->f_fstypename);
402 	linux_statfs->f_bsize = bsd_statfs->f_bsize;
403 	linux_statfs->f_blocks = bsd_statfs->f_blocks;
404 	linux_statfs->f_bfree = bsd_statfs->f_bfree;
405 	linux_statfs->f_bavail = bsd_statfs->f_bavail;
406 	linux_statfs->f_ffree = bsd_statfs->f_ffree;
407 	linux_statfs->f_files = bsd_statfs->f_files;
408 	linux_statfs->f_fsid.val[0] = bsd_statfs->f_fsid.val[0];
409 	linux_statfs->f_fsid.val[1] = bsd_statfs->f_fsid.val[1];
410 	linux_statfs->f_namelen = MAXNAMLEN;
411 	linux_statfs->f_frsize = bsd_statfs->f_bsize;
412 	linux_statfs->f_flags = bsd_to_linux_mnt_flags(bsd_statfs->f_flags);
413 	memset(linux_statfs->f_spare, 0, sizeof(linux_statfs->f_spare));
414 }
415 
416 int
417 linux_statfs64(struct thread *td, struct linux_statfs64_args *args)
418 {
419 	struct l_statfs64 linux_statfs;
420 	struct statfs *bsd_statfs;
421 	int error;
422 
423 	if (args->bufsize != sizeof(struct l_statfs64))
424 		return (EINVAL);
425 
426 	bsd_statfs = malloc(sizeof(struct statfs), M_STATFS, M_WAITOK);
427 	error = kern_statfs(td, args->path, UIO_USERSPACE, bsd_statfs);
428 	if (error == 0)
429 		bsd_to_linux_statfs64(bsd_statfs, &linux_statfs);
430 	free(bsd_statfs, M_STATFS);
431 	if (error != 0)
432 		return (error);
433 	return (copyout(&linux_statfs, args->buf, sizeof(linux_statfs)));
434 }
435 
436 int
437 linux_fstatfs64(struct thread *td, struct linux_fstatfs64_args *args)
438 {
439 	struct l_statfs64 linux_statfs;
440 	struct statfs *bsd_statfs;
441 	int error;
442 
443 	if (args->bufsize != sizeof(struct l_statfs64))
444 		return (EINVAL);
445 
446 	bsd_statfs = malloc(sizeof(struct statfs), M_STATFS, M_WAITOK);
447 	error = kern_fstatfs(td, args->fd, bsd_statfs);
448 	if (error == 0)
449 		bsd_to_linux_statfs64(bsd_statfs, &linux_statfs);
450 	free(bsd_statfs, M_STATFS);
451 	if (error != 0)
452 		return (error);
453 	return (copyout(&linux_statfs, args->buf, sizeof(linux_statfs)));
454 }
455 #endif /* __i386__ || (__amd64__ && COMPAT_LINUX32) */
456 
457 int
458 linux_fstatfs(struct thread *td, struct linux_fstatfs_args *args)
459 {
460 	struct l_statfs linux_statfs;
461 	struct statfs *bsd_statfs;
462 	int error;
463 
464 	bsd_statfs = malloc(sizeof(struct statfs), M_STATFS, M_WAITOK);
465 	error = kern_fstatfs(td, args->fd, bsd_statfs);
466 	if (error == 0)
467 		error = bsd_to_linux_statfs(bsd_statfs, &linux_statfs);
468 	free(bsd_statfs, M_STATFS);
469 	if (error != 0)
470 		return (error);
471 	return (copyout(&linux_statfs, args->buf, sizeof(linux_statfs)));
472 }
473 
474 struct l_ustat
475 {
476 	l_daddr_t	f_tfree;
477 	l_ino_t		f_tinode;
478 	char		f_fname[6];
479 	char		f_fpack[6];
480 };
481 
482 #ifdef LINUX_LEGACY_SYSCALLS
483 int
484 linux_ustat(struct thread *td, struct linux_ustat_args *args)
485 {
486 
487 	return (EOPNOTSUPP);
488 }
489 #endif
490 
491 #if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32))
492 
493 static int
494 stat64_copyout(struct stat *buf, void *ubuf)
495 {
496 	struct l_stat64 lbuf;
497 
498 	bzero(&lbuf, sizeof(lbuf));
499 	lbuf.st_dev = linux_new_encode_dev(buf->st_dev);
500 	lbuf.st_ino = buf->st_ino;
501 	lbuf.st_mode = buf->st_mode;
502 	lbuf.st_nlink = buf->st_nlink;
503 	lbuf.st_uid = buf->st_uid;
504 	lbuf.st_gid = buf->st_gid;
505 	lbuf.st_rdev = linux_new_encode_dev(buf->st_rdev);
506 	lbuf.st_size = buf->st_size;
507 	lbuf.st_atim.tv_sec = buf->st_atim.tv_sec;
508 	lbuf.st_atim.tv_nsec = buf->st_atim.tv_nsec;
509 	lbuf.st_mtim.tv_sec = buf->st_mtim.tv_sec;
510 	lbuf.st_mtim.tv_nsec = buf->st_mtim.tv_nsec;
511 	lbuf.st_ctim.tv_sec = buf->st_ctim.tv_sec;
512 	lbuf.st_ctim.tv_nsec = buf->st_ctim.tv_nsec;
513 	lbuf.st_blksize = buf->st_blksize;
514 	lbuf.st_blocks = buf->st_blocks;
515 
516 	/*
517 	 * The __st_ino field makes all the difference. In the Linux kernel
518 	 * it is conditionally compiled based on STAT64_HAS_BROKEN_ST_INO,
519 	 * but without the assignment to __st_ino the runtime linker refuses
520 	 * to mmap(2) any shared libraries. I guess it's broken alright :-)
521 	 */
522 	lbuf.__st_ino = buf->st_ino;
523 
524 	return (copyout(&lbuf, ubuf, sizeof(lbuf)));
525 }
526 
527 int
528 linux_stat64(struct thread *td, struct linux_stat64_args *args)
529 {
530 	struct stat buf;
531 	int error;
532 
533 	error = linux_kern_stat(td, args->filename, UIO_USERSPACE, &buf);
534 	if (error)
535 		return (error);
536 	return (stat64_copyout(&buf, args->statbuf));
537 }
538 
539 int
540 linux_lstat64(struct thread *td, struct linux_lstat64_args *args)
541 {
542 	struct stat sb;
543 	int error;
544 
545 	error = linux_kern_lstat(td, args->filename, UIO_USERSPACE, &sb);
546 	if (error)
547 		return (error);
548 	return (stat64_copyout(&sb, args->statbuf));
549 }
550 
551 int
552 linux_fstat64(struct thread *td, struct linux_fstat64_args *args)
553 {
554 	struct stat buf;
555 	int error;
556 
557 	error = linux_kern_fstat(td, args->fd, &buf);
558 	if (!error)
559 		error = stat64_copyout(&buf, args->statbuf);
560 
561 	return (error);
562 }
563 
564 int
565 linux_fstatat64(struct thread *td, struct linux_fstatat64_args *args)
566 {
567 	int error, dfd, flag, unsupported;
568 	struct stat buf;
569 
570 	unsupported = args->flag & ~(LINUX_AT_SYMLINK_NOFOLLOW | LINUX_AT_EMPTY_PATH);
571 	if (unsupported != 0) {
572 		linux_msg(td, "fstatat64 unsupported flag 0x%x", unsupported);
573 		return (EINVAL);
574 	}
575 	flag = (args->flag & LINUX_AT_SYMLINK_NOFOLLOW) ?
576 	    AT_SYMLINK_NOFOLLOW : 0;
577 	flag |= (args->flag & LINUX_AT_EMPTY_PATH) ?
578 	    AT_EMPTY_PATH : 0;
579 
580 	dfd = (args->dfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->dfd;
581 	error = linux_kern_statat(td, flag, dfd, args->pathname,
582 	    UIO_USERSPACE, &buf);
583 	if (error == 0)
584 		error = stat64_copyout(&buf, args->statbuf);
585 
586 	return (error);
587 }
588 
589 #else /* __amd64__ && !COMPAT_LINUX32 */
590 
591 int
592 linux_newfstatat(struct thread *td, struct linux_newfstatat_args *args)
593 {
594 	int error, dfd, flag, unsupported;
595 	struct stat buf;
596 
597 	unsupported = args->flag & ~(LINUX_AT_SYMLINK_NOFOLLOW | LINUX_AT_EMPTY_PATH);
598 	if (unsupported != 0) {
599 		linux_msg(td, "fstatat unsupported flag 0x%x", unsupported);
600 		return (EINVAL);
601 	}
602 
603 	flag = (args->flag & LINUX_AT_SYMLINK_NOFOLLOW) ?
604 	    AT_SYMLINK_NOFOLLOW : 0;
605 	flag |= (args->flag & LINUX_AT_EMPTY_PATH) ?
606 	    AT_EMPTY_PATH : 0;
607 
608 	dfd = (args->dfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->dfd;
609 	error = linux_kern_statat(td, flag, dfd, args->pathname,
610 	    UIO_USERSPACE, &buf);
611 	if (error == 0)
612 		error = newstat_copyout(&buf, args->statbuf);
613 
614 	return (error);
615 }
616 
617 #endif /* __i386__ || (__amd64__ && COMPAT_LINUX32) */
618 
619 int
620 linux_syncfs(struct thread *td, struct linux_syncfs_args *args)
621 {
622 	struct mount *mp;
623 	struct vnode *vp;
624 	int error, save;
625 
626 	error = fgetvp(td, args->fd, &cap_fsync_rights, &vp);
627 	if (error != 0)
628 		/*
629 		 * Linux syncfs() returns only EBADF, however fgetvp()
630 		 * can return EINVAL in case of file descriptor does
631 		 * not represent a vnode. XXX.
632 		 */
633 		return (error);
634 
635 	mp = vp->v_mount;
636 	mtx_lock(&mountlist_mtx);
637 	error = vfs_busy(mp, MBF_MNTLSTLOCK);
638 	if (error != 0) {
639 		/* See comment above. */
640 		mtx_unlock(&mountlist_mtx);
641 		goto out;
642 	}
643 	if ((mp->mnt_flag & MNT_RDONLY) == 0 &&
644 	    vn_start_write(NULL, &mp, V_NOWAIT) == 0) {
645 		save = curthread_pflags_set(TDP_SYNCIO);
646 		vfs_periodic(mp, MNT_NOWAIT);
647 		VFS_SYNC(mp, MNT_NOWAIT);
648 		curthread_pflags_restore(save);
649 		vn_finished_write(mp);
650 	}
651 	vfs_unbusy(mp);
652 
653  out:
654 	vrele(vp);
655 	return (error);
656 }
657 
658 static int
659 statx_copyout(struct stat *buf, void *ubuf)
660 {
661 	struct l_statx tbuf;
662 
663 	bzero(&tbuf, sizeof(tbuf));
664 	tbuf.stx_mask = STATX_ALL;
665 	tbuf.stx_blksize = buf->st_blksize;
666 	tbuf.stx_attributes = 0;
667 	tbuf.stx_nlink = buf->st_nlink;
668 	tbuf.stx_uid = buf->st_uid;
669 	tbuf.stx_gid = buf->st_gid;
670 	tbuf.stx_mode = buf->st_mode;
671 	tbuf.stx_ino = buf->st_ino;
672 	tbuf.stx_size = buf->st_size;
673 	tbuf.stx_blocks = buf->st_blocks;
674 
675 	tbuf.stx_atime.tv_sec = buf->st_atim.tv_sec;
676 	tbuf.stx_atime.tv_nsec = buf->st_atim.tv_nsec;
677 	tbuf.stx_btime.tv_sec = buf->st_birthtim.tv_sec;
678 	tbuf.stx_btime.tv_nsec = buf->st_birthtim.tv_nsec;
679 	tbuf.stx_ctime.tv_sec = buf->st_ctim.tv_sec;
680 	tbuf.stx_ctime.tv_nsec = buf->st_ctim.tv_nsec;
681 	tbuf.stx_mtime.tv_sec = buf->st_mtim.tv_sec;
682 	tbuf.stx_mtime.tv_nsec = buf->st_mtim.tv_nsec;
683 	tbuf.stx_rdev_major = linux_encode_major(buf->st_rdev);
684 	tbuf.stx_rdev_minor = linux_encode_minor(buf->st_rdev);
685 	tbuf.stx_dev_major = linux_encode_major(buf->st_dev);
686 	tbuf.stx_dev_minor = linux_encode_minor(buf->st_dev);
687 
688 	return (copyout(&tbuf, ubuf, sizeof(tbuf)));
689 }
690 
691 int
692 linux_statx(struct thread *td, struct linux_statx_args *args)
693 {
694 	int error, dirfd, flags, unsupported;
695 	struct stat buf;
696 
697 	unsupported = args->flags & ~(LINUX_AT_SYMLINK_NOFOLLOW |
698 	    LINUX_AT_EMPTY_PATH | LINUX_AT_NO_AUTOMOUNT);
699 	if (unsupported != 0) {
700 		linux_msg(td, "statx unsupported flags 0x%x", unsupported);
701 		return (EINVAL);
702 	}
703 
704 	flags = (args->flags & LINUX_AT_SYMLINK_NOFOLLOW) ?
705 	    AT_SYMLINK_NOFOLLOW : 0;
706 	flags |= (args->flags & LINUX_AT_EMPTY_PATH) ?
707 	    AT_EMPTY_PATH : 0;
708 
709 	dirfd = (args->dirfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->dirfd;
710 	error = linux_kern_statat(td, flags, dirfd, args->pathname,
711 	    UIO_USERSPACE, &buf);
712 	if (error == 0)
713 		error = statx_copyout(&buf, args->statxbuf);
714 
715 	return (error);
716 }
717