add linux_emul base, reorganize docs
[openbsd_emul.git] / linux_emul_base / file.c
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