* Changed Inode::WriteAttribute() so that it no longer reads from the old

attribute when its size was 0 - this also fixes calling Index::Update()
  with invalid values, and therefore bug #1178.
* Style cleanup: honour 80 column limit a bit more, use doxygen style comments.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@20809 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler
2007-04-25 10:47:24 +00:00
parent 6d1144bbca
commit cb0572cac5
2 changed files with 350 additions and 270 deletions
+68 -51
View File
@@ -1,9 +1,10 @@
/* Index - index access functions /*
* * Copyright 2001-2007, Axel Dörfler, [email protected].
* Copyright 2001-2006, Axel Dörfler, [email protected].
* This file may be used under the terms of the MIT License. * This file may be used under the terms of the MIT License.
*/ */
//! index access functions
#include "Debug.h" #include "Debug.h"
#include "Index.h" #include "Index.h"
@@ -50,13 +51,13 @@ Index::Unset()
} }
/** Sets the index to specified one. Returns an error if the index could /*!
* not be found or initialized. Sets the index to specified one. Returns an error if the index could
* Note, Index::Update() may be called on the object even if this method not be found or initialized.
* failed previously. In this case, it will only update live queries for Note, Index::Update() may be called on the object even if this method
* the updated attribute. failed previously. In this case, it will only update live queries for
*/ the updated attribute.
*/
status_t status_t
Index::SetTo(const char *name) Index::SetTo(const char *name)
{ {
@@ -88,7 +89,8 @@ Index::SetTo(const char *name)
return B_ENTRY_NOT_FOUND; return B_ENTRY_NOT_FOUND;
if (fNode == NULL) { if (fNode == NULL) {
FATAL(("fatal error at Index::InitCheck(), get_vnode() returned NULL pointer\n")); FATAL(("fatal error at Index::InitCheck(), get_vnode() returned "
"NULL pointer\n"));
return B_ERROR; return B_ERROR;
} }
@@ -97,19 +99,20 @@ Index::SetTo(const char *name)
} }
/** Returns a standard type code for the stat() index type codes. Returns /*!
* zero if the type is not known (can only happen if the mode field is Returns a standard type code for the stat() index type codes. Returns
* corrupted somehow or not that of an index). zero if the type is not known (can only happen if the mode field is
*/ corrupted somehow or not that of an index).
*/
uint32 uint32
Index::Type() Index::Type()
{ {
if (fNode == NULL) if (fNode == NULL)
return 0; return 0;
switch (fNode->Mode() & (S_STR_INDEX | S_INT_INDEX | S_UINT_INDEX | S_LONG_LONG_INDEX | switch (fNode->Mode() & (S_STR_INDEX | S_INT_INDEX | S_UINT_INDEX
S_ULONG_LONG_INDEX | S_FLOAT_INDEX | S_DOUBLE_INDEX)) { | S_LONG_LONG_INDEX | S_ULONG_LONG_INDEX | S_FLOAT_INDEX
| S_DOUBLE_INDEX)) {
case S_INT_INDEX: case S_INT_INDEX:
return B_INT32_TYPE; return B_INT32_TYPE;
case S_UINT_INDEX: case S_UINT_INDEX:
@@ -136,8 +139,9 @@ Index::KeySize()
if (fNode == NULL) if (fNode == NULL)
return 0; return 0;
int32 mode = fNode->Mode() & (S_STR_INDEX | S_INT_INDEX | S_UINT_INDEX | S_LONG_LONG_INDEX | int32 mode = fNode->Mode() & (S_STR_INDEX | S_INT_INDEX | S_UINT_INDEX
S_ULONG_LONG_INDEX | S_FLOAT_INDEX | S_DOUBLE_INDEX); | S_LONG_LONG_INDEX | S_ULONG_LONG_INDEX | S_FLOAT_INDEX
| S_DOUBLE_INDEX);
if (mode == S_STR_INDEX) if (mode == S_STR_INDEX)
// string indices don't have a fixed key size // string indices don't have a fixed key size
@@ -209,16 +213,17 @@ Index::Create(Transaction &transaction, const char *name, uint32 type)
} }
/** Updates the specified index, the oldKey will be removed from, the newKey /*!
* inserted into the tree. Updates the specified index, the oldKey will be removed from, the newKey
* If the method returns B_BAD_INDEX, it means the index couldn't be found - inserted into the tree.
* the most common reason will be that the index doesn't exist. If the method returns B_BAD_INDEX, it means the index couldn't be found -
* You may not want to let the whole transaction fail because of that. the most common reason will be that the index doesn't exist.
*/ You may not want to let the whole transaction fail because of that.
*/
status_t status_t
Index::Update(Transaction &transaction, const char *name, int32 type, const uint8 *oldKey, Index::Update(Transaction &transaction, const char *name, int32 type,
uint16 oldLength, const uint8 *newKey, uint16 newLength, Inode *inode) const uint8 *oldKey, uint16 oldLength, const uint8 *newKey,
uint16 newLength, Inode *inode)
{ {
if (name == NULL if (name == NULL
|| oldKey == NULL && newKey == NULL || oldKey == NULL && newKey == NULL
@@ -237,11 +242,12 @@ Index::Update(Transaction &transaction, const char *name, int32 type, const uint
return B_OK; return B_OK;
// update all live queries about the change, if they have an index or not // update all live queries about the change, if they have an index or not
if (type != 0) if (type != 0) {
fVolume->UpdateLiveQueries(inode, name, type, oldKey, oldLength, newKey, newLength); fVolume->UpdateLiveQueries(inode, name, type, oldKey, oldLength,
newKey, newLength);
}
status_t status; if (((name != fName || strcmp(name, fName)) && SetTo(name) < B_OK)
if (((name != fName || strcmp(name, fName)) && (status = SetTo(name)) < B_OK)
|| fNode == NULL) || fNode == NULL)
return B_BAD_INDEX; return B_BAD_INDEX;
@@ -250,19 +256,23 @@ Index::Update(Transaction &transaction, const char *name, int32 type, const uint
return B_OK; return B_OK;
// same for the live query update // same for the live query update
if (type == 0) if (type == 0) {
fVolume->UpdateLiveQueries(inode, name, Type(), oldKey, oldLength, newKey, newLength); fVolume->UpdateLiveQueries(inode, name, Type(), oldKey, oldLength,
newKey, newLength);
}
BPlusTree *tree; BPlusTree *tree;
if ((status = Node()->GetTree(&tree)) < B_OK) status_t status = Node()->GetTree(&tree);
if (status < B_OK)
return status; return status;
// remove the old key from the tree // remove the old key from the tree
if (oldKey != NULL) { if (oldKey != NULL) {
status = tree->Remove(transaction, (const uint8 *)oldKey, oldLength, inode->ID()); status = tree->Remove(transaction, (const uint8 *)oldKey, oldLength,
inode->ID());
if (status == B_ENTRY_NOT_FOUND) { if (status == B_ENTRY_NOT_FOUND) {
// That's not nice, but should be no reason to let the whole thing fail // That's not nice, but no reason to let the whole thing fail
INFORM(("Could not find value in index \"%s\"!\n", name)); INFORM(("Could not find value in index \"%s\"!\n", name));
} else if (status < B_OK) } else if (status < B_OK)
return status; return status;
@@ -270,8 +280,10 @@ Index::Update(Transaction &transaction, const char *name, int32 type, const uint
// add the new key to the tree // add the new key to the tree
if (newKey != NULL) if (newKey != NULL) {
status = tree->Insert(transaction, (const uint8 *)newKey, newLength, inode->ID()); status = tree->Insert(transaction, (const uint8 *)newKey, newLength,
inode->ID());
}
RETURN_ERROR(status); RETURN_ERROR(status);
} }
@@ -297,8 +309,8 @@ Index::UpdateName(Transaction &transaction, const char *oldName,
{ {
ASSERT(inode->IsRegularNode()); ASSERT(inode->IsRegularNode());
uint16 oldLength = oldName ? strlen(oldName) : 0; uint16 oldLength = oldName != NULL ? strlen(oldName) : 0;
uint16 newLength = newName ? strlen(newName) : 0; uint16 newLength = newName != NULL ? strlen(newName) : 0;
return Update(transaction, "name", B_STRING_TYPE, (uint8 *)oldName, return Update(transaction, "name", B_STRING_TYPE, (uint8 *)oldName,
oldLength, (uint8 *)newName, newLength, inode); oldLength, (uint8 *)newName, newLength, inode);
} }
@@ -310,7 +322,8 @@ Index::InsertSize(Transaction &transaction, Inode *inode)
ASSERT(inode->IsFile()); ASSERT(inode->IsFile());
off_t size = inode->Size(); off_t size = inode->Size();
return Update(transaction, "size", B_INT64_TYPE, NULL, 0, (uint8 *)&size, sizeof(int64), inode); return Update(transaction, "size", B_INT64_TYPE, NULL, 0, (uint8 *)&size,
sizeof(int64), inode);
} }
@@ -321,7 +334,8 @@ Index::RemoveSize(Transaction &transaction, Inode *inode)
// Inode::OldSize() is the size that's in the index // Inode::OldSize() is the size that's in the index
off_t size = inode->OldSize(); off_t size = inode->OldSize();
return Update(transaction, "size", B_INT64_TYPE, (uint8 *)&size, sizeof(int64), NULL, 0, inode); return Update(transaction, "size", B_INT64_TYPE, (uint8 *)&size,
sizeof(int64), NULL, 0, inode);
} }
@@ -333,8 +347,9 @@ Index::UpdateSize(Transaction &transaction, Inode *inode)
off_t oldSize = inode->OldSize(); off_t oldSize = inode->OldSize();
off_t newSize = inode->Size(); off_t newSize = inode->Size();
status_t status = Update(transaction, "size", B_INT64_TYPE, (uint8 *)&oldSize, status_t status = Update(transaction, "size", B_INT64_TYPE,
sizeof(int64), (uint8 *)&newSize, sizeof(int64), inode); (uint8 *)&oldSize, sizeof(int64), (uint8 *)&newSize, sizeof(int64),
inode);
if (status == B_OK) if (status == B_OK)
inode->UpdateOldSize(); inode->UpdateOldSize();
@@ -360,13 +375,14 @@ Index::RemoveLastModified(Transaction &transaction, Inode *inode)
// Inode::OldLastModified() is the value which is in the index // Inode::OldLastModified() is the value which is in the index
off_t modified = inode->OldLastModified(); off_t modified = inode->OldLastModified();
return Update(transaction, "last_modified", B_INT64_TYPE, (uint8 *)&modified, return Update(transaction, "last_modified", B_INT64_TYPE,
sizeof(int64), NULL, 0, inode); (uint8 *)&modified, sizeof(int64), NULL, 0, inode);
} }
status_t status_t
Index::UpdateLastModified(Transaction &transaction, Inode *inode, off_t modified) Index::UpdateLastModified(Transaction &transaction, Inode *inode,
off_t modified)
{ {
ASSERT(inode->IsFile() || inode->IsSymLink()); ASSERT(inode->IsFile() || inode->IsSymLink());
@@ -375,8 +391,9 @@ Index::UpdateLastModified(Transaction &transaction, Inode *inode, off_t modified
modified = (bigtime_t)time(NULL) << INODE_TIME_SHIFT; modified = (bigtime_t)time(NULL) << INODE_TIME_SHIFT;
modified |= fVolume->GetUniqueID() & INODE_TIME_MASK; modified |= fVolume->GetUniqueID() & INODE_TIME_MASK;
status_t status = Update(transaction, "last_modified", B_INT64_TYPE, (uint8 *)&oldModified, status_t status = Update(transaction, "last_modified", B_INT64_TYPE,
sizeof(int64), (uint8 *)&modified, sizeof(int64), inode); (uint8 *)&oldModified, sizeof(int64), (uint8 *)&modified,
sizeof(int64), inode);
inode->Node().last_modified_time = HOST_ENDIAN_TO_BFS_INT64(modified); inode->Node().last_modified_time = HOST_ENDIAN_TO_BFS_INT64(modified);
if (status == B_OK) if (status == B_OK)
+281 -218
View File
@@ -166,7 +166,7 @@ bfs_inode::InitCheck(Volume *volume)
} }
// #pragma mark - // #pragma mark - Inode
Inode::Inode(Volume *volume, vnode_id id) Inode::Inode(Volume *volume, vnode_id id)
@@ -349,14 +349,14 @@ Inode::_RemoveIterator(AttributeIterator *iterator)
} }
/** Tries to free up "bytes" space in the small_data section by moving /*!
* attributes to real files. Used for system attributes like the name. Tries to free up "bytes" space in the small_data section by moving
* You need to hold the fSmallDataLock when you call this method attributes to real files. Used for system attributes like the name.
*/ You need to hold the fSmallDataLock when you call this method
*/
status_t status_t
Inode::_MakeSpaceForSmallData(Transaction &transaction, bfs_inode *node, const char *name, Inode::_MakeSpaceForSmallData(Transaction &transaction, bfs_inode *node,
int32 bytes) const char *name, int32 bytes)
{ {
ASSERT(fSmallDataLock.IsLocked()); ASSERT(fSmallDataLock.IsLocked());
@@ -414,11 +414,11 @@ Inode::_MakeSpaceForSmallData(Transaction &transaction, bfs_inode *node, const c
} }
/** Private function which removes the given attribute from the small_data /*!
* section. Private function which removes the given attribute from the small_data
* You need to hold the fSmallDataLock when you call this method section.
*/ You need to hold the fSmallDataLock when you call this method
*/
status_t status_t
Inode::_RemoveSmallData(bfs_inode *node, small_data *item, int32 index) Inode::_RemoveSmallData(bfs_inode *node, small_data *item, int32 index)
{ {
@@ -446,16 +446,15 @@ Inode::_RemoveSmallData(bfs_inode *node, small_data *item, int32 index)
// update all current iterators // update all current iterators
AttributeIterator *iterator = NULL; AttributeIterator *iterator = NULL;
while ((iterator = fIterators.Next(iterator)) != NULL) while ((iterator = fIterators.Next(iterator)) != NULL) {
iterator->Update(index, -1); iterator->Update(index, -1);
}
return B_OK; return B_OK;
} }
/** Removes the given attribute from the small_data section. //! Removes the given attribute from the small_data section.
*/
status_t status_t
Inode::_RemoveSmallData(Transaction &transaction, NodeGetter &nodeGetter, Inode::_RemoveSmallData(Transaction &transaction, NodeGetter &nodeGetter,
const char *name) const char *name)
@@ -487,18 +486,18 @@ Inode::_RemoveSmallData(Transaction &transaction, NodeGetter &nodeGetter,
} }
/** Try to place the given attribute in the small_data section - if the /*!
* new attribute is too big to fit in that section, it returns B_DEVICE_FULL. Try to place the given attribute in the small_data section - if the
* In that case, the attribute should be written to a real attribute file; new attribute is too big to fit in that section, it returns B_DEVICE_FULL.
* it's the caller's responsibility to remove any existing attributes in the small In that case, the attribute should be written to a real attribute file;
* data section if that's the case. it's the caller's responsibility to remove any existing attributes in the small
* data section if that's the case.
* Note that you need to write back the inode yourself after having called that
* method - it's a bad API decision that it needs a transaction but enforces you
* to write back the inode all by yourself, but it's just more efficient in most
* cases...
*/
Note that you need to write back the inode yourself after having called that
method - it's a bad API decision that it needs a transaction but enforces you
to write back the inode all by yourself, but it's just more efficient in most
cases...
*/
status_t status_t
Inode::_AddSmallData(Transaction &transaction, NodeGetter &nodeGetter, Inode::_AddSmallData(Transaction &transaction, NodeGetter &nodeGetter,
const char *name, uint32 type, const uint8 *data, size_t length, bool force) const char *name, uint32 type, const uint8 *data, size_t length, bool force)
@@ -622,25 +621,26 @@ Inode::_AddSmallData(Transaction &transaction, NodeGetter &nodeGetter,
// update all current iterators // update all current iterators
AttributeIterator *iterator = NULL; AttributeIterator *iterator = NULL;
while ((iterator = fIterators.Next(iterator)) != NULL) while ((iterator = fIterators.Next(iterator)) != NULL) {
iterator->Update(index, 1); iterator->Update(index, 1);
}
return B_OK; return B_OK;
} }
/** Iterates through the small_data section of an inode. /*!
* To start at the beginning of this section, you let smallData Iterates through the small_data section of an inode.
* point to NULL, like: To start at the beginning of this section, you let smallData
* small_data *data = NULL; point to NULL, like:
* while (inode->GetNextSmallData(&data) { ... } small_data *data = NULL;
* while (inode->GetNextSmallData(&data) { ... }
* This function is reentrant and doesn't allocate any memory;
* you can safely stop calling it at any point (you don't need
* to iterate through the whole list).
* You need to hold the fSmallDataLock when you call this method
*/
This function is reentrant and doesn't allocate any memory;
you can safely stop calling it at any point (you don't need
to iterate through the whole list).
You need to hold the fSmallDataLock when you call this method
*/
status_t status_t
Inode::_GetNextSmallData(bfs_inode *node, small_data **_smallData) const Inode::_GetNextSmallData(bfs_inode *node, small_data **_smallData) const
{ {
@@ -667,11 +667,11 @@ Inode::_GetNextSmallData(bfs_inode *node, small_data **_smallData) const
} }
/** Finds the attribute "name" in the small data section, and /*!
* returns a pointer to it (or NULL if it doesn't exist). Finds the attribute "name" in the small data section, and
* You need to hold the fSmallDataLock when you call this method returns a pointer to it (or NULL if it doesn't exist).
*/ You need to hold the fSmallDataLock when you call this method
*/
small_data * small_data *
Inode::FindSmallData(const bfs_inode *node, const char *name) const Inode::FindSmallData(const bfs_inode *node, const char *name) const
{ {
@@ -686,11 +686,11 @@ Inode::FindSmallData(const bfs_inode *node, const char *name) const
} }
/** Returns a pointer to the node's name if present in the small data /*!
* section, NULL otherwise. Returns a pointer to the node's name if present in the small data
* You need to hold the fSmallDataLock when you call this method section, NULL otherwise.
*/ You need to hold the fSmallDataLock when you call this method
*/
const char * const char *
Inode::Name(const bfs_inode *node) const Inode::Name(const bfs_inode *node) const
{ {
@@ -706,10 +706,10 @@ Inode::Name(const bfs_inode *node) const
} }
/** Copies the node's name into the provided buffer. /*!
* The buffer should be B_FILE_NAME_LENGTH bytes large. Copies the node's name into the provided buffer.
*/ The buffer should be B_FILE_NAME_LENGTH bytes large.
*/
status_t status_t
Inode::GetName(char *buffer, size_t size) const Inode::GetName(char *buffer, size_t size) const
{ {
@@ -725,13 +725,13 @@ Inode::GetName(char *buffer, size_t size) const
} }
/** Changes or set the name of a file: in the inode small_data section only, it /*!
* doesn't change it in the parent directory's b+tree. Changes or set the name of a file: in the inode small_data section only, it
* Note that you need to write back the inode yourself after having called doesn't change it in the parent directory's b+tree.
* that method. It suffers from the same API decision as AddSmallData() does Note that you need to write back the inode yourself after having called
* (and for the same reason). that method. It suffers from the same API decision as AddSmallData() does
*/ (and for the same reason).
*/
status_t status_t
Inode::SetName(Transaction &transaction, const char *name) Inode::SetName(Transaction &transaction, const char *name)
{ {
@@ -793,11 +793,11 @@ Inode::_RemoveAttribute(Transaction &transaction, const char *name,
} }
/** Reads data from the specified attribute. /*!
* This is a high-level attribute function that understands attributes Reads data from the specified attribute.
* in the small_data section as well as real attribute files. This is a high-level attribute function that understands attributes
*/ in the small_data section as well as real attribute files.
*/
status_t status_t
Inode::ReadAttribute(const char *name, int32 type, off_t pos, uint8 *buffer, Inode::ReadAttribute(const char *name, int32 type, off_t pos, uint8 *buffer,
size_t *_length) size_t *_length)
@@ -843,11 +843,11 @@ Inode::ReadAttribute(const char *name, int32 type, off_t pos, uint8 *buffer,
} }
/** Writes data to the specified attribute. /*!
* This is a high-level attribute function that understands attributes Writes data to the specified attribute.
* in the small_data section as well as real attribute files. This is a high-level attribute function that understands attributes
*/ in the small_data section as well as real attribute files.
*/
status_t status_t
Inode::WriteAttribute(Transaction &transaction, const char *name, int32 type, Inode::WriteAttribute(Transaction &transaction, const char *name, int32 type,
off_t pos, const uint8 *buffer, size_t *_length) off_t pos, const uint8 *buffer, size_t *_length)
@@ -856,7 +856,7 @@ Inode::WriteAttribute(Transaction &transaction, const char *name, int32 type,
uint8 oldBuffer[BPLUSTREE_MAX_KEY_LENGTH], *oldData = NULL; uint8 oldBuffer[BPLUSTREE_MAX_KEY_LENGTH], *oldData = NULL;
size_t oldLength = 0; size_t oldLength = 0;
// ToDo: we actually depend on that the contents of "buffer" are constant. // TODO: we actually depend on that the contents of "buffer" are constant.
// If they get changed during the write (hey, user programs), we may mess // If they get changed during the write (hey, user programs), we may mess
// up our index trees! // up our index trees!
@@ -874,9 +874,11 @@ Inode::WriteAttribute(Transaction &transaction, const char *name, int32 type,
small_data *smallData = FindSmallData(node.Node(), name); small_data *smallData = FindSmallData(node.Node(), name);
if (smallData != NULL) { if (smallData != NULL) {
oldLength = smallData->DataSize(); oldLength = smallData->DataSize();
if (oldLength > BPLUSTREE_MAX_KEY_LENGTH) if (oldLength > 0) {
oldLength = BPLUSTREE_MAX_KEY_LENGTH; if (oldLength > BPLUSTREE_MAX_KEY_LENGTH)
memcpy(oldData = oldBuffer, smallData->Data(), oldLength); oldLength = BPLUSTREE_MAX_KEY_LENGTH;
memcpy(oldData = oldBuffer, smallData->Data(), oldLength);
}
} }
fSmallDataLock.Unlock(); fSmallDataLock.Unlock();
@@ -903,7 +905,7 @@ Inode::WriteAttribute(Transaction &transaction, const char *name, int32 type,
if (attribute != NULL) { if (attribute != NULL) {
if (attribute->Lock().LockWrite() == B_OK) { if (attribute->Lock().LockWrite() == B_OK) {
// save the old attribute data (if this fails, oldLength will reflect it) // save the old attribute data (if this fails, oldLength will reflect it)
if (fVolume->CheckForLiveQuery(name)) { if (fVolume->CheckForLiveQuery(name) && attribute->Size() > 0) {
oldLength = BPLUSTREE_MAX_KEY_LENGTH; oldLength = BPLUSTREE_MAX_KEY_LENGTH;
if (attribute->ReadAt(0, oldBuffer, &oldLength) == B_OK) if (attribute->ReadAt(0, oldBuffer, &oldLength) == B_OK)
oldData = oldBuffer; oldData = oldBuffer;
@@ -931,7 +933,7 @@ Inode::WriteAttribute(Transaction &transaction, const char *name, int32 type,
ReleaseAttribute(attribute); ReleaseAttribute(attribute);
} }
// ToDo: find a better way than this "pos" thing (the begin of the old key // TODO: find a better way than this "pos" thing (the begin of the old key
// must be copied to the start of the new one for a comparison) // must be copied to the start of the new one for a comparison)
if (status == B_OK && pos == 0) { if (status == B_OK && pos == 0) {
// index only the first BPLUSTREE_MAX_KEY_LENGTH bytes // index only the first BPLUSTREE_MAX_KEY_LENGTH bytes
@@ -941,18 +943,20 @@ Inode::WriteAttribute(Transaction &transaction, const char *name, int32 type,
// Update index. Note, Index::Update() may be called even if initializing // Update index. Note, Index::Update() may be called even if initializing
// the index failed - it will just update the live queries in this case // the index failed - it will just update the live queries in this case
if (pos < length || pos < oldLength) if (pos < length || pos < oldLength) {
index.Update(transaction, name, type, oldData, oldLength, buffer, length, this); index.Update(transaction, name, type, oldData, oldLength, buffer,
length, this);
}
} }
return status; return status;
} }
/** Removes the specified attribute from the inode. /*!
* This is a high-level attribute function that understands attributes Removes the specified attribute from the inode.
* in the small_data section as well as real attribute files. This is a high-level attribute function that understands attributes
*/ in the small_data section as well as real attribute files.
*/
status_t status_t
Inode::RemoveAttribute(Transaction &transaction, const char *name) Inode::RemoveAttribute(Transaction &transaction, const char *name)
{ {
@@ -985,7 +989,7 @@ Inode::RemoveAttribute(Transaction &transaction, const char *name)
status_t status_t
Inode::GetAttribute(const char *name, Inode **attribute) Inode::GetAttribute(const char *name, Inode **_attribute)
{ {
// does this inode even have attributes? // does this inode even have attributes?
if (Attributes().IsZero()) if (Attributes().IsZero())
@@ -1002,13 +1006,15 @@ Inode::GetAttribute(const char *name, Inode **attribute)
status_t status = attributes->GetTree(&tree); status_t status = attributes->GetTree(&tree);
if (status == B_OK) { if (status == B_OK) {
vnode_id id; vnode_id id;
if ((status = tree->Find((uint8 *)name, (uint16)strlen(name), &id)) == B_OK) { status = tree->Find((uint8 *)name, (uint16)strlen(name), &id);
if (status == B_OK) {
Vnode vnode(fVolume, id); Vnode vnode(fVolume, id);
Inode *inode;
// Check if the attribute is really an attribute // Check if the attribute is really an attribute
if (vnode.Get(attribute) < B_OK if (vnode.Get(&inode) < B_OK || !inode->IsAttribute())
|| !(*attribute)->IsAttribute())
return B_ERROR; return B_ERROR;
*_attribute = inode;
vnode.Keep(); vnode.Keep();
return B_OK; return B_OK;
} }
@@ -1049,15 +1055,15 @@ Inode::CreateAttribute(Transaction &transaction, const char *name, uint32 type,
} }
// #pragma mark - // #pragma mark - directory tree
/** Gives the caller direct access to the b+tree for a given directory. /*!
* The tree is no longer created on demand, but when the inode is first Gives the caller direct access to the b+tree for a given directory.
* created. That will report any potential errors upfront, saves locking, The tree is no longer created on demand, but when the inode is first
* and should work as good (though a bit slower). created. That will report any potential errors upfront, saves locking,
*/ and should work as good (though a bit slower).
*/
status_t status_t
Inode::GetTree(BPlusTree **tree) Inode::GetTree(BPlusTree **tree)
{ {
@@ -1088,7 +1094,8 @@ Inode::IsEmpty()
char name[BPLUSTREE_MAX_KEY_LENGTH]; char name[BPLUSTREE_MAX_KEY_LENGTH];
uint16 length; uint16 length;
vnode_id id; vnode_id id;
while (iterator.GetNextEntry(name, &length, B_FILE_NAME_LENGTH, &id) == B_OK) { while (iterator.GetNextEntry(name, &length, B_FILE_NAME_LENGTH,
&id) == B_OK) {
if (Mode() & (S_ATTR_DIR | S_INDEX_DIR)) if (Mode() & (S_ATTR_DIR | S_INDEX_DIR))
return false; return false;
@@ -1099,14 +1106,17 @@ Inode::IsEmpty()
} }
/** Finds the block_run where "pos" is located in the data_stream of // #pragma mark - data stream
* the inode.
* If successful, "offset" will then be set to the file offset
* of the block_run returned; so "pos - offset" is for the block_run
* what "pos" is for the whole stream.
* The caller has to make sure that "pos" is inside the stream.
*/
/*!
Finds the block_run where "pos" is located in the data_stream of
the inode.
If successful, "offset" will then be set to the file offset
of the block_run returned; so "pos - offset" is for the block_run
what "pos" is for the whole stream.
The caller has to make sure that "pos" is inside the stream.
*/
status_t status_t
Inode::FindBlockRun(off_t pos, block_run &run, off_t &offset) Inode::FindBlockRun(off_t pos, block_run &run, off_t &offset)
{ {
@@ -1121,7 +1131,8 @@ Inode::FindBlockRun(off_t pos, block_run &run, off_t &offset)
CachedBlock cached(fVolume); CachedBlock cached(fVolume);
off_t start = pos - data->MaxIndirectRange(); off_t start = pos - data->MaxIndirectRange();
int32 indirectSize = (1L << (INDIRECT_BLOCKS_SHIFT + cached.BlockShift())) int32 indirectSize = (1L << (INDIRECT_BLOCKS_SHIFT
+ cached.BlockShift()))
* (fVolume->BlockSize() / sizeof(block_run)); * (fVolume->BlockSize() / sizeof(block_run));
int32 directSize = NUM_ARRAY_BLOCKS << cached.BlockShift(); int32 directSize = NUM_ARRAY_BLOCKS << cached.BlockShift();
int32 index = start / indirectSize; int32 index = start / indirectSize;
@@ -1138,12 +1149,14 @@ Inode::FindBlockRun(off_t pos, block_run &run, off_t &offset)
int32 current = (start % indirectSize) / directSize; int32 current = (start % indirectSize) / directSize;
indirect = (block_run *)cached.SetTo( indirect = (block_run *)cached.SetTo(
fVolume->ToBlock(indirect[index % runsPerBlock]) + current / runsPerBlock); fVolume->ToBlock(indirect[index % runsPerBlock])
+ current / runsPerBlock);
if (indirect == NULL) if (indirect == NULL)
RETURN_ERROR(B_ERROR); RETURN_ERROR(B_ERROR);
run = indirect[current % runsPerBlock]; run = indirect[current % runsPerBlock];
offset = data->MaxIndirectRange() + (index * indirectSize) + (current * directSize); 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); //printf("\tfCurrent = %ld, fRunFileOffset = %Ld, fRunBlockEnd = %Ld, fRun = %ld,%d\n",fCurrent,fRunFileOffset,fRunBlockEnd,fRun.allocation_group,fRun.start);
} else { } else {
// access to indirect blocks // access to indirect blocks
@@ -1164,10 +1177,12 @@ Inode::FindBlockRun(off_t pos, block_run &run, off_t &offset)
if (indirect[current].IsZero()) if (indirect[current].IsZero())
break; break;
runBlockEnd += indirect[current].Length() << cached.BlockShift(); runBlockEnd += indirect[current].Length()
<< cached.BlockShift();
if (runBlockEnd > pos) { if (runBlockEnd > pos) {
run = indirect[current]; run = indirect[current];
offset = runBlockEnd - (run.Length() << cached.BlockShift()); offset = runBlockEnd - (run.Length()
<< cached.BlockShift());
//printf("reading from indirect block: %ld,%d\n",fRun.allocation_group,fRun.start); //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); //printf("### indirect-run[%ld] = (%ld,%d,%d), offset = %Ld\n",fCurrent,fRun.allocation_group,fRun.start,fRun.Length(),fRunFileOffset);
return fVolume->ValidateBlockRun(run); return fVolume->ValidateBlockRun(run);
@@ -1186,7 +1201,8 @@ Inode::FindBlockRun(off_t pos, block_run &run, off_t &offset)
if (data->direct[current].IsZero()) if (data->direct[current].IsZero())
break; break;
runBlockEnd += data->direct[current].Length() << fVolume->BlockShift(); runBlockEnd += data->direct[current].Length()
<< fVolume->BlockShift();
if (runBlockEnd > pos) { if (runBlockEnd > pos) {
run = data->direct[current]; run = data->direct[current];
offset = runBlockEnd - (run.Length() << fVolume->BlockShift()); offset = runBlockEnd - (run.Length() << fVolume->BlockShift());
@@ -1229,14 +1245,16 @@ Inode::ReadAt(off_t pos, uint8 *buffer, size_t *_length)
status_t 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)
{ {
// update the last modification time in memory, it will be written // update the last modification time in memory, it will be written
// back to the inode, and the index when the file is closed // back to the inode, and the index when the file is closed
// ToDo: should update the internal last modified time only at this point! // ToDo: should update the internal last modified time only at this point!
Node().last_modified_time = HOST_ENDIAN_TO_BFS_INT64((bigtime_t)time(NULL) << INODE_TIME_SHIFT); Node().last_modified_time = HOST_ENDIAN_TO_BFS_INT64((bigtime_t)time(NULL)
<< INODE_TIME_SHIFT);
// ToDo: support INODE_LOGGED! // TODO: support INODE_LOGGED!
#if 0 #if 0
if (Flags() & INODE_LOGGED) if (Flags() & INODE_LOGGED)
return ((Stream<Access::Logged> *)this)->WriteAt(transaction, pos, buffer, _length); return ((Stream<Access::Logged> *)this)->WriteAt(transaction, pos, buffer, _length);
@@ -1286,13 +1304,13 @@ Inode::WriteAt(Transaction &transaction, off_t pos, const uint8 *buffer, size_t
} }
/** Fills the gap between the old file size and the new file size /*!
* with zeros. Fills the gap between the old file size and the new file size
* It's more or less a copy of Inode::WriteAt() but it can handle with zeros.
* length differences of more than just 4 GB, and it never uses It's more or less a copy of Inode::WriteAt() but it can handle
* the log, even if the INODE_LOGGED flag is set. length differences of more than just 4 GB, and it never uses
*/ the log, even if the INODE_LOGGED flag is set.
*/
status_t status_t
Inode::FillGapWithZeros(off_t pos, off_t newSize) Inode::FillGapWithZeros(off_t pos, off_t newSize)
{ {
@@ -1374,19 +1392,19 @@ Inode::FillGapWithZeros(off_t pos, off_t newSize)
} }
/** Allocates NUM_ARRAY_BLOCKS blocks, and clears their contents. Growing /*!
* the indirect and double indirect range uses this method. Allocates NUM_ARRAY_BLOCKS blocks, and clears their contents. Growing
* The allocated block_run is saved in "run" the indirect and double indirect range uses this method.
*/ The allocated block_run is saved in "run"
*/
status_t status_t
Inode::_AllocateBlockArray(Transaction &transaction, block_run &run) Inode::_AllocateBlockArray(Transaction &transaction, block_run &run)
{ {
if (!run.IsZero()) if (!run.IsZero())
return B_BAD_VALUE; return B_BAD_VALUE;
status_t status = fVolume->Allocate(transaction, this, NUM_ARRAY_BLOCKS, run, status_t status = fVolume->Allocate(transaction, this, NUM_ARRAY_BLOCKS,
NUM_ARRAY_BLOCKS); run, NUM_ARRAY_BLOCKS);
if (status < B_OK) if (status < B_OK)
return status; return status;
@@ -1395,7 +1413,8 @@ Inode::_AllocateBlockArray(Transaction &transaction, block_run &run)
off_t block = fVolume->ToBlock(run); off_t block = fVolume->ToBlock(run);
for (int32 i = 0; i < run.Length(); i++) { for (int32 i = 0; i < run.Length(); i++) {
block_run *runs = (block_run *)cached.SetToWritable(transaction, block + i, true); block_run *runs = (block_run *)cached.SetToWritable(transaction,
block + i, true);
if (runs == NULL) if (runs == NULL)
return B_IO_ERROR; return B_IO_ERROR;
} }
@@ -1432,7 +1451,8 @@ Inode::_GrowStream(Transaction &transaction, off_t size)
bytes = size - data->Size(); bytes = size - data->Size();
// do we have enough free blocks on the disk? // do we have enough free blocks on the disk?
off_t blocksRequested = (bytes + fVolume->BlockSize() - 1) >> fVolume->BlockShift(); off_t blocksRequested = (bytes + fVolume->BlockSize() - 1)
>> fVolume->BlockShift();
if (blocksRequested > fVolume->FreeBlocks()) if (blocksRequested > fVolume->FreeBlocks())
return B_DEVICE_FULL; return B_DEVICE_FULL;
@@ -1442,8 +1462,9 @@ Inode::_GrowStream(Transaction &transaction, off_t size)
// from the block allocator // from the block allocator
// Should we preallocate some blocks (currently, always 64k)? // Should we preallocate some blocks (currently, always 64k)?
// Attributes, attribute directories, and long symlinks usually won't get that big, // Attributes, attribute directories, and long symlinks usually won't get
// and should stay close to the inode - preallocating could be counterproductive. // that big, and should stay close to the inode - preallocating could be
// counterproductive.
// Also, if free disk space is tight, we probably don't want to do this as well. // Also, if free disk space is tight, we probably don't want to do this as well.
if (!IsAttribute() && !IsAttributeDirectory() && !IsSymLink() if (!IsAttribute() && !IsAttributeDirectory() && !IsSymLink()
&& blocksRequested < (65536 >> fVolume->BlockShift()) && blocksRequested < (65536 >> fVolume->BlockShift())
@@ -1455,14 +1476,15 @@ Inode::_GrowStream(Transaction &transaction, off_t size)
// single allocation, so we need to iterate until we have // single allocation, so we need to iterate until we have
// enough blocks allocated // enough blocks allocated
block_run run; block_run run;
status_t status = fVolume->Allocate(transaction, this, blocksRequested, run, minimum); status_t status = fVolume->Allocate(transaction, this, blocksRequested,
run, minimum);
if (status < B_OK) if (status < B_OK)
return status; return status;
// okay, we have the needed blocks, so just distribute them to the // okay, we have the needed blocks, so just distribute them to the
// different ranges of the stream (direct, indirect & double indirect) // different ranges of the stream (direct, indirect & double indirect)
// ToDo: if anything goes wrong here, we probably want to free the // TODO: if anything goes wrong here, we probably want to free the
// blocks that couldn't be distributed into the stream! // blocks that couldn't be distributed into the stream!
blocksNeeded -= run.Length(); blocksNeeded -= run.Length();
@@ -1485,20 +1507,24 @@ Inode::_GrowStream(Transaction &transaction, off_t size)
if (free < NUM_DIRECT_BLOCKS) { if (free < NUM_DIRECT_BLOCKS) {
// can we merge the last allocated run with the new one? // can we merge the last allocated run with the new one?
int32 last = free - 1; int32 last = free - 1;
if (free > 0 && data->direct[last].MergeableWith(run)) if (free > 0 && data->direct[last].MergeableWith(run)) {
data->direct[last].length = HOST_ENDIAN_TO_BFS_INT16(data->direct[last].Length() + run.Length()); data->direct[last].length = HOST_ENDIAN_TO_BFS_INT16(
else data->direct[last].Length() + run.Length());
} else
data->direct[free] = run; data->direct[free] = run;
data->max_direct_range = HOST_ENDIAN_TO_BFS_INT64(data->MaxDirectRange() + run.Length() * fVolume->BlockSize()); data->max_direct_range = HOST_ENDIAN_TO_BFS_INT64(
data->size = HOST_ENDIAN_TO_BFS_INT64(blocksNeeded > 0 ? data->max_direct_range : size); data->MaxDirectRange() + run.Length() * fVolume->BlockSize());
data->size = HOST_ENDIAN_TO_BFS_INT64(blocksNeeded > 0
? data->max_direct_range : size);
continue; continue;
} }
} }
// Indirect block range // Indirect block range
if (data->Size() <= data->MaxIndirectRange() || !data->MaxIndirectRange()) { if (data->Size() <= data->MaxIndirectRange()
|| !data->MaxIndirectRange()) {
CachedBlock cached(fVolume); CachedBlock cached(fVolume);
block_run *runs = NULL; block_run *runs = NULL;
uint32 free = 0; uint32 free = 0;
@@ -1510,7 +1536,8 @@ Inode::_GrowStream(Transaction &transaction, off_t size)
if (status < B_OK) if (status < B_OK)
return status; return status;
data->max_indirect_range = HOST_ENDIAN_TO_BFS_INT64(data->MaxDirectRange()); data->max_indirect_range = HOST_ENDIAN_TO_BFS_INT64(
data->MaxDirectRange());
// insert the block_run in the first block // insert the block_run in the first block
runs = (block_run *)cached.SetTo(data->indirect); runs = (block_run *)cached.SetTo(data->indirect);
} else { } else {
@@ -1535,44 +1562,54 @@ Inode::_GrowStream(Transaction &transaction, off_t size)
} }
if (runs != NULL) { if (runs != NULL) {
// try to insert the run to the last one - note that this doesn't // try to insert the run to the last one - note that this
// take block borders into account, so it could be further optimized // doesn't take block borders into account, so it could be
// further optimized
cached.MakeWritable(transaction); cached.MakeWritable(transaction);
int32 last = free - 1; int32 last = free - 1;
if (free > 0 && runs[last].MergeableWith(run)) if (free > 0 && runs[last].MergeableWith(run)) {
runs[last].length = HOST_ENDIAN_TO_BFS_INT16(runs[last].Length() + run.Length()); runs[last].length = HOST_ENDIAN_TO_BFS_INT16(
else runs[last].Length() + run.Length());
} else
runs[free] = run; runs[free] = run;
data->max_indirect_range = HOST_ENDIAN_TO_BFS_INT64(data->MaxIndirectRange() + (run.Length() << fVolume->BlockShift())); data->max_indirect_range = HOST_ENDIAN_TO_BFS_INT64(
data->size = HOST_ENDIAN_TO_BFS_INT64(blocksNeeded > 0 ? data->MaxIndirectRange() : size); data->MaxIndirectRange()
+ (run.Length() << fVolume->BlockShift()));
data->size = HOST_ENDIAN_TO_BFS_INT64(blocksNeeded > 0
? data->MaxIndirectRange() : size);
continue; continue;
} }
} }
// Double indirect block range // Double indirect block range
if (data->Size() <= data->MaxDoubleIndirectRange() || !data->max_double_indirect_range) { if (data->Size() <= data->MaxDoubleIndirectRange()
|| !data->max_double_indirect_range) {
while ((run.Length() % NUM_ARRAY_BLOCKS) != 0) { while ((run.Length() % NUM_ARRAY_BLOCKS) != 0) {
// The number of allocated blocks isn't a multiple of NUM_ARRAY_BLOCKS, // The number of allocated blocks isn't a multiple of
// so we have to change this. This can happen the first time the stream // NUM_ARRAY_BLOCKS, so we have to change this. This can happen
// grows into the double indirect range. // the first time the stream grows into the double
// indirect range.
// First, free the remaining blocks that don't fit into a multiple // First, free the remaining blocks that don't fit into a multiple
// of NUM_ARRAY_BLOCKS // of NUM_ARRAY_BLOCKS
int32 rest = run.Length() % NUM_ARRAY_BLOCKS; int32 rest = run.Length() % NUM_ARRAY_BLOCKS;
run.length = HOST_ENDIAN_TO_BFS_INT16(run.Length() - rest); run.length = HOST_ENDIAN_TO_BFS_INT16(run.Length() - rest);
status = fVolume->Free(transaction, block_run::Run(run.AllocationGroup(), status = fVolume->Free(transaction,
block_run::Run(run.AllocationGroup(),
run.Start() + run.Length(), rest)); run.Start() + run.Length(), rest));
if (status < B_OK) if (status < B_OK)
return status; return status;
blocksNeeded += rest; blocksNeeded += rest;
blocksRequested = (blocksNeeded + NUM_ARRAY_BLOCKS - 1) & ~(NUM_ARRAY_BLOCKS - 1); blocksRequested = (blocksNeeded + NUM_ARRAY_BLOCKS - 1)
& ~(NUM_ARRAY_BLOCKS - 1);
minimum = NUM_ARRAY_BLOCKS; minimum = NUM_ARRAY_BLOCKS;
// we make sure here that we have at minimum NUM_ARRAY_BLOCKS allocated, // we make sure here that we have at minimum
// so if the allocation succeeds, we don't run into an endless loop // NUM_ARRAY_BLOCKS allocated, so if the allocation
// succeeds, we don't run into an endless loop
// Are there any blocks left in the run? If not, allocate a new one // Are there any blocks left in the run? If not, allocate a new one
if (run.length == 0) if (run.length == 0)
@@ -1608,7 +1645,7 @@ Inode::_GrowStream(Transaction &transaction, off_t size)
block_run *array = NULL; block_run *array = NULL;
uint32 runLength = run.Length(); uint32 runLength = run.Length();
// ToDo: the following code is commented - it could be used to // TODO: the following code is commented - it could be used to
// preallocate all needed block arrays to see in advance if the // preallocate all needed block arrays to see in advance if the
// allocation will succeed. // allocation will succeed.
// I will probably remove it later, because it's no perfect solution // I will probably remove it later, because it's no perfect solution
@@ -1641,8 +1678,8 @@ Inode::_GrowStream(Transaction &transaction, off_t size)
while (run.length != 0) { while (run.length != 0) {
// get the indirect array block // get the indirect array block
if (array == NULL) { if (array == NULL) {
array = (block_run *)cached.SetTo(fVolume->ToBlock(data->double_indirect) array = (block_run *)cached.SetTo(fVolume->ToBlock(
+ indirectIndex / runsPerBlock); data->double_indirect) + indirectIndex / runsPerBlock);
if (array == NULL) if (array == NULL)
return B_IO_ERROR; return B_IO_ERROR;
} }
@@ -1656,20 +1693,23 @@ Inode::_GrowStream(Transaction &transaction, off_t size)
return status; return status;
} }
block_run *runs = (block_run *)cachedDirect.SetToWritable(transaction, block_run *runs = (block_run *)cachedDirect.SetToWritable(
fVolume->ToBlock(array[indirectIndex % runsPerBlock]) transaction, fVolume->ToBlock(array[indirectIndex
+ index / runsPerBlock); % runsPerBlock]) + index / runsPerBlock);
if (runs == NULL) if (runs == NULL)
return B_IO_ERROR; return B_IO_ERROR;
do { do {
// insert the block_run into the array // insert the block_run into the array
runs[index % runsPerBlock] = run; runs[index % runsPerBlock] = run;
runs[index % runsPerBlock].length = HOST_ENDIAN_TO_BFS_INT16(NUM_ARRAY_BLOCKS); runs[index % runsPerBlock].length
= HOST_ENDIAN_TO_BFS_INT16(NUM_ARRAY_BLOCKS);
// alter the remaining block_run // alter the remaining block_run
run.start = HOST_ENDIAN_TO_BFS_INT16(run.Start() + NUM_ARRAY_BLOCKS); run.start = HOST_ENDIAN_TO_BFS_INT16(run.Start()
run.length = HOST_ENDIAN_TO_BFS_INT16(run.Length() - NUM_ARRAY_BLOCKS); + NUM_ARRAY_BLOCKS);
run.length = HOST_ENDIAN_TO_BFS_INT16(run.Length()
- NUM_ARRAY_BLOCKS);
} while ((++index % runsPerBlock) != 0 && run.length); } while ((++index % runsPerBlock) != 0 && run.length);
} while ((index % runsPerArray) != 0 && run.length); } while ((index % runsPerArray) != 0 && run.length);
@@ -1679,8 +1719,11 @@ Inode::_GrowStream(Transaction &transaction, off_t size)
} }
} }
data->max_double_indirect_range = HOST_ENDIAN_TO_BFS_INT64(data->MaxDoubleIndirectRange() + (runLength << fVolume->BlockShift())); data->max_double_indirect_range = HOST_ENDIAN_TO_BFS_INT64(
data->size = blocksNeeded > 0 ? HOST_ENDIAN_TO_BFS_INT64(data->max_double_indirect_range) : size; data->MaxDoubleIndirectRange()
+ (runLength << fVolume->BlockShift()));
data->size = blocksNeeded > 0 ? HOST_ENDIAN_TO_BFS_INT64(
data->max_double_indirect_range) : size;
continue; continue;
} }
@@ -1695,8 +1738,8 @@ Inode::_GrowStream(Transaction &transaction, off_t size)
status_t status_t
Inode::_FreeStaticStreamArray(Transaction &transaction, int32 level, block_run run, Inode::_FreeStaticStreamArray(Transaction &transaction, int32 level,
off_t size, off_t offset, off_t &max) block_run run, off_t size, off_t offset, off_t &max)
{ {
int32 indirectSize = 0; int32 indirectSize = 0;
if (level == 0) if (level == 0)
@@ -1721,7 +1764,8 @@ Inode::_FreeStaticStreamArray(Transaction &transaction, int32 level, block_run r
offset += (off_t)index * indirectSize; offset += (off_t)index * indirectSize;
for (int32 i = index / runsPerBlock; i < run.Length(); i++) { for (int32 i = index / runsPerBlock; i < run.Length(); i++) {
block_run *array = (block_run *)cached.SetToWritable(transaction, blockNumber + i); block_run *array = (block_run *)cached.SetToWritable(transaction,
blockNumber + i);
if (array == NULL) if (array == NULL)
RETURN_ERROR(B_ERROR); RETURN_ERROR(B_ERROR);
@@ -1734,8 +1778,8 @@ Inode::_FreeStaticStreamArray(Transaction &transaction, int32 level, block_run r
status_t status = B_OK; status_t status = B_OK;
if (level == 0) { if (level == 0) {
status = _FreeStaticStreamArray(transaction, 1, array[index], size, status = _FreeStaticStreamArray(transaction, 1, array[index],
offset, max); size, offset, max);
} else if (offset >= size) } else if (offset >= size)
status = fVolume->Free(transaction, array[index]); status = fVolume->Free(transaction, array[index]);
else else
@@ -1755,17 +1799,17 @@ Inode::_FreeStaticStreamArray(Transaction &transaction, int32 level, block_run r
} }
/** Frees all block_runs in the array which come after the specified size. /*!
* It also trims the last block_run that contain the size. Frees all block_runs in the array which come after the specified size.
* "offset" and "max" are maintained until the last block_run that doesn't It also trims the last block_run that contain the size.
* have to be freed - after this, the values won't be correct anymore, but "offset" and "max" are maintained until the last block_run that doesn't
* will still assure correct function for all subsequent calls. have to be freed - after this, the values won't be correct anymore, but
* "max" is considered to be in file system byte order. will still assure correct function for all subsequent calls.
*/ "max" is considered to be in file system byte order.
*/
status_t status_t
Inode::_FreeStreamArray(Transaction &transaction, block_run *array, uint32 arrayLength, Inode::_FreeStreamArray(Transaction &transaction, block_run *array,
off_t size, off_t &offset, off_t &max) uint32 arrayLength, off_t size, off_t &offset, off_t &max)
{ {
PRINT(("FreeStreamArray: arrayLength %lu, size %Ld, offset %Ld, max %Ld\n", PRINT(("FreeStreamArray: arrayLength %lu, size %Ld, offset %Ld, max %Ld\n",
arrayLength, size, offset, max)); arrayLength, size, offset, max));
@@ -1785,15 +1829,19 @@ Inode::_FreeStreamArray(Transaction &transaction, block_run *array, uint32 array
// determine the block_run to be freed // determine the block_run to be freed
if (newOffset > size && offset < size) { if (newOffset > size && offset < size) {
// free partial block_run (and update the original block_run) // free partial block_run (and update the original block_run)
run.start = array[i].start + ((size - offset) >> fVolume->BlockShift()) + 1; run.start = array[i].start
array[i].length = HOST_ENDIAN_TO_BFS_INT16(run.Start() - array[i].Start()); + ((size - offset) >> fVolume->BlockShift()) + 1;
run.length = HOST_ENDIAN_TO_BFS_INT16(run.Length() - array[i].Length()); array[i].length = HOST_ENDIAN_TO_BFS_INT16(run.Start()
- array[i].Start());
run.length = HOST_ENDIAN_TO_BFS_INT16(run.Length()
- array[i].Length());
if (run.length == 0) if (run.length == 0)
continue; continue;
// update maximum range // update maximum range
max = HOST_ENDIAN_TO_BFS_INT64(offset + ((off_t)array[i].Length() << fVolume->BlockShift())); max = HOST_ENDIAN_TO_BFS_INT64(offset + ((off_t)array[i].Length()
<< fVolume->BlockShift()));
} else { } else {
// free the whole block_run // free the whole block_run
array[i].SetTo(0, 0, 0); array[i].SetTo(0, 0, 0);
@@ -1819,8 +1867,8 @@ Inode::_ShrinkStream(Transaction &transaction, off_t size)
off_t *maxDoubleIndirect = &data->max_double_indirect_range; off_t *maxDoubleIndirect = &data->max_double_indirect_range;
// gcc 4 work-around: "error: cannot bind packed field // gcc 4 work-around: "error: cannot bind packed field
// 'data->data_stream::max_double_indirect_range' to 'off_t&'" // 'data->data_stream::max_double_indirect_range' to 'off_t&'"
status = _FreeStaticStreamArray(transaction, 0, data->double_indirect, size, status = _FreeStaticStreamArray(transaction, 0, data->double_indirect,
data->MaxIndirectRange(), *maxDoubleIndirect); size, data->MaxIndirectRange(), *maxDoubleIndirect);
if (status < B_OK) if (status < B_OK)
return status; return status;
@@ -1837,15 +1885,16 @@ Inode::_ShrinkStream(Transaction &transaction, off_t size)
off_t offset = data->MaxDirectRange(); off_t offset = data->MaxDirectRange();
for (int32 i = 0; i < data->indirect.Length(); i++) { for (int32 i = 0; i < data->indirect.Length(); i++) {
block_run *array = (block_run *)cached.SetToWritable(transaction, block + i); block_run *array = (block_run *)cached.SetToWritable(transaction,
block + i);
if (array == NULL) if (array == NULL)
break; break;
off_t *maxIndirect = &data->max_indirect_range; off_t *maxIndirect = &data->max_indirect_range;
// gcc 4 work-around: "error: cannot bind packed field // gcc 4 work-around: "error: cannot bind packed field
// 'data->data_stream::max_indirect_range' to 'off_t&'" // 'data->data_stream::max_indirect_range' to 'off_t&'"
if (_FreeStreamArray(transaction, array, fVolume->BlockSize() / sizeof(block_run), if (_FreeStreamArray(transaction, array, fVolume->BlockSize()
size, offset, *maxIndirect) != B_OK) / sizeof(block_run), size, offset, *maxIndirect) != B_OK)
return B_IO_ERROR; return B_IO_ERROR;
} }
if (data->max_direct_range == data->max_indirect_range) { if (data->max_direct_range == data->max_indirect_range) {
@@ -1909,11 +1958,11 @@ Inode::Append(Transaction &transaction, off_t bytes)
} }
/** Checks wether or not this inode's data stream needs to be trimmed /*!
* because of an earlier preallocation. Checks wether or not this inode's data stream needs to be trimmed
* Returns true if there are any blocks to be trimmed. because of an earlier preallocation.
*/ Returns true if there are any blocks to be trimmed.
*/
bool bool
Inode::NeedsTrimming() Inode::NeedsTrimming()
{ {
@@ -1923,7 +1972,8 @@ Inode::NeedsTrimming()
if (IsIndex() || IsDeleted()) if (IsIndex() || IsDeleted())
return false; return false;
off_t roundedSize = (Size() + fVolume->BlockSize() - 1) & ~(fVolume->BlockSize() - 1); off_t roundedSize = (Size() + fVolume->BlockSize() - 1)
& ~(fVolume->BlockSize() - 1);
return Node().data.MaxDirectRange() > roundedSize return Node().data.MaxDirectRange() > roundedSize
|| Node().data.MaxIndirectRange() > roundedSize || Node().data.MaxIndirectRange() > roundedSize
@@ -1942,6 +1992,7 @@ Inode::TrimPreallocation(Transaction &transaction)
} }
//! Frees the file's data stream and removes all attributes
status_t status_t
Inode::Free(Transaction &transaction) Inode::Free(Transaction &transaction)
{ {
@@ -1964,8 +2015,9 @@ Inode::Free(Transaction &transaction)
uint32 type; uint32 type;
size_t length; size_t length;
vnode_id id; vnode_id id;
while ((status = iterator.GetNext(name, &length, &type, &id)) == B_OK) while ((status = iterator.GetNext(name, &length, &type, &id)) == B_OK) {
RemoveAttribute(transaction, name); RemoveAttribute(transaction, name);
}
} }
if (WriteBack(transaction) < B_OK) if (WriteBack(transaction) < B_OK)
@@ -2065,8 +2117,12 @@ Inode::Sync()
} }
// #pragma mark - creation/deletion
status_t 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; BPlusTree *tree;
if (GetTree(&tree) != B_OK) if (GetTree(&tree) != B_OK)
@@ -2145,21 +2201,21 @@ Inode::Remove(Transaction &transaction, const char *name, off_t *_id, bool isDir
} }
/** Creates the inode with the specified parent directory, and automatically /*!
* adds the created inode to that parent directory. If an attribute directory Creates the inode with the specified parent directory, and automatically
* is created, it will also automatically added to the parent inode as such. adds the created inode to that parent directory. If an attribute directory
* However, the indices root node, and the regular root node won't be added is created, it will also automatically added to the parent inode as such.
* to the super block. However, the indices root node, and the regular root node won't be added
* It will also create the initial B+tree for the inode if it's a directory to the super block.
* of any kind. It will also create the initial B+tree for the inode if it's a directory
* If the "_id" or "_inode" variable is given and non-NULL to store the inode's of any kind.
* ID, the inode stays locked - you have to call put_vnode() if you don't use it If the "_id" or "_inode" variable is given and non-NULL to store the inode's
* anymore. ID, the inode stays locked - you have to call put_vnode() if you don't use it
*/ anymore.
*/
status_t status_t
Inode::Create(Transaction &transaction, Inode *parent, const char *name, int32 mode, Inode::Create(Transaction &transaction, Inode *parent, const char *name,
int openMode, uint32 type, off_t *_id, Inode **_inode) int32 mode, int openMode, uint32 type, off_t *_id, Inode **_inode)
{ {
FUNCTION_START(("name = %s, mode = %ld\n", name, mode)); FUNCTION_START(("name = %s, mode = %ld\n", name, mode));
@@ -2258,7 +2314,8 @@ Inode::Create(Transaction &transaction, Inode *parent, const char *name, int32 m
node->parent = parentRun; node->parent = parentRun;
node->uid = HOST_ENDIAN_TO_BFS_INT32(geteuid()); node->uid = HOST_ENDIAN_TO_BFS_INT32(geteuid());
node->gid = HOST_ENDIAN_TO_BFS_INT32(parent ? parent->Node().GroupID() : getegid()); node->gid = HOST_ENDIAN_TO_BFS_INT32(parent
? parent->Node().GroupID() : getegid());
// the group ID is inherited from the parent, if available // the group ID is inherited from the parent, if available
node->type = HOST_ENDIAN_TO_BFS_INT32(type); node->type = HOST_ENDIAN_TO_BFS_INT32(type);
@@ -2332,8 +2389,10 @@ Inode::Create(Transaction &transaction, Inode *parent, const char *name, int32 m
// initialized inode, and we want to keep it // initialized inode, and we want to keep it
allocator.Keep(); allocator.Keep();
if (inode->IsFile() || inode->IsAttribute()) if (inode->IsFile() || inode->IsAttribute()) {
inode->SetFileCache(file_cache_create(volume->ID(), inode->ID(), inode->Size(), volume->Device())); inode->SetFileCache(file_cache_create(volume->ID(), inode->ID(),
inode->Size(), volume->Device()));
}
if (_id != NULL) if (_id != NULL)
*_id = inode->ID(); *_id = inode->ID();
@@ -2386,7 +2445,8 @@ AttributeIterator::Rewind()
status_t status_t
AttributeIterator::GetNext(char *name, size_t *_length, uint32 *_type, vnode_id *_id) AttributeIterator::GetNext(char *name, size_t *_length, uint32 *_type,
vnode_id *_id)
{ {
// read attributes out of the small data section // read attributes out of the small data section
@@ -2440,21 +2500,24 @@ AttributeIterator::GetNext(char *name, size_t *_length, uint32 *_type, vnode_id
if (fAttributes == NULL) { if (fAttributes == NULL) {
if (get_vnode(volume->ID(), volume->ToVnode(fInode->Attributes()), if (get_vnode(volume->ID(), volume->ToVnode(fInode->Attributes()),
(void **)&fAttributes) != B_OK) { (void **)&fAttributes) != B_OK) {
FATAL(("get_vnode() failed in AttributeIterator::GetNext(vnode_id = %Ld,name = \"%s\")\n",fInode->ID(),name)); FATAL(("get_vnode() failed in AttributeIterator::GetNext(vnode_id"
" = %Ld,name = \"%s\")\n",fInode->ID(),name));
return B_ENTRY_NOT_FOUND; return B_ENTRY_NOT_FOUND;
} }
BPlusTree *tree; BPlusTree *tree;
if (fAttributes->GetTree(&tree) < B_OK if (fAttributes->GetTree(&tree) < B_OK
|| (fIterator = new TreeIterator(tree)) == NULL) { || (fIterator = new TreeIterator(tree)) == NULL) {
FATAL(("could not get tree in AttributeIterator::GetNext(vnode_id = %Ld,name = \"%s\")\n",fInode->ID(),name)); FATAL(("could not get tree in AttributeIterator::GetNext(vnode_id"
" = %Ld,name = \"%s\")\n",fInode->ID(),name));
return B_ENTRY_NOT_FOUND; return B_ENTRY_NOT_FOUND;
} }
} }
uint16 length; uint16 length;
vnode_id id; vnode_id id;
status_t status = fIterator->GetNextEntry(name, &length, B_FILE_NAME_LENGTH, &id); status_t status = fIterator->GetNextEntry(name, &length,
B_FILE_NAME_LENGTH, &id);
if (status < B_OK) if (status < B_OK)
return status; return status;