* Copyright 2007, Ingo Weinhold, ingo_weinhold@gmx.de.
* Copyright 2008-2012, Axel Dörfler, axeld@pinc-software.de.
*
* Distributed under the terms of the MIT License.
*/
#include "BFSAddOn.h"
#include "InitializeParameterEditor.h"
#include <new>
#include <Directory.h>
#include <List.h>
#include <Path.h>
#include <Volume.h>
#include <DiskDeviceTypes.h>
#include <MutablePartition.h>
#include <AutoDeleter.h>
#include <StringForSize.h>
#ifdef ASSERT
# undef ASSERT
#endif
#include "bfs.h"
#include "bfs_control.h"
#include "bfs_disk_system.h"
using std::nothrow;
static const uint32 kDiskSystemFlags =
0
| B_DISK_SYSTEM_SUPPORTS_INITIALIZING
| B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME
| B_DISK_SYSTEM_SUPPORTS_CHECKING_WHILE_MOUNTED
| B_DISK_SYSTEM_SUPPORTS_REPAIRING_WHILE_MOUNTED
;
static BString
size_string(double size)
{
BString string;
char* buffer = string.LockBuffer(256);
string_for_size(size, buffer, 256);
string.UnlockBuffer();
return string;
}
BFSAddOn::BFSAddOn()
: BDiskSystemAddOn(kPartitionTypeBFS)
{
}
BFSAddOn::~BFSAddOn()
{
}
status_t
BFSAddOn::CreatePartitionHandle(BMutablePartition* partition,
BPartitionHandle** _handle)
{
BFSPartitionHandle* handle = new(nothrow) BFSPartitionHandle(partition);
if (!handle)
return B_NO_MEMORY;
status_t error = handle->Init();
if (error != B_OK) {
delete handle;
return error;
}
*_handle = handle;
return B_OK;
}
bool
BFSAddOn::CanInitialize(const BMutablePartition* partition)
{
return partition->Size() >= 1L * 1024 * 1024;
}
status_t
BFSAddOn::ValidateInitialize(const BMutablePartition* partition, BString* name,
const char* parameterString)
{
if (!CanInitialize(partition) || !name)
return B_BAD_VALUE;
if (name->Length() >= BFS_DISK_NAME_LENGTH)
name->Truncate(BFS_DISK_NAME_LENGTH - 1);
name->ReplaceAll('/', '-');
initialize_parameters parameters;
status_t error = parse_initialize_parameters(parameterString, parameters);
if (error != B_OK)
return error;
off_t size = partition->Size();
uint32 blockSize = parameters.blockSize;
if (size < 2 * 1024 * 1024) {
if (blockSize != 1024)
return B_BAD_VALUE;
} else if (size < 4 * 1024 * 1024) {
if (blockSize >= 4 * 1024)
return B_BAD_VALUE;
} else if (size < 8 * 1024 * 1024) {
if (blockSize >= 8 * 1024)
return B_BAD_VALUE;
}
return B_OK;
}
status_t
BFSAddOn::Initialize(BMutablePartition* partition, const char* name,
const char* parameterString, BPartitionHandle** _handle)
{
if (!CanInitialize(partition) || check_volume_name(name) != B_OK)
return B_BAD_VALUE;
initialize_parameters parameters;
status_t error = parse_initialize_parameters(parameterString, parameters);
if (error != B_OK)
return error;
BFSPartitionHandle* handle = new(nothrow) BFSPartitionHandle(partition);
if (!handle)
return B_NO_MEMORY;
ObjectDeleter<BFSPartitionHandle> handleDeleter(handle);
error = partition->SetContentType(Name());
if (error != B_OK)
return error;
partition->SetContentName(name);
partition->SetContentParameters(parameterString);
uint32 blockSize = parameters.blockSize;
partition->SetBlockSize(blockSize);
partition->SetContentSize(partition->Size() / blockSize * blockSize);
partition->Changed(B_PARTITION_CHANGED_INITIALIZATION);
*_handle = handleDeleter.Detach();
return B_OK;
}
status_t
BFSAddOn::GetParameterEditor(B_PARAMETER_EDITOR_TYPE type,
BPartitionParameterEditor** editor)
{
*editor = NULL;
if (type == B_INITIALIZE_PARAMETER_EDITOR) {
try {
*editor = new InitializeBFSEditor();
} catch (std::bad_alloc&) {
return B_NO_MEMORY;
}
return B_OK;
}
return B_NOT_SUPPORTED;
}
BFSPartitionHandle::BFSPartitionHandle(BMutablePartition* partition)
: BPartitionHandle(partition)
{
}
BFSPartitionHandle::~BFSPartitionHandle()
{
}
status_t
BFSPartitionHandle::Init()
{
return B_OK;
}
uint32
BFSPartitionHandle::SupportedOperations(uint32 mask)
{
return kDiskSystemFlags & mask;
}
status_t
BFSPartitionHandle::Repair(bool checkOnly)
{
BVolume volume(Partition()->VolumeID());
BDirectory directory;
volume.GetRootDirectory(&directory);
BPath path;
path.SetTo(&directory, ".");
FileDescriptorCloser fd(open(path.Path(), O_RDONLY));
if (!fd.IsSet())
return errno;
struct check_control result;
memset(&result, 0, sizeof(result));
result.magic = BFS_IOCTL_CHECK_MAGIC;
result.flags = 0;
if (!checkOnly) {
result.flags |= BFS_FIX_BITMAP_ERRORS | BFS_REMOVE_WRONG_TYPES
| BFS_REMOVE_INVALID | BFS_FIX_NAME_MISMATCHES | BFS_FIX_BPLUSTREES;
}
if (ioctl(fd.Get(), BFS_IOCTL_START_CHECKING, &result, sizeof(result)) < 0)
return errno;
uint64 attributeDirectories = 0, attributes = 0;
uint64 files = 0, directories = 0, indices = 0;
uint64 counter = 0;
uint32 previousPass = result.pass;
while (ioctl(fd.Get(), BFS_IOCTL_CHECK_NEXT_NODE, &result,
sizeof(result)) == 0) {
if (++counter % 50 == 0)
printf("%9" B_PRIu64 " nodes processed\x1b[1A\n", counter);
if (result.pass == BFS_CHECK_PASS_BITMAP) {
if (result.errors) {
printf("%s (inode = %" B_PRIdINO ")", result.name, result.inode);
if ((result.errors & BFS_MISSING_BLOCKS) != 0)
printf(", some blocks weren't allocated");
if ((result.errors & BFS_BLOCKS_ALREADY_SET) != 0)
printf(", has blocks already set");
if ((result.errors & BFS_INVALID_BLOCK_RUN) != 0)
printf(", has invalid block run(s)");
if ((result.errors & BFS_COULD_NOT_OPEN) != 0)
printf(", could not be opened");
if ((result.errors & BFS_WRONG_TYPE) != 0)
printf(", has wrong type");
if ((result.errors & BFS_NAMES_DONT_MATCH) != 0)
printf(", names don't match");
if ((result.errors & BFS_INVALID_BPLUSTREE) != 0)
printf(", invalid b+tree");
putchar('\n');
}
if ((result.mode & (S_INDEX_DIR | 0777)) == S_INDEX_DIR)
indices++;
else if (result.mode & S_ATTR_DIR)
attributeDirectories++;
else if (result.mode & S_ATTR)
attributes++;
else if (S_ISDIR(result.mode))
directories++;
else
files++;
} else if (result.pass == BFS_CHECK_PASS_INDEX) {
if (previousPass != result.pass) {
printf("Recreating broken index b+trees...\n");
previousPass = result.pass;
counter = 0;
}
}
}
if (ioctl(fd.Get(), BFS_IOCTL_STOP_CHECKING, &result, sizeof(result)) != 0)
return errno;
printf(" %" B_PRIu64 " nodes checked,\n\t%" B_PRIu64 " blocks not "
"allocated,\n\t%" B_PRIu64 " blocks already set,\n\t%" B_PRIu64
" blocks could be freed\n\n", counter, result.stats.missing,
result.stats.already_set, result.stats.freed);
printf("\tfiles\t\t%" B_PRIu64 "\n\tdirectories\t%" B_PRIu64 "\n"
"\tattributes\t%" B_PRIu64 "\n\tattr. dirs\t%" B_PRIu64 "\n"
"\tindices\t\t%" B_PRIu64 "\n", files, directories, attributes,
attributeDirectories, indices);
printf("\n\tdirect block runs\t\t%" B_PRIu64 " (%s)\n",
result.stats.direct_block_runs, size_string(1.0
* result.stats.blocks_in_direct
* result.stats.block_size).String());
printf("\tindirect block runs\t\t%" B_PRIu64 " (in %" B_PRIu64
" array blocks, %s)\n", result.stats.indirect_block_runs,
result.stats.indirect_array_blocks,
size_string(1.0 * result.stats.blocks_in_indirect
* result.stats.block_size).String());
printf("\tdouble indirect block runs\t%" B_PRIu64 " (in %" B_PRIu64
" array blocks, %s)\n", result.stats.double_indirect_block_runs,
result.stats.double_indirect_array_blocks,
size_string(1.0 * result.stats.blocks_in_double_indirect
* result.stats.block_size).String());
if (result.status == B_ENTRY_NOT_FOUND)
result.status = B_OK;
return result.status;
}
status_t
get_disk_system_add_ons(BList* addOns)
{
BFSAddOn* addOn = new(nothrow) BFSAddOn;
if (!addOn)
return B_NO_MEMORY;
if (!addOns->AddItem(addOn)) {
delete addOn;
return B_NO_MEMORY;
}
return B_OK;
}