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