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