linux_file.c revision 163606
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 163606 2006-10-22 11:52:19Z rwatson $");
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/proc.h>
47#include <sys/stat.h>
48#include <sys/syscallsubr.h>
49#include <sys/sysproto.h>
50#include <sys/tty.h>
51#include <sys/unistd.h>
52#include <sys/vnode.h>
53
54#include <security/mac/mac_framework.h>
55
56#include <ufs/ufs/extattr.h>
57#include <ufs/ufs/quota.h>
58#include <ufs/ufs/ufsmount.h>
59
60#ifdef COMPAT_LINUX32
61#include <machine/../linux32/linux.h>
62#include <machine/../linux32/linux32_proto.h>
63#else
64#include <machine/../linux/linux.h>
65#include <machine/../linux/linux_proto.h>
66#endif
67#include <compat/linux/linux_util.h>
68
69int
70linux_creat(struct thread *td, struct linux_creat_args *args)
71{
72    char *path;
73    int error;
74
75    LCONVPATHEXIST(td, args->path, &path);
76
77#ifdef DEBUG
78	if (ldebug(creat))
79		printf(ARGS(creat, "%s, %d"), path, args->mode);
80#endif
81    error = kern_open(td, path, UIO_SYSSPACE, O_WRONLY | O_CREAT | O_TRUNC,
82	args->mode);
83    LFREEPATH(path);
84    return (error);
85}
86
87int
88linux_open(struct thread *td, struct linux_open_args *args)
89{
90    struct proc *p = td->td_proc;
91    char *path;
92    int bsd_flags, error;
93
94    if (args->flags & LINUX_O_CREAT)
95	LCONVPATHCREAT(td, args->path, &path);
96    else
97	LCONVPATHEXIST(td, args->path, &path);
98
99#ifdef DEBUG
100	if (ldebug(open))
101		printf(ARGS(open, "%s, 0x%x, 0x%x"),
102		    path, args->flags, args->mode);
103#endif
104    bsd_flags = 0;
105    if (args->flags & LINUX_O_RDONLY)
106	bsd_flags |= O_RDONLY;
107    if (args->flags & LINUX_O_WRONLY)
108	bsd_flags |= O_WRONLY;
109    if (args->flags & LINUX_O_RDWR)
110	bsd_flags |= O_RDWR;
111    if (args->flags & LINUX_O_NDELAY)
112	bsd_flags |= O_NONBLOCK;
113    if (args->flags & LINUX_O_APPEND)
114	bsd_flags |= O_APPEND;
115    if (args->flags & LINUX_O_SYNC)
116	bsd_flags |= O_FSYNC;
117    if (args->flags & LINUX_O_NONBLOCK)
118	bsd_flags |= O_NONBLOCK;
119    if (args->flags & LINUX_FASYNC)
120	bsd_flags |= O_ASYNC;
121    if (args->flags & LINUX_O_CREAT)
122	bsd_flags |= O_CREAT;
123    if (args->flags & LINUX_O_TRUNC)
124	bsd_flags |= O_TRUNC;
125    if (args->flags & LINUX_O_EXCL)
126	bsd_flags |= O_EXCL;
127    if (args->flags & LINUX_O_NOCTTY)
128	bsd_flags |= O_NOCTTY;
129
130    error = kern_open(td, path, UIO_SYSSPACE, bsd_flags, args->mode);
131    PROC_LOCK(p);
132    if (!error && !(bsd_flags & O_NOCTTY) &&
133	SESS_LEADER(p) && !(p->p_flag & P_CONTROLT)) {
134	struct file *fp;
135
136	PROC_UNLOCK(p);
137	error = fget(td, td->td_retval[0], &fp);
138	if (!error) {
139		if (fp->f_type == DTYPE_VNODE)
140			fo_ioctl(fp, TIOCSCTTY, (caddr_t) 0, td->td_ucred,
141			    td);
142	    fdrop(fp, td);
143	}
144    } else {
145	PROC_UNLOCK(p);
146#ifdef DEBUG
147	if (ldebug(open))
148		printf(LMSG("open returns error %d"), error);
149#endif
150    }
151    LFREEPATH(path);
152    return error;
153}
154
155int
156linux_lseek(struct thread *td, struct linux_lseek_args *args)
157{
158
159    struct lseek_args /* {
160	int fd;
161	int pad;
162	off_t offset;
163	int whence;
164    } */ tmp_args;
165    int error;
166
167#ifdef DEBUG
168	if (ldebug(lseek))
169		printf(ARGS(lseek, "%d, %ld, %d"),
170		    args->fdes, (long)args->off, args->whence);
171#endif
172    tmp_args.fd = args->fdes;
173    tmp_args.offset = (off_t)args->off;
174    tmp_args.whence = args->whence;
175    error = lseek(td, &tmp_args);
176    return error;
177}
178
179int
180linux_llseek(struct thread *td, struct linux_llseek_args *args)
181{
182	struct lseek_args bsd_args;
183	int error;
184	off_t off;
185
186#ifdef DEBUG
187	if (ldebug(llseek))
188		printf(ARGS(llseek, "%d, %d:%d, %d"),
189		    args->fd, args->ohigh, args->olow, args->whence);
190#endif
191	off = (args->olow) | (((off_t) args->ohigh) << 32);
192
193	bsd_args.fd = args->fd;
194	bsd_args.offset = off;
195	bsd_args.whence = args->whence;
196
197	if ((error = lseek(td, &bsd_args)))
198		return error;
199
200	if ((error = copyout(td->td_retval, args->res, sizeof (off_t))))
201		return error;
202
203	td->td_retval[0] = 0;
204	return 0;
205}
206
207int
208linux_readdir(struct thread *td, struct linux_readdir_args *args)
209{
210	struct linux_getdents_args lda;
211
212	lda.fd = args->fd;
213	lda.dent = args->dent;
214	lda.count = 1;
215	return linux_getdents(td, &lda);
216}
217
218/*
219 * Note that linux_getdents(2) and linux_getdents64(2) have the same
220 * arguments. They only differ in the definition of struct dirent they
221 * operate on. We use this to common the code, with the exception of
222 * accessing struct dirent. Note that linux_readdir(2) is implemented
223 * by means of linux_getdents(2). In this case we never operate on
224 * struct dirent64 and thus don't need to handle it...
225 */
226
227struct l_dirent {
228	l_long		d_ino;
229	l_off_t		d_off;
230	l_ushort	d_reclen;
231	char		d_name[LINUX_NAME_MAX + 1];
232};
233
234struct l_dirent64 {
235	uint64_t	d_ino;
236	int64_t		d_off;
237	l_ushort	d_reclen;
238	u_char		d_type;
239	char		d_name[LINUX_NAME_MAX + 1];
240};
241
242#define LINUX_RECLEN(de,namlen) \
243    ALIGN((((char *)&(de)->d_name - (char *)de) + (namlen) + 1))
244
245#define	LINUX_DIRBLKSIZ		512
246
247static int
248getdents_common(struct thread *td, struct linux_getdents64_args *args,
249    int is64bit)
250{
251	struct dirent *bdp;
252	struct vnode *vp;
253	caddr_t inp, buf;		/* BSD-format */
254	int len, reclen;		/* BSD-format */
255	caddr_t outp;			/* Linux-format */
256	int resid, linuxreclen=0;	/* Linux-format */
257	struct file *fp;
258	struct uio auio;
259	struct iovec aiov;
260	off_t off;
261	struct l_dirent linux_dirent;
262	struct l_dirent64 linux_dirent64;
263	int buflen, error, eofflag, nbytes, justone;
264	u_long *cookies = NULL, *cookiep;
265	int ncookies, vfslocked;
266
267	nbytes = args->count;
268	if (nbytes == 1) {
269		/* readdir(2) case. Always struct dirent. */
270		if (is64bit)
271			return (EINVAL);
272		nbytes = sizeof(linux_dirent);
273		justone = 1;
274	} else
275		justone = 0;
276
277	if ((error = getvnode(td->td_proc->p_fd, args->fd, &fp)) != 0)
278		return (error);
279
280	if ((fp->f_flag & FREAD) == 0) {
281		fdrop(fp, td);
282		return (EBADF);
283	}
284
285	vp = fp->f_vnode;
286	vfslocked = VFS_LOCK_GIANT(vp->v_mount);
287	if (vp->v_type != VDIR) {
288		VFS_UNLOCK_GIANT(vfslocked);
289		fdrop(fp, td);
290		return (EINVAL);
291	}
292
293	off = fp->f_offset;
294
295	buflen = max(LINUX_DIRBLKSIZ, nbytes);
296	buflen = min(buflen, MAXBSIZE);
297	buf = malloc(buflen, M_TEMP, M_WAITOK);
298	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td);
299
300again:
301	aiov.iov_base = buf;
302	aiov.iov_len = buflen;
303	auio.uio_iov = &aiov;
304	auio.uio_iovcnt = 1;
305	auio.uio_rw = UIO_READ;
306	auio.uio_segflg = UIO_SYSSPACE;
307	auio.uio_td = td;
308	auio.uio_resid = buflen;
309	auio.uio_offset = off;
310
311	if (cookies) {
312		free(cookies, M_TEMP);
313		cookies = NULL;
314	}
315
316#ifdef MAC
317	/*
318	 * Do directory search MAC check using non-cached credentials.
319	 */
320	if ((error = mac_check_vnode_readdir(td->td_ucred, vp)))
321		goto out;
322#endif /* MAC */
323	if ((error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, &ncookies,
324		 &cookies)))
325		goto out;
326
327	inp = buf;
328	outp = (caddr_t)args->dirent;
329	resid = nbytes;
330	if ((len = buflen - auio.uio_resid) <= 0)
331		goto eof;
332
333	cookiep = cookies;
334
335	if (cookies) {
336		/*
337		 * When using cookies, the vfs has the option of reading from
338		 * a different offset than that supplied (UFS truncates the
339		 * offset to a block boundary to make sure that it never reads
340		 * partway through a directory entry, even if the directory
341		 * has been compacted).
342		 */
343		while (len > 0 && ncookies > 0 && *cookiep <= off) {
344			bdp = (struct dirent *) inp;
345			len -= bdp->d_reclen;
346			inp += bdp->d_reclen;
347			cookiep++;
348			ncookies--;
349		}
350	}
351
352	while (len > 0) {
353		if (cookiep && ncookies == 0)
354			break;
355		bdp = (struct dirent *) inp;
356		reclen = bdp->d_reclen;
357		if (reclen & 3) {
358			error = EFAULT;
359			goto out;
360		}
361
362		if (bdp->d_fileno == 0) {
363			inp += reclen;
364			if (cookiep) {
365				off = *cookiep++;
366				ncookies--;
367			} else
368				off += reclen;
369
370			len -= reclen;
371			continue;
372		}
373
374		linuxreclen = (is64bit)
375		    ? LINUX_RECLEN(&linux_dirent64, bdp->d_namlen)
376		    : LINUX_RECLEN(&linux_dirent, bdp->d_namlen);
377
378		if (reclen > len || resid < linuxreclen) {
379			outp++;
380			break;
381		}
382
383		if (justone) {
384			/* readdir(2) case. */
385			linux_dirent.d_ino = (l_long)bdp->d_fileno;
386			linux_dirent.d_off = (l_off_t)linuxreclen;
387			linux_dirent.d_reclen = (l_ushort)bdp->d_namlen;
388			strcpy(linux_dirent.d_name, bdp->d_name);
389			error = copyout(&linux_dirent, outp, linuxreclen);
390		} else {
391			if (is64bit) {
392				linux_dirent64.d_ino = bdp->d_fileno;
393				linux_dirent64.d_off = (cookiep)
394				    ? (l_off_t)*cookiep
395				    : (l_off_t)(off + reclen);
396				linux_dirent64.d_reclen =
397				    (l_ushort)linuxreclen;
398				linux_dirent64.d_type = bdp->d_type;
399				strcpy(linux_dirent64.d_name, bdp->d_name);
400				error = copyout(&linux_dirent64, outp,
401				    linuxreclen);
402			} else {
403				linux_dirent.d_ino = bdp->d_fileno;
404				linux_dirent.d_off = (cookiep)
405				    ? (l_off_t)*cookiep
406				    : (l_off_t)(off + reclen);
407				linux_dirent.d_reclen = (l_ushort)linuxreclen;
408				strcpy(linux_dirent.d_name, bdp->d_name);
409				error = copyout(&linux_dirent, outp,
410				    linuxreclen);
411			}
412		}
413		if (error)
414			goto out;
415
416		inp += reclen;
417		if (cookiep) {
418			off = *cookiep++;
419			ncookies--;
420		} else
421			off += reclen;
422
423		outp += linuxreclen;
424		resid -= linuxreclen;
425		len -= reclen;
426		if (justone)
427			break;
428	}
429
430	if (outp == (caddr_t)args->dirent)
431		goto again;
432
433	fp->f_offset = off;
434	if (justone)
435		nbytes = resid + linuxreclen;
436
437eof:
438	td->td_retval[0] = nbytes - resid;
439
440out:
441	if (cookies)
442		free(cookies, M_TEMP);
443
444	VOP_UNLOCK(vp, 0, td);
445	VFS_UNLOCK_GIANT(vfslocked);
446	fdrop(fp, td);
447	free(buf, M_TEMP);
448	return (error);
449}
450
451int
452linux_getdents(struct thread *td, struct linux_getdents_args *args)
453{
454
455#ifdef DEBUG
456	if (ldebug(getdents))
457		printf(ARGS(getdents, "%d, *, %d"), args->fd, args->count);
458#endif
459
460	return (getdents_common(td, (struct linux_getdents64_args*)args, 0));
461}
462
463int
464linux_getdents64(struct thread *td, struct linux_getdents64_args *args)
465{
466
467#ifdef DEBUG
468	if (ldebug(getdents64))
469		printf(ARGS(getdents64, "%d, *, %d"), args->fd, args->count);
470#endif
471
472	return (getdents_common(td, args, 1));
473}
474
475/*
476 * These exist mainly for hooks for doing /compat/linux translation.
477 */
478
479int
480linux_access(struct thread *td, struct linux_access_args *args)
481{
482	char *path;
483	int error;
484
485	/* linux convention */
486	if (args->flags & ~(F_OK | X_OK | W_OK | R_OK))
487		return (EINVAL);
488
489	LCONVPATHEXIST(td, args->path, &path);
490
491#ifdef DEBUG
492	if (ldebug(access))
493		printf(ARGS(access, "%s, %d"), path, args->flags);
494#endif
495	error = kern_access(td, path, UIO_SYSSPACE, args->flags);
496	LFREEPATH(path);
497
498	return (error);
499}
500
501int
502linux_unlink(struct thread *td, struct linux_unlink_args *args)
503{
504	char *path;
505	int error;
506	struct stat st;
507
508	LCONVPATHEXIST(td, args->path, &path);
509
510#ifdef DEBUG
511	if (ldebug(unlink))
512		printf(ARGS(unlink, "%s"), path);
513#endif
514
515	error = kern_unlink(td, path, UIO_SYSSPACE);
516	if (error == EPERM)
517		/* Introduce POSIX noncompliant behaviour of Linux */
518		if (kern_stat(td, path, UIO_SYSSPACE, &st) == 0)
519			if (S_ISDIR(st.st_mode))
520				error = EISDIR;
521	LFREEPATH(path);
522	return (error);
523}
524
525int
526linux_chdir(struct thread *td, struct linux_chdir_args *args)
527{
528	char *path;
529	int error;
530
531	LCONVPATHEXIST(td, args->path, &path);
532
533#ifdef DEBUG
534	if (ldebug(chdir))
535		printf(ARGS(chdir, "%s"), path);
536#endif
537	error = kern_chdir(td, path, UIO_SYSSPACE);
538	LFREEPATH(path);
539	return (error);
540}
541
542int
543linux_chmod(struct thread *td, struct linux_chmod_args *args)
544{
545	char *path;
546	int error;
547
548	LCONVPATHEXIST(td, args->path, &path);
549
550#ifdef DEBUG
551	if (ldebug(chmod))
552		printf(ARGS(chmod, "%s, %d"), path, args->mode);
553#endif
554	error = kern_chmod(td, path, UIO_SYSSPACE, args->mode);
555	LFREEPATH(path);
556	return (error);
557}
558
559int
560linux_mkdir(struct thread *td, struct linux_mkdir_args *args)
561{
562	char *path;
563	int error;
564
565	LCONVPATHCREAT(td, args->path, &path);
566
567#ifdef DEBUG
568	if (ldebug(mkdir))
569		printf(ARGS(mkdir, "%s, %d"), path, args->mode);
570#endif
571	error = kern_mkdir(td, path, UIO_SYSSPACE, args->mode);
572	LFREEPATH(path);
573	return (error);
574}
575
576int
577linux_rmdir(struct thread *td, struct linux_rmdir_args *args)
578{
579	char *path;
580	int error;
581
582	LCONVPATHEXIST(td, args->path, &path);
583
584#ifdef DEBUG
585	if (ldebug(rmdir))
586		printf(ARGS(rmdir, "%s"), path);
587#endif
588	error = kern_rmdir(td, path, UIO_SYSSPACE);
589	LFREEPATH(path);
590	return (error);
591}
592
593int
594linux_rename(struct thread *td, struct linux_rename_args *args)
595{
596	char *from, *to;
597	int error;
598
599	LCONVPATHEXIST(td, args->from, &from);
600	/* Expand LCONVPATHCREATE so that `from' can be freed on errors */
601	error = linux_emul_convpath(td, args->to, UIO_USERSPACE, &to, 1);
602	if (to == NULL) {
603		LFREEPATH(from);
604		return (error);
605	}
606
607#ifdef DEBUG
608	if (ldebug(rename))
609		printf(ARGS(rename, "%s, %s"), from, to);
610#endif
611	error = kern_rename(td, from, to, UIO_SYSSPACE);
612	LFREEPATH(from);
613	LFREEPATH(to);
614	return (error);
615}
616
617int
618linux_symlink(struct thread *td, struct linux_symlink_args *args)
619{
620	char *path, *to;
621	int error;
622
623	LCONVPATHEXIST(td, args->path, &path);
624	/* Expand LCONVPATHCREATE so that `path' can be freed on errors */
625	error = linux_emul_convpath(td, args->to, UIO_USERSPACE, &to, 1);
626	if (to == NULL) {
627		LFREEPATH(path);
628		return (error);
629	}
630
631#ifdef DEBUG
632	if (ldebug(symlink))
633		printf(ARGS(symlink, "%s, %s"), path, to);
634#endif
635	error = kern_symlink(td, path, to, UIO_SYSSPACE);
636	LFREEPATH(path);
637	LFREEPATH(to);
638	return (error);
639}
640
641int
642linux_readlink(struct thread *td, struct linux_readlink_args *args)
643{
644	char *name;
645	int error;
646
647	LCONVPATHEXIST(td, args->name, &name);
648
649#ifdef DEBUG
650	if (ldebug(readlink))
651		printf(ARGS(readlink, "%s, %p, %d"), name, (void *)args->buf,
652		    args->count);
653#endif
654	error = kern_readlink(td, name, UIO_SYSSPACE, args->buf, UIO_USERSPACE,
655	    args->count);
656	LFREEPATH(name);
657	return (error);
658}
659
660int
661linux_truncate(struct thread *td, struct linux_truncate_args *args)
662{
663	char *path;
664	int error;
665
666	LCONVPATHEXIST(td, args->path, &path);
667
668#ifdef DEBUG
669	if (ldebug(truncate))
670		printf(ARGS(truncate, "%s, %ld"), path, (long)args->length);
671#endif
672
673	error = kern_truncate(td, path, UIO_SYSSPACE, args->length);
674	LFREEPATH(path);
675	return (error);
676}
677
678int
679linux_ftruncate(struct thread *td, struct linux_ftruncate_args *args)
680{
681	struct ftruncate_args /* {
682		int fd;
683		int pad;
684		off_t length;
685		} */ nuap;
686
687	nuap.fd = args->fd;
688	nuap.pad = 0;
689	nuap.length = args->length;
690	return (ftruncate(td, &nuap));
691}
692
693int
694linux_link(struct thread *td, struct linux_link_args *args)
695{
696	char *path, *to;
697	int error;
698
699	LCONVPATHEXIST(td, args->path, &path);
700	/* Expand LCONVPATHCREATE so that `path' can be freed on errors */
701	error = linux_emul_convpath(td, args->to, UIO_USERSPACE, &to, 1);
702	if (to == NULL) {
703		LFREEPATH(path);
704		return (error);
705	}
706
707#ifdef DEBUG
708	if (ldebug(link))
709		printf(ARGS(link, "%s, %s"), path, to);
710#endif
711	error = kern_link(td, path, to, UIO_SYSSPACE);
712	LFREEPATH(path);
713	LFREEPATH(to);
714	return (error);
715}
716
717int
718linux_fdatasync(td, uap)
719	struct thread *td;
720	struct linux_fdatasync_args *uap;
721{
722	struct fsync_args bsd;
723
724	bsd.fd = uap->fd;
725	return fsync(td, &bsd);
726}
727
728int
729linux_pread(td, uap)
730	struct thread *td;
731	struct linux_pread_args *uap;
732{
733	struct pread_args bsd;
734	struct vnode *vp;
735	int error;
736
737	bsd.fd = uap->fd;
738	bsd.buf = uap->buf;
739	bsd.nbyte = uap->nbyte;
740	bsd.offset = uap->offset;
741
742	error = pread(td, &bsd);
743
744	if (error == 0) {
745   	   	/* This seems to violate POSIX but linux does it */
746   	   	if ((error = fgetvp(td, uap->fd, &vp)) != 0)
747   		   	return (error);
748		if (vp->v_type == VDIR) {
749   		   	vrele(vp);
750			return (EISDIR);
751		}
752		vrele(vp);
753	}
754
755	return (error);
756}
757
758int
759linux_pwrite(td, uap)
760	struct thread *td;
761	struct linux_pwrite_args *uap;
762{
763	struct pwrite_args bsd;
764
765	bsd.fd = uap->fd;
766	bsd.buf = uap->buf;
767	bsd.nbyte = uap->nbyte;
768	bsd.offset = uap->offset;
769	return pwrite(td, &bsd);
770}
771
772int
773linux_mount(struct thread *td, struct linux_mount_args *args)
774{
775	struct ufs_args ufs;
776	char fstypename[MFSNAMELEN];
777	char mntonname[MNAMELEN], mntfromname[MNAMELEN];
778	int error;
779	int fsflags;
780	void *fsdata;
781
782	error = copyinstr(args->filesystemtype, fstypename, MFSNAMELEN - 1,
783	    NULL);
784	if (error)
785		return (error);
786	error = copyinstr(args->specialfile, mntfromname, MNAMELEN - 1, NULL);
787	if (error)
788		return (error);
789	error = copyinstr(args->dir, mntonname, MNAMELEN - 1, NULL);
790	if (error)
791		return (error);
792
793#ifdef DEBUG
794	if (ldebug(mount))
795		printf(ARGS(mount, "%s, %s, %s"),
796		    fstypename, mntfromname, mntonname);
797#endif
798
799	if (strcmp(fstypename, "ext2") == 0) {
800		strcpy(fstypename, "ext2fs");
801		fsdata = &ufs;
802		ufs.fspec = mntfromname;
803#define DEFAULT_ROOTID		-2
804		ufs.export.ex_root = DEFAULT_ROOTID;
805		ufs.export.ex_flags =
806		    args->rwflag & LINUX_MS_RDONLY ? MNT_EXRDONLY : 0;
807	} else if (strcmp(fstypename, "proc") == 0) {
808		strcpy(fstypename, "linprocfs");
809		fsdata = NULL;
810	} else {
811		return (ENODEV);
812	}
813
814	fsflags = 0;
815
816	if ((args->rwflag & 0xffff0000) == 0xc0ed0000) {
817		/*
818		 * Linux SYNC flag is not included; the closest equivalent
819		 * FreeBSD has is !ASYNC, which is our default.
820		 */
821		if (args->rwflag & LINUX_MS_RDONLY)
822			fsflags |= MNT_RDONLY;
823		if (args->rwflag & LINUX_MS_NOSUID)
824			fsflags |= MNT_NOSUID;
825		if (args->rwflag & LINUX_MS_NOEXEC)
826			fsflags |= MNT_NOEXEC;
827		if (args->rwflag & LINUX_MS_REMOUNT)
828			fsflags |= MNT_UPDATE;
829	}
830
831	if (strcmp(fstypename, "linprocfs") == 0) {
832		error = kernel_vmount(fsflags,
833			"fstype", fstypename,
834			"fspath", mntonname,
835			NULL);
836	} else
837		error = EOPNOTSUPP;
838	return (error);
839}
840
841int
842linux_oldumount(struct thread *td, struct linux_oldumount_args *args)
843{
844	struct linux_umount_args args2;
845
846	args2.path = args->path;
847	args2.flags = 0;
848	return (linux_umount(td, &args2));
849}
850
851int
852linux_umount(struct thread *td, struct linux_umount_args *args)
853{
854	struct unmount_args bsd;
855
856	bsd.path = args->path;
857	bsd.flags = args->flags;	/* XXX correct? */
858	return (unmount(td, &bsd));
859}
860
861/*
862 * fcntl family of syscalls
863 */
864
865struct l_flock {
866	l_short		l_type;
867	l_short		l_whence;
868	l_off_t		l_start;
869	l_off_t		l_len;
870	l_pid_t		l_pid;
871}
872#if defined(__amd64__) && defined(COMPAT_LINUX32)
873__packed
874#endif
875;
876
877static void
878linux_to_bsd_flock(struct l_flock *linux_flock, struct flock *bsd_flock)
879{
880	switch (linux_flock->l_type) {
881	case LINUX_F_RDLCK:
882		bsd_flock->l_type = F_RDLCK;
883		break;
884	case LINUX_F_WRLCK:
885		bsd_flock->l_type = F_WRLCK;
886		break;
887	case LINUX_F_UNLCK:
888		bsd_flock->l_type = F_UNLCK;
889		break;
890	default:
891		bsd_flock->l_type = -1;
892		break;
893	}
894	bsd_flock->l_whence = linux_flock->l_whence;
895	bsd_flock->l_start = (off_t)linux_flock->l_start;
896	bsd_flock->l_len = (off_t)linux_flock->l_len;
897	bsd_flock->l_pid = (pid_t)linux_flock->l_pid;
898}
899
900static void
901bsd_to_linux_flock(struct flock *bsd_flock, struct l_flock *linux_flock)
902{
903	switch (bsd_flock->l_type) {
904	case F_RDLCK:
905		linux_flock->l_type = LINUX_F_RDLCK;
906		break;
907	case F_WRLCK:
908		linux_flock->l_type = LINUX_F_WRLCK;
909		break;
910	case F_UNLCK:
911		linux_flock->l_type = LINUX_F_UNLCK;
912		break;
913	}
914	linux_flock->l_whence = bsd_flock->l_whence;
915	linux_flock->l_start = (l_off_t)bsd_flock->l_start;
916	linux_flock->l_len = (l_off_t)bsd_flock->l_len;
917	linux_flock->l_pid = (l_pid_t)bsd_flock->l_pid;
918}
919
920#if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32))
921struct l_flock64 {
922	l_short		l_type;
923	l_short		l_whence;
924	l_loff_t	l_start;
925	l_loff_t	l_len;
926	l_pid_t		l_pid;
927}
928#if defined(__amd64__) && defined(COMPAT_LINUX32)
929__packed
930#endif
931;
932
933static void
934linux_to_bsd_flock64(struct l_flock64 *linux_flock, struct flock *bsd_flock)
935{
936	switch (linux_flock->l_type) {
937	case LINUX_F_RDLCK:
938		bsd_flock->l_type = F_RDLCK;
939		break;
940	case LINUX_F_WRLCK:
941		bsd_flock->l_type = F_WRLCK;
942		break;
943	case LINUX_F_UNLCK:
944		bsd_flock->l_type = F_UNLCK;
945		break;
946	default:
947		bsd_flock->l_type = -1;
948		break;
949	}
950	bsd_flock->l_whence = linux_flock->l_whence;
951	bsd_flock->l_start = (off_t)linux_flock->l_start;
952	bsd_flock->l_len = (off_t)linux_flock->l_len;
953	bsd_flock->l_pid = (pid_t)linux_flock->l_pid;
954}
955
956static void
957bsd_to_linux_flock64(struct flock *bsd_flock, struct l_flock64 *linux_flock)
958{
959	switch (bsd_flock->l_type) {
960	case F_RDLCK:
961		linux_flock->l_type = LINUX_F_RDLCK;
962		break;
963	case F_WRLCK:
964		linux_flock->l_type = LINUX_F_WRLCK;
965		break;
966	case F_UNLCK:
967		linux_flock->l_type = LINUX_F_UNLCK;
968		break;
969	}
970	linux_flock->l_whence = bsd_flock->l_whence;
971	linux_flock->l_start = (l_loff_t)bsd_flock->l_start;
972	linux_flock->l_len = (l_loff_t)bsd_flock->l_len;
973	linux_flock->l_pid = (l_pid_t)bsd_flock->l_pid;
974}
975#endif /* __i386__ || (__amd64__ && COMPAT_LINUX32) */
976
977static int
978fcntl_common(struct thread *td, struct linux_fcntl64_args *args)
979{
980	struct l_flock linux_flock;
981	struct flock bsd_flock;
982	struct file *fp;
983	long arg;
984	int error, result;
985
986	switch (args->cmd) {
987	case LINUX_F_DUPFD:
988		return (kern_fcntl(td, args->fd, F_DUPFD, args->arg));
989
990	case LINUX_F_GETFD:
991		return (kern_fcntl(td, args->fd, F_GETFD, 0));
992
993	case LINUX_F_SETFD:
994		return (kern_fcntl(td, args->fd, F_SETFD, args->arg));
995
996	case LINUX_F_GETFL:
997		error = kern_fcntl(td, args->fd, F_GETFL, 0);
998		result = td->td_retval[0];
999		td->td_retval[0] = 0;
1000		if (result & O_RDONLY)
1001			td->td_retval[0] |= LINUX_O_RDONLY;
1002		if (result & O_WRONLY)
1003			td->td_retval[0] |= LINUX_O_WRONLY;
1004		if (result & O_RDWR)
1005			td->td_retval[0] |= LINUX_O_RDWR;
1006		if (result & O_NDELAY)
1007			td->td_retval[0] |= LINUX_O_NONBLOCK;
1008		if (result & O_APPEND)
1009			td->td_retval[0] |= LINUX_O_APPEND;
1010		if (result & O_FSYNC)
1011			td->td_retval[0] |= LINUX_O_SYNC;
1012		if (result & O_ASYNC)
1013			td->td_retval[0] |= LINUX_FASYNC;
1014#ifdef LINUX_O_NOFOLLOW
1015		if (result & O_NOFOLLOW)
1016			td->td_retval[0] |= LINUX_O_NOFOLLOW;
1017#endif
1018#ifdef LINUX_O_DIRECT
1019		if (result & O_DIRECT)
1020			td->td_retval[0] |= LINUX_O_DIRECT;
1021#endif
1022		return (error);
1023
1024	case LINUX_F_SETFL:
1025		arg = 0;
1026		if (args->arg & LINUX_O_NDELAY)
1027			arg |= O_NONBLOCK;
1028		if (args->arg & LINUX_O_APPEND)
1029			arg |= O_APPEND;
1030		if (args->arg & LINUX_O_SYNC)
1031			arg |= O_FSYNC;
1032		if (args->arg & LINUX_FASYNC)
1033			arg |= O_ASYNC;
1034#ifdef LINUX_O_NOFOLLOW
1035		if (args->arg & LINUX_O_NOFOLLOW)
1036			arg |= O_NOFOLLOW;
1037#endif
1038#ifdef LINUX_O_DIRECT
1039		if (args->arg & LINUX_O_DIRECT)
1040			arg |= O_DIRECT;
1041#endif
1042		return (kern_fcntl(td, args->fd, F_SETFL, arg));
1043
1044	case LINUX_F_GETLK:
1045		error = copyin((void *)args->arg, &linux_flock,
1046		    sizeof(linux_flock));
1047		if (error)
1048			return (error);
1049		linux_to_bsd_flock(&linux_flock, &bsd_flock);
1050		error = kern_fcntl(td, args->fd, F_GETLK, (intptr_t)&bsd_flock);
1051		if (error)
1052			return (error);
1053		bsd_to_linux_flock(&bsd_flock, &linux_flock);
1054		return (copyout(&linux_flock, (void *)args->arg,
1055		    sizeof(linux_flock)));
1056
1057	case LINUX_F_SETLK:
1058		error = copyin((void *)args->arg, &linux_flock,
1059		    sizeof(linux_flock));
1060		if (error)
1061			return (error);
1062		linux_to_bsd_flock(&linux_flock, &bsd_flock);
1063		return (kern_fcntl(td, args->fd, F_SETLK,
1064		    (intptr_t)&bsd_flock));
1065
1066	case LINUX_F_SETLKW:
1067		error = copyin((void *)args->arg, &linux_flock,
1068		    sizeof(linux_flock));
1069		if (error)
1070			return (error);
1071		linux_to_bsd_flock(&linux_flock, &bsd_flock);
1072		return (kern_fcntl(td, args->fd, F_SETLKW,
1073		     (intptr_t)&bsd_flock));
1074
1075	case LINUX_F_GETOWN:
1076		return (kern_fcntl(td, args->fd, F_GETOWN, 0));
1077
1078	case LINUX_F_SETOWN:
1079		/*
1080		 * XXX some Linux applications depend on F_SETOWN having no
1081		 * significant effect for pipes (SIGIO is not delivered for
1082		 * pipes under Linux-2.2.35 at least).
1083		 */
1084		error = fget(td, args->fd, &fp);
1085		if (error)
1086			return (error);
1087		if (fp->f_type == DTYPE_PIPE) {
1088			fdrop(fp, td);
1089			return (EINVAL);
1090		}
1091		fdrop(fp, td);
1092
1093		return (kern_fcntl(td, args->fd, F_SETOWN, args->arg));
1094	}
1095
1096	return (EINVAL);
1097}
1098
1099int
1100linux_fcntl(struct thread *td, struct linux_fcntl_args *args)
1101{
1102	struct linux_fcntl64_args args64;
1103
1104#ifdef DEBUG
1105	if (ldebug(fcntl))
1106		printf(ARGS(fcntl, "%d, %08x, *"), args->fd, args->cmd);
1107#endif
1108
1109	args64.fd = args->fd;
1110	args64.cmd = args->cmd;
1111	args64.arg = args->arg;
1112	return (fcntl_common(td, &args64));
1113}
1114
1115#if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32))
1116int
1117linux_fcntl64(struct thread *td, struct linux_fcntl64_args *args)
1118{
1119	struct l_flock64 linux_flock;
1120	struct flock bsd_flock;
1121	int error;
1122
1123#ifdef DEBUG
1124	if (ldebug(fcntl64))
1125		printf(ARGS(fcntl64, "%d, %08x, *"), args->fd, args->cmd);
1126#endif
1127
1128	switch (args->cmd) {
1129	case LINUX_F_GETLK64:
1130		error = copyin((void *)args->arg, &linux_flock,
1131		    sizeof(linux_flock));
1132		if (error)
1133			return (error);
1134		linux_to_bsd_flock64(&linux_flock, &bsd_flock);
1135		error = kern_fcntl(td, args->fd, F_GETLK, (intptr_t)&bsd_flock);
1136		if (error)
1137			return (error);
1138		bsd_to_linux_flock64(&bsd_flock, &linux_flock);
1139		return (copyout(&linux_flock, (void *)args->arg,
1140			    sizeof(linux_flock)));
1141
1142	case LINUX_F_SETLK64:
1143		error = copyin((void *)args->arg, &linux_flock,
1144		    sizeof(linux_flock));
1145		if (error)
1146			return (error);
1147		linux_to_bsd_flock64(&linux_flock, &bsd_flock);
1148		return (kern_fcntl(td, args->fd, F_SETLK,
1149		    (intptr_t)&bsd_flock));
1150
1151	case LINUX_F_SETLKW64:
1152		error = copyin((void *)args->arg, &linux_flock,
1153		    sizeof(linux_flock));
1154		if (error)
1155			return (error);
1156		linux_to_bsd_flock64(&linux_flock, &bsd_flock);
1157		return (kern_fcntl(td, args->fd, F_SETLKW,
1158		    (intptr_t)&bsd_flock));
1159	}
1160
1161	return (fcntl_common(td, args));
1162}
1163#endif /* __i386__ || (__amd64__ && COMPAT_LINUX32) */
1164
1165int
1166linux_chown(struct thread *td, struct linux_chown_args *args)
1167{
1168	char *path;
1169	int error;
1170
1171	LCONVPATHEXIST(td, args->path, &path);
1172
1173#ifdef DEBUG
1174	if (ldebug(chown))
1175		printf(ARGS(chown, "%s, %d, %d"), path, args->uid, args->gid);
1176#endif
1177	error = kern_chown(td, path, UIO_SYSSPACE, args->uid, args->gid);
1178	LFREEPATH(path);
1179	return (error);
1180}
1181
1182int
1183linux_lchown(struct thread *td, struct linux_lchown_args *args)
1184{
1185	char *path;
1186	int error;
1187
1188	LCONVPATHEXIST(td, args->path, &path);
1189
1190#ifdef DEBUG
1191	if (ldebug(lchown))
1192		printf(ARGS(lchown, "%s, %d, %d"), path, args->uid, args->gid);
1193#endif
1194	error = kern_lchown(td, path, UIO_SYSSPACE, args->uid, args->gid);
1195	LFREEPATH(path);
1196	return (error);
1197}
1198