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