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