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