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