Moved the functionality of Volume::IsSuperBlockValid() into the new

disk_super_block::IsValid() method.
There is now a disk_super_block::Initialize() method that sets up
a super block for creating a new file system on it - it's currently
hard coded to produce correct results for 10 MB images, though;
the allocation group stuff has to be done a bit more flexible :)

Added a DeviceOpener class that simplifies Volume::Mount() a bit and
fixes some problems of it (forgot to call close() once or twice).
Implemented the new Volume::Initialize() method that completely
covers the mkbfs functionality.

Fixed the broken Volume::ToBlockRun() method - AFAICT it has only
be used by Volume::CreateIndicesRoot() and in dump_bplustree_node
so far (should not have been critical, as the former was probably
never called yet [only if you had tried to create an index on a
BFS volume that didn't have indices yet]).


git-svn-id: file:///srv/svn/repos/haiku/trunk/current@6313 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler 2004-01-26 02:44:27 +00:00
parent d7db210882
commit 3154133754
3 changed files with 329 additions and 77 deletions

View File

@ -20,11 +20,176 @@
#include <string.h>
#include <ctype.h>
class DeviceOpener {
public:
DeviceOpener(const char *device, int mode);
~DeviceOpener();
int Open(const char *device, int mode);
status_t InitCache(off_t numBlocks);
void RemoveCache(int mode);
void Keep();
int Device() const { return fDevice; }
status_t GetSize(off_t *_size, uint32 *_blockSize = NULL);
private:
int fDevice;
bool fCached;
};
DeviceOpener::DeviceOpener(const char *device, int mode)
:
fCached(false)
{
Open(device, mode);
}
DeviceOpener::~DeviceOpener()
{
if (fDevice >= B_OK) {
close(fDevice);
if (fCached)
remove_cached_device_blocks(fDevice, NO_WRITES);
}
}
int
DeviceOpener::Open(const char *device, int mode)
{
fDevice = open(device, mode);
return fDevice;
}
status_t
DeviceOpener::InitCache(off_t numBlocks)
{
if (init_cache_for_device(fDevice, numBlocks) == B_OK) {
fCached = true;
return B_OK;
}
return B_ERROR;
}
void
DeviceOpener::RemoveCache(int mode)
{
if (!fCached)
return;
remove_cached_device_blocks(fDevice, mode);
fCached = false;
}
void
DeviceOpener::Keep()
{
fDevice = -1;
}
/** Returns the size of the device in bytes. It uses B_GET_GEOMETRY
* to compute the size, or fstat() if that failed.
*/
status_t
DeviceOpener::GetSize(off_t *_size, uint32 *_blockSize)
{
device_geometry geometry;
if (ioctl(fDevice, B_GET_GEOMETRY, &geometry) < 0) {
// maybe it's just a file
struct stat stat;
if (fstat(fDevice, &stat) < 0)
return B_ERROR;
if (_size)
*_size = stat.st_size;
if (_blockSize) // that shouldn't cause us any problems
*_blockSize = 512;
return B_OK;
}
if (_size)
*_size = geometry.head_count * geometry.cylinder_count * geometry.sectors_per_track;
if (_blockSize)
*_blockSize = geometry.bytes_per_sector;
return B_OK;
}
// #pragma mark -
bool
disk_super_block::IsValid()
{
if (Magic1() != (int32)SUPER_BLOCK_MAGIC1
|| Magic2() != (int32)SUPER_BLOCK_MAGIC2
|| Magic3() != (int32)SUPER_BLOCK_MAGIC3
|| (int32)block_size != inode_size
|| ByteOrder() != SUPER_BLOCK_FS_LENDIAN
|| (1UL << BlockShift()) != BlockSize()
|| AllocationGroups() < 1
|| AllocationGroupShift() < 1
|| BlocksPerAllocationGroup() < 1
|| NumBlocks() < 10
|| AllocationGroups() != divide_roundup(NumBlocks(),
1L << AllocationGroupShift()))
return false;
return true;
}
void
disk_super_block::Initialize(const char *diskName, off_t numBlocks, uint32 blockSize)
{
memset(this, 0, sizeof(disk_super_block));
magic1 = HOST_ENDIAN_TO_BFS_INT32(SUPER_BLOCK_MAGIC1);
magic2 = HOST_ENDIAN_TO_BFS_INT32(SUPER_BLOCK_MAGIC2);
magic3 = HOST_ENDIAN_TO_BFS_INT32(SUPER_BLOCK_MAGIC3);
fs_byte_order = SUPER_BLOCK_FS_LENDIAN;
flags = SUPER_BLOCK_DISK_CLEAN;
strlcpy(name, diskName, sizeof(name));
block_size = inode_size = HOST_ENDIAN_TO_BFS_INT32(blockSize);
for (block_shift = 9; (1UL << block_shift) < blockSize; block_shift++);
num_blocks = numBlocks;
used_blocks = 0;
// ToDo: set allocation group stuff for real (this is hardcoded for a 10 MB file)!!!
blocks_per_ag = 1;
num_ags = 2;
ag_shift = 13;
}
// #pragma mark -
Volume::Volume(nspace_id id)
:
fID(id),
fBlockAllocator(this),
fLock("bfs volume"),
fRootNode(NULL),
fIndicesNode(NULL),
fDirtyCachedBlocks(0),
fUniqueID(0),
fFlags(0)
@ -40,21 +205,7 @@ Volume::~Volume()
bool
Volume::IsValidSuperBlock()
{
if (fSuperBlock.Magic1() != (int32)SUPER_BLOCK_MAGIC1
|| fSuperBlock.Magic2() != (int32)SUPER_BLOCK_MAGIC2
|| fSuperBlock.Magic3() != (int32)SUPER_BLOCK_MAGIC3
|| (int32)fSuperBlock.block_size != fSuperBlock.inode_size
|| fSuperBlock.ByteOrder() != SUPER_BLOCK_FS_LENDIAN
|| (1UL << fSuperBlock.BlockShift()) != fSuperBlock.BlockSize()
|| fSuperBlock.AllocationGroups() < 1
|| fSuperBlock.AllocationGroupShift() < 1
|| fSuperBlock.BlocksPerAllocationGroup() < 1
|| fSuperBlock.NumBlocks() < 10
|| fSuperBlock.AllocationGroups() != divide_roundup(fSuperBlock.NumBlocks(),
1L << fSuperBlock.AllocationGroupShift()))
return false;
return true;
return fSuperBlock.IsValid();
}
@ -84,14 +235,15 @@ Volume::Mount(const char *deviceName, uint32 flags)
flags |= B_MOUNT_READ_ONLY;
#endif
fDevice = open(deviceName, flags & B_MOUNT_READ_ONLY ? O_RDONLY : O_RDWR);
DeviceOpener opener(deviceName, flags & B_MOUNT_READ_ONLY ? O_RDONLY : O_RDWR);
// if we couldn't open the device, try read-only (don't rely on a specific error code)
if (fDevice < B_OK && (flags & B_MOUNT_READ_ONLY) == 0) {
fDevice = open(deviceName, O_RDONLY);
if (opener.Device() < B_OK && (flags & B_MOUNT_READ_ONLY) == 0) {
opener.Open(deviceName, O_RDONLY);
fFlags |= VOLUME_READ_ONLY;
}
fDevice = opener.Device();
if (fDevice < B_OK)
RETURN_ERROR(fDevice);
@ -126,81 +278,79 @@ Volume::Mount(const char *deviceName, uint32 flags)
if (!IsValidSuperBlock()) {
#ifndef BFS_LITTLE_ENDIAN_ONLY
memcpy(&fSuperBlock, buffer, sizeof(disk_super_block));
if (!IsValidSuperBlock()) {
close(fDevice);
if (!IsValidSuperBlock())
return B_BAD_VALUE;
}
#else
close(fDevice);
#else
return B_BAD_VALUE;
#endif
}
if (IsValidSuperBlock()) {
// set the current log pointers, so that journaling will work correctly
fLogStart = fSuperBlock.LogStart();
fLogEnd = fSuperBlock.LogEnd();
if (!IsValidSuperBlock()) {
FATAL(("invalid super block!\n"));
return B_BAD_VALUE;
}
// initialize short hands to the super block (to save byte swapping)
fBlockSize = fSuperBlock.BlockSize();
fBlockShift = fSuperBlock.BlockShift();
fAllocationGroupShift = fSuperBlock.AllocationGroupShift();
// check if the device size is large enough to hold the file system
off_t diskSize;
if (opener.GetSize(&diskSize) < B_OK)
RETURN_ERROR(B_ERROR);
if (diskSize < (NumBlocks() << BlockShift()))
RETURN_ERROR(B_BAD_VALUE);
if (init_cache_for_device(fDevice, NumBlocks()) == B_OK) {
fJournal = new Journal(this);
// replaying the log is the first thing we will do on this disk
if (fJournal && fJournal->InitCheck() == B_OK
&& fBlockAllocator.Initialize() == B_OK) {
fRootNode = new Inode(this, ToVnode(Root()));
// set the current log pointers, so that journaling will work correctly
fLogStart = fSuperBlock.LogStart();
fLogEnd = fSuperBlock.LogEnd();
if (fRootNode && fRootNode->InitCheck() == B_OK) {
if (new_vnode(fID, ToVnode(Root()), (void *)fRootNode) == B_OK) {
// try to get indices root dir
// initialize short hands to the super block (to save byte swapping)
fBlockSize = fSuperBlock.BlockSize();
fBlockShift = fSuperBlock.BlockShift();
fAllocationGroupShift = fSuperBlock.AllocationGroupShift();
// question: why doesn't get_vnode() work here??
// answer: we have not yet backpropagated the pointer to the
// volume in bfs_mount(), so bfs_read_vnode() can't get it.
// But it's not needed to do that anyway.
if (opener.InitCache(NumBlocks()) != B_OK)
return B_ERROR;
fIndicesNode = new Inode(this, ToVnode(Indices()));
if (fIndicesNode == NULL
|| fIndicesNode->InitCheck() < B_OK
|| !fIndicesNode->IsContainer()) {
INFORM(("bfs: volume doesn't have indices!\n"));
fJournal = new Journal(this);
// replaying the log is the first thing we will do on this disk
if (fJournal && fJournal->InitCheck() < B_OK
&& fBlockAllocator.Initialize() < B_OK) {
// ToDo: improve error reporting for a bad journal
FATAL(("could not initialize journal/block bitmap allocator!\n"));
return B_NO_MEMORY;
}
if (fIndicesNode) {
// if this is the case, the index root node is gone bad, and
// BFS switch to read-only mode
fFlags |= VOLUME_READ_ONLY;
fIndicesNode = NULL;
}
}
fRootNode = new Inode(this, ToVnode(Root()));
if (fRootNode && fRootNode->InitCheck() == B_OK) {
if (new_vnode(fID, ToVnode(Root()), (void *)fRootNode) == B_OK) {
// try to get indices root dir
// all went fine
return B_OK;
} else
status = B_NO_MEMORY;
} else
status = B_BAD_VALUE;
// question: why doesn't get_vnode() work here??
// answer: we have not yet backpropagated the pointer to the
// volume in bfs_mount(), so bfs_read_vnode() can't get it.
// But it's not needed to do that anyway.
FATAL(("could not create root node: new_vnode() failed!\n"));
} else {
// ToDo: improve error reporting for a bad journal
status = B_NO_MEMORY;
FATAL(("could not initialize journal/block bitmap allocator!\n"));
fIndicesNode = new Inode(this, ToVnode(Indices()));
if (fIndicesNode == NULL
|| fIndicesNode->InitCheck() < B_OK
|| !fIndicesNode->IsContainer()) {
INFORM(("bfs: volume doesn't have indices!\n"));
if (fIndicesNode) {
// if this is the case, the index root node is gone bad, and
// BFS switch to read-only mode
fFlags |= VOLUME_READ_ONLY;
fIndicesNode = NULL;
}
}
remove_cached_device_blocks(fDevice, NO_WRITES);
} else {
FATAL(("could not initialize cache!\n"));
status = B_IO_ERROR;
}
FATAL(("invalid super block!\n"));
}
else
// all went fine
opener.Keep();
return B_OK;
} else
status = B_NO_MEMORY;
} else
status = B_BAD_VALUE;
close(fDevice);
FATAL(("could not create root node: new_vnode() failed!\n"));
return status;
}
@ -249,8 +399,9 @@ Volume::ToBlockRun(off_t block) const
{
block_run run;
run.allocation_group = HOST_ENDIAN_TO_BFS_INT32(block >> AllocationGroupShift());
run.start = HOST_ENDIAN_TO_BFS_INT16(block & ~((1LL << AllocationGroupShift()) - 1));
run.start = HOST_ENDIAN_TO_BFS_INT16(block & ((1LL << AllocationGroupShift()) - 1));
run.length = HOST_ENDIAN_TO_BFS_INT16(1);
__out("block %Ld -> (%lu, %u) %ld (%lu)\n", block, run.AllocationGroup(), run.Start(), AllocationGroupShift(), ((1UL << AllocationGroupShift()) - 1));
return run;
}
@ -338,3 +489,95 @@ Volume::RemoveQuery(Query *query)
fQueryLock.Unlock();
}
// #pragma mark -
// Disk initialization
status_t
Volume::Initialize(const char *device, const char *name, uint32 blockSize, uint32 flags)
{
// although there is no really good reason for it, we won't
// accept '/' in disk names (mkbfs does this, too - and since
// Tracker names mounted volumes like their name)
if (strchr(name, '/') != NULL)
return B_BAD_VALUE;
if (blockSize != 1024 && blockSize != 2048 && blockSize != 4096 && blockSize != 8192)
return B_BAD_VALUE;
DeviceOpener opener(device, O_RDWR);
if (opener.Device() < B_OK)
return B_BAD_VALUE;
fDevice = opener.Device();
uint32 deviceBlockSize;
off_t deviceSize;
if (opener.GetSize(&deviceSize, &deviceBlockSize) < B_OK)
return B_ERROR;
off_t numBlocks = deviceSize / blockSize;
// create valid super block
fSuperBlock.Initialize(name, numBlocks, blockSize);
// initialize short hands to the super block (to save byte swapping)
fBlockSize = fSuperBlock.BlockSize();
fBlockShift = fSuperBlock.BlockShift();
fAllocationGroupShift = fSuperBlock.AllocationGroupShift();
// since the allocator has not been initialized yet, we
// cannot use BlockAllocator::BitmapSize() here
fSuperBlock.log_blocks = ToBlockRun(AllocationGroups()
* fSuperBlock.BlocksPerAllocationGroup() + 1);
fSuperBlock.log_blocks.length = 4096;
// ToDo: set the log size depending on the disk size
fSuperBlock.log_start = fSuperBlock.log_end = HOST_ENDIAN_TO_BFS_INT64(ToBlock(Log()));
// set the current log pointers, so that journaling will work correctly
fLogStart = fSuperBlock.LogStart();
fLogEnd = fSuperBlock.LogEnd();
if (!IsValidSuperBlock())
RETURN_ERROR(B_ERROR);
if (opener.InitCache(numBlocks) != B_OK)
return B_ERROR;
fJournal = new Journal(this);
if (fJournal == NULL || fJournal->InitCheck() < B_OK)
RETURN_ERROR(B_ERROR);
// ready to write data to disk
Transaction transaction(this, 0);
if (fBlockAllocator.InitializeAndClearBitmap(transaction) < B_OK)
RETURN_ERROR(B_ERROR);
off_t id;
status_t status = Inode::Create(&transaction, NULL, NULL,
S_DIRECTORY | 0777, 0, 0, &id, &fRootNode);
if (status < B_OK)
RETURN_ERROR(status);
fSuperBlock.root_dir = ToBlockRun(id);
if ((flags & VOLUME_NO_INDICES) == 0) {
if ((status = CreateIndicesRoot(&transaction)) < B_OK)
return status;
}
WriteSuperBlock();
transaction.Done();
put_vnode(ID(), fRootNode->ID());
if (fIndicesNode != NULL)
put_vnode(ID(), fIndicesNode->ID());
Sync();
opener.RemoveCache(ALLOW_WRITES);
return B_OK;
}

View File

@ -31,6 +31,9 @@ enum volume_flags {
VOLUME_READ_ONLY = 0x0001
};
enum volume_initialize_flags {
VOLUME_NO_INDICES = 0x0001,
};
class Volume {
public:
@ -39,6 +42,8 @@ class Volume {
status_t Mount(const char *device, uint32 flags);
status_t Unmount();
status_t Initialize(const char *device, const char *name,
uint32 blockSize, uint32 flags);
bool IsValidSuperBlock();
bool IsReadOnly() const;

View File

@ -88,6 +88,10 @@ struct disk_super_block {
int32 Flags() const { return BFS_ENDIAN_TO_HOST_INT32(flags); }
off_t LogStart() const { return BFS_ENDIAN_TO_HOST_INT64(log_start); }
off_t LogEnd() const { return BFS_ENDIAN_TO_HOST_INT64(log_end); }
// implemented in Volume.cpp:
bool IsValid();
void Initialize(const char *name, off_t numBlocks, uint32 blockSize);
} _PACKED;
#define SUPER_BLOCK_FS_LENDIAN 'BIGE' /* BIGE */