linux_llseek.c revision 1.8 1 /* $NetBSD: linux_llseek.c,v 1.8 1995/08/14 01:27:50 mycroft 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.
366 */
367 static void
368 bsd_to_linux_stat(bsp, lsp)
369 struct stat *bsp;
370 struct linux_stat *lsp;
371 {
372 lsp->lst_dev = bsp->st_dev;
373 lsp->lst_ino = bsp->st_ino;
374 lsp->lst_mode = bsp->st_mode;
375 lsp->lst_nlink = bsp->st_nlink;
376 lsp->lst_uid = bsp->st_uid;
377 lsp->lst_gid = bsp->st_gid;
378 lsp->lst_rdev = bsp->st_rdev;
379 lsp->lst_size = bsp->st_size;
380 lsp->lst_blksize = bsp->st_blksize;
381 lsp->lst_blocks = bsp->st_blocks;
382 lsp->lst_atime = bsp->st_atime;
383 lsp->lst_mtime = bsp->st_mtime;
384 lsp->lst_ctime = bsp->st_ctime;
385 }
386
387 /*
388 * The stat functions below are plain sailing. stat and lstat are handled
389 * by one function to avoid code duplication.
390 */
391 int
392 linux_fstat(p, uap, retval)
393 struct proc *p;
394 struct linux_fstat_args /* {
395 syscallarg(int) fd;
396 syscallarg(linux_stat *) sp;
397 } */ *uap;
398 register_t *retval;
399 {
400 struct fstat_args fsa;
401 struct linux_stat tmplst;
402 struct stat *st,tmpst;
403 caddr_t sg;
404 int error;
405
406 sg = stackgap_init(p->p_emul);
407
408 st = stackgap_alloc(&sg, sizeof (struct stat));
409
410 SCARG(&fsa, fd) = SCARG(uap, fd);
411 SCARG(&fsa, sb) = st;
412
413 if ((error = fstat(p, &fsa, retval)))
414 return error;
415
416 if ((error = copyin(st, &tmpst, sizeof tmpst)))
417 return error;
418
419 bsd_to_linux_stat(&tmpst, &tmplst);
420
421 if ((error = copyout(&tmplst, SCARG(uap, sp), sizeof tmplst)))
422 return error;
423
424 return 0;
425 }
426
427 static int
428 linux_stat1(p, uap, retval, dolstat)
429 struct proc *p;
430 struct linux_stat_args *uap;
431 register_t *retval;
432 int dolstat;
433 {
434 struct stat_args sa;
435 struct linux_stat tmplst;
436 struct stat *st, tmpst;
437 caddr_t sg;
438 int error;
439
440 sg = stackgap_init(p->p_emul);
441
442 LINUX_CHECK_ALT_EXIST(p, &sg, SCARG(uap, path));
443
444 st = stackgap_alloc(&sg, sizeof (struct stat));
445 SCARG(&sa, ub) = st;
446 SCARG(&sa, path) = SCARG(uap, path);
447
448 if ((error = (dolstat ? lstat(p, &sa, retval) : stat(p, &sa, retval))))
449 return error;
450
451 if ((error = copyin(st, &tmpst, sizeof tmpst)))
452 return error;
453
454 bsd_to_linux_stat(&tmpst, &tmplst);
455
456 if ((error = copyout(&tmplst, SCARG(uap, sp), sizeof tmplst)))
457 return error;
458
459 return 0;
460 }
461
462 int
463 linux_stat(p, uap, retval)
464 struct proc *p;
465 struct linux_stat_args /* {
466 syscallarg(char *) path;
467 syscallarg(struct linux_stat *) sp;
468 } */ *uap;
469 register_t *retval;
470 {
471 return linux_stat1(p, uap, retval, 0);
472 }
473
474 int
475 linux_lstat(p, uap, retval)
476 struct proc *p;
477 struct linux_lstat_args /* {
478 syscallarg(char *) path;
479 syscallarg(struct linux_stat *) sp;
480 } */ *uap;
481 register_t *retval;
482 {
483 return linux_stat1(p, uap, retval, 1);
484 }
485
486 /*
487 * The following syscalls are only here because of the alternate path check.
488 */
489 int
490 linux_access(p, uap, retval)
491 struct proc *p;
492 struct linux_access_args /* {
493 syscallarg(char *) path;
494 syscallarg(int) flags;
495 } */ *uap;
496 register_t *retval;
497 {
498 caddr_t sg = stackgap_init(p->p_emul);
499
500 LINUX_CHECK_ALT_EXIST(p, &sg, SCARG(uap, path));
501
502 return access(p, uap, retval);
503 }
504
505 int
506 linux_unlink(p, uap, retval)
507 struct proc *p;
508 struct linux_unlink_args /* {
509 syscallarg(char *) path;
510 } */ *uap;
511 register_t *retval;
512
513 {
514 caddr_t sg = stackgap_init(p->p_emul);
515
516 LINUX_CHECK_ALT_EXIST(p, &sg, SCARG(uap, path));
517
518 return unlink(p, uap, retval);
519 }
520
521 int linux_chdir(p, uap, retval)
522 struct proc *p;
523 struct linux_chdir_args /* {
524 syscallarg(char *) path;
525 } */ *uap;
526 register_t *retval;
527 {
528 caddr_t sg = stackgap_init(p->p_emul);
529
530 LINUX_CHECK_ALT_EXIST(p, &sg, SCARG(uap, path));
531
532 return chdir(p, uap, retval);
533 }
534
535 int
536 linux_mknod(p, uap, retval)
537 struct proc *p;
538 struct linux_mknod_args /* {
539 syscallarg(char *) path;
540 syscallarg(int) mode;
541 syscallarg(int) dev;
542 } */ *uap;
543 register_t *retval;
544 {
545 caddr_t sg = stackgap_init(p->p_emul);
546
547 LINUX_CHECK_ALT_CREAT(p, &sg, SCARG(uap, path));
548
549 return mknod(p, uap, retval);
550 }
551
552 int
553 linux_chmod(p, uap, retval)
554 struct proc *p;
555 struct linux_chmod_args /* {
556 syscallarg(char *) path;
557 syscallarg(int) mode;
558 } */ *uap;
559 register_t *retval;
560 {
561 caddr_t sg = stackgap_init(p->p_emul);
562
563 LINUX_CHECK_ALT_EXIST(p, &sg, SCARG(uap, path));
564
565 return chmod(p, uap, retval);
566 }
567
568 int
569 linux_chown(p, uap, retval)
570 struct proc *p;
571 struct linux_chown_args /* {
572 syscallarg(char *) path;
573 syscallarg(int) uid;
574 syscallarg(int) gid;
575 } */ *uap;
576 register_t *retval;
577 {
578 caddr_t sg = stackgap_init(p->p_emul);
579
580 LINUX_CHECK_ALT_EXIST(p, &sg, SCARG(uap, path));
581
582 return chmod(p, uap, retval);
583 }
584
585 int
586 linux_rename(p, uap, retval)
587 struct proc *p;
588 struct linux_rename_args /* {
589 syscallarg(char *) from;
590 syscallarg(char *) to;
591 } */ *uap;
592 register_t *retval;
593 {
594 caddr_t sg = stackgap_init(p->p_emul);
595
596 LINUX_CHECK_ALT_EXIST(p, &sg, SCARG(uap, from));
597 LINUX_CHECK_ALT_CREAT(p, &sg, SCARG(uap, to));
598
599 return rename(p, uap, retval);
600 }
601
602 int
603 linux_mkdir(p, uap, retval)
604 struct proc *p;
605 struct linux_mkdir_args /* {
606 syscallarg(char *) path;
607 syscallarg(int) mode;
608 } */ *uap;
609 register_t *retval;
610 {
611 caddr_t sg = stackgap_init(p->p_emul);
612
613 LINUX_CHECK_ALT_CREAT(p, &sg, SCARG(uap, path));
614 return mkdir(p, uap, retval);
615 }
616
617 int
618 linux_rmdir(p, uap, retval)
619 struct proc *p;
620 struct linux_rmdir_args /* {
621 syscallarg(char *) path;
622 } */ *uap;
623 register_t *retval;
624 {
625 caddr_t sg = stackgap_init(p->p_emul);
626
627 LINUX_CHECK_ALT_EXIST(p, &sg, SCARG(uap, path));
628 return rmdir(p, uap, retval);
629 }
630
631 int
632 linux_symlink(p, uap, retval)
633 struct proc *p;
634 struct linux_symlink_args /* {
635 syscallarg(char *) path;
636 syscallarg(char *) to;
637 } */ *uap;
638 register_t *retval;
639 {
640 caddr_t sg = stackgap_init(p->p_emul);
641
642 LINUX_CHECK_ALT_EXIST(p, &sg, SCARG(uap, path));
643 LINUX_CHECK_ALT_CREAT(p, &sg, SCARG(uap, to));
644
645 return symlink(p, uap, retval);
646 }
647
648 int
649 linux_readlink(p, uap, retval)
650 struct proc *p;
651 struct linux_readlink_args /* {
652 syscallarg(char *) name;
653 syscallarg(char *) buf;
654 syscallarg(int) count;
655 } */ *uap;
656 {
657 caddr_t sg = stackgap_init(p->p_emul);
658
659 LINUX_CHECK_ALT_EXIST(p, &sg, SCARG(uap, name));
660 return readlink(p, uap, retval);
661 }
662
663 int
664 linux_truncate(p, uap, retval)
665 struct proc *p;
666 struct linux_truncate_args /* {
667 syscallarg(char *) path;
668 syscallarg(long) length;
669 } */ *uap;
670 {
671 caddr_t sg = stackgap_init(p->p_emul);
672
673 LINUX_CHECK_ALT_EXIST(p, &sg, SCARG(uap, path));
674 return compat_43_truncate(p, uap, retval);
675 }
676