devfs-posix.c - vx32 - Local 9vx git repository for patches.
(HTM) git clone git://r-36.net/vx32
(DIR) Log
(DIR) Files
(DIR) Refs
---
devfs-posix.c (15130B)
---
1 #include "u.h"
2 #include <stdio.h> /* for remove, rename */
3 #include <pwd.h>
4 #include <grp.h> /* going to regret this - getgrgid is a stack smasher */
5 #include <sys/socket.h>
6 #include <sys/un.h>
7
8 #if defined(__FreeBSD__)
9 #include <sys/disk.h>
10 #include <sys/disklabel.h>
11 #include <sys/ioctl.h>
12 #endif
13
14 #if defined(__APPLE__)
15 #include <sys/disk.h>
16 #endif
17
18 #if defined(__linux__)
19 #include <linux/hdreg.h>
20 #include <linux/fs.h>
21 #include <sys/ioctl.h>
22 #endif
23
24 #include "lib.h"
25 #include "mem.h"
26 #include "dat.h"
27 #include "fns.h"
28 #include "error.h"
29
30 enum
31 {
32 Trace = 0,
33 FsChar = 'Z',
34 };
35
36 extern char *canopen;
37 extern Path *addelem(Path*, char*, Chan*);
38 static char *uidtoname(int);
39 static char *gidtoname(int);
40 static int nametouid(char*);
41 static int nametogid(char*);
42
43 static vlong disksize(int, struct stat*);
44
45 typedef struct UnixFd UnixFd;
46 struct UnixFd
47 {
48 int fd;
49 int issocket;
50 DIR* dir;
51 vlong diroffset;
52 QLock dirlock;
53 struct dirent *nextde;
54 Path *path;
55 };
56
57 void
58 oserrstr(void)
59 {
60 if (errno == EINTR)
61 kstrcpy(up->errstr, Eintr, ERRMAX);
62 else
63 kstrcpy(up->errstr, strerror(errno), ERRMAX);
64 }
65
66 void
67 oserror(void)
68 {
69 if (errno == EINTR)
70 error(Eintr);
71 else
72 error(strerror(errno));
73 }
74
75 static Qid
76 fsqid(struct stat *st)
77 {
78 Qid q;
79 int dev;
80 static int nqdev;
81 static uchar *qdev;
82
83 if(qdev == 0)
84 qdev = smalloc(65536U);
85
86 q.type = 0;
87 if((st->st_mode&S_IFMT) == S_IFDIR)
88 q.type = QTDIR;
89
90 dev = st->st_dev & 0xFFFFUL;
91 if(qdev[dev] == 0)
92 qdev[dev] = ++nqdev;
93
94 q.path = (vlong)qdev[dev]<<48;
95 q.path ^= st->st_ino;
96 q.vers = st->st_mtime;
97
98 return q;
99 }
100
101 static Chan*
102 fsattach(char *spec)
103 {
104 struct stat st;
105 Chan *c;
106 UnixFd *ufd;
107 int dev;
108
109 dev = 1;
110 if(spec && spec[0]){
111 snprint(up->genbuf, sizeof up->genbuf, "no file system #%C%s", FsChar, spec);
112 error(up->genbuf);
113 }
114
115 if(stat("/", &st) < 0)
116 oserror();
117
118 c = devattach(FsChar, 0);
119 ufd = mallocz(sizeof(UnixFd), 1);
120 ufd->path = newpath("/");
121 ufd->fd = -1;
122
123 c->aux = ufd;
124 c->dev = dev;
125 c->qid = fsqid(&st);
126
127 if(Trace)
128 print("fsattach /\n");
129
130 return c;
131 }
132
133 static Chan*
134 fsclone(Chan *c, Chan *nc)
135 {
136 UnixFd *ufd;
137
138 ufd = mallocz(sizeof(UnixFd), 1);
139 *ufd = *(UnixFd*)c->aux;
140 if(ufd->path)
141 incref(&ufd->path->ref);
142 ufd->fd = -1;
143 nc->aux = ufd;
144 return nc;
145 }
146
147 static char*
148 lastelem(char *s)
149 {
150 char *t;
151
152 if(s[0] == '/' && s[1] == 0)
153 return s;
154 t = strrchr(s, '/');
155 if(t == nil)
156 return s;
157 return t+1;
158 }
159
160 static char*
161 fspath(Chan *c, char *suffix)
162 {
163 char *s, *t;
164 int len;
165 UnixFd *ufd;
166
167 ufd = c->aux;
168 s = ufd->path->s;
169 len = strlen(s)+1;
170 if(suffix)
171 len += 1+strlen(suffix);
172 t = smalloc(len);
173 strcpy(t, s);
174 if(suffix){
175 if(s[strlen(s)-1] != '/')
176 strcat(t, "/");
177 strcat(t, suffix);
178 }
179 return t;
180 }
181
182 static int
183 fswalk1(Chan *c, char *name)
184 {
185 struct stat st;
186 char *path;
187 UnixFd *ufd;
188
189 ufd = c->aux;
190 if(strcmp(name, "..") == 0 && strcmp(ufd->path->s, "/") == 0)
191 return 0;
192
193 path = fspath(c, name);
194 if(stat(path, &st) < 0){
195 if(Trace)
196 print("fswalk1 %s (%s)\n", path, strerror(errno));
197 free(path);
198 return -1;
199 }
200 if(Trace)
201 print("fswalk1 %s\n", path);
202 free(path);
203
204 c->qid = fsqid(&st);
205 return 0;
206 }
207
208 static void
209 replacepath(Chan *c, Path *p)
210 {
211 UnixFd *ufd;
212
213 ufd = c->aux;
214 incref(&p->ref);
215 pathclose(ufd->path);
216 ufd->path = p;
217 }
218
219 static Walkqid*
220 fswalk(Chan *c, Chan *nc, char **name, int nname)
221 {
222 int i;
223 Path *path;
224 Walkqid *wq;
225 UnixFd *ufd;
226
227 if(nc != nil)
228 panic("fswalk: nc != nil");
229 wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid));
230 nc = devclone(c);
231 fsclone(c, nc);
232 ufd = c->aux;
233 path = ufd->path;
234 incref(&path->ref);
235
236 wq->clone = nc;
237 for(i=0; i<nname; i++){
238 ufd = nc->aux;
239 replacepath(nc, path);
240 if(fswalk1(nc, name[i]) < 0){
241 if(i == 0){
242 pathclose(path);
243 cclose(nc);
244 free(wq);
245 error(Enonexist);
246 }
247 break;
248 }
249 path = addelem(path, name[i], nil);
250 wq->qid[i] = nc->qid;
251 }
252 replacepath(nc, path);
253 pathclose(path);
254 if(i != nname){
255 cclose(nc);
256 wq->clone = nil;
257 }
258 wq->nqid = i;
259 return wq;
260 }
261
262 static int
263 fsdirstat(char *path, int dev, Dir *d)
264 {
265 int fd;
266 struct stat st;
267
268 if(stat(path, &st) < 0 && lstat(path, &st) < 0)
269 return -1;
270
271 d->name = lastelem(path);
272 d->uid = uidtoname(st.st_uid);
273 d->gid = gidtoname(st.st_gid);
274 d->muid = "";
275 d->qid = fsqid(&st);
276 d->mode = (d->qid.type<<24) | (st.st_mode&0777);
277 d->atime = st.st_atime;
278 d->mtime = st.st_mtime;
279 d->length = st.st_size;
280 if(S_ISBLK(st.st_mode) && (fd = open(path, O_RDONLY)) >= 0){
281 d->length = disksize(fd, &st);
282 close(fd);
283 }
284
285 // devmnt leaves 1-9 unused so that we can steal them.
286 // it is easier for all involved if #Z shows M as the file type instead of Z.
287 // dev is c->dev, either 1 (#Z) or 2 (#Zplan9).
288 d->type = 'M';
289 d->dev = dev;
290 return 0;
291 }
292
293 static int
294 fsstat(Chan *c, uchar *buf, int n)
295 {
296 Dir d;
297 char *path;
298 UnixFd *ufd;
299
300 ufd = c->aux;
301 if(Trace)
302 print("fsstat %s\n", ufd->path->s);
303
304 if(n < BIT16SZ)
305 error(Eshortstat);
306
307 path = fspath(c, nil);
308 if(fsdirstat(path, c->dev, &d) < 0){
309 free(path);
310 oserror();
311 }
312 if(strcmp(ufd->path->s, "/") == 0)
313 d.name = "/";
314 n = convD2M(&d, buf, n);
315 free(path);
316 return n;
317 }
318
319 static int
320 opensocket(UnixFd *ufd, char *path)
321 {
322 int fd;
323 struct stat st;
324 struct sockaddr_un su;
325
326 if(stat(path, &st) < 0)
327 return -1;
328 if(!S_ISSOCK(st.st_mode))
329 return -1;
330 memset(&su, 0, sizeof su);
331 su.sun_family = AF_UNIX;
332 if(strlen(path)+1 > sizeof su.sun_path){
333 errno = ENAMETOOLONG;
334 return -1;
335 }
336 strcpy(su.sun_path, path);
337 if((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
338 return -1;
339 if(connect(fd, (struct sockaddr*)&su, sizeof su) < 0){
340 close(fd);
341 return -1;
342 }
343 ufd->fd = fd;
344 ufd->issocket = 1;
345 return 0;
346 }
347
348 static Chan*
349 fsopen(Chan *c, int mode)
350 {
351 char *path;
352 int m;
353 UnixFd *ufd;
354
355 ufd = c->aux;
356 if(Trace)
357 print("fsopen %s %#x\n", ufd->path->s, mode);
358
359 /* protect files whose path does not begin with allowed */
360 if(strncmp(ufd->path->s, canopen, strlen(canopen)) != 0)
361 error(Eperm);
362
363 if(mode & ~(OTRUNC|ORCLOSE|3))
364 error(Ebadarg);
365
366 if((c->qid.type & QTDIR) && mode != OREAD)
367 error(Eperm);
368
369 if((c->qid.type&QTDIR) && mode != OREAD)
370 error(Eperm);
371
372 c->mode = openmode(mode);
373 path = fspath(c, nil);
374 if(c->qid.type & QTDIR){
375 ufd->dir = opendir(path);
376 if(ufd->dir == nil){
377 free(path);
378 oserror();
379 }
380 ufd->diroffset = 0;
381 ufd->nextde = nil;
382 }else{
383 m = mode & 3;
384 if(m == OEXEC)
385 m = OREAD;
386 if(mode & OTRUNC)
387 m |= O_TRUNC;
388 if((ufd->fd = open(path, m)) < 0 && opensocket(ufd, path) < 0){
389 free(path);
390 oserror();
391 }
392 }
393 free(path);
394 c->flag |= COPEN;
395 return c;
396 }
397
398 static void
399 fscreate(Chan *c, char *name, int mode, ulong perm)
400 {
401 char *path, *path0;
402 int fd, mm;
403 UnixFd *ufd;
404 struct stat st;
405
406 ufd = c->aux;
407 if(Trace)
408 print("fscreate %s %#x %#o\n", ufd->path->s, mode, perm);
409
410 if(!(c->qid.type & QTDIR))
411 error(Enotdir);
412
413 if(mode & ~(OTRUNC|ORCLOSE|3))
414 error(Ebadarg);
415
416 if(perm & ~(DMDIR|0777))
417 error(Ebadarg);
418
419 path0 = fspath(c, nil);
420 path = fspath(c, name);
421 if(waserror()){
422 free(path);
423 free(path0);
424 nexterror();
425 }
426
427 if(stat(path0, &st) < 0)
428 oserror();
429
430 if(perm & DMDIR){
431 if(mode != OREAD)
432 error(Eperm);
433 /* have to do the minimum 0400 so we can open it */
434 if(mkdir(path, (0400 | perm) & 0777) < 0)
435 oserror();
436 if((fd = open(path, 0)) < 0)
437 oserror();
438 fchown(fd, -1, st.st_gid);
439 if(fstat(fd, &st) < 0){
440 close(fd);
441 oserror();
442 }
443 if((ufd->dir = opendir(path)) == nil) {
444 /* arguably we should set the mode here too
445 * but it's hard to see that this case
446 * will ever happen
447 */
448 close(fd);
449 oserror();
450 }
451 // Be like Plan 9 file servers: inherit mode bits
452 // and group from parent.
453 fchmod(fd, perm & st.st_mode & 0777);
454 close(fd);
455 ufd->diroffset = 0;
456 ufd->nextde = nil;
457 }else{
458 mm = mode & 3;
459 if(mode & OTRUNC)
460 mm |= O_TRUNC;
461 if((fd = open(path, mm|O_CREAT|O_EXCL, 0666)) < 0)
462 oserror();
463 // Be like Plan 9 file servers: inherit mode bits
464 // and group from parent.
465 fchmod(fd, perm & st.st_mode & 0777);
466 fchown(fd, -1, st.st_gid);
467 if(fstat(fd, &st) < 0){
468 close(fd);
469 oserror();
470 }
471 ufd->fd = fd;
472 }
473 free(path);
474 free(path0);
475 poperror();
476
477 ufd->path = addelem(ufd->path, name, nil);
478 c->qid = fsqid(&st);
479 c->offset = 0;
480 c->flag |= COPEN;
481 c->mode = openmode(mode);
482 }
483
484 static void
485 fsclose(Chan *c)
486 {
487 UnixFd *ufd;
488 char *path;
489
490 ufd = c->aux;
491 if(Trace)
492 print("fsclose %s\n", ufd->path->s);
493
494 if(c->flag & COPEN) {
495 if(c->flag & CRCLOSE) {
496 path = fspath(c, nil);
497 unlink(path);
498 free(path);
499 }
500 if(c->qid.type & QTDIR)
501 closedir(ufd->dir);
502 else
503 close(ufd->fd);
504 }
505 if(ufd->path)
506 pathclose(ufd->path);
507 free(ufd);
508 }
509
510 static long fsdirread(Chan*, uchar*, long, vlong);
511
512 static long
513 fsread(Chan *c, void *va, long n, vlong offset)
514 {
515 int r;
516 UnixFd *ufd;
517
518 if(c->qid.type & QTDIR)
519 return fsdirread(c, va, n, offset);
520
521 ufd = c->aux;
522 if(ufd->issocket)
523 r = read(ufd->fd, va, n);
524 else
525 r = pread(ufd->fd, va, n, offset);
526 if(r < 0)
527 oserror();
528 return r;
529 }
530
531 static long
532 fswrite(Chan *c, void *va, long n, vlong offset)
533 {
534 int r;
535 UnixFd *ufd;
536
537 ufd = c->aux;
538 if(ufd->issocket)
539 r = write(ufd->fd, va, n);
540 else
541 r = pwrite(ufd->fd, va, n, offset);
542 if(r < 0)
543 oserror();
544 return r;
545 }
546
547 static int
548 fswstat(Chan *c, uchar *buf, int n)
549 {
550 char *elem, *path, *npath, *strs, *t;
551 int nn;
552 Dir d;
553 UnixFd *ufd;
554
555 if(n < 2)
556 error(Ebadstat);
557
558 nn = GBIT16((uchar*)buf);
559 strs = smalloc(nn);
560 if(convM2D(buf, n, &d, strs) != n){
561 free(strs);
562 error(Ebadstat);
563 }
564
565 path = fspath(c, nil);
566 if(waserror()){
567 free(path);
568 free(strs);
569 nexterror();
570 }
571
572 if(d.muid[0])
573 error("cannot change muid");
574
575 if(d.uid[0] || d.gid[0]){
576 int uid, gid;
577
578 uid = -1;
579 gid = -1;
580 if(d.uid[0] && (uid = nametouid(d.uid)) < 0)
581 error("unknown uid");
582 if(d.gid[0] && (gid = nametogid(d.gid)) < 0)
583 error("unknown gid");
584 if(chown(path, uid, gid) < 0)
585 oserror();
586 }
587
588 ufd = c->aux;
589 elem = lastelem(path);
590 if(d.name[0] && strcmp(d.name, elem) != 0){
591 if(strchr(d.name, '/'))
592 error(Ebadarg);
593 npath = smalloc(strlen(path)+strlen(d.name)+1);
594 strcpy(npath, path);
595 t = strrchr(npath, '/');
596 strcpy(t+1, d.name);
597 if(rename(path, npath) < 0){
598 free(npath);
599 oserror();
600 }
601 free(npath);
602 }
603
604 if(~d.mode != 0 && chmod(path, d.mode&0777) < 0)
605 oserror();
606
607 // TODO: Code to change uid, gid.
608
609 poperror();
610 return n;
611 }
612
613 static int
614 isdots(char *name)
615 {
616 if(name[0] != '.')
617 return 0;
618 if(name[1] == '\0')
619 return 1;
620 if(name[1] != '.')
621 return 0;
622 if(name[2] == '\0')
623 return 1;
624 return 0;
625 }
626
627 static long
628 fsdirread(Chan *c, uchar *va, long count, vlong offset)
629 {
630 char *path;
631 int n, total;
632 struct dirent *de;
633 UnixFd *ufd;
634 Dir d;
635
636 ufd = c->aux;
637 qlock(&ufd->dirlock);
638 if(waserror()){
639 qunlock(&ufd->dirlock);
640 nexterror();
641 }
642
643 if(ufd->diroffset != offset){
644 if(offset != 0)
645 error(Ebadarg);
646 ufd->diroffset = 0;
647 ufd->nextde = nil;
648 rewinddir(ufd->dir);
649 }
650
651 total = 0;
652 while(total+BIT16SZ < count) {
653 if(ufd->nextde){
654 de = ufd->nextde;
655 ufd->nextde = nil;
656 }
657 else if((de = readdir(ufd->dir)) == nil)
658 break;
659 if(isdots(de->d_name))
660 continue;
661 path = fspath(c, de->d_name);
662 if(fsdirstat(path, c->dev, &d) < 0){
663 free(path);
664 continue;
665 }
666 n = convD2M(&d, (uchar*)va+total, count-total);
667 free(path);
668 if(n == BIT16SZ){
669 ufd->nextde = de;
670 break;
671 }
672 total += n;
673 }
674 ufd->diroffset += total;
675 qunlock(&ufd->dirlock);
676 poperror();
677 return total;
678 }
679
680 static void
681 fsremove(Chan *c)
682 {
683 char *path;
684 UnixFd *ufd;
685
686 ufd = c->aux;
687 if(Trace)
688 print("fsremove %s\n", ufd->path->s);
689
690 path = fspath(c, nil);
691 if(waserror()){
692 free(path);
693 nexterror();
694 }
695 if(c->qid.type & QTDIR){
696 if(rmdir(path) < 0)
697 oserror();
698 }else{
699 if(remove(path) < 0)
700 oserror();
701 }
702 free(path);
703 poperror();
704 }
705
706 Dev fsdevtab = {
707 FsChar,
708 "fs",
709
710 devreset,
711 devinit,
712 devshutdown,
713 fsattach,
714 fswalk,
715 fsstat,
716 fsopen,
717 fscreate,
718 fsclose,
719 fsread,
720 devbread,
721 fswrite,
722 devbwrite,
723 fsremove,
724 fswstat,
725 };
726
727
728 /* Uid management code adapted from u9fs */
729
730 /*
731 * we keep a table by numeric id. by name lookups happen infrequently
732 * while by-number lookups happen once for every directory entry read
733 * and every stat request.
734 */
735 typedef struct User User;
736 struct User {
737 int id;
738 gid_t defaultgid;
739 char *name;
740 User *next;
741 };
742
743
744 static User *utab[64];
745 static User *gtab[64];
746
747 static User*
748 adduser(struct passwd *p)
749 {
750 User *u;
751
752 u = smalloc(sizeof(*u));
753 u->id = p->pw_uid;
754 kstrdup(&u->name, p->pw_name);
755 u->next = utab[p->pw_uid%nelem(utab)];
756 u->defaultgid = p->pw_gid;
757 utab[p->pw_uid%nelem(utab)] = u;
758 return u;
759 }
760
761 static User*
762 addgroup(struct group *g)
763 {
764 User *u;
765
766 u = smalloc(sizeof(*u));
767 u->id = g->gr_gid;
768 kstrdup(&u->name, g->gr_name);
769 u->next = gtab[g->gr_gid%nelem(gtab)];
770 gtab[g->gr_gid%nelem(gtab)] = u;
771 return u;
772 }
773
774 static User*
775 uname2user(char *name)
776 {
777 int i;
778 User *u;
779 struct passwd *p;
780
781 for(i=0; i<nelem(utab); i++)
782 for(u=utab[i]; u; u=u->next)
783 if(strcmp(u->name, name) == 0)
784 return u;
785
786 if((p = getpwnam(name)) == nil)
787 return nil;
788 return adduser(p);
789 }
790
791 static User*
792 uid2user(int id)
793 {
794 User *u;
795 struct passwd *p;
796
797 for(u=utab[id%nelem(utab)]; u; u=u->next)
798 if(u->id == id)
799 return u;
800
801 if((p = getpwuid(id)) == nil)
802 return nil;
803 return adduser(p);
804 }
805
806 static User*
807 gname2user(char *name)
808 {
809 int i;
810 User *u;
811 struct group *g;
812
813 for(i=0; i<nelem(gtab); i++)
814 for(u=gtab[i]; u; u=u->next)
815 if(strcmp(u->name, name) == 0)
816 return u;
817
818 if((g = getgrnam(name)) == nil)
819 return nil;
820 return addgroup(g);
821 }
822
823 static User*
824 gid2user(int id)
825 {
826 User *u;
827 struct group *g;
828
829 for(u=gtab[id%nelem(gtab)]; u; u=u->next)
830 if(u->id == id)
831 return u;
832
833 if((g = getgrgid(id)) == nil)
834 return nil;
835 return addgroup(g);
836 }
837
838 static char*
839 uidtoname(int uid)
840 {
841 User *u;
842
843 u = uid2user(uid);
844 if(u == nil)
845 return "?";
846 return u->name;
847 }
848
849 static char*
850 gidtoname(int gid)
851 {
852 User *u;
853
854 u = gid2user(gid);
855 if(u == nil)
856 return "?";
857 return u->name;
858 }
859
860 static int
861 nametouid(char *name)
862 {
863 User *u;
864
865 u = uname2user(name);
866 if(u == nil)
867 return -1;
868 return u->id;
869 }
870
871 static int
872 nametogid(char *name)
873 {
874 User *u;
875
876 u = gname2user(name);
877 if(u == nil)
878 return -1;
879 return u->id;
880 }
881
882 #if defined(__linux__)
883
884 static vlong
885 disksize(int fd, struct stat *st)
886 {
887 uvlong u64;
888 long l;
889 struct hd_geometry geo;
890
891 memset(&geo, 0, sizeof geo);
892 l = 0;
893 u64 = 0;
894 #ifdef BLKGETSIZE64
895 if(ioctl(fd, BLKGETSIZE64, &u64) >= 0)
896 return u64;
897 #endif
898 if(ioctl(fd, BLKGETSIZE, &l) >= 0)
899 return l*512;
900 if(ioctl(fd, HDIO_GETGEO, &geo) >= 0)
901 return (vlong)geo.heads*geo.sectors*geo.cylinders*512;
902 return 0;
903 }
904
905 #elif defined(__FreeBSD__) && defined(DIOCGMEDIASIZE)
906
907 static vlong
908 disksize(int fd, struct stat *st)
909 {
910 off_t mediasize;
911
912 if(ioctl(fd, DIOCGMEDIASIZE, &mediasize) >= 0)
913 return mediasize;
914 return 0;
915 }
916
917 #elif defined(__APPLE__)
918
919 static vlong
920 disksize(int fd, struct stat *st)
921 {
922 uvlong bc;
923 unsigned int bs;
924
925 bs = 0;
926 bc = 0;
927 ioctl(fd, DKIOCGETBLOCKSIZE, &bs);
928 ioctl(fd, DKIOCGETBLOCKCOUNT, &bc);
929 if(bs >0 && bc > 0)
930 return bc*bs;
931 return 0;
932 }
933
934 #else
935
936 static vlong
937 disksize(int fd, struct stat *st)
938 {
939 return 0;
940 }
941
942 #endif