* 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:
@@ -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)
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user