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. * 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)

View File

@ -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());

View File

@ -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;

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. * 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
//************************************** //**************************************