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 "CRCTable.h" 16 #include "DataStream.h" 17 #include "DirectoryIterator.h" 18 #include "ExtentStream.h" 19 #include "HTree.h" 20 #include "Utility.h" 21 22 23 #undef ASSERT 24 //#define TRACE_EXT2 25 #ifdef TRACE_EXT2 26 # define TRACE(x...) dprintf("\33[34mext2:\33[0m " x) 27 # define ASSERT(x) { if (!(x)) kernel_debugger("ext2: assert failed: " #x "\n"); } 28 #else 29 # define TRACE(x...) ; 30 # define ASSERT(x) ; 31 #endif 32 #define ERROR(x...) dprintf("\33[34mext2:\33[0m " x) 33 34 35 Inode::Inode(Volume* volume, ino_t id) 36 : 37 fVolume(volume), 38 fID(id), 39 fCache(NULL), 40 fMap(NULL), 41 fUnlinked(false), 42 fHasExtraAttributes(false) 43 { 44 rw_lock_init(&fLock, "ext2 inode"); 45 recursive_lock_init(&fSmallDataLock, "ext2 inode small data"); 46 memset(&fNode, 0, sizeof(fNode)); 47 48 TRACE("Inode::Inode(): ext2_inode: %lu, disk inode: %" B_PRIu32 49 "\n", sizeof(ext2_inode), fVolume->InodeSize()); 50 fNodeSize = sizeof(ext2_inode) > fVolume->InodeSize() 51 ? fVolume->InodeSize() : sizeof(ext2_inode); 52 53 fInitStatus = UpdateNodeFromDisk(); 54 if (fInitStatus == B_OK) { 55 fHasExtraAttributes = (fNodeSize == sizeof(ext2_inode) 56 && fNode.ExtraInodeSize() + EXT2_INODE_NORMAL_SIZE 57 == sizeof(ext2_inode)); 58 59 if (IsDirectory() || (IsSymLink() && Size() < 60)) { 60 TRACE("Inode::Inode(): Not creating the file cache\n"); 61 fInitStatus = B_OK; 62 } else 63 fInitStatus = CreateFileCache(); 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 fUnlinked(false), 76 fInitStatus(B_NO_INIT) 77 { 78 rw_lock_init(&fLock, "ext2 inode"); 79 recursive_lock_init(&fSmallDataLock, "ext2 inode small data"); 80 memset(&fNode, 0, sizeof(fNode)); 81 82 TRACE("Inode::Inode(): ext2_inode: %lu, disk inode: %" B_PRIu32 "\n", 83 sizeof(ext2_inode), fVolume->InodeSize()); 84 fNodeSize = sizeof(ext2_inode) > fVolume->InodeSize() 85 ? fVolume->InodeSize() : sizeof(ext2_inode); 86 } 87 88 89 Inode::~Inode() 90 { 91 TRACE("Inode destructor\n"); 92 93 DeleteFileCache(); 94 95 TRACE("Inode destructor: Done\n"); 96 } 97 98 99 status_t 100 Inode::InitCheck() 101 { 102 return fInitStatus; 103 } 104 105 106 void 107 Inode::WriteLockInTransaction(Transaction& transaction) 108 { 109 acquire_vnode(fVolume->FSVolume(), ID()); 110 111 TRACE("Inode::WriteLockInTransaction(): Locking\n"); 112 rw_lock_write_lock(&fLock); 113 114 transaction.AddListener(this); 115 } 116 117 118 status_t 119 Inode::WriteBack(Transaction& transaction) 120 { 121 off_t blockNum; 122 123 status_t status = fVolume->GetInodeBlock(fID, blockNum); 124 if (status != B_OK) 125 return status; 126 127 if (Node().Size() > 0x7fffffffLL) { 128 status = fVolume->ActivateLargeFiles(transaction); 129 if (status != B_OK) 130 return status; 131 } 132 133 CachedBlock cached(fVolume); 134 uint8* inodeBlockData = cached.SetToWritable(transaction, blockNum); 135 if (inodeBlockData == NULL) 136 return B_IO_ERROR; 137 138 if (fVolume->HasMetaGroupChecksumFeature()) { 139 uint32 checksum = _InodeChecksum(); 140 fNode.checksum = checksum & 0xffff; 141 if (fNodeSize > EXT2_INODE_NORMAL_SIZE 142 && fNodeSize >= offsetof(ext2_inode, change_time_extra)) { 143 fNode.checksum_high = checksum >> 16; 144 } 145 } 146 TRACE("Inode::WriteBack(): Inode ID: %" B_PRIdINO ", inode block: %" 147 B_PRIdOFF ", data: %p, index: %" B_PRIu32 ", inode size: %" B_PRIu32 148 ", node size: %" B_PRIu32 ", this: %p, node: %p\n", 149 fID, blockNum, inodeBlockData, fVolume->InodeBlockIndex(fID), 150 fVolume->InodeSize(), fNodeSize, this, &fNode); 151 memcpy(inodeBlockData + 152 fVolume->InodeBlockIndex(fID) * fVolume->InodeSize(), 153 (uint8*)&fNode, fNodeSize); 154 155 TRACE("Inode::WriteBack() finished %" B_PRId32 "\n", Node().stream.direct[0]); 156 157 return B_OK; 158 } 159 160 161 status_t 162 Inode::UpdateNodeFromDisk() 163 { 164 off_t blockNum; 165 166 status_t status = fVolume->GetInodeBlock(fID, blockNum); 167 if (status != B_OK) 168 return status; 169 170 TRACE("inode %" B_PRIdINO " at block %" B_PRIdOFF "\n", fID, blockNum); 171 172 CachedBlock cached(fVolume); 173 const uint8* inodeBlock = cached.SetTo(blockNum); 174 175 if (inodeBlock == NULL) 176 return B_IO_ERROR; 177 178 TRACE("Inode size: %" B_PRIu32 ", inode index: %" B_PRIu32 "\n", 179 fVolume->InodeSize(), fVolume->InodeBlockIndex(fID)); 180 ext2_inode* inode = (ext2_inode*)(inodeBlock 181 + fVolume->InodeBlockIndex(fID) * fVolume->InodeSize()); 182 183 TRACE("Attempting to copy inode data from %p to %p, ext2_inode " 184 "size: %" B_PRIu32 "\n", inode, &fNode, fNodeSize); 185 186 memcpy(&fNode, inode, fNodeSize); 187 188 if (fVolume->HasMetaGroupChecksumFeature()) { 189 uint32 checksum = _InodeChecksum(); 190 uint32 provided = fNode.checksum; 191 if (fNodeSize > EXT2_INODE_NORMAL_SIZE 192 && fNodeSize >= offsetof(ext2_inode, change_time_extra)) { 193 provided |= (fNode.checksum_high << 16); 194 } else 195 checksum &= 0xffff; 196 if (provided != checksum) { 197 ERROR("Inode::UpdateNodeFromDisk(%" B_PRIu32 "): " 198 "verification failed\n", blockNum); 199 return B_BAD_DATA; 200 } 201 } 202 203 uint32 numLinks = fNode.NumLinks(); 204 fUnlinked = numLinks == 0 || (IsDirectory() && numLinks == 1); 205 206 return B_OK; 207 } 208 209 210 status_t 211 Inode::CheckPermissions(int accessMode) const 212 { 213 // you never have write access to a read-only volume 214 if ((accessMode & W_OK) != 0 && fVolume->IsReadOnly()) 215 return B_READ_ONLY_DEVICE; 216 217 return check_access_permissions(accessMode, Mode(), (gid_t)fNode.GroupID(), 218 (uid_t)fNode.UserID()); 219 } 220 221 222 status_t 223 Inode::FindBlock(off_t offset, fsblock_t& block, uint32 *_count) 224 { 225 if (Flags() & EXT2_INODE_EXTENTS) { 226 ExtentStream stream(fVolume, this, &fNode.extent_stream, Size()); 227 return stream.FindBlock(offset, block, _count); 228 } 229 DataStream stream(fVolume, &fNode.stream, Size()); 230 return stream.FindBlock(offset, block, _count); 231 } 232 233 234 status_t 235 Inode::ReadAt(off_t pos, uint8* buffer, size_t* _length) 236 { 237 return file_cache_read(FileCache(), NULL, pos, buffer, _length); 238 } 239 240 241 status_t 242 Inode::WriteAt(Transaction& transaction, off_t pos, const uint8* buffer, 243 size_t* _length) 244 { 245 TRACE("Inode::WriteAt(%" B_PRIdOFF ", %p, *(%p) = %" B_PRIuSIZE ")\n", pos, 246 buffer, _length, *_length); 247 ReadLocker readLocker(fLock); 248 249 if (!HasFileCache()) 250 return B_BAD_VALUE; 251 252 if (pos < 0) 253 return B_BAD_VALUE; 254 255 readLocker.Unlock(); 256 257 TRACE("Inode::WriteAt(): Starting transaction\n"); 258 transaction.Start(fVolume->GetJournal()); 259 260 WriteLocker writeLocker(fLock); 261 262 TRACE("Inode::WriteAt(): Updating modification time\n"); 263 struct timespec timespec; 264 _BigtimeToTimespec(real_time_clock_usecs(), ×pec); 265 SetModificationTime(×pec); 266 267 // NOTE: Debugging info to find why sometimes resize doesn't happen 268 size_t length = *_length; 269 #ifdef TRACE_EXT2 270 off_t oldEnd = pos + length; 271 TRACE("Inode::WriteAt(): Old calc for end? %x:%x\n", 272 (int)(oldEnd >> 32), (int)(oldEnd & 0xFFFFFFFF)); 273 #endif 274 275 off_t end = pos + (off_t)length; 276 off_t oldSize = Size(); 277 278 TRACE("Inode::WriteAt(): Old size: %" B_PRIdOFF ":%" B_PRIdOFF 279 ", new size: %" B_PRIdOFF ":%" B_PRIdOFF "\n", 280 oldSize >> 32, oldSize & 0xFFFFFFFF, 281 end >> 32, end & 0xFFFFFFFF); 282 283 if (end > oldSize) { 284 status_t status = Resize(transaction, end); 285 if (status != B_OK) { 286 *_length = 0; 287 WriteLockInTransaction(transaction); 288 return status; 289 } 290 291 status = WriteBack(transaction); 292 if (status != B_OK) { 293 *_length = 0; 294 WriteLockInTransaction(transaction); 295 return status; 296 } 297 } 298 299 writeLocker.Unlock(); 300 301 if (oldSize < pos) 302 FillGapWithZeros(oldSize, pos); 303 304 if (length == 0) { 305 // Probably just changed the file size with the pos parameter 306 return B_OK; 307 } 308 309 TRACE("Inode::WriteAt(): Performing write: %p, %" B_PRIdOFF ", %p, %" 310 B_PRIuSIZE "\n", FileCache(), pos, buffer, *_length); 311 status_t status = file_cache_write(FileCache(), NULL, pos, buffer, 312 _length); 313 314 WriteLockInTransaction(transaction); 315 316 TRACE("Inode::WriteAt(): Done\n"); 317 318 return status; 319 } 320 321 322 status_t 323 Inode::FillGapWithZeros(off_t start, off_t end) 324 { 325 TRACE("Inode::FileGapWithZeros(%" B_PRIdOFF " - %" B_PRIdOFF ")\n", start, 326 end); 327 328 while (start < end) { 329 size_t size; 330 331 if (end > start + 1024 * 1024 * 1024) 332 size = 1024 * 1024 * 1024; 333 else 334 size = end - start; 335 336 TRACE("Inode::FillGapWithZeros(): Calling file_cache_write(%p, NULL, " 337 "%" B_PRIdOFF ", NULL, &(%" B_PRIuSIZE ") = %p)\n", fCache, start, 338 size, &size); 339 status_t status = file_cache_write(fCache, NULL, start, NULL, 340 &size); 341 if (status != B_OK) 342 return status; 343 344 start += size; 345 } 346 347 return B_OK; 348 } 349 350 351 status_t 352 Inode::Resize(Transaction& transaction, off_t size) 353 { 354 TRACE("Inode::Resize() ID:%" B_PRIdINO " size: %" B_PRIdOFF "\n", ID(), 355 size); 356 if (size < 0) 357 return B_BAD_VALUE; 358 359 off_t oldSize = Size(); 360 361 if (size == oldSize) 362 return B_OK; 363 364 TRACE("Inode::Resize(): old size: %" B_PRIdOFF ", new size: %" B_PRIdOFF 365 "\n", oldSize, size); 366 367 status_t status; 368 if (size > oldSize) { 369 status = _EnlargeDataStream(transaction, size); 370 if (status != B_OK) { 371 // Restore original size 372 _ShrinkDataStream(transaction, oldSize); 373 } 374 } else 375 status = _ShrinkDataStream(transaction, size); 376 377 TRACE("Inode::Resize(): Updating file map and cache\n"); 378 379 if (status != B_OK) 380 return status; 381 382 file_cache_set_size(FileCache(), size); 383 file_map_set_size(Map(), size); 384 385 TRACE("Inode::Resize(): Writing back inode changes. Size: %" B_PRIdOFF 386 "\n", Size()); 387 388 return WriteBack(transaction); 389 } 390 391 392 status_t 393 Inode::InitDirectory(Transaction& transaction, Inode* parent) 394 { 395 TRACE("Inode::InitDirectory()\n"); 396 uint32 blockSize = fVolume->BlockSize(); 397 398 status_t status = Resize(transaction, blockSize); 399 if (status != B_OK) 400 return status; 401 402 fsblock_t blockNum; 403 if (Flags() & EXT2_INODE_EXTENTS) { 404 ExtentStream stream(fVolume, this, &fNode.extent_stream, Size()); 405 status = stream.FindBlock(0, blockNum); 406 } else { 407 DataStream stream(fVolume, &fNode.stream, Size()); 408 status = stream.FindBlock(0, blockNum); 409 } 410 if (status != B_OK) 411 return status; 412 413 CachedBlock cached(fVolume); 414 uint8* block = cached.SetToWritable(transaction, blockNum, true); 415 416 HTreeRoot* root = (HTreeRoot*)block; 417 root->dot.inode_id = fID; 418 root->dot.entry_length = 12; 419 root->dot.name_length = 1; 420 root->dot.file_type = EXT2_TYPE_DIRECTORY; 421 root->dot_entry_name[0] = '.'; 422 423 root->dotdot.inode_id = parent == NULL ? fID : parent->ID(); 424 uint32 dotdotlength = blockSize - 12; 425 if (fVolume->HasMetaGroupChecksumFeature()) 426 dotdotlength -= sizeof(ext2_dir_entry_tail); 427 root->dotdot.entry_length = dotdotlength; 428 root->dotdot.name_length = 2; 429 root->dotdot.file_type = EXT2_TYPE_DIRECTORY; 430 root->dotdot_entry_name[0] = '.'; 431 root->dotdot_entry_name[1] = '.'; 432 433 parent->IncrementNumLinks(transaction); 434 435 SetDirEntryChecksum((uint8*)root); 436 437 return parent->WriteBack(transaction); 438 } 439 440 441 status_t 442 Inode::Unlink(Transaction& transaction) 443 { 444 uint32 numLinks = fNode.NumLinks(); 445 TRACE("Inode::Unlink(): Current links: %" B_PRIu32 "\n", numLinks); 446 447 if (numLinks == 0) { 448 ERROR("Inode::Unlink(): no links\n"); 449 return B_BAD_VALUE; 450 } 451 452 if ((IsDirectory() && numLinks == 2) || (numLinks == 1)) { 453 fUnlinked = true; 454 455 TRACE("Inode::Unlink(): Putting inode in orphan list\n"); 456 ino_t firstOrphanID; 457 status_t status = fVolume->SaveOrphan(transaction, fID, firstOrphanID); 458 if (status != B_OK) 459 return status; 460 461 if (firstOrphanID != 0) { 462 Vnode firstOrphan(fVolume, firstOrphanID); 463 Inode* nextOrphan; 464 465 status = firstOrphan.Get(&nextOrphan); 466 if (status != B_OK) 467 return status; 468 469 fNode.SetNextOrphan(nextOrphan->ID()); 470 } else { 471 // Next orphan link is stored in deletion time 472 fNode.deletion_time = 0; 473 } 474 475 fNode.num_links = 0; 476 477 status = remove_vnode(fVolume->FSVolume(), fID); 478 if (status != B_OK) 479 return status; 480 } else if (!IsDirectory() || numLinks > 2) 481 fNode.SetNumLinks(--numLinks); 482 483 return WriteBack(transaction); 484 } 485 486 487 /*static*/ status_t 488 Inode::Create(Transaction& transaction, Inode* parent, const char* name, 489 int32 mode, int openMode, uint8 type, bool* _created, ino_t* _id, 490 Inode** _inode, fs_vnode_ops* vnodeOps, uint32 publishFlags) 491 { 492 TRACE("Inode::Create()\n"); 493 Volume* volume = transaction.GetVolume(); 494 495 DirectoryIterator* entries = NULL; 496 ObjectDeleter<DirectoryIterator> entriesDeleter; 497 498 if (parent != NULL) { 499 parent->WriteLockInTransaction(transaction); 500 501 // don't create anything in removed directories 502 bool removed; 503 if (get_vnode_removed(volume->FSVolume(), parent->ID(), &removed) 504 == B_OK && removed) { 505 return B_ENTRY_NOT_FOUND; 506 } 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 if (parent != NULL) 628 node.SetFlags(parent->Flags() & EXT2_INODE_INHERITED); 629 if (volume->HasExtentsFeature() 630 && (inode->IsDirectory() || inode->IsFile())) { 631 node.SetFlag(EXT2_INODE_EXTENTS); 632 ExtentStream stream(volume, inode, &node.extent_stream, 0); 633 stream.Init(); 634 ASSERT(stream.Check()); 635 } 636 637 if (sizeof(ext2_inode) < volume->InodeSize()) 638 node.SetExtraInodeSize(sizeof(ext2_inode) - EXT2_INODE_NORMAL_SIZE); 639 640 TRACE("Inode::Create(): Updating ID\n"); 641 inode->fID = id; 642 643 if (inode->IsDirectory()) { 644 TRACE("Inode::Create(): Initializing directory\n"); 645 status = inode->InitDirectory(transaction, parent); 646 if (status != B_OK) { 647 ERROR("Inode::Create(): InitDirectory() failed\n"); 648 delete inode; 649 return status; 650 } 651 } 652 653 // TODO: Maybe it can be better 654 /*if (volume->HasExtendedAttributes()) { 655 TRACE("Inode::Create(): Initializing extended attributes\n"); 656 uint32 blockGroup = 0; 657 uint32 pos = 0; 658 uint32 allocated; 659 660 status = volume->AllocateBlocks(transaction, 1, 1, blockGroup, pos, 661 allocated); 662 if (status != B_OK) 663 return status; 664 665 // Clear the new block 666 uint32 blockNum = volume->FirstDataBlock() + pos + 667 volume->BlocksPerGroup() * blockGroup; 668 CachedBlock cached(volume); 669 cached.SetToWritable(transaction, blockNum, true); 670 671 node.SetExtendedAttributesBlock(blockNum); 672 }*/ 673 674 TRACE("Inode::Create(): Saving inode\n"); 675 status = inode->WriteBack(transaction); 676 if (status != B_OK) { 677 delete inode; 678 return status; 679 } 680 681 TRACE("Inode::Create(): Creating vnode\n"); 682 683 Vnode vnode; 684 status = vnode.Publish(transaction, inode, vnodeOps, publishFlags); 685 if (status != B_OK) 686 return status; 687 688 if (!inode->IsSymLink()) { 689 // Vnode::Publish doesn't publish symlinks 690 if (!inode->IsDirectory()) { 691 status = inode->CreateFileCache(); 692 if (status != B_OK) 693 return status; 694 } 695 696 inode->WriteLockInTransaction(transaction); 697 } 698 699 if (_created) 700 *_created = true; 701 if (_id != NULL) 702 *_id = id; 703 if (_inode != NULL) 704 *_inode = inode; 705 706 if (_id != NULL || _inode != NULL) 707 vnode.Keep(); 708 709 TRACE("Inode::Create(): Deleting entries iterator\n"); 710 DirectoryIterator* iterator = entriesDeleter.Detach(); 711 TRACE("Inode::Create(): Entries iterator: %p\n", entries); 712 delete iterator; 713 TRACE("Inode::Create(): Done\n"); 714 715 return B_OK; 716 } 717 718 719 status_t 720 Inode::CreateFileCache() 721 { 722 TRACE("Inode::CreateFileCache()\n"); 723 724 if (fCache != NULL) 725 return B_OK; 726 727 TRACE("Inode::CreateFileCache(): Creating file cache: %" B_PRIu32 ", %" 728 B_PRIdINO ", %" B_PRIdOFF "\n", fVolume->ID(), ID(), Size()); 729 730 fCache = file_cache_create(fVolume->ID(), ID(), Size()); 731 if (fCache == NULL) { 732 ERROR("Inode::CreateFileCache(): Failed to create file cache\n"); 733 return B_ERROR; 734 } 735 736 fMap = file_map_create(fVolume->ID(), ID(), Size()); 737 if (fMap == NULL) { 738 ERROR("Inode::CreateFileCache(): Failed to create file map\n"); 739 file_cache_delete(fCache); 740 fCache = NULL; 741 return B_ERROR; 742 } 743 744 TRACE("Inode::CreateFileCache(): Done\n"); 745 746 return B_OK; 747 } 748 749 750 void 751 Inode::DeleteFileCache() 752 { 753 TRACE("Inode::DeleteFileCache()\n"); 754 755 if (fCache == NULL) 756 return; 757 758 file_cache_delete(fCache); 759 file_map_delete(fMap); 760 761 fCache = NULL; 762 fMap = NULL; 763 } 764 765 766 status_t 767 Inode::EnableFileCache() 768 { 769 if (fCache == NULL) 770 return B_BAD_VALUE; 771 772 file_cache_enable(fCache); 773 return B_OK; 774 } 775 776 777 status_t 778 Inode::DisableFileCache() 779 { 780 status_t error = file_cache_disable(fCache); 781 if (error != B_OK) 782 return error; 783 784 return B_OK; 785 } 786 787 788 status_t 789 Inode::Sync() 790 { 791 if (HasFileCache()) 792 return file_cache_sync(fCache); 793 794 return B_OK; 795 } 796 797 798 void 799 Inode::TransactionDone(bool success) 800 { 801 if (!success) { 802 // Revert any changes to the inode 803 if (UpdateNodeFromDisk() != B_OK) 804 panic("Failed to reload inode from disk!\n"); 805 } 806 } 807 808 809 void 810 Inode::RemovedFromTransaction() 811 { 812 TRACE("Inode::RemovedFromTransaction(): Unlocking\n"); 813 rw_lock_write_unlock(&fLock); 814 815 put_vnode(fVolume->FSVolume(), ID()); 816 } 817 818 819 status_t 820 Inode::_EnlargeDataStream(Transaction& transaction, off_t size) 821 { 822 if (size < 0) 823 return B_BAD_VALUE; 824 825 TRACE("Inode::_EnlargeDataStream()\n"); 826 827 uint32 blockSize = fVolume->BlockSize(); 828 off_t oldSize = Size(); 829 off_t maxSize = oldSize; 830 if (maxSize % blockSize != 0) 831 maxSize += blockSize - maxSize % blockSize; 832 833 if (size <= maxSize) { 834 // No need to allocate more blocks 835 TRACE("Inode::_EnlargeDataStream(): No need to allocate more blocks\n"); 836 TRACE("Inode::_EnlargeDataStream(): Setting size to %" B_PRIdOFF "\n", 837 size); 838 fNode.SetSize(size); 839 return B_OK; 840 } 841 842 off_t end = size == 0 ? 0 : (size - 1) / fVolume->BlockSize() + 1; 843 if (Flags() & EXT2_INODE_EXTENTS) { 844 ExtentStream stream(fVolume, this, &fNode.extent_stream, Size()); 845 stream.Enlarge(transaction, end); 846 ASSERT(stream.Check()); 847 } else { 848 DataStream stream(fVolume, &fNode.stream, oldSize); 849 stream.Enlarge(transaction, end); 850 } 851 TRACE("Inode::_EnlargeDataStream(): Setting size to %" B_PRIdOFF "\n", 852 size); 853 fNode.SetSize(size); 854 TRACE("Inode::_EnlargeDataStream(): Setting allocated block count to %" 855 B_PRIdOFF "\n", end); 856 return _SetNumBlocks(_NumBlocks() + end * (fVolume->BlockSize() / 512)); 857 } 858 859 860 status_t 861 Inode::_ShrinkDataStream(Transaction& transaction, off_t size) 862 { 863 TRACE("Inode::_ShrinkDataStream()\n"); 864 865 if (size < 0) 866 return B_BAD_VALUE; 867 868 uint32 blockSize = fVolume->BlockSize(); 869 off_t oldSize = Size(); 870 off_t lastByte = oldSize == 0 ? 0 : oldSize - 1; 871 off_t minSize = (lastByte / blockSize + 1) * blockSize; 872 // Minimum size that doesn't require freeing blocks 873 874 if (size > minSize) { 875 // No need to allocate more blocks 876 TRACE("Inode::_ShrinkDataStream(): No need to allocate more blocks\n"); 877 TRACE("Inode::_ShrinkDataStream(): Setting size to %" B_PRIdOFF "\n", 878 size); 879 fNode.SetSize(size); 880 return B_OK; 881 } 882 883 off_t end = size == 0 ? 0 : (size - 1) / fVolume->BlockSize() + 1; 884 if (Flags() & EXT2_INODE_EXTENTS) { 885 ExtentStream stream(fVolume, this, &fNode.extent_stream, Size()); 886 stream.Shrink(transaction, end); 887 ASSERT(stream.Check()); 888 } else { 889 DataStream stream(fVolume, &fNode.stream, oldSize); 890 stream.Shrink(transaction, end); 891 } 892 893 fNode.SetSize(size); 894 return _SetNumBlocks(_NumBlocks() - end * (fVolume->BlockSize() / 512)); 895 } 896 897 898 uint64 899 Inode::_NumBlocks() 900 { 901 if (fVolume->HugeFiles()) { 902 if (fNode.Flags() & EXT2_INODE_HUGE_FILE) 903 return fNode.NumBlocks64() * (fVolume->BlockSize() / 512); 904 else 905 return fNode.NumBlocks64(); 906 } else 907 return fNode.NumBlocks(); 908 } 909 910 911 status_t 912 Inode::_SetNumBlocks(uint64 numBlocks) 913 { 914 if (numBlocks <= 0xffffffff) { 915 fNode.SetNumBlocks(numBlocks); 916 fNode.ClearFlag(EXT2_INODE_HUGE_FILE); 917 return B_OK; 918 } 919 if (!fVolume->HugeFiles()) 920 return E2BIG; 921 922 if (numBlocks > 0xffffffffffffULL) { 923 fNode.SetFlag(EXT2_INODE_HUGE_FILE); 924 numBlocks /= (fVolume->BlockSize() / 512); 925 } else 926 fNode.ClearFlag(EXT2_INODE_HUGE_FILE); 927 928 fNode.SetNumBlocks64(numBlocks); 929 return B_OK; 930 } 931 932 933 void 934 Inode::IncrementNumLinks(Transaction& transaction) 935 { 936 fNode.SetNumLinks(fNode.NumLinks() + 1); 937 if (IsIndexed() && (fNode.NumLinks() >= EXT2_INODE_MAX_LINKS 938 || fNode.NumLinks() == 2)) { 939 fNode.SetNumLinks(1); 940 fVolume->ActivateDirNLink(transaction); 941 } 942 } 943 944 945 uint32 946 Inode::_InodeChecksum() 947 { 948 size_t offset = offsetof(ext2_inode, checksum); 949 size_t offset2 = offsetof(ext2_inode, reserved); 950 uint32 number = fID; 951 uint32 checksum = calculate_crc32c(fVolume->ChecksumSeed(), (uint8*)&number, 952 sizeof(number)); 953 uint32 gen = fNode.generation; 954 checksum = calculate_crc32c(checksum, (uint8*)&gen, sizeof(gen)); 955 checksum = calculate_crc32c(checksum, (uint8*)&fNode, offset); 956 uint16 dummy = 0; 957 checksum = calculate_crc32c(checksum, (uint8*)&dummy, sizeof(dummy)); 958 checksum = calculate_crc32c(checksum, (uint8*)&fNode + offset2, 959 EXT2_INODE_NORMAL_SIZE - offset2); 960 if (fNodeSize > EXT2_INODE_NORMAL_SIZE) { 961 offset = offsetof(ext2_inode, checksum_high); 962 offset2 = offsetof(ext2_inode, change_time_extra); 963 checksum = calculate_crc32c(checksum, (uint8*)&fNode + EXT2_INODE_NORMAL_SIZE, 964 offset - EXT2_INODE_NORMAL_SIZE); 965 checksum = calculate_crc32c(checksum, (uint8*)&dummy, sizeof(dummy)); 966 checksum = calculate_crc32c(checksum, (uint8*)&fNode + offset2, 967 fNodeSize - offset2); 968 } 969 return checksum; 970 } 971 972 973 ext2_dir_entry_tail* 974 Inode::_DirEntryTail(uint8* block) const 975 { 976 return (ext2_dir_entry_tail*)(block + fVolume->BlockSize() 977 - sizeof(ext2_dir_entry_tail)); 978 } 979 980 981 uint32 982 Inode::_DirEntryChecksum(uint8* block, uint32 id, uint32 gen) const 983 { 984 uint32 number = id; 985 uint32 checksum = calculate_crc32c(fVolume->ChecksumSeed(), 986 (uint8*)&number, sizeof(number)); 987 checksum = calculate_crc32c(checksum, (uint8*)&gen, sizeof(gen)); 988 checksum = calculate_crc32c(checksum, block, 989 fVolume->BlockSize() - sizeof(ext2_dir_entry_tail)); 990 return checksum; 991 } 992 993 994 void 995 Inode::SetDirEntryChecksum(uint8* block, uint32 id, uint32 gen) 996 { 997 if (fVolume->HasMetaGroupChecksumFeature()) { 998 ext2_dir_entry_tail* tail = _DirEntryTail(block); 999 tail->twelve = 12; 1000 tail->hexade = 0xde; 1001 tail->checksum = _DirEntryChecksum(block, id, gen); 1002 } 1003 } 1004 1005 1006 void 1007 Inode::SetDirEntryChecksum(uint8* block) 1008 { 1009 SetDirEntryChecksum(block, fID, fNode.generation); 1010 } 1011 1012 1013 uint32 1014 Inode::_ExtentLength(ext2_extent_stream* stream) const 1015 { 1016 return sizeof(struct ext2_extent_header) 1017 + stream->extent_header.MaxEntries() 1018 * sizeof(struct ext2_extent_entry); 1019 } 1020 1021 1022 uint32 1023 Inode::_ExtentChecksum(ext2_extent_stream* stream) const 1024 { 1025 uint32 number = fID; 1026 uint32 checksum = calculate_crc32c(fVolume->ChecksumSeed(), 1027 (uint8*)&number, sizeof(number)); 1028 checksum = calculate_crc32c(checksum, (uint8*)&fNode.generation, 1029 sizeof(fNode.generation)); 1030 checksum = calculate_crc32c(checksum, (uint8*)stream, 1031 _ExtentLength(stream)); 1032 return checksum; 1033 } 1034 1035 1036 void 1037 Inode::SetExtentChecksum(ext2_extent_stream* stream) 1038 { 1039 if (fVolume->HasMetaGroupChecksumFeature()) { 1040 uint32 checksum = _ExtentChecksum(stream); 1041 struct ext2_extent_tail *tail = (struct ext2_extent_tail *) 1042 ((uint8*)stream + _ExtentLength(stream)); 1043 tail->checksum = checksum; 1044 } 1045 } 1046 1047 1048 bool 1049 Inode::VerifyExtentChecksum(ext2_extent_stream* stream) 1050 { 1051 if (fVolume->HasMetaGroupChecksumFeature()) { 1052 uint32 checksum = _ExtentChecksum(stream); 1053 struct ext2_extent_tail *tail = (struct ext2_extent_tail *) 1054 ((uint8*)stream + _ExtentLength(stream)); 1055 return tail->checksum == checksum; 1056 } 1057 return true; 1058 } 1059 1060