1 /* 2 * Copyright 2001-2008 pinc Software. All Rights Reserved. 3 */ 4 5 //! BFS Inode classes 6 7 8 #include "Inode.h" 9 #include "BPlusTree.h" 10 11 #include <Directory.h> 12 #include <SymLink.h> 13 #include <Entry.h> 14 #include <Path.h> 15 #include <String.h> 16 17 #include <new> 18 #include <stdlib.h> 19 #include <stdio.h> 20 #include <string.h> 21 22 23 class NodeGetter { 24 public: 25 NodeGetter(Inode* inode) 26 : 27 fInode(inode) 28 { 29 fInode->AcquireBuffer(); 30 } 31 32 ~NodeGetter() 33 { 34 fInode->ReleaseBuffer(); 35 } 36 37 private: 38 Inode* fInode; 39 }; 40 41 42 // #pragma mark - 43 44 45 Inode::Inode(Disk* disk, bfs_inode* inode, bool ownBuffer) 46 : 47 fDisk(disk), 48 fInode(inode), 49 fOwnBuffer(ownBuffer), 50 fPath(NULL), 51 fRefCount(1), 52 fCurrentSmallData(NULL), 53 fAttributes(NULL), 54 fAttributeBuffer(NULL) 55 { 56 if (inode != NULL) 57 fBlockRun = inode->inode_num; 58 } 59 60 61 Inode::Inode(const Inode& inode) 62 : 63 fDisk(inode.fDisk), 64 fInode(inode.fInode), 65 fOwnBuffer(false), 66 fPath(NULL), 67 fBlockRun(inode.fBlockRun), 68 fRefCount(1), 69 fCurrentSmallData(NULL), 70 fAttributes(NULL), 71 fAttributeBuffer(NULL) 72 { 73 } 74 75 76 Inode::~Inode() 77 { 78 _Unset(); 79 } 80 81 82 void 83 Inode::_Unset() 84 { 85 if (fOwnBuffer) 86 free(fInode); 87 88 fInode = NULL; 89 fBlockRun.SetTo(0, 0, 0); 90 91 free(fPath); 92 fPath = NULL; 93 94 delete fAttributes; 95 fAttributes = NULL; 96 } 97 98 99 status_t 100 Inode::SetTo(bfs_inode *inode) 101 { 102 _Unset(); 103 104 fInode = inode; 105 fBlockRun = inode->inode_num; 106 return B_OK; 107 } 108 109 110 status_t 111 Inode::InitCheck() 112 { 113 if (!fInode) 114 return B_ERROR; 115 116 // test inode magic and flags 117 if (fInode->magic1 != INODE_MAGIC1 118 || !(fInode->flags & INODE_IN_USE) 119 || fInode->inode_num.length != 1) 120 return B_ERROR; 121 122 if (fDisk->BlockSize()) { 123 // matches known block size? 124 if (fInode->inode_size != fDisk->SuperBlock()->inode_size 125 // parent resides on disk? 126 || fInode->parent.allocation_group > fDisk->SuperBlock()->num_ags 127 || fInode->parent.allocation_group < 0 128 || fInode->parent.start > (1L << fDisk->SuperBlock()->ag_shift) 129 || fInode->parent.length != 1 130 // attributes, too? 131 || fInode->attributes.allocation_group > fDisk->SuperBlock()->num_ags 132 || fInode->attributes.allocation_group < 0 133 || fInode->attributes.start > (1L << fDisk->SuperBlock()->ag_shift)) 134 return B_ERROR; 135 } else { 136 // is inode size one of the valid values? 137 switch (fInode->inode_size) { 138 case 1024: 139 case 2048: 140 case 4096: 141 case 8192: 142 break; 143 default: 144 return B_ERROR; 145 } 146 } 147 return B_OK; 148 // is inode on a boundary matching it's size? 149 //return (Offset() % fInode->inode_size) == 0 ? B_OK : B_ERROR; 150 } 151 152 153 status_t 154 Inode::CopyBuffer() 155 { 156 if (!fInode) 157 return B_ERROR; 158 159 bfs_inode *buffer = (bfs_inode *)malloc(fInode->inode_size); 160 if (!buffer) 161 return B_NO_MEMORY; 162 163 memcpy(buffer, fInode, fInode->inode_size); 164 fInode = buffer; 165 fOwnBuffer = true; 166 BufferClobbered(); 167 // this must not be deleted anymore 168 169 return B_OK; 170 } 171 172 173 /*static*/ bool 174 Inode::_LowMemory() 175 { 176 static bigtime_t lastChecked; 177 static int32 percentUsed; 178 179 if (system_time() > lastChecked + 1000000LL) { 180 system_info info; 181 get_system_info(&info); 182 percentUsed = 100 * info.used_pages / info.max_pages; 183 } 184 185 return percentUsed > 75; 186 } 187 188 189 void 190 Inode::ReleaseBuffer() 191 { 192 if (atomic_add(&fRefCount, -1) != 1) 193 return; 194 195 if (fOwnBuffer) { 196 if (!_LowMemory()) 197 return; 198 199 free(fInode); 200 fInode = NULL; 201 } 202 } 203 204 205 status_t 206 Inode::AcquireBuffer() 207 { 208 if (atomic_add(&fRefCount, 1) != 0) 209 return B_OK; 210 211 if (!fOwnBuffer || fInode != NULL) 212 return B_OK; 213 214 fInode = (bfs_inode*)malloc(fDisk->BlockSize()); 215 if (fInode == NULL) 216 return B_NO_MEMORY; 217 218 ssize_t bytesRead = fDisk->ReadAt(Offset(), fInode, fDisk->BlockSize()); 219 if (bytesRead < B_OK) 220 return bytesRead; 221 222 return B_OK; 223 } 224 225 226 void 227 Inode::BufferClobbered() 228 { 229 AcquireBuffer(); 230 } 231 232 233 void 234 Inode::SetParent(const block_run& run) 235 { 236 fInode->parent = run; 237 BufferClobbered(); 238 } 239 240 241 void 242 Inode::SetBlockRun(const block_run& run) 243 { 244 fInode->inode_num = run; 245 fBlockRun = run; 246 BufferClobbered(); 247 } 248 249 250 void 251 Inode::SetMode(uint32 mode) 252 { 253 fInode->mode = mode; 254 BufferClobbered(); 255 } 256 257 258 status_t 259 Inode::SetName(const char *name) 260 { 261 if (name == NULL || *name == '\0') 262 return B_BAD_VALUE; 263 264 small_data *data = fInode->small_data_start, *nameData = NULL; 265 BufferClobbered(); 266 267 while (!data->IsLast(fInode)) { 268 if (data->type == FILE_NAME_TYPE 269 && data->name_size == FILE_NAME_NAME_LENGTH 270 && *data->Name() == FILE_NAME_NAME) 271 nameData = data; 272 273 data = data->Next(); 274 } 275 276 int32 oldLength = nameData == NULL ? 0 : nameData->data_size; 277 int32 newLength = strlen(name) + (nameData == NULL ? sizeof(small_data) + 5 : 0); 278 279 if ((addr_t)data + newLength - oldLength >= (addr_t)(fInode 280 + fDisk->BlockSize())) 281 return B_NO_MEMORY; 282 283 if (nameData == NULL) { 284 memmove(newLength + (uint8 *)fInode->small_data_start, 285 fInode->small_data_start, 286 (addr_t)data - (addr_t)fInode->small_data_start); 287 nameData = fInode->small_data_start; 288 } else { 289 memmove(newLength + (uint8 *)nameData, nameData, 290 (addr_t)data - (addr_t)fInode->small_data_start); 291 } 292 293 memset(nameData, 0, sizeof(small_data) + 5 + strlen(name)); 294 nameData->type = FILE_NAME_TYPE; 295 nameData->name_size = FILE_NAME_NAME_LENGTH; 296 nameData->data_size = strlen(name); 297 *nameData->Name() = FILE_NAME_NAME; 298 strcpy((char *)nameData->Data(),name); 299 300 return B_OK; 301 } 302 303 304 const char * 305 Inode::Name() const 306 { 307 small_data *data = fInode->small_data_start; 308 while (!data->IsLast(fInode)) { 309 if (data->type == FILE_NAME_TYPE 310 && data->name_size == FILE_NAME_NAME_LENGTH 311 && *data->Name() == FILE_NAME_NAME) 312 return (const char *)data->Data(); 313 314 data = data->Next(); 315 } 316 return NULL; 317 } 318 319 320 status_t 321 Inode::GetNextSmallData(small_data **smallData) 322 { 323 if (!fInode) 324 return B_ERROR; 325 326 small_data *data = *smallData; 327 328 // begin from the start? 329 if (data == NULL) 330 data = fInode->small_data_start; 331 else 332 data = data->Next(); 333 334 // is already last item? 335 if (data->IsLast(fInode)) 336 return B_ENTRY_NOT_FOUND; 337 338 *smallData = data; 339 return B_OK; 340 } 341 342 343 status_t 344 Inode::RewindAttributes() 345 { 346 fCurrentSmallData = NULL; 347 348 if (fAttributes != NULL) 349 fAttributes->Rewind(); 350 351 return B_OK; 352 } 353 354 355 status_t 356 Inode::GetNextAttribute(char *name, uint32 *type, void **data, size_t *length) 357 { 358 // read attributes out of the small data section 359 360 if (fCurrentSmallData == NULL || !fCurrentSmallData->IsLast(fInode)) { 361 if (fCurrentSmallData == NULL) 362 fCurrentSmallData = fInode->small_data_start; 363 else 364 fCurrentSmallData = fCurrentSmallData->Next(); 365 366 // skip name attribute 367 if (!fCurrentSmallData->IsLast(fInode) 368 && fCurrentSmallData->name_size == FILE_NAME_NAME_LENGTH 369 && *fCurrentSmallData->Name() == FILE_NAME_NAME) 370 fCurrentSmallData = fCurrentSmallData->Next(); 371 372 if (!fCurrentSmallData->IsLast(fInode)) { 373 strncpy(name,fCurrentSmallData->Name(), B_FILE_NAME_LENGTH); 374 *type = fCurrentSmallData->type; 375 *data = fCurrentSmallData->Data(); 376 *length = fCurrentSmallData->data_size; 377 378 return B_OK; 379 } 380 } 381 382 // read attributes out of the attribute directory 383 384 if (Attributes().IsZero()) 385 return B_ENTRY_NOT_FOUND; 386 387 if (fAttributes == NULL) 388 fAttributes = (Directory *)Inode::Factory(fDisk, Attributes()); 389 390 status_t status = fAttributes ? fAttributes->InitCheck() : B_ERROR; 391 if (status < B_OK) 392 return status; 393 394 block_run run; 395 status = fAttributes->GetNextEntry(name, &run); 396 if (status < B_OK) { 397 free(fAttributeBuffer); 398 fAttributeBuffer = NULL; 399 return status; 400 } 401 402 Attribute *attribute = (Attribute *)Inode::Factory(fDisk, run); 403 if (attribute == NULL || attribute->InitCheck() < B_OK) 404 return B_IO_ERROR; 405 406 *type = attribute->Type(); 407 408 void *buffer = realloc(fAttributeBuffer, attribute->Size()); 409 if (buffer == NULL) { 410 free(fAttributeBuffer); 411 fAttributeBuffer = NULL; 412 delete attribute; 413 return B_NO_MEMORY; 414 } 415 fAttributeBuffer = buffer; 416 417 ssize_t size = attribute->Read(fAttributeBuffer, attribute->Size()); 418 delete attribute; 419 420 *length = size; 421 *data = fAttributeBuffer; 422 423 return size < B_OK ? size : B_OK; 424 } 425 426 427 status_t 428 Inode::_FindPath(Inode::Source *source) 429 { 430 BString path; 431 432 block_run parent = Parent(); 433 while (!parent.IsZero() && parent != fDisk->Root()) { 434 Inode *inode; 435 if (source) 436 inode = source->InodeAt(parent); 437 else 438 inode = Inode::Factory(fDisk, parent); 439 440 if (inode == NULL 441 || inode->InitCheck() < B_OK 442 || inode->Name() == NULL 443 || !*inode->Name()) { 444 BString sub; 445 sub << "__recovered " << parent.allocation_group << ":" 446 << (int32)parent.start << "/"; 447 path.Prepend(sub); 448 449 delete inode; 450 break; 451 } 452 parent = inode->Parent(); 453 path.Prepend("/"); 454 path.Prepend(inode->Name()); 455 456 delete inode; 457 } 458 fPath = strdup(path.String()); 459 460 return B_OK; 461 } 462 463 464 const char * 465 Inode::Path(Inode::Source *source) 466 { 467 if (fPath == NULL) 468 _FindPath(source); 469 470 return fPath; 471 } 472 473 474 status_t 475 Inode::CopyTo(const char *root, bool fullPath, Inode::Source *source) 476 { 477 if (root == NULL) 478 return B_ENTRY_NOT_FOUND; 479 480 BString path; 481 482 if (fullPath) 483 path.Append(Path(source)); 484 485 if (*(root + strlen(root) - 1) != '/') 486 path.Prepend("/"); 487 path.Prepend(root); 488 489 return create_directory(path.String(), 0777); 490 } 491 492 493 status_t 494 Inode::CopyAttributesTo(BNode *node) 495 { 496 // copy attributes 497 498 RewindAttributes(); 499 500 char name[B_FILE_NAME_LENGTH]; 501 const uint32 kMaxBrokenAttributes = 64; 502 // sanity max value 503 uint32 count = 0; 504 uint32 type; 505 void *data; 506 size_t size; 507 508 status_t status; 509 while ((status = GetNextAttribute(name, &type, &data, &size)) 510 != B_ENTRY_NOT_FOUND) { 511 if (status != B_OK) { 512 printf("could not open attribute (possibly: %s): %s!\n", 513 name, strerror(status)); 514 if (count++ > kMaxBrokenAttributes) 515 break; 516 517 continue; 518 } 519 520 ssize_t written = node->WriteAttr(name, type, 0, data, size); 521 if (written < B_OK) { 522 printf("could not write attribute \"%s\": %s\n", name, 523 strerror(written)); 524 } else if ((size_t)written < size) { 525 printf("could only write %ld bytes (from %ld) at attribute \"%s\"\n", 526 written, size, name); 527 } 528 } 529 530 // copy stats 531 532 node->SetPermissions(fInode->mode); 533 node->SetOwner(fInode->uid); 534 node->SetGroup(fInode->gid); 535 node->SetModificationTime(fInode->last_modified_time >> 16); 536 node->SetCreationTime(fInode->create_time >> 16); 537 538 return B_OK; 539 } 540 541 542 Inode * 543 Inode::Factory(Disk *disk, bfs_inode *inode, bool ownBuffer) 544 { 545 // attributes (of a file) 546 if ((inode->mode & (S_ATTR | S_ATTR_DIR)) == S_ATTR) 547 return new Attribute(disk, inode, ownBuffer); 548 549 // directories, attribute directories, indices 550 if (S_ISDIR(inode->mode) || inode->mode & S_ATTR_DIR) 551 return new Directory(disk, inode, ownBuffer); 552 553 // regular files 554 if (S_ISREG(inode->mode)) 555 return new File(disk, inode, ownBuffer); 556 557 // symlinks (short and link in data-stream) 558 if (S_ISLNK(inode->mode)) 559 return new Symlink(disk, inode, ownBuffer); 560 561 return NULL; 562 } 563 564 565 Inode * 566 Inode::Factory(Disk *disk, block_run run) 567 { 568 bfs_inode *inode = (bfs_inode *)malloc(disk->BlockSize()); 569 if (!inode) 570 return NULL; 571 572 if (disk->ReadAt(disk->ToOffset(run), inode, disk->BlockSize()) <= 0) 573 return NULL; 574 575 Inode *object = Factory(disk, inode); 576 if (object == NULL) 577 free(inode); 578 579 return object; 580 } 581 582 583 Inode * 584 Inode::Factory(Disk *disk, Inode *inode, bool copyBuffer) 585 { 586 bfs_inode *inodeBuffer = inode->fInode; 587 588 if (copyBuffer) { 589 bfs_inode *inodeCopy = (bfs_inode *)malloc(inodeBuffer->inode_size); 590 if (!inodeCopy) 591 return NULL; 592 593 memcpy(inodeCopy, inodeBuffer, inodeBuffer->inode_size); 594 inodeBuffer = inodeCopy; 595 } 596 return Factory(disk, inodeBuffer, copyBuffer); 597 } 598 599 600 Inode * 601 Inode::EmptyInode(Disk *disk, const char *name, int32 mode) 602 { 603 bfs_inode *inode = (bfs_inode *)malloc(disk->BlockSize()); 604 if (!inode) 605 return NULL; 606 607 memset(inode, 0, sizeof(bfs_inode)); 608 609 inode->magic1 = INODE_MAGIC1; 610 inode->inode_size = disk->BlockSize(); 611 inode->mode = mode; 612 inode->flags = INODE_IN_USE | (mode & S_IFDIR ? INODE_LOGGED : 0); 613 614 if (name) { 615 small_data *data = inode->small_data_start; 616 data->type = FILE_NAME_TYPE; 617 data->name_size = FILE_NAME_NAME_LENGTH; 618 *data->Name() = FILE_NAME_NAME; 619 data->data_size = strlen(name); 620 strcpy((char *)data->Data(), name); 621 } 622 623 Inode *object = new (std::nothrow) Inode(disk, inode); 624 if (object == NULL) 625 free(inode); 626 627 object->AcquireBuffer(); 628 // this must not be deleted anymore! 629 return object; 630 } 631 632 633 // #pragma mark - 634 635 636 DataStream::DataStream(Disk *disk, bfs_inode *inode, bool ownBuffer) 637 : Inode(disk,inode,ownBuffer), 638 fCurrent(-1), 639 fPosition(0LL) 640 { 641 } 642 643 644 DataStream::DataStream(const Inode &inode) 645 : Inode(inode), 646 fCurrent(-1), 647 fPosition(0LL) 648 { 649 } 650 651 652 DataStream::~DataStream() 653 { 654 } 655 656 657 status_t 658 DataStream::FindBlockRun(off_t pos) 659 { 660 NodeGetter _(this); 661 662 if (pos > fInode->data.size) 663 return B_ENTRY_NOT_FOUND; 664 665 if (fCurrent < 0) 666 fLevel = 0; 667 668 fRunBlockEnd = fCurrent >= 0 669 ? fRunFileOffset + (fRun.length << fDisk->BlockShift()) : 0LL; 670 671 // access in current block run? 672 673 if (fCurrent >= 0 && pos >= fRunFileOffset && pos < fRunBlockEnd) 674 return B_OK; 675 676 // find matching block run 677 678 if (fInode->data.max_direct_range > 0 679 && pos >= fInode->data.max_direct_range) { 680 if (fInode->data.max_double_indirect_range > 0 681 && pos >= fInode->data.max_indirect_range) { 682 // read from double indirect blocks 683 684 //printf("find double indirect block: %ld,%d!\n",fInode->data.double_indirect.allocation_group,fInode->data.double_indirect.start); 685 block_run *indirect = (block_run *)fDisk->ReadBlockRun(fInode->data.double_indirect); 686 if (indirect == NULL) 687 return B_ERROR; 688 689 off_t start = pos - fInode->data.max_indirect_range; 690 int32 indirectSize = fDisk->BlockSize() * 16 * (fDisk->BlockSize() / sizeof(block_run)); 691 int32 directSize = fDisk->BlockSize() * 4; 692 int32 index = start / indirectSize; 693 694 //printf("\tstart = %Ld, indirectSize = %ld, directSize = %ld, index = %ld\n",start,indirectSize,directSize,index); 695 //printf("\tlook for indirect block at %ld,%d\n",indirect[index].allocation_group,indirect[index].start); 696 indirect = (block_run *)fDisk->ReadBlockRun(indirect[index]); 697 if (indirect == NULL) 698 return B_ERROR; 699 700 fCurrent = (start % indirectSize) / directSize; 701 fRunFileOffset = fInode->data.max_indirect_range + (index * indirectSize) + (fCurrent * directSize); 702 fRunBlockEnd = fRunFileOffset + directSize; 703 fRun = indirect[fCurrent]; 704 //printf("\tfCurrent = %ld, fRunFileOffset = %Ld, fRunBlockEnd = %Ld, fRun = %ld,%d\n",fCurrent,fRunFileOffset,fRunBlockEnd,fRun.allocation_group,fRun.start); 705 } else { 706 // access from indirect blocks 707 708 block_run *indirect = (block_run *)fDisk->ReadBlockRun(fInode->data.indirect); 709 if (!indirect) 710 return B_ERROR; 711 712 int32 indirectRuns = (fInode->data.indirect.length << fDisk->BlockShift()) / sizeof(block_run); 713 714 if (fLevel != 1 || pos < fRunFileOffset) { 715 fRunBlockEnd = fInode->data.max_direct_range; 716 fCurrent = -1; 717 fLevel = 1; 718 } 719 720 while (++fCurrent < indirectRuns) { 721 if (indirect[fCurrent].IsZero()) 722 break; 723 724 fRunFileOffset = fRunBlockEnd; 725 fRunBlockEnd += indirect[fCurrent].length << fDisk->BlockShift(); 726 if (fRunBlockEnd > pos) 727 break; 728 } 729 if (fCurrent == indirectRuns || indirect[fCurrent].IsZero()) 730 return B_ERROR; 731 732 fRun = indirect[fCurrent]; 733 //printf("reading from indirect block: %ld,%d\n",fRun.allocation_group,fRun.start); 734 //printf("### indirect-run[%ld] = (%ld,%d,%d), offset = %Ld\n",fCurrent,fRun.allocation_group,fRun.start,fRun.length,fRunFileOffset); 735 } 736 } else { 737 // access from direct blocks 738 if (fRunFileOffset > pos) { 739 fRunBlockEnd = 0LL; 740 fCurrent = -1; 741 } 742 fLevel = 0; 743 744 while (++fCurrent < NUM_DIRECT_BLOCKS) { 745 if (fInode->data.direct[fCurrent].IsZero()) 746 break; 747 748 fRunFileOffset = fRunBlockEnd; 749 fRunBlockEnd += fInode->data.direct[fCurrent].length << fDisk->BlockShift(); 750 if (fRunBlockEnd > pos) 751 break; 752 } 753 if (fCurrent == NUM_DIRECT_BLOCKS || fInode->data.direct[fCurrent].IsZero()) 754 return B_ERROR; 755 756 fRun = fInode->data.direct[fCurrent]; 757 //printf("### run[%ld] = (%ld,%d,%d), offset = %Ld\n",fCurrent,fRun.allocation_group,fRun.start,fRun.length,fRunFileOffset); 758 } 759 return B_OK; 760 } 761 762 763 ssize_t 764 DataStream::ReadAt(off_t pos, void *buffer, size_t size) 765 { 766 NodeGetter _(this); 767 768 //printf("DataStream::ReadAt(pos = %Ld,buffer = %p,size = %ld);\n",pos,buffer,size); 769 // truncate size to read 770 if (pos + (off_t)size > fInode->data.size) { 771 if (pos > fInode->data.size) // reading outside the file 772 return B_ERROR; 773 774 size = fInode->data.size - pos; 775 if (!size) // there is nothing left to read 776 return 0; 777 } 778 ssize_t read = 0; 779 780 //printf("### read %ld bytes at %Ld\n",size,pos); 781 while (size > 0) { 782 status_t status = FindBlockRun(pos); 783 if (status < B_OK) 784 return status; 785 786 ssize_t bytes = min_c((off_t)size, fRunBlockEnd - pos); 787 788 //printf("### read %ld bytes from %Ld\n### --\n",bytes,fDisk->ToOffset(fRun) + pos - fRunFileOffset); 789 bytes = fDisk->ReadAt(fDisk->ToOffset(fRun) + pos - fRunFileOffset, 790 buffer, bytes); 791 if (bytes <= 0) { 792 if (bytes == 0) { 793 printf("could not read bytes at: %" B_PRId32 ",%d\n", 794 fRun.allocation_group, fRun.start); 795 } 796 return bytes < 0 ? bytes : B_BAD_DATA; 797 } 798 799 buffer = (void *)((uint8 *)buffer + bytes); 800 size -= bytes; 801 pos += bytes; 802 read += bytes; 803 } 804 if (read >= 0) 805 return read; 806 807 return B_IO_ERROR; 808 } 809 810 811 ssize_t 812 DataStream::WriteAt(off_t pos, const void *buffer, size_t size) 813 { 814 NodeGetter _(this); 815 816 // FIXME: truncate size -> should enlargen the file 817 if (pos + (off_t)size > fInode->data.size) { 818 if (pos > fInode->data.size) // writing outside the file 819 return B_ERROR; 820 821 size = fInode->data.size - pos; 822 if (!size) // there is nothing left to write 823 return 0; 824 } 825 ssize_t written = 0; 826 827 //printf("### write %ld bytes at %Ld\n",size,pos); 828 while (size > 0) { 829 status_t status = FindBlockRun(pos); 830 if (status < B_OK) 831 return status; 832 833 ssize_t bytes = min_c((off_t)size, fRunBlockEnd - pos); 834 835 //printf("### write %ld bytes to %Ld\n### --\n",bytes,fDisk->ToOffset(fRun) + pos - fRunFileOffset); 836 bytes = fDisk->WriteAt(fDisk->ToOffset(fRun) + pos - fRunFileOffset,buffer,bytes); 837 if (bytes < 0) 838 return bytes; 839 840 buffer = (void *)((uint8 *)buffer + bytes); 841 size -= bytes; 842 pos += bytes; 843 written += bytes; 844 } 845 if (written >= 0) 846 return written; 847 848 return B_IO_ERROR; 849 } 850 851 852 off_t 853 DataStream::Seek(off_t position, uint32 seekMode) 854 { 855 NodeGetter _(this); 856 857 if (seekMode == SEEK_SET) 858 fPosition = position; 859 else if (seekMode == SEEK_END) 860 fPosition = fInode->data.size + position; 861 else 862 fPosition += position; 863 864 return fPosition; 865 } 866 867 868 off_t 869 DataStream::Position() const 870 { 871 return fPosition; 872 } 873 874 875 status_t 876 DataStream::SetSize(off_t size) 877 { 878 NodeGetter _(this); 879 880 // FIXME: not yet supported 881 if (size > fInode->data.size || size > fInode->data.max_direct_range) 882 return B_ERROR; 883 884 if (size == fInode->data.size) 885 return B_OK; 886 887 BufferClobbered(); 888 889 fInode->data.size = size; 890 fInode->data.max_direct_range = size; 891 fInode->data.max_indirect_range = 0; 892 fInode->data.max_double_indirect_range = 0; 893 894 for (int32 i = 0;i < NUM_DIRECT_BLOCKS;i++) { 895 if (size <= 0) 896 fInode->data.direct[i].SetTo(0, 0, 0); 897 else if ((fInode->data.direct[i].length << fDisk->BlockShift()) >= size) { 898 off_t blocks = (size + fDisk->BlockSize() - 1) / fDisk->BlockSize(); 899 fInode->data.direct[i].length = blocks; 900 size = 0; 901 } else 902 size -= fInode->data.direct[i].length << fDisk->BlockShift(); 903 } 904 905 return B_OK; 906 } 907 908 909 // #pragma mark - 910 911 912 File::File(Disk *disk, bfs_inode *inode,bool ownBuffer) 913 : DataStream(disk,inode,ownBuffer) 914 { 915 } 916 917 918 File::File(const Inode &inode) 919 : DataStream(inode) 920 { 921 } 922 923 924 File::~File() 925 { 926 } 927 928 929 status_t 930 File::InitCheck() 931 { 932 status_t status = DataStream::InitCheck(); 933 if (status == B_OK) 934 return IsFile() ? B_OK : B_ERROR; 935 936 return status; 937 } 938 939 940 status_t 941 File::CopyTo(const char *root, bool fullPath, Inode::Source *source) 942 { 943 status_t status = Inode::CopyTo(root, fullPath, source); 944 if (status < B_OK) 945 return status; 946 947 BPath path(root); 948 if (fullPath && Path(source)) 949 path.Append(Path(source)); 950 951 char *name = (char *)Name(); 952 if (name != NULL) { 953 // changes the filename in the inode buffer (for deleted entries) 954 if (!*name) 955 *name = '_'; 956 path.Append(name); 957 } else { 958 BString sub; 959 sub << "__untitled " << BlockRun().allocation_group << ":" 960 << (int32)BlockRun().start; 961 path.Append(sub.String()); 962 } 963 printf("%" B_PRId32 ",%d -> %s\n", BlockRun().allocation_group, 964 BlockRun().start, path.Path()); 965 966 BFile file; 967 status = file.SetTo(path.Path(), 968 B_WRITE_ONLY | B_CREATE_FILE | B_FAIL_IF_EXISTS); 969 if (status < B_OK) 970 return status; 971 972 char buffer[fDisk->BlockSize()]; 973 ssize_t size; 974 Seek(0, SEEK_SET); 975 976 while ((size = Read(buffer, sizeof(buffer))) > B_OK) { 977 ssize_t written = file.Write(buffer, size); 978 if (written < B_OK) 979 return written; 980 } 981 982 return CopyAttributesTo(&file); 983 } 984 985 986 // #pragma mark - 987 988 989 Attribute::Attribute(Disk *disk, bfs_inode *inode, bool ownBuffer) 990 : File(disk, inode, ownBuffer) 991 { 992 } 993 994 995 Attribute::Attribute(const Inode &inode) 996 : File(inode) 997 { 998 } 999 1000 1001 Attribute::~Attribute() 1002 { 1003 } 1004 1005 1006 status_t 1007 Attribute::InitCheck() 1008 { 1009 status_t status = DataStream::InitCheck(); 1010 if (status == B_OK) 1011 return IsAttribute() ? B_OK : B_ERROR; 1012 1013 return status; 1014 } 1015 1016 1017 status_t 1018 Attribute::CopyTo(const char */*path*/, bool /*fullPath*/, 1019 Inode::Source */*source*/) 1020 { 1021 // files and directories already copy all attributes 1022 1023 // eventually, this method should be implemented to recover lost 1024 // attributes on the disk 1025 1026 return B_OK; 1027 } 1028 1029 1030 // #pragma mark - 1031 1032 1033 Directory::Directory(Disk *disk, bfs_inode *inode, bool ownBuffer) 1034 : DataStream(disk, inode, ownBuffer), 1035 fTree(NULL) 1036 { 1037 } 1038 1039 1040 Directory::Directory(const Inode &inode) 1041 : DataStream(inode), 1042 fTree(NULL) 1043 { 1044 } 1045 1046 1047 Directory::~Directory() 1048 { 1049 delete fTree; 1050 } 1051 1052 1053 status_t 1054 Directory::InitCheck() 1055 { 1056 status_t status = DataStream::InitCheck(); 1057 if (status == B_OK) 1058 return (IsDirectory() || IsAttributeDirectory()) ? B_OK : B_ERROR; 1059 1060 return status; 1061 } 1062 1063 1064 status_t 1065 Directory::CopyTo(const char *root, bool fullPath, Inode::Source *source) 1066 { 1067 // don't copy attributes or indices 1068 // the recovery program should make empty files to recover lost attributes 1069 if (IsAttributeDirectory() || IsIndex()) 1070 return B_OK; 1071 1072 status_t status = Inode::CopyTo(root, fullPath, source); 1073 if (status < B_OK) 1074 return status; 1075 1076 BPath path(root); 1077 if (fullPath && Path(source)) 1078 path.Append(Path(source)); 1079 1080 char *name = (char *)Name(); 1081 if (name != NULL) { 1082 // changes the filename in the inode buffer (for deleted entries) 1083 if (!*name) 1084 *name = '_'; 1085 path.Append(name); 1086 } else { 1087 // create unique name 1088 BString sub; 1089 sub << "__untitled " << BlockRun().allocation_group << ":" 1090 << (int32)BlockRun().start; 1091 path.Append(sub.String()); 1092 } 1093 1094 BEntry entry(path.Path()); 1095 BDirectory directory; 1096 if ((status = entry.GetParent(&directory)) < B_OK) 1097 return status; 1098 1099 status = directory.CreateDirectory(path.Leaf(), NULL); 1100 if (status < B_OK && status != B_FILE_EXISTS) 1101 return status; 1102 1103 if ((status = directory.SetTo(&entry)) < B_OK) 1104 return status; 1105 1106 return CopyAttributesTo(&directory); 1107 } 1108 1109 1110 status_t 1111 Directory::Rewind() 1112 { 1113 if (!fTree) { 1114 status_t status = CreateTree(); 1115 if (status < B_OK) 1116 return status; 1117 } 1118 return fTree->Rewind(); 1119 } 1120 1121 1122 status_t 1123 Directory::GetNextEntry(char *name, block_run *run) 1124 { 1125 status_t status; 1126 1127 if (!fTree) { 1128 if ((status = Rewind()) < B_OK) 1129 return status; 1130 } 1131 uint16 length; 1132 off_t offset; 1133 1134 if ((status = fTree->GetNextEntry(name, &length, B_FILE_NAME_LENGTH - 1, 1135 &offset)) < B_OK) 1136 return status; 1137 1138 *run = fDisk->ToBlockRun(offset); 1139 1140 return B_OK; 1141 } 1142 1143 1144 status_t 1145 Directory::GetNextEntry(block_run *run) 1146 { 1147 char name[B_FILE_NAME_LENGTH]; 1148 1149 return GetNextEntry(name, run); 1150 } 1151 1152 1153 status_t 1154 Directory::Contains(const block_run *run) 1155 { 1156 status_t status; 1157 1158 if (!fTree) { 1159 if ((status = Rewind()) < B_OK) 1160 return status; 1161 } 1162 1163 block_run searchRun; 1164 while (GetNextEntry(&searchRun) == B_OK) { 1165 if (searchRun == *run) 1166 return B_OK; 1167 } 1168 1169 return B_ENTRY_NOT_FOUND; 1170 } 1171 1172 1173 status_t 1174 Directory::Contains(const Inode *inode) 1175 { 1176 status_t status; 1177 1178 if (!fTree) { 1179 if ((status = CreateTree()) < B_OK) 1180 return status; 1181 } 1182 1183 off_t value; 1184 const char *name = inode->Name(); 1185 status = B_ENTRY_NOT_FOUND; 1186 1187 if (name && (status = fTree->Find((uint8 *)name, (uint16)strlen(name), 1188 &value)) == B_OK) { 1189 if (fDisk->ToBlockRun(value) == inode->InodeBuffer()->inode_num) 1190 return B_OK; 1191 1192 printf("inode address do not match (%s)!\n", inode->Name()); 1193 } 1194 1195 if (status != B_OK && status != B_ENTRY_NOT_FOUND) 1196 return status; 1197 1198 return Contains(&inode->InodeBuffer()->inode_num); 1199 } 1200 1201 1202 status_t 1203 Directory::FindEntry(const char *name, block_run *run) 1204 { 1205 status_t status; 1206 1207 if (!name) 1208 return B_BAD_VALUE; 1209 1210 if (!fTree) { 1211 if ((status = CreateTree()) < B_OK) 1212 return status; 1213 } 1214 1215 off_t value; 1216 1217 if ((status = fTree->Find((uint8 *)name, (uint16)strlen(name), 1218 &value)) >= B_OK) { 1219 if (run) 1220 *run = fDisk->ToBlockRun(value); 1221 return B_OK; 1222 } 1223 return status; 1224 } 1225 1226 1227 status_t 1228 Directory::AddEntry(Inode *inode) 1229 { 1230 status_t status; 1231 bool created = false; 1232 1233 if (!fTree) { 1234 status = CreateTree(); 1235 if (status == B_OK) 1236 status = fTree->Validate(); 1237 1238 if (status == B_BAD_DATA) { 1239 //puts("bplustree corrupted!"); 1240 fTree = new BPlusTree(BPLUSTREE_STRING_TYPE, BPLUSTREE_NODE_SIZE, 1241 false); 1242 if ((status = fTree->InitCheck()) < B_OK) { 1243 delete fTree; 1244 fTree = NULL; 1245 } else 1246 created = true; 1247 } 1248 1249 if (status < B_OK) 1250 return status; 1251 } 1252 // keep all changes in memory 1253 fTree->SetHoldChanges(true); 1254 1255 if (created) { 1256 // add . and .. 1257 fTree->Insert(".", Block()); 1258 fTree->Insert("..", fDisk->ToBlock(Parent())); 1259 } 1260 1261 if (inode->Flags() & INODE_DELETED) 1262 return B_ENTRY_NOT_FOUND; 1263 1264 BString name = inode->Name(); 1265 if (name == "") { 1266 name << "__file " << inode->BlockRun().allocation_group << ":" 1267 << (int32)inode->BlockRun().start; 1268 } 1269 1270 return fTree->Insert(name.String(), inode->Block()); 1271 } 1272 1273 1274 status_t 1275 Directory::CreateTree() 1276 { 1277 fTree = new BPlusTree(this); 1278 1279 status_t status = fTree->InitCheck(); 1280 if (status < B_OK) { 1281 delete fTree; 1282 fTree = NULL; 1283 return status; 1284 } 1285 return B_OK; 1286 } 1287 1288 1289 status_t 1290 Directory::GetTree(BPlusTree **tree) 1291 { 1292 if (!fTree) { 1293 status_t status = CreateTree(); 1294 if (status < B_OK) 1295 return status; 1296 } 1297 *tree = fTree; 1298 return B_OK; 1299 } 1300 1301 1302 // #pragma mark - 1303 1304 1305 Symlink::Symlink(Disk *disk, bfs_inode *inode,bool ownBuffer) 1306 : Inode(disk,inode,ownBuffer) 1307 { 1308 } 1309 1310 1311 Symlink::Symlink(const Inode &inode) 1312 : Inode(inode) 1313 { 1314 } 1315 1316 1317 Symlink::~Symlink() 1318 { 1319 } 1320 1321 1322 status_t 1323 Symlink::InitCheck() 1324 { 1325 status_t status = Inode::InitCheck(); 1326 if (status == B_OK) 1327 return IsSymlink() ? B_OK : B_ERROR; 1328 1329 return status; 1330 } 1331 1332 1333 status_t 1334 Symlink::CopyTo(const char *root, bool fullPath,Inode::Source *source) 1335 { 1336 status_t status = Inode::CopyTo(root,fullPath,source); 1337 if (status < B_OK) 1338 return status; 1339 1340 BPath path(root); 1341 if (fullPath && Path(source)) 1342 path.Append(Path(source)); 1343 1344 char *name = (char *)Name(); 1345 if (name != NULL) { 1346 // changes the filename in the inode buffer (for deleted entries) 1347 if (!*name) 1348 *name = '_'; 1349 path.Append(name); 1350 } else { 1351 // create unique name 1352 BString sub; 1353 sub << "__symlink " << BlockRun().allocation_group << ":" 1354 << (int32)BlockRun().start; 1355 path.Append(sub.String()); 1356 } 1357 1358 BEntry entry(path.Path()); 1359 BDirectory directory; 1360 if ((status = entry.GetParent(&directory)) < B_OK) 1361 return status; 1362 1363 char to[2048]; 1364 if (LinksTo(to,sizeof(to)) < B_OK) 1365 return B_ERROR; 1366 1367 BSymLink link; 1368 status = directory.CreateSymLink(path.Leaf(),to,&link); 1369 if (status < B_OK && status != B_FILE_EXISTS) 1370 return status; 1371 1372 if ((status = link.SetTo(&entry)) < B_OK) 1373 return status; 1374 1375 return CopyAttributesTo(&link); 1376 } 1377 1378 1379 status_t 1380 Symlink::LinksTo(char *to,size_t maxLength) 1381 { 1382 if ((fInode->flags & INODE_LONG_SYMLINK) == 0) { 1383 strcpy(to,fInode->short_symlink); 1384 return B_OK; 1385 } 1386 1387 DataStream stream(*this); 1388 status_t status = stream.InitCheck(); 1389 if (status < B_OK) 1390 return status; 1391 1392 status = stream.Read(to,maxLength); 1393 1394 return status < B_OK ? status : B_OK; 1395 } 1396 1397