1 /* 2 * Copyright 2011, Jérôme Duval, korli@users.berlios.de. 3 * Copyright 2008-2014, Axel Dörfler, axeld@pinc-software.de. 4 * This file may be used under the terms of the MIT License. 5 */ 6 7 8 #include "Inode.h" 9 10 #include <string.h> 11 #include <util/AutoLock.h> 12 #include <NodeMonitor.h> 13 14 #include "CachedBlock.h" 15 #include "DataStream.h" 16 #include "DirectoryIterator.h" 17 #include "ExtentStream.h" 18 #include "HTree.h" 19 #include "Utility.h" 20 21 22 #undef ASSERT 23 //#define TRACE_EXT2 24 #ifdef TRACE_EXT2 25 # define TRACE(x...) dprintf("\33[34mext2:\33[0m " x) 26 # define ASSERT(x) { if (!(x)) kernel_debugger("ext2: assert failed: " #x "\n"); } 27 #else 28 # define TRACE(x...) ; 29 # define ASSERT(x) ; 30 #endif 31 #define ERROR(x...) dprintf("\33[34mext2:\33[0m " x) 32 33 34 Inode::Inode(Volume* volume, ino_t id) 35 : 36 fVolume(volume), 37 fID(id), 38 fCache(NULL), 39 fMap(NULL), 40 fHasExtraAttributes(false) 41 { 42 rw_lock_init(&fLock, "ext2 inode"); 43 recursive_lock_init(&fSmallDataLock, "ext2 inode small data"); 44 45 TRACE("Inode::Inode(): ext2_inode: %lu, disk inode: %" B_PRIu32 46 "\n", sizeof(ext2_inode), fVolume->InodeSize()); 47 fNodeSize = sizeof(ext2_inode) > fVolume->InodeSize() 48 ? fVolume->InodeSize() : sizeof(ext2_inode); 49 50 fInitStatus = UpdateNodeFromDisk(); 51 if (fInitStatus == B_OK) { 52 fHasExtraAttributes = (fNodeSize == sizeof(ext2_inode) 53 && fNode.ExtraInodeSize() + EXT2_INODE_NORMAL_SIZE 54 == sizeof(ext2_inode)); 55 56 if (IsDirectory() || (IsSymLink() && Size() < 60)) { 57 TRACE("Inode::Inode(): Not creating the file cache\n"); 58 fInitStatus = B_OK; 59 } else 60 fInitStatus = CreateFileCache(); 61 } else 62 TRACE("Inode: Failed initialization\n"); 63 } 64 65 66 Inode::Inode(Volume* volume) 67 : 68 fVolume(volume), 69 fID(0), 70 fCache(NULL), 71 fMap(NULL), 72 fInitStatus(B_NO_INIT) 73 { 74 rw_lock_init(&fLock, "ext2 inode"); 75 recursive_lock_init(&fSmallDataLock, "ext2 inode small data"); 76 77 TRACE("Inode::Inode(): ext2_inode: %lu, disk inode: %" B_PRIu32 "\n", 78 sizeof(ext2_inode), fVolume->InodeSize()); 79 fNodeSize = sizeof(ext2_inode) > fVolume->InodeSize() 80 ? fVolume->InodeSize() : sizeof(ext2_inode); 81 } 82 83 84 Inode::~Inode() 85 { 86 TRACE("Inode destructor\n"); 87 88 DeleteFileCache(); 89 90 TRACE("Inode destructor: Done\n"); 91 } 92 93 94 status_t 95 Inode::InitCheck() 96 { 97 return fInitStatus; 98 } 99 100 101 void 102 Inode::WriteLockInTransaction(Transaction& transaction) 103 { 104 acquire_vnode(fVolume->FSVolume(), ID()); 105 106 TRACE("Inode::WriteLockInTransaction(): Locking\n"); 107 rw_lock_write_lock(&fLock); 108 109 transaction.AddListener(this); 110 } 111 112 113 status_t 114 Inode::WriteBack(Transaction& transaction) 115 { 116 off_t blockNum; 117 118 status_t status = fVolume->GetInodeBlock(fID, blockNum); 119 if (status != B_OK) 120 return status; 121 122 if (Node().Size() > 0x7fffffffLL) { 123 status = fVolume->ActivateLargeFiles(transaction); 124 if (status != B_OK) 125 return status; 126 } 127 128 CachedBlock cached(fVolume); 129 uint8* inodeBlockData = cached.SetToWritable(transaction, blockNum); 130 if (inodeBlockData == NULL) 131 return B_IO_ERROR; 132 133 TRACE("Inode::WriteBack(): Inode ID: %" B_PRIdINO ", inode block: %" 134 B_PRIdOFF ", data: %p, index: %" B_PRIu32 ", inode size: %" B_PRIu32 135 ", node size: %" B_PRIu32 ", this: %p, node: %p\n", 136 fID, blockNum, inodeBlockData, fVolume->InodeBlockIndex(fID), 137 fVolume->InodeSize(), fNodeSize, this, &fNode); 138 memcpy(inodeBlockData + 139 fVolume->InodeBlockIndex(fID) * fVolume->InodeSize(), 140 (uint8*)&fNode, fNodeSize); 141 142 TRACE("Inode::WriteBack() finished %" B_PRId32 "\n", Node().stream.direct[0]); 143 144 return B_OK; 145 } 146 147 148 status_t 149 Inode::UpdateNodeFromDisk() 150 { 151 off_t blockNum; 152 153 status_t status = fVolume->GetInodeBlock(fID, blockNum); 154 if (status != B_OK) 155 return status; 156 157 TRACE("inode %" B_PRIdINO " at block %" B_PRIdOFF "\n", fID, blockNum); 158 159 CachedBlock cached(fVolume); 160 const uint8* inodeBlock = cached.SetTo(blockNum); 161 162 if (inodeBlock == NULL) 163 return B_IO_ERROR; 164 165 TRACE("Inode size: %" B_PRIu32 ", inode index: %" B_PRIu32 "\n", 166 fVolume->InodeSize(), fVolume->InodeBlockIndex(fID)); 167 ext2_inode* inode = (ext2_inode*)(inodeBlock 168 + fVolume->InodeBlockIndex(fID) * fVolume->InodeSize()); 169 170 TRACE("Attempting to copy inode data from %p to %p, ext2_inode " 171 "size: %" B_PRIu32 "\n", inode, &fNode, fNodeSize); 172 173 memcpy(&fNode, inode, fNodeSize); 174 175 uint32 numLinks = fNode.NumLinks(); 176 fUnlinked = numLinks == 0 || (IsDirectory() && numLinks == 1); 177 178 return B_OK; 179 } 180 181 182 status_t 183 Inode::CheckPermissions(int accessMode) const 184 { 185 // you never have write access to a read-only volume 186 if ((accessMode & W_OK) != 0 && fVolume->IsReadOnly()) 187 return B_READ_ONLY_DEVICE; 188 189 return check_access_permissions(accessMode, Mode(), (gid_t)fNode.GroupID(), 190 (uid_t)fNode.UserID()); 191 } 192 193 194 status_t 195 Inode::FindBlock(off_t offset, fsblock_t& block, uint32 *_count) 196 { 197 if (Flags() & EXT2_INODE_EXTENTS) { 198 ExtentStream stream(fVolume, &fNode.extent_stream, Size()); 199 return stream.FindBlock(offset, block, _count); 200 } 201 DataStream stream(fVolume, &fNode.stream, Size()); 202 return stream.FindBlock(offset, block, _count); 203 } 204 205 206 status_t 207 Inode::ReadAt(off_t pos, uint8* buffer, size_t* _length) 208 { 209 return file_cache_read(FileCache(), NULL, pos, buffer, _length); 210 } 211 212 213 status_t 214 Inode::WriteAt(Transaction& transaction, off_t pos, const uint8* buffer, 215 size_t* _length) 216 { 217 TRACE("Inode::WriteAt(%" B_PRIdOFF ", %p, *(%p) = %" B_PRIuSIZE ")\n", pos, 218 buffer, _length, *_length); 219 ReadLocker readLocker(fLock); 220 221 if (!HasFileCache()) 222 return B_BAD_VALUE; 223 224 if (pos < 0) 225 return B_BAD_VALUE; 226 227 readLocker.Unlock(); 228 229 TRACE("Inode::WriteAt(): Starting transaction\n"); 230 transaction.Start(fVolume->GetJournal()); 231 232 WriteLocker writeLocker(fLock); 233 234 TRACE("Inode::WriteAt(): Updating modification time\n"); 235 struct timespec timespec; 236 _BigtimeToTimespec(real_time_clock_usecs(), ×pec); 237 SetModificationTime(×pec); 238 239 // NOTE: Debugging info to find why sometimes resize doesn't happen 240 size_t length = *_length; 241 #ifdef TRACE_EXT2 242 off_t oldEnd = pos + length; 243 TRACE("Inode::WriteAt(): Old calc for end? %x:%x\n", 244 (int)(oldEnd >> 32), (int)(oldEnd & 0xFFFFFFFF)); 245 #endif 246 247 off_t end = pos + (off_t)length; 248 off_t oldSize = Size(); 249 250 TRACE("Inode::WriteAt(): Old size: %" B_PRIdOFF ":%" B_PRIdOFF 251 ", new size: %" B_PRIdOFF ":%" B_PRIdOFF "\n", 252 oldSize >> 32, oldSize & 0xFFFFFFFF, 253 end >> 32, end & 0xFFFFFFFF); 254 255 if (end > oldSize) { 256 status_t status = Resize(transaction, end); 257 if (status != B_OK) { 258 *_length = 0; 259 WriteLockInTransaction(transaction); 260 return status; 261 } 262 263 status = WriteBack(transaction); 264 if (status != B_OK) { 265 *_length = 0; 266 WriteLockInTransaction(transaction); 267 return status; 268 } 269 } 270 271 writeLocker.Unlock(); 272 273 if (oldSize < pos) 274 FillGapWithZeros(oldSize, pos); 275 276 if (length == 0) { 277 // Probably just changed the file size with the pos parameter 278 return B_OK; 279 } 280 281 TRACE("Inode::WriteAt(): Performing write: %p, %" B_PRIdOFF ", %p, %" 282 B_PRIuSIZE "\n", FileCache(), pos, buffer, *_length); 283 status_t status = file_cache_write(FileCache(), NULL, pos, buffer, 284 _length); 285 286 WriteLockInTransaction(transaction); 287 288 TRACE("Inode::WriteAt(): Done\n"); 289 290 return status; 291 } 292 293 294 status_t 295 Inode::FillGapWithZeros(off_t start, off_t end) 296 { 297 TRACE("Inode::FileGapWithZeros(%" B_PRIdOFF " - %" B_PRIdOFF ")\n", start, 298 end); 299 300 while (start < end) { 301 size_t size; 302 303 if (end > start + 1024 * 1024 * 1024) 304 size = 1024 * 1024 * 1024; 305 else 306 size = end - start; 307 308 TRACE("Inode::FillGapWithZeros(): Calling file_cache_write(%p, NULL, " 309 "%" B_PRIdOFF ", NULL, &(%" B_PRIuSIZE ") = %p)\n", fCache, start, 310 size, &size); 311 status_t status = file_cache_write(fCache, NULL, start, NULL, 312 &size); 313 if (status != B_OK) 314 return status; 315 316 start += size; 317 } 318 319 return B_OK; 320 } 321 322 323 status_t 324 Inode::Resize(Transaction& transaction, off_t size) 325 { 326 TRACE("Inode::Resize() ID:%" B_PRIdINO " size: %" B_PRIdOFF "\n", ID(), 327 size); 328 if (size < 0) 329 return B_BAD_VALUE; 330 331 off_t oldSize = Size(); 332 333 if (size == oldSize) 334 return B_OK; 335 336 TRACE("Inode::Resize(): old size: %" B_PRIdOFF ", new size: %" B_PRIdOFF 337 "\n", oldSize, size); 338 339 status_t status; 340 if (size > oldSize) { 341 status = _EnlargeDataStream(transaction, size); 342 if (status != B_OK) { 343 // Restore original size 344 _ShrinkDataStream(transaction, oldSize); 345 } 346 } else 347 status = _ShrinkDataStream(transaction, size); 348 349 TRACE("Inode::Resize(): Updating file map and cache\n"); 350 351 if (status != B_OK) 352 return status; 353 354 file_cache_set_size(FileCache(), size); 355 file_map_set_size(Map(), size); 356 357 TRACE("Inode::Resize(): Writing back inode changes. Size: %" B_PRIdOFF 358 "\n", Size()); 359 360 return WriteBack(transaction); 361 } 362 363 364 status_t 365 Inode::InitDirectory(Transaction& transaction, Inode* parent) 366 { 367 TRACE("Inode::InitDirectory()\n"); 368 uint32 blockSize = fVolume->BlockSize(); 369 370 status_t status = Resize(transaction, blockSize); 371 if (status != B_OK) 372 return status; 373 374 fsblock_t blockNum; 375 if (Flags() & EXT2_INODE_EXTENTS) { 376 ExtentStream stream(fVolume, &fNode.extent_stream, Size()); 377 status = stream.FindBlock(0, blockNum); 378 } else { 379 DataStream stream(fVolume, &fNode.stream, Size()); 380 status = stream.FindBlock(0, blockNum); 381 } 382 if (status != B_OK) 383 return status; 384 385 CachedBlock cached(fVolume); 386 uint8* block = cached.SetToWritable(transaction, blockNum, true); 387 388 HTreeRoot* root = (HTreeRoot*)block; 389 root->dot.inode_id = fID; 390 root->dot.entry_length = 12; 391 root->dot.name_length = 1; 392 root->dot.file_type = EXT2_TYPE_DIRECTORY; 393 root->dot_entry_name[0] = '.'; 394 395 root->dotdot.inode_id = parent == NULL ? fID : parent->ID(); 396 root->dotdot.entry_length = blockSize - 12; 397 root->dotdot.name_length = 2; 398 root->dotdot.file_type = EXT2_TYPE_DIRECTORY; 399 root->dotdot_entry_name[0] = '.'; 400 root->dotdot_entry_name[1] = '.'; 401 402 parent->IncrementNumLinks(transaction); 403 404 return parent->WriteBack(transaction); 405 } 406 407 408 status_t 409 Inode::Unlink(Transaction& transaction) 410 { 411 uint32 numLinks = fNode.NumLinks(); 412 TRACE("Inode::Unlink(): Current links: %" B_PRIu32 "\n", numLinks); 413 414 if (numLinks == 0) 415 return B_BAD_VALUE; 416 417 if ((IsDirectory() && numLinks == 2) || (numLinks == 1)) { 418 fUnlinked = true; 419 420 TRACE("Inode::Unlink(): Putting inode in orphan list\n"); 421 ino_t firstOrphanID; 422 status_t status = fVolume->SaveOrphan(transaction, fID, firstOrphanID); 423 if (status != B_OK) 424 return status; 425 426 if (firstOrphanID != 0) { 427 Vnode firstOrphan(fVolume, firstOrphanID); 428 Inode* nextOrphan; 429 430 status = firstOrphan.Get(&nextOrphan); 431 if (status != B_OK) 432 return status; 433 434 fNode.SetNextOrphan(nextOrphan->ID()); 435 } else { 436 // Next orphan link is stored in deletion time 437 fNode.deletion_time = 0; 438 } 439 440 fNode.num_links = 0; 441 442 status = remove_vnode(fVolume->FSVolume(), fID); 443 if (status != B_OK) 444 return status; 445 } else 446 fNode.SetNumLinks(--numLinks); 447 448 return WriteBack(transaction); 449 } 450 451 452 /*static*/ status_t 453 Inode::Create(Transaction& transaction, Inode* parent, const char* name, 454 int32 mode, int openMode, uint8 type, bool* _created, ino_t* _id, 455 Inode** _inode, fs_vnode_ops* vnodeOps, uint32 publishFlags) 456 { 457 TRACE("Inode::Create()\n"); 458 Volume* volume = transaction.GetVolume(); 459 460 DirectoryIterator* entries = NULL; 461 ObjectDeleter<DirectoryIterator> entriesDeleter; 462 463 if (parent != NULL) { 464 parent->WriteLockInTransaction(transaction); 465 466 TRACE("Inode::Create(): Looking up entry destination\n"); 467 HTree htree(volume, parent); 468 469 status_t status = htree.Lookup(name, &entries); 470 if (status == B_ENTRY_NOT_FOUND) { 471 panic("We need to add the first node.\n"); 472 return B_ERROR; 473 } 474 if (status != B_OK) 475 return status; 476 entriesDeleter.SetTo(entries); 477 478 TRACE("Inode::Create(): Looking up to see if file already exists\n"); 479 ino_t entryID; 480 481 status = entries->FindEntry(name, &entryID); 482 if (status == B_OK) { 483 // File already exists 484 TRACE("Inode::Create(): File already exists\n"); 485 if (S_ISDIR(mode) || S_ISLNK(mode) || (openMode & O_EXCL) != 0) 486 return B_FILE_EXISTS; 487 488 Vnode vnode(volume, entryID); 489 Inode* inode; 490 491 status = vnode.Get(&inode); 492 if (status != B_OK) { 493 TRACE("Inode::Create() Failed to get the inode from the " 494 "vnode\n"); 495 return B_ENTRY_NOT_FOUND; 496 } 497 498 if (inode->IsDirectory() && (openMode & O_RWMASK) != O_RDONLY) 499 return B_IS_A_DIRECTORY; 500 if ((openMode & O_DIRECTORY) != 0 && !inode->IsDirectory()) 501 return B_NOT_A_DIRECTORY; 502 503 if (inode->CheckPermissions(open_mode_to_access(openMode) 504 | ((openMode & O_TRUNC) != 0 ? W_OK : 0)) != B_OK) 505 return B_NOT_ALLOWED; 506 507 if ((openMode & O_TRUNC) != 0) { 508 // Truncate requested 509 TRACE("Inode::Create(): Truncating file\n"); 510 inode->WriteLockInTransaction(transaction); 511 512 status = inode->Resize(transaction, 0); 513 if (status != B_OK) 514 return status; 515 } 516 517 if (_created != NULL) 518 *_created = false; 519 if (_id != NULL) 520 *_id = inode->ID(); 521 if (_inode != NULL) 522 *_inode = inode; 523 524 if (_id != NULL || _inode != NULL) 525 vnode.Keep(); 526 527 TRACE("Inode::Create(): Done opening file\n"); 528 return B_OK; 529 /*} else if ((mode & S_ATTR_DIR) == 0) { 530 TRACE("Inode::Create(): (mode & S_ATTR_DIR) == 0\n"); 531 return B_BAD_VALUE;*/ 532 } else if ((openMode & O_DIRECTORY) != 0) { 533 TRACE("Inode::Create(): (openMode & O_DIRECTORY) != 0\n"); 534 return B_ENTRY_NOT_FOUND; 535 } 536 537 // Return to initial position 538 TRACE("Inode::Create(): Restarting iterator\n"); 539 entries->Restart(); 540 } 541 542 status_t status; 543 if (parent != NULL) { 544 status = parent->CheckPermissions(W_OK); 545 if (status != B_OK) 546 return status; 547 } 548 549 TRACE("Inode::Create(): Allocating inode\n"); 550 ino_t id; 551 status = volume->AllocateInode(transaction, parent, mode, id); 552 if (status != B_OK) { 553 ERROR("Inode::Create(): AllocateInode() failed\n"); 554 return status; 555 } 556 557 if (entries != NULL) { 558 size_t nameLength = strlen(name); 559 status = entries->AddEntry(transaction, name, nameLength, id, type); 560 if (status != B_OK) { 561 ERROR("Inode::Create(): AddEntry() failed\n"); 562 return status; 563 } 564 } 565 566 TRACE("Inode::Create(): Creating inode\n"); 567 Inode* inode = new(std::nothrow) Inode(volume); 568 if (inode == NULL) 569 return B_NO_MEMORY; 570 571 TRACE("Inode::Create(): Getting node structure\n"); 572 ext2_inode& node = inode->Node(); 573 TRACE("Inode::Create(): Initializing inode data\n"); 574 memset(&node, 0, sizeof(ext2_inode)); 575 node.SetMode(mode); 576 node.SetUserID(geteuid()); 577 node.SetGroupID(parent != NULL ? parent->Node().GroupID() : getegid()); 578 node.SetNumLinks(inode->IsDirectory() ? 2 : 1); 579 TRACE("Inode::Create(): Updating time\n"); 580 struct timespec timespec; 581 _BigtimeToTimespec(real_time_clock_usecs(), ×pec); 582 inode->SetAccessTime(×pec); 583 inode->SetCreationTime(×pec); 584 inode->SetModificationTime(×pec); 585 if (parent != NULL) 586 node.SetFlags(parent->Flags() & EXT2_INODE_INHERITED); 587 if (volume->HasExtentsFeature() 588 && (inode->IsDirectory() || inode->IsFile())) { 589 node.SetFlag(EXT2_INODE_EXTENTS); 590 ExtentStream stream(volume, &node.extent_stream, 0); 591 stream.Init(); 592 ASSERT(stream.Check()); 593 } 594 595 if (sizeof(ext2_inode) < volume->InodeSize()) 596 node.SetExtraInodeSize(sizeof(ext2_inode) - EXT2_INODE_NORMAL_SIZE); 597 598 TRACE("Inode::Create(): Updating ID\n"); 599 inode->fID = id; 600 601 if (inode->IsDirectory()) { 602 TRACE("Inode::Create(): Initializing directory\n"); 603 status = inode->InitDirectory(transaction, parent); 604 if (status != B_OK) { 605 ERROR("Inode::Create(): InitDirectory() failed\n"); 606 delete inode; 607 return status; 608 } 609 } 610 611 // TODO: Maybe it can be better 612 /*if (volume->HasExtendedAttributes()) { 613 TRACE("Inode::Create(): Initializing extended attributes\n"); 614 uint32 blockGroup = 0; 615 uint32 pos = 0; 616 uint32 allocated; 617 618 status = volume->AllocateBlocks(transaction, 1, 1, blockGroup, pos, 619 allocated); 620 if (status != B_OK) 621 return status; 622 623 // Clear the new block 624 uint32 blockNum = volume->FirstDataBlock() + pos + 625 volume->BlocksPerGroup() * blockGroup; 626 CachedBlock cached(volume); 627 cached.SetToWritable(transaction, blockNum, true); 628 629 node.SetExtendedAttributesBlock(blockNum); 630 }*/ 631 632 TRACE("Inode::Create(): Saving inode\n"); 633 status = inode->WriteBack(transaction); 634 if (status != B_OK) { 635 delete inode; 636 return status; 637 } 638 639 TRACE("Inode::Create(): Creating vnode\n"); 640 641 Vnode vnode; 642 status = vnode.Publish(transaction, inode, vnodeOps, publishFlags); 643 if (status != B_OK) 644 return status; 645 646 if (!inode->IsSymLink()) { 647 // Vnode::Publish doesn't publish symlinks 648 if (!inode->IsDirectory()) { 649 status = inode->CreateFileCache(); 650 if (status != B_OK) 651 return status; 652 } 653 654 inode->WriteLockInTransaction(transaction); 655 } 656 657 if (_created) 658 *_created = true; 659 if (_id != NULL) 660 *_id = id; 661 if (_inode != NULL) 662 *_inode = inode; 663 664 if (_id != NULL || _inode != NULL) 665 vnode.Keep(); 666 667 TRACE("Inode::Create(): Deleting entries iterator\n"); 668 DirectoryIterator* iterator = entriesDeleter.Detach(); 669 TRACE("Inode::Create(): Entries iterator: %p\n", entries); 670 delete iterator; 671 TRACE("Inode::Create(): Done\n"); 672 673 return B_OK; 674 } 675 676 677 status_t 678 Inode::CreateFileCache() 679 { 680 TRACE("Inode::CreateFileCache()\n"); 681 682 if (fCache != NULL) 683 return B_OK; 684 685 TRACE("Inode::CreateFileCache(): Creating file cache: %" B_PRIu32 ", %" 686 B_PRIdINO ", %" B_PRIdOFF "\n", fVolume->ID(), ID(), Size()); 687 688 fCache = file_cache_create(fVolume->ID(), ID(), Size()); 689 if (fCache == NULL) { 690 ERROR("Inode::CreateFileCache(): Failed to create file cache\n"); 691 return B_ERROR; 692 } 693 694 fMap = file_map_create(fVolume->ID(), ID(), Size()); 695 if (fMap == NULL) { 696 ERROR("Inode::CreateFileCache(): Failed to create file map\n"); 697 file_cache_delete(fCache); 698 fCache = NULL; 699 return B_ERROR; 700 } 701 702 TRACE("Inode::CreateFileCache(): Done\n"); 703 704 return B_OK; 705 } 706 707 708 void 709 Inode::DeleteFileCache() 710 { 711 TRACE("Inode::DeleteFileCache()\n"); 712 713 if (fCache == NULL) 714 return; 715 716 file_cache_delete(fCache); 717 file_map_delete(fMap); 718 719 fCache = NULL; 720 fMap = NULL; 721 } 722 723 724 status_t 725 Inode::EnableFileCache() 726 { 727 if (fCache == NULL) 728 return B_BAD_VALUE; 729 730 file_cache_enable(fCache); 731 return B_OK; 732 } 733 734 735 status_t 736 Inode::DisableFileCache() 737 { 738 status_t error = file_cache_disable(fCache); 739 if (error != B_OK) 740 return error; 741 742 return B_OK; 743 } 744 745 746 status_t 747 Inode::Sync() 748 { 749 if (HasFileCache()) 750 return file_cache_sync(fCache); 751 752 return B_OK; 753 } 754 755 756 void 757 Inode::TransactionDone(bool success) 758 { 759 if (!success) { 760 // Revert any changes to the inode 761 if (fInitStatus == B_OK && UpdateNodeFromDisk() != B_OK) 762 panic("Failed to reload inode from disk!\n"); 763 else if (fInitStatus == B_NO_INIT) { 764 // TODO: Unpublish vnode? 765 panic("Failed to finish creating inode\n"); 766 } 767 } else { 768 if (fInitStatus == B_NO_INIT) { 769 TRACE("Inode::TransactionDone(): Inode creation succeeded\n"); 770 fInitStatus = B_OK; 771 } 772 } 773 } 774 775 776 void 777 Inode::RemovedFromTransaction() 778 { 779 TRACE("Inode::RemovedFromTransaction(): Unlocking\n"); 780 rw_lock_write_unlock(&fLock); 781 782 put_vnode(fVolume->FSVolume(), ID()); 783 } 784 785 786 status_t 787 Inode::_EnlargeDataStream(Transaction& transaction, off_t size) 788 { 789 if (size < 0) 790 return B_BAD_VALUE; 791 792 TRACE("Inode::_EnlargeDataStream()\n"); 793 794 uint32 blockSize = fVolume->BlockSize(); 795 off_t oldSize = Size(); 796 off_t maxSize = oldSize; 797 if (maxSize % blockSize != 0) 798 maxSize += blockSize - maxSize % blockSize; 799 800 if (size <= maxSize) { 801 // No need to allocate more blocks 802 TRACE("Inode::_EnlargeDataStream(): No need to allocate more blocks\n"); 803 TRACE("Inode::_EnlargeDataStream(): Setting size to %" B_PRIdOFF "\n", 804 size); 805 fNode.SetSize(size); 806 return B_OK; 807 } 808 809 off_t end = size == 0 ? 0 : (size - 1) / fVolume->BlockSize() + 1; 810 if (Flags() & EXT2_INODE_EXTENTS) { 811 ExtentStream stream(fVolume, &fNode.extent_stream, Size()); 812 stream.Enlarge(transaction, end); 813 ASSERT(stream.Check()); 814 } else { 815 DataStream stream(fVolume, &fNode.stream, oldSize); 816 stream.Enlarge(transaction, end); 817 } 818 TRACE("Inode::_EnlargeDataStream(): Setting size to %" B_PRIdOFF "\n", 819 size); 820 fNode.SetSize(size); 821 TRACE("Inode::_EnlargeDataStream(): Setting allocated block count to %" 822 B_PRIdOFF "\n", end); 823 return _SetNumBlocks(_NumBlocks() + end * (fVolume->BlockSize() / 512)); 824 } 825 826 827 status_t 828 Inode::_ShrinkDataStream(Transaction& transaction, off_t size) 829 { 830 TRACE("Inode::_ShrinkDataStream()\n"); 831 832 if (size < 0) 833 return B_BAD_VALUE; 834 835 uint32 blockSize = fVolume->BlockSize(); 836 off_t oldSize = Size(); 837 off_t lastByte = oldSize == 0 ? 0 : oldSize - 1; 838 off_t minSize = (lastByte / blockSize + 1) * blockSize; 839 // Minimum size that doesn't require freeing blocks 840 841 if (size > minSize) { 842 // No need to allocate more blocks 843 TRACE("Inode::_ShrinkDataStream(): No need to allocate more blocks\n"); 844 TRACE("Inode::_ShrinkDataStream(): Setting size to %" B_PRIdOFF "\n", 845 size); 846 fNode.SetSize(size); 847 return B_OK; 848 } 849 850 off_t end = size == 0 ? 0 : (size - 1) / fVolume->BlockSize() + 1; 851 if (Flags() & EXT2_INODE_EXTENTS) { 852 ExtentStream stream(fVolume, &fNode.extent_stream, Size()); 853 stream.Shrink(transaction, end); 854 ASSERT(stream.Check()); 855 } else { 856 DataStream stream(fVolume, &fNode.stream, oldSize); 857 stream.Shrink(transaction, end); 858 } 859 860 fNode.SetSize(size); 861 return _SetNumBlocks(_NumBlocks() - end * (fVolume->BlockSize() / 512)); 862 } 863 864 865 uint64 866 Inode::_NumBlocks() 867 { 868 if (fVolume->HugeFiles()) { 869 if (fNode.Flags() & EXT2_INODE_HUGE_FILE) 870 return fNode.NumBlocks64() * (fVolume->BlockSize() / 512); 871 else 872 return fNode.NumBlocks64(); 873 } else 874 return fNode.NumBlocks(); 875 } 876 877 878 status_t 879 Inode::_SetNumBlocks(uint64 numBlocks) 880 { 881 if (numBlocks <= 0xffffffff) { 882 fNode.SetNumBlocks(numBlocks); 883 fNode.ClearFlag(EXT2_INODE_HUGE_FILE); 884 return B_OK; 885 } 886 if (!fVolume->HugeFiles()) 887 return E2BIG; 888 889 if (numBlocks > 0xffffffffffffULL) { 890 fNode.SetFlag(EXT2_INODE_HUGE_FILE); 891 numBlocks /= (fVolume->BlockSize() / 512); 892 } else 893 fNode.ClearFlag(EXT2_INODE_HUGE_FILE); 894 895 fNode.SetNumBlocks64(numBlocks); 896 return B_OK; 897 } 898 899 900 void 901 Inode::IncrementNumLinks(Transaction& transaction) 902 { 903 fNode.SetNumLinks(fNode.NumLinks() + 1); 904 if (IsIndexed() && (fNode.NumLinks() >= EXT2_INODE_MAX_LINKS 905 || fNode.NumLinks() == 2)) { 906 fNode.SetNumLinks(1); 907 fVolume->ActivateDirNLink(transaction); 908 } 909 } 910