* 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
+65 -48
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)
+263 -200
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,10 +874,12 @@ 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 > 0) {
if (oldLength > BPLUSTREE_MAX_KEY_LENGTH) if (oldLength > BPLUSTREE_MAX_KEY_LENGTH)
oldLength = BPLUSTREE_MAX_KEY_LENGTH; oldLength = BPLUSTREE_MAX_KEY_LENGTH;
memcpy(oldData = oldBuffer, smallData->Data(), oldLength); memcpy(oldData = oldBuffer, smallData->Data(), oldLength);
} }
}
fSmallDataLock.Unlock(); fSmallDataLock.Unlock();
// if the attribute doesn't exist yet (as a file), try to put it in the // if the attribute doesn't exist yet (as a file), try to put it in the
@@ -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,9 +2015,10 @@ 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)
return B_IO_ERROR; return B_IO_ERROR;
@@ -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;