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