git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@24576 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler 2008-03-25 19:06:25 +00:00
parent 2f20bf154e
commit 8af46ac846

View File

@ -369,11 +369,12 @@ Inode::Inode(Volume *volume, Transaction &transaction, ino_t id, mode_t mode,
Node().mode = HOST_ENDIAN_TO_BFS_INT32(mode);
Node().flags = HOST_ENDIAN_TO_BFS_INT32(INODE_IN_USE);
Node().create_time = HOST_ENDIAN_TO_BFS_INT64((bigtime_t)time(NULL) << INODE_TIME_SHIFT);
Node().create_time = HOST_ENDIAN_TO_BFS_INT64((bigtime_t)time(NULL)
<< INODE_TIME_SHIFT);
Node().last_modified_time = HOST_ENDIAN_TO_BFS_INT64(Node().create_time
| (volume->GetUniqueID() & INODE_TIME_MASK));
// we use Volume::GetUniqueID() to avoid having too many duplicates in the
// last_modified index
// we use Volume::GetUniqueID() to avoid having too many duplicates
// in the last_modified index
Node().inode_size = HOST_ENDIAN_TO_BFS_INT32(volume->InodeSize());
@ -498,8 +499,7 @@ Inode::_RemoveIterator(AttributeIterator *iterator)
}
/*!
Tries to free up "bytes" space in the small_data section by moving
/*! Tries to free up "bytes" space in the small_data section by moving
attributes to real files. Used for system attributes like the name.
You need to hold the fSmallDataLock when you call this method
*/
@ -522,7 +522,8 @@ Inode::_MakeSpaceForSmallData(Transaction &transaction, bfs_inode *node,
max = item;
}
// remove the first one large enough to free the needed amount of bytes
// Remove the first one large enough to free the needed amount of
// bytes
if (bytes < (int32)item->Size())
break;
}
@ -563,8 +564,7 @@ Inode::_MakeSpaceForSmallData(Transaction &transaction, bfs_inode *node,
}
/*!
Private function which removes the given attribute from the small_data
/*! Private function which removes the given attribute from the small_data
section.
You need to hold the fSmallDataLock when you call this method
*/
@ -581,7 +581,8 @@ Inode::_RemoveSmallData(bfs_inode *node, small_data *item, int32 index)
last = last->Next();
int32 size = (uint8 *)last - (uint8 *)next;
if (size < 0 || size > (uint8 *)node + fVolume->BlockSize() - (uint8 *)next)
if (size < 0
|| size > (uint8 *)node + fVolume->BlockSize() - (uint8 *)next)
return B_BAD_DATA;
memmove(item, next, size);
@ -635,17 +636,16 @@ Inode::_RemoveSmallData(Transaction &transaction, NodeGetter &nodeGetter,
}
/*!
Try to place the given attribute in the small_data section - if the
/*! 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.
In that case, the attribute should be written to a real attribute file;
it's the caller's responsibility to remove any existing attributes in the small
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...
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
Inode::_AddSmallData(Transaction &transaction, NodeGetter &nodeGetter,
@ -685,16 +685,20 @@ Inode::_AddSmallData(Transaction &transaction, NodeGetter &nodeGetter,
// try to change the attributes value
if (item->data_size > length
|| force
|| ((uint8 *)last + length - item->DataSize()) <= ((uint8 *)node + fVolume->InodeSize())) {
// make room for the new attribute if needed (and we are forced to do so)
if (force
&& ((uint8 *)last + length - item->DataSize()) > ((uint8 *)node + fVolume->InodeSize())) {
// We also take the free space at the end of the small_data section
// into account, and request only what's really needed
|| ((uint8 *)last + length - item->DataSize())
<= ((uint8 *)node + fVolume->InodeSize())) {
// Make room for the new attribute if needed (and we are forced
// to do so)
if (force && ((uint8 *)last + length - item->DataSize())
> ((uint8 *)node + fVolume->InodeSize())) {
// We also take the free space at the end of the small_data
// section into account, and request only what's really needed
uint32 needed = length - item->DataSize() -
(uint32)((uint8 *)node + fVolume->InodeSize() - (uint8 *)last);
(uint32)((uint8 *)node + fVolume->InodeSize()
- (uint8 *)last);
if (_MakeSpaceForSmallData(transaction, node, name, needed) < B_OK)
if (_MakeSpaceForSmallData(transaction, node, name, needed)
< B_OK)
return B_ERROR;
// reset our pointers
@ -715,14 +719,19 @@ Inode::_AddSmallData(Transaction &transaction, NodeGetter &nodeGetter,
if (length != item->DataSize()) {
// move the attributes after the current one
small_data *next = item->Next();
if (!next->IsLast(node))
memmove((uint8 *)item + spaceNeeded, next, (uint8 *)last - (uint8 *)next);
if (!next->IsLast(node)) {
memmove((uint8 *)item + spaceNeeded, next,
(uint8 *)last - (uint8 *)next);
}
// Move the "last" one to its new location and
// correctly terminate the small_data section
last = (small_data *)((uint8 *)last - ((uint8 *)next - ((uint8 *)item + spaceNeeded)));
if ((uint8 *)last < (uint8 *)node + fVolume->BlockSize())
memset(last, 0, (uint8 *)node + fVolume->BlockSize() - (uint8 *)last);
last = (small_data *)((uint8 *)last
- ((uint8 *)next - ((uint8 *)item + spaceNeeded)));
if ((uint8 *)last < (uint8 *)node + fVolume->BlockSize()) {
memset(last, 0, (uint8 *)node + fVolume->BlockSize()
- (uint8 *)last);
}
item->data_size = HOST_ENDIAN_TO_BFS_INT16(length);
}
@ -779,8 +788,7 @@ Inode::_AddSmallData(Transaction &transaction, NodeGetter &nodeGetter,
}
/*!
Iterates through the small_data section of an inode.
/*! Iterates through the small_data section of an inode.
To start at the beginning of this section, you let smallData
point to NULL, like:
small_data *data = NULL;
@ -817,8 +825,7 @@ Inode::_GetNextSmallData(bfs_inode *node, small_data **_smallData) const
}
/*!
Finds the attribute "name" in the small data section, and
/*! Finds the attribute "name" in the small data section, and
returns a pointer to it (or NULL if it doesn't exist).
You need to hold the fSmallDataLock when you call this method
*/
@ -828,7 +835,8 @@ Inode::FindSmallData(const bfs_inode *node, const char *name) const
ASSERT(fSmallDataLock.IsLocked());
small_data *smallData = NULL;
while (_GetNextSmallData(const_cast<bfs_inode *>(node), &smallData) == B_OK) {
while (_GetNextSmallData(const_cast<bfs_inode *>(node), &smallData)
== B_OK) {
if (!strcmp(smallData->Name(), name))
return smallData;
}
@ -836,8 +844,7 @@ Inode::FindSmallData(const bfs_inode *node, const char *name) const
}
/*!
Returns a pointer to the node's name if present in the small data
/*! Returns a pointer to the node's name if present in the small data
section, NULL otherwise.
You need to hold the fSmallDataLock when you call this method
*/
@ -856,8 +863,7 @@ Inode::Name(const bfs_inode *node) const
}
/*!
Copies the node's name into the provided buffer.
/*! Copies the node's name into the provided buffer.
The buffer should be B_FILE_NAME_LENGTH bytes large.
*/
status_t
@ -875,8 +881,7 @@ Inode::GetName(char *buffer, size_t size) const
}
/*!
Changes or set the name of a file: in the inode small_data section only, it
/*! 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.
Note that you need to write back the inode yourself after having called
that method. It suffers from the same API decision as AddSmallData() does
@ -943,8 +948,7 @@ Inode::_RemoveAttribute(Transaction &transaction, const char *name,
}
/*!
Reads data from the specified attribute.
/*! Reads data from the specified attribute.
This is a high-level attribute function that understands attributes
in the small_data section as well as real attribute files.
*/
@ -989,8 +993,7 @@ Inode::ReadAttribute(const char *name, int32 type, off_t pos, uint8 *buffer,
}
/*!
Writes data to the specified attribute.
/*! Writes data to the specified attribute.
This is a high-level attribute function that understands attributes
in the small_data section as well as real attribute files.
*/
@ -1005,8 +1008,9 @@ Inode::WriteAttribute(Transaction &transaction, const char *name, int32 type,
// 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
// up our index trees!
// TODO: for attribute files, we need to log the first BPLUSTREE_MAX_KEY_LENGTH
// bytes of the data stream, or the same as above might happen.
// TODO: for attribute files, we need to log the first
// BPLUSTREE_MAX_KEY_LENGTH bytes of the data stream, or the same as above
// might happen.
Index index(fVolume);
index.SetTo(name);
@ -1052,7 +1056,8 @@ Inode::WriteAttribute(Transaction &transaction, const char *name, int32 type,
if (attribute != NULL) {
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) && attribute->Size() > 0) {
oldLength = BPLUSTREE_MAX_KEY_LENGTH;
if (attribute->ReadAt(0, oldBuffer, &oldLength) == B_OK)
@ -1094,8 +1099,9 @@ Inode::WriteAttribute(Transaction &transaction, const char *name, int32 type,
if (length > BPLUSTREE_MAX_KEY_LENGTH)
length = BPLUSTREE_MAX_KEY_LENGTH;
// Update index. Note, Index::Update() may be called even if initializing
// the index failed - it will just update the live queries in this case
// Update index. Note, Index::Update() may be called even if
// initializing the index failed - it will just update the live
// queries in this case
if (pos < length || pos < oldLength) {
index.Update(transaction, name, type, oldData, oldLength, buffer,
length, this);
@ -1105,8 +1111,7 @@ Inode::WriteAttribute(Transaction &transaction, const char *name, int32 type,
}
/*!
Removes the specified attribute from the inode.
/*! Removes the specified attribute from the inode.
This is a high-level attribute function that understands attributes
in the small_data section as well as real attribute files.
*/
@ -1151,7 +1156,8 @@ Inode::GetAttribute(const char *name, Inode **_attribute)
Vnode vnode(fVolume, Attributes());
Inode *attributes;
if (vnode.Get(&attributes) < B_OK) {
FATAL(("get_vnode() failed in Inode::GetAttribute(name = \"%s\")\n", name));
FATAL(("get_vnode() failed in Inode::GetAttribute(name = \"%s\")\n",
name));
return B_ERROR;
}
@ -1211,8 +1217,7 @@ Inode::CreateAttribute(Transaction &transaction, const char *name, uint32 type,
// #pragma mark - directory tree
/*!
Gives the caller direct access to the b+tree for a given directory.
/*! 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
created. That will report any potential errors upfront, saves locking,
and should work as good (though a bit slower).
@ -1262,8 +1267,7 @@ Inode::IsEmpty()
// #pragma mark - data stream
/*!
Finds the block_run where "pos" is located in the data_stream of
/*! 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
@ -1278,7 +1282,8 @@ Inode::FindBlockRun(off_t pos, block_run &run, off_t &offset)
// find matching block run
if (data->MaxDirectRange() > 0 && pos >= data->MaxDirectRange()) {
if (data->MaxDoubleIndirectRange() > 0 && pos >= data->MaxIndirectRange()) {
if (data->MaxDoubleIndirectRange() > 0
&& pos >= data->MaxIndirectRange()) {
// access to double indirect blocks
CachedBlock cached(fVolume);
@ -1292,13 +1297,10 @@ Inode::FindBlockRun(off_t pos, block_run &run, off_t &offset)
int32 runsPerBlock = cached.BlockSize() / sizeof(block_run);
block_run *indirect = (block_run *)cached.SetTo(
fVolume->ToBlock(data->double_indirect) + index / runsPerBlock);
fVolume->ToBlock(data->double_indirect) + index / runsPerBlock);
if (indirect == NULL)
RETURN_ERROR(B_ERROR);
//printf("\tstart = %Ld, indirectSize = %ld, directSize = %ld, index = %ld\n",start,indirectSize,directSize,index);
//printf("\tlook for indirect block at %ld,%d\n",indirect[index].allocation_group,indirect[index].start);
int32 current = (start % indirectSize) / directSize;
indirect = (block_run *)cached.SetTo(
@ -1310,7 +1312,6 @@ Inode::FindBlockRun(off_t pos, block_run &run, off_t &offset)
run = indirect[current % runsPerBlock];
offset = data->MaxIndirectRange() + (index * indirectSize)
+ (current * directSize);
//printf("\tfCurrent = %ld, fRunFileOffset = %Ld, fRunBlockEnd = %Ld, fRun = %ld,%d\n",fCurrent,fRunFileOffset,fRunBlockEnd,fRun.allocation_group,fRun.start);
} else {
// access to indirect blocks
@ -1336,8 +1337,6 @@ Inode::FindBlockRun(off_t pos, block_run &run, off_t &offset)
run = indirect[current];
offset = runBlockEnd - (run.Length()
<< cached.BlockShift());
//printf("reading from indirect block: %ld,%d\n",fRun.allocation_group,fRun.start);
//printf("### indirect-run[%ld] = (%ld,%d,%d), offset = %Ld\n",fCurrent,fRun.allocation_group,fRun.start,fRun.Length(),fRunFileOffset);
return fVolume->ValidateBlockRun(run);
}
}
@ -1359,12 +1358,10 @@ Inode::FindBlockRun(off_t pos, block_run &run, off_t &offset)
if (runBlockEnd > pos) {
run = data->direct[current];
offset = runBlockEnd - (run.Length() << fVolume->BlockShift());
//printf("### run[%ld] = (%ld,%d,%d), offset = %Ld\n",fCurrent,fRun.allocation_group,fRun.start,fRun.Length(),fRunFileOffset);
return fVolume->ValidateBlockRun(run);
}
}
//PRINT(("FindBlockRun() failed in direct range: size = %Ld, pos = %Ld\n",data->size,pos));
return B_ENTRY_NOT_FOUND;
}
return fVolume->ValidateBlockRun(run);
@ -1457,8 +1454,7 @@ Inode::WriteAt(Transaction &transaction, off_t pos, const uint8 *buffer,
}
/*!
Fills the gap between the old file size and the new file size
/*! Fills the gap between the old file size and the new file size
with zeros.
It's more or less a copy of Inode::WriteAt() but it can handle
length differences of more than just 4 GB, and it never uses
@ -1485,8 +1481,7 @@ Inode::FillGapWithZeros(off_t pos, off_t newSize)
}
/*!
Allocates NUM_ARRAY_BLOCKS blocks, and clears their contents. Growing
/*! Allocates NUM_ARRAY_BLOCKS blocks, and clears their contents. Growing
the indirect and double indirect range uses this method.
The allocated block_run is saved in "run"
*/
@ -1534,7 +1529,8 @@ Inode::_GrowStream(Transaction &transaction, off_t size)
off_t bytes;
if (data->Size() < data->MaxDoubleIndirectRange()) {
bytes = size - data->MaxDoubleIndirectRange();
// the double indirect range can only handle multiple of NUM_ARRAY_BLOCKS
// The double indirect range can only handle multiple of
// NUM_ARRAY_BLOCKS
minimum = NUM_ARRAY_BLOCKS;
} else if (data->Size() < data->MaxIndirectRange())
bytes = size - data->MaxIndirectRange();
@ -1558,7 +1554,8 @@ Inode::_GrowStream(Transaction &transaction, off_t size)
// Attributes, attribute directories, and long symlinks usually won't get
// 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()
&& blocksRequested < (65536 >> fVolume->BlockShift())
&& fVolume->FreeBlocks() > 128)
@ -1607,7 +1604,8 @@ Inode::_GrowStream(Transaction &transaction, off_t size)
data->direct[free] = run;
data->max_direct_range = HOST_ENDIAN_TO_BFS_INT64(
data->MaxDirectRange() + run.Length() * fVolume->BlockSize());
data->MaxDirectRange()
+ run.Length() * fVolume->BlockSize());
data->size = HOST_ENDIAN_TO_BFS_INT64(blocksNeeded > 0
? data->max_direct_range : size);
continue;
@ -1685,8 +1683,8 @@ Inode::_GrowStream(Transaction &transaction, off_t size)
// NUM_ARRAY_BLOCKS, so we have to change this. This can happen
// the first time the stream grows into the double
// indirect range.
// First, free the remaining blocks that don't fit into a multiple
// of NUM_ARRAY_BLOCKS
// First, free the remaining blocks that don't fit into a
// multiple of NUM_ARRAY_BLOCKS
int32 rest = run.Length() % NUM_ARRAY_BLOCKS;
run.length = HOST_ENDIAN_TO_BFS_INT16(run.Length() - rest);
@ -1704,14 +1702,16 @@ Inode::_GrowStream(Transaction &transaction, off_t size)
// 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)
continue;
}
// if there is no double indirect block yet, create one
if (data->double_indirect.IsZero()) {
status = _AllocateBlockArray(transaction, data->double_indirect);
status = _AllocateBlockArray(transaction,
data->double_indirect);
if (status < B_OK)
return status;
@ -1721,12 +1721,13 @@ Inode::_GrowStream(Transaction &transaction, off_t size)
// calculate the index where to insert the new blocks
int32 runsPerBlock = fVolume->BlockSize() / sizeof(block_run);
int32 indirectSize = ((1L << INDIRECT_BLOCKS_SHIFT) << fVolume->BlockShift())
* runsPerBlock;
int32 indirectSize = ((1L << INDIRECT_BLOCKS_SHIFT)
<< fVolume->BlockShift()) * runsPerBlock;
int32 directSize = NUM_ARRAY_BLOCKS << fVolume->BlockShift();
int32 runsPerArray = runsPerBlock << ARRAY_BLOCKS_SHIFT;
off_t start = data->MaxDoubleIndirectRange() - data->MaxIndirectRange();
off_t start = data->MaxDoubleIndirectRange()
- data->MaxIndirectRange();
int32 indirectIndex = start / indirectSize;
int32 index = start / directSize;
@ -1738,36 +1739,6 @@ Inode::_GrowStream(Transaction &transaction, off_t size)
block_run *array = NULL;
uint32 runLength = run.Length();
// TODO: the following code is commented - it could be used to
// preallocate all needed block arrays to see in advance if the
// allocation will succeed.
// I will probably remove it later, because it's no perfect solution
// either: if the allocation was broken up before (blocksNeeded != 0),
// it doesn't guarantee anything.
// And since failing in this case is not that common, it doesn't have
// to be optimized in that way.
// Anyway, I wanted to have it in CVS - all those lines, and they will
// be removed soon :-)
/*
// allocate new block arrays if needed
off_t block = -1;
for (int32 i = 0;i < needed;i++) {
// get the block to insert the run into
block = fVolume->ToBlock(data->double_indirect) + i + indirectIndex / runsPerBlock;
if (cached.BlockNumber() != block)
array = (block_run *)cached.SetTo(block);
if (array == NULL)
return B_ERROR;
status = AllocateBlockArray(transaction, array[i + indirectIndex % runsPerBlock]);
if (status < B_OK)
return status;
}
*/
while (run.length != 0) {
// get the indirect array block
if (array == NULL) {
@ -1892,8 +1863,7 @@ Inode::_FreeStaticStreamArray(Transaction &transaction, int32 level,
}
/*!
Frees all block_runs in the array which come after the specified size.
/*! Frees all block_runs in the array which come after the specified size.
It also trims the last block_run that contain the size.
"offset" and "max" are maintained until the last block_run that doesn't
have to be freed - after this, the values won't be correct anymore, but
@ -2057,8 +2027,7 @@ Inode::Append(Transaction &transaction, off_t bytes)
}
/*!
Checks wether or not this inode's data stream needs to be trimmed
/*! Checks wether or not this inode's data stream needs to be trimmed
because of an earlier preallocation.
Returns true if there are any blocks to be trimmed.
*/
@ -2311,8 +2280,7 @@ Inode::Remove(Transaction &transaction, const char *name, ino_t *_id,
}
/*!
Creates the inode with the specified parent directory, and automatically
/*! Creates the inode with the specified parent directory, and automatically
adds the created inode to that parent directory. If an attribute directory
is created, it will also automatically be added to the parent inode as
such. However, the indices root node, and the regular root node won't be
@ -2347,10 +2315,11 @@ Inode::Create(Transaction &transaction, Inode *parent, const char *name,
// the parent directory is locked during the whole inode creation
if (tree != NULL) {
// does the file already exist?
// Does the file already exist?
off_t offset;
if (tree->Find((uint8 *)name, (uint16)strlen(name), &offset) == B_OK) {
// return if the file should be a directory/link or opened in exclusive mode
// Return if the file should be a directory/link or opened in
// exclusive mode
if (S_ISDIR(mode) || S_ISLNK(mode) || openMode & O_EXCL)
return B_FILE_EXISTS;
@ -2394,7 +2363,8 @@ Inode::Create(Transaction &transaction, Inode *parent, const char *name,
if (_inode)
*_inode = inode;
// only keep the vnode in memory if the _id or _inode pointer is provided
// Only keep the vnode in memory if the _id or _inode pointer is
// provided
if (_id != NULL || _inode != NULL)
vnode.Keep();
@ -2443,7 +2413,8 @@ Inode::Create(Transaction &transaction, Inode *parent, const char *name,
// only add the name to regular files, directories, or symlinks
// don't add it to attributes, or indices
if (tree && inode->IsRegularNode() && inode->SetName(transaction, name) < B_OK)
if (tree && inode->IsRegularNode()
&& inode->SetName(transaction, name) < B_OK)
return B_ERROR;
// Initialize b+tree if it's a directory (and add "." & ".." if it's
@ -2496,8 +2467,8 @@ Inode::Create(Transaction &transaction, Inode *parent, const char *name,
// The "size" & "last_modified" indices don't contain directories
if (inode->IsFile() || inode->IsSymLink()) {
// if adding to these indices fails, the inode creation will not be harmed;
// they are considered less important than the "name" index
// if adding to these indices fails, the inode creation will not be
// harmed; they are considered less important than the "name" index
if (inode->IsFile())
index.InsertSize(transaction, inode);
index.InsertLastModified(transaction, inode);