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 size_t length = *_length; 210 211 // set/check boundaries for pos/length 212 if (pos < 0) { 213 ERROR("inode %" B_PRIdINO ": ReadAt failed(pos %" B_PRIdOFF 214 ", length %" B_PRIuSIZE ")\n", ID(), pos, length); 215 return B_BAD_VALUE; 216 } 217 218 if (pos >= Size() || length == 0) { 219 TRACE("inode %" B_PRIdINO ": ReadAt 0 (pos %" B_PRIdOFF ", length %" 220 B_PRIuSIZE ")\n", ID(), pos, length); 221 *_length = 0; 222 return B_NO_ERROR; 223 } 224 225 return file_cache_read(FileCache(), NULL, pos, buffer, _length); 226 } 227 228 229 status_t 230 Inode::WriteAt(Transaction& transaction, off_t pos, const uint8* buffer, 231 size_t* _length) 232 { 233 TRACE("Inode::WriteAt(%" B_PRIdOFF ", %p, *(%p) = %" B_PRIuSIZE ")\n", pos, 234 buffer, _length, *_length); 235 ReadLocker readLocker(fLock); 236 237 if (!HasFileCache()) 238 return B_BAD_VALUE; 239 240 if (pos < 0) 241 return B_BAD_VALUE; 242 243 readLocker.Unlock(); 244 245 TRACE("Inode::WriteAt(): Starting transaction\n"); 246 transaction.Start(fVolume->GetJournal()); 247 248 WriteLocker writeLocker(fLock); 249 250 TRACE("Inode::WriteAt(): Updating modification time\n"); 251 struct timespec timespec; 252 _BigtimeToTimespec(real_time_clock_usecs(), ×pec); 253 SetModificationTime(×pec); 254 255 // NOTE: Debugging info to find why sometimes resize doesn't happen 256 size_t length = *_length; 257 #ifdef TRACE_EXT2 258 off_t oldEnd = pos + length; 259 TRACE("Inode::WriteAt(): Old calc for end? %x:%x\n", 260 (int)(oldEnd >> 32), (int)(oldEnd & 0xFFFFFFFF)); 261 #endif 262 263 off_t end = pos + (off_t)length; 264 off_t oldSize = Size(); 265 266 TRACE("Inode::WriteAt(): Old size: %" B_PRIdOFF ":%" B_PRIdOFF 267 ", new size: %" B_PRIdOFF ":%" B_PRIdOFF "\n", 268 oldSize >> 32, oldSize & 0xFFFFFFFF, 269 end >> 32, end & 0xFFFFFFFF); 270 271 if (end > oldSize) { 272 status_t status = Resize(transaction, end); 273 if (status != B_OK) { 274 *_length = 0; 275 WriteLockInTransaction(transaction); 276 return status; 277 } 278 279 status = WriteBack(transaction); 280 if (status != B_OK) { 281 *_length = 0; 282 WriteLockInTransaction(transaction); 283 return status; 284 } 285 } 286 287 writeLocker.Unlock(); 288 289 if (oldSize < pos) 290 FillGapWithZeros(oldSize, pos); 291 292 if (length == 0) { 293 // Probably just changed the file size with the pos parameter 294 return B_OK; 295 } 296 297 TRACE("Inode::WriteAt(): Performing write: %p, %" B_PRIdOFF ", %p, %" 298 B_PRIuSIZE "\n", FileCache(), pos, buffer, *_length); 299 status_t status = file_cache_write(FileCache(), NULL, pos, buffer, 300 _length); 301 302 WriteLockInTransaction(transaction); 303 304 TRACE("Inode::WriteAt(): Done\n"); 305 306 return status; 307 } 308 309 310 status_t 311 Inode::FillGapWithZeros(off_t start, off_t end) 312 { 313 TRACE("Inode::FileGapWithZeros(%" B_PRIdOFF " - %" B_PRIdOFF ")\n", start, 314 end); 315 316 while (start < end) { 317 size_t size; 318 319 if (end > start + 1024 * 1024 * 1024) 320 size = 1024 * 1024 * 1024; 321 else 322 size = end - start; 323 324 TRACE("Inode::FillGapWithZeros(): Calling file_cache_write(%p, NULL, " 325 "%" B_PRIdOFF ", NULL, &(%" B_PRIuSIZE ") = %p)\n", fCache, start, 326 size, &size); 327 status_t status = file_cache_write(fCache, NULL, start, NULL, 328 &size); 329 if (status != B_OK) 330 return status; 331 332 start += size; 333 } 334 335 return B_OK; 336 } 337 338 339 status_t 340 Inode::Resize(Transaction& transaction, off_t size) 341 { 342 TRACE("Inode::Resize() ID:%" B_PRIdINO " size: %" B_PRIdOFF "\n", ID(), 343 size); 344 if (size < 0) 345 return B_BAD_VALUE; 346 347 off_t oldSize = Size(); 348 349 if (size == oldSize) 350 return B_OK; 351 352 TRACE("Inode::Resize(): old size: %" B_PRIdOFF ", new size: %" B_PRIdOFF 353 "\n", oldSize, size); 354 355 status_t status; 356 if (size > oldSize) { 357 status = _EnlargeDataStream(transaction, size); 358 if (status != B_OK) { 359 // Restore original size 360 _ShrinkDataStream(transaction, oldSize); 361 } 362 } else 363 status = _ShrinkDataStream(transaction, size); 364 365 TRACE("Inode::Resize(): Updating file map and cache\n"); 366 367 if (status != B_OK) 368 return status; 369 370 file_cache_set_size(FileCache(), size); 371 file_map_set_size(Map(), size); 372 373 TRACE("Inode::Resize(): Writing back inode changes. Size: %" B_PRIdOFF 374 "\n", Size()); 375 376 return WriteBack(transaction); 377 } 378 379 380 status_t 381 Inode::InitDirectory(Transaction& transaction, Inode* parent) 382 { 383 TRACE("Inode::InitDirectory()\n"); 384 uint32 blockSize = fVolume->BlockSize(); 385 386 status_t status = Resize(transaction, blockSize); 387 if (status != B_OK) 388 return status; 389 390 fsblock_t blockNum; 391 if (Flags() & EXT2_INODE_EXTENTS) { 392 ExtentStream stream(fVolume, &fNode.extent_stream, Size()); 393 status = stream.FindBlock(0, blockNum); 394 } else { 395 DataStream stream(fVolume, &fNode.stream, Size()); 396 status = stream.FindBlock(0, blockNum); 397 } 398 if (status != B_OK) 399 return status; 400 401 CachedBlock cached(fVolume); 402 uint8* block = cached.SetToWritable(transaction, blockNum, true); 403 404 HTreeRoot* root = (HTreeRoot*)block; 405 root->dot.inode_id = fID; 406 root->dot.entry_length = 12; 407 root->dot.name_length = 1; 408 root->dot.file_type = EXT2_TYPE_DIRECTORY; 409 root->dot_entry_name[0] = '.'; 410 411 root->dotdot.inode_id = parent == NULL ? fID : parent->ID(); 412 root->dotdot.entry_length = blockSize - 12; 413 root->dotdot.name_length = 2; 414 root->dotdot.file_type = EXT2_TYPE_DIRECTORY; 415 root->dotdot_entry_name[0] = '.'; 416 root->dotdot_entry_name[1] = '.'; 417 418 parent->IncrementNumLinks(transaction); 419 420 return parent->WriteBack(transaction); 421 } 422 423 424 status_t 425 Inode::Unlink(Transaction& transaction) 426 { 427 uint32 numLinks = fNode.NumLinks(); 428 TRACE("Inode::Unlink(): Current links: %" B_PRIu32 "\n", numLinks); 429 430 if (numLinks == 0) 431 return B_BAD_VALUE; 432 433 if ((IsDirectory() && numLinks == 2) || (numLinks == 1)) { 434 fUnlinked = true; 435 436 TRACE("Inode::Unlink(): Putting inode in orphan list\n"); 437 ino_t firstOrphanID; 438 status_t status = fVolume->SaveOrphan(transaction, fID, firstOrphanID); 439 if (status != B_OK) 440 return status; 441 442 if (firstOrphanID != 0) { 443 Vnode firstOrphan(fVolume, firstOrphanID); 444 Inode* nextOrphan; 445 446 status = firstOrphan.Get(&nextOrphan); 447 if (status != B_OK) 448 return status; 449 450 fNode.SetNextOrphan(nextOrphan->ID()); 451 } else { 452 // Next orphan link is stored in deletion time 453 fNode.deletion_time = 0; 454 } 455 456 fNode.num_links = 0; 457 458 status = remove_vnode(fVolume->FSVolume(), fID); 459 if (status != B_OK) 460 return status; 461 } else 462 fNode.SetNumLinks(--numLinks); 463 464 return WriteBack(transaction); 465 } 466 467 468 /*static*/ status_t 469 Inode::Create(Transaction& transaction, Inode* parent, const char* name, 470 int32 mode, int openMode, uint8 type, bool* _created, ino_t* _id, 471 Inode** _inode, fs_vnode_ops* vnodeOps, uint32 publishFlags) 472 { 473 TRACE("Inode::Create()\n"); 474 Volume* volume = transaction.GetVolume(); 475 476 DirectoryIterator* entries = NULL; 477 ObjectDeleter<DirectoryIterator> entriesDeleter; 478 479 if (parent != NULL) { 480 parent->WriteLockInTransaction(transaction); 481 482 TRACE("Inode::Create(): Looking up entry destination\n"); 483 HTree htree(volume, parent); 484 485 status_t status = htree.Lookup(name, &entries); 486 if (status == B_ENTRY_NOT_FOUND) { 487 panic("We need to add the first node.\n"); 488 return B_ERROR; 489 } 490 if (status != B_OK) 491 return status; 492 entriesDeleter.SetTo(entries); 493 494 TRACE("Inode::Create(): Looking up to see if file already exists\n"); 495 ino_t entryID; 496 497 status = entries->FindEntry(name, &entryID); 498 if (status == B_OK) { 499 // File already exists 500 TRACE("Inode::Create(): File already exists\n"); 501 if (S_ISDIR(mode) || S_ISLNK(mode) || (openMode & O_EXCL) != 0) 502 return B_FILE_EXISTS; 503 504 Vnode vnode(volume, entryID); 505 Inode* inode; 506 507 status = vnode.Get(&inode); 508 if (status != B_OK) { 509 TRACE("Inode::Create() Failed to get the inode from the " 510 "vnode\n"); 511 return B_ENTRY_NOT_FOUND; 512 } 513 514 if (inode->IsDirectory() && (openMode & O_RWMASK) != O_RDONLY) 515 return B_IS_A_DIRECTORY; 516 if ((openMode & O_DIRECTORY) != 0 && !inode->IsDirectory()) 517 return B_NOT_A_DIRECTORY; 518 519 if (inode->CheckPermissions(open_mode_to_access(openMode) 520 | ((openMode & O_TRUNC) != 0 ? W_OK : 0)) != B_OK) 521 return B_NOT_ALLOWED; 522 523 if ((openMode & O_TRUNC) != 0) { 524 // Truncate requested 525 TRACE("Inode::Create(): Truncating file\n"); 526 inode->WriteLockInTransaction(transaction); 527 528 status = inode->Resize(transaction, 0); 529 if (status != B_OK) 530 return status; 531 } 532 533 if (_created != NULL) 534 *_created = false; 535 if (_id != NULL) 536 *_id = inode->ID(); 537 if (_inode != NULL) 538 *_inode = inode; 539 540 if (_id != NULL || _inode != NULL) 541 vnode.Keep(); 542 543 TRACE("Inode::Create(): Done opening file\n"); 544 return B_OK; 545 /*} else if ((mode & S_ATTR_DIR) == 0) { 546 TRACE("Inode::Create(): (mode & S_ATTR_DIR) == 0\n"); 547 return B_BAD_VALUE;*/ 548 } else if ((openMode & O_DIRECTORY) != 0) { 549 TRACE("Inode::Create(): (openMode & O_DIRECTORY) != 0\n"); 550 return B_ENTRY_NOT_FOUND; 551 } 552 553 // Return to initial position 554 TRACE("Inode::Create(): Restarting iterator\n"); 555 entries->Restart(); 556 } 557 558 status_t status; 559 if (parent != NULL) { 560 status = parent->CheckPermissions(W_OK); 561 if (status != B_OK) 562 return status; 563 } 564 565 TRACE("Inode::Create(): Allocating inode\n"); 566 ino_t id; 567 status = volume->AllocateInode(transaction, parent, mode, id); 568 if (status != B_OK) { 569 ERROR("Inode::Create(): AllocateInode() failed\n"); 570 return status; 571 } 572 573 if (entries != NULL) { 574 size_t nameLength = strlen(name); 575 status = entries->AddEntry(transaction, name, nameLength, id, type); 576 if (status != B_OK) { 577 ERROR("Inode::Create(): AddEntry() failed\n"); 578 return status; 579 } 580 } 581 582 TRACE("Inode::Create(): Creating inode\n"); 583 Inode* inode = new(std::nothrow) Inode(volume); 584 if (inode == NULL) 585 return B_NO_MEMORY; 586 587 TRACE("Inode::Create(): Getting node structure\n"); 588 ext2_inode& node = inode->Node(); 589 TRACE("Inode::Create(): Initializing inode data\n"); 590 memset(&node, 0, sizeof(ext2_inode)); 591 node.SetMode(mode); 592 node.SetUserID(geteuid()); 593 node.SetGroupID(parent != NULL ? parent->Node().GroupID() : getegid()); 594 node.SetNumLinks(inode->IsDirectory() ? 2 : 1); 595 TRACE("Inode::Create(): Updating time\n"); 596 struct timespec timespec; 597 _BigtimeToTimespec(real_time_clock_usecs(), ×pec); 598 inode->SetAccessTime(×pec); 599 inode->SetCreationTime(×pec); 600 inode->SetModificationTime(×pec); 601 if (parent != NULL) 602 node.SetFlags(parent->Flags() & EXT2_INODE_INHERITED); 603 if (volume->HasExtentsFeature() 604 && (inode->IsDirectory() || inode->IsFile())) { 605 node.SetFlag(EXT2_INODE_EXTENTS); 606 ExtentStream stream(volume, &node.extent_stream, 0); 607 stream.Init(); 608 ASSERT(stream.Check()); 609 } 610 611 if (sizeof(ext2_inode) < volume->InodeSize()) 612 node.SetExtraInodeSize(sizeof(ext2_inode) - EXT2_INODE_NORMAL_SIZE); 613 614 TRACE("Inode::Create(): Updating ID\n"); 615 inode->fID = id; 616 617 if (inode->IsDirectory()) { 618 TRACE("Inode::Create(): Initializing directory\n"); 619 status = inode->InitDirectory(transaction, parent); 620 if (status != B_OK) { 621 ERROR("Inode::Create(): InitDirectory() failed\n"); 622 delete inode; 623 return status; 624 } 625 } 626 627 // TODO: Maybe it can be better 628 /*if (volume->HasExtendedAttributes()) { 629 TRACE("Inode::Create(): Initializing extended attributes\n"); 630 uint32 blockGroup = 0; 631 uint32 pos = 0; 632 uint32 allocated; 633 634 status = volume->AllocateBlocks(transaction, 1, 1, blockGroup, pos, 635 allocated); 636 if (status != B_OK) 637 return status; 638 639 // Clear the new block 640 uint32 blockNum = volume->FirstDataBlock() + pos + 641 volume->BlocksPerGroup() * blockGroup; 642 CachedBlock cached(volume); 643 cached.SetToWritable(transaction, blockNum, true); 644 645 node.SetExtendedAttributesBlock(blockNum); 646 }*/ 647 648 TRACE("Inode::Create(): Saving inode\n"); 649 status = inode->WriteBack(transaction); 650 if (status != B_OK) { 651 delete inode; 652 return status; 653 } 654 655 TRACE("Inode::Create(): Creating vnode\n"); 656 657 Vnode vnode; 658 status = vnode.Publish(transaction, inode, vnodeOps, publishFlags); 659 if (status != B_OK) 660 return status; 661 662 if (!inode->IsSymLink()) { 663 // Vnode::Publish doesn't publish symlinks 664 if (!inode->IsDirectory()) { 665 status = inode->CreateFileCache(); 666 if (status != B_OK) 667 return status; 668 } 669 670 inode->WriteLockInTransaction(transaction); 671 } 672 673 if (_created) 674 *_created = true; 675 if (_id != NULL) 676 *_id = id; 677 if (_inode != NULL) 678 *_inode = inode; 679 680 if (_id != NULL || _inode != NULL) 681 vnode.Keep(); 682 683 TRACE("Inode::Create(): Deleting entries iterator\n"); 684 DirectoryIterator* iterator = entriesDeleter.Detach(); 685 TRACE("Inode::Create(): Entries iterator: %p\n", entries); 686 delete iterator; 687 TRACE("Inode::Create(): Done\n"); 688 689 return B_OK; 690 } 691 692 693 status_t 694 Inode::CreateFileCache() 695 { 696 TRACE("Inode::CreateFileCache()\n"); 697 698 if (fCache != NULL) 699 return B_OK; 700 701 TRACE("Inode::CreateFileCache(): Creating file cache: %" B_PRIu32 ", %" 702 B_PRIdINO ", %" B_PRIdOFF "\n", fVolume->ID(), ID(), Size()); 703 704 fCache = file_cache_create(fVolume->ID(), ID(), Size()); 705 if (fCache == NULL) { 706 ERROR("Inode::CreateFileCache(): Failed to create file cache\n"); 707 return B_ERROR; 708 } 709 710 fMap = file_map_create(fVolume->ID(), ID(), Size()); 711 if (fMap == NULL) { 712 ERROR("Inode::CreateFileCache(): Failed to create file map\n"); 713 file_cache_delete(fCache); 714 fCache = NULL; 715 return B_ERROR; 716 } 717 718 TRACE("Inode::CreateFileCache(): Done\n"); 719 720 return B_OK; 721 } 722 723 724 void 725 Inode::DeleteFileCache() 726 { 727 TRACE("Inode::DeleteFileCache()\n"); 728 729 if (fCache == NULL) 730 return; 731 732 file_cache_delete(fCache); 733 file_map_delete(fMap); 734 735 fCache = NULL; 736 fMap = NULL; 737 } 738 739 740 status_t 741 Inode::EnableFileCache() 742 { 743 if (fCache == NULL) 744 return B_BAD_VALUE; 745 746 file_cache_enable(fCache); 747 return B_OK; 748 } 749 750 751 status_t 752 Inode::DisableFileCache() 753 { 754 status_t error = file_cache_disable(fCache); 755 if (error != B_OK) 756 return error; 757 758 return B_OK; 759 } 760 761 762 status_t 763 Inode::Sync() 764 { 765 if (HasFileCache()) 766 return file_cache_sync(fCache); 767 768 return B_OK; 769 } 770 771 772 void 773 Inode::TransactionDone(bool success) 774 { 775 if (!success) { 776 // Revert any changes to the inode 777 if (fInitStatus == B_OK && UpdateNodeFromDisk() != B_OK) 778 panic("Failed to reload inode from disk!\n"); 779 else if (fInitStatus == B_NO_INIT) { 780 // TODO: Unpublish vnode? 781 panic("Failed to finish creating inode\n"); 782 } 783 } else { 784 if (fInitStatus == B_NO_INIT) { 785 TRACE("Inode::TransactionDone(): Inode creation succeeded\n"); 786 fInitStatus = B_OK; 787 } 788 } 789 } 790 791 792 void 793 Inode::RemovedFromTransaction() 794 { 795 TRACE("Inode::RemovedFromTransaction(): Unlocking\n"); 796 rw_lock_write_unlock(&fLock); 797 798 put_vnode(fVolume->FSVolume(), ID()); 799 } 800 801 802 status_t 803 Inode::_EnlargeDataStream(Transaction& transaction, off_t size) 804 { 805 if (size < 0) 806 return B_BAD_VALUE; 807 808 TRACE("Inode::_EnlargeDataStream()\n"); 809 810 uint32 blockSize = fVolume->BlockSize(); 811 off_t oldSize = Size(); 812 off_t maxSize = oldSize; 813 if (maxSize % blockSize != 0) 814 maxSize += blockSize - maxSize % blockSize; 815 816 if (size <= maxSize) { 817 // No need to allocate more blocks 818 TRACE("Inode::_EnlargeDataStream(): No need to allocate more blocks\n"); 819 TRACE("Inode::_EnlargeDataStream(): Setting size to %" B_PRIdOFF "\n", 820 size); 821 fNode.SetSize(size); 822 return B_OK; 823 } 824 825 off_t end = size == 0 ? 0 : (size - 1) / fVolume->BlockSize() + 1; 826 if (Flags() & EXT2_INODE_EXTENTS) { 827 ExtentStream stream(fVolume, &fNode.extent_stream, Size()); 828 stream.Enlarge(transaction, end); 829 ASSERT(stream.Check()); 830 } else { 831 DataStream stream(fVolume, &fNode.stream, oldSize); 832 stream.Enlarge(transaction, end); 833 } 834 TRACE("Inode::_EnlargeDataStream(): Setting size to %" B_PRIdOFF "\n", 835 size); 836 fNode.SetSize(size); 837 TRACE("Inode::_EnlargeDataStream(): Setting allocated block count to %" 838 B_PRIdOFF "\n", end); 839 return _SetNumBlocks(_NumBlocks() + end * (fVolume->BlockSize() / 512)); 840 } 841 842 843 status_t 844 Inode::_ShrinkDataStream(Transaction& transaction, off_t size) 845 { 846 TRACE("Inode::_ShrinkDataStream()\n"); 847 848 if (size < 0) 849 return B_BAD_VALUE; 850 851 uint32 blockSize = fVolume->BlockSize(); 852 off_t oldSize = Size(); 853 off_t lastByte = oldSize == 0 ? 0 : oldSize - 1; 854 off_t minSize = (lastByte / blockSize + 1) * blockSize; 855 // Minimum size that doesn't require freeing blocks 856 857 if (size > minSize) { 858 // No need to allocate more blocks 859 TRACE("Inode::_ShrinkDataStream(): No need to allocate more blocks\n"); 860 TRACE("Inode::_ShrinkDataStream(): Setting size to %" B_PRIdOFF "\n", 861 size); 862 fNode.SetSize(size); 863 return B_OK; 864 } 865 866 off_t end = size == 0 ? 0 : (size - 1) / fVolume->BlockSize() + 1; 867 if (Flags() & EXT2_INODE_EXTENTS) { 868 ExtentStream stream(fVolume, &fNode.extent_stream, Size()); 869 stream.Shrink(transaction, end); 870 ASSERT(stream.Check()); 871 } else { 872 DataStream stream(fVolume, &fNode.stream, oldSize); 873 stream.Shrink(transaction, end); 874 } 875 876 fNode.SetSize(size); 877 return _SetNumBlocks(_NumBlocks() - end * (fVolume->BlockSize() / 512)); 878 } 879 880 881 uint64 882 Inode::_NumBlocks() 883 { 884 if (fVolume->HugeFiles()) { 885 if (fNode.Flags() & EXT2_INODE_HUGE_FILE) 886 return fNode.NumBlocks64() * (fVolume->BlockSize() / 512); 887 else 888 return fNode.NumBlocks64(); 889 } else 890 return fNode.NumBlocks(); 891 } 892 893 894 status_t 895 Inode::_SetNumBlocks(uint64 numBlocks) 896 { 897 if (numBlocks <= 0xffffffff) { 898 fNode.SetNumBlocks(numBlocks); 899 fNode.ClearFlag(EXT2_INODE_HUGE_FILE); 900 return B_OK; 901 } 902 if (!fVolume->HugeFiles()) 903 return E2BIG; 904 905 if (numBlocks > 0xffffffffffffULL) { 906 fNode.SetFlag(EXT2_INODE_HUGE_FILE); 907 numBlocks /= (fVolume->BlockSize() / 512); 908 } else 909 fNode.ClearFlag(EXT2_INODE_HUGE_FILE); 910 911 fNode.SetNumBlocks64(numBlocks); 912 return B_OK; 913 } 914 915 916 void 917 Inode::IncrementNumLinks(Transaction& transaction) 918 { 919 fNode.SetNumLinks(fNode.NumLinks() + 1); 920 if (IsIndexed() && (fNode.NumLinks() >= EXT2_INODE_MAX_LINKS 921 || fNode.NumLinks() == 2)) { 922 fNode.SetNumLinks(1); 923 fVolume->ActivateDirNLink(transaction); 924 } 925 } 926