diff --git a/src/add-ons/kernel/file_systems/bfs/BPlusTree.cpp b/src/add-ons/kernel/file_systems/bfs/BPlusTree.cpp index 0dc90ee9df..73d1b160b6 100644 --- a/src/add-ons/kernel/file_systems/bfs/BPlusTree.cpp +++ b/src/add-ons/kernel/file_systems/bfs/BPlusTree.cpp @@ -1,11 +1,11 @@ /* BPlusTree - BFS B+Tree implementation -** -** Initial version by Axel Dörfler, axeld@pinc-software.de -** Roughly based on 'btlib' written by Marcus J. Ranum -** -** Copyright (c) 2001-2004 pinc Software. All Rights Reserved. -** This file may be used under the terms of the OpenBeOS License. -*/ + * + * Roughly based on 'btlib' written by Marcus J. Ranum - it shares + * no code but achieves binary compatibility with the on disk format. + * + * Copyright 2001-2004, Axel Dörfler, axeld@pinc-software.de. + * This file may be used under the terms of the MIT License. + */ #include "Debug.h" @@ -43,6 +43,7 @@ class NodeChecker { }; #endif + // Node Caching for the BPlusTree class // // With write support, there is the need for a function that allocates new @@ -55,16 +56,30 @@ class NodeChecker { // Since BFS supports block sizes of 1024 bytes or greater, and the node size // is hard-coded to 1024 bytes, that's not an issue now. +void +CachedNode::UnsetUnchanged(Transaction &transaction) +{ + if (fTree == NULL || fTree->fStream == NULL) + return; + + if (fNode != NULL) { + void *cache = fTree->fStream->GetVolume()->BlockCache(); + + block_cache_set_dirty(cache, fBlockNumber, false, transaction.ID()); + block_cache_put(cache, fBlockNumber); + fNode = NULL; + } +} + + void CachedNode::Unset() { if (fTree == NULL || fTree->fStream == NULL) return; - if (fBlock != NULL) { - release_block(fTree->fStream->GetVolume()->Device(), fBlockNumber); - - fBlock = NULL; + if (fNode != NULL) { + block_cache_put(fTree->fStream->GetVolume()->BlockCache(), fBlockNumber); fNode = NULL; } } @@ -88,14 +103,9 @@ CachedNode::SetTo(off_t offset, bool check) || (offset % fTree->fNodeSize) != 0) return NULL; - if (InternalSetTo(offset) != NULL && check) { + if (InternalSetTo(NULL, offset) != NULL && check) { // sanity checks (links, all_key_count) - bplustree_header *header = fTree->fHeader; - if (!header->IsValidLink(fNode->LeftLink()) - || !header->IsValidLink(fNode->RightLink()) - || !header->IsValidLink(fNode->OverflowLink()) - || (int8 *)fNode->Values() + fNode->NumKeys() * sizeof(off_t) > - (int8 *)fNode + fTree->fNodeSize) { + if (!fTree->fHeader->CheckNode(fNode)) { FATAL(("invalid node read from offset %Ld, inode at %Ld\n", offset, fTree->fStream->ID())); return NULL; @@ -105,6 +115,47 @@ CachedNode::SetTo(off_t offset, bool check) } +bplustree_node * +CachedNode::SetToWritable(Transaction &transaction, off_t offset, bool check) +{ + if (fTree == NULL || fTree->fStream == NULL) { + REPORT_ERROR(B_BAD_VALUE); + return NULL; + } + + Unset(); + + // You can only ask for nodes at valid positions - you can't + // even access the b+tree header with this method (use SetToHeader() + // instead) + if (offset > fTree->fHeader->MaximumSize() - fTree->fNodeSize + || offset <= 0 + || (offset % fTree->fNodeSize) != 0) + return NULL; + + if (InternalSetTo(&transaction, offset) != NULL && check) { + // sanity checks (links, all_key_count) + if (!fTree->fHeader->CheckNode(fNode)) { + FATAL(("invalid node read from offset %Ld, inode at %Ld\n", + offset, fTree->fStream->ID())); + return NULL; + } + } + return fNode; +} + + +status_t +CachedNode::MakeWritable(Transaction &transaction) +{ + if (fNode == NULL) + return B_NO_INIT; + + return block_cache_make_writable(transaction.GetVolume()->BlockCache(), + fBlockNumber, transaction.ID()); +} + + bplustree_header * CachedNode::SetToHeader() { @@ -114,16 +165,32 @@ CachedNode::SetToHeader() } Unset(); - - InternalSetTo(0LL); + + InternalSetTo(NULL, 0LL); + return (bplustree_header *)fNode; +} + + +bplustree_header * +CachedNode::SetToWritableHeader(Transaction &transaction) +{ + if (fTree == NULL || fTree->fStream == NULL) { + REPORT_ERROR(B_BAD_VALUE); + return NULL; + } + + Unset(); + + InternalSetTo(&transaction, 0LL); return (bplustree_header *)fNode; } bplustree_node * -CachedNode::InternalSetTo(off_t offset) +CachedNode::InternalSetTo(Transaction *transaction, off_t offset) { fNode = NULL; + fOffset = offset; off_t fileOffset; block_run run; @@ -133,12 +200,20 @@ CachedNode::InternalSetTo(off_t offset) int32 blockOffset = (offset - fileOffset) / volume->BlockSize(); fBlockNumber = volume->ToBlock(run) + blockOffset; + uint8 *block; - fBlock = (uint8 *)get_block(volume->Device(), fBlockNumber, volume->BlockSize()); - if (fBlock) { + if (transaction != NULL) { + block = (uint8 *)block_cache_get_writable(volume->BlockCache(), fBlockNumber, transaction->ID()); + fWritable = true; + } else { + block = (uint8 *)block_cache_get(volume->BlockCache(), fBlockNumber); + fWritable = false; + } + + if (block) { // the node is somewhere in that block... (confusing offset calculation) - fNode = (bplustree_node *)(fBlock + offset - - (fileOffset + blockOffset * volume->BlockSize())); + fNode = (bplustree_node *)(block + offset - + (fileOffset + (blockOffset << volume->BlockShift()))); } else REPORT_ERROR(B_IO_ERROR); } @@ -147,10 +222,9 @@ CachedNode::InternalSetTo(off_t offset) status_t -CachedNode::Free(Transaction *transaction, off_t offset) +CachedNode::Free(Transaction &transaction, off_t offset) { - if (transaction == NULL || fTree == NULL || fTree->fStream == NULL - || offset == BPLUSTREE_NULL) + if (fTree == NULL || fTree->fStream == NULL || offset == BPLUSTREE_NULL) RETURN_ERROR(B_BAD_VALUE); // ToDo: scan the free nodes list and remove all nodes at the end @@ -158,91 +232,80 @@ CachedNode::Free(Transaction *transaction, off_t offset) // function is called, perhaps it should be done when the directory // inode is closed or based on some calculation or whatever... + bplustree_header *header = fTree->fHeader; + if (fTree->fCachedHeader.MakeWritable(transaction) != B_OK) + return B_IO_ERROR; + // if the node is the last one in the tree, we shrink // the tree and file size by one node - off_t lastOffset = fTree->fHeader->MaximumSize() - fTree->fNodeSize; + off_t lastOffset = header->MaximumSize() - fTree->fNodeSize; if (offset == lastOffset) { - fTree->fHeader->maximum_size = HOST_ENDIAN_TO_BFS_INT64(lastOffset); - - status_t status = fTree->fStream->SetFileSize(transaction, lastOffset); - if (status < B_OK) - return status; - - return fTree->fCachedHeader.WriteBack(transaction); + header->maximum_size = HOST_ENDIAN_TO_BFS_INT64(lastOffset); + return fTree->fStream->SetFileSize(transaction, lastOffset); } // add the node to the free nodes list - fNode->left_link = fTree->fHeader->free_node_pointer; + fNode->left_link = header->free_node_pointer; fNode->overflow_link = HOST_ENDIAN_TO_BFS_INT64((uint64)BPLUSTREE_FREE); - if (WriteBack(transaction) == B_OK) { - fTree->fHeader->free_node_pointer = HOST_ENDIAN_TO_BFS_INT64(offset); - return fTree->fCachedHeader.WriteBack(transaction); - } - return B_ERROR; + header->free_node_pointer = HOST_ENDIAN_TO_BFS_INT64(offset); + return B_OK; } status_t -CachedNode::Allocate(Transaction *transaction, bplustree_node **_node, off_t *_offset) +CachedNode::Allocate(Transaction &transaction, bplustree_node **_node, off_t *_offset) { - if (transaction == NULL || fTree == NULL || fTree->fHeader == NULL - || fTree->fStream == NULL) { + if (fTree == NULL || fTree->fHeader == NULL || fTree->fStream == NULL) RETURN_ERROR(B_BAD_VALUE); - } + Unset(); + + bplustree_header *header = fTree->fHeader; status_t status; // if there are any free nodes, recycle them - if (SetTo(fTree->fHeader->FreeNode(), false) != NULL) { - *_offset = fTree->fHeader->FreeNode(); - + if (SetToWritable(transaction, header->FreeNode(), false) != NULL) { + if (fTree->fCachedHeader.MakeWritable(transaction) != B_OK) + return B_IO_ERROR; + // set new free node pointer - fTree->fHeader->free_node_pointer = fNode->left_link; - if ((status = fTree->fCachedHeader.WriteBack(transaction)) == B_OK) { - fNode->Initialize(); - *_node = fNode; - return B_OK; - } - return status; + header->free_node_pointer = fNode->left_link; + fNode->Initialize(); + + *_offset = header->FreeNode(); + *_node = fNode; + return B_OK; } + // allocate space for a new node Inode *stream = fTree->fStream; if ((status = stream->Append(transaction, fTree->fNodeSize)) < B_OK) return status; + if (fTree->fCachedHeader.MakeWritable(transaction) != B_OK) + return B_IO_ERROR; + // the maximum_size has to be changed before the call to SetTo() - or // else it will fail because the requested node is out of bounds - off_t offset = fTree->fHeader->MaximumSize(); - fTree->fHeader->maximum_size = HOST_ENDIAN_TO_BFS_INT64(fTree->fHeader->MaximumSize() + fTree->fNodeSize); + off_t offset = header->MaximumSize(); + header->maximum_size = HOST_ENDIAN_TO_BFS_INT64(header->MaximumSize() + fTree->fNodeSize); + + if (SetToWritable(transaction, offset, false) != NULL) { + fNode->Initialize(); - if (SetTo(offset, false) != NULL) { *_offset = offset; - - if (fTree->fCachedHeader.WriteBack(transaction) >= B_OK) { - fNode->Initialize(); - *_node = fNode; - return B_OK; - } + *_node = fNode; + return B_OK; } RETURN_ERROR(B_ERROR); } -status_t -CachedNode::WriteBack(Transaction *transaction) -{ - if (transaction == NULL || fTree == NULL || fTree->fStream == NULL || fNode == NULL) - RETURN_ERROR(B_BAD_VALUE); - - return transaction->WriteBlocks(fBlockNumber, fBlock); -} - - // #pragma mark - -BPlusTree::BPlusTree(Transaction *transaction, Inode *stream, int32 nodeSize) +BPlusTree::BPlusTree(Transaction &transaction, Inode *stream, int32 nodeSize) : fStream(NULL), fHeader(NULL), @@ -293,21 +356,20 @@ BPlusTree::~BPlusTree() /** Create a new B+Tree on the specified stream */ status_t -BPlusTree::SetTo(Transaction *transaction, Inode *stream, int32 nodeSize) +BPlusTree::SetTo(Transaction &transaction, Inode *stream, int32 nodeSize) { // initializes in-memory B+Tree - fCachedHeader.Unset(); fStream = stream; - fHeader = fCachedHeader.SetToHeader(); + fHeader = fCachedHeader.SetToWritableHeader(transaction); if (fHeader == NULL) { // allocate space for new header + node! fStatus = stream->SetFileSize(transaction, nodeSize * 2); if (fStatus < B_OK) RETURN_ERROR(fStatus); - - fHeader = fCachedHeader.SetToHeader(); + + fHeader = fCachedHeader.SetToWritableHeader(transaction); if (fHeader == NULL) RETURN_ERROR(fStatus = B_ERROR); } @@ -327,23 +389,22 @@ BPlusTree::SetTo(Transaction *transaction, Inode *stream, int32 nodeSize) fHeader->free_node_pointer = HOST_ENDIAN_TO_BFS_INT64((uint64)BPLUSTREE_NULL); fHeader->maximum_size = HOST_ENDIAN_TO_BFS_INT64(nodeSize * 2); - if (fCachedHeader.WriteBack(transaction) < B_OK) - RETURN_ERROR(fStatus = B_ERROR); - // initialize b+tree root node - CachedNode cached(this, fHeader->RootNode(), false); + CachedNode cached(this); + cached.SetToWritable(transaction, fHeader->RootNode(), false); if (cached.Node() == NULL) - RETURN_ERROR(B_ERROR); + RETURN_ERROR(B_IO_ERROR); cached.Node()->Initialize(); - return fStatus = cached.WriteBack(transaction); + + return fStatus = B_OK; } status_t BPlusTree::SetTo(Inode *stream) { - if (stream == NULL || stream->Node() == NULL) + if (stream == NULL) RETURN_ERROR(fStatus = B_BAD_VALUE); // get on-disk B+Tree header @@ -620,9 +681,13 @@ BPlusTree::SeekDown(Stack &stack, const uint8 *key, uint16 keyLeng } +/** This will find a free duplicate fragment in the given bplustree_node. + * The CachedNode will be set to the readable fragment on success. + */ + status_t -BPlusTree::FindFreeDuplicateFragment(bplustree_node *node, CachedNode *cached, off_t *_offset, - bplustree_node **_fragment, uint32 *_index) +BPlusTree::FindFreeDuplicateFragment(bplustree_node *node, CachedNode &cached, + off_t *_offset, bplustree_node **_fragment, uint32 *_index) { off_t *values = node->Values(); for (int32 i = 0; i < node->NumKeys(); i++) { @@ -630,12 +695,12 @@ BPlusTree::FindFreeDuplicateFragment(bplustree_node *node, CachedNode *cached, o if (bplustree_node::LinkType(values[i]) != BPLUSTREE_DUPLICATE_FRAGMENT) continue; - bplustree_node *fragment = cached->SetTo(bplustree_node::FragmentOffset(values[i]), false); + bplustree_node *fragment = cached.SetTo(bplustree_node::FragmentOffset(values[i]), false); if (fragment == NULL) { FATAL(("Could not get duplicate fragment at %Ld\n", values[i])); continue; } - + // see if there is some space left for us int32 num = (fNodeSize >> 3) / (NUM_FRAGMENT_VALUES + 1); for (int32 j = 0;j < num;j++) { @@ -654,7 +719,7 @@ BPlusTree::FindFreeDuplicateFragment(bplustree_node *node, CachedNode *cached, o status_t -BPlusTree::InsertDuplicate(Transaction *transaction, CachedNode *cached, bplustree_node *node, +BPlusTree::InsertDuplicate(Transaction &transaction, CachedNode &cached, bplustree_node *node, uint16 index, off_t value) { CachedNode cachedDuplicate(this); @@ -669,7 +734,8 @@ BPlusTree::InsertDuplicate(Transaction *transaction, CachedNode *cached, bplustr // doesn't fit anymore, create a new duplicate node // if (bplustree_node::LinkType(oldValue) == BPLUSTREE_DUPLICATE_FRAGMENT) { - bplustree_node *duplicate = cachedDuplicate.SetTo(bplustree_node::FragmentOffset(oldValue), false); + bplustree_node *duplicate = cachedDuplicate.SetToWritable(transaction, + bplustree_node::FragmentOffset(oldValue), false); if (duplicate == NULL) return B_IO_ERROR; @@ -699,9 +765,12 @@ BPlusTree::InsertDuplicate(Transaction *transaction, CachedNode *cached, bplustr array->Insert(value); } else { // create a new duplicate node - CachedNode cachedNewDuplicate(this); + + cachedDuplicate.UnsetUnchanged(transaction); + // the old duplicate has not been touched, so we can reuse it + bplustree_node *newDuplicate; - status = cachedNewDuplicate.Allocate(transaction, &newDuplicate, &offset); + status = cachedDuplicate.Allocate(transaction, &newDuplicate, &offset); if (status < B_OK) return status; @@ -714,21 +783,16 @@ BPlusTree::InsertDuplicate(Transaction *transaction, CachedNode *cached, bplustr array = newDuplicate->DuplicateArray(); array->Insert(value); - - // if this fails, the old fragments node will contain wrong - // data... (but since it couldn't be written, it shouldn't - // be fatal) - if ((status = cachedNewDuplicate.WriteBack(transaction)) < B_OK) - return status; } // update the main pointer to link to a duplicate node + if (cached.MakeWritable(transaction) != B_OK) + return B_IO_ERROR; + values[index] = bplustree_node::MakeLink(BPLUSTREE_DUPLICATE_NODE, offset); - if ((status = cached->WriteBack(transaction)) < B_OK) - return status; } - return cachedDuplicate.WriteBack(transaction); + return B_OK; } // @@ -755,29 +819,27 @@ BPlusTree::InsertDuplicate(Transaction *transaction, CachedNode *cached, bplustr && (oldValue = duplicate->RightLink()) != BPLUSTREE_NULL); if (array->count < NUM_DUPLICATE_VALUES) { + cachedDuplicate.MakeWritable(transaction); + array = duplicate->DuplicateArray(); + array->Insert(value); } else { // no space left - add a new duplicate node - CachedNode cachedNewDuplicate(this); bplustree_node *newDuplicate; - status = cachedNewDuplicate.Allocate(transaction, &newDuplicate, &offset); + status = cachedDuplicate.Allocate(transaction, &newDuplicate, &offset); if (status < B_OK) return status; // link the two nodes together duplicate->right_link = HOST_ENDIAN_TO_BFS_INT64(offset); newDuplicate->left_link = HOST_ENDIAN_TO_BFS_INT64(duplicateOffset); - + array = newDuplicate->DuplicateArray(); array->count = 0; array->Insert(value); - - status = cachedNewDuplicate.WriteBack(transaction); - if (status < B_OK) - return status; } - return cachedDuplicate.WriteBack(transaction); + return B_OK; } // @@ -787,23 +849,27 @@ BPlusTree::InsertDuplicate(Transaction *transaction, CachedNode *cached, bplustr uint32 fragmentIndex = 0; bplustree_node *fragment; - if (FindFreeDuplicateFragment(node, &cachedDuplicate, &offset, &fragment, &fragmentIndex) < B_OK) { + if (FindFreeDuplicateFragment(node, cachedDuplicate, &offset, &fragment, &fragmentIndex) == B_OK) { + if (cachedDuplicate.MakeWritable(transaction) != B_OK) + return B_IO_ERROR; + } else { // allocate a new duplicate fragment node if ((status = cachedDuplicate.Allocate(transaction, &fragment, &offset)) < B_OK) return status; memset(fragment, 0, fNodeSize); } + duplicate_array *array = fragment->FragmentAt(fragmentIndex); array->Insert(oldValue); array->Insert(value); - if ((status = cachedDuplicate.WriteBack(transaction)) < B_OK) - return status; + if (cached.MakeWritable(transaction) != B_OK) + return B_IO_ERROR; values[index] = bplustree_node::MakeLink(BPLUSTREE_DUPLICATE_FRAGMENT, offset, fragmentIndex); - return cached->WriteBack(transaction); + return B_OK; } @@ -1070,8 +1136,12 @@ BPlusTree::SplitNode(bplustree_node *node, off_t nodeOffset, bplustree_node *oth } +/** This inserts a key into the tree. The changes made to the tree will + * all be part of the \a transaction. + */ + status_t -BPlusTree::Insert(Transaction *transaction, const uint8 *key, uint16 keyLength, off_t value) +BPlusTree::Insert(Transaction &transaction, const uint8 *key, uint16 keyLength, off_t value) { if (keyLength < BPLUSTREE_MIN_KEY_LENGTH || keyLength > BPLUSTREE_MAX_KEY_LENGTH) RETURN_ERROR(B_BAD_VALUE); @@ -1103,12 +1173,15 @@ BPlusTree::Insert(Transaction *transaction, const uint8 *key, uint16 keyLength, // is this a duplicate entry? if (status == B_OK) { if (fAllowDuplicates) - return InsertDuplicate(transaction, &cached, node, nodeAndKey.keyIndex, value); + return InsertDuplicate(transaction, cached, node, nodeAndKey.keyIndex, value); RETURN_ERROR(B_NAME_IN_USE); } } + if (cached.MakeWritable(transaction) != B_OK) + return B_IO_ERROR; + // is the node big enough to hold the pair? if (int32(round_up(sizeof(bplustree_node) + node->AllKeyLength() + keyLength) + (node->NumKeys() + 1) * (sizeof(uint16) + sizeof(off_t))) < fNodeSize) @@ -1116,7 +1189,7 @@ BPlusTree::Insert(Transaction *transaction, const uint8 *key, uint16 keyLength, InsertKey(node, nodeAndKey.keyIndex, keyBuffer, keyLength, value); UpdateIterators(nodeAndKey.nodeOffset, BPLUSTREE_NULL, nodeAndKey.keyIndex, 0, 1); - return cached.WriteBack(transaction); + return B_OK; } else { CachedNode cachedNewRoot(this); CachedNode cachedOther(this); @@ -1156,20 +1229,12 @@ BPlusTree::Insert(Transaction *transaction, const uint8 *key, uint16 keyLength, RETURN_ERROR(B_ERROR); } - // write the updated nodes back - - if (cached.WriteBack(transaction) < B_OK - || cachedOther.WriteBack(transaction) < B_OK) - RETURN_ERROR(B_ERROR); - UpdateIterators(nodeAndKey.nodeOffset, otherOffset, nodeAndKey.keyIndex, node->NumKeys(), 1); // update the right link of the node in the left of the new node - if ((other = cachedOther.SetTo(other->LeftLink())) != NULL) { + if ((other = cachedOther.SetToWritable(transaction, other->LeftLink())) != NULL) { other->right_link = HOST_ENDIAN_TO_BFS_INT64(otherOffset); - if (cachedOther.WriteBack(transaction) < B_OK) - RETURN_ERROR(B_ERROR); } // create a new root if necessary @@ -1179,14 +1244,15 @@ BPlusTree::Insert(Transaction *transaction, const uint8 *key, uint16 keyLength, InsertKey(root, 0, keyBuffer, keyLength, node->LeftLink()); root->overflow_link = HOST_ENDIAN_TO_BFS_INT64(nodeAndKey.nodeOffset); - if (cachedNewRoot.WriteBack(transaction) < B_OK) - RETURN_ERROR(B_ERROR); + bplustree_header *header = fCachedHeader.SetToWritableHeader(transaction); + if (header == NULL) + return B_IO_ERROR; // finally, update header to point to the new root - fHeader->root_node_pointer = HOST_ENDIAN_TO_BFS_INT64(newRoot); - fHeader->max_number_of_levels = HOST_ENDIAN_TO_BFS_INT32(fHeader->MaxNumberOfLevels() + 1); + header->root_node_pointer = HOST_ENDIAN_TO_BFS_INT64(newRoot); + header->max_number_of_levels = HOST_ENDIAN_TO_BFS_INT32(header->MaxNumberOfLevels() + 1); - return fCachedHeader.WriteBack(transaction); + return B_OK; } } } @@ -1194,17 +1260,20 @@ BPlusTree::Insert(Transaction *transaction, const uint8 *key, uint16 keyLength, } +/** Removes the duplicate index/value pair from the tree. + * It's part of the private tree interface. + */ + status_t -BPlusTree::RemoveDuplicate(Transaction *transaction, bplustree_node *node, CachedNode *cached, +BPlusTree::RemoveDuplicate(Transaction &transaction, bplustree_node *node, CachedNode &cached, uint16 index, off_t value) { - CachedNode cachedDuplicate(this); off_t *values = node->Values(); off_t oldValue = values[index]; - status_t status; + CachedNode cachedDuplicate(this); off_t duplicateOffset = bplustree_node::FragmentOffset(oldValue); - bplustree_node *duplicate = cachedDuplicate.SetTo(duplicateOffset, false); + bplustree_node *duplicate = cachedDuplicate.SetToWritable(transaction, duplicateOffset, false); if (duplicate == NULL) return B_IO_ERROR; @@ -1225,22 +1294,21 @@ BPlusTree::RemoveDuplicate(Transaction *transaction, bplustree_node *node, Cache // remove the array from the fragment node if it is empty if (array->count == 1) { // set the link to the remaining value + if (cached.MakeWritable(transaction) != B_OK) + return B_IO_ERROR; + values[index] = array->values[0]; // Remove the whole fragment node, if this was the only array, // otherwise free the array and write the changes back - if (duplicate->FragmentsUsed(fNodeSize) == 1) - status = cachedDuplicate.Free(transaction, duplicateOffset); - else { + if (duplicate->FragmentsUsed(fNodeSize) == 1) { + status_t status = cachedDuplicate.Free(transaction, duplicateOffset); + if (status < B_OK) + return status; + } else array->count = 0; - status = cachedDuplicate.WriteBack(transaction); - } - if (status < B_OK) - return status; - - return cached->WriteBack(transaction); } - return cachedDuplicate.WriteBack(transaction); + return B_OK; } // @@ -1269,8 +1337,9 @@ BPlusTree::RemoveDuplicate(Transaction *transaction, bplustree_node *node, Cache if ((duplicateOffset = duplicate->RightLink()) == BPLUSTREE_NULL) RETURN_ERROR(B_ENTRY_NOT_FOUND); - - duplicate = cachedDuplicate.SetTo(duplicateOffset, false); + + cachedDuplicate.UnsetUnchanged(transaction); + duplicate = cachedDuplicate.SetToWritable(transaction, duplicateOffset, false); } if (duplicate == NULL) RETURN_ERROR(B_IO_ERROR); @@ -1287,22 +1356,23 @@ BPlusTree::RemoveDuplicate(Transaction *transaction, bplustree_node *node, Cache if (duplicateOffset == bplustree_node::FragmentOffset(oldValue) || array->count == 1) { + if (cached.MakeWritable(transaction) != B_OK) + return B_IO_ERROR; + if (array->count == 1 && isLast) values[index] = array->values[0]; else if (isLast) { FATAL(("removed last value from duplicate!\n")); } else values[index] = bplustree_node::MakeLink(BPLUSTREE_DUPLICATE_NODE, right); - - if ((status = cached->WriteBack(transaction)) < B_OK) - return status; } + status_t status; if ((status = cachedDuplicate.Free(transaction, duplicateOffset)) < B_OK) return status; if (left != BPLUSTREE_NULL - && (duplicate = cachedDuplicate.SetTo(left, false)) != NULL) { + && (duplicate = cachedDuplicate.SetToWritable(transaction, left, false)) != NULL) { duplicate->right_link = HOST_ENDIAN_TO_BFS_INT64(right); // If the next node is the last node, we need to free that node @@ -1312,13 +1382,9 @@ BPlusTree::RemoveDuplicate(Transaction *transaction, bplustree_node *node, Cache duplicateOffset = left; continue; } - - status = cachedDuplicate.WriteBack(transaction); - if (status < B_OK) - return status; } if (right != BPLUSTREE_NULL - && (duplicate = cachedDuplicate.SetTo(right, false)) != NULL) { + && (duplicate = cachedDuplicate.SetToWritable(transaction, right, false)) != NULL) { duplicate->left_link = HOST_ENDIAN_TO_BFS_INT64(left); // Again, we may need to turn the duplicate entry back into a normal entry @@ -1328,43 +1394,41 @@ BPlusTree::RemoveDuplicate(Transaction *transaction, bplustree_node *node, Cache duplicateOffset = right; continue; } - - return cachedDuplicate.WriteBack(transaction); } - return status; + return B_OK; } else if (isLast && array->count <= NUM_FRAGMENT_VALUES) { // If the number of entries fits in a duplicate fragment, then // either find a free fragment node, or convert this node to a // fragment node. CachedNode cachedOther(this); - + bplustree_node *fragment = NULL; uint32 fragmentIndex = 0; off_t offset; - if (FindFreeDuplicateFragment(node, &cachedOther, &offset, - &fragment, &fragmentIndex) < B_OK) { - // convert node - memmove(duplicate, array, (NUM_FRAGMENT_VALUES + 1) * sizeof(off_t)); - memset((off_t *)duplicate + NUM_FRAGMENT_VALUES + 1, 0, - fNodeSize - (NUM_FRAGMENT_VALUES + 1) * sizeof(off_t)); - } else { + if (FindFreeDuplicateFragment(node, cachedOther, &offset, &fragment, &fragmentIndex) == B_OK) { // move to other node + if (cachedOther.MakeWritable(transaction) != B_OK) + return B_IO_ERROR; + duplicate_array *target = fragment->FragmentAt(fragmentIndex); memcpy(target, array, (NUM_FRAGMENT_VALUES + 1) * sizeof(off_t)); cachedDuplicate.Free(transaction, duplicateOffset); duplicateOffset = offset; + } else { + // convert node + memmove(duplicate, array, (NUM_FRAGMENT_VALUES + 1) * sizeof(off_t)); + memset((off_t *)duplicate + NUM_FRAGMENT_VALUES + 1, 0, + fNodeSize - (NUM_FRAGMENT_VALUES + 1) * sizeof(off_t)); } + + if (cached.MakeWritable(transaction) != B_OK) + return B_IO_ERROR; + values[index] = bplustree_node::MakeLink(BPLUSTREE_DUPLICATE_FRAGMENT, duplicateOffset, fragmentIndex); - - if ((status = cached->WriteBack(transaction)) < B_OK) - return status; - - if (fragment != NULL) - return cachedOther.WriteBack(transaction); } - return cachedDuplicate.WriteBack(transaction); + return B_OK; } } @@ -1434,7 +1498,7 @@ BPlusTree::RemoveKey(bplustree_node *node, uint16 index) */ status_t -BPlusTree::Remove(Transaction *transaction, const uint8 *key, uint16 keyLength, off_t value) +BPlusTree::Remove(Transaction &transaction, const uint8 *key, uint16 keyLength, off_t value) { if (keyLength < BPLUSTREE_MIN_KEY_LENGTH || keyLength > BPLUSTREE_MAX_KEY_LENGTH) RETURN_ERROR(B_BAD_VALUE); @@ -1471,12 +1535,17 @@ BPlusTree::Remove(Transaction *transaction, const uint8 *key, uint16 keyLength, // is this a duplicate entry? if (bplustree_node::IsDuplicate(node->Values()[nodeAndKey.keyIndex])) { if (fAllowDuplicates) - return RemoveDuplicate(transaction, node, &cached, nodeAndKey.keyIndex, value); - else - RETURN_ERROR(B_NAME_IN_USE); + return RemoveDuplicate(transaction, node, cached, nodeAndKey.keyIndex, value); + else { + FATAL(("dupliate node found where no duplicates are allowed!\n")); + RETURN_ERROR(B_ERROR); + } } } + if (cached.MakeWritable(transaction) != B_OK) + return B_IO_ERROR; + // if it's an empty root node, we have to convert it // to a leaf node by dropping the overflow link, or, // if it's a leaf node, just empty it @@ -1487,14 +1556,13 @@ BPlusTree::Remove(Transaction *transaction, const uint8 *key, uint16 keyLength, node->all_key_count = 0; node->all_key_length = 0; - if (cached.WriteBack(transaction) < B_OK) - return B_IO_ERROR; - // if we've cleared the root node, reset the maximum // number of levels in the header if (nodeAndKey.nodeOffset == fHeader->RootNode()) { + if (fCachedHeader.MakeWritable(transaction) != B_OK) + return B_IO_ERROR; + fHeader->max_number_of_levels = HOST_ENDIAN_TO_BFS_INT32(1); - return fCachedHeader.WriteBack(transaction); } return B_OK; } @@ -1505,25 +1573,19 @@ BPlusTree::Remove(Transaction *transaction, const uint8 *key, uint16 keyLength, if (node->NumKeys() > 1 || !node->IsLeaf() && node->NumKeys() == 1) { RemoveKey(node, nodeAndKey.keyIndex); - return cached.WriteBack(transaction); + return B_OK; } // when we are here, we can just free the node, but // we have to update the right/left link of the // siblings first CachedNode otherCached(this); - bplustree_node *other = otherCached.SetTo(node->LeftLink()); - if (other != NULL) { + bplustree_node *other = otherCached.SetToWritable(transaction, node->LeftLink()); + if (other != NULL) other->right_link = node->right_link; - if (otherCached.WriteBack(transaction) < B_OK) - return B_IO_ERROR; - } - if ((other = otherCached.SetTo(node->RightLink())) != NULL) { + if ((other = otherCached.SetToWritable(transaction, node->RightLink())) != NULL) other->left_link = node->left_link; - if (otherCached.WriteBack(transaction) < B_OK) - return B_IO_ERROR; - } cached.Free(transaction, nodeAndKey.nodeOffset); } @@ -1541,7 +1603,7 @@ BPlusTree::Remove(Transaction *transaction, const uint8 *key, uint16 keyLength, */ status_t -BPlusTree::Replace(Transaction *transaction, const uint8 *key, uint16 keyLength, off_t value) +BPlusTree::Replace(Transaction &transaction, const uint8 *key, uint16 keyLength, off_t value) { if (keyLength < BPLUSTREE_MIN_KEY_LENGTH || keyLength > BPLUSTREE_MAX_KEY_LENGTH || key == NULL) @@ -1564,8 +1626,9 @@ BPlusTree::Replace(Transaction *transaction, const uint8 *key, uint16 keyLength, if (node->OverflowLink() == BPLUSTREE_NULL) { if (status == B_OK) { - node->Values()[keyIndex] = value; - return cached.WriteBack(transaction); + status = cached.MakeWritable(transaction); + if (status == B_OK) + node->Values()[keyIndex] = value; } return status; diff --git a/src/add-ons/kernel/file_systems/bfs/BPlusTree.h b/src/add-ons/kernel/file_systems/bfs/BPlusTree.h index 2903c03968..8306d52568 100644 --- a/src/add-ons/kernel/file_systems/bfs/BPlusTree.h +++ b/src/add-ons/kernel/file_systems/bfs/BPlusTree.h @@ -1,13 +1,13 @@ +/* BPlusTree - BFS B+Tree implementation + * + * Roughly based on 'btlib' written by Marcus J. Ranum - it shares + * no code but achieves binary compatibility with the on disk format. + * + * Copyright 2001-2004, Axel Dörfler, axeld@pinc-software.de. + * This file may be used under the terms of the MIT License. + */ #ifndef B_PLUS_TREE_H #define B_PLUS_TREE_H -/* BPlusTree - BFS B+Tree implementation -** -** Initial version by Axel Dörfler, axeld@pinc-software.de -** Roughly based on 'btlib' written by Marcus J. Ranum -** -** Copyright (c) 2001-2004 pinc Software. All Rights Reserved. -** This file may be used under the terms of the OpenBeOS License. -*/ #include "bfs.h" @@ -19,6 +19,8 @@ //****************** on-disk structures ******************** +struct bplustree_node; + #define BPLUSTREE_NULL -1LL #define BPLUSTREE_FREE -2LL @@ -39,6 +41,7 @@ struct bplustree_header { off_t MaximumSize() const { return BFS_ENDIAN_TO_HOST_INT64(maximum_size); } uint32 MaxNumberOfLevels() const { return BFS_ENDIAN_TO_HOST_INT32(max_number_of_levels); } + inline bool CheckNode(bplustree_node *node); inline bool IsValidLink(off_t link); } _PACKED; @@ -139,16 +142,14 @@ class CachedNode { CachedNode(BPlusTree *tree) : fTree(tree), - fNode(NULL), - fBlock(NULL) + fNode(NULL) { } CachedNode(BPlusTree *tree, off_t offset, bool check = true) : fTree(tree), - fNode(NULL), - fBlock(NULL) + fNode(NULL) { SetTo(offset, check); } @@ -159,22 +160,27 @@ class CachedNode { } bplustree_node *SetTo(off_t offset, bool check = true); + bplustree_node *SetToWritable(Transaction &transaction, off_t offset, bool check = true); + bplustree_header *SetToWritableHeader(Transaction &transaction); bplustree_header *SetToHeader(); + status_t MakeWritable(Transaction &transaction); + void UnsetUnchanged(Transaction &transaction); void Unset(); - status_t Free(Transaction *transaction, off_t offset); - status_t Allocate(Transaction *transaction, bplustree_node **node, off_t *offset); - status_t WriteBack(Transaction *transaction); + status_t Free(Transaction &transaction, off_t offset); + status_t Allocate(Transaction &transaction, bplustree_node **node, off_t *offset); + bool IsWritable() const { return fWritable; } bplustree_node *Node() const { return fNode; } protected: - bplustree_node *InternalSetTo(off_t offset); + bplustree_node *InternalSetTo(Transaction *transaction, off_t offset); BPlusTree *fTree; bplustree_node *fNode; - uint8 *fBlock; + off_t fOffset; off_t fBlockNumber; + bool fWritable; }; @@ -182,31 +188,31 @@ class CachedNode { class BPlusTree { public: - BPlusTree(Transaction *transaction, Inode *stream, int32 nodeSize = BPLUSTREE_NODE_SIZE); + BPlusTree(Transaction &transaction, Inode *stream, int32 nodeSize = BPLUSTREE_NODE_SIZE); BPlusTree(Inode *stream); BPlusTree(); ~BPlusTree(); - status_t SetTo(Transaction *transaction, Inode *stream, int32 nodeSize = BPLUSTREE_NODE_SIZE); + status_t SetTo(Transaction &transaction, Inode *stream, int32 nodeSize = BPLUSTREE_NODE_SIZE); status_t SetTo(Inode *stream); status_t SetStream(Inode *stream); status_t InitCheck(); status_t Validate(); - status_t Remove(Transaction *transaction, const uint8 *key, uint16 keyLength, off_t value); - status_t Insert(Transaction *transaction, const uint8 *key, uint16 keyLength, off_t value); + status_t Remove(Transaction &transaction, const uint8 *key, uint16 keyLength, off_t value); + status_t Insert(Transaction &transaction, const uint8 *key, uint16 keyLength, off_t value); - status_t Remove(Transaction *transaction, const char *key, off_t value); - status_t Insert(Transaction *transaction, const char *key, off_t value); - status_t Insert(Transaction *transaction, int32 key, off_t value); - status_t Insert(Transaction *transaction, uint32 key, off_t value); - status_t Insert(Transaction *transaction, int64 key, off_t value); - status_t Insert(Transaction *transaction, uint64 key, off_t value); - status_t Insert(Transaction *transaction, float key, off_t value); - status_t Insert(Transaction *transaction, double key, off_t value); + status_t Remove(Transaction &transaction, const char *key, off_t value); + status_t Insert(Transaction &transaction, const char *key, off_t value); + status_t Insert(Transaction &transaction, int32 key, off_t value); + status_t Insert(Transaction &transaction, uint32 key, off_t value); + status_t Insert(Transaction &transaction, int64 key, off_t value); + status_t Insert(Transaction &transaction, uint64 key, off_t value); + status_t Insert(Transaction &transaction, float key, off_t value); + status_t Insert(Transaction &transaction, double key, off_t value); - status_t Replace(Transaction *transaction, const uint8 *key, uint16 keyLength, off_t value); + status_t Replace(Transaction &transaction, const uint8 *key, uint16 keyLength, off_t value); status_t Find(const uint8 *key, uint16 keyLength, off_t *value); static int32 TypeCodeToKeyType(type_code code); @@ -222,9 +228,9 @@ class BPlusTree { uint16 *index = NULL, off_t *next = NULL); status_t SeekDown(Stack &stack, const uint8 *key, uint16 keyLength); - status_t FindFreeDuplicateFragment(bplustree_node *node, CachedNode *cached, + status_t FindFreeDuplicateFragment(bplustree_node *node, CachedNode &cached, off_t *_offset, bplustree_node **_fragment, uint32 *_index); - status_t InsertDuplicate(Transaction *transaction, CachedNode *cached, + status_t InsertDuplicate(Transaction &transaction, CachedNode &cached, bplustree_node *node, uint16 index, off_t value); void InsertKey(bplustree_node *node, uint16 index, uint8 *key, uint16 keyLength, off_t value); @@ -232,8 +238,8 @@ class BPlusTree { off_t otherOffset, uint16 *_keyIndex, uint8 *key, uint16 *_keyLength, off_t *_value); - status_t RemoveDuplicate(Transaction *transaction, bplustree_node *node, - CachedNode *cached, uint16 keyIndex, off_t value); + status_t RemoveDuplicate(Transaction &transaction, bplustree_node *node, + CachedNode &cached, uint16 keyIndex, off_t value); void RemoveKey(bplustree_node *node, uint16 index); void UpdateIterators(off_t offset, off_t nextOffset, uint16 keyIndex, @@ -303,7 +309,7 @@ class TreeIterator { // BPlusTree's inline functions (most of them may not be needed) inline status_t -BPlusTree::Remove(Transaction *transaction, const char *key, off_t value) +BPlusTree::Remove(Transaction &transaction, const char *key, off_t value) { if (fHeader->data_type != BPLUSTREE_STRING_TYPE) return B_BAD_TYPE; @@ -311,7 +317,7 @@ BPlusTree::Remove(Transaction *transaction, const char *key, off_t value) } inline status_t -BPlusTree::Insert(Transaction *transaction, const char *key, off_t value) +BPlusTree::Insert(Transaction &transaction, const char *key, off_t value) { if (fHeader->data_type != BPLUSTREE_STRING_TYPE) return B_BAD_TYPE; @@ -319,7 +325,7 @@ BPlusTree::Insert(Transaction *transaction, const char *key, off_t value) } inline status_t -BPlusTree::Insert(Transaction *transaction, int32 key, off_t value) +BPlusTree::Insert(Transaction &transaction, int32 key, off_t value) { if (fHeader->data_type != BPLUSTREE_INT32_TYPE) return B_BAD_TYPE; @@ -327,7 +333,7 @@ BPlusTree::Insert(Transaction *transaction, int32 key, off_t value) } inline status_t -BPlusTree::Insert(Transaction *transaction, uint32 key, off_t value) +BPlusTree::Insert(Transaction &transaction, uint32 key, off_t value) { if (fHeader->data_type != BPLUSTREE_UINT32_TYPE) return B_BAD_TYPE; @@ -335,7 +341,7 @@ BPlusTree::Insert(Transaction *transaction, uint32 key, off_t value) } inline status_t -BPlusTree::Insert(Transaction *transaction, int64 key, off_t value) +BPlusTree::Insert(Transaction &transaction, int64 key, off_t value) { if (fHeader->data_type != BPLUSTREE_INT64_TYPE) return B_BAD_TYPE; @@ -343,7 +349,7 @@ BPlusTree::Insert(Transaction *transaction, int64 key, off_t value) } inline status_t -BPlusTree::Insert(Transaction *transaction, uint64 key, off_t value) +BPlusTree::Insert(Transaction &transaction, uint64 key, off_t value) { if (fHeader->data_type != BPLUSTREE_UINT64_TYPE) return B_BAD_TYPE; @@ -351,7 +357,7 @@ BPlusTree::Insert(Transaction *transaction, uint64 key, off_t value) } inline status_t -BPlusTree::Insert(Transaction *transaction, float key, off_t value) +BPlusTree::Insert(Transaction &transaction, float key, off_t value) { if (fHeader->data_type != BPLUSTREE_FLOAT_TYPE) return B_BAD_TYPE; @@ -359,7 +365,7 @@ BPlusTree::Insert(Transaction *transaction, float key, off_t value) } inline status_t -BPlusTree::Insert(Transaction *transaction, double key, off_t value) +BPlusTree::Insert(Transaction &transaction, double key, off_t value) { if (fHeader->data_type != BPLUSTREE_DOUBLE_TYPE) return B_BAD_TYPE; @@ -394,6 +400,17 @@ TreeIterator::GetPreviousEntry(void *key, uint16 *keyLength, uint16 maxLength, // #pragma mark - +inline bool +bplustree_header::CheckNode(bplustree_node *node) +{ + // sanity checks (links, all_key_count) + return IsValidLink(node->LeftLink()) + && IsValidLink(node->RightLink()) + && IsValidLink(node->OverflowLink()) + && (int8 *)node->Values() + node->NumKeys() * sizeof(off_t) <= (int8 *)node + NodeSize(); +} + + inline bool bplustree_header::IsValidLink(off_t link) { diff --git a/src/add-ons/kernel/file_systems/bfs/BlockAllocator.cpp b/src/add-ons/kernel/file_systems/bfs/BlockAllocator.cpp index 6af62c984d..6eda4da0f7 100644 --- a/src/add-ons/kernel/file_systems/bfs/BlockAllocator.cpp +++ b/src/add-ons/kernel/file_systems/bfs/BlockAllocator.cpp @@ -1,8 +1,8 @@ /* BlockAllocator - block bitmap handling and allocation policies -** -** Initial version by Axel Dörfler, axeld@pinc-software.de -** This file may be used under the terms of the OpenBeOS License. -*/ + * + * Copyright 2001-2004, Axel Dörfler, axeld@pinc-software.de. + * This file may be used under the terms of the MIT License. + */ #include "Debug.h" @@ -62,12 +62,16 @@ class AllocationBlock : public CachedBlock { inline bool IsUsed(uint16 block); status_t SetTo(AllocationGroup &group, uint16 block); + status_t SetToWritable(Transaction &transaction, AllocationGroup &group, uint16 block); uint32 NumBlockBits() const { return fNumBits; } uint32 &Block(int32 index) { return ((uint32 *)fBlock)[index]; } private: - uint32 fNumBits; + uint32 fNumBits; +#ifdef DEBUG + bool fWritable; +#endif }; @@ -78,8 +82,8 @@ class AllocationGroup { void AddFreeRange(int32 start, int32 blocks); bool IsFull() const { return fFreeBits == 0; } - status_t Allocate(Transaction *transaction, uint16 start, int32 length); - status_t Free(Transaction *transaction, uint16 start, int32 length); + status_t Allocate(Transaction &transaction, uint16 start, int32 length); + status_t Free(Transaction &transaction, uint16 start, int32 length); uint32 fNumBits; int32 fStart; @@ -104,10 +108,29 @@ AllocationBlock::SetTo(AllocationGroup &group, uint16 block) if ((block + 1) * fNumBits > group.fNumBits) fNumBits = group.fNumBits % fNumBits; +#ifdef DEBUG + fWritable = false; +#endif return CachedBlock::SetTo(group.fStart + block) != NULL ? B_OK : B_ERROR; } +status_t +AllocationBlock::SetToWritable(Transaction &transaction, AllocationGroup &group, uint16 block) +{ + // 8 blocks per byte + fNumBits = fVolume->BlockSize() << 3; + // the last group may have less bits in the last block + if ((block + 1) * fNumBits > group.fNumBits) + fNumBits = group.fNumBits % fNumBits; + +#ifdef DEBUG + fWritable = true; +#endif + return CachedBlock::SetToWritable(transaction, group.fStart + block) != NULL ? B_OK : B_ERROR; +} + + bool AllocationBlock::IsUsed(uint16 block) { @@ -123,6 +146,9 @@ AllocationBlock::Allocate(uint16 start, uint16 numBlocks) { ASSERT(start < fNumBits); ASSERT(uint32(start + numBlocks) <= fNumBits); +#ifdef DEBUG + ASSERT(fWritable); +#endif if (uint32(start + numBlocks) > fNumBits) { FATAL(("Allocation::Allocate(): tried to allocate too many blocks: %u (numBlocks = %lu)!\n", numBlocks, fNumBits)); @@ -154,6 +180,9 @@ AllocationBlock::Free(uint16 start, uint16 numBlocks) { ASSERT(start < fNumBits); ASSERT(uint32(start + numBlocks) <= fNumBits); +#ifdef DEBUG + ASSERT(fWritable); +#endif if (uint32(start + numBlocks) > fNumBits) { FATAL(("Allocation::Free(): tried to free too many blocks: %u (numBlocks = %lu)!\n", numBlocks, fNumBits)); @@ -214,7 +243,7 @@ AllocationGroup::AddFreeRange(int32 start, int32 blocks) */ status_t -AllocationGroup::Allocate(Transaction *transaction, uint16 start, int32 length) +AllocationGroup::Allocate(Transaction &transaction, uint16 start, int32 length) { if (start > fNumBits) return B_ERROR; @@ -226,7 +255,7 @@ AllocationGroup::Allocate(Transaction *transaction, uint16 start, int32 length) fFirstFree = start + length; fFreeBits -= length; - Volume *volume = transaction->GetVolume(); + Volume *volume = transaction.GetVolume(); // calculate block in the block bitmap and position within uint32 bitsPerBlock = volume->BlockSize() << 3; @@ -236,7 +265,7 @@ AllocationGroup::Allocate(Transaction *transaction, uint16 start, int32 length) AllocationBlock cached(volume); while (length > 0) { - if (cached.SetTo(*this, block) < B_OK) + if (cached.SetToWritable(transaction, *this, block) < B_OK) RETURN_ERROR(B_IO_ERROR); uint32 numBlocks = length; @@ -245,9 +274,6 @@ AllocationGroup::Allocate(Transaction *transaction, uint16 start, int32 length) cached.Allocate(start, numBlocks); - if (cached.WriteBack(transaction) < B_OK) - return B_IO_ERROR; - length -= numBlocks; start = 0; block++; @@ -267,7 +293,7 @@ AllocationGroup::Allocate(Transaction *transaction, uint16 start, int32 length) */ status_t -AllocationGroup::Free(Transaction *transaction, uint16 start, int32 length) +AllocationGroup::Free(Transaction &transaction, uint16 start, int32 length) { if (start > fNumBits) return B_ERROR; @@ -278,7 +304,7 @@ AllocationGroup::Free(Transaction *transaction, uint16 start, int32 length) fFirstFree = start; fFreeBits += length; - Volume *volume = transaction->GetVolume(); + Volume *volume = transaction.GetVolume(); // calculate block in the block bitmap and position within uint32 bitsPerBlock = volume->BlockSize() << 3; @@ -288,7 +314,7 @@ AllocationGroup::Free(Transaction *transaction, uint16 start, int32 length) AllocationBlock cached(volume); while (length > 0) { - if (cached.SetTo(*this, block) < B_OK) + if (cached.SetToWritable(transaction, *this, block) < B_OK) RETURN_ERROR(B_IO_ERROR); uint16 freeLength = length; @@ -297,9 +323,6 @@ AllocationGroup::Free(Transaction *transaction, uint16 start, int32 length) cached.Free(start, freeLength); - if (cached.WriteBack(transaction) < B_OK) - return B_IO_ERROR; - length -= freeLength; start = 0; block++; @@ -363,6 +386,7 @@ BlockAllocator::InitializeAndClearBitmap(Transaction &transaction) uint32 blocks = fBlocksPerGroup; uint32 numBits = 8 * blocks * fVolume->BlockSize(); + uint32 blockShift = fVolume->BlockShift(); uint32 *buffer = (uint32 *)malloc(numBits >> 3); if (buffer == NULL) @@ -375,7 +399,7 @@ BlockAllocator::InitializeAndClearBitmap(Transaction &transaction) // initialize the AllocationGroup objects and clear the on-disk bitmap for (int32 i = 0; i < fNumGroups; i++) { - if (cached_write(fVolume->Device(), offset, buffer, blocks, fVolume->BlockSize()) < B_OK) + if (write_pos(fVolume->Device(), offset << blockShift, buffer, blocks << blockShift) < B_OK) return B_ERROR; // the last allocation group may contain less blocks than the others @@ -391,7 +415,7 @@ BlockAllocator::InitializeAndClearBitmap(Transaction &transaction) // reserve the boot block, the log area, and the block bitmap itself uint32 reservedBlocks = fVolume->Log().Start() + fVolume->Log().Length(); - if (fGroups[0].Allocate(&transaction, 0, reservedBlocks) < B_OK) { + if (fGroups[0].Allocate(transaction, 0, reservedBlocks) < B_OK) { FATAL(("could not allocate reserved space for block bitmap/log!\n")); return B_ERROR; } @@ -409,6 +433,7 @@ BlockAllocator::initialize(BlockAllocator *allocator) Volume *volume = allocator->fVolume; uint32 blocks = allocator->fBlocksPerGroup; uint32 numBits = 8 * blocks * volume->BlockSize(); + uint32 blockShift = volume->BlockShift(); off_t freeBlocks = 0; uint32 *buffer = (uint32 *)malloc(numBits >> 3); @@ -421,8 +446,8 @@ BlockAllocator::initialize(BlockAllocator *allocator) off_t offset = 1; int32 num = allocator->fNumGroups; - for (int32 i = 0;i < num;i++) { - if (cached_read(volume->Device(), offset, buffer, blocks, volume->BlockSize()) < B_OK) + for (int32 i = 0; i < num; i++) { + if (read_pos(volume->Device(), offset << blockShift, buffer, blocks << blockShift) < B_OK) break; // the last allocation group may contain less blocks than the others @@ -457,7 +482,7 @@ BlockAllocator::initialize(BlockAllocator *allocator) uint32 reservedBlocks = volume->Log().Start() + volume->Log().Length(); if (allocator->CheckBlockRun(block_run::Run(0, 0, reservedBlocks)) < B_OK) { Transaction transaction(volume, 0); - if (groups[0].Allocate(&transaction, 0, reservedBlocks) < B_OK) { + if (groups[0].Allocate(transaction, 0, reservedBlocks) < B_OK) { FATAL(("could not allocate reserved space for block bitmap/log!\n")); volume->Panic(); } else { @@ -481,7 +506,7 @@ BlockAllocator::initialize(BlockAllocator *allocator) status_t -BlockAllocator::AllocateBlocks(Transaction *transaction, int32 group, uint16 start, +BlockAllocator::AllocateBlocks(Transaction &transaction, int32 group, uint16 start, uint16 maximum, uint16 minimum, block_run &run) { if (maximum == 0) @@ -586,7 +611,7 @@ BlockAllocator::AllocateBlocks(Transaction *transaction, int32 group, uint16 sta status_t -BlockAllocator::AllocateForInode(Transaction *transaction, const block_run *parent, +BlockAllocator::AllocateForInode(Transaction &transaction, const block_run *parent, mode_t type, block_run &run) { // apply some allocation policies here (AllocateBlocks() will break them @@ -604,7 +629,7 @@ BlockAllocator::AllocateForInode(Transaction *transaction, const block_run *pare status_t -BlockAllocator::Allocate(Transaction *transaction, const Inode *inode, off_t numBlocks, +BlockAllocator::Allocate(Transaction &transaction, Inode *inode, off_t numBlocks, block_run &run, uint16 minimum) { if (numBlocks <= 0) @@ -633,19 +658,19 @@ BlockAllocator::Allocate(Transaction *transaction, const Inode *inode, off_t num // are there already allocated blocks? (then just try to allocate near the last one) if (inode->Size() > 0) { - data_stream *data = &inode->Node()->data; + const data_stream &data = inode->Node().data; // ToDo: we currently don't care for when the data stream // is already grown into the indirect ranges - if (data->max_double_indirect_range == 0 - && data->max_indirect_range == 0) { + if (data.max_double_indirect_range == 0 + && data.max_indirect_range == 0) { // Since size > 0, there must be a valid block run in this stream int32 last = 0; for (; last < NUM_DIRECT_BLOCKS - 1; last++) - if (data->direct[last + 1].IsZero()) + if (data.direct[last + 1].IsZero()) break; - group = data->direct[last].AllocationGroup(); - start = data->direct[last].Start() + data->direct[last].Length(); + group = data.direct[last].AllocationGroup(); + start = data.direct[last].Start() + data.direct[last].Length(); } } else if (inode->IsContainer() || inode->IsSymLink()) { // directory and symbolic link data will go in the same allocation @@ -661,7 +686,7 @@ BlockAllocator::Allocate(Transaction *transaction, const Inode *inode, off_t num status_t -BlockAllocator::Free(Transaction *transaction, block_run run) +BlockAllocator::Free(Transaction &transaction, block_run run) { Locker lock(fLock); @@ -955,8 +980,9 @@ BlockAllocator::CheckNextNode(check_control *control) // check if the inode's name is the same as in the b+tree if (inode->IsRegularNode()) { SimpleLocker locker(inode->SmallDataLock()); + NodeGetter node(fVolume, inode); - const char *localName = inode->Name(); + const char *localName = inode->Name(node.Node()); if (localName == NULL || strcmp(localName, name)) { control->errors |= BFS_NAMES_DONT_MATCH; FATAL(("Names differ: tree \"%s\", inode \"%s\"\n", name, localName)); @@ -986,8 +1012,8 @@ BlockAllocator::CheckNextNode(check_control *control) // the bitmap anyway Transaction transaction(fVolume, cookie->parent->BlockNumber()); - inode->Node()->flags |= INODE_DONT_FREE_SPACE; - status = cookie->parent->Remove(&transaction, name, NULL, inode->IsContainer()); + inode->Node().flags |= HOST_ENDIAN_TO_BFS_INT32(INODE_DONT_FREE_SPACE); + status = cookie->parent->Remove(transaction, name, NULL, inode->IsContainer()); if (status == B_OK) transaction.Done(); } else @@ -1140,7 +1166,7 @@ BlockAllocator::CheckInode(Inode *inode, check_control *control) if (status < B_OK) return status; - data_stream *data = &inode->Node()->data; + data_stream *data = &inode->Node().data; // check the direct range diff --git a/src/add-ons/kernel/file_systems/bfs/BlockAllocator.h b/src/add-ons/kernel/file_systems/bfs/BlockAllocator.h index deaea7a50e..a281fd3b94 100644 --- a/src/add-ons/kernel/file_systems/bfs/BlockAllocator.h +++ b/src/add-ons/kernel/file_systems/bfs/BlockAllocator.h @@ -1,10 +1,10 @@ +/* BlockAllocator - block bitmap handling and allocation policies + * + * Copyright 2001-2004, Axel Dörfler, axeld@pinc-software.de. + * This file may be used under the terms of the MIT License. + */ #ifndef BLOCK_ALLOCATOR_H #define BLOCK_ALLOCATOR_H -/* BlockAllocator - block bitmap handling and allocation policies -** -** Initial version by Axel Dörfler, axeld@pinc-software.de -** This file may be used under the terms of the OpenBeOS License. -*/ #include "Lock.h" @@ -28,13 +28,13 @@ class BlockAllocator { status_t Initialize(bool full = true); status_t InitializeAndClearBitmap(Transaction &transaction); - status_t AllocateForInode(Transaction *transaction, const block_run *parent, + status_t AllocateForInode(Transaction &transaction, const block_run *parent, mode_t type, block_run &run); - status_t Allocate(Transaction *transaction, const Inode *inode, off_t numBlocks, + status_t Allocate(Transaction &transaction, Inode *inode, off_t numBlocks, block_run &run, uint16 minimum = 1); - status_t Free(Transaction *transaction, block_run run); + status_t Free(Transaction &transaction, block_run run); - status_t AllocateBlocks(Transaction *transaction, int32 group, uint16 start, + status_t AllocateBlocks(Transaction &transaction, int32 group, uint16 start, uint16 numBlocks, uint16 minimum, block_run &run); status_t StartChecking(check_control *control); diff --git a/src/add-ons/kernel/file_systems/bfs/BufferPool.cpp b/src/add-ons/kernel/file_systems/bfs/BufferPool.cpp deleted file mode 100644 index 3d1a64f737..0000000000 --- a/src/add-ons/kernel/file_systems/bfs/BufferPool.cpp +++ /dev/null @@ -1,162 +0,0 @@ -/* BufferPool - a buffer pool for uncached file access -** -** Initial version by Axel Dörfler, axeld@pinc-software.de -** This file may be used under the terms of the OpenBeOS License. -*/ - - -#include "BufferPool.h" -#include "Debug.h" - -#include - - -const uint32 kNumBuffers = 8; - - -BufferPool::BufferPool() - : - fFirstFree(NULL) -{ - fLock = create_sem(1, "buffer lock"); - fFreeBuffers = create_sem(0, "free buffers"); - -#ifndef USER - set_sem_owner(fLock, B_SYSTEM_TEAM); - set_sem_owner(fFreeBuffers, B_SYSTEM_TEAM); -#endif -} - - -BufferPool::~BufferPool() -{ - delete_sem(fFreeBuffers); - - acquire_sem(fLock); - // the return value doesn't interest us anymore - - void **buffer = fFirstFree; - while (buffer != NULL) { - void **nextBuffer = (void **)*buffer; - free(buffer); - buffer = nextBuffer; - } - - delete_sem(fLock); -} - - -status_t -BufferPool::InitCheck() -{ - if (fLock < B_OK - || fFreeBuffers < B_OK) - return B_ERROR; - - return B_OK; -} - - -status_t -BufferPool::RequestBuffers(uint32 blockSize) -{ - void **buffers[kNumBuffers]; - - // allocate and connect buffers - - for (uint32 i = 0; i < kNumBuffers; i++) { - buffers[i] = (void **)malloc(blockSize); - if (buffers[i] == NULL) { - // free already allocated buffers - for (;i-- > 0; i++) - free(buffers[i]); - RETURN_ERROR(B_NO_MEMORY); - } - if (i > 0) - *(buffers[i]) = buffers[i - 1]; - } - - // add the buffers to the free buffers queue - - status_t status = acquire_sem(fLock); - if (status == B_OK) { - *(buffers[0]) = fFirstFree; - fFirstFree = buffers[kNumBuffers - 1]; - release_sem(fLock); - release_sem_etc(fFreeBuffers, kNumBuffers, B_DO_NOT_RESCHEDULE); - } else { - for (uint32 i = 0; i < kNumBuffers; i++) - free(buffers[i]); - } - - RETURN_ERROR(status); -} - - -status_t -BufferPool::ReleaseBuffers() -{ - status_t status = acquire_sem_etc(fFreeBuffers, kNumBuffers, 0, 0); - if (status < B_OK) - return status; - - status = acquire_sem(fLock); - if (status < B_OK) - return status; - - void **buffer = fFirstFree; - for (uint32 i = 0; i < kNumBuffers && buffer; i++) { - void **nextBuffer = (void **)*buffer; - - free(buffer); - buffer = nextBuffer; - } - fFirstFree = buffer; - - release_sem(fLock); - return B_OK; -} - - -status_t -BufferPool::GetBuffer(void **_buffer) -{ - status_t status = acquire_sem(fFreeBuffers); - if (status < B_OK) - return status; - - if ((status = acquire_sem(fLock)) < B_OK) { - release_sem(fFreeBuffers); - return status; - } - - void **buffer = fFirstFree; - fFirstFree = (void **)*buffer; - - release_sem(fLock); - - *_buffer = (void *)buffer; - return B_OK; -} - - -status_t -BufferPool::PutBuffer(void *_buffer) -{ - void **buffer = (void **)_buffer; - if (buffer == NULL) - return B_BAD_VALUE; - - status_t status = acquire_sem(fLock); - if (status < B_OK) - return status; - - *buffer = fFirstFree; - fFirstFree = buffer; - - release_sem(fLock); - release_sem(fFreeBuffers); - - return B_OK; -} - diff --git a/src/add-ons/kernel/file_systems/bfs/BufferPool.h b/src/add-ons/kernel/file_systems/bfs/BufferPool.h deleted file mode 100644 index 9e18ca27d2..0000000000 --- a/src/add-ons/kernel/file_systems/bfs/BufferPool.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef BUFFER_POOL_H -#define BUFFER_POOL_H -/* BufferPool - a buffer pool for uncached file access -** -** Initial version by Axel Dörfler, axeld@pinc-software.de -** This file may be used under the terms of the OpenBeOS License. -*/ - - -#include - - -class BufferPool { - public: - BufferPool(); - ~BufferPool(); - - status_t InitCheck(); - - status_t RequestBuffers(uint32 blockSize); - status_t ReleaseBuffers(); - - status_t GetBuffer(void **_buffer); - status_t PutBuffer(void *buffer); - - private: - sem_id fLock, fFreeBuffers; - void **fFirstFree; -}; - -#endif /* BUFFER_POOL_H */ diff --git a/src/add-ons/kernel/file_systems/bfs/CachedBlock.h b/src/add-ons/kernel/file_systems/bfs/CachedBlock.h new file mode 100644 index 0000000000..59f792ca06 --- /dev/null +++ b/src/add-ons/kernel/file_systems/bfs/CachedBlock.h @@ -0,0 +1,194 @@ +/* CachedBlock - interface for the block cache + * + * Copyright 2001-2004, Axel Dörfler, axeld@pinc-software.de. + * This file may be used under the terms of the MIT License. + */ +#ifndef CACHED_BLOCK_H +#define CACHED_BLOCK_H + + +#include +#ifdef USER +# include "myfs.h" +# include +#endif + +#include +#include + +#include "Volume.h" +#include "Journal.h" +#include "Lock.h" +#include "Chain.h" +#include "Debug.h" + + +// The CachedBlock class is completely implemented as inlines. +// It should be used when cache single blocks to make sure they +// will be properly released after use (and it's also very +// convenient to use them). + +class CachedBlock { + public: + CachedBlock(Volume *volume); + CachedBlock(Volume *volume, off_t block); + CachedBlock(Volume *volume, block_run run); + CachedBlock(CachedBlock *cached); + ~CachedBlock(); + + inline void Keep(); + inline void Unset(); + + inline const uint8 *SetTo(off_t block, off_t base, size_t length); + inline const uint8 *SetTo(off_t block); + inline const uint8 *SetTo(block_run run); + inline uint8 *SetToWritable(Transaction &transaction, off_t block, off_t base, size_t length, bool empty = false); + inline uint8 *SetToWritable(Transaction &transaction, off_t block, bool empty = false); + inline uint8 *SetToWritable(Transaction &transaction, block_run run, bool empty = false); + inline status_t MakeWritable(Transaction &transaction); + + const uint8 *Block() const { return fBlock; } + off_t BlockNumber() const { return fBlockNumber; } + uint32 BlockSize() const { return fVolume->BlockSize(); } + uint32 BlockShift() const { return fVolume->BlockShift(); } + + private: + CachedBlock(const CachedBlock &); + CachedBlock &operator=(const CachedBlock &); + // no implementation + + protected: + Volume *fVolume; + off_t fBlockNumber; + uint8 *fBlock; +}; + + +//-------------------------------------- +// inlines + + +inline +CachedBlock::CachedBlock(Volume *volume) + : + fVolume(volume), + fBlock(NULL) +{ +} + + +inline +CachedBlock::CachedBlock(Volume *volume, off_t block) + : + fVolume(volume), + fBlock(NULL) +{ + SetTo(block); +} + + +inline +CachedBlock::CachedBlock(Volume *volume, block_run run) + : + fVolume(volume), + fBlock(NULL) +{ + SetTo(volume->ToBlock(run)); +} + + +inline +CachedBlock::CachedBlock(CachedBlock *cached) + : + fVolume(cached->fVolume), + fBlockNumber(cached->BlockNumber()), + fBlock(cached->fBlock) +{ + cached->Keep(); +} + + +inline +CachedBlock::~CachedBlock() +{ + Unset(); +} + + +inline void +CachedBlock::Keep() +{ + fBlock = NULL; +} + + +inline void +CachedBlock::Unset() +{ + if (fBlock != NULL) + block_cache_put(fVolume->BlockCache(), fBlockNumber); +} + + +inline const uint8 * +CachedBlock::SetTo(off_t block, off_t base, size_t length) +{ + Unset(); + fBlockNumber = block; + return fBlock = (uint8 *)block_cache_get_etc(fVolume->BlockCache(), block, base, length); +} + + +inline const uint8 * +CachedBlock::SetTo(off_t block) +{ + return SetTo(block, block, 1); +} + + +inline const uint8 * +CachedBlock::SetTo(block_run run) +{ + return SetTo(fVolume->ToBlock(run)); +} + + +inline uint8 * +CachedBlock::SetToWritable(Transaction &transaction, off_t block, off_t base, size_t length, bool empty) +{ + Unset(); + fBlockNumber = block; + + if (empty) + return fBlock = (uint8 *)block_cache_get_empty(fVolume->BlockCache(), block, transaction.ID()); + + return fBlock = (uint8 *)block_cache_get_writable_etc(fVolume->BlockCache(), + block, base, length, transaction.ID()); +} + + +inline uint8 * +CachedBlock::SetToWritable(Transaction &transaction, off_t block, bool empty) +{ + return SetToWritable(transaction, block, block, 1, empty); +} + + +inline uint8 * +CachedBlock::SetToWritable(Transaction &transaction, block_run run, bool empty) +{ + return SetToWritable(transaction, fVolume->ToBlock(run), empty); +} + + +inline status_t +CachedBlock::MakeWritable(Transaction &transaction) +{ + if (fBlock == NULL) + return B_NO_INIT; + + return block_cache_make_writable(fVolume->BlockCache(), fBlockNumber, transaction.ID()); +} + + +#endif /* CACHED_BLOCK_H */ diff --git a/src/add-ons/kernel/file_systems/bfs/Debug.cpp b/src/add-ons/kernel/file_systems/bfs/Debug.cpp index 190eb30180..6e64fff1d9 100644 --- a/src/add-ons/kernel/file_systems/bfs/Debug.cpp +++ b/src/add-ons/kernel/file_systems/bfs/Debug.cpp @@ -1,10 +1,10 @@ /* Debug - debug stuff -** -** Initial version by Axel Dörfler, axeld@pinc-software.de -** Some code is based on work previously done by Marcus Overhagen -** -** This file may be used under the terms of the OpenBeOS License. -*/ + * + * Copyright 2001-2004, Axel Dörfler, axeld@pinc-software.de. + * Some code is based on work previously done by Marcus Overhagen. + * + * This file may be used under the terms of the MIT License. + */ #include "Debug.h" @@ -44,13 +44,12 @@ dump_block_run(const char *prefix, block_run &run) } -void +void dump_inode(Inode &inode) { Print("Inode (%p) {\n", &inode); Print("\tfVolume = %p\n", inode.fVolume); - Print("\tfBlockNumber = 0x%16Lx\n", inode.fBlockNumber); - Print("\tfNode = %p\n", inode.Node()); + Print("\tfBlockNumber = 0x%16Lx\n", inode.BlockNumber()); Print("\tfTree = %p\n", inode.fTree); Print("\tfAttributes = %p\n", inode.fAttributes); Print("\tfOldSize = 0x%16Lx\n", inode.fOldSize); @@ -261,13 +260,12 @@ dump_bplustree_node(bplustree_node *node,bplustree_header *header,Volume *volume #ifndef USER -//#warn Don't mount more than once... would register twice the debugger commands! static int dbg_inode(int argc, char **argv) { if (argc < 2) { - kprintf("usage: obfsinode ptr-to-inode\n"); + kprintf("usage: bfsinode ptr-to-inode\n"); return 0; } @@ -283,7 +281,7 @@ void remove_debugger_commands() { #ifndef USER - remove_debugger_command("obfsinode", dbg_inode); + remove_debugger_command("bfsinode", dbg_inode); #endif } @@ -292,7 +290,7 @@ void add_debugger_commands() { #ifndef USER - add_debugger_command("obfsinode", dbg_inode, "dump an Inode object"); + add_debugger_command("bfsinode", dbg_inode, "dump an Inode object"); #endif } diff --git a/src/add-ons/kernel/file_systems/bfs/Debug.h b/src/add-ons/kernel/file_systems/bfs/Debug.h index 15f47bbcd7..6d334d2799 100644 --- a/src/add-ons/kernel/file_systems/bfs/Debug.h +++ b/src/add-ons/kernel/file_systems/bfs/Debug.h @@ -1,10 +1,10 @@ +/* Debug - debug stuff + * + * Copyright 2001-2004, Axel Dörfler, axeld@pinc-software.de. + * This file may be used under the terms of the MIT License. + */ #ifndef DEBUG_H #define DEBUG_H -/* Debug - debug stuff -** -** Initial version by Axel Dörfler, axeld@pinc-software.de -** This file may be used under the terms of the OpenBeOS License. -*/ #include @@ -71,7 +71,7 @@ #define PRINT(x) ; #define REPORT_ERROR(status) ; #define RETURN_ERROR(status) return status; - #define FATAL(x) { __out("bfs: "); __out x; } + #define FATAL(x) { __out("bfs: "); __out x; panic("BFS!\n"); } #define INFORM(x) { __out("bfs: "); __out x; } #define FUNCTION() ; #define FUNCTION_START(x) ; diff --git a/src/add-ons/kernel/file_systems/bfs/Index.cpp b/src/add-ons/kernel/file_systems/bfs/Index.cpp index 7900e8906c..8756f47b82 100644 --- a/src/add-ons/kernel/file_systems/bfs/Index.cpp +++ b/src/add-ons/kernel/file_systems/bfs/Index.cpp @@ -1,8 +1,8 @@ /* Index - index access functions -** -** Initial version by Axel Dörfler, axeld@pinc-software.de -** This file may be used under the terms of the OpenBeOS License. -*/ + * + * Copyright 2001-2004, Axel Dörfler, axeld@pinc-software.de. + * This file may be used under the terms of the MIT License. + */ #include "Debug.h" @@ -57,7 +57,7 @@ Index::Unset() * the updated attribute. */ -status_t +status_t Index::SetTo(const char *name) { // remove the old node, if the index is set for the second time @@ -102,7 +102,7 @@ Index::SetTo(const char *name) * corrupted somehow or not that of an index). */ -uint32 +uint32 Index::Type() { if (fNode == NULL) @@ -161,7 +161,7 @@ Index::KeySize() status_t -Index::Create(Transaction *transaction, const char *name, uint32 type) +Index::Create(Transaction &transaction, const char *name, uint32 type) { Unset(); @@ -217,7 +217,7 @@ Index::Create(Transaction *transaction, const char *name, uint32 type) */ status_t -Index::Update(Transaction *transaction, const char *name, int32 type, const uint8 *oldKey, +Index::Update(Transaction &transaction, const char *name, int32 type, const uint8 *oldKey, uint16 oldLength, const uint8 *newKey, uint16 newLength, Inode *inode) { if (name == NULL @@ -277,22 +277,22 @@ Index::Update(Transaction *transaction, const char *name, int32 type, const uint } -status_t -Index::InsertName(Transaction *transaction, const char *name, Inode *inode) +status_t +Index::InsertName(Transaction &transaction, const char *name, Inode *inode) { return UpdateName(transaction, NULL, name, inode); } -status_t -Index::RemoveName(Transaction *transaction, const char *name, Inode *inode) +status_t +Index::RemoveName(Transaction &transaction, const char *name, Inode *inode) { return UpdateName(transaction, name, NULL, inode); } -status_t -Index::UpdateName(Transaction *transaction, const char *oldName, const char *newName, Inode *inode) +status_t +Index::UpdateName(Transaction &transaction, const char *oldName, const char *newName, Inode *inode) { uint16 oldLength = oldName ? strlen(oldName) : 0; uint16 newLength = newName ? strlen(newName) : 0; @@ -301,16 +301,16 @@ Index::UpdateName(Transaction *transaction, const char *oldName, const char *new } -status_t -Index::InsertSize(Transaction *transaction, Inode *inode) +status_t +Index::InsertSize(Transaction &transaction, Inode *inode) { off_t size = inode->Size(); return Update(transaction, "size", B_INT64_TYPE, NULL, 0, (uint8 *)&size, sizeof(int64), inode); } -status_t -Index::RemoveSize(Transaction *transaction, Inode *inode) +status_t +Index::RemoveSize(Transaction &transaction, Inode *inode) { // Inode::OldSize() is the size that's in the index off_t size = inode->OldSize(); @@ -319,7 +319,7 @@ Index::RemoveSize(Transaction *transaction, Inode *inode) status_t -Index::UpdateSize(Transaction *transaction, Inode *inode) +Index::UpdateSize(Transaction &transaction, Inode *inode) { off_t oldSize = inode->OldSize(); off_t newSize = inode->Size(); @@ -333,8 +333,8 @@ Index::UpdateSize(Transaction *transaction, Inode *inode) } -status_t -Index::InsertLastModified(Transaction *transaction, Inode *inode) +status_t +Index::InsertLastModified(Transaction &transaction, Inode *inode) { off_t modified = inode->LastModified(); return Update(transaction, "last_modified", B_INT64_TYPE, NULL, 0, @@ -342,8 +342,8 @@ Index::InsertLastModified(Transaction *transaction, Inode *inode) } -status_t -Index::RemoveLastModified(Transaction *transaction, Inode *inode) +status_t +Index::RemoveLastModified(Transaction &transaction, Inode *inode) { // Inode::OldLastModified() is the value which is in the index off_t modified = inode->OldLastModified(); @@ -352,8 +352,8 @@ Index::RemoveLastModified(Transaction *transaction, Inode *inode) } -status_t -Index::UpdateLastModified(Transaction *transaction, Inode *inode, off_t modified) +status_t +Index::UpdateLastModified(Transaction &transaction, Inode *inode, off_t modified) { off_t oldModified = inode->OldLastModified(); if (modified == -1) @@ -363,7 +363,7 @@ Index::UpdateLastModified(Transaction *transaction, Inode *inode, off_t modified status_t status = Update(transaction, "last_modified", B_INT64_TYPE, (uint8 *)&oldModified, sizeof(int64), (uint8 *)&modified, sizeof(int64), inode); - inode->Node()->last_modified_time = modified; + inode->Node().last_modified_time = HOST_ENDIAN_TO_BFS_INT64(modified); if (status == B_OK) inode->UpdateOldLastModified(); diff --git a/src/add-ons/kernel/file_systems/bfs/Index.h b/src/add-ons/kernel/file_systems/bfs/Index.h index 2cd91cfaf1..f537bc6e1c 100644 --- a/src/add-ons/kernel/file_systems/bfs/Index.h +++ b/src/add-ons/kernel/file_systems/bfs/Index.h @@ -1,10 +1,10 @@ +/* Index - index access functions + * + * Copyright 2001-2004, Axel Dörfler, axeld@pinc-software.de. + * This file may be used under the terms of the MIT License. + */ #ifndef INDEX_H #define INDEX_H -/* Index - index access functions -** -** Initial version by Axel Dörfler, axeld@pinc-software.de -** This file may be used under the terms of the OpenBeOS License. -*/ #include @@ -26,21 +26,23 @@ class Index { uint32 Type(); size_t KeySize(); - status_t Create(Transaction *transaction, const char *name, uint32 type); + status_t Create(Transaction &transaction, const char *name, uint32 type); - status_t Update(Transaction *transaction, const char *name, int32 type, const uint8 *oldKey, uint16 oldLength, const uint8 *newKey, uint16 newLength, Inode *inode); + status_t Update(Transaction &transaction, const char *name, int32 type, const uint8 *oldKey, + uint16 oldLength, const uint8 *newKey, uint16 newLength, Inode *inode); - status_t InsertName(Transaction *transaction,const char *name,Inode *inode); - status_t RemoveName(Transaction *transaction,const char *name,Inode *inode); - status_t UpdateName(Transaction *transaction,const char *oldName,const char *newName,Inode *inode); + status_t InsertName(Transaction &transaction, const char *name, Inode *inode); + status_t RemoveName(Transaction &transaction, const char *name, Inode *inode); + status_t UpdateName(Transaction &transaction, const char *oldName, const char *newName, + Inode *inode); - status_t InsertSize(Transaction *transaction, Inode *inode); - status_t RemoveSize(Transaction *transaction, Inode *inode); - status_t UpdateSize(Transaction *transaction, Inode *inode); + status_t InsertSize(Transaction &transaction, Inode *inode); + status_t RemoveSize(Transaction &transaction, Inode *inode); + status_t UpdateSize(Transaction &transaction, Inode *inode); - status_t InsertLastModified(Transaction *transaction, Inode *inode); - status_t RemoveLastModified(Transaction *transaction, Inode *inode); - status_t UpdateLastModified(Transaction *transaction, Inode *inode,off_t modified = -1); + status_t InsertLastModified(Transaction &transaction, Inode *inode); + status_t RemoveLastModified(Transaction &transaction, Inode *inode); + status_t UpdateLastModified(Transaction &transaction, Inode *inode, off_t modified = -1); private: Index(const Index &); diff --git a/src/add-ons/kernel/file_systems/bfs/Inode.cpp b/src/add-ons/kernel/file_systems/bfs/Inode.cpp index 0b0ff0cafa..08f42772cc 100644 --- a/src/add-ons/kernel/file_systems/bfs/Inode.cpp +++ b/src/add-ons/kernel/file_systems/bfs/Inode.cpp @@ -1,14 +1,13 @@ /* Inode - inode access functions -** -** Initial version by Axel Dörfler, axeld@pinc-software.de -** This file may be used under the terms of the Haiku License. -*/ + * + * Copyright 2001-2004, Axel Dörfler, axeld@pinc-software.de. + * This file may be used under the terms of the MIT License. + */ #include "Debug.h" #include "Inode.h" #include "BPlusTree.h" -#include "Stream.h" #include "Index.h" #include @@ -20,7 +19,7 @@ class InodeAllocator { public: - InodeAllocator(Transaction *transaction); + InodeAllocator(Transaction &transaction); ~InodeAllocator(); status_t New(block_run *parentRun, mode_t mode, block_run &run, Inode **_inode); @@ -34,9 +33,9 @@ class InodeAllocator { }; -InodeAllocator::InodeAllocator(Transaction *transaction) +InodeAllocator::InodeAllocator(Transaction &transaction) : - fTransaction(transaction), + fTransaction(&transaction), fInode(NULL) { } @@ -46,11 +45,11 @@ InodeAllocator::~InodeAllocator() { if (fTransaction != NULL) { if (fInode != NULL) { - fInode->Node()->flags &= ~HOST_ENDIAN_TO_BFS_INT32(INODE_IN_USE | INODE_NOT_READY); + fInode->Node().flags &= ~HOST_ENDIAN_TO_BFS_INT32(INODE_IN_USE | INODE_NOT_READY); // this unblocks any pending bfs_read_vnode() calls - fInode->Free(fTransaction); + fInode->Free(*fTransaction); } else - fTransaction->GetVolume()->Free(fTransaction, fRun); + fTransaction->GetVolume()->Free(*fTransaction, fRun); } delete fInode; @@ -62,7 +61,7 @@ InodeAllocator::New(block_run *parentRun, mode_t mode, block_run &run, Inode **_ { Volume *volume = fTransaction->GetVolume(); - status_t status = volume->AllocateForInode(fTransaction, parentRun, mode, fRun); + status_t status = volume->AllocateForInode(*fTransaction, parentRun, mode, fRun); if (status < B_OK) { // don't free the space in the destructor, because // the allocation failed @@ -71,32 +70,10 @@ InodeAllocator::New(block_run *parentRun, mode_t mode, block_run &run, Inode **_ } run = fRun; - fInode = new Inode(volume, volume->ToVnode(run), true); + fInode = new Inode(volume, *fTransaction, volume->ToVnode(run), mode, run); if (fInode == NULL) RETURN_ERROR(B_NO_MEMORY); - // initialize the on-disk bfs_inode structure - - bfs_inode *node = fInode->Node(); - - node->magic1 = HOST_ENDIAN_TO_BFS_INT32(INODE_MAGIC1); - node->inode_num = run; - node->mode = HOST_ENDIAN_TO_BFS_INT32(mode); - node->flags = HOST_ENDIAN_TO_BFS_INT32(INODE_IN_USE | INODE_NOT_READY); - // INODE_NOT_READY prevents the inode from being opened - it is - // cleared in InodeAllocator::Keep() - node->etc = (uint32)fInode; - // this is temporarily set along INODE_NOT_READY and lets bfs_read_vnode() - // find the associated Inode object - - node->create_time = HOST_ENDIAN_TO_BFS_INT64((bigtime_t)time(NULL) << INODE_TIME_SHIFT); - node->last_modified_time = HOST_ENDIAN_TO_BFS_INT64(node->create_time - | (volume->GetUniqueID() & INODE_TIME_MASK)); - // we use Volume::GetUniqueID() to avoid having too many duplicates in the - // last_modified index - - node->inode_size = HOST_ENDIAN_TO_BFS_INT32(volume->InodeSize()); - *_inode = fInode; return B_OK; } @@ -109,15 +86,15 @@ InodeAllocator::CreateTree() // force S_STR_INDEX to be set, if no type is set if ((fInode->Mode() & S_INDEX_TYPES) == 0) - fInode->Node()->mode |= HOST_ENDIAN_TO_BFS_INT32(S_STR_INDEX); + fInode->Node().mode |= HOST_ENDIAN_TO_BFS_INT32(S_STR_INDEX); - BPlusTree *tree = fInode->fTree = new BPlusTree(fTransaction, fInode); + BPlusTree *tree = fInode->fTree = new BPlusTree(*fTransaction, fInode); if (tree == NULL || tree->InitCheck() < B_OK) return B_ERROR; if (fInode->IsRegularNode()) { - if (tree->Insert(fTransaction, ".", fInode->ID()) < B_OK - || tree->Insert(fTransaction, "..", volume->ToVnode(fInode->Parent())) < B_OK) + if (tree->Insert(*fTransaction, ".", fInode->ID()) < B_OK + || tree->Insert(*fTransaction, "..", volume->ToVnode(fInode->Parent())) < B_OK) return B_ERROR; } return B_OK; @@ -129,8 +106,8 @@ InodeAllocator::Keep() { ASSERT(fInode != NULL && fTransaction != NULL); - fInode->Node()->flags &= ~HOST_ENDIAN_TO_BFS_INT32(INODE_NOT_READY); - status_t status = fInode->WriteBack(fTransaction); + fInode->Node().flags &= ~HOST_ENDIAN_TO_BFS_INT32(INODE_NOT_READY); + status_t status = fInode->WriteBack(*fTransaction); fTransaction = NULL; fInode = NULL; @@ -178,71 +155,114 @@ bfs_inode::InitCheck(Volume *volume) // #pragma mark - -Inode::Inode(Volume *volume, vnode_id id, bool empty, uint8 reenter) - : CachedBlock(volume, volume->VnodeToBlock(id), empty), - fTree(NULL), - fLock() +Inode::Inode(Volume *volume, vnode_id id) + : + fVolume(volume), + fID(id) { - PRINT(("Inode::Inode(%p, %Ld, %s, %s) @ %p\n", - volume, id, empty ? "empty" : "not-empty", reenter ? "reenter":"not-reenter", this)); + PRINT(("Inode::Inode(volume = %p, id = %Ld) @ %p\n", volume, id, this)); - Initialize(); -} + NodeGetter node(volume, this); + memcpy(&fNode, node.Node(), sizeof(bfs_inode)); - -Inode::Inode(CachedBlock *cached) - : CachedBlock(cached), - fTree(NULL), - fLock() -{ - PRINT(("Inode::Inode(%p) @ %p\n", cached, this)); - - Initialize(); -} - - -Inode::~Inode() -{ - PRINT(("Inode::~Inode() @ %p\n", this)); - - delete fTree; -} - - -void -Inode::Initialize() -{ char lockName[32]; sprintf(lockName, "bfs inode %ld.%d", BlockRun().AllocationGroup(), BlockRun().Start()); fLock.Initialize(lockName); - Node()->flags &= HOST_ENDIAN_TO_BFS_INT32(INODE_PERMANENT_FLAGS); - // these two will help to maintain the indices fOldSize = Size(); fOldLastModified = LastModified(); + fTree = NULL; + fCache = NULL; + if (IsContainer()) fTree = new BPlusTree(this); - - fCache = NULL; + if (IsFile()) + SetFileCache(file_cache_create(fVolume->ID(), ID(), Size(), fVolume->Device())); } -status_t +Inode::Inode(Volume *volume, Transaction &transaction, vnode_id id, mode_t mode, block_run &run) + : + fVolume(volume), + fID(id) +{ + PRINT(("Inode::Inode(volume = %p, transaction = %p, id = %Ld) @ %p\n", + volume, &transaction, id, this)); + + char lockName[32]; + sprintf(lockName, "bfs inode %ld.%d", run.AllocationGroup(), run.Start()); + fLock.Initialize(lockName); + + NodeGetter node(volume, transaction, this, true); + memset(&fNode, 0, sizeof(bfs_inode)); + + // Initialize the bfs_inode structure -- it's not written back to disk + // here, because it will be done so already when the inode could be + // created completely. + + Node().magic1 = HOST_ENDIAN_TO_BFS_INT32(INODE_MAGIC1); + Node().inode_num = run; + Node().mode = HOST_ENDIAN_TO_BFS_INT32(mode); + Node().flags = HOST_ENDIAN_TO_BFS_INT32(INODE_IN_USE | INODE_NOT_READY); + // INODE_NOT_READY prevents the inode from being opened - it is + // cleared in InodeAllocator::Keep() + Node().etc = (uint32)this; + // this is temporarily set along INODE_NOT_READY and lets bfs_read_vnode() + // find the associated Inode object + + Node().create_time = HOST_ENDIAN_TO_BFS_INT64((bigtime_t)time(NULL) << INODE_TIME_SHIFT); + Node().last_modified_time = HOST_ENDIAN_TO_BFS_INT64(Node().create_time + | (volume->GetUniqueID() & INODE_TIME_MASK)); + // we use Volume::GetUniqueID() to avoid having too many duplicates in the + // last_modified index + + Node().inode_size = HOST_ENDIAN_TO_BFS_INT32(volume->InodeSize()); + + // these two will help to maintain the indices + fOldSize = Size(); + fOldLastModified = LastModified(); + + fTree = NULL; + fCache = NULL; + + if (IsFile()) + SetFileCache(file_cache_create(fVolume->ID(), ID(), Size(), fVolume->Device())); +} + + +/* +Inode::Inode(CachedBlock *cached) + : + fLock() +{ + PRINT(("Inode::Inode(cached = %p) @ %p\n", cached, this)); + + Initialize(); +} +*/ + +Inode::~Inode() +{ + PRINT(("Inode::~Inode() @ %p\n", this)); + + file_cache_delete(FileCache()); + delete fTree; +} + + +status_t Inode::InitCheck(bool checkNode) { - if (!Node()) - RETURN_ERROR(B_IO_ERROR); - // test inode magic and flags if (checkNode) { - status_t status = Node()->InitCheck(fVolume); + status_t status = Node().InitCheck(fVolume); if (status == B_BUSY) return B_BUSY; if (status < B_OK) { - FATAL(("inode at block %Ld corrupt!\n", fBlockNumber)); + FATAL(("inode at block %Ld corrupt!\n", BlockNumber())); RETURN_ERROR(B_BAD_DATA); } } @@ -254,7 +274,7 @@ Inode::InitCheck(bool checkNode) status_t status = fTree->InitCheck(); if (status < B_OK) { - FATAL(("inode tree at block %Ld corrupt!\n", fBlockNumber)); + FATAL(("inode tree at block %Ld corrupt!\n", BlockNumber())); RETURN_ERROR(B_BAD_DATA); } } @@ -265,7 +285,19 @@ Inode::InitCheck(bool checkNode) } -status_t +status_t +Inode::WriteBack(Transaction &transaction) +{ + NodeGetter node(fVolume, transaction, this); + if (node.WritableNode() == NULL) + return B_IO_ERROR; + + memcpy(node.WritableNode(), &Node(), sizeof(bfs_inode)); + return B_OK; +} + + +status_t Inode::CheckPermissions(int accessMode) const { uid_t user = geteuid(); @@ -281,9 +313,9 @@ Inode::CheckPermissions(int accessMode) const // shift mode bits, to check directly against accessMode mode_t mode = Mode(); - if (user == (uid_t)Node()->UserID()) + if (user == (uid_t)fNode.UserID()) mode >>= 6; - else if (group == (gid_t)Node()->GroupID()) + else if (group == (gid_t)fNode.GroupID()) mode >>= 3; if (accessMode & ~(mode & S_IRWXO)) @@ -326,14 +358,14 @@ Inode::RemoveIterator(AttributeIterator *iterator) */ status_t -Inode::MakeSpaceForSmallData(Transaction *transaction, const char *name, int32 bytes) +Inode::MakeSpaceForSmallData(Transaction &transaction, bfs_inode *node, const char *name, int32 bytes) { ASSERT(fSmallDataLock.IsLocked()); while (bytes > 0) { - small_data *item = Node()->SmallDataStart(), *max = NULL; + small_data *item = node->SmallDataStart(), *max = NULL; int32 index = 0, maxIndex = 0; - for (; !item->IsLast(Node()); item = item->Next(), index++) { + for (; !item->IsLast(node); item = item->Next(), index++) { // should not remove those if (*item->Name() == FILE_NAME_NAME || !strcmp(name, item->Name())) continue; @@ -348,7 +380,7 @@ Inode::MakeSpaceForSmallData(Transaction *transaction, const char *name, int32 b break; } - if (item->IsLast(Node()) || (int32)item->Size() < bytes) + if (item->IsLast(node) || (int32)item->Size() < bytes) return B_ERROR; bytes -= max->Size(); @@ -367,7 +399,7 @@ Inode::MakeSpaceForSmallData(Transaction *transaction, const char *name, int32 b ReleaseAttribute(attribute); if (status < B_OK) { - Vnode vnode(fVolume,Attributes()); + Vnode vnode(fVolume, Attributes()); Inode *attributes; if (vnode.Get(&attributes) < B_OK || attributes->Remove(transaction, name) < B_OK) { @@ -377,7 +409,7 @@ Inode::MakeSpaceForSmallData(Transaction *transaction, const char *name, int32 b RETURN_ERROR(status); } - RemoveSmallData(max, maxIndex); + RemoveSmallData(node, max, maxIndex); } return B_OK; } @@ -389,19 +421,19 @@ Inode::MakeSpaceForSmallData(Transaction *transaction, const char *name, int32 b */ status_t -Inode::RemoveSmallData(small_data *item, int32 index) +Inode::RemoveSmallData(bfs_inode *node, small_data *item, int32 index) { ASSERT(fSmallDataLock.IsLocked()); small_data *next = item->Next(); - if (!next->IsLast(Node())) { + if (!next->IsLast(node)) { // find the last attribute small_data *last = next; - while (!last->IsLast(Node())) + while (!last->IsLast(node)) last = last->Next(); int32 size = (uint8 *)last - (uint8 *)next; - if (size < 0 || size > (uint8 *)Node() + fVolume->BlockSize() - (uint8 *)next) + if (size < 0 || size > (uint8 *)node + fVolume->BlockSize() - (uint8 *)next) return B_BAD_DATA; memmove(item, next, size); @@ -409,7 +441,7 @@ Inode::RemoveSmallData(small_data *item, int32 index) // Move the "last" one to its new location and // correctly terminate the small_data section last = (small_data *)((uint8 *)last - ((uint8 *)next - (uint8 *)item)); - memset(last, 0, (uint8 *)Node() + fVolume->BlockSize() - (uint8 *)last); + memset(last, 0, (uint8 *)node + fVolume->BlockSize() - (uint8 *)last); } else memset(item, 0, item->Size()); @@ -428,26 +460,28 @@ Inode::RemoveSmallData(small_data *item, int32 index) */ status_t -Inode::RemoveSmallData(Transaction *transaction, const char *name) +Inode::RemoveSmallData(Transaction &transaction, NodeGetter &nodeGetter, const char *name) { if (name == NULL) return B_BAD_VALUE; + bfs_inode *node = nodeGetter.WritableNode(); SimpleLocker locker(fSmallDataLock); // search for the small_data item - small_data *item = Node()->SmallDataStart(); + small_data *item = node->SmallDataStart(); int32 index = 0; - while (!item->IsLast(Node()) && strcmp(item->Name(), name)) { + while (!item->IsLast(node) && strcmp(item->Name(), name)) { item = item->Next(); index++; } - if (item->IsLast(Node())) + if (item->IsLast(node)) return B_ENTRY_NOT_FOUND; - return RemoveSmallData(item, index); + nodeGetter.MakeWritable(transaction); + return RemoveSmallData(node, item, index); } @@ -464,10 +498,12 @@ Inode::RemoveSmallData(Transaction *transaction, const char *name) */ status_t -Inode::AddSmallData(Transaction *transaction, const char *name, uint32 type, - const uint8 *data, size_t length, bool force) +Inode::AddSmallData(Transaction &transaction, NodeGetter &nodeGetter, const char *name, + uint32 type, const uint8 *data, size_t length, bool force) { - if (name == NULL || data == NULL || type == 0) + bfs_inode *node = nodeGetter.WritableNode(); + + if (node == NULL || name == NULL || data == NULL || type == 0) return B_BAD_VALUE; // reject any requests that can't fit into the small_data section @@ -476,48 +512,50 @@ Inode::AddSmallData(Transaction *transaction, const char *name, uint32 type, if (spaceNeeded > fVolume->InodeSize() - sizeof(bfs_inode)) return B_DEVICE_FULL; + nodeGetter.MakeWritable(transaction); SimpleLocker locker(fSmallDataLock); - small_data *item = Node()->SmallDataStart(); + // Find the last item or one with the same name we have to add + small_data *item = node->SmallDataStart(); int32 index = 0; - while (!item->IsLast(Node()) && strcmp(item->Name(), name)) { + while (!item->IsLast(node) && strcmp(item->Name(), name)) { item = item->Next(); index++; } // is the attribute already in the small_data section? // then just replace the data part of that one - if (!item->IsLast(Node())) { + if (!item->IsLast(node)) { // find last attribute small_data *last = item; - while (!last->IsLast(Node())) + while (!last->IsLast(node)) last = last->Next(); // try to change the attributes value if (item->data_size > length || force - || ((uint8 *)last + length - item->DataSize()) <= ((uint8 *)Node() + fVolume->InodeSize())) { + || ((uint8 *)last + length - item->DataSize()) <= ((uint8 *)node + fVolume->InodeSize())) { // make room for the new attribute if needed (and we are forced to do so) if (force - && ((uint8 *)last + length - item->DataSize()) > ((uint8 *)Node() + fVolume->InodeSize())) { + && ((uint8 *)last + length - item->DataSize()) > ((uint8 *)node + fVolume->InodeSize())) { // We also take the free space at the end of the small_data section // into account, and request only what's really needed uint32 needed = length - item->DataSize() - - (uint32)((uint8 *)Node() + fVolume->InodeSize() - (uint8 *)last); + (uint32)((uint8 *)node + fVolume->InodeSize() - (uint8 *)last); - if (MakeSpaceForSmallData(transaction, name, needed) < B_OK) + if (MakeSpaceForSmallData(transaction, node, name, needed) < B_OK) return B_ERROR; // reset our pointers - item = Node()->SmallDataStart(); + item = node->SmallDataStart(); index = 0; - while (!item->IsLast(Node()) && strcmp(item->Name(), name)) { + while (!item->IsLast(node) && strcmp(item->Name(), name)) { item = item->Next(); index++; } last = item; - while (!last->IsLast(Node())) + while (!last->IsLast(node)) last = last->Next(); } @@ -526,14 +564,14 @@ Inode::AddSmallData(Transaction *transaction, const char *name, uint32 type, if (length != item->DataSize()) { // move the attributes after the current one small_data *next = item->Next(); - if (!next->IsLast(Node())) + if (!next->IsLast(node)) memmove((uint8 *)item + spaceNeeded, next, (uint8 *)last - (uint8 *)next); // Move the "last" one to its new location and // correctly terminate the small_data section last = (small_data *)((uint8 *)last - ((uint8 *)next - ((uint8 *)item + spaceNeeded))); - if ((uint8 *)last < (uint8 *)Node() + fVolume->BlockSize()) - memset(last, 0, (uint8 *)Node() + fVolume->BlockSize() - (uint8 *)last); + if ((uint8 *)last < (uint8 *)node + fVolume->BlockSize()) + memset(last, 0, (uint8 *)node + fVolume->BlockSize() - (uint8 *)last); item->data_size = HOST_ENDIAN_TO_BFS_INT16(length); } @@ -547,7 +585,7 @@ Inode::AddSmallData(Transaction *transaction, const char *name, uint32 type, // Could not replace the old attribute, so remove it to let // let the calling function create an attribute file for it - if (RemoveSmallData(item, index) < B_OK) + if (RemoveSmallData(node, item, index) < B_OK) return B_ERROR; return B_DEVICE_FULL; @@ -555,19 +593,19 @@ Inode::AddSmallData(Transaction *transaction, const char *name, uint32 type, // try to add the new attribute! - if ((uint8 *)item + spaceNeeded > (uint8 *)Node() + fVolume->InodeSize()) { + if ((uint8 *)item + spaceNeeded > (uint8 *)node + fVolume->InodeSize()) { // there is not enough space for it! if (!force) return B_DEVICE_FULL; // make room for the new attribute - if (MakeSpaceForSmallData(transaction, name, spaceNeeded) < B_OK) + if (MakeSpaceForSmallData(transaction, node, name, spaceNeeded) < B_OK) return B_ERROR; // get new last item! - item = Node()->SmallDataStart(); + item = node->SmallDataStart(); index = 0; - while (!item->IsLast(Node())) { + while (!item->IsLast(node)) { item = item->Next(); index++; } @@ -582,8 +620,8 @@ Inode::AddSmallData(Transaction *transaction, const char *name, uint32 type, // correctly terminate the small_data section item = item->Next(); - if (!item->IsLast(Node())) - memset(item, 0, (uint8 *)Node() + fVolume->InodeSize() - (uint8 *)item); + if (!item->IsLast(node)) + memset(item, 0, (uint8 *)node + fVolume->InodeSize() - (uint8 *)item); // update all current iterators AttributeIterator *iterator = NULL; @@ -607,10 +645,10 @@ Inode::AddSmallData(Transaction *transaction, const char *name, uint32 type, */ status_t -Inode::GetNextSmallData(small_data **_smallData) const +Inode::GetNextSmallData(bfs_inode *node, small_data **_smallData) const { - if (!Node()) - RETURN_ERROR(B_ERROR); + if (node == NULL) + RETURN_ERROR(B_BAD_VALUE); ASSERT(fSmallDataLock.IsLocked()); @@ -618,12 +656,12 @@ Inode::GetNextSmallData(small_data **_smallData) const // begin from the start? if (data == NULL) - data = Node()->SmallDataStart(); + data = node->SmallDataStart(); else data = data->Next(); // is already last item? - if (data->IsLast(Node())) + if (data->IsLast(node)) return B_ENTRY_NOT_FOUND; *_smallData = data; @@ -638,12 +676,12 @@ Inode::GetNextSmallData(small_data **_smallData) const */ small_data * -Inode::FindSmallData(const char *name) const +Inode::FindSmallData(const bfs_inode *node, const char *name) const { ASSERT(fSmallDataLock.IsLocked()); small_data *smallData = NULL; - while (GetNextSmallData(&smallData) == B_OK) { + while (GetNextSmallData(const_cast(node), &smallData) == B_OK) { if (!strcmp(smallData->Name(), name)) return smallData; } @@ -657,12 +695,12 @@ Inode::FindSmallData(const char *name) const */ const char * -Inode::Name() const +Inode::Name(const bfs_inode *node) const { ASSERT(fSmallDataLock.IsLocked()); small_data *smallData = NULL; - while (GetNextSmallData(&smallData) == B_OK) { + while (GetNextSmallData((bfs_inode *)node, &smallData) == B_OK) { if (*smallData->Name() == FILE_NAME_NAME && smallData->NameSize() == FILE_NAME_NAME_LENGTH) return (const char *)smallData->Data(); } @@ -677,9 +715,10 @@ Inode::Name() const status_t Inode::GetName(char *buffer) const { + NodeGetter node(fVolume, this); SimpleLocker locker(fSmallDataLock); - const char *name = Name(); + const char *name = Name(node.Node()); if (name == NULL) return B_ENTRY_NOT_FOUND; @@ -696,14 +735,16 @@ Inode::GetName(char *buffer) const */ status_t -Inode::SetName(Transaction *transaction, const char *name) +Inode::SetName(Transaction &transaction, const char *name) { if (name == NULL || *name == '\0') return B_BAD_VALUE; + NodeGetter node(fVolume, transaction, this); const char nameTag[2] = {FILE_NAME_NAME, 0}; - return AddSmallData(transaction, nameTag, FILE_NAME_TYPE, (uint8 *)name, strlen(name), true); + return AddSmallData(transaction, node, nameTag, FILE_NAME_TYPE, + (uint8 *)name, strlen(name), true); } @@ -720,9 +761,10 @@ Inode::ReadAttribute(const char *name, int32 type, off_t pos, uint8 *buffer, siz // search in the small_data section (which has to be locked first) { + NodeGetter node(fVolume, this); SimpleLocker locker(fSmallDataLock); - small_data *smallData = FindSmallData(name); + small_data *smallData = FindSmallData(node.Node(), name); if (smallData != NULL) { size_t length = *_length; if (pos >= smallData->data_size) { @@ -761,7 +803,7 @@ Inode::ReadAttribute(const char *name, int32 type, off_t pos, uint8 *buffer, siz */ status_t -Inode::WriteAttribute(Transaction *transaction, const char *name, int32 type, off_t pos, +Inode::WriteAttribute(Transaction &transaction, const char *name, int32 type, off_t pos, const uint8 *buffer, size_t *_length) { // needed to maintain the index @@ -780,9 +822,10 @@ Inode::WriteAttribute(Transaction *transaction, const char *name, int32 type, of if (GetAttribute(name, &attribute) < B_OK) { // save the old attribute data + NodeGetter node(fVolume, transaction, this); fSmallDataLock.Lock(); - small_data *smallData = FindSmallData(name); + small_data *smallData = FindSmallData(node.Node(), name); if (smallData != NULL) { oldLength = smallData->DataSize(); if (oldLength > BPLUSTREE_MAX_KEY_LENGTH) @@ -794,7 +837,7 @@ Inode::WriteAttribute(Transaction *transaction, const char *name, int32 type, of // if the attribute doesn't exist yet (as a file), try to put it in the // small_data section first - if that fails (due to insufficent space), // create a real attribute file - status = AddSmallData(transaction, name, type, buffer, *_length); + status = AddSmallData(transaction, node, name, type, buffer, *_length); if (status == B_DEVICE_FULL) { status = CreateAttribute(transaction, name, type, &attribute); if (status < B_OK) @@ -844,16 +887,17 @@ Inode::WriteAttribute(Transaction *transaction, const char *name, int32 type, of */ status_t -Inode::RemoveAttribute(Transaction *transaction, const char *name) +Inode::RemoveAttribute(Transaction &transaction, const char *name) { Index index(fVolume); bool hasIndex = index.SetTo(name) == B_OK; + NodeGetter node(fVolume, this); // update index for attributes in the small_data section { fSmallDataLock.Lock(); - small_data *smallData = FindSmallData(name); + small_data *smallData = FindSmallData(node.Node(), name); if (smallData != NULL) { uint32 length = smallData->DataSize(); if (length > BPLUSTREE_MAX_KEY_LENGTH) @@ -863,9 +907,9 @@ Inode::RemoveAttribute(Transaction *transaction, const char *name) fSmallDataLock.Unlock(); } - status_t status = RemoveSmallData(transaction, name); + status_t status = RemoveSmallData(transaction, node, name); if (status == B_OK) { - status = WriteBack(transaction); + WriteBack(transaction); } else if (status == B_ENTRY_NOT_FOUND && !Attributes().IsZero()) { // remove the attribute file if it exists Vnode vnode(fVolume, Attributes()); @@ -892,7 +936,7 @@ Inode::RemoveAttribute(Transaction *transaction, const char *name) // remove attribute directory (don't fail if that can't be done) if (remove_vnode(fVolume->ID(), attributes->ID()) == B_OK) { // update the inode, so that no one will ever doubt it's deleted :-) - attributes->Node()->flags |= HOST_ENDIAN_TO_BFS_INT32(INODE_DELETED); + attributes->Node().flags |= HOST_ENDIAN_TO_BFS_INT32(INODE_DELETED); if (attributes->WriteBack(transaction) == B_OK) { Attributes().SetTo(0, 0, 0); WriteBack(transaction); @@ -949,7 +993,7 @@ Inode::ReleaseAttribute(Inode *attribute) status_t -Inode::CreateAttribute(Transaction *transaction, const char *name, uint32 type, Inode **attribute) +Inode::CreateAttribute(Transaction &transaction, const char *name, uint32 type, Inode **attribute) { // do we need to create the attribute directory first? if (Attributes().IsZero()) { @@ -1028,44 +1072,179 @@ Inode::IsEmpty() status_t Inode::FindBlockRun(off_t pos, block_run &run, off_t &offset) { - // The BPlusTree class will call this function, we'll provide - // standard cached access only from here - return ((Stream *)this)->FindBlockRun(pos, run, offset); + data_stream *data = &Node().data; + + // find matching block run + + if (data->MaxDirectRange() > 0 && pos >= data->MaxDirectRange()) { + if (data->MaxDoubleIndirectRange() > 0 && pos >= data->MaxIndirectRange()) { + // access to double indirect blocks + + CachedBlock cached(fVolume); + + off_t start = pos - data->MaxIndirectRange(); + int32 indirectSize = (1L << (INDIRECT_BLOCKS_SHIFT + cached.BlockShift())) + * (fVolume->BlockSize() / sizeof(block_run)); + int32 directSize = NUM_ARRAY_BLOCKS << cached.BlockShift(); + int32 index = start / indirectSize; + int32 runsPerBlock = cached.BlockSize() / sizeof(block_run); + + block_run *indirect = (block_run *)cached.SetTo( + fVolume->ToBlock(data->double_indirect) + index / runsPerBlock); + if (indirect == NULL) + RETURN_ERROR(B_ERROR); + + //printf("\tstart = %Ld, indirectSize = %ld, directSize = %ld, index = %ld\n",start,indirectSize,directSize,index); + //printf("\tlook for indirect block at %ld,%d\n",indirect[index].allocation_group,indirect[index].start); + + int32 current = (start % indirectSize) / directSize; + + indirect = (block_run *)cached.SetTo( + fVolume->ToBlock(indirect[index % runsPerBlock]) + current / runsPerBlock); + if (indirect == NULL) + RETURN_ERROR(B_ERROR); + + run = indirect[current % runsPerBlock]; + offset = data->MaxIndirectRange() + (index * indirectSize) + (current * directSize); + //printf("\tfCurrent = %ld, fRunFileOffset = %Ld, fRunBlockEnd = %Ld, fRun = %ld,%d\n",fCurrent,fRunFileOffset,fRunBlockEnd,fRun.allocation_group,fRun.start); + } else { + // access to indirect blocks + + int32 runsPerBlock = fVolume->BlockSize() / sizeof(block_run); + off_t runBlockEnd = data->MaxDirectRange(); + + CachedBlock cached(fVolume); + off_t block = fVolume->ToBlock(data->indirect); + + for (int32 i = 0; i < data->indirect.Length(); i++) { + block_run *indirect = (block_run *)cached.SetTo(block + i); + if (indirect == NULL) + RETURN_ERROR(B_IO_ERROR); + + int32 current = -1; + while (++current < runsPerBlock) { + if (indirect[current].IsZero()) + break; + + runBlockEnd += indirect[current].Length() << cached.BlockShift(); + if (runBlockEnd > pos) { + run = indirect[current]; + offset = runBlockEnd - (run.Length() << cached.BlockShift()); + //printf("reading from indirect block: %ld,%d\n",fRun.allocation_group,fRun.start); + //printf("### indirect-run[%ld] = (%ld,%d,%d), offset = %Ld\n",fCurrent,fRun.allocation_group,fRun.start,fRun.Length(),fRunFileOffset); + return fVolume->ValidateBlockRun(run); + } + } + } + RETURN_ERROR(B_ERROR); + } + } else { + // access from direct blocks + + off_t runBlockEnd = 0LL; + int32 current = -1; + + while (++current < NUM_DIRECT_BLOCKS) { + if (data->direct[current].IsZero()) + break; + + runBlockEnd += data->direct[current].Length() << fVolume->BlockShift(); + if (runBlockEnd > pos) { + run = data->direct[current]; + offset = runBlockEnd - (run.Length() << fVolume->BlockShift()); + //printf("### run[%ld] = (%ld,%d,%d), offset = %Ld\n",fCurrent,fRun.allocation_group,fRun.start,fRun.Length(),fRunFileOffset); + return fVolume->ValidateBlockRun(run); + } + } + //PRINT(("FindBlockRun() failed in direct range: size = %Ld, pos = %Ld\n",data->size,pos)); + return B_ENTRY_NOT_FOUND; + } + return fVolume->ValidateBlockRun(run); } status_t Inode::ReadAt(off_t pos, uint8 *buffer, size_t *_length) { +#if 0 // call the right ReadAt() method, depending on the inode flags - if (Flags() & INODE_NO_CACHE) - return ((Stream *)this)->ReadAt(pos, buffer, _length); - if (Flags() & INODE_LOGGED) return ((Stream *)this)->ReadAt(pos, buffer, _length); return ((Stream *)this)->ReadAt(pos, buffer, _length); +#endif + size_t length = *_length; + + // set/check boundaries for pos/length + if (pos < 0) + return B_BAD_VALUE; + + if (pos >= Size() || length == 0) { + *_length = 0; + return B_NO_ERROR; + } + + return file_cache_read(FileCache(), pos, buffer, _length); } status_t -Inode::WriteAt(Transaction *transaction, off_t pos, const uint8 *buffer, size_t *_length) +Inode::WriteAt(Transaction &transaction, off_t pos, const uint8 *buffer, size_t *_length) { // call the right WriteAt() method, depending on the inode flags // update the last modification time in memory, it will be written // back to the inode, and the index when the file is closed // ToDo: should update the internal last modified time only at this point! - Node()->last_modified_time = (bigtime_t)time(NULL) << INODE_TIME_SHIFT; - - if (Flags() & INODE_NO_CACHE) - return ((Stream *)this)->WriteAt(transaction, pos, buffer, _length); + Node().last_modified_time = HOST_ENDIAN_TO_BFS_INT64((bigtime_t)time(NULL) << INODE_TIME_SHIFT); + // ToDo: support INODE_LOGGED! +#if 0 if (Flags() & INODE_LOGGED) return ((Stream *)this)->WriteAt(transaction, pos, buffer, _length); return ((Stream *)this)->WriteAt(transaction, pos, buffer, _length); +#endif + size_t length = *_length; + + // set/check boundaries for pos/length + if (pos < 0) + return B_BAD_VALUE; + + if (pos + length > Size()) { + off_t oldSize = Size(); + + // uncached files can't be resized (Inode::SetFileSize() also + // doesn't allow this, but this way we don't have to start a + // transaction to find out). + if (Flags() & INODE_NO_CACHE) + return B_BAD_VALUE; + + // the transaction doesn't have to be started already + // ToDo: what's that INODE_NO_TRANSACTION flag good for again? + if ((Flags() & INODE_NO_TRANSACTION) == 0 + && !transaction.IsStarted()) + transaction.Start(fVolume, BlockNumber()); + + // let's grow the data stream to the size needed + status_t status = SetFileSize(transaction, pos + length); + if (status < B_OK) { + *_length = 0; + RETURN_ERROR(status); + } + // If the position of the write was beyond the file size, we + // have to fill the gap between that position and the old file + // size with zeros. + FillGapWithZeros(oldSize, pos); + } + + // If we don't want to write anything, we can now return (we may + // just have changed the file size using the position parameter) + if (length == 0) + return B_OK; + + return file_cache_write(FileCache(), pos, buffer, _length); } @@ -1081,8 +1260,8 @@ Inode::FillGapWithZeros(off_t pos, off_t newSize) { // ToDo: we currently do anything here, same as original BFS! //if (pos >= newSize) - return B_OK; - + return B_OK; +#if 0 block_run run; off_t offset; if (FindBlockRun(pos, run, offset) < B_OK) @@ -1153,6 +1332,7 @@ Inode::FillGapWithZeros(off_t pos, off_t newSize) RETURN_ERROR(B_BAD_VALUE); } return B_OK; +#endif } @@ -1162,7 +1342,7 @@ Inode::FillGapWithZeros(off_t pos, off_t newSize) */ status_t -Inode::AllocateBlockArray(Transaction *transaction, block_run &run) +Inode::AllocateBlockArray(Transaction &transaction, block_run &run) { if (!run.IsZero()) return B_BAD_VALUE; @@ -1176,21 +1356,18 @@ Inode::AllocateBlockArray(Transaction *transaction, block_run &run) off_t block = fVolume->ToBlock(run); for (int32 i = 0; i < run.Length(); i++) { - block_run *runs = (block_run *)cached.SetTo(block + i, true); + block_run *runs = (block_run *)cached.SetToWritable(transaction, block + i, true); if (runs == NULL) return B_IO_ERROR; - - if (cached.WriteBack(transaction) < B_OK) - return B_IO_ERROR; } return B_OK; } status_t -Inode::GrowStream(Transaction *transaction, off_t size) +Inode::GrowStream(Transaction &transaction, off_t size) { - data_stream *data = &Node()->data; + data_stream *data = &Node().data; // is the data stream already large enough to hold the new size? // (can be the case with preallocated blocks) @@ -1316,6 +1493,8 @@ Inode::GrowStream(Transaction *transaction, off_t size) if (runs != NULL) { // try to insert the run to the last one - note that this doesn't // take block borders into account, so it could be further optimized + cached.MakeWritable(transaction); + int32 last = free - 1; if (free > 0 && runs[last].MergeableWith(run)) runs[last].length = HOST_ENDIAN_TO_BFS_INT16(runs[last].Length() + run.Length()); @@ -1324,8 +1503,6 @@ Inode::GrowStream(Transaction *transaction, off_t size) data->max_indirect_range = HOST_ENDIAN_TO_BFS_INT64(data->MaxIndirectRange() + (run.Length() << fVolume->BlockShift())); data->size = HOST_ENDIAN_TO_BFS_INT64(blocksNeeded > 0 ? data->MaxIndirectRange() : size); - - cached.WriteBack(transaction); continue; } } @@ -1420,10 +1597,6 @@ Inode::GrowStream(Transaction *transaction, off_t size) while (run.length != 0) { // get the indirect array block if (array == NULL) { - if (cached.Block() != NULL - && cached.WriteBack(transaction) < B_OK) - return B_IO_ERROR; - array = (block_run *)cached.SetTo(fVolume->ToBlock(data->double_indirect) + indirectIndex / runsPerBlock); if (array == NULL) @@ -1438,7 +1611,7 @@ Inode::GrowStream(Transaction *transaction, off_t size) return status; } - block_run *runs = (block_run *)cachedDirect.SetTo( + block_run *runs = (block_run *)cachedDirect.SetToWritable(transaction, fVolume->ToBlock(array[indirectIndex % runsPerBlock]) + index / runsPerBlock); if (runs == NULL) @@ -1453,9 +1626,6 @@ Inode::GrowStream(Transaction *transaction, off_t size) run.start = HOST_ENDIAN_TO_BFS_INT16(run.Start() + NUM_ARRAY_BLOCKS); run.length = HOST_ENDIAN_TO_BFS_INT16(run.Length() - NUM_ARRAY_BLOCKS); } while ((++index % runsPerBlock) != 0 && run.length); - - if (cachedDirect.WriteBack(transaction) < B_OK) - return B_IO_ERROR; } while ((index % runsPerArray) != 0 && run.length); if (++indirectIndex % runsPerBlock == 0) { @@ -1480,7 +1650,7 @@ Inode::GrowStream(Transaction *transaction, off_t size) status_t -Inode::FreeStaticStreamArray(Transaction *transaction, int32 level, block_run run, +Inode::FreeStaticStreamArray(Transaction &transaction, int32 level, block_run run, off_t size, off_t offset, off_t &max) { int32 indirectSize = 0; @@ -1506,7 +1676,7 @@ Inode::FreeStaticStreamArray(Transaction *transaction, int32 level, block_run ru offset += (off_t)index * indirectSize; for (int32 i = index / runsPerBlock; i < run.Length(); i++) { - block_run *array = (block_run *)cached.SetTo(blockNumber + i); + block_run *array = (block_run *)cached.SetToWritable(transaction, blockNumber + i); if (array == NULL) RETURN_ERROR(B_ERROR); @@ -1534,8 +1704,6 @@ Inode::FreeStaticStreamArray(Transaction *transaction, int32 level, block_run ru offset += indirectSize; } index = 0; - - cached.WriteBack(transaction); } return B_OK; } @@ -1550,7 +1718,7 @@ Inode::FreeStaticStreamArray(Transaction *transaction, int32 level, block_run ru */ status_t -Inode::FreeStreamArray(Transaction *transaction, block_run *array, uint32 arrayLength, +Inode::FreeStreamArray(Transaction &transaction, block_run *array, uint32 arrayLength, off_t size, off_t &offset, off_t &max) { off_t newOffset = offset; @@ -1593,9 +1761,9 @@ Inode::FreeStreamArray(Transaction *transaction, block_run *array, uint32 arrayL status_t -Inode::ShrinkStream(Transaction *transaction, off_t size) +Inode::ShrinkStream(Transaction &transaction, off_t size) { - data_stream *data = &Node()->data; + data_stream *data = &Node().data; if (data->MaxDoubleIndirectRange() > size) { FreeStaticStreamArray(transaction, 0, data->double_indirect, size, @@ -1613,13 +1781,13 @@ Inode::ShrinkStream(Transaction *transaction, off_t size) off_t offset = data->MaxDirectRange(); for (int32 i = 0; i < data->indirect.Length(); i++) { - block_run *array = (block_run *)cached.SetTo(block + i); + block_run *array = (block_run *)cached.SetToWritable(transaction, block + i); if (array == NULL) break; if (FreeStreamArray(transaction, array, fVolume->BlockSize() / sizeof(block_run), - size, offset, data->max_indirect_range) == B_OK) - cached.WriteBack(transaction); + size, offset, data->max_indirect_range) != B_OK) + return B_IO_ERROR; } if (data->max_direct_range == data->max_indirect_range) { fVolume->Free(transaction, data->indirect); @@ -1639,7 +1807,7 @@ Inode::ShrinkStream(Transaction *transaction, off_t size) status_t -Inode::SetFileSize(Transaction *transaction, off_t size) +Inode::SetFileSize(Transaction &transaction, off_t size) { if (size < 0 // uncached files can't be resized (Stream::WriteAt() specifically @@ -1674,14 +1842,14 @@ Inode::SetFileSize(Transaction *transaction, off_t size) status_t -Inode::Append(Transaction *transaction, off_t bytes) +Inode::Append(Transaction &transaction, off_t bytes) { return SetFileSize(transaction, Size() + bytes); } status_t -Inode::Trim(Transaction *transaction) +Inode::Trim(Transaction &transaction) { status_t status = ShrinkStream(transaction, Size()); if (status < B_OK) @@ -1692,7 +1860,7 @@ Inode::Trim(Transaction *transaction) status_t -Inode::Free(Transaction *transaction) +Inode::Free(Transaction &transaction) { FUNCTION(); @@ -1718,7 +1886,7 @@ Inode::Free(Transaction *transaction) } if (WriteBack(transaction) < B_OK) - return B_ERROR; + return B_IO_ERROR; return fVolume->Free(transaction, BlockRun()); } @@ -1727,11 +1895,14 @@ Inode::Free(Transaction *transaction) status_t Inode::Sync() { + if (FileCache()) + return file_cache_sync(FileCache()); + // We may also want to flush the attribute's data stream to // disk here... (do we?) - data_stream *data = &Node()->data; - status_t status; + data_stream *data = &Node().data; + status_t status = B_OK; // flush direct range @@ -1739,8 +1910,8 @@ Inode::Sync() if (data->direct[i].IsZero()) return B_OK; - status = flush_blocks(fVolume->Device(), fVolume->ToBlock(data->direct[i]), - data->direct[i].Length()); +// status = flush_blocks(fVolume->Device(), fVolume->ToBlock(data->direct[i]), +// data->direct[i].Length()); if (status != B_OK) return status; } @@ -1763,7 +1934,7 @@ Inode::Sync() if (runs[i].IsZero()) return B_OK; - status = flush_blocks(fVolume->Device(), fVolume->ToBlock(runs[i]), runs[i].Length()); +// status = flush_blocks(fVolume->Device(), fVolume->ToBlock(runs[i]), runs[i].Length()); if (status != B_OK) return status; } @@ -1799,8 +1970,8 @@ Inode::Sync() // ToDo: combine single block_runs to bigger ones when // they are adjacent - status = flush_blocks(fVolume->Device(), fVolume->ToBlock(runs[i]), - runs[i].Length()); +// status = flush_blocks(fVolume->Device(), fVolume->ToBlock(runs[i]), +// runs[i].Length()); if (status != B_OK) return status; } @@ -1812,7 +1983,7 @@ Inode::Sync() status_t -Inode::Remove(Transaction *transaction, const char *name, off_t *_id, bool isDirectory) +Inode::Remove(Transaction &transaction, const char *name, off_t *_id, bool isDirectory) { BPlusTree *tree; if (GetTree(&tree) != B_OK) @@ -1872,8 +2043,8 @@ Inode::Remove(Transaction *transaction, const char *name, off_t *_id, bool isDir #endif // update the inode, so that no one will ever doubt it's deleted :-) - inode->Node()->flags |= HOST_ENDIAN_TO_BFS_INT32(INODE_DELETED); - inode->Node()->flags &= ~HOST_ENDIAN_TO_BFS_INT32(INODE_IN_USE); + inode->Node().flags |= HOST_ENDIAN_TO_BFS_INT32(INODE_DELETED); + inode->Node().flags &= ~HOST_ENDIAN_TO_BFS_INT32(INODE_IN_USE); // In balance to the Inode::Create() method, the main indices // are updated here (name, size, & last_modified) @@ -1892,10 +2063,7 @@ Inode::Remove(Transaction *transaction, const char *name, off_t *_id, bool isDir index.RemoveLastModified(transaction, inode); } - if (inode->WriteBack(transaction) < B_OK) - return B_ERROR; - - return B_OK; + return inode->WriteBack(transaction); } @@ -1912,13 +2080,13 @@ Inode::Remove(Transaction *transaction, const char *name, off_t *_id, bool isDir */ status_t -Inode::Create(Transaction *transaction, Inode *parent, const char *name, int32 mode, +Inode::Create(Transaction &transaction, Inode *parent, const char *name, int32 mode, int omode, uint32 type, off_t *_id, Inode **_inode) { FUNCTION(); block_run parentRun = parent ? parent->BlockRun() : block_run::Run(0, 0, 0); - Volume *volume = transaction->GetVolume(); + Volume *volume = transaction.GetVolume(); BPlusTree *tree = NULL; RecursiveLocker locker(volume->Lock()); @@ -1988,7 +2156,7 @@ Inode::Create(Transaction *transaction, Inode *parent, const char *name, int32 m // Initialize the parts of the bfs_inode structure that // InodeAllocator::New() hasn't touched yet - bfs_inode *node = inode->Node(); + bfs_inode *node = &inode->Node(); if (parent == NULL) { // we set the parent to itself in this case @@ -1998,7 +2166,7 @@ Inode::Create(Transaction *transaction, Inode *parent, const char *name, int32 m node->parent = parentRun; node->uid = HOST_ENDIAN_TO_BFS_INT32(geteuid()); - node->gid = HOST_ENDIAN_TO_BFS_INT32(parent ? parent->Node()->gid : getegid()); + node->gid = HOST_ENDIAN_TO_BFS_INT32(parent ? parent->Node().GroupID() : getegid()); // the group ID is inherited from the parent, if available node->type = HOST_ENDIAN_TO_BFS_INT32(type); @@ -2048,7 +2216,7 @@ Inode::Create(Transaction *transaction, Inode *parent, const char *name, int32 m if (tree) tree->Remove(transaction, name, inode->ID()); else if (parent != NULL && (mode & S_ATTR_DIR) != 0) - parent->Node()->attributes.SetTo(0, 0, 0); + parent->Node().attributes.SetTo(0, 0, 0); return status; } @@ -2135,13 +2303,15 @@ AttributeIterator::GetNext(char *name, size_t *_length, uint32 *_type, vnode_id // read attributes out of the small data section if (fCurrentSmallData >= 0) { - small_data *item = fInode->Node()->SmallDataStart(); + NodeGetter nodeGetter(fInode->GetVolume(), fInode); + const bfs_inode *node = nodeGetter.Node(); + const small_data *item = ((bfs_inode *)node)->SmallDataStart(); fInode->SmallDataLock().Lock(); int32 i = 0; for (;;item = item->Next()) { - if (item->IsLast(fInode->Node())) + if (item->IsLast(node)) break; if (item->NameSize() == FILE_NAME_NAME_LENGTH @@ -2152,7 +2322,7 @@ AttributeIterator::GetNext(char *name, size_t *_length, uint32 *_type, vnode_id break; } - if (!item->IsLast(fInode->Node())) { + if (!item->IsLast(node)) { strncpy(name, item->Name(), B_FILE_NAME_LENGTH); *_type = item->Type(); *_length = item->NameSize(); diff --git a/src/add-ons/kernel/file_systems/bfs/Inode.h b/src/add-ons/kernel/file_systems/bfs/Inode.h index 2eaf972396..b8dbffb310 100644 --- a/src/add-ons/kernel/file_systems/bfs/Inode.h +++ b/src/add-ons/kernel/file_systems/bfs/Inode.h @@ -1,10 +1,10 @@ +/* Inode - inode access functions + * + * Copyright 2001-2004, Axel Dörfler, axeld@pinc-software.de. + * This file may be used under the terms of the MIT License. + */ #ifndef INODE_H #define INODE_H -/* Inode - inode access functions -** -** Copyright 2001-2004, Axel Dörfler, axeld@pinc-software.de -** This file may be used under the terms of the OpenBeOS License. -*/ #include @@ -17,9 +17,6 @@ # define _IMPEXP_KERNEL #endif -#include -#include - #include #include @@ -28,12 +25,14 @@ #include "Lock.h" #include "Chain.h" #include "Debug.h" +#include "CachedBlock.h" class BPlusTree; class TreeIterator; class AttributeIterator; class InodeAllocator; +class NodeGetter; enum inode_type { @@ -46,59 +45,21 @@ enum inode_type { | S_ULONG_LONG_INDEX | S_FLOAT_INDEX | S_DOUBLE_INDEX) }; - -// The CachedBlock class is completely implemented as inlines. -// It should be used when cache single blocks to make sure they -// will be properly released after use (and it's also very -// convenient to use them). - -class CachedBlock { +class Inode { public: - CachedBlock(Volume *volume); - CachedBlock(Volume *volume, off_t block, bool empty = false); - CachedBlock(Volume *volume, block_run run, bool empty = false); - CachedBlock(CachedBlock *cached); - ~CachedBlock(); - - inline void Keep(); - inline void Unset(); - inline uint8 *SetTo(off_t block, bool empty = false); - inline uint8 *SetTo(block_run run, bool empty = false); - inline status_t WriteBack(Transaction *transaction); - - uint8 *Block() const { return fBlock; } - off_t BlockNumber() const { return fBlockNumber; } - uint32 BlockSize() const { return fVolume->BlockSize(); } - uint32 BlockShift() const { return fVolume->BlockShift(); } - - private: - CachedBlock(const CachedBlock &); - CachedBlock &operator=(const CachedBlock &); - // no implementation - - protected: - Volume *fVolume; - off_t fBlockNumber; - uint8 *fBlock; -}; - -//-------------------------------------- - -class Inode : public CachedBlock { - public: - Inode(Volume *volume, vnode_id id, bool empty = false, uint8 reenter = 0); - Inode(CachedBlock *cached); + Inode(Volume *volume, vnode_id id); + Inode(Volume *volume, Transaction &transaction, vnode_id id, mode_t mode, block_run &run); + //Inode(CachedBlock *cached); ~Inode(); - bfs_inode *Node() const { return (bfs_inode *)fBlock; } - vnode_id ID() const { return fVolume->ToVnode(fBlockNumber); } + //bfs_inode *Node() const { return (bfs_inode *)fBlock; } + vnode_id ID() const { return fID; } + off_t BlockNumber() const { return fVolume->VnodeToBlock(fID); } ReadWriteLock &Lock() { return fLock; } SimpleLock &SmallDataLock() { return fSmallDataLock; } + status_t WriteBack(Transaction &transaction); - mode_t Mode() const { return Node()->Mode(); } - uint32 Type() const { return Node()->Type(); } - int32 Flags() const { return Node()->Flags(); } bool IsContainer() const { return Mode() & (S_DIRECTORY | S_INDEX_DIR | S_ATTR_DIR); } // note, that this test will also be true for S_IFBLK (not that it's used in the fs :) bool IsDirectory() const { return (Mode() & (S_DIRECTORY | S_INDEX_DIR | S_ATTR_DIR)) == S_DIRECTORY; } @@ -113,12 +74,17 @@ class Inode : public CachedBlock { bool HasUserAccessableStream() const { return S_ISREG(Mode()); } // currently only files can be accessed with bfs_read()/bfs_write() - off_t Size() const { return Node()->data.Size(); } - off_t LastModified() const { return Node()->last_modified_time; } + mode_t Mode() const { return fNode.Mode(); } + uint32 Type() const { return fNode.Type(); } + int32 Flags() const { return fNode.Flags(); } + + off_t Size() const { return fNode.data.Size(); } + off_t LastModified() const { return fNode.last_modified_time; } + + const block_run &BlockRun() const { return fNode.inode_num; } + block_run &Parent() { return fNode.parent; } + block_run &Attributes() { return fNode.attributes; } - block_run &BlockRun() const { return Node()->inode_num; } - block_run &Parent() const { return Node()->parent; } - block_run &Attributes() const { return Node()->attributes; } Volume *GetVolume() const { return fVolume; } status_t InitCheck(bool checkNode = true); @@ -126,25 +92,25 @@ class Inode : public CachedBlock { status_t CheckPermissions(int accessMode) const; // small_data access methods - status_t MakeSpaceForSmallData(Transaction *transaction, const char *name, int32 length); - status_t RemoveSmallData(Transaction *transaction, const char *name); - status_t AddSmallData(Transaction *transaction, const char *name, uint32 type, + status_t MakeSpaceForSmallData(Transaction &transaction, bfs_inode *node, const char *name, int32 length); + status_t RemoveSmallData(Transaction &transaction, NodeGetter &node, const char *name); + status_t AddSmallData(Transaction &transaction, NodeGetter &node, const char *name, uint32 type, const uint8 *data, size_t length, bool force = false); - status_t GetNextSmallData(small_data **_smallData) const; - small_data *FindSmallData(const char *name) const; - const char *Name() const; + status_t GetNextSmallData(bfs_inode *node, small_data **_smallData) const; + small_data *FindSmallData(const bfs_inode *node, const char *name) const; + const char *Name(const bfs_inode *node) const; status_t GetName(char *buffer) const; - status_t SetName(Transaction *transaction, const char *name); + status_t SetName(Transaction &transaction, const char *name); // high-level attribute methods status_t ReadAttribute(const char *name, int32 type, off_t pos, uint8 *buffer, size_t *_length); - status_t WriteAttribute(Transaction *transaction, const char *name, int32 type, off_t pos, const uint8 *buffer, size_t *_length); - status_t RemoveAttribute(Transaction *transaction, const char *name); + status_t WriteAttribute(Transaction &transaction, const char *name, int32 type, off_t pos, const uint8 *buffer, size_t *_length); + status_t RemoveAttribute(Transaction &transaction, const char *name); // attribute methods status_t GetAttribute(const char *name, Inode **attribute); void ReleaseAttribute(Inode *attribute); - status_t CreateAttribute(Transaction *transaction, const char *name, uint32 type, Inode **attribute); + status_t CreateAttribute(Transaction &transaction, const char *name, uint32 type, Inode **attribute); // for directories only: status_t GetTree(BPlusTree **); @@ -154,25 +120,27 @@ class Inode : public CachedBlock { status_t FindBlockRun(off_t pos, block_run &run, off_t &offset); status_t ReadAt(off_t pos, uint8 *buffer, size_t *length); - status_t WriteAt(Transaction *transaction, off_t pos, const uint8 *buffer, size_t *length); + status_t WriteAt(Transaction &transaction, off_t pos, const uint8 *buffer, size_t *length); status_t FillGapWithZeros(off_t oldSize, off_t newSize); - status_t SetFileSize(Transaction *transaction, off_t size); - status_t Append(Transaction *transaction, off_t bytes); - status_t Trim(Transaction *transaction); + status_t SetFileSize(Transaction &transaction, off_t size); + status_t Append(Transaction &transaction, off_t bytes); + status_t Trim(Transaction &transaction); - status_t Free(Transaction *transaction); + status_t Free(Transaction &transaction); status_t Sync(); + bfs_inode &Node() { return fNode; } + // create/remove inodes - status_t Remove(Transaction *transaction, const char *name, off_t *_id = NULL, + status_t Remove(Transaction &transaction, const char *name, off_t *_id = NULL, bool isDirectory = false); - static status_t Create(Transaction *transaction, Inode *parent, const char *name, + static status_t Create(Transaction &transaction, Inode *parent, const char *name, int32 mode, int omode, uint32 type, off_t *_id = NULL, Inode **_inode = NULL); // index maintaining helper void UpdateOldSize() { fOldSize = Size(); } - void UpdateOldLastModified() { fOldLastModified = Node()->LastModifiedTime(); } + void UpdateOldLastModified() { fOldLastModified = Node().LastModifiedTime(); } off_t OldSize() { return fOldSize; } off_t OldLastModified() { return fOldLastModified; } @@ -186,36 +154,72 @@ class Inode : public CachedBlock { // no implementation friend void dump_inode(Inode &inode); - friend AttributeIterator; - friend InodeAllocator; + friend class AttributeIterator; + friend class InodeAllocator; - void Initialize(); - - status_t RemoveSmallData(small_data *item, int32 index); + status_t RemoveSmallData(bfs_inode *node, small_data *item, int32 index); void AddIterator(AttributeIterator *iterator); void RemoveIterator(AttributeIterator *iterator); - status_t FreeStaticStreamArray(Transaction *transaction, int32 level, block_run run, + status_t FreeStaticStreamArray(Transaction &transaction, int32 level, block_run run, off_t size, off_t offset, off_t &max); - status_t FreeStreamArray(Transaction *transaction, block_run *array, uint32 arrayLength, + status_t FreeStreamArray(Transaction &transaction, block_run *array, uint32 arrayLength, off_t size, off_t &offset, off_t &max); - status_t AllocateBlockArray(Transaction *transaction, block_run &run); - status_t GrowStream(Transaction *transaction, off_t size); - status_t ShrinkStream(Transaction *transaction, off_t size); + status_t AllocateBlockArray(Transaction &transaction, block_run &run); + status_t GrowStream(Transaction &transaction, off_t size); + status_t ShrinkStream(Transaction &transaction, off_t size); + private: + ReadWriteLock fLock; + Volume *fVolume; + vnode_id fID; BPlusTree *fTree; Inode *fAttributes; - ReadWriteLock fLock; + void *fCache; + bfs_inode fNode; off_t fOldSize; // we need those values to ensure we will remove off_t fOldLastModified; // the correct keys from the indices - void *fCache; mutable SimpleLock fSmallDataLock; Chain fIterators; }; +class NodeGetter : public CachedBlock { + public: + NodeGetter(Volume *volume) + : CachedBlock(volume) + { + } + + NodeGetter(Volume *volume, const Inode *inode) + : CachedBlock(volume) + { + SetTo(volume->VnodeToBlock(inode->ID())); + } + + NodeGetter(Volume *volume, Transaction &transaction, const Inode *inode, bool empty = false) + : CachedBlock(volume) + { + SetToWritable(transaction, volume->VnodeToBlock(inode->ID()), empty); + } + + ~NodeGetter() + { + } + + const bfs_inode * + SetToNode(const Inode *inode) + { + return (const bfs_inode *)SetTo(fVolume->VnodeToBlock(inode->ID())); + } + + const bfs_inode *Node() const { return (const bfs_inode *)Block(); } + bfs_inode *WritableNode() const { return (bfs_inode *)Block(); } +}; + + // The Vnode class provides a convenience layer upon get_vnode(), so that // you don't have to call put_vnode() anymore, which may make code more // readable in some cases @@ -241,13 +245,13 @@ class Vnode { Put(); } - status_t Get(Inode **inode) + status_t Get(Inode **_inode) { // should we check inode against NULL here? it should not be necessary #ifdef UNSAFE_GET_VNODE RecursiveLocker locker(fVolume->Lock()); #endif - return get_vnode(fVolume->ID(), fID, (void **)inode); + return get_vnode(fVolume->ID(), fID, (void **)_inode); } void Put() @@ -291,113 +295,20 @@ class AttributeIterator { }; -//-------------------------------------- -// inlines - - -inline -CachedBlock::CachedBlock(Volume *volume) - : - fVolume(volume), - fBlock(NULL) -{ -} - - -inline -CachedBlock::CachedBlock(Volume *volume, off_t block, bool empty) - : - fVolume(volume), - fBlock(NULL) -{ - SetTo(block, empty); -} - - -inline -CachedBlock::CachedBlock(Volume *volume, block_run run, bool empty) - : - fVolume(volume), - fBlock(NULL) -{ - SetTo(volume->ToBlock(run), empty); -} - - -inline -CachedBlock::CachedBlock(CachedBlock *cached) - : - fVolume(cached->fVolume), - fBlockNumber(cached->BlockNumber()), - fBlock(cached->fBlock) -{ - cached->Keep(); -} - - -inline -CachedBlock::~CachedBlock() -{ - Unset(); -} - - -inline void -CachedBlock::Keep() -{ - fBlock = NULL; -} - - -inline void -CachedBlock::Unset() -{ - if (fBlock != NULL) - release_block(fVolume->Device(), fBlockNumber); -} - - -inline uint8 * -CachedBlock::SetTo(off_t block, bool empty) -{ - Unset(); - fBlockNumber = block; - return fBlock = empty ? (uint8 *)get_empty_block(fVolume->Device(), block, BlockSize()) - : (uint8 *)get_block(fVolume->Device(), block, BlockSize()); -} - - -inline uint8 * -CachedBlock::SetTo(block_run run, bool empty) -{ - return SetTo(fVolume->ToBlock(run), empty); -} - - -inline status_t -CachedBlock::WriteBack(Transaction *transaction) -{ - if (transaction == NULL || fBlock == NULL) - RETURN_ERROR(B_BAD_VALUE); - - return transaction->WriteBlocks(fBlockNumber, fBlock); -} - - -/** Converts the "omode", the open flags given to bfs_open(), into +/** Converts the open mode, the open flags given to bfs_open(), into * access modes, e.g. since O_RDONLY requires read access to the * file, it will be converted to R_OK. */ inline int -oModeToAccess(int omode) +openModeToAccess(int openMode) { - omode &= O_RWMASK; - if (omode == O_RDONLY) + openMode &= O_RWMASK; + if (openMode == O_RDONLY) return R_OK; - else if (omode == O_WRONLY) + else if (openMode == O_WRONLY) return W_OK; - + return R_OK | W_OK; } diff --git a/src/add-ons/kernel/file_systems/bfs/Jamfile b/src/add-ons/kernel/file_systems/bfs/Jamfile index ec0560f7b6..d0e7ff90b8 100644 --- a/src/add-ons/kernel/file_systems/bfs/Jamfile +++ b/src/add-ons/kernel/file_systems/bfs/Jamfile @@ -3,28 +3,20 @@ SubDir OBOS_TOP src add-ons kernel file_systems bfs ; # save original optimization level oldOPTIM = $(OPTIM) ; +# R5 support is currently disabled! +# +# Have a look in src/tests/add-ons/kernel/file_systems/bfs/r5/ +# for an R5 compatible version. +# In order to make the current BFS version R5 compatible again, +# we would need to port over the Haiku cache API to R5. + # set some additional defines { local defines = - KEEP_WRONG_DIRENT_RECLEN UNSAFE_GET_VNODE #BFS_BIG_ENDIAN_ONLY ; - # By default, the R5 API version is used unless you define this - if $(COMPILE_FOR_ZETA) { - defines += COMPILE_FOR_ZETA ; - } - - # Enable OpenBFS to be compiled as a full BFS replacement. Will - # report itself as "bfs" instead of "obfs" (only R5 version) - if $(BFS_REPLACEMENT) { - defines += BFS_REPLACEMENT ; - bfsAddOnName = bfs ; - } else { - bfsAddOnName = obfs ; - } - if $(DEBUG) = 0 { # the gcc on BeOS doesn't compile BFS correctly with -O2 or more OPTIM = -O1 ; @@ -39,7 +31,7 @@ UsePrivateHeaders [ FDirName kernel ] ; # For kernel_cpp.cpp UsePrivateHeaders [ FDirName kernel disk_device_manager ] ; UsePrivateHeaders [ FDirName storage ] ; -KernelStaticLibrary libbfs : +KernelAddon bfs : kernel file_systems : BlockAllocator.cpp BPlusTree.cpp kernel_cpp.cpp @@ -50,37 +42,14 @@ KernelStaticLibrary libbfs : Query.cpp Utility.cpp Volume.cpp - BufferPool.cpp - ; -KernelAddon bfs : kernel file_systems : kernel_interface.cpp disk_device_interface.cpp - : libbfs.a - ; - -R5KernelAddon $(bfsAddOnName)_r5 : kernel file_systems : - kernel_interface_r5.cpp - : libbfs.a ; SEARCH on [ FGristFiles kernel_cpp.cpp ] = [ FDirName $(OBOS_TOP) src kernel core util ] ; -#----------------------------------------------------- - -rule InstallBFS -{ - Depends $(<) : $(>) ; -} - -actions ignore InstallBFS -{ - cp $(>) /boot/home/config/add-ons/kernel/file_systems/ -} - -InstallBFS install : obfs ; - # restore original optimization level OPTIM = $(oldOPTIM) ; diff --git a/src/add-ons/kernel/file_systems/bfs/Journal.cpp b/src/add-ons/kernel/file_systems/bfs/Journal.cpp index 2fde3995b4..488044f99d 100644 --- a/src/add-ons/kernel/file_systems/bfs/Journal.cpp +++ b/src/add-ons/kernel/file_systems/bfs/Journal.cpp @@ -1,8 +1,8 @@ /* Journal - transaction and logging -** -** Initial version by Axel Dörfler, axeld@pinc-software.de -** This file may be used under the terms of the OpenBeOS License. -*/ + * + * Copyright 2001-2004, Axel Dörfler, axeld@pinc-software.de. + * This file may be used under the terms of the MIT License. + */ #include "Journal.h" @@ -11,6 +11,7 @@ #include #include +#include Journal::Journal(Volume *volume) @@ -50,7 +51,7 @@ Journal::InitCheck() status_t -Journal::CheckLogEntry(int32 count, off_t *array) +Journal::CheckLogEntry(int32 count, const off_t *array) { // ToDo: check log entry integrity (block numbers and entry size) PRINT(("Log entry has %ld entries (%Ld)\n", count, array[0])); @@ -58,6 +59,11 @@ Journal::CheckLogEntry(int32 count, off_t *array) } +/** Replays an entry in the log. + * \a _start points to the entry in the log, and will be bumped to the next + * one if replaying succeeded. + */ + status_t Journal::ReplayLogEntry(int32 *_start) { @@ -72,8 +78,9 @@ Journal::ReplayLogEntry(int32 *_start) bool first = true; CachedBlock cached(fVolume); + while (count > 0) { - off_t *array = (off_t *)cached.SetTo(arrayBlock); + const off_t *array = (const off_t *)cached.SetTo(arrayBlock); if (array == NULL) return B_IO_ERROR; @@ -102,7 +109,7 @@ Journal::ReplayLogEntry(int32 *_start) for (; index < valuesInBlock && count-- > 0; index++) { PRINT(("replay block %Ld in log at %Ld!\n", array[index], blockNumber)); - uint8 *copy = cachedCopy.SetTo(logOffset + blockNumber); + const uint8 *copy = cachedCopy.SetTo(logOffset + blockNumber); if (copy == NULL) RETURN_ERROR(B_IO_ERROR); @@ -170,16 +177,10 @@ Journal::ReplayLog() */ void -Journal::blockNotify(off_t blockNumber, size_t numBlocks, void *arg) +Journal::blockNotify(int32 transactionID, void *arg) { log_entry *logEntry = (log_entry *)arg; - logEntry->cached_blocks -= numBlocks; - if (logEntry->cached_blocks > 0) { - // nothing to do yet... - return; - } - Journal *journal = logEntry->journal; disk_super_block &superBlock = journal->fVolume->SuperBlock(); bool update = false; @@ -222,13 +223,24 @@ Journal::WriteLogEntry() fTransactionsInEntry = 0; fHasChangedBlocks = false; + // insert all changed blocks into the log array + uint32 cookie = 0; + { + off_t blockNumber; + while (cache_next_block_in_transaction(fVolume->BlockCache(), fTransactionID, &cookie, + &blockNumber, NULL, NULL) == B_OK) { + fArray.Insert(blockNumber); + } + } + sorted_array *array = fArray.Array(); if (array == NULL || array->count == 0) return B_OK; // Make sure there is enough space in the log. // If that fails for whatever reason, panic! - force_cache_flush(fVolume->Device(), false); + // ToDo: +/* force_cache_flush(fVolume->Device(), false); int32 tries = fLogSize / 2 + 1; while (TransactionSize() > FreeLogBlocks() && tries-- > 0) force_cache_flush(fVolume->Device(), true); @@ -237,7 +249,7 @@ Journal::WriteLogEntry() fVolume->Panic(); return B_BAD_DATA; } - +*/ int32 blockShift = fVolume->BlockShift(); off_t logOffset = fVolume->ToBlock(fVolume->Log()) << blockShift; off_t logStart = fVolume->LogEnd(); @@ -257,14 +269,11 @@ Journal::WriteLogEntry() // Write logged blocks into the log - CachedBlock cached(fVolume); - for (int32 i = 0;i < array->count;i++) { + cookie = 0; + const uint8 *block; + while (cache_next_block_in_transaction(fVolume->BlockCache(), fTransactionID, &cookie, + NULL, (void **)&block, NULL) == B_OK) { // ToDo: combine blocks if possible (using iovecs)! - - uint8 *block = cached.SetTo(array->values[i]); - if (block == NULL) - return B_IO_ERROR; - write_pos(fVolume->Device(), logOffset + (logPosition << blockShift), block, fVolume->BlockSize()); logPosition = (logPosition + 1) % fLogSize; @@ -283,10 +292,10 @@ Journal::WriteLogEntry() fCurrent = logEntry; fUsed += logEntry->length; - - set_blocks_info(fVolume->Device(), &array->values[0], array->count, blockNotify, logEntry); } + cache_end_transaction(fVolume->BlockCache(), fTransactionID, blockNotify, fCurrent); + // If the log goes to the next round (the log is written as a // circular buffer), all blocks will be flushed out which is // possible because we don't have any locked blocks at this @@ -318,7 +327,7 @@ Journal::FlushLogAndBlocks() return status; // write the current log entry to disk - + if (TransactionSize() != 0) { status = WriteLogEntry(); if (status < B_OK) @@ -341,9 +350,17 @@ Journal::Lock(Transaction *owner) if (status == B_OK) fOwner = owner; +/* ToDo: // if the last transaction is older than 2 secs, start a new one if (fTransactionsInEntry != 0 && system_time() - fTimestamp > 2000000L) WriteLogEntry(); +*/ + + fTransactionID = cache_start_transaction(fVolume->BlockCache()); + if (fTransactionID < B_OK) { + fLock.Unlock(); + return fTransactionID; + } return B_OK; } @@ -357,6 +374,7 @@ Journal::Unlock(Transaction *owner, bool success) TransactionDone(success); + fTransactionID = -1; fTimestamp = system_time(); fOwner = NULL; fLock.Unlock(); @@ -383,6 +401,13 @@ Journal::CurrentTransaction() status_t Journal::TransactionDone(bool success) { + if (!success) { + cache_abort_transaction(fVolume->BlockCache(), fTransactionID); + return B_OK; + } + +// ToDo: +/* if (!success && fTransactionsInEntry == 0) { // we can safely abort the transaction sorted_array *array = fArray.Array(); @@ -404,6 +429,7 @@ Journal::TransactionDone(bool success) return B_OK; } +*/ return WriteLogEntry(); } @@ -412,6 +438,8 @@ Journal::TransactionDone(bool success) status_t Journal::LogBlocks(off_t blockNumber, const uint8 *buffer, size_t numBlocks) { + panic("LogBlocks() called!\n"); + // ToDo: that's for now - we should change the log file size here if (TransactionSize() + numBlocks + 1 > fLogSize) return B_DEVICE_FULL; @@ -425,25 +453,29 @@ Journal::LogBlocks(off_t blockNumber, const uint8 *buffer, size_t numBlocks) // Note, this is only necessary if this method is called with a buffer // different from the cached block buffer - which is unlikely but // we'll make sure this way (costs one cache lookup, though). - status_t status = cached_write(fVolume->Device(), blockNumber, buffer, 1, blockSize); + // ToDo: +/* status_t status = cached_write(fVolume->Device(), blockNumber, buffer, 1, blockSize); if (status < B_OK) return status; - +*/ continue; } // Insert the block into the transaction's array, and write the changes // back into the locked cache buffer fArray.Insert(blockNumber); - status_t status = cached_write_locked(fVolume->Device(), blockNumber, buffer, 1, blockSize); + + // ToDo: +/* status_t status = cached_write_locked(fVolume->Device(), blockNumber, buffer, 1, blockSize); if (status < B_OK) return status; - } +*/ } + // ToDo: // If necessary, flush the log, so that we have enough space for this transaction - if (TransactionSize() > FreeLogBlocks()) +/* if (TransactionSize() > FreeLogBlocks()) force_cache_flush(fVolume->Device(), true); - +*/ return B_OK; } diff --git a/src/add-ons/kernel/file_systems/bfs/Journal.h b/src/add-ons/kernel/file_systems/bfs/Journal.h index 33a397a590..9c83fe441b 100644 --- a/src/add-ons/kernel/file_systems/bfs/Journal.h +++ b/src/add-ons/kernel/file_systems/bfs/Journal.h @@ -1,10 +1,10 @@ +/* Journal - transaction and logging + * + * Copyright 2001-2004, Axel Dörfler, axeld@pinc-software.de. + * This file may be used under the terms of the MIT License. + */ #ifndef JOURNAL_H #define JOURNAL_H -/* Journal - transaction and logging -** -** Copyright 2001-2004, Axel Dörfler, axeld@pinc-software.de -** This file may be used under the terms of the OpenBeOS License. -*/ #include @@ -18,9 +18,6 @@ # define _IMPEXP_KERNEL #endif -#include -#include - #include "Volume.h" #include "Chain.h" #include "Utility.h" @@ -52,7 +49,7 @@ class Journal { status_t Lock(Transaction *owner); void Unlock(Transaction *owner, bool success); - status_t CheckLogEntry(int32 count, off_t *array); + status_t CheckLogEntry(int32 count, const off_t *array); status_t ReplayLogEntry(int32 *start); status_t ReplayLog(); @@ -64,13 +61,14 @@ class Journal { status_t FlushLogAndBlocks(); Volume *GetVolume() const { return fVolume; } + int32 TransactionID() const { return fTransactionID; } inline uint32 FreeLogBlocks() const; private: friend log_entry; - static void blockNotify(off_t blockNumber, size_t numBlocks, void *arg); + static void blockNotify(int32 transactionID, void *arg); status_t TransactionDone(bool success); Volume *fVolume; @@ -84,6 +82,7 @@ class Journal { log_entry *fCurrent; bool fHasChangedBlocks; bigtime_t fTimestamp; + int32 fTransactionID; }; @@ -143,10 +142,15 @@ class Transaction { if (fJournal == NULL) return B_NO_INIT; + // ToDo: implement this properly! +#if 0 return fJournal->LogBlocks(blockNumber, buffer, numBlocks); +#endif + return B_ERROR; } Volume *GetVolume() { return fJournal != NULL ? fJournal->GetVolume() : NULL; } + int32 ID() const { return fJournal->TransactionID(); } private: Transaction(const Transaction &); diff --git a/src/add-ons/kernel/file_systems/bfs/Stream.h b/src/add-ons/kernel/file_systems/bfs/Stream.h deleted file mode 100644 index beff1cc30d..0000000000 --- a/src/add-ons/kernel/file_systems/bfs/Stream.h +++ /dev/null @@ -1,636 +0,0 @@ -/* Stream - inode stream access functions -** -** Initial version by Axel Dörfler, axeld@pinc-software.de -** This file may be used under the terms of the OpenBeOS License. -*/ - - -#include - - -// The classes in the namespace "Access" provide different type of access -// to the inode's data stream. -// Uncached accesses the underlaying device directly, Cached uses the -// standard cache, while Logged directs write accesses through the log. -// -// The classes interface is similar to the one of the CachedBlock class, -// but adds two other (static) functions for reading/writing several -// blocks at once. -// We don't use a real pure virtual interface as the class base, but we -// provide the same mechanism using templates. - -namespace Access { - -class Uncached { - public: - Uncached(Volume *volume); - Uncached(Volume *volume, off_t block, bool empty = false); - Uncached(Volume *volume, block_run run, bool empty = false); - ~Uncached(); - - void Unset(); - uint8 *SetTo(off_t block, bool empty = false); - uint8 *SetTo(block_run run, bool empty = false); - status_t WriteBack(Transaction *transaction); - - uint8 *Block() const { return fBlock; } - off_t BlockNumber() const { return fBlockNumber; } - uint32 BlockSize() const { return fVolume->BlockSize(); } - uint32 BlockShift() const { return fVolume->BlockShift(); } - - static status_t Read(Volume *volume, block_run run, uint8 *buffer); - static status_t Write(Transaction *transaction, Volume *volume, block_run run, const uint8 *buffer); - - private: - Volume *fVolume; - off_t fBlockNumber; - uint8 *fBlock; -}; - -class Cached : public CachedBlock { - public: - Cached(Volume *volume); - Cached(Volume *volume, off_t block, bool empty = false); - Cached(Volume *volume, block_run run, bool empty = false); - - status_t WriteBack(Transaction *transaction); - static status_t Read(Volume *volume, block_run run, uint8 *buffer); - static status_t Write(Transaction *transaction, Volume *volume, block_run run, const uint8 *buffer); -}; - -class Logged : public CachedBlock { - public: - Logged(Volume *volume); - Logged(Volume *volume,off_t block, bool empty = false); - Logged(Volume *volume, block_run run, bool empty = false); - - static status_t Read(Volume *volume, block_run run, uint8 *buffer); - static status_t Write(Transaction *transaction, Volume *volume, block_run run, const uint8 *buffer); -}; - - -Uncached::Uncached(Volume *volume) - : - fVolume(volume), - fBlock(NULL) -{ -} - - -Uncached::Uncached(Volume *volume,off_t block, bool empty) - : - fVolume(volume), - fBlock(NULL) -{ - SetTo(block,empty); -} - - -Uncached::Uncached(Volume *volume,block_run run,bool empty) - : - fVolume(volume), - fBlock(NULL) -{ - SetTo(volume->ToBlock(run),empty); -} - - -Uncached::~Uncached() -{ - Unset(); -} - - -void -Uncached::Unset() -{ - if (fBlock != NULL) - fVolume->Pool().PutBuffer((void *)fBlock); -} - - -uint8 * -Uncached::SetTo(off_t block, bool empty) -{ - Unset(); - fBlockNumber = block; - if (fVolume->Pool().GetBuffer((void **)&fBlock) < B_OK) - return NULL; - - if (empty) - memset(fBlock, 0, BlockSize()); - else - read_pos(fVolume->Device(), fBlockNumber << BlockShift(), fBlock, BlockSize()); - - return fBlock; -} - - -uint8 * -Uncached::SetTo(block_run run, bool empty) -{ - return SetTo(fVolume->ToBlock(run), empty); -} - - -status_t -Uncached::WriteBack(Transaction *transaction) -{ - if (fBlock == NULL) - RETURN_ERROR(B_BAD_VALUE); - - return write_pos(fVolume->Device(), fBlockNumber << BlockShift(), fBlock, BlockSize()); -} - - -status_t -Uncached::Read(Volume *volume, block_run run, uint8 *buffer) -{ - return read_pos(volume->Device(), volume->ToBlock(run) << volume->BlockShift(), buffer, run.Length() << volume->BlockShift()); -} - - -status_t -Uncached::Write(Transaction *transaction, Volume *volume, block_run run, const uint8 *buffer) -{ - return write_pos(volume->Device(), volume->ToBlock(run) << volume->BlockShift(), buffer, run.Length() << volume->BlockShift()); -} - - -// #pragma mark - - - -Cached::Cached(Volume *volume) - : CachedBlock(volume) -{ -} - - -Cached::Cached(Volume *volume,off_t block,bool empty) - : CachedBlock(volume, block, empty) -{ -} - - -Cached::Cached(Volume *volume,block_run run,bool empty) - : CachedBlock(volume, run, empty) -{ -} - - -status_t -Cached::WriteBack(Transaction *transaction) -{ - if (transaction == NULL || fBlock == NULL) - RETURN_ERROR(B_BAD_VALUE); - - return fVolume->WriteBlocks(fBlockNumber, fBlock, 1); -} - - -status_t -Cached::Read(Volume *volume, block_run run, uint8 *buffer) -{ - return cached_read(volume->Device(), volume->ToBlock(run), buffer, run.Length(), volume->BlockSize()); -} - - -status_t -Cached::Write(Transaction *transaction, Volume *volume, block_run run, const uint8 *buffer) -{ - return volume->WriteBlocks(volume->ToBlock(run), buffer, run.Length()); -} - - -// #pragma mark - - - -Logged::Logged(Volume *volume) - : CachedBlock(volume) -{ -} - - -Logged::Logged(Volume *volume, off_t block, bool empty) - : CachedBlock(volume, block, empty) -{ -} - - -Logged::Logged(Volume *volume, block_run run, bool empty) - : CachedBlock(volume, run, empty) -{ -} - - -status_t -Logged::Read(Volume *volume, block_run run, uint8 *buffer) -{ - return cached_read(volume->Device(), volume->ToBlock(run), buffer, run.Length(), volume->BlockSize()); -} - - -status_t -Logged::Write(Transaction *transaction, Volume *volume, block_run run, const uint8 *buffer) -{ - return transaction->WriteBlocks(volume->ToBlock(run), buffer, run.Length()); -} - -}; // namespace Access - - -// #pragma mark - - - -// The Stream template class allows to have only one straight-forward -// implementation of the FindBlockRun(), ReadAt(), and WriteAt() methods. -// They will access the disk through the given cache class only, which -// means either uncached, cached, or logged (see above). - -template -class Stream : public Inode { - private: - // The constructor only exists to make the compiler happy - it - // is never called in the code itself - Stream() : Inode(NULL, -1) {} - - public: - status_t FindBlockRun(off_t pos, block_run &run, off_t &offset); - status_t ReadAt(off_t pos, uint8 *buffer, size_t *length); - status_t WriteAt(Transaction *transaction, off_t pos, const uint8 *buffer, size_t *length); -}; - - -/** see Inode::FindBlockRun() for the documentation of this method */ - -template -status_t -Stream::FindBlockRun(off_t pos, block_run &run, off_t &offset) -{ - data_stream *data = &Node()->data; - - // find matching block run - - if (data->MaxDirectRange() > 0 && pos >= data->MaxDirectRange()) { - if (data->MaxDoubleIndirectRange() > 0 && pos >= data->MaxIndirectRange()) { - // access to double indirect blocks - - Cache cached(fVolume); - - off_t start = pos - data->MaxIndirectRange(); - int32 indirectSize = (1L << (INDIRECT_BLOCKS_SHIFT + cached.BlockShift())) - * (fVolume->BlockSize() / sizeof(block_run)); - int32 directSize = NUM_ARRAY_BLOCKS << cached.BlockShift(); - int32 index = start / indirectSize; - int32 runsPerBlock = cached.BlockSize() / sizeof(block_run); - - block_run *indirect = (block_run *)cached.SetTo( - fVolume->ToBlock(data->double_indirect) + index / runsPerBlock); - if (indirect == NULL) - RETURN_ERROR(B_ERROR); - - //printf("\tstart = %Ld, indirectSize = %ld, directSize = %ld, index = %ld\n",start,indirectSize,directSize,index); - //printf("\tlook for indirect block at %ld,%d\n",indirect[index].allocation_group,indirect[index].start); - - int32 current = (start % indirectSize) / directSize; - - indirect = (block_run *)cached.SetTo( - fVolume->ToBlock(indirect[index % runsPerBlock]) + current / runsPerBlock); - if (indirect == NULL) - RETURN_ERROR(B_ERROR); - - run = indirect[current % runsPerBlock]; - offset = data->MaxIndirectRange() + (index * indirectSize) + (current * directSize); - //printf("\tfCurrent = %ld, fRunFileOffset = %Ld, fRunBlockEnd = %Ld, fRun = %ld,%d\n",fCurrent,fRunFileOffset,fRunBlockEnd,fRun.allocation_group,fRun.start); - } else { - // access to indirect blocks - - int32 runsPerBlock = fVolume->BlockSize() / sizeof(block_run); - off_t runBlockEnd = data->MaxDirectRange(); - - Cache cached(fVolume); - off_t block = fVolume->ToBlock(data->indirect); - - for (int32 i = 0; i < data->indirect.Length(); i++) { - block_run *indirect = (block_run *)cached.SetTo(block + i); - if (indirect == NULL) - RETURN_ERROR(B_IO_ERROR); - - int32 current = -1; - while (++current < runsPerBlock) { - if (indirect[current].IsZero()) - break; - - runBlockEnd += indirect[current].Length() << cached.BlockShift(); - if (runBlockEnd > pos) { - run = indirect[current]; - offset = runBlockEnd - (run.Length() << cached.BlockShift()); - //printf("reading from indirect block: %ld,%d\n",fRun.allocation_group,fRun.start); - //printf("### indirect-run[%ld] = (%ld,%d,%d), offset = %Ld\n",fCurrent,fRun.allocation_group,fRun.start,fRun.Length(),fRunFileOffset); - return fVolume->ValidateBlockRun(run); - } - } - } - RETURN_ERROR(B_ERROR); - } - } else { - // access from direct blocks - - off_t runBlockEnd = 0LL; - int32 current = -1; - - while (++current < NUM_DIRECT_BLOCKS) { - if (data->direct[current].IsZero()) - break; - - runBlockEnd += data->direct[current].Length() << fVolume->BlockShift(); - if (runBlockEnd > pos) { - run = data->direct[current]; - offset = runBlockEnd - (run.Length() << fVolume->BlockShift()); - //printf("### run[%ld] = (%ld,%d,%d), offset = %Ld\n",fCurrent,fRun.allocation_group,fRun.start,fRun.Length(),fRunFileOffset); - return fVolume->ValidateBlockRun(run); - } - } - //PRINT(("FindBlockRun() failed in direct range: size = %Ld, pos = %Ld\n",data->size,pos)); - return B_ENTRY_NOT_FOUND; - } - return fVolume->ValidateBlockRun(run); -} - - -template -status_t -Stream::ReadAt(off_t pos, uint8 *buffer, size_t *_length) -{ - size_t length = *_length; - - // set/check boundaries for pos/length - if (pos < 0) - return B_BAD_VALUE; - if (pos >= Node()->data.Size() || length == 0) { - *_length = 0; - return B_NO_ERROR; - } - - if (pos + length > Node()->data.Size()) - length = Node()->data.Size() - pos; - - block_run run; - off_t offset; - if (FindBlockRun(pos, run, offset) < B_OK) { - *_length = 0; - RETURN_ERROR(B_BAD_VALUE); - } - - uint32 bytesRead = 0; - uint32 blockSize = fVolume->BlockSize(); - uint32 blockShift = fVolume->BlockShift(); - uint8 *block; - - // the first block_run we read could not be aligned to the block_size boundary - // (read partial block at the beginning) - - // pos % block_size == (pos - offset) % block_size, offset % block_size == 0 - if (pos % blockSize != 0) { - run.start = HOST_ENDIAN_TO_BFS_INT16(run.Start() + ((pos - offset) >> blockShift)); - run.length = HOST_ENDIAN_TO_BFS_INT16(run.Length() - ((pos - offset) >> blockShift)); - - Cache cached(fVolume,run); - if ((block = cached.Block()) == NULL) { - *_length = 0; - RETURN_ERROR(B_BAD_VALUE); - } - - bytesRead = blockSize - (pos % blockSize); - if (length < bytesRead) - bytesRead = length; - - memcpy(buffer, block + (pos % blockSize), bytesRead); - pos += bytesRead; - - length -= bytesRead; - if (length == 0) { - *_length = bytesRead; - return B_OK; - } - - if (FindBlockRun(pos, run, offset) < B_OK) { - *_length = bytesRead; - RETURN_ERROR(B_BAD_VALUE); - } - } - - // the first block_run is already filled in at this point - // read the following complete blocks using cached_read(), - // the last partial block is read using the generic Cache class - - bool partial = false; - - while (length > 0) { - // offset is the offset to the current pos in the block_run - run.start = HOST_ENDIAN_TO_BFS_INT16(run.Start() + ((pos - offset) >> blockShift)); - run.length = HOST_ENDIAN_TO_BFS_INT16(run.Length() - ((pos - offset) >> blockShift)); - - if (uint32(run.Length() << blockShift) > length) { - if (length < blockSize) { - Cache cached(fVolume, run); - if ((block = cached.Block()) == NULL) { - *_length = bytesRead; - RETURN_ERROR(B_BAD_VALUE); - } - memcpy(buffer + bytesRead, block, length); - bytesRead += length; - break; - } - run.length = HOST_ENDIAN_TO_BFS_INT16(length >> blockShift); - partial = true; - } - - if (Cache::Read(fVolume, run, buffer + bytesRead) < B_OK) { - *_length = bytesRead; - RETURN_ERROR(B_BAD_VALUE); - } - - int32 bytes = run.Length() << blockShift; -#ifdef DEBUG - if ((uint32)bytes > length) - DEBUGGER(("bytes greater than length")); -#endif - length -= bytes; - bytesRead += bytes; - if (length == 0) - break; - - pos += bytes; - - if (partial) { - // if the last block was read only partially, point block_run - // to the remaining part - run.start = HOST_ENDIAN_TO_BFS_INT16(run.Start() + run.Length()); - run.length = HOST_ENDIAN_TO_BFS_INT16(1); - offset = pos; - } else if (FindBlockRun(pos, run, offset) < B_OK) { - *_length = bytesRead; - RETURN_ERROR(B_BAD_VALUE); - } - } - - *_length = bytesRead; - return B_OK; -} - - -template -status_t -Stream::WriteAt(Transaction *transaction, off_t pos, const uint8 *buffer, size_t *_length) -{ - size_t length = *_length; - - // set/check boundaries for pos/length - if (pos < 0) - return B_BAD_VALUE; - - if (pos + length > Size()) { - off_t oldSize = Size(); - - // uncached files can't be resized (Inode::SetFileSize() also - // doesn't allow this, but this way we don't have to start a - // transaction to find out). - if (Flags() & INODE_NO_CACHE) - return B_BAD_VALUE; - - // the transaction doesn't have to be started already - // ToDo: what's that INODE_NO_TRANSACTION flag good for again? - if ((Flags() & INODE_NO_TRANSACTION) == 0 - && !transaction->IsStarted()) - transaction->Start(fVolume, BlockNumber()); - - // let's grow the data stream to the size needed - status_t status = SetFileSize(transaction, pos + length); - if (status < B_OK) { - *_length = 0; - RETURN_ERROR(status); - } - // If the position of the write was beyond the file size, we - // have to fill the gap between that position and the old file - // size with zeros. - FillGapWithZeros(oldSize, pos); - } - - // If we don't want to write anything, we can now return (we may - // just have changed the file size using the position parameter) - if (length == 0) - return B_OK; - - block_run run; - off_t offset; - if (FindBlockRun(pos, run, offset) < B_OK) { - *_length = 0; - RETURN_ERROR(B_BAD_VALUE); - } - - bool logStream = (Flags() & INODE_LOGGED) == INODE_LOGGED; - if (logStream - && !transaction->IsStarted()) - transaction->Start(fVolume, BlockNumber()); - - uint32 bytesWritten = 0; - uint32 blockSize = fVolume->BlockSize(); - uint32 blockShift = fVolume->BlockShift(); - uint8 *block; - - // the first block_run we write could not be aligned to the block_size boundary - // (write partial block at the beginning) - - // pos % block_size == (pos - offset) % block_size, offset % block_size == 0 - if (pos % blockSize != 0) { - run.start = HOST_ENDIAN_TO_BFS_INT16(run.Start() + ((pos - offset) >> blockShift)); - run.length = HOST_ENDIAN_TO_BFS_INT16(run.Length() - ((pos - offset) >> blockShift)); - - Cache cached(fVolume, run); - if ((block = cached.Block()) == NULL) { - *_length = 0; - RETURN_ERROR(B_BAD_VALUE); - } - - bytesWritten = blockSize - (pos % blockSize); - if (length < bytesWritten) - bytesWritten = length; - - memcpy(block + (pos % blockSize),buffer,bytesWritten); - - cached.WriteBack(transaction); - - pos += bytesWritten; - - length -= bytesWritten; - if (length == 0) { - *_length = bytesWritten; - return B_OK; - } - - if (FindBlockRun(pos, run, offset) < B_OK) { - *_length = bytesWritten; - RETURN_ERROR(B_BAD_VALUE); - } - } - - // the first block_run is already filled in at this point - // write the following complete blocks using Volume::WriteBlocks(), - // the last partial block is written using the generic Cache class - - bool partial = false; - - while (length > 0) { - // offset is the offset to the current pos in the block_run - run.start = HOST_ENDIAN_TO_BFS_INT16(run.Start() + ((pos - offset) >> blockShift)); - run.length = HOST_ENDIAN_TO_BFS_INT16(run.Length() - ((pos - offset) >> blockShift)); - - if (uint32(run.Length() << blockShift) > length) { - if (length < blockSize) { - Cache cached(fVolume,run); - if ((block = cached.Block()) == NULL) { - *_length = bytesWritten; - RETURN_ERROR(B_BAD_VALUE); - } - memcpy(block, buffer + bytesWritten, length); - - cached.WriteBack(transaction); - - bytesWritten += length; - break; - } - run.length = HOST_ENDIAN_TO_BFS_INT16(length >> blockShift); - partial = true; - } - - if (Cache::Write(transaction, fVolume, run, buffer + bytesWritten) < B_OK) { - *_length = bytesWritten; - RETURN_ERROR(B_BAD_VALUE); - } - - int32 bytes = run.Length() << blockShift; - length -= bytes; - bytesWritten += bytes; - if (length == 0) - break; - - pos += bytes; - - if (partial) { - // if the last block was written only partially, point block_run - // to the remaining part - run.start = HOST_ENDIAN_TO_BFS_INT16(run.Start() + run.Length()); - run.length = HOST_ENDIAN_TO_BFS_INT16(1); - offset = pos; - } else if (FindBlockRun(pos, run, offset) < B_OK) { - *_length = bytesWritten; - RETURN_ERROR(B_BAD_VALUE); - } - } - - *_length = bytesWritten; - - return B_OK; -} - diff --git a/src/add-ons/kernel/file_systems/bfs/ToDo b/src/add-ons/kernel/file_systems/bfs/ToDo index 8a4f8be104..3b721cfec6 100644 --- a/src/add-ons/kernel/file_systems/bfs/ToDo +++ b/src/add-ons/kernel/file_systems/bfs/ToDo @@ -1,4 +1,4 @@ -BFS - ToDo, May 26th, 2003 +BFS - ToDo, November 12th, 2004 ----- BlockAllocator @@ -76,7 +76,6 @@ kernel_interface general stuff - There are also some comments with a leading "ToDo:" directly in the code which may not be mentioned here. - - implement mkbfs (try to do it in OpenBeOS style directly - only write the super block from user space) ----- diff --git a/src/add-ons/kernel/file_systems/bfs/Volume.cpp b/src/add-ons/kernel/file_systems/bfs/Volume.cpp index 799a0f8c83..e4b4197985 100644 --- a/src/add-ons/kernel/file_systems/bfs/Volume.cpp +++ b/src/add-ons/kernel/file_systems/bfs/Volume.cpp @@ -1,8 +1,8 @@ /* Volume - BFS super block, mounting, etc. -** -** Initial version by Axel Dörfler, axeld@pinc-software.de -** This file may be used under the terms of the OpenBeOS License. -*/ + * + * Copyright 2001-2004, Axel Dörfler, axeld@pinc-software.de. + * This file may be used under the terms of the MIT License. + */ #include "Debug.h" @@ -40,8 +40,8 @@ class DeviceOpener { ~DeviceOpener(); int Open(const char *device, int mode); - status_t InitCache(off_t numBlocks); - void RemoveCache(int mode); + void *InitCache(off_t numBlocks, uint32 blockSize); + void RemoveCache(bool allowWrites); void Keep(); @@ -51,13 +51,13 @@ class DeviceOpener { private: int fDevice; - bool fCached; + void *fBlockCache; }; DeviceOpener::DeviceOpener(const char *device, int mode) : - fCached(false) + fBlockCache(NULL) { Open(device, mode); } @@ -66,14 +66,13 @@ DeviceOpener::DeviceOpener(const char *device, int mode) DeviceOpener::~DeviceOpener() { if (fDevice >= B_OK) { + RemoveCache(false); close(fDevice); - if (fCached) - remove_cached_device_blocks(fDevice, NO_WRITES); } } -int +int DeviceOpener::Open(const char *device, int mode) { fDevice = open(device, mode); @@ -81,30 +80,25 @@ DeviceOpener::Open(const char *device, int mode) } -status_t -DeviceOpener::InitCache(off_t numBlocks) +void * +DeviceOpener::InitCache(off_t numBlocks, uint32 blockSize) { - if (init_cache_for_device(fDevice, numBlocks) == B_OK) { - fCached = true; - return B_OK; - } - - return B_ERROR; + return block_cache_create(fDevice, numBlocks, blockSize); } -void -DeviceOpener::RemoveCache(int mode) +void +DeviceOpener::RemoveCache(bool allowWrites) { - if (!fCached) + if (fBlockCache == NULL) return; - remove_cached_device_blocks(fDevice, mode); - fCached = false; + block_cache_delete(fBlockCache, allowWrites); + fBlockCache = NULL; } -void +void DeviceOpener::Keep() { fDevice = -1; @@ -115,7 +109,7 @@ DeviceOpener::Keep() * to compute the size, or fstat() if that failed. */ -status_t +status_t DeviceOpener::GetSize(off_t *_size, uint32 *_blockSize) { device_geometry geometry; @@ -247,7 +241,7 @@ Volume::IsValidSuperBlock() } -void +void Volume::Panic() { FATAL(("we have to panic... switch to read-only mode!\n")); @@ -325,7 +319,7 @@ Volume::Mount(const char *deviceName, uint32 flags) fLogStart = fSuperBlock.LogStart(); fLogEnd = fSuperBlock.LogEnd(); - if (opener.InitCache(NumBlocks()) != B_OK) + if ((fBlockCache = opener.InitCache(NumBlocks(), fBlockSize)) == NULL) return B_ERROR; fJournal = new Journal(this); @@ -392,14 +386,14 @@ Volume::Unmount() delete fIndicesNode; - remove_cached_device_blocks(fDevice, IsReadOnly() ? NO_WRITES : ALLOW_WRITES); + block_cache_delete(fBlockCache, !IsReadOnly()); close(fDevice); return B_OK; } -status_t +status_t Volume::Sync() { return fJournal->FlushLogAndBlocks(); @@ -421,7 +415,7 @@ Volume::ValidateBlockRun(block_run run) } -block_run +block_run Volume::ToBlockRun(off_t block) const { block_run run; @@ -433,7 +427,7 @@ Volume::ToBlockRun(off_t block) const status_t -Volume::CreateIndicesRoot(Transaction *transaction) +Volume::CreateIndicesRoot(Transaction &transaction) { off_t id; status_t status = Inode::Create(transaction, NULL, NULL, @@ -446,14 +440,14 @@ Volume::CreateIndicesRoot(Transaction *transaction) } -status_t -Volume::AllocateForInode(Transaction *transaction, const Inode *parent, mode_t type, block_run &run) +status_t +Volume::AllocateForInode(Transaction &transaction, const Inode *parent, mode_t type, block_run &run) { return fBlockAllocator.AllocateForInode(transaction, &parent->BlockRun(), type, run); } -status_t +status_t Volume::WriteSuperBlock() { if (write_pos(fDevice, 512, &fSuperBlock, sizeof(disk_super_block)) != sizeof(disk_super_block)) @@ -484,7 +478,7 @@ Volume::UpdateLiveQueries(Inode *inode, const char *attribute, int32 type, const * the queries - it wouldn't safe you anything in this case. */ -bool +bool Volume::CheckForLiveQuery(const char *attribute) { // ToDo: check for a live query that depends on the specified attribute @@ -492,7 +486,7 @@ Volume::CheckForLiveQuery(const char *attribute) } -void +void Volume::AddQuery(Query *query) { if (fQueryLock.Lock() < B_OK) @@ -504,7 +498,7 @@ Volume::AddQuery(Query *query) } -void +void Volume::RemoveQuery(Query *query) { if (fQueryLock.Lock() < B_OK) diff --git a/src/add-ons/kernel/file_systems/bfs/Volume.h b/src/add-ons/kernel/file_systems/bfs/Volume.h index ae74bba412..e7bd3d3fa6 100644 --- a/src/add-ons/kernel/file_systems/bfs/Volume.h +++ b/src/add-ons/kernel/file_systems/bfs/Volume.h @@ -1,26 +1,18 @@ +/* Volume - BFS super block, mounting, etc. + * + * Copyright 2001-2004, Axel Dörfler, axeld@pinc-software.de. + * This file may be used under the terms of the MIT License. + */ #ifndef VOLUME_H #define VOLUME_H -/* Volume - BFS super block, mounting, etc. -** -** Initial version by Axel Dörfler, axeld@pinc-software.de -** This file may be used under the terms of the OpenBeOS License. -*/ #include #include - -extern "C" { - #ifndef _IMPEXP_KERNEL - # define _IMPEXP_KERNEL - #endif - #include "lock.h" - #include "cache.h" -} +#include #include "bfs.h" #include "BlockAllocator.h" -#include "BufferPool.h" #include "Chain.h" class Journal; @@ -82,22 +74,20 @@ class Volume { off_t ToVnode(off_t block) const { return block; } off_t VnodeToBlock(vnode_id id) const { return (off_t)id; } - status_t CreateIndicesRoot(Transaction *transaction); + status_t CreateIndicesRoot(Transaction &transaction); // block bitmap BlockAllocator &Allocator(); - status_t AllocateForInode(Transaction *transaction, const Inode *parent, + status_t AllocateForInode(Transaction &transaction, const Inode *parent, mode_t type, block_run &run); - status_t AllocateForInode(Transaction *transaction, const block_run *parent, + status_t AllocateForInode(Transaction &transaction, const block_run *parent, mode_t type, block_run &run); - status_t Allocate(Transaction *transaction,const Inode *inode, + status_t Allocate(Transaction &transaction, Inode *inode, off_t numBlocks, block_run &run, uint16 minimum = 1); - status_t Free(Transaction *transaction, block_run run); + status_t Free(Transaction &transaction, block_run run); // cache access status_t WriteSuperBlock(); - status_t WriteBlocks(off_t blockNumber, const uint8 *block, uint32 numBlocks); - void WriteCachedBlocksIfNecessary(); status_t FlushDevice(); // queries @@ -111,7 +101,7 @@ class Volume { status_t Sync(); Journal *GetJournal(off_t refBlock) const; - BufferPool &Pool(); + void *BlockCache() { return fBlockCache; } uint32 GetUniqueID(); @@ -142,13 +132,13 @@ class Volume { int32 fUniqueID; uint32 fFlags; - BufferPool fBufferPool; + void *fBlockCache; }; // inline functions -inline bool +inline bool Volume::IsReadOnly() const { return fFlags & VOLUME_READ_ONLY; @@ -169,51 +159,31 @@ Volume::Allocator() } -inline status_t -Volume::AllocateForInode(Transaction *transaction, const block_run *parent, mode_t type, block_run &run) +inline status_t +Volume::AllocateForInode(Transaction &transaction, const block_run *parent, mode_t type, block_run &run) { return fBlockAllocator.AllocateForInode(transaction, parent, type, run); } -inline status_t -Volume::Allocate(Transaction *transaction, const Inode *inode, off_t numBlocks, block_run &run, uint16 minimum) +inline status_t +Volume::Allocate(Transaction &transaction, Inode *inode, off_t numBlocks, block_run &run, uint16 minimum) { return fBlockAllocator.Allocate(transaction, inode, numBlocks, run, minimum); } -inline status_t -Volume::Free(Transaction *transaction, block_run run) +inline status_t +Volume::Free(Transaction &transaction, block_run run) { return fBlockAllocator.Free(transaction, run); } -inline status_t -Volume::WriteBlocks(off_t blockNumber, const uint8 *block, uint32 numBlocks) -{ - atomic_add(&fDirtyCachedBlocks, numBlocks); - return cached_write(fDevice, blockNumber, block, numBlocks, fSuperBlock.block_size); -} - - -inline void -Volume::WriteCachedBlocksIfNecessary() -{ - // the specific values are only valid for the current BeOS cache - if (fDirtyCachedBlocks > 128) { - force_cache_flush(fDevice, false); - atomic_add(&fDirtyCachedBlocks, -64); - } -} - - -inline status_t +inline status_t Volume::FlushDevice() { - fDirtyCachedBlocks = 0; - return flush_device(fDevice, 0); + return block_cache_sync(fBlockCache); } @@ -224,14 +194,7 @@ Volume::GetJournal(off_t /*refBlock*/) const } -inline BufferPool & -Volume::Pool() -{ - return fBufferPool; -} - - -inline uint32 +inline uint32 Volume::GetUniqueID() { return atomic_add(&fUniqueID, 1); diff --git a/src/add-ons/kernel/file_systems/bfs/cache.h b/src/add-ons/kernel/file_systems/bfs/cache.h deleted file mode 100644 index d57d1917d4..0000000000 --- a/src/add-ons/kernel/file_systems/bfs/cache.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - Copyright 1999-2001, Be Incorporated. All Rights Reserved. - This file may be used under the terms of the Be Sample Code License. -*/ - -#ifndef _CACHE_H_ -#define _CACHE_H_ - -#include - -#define ALLOW_WRITES 1 -#define NO_WRITES 0 - -#ifdef __cplusplus -extern "C" { -#endif - -extern int init_block_cache(int max_blocks, int flags); -extern void shutdown_block_cache(void); - -extern void force_cache_flush(int dev, int prefer_log_blocks); -extern int flush_blocks(int dev, off_t bnum, int nblocks); -extern int flush_device(int dev, int warn_locked); - -extern int init_cache_for_device(int fd, off_t max_blocks); -extern int remove_cached_device_blocks(int dev, int allow_write); - -extern void *get_block(int dev, off_t bnum, int bsize); -extern void *get_empty_block(int dev, off_t bnum, int bsize); -extern int release_block(int dev, off_t bnum); -extern int mark_blocks_dirty(int dev, off_t bnum, int nblocks); - - -extern int cached_read(int dev, off_t bnum, void *data, off_t num_blocks, int bsize); -extern int cached_write(int dev, off_t bnum, const void *data, - off_t num_blocks, int bsize); -extern int cached_write_locked(int dev, off_t bnum, const void *data, - off_t num_blocks, int bsize); -extern int set_blocks_info(int dev, off_t *blocks, int nblocks, - void (*func)(off_t bnum, size_t nblocks, void *arg), - void *arg); - -extern size_t read_phys_blocks (int fd, off_t bnum, void *data, uint num_blocks, int bsize); -extern size_t write_phys_blocks(int fd, off_t bnum, void *data, uint num_blocks, int bsize); - -#ifdef __cplusplus -} -#endif - -#endif /* _CACHE_H_ */ diff --git a/src/add-ons/kernel/file_systems/bfs/fsproto.h b/src/add-ons/kernel/file_systems/bfs/fsproto.h deleted file mode 100644 index 833c930b2e..0000000000 --- a/src/add-ons/kernel/file_systems/bfs/fsproto.h +++ /dev/null @@ -1,247 +0,0 @@ -/* - Copyright 1999-2001, Be Incorporated. All Rights Reserved. - This file may be used under the terms of the Be Sample Code License. -*/ - -#ifndef _FSPROTO_H -#define _FSPROTO_H - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -typedef dev_t nspace_id; -typedef ino_t vnode_id; - -/* - * PUBLIC PART OF THE FILE SYSTEM PROTOCOL - */ - -#define WSTAT_MODE 0x0001 -#define WSTAT_UID 0x0002 -#define WSTAT_GID 0x0004 -#define WSTAT_SIZE 0x0008 -#define WSTAT_ATIME 0x0010 -#define WSTAT_MTIME 0x0020 -#define WSTAT_CRTIME 0x0040 - -#define WFSSTAT_NAME 0x0001 - -#define SELECT_READ 1 -#define SELECT_WRITE 2 -#define SELECT_EXCEPTION 3 - -// missing ioctl() call added -#define IOCTL_FILE_UNCACHED_IO 10000 -#define IOCTL_CREATE_TIME 10002 -#define IOCTL_MODIFIED_TIME 10003 - - -// B_CUR_FS_API_VERSION is 2 for R5, but 3 on Zeta and Dano, because -// of the added calls for power management - so it's set to 3 here -// because that's a requirement to let Dano boot from our fs... -#ifdef COMPILE_FOR_ZETA -# define B_CUR_FS_API_VERSION 3 -#else -# define B_CUR_FS_API_VERSION 2 -#endif - -struct attr_info; -struct index_info; - -typedef int op_read_vnode(void *ns, vnode_id vnid, char r, void **node); -typedef int op_write_vnode(void *ns, void *node, char r); -typedef int op_remove_vnode(void *ns, void *node, char r); -typedef int op_secure_vnode(void *ns, void *node); -typedef int op_wake_vnode(void *ns, void *node); -typedef int op_suspend_vnode(void *ns, void *node); - -typedef int op_walk(void *ns, void *base, const char *file, char **newpath, - vnode_id *vnid); - -typedef int op_access(void *ns, void *node, int mode); - -typedef int op_create(void *ns, void *dir, const char *name, - int omode, int perms, vnode_id *vnid, void **cookie); -typedef int op_mkdir(void *ns, void *dir, const char *name, int perms); -typedef int op_symlink(void *ns, void *dir, const char *name, - const char *path); -typedef int op_link(void *ns, void *dir, const char *name, void *node); - -typedef int op_rename(void *ns, void *olddir, const char *oldname, - void *newdir, const char *newname); -typedef int op_unlink(void *ns, void *dir, const char *name); -typedef int op_rmdir(void *ns, void *dir, const char *name); - -typedef int op_readlink(void *ns, void *node, char *buf, size_t *bufsize); - -typedef int op_opendir(void *ns, void *node, void **cookie); -typedef int op_closedir(void *ns, void *node, void *cookie); -typedef int op_rewinddir(void *ns, void *node, void *cookie); -typedef int op_readdir(void *ns, void *node, void *cookie, long *num, - struct dirent *buf, size_t bufsize); - -typedef int op_open(void *ns, void *node, int omode, void **cookie); -typedef int op_close(void *ns, void *node, void *cookie); -typedef int op_free_cookie(void *ns, void *node, void *cookie); -typedef int op_read(void *ns, void *node, void *cookie, off_t pos, void *buf, - size_t *len); -typedef int op_write(void *ns, void *node, void *cookie, off_t pos, - const void *buf, size_t *len); -typedef int op_readv(void *ns, void *node, void *cookie, off_t pos, const iovec *vec, - size_t count, size_t *len); -typedef int op_writev(void *ns, void *node, void *cookie, off_t pos, const iovec *vec, - size_t count, size_t *len); -typedef int op_ioctl(void *ns, void *node, void *cookie, int cmd, void *buf, - size_t len); -typedef int op_setflags(void *ns, void *node, void *cookie, int flags); - -typedef int op_rstat(void *ns, void *node, struct stat *); -typedef int op_wstat(void *ns, void *node, struct stat *, long mask); -typedef int op_fsync(void *ns, void *node); - -typedef int op_select(void *ns, void *node, void *cookie, uint8 event, - uint32 ref, selectsync *sync); -typedef int op_deselect(void *ns, void *node, void *cookie, uint8 event, - selectsync *sync); - -typedef int op_initialize(const char *devname, void *parms, size_t len); -typedef int op_mount(nspace_id nsid, const char *devname, ulong flags, - void *parms, size_t len, void **data, vnode_id *vnid); -typedef int op_unmount(void *ns); -typedef int op_sync(void *ns); -typedef int op_rfsstat(void *ns, struct fs_info *); -typedef int op_wfsstat(void *ns, struct fs_info *, long mask); - - -typedef int op_open_attrdir(void *ns, void *node, void **cookie); -typedef int op_close_attrdir(void *ns, void *node, void *cookie); -typedef int op_rewind_attrdir(void *ns, void *node, void *cookie); -typedef int op_read_attrdir(void *ns, void *node, void *cookie, long *num, - struct dirent *buf, size_t bufsize); -typedef int op_remove_attr(void *ns, void *node, const char *name); -typedef int op_rename_attr(void *ns, void *node, const char *oldname, - const char *newname); -typedef int op_stat_attr(void *ns, void *node, const char *name, - struct attr_info *buf); - -typedef int op_write_attr(void *ns, void *node, const char *name, int type, - const void *buf, size_t *len, off_t pos); -typedef int op_read_attr(void *ns, void *node, const char *name, int type, - void *buf, size_t *len, off_t pos); - -typedef int op_open_indexdir(void *ns, void **cookie); -typedef int op_close_indexdir(void *ns, void *cookie); -typedef int op_rewind_indexdir(void *ns, void *cookie); -typedef int op_read_indexdir(void *ns, void *cookie, long *num, - struct dirent *buf, size_t bufsize); -typedef int op_create_index(void *ns, const char *name, int type, int flags); -typedef int op_remove_index(void *ns, const char *name); -typedef int op_rename_index(void *ns, const char *oldname, - const char *newname); -typedef int op_stat_index(void *ns, const char *name, struct index_info *buf); - -typedef int op_open_query(void *ns, const char *query, ulong flags, - port_id port, long token, void **cookie); -typedef int op_close_query(void *ns, void *cookie); -typedef int op_read_query(void *ns, void *cookie, long *num, - struct dirent *buf, size_t bufsize); - -typedef struct vnode_ops { - op_read_vnode (*read_vnode); - op_write_vnode (*write_vnode); - op_remove_vnode (*remove_vnode); - op_secure_vnode (*secure_vnode); - op_walk (*walk); - op_access (*access); - op_create (*create); - op_mkdir (*mkdir); - op_symlink (*symlink); - op_link (*link); - op_rename (*rename); - op_unlink (*unlink); - op_rmdir (*rmdir); - op_readlink (*readlink); - op_opendir (*opendir); - op_closedir (*closedir); - op_free_cookie (*free_dircookie); - op_rewinddir (*rewinddir); - op_readdir (*readdir); - op_open (*open); - op_close (*close); - op_free_cookie (*free_cookie); - op_read (*read); - op_write (*write); - op_readv (*readv); - op_writev (*writev); - op_ioctl (*ioctl); - op_setflags (*setflags); - op_rstat (*rstat); - op_wstat (*wstat); - op_fsync (*fsync); - op_initialize (*initialize); - op_mount (*mount); - op_unmount (*unmount); - op_sync (*sync); - op_rfsstat (*rfsstat); - op_wfsstat (*wfsstat); - op_select (*select); - op_deselect (*deselect); - op_open_indexdir (*open_indexdir); - op_close_indexdir (*close_indexdir); - op_free_cookie (*free_indexdircookie); - op_rewind_indexdir (*rewind_indexdir); - op_read_indexdir (*read_indexdir); - op_create_index (*create_index); - op_remove_index (*remove_index); - op_rename_index (*rename_index); - op_stat_index (*stat_index); - op_open_attrdir (*open_attrdir); - op_close_attrdir (*close_attrdir); - op_free_cookie (*free_attrdircookie); - op_rewind_attrdir (*rewind_attrdir); - op_read_attrdir (*read_attrdir); - op_write_attr (*write_attr); - op_read_attr (*read_attr); - op_remove_attr (*remove_attr); - op_rename_attr (*rename_attr); - op_stat_attr (*stat_attr); - op_open_query (*open_query); - op_close_query (*close_query); - op_free_cookie (*free_querycookie); - op_read_query (*read_query); - // for Dano compatibility only - op_wake_vnode (*wake_vnode); - op_suspend_vnode (*suspend_vnode); -} vnode_ops; - -#ifdef __cplusplus -extern "C" { -#endif - -extern _IMPEXP_KERNEL int new_path(const char *path, char **copy); -extern _IMPEXP_KERNEL void free_path(char *p); - -extern _IMPEXP_KERNEL void notify_select_event(selectsync *sync, uint32 ref); -extern _IMPEXP_KERNEL status_t is_vnode_removed(nspace_id nsid, vnode_id vnid); - -// The missing prototypes can be found in the fs_interface.h file. -// That part of the VFS is still compatible with BeOS :) - -#ifdef __cplusplus -} -#endif - -extern _EXPORT vnode_ops fs_entry; -extern _EXPORT int32 api_version; - -#endif /* _FSPROTO_H */ diff --git a/src/add-ons/kernel/file_systems/bfs/kernel_interface.cpp b/src/add-ons/kernel/file_systems/bfs/kernel_interface.cpp index 4fd8d2eb12..2144d54ae2 100644 --- a/src/add-ons/kernel/file_systems/bfs/kernel_interface.cpp +++ b/src/add-ons/kernel/file_systems/bfs/kernel_interface.cpp @@ -1,8 +1,8 @@ /* kernel_interface - file system interface to BeOS' vnode layer -** -** Copyright 2001-2004, Axel Dörfler, axeld@pinc-software.de -** This file may be used under the terms of the Haiku License. -*/ + * + * Copyright 2001-2004, Axel Dörfler, axeld@pinc-software.de. + * This file may be used under the terms of the MIT License. + */ #include "Debug.h" @@ -23,13 +23,6 @@ #include #include -#ifndef _IMPEXP_KERNEL -# define _IMPEXP_KERNEL -#endif - -#include -#include - #include #include #include @@ -53,7 +46,8 @@ strtod(const char */*start*/, char **/*end*/) static status_t -bfs_mount(mount_id mountID, const char *device, uint32 flags, const char *args, void **data, vnode_id *rootID) +bfs_mount(mount_id mountID, const char *device, uint32 flags, const char *args, + void **_data, vnode_id *_rootID) { FUNCTION(); @@ -62,11 +56,11 @@ bfs_mount(mount_id mountID, const char *device, uint32 flags, const char *args, return B_NO_MEMORY; status_t status; - if ((status = volume->Mount(device, B_MOUNT_READ_ONLY/*flags*/)) == B_OK) { - *data = volume; - *rootID = volume->ToVnode(volume->Root()); + if ((status = volume->Mount(device, flags)) == B_OK) { + *_data = volume; + *_rootID = volume->ToVnode(volume->Root()); INFORM(("mounted \"%s\" (root node at %Ld, device = %s)\n", - volume->Name(), *rootID, device)); + volume->Name(), *_rootID, device)); } else delete volume; @@ -232,7 +226,7 @@ restartIfBusy: // If not, we create a new one here if (inode == NULL) { - inode = new Inode(&cached); + inode = new Inode(volume, id); if (inode == NULL) return B_NO_MEMORY; @@ -242,12 +236,8 @@ restartIfBusy: } else status = inode->InitCheck(false); - if (status == B_OK) { - if (inode->IsFile() && inode->FileCache() == NULL) - inode->SetFileCache(file_cache_create(volume->ID(), inode->ID(), inode->Size(), volume->Device())); - + if (status == B_OK) *_node = inode; - } return status; } @@ -299,7 +289,7 @@ bfs_remove_vnode(void *_ns, void *_node, bool reenter) localTransaction.Start(volume, inode->BlockNumber()); } - status_t status = inode->Free(transaction); + status_t status = inode->Free(*transaction); if (status == B_OK) { if (transaction == &localTransaction) localTransaction.Done(); @@ -471,31 +461,6 @@ bfs_ioctl(void *_ns, void *_node, void *_cookie, ulong cmd, void *buffer, size_t Inode *inode = (Inode *)_node; switch (cmd) { - case IOCTL_FILE_UNCACHED_IO: - { - if (inode == NULL) - return B_BAD_VALUE; - - // if the inode is already set up for uncached access, bail out - if (inode->Flags() & INODE_NO_CACHE) { - FATAL(("File %Ld is already uncached\n", inode->ID())); - return B_ERROR; - } - - PRINT(("uncached access to inode %Ld\n", inode->ID())); - - // ToDo: sync the cache for this file! - // Unfortunately, we can't remove any blocks from the cache in BeOS, - // that means we can't guarantee consistency for the file contents - // spanning over both access modes. - - // request buffers for being able to access the file without - // using the cache or allocating memory - status_t status = volume->Pool().RequestBuffers(volume->BlockSize()); - if (status == B_OK) - inode->Node()->flags |= HOST_ENDIAN_TO_BFS_INT32(INODE_NO_CACHE); - return status; - } case BFS_IOCTL_VERSION: { uint32 *version = (uint32 *)buffer; @@ -511,7 +476,7 @@ bfs_ioctl(void *_ns, void *_node, void *_cookie, ulong cmd, void *buffer, size_t status_t status = allocator.StartChecking(control); if (status == B_OK && inode != NULL) - inode->Node()->flags |= HOST_ENDIAN_TO_BFS_INT32(INODE_CHKBFS_RUNNING); + inode->Node().flags |= HOST_ENDIAN_TO_BFS_INT32(INODE_CHKBFS_RUNNING); return status; } @@ -523,7 +488,7 @@ bfs_ioctl(void *_ns, void *_node, void *_cookie, ulong cmd, void *buffer, size_t status_t status = allocator.StopChecking(control); if (status == B_OK && inode != NULL) - inode->Node()->flags &= HOST_ENDIAN_TO_BFS_INT32(~INODE_CHKBFS_RUNNING); + inode->Node().flags &= HOST_ENDIAN_TO_BFS_INT32(~INODE_CHKBFS_RUNNING); return status; } @@ -543,14 +508,12 @@ bfs_ioctl(void *_ns, void *_node, void *_cookie, ulong cmd, void *buffer, size_t Transaction transaction(volume, 0); CachedBlock cached(volume); block_run run; - while (allocator.AllocateBlocks(&transaction, 8, 0, 64, 1, run) == B_OK) { + while (allocator.AllocateBlocks(transaction, 8, 0, 64, 1, run) == B_OK) { PRINT(("write block_run(%ld, %d, %d)\n", run.allocation_group, run.start, run.length)); for (int32 i = 0;i < run.length;i++) { - uint8 *block = cached.SetTo(run); - if (block != NULL) { + uint8 *block = cached.SetToWritable(transaction, run); + if (block != NULL) memset(block, 0, volume->BlockSize()); - cached.WriteBack(&transaction); - } } } return B_OK; @@ -560,11 +523,13 @@ bfs_ioctl(void *_ns, void *_node, void *_cookie, ulong cmd, void *buffer, size_t return B_OK; case 56744: if (inode != NULL) - dump_inode(inode->Node()); + dump_inode(&inode->Node()); return B_OK; case 56745: - if (inode != NULL) - dump_block((const char *)inode->Node(), volume->BlockSize()); + if (inode != NULL) { + NodeGetter node(volume, inode); + dump_block((const char *)node.Node(), volume->BlockSize()); + } return B_OK; #endif } @@ -630,26 +595,27 @@ bfs_read_stat(void *_ns, void *_node, struct stat *st) Volume *volume = (Volume *)_ns; Inode *inode = (Inode *)_node; - bfs_inode *node = inode->Node(); + + const bfs_inode &node = inode->Node(); st->st_dev = volume->ID(); st->st_ino = inode->ID(); st->st_nlink = 1; st->st_blksize = BFS_IO_SIZE; - st->st_uid = node->UserID(); - st->st_gid = node->GroupID(); - st->st_mode = node->Mode(); + st->st_uid = node.UserID(); + st->st_gid = node.GroupID(); + st->st_mode = node.Mode(); - if (inode->IsSymLink() && (node->Flags() & INODE_LONG_SYMLINK) == 0) { + if (inode->IsSymLink() && (node.Flags() & INODE_LONG_SYMLINK) == 0) { // symlinks report the size of the link here - st->st_size = strlen(node->short_symlink) + 1; + st->st_size = strlen(node.short_symlink) + 1; } else - st->st_size = node->data.Size(); + st->st_size = inode->Size(); st->st_atime = time(NULL); - st->st_mtime = st->st_ctime = (time_t)(node->LastModifiedTime() >> INODE_TIME_SHIFT); - st->st_crtime = (time_t)(node->CreateTime() >> INODE_TIME_SHIFT); + st->st_mtime = st->st_ctime = (time_t)(node.LastModifiedTime() >> INODE_TIME_SHIFT); + st->st_crtime = (time_t)(node.CreateTime() >> INODE_TIME_SHIFT); return B_NO_ERROR; } @@ -684,7 +650,7 @@ bfs_write_stat(void *_ns, void *_node, const struct stat *stat, uint32 mask) Transaction transaction(volume, inode->BlockNumber()); - bfs_inode *node = inode->Node(); + bfs_inode &node = inode->Node(); if (mask & FS_WRITE_STAT_SIZE) { // Since WSTAT_SIZE is the only thing that can fail directly, we @@ -694,7 +660,7 @@ bfs_write_stat(void *_ns, void *_node, const struct stat *stat, uint32 mask) return B_IS_A_DIRECTORY; if (inode->Size() != stat->st_size) { - status = inode->SetFileSize(&transaction, stat->st_size); + status = inode->SetFileSize(transaction, stat->st_size); if (status < B_OK) return status; @@ -702,34 +668,34 @@ bfs_write_stat(void *_ns, void *_node, const struct stat *stat, uint32 mask) inode->FillGapWithZeros(inode->OldSize(), inode->Size()); Index index(volume); - index.UpdateSize(&transaction, inode); + index.UpdateSize(transaction, inode); if ((mask & FS_WRITE_STAT_MTIME) == 0) - index.UpdateLastModified(&transaction, inode); + index.UpdateLastModified(transaction, inode); } } if (mask & FS_WRITE_STAT_MODE) { - PRINT(("original mode = %ld, stat->st_mode = %d\n", node->mode, stat->st_mode)); - node->mode = node->mode & ~S_IUMSK | stat->st_mode & S_IUMSK; + PRINT(("original mode = %ld, stat->st_mode = %d\n", node.Mode(), stat->st_mode)); + node.mode = HOST_ENDIAN_TO_BFS_INT32(node.Mode() & ~S_IUMSK | stat->st_mode & S_IUMSK); } if (mask & FS_WRITE_STAT_UID) - node->uid = stat->st_uid; + node.uid = HOST_ENDIAN_TO_BFS_INT32(stat->st_uid); if (mask & FS_WRITE_STAT_GID) - node->gid = stat->st_gid; + node.gid = HOST_ENDIAN_TO_BFS_INT32(stat->st_gid); if (mask & FS_WRITE_STAT_MTIME) { // Index::UpdateLastModified() will set the new time in the inode Index index(volume); - index.UpdateLastModified(&transaction, inode, + index.UpdateLastModified(transaction, inode, (bigtime_t)stat->st_mtime << INODE_TIME_SHIFT); } if (mask & FS_WRITE_STAT_CRTIME) { - node->create_time = (bigtime_t)stat->st_crtime << INODE_TIME_SHIFT; + node.create_time = HOST_ENDIAN_TO_BFS_INT64((bigtime_t)stat->st_crtime << INODE_TIME_SHIFT); } - if ((status = inode->WriteBack(&transaction)) == B_OK) + if ((status = inode->WriteBack(transaction)) == B_OK) transaction.Done(); notify_listener(B_STAT_CHANGED, volume->ID(), 0, 0, inode->ID(), NULL); @@ -774,7 +740,7 @@ bfs_create(void *_ns, void *_directory, const char *name, int omode, int mode, #endif Transaction transaction(volume, directory->BlockNumber()); - status = Inode::Create(&transaction, directory, name, S_FILE | (mode & S_IUMSK), + status = Inode::Create(transaction, directory, name, S_FILE | (mode & S_IUMSK), omode, 0, vnodeID); if (status >= B_OK) { @@ -817,23 +783,25 @@ bfs_create_symlink(void *_ns, void *_directory, const char *name, const char *pa Inode *link; off_t id; - status = Inode::Create(&transaction, directory, name, S_SYMLINK | 0777, 0, 0, &id, &link); + status = Inode::Create(transaction, directory, name, S_SYMLINK | 0777, 0, 0, &id, &link); if (status < B_OK) RETURN_ERROR(status); size_t length = strlen(path); if (length < SHORT_SYMLINK_NAME_LENGTH) { - strcpy(link->Node()->short_symlink, path); - status = link->WriteBack(&transaction); + strcpy(link->Node().short_symlink, path); } else { - link->Node()->flags |= HOST_ENDIAN_TO_BFS_INT32(INODE_LONG_SYMLINK | INODE_LOGGED); + link->Node().flags |= HOST_ENDIAN_TO_BFS_INT32(INODE_LONG_SYMLINK | INODE_LOGGED); // The following call will have to write the inode back, so // we don't have to do that here... - status = link->WriteAt(&transaction, 0, (const uint8 *)path, &length); + status = link->WriteAt(transaction, 0, (const uint8 *)path, &length); } // ToDo: would be nice if Inode::Create() would let the INODE_NOT_READY // flag set until here, so that it can be accessed directly + if (status == B_OK) + status = link->WriteBack(transaction); + // Inode::Create() left the inode locked in memory put_vnode(volume->ID(), id); @@ -881,7 +849,7 @@ bfs_unlink(void *_ns, void *_directory, const char *name) Transaction transaction(volume, directory->BlockNumber()); off_t id; - if ((status = directory->Remove(&transaction, name, &id)) == B_OK) { + if ((status = directory->Remove(transaction, name, &id)) == B_OK) { transaction.Done(); notify_listener(B_ENTRY_REMOVED, volume->ID(), directory->ID(), 0, id, NULL); @@ -968,7 +936,7 @@ bfs_rename(void *_ns, void *_oldDir, const char *oldName, void *_newDir, const c RETURN_ERROR(status); } - status = newTree->Insert(&transaction, (const uint8 *)newName, strlen(newName), id); + status = newTree->Insert(transaction, (const uint8 *)newName, strlen(newName), id); if (status == B_NAME_IN_USE) { // If there is already a file with that name, we have to remove // it, as long it's not a directory with files in it @@ -983,13 +951,13 @@ bfs_rename(void *_ns, void *_oldDir, const char *oldName, void *_newDir, const c if (vnode.Get(&other) < B_OK) return B_NAME_IN_USE; - status = newDirectory->Remove(&transaction, newName, NULL, other->IsDirectory()); + status = newDirectory->Remove(transaction, newName, NULL, other->IsDirectory()); if (status < B_OK) return status; notify_listener(B_ENTRY_REMOVED, volume->ID(), newDirectory->ID(), 0, clobber, NULL); - status = newTree->Insert(&transaction, (const uint8 *)newName, strlen(newName), id); + status = newTree->Insert(transaction, (const uint8 *)newName, strlen(newName), id); } if (status < B_OK) return status; @@ -1001,18 +969,18 @@ bfs_rename(void *_ns, void *_oldDir, const char *oldName, void *_newDir, const c // update the name only when they differ bool nameUpdated = false; if (strcmp(oldName, newName)) { - status = inode->SetName(&transaction, newName); + status = inode->SetName(transaction, newName); if (status == B_OK) { Index index(volume); - index.UpdateName(&transaction, oldName, newName, inode); + index.UpdateName(transaction, oldName, newName, inode); nameUpdated = true; } } if (status == B_OK) { - status = tree->Remove(&transaction, (const uint8 *)oldName, strlen(oldName), id); + status = tree->Remove(transaction, (const uint8 *)oldName, strlen(oldName), id); if (status == B_OK) { - inode->Node()->parent = newDirectory->BlockRun(); + inode->Parent() = newDirectory->BlockRun(); // if it's a directory, update the parent directory pointer // in its tree if necessary @@ -1020,17 +988,17 @@ bfs_rename(void *_ns, void *_oldDir, const char *oldName, void *_newDir, const c if (oldDirectory != newDirectory && inode->IsDirectory() && (status = inode->GetTree(&movedTree)) == B_OK) - status = movedTree->Replace(&transaction, (const uint8 *)"..", 2, newDirectory->ID()); + status = movedTree->Replace(transaction, (const uint8 *)"..", 2, newDirectory->ID()); + + if (status == B_OK) + status = inode->WriteBack(transaction); if (status == B_OK) { - status = inode->WriteBack(&transaction); - if (status == B_OK) { - transaction.Done(); + transaction.Done(); - notify_listener(B_ENTRY_MOVED, volume->ID(), oldDirectory->ID(), - newDirectory->ID(), id, newName); - return B_OK; - } + notify_listener(B_ENTRY_MOVED, volume->ID(), oldDirectory->ID(), + newDirectory->ID(), id, newName); + return B_OK; } // If we get here, something has gone wrong already! @@ -1039,25 +1007,25 @@ bfs_rename(void *_ns, void *_oldDir, const char *oldName, void *_newDir, const c // for us) // Anyway, if we overwrote a file in the target directory // this is lost now (only in-memory, not on-disk)... - bailStatus = tree->Insert(&transaction, (const uint8 *)oldName, strlen(oldName), id); + bailStatus = tree->Insert(transaction, (const uint8 *)oldName, strlen(oldName), id); if (movedTree != NULL) - movedTree->Replace(&transaction, (const uint8 *)"..", 2, oldDirectory->ID()); + movedTree->Replace(transaction, (const uint8 *)"..", 2, oldDirectory->ID()); } } if (bailStatus == B_OK && nameUpdated) { - bailStatus = inode->SetName(&transaction, oldName); + bailStatus = inode->SetName(transaction, oldName); if (status == B_OK) { // update inode and index - inode->WriteBack(&transaction); + inode->WriteBack(transaction); Index index(volume); - index.UpdateName(&transaction, newName, oldName, inode); + index.UpdateName(transaction, newName, oldName, inode); } } if (bailStatus == B_OK) - bailStatus = newTree->Remove(&transaction, (const uint8 *)newName, strlen(newName), id); + bailStatus = newTree->Remove(transaction, (const uint8 *)newName, strlen(newName), id); if (bailStatus < B_OK) volume->Panic(); @@ -1092,7 +1060,7 @@ bfs_open(void *_ns, void *_node, int omode, void **_cookie) //return B_IS_A_DIRECTORY; } - status_t status = inode->CheckPermissions(oModeToAccess(omode)); + status_t status = inode->CheckPermissions(openModeToAccess(omode)); if (status < B_OK) RETURN_ERROR(status); @@ -1119,7 +1087,7 @@ bfs_open(void *_ns, void *_node, int omode, void **_cookie) WriteLocked locked(inode->Lock()); Transaction transaction(volume, inode->BlockNumber()); - status_t status = inode->SetFileSize(&transaction, 0); + status_t status = inode->SetFileSize(transaction, 0); if (status < B_OK) { // bfs_free_cookie() is only called if this function is successful free(cookie); @@ -1150,7 +1118,7 @@ bfs_read(void *_ns, void *_node, void *_cookie, off_t pos, void *buffer, size_t } ReadLocked locked(inode->Lock()); - return file_cache_read(inode->FileCache(), pos, buffer, _length); + return inode->ReadAt(pos, (uint8 *)buffer, _length); } @@ -1184,7 +1152,7 @@ bfs_write(void *_ns, void *_node, void *_cookie, off_t pos, const void *buffer, // it might not be needed at all (the contents of // regular files aren't logged) - status_t status = inode->WriteAt(&transaction, pos, (const uint8 *)buffer, _length); + status_t status = inode->WriteAt(transaction, pos, (const uint8 *)buffer, _length); if (status == B_OK) transaction.Done(); @@ -1201,12 +1169,6 @@ bfs_write(void *_ns, void *_node, void *_cookie, off_t pos, const void *buffer, cookie->last_size = inode->Size(); cookie->last_notification = system_time(); } - - // This will flush the dirty blocks to disk from time to time. - // It's done here and not in Inode::WriteAt() so that it won't - // add to the duration of a transaction - it might even be a - // good idea to offload those calls to another thread - volume->WriteCachedBlocksIfNecessary(); } return status; @@ -1256,19 +1218,19 @@ bfs_free_cookie(void *_ns, void *_node, void *_cookie) Index index(volume); if (inode->OldSize() != inode->Size()) { - status = inode->Trim(&transaction); + status = inode->Trim(transaction); if (status < B_OK) FATAL(("Could not trim preallocated blocks!")); - index.UpdateSize(&transaction, inode); + index.UpdateSize(transaction, inode); changed = true; } if (inode->OldLastModified() != inode->LastModified()) { - index.UpdateLastModified(&transaction, inode, inode->LastModified()); + index.UpdateLastModified(transaction, inode, inode->LastModified()); changed = true; - + // updating the index doesn't write back the inode - inode->WriteBack(&transaction); + inode->WriteBack(transaction); } if (status == B_OK) @@ -1278,14 +1240,6 @@ bfs_free_cookie(void *_ns, void *_node, void *_cookie) notify_listener(B_STAT_CHANGED, volume->ID(), 0, 0, inode->ID(), NULL); } - if (inode->Flags() & INODE_NO_CACHE) { - volume->Pool().ReleaseBuffers(); - inode->Node()->flags &= HOST_ENDIAN_TO_BFS_INT32(~INODE_NO_CACHE); - // We don't need to save the inode, because INODE_NO_CACHE is a - // non-permanent flag which will be removed when the inode is loaded - // into memory. - } - if (inode->Flags() & INODE_CHKBFS_RUNNING) { // "chkbfs" exited abnormally, so we have to stop it here... FATAL(("check process was aborted!\n")); @@ -1340,7 +1294,7 @@ bfs_read_link(void *_ns, void *_node, char *buffer, size_t bufferSize) return B_OK; } - if (strlcpy(buffer, inode->Node()->short_symlink, bufferSize) > bufferSize) + if (strlcpy(buffer, inode->Node().short_symlink, bufferSize) > bufferSize) return B_BUFFER_OVERFLOW; return B_OK; @@ -1378,7 +1332,7 @@ bfs_create_dir(void *_ns, void *_directory, const char *name, int mode, vnode_id // Inode::Create() locks the inode if we pass the "id" parameter, but we // need it anyway off_t id; - status = Inode::Create(&transaction, directory, name, S_DIRECTORY | (mode & S_IUMSK), 0, 0, &id); + status = Inode::Create(transaction, directory, name, S_DIRECTORY | (mode & S_IUMSK), 0, 0, &id); if (status == B_OK) { *_newVnodeID = id; put_vnode(volume->ID(), id); @@ -1408,7 +1362,7 @@ bfs_remove_dir(void *_ns, void *_directory, const char *name) Transaction transaction(volume, directory->BlockNumber()); off_t id; - status_t status = directory->Remove(&transaction, name, &id, true); + status_t status = directory->Remove(transaction, name, &id, true); if (status == B_OK) { transaction.Done(); @@ -1475,11 +1429,7 @@ bfs_read_dir(void *_ns, void *_node, void *_cookie, struct dirent *dirent, dirent->d_dev = volume->ID(); dirent->d_ino = id; -#ifdef KEEP_WRONG_DIRENT_RECLEN - dirent->d_reclen = length; -#else dirent->d_reclen = sizeof(struct dirent) + length; -#endif *_num = 1; return B_OK; @@ -1602,11 +1552,7 @@ bfs_read_attrdir(void *_ns, void *node, void *_cookie, long *num, struct dirent Volume *volume = (Volume *)_ns; dirent->d_dev = volume->ID(); -#ifdef KEEP_WRONG_DIRENT_RECLEN - dirent->d_reclen = length; -#else dirent->d_reclen = sizeof(struct dirent) + length; -#endif *num = 1; return B_OK; @@ -1633,7 +1579,7 @@ bfs_remove_attr(void *_ns, void *_node, const char *name) #endif Transaction transaction(volume, inode->BlockNumber()); - status = inode->RemoveAttribute(&transaction, name); + status = inode->RemoveAttribute(transaction, name); if (status == B_OK) { transaction.Done(); @@ -1725,7 +1671,7 @@ bfs_write_attr(void *_ns, void *_node, const char *name, int type, const void *b #endif Transaction transaction(volume, inode->BlockNumber()); - status = inode->WriteAttribute(&transaction, name, type, pos, (const uint8 *)buffer, _length); + status = inode->WriteAttribute(transaction, name, type, pos, (const uint8 *)buffer, _length); if (status == B_OK) { transaction.Done(); @@ -1830,7 +1776,7 @@ bfs_read_indexdir(fs_volume _fs, fs_cookie _cookie, struct dirent *dirent, size_ static status_t bfs_create_index(fs_volume _fs, const char *name, uint32 type, uint32 flags) { - FUNCTION_START(("name = \"%s\", type = %d, flags = %d\n", name, type, flags)); + FUNCTION_START(("name = \"%s\", type = %ld, flags = %ld\n", name, type, flags)); if (_fs == NULL || name == NULL || *name == '\0') return B_BAD_VALUE; @@ -1849,7 +1795,7 @@ bfs_create_index(fs_volume _fs, const char *name, uint32 type, uint32 flags) Transaction transaction(volume, volume->Indices()); Index index(volume); - status_t status = index.Create(&transaction, name, type); + status_t status = index.Create(transaction, name, type); if (status == B_OK) transaction.Done(); @@ -1883,7 +1829,7 @@ bfs_remove_index(void *_ns, const char *name) #endif Transaction transaction(volume, volume->Indices()); - status_t status = indices->Remove(&transaction, name); + status_t status = indices->Remove(transaction, name); if (status == B_OK) transaction.Done(); @@ -1904,21 +1850,21 @@ bfs_stat_index(fs_volume _fs, const char *name, struct stat *stat) if (status < B_OK) RETURN_ERROR(status); - bfs_inode *node = index.Node()->Node(); + bfs_inode &node = index.Node()->Node(); stat->st_type = index.Type(); - stat->st_size = node->data.Size(); - stat->st_mode = node->Mode(); + stat->st_size = node.data.Size(); + stat->st_mode = node.Mode(); stat->st_nlink = 1; stat->st_blksize = 65536; - stat->st_uid = node->UserID(); - stat->st_gid = node->GroupID(); + stat->st_uid = node.UserID(); + stat->st_gid = node.GroupID(); stat->st_atime = time(NULL); - stat->st_mtime = stat->st_ctime = (time_t)(node->LastModifiedTime() >> INODE_TIME_SHIFT); - stat->st_crtime = (time_t)(node->CreateTime() >> INODE_TIME_SHIFT); + stat->st_mtime = stat->st_ctime = (time_t)(node.LastModifiedTime() >> INODE_TIME_SHIFT); + stat->st_crtime = (time_t)(node.CreateTime() >> INODE_TIME_SHIFT); return B_OK; } diff --git a/src/add-ons/kernel/file_systems/bfs/kernel_interface_r5.cpp b/src/add-ons/kernel/file_systems/bfs/kernel_interface_r5.cpp deleted file mode 100644 index 4f42a2f630..0000000000 --- a/src/add-ons/kernel/file_systems/bfs/kernel_interface_r5.cpp +++ /dev/null @@ -1,2157 +0,0 @@ -/* kernel_interface - file system interface to BeOS' vnode layer -** -** Copyright 2001-2004, Axel Dörfler, axeld@pinc-software.de -** This file may be used under the terms of the Haiku License. -*/ - - -#define COMPILE_FOR_R5 - -#include "Debug.h" -#include "Volume.h" -#include "Inode.h" -#include "Index.h" -#include "BPlusTree.h" -#include "Query.h" -#include "bfs_control.h" - -#include - -#include -#include - -// BeOS vnode layer stuff -#include -#ifndef _IMPEXP_KERNEL -# define _IMPEXP_KERNEL -#endif - -#include -#include -#include - -#include -#include - - -#ifdef USER -# define dprintf printf -#endif - -// file system API -static int bfs_mount(nspace_id nsid, const char *device, ulong flags, - void *parms, size_t len, void **data, vnode_id *vnid); -static int bfs_unmount(void *_ns); -static int bfs_read_fs_stat(void *_ns, struct fs_info *); -static int bfs_write_fs_stat(void *ns, struct fs_info *, long mode); -static int bfs_initialize(const char *devname, void *parms, size_t len); - -static int bfs_sync(void *ns); - -static int bfs_read_vnode(void *_ns, vnode_id vnid, char r, void **node); -static int bfs_release_vnode(void *_ns, void *_node, char r); -static int bfs_remove_vnode(void *ns, void *node, char r); - -static int bfs_walk(void *_ns, void *_base, const char *file, - char **newpath, vnode_id *vnid); - -static int bfs_ioctl(void *ns, void *node, void *cookie, int cmd, void *buf, size_t len); -static int bfs_setflags(void *ns, void *node, void *cookie, int flags); - -static int bfs_select(void *ns, void *node, void *cookie, uint8 event, - uint32 ref, selectsync *sync); -static int bfs_deselect(void *ns, void *node, void *cookie, uint8 event, - selectsync *sync); -static int bfs_fsync(void *ns, void *node); - -static int bfs_create(void *ns, void *dir, const char *name, - int perms, int omode, vnode_id *vnid, void **cookie); -static int bfs_symlink(void *ns, void *dir, const char *name, - const char *path); -static int bfs_link(void *ns, void *dir, const char *name, void *node); -static int bfs_unlink(void *ns, void *dir, const char *name); -static int bfs_rename(void *ns, void *oldDir, const char *oldName, void *newDir, const char *newName); - -static int bfs_read_stat(void *_ns, void *_node, struct stat *st); -static int bfs_write_stat(void *ns, void *node, struct stat *st, long mask); - -static int bfs_open(void *_ns, void *_node, int omode, void **cookie); -static int bfs_read(void *_ns, void *_node, void *cookie, off_t pos, - void *buf, size_t *len); -static int bfs_write(void *ns, void *node, void *cookie, off_t pos, - const void *buf, size_t *len); -static int bfs_free_cookie(void *ns, void *node, void *cookie); -static int bfs_close(void *ns, void *node, void *cookie); - -static int bfs_access(void *_ns, void *_node, int mode); -static int bfs_read_link(void *_ns, void *_node, char *buffer, size_t *bufferSize); - -// directory functions -static int bfs_mkdir(void *ns, void *dir, const char *name, int perms); -static int bfs_rmdir(void *ns, void *dir, const char *name); -static int bfs_open_dir(void *_ns, void *_node, void **cookie); -static int bfs_read_dir(void *_ns, void *_node, void *cookie, - long *num, struct dirent *dirent, size_t bufferSize); -static int bfs_rewind_dir(void *_ns, void *_node, void *cookie); -static int bfs_close_dir(void *_ns, void *_node, void *cookie); -static int bfs_free_dir_cookie(void *_ns, void *_node, void *cookie); - -// attribute support -static int bfs_open_attrdir(void *ns, void *node, void **cookie); -static int bfs_close_attrdir(void *ns, void *node, void *cookie); -static int bfs_free_attrdir_cookie(void *ns, void *node, void *cookie); -static int bfs_rewind_attrdir(void *ns, void *node, void *cookie); -static int bfs_read_attrdir(void *ns, void *node, void *cookie, long *num, - struct dirent *buf, size_t bufferSize); -static int bfs_remove_attr(void *ns, void *node, const char *name); -static int bfs_rename_attr(void *ns, void *node, const char *oldname, - const char *newname); -static int bfs_stat_attr(void *ns, void *node, const char *name, - struct attr_info *buf); -static int bfs_write_attr(void *ns, void *node, const char *name, int type, - const void *buf, size_t *len, off_t pos); -static int bfs_read_attr(void *ns, void *node, const char *name, int type, - void *buf, size_t *len, off_t pos); - -// index support -static int bfs_open_indexdir(void *ns, void **cookie); -static int bfs_close_indexdir(void *ns, void *cookie); -static int bfs_free_indexdir_cookie(void *ns, void *node, void *cookie); -static int bfs_rewind_indexdir(void *ns, void *cookie); -static int bfs_read_indexdir(void *ns, void *cookie, long *num, struct dirent *dirent, - size_t bufferSize); -static int bfs_create_index(void *ns, const char *name, int type, int flags); -static int bfs_remove_index(void *ns, const char *name); -static int bfs_rename_index(void *ns, const char *oldname, const char *newname); -static int bfs_stat_index(void *ns, const char *name, struct index_info *indexInfo); - -// query support -static int bfs_open_query(void *ns, const char *query, ulong flags, - port_id port, long token, void **cookie); -static int bfs_close_query(void *ns, void *cookie); -static int bfs_free_query_cookie(void *ns, void *node, void *cookie); -static int bfs_read_query(void *ns, void *cookie, long *num, - struct dirent *buf, size_t bufsize); - -// Dano compatibility (required for booting) -static int bfs_wake_vnode(void *ns, void *node); -static int bfs_suspend_vnode(void *ns, void *node); - - -/* vnode_ops struct. Fill this in to tell the kernel how to call - functions in your driver. -*/ - -vnode_ops fs_entry = { - &bfs_read_vnode, // read_vnode - &bfs_release_vnode, // write_vnode - &bfs_remove_vnode, // remove_vnode - NULL, // secure_vnode (not needed) - &bfs_walk, // walk - &bfs_access, // access - &bfs_create, // create - &bfs_mkdir, // mkdir - &bfs_symlink, // symlink - &bfs_link, // link - &bfs_rename, // rename - &bfs_unlink, // unlink - &bfs_rmdir, // rmdir - &bfs_read_link, // readlink - &bfs_open_dir, // opendir - &bfs_close_dir, // closedir - &bfs_free_dir_cookie, // free_dircookie - &bfs_rewind_dir, // rewinddir - &bfs_read_dir, // readdir - &bfs_open, // open file - &bfs_close, // close file - &bfs_free_cookie, // free cookie - &bfs_read, // read file - &bfs_write, // write file - NULL, // readv - NULL, // writev - &bfs_ioctl, // ioctl - &bfs_setflags, // setflags file - &bfs_read_stat, // read stat - &bfs_write_stat, // write stat - &bfs_fsync, // fsync - &bfs_initialize, // initialize - &bfs_mount, // mount - &bfs_unmount, // unmount - &bfs_sync, // sync - &bfs_read_fs_stat, // read fs stat - &bfs_write_fs_stat, // write fs stat - &bfs_select, // select - &bfs_deselect, // deselect - - &bfs_open_indexdir, // open index dir - &bfs_close_indexdir, // close index dir - &bfs_free_indexdir_cookie, // free index dir cookie - &bfs_rewind_indexdir, // rewind index dir - &bfs_read_indexdir, // read index dir - &bfs_create_index, // create index - &bfs_remove_index, // remove index - &bfs_rename_index, // rename index - &bfs_stat_index, // stat index - - &bfs_open_attrdir, // open attr dir - &bfs_close_attrdir, // close attr dir - &bfs_free_attrdir_cookie, // free attr dir cookie - &bfs_rewind_attrdir, // rewind attr dir - &bfs_read_attrdir, // read attr dir - &bfs_write_attr, // write attr - &bfs_read_attr, // read attr - &bfs_remove_attr, // remove attr - &bfs_rename_attr, // rename attr - &bfs_stat_attr, // stat attr - - &bfs_open_query, // open query - &bfs_close_query, // close query - &bfs_free_query_cookie, // free query cookie - &bfs_read_query, // read query - - &bfs_wake_vnode, // these two are added for Dano compatibility; - &bfs_suspend_vnode // they do nothing. -}; - -#define BFS_IO_SIZE 65536 - -// ToDo: has to change to "bfs" later -#ifdef BFS_REPLACEMENT -# define BFS_NAME "bfs" -#else -# define BFS_NAME "obfs" -#endif - -int32 api_version = B_CUR_FS_API_VERSION; - - -static int -bfs_mount(nspace_id nsid, const char *device, ulong flags, void *parms, - size_t len, void **data, vnode_id *rootID) -{ - FUNCTION(); - - Volume *volume = new Volume(nsid); - if (volume == NULL) - return B_NO_MEMORY; - - status_t status; - if ((status = volume->Mount(device, flags)) == B_OK) { - *data = volume; - *rootID = volume->ToVnode(volume->Root()); - INFORM(("mounted \"%s\" (root node at %Ld, device = %s)\n", - volume->Name(), *rootID, device)); - -#ifdef DEBUG - add_debugger_commands(); -#endif - } - else - delete volume; - - RETURN_ERROR(status); -} - - -static int -bfs_unmount(void *ns) -{ - FUNCTION(); - Volume* volume = (Volume *)ns; - - status_t status = volume->Unmount(); - delete volume; - -#ifdef DEBUG - remove_debugger_commands(); -#endif - RETURN_ERROR(status); -} - - -/** Fill in bfs_info struct for device. - */ - -static int -bfs_read_fs_stat(void *_ns, struct fs_info *info) -{ - FUNCTION(); - if (_ns == NULL || info == NULL) - return B_BAD_VALUE; - - Volume *volume = (Volume *)_ns; - - RecursiveLocker locker(volume->Lock()); - - // File system flags. - info->flags = B_FS_IS_PERSISTENT | B_FS_HAS_ATTR | B_FS_HAS_MIME | B_FS_HAS_QUERY | - (volume->IsReadOnly() ? B_FS_IS_READONLY : 0); - - info->io_size = BFS_IO_SIZE; - // whatever is appropriate here? Just use the same value as BFS (and iso9660) for now - - info->block_size = volume->BlockSize(); - info->total_blocks = volume->NumBlocks(); - info->free_blocks = volume->FreeBlocks(); - - // Volume name - strlcpy(info->volume_name, volume->Name(), sizeof(info->volume_name)); - - // File system name - strlcpy(info->fsh_name, BFS_NAME, sizeof(info->fsh_name)); - - return B_OK; -} - - -static int -bfs_write_fs_stat(void *_ns, struct fs_info *info, long mask) -{ - FUNCTION_START(("mask = %ld\n", mask)); - - Volume *volume = (Volume *)_ns; - disk_super_block &superBlock = volume->SuperBlock(); - - RecursiveLocker locker(volume->Lock()); - - status_t status = B_BAD_VALUE; - - if (mask & WFSSTAT_NAME) { - strncpy(superBlock.name, info->volume_name, sizeof(superBlock.name) - 1); - superBlock.name[sizeof(superBlock.name) - 1] = '\0'; - - status = volume->WriteSuperBlock(); - } - return status; -} - - -static int -bfs_initialize(const char *deviceName, void *parms, size_t len) -{ - FUNCTION_START(("deviceName = %s, parameter len = %ld\n", deviceName, len)); - - // This function is not available from the outside in BeOS - // It will be similarly implemented in OpenBeOS, though - the - // backend (to create the file system) is already done; just - // call Volume::Initialize(). - - return B_ERROR; -} - - -static int -bfs_sync(void *_ns) -{ - FUNCTION(); - if (_ns == NULL) - return B_BAD_VALUE; - - Volume *volume = (Volume *)_ns; - - return volume->Sync(); -} - - -// #pragma mark - - - -/** Reads in the node from disk and creates an inode object from it. - * Has to be cautious if the node in question is currently under - * construction, in which case it waits for that action to be completed, - * and uses the inode object from the construction instead of creating - * a new one. - * ToDo: Must not be called without the volume lock being held. Actually, - * this might even happen with the BDirectory(node_ref *) constructor - * (at least I think so, I haven't tested it yet), so we should better - * test this. Fortunately, we can easily solve the issue with our kernel. - */ - -static int -bfs_read_vnode(void *_ns, vnode_id id, char reenter, void **_node) -{ - FUNCTION_START(("vnode_id = %Ld\n", id)); - Volume *volume = (Volume *)_ns; - - if (id < 0 || id > volume->NumBlocks()) { - FATAL(("inode at %Ld requested!\n", id)); - return B_ERROR; - } - - CachedBlock cached(volume, id); - - bfs_inode *node = (bfs_inode *)cached.Block(); - Inode *inode = NULL; - int32 tries = 0; - -restartIfBusy: - status_t status = node->InitCheck(volume); - - if (status == B_BUSY) { - inode = (Inode *)node->etc; - // We have to use the "etc" field to get the inode object - // (the inode is currently being constructed) - // We would need to call something like get_vnode() again here - // to get rid of the "etc" field - which would be nice, especially - // for other file systems which don't have this messy field. - - // let us wait a bit and try again later - if (tries++ < 200) { - // wait for one second at maximum - snooze(5000); - goto restartIfBusy; - } - FATAL(("inode is not becoming unbusy (id = %Ld)\n", id)); - return status; - } else if (status < B_OK) { - FATAL(("inode at %Ld is corrupt!\n", id)); - return status; - } - - // If the inode is currently being constructed, we already have an inode - // pointer (taken from the node's etc field). - // If not, we create a new one here - - if (inode == NULL) { - inode = new Inode(&cached); - if (inode == NULL) - return B_NO_MEMORY; - - status = inode->InitCheck(false); - if (status < B_OK) - delete inode; - } else - status = inode->InitCheck(false); - - if (status == B_OK) - *_node = inode; - - return status; -} - - -static int -bfs_release_vnode(void *ns, void *_node, char reenter) -{ - //FUNCTION_START(("node = %p\n", _node)); - Inode *inode = (Inode *)_node; - - delete inode; - - return B_NO_ERROR; -} - - -static int -bfs_remove_vnode(void *_ns, void *_node, char reenter) -{ - FUNCTION(); - - if (_ns == NULL || _node == NULL) - return B_BAD_VALUE; - - Volume *volume = (Volume *)_ns; - Inode *inode = (Inode *)_node; - - // The "chkbfs" functionality uses this flag to prevent the space used - // up by the inode from being freed - this flag is set only in situations - // where this is a good idea... (the block bitmap will get fixed anyway - // in this case). - if (inode->Flags() & INODE_DONT_FREE_SPACE) { - delete inode; - return B_OK; - } - - // If the inode isn't in use anymore, we were called before - // bfs_unlink() returns - in this case, we can just use the - // transaction which has already deleted the inode. - Transaction localTransaction, *transaction = NULL; - - Journal *journal = volume->GetJournal(volume->ToBlock(inode->Parent())); - if (journal != NULL) - transaction = journal->CurrentTransaction(); - - if (transaction == NULL) { - transaction = &localTransaction; - localTransaction.Start(volume, inode->BlockNumber()); - } - - status_t status = inode->Free(transaction); - if (status == B_OK) { - if (transaction == &localTransaction) - localTransaction.Done(); - - delete inode; - } - - return status; -} - - -static int -bfs_wake_vnode(void *_ns, void *_node) -{ - return B_OK; -} - - -static int -bfs_suspend_vnode(void *_ns, void *_node) -{ - return B_OK; -} - - -// #pragma mark - - - -/** the walk function just "walks" through a directory looking for the - * specified file. It calls get_vnode() on its vnode-id to init it - * for the kernel. - */ - -static int -bfs_walk(void *_ns, void *_directory, const char *file, char **_resolvedPath, vnode_id *_vnodeID) -{ - //FUNCTION_START(("file = %s\n", file)); - if (_ns == NULL || _directory == NULL || file == NULL || _vnodeID == NULL) - return B_BAD_VALUE; - - Volume *volume = (Volume *)_ns; - Inode *directory = (Inode *)_directory; - - // check access permissions - status_t status = directory->CheckPermissions(R_OK); - if (status < B_OK) - RETURN_ERROR(status); - - BPlusTree *tree; - if (directory->GetTree(&tree) != B_OK) - RETURN_ERROR(B_BAD_VALUE); - - if ((status = tree->Find((uint8 *)file, (uint16)strlen(file), _vnodeID)) < B_OK) { - PRINT(("bfs_walk() could not find %Ld:\"%s\": %s\n", directory->BlockNumber(), file, strerror(status))); - return status; - } - - RecursiveLocker locker(volume->Lock()); - // we have to hold the volume lock in order to not - // interfere with new_vnode() here - - Inode *inode; - if ((status = get_vnode(volume->ID(), *_vnodeID, (void **)&inode)) != B_OK) { - REPORT_ERROR(status); - return B_ENTRY_NOT_FOUND; - } - - // Is inode a symlink? Then resolve it, if we should - - if (inode->IsSymLink() && _resolvedPath != NULL) { - status_t status = B_OK; - char *newPath = NULL; - - // Symbolic links can store their target in the data stream (for links - // that take more than 144 bytes of storage [the size of the data_stream - // structure]), or directly instead of the data_stream class - // So we have to deal with both cases here. - - // Note: we would naturally call bfs_read_link() here, but the API of the - // vnode layer would require us to always reserve a large chunk of memory - // for the path, so we're not going to do that - - if (inode->Flags() & INODE_LONG_SYMLINK) { - size_t readBytes = inode->Size(); - char *data = (char *)malloc(readBytes); - if (data != NULL) { - status = inode->ReadAt(0, (uint8 *)data, &readBytes); - if (status == B_OK && readBytes == inode->Size()) - status = new_path(data, &newPath); - - free(data); - } else - status = B_NO_MEMORY; - } else - status = new_path((char *)&inode->Node()->short_symlink, &newPath); - - put_vnode(volume->ID(), inode->ID()); - if (status == B_OK) - *_resolvedPath = newPath; - - RETURN_ERROR(status); - } - - return B_OK; -} - - -static int -bfs_ioctl(void *_ns, void *_node, void *_cookie, int cmd, void *buffer, size_t bufferLength) -{ - FUNCTION_START(("node = %p, cmd = %d, buf = %p, len = %ld\n", _node, cmd, buffer, bufferLength)); - - if (_ns == NULL) - return B_BAD_VALUE; - - Volume *volume = (Volume *)_ns; - Inode *inode = (Inode *)_node; - - switch (cmd) { - case IOCTL_FILE_UNCACHED_IO: - { - if (inode == NULL) - return B_BAD_VALUE; - - // if the inode is already set up for uncached access, bail out - if (inode->Flags() & INODE_NO_CACHE) { - FATAL(("File %Ld is already uncached\n", inode->ID())); - return B_ERROR; - } - - PRINT(("uncached access to inode %Ld\n", inode->ID())); - - // ToDo: sync the cache for this file! - // Unfortunately, we can't remove any blocks from the cache in BeOS, - // that means we can't guarantee consistency for the file contents - // spanning over both access modes. - - // request buffers for being able to access the file without - // using the cache or allocating memory - status_t status = volume->Pool().RequestBuffers(volume->BlockSize()); - if (status == B_OK) - inode->Node()->flags |= HOST_ENDIAN_TO_BFS_INT32(INODE_NO_CACHE); - return status; - } - case IOCTL_CREATE_TIME: - { - if (inode == NULL || buffer == NULL) - return B_BAD_VALUE; - - off_t *creationTime = (off_t *)buffer; - *creationTime = inode->Node()->CreateTime(); - return B_OK; - } - case IOCTL_MODIFIED_TIME: - { - if (inode == NULL || buffer == NULL) - return B_BAD_VALUE; - - off_t *modifiedTime = (off_t *)buffer; - *modifiedTime = inode->LastModified(); - return B_OK; - } - case BFS_IOCTL_VERSION: - { - uint32 *version = (uint32 *)buffer; - - *version = 0x10000; - return B_OK; - } - case BFS_IOCTL_START_CHECKING: - { - // start checking - BlockAllocator &allocator = volume->Allocator(); - check_control *control = (check_control *)buffer; - - status_t status = allocator.StartChecking(control); - if (status == B_OK && inode != NULL) - inode->Node()->flags |= HOST_ENDIAN_TO_BFS_INT32(INODE_CHKBFS_RUNNING); - - return status; - } - case BFS_IOCTL_STOP_CHECKING: - { - // stop checking - BlockAllocator &allocator = volume->Allocator(); - check_control *control = (check_control *)buffer; - - status_t status = allocator.StopChecking(control); - if (status == B_OK && inode != NULL) - inode->Node()->flags &= HOST_ENDIAN_TO_BFS_INT32(~INODE_CHKBFS_RUNNING); - - return status; - } - case BFS_IOCTL_CHECK_NEXT_NODE: - { - // check next - BlockAllocator &allocator = volume->Allocator(); - check_control *control = (check_control *)buffer; - - return allocator.CheckNextNode(control); - } -#ifdef DEBUG - case 56742: - { - // allocate all free blocks and zero them out (a test for the BlockAllocator)! - BlockAllocator &allocator = volume->Allocator(); - Transaction transaction(volume, 0); - CachedBlock cached(volume); - block_run run; - while (allocator.AllocateBlocks(&transaction, 8, 0, 64, 1, run) == B_OK) { - PRINT(("write block_run(%ld, %d, %d)\n", run.allocation_group, run.start, run.length)); - for (int32 i = 0;i < run.length;i++) { - uint8 *block = cached.SetTo(run); - if (block != NULL) { - memset(block, 0, volume->BlockSize()); - cached.WriteBack(&transaction); - } - } - } - return B_OK; - } - case 56743: - dump_super_block(&volume->SuperBlock()); - return B_OK; - case 56744: - if (inode != NULL) - dump_inode(inode->Node()); - return B_OK; - case 56745: - if (inode != NULL) - dump_block((const char *)inode->Node(), volume->BlockSize()); - return B_OK; -#endif - } - return B_BAD_VALUE; -} - - -/** Sets the open-mode flags for the open file cookie - only - * supports O_APPEND currently, but that should be sufficient - * for a file system. - */ - -static int -bfs_setflags(void *_ns, void *_node, void *_cookie, int flags) -{ - FUNCTION_START(("node = %p, flags = %d", _node, flags)); - - file_cookie *cookie = (file_cookie *)_cookie; - cookie->open_mode = (cookie->open_mode & ~O_APPEND) | (flags & O_APPEND); - - return B_OK; -} - - -int -bfs_select(void *ns, void *node, void *cookie, uint8 event, uint32 ref, selectsync *sync) -{ - FUNCTION_START(("event = %d, ref = %lu, sync = %p\n", event, ref, sync)); - notify_select_event(sync, ref); - - return B_OK; -} - - -static int -bfs_deselect(void *ns, void *node, void *cookie, uint8 event, selectsync *sync) -{ - FUNCTION(); - return B_OK; -} - - -static int -bfs_fsync(void *_ns, void *_node) -{ - FUNCTION(); - if (_node == NULL) - return B_BAD_VALUE; - - Inode *inode = (Inode *)_node; - return inode->Sync(); -} - - -/** Fills in the stat struct for a node - */ - -static int -bfs_read_stat(void *_ns, void *_node, struct stat *st) -{ - FUNCTION(); - - Volume *volume = (Volume *)_ns; - Inode *inode = (Inode *)_node; - bfs_inode *node = inode->Node(); - - st->st_dev = volume->ID(); - st->st_ino = inode->ID(); - st->st_nlink = 1; - st->st_blksize = BFS_IO_SIZE; - - st->st_uid = node->UserID(); - st->st_gid = node->GroupID(); - st->st_mode = node->Mode(); - st->st_size = node->data.Size(); - - st->st_atime = time(NULL); - st->st_mtime = st->st_ctime = (time_t)(node->LastModifiedTime() >> INODE_TIME_SHIFT); - st->st_crtime = (time_t)(node->CreateTime() >> INODE_TIME_SHIFT); - - return B_NO_ERROR; -} - - -static int -bfs_write_stat(void *_ns, void *_node, struct stat *stat, long mask) -{ - FUNCTION(); - - if (_ns == NULL || _node == NULL || stat == NULL) - RETURN_ERROR(B_BAD_VALUE); - - Volume *volume = (Volume *)_ns; - Inode *inode = (Inode *)_node; - - // that may be incorrect here - I don't think we need write access to - // change most of the stat... - // we should definitely check a bit more if the new stats are correct and valid... - - status_t status = inode->CheckPermissions(W_OK); - if (status < B_OK) - RETURN_ERROR(status); - -#ifdef UNSAFE_GET_VNODE - RecursiveLocker locker(volume->Lock()); -#endif - - WriteLocked locked(inode->Lock()); - if (locked.IsLocked() < B_OK) - RETURN_ERROR(B_ERROR); - - Transaction transaction(volume, inode->BlockNumber()); - - bfs_inode *node = inode->Node(); - - if (mask & WSTAT_SIZE) { - // Since WSTAT_SIZE is the only thing that can fail directly, we - // do it first, so that the inode state will still be consistent - // with the on-disk version - if (inode->IsDirectory()) - return B_IS_A_DIRECTORY; - - if (inode->Size() != stat->st_size) { - status = inode->SetFileSize(&transaction, stat->st_size); - if (status < B_OK) - return status; - - // fill the new blocks (if any) with zeros - inode->FillGapWithZeros(inode->OldSize(), inode->Size()); - - Index index(volume); - index.UpdateSize(&transaction, inode); - - if ((mask & WSTAT_MTIME) == 0) - index.UpdateLastModified(&transaction, inode); - } - } - - if (mask & WSTAT_MODE) { - PRINT(("original mode = %ld, stat->st_mode = %d\n", node->mode, stat->st_mode)); - node->mode = node->mode & ~S_IUMSK | stat->st_mode & S_IUMSK; - } - - if (mask & WSTAT_UID) - node->uid = stat->st_uid; - if (mask & WSTAT_GID) - node->gid = stat->st_gid; - - if (mask & WSTAT_MTIME) { - // Index::UpdateLastModified() will set the new time in the inode - Index index(volume); - index.UpdateLastModified(&transaction, inode, - (bigtime_t)stat->st_mtime << INODE_TIME_SHIFT); - } - if (mask & WSTAT_CRTIME) { - node->create_time = (bigtime_t)stat->st_crtime << INODE_TIME_SHIFT; - } - - if ((status = inode->WriteBack(&transaction)) == B_OK) - transaction.Done(); - - notify_listener(B_STAT_CHANGED, volume->ID(), 0, 0, inode->ID(), NULL); - - return status; -} - - -int -bfs_create(void *_ns, void *_directory, const char *name, int omode, int mode, - vnode_id *vnodeID, void **_cookie) -{ - FUNCTION_START(("name = \"%s\", perms = %d, omode = %d\n", name, mode, omode)); - - if (_ns == NULL || _directory == NULL || _cookie == NULL - || name == NULL || *name == '\0') - RETURN_ERROR(B_BAD_VALUE); - - Volume *volume = (Volume *)_ns; - Inode *directory = (Inode *)_directory; - - if (!directory->IsDirectory()) - RETURN_ERROR(B_BAD_TYPE); - - status_t status = directory->CheckPermissions(W_OK); - if (status < B_OK) - RETURN_ERROR(status); - - // We are creating the cookie at this point, so that we don't have - // to remove the inode if we don't have enough free memory later... - file_cookie *cookie = (file_cookie *)malloc(sizeof(file_cookie)); - if (cookie == NULL) - RETURN_ERROR(B_NO_MEMORY); - - // initialize the cookie - cookie->open_mode = omode; - cookie->last_size = 0; - cookie->last_notification = system_time(); - -#ifdef UNSAFE_GET_VNODE - RecursiveLocker locker(volume->Lock()); -#endif - Transaction transaction(volume, directory->BlockNumber()); - - status = Inode::Create(&transaction, directory, name, S_FILE | (mode & S_IUMSK), - omode, 0, vnodeID); - - if (status >= B_OK) { - transaction.Done(); - - // register the cookie - *_cookie = cookie; - - notify_listener(B_ENTRY_CREATED, volume->ID(), directory->ID(), 0, *vnodeID, name); - } else - free(cookie); - - return status; -} - - -int -bfs_symlink(void *_ns, void *_directory, const char *name, const char *path) -{ - FUNCTION(); - - if (_ns == NULL || _directory == NULL || path == NULL - || name == NULL || *name == '\0') - RETURN_ERROR(B_BAD_VALUE); - - Volume *volume = (Volume *)_ns; - Inode *directory = (Inode *)_directory; - - if (!directory->IsDirectory()) - RETURN_ERROR(B_BAD_TYPE); - - status_t status = directory->CheckPermissions(W_OK); - if (status < B_OK) - RETURN_ERROR(status); - -#ifdef UNSAFE_GET_VNODE - RecursiveLocker locker(volume->Lock()); -#endif - Transaction transaction(volume, directory->BlockNumber()); - - Inode *link; - off_t id; - status = Inode::Create(&transaction, directory, name, S_SYMLINK | 0777, 0, 0, &id, &link); - if (status < B_OK) - RETURN_ERROR(status); - - size_t length = strlen(path); - if (length < SHORT_SYMLINK_NAME_LENGTH) { - strcpy(link->Node()->short_symlink, path); - status = link->WriteBack(&transaction); - } else { - link->Node()->flags |= HOST_ENDIAN_TO_BFS_INT32(INODE_LONG_SYMLINK | INODE_LOGGED); - // The following call will have to write the inode back, so - // we don't have to do that here... - status = link->WriteAt(&transaction, 0, (const uint8 *)path, &length); - } - // ToDo: would be nice if Inode::Create() would let the INODE_NOT_READY - // flag set until here, so that it can be accessed directly - - // Inode::Create() left the inode locked in memory - put_vnode(volume->ID(), id); - - if (status == B_OK) { - transaction.Done(); - - notify_listener(B_ENTRY_CREATED, volume->ID(), directory->ID(), 0, id, name); - } - - return status; -} - - -int -bfs_link(void *ns, void *dir, const char *name, void *node) -{ - FUNCTION_START(("name = \"%s\"\n", name)); - - // This one won't be implemented in a binary compatible BFS - - return B_ERROR; -} - - -int -bfs_unlink(void *_ns, void *_directory, const char *name) -{ - FUNCTION_START(("name = \"%s\"\n", name)); - - if (_ns == NULL || _directory == NULL || name == NULL || *name == '\0') - return B_BAD_VALUE; - if (!strcmp(name, "..") || !strcmp(name, ".")) - return B_NOT_ALLOWED; - - Volume *volume = (Volume *)_ns; - Inode *directory = (Inode *)_directory; - - status_t status = directory->CheckPermissions(W_OK); - if (status < B_OK) - return status; - -#ifdef UNSAFE_GET_VNODE - RecursiveLocker locker(volume->Lock()); -#endif - Transaction transaction(volume, directory->BlockNumber()); - - off_t id; - if ((status = directory->Remove(&transaction, name, &id)) == B_OK) { - transaction.Done(); - - notify_listener(B_ENTRY_REMOVED, volume->ID(), directory->ID(), 0, id, NULL); - } - return status; -} - - -int -bfs_rename(void *_ns, void *_oldDir, const char *oldName, void *_newDir, const char *newName) -{ - FUNCTION_START(("oldDir = %p, oldName = \"%s\", newDir = %p, newName = \"%s\"\n", _oldDir, oldName, _newDir, newName)); - - // there might be some more tests needed?! - if (_ns == NULL || _oldDir == NULL || _newDir == NULL - || oldName == NULL || *oldName == '\0' - || newName == NULL || *newName == '\0' - || !strcmp(oldName, ".") || !strcmp(oldName, "..") - || !strcmp(newName, ".") || !strcmp(newName, "..") - || strchr(newName, '/') != NULL) - RETURN_ERROR(B_BAD_VALUE); - - Volume *volume = (Volume *)_ns; - Inode *oldDirectory = (Inode *)_oldDir; - Inode *newDirectory = (Inode *)_newDir; - - // are we already done? - if (oldDirectory == newDirectory && !strcmp(oldName, newName)) - return B_OK; - - RecursiveLocker locker(volume->Lock()); - - // get the directory's tree, and a pointer to the inode which should be changed - BPlusTree *tree; - status_t status = oldDirectory->GetTree(&tree); - if (status < B_OK) - RETURN_ERROR(status); - - off_t id; - status = tree->Find((const uint8 *)oldName, strlen(oldName), &id); - if (status < B_OK) - RETURN_ERROR(status); - - Vnode vnode(volume, id); - Inode *inode; - if (vnode.Get(&inode) < B_OK) - return B_IO_ERROR; - - // Don't move a directory into one of its children - we soar up - // from the newDirectory to either the root node or the old - // directory, whichever comes first. - // If we meet our inode on that way, we have to bail out. - - if (oldDirectory != newDirectory) { - vnode_id parent = volume->ToVnode(newDirectory->Parent()); - vnode_id root = volume->RootNode()->ID(); - - while (true) { - if (parent == id) - return B_BAD_VALUE; - else if (parent == root || parent == oldDirectory->ID()) - break; - - Vnode vnode(volume, parent); - Inode *parentNode; - if (vnode.Get(&parentNode) < B_OK) - return B_ERROR; - - parent = volume->ToVnode(parentNode->Parent()); - } - } - - // Everything okay? Then lets get to work... - - Transaction transaction(volume, oldDirectory->BlockNumber()); - - // First, try to make sure there is nothing that will stop us in - // the target directory - since this is the only non-critical - // failure, we will test this case first - BPlusTree *newTree = tree; - if (newDirectory != oldDirectory) { - status = newDirectory->GetTree(&newTree); - if (status < B_OK) - RETURN_ERROR(status); - } - - status = newTree->Insert(&transaction, (const uint8 *)newName, strlen(newName), id); - if (status == B_NAME_IN_USE) { - // If there is already a file with that name, we have to remove - // it, as long it's not a directory with files in it - off_t clobber; - if (newTree->Find((const uint8 *)newName, strlen(newName), &clobber) < B_OK) - return B_NAME_IN_USE; - if (clobber == id) - return B_BAD_VALUE; - - Vnode vnode(volume, clobber); - Inode *other; - if (vnode.Get(&other) < B_OK) - return B_NAME_IN_USE; - - status = newDirectory->Remove(&transaction, newName, NULL, other->IsDirectory()); - if (status < B_OK) - return status; - - notify_listener(B_ENTRY_REMOVED, volume->ID(), newDirectory->ID(), 0, clobber, NULL); - - status = newTree->Insert(&transaction, (const uint8 *)newName, strlen(newName), id); - } - if (status < B_OK) - return status; - - // If anything fails now, we have to remove the inode from the - // new directory in any case to restore the previous state - status_t bailStatus = B_OK; - - // update the name only when they differ - bool nameUpdated = false; - if (strcmp(oldName, newName)) { - status = inode->SetName(&transaction, newName); - if (status == B_OK) { - Index index(volume); - index.UpdateName(&transaction, oldName, newName, inode); - nameUpdated = true; - } - } - - if (status == B_OK) { - status = tree->Remove(&transaction, (const uint8 *)oldName, strlen(oldName), id); - if (status == B_OK) { - inode->Node()->parent = newDirectory->BlockRun(); - - // if it's a directory, update the parent directory pointer - // in its tree if necessary - BPlusTree *movedTree = NULL; - if (oldDirectory != newDirectory - && inode->IsDirectory() - && (status = inode->GetTree(&movedTree)) == B_OK) - status = movedTree->Replace(&transaction, (const uint8 *)"..", 2, newDirectory->ID()); - - if (status == B_OK) { - status = inode->WriteBack(&transaction); - if (status == B_OK) { - transaction.Done(); - - notify_listener(B_ENTRY_MOVED, volume->ID(), oldDirectory->ID(), - newDirectory->ID(), id, newName); - return B_OK; - } - } - // If we get here, something has gone wrong already! - - // Those better don't fail, or we switch to a read-only - // device for safety reasons (Volume::Panic() does this - // for us) - // Anyway, if we overwrote a file in the target directory - // this is lost now (only in-memory, not on-disk)... - bailStatus = tree->Insert(&transaction, (const uint8 *)oldName, strlen(oldName), id); - if (movedTree != NULL) - movedTree->Replace(&transaction, (const uint8 *)"..", 2, oldDirectory->ID()); - } - } - - if (bailStatus == B_OK && nameUpdated) { - bailStatus = inode->SetName(&transaction, oldName); - if (status == B_OK) { - // update inode and index - inode->WriteBack(&transaction); - - Index index(volume); - index.UpdateName(&transaction, newName, oldName, inode); - } - } - - if (bailStatus == B_OK) - bailStatus = newTree->Remove(&transaction, (const uint8 *)newName, strlen(newName), id); - - if (bailStatus < B_OK) - volume->Panic(); - - return status; -} - - -/** Opens the file with the specified mode. - */ - -static int -bfs_open(void *_ns, void *_node, int omode, void **_cookie) -{ - FUNCTION(); - if (_ns == NULL || _node == NULL || _cookie == NULL) - RETURN_ERROR(B_BAD_VALUE); - - Volume *volume = (Volume *)_ns; - Inode *inode = (Inode *)_node; - - // we can't open a file which uses uncached access twice - if (inode->Flags() & INODE_NO_CACHE) - return B_BUSY; - - // opening a directory read-only is allowed, although you can't read - // any data from it. - if (inode->IsDirectory() && omode & O_RWMASK) { - omode = omode & ~O_RWMASK; - // ToDo: for compatibility reasons, we don't return an error here... - // e.g. "copyattr" tries to do that - //return B_IS_A_DIRECTORY; - } - - status_t status = inode->CheckPermissions(oModeToAccess(omode)); - if (status < B_OK) - RETURN_ERROR(status); - - // we could actually use the cookie to keep track of: - // - the last block_run - // - the location in the data_stream (indirect, double indirect, - // position in block_run array) - // - // This could greatly speed up continuous reads of big files, especially - // in the indirect block section. - - file_cookie *cookie = (file_cookie *)malloc(sizeof(file_cookie)); - if (cookie == NULL) - RETURN_ERROR(B_NO_MEMORY); - - // initialize the cookie - cookie->open_mode = omode; - // needed by e.g. bfs_write() for O_APPEND - cookie->last_size = inode->Size(); - cookie->last_notification = system_time(); - - // Should we truncate the file? - if (omode & O_TRUNC) { - WriteLocked locked(inode->Lock()); - Transaction transaction(volume, inode->BlockNumber()); - - status_t status = inode->SetFileSize(&transaction, 0); - if (status < B_OK) { - // bfs_free_cookie() is only called if this function is successful - free(cookie); - return status; - } - - transaction.Done(); - } - - *_cookie = cookie; - return B_OK; -} - - -/** Read a file specified by node, using information in cookie - * and at offset specified by pos. read len bytes into buffer buf. - */ - -static int -bfs_read(void *_ns, void *_node, void *_cookie, off_t pos, void *buffer, size_t *_length) -{ - //FUNCTION(); - Inode *inode = (Inode *)_node; - - if (!inode->HasUserAccessableStream()) { - *_length = 0; - RETURN_ERROR(B_BAD_VALUE); - } - - ReadLocked locked(inode->Lock()); - return inode->ReadAt(pos, (uint8 *)buffer, _length); -} - - -int -bfs_write(void *_ns, void *_node, void *_cookie, off_t pos, const void *buffer, size_t *_length) -{ - //FUNCTION(); - // uncomment to be more robust against a buggy vnode layer ;-) - //if (_ns == NULL || _node == NULL || _cookie == NULL) - // return B_BAD_VALUE; - - Volume *volume = (Volume *)_ns; - Inode *inode = (Inode *)_node; - - if (!inode->HasUserAccessableStream()) { - *_length = 0; - RETURN_ERROR(B_BAD_VALUE); - } - - file_cookie *cookie = (file_cookie *)_cookie; - - if (cookie->open_mode & O_APPEND) - pos = inode->Size(); - - WriteLocked locked(inode->Lock()); - if (locked.IsLocked() < B_OK) - RETURN_ERROR(B_ERROR); - - Transaction transaction; - // We are not starting the transaction here, since - // it might not be needed at all (the contents of - // regular files aren't logged) - - status_t status = inode->WriteAt(&transaction, pos, (const uint8 *)buffer, _length); - - if (status == B_OK) - transaction.Done(); - - if (status == B_OK && (inode->Flags() & INODE_NO_CACHE) == 0) { - // uncached files don't cause notifications during access, and - // never want to write back any cached blocks - - // periodically notify if the file size has changed - // ToDo: should we better test for a change in the last_modified time only? - if (cookie->last_size != inode->Size() - && system_time() > cookie->last_notification + INODE_NOTIFICATION_INTERVAL) { - notify_listener(B_STAT_CHANGED, volume->ID(), 0, 0, inode->ID(), NULL); - cookie->last_size = inode->Size(); - cookie->last_notification = system_time(); - } - - // This will flush the dirty blocks to disk from time to time. - // It's done here and not in Inode::WriteAt() so that it won't - // add to the duration of a transaction - it might even be a - // good idea to offload those calls to another thread - volume->WriteCachedBlocksIfNecessary(); - } - - return status; -} - - -/** Do whatever is necessary to close a file, EXCEPT for freeing - * the cookie! - */ - -static int -bfs_close(void *_ns, void *_node, void *_cookie) -{ - FUNCTION(); - if (_ns == NULL || _node == NULL || _cookie == NULL) - return B_BAD_VALUE; - - return B_OK; -} - - -static int -bfs_free_cookie(void *_ns, void *_node, void *_cookie) -{ - FUNCTION(); - - if (_ns == NULL || _node == NULL || _cookie == NULL) - return B_BAD_VALUE; - - file_cookie *cookie = (file_cookie *)_cookie; - - Volume *volume = (Volume *)_ns; - Inode *inode = (Inode *)_node; - - if (cookie->open_mode & O_RWMASK) { -#ifdef UNSAFE_GET_VNODE - RecursiveLocker locker(volume->Lock()); -#endif - ReadLocked locked(inode->Lock()); - - // trim the preallocated blocks and update the size, - // and last_modified indices if needed - - Transaction transaction(volume, inode->BlockNumber()); - status_t status = B_OK; - bool changed = false; - Index index(volume); - - if (inode->OldSize() != inode->Size()) { - status = inode->Trim(&transaction); - if (status < B_OK) - FATAL(("Could not trim preallocated blocks!")); - - index.UpdateSize(&transaction, inode); - changed = true; - } - if (inode->OldLastModified() != inode->LastModified()) { - index.UpdateLastModified(&transaction, inode, inode->LastModified()); - changed = true; - - // updating the index doesn't write back the inode - inode->WriteBack(&transaction); - } - - if (status == B_OK) - transaction.Done(); - - if (changed) - notify_listener(B_STAT_CHANGED, volume->ID(), 0, 0, inode->ID(), NULL); - } - - if (inode->Flags() & INODE_NO_CACHE) { - volume->Pool().ReleaseBuffers(); - inode->Node()->flags &= HOST_ENDIAN_TO_BFS_INT32(~INODE_NO_CACHE); - // We don't need to save the inode, because INODE_NO_CACHE is a - // non-permanent flag which will be removed when the inode is loaded - // into memory. - } - - if (inode->Flags() & INODE_CHKBFS_RUNNING) { - // "chkbfs" exited abnormally, so we have to stop it here... - FATAL(("check process was aborted!\n")); - volume->Allocator().StopChecking(NULL); - } - - return B_OK; -} - - -/** Checks access permissions, return B_NOT_ALLOWED if the action - * is not allowed. - */ - -static int -bfs_access(void *_ns, void *_node, int accessMode) -{ - FUNCTION(); - - if (_ns == NULL || _node == NULL) - return B_BAD_VALUE; - - Inode *inode = (Inode *)_node; - status_t status = inode->CheckPermissions(accessMode); - if (status < B_OK) - RETURN_ERROR(status); - - return B_OK; -} - - -static int -bfs_read_link(void *_ns, void *_node, char *buffer, size_t *bufferSize) -{ - FUNCTION(); - - Inode *inode = (Inode *)_node; - - if (!inode->IsSymLink()) - RETURN_ERROR(B_BAD_VALUE); - - if (inode->Flags() & INODE_LONG_SYMLINK) { - status_t status = inode->ReadAt(0, (uint8 *)buffer, bufferSize); - if (status < B_OK) - RETURN_ERROR(status); - - *bufferSize = inode->Size(); - return B_OK; - } - - size_t numBytes = strlen((char *)&inode->Node()->short_symlink); - uint32 bytes = numBytes; - if (bytes > *bufferSize) - bytes = *bufferSize; - - memcpy(buffer, inode->Node()->short_symlink, bytes); - *bufferSize = numBytes; - - return B_OK; -} - - -// #pragma mark - -// Directory functions - - -static int -bfs_mkdir(void *_ns, void *_directory, const char *name, int mode) -{ - FUNCTION_START(("name = \"%s\", perms = %d\n", name, mode)); - - if (_ns == NULL || _directory == NULL - || name == NULL || *name == '\0') - RETURN_ERROR(B_BAD_VALUE); - - Volume *volume = (Volume *)_ns; - Inode *directory = (Inode *)_directory; - - if (!directory->IsDirectory()) - RETURN_ERROR(B_BAD_TYPE); - - status_t status = directory->CheckPermissions(W_OK); - if (status < B_OK) - RETURN_ERROR(status); - -#ifdef UNSAFE_GET_VNODE - RecursiveLocker locker(volume->Lock()); -#endif - Transaction transaction(volume, directory->BlockNumber()); - - // Inode::Create() locks the inode if we pass the "id" parameter, but we - // need it anyway - off_t id; - status = Inode::Create(&transaction, directory, name, S_DIRECTORY | (mode & S_IUMSK), 0, 0, &id); - if (status == B_OK) { - put_vnode(volume->ID(), id); - transaction.Done(); - - notify_listener(B_ENTRY_CREATED, volume->ID(), directory->ID(), 0, id, name); - } - - return status; -} - - -static int -bfs_rmdir(void *_ns, void *_directory, const char *name) -{ - FUNCTION_START(("name = \"%s\"\n", name)); - - if (_ns == NULL || _directory == NULL || name == NULL || *name == '\0') - return B_BAD_VALUE; - - Volume *volume = (Volume *)_ns; - Inode *directory = (Inode *)_directory; - -#ifdef UNSAFE_GET_VNODE - RecursiveLocker locker(volume->Lock()); -#endif - Transaction transaction(volume, directory->BlockNumber()); - - off_t id; - status_t status = directory->Remove(&transaction, name, &id, true); - if (status == B_OK) { - transaction.Done(); - - notify_listener(B_ENTRY_REMOVED, volume->ID(), directory->ID(), 0, id, NULL); - } - - return status; -} - - -/** creates fs-specific "cookie" struct that keeps track of where - * you are at in reading through directory entries in bfs_readdir. - */ - -static int -bfs_open_dir(void *_ns, void *_node, void **_cookie) -{ - FUNCTION(); - - if (_ns == NULL || _node == NULL || _cookie == NULL) - RETURN_ERROR(B_BAD_VALUE); - - Inode *inode = (Inode *)_node; - - // we don't ask here for directories only, because the bfs_open_index_dir() - // function utilizes us (so we must be able to open indices as well) - if (!inode->IsContainer()) - RETURN_ERROR(B_BAD_VALUE); - - BPlusTree *tree; - if (inode->GetTree(&tree) != B_OK) - RETURN_ERROR(B_BAD_VALUE); - - TreeIterator *iterator = new TreeIterator(tree); - if (iterator == NULL) - RETURN_ERROR(B_NO_MEMORY); - - *_cookie = iterator; - return B_OK; -} - - -static int -bfs_read_dir(void *_ns, void *_node, void *_cookie, long *num, - struct dirent *dirent, size_t bufferSize) -{ - FUNCTION(); - - TreeIterator *iterator = (TreeIterator *)_cookie; - if (iterator == NULL) - RETURN_ERROR(B_BAD_VALUE); - - uint16 length; - vnode_id id; - status_t status = iterator->GetNextEntry(dirent->d_name, &length, bufferSize, &id); - if (status == B_ENTRY_NOT_FOUND) { - *num = 0; - return B_OK; - } else if (status != B_OK) - RETURN_ERROR(status); - - Volume *volume = (Volume *)_ns; - - dirent->d_dev = volume->ID(); - dirent->d_ino = id; - -#ifdef KEEP_WRONG_DIRENT_RECLEN - dirent->d_reclen = length; -#else - dirent->d_reclen = sizeof(struct dirent) + length; -#endif - - *num = 1; - return B_OK; -} - - -/** Sets the TreeIterator back to the beginning of the directory - */ - -static int -bfs_rewind_dir(void * /*ns*/, void * /*node*/, void *_cookie) -{ - FUNCTION(); - TreeIterator *iterator = (TreeIterator *)_cookie; - - if (iterator == NULL) - RETURN_ERROR(B_BAD_VALUE); - - return iterator->Rewind(); -} - - -static int -bfs_close_dir(void * /*ns*/, void * /*node*/, void * /*_cookie*/) -{ - FUNCTION(); - // Do whatever you need to to close a directory, but DON'T free the cookie! - return B_OK; -} - - -static int -bfs_free_dir_cookie(void *ns, void *node, void *_cookie) -{ - TreeIterator *iterator = (TreeIterator *)_cookie; - - if (iterator == NULL) - RETURN_ERROR(B_BAD_VALUE); - - delete iterator; - return B_OK; -} - - -// #pragma mark - -// Attribute functions - - -static int -bfs_open_attrdir(void *_ns, void *_node, void **cookie) -{ - FUNCTION(); - - Inode *inode = (Inode *)_node; - if (inode == NULL || inode->Node() == NULL) - RETURN_ERROR(B_ERROR); - - AttributeIterator *iterator = new AttributeIterator(inode); - if (iterator == NULL) - RETURN_ERROR(B_NO_MEMORY); - - *cookie = iterator; - return B_OK; -} - - -static int -bfs_close_attrdir(void *ns, void *node, void *cookie) -{ - FUNCTION(); - return B_OK; -} - - -static int -bfs_free_attrdir_cookie(void *ns, void *node, void *_cookie) -{ - FUNCTION(); - AttributeIterator *iterator = (AttributeIterator *)_cookie; - - if (iterator == NULL) - RETURN_ERROR(B_BAD_VALUE); - - delete iterator; - return B_OK; -} - - -static int -bfs_rewind_attrdir(void *_ns, void *_node, void *_cookie) -{ - FUNCTION(); - - AttributeIterator *iterator = (AttributeIterator *)_cookie; - if (iterator == NULL) - RETURN_ERROR(B_BAD_VALUE); - - RETURN_ERROR(iterator->Rewind()); -} - - -static int -bfs_read_attrdir(void *_ns, void *node, void *_cookie, long *num, struct dirent *dirent, size_t bufsize) -{ - FUNCTION(); - AttributeIterator *iterator = (AttributeIterator *)_cookie; - - if (iterator == NULL) - RETURN_ERROR(B_BAD_VALUE); - - uint32 type; - size_t length; - status_t status = iterator->GetNext(dirent->d_name, &length, &type, &dirent->d_ino); - if (status == B_ENTRY_NOT_FOUND) { - *num = 0; - return B_OK; - } else if (status != B_OK) - RETURN_ERROR(status); - - Volume *volume = (Volume *)_ns; - - dirent->d_dev = volume->ID(); -#ifdef KEEP_WRONG_DIRENT_RECLEN - dirent->d_reclen = length; -#else - dirent->d_reclen = sizeof(struct dirent) + length; -#endif - - *num = 1; - return B_OK; -} - - -static int -bfs_remove_attr(void *_ns, void *_node, const char *name) -{ - FUNCTION_START(("name = \"%s\"\n", name)); - - if (_ns == NULL || _node == NULL || name == NULL) - return B_BAD_VALUE; - - Volume *volume = (Volume *)_ns; - Inode *inode = (Inode *)_node; - - status_t status = inode->CheckPermissions(W_OK); - if (status < B_OK) - return status; - -#ifdef UNSAFE_GET_VNODE - RecursiveLocker locker(volume->Lock()); -#endif - Transaction transaction(volume, inode->BlockNumber()); - - status = inode->RemoveAttribute(&transaction, name); - if (status == B_OK) { - transaction.Done(); - - notify_listener(B_ATTR_CHANGED, volume->ID(), 0, 0, inode->ID(), name); - } - - RETURN_ERROR(status); -} - - -static int -bfs_rename_attr(void *ns, void *node, const char *oldname, const char *newname) -{ - FUNCTION_START(("name = \"%s\", to = \"%s\"\n", oldname, newname)); - - // ToDo: implement bfs_rename_attr()! - // I'll skip the implementation here, and will do it for OpenBeOS - at least - // there will be an API to move one attribute to another file, making that - // function much more complicated - oh joy ;-) - - RETURN_ERROR(B_ENTRY_NOT_FOUND); -} - - -static int -bfs_stat_attr(void *ns, void *_node, const char *name, struct attr_info *attrInfo) -{ - FUNCTION_START(("name = \"%s\"\n", name)); - - Inode *inode = (Inode *)_node; - if (inode == NULL || inode->Node() == NULL) - RETURN_ERROR(B_ERROR); - - // first, try to find it in the small data region - small_data *smallData = NULL; - if (inode->SmallDataLock().Lock() == B_OK) { - if ((smallData = inode->FindSmallData((const char *)name)) != NULL) { - attrInfo->type = smallData->Type(); - attrInfo->size = smallData->DataSize(); - } - inode->SmallDataLock().Unlock(); - } - if (smallData != NULL) - return B_OK; - - // then, search in the attribute directory - Inode *attribute; - status_t status = inode->GetAttribute(name, &attribute); - if (status == B_OK) { - attrInfo->type = attribute->Type(); - attrInfo->size = attribute->Size(); - - inode->ReleaseAttribute(attribute); - return B_OK; - } - - RETURN_ERROR(status); -} - - -static int -bfs_write_attr(void *_ns, void *_node, const char *name, int type, const void *buffer, - size_t *_length, off_t pos) -{ - FUNCTION_START(("name = \"%s\"\n", name)); - if (_ns == NULL || _node == NULL || name == NULL || *name == '\0') - RETURN_ERROR(B_BAD_VALUE); - - // Writing the name attribute using this function is not allowed, - // also using the reserved indices name, last_modified, and size - // shouldn't be allowed. - // ToDo: we might think about allowing to update those values, but - // really change their corresponding values in the bfs_inode structure - if (name[0] == FILE_NAME_NAME && name[1] == '\0' - || !strcmp(name, "name") - || !strcmp(name, "last_modified") - || !strcmp(name, "size")) - RETURN_ERROR(B_NOT_ALLOWED); - - Volume *volume = (Volume *)_ns; - Inode *inode = (Inode *)_node; - - status_t status = inode->CheckPermissions(W_OK); - if (status < B_OK) - return status; - -#ifdef UNSAFE_GET_VNODE - RecursiveLocker locker(volume->Lock()); -#endif - Transaction transaction(volume, inode->BlockNumber()); - - status = inode->WriteAttribute(&transaction, name, type, pos, (const uint8 *)buffer, _length); - if (status == B_OK) { - transaction.Done(); - - notify_listener(B_ATTR_CHANGED, volume->ID(), 0, 0, inode->ID(), name); - } - - return status; -} - - -static int -bfs_read_attr(void *_ns, void *_node, const char *name, int type, void *buffer, - size_t *_length, off_t pos) -{ - FUNCTION(); - Inode *inode = (Inode *)_node; - - if (inode == NULL || name == NULL || *name == '\0' || buffer == NULL) - RETURN_ERROR(B_BAD_VALUE); - - status_t status = inode->CheckPermissions(R_OK); - if (status < B_OK) - return status; - - return inode->ReadAttribute(name, type, pos, (uint8 *)buffer, _length); -} - - -// #pragma mark - -// Index functions - - -static int -bfs_open_indexdir(void *_ns, void **_cookie) -{ - FUNCTION(); - if (_ns == NULL || _cookie == NULL) - RETURN_ERROR(B_BAD_VALUE); - - Volume *volume = (Volume *)_ns; - - if (volume->IndicesNode() == NULL) - RETURN_ERROR(B_ENTRY_NOT_FOUND); - - // Since the indices root node is just a directory, and we are storing - // a pointer to it in our Volume object, we can just use the directory - // traversal functions. - // In fact we're storing it in the Volume object for that reason. - - RETURN_ERROR(bfs_open_dir(_ns, volume->IndicesNode(), _cookie)); -} - - -static int -bfs_close_indexdir(void *_ns, void *_cookie) -{ - FUNCTION(); - if (_ns == NULL || _cookie == NULL) - RETURN_ERROR(B_BAD_VALUE); - - Volume *volume = (Volume *)_ns; - RETURN_ERROR(bfs_close_dir(_ns, volume->IndicesNode(), _cookie)); -} - - -static int -bfs_free_indexdir_cookie(void *_ns, void *_node, void *_cookie) -{ - FUNCTION(); - if (_ns == NULL || _cookie == NULL) - RETURN_ERROR(B_BAD_VALUE); - - Volume *volume = (Volume *)_ns; - RETURN_ERROR(bfs_free_dir_cookie(_ns, volume->IndicesNode(), _cookie)); -} - - -static int -bfs_rewind_indexdir(void *_ns, void *_cookie) -{ - FUNCTION(); - if (_ns == NULL || _cookie == NULL) - RETURN_ERROR(B_BAD_VALUE); - - Volume *volume = (Volume *)_ns; - RETURN_ERROR(bfs_rewind_dir(_ns, volume->IndicesNode(), _cookie)); -} - - -static int -bfs_read_indexdir(void *_ns, void *_cookie, long *num, struct dirent *dirent, size_t bufferSize) -{ - FUNCTION(); - if (_ns == NULL || _cookie == NULL) - RETURN_ERROR(B_BAD_VALUE); - - Volume *volume = (Volume *)_ns; - RETURN_ERROR(bfs_read_dir(_ns, volume->IndicesNode(), _cookie, num, dirent, bufferSize)); -} - - -static int -bfs_create_index(void *_ns, const char *name, int type, int flags) -{ - FUNCTION_START(("name = \"%s\", type = %d, flags = %d\n", name, type, flags)); - if (_ns == NULL || name == NULL || *name == '\0') - return B_BAD_VALUE; - - Volume *volume = (Volume *)_ns; - - if (volume->IsReadOnly()) - return B_READ_ONLY_DEVICE; - - // only root users are allowed to create indices - if (geteuid() != 0) - return B_NOT_ALLOWED; - -#ifdef UNSAFE_GET_VNODE - RecursiveLocker locker(volume->Lock()); -#endif - Transaction transaction(volume, volume->Indices()); - - Index index(volume); - status_t status = index.Create(&transaction, name, type); - - if (status == B_OK) - transaction.Done(); - - RETURN_ERROR(status); -} - - -static int -bfs_remove_index(void *_ns, const char *name) -{ - FUNCTION(); - if (_ns == NULL || name == NULL || *name == '\0') - return B_BAD_VALUE; - - Volume *volume = (Volume *)_ns; - - if (volume->IsReadOnly()) - return B_READ_ONLY_DEVICE; - - // only root users are allowed to remove indices - if (geteuid() != 0) - return B_NOT_ALLOWED; - - Inode *indices; - if ((indices = volume->IndicesNode()) == NULL) - return B_ENTRY_NOT_FOUND; - -#ifdef UNSAFE_GET_VNODE - RecursiveLocker locker(volume->Lock()); -#endif - Transaction transaction(volume, volume->Indices()); - - status_t status = indices->Remove(&transaction, name); - if (status == B_OK) - transaction.Done(); - - RETURN_ERROR(status); -} - - -static int -bfs_rename_index(void *ns, const char *oldname, const char *newname) -{ - FUNCTION_START(("from = %s, to = %s\n", oldname, newname)); - - // Well, renaming an index doesn't make that much sense, as you - // would also need to remove every file in it (or the index - // would contain wrong data) - // But in that case, you can better remove the old one, and - // create a new one... - // There is also no way to call this function from a userland - // application. - - RETURN_ERROR(B_ENTRY_NOT_FOUND); -} - - -static int -bfs_stat_index(void *_ns, const char *name, struct index_info *indexInfo) -{ - FUNCTION_START(("name = %s\n", name)); - if (_ns == NULL || name == NULL || indexInfo == NULL) - RETURN_ERROR(B_BAD_VALUE); - - Volume *volume = (Volume *)_ns; - Index index(volume); - status_t status = index.SetTo(name); - if (status < B_OK) - RETURN_ERROR(status); - - bfs_inode *node = index.Node()->Node(); - - indexInfo->type = index.Type(); - indexInfo->size = node->data.Size(); - indexInfo->modification_time = (time_t)(node->LastModifiedTime() >> INODE_TIME_SHIFT); - indexInfo->creation_time = (time_t)(node->CreateTime() >> INODE_TIME_SHIFT); - indexInfo->uid = node->UserID(); - indexInfo->gid = node->GroupID(); - - return B_OK; -} - - -// #pragma mark - -// Query functions - - -static int -bfs_open_query(void *_ns, const char *queryString, ulong flags, port_id port, - long token, void **cookie) -{ - FUNCTION(); - if (_ns == NULL || queryString == NULL || cookie == NULL) - RETURN_ERROR(B_BAD_VALUE); - - PRINT(("query = \"%s\", flags = %lu, port_id = %ld, token = %ld\n", queryString, flags, port, token)); - - Volume *volume = (Volume *)_ns; - - Expression *expression = new Expression((char *)queryString); - if (expression == NULL) - RETURN_ERROR(B_NO_MEMORY); - - if (expression->InitCheck() < B_OK) { - FATAL(("Could not parse query, stopped at: \"%s\"\n", expression->Position())); - delete expression; - RETURN_ERROR(B_BAD_VALUE); - } - - Query *query = new Query(volume, expression, flags); - if (query == NULL) { - delete expression; - RETURN_ERROR(B_NO_MEMORY); - } - - if (flags & B_LIVE_QUERY) - query->SetLiveMode(port, token); - - *cookie = (void *)query; - - return B_OK; -} - - -static int -bfs_close_query(void *ns, void *cookie) -{ - FUNCTION(); - return B_OK; -} - - -static int -bfs_free_query_cookie(void *ns, void *node, void *cookie) -{ - FUNCTION(); - if (cookie == NULL) - RETURN_ERROR(B_BAD_VALUE); - - Query *query = (Query *)cookie; - Expression *expression = query->GetExpression(); - delete query; - delete expression; - - return B_OK; -} - - -static int -bfs_read_query(void */*ns*/, void *cookie, long *num, struct dirent *dirent, size_t bufferSize) -{ - FUNCTION(); - Query *query = (Query *)cookie; - if (query == NULL) - RETURN_ERROR(B_BAD_VALUE); - - status_t status = query->GetNextEntry(dirent, bufferSize); - if (status == B_OK) - *num = 1; - else if (status == B_ENTRY_NOT_FOUND) - *num = 0; - else - return status; - - return B_OK; -} - diff --git a/src/add-ons/kernel/file_systems/bfs/lock.h b/src/add-ons/kernel/file_systems/bfs/lock.h deleted file mode 100644 index 905470270d..0000000000 --- a/src/add-ons/kernel/file_systems/bfs/lock.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - Copyright 1999-2001, Be Incorporated. All Rights Reserved. - This file may be used under the terms of the Be Sample Code License. -*/ - -#ifndef _LOCK_H -#define _LOCK_H - -#include - -#include - -#ifdef __cplusplus - extern "C" { -#else - typedef struct lock lock; - typedef struct mlock mlock; -#endif - - -struct lock { - sem_id s; - long c; -}; - -struct mlock { - sem_id s; -}; - -extern _IMPEXP_KERNEL int new_lock(lock *l, const char *name); -extern _IMPEXP_KERNEL int free_lock(lock *l); - -#ifdef LOCK -#undef LOCK -#endif - -#define LOCK(l) if (atomic_add(&l.c, -1) <= 0) acquire_sem(l.s); -#define UNLOCK(l) if (atomic_add(&l.c, 1) < 0) release_sem(l.s); - -extern _IMPEXP_KERNEL int new_mlock(mlock *l, long c, const char *name); -extern _IMPEXP_KERNEL int free_mlock(mlock *l); - -#define LOCKM(l,cnt) acquire_sem_etc(l.s, cnt, 0, 0) -#define UNLOCKM(l,cnt) release_sem_etc(l.s, cnt, 0) - - -#ifdef __cplusplus - } // extern "C" -#endif - -#endif