linux_llseek.c revision 1.9 1 /* $NetBSD: linux_llseek.c,v 1.9 1995/08/27 20:51:48 fvdl Exp $ */
2
3 /*
4 * Copyright (c) 1995 Frank van der Linden
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed for the NetBSD Project
18 * by Frank van der Linden
19 * 4. The name of the author may not be used to endorse or promote products
20 * derived from this software without specific prior written permission
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/namei.h>
37 #include <sys/proc.h>
38 #include <sys/file.h>
39 #include <sys/stat.h>
40 #include <sys/filedesc.h>
41 #include <sys/ioctl.h>
42 #include <sys/kernel.h>
43 #include <sys/mount.h>
44 #include <sys/malloc.h>
45
46 #include <sys/syscallargs.h>
47
48 #include <compat/linux/linux_types.h>
49 #include <compat/linux/linux_signal.h>
50 #include <compat/linux/linux_syscallargs.h>
51 #include <compat/linux/linux_fcntl.h>
52 #include <compat/linux/linux_util.h>
53
54 /*
55 * Some file-related calls are handled here. The usual flag conversion
56 * an structure conversion is done, and alternate emul path searching.
57 */
58
59 /*
60 * The next two functions convert between the Linux and NetBSD values
61 * of the flags used in open(2) and fcntl(2).
62 */
63 static int
64 linux_to_bsd_ioflags(int lflags)
65 {
66 int res = 0;
67
68 res |= cvtto_bsd_mask(lflags, LINUX_O_WRONLY, O_WRONLY);
69 res |= cvtto_bsd_mask(lflags, LINUX_O_RDONLY, O_RDONLY);
70 res |= cvtto_bsd_mask(lflags, LINUX_O_RDWR, O_RDWR);
71 res |= cvtto_bsd_mask(lflags, LINUX_O_CREAT, O_CREAT);
72 res |= cvtto_bsd_mask(lflags, LINUX_O_EXCL, O_EXCL);
73 res |= cvtto_bsd_mask(lflags, LINUX_O_NOCTTY, O_NOCTTY);
74 res |= cvtto_bsd_mask(lflags, LINUX_O_TRUNC, O_TRUNC);
75 res |= cvtto_bsd_mask(lflags, LINUX_O_NDELAY, O_NDELAY);
76 res |= cvtto_bsd_mask(lflags, LINUX_O_SYNC, O_FSYNC);
77 res |= cvtto_bsd_mask(lflags, LINUX_FASYNC, O_ASYNC);
78 res |= cvtto_bsd_mask(lflags, LINUX_O_APPEND, O_APPEND);
79
80 return res;
81 }
82
83 static int
84 bsd_to_linux_ioflags(int bflags)
85 {
86 int res = 0;
87
88 res |= cvtto_linux_mask(bflags, O_WRONLY, LINUX_O_WRONLY);
89 res |= cvtto_linux_mask(bflags, O_RDONLY, LINUX_O_RDONLY);
90 res |= cvtto_linux_mask(bflags, O_RDWR, LINUX_O_RDWR);
91 res |= cvtto_linux_mask(bflags, O_CREAT, LINUX_O_CREAT);
92 res |= cvtto_linux_mask(bflags, O_EXCL, LINUX_O_EXCL);
93 res |= cvtto_linux_mask(bflags, O_NOCTTY, LINUX_O_NOCTTY);
94 res |= cvtto_linux_mask(bflags, O_TRUNC, LINUX_O_TRUNC);
95 res |= cvtto_linux_mask(bflags, O_NDELAY, LINUX_O_NDELAY);
96 res |= cvtto_linux_mask(bflags, O_FSYNC, LINUX_O_SYNC);
97 res |= cvtto_linux_mask(bflags, O_ASYNC, LINUX_FASYNC);
98 res |= cvtto_linux_mask(bflags, O_APPEND, LINUX_O_APPEND);
99
100 return res;
101 }
102
103 /*
104 * creat(2) is an obsolete function, but it's present as a Linux
105 * system call, so let's deal with it.
106 *
107 * Just call open(2) with the TRUNC, CREAT and WRONLY flags.
108 */
109 int
110 linux_creat(p, uap, retval)
111 struct proc *p;
112 struct linux_creat_args /* {
113 syscallarg(char *) path;
114 syscallarg(int) mode;
115 } */ *uap;
116 register_t *retval;
117 {
118 struct open_args oa;
119 caddr_t sg;
120
121 sg = stackgap_init(p->p_emul);
122 LINUX_CHECK_ALT_CREAT(p, &sg, SCARG(uap, path));
123
124 SCARG(&oa, path) = SCARG(uap, path);
125 SCARG(&oa, flags) = O_CREAT | O_TRUNC | O_WRONLY;
126 SCARG(&oa, mode) = SCARG(uap, mode);
127 return open(p, &oa, retval);
128 }
129
130 /*
131 * open(2). Take care of the different flag values, and let the
132 * NetBSD syscall do the real work. See if this operation
133 * gives the current process a controlling terminal.
134 * (XXX is this necessary?)
135 */
136 int
137 linux_open(p, uap, retval)
138 struct proc *p;
139 struct linux_open_args /* {
140 syscallarg(char *) path;
141 syscallarg(int) flags;
142 syscallarg(int) mode;
143 } */ *uap;
144 register_t *retval;
145 {
146 int error, fl;
147 struct open_args boa;
148 caddr_t sg;
149
150 sg = stackgap_init(p->p_emul);
151
152 fl = linux_to_bsd_ioflags(SCARG(uap, flags));
153
154 if (fl & O_CREAT)
155 LINUX_CHECK_ALT_CREAT(p, &sg, SCARG(uap, path));
156 else
157 LINUX_CHECK_ALT_EXIST(p, &sg, SCARG(uap, path));
158
159 SCARG(&boa, path) = SCARG(uap, path);
160 SCARG(&boa, flags) = fl;
161 SCARG(&boa, mode) = SCARG(uap, mode);
162
163 if ((error = open(p, &boa, retval)))
164 return error;
165
166 /*
167 * this bit from sunos_misc.c (and svr4_fcntl.c).
168 * If we are a session leader, and we don't have a controlling
169 * terminal yet, and the O_NOCTTY flag is not set, try to make
170 * this the controlling terminal.
171 */
172 if (!(fl & O_NOCTTY) && SESS_LEADER(p) && !(p->p_flag & P_CONTROLT)) {
173 struct filedesc *fdp = p->p_fd;
174 struct file *fp = fdp->fd_ofiles[*retval];
175
176 /* ignore any error, just give it a try */
177 if (fp->f_type == DTYPE_VNODE)
178 (fp->f_ops->fo_ioctl) (fp, TIOCSCTTY, (caddr_t) 0, p);
179 }
180 return 0;
181 }
182
183 /*
184 * This appears to be part of a Linux attempt to switch to 64 bits file sizes.
185 */
186 int
187 linux_llseek(p, uap, retval)
188 struct proc *p;
189 struct linux_llseek_args /* {
190 syscallarg(int) fd;
191 syscallarg(uint32_t) ohigh;
192 syscallarg(uint32_t) olow;
193 syscallarg(caddr_t) res;
194 syscallarg(int) whence;
195 } */ *uap;
196 register_t *retval;
197 {
198 struct lseek_args bla;
199 int error;
200 off_t off;
201
202 off = SCARG(uap, olow) | (((off_t) SCARG(uap, ohigh)) << 32);
203
204 SCARG(&bla, fd) = SCARG(uap, fd);
205 SCARG(&bla, offset) = off;
206 SCARG(&bla, whence) = SCARG(uap, whence);
207
208 if ((error = lseek(p, &bla, retval)))
209 return error;
210
211 if ((error = copyout(retval, SCARG(uap, res), sizeof (off_t))))
212 return error;
213
214 retval[0] = 0;
215 return 0;
216 }
217
218 /*
219 * The next two functions take care of converting the flock
220 * structure back and forth between Linux and NetBSD format.
221 * The only difference in the structures is the order of
222 * the fields, and the 'whence' value.
223 */
224 static void
225 bsd_to_linux_flock(bfp, lfp)
226 struct flock *bfp;
227 struct linux_flock *lfp;
228 {
229 lfp->l_start = bfp->l_start;
230 lfp->l_len = bfp->l_len;
231 lfp->l_pid = bfp->l_pid;
232 lfp->l_whence = bfp->l_whence;
233 switch (bfp->l_type) {
234 case F_RDLCK:
235 lfp->l_type = LINUX_F_RDLCK;
236 break;
237 case F_UNLCK:
238 lfp->l_type = LINUX_F_UNLCK;
239 break;
240 case F_WRLCK:
241 lfp->l_type = LINUX_F_WRLCK;
242 break;
243 }
244 }
245
246 static void
247 linux_to_bsd_flock(lfp, bfp)
248 struct linux_flock *lfp;
249 struct flock *bfp;
250 {
251 bfp->l_start = lfp->l_start;
252 bfp->l_len = lfp->l_len;
253 bfp->l_pid = lfp->l_pid;
254 bfp->l_whence = lfp->l_whence;
255 switch (lfp->l_type) {
256 case LINUX_F_RDLCK:
257 bfp->l_type = F_RDLCK;
258 break;
259 case LINUX_F_UNLCK:
260 bfp->l_type = F_UNLCK;
261 break;
262 case LINUX_F_WRLCK:
263 bfp->l_type = F_WRLCK;
264 break;
265 }
266 }
267
268 /*
269 * Most actions in the fcntl() call are straightforward; simply
270 * pass control to the NetBSD system call. A few commands need
271 * conversions after the actual system call has done its work,
272 * because the flag values and lock structure are different.
273 */
274 int
275 linux_fcntl(p, uap, retval)
276 struct proc *p;
277 struct linux_fcntl_args /* {
278 syscallarg(int) fd;
279 syscallarg(int) cmd;
280 syscallarg(void *) arg;
281 } */ *uap;
282 register_t *retval;
283 {
284 int fd, cmd, error, val;
285 caddr_t arg, sg;
286 struct linux_flock lfl;
287 struct flock *bfp, bfl;
288 struct fcntl_args fca;
289
290 fd = SCARG(uap, fd);
291 cmd = SCARG(uap, cmd);
292 arg = (caddr_t) SCARG(uap, arg);
293
294 switch (cmd) {
295 case LINUX_F_DUPFD:
296 cmd = F_DUPFD;
297 break;
298 case LINUX_F_GETFD:
299 cmd = F_GETFD;
300 break;
301 case LINUX_F_SETFD:
302 cmd = F_SETFD;
303 break;
304 case LINUX_F_GETFL:
305 SCARG(&fca, fd) = fd;
306 SCARG(&fca, cmd) = F_GETFL;
307 SCARG(&fca, arg) = arg;
308 if ((error = fcntl(p, &fca, retval)))
309 return error;
310 retval[0] = bsd_to_linux_ioflags(retval[0]);
311 return 0;
312 case LINUX_F_SETFL:
313 val = linux_to_bsd_ioflags((int)SCARG(uap, arg));
314 SCARG(&fca, fd) = fd;
315 SCARG(&fca, cmd) = F_SETFL;
316 SCARG(&fca, arg) = (caddr_t) val;
317 return fcntl(p, &fca, retval);
318 case LINUX_F_GETLK:
319 sg = stackgap_init(p->p_emul);
320 bfp = (struct flock *) stackgap_alloc(&sg, sizeof *bfp);
321 SCARG(&fca, fd) = fd;
322 SCARG(&fca, cmd) = F_GETLK;
323 SCARG(&fca, arg) = bfp;
324 if ((error = fcntl(p, &fca, retval)))
325 return error;
326 if ((error = copyin(bfp, &bfl, sizeof bfl)))
327 return error;
328 bsd_to_linux_flock(&bfl, &lfl);
329 return copyout(&lfl, arg, sizeof lfl);
330 break;
331 case LINUX_F_SETLK:
332 case LINUX_F_SETLKW:
333 cmd = (cmd == LINUX_F_SETLK ? F_SETLK : F_SETLKW);
334 if ((error = copyin(arg, &lfl, sizeof lfl)))
335 return error;
336 linux_to_bsd_flock(&lfl, &bfl);
337 sg = stackgap_init(p->p_emul);
338 bfp = (struct flock *) stackgap_alloc(&sg, sizeof *bfp);
339 if ((error = copyout(&bfl, bfp, sizeof bfl)))
340 return error;
341 SCARG(&fca, fd) = fd;
342 SCARG(&fca, cmd) = cmd;
343 SCARG(&fca, arg) = bfp;
344 return fcntl(p, &fca, retval);
345 break;
346 case LINUX_F_SETOWN:
347 cmd = F_SETOWN;
348 break;
349 case LINUX_F_GETOWN:
350 cmd = F_GETOWN;
351 break;
352 default:
353 return EOPNOTSUPP;
354 }
355
356 SCARG(&fca, fd) = fd;
357 SCARG(&fca, cmd) = cmd;
358 SCARG(&fca, arg) = arg;
359 return fcntl(p, &fca, retval);
360 }
361
362 /*
363 * Convert a NetBSD stat structure to a Linux stat structure.
364 * Only the order of the fields and the padding in the structure
365 * is different. linux_fakedev is a machine-dependent function
366 * which optionally converts device driver major/minor numbers
367 * (XXX horrible, but what can you do against code that compares
368 * things against constant major device numbers? sigh)
369 */
370 static void
371 bsd_to_linux_stat(bsp, lsp)
372 struct stat *bsp;
373 struct linux_stat *lsp;
374 {
375 lsp->lst_dev = bsp->st_dev;
376 lsp->lst_ino = bsp->st_ino;
377 lsp->lst_mode = bsp->st_mode;
378 lsp->lst_nlink = bsp->st_nlink;
379 lsp->lst_uid = bsp->st_uid;
380 lsp->lst_gid = bsp->st_gid;
381 lsp->lst_rdev = linux_fakedev(bsp->st_rdev);
382 lsp->lst_size = bsp->st_size;
383 lsp->lst_blksize = bsp->st_blksize;
384 lsp->lst_blocks = bsp->st_blocks;
385 lsp->lst_atime = bsp->st_atime;
386 lsp->lst_mtime = bsp->st_mtime;
387 lsp->lst_ctime = bsp->st_ctime;
388 }
389
390 /*
391 * The stat functions below are plain sailing. stat and lstat are handled
392 * by one function to avoid code duplication.
393 */
394 int
395 linux_fstat(p, uap, retval)
396 struct proc *p;
397 struct linux_fstat_args /* {
398 syscallarg(int) fd;
399 syscallarg(linux_stat *) sp;
400 } */ *uap;
401 register_t *retval;
402 {
403 struct fstat_args fsa;
404 struct linux_stat tmplst;
405 struct stat *st,tmpst;
406 caddr_t sg;
407 int error;
408
409 sg = stackgap_init(p->p_emul);
410
411 st = stackgap_alloc(&sg, sizeof (struct stat));
412
413 SCARG(&fsa, fd) = SCARG(uap, fd);
414 SCARG(&fsa, sb) = st;
415
416 if ((error = fstat(p, &fsa, retval)))
417 return error;
418
419 if ((error = copyin(st, &tmpst, sizeof tmpst)))
420 return error;
421
422 bsd_to_linux_stat(&tmpst, &tmplst);
423
424 if ((error = copyout(&tmplst, SCARG(uap, sp), sizeof tmplst)))
425 return error;
426
427 return 0;
428 }
429
430 static int
431 linux_stat1(p, uap, retval, dolstat)
432 struct proc *p;
433 struct linux_stat_args *uap;
434 register_t *retval;
435 int dolstat;
436 {
437 struct stat_args sa;
438 struct linux_stat tmplst;
439 struct stat *st, tmpst;
440 caddr_t sg;
441 int error;
442
443 sg = stackgap_init(p->p_emul);
444
445 LINUX_CHECK_ALT_EXIST(p, &sg, SCARG(uap, path));
446
447 st = stackgap_alloc(&sg, sizeof (struct stat));
448 SCARG(&sa, ub) = st;
449 SCARG(&sa, path) = SCARG(uap, path);
450
451 if ((error = (dolstat ? lstat(p, &sa, retval) : stat(p, &sa, retval))))
452 return error;
453
454 if ((error = copyin(st, &tmpst, sizeof tmpst)))
455 return error;
456
457 bsd_to_linux_stat(&tmpst, &tmplst);
458
459 if ((error = copyout(&tmplst, SCARG(uap, sp), sizeof tmplst)))
460 return error;
461
462 return 0;
463 }
464
465 int
466 linux_stat(p, uap, retval)
467 struct proc *p;
468 struct linux_stat_args /* {
469 syscallarg(char *) path;
470 syscallarg(struct linux_stat *) sp;
471 } */ *uap;
472 register_t *retval;
473 {
474 return linux_stat1(p, uap, retval, 0);
475 }
476
477 int
478 linux_lstat(p, uap, retval)
479 struct proc *p;
480 struct linux_lstat_args /* {
481 syscallarg(char *) path;
482 syscallarg(struct linux_stat *) sp;
483 } */ *uap;
484 register_t *retval;
485 {
486 return linux_stat1(p, uap, retval, 1);
487 }
488
489 /*
490 * The following syscalls are only here because of the alternate path check.
491 */
492 int
493 linux_access(p, uap, retval)
494 struct proc *p;
495 struct linux_access_args /* {
496 syscallarg(char *) path;
497 syscallarg(int) flags;
498 } */ *uap;
499 register_t *retval;
500 {
501 caddr_t sg = stackgap_init(p->p_emul);
502
503 LINUX_CHECK_ALT_EXIST(p, &sg, SCARG(uap, path));
504
505 return access(p, uap, retval);
506 }
507
508 int
509 linux_unlink(p, uap, retval)
510 struct proc *p;
511 struct linux_unlink_args /* {
512 syscallarg(char *) path;
513 } */ *uap;
514 register_t *retval;
515
516 {
517 caddr_t sg = stackgap_init(p->p_emul);
518
519 LINUX_CHECK_ALT_EXIST(p, &sg, SCARG(uap, path));
520
521 return unlink(p, uap, retval);
522 }
523
524 int linux_chdir(p, uap, retval)
525 struct proc *p;
526 struct linux_chdir_args /* {
527 syscallarg(char *) path;
528 } */ *uap;
529 register_t *retval;
530 {
531 caddr_t sg = stackgap_init(p->p_emul);
532
533 LINUX_CHECK_ALT_EXIST(p, &sg, SCARG(uap, path));
534
535 return chdir(p, uap, retval);
536 }
537
538 int
539 linux_mknod(p, uap, retval)
540 struct proc *p;
541 struct linux_mknod_args /* {
542 syscallarg(char *) path;
543 syscallarg(int) mode;
544 syscallarg(int) dev;
545 } */ *uap;
546 register_t *retval;
547 {
548 caddr_t sg = stackgap_init(p->p_emul);
549
550 LINUX_CHECK_ALT_CREAT(p, &sg, SCARG(uap, path));
551
552 return mknod(p, uap, retval);
553 }
554
555 int
556 linux_chmod(p, uap, retval)
557 struct proc *p;
558 struct linux_chmod_args /* {
559 syscallarg(char *) path;
560 syscallarg(int) mode;
561 } */ *uap;
562 register_t *retval;
563 {
564 caddr_t sg = stackgap_init(p->p_emul);
565
566 LINUX_CHECK_ALT_EXIST(p, &sg, SCARG(uap, path));
567
568 return chmod(p, uap, retval);
569 }
570
571 int
572 linux_chown(p, uap, retval)
573 struct proc *p;
574 struct linux_chown_args /* {
575 syscallarg(char *) path;
576 syscallarg(int) uid;
577 syscallarg(int) gid;
578 } */ *uap;
579 register_t *retval;
580 {
581 caddr_t sg = stackgap_init(p->p_emul);
582
583 LINUX_CHECK_ALT_EXIST(p, &sg, SCARG(uap, path));
584
585 return chmod(p, uap, retval);
586 }
587
588 int
589 linux_rename(p, uap, retval)
590 struct proc *p;
591 struct linux_rename_args /* {
592 syscallarg(char *) from;
593 syscallarg(char *) to;
594 } */ *uap;
595 register_t *retval;
596 {
597 caddr_t sg = stackgap_init(p->p_emul);
598
599 LINUX_CHECK_ALT_EXIST(p, &sg, SCARG(uap, from));
600 LINUX_CHECK_ALT_CREAT(p, &sg, SCARG(uap, to));
601
602 return rename(p, uap, retval);
603 }
604
605 int
606 linux_mkdir(p, uap, retval)
607 struct proc *p;
608 struct linux_mkdir_args /* {
609 syscallarg(char *) path;
610 syscallarg(int) mode;
611 } */ *uap;
612 register_t *retval;
613 {
614 caddr_t sg = stackgap_init(p->p_emul);
615
616 LINUX_CHECK_ALT_CREAT(p, &sg, SCARG(uap, path));
617 return mkdir(p, uap, retval);
618 }
619
620 int
621 linux_rmdir(p, uap, retval)
622 struct proc *p;
623 struct linux_rmdir_args /* {
624 syscallarg(char *) path;
625 } */ *uap;
626 register_t *retval;
627 {
628 caddr_t sg = stackgap_init(p->p_emul);
629
630 LINUX_CHECK_ALT_EXIST(p, &sg, SCARG(uap, path));
631 return rmdir(p, uap, retval);
632 }
633
634 int
635 linux_symlink(p, uap, retval)
636 struct proc *p;
637 struct linux_symlink_args /* {
638 syscallarg(char *) path;
639 syscallarg(char *) to;
640 } */ *uap;
641 register_t *retval;
642 {
643 caddr_t sg = stackgap_init(p->p_emul);
644
645 LINUX_CHECK_ALT_EXIST(p, &sg, SCARG(uap, path));
646 LINUX_CHECK_ALT_CREAT(p, &sg, SCARG(uap, to));
647
648 return symlink(p, uap, retval);
649 }
650
651 int
652 linux_readlink(p, uap, retval)
653 struct proc *p;
654 struct linux_readlink_args /* {
655 syscallarg(char *) name;
656 syscallarg(char *) buf;
657 syscallarg(int) count;
658 } */ *uap;
659 {
660 caddr_t sg = stackgap_init(p->p_emul);
661
662 LINUX_CHECK_ALT_EXIST(p, &sg, SCARG(uap, name));
663 return readlink(p, uap, retval);
664 }
665
666 int
667 linux_truncate(p, uap, retval)
668 struct proc *p;
669 struct linux_truncate_args /* {
670 syscallarg(char *) path;
671 syscallarg(long) length;
672 } */ *uap;
673 {
674 caddr_t sg = stackgap_init(p->p_emul);
675
676 LINUX_CHECK_ALT_EXIST(p, &sg, SCARG(uap, path));
677 return compat_43_truncate(p, uap, retval);
678 }
679