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