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:
Axel Dörfler 2017-04-08 22:01:18 +02:00
parent a8ef140948
commit 4069e1f302
4 changed files with 42 additions and 32 deletions

View File

@ -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.
*
* 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)
RETURN_ERROR(B_ERROR);
uint8 keyBuffer[BPLUSTREE_MAX_KEY_LENGTH + 1];
uint8 keyBuffer[BPLUSTREE_MAX_KEY_LENGTH];
memcpy(keyBuffer, key, keyLength);
keyBuffer[keyLength] = 0;
node_and_key nodeAndKey;
const bplustree_node* node;
@ -2746,10 +2744,18 @@ TreeIterator::Traverse(int8 direction, void* key, uint16* keyLength,
// include the termination for string types
bool needsTermination = fTree->fHeader.DataType() == BPLUSTREE_STRING_TYPE;
if (length + (needsTermination ? 1 : 0) > maxLength) {
// the buffer is too small, restore the last key and return an error
fCurrentNodeOffset = savedNodeOffset;
fCurrentKey = savedKey;
return B_BUFFER_OVERFLOW;
if (!needsTermination) {
// The buffer is too small, restore the last key and return
// an error
fCurrentNodeOffset = savedNodeOffset;
fCurrentKey = savedKey;
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);

View File

@ -2175,8 +2175,8 @@ BlockAllocator::_AddInodeToIndex(Inode* inode)
if (inode->InSizeIndex())
status = tree->Insert(transaction, inode->Size(), inode->ID());
} else {
uint8 key[BPLUSTREE_MAX_KEY_LENGTH];
size_t keyLength = BPLUSTREE_MAX_KEY_LENGTH;
uint8 key[MAX_INDEX_KEY_LENGTH];
size_t keyLength = MAX_INDEX_KEY_LENGTH;
if (inode->ReadAttribute(index->name, B_ANY_TYPE, 0, key,
&keyLength) == B_OK) {
status = tree->Insert(transaction, key, keyLength, inode->ID());

View File

@ -986,8 +986,8 @@ Inode::_RemoveAttribute(Transaction& transaction, const char* name,
Inode* attribute;
if ((hasIndex || fVolume->CheckForLiveQuery(name))
&& GetAttribute(name, &attribute) == B_OK) {
uint8 data[BPLUSTREE_MAX_KEY_LENGTH];
size_t length = BPLUSTREE_MAX_KEY_LENGTH;
uint8 data[MAX_INDEX_KEY_LENGTH];
size_t length = MAX_INDEX_KEY_LENGTH;
if (attribute->ReadAt(0, data, &length) == B_OK) {
index->Update(transaction, name, attribute->Type(), data,
length, NULL, 0, this);
@ -1082,7 +1082,7 @@ Inode::WriteAttribute(Transaction& transaction, const char* name, int32 type,
return B_BAD_VALUE;
// needed to maintain the index
uint8 oldBuffer[BPLUSTREE_MAX_KEY_LENGTH];
uint8 oldBuffer[MAX_INDEX_KEY_LENGTH];
uint8* oldData = NULL;
size_t oldLength = 0;
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
// 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
// MAX_INDEX_KEY_LENGTH bytes of the data stream, or the same as above
// might happen.
Index index(fVolume);
@ -1114,8 +1114,8 @@ Inode::WriteAttribute(Transaction& transaction, const char* name, int32 type,
if (smallData != NULL) {
oldLength = smallData->DataSize();
if (oldLength > 0) {
if (oldLength > BPLUSTREE_MAX_KEY_LENGTH)
oldLength = BPLUSTREE_MAX_KEY_LENGTH;
if (oldLength > MAX_INDEX_KEY_LENGTH)
oldLength = MAX_INDEX_KEY_LENGTH;
memcpy(oldData = oldBuffer, smallData->Data(), oldLength);
}
} else
@ -1161,7 +1161,7 @@ Inode::WriteAttribute(Transaction& transaction, const char* name, int32 type,
bigtime_t oldModified = attribute->LastModified();
writeLocker.Unlock();
oldLength = BPLUSTREE_MAX_KEY_LENGTH;
oldLength = MAX_INDEX_KEY_LENGTH;
if (attribute->ReadAt(0, oldBuffer, &oldLength) == B_OK)
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
// must be copied to the start of the new one for a comparison)
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;
if (length > BPLUSTREE_MAX_KEY_LENGTH)
length = BPLUSTREE_MAX_KEY_LENGTH;
if (length > MAX_INDEX_KEY_LENGTH)
length = MAX_INDEX_KEY_LENGTH;
// Update index. Note, Index::Update() may be called even if
// 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);
if (smallData != NULL) {
uint32 length = smallData->DataSize();
if (length > BPLUSTREE_MAX_KEY_LENGTH)
length = BPLUSTREE_MAX_KEY_LENGTH;
if (length > MAX_INDEX_KEY_LENGTH)
length = MAX_INDEX_KEY_LENGTH;
index.Update(transaction, name, smallData->Type(),
smallData->Data(), length, NULL, 0, this);
}
@ -1361,20 +1361,19 @@ Inode::IsEmpty()
{
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;
char name[BPLUSTREE_MAX_KEY_LENGTH];
char name[MAX_INDEX_KEY_LENGTH + 1];
uint16 length;
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) {
if (Mode() & (S_ATTR_DIR | S_INDEX_DIR))
if ((Mode() & (S_ATTR_DIR | S_INDEX_DIR)) != 0)
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 true;

View File

@ -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.
*
* 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;
} _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_NAME 0x13
#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
//**************************************