linux_file.c revision 144987
1113229Smdodd/*-
2113229Smdodd * Copyright (c) 1994-1995 S�ren Schmidt
3113229Smdodd * All rights reserved.
4113229Smdodd *
5113229Smdodd * Redistribution and use in source and binary forms, with or without
6113229Smdodd * modification, are permitted provided that the following conditions
7113229Smdodd * are met:
8113229Smdodd * 1. Redistributions of source code must retain the above copyright
9113229Smdodd *    notice, this list of conditions and the following disclaimer
10113229Smdodd *    in this position and unchanged.
11113229Smdodd * 2. Redistributions in binary form must reproduce the above copyright
12115150Smdodd *    notice, this list of conditions and the following disclaimer in the
13115150Smdodd *    documentation and/or other materials provided with the distribution.
14116513Smdodd * 3. The name of the author may not be used to endorse or promote products
15115150Smdodd *    derived from this software without specific prior written permission
16113229Smdodd *
17113229Smdodd * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18113229Smdodd * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19113229Smdodd * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20127250Speter * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21127250Speter * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22127250Speter * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23127250Speter * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24127250Speter * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25113229Smdodd * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26113229Smdodd * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27113229Smdodd */
28113229Smdodd
29113229Smdodd#include <sys/cdefs.h>
30113229Smdodd__FBSDID("$FreeBSD: head/sys/compat/linux/linux_file.c 144987 2005-04-13 04:31:43Z mdodd $");
31113229Smdodd
32113229Smdodd#include "opt_compat.h"
33113229Smdodd#include "opt_mac.h"
34113229Smdodd
35113229Smdodd#include <sys/param.h>
36129638Smdodd#include <sys/systm.h>
37113229Smdodd#include <sys/conf.h>
38113229Smdodd#include <sys/dirent.h>
39113229Smdodd#include <sys/fcntl.h>
40113229Smdodd#include <sys/file.h>
41141232Smdodd#include <sys/filedesc.h>
42141232Smdodd#include <sys/lock.h>
43141232Smdodd#include <sys/mac.h>
44115400Smdodd#include <sys/malloc.h>
45113229Smdodd#include <sys/mount.h>
46113229Smdodd#include <sys/mutex.h>
47113229Smdodd#include <sys/proc.h>
48113229Smdodd#include <sys/syscallsubr.h>
49129638Smdodd#include <sys/sysproto.h>
50141232Smdodd#include <sys/tty.h>
51141232Smdodd#include <sys/vnode.h>
52113229Smdodd
53113312Smdodd#include <ufs/ufs/extattr.h>
54113312Smdodd#include <ufs/ufs/quota.h>
55113312Smdodd#include <ufs/ufs/ufsmount.h>
56120038Smdodd
57141232Smdodd#include "opt_compat.h"
58113229Smdodd
59113229Smdodd#ifdef COMPAT_LINUX32
60113229Smdodd#include <machine/../linux32/linux.h>
61141232Smdodd#include <machine/../linux32/linux32_proto.h>
62115445Smdodd#else
63113229Smdodd#include <machine/../linux/linux.h>
64113229Smdodd#include <machine/../linux/linux_proto.h>
65141232Smdodd#endif
66141232Smdodd#include <compat/linux/linux_util.h>
67141232Smdodd
68141232Smdodd#ifndef __alpha__
69141232Smdoddint
70113229Smdoddlinux_creat(struct thread *td, struct linux_creat_args *args)
71141232Smdodd{
72141232Smdodd    char *path;
73141232Smdodd    int error;
74141232Smdodd
75141232Smdodd    LCONVPATHEXIST(td, args->path, &path);
76141232Smdodd
77141232Smdodd#ifdef DEBUG
78141232Smdodd	if (ldebug(creat))
79141232Smdodd		printf(ARGS(creat, "%s, %d"), path, args->mode);
80141232Smdodd#endif
81141232Smdodd    error = kern_open(td, path, UIO_SYSSPACE, O_WRONLY | O_CREAT | O_TRUNC,
82141232Smdodd	args->mode);
83141232Smdodd    LFREEPATH(path);
84141232Smdodd    return (error);
85141232Smdodd}
86141232Smdodd#endif /*!__alpha__*/
87141232Smdodd
88141232Smdoddint
89141232Smdoddlinux_open(struct thread *td, struct linux_open_args *args)
90141232Smdodd{
91141232Smdodd    struct proc *p = td->td_proc;
92141232Smdodd    char *path;
93141232Smdodd    int bsd_flags, error;
94141232Smdodd
95141232Smdodd    if (args->flags & LINUX_O_CREAT)
96141232Smdodd	LCONVPATHCREAT(td, args->path, &path);
97141232Smdodd    else
98141232Smdodd	LCONVPATHEXIST(td, args->path, &path);
99141232Smdodd
100141232Smdodd#ifdef DEBUG
101141232Smdodd	if (ldebug(open))
102141232Smdodd		printf(ARGS(open, "%s, 0x%x, 0x%x"),
103141232Smdodd		    path, args->flags, args->mode);
104141232Smdodd#endif
105113229Smdodd    bsd_flags = 0;
106113229Smdodd    if (args->flags & LINUX_O_RDONLY)
107115400Smdodd	bsd_flags |= O_RDONLY;
108113312Smdodd    if (args->flags & LINUX_O_WRONLY)
109113229Smdodd	bsd_flags |= O_WRONLY;
110113312Smdodd    if (args->flags & LINUX_O_RDWR)
111113312Smdodd	bsd_flags |= O_RDWR;
112113229Smdodd    if (args->flags & LINUX_O_NDELAY)
113114316Skan	bsd_flags |= O_NONBLOCK;
114113312Smdodd    if (args->flags & LINUX_O_APPEND)
115113312Smdodd	bsd_flags |= O_APPEND;
116113229Smdodd    if (args->flags & LINUX_O_SYNC)
117113229Smdodd	bsd_flags |= O_FSYNC;
118113312Smdodd    if (args->flags & LINUX_O_NONBLOCK)
119113229Smdodd	bsd_flags |= O_NONBLOCK;
120113312Smdodd    if (args->flags & LINUX_FASYNC)
121113312Smdodd	bsd_flags |= O_ASYNC;
122113229Smdodd    if (args->flags & LINUX_O_CREAT)
123114316Skan	bsd_flags |= O_CREAT;
124114316Skan    if (args->flags & LINUX_O_TRUNC)
125113312Smdodd	bsd_flags |= O_TRUNC;
126115400Smdodd    if (args->flags & LINUX_O_EXCL)
127113229Smdodd	bsd_flags |= O_EXCL;
128114316Skan    if (args->flags & LINUX_O_NOCTTY)
129114316Skan	bsd_flags |= O_NOCTTY;
130113312Smdodd
131113312Smdodd    error = kern_open(td, path, UIO_SYSSPACE, bsd_flags, args->mode);
132113312Smdodd    PROC_LOCK(p);
133113312Smdodd    if (!error && !(bsd_flags & O_NOCTTY) &&
134113312Smdodd	SESS_LEADER(p) && !(p->p_flag & P_CONTROLT)) {
135114316Skan	struct file *fp;
136113312Smdodd
137113312Smdodd	PROC_UNLOCK(p);
138113229Smdodd	error = fget(td, td->td_retval[0], &fp);
139113312Smdodd	if (!error) {
140113312Smdodd		if (fp->f_type == DTYPE_VNODE)
141113312Smdodd			fo_ioctl(fp, TIOCSCTTY, (caddr_t) 0, td->td_ucred,
142115396Skan			    td);
143113312Smdodd	    fdrop(fp, td);
144125397Sfjoe	}
145114316Skan    } else {
146113312Smdodd	PROC_UNLOCK(p);
147115400Smdodd#ifdef DEBUG
148114316Skan	if (ldebug(open))
149114316Skan		printf(LMSG("open returns error %d"), error);
150113229Smdodd#endif
151113312Smdodd    }
152113312Smdodd    LFREEPATH(path);
153114316Skan    return error;
154113312Smdodd}
155113312Smdodd
156113312Smdoddint
157113312Smdoddlinux_lseek(struct thread *td, struct linux_lseek_args *args)
158113312Smdodd{
159113312Smdodd
160114316Skan    struct lseek_args /* {
161113312Smdodd	int fd;
162113312Smdodd	int pad;
163114316Skan	off_t offset;
164113312Smdodd	int whence;
165115396Skan    } */ tmp_args;
166114316Skan    int error;
167114316Skan
168113229Smdodd#ifdef DEBUG
169114316Skan	if (ldebug(lseek))
170114316Skan		printf(ARGS(lseek, "%d, %ld, %d"),
171114316Skan		    args->fdes, (long)args->off, args->whence);
172114316Skan#endif
173115400Smdodd    tmp_args.fd = args->fdes;
174113229Smdodd    tmp_args.offset = (off_t)args->off;
175113229Smdodd    tmp_args.whence = args->whence;
176113229Smdodd    error = lseek(td, &tmp_args);
177113229Smdodd    return error;
178113229Smdodd}
179113229Smdodd
180113229Smdodd#ifndef __alpha__
181113229Smdoddint
182115445Smdoddlinux_llseek(struct thread *td, struct linux_llseek_args *args)
183115445Smdodd{
184113229Smdodd	struct lseek_args bsd_args;
185113229Smdodd	int error;
186113229Smdodd	off_t off;
187113229Smdodd
188113229Smdodd#ifdef DEBUG
189113229Smdodd	if (ldebug(llseek))
190113229Smdodd		printf(ARGS(llseek, "%d, %d:%d, %d"),
191113229Smdodd		    args->fd, args->ohigh, args->olow, args->whence);
192113229Smdodd#endif
193113229Smdodd	off = (args->olow) | (((off_t) args->ohigh) << 32);
194113229Smdodd
195113229Smdodd	bsd_args.fd = args->fd;
196113229Smdodd	bsd_args.offset = off;
197113229Smdodd	bsd_args.whence = args->whence;
198113229Smdodd
199115445Smdodd	if ((error = lseek(td, &bsd_args)))
200115445Smdodd		return error;
201113229Smdodd
202113229Smdodd	if ((error = copyout(td->td_retval, args->res, sizeof (off_t))))
203113229Smdodd		return error;
204113229Smdodd
205113229Smdodd	td->td_retval[0] = 0;
206113229Smdodd	return 0;
207113229Smdodd}
208113229Smdodd#endif /*!__alpha__*/
209113229Smdodd
210113229Smdodd#ifndef __alpha__
211113229Smdoddint
212115400Smdoddlinux_readdir(struct thread *td, struct linux_readdir_args *args)
213113229Smdodd{
214113229Smdodd	struct linux_getdents_args lda;
215113229Smdodd
216113229Smdodd	lda.fd = args->fd;
217113229Smdodd	lda.dent = args->dent;
218113229Smdodd	lda.count = 1;
219113229Smdodd	return linux_getdents(td, &lda);
220115445Smdodd}
221115445Smdodd#endif /*!__alpha__*/
222113229Smdodd
223115150Smdodd/*
224113229Smdodd * Note that linux_getdents(2) and linux_getdents64(2) have the same
225115150Smdodd * arguments. They only differ in the definition of struct dirent they
226115400Smdodd * operate on. We use this to common the code, with the exception of
227115400Smdodd * accessing struct dirent. Note that linux_readdir(2) is implemented
228113229Smdodd * by means of linux_getdents(2). In this case we never operate on
229141232Smdodd * struct dirent64 and thus don't need to handle it...
230113229Smdodd */
231113229Smdodd
232113229Smdoddstruct l_dirent {
233113229Smdodd	l_long		d_ino;
234113229Smdodd	l_off_t		d_off;
235113229Smdodd	l_ushort	d_reclen;
236113229Smdodd	char		d_name[LINUX_NAME_MAX + 1];
237113229Smdodd};
238115445Smdodd
239115445Smdoddstruct l_dirent64 {
240113229Smdodd	uint64_t	d_ino;
241113229Smdodd	int64_t		d_off;
242115150Smdodd	l_ushort	d_reclen;
243115150Smdodd	u_char		d_type;
244115150Smdodd	char		d_name[LINUX_NAME_MAX + 1];
245115150Smdodd};
246115150Smdodd
247115400Smdodd#define LINUX_RECLEN(de,namlen) \
248113229Smdodd    ALIGN((((char *)&(de)->d_name - (char *)de) + (namlen) + 1))
249115150Smdodd
250113229Smdodd#define	LINUX_DIRBLKSIZ		512
251113229Smdodd
252113229Smdoddstatic int
253113229Smdoddgetdents_common(struct thread *td, struct linux_getdents64_args *args,
254113229Smdodd    int is64bit)
255113229Smdodd{
256113229Smdodd	struct dirent *bdp;
257113229Smdodd	struct vnode *vp;
258129638Smdodd	caddr_t inp, buf;		/* BSD-format */
259129638Smdodd	int len, reclen;		/* BSD-format */
260127250Speter	caddr_t outp;			/* Linux-format */
261127250Speter	int resid, linuxreclen=0;	/* Linux-format */
262127250Speter	struct file *fp;
263127250Speter	struct uio auio;
264127250Speter	struct iovec aiov;
265127250Speter	off_t off;
266127250Speter	struct l_dirent linux_dirent;
267127250Speter	struct l_dirent64 linux_dirent64;
268127250Speter	int buflen, error, eofflag, nbytes, justone;
269127250Speter	u_long *cookies = NULL, *cookiep;
270127250Speter	int ncookies;
271127250Speter
272127250Speter	if ((error = getvnode(td->td_proc->p_fd, args->fd, &fp)) != 0)
273127250Speter		return (error);
274127250Speter
275127250Speter	if ((fp->f_flag & FREAD) == 0) {
276127250Speter		fdrop(fp, td);
277127250Speter		return (EBADF);
278127250Speter	}
279127250Speter
280127250Speter	vp = fp->f_vnode;
281113229Smdodd	if (vp->v_type != VDIR) {
282113229Smdodd		fdrop(fp, td);
283113229Smdodd		return (EINVAL);
284113229Smdodd	}
285113229Smdodd
286115445Smdodd	nbytes = args->count;
287115445Smdodd	if (nbytes == 1) {
288113229Smdodd		/* readdir(2) case. Always struct dirent. */
289127250Speter		if (is64bit) {
290113229Smdodd			fdrop(fp, td);
291141230Smdodd			return (EINVAL);
292113229Smdodd		}
293113229Smdodd		nbytes = sizeof(linux_dirent);
294129638Smdodd		justone = 1;
295129638Smdodd	} else
296113229Smdodd		justone = 0;
297113229Smdodd
298113229Smdodd	off = fp->f_offset;
299113229Smdodd
300113229Smdodd	buflen = max(LINUX_DIRBLKSIZ, nbytes);
301115445Smdodd	buflen = min(buflen, MAXBSIZE);
302115445Smdodd	buf = malloc(buflen, M_TEMP, M_WAITOK);
303113229Smdodd	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td);
304129638Smdodd
305129638Smdoddagain:
306129638Smdodd	aiov.iov_base = buf;
307113229Smdodd	aiov.iov_len = buflen;
308113229Smdodd	auio.uio_iov = &aiov;
309113229Smdodd	auio.uio_iovcnt = 1;
310113229Smdodd	auio.uio_rw = UIO_READ;
311113229Smdodd	auio.uio_segflg = UIO_SYSSPACE;
312113229Smdodd	auio.uio_td = td;
313113229Smdodd	auio.uio_resid = buflen;
314113229Smdodd	auio.uio_offset = off;
315113229Smdodd
316115445Smdodd	if (cookies) {
317115445Smdodd		free(cookies, M_TEMP);
318115150Smdodd		cookies = NULL;
319113229Smdodd	}
320129638Smdodd
321129638Smdodd#ifdef MAC
322129638Smdodd	/*
323129638Smdodd	 * Do directory search MAC check using non-cached credentials.
324129638Smdodd	 */
325129638Smdodd	if ((error = mac_check_vnode_readdir(td->td_ucred, vp)))
326113229Smdodd		goto out;
327113229Smdodd#endif /* MAC */
328113229Smdodd	if ((error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, &ncookies,
329113229Smdodd		 &cookies)))
330113229Smdodd		goto out;
331129638Smdodd
332129638Smdodd	inp = buf;
333129638Smdodd	outp = (caddr_t)args->dirent;
334129638Smdodd	resid = nbytes;
335129638Smdodd	if ((len = buflen - auio.uio_resid) <= 0)
336129638Smdodd		goto eof;
337129638Smdodd
338141230Smdodd	cookiep = cookies;
339129638Smdodd
340129638Smdodd	if (cookies) {
341129638Smdodd		/*
342141230Smdodd		 * When using cookies, the vfs has the option of reading from
343129638Smdodd		 * a different offset than that supplied (UFS truncates the
344141232Smdodd		 * offset to a block boundary to make sure that it never reads
345141232Smdodd		 * partway through a directory entry, even if the directory
346141232Smdodd		 * has been compacted).
347141232Smdodd		 */
348141232Smdodd		while (len > 0 && ncookies > 0 && *cookiep <= off) {
349141232Smdodd			bdp = (struct dirent *) inp;
350141232Smdodd			len -= bdp->d_reclen;
351141232Smdodd			inp += bdp->d_reclen;
352141232Smdodd			cookiep++;
353141232Smdodd			ncookies--;
354141232Smdodd		}
355141232Smdodd	}
356141232Smdodd
357141232Smdodd	while (len > 0) {
358141232Smdodd		if (cookiep && ncookies == 0)
359141232Smdodd			break;
360141232Smdodd		bdp = (struct dirent *) inp;
361141232Smdodd		reclen = bdp->d_reclen;
362141232Smdodd		if (reclen & 3) {
363141232Smdodd			error = EFAULT;
364141232Smdodd			goto out;
365141232Smdodd		}
366141232Smdodd
367141232Smdodd		if (bdp->d_fileno == 0) {
368141232Smdodd			inp += reclen;
369141232Smdodd			if (cookiep) {
370141232Smdodd				off = *cookiep++;
371141232Smdodd				ncookies--;
372			} else
373				off += reclen;
374
375			len -= reclen;
376			continue;
377		}
378
379		linuxreclen = (is64bit)
380		    ? LINUX_RECLEN(&linux_dirent64, bdp->d_namlen)
381		    : LINUX_RECLEN(&linux_dirent, bdp->d_namlen);
382
383		if (reclen > len || resid < linuxreclen) {
384			outp++;
385			break;
386		}
387
388		if (justone) {
389			/* readdir(2) case. */
390			linux_dirent.d_ino = (l_long)bdp->d_fileno;
391			linux_dirent.d_off = (l_off_t)linuxreclen;
392			linux_dirent.d_reclen = (l_ushort)bdp->d_namlen;
393			strcpy(linux_dirent.d_name, bdp->d_name);
394			error = copyout(&linux_dirent, outp, linuxreclen);
395		} else {
396			if (is64bit) {
397				linux_dirent64.d_ino = bdp->d_fileno;
398				linux_dirent64.d_off = (cookiep)
399				    ? (l_off_t)*cookiep
400				    : (l_off_t)(off + reclen);
401				linux_dirent64.d_reclen =
402				    (l_ushort)linuxreclen;
403				linux_dirent64.d_type = bdp->d_type;
404				strcpy(linux_dirent64.d_name, bdp->d_name);
405				error = copyout(&linux_dirent64, outp,
406				    linuxreclen);
407			} else {
408				linux_dirent.d_ino = bdp->d_fileno;
409				linux_dirent.d_off = (cookiep)
410				    ? (l_off_t)*cookiep
411				    : (l_off_t)(off + reclen);
412				linux_dirent.d_reclen = (l_ushort)linuxreclen;
413				strcpy(linux_dirent.d_name, bdp->d_name);
414				error = copyout(&linux_dirent, outp,
415				    linuxreclen);
416			}
417		}
418		if (error)
419			goto out;
420
421		inp += reclen;
422		if (cookiep) {
423			off = *cookiep++;
424			ncookies--;
425		} else
426			off += reclen;
427
428		outp += linuxreclen;
429		resid -= linuxreclen;
430		len -= reclen;
431		if (justone)
432			break;
433	}
434
435	if (outp == (caddr_t)args->dirent)
436		goto again;
437
438	fp->f_offset = off;
439	if (justone)
440		nbytes = resid + linuxreclen;
441
442eof:
443	td->td_retval[0] = nbytes - resid;
444
445out:
446	if (cookies)
447		free(cookies, M_TEMP);
448
449	VOP_UNLOCK(vp, 0, td);
450	fdrop(fp, td);
451	free(buf, M_TEMP);
452	return (error);
453}
454
455int
456linux_getdents(struct thread *td, struct linux_getdents_args *args)
457{
458
459#ifdef DEBUG
460	if (ldebug(getdents))
461		printf(ARGS(getdents, "%d, *, %d"), args->fd, args->count);
462#endif
463
464	return (getdents_common(td, (struct linux_getdents64_args*)args, 0));
465}
466
467int
468linux_getdents64(struct thread *td, struct linux_getdents64_args *args)
469{
470
471#ifdef DEBUG
472	if (ldebug(getdents64))
473		printf(ARGS(getdents64, "%d, *, %d"), args->fd, args->count);
474#endif
475
476	return (getdents_common(td, args, 1));
477}
478
479/*
480 * These exist mainly for hooks for doing /compat/linux translation.
481 */
482
483int
484linux_access(struct thread *td, struct linux_access_args *args)
485{
486	char *path;
487	int error;
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	return (error);
498}
499
500int
501linux_unlink(struct thread *td, struct linux_unlink_args *args)
502{
503	char *path;
504	int error;
505
506	LCONVPATHEXIST(td, args->path, &path);
507
508#ifdef DEBUG
509	if (ldebug(unlink))
510		printf(ARGS(unlink, "%s"), path);
511#endif
512
513	error = kern_unlink(td, path, UIO_SYSSPACE);
514	LFREEPATH(path);
515	return (error);
516}
517
518int
519linux_chdir(struct thread *td, struct linux_chdir_args *args)
520{
521	char *path;
522	int error;
523
524	LCONVPATHEXIST(td, args->path, &path);
525
526#ifdef DEBUG
527	if (ldebug(chdir))
528		printf(ARGS(chdir, "%s"), path);
529#endif
530	error = kern_chdir(td, path, UIO_SYSSPACE);
531	LFREEPATH(path);
532	return (error);
533}
534
535int
536linux_chmod(struct thread *td, struct linux_chmod_args *args)
537{
538	char *path;
539	int error;
540
541	LCONVPATHEXIST(td, args->path, &path);
542
543#ifdef DEBUG
544	if (ldebug(chmod))
545		printf(ARGS(chmod, "%s, %d"), path, args->mode);
546#endif
547	error = kern_chmod(td, path, UIO_SYSSPACE, args->mode);
548	LFREEPATH(path);
549	return (error);
550}
551
552int
553linux_mkdir(struct thread *td, struct linux_mkdir_args *args)
554{
555	char *path;
556	int error;
557
558	LCONVPATHCREAT(td, args->path, &path);
559
560#ifdef DEBUG
561	if (ldebug(mkdir))
562		printf(ARGS(mkdir, "%s, %d"), path, args->mode);
563#endif
564	error = kern_mkdir(td, path, UIO_SYSSPACE, args->mode);
565	LFREEPATH(path);
566	return (error);
567}
568
569int
570linux_rmdir(struct thread *td, struct linux_rmdir_args *args)
571{
572	char *path;
573	int error;
574
575	LCONVPATHEXIST(td, args->path, &path);
576
577#ifdef DEBUG
578	if (ldebug(rmdir))
579		printf(ARGS(rmdir, "%s"), path);
580#endif
581	error = kern_rmdir(td, path, UIO_SYSSPACE);
582	LFREEPATH(path);
583	return (error);
584}
585
586int
587linux_rename(struct thread *td, struct linux_rename_args *args)
588{
589	char *from, *to;
590	int error;
591
592	LCONVPATHEXIST(td, args->from, &from);
593	/* Expand LCONVPATHCREATE so that `from' can be freed on errors */
594	error = linux_emul_convpath(td, args->to, UIO_USERSPACE, &to, 1);
595	if (to == NULL) {
596		LFREEPATH(from);
597		return (error);
598	}
599
600#ifdef DEBUG
601	if (ldebug(rename))
602		printf(ARGS(rename, "%s, %s"), from, to);
603#endif
604	error = kern_rename(td, from, to, UIO_SYSSPACE);
605	LFREEPATH(from);
606	LFREEPATH(to);
607	return (error);
608}
609
610int
611linux_symlink(struct thread *td, struct linux_symlink_args *args)
612{
613	char *path, *to;
614	int error;
615
616	LCONVPATHEXIST(td, args->path, &path);
617	/* Expand LCONVPATHCREATE so that `path' can be freed on errors */
618	error = linux_emul_convpath(td, args->to, UIO_USERSPACE, &to, 1);
619	if (to == NULL) {
620		LFREEPATH(path);
621		return (error);
622	}
623
624#ifdef DEBUG
625	if (ldebug(symlink))
626		printf(ARGS(symlink, "%s, %s"), path, to);
627#endif
628	error = kern_symlink(td, path, to, UIO_SYSSPACE);
629	LFREEPATH(path);
630	LFREEPATH(to);
631	return (error);
632}
633
634int
635linux_readlink(struct thread *td, struct linux_readlink_args *args)
636{
637	char *name;
638	int error;
639
640	LCONVPATHEXIST(td, args->name, &name);
641
642#ifdef DEBUG
643	if (ldebug(readlink))
644		printf(ARGS(readlink, "%s, %p, %d"), name, (void *)args->buf,
645		    args->count);
646#endif
647	error = kern_readlink(td, name, UIO_SYSSPACE, args->buf, UIO_USERSPACE,
648	    args->count);
649	LFREEPATH(name);
650	return (error);
651}
652
653int
654linux_truncate(struct thread *td, struct linux_truncate_args *args)
655{
656	char *path;
657	int error;
658
659	LCONVPATHEXIST(td, args->path, &path);
660
661#ifdef DEBUG
662	if (ldebug(truncate))
663		printf(ARGS(truncate, "%s, %ld"), path, (long)args->length);
664#endif
665
666	error = kern_truncate(td, path, UIO_SYSSPACE, args->length);
667	LFREEPATH(path);
668	return (error);
669}
670
671int
672linux_link(struct thread *td, struct linux_link_args *args)
673{
674	char *path, *to;
675	int error;
676
677	LCONVPATHEXIST(td, args->path, &path);
678	/* Expand LCONVPATHCREATE so that `path' can be freed on errors */
679	error = linux_emul_convpath(td, args->to, UIO_USERSPACE, &to, 1);
680	if (to == NULL) {
681		LFREEPATH(path);
682		return (error);
683	}
684
685#ifdef DEBUG
686	if (ldebug(link))
687		printf(ARGS(link, "%s, %s"), path, to);
688#endif
689	error = kern_link(td, path, to, UIO_SYSSPACE);
690	LFREEPATH(path);
691	LFREEPATH(to);
692	return (error);
693}
694
695#ifndef __alpha__
696int
697linux_fdatasync(td, uap)
698	struct thread *td;
699	struct linux_fdatasync_args *uap;
700{
701	struct fsync_args bsd;
702
703	bsd.fd = uap->fd;
704	return fsync(td, &bsd);
705}
706#endif /*!__alpha__*/
707
708int
709linux_pread(td, uap)
710	struct thread *td;
711	struct linux_pread_args *uap;
712{
713	struct pread_args bsd;
714
715	bsd.fd = uap->fd;
716	bsd.buf = uap->buf;
717	bsd.nbyte = uap->nbyte;
718	bsd.offset = uap->offset;
719	return pread(td, &bsd);
720}
721
722int
723linux_pwrite(td, uap)
724	struct thread *td;
725	struct linux_pwrite_args *uap;
726{
727	struct pwrite_args bsd;
728
729	bsd.fd = uap->fd;
730	bsd.buf = uap->buf;
731	bsd.nbyte = uap->nbyte;
732	bsd.offset = uap->offset;
733	return pwrite(td, &bsd);
734}
735
736int
737linux_mount(struct thread *td, struct linux_mount_args *args)
738{
739	struct ufs_args ufs;
740	char fstypename[MFSNAMELEN];
741	char mntonname[MNAMELEN], mntfromname[MNAMELEN];
742	int error;
743	int fsflags;
744	void *fsdata;
745
746	error = copyinstr(args->filesystemtype, fstypename, MFSNAMELEN - 1,
747	    NULL);
748	if (error)
749		return (error);
750	error = copyinstr(args->specialfile, mntfromname, MNAMELEN - 1, NULL);
751	if (error)
752		return (error);
753	error = copyinstr(args->dir, mntonname, MNAMELEN - 1, NULL);
754	if (error)
755		return (error);
756
757#ifdef DEBUG
758	if (ldebug(mount))
759		printf(ARGS(mount, "%s, %s, %s"),
760		    fstypename, mntfromname, mntonname);
761#endif
762
763	if (strcmp(fstypename, "ext2") == 0) {
764		strcpy(fstypename, "ext2fs");
765		fsdata = &ufs;
766		ufs.fspec = mntfromname;
767#define DEFAULT_ROOTID		-2
768		ufs.export.ex_root = DEFAULT_ROOTID;
769		ufs.export.ex_flags =
770		    args->rwflag & LINUX_MS_RDONLY ? MNT_EXRDONLY : 0;
771	} else if (strcmp(fstypename, "proc") == 0) {
772		strcpy(fstypename, "linprocfs");
773		fsdata = NULL;
774	} else {
775		return (ENODEV);
776	}
777
778	fsflags = 0;
779
780	if ((args->rwflag & 0xffff0000) == 0xc0ed0000) {
781		/*
782		 * Linux SYNC flag is not included; the closest equivalent
783		 * FreeBSD has is !ASYNC, which is our default.
784		 */
785		if (args->rwflag & LINUX_MS_RDONLY)
786			fsflags |= MNT_RDONLY;
787		if (args->rwflag & LINUX_MS_NOSUID)
788			fsflags |= MNT_NOSUID;
789		if (args->rwflag & LINUX_MS_NOEXEC)
790			fsflags |= MNT_NOEXEC;
791		if (args->rwflag & LINUX_MS_REMOUNT)
792			fsflags |= MNT_UPDATE;
793	}
794
795	if (strcmp(fstypename, "linprocfs") == 0) {
796		error = kernel_vmount(fsflags,
797			"fstype", fstypename,
798			"fspath", mntonname,
799			NULL);
800	} else
801		error = EOPNOTSUPP;
802	return (error);
803}
804
805int
806linux_oldumount(struct thread *td, struct linux_oldumount_args *args)
807{
808	struct linux_umount_args args2;
809
810	args2.path = args->path;
811	args2.flags = 0;
812	return (linux_umount(td, &args2));
813}
814
815int
816linux_umount(struct thread *td, struct linux_umount_args *args)
817{
818	struct unmount_args bsd;
819
820	bsd.path = args->path;
821	bsd.flags = args->flags;	/* XXX correct? */
822	return (unmount(td, &bsd));
823}
824
825/*
826 * fcntl family of syscalls
827 */
828
829struct l_flock {
830	l_short		l_type;
831	l_short		l_whence;
832	l_off_t		l_start;
833	l_off_t		l_len;
834	l_pid_t		l_pid;
835}
836#if defined(__amd64__) && defined(COMPAT_LINUX32)
837__packed
838#endif
839;
840
841static void
842linux_to_bsd_flock(struct l_flock *linux_flock, struct flock *bsd_flock)
843{
844	switch (linux_flock->l_type) {
845	case LINUX_F_RDLCK:
846		bsd_flock->l_type = F_RDLCK;
847		break;
848	case LINUX_F_WRLCK:
849		bsd_flock->l_type = F_WRLCK;
850		break;
851	case LINUX_F_UNLCK:
852		bsd_flock->l_type = F_UNLCK;
853		break;
854	default:
855		bsd_flock->l_type = -1;
856		break;
857	}
858	bsd_flock->l_whence = linux_flock->l_whence;
859	bsd_flock->l_start = (off_t)linux_flock->l_start;
860	bsd_flock->l_len = (off_t)linux_flock->l_len;
861	bsd_flock->l_pid = (pid_t)linux_flock->l_pid;
862}
863
864static void
865bsd_to_linux_flock(struct flock *bsd_flock, struct l_flock *linux_flock)
866{
867	switch (bsd_flock->l_type) {
868	case F_RDLCK:
869		linux_flock->l_type = LINUX_F_RDLCK;
870		break;
871	case F_WRLCK:
872		linux_flock->l_type = LINUX_F_WRLCK;
873		break;
874	case F_UNLCK:
875		linux_flock->l_type = LINUX_F_UNLCK;
876		break;
877	}
878	linux_flock->l_whence = bsd_flock->l_whence;
879	linux_flock->l_start = (l_off_t)bsd_flock->l_start;
880	linux_flock->l_len = (l_off_t)bsd_flock->l_len;
881	linux_flock->l_pid = (l_pid_t)bsd_flock->l_pid;
882}
883
884#if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32))
885struct l_flock64 {
886	l_short		l_type;
887	l_short		l_whence;
888	l_loff_t	l_start;
889	l_loff_t	l_len;
890	l_pid_t		l_pid;
891}
892#if defined(__amd64__) && defined(COMPAT_LINUX32)
893__packed
894#endif
895;
896
897static void
898linux_to_bsd_flock64(struct l_flock64 *linux_flock, struct flock *bsd_flock)
899{
900	switch (linux_flock->l_type) {
901	case LINUX_F_RDLCK:
902		bsd_flock->l_type = F_RDLCK;
903		break;
904	case LINUX_F_WRLCK:
905		bsd_flock->l_type = F_WRLCK;
906		break;
907	case LINUX_F_UNLCK:
908		bsd_flock->l_type = F_UNLCK;
909		break;
910	default:
911		bsd_flock->l_type = -1;
912		break;
913	}
914	bsd_flock->l_whence = linux_flock->l_whence;
915	bsd_flock->l_start = (off_t)linux_flock->l_start;
916	bsd_flock->l_len = (off_t)linux_flock->l_len;
917	bsd_flock->l_pid = (pid_t)linux_flock->l_pid;
918}
919
920static void
921bsd_to_linux_flock64(struct flock *bsd_flock, struct l_flock64 *linux_flock)
922{
923	switch (bsd_flock->l_type) {
924	case F_RDLCK:
925		linux_flock->l_type = LINUX_F_RDLCK;
926		break;
927	case F_WRLCK:
928		linux_flock->l_type = LINUX_F_WRLCK;
929		break;
930	case F_UNLCK:
931		linux_flock->l_type = LINUX_F_UNLCK;
932		break;
933	}
934	linux_flock->l_whence = bsd_flock->l_whence;
935	linux_flock->l_start = (l_loff_t)bsd_flock->l_start;
936	linux_flock->l_len = (l_loff_t)bsd_flock->l_len;
937	linux_flock->l_pid = (l_pid_t)bsd_flock->l_pid;
938}
939#endif /* __i386__ || (__amd64__ && COMPAT_LINUX32) */
940
941#if defined(__alpha__)
942#define	linux_fcntl64_args	linux_fcntl_args
943#endif
944
945static int
946fcntl_common(struct thread *td, struct linux_fcntl64_args *args)
947{
948	struct l_flock linux_flock;
949	struct flock bsd_flock;
950	struct file *fp;
951	long arg;
952	int error, result;
953
954	switch (args->cmd) {
955	case LINUX_F_DUPFD:
956		return (kern_fcntl(td, args->fd, F_DUPFD, args->arg));
957
958	case LINUX_F_GETFD:
959		return (kern_fcntl(td, args->fd, F_GETFD, 0));
960
961	case LINUX_F_SETFD:
962		return (kern_fcntl(td, args->fd, F_SETFD, args->arg));
963
964	case LINUX_F_GETFL:
965		error = kern_fcntl(td, args->fd, F_GETFL, 0);
966		result = td->td_retval[0];
967		td->td_retval[0] = 0;
968		if (result & O_RDONLY)
969			td->td_retval[0] |= LINUX_O_RDONLY;
970		if (result & O_WRONLY)
971			td->td_retval[0] |= LINUX_O_WRONLY;
972		if (result & O_RDWR)
973			td->td_retval[0] |= LINUX_O_RDWR;
974		if (result & O_NDELAY)
975			td->td_retval[0] |= LINUX_O_NONBLOCK;
976		if (result & O_APPEND)
977			td->td_retval[0] |= LINUX_O_APPEND;
978		if (result & O_FSYNC)
979			td->td_retval[0] |= LINUX_O_SYNC;
980		if (result & O_ASYNC)
981			td->td_retval[0] |= LINUX_FASYNC;
982#ifdef LINUX_O_NOFOLLOW
983		if (result & O_NOFOLLOW)
984			td->td_retval[0] |= LINUX_O_NOFOLLOW;
985#endif
986#ifdef LINUX_O_DIRECT
987		if (result & O_DIRECT)
988			td->td_retval[0] |= LINUX_O_DIRECT;
989#endif
990		return (error);
991
992	case LINUX_F_SETFL:
993		arg = 0;
994		if (args->arg & LINUX_O_NDELAY)
995			arg |= O_NONBLOCK;
996		if (args->arg & LINUX_O_APPEND)
997			arg |= O_APPEND;
998		if (args->arg & LINUX_O_SYNC)
999			arg |= O_FSYNC;
1000		if (args->arg & LINUX_FASYNC)
1001			arg |= O_ASYNC;
1002#ifdef LINUX_O_NOFOLLOW
1003		if (args->arg & LINUX_O_NOFOLLOW)
1004			arg |= O_NOFOLLOW;
1005#endif
1006#ifdef LINUX_O_DIRECT
1007		if (args->arg & LINUX_O_DIRECT)
1008			arg |= O_DIRECT;
1009#endif
1010		return (kern_fcntl(td, args->fd, F_SETFL, arg));
1011
1012	case LINUX_F_GETLK:
1013		error = copyin((void *)args->arg, &linux_flock,
1014		    sizeof(linux_flock));
1015		if (error)
1016			return (error);
1017		linux_to_bsd_flock(&linux_flock, &bsd_flock);
1018		error = kern_fcntl(td, args->fd, F_GETLK, (intptr_t)&bsd_flock);
1019		if (error)
1020			return (error);
1021		bsd_to_linux_flock(&bsd_flock, &linux_flock);
1022		return (copyout(&linux_flock, (void *)args->arg,
1023		    sizeof(linux_flock)));
1024
1025	case LINUX_F_SETLK:
1026		error = copyin((void *)args->arg, &linux_flock,
1027		    sizeof(linux_flock));
1028		if (error)
1029			return (error);
1030		linux_to_bsd_flock(&linux_flock, &bsd_flock);
1031		return (kern_fcntl(td, args->fd, F_SETLK,
1032		    (intptr_t)&bsd_flock));
1033
1034	case LINUX_F_SETLKW:
1035		error = copyin((void *)args->arg, &linux_flock,
1036		    sizeof(linux_flock));
1037		if (error)
1038			return (error);
1039		linux_to_bsd_flock(&linux_flock, &bsd_flock);
1040		return (kern_fcntl(td, args->fd, F_SETLKW,
1041		     (intptr_t)&bsd_flock));
1042
1043	case LINUX_F_GETOWN:
1044		return (kern_fcntl(td, args->fd, F_GETOWN, 0));
1045
1046	case LINUX_F_SETOWN:
1047		/*
1048		 * XXX some Linux applications depend on F_SETOWN having no
1049		 * significant effect for pipes (SIGIO is not delivered for
1050		 * pipes under Linux-2.2.35 at least).
1051		 */
1052		error = fget(td, args->fd, &fp);
1053		if (error)
1054			return (error);
1055		if (fp->f_type == DTYPE_PIPE) {
1056			fdrop(fp, td);
1057			return (EINVAL);
1058		}
1059		fdrop(fp, td);
1060
1061		return (kern_fcntl(td, args->fd, F_SETOWN, args->arg));
1062	}
1063
1064	return (EINVAL);
1065}
1066
1067int
1068linux_fcntl(struct thread *td, struct linux_fcntl_args *args)
1069{
1070	struct linux_fcntl64_args args64;
1071
1072#ifdef DEBUG
1073	if (ldebug(fcntl))
1074		printf(ARGS(fcntl, "%d, %08x, *"), args->fd, args->cmd);
1075#endif
1076
1077	args64.fd = args->fd;
1078	args64.cmd = args->cmd;
1079	args64.arg = args->arg;
1080	return (fcntl_common(td, &args64));
1081}
1082
1083#if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32))
1084int
1085linux_fcntl64(struct thread *td, struct linux_fcntl64_args *args)
1086{
1087	struct l_flock64 linux_flock;
1088	struct flock bsd_flock;
1089	int error;
1090
1091#ifdef DEBUG
1092	if (ldebug(fcntl64))
1093		printf(ARGS(fcntl64, "%d, %08x, *"), args->fd, args->cmd);
1094#endif
1095
1096	switch (args->cmd) {
1097	case LINUX_F_GETLK64:
1098		error = copyin((void *)args->arg, &linux_flock,
1099		    sizeof(linux_flock));
1100		if (error)
1101			return (error);
1102		linux_to_bsd_flock64(&linux_flock, &bsd_flock);
1103		error = kern_fcntl(td, args->fd, F_GETLK, (intptr_t)&bsd_flock);
1104		if (error)
1105			return (error);
1106		bsd_to_linux_flock64(&bsd_flock, &linux_flock);
1107		return (copyout(&linux_flock, (void *)args->arg,
1108			    sizeof(linux_flock)));
1109
1110	case LINUX_F_SETLK64:
1111		error = copyin((void *)args->arg, &linux_flock,
1112		    sizeof(linux_flock));
1113		if (error)
1114			return (error);
1115		linux_to_bsd_flock64(&linux_flock, &bsd_flock);
1116		return (kern_fcntl(td, args->fd, F_SETLK,
1117		    (intptr_t)&bsd_flock));
1118
1119	case LINUX_F_SETLKW64:
1120		error = copyin((void *)args->arg, &linux_flock,
1121		    sizeof(linux_flock));
1122		if (error)
1123			return (error);
1124		linux_to_bsd_flock64(&linux_flock, &bsd_flock);
1125		return (kern_fcntl(td, args->fd, F_SETLKW,
1126		    (intptr_t)&bsd_flock));
1127	}
1128
1129	return (fcntl_common(td, args));
1130}
1131#endif /* __i386__ || (__amd64__ && COMPAT_LINUX32) */
1132
1133int
1134linux_chown(struct thread *td, struct linux_chown_args *args)
1135{
1136	char *path;
1137	int error;
1138
1139	LCONVPATHEXIST(td, args->path, &path);
1140
1141#ifdef DEBUG
1142	if (ldebug(chown))
1143		printf(ARGS(chown, "%s, %d, %d"), path, args->uid, args->gid);
1144#endif
1145	error = kern_chown(td, path, UIO_SYSSPACE, args->uid, args->gid);
1146	LFREEPATH(path);
1147	return (error);
1148}
1149
1150int
1151linux_lchown(struct thread *td, struct linux_lchown_args *args)
1152{
1153	char *path;
1154	int error;
1155
1156	LCONVPATHEXIST(td, args->path, &path);
1157
1158#ifdef DEBUG
1159	if (ldebug(lchown))
1160		printf(ARGS(lchown, "%s, %d, %d"), path, args->uid, args->gid);
1161#endif
1162	error = kern_lchown(td, path, UIO_SYSSPACE, args->uid, args->gid);
1163	LFREEPATH(path);
1164	return (error);
1165}
1166