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