cae36a52 |
1 | #include <u.h> |
2 | #include <libc.h> |
3 | #include <ureg.h> |
4 | #include "dat.h" |
5 | #include "fns.h" |
6 | #include "linux.h" |
7 | |
8 | typedef struct Fd Fd; |
9 | typedef struct Fdtab Fdtab; |
10 | |
11 | struct Fd |
12 | { |
13 | int flags; |
14 | Ufile *file; |
15 | }; |
16 | |
17 | struct Fdtab |
18 | { |
19 | Ref; |
20 | QLock; |
21 | int lastfd; |
22 | int nfd; |
23 | Fd *fd; |
24 | }; |
25 | |
26 | Ufile* |
27 | getfile(Ufile *file) |
28 | { |
29 | if(file) |
30 | incref(file); |
31 | return file; |
32 | } |
33 | |
34 | void |
35 | putfile(Ufile *file) |
36 | { |
37 | Udirent *d; |
38 | |
39 | if(file == nil) |
40 | return; |
41 | if(decref(file)) |
42 | return; |
43 | trace("putfile(): closing %p %s", file, file->path); |
44 | if(devtab[file->dev]->close) |
45 | devtab[file->dev]->close(file); |
46 | free(file->path); |
47 | while(d = file->rdaux){ |
48 | file->rdaux = d->next; |
49 | free(d); |
50 | } |
51 | free(file); |
52 | } |
53 | |
54 | static Fdtab* |
55 | newfdtab(void) |
56 | { |
57 | Fdtab *tab; |
58 | |
59 | tab = kmallocz(sizeof(*tab), 1); |
60 | tab->ref = 1; |
61 | tab->lastfd = -1; |
62 | tab->nfd = 0; |
63 | tab->fd = nil; |
64 | |
65 | return tab; |
66 | } |
67 | |
68 | enum { |
69 | CHUNK = 64, |
70 | }; |
71 | |
72 | /* assumes tab->lock aquired */ |
73 | static int |
74 | grow1(Fdtab *tab) |
75 | { |
76 | if(tab->nfd >= MAXFD) |
77 | return -EMFILE; |
78 | if((tab->nfd % CHUNK) == 0) |
79 | tab->fd = krealloc(tab->fd, sizeof(tab->fd[0]) * (tab->nfd + CHUNK)); |
80 | memset(&tab->fd[tab->nfd], 0, sizeof(tab->fd[0])); |
81 | return tab->nfd++; |
82 | } |
83 | |
84 | Ufile *procfdgetfile(Uproc *proc, int fd) |
85 | { |
86 | Fdtab *tab; |
87 | Ufile *file; |
88 | |
89 | file = nil; |
90 | if(tab = proc->fdtab){ |
91 | qlock(tab); |
92 | if(fd >= 0 && fd < tab->nfd) |
93 | file = getfile(tab->fd[fd].file); |
94 | qunlock(tab); |
95 | } |
96 | return file; |
97 | } |
98 | |
99 | Ufile* |
100 | fdgetfile(int fd) |
101 | { |
102 | return procfdgetfile(current, fd); |
103 | } |
104 | |
105 | int |
106 | newfd(Ufile *file, int flags) |
107 | { |
108 | int fd; |
109 | Fdtab *tab; |
110 | |
111 | tab = current->fdtab; |
112 | qlock(tab); |
113 | fd = tab->lastfd; |
114 | if((fd >= 0) && (fd < tab->nfd) && (tab->fd[fd].file == nil)) |
115 | goto found; |
116 | for(fd=0; fd<tab->nfd; fd++) |
117 | if(tab->fd[fd].file == nil) |
118 | goto found; |
119 | fd = grow1(tab); |
120 | found: |
121 | if(fd >= 0){ |
122 | tab->fd[fd].file = file; |
123 | tab->fd[fd].flags = flags; |
124 | file = nil; |
125 | } |
126 | qunlock(tab); |
127 | putfile(file); |
128 | |
129 | return fd; |
130 | } |
131 | |
132 | static Fdtab* |
133 | getfdtab(Fdtab *tab, int copy) |
134 | { |
135 | Fdtab *new; |
136 | int i; |
137 | |
138 | if(!copy){ |
139 | incref(tab); |
140 | return tab; |
141 | } |
142 | qlock(tab); |
143 | new = newfdtab(); |
144 | new->lastfd = tab->lastfd; |
145 | new->nfd = tab->nfd; |
146 | new->fd = kmallocz(sizeof(new->fd[0]) * (((tab->nfd+CHUNK-1)/CHUNK)*CHUNK), 1); |
147 | for(i=0; i<new->nfd; i++){ |
148 | Ufile *file; |
149 | |
150 | if((file = tab->fd[i].file) == nil) |
151 | continue; |
152 | incref(file); |
153 | new->fd[i].file = file; |
154 | new->fd[i].flags = tab->fd[i].flags; |
155 | } |
156 | qunlock(tab); |
157 | return new; |
158 | } |
159 | |
160 | static void |
161 | putfdtab(Fdtab *tab) |
162 | { |
163 | int i; |
164 | |
165 | if(decref(tab)) |
166 | return; |
167 | for(i=0; i<tab->nfd; i++){ |
168 | Ufile *file; |
169 | if((file = tab->fd[i].file) == nil) |
170 | continue; |
171 | tab->fd[i].file = nil; |
172 | putfile(file); |
173 | } |
174 | free(tab->fd); |
175 | free(tab); |
176 | } |
177 | |
178 | int sys_dup2(int old, int new) |
179 | { |
180 | Ufile *file; |
181 | Fdtab *tab; |
182 | int err; |
183 | |
184 | trace("sys_dup2(%d, %d)", old, new); |
185 | |
186 | tab = current->fdtab; |
187 | |
188 | if((file = fdgetfile(old)) == nil) |
189 | return -EBADF; |
190 | if(new < 0) |
191 | return newfd(file, 0); |
192 | if(new >= MAXFD) |
193 | return -EBADF; |
194 | qlock(tab); |
195 | while(new >= tab->nfd){ |
196 | err = grow1(tab); |
197 | if(err < 0){ |
198 | qunlock(tab); |
199 | putfile(file); |
200 | return err; |
201 | } |
202 | } |
203 | if(tab->fd[new].file != nil) |
204 | putfile(tab->fd[new].file); |
205 | tab->fd[new].file = file; |
206 | tab->fd[new].flags &= ~FD_CLOEXEC; |
207 | qunlock(tab); |
208 | |
209 | return new; |
210 | } |
211 | |
212 | int sys_dup(int fd) |
213 | { |
214 | return sys_dup2(fd, -1); |
215 | } |
216 | |
217 | struct linux_flock |
218 | { |
219 | short l_type; |
220 | short l_whence; |
221 | ulong l_start; |
222 | ulong l_len; |
223 | int l_pid; |
224 | }; |
225 | |
226 | struct linux_flock64 |
227 | { |
228 | short l_type; |
229 | short l_whence; |
230 | uvlong l_start; |
231 | uvlong l_len; |
232 | int l_pid; |
233 | }; |
234 | |
235 | enum { |
236 | F_RDLCK, |
237 | F_WRLCK, |
238 | F_UNLCK, |
239 | }; |
240 | |
241 | int sys_fcntl(int fd, int cmd, int arg) |
242 | { |
243 | int ret; |
244 | Ufile *file; |
245 | Fdtab *tab; |
246 | |
247 | trace("sys_fcntl(%d, %lux, %lux)", fd, (ulong)cmd, (ulong)arg); |
248 | |
249 | tab = current->fdtab; |
250 | |
251 | ret = -EBADF; |
252 | if((file = fdgetfile(fd)) == nil) |
253 | goto out; |
254 | ret = -EINVAL; |
255 | switch(cmd){ |
256 | default: |
257 | trace("sys_fcntl() cmd %lux not implemented", (ulong)cmd); |
258 | break; |
259 | |
260 | case F_DUPFD: |
261 | if(arg < 0 || arg >= MAXFD) |
262 | break; |
263 | qlock(tab); |
264 | for(ret=arg; ret<tab->nfd; ret++) |
265 | if(tab->fd[ret].file == nil) |
266 | goto found; |
267 | do { |
268 | if((ret = grow1(tab)) < 0) |
269 | break; |
270 | } while(ret < arg); |
271 | found: |
272 | if(ret >= 0){ |
273 | tab->fd[ret].file = file; |
274 | tab->fd[ret].flags = tab->fd[fd].flags & ~FD_CLOEXEC; |
275 | file = nil; |
276 | } |
277 | qunlock(tab); |
278 | break; |
279 | |
280 | case F_GETFD: |
281 | case F_SETFD: |
282 | qlock(tab); |
283 | if(cmd == F_GETFD){ |
284 | ret = tab->fd[fd].flags & FD_CLOEXEC; |
285 | } else { |
286 | tab->fd[fd].flags = (arg & FD_CLOEXEC); |
287 | ret = 0; |
288 | } |
289 | qunlock(tab); |
290 | break; |
291 | |
292 | case F_GETFL: |
293 | ret = file->mode; |
294 | break; |
295 | case F_SETFL: |
296 | trace("sys_fcntl() changing mode from %o to %o", file->mode, arg); |
297 | file->mode = arg; |
298 | ret = 0; |
299 | break; |
300 | |
301 | case F_GETLK: |
302 | ((struct linux_flock*)arg)->l_type = F_UNLCK; |
303 | case F_SETLK: |
304 | case F_SETLKW: |
305 | ret = 0; |
306 | break; |
307 | |
308 | case F_GETLK64: |
309 | ((struct linux_flock64*)arg)->l_type = F_UNLCK; |
310 | case F_SETLK64: |
311 | ret = 0; |
312 | break; |
313 | } |
314 | out: |
315 | putfile(file); |
316 | return ret; |
317 | } |
318 | |
319 | int sys_close(int fd) |
320 | { |
321 | Fdtab *tab; |
322 | Ufile *file; |
323 | |
324 | trace("sys_close(%d)", fd); |
325 | |
326 | tab = current->fdtab; |
327 | qlock(tab); |
328 | if(fd >= 0 && fd < tab->nfd){ |
329 | if(file = tab->fd[fd].file){ |
330 | tab->fd[fd].file = nil; |
331 | tab->lastfd = fd; |
332 | qunlock(tab); |
333 | |
334 | putfile(file); |
335 | return 0; |
336 | } |
337 | } |
338 | qunlock(tab); |
339 | return -EBADF; |
340 | } |
341 | |
342 | int sys_ioctl(int fd, int cmd, void *arg) |
343 | { |
344 | Ufile *file; |
345 | int ret; |
346 | |
347 | trace("sys_ioctl(%d, %lux, %p)", fd, (ulong)cmd, arg); |
348 | |
349 | if((file = fdgetfile(fd)) == nil) |
350 | return -EBADF; |
351 | ret = -ENOTTY; |
352 | if(devtab[file->dev]->ioctl) |
353 | ret = devtab[file->dev]->ioctl(file, cmd, arg); |
354 | putfile(file); |
355 | return ret; |
356 | } |
357 | |
358 | int preadfile(Ufile *file, void *buf, int len, vlong off) |
359 | { |
360 | if(file->mode & O_NONBLOCK){ |
361 | if(devtab[file->dev]->poll != nil){ |
362 | if((devtab[file->dev]->poll(file, nil) & POLLIN) == 0){ |
363 | trace("readfile(): nonblocking read blocked"); |
364 | |
365 | return -EAGAIN; |
366 | } |
367 | } |
368 | } |
369 | if(devtab[file->dev]->read == nil) |
370 | return 0; |
371 | return devtab[file->dev]->read(file, buf, len, off); |
372 | } |
373 | |
374 | int readfile(Ufile *file, void *buf, int len) |
375 | { |
376 | int err; |
377 | |
378 | if((err = preadfile(file, buf, len, file->off)) > 0) |
379 | file->off += err; |
380 | return err; |
381 | } |
382 | |
383 | int pwritefile(Ufile *file, void *buf, int len, vlong off) |
384 | { |
385 | if(devtab[file->dev]->write == nil) |
386 | return 0; |
387 | if(file->mode & O_APPEND){ |
388 | if(devtab[file->dev]->size){ |
389 | off = devtab[file->dev]->size(file); |
390 | if(off < 0) |
391 | return (int)off; |
392 | } |
393 | } |
394 | return devtab[file->dev]->write(file, buf, len, off); |
395 | } |
396 | |
397 | int writefile(Ufile *file, void *buf, int len) |
398 | { |
399 | int err; |
400 | vlong end; |
401 | |
402 | if(devtab[file->dev]->write == nil) |
403 | return 0; |
404 | if(file->mode & O_APPEND){ |
405 | if(devtab[file->dev]->size){ |
406 | end = devtab[file->dev]->size(file); |
407 | if(end < 0) |
408 | return (int)end; |
409 | file->off = end; |
410 | } |
411 | } |
412 | if(len == 0) |
413 | return 0; |
414 | if((err = devtab[file->dev]->write(file, buf, len, file->off)) > 0) |
415 | file->off += err; |
416 | return err; |
417 | } |
418 | |
419 | int sys_read(int fd, void *buf, int len) |
420 | { |
421 | int ret; |
422 | Ufile *file; |
423 | |
424 | trace("sys_read(%d, %p, %x)", fd, buf, len); |
425 | if((file = fdgetfile(fd)) == nil) |
426 | return -EBADF; |
427 | ret = readfile(file, buf, len); |
428 | putfile(file); |
429 | return ret; |
430 | } |
431 | |
432 | int sys_write(int fd, void *buf, int len) |
433 | { |
434 | Ufile *file; |
435 | int ret; |
436 | |
437 | trace("sys_write(%d, %p, %x)", fd, buf, len); |
438 | if((file = fdgetfile(fd)) == nil) |
439 | return -EBADF; |
440 | ret = writefile(file, buf, len); |
441 | putfile(file); |
442 | |
443 | return ret; |
444 | } |
445 | |
446 | int sys_pread64(int fd, void *buf, int len, ulong off) |
447 | { |
448 | Ufile *file; |
449 | int ret; |
450 | |
451 | trace("sys_pread(%d, %p, %x, %lux)", fd, buf, len, off); |
452 | if((file = fdgetfile(fd)) == nil) |
453 | return -EBADF; |
454 | ret = preadfile(file, buf, len, off); |
455 | putfile(file); |
456 | return ret; |
457 | } |
458 | |
459 | int sys_pwrite64(int fd, void *buf, int len, ulong off) |
460 | { |
461 | Ufile *file; |
462 | int ret; |
463 | |
464 | trace("sys_pwrite(%d, %p, %x, %lux)", fd, buf, len, off); |
465 | if((file = fdgetfile(fd)) == nil) |
466 | return -EBADF; |
467 | ret = pwritefile(file, buf, len, off); |
468 | putfile(file); |
469 | return ret; |
470 | } |
471 | |
472 | struct linux_iovec |
473 | { |
474 | void *base; |
475 | ulong len; |
476 | }; |
477 | |
478 | int sys_writev(int fd, void *vec, int n) |
479 | { |
480 | struct linux_iovec *v = vec; |
481 | int ret, i, w; |
482 | Ufile *file; |
483 | |
484 | trace("sys_writev(%d, %p, %d)", fd, vec, n); |
485 | |
486 | if((file = fdgetfile(fd)) == nil) |
487 | return -EBADF; |
488 | ret = 0; |
489 | for(i=0; i<n; i++){ |
490 | w = writefile(file, v[i].base, v[i].len); |
491 | if(w < 0){ |
492 | if(ret == 0) |
493 | ret = w; |
494 | break; |
495 | } |
496 | ret += w; |
497 | if(w < v[i].len) |
498 | break; |
499 | } |
500 | putfile(file); |
501 | |
502 | return ret; |
503 | } |
504 | |
505 | int sys_readv(int fd, void *vec, int n) |
506 | { |
507 | struct linux_iovec *v = vec; |
508 | int ret, i, r; |
509 | Ufile *file; |
510 | |
511 | trace("sys_readv(%d, %p, %d)", fd, vec, n); |
512 | |
513 | if((file = fdgetfile(fd)) == nil) |
514 | return -EBADF; |
515 | ret = 0; |
516 | for(i=0; i<n; i++){ |
517 | r = readfile(file, v[i].base, v[i].len); |
518 | if(r < 0){ |
519 | if(ret == 0) |
520 | ret = r; |
521 | break; |
522 | } |
523 | ret += r; |
524 | if(r < v[i].len) |
525 | break; |
526 | } |
527 | putfile(file); |
528 | |
529 | return ret; |
530 | } |
531 | |
532 | int seekfile(Ufile *file, vlong off, int whence) |
533 | { |
534 | vlong end; |
535 | |
536 | if(devtab[file->dev]->size == nil) |
537 | return -ESPIPE; |
538 | |
539 | switch(whence){ |
540 | case 0: |
541 | file->off = off; |
542 | return 0; |
543 | case 1: |
544 | file->off += off; |
545 | return 0; |
546 | case 2: |
547 | end = devtab[file->dev]->size(file); |
548 | if(end < 0) |
549 | return end; |
550 | file->off = end + off; |
551 | return 0; |
552 | } |
553 | |
554 | return -EINVAL; |
555 | } |
556 | |
557 | ulong sys_lseek(int fd, ulong off, int whence) |
558 | { |
559 | Ufile *file; |
560 | int ret; |
561 | |
562 | trace("sys_lseek(%d, %lux, %d)", fd, off, whence); |
563 | |
564 | if((file = fdgetfile(fd)) == nil) |
565 | return (ulong)-EBADF; |
566 | ret = seekfile(file, off, whence); |
567 | if(ret == 0) |
568 | ret = file->off; |
569 | putfile(file); |
570 | |
571 | return ret; |
572 | } |
573 | |
574 | int sys_llseek(int fd, ulong hioff, ulong looff, vlong *res, int whence) |
575 | { |
576 | Ufile *file; |
577 | int ret; |
578 | |
579 | trace("sys_llseek(%d, %lux, %lux, %p, %d)", fd, hioff, looff, res, whence); |
580 | |
581 | if((file = fdgetfile(fd)) == nil) |
582 | return -EBADF; |
583 | ret = seekfile(file, ((vlong)hioff<<32) | ((vlong)looff), whence); |
584 | if((ret == 0) && res) |
585 | *res = file->off; |
586 | putfile(file); |
587 | |
588 | return ret; |
589 | } |
590 | |
591 | int sys_umask(int umask) |
592 | { |
593 | int old; |
594 | |
595 | trace("sys_umask(%#o)", umask); |
596 | |
597 | old = current->umask; |
598 | current->umask = (umask & 0777); |
599 | return old; |
600 | } |
601 | |
602 | int |
603 | chdirfile(Ufile *f) |
604 | { |
605 | Ustat s; |
606 | int err; |
607 | |
608 | trace("chdirfile(%s)", f->path); |
609 | |
610 | err = -ENOTDIR; |
611 | if(f->path == nil) |
612 | return err; |
613 | if(devtab[f->dev]->fstat == nil) |
614 | return err; |
615 | if((err = devtab[f->dev]->fstat(f, &s)) < 0) |
616 | return err; |
617 | err = -ENOTDIR; |
618 | if((s.mode & ~0777) != S_IFDIR) |
619 | return err; |
620 | free(current->cwd); |
621 | current->cwd = kstrdup(fsrootpath(f->path)); |
622 | if(f->dev == ROOTDEV && chdir(f->path) == 0){ |
623 | free(current->kcwd); |
624 | current->kcwd = kstrdup(f->path); |
625 | } |
626 | return 0; |
627 | } |
628 | |
629 | int |
630 | sys_fchdir(int fd) |
631 | { |
632 | Ufile *f; |
633 | int err; |
634 | |
635 | trace("sys_fchdir(%d)", fd); |
636 | |
637 | if((f = fdgetfile(fd)) == nil) |
638 | return -EBADF; |
639 | err = chdirfile(f); |
640 | putfile(f); |
641 | return err; |
642 | } |
643 | |
644 | int |
645 | sys_fchown(int fd, int uid, int gid) |
646 | { |
647 | int err; |
648 | Ufile *f; |
649 | |
650 | trace("sys_fchown(%d, %d, %d)", fd, uid, gid); |
651 | |
652 | if((f = fdgetfile(fd)) == nil) |
653 | return -EBADF; |
654 | err = -EPERM; |
655 | if(devtab[f->dev]->fchown) |
656 | err = devtab[f->dev]->fchown(f, uid, gid); |
657 | putfile(f); |
658 | |
659 | return err; |
660 | } |
661 | |
662 | int |
663 | sys_fchmod(int fd, int mode) |
664 | { |
665 | int err; |
666 | Ufile *f; |
667 | |
668 | trace("sys_fchmod(%d, %#o)", fd, mode); |
669 | |
670 | if((f = fdgetfile(fd)) == nil) |
671 | return -EBADF; |
672 | err = -EPERM; |
673 | if(devtab[f->dev]->fchmod) |
674 | err = devtab[f->dev]->fchmod(f, mode); |
675 | putfile(f); |
676 | |
677 | return err; |
678 | } |
679 | |
680 | int |
681 | sys_ftruncate(int fd, ulong size) |
682 | { |
683 | int err; |
684 | Ufile *f; |
685 | |
686 | trace("sys_ftruncate(%d, %lux)", fd, size); |
687 | |
688 | if((f = fdgetfile(fd)) == nil) |
689 | return -EBADF; |
690 | err = -EPERM; |
691 | if(devtab[f->dev]->ftruncate) |
692 | err = devtab[f->dev]->ftruncate(f, (uvlong)size); |
693 | putfile(f); |
694 | |
695 | return err; |
696 | } |
697 | |
698 | void initfile(void) |
699 | { |
700 | current->fdtab = newfdtab(); |
701 | current->umask = 022; |
702 | } |
703 | |
704 | void exitfile(Uproc *proc) |
705 | { |
706 | Fdtab *tab; |
707 | |
708 | if(tab = proc->fdtab){ |
709 | proc->fdtab = nil; |
710 | putfdtab(tab); |
711 | } |
712 | } |
713 | |
714 | void clonefile(Uproc *new, int copy) |
715 | { |
716 | Fdtab *tab; |
717 | |
718 | if((tab = current->fdtab) == nil){ |
719 | new->fdtab = nil; |
720 | return; |
721 | } |
722 | new->fdtab = getfdtab(tab, copy); |
723 | } |
724 | |
725 | void closexfds(void) |
726 | { |
727 | Fdtab *tab; |
728 | int i; |
729 | |
730 | if((tab = current->fdtab) == nil) |
731 | return; |
732 | qlock(tab); |
733 | for(i=0; i<tab->nfd; i++){ |
734 | Ufile *f; |
735 | |
736 | if((f = tab->fd[i].file) == nil) |
737 | continue; |
738 | if((tab->fd[i].flags & FD_CLOEXEC) == 0) |
739 | continue; |
740 | |
741 | tab->fd[i].file = nil; |
742 | tab->fd[i].flags = 0; |
743 | |
744 | putfile(f); |
745 | } |
746 | qunlock(tab); |
747 | } |
748 | |
749 | int sys_flock(int fd, int cmd) |
750 | { |
751 | trace("sys_flock(%d, %d)", fd, cmd); |
752 | return 0; |
753 | } |
754 | |
755 | int sys_fsync(int fd) |
756 | { |
757 | trace("sys_fsync(%d)", fd); |
758 | return 0; |
759 | } |
760 | |