1 #include "u.h" 2 #include <sys/types.h> 3 #include <sys/stat.h> 4 #include <dirent.h> 5 #include <fcntl.h> 6 #include <errno.h> 7 #include <stdio.h> /* for remove, rename */ 8 #include <limits.h> 9 10 #ifndef NAME_MAX 11 # define NAME_MAX 256 12 #endif 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 vlong 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 = cname; 164 if(i != nname){ 165 cclose(nc); 166 wq->clone = nil; 167 } 168 wq->nqid = i; 169 return wq; 170 } 171 172 static int 173 fsstat(Chan *c, uchar *buf, int n) 174 { 175 Dir d; 176 struct stat stbuf; 177 char path[MAXPATH]; 178 179 if(n < BIT16SZ) 180 error(Eshortstat); 181 182 fspath(c, 0, path); 183 if(stat(path, &stbuf) < 0) 184 error(strerror(errno)); 185 186 d.name = lastelem(c); 187 d.uid = "unknown"; 188 d.gid = "unknown"; 189 d.muid = "unknown"; 190 d.qid = c->qid; 191 d.mode = (c->qid.type<<24)|(stbuf.st_mode&0777); 192 d.atime = stbuf.st_atime; 193 d.mtime = stbuf.st_mtime; 194 d.length = stbuf.st_size; 195 d.type = 'U'; 196 d.dev = c->dev; 197 return convD2M(&d, buf, n); 198 } 199 200 static Chan* 201 fsopen(Chan *c, int mode) 202 { 203 char path[MAXPATH]; 204 int m, isdir; 205 Ufsinfo *uif; 206 207 /*print("fsopen %s\n", c2name(c));*/ 208 m = mode & (OTRUNC|3); 209 switch(m) { 210 case 0: 211 break; 212 case 1: 213 case 1|16: 214 break; 215 case 2: 216 case 0|16: 217 case 2|16: 218 break; 219 case 3: 220 break; 221 default: 222 error(Ebadarg); 223 } 224 225 isdir = c->qid.type & QTDIR; 226 227 if(isdir && mode != OREAD) 228 error(Eperm); 229 230 m = fsomode(m & 3); 231 c->mode = openmode(mode); 232 233 uif = c->aux; 234 235 fspath(c, 0, path); 236 if(isdir) { 237 uif->dir = opendir(path); 238 if(uif->dir == 0) 239 error(strerror(errno)); 240 } 241 else { 242 if(mode & OTRUNC) 243 m |= O_TRUNC; 244 uif->fd = open(path, m, 0666); 245 246 if(uif->fd < 0) 247 error(strerror(errno)); 248 } 249 uif->offset = 0; 250 251 c->offset = 0; 252 c->flag |= COPEN; 253 return c; 254 } 255 256 static void 257 fscreate(Chan *c, char *name, int mode, ulong perm) 258 { 259 int fd, m; 260 char path[MAXPATH]; 261 struct stat stbuf; 262 Ufsinfo *uif; 263 264 m = fsomode(mode&3); 265 266 fspath(c, name, path); 267 268 uif = c->aux; 269 270 if(perm & DMDIR) { 271 if(m) 272 error(Eperm); 273 274 if(mkdir(path, perm & 0777) < 0) 275 error(strerror(errno)); 276 277 fd = open(path, 0); 278 if(fd >= 0) { 279 chmod(path, perm & 0777); 280 chown(path, uif->uid, uif->uid); 281 } 282 close(fd); 283 284 uif->dir = opendir(path); 285 if(uif->dir == 0) 286 error(strerror(errno)); 287 } 288 else { 289 fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0666); 290 if(fd >= 0) { 291 if(m != 1) { 292 close(fd); 293 fd = open(path, m); 294 } 295 chmod(path, perm & 0777); 296 chown(path, uif->uid, uif->gid); 297 } 298 if(fd < 0) 299 error(strerror(errno)); 300 uif->fd = fd; 301 } 302 303 if(stat(path, &stbuf) < 0) 304 error(strerror(errno)); 305 c->qid = fsqid(path, &stbuf); 306 c->offset = 0; 307 c->flag |= COPEN; 308 c->mode = openmode(mode); 309 } 310 311 static void 312 fsclose(Chan *c) 313 { 314 Ufsinfo *uif; 315 316 uif = c->aux; 317 318 if(c->flag & COPEN) { 319 if(c->qid.type & QTDIR) 320 closedir(uif->dir); 321 else 322 close(uif->fd); 323 } 324 325 free(uif); 326 } 327 328 static long 329 fsread(Chan *c, void *va, long n, vlong offset) 330 { 331 int fd, r; 332 Ufsinfo *uif; 333 334 /*print("fsread %s\n", c2name(c));*/ 335 if(c->qid.type & QTDIR) 336 return fsdirread(c, va, n, offset); 337 338 uif = c->aux; 339 qlock(&uif->oq); 340 if(waserror()) { 341 qunlock(&uif->oq); 342 nexterror(); 343 } 344 fd = uif->fd; 345 if(uif->offset != offset) { 346 r = lseek(fd, offset, 0); 347 if(r < 0) 348 error(strerror(errno)); 349 uif->offset = offset; 350 } 351 352 n = read(fd, va, n); 353 if(n < 0) 354 error(strerror(errno)); 355 356 uif->offset += n; 357 qunlock(&uif->oq); 358 poperror(); 359 360 return n; 361 } 362 363 static long 364 fswrite(Chan *c, void *va, long n, vlong offset) 365 { 366 int fd, r; 367 Ufsinfo *uif; 368 369 uif = c->aux; 370 371 qlock(&uif->oq); 372 if(waserror()) { 373 qunlock(&uif->oq); 374 nexterror(); 375 } 376 fd = uif->fd; 377 if(uif->offset != offset) { 378 r = lseek(fd, offset, 0); 379 if(r < 0) 380 error(strerror(errno)); 381 uif->offset = offset; 382 } 383 384 n = write(fd, va, n); 385 if(n < 0) 386 error(strerror(errno)); 387 388 uif->offset += n; 389 qunlock(&uif->oq); 390 poperror(); 391 392 return n; 393 } 394 395 static void 396 fsremove(Chan *c) 397 { 398 int n; 399 char path[MAXPATH]; 400 401 fspath(c, 0, path); 402 if(c->qid.type & QTDIR) 403 n = rmdir(path); 404 else 405 n = remove(path); 406 if(n < 0) 407 error(strerror(errno)); 408 } 409 410 int 411 fswstat(Chan *c, uchar *buf, int n) 412 { 413 Dir d; 414 struct stat stbuf; 415 char old[MAXPATH], new[MAXPATH]; 416 char strs[MAXPATH*3], *p; 417 Ufsinfo *uif; 418 419 if(convM2D(buf, n, &d, strs) != n) 420 error(Ebadstat); 421 422 fspath(c, 0, old); 423 if(stat(old, &stbuf) < 0) 424 error(strerror(errno)); 425 426 uif = c->aux; 427 428 if(d.name[0] && strcmp(d.name, lastelem(c)) != 0) { 429 fspath(c, 0, old); 430 strcpy(new, old); 431 p = strrchr(new, '/'); 432 strcpy(p+1, d.name); 433 if(rename(old, new) < 0) 434 error(strerror(errno)); 435 } 436 437 fspath(c, 0, old); 438 if(~d.mode != 0 && (int)(d.mode&0777) != (int)(stbuf.st_mode&0777)) { 439 if(chmod(old, d.mode&0777) < 0) 440 error(strerror(errno)); 441 uif->mode &= ~0777; 442 uif->mode |= d.mode&0777; 443 } 444 /* 445 p = name2pass(gid, d.gid); 446 if(p == 0) 447 error(Eunknown); 448 449 if(p->id != stbuf.st_gid) { 450 if(chown(old, stbuf.st_uid, p->id) < 0) 451 error(strerror(errno)); 452 453 uif->gid = p->id; 454 } 455 */ 456 return n; 457 } 458 459 static Qid 460 fsqid(char *p, struct stat *st) 461 { 462 Qid q; 463 int dev; 464 ulong h; 465 static int nqdev; 466 static uchar *qdev; 467 468 if(qdev == 0) 469 qdev = mallocz(65536U, 1); 470 471 q.type = 0; 472 if((st->st_mode&S_IFMT) == S_IFDIR) 473 q.type = QTDIR; 474 475 dev = st->st_dev & 0xFFFFUL; 476 if(qdev[dev] == 0) 477 qdev[dev] = ++nqdev; 478 479 h = 0; 480 while(*p != '\0') 481 h += *p++ * 13; 482 483 q.path = (vlong)qdev[dev]<<32; 484 q.path |= h; 485 q.vers = st->st_mtime; 486 487 return q; 488 } 489 490 static void 491 fspath(Chan *c, char *ext, char *path) 492 { 493 strcpy(path, base); 494 strcat(path, "/"); 495 strcat(path, uc2name(c)); 496 if(ext){ 497 strcat(path, "/"); 498 strcat(path, ext); 499 } 500 cleanname(path); 501 } 502 503 static int 504 isdots(char *name) 505 { 506 if(name[0] != '.') 507 return 0; 508 if(name[1] == '\0') 509 return 1; 510 if(name[1] != '.') 511 return 0; 512 if(name[2] == '\0') 513 return 1; 514 return 0; 515 } 516 517 static int 518 p9readdir(char *name, Ufsinfo *uif) 519 { 520 struct dirent *de; 521 522 if(uif->nextname[0]){ 523 strcpy(name, uif->nextname); 524 uif->nextname[0] = 0; 525 return 1; 526 } 527 528 de = readdir(uif->dir); 529 if(de == NULL) 530 return 0; 531 532 strcpy(name, de->d_name); 533 return 1; 534 } 535 536 static ulong 537 fsdirread(Chan *c, uchar *va, int count, ulong offset) 538 { 539 int i; 540 Dir d; 541 long n; 542 char de[NAME_MAX]; 543 struct stat stbuf; 544 char path[MAXPATH], dirpath[MAXPATH]; 545 Ufsinfo *uif; 546 547 /*print("fsdirread %s\n", c2name(c));*/ 548 i = 0; 549 uif = c->aux; 550 551 errno = 0; 552 if(uif->offset != offset) { 553 if(offset != 0) 554 error("bad offset in fsdirread"); 555 uif->offset = offset; /* sync offset */ 556 uif->nextname[0] = 0; 557 rewinddir(uif->dir); 558 } 559 560 fspath(c, 0, dirpath); 561 562 while(i+BIT16SZ < count) { 563 if(!p9readdir(de, uif)) 564 break; 565 566 if(de[0]==0 || isdots(de)) 567 continue; 568 569 d.name = de; 570 sprint(path, "%s/%s", dirpath, de); 571 memset(&stbuf, 0, sizeof stbuf); 572 573 if(stat(path, &stbuf) < 0) { 574 /* fprint(2, "dir: bad path %s\n", path); */ 575 /* but continue... probably a bad symlink */ 576 } 577 578 d.uid = "unknown"; 579 d.gid = "unknown"; 580 d.muid = "unknown"; 581 d.qid = fsqid(path, &stbuf); 582 d.mode = (d.qid.type<<24)|(stbuf.st_mode&0777); 583 d.atime = stbuf.st_atime; 584 d.mtime = stbuf.st_mtime; 585 d.length = stbuf.st_size; 586 d.type = 'U'; 587 d.dev = c->dev; 588 n = convD2M(&d, (uchar*)va+i, count-i); 589 if(n == BIT16SZ){ 590 strcpy(uif->nextname, de); 591 break; 592 } 593 i += n; 594 } 595 /*print("got %d\n", i);*/ 596 uif->offset += i; 597 return i; 598 } 599 600 static int 601 fsomode(int m) 602 { 603 switch(m) { 604 case 0: /* OREAD */ 605 case 3: /* OEXEC */ 606 return 0; 607 case 1: /* OWRITE */ 608 return 1; 609 case 2: /* ORDWR */ 610 return 2; 611 } 612 error(Ebadarg); 613 return 0; 614 } 615 616 Dev fsdevtab = { 617 'U', 618 "fs", 619 620 devreset, 621 devinit, 622 devshutdown, 623 fsattach, 624 fswalk, 625 fsstat, 626 fsopen, 627 fscreate, 628 fsclose, 629 fsread, 630 devbread, 631 fswrite, 632 devbwrite, 633 fsremove, 634 fswstat, 635 }; 636