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() const 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 if (InitCheck() != B_OK) { 308 puts("Not getting name because node is invalid"); 309 return NULL; 310 } 311 small_data *data = fInode->small_data_start; 312 while (!data->IsLast(fInode)) { 313 if (data->type == FILE_NAME_TYPE 314 && data->name_size == FILE_NAME_NAME_LENGTH 315 && *data->Name() == FILE_NAME_NAME) 316 return (const char *)data->Data(); 317 318 data = data->Next(); 319 } 320 return NULL; 321 } 322 323 324 status_t 325 Inode::GetNextSmallData(small_data **smallData) 326 { 327 if (!fInode) 328 return B_ERROR; 329 330 small_data *data = *smallData; 331 332 // begin from the start? 333 if (data == NULL) 334 data = fInode->small_data_start; 335 else 336 data = data->Next(); 337 338 // is already last item? 339 if (data->IsLast(fInode)) 340 return B_ENTRY_NOT_FOUND; 341 342 *smallData = data; 343 return B_OK; 344 } 345 346 347 status_t 348 Inode::RewindAttributes() 349 { 350 fCurrentSmallData = NULL; 351 352 if (fAttributes != NULL) 353 fAttributes->Rewind(); 354 355 return B_OK; 356 } 357 358 359 status_t 360 Inode::GetNextAttribute(char *name, uint32 *type, void **data, size_t *length) 361 { 362 // read attributes out of the small data section 363 364 if (fCurrentSmallData == NULL || !fCurrentSmallData->IsLast(fInode)) { 365 if (fCurrentSmallData == NULL) 366 fCurrentSmallData = fInode->small_data_start; 367 else 368 fCurrentSmallData = fCurrentSmallData->Next(); 369 370 // skip name attribute 371 if (!fCurrentSmallData->IsLast(fInode) 372 && fCurrentSmallData->name_size == FILE_NAME_NAME_LENGTH 373 && *fCurrentSmallData->Name() == FILE_NAME_NAME) 374 fCurrentSmallData = fCurrentSmallData->Next(); 375 376 if (!fCurrentSmallData->IsLast(fInode)) { 377 strncpy(name,fCurrentSmallData->Name(), B_FILE_NAME_LENGTH); 378 *type = fCurrentSmallData->type; 379 *data = fCurrentSmallData->Data(); 380 *length = fCurrentSmallData->data_size; 381 382 return B_OK; 383 } 384 } 385 386 // read attributes out of the attribute directory 387 388 if (Attributes().IsZero()) 389 return B_ENTRY_NOT_FOUND; 390 391 if (fAttributes == NULL) 392 fAttributes = (Directory *)Inode::Factory(fDisk, Attributes()); 393 394 status_t status = fAttributes ? fAttributes->InitCheck() : B_ERROR; 395 if (status < B_OK) 396 return status; 397 398 block_run run; 399 status = fAttributes->GetNextEntry(name, &run); 400 if (status < B_OK) { 401 free(fAttributeBuffer); 402 fAttributeBuffer = NULL; 403 return status; 404 } 405 406 Attribute *attribute = (Attribute *)Inode::Factory(fDisk, run); 407 if (attribute == NULL || attribute->InitCheck() < B_OK) 408 return B_IO_ERROR; 409 410 *type = attribute->Type(); 411 412 void *buffer = realloc(fAttributeBuffer, attribute->Size()); 413 if (buffer == NULL) { 414 free(fAttributeBuffer); 415 fAttributeBuffer = NULL; 416 delete attribute; 417 return B_NO_MEMORY; 418 } 419 fAttributeBuffer = buffer; 420 421 ssize_t size = attribute->Read(fAttributeBuffer, attribute->Size()); 422 delete attribute; 423 424 *length = size; 425 *data = fAttributeBuffer; 426 427 return size < B_OK ? size : B_OK; 428 } 429 430 431 status_t 432 Inode::_FindPath(Inode::Source *source) 433 { 434 BString path; 435 436 block_run parent = Parent(); 437 while (!parent.IsZero() && parent != fDisk->Root()) { 438 Inode *inode; 439 if (source) 440 inode = source->InodeAt(parent); 441 else 442 inode = Inode::Factory(fDisk, parent); 443 444 if (inode == NULL 445 || inode->InitCheck() < B_OK 446 || inode->Name() == NULL 447 || !*inode->Name()) { 448 BString sub; 449 sub << "__recovered " << parent.allocation_group << ":" 450 << (int32)parent.start << "/"; 451 path.Prepend(sub); 452 453 delete inode; 454 break; 455 } 456 parent = inode->Parent(); 457 path.Prepend("/"); 458 path.Prepend(inode->Name()); 459 460 delete inode; 461 } 462 fPath = strdup(path.String()); 463 464 return B_OK; 465 } 466 467 468 const char * 469 Inode::Path(Inode::Source *source) 470 { 471 if (fPath == NULL) 472 _FindPath(source); 473 474 return fPath; 475 } 476 477 478 status_t 479 Inode::CopyTo(const char *root, bool fullPath, Inode::Source *source) 480 { 481 if (root == NULL) 482 return B_ENTRY_NOT_FOUND; 483 484 BString path; 485 486 if (fullPath) 487 path.Append(Path(source)); 488 489 if (*(root + strlen(root) - 1) != '/') 490 path.Prepend("/"); 491 path.Prepend(root); 492 493 return create_directory(path.String(), 0777); 494 } 495 496 497 status_t 498 Inode::CopyAttributesTo(BNode *node) 499 { 500 // copy attributes 501 502 RewindAttributes(); 503 504 char name[B_FILE_NAME_LENGTH]; 505 const uint32 kMaxBrokenAttributes = 64; 506 // sanity max value 507 uint32 count = 0; 508 uint32 type; 509 void *data; 510 size_t size; 511 512 status_t status; 513 while ((status = GetNextAttribute(name, &type, &data, &size)) 514 != B_ENTRY_NOT_FOUND) { 515 if (status != B_OK) { 516 printf("could not open attribute (possibly: %s): %s!\n", 517 name, strerror(status)); 518 if (count++ > kMaxBrokenAttributes) 519 break; 520 521 continue; 522 } 523 524 ssize_t written = node->WriteAttr(name, type, 0, data, size); 525 if (written < B_OK) { 526 printf("could not write attribute \"%s\": %s\n", name, 527 strerror(written)); 528 } else if ((size_t)written < size) { 529 printf("could only write %ld bytes (from %ld) at attribute \"%s\"\n", 530 written, size, name); 531 } 532 } 533 534 // copy stats 535 536 node->SetPermissions(fInode->mode); 537 node->SetOwner(fInode->uid); 538 node->SetGroup(fInode->gid); 539 node->SetModificationTime(fInode->last_modified_time >> 16); 540 node->SetCreationTime(fInode->create_time >> 16); 541 542 return B_OK; 543 } 544 545 546 Inode * 547 Inode::Factory(Disk *disk, bfs_inode *inode, bool ownBuffer) 548 { 549 // attributes (of a file) 550 if ((inode->mode & (S_ATTR | S_ATTR_DIR)) == S_ATTR) 551 return new Attribute(disk, inode, ownBuffer); 552 553 // directories, attribute directories, indices 554 if (S_ISDIR(inode->mode) || inode->mode & S_ATTR_DIR) 555 return new Directory(disk, inode, ownBuffer); 556 557 // regular files 558 if (S_ISREG(inode->mode)) 559 return new File(disk, inode, ownBuffer); 560 561 // symlinks (short and link in data-stream) 562 if (S_ISLNK(inode->mode)) 563 return new Symlink(disk, inode, ownBuffer); 564 565 return NULL; 566 } 567 568 569 Inode * 570 Inode::Factory(Disk *disk, block_run run) 571 { 572 bfs_inode *inode = (bfs_inode *)malloc(disk->BlockSize()); 573 if (!inode) 574 return NULL; 575 576 if (disk->ReadAt(disk->ToOffset(run), inode, disk->BlockSize()) <= 0) 577 return NULL; 578 579 Inode *object = Factory(disk, inode); 580 if (object == NULL) 581 free(inode); 582 583 return object; 584 } 585 586 587 Inode * 588 Inode::Factory(Disk *disk, Inode *inode, bool copyBuffer) 589 { 590 bfs_inode *inodeBuffer = inode->fInode; 591 592 if (copyBuffer) { 593 bfs_inode *inodeCopy = (bfs_inode *)malloc(inodeBuffer->inode_size); 594 if (!inodeCopy) 595 return NULL; 596 597 memcpy(inodeCopy, inodeBuffer, inodeBuffer->inode_size); 598 inodeBuffer = inodeCopy; 599 } 600 return Factory(disk, inodeBuffer, copyBuffer); 601 } 602 603 604 Inode * 605 Inode::EmptyInode(Disk *disk, const char *name, int32 mode) 606 { 607 bfs_inode *inode = (bfs_inode *)malloc(disk->BlockSize()); 608 if (!inode) 609 return NULL; 610 611 memset(inode, 0, sizeof(bfs_inode)); 612 613 inode->magic1 = INODE_MAGIC1; 614 inode->inode_size = disk->BlockSize(); 615 inode->mode = mode; 616 inode->flags = INODE_IN_USE | (mode & S_IFDIR ? INODE_LOGGED : 0); 617 618 if (name) { 619 small_data *data = inode->small_data_start; 620 data->type = FILE_NAME_TYPE; 621 data->name_size = FILE_NAME_NAME_LENGTH; 622 *data->Name() = FILE_NAME_NAME; 623 data->data_size = strlen(name); 624 strcpy((char *)data->Data(), name); 625 } 626 627 Inode *object = new (std::nothrow) Inode(disk, inode); 628 if (object == NULL) { 629 free(inode); 630 return NULL; 631 } 632 633 object->AcquireBuffer(); 634 // this must not be deleted anymore! 635 return object; 636 } 637 638 639 // #pragma mark - 640 641 642 DataStream::DataStream(Disk *disk, bfs_inode *inode, bool ownBuffer) 643 : Inode(disk,inode,ownBuffer), 644 fCurrent(-1), 645 fPosition(0LL) 646 { 647 } 648 649 650 DataStream::DataStream(const Inode &inode) 651 : Inode(inode), 652 fCurrent(-1), 653 fPosition(0LL) 654 { 655 } 656 657 658 DataStream::~DataStream() 659 { 660 } 661 662 663 status_t 664 DataStream::FindBlockRun(off_t pos) 665 { 666 NodeGetter _(this); 667 668 if (pos > fInode->data.size) 669 return B_ENTRY_NOT_FOUND; 670 671 if (fCurrent < 0) 672 fLevel = 0; 673 674 fRunBlockEnd = fCurrent >= 0 675 ? fRunFileOffset + (fRun.length << fDisk->BlockShift()) : 0LL; 676 677 // access in current block run? 678 679 if (fCurrent >= 0 && pos >= fRunFileOffset && pos < fRunBlockEnd) 680 return B_OK; 681 682 // find matching block run 683 684 if (fInode->data.max_direct_range > 0 685 && pos >= fInode->data.max_direct_range) { 686 if (fInode->data.max_double_indirect_range > 0 687 && pos >= fInode->data.max_indirect_range) { 688 // read from double indirect blocks 689 690 //printf("find double indirect block: %ld,%d!\n",fInode->data.double_indirect.allocation_group,fInode->data.double_indirect.start); 691 block_run *indirect = (block_run *)fDisk->ReadBlockRun(fInode->data.double_indirect); 692 if (indirect == NULL) 693 return B_ERROR; 694 695 off_t start = pos - fInode->data.max_indirect_range; 696 int32 indirectSize = fDisk->BlockSize() * 16 * (fDisk->BlockSize() / sizeof(block_run)); 697 int32 directSize = fDisk->BlockSize() * 4; 698 int32 index = start / indirectSize; 699 700 //printf("\tstart = %Ld, indirectSize = %ld, directSize = %ld, index = %ld\n",start,indirectSize,directSize,index); 701 //printf("\tlook for indirect block at %ld,%d\n",indirect[index].allocation_group,indirect[index].start); 702 indirect = (block_run *)fDisk->ReadBlockRun(indirect[index]); 703 if (indirect == NULL) 704 return B_ERROR; 705 706 fCurrent = (start % indirectSize) / directSize; 707 fRunFileOffset = fInode->data.max_indirect_range + (index * indirectSize) + (fCurrent * directSize); 708 fRunBlockEnd = fRunFileOffset + directSize; 709 fRun = indirect[fCurrent]; 710 //printf("\tfCurrent = %ld, fRunFileOffset = %Ld, fRunBlockEnd = %Ld, fRun = %ld,%d\n",fCurrent,fRunFileOffset,fRunBlockEnd,fRun.allocation_group,fRun.start); 711 } else { 712 // access from indirect blocks 713 714 block_run *indirect = (block_run *)fDisk->ReadBlockRun(fInode->data.indirect); 715 if (!indirect) 716 return B_ERROR; 717 718 int32 indirectRuns = (fInode->data.indirect.length << fDisk->BlockShift()) / sizeof(block_run); 719 720 if (fLevel != 1 || pos < fRunFileOffset) { 721 fRunBlockEnd = fInode->data.max_direct_range; 722 fCurrent = -1; 723 fLevel = 1; 724 } 725 726 while (++fCurrent < indirectRuns) { 727 if (indirect[fCurrent].IsZero()) 728 break; 729 730 fRunFileOffset = fRunBlockEnd; 731 fRunBlockEnd += indirect[fCurrent].length << fDisk->BlockShift(); 732 if (fRunBlockEnd > pos) 733 break; 734 } 735 if (fCurrent == indirectRuns || indirect[fCurrent].IsZero()) 736 return B_ERROR; 737 738 fRun = indirect[fCurrent]; 739 //printf("reading from indirect block: %ld,%d\n",fRun.allocation_group,fRun.start); 740 //printf("### indirect-run[%ld] = (%ld,%d,%d), offset = %Ld\n",fCurrent,fRun.allocation_group,fRun.start,fRun.length,fRunFileOffset); 741 } 742 } else { 743 // access from direct blocks 744 if (fRunFileOffset > pos) { 745 fRunBlockEnd = 0LL; 746 fCurrent = -1; 747 } 748 fLevel = 0; 749 750 while (++fCurrent < NUM_DIRECT_BLOCKS) { 751 if (fInode->data.direct[fCurrent].IsZero()) 752 break; 753 754 fRunFileOffset = fRunBlockEnd; 755 fRunBlockEnd += fInode->data.direct[fCurrent].length << fDisk->BlockShift(); 756 if (fRunBlockEnd > pos) 757 break; 758 } 759 if (fCurrent == NUM_DIRECT_BLOCKS || fInode->data.direct[fCurrent].IsZero()) 760 return B_ERROR; 761 762 fRun = fInode->data.direct[fCurrent]; 763 //printf("### run[%ld] = (%ld,%d,%d), offset = %Ld\n",fCurrent,fRun.allocation_group,fRun.start,fRun.length,fRunFileOffset); 764 } 765 return B_OK; 766 } 767 768 769 ssize_t 770 DataStream::ReadAt(off_t pos, void *buffer, size_t size) 771 { 772 NodeGetter _(this); 773 774 //printf("DataStream::ReadAt(pos = %Ld,buffer = %p,size = %ld);\n",pos,buffer,size); 775 // truncate size to read 776 if (pos + (off_t)size > fInode->data.size) { 777 if (pos > fInode->data.size) // reading outside the file 778 return B_ERROR; 779 780 size = fInode->data.size - pos; 781 if (!size) // there is nothing left to read 782 return 0; 783 } 784 ssize_t read = 0; 785 786 //printf("### read %ld bytes at %Ld\n",size,pos); 787 while (size > 0) { 788 status_t status = FindBlockRun(pos); 789 if (status < B_OK) 790 return status; 791 792 ssize_t bytes = min_c((off_t)size, fRunBlockEnd - pos); 793 794 //printf("### read %ld bytes from %Ld\n### --\n",bytes,fDisk->ToOffset(fRun) + pos - fRunFileOffset); 795 bytes = fDisk->ReadAt(fDisk->ToOffset(fRun) + pos - fRunFileOffset, 796 buffer, bytes); 797 if (bytes <= 0) { 798 if (bytes == 0) { 799 printf("could not read bytes at: %" B_PRId32 ",%d\n", 800 fRun.allocation_group, fRun.start); 801 } 802 return bytes < 0 ? bytes : B_BAD_DATA; 803 } 804 805 buffer = (void *)((uint8 *)buffer + bytes); 806 size -= bytes; 807 pos += bytes; 808 read += bytes; 809 } 810 if (read >= 0) 811 return read; 812 813 return B_IO_ERROR; 814 } 815 816 817 ssize_t 818 DataStream::WriteAt(off_t pos, const void *buffer, size_t size) 819 { 820 NodeGetter _(this); 821 822 // FIXME: truncate size -> should enlargen the file 823 if (pos + (off_t)size > fInode->data.size) { 824 if (pos > fInode->data.size) // writing outside the file 825 return B_ERROR; 826 827 size = fInode->data.size - pos; 828 if (!size) // there is nothing left to write 829 return 0; 830 } 831 ssize_t written = 0; 832 833 //printf("### write %ld bytes at %Ld\n",size,pos); 834 while (size > 0) { 835 status_t status = FindBlockRun(pos); 836 if (status < B_OK) 837 return status; 838 839 ssize_t bytes = min_c((off_t)size, fRunBlockEnd - pos); 840 841 //printf("### write %ld bytes to %Ld\n### --\n",bytes,fDisk->ToOffset(fRun) + pos - fRunFileOffset); 842 bytes = fDisk->WriteAt(fDisk->ToOffset(fRun) + pos - fRunFileOffset,buffer,bytes); 843 if (bytes < 0) 844 return bytes; 845 846 buffer = (void *)((uint8 *)buffer + bytes); 847 size -= bytes; 848 pos += bytes; 849 written += bytes; 850 } 851 if (written >= 0) 852 return written; 853 854 return B_IO_ERROR; 855 } 856 857 858 off_t 859 DataStream::Seek(off_t position, uint32 seekMode) 860 { 861 NodeGetter _(this); 862 863 if (seekMode == SEEK_SET) 864 fPosition = position; 865 else if (seekMode == SEEK_END) 866 fPosition = fInode->data.size + position; 867 else 868 fPosition += position; 869 870 return fPosition; 871 } 872 873 874 off_t 875 DataStream::Position() const 876 { 877 return fPosition; 878 } 879 880 881 status_t 882 DataStream::SetSize(off_t size) 883 { 884 NodeGetter _(this); 885 886 // FIXME: not yet supported 887 if (size > fInode->data.size || size > fInode->data.max_direct_range) 888 return B_ERROR; 889 890 if (size == fInode->data.size) 891 return B_OK; 892 893 BufferClobbered(); 894 895 fInode->data.size = size; 896 fInode->data.max_direct_range = size; 897 fInode->data.max_indirect_range = 0; 898 fInode->data.max_double_indirect_range = 0; 899 900 for (int32 i = 0;i < NUM_DIRECT_BLOCKS;i++) { 901 if (size <= 0) 902 fInode->data.direct[i].SetTo(0, 0, 0); 903 else if ((fInode->data.direct[i].length << fDisk->BlockShift()) >= size) { 904 off_t blocks = (size + fDisk->BlockSize() - 1) / fDisk->BlockSize(); 905 fInode->data.direct[i].length = blocks; 906 size = 0; 907 } else 908 size -= fInode->data.direct[i].length << fDisk->BlockShift(); 909 } 910 911 return B_OK; 912 } 913 914 915 // #pragma mark - 916 917 918 File::File(Disk *disk, bfs_inode *inode,bool ownBuffer) 919 : DataStream(disk,inode,ownBuffer) 920 { 921 } 922 923 924 File::File(const Inode &inode) 925 : DataStream(inode) 926 { 927 } 928 929 930 File::~File() 931 { 932 } 933 934 935 status_t 936 File::InitCheck() const 937 { 938 status_t status = DataStream::InitCheck(); 939 if (status == B_OK) 940 return IsFile() ? B_OK : B_ERROR; 941 942 return status; 943 } 944 945 946 status_t 947 File::CopyTo(const char *root, bool fullPath, Inode::Source *source) 948 { 949 status_t status = Inode::CopyTo(root, fullPath, source); 950 if (status < B_OK) 951 return status; 952 953 BPath path(root); 954 if (fullPath && Path(source)) 955 path.Append(Path(source)); 956 957 char *name = (char *)Name(); 958 if (name != NULL) { 959 // changes the filename in the inode buffer (for deleted entries) 960 if (!*name) 961 *name = '_'; 962 path.Append(name); 963 } else { 964 BString sub; 965 sub << "__untitled " << BlockRun().allocation_group << ":" 966 << (int32)BlockRun().start; 967 path.Append(sub.String()); 968 } 969 printf("%" B_PRId32 ",%d -> %s\n", BlockRun().allocation_group, 970 BlockRun().start, path.Path()); 971 972 BFile file; 973 status = file.SetTo(path.Path(), 974 B_WRITE_ONLY | B_CREATE_FILE | B_FAIL_IF_EXISTS); 975 if (status < B_OK) 976 return status; 977 978 char buffer[fDisk->BlockSize()]; 979 ssize_t size; 980 Seek(0, SEEK_SET); 981 982 while ((size = Read(buffer, sizeof(buffer))) > B_OK) { 983 ssize_t written = file.Write(buffer, size); 984 if (written < B_OK) 985 return written; 986 } 987 988 return CopyAttributesTo(&file); 989 } 990 991 992 // #pragma mark - 993 994 995 Attribute::Attribute(Disk *disk, bfs_inode *inode, bool ownBuffer) 996 : File(disk, inode, ownBuffer) 997 { 998 } 999 1000 1001 Attribute::Attribute(const Inode &inode) 1002 : File(inode) 1003 { 1004 } 1005 1006 1007 Attribute::~Attribute() 1008 { 1009 } 1010 1011 1012 status_t 1013 Attribute::InitCheck() const 1014 { 1015 status_t status = DataStream::InitCheck(); 1016 if (status == B_OK) 1017 return IsAttribute() ? B_OK : B_ERROR; 1018 1019 return status; 1020 } 1021 1022 1023 status_t 1024 Attribute::CopyTo(const char */*path*/, bool /*fullPath*/, 1025 Inode::Source */*source*/) 1026 { 1027 // files and directories already copy all attributes 1028 1029 // eventually, this method should be implemented to recover lost 1030 // attributes on the disk 1031 1032 return B_OK; 1033 } 1034 1035 1036 // #pragma mark - 1037 1038 1039 Directory::Directory(Disk *disk, bfs_inode *inode, bool ownBuffer) 1040 : DataStream(disk, inode, ownBuffer), 1041 fTree(NULL) 1042 { 1043 } 1044 1045 1046 Directory::Directory(const Inode &inode) 1047 : DataStream(inode), 1048 fTree(NULL) 1049 { 1050 } 1051 1052 1053 Directory::~Directory() 1054 { 1055 delete fTree; 1056 } 1057 1058 1059 status_t 1060 Directory::InitCheck() const 1061 { 1062 status_t status = DataStream::InitCheck(); 1063 if (status == B_OK) 1064 return (IsDirectory() || IsAttributeDirectory()) ? B_OK : B_ERROR; 1065 1066 return status; 1067 } 1068 1069 1070 status_t 1071 Directory::CopyTo(const char *root, bool fullPath, Inode::Source *source) 1072 { 1073 // don't copy attributes or indices 1074 // the recovery program should make empty files to recover lost attributes 1075 if (IsAttributeDirectory() || IsIndex()) 1076 return B_OK; 1077 1078 status_t status = Inode::CopyTo(root, fullPath, source); 1079 if (status < B_OK) 1080 return status; 1081 1082 BPath path(root); 1083 if (fullPath && Path(source)) 1084 path.Append(Path(source)); 1085 1086 char *name = (char *)Name(); 1087 if (name != NULL) { 1088 // changes the filename in the inode buffer (for deleted entries) 1089 if (!*name) 1090 *name = '_'; 1091 path.Append(name); 1092 } else { 1093 // create unique name 1094 BString sub; 1095 sub << "__untitled " << BlockRun().allocation_group << ":" 1096 << (int32)BlockRun().start; 1097 path.Append(sub.String()); 1098 } 1099 1100 BEntry entry(path.Path()); 1101 BDirectory directory; 1102 if ((status = entry.GetParent(&directory)) < B_OK) 1103 return status; 1104 1105 status = directory.CreateDirectory(path.Leaf(), NULL); 1106 if (status < B_OK && status != B_FILE_EXISTS) 1107 return status; 1108 1109 if ((status = directory.SetTo(&entry)) < B_OK) 1110 return status; 1111 1112 return CopyAttributesTo(&directory); 1113 } 1114 1115 1116 status_t 1117 Directory::Rewind() 1118 { 1119 if (!fTree) { 1120 status_t status = CreateTree(); 1121 if (status < B_OK) 1122 return status; 1123 } 1124 return fTree->Rewind(); 1125 } 1126 1127 1128 status_t 1129 Directory::GetNextEntry(char *name, block_run *run) 1130 { 1131 status_t status; 1132 1133 if (!fTree) { 1134 if ((status = Rewind()) < B_OK) 1135 return status; 1136 } 1137 uint16 length; 1138 off_t offset; 1139 1140 if ((status = fTree->GetNextEntry(name, &length, B_FILE_NAME_LENGTH - 1, 1141 &offset)) < B_OK) 1142 return status; 1143 1144 *run = fDisk->ToBlockRun(offset); 1145 1146 return B_OK; 1147 } 1148 1149 1150 status_t 1151 Directory::GetNextEntry(block_run *run) 1152 { 1153 char name[B_FILE_NAME_LENGTH]; 1154 1155 return GetNextEntry(name, run); 1156 } 1157 1158 1159 status_t 1160 Directory::Contains(const block_run *run) 1161 { 1162 status_t status; 1163 1164 if (!fTree) { 1165 if ((status = Rewind()) < B_OK) 1166 return status; 1167 } 1168 1169 block_run searchRun; 1170 while (GetNextEntry(&searchRun) == B_OK) { 1171 if (searchRun == *run) 1172 return B_OK; 1173 } 1174 1175 return B_ENTRY_NOT_FOUND; 1176 } 1177 1178 1179 status_t 1180 Directory::Contains(const Inode *inode) 1181 { 1182 status_t status; 1183 1184 if (!fTree) { 1185 if ((status = CreateTree()) < B_OK) 1186 return status; 1187 } 1188 1189 off_t value; 1190 const char *name = inode->Name(); 1191 status = B_ENTRY_NOT_FOUND; 1192 1193 if (name && (status = fTree->Find((uint8 *)name, (uint16)strlen(name), 1194 &value)) == B_OK) { 1195 if (fDisk->ToBlockRun(value) == inode->InodeBuffer()->inode_num) 1196 return B_OK; 1197 1198 printf("inode address do not match (%s)!\n", inode->Name()); 1199 } 1200 1201 if (status != B_OK && status != B_ENTRY_NOT_FOUND) 1202 return status; 1203 1204 return Contains(&inode->InodeBuffer()->inode_num); 1205 } 1206 1207 1208 status_t 1209 Directory::FindEntry(const char *name, block_run *run) 1210 { 1211 status_t status; 1212 1213 if (!name) 1214 return B_BAD_VALUE; 1215 1216 if (!fTree) { 1217 if ((status = CreateTree()) < B_OK) 1218 return status; 1219 } 1220 1221 off_t value; 1222 1223 if ((status = fTree->Find((uint8 *)name, (uint16)strlen(name), 1224 &value)) >= B_OK) { 1225 if (run) 1226 *run = fDisk->ToBlockRun(value); 1227 return B_OK; 1228 } 1229 return status; 1230 } 1231 1232 1233 status_t 1234 Directory::AddEntry(Inode *inode) 1235 { 1236 status_t status; 1237 bool created = false; 1238 1239 if (!fTree) { 1240 status = CreateTree(); 1241 if (status == B_OK) 1242 status = fTree->Validate(); 1243 1244 if (status == B_BAD_DATA) { 1245 //puts("bplustree corrupted!"); 1246 fTree = new BPlusTree(BPLUSTREE_STRING_TYPE, BPLUSTREE_NODE_SIZE, 1247 false); 1248 if ((status = fTree->InitCheck()) < B_OK) { 1249 delete fTree; 1250 fTree = NULL; 1251 } else 1252 created = true; 1253 } 1254 1255 if (status < B_OK) 1256 return status; 1257 } 1258 // keep all changes in memory 1259 fTree->SetHoldChanges(true); 1260 1261 if (created) { 1262 // add . and .. 1263 fTree->Insert(".", Block()); 1264 fTree->Insert("..", fDisk->ToBlock(Parent())); 1265 } 1266 1267 if (inode->Flags() & INODE_DELETED) 1268 return B_ENTRY_NOT_FOUND; 1269 1270 BString name = inode->Name(); 1271 if (name == "") { 1272 name << "__file " << inode->BlockRun().allocation_group << ":" 1273 << (int32)inode->BlockRun().start; 1274 } 1275 1276 return fTree->Insert(name.String(), inode->Block()); 1277 } 1278 1279 1280 status_t 1281 Directory::CreateTree() 1282 { 1283 fTree = new BPlusTree(this); 1284 1285 status_t status = fTree->InitCheck(); 1286 if (status < B_OK) { 1287 delete fTree; 1288 fTree = NULL; 1289 return status; 1290 } 1291 return B_OK; 1292 } 1293 1294 1295 status_t 1296 Directory::GetTree(BPlusTree **tree) 1297 { 1298 if (!fTree) { 1299 status_t status = CreateTree(); 1300 if (status < B_OK) 1301 return status; 1302 } 1303 *tree = fTree; 1304 return B_OK; 1305 } 1306 1307 1308 // #pragma mark - 1309 1310 1311 Symlink::Symlink(Disk *disk, bfs_inode *inode,bool ownBuffer) 1312 : Inode(disk,inode,ownBuffer) 1313 { 1314 } 1315 1316 1317 Symlink::Symlink(const Inode &inode) 1318 : Inode(inode) 1319 { 1320 } 1321 1322 1323 Symlink::~Symlink() 1324 { 1325 } 1326 1327 1328 status_t 1329 Symlink::InitCheck() const 1330 { 1331 status_t status = Inode::InitCheck(); 1332 if (status == B_OK) 1333 return IsSymlink() ? B_OK : B_ERROR; 1334 1335 return status; 1336 } 1337 1338 1339 status_t 1340 Symlink::CopyTo(const char *root, bool fullPath,Inode::Source *source) 1341 { 1342 status_t status = Inode::CopyTo(root,fullPath,source); 1343 if (status < B_OK) 1344 return status; 1345 1346 BPath path(root); 1347 if (fullPath && Path(source)) 1348 path.Append(Path(source)); 1349 1350 char *name = (char *)Name(); 1351 if (name != NULL) { 1352 // changes the filename in the inode buffer (for deleted entries) 1353 if (!*name) 1354 *name = '_'; 1355 path.Append(name); 1356 } else { 1357 // create unique name 1358 BString sub; 1359 sub << "__symlink " << BlockRun().allocation_group << ":" 1360 << (int32)BlockRun().start; 1361 path.Append(sub.String()); 1362 } 1363 1364 BEntry entry(path.Path()); 1365 BDirectory directory; 1366 if ((status = entry.GetParent(&directory)) < B_OK) 1367 return status; 1368 1369 char to[2048]; 1370 if (LinksTo(to,sizeof(to)) < B_OK) 1371 return B_ERROR; 1372 1373 BSymLink link; 1374 status = directory.CreateSymLink(path.Leaf(),to,&link); 1375 if (status < B_OK && status != B_FILE_EXISTS) 1376 return status; 1377 1378 if ((status = link.SetTo(&entry)) < B_OK) 1379 return status; 1380 1381 return CopyAttributesTo(&link); 1382 } 1383 1384 1385 status_t 1386 Symlink::LinksTo(char *to,size_t maxLength) 1387 { 1388 if ((fInode->flags & INODE_LONG_SYMLINK) == 0) { 1389 strcpy(to,fInode->short_symlink); 1390 return B_OK; 1391 } 1392 1393 DataStream stream(*this); 1394 status_t status = stream.InitCheck(); 1395 if (status < B_OK) 1396 return status; 1397 1398 status = stream.Read(to,maxLength); 1399 1400 return status < B_OK ? status : B_OK; 1401 } 1402 1403