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