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