linux_file.c revision 10355
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 withough 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 *  $Id: linux_file.c,v 1.1 1995/06/25 17:32:34 sos Exp $
29 */
30
31#include <i386/linux/linux.h>
32#include <sys/param.h>
33#include <sys/systm.h>
34#include <sys/fcntl.h>
35#include <sys/file.h>
36#include <sys/filedesc.h>
37#include <sys/proc.h>
38#include <sys/ioctl.h>
39#include <sys/stat.h>
40#include <sys/vnode.h>
41#include <sys/malloc.h>
42#include <sys/exec.h>
43#include <sys/dirent.h>
44#include <vm/vm.h>
45#include <ufs/ufs/dir.h>
46
47
48struct linux_creat_args {
49    char    *path;
50    int mode;
51};
52
53int
54linux_creat(struct proc *p, struct linux_creat_args *args, int *retval)
55{
56    struct {
57	char *path;
58	int flags;
59	int mode;
60    } bsd_open_args;
61
62#ifdef DEBUG
63    printf("Linux-emul(%d): creat(%s, %d)\n",
64	   p->p_pid, args->path, args->mode);
65#endif
66    bsd_open_args.path = args->path;
67    bsd_open_args.mode = args->mode;
68    bsd_open_args.flags = O_WRONLY | O_CREAT | O_TRUNC;
69    return open(p, &bsd_open_args, retval);
70}
71
72struct linux_open_args {
73    char *path;
74    int flags;
75    int mode;
76};
77
78int
79linux_open(struct proc *p, struct linux_open_args *args, int *retval)
80{
81    struct {
82	char *path;
83	int flags;
84	int mode;
85    } bsd_open_args;
86    int error;
87
88#ifdef DEBUG
89    printf("Linux-emul(%d): open(%s, 0x%x, 0x%x)\n",
90	   p->p_pid, args->path, args->flags, args->mode);
91#endif
92    bsd_open_args.flags = 0;
93    if (args->flags & LINUX_O_RDONLY)
94	bsd_open_args.flags |= O_RDONLY;
95    if (args->flags & LINUX_O_WRONLY)
96	bsd_open_args.flags |= O_WRONLY;
97    if (args->flags & LINUX_O_RDWR)
98	bsd_open_args.flags |= O_RDWR;
99    if (args->flags & LINUX_O_NDELAY)
100	bsd_open_args.flags |= O_NONBLOCK;
101    if (args->flags & LINUX_O_APPEND)
102	bsd_open_args.flags |= O_APPEND;
103    if (args->flags & LINUX_O_SYNC)
104	bsd_open_args.flags |= O_FSYNC;
105    if (args->flags & LINUX_O_NONBLOCK)
106	bsd_open_args.flags |= O_NONBLOCK;
107    if (args->flags & LINUX_FASYNC)
108	bsd_open_args.flags |= O_ASYNC;
109    if (args->flags & LINUX_O_CREAT)
110	bsd_open_args.flags |= O_CREAT;
111    if (args->flags & LINUX_O_TRUNC)
112	bsd_open_args.flags |= O_TRUNC;
113    if (args->flags & LINUX_O_EXCL)
114	bsd_open_args.flags |= O_EXCL;
115    if (args->flags & LINUX_O_NOCTTY)
116	bsd_open_args.flags |= O_NOCTTY;
117    bsd_open_args.path = args->path;
118    bsd_open_args.mode = args->mode;
119
120    error = open(p, &bsd_open_args, retval);
121    if (!error && !(bsd_open_args.flags & O_NOCTTY) &&
122	SESS_LEADER(p) && !(p->p_flag & P_CONTROLT)) {
123	struct filedesc *fdp = p->p_fd;
124	struct file *fp = fdp->fd_ofiles[*retval];
125
126	if (fp->f_type == DTYPE_VNODE)
127	    (fp->f_ops->fo_ioctl)(fp, TIOCSCTTY, (caddr_t) 0, p);
128    }
129    return error;
130}
131
132struct linux_flock {
133    short l_type;
134    short l_whence;
135    linux_off_t l_start;
136    linux_off_t l_len;
137    linux_pid_t l_pid;
138};
139
140static void
141linux_to_bsd_flock(struct linux_flock *linux_flock, struct flock *bsd_flock)
142{
143    switch (linux_flock->l_type) {
144    case LINUX_F_RDLCK:
145	bsd_flock->l_type = F_RDLCK;
146	break;
147    case LINUX_F_WRLCK:
148	bsd_flock->l_type = F_WRLCK;
149	break;
150    case LINUX_F_UNLCK:
151	bsd_flock->l_type = F_UNLCK;
152	break;
153    }
154    bsd_flock->l_whence = linux_flock->l_whence;
155    bsd_flock->l_start = (off_t)linux_flock->l_start;
156    bsd_flock->l_len = (off_t)linux_flock->l_len;
157    bsd_flock->l_pid = (pid_t)linux_flock->l_pid;
158}
159
160static void
161bsd_to_linux_flock(struct flock *bsd_flock, struct linux_flock *linux_flock)
162{
163    switch (bsd_flock->l_type) {
164    case F_RDLCK:
165	linux_flock->l_type = LINUX_F_RDLCK;
166	break;
167    case F_WRLCK:
168	linux_flock->l_type = LINUX_F_WRLCK;
169	break;
170    case F_UNLCK:
171	linux_flock->l_type = LINUX_F_UNLCK;
172	break;
173    }
174    linux_flock->l_whence = bsd_flock->l_whence;
175    linux_flock->l_start = (linux_off_t)bsd_flock->l_start;
176    linux_flock->l_len = (linux_off_t)bsd_flock->l_len;
177    linux_flock->l_pid = (linux_pid_t)bsd_flock->l_pid;
178}
179
180struct linux_fcntl_args {
181    int fd;
182    int cmd;
183    int arg;
184};
185
186int
187linux_fcntl(struct proc *p, struct linux_fcntl_args *args, int *retval)
188{
189    int error, result;
190    struct fcntl_args {
191	int fd;
192	int cmd;
193	int arg;
194    } fcntl_args;
195    struct linux_flock linux_flock;
196    struct flock *bsd_flock =
197	(struct flock *)ua_alloc_init(sizeof(struct flock));
198
199#ifdef DEBUG
200    printf("Linux-emul(%d): fcntl(%d, %08x, *)\n",
201	   p->p_pid, args->fd, args->cmd);
202#endif
203    fcntl_args.fd = args->fd;
204    fcntl_args.arg = 0;
205
206    switch (args->cmd) {
207    case LINUX_F_DUPFD:
208	fcntl_args.cmd = F_DUPFD;
209	return fcntl(p, &fcntl_args, retval);
210
211    case LINUX_F_GETFD:
212	fcntl_args.cmd = F_GETFD;
213	return fcntl(p, &fcntl_args, retval);
214
215    case LINUX_F_SETFD:
216	fcntl_args.cmd = F_SETFD;
217	return fcntl(p, &fcntl_args, retval);
218
219    case LINUX_F_GETFL:
220	fcntl_args.cmd = F_GETFL;
221	error = fcntl(p, &fcntl_args, &result);
222	*retval = 0;
223	if (result & O_RDONLY) *retval |= LINUX_O_RDONLY;
224	if (result & O_WRONLY) *retval |= LINUX_O_WRONLY;
225	if (result & O_RDWR) *retval |= LINUX_O_RDWR;
226	if (result & O_NDELAY) *retval |= LINUX_O_NONBLOCK;
227	if (result & O_APPEND) *retval |= LINUX_O_APPEND;
228	if (result & O_FSYNC) *retval |= LINUX_O_SYNC;
229	return error;
230
231    case LINUX_F_SETFL:
232	if (args->arg & LINUX_O_NDELAY) fcntl_args.arg |= O_NONBLOCK;
233	if (args->arg & LINUX_O_APPEND) fcntl_args.arg |= O_APPEND;
234	if (args->arg & LINUX_O_SYNC) fcntl_args.arg |= O_FSYNC;
235	fcntl_args.cmd = F_SETFL;
236	return fcntl(p, &fcntl_args, retval);
237
238    case LINUX_F_GETLK:
239	if ((error = copyin((caddr_t)args->arg, (caddr_t)&linux_flock,
240		   	    sizeof(struct linux_flock))))
241	    return error;
242	linux_to_bsd_flock(&linux_flock, bsd_flock);
243	fcntl_args.cmd = F_GETLK;
244	fcntl_args.arg = (int)bsd_flock;
245	if (error = fcntl(p, &fcntl_args, retval))
246	    return error;
247	bsd_to_linux_flock(bsd_flock, &linux_flock);
248	return copyout((caddr_t)&linux_flock, (caddr_t)args->arg,
249		       sizeof(struct linux_flock));
250
251    case LINUX_F_SETLK:
252	if ((error = copyin((caddr_t)args->arg, (caddr_t)&linux_flock,
253		   	    sizeof(struct linux_flock))))
254	    return error;
255	linux_to_bsd_flock(&linux_flock, bsd_flock);
256	fcntl_args.cmd = F_SETLK;
257	fcntl_args.arg = (int)bsd_flock;
258	return fcntl(p, &fcntl_args, retval);
259
260    case LINUX_F_SETLKW:
261	if ((error = copyin((caddr_t)args->arg, (caddr_t)&linux_flock,
262		   	    sizeof(struct linux_flock))))
263	    return error;
264	linux_to_bsd_flock(&linux_flock, bsd_flock);
265	fcntl_args.cmd = F_SETLKW;
266	fcntl_args.arg = (int)bsd_flock;
267	return fcntl(p, &fcntl_args, retval);
268
269    case LINUX_F_SETOWN:
270	fcntl_args.cmd = F_SETOWN;
271	return fcntl(p, &fcntl_args, retval);
272
273    case LINUX_F_GETOWN:
274	fcntl_args.cmd = F_GETOWN;
275	return fcntl(p, &fcntl_args, retval);
276    }
277    return EINVAL;
278}
279
280struct linux_lseek_args {
281    int fdes;
282    unsigned long off;
283    int whence;
284};
285
286int
287linux_lseek(struct proc *p, struct linux_lseek_args *args, int *retval)
288{
289
290    struct lseek_args {
291	int fdes;
292	int pad;
293	off_t off;
294	int whence;
295    } tmp_args;
296    off_t tmp_retval;
297    int error;
298
299#ifdef DEBUG
300    printf("Linux-emul(%d): lseek(%d, %d, %d)\n",
301	   p->p_pid, args->fdes, args->off, args->whence);
302#endif
303    tmp_args.fdes = args->fdes;
304    tmp_args.off = (off_t)args->off;
305    tmp_args.whence = args->whence;
306    error = lseek(p, &tmp_args, &tmp_retval);
307    *retval = (int)tmp_retval;
308    return error;
309}
310
311struct linux_dirent {
312    long dino;
313    linux_off_t doff;
314    unsigned short dreclen;
315    char dname[LINUX_NAME_MAX + 1];
316};
317
318#define LINUX_RECLEN(de,namlen) \
319    ALIGN((((char *)&(de)->dname - (char *)de) + (namlen) + 1))
320
321struct linux_readdir_args {
322    int fd;
323    struct linux_dirent *dent;
324    unsigned int count;
325};
326
327int
328linux_readdir(struct proc *p, struct linux_readdir_args *args, int *retval)
329{
330    register struct dirent *bdp;
331    struct vnode *vp;
332    caddr_t inp, buf;		/* BSD-format */
333    int len, reclen;		/* BSD-format */
334    caddr_t outp;		/* Linux-format */
335    int resid, linuxreclen=0;	/* Linux-format */
336    struct file *fp;
337    struct uio auio;
338    struct iovec aiov;
339    struct vattr va;
340    off_t off;
341    struct linux_dirent linux_dirent;
342    int buflen, error, eofflag, nbytes, justone, blockoff;
343
344#ifdef DEBUG
345    printf("Linux-emul(%d): readdir(%d, *, %d)\n",
346	   p->p_pid, args->fd, args->count);
347#endif
348    if ((error = getvnode(p->p_fd, args->fd, &fp)) != 0) {
349	return (error);
350}
351
352    if ((fp->f_flag & FREAD) == 0)
353	return (EBADF);
354
355    vp = (struct vnode *) fp->f_data;
356
357    if (vp->v_type != VDIR)
358	return (EINVAL);
359
360    if ((error = VOP_GETATTR(vp, &va, p->p_ucred, p))) {
361	return error;
362      }
363
364    nbytes = args->count;
365    if (nbytes == 1) {
366	nbytes = sizeof (struct linux_dirent);
367	justone = 1;
368    }
369    else
370	justone = 0;
371
372    off = fp->f_offset;
373    blockoff = off % va.va_blocksize;
374    buflen = max(va.va_blocksize, (nbytes + blockoff));
375    buf = malloc(buflen, M_TEMP, M_WAITOK);
376    VOP_LOCK(vp);
377again:
378    aiov.iov_base = buf;
379    aiov.iov_len = buflen;
380    auio.uio_iov = &aiov;
381    auio.uio_iovcnt = 1;
382    auio.uio_rw = UIO_READ;
383    auio.uio_segflg = UIO_SYSSPACE;
384    auio.uio_procp = p;
385    auio.uio_resid = buflen;
386    auio.uio_offset = off - (off_t)blockoff;
387
388    error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, (u_long *) 0, 0);
389    if (error) {
390	goto out;
391}
392
393    inp = buf;
394    inp += blockoff;
395    outp = (caddr_t) args->dent;
396    resid = nbytes;
397    if ((len = buflen - auio.uio_resid - blockoff) == 0) {
398	goto eof;
399      }
400
401    while (len > 0) {
402	bdp = (struct dirent *) inp;
403	reclen = bdp->d_reclen;
404	if (reclen & 3) {
405	    printf("linux_readdir: reclen=%d\n", reclen);
406	    error = EFAULT;
407	    goto out;
408	}
409
410	off += reclen;
411	if (bdp->d_fileno == 0) {
412	    inp += reclen;
413	    len -= reclen;
414	    continue;
415	}
416	linuxreclen = LINUX_RECLEN(&linux_dirent, bdp->d_namlen);
417	if (reclen > len || resid < linuxreclen) {
418	    outp++;
419	    break;
420	}
421	linux_dirent.dino = (long) bdp->d_fileno;
422	linux_dirent.doff = (linux_off_t) linuxreclen;
423	linux_dirent.dreclen = (u_short) bdp->d_namlen;
424	strcpy(linux_dirent.dname, bdp->d_name);
425	if ((error = copyout((caddr_t)&linux_dirent, outp, linuxreclen))) {
426	    goto out;
427	  }
428	inp += reclen;
429	outp += linuxreclen;
430	resid -= linuxreclen;
431	len -= reclen;
432	if (justone)
433	    break;
434    }
435
436    if (outp == (caddr_t) args->dent)
437	goto again;
438    fp->f_offset = off;
439
440    if (justone)
441	nbytes = resid + linuxreclen;
442
443eof:
444    *retval = nbytes - resid;
445out:
446    VOP_UNLOCK(vp);
447    free(buf, M_TEMP);
448    return error;
449}
450