linux_file.c revision 12458
19313Ssos/*-
29313Ssos * Copyright (c) 1994-1995 S�ren Schmidt
39313Ssos * All rights reserved.
49313Ssos *
59313Ssos * Redistribution and use in source and binary forms, with or without
69313Ssos * modification, are permitted provided that the following conditions
79313Ssos * are met:
89313Ssos * 1. Redistributions of source code must retain the above copyright
99313Ssos *    notice, this list of conditions and the following disclaimer
109313Ssos *    in this position and unchanged.
119313Ssos * 2. Redistributions in binary form must reproduce the above copyright
129313Ssos *    notice, this list of conditions and the following disclaimer in the
139313Ssos *    documentation and/or other materials provided with the distribution.
149313Ssos * 3. The name of the author may not be used to endorse or promote products
159313Ssos *    derived from this software withough specific prior written permission
169313Ssos *
179313Ssos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
189313Ssos * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
199313Ssos * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
209313Ssos * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
219313Ssos * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
229313Ssos * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
239313Ssos * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
249313Ssos * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
259313Ssos * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
269313Ssos * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
279313Ssos *
2812458Sbde *  $Id: linux_file.c,v 1.3 1995/10/10 23:13:27 swallace Exp $
299313Ssos */
309313Ssos
319313Ssos#include <sys/param.h>
329313Ssos#include <sys/systm.h>
3312458Sbde#include <sys/sysproto.h>
349313Ssos#include <sys/fcntl.h>
359313Ssos#include <sys/file.h>
369313Ssos#include <sys/filedesc.h>
379313Ssos#include <sys/proc.h>
389313Ssos#include <sys/ioctl.h>
399313Ssos#include <sys/stat.h>
409313Ssos#include <sys/vnode.h>
419313Ssos#include <sys/malloc.h>
429313Ssos#include <sys/exec.h>
439313Ssos#include <sys/dirent.h>
4412458Sbde
459313Ssos#include <ufs/ufs/dir.h>
469313Ssos
4712458Sbde#include <i386/linux/linux.h>
4812458Sbde#include <i386/linux/sysproto.h>
499313Ssos
509313Ssosstruct linux_creat_args {
519313Ssos    char    *path;
529313Ssos    int mode;
539313Ssos};
549313Ssos
559313Ssosint
569313Ssoslinux_creat(struct proc *p, struct linux_creat_args *args, int *retval)
579313Ssos{
589313Ssos    struct {
599313Ssos	char *path;
609313Ssos	int flags;
619313Ssos	int mode;
629313Ssos    } bsd_open_args;
639313Ssos
649313Ssos#ifdef DEBUG
659313Ssos    printf("Linux-emul(%d): creat(%s, %d)\n",
669313Ssos	   p->p_pid, args->path, args->mode);
679313Ssos#endif
689313Ssos    bsd_open_args.path = args->path;
699313Ssos    bsd_open_args.mode = args->mode;
709313Ssos    bsd_open_args.flags = O_WRONLY | O_CREAT | O_TRUNC;
719313Ssos    return open(p, &bsd_open_args, retval);
729313Ssos}
739313Ssos
749313Ssosstruct linux_open_args {
759313Ssos    char *path;
769313Ssos    int flags;
779313Ssos    int mode;
789313Ssos};
799313Ssos
809313Ssosint
819313Ssoslinux_open(struct proc *p, struct linux_open_args *args, int *retval)
829313Ssos{
839313Ssos    struct {
849313Ssos	char *path;
859313Ssos	int flags;
869313Ssos	int mode;
879313Ssos    } bsd_open_args;
889313Ssos    int error;
899313Ssos
909313Ssos#ifdef DEBUG
919313Ssos    printf("Linux-emul(%d): open(%s, 0x%x, 0x%x)\n",
929313Ssos	   p->p_pid, args->path, args->flags, args->mode);
939313Ssos#endif
949313Ssos    bsd_open_args.flags = 0;
959313Ssos    if (args->flags & LINUX_O_RDONLY)
969313Ssos	bsd_open_args.flags |= O_RDONLY;
979313Ssos    if (args->flags & LINUX_O_WRONLY)
989313Ssos	bsd_open_args.flags |= O_WRONLY;
999313Ssos    if (args->flags & LINUX_O_RDWR)
1009313Ssos	bsd_open_args.flags |= O_RDWR;
1019313Ssos    if (args->flags & LINUX_O_NDELAY)
1029313Ssos	bsd_open_args.flags |= O_NONBLOCK;
1039313Ssos    if (args->flags & LINUX_O_APPEND)
1049313Ssos	bsd_open_args.flags |= O_APPEND;
1059313Ssos    if (args->flags & LINUX_O_SYNC)
1069313Ssos	bsd_open_args.flags |= O_FSYNC;
1079313Ssos    if (args->flags & LINUX_O_NONBLOCK)
1089313Ssos	bsd_open_args.flags |= O_NONBLOCK;
1099313Ssos    if (args->flags & LINUX_FASYNC)
1109313Ssos	bsd_open_args.flags |= O_ASYNC;
1119313Ssos    if (args->flags & LINUX_O_CREAT)
1129313Ssos	bsd_open_args.flags |= O_CREAT;
1139313Ssos    if (args->flags & LINUX_O_TRUNC)
1149313Ssos	bsd_open_args.flags |= O_TRUNC;
1159313Ssos    if (args->flags & LINUX_O_EXCL)
1169313Ssos	bsd_open_args.flags |= O_EXCL;
1179313Ssos    if (args->flags & LINUX_O_NOCTTY)
1189313Ssos	bsd_open_args.flags |= O_NOCTTY;
1199313Ssos    bsd_open_args.path = args->path;
1209313Ssos    bsd_open_args.mode = args->mode;
1219313Ssos
1229313Ssos    error = open(p, &bsd_open_args, retval);
1239313Ssos    if (!error && !(bsd_open_args.flags & O_NOCTTY) &&
1249313Ssos	SESS_LEADER(p) && !(p->p_flag & P_CONTROLT)) {
1259313Ssos	struct filedesc *fdp = p->p_fd;
1269313Ssos	struct file *fp = fdp->fd_ofiles[*retval];
1279313Ssos
1289313Ssos	if (fp->f_type == DTYPE_VNODE)
1299313Ssos	    (fp->f_ops->fo_ioctl)(fp, TIOCSCTTY, (caddr_t) 0, p);
1309313Ssos    }
1319313Ssos    return error;
1329313Ssos}
1339313Ssos
1349313Ssosstruct linux_flock {
1359313Ssos    short l_type;
1369313Ssos    short l_whence;
1379313Ssos    linux_off_t l_start;
1389313Ssos    linux_off_t l_len;
1399313Ssos    linux_pid_t l_pid;
1409313Ssos};
1419313Ssos
1429313Ssosstatic void
1439313Ssoslinux_to_bsd_flock(struct linux_flock *linux_flock, struct flock *bsd_flock)
1449313Ssos{
1459313Ssos    switch (linux_flock->l_type) {
1469313Ssos    case LINUX_F_RDLCK:
1479313Ssos	bsd_flock->l_type = F_RDLCK;
1489313Ssos	break;
1499313Ssos    case LINUX_F_WRLCK:
1509313Ssos	bsd_flock->l_type = F_WRLCK;
1519313Ssos	break;
1529313Ssos    case LINUX_F_UNLCK:
1539313Ssos	bsd_flock->l_type = F_UNLCK;
1549313Ssos	break;
1559313Ssos    }
1569313Ssos    bsd_flock->l_whence = linux_flock->l_whence;
1579313Ssos    bsd_flock->l_start = (off_t)linux_flock->l_start;
1589313Ssos    bsd_flock->l_len = (off_t)linux_flock->l_len;
1599313Ssos    bsd_flock->l_pid = (pid_t)linux_flock->l_pid;
1609313Ssos}
1619313Ssos
1629313Ssosstatic void
1639313Ssosbsd_to_linux_flock(struct flock *bsd_flock, struct linux_flock *linux_flock)
1649313Ssos{
1659313Ssos    switch (bsd_flock->l_type) {
1669313Ssos    case F_RDLCK:
1679313Ssos	linux_flock->l_type = LINUX_F_RDLCK;
1689313Ssos	break;
1699313Ssos    case F_WRLCK:
1709313Ssos	linux_flock->l_type = LINUX_F_WRLCK;
1719313Ssos	break;
1729313Ssos    case F_UNLCK:
1739313Ssos	linux_flock->l_type = LINUX_F_UNLCK;
1749313Ssos	break;
1759313Ssos    }
1769313Ssos    linux_flock->l_whence = bsd_flock->l_whence;
1779313Ssos    linux_flock->l_start = (linux_off_t)bsd_flock->l_start;
1789313Ssos    linux_flock->l_len = (linux_off_t)bsd_flock->l_len;
1799313Ssos    linux_flock->l_pid = (linux_pid_t)bsd_flock->l_pid;
1809313Ssos}
1819313Ssos
1829313Ssosstruct linux_fcntl_args {
1839313Ssos    int fd;
1849313Ssos    int cmd;
1859313Ssos    int arg;
1869313Ssos};
1879313Ssos
1889313Ssosint
1899313Ssoslinux_fcntl(struct proc *p, struct linux_fcntl_args *args, int *retval)
1909313Ssos{
1919313Ssos    int error, result;
1929313Ssos    struct fcntl_args {
1939313Ssos	int fd;
1949313Ssos	int cmd;
1959313Ssos	int arg;
1969313Ssos    } fcntl_args;
1979313Ssos    struct linux_flock linux_flock;
1989313Ssos    struct flock *bsd_flock =
1999313Ssos	(struct flock *)ua_alloc_init(sizeof(struct flock));
2009313Ssos
2019313Ssos#ifdef DEBUG
2029313Ssos    printf("Linux-emul(%d): fcntl(%d, %08x, *)\n",
2039313Ssos	   p->p_pid, args->fd, args->cmd);
2049313Ssos#endif
2059313Ssos    fcntl_args.fd = args->fd;
2069313Ssos    fcntl_args.arg = 0;
2079313Ssos
2089313Ssos    switch (args->cmd) {
2099313Ssos    case LINUX_F_DUPFD:
2109313Ssos	fcntl_args.cmd = F_DUPFD;
2119313Ssos	return fcntl(p, &fcntl_args, retval);
2129313Ssos
2139313Ssos    case LINUX_F_GETFD:
2149313Ssos	fcntl_args.cmd = F_GETFD;
2159313Ssos	return fcntl(p, &fcntl_args, retval);
2169313Ssos
2179313Ssos    case LINUX_F_SETFD:
2189313Ssos	fcntl_args.cmd = F_SETFD;
2199313Ssos	return fcntl(p, &fcntl_args, retval);
2209313Ssos
2219313Ssos    case LINUX_F_GETFL:
2229313Ssos	fcntl_args.cmd = F_GETFL;
2239313Ssos	error = fcntl(p, &fcntl_args, &result);
2249313Ssos	*retval = 0;
2259313Ssos	if (result & O_RDONLY) *retval |= LINUX_O_RDONLY;
2269313Ssos	if (result & O_WRONLY) *retval |= LINUX_O_WRONLY;
2279313Ssos	if (result & O_RDWR) *retval |= LINUX_O_RDWR;
2289313Ssos	if (result & O_NDELAY) *retval |= LINUX_O_NONBLOCK;
2299313Ssos	if (result & O_APPEND) *retval |= LINUX_O_APPEND;
2309313Ssos	if (result & O_FSYNC) *retval |= LINUX_O_SYNC;
2319313Ssos	return error;
2329313Ssos
2339313Ssos    case LINUX_F_SETFL:
2349313Ssos	if (args->arg & LINUX_O_NDELAY) fcntl_args.arg |= O_NONBLOCK;
2359313Ssos	if (args->arg & LINUX_O_APPEND) fcntl_args.arg |= O_APPEND;
2369313Ssos	if (args->arg & LINUX_O_SYNC) fcntl_args.arg |= O_FSYNC;
2379313Ssos	fcntl_args.cmd = F_SETFL;
2389313Ssos	return fcntl(p, &fcntl_args, retval);
2399313Ssos
2409313Ssos    case LINUX_F_GETLK:
2419313Ssos	if ((error = copyin((caddr_t)args->arg, (caddr_t)&linux_flock,
2429313Ssos		   	    sizeof(struct linux_flock))))
2439313Ssos	    return error;
2449313Ssos	linux_to_bsd_flock(&linux_flock, bsd_flock);
2459313Ssos	fcntl_args.cmd = F_GETLK;
2469313Ssos	fcntl_args.arg = (int)bsd_flock;
2479313Ssos	if (error = fcntl(p, &fcntl_args, retval))
2489313Ssos	    return error;
2499313Ssos	bsd_to_linux_flock(bsd_flock, &linux_flock);
2509313Ssos	return copyout((caddr_t)&linux_flock, (caddr_t)args->arg,
2519313Ssos		       sizeof(struct linux_flock));
2529313Ssos
2539313Ssos    case LINUX_F_SETLK:
2549313Ssos	if ((error = copyin((caddr_t)args->arg, (caddr_t)&linux_flock,
2559313Ssos		   	    sizeof(struct linux_flock))))
2569313Ssos	    return error;
2579313Ssos	linux_to_bsd_flock(&linux_flock, bsd_flock);
2589313Ssos	fcntl_args.cmd = F_SETLK;
2599313Ssos	fcntl_args.arg = (int)bsd_flock;
2609313Ssos	return fcntl(p, &fcntl_args, retval);
2619313Ssos
2629313Ssos    case LINUX_F_SETLKW:
2639313Ssos	if ((error = copyin((caddr_t)args->arg, (caddr_t)&linux_flock,
2649313Ssos		   	    sizeof(struct linux_flock))))
2659313Ssos	    return error;
2669313Ssos	linux_to_bsd_flock(&linux_flock, bsd_flock);
2679313Ssos	fcntl_args.cmd = F_SETLKW;
2689313Ssos	fcntl_args.arg = (int)bsd_flock;
2699313Ssos	return fcntl(p, &fcntl_args, retval);
2709313Ssos
2719313Ssos    case LINUX_F_SETOWN:
2729313Ssos	fcntl_args.cmd = F_SETOWN;
2739313Ssos	return fcntl(p, &fcntl_args, retval);
2749313Ssos
2759313Ssos    case LINUX_F_GETOWN:
2769313Ssos	fcntl_args.cmd = F_GETOWN;
2779313Ssos	return fcntl(p, &fcntl_args, retval);
2789313Ssos    }
2799313Ssos    return EINVAL;
2809313Ssos}
2819313Ssos
2829313Ssosstruct linux_lseek_args {
2839313Ssos    int fdes;
2849313Ssos    unsigned long off;
2859313Ssos    int whence;
2869313Ssos};
2879313Ssos
2889313Ssosint
2899313Ssoslinux_lseek(struct proc *p, struct linux_lseek_args *args, int *retval)
2909313Ssos{
2919313Ssos
2929313Ssos    struct lseek_args {
2939313Ssos	int fdes;
2949313Ssos	int pad;
2959313Ssos	off_t off;
2969313Ssos	int whence;
2979313Ssos    } tmp_args;
2989313Ssos    off_t tmp_retval;
2999313Ssos    int error;
3009313Ssos
3019313Ssos#ifdef DEBUG
3029313Ssos    printf("Linux-emul(%d): lseek(%d, %d, %d)\n",
3039313Ssos	   p->p_pid, args->fdes, args->off, args->whence);
3049313Ssos#endif
3059313Ssos    tmp_args.fdes = args->fdes;
3069313Ssos    tmp_args.off = (off_t)args->off;
3079313Ssos    tmp_args.whence = args->whence;
3089313Ssos    error = lseek(p, &tmp_args, &tmp_retval);
3099313Ssos    *retval = (int)tmp_retval;
3109313Ssos    return error;
3119313Ssos}
3129313Ssos
3139313Ssosstruct linux_dirent {
3149313Ssos    long dino;
3159313Ssos    linux_off_t doff;
3169313Ssos    unsigned short dreclen;
3179313Ssos    char dname[LINUX_NAME_MAX + 1];
3189313Ssos};
3199313Ssos
3209313Ssos#define LINUX_RECLEN(de,namlen) \
3219313Ssos    ALIGN((((char *)&(de)->dname - (char *)de) + (namlen) + 1))
3229313Ssos
3239313Ssosstruct linux_readdir_args {
3249313Ssos    int fd;
3259313Ssos    struct linux_dirent *dent;
3269313Ssos    unsigned int count;
3279313Ssos};
3289313Ssos
3299313Ssosint
3309313Ssoslinux_readdir(struct proc *p, struct linux_readdir_args *args, int *retval)
3319313Ssos{
3329313Ssos    register struct dirent *bdp;
3339313Ssos    struct vnode *vp;
3349313Ssos    caddr_t inp, buf;		/* BSD-format */
3359313Ssos    int len, reclen;		/* BSD-format */
3369313Ssos    caddr_t outp;		/* Linux-format */
3379313Ssos    int resid, linuxreclen=0;	/* Linux-format */
3389313Ssos    struct file *fp;
3399313Ssos    struct uio auio;
3409313Ssos    struct iovec aiov;
3419313Ssos    struct vattr va;
3429313Ssos    off_t off;
3439313Ssos    struct linux_dirent linux_dirent;
34410355Sswallace    int buflen, error, eofflag, nbytes, justone, blockoff;
3459313Ssos
3469313Ssos#ifdef DEBUG
3479313Ssos    printf("Linux-emul(%d): readdir(%d, *, %d)\n",
3489313Ssos	   p->p_pid, args->fd, args->count);
3499313Ssos#endif
35010355Sswallace    if ((error = getvnode(p->p_fd, args->fd, &fp)) != 0) {
3519313Ssos	return (error);
35210355Sswallace}
3539313Ssos
3549313Ssos    if ((fp->f_flag & FREAD) == 0)
3559313Ssos	return (EBADF);
3569313Ssos
3579313Ssos    vp = (struct vnode *) fp->f_data;
3589313Ssos
3599313Ssos    if (vp->v_type != VDIR)
3609313Ssos	return (EINVAL);
3619313Ssos
36210355Sswallace    if ((error = VOP_GETATTR(vp, &va, p->p_ucred, p))) {
3639313Ssos	return error;
36411418Sswallace    }
3659313Ssos
3669313Ssos    nbytes = args->count;
3679313Ssos    if (nbytes == 1) {
3689313Ssos	nbytes = sizeof (struct linux_dirent);
3699313Ssos	justone = 1;
3709313Ssos    }
3719313Ssos    else
3729313Ssos	justone = 0;
3739313Ssos
37410355Sswallace    off = fp->f_offset;
37511418Sswallace    blockoff = off % DIRBLKSIZ;
37611418Sswallace    buflen = max(DIRBLKSIZ, nbytes + blockoff);
37711418Sswallace    buflen = min(buflen, MAXBSIZE);
3789313Ssos    buf = malloc(buflen, M_TEMP, M_WAITOK);
3799313Ssos    VOP_LOCK(vp);
3809313Ssosagain:
3819313Ssos    aiov.iov_base = buf;
3829313Ssos    aiov.iov_len = buflen;
3839313Ssos    auio.uio_iov = &aiov;
3849313Ssos    auio.uio_iovcnt = 1;
3859313Ssos    auio.uio_rw = UIO_READ;
3869313Ssos    auio.uio_segflg = UIO_SYSSPACE;
3879313Ssos    auio.uio_procp = p;
3889313Ssos    auio.uio_resid = buflen;
38910355Sswallace    auio.uio_offset = off - (off_t)blockoff;
3909313Ssos
39112458Sbde    error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, (int *) NULL,
39212458Sbde			(u_int **) NULL);
39310355Sswallace    if (error) {
3949313Ssos	goto out;
39510355Sswallace}
3969313Ssos
3979313Ssos    inp = buf;
39810355Sswallace    inp += blockoff;
3999313Ssos    outp = (caddr_t) args->dent;
4009313Ssos    resid = nbytes;
40110355Sswallace    if ((len = buflen - auio.uio_resid - blockoff) == 0) {
4029313Ssos	goto eof;
40310355Sswallace      }
4049313Ssos
4059313Ssos    while (len > 0) {
40610355Sswallace	bdp = (struct dirent *) inp;
40710355Sswallace	reclen = bdp->d_reclen;
40810355Sswallace	if (reclen & 3) {
40910355Sswallace	    printf("linux_readdir: reclen=%d\n", reclen);
41010355Sswallace	    error = EFAULT;
41110355Sswallace	    goto out;
41210355Sswallace	}
41310355Sswallace
4149313Ssos	if (bdp->d_fileno == 0) {
4159313Ssos	    inp += reclen;
41611418Sswallace	    off += reclen;
41710355Sswallace	    len -= reclen;
4189313Ssos	    continue;
4199313Ssos	}
4209313Ssos	linuxreclen = LINUX_RECLEN(&linux_dirent, bdp->d_namlen);
4219313Ssos	if (reclen > len || resid < linuxreclen) {
4229313Ssos	    outp++;
4239313Ssos	    break;
4249313Ssos	}
4259313Ssos	linux_dirent.dino = (long) bdp->d_fileno;
4269313Ssos	linux_dirent.doff = (linux_off_t) linuxreclen;
4279313Ssos	linux_dirent.dreclen = (u_short) bdp->d_namlen;
4289313Ssos	strcpy(linux_dirent.dname, bdp->d_name);
42910355Sswallace	if ((error = copyout((caddr_t)&linux_dirent, outp, linuxreclen))) {
4309313Ssos	    goto out;
43110355Sswallace	  }
4329313Ssos	inp += reclen;
43311418Sswallace	off += reclen;
4349313Ssos	outp += linuxreclen;
4359313Ssos	resid -= linuxreclen;
4369313Ssos	len -= reclen;
4379313Ssos	if (justone)
4389313Ssos	    break;
4399313Ssos    }
4409313Ssos
4419313Ssos    if (outp == (caddr_t) args->dent)
4429313Ssos	goto again;
4439313Ssos    fp->f_offset = off;
4449313Ssos
4459313Ssos    if (justone)
4469313Ssos	nbytes = resid + linuxreclen;
44710355Sswallace
4489313Ssoseof:
4499313Ssos    *retval = nbytes - resid;
4509313Ssosout:
4519313Ssos    VOP_UNLOCK(vp);
4529313Ssos    free(buf, M_TEMP);
4539313Ssos    return error;
4549313Ssos}
455