1 #include <sys/types.h> 2 #include <sys/stat.h> 3 #include <dirent.h> 4 #include <fcntl.h> 5 #include <errno.h> 6 #include <stdio.h> /* for remove, rename */ 7 #include <limits.h> 8 9 #ifndef NAME_MAX 10 # define NAME_MAX 256 11 #endif 12 #include "u.h" 13 #include "lib.h" 14 #include "dat.h" 15 #include "fns.h" 16 #include "error.h" 17 18 19 typedef struct Ufsinfo Ufsinfo; 20 21 enum 22 { 23 NUID = 256, 24 NGID = 256, 25 MAXPATH = 1024, 26 MAXCOMP = 128 27 }; 28 29 struct Ufsinfo 30 { 31 int mode; 32 int fd; 33 int uid; 34 int gid; 35 DIR* dir; 36 ulong offset; 37 QLock oq; 38 char nextname[NAME_MAX]; 39 }; 40 41 char *base = "/"; 42 43 static Qid fsqid(char*, struct stat *); 44 static void fspath(Chan*, char*, char*); 45 static ulong fsdirread(Chan*, uchar*, int, ulong); 46 static int fsomode(int); 47 48 /* clumsy hack, but not worse than the Path stuff in the last one */ 49 static char* 50 uc2name(Chan *c) 51 { 52 char *s; 53 54 if(c->name == nil) 55 return "/"; 56 s = c2name(c); 57 if(s[0]=='#' && s[1]=='U') 58 return s+2; 59 return s; 60 } 61 62 static char* 63 lastelem(Chan *c) 64 { 65 char *s, *t; 66 67 s = uc2name(c); 68 if((t = strrchr(s, '/')) == nil) 69 return s; 70 if(t[1] == 0) 71 return t; 72 return t+1; 73 } 74 75 static Chan* 76 fsattach(char *spec) 77 { 78 Chan *c; 79 struct stat stbuf; 80 static int devno; 81 Ufsinfo *uif; 82 83 if(stat(base, &stbuf) < 0) 84 error(strerror(errno)); 85 86 c = devattach('U', spec); 87 88 uif = mallocz(sizeof(Ufsinfo), 1); 89 uif->mode = stbuf.st_mode; 90 uif->uid = stbuf.st_uid; 91 uif->gid = stbuf.st_gid; 92 93 c->aux = uif; 94 c->dev = devno++; 95 c->qid.type = QTDIR; 96 /*print("fsattach %s\n", c2name(c));*/ 97 98 return c; 99 } 100 101 static Chan* 102 fsclone(Chan *c, Chan *nc) 103 { 104 Ufsinfo *uif; 105 106 uif = mallocz(sizeof(Ufsinfo), 1); 107 *uif = *(Ufsinfo*)c->aux; 108 nc->aux = uif; 109 110 return nc; 111 } 112 113 static int 114 fswalk1(Chan *c, char *name) 115 { 116 struct stat stbuf; 117 char path[MAXPATH]; 118 Ufsinfo *uif; 119 120 fspath(c, name, path); 121 122 /*print("** fs walk '%s' -> %s\n", path, name); */ 123 124 if(stat(path, &stbuf) < 0) 125 return 0; 126 127 uif = c->aux; 128 129 uif->mode = stbuf.st_mode; 130 uif->uid = stbuf.st_uid; 131 uif->gid = stbuf.st_gid; 132 133 c->qid = fsqid(path, &stbuf); 134 135 return 1; 136 } 137 138 extern Cname* addelem(Cname*, char*); 139 140 static Walkqid* 141 fswalk(Chan *c, Chan *nc, char **name, int nname) 142 { 143 int i; 144 Cname *cname; 145 Walkqid *wq; 146 147 if(nc != nil) 148 panic("fswalk: nc != nil"); 149 wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid)); 150 nc = devclone(c); 151 cname = c->name; 152 incref(&cname->ref); 153 154 fsclone(c, nc); 155 wq->clone = nc; 156 for(i=0; i<nname; i++){ 157 nc->name = cname; 158 if(fswalk1(nc, name[i]) == 0) 159 break; 160 cname = addelem(cname, name[i]); 161 wq->qid[i] = nc->qid; 162 } 163 nc->name = nil; 164 cnameclose(cname); 165 if(i != nname){ 166 cclose(nc); 167 wq->clone = nil; 168 } 169 wq->nqid = i; 170 return wq; 171 } 172 173 static int 174 fsstat(Chan *c, uchar *buf, int n) 175 { 176 Dir d; 177 struct stat stbuf; 178 char path[MAXPATH]; 179 180 if(n < BIT16SZ) 181 error(Eshortstat); 182 183 fspath(c, 0, path); 184 if(stat(path, &stbuf) < 0) 185 error(strerror(errno)); 186 187 d.name = lastelem(c); 188 d.uid = "unknown"; 189 d.gid = "unknown"; 190 d.muid = "unknown"; 191 d.qid = c->qid; 192 d.mode = (c->qid.type<<24)|(stbuf.st_mode&0777); 193 d.atime = stbuf.st_atime; 194 d.mtime = stbuf.st_mtime; 195 d.length = stbuf.st_size; 196 d.type = 'U'; 197 d.dev = c->dev; 198 return convD2M(&d, buf, n); 199 } 200 201 static Chan* 202 fsopen(Chan *c, int mode) 203 { 204 char path[MAXPATH]; 205 int m, isdir; 206 Ufsinfo *uif; 207 208 /*print("fsopen %s\n", c2name(c));*/ 209 m = mode & (OTRUNC|3); 210 switch(m) { 211 case 0: 212 break; 213 case 1: 214 case 1|16: 215 break; 216 case 2: 217 case 0|16: 218 case 2|16: 219 break; 220 case 3: 221 break; 222 default: 223 error(Ebadarg); 224 } 225 226 isdir = c->qid.type & QTDIR; 227 228 if(isdir && mode != OREAD) 229 error(Eperm); 230 231 m = fsomode(m & 3); 232 c->mode = openmode(mode); 233 234 uif = c->aux; 235 236 fspath(c, 0, path); 237 if(isdir) { 238 uif->dir = opendir(path); 239 if(uif->dir == 0) 240 error(strerror(errno)); 241 } 242 else { 243 if(mode & OTRUNC) 244 m |= O_TRUNC; 245 uif->fd = open(path, m, 0666); 246 247 if(uif->fd < 0) 248 error(strerror(errno)); 249 } 250 uif->offset = 0; 251 252 c->offset = 0; 253 c->flag |= COPEN; 254 return c; 255 } 256 257 static void 258 fscreate(Chan *c, char *name, int mode, ulong perm) 259 { 260 int fd, m; 261 char path[MAXPATH]; 262 struct stat stbuf; 263 Ufsinfo *uif; 264 265 m = fsomode(mode&3); 266 267 fspath(c, name, path); 268 269 uif = c->aux; 270 271 if(perm & DMDIR) { 272 if(m) 273 error(Eperm); 274 275 if(mkdir(path, perm & 0777) < 0) 276 error(strerror(errno)); 277 278 fd = open(path, 0); 279 if(fd >= 0) { 280 chmod(path, perm & 0777); 281 chown(path, uif->uid, uif->uid); 282 } 283 close(fd); 284 285 uif->dir = opendir(path); 286 if(uif->dir == 0) 287 error(strerror(errno)); 288 } 289 else { 290 fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0666); 291 if(fd >= 0) { 292 if(m != 1) { 293 close(fd); 294 fd = open(path, m); 295 } 296 chmod(path, perm & 0777); 297 chown(path, uif->uid, uif->gid); 298 } 299 if(fd < 0) 300 error(strerror(errno)); 301 uif->fd = fd; 302 } 303 304 if(stat(path, &stbuf) < 0) 305 error(strerror(errno)); 306 c->qid = fsqid(path, &stbuf); 307 c->offset = 0; 308 c->flag |= COPEN; 309 c->mode = openmode(mode); 310 } 311 312 static void 313 fsclose(Chan *c) 314 { 315 Ufsinfo *uif; 316 317 uif = c->aux; 318 319 if(c->flag & COPEN) { 320 if(c->qid.type & QTDIR) 321 closedir(uif->dir); 322 else 323 close(uif->fd); 324 } 325 326 free(uif); 327 } 328 329 static long 330 fsread(Chan *c, void *va, long n, vlong offset) 331 { 332 int fd, r; 333 Ufsinfo *uif; 334 335 /*print("fsread %s\n", c2name(c));*/ 336 if(c->qid.type & QTDIR) 337 return fsdirread(c, va, n, offset); 338 339 uif = c->aux; 340 qlock(&uif->oq); 341 if(waserror()) { 342 qunlock(&uif->oq); 343 nexterror(); 344 } 345 fd = uif->fd; 346 if(uif->offset != offset) { 347 r = lseek(fd, offset, 0); 348 if(r < 0) 349 error(strerror(errno)); 350 uif->offset = offset; 351 } 352 353 n = read(fd, va, n); 354 if(n < 0) 355 error(strerror(errno)); 356 357 uif->offset += n; 358 qunlock(&uif->oq); 359 poperror(); 360 361 return n; 362 } 363 364 static long 365 fswrite(Chan *c, void *va, long n, vlong offset) 366 { 367 int fd, r; 368 Ufsinfo *uif; 369 370 uif = c->aux; 371 372 qlock(&uif->oq); 373 if(waserror()) { 374 qunlock(&uif->oq); 375 nexterror(); 376 } 377 fd = uif->fd; 378 if(uif->offset != offset) { 379 r = lseek(fd, offset, 0); 380 if(r < 0) 381 error(strerror(errno)); 382 uif->offset = offset; 383 } 384 385 n = write(fd, va, n); 386 if(n < 0) 387 error(strerror(errno)); 388 389 uif->offset += n; 390 qunlock(&uif->oq); 391 poperror(); 392 393 return n; 394 } 395 396 static void 397 fsremove(Chan *c) 398 { 399 int n; 400 char path[MAXPATH]; 401 402 fspath(c, 0, path); 403 if(c->qid.type & QTDIR) 404 n = rmdir(path); 405 else 406 n = remove(path); 407 if(n < 0) 408 error(strerror(errno)); 409 } 410 411 int 412 fswstat(Chan *c, uchar *buf, int n) 413 { 414 Dir d; 415 struct stat stbuf; 416 char old[MAXPATH], new[MAXPATH]; 417 char strs[MAXPATH*3], *p; 418 Ufsinfo *uif; 419 420 if(convM2D(buf, n, &d, strs) != n) 421 error(Ebadstat); 422 423 fspath(c, 0, old); 424 if(stat(old, &stbuf) < 0) 425 error(strerror(errno)); 426 427 uif = c->aux; 428 429 if(d.name[0] && strcmp(d.name, lastelem(c)) != 0) { 430 fspath(c, 0, old); 431 strcpy(new, old); 432 p = strrchr(new, '/'); 433 strcpy(p+1, d.name); 434 if(rename(old, new) < 0) 435 error(strerror(errno)); 436 } 437 438 fspath(c, 0, old); 439 if(~d.mode != 0 && (int)(d.mode&0777) != (int)(stbuf.st_mode&0777)) { 440 if(chmod(old, d.mode&0777) < 0) 441 error(strerror(errno)); 442 uif->mode &= ~0777; 443 uif->mode |= d.mode&0777; 444 } 445 /* 446 p = name2pass(gid, d.gid); 447 if(p == 0) 448 error(Eunknown); 449 450 if(p->id != stbuf.st_gid) { 451 if(chown(old, stbuf.st_uid, p->id) < 0) 452 error(strerror(errno)); 453 454 uif->gid = p->id; 455 } 456 */ 457 return n; 458 } 459 460 static Qid 461 fsqid(char *p, struct stat *st) 462 { 463 Qid q; 464 int dev; 465 ulong h; 466 static int nqdev; 467 static uchar *qdev; 468 469 if(qdev == 0) 470 qdev = mallocz(65536U, 1); 471 472 q.type = 0; 473 if((st->st_mode&S_IFMT) == S_IFDIR) 474 q.type = QTDIR; 475 476 dev = st->st_dev & 0xFFFFUL; 477 if(qdev[dev] == 0) 478 qdev[dev] = ++nqdev; 479 480 h = 0; 481 while(*p != '\0') 482 h += *p++ * 13; 483 484 q.path = (vlong)qdev[dev]<<32; 485 q.path |= h; 486 q.vers = st->st_mtime; 487 488 return q; 489 } 490 491 static void 492 fspath(Chan *c, char *ext, char *path) 493 { 494 strcpy(path, base); 495 strcat(path, "/"); 496 strcat(path, uc2name(c)); 497 if(ext){ 498 strcat(path, "/"); 499 strcat(path, ext); 500 } 501 cleanname(path); 502 } 503 504 static int 505 isdots(char *name) 506 { 507 if(name[0] != '.') 508 return 0; 509 if(name[1] == '\0') 510 return 1; 511 if(name[1] != '.') 512 return 0; 513 if(name[2] == '\0') 514 return 1; 515 return 0; 516 } 517 518 static int 519 p9readdir(char *name, Ufsinfo *uif) 520 { 521 struct dirent *de; 522 523 if(uif->nextname[0]){ 524 strcpy(name, uif->nextname); 525 uif->nextname[0] = 0; 526 return 1; 527 } 528 529 de = readdir(uif->dir); 530 if(de == NULL) 531 return 0; 532 533 strcpy(name, de->d_name); 534 return 1; 535 } 536 537 static ulong 538 fsdirread(Chan *c, uchar *va, int count, ulong offset) 539 { 540 int i; 541 Dir d; 542 long n; 543 char de[NAME_MAX]; 544 struct stat stbuf; 545 char path[MAXPATH], dirpath[MAXPATH]; 546 Ufsinfo *uif; 547 548 /*print("fsdirread %s\n", c2name(c));*/ 549 i = 0; 550 uif = c->aux; 551 552 errno = 0; 553 if(uif->offset != offset) { 554 if(offset != 0) 555 error("bad offset in fsdirread"); 556 uif->offset = offset; /* sync offset */ 557 uif->nextname[0] = 0; 558 rewinddir(uif->dir); 559 } 560 561 fspath(c, 0, dirpath); 562 563 while(i+BIT16SZ < count) { 564 if(!p9readdir(de, uif)) 565 break; 566 567 if(de[0]==0 || isdots(de)) 568 continue; 569 570 d.name = de; 571 sprint(path, "%s/%s", dirpath, de); 572 memset(&stbuf, 0, sizeof stbuf); 573 574 if(stat(path, &stbuf) < 0) { 575 /* fprint(2, "dir: bad path %s\n", path); */ 576 /* but continue... probably a bad symlink */ 577 } 578 579 d.uid = "unknown"; 580 d.gid = "unknown"; 581 d.muid = "unknown"; 582 d.qid = fsqid(path, &stbuf); 583 d.mode = (d.qid.type<<24)|(stbuf.st_mode&0777); 584 d.atime = stbuf.st_atime; 585 d.mtime = stbuf.st_mtime; 586 d.length = stbuf.st_size; 587 d.type = 'U'; 588 d.dev = c->dev; 589 n = convD2M(&d, (uchar*)va+i, count-i); 590 if(n == BIT16SZ){ 591 strcpy(uif->nextname, de); 592 break; 593 } 594 i += n; 595 } 596 /*print("got %d\n", i);*/ 597 uif->offset += i; 598 return i; 599 } 600 601 static int 602 fsomode(int m) 603 { 604 switch(m) { 605 case 0: /* OREAD */ 606 case 3: /* OEXEC */ 607 return 0; 608 case 1: /* OWRITE */ 609 return 1; 610 case 2: /* ORDWR */ 611 return 2; 612 } 613 error(Ebadarg); 614 return 0; 615 } 616 617 Dev fsdevtab = { 618 'U', 619 "fs", 620 621 devreset, 622 devinit, 623 devshutdown, 624 fsattach, 625 fswalk, 626 fsstat, 627 fsopen, 628 fscreate, 629 fsclose, 630 fsread, 631 devbread, 632 fswrite, 633 devbwrite, 634 fsremove, 635 fswstat, 636 }; 637