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