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 uchar 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 if(waserror()){ 779 qunlock(&idl); 780 nexterror(); 781 } 782 r = fsdirconv(c, de->d_name, &stbuf, slop, sizeof(slop), 1); 783 poperror(); 784 qunlock(&idl); 785 if(r <= 0) { 786 FS(c)->offset = n; 787 return 0; 788 } 789 n += r; 790 } 791 FS(c)->offset = offset; 792 } 793 794 if(FS(c)->eod) 795 return 0; 796 797 /* 798 * Take idl on behalf of id2name. Stalling attach, which is a 799 * rare operation, until the readdir completes is probably 800 * preferable to adding lock round-trips. 801 */ 802 qlock(&idl); 803 while(i < count){ 804 de = FS(c)->de; 805 FS(c)->de = nil; 806 if(de == nil) 807 de = readdir(FS(c)->dir); 808 if(de == nil){ 809 FS(c)->eod = 1; 810 break; 811 } 812 813 if(de->d_ino==0 || de->d_name[0]==0 || isdots(de->d_name)) 814 continue; 815 816 strecpy(ep, path+sizeof(path), de->d_name); 817 if(xstat(path, &stbuf) < 0) { 818 fprint(2, "dir: bad path %s\n", path); 819 continue; 820 } 821 r = fsdirconv(c, de->d_name, &stbuf, va+i, count-i, 1); 822 if(r <= 0){ 823 FS(c)->de = de; 824 break; 825 } 826 i += r; 827 FS(c)->offset += r; 828 } 829 qunlock(&idl); 830 return i; 831 } 832 833 static int 834 fsomode(int m) 835 { 836 if(m < 0 || m > 3) 837 error(Ebadarg); 838 return m == 3? 0: m; 839 } 840 841 void 842 setid(char *name, int owner) 843 { 844 User *u; 845 846 if(owner && !iseve()) 847 return; 848 kstrdup(&up->env->user, name); 849 850 qlock(&idl); 851 u = name2user(uidmap, name, newuname); 852 if(u == nil){ 853 qunlock(&idl); 854 up->env->uid = -1; 855 up->env->gid = -1; 856 return; 857 } 858 859 up->env->uid = u->id; 860 up->env->gid = u->gid; 861 qunlock(&idl); 862 } 863 864 static User** 865 hashuser(User** tab, int id) 866 { 867 int i; 868 869 i = (id>>IDSHIFT) ^ id; 870 return &tab[i & IDMASK]; 871 } 872 873 /* 874 * the caller of the following functions must hold QLock idl. 875 */ 876 877 /* 878 * we could keep separate maps of user and group names to Users to 879 * speed this up, but the reverse lookup currently isn't common (ie, change group by wstat and setid) 880 */ 881 static User* 882 name2user(User **tab, char *name, User* (*get)(char*)) 883 { 884 int i; 885 User *u, **h; 886 static User *prevu; 887 static User **prevtab; 888 889 if(prevu != nil && prevtab == tab && strcmp(name, prevu->name) == 0) 890 return prevu; /* it's often the one we've just seen */ 891 892 for(i=0; i<NID; i++) 893 for(u = tab[i]; u != nil; u = u->next) 894 if(strcmp(name, u->name) == 0) { 895 prevtab = tab; 896 prevu = u; 897 return u; 898 } 899 900 u = get(name); 901 if(u == nil) 902 return nil; 903 h = hashuser(tab, u->id); 904 u->next = *h; 905 *h = u; 906 prevtab = tab; 907 prevu = u; 908 return u; 909 } 910 911 static void 912 freeuser(User *u) 913 { 914 if(u != nil){ 915 free(u->name); 916 free(u->mem); 917 free(u); 918 } 919 } 920 921 static User* 922 newuser(int id, int gid, char *name, int nmem) 923 { 924 User *u; 925 926 u = malloc(sizeof(*u)); 927 if(u == nil) 928 return nil; 929 u->name = strdup(name); 930 if(u->name == nil){ 931 free(u); 932 return nil; 933 } 934 u->nmem = nmem; 935 if(nmem){ 936 u->mem = malloc(nmem*sizeof(*u->mem)); 937 if(u->mem == nil){ 938 free(u->name); 939 free(u); 940 return nil; 941 } 942 }else 943 u->mem = nil; 944 u->id = id; 945 u->gid = gid; 946 u->next = nil; 947 return u; 948 } 949 950 static User* 951 newuname(char *name) 952 { 953 struct passwd *p; 954 955 p = getpwnam(name); 956 if(p == nil) 957 return nil; 958 return newuser(p->pw_uid, p->pw_gid, name, 0); 959 } 960 961 static User* 962 newuid(int id) 963 { 964 struct passwd *p; 965 966 p = getpwuid(id); 967 if(p == nil) 968 return nil; 969 return newuser(p->pw_uid, p->pw_gid, p->pw_name, 0); 970 } 971 972 static User* 973 newgroup(struct group *g) 974 { 975 User *u, *gm; 976 int n, o; 977 978 if(g == nil) 979 return nil; 980 for(n=0; g->gr_mem[n] != nil; n++) 981 ; 982 u = newuser(g->gr_gid, g->gr_gid, g->gr_name, n); 983 if(u == nil) 984 return nil; 985 o = 0; 986 for(n=0; g->gr_mem[n] != nil; n++){ 987 gm = name2user(uidmap, g->gr_mem[n], newuname); 988 if(gm != nil) 989 u->mem[o++] = gm->id; 990 /* ignore names that don't map to IDs */ 991 } 992 u->nmem = o; 993 return u; 994 } 995 996 static User* 997 newgid(int id) 998 { 999 return newgroup(getgrgid(id)); 1000 } 1001 1002 static User* 1003 newgname(char *name) 1004 { 1005 return newgroup(getgrnam(name)); 1006 } 1007 1008 static User* 1009 id2user(User **tab, int id, User* (*get)(int)) 1010 { 1011 User *u, **h; 1012 1013 h = hashuser(tab, id); 1014 for(u = *h; u != nil; u = u->next) 1015 if(u->id == id) 1016 return u; 1017 u = get(id); 1018 if(u == nil) 1019 return nil; 1020 u->next = *h; 1021 *h = u; 1022 return u; 1023 } 1024 1025 static int 1026 ingroup(int id, int gid) 1027 { 1028 int i; 1029 User *g; 1030 1031 g = id2user(gidmap, gid, newgid); 1032 if(g == nil || g->mem == nil) 1033 return 0; 1034 for(i = 0; i < g->nmem; i++) 1035 if(g->mem[i] == id) 1036 return 1; 1037 return 0; 1038 } 1039 1040 Dev fsdevtab = { 1041 'U', 1042 "fs", 1043 1044 devinit, 1045 fsattach, 1046 fswalk, 1047 fsstat, 1048 fsopen, 1049 fscreate, 1050 fsclose, 1051 fsread, 1052 devbread, 1053 fswrite, 1054 devbwrite, 1055 fsremove, 1056 fswstat 1057 }; 1058