BFS: Fixed maximum index key length.
* The maximum key length of the B+tree, and the one BFS uses are now separated. This allows to stay BeOS compatible to only put a maximum of 255 bytes into the index, but also to handle the already existing larger keys (we did allow 256 bytes) without issue. * TreeIterator::Traverse() now always cuts off string keys at the maximum buffer length, and only reports a B_BUFFER_OVERFLOW for the fixed length types. * This fixes the important part of #13254.
This commit is contained in:
parent
a8ef140948
commit
4069e1f302
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2001-2015, Axel Dörfler, axeld@pinc-software.de.
|
* Copyright 2001-2017, Axel Dörfler, axeld@pinc-software.de.
|
||||||
* This file may be used under the terms of the MIT License.
|
* This file may be used under the terms of the MIT License.
|
||||||
*
|
*
|
||||||
* Roughly based on 'btlib' written by Marcus J. Ranum - it shares
|
* Roughly based on 'btlib' written by Marcus J. Ranum - it shares
|
||||||
@ -1692,10 +1692,8 @@ BPlusTree::Insert(Transaction& transaction, const uint8* key, uint16 keyLength,
|
|||||||
if (_SeekDown(stack, key, keyLength) != B_OK)
|
if (_SeekDown(stack, key, keyLength) != B_OK)
|
||||||
RETURN_ERROR(B_ERROR);
|
RETURN_ERROR(B_ERROR);
|
||||||
|
|
||||||
uint8 keyBuffer[BPLUSTREE_MAX_KEY_LENGTH + 1];
|
uint8 keyBuffer[BPLUSTREE_MAX_KEY_LENGTH];
|
||||||
|
|
||||||
memcpy(keyBuffer, key, keyLength);
|
memcpy(keyBuffer, key, keyLength);
|
||||||
keyBuffer[keyLength] = 0;
|
|
||||||
|
|
||||||
node_and_key nodeAndKey;
|
node_and_key nodeAndKey;
|
||||||
const bplustree_node* node;
|
const bplustree_node* node;
|
||||||
@ -2746,12 +2744,20 @@ TreeIterator::Traverse(int8 direction, void* key, uint16* keyLength,
|
|||||||
// include the termination for string types
|
// include the termination for string types
|
||||||
bool needsTermination = fTree->fHeader.DataType() == BPLUSTREE_STRING_TYPE;
|
bool needsTermination = fTree->fHeader.DataType() == BPLUSTREE_STRING_TYPE;
|
||||||
if (length + (needsTermination ? 1 : 0) > maxLength) {
|
if (length + (needsTermination ? 1 : 0) > maxLength) {
|
||||||
// the buffer is too small, restore the last key and return an error
|
if (!needsTermination) {
|
||||||
|
// The buffer is too small, restore the last key and return
|
||||||
|
// an error
|
||||||
fCurrentNodeOffset = savedNodeOffset;
|
fCurrentNodeOffset = savedNodeOffset;
|
||||||
fCurrentKey = savedKey;
|
fCurrentKey = savedKey;
|
||||||
return B_BUFFER_OVERFLOW;
|
return B_BUFFER_OVERFLOW;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Always cut off strings at the maximum buffer size, and leave
|
||||||
|
// room for a terminating null byte.
|
||||||
|
// This allows to handle larger key sizes gracefully.
|
||||||
|
length = maxLength - 1;
|
||||||
|
}
|
||||||
|
|
||||||
memcpy(key, keyStart, length);
|
memcpy(key, keyStart, length);
|
||||||
|
|
||||||
if (needsTermination)
|
if (needsTermination)
|
||||||
|
@ -2175,8 +2175,8 @@ BlockAllocator::_AddInodeToIndex(Inode* inode)
|
|||||||
if (inode->InSizeIndex())
|
if (inode->InSizeIndex())
|
||||||
status = tree->Insert(transaction, inode->Size(), inode->ID());
|
status = tree->Insert(transaction, inode->Size(), inode->ID());
|
||||||
} else {
|
} else {
|
||||||
uint8 key[BPLUSTREE_MAX_KEY_LENGTH];
|
uint8 key[MAX_INDEX_KEY_LENGTH];
|
||||||
size_t keyLength = BPLUSTREE_MAX_KEY_LENGTH;
|
size_t keyLength = MAX_INDEX_KEY_LENGTH;
|
||||||
if (inode->ReadAttribute(index->name, B_ANY_TYPE, 0, key,
|
if (inode->ReadAttribute(index->name, B_ANY_TYPE, 0, key,
|
||||||
&keyLength) == B_OK) {
|
&keyLength) == B_OK) {
|
||||||
status = tree->Insert(transaction, key, keyLength, inode->ID());
|
status = tree->Insert(transaction, key, keyLength, inode->ID());
|
||||||
|
@ -986,8 +986,8 @@ Inode::_RemoveAttribute(Transaction& transaction, const char* name,
|
|||||||
Inode* attribute;
|
Inode* attribute;
|
||||||
if ((hasIndex || fVolume->CheckForLiveQuery(name))
|
if ((hasIndex || fVolume->CheckForLiveQuery(name))
|
||||||
&& GetAttribute(name, &attribute) == B_OK) {
|
&& GetAttribute(name, &attribute) == B_OK) {
|
||||||
uint8 data[BPLUSTREE_MAX_KEY_LENGTH];
|
uint8 data[MAX_INDEX_KEY_LENGTH];
|
||||||
size_t length = BPLUSTREE_MAX_KEY_LENGTH;
|
size_t length = MAX_INDEX_KEY_LENGTH;
|
||||||
if (attribute->ReadAt(0, data, &length) == B_OK) {
|
if (attribute->ReadAt(0, data, &length) == B_OK) {
|
||||||
index->Update(transaction, name, attribute->Type(), data,
|
index->Update(transaction, name, attribute->Type(), data,
|
||||||
length, NULL, 0, this);
|
length, NULL, 0, this);
|
||||||
@ -1082,7 +1082,7 @@ Inode::WriteAttribute(Transaction& transaction, const char* name, int32 type,
|
|||||||
return B_BAD_VALUE;
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
// needed to maintain the index
|
// needed to maintain the index
|
||||||
uint8 oldBuffer[BPLUSTREE_MAX_KEY_LENGTH];
|
uint8 oldBuffer[MAX_INDEX_KEY_LENGTH];
|
||||||
uint8* oldData = NULL;
|
uint8* oldData = NULL;
|
||||||
size_t oldLength = 0;
|
size_t oldLength = 0;
|
||||||
bool created = false;
|
bool created = false;
|
||||||
@ -1091,7 +1091,7 @@ Inode::WriteAttribute(Transaction& transaction, const char* name, int32 type,
|
|||||||
// 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!
|
||||||
// TODO: for attribute files, we need to log the first
|
// TODO: for attribute files, we need to log the first
|
||||||
// BPLUSTREE_MAX_KEY_LENGTH bytes of the data stream, or the same as above
|
// MAX_INDEX_KEY_LENGTH bytes of the data stream, or the same as above
|
||||||
// might happen.
|
// might happen.
|
||||||
|
|
||||||
Index index(fVolume);
|
Index index(fVolume);
|
||||||
@ -1114,8 +1114,8 @@ Inode::WriteAttribute(Transaction& transaction, const char* name, int32 type,
|
|||||||
if (smallData != NULL) {
|
if (smallData != NULL) {
|
||||||
oldLength = smallData->DataSize();
|
oldLength = smallData->DataSize();
|
||||||
if (oldLength > 0) {
|
if (oldLength > 0) {
|
||||||
if (oldLength > BPLUSTREE_MAX_KEY_LENGTH)
|
if (oldLength > MAX_INDEX_KEY_LENGTH)
|
||||||
oldLength = BPLUSTREE_MAX_KEY_LENGTH;
|
oldLength = MAX_INDEX_KEY_LENGTH;
|
||||||
memcpy(oldData = oldBuffer, smallData->Data(), oldLength);
|
memcpy(oldData = oldBuffer, smallData->Data(), oldLength);
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
@ -1161,7 +1161,7 @@ Inode::WriteAttribute(Transaction& transaction, const char* name, int32 type,
|
|||||||
bigtime_t oldModified = attribute->LastModified();
|
bigtime_t oldModified = attribute->LastModified();
|
||||||
writeLocker.Unlock();
|
writeLocker.Unlock();
|
||||||
|
|
||||||
oldLength = BPLUSTREE_MAX_KEY_LENGTH;
|
oldLength = MAX_INDEX_KEY_LENGTH;
|
||||||
if (attribute->ReadAt(0, oldBuffer, &oldLength) == B_OK)
|
if (attribute->ReadAt(0, oldBuffer, &oldLength) == B_OK)
|
||||||
oldData = oldBuffer;
|
oldData = oldBuffer;
|
||||||
|
|
||||||
@ -1215,10 +1215,10 @@ Inode::WriteAttribute(Transaction& transaction, const char* name, int32 type,
|
|||||||
// 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 MAX_INDEX_KEY_LENGTH bytes
|
||||||
uint16 length = *_length;
|
uint16 length = *_length;
|
||||||
if (length > BPLUSTREE_MAX_KEY_LENGTH)
|
if (length > MAX_INDEX_KEY_LENGTH)
|
||||||
length = BPLUSTREE_MAX_KEY_LENGTH;
|
length = MAX_INDEX_KEY_LENGTH;
|
||||||
|
|
||||||
// Update index. Note, Index::Update() may be called even if
|
// Update index. Note, Index::Update() may be called even if
|
||||||
// initializing the index failed - it will just update the live
|
// initializing the index failed - it will just update the live
|
||||||
@ -1256,8 +1256,8 @@ Inode::RemoveAttribute(Transaction& transaction, const char* name)
|
|||||||
small_data* smallData = FindSmallData(node.Node(), name);
|
small_data* smallData = FindSmallData(node.Node(), name);
|
||||||
if (smallData != NULL) {
|
if (smallData != NULL) {
|
||||||
uint32 length = smallData->DataSize();
|
uint32 length = smallData->DataSize();
|
||||||
if (length > BPLUSTREE_MAX_KEY_LENGTH)
|
if (length > MAX_INDEX_KEY_LENGTH)
|
||||||
length = BPLUSTREE_MAX_KEY_LENGTH;
|
length = MAX_INDEX_KEY_LENGTH;
|
||||||
index.Update(transaction, name, smallData->Type(),
|
index.Update(transaction, name, smallData->Type(),
|
||||||
smallData->Data(), length, NULL, 0, this);
|
smallData->Data(), length, NULL, 0, this);
|
||||||
}
|
}
|
||||||
@ -1361,20 +1361,19 @@ Inode::IsEmpty()
|
|||||||
{
|
{
|
||||||
TreeIterator iterator(fTree);
|
TreeIterator iterator(fTree);
|
||||||
|
|
||||||
// index and attribute directories are really empty when they are
|
|
||||||
// empty - directories for standard files always contain ".", and
|
|
||||||
// "..", so we need to ignore those two
|
|
||||||
|
|
||||||
uint32 count = 0;
|
uint32 count = 0;
|
||||||
char name[BPLUSTREE_MAX_KEY_LENGTH];
|
char name[MAX_INDEX_KEY_LENGTH + 1];
|
||||||
uint16 length;
|
uint16 length;
|
||||||
ino_t id;
|
ino_t id;
|
||||||
while (iterator.GetNextEntry(name, &length, B_FILE_NAME_LENGTH,
|
while (iterator.GetNextEntry(name, &length, MAX_INDEX_KEY_LENGTH + 1,
|
||||||
&id) == B_OK) {
|
&id) == B_OK) {
|
||||||
if (Mode() & (S_ATTR_DIR | S_INDEX_DIR))
|
if ((Mode() & (S_ATTR_DIR | S_INDEX_DIR)) != 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (++count > 2 || (strcmp(".", name) && strcmp("..", name)))
|
// Unlike index and attribute directories, directories
|
||||||
|
// for standard files always contain ".", and "..", so
|
||||||
|
// we need to ignore those two
|
||||||
|
if (++count > 2 || (strcmp(".", name) != 0 && strcmp("..", name) != 0))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2001-2010, Axel Dörfler, axeld@pinc-software.de.
|
* Copyright 2001-2017, Axel Dörfler, axeld@pinc-software.de.
|
||||||
* Parts of this code is based on work previously done by Marcus Overhagen.
|
* Parts of this code is based on work previously done by Marcus Overhagen.
|
||||||
*
|
*
|
||||||
* This file may be used under the terms of the MIT License.
|
* This file may be used under the terms of the MIT License.
|
||||||
@ -166,11 +166,16 @@ struct small_data {
|
|||||||
inline bool IsLast(const bfs_inode* inode) const;
|
inline bool IsLast(const bfs_inode* inode) const;
|
||||||
} _PACKED;
|
} _PACKED;
|
||||||
|
|
||||||
// the file name is part of the small_data structure
|
// The file name is part of the small_data structure
|
||||||
#define FILE_NAME_TYPE 'CSTR'
|
#define FILE_NAME_TYPE 'CSTR'
|
||||||
#define FILE_NAME_NAME 0x13
|
#define FILE_NAME_NAME 0x13
|
||||||
#define FILE_NAME_NAME_LENGTH 1
|
#define FILE_NAME_NAME_LENGTH 1
|
||||||
|
|
||||||
|
// The maximum key length of attribute data that is put in the index.
|
||||||
|
// This excludes a terminating null byte.
|
||||||
|
// This must be smaller than or equal as BPLUSTREE_MAX_KEY_LENGTH.
|
||||||
|
#define MAX_INDEX_KEY_LENGTH 255
|
||||||
|
|
||||||
|
|
||||||
//**************************************
|
//**************************************
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user