1 /*
2 * Unix file system interface
3 */
4 #define _LARGEFILE64_SOURCE 1
5 #define _FILE_OFFSET_BITS 64
6 #include "dat.h"
7 #include "fns.h"
8 #include "error.h"
9
10 #include <sys/types.h>
11 #include <sys/stat.h>
12 #include <fcntl.h>
13 #include <sys/socket.h>
14 #include <sys/un.h>
15 #include <utime.h>
16 #include <dirent.h>
17 #include <stdio.h>
18 #define __EXTENSIONS__
19 #undef getwd
20 #include <unistd.h>
21 #include <pwd.h>
22 #include <grp.h>
23
24 typedef struct Fsinfo Fsinfo;
25 struct Fsinfo
26 {
27 int uid;
28 int gid;
29 int mode; /* Unix mode */
30 DIR* dir; /* open directory */
31 struct dirent* de; /* directory reading */
32 int fd; /* open files */
33 ulong offset; /* offset when reading directory */
34 int eod; /* end of directory */
35 int issocket;
36 QLock oq; /* mutex for offset */
37 char* spec;
38 Cname* name; /* Unix's name for file */
39 Qid rootqid; /* Plan 9's qid for Inferno's root */
40 };
41
42 #define FS(c) ((Fsinfo*)(c)->aux)
43
44 enum
45 {
46 IDSHIFT = 8,
47 NID = 1 << IDSHIFT,
48 IDMASK = NID - 1,
49 MAXPATH = 1024 /* TO DO: eliminate this */
50 };
51
52 typedef struct User User;
53 struct User
54 {
55 int id; /* might be user or group ID */
56 int gid; /* if it's a user not a group, the group ID (only for setid) */
57 char* name;
58 int nmem;
59 int* mem; /* member array, if nmem != 0 */
60 User* next;
61 };
62
63 char rootdir[MAXROOT] = ROOT;
64
65 static User* uidmap[NID];
66 static User* gidmap[NID];
67 static QLock idl;
68 static User* name2user(User**, char*, User* (*get)(char*));
69 static User* id2user(User**, int, User* (*get)(int));
70 static User* newuid(int);
71 static User* newgid(int);
72 static User* newuname(char*);
73 static User* newgname(char*);
74
75 static Qid fsqid(struct stat *);
76 static void fspath(Cname*, char*, char*);
77 static int fsdirconv(Chan*, char*, char*, struct stat*, uchar*, int, int);
78 static Cname* fswalkpath(Cname*, char*, int);
79 static char* fslastelem(Cname*);
80 static int ingroup(int id, int gid);
81 static void fsperm(Chan*, int);
82 static long fsdirread(Chan*, uchar*, int, vlong);
83 static int fsomode(int);
84 static void fsremove(Chan*);
85 static vlong osdisksize(int); /* defined by including file */
86
87 /*
88 * make invalid symbolic links visible; less confusing, and at least you can then delete them.
89 */
90 static int
xstat(char * f,struct stat * sb)91 xstat(char *f, struct stat *sb)
92 {
93 if(stat(f, sb) >= 0)
94 return 0;
95 /* could possibly generate ->name as rob once suggested */
96 return lstat(f, sb);
97 }
98
99 static void
fsfree(Chan * c)100 fsfree(Chan *c)
101 {
102 cnameclose(FS(c)->name);
103 free(FS(c));
104 }
105
106 Chan*
fsattach(char * spec)107 fsattach(char *spec)
108 {
109 Chan *c;
110 struct stat st;
111 static int devno;
112 static Lock l;
113
114 if(!emptystr(spec) && strcmp(spec, "*") != 0)
115 error(Ebadspec);
116 if(stat(rootdir, &st) < 0)
117 oserror();
118 if(!S_ISDIR(st.st_mode))
119 error(Enotdir);
120
121 c = devattach('U', spec);
122 c->qid = fsqid(&st);
123 c->aux = smalloc(sizeof(Fsinfo));
124 FS(c)->dir = nil;
125 FS(c)->de = nil;
126 FS(c)->fd = -1;
127 FS(c)->issocket = 0;
128 FS(c)->gid = st.st_gid;
129 FS(c)->uid = st.st_uid;
130 FS(c)->mode = st.st_mode;
131 lock(&l);
132 c->dev = devno++;
133 unlock(&l);
134 if(!emptystr(spec)){
135 FS(c)->spec = "/";
136 FS(c)->name = newcname(FS(c)->spec);
137 }else
138 FS(c)->name = newcname(rootdir);
139 FS(c)->rootqid = c->qid;
140
141 return c;
142 }
143
144 Walkqid*
fswalk(Chan * c,Chan * nc,char ** name,int nname)145 fswalk(Chan *c, Chan *nc, char **name, int nname)
146 {
147 int j;
148 volatile int alloc;
149 Walkqid *wq;
150 struct stat st;
151 char *n;
152 Cname *next;
153 Cname *volatile current;
154 Qid rootqid;
155
156 if(nname > 0)
157 isdir(c);
158
159 alloc = 0;
160 current = nil;
161 wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid));
162 if(waserror()){
163 if(alloc && wq->clone != nil)
164 cclose(wq->clone);
165 cnameclose(current);
166 free(wq);
167 return nil;
168 }
169 if(nc == nil){
170 nc = devclone(c);
171 nc->type = 0;
172 alloc = 1;
173 }
174 wq->clone = nc;
175 rootqid = FS(c)->rootqid;
176 current = FS(c)->name;
177 if(current != nil)
178 incref(¤t->r);
179 for(j = 0; j < nname; j++){
180 if(!(nc->qid.type&QTDIR)){
181 if(j==0)
182 error(Enotdir);
183 break;
184 }
185 n = name[j];
186 if(strcmp(n, ".") != 0 && !(isdotdot(n) && nc->qid.path == rootqid.path)){
187 next = current;
188 incref(&next->r);
189 next = addelem(current, n);
190 //print("** ufs walk '%s' -> %s [%s]\n", current->s, n, next->s);
191 if(xstat(next->s, &st) < 0){
192 cnameclose(next);
193 if(j == 0)
194 error(Enonexist);
195 strcpy(up->env->errstr, Enonexist);
196 break;
197 }
198 nc->qid = fsqid(&st);
199 cnameclose(current);
200 current = next;
201 }
202 wq->qid[wq->nqid++] = nc->qid;
203 }
204 poperror();
205 if(wq->nqid < nname){
206 cnameclose(current);
207 if(alloc)
208 cclose(wq->clone);
209 wq->clone = nil;
210 }else if(wq->clone){
211 nc->aux = smalloc(sizeof(Fsinfo));
212 nc->type = c->type;
213 if(nname > 0) {
214 FS(nc)->gid = st.st_gid;
215 FS(nc)->uid = st.st_uid;
216 FS(nc)->mode = st.st_mode;
217 FS(nc)->issocket = S_ISSOCK(st.st_mode);
218 } else {
219 FS(nc)->gid = FS(c)->gid;
220 FS(nc)->uid = FS(c)->uid;
221 FS(nc)->mode = FS(c)->mode;
222 FS(nc)->issocket = FS(c)->issocket;
223 }
224 FS(nc)->name = current;
225 FS(nc)->spec = FS(c)->spec;
226 FS(nc)->rootqid = rootqid;
227 FS(nc)->fd = -1;
228 FS(nc)->dir = nil;
229 FS(nc)->de = nil;
230 }
231 return wq;
232 }
233
234 static int
fsstat(Chan * c,uchar * dp,int n)235 fsstat(Chan *c, uchar *dp, int n)
236 {
237 struct stat st;
238 char *p;
239
240 if(FS(c)->fd >= 0){
241 if(fstat(FS(c)->fd, &st) < 0)
242 oserror();
243 }else{
244 if(xstat(FS(c)->name->s, &st) < 0)
245 oserror();
246 }
247 p = fslastelem(FS(c)->name);
248 if(*p == 0)
249 p = "/";
250 qlock(&idl);
251 n = fsdirconv(c, FS(c)->name->s, p, &st, dp, n, 0);
252 qunlock(&idl);
253 return n;
254 }
255
256 static int
opensocket(char * path)257 opensocket(char *path)
258 {
259 int fd;
260 struct sockaddr_un su;
261
262 memset(&su, 0, sizeof su);
263 su.sun_family = AF_UNIX;
264 if(strlen(path)+1 > sizeof su.sun_path)
265 error("unix socket name too long");
266 strcpy(su.sun_path, path);
267 if((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
268 return -1;
269 if(connect(fd, (struct sockaddr*)&su, sizeof su) >= 0)
270 return fd;
271 close(fd);
272 if((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0)
273 return -1;
274 if(connect(fd, (struct sockaddr*)&su, sizeof su) >= 0)
275 return fd;
276 close(fd);
277 return -1;
278 }
279
280 static Chan*
fsopen(Chan * c,int mode)281 fsopen(Chan *c, int mode)
282 {
283 int m, isdir;
284
285 m = mode & (OTRUNC|3);
286 switch(m) {
287 case 0:
288 fsperm(c, 4);
289 break;
290 case 1:
291 case 1|16:
292 fsperm(c, 2);
293 break;
294 case 2:
295 case 0|16:
296 case 2|16:
297 fsperm(c, 4);
298 fsperm(c, 2);
299 break;
300 case 3:
301 fsperm(c, 1);
302 break;
303 default:
304 error(Ebadarg);
305 }
306
307 isdir = c->qid.type & QTDIR;
308
309 if(isdir && mode != OREAD)
310 error(Eperm);
311
312 m = fsomode(m & 3);
313 c->mode = openmode(mode);
314
315 if(isdir) {
316 FS(c)->dir = opendir(FS(c)->name->s);
317 if(FS(c)->dir == nil)
318 oserror();
319 FS(c)->eod = 0;
320 }
321 else {
322 if(!FS(c)->issocket){
323 if(mode & OTRUNC)
324 m |= O_TRUNC;
325 FS(c)->fd = open(FS(c)->name->s, m, 0666);
326 }else
327 FS(c)->fd = opensocket(FS(c)->name->s);
328 if(FS(c)->fd < 0)
329 oserror();
330 }
331
332 c->offset = 0;
333 FS(c)->offset = 0;
334 c->flag |= COPEN;
335 return c;
336 }
337
338 static void
fscreate(Chan * c,char * name,int mode,ulong perm)339 fscreate(Chan *c, char *name, int mode, ulong perm)
340 {
341 int fd, m, o;
342 struct stat st;
343 Cname *n;
344
345 fsperm(c, 2);
346
347 m = fsomode(mode&3);
348 openmode(mode); /* get the errors out of the way */
349
350 if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
351 error(Efilename);
352 n = fswalkpath(FS(c)->name, name, 1);
353 if(waserror()){
354 cnameclose(n);
355 nexterror();
356 }
357 if(perm & DMDIR) {
358 if(m)
359 error(Eperm);
360
361 perm &= ~0777 | (FS(c)->mode & 0777);
362 if(mkdir(n->s, perm) < 0)
363 oserror();
364
365 fd = open(n->s, 0);
366 if(fd < 0)
367 oserror();
368 fchmod(fd, perm);
369 fchown(fd, up->env->uid, FS(c)->gid);
370 if(fstat(fd, &st) <0){
371 close(fd);
372 oserror();
373 }
374 close(fd);
375 FS(c)->dir = opendir(n->s);
376 if(FS(c)->dir == nil)
377 oserror();
378 FS(c)->eod = 0;
379 } else {
380 o = (O_CREAT | O_EXCL) | (mode&3);
381 if(mode & OTRUNC)
382 o |= O_TRUNC;
383 perm &= ~0666 | (FS(c)->mode & 0666);
384 fd = open(n->s, o, perm);
385 if(fd < 0)
386 oserror();
387 fchmod(fd, perm);
388 fchown(fd, up->env->uid, FS(c)->gid);
389 if(fstat(fd, &st) < 0){
390 close(fd);
391 oserror();
392 }
393 FS(c)->fd = fd;
394 }
395 cnameclose(FS(c)->name);
396 FS(c)->name = n;
397 poperror();
398
399 c->qid = fsqid(&st);
400 FS(c)->gid = st.st_gid;
401 FS(c)->uid = st.st_uid;
402 FS(c)->mode = st.st_mode;
403 c->mode = openmode(mode);
404 c->offset = 0;
405 FS(c)->offset = 0;
406 FS(c)->issocket = 0;
407 c->flag |= COPEN;
408 }
409
410 static void
fsclose(Chan * c)411 fsclose(Chan *c)
412 {
413 if((c->flag & COPEN) != 0){
414 if(c->qid.type & QTDIR)
415 closedir(FS(c)->dir);
416 else
417 close(FS(c)->fd);
418 }
419 if(c->flag & CRCLOSE) {
420 if(!waserror()) {
421 fsremove(c);
422 poperror();
423 }
424 return;
425 }
426 fsfree(c);
427 }
428
429 static long
fsread(Chan * c,void * va,long n,vlong offset)430 fsread(Chan *c, void *va, long n, vlong offset)
431 {
432 long r;
433
434 if(c->qid.type & QTDIR){
435 qlock(&FS(c)->oq);
436 if(waserror()) {
437 qunlock(&FS(c)->oq);
438 nexterror();
439 }
440 r = fsdirread(c, va, n, offset);
441 poperror();
442 qunlock(&FS(c)->oq);
443 }else{
444 if(!FS(c)->issocket){
445 r = pread(FS(c)->fd, va, n, offset);
446 if(r >= 0)
447 return r;
448 if(errno != ESPIPE && errno != EPIPE)
449 oserror();
450 }
451 r = read(FS(c)->fd, va, n);
452 if(r < 0)
453 oserror();
454 }
455 return r;
456 }
457
458 static long
fswrite(Chan * c,void * va,long n,vlong offset)459 fswrite(Chan *c, void *va, long n, vlong offset)
460 {
461 long r;
462
463 if(!FS(c)->issocket){
464 r = pwrite(FS(c)->fd, va, n, offset);
465 if(r >= 0)
466 return r;
467 if(errno != ESPIPE && errno != EPIPE)
468 oserror();
469 }
470 r = write(FS(c)->fd, va, n);
471 if(r < 0)
472 oserror();
473 return r;
474 }
475
476 static void
fswchk(Cname * c)477 fswchk(Cname *c)
478 {
479 struct stat st;
480
481 if(stat(c->s, &st) < 0)
482 oserror();
483
484 if(st.st_uid == up->env->uid)
485 st.st_mode >>= 6;
486 else if(st.st_gid == up->env->gid || ingroup(up->env->uid, st.st_gid))
487 st.st_mode >>= 3;
488
489 if(st.st_mode & S_IWOTH)
490 return;
491
492 error(Eperm);
493 }
494
495 static void
fsremove(Chan * c)496 fsremove(Chan *c)
497 {
498 int n;
499 Cname *volatile dir;
500
501 if(waserror()){
502 fsfree(c);
503 nexterror();
504 }
505 dir = fswalkpath(FS(c)->name, "..", 1);
506 if(waserror()){
507 cnameclose(dir);
508 nexterror();
509 }
510 fswchk(dir);
511 cnameclose(dir);
512 poperror();
513 if(c->qid.type & QTDIR)
514 n = rmdir(FS(c)->name->s);
515 else
516 n = remove(FS(c)->name->s);
517 if(n < 0)
518 oserror();
519 poperror();
520 fsfree(c);
521 }
522
523 static int
fswstat(Chan * c,uchar * buf,int nb)524 fswstat(Chan *c, uchar *buf, int nb)
525 {
526 Dir *d;
527 User *p;
528 Cname *volatile ph;
529 struct stat st;
530 struct utimbuf utbuf;
531 int tsync;
532
533 if(FS(c)->fd >= 0){
534 if(fstat(FS(c)->fd, &st) < 0)
535 oserror();
536 }else{
537 if(stat(FS(c)->name->s, &st) < 0)
538 oserror();
539 }
540 d = malloc(sizeof(*d)+nb);
541 if(d == nil)
542 error(Enomem);
543 if(waserror()){
544 free(d);
545 nexterror();
546 }
547 tsync = 1;
548 nb = convM2D(buf, nb, d, (char*)&d[1]);
549 if(nb == 0)
550 error(Eshortstat);
551 if(!emptystr(d->name) && strcmp(d->name, fslastelem(FS(c)->name)) != 0) {
552 tsync = 0;
553 validname(d->name, 0);
554 ph = fswalkpath(FS(c)->name, "..", 1);
555 if(waserror()){
556 cnameclose(ph);
557 nexterror();
558 }
559 fswchk(ph);
560 ph = fswalkpath(ph, d->name, 0);
561 if(rename(FS(c)->name->s, ph->s) < 0)
562 oserror();
563 cnameclose(FS(c)->name);
564 poperror();
565 FS(c)->name = ph;
566 }
567
568 if(d->mode != ~0 && (d->mode&0777) != (st.st_mode&0777)) {
569 tsync = 0;
570 if(up->env->uid != st.st_uid)
571 error(Eowner);
572 if(FS(c)->fd >= 0){
573 if(fchmod(FS(c)->fd, d->mode&0777) < 0)
574 oserror();
575 }else{
576 if(chmod(FS(c)->name->s, d->mode&0777) < 0)
577 oserror();
578 }
579 FS(c)->mode &= ~0777;
580 FS(c)->mode |= d->mode&0777;
581 }
582
583 if(d->atime != ~0 && d->atime != st.st_atime ||
584 d->mtime != ~0 && d->mtime != st.st_mtime) {
585 tsync = 0;
586 if(up->env->uid != st.st_uid)
587 error(Eowner);
588 if(d->mtime != ~0)
589 utbuf.modtime = d->mtime;
590 else
591 utbuf.modtime = st.st_mtime;
592 if(d->atime != ~0)
593 utbuf.actime = d->atime;
594 else
595 utbuf.actime = st.st_atime;
596 if(utime(FS(c)->name->s, &utbuf) < 0) /* TO DO: futimes isn't portable */
597 oserror();
598 }
599
600 if(*d->gid){
601 tsync = 0;
602 qlock(&idl);
603 if(waserror()){
604 qunlock(&idl);
605 nexterror();
606 }
607 p = name2user(gidmap, d->gid, newgname);
608 if(p == 0)
609 error(Eunknown);
610 if(p->id != st.st_gid) {
611 if(up->env->uid != st.st_uid)
612 error(Eowner);
613 if(FS(c)->fd >= 0){
614 if(fchown(FS(c)->fd, st.st_uid, p->id) < 0)
615 oserror();
616 }else{
617 if(chown(FS(c)->name->s, st.st_uid, p->id) < 0)
618 oserror();
619 }
620 FS(c)->gid = p->id;
621 }
622 poperror();
623 qunlock(&idl);
624 }
625
626 if(d->length != ~(uvlong)0){
627 tsync = 0;
628 if(FS(c)->fd >= 0){
629 fsperm(c, 2);
630 if(ftruncate(FS(c)->fd, d->length) < 0)
631 oserror();
632 }else{
633 fswchk(FS(c)->name);
634 if(truncate(FS(c)->name->s, d->length) < 0)
635 oserror();
636 }
637 }
638
639 poperror();
640 free(d);
641 if(tsync && FS(c)->fd >= 0 && fsync(FS(c)->fd) < 0)
642 oserror();
643 return nb;
644 }
645
646 static Qid
fsqid(struct stat * st)647 fsqid(struct stat *st)
648 {
649 Qid q;
650 u16int dev;
651
652 q.type = QTFILE;
653 if(S_ISDIR(st->st_mode))
654 q.type = QTDIR;
655
656 dev = (u16int)st->st_dev;
657 if(dev & 0x8000){
658 static int aware = 1;
659 if(aware==0){
660 aware = 1;
661 fprint(2, "fs: fsqid: top-bit dev: %#4.4ux\n", dev);
662 }
663 dev ^= 0x8080;
664 }
665
666 q.path = (uvlong)dev<<48;
667 q.path ^= st->st_ino;
668 q.vers = st->st_mtime;
669
670 return q;
671 }
672
673 static void
fspath(Cname * c,char * name,char * path)674 fspath(Cname *c, char *name, char *path)
675 {
676 int n;
677
678 if(c->len+strlen(name) >= MAXPATH)
679 panic("fspath: name too long");
680 memmove(path, c->s, c->len);
681 n = c->len;
682 if(path[n-1] != '/')
683 path[n++] = '/';
684 strcpy(path+n, name);
685 if(isdotdot(name))
686 cleanname(path);
687 /*print("->%s\n", path);*/
688 }
689
690 static Cname *
fswalkpath(Cname * c,char * name,int dup)691 fswalkpath(Cname *c, char *name, int dup)
692 {
693 if(dup)
694 c = newcname(c->s);
695 c = addelem(c, name);
696 if(isdotdot(name))
697 cleancname(c);
698 return c;
699 }
700
701 static char *
fslastelem(Cname * c)702 fslastelem(Cname *c)
703 {
704 char *p;
705
706 p = c->s + c->len;
707 while(p > c->s && p[-1] != '/')
708 p--;
709 return p;
710 }
711
712 static void
fsperm(Chan * c,int mask)713 fsperm(Chan *c, int mask)
714 {
715 int m;
716
717 m = FS(c)->mode;
718 /*
719 print("fsperm: %o %o uuid %d ugid %d cuid %d cgid %d\n",
720 m, mask, up->env->uid, up->env->gid, FS(c)->uid, FS(c)->gid);
721 */
722 if(FS(c)->uid == up->env->uid)
723 m >>= 6;
724 else if(FS(c)->gid == up->env->gid || ingroup(up->env->uid, FS(c)->gid))
725 m >>= 3;
726
727 m &= mask;
728 if(m == 0)
729 error(Eperm);
730 }
731
732 static int
isdots(char * name)733 isdots(char *name)
734 {
735 return name[0] == '.' && (name[1] == '\0' || name[1] == '.' && name[2] == '\0');
736 }
737
738 static int
fsdirconv(Chan * c,char * path,char * name,struct stat * s,uchar * va,int nb,int indir)739 fsdirconv(Chan *c, char *path, char *name, struct stat *s, uchar *va, int nb, int indir)
740 {
741 Dir d;
742 char uidbuf[NUMSIZE], gidbuf[NUMSIZE];
743 User *u;
744 int fd;
745
746 memset(&d, 0, sizeof(d));
747 d.name = name;
748 u = id2user(uidmap, s->st_uid, newuid);
749 if(u == nil){
750 snprint(uidbuf, sizeof(uidbuf), "#%lud", (long)s->st_uid);
751 d.uid = uidbuf;
752 }else
753 d.uid = u->name;
754 u = id2user(gidmap, s->st_gid, newgid);
755 if(u == nil){
756 snprint(gidbuf, sizeof(gidbuf), "#%lud", (long)s->st_gid);
757 d.gid = gidbuf;
758 }else
759 d.gid = u->name;
760 d.muid = "";
761 d.qid = fsqid(s);
762 d.mode = (d.qid.type<<24)|(s->st_mode&0777);
763 d.atime = s->st_atime;
764 d.mtime = s->st_mtime;
765 d.length = s->st_size;
766 if(d.mode&DMDIR)
767 d.length = 0;
768 else if(S_ISBLK(s->st_mode) && s->st_size == 0){
769 fd = open(path, O_RDONLY);
770 if(fd >= 0){
771 d.length = osdisksize(fd);
772 close(fd);
773 }
774 }
775 d.type = 'U';
776 d.dev = c->dev;
777 if(indir && sizeD2M(&d) > nb)
778 return -1; /* directory reader needs to know it didn't fit */
779 return convD2M(&d, va, nb);
780 }
781
782 static long
fsdirread(Chan * c,uchar * va,int count,vlong offset)783 fsdirread(Chan *c, uchar *va, int count, vlong offset)
784 {
785 int i;
786 long n, r;
787 struct stat st;
788 char path[MAXPATH], *ep;
789 struct dirent *de;
790 static uchar slop[8192];
791
792 i = 0;
793 fspath(FS(c)->name, "", path);
794 ep = path+strlen(path);
795 if(FS(c)->offset != offset) {
796 seekdir(FS(c)->dir, 0);
797 FS(c)->de = nil;
798 FS(c)->eod = 0;
799 for(n=0; n<offset; ) {
800 de = readdir(FS(c)->dir);
801 if(de == 0) {
802 /* EOF, so stash offset and return 0 */
803 FS(c)->offset = n;
804 FS(c)->eod = 1;
805 return 0;
806 }
807 if(de->d_ino==0 || de->d_name[0]==0 || isdots(de->d_name))
808 continue;
809 strecpy(ep, path+sizeof(path), de->d_name);
810 if(xstat(path, &st) < 0) {
811 fprint(2, "dir: bad path %s\n", path);
812 continue;
813 }
814 qlock(&idl);
815 if(waserror()){
816 qunlock(&idl);
817 nexterror();
818 }
819 r = fsdirconv(c, path, de->d_name, &st, slop, sizeof(slop), 1);
820 poperror();
821 qunlock(&idl);
822 if(r <= 0) {
823 FS(c)->offset = n;
824 return 0;
825 }
826 n += r;
827 }
828 FS(c)->offset = offset;
829 }
830
831 if(FS(c)->eod)
832 return 0;
833
834 /*
835 * Take idl on behalf of id2name. Stalling attach, which is a
836 * rare operation, until the readdir completes is probably
837 * preferable to adding lock round-trips.
838 */
839 qlock(&idl);
840 while(i < count){
841 de = FS(c)->de;
842 FS(c)->de = nil;
843 if(de == nil)
844 de = readdir(FS(c)->dir);
845 if(de == nil){
846 FS(c)->eod = 1;
847 break;
848 }
849
850 if(de->d_ino==0 || de->d_name[0]==0 || isdots(de->d_name))
851 continue;
852
853 strecpy(ep, path+sizeof(path), de->d_name);
854 if(xstat(path, &st) < 0) {
855 fprint(2, "dir: bad path %s\n", path);
856 continue;
857 }
858 r = fsdirconv(c, path, de->d_name, &st, va+i, count-i, 1);
859 if(r <= 0){
860 FS(c)->de = de;
861 break;
862 }
863 i += r;
864 FS(c)->offset += r;
865 }
866 qunlock(&idl);
867 return i;
868 }
869
870 static int
fsomode(int m)871 fsomode(int m)
872 {
873 if(m < 0 || m > 3)
874 error(Ebadarg);
875 return m == 3? 0: m;
876 }
877
878 void
setid(char * name,int owner)879 setid(char *name, int owner)
880 {
881 User *u;
882
883 if(owner && !iseve())
884 return;
885 kstrdup(&up->env->user, name);
886
887 qlock(&idl);
888 u = name2user(uidmap, name, newuname);
889 if(u == nil){
890 qunlock(&idl);
891 up->env->uid = -1;
892 up->env->gid = -1;
893 return;
894 }
895
896 up->env->uid = u->id;
897 up->env->gid = u->gid;
898 qunlock(&idl);
899 }
900
901 static User**
hashuser(User ** tab,int id)902 hashuser(User** tab, int id)
903 {
904 int i;
905
906 i = (id>>IDSHIFT) ^ id;
907 return &tab[i & IDMASK];
908 }
909
910 /*
911 * the caller of the following functions must hold QLock idl.
912 */
913
914 /*
915 * we could keep separate maps of user and group names to Users to
916 * speed this up, but the reverse lookup currently isn't common (ie, change group by wstat and setid)
917 */
918 static User*
name2user(User ** tab,char * name,User * (* get)(char *))919 name2user(User **tab, char *name, User* (*get)(char*))
920 {
921 int i;
922 User *u, **h;
923 static User *prevu;
924 static User **prevtab;
925
926 if(prevu != nil && prevtab == tab && strcmp(name, prevu->name) == 0)
927 return prevu; /* it's often the one we've just seen */
928
929 for(i=0; i<NID; i++)
930 for(u = tab[i]; u != nil; u = u->next)
931 if(strcmp(name, u->name) == 0) {
932 prevtab = tab;
933 prevu = u;
934 return u;
935 }
936
937 u = get(name);
938 if(u == nil)
939 return nil;
940 h = hashuser(tab, u->id);
941 u->next = *h;
942 *h = u;
943 prevtab = tab;
944 prevu = u;
945 return u;
946 }
947
948 static void
freeuser(User * u)949 freeuser(User *u)
950 {
951 if(u != nil){
952 free(u->name);
953 free(u->mem);
954 free(u);
955 }
956 }
957
958 static User*
newuser(int id,int gid,char * name,int nmem)959 newuser(int id, int gid, char *name, int nmem)
960 {
961 User *u;
962
963 u = malloc(sizeof(*u));
964 if(u == nil)
965 return nil;
966 u->name = strdup(name);
967 if(u->name == nil){
968 free(u);
969 return nil;
970 }
971 u->nmem = nmem;
972 if(nmem){
973 u->mem = malloc(nmem*sizeof(*u->mem));
974 if(u->mem == nil){
975 free(u->name);
976 free(u);
977 return nil;
978 }
979 }else
980 u->mem = nil;
981 u->id = id;
982 u->gid = gid;
983 u->next = nil;
984 return u;
985 }
986
987 static User*
newuname(char * name)988 newuname(char *name)
989 {
990 struct passwd *p;
991
992 p = getpwnam(name);
993 if(p == nil)
994 return nil;
995 return newuser(p->pw_uid, p->pw_gid, name, 0);
996 }
997
998 static User*
newuid(int id)999 newuid(int id)
1000 {
1001 struct passwd *p;
1002
1003 p = getpwuid(id);
1004 if(p == nil)
1005 return nil;
1006 return newuser(p->pw_uid, p->pw_gid, p->pw_name, 0);
1007 }
1008
1009 static User*
newgroup(struct group * g)1010 newgroup(struct group *g)
1011 {
1012 User *u, *gm;
1013 int n, o;
1014
1015 if(g == nil)
1016 return nil;
1017 for(n=0; g->gr_mem[n] != nil; n++)
1018 ;
1019 u = newuser(g->gr_gid, g->gr_gid, g->gr_name, n);
1020 if(u == nil)
1021 return nil;
1022 o = 0;
1023 for(n=0; g->gr_mem[n] != nil; n++){
1024 gm = name2user(uidmap, g->gr_mem[n], newuname);
1025 if(gm != nil)
1026 u->mem[o++] = gm->id;
1027 /* ignore names that don't map to IDs */
1028 }
1029 u->nmem = o;
1030 return u;
1031 }
1032
1033 static User*
newgid(int id)1034 newgid(int id)
1035 {
1036 return newgroup(getgrgid(id));
1037 }
1038
1039 static User*
newgname(char * name)1040 newgname(char *name)
1041 {
1042 return newgroup(getgrnam(name));
1043 }
1044
1045 static User*
id2user(User ** tab,int id,User * (* get)(int))1046 id2user(User **tab, int id, User* (*get)(int))
1047 {
1048 User *u, **h;
1049
1050 h = hashuser(tab, id);
1051 for(u = *h; u != nil; u = u->next)
1052 if(u->id == id)
1053 return u;
1054 u = get(id);
1055 if(u == nil)
1056 return nil;
1057 u->next = *h;
1058 *h = u;
1059 return u;
1060 }
1061
1062 static int
ingroup(int id,int gid)1063 ingroup(int id, int gid)
1064 {
1065 int i;
1066 User *g;
1067
1068 g = id2user(gidmap, gid, newgid);
1069 if(g == nil || g->mem == nil)
1070 return 0;
1071 for(i = 0; i < g->nmem; i++)
1072 if(g->mem[i] == id)
1073 return 1;
1074 return 0;
1075 }
1076
1077 Dev fsdevtab = {
1078 'U',
1079 "fs",
1080
1081 devinit,
1082 fsattach,
1083 fswalk,
1084 fsstat,
1085 fsopen,
1086 fscreate,
1087 fsclose,
1088 fsread,
1089 devbread,
1090 fswrite,
1091 devbwrite,
1092 fsremove,
1093 fswstat
1094 };
1095