linux_file.c revision 178439
1/*-
2 * Copyright (c) 1994-1995 S�ren Schmidt
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer
10 *    in this position and unchanged.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 *    derived from this software without specific prior written permission
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30__FBSDID("$FreeBSD: head/sys/compat/linux/linux_file.c 178439 2008-04-23 15:56:33Z rdivacky $");
31
32#include "opt_compat.h"
33#include "opt_mac.h"
34
35#include <sys/param.h>
36#include <sys/systm.h>
37#include <sys/conf.h>
38#include <sys/dirent.h>
39#include <sys/fcntl.h>
40#include <sys/file.h>
41#include <sys/filedesc.h>
42#include <sys/lock.h>
43#include <sys/malloc.h>
44#include <sys/mount.h>
45#include <sys/mutex.h>
46#include <sys/namei.h>
47#include <sys/proc.h>
48#include <sys/stat.h>
49#include <sys/sx.h>
50#include <sys/syscallsubr.h>
51#include <sys/sysproto.h>
52#include <sys/tty.h>
53#include <sys/unistd.h>
54#include <sys/vnode.h>
55
56#include <security/mac/mac_framework.h>
57
58#include <ufs/ufs/extattr.h>
59#include <ufs/ufs/quota.h>
60#include <ufs/ufs/ufsmount.h>
61
62#ifdef COMPAT_LINUX32
63#include <machine/../linux32/linux.h>
64#include <machine/../linux32/linux32_proto.h>
65#else
66#include <machine/../linux/linux.h>
67#include <machine/../linux/linux_proto.h>
68#endif
69#include <compat/linux/linux_util.h>
70#include <compat/linux/linux_file.h>
71
72int
73linux_creat(struct thread *td, struct linux_creat_args *args)
74{
75    char *path;
76    int error;
77
78    LCONVPATHEXIST(td, args->path, &path);
79
80#ifdef DEBUG
81	if (ldebug(creat))
82		printf(ARGS(creat, "%s, %d"), path, args->mode);
83#endif
84    error = kern_open(td, path, UIO_SYSSPACE, O_WRONLY | O_CREAT | O_TRUNC,
85	args->mode);
86    LFREEPATH(path);
87    return (error);
88}
89
90
91static int
92linux_common_open(struct thread *td, int dirfd, char *path, int l_flags, int mode)
93{
94    struct proc *p = td->td_proc;
95    struct file *fp;
96    int fd;
97    int bsd_flags, error;
98
99    bsd_flags = 0;
100    switch (l_flags & LINUX_O_ACCMODE) {
101    case LINUX_O_WRONLY:
102	bsd_flags |= O_WRONLY;
103	break;
104    case LINUX_O_RDWR:
105	bsd_flags |= O_RDWR;
106	break;
107    default:
108	bsd_flags |= O_RDONLY;
109    }
110    if (l_flags & LINUX_O_NDELAY)
111	bsd_flags |= O_NONBLOCK;
112    if (l_flags & LINUX_O_APPEND)
113	bsd_flags |= O_APPEND;
114    if (l_flags & LINUX_O_SYNC)
115	bsd_flags |= O_FSYNC;
116    if (l_flags & LINUX_O_NONBLOCK)
117	bsd_flags |= O_NONBLOCK;
118    if (l_flags & LINUX_FASYNC)
119	bsd_flags |= O_ASYNC;
120    if (l_flags & LINUX_O_CREAT)
121	bsd_flags |= O_CREAT;
122    if (l_flags & LINUX_O_TRUNC)
123	bsd_flags |= O_TRUNC;
124    if (l_flags & LINUX_O_EXCL)
125	bsd_flags |= O_EXCL;
126    if (l_flags & LINUX_O_NOCTTY)
127	bsd_flags |= O_NOCTTY;
128    if (l_flags & LINUX_O_DIRECT)
129	bsd_flags |= O_DIRECT;
130    if (l_flags & LINUX_O_NOFOLLOW)
131	bsd_flags |= O_NOFOLLOW;
132    /* XXX LINUX_O_NOATIME: unable to be easily implemented. */
133
134    error = kern_openat(td, dirfd, path, UIO_SYSSPACE, bsd_flags, mode);
135
136    if (!error) {
137	    fd = td->td_retval[0];
138	    /*
139	     * XXX In between kern_open() and fget(), another process
140	     * having the same filedesc could use that fd without
141	     * checking below.
142	     */
143	    error = fget(td, fd, &fp);
144	    if (!error) {
145		    sx_slock(&proctree_lock);
146		    PROC_LOCK(p);
147		    if (!(bsd_flags & O_NOCTTY) &&
148			SESS_LEADER(p) && !(p->p_flag & P_CONTROLT)) {
149			    PROC_UNLOCK(p);
150			    sx_unlock(&proctree_lock);
151			    if (fp->f_type == DTYPE_VNODE)
152				    (void) fo_ioctl(fp, TIOCSCTTY, (caddr_t) 0,
153					     td->td_ucred, td);
154		    } else {
155			    PROC_UNLOCK(p);
156			    sx_sunlock(&proctree_lock);
157		    }
158		    if (l_flags & LINUX_O_DIRECTORY) {
159			    if (fp->f_type != DTYPE_VNODE ||
160				fp->f_vnode->v_type != VDIR) {
161				    error = ENOTDIR;
162			    }
163		    }
164		    fdrop(fp, td);
165		    /*
166		     * XXX as above, fdrop()/kern_close() pair is racy.
167		     */
168		    if (error)
169			    kern_close(td, fd);
170	    }
171    }
172
173#ifdef DEBUG
174    if (ldebug(open))
175	    printf(LMSG("open returns error %d"), error);
176#endif
177    LFREEPATH(path);
178    return (error);
179}
180
181int
182linux_openat(struct thread *td, struct linux_openat_args *args)
183{
184	char *path;
185	int dfd;
186
187	dfd = (args->dfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->dfd;
188	if (args->flags & LINUX_O_CREAT)
189		LCONVPATH_AT(td, args->filename, &path, 1, dfd);
190	else
191		LCONVPATH_AT(td, args->filename, &path, 0, dfd);
192#ifdef DEBUG
193	if (ldebug(openat))
194		printf(ARGS(openat, "%i, %s, 0x%x, 0x%x"), args->dfd,
195		    path, args->flags, args->mode);
196#endif
197	return (linux_common_open(td, dfd, path, args->flags, args->mode));
198}
199
200int
201linux_open(struct thread *td, struct linux_open_args *args)
202{
203    char *path;
204
205    if (args->flags & LINUX_O_CREAT)
206	LCONVPATHCREAT(td, args->path, &path);
207    else
208	LCONVPATHEXIST(td, args->path, &path);
209
210#ifdef DEBUG
211	if (ldebug(open))
212		printf(ARGS(open, "%s, 0x%x, 0x%x"),
213		    path, args->flags, args->mode);
214#endif
215
216	return (linux_common_open(td, AT_FDCWD, path, args->flags, args->mode));
217}
218
219int
220linux_lseek(struct thread *td, struct linux_lseek_args *args)
221{
222
223    struct lseek_args /* {
224	int fd;
225	int pad;
226	off_t offset;
227	int whence;
228    } */ tmp_args;
229    int error;
230
231#ifdef DEBUG
232	if (ldebug(lseek))
233		printf(ARGS(lseek, "%d, %ld, %d"),
234		    args->fdes, (long)args->off, args->whence);
235#endif
236    tmp_args.fd = args->fdes;
237    tmp_args.offset = (off_t)args->off;
238    tmp_args.whence = args->whence;
239    error = lseek(td, &tmp_args);
240    return error;
241}
242
243int
244linux_llseek(struct thread *td, struct linux_llseek_args *args)
245{
246	struct lseek_args bsd_args;
247	int error;
248	off_t off;
249
250#ifdef DEBUG
251	if (ldebug(llseek))
252		printf(ARGS(llseek, "%d, %d:%d, %d"),
253		    args->fd, args->ohigh, args->olow, args->whence);
254#endif
255	off = (args->olow) | (((off_t) args->ohigh) << 32);
256
257	bsd_args.fd = args->fd;
258	bsd_args.offset = off;
259	bsd_args.whence = args->whence;
260
261	if ((error = lseek(td, &bsd_args)))
262		return error;
263
264	if ((error = copyout(td->td_retval, args->res, sizeof (off_t))))
265		return error;
266
267	td->td_retval[0] = 0;
268	return 0;
269}
270
271int
272linux_readdir(struct thread *td, struct linux_readdir_args *args)
273{
274	struct linux_getdents_args lda;
275
276	lda.fd = args->fd;
277	lda.dent = args->dent;
278	lda.count = 1;
279	return linux_getdents(td, &lda);
280}
281
282/*
283 * Note that linux_getdents(2) and linux_getdents64(2) have the same
284 * arguments. They only differ in the definition of struct dirent they
285 * operate on. We use this to common the code, with the exception of
286 * accessing struct dirent. Note that linux_readdir(2) is implemented
287 * by means of linux_getdents(2). In this case we never operate on
288 * struct dirent64 and thus don't need to handle it...
289 */
290
291struct l_dirent {
292	l_long		d_ino;
293	l_off_t		d_off;
294	l_ushort	d_reclen;
295	char		d_name[LINUX_NAME_MAX + 1];
296};
297
298struct l_dirent64 {
299	uint64_t	d_ino;
300	int64_t		d_off;
301	l_ushort	d_reclen;
302	u_char		d_type;
303	char		d_name[LINUX_NAME_MAX + 1];
304};
305
306#define LINUX_RECLEN(de,namlen) \
307    ALIGN((((char *)&(de)->d_name - (char *)de) + (namlen) + 1))
308
309#define	LINUX_DIRBLKSIZ		512
310
311static int
312getdents_common(struct thread *td, struct linux_getdents64_args *args,
313    int is64bit)
314{
315	struct dirent *bdp;
316	struct vnode *vp;
317	caddr_t inp, buf;		/* BSD-format */
318	int len, reclen;		/* BSD-format */
319	caddr_t outp;			/* Linux-format */
320	int resid, linuxreclen=0;	/* Linux-format */
321	struct file *fp;
322	struct uio auio;
323	struct iovec aiov;
324	off_t off;
325	struct l_dirent linux_dirent;
326	struct l_dirent64 linux_dirent64;
327	int buflen, error, eofflag, nbytes, justone;
328	u_long *cookies = NULL, *cookiep;
329	int ncookies, vfslocked;
330
331	nbytes = args->count;
332	if (nbytes == 1) {
333		/* readdir(2) case. Always struct dirent. */
334		if (is64bit)
335			return (EINVAL);
336		nbytes = sizeof(linux_dirent);
337		justone = 1;
338	} else
339		justone = 0;
340
341	if ((error = getvnode(td->td_proc->p_fd, args->fd, &fp)) != 0)
342		return (error);
343
344	if ((fp->f_flag & FREAD) == 0) {
345		fdrop(fp, td);
346		return (EBADF);
347	}
348
349	vp = fp->f_vnode;
350	vfslocked = VFS_LOCK_GIANT(vp->v_mount);
351	if (vp->v_type != VDIR) {
352		VFS_UNLOCK_GIANT(vfslocked);
353		fdrop(fp, td);
354		return (EINVAL);
355	}
356
357	off = fp->f_offset;
358
359	buflen = max(LINUX_DIRBLKSIZ, nbytes);
360	buflen = min(buflen, MAXBSIZE);
361	buf = malloc(buflen, M_TEMP, M_WAITOK);
362	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
363
364again:
365	aiov.iov_base = buf;
366	aiov.iov_len = buflen;
367	auio.uio_iov = &aiov;
368	auio.uio_iovcnt = 1;
369	auio.uio_rw = UIO_READ;
370	auio.uio_segflg = UIO_SYSSPACE;
371	auio.uio_td = td;
372	auio.uio_resid = buflen;
373	auio.uio_offset = off;
374
375	if (cookies) {
376		free(cookies, M_TEMP);
377		cookies = NULL;
378	}
379
380#ifdef MAC
381	/*
382	 * Do directory search MAC check using non-cached credentials.
383	 */
384	if ((error = mac_vnode_check_readdir(td->td_ucred, vp)))
385		goto out;
386#endif /* MAC */
387	if ((error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, &ncookies,
388		 &cookies)))
389		goto out;
390
391	inp = buf;
392	outp = (caddr_t)args->dirent;
393	resid = nbytes;
394	if ((len = buflen - auio.uio_resid) <= 0)
395		goto eof;
396
397	cookiep = cookies;
398
399	if (cookies) {
400		/*
401		 * When using cookies, the vfs has the option of reading from
402		 * a different offset than that supplied (UFS truncates the
403		 * offset to a block boundary to make sure that it never reads
404		 * partway through a directory entry, even if the directory
405		 * has been compacted).
406		 */
407		while (len > 0 && ncookies > 0 && *cookiep <= off) {
408			bdp = (struct dirent *) inp;
409			len -= bdp->d_reclen;
410			inp += bdp->d_reclen;
411			cookiep++;
412			ncookies--;
413		}
414	}
415
416	while (len > 0) {
417		if (cookiep && ncookies == 0)
418			break;
419		bdp = (struct dirent *) inp;
420		reclen = bdp->d_reclen;
421		if (reclen & 3) {
422			error = EFAULT;
423			goto out;
424		}
425
426		if (bdp->d_fileno == 0) {
427			inp += reclen;
428			if (cookiep) {
429				off = *cookiep++;
430				ncookies--;
431			} else
432				off += reclen;
433
434			len -= reclen;
435			continue;
436		}
437
438		linuxreclen = (is64bit)
439		    ? LINUX_RECLEN(&linux_dirent64, bdp->d_namlen)
440		    : LINUX_RECLEN(&linux_dirent, bdp->d_namlen);
441
442		if (reclen > len || resid < linuxreclen) {
443			outp++;
444			break;
445		}
446
447		if (justone) {
448			/* readdir(2) case. */
449			linux_dirent.d_ino = (l_long)bdp->d_fileno;
450			linux_dirent.d_off = (l_off_t)linuxreclen;
451			linux_dirent.d_reclen = (l_ushort)bdp->d_namlen;
452			strcpy(linux_dirent.d_name, bdp->d_name);
453			error = copyout(&linux_dirent, outp, linuxreclen);
454		} else {
455			if (is64bit) {
456				linux_dirent64.d_ino = bdp->d_fileno;
457				linux_dirent64.d_off = (cookiep)
458				    ? (l_off_t)*cookiep
459				    : (l_off_t)(off + reclen);
460				linux_dirent64.d_reclen =
461				    (l_ushort)linuxreclen;
462				linux_dirent64.d_type = bdp->d_type;
463				strcpy(linux_dirent64.d_name, bdp->d_name);
464				error = copyout(&linux_dirent64, outp,
465				    linuxreclen);
466			} else {
467				linux_dirent.d_ino = bdp->d_fileno;
468				linux_dirent.d_off = (cookiep)
469				    ? (l_off_t)*cookiep
470				    : (l_off_t)(off + reclen);
471				linux_dirent.d_reclen = (l_ushort)linuxreclen;
472				strcpy(linux_dirent.d_name, bdp->d_name);
473				error = copyout(&linux_dirent, outp,
474				    linuxreclen);
475			}
476		}
477		if (error)
478			goto out;
479
480		inp += reclen;
481		if (cookiep) {
482			off = *cookiep++;
483			ncookies--;
484		} else
485			off += reclen;
486
487		outp += linuxreclen;
488		resid -= linuxreclen;
489		len -= reclen;
490		if (justone)
491			break;
492	}
493
494	if (outp == (caddr_t)args->dirent)
495		goto again;
496
497	fp->f_offset = off;
498	if (justone)
499		nbytes = resid + linuxreclen;
500
501eof:
502	td->td_retval[0] = nbytes - resid;
503
504out:
505	if (cookies)
506		free(cookies, M_TEMP);
507
508	VOP_UNLOCK(vp, 0);
509	VFS_UNLOCK_GIANT(vfslocked);
510	fdrop(fp, td);
511	free(buf, M_TEMP);
512	return (error);
513}
514
515int
516linux_getdents(struct thread *td, struct linux_getdents_args *args)
517{
518
519#ifdef DEBUG
520	if (ldebug(getdents))
521		printf(ARGS(getdents, "%d, *, %d"), args->fd, args->count);
522#endif
523
524	return (getdents_common(td, (struct linux_getdents64_args*)args, 0));
525}
526
527int
528linux_getdents64(struct thread *td, struct linux_getdents64_args *args)
529{
530
531#ifdef DEBUG
532	if (ldebug(getdents64))
533		printf(ARGS(getdents64, "%d, *, %d"), args->fd, args->count);
534#endif
535
536	return (getdents_common(td, args, 1));
537}
538
539/*
540 * These exist mainly for hooks for doing /compat/linux translation.
541 */
542
543int
544linux_access(struct thread *td, struct linux_access_args *args)
545{
546	char *path;
547	int error;
548
549	/* linux convention */
550	if (args->flags & ~(F_OK | X_OK | W_OK | R_OK))
551		return (EINVAL);
552
553	LCONVPATHEXIST(td, args->path, &path);
554
555#ifdef DEBUG
556	if (ldebug(access))
557		printf(ARGS(access, "%s, %d"), path, args->flags);
558#endif
559	error = kern_access(td, path, UIO_SYSSPACE, args->flags);
560	LFREEPATH(path);
561
562	return (error);
563}
564
565int
566linux_faccessat(struct thread *td, struct linux_faccessat_args *args)
567{
568	char *path;
569	int error, dfd;
570
571	/* linux convention */
572	if (args->mode & ~(F_OK | X_OK | W_OK | R_OK))
573		return (EINVAL);
574
575	dfd = (args->dfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->dfd;
576	LCONVPATHEXIST_AT(td, args->filename, &path, dfd);
577
578#ifdef DEBUG
579	if (ldebug(access))
580		printf(ARGS(access, "%s, %d"), path, args->mode);
581#endif
582
583	error = kern_accessat(td, dfd, path, UIO_SYSSPACE, 0 /* XXX */,
584	    args->mode);
585	LFREEPATH(path);
586
587	return (error);
588}
589
590int
591linux_unlink(struct thread *td, struct linux_unlink_args *args)
592{
593	char *path;
594	int error;
595	struct stat st;
596
597	LCONVPATHEXIST(td, args->path, &path);
598
599#ifdef DEBUG
600	if (ldebug(unlink))
601		printf(ARGS(unlink, "%s"), path);
602#endif
603
604	error = kern_unlink(td, path, UIO_SYSSPACE);
605	if (error == EPERM)
606		/* Introduce POSIX noncompliant behaviour of Linux */
607		if (kern_stat(td, path, UIO_SYSSPACE, &st) == 0)
608			if (S_ISDIR(st.st_mode))
609				error = EISDIR;
610	LFREEPATH(path);
611	return (error);
612}
613
614int
615linux_unlinkat(struct thread *td, struct linux_unlinkat_args *args)
616{
617	char *path;
618	int error, dfd;
619	struct stat st;
620
621	if (args->flag & ~LINUX_AT_REMOVEDIR)
622		return (EINVAL);
623
624	dfd = (args->dfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->dfd;
625	LCONVPATHEXIST_AT(td, args->pathname, &path, dfd);
626
627#ifdef DEBUG
628	if (ldebug(unlinkat))
629		printf(ARGS(unlinkat, "%s"), path);
630#endif
631
632	if (args->flag & LINUX_AT_REMOVEDIR)
633		error = kern_rmdirat(td, dfd, path, UIO_SYSSPACE);
634	else
635		error = kern_unlinkat(td, dfd, path, UIO_SYSSPACE);
636	if (error == EPERM && !(args->flag & LINUX_AT_REMOVEDIR)) {
637		/* Introduce POSIX noncompliant behaviour of Linux */
638		if (kern_statat(td, AT_SYMLINK_NOFOLLOW, dfd, path,
639		    UIO_SYSSPACE, &st) == 0 && S_ISDIR(st.st_mode))
640			error = EISDIR;
641	}
642	LFREEPATH(path);
643	return (error);
644}
645int
646linux_chdir(struct thread *td, struct linux_chdir_args *args)
647{
648	char *path;
649	int error;
650
651	LCONVPATHEXIST(td, args->path, &path);
652
653#ifdef DEBUG
654	if (ldebug(chdir))
655		printf(ARGS(chdir, "%s"), path);
656#endif
657	error = kern_chdir(td, path, UIO_SYSSPACE);
658	LFREEPATH(path);
659	return (error);
660}
661
662int
663linux_chmod(struct thread *td, struct linux_chmod_args *args)
664{
665	char *path;
666	int error;
667
668	LCONVPATHEXIST(td, args->path, &path);
669
670#ifdef DEBUG
671	if (ldebug(chmod))
672		printf(ARGS(chmod, "%s, %d"), path, args->mode);
673#endif
674	error = kern_chmod(td, path, UIO_SYSSPACE, args->mode);
675	LFREEPATH(path);
676	return (error);
677}
678
679int
680linux_fchmodat(struct thread *td, struct linux_fchmodat_args *args)
681{
682	char *path;
683	int error, dfd;
684
685	dfd = (args->dfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->dfd;
686	LCONVPATHEXIST_AT(td, args->filename, &path, dfd);
687
688#ifdef DEBUG
689	if (ldebug(fchmodat))
690		printf(ARGS(fchmodat, "%s, %d"), path, args->mode);
691#endif
692
693	error = kern_fchmodat(td, dfd, path, UIO_SYSSPACE, args->mode, 0);
694	LFREEPATH(path);
695	return (error);
696}
697
698int
699linux_mkdir(struct thread *td, struct linux_mkdir_args *args)
700{
701	char *path;
702	int error;
703
704	LCONVPATHCREAT(td, args->path, &path);
705
706#ifdef DEBUG
707	if (ldebug(mkdir))
708		printf(ARGS(mkdir, "%s, %d"), path, args->mode);
709#endif
710	error = kern_mkdir(td, path, UIO_SYSSPACE, args->mode);
711	LFREEPATH(path);
712	return (error);
713}
714
715int
716linux_mkdirat(struct thread *td, struct linux_mkdirat_args *args)
717{
718	char *path;
719	int error, dfd;
720
721	dfd = (args->dfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->dfd;
722	LCONVPATHCREAT_AT(td, args->pathname, &path, dfd);
723
724#ifdef DEBUG
725	if (ldebug(mkdirat))
726		printf(ARGS(mkdirat, "%s, %d"), path, args->mode);
727#endif
728	error = kern_mkdirat(td, dfd, path, UIO_SYSSPACE, args->mode);
729	LFREEPATH(path);
730	return (error);
731}
732
733int
734linux_rmdir(struct thread *td, struct linux_rmdir_args *args)
735{
736	char *path;
737	int error;
738
739	LCONVPATHEXIST(td, args->path, &path);
740
741#ifdef DEBUG
742	if (ldebug(rmdir))
743		printf(ARGS(rmdir, "%s"), path);
744#endif
745	error = kern_rmdir(td, path, UIO_SYSSPACE);
746	LFREEPATH(path);
747	return (error);
748}
749
750int
751linux_rename(struct thread *td, struct linux_rename_args *args)
752{
753	char *from, *to;
754	int error;
755
756	LCONVPATHEXIST(td, args->from, &from);
757	/* Expand LCONVPATHCREATE so that `from' can be freed on errors */
758	error = linux_emul_convpath(td, args->to, UIO_USERSPACE, &to, 1, AT_FDCWD);
759	if (to == NULL) {
760		LFREEPATH(from);
761		return (error);
762	}
763
764#ifdef DEBUG
765	if (ldebug(rename))
766		printf(ARGS(rename, "%s, %s"), from, to);
767#endif
768	error = kern_rename(td, from, to, UIO_SYSSPACE);
769	LFREEPATH(from);
770	LFREEPATH(to);
771	return (error);
772}
773
774int
775linux_renameat(struct thread *td, struct linux_renameat_args *args)
776{
777	char *from, *to;
778	int error, olddfd, newdfd;
779
780	olddfd = (args->olddfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->olddfd;
781	newdfd = (args->newdfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->newdfd;
782	LCONVPATHEXIST_AT(td, args->oldname, &from, olddfd);
783	/* Expand LCONVPATHCREATE so that `from' can be freed on errors */
784	error = linux_emul_convpath(td, args->newname, UIO_USERSPACE, &to, 1, newdfd);
785	if (to == NULL) {
786		LFREEPATH(from);
787		return (error);
788	}
789
790#ifdef DEBUG
791	if (ldebug(renameat))
792		printf(ARGS(renameat, "%s, %s"), from, to);
793#endif
794	error = kern_renameat(td, olddfd, from, newdfd, to, UIO_SYSSPACE);
795	LFREEPATH(from);
796	LFREEPATH(to);
797	return (error);
798}
799
800int
801linux_symlink(struct thread *td, struct linux_symlink_args *args)
802{
803	char *path, *to;
804	int error;
805
806	LCONVPATHEXIST(td, args->path, &path);
807	/* Expand LCONVPATHCREATE so that `path' can be freed on errors */
808	error = linux_emul_convpath(td, args->to, UIO_USERSPACE, &to, 1, AT_FDCWD);
809	if (to == NULL) {
810		LFREEPATH(path);
811		return (error);
812	}
813
814#ifdef DEBUG
815	if (ldebug(symlink))
816		printf(ARGS(symlink, "%s, %s"), path, to);
817#endif
818	error = kern_symlink(td, path, to, UIO_SYSSPACE);
819	LFREEPATH(path);
820	LFREEPATH(to);
821	return (error);
822}
823
824int
825linux_symlinkat(struct thread *td, struct linux_symlinkat_args *args)
826{
827	char *path, *to;
828	int error, dfd;
829
830	dfd = (args->newdfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->newdfd;
831	LCONVPATHEXIST_AT(td, args->oldname, &path, dfd);
832	/* Expand LCONVPATHCREATE so that `path' can be freed on errors */
833	error = linux_emul_convpath(td, args->newname, UIO_USERSPACE, &to, 1, dfd);
834	if (to == NULL) {
835		LFREEPATH(path);
836		return (error);
837	}
838
839#ifdef DEBUG
840	if (ldebug(symlinkat))
841		printf(ARGS(symlinkat, "%s, %s"), path, to);
842#endif
843
844	error = kern_symlinkat(td, path, dfd, to, UIO_SYSSPACE);
845	LFREEPATH(path);
846	LFREEPATH(to);
847	return (error);
848}
849
850int
851linux_readlink(struct thread *td, struct linux_readlink_args *args)
852{
853	char *name;
854	int error;
855
856	LCONVPATHEXIST(td, args->name, &name);
857
858#ifdef DEBUG
859	if (ldebug(readlink))
860		printf(ARGS(readlink, "%s, %p, %d"), name, (void *)args->buf,
861		    args->count);
862#endif
863	error = kern_readlink(td, name, UIO_SYSSPACE, args->buf, UIO_USERSPACE,
864	    args->count);
865	LFREEPATH(name);
866	return (error);
867}
868
869int
870linux_readlinkat(struct thread *td, struct linux_readlinkat_args *args)
871{
872	char *name;
873	int error, dfd;
874
875	dfd = (args->dfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->dfd;
876	LCONVPATHEXIST_AT(td, args->path, &name, dfd);
877
878#ifdef DEBUG
879	if (ldebug(readlinkat))
880		printf(ARGS(readlinkat, "%s, %p, %d"), name, (void *)args->buf,
881		    args->bufsiz);
882#endif
883
884	error = kern_readlinkat(td, dfd, name, UIO_SYSSPACE, args->buf,
885	    UIO_USERSPACE, args->bufsiz);
886	LFREEPATH(name);
887	return (error);
888}
889
890int
891linux_truncate(struct thread *td, struct linux_truncate_args *args)
892{
893	char *path;
894	int error;
895
896	LCONVPATHEXIST(td, args->path, &path);
897
898#ifdef DEBUG
899	if (ldebug(truncate))
900		printf(ARGS(truncate, "%s, %ld"), path, (long)args->length);
901#endif
902
903	error = kern_truncate(td, path, UIO_SYSSPACE, args->length);
904	LFREEPATH(path);
905	return (error);
906}
907
908int
909linux_truncate64(struct thread *td, struct linux_truncate64_args *args)
910{
911	char *path;
912	int error;
913
914	LCONVPATHEXIST(td, args->path, &path);
915
916#ifdef DEBUG
917	if (ldebug(truncate64))
918		printf(ARGS(truncate64, "%s, %jd"), path, args->length);
919#endif
920
921	error = kern_truncate(td, path, UIO_SYSSPACE, args->length);
922	LFREEPATH(path);
923	return (error);
924}
925int
926linux_ftruncate(struct thread *td, struct linux_ftruncate_args *args)
927{
928	struct ftruncate_args /* {
929		int fd;
930		int pad;
931		off_t length;
932		} */ nuap;
933
934	nuap.fd = args->fd;
935	nuap.length = args->length;
936	return (ftruncate(td, &nuap));
937}
938
939int
940linux_link(struct thread *td, struct linux_link_args *args)
941{
942	char *path, *to;
943	int error;
944
945	LCONVPATHEXIST(td, args->path, &path);
946	/* Expand LCONVPATHCREATE so that `path' can be freed on errors */
947	error = linux_emul_convpath(td, args->to, UIO_USERSPACE, &to, 1, AT_FDCWD);
948	if (to == NULL) {
949		LFREEPATH(path);
950		return (error);
951	}
952
953#ifdef DEBUG
954	if (ldebug(link))
955		printf(ARGS(link, "%s, %s"), path, to);
956#endif
957	error = kern_link(td, path, to, UIO_SYSSPACE);
958	LFREEPATH(path);
959	LFREEPATH(to);
960	return (error);
961}
962
963int
964linux_linkat(struct thread *td, struct linux_linkat_args *args)
965{
966	char *path, *to;
967	int error, olddfd, newdfd;
968
969	/*
970	 * They really introduced flags argument which is forbidden to
971	 * use.
972	 */
973	if (args->flags != 0)
974		return (EINVAL);
975
976	olddfd = (args->olddfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->olddfd;
977	newdfd = (args->newdfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->newdfd;
978	LCONVPATHEXIST_AT(td, args->oldname, &path, olddfd);
979	/* Expand LCONVPATHCREATE so that `path' can be freed on errors */
980	error = linux_emul_convpath(td, args->newname, UIO_USERSPACE, &to, 1, newdfd);
981	if (to == NULL) {
982		LFREEPATH(path);
983		return (error);
984	}
985
986#ifdef DEBUG
987	if (ldebug(linkat))
988		printf(ARGS(linkat, "%i, %s, %i, %s, %i"), args->olddfd, path,
989			args->newdfd, to, args->flags);
990#endif
991
992	error = kern_linkat(td, olddfd, newdfd, path, to, UIO_SYSSPACE, FOLLOW);
993	LFREEPATH(path);
994	LFREEPATH(to);
995	return (error);
996}
997
998int
999linux_fdatasync(td, uap)
1000	struct thread *td;
1001	struct linux_fdatasync_args *uap;
1002{
1003	struct fsync_args bsd;
1004
1005	bsd.fd = uap->fd;
1006	return fsync(td, &bsd);
1007}
1008
1009int
1010linux_pread(td, uap)
1011	struct thread *td;
1012	struct linux_pread_args *uap;
1013{
1014	struct pread_args bsd;
1015	struct vnode *vp;
1016	int error;
1017
1018	bsd.fd = uap->fd;
1019	bsd.buf = uap->buf;
1020	bsd.nbyte = uap->nbyte;
1021	bsd.offset = uap->offset;
1022
1023	error = pread(td, &bsd);
1024
1025	if (error == 0) {
1026   	   	/* This seems to violate POSIX but linux does it */
1027   	   	if ((error = fgetvp(td, uap->fd, &vp)) != 0)
1028   		   	return (error);
1029		if (vp->v_type == VDIR) {
1030   		   	vrele(vp);
1031			return (EISDIR);
1032		}
1033		vrele(vp);
1034	}
1035
1036	return (error);
1037}
1038
1039int
1040linux_pwrite(td, uap)
1041	struct thread *td;
1042	struct linux_pwrite_args *uap;
1043{
1044	struct pwrite_args bsd;
1045
1046	bsd.fd = uap->fd;
1047	bsd.buf = uap->buf;
1048	bsd.nbyte = uap->nbyte;
1049	bsd.offset = uap->offset;
1050	return pwrite(td, &bsd);
1051}
1052
1053int
1054linux_mount(struct thread *td, struct linux_mount_args *args)
1055{
1056	struct ufs_args ufs;
1057	char fstypename[MFSNAMELEN];
1058	char mntonname[MNAMELEN], mntfromname[MNAMELEN];
1059	int error;
1060	int fsflags;
1061	void *fsdata;
1062
1063	error = copyinstr(args->filesystemtype, fstypename, MFSNAMELEN - 1,
1064	    NULL);
1065	if (error)
1066		return (error);
1067	error = copyinstr(args->specialfile, mntfromname, MNAMELEN - 1, NULL);
1068	if (error)
1069		return (error);
1070	error = copyinstr(args->dir, mntonname, MNAMELEN - 1, NULL);
1071	if (error)
1072		return (error);
1073
1074#ifdef DEBUG
1075	if (ldebug(mount))
1076		printf(ARGS(mount, "%s, %s, %s"),
1077		    fstypename, mntfromname, mntonname);
1078#endif
1079
1080	if (strcmp(fstypename, "ext2") == 0) {
1081		strcpy(fstypename, "ext2fs");
1082		fsdata = &ufs;
1083		ufs.fspec = mntfromname;
1084#define DEFAULT_ROOTID		-2
1085		ufs.export.ex_root = DEFAULT_ROOTID;
1086		ufs.export.ex_flags =
1087		    args->rwflag & LINUX_MS_RDONLY ? MNT_EXRDONLY : 0;
1088	} else if (strcmp(fstypename, "proc") == 0) {
1089		strcpy(fstypename, "linprocfs");
1090		fsdata = NULL;
1091	} else {
1092		return (ENODEV);
1093	}
1094
1095	fsflags = 0;
1096
1097	if ((args->rwflag & 0xffff0000) == 0xc0ed0000) {
1098		/*
1099		 * Linux SYNC flag is not included; the closest equivalent
1100		 * FreeBSD has is !ASYNC, which is our default.
1101		 */
1102		if (args->rwflag & LINUX_MS_RDONLY)
1103			fsflags |= MNT_RDONLY;
1104		if (args->rwflag & LINUX_MS_NOSUID)
1105			fsflags |= MNT_NOSUID;
1106		if (args->rwflag & LINUX_MS_NOEXEC)
1107			fsflags |= MNT_NOEXEC;
1108		if (args->rwflag & LINUX_MS_REMOUNT)
1109			fsflags |= MNT_UPDATE;
1110	}
1111
1112	if (strcmp(fstypename, "linprocfs") == 0) {
1113		error = kernel_vmount(fsflags,
1114			"fstype", fstypename,
1115			"fspath", mntonname,
1116			NULL);
1117	} else
1118		error = EOPNOTSUPP;
1119	return (error);
1120}
1121
1122int
1123linux_oldumount(struct thread *td, struct linux_oldumount_args *args)
1124{
1125	struct linux_umount_args args2;
1126
1127	args2.path = args->path;
1128	args2.flags = 0;
1129	return (linux_umount(td, &args2));
1130}
1131
1132int
1133linux_umount(struct thread *td, struct linux_umount_args *args)
1134{
1135	struct unmount_args bsd;
1136
1137	bsd.path = args->path;
1138	bsd.flags = args->flags;	/* XXX correct? */
1139	return (unmount(td, &bsd));
1140}
1141
1142/*
1143 * fcntl family of syscalls
1144 */
1145
1146struct l_flock {
1147	l_short		l_type;
1148	l_short		l_whence;
1149	l_off_t		l_start;
1150	l_off_t		l_len;
1151	l_pid_t		l_pid;
1152}
1153#if defined(__amd64__) && defined(COMPAT_LINUX32)
1154__packed
1155#endif
1156;
1157
1158static void
1159linux_to_bsd_flock(struct l_flock *linux_flock, struct flock *bsd_flock)
1160{
1161	switch (linux_flock->l_type) {
1162	case LINUX_F_RDLCK:
1163		bsd_flock->l_type = F_RDLCK;
1164		break;
1165	case LINUX_F_WRLCK:
1166		bsd_flock->l_type = F_WRLCK;
1167		break;
1168	case LINUX_F_UNLCK:
1169		bsd_flock->l_type = F_UNLCK;
1170		break;
1171	default:
1172		bsd_flock->l_type = -1;
1173		break;
1174	}
1175	bsd_flock->l_whence = linux_flock->l_whence;
1176	bsd_flock->l_start = (off_t)linux_flock->l_start;
1177	bsd_flock->l_len = (off_t)linux_flock->l_len;
1178	bsd_flock->l_pid = (pid_t)linux_flock->l_pid;
1179	bsd_flock->l_sysid = 0;
1180}
1181
1182static void
1183bsd_to_linux_flock(struct flock *bsd_flock, struct l_flock *linux_flock)
1184{
1185	switch (bsd_flock->l_type) {
1186	case F_RDLCK:
1187		linux_flock->l_type = LINUX_F_RDLCK;
1188		break;
1189	case F_WRLCK:
1190		linux_flock->l_type = LINUX_F_WRLCK;
1191		break;
1192	case F_UNLCK:
1193		linux_flock->l_type = LINUX_F_UNLCK;
1194		break;
1195	}
1196	linux_flock->l_whence = bsd_flock->l_whence;
1197	linux_flock->l_start = (l_off_t)bsd_flock->l_start;
1198	linux_flock->l_len = (l_off_t)bsd_flock->l_len;
1199	linux_flock->l_pid = (l_pid_t)bsd_flock->l_pid;
1200}
1201
1202#if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32))
1203struct l_flock64 {
1204	l_short		l_type;
1205	l_short		l_whence;
1206	l_loff_t	l_start;
1207	l_loff_t	l_len;
1208	l_pid_t		l_pid;
1209}
1210#if defined(__amd64__) && defined(COMPAT_LINUX32)
1211__packed
1212#endif
1213;
1214
1215static void
1216linux_to_bsd_flock64(struct l_flock64 *linux_flock, struct flock *bsd_flock)
1217{
1218	switch (linux_flock->l_type) {
1219	case LINUX_F_RDLCK:
1220		bsd_flock->l_type = F_RDLCK;
1221		break;
1222	case LINUX_F_WRLCK:
1223		bsd_flock->l_type = F_WRLCK;
1224		break;
1225	case LINUX_F_UNLCK:
1226		bsd_flock->l_type = F_UNLCK;
1227		break;
1228	default:
1229		bsd_flock->l_type = -1;
1230		break;
1231	}
1232	bsd_flock->l_whence = linux_flock->l_whence;
1233	bsd_flock->l_start = (off_t)linux_flock->l_start;
1234	bsd_flock->l_len = (off_t)linux_flock->l_len;
1235	bsd_flock->l_pid = (pid_t)linux_flock->l_pid;
1236	bsd_flock->l_sysid = 0;
1237}
1238
1239static void
1240bsd_to_linux_flock64(struct flock *bsd_flock, struct l_flock64 *linux_flock)
1241{
1242	switch (bsd_flock->l_type) {
1243	case F_RDLCK:
1244		linux_flock->l_type = LINUX_F_RDLCK;
1245		break;
1246	case F_WRLCK:
1247		linux_flock->l_type = LINUX_F_WRLCK;
1248		break;
1249	case F_UNLCK:
1250		linux_flock->l_type = LINUX_F_UNLCK;
1251		break;
1252	}
1253	linux_flock->l_whence = bsd_flock->l_whence;
1254	linux_flock->l_start = (l_loff_t)bsd_flock->l_start;
1255	linux_flock->l_len = (l_loff_t)bsd_flock->l_len;
1256	linux_flock->l_pid = (l_pid_t)bsd_flock->l_pid;
1257}
1258#endif /* __i386__ || (__amd64__ && COMPAT_LINUX32) */
1259
1260static int
1261fcntl_common(struct thread *td, struct linux_fcntl64_args *args)
1262{
1263	struct l_flock linux_flock;
1264	struct flock bsd_flock;
1265	struct file *fp;
1266	long arg;
1267	int error, result;
1268
1269	switch (args->cmd) {
1270	case LINUX_F_DUPFD:
1271		return (kern_fcntl(td, args->fd, F_DUPFD, args->arg));
1272
1273	case LINUX_F_GETFD:
1274		return (kern_fcntl(td, args->fd, F_GETFD, 0));
1275
1276	case LINUX_F_SETFD:
1277		return (kern_fcntl(td, args->fd, F_SETFD, args->arg));
1278
1279	case LINUX_F_GETFL:
1280		error = kern_fcntl(td, args->fd, F_GETFL, 0);
1281		result = td->td_retval[0];
1282		td->td_retval[0] = 0;
1283		if (result & O_RDONLY)
1284			td->td_retval[0] |= LINUX_O_RDONLY;
1285		if (result & O_WRONLY)
1286			td->td_retval[0] |= LINUX_O_WRONLY;
1287		if (result & O_RDWR)
1288			td->td_retval[0] |= LINUX_O_RDWR;
1289		if (result & O_NDELAY)
1290			td->td_retval[0] |= LINUX_O_NONBLOCK;
1291		if (result & O_APPEND)
1292			td->td_retval[0] |= LINUX_O_APPEND;
1293		if (result & O_FSYNC)
1294			td->td_retval[0] |= LINUX_O_SYNC;
1295		if (result & O_ASYNC)
1296			td->td_retval[0] |= LINUX_FASYNC;
1297#ifdef LINUX_O_NOFOLLOW
1298		if (result & O_NOFOLLOW)
1299			td->td_retval[0] |= LINUX_O_NOFOLLOW;
1300#endif
1301#ifdef LINUX_O_DIRECT
1302		if (result & O_DIRECT)
1303			td->td_retval[0] |= LINUX_O_DIRECT;
1304#endif
1305		return (error);
1306
1307	case LINUX_F_SETFL:
1308		arg = 0;
1309		if (args->arg & LINUX_O_NDELAY)
1310			arg |= O_NONBLOCK;
1311		if (args->arg & LINUX_O_APPEND)
1312			arg |= O_APPEND;
1313		if (args->arg & LINUX_O_SYNC)
1314			arg |= O_FSYNC;
1315		if (args->arg & LINUX_FASYNC)
1316			arg |= O_ASYNC;
1317#ifdef LINUX_O_NOFOLLOW
1318		if (args->arg & LINUX_O_NOFOLLOW)
1319			arg |= O_NOFOLLOW;
1320#endif
1321#ifdef LINUX_O_DIRECT
1322		if (args->arg & LINUX_O_DIRECT)
1323			arg |= O_DIRECT;
1324#endif
1325		return (kern_fcntl(td, args->fd, F_SETFL, arg));
1326
1327	case LINUX_F_GETLK:
1328		error = copyin((void *)args->arg, &linux_flock,
1329		    sizeof(linux_flock));
1330		if (error)
1331			return (error);
1332		linux_to_bsd_flock(&linux_flock, &bsd_flock);
1333		error = kern_fcntl(td, args->fd, F_GETLK, (intptr_t)&bsd_flock);
1334		if (error)
1335			return (error);
1336		bsd_to_linux_flock(&bsd_flock, &linux_flock);
1337		return (copyout(&linux_flock, (void *)args->arg,
1338		    sizeof(linux_flock)));
1339
1340	case LINUX_F_SETLK:
1341		error = copyin((void *)args->arg, &linux_flock,
1342		    sizeof(linux_flock));
1343		if (error)
1344			return (error);
1345		linux_to_bsd_flock(&linux_flock, &bsd_flock);
1346		return (kern_fcntl(td, args->fd, F_SETLK,
1347		    (intptr_t)&bsd_flock));
1348
1349	case LINUX_F_SETLKW:
1350		error = copyin((void *)args->arg, &linux_flock,
1351		    sizeof(linux_flock));
1352		if (error)
1353			return (error);
1354		linux_to_bsd_flock(&linux_flock, &bsd_flock);
1355		return (kern_fcntl(td, args->fd, F_SETLKW,
1356		     (intptr_t)&bsd_flock));
1357
1358	case LINUX_F_GETOWN:
1359		return (kern_fcntl(td, args->fd, F_GETOWN, 0));
1360
1361	case LINUX_F_SETOWN:
1362		/*
1363		 * XXX some Linux applications depend on F_SETOWN having no
1364		 * significant effect for pipes (SIGIO is not delivered for
1365		 * pipes under Linux-2.2.35 at least).
1366		 */
1367		error = fget(td, args->fd, &fp);
1368		if (error)
1369			return (error);
1370		if (fp->f_type == DTYPE_PIPE) {
1371			fdrop(fp, td);
1372			return (EINVAL);
1373		}
1374		fdrop(fp, td);
1375
1376		return (kern_fcntl(td, args->fd, F_SETOWN, args->arg));
1377	}
1378
1379	return (EINVAL);
1380}
1381
1382int
1383linux_fcntl(struct thread *td, struct linux_fcntl_args *args)
1384{
1385	struct linux_fcntl64_args args64;
1386
1387#ifdef DEBUG
1388	if (ldebug(fcntl))
1389		printf(ARGS(fcntl, "%d, %08x, *"), args->fd, args->cmd);
1390#endif
1391
1392	args64.fd = args->fd;
1393	args64.cmd = args->cmd;
1394	args64.arg = args->arg;
1395	return (fcntl_common(td, &args64));
1396}
1397
1398#if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32))
1399int
1400linux_fcntl64(struct thread *td, struct linux_fcntl64_args *args)
1401{
1402	struct l_flock64 linux_flock;
1403	struct flock bsd_flock;
1404	int error;
1405
1406#ifdef DEBUG
1407	if (ldebug(fcntl64))
1408		printf(ARGS(fcntl64, "%d, %08x, *"), args->fd, args->cmd);
1409#endif
1410
1411	switch (args->cmd) {
1412	case LINUX_F_GETLK64:
1413		error = copyin((void *)args->arg, &linux_flock,
1414		    sizeof(linux_flock));
1415		if (error)
1416			return (error);
1417		linux_to_bsd_flock64(&linux_flock, &bsd_flock);
1418		error = kern_fcntl(td, args->fd, F_GETLK, (intptr_t)&bsd_flock);
1419		if (error)
1420			return (error);
1421		bsd_to_linux_flock64(&bsd_flock, &linux_flock);
1422		return (copyout(&linux_flock, (void *)args->arg,
1423			    sizeof(linux_flock)));
1424
1425	case LINUX_F_SETLK64:
1426		error = copyin((void *)args->arg, &linux_flock,
1427		    sizeof(linux_flock));
1428		if (error)
1429			return (error);
1430		linux_to_bsd_flock64(&linux_flock, &bsd_flock);
1431		return (kern_fcntl(td, args->fd, F_SETLK,
1432		    (intptr_t)&bsd_flock));
1433
1434	case LINUX_F_SETLKW64:
1435		error = copyin((void *)args->arg, &linux_flock,
1436		    sizeof(linux_flock));
1437		if (error)
1438			return (error);
1439		linux_to_bsd_flock64(&linux_flock, &bsd_flock);
1440		return (kern_fcntl(td, args->fd, F_SETLKW,
1441		    (intptr_t)&bsd_flock));
1442	}
1443
1444	return (fcntl_common(td, args));
1445}
1446#endif /* __i386__ || (__amd64__ && COMPAT_LINUX32) */
1447
1448int
1449linux_chown(struct thread *td, struct linux_chown_args *args)
1450{
1451	char *path;
1452	int error;
1453
1454	LCONVPATHEXIST(td, args->path, &path);
1455
1456#ifdef DEBUG
1457	if (ldebug(chown))
1458		printf(ARGS(chown, "%s, %d, %d"), path, args->uid, args->gid);
1459#endif
1460	error = kern_chown(td, path, UIO_SYSSPACE, args->uid, args->gid);
1461	LFREEPATH(path);
1462	return (error);
1463}
1464
1465int
1466linux_fchownat(struct thread *td, struct linux_fchownat_args *args)
1467{
1468	char *path;
1469	int error, dfd, follow;
1470
1471	if (args->flag & ~LINUX_AT_SYMLINK_NOFOLLOW)
1472		return (EINVAL);
1473
1474	dfd = (args->dfd == LINUX_AT_FDCWD) ? AT_FDCWD :  args->dfd;
1475	LCONVPATHEXIST_AT(td, args->filename, &path, dfd);
1476
1477#ifdef DEBUG
1478	if (ldebug(fchownat))
1479		printf(ARGS(fchownat, "%s, %d, %d"), path, args->uid, args->gid);
1480#endif
1481
1482	follow = (args->flag & LINUX_AT_SYMLINK_NOFOLLOW) == 0 ? 0 :
1483	    AT_SYMLINK_NOFOLLOW;
1484	error = kern_fchownat(td, dfd, path, UIO_SYSSPACE, args->uid, args->gid,
1485	    follow);
1486	LFREEPATH(path);
1487	return (error);
1488}
1489
1490int
1491linux_lchown(struct thread *td, struct linux_lchown_args *args)
1492{
1493	char *path;
1494	int error;
1495
1496	LCONVPATHEXIST(td, args->path, &path);
1497
1498#ifdef DEBUG
1499	if (ldebug(lchown))
1500		printf(ARGS(lchown, "%s, %d, %d"), path, args->uid, args->gid);
1501#endif
1502	error = kern_lchown(td, path, UIO_SYSSPACE, args->uid, args->gid);
1503	LFREEPATH(path);
1504	return (error);
1505}
1506