1/* 2 * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7#include <dirent.h> 8#include <string.h> 9#include <sys/stat.h> 10#include <unistd.h> 11 12#include <new> 13 14#include <fs_interface.h> 15#include <io_requests.h> 16#include <NodeMonitor.h> 17 18#include <AutoDeleter.h> 19#include <AutoLocker.h> 20 21#include <debug.h> 22#include <util/AutoLock.h> 23 24#include "checksumfs.h" 25#include "checksumfs_private.h" 26#include "DebugSupport.h" 27#include "Directory.h" 28#include "File.h" 29#include "Notifications.h" 30#include "SuperBlock.h" 31#include "SymLink.h" 32#include "Transaction.h" 33#include "Volume.h" 34 35 36static const char* const kCheckSumFSModuleName = "file_systems/checksumfs" 37 B_CURRENT_FS_API_VERSION; 38static const char* const kCheckSumFSShortName = "checksumfs"; 39 40static const bigtime_t kModifiedInterimUpdateInterval = 500000; 41 // wait at least 0.5s between interim modified updates 42 43 44// #pragma mark - 45 46 47struct FileCookie { 48 mutex lock; 49 int openMode; 50 bigtime_t lastModifiedUpdate; 51 bool modifiedNeedsUpdate; 52 bool sizeChangedSinceUpdate; 53 bool modifiedNeedsFinalUpdate; 54 bool finalSizeChanged; 55 56 57 FileCookie(int openMode) 58 : 59 openMode(openMode), 60 lastModifiedUpdate(0), 61 modifiedNeedsUpdate(false), 62 sizeChangedSinceUpdate(false), 63 modifiedNeedsFinalUpdate(false), 64 finalSizeChanged(false) 65 { 66 mutex_init(&lock, "checksumfs file cookie"); 67 } 68 69 ~FileCookie() 70 { 71 mutex_destroy(&lock); 72 } 73 74 status_t UpdateModifiedIfNecessary(Node* node, bool finalUpdate) 75 { 76 MutexLocker locker(lock); 77 78 return _UpdateModifiedIfNecessary(node, finalUpdate); 79 } 80 81 status_t FileModified(Node* node, bool sizeChanged) 82 { 83 MutexLocker locker(lock); 84 85 modifiedNeedsUpdate = true; 86 modifiedNeedsFinalUpdate = true; 87 sizeChangedSinceUpdate |= sizeChanged; 88 finalSizeChanged |= sizeChanged; 89 90 return _UpdateModifiedIfNecessary(node, false); 91 } 92 93private: 94 status_t _UpdateModifiedIfNecessary(Node* node, bool finalUpdate) 95 { 96 uint32 statFlags = B_STAT_MODIFICATION_TIME | B_STAT_CHANGE_TIME; 97 98 if (finalUpdate) { 99 if (!modifiedNeedsFinalUpdate) 100 return B_OK; 101 if (finalSizeChanged) 102 statFlags |= B_STAT_SIZE; 103 } else { 104 if (!modifiedNeedsUpdate) 105 return B_OK; 106 107 if (system_time() 108 < lastModifiedUpdate + kModifiedInterimUpdateInterval) { 109 // not enough time passed -- postpone update 110 return B_OK; 111 } 112 113 statFlags |= B_STAT_INTERIM_UPDATE 114 | (sizeChangedSinceUpdate ? B_STAT_SIZE : 0); 115 } 116 117 // do the update -- start a transaction, lock the node, and update 118 Transaction transaction(node->GetVolume()); 119 status_t error = transaction.StartAndAddNode(node); 120 if (error != B_OK) 121 return error; 122 123 node->Touched(NODE_MODIFIED); 124 125 error = transaction.Commit(StatChangedNotification(node, statFlags)); 126 if (error != B_OK) 127 return error; 128 129 modifiedNeedsUpdate = false; 130 lastModifiedUpdate = system_time(); 131 132 return B_OK; 133 } 134}; 135 136 137struct DirCookie { 138 DirCookie(Directory* directory) 139 : 140 fDirectory(directory) 141 { 142 Rewind(); 143 } 144 145 Directory* GetDirectory() const 146 { 147 return fDirectory; 148 } 149 150 void SetTo(Directory* directory, bool skipArtificialEntries) 151 { 152 fDirectory = directory; 153 Rewind(skipArtificialEntries); 154 } 155 156 status_t ReadNextEntry(struct dirent* buffer, size_t size, 157 uint32& _countRead) 158 { 159 const char* name; 160 size_t nameLength; 161 uint64 blockIndex; 162 163 int nextIterationState = OTHERS; 164 switch (fIterationState) { 165 case DOT: 166 name = "."; 167 nameLength = 1; 168 blockIndex = fDirectory->BlockIndex(); 169 nextIterationState = DOT_DOT; 170 break; 171 case DOT_DOT: 172 name = ".."; 173 nameLength = 2; 174 blockIndex = fDirectory->ParentDirectory(); 175 break; 176 default: 177 { 178 status_t error = fDirectory->LookupNextEntry(fEntryName, 179 fEntryName, nameLength, blockIndex); 180 if (error != B_OK) { 181 if (error != B_ENTRY_NOT_FOUND) 182 return error; 183 184 _countRead = 0; 185 return B_OK; 186 } 187 188 name = fEntryName; 189 break; 190 } 191 } 192 193 size_t entrySize = sizeof(dirent) + nameLength; 194 if (entrySize > size) 195 return B_BUFFER_OVERFLOW; 196 197 buffer->d_dev = fDirectory->GetVolume()->ID(); 198 buffer->d_ino = blockIndex; 199 buffer->d_reclen = entrySize; 200 strcpy(buffer->d_name, name); 201 202 fIterationState = nextIterationState; 203 204 _countRead = 1; 205 return B_OK; 206 } 207 208 void Rewind(bool skipArtificialEntries = false) 209 { 210 fIterationState = skipArtificialEntries ? OTHERS : DOT; 211 fEntryName[0] = '\0'; 212 } 213 214private: 215 enum { 216 DOT, 217 DOT_DOT, 218 OTHERS 219 }; 220 221 Directory* fDirectory; 222 int fIterationState; 223 char fEntryName[kCheckSumFSNameLength + 1]; 224}; 225 226 227struct AttrDirCookie { 228 AttrDirCookie(Node* node) 229 : 230 fNode(node), 231 fAttributeDirectory(NULL), 232 fDirCookie(NULL) 233 { 234 } 235 236 ~AttrDirCookie() 237 { 238 if (fAttributeDirectory != NULL) 239 fNode->GetVolume()->PutNode(fAttributeDirectory); 240 } 241 242 status_t ReadNextEntry(struct dirent* buffer, size_t size, 243 uint32& _countRead) 244 { 245 status_t error = _UpdateAttributeDirectory(); 246 if (error != B_OK) 247 RETURN_ERROR(error); 248 249 if (fAttributeDirectory == NULL) { 250 _countRead = 0; 251 return B_OK; 252 } 253 254 return fDirCookie.ReadNextEntry(buffer, size, _countRead); 255 } 256 257 void Rewind() 258 { 259 fDirCookie.Rewind(true); 260 } 261 262private: 263 status_t _UpdateAttributeDirectory() 264 { 265 uint64 blockIndex = fNode->AttributeDirectory(); 266 if (blockIndex == 0) { 267 // no (empty) attribute directory 268 if (fAttributeDirectory != NULL) { 269 fNode->GetVolume()->PutNode(fAttributeDirectory); 270 fAttributeDirectory = NULL; 271 } 272 273 return B_OK; 274 } 275 276 if (fAttributeDirectory != NULL) { 277 if (blockIndex == fAttributeDirectory->BlockIndex()) 278 return B_OK; 279 280 // The attribute directory has changed in the meantime -- get rid 281 // of the old one. 282 fNode->GetVolume()->PutNode(fAttributeDirectory); 283 fAttributeDirectory = NULL; 284 } 285 286 // get the attribute directory node 287 Node* node; 288 status_t error = fNode->GetVolume()->GetNode(blockIndex, node); 289 if (error != B_OK) 290 RETURN_ERROR(error); 291 292 fAttributeDirectory = dynamic_cast<Directory*>(node); 293 if (fAttributeDirectory == NULL) { 294 fNode->GetVolume()->PutNode(node); 295 ERROR("checksumfs: attribute directory (%" B_PRIu64 ") of node %" 296 B_PRIu64 " is not a directory!\n", blockIndex, 297 fNode->BlockIndex()); 298 RETURN_ERROR(B_BAD_DATA); 299 } 300 301 fDirCookie.SetTo(fAttributeDirectory, true); 302 303 return B_OK; 304 } 305 306private: 307 Node* fNode; 308 Directory* fAttributeDirectory; 309 DirCookie fDirCookie; 310}; 311 312 313struct AttributeCookie { 314 char* name; 315 Node* attribute; 316 FileCookie* fileCookie; 317 318 AttributeCookie(const char* name) 319 : 320 name(strdup(name)), 321 attribute(NULL), 322 fileCookie(NULL) 323 { 324 } 325 326 ~AttributeCookie() 327 { 328 if (attribute != NULL) 329 attribute->GetVolume()->PutNode(attribute); 330 delete fileCookie; 331 free(name); 332 } 333}; 334 335 336// #pragma mark - 337 338 339static void 340set_timespec(timespec& time, uint64 nanos) 341{ 342 time.tv_sec = nanos / 1000000000; 343 time.tv_nsec = nanos % 1000000000; 344} 345 346 347static uint64 348timespec_to_nsecs(const timespec& time) 349{ 350 return (uint64)time.tv_sec * 1000000000 + time.tv_nsec; 351} 352 353 354struct PutNode { 355 inline void operator()(Node* node) 356 { 357 if (node != NULL) 358 node->GetVolume()->PutNode(node); 359 } 360}; 361 362typedef BPrivate::AutoDeleter<Node, PutNode> NodePutter; 363 364 365static bool 366is_user_in_group(gid_t gid) 367{ 368 gid_t groups[NGROUPS_MAX]; 369 int groupCount = getgroups(NGROUPS_MAX, groups); 370 for (int i = 0; i < groupCount; i++) { 371 if (gid == groups[i]) 372 return true; 373 } 374 375 return gid == getegid(); 376} 377 378 379static status_t 380check_access(Node* node, uint32 accessFlags) 381{ 382 // Note: we assume that the access flags are compatible with the permission 383 // bits. 384 STATIC_ASSERT(R_OK == S_IROTH && W_OK == S_IWOTH && X_OK == S_IXOTH); 385 386 // get node permissions 387 int userPermissions = (node->Mode() & S_IRWXU) >> 6; 388 int groupPermissions = (node->Mode() & S_IRWXG) >> 3; 389 int otherPermissions = node->Mode() & S_IRWXO; 390 391 // get the permissions for this uid/gid 392 int permissions = 0; 393 uid_t uid = geteuid(); 394 395 if (uid == 0) { 396 // user is root 397 // root has always read/write permission, but at least one of the 398 // X bits must be set for execute permission 399 permissions = userPermissions | groupPermissions | otherPermissions 400 | R_OK | W_OK; 401 } else if (uid == node->UID()) { 402 // user is node owner 403 permissions = userPermissions; 404 } else if (is_user_in_group(node->GID())) { 405 // user is in owning group 406 permissions = groupPermissions; 407 } else { 408 // user is one of the others 409 permissions = otherPermissions; 410 } 411 412 return (accessFlags & ~permissions) == 0 ? B_OK : B_NOT_ALLOWED; 413} 414 415 416static status_t 417remove_entry(fs_volume* fsVolume, fs_vnode* parent, const char* name, 418 bool removeDirectory) 419{ 420 Volume* volume = (Volume*)fsVolume->private_volume; 421 Directory* directory 422 = dynamic_cast<Directory*>((Node*)parent->private_node); 423 if (directory == NULL) 424 return B_NOT_A_DIRECTORY; 425 426 if (volume->IsReadOnly()) 427 return B_READ_ONLY_DEVICE; 428 429 // Since we need to lock both nodes (the directory and the entry's), this 430 // is a bit cumbersome. We first look up the entry while having the 431 // directory read-locked, then drop the read lock, write-lock both nodes 432 // and check whether anything has changed. 433 Transaction transaction(volume); 434 Node* childNode; 435 NodePutter childNodePutter; 436 437 while (true) { 438 // look up the entry 439 NodeReadLocker directoryLocker(directory); 440 441 uint64 blockIndex; 442 status_t error = directory->LookupEntry(name, blockIndex); 443 if (error != B_OK) 444 RETURN_ERROR(error); 445 446 directoryLocker.Unlock(); 447 448 // get the entry's node 449 error = volume->GetNode(blockIndex, childNode); 450 if (error != B_OK) 451 RETURN_ERROR(error); 452 childNodePutter.SetTo(childNode); 453 454 // start the transaction 455 error = transaction.Start(); 456 if (error != B_OK) 457 RETURN_ERROR(error); 458 459 // write-lock the nodes 460 error = transaction.AddNodes(directory, childNode); 461 if (error != B_OK) 462 RETURN_ERROR(error); 463 464 // check the situation again 465 error = directory->LookupEntry(name, blockIndex); 466 if (error != B_OK) 467 RETURN_ERROR(error); 468 if (blockIndex != childNode->BlockIndex()) { 469 transaction.Abort(); 470 continue; 471 } 472 473 break; 474 } 475 476 // check permissions 477 status_t error = check_access(directory, W_OK); 478 if (error != B_OK) 479 return error; 480 481 // check whether the child node type agrees with our caller 482 if (removeDirectory) { 483 if (!S_ISDIR(childNode->Mode())) 484 RETURN_ERROR(B_NOT_A_DIRECTORY); 485 486 // directory must be empty 487 if (childNode->Size() > 0) 488 RETURN_ERROR(B_DIRECTORY_NOT_EMPTY); 489 } else if (S_ISDIR(childNode->Mode())) 490 RETURN_ERROR(B_IS_A_DIRECTORY); 491 492 // remove the entry 493 error = directory->RemoveEntry(name, transaction); 494 if (error != B_OK) 495 RETURN_ERROR(error); 496 497 // update stat data 498 childNode->SetHardLinks(childNode->HardLinks() - 1); 499 500 directory->Touched(NODE_MODIFIED); 501 502 // remove the child node, if no longer referenced 503 if (childNode->HardLinks() == 0) { 504 error = volume->RemoveNode(childNode); 505 if (error != B_OK) 506 return error; 507 } 508 509 // commit the transaction 510 return transaction.Commit(EntryRemovedNotification(directory, name, 511 childNode)); 512} 513 514 515/*! Opens the node according to the given open mode (if the permissions allow 516 that) and creates a file cookie. 517*/ 518static status_t 519open_file(Volume* volume, Node* node, int openMode, Transaction& transaction, 520 bool commitTransaction, FileCookie*& _cookie) 521{ 522 // translate the open mode to required permissions 523 uint32 accessFlags = 0; 524 switch (openMode & O_RWMASK) { 525 case O_RDONLY: 526 accessFlags = R_OK; 527 break; 528 case O_WRONLY: 529 accessFlags = W_OK; 530 break; 531 case O_RDWR: 532 accessFlags = R_OK | W_OK; 533 break; 534 } 535 536 // We need to at least read-lock the node. If O_TRUNC is specified, we even 537 // need a write lock and a transaction. 538 NodeReadLocker nodeReadLocker; 539 540 if ((openMode & O_TRUNC) != 0) { 541 accessFlags |= W_OK; 542 543 status_t error = transaction.IsActive() 544 ? transaction.AddNode(node) : transaction.StartAndAddNode(node); 545 if (error != B_OK) 546 RETURN_ERROR(error); 547 } else if (!transaction.IsNodeLocked(node)) 548 nodeReadLocker.SetTo(node, false); 549 550 // check permissions 551 if ((accessFlags & W_OK) != 0) { 552 if (volume->IsReadOnly()) 553 return B_READ_ONLY_DEVICE; 554 if (S_ISDIR(node->Mode())) 555 return B_IS_A_DIRECTORY; 556 } 557 558 if ((openMode & O_DIRECTORY) != 0 && !S_ISDIR(node->Mode())) 559 return B_NOT_A_DIRECTORY; 560 561 status_t error = check_access(node, accessFlags); 562 if (error != B_OK) 563 return error; 564 565 // TODO: Support O_NOCACHE. 566 567 FileCookie* cookie = new(std::nothrow) FileCookie(openMode); 568 if (cookie == NULL) 569 return B_NO_MEMORY; 570 ObjectDeleter<FileCookie> cookieDeleter(cookie); 571 572 // truncate the file, if requested 573 if ((openMode & O_TRUNC) != 0 && node->Size() > 0) { 574 error = node->Resize(0, false, transaction); 575 if (error != B_OK) 576 return error; 577 578 node->Touched(NODE_MODIFIED); 579 580 if (commitTransaction) { 581 uint32 statFlags = B_STAT_MODIFICATION_TIME | B_STAT_CHANGE_TIME 582 | B_STAT_SIZE; 583 error = transaction.Commit(StatChangedNotification(node, 584 statFlags)); 585 if (error != B_OK) 586 return error; 587 } 588 } 589 590 _cookie = cookieDeleter.Detach(); 591 return B_OK; 592} 593 594 595static status_t 596create_file(Volume* volume, Directory* directory, const char* name, 597 int openMode, int permissions, Transaction& transaction, 598 bool commitTransaction, FileCookie*& _cookie, Node*& _node, bool& _created) 599{ 600 Node* childNode = NULL; 601 NodePutter childNodePutter; 602 603 // Start the transaction and add the directory. We only need a read lock 604 // for the lookup, but later we'll need a write lock, if we have to create 605 // the file. So this is simpler. 606 status_t error = B_OK; 607 bool directoryLocked = false; 608 if (transaction.IsActive()) { 609 directoryLocked = transaction.IsNodeLocked(directory); 610 if (!directoryLocked) 611 error = transaction.AddNode(directory); 612 } else 613 error = transaction.StartAndAddNode(directory); 614 if (error != B_OK) 615 RETURN_ERROR(error); 616 617 // look up the entry 618 uint64 blockIndex; 619 error = directory->LookupEntry(name, blockIndex); 620 if (error == B_OK) { 621 // the entry already exists 622 if ((openMode & O_EXCL) != 0) 623 return B_FILE_EXISTS; 624 625 // get the entry's node 626 error = volume->GetNode(blockIndex, childNode); 627 if (error != B_OK) 628 RETURN_ERROR(error); 629 childNodePutter.SetTo(childNode); 630 631 // We can (must even) unlock the directory now. The file won't go 632 // anywhere, since a transaction is already running. 633 if (!directoryLocked) 634 transaction.RemoveNode(directory); 635 636 error = open_file(volume, childNode, openMode, transaction, 637 commitTransaction, _cookie); 638 if (error != B_OK) 639 RETURN_ERROR(error); 640 641 childNodePutter.Detach(); 642 _node = childNode; 643 _created = false; 644 return B_OK; 645 } 646 647 if (error != B_ENTRY_NOT_FOUND) 648 RETURN_ERROR(error); 649 650 // The entry doesn't exist yet. We have to create a new file. 651 652 // check the directory write permission 653 error = check_access(directory, W_OK); 654 if (error != B_OK) 655 return error; 656 657 // don't create an entry in an unlinked directory 658 if (directory->HardLinks() == 0) 659 RETURN_ERROR(B_ENTRY_NOT_FOUND); 660 661 // create a file 662 File* newFile; 663 error = volume->CreateFile(permissions, transaction, newFile); 664 if (error != B_OK) 665 return error; 666 667 // insert the new file 668 error = directory->InsertEntry(name, newFile->BlockIndex(), transaction); 669 if (error != B_OK) 670 return error; 671 672 // open the file 673 FileCookie* cookie; 674 error = open_file(volume, newFile, openMode & ~O_TRUNC, transaction, 675 false, cookie); 676 if (error != B_OK) 677 RETURN_ERROR(error); 678 ObjectDeleter<FileCookie> cookieDeleter(cookie); 679 680 // update stat data 681 newFile->SetHardLinks(1); 682 newFile->SetParentDirectory(directory->BlockIndex()); 683 684 directory->Touched(NODE_MODIFIED); 685 686 // announce the new vnode (needed for creating the file cache), but don't 687 // publish it yet 688 error = volume->NewNode(newFile); 689 if (error != B_OK) 690 RETURN_ERROR(error); 691 692 // There's a vnode now -- the File object will be deleted when that is 693 // removed. 694 transaction.UpdateNodeFlags(newFile, TRANSACTION_REMOVE_NODE_ON_ERROR); 695 696 // create the file cache 697 error = newFile->InitForVFS(); 698 if (error != B_OK) 699 return error; 700 701 // node is fully initialized -- publish the vnode 702 error = volume->PublishNode(newFile, 0); 703 if (error != B_OK) { 704 // publish_vnode() deletes the vnode on error, but it doesn't call the 705 // remove_vnode() hook. So we need to make sure the object is deleted. 706 transaction.UpdateNodeFlags(newFile, TRANSACTION_DELETE_NODE); 707 RETURN_ERROR(error); 708 } 709 710 // commit the transaction 711 if (commitTransaction) { 712 error = transaction.Commit(EntryCreatedNotification(directory, name, 713 newFile)); 714 if (error != B_OK) { 715 volume->PutNode(newFile); 716 RETURN_ERROR(error); 717 } 718 } 719 720 _cookie = cookieDeleter.Detach(); 721 _node = newFile; 722 _created = true; 723 724 return B_OK; 725} 726 727 728/*! Gets the node's attribute directory. 729 If a transaction is given and the attribute directory doesn't exist, a new 730 one is created and associate with the node. 731 On success the caller gets a reference to the attribute directory and is 732 responsible for putting it. If a transaction was given, the attribute 733 directory must be put after committing/aborting the transaction. 734*/ 735static status_t 736get_attribute_directory(Node* node, Transaction* transaction, 737 Directory*& _attributeDirectory) 738{ 739 uint64 blockIndex = node->AttributeDirectory(); 740 Directory* attributeDirectory; 741 742 if (blockIndex != 0) { 743 // get the attribute directory node 744 Node* attrDirNode; 745 status_t error = node->GetVolume()->GetNode(blockIndex, attrDirNode); 746 if (error != B_OK) 747 RETURN_ERROR(error); 748 749 attributeDirectory = dynamic_cast<Directory*>(attrDirNode); 750 if (attributeDirectory == NULL) { 751 node->GetVolume()->PutNode(node); 752 ERROR("checksumfs: attribute directory (%" B_PRIu64 ") of node %" 753 B_PRIu64 " is not a directory!\n", blockIndex, 754 node->BlockIndex()); 755 RETURN_ERROR(B_BAD_DATA); 756 } 757 } else { 758 // no (i.e. empty) attribute directory yet 759 if (transaction == NULL) 760 return B_ENTRY_NOT_FOUND; 761 762 // create a new one 763 status_t error = node->GetVolume()->CreateDirectory( 764 S_IRWXU | S_IRWXG | S_IRWXO, *transaction, attributeDirectory); 765 if (error != B_OK) 766 RETURN_ERROR(error); 767 768 attributeDirectory->SetMode(attributeDirectory->Mode() | S_ATTR_DIR); 769 attributeDirectory->SetParentDirectory(node->BlockIndex()); 770 attributeDirectory->SetHardLinks(1); 771 node->SetAttributeDirectory(attributeDirectory->BlockIndex()); 772 773 // publish it 774 error = node->GetVolume()->PublishNode(attributeDirectory, 0); 775 if (error != B_OK) 776 RETURN_ERROR(error); 777 778 // We have published the attribute directory, so don't delete it when 779 // committing or aborting the transaction. Instead, on error remove it. 780 transaction->UpdateNodeFlags(attributeDirectory, 781 TRANSACTION_REMOVE_NODE_ON_ERROR); 782 } 783 784 _attributeDirectory = attributeDirectory; 785 return B_OK; 786} 787 788 789// #pragma mark - FS operations 790 791 792static float 793checksumfs_identify_partition(int fd, partition_data* partition, 794 void** _cookie) 795{ 796 if ((uint64)partition->size < kCheckSumFSMinSize) 797 return -1; 798 799 SuperBlock* superBlock = new(std::nothrow) SuperBlock; 800 if (superBlock == NULL) 801 return -1; 802 ObjectDeleter<SuperBlock> superBlockDeleter(superBlock); 803 804 if (pread(fd, superBlock, sizeof(*superBlock), kCheckSumFSSuperBlockOffset) 805 != sizeof(*superBlock)) { 806 return -1; 807 } 808 809 if (!superBlock->Check((uint64)partition->size / B_PAGE_SIZE)) 810 return -1; 811 812 *_cookie = superBlockDeleter.Detach(); 813 return 0.8f; 814} 815 816 817static status_t 818checksumfs_scan_partition(int fd, partition_data* partition, void* cookie) 819{ 820 SuperBlock* superBlock = (SuperBlock*)cookie; 821 822 partition->status = B_PARTITION_VALID; 823 partition->flags |= B_PARTITION_FILE_SYSTEM; 824 partition->content_size = superBlock->TotalBlocks() * B_PAGE_SIZE; 825 partition->block_size = B_PAGE_SIZE; 826 partition->content_name = strdup(superBlock->Name()); 827 if (partition->content_name == NULL) 828 return B_NO_MEMORY; 829 830 return B_OK; 831} 832 833 834static void 835checksumfs_free_identify_partition_cookie(partition_data* partition, 836 void* cookie) 837{ 838 SuperBlock* superBlock = (SuperBlock*)cookie; 839 delete superBlock; 840} 841 842 843static status_t 844checksumfs_mount(fs_volume* fsVolume, const char* device, uint32 flags, 845 const char* args, ino_t* _rootVnodeID) 846{ 847 Volume* volume = new(std::nothrow) Volume(flags); 848 if (volume == NULL) 849 RETURN_ERROR(B_NO_MEMORY); 850 ObjectDeleter<Volume> volumeDeleter(volume); 851 852 status_t error = volume->Init(device); 853 if (error != B_OK) 854 RETURN_ERROR(error); 855 856 error = volume->Mount(fsVolume); 857 if (error != B_OK) 858 RETURN_ERROR(error); 859 860 fsVolume->private_volume = volumeDeleter.Detach(); 861 fsVolume->ops = &gCheckSumFSVolumeOps; 862 *_rootVnodeID = volume->RootDirectory()->BlockIndex(); 863 864 return B_OK; 865} 866 867 868static status_t 869checksumfs_set_content_name(int fd, partition_id partition, const char* name, 870 disk_job_id job) 871{ 872 // TODO: Implement! 873 return B_UNSUPPORTED; 874} 875 876 877static status_t 878checksumfs_initialize(int fd, partition_id partition, const char* name, 879 const char* parameters, off_t partitionSize, disk_job_id job) 880{ 881 if (name == NULL || strlen(name) >= kCheckSumFSNameLength) 882 return B_BAD_VALUE; 883 884 // TODO: Forcing a non-empty name here. Superfluous when the userland disk 885 // system add-on has a parameter editor for it. 886 if (*name == '\0') 887 name = "Unnamed"; 888 889 update_disk_device_job_progress(job, 0); 890 891 Volume volume(0); 892 893 status_t error = volume.Init(fd, partitionSize / B_PAGE_SIZE); 894 if (error != B_OK) 895 return error; 896 897 error = volume.Initialize(name); 898 if (error != B_OK) 899 return error; 900 901 // rescan partition 902 error = scan_partition(partition); 903 if (error != B_OK) 904 return error; 905 906 update_disk_device_job_progress(job, 1); 907 908 return B_OK; 909} 910 911 912// #pragma mark - volume operations 913 914 915static status_t 916checksumfs_unmount(fs_volume* fsVolume) 917{ 918 Volume* volume = (Volume*)fsVolume->private_volume; 919 volume->Unmount(); 920 return B_OK; 921} 922 923 924static status_t 925checksumfs_read_fs_info(fs_volume* fsVolume, struct fs_info* info) 926{ 927 Volume* volume = (Volume*)fsVolume->private_volume; 928 volume->GetInfo(*info); 929 return B_OK; 930} 931 932 933static status_t 934checksumfs_write_fs_info(fs_volume* fsVolume, const struct fs_info* info, 935 uint32 mask) 936{ 937 Volume* volume = (Volume*)fsVolume->private_volume; 938 939 if ((mask & FS_WRITE_FSINFO_NAME) != 0) { 940 status_t error = volume->SetName(info->volume_name); 941 if (error != B_OK) 942 return error; 943 } 944 945 return B_OK; 946} 947 948 949static status_t 950checksumfs_sync(fs_volume* fsVolume) 951{ 952 Volume* volume = (Volume*)fsVolume->private_volume; 953 954 return block_cache_sync(volume->BlockCache()); 955} 956 957 958static status_t 959checksumfs_get_vnode(fs_volume* fsVolume, ino_t id, fs_vnode* vnode, 960 int* _type, uint32* _flags, bool reenter) 961{ 962 Volume* volume = (Volume*)fsVolume->private_volume; 963 964 Node* node; 965 status_t error = volume->ReadNode(id, node); 966 if (error != B_OK) 967 return error; 968 969 error = node->InitForVFS(); 970 if (error != B_OK) { 971 delete node; 972 return error; 973 } 974 975 vnode->private_node = node; 976 vnode->ops = &gCheckSumFSVnodeOps; 977 *_type = node->Mode(); 978 *_flags = 0; 979 980 return B_OK; 981} 982 983 984// #pragma mark - vnode operations 985 986 987static status_t 988checksumfs_lookup(fs_volume* fsVolume, fs_vnode* fsDir, const char* name, 989 ino_t* _id) 990{ 991 Volume* volume = (Volume*)fsVolume->private_volume; 992 Node* node = (Node*)fsDir->private_node; 993 994 Directory* directory = dynamic_cast<Directory*>(node); 995 if (directory == NULL) 996 return B_NOT_A_DIRECTORY; 997 998 status_t error = check_access(directory, X_OK); 999 if (error != B_OK) 1000 return error; 1001 1002 NodeReadLocker nodeLocker(node); 1003 1004 uint64 blockIndex; 1005 1006 if (strcmp(name, ".") == 0) { 1007 blockIndex = directory->BlockIndex(); 1008 } else if (strcmp(name, "..") == 0) { 1009 blockIndex = directory->ParentDirectory(); 1010 } else { 1011 status_t error = directory->LookupEntry(name, blockIndex); 1012 if (error != B_OK) 1013 return error; 1014 } 1015 1016 // get the node 1017 Node* childNode; 1018 error = volume->GetNode(blockIndex, childNode); 1019 if (error != B_OK) 1020 return error; 1021 1022 *_id = blockIndex; 1023 return B_OK; 1024} 1025 1026 1027static status_t 1028checksumfs_put_vnode(fs_volume* fsVolume, fs_vnode* vnode, bool reenter) 1029{ 1030 Node* node = (Node*)vnode->private_node; 1031 delete node; 1032 return B_OK; 1033} 1034 1035 1036static status_t 1037checksumfs_remove_vnode(fs_volume* fsVolume, fs_vnode* vnode, bool reenter) 1038{ 1039 Volume* volume = (Volume*)fsVolume->private_volume; 1040 Node* node = (Node*)vnode->private_node; 1041 return volume->DeleteNode(node); 1042} 1043 1044 1045 1046// #pragma mark - asynchronous I/O 1047 1048 1049static status_t 1050iterative_io_get_vecs_hook(void* cookie, io_request* request, off_t offset, 1051 size_t size, file_io_vec* vecs, size_t* _count) 1052{ 1053 File* file = (File*)cookie; 1054 1055 RETURN_ERROR(file_map_translate(file->FileMap(), offset, size, vecs, _count, 1056 B_PAGE_SIZE)); 1057} 1058 1059 1060static status_t 1061iterative_io_finished_hook(void* cookie, io_request* request, status_t status, 1062 bool partialTransfer, size_t bytesTransferred) 1063{ 1064 File* file = (File*)cookie; 1065 file->ReadUnlock(); 1066 return B_OK; 1067} 1068 1069 1070static status_t 1071checksumfs_io(fs_volume* fsVolume, fs_vnode* vnode, void* cookie, 1072 io_request* request) 1073{ 1074 Volume* volume = (Volume*)fsVolume->private_volume; 1075 File* file = dynamic_cast<File*>((Node*)vnode->private_node); 1076 if (file == NULL) { 1077 notify_io_request(request, B_READ_ONLY_DEVICE); 1078 RETURN_ERROR(B_BAD_VALUE); 1079 } 1080 1081 if (io_request_is_write(request) && volume->IsReadOnly()) { 1082 notify_io_request(request, B_READ_ONLY_DEVICE); 1083 RETURN_ERROR(B_READ_ONLY_DEVICE); 1084 } 1085 1086 // Read-lock the file -- we'll unlock it in the finished hook. 1087 if (io_request_is_vip(request)) { 1088 // We cannot wait for the node lock indefinitely. So try read-locking 1089 // with a timeout (0.1 s). 1090 if (!file->ReadLockWithTimeout(B_RELATIVE_TIMEOUT, 100000)) { 1091 notify_io_request(request, B_BUSY); 1092 RETURN_ERROR(B_BUSY); 1093 } 1094 } else 1095 file->ReadLock(); 1096 1097 RETURN_ERROR(do_iterative_fd_io(volume->FD(), request, 1098 iterative_io_get_vecs_hook, iterative_io_finished_hook, file)); 1099} 1100 1101 1102// #pragma mark - cache file access 1103 1104 1105static status_t 1106checksumfs_get_file_map(fs_volume* fsVolume, fs_vnode* vnode, off_t offset, 1107 size_t size, struct file_io_vec* vecs, size_t* _count) 1108{ 1109 if (offset < 0) 1110 RETURN_ERROR(B_BAD_VALUE); 1111 1112 File* file = dynamic_cast<File*>((Node*)vnode->private_node); 1113 if (file == NULL) 1114 RETURN_ERROR(B_BAD_VALUE); 1115 1116 RETURN_ERROR(file->GetFileVecs(offset, size, vecs, *_count, *_count)); 1117} 1118 1119 1120// #pragma mark - common operations 1121 1122 1123static status_t 1124checksumfs_set_flags(fs_volume* fsVolume, fs_vnode* vnode, void* _cookie, 1125 int flags) 1126{ 1127 FileCookie* cookie = (FileCookie*)_cookie; 1128 1129 cookie->openMode = (cookie->openMode & ~O_APPEND) | (flags & O_APPEND); 1130 1131 // TODO: Also support O_NOCACHE! 1132 1133 return B_OK; 1134} 1135 1136 1137static status_t 1138checksumfs_fsync(fs_volume* fsVolume, fs_vnode* vnode) 1139{ 1140 Node* node = (Node*)vnode->private_node; 1141 1142 NodeReadLocker nodeLocker(node); 1143 1144 return node->Sync(); 1145} 1146 1147 1148static status_t 1149checksumfs_read_symlink(fs_volume* fsVolume, fs_vnode* vnode, char* buffer, 1150 size_t* _bufferSize) 1151{ 1152 SymLink* symLink = dynamic_cast<SymLink*>((Node*)vnode->private_node); 1153 if (symLink == NULL) 1154 RETURN_ERROR(B_BAD_VALUE); 1155 1156 status_t error = check_access(symLink, R_OK); 1157 if (error != B_OK) 1158 return error; 1159 1160 return symLink->ReadSymLink(buffer, *_bufferSize, *_bufferSize); 1161} 1162 1163 1164static status_t 1165checksumfs_create_symlink(fs_volume* fsVolume, fs_vnode* parent, 1166 const char* name, const char* path, int mode) 1167{ 1168 Volume* volume = (Volume*)fsVolume->private_volume; 1169 Directory* directory 1170 = dynamic_cast<Directory*>((Node*)parent->private_node); 1171 if (directory == NULL) 1172 return B_NOT_A_DIRECTORY; 1173 1174 if (volume->IsReadOnly()) 1175 return B_READ_ONLY_DEVICE; 1176 1177 status_t error = check_access(directory, W_OK); 1178 if (error != B_OK) 1179 return error; 1180 1181 // start a transaction and add the directory (write locks it, too) 1182 Transaction transaction(volume); 1183 error = transaction.StartAndAddNode(directory); 1184 if (error != B_OK) 1185 return error; 1186 1187 // don't create an entry in an unlinked directory 1188 if (directory->HardLinks() == 0) 1189 RETURN_ERROR(B_ENTRY_NOT_FOUND); 1190 1191 // create a symlink node 1192 SymLink* newSymLink; 1193 error = volume->CreateSymLink(mode, transaction, newSymLink); 1194 if (error != B_OK) 1195 return error; 1196 1197 // write it 1198 error = newSymLink->WriteSymLink(path, strlen(path), transaction); 1199 if (error != B_OK) 1200 return error; 1201 1202 // insert the new symlink 1203 error = directory->InsertEntry(name, newSymLink->BlockIndex(), transaction); 1204 if (error != B_OK) 1205 return error; 1206 1207 // update stat data 1208 newSymLink->SetHardLinks(1); 1209 1210 directory->Touched(NODE_MODIFIED); 1211 1212 // commit the transaction 1213 return transaction.Commit(EntryCreatedNotification(directory, name, 1214 newSymLink)); 1215} 1216 1217 1218static status_t 1219checksumfs_link(fs_volume* fsVolume, fs_vnode* dir, const char* name, 1220 fs_vnode* vnode) 1221{ 1222 Volume* volume = (Volume*)fsVolume->private_volume; 1223 Node* node = (Node*)vnode->private_node; 1224 Directory* directory = dynamic_cast<Directory*>((Node*)dir->private_node); 1225 if (directory == NULL) 1226 return B_NOT_A_DIRECTORY; 1227 1228 if (volume->IsReadOnly()) 1229 return B_READ_ONLY_DEVICE; 1230 1231 // don't allow hardlinking directories 1232 if (S_ISDIR(node->Mode())) 1233 RETURN_ERROR(B_NOT_ALLOWED); 1234 1235 // start a transaction and lock the nodes 1236 Transaction transaction(volume); 1237 status_t error = transaction.Start(); 1238 if (error != B_OK) 1239 RETURN_ERROR(error); 1240 1241 error = transaction.AddNodes(directory, node); 1242 if (error != B_OK) 1243 RETURN_ERROR(error); 1244 1245 // check permissions 1246 error = check_access(directory, W_OK); 1247 if (error != B_OK) 1248 RETURN_ERROR(error); 1249 1250 // don't create an entry in an unlinked directory 1251 if (directory->HardLinks() == 0) 1252 RETURN_ERROR(B_ENTRY_NOT_FOUND); 1253 1254 // insert the new entry 1255 error = directory->InsertEntry(name, node->BlockIndex(), transaction); 1256 if (error != B_OK) 1257 RETURN_ERROR(error); 1258 1259 // update stat data 1260 node->SetHardLinks(node->HardLinks() + 1); 1261 directory->Touched(NODE_MODIFIED); 1262 1263 // commit the transaction 1264 return transaction.Commit(EntryCreatedNotification(directory, name, node)); 1265} 1266 1267 1268static status_t 1269checksumfs_unlink(fs_volume* fsVolume, fs_vnode* dir, const char* name) 1270{ 1271 return remove_entry(fsVolume, dir, name, false); 1272} 1273 1274 1275static status_t 1276checksumfs_rename(fs_volume* fsVolume, fs_vnode* fromDir, const char* fromName, 1277 fs_vnode* toDir, const char* toName) 1278{ 1279 Volume* volume = (Volume*)fsVolume->private_volume; 1280 Directory* fromDirectory 1281 = dynamic_cast<Directory*>((Node*)fromDir->private_node); 1282 Directory* toDirectory 1283 = dynamic_cast<Directory*>((Node*)toDir->private_node); 1284 if (fromDirectory == NULL || toDirectory == NULL) 1285 return B_NOT_A_DIRECTORY; 1286 1287 if (volume->IsReadOnly()) 1288 return B_READ_ONLY_DEVICE; 1289 1290 // We need to write-lock all three nodes (both directories and the moved 1291 // node). To make that atomic, we have to lock the source directory, look up 1292 // the entry, unlock the directory, get the node, re-lock all, and look up 1293 // the entry again to check for changes. 1294 Transaction transaction(volume); 1295 Node* node; 1296 NodePutter nodePutter; 1297 1298 while (true) { 1299 // look up the entry 1300 NodeReadLocker directoryLocker(fromDirectory); 1301 1302 uint64 blockIndex; 1303 status_t error = fromDirectory->LookupEntry(fromName, blockIndex); 1304 if (error != B_OK) 1305 RETURN_ERROR(error); 1306 1307 directoryLocker.Unlock(); 1308 1309 // get the entry's node 1310 error = volume->GetNode(blockIndex, node); 1311 if (error != B_OK) 1312 RETURN_ERROR(error); 1313 nodePutter.SetTo(node); 1314 1315 // start the transaction 1316 error = transaction.Start(); 1317 if (error != B_OK) 1318 RETURN_ERROR(error); 1319 1320 // write-lock the nodes 1321 error = fromDirectory != toDirectory 1322 ? transaction.AddNodes(fromDirectory, toDirectory, node) 1323 : transaction.AddNodes(fromDirectory, node); 1324 if (error != B_OK) 1325 RETURN_ERROR(error); 1326 1327 // check the situation again 1328 error = fromDirectory->LookupEntry(fromName, blockIndex); 1329 if (error != B_OK) 1330 RETURN_ERROR(error); 1331 if (blockIndex != node->BlockIndex()) { 1332 transaction.Abort(); 1333 continue; 1334 } 1335 1336 break; 1337 } 1338 1339 // check permissions 1340 status_t error = check_access(fromDirectory, W_OK); 1341 if (error != B_OK) 1342 RETURN_ERROR(error); 1343 1344 error = check_access(toDirectory, W_OK); 1345 if (error != B_OK) 1346 RETURN_ERROR(error); 1347 1348 // don't create an entry in an unlinked directory 1349 if (toDirectory->HardLinks() == 0) 1350 RETURN_ERROR(B_ENTRY_NOT_FOUND); 1351 1352 // Check whether this operation would move a directory into one of its 1353 // descendents. We iterate upwards, checking whether any ancestor of the 1354 // target directory is the moved directory (if it is a directory that is). 1355 if (fromDirectory != toDirectory && S_ISDIR(node->Mode())) { 1356 NodePutter ancestorPutter; 1357 Node* ancestor = toDirectory; 1358 1359 while (ancestor != volume->RootDirectory() 1360 || ancestor == fromDirectory) { 1361 if (ancestor == node) 1362 RETURN_ERROR(B_BAD_VALUE); 1363 1364 error = volume->GetNode(ancestor->ParentDirectory(), ancestor); 1365 if (error != B_OK) 1366 RETURN_ERROR(error); 1367 ancestorPutter.SetTo(ancestor); 1368 } 1369 } 1370 1371 // Everything looks good -- insert a new entry in the target directory and 1372 // remove the old entry from the source directory. 1373 error = toDirectory->InsertEntry(toName, node->BlockIndex(), transaction); 1374 if (error != B_OK) 1375 RETURN_ERROR(error); 1376 1377 error = fromDirectory->RemoveEntry(fromName, transaction); 1378 if (error != B_OK) 1379 RETURN_ERROR(error); 1380 1381 // update stat data 1382 node->SetParentDirectory(toDirectory->BlockIndex()); 1383 fromDirectory->Touched(NODE_MODIFIED); 1384 toDirectory->Touched(NODE_MODIFIED); 1385 1386 // commit the transaction 1387 return transaction.Commit(EntryMovedNotification(fromDirectory, fromName, 1388 toDirectory, toName, node)); 1389} 1390 1391 1392static status_t 1393checksumfs_access(fs_volume* fsVolume, fs_vnode* vnode, int mode) 1394{ 1395 Node* node = (Node*)vnode->private_node; 1396 1397 NodeReadLocker nodeLocker(node); 1398 1399 return check_access(node, mode); 1400} 1401 1402 1403static status_t 1404checksumfs_read_stat(fs_volume* fsVolume, fs_vnode* vnode, struct stat* st) 1405{ 1406 Node* node = (Node*)vnode->private_node; 1407 1408 NodeReadLocker nodeLocker(node); 1409 1410 st->st_mode = node->Mode(); 1411 st->st_nlink = node->HardLinks(); 1412 st->st_uid = node->UID(); 1413 st->st_gid = node->GID(); 1414 st->st_size = node->Size(); 1415 st->st_blksize = B_PAGE_SIZE * 16; // random number 1416 set_timespec(st->st_mtim, node->ModificationTime()); 1417 set_timespec(st->st_ctim, node->ChangeTime()); 1418 set_timespec(st->st_crtim, node->CreationTime()); 1419 set_timespec(st->st_atim, node->AccessedTime()); 1420 st->st_type = 0; /* attribute/index type */ 1421 st->st_blocks = 1 + (st->st_size + B_PAGE_SIZE - 1) / B_PAGE_SIZE; 1422 // TODO: That does neither count management structures for the content 1423 // (for files) nor attributes. 1424 1425 return B_OK; 1426} 1427 1428 1429static status_t 1430checksumfs_write_stat(fs_volume* fsVolume, fs_vnode* vnode, 1431 const struct stat* st, uint32 statMask) 1432{ 1433 Volume* volume = (Volume*)fsVolume->private_volume; 1434 Node* node = (Node*)vnode->private_node; 1435 1436 if (volume->IsReadOnly()) 1437 return B_READ_ONLY_DEVICE; 1438 1439 // start a transaction and add the node to it (write locks the node, too) 1440 Transaction transaction(volume); 1441 status_t error = transaction.StartAndAddNode(node); 1442 if (error != B_OK) 1443 return error; 1444 1445 uid_t uid = geteuid(); 1446 bool isOwnerOrRoot = uid == 0 || uid == node->UID(); 1447 bool hasWriteAccess = check_access(node, W_OK) == B_OK; 1448 1449 bool updateModified = false; 1450 bool updateChanged = false; 1451 1452 if ((statMask & B_STAT_SIZE) != 0 && (uint64)st->st_size != node->Size()) { 1453 if (!hasWriteAccess) 1454 RETURN_ERROR(B_NOT_ALLOWED); 1455 1456 error = node->Resize(st->st_size, true, transaction); 1457 if (error != B_OK) 1458 RETURN_ERROR(error); 1459 1460 updateModified = updateChanged = true; 1461 } 1462 1463 if ((statMask & B_STAT_UID) != 0 && st->st_uid != node->UID()) { 1464 // only root can do that 1465 if (uid != 0) 1466 RETURN_ERROR(B_NOT_ALLOWED); 1467 1468 node->SetUID(st->st_uid); 1469 updateChanged = true; 1470 } 1471 1472 if ((statMask & B_STAT_GID) != 0 && st->st_gid != node->GID()) { 1473 // only the user or root can do that 1474 if (!isOwnerOrRoot) 1475 RETURN_ERROR(B_NOT_ALLOWED); 1476 1477 node->SetGID(st->st_gid); 1478 updateChanged = true; 1479 } 1480 1481 if ((statMask & B_STAT_MODE) != 0) { 1482 // only the user or root can do that 1483 if (!isOwnerOrRoot) 1484 RETURN_ERROR(B_NOT_ALLOWED); 1485 1486 node->SetMode((node->Mode() & ~(mode_t)S_IUMSK) 1487 | (st->st_mode & S_IUMSK)); 1488 updateChanged = true; 1489 } 1490 1491 if ((statMask & B_STAT_CREATION_TIME) != 0) { 1492 // the user or root can do that or any user with write access 1493 if (!isOwnerOrRoot && !hasWriteAccess) 1494 RETURN_ERROR(B_NOT_ALLOWED); 1495 1496 node->SetCreationTime(timespec_to_nsecs(st->st_crtim)); 1497 updateChanged = true; 1498 } 1499 1500 if ((statMask & B_STAT_MODIFICATION_TIME) != 0) { 1501 // the user or root can do that or any user with write access 1502 if (!isOwnerOrRoot && !hasWriteAccess) 1503 RETURN_ERROR(B_NOT_ALLOWED); 1504 1505 node->SetModificationTime(timespec_to_nsecs(st->st_mtim)); 1506 updateModified = false; 1507 updateChanged = true; 1508 } 1509 1510 if ((statMask & B_STAT_CHANGE_TIME) != 0) { 1511 // the user or root can do that or any user with write access 1512 if (!isOwnerOrRoot && !hasWriteAccess) 1513 RETURN_ERROR(B_NOT_ALLOWED); 1514 1515 node->SetModificationTime(timespec_to_nsecs(st->st_mtim)); 1516 updateModified = false; 1517 updateChanged = false; 1518 } 1519 1520 // update access/change/modification time 1521 if (updateModified) 1522 node->Touched(NODE_MODIFIED); 1523 else if (updateChanged) 1524 node->Touched(NODE_STAT_CHANGED); 1525 else 1526 node->Touched(NODE_ACCESSED); 1527 1528 // commit the transaction 1529 return transaction.Commit(StatChangedNotification(node, statMask)); 1530} 1531 1532 1533// #pragma mark - file operations 1534 1535 1536static status_t 1537checksumfs_create(fs_volume* fsVolume, fs_vnode* parent, const char* name, 1538 int openMode, int permissions, void** _cookie, ino_t* _newVnodeID) 1539{ 1540 Volume* volume = (Volume*)fsVolume->private_volume; 1541 Directory* directory 1542 = dynamic_cast<Directory*>((Node*)parent->private_node); 1543 if (directory == NULL) 1544 return B_NOT_A_DIRECTORY; 1545 1546 if (volume->IsReadOnly()) 1547 return B_READ_ONLY_DEVICE; 1548 1549 Transaction transaction(volume); 1550 FileCookie* cookie; 1551 Node* node; 1552 bool created; 1553 status_t error = create_file(volume, directory, name, openMode, permissions, 1554 transaction, true, cookie, node, created); 1555 if (error != B_OK) 1556 RETURN_ERROR(error); 1557 1558 *_cookie = cookie; 1559 *_newVnodeID = node->BlockIndex(); 1560 1561 return B_OK; 1562} 1563 1564 1565static status_t 1566checksumfs_open(fs_volume* fsVolume, fs_vnode* vnode, int openMode, 1567 void** _cookie) 1568{ 1569 Volume* volume = (Volume*)fsVolume->private_volume; 1570 Node* node = (Node*)vnode->private_node; 1571 1572 // don't allow opening an attribute this way 1573 if ((node->Mode() & S_ATTR) != 0) 1574 RETURN_ERROR(B_BAD_VALUE); 1575 1576 Transaction transaction(volume); 1577 FileCookie* cookie; 1578 status_t error = open_file(volume, node, openMode, transaction, true, 1579 cookie); 1580 if (error != B_OK) 1581 RETURN_ERROR(error); 1582 1583 *_cookie = cookie; 1584 return B_OK; 1585} 1586 1587 1588static status_t 1589checksumfs_close(fs_volume* fsVolume, fs_vnode* vnode, void* cookie) 1590{ 1591 return B_OK; 1592} 1593 1594 1595static status_t 1596checksumfs_free_cookie(fs_volume* fsVolume, fs_vnode* vnode, void* _cookie) 1597{ 1598 FileCookie* cookie = (FileCookie*)_cookie; 1599 Node* node = (Node*)vnode->private_node; 1600 1601 cookie->UpdateModifiedIfNecessary(node, true); 1602 1603 delete cookie; 1604 return B_OK; 1605} 1606 1607 1608static status_t 1609checksumfs_read(fs_volume* fsVolume, fs_vnode* vnode, void* _cookie, off_t pos, 1610 void* buffer, size_t* _length) 1611{ 1612 FileCookie* cookie = (FileCookie*)_cookie; 1613 Node* node = (Node*)vnode->private_node; 1614 1615 switch (cookie->openMode & O_RWMASK) { 1616 case O_RDONLY: 1617 case O_RDWR: 1618 break; 1619 case O_WRONLY: 1620 default: 1621 RETURN_ERROR(EBADF); 1622 } 1623 1624 return node->Read(pos, buffer, *_length, *_length); 1625} 1626 1627 1628static status_t 1629checksumfs_write(fs_volume* fsVolume, fs_vnode* vnode, void* _cookie, off_t pos, 1630 const void* buffer, size_t* _length) 1631{ 1632 FileCookie* cookie = (FileCookie*)_cookie; 1633 Node* node = (Node*)vnode->private_node; 1634 1635 switch (cookie->openMode & O_RWMASK) { 1636 case O_WRONLY: 1637 case O_RDWR: 1638 break; 1639 case O_RDONLY: 1640 default: 1641 RETURN_ERROR(EBADF); 1642 } 1643 1644 if (pos < 0) 1645 RETURN_ERROR(B_BAD_VALUE); 1646 1647 if ((cookie->openMode & O_APPEND) != 0) { 1648 pos = -1; 1649 // special value handled by Write() 1650 } 1651 1652 bool sizeChanged; 1653 status_t error = node->Write(pos, buffer, *_length, *_length, sizeChanged); 1654 if (error != B_OK) 1655 RETURN_ERROR(error); 1656 1657 // update the modification time and send out a notification from time to 1658 // time 1659 cookie->FileModified(node, sizeChanged); 1660 1661 return B_OK; 1662} 1663 1664 1665// #pragma mark - directory operations 1666 1667 1668status_t 1669checksumfs_create_dir(fs_volume* fsVolume, fs_vnode* parent, const char* name, 1670 int perms) 1671{ 1672 Volume* volume = (Volume*)fsVolume->private_volume; 1673 Directory* directory 1674 = dynamic_cast<Directory*>((Node*)parent->private_node); 1675 if (directory == NULL) 1676 return B_NOT_A_DIRECTORY; 1677 1678 if (volume->IsReadOnly()) 1679 return B_READ_ONLY_DEVICE; 1680 1681 status_t error = check_access(directory, W_OK); 1682 if (error != B_OK) 1683 return error; 1684 1685 // start a transaction and attach the directory (write locks it, too) 1686 Transaction transaction(volume); 1687 error = transaction.StartAndAddNode(directory); 1688 if (error != B_OK) 1689 return error; 1690 1691 // don't create an entry in an unlinked directory 1692 if (directory->HardLinks() == 0) 1693 RETURN_ERROR(B_ENTRY_NOT_FOUND); 1694 1695 // create a directory node 1696 Directory* newDirectory; 1697 error = volume->CreateDirectory(perms, transaction, newDirectory); 1698 if (error != B_OK) 1699 return error; 1700 1701 // insert the new directory 1702 error = directory->InsertEntry(name, newDirectory->BlockIndex(), 1703 transaction); 1704 if (error != B_OK) 1705 return error; 1706 1707 // update stat data 1708 newDirectory->SetHardLinks(1); 1709 newDirectory->SetParentDirectory(directory->BlockIndex()); 1710 1711 directory->Touched(NODE_MODIFIED); 1712 1713 // commit the transaction 1714 return transaction.Commit(EntryCreatedNotification(directory, name, 1715 newDirectory)); 1716} 1717 1718 1719status_t 1720checksumfs_remove_dir(fs_volume* volume, fs_vnode* parent, const char* name) 1721{ 1722 return remove_entry(volume, parent, name, true); 1723} 1724 1725 1726static status_t 1727checksumfs_open_dir(fs_volume* fsVolume, fs_vnode* vnode, void** _cookie) 1728{ 1729 Directory* directory = dynamic_cast<Directory*>((Node*)vnode->private_node); 1730 if (directory == NULL) 1731 return B_NOT_A_DIRECTORY; 1732 1733 NodeReadLocker nodeLocker(directory); 1734 1735 // don't allow opening an attribute directory this way 1736 if ((directory->Mode() & S_ATTR_DIR) != 0) 1737 RETURN_ERROR(B_BAD_VALUE); 1738 1739 status_t error = check_access(directory, R_OK); 1740 if (error != B_OK) 1741 return error; 1742 1743 DirCookie* cookie = new(std::nothrow) DirCookie(directory); 1744 if (cookie == NULL) 1745 return B_NO_MEMORY; 1746 1747 *_cookie = cookie; 1748 return B_OK; 1749} 1750 1751 1752static status_t 1753checksumfs_close_dir(fs_volume* fsVolume, fs_vnode* vnode, void* cookie) 1754{ 1755 return B_OK; 1756} 1757 1758 1759static status_t 1760checksumfs_free_dir_cookie(fs_volume* fsVolume, fs_vnode* vnode, void* _cookie) 1761{ 1762 DirCookie* cookie = (DirCookie*)_cookie; 1763 delete cookie; 1764 return B_OK; 1765} 1766 1767 1768static status_t 1769checksumfs_read_dir(fs_volume* fsVolume, fs_vnode* vnode, void* _cookie, 1770 struct dirent* buffer, size_t bufferSize, uint32* _num) 1771{ 1772 if (*_num == 0) 1773 return B_OK; 1774 1775 DirCookie* cookie = (DirCookie*)_cookie; 1776 1777 NodeReadLocker nodeLocker(cookie->GetDirectory()); 1778 1779 return cookie->ReadNextEntry(buffer, bufferSize, *_num); 1780} 1781 1782 1783static status_t 1784checksumfs_rewind_dir(fs_volume* fsVolume, fs_vnode* vnode, void* _cookie) 1785{ 1786 DirCookie* cookie = (DirCookie*)_cookie; 1787 1788 NodeReadLocker nodeLocker(cookie->GetDirectory()); 1789 1790 cookie->Rewind(); 1791 return B_OK; 1792} 1793 1794 1795// #pragma mark - attribute directory operations 1796 1797 1798static status_t 1799checksumfs_open_attr_dir(fs_volume* volume, fs_vnode* vnode, void** _cookie) 1800{ 1801 Node* node = (Node*)vnode->private_node; 1802 1803 NodeReadLocker nodeLocker(node); 1804 1805 status_t error = check_access(node, R_OK); 1806 if (error != B_OK) 1807 return error; 1808 1809 AttrDirCookie* cookie = new(std::nothrow) AttrDirCookie(node); 1810 if (cookie == NULL) 1811 return B_NO_MEMORY; 1812 1813 *_cookie = cookie; 1814 return B_OK; 1815} 1816 1817 1818static status_t 1819checksumfs_close_attr_dir(fs_volume* volume, fs_vnode* vnode, void* cookie) 1820{ 1821 return B_OK; 1822} 1823 1824 1825static status_t 1826checksumfs_free_attr_dir_cookie(fs_volume* volume, fs_vnode* vnode, 1827 void* _cookie) 1828{ 1829 AttrDirCookie* cookie = (AttrDirCookie*)_cookie; 1830 delete cookie; 1831 return B_OK; 1832} 1833 1834 1835static status_t 1836checksumfs_read_attr_dir(fs_volume* volume, fs_vnode* vnode, void* _cookie, 1837 struct dirent* buffer, size_t bufferSize, uint32* _num) 1838{ 1839 if (*_num == 0) 1840 return B_OK; 1841 1842 Node* node = (Node*)vnode->private_node; 1843 AttrDirCookie* cookie = (AttrDirCookie*)_cookie; 1844 1845 NodeReadLocker nodeLocker(node); 1846 1847 return cookie->ReadNextEntry(buffer, bufferSize, *_num); 1848} 1849 1850 1851static status_t 1852checksumfs_rewind_attr_dir(fs_volume* volume, fs_vnode* vnode, void* _cookie) 1853{ 1854 Node* node = (Node*)vnode->private_node; 1855 AttrDirCookie* cookie = (AttrDirCookie*)_cookie; 1856 1857 NodeReadLocker nodeLocker(node); 1858 1859 cookie->Rewind(); 1860 return B_OK; 1861} 1862 1863 1864// #pragma mark - attribute operations 1865 1866 1867static status_t 1868checksumfs_create_attr(fs_volume* fsVolume, fs_vnode* vnode, const char* name, 1869 uint32 type, int openMode, void** _cookie) 1870{ 1871 Volume* volume = (Volume*)fsVolume->private_volume; 1872 Node* node = (Node*)vnode->private_node; 1873 if (node == NULL) 1874 return B_NOT_A_DIRECTORY; 1875 1876 if (volume->IsReadOnly()) 1877 return B_READ_ONLY_DEVICE; 1878 1879 // create the attribute cookie 1880 AttributeCookie* cookie = new(std::nothrow) AttributeCookie(name); 1881 if (cookie == NULL || cookie->name == NULL) { 1882 delete cookie; 1883 return B_NO_MEMORY; 1884 } 1885 ObjectDeleter<AttributeCookie> cookieDeleter(cookie); 1886 1887 // Start a transaction and lock the node. 1888 // Note: Other than for ordinary nodes the locking order when attributes 1889 // are involved is: node -> attribute directory -> attribute. 1890 Transaction transaction(volume); 1891 status_t error = transaction.StartAndAddNode(node); 1892 if (error != B_OK) 1893 RETURN_ERROR(error); 1894 1895 // check permissions 1896 error = check_access(node, W_OK); 1897 if (error != B_OK) 1898 return error; 1899 1900 // get the attribute directory (create, if necessary) 1901 Directory* attributeDirectory; 1902 error = get_attribute_directory(node, &transaction, attributeDirectory); 1903 if (error != B_OK) 1904 RETURN_ERROR(error); 1905 NodePutter attributeDirectoryPutter(attributeDirectory); 1906 1907 // open/create the attribute 1908 bool created; 1909 error = create_file(volume, attributeDirectory, name, openMode, 1910 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, transaction, 1911 false, cookie->fileCookie, cookie->attribute, created); 1912 if (error != B_OK) 1913 RETURN_ERROR(error); 1914 1915 if (created) { 1916 cookie->attribute->SetMode(cookie->attribute->Mode() | S_ATTR); 1917 cookie->attribute->SetAttributeType(type); 1918 } 1919 1920 // commit the transaction 1921 if (transaction.IsActive()) { 1922 if (created || (openMode & O_TRUNC) != 0) { 1923 node->Touched(NODE_STAT_CHANGED); 1924 1925 AttributeChangedNotification attributeNotification(node, name, 1926 created ? B_ATTR_CREATED : B_ATTR_CHANGED); 1927 StatChangedNotification statNotification(node, B_STAT_CHANGE_TIME); 1928 error = transaction.Commit(&attributeNotification, 1929 &statNotification); 1930 } else 1931 error = transaction.Commit(); 1932 } 1933 1934 *_cookie = cookieDeleter.Detach(); 1935 return B_OK; 1936} 1937 1938 1939static status_t 1940checksumfs_open_attr(fs_volume* fsVolume, fs_vnode* vnode, const char* name, 1941 int openMode, void** _cookie) 1942{ 1943 Volume* volume = (Volume*)fsVolume->private_volume; 1944 Node* node = (Node*)vnode->private_node; 1945 1946 // create the attribute cookie 1947 AttributeCookie* cookie = new(std::nothrow) AttributeCookie(name); 1948 if (cookie == NULL || cookie->name == NULL) { 1949 delete cookie; 1950 return B_NO_MEMORY; 1951 } 1952 ObjectDeleter<AttributeCookie> cookieDeleter(cookie); 1953 1954 // Get the node's attribute directory (don't create it, if it doesn't exist 1955 // yet). We only need to read-lock the node for that, but when O_TRUNC is 1956 // given, we already start the transaction and write-lock the node, so we 1957 // don't get a locking order inversion later. The locking order when 1958 // attributes are involved is: node -> attribute directory -> attribute. 1959 Transaction transaction(volume); 1960 NodeReadLocker readLocker; 1961 if ((openMode & O_TRUNC) != 0) { 1962 status_t error = transaction.StartAndAddNode(node); 1963 if (error != B_OK) 1964 RETURN_ERROR(error); 1965 } else 1966 readLocker.SetTo(node, false); 1967 1968 Directory* attributeDirectory; 1969 status_t error = get_attribute_directory(node, NULL, attributeDirectory); 1970 if (error != B_OK) 1971 RETURN_ERROR(error); 1972 NodePutter attributeDirectoryPutter(attributeDirectory); 1973 1974 // look up the attribute 1975 readLocker.SetTo(attributeDirectory, false); 1976 uint64 blockIndex; 1977 error = attributeDirectory->LookupEntry(name, blockIndex); 1978 if (error != B_OK) 1979 RETURN_ERROR(error); 1980 1981 error = volume->GetNode(blockIndex, cookie->attribute); 1982 // the vnode reference directly goes to the cookie in case of success 1983 if (error != B_OK) 1984 RETURN_ERROR(error); 1985 1986 // open the attribute 1987 error = open_file(volume, cookie->attribute, openMode, transaction, false, 1988 cookie->fileCookie); 1989 if (error != B_OK) 1990 RETURN_ERROR(error); 1991 1992 // commit the transaction 1993 if (transaction.IsActive()) { 1994 if ((openMode & O_TRUNC) != 0) { 1995 node->Touched(NODE_STAT_CHANGED); 1996 1997 AttributeChangedNotification attributeNotification(node, name, 1998 B_ATTR_CHANGED); 1999 StatChangedNotification statNotification(node, B_STAT_CHANGE_TIME); 2000 error = transaction.Commit(&attributeNotification, 2001 &statNotification); 2002 } else 2003 error = transaction.Commit(); 2004 } 2005 2006 *_cookie = cookieDeleter.Detach(); 2007 return B_OK; 2008} 2009 2010 2011static status_t 2012checksumfs_close_attr(fs_volume* fsVolume, fs_vnode* vnode, void* cookie) 2013{ 2014 return B_OK; 2015} 2016 2017 2018static status_t 2019checksumfs_free_attr_cookie(fs_volume* fsVolume, fs_vnode* vnode, void* _cookie) 2020{ 2021 AttributeCookie* cookie = (AttributeCookie*)_cookie; 2022 delete cookie; 2023 return B_OK; 2024} 2025 2026 2027static status_t 2028checksumfs_read_attr(fs_volume* fsVolume, fs_vnode* vnode, void* _cookie, 2029 off_t pos, void* buffer, size_t* _length) 2030{ 2031 AttributeCookie* cookie = (AttributeCookie*)_cookie; 2032 2033 switch (cookie->fileCookie->openMode & O_RWMASK) { 2034 case O_RDONLY: 2035 case O_RDWR: 2036 break; 2037 case O_WRONLY: 2038 default: 2039 RETURN_ERROR(EBADF); 2040 } 2041 2042 return cookie->attribute->Read(pos, buffer, *_length, *_length); 2043} 2044 2045 2046static status_t 2047checksumfs_write_attr(fs_volume* fsVolume, fs_vnode* vnode, void* _cookie, 2048 off_t pos, const void* buffer, size_t* _length) 2049{ 2050 Volume* volume = (Volume*)fsVolume->private_volume; 2051 Node* node = (Node*)vnode->private_node; 2052 AttributeCookie* cookie = (AttributeCookie*)_cookie; 2053 2054 switch (cookie->fileCookie->openMode & O_RWMASK) { 2055 case O_WRONLY: 2056 case O_RDWR: 2057 break; 2058 case O_RDONLY: 2059 default: 2060 RETURN_ERROR(EBADF); 2061 } 2062 2063 if (pos < 0) 2064 RETURN_ERROR(B_BAD_VALUE); 2065 2066 if ((cookie->fileCookie->openMode & O_APPEND) != 0) { 2067 pos = -1; 2068 // special value handled by Write() 2069 } 2070 2071 bool sizeChanged; 2072 status_t error = cookie->attribute->Write(pos, buffer, *_length, *_length, 2073 sizeChanged); 2074 if (error != B_OK) 2075 RETURN_ERROR(error); 2076 2077 // update the node changed time and send out a notifications (don't fail, 2078 // if any of this fails) 2079 Transaction transaction(volume); 2080 if (transaction.StartAndAddNode(node) != B_OK) 2081 return B_OK; 2082 if (transaction.AddNode(cookie->attribute) != B_OK) 2083 return B_OK; 2084 2085 cookie->attribute->Touched(NODE_MODIFIED); 2086 2087 // commit the transaction 2088 if (cookie->attribute->ParentDirectory() != 0) { 2089 node->Touched(NODE_STAT_CHANGED); 2090 2091 AttributeChangedNotification attributeNotification(node, cookie->name, 2092 B_ATTR_CHANGED); 2093 StatChangedNotification statNotification(node, B_STAT_CHANGE_TIME); 2094 transaction.Commit(&attributeNotification, &statNotification); 2095 } else { 2096 // attribute has been removed -- no notifications needed 2097 transaction.Commit(); 2098 } 2099 2100 return B_OK; 2101} 2102 2103 2104static status_t 2105checksumfs_read_attr_stat(fs_volume* fsVolume, fs_vnode* vnode, void* _cookie, 2106 struct stat* st) 2107{ 2108 AttributeCookie* cookie = (AttributeCookie*)_cookie; 2109 2110 // not many fields needed ATM 2111 st->st_size = cookie->attribute->Size(); 2112 st->st_type = cookie->attribute->AttributeType(); 2113 2114 return B_OK; 2115} 2116 2117 2118static status_t 2119checksumfs_remove_attr(fs_volume* fsVolume, fs_vnode* vnode, const char* name) 2120{ 2121 Volume* volume = (Volume*)fsVolume->private_volume; 2122 Node* node = (Node*)vnode->private_node; 2123 2124 if (volume->IsReadOnly()) 2125 return B_READ_ONLY_DEVICE; 2126 2127 // start a transaction 2128 Transaction transaction(volume); 2129 status_t error = transaction.StartAndAddNode(node); 2130 if (error != B_OK) 2131 RETURN_ERROR(error); 2132 2133 // check permissions 2134 error = check_access(node, W_OK); 2135 if (error != B_OK) 2136 RETURN_ERROR(error); 2137 2138 // get the attribute directory 2139 Directory* attributeDirectory; 2140 error = get_attribute_directory(node, NULL, attributeDirectory); 2141 if (error != B_OK) 2142 RETURN_ERROR(error); 2143 NodePutter attributeDirectoryPutter(attributeDirectory); 2144 2145 error = transaction.AddNode(attributeDirectory); 2146 if (error != B_OK) 2147 RETURN_ERROR(error); 2148 2149 // look up the entry 2150 uint64 blockIndex; 2151 error = attributeDirectory->LookupEntry(name, blockIndex); 2152 if (error != B_OK) 2153 RETURN_ERROR(error); 2154 2155 // get the attribute node 2156 Node* attribute; 2157 error = volume->GetNode(blockIndex, attribute); 2158 if (error != B_OK) 2159 RETURN_ERROR(error); 2160 NodePutter attributePutter(attribute); 2161 2162 error = transaction.AddNode(attribute); 2163 if (error != B_OK) { 2164 volume->PutNode(attribute); 2165 RETURN_ERROR(error); 2166 } 2167 2168 // remove the entry 2169 bool attrDirEmpty; 2170 error = attributeDirectory->RemoveEntry(name, transaction, &attrDirEmpty); 2171 if (error != B_OK) 2172 RETURN_ERROR(error); 2173 2174 // remove the attribute node 2175 error = volume->RemoveNode(attribute); 2176 if (error != B_OK) 2177 return error; 2178 transaction.UpdateNodeFlags(attribute, TRANSACTION_UNREMOVE_NODE_ON_ERROR); 2179 2180 // if the attribute directory is empty now, remove it too 2181 if (attrDirEmpty) { 2182 error = volume->RemoveNode(attributeDirectory); 2183 if (error != B_OK) 2184 return error; 2185 transaction.UpdateNodeFlags(attributeDirectory, 2186 TRANSACTION_UNREMOVE_NODE_ON_ERROR); 2187 2188 node->SetAttributeDirectory(0); 2189 } 2190 2191 // update stat data 2192 attribute->SetHardLinks(0); 2193 2194 node->Touched(NODE_STAT_CHANGED); 2195 2196 // commit the transaction 2197 AttributeChangedNotification attributeNotification(node, name, 2198 B_ATTR_REMOVED); 2199 StatChangedNotification statNotification(node, B_STAT_CHANGE_TIME); 2200 return transaction.Commit(&attributeNotification, &statNotification); 2201} 2202 2203 2204// #pragma mark - module 2205 2206 2207static status_t 2208checksumfs_std_ops(int32 operation, ...) 2209{ 2210 switch (operation) { 2211 case B_MODULE_INIT: 2212 init_debugging(); 2213 PRINT("checksumfs_std_ops(): B_MODULE_INIT\n"); 2214 return B_OK; 2215 2216 case B_MODULE_UNINIT: 2217 PRINT("checksumfs_std_ops(): B_MODULE_UNINIT\n"); 2218 exit_debugging(); 2219 return B_OK; 2220 2221 default: 2222 return B_BAD_VALUE; 2223 } 2224} 2225 2226 2227static file_system_module_info sFSModule = { 2228 { 2229 kCheckSumFSModuleName, 2230 0, 2231 checksumfs_std_ops 2232 }, 2233 kCheckSumFSShortName, 2234 CHECK_SUM_FS_PRETTY_NAME, 2235 // DDM flags 2236 B_DISK_SYSTEM_SUPPORTS_INITIALIZING 2237 | B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME 2238 | B_DISK_SYSTEM_SUPPORTS_WRITING, 2239 2240 /* scanning (the device is write locked) */ 2241 checksumfs_identify_partition, 2242 checksumfs_scan_partition, 2243 checksumfs_free_identify_partition_cookie, 2244 NULL, // free_partition_content_cookie 2245 2246 /* general operations */ 2247 checksumfs_mount, 2248 2249 /* capability querying (the device is read locked) */ 2250 NULL, // get_supported_operations 2251 2252 NULL, // validate_resize 2253 NULL, // validate_move 2254 NULL, // validate_set_content_name 2255 NULL, // validate_set_content_parameters 2256 NULL, // validate_initialize 2257 2258 /* shadow partition modification (device is write locked) */ 2259 NULL, // shadow_changed 2260 2261 /* writing (the device is NOT locked) */ 2262 NULL, // defragment 2263 NULL, // repair 2264 NULL, // resize 2265 NULL, // move 2266 checksumfs_set_content_name, 2267 NULL, // set_content_parameters 2268 checksumfs_initialize 2269}; 2270 2271 2272const module_info* modules[] = { 2273 (module_info*)&sFSModule, 2274 NULL 2275}; 2276 2277 2278fs_volume_ops gCheckSumFSVolumeOps = { 2279 checksumfs_unmount, 2280 2281 checksumfs_read_fs_info, 2282 checksumfs_write_fs_info, 2283 checksumfs_sync, 2284 2285 checksumfs_get_vnode, 2286 2287 /* index directory & index operations */ 2288 NULL, // open_index_dir 2289 NULL, // close_index_dir 2290 NULL, // free_index_dir_cookie 2291 NULL, // read_index_dir 2292 NULL, // rewind_index_dir 2293 2294 NULL, // create_index 2295 NULL, // remove_index 2296 NULL, // read_index_stat 2297 2298 /* query operations */ 2299 NULL, // open_query 2300 NULL, // close_query 2301 NULL, // free_query_cookie 2302 NULL, // read_query 2303 NULL, // rewind_query 2304 2305 /* support for FS layers */ 2306 NULL, // all_layers_mounted 2307 NULL, // create_sub_vnode 2308 NULL, // delete_sub_vnode 2309}; 2310 2311 2312fs_vnode_ops gCheckSumFSVnodeOps = { 2313 /* vnode operations */ 2314 checksumfs_lookup, 2315 NULL, // get_vnode_name 2316 2317 checksumfs_put_vnode, 2318 checksumfs_remove_vnode, 2319 2320 /* VM file access */ 2321 NULL, // can_page 2322 NULL, // read_pages 2323 NULL, // write_pages 2324 2325 /* asynchronous I/O */ 2326 checksumfs_io, 2327 NULL, // cancel_io 2328 2329 /* cache file access */ 2330 checksumfs_get_file_map, 2331 2332 /* common operations */ 2333 NULL, // ioctl 2334 checksumfs_set_flags, 2335 NULL, // select 2336 NULL, // deselect 2337 checksumfs_fsync, 2338 2339 checksumfs_read_symlink, 2340 checksumfs_create_symlink, 2341 2342 checksumfs_link, 2343 checksumfs_unlink, 2344 checksumfs_rename, 2345 2346 checksumfs_access, 2347 checksumfs_read_stat, 2348 checksumfs_write_stat, 2349 NULL, // preallocate 2350 2351 /* file operations */ 2352 checksumfs_create, 2353 checksumfs_open, 2354 checksumfs_close, 2355 checksumfs_free_cookie, 2356 checksumfs_read, 2357 checksumfs_write, 2358 2359 /* directory operations */ 2360 checksumfs_create_dir, 2361 checksumfs_remove_dir, 2362 checksumfs_open_dir, 2363 checksumfs_close_dir, 2364 checksumfs_free_dir_cookie, 2365 checksumfs_read_dir, 2366 checksumfs_rewind_dir, 2367 2368 /* attribute directory operations */ 2369 checksumfs_open_attr_dir, 2370 checksumfs_close_attr_dir, 2371 checksumfs_free_attr_dir_cookie, 2372 checksumfs_read_attr_dir, 2373 checksumfs_rewind_attr_dir, 2374 2375 /* attribute operations */ 2376 checksumfs_create_attr, 2377 checksumfs_open_attr, 2378 checksumfs_close_attr, 2379 checksumfs_free_attr_cookie, 2380 checksumfs_read_attr, 2381 checksumfs_write_attr, 2382 2383 checksumfs_read_attr_stat, 2384 NULL, // write_attr_stat 2385 NULL, // rename_attr 2386 checksumfs_remove_attr, 2387 2388 /* support for node and FS layers */ 2389 NULL, // create_special_node 2390 NULL // get_super_vnode 2391};