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:
parent
d7db210882
commit
3154133754
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user