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){ 405 if(errno == ESPIPE || errno == EPIPE){ 406 r = read(FS(c)->fd, va, n); 407 if(r >= 0) 408 return r; 409 } 410 oserror(); 411 } 412 } 413 return r; 414 } 415 416 static long 417 fswrite(Chan *c, void *va, long n, vlong offset) 418 { 419 long r; 420 421 r = pwrite(FS(c)->fd, va, n, offset); 422 if(r < 0 && (errno == ESPIPE || errno == EPIPE)){ 423 r = write(FS(c)->fd, va, n); 424 if(r < 0) 425 oserror(); 426 } 427 return r; 428 } 429 430 static void 431 fswchk(Cname *c) 432 { 433 struct stat stbuf; 434 435 if(stat(c->s, &stbuf) < 0) 436 oserror(); 437 438 if(stbuf.st_uid == up->env->uid) 439 stbuf.st_mode >>= 6; 440 else 441 if(stbuf.st_gid == up->env->gid || ingroup(up->env->uid, stbuf.st_gid)) 442 stbuf.st_mode >>= 3; 443 444 if(stbuf.st_mode & S_IWOTH) 445 return; 446 447 error(Eperm); 448 } 449 450 static void 451 fsremove(Chan *c) 452 { 453 int n; 454 volatile struct { Cname *dir; } dir; 455 456 dir.dir = fswalkpath(FS(c)->name, "..", 1); 457 if(waserror()){ 458 if(dir.dir != nil) 459 cnameclose(dir.dir); 460 fsfree(c); 461 nexterror(); 462 } 463 fswchk(dir.dir); 464 cnameclose(dir.dir); 465 dir.dir = nil; 466 if(c->qid.type & QTDIR) 467 n = rmdir(FS(c)->name->s); 468 else 469 n = remove(FS(c)->name->s); 470 if(n < 0) 471 oserror(); 472 poperror(); 473 fsfree(c); 474 } 475 476 static int 477 fswstat(Chan *c, uchar *buf, int nb) 478 { 479 Dir *d; 480 User *p; 481 volatile struct { Cname *ph; } ph; 482 struct stat stbuf; 483 struct utimbuf utbuf; 484 int tsync; 485 486 if(FS(c)->fd >= 0){ 487 if(fstat(FS(c)->fd, &stbuf) < 0) 488 oserror(); 489 }else{ 490 if(stat(FS(c)->name->s, &stbuf) < 0) 491 oserror(); 492 } 493 d = malloc(sizeof(*d)+nb); 494 if(d == nil) 495 error(Enomem); 496 if(waserror()){ 497 free(d); 498 nexterror(); 499 } 500 tsync = 1; 501 nb = convM2D(buf, nb, d, (char*)&d[1]); 502 if(nb == 0) 503 error(Eshortstat); 504 if(!emptystr(d->name) && strcmp(d->name, fslastelem(FS(c)->name)) != 0) { 505 tsync = 0; 506 validname(d->name, 0); 507 ph.ph = fswalkpath(FS(c)->name, "..", 1); 508 if(waserror()){ 509 cnameclose(ph.ph); 510 nexterror(); 511 } 512 fswchk(ph.ph); 513 ph.ph = fswalkpath(ph.ph, d->name, 0); 514 if(rename(FS(c)->name->s, ph.ph->s) < 0) 515 oserror(); 516 cnameclose(FS(c)->name); 517 poperror(); 518 FS(c)->name = ph.ph; 519 } 520 521 if(d->mode != ~0 && (d->mode&0777) != (stbuf.st_mode&0777)) { 522 tsync = 0; 523 if(up->env->uid != stbuf.st_uid) 524 error(Eowner); 525 if(FS(c)->fd >= 0){ 526 if(fchmod(FS(c)->fd, d->mode&0777) < 0) 527 oserror(); 528 }else{ 529 if(chmod(FS(c)->name->s, d->mode&0777) < 0) 530 oserror(); 531 } 532 FS(c)->mode &= ~0777; 533 FS(c)->mode |= d->mode&0777; 534 } 535 536 if(d->atime != ~0 && d->atime != stbuf.st_atime 537 || d->mtime != ~0 && d->mtime != stbuf.st_mtime) { 538 tsync = 0; 539 if(up->env->uid != stbuf.st_uid) 540 error(Eowner); 541 if(d->mtime != ~0) 542 utbuf.modtime = d->mtime; 543 else 544 utbuf.modtime = stbuf.st_mtime; 545 if(d->atime != ~0) 546 utbuf.actime = d->atime; 547 else 548 utbuf.actime = stbuf.st_atime; 549 if(utime(FS(c)->name->s, &utbuf) < 0) /* TO DO: futimes isn't portable */ 550 oserror(); 551 } 552 553 if(*d->gid){ 554 tsync = 0; 555 qlock(&idl); 556 if(waserror()){ 557 qunlock(&idl); 558 nexterror(); 559 } 560 p = name2user(gidmap, d->gid, newgname); 561 if(p == 0) 562 error(Eunknown); 563 if(p->id != stbuf.st_gid) { 564 if(up->env->uid != stbuf.st_uid) 565 error(Eowner); 566 if(FS(c)->fd >= 0){ 567 if(fchown(FS(c)->fd, stbuf.st_uid, p->id) < 0) 568 oserror(); 569 }else{ 570 if(chown(FS(c)->name->s, stbuf.st_uid, p->id) < 0) 571 oserror(); 572 } 573 FS(c)->gid = p->id; 574 } 575 poperror(); 576 qunlock(&idl); 577 } 578 579 if(d->length != ~(uvlong)0){ 580 tsync = 0; 581 if(FS(c)->fd >= 0){ 582 fsperm(c, 2); 583 if(ftruncate(FS(c)->fd, d->length) < 0) 584 oserror(); 585 }else{ 586 fswchk(FS(c)->name); 587 if(truncate(FS(c)->name->s, d->length) < 0) 588 oserror(); 589 } 590 } 591 592 poperror(); 593 free(d); 594 if(tsync && FS(c)->fd >= 0 && fsync(FS(c)->fd) < 0) 595 oserror(); 596 return nb; 597 } 598 599 #define QDEVBITS 4 /* 16 devices should be plenty */ 600 #define MAXDEV (1<<QDEVBITS) 601 #define QDEVSHIFT (64-QDEVBITS) 602 #define QINOMASK (((uvlong)1<<QDEVSHIFT)-1) 603 #define QPATH(d,i) (((uvlong)(d)<<QDEVSHIFT)|((uvlong)(i)&QINOMASK)) 604 605 static Qid 606 fsqid(struct stat *st) 607 { 608 Qid q; 609 ulong dev; 610 int idev; 611 static int nqdev = 0; 612 static ulong qdev[MAXDEV]; 613 static Lock l; 614 615 q.type = QTFILE; 616 if(S_ISDIR(st->st_mode)) 617 q.type = QTDIR; 618 619 dev = st->st_dev; 620 lock(&l); 621 for(idev = 0; idev < nqdev; idev++) 622 if(qdev[idev] == dev) 623 break; 624 if(idev == nqdev) { 625 if(nqdev == MAXDEV) { 626 unlock(&l); 627 error("too many devices"); 628 } 629 qdev[nqdev++] = dev; 630 } 631 unlock(&l); 632 633 if(0) /* we'll just let it be masked off */ 634 if((uvlong)st->st_ino & ~QINOMASK) 635 error("inode number too large"); 636 637 q.path = QPATH(idev, st->st_ino); 638 q.vers = st->st_mtime; 639 640 return q; 641 } 642 643 static void 644 fspath(Cname *c, char *name, char *path) 645 { 646 int n; 647 648 if(c->len+strlen(name) >= MAXPATH) 649 panic("fspath: name too long"); 650 memmove(path, c->s, c->len); 651 n = c->len; 652 if(path[n-1] != '/') 653 path[n++] = '/'; 654 strcpy(path+n, name); 655 if(isdotdot(name)) 656 cleanname(path); 657 /*print("->%s\n", path);*/ 658 } 659 660 static Cname * 661 fswalkpath(Cname *c, char *name, int dup) 662 { 663 if(dup) 664 c = newcname(c->s); 665 c = addelem(c, name); 666 if(isdotdot(name)) 667 cleancname(c); 668 return c; 669 } 670 671 static char * 672 fslastelem(Cname *c) 673 { 674 char *p; 675 676 p = c->s + c->len; 677 while(p > c->s && p[-1] != '/') 678 p--; 679 return p; 680 } 681 682 static void 683 fsperm(Chan *c, int mask) 684 { 685 int m; 686 687 m = FS(c)->mode; 688 /* 689 print("fsperm: %o %o uuid %d ugid %d cuid %d cgid %d\n", 690 m, mask, up->env->uid, up->env->gid, FS(c)->uid, FS(c)->gid); 691 */ 692 if(FS(c)->uid == up->env->uid) 693 m >>= 6; 694 else 695 if(FS(c)->gid == up->env->gid || ingroup(up->env->uid, FS(c)->gid)) 696 m >>= 3; 697 698 m &= mask; 699 if(m == 0) 700 error(Eperm); 701 } 702 703 static int 704 isdots(char *name) 705 { 706 return name[0] == '.' && (name[1] == '\0' || name[1] == '.' && name[2] == '\0'); 707 } 708 709 static int 710 fsdirconv(Chan *c, char *name, struct stat *s, uchar *va, int nb, int indir) 711 { 712 Dir d; 713 char uidbuf[NUMSIZE], gidbuf[NUMSIZE]; 714 User *u; 715 716 memset(&d, 0, sizeof(d)); 717 d.name = name; 718 u = id2user(uidmap, s->st_uid, newuid); 719 if(u == nil){ 720 snprint(uidbuf, sizeof(uidbuf), "#%lud", (long)s->st_uid); 721 d.uid = uidbuf; 722 }else 723 d.uid = u->name; 724 u = id2user(gidmap, s->st_gid, newgid); 725 if(u == nil){ 726 snprint(gidbuf, sizeof(gidbuf), "#%lud", (long)s->st_gid); 727 d.gid = gidbuf; 728 }else 729 d.gid = u->name; 730 d.muid = ""; 731 d.qid = fsqid(s); 732 d.mode = (d.qid.type<<24)|(s->st_mode&0777); 733 d.atime = s->st_atime; 734 d.mtime = s->st_mtime; 735 d.length = s->st_size; 736 if(d.mode&DMDIR) 737 d.length = 0; 738 d.type = 'U'; 739 d.dev = c->dev; 740 if(indir && sizeD2M(&d) > nb) 741 return -1; /* directory reader needs to know it didn't fit */ 742 return convD2M(&d, va, nb); 743 } 744 745 static long 746 fsdirread(Chan *c, uchar *va, int count, vlong offset) 747 { 748 int i; 749 long n, r; 750 struct stat stbuf; 751 char path[MAXPATH], *ep; 752 struct dirent *de; 753 static char slop[8192]; 754 755 i = 0; 756 fspath(FS(c)->name, "", path); 757 ep = path+strlen(path); 758 if(FS(c)->offset != offset) { 759 seekdir(FS(c)->dir, 0); 760 FS(c)->de = nil; 761 FS(c)->eod = 0; 762 for(n=0; n<offset; ) { 763 de = readdir(FS(c)->dir); 764 if(de == 0) { 765 /* EOF, so stash offset and return 0 */ 766 FS(c)->offset = n; 767 FS(c)->eod = 1; 768 return 0; 769 } 770 if(de->d_ino==0 || de->d_name[0]==0 || isdots(de->d_name)) 771 continue; 772 strecpy(ep, path+sizeof(path), de->d_name); 773 if(xstat(path, &stbuf) < 0) { 774 fprint(2, "dir: bad path %s\n", path); 775 continue; 776 } 777 qlock(&idl); 778 r = fsdirconv(c, de->d_name, &stbuf, slop, sizeof(slop), 1); 779 qunlock(&idl); 780 if(r <= 0) { 781 FS(c)->offset = n; 782 return 0; 783 } 784 n += r; 785 } 786 FS(c)->offset = offset; 787 } 788 789 if(FS(c)->eod) 790 return 0; 791 792 /* 793 * Take idl on behalf of id2name. Stalling attach, which is a 794 * rare operation, until the readdir completes is probably 795 * preferable to adding lock round-trips. 796 */ 797 qlock(&idl); 798 while(i < count){ 799 de = FS(c)->de; 800 FS(c)->de = nil; 801 if(de == nil) 802 de = readdir(FS(c)->dir); 803 if(de == nil){ 804 FS(c)->eod = 1; 805 break; 806 } 807 808 if(de->d_ino==0 || de->d_name[0]==0 || isdots(de->d_name)) 809 continue; 810 811 strecpy(ep, path+sizeof(path), de->d_name); 812 if(xstat(path, &stbuf) < 0) { 813 fprint(2, "dir: bad path %s\n", path); 814 continue; 815 } 816 r = fsdirconv(c, de->d_name, &stbuf, va+i, count-i, 1); 817 if(r <= 0){ 818 FS(c)->de = de; 819 break; 820 } 821 i += r; 822 FS(c)->offset += r; 823 } 824 qunlock(&idl); 825 return i; 826 } 827 828 static int 829 fsomode(int m) 830 { 831 if(m < 0 || m > 3) 832 error(Ebadarg); 833 return m == 3? 0: m; 834 } 835 836 void 837 setid(char *name, int owner) 838 { 839 User *u; 840 841 if(owner && !iseve()) 842 return; 843 kstrdup(&up->env->user, name); 844 845 qlock(&idl); 846 u = name2user(uidmap, name, newuname); 847 if(u == nil){ 848 qunlock(&idl); 849 up->env->uid = -1; 850 up->env->gid = -1; 851 return; 852 } 853 854 up->env->uid = u->id; 855 up->env->gid = u->gid; 856 qunlock(&idl); 857 } 858 859 static User** 860 hashuser(User** tab, int id) 861 { 862 int i; 863 864 i = (id>>IDSHIFT) ^ id; 865 return &tab[i & IDMASK]; 866 } 867 868 /* 869 * the caller of the following functions must hold QLock idl. 870 */ 871 872 /* 873 * we could keep separate maps of user and group names to Users to 874 * speed this up, but the reverse lookup currently isn't common (ie, change group by wstat and setid) 875 */ 876 static User* 877 name2user(User **tab, char *name, User* (*get)(char*)) 878 { 879 int i; 880 User *u, **h; 881 static User *prevu; 882 static User **prevtab; 883 884 if(prevu != nil && prevtab == tab && strcmp(name, prevu->name) == 0) 885 return prevu; /* it's often the one we've just seen */ 886 887 for(i=0; i<NID; i++) 888 for(u = tab[i]; u != nil; u = u->next) 889 if(strcmp(name, u->name) == 0) { 890 prevtab = tab; 891 prevu = u; 892 return u; 893 } 894 895 u = get(name); 896 if(u == nil) 897 return nil; 898 h = hashuser(tab, u->id); 899 u->next = *h; 900 *h = u; 901 prevtab = tab; 902 prevu = u; 903 return u; 904 } 905 906 static void 907 freeuser(User *u) 908 { 909 if(u != nil){ 910 free(u->name); 911 free(u->mem); 912 free(u); 913 } 914 } 915 916 static User* 917 newuser(int id, int gid, char *name, int nmem) 918 { 919 User *u; 920 921 u = malloc(sizeof(*u)); 922 if(u == nil) 923 return nil; 924 u->name = strdup(name); 925 if(u->name == nil){ 926 free(u); 927 return nil; 928 } 929 u->nmem = nmem; 930 if(nmem){ 931 u->mem = malloc(nmem*sizeof(*u->mem)); 932 if(u->mem == nil){ 933 free(u->name); 934 free(u); 935 return nil; 936 } 937 }else 938 u->mem = nil; 939 u->id = id; 940 u->gid = gid; 941 u->next = nil; 942 return u; 943 } 944 945 static User* 946 newuname(char *name) 947 { 948 struct passwd *p; 949 User *u; 950 951 p = getpwnam(name); 952 if(p == nil) 953 return nil; 954 return newuser(p->pw_uid, p->pw_gid, name, 0); 955 } 956 957 static User* 958 newuid(int id) 959 { 960 struct passwd *p; 961 User *u; 962 963 p = getpwuid(id); 964 if(p == nil) 965 return nil; 966 return newuser(p->pw_uid, p->pw_gid, p->pw_name, 0); 967 } 968 969 static User* 970 newgroup(struct group *g) 971 { 972 User *u, *gm; 973 int n, o; 974 975 if(g == nil) 976 return nil; 977 for(n=0; g->gr_mem[n] != nil; n++) 978 ; 979 u = newuser(g->gr_gid, g->gr_gid, g->gr_name, n); 980 if(u == nil) 981 return nil; 982 o = 0; 983 for(n=0; g->gr_mem[n] != nil; n++){ 984 gm = name2user(uidmap, g->gr_mem[n], newuname); 985 if(gm != nil) 986 u->mem[o++] = gm->id; 987 /* ignore names that don't map to IDs */ 988 } 989 u->nmem = o; 990 return u; 991 } 992 993 static User* 994 newgid(int id) 995 { 996 return newgroup(getgrgid(id)); 997 } 998 999 static User* 1000 newgname(char *name) 1001 { 1002 return newgroup(getgrnam(name)); 1003 } 1004 1005 static User* 1006 id2user(User **tab, int id, User* (*get)(int)) 1007 { 1008 int i; 1009 User *u, **h; 1010 1011 h = hashuser(tab, id); 1012 for(u = *h; u != nil; u = u->next) 1013 if(u->id == id) 1014 return u; 1015 u = get(id); 1016 if(u == nil) 1017 return nil; 1018 u->next = *h; 1019 *h = u; 1020 return u; 1021 } 1022 1023 static int 1024 ingroup(int id, int gid) 1025 { 1026 int i; 1027 User *g; 1028 1029 g = id2user(gidmap, gid, newgid); 1030 if(g == nil || g->mem == nil) 1031 return 0; 1032 for(i = 0; i < g->nmem; i++) 1033 if(g->mem[i] == id) 1034 return 1; 1035 return 0; 1036 } 1037 1038 Dev fsdevtab = { 1039 'U', 1040 "fs", 1041 1042 devinit, 1043 fsattach, 1044 fswalk, 1045 fsstat, 1046 fsopen, 1047 fscreate, 1048 fsclose, 1049 fsread, 1050 devbread, 1051 fswrite, 1052 devbwrite, 1053 fsremove, 1054 fswstat 1055 }; 1056