1 #include "stdinc.h" 2 #include "dat.h" 3 #include "fns.h" 4 #include "error.h" 5 6 /* 7 * locking order is upwards. A thread can hold the lock for a File 8 * and then acquire the lock of its parent 9 */ 10 11 struct File { 12 Fs *fs; /* immutable */ 13 14 /* meta data for file: protected by the lk in the parent */ 15 int ref; /* holds this data structure up */ 16 17 int partial; /* file was never really open */ 18 int removed; /* file has been removed */ 19 int dirty; /* dir is dirty with respect to meta data in block */ 20 u32int boff; /* block offset within msource for this file's meta data */ 21 22 DirEntry dir; /* meta data for this file */ 23 24 File *up; /* parent file */ 25 File *next; /* sibling */ 26 27 /* data for file */ 28 VtLock *lk; /* lock for the following */ 29 Source *source; 30 Source *msource; /* for directories: meta data for children */ 31 File *down; /* children */ 32 33 int mode; 34 }; 35 36 static int fileMetaFlush2(File*, char*); 37 static u32int fileMetaAlloc(File*, DirEntry*, u32int); 38 static int fileRLock(File*); 39 static void fileRUnlock(File*); 40 static int fileLock(File*); 41 static void fileUnlock(File*); 42 static void fileMetaLock(File*); 43 static void fileMetaUnlock(File*); 44 static void fileRAccess(File*); 45 static void fileWAccess(File*, char*); 46 47 static File * 48 fileAlloc(Fs *fs) 49 { 50 File *f; 51 52 f = vtMemAllocZ(sizeof(File)); 53 f->lk = vtLockAlloc(); 54 f->ref = 1; 55 f->fs = fs; 56 f->boff = NilBlock; 57 f->mode = fs->mode; 58 return f; 59 } 60 61 static void 62 fileFree(File *f) 63 { 64 sourceClose(f->source); 65 vtLockFree(f->lk); 66 sourceClose(f->msource); 67 deCleanup(&f->dir); 68 69 memset(f, ~0, sizeof(File)); 70 vtMemFree(f); 71 } 72 73 /* 74 * the file is locked already 75 * f->msource is unlocked 76 */ 77 static File * 78 dirLookup(File *f, char *elem) 79 { 80 int i; 81 MetaBlock mb; 82 MetaEntry me; 83 Block *b; 84 Source *meta; 85 File *ff; 86 u32int bo, nb; 87 88 meta = f->msource; 89 b = nil; 90 if(!sourceLock(meta, -1)) 91 return nil; 92 nb = (sourceGetSize(meta)+meta->dsize-1)/meta->dsize; 93 for(bo=0; bo<nb; bo++){ 94 b = sourceBlock(meta, bo, OReadOnly); 95 if(b == nil) 96 goto Err; 97 if(!mbUnpack(&mb, b->data, meta->dsize)) 98 goto Err; 99 if(mbSearch(&mb, elem, &i, &me)){ 100 ff = fileAlloc(f->fs); 101 if(!deUnpack(&ff->dir, &me)){ 102 fileFree(ff); 103 goto Err; 104 } 105 sourceUnlock(meta); 106 blockPut(b); 107 ff->boff = bo; 108 ff->mode = f->mode; 109 return ff; 110 } 111 112 blockPut(b); 113 b = nil; 114 } 115 vtSetError(ENoFile); 116 /* fall through */ 117 Err: 118 sourceUnlock(meta); 119 blockPut(b); 120 return nil; 121 } 122 123 File * 124 fileRoot(Source *r) 125 { 126 Block *b; 127 Source *r0, *r1, *r2; 128 MetaBlock mb; 129 MetaEntry me; 130 File *root, *mr; 131 Fs *fs; 132 133 b = nil; 134 root = nil; 135 mr = nil; 136 r1 = nil; 137 r2 = nil; 138 139 fs = r->fs; 140 if(!sourceLock(r, -1)) 141 return nil; 142 r0 = sourceOpen(r, 0, fs->mode); 143 if(r0 == nil) 144 goto Err; 145 r1 = sourceOpen(r, 1, fs->mode); 146 if(r1 == nil) 147 goto Err; 148 r2 = sourceOpen(r, 2, fs->mode); 149 if(r2 == nil) 150 goto Err; 151 152 mr = fileAlloc(fs); 153 mr->msource = r2; 154 r2 = nil; 155 156 root = fileAlloc(fs); 157 root->boff = 0; 158 root->up = mr; 159 root->source = r0; 160 r0 = nil; 161 root->msource = r1; 162 r1 = nil; 163 164 mr->down = root; 165 166 if(!sourceLock(mr->msource, -1)) 167 goto Err; 168 b = sourceBlock(mr->msource, 0, OReadOnly); 169 sourceUnlock(mr->msource); 170 if(b == nil) 171 goto Err; 172 173 if(!mbUnpack(&mb, b->data, mr->msource->dsize)) 174 goto Err; 175 176 meUnpack(&me, &mb, 0); 177 if(!deUnpack(&root->dir, &me)) 178 goto Err; 179 blockPut(b); 180 sourceUnlock(r); 181 fileRAccess(root); 182 183 return root; 184 Err: 185 blockPut(b); 186 if(r0) 187 sourceClose(r0); 188 if(r1) 189 sourceClose(r1); 190 if(r2) 191 sourceClose(r2); 192 if(mr) 193 fileFree(mr); 194 if(root) 195 fileFree(root); 196 sourceUnlock(r); 197 198 return nil; 199 } 200 201 static Source * 202 fileOpenSource(File *f, u32int offset, u32int gen, int dir, uint mode) 203 { 204 Source *r; 205 206 if(!sourceLock(f->source, mode)) 207 return nil; 208 r = sourceOpen(f->source, offset, mode); 209 sourceUnlock(f->source); 210 if(r == nil) 211 return nil; 212 if(r->gen != gen){ 213 vtSetError(ERemoved); 214 goto Err; 215 } 216 if(r->dir != dir && r->mode != -1){ 217 fprint(2, "fileOpenSource: dir mismatch %d %d\n", r->dir, dir); 218 vtSetError(EBadMeta); 219 goto Err; 220 } 221 return r; 222 Err: 223 sourceClose(r); 224 return nil; 225 } 226 227 File * 228 _fileWalk(File *f, char *elem, int partial) 229 { 230 File *ff; 231 232 fileRAccess(f); 233 234 if(elem[0] == 0){ 235 vtSetError(EBadPath); 236 return nil; 237 } 238 239 if(!fileIsDir(f)){ 240 vtSetError(ENotDir); 241 return nil; 242 } 243 244 if(strcmp(elem, ".") == 0){ 245 return fileIncRef(f); 246 } 247 248 if(strcmp(elem, "..") == 0){ 249 if(fileIsRoot(f)) 250 return fileIncRef(f); 251 return fileIncRef(f->up); 252 } 253 254 if(!fileLock(f)) 255 return nil; 256 257 for(ff = f->down; ff; ff=ff->next){ 258 if(strcmp(elem, ff->dir.elem) == 0 && !ff->removed){ 259 ff->ref++; 260 goto Exit; 261 } 262 } 263 264 ff = dirLookup(f, elem); 265 if(ff == nil) 266 goto Err; 267 268 if(ff->dir.mode & ModeSnapshot) 269 ff->mode = OReadOnly; 270 271 if(partial){ 272 /* 273 * Do nothing. We're opening this file only so we can clri it. 274 * Usually the sources can't be opened, hence we won't even bother. 275 * Be VERY careful with the returned file. If you hand it to a routine 276 * expecting ff->source and/or ff->msource to be non-nil, we're 277 * likely to dereference nil. FileClri should be the only routine 278 * setting partial. 279 */ 280 ff->partial = 1; 281 }else if(ff->dir.mode & ModeDir){ 282 ff->source = fileOpenSource(f, ff->dir.entry, ff->dir.gen, 1, ff->mode); 283 ff->msource = fileOpenSource(f, ff->dir.mentry, ff->dir.mgen, 0, ff->mode); 284 if(ff->source == nil || ff->msource == nil) 285 goto Err; 286 }else{ 287 ff->source = fileOpenSource(f, ff->dir.entry, ff->dir.gen, 0, ff->mode); 288 if(ff->source == nil) 289 goto Err; 290 } 291 292 /* link in and up parent ref count */ 293 ff->next = f->down; 294 f->down = ff; 295 ff->up = f; 296 fileIncRef(f); 297 Exit: 298 fileUnlock(f); 299 return ff; 300 Err: 301 fileUnlock(f); 302 if(ff != nil) 303 fileDecRef(ff); 304 return nil; 305 } 306 307 File * 308 fileWalk(File *f, char *elem) 309 { 310 return _fileWalk(f, elem, 0); 311 } 312 313 File * 314 _fileOpen(Fs *fs, char *path, int partial) 315 { 316 File *f, *ff; 317 char *p, elem[VtMaxStringSize], *opath; 318 int n; 319 320 f = fs->file; 321 fileIncRef(f); 322 opath = path; 323 while(*path != 0){ 324 for(p = path; *p && *p != '/'; p++) 325 ; 326 n = p - path; 327 if(n > 0){ 328 if(n > VtMaxStringSize){ 329 vtSetError("%s: element too long", EBadPath); 330 goto Err; 331 } 332 memmove(elem, path, n); 333 elem[n] = 0; 334 ff = _fileWalk(f, elem, partial && *p=='\0'); 335 if(ff == nil){ 336 vtSetError("%.*s: %R", utfnlen(opath, p-opath), opath); 337 goto Err; 338 } 339 fileDecRef(f); 340 f = ff; 341 } 342 if(*p == '/') 343 p++; 344 path = p; 345 } 346 return f; 347 Err: 348 fileDecRef(f); 349 return nil; 350 } 351 352 File* 353 fileOpen(Fs *fs, char *path) 354 { 355 return _fileOpen(fs, path, 0); 356 } 357 358 static void 359 fileSetTmp(File *f, int istmp) 360 { 361 int i; 362 Entry e; 363 Source *r; 364 365 for(i=0; i<2; i++){ 366 if(i==0) 367 r = f->source; 368 else 369 r = f->msource; 370 if(r == nil) 371 continue; 372 if(!sourceGetEntry(f->source, &e)){ 373 fprint(2, "sourceGetEntry failed (cannot happen): %r\n"); 374 continue; 375 } 376 if(istmp) 377 e.flags |= VtEntryNoArchive; 378 else 379 e.flags &= ~VtEntryNoArchive; 380 if(!sourceSetEntry(f->source, &e)){ 381 fprint(2, "sourceSetEntry failed (cannot happen): %r\n"); 382 continue; 383 } 384 } 385 } 386 387 File * 388 fileCreate(File *f, char *elem, ulong mode, char *uid) 389 { 390 File *ff; 391 DirEntry *dir; 392 Source *pr, *r, *mr; 393 int isdir; 394 395 if(!fileLock(f)) 396 return nil; 397 398 r = nil; 399 mr = nil; 400 for(ff = f->down; ff; ff=ff->next){ 401 if(strcmp(elem, ff->dir.elem) == 0 && !ff->removed){ 402 ff = nil; 403 vtSetError(EExists); 404 goto Err1; 405 } 406 } 407 408 ff = dirLookup(f, elem); 409 if(ff != nil){ 410 vtSetError(EExists); 411 goto Err1; 412 } 413 414 pr = f->source; 415 if(pr->mode != OReadWrite){ 416 vtSetError(EReadOnly); 417 goto Err1; 418 } 419 420 if(!sourceLock2(f->source, f->msource, -1)) 421 goto Err1; 422 423 ff = fileAlloc(f->fs); 424 isdir = mode & ModeDir; 425 426 r = sourceCreate(pr, pr->dsize, isdir, 0); 427 if(r == nil) 428 goto Err; 429 if(isdir){ 430 mr = sourceCreate(pr, pr->dsize, 0, r->offset); 431 if(mr == nil) 432 goto Err; 433 } 434 435 dir = &ff->dir; 436 dir->elem = vtStrDup(elem); 437 dir->entry = r->offset; 438 dir->gen = r->gen; 439 if(isdir){ 440 dir->mentry = mr->offset; 441 dir->mgen = mr->gen; 442 } 443 dir->size = 0; 444 if(!fsNextQid(f->fs, &dir->qid)) 445 goto Err; 446 dir->uid = vtStrDup(uid); 447 dir->gid = vtStrDup(f->dir.gid); 448 dir->mid = vtStrDup(uid); 449 dir->mtime = time(0L); 450 dir->mcount = 0; 451 dir->ctime = dir->mtime; 452 dir->atime = dir->mtime; 453 dir->mode = mode; 454 455 ff->boff = fileMetaAlloc(f, dir, 0); 456 if(ff->boff == NilBlock) 457 goto Err; 458 459 sourceUnlock(f->source); 460 sourceUnlock(f->msource); 461 462 ff->source = r; 463 ff->msource = mr; 464 465 if(mode&ModeTemporary){ 466 if(!sourceLock2(r, mr, -1)) 467 goto Err1; 468 fileSetTmp(ff, 1); 469 sourceUnlock(r); 470 if(mr) 471 sourceUnlock(mr); 472 } 473 474 /* committed */ 475 476 /* link in and up parent ref count */ 477 ff->next = f->down; 478 f->down = ff; 479 ff->up = f; 480 fileIncRef(f); 481 482 fileWAccess(f, uid); 483 484 fileUnlock(f); 485 return ff; 486 487 Err: 488 sourceUnlock(f->source); 489 sourceUnlock(f->msource); 490 Err1: 491 if(r){ 492 sourceLock(r, -1); 493 sourceRemove(r); 494 } 495 if(mr){ 496 sourceLock(mr, -1); 497 sourceRemove(mr); 498 } 499 if(ff) 500 fileDecRef(ff); 501 fileUnlock(f); 502 return 0; 503 } 504 505 int 506 fileRead(File *f, void *buf, int cnt, vlong offset) 507 { 508 Source *s; 509 uvlong size; 510 u32int bn; 511 int off, dsize, n, nn; 512 Block *b; 513 uchar *p; 514 515 if(0)fprint(2, "fileRead: %s %d, %lld\n", f->dir.elem, cnt, offset); 516 517 if(!fileRLock(f)) 518 return -1; 519 520 if(offset < 0){ 521 vtSetError(EBadOffset); 522 goto Err1; 523 } 524 525 fileRAccess(f); 526 527 if(!sourceLock(f->source, OReadOnly)) 528 goto Err1; 529 530 s = f->source; 531 dsize = s->dsize; 532 size = sourceGetSize(s); 533 534 if(offset >= size) 535 offset = size; 536 537 if(cnt > size-offset) 538 cnt = size-offset; 539 bn = offset/dsize; 540 off = offset%dsize; 541 p = buf; 542 while(cnt > 0){ 543 b = sourceBlock(s, bn, OReadOnly); 544 if(b == nil) 545 goto Err; 546 n = cnt; 547 if(n > dsize-off) 548 n = dsize-off; 549 nn = dsize-off; 550 if(nn > n) 551 nn = n; 552 memmove(p, b->data+off, nn); 553 memset(p+nn, 0, nn-n); 554 off = 0; 555 bn++; 556 cnt -= n; 557 p += n; 558 blockPut(b); 559 } 560 sourceUnlock(s); 561 fileRUnlock(f); 562 return p-(uchar*)buf; 563 564 Err: 565 sourceUnlock(s); 566 Err1: 567 fileRUnlock(f); 568 return -1; 569 } 570 571 /* 572 * Changes the file block bn to be the given block score. 573 * Very sneaky. Only used by flfmt. 574 */ 575 int 576 fileMapBlock(File *f, ulong bn, uchar score[VtScoreSize], ulong tag) 577 { 578 Block *b; 579 Entry e; 580 Source *s; 581 582 if(!fileLock(f)) 583 return 0; 584 585 s = nil; 586 if(f->dir.mode & ModeDir){ 587 vtSetError(ENotFile); 588 goto Err; 589 } 590 591 if(f->source->mode != OReadWrite){ 592 vtSetError(EReadOnly); 593 goto Err; 594 } 595 596 if(!sourceLock(f->source, -1)) 597 goto Err; 598 599 s = f->source; 600 b = _sourceBlock(s, bn, OReadWrite, 1, tag); 601 if(b == nil) 602 goto Err; 603 604 if(!sourceGetEntry(s, &e)) 605 goto Err; 606 if(b->l.type == BtDir){ 607 memmove(e.score, score, VtScoreSize); 608 assert(e.tag == tag || e.tag == 0); 609 e.tag = tag; 610 e.flags |= VtEntryLocal; 611 entryPack(&e, b->data, f->source->offset % f->source->epb); 612 }else 613 memmove(b->data + (bn%(e.psize/VtScoreSize))*VtScoreSize, score, VtScoreSize); 614 blockDirty(b); 615 blockPut(b); 616 sourceUnlock(s); 617 fileUnlock(f); 618 return 1; 619 620 Err: 621 if(s) 622 sourceUnlock(s); 623 fileUnlock(f); 624 return 0; 625 } 626 627 int 628 fileSetSize(File *f, uvlong size) 629 { 630 int r; 631 632 if(!fileLock(f)) 633 return 0; 634 r = 0; 635 if(f->dir.mode & ModeDir){ 636 vtSetError(ENotFile); 637 goto Err; 638 } 639 if(f->source->mode != OReadWrite){ 640 vtSetError(EReadOnly); 641 goto Err; 642 } 643 if(!sourceLock(f->source, -1)) 644 goto Err; 645 r = sourceSetSize(f->source, size); 646 sourceUnlock(f->source); 647 Err: 648 fileUnlock(f); 649 return r; 650 } 651 652 int 653 fileWrite(File *f, void *buf, int cnt, vlong offset, char *uid) 654 { 655 Source *s; 656 ulong bn; 657 int off, dsize, n; 658 Block *b; 659 uchar *p; 660 vlong eof; 661 662 if(0)fprint(2, "fileWrite: %s %d, %lld\n", f->dir.elem, cnt, offset); 663 664 if(!fileLock(f)) 665 return -1; 666 667 s = nil; 668 if(f->dir.mode & ModeDir){ 669 vtSetError(ENotFile); 670 goto Err; 671 } 672 673 if(f->source->mode != OReadWrite){ 674 vtSetError(EReadOnly); 675 goto Err; 676 } 677 if(offset < 0){ 678 vtSetError(EBadOffset); 679 goto Err; 680 } 681 682 fileWAccess(f, uid); 683 684 if(!sourceLock(f->source, -1)) 685 goto Err; 686 s = f->source; 687 dsize = s->dsize; 688 689 eof = sourceGetSize(s); 690 if(f->dir.mode & ModeAppend) 691 offset = eof; 692 bn = offset/dsize; 693 off = offset%dsize; 694 p = buf; 695 while(cnt > 0){ 696 n = cnt; 697 if(n > dsize-off) 698 n = dsize-off; 699 b = sourceBlock(s, bn, n<dsize?OReadWrite:OOverWrite); 700 if(b == nil){ 701 if(offset > eof) 702 sourceSetSize(s, offset); 703 goto Err; 704 } 705 memmove(b->data+off, p, n); 706 off = 0; 707 cnt -= n; 708 p += n; 709 offset += n; 710 bn++; 711 blockDirty(b); 712 blockPut(b); 713 } 714 if(offset > eof && !sourceSetSize(s, offset)) 715 goto Err; 716 sourceUnlock(s); 717 fileUnlock(f); 718 return p-(uchar*)buf; 719 Err: 720 if(s) 721 sourceUnlock(s); 722 fileUnlock(f); 723 return -1; 724 } 725 726 int 727 fileGetDir(File *f, DirEntry *dir) 728 { 729 if(!fileRLock(f)) 730 return 0; 731 732 fileMetaLock(f); 733 deCopy(dir, &f->dir); 734 fileMetaUnlock(f); 735 736 if(!fileIsDir(f)){ 737 if(!sourceLock(f->source, OReadOnly)){ 738 fileRUnlock(f); 739 return 0; 740 } 741 dir->size = sourceGetSize(f->source); 742 sourceUnlock(f->source); 743 } 744 fileRUnlock(f); 745 746 return 1; 747 } 748 749 int 750 fileTruncate(File *f, char *uid) 751 { 752 if(fileIsDir(f)){ 753 vtSetError(ENotFile); 754 return 0; 755 } 756 757 if(!fileLock(f)) 758 return 0; 759 760 if(f->source->mode != OReadWrite){ 761 vtSetError(EReadOnly); 762 fileUnlock(f); 763 return 0; 764 } 765 if(!sourceLock(f->source, -1)){ 766 fileUnlock(f); 767 return 0; 768 } 769 if(!sourceTruncate(f->source)){ 770 sourceUnlock(f->source); 771 fileUnlock(f); 772 return 0; 773 } 774 sourceUnlock(f->source); 775 fileUnlock(f); 776 777 fileWAccess(f, uid); 778 779 return 1; 780 } 781 782 int 783 fileSetDir(File *f, DirEntry *dir, char *uid) 784 { 785 File *ff; 786 char *oelem; 787 u32int mask; 788 u64int size; 789 790 /* can not set permissions for the root */ 791 if(fileIsRoot(f)){ 792 vtSetError(ERoot); 793 return 0; 794 } 795 796 if(!fileLock(f)) 797 return 0; 798 799 if(f->source->mode != OReadWrite){ 800 vtSetError(EReadOnly); 801 fileUnlock(f); 802 return 0; 803 } 804 805 fileMetaLock(f); 806 807 /* check new name does not already exist */ 808 if(strcmp(f->dir.elem, dir->elem) != 0){ 809 for(ff = f->up->down; ff; ff=ff->next){ 810 if(strcmp(dir->elem, ff->dir.elem) == 0 && !ff->removed){ 811 vtSetError(EExists); 812 goto Err; 813 } 814 } 815 816 ff = dirLookup(f->up, dir->elem); 817 if(ff != nil){ 818 fileDecRef(ff); 819 vtSetError(EExists); 820 goto Err; 821 } 822 } 823 824 if(!sourceLock2(f->source, f->msource, -1)) 825 goto Err; 826 if(!fileIsDir(f)){ 827 size = sourceGetSize(f->source); 828 if(size != dir->size){ 829 if(!sourceSetSize(f->source, dir->size)){ 830 sourceUnlock(f->source); 831 if(f->msource) 832 sourceUnlock(f->msource); 833 goto Err; 834 } 835 /* commited to changing it now */ 836 } 837 } 838 /* commited to changing it now */ 839 if((f->dir.mode&ModeTemporary) != (dir->mode&ModeTemporary)) 840 fileSetTmp(f, dir->mode&ModeTemporary); 841 sourceUnlock(f->source); 842 if(f->msource) 843 sourceUnlock(f->msource); 844 845 oelem = nil; 846 if(strcmp(f->dir.elem, dir->elem) != 0){ 847 oelem = f->dir.elem; 848 f->dir.elem = vtStrDup(dir->elem); 849 } 850 851 if(strcmp(f->dir.uid, dir->uid) != 0){ 852 vtMemFree(f->dir.uid); 853 f->dir.uid = vtStrDup(dir->uid); 854 } 855 856 if(strcmp(f->dir.gid, dir->gid) != 0){ 857 vtMemFree(f->dir.gid); 858 f->dir.gid = vtStrDup(dir->gid); 859 } 860 861 f->dir.mtime = dir->mtime; 862 f->dir.atime = dir->atime; 863 864 //fprint(2, "mode %x %x ", f->dir.mode, dir->mode); 865 mask = ~(ModeDir|ModeSnapshot); 866 f->dir.mode &= ~mask; 867 f->dir.mode |= mask & dir->mode; 868 f->dirty = 1; 869 //fprint(2, "->%x\n", f->dir.mode); 870 871 fileMetaFlush2(f, oelem); 872 vtMemFree(oelem); 873 874 fileMetaUnlock(f); 875 fileUnlock(f); 876 877 fileWAccess(f->up, uid); 878 879 return 1; 880 Err: 881 fileMetaUnlock(f); 882 fileUnlock(f); 883 return 0; 884 } 885 886 int 887 fileSetQidSpace(File *f, u64int offset, u64int max) 888 { 889 int ret; 890 891 if(!fileLock(f)) 892 return 0; 893 fileMetaLock(f); 894 f->dir.qidSpace = 1; 895 f->dir.qidOffset = offset; 896 f->dir.qidMax = max; 897 ret = fileMetaFlush2(f, nil); 898 fileMetaUnlock(f); 899 fileUnlock(f); 900 return ret; 901 } 902 903 904 uvlong 905 fileGetId(File *f) 906 { 907 /* immutable */ 908 return f->dir.qid; 909 } 910 911 ulong 912 fileGetMcount(File *f) 913 { 914 ulong mcount; 915 916 fileMetaLock(f); 917 mcount = f->dir.mcount; 918 fileMetaUnlock(f); 919 return mcount; 920 } 921 922 ulong 923 fileGetMode(File *f) 924 { 925 ulong mode; 926 927 fileMetaLock(f); 928 mode = f->dir.mode; 929 fileMetaUnlock(f); 930 return mode; 931 } 932 933 int 934 fileIsDir(File *f) 935 { 936 /* immutable */ 937 return (f->dir.mode & ModeDir) != 0; 938 } 939 940 int 941 fileIsRoot(File *f) 942 { 943 return f == f->fs->file; 944 } 945 946 int 947 fileIsRoFs(File *f) 948 { 949 return f->fs->mode == OReadOnly; 950 } 951 952 int 953 fileGetSize(File *f, uvlong *size) 954 { 955 if(!fileRLock(f)) 956 return 0; 957 if(!sourceLock(f->source, OReadOnly)){ 958 fileRUnlock(f); 959 return 0; 960 } 961 *size = sourceGetSize(f->source); 962 sourceUnlock(f->source); 963 fileRUnlock(f); 964 965 return 1; 966 } 967 968 void 969 fileMetaFlush(File *f, int rec) 970 { 971 File **kids, *p; 972 int nkids; 973 int i; 974 975 fileMetaLock(f); 976 fileMetaFlush2(f, nil); 977 fileMetaUnlock(f); 978 979 if(!rec || !fileIsDir(f)) 980 return; 981 982 if(!fileLock(f)) 983 return; 984 nkids = 0; 985 for(p=f->down; p; p=p->next) 986 nkids++; 987 kids = vtMemAlloc(nkids*sizeof(File*)); 988 i = 0; 989 for(p=f->down; p; p=p->next){ 990 kids[i++] = p; 991 p->ref++; 992 } 993 fileUnlock(f); 994 995 for(i=0; i<nkids; i++){ 996 fileMetaFlush(kids[i], 1); 997 fileDecRef(kids[i]); 998 } 999 vtMemFree(kids); 1000 } 1001 1002 /* assumes metaLock is held */ 1003 static int 1004 fileMetaFlush2(File *f, char *oelem) 1005 { 1006 File *fp; 1007 Block *b, *bb; 1008 MetaBlock mb; 1009 MetaEntry me, me2; 1010 int i, n; 1011 u32int boff; 1012 1013 if(!f->dirty) 1014 return 1; 1015 1016 if(oelem == nil) 1017 oelem = f->dir.elem; 1018 1019 //print("fileMetaFlush %s->%s\n", oelem, f->dir.elem); 1020 1021 fp = f->up; 1022 1023 if(!sourceLock(fp->msource, -1)) 1024 return 0; 1025 /* can happen if source is clri'ed out from under us */ 1026 if(f->boff == NilBlock) 1027 goto Err1; 1028 b = sourceBlock(fp->msource, f->boff, OReadWrite); 1029 if(b == nil) 1030 goto Err1; 1031 1032 if(!mbUnpack(&mb, b->data, fp->msource->dsize)) 1033 goto Err; 1034 if(!mbSearch(&mb, oelem, &i, &me)) 1035 goto Err; 1036 1037 n = deSize(&f->dir); 1038 if(0)fprint(2, "old size %d new size %d\n", me.size, n); 1039 1040 if(mbResize(&mb, &me, n)){ 1041 /* fits in the block */ 1042 mbDelete(&mb, i); 1043 if(strcmp(f->dir.elem, oelem) != 0) 1044 mbSearch(&mb, f->dir.elem, &i, &me2); 1045 dePack(&f->dir, &me); 1046 mbInsert(&mb, i, &me); 1047 mbPack(&mb); 1048 blockDirty(b); 1049 blockPut(b); 1050 sourceUnlock(fp->msource); 1051 f->dirty = 0; 1052 1053 return 1; 1054 } 1055 1056 /* 1057 * moving entry to another block 1058 * it is feasible for the fs to crash leaving two copies 1059 * of the directory entry. This is just too much work to 1060 * fix. Given that entries are only allocated in a block that 1061 * is less than PercentageFull, most modifications of meta data 1062 * will fit within the block. i.e. this code should almost 1063 * never be executed. 1064 */ 1065 boff = fileMetaAlloc(fp, &f->dir, f->boff+1); 1066 if(boff == NilBlock){ 1067 /* mbResize might have modified block */ 1068 mbPack(&mb); 1069 blockDirty(b); 1070 goto Err; 1071 } 1072 fprint(2, "fileMetaFlush moving entry from %ud -> %ud\n", f->boff, boff); 1073 f->boff = boff; 1074 1075 /* make sure deletion goes to disk after new entry */ 1076 bb = sourceBlock(fp->msource, f->boff, OReadWrite); 1077 mbDelete(&mb, i); 1078 mbPack(&mb); 1079 blockDependency(b, bb, -1, nil, nil); 1080 blockPut(bb); 1081 blockDirty(b); 1082 blockPut(b); 1083 sourceUnlock(fp->msource); 1084 1085 f->dirty = 0; 1086 1087 return 1; 1088 1089 Err: 1090 blockPut(b); 1091 Err1: 1092 sourceUnlock(fp->msource); 1093 return 0; 1094 } 1095 1096 static int 1097 fileMetaRemove(File *f, char *uid) 1098 { 1099 Block *b; 1100 MetaBlock mb; 1101 MetaEntry me; 1102 int i; 1103 File *up; 1104 1105 up = f->up; 1106 1107 fileWAccess(up, uid); 1108 1109 fileMetaLock(f); 1110 1111 sourceLock(up->msource, OReadWrite); 1112 b = sourceBlock(up->msource, f->boff, OReadWrite); 1113 if(b == nil) 1114 goto Err; 1115 1116 if(!mbUnpack(&mb, b->data, up->msource->dsize)) 1117 { 1118 fprint(2, "U\n"); 1119 goto Err; 1120 } 1121 if(!mbSearch(&mb, f->dir.elem, &i, &me)) 1122 { 1123 fprint(2, "S\n"); 1124 goto Err; 1125 } 1126 mbDelete(&mb, i); 1127 mbPack(&mb); 1128 sourceUnlock(up->msource); 1129 1130 blockDirty(b); 1131 blockPut(b); 1132 1133 f->removed = 1; 1134 f->boff = NilBlock; 1135 f->dirty = 0; 1136 1137 fileMetaUnlock(f); 1138 return 1; 1139 1140 Err: 1141 sourceUnlock(up->msource); 1142 blockPut(b); 1143 fileMetaUnlock(f); 1144 return 0; 1145 } 1146 1147 /* assume file is locked, assume f->msource is locked */ 1148 static int 1149 fileCheckEmpty(File *f) 1150 { 1151 u32int i, n; 1152 Block *b; 1153 MetaBlock mb; 1154 Source *r; 1155 1156 r = f->msource; 1157 n = (sourceGetSize(r)+r->dsize-1)/r->dsize; 1158 for(i=0; i<n; i++){ 1159 b = sourceBlock(r, i, OReadOnly); 1160 if(b == nil) 1161 goto Err; 1162 if(!mbUnpack(&mb, b->data, r->dsize)) 1163 goto Err; 1164 if(mb.nindex > 0){ 1165 vtSetError(ENotEmpty); 1166 goto Err; 1167 } 1168 blockPut(b); 1169 } 1170 return 1; 1171 Err: 1172 blockPut(b); 1173 return 0; 1174 } 1175 1176 int 1177 fileRemove(File *f, char *uid) 1178 { 1179 File *ff; 1180 1181 /* can not remove the root */ 1182 if(fileIsRoot(f)){ 1183 vtSetError(ERoot); 1184 return 0; 1185 } 1186 1187 if(!fileLock(f)) 1188 return 0; 1189 1190 if(f->source->mode != OReadWrite){ 1191 vtSetError(EReadOnly); 1192 goto Err1; 1193 } 1194 if(!sourceLock2(f->source, f->msource, -1)) 1195 goto Err1; 1196 if(fileIsDir(f) && !fileCheckEmpty(f)) 1197 goto Err; 1198 1199 for(ff=f->down; ff; ff=ff->next) 1200 assert(ff->removed); 1201 1202 sourceRemove(f->source); 1203 f->source = nil; 1204 if(f->msource){ 1205 sourceRemove(f->msource); 1206 f->msource = nil; 1207 } 1208 1209 fileUnlock(f); 1210 1211 if(!fileMetaRemove(f, uid)) 1212 return 0; 1213 1214 return 1; 1215 1216 Err: 1217 sourceUnlock(f->source); 1218 if(f->msource) 1219 sourceUnlock(f->msource); 1220 Err1: 1221 fileUnlock(f); 1222 return 0; 1223 } 1224 1225 static int 1226 clri(File *f, char *uid) 1227 { 1228 int r; 1229 1230 if(f == nil) 1231 return 0; 1232 if(f->up->source->mode != OReadWrite){ 1233 vtSetError(EReadOnly); 1234 fileDecRef(f); 1235 return 0; 1236 } 1237 r = fileMetaRemove(f, uid); 1238 fileDecRef(f); 1239 return r; 1240 } 1241 1242 int 1243 fileClriPath(Fs *fs, char *path, char *uid) 1244 { 1245 return clri(_fileOpen(fs, path, 1), uid); 1246 } 1247 1248 int 1249 fileClri(File *dir, char *elem, char *uid) 1250 { 1251 return clri(_fileWalk(dir, elem, 1), uid); 1252 } 1253 1254 File * 1255 fileIncRef(File *vf) 1256 { 1257 fileMetaLock(vf); 1258 assert(vf->ref > 0); 1259 vf->ref++; 1260 fileMetaUnlock(vf); 1261 return vf; 1262 } 1263 1264 int 1265 fileDecRef(File *f) 1266 { 1267 File *p, *q, **qq; 1268 1269 if(f->up == nil){ 1270 /* never linked in */ 1271 assert(f->ref == 1); 1272 fileFree(f); 1273 return 1; 1274 } 1275 1276 fileMetaLock(f); 1277 f->ref--; 1278 if(f->ref > 0){ 1279 fileMetaUnlock(f); 1280 return 0; 1281 } 1282 assert(f->ref == 0); 1283 assert(f->down == nil); 1284 1285 fileMetaFlush2(f, nil); 1286 1287 p = f->up; 1288 qq = &p->down; 1289 for(q = *qq; q; q = *qq){ 1290 if(q == f) 1291 break; 1292 qq = &q->next; 1293 } 1294 assert(q != nil); 1295 *qq = f->next; 1296 1297 fileMetaUnlock(f); 1298 fileFree(f); 1299 1300 fileDecRef(p); 1301 return 1; 1302 } 1303 1304 File * 1305 fileGetParent(File *f) 1306 { 1307 if(fileIsRoot(f)) 1308 return fileIncRef(f); 1309 return fileIncRef(f->up); 1310 } 1311 1312 DirEntryEnum * 1313 deeOpen(File *f) 1314 { 1315 DirEntryEnum *dee; 1316 File *p; 1317 1318 if(!fileIsDir(f)){ 1319 vtSetError(ENotDir); 1320 fileDecRef(f); 1321 return nil; 1322 } 1323 1324 /* flush out meta data */ 1325 if(!fileLock(f)) 1326 return nil; 1327 for(p=f->down; p; p=p->next) 1328 fileMetaFlush2(p, nil); 1329 fileUnlock(f); 1330 1331 dee = vtMemAllocZ(sizeof(DirEntryEnum)); 1332 dee->file = fileIncRef(f); 1333 1334 return dee; 1335 } 1336 1337 static int 1338 dirEntrySize(Source *s, ulong elem, ulong gen, uvlong *size) 1339 { 1340 Block *b; 1341 ulong bn; 1342 Entry e; 1343 int epb; 1344 1345 epb = s->dsize/VtEntrySize; 1346 bn = elem/epb; 1347 elem -= bn*epb; 1348 1349 b = sourceBlock(s, bn, OReadOnly); 1350 if(b == nil) 1351 goto Err; 1352 if(!entryUnpack(&e, b->data, elem)) 1353 goto Err; 1354 1355 /* hanging entries are returned as zero size */ 1356 if(!(e.flags & VtEntryActive) || e.gen != gen) 1357 *size = 0; 1358 else 1359 *size = e.size; 1360 blockPut(b); 1361 return 1; 1362 1363 Err: 1364 blockPut(b); 1365 return 0; 1366 } 1367 1368 static int 1369 deeFill(DirEntryEnum *dee) 1370 { 1371 int i, n; 1372 Source *meta, *source; 1373 MetaBlock mb; 1374 MetaEntry me; 1375 File *f; 1376 Block *b; 1377 DirEntry *de; 1378 1379 /* clean up first */ 1380 for(i=dee->i; i<dee->n; i++) 1381 deCleanup(dee->buf+i); 1382 vtMemFree(dee->buf); 1383 dee->buf = nil; 1384 dee->i = 0; 1385 dee->n = 0; 1386 1387 f = dee->file; 1388 1389 source = f->source; 1390 meta = f->msource; 1391 1392 b = sourceBlock(meta, dee->boff, OReadOnly); 1393 if(b == nil) 1394 goto Err; 1395 if(!mbUnpack(&mb, b->data, meta->dsize)) 1396 goto Err; 1397 1398 n = mb.nindex; 1399 dee->buf = vtMemAlloc(n * sizeof(DirEntry)); 1400 1401 for(i=0; i<n; i++){ 1402 de = dee->buf + i; 1403 meUnpack(&me, &mb, i); 1404 if(!deUnpack(de, &me)) 1405 goto Err; 1406 dee->n++; 1407 if(!(de->mode & ModeDir)) 1408 if(!dirEntrySize(source, de->entry, de->gen, &de->size)) 1409 goto Err; 1410 } 1411 dee->boff++; 1412 blockPut(b); 1413 return 1; 1414 Err: 1415 blockPut(b); 1416 return 0; 1417 } 1418 1419 int 1420 deeRead(DirEntryEnum *dee, DirEntry *de) 1421 { 1422 int ret, didread; 1423 File *f; 1424 u32int nb; 1425 1426 f = dee->file; 1427 if(!fileRLock(f)) 1428 return -1; 1429 1430 if(!sourceLock2(f->source, f->msource, OReadOnly)){ 1431 fileRUnlock(f); 1432 return -1; 1433 } 1434 1435 nb = (sourceGetSize(f->msource)+f->msource->dsize-1)/f->msource->dsize; 1436 1437 didread = 0; 1438 while(dee->i >= dee->n){ 1439 if(dee->boff >= nb){ 1440 ret = 0; 1441 goto Return; 1442 } 1443 didread = 1; 1444 if(!deeFill(dee)){ 1445 ret = -1; 1446 goto Return; 1447 } 1448 } 1449 1450 memmove(de, dee->buf + dee->i, sizeof(DirEntry)); 1451 dee->i++; 1452 ret = 1; 1453 1454 Return: 1455 sourceUnlock(f->source); 1456 sourceUnlock(f->msource); 1457 fileRUnlock(f); 1458 1459 if(didread) 1460 fileRAccess(f); 1461 return ret; 1462 } 1463 1464 void 1465 deeClose(DirEntryEnum *dee) 1466 { 1467 int i; 1468 if(dee == nil) 1469 return; 1470 for(i=dee->i; i<dee->n; i++) 1471 deCleanup(dee->buf+i); 1472 vtMemFree(dee->buf); 1473 fileDecRef(dee->file); 1474 vtMemFree(dee); 1475 } 1476 1477 /* 1478 * caller must lock f->source and f->msource 1479 * caller must NOT lock the source and msource 1480 * referenced by dir. 1481 */ 1482 static u32int 1483 fileMetaAlloc(File *f, DirEntry *dir, u32int start) 1484 { 1485 u32int nb, bo; 1486 Block *b, *bb; 1487 MetaBlock mb; 1488 int nn; 1489 uchar *p; 1490 int i, n, epb; 1491 MetaEntry me; 1492 Source *s, *ms; 1493 1494 s = f->source; 1495 ms = f->msource; 1496 1497 n = deSize(dir); 1498 nb = (sourceGetSize(ms)+ms->dsize-1)/ms->dsize; 1499 b = nil; 1500 if(start > nb) 1501 start = nb; 1502 for(bo=start; bo<nb; bo++){ 1503 b = sourceBlock(ms, bo, OReadWrite); 1504 if(b == nil) 1505 goto Err; 1506 if(!mbUnpack(&mb, b->data, ms->dsize)) 1507 goto Err; 1508 nn = (mb.maxsize*FullPercentage/100) - mb.size + mb.free; 1509 if(n <= nn && mb.nindex < mb.maxindex) 1510 break; 1511 blockPut(b); 1512 b = nil; 1513 } 1514 1515 /* add block to meta file */ 1516 if(b == nil){ 1517 b = sourceBlock(ms, bo, OReadWrite); 1518 if(b == nil) 1519 goto Err; 1520 sourceSetSize(ms, (nb+1)*ms->dsize); 1521 mbInit(&mb, b->data, ms->dsize, ms->dsize/BytesPerEntry); 1522 } 1523 1524 p = mbAlloc(&mb, n); 1525 if(p == nil){ 1526 /* mbAlloc might have changed block */ 1527 mbPack(&mb); 1528 blockDirty(b); 1529 vtSetError(EBadMeta); 1530 goto Err; 1531 } 1532 1533 mbSearch(&mb, dir->elem, &i, &me); 1534 assert(me.p == nil); 1535 me.p = p; 1536 me.size = n; 1537 dePack(dir, &me); 1538 mbInsert(&mb, i, &me); 1539 mbPack(&mb); 1540 1541 /* meta block depends on super block for qid ... */ 1542 bb = cacheLocal(b->c, PartSuper, 0, OReadOnly); 1543 blockDependency(b, bb, -1, nil, nil); 1544 blockPut(bb); 1545 1546 /* ... and one or two dir entries */ 1547 epb = s->dsize/VtEntrySize; 1548 bb = sourceBlock(s, dir->entry/epb, OReadOnly); 1549 blockDependency(b, bb, -1, nil, nil); 1550 blockPut(bb); 1551 if(dir->mode & ModeDir){ 1552 bb = sourceBlock(s, dir->mentry/epb, OReadOnly); 1553 blockDependency(b, bb, -1, nil, nil); 1554 blockPut(bb); 1555 } 1556 1557 blockDirty(b); 1558 blockPut(b); 1559 return bo; 1560 Err: 1561 blockPut(b); 1562 return NilBlock; 1563 } 1564 1565 static int 1566 chkSource(File *f) 1567 { 1568 if(f->partial) 1569 return 1; 1570 1571 if(f->source == nil || (f->dir.mode & ModeDir) && f->msource == nil){ 1572 vtSetError(ERemoved); 1573 return 0; 1574 } 1575 return 1; 1576 } 1577 1578 static int 1579 fileRLock(File *f) 1580 { 1581 assert(!vtCanLock(f->fs->elk)); 1582 vtRLock(f->lk); 1583 if(!chkSource(f)){ 1584 fileRUnlock(f); 1585 return 0; 1586 } 1587 return 1; 1588 } 1589 1590 static void 1591 fileRUnlock(File *f) 1592 { 1593 vtRUnlock(f->lk); 1594 } 1595 1596 static int 1597 fileLock(File *f) 1598 { 1599 assert(!vtCanLock(f->fs->elk)); 1600 vtLock(f->lk); 1601 if(!chkSource(f)){ 1602 fileUnlock(f); 1603 return 0; 1604 } 1605 return 1; 1606 } 1607 1608 static void 1609 fileUnlock(File *f) 1610 { 1611 vtUnlock(f->lk); 1612 } 1613 1614 /* 1615 * f->source and f->msource must NOT be locked. 1616 * fileMetaFlush locks the fileMeta and then the source (in fileMetaFlush2). 1617 * We have to respect that ordering. 1618 */ 1619 static void 1620 fileMetaLock(File *f) 1621 { 1622 if(f->up == nil) 1623 fprint(2, "f->elem = %s\n", f->dir.elem); 1624 assert(f->up != nil); 1625 assert(!vtCanLock(f->fs->elk)); 1626 vtLock(f->up->lk); 1627 } 1628 1629 static void 1630 fileMetaUnlock(File *f) 1631 { 1632 vtUnlock(f->up->lk); 1633 } 1634 1635 /* 1636 * f->source and f->msource must NOT be locked. 1637 * see fileMetaLock. 1638 */ 1639 static void 1640 fileRAccess(File* f) 1641 { 1642 if(f->mode == OReadOnly) 1643 return; 1644 1645 fileMetaLock(f); 1646 f->dir.atime = time(0L); 1647 f->dirty = 1; 1648 fileMetaUnlock(f); 1649 } 1650 1651 /* 1652 * f->source and f->msource must NOT be locked. 1653 * see fileMetaLock. 1654 */ 1655 static void 1656 fileWAccess(File* f, char *mid) 1657 { 1658 if(f->mode == OReadOnly) 1659 return; 1660 1661 fileMetaLock(f); 1662 f->dir.atime = f->dir.mtime = time(0L); 1663 if(strcmp(f->dir.mid, mid) != 0){ 1664 vtMemFree(f->dir.mid); 1665 f->dir.mid = vtStrDup(mid); 1666 } 1667 f->dir.mcount++; 1668 f->dirty = 1; 1669 fileMetaUnlock(f); 1670 1671 /*RSC: let's try this */ 1672 /*presotto - lets not 1673 if(f->up) 1674 fileWAccess(f->up, mid); 1675 */ 1676 } 1677 1678 static void 1679 markCopied(Block *b) 1680 { 1681 Block *lb; 1682 Label l; 1683 1684 if(globalToLocal(b->score) == NilBlock) 1685 return; 1686 1687 if(!(b->l.state & BsCopied)){ 1688 /* 1689 * We need to record that there are now pointers in 1690 * b that are not unique to b. We do this by marking 1691 * b as copied. Since we don't return the label block, 1692 * the caller can't get the dependencies right. So we have 1693 * to flush the block ourselves. This is a rare occurrence. 1694 */ 1695 l = b->l; 1696 l.state |= BsCopied; 1697 lb = _blockSetLabel(b, &l); 1698 WriteAgain: 1699 while(!blockWrite(lb)){ 1700 fprint(2, "getEntry: could not write label block\n"); 1701 sleep(10*1000); 1702 } 1703 while(lb->iostate != BioClean && lb->iostate != BioDirty){ 1704 assert(lb->iostate == BioWriting); 1705 vtSleep(lb->ioready); 1706 } 1707 if(lb->iostate == BioDirty) 1708 goto WriteAgain; 1709 blockPut(lb); 1710 } 1711 } 1712 1713 static int 1714 getEntry(Source *r, Entry *e, int mark) 1715 { 1716 Block *b; 1717 1718 if(r == nil){ 1719 memset(&e, 0, sizeof e); 1720 return 1; 1721 } 1722 1723 b = cacheGlobal(r->fs->cache, r->score, BtDir, r->tag, OReadOnly); 1724 if(b == nil) 1725 return 0; 1726 if(!entryUnpack(e, b->data, r->offset % r->epb)){ 1727 blockPut(b); 1728 return 0; 1729 } 1730 1731 if(mark) 1732 markCopied(b); 1733 blockPut(b); 1734 return 1; 1735 } 1736 1737 static int 1738 setEntry(Source *r, Entry *e) 1739 { 1740 Block *b; 1741 Entry oe; 1742 1743 b = cacheGlobal(r->fs->cache, r->score, BtDir, r->tag, OReadWrite); 1744 if(0) fprint(2, "setEntry: b %#ux %d score=%V\n", b->addr, r->offset % r->epb, e->score); 1745 if(b == nil) 1746 return 0; 1747 if(!entryUnpack(&oe, b->data, r->offset % r->epb)){ 1748 blockPut(b); 1749 return 0; 1750 } 1751 e->gen = oe.gen; 1752 entryPack(e, b->data, r->offset % r->epb); 1753 1754 /* BUG b should depend on the entry pointer */ 1755 1756 markCopied(b); 1757 blockDirty(b); 1758 blockPut(b); 1759 return 1; 1760 } 1761 1762 /* assumes hold elk */ 1763 int 1764 fileSnapshot(File *dst, File *src, u32int epoch, int doarchive) 1765 { 1766 Entry e, ee; 1767 1768 /* add link to snapshot */ 1769 if(!getEntry(src->source, &e, 1) || !getEntry(src->msource, &ee, 1)) 1770 return 0; 1771 1772 e.snap = epoch; 1773 e.archive = doarchive; 1774 ee.snap = epoch; 1775 ee.archive = doarchive; 1776 1777 if(!setEntry(dst->source, &e) || !setEntry(dst->msource, &ee)) 1778 return 0; 1779 return 1; 1780 } 1781 1782 int 1783 fileGetSources(File *f, Entry *e, Entry *ee, int mark) 1784 { 1785 if(!getEntry(f->source, e, mark) 1786 || !getEntry(f->msource, ee, mark)) 1787 return 0; 1788 return 1; 1789 } 1790 1791 int 1792 fileWalkSources(File *f) 1793 { 1794 if(f->mode == OReadOnly) 1795 return 1; 1796 if(!sourceLock2(f->source, f->msource, OReadWrite)) 1797 return 0; 1798 sourceUnlock(f->source); 1799 sourceUnlock(f->msource); 1800 return 1; 1801 } 1802