xref: /freebsd/sys/compat/linux/linux_file.c (revision 5ab6ed93cd3680f8b69dd4d05823f4740a2bdef9)
1c21dee17SSøren Schmidt /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
37f2d13d6SPedro F. Giffuni  *
49a14aa01SUlrich Spörlein  * Copyright (c) 1994-1995 Søren Schmidt
5c21dee17SSøren Schmidt  * All rights reserved.
6c21dee17SSøren Schmidt  *
7c21dee17SSøren Schmidt  * Redistribution and use in source and binary forms, with or without
8c21dee17SSøren Schmidt  * modification, are permitted provided that the following conditions
9c21dee17SSøren Schmidt  * are met:
10c21dee17SSøren Schmidt  * 1. Redistributions of source code must retain the above copyright
110ba1b365SEd Maste  *    notice, this list of conditions and the following disclaimer.
12c21dee17SSøren Schmidt  * 2. Redistributions in binary form must reproduce the above copyright
13c21dee17SSøren Schmidt  *    notice, this list of conditions and the following disclaimer in the
14c21dee17SSøren Schmidt  *    documentation and/or other materials provided with the distribution.
15c21dee17SSøren Schmidt  *
160ba1b365SEd Maste  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
170ba1b365SEd Maste  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
180ba1b365SEd Maste  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
190ba1b365SEd Maste  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
200ba1b365SEd Maste  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
210ba1b365SEd Maste  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
220ba1b365SEd Maste  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
230ba1b365SEd Maste  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
240ba1b365SEd Maste  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
250ba1b365SEd Maste  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
260ba1b365SEd Maste  * SUCH DAMAGE.
27c21dee17SSøren Schmidt  */
28c21dee17SSøren Schmidt 
29c21dee17SSøren Schmidt #include <sys/param.h>
30c21dee17SSøren Schmidt #include <sys/systm.h>
31fb919e4dSMark Murray #include <sys/dirent.h>
32c21dee17SSøren Schmidt #include <sys/fcntl.h>
33c21dee17SSøren Schmidt #include <sys/file.h>
34c21dee17SSøren Schmidt #include <sys/filedesc.h>
351cd52ec3SBruce Evans #include <sys/lock.h>
365403f186SKyle Evans #include <sys/mman.h>
37a06c1246SDmitry Chagin #include <sys/selinfo.h>
38a06c1246SDmitry Chagin #include <sys/pipe.h>
39fb919e4dSMark Murray #include <sys/proc.h>
40db0d9640SAlexander Leidinger #include <sys/stat.h>
414349c6baSKonstantin Belousov #include <sys/sx.h>
42206a5d3aSIan Dowse #include <sys/syscallsubr.h>
43e58ff664SDmitry Chagin #include <sys/sysproto.h>
44d66a5066SPeter Wemm #include <sys/tty.h>
45d4b7423fSAlexander Leidinger #include <sys/unistd.h>
46fb919e4dSMark Murray #include <sys/vnode.h>
471f3dad5aSBruce Evans 
481997c537SDavid E. O'Brien #ifdef COMPAT_LINUX32
49149afbf3SMark Johnston #include <compat/freebsd32/freebsd32_misc.h>
504231b825SDmitry Chagin #include <compat/freebsd32/freebsd32_util.h>
514af27623STim J. Robbins #include <machine/../linux32/linux.h>
524af27623STim J. Robbins #include <machine/../linux32/linux32_proto.h>
531997c537SDavid E. O'Brien #else
541997c537SDavid E. O'Brien #include <machine/../linux/linux.h>
551997c537SDavid E. O'Brien #include <machine/../linux/linux_proto.h>
564af27623STim J. Robbins #endif
57d825ce0aSJohn Baldwin #include <compat/linux/linux_misc.h>
586fd44518SMarcel Moolenaar #include <compat/linux/linux_util.h>
5948b05c3fSKonstantin Belousov #include <compat/linux/linux_file.h>
60c21dee17SSøren Schmidt 
61a125ed50SMateusz Guzik static int	linux_common_open(struct thread *, int, const char *, int, int,
62a125ed50SMateusz Guzik 		    enum uio_seg);
6313d79be9SDmitry Chagin static int	linux_do_accessat(struct thread *t, int, const char *, int, int);
643a66cf03SDmitry Chagin static int	linux_getdents_error(struct thread *, int, int);
653a66cf03SDmitry Chagin 
665403f186SKyle Evans static struct bsd_to_linux_bitmap seal_bitmap[] = {
675403f186SKyle Evans 	BITMAP_1t1_LINUX(F_SEAL_SEAL),
685403f186SKyle Evans 	BITMAP_1t1_LINUX(F_SEAL_SHRINK),
695403f186SKyle Evans 	BITMAP_1t1_LINUX(F_SEAL_GROW),
705403f186SKyle Evans 	BITMAP_1t1_LINUX(F_SEAL_WRITE),
715403f186SKyle Evans };
725403f186SKyle Evans 
735403f186SKyle Evans #define	MFD_HUGETLB_ENTRY(_size)					\
745403f186SKyle Evans 	{								\
755403f186SKyle Evans 		.bsd_value = MFD_HUGE_##_size,				\
765403f186SKyle Evans 		.linux_value = LINUX_HUGETLB_FLAG_ENCODE_##_size	\
775403f186SKyle Evans 	}
785403f186SKyle Evans static struct bsd_to_linux_bitmap mfd_bitmap[] = {
795403f186SKyle Evans 	BITMAP_1t1_LINUX(MFD_CLOEXEC),
805403f186SKyle Evans 	BITMAP_1t1_LINUX(MFD_ALLOW_SEALING),
815403f186SKyle Evans 	BITMAP_1t1_LINUX(MFD_HUGETLB),
825403f186SKyle Evans 	MFD_HUGETLB_ENTRY(64KB),
835403f186SKyle Evans 	MFD_HUGETLB_ENTRY(512KB),
845403f186SKyle Evans 	MFD_HUGETLB_ENTRY(1MB),
855403f186SKyle Evans 	MFD_HUGETLB_ENTRY(2MB),
865403f186SKyle Evans 	MFD_HUGETLB_ENTRY(8MB),
875403f186SKyle Evans 	MFD_HUGETLB_ENTRY(16MB),
885403f186SKyle Evans 	MFD_HUGETLB_ENTRY(32MB),
895403f186SKyle Evans 	MFD_HUGETLB_ENTRY(256MB),
905403f186SKyle Evans 	MFD_HUGETLB_ENTRY(512MB),
915403f186SKyle Evans 	MFD_HUGETLB_ENTRY(1GB),
925403f186SKyle Evans 	MFD_HUGETLB_ENTRY(2GB),
935403f186SKyle Evans 	MFD_HUGETLB_ENTRY(16GB),
945403f186SKyle Evans };
955403f186SKyle Evans #undef MFD_HUGETLB_ENTRY
965403f186SKyle Evans 
97931e2a1aSEd Maste #ifdef LINUX_LEGACY_SYSCALLS
98c21dee17SSøren Schmidt int
linux_creat(struct thread * td,struct linux_creat_args * args)99b40ce416SJulian Elischer linux_creat(struct thread *td, struct linux_creat_args *args)
100c21dee17SSøren Schmidt {
101d66a5066SPeter Wemm 
1021024de70SMateusz Guzik 	return (kern_openat(td, AT_FDCWD, args->path, UIO_USERSPACE,
1031024de70SMateusz Guzik 	    O_WRONLY | O_CREAT | O_TRUNC, args->mode));
1041024de70SMateusz Guzik }
105931e2a1aSEd Maste #endif
1066734f35eSJulian Elischer 
1079c7b1bf2SRicardo Branco int
linux_common_openflags(int l_flags)108de774e42SConrad Meyer linux_common_openflags(int l_flags)
109c21dee17SSøren Schmidt {
110de774e42SConrad Meyer 	int bsd_flags;
111d66a5066SPeter Wemm 
112206a5d3aSIan Dowse 	bsd_flags = 0;
1136734f35eSJulian Elischer 	switch (l_flags & LINUX_O_ACCMODE) {
1146734f35eSJulian Elischer 	case LINUX_O_WRONLY:
115206a5d3aSIan Dowse 		bsd_flags |= O_WRONLY;
1166734f35eSJulian Elischer 		break;
1176734f35eSJulian Elischer 	case LINUX_O_RDWR:
118206a5d3aSIan Dowse 		bsd_flags |= O_RDWR;
1196734f35eSJulian Elischer 		break;
1206734f35eSJulian Elischer 	default:
1216734f35eSJulian Elischer 		bsd_flags |= O_RDONLY;
1226734f35eSJulian Elischer 	}
1236734f35eSJulian Elischer 	if (l_flags & LINUX_O_NDELAY)
124206a5d3aSIan Dowse 		bsd_flags |= O_NONBLOCK;
1256734f35eSJulian Elischer 	if (l_flags & LINUX_O_APPEND)
126206a5d3aSIan Dowse 		bsd_flags |= O_APPEND;
1276734f35eSJulian Elischer 	if (l_flags & LINUX_O_SYNC)
128206a5d3aSIan Dowse 		bsd_flags |= O_FSYNC;
129299cb52aSEdward Tomasz Napierala 	if (l_flags & LINUX_O_CLOEXEC)
130299cb52aSEdward Tomasz Napierala 		bsd_flags |= O_CLOEXEC;
1316734f35eSJulian Elischer 	if (l_flags & LINUX_O_NONBLOCK)
132206a5d3aSIan Dowse 		bsd_flags |= O_NONBLOCK;
133bc8e2810SEdward Tomasz Napierala 	if (l_flags & LINUX_O_ASYNC)
134206a5d3aSIan Dowse 		bsd_flags |= O_ASYNC;
1356734f35eSJulian Elischer 	if (l_flags & LINUX_O_CREAT)
136206a5d3aSIan Dowse 		bsd_flags |= O_CREAT;
1376734f35eSJulian Elischer 	if (l_flags & LINUX_O_TRUNC)
138206a5d3aSIan Dowse 		bsd_flags |= O_TRUNC;
1396734f35eSJulian Elischer 	if (l_flags & LINUX_O_EXCL)
140206a5d3aSIan Dowse 		bsd_flags |= O_EXCL;
1416734f35eSJulian Elischer 	if (l_flags & LINUX_O_NOCTTY)
142206a5d3aSIan Dowse 		bsd_flags |= O_NOCTTY;
1436734f35eSJulian Elischer 	if (l_flags & LINUX_O_DIRECT)
1444349c6baSKonstantin Belousov 		bsd_flags |= O_DIRECT;
1456734f35eSJulian Elischer 	if (l_flags & LINUX_O_NOFOLLOW)
1464349c6baSKonstantin Belousov 		bsd_flags |= O_NOFOLLOW;
1470fef797fSEd Schouten 	if (l_flags & LINUX_O_DIRECTORY)
1480fef797fSEd Schouten 		bsd_flags |= O_DIRECTORY;
1491663120aSEdward Tomasz Napierala 	if (l_flags & LINUX_O_PATH)
1501663120aSEdward Tomasz Napierala 		bsd_flags |= O_PATH;
1514349c6baSKonstantin Belousov 	/* XXX LINUX_O_NOATIME: unable to be easily implemented. */
152de774e42SConrad Meyer 	return (bsd_flags);
153de774e42SConrad Meyer }
154c21dee17SSøren Schmidt 
155de774e42SConrad Meyer static int
linux_common_open(struct thread * td,int dirfd,const char * path,int l_flags,int mode,enum uio_seg seg)156de774e42SConrad Meyer linux_common_open(struct thread *td, int dirfd, const char *path, int l_flags,
157de774e42SConrad Meyer     int mode, enum uio_seg seg)
158de774e42SConrad Meyer {
159de774e42SConrad Meyer 	struct proc *p = td->td_proc;
160de774e42SConrad Meyer 	struct file *fp;
161de774e42SConrad Meyer 	int fd;
162de774e42SConrad Meyer 	int bsd_flags, error;
163de774e42SConrad Meyer 
164de774e42SConrad Meyer 	bsd_flags = linux_common_openflags(l_flags);
165a125ed50SMateusz Guzik 	error = kern_openat(td, dirfd, path, seg, bsd_flags, mode);
166dfe91e5eSEdward Tomasz Napierala 	if (error != 0) {
167dfe91e5eSEdward Tomasz Napierala 		if (error == EMLINK)
168dfe91e5eSEdward Tomasz Napierala 			error = ELOOP;
16992f74413SEdward Tomasz Napierala 		goto done;
170dfe91e5eSEdward Tomasz Napierala 	}
1714de1818bSMateusz Guzik 	if (p->p_flag & P_CONTROLT)
1724de1818bSMateusz Guzik 		goto done;
17392f74413SEdward Tomasz Napierala 	if (bsd_flags & O_NOCTTY)
17492f74413SEdward Tomasz Napierala 		goto done;
17592f74413SEdward Tomasz Napierala 
1764349c6baSKonstantin Belousov 	/*
17761407763SDavid E. O'Brien 	 * XXX In between kern_openat() and fget(), another process
1784349c6baSKonstantin Belousov 	 * having the same filedesc could use that fd without
1794349c6baSKonstantin Belousov 	 * checking below.
1804349c6baSKonstantin Belousov 	*/
18192f74413SEdward Tomasz Napierala 	fd = td->td_retval[0];
182cbd92ce6SMatt Macy 	if (fget(td, fd, &cap_ioctl_rights, &fp) == 0) {
18392f74413SEdward Tomasz Napierala 		if (fp->f_type != DTYPE_VNODE) {
18492f74413SEdward Tomasz Napierala 			fdrop(fp, td);
18592f74413SEdward Tomasz Napierala 			goto done;
18692f74413SEdward Tomasz Napierala 		}
1874349c6baSKonstantin Belousov 		sx_slock(&proctree_lock);
1884349c6baSKonstantin Belousov 		PROC_LOCK(p);
18992f74413SEdward Tomasz Napierala 		if (SESS_LEADER(p) && !(p->p_flag & P_CONTROLT)) {
1904349c6baSKonstantin Belousov 			PROC_UNLOCK(p);
19192f74413SEdward Tomasz Napierala 			sx_sunlock(&proctree_lock);
1922609222aSPawel Jakub Dawidek 			/* XXXPJD: Verify if TIOCSCTTY is allowed. */
1934349c6baSKonstantin Belousov 			(void) fo_ioctl(fp, TIOCSCTTY, (caddr_t) 0,
1944349c6baSKonstantin Belousov 			    td->td_ucred, td);
195f591779bSSeigo Tanimura 		} else {
196216af822SJohn Baldwin 			PROC_UNLOCK(p);
1974349c6baSKonstantin Belousov 			sx_sunlock(&proctree_lock);
1984349c6baSKonstantin Belousov 		}
199565716e6SEdward Tomasz Napierala 		fdrop(fp, td);
2004349c6baSKonstantin Belousov 	}
2014349c6baSKonstantin Belousov 
20292f74413SEdward Tomasz Napierala done:
2036734f35eSJulian Elischer 	return (error);
2046734f35eSJulian Elischer }
2056734f35eSJulian Elischer 
2066734f35eSJulian Elischer int
linux_openat(struct thread * td,struct linux_openat_args * args)2076734f35eSJulian Elischer linux_openat(struct thread *td, struct linux_openat_args *args)
2086734f35eSJulian Elischer {
209fd745e1dSDmitry Chagin 	int dfd;
2106734f35eSJulian Elischer 
21148b05c3fSKonstantin Belousov 	dfd = (args->dfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->dfd;
212a125ed50SMateusz Guzik 	return (linux_common_open(td, dfd, args->filename, args->flags,
213a125ed50SMateusz Guzik 	    args->mode, UIO_USERSPACE));
214a125ed50SMateusz Guzik }
2156734f35eSJulian Elischer 
216931e2a1aSEd Maste #ifdef LINUX_LEGACY_SYSCALLS
2176734f35eSJulian Elischer int
linux_open(struct thread * td,struct linux_open_args * args)2186734f35eSJulian Elischer linux_open(struct thread *td, struct linux_open_args *args)
2196734f35eSJulian Elischer {
2206734f35eSJulian Elischer 
221a125ed50SMateusz Guzik 	return (linux_common_open(td, AT_FDCWD, args->path, args->flags,
222a125ed50SMateusz Guzik 	    args->mode, UIO_USERSPACE));
223a125ed50SMateusz Guzik }
224931e2a1aSEd Maste #endif
2256734f35eSJulian Elischer 
226c21dee17SSøren Schmidt int
linux_name_to_handle_at(struct thread * td,struct linux_name_to_handle_at_args * args)227de774e42SConrad Meyer linux_name_to_handle_at(struct thread *td,
228de774e42SConrad Meyer     struct linux_name_to_handle_at_args *args)
229de774e42SConrad Meyer {
230de774e42SConrad Meyer 	static const l_int valid_flags = (LINUX_AT_SYMLINK_FOLLOW |
231de774e42SConrad Meyer 	    LINUX_AT_EMPTY_PATH);
232de774e42SConrad Meyer 	static const l_uint fh_size = sizeof(fhandle_t);
233de774e42SConrad Meyer 
234de774e42SConrad Meyer 	fhandle_t fh;
235de774e42SConrad Meyer 	l_uint fh_bytes;
236de774e42SConrad Meyer 	l_int mount_id;
237de774e42SConrad Meyer 	int error, fd, bsd_flags;
238de774e42SConrad Meyer 
239de774e42SConrad Meyer 	if (args->flags & ~valid_flags)
240de774e42SConrad Meyer 		return (EINVAL);
241de774e42SConrad Meyer 
242de774e42SConrad Meyer 	fd = args->dirfd;
243de774e42SConrad Meyer 	if (fd == LINUX_AT_FDCWD)
244de774e42SConrad Meyer 		fd = AT_FDCWD;
245de774e42SConrad Meyer 
246de774e42SConrad Meyer 	bsd_flags = 0;
247de774e42SConrad Meyer 	if (!(args->flags & LINUX_AT_SYMLINK_FOLLOW))
248de774e42SConrad Meyer 		bsd_flags |= AT_SYMLINK_NOFOLLOW;
2498b2ce7a3SJohn Baldwin 	if ((args->flags & LINUX_AT_EMPTY_PATH) != 0)
2508b2ce7a3SJohn Baldwin 		bsd_flags |= AT_EMPTY_PATH;
251de774e42SConrad Meyer 
252de774e42SConrad Meyer 	error = kern_getfhat(td, bsd_flags, fd, args->name,
253de774e42SConrad Meyer 	    UIO_USERSPACE, &fh, UIO_SYSSPACE);
254de774e42SConrad Meyer 	if (error != 0)
255de774e42SConrad Meyer 		return (error);
256de774e42SConrad Meyer 
257de774e42SConrad Meyer 	/* Emit mount_id -- required before EOVERFLOW case. */
258de774e42SConrad Meyer 	mount_id = (fh.fh_fsid.val[0] ^ fh.fh_fsid.val[1]);
259de774e42SConrad Meyer 	error = copyout(&mount_id, args->mnt_id, sizeof(mount_id));
260de774e42SConrad Meyer 	if (error != 0)
261de774e42SConrad Meyer 		return (error);
262de774e42SConrad Meyer 
263de774e42SConrad Meyer 	/* Check if there is room for handle. */
264de774e42SConrad Meyer 	error = copyin(&args->handle->handle_bytes, &fh_bytes,
265de774e42SConrad Meyer 	    sizeof(fh_bytes));
266de774e42SConrad Meyer 	if (error != 0)
267de774e42SConrad Meyer 		return (error);
268de774e42SConrad Meyer 
269de774e42SConrad Meyer 	if (fh_bytes < fh_size) {
270de774e42SConrad Meyer 		error = copyout(&fh_size, &args->handle->handle_bytes,
271de774e42SConrad Meyer 		    sizeof(fh_size));
272de774e42SConrad Meyer 		if (error == 0)
273de774e42SConrad Meyer 			error = EOVERFLOW;
274de774e42SConrad Meyer 		return (error);
275de774e42SConrad Meyer 	}
276de774e42SConrad Meyer 
277de774e42SConrad Meyer 	/* Emit handle. */
278de774e42SConrad Meyer 	mount_id = 0;
279de774e42SConrad Meyer 	/*
280de774e42SConrad Meyer 	 * We don't use handle_type for anything yet, but initialize a known
281de774e42SConrad Meyer 	 * value.
282de774e42SConrad Meyer 	 */
283de774e42SConrad Meyer 	error = copyout(&mount_id, &args->handle->handle_type,
284de774e42SConrad Meyer 	    sizeof(mount_id));
285de774e42SConrad Meyer 	if (error != 0)
286de774e42SConrad Meyer 		return (error);
287de774e42SConrad Meyer 
288de774e42SConrad Meyer 	error = copyout(&fh, &args->handle->f_handle,
289de774e42SConrad Meyer 	    sizeof(fh));
290de774e42SConrad Meyer 	return (error);
291de774e42SConrad Meyer }
292de774e42SConrad Meyer 
293de774e42SConrad Meyer int
linux_open_by_handle_at(struct thread * td,struct linux_open_by_handle_at_args * args)294de774e42SConrad Meyer linux_open_by_handle_at(struct thread *td,
295de774e42SConrad Meyer     struct linux_open_by_handle_at_args *args)
296de774e42SConrad Meyer {
297de774e42SConrad Meyer 	l_uint fh_bytes;
298de774e42SConrad Meyer 	int bsd_flags, error;
299de774e42SConrad Meyer 
300de774e42SConrad Meyer 	error = copyin(&args->handle->handle_bytes, &fh_bytes,
301de774e42SConrad Meyer 	    sizeof(fh_bytes));
302de774e42SConrad Meyer 	if (error != 0)
303de774e42SConrad Meyer 		return (error);
304de774e42SConrad Meyer 
305de774e42SConrad Meyer 	if (fh_bytes < sizeof(fhandle_t))
306de774e42SConrad Meyer 		return (EINVAL);
307de774e42SConrad Meyer 
308de774e42SConrad Meyer 	bsd_flags = linux_common_openflags(args->flags);
309de774e42SConrad Meyer 	return (kern_fhopen(td, (void *)&args->handle->f_handle, bsd_flags));
310de774e42SConrad Meyer }
311de774e42SConrad Meyer 
312de774e42SConrad Meyer int
linux_lseek(struct thread * td,struct linux_lseek_args * args)313b40ce416SJulian Elischer linux_lseek(struct thread *td, struct linux_lseek_args *args)
314c21dee17SSøren Schmidt {
315c21dee17SSøren Schmidt 
316f67d6b5fSEdward Tomasz Napierala 	return (kern_lseek(td, args->fdes, args->off, args->whence));
317c21dee17SSøren Schmidt }
318c21dee17SSøren Schmidt 
3197f8f1d7fSDmitry Chagin #if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32))
320d66a5066SPeter Wemm int
linux_llseek(struct thread * td,struct linux_llseek_args * args)321b40ce416SJulian Elischer linux_llseek(struct thread *td, struct linux_llseek_args *args)
322d66a5066SPeter Wemm {
323d66a5066SPeter Wemm 	int error;
324d66a5066SPeter Wemm 	off_t off;
325d66a5066SPeter Wemm 
326d66a5066SPeter Wemm 	off = (args->olow) | (((off_t) args->ohigh) << 32);
327d66a5066SPeter Wemm 
328f67d6b5fSEdward Tomasz Napierala 	error = kern_lseek(td, args->fd, off, args->whence);
329f67d6b5fSEdward Tomasz Napierala 	if (error != 0)
33056c4f83dSDmitry Chagin 		return (error);
331d66a5066SPeter Wemm 
332f67d6b5fSEdward Tomasz Napierala 	error = copyout(td->td_retval, args->res, sizeof(off_t));
333f67d6b5fSEdward Tomasz Napierala 	if (error != 0)
33456c4f83dSDmitry Chagin 		return (error);
335d66a5066SPeter Wemm 
336b40ce416SJulian Elischer 	td->td_retval[0] = 0;
33756c4f83dSDmitry Chagin 	return (0);
338d66a5066SPeter Wemm }
3397f8f1d7fSDmitry Chagin #endif /* __i386__ || (__amd64__ && COMPAT_LINUX32) */
340d66a5066SPeter Wemm 
3415002a60fSMarcel Moolenaar /*
3425002a60fSMarcel Moolenaar  * Note that linux_getdents(2) and linux_getdents64(2) have the same
3435002a60fSMarcel Moolenaar  * arguments. They only differ in the definition of struct dirent they
3443a66cf03SDmitry Chagin  * operate on.
3453a66cf03SDmitry Chagin  * Note that linux_readdir(2) is a special case of linux_getdents(2)
3463a66cf03SDmitry Chagin  * where count is always equals 1, meaning that the buffer is one
3473a66cf03SDmitry Chagin  * dirent-structure in size and that the code can't handle more anyway.
3483a66cf03SDmitry Chagin  * Note that linux_readdir(2) can't be implemented by means of linux_getdents(2)
3493a66cf03SDmitry Chagin  * as in case when the *dent buffer size is equal to 1 linux_getdents(2) will
3503a66cf03SDmitry Chagin  * trash user stack.
3515002a60fSMarcel Moolenaar  */
3525002a60fSMarcel Moolenaar 
3533a66cf03SDmitry Chagin static int
linux_getdents_error(struct thread * td,int fd,int err)3543a66cf03SDmitry Chagin linux_getdents_error(struct thread *td, int fd, int err)
3553a66cf03SDmitry Chagin {
3563a66cf03SDmitry Chagin 	struct vnode *vp;
3573a66cf03SDmitry Chagin 	struct file *fp;
3583a66cf03SDmitry Chagin 	int error;
3593a66cf03SDmitry Chagin 
3603a66cf03SDmitry Chagin 	/* Linux return ENOTDIR in case when fd is not a directory. */
361cbd92ce6SMatt Macy 	error = getvnode(td, fd, &cap_read_rights, &fp);
3623a66cf03SDmitry Chagin 	if (error != 0)
3633a66cf03SDmitry Chagin 		return (error);
3643a66cf03SDmitry Chagin 	vp = fp->f_vnode;
3653a66cf03SDmitry Chagin 	if (vp->v_type != VDIR) {
3663a66cf03SDmitry Chagin 		fdrop(fp, td);
3673a66cf03SDmitry Chagin 		return (ENOTDIR);
3683a66cf03SDmitry Chagin 	}
3693a66cf03SDmitry Chagin 	fdrop(fp, td);
3703a66cf03SDmitry Chagin 	return (err);
3713a66cf03SDmitry Chagin }
3723a66cf03SDmitry Chagin 
3735002a60fSMarcel Moolenaar struct l_dirent {
3742e1a4893SRoman Divacky 	l_ulong		d_ino;
3755002a60fSMarcel Moolenaar 	l_off_t		d_off;
3765002a60fSMarcel Moolenaar 	l_ushort	d_reclen;
3775002a60fSMarcel Moolenaar 	char		d_name[LINUX_NAME_MAX + 1];
3785002a60fSMarcel Moolenaar };
3795002a60fSMarcel Moolenaar 
3805002a60fSMarcel Moolenaar struct l_dirent64 {
3815002a60fSMarcel Moolenaar 	uint64_t	d_ino;
3825002a60fSMarcel Moolenaar 	int64_t		d_off;
3835002a60fSMarcel Moolenaar 	l_ushort	d_reclen;
3845002a60fSMarcel Moolenaar 	u_char		d_type;
3855002a60fSMarcel Moolenaar 	char		d_name[LINUX_NAME_MAX + 1];
3865002a60fSMarcel Moolenaar };
3875002a60fSMarcel Moolenaar 
38829635842SRoman Divacky /*
38929635842SRoman Divacky  * Linux uses the last byte in the dirent buffer to store d_type,
39029635842SRoman Divacky  * at least glibc-2.7 requires it. That is why l_dirent is padded with 2 bytes.
39129635842SRoman Divacky  */
39229635842SRoman Divacky #define LINUX_RECLEN(namlen)						\
393b66bb393SPedro F. Giffuni     roundup(offsetof(struct l_dirent, d_name) + (namlen) + 2, sizeof(l_ulong))
3945002a60fSMarcel Moolenaar 
39529635842SRoman Divacky #define LINUX_RECLEN64(namlen)						\
396b66bb393SPedro F. Giffuni     roundup(offsetof(struct l_dirent64, d_name) + (namlen) + 1,		\
39729635842SRoman Divacky     sizeof(uint64_t))
39829635842SRoman Divacky 
399931e2a1aSEd Maste #ifdef LINUX_LEGACY_SYSCALLS
4003a66cf03SDmitry Chagin int
linux_getdents(struct thread * td,struct linux_getdents_args * args)4013a66cf03SDmitry Chagin linux_getdents(struct thread *td, struct linux_getdents_args *args)
402d66a5066SPeter Wemm {
4031d062e2bSDag-Erling Smørgrav 	struct dirent *bdp;
404c21dee17SSøren Schmidt 	caddr_t inp, buf;		/* BSD-format */
405c21dee17SSøren Schmidt 	int len, reclen;		/* BSD-format */
406c21dee17SSøren Schmidt 	caddr_t outp;			/* Linux-format */
4073a66cf03SDmitry Chagin 	int resid, linuxreclen;		/* Linux-format */
40829635842SRoman Divacky 	caddr_t lbuf;			/* Linux-format */
40969921123SKonstantin Belousov 	off_t base;
41029635842SRoman Divacky 	struct l_dirent *linux_dirent;
4113a66cf03SDmitry Chagin 	int buflen, error;
4123a66cf03SDmitry Chagin 	size_t retval;
413be5747d5SJohn Baldwin 
414ac1082e5SDmitry Chagin 	buflen = min(args->count, MAXBSIZE);
41577294677SDmitry Chagin 	buf = malloc(buflen, M_LINUX, M_WAITOK);
4165002a60fSMarcel Moolenaar 
4173a66cf03SDmitry Chagin 	error = kern_getdirentries(td, args->fd, buf, buflen,
4183a66cf03SDmitry Chagin 	    &base, NULL, UIO_SYSSPACE);
4193a66cf03SDmitry Chagin 	if (error != 0) {
4203a66cf03SDmitry Chagin 		error = linux_getdents_error(td, args->fd, error);
4213a66cf03SDmitry Chagin 		goto out1;
4223a66cf03SDmitry Chagin 	}
423c21dee17SSøren Schmidt 
42477294677SDmitry Chagin 	lbuf = malloc(LINUX_RECLEN(LINUX_NAME_MAX), M_LINUX, M_WAITOK | M_ZERO);
425c21dee17SSøren Schmidt 
4263a66cf03SDmitry Chagin 	len = td->td_retval[0];
427c21dee17SSøren Schmidt 	inp = buf;
4283a66cf03SDmitry Chagin 	outp = (caddr_t)args->dent;
4293a66cf03SDmitry Chagin 	resid = args->count;
4303a66cf03SDmitry Chagin 	retval = 0;
431d41979a0SDoug Rabson 
432c21dee17SSøren Schmidt 	while (len > 0) {
433c21dee17SSøren Schmidt 		bdp = (struct dirent *) inp;
434fe89a532SSteven Wallace 		reclen = bdp->d_reclen;
4353a66cf03SDmitry Chagin 		linuxreclen = LINUX_RECLEN(bdp->d_namlen);
4363a66cf03SDmitry Chagin 		/*
4373a66cf03SDmitry Chagin 		 * No more space in the user supplied dirent buffer.
4383a66cf03SDmitry Chagin 		 * Return EINVAL.
4393a66cf03SDmitry Chagin 		 */
4403a66cf03SDmitry Chagin 		if (resid < linuxreclen) {
4413a66cf03SDmitry Chagin 			error = EINVAL;
442fe89a532SSteven Wallace 			goto out;
443fe89a532SSteven Wallace 		}
444fe89a532SSteven Wallace 
44529635842SRoman Divacky 		linux_dirent = (struct l_dirent*)lbuf;
44629635842SRoman Divacky 		linux_dirent->d_ino = bdp->d_fileno;
447099fa2feSDmitry Chagin 		linux_dirent->d_off = bdp->d_off;
4483a66cf03SDmitry Chagin 		linux_dirent->d_reclen = linuxreclen;
44929635842SRoman Divacky 		/*
45029635842SRoman Divacky 		 * Copy d_type to last byte of l_dirent buffer
45129635842SRoman Divacky 		 */
45229635842SRoman Divacky 		lbuf[linuxreclen - 1] = bdp->d_type;
45329635842SRoman Divacky 		strlcpy(linux_dirent->d_name, bdp->d_name,
45429635842SRoman Divacky 		    linuxreclen - offsetof(struct l_dirent, d_name)-1);
45529635842SRoman Divacky 		error = copyout(linux_dirent, outp, linuxreclen);
4563a66cf03SDmitry Chagin 		if (error != 0)
457c21dee17SSøren Schmidt 			goto out;
4585002a60fSMarcel Moolenaar 
459c21dee17SSøren Schmidt 		inp += reclen;
4603a66cf03SDmitry Chagin 		base += reclen;
4613a66cf03SDmitry Chagin 		len -= reclen;
4625002a60fSMarcel Moolenaar 
4633a66cf03SDmitry Chagin 		retval += linuxreclen;
464c21dee17SSøren Schmidt 		outp += linuxreclen;
465c21dee17SSøren Schmidt 		resid -= linuxreclen;
466c21dee17SSøren Schmidt 	}
4673a66cf03SDmitry Chagin 	td->td_retval[0] = retval;
4685002a60fSMarcel Moolenaar 
469c21dee17SSøren Schmidt out:
47077294677SDmitry Chagin 	free(lbuf, M_LINUX);
4713a66cf03SDmitry Chagin out1:
47277294677SDmitry Chagin 	free(buf, M_LINUX);
4735002a60fSMarcel Moolenaar 	return (error);
4745002a60fSMarcel Moolenaar }
475931e2a1aSEd Maste #endif
4765002a60fSMarcel Moolenaar 
4775002a60fSMarcel Moolenaar int
linux_getdents64(struct thread * td,struct linux_getdents64_args * args)478b40ce416SJulian Elischer linux_getdents64(struct thread *td, struct linux_getdents64_args *args)
4795002a60fSMarcel Moolenaar {
4803a66cf03SDmitry Chagin 	struct dirent *bdp;
4813a66cf03SDmitry Chagin 	caddr_t inp, buf;		/* BSD-format */
4823a66cf03SDmitry Chagin 	int len, reclen;		/* BSD-format */
4833a66cf03SDmitry Chagin 	caddr_t outp;			/* Linux-format */
4843a66cf03SDmitry Chagin 	int resid, linuxreclen;		/* Linux-format */
48569921123SKonstantin Belousov 	off_t base;
4863a66cf03SDmitry Chagin 	struct l_dirent64 *linux_dirent64;
4873a66cf03SDmitry Chagin 	int buflen, error;
4883a66cf03SDmitry Chagin 	size_t retval;
4895002a60fSMarcel Moolenaar 
490ac1082e5SDmitry Chagin 	buflen = min(args->count, MAXBSIZE);
491e27e3fa7SDmitry Chagin 	buf = malloc(buflen, M_LINUX, M_WAITOK);
4925002a60fSMarcel Moolenaar 
4933a66cf03SDmitry Chagin 	error = kern_getdirentries(td, args->fd, buf, buflen,
4943a66cf03SDmitry Chagin 	    &base, NULL, UIO_SYSSPACE);
4953a66cf03SDmitry Chagin 	if (error != 0) {
4963a66cf03SDmitry Chagin 		error = linux_getdents_error(td, args->fd, error);
4973a66cf03SDmitry Chagin 		goto out1;
498c21dee17SSøren Schmidt 	}
499d66a5066SPeter Wemm 
500e27e3fa7SDmitry Chagin 	linux_dirent64 = malloc(LINUX_RECLEN64(LINUX_NAME_MAX), M_LINUX,
501b27f3237SDmitry Chagin 	    M_WAITOK | M_ZERO);
5023a66cf03SDmitry Chagin 
5033a66cf03SDmitry Chagin 	len = td->td_retval[0];
5043a66cf03SDmitry Chagin 	inp = buf;
5053a66cf03SDmitry Chagin 	outp = (caddr_t)args->dirent;
5063a66cf03SDmitry Chagin 	resid = args->count;
5073a66cf03SDmitry Chagin 	retval = 0;
5083a66cf03SDmitry Chagin 
5093a66cf03SDmitry Chagin 	while (len > 0) {
5103a66cf03SDmitry Chagin 		bdp = (struct dirent *) inp;
5113a66cf03SDmitry Chagin 		reclen = bdp->d_reclen;
5123a66cf03SDmitry Chagin 		linuxreclen = LINUX_RECLEN64(bdp->d_namlen);
5133a66cf03SDmitry Chagin 		/*
5143a66cf03SDmitry Chagin 		 * No more space in the user supplied dirent buffer.
5153a66cf03SDmitry Chagin 		 * Return EINVAL.
5163a66cf03SDmitry Chagin 		 */
5173a66cf03SDmitry Chagin 		if (resid < linuxreclen) {
5183a66cf03SDmitry Chagin 			error = EINVAL;
5193a66cf03SDmitry Chagin 			goto out;
5203a66cf03SDmitry Chagin 		}
5213a66cf03SDmitry Chagin 
5223a66cf03SDmitry Chagin 		linux_dirent64->d_ino = bdp->d_fileno;
523099fa2feSDmitry Chagin 		linux_dirent64->d_off = bdp->d_off;
5243a66cf03SDmitry Chagin 		linux_dirent64->d_reclen = linuxreclen;
5253a66cf03SDmitry Chagin 		linux_dirent64->d_type = bdp->d_type;
5263a66cf03SDmitry Chagin 		strlcpy(linux_dirent64->d_name, bdp->d_name,
5273a66cf03SDmitry Chagin 		    linuxreclen - offsetof(struct l_dirent64, d_name));
5283a66cf03SDmitry Chagin 		error = copyout(linux_dirent64, outp, linuxreclen);
5293a66cf03SDmitry Chagin 		if (error != 0)
5303a66cf03SDmitry Chagin 			goto out;
5313a66cf03SDmitry Chagin 
5323a66cf03SDmitry Chagin 		inp += reclen;
5333a66cf03SDmitry Chagin 		base += reclen;
5343a66cf03SDmitry Chagin 		len -= reclen;
5353a66cf03SDmitry Chagin 
5363a66cf03SDmitry Chagin 		retval += linuxreclen;
5373a66cf03SDmitry Chagin 		outp += linuxreclen;
5383a66cf03SDmitry Chagin 		resid -= linuxreclen;
5393a66cf03SDmitry Chagin 	}
5403a66cf03SDmitry Chagin 	td->td_retval[0] = retval;
5413a66cf03SDmitry Chagin 
5423a66cf03SDmitry Chagin out:
543e27e3fa7SDmitry Chagin 	free(linux_dirent64, M_LINUX);
5443a66cf03SDmitry Chagin out1:
545e27e3fa7SDmitry Chagin 	free(buf, M_LINUX);
5463a66cf03SDmitry Chagin 	return (error);
5473a66cf03SDmitry Chagin }
5483a66cf03SDmitry Chagin 
5493a66cf03SDmitry Chagin #if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32))
5503a66cf03SDmitry Chagin int
linux_readdir(struct thread * td,struct linux_readdir_args * args)5513a66cf03SDmitry Chagin linux_readdir(struct thread *td, struct linux_readdir_args *args)
5523a66cf03SDmitry Chagin {
5533a66cf03SDmitry Chagin 	struct dirent *bdp;
5543a66cf03SDmitry Chagin 	caddr_t buf;			/* BSD-format */
5553a66cf03SDmitry Chagin 	int linuxreclen;		/* Linux-format */
55669921123SKonstantin Belousov 	off_t base;
557fffb2e8dSDmitry Chagin 	struct l_dirent *linux_dirent;	/* Linux-format */
5583a66cf03SDmitry Chagin 	int buflen, error;
5593a66cf03SDmitry Chagin 
560fc1c787aSDmitry Chagin 	buflen = sizeof(*bdp);
56113d049abSDmitry Chagin 	buf = malloc(buflen, M_LINUX, M_WAITOK);
5623a66cf03SDmitry Chagin 
5633a66cf03SDmitry Chagin 	error = kern_getdirentries(td, args->fd, buf, buflen,
5643a66cf03SDmitry Chagin 	    &base, NULL, UIO_SYSSPACE);
5653a66cf03SDmitry Chagin 	if (error != 0) {
5663a66cf03SDmitry Chagin 		error = linux_getdents_error(td, args->fd, error);
5673a66cf03SDmitry Chagin 		goto out;
5683a66cf03SDmitry Chagin 	}
5693a66cf03SDmitry Chagin 	if (td->td_retval[0] == 0)
5703a66cf03SDmitry Chagin 		goto out;
5713a66cf03SDmitry Chagin 
57213d049abSDmitry Chagin 	linux_dirent = malloc(LINUX_RECLEN(LINUX_NAME_MAX), M_LINUX,
573fffb2e8dSDmitry Chagin 	    M_WAITOK | M_ZERO);
5743a66cf03SDmitry Chagin 
5753a66cf03SDmitry Chagin 	bdp = (struct dirent *) buf;
5763a66cf03SDmitry Chagin 	linuxreclen = LINUX_RECLEN(bdp->d_namlen);
5773a66cf03SDmitry Chagin 
5783a66cf03SDmitry Chagin 	linux_dirent->d_ino = bdp->d_fileno;
579099fa2feSDmitry Chagin 	linux_dirent->d_off = bdp->d_off;
5803a66cf03SDmitry Chagin 	linux_dirent->d_reclen = bdp->d_namlen;
5813a66cf03SDmitry Chagin 	strlcpy(linux_dirent->d_name, bdp->d_name,
5823a66cf03SDmitry Chagin 	    linuxreclen - offsetof(struct l_dirent, d_name));
5833a66cf03SDmitry Chagin 	error = copyout(linux_dirent, args->dent, linuxreclen);
5843a66cf03SDmitry Chagin 	if (error == 0)
5853a66cf03SDmitry Chagin 		td->td_retval[0] = linuxreclen;
5863a66cf03SDmitry Chagin 
58713d049abSDmitry Chagin 	free(linux_dirent, M_LINUX);
5883a66cf03SDmitry Chagin out:
58913d049abSDmitry Chagin 	free(buf, M_LINUX);
5903a66cf03SDmitry Chagin 	return (error);
5913a66cf03SDmitry Chagin }
5923a66cf03SDmitry Chagin #endif /* __i386__ || (__amd64__ && COMPAT_LINUX32) */
5933a66cf03SDmitry Chagin 
594d66a5066SPeter Wemm /*
595d66a5066SPeter Wemm  * These exist mainly for hooks for doing /compat/linux translation.
596d66a5066SPeter Wemm  */
597d66a5066SPeter Wemm 
598931e2a1aSEd Maste #ifdef LINUX_LEGACY_SYSCALLS
599d66a5066SPeter Wemm int
linux_access(struct thread * td,struct linux_access_args * args)600b40ce416SJulian Elischer linux_access(struct thread *td, struct linux_access_args *args)
601d66a5066SPeter Wemm {
602d66a5066SPeter Wemm 
603eae594f7SEd Maste 	/* Linux convention. */
604d3a993d4SEd Schouten 	if (args->amode & ~(F_OK | X_OK | W_OK | R_OK))
605d4b7423fSAlexander Leidinger 		return (EINVAL);
606d4b7423fSAlexander Leidinger 
607fd745e1dSDmitry Chagin 	return (kern_accessat(td, AT_FDCWD, args->path, UIO_USERSPACE, 0,
608fd745e1dSDmitry Chagin 	    args->amode));
609d66a5066SPeter Wemm }
610931e2a1aSEd Maste #endif
611d66a5066SPeter Wemm 
61213d79be9SDmitry Chagin static int
linux_do_accessat(struct thread * td,int ldfd,const char * filename,int amode,int flags)61313d79be9SDmitry Chagin linux_do_accessat(struct thread *td, int ldfd, const char *filename,
61413d79be9SDmitry Chagin     int amode, int flags)
61548b05c3fSKonstantin Belousov {
616fd745e1dSDmitry Chagin 	int dfd;
61748b05c3fSKonstantin Belousov 
618eae594f7SEd Maste 	/* Linux convention. */
61913d79be9SDmitry Chagin 	if (amode & ~(F_OK | X_OK | W_OK | R_OK))
62048b05c3fSKonstantin Belousov 		return (EINVAL);
62148b05c3fSKonstantin Belousov 
62213d79be9SDmitry Chagin 	dfd = (ldfd == LINUX_AT_FDCWD) ? AT_FDCWD : ldfd;
623fd745e1dSDmitry Chagin 	return (kern_accessat(td, dfd, filename, UIO_USERSPACE, flags, amode));
62448b05c3fSKonstantin Belousov }
62548b05c3fSKonstantin Belousov 
62613d79be9SDmitry Chagin int
linux_faccessat(struct thread * td,struct linux_faccessat_args * args)62713d79be9SDmitry Chagin linux_faccessat(struct thread *td, struct linux_faccessat_args *args)
62813d79be9SDmitry Chagin {
62913d79be9SDmitry Chagin 
63013d79be9SDmitry Chagin 	return (linux_do_accessat(td, args->dfd, args->filename, args->amode,
63113d79be9SDmitry Chagin 	    0));
63213d79be9SDmitry Chagin }
63313d79be9SDmitry Chagin 
63413d79be9SDmitry Chagin int
linux_faccessat2(struct thread * td,struct linux_faccessat2_args * args)63513d79be9SDmitry Chagin linux_faccessat2(struct thread *td, struct linux_faccessat2_args *args)
63613d79be9SDmitry Chagin {
63713d79be9SDmitry Chagin 	int flags, unsupported;
63813d79be9SDmitry Chagin 
639*5ab6ed93SFernando Apesteguía 	unsupported = args->flags & ~(LINUX_AT_EACCESS | LINUX_AT_EMPTY_PATH  |
640*5ab6ed93SFernando Apesteguía 	    LINUX_AT_SYMLINK_NOFOLLOW);
64113d79be9SDmitry Chagin 	if (unsupported != 0) {
64213d79be9SDmitry Chagin 		linux_msg(td, "faccessat2 unsupported flag 0x%x", unsupported);
64313d79be9SDmitry Chagin 		return (EINVAL);
64413d79be9SDmitry Chagin 	}
64513d79be9SDmitry Chagin 
64613d79be9SDmitry Chagin 	flags = (args->flags & LINUX_AT_EACCESS) == 0 ? 0 :
64713d79be9SDmitry Chagin 	    AT_EACCESS;
64813d79be9SDmitry Chagin 	flags |= (args->flags & LINUX_AT_EMPTY_PATH) == 0 ? 0 :
64913d79be9SDmitry Chagin 	    AT_EMPTY_PATH;
650*5ab6ed93SFernando Apesteguía 	flags |= (args->flags & LINUX_AT_SYMLINK_NOFOLLOW) == 0 ? 0 :
651*5ab6ed93SFernando Apesteguía 	    AT_SYMLINK_NOFOLLOW;
65213d79be9SDmitry Chagin 	return (linux_do_accessat(td, args->dfd, args->filename, args->amode,
65313d79be9SDmitry Chagin 	    flags));
65413d79be9SDmitry Chagin }
65513d79be9SDmitry Chagin 
65613d79be9SDmitry Chagin 
657931e2a1aSEd Maste #ifdef LINUX_LEGACY_SYSCALLS
65848b05c3fSKonstantin Belousov int
linux_unlink(struct thread * td,struct linux_unlink_args * args)659b40ce416SJulian Elischer linux_unlink(struct thread *td, struct linux_unlink_args *args)
660d66a5066SPeter Wemm {
661206a5d3aSIan Dowse 	int error;
662db0d9640SAlexander Leidinger 	struct stat st;
663d66a5066SPeter Wemm 
664a125ed50SMateusz Guzik 	error = kern_funlinkat(td, AT_FDCWD, args->path, FD_NONE,
665a125ed50SMateusz Guzik 	    UIO_USERSPACE, 0, 0);
666a125ed50SMateusz Guzik 	if (error == EPERM) {
667a125ed50SMateusz Guzik 		/* Introduce POSIX noncompliant behaviour of Linux */
668a125ed50SMateusz Guzik 		if (kern_statat(td, 0, AT_FDCWD, args->path,
669cb858340SDmitry Chagin 		    UIO_USERSPACE, &st) == 0) {
670a125ed50SMateusz Guzik 			if (S_ISDIR(st.st_mode))
671a125ed50SMateusz Guzik 				error = EISDIR;
672a125ed50SMateusz Guzik 		}
673a125ed50SMateusz Guzik 	}
674a125ed50SMateusz Guzik 
675206a5d3aSIan Dowse 	return (error);
676d66a5066SPeter Wemm }
677931e2a1aSEd Maste #endif
678d66a5066SPeter Wemm 
6791024de70SMateusz Guzik static int
linux_unlinkat_impl(struct thread * td,enum uio_seg pathseg,const char * path,int dfd,struct linux_unlinkat_args * args)6801024de70SMateusz Guzik linux_unlinkat_impl(struct thread *td, enum uio_seg pathseg, const char *path,
6811024de70SMateusz Guzik     int dfd, struct linux_unlinkat_args *args)
68248b05c3fSKonstantin Belousov {
68348b05c3fSKonstantin Belousov 	struct stat st;
6841024de70SMateusz Guzik 	int error;
68548b05c3fSKonstantin Belousov 
68648b05c3fSKonstantin Belousov 	if (args->flag & LINUX_AT_REMOVEDIR)
6871024de70SMateusz Guzik 		error = kern_frmdirat(td, dfd, path, FD_NONE, pathseg, 0);
68848b05c3fSKonstantin Belousov 	else
6891024de70SMateusz Guzik 		error = kern_funlinkat(td, dfd, path, FD_NONE, pathseg, 0, 0);
69048b05c3fSKonstantin Belousov 	if (error == EPERM && !(args->flag & LINUX_AT_REMOVEDIR)) {
69148b05c3fSKonstantin Belousov 		/* Introduce POSIX noncompliant behaviour of Linux */
69248b05c3fSKonstantin Belousov 		if (kern_statat(td, AT_SYMLINK_NOFOLLOW, dfd, path,
693cb858340SDmitry Chagin 		    pathseg, &st) == 0 && S_ISDIR(st.st_mode))
69448b05c3fSKonstantin Belousov 			error = EISDIR;
69548b05c3fSKonstantin Belousov 	}
6961024de70SMateusz Guzik 	return (error);
6971024de70SMateusz Guzik }
6981024de70SMateusz Guzik 
6991024de70SMateusz Guzik int
linux_unlinkat(struct thread * td,struct linux_unlinkat_args * args)7001024de70SMateusz Guzik linux_unlinkat(struct thread *td, struct linux_unlinkat_args *args)
7011024de70SMateusz Guzik {
702fd745e1dSDmitry Chagin 	int dfd;
7031024de70SMateusz Guzik 
7041024de70SMateusz Guzik 	if (args->flag & ~LINUX_AT_REMOVEDIR)
7051024de70SMateusz Guzik 		return (EINVAL);
7061024de70SMateusz Guzik 	dfd = (args->dfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->dfd;
7071024de70SMateusz Guzik 	return (linux_unlinkat_impl(td, UIO_USERSPACE, args->pathname,
7081024de70SMateusz Guzik 	    dfd, args));
7091024de70SMateusz Guzik }
710fd745e1dSDmitry Chagin 
71148b05c3fSKonstantin Belousov int
linux_chdir(struct thread * td,struct linux_chdir_args * args)712b40ce416SJulian Elischer linux_chdir(struct thread *td, struct linux_chdir_args *args)
713d66a5066SPeter Wemm {
714d66a5066SPeter Wemm 
7151024de70SMateusz Guzik 	return (kern_chdir(td, args->path, UIO_USERSPACE));
7161024de70SMateusz Guzik }
717d66a5066SPeter Wemm 
718931e2a1aSEd Maste #ifdef LINUX_LEGACY_SYSCALLS
719d66a5066SPeter Wemm int
linux_chmod(struct thread * td,struct linux_chmod_args * args)720b40ce416SJulian Elischer linux_chmod(struct thread *td, struct linux_chmod_args *args)
721d66a5066SPeter Wemm {
722d66a5066SPeter Wemm 
7231024de70SMateusz Guzik 	return (kern_fchmodat(td, AT_FDCWD, args->path, UIO_USERSPACE,
7241024de70SMateusz Guzik 	    args->mode, 0));
7251024de70SMateusz Guzik }
726931e2a1aSEd Maste #endif
727d66a5066SPeter Wemm 
728d66a5066SPeter Wemm int
linux_fchmodat(struct thread * td,struct linux_fchmodat_args * args)72948b05c3fSKonstantin Belousov linux_fchmodat(struct thread *td, struct linux_fchmodat_args *args)
73048b05c3fSKonstantin Belousov {
731fd745e1dSDmitry Chagin 	int dfd;
73248b05c3fSKonstantin Belousov 
73348b05c3fSKonstantin Belousov 	dfd = (args->dfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->dfd;
7341024de70SMateusz Guzik 	return (kern_fchmodat(td, dfd, args->filename, UIO_USERSPACE,
7351024de70SMateusz Guzik 	    args->mode, 0));
7361024de70SMateusz Guzik }
73748b05c3fSKonstantin Belousov 
738931e2a1aSEd Maste #ifdef LINUX_LEGACY_SYSCALLS
73948b05c3fSKonstantin Belousov int
linux_mkdir(struct thread * td,struct linux_mkdir_args * args)740b40ce416SJulian Elischer linux_mkdir(struct thread *td, struct linux_mkdir_args *args)
741d66a5066SPeter Wemm {
742d66a5066SPeter Wemm 
7431024de70SMateusz Guzik 	return (kern_mkdirat(td, AT_FDCWD, args->path, UIO_USERSPACE, args->mode));
7441024de70SMateusz Guzik }
745931e2a1aSEd Maste #endif
746d66a5066SPeter Wemm 
747d66a5066SPeter Wemm int
linux_mkdirat(struct thread * td,struct linux_mkdirat_args * args)74848b05c3fSKonstantin Belousov linux_mkdirat(struct thread *td, struct linux_mkdirat_args *args)
74948b05c3fSKonstantin Belousov {
750fd745e1dSDmitry Chagin 	int dfd;
75148b05c3fSKonstantin Belousov 
75248b05c3fSKonstantin Belousov 	dfd = (args->dfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->dfd;
7531024de70SMateusz Guzik 	return (kern_mkdirat(td, dfd, args->pathname, UIO_USERSPACE, args->mode));
7541024de70SMateusz Guzik }
75548b05c3fSKonstantin Belousov 
756931e2a1aSEd Maste #ifdef LINUX_LEGACY_SYSCALLS
75748b05c3fSKonstantin Belousov int
linux_rmdir(struct thread * td,struct linux_rmdir_args * args)758b40ce416SJulian Elischer linux_rmdir(struct thread *td, struct linux_rmdir_args *args)
759d66a5066SPeter Wemm {
760d66a5066SPeter Wemm 
7611024de70SMateusz Guzik 	return (kern_frmdirat(td, AT_FDCWD, args->path, FD_NONE,
7621024de70SMateusz Guzik 	    UIO_USERSPACE, 0));
7631024de70SMateusz Guzik }
764d66a5066SPeter Wemm 
765d66a5066SPeter Wemm int
linux_rename(struct thread * td,struct linux_rename_args * args)766b40ce416SJulian Elischer linux_rename(struct thread *td, struct linux_rename_args *args)
767d66a5066SPeter Wemm {
768d66a5066SPeter Wemm 
7691024de70SMateusz Guzik 	return (kern_renameat(td, AT_FDCWD, args->from, AT_FDCWD,
7701024de70SMateusz Guzik 	    args->to, UIO_USERSPACE));
7711024de70SMateusz Guzik }
772931e2a1aSEd Maste #endif
773d66a5066SPeter Wemm 
774d66a5066SPeter Wemm int
linux_renameat(struct thread * td,struct linux_renameat_args * args)77548b05c3fSKonstantin Belousov linux_renameat(struct thread *td, struct linux_renameat_args *args)
77648b05c3fSKonstantin Belousov {
7772eb6ef20SEd Maste 	struct linux_renameat2_args renameat2_args = {
7782eb6ef20SEd Maste 	    .olddfd = args->olddfd,
7792eb6ef20SEd Maste 	    .oldname = args->oldname,
7802eb6ef20SEd Maste 	    .newdfd = args->newdfd,
7812eb6ef20SEd Maste 	    .newname = args->newname,
7822eb6ef20SEd Maste 	    .flags = 0
7832eb6ef20SEd Maste 	};
7842eb6ef20SEd Maste 
7852eb6ef20SEd Maste 	return (linux_renameat2(td, &renameat2_args));
7862eb6ef20SEd Maste }
7872eb6ef20SEd Maste 
7882eb6ef20SEd Maste int
linux_renameat2(struct thread * td,struct linux_renameat2_args * args)7892eb6ef20SEd Maste linux_renameat2(struct thread *td, struct linux_renameat2_args *args)
7902eb6ef20SEd Maste {
791fd745e1dSDmitry Chagin 	int olddfd, newdfd;
79248b05c3fSKonstantin Belousov 
7932eb6ef20SEd Maste 	if (args->flags != 0) {
79401b9ee4cSEd Maste 		if (args->flags & ~(LINUX_RENAME_EXCHANGE |
79501b9ee4cSEd Maste 		    LINUX_RENAME_NOREPLACE | LINUX_RENAME_WHITEOUT))
79601b9ee4cSEd Maste 			return (EINVAL);
79701b9ee4cSEd Maste 		if (args->flags & LINUX_RENAME_EXCHANGE &&
79801b9ee4cSEd Maste 		    args->flags & (LINUX_RENAME_NOREPLACE |
79901b9ee4cSEd Maste 		    LINUX_RENAME_WHITEOUT))
80001b9ee4cSEd Maste 			return (EINVAL);
801fe76bef4SMateusz Guzik #if 0
802fe76bef4SMateusz Guzik 		/*
803fe76bef4SMateusz Guzik 		 * This spams the console on Ubuntu Focal.
804fe76bef4SMateusz Guzik 		 *
805fe76bef4SMateusz Guzik 		 * What's needed here is a general mechanism to let users know
806fe76bef4SMateusz Guzik 		 * about missing features without hogging the system.
807fe76bef4SMateusz Guzik 		 */
808ea2609a4SPawel Biernacki 		linux_msg(td, "renameat2 unsupported flags 0x%x",
8092eb6ef20SEd Maste 		    args->flags);
810fe76bef4SMateusz Guzik #endif
8112eb6ef20SEd Maste 		return (EINVAL);
8122eb6ef20SEd Maste 	}
8132eb6ef20SEd Maste 
81448b05c3fSKonstantin Belousov 	olddfd = (args->olddfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->olddfd;
81548b05c3fSKonstantin Belousov 	newdfd = (args->newdfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->newdfd;
8161024de70SMateusz Guzik 	return (kern_renameat(td, olddfd, args->oldname, newdfd,
8171024de70SMateusz Guzik 	    args->newname, UIO_USERSPACE));
8181024de70SMateusz Guzik }
81948b05c3fSKonstantin Belousov 
820931e2a1aSEd Maste #ifdef LINUX_LEGACY_SYSCALLS
82148b05c3fSKonstantin Belousov int
linux_symlink(struct thread * td,struct linux_symlink_args * args)822b40ce416SJulian Elischer linux_symlink(struct thread *td, struct linux_symlink_args *args)
823d66a5066SPeter Wemm {
824d66a5066SPeter Wemm 
8251024de70SMateusz Guzik 	return (kern_symlinkat(td, args->path, AT_FDCWD, args->to,
8261024de70SMateusz Guzik 	    UIO_USERSPACE));
8271024de70SMateusz Guzik }
828931e2a1aSEd Maste #endif
829d66a5066SPeter Wemm 
830d66a5066SPeter Wemm int
linux_symlinkat(struct thread * td,struct linux_symlinkat_args * args)83148b05c3fSKonstantin Belousov linux_symlinkat(struct thread *td, struct linux_symlinkat_args *args)
83248b05c3fSKonstantin Belousov {
833fd745e1dSDmitry Chagin 	int dfd;
83448b05c3fSKonstantin Belousov 
83548b05c3fSKonstantin Belousov 	dfd = (args->newdfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->newdfd;
8361024de70SMateusz Guzik 	return (kern_symlinkat(td, args->oldname, dfd, args->newname,
8371024de70SMateusz Guzik 	    UIO_USERSPACE));
8381024de70SMateusz Guzik }
83948b05c3fSKonstantin Belousov 
840931e2a1aSEd Maste #ifdef LINUX_LEGACY_SYSCALLS
84148b05c3fSKonstantin Belousov int
linux_readlink(struct thread * td,struct linux_readlink_args * args)842b40ce416SJulian Elischer linux_readlink(struct thread *td, struct linux_readlink_args *args)
843d66a5066SPeter Wemm {
844d66a5066SPeter Wemm 
845d6ccce0aSDmitry Chagin 	if (args->count <= 0)
846d6ccce0aSDmitry Chagin 		return (EINVAL);
847d6ccce0aSDmitry Chagin 
8481024de70SMateusz Guzik 	return (kern_readlinkat(td, AT_FDCWD, args->name, UIO_USERSPACE,
8491024de70SMateusz Guzik 	    args->buf, UIO_USERSPACE, args->count));
8501024de70SMateusz Guzik }
851931e2a1aSEd Maste #endif
852d66a5066SPeter Wemm 
853d66a5066SPeter Wemm int
linux_readlinkat(struct thread * td,struct linux_readlinkat_args * args)85448b05c3fSKonstantin Belousov linux_readlinkat(struct thread *td, struct linux_readlinkat_args *args)
85548b05c3fSKonstantin Belousov {
856fd745e1dSDmitry Chagin 	int dfd;
85748b05c3fSKonstantin Belousov 
858d6ccce0aSDmitry Chagin 	if (args->bufsiz <= 0)
859d6ccce0aSDmitry Chagin 		return (EINVAL);
860d6ccce0aSDmitry Chagin 
86148b05c3fSKonstantin Belousov 	dfd = (args->dfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->dfd;
8621024de70SMateusz Guzik 	return (kern_readlinkat(td, dfd, args->path, UIO_USERSPACE,
8631024de70SMateusz Guzik 	    args->buf, UIO_USERSPACE, args->bufsiz));
8641024de70SMateusz Guzik }
865a6d043e3SRoman Divacky 
86648b05c3fSKonstantin Belousov int
linux_truncate(struct thread * td,struct linux_truncate_args * args)867b40ce416SJulian Elischer linux_truncate(struct thread *td, struct linux_truncate_args *args)
868d66a5066SPeter Wemm {
869d66a5066SPeter Wemm 
8701024de70SMateusz Guzik 	return (kern_truncate(td, args->path, UIO_USERSPACE, args->length));
8711024de70SMateusz Guzik }
872d66a5066SPeter Wemm 
8737f8f1d7fSDmitry Chagin #if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32))
87400d3c516SMarcel Moolenaar int
linux_truncate64(struct thread * td,struct linux_truncate64_args * args)875a6d043e3SRoman Divacky linux_truncate64(struct thread *td, struct linux_truncate64_args *args)
876a6d043e3SRoman Divacky {
877149afbf3SMark Johnston 	off_t length;
878a6d043e3SRoman Divacky 
879149afbf3SMark Johnston #if defined(__amd64__) && defined(COMPAT_LINUX32)
880149afbf3SMark Johnston 	length = PAIR32TO64(off_t, args->length);
881149afbf3SMark Johnston #else
882149afbf3SMark Johnston 	length = args->length;
883149afbf3SMark Johnston #endif
884a6d043e3SRoman Divacky 
8851024de70SMateusz Guzik 	return (kern_truncate(td, args->path, UIO_USERSPACE, length));
8861024de70SMateusz Guzik }
8877f8f1d7fSDmitry Chagin #endif /* __i386__ || (__amd64__ && COMPAT_LINUX32) */
8887f8f1d7fSDmitry Chagin 
889a6d043e3SRoman Divacky int
linux_ftruncate(struct thread * td,struct linux_ftruncate_args * args)8905c8919adSAlexander Leidinger linux_ftruncate(struct thread *td, struct linux_ftruncate_args *args)
8915c8919adSAlexander Leidinger {
8925c8919adSAlexander Leidinger 
893ae6b6ef6SEdward Tomasz Napierala 	return (kern_ftruncate(td, args->fd, args->length));
8945c8919adSAlexander Leidinger }
8955c8919adSAlexander Leidinger 
896149afbf3SMark Johnston #if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32))
897149afbf3SMark Johnston int
linux_ftruncate64(struct thread * td,struct linux_ftruncate64_args * args)898149afbf3SMark Johnston linux_ftruncate64(struct thread *td, struct linux_ftruncate64_args *args)
899149afbf3SMark Johnston {
900149afbf3SMark Johnston 	off_t length;
901149afbf3SMark Johnston 
902149afbf3SMark Johnston #if defined(__amd64__) && defined(COMPAT_LINUX32)
903149afbf3SMark Johnston 	length = PAIR32TO64(off_t, args->length);
904149afbf3SMark Johnston #else
905149afbf3SMark Johnston 	length = args->length;
906149afbf3SMark Johnston #endif
907149afbf3SMark Johnston 
908149afbf3SMark Johnston 	return (kern_ftruncate(td, args->fd, length));
909149afbf3SMark Johnston }
910149afbf3SMark Johnston #endif
911149afbf3SMark Johnston 
912931e2a1aSEd Maste #ifdef LINUX_LEGACY_SYSCALLS
9135c8919adSAlexander Leidinger int
linux_link(struct thread * td,struct linux_link_args * args)914b40ce416SJulian Elischer linux_link(struct thread *td, struct linux_link_args *args)
91500d3c516SMarcel Moolenaar {
91600d3c516SMarcel Moolenaar 
9171024de70SMateusz Guzik 	return (kern_linkat(td, AT_FDCWD, AT_FDCWD, args->path, args->to,
9185d1d844aSEdward Tomasz Napierala 	    UIO_USERSPACE, AT_SYMLINK_FOLLOW));
9191024de70SMateusz Guzik }
920931e2a1aSEd Maste #endif
92181d960faSMarcel Moolenaar 
92225e5bdabSMarcel Moolenaar int
linux_linkat(struct thread * td,struct linux_linkat_args * args)92348b05c3fSKonstantin Belousov linux_linkat(struct thread *td, struct linux_linkat_args *args)
92448b05c3fSKonstantin Belousov {
925fd745e1dSDmitry Chagin 	int olddfd, newdfd, flag;
92648b05c3fSKonstantin Belousov 
927ee384b22SEdward Tomasz Napierala 	if (args->flag & ~(LINUX_AT_SYMLINK_FOLLOW | LINUX_AT_EMPTY_PATH))
92848b05c3fSKonstantin Belousov 		return (EINVAL);
92948b05c3fSKonstantin Belousov 
930a8d88529SJohn Baldwin 	flag = (args->flag & LINUX_AT_SYMLINK_FOLLOW) != 0 ? AT_SYMLINK_FOLLOW :
9315d1d844aSEdward Tomasz Napierala 	    0;
932a8d88529SJohn Baldwin 	flag |= (args->flag & LINUX_AT_EMPTY_PATH) != 0 ? AT_EMPTY_PATH : 0;
9335d1d844aSEdward Tomasz Napierala 
93448b05c3fSKonstantin Belousov 	olddfd = (args->olddfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->olddfd;
93548b05c3fSKonstantin Belousov 	newdfd = (args->newdfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->newdfd;
9361024de70SMateusz Guzik 	return (kern_linkat(td, olddfd, newdfd, args->oldname,
9375d1d844aSEdward Tomasz Napierala 	    args->newname, UIO_USERSPACE, flag));
9381024de70SMateusz Guzik }
93948b05c3fSKonstantin Belousov 
94048b05c3fSKonstantin Belousov int
linux_fdatasync(struct thread * td,struct linux_fdatasync_args * uap)941518cce02SEdward Tomasz Napierala linux_fdatasync(struct thread *td, struct linux_fdatasync_args *uap)
94225e5bdabSMarcel Moolenaar {
94325e5bdabSMarcel Moolenaar 
94493d9ebd8SEd Schouten 	return (kern_fsync(td, uap->fd, false));
94525e5bdabSMarcel Moolenaar }
946d5124417SMarcel Moolenaar 
947d5124417SMarcel Moolenaar int
linux_sync_file_range(struct thread * td,struct linux_sync_file_range_args * uap)948518cce02SEdward Tomasz Napierala linux_sync_file_range(struct thread *td, struct linux_sync_file_range_args *uap)
949cf69fe66SEdward Tomasz Napierala {
950149afbf3SMark Johnston 	off_t nbytes, offset;
951cf69fe66SEdward Tomasz Napierala 
952149afbf3SMark Johnston #if defined(__amd64__) && defined(COMPAT_LINUX32)
953149afbf3SMark Johnston 	nbytes = PAIR32TO64(off_t, uap->nbytes);
954149afbf3SMark Johnston 	offset = PAIR32TO64(off_t, uap->offset);
955149afbf3SMark Johnston #else
956149afbf3SMark Johnston 	nbytes = uap->nbytes;
957149afbf3SMark Johnston 	offset = uap->offset;
958149afbf3SMark Johnston #endif
959149afbf3SMark Johnston 
960149afbf3SMark Johnston 	if (offset < 0 || nbytes < 0 ||
961cf69fe66SEdward Tomasz Napierala 	    (uap->flags & ~(LINUX_SYNC_FILE_RANGE_WAIT_BEFORE |
962cf69fe66SEdward Tomasz Napierala 	    LINUX_SYNC_FILE_RANGE_WRITE |
963cf69fe66SEdward Tomasz Napierala 	    LINUX_SYNC_FILE_RANGE_WAIT_AFTER)) != 0) {
964cf69fe66SEdward Tomasz Napierala 		return (EINVAL);
965cf69fe66SEdward Tomasz Napierala 	}
966cf69fe66SEdward Tomasz Napierala 
967cf69fe66SEdward Tomasz Napierala 	return (kern_fsync(td, uap->fd, false));
968cf69fe66SEdward Tomasz Napierala }
969cf69fe66SEdward Tomasz Napierala 
970cf69fe66SEdward Tomasz Napierala int
linux_pread(struct thread * td,struct linux_pread_args * uap)971b38b22b0SEdward Tomasz Napierala linux_pread(struct thread *td, struct linux_pread_args *uap)
972d5124417SMarcel Moolenaar {
973d4b7423fSAlexander Leidinger 	struct vnode *vp;
974149afbf3SMark Johnston 	off_t offset;
975d4b7423fSAlexander Leidinger 	int error;
976d5124417SMarcel Moolenaar 
977149afbf3SMark Johnston #if defined(__amd64__) && defined(COMPAT_LINUX32)
978149afbf3SMark Johnston 	offset = PAIR32TO64(off_t, uap->offset);
979149afbf3SMark Johnston #else
980149afbf3SMark Johnston 	offset = uap->offset;
981149afbf3SMark Johnston #endif
982149afbf3SMark Johnston 
983149afbf3SMark Johnston 	error = kern_pread(td, uap->fd, uap->buf, uap->nbyte, offset);
984d4b7423fSAlexander Leidinger 	if (error == 0) {
985eae594f7SEd Maste 		/* This seems to violate POSIX but Linux does it. */
986cbd92ce6SMatt Macy 		error = fgetvp(td, uap->fd, &cap_pread_rights, &vp);
9877008be5bSPawel Jakub Dawidek 		if (error != 0)
988d4b7423fSAlexander Leidinger 			return (error);
989149afbf3SMark Johnston 		if (vp->v_type == VDIR)
990149afbf3SMark Johnston 			error = EISDIR;
991d4b7423fSAlexander Leidinger 		vrele(vp);
992d4b7423fSAlexander Leidinger 	}
993d4b7423fSAlexander Leidinger 	return (error);
994d5124417SMarcel Moolenaar }
995d5124417SMarcel Moolenaar 
996d5124417SMarcel Moolenaar int
linux_pwrite(struct thread * td,struct linux_pwrite_args * uap)997b38b22b0SEdward Tomasz Napierala linux_pwrite(struct thread *td, struct linux_pwrite_args *uap)
998d5124417SMarcel Moolenaar {
999149afbf3SMark Johnston 	off_t offset;
1000d5124417SMarcel Moolenaar 
1001149afbf3SMark Johnston #if defined(__amd64__) && defined(COMPAT_LINUX32)
1002149afbf3SMark Johnston 	offset = PAIR32TO64(off_t, uap->offset);
1003149afbf3SMark Johnston #else
1004149afbf3SMark Johnston 	offset = uap->offset;
1005149afbf3SMark Johnston #endif
1006149afbf3SMark Johnston 
1007d6cb9e72SDmitry Chagin 	return (linux_enobufs2eagain(td, uap->fd,
1008d6cb9e72SDmitry Chagin 	    kern_pwrite(td, uap->fd, uap->buf, uap->nbyte, offset)));
1009d5124417SMarcel Moolenaar }
1010705deb78SJonathan Lemon 
1011b1744628SDmitry Chagin #define HALF_LONG_BITS ((sizeof(l_long) * NBBY / 2))
1012b1744628SDmitry Chagin 
1013b1744628SDmitry Chagin static inline off_t
pos_from_hilo(unsigned long high,unsigned long low)1014b1744628SDmitry Chagin pos_from_hilo(unsigned long high, unsigned long low)
1015b1744628SDmitry Chagin {
1016b1744628SDmitry Chagin 
1017b1744628SDmitry Chagin 	return (((off_t)high << HALF_LONG_BITS) << HALF_LONG_BITS) | low;
1018b1744628SDmitry Chagin }
1019b1744628SDmitry Chagin 
1020705deb78SJonathan Lemon int
linux_preadv(struct thread * td,struct linux_preadv_args * uap)1021bd911530SMahdi Mokhtari linux_preadv(struct thread *td, struct linux_preadv_args *uap)
1022bd911530SMahdi Mokhtari {
1023bd911530SMahdi Mokhtari 	struct uio *auio;
1024bd911530SMahdi Mokhtari 	int error;
1025bd911530SMahdi Mokhtari 	off_t offset;
1026bd911530SMahdi Mokhtari 
1027bd911530SMahdi Mokhtari 	/*
1028bd911530SMahdi Mokhtari 	 * According http://man7.org/linux/man-pages/man2/preadv.2.html#NOTES
1029bd911530SMahdi Mokhtari 	 * pos_l and pos_h, respectively, contain the
1030bd911530SMahdi Mokhtari 	 * low order and high order 32 bits of offset.
1031bd911530SMahdi Mokhtari 	 */
1032b1744628SDmitry Chagin 	offset = pos_from_hilo(uap->pos_h, uap->pos_l);
1033bd911530SMahdi Mokhtari 	if (offset < 0)
1034bd911530SMahdi Mokhtari 		return (EINVAL);
1035bd911530SMahdi Mokhtari #ifdef COMPAT_LINUX32
1036c987ff4dSDmitry Chagin 	error = freebsd32_copyinuio(PTRIN(uap->vec), uap->vlen, &auio);
1037bd911530SMahdi Mokhtari #else
1038bd911530SMahdi Mokhtari 	error = copyinuio(uap->vec, uap->vlen, &auio);
1039bd911530SMahdi Mokhtari #endif
1040bd911530SMahdi Mokhtari 	if (error != 0)
1041bd911530SMahdi Mokhtari 		return (error);
1042bd911530SMahdi Mokhtari 	error = kern_preadv(td, uap->fd, auio, offset);
104361cc4830SAlfredo Mazzinghi 	freeuio(auio);
1044bd911530SMahdi Mokhtari 	return (error);
1045bd911530SMahdi Mokhtari }
1046bd911530SMahdi Mokhtari 
1047bd911530SMahdi Mokhtari int
linux_pwritev(struct thread * td,struct linux_pwritev_args * uap)1048bd911530SMahdi Mokhtari linux_pwritev(struct thread *td, struct linux_pwritev_args *uap)
1049bd911530SMahdi Mokhtari {
1050bd911530SMahdi Mokhtari 	struct uio *auio;
1051bd911530SMahdi Mokhtari 	int error;
1052bd911530SMahdi Mokhtari 	off_t offset;
1053bd911530SMahdi Mokhtari 
1054bd911530SMahdi Mokhtari 	/*
1055bd911530SMahdi Mokhtari 	 * According http://man7.org/linux/man-pages/man2/pwritev.2.html#NOTES
1056bd911530SMahdi Mokhtari 	 * pos_l and pos_h, respectively, contain the
1057bd911530SMahdi Mokhtari 	 * low order and high order 32 bits of offset.
1058bd911530SMahdi Mokhtari 	 */
1059b1744628SDmitry Chagin 	offset = pos_from_hilo(uap->pos_h, uap->pos_l);
1060bd911530SMahdi Mokhtari 	if (offset < 0)
1061bd911530SMahdi Mokhtari 		return (EINVAL);
1062bd911530SMahdi Mokhtari #ifdef COMPAT_LINUX32
1063c987ff4dSDmitry Chagin 	error = freebsd32_copyinuio(PTRIN(uap->vec), uap->vlen, &auio);
1064bd911530SMahdi Mokhtari #else
1065bd911530SMahdi Mokhtari 	error = copyinuio(uap->vec, uap->vlen, &auio);
1066bd911530SMahdi Mokhtari #endif
1067bd911530SMahdi Mokhtari 	if (error != 0)
1068bd911530SMahdi Mokhtari 		return (error);
1069bd911530SMahdi Mokhtari 	error = kern_pwritev(td, uap->fd, auio, offset);
107061cc4830SAlfredo Mazzinghi 	freeuio(auio);
1071dfbb3e2aSDmitry Chagin 	return (linux_enobufs2eagain(td, uap->fd, error));
1072bd911530SMahdi Mokhtari }
1073bd911530SMahdi Mokhtari 
1074bd911530SMahdi Mokhtari int
linux_mount(struct thread * td,struct linux_mount_args * args)1075b40ce416SJulian Elischer linux_mount(struct thread *td, struct linux_mount_args *args)
1076705deb78SJonathan Lemon {
1077e3b1c847SEdward Tomasz Napierala 	struct mntarg *ma = NULL;
1078e3b1c847SEdward Tomasz Napierala 	char *fstypename, *mntonname, *mntfromname, *data;
107969921123SKonstantin Belousov 	int error, fsflags;
1080705deb78SJonathan Lemon 
1081e3b1c847SEdward Tomasz Napierala 	fstypename = malloc(MNAMELEN, M_TEMP, M_WAITOK);
108269921123SKonstantin Belousov 	mntonname = malloc(MNAMELEN, M_TEMP, M_WAITOK);
108369921123SKonstantin Belousov 	mntfromname = malloc(MNAMELEN, M_TEMP, M_WAITOK);
1084e3b1c847SEdward Tomasz Napierala 	data = NULL;
1085e3b1c847SEdward Tomasz Napierala 	error = copyinstr(args->filesystemtype, fstypename, MNAMELEN - 1,
1086f3a90da9SAdrian Chadd 	    NULL);
108769921123SKonstantin Belousov 	if (error != 0)
108869921123SKonstantin Belousov 		goto out;
1089135b7238SEdward Tomasz Napierala 	if (args->specialfile != NULL) {
10902ba9b766STim J. Robbins 		error = copyinstr(args->specialfile, mntfromname, MNAMELEN - 1, NULL);
109169921123SKonstantin Belousov 		if (error != 0)
109269921123SKonstantin Belousov 			goto out;
1093135b7238SEdward Tomasz Napierala 	} else {
1094135b7238SEdward Tomasz Napierala 		mntfromname[0] = '\0';
1095135b7238SEdward Tomasz Napierala 	}
10962ba9b766STim J. Robbins 	error = copyinstr(args->dir, mntonname, MNAMELEN - 1, NULL);
109769921123SKonstantin Belousov 	if (error != 0)
109869921123SKonstantin Belousov 		goto out;
1099705deb78SJonathan Lemon 
1100705deb78SJonathan Lemon 	if (strcmp(fstypename, "ext2") == 0) {
11017b0d0172STim J. Robbins 		strcpy(fstypename, "ext2fs");
1102705deb78SJonathan Lemon 	} else if (strcmp(fstypename, "proc") == 0) {
11037b0d0172STim J. Robbins 		strcpy(fstypename, "linprocfs");
1104d2b2128aSDoug Ambrisko 	} else if (strcmp(fstypename, "vfat") == 0) {
1105d2b2128aSDoug Ambrisko 		strcpy(fstypename, "msdosfs");
1106128a1db8SEdward Tomasz Napierala 	} else if (strcmp(fstypename, "fuse") == 0 ||
1107128a1db8SEdward Tomasz Napierala 	    strncmp(fstypename, "fuse.", 5) == 0) {
1108e3b1c847SEdward Tomasz Napierala 		char *fuse_options, *fuse_option, *fuse_name;
1109e3b1c847SEdward Tomasz Napierala 
1110e3b1c847SEdward Tomasz Napierala 		strcpy(mntfromname, "/dev/fuse");
1111e3b1c847SEdward Tomasz Napierala 		strcpy(fstypename, "fusefs");
1112e3b1c847SEdward Tomasz Napierala 		data = malloc(MNAMELEN, M_TEMP, M_WAITOK);
1113e3b1c847SEdward Tomasz Napierala 		error = copyinstr(args->data, data, MNAMELEN - 1, NULL);
1114e3b1c847SEdward Tomasz Napierala 		if (error != 0)
1115e3b1c847SEdward Tomasz Napierala 			goto out;
1116e3b1c847SEdward Tomasz Napierala 
1117e3b1c847SEdward Tomasz Napierala 		fuse_options = data;
1118e3b1c847SEdward Tomasz Napierala 		while ((fuse_option = strsep(&fuse_options, ",")) != NULL) {
1119e3b1c847SEdward Tomasz Napierala 			fuse_name = strsep(&fuse_option, "=");
1120e3b1c847SEdward Tomasz Napierala 			if (fuse_name == NULL || fuse_option == NULL)
1121e3b1c847SEdward Tomasz Napierala 				goto out;
1122e3b1c847SEdward Tomasz Napierala 			ma = mount_arg(ma, fuse_name, fuse_option, -1);
1123e3b1c847SEdward Tomasz Napierala 		}
1124e3b1c847SEdward Tomasz Napierala 
1125e3b1c847SEdward Tomasz Napierala 		/*
1126e3b1c847SEdward Tomasz Napierala 		 * The FUSE server uses Linux errno values instead of FreeBSD
1127e3b1c847SEdward Tomasz Napierala 		 * ones; add a flag to tell fuse(4) to do errno translation.
1128e3b1c847SEdward Tomasz Napierala 		 */
1129e3b1c847SEdward Tomasz Napierala 		ma = mount_arg(ma, "linux_errnos", "1", -1);
1130705deb78SJonathan Lemon 	}
1131705deb78SJonathan Lemon 
1132f3a90da9SAdrian Chadd 	fsflags = 0;
1133705deb78SJonathan Lemon 
1134705deb78SJonathan Lemon 	/*
1135705deb78SJonathan Lemon 	 * Linux SYNC flag is not included; the closest equivalent
1136705deb78SJonathan Lemon 	 * FreeBSD has is !ASYNC, which is our default.
1137705deb78SJonathan Lemon 	 */
1138705deb78SJonathan Lemon 	if (args->rwflag & LINUX_MS_RDONLY)
1139f3a90da9SAdrian Chadd 		fsflags |= MNT_RDONLY;
1140705deb78SJonathan Lemon 	if (args->rwflag & LINUX_MS_NOSUID)
1141f3a90da9SAdrian Chadd 		fsflags |= MNT_NOSUID;
1142705deb78SJonathan Lemon 	if (args->rwflag & LINUX_MS_NOEXEC)
1143f3a90da9SAdrian Chadd 		fsflags |= MNT_NOEXEC;
1144705deb78SJonathan Lemon 	if (args->rwflag & LINUX_MS_REMOUNT)
1145f3a90da9SAdrian Chadd 		fsflags |= MNT_UPDATE;
1146705deb78SJonathan Lemon 
1147e3b1c847SEdward Tomasz Napierala 	ma = mount_arg(ma, "fstype", fstypename, -1);
1148e3b1c847SEdward Tomasz Napierala 	ma = mount_arg(ma, "fspath", mntonname, -1);
1149e3b1c847SEdward Tomasz Napierala 	ma = mount_arg(ma, "from", mntfromname, -1);
1150e3b1c847SEdward Tomasz Napierala 	error = kernel_mount(ma, fsflags);
115169921123SKonstantin Belousov out:
1152e3b1c847SEdward Tomasz Napierala 	free(fstypename, M_TEMP);
115369921123SKonstantin Belousov 	free(mntonname, M_TEMP);
115469921123SKonstantin Belousov 	free(mntfromname, M_TEMP);
1155e3b1c847SEdward Tomasz Napierala 	free(data, M_TEMP);
11567b0d0172STim J. Robbins 	return (error);
1157705deb78SJonathan Lemon }
1158705deb78SJonathan Lemon 
11597f8f1d7fSDmitry Chagin #if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32))
1160705deb78SJonathan Lemon int
linux_oldumount(struct thread * td,struct linux_oldumount_args * args)1161b40ce416SJulian Elischer linux_oldumount(struct thread *td, struct linux_oldumount_args *args)
1162705deb78SJonathan Lemon {
1163705deb78SJonathan Lemon 
1164b3fb13ebSEdward Tomasz Napierala 	return (kern_unmount(td, args->path, 0));
1165705deb78SJonathan Lemon }
11667f8f1d7fSDmitry Chagin #endif /* __i386__ || (__amd64__ && COMPAT_LINUX32) */
1167705deb78SJonathan Lemon 
1168931e2a1aSEd Maste #ifdef LINUX_LEGACY_SYSCALLS
1169705deb78SJonathan Lemon int
linux_umount(struct thread * td,struct linux_umount_args * args)1170b40ce416SJulian Elischer linux_umount(struct thread *td, struct linux_umount_args *args)
1171705deb78SJonathan Lemon {
117266632fe7SEdward Tomasz Napierala 	int flags;
117366632fe7SEdward Tomasz Napierala 
117466632fe7SEdward Tomasz Napierala 	flags = 0;
1175b3fb13ebSEdward Tomasz Napierala 	if ((args->flags & LINUX_MNT_FORCE) != 0) {
1176b3fb13ebSEdward Tomasz Napierala 		args->flags &= ~LINUX_MNT_FORCE;
117766632fe7SEdward Tomasz Napierala 		flags |= MNT_FORCE;
1178b3fb13ebSEdward Tomasz Napierala 	}
1179b3fb13ebSEdward Tomasz Napierala 	if (args->flags != 0) {
1180b3fb13ebSEdward Tomasz Napierala 		linux_msg(td, "unsupported umount2 flags %#x", args->flags);
1181b3fb13ebSEdward Tomasz Napierala 		return (EINVAL);
1182b3fb13ebSEdward Tomasz Napierala 	}
1183705deb78SJonathan Lemon 
1184b3fb13ebSEdward Tomasz Napierala 	return (kern_unmount(td, args->path, flags));
1185705deb78SJonathan Lemon }
1186931e2a1aSEd Maste #endif
11875002a60fSMarcel Moolenaar 
11885002a60fSMarcel Moolenaar /*
11895002a60fSMarcel Moolenaar  * fcntl family of syscalls
11905002a60fSMarcel Moolenaar  */
11915002a60fSMarcel Moolenaar 
11925002a60fSMarcel Moolenaar struct l_flock {
11935002a60fSMarcel Moolenaar 	l_short		l_type;
11945002a60fSMarcel Moolenaar 	l_short		l_whence;
11955002a60fSMarcel Moolenaar 	l_off_t		l_start;
11965002a60fSMarcel Moolenaar 	l_off_t		l_len;
11975002a60fSMarcel Moolenaar 	l_pid_t		l_pid;
11984af27623STim J. Robbins }
11991997c537SDavid E. O'Brien #if defined(__amd64__) && defined(COMPAT_LINUX32)
12004af27623STim J. Robbins __packed
12014af27623STim J. Robbins #endif
12024af27623STim J. Robbins ;
12035002a60fSMarcel Moolenaar 
12045002a60fSMarcel Moolenaar static void
linux_to_bsd_flock(struct l_flock * linux_flock,struct flock * bsd_flock)12055002a60fSMarcel Moolenaar linux_to_bsd_flock(struct l_flock *linux_flock, struct flock *bsd_flock)
12065002a60fSMarcel Moolenaar {
12075002a60fSMarcel Moolenaar 	switch (linux_flock->l_type) {
12085002a60fSMarcel Moolenaar 	case LINUX_F_RDLCK:
12095002a60fSMarcel Moolenaar 		bsd_flock->l_type = F_RDLCK;
12105002a60fSMarcel Moolenaar 		break;
12115002a60fSMarcel Moolenaar 	case LINUX_F_WRLCK:
12125002a60fSMarcel Moolenaar 		bsd_flock->l_type = F_WRLCK;
12135002a60fSMarcel Moolenaar 		break;
12145002a60fSMarcel Moolenaar 	case LINUX_F_UNLCK:
12155002a60fSMarcel Moolenaar 		bsd_flock->l_type = F_UNLCK;
12165002a60fSMarcel Moolenaar 		break;
12175002a60fSMarcel Moolenaar 	default:
12185002a60fSMarcel Moolenaar 		bsd_flock->l_type = -1;
12195002a60fSMarcel Moolenaar 		break;
12205002a60fSMarcel Moolenaar 	}
12215002a60fSMarcel Moolenaar 	bsd_flock->l_whence = linux_flock->l_whence;
12225002a60fSMarcel Moolenaar 	bsd_flock->l_start = (off_t)linux_flock->l_start;
12235002a60fSMarcel Moolenaar 	bsd_flock->l_len = (off_t)linux_flock->l_len;
12245002a60fSMarcel Moolenaar 	bsd_flock->l_pid = (pid_t)linux_flock->l_pid;
1225dfdcada3SDoug Rabson 	bsd_flock->l_sysid = 0;
12265002a60fSMarcel Moolenaar }
12275002a60fSMarcel Moolenaar 
12285002a60fSMarcel Moolenaar static void
bsd_to_linux_flock(struct flock * bsd_flock,struct l_flock * linux_flock)12295002a60fSMarcel Moolenaar bsd_to_linux_flock(struct flock *bsd_flock, struct l_flock *linux_flock)
12305002a60fSMarcel Moolenaar {
12315002a60fSMarcel Moolenaar 	switch (bsd_flock->l_type) {
12325002a60fSMarcel Moolenaar 	case F_RDLCK:
12335002a60fSMarcel Moolenaar 		linux_flock->l_type = LINUX_F_RDLCK;
12345002a60fSMarcel Moolenaar 		break;
12355002a60fSMarcel Moolenaar 	case F_WRLCK:
12365002a60fSMarcel Moolenaar 		linux_flock->l_type = LINUX_F_WRLCK;
12375002a60fSMarcel Moolenaar 		break;
12385002a60fSMarcel Moolenaar 	case F_UNLCK:
12395002a60fSMarcel Moolenaar 		linux_flock->l_type = LINUX_F_UNLCK;
12405002a60fSMarcel Moolenaar 		break;
12415002a60fSMarcel Moolenaar 	}
12425002a60fSMarcel Moolenaar 	linux_flock->l_whence = bsd_flock->l_whence;
12435002a60fSMarcel Moolenaar 	linux_flock->l_start = (l_off_t)bsd_flock->l_start;
12445002a60fSMarcel Moolenaar 	linux_flock->l_len = (l_off_t)bsd_flock->l_len;
12455002a60fSMarcel Moolenaar 	linux_flock->l_pid = (l_pid_t)bsd_flock->l_pid;
12465002a60fSMarcel Moolenaar }
12475002a60fSMarcel Moolenaar 
12481997c537SDavid E. O'Brien #if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32))
12495002a60fSMarcel Moolenaar struct l_flock64 {
12505002a60fSMarcel Moolenaar 	l_short		l_type;
12515002a60fSMarcel Moolenaar 	l_short		l_whence;
12525002a60fSMarcel Moolenaar 	l_loff_t	l_start;
12535002a60fSMarcel Moolenaar 	l_loff_t	l_len;
12545002a60fSMarcel Moolenaar 	l_pid_t		l_pid;
12554af27623STim J. Robbins }
12561997c537SDavid E. O'Brien #if defined(__amd64__) && defined(COMPAT_LINUX32)
12574af27623STim J. Robbins __packed
12584af27623STim J. Robbins #endif
12594af27623STim J. Robbins ;
12605002a60fSMarcel Moolenaar 
12615002a60fSMarcel Moolenaar static void
linux_to_bsd_flock64(struct l_flock64 * linux_flock,struct flock * bsd_flock)12625002a60fSMarcel Moolenaar linux_to_bsd_flock64(struct l_flock64 *linux_flock, struct flock *bsd_flock)
12635002a60fSMarcel Moolenaar {
12645002a60fSMarcel Moolenaar 	switch (linux_flock->l_type) {
12655002a60fSMarcel Moolenaar 	case LINUX_F_RDLCK:
12665002a60fSMarcel Moolenaar 		bsd_flock->l_type = F_RDLCK;
12675002a60fSMarcel Moolenaar 		break;
12685002a60fSMarcel Moolenaar 	case LINUX_F_WRLCK:
12695002a60fSMarcel Moolenaar 		bsd_flock->l_type = F_WRLCK;
12705002a60fSMarcel Moolenaar 		break;
12715002a60fSMarcel Moolenaar 	case LINUX_F_UNLCK:
12725002a60fSMarcel Moolenaar 		bsd_flock->l_type = F_UNLCK;
12735002a60fSMarcel Moolenaar 		break;
12745002a60fSMarcel Moolenaar 	default:
12755002a60fSMarcel Moolenaar 		bsd_flock->l_type = -1;
12765002a60fSMarcel Moolenaar 		break;
12775002a60fSMarcel Moolenaar 	}
12785002a60fSMarcel Moolenaar 	bsd_flock->l_whence = linux_flock->l_whence;
12795002a60fSMarcel Moolenaar 	bsd_flock->l_start = (off_t)linux_flock->l_start;
12805002a60fSMarcel Moolenaar 	bsd_flock->l_len = (off_t)linux_flock->l_len;
12815002a60fSMarcel Moolenaar 	bsd_flock->l_pid = (pid_t)linux_flock->l_pid;
1282dfdcada3SDoug Rabson 	bsd_flock->l_sysid = 0;
12835002a60fSMarcel Moolenaar }
12845002a60fSMarcel Moolenaar 
12855002a60fSMarcel Moolenaar static void
bsd_to_linux_flock64(struct flock * bsd_flock,struct l_flock64 * linux_flock)12865002a60fSMarcel Moolenaar bsd_to_linux_flock64(struct flock *bsd_flock, struct l_flock64 *linux_flock)
12875002a60fSMarcel Moolenaar {
12885002a60fSMarcel Moolenaar 	switch (bsd_flock->l_type) {
12895002a60fSMarcel Moolenaar 	case F_RDLCK:
12905002a60fSMarcel Moolenaar 		linux_flock->l_type = LINUX_F_RDLCK;
12915002a60fSMarcel Moolenaar 		break;
12925002a60fSMarcel Moolenaar 	case F_WRLCK:
12935002a60fSMarcel Moolenaar 		linux_flock->l_type = LINUX_F_WRLCK;
12945002a60fSMarcel Moolenaar 		break;
12955002a60fSMarcel Moolenaar 	case F_UNLCK:
12965002a60fSMarcel Moolenaar 		linux_flock->l_type = LINUX_F_UNLCK;
12975002a60fSMarcel Moolenaar 		break;
12985002a60fSMarcel Moolenaar 	}
12995002a60fSMarcel Moolenaar 	linux_flock->l_whence = bsd_flock->l_whence;
13005002a60fSMarcel Moolenaar 	linux_flock->l_start = (l_loff_t)bsd_flock->l_start;
13015002a60fSMarcel Moolenaar 	linux_flock->l_len = (l_loff_t)bsd_flock->l_len;
13025002a60fSMarcel Moolenaar 	linux_flock->l_pid = (l_pid_t)bsd_flock->l_pid;
13035002a60fSMarcel Moolenaar }
13044af27623STim J. Robbins #endif /* __i386__ || (__amd64__ && COMPAT_LINUX32) */
13055002a60fSMarcel Moolenaar 
13065002a60fSMarcel Moolenaar static int
fcntl_common(struct thread * td,struct linux_fcntl_args * args)13077f8f1d7fSDmitry Chagin fcntl_common(struct thread *td, struct linux_fcntl_args *args)
13085002a60fSMarcel Moolenaar {
130942d5b03cSIan Dowse 	struct l_flock linux_flock;
131042d5b03cSIan Dowse 	struct flock bsd_flock;
1311a06c1246SDmitry Chagin 	struct pipe *fpipe;
13125002a60fSMarcel Moolenaar 	struct file *fp;
1313c9400e18SIan Dowse 	long arg;
13145002a60fSMarcel Moolenaar 	int error, result;
13155002a60fSMarcel Moolenaar 
13165002a60fSMarcel Moolenaar 	switch (args->cmd) {
13175002a60fSMarcel Moolenaar 	case LINUX_F_DUPFD:
1318c9400e18SIan Dowse 		return (kern_fcntl(td, args->fd, F_DUPFD, args->arg));
13195002a60fSMarcel Moolenaar 
13205002a60fSMarcel Moolenaar 	case LINUX_F_GETFD:
1321c9400e18SIan Dowse 		return (kern_fcntl(td, args->fd, F_GETFD, 0));
13225002a60fSMarcel Moolenaar 
13235002a60fSMarcel Moolenaar 	case LINUX_F_SETFD:
1324c9400e18SIan Dowse 		return (kern_fcntl(td, args->fd, F_SETFD, args->arg));
13255002a60fSMarcel Moolenaar 
13265002a60fSMarcel Moolenaar 	case LINUX_F_GETFL:
1327c9400e18SIan Dowse 		error = kern_fcntl(td, args->fd, F_GETFL, 0);
1328b40ce416SJulian Elischer 		result = td->td_retval[0];
1329b40ce416SJulian Elischer 		td->td_retval[0] = 0;
13305002a60fSMarcel Moolenaar 		if (result & O_RDONLY)
1331b40ce416SJulian Elischer 			td->td_retval[0] |= LINUX_O_RDONLY;
13325002a60fSMarcel Moolenaar 		if (result & O_WRONLY)
1333b40ce416SJulian Elischer 			td->td_retval[0] |= LINUX_O_WRONLY;
13345002a60fSMarcel Moolenaar 		if (result & O_RDWR)
1335b40ce416SJulian Elischer 			td->td_retval[0] |= LINUX_O_RDWR;
13365002a60fSMarcel Moolenaar 		if (result & O_NDELAY)
1337b40ce416SJulian Elischer 			td->td_retval[0] |= LINUX_O_NONBLOCK;
13385002a60fSMarcel Moolenaar 		if (result & O_APPEND)
1339b40ce416SJulian Elischer 			td->td_retval[0] |= LINUX_O_APPEND;
13405002a60fSMarcel Moolenaar 		if (result & O_FSYNC)
1341b40ce416SJulian Elischer 			td->td_retval[0] |= LINUX_O_SYNC;
13425002a60fSMarcel Moolenaar 		if (result & O_ASYNC)
1343bc8e2810SEdward Tomasz Napierala 			td->td_retval[0] |= LINUX_O_ASYNC;
134473c730a6SMatthew N. Dodd #ifdef LINUX_O_NOFOLLOW
134573c730a6SMatthew N. Dodd 		if (result & O_NOFOLLOW)
134673c730a6SMatthew N. Dodd 			td->td_retval[0] |= LINUX_O_NOFOLLOW;
134773c730a6SMatthew N. Dodd #endif
134873c730a6SMatthew N. Dodd #ifdef LINUX_O_DIRECT
134973c730a6SMatthew N. Dodd 		if (result & O_DIRECT)
135073c730a6SMatthew N. Dodd 			td->td_retval[0] |= LINUX_O_DIRECT;
135173c730a6SMatthew N. Dodd #endif
13525002a60fSMarcel Moolenaar 		return (error);
13535002a60fSMarcel Moolenaar 
13545002a60fSMarcel Moolenaar 	case LINUX_F_SETFL:
1355c9400e18SIan Dowse 		arg = 0;
13565002a60fSMarcel Moolenaar 		if (args->arg & LINUX_O_NDELAY)
1357c9400e18SIan Dowse 			arg |= O_NONBLOCK;
13585002a60fSMarcel Moolenaar 		if (args->arg & LINUX_O_APPEND)
1359c9400e18SIan Dowse 			arg |= O_APPEND;
13605002a60fSMarcel Moolenaar 		if (args->arg & LINUX_O_SYNC)
1361c9400e18SIan Dowse 			arg |= O_FSYNC;
1362bc8e2810SEdward Tomasz Napierala 		if (args->arg & LINUX_O_ASYNC)
1363c9400e18SIan Dowse 			arg |= O_ASYNC;
136473c730a6SMatthew N. Dodd #ifdef LINUX_O_NOFOLLOW
136573c730a6SMatthew N. Dodd 		if (args->arg & LINUX_O_NOFOLLOW)
136673c730a6SMatthew N. Dodd 			arg |= O_NOFOLLOW;
136773c730a6SMatthew N. Dodd #endif
136873c730a6SMatthew N. Dodd #ifdef LINUX_O_DIRECT
136973c730a6SMatthew N. Dodd 		if (args->arg & LINUX_O_DIRECT)
137073c730a6SMatthew N. Dodd 			arg |= O_DIRECT;
137173c730a6SMatthew N. Dodd #endif
1372c9400e18SIan Dowse 		return (kern_fcntl(td, args->fd, F_SETFL, arg));
13735002a60fSMarcel Moolenaar 
13745002a60fSMarcel Moolenaar 	case LINUX_F_GETLK:
13754b7ef73dSDag-Erling Smørgrav 		error = copyin((void *)args->arg, &linux_flock,
13765002a60fSMarcel Moolenaar 		    sizeof(linux_flock));
13775002a60fSMarcel Moolenaar 		if (error)
13785002a60fSMarcel Moolenaar 			return (error);
1379c9400e18SIan Dowse 		linux_to_bsd_flock(&linux_flock, &bsd_flock);
1380c9400e18SIan Dowse 		error = kern_fcntl(td, args->fd, F_GETLK, (intptr_t)&bsd_flock);
13815002a60fSMarcel Moolenaar 		if (error)
13825002a60fSMarcel Moolenaar 			return (error);
1383c9400e18SIan Dowse 		bsd_to_linux_flock(&bsd_flock, &linux_flock);
13844b7ef73dSDag-Erling Smørgrav 		return (copyout(&linux_flock, (void *)args->arg,
13855002a60fSMarcel Moolenaar 		    sizeof(linux_flock)));
13865002a60fSMarcel Moolenaar 
13875002a60fSMarcel Moolenaar 	case LINUX_F_SETLK:
13884b7ef73dSDag-Erling Smørgrav 		error = copyin((void *)args->arg, &linux_flock,
13895002a60fSMarcel Moolenaar 		    sizeof(linux_flock));
13905002a60fSMarcel Moolenaar 		if (error)
13915002a60fSMarcel Moolenaar 			return (error);
1392c9400e18SIan Dowse 		linux_to_bsd_flock(&linux_flock, &bsd_flock);
1393c9400e18SIan Dowse 		return (kern_fcntl(td, args->fd, F_SETLK,
1394c9400e18SIan Dowse 		    (intptr_t)&bsd_flock));
13955002a60fSMarcel Moolenaar 
13965002a60fSMarcel Moolenaar 	case LINUX_F_SETLKW:
13974b7ef73dSDag-Erling Smørgrav 		error = copyin((void *)args->arg, &linux_flock,
13985002a60fSMarcel Moolenaar 		    sizeof(linux_flock));
13995002a60fSMarcel Moolenaar 		if (error)
14005002a60fSMarcel Moolenaar 			return (error);
1401c9400e18SIan Dowse 		linux_to_bsd_flock(&linux_flock, &bsd_flock);
1402c9400e18SIan Dowse 		return (kern_fcntl(td, args->fd, F_SETLKW,
1403c9400e18SIan Dowse 		     (intptr_t)&bsd_flock));
140442d5b03cSIan Dowse 
140542d5b03cSIan Dowse 	case LINUX_F_GETOWN:
140642d5b03cSIan Dowse 		return (kern_fcntl(td, args->fd, F_GETOWN, 0));
140742d5b03cSIan Dowse 
140842d5b03cSIan Dowse 	case LINUX_F_SETOWN:
140942d5b03cSIan Dowse 		/*
141042d5b03cSIan Dowse 		 * XXX some Linux applications depend on F_SETOWN having no
141142d5b03cSIan Dowse 		 * significant effect for pipes (SIGIO is not delivered for
141242d5b03cSIan Dowse 		 * pipes under Linux-2.2.35 at least).
141342d5b03cSIan Dowse 		 */
14147008be5bSPawel Jakub Dawidek 		error = fget(td, args->fd,
1415cbd92ce6SMatt Macy 		    &cap_fcntl_rights, &fp);
141642d5b03cSIan Dowse 		if (error)
141742d5b03cSIan Dowse 			return (error);
141842d5b03cSIan Dowse 		if (fp->f_type == DTYPE_PIPE) {
141942d5b03cSIan Dowse 			fdrop(fp, td);
142042d5b03cSIan Dowse 			return (EINVAL);
14215002a60fSMarcel Moolenaar 		}
142242d5b03cSIan Dowse 		fdrop(fp, td);
142342d5b03cSIan Dowse 
142442d5b03cSIan Dowse 		return (kern_fcntl(td, args->fd, F_SETOWN, args->arg));
1425d2b6dbc0SDmitry Chagin 
1426d2b6dbc0SDmitry Chagin 	case LINUX_F_DUPFD_CLOEXEC:
1427d2b6dbc0SDmitry Chagin 		return (kern_fcntl(td, args->fd, F_DUPFD_CLOEXEC, args->arg));
14285403f186SKyle Evans 	/*
14295403f186SKyle Evans 	 * Our F_SEAL_* values match Linux one for maximum compatibility.  So we
14305403f186SKyle Evans 	 * only needed to account for different values for fcntl(2) commands.
14315403f186SKyle Evans 	 */
14325403f186SKyle Evans 	case LINUX_F_GET_SEALS:
14335403f186SKyle Evans 		error = kern_fcntl(td, args->fd, F_GET_SEALS, 0);
14345403f186SKyle Evans 		if (error != 0)
14355403f186SKyle Evans 			return (error);
14365403f186SKyle Evans 		td->td_retval[0] = bsd_to_linux_bits(td->td_retval[0],
14375403f186SKyle Evans 		    seal_bitmap, 0);
14385403f186SKyle Evans 		return (0);
14395403f186SKyle Evans 
14405403f186SKyle Evans 	case LINUX_F_ADD_SEALS:
14415403f186SKyle Evans 		return (kern_fcntl(td, args->fd, F_ADD_SEALS,
14425403f186SKyle Evans 		    linux_to_bsd_bits(args->arg, seal_bitmap, 0)));
1443a06c1246SDmitry Chagin 
1444a06c1246SDmitry Chagin 	case LINUX_F_GETPIPE_SZ:
1445a06c1246SDmitry Chagin 		error = fget(td, args->fd,
1446a06c1246SDmitry Chagin 		    &cap_fcntl_rights, &fp);
1447a06c1246SDmitry Chagin 		if (error != 0)
1448a06c1246SDmitry Chagin 			return (error);
1449a06c1246SDmitry Chagin 		if (fp->f_type != DTYPE_PIPE) {
1450a06c1246SDmitry Chagin 			fdrop(fp, td);
1451a06c1246SDmitry Chagin 			return (EINVAL);
1452a06c1246SDmitry Chagin 		}
1453a06c1246SDmitry Chagin 		fpipe = fp->f_data;
1454a06c1246SDmitry Chagin 		td->td_retval[0] = fpipe->pipe_buffer.size;
1455a06c1246SDmitry Chagin 		fdrop(fp, td);
1456a06c1246SDmitry Chagin 		return (0);
1457a06c1246SDmitry Chagin 
145834ff0c0eSEdward Tomasz Napierala 	default:
14597d3310c4SEdward Tomasz Napierala 		linux_msg(td, "unsupported fcntl cmd %d", args->cmd);
146042d5b03cSIan Dowse 		return (EINVAL);
146142d5b03cSIan Dowse 	}
146234ff0c0eSEdward Tomasz Napierala }
146342d5b03cSIan Dowse 
146442d5b03cSIan Dowse int
linux_fcntl(struct thread * td,struct linux_fcntl_args * args)146542d5b03cSIan Dowse linux_fcntl(struct thread *td, struct linux_fcntl_args *args)
146642d5b03cSIan Dowse {
146742d5b03cSIan Dowse 
14687f8f1d7fSDmitry Chagin 	return (fcntl_common(td, args));
14695002a60fSMarcel Moolenaar }
14705002a60fSMarcel Moolenaar 
14711997c537SDavid E. O'Brien #if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32))
14725002a60fSMarcel Moolenaar int
linux_fcntl64(struct thread * td,struct linux_fcntl64_args * args)1473b40ce416SJulian Elischer linux_fcntl64(struct thread *td, struct linux_fcntl64_args *args)
14745002a60fSMarcel Moolenaar {
14755002a60fSMarcel Moolenaar 	struct l_flock64 linux_flock;
1476c9400e18SIan Dowse 	struct flock bsd_flock;
14777f8f1d7fSDmitry Chagin 	struct linux_fcntl_args fcntl_args;
14785002a60fSMarcel Moolenaar 	int error;
14795002a60fSMarcel Moolenaar 
14805002a60fSMarcel Moolenaar 	switch (args->cmd) {
1481ad32affeSRobert Drehmel 	case LINUX_F_GETLK64:
14824b7ef73dSDag-Erling Smørgrav 		error = copyin((void *)args->arg, &linux_flock,
14835002a60fSMarcel Moolenaar 		    sizeof(linux_flock));
14845002a60fSMarcel Moolenaar 		if (error)
14855002a60fSMarcel Moolenaar 			return (error);
1486c9400e18SIan Dowse 		linux_to_bsd_flock64(&linux_flock, &bsd_flock);
1487c9400e18SIan Dowse 		error = kern_fcntl(td, args->fd, F_GETLK, (intptr_t)&bsd_flock);
14885002a60fSMarcel Moolenaar 		if (error)
14895002a60fSMarcel Moolenaar 			return (error);
1490c9400e18SIan Dowse 		bsd_to_linux_flock64(&bsd_flock, &linux_flock);
14914b7ef73dSDag-Erling Smørgrav 		return (copyout(&linux_flock, (void *)args->arg,
14925002a60fSMarcel Moolenaar 			    sizeof(linux_flock)));
14935002a60fSMarcel Moolenaar 
1494ad32affeSRobert Drehmel 	case LINUX_F_SETLK64:
14954b7ef73dSDag-Erling Smørgrav 		error = copyin((void *)args->arg, &linux_flock,
14965002a60fSMarcel Moolenaar 		    sizeof(linux_flock));
14975002a60fSMarcel Moolenaar 		if (error)
14985002a60fSMarcel Moolenaar 			return (error);
1499c9400e18SIan Dowse 		linux_to_bsd_flock64(&linux_flock, &bsd_flock);
1500c9400e18SIan Dowse 		return (kern_fcntl(td, args->fd, F_SETLK,
1501c9400e18SIan Dowse 		    (intptr_t)&bsd_flock));
15025002a60fSMarcel Moolenaar 
1503ad32affeSRobert Drehmel 	case LINUX_F_SETLKW64:
15044b7ef73dSDag-Erling Smørgrav 		error = copyin((void *)args->arg, &linux_flock,
15055002a60fSMarcel Moolenaar 		    sizeof(linux_flock));
15065002a60fSMarcel Moolenaar 		if (error)
15075002a60fSMarcel Moolenaar 			return (error);
1508c9400e18SIan Dowse 		linux_to_bsd_flock64(&linux_flock, &bsd_flock);
1509c9400e18SIan Dowse 		return (kern_fcntl(td, args->fd, F_SETLKW,
1510c9400e18SIan Dowse 		    (intptr_t)&bsd_flock));
15115002a60fSMarcel Moolenaar 	}
15125002a60fSMarcel Moolenaar 
15137f8f1d7fSDmitry Chagin 	fcntl_args.fd = args->fd;
15147f8f1d7fSDmitry Chagin 	fcntl_args.cmd = args->cmd;
15157f8f1d7fSDmitry Chagin 	fcntl_args.arg = args->arg;
15167f8f1d7fSDmitry Chagin 	return (fcntl_common(td, &fcntl_args));
15175002a60fSMarcel Moolenaar }
15184af27623STim J. Robbins #endif /* __i386__ || (__amd64__ && COMPAT_LINUX32) */
15194c1e3817SMarcel Moolenaar 
1520931e2a1aSEd Maste #ifdef LINUX_LEGACY_SYSCALLS
15214c1e3817SMarcel Moolenaar int
linux_chown(struct thread * td,struct linux_chown_args * args)15224c1e3817SMarcel Moolenaar linux_chown(struct thread *td, struct linux_chown_args *args)
15234c1e3817SMarcel Moolenaar {
15244c1e3817SMarcel Moolenaar 
15251024de70SMateusz Guzik 	return (kern_fchownat(td, AT_FDCWD, args->path, UIO_USERSPACE,
15261024de70SMateusz Guzik 	    args->uid, args->gid, 0));
15271024de70SMateusz Guzik }
1528931e2a1aSEd Maste #endif
15294c1e3817SMarcel Moolenaar 
15304c1e3817SMarcel Moolenaar int
linux_fchownat(struct thread * td,struct linux_fchownat_args * args)153148b05c3fSKonstantin Belousov linux_fchownat(struct thread *td, struct linux_fchownat_args *args)
153248b05c3fSKonstantin Belousov {
1533fd745e1dSDmitry Chagin 	int dfd, flag, unsupported;
153448b05c3fSKonstantin Belousov 
15359d167945SEdward Tomasz Napierala 	unsupported = args->flag & ~(LINUX_AT_SYMLINK_NOFOLLOW | LINUX_AT_EMPTY_PATH);
15369d167945SEdward Tomasz Napierala 	if (unsupported != 0) {
15379d167945SEdward Tomasz Napierala 		linux_msg(td, "fchownat unsupported flag 0x%x", unsupported);
153848b05c3fSKonstantin Belousov 		return (EINVAL);
1539e47823b8SEdward Tomasz Napierala 	}
154048b05c3fSKonstantin Belousov 
1541767a3264SEd Schouten 	flag = (args->flag & LINUX_AT_SYMLINK_NOFOLLOW) == 0 ? 0 :
154248b05c3fSKonstantin Belousov 	    AT_SYMLINK_NOFOLLOW;
1543e47823b8SEdward Tomasz Napierala 	flag |= (args->flag & LINUX_AT_EMPTY_PATH) == 0 ? 0 :
1544e47823b8SEdward Tomasz Napierala 	    AT_EMPTY_PATH;
1545e47823b8SEdward Tomasz Napierala 
1546e47823b8SEdward Tomasz Napierala 	dfd = (args->dfd == LINUX_AT_FDCWD) ? AT_FDCWD :  args->dfd;
15471024de70SMateusz Guzik 	return (kern_fchownat(td, dfd, args->filename, UIO_USERSPACE,
15481024de70SMateusz Guzik 	    args->uid, args->gid, flag));
15491024de70SMateusz Guzik }
155048b05c3fSKonstantin Belousov 
1551931e2a1aSEd Maste #ifdef LINUX_LEGACY_SYSCALLS
155248b05c3fSKonstantin Belousov int
linux_lchown(struct thread * td,struct linux_lchown_args * args)15534c1e3817SMarcel Moolenaar linux_lchown(struct thread *td, struct linux_lchown_args *args)
15544c1e3817SMarcel Moolenaar {
15554c1e3817SMarcel Moolenaar 
15561024de70SMateusz Guzik 	return (kern_fchownat(td, AT_FDCWD, args->path, UIO_USERSPACE, args->uid,
15571024de70SMateusz Guzik 	    args->gid, AT_SYMLINK_NOFOLLOW));
15581024de70SMateusz Guzik }
1559931e2a1aSEd Maste #endif
1560dd01579cSJohn Baldwin 
1561dd01579cSJohn Baldwin static int
convert_fadvice(int advice)1562dd01579cSJohn Baldwin convert_fadvice(int advice)
1563dd01579cSJohn Baldwin {
1564dd01579cSJohn Baldwin 	switch (advice) {
1565dd01579cSJohn Baldwin 	case LINUX_POSIX_FADV_NORMAL:
1566dd01579cSJohn Baldwin 		return (POSIX_FADV_NORMAL);
1567dd01579cSJohn Baldwin 	case LINUX_POSIX_FADV_RANDOM:
1568dd01579cSJohn Baldwin 		return (POSIX_FADV_RANDOM);
1569dd01579cSJohn Baldwin 	case LINUX_POSIX_FADV_SEQUENTIAL:
1570dd01579cSJohn Baldwin 		return (POSIX_FADV_SEQUENTIAL);
1571dd01579cSJohn Baldwin 	case LINUX_POSIX_FADV_WILLNEED:
1572dd01579cSJohn Baldwin 		return (POSIX_FADV_WILLNEED);
1573dd01579cSJohn Baldwin 	case LINUX_POSIX_FADV_DONTNEED:
1574dd01579cSJohn Baldwin 		return (POSIX_FADV_DONTNEED);
1575dd01579cSJohn Baldwin 	case LINUX_POSIX_FADV_NOREUSE:
1576dd01579cSJohn Baldwin 		return (POSIX_FADV_NOREUSE);
1577dd01579cSJohn Baldwin 	default:
1578dd01579cSJohn Baldwin 		return (-1);
1579dd01579cSJohn Baldwin 	}
1580dd01579cSJohn Baldwin }
1581dd01579cSJohn Baldwin 
1582dd01579cSJohn Baldwin int
linux_fadvise64(struct thread * td,struct linux_fadvise64_args * args)1583dd01579cSJohn Baldwin linux_fadvise64(struct thread *td, struct linux_fadvise64_args *args)
1584dd01579cSJohn Baldwin {
1585149afbf3SMark Johnston 	off_t offset;
1586dd01579cSJohn Baldwin 	int advice;
1587dd01579cSJohn Baldwin 
1588149afbf3SMark Johnston #if defined(__amd64__) && defined(COMPAT_LINUX32)
1589149afbf3SMark Johnston 	offset = PAIR32TO64(off_t, args->offset);
1590149afbf3SMark Johnston #else
1591149afbf3SMark Johnston 	offset = args->offset;
1592149afbf3SMark Johnston #endif
1593149afbf3SMark Johnston 
1594dd01579cSJohn Baldwin 	advice = convert_fadvice(args->advice);
1595dd01579cSJohn Baldwin 	if (advice == -1)
1596dd01579cSJohn Baldwin 		return (EINVAL);
1597149afbf3SMark Johnston 	return (kern_posix_fadvise(td, args->fd, offset, args->len, advice));
1598dd01579cSJohn Baldwin }
1599dd01579cSJohn Baldwin 
16007f8f1d7fSDmitry Chagin #if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32))
1601dd01579cSJohn Baldwin int
linux_fadvise64_64(struct thread * td,struct linux_fadvise64_64_args * args)1602dd01579cSJohn Baldwin linux_fadvise64_64(struct thread *td, struct linux_fadvise64_64_args *args)
1603dd01579cSJohn Baldwin {
1604149afbf3SMark Johnston 	off_t len, offset;
1605dd01579cSJohn Baldwin 	int advice;
1606dd01579cSJohn Baldwin 
1607149afbf3SMark Johnston #if defined(__amd64__) && defined(COMPAT_LINUX32)
1608149afbf3SMark Johnston 	len = PAIR32TO64(off_t, args->len);
1609149afbf3SMark Johnston 	offset = PAIR32TO64(off_t, args->offset);
1610149afbf3SMark Johnston #else
1611149afbf3SMark Johnston 	len = args->len;
1612149afbf3SMark Johnston 	offset = args->offset;
1613149afbf3SMark Johnston #endif
1614149afbf3SMark Johnston 
1615dd01579cSJohn Baldwin 	advice = convert_fadvice(args->advice);
1616dd01579cSJohn Baldwin 	if (advice == -1)
1617dd01579cSJohn Baldwin 		return (EINVAL);
1618149afbf3SMark Johnston 	return (kern_posix_fadvise(td, args->fd, offset, len, advice));
1619dd01579cSJohn Baldwin }
16207f8f1d7fSDmitry Chagin #endif /* __i386__ || (__amd64__ && COMPAT_LINUX32) */
1621d69a426fSJung-uk Kim 
1622931e2a1aSEd Maste #ifdef LINUX_LEGACY_SYSCALLS
1623d69a426fSJung-uk Kim int
linux_pipe(struct thread * td,struct linux_pipe_args * args)1624d69a426fSJung-uk Kim linux_pipe(struct thread *td, struct linux_pipe_args *args)
1625d69a426fSJung-uk Kim {
1626d69a426fSJung-uk Kim 	int fildes[2];
1627d69a426fSJung-uk Kim 	int error;
1628d69a426fSJung-uk Kim 
16298328babdSEd Schouten 	error = kern_pipe(td, fildes, 0, NULL, NULL);
163085dbb416SEdward Tomasz Napierala 	if (error != 0)
1631d69a426fSJung-uk Kim 		return (error);
1632d69a426fSJung-uk Kim 
163385dbb416SEdward Tomasz Napierala 	error = copyout(fildes, args->pipefds, sizeof(fildes));
163485dbb416SEdward Tomasz Napierala 	if (error != 0) {
163585dbb416SEdward Tomasz Napierala 		(void)kern_close(td, fildes[0]);
163685dbb416SEdward Tomasz Napierala 		(void)kern_close(td, fildes[1]);
163785dbb416SEdward Tomasz Napierala 	}
163885dbb416SEdward Tomasz Napierala 
163985dbb416SEdward Tomasz Napierala 	return (error);
1640d69a426fSJung-uk Kim }
1641931e2a1aSEd Maste #endif
1642d69a426fSJung-uk Kim 
1643d69a426fSJung-uk Kim int
linux_pipe2(struct thread * td,struct linux_pipe2_args * args)1644d69a426fSJung-uk Kim linux_pipe2(struct thread *td, struct linux_pipe2_args *args)
1645d69a426fSJung-uk Kim {
1646d69a426fSJung-uk Kim 	int fildes[2];
1647d69a426fSJung-uk Kim 	int error, flags;
1648d69a426fSJung-uk Kim 
1649d69a426fSJung-uk Kim 	if ((args->flags & ~(LINUX_O_NONBLOCK | LINUX_O_CLOEXEC)) != 0)
1650d69a426fSJung-uk Kim 		return (EINVAL);
1651d69a426fSJung-uk Kim 
1652d69a426fSJung-uk Kim 	flags = 0;
1653d69a426fSJung-uk Kim 	if ((args->flags & LINUX_O_NONBLOCK) != 0)
1654d69a426fSJung-uk Kim 		flags |= O_NONBLOCK;
1655d69a426fSJung-uk Kim 	if ((args->flags & LINUX_O_CLOEXEC) != 0)
1656d69a426fSJung-uk Kim 		flags |= O_CLOEXEC;
16578328babdSEd Schouten 	error = kern_pipe(td, fildes, flags, NULL, NULL);
165885dbb416SEdward Tomasz Napierala 	if (error != 0)
1659d69a426fSJung-uk Kim 		return (error);
1660d69a426fSJung-uk Kim 
166185dbb416SEdward Tomasz Napierala 	error = copyout(fildes, args->pipefds, sizeof(fildes));
166285dbb416SEdward Tomasz Napierala 	if (error != 0) {
166385dbb416SEdward Tomasz Napierala 		(void)kern_close(td, fildes[0]);
166485dbb416SEdward Tomasz Napierala 		(void)kern_close(td, fildes[1]);
166585dbb416SEdward Tomasz Napierala 	}
166685dbb416SEdward Tomasz Napierala 
166785dbb416SEdward Tomasz Napierala 	return (error);
1668d69a426fSJung-uk Kim }
1669254a937eSDmitry Chagin 
1670254a937eSDmitry Chagin int
linux_dup3(struct thread * td,struct linux_dup3_args * args)1671254a937eSDmitry Chagin linux_dup3(struct thread *td, struct linux_dup3_args *args)
1672254a937eSDmitry Chagin {
1673254a937eSDmitry Chagin 	int cmd;
1674254a937eSDmitry Chagin 	intptr_t newfd;
1675254a937eSDmitry Chagin 
1676254a937eSDmitry Chagin 	if (args->oldfd == args->newfd)
1677254a937eSDmitry Chagin 		return (EINVAL);
1678254a937eSDmitry Chagin 	if ((args->flags & ~LINUX_O_CLOEXEC) != 0)
1679254a937eSDmitry Chagin 		return (EINVAL);
1680254a937eSDmitry Chagin 	if (args->flags & LINUX_O_CLOEXEC)
1681254a937eSDmitry Chagin 		cmd = F_DUP2FD_CLOEXEC;
1682254a937eSDmitry Chagin 	else
1683254a937eSDmitry Chagin 		cmd = F_DUP2FD;
1684254a937eSDmitry Chagin 
1685254a937eSDmitry Chagin 	newfd = args->newfd;
1686254a937eSDmitry Chagin 	return (kern_fcntl(td, args->oldfd, cmd, newfd));
1687254a937eSDmitry Chagin }
1688b6aeb7d5SDmitry Chagin 
1689b6aeb7d5SDmitry Chagin int
linux_fallocate(struct thread * td,struct linux_fallocate_args * args)1690b6aeb7d5SDmitry Chagin linux_fallocate(struct thread *td, struct linux_fallocate_args *args)
1691b6aeb7d5SDmitry Chagin {
1692149afbf3SMark Johnston 	off_t len, offset;
1693b6aeb7d5SDmitry Chagin 
1694b6aeb7d5SDmitry Chagin 	/*
1695b6aeb7d5SDmitry Chagin 	 * We emulate only posix_fallocate system call for which
1696b6aeb7d5SDmitry Chagin 	 * mode should be 0.
1697b6aeb7d5SDmitry Chagin 	 */
1698b6aeb7d5SDmitry Chagin 	if (args->mode != 0)
1699d5c5b4b3SEdward Tomasz Napierala 		return (EOPNOTSUPP);
1700b6aeb7d5SDmitry Chagin 
1701149afbf3SMark Johnston #if defined(__amd64__) && defined(COMPAT_LINUX32)
1702149afbf3SMark Johnston 	len = PAIR32TO64(off_t, args->len);
1703149afbf3SMark Johnston 	offset = PAIR32TO64(off_t, args->offset);
1704149afbf3SMark Johnston #else
1705149afbf3SMark Johnston 	len = args->len;
1706149afbf3SMark Johnston 	offset = args->offset;
1707149afbf3SMark Johnston #endif
1708149afbf3SMark Johnston 
1709149afbf3SMark Johnston 	return (kern_posix_fallocate(td, args->fd, offset, len));
1710b6aeb7d5SDmitry Chagin }
171154666dffSPawel Biernacki 
171254666dffSPawel Biernacki int
linux_copy_file_range(struct thread * td,struct linux_copy_file_range_args * args)171354666dffSPawel Biernacki linux_copy_file_range(struct thread *td, struct linux_copy_file_range_args
171454666dffSPawel Biernacki     *args)
171554666dffSPawel Biernacki {
171654666dffSPawel Biernacki 	l_loff_t inoff, outoff, *inoffp, *outoffp;
171754666dffSPawel Biernacki 	int error, flags;
171854666dffSPawel Biernacki 
171954666dffSPawel Biernacki 	/*
172054666dffSPawel Biernacki 	 * copy_file_range(2) on Linux doesn't define any flags (yet), so is
172154666dffSPawel Biernacki 	 * the native implementation.  Enforce it.
172254666dffSPawel Biernacki 	 */
172354666dffSPawel Biernacki 	if (args->flags != 0) {
172454666dffSPawel Biernacki 		linux_msg(td, "copy_file_range unsupported flags 0x%x",
172554666dffSPawel Biernacki 		    args->flags);
172654666dffSPawel Biernacki 		return (EINVAL);
172754666dffSPawel Biernacki 	}
172854666dffSPawel Biernacki 	flags = 0;
172954666dffSPawel Biernacki 	inoffp = outoffp = NULL;
173054666dffSPawel Biernacki 	if (args->off_in != NULL) {
173154666dffSPawel Biernacki 		error = copyin(args->off_in, &inoff, sizeof(l_loff_t));
173254666dffSPawel Biernacki 		if (error != 0)
173354666dffSPawel Biernacki 			return (error);
173454666dffSPawel Biernacki 		inoffp = &inoff;
173554666dffSPawel Biernacki 	}
173654666dffSPawel Biernacki 	if (args->off_out != NULL) {
173754666dffSPawel Biernacki 		error = copyin(args->off_out, &outoff, sizeof(l_loff_t));
173854666dffSPawel Biernacki 		if (error != 0)
173954666dffSPawel Biernacki 			return (error);
174054666dffSPawel Biernacki 		outoffp = &outoff;
174154666dffSPawel Biernacki 	}
174254666dffSPawel Biernacki 
174354666dffSPawel Biernacki 	error = kern_copy_file_range(td, args->fd_in, inoffp, args->fd_out,
174454666dffSPawel Biernacki 	    outoffp, args->len, flags);
174554666dffSPawel Biernacki 	if (error == 0 && args->off_in != NULL)
174654666dffSPawel Biernacki 		error = copyout(inoffp, args->off_in, sizeof(l_loff_t));
174754666dffSPawel Biernacki 	if (error == 0 && args->off_out != NULL)
174854666dffSPawel Biernacki 		error = copyout(outoffp, args->off_out, sizeof(l_loff_t));
174954666dffSPawel Biernacki 	return (error);
175054666dffSPawel Biernacki }
175154666dffSPawel Biernacki 
17525403f186SKyle Evans #define	LINUX_MEMFD_PREFIX	"memfd:"
17535403f186SKyle Evans 
17545403f186SKyle Evans int
linux_memfd_create(struct thread * td,struct linux_memfd_create_args * args)17555403f186SKyle Evans linux_memfd_create(struct thread *td, struct linux_memfd_create_args *args)
17565403f186SKyle Evans {
17575403f186SKyle Evans 	char memfd_name[LINUX_NAME_MAX + 1];
17585403f186SKyle Evans 	int error, flags, shmflags, oflags;
17595403f186SKyle Evans 
17605403f186SKyle Evans 	/*
17615403f186SKyle Evans 	 * This is our clever trick to avoid the heap allocation to copy in the
17625403f186SKyle Evans 	 * uname.  We don't really need to go this far out of our way, but it
17635403f186SKyle Evans 	 * does keep the rest of this function fairly clean as they don't have
17645403f186SKyle Evans 	 * to worry about cleanup on the way out.
17655403f186SKyle Evans 	 */
17665403f186SKyle Evans 	error = copyinstr(args->uname_ptr,
17675403f186SKyle Evans 	    memfd_name + sizeof(LINUX_MEMFD_PREFIX) - 1,
17685403f186SKyle Evans 	    LINUX_NAME_MAX - sizeof(LINUX_MEMFD_PREFIX) - 1, NULL);
17695403f186SKyle Evans 	if (error != 0) {
17705403f186SKyle Evans 		if (error == ENAMETOOLONG)
17715403f186SKyle Evans 			error = EINVAL;
17725403f186SKyle Evans 		return (error);
17735403f186SKyle Evans 	}
17745403f186SKyle Evans 
17755403f186SKyle Evans 	memcpy(memfd_name, LINUX_MEMFD_PREFIX, sizeof(LINUX_MEMFD_PREFIX) - 1);
17765403f186SKyle Evans 	flags = linux_to_bsd_bits(args->flags, mfd_bitmap, 0);
17775403f186SKyle Evans 	if ((flags & ~(MFD_CLOEXEC | MFD_ALLOW_SEALING | MFD_HUGETLB |
17785403f186SKyle Evans 	    MFD_HUGE_MASK)) != 0)
17795403f186SKyle Evans 		return (EINVAL);
17805403f186SKyle Evans 	/* Size specified but no HUGETLB. */
17815403f186SKyle Evans 	if ((flags & MFD_HUGE_MASK) != 0 && (flags & MFD_HUGETLB) == 0)
17825403f186SKyle Evans 		return (EINVAL);
17835403f186SKyle Evans 	/* We don't actually support HUGETLB. */
17845403f186SKyle Evans 	if ((flags & MFD_HUGETLB) != 0)
17855403f186SKyle Evans 		return (ENOSYS);
17865403f186SKyle Evans 	oflags = O_RDWR;
1787423a033bSKyle Evans 	shmflags = SHM_GROW_ON_WRITE;
17885403f186SKyle Evans 	if ((flags & MFD_CLOEXEC) != 0)
17895403f186SKyle Evans 		oflags |= O_CLOEXEC;
17905403f186SKyle Evans 	if ((flags & MFD_ALLOW_SEALING) != 0)
17915403f186SKyle Evans 		shmflags |= SHM_ALLOW_SEALING;
17925403f186SKyle Evans 	return (kern_shm_open2(td, SHM_ANON, oflags, 0, shmflags, NULL,
17935403f186SKyle Evans 	    memfd_name));
17945403f186SKyle Evans }
17958d1d0171SEdward Tomasz Napierala 
17968d1d0171SEdward Tomasz Napierala int
linux_splice(struct thread * td,struct linux_splice_args * args)17978d1d0171SEdward Tomasz Napierala linux_splice(struct thread *td, struct linux_splice_args *args)
17988d1d0171SEdward Tomasz Napierala {
17998d1d0171SEdward Tomasz Napierala 
18008d1d0171SEdward Tomasz Napierala 	linux_msg(td, "syscall splice not really implemented");
18018d1d0171SEdward Tomasz Napierala 
18028d1d0171SEdward Tomasz Napierala 	/*
18038d1d0171SEdward Tomasz Napierala 	 * splice(2) is documented to return EINVAL in various circumstances;
18048d1d0171SEdward Tomasz Napierala 	 * returning it instead of ENOSYS should hint the caller to use fallback
18058d1d0171SEdward Tomasz Napierala 	 * instead.
18068d1d0171SEdward Tomasz Napierala 	 */
18078d1d0171SEdward Tomasz Napierala 	return (EINVAL);
18088d1d0171SEdward Tomasz Napierala }
180971bc1780SDmitry Chagin 
181071bc1780SDmitry Chagin int
linux_close_range(struct thread * td,struct linux_close_range_args * args)181171bc1780SDmitry Chagin linux_close_range(struct thread *td, struct linux_close_range_args *args)
181271bc1780SDmitry Chagin {
181371bc1780SDmitry Chagin 	u_int flags = 0;
181471bc1780SDmitry Chagin 
181571bc1780SDmitry Chagin 	/*
181671bc1780SDmitry Chagin 	 * Implementing close_range(CLOSE_RANGE_UNSHARE) allows Linux to
181771bc1780SDmitry Chagin 	 * unshare filedesc table of the calling thread from others threads
181871bc1780SDmitry Chagin 	 * in a thread group (i.e., process in the FreeBSD) or others processes,
181971bc1780SDmitry Chagin 	 * which shares the same table, before closing the files. FreeBSD does
182071bc1780SDmitry Chagin 	 * not have compatible unsharing mechanism due to the fact that sharing
182171bc1780SDmitry Chagin 	 * process resources, including filedesc table, is at thread level in the
182271bc1780SDmitry Chagin 	 * Linux, while in the FreeBSD it is at the process level.
182371bc1780SDmitry Chagin 	 * Return EINVAL for now if the CLOSE_RANGE_UNSHARE flag is specified
182471bc1780SDmitry Chagin 	 * until this new Linux API stabilizes.
182571bc1780SDmitry Chagin 	 */
182671bc1780SDmitry Chagin 
182771bc1780SDmitry Chagin 	if ((args->flags & ~(LINUX_CLOSE_RANGE_CLOEXEC)) != 0)
182871bc1780SDmitry Chagin 		return (EINVAL);
182971bc1780SDmitry Chagin 	if (args->first > args->last)
183071bc1780SDmitry Chagin 		return (EINVAL);
183171bc1780SDmitry Chagin 	if ((args->flags & LINUX_CLOSE_RANGE_CLOEXEC) != 0)
183271bc1780SDmitry Chagin 		flags |= CLOSE_RANGE_CLOEXEC;
183371bc1780SDmitry Chagin 	return (kern_close_range(td, flags, args->first, args->last));
183471bc1780SDmitry Chagin }
1835e58ff664SDmitry Chagin 
1836e58ff664SDmitry Chagin int
linux_enobufs2eagain(struct thread * td,int fd,int error)1837e58ff664SDmitry Chagin linux_enobufs2eagain(struct thread *td, int fd, int error)
1838e58ff664SDmitry Chagin {
1839e58ff664SDmitry Chagin 	struct file *fp;
1840e58ff664SDmitry Chagin 
1841e58ff664SDmitry Chagin 	if (error != ENOBUFS)
1842e58ff664SDmitry Chagin 		return (error);
1843e58ff664SDmitry Chagin 	if (fget(td, fd, &cap_no_rights, &fp) != 0)
1844e58ff664SDmitry Chagin 		return (error);
1845e58ff664SDmitry Chagin 	if (fp->f_type == DTYPE_SOCKET && (fp->f_flag & FNONBLOCK) != 0)
1846e58ff664SDmitry Chagin 		error = EAGAIN;
1847e58ff664SDmitry Chagin 	fdrop(fp, td);
1848e58ff664SDmitry Chagin 	return (error);
1849e58ff664SDmitry Chagin }
1850e58ff664SDmitry Chagin 
1851e58ff664SDmitry Chagin int
linux_write(struct thread * td,struct linux_write_args * args)1852e58ff664SDmitry Chagin linux_write(struct thread *td, struct linux_write_args *args)
1853e58ff664SDmitry Chagin {
1854e58ff664SDmitry Chagin 	struct write_args bargs = {
1855e58ff664SDmitry Chagin 		.fd	= args->fd,
1856e58ff664SDmitry Chagin 		.buf	= args->buf,
1857e58ff664SDmitry Chagin 		.nbyte	= args->nbyte,
1858e58ff664SDmitry Chagin 	};
1859e58ff664SDmitry Chagin 
1860e58ff664SDmitry Chagin 	return (linux_enobufs2eagain(td, args->fd, sys_write(td, &bargs)));
1861e58ff664SDmitry Chagin }
18624231b825SDmitry Chagin 
18634231b825SDmitry Chagin int
linux_writev(struct thread * td,struct linux_writev_args * args)18644231b825SDmitry Chagin linux_writev(struct thread *td, struct linux_writev_args *args)
18654231b825SDmitry Chagin {
18664231b825SDmitry Chagin 	struct uio *auio;
18674231b825SDmitry Chagin 	int error;
18684231b825SDmitry Chagin 
18694231b825SDmitry Chagin #ifdef COMPAT_LINUX32
18704231b825SDmitry Chagin 	error = freebsd32_copyinuio(PTRIN(args->iovp), args->iovcnt, &auio);
18714231b825SDmitry Chagin #else
18724231b825SDmitry Chagin 	error = copyinuio(args->iovp, args->iovcnt, &auio);
18734231b825SDmitry Chagin #endif
18744231b825SDmitry Chagin 	if (error != 0)
18754231b825SDmitry Chagin 		return (error);
18764231b825SDmitry Chagin 	error = kern_writev(td, args->fd, auio);
187761cc4830SAlfredo Mazzinghi 	freeuio(auio);
18784231b825SDmitry Chagin 	return (linux_enobufs2eagain(td, args->fd, error));
18794231b825SDmitry Chagin }
1880