* Removed the cookie field of the check_control structure; that really was

a stupid idea. Instead, the already existing fCheckCookie member is used.
* bfs_ioctl() now accesses all userland buffers safely, this should help with
  #3264, and move the crash where it belongs.
* Changes not yet tested; they only affect checkfs.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@35743 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler
2010-03-03 19:38:56 +00:00
parent 625ab0dea1
commit d501707d8a
4 changed files with 58 additions and 67 deletions
@@ -1177,7 +1177,7 @@ BlockAllocator::_CheckGroup(int32 groupIndex) const
bool bool
BlockAllocator::_IsValidCheckControl(check_control* control) BlockAllocator::_IsValidCheckControl(const check_control* control)
{ {
if (control == NULL if (control == NULL
|| control->magic != BFS_IOCTL_CHECK_MAGIC) { || control->magic != BFS_IOCTL_CHECK_MAGIC) {
@@ -1190,7 +1190,7 @@ BlockAllocator::_IsValidCheckControl(check_control* control)
status_t status_t
BlockAllocator::StartChecking(check_control* control) BlockAllocator::StartChecking(const check_control* control)
{ {
if (!_IsValidCheckControl(control)) if (!_IsValidCheckControl(control))
return B_BAD_VALUE; return B_BAD_VALUE;
@@ -1212,8 +1212,8 @@ BlockAllocator::StartChecking(check_control* control)
return B_NO_MEMORY; return B_NO_MEMORY;
} }
check_cookie* cookie = new check_cookie(); fCheckCookie = new check_cookie();
if (cookie == NULL) { if (fCheckCookie == NULL) {
free(fCheckBitmap); free(fCheckBitmap);
fCheckBitmap = NULL; fCheckBitmap = NULL;
mutex_unlock(&fLock); mutex_unlock(&fLock);
@@ -1229,19 +1229,15 @@ BlockAllocator::StartChecking(check_control* control)
_SetCheckBitmapAt(block); _SetCheckBitmapAt(block);
} }
cookie->stack.Push(fVolume->Root()); fCheckCookie->stack.Push(fVolume->Root());
cookie->stack.Push(fVolume->Indices()); fCheckCookie->stack.Push(fVolume->Indices());
cookie->iterator = NULL; fCheckCookie->iterator = NULL;
control->cookie = cookie;
fCheckCookie = cookie;
// to be able to restore nicely if "chkbfs" exited abnormally
// Put removed vnodes to the stack -- they are not reachable by traversing // Put removed vnodes to the stack -- they are not reachable by traversing
// the file system anymore. // the file system anymore.
InodeList::Iterator iterator = fVolume->RemovedInodes().GetIterator(); InodeList::Iterator iterator = fVolume->RemovedInodes().GetIterator();
while (Inode* inode = iterator.Next()) { while (Inode* inode = iterator.Next()) {
cookie->stack.Push(inode->BlockRun()); fCheckCookie->stack.Push(inode->BlockRun());
} }
// TODO: check reserved area in bitmap! // TODO: check reserved area in bitmap!
@@ -1253,21 +1249,15 @@ BlockAllocator::StartChecking(check_control* control)
status_t status_t
BlockAllocator::StopChecking(check_control* control) BlockAllocator::StopChecking(check_control* control)
{ {
check_cookie* cookie; if (fCheckCookie == NULL)
if (control == NULL)
cookie = fCheckCookie;
else
cookie = (check_cookie*)control->cookie;
if (cookie == NULL)
return B_ERROR; return B_ERROR;
if (cookie->iterator != NULL) { if (fCheckCookie->iterator != NULL) {
delete cookie->iterator; delete fCheckCookie->iterator;
cookie->iterator = NULL; fCheckCookie->iterator = NULL;
// the current directory inode is still locked in memory // the current directory inode is still locked in memory
put_vnode(fVolume->FSVolume(), fVolume->ToVnode(cookie->current)); put_vnode(fVolume->FSVolume(), fVolume->ToVnode(fCheckCookie->current));
} }
if (fVolume->IsReadOnly()) { if (fVolume->IsReadOnly()) {
@@ -1351,8 +1341,8 @@ BlockAllocator::StopChecking(check_control* control)
free(fCheckBitmap); free(fCheckBitmap);
fCheckBitmap = NULL; fCheckBitmap = NULL;
delete fCheckCookie;
fCheckCookie = NULL; fCheckCookie = NULL;
delete cookie;
mutex_unlock(&fLock); mutex_unlock(&fLock);
fVolume->GetJournal(0)->Unlock(NULL, true); fVolume->GetJournal(0)->Unlock(NULL, true);
@@ -1366,22 +1356,21 @@ BlockAllocator::CheckNextNode(check_control* control)
if (!_IsValidCheckControl(control)) if (!_IsValidCheckControl(control))
return B_BAD_VALUE; return B_BAD_VALUE;
check_cookie* cookie = (check_cookie*)control->cookie;
fVolume->SetCheckingThread(find_thread(NULL)); fVolume->SetCheckingThread(find_thread(NULL));
while (true) { while (true) {
if (cookie->iterator == NULL) { if (fCheckCookie->iterator == NULL) {
if (!cookie->stack.Pop(&cookie->current)) { if (!fCheckCookie->stack.Pop(&fCheckCookie->current)) {
// no more runs on the stack, we are obviously finished! // no more runs on the stack, we are obviously finished!
control->status = B_ENTRY_NOT_FOUND; control->status = B_ENTRY_NOT_FOUND;
return B_ENTRY_NOT_FOUND; return B_ENTRY_NOT_FOUND;
} }
Vnode vnode(fVolume, cookie->current); Vnode vnode(fVolume, fCheckCookie->current);
Inode* inode; Inode* inode;
if (vnode.Get(&inode) != B_OK) { if (vnode.Get(&inode) != B_OK) {
FATAL(("check: Could not open inode at %" B_PRIdOFF "\n", FATAL(("check: Could not open inode at %" B_PRIdOFF "\n",
fVolume->ToBlock(cookie->current))); fVolume->ToBlock(fCheckCookie->current)));
continue; continue;
} }
@@ -1404,15 +1393,15 @@ BlockAllocator::CheckNextNode(check_control* control)
BPlusTree* tree = inode->Tree(); BPlusTree* tree = inode->Tree();
if (tree == NULL) { if (tree == NULL) {
FATAL(("check: could not open b+tree from inode at %" B_PRIdOFF FATAL(("check: could not open b+tree from inode at %" B_PRIdOFF
"\n", fVolume->ToBlock(cookie->current))); "\n", fVolume->ToBlock(fCheckCookie->current)));
continue; continue;
} }
cookie->parent = inode; fCheckCookie->parent = inode;
cookie->parent_mode = inode->Mode(); fCheckCookie->parent_mode = inode->Mode();
cookie->iterator = new TreeIterator(tree); fCheckCookie->iterator = new TreeIterator(tree);
if (cookie->iterator == NULL) if (fCheckCookie->iterator == NULL)
RETURN_ERROR(B_NO_MEMORY); RETURN_ERROR(B_NO_MEMORY);
// the inode must stay locked in memory until the iterator is freed // the inode must stay locked in memory until the iterator is freed
@@ -1432,15 +1421,16 @@ BlockAllocator::CheckNextNode(check_control* control)
uint16 length; uint16 length;
ino_t id; ino_t id;
status_t status = cookie->iterator->GetNextEntry(name, &length, status_t status = fCheckCookie->iterator->GetNextEntry(name, &length,
B_FILE_NAME_LENGTH, &id); B_FILE_NAME_LENGTH, &id);
if (status == B_ENTRY_NOT_FOUND) { if (status == B_ENTRY_NOT_FOUND) {
// there are no more entries in this iterator, free it and go on // there are no more entries in this iterator, free it and go on
delete cookie->iterator; delete fCheckCookie->iterator;
cookie->iterator = NULL; fCheckCookie->iterator = NULL;
// unlock the directory's inode from memory // unlock the directory's inode from memory
put_vnode(fVolume->FSVolume(), fVolume->ToVnode(cookie->current)); put_vnode(fVolume->FSVolume(),
fVolume->ToVnode(fCheckCookie->current));
continue; continue;
} else if (status == B_OK) { } else if (status == B_OK) {
@@ -1460,8 +1450,8 @@ BlockAllocator::CheckNextNode(check_control* control)
control->errors |= BFS_COULD_NOT_OPEN; control->errors |= BFS_COULD_NOT_OPEN;
if ((control->flags & BFS_REMOVE_INVALID) != 0) { if ((control->flags & BFS_REMOVE_INVALID) != 0) {
status = _RemoveInvalidNode(cookie->parent, status = _RemoveInvalidNode(fCheckCookie->parent,
cookie->iterator->Tree(), NULL, name); fCheckCookie->iterator->Tree(), NULL, name);
} else } else
status = B_ERROR; status = B_ERROR;
@@ -1501,22 +1491,22 @@ BlockAllocator::CheckNextNode(check_control* control)
// Check for the correct mode of the node (if the mode of the // Check for the correct mode of the node (if the mode of the
// file don't fit to its parent, there is a serious problem) // file don't fit to its parent, there is a serious problem)
if (((cookie->parent_mode & S_ATTR_DIR) != 0 if (((fCheckCookie->parent_mode & S_ATTR_DIR) != 0
&& !inode->IsAttribute()) && !inode->IsAttribute())
|| ((cookie->parent_mode & S_INDEX_DIR) != 0 || ((fCheckCookie->parent_mode & S_INDEX_DIR) != 0
&& !inode->IsIndex()) && !inode->IsIndex())
|| (is_directory(cookie->parent_mode) || (is_directory(fCheckCookie->parent_mode)
&& !inode->IsRegularNode())) { && !inode->IsRegularNode())) {
FATAL(("inode at %" B_PRIdOFF " is of wrong type: %o (parent " FATAL(("inode at %" B_PRIdOFF " is of wrong type: %o (parent "
"%o at %" B_PRIdOFF ")!\n", inode->BlockNumber(), "%o at %" B_PRIdOFF ")!\n", inode->BlockNumber(),
inode->Mode(), cookie->parent_mode, inode->Mode(), fCheckCookie->parent_mode,
cookie->parent->BlockNumber())); fCheckCookie->parent->BlockNumber()));
// if we are allowed to fix errors, we should remove the file // if we are allowed to fix errors, we should remove the file
if ((control->flags & BFS_REMOVE_WRONG_TYPES) != 0 if ((control->flags & BFS_REMOVE_WRONG_TYPES) != 0
&& (control->flags & BFS_FIX_BITMAP_ERRORS) != 0) { && (control->flags & BFS_FIX_BITMAP_ERRORS) != 0) {
status = _RemoveInvalidNode(cookie->parent, NULL, inode, status = _RemoveInvalidNode(fCheckCookie->parent, NULL,
name); inode, name);
} else } else
status = B_ERROR; status = B_ERROR;
@@ -1527,7 +1517,7 @@ BlockAllocator::CheckNextNode(check_control* control)
// push the directory on the stack so that it will be scanned later // push the directory on the stack so that it will be scanned later
if (inode->IsContainer() && !inode->IsIndex()) if (inode->IsContainer() && !inode->IsIndex())
cookie->stack.Push(inode->BlockRun()); fCheckCookie->stack.Push(inode->BlockRun());
else { else {
// check it now // check it now
control->status = CheckInode(inode, control); control->status = CheckInode(inode, control);
@@ -1757,10 +1747,8 @@ BlockAllocator::CheckInode(Inode* inode, check_control* control)
return status; return status;
// If the inode has an attribute directory, push it on the stack // If the inode has an attribute directory, push it on the stack
if (!inode->Attributes().IsZero()) { if (!inode->Attributes().IsZero())
check_cookie* cookie = (check_cookie*)control->cookie; fCheckCookie->stack.Push(inode->Attributes());
cookie->stack.Push(inode->Attributes());
}
if (inode->IsSymLink() && (inode->Flags() & INODE_LONG_SYMLINK) == 0) { if (inode->IsSymLink() && (inode->Flags() & INODE_LONG_SYMLINK) == 0) {
// symlinks may not have a valid data stream // symlinks may not have a valid data stream
@@ -46,7 +46,7 @@ public:
int32 group, uint16 start, uint16 numBlocks, int32 group, uint16 start, uint16 numBlocks,
uint16 minimum, block_run& run); uint16 minimum, block_run& run);
status_t StartChecking(check_control* control); status_t StartChecking(const check_control* control);
status_t StopChecking(check_control* control); status_t StopChecking(check_control* control);
status_t CheckNextNode(check_control* control); status_t CheckNextNode(check_control* control);
@@ -74,7 +74,7 @@ private:
#ifdef DEBUG_ALLOCATION_GROUPS #ifdef DEBUG_ALLOCATION_GROUPS
void _CheckGroup(int32 group) const; void _CheckGroup(int32 group) const;
#endif #endif
bool _IsValidCheckControl(check_control* control); bool _IsValidCheckControl(const check_control* control);
bool _CheckBitmapIsUsedAt(off_t block) const; bool _CheckBitmapIsUsedAt(off_t block) const;
void _SetCheckBitmapAt(off_t block); void _SetCheckBitmapAt(off_t block);
@@ -51,7 +51,6 @@ struct check_control {
uint64 freed; uint64 freed;
} stats; } stats;
status_t status; status_t status;
void* cookie;
}; };
/* values for the flags field */ /* values for the flags field */
@@ -618,23 +618,21 @@ bfs_ioctl(fs_volume* _volume, fs_vnode* _node, void* _cookie, ulong cmd,
Volume* volume = (Volume*)_volume->private_volume; Volume* volume = (Volume*)_volume->private_volume;
// TODO: Access user buffers safely!
switch (cmd) { switch (cmd) {
case BFS_IOCTL_VERSION: case BFS_IOCTL_VERSION:
{ {
uint32 *version = (uint32*)buffer; uint32 version = 0x10000;
return user_memcpy(buffer, &version, sizeof(uint32));
*version = 0x10000;
return B_OK;
} }
case BFS_IOCTL_START_CHECKING: case BFS_IOCTL_START_CHECKING:
{ {
// start checking // start checking
BlockAllocator& allocator = volume->Allocator(); BlockAllocator& allocator = volume->Allocator();
check_control* control = (check_control*)buffer; check_control control;
if (user_memcpy(&control, buffer, sizeof(check_control)) != B_OK)
return B_BAD_ADDRESS;
status_t status = allocator.StartChecking(control); status_t status = allocator.StartChecking(&control);
if (status == B_OK) { if (status == B_OK) {
file_cookie* cookie = (file_cookie*)_cookie; file_cookie* cookie = (file_cookie*)_cookie;
cookie->open_mode |= BFS_OPEN_MODE_CHECKING; cookie->open_mode |= BFS_OPEN_MODE_CHECKING;
@@ -646,13 +644,15 @@ bfs_ioctl(fs_volume* _volume, fs_vnode* _node, void* _cookie, ulong cmd,
{ {
// stop checking // stop checking
BlockAllocator& allocator = volume->Allocator(); BlockAllocator& allocator = volume->Allocator();
check_control* control = (check_control*)buffer; check_control control;
status_t status = allocator.StopChecking(control); status_t status = allocator.StopChecking(&control);
if (status == B_OK) { if (status == B_OK) {
file_cookie* cookie = (file_cookie*)_cookie; file_cookie* cookie = (file_cookie*)_cookie;
cookie->open_mode &= ~BFS_OPEN_MODE_CHECKING; cookie->open_mode &= ~BFS_OPEN_MODE_CHECKING;
} }
if (status == B_OK)
status = user_memcpy(buffer, &control, sizeof(check_control));
return status; return status;
} }
@@ -660,9 +660,13 @@ bfs_ioctl(fs_volume* _volume, fs_vnode* _node, void* _cookie, ulong cmd,
{ {
// check next // check next
BlockAllocator& allocator = volume->Allocator(); BlockAllocator& allocator = volume->Allocator();
check_control* control = (check_control*)buffer; check_control control;
return allocator.CheckNextNode(control); status_t status = allocator.CheckNextNode(&control);
if (status == B_OK)
status = user_memcpy(buffer, &control, sizeof(check_control));
return status;
} }
case BFS_IOCTL_UPDATE_BOOT_BLOCK: case BFS_IOCTL_UPDATE_BOOT_BLOCK:
{ {