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