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