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 Rfile Rfile; |
9 | typedef struct Rpath Rpath; |
10 | |
11 | struct Rfile |
12 | { |
13 | Ufile; |
14 | struct |
15 | { |
16 | Dir *d; |
17 | int i; |
18 | int n; |
19 | } diraux; |
20 | }; |
21 | |
22 | struct Rpath |
23 | { |
24 | Ref; |
25 | |
26 | Rpath *hash; |
27 | |
28 | int deleted; |
29 | char str[]; |
30 | }; |
31 | |
32 | static Rpath *rpathtab[64]; |
33 | static QLock rpathtablk; |
34 | |
35 | static Rpath ** |
36 | rpathent(char *path) |
37 | { |
38 | Rpath **prp; |
39 | |
40 | prp = &rpathtab[hashpath(path) % nelem(rpathtab)]; |
41 | while(*prp){ |
42 | if(strcmp(path, (*prp)->str) == 0) |
43 | break; |
44 | prp = &((*prp)->hash); |
45 | } |
46 | return prp; |
47 | } |
48 | |
49 | static char* |
50 | linkname(char *name) |
51 | { |
52 | if(strncmp(name, ".udir.L.", 8) == 0) |
53 | name += 8; |
54 | return name; |
55 | } |
56 | |
57 | static char* |
58 | udirpath(char *base, char *name, char type) |
59 | { |
60 | char buf[9]; |
61 | |
62 | strcpy(buf, ".udir.T."); |
63 | buf[6] = type; |
64 | return allocpath(base, buf, name); |
65 | } |
66 | |
67 | static int |
68 | udirget(char *base, char *name, char type, char **val) |
69 | { |
70 | char *f, *b; |
71 | int n, r, s; |
72 | int fd; |
73 | |
74 | r = -1; |
75 | f = udirpath(base, name, type); |
76 | if((fd = open(shortpath(current->kcwd, f), OREAD)) < 0) |
77 | goto out; |
78 | if(val == nil){ |
79 | r = 0; |
80 | goto out; |
81 | } |
82 | if((s = seek(fd, 0, 2)) < 0) |
83 | goto out; |
84 | b = kmalloc(s+1); |
85 | n = 0; |
86 | if(s > 0){ |
87 | seek(fd, 0, 0); |
88 | if((n = read(fd, b, s)) < 0){ |
89 | free(b); |
90 | goto out; |
91 | } |
92 | } |
93 | b[n] = 0; |
94 | |
95 | r = 0; |
96 | *val = b; |
97 | out: |
98 | free(f); |
99 | close(fd); |
100 | return r; |
101 | } |
102 | |
103 | static char* |
104 | resolvepath1(char *path, int link) |
105 | { |
106 | char *r, *b, *p, *o, *e; |
107 | char **a; |
108 | |
109 | int n; |
110 | int i; |
111 | |
112 | r = nil; |
113 | a = nil; |
114 | n = 0; |
115 | |
116 | b = kstrdup(path); |
117 | for(p=b; *p; p++){ |
118 | if(*p == '/'){ |
119 | if((n % 16) == 0) |
120 | a = krealloc(a, sizeof(a[0]) * (n+16)); |
121 | a[n++] = p; |
122 | } |
123 | } |
124 | |
125 | e = nil; |
126 | for(i=n-1; i>=0; i--){ |
127 | char *t; |
128 | char *f; |
129 | |
130 | o = e; |
131 | e = a[i]; |
132 | *e++ = 0; |
133 | |
134 | f = linkname(e); |
135 | t = nil; |
136 | |
137 | if(!udirget(b, f, 'L', &t)){ |
138 | if(t == nil) |
139 | break; |
140 | if(link && o==nil){ |
141 | free(t); |
142 | if(f != e) |
143 | break; |
144 | t = udirpath(b, e, 'L'); |
145 | } |
146 | r = fullpath(b, t); |
147 | free(t); |
148 | if(o && o[1]){ |
149 | t = r; |
150 | r = fullpath(t, &o[1]); |
151 | free(t); |
152 | } |
153 | break; |
154 | } |
155 | |
156 | --e; |
157 | if(o) *o = '/'; |
158 | } |
159 | free(b); |
160 | free(a); |
161 | |
162 | return r; |
163 | } |
164 | |
165 | static char * |
166 | resolvepath(char *path, int link) |
167 | { |
168 | char *t; |
169 | int x; |
170 | |
171 | x = 0; |
172 | path = kstrdup(path); |
173 | while(t = resolvepath1(path, link)){ |
174 | if(++x > 8){ |
175 | free(t); |
176 | free(path); |
177 | return nil; |
178 | } |
179 | free(path); path = t; |
180 | } |
181 | return path; |
182 | } |
183 | |
184 | static int |
185 | ropen(char *path, int mode, int perm, Ufile **pf) |
186 | { |
187 | Ufile *f; |
188 | int err; |
189 | char *s, *t; |
190 | int mode9, perm9; |
191 | int fd; |
192 | char *base; |
193 | char *name; |
194 | Rpath **prp; |
195 | |
196 | trace("ropen(%s, %#o, %#o, ...)", path, mode, perm); |
197 | |
198 | base = nil; |
199 | name = nil; |
200 | mode9 = mode & 3; |
201 | perm9 = (perm & ~current->umask) & 0777; |
202 | |
203 | s = shortpath(current->kcwd, path); |
204 | |
205 | if(mode & O_CREAT) { |
206 | Dir *d; |
207 | |
208 | err = -EINVAL; |
209 | if((base = basepath(path, &name)) == nil) |
210 | goto out; |
211 | |
212 | /* resolve base directory */ |
213 | if((d = dirstat(shortpath(current->kcwd, base))) == nil){ |
214 | err = mkerror(); |
215 | if(t = resolvepath1(base, 0)){ |
216 | free(base); base = t; |
217 | t = allocpath(t, nil, name); |
218 | err = fsopen(t, mode, perm, pf); |
219 | free(t); |
220 | } |
221 | goto out; |
222 | } |
223 | err = -ENOTDIR; |
224 | if((d->mode & DMDIR) == 0){ |
225 | free(d); |
226 | goto out; |
227 | } |
228 | free(d); |
229 | |
230 | /* check if here is a symlink in the way */ |
231 | t = udirpath(base, name, 'L'); |
232 | if((fd = open(shortpath(current->kcwd, t), OREAD)) >= 0){ |
233 | free(t); |
234 | close(fd); |
235 | |
236 | if(mode & O_EXCL){ |
237 | err = -EEXIST; |
238 | goto out; |
239 | } |
240 | |
241 | if((t = resolvepath1(path, 0)) == nil) |
242 | goto out; |
243 | err = fsopen(t, mode, perm, pf); |
244 | free(t); |
245 | goto out; |
246 | } |
247 | free(t); |
248 | |
249 | if(mode & (O_EXCL | O_TRUNC)){ |
250 | if(mode & O_EXCL) |
251 | mode9 |= OEXCL; |
252 | fd = create(s, mode9, perm9); |
253 | } else { |
254 | /* try open first to avoid truncating existing the file */ |
255 | if((fd = open(s, mode9)) < 0) |
256 | fd = create(s, mode9, perm9); |
257 | } |
258 | if(fd < 0){ |
259 | err = mkerror(); |
260 | goto out; |
261 | } |
262 | } else { |
263 | if(((mode & 3) == O_RDWR) || ((mode & 3) == O_WRONLY)) |
264 | if(mode & O_TRUNC) |
265 | mode9 |= OTRUNC; |
266 | |
267 | if((fd = open(s, mode9)) < 0){ |
268 | err = mkerror(); |
269 | if(t = resolvepath1(path, 0)){ |
270 | err = fsopen(t, mode, perm, pf); |
271 | free(t); |
272 | } |
273 | goto out; |
274 | } |
275 | } |
276 | |
277 | qlock(&rpathtablk); |
278 | prp = rpathent(path); |
279 | if(*prp != nil){ |
280 | incref(*prp); |
281 | } else { |
282 | Rpath *rp; |
283 | |
284 | rp = kmalloc(sizeof(*rp) + strlen(path) + 1); |
285 | rp->ref = 1; |
286 | rp->hash = nil; |
287 | rp->deleted = 0; |
288 | strcpy(rp->str, path); |
289 | *prp = rp; |
290 | } |
291 | qunlock(&rpathtablk); |
292 | |
293 | f = kmallocz(sizeof(Rfile), 1); |
294 | f->ref = 1; |
295 | f->path = kstrdup(path); |
296 | f->dev = ROOTDEV; |
297 | f->mode = mode; |
298 | f->fd = fd; |
299 | f->off = 0; |
300 | *pf = f; |
301 | |
302 | err = 0; |
303 | |
304 | out: |
305 | free(base); |
306 | free(name); |
307 | |
308 | return err; |
309 | } |
310 | |
311 | static int |
312 | rclose(Ufile *f) |
313 | { |
314 | Rpath **prp; |
315 | Rfile *file = (Rfile*)f; |
316 | static char path[1024]; /* protected by rpathtablk */ |
317 | |
318 | qlock(&rpathtablk); |
319 | prp = rpathent(file->path); |
320 | if(!decref(*prp)){ |
321 | Rpath *rp = *prp; |
322 | *prp = rp->hash; |
323 | if(rp->deleted){ |
324 | if(fd2path(file->fd, path, sizeof(path)) == 0) |
325 | remove(shortpath(current->kcwd, path)); |
326 | } |
327 | free(rp); |
328 | } |
329 | qunlock(&rpathtablk); |
330 | |
331 | close(file->fd); |
332 | return 0; |
333 | } |
334 | |
335 | static int |
336 | rread(Ufile *f, void *buf, int len, vlong off) |
337 | { |
338 | Rfile *file = (Rfile*)f; |
339 | int ret, n; |
340 | |
341 | n = ret = 0; |
342 | if(notifyme(1)) |
343 | return -ERESTART; |
344 | while((n < len) && ((ret = pread(file->fd, (uchar*)buf + n, len - n, off + n)) > 0)) |
345 | n += ret; |
346 | notifyme(0); |
347 | if(ret < 0) |
348 | return mkerror(); |
349 | return n; |
350 | } |
351 | |
352 | static int |
353 | rwrite(Ufile *f, void *buf, int len, vlong off) |
354 | { |
355 | Rfile *file = (Rfile*)f; |
356 | int ret; |
357 | |
358 | if(notifyme(1)) |
359 | return -ERESTART; |
360 | ret = pwrite(file->fd, buf, len, off); |
361 | notifyme(0); |
362 | if(ret < 0) |
363 | ret = mkerror(); |
364 | return ret; |
365 | } |
366 | |
367 | static vlong |
368 | rsize(Ufile *f) |
369 | { |
370 | Rfile *file = (Rfile*)f; |
371 | |
372 | return seek(file->fd, 0, 2); |
373 | } |
374 | |
375 | static int |
376 | raccess(char *path, int mode) |
377 | { |
378 | static char omode[] = { |
379 | 0, // --- |
380 | OEXEC, // --x |
381 | OWRITE, // -w- |
382 | ORDWR, // -wx |
383 | OREAD, // r-- |
384 | OEXEC, // r-x |
385 | ORDWR, // rw- |
386 | ORDWR // rwx |
387 | }; |
388 | |
389 | int err; |
390 | int fd; |
391 | Dir *d; |
392 | char *s; |
393 | |
394 | err = -EINVAL; |
395 | if(mode & ~07) |
396 | return err; |
397 | |
398 | s = shortpath(current->kcwd, path); |
399 | if((d = dirstat(s)) == nil){ |
400 | err = mkerror(); |
401 | if(path = resolvepath1(path, 0)){ |
402 | err = fsaccess(path, mode); |
403 | free(path); |
404 | } |
405 | goto out; |
406 | } |
407 | |
408 | /* ignore the exec bit... firefox gets confused */ |
409 | mode &= ~01; |
410 | if((mode == 0) || (d->mode & DMDIR)){ |
411 | err = 0; |
412 | } else { |
413 | err = -EACCES; |
414 | if((mode & 01) && ((d->mode & 0111) == 0)) |
415 | goto out; |
416 | if((mode & 02) && ((d->mode & 0222) == 0)) |
417 | goto out; |
418 | if((mode & 04) && ((d->mode & 0444) == 0)) |
419 | goto out; |
420 | if((fd = open(s, omode[mode])) >= 0){ |
421 | close(fd); |
422 | err = 0; |
423 | } |
424 | } |
425 | out: |
426 | free(d); |
427 | return err; |
428 | } |
429 | |
430 | static ulong |
431 | dir2statmode(Dir *d) |
432 | { |
433 | ulong mode; |
434 | |
435 | mode = d->mode & 0777; |
436 | if(d->mode & DMDIR) |
437 | mode |= S_IFDIR; |
438 | else if(strcmp(d->name, "cons") == 0) |
439 | mode |= S_IFCHR; |
440 | else if(strncmp(d->name, "PTS.", 4) == 0) |
441 | mode |= S_IFCHR; |
442 | else if(strcmp(d->name, "zero") == 0) |
443 | mode |= S_IFCHR | 0222; |
444 | else if(strcmp(d->name, "null") == 0) |
445 | mode |= S_IFCHR | 0222; |
446 | else if(strncmp(d->name, ".udir.", 6) == 0){ |
447 | switch(d->name[6]){ |
448 | case 'L': |
449 | mode |= S_IFLNK; |
450 | break; |
451 | case 'S': |
452 | mode |= S_IFSOCK; |
453 | break; |
454 | case 'F': |
455 | mode |= S_IFIFO; |
456 | break; |
457 | case 'C': |
458 | mode |= S_IFCHR; |
459 | break; |
460 | case 'B': |
461 | mode |= S_IFBLK; |
462 | break; |
463 | } |
464 | } else if(d->type == '|') |
465 | mode |= S_IFIFO; |
466 | else if(d->type == 'H') |
467 | mode |= S_IFBLK; |
468 | else |
469 | mode |= S_IFREG; |
470 | |
471 | return mode; |
472 | } |
473 | |
474 | static void |
475 | dir2ustat(Dir *d, Ustat *s) |
476 | { |
477 | s->mode = dir2statmode(d); |
478 | s->uid = current->uid; |
479 | s->gid = current->gid; |
480 | s->size = d->length; |
481 | s->atime = d->atime; |
482 | s->mtime = d->mtime; |
483 | s->ctime = d->mtime; |
484 | s->ino = 0; // use d->qid? |
485 | s->dev = 0; |
486 | s->rdev = 0; |
487 | } |
488 | |
489 | static int |
490 | rstat(char *path, int link, Ustat *s) |
491 | { |
492 | Dir *d; |
493 | int err; |
494 | char *t; |
495 | |
496 | if((d = dirstat(shortpath(current->kcwd, path))) == nil){ |
497 | if(link){ |
498 | char *base; |
499 | char *name; |
500 | if(base = basepath(path, &name)){ |
501 | t = udirpath(base, name, 'L'); |
502 | free(name); |
503 | free(base); |
504 | d = dirstat(shortpath(current->kcwd, t)); |
505 | free(t); |
506 | } |
507 | |
508 | } |
509 | } |
510 | if(d == nil){ |
511 | err = mkerror(); |
512 | if(t = resolvepath1(path, 0)){ |
513 | err = fsstat(t, link, s); |
514 | free(t); |
515 | } |
516 | return err; |
517 | } |
518 | |
519 | dir2ustat(d, s); |
520 | s->ino = hashpath(path); |
521 | |
522 | free(d); |
523 | return 0; |
524 | } |
525 | |
526 | static int |
527 | rfstat(Ufile *f, Ustat *s) |
528 | { |
529 | Dir *d; |
530 | |
531 | if((d = dirfstat(f->fd)) == nil) |
532 | return mkerror(); |
533 | |
534 | dir2ustat(d, s); |
535 | s->ino = hashpath(f->path); |
536 | |
537 | free(d); |
538 | return 0; |
539 | } |
540 | |
541 | static char* |
542 | fixname(char *name) |
543 | { |
544 | if(name == nil) |
545 | return nil; |
546 | if(strncmp(name, ".udir.", 6) == 0){ |
547 | if(name[6] && name[7]=='.') |
548 | name += 8; |
549 | } |
550 | return name; |
551 | } |
552 | |
553 | static int |
554 | rreaddir(Ufile *f, Udirent **pd) |
555 | { |
556 | Dir *d; |
557 | int i, n; |
558 | |
559 | seek(f->fd, 0, 0); |
560 | n = dirreadall(f->fd, &d); |
561 | if(n < 0) |
562 | return mkerror(); |
563 | for(i=0; i<n; i++){ |
564 | if((*pd = newdirent(f->path, fixname(d[i].name), dir2statmode(&d[i]))) == nil) |
565 | break; |
566 | pd = &((*pd)->next); |
567 | } |
568 | free(d); |
569 | return i; |
570 | } |
571 | |
572 | static int |
573 | rreadlink(char *path, char *buf, int len) |
574 | { |
575 | int err; |
576 | int fd; |
577 | |
578 | char *t; |
579 | char *name; |
580 | char *base; |
581 | |
582 | trace("rreadlink(%s)", path); |
583 | |
584 | if((base = basepath(path, &name)) == nil) |
585 | return -EINVAL; |
586 | |
587 | /* resolve base path */ |
588 | if((fd = open(shortpath(current->kcwd, base), OREAD)) < 0){ |
589 | err = mkerror(); |
590 | if(t = resolvepath1(base, 0)){ |
591 | free(base); base = t; |
592 | t = allocpath(base, nil, name); |
593 | err = fsreadlink(t, buf, len); |
594 | free(t); |
595 | } |
596 | goto out; |
597 | } |
598 | close(fd); |
599 | |
600 | /* check if path is regular file */ |
601 | if((fd = open(shortpath(current->kcwd, path), OREAD)) >= 0){ |
602 | close(fd); |
603 | err = -EINVAL; |
604 | goto out; |
605 | } |
606 | |
607 | t = udirpath(base, name, 'L'); |
608 | if((fd = open(shortpath(current->kcwd, t), OREAD)) < 0){ |
609 | err = mkerror(); |
610 | free(t); |
611 | goto out; |
612 | } |
613 | free(t); |
614 | if((err = read(fd, buf, len)) < 0) |
615 | err = mkerror(); |
616 | close(fd); |
617 | out: |
618 | free(base); |
619 | free(name); |
620 | return err; |
621 | } |
622 | |
623 | enum { |
624 | COPYSIZE = 8*1024, |
625 | }; |
626 | |
627 | static int |
628 | copyfile(char *from, char *to) |
629 | { |
630 | int err, fromfd, tofd; |
631 | char *buf, *s; |
632 | Dir *ent; |
633 | Dir *dir; |
634 | |
635 | dir = nil; |
636 | buf = nil; |
637 | ent = nil; |
638 | |
639 | tofd = -1; |
640 | |
641 | trace("copyfile(%s, %s)", from, to); |
642 | |
643 | if((fromfd = open(shortpath(current->kcwd, from), OREAD)) < 0){ |
644 | err = mkerror(); |
645 | goto out; |
646 | } |
647 | if((dir = dirfstat(fromfd)) == nil){ |
648 | err = mkerror(); |
649 | goto out; |
650 | } |
651 | s = shortpath(current->kcwd, to); |
652 | if((err = open(s, OREAD)) >= 0){ |
653 | close(err); |
654 | err = -EEXIST; |
655 | goto out; |
656 | } |
657 | if(dir->mode & DMDIR){ |
658 | int n; |
659 | if((tofd = create(s, OREAD, dir->mode)) < 0){ |
660 | err = mkerror(); |
661 | goto out; |
662 | } |
663 | close(tofd); |
664 | tofd = -1; |
665 | while((n = dirread(fromfd, &ent)) > 0){ |
666 | int i; |
667 | |
668 | for(i=0; i<n; i++){ |
669 | char *froment, *toent; |
670 | |
671 | froment = allocpath(from, nil, ent[i].name); |
672 | toent = allocpath(to, nil, ent[i].name); |
673 | err = copyfile(froment, toent); |
674 | free(froment); |
675 | free(toent); |
676 | |
677 | if(err < 0) |
678 | goto out; |
679 | } |
680 | free(ent); ent = nil; |
681 | } |
682 | } else { |
683 | if((tofd = create(s, OWRITE, dir->mode)) < 0){ |
684 | err = mkerror(); |
685 | goto out; |
686 | } |
687 | buf = kmalloc(COPYSIZE); |
688 | for(;;){ |
689 | err = read(fromfd, buf, COPYSIZE); |
690 | if(err == 0) |
691 | break; |
692 | if(err < 0){ |
693 | err = mkerror(); |
694 | goto out; |
695 | } |
696 | if(write(tofd, buf, err) != err){ |
697 | err = mkerror(); |
698 | goto out; |
699 | } |
700 | } |
701 | } |
702 | |
703 | err = 0; |
704 | out: |
705 | free(ent); |
706 | free(dir); |
707 | free(buf); |
708 | close(fromfd); |
709 | close(tofd); |
710 | return err; |
711 | } |
712 | |
713 | static int |
714 | removefile(char *path) |
715 | { |
716 | int err; |
717 | int n; |
718 | Dir *d; |
719 | int fd; |
720 | char *s; |
721 | |
722 | trace("removefile(%s)", path); |
723 | |
724 | s = shortpath(current->kcwd, path); |
725 | |
726 | if((d = dirstat(s)) == nil) |
727 | return mkerror(); |
728 | if(remove(s) == 0){ |
729 | free(d); |
730 | return 0; |
731 | } |
732 | if((d->mode & DMDIR) == 0){ |
733 | free(d); |
734 | return mkerror(); |
735 | } |
736 | free(d); |
737 | if((fd = open(s, OREAD)) < 0) |
738 | return mkerror(); |
739 | err = 0; |
740 | d = nil; |
741 | while((n = dirread(fd, &d)) > 0){ |
742 | char *t; |
743 | int i; |
744 | |
745 | for(i=0; i<n; i++){ |
746 | t = allocpath(path, nil, d[i].name); |
747 | err = removefile(t); |
748 | free(t); |
749 | |
750 | if(err < 0) |
751 | break; |
752 | } |
753 | free(d); d = nil; |
754 | |
755 | if(err < 0) |
756 | break; |
757 | } |
758 | close(fd); |
759 | if(err < 0) |
760 | return err; |
761 | if(n < 0) |
762 | return mkerror(); |
763 | if(remove(s) < 0) |
764 | return mkerror(); |
765 | return 0; |
766 | } |
767 | |
768 | static int |
769 | resolvefromtopath(char **from, char **to) |
770 | { |
771 | char *t; |
772 | |
773 | trace("resolvefromtopath(%s, %s)", *from, *to); |
774 | |
775 | if((*from = resolvepath(*from, 1)) == nil){ |
776 | *to = nil; |
777 | return -ELOOP; |
778 | } |
779 | if((*to = resolvepath(*to, 1)) == nil){ |
780 | free(*from); |
781 | *from = nil; |
782 | return -ELOOP; |
783 | } |
784 | if(strstr(*from, ".udir.L")){ |
785 | char *x; |
786 | |
787 | x = nil; |
788 | for(t=*to; *t; t++){ |
789 | if(*t == '/') |
790 | x = t; |
791 | } |
792 | |
793 | if(strncmp(x+1, ".udir.", 6)){ |
794 | *x = 0; |
795 | t = udirpath(*to, x+1, 'L'); |
796 | free(*to); *to = t; |
797 | } |
798 | } |
799 | |
800 | return 0; |
801 | } |
802 | |
803 | static int |
804 | rrename(char *from, char *to) |
805 | { |
806 | int err; |
807 | char *x, *y, *t; |
808 | |
809 | trace("rrename(%s, %s)", from, to); |
810 | |
811 | if((err = resolvefromtopath(&from, &to)) < 0) |
812 | goto out; |
813 | if(strcmp(from, to) == 0) |
814 | goto out; |
815 | x = nil; |
816 | for(t=from; *t; t++){ |
817 | if(*t == '/') |
818 | x = t; |
819 | } |
820 | y = nil; |
821 | for(t=to; *t; t++){ |
822 | if(*t == '/') |
823 | y = t; |
824 | } |
825 | if(x && y){ |
826 | char *e; |
827 | |
828 | e = nil; |
829 | *x = 0; *y = 0; |
830 | if(strcmp(from, to) == 0) |
831 | e = &y[1]; |
832 | *x = '/'; *y = '/'; |
833 | |
834 | if(e != nil){ |
835 | Dir d; |
836 | |
837 | nulldir(&d); |
838 | d.name = e; |
839 | |
840 | remove(to); |
841 | if(dirwstat(shortpath(current->kcwd, from), &d) < 0) |
842 | err = mkerror(); |
843 | goto out; |
844 | } |
845 | } |
846 | t = ksmprint("%s%d%d.tmp", to, current->pid, current->tid); |
847 | if((err = copyfile(from, t)) == 0){ |
848 | Dir d; |
849 | |
850 | nulldir(&d); |
851 | d.name = &y[1]; |
852 | |
853 | remove(shortpath(current->kcwd, to)); |
854 | if(dirwstat(shortpath(current->kcwd, t), &d) < 0) { |
855 | err = mkerror(); |
856 | } else { |
857 | removefile(from); |
858 | } |
859 | } |
860 | if(err != 0) |
861 | removefile(t); |
862 | free(t); |
863 | out: |
864 | free(from); |
865 | free(to); |
866 | |
867 | return err; |
868 | } |
869 | |
870 | static int |
871 | rmkdir(char *path, int mode) |
872 | { |
873 | int err; |
874 | Dir *d; |
875 | int fd; |
876 | int mode9; |
877 | |
878 | char *base; |
879 | char *name; |
880 | char *t; |
881 | |
882 | trace("rmkdir(%s, %#o)", path, mode); |
883 | |
884 | if((base = basepath(path, &name)) == nil) |
885 | return -EINVAL; |
886 | |
887 | if((d = dirstat(shortpath(current->kcwd, base))) == nil){ |
888 | err = mkerror(); |
889 | if(t = resolvepath1(base, 0)){ |
890 | free(base); base = t; |
891 | t = allocpath(base, nil, name); |
892 | err = fsmkdir(t, mode); |
893 | free(t); |
894 | } |
895 | goto out; |
896 | } |
897 | err = -ENOTDIR; |
898 | if((d->mode & DMDIR) == 0){ |
899 | free(d); |
900 | goto out; |
901 | } |
902 | free(d); |
903 | |
904 | err = -EEXIST; |
905 | t = udirpath(base, name, 'L'); |
906 | if(d = dirstat(shortpath(current->kcwd, t))){ |
907 | free(d); |
908 | free(t); |
909 | goto out; |
910 | } |
911 | free(t); |
912 | |
913 | mode9 = DMDIR | ((mode & ~current->umask) & 0777); |
914 | if((fd = create(shortpath(current->kcwd, path), OREAD|OEXCL, mode9)) < 0){ |
915 | err = mkerror(); |
916 | goto out; |
917 | } |
918 | close(fd); |
919 | err = 0; |
920 | |
921 | out: |
922 | free(name); |
923 | free(base); |
924 | return err; |
925 | } |
926 | |
927 | static void |
928 | combinedir(Dir *ndir, Dir *odir) |
929 | { |
930 | if(ndir->mode != ~0) |
931 | ndir->mode = (odir->mode & ~0777) | (ndir->mode & 0777); |
932 | } |
933 | |
934 | static int |
935 | uwstat(char *path, Dir *ndir, int link) |
936 | { |
937 | int err; |
938 | Dir *dir; |
939 | char *s; |
940 | |
941 | trace("uwstat(%s, ..., %d)", path, link); |
942 | |
943 | s = shortpath(current->kcwd, path); |
944 | if((dir = dirstat(s)) == nil){ |
945 | err = mkerror(); |
946 | if(link){ |
947 | char *base; |
948 | char *name; |
949 | |
950 | if(base = basepath(path, &name)){ |
951 | char *t; |
952 | |
953 | t = udirpath(base, name, 'L'); |
954 | free(base); |
955 | free(name); |
956 | |
957 | err = uwstat(t, ndir, 0); |
958 | free(t); |
959 | } |
960 | } |
961 | return err; |
962 | } |
963 | combinedir(ndir, dir); |
964 | err = 0; |
965 | if(dirwstat(s, ndir) < 0) |
966 | err = mkerror(); |
967 | free(dir); |
968 | return err; |
969 | } |
970 | |
971 | static int |
972 | uwfstat(Ufile *f, Dir *ndir) |
973 | { |
974 | int err; |
975 | Dir *dir; |
976 | |
977 | if((dir = dirfstat(f->fd)) == nil){ |
978 | err = mkerror(); |
979 | goto out; |
980 | } |
981 | combinedir(ndir, dir); |
982 | err = 0; |
983 | if(dirfwstat(f->fd, ndir) < 0) |
984 | err = mkerror(); |
985 | out: |
986 | free(dir); |
987 | return err; |
988 | } |
989 | |
990 | static int |
991 | rutime(char *path, long atime, long mtime) |
992 | { |
993 | Dir ndir; |
994 | int err; |
995 | |
996 | trace("rutime(%s, %ld, %ld)", path, atime, mtime); |
997 | |
998 | nulldir(&ndir); |
999 | ndir.atime = atime; |
1000 | ndir.mtime = mtime; |
1001 | |
1002 | if((err = uwstat(path, &ndir, 1)) < 0){ |
1003 | char *t; |
1004 | |
1005 | if(t = resolvepath1(path, 0)){ |
1006 | err = fsutime(t, atime, mtime); |
1007 | free(t); |
1008 | } |
1009 | } |
1010 | return err; |
1011 | } |
1012 | |
1013 | static int |
1014 | rchmod(char *path, int mode) |
1015 | { |
1016 | Dir ndir; |
1017 | int err; |
1018 | |
1019 | trace("rchmod(%s, %#o)", path, mode); |
1020 | |
1021 | nulldir(&ndir); |
1022 | ndir.mode = mode; |
1023 | |
1024 | if((err = uwstat(path, &ndir, 1)) < 0){ |
1025 | char *t; |
1026 | |
1027 | if(t = resolvepath1(path, 0)){ |
1028 | err = fschmod(t, mode); |
1029 | free(t); |
1030 | } |
1031 | } |
1032 | return err; |
1033 | } |
1034 | |
1035 | static int |
1036 | rchown(char *path, int uid, int gid, int link) |
1037 | { |
1038 | Ustat s; |
1039 | |
1040 | USED(uid); |
1041 | USED(gid); |
1042 | |
1043 | /* FIXME, just return the right errorcode for now */ |
1044 | return fsstat(path, link, &s); |
1045 | } |
1046 | |
1047 | static int |
1048 | rtruncate(char *path, vlong size) |
1049 | { |
1050 | Dir ndir; |
1051 | int err; |
1052 | |
1053 | trace("rtruncate(%s, %lld)", path, size); |
1054 | |
1055 | nulldir(&ndir); |
1056 | ndir.length = size; |
1057 | |
1058 | if((err = uwstat(path, &ndir, 0)) < 0){ |
1059 | char *t; |
1060 | |
1061 | if(t = resolvepath1(path, 0)){ |
1062 | err = fstruncate(t, size); |
1063 | free(t); |
1064 | } |
1065 | } |
1066 | return err; |
1067 | } |
1068 | |
1069 | static int |
1070 | rfchmod(Ufile *f, int mode) |
1071 | { |
1072 | Dir ndir; |
1073 | |
1074 | nulldir(&ndir); |
1075 | ndir.mode = mode; |
1076 | return uwfstat(f, &ndir); |
1077 | } |
1078 | |
1079 | static int |
1080 | rfchown(Ufile *f, int uid, int gid) |
1081 | { |
1082 | USED(f); |
1083 | USED(uid); |
1084 | USED(gid); |
1085 | |
1086 | return 0; |
1087 | } |
1088 | |
1089 | static int |
1090 | rftruncate(Ufile *f, vlong size) |
1091 | { |
1092 | Dir ndir; |
1093 | |
1094 | nulldir(&ndir); |
1095 | ndir.length = size; |
1096 | return uwfstat(f, &ndir); |
1097 | } |
1098 | |
1099 | static int |
1100 | runlink(char *path, int rmdir) |
1101 | { |
1102 | int err; |
1103 | Dir *dir; |
1104 | char *t, *s; |
1105 | char *base; |
1106 | char *name; |
1107 | char *rpath; |
1108 | Rpath **prp; |
1109 | |
1110 | trace("runlink(%s, %d)", path, rmdir); |
1111 | |
1112 | rpath = nil; |
1113 | dir = nil; |
1114 | err = -EINVAL; |
1115 | if((base = basepath(path, &name)) == nil) |
1116 | goto out; |
1117 | if(dir = dirstat(shortpath(current->kcwd, path))){ |
1118 | rpath = kstrdup(path); |
1119 | } else { |
1120 | rpath = udirpath(base, name, 'L'); |
1121 | dir = dirstat(shortpath(current->kcwd, rpath)); |
1122 | } |
1123 | if(dir == nil){ |
1124 | err = mkerror(); |
1125 | if(t = resolvepath1(path, 0)){ |
1126 | err = fsunlink(t, rmdir); |
1127 | free(t); |
1128 | } |
1129 | goto out; |
1130 | } |
1131 | if(rmdir){ |
1132 | if((dir->mode & DMDIR) == 0){ |
1133 | err = -ENOTDIR; |
1134 | goto out; |
1135 | } |
1136 | } else { |
1137 | if(dir->mode & DMDIR){ |
1138 | err = -EISDIR; |
1139 | goto out; |
1140 | } |
1141 | } |
1142 | |
1143 | s = shortpath(current->kcwd, rpath); |
1144 | |
1145 | qlock(&rpathtablk); |
1146 | prp = rpathent(path); |
1147 | if(*prp){ |
1148 | Dir ndir; |
1149 | |
1150 | t = ksmprint(".%s.%d.deleted", name, current->kpid); |
1151 | nulldir(&ndir); |
1152 | ndir.name = t; |
1153 | trace("runlink: file %s still in use renaming to -> %s", path, t); |
1154 | if(dirwstat(s, &ndir) < 0){ |
1155 | qunlock(&rpathtablk); |
1156 | err = mkerror(); |
1157 | free(t); |
1158 | goto out; |
1159 | } |
1160 | free(t); |
1161 | (*prp)->deleted = 1; |
1162 | qunlock(&rpathtablk); |
1163 | |
1164 | } else { |
1165 | int x; |
1166 | qunlock(&rpathtablk); |
1167 | |
1168 | x = 0; |
1169 | while(remove(s) < 0){ |
1170 | err = mkerror(); |
1171 | if(++x > 8){ |
1172 | /* old debian bug clashes with mntgen */ |
1173 | if(strcmp(base, "/")==0 && strstr(path, ".dpkg-")) |
1174 | err = -ENOENT; |
1175 | goto out; |
1176 | } |
1177 | } |
1178 | } |
1179 | err = 0; |
1180 | out: |
1181 | free(dir); |
1182 | free(name); |
1183 | free(base); |
1184 | free(rpath); |
1185 | |
1186 | return err; |
1187 | } |
1188 | |
1189 | static int |
1190 | rlink(char *old, char *new, int sym) |
1191 | { |
1192 | int err; |
1193 | int fd; |
1194 | char *base; |
1195 | char *name; |
1196 | char *t; |
1197 | |
1198 | trace("rlink(%s, %s, %d)", old, new, sym); |
1199 | |
1200 | if((base = basepath(new, &name)) == nil) |
1201 | return -EINVAL; |
1202 | |
1203 | /* resolve base directory */ |
1204 | if((fd = open(shortpath(current->kcwd, base), OREAD)) < 0){ |
1205 | err = mkerror(); |
1206 | if(t = resolvepath1(base, 0)){ |
1207 | free(base); base = t; |
1208 | t = allocpath(base, nil, name); |
1209 | err = fslink(old, t, sym); |
1210 | free(t); |
1211 | } |
1212 | goto out; |
1213 | } |
1214 | close(fd); |
1215 | |
1216 | if(sym == 0){ |
1217 | if((err = resolvefromtopath(&old, &new)) == 0) |
1218 | err = copyfile(old, new); |
1219 | free(old); |
1220 | free(new); |
1221 | goto out; |
1222 | } |
1223 | |
1224 | /* check if regular file is in the way */ |
1225 | err = -EEXIST; |
1226 | if((fd = open(shortpath(current->kcwd, new), OREAD)) >= 0){ |
1227 | close(fd); |
1228 | goto out; |
1229 | } |
1230 | |
1231 | /* try to create the link, will fail if alreadt exists */ |
1232 | t = udirpath(base, name, 'L'); |
1233 | if((fd = create(shortpath(current->kcwd, t), OWRITE|OEXCL, 0777)) < 0){ |
1234 | err = mkerror(); |
1235 | free(t); |
1236 | goto out; |
1237 | } |
1238 | free(t); |
1239 | |
1240 | if(write(fd, old, strlen(old)) < 0){ |
1241 | err = mkerror(); |
1242 | close(fd); |
1243 | goto out; |
1244 | } |
1245 | close(fd); |
1246 | err = 0; |
1247 | out: |
1248 | free(base); |
1249 | free(name); |
1250 | return err; |
1251 | } |
1252 | |
1253 | static Udev rootdev = |
1254 | { |
1255 | .open = ropen, |
1256 | .access = raccess, |
1257 | .stat = rstat, |
1258 | .link = rlink, |
1259 | .unlink = runlink, |
1260 | .rename = rrename, |
1261 | .mkdir = rmkdir, |
1262 | .utime = rutime, |
1263 | .chmod = rchmod, |
1264 | .chown = rchown, |
1265 | .truncate = rtruncate, |
1266 | |
1267 | .read = rread, |
1268 | .write = rwrite, |
1269 | .size = rsize, |
1270 | .close = rclose, |
1271 | |
1272 | .fstat = rfstat, |
1273 | .readdir = rreaddir, |
1274 | .readlink = rreadlink, |
1275 | |
1276 | .fchmod = rfchmod, |
1277 | .fchown = rfchown, |
1278 | .ftruncate = rftruncate, |
1279 | }; |
1280 | |
1281 | void rootdevinit(void) |
1282 | { |
1283 | devtab[ROOTDEV] = &rootdev; |
1284 | |
1285 | fsmount(&rootdev, ""); |
1286 | } |