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