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 return NULL; 627 } 628 629 object->AcquireBuffer(); 630 // this must not be deleted anymore! 631 return object; 632 } 633 634 635 // #pragma mark - 636 637 638 DataStream::DataStream(Disk *disk, bfs_inode *inode, bool ownBuffer) 639 : Inode(disk,inode,ownBuffer), 640 fCurrent(-1), 641 fPosition(0LL) 642 { 643 } 644 645 646 DataStream::DataStream(const Inode &inode) 647 : Inode(inode), 648 fCurrent(-1), 649 fPosition(0LL) 650 { 651 } 652 653 654 DataStream::~DataStream() 655 { 656 } 657 658 659 status_t 660 DataStream::FindBlockRun(off_t pos) 661 { 662 NodeGetter _(this); 663 664 if (pos > fInode->data.size) 665 return B_ENTRY_NOT_FOUND; 666 667 if (fCurrent < 0) 668 fLevel = 0; 669 670 fRunBlockEnd = fCurrent >= 0 671 ? fRunFileOffset + (fRun.length << fDisk->BlockShift()) : 0LL; 672 673 // access in current block run? 674 675 if (fCurrent >= 0 && pos >= fRunFileOffset && pos < fRunBlockEnd) 676 return B_OK; 677 678 // find matching block run 679 680 if (fInode->data.max_direct_range > 0 681 && pos >= fInode->data.max_direct_range) { 682 if (fInode->data.max_double_indirect_range > 0 683 && pos >= fInode->data.max_indirect_range) { 684 // read from double indirect blocks 685 686 //printf("find double indirect block: %ld,%d!\n",fInode->data.double_indirect.allocation_group,fInode->data.double_indirect.start); 687 block_run *indirect = (block_run *)fDisk->ReadBlockRun(fInode->data.double_indirect); 688 if (indirect == NULL) 689 return B_ERROR; 690 691 off_t start = pos - fInode->data.max_indirect_range; 692 int32 indirectSize = fDisk->BlockSize() * 16 * (fDisk->BlockSize() / sizeof(block_run)); 693 int32 directSize = fDisk->BlockSize() * 4; 694 int32 index = start / indirectSize; 695 696 //printf("\tstart = %Ld, indirectSize = %ld, directSize = %ld, index = %ld\n",start,indirectSize,directSize,index); 697 //printf("\tlook for indirect block at %ld,%d\n",indirect[index].allocation_group,indirect[index].start); 698 indirect = (block_run *)fDisk->ReadBlockRun(indirect[index]); 699 if (indirect == NULL) 700 return B_ERROR; 701 702 fCurrent = (start % indirectSize) / directSize; 703 fRunFileOffset = fInode->data.max_indirect_range + (index * indirectSize) + (fCurrent * directSize); 704 fRunBlockEnd = fRunFileOffset + directSize; 705 fRun = indirect[fCurrent]; 706 //printf("\tfCurrent = %ld, fRunFileOffset = %Ld, fRunBlockEnd = %Ld, fRun = %ld,%d\n",fCurrent,fRunFileOffset,fRunBlockEnd,fRun.allocation_group,fRun.start); 707 } else { 708 // access from indirect blocks 709 710 block_run *indirect = (block_run *)fDisk->ReadBlockRun(fInode->data.indirect); 711 if (!indirect) 712 return B_ERROR; 713 714 int32 indirectRuns = (fInode->data.indirect.length << fDisk->BlockShift()) / sizeof(block_run); 715 716 if (fLevel != 1 || pos < fRunFileOffset) { 717 fRunBlockEnd = fInode->data.max_direct_range; 718 fCurrent = -1; 719 fLevel = 1; 720 } 721 722 while (++fCurrent < indirectRuns) { 723 if (indirect[fCurrent].IsZero()) 724 break; 725 726 fRunFileOffset = fRunBlockEnd; 727 fRunBlockEnd += indirect[fCurrent].length << fDisk->BlockShift(); 728 if (fRunBlockEnd > pos) 729 break; 730 } 731 if (fCurrent == indirectRuns || indirect[fCurrent].IsZero()) 732 return B_ERROR; 733 734 fRun = indirect[fCurrent]; 735 //printf("reading from indirect block: %ld,%d\n",fRun.allocation_group,fRun.start); 736 //printf("### indirect-run[%ld] = (%ld,%d,%d), offset = %Ld\n",fCurrent,fRun.allocation_group,fRun.start,fRun.length,fRunFileOffset); 737 } 738 } else { 739 // access from direct blocks 740 if (fRunFileOffset > pos) { 741 fRunBlockEnd = 0LL; 742 fCurrent = -1; 743 } 744 fLevel = 0; 745 746 while (++fCurrent < NUM_DIRECT_BLOCKS) { 747 if (fInode->data.direct[fCurrent].IsZero()) 748 break; 749 750 fRunFileOffset = fRunBlockEnd; 751 fRunBlockEnd += fInode->data.direct[fCurrent].length << fDisk->BlockShift(); 752 if (fRunBlockEnd > pos) 753 break; 754 } 755 if (fCurrent == NUM_DIRECT_BLOCKS || fInode->data.direct[fCurrent].IsZero()) 756 return B_ERROR; 757 758 fRun = fInode->data.direct[fCurrent]; 759 //printf("### run[%ld] = (%ld,%d,%d), offset = %Ld\n",fCurrent,fRun.allocation_group,fRun.start,fRun.length,fRunFileOffset); 760 } 761 return B_OK; 762 } 763 764 765 ssize_t 766 DataStream::ReadAt(off_t pos, void *buffer, size_t size) 767 { 768 NodeGetter _(this); 769 770 //printf("DataStream::ReadAt(pos = %Ld,buffer = %p,size = %ld);\n",pos,buffer,size); 771 // truncate size to read 772 if (pos + (off_t)size > fInode->data.size) { 773 if (pos > fInode->data.size) // reading outside the file 774 return B_ERROR; 775 776 size = fInode->data.size - pos; 777 if (!size) // there is nothing left to read 778 return 0; 779 } 780 ssize_t read = 0; 781 782 //printf("### read %ld bytes at %Ld\n",size,pos); 783 while (size > 0) { 784 status_t status = FindBlockRun(pos); 785 if (status < B_OK) 786 return status; 787 788 ssize_t bytes = min_c((off_t)size, fRunBlockEnd - pos); 789 790 //printf("### read %ld bytes from %Ld\n### --\n",bytes,fDisk->ToOffset(fRun) + pos - fRunFileOffset); 791 bytes = fDisk->ReadAt(fDisk->ToOffset(fRun) + pos - fRunFileOffset, 792 buffer, bytes); 793 if (bytes <= 0) { 794 if (bytes == 0) { 795 printf("could not read bytes at: %" B_PRId32 ",%d\n", 796 fRun.allocation_group, fRun.start); 797 } 798 return bytes < 0 ? bytes : B_BAD_DATA; 799 } 800 801 buffer = (void *)((uint8 *)buffer + bytes); 802 size -= bytes; 803 pos += bytes; 804 read += bytes; 805 } 806 if (read >= 0) 807 return read; 808 809 return B_IO_ERROR; 810 } 811 812 813 ssize_t 814 DataStream::WriteAt(off_t pos, const void *buffer, size_t size) 815 { 816 NodeGetter _(this); 817 818 // FIXME: truncate size -> should enlargen the file 819 if (pos + (off_t)size > fInode->data.size) { 820 if (pos > fInode->data.size) // writing outside the file 821 return B_ERROR; 822 823 size = fInode->data.size - pos; 824 if (!size) // there is nothing left to write 825 return 0; 826 } 827 ssize_t written = 0; 828 829 //printf("### write %ld bytes at %Ld\n",size,pos); 830 while (size > 0) { 831 status_t status = FindBlockRun(pos); 832 if (status < B_OK) 833 return status; 834 835 ssize_t bytes = min_c((off_t)size, fRunBlockEnd - pos); 836 837 //printf("### write %ld bytes to %Ld\n### --\n",bytes,fDisk->ToOffset(fRun) + pos - fRunFileOffset); 838 bytes = fDisk->WriteAt(fDisk->ToOffset(fRun) + pos - fRunFileOffset,buffer,bytes); 839 if (bytes < 0) 840 return bytes; 841 842 buffer = (void *)((uint8 *)buffer + bytes); 843 size -= bytes; 844 pos += bytes; 845 written += bytes; 846 } 847 if (written >= 0) 848 return written; 849 850 return B_IO_ERROR; 851 } 852 853 854 off_t 855 DataStream::Seek(off_t position, uint32 seekMode) 856 { 857 NodeGetter _(this); 858 859 if (seekMode == SEEK_SET) 860 fPosition = position; 861 else if (seekMode == SEEK_END) 862 fPosition = fInode->data.size + position; 863 else 864 fPosition += position; 865 866 return fPosition; 867 } 868 869 870 off_t 871 DataStream::Position() const 872 { 873 return fPosition; 874 } 875 876 877 status_t 878 DataStream::SetSize(off_t size) 879 { 880 NodeGetter _(this); 881 882 // FIXME: not yet supported 883 if (size > fInode->data.size || size > fInode->data.max_direct_range) 884 return B_ERROR; 885 886 if (size == fInode->data.size) 887 return B_OK; 888 889 BufferClobbered(); 890 891 fInode->data.size = size; 892 fInode->data.max_direct_range = size; 893 fInode->data.max_indirect_range = 0; 894 fInode->data.max_double_indirect_range = 0; 895 896 for (int32 i = 0;i < NUM_DIRECT_BLOCKS;i++) { 897 if (size <= 0) 898 fInode->data.direct[i].SetTo(0, 0, 0); 899 else if ((fInode->data.direct[i].length << fDisk->BlockShift()) >= size) { 900 off_t blocks = (size + fDisk->BlockSize() - 1) / fDisk->BlockSize(); 901 fInode->data.direct[i].length = blocks; 902 size = 0; 903 } else 904 size -= fInode->data.direct[i].length << fDisk->BlockShift(); 905 } 906 907 return B_OK; 908 } 909 910 911 // #pragma mark - 912 913 914 File::File(Disk *disk, bfs_inode *inode,bool ownBuffer) 915 : DataStream(disk,inode,ownBuffer) 916 { 917 } 918 919 920 File::File(const Inode &inode) 921 : DataStream(inode) 922 { 923 } 924 925 926 File::~File() 927 { 928 } 929 930 931 status_t 932 File::InitCheck() 933 { 934 status_t status = DataStream::InitCheck(); 935 if (status == B_OK) 936 return IsFile() ? B_OK : B_ERROR; 937 938 return status; 939 } 940 941 942 status_t 943 File::CopyTo(const char *root, bool fullPath, Inode::Source *source) 944 { 945 status_t status = Inode::CopyTo(root, fullPath, source); 946 if (status < B_OK) 947 return status; 948 949 BPath path(root); 950 if (fullPath && Path(source)) 951 path.Append(Path(source)); 952 953 char *name = (char *)Name(); 954 if (name != NULL) { 955 // changes the filename in the inode buffer (for deleted entries) 956 if (!*name) 957 *name = '_'; 958 path.Append(name); 959 } else { 960 BString sub; 961 sub << "__untitled " << BlockRun().allocation_group << ":" 962 << (int32)BlockRun().start; 963 path.Append(sub.String()); 964 } 965 printf("%" B_PRId32 ",%d -> %s\n", BlockRun().allocation_group, 966 BlockRun().start, path.Path()); 967 968 BFile file; 969 status = file.SetTo(path.Path(), 970 B_WRITE_ONLY | B_CREATE_FILE | B_FAIL_IF_EXISTS); 971 if (status < B_OK) 972 return status; 973 974 char buffer[fDisk->BlockSize()]; 975 ssize_t size; 976 Seek(0, SEEK_SET); 977 978 while ((size = Read(buffer, sizeof(buffer))) > B_OK) { 979 ssize_t written = file.Write(buffer, size); 980 if (written < B_OK) 981 return written; 982 } 983 984 return CopyAttributesTo(&file); 985 } 986 987 988 // #pragma mark - 989 990 991 Attribute::Attribute(Disk *disk, bfs_inode *inode, bool ownBuffer) 992 : File(disk, inode, ownBuffer) 993 { 994 } 995 996 997 Attribute::Attribute(const Inode &inode) 998 : File(inode) 999 { 1000 } 1001 1002 1003 Attribute::~Attribute() 1004 { 1005 } 1006 1007 1008 status_t 1009 Attribute::InitCheck() 1010 { 1011 status_t status = DataStream::InitCheck(); 1012 if (status == B_OK) 1013 return IsAttribute() ? B_OK : B_ERROR; 1014 1015 return status; 1016 } 1017 1018 1019 status_t 1020 Attribute::CopyTo(const char */*path*/, bool /*fullPath*/, 1021 Inode::Source */*source*/) 1022 { 1023 // files and directories already copy all attributes 1024 1025 // eventually, this method should be implemented to recover lost 1026 // attributes on the disk 1027 1028 return B_OK; 1029 } 1030 1031 1032 // #pragma mark - 1033 1034 1035 Directory::Directory(Disk *disk, bfs_inode *inode, bool ownBuffer) 1036 : DataStream(disk, inode, ownBuffer), 1037 fTree(NULL) 1038 { 1039 } 1040 1041 1042 Directory::Directory(const Inode &inode) 1043 : DataStream(inode), 1044 fTree(NULL) 1045 { 1046 } 1047 1048 1049 Directory::~Directory() 1050 { 1051 delete fTree; 1052 } 1053 1054 1055 status_t 1056 Directory::InitCheck() 1057 { 1058 status_t status = DataStream::InitCheck(); 1059 if (status == B_OK) 1060 return (IsDirectory() || IsAttributeDirectory()) ? B_OK : B_ERROR; 1061 1062 return status; 1063 } 1064 1065 1066 status_t 1067 Directory::CopyTo(const char *root, bool fullPath, Inode::Source *source) 1068 { 1069 // don't copy attributes or indices 1070 // the recovery program should make empty files to recover lost attributes 1071 if (IsAttributeDirectory() || IsIndex()) 1072 return B_OK; 1073 1074 status_t status = Inode::CopyTo(root, fullPath, source); 1075 if (status < B_OK) 1076 return status; 1077 1078 BPath path(root); 1079 if (fullPath && Path(source)) 1080 path.Append(Path(source)); 1081 1082 char *name = (char *)Name(); 1083 if (name != NULL) { 1084 // changes the filename in the inode buffer (for deleted entries) 1085 if (!*name) 1086 *name = '_'; 1087 path.Append(name); 1088 } else { 1089 // create unique name 1090 BString sub; 1091 sub << "__untitled " << BlockRun().allocation_group << ":" 1092 << (int32)BlockRun().start; 1093 path.Append(sub.String()); 1094 } 1095 1096 BEntry entry(path.Path()); 1097 BDirectory directory; 1098 if ((status = entry.GetParent(&directory)) < B_OK) 1099 return status; 1100 1101 status = directory.CreateDirectory(path.Leaf(), NULL); 1102 if (status < B_OK && status != B_FILE_EXISTS) 1103 return status; 1104 1105 if ((status = directory.SetTo(&entry)) < B_OK) 1106 return status; 1107 1108 return CopyAttributesTo(&directory); 1109 } 1110 1111 1112 status_t 1113 Directory::Rewind() 1114 { 1115 if (!fTree) { 1116 status_t status = CreateTree(); 1117 if (status < B_OK) 1118 return status; 1119 } 1120 return fTree->Rewind(); 1121 } 1122 1123 1124 status_t 1125 Directory::GetNextEntry(char *name, block_run *run) 1126 { 1127 status_t status; 1128 1129 if (!fTree) { 1130 if ((status = Rewind()) < B_OK) 1131 return status; 1132 } 1133 uint16 length; 1134 off_t offset; 1135 1136 if ((status = fTree->GetNextEntry(name, &length, B_FILE_NAME_LENGTH - 1, 1137 &offset)) < B_OK) 1138 return status; 1139 1140 *run = fDisk->ToBlockRun(offset); 1141 1142 return B_OK; 1143 } 1144 1145 1146 status_t 1147 Directory::GetNextEntry(block_run *run) 1148 { 1149 char name[B_FILE_NAME_LENGTH]; 1150 1151 return GetNextEntry(name, run); 1152 } 1153 1154 1155 status_t 1156 Directory::Contains(const block_run *run) 1157 { 1158 status_t status; 1159 1160 if (!fTree) { 1161 if ((status = Rewind()) < B_OK) 1162 return status; 1163 } 1164 1165 block_run searchRun; 1166 while (GetNextEntry(&searchRun) == B_OK) { 1167 if (searchRun == *run) 1168 return B_OK; 1169 } 1170 1171 return B_ENTRY_NOT_FOUND; 1172 } 1173 1174 1175 status_t 1176 Directory::Contains(const Inode *inode) 1177 { 1178 status_t status; 1179 1180 if (!fTree) { 1181 if ((status = CreateTree()) < B_OK) 1182 return status; 1183 } 1184 1185 off_t value; 1186 const char *name = inode->Name(); 1187 status = B_ENTRY_NOT_FOUND; 1188 1189 if (name && (status = fTree->Find((uint8 *)name, (uint16)strlen(name), 1190 &value)) == B_OK) { 1191 if (fDisk->ToBlockRun(value) == inode->InodeBuffer()->inode_num) 1192 return B_OK; 1193 1194 printf("inode address do not match (%s)!\n", inode->Name()); 1195 } 1196 1197 if (status != B_OK && status != B_ENTRY_NOT_FOUND) 1198 return status; 1199 1200 return Contains(&inode->InodeBuffer()->inode_num); 1201 } 1202 1203 1204 status_t 1205 Directory::FindEntry(const char *name, block_run *run) 1206 { 1207 status_t status; 1208 1209 if (!name) 1210 return B_BAD_VALUE; 1211 1212 if (!fTree) { 1213 if ((status = CreateTree()) < B_OK) 1214 return status; 1215 } 1216 1217 off_t value; 1218 1219 if ((status = fTree->Find((uint8 *)name, (uint16)strlen(name), 1220 &value)) >= B_OK) { 1221 if (run) 1222 *run = fDisk->ToBlockRun(value); 1223 return B_OK; 1224 } 1225 return status; 1226 } 1227 1228 1229 status_t 1230 Directory::AddEntry(Inode *inode) 1231 { 1232 status_t status; 1233 bool created = false; 1234 1235 if (!fTree) { 1236 status = CreateTree(); 1237 if (status == B_OK) 1238 status = fTree->Validate(); 1239 1240 if (status == B_BAD_DATA) { 1241 //puts("bplustree corrupted!"); 1242 fTree = new BPlusTree(BPLUSTREE_STRING_TYPE, BPLUSTREE_NODE_SIZE, 1243 false); 1244 if ((status = fTree->InitCheck()) < B_OK) { 1245 delete fTree; 1246 fTree = NULL; 1247 } else 1248 created = true; 1249 } 1250 1251 if (status < B_OK) 1252 return status; 1253 } 1254 // keep all changes in memory 1255 fTree->SetHoldChanges(true); 1256 1257 if (created) { 1258 // add . and .. 1259 fTree->Insert(".", Block()); 1260 fTree->Insert("..", fDisk->ToBlock(Parent())); 1261 } 1262 1263 if (inode->Flags() & INODE_DELETED) 1264 return B_ENTRY_NOT_FOUND; 1265 1266 BString name = inode->Name(); 1267 if (name == "") { 1268 name << "__file " << inode->BlockRun().allocation_group << ":" 1269 << (int32)inode->BlockRun().start; 1270 } 1271 1272 return fTree->Insert(name.String(), inode->Block()); 1273 } 1274 1275 1276 status_t 1277 Directory::CreateTree() 1278 { 1279 fTree = new BPlusTree(this); 1280 1281 status_t status = fTree->InitCheck(); 1282 if (status < B_OK) { 1283 delete fTree; 1284 fTree = NULL; 1285 return status; 1286 } 1287 return B_OK; 1288 } 1289 1290 1291 status_t 1292 Directory::GetTree(BPlusTree **tree) 1293 { 1294 if (!fTree) { 1295 status_t status = CreateTree(); 1296 if (status < B_OK) 1297 return status; 1298 } 1299 *tree = fTree; 1300 return B_OK; 1301 } 1302 1303 1304 // #pragma mark - 1305 1306 1307 Symlink::Symlink(Disk *disk, bfs_inode *inode,bool ownBuffer) 1308 : Inode(disk,inode,ownBuffer) 1309 { 1310 } 1311 1312 1313 Symlink::Symlink(const Inode &inode) 1314 : Inode(inode) 1315 { 1316 } 1317 1318 1319 Symlink::~Symlink() 1320 { 1321 } 1322 1323 1324 status_t 1325 Symlink::InitCheck() 1326 { 1327 status_t status = Inode::InitCheck(); 1328 if (status == B_OK) 1329 return IsSymlink() ? B_OK : B_ERROR; 1330 1331 return status; 1332 } 1333 1334 1335 status_t 1336 Symlink::CopyTo(const char *root, bool fullPath,Inode::Source *source) 1337 { 1338 status_t status = Inode::CopyTo(root,fullPath,source); 1339 if (status < B_OK) 1340 return status; 1341 1342 BPath path(root); 1343 if (fullPath && Path(source)) 1344 path.Append(Path(source)); 1345 1346 char *name = (char *)Name(); 1347 if (name != NULL) { 1348 // changes the filename in the inode buffer (for deleted entries) 1349 if (!*name) 1350 *name = '_'; 1351 path.Append(name); 1352 } else { 1353 // create unique name 1354 BString sub; 1355 sub << "__symlink " << BlockRun().allocation_group << ":" 1356 << (int32)BlockRun().start; 1357 path.Append(sub.String()); 1358 } 1359 1360 BEntry entry(path.Path()); 1361 BDirectory directory; 1362 if ((status = entry.GetParent(&directory)) < B_OK) 1363 return status; 1364 1365 char to[2048]; 1366 if (LinksTo(to,sizeof(to)) < B_OK) 1367 return B_ERROR; 1368 1369 BSymLink link; 1370 status = directory.CreateSymLink(path.Leaf(),to,&link); 1371 if (status < B_OK && status != B_FILE_EXISTS) 1372 return status; 1373 1374 if ((status = link.SetTo(&entry)) < B_OK) 1375 return status; 1376 1377 return CopyAttributesTo(&link); 1378 } 1379 1380 1381 status_t 1382 Symlink::LinksTo(char *to,size_t maxLength) 1383 { 1384 if ((fInode->flags & INODE_LONG_SYMLINK) == 0) { 1385 strcpy(to,fInode->short_symlink); 1386 return B_OK; 1387 } 1388 1389 DataStream stream(*this); 1390 status_t status = stream.InitCheck(); 1391 if (status < B_OK) 1392 return status; 1393 1394 status = stream.Read(to,maxLength); 1395 1396 return status < B_OK ? status : B_OK; 1397 } 1398 1399