From 315413375486b54fbf4ec9569a5c4c8af7c63411 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Mon, 26 Jan 2004 02:44:27 +0000 Subject: [PATCH] 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 --- .../kernel/file_systems/bfs/Volume.cpp | 397 ++++++++++++++---- src/add-ons/kernel/file_systems/bfs/Volume.h | 5 + src/add-ons/kernel/file_systems/bfs/bfs.h | 4 + 3 files changed, 329 insertions(+), 77 deletions(-) diff --git a/src/add-ons/kernel/file_systems/bfs/Volume.cpp b/src/add-ons/kernel/file_systems/bfs/Volume.cpp index bfe9ec3a30..d9488d96d1 100644 --- a/src/add-ons/kernel/file_systems/bfs/Volume.cpp +++ b/src/add-ons/kernel/file_systems/bfs/Volume.cpp @@ -20,11 +20,176 @@ #include #include + +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; +} diff --git a/src/add-ons/kernel/file_systems/bfs/Volume.h b/src/add-ons/kernel/file_systems/bfs/Volume.h index 43a28d2ae1..bf34ede1c5 100644 --- a/src/add-ons/kernel/file_systems/bfs/Volume.h +++ b/src/add-ons/kernel/file_systems/bfs/Volume.h @@ -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; diff --git a/src/add-ons/kernel/file_systems/bfs/bfs.h b/src/add-ons/kernel/file_systems/bfs/bfs.h index 4da13527a7..2e0cc3a875 100644 --- a/src/add-ons/kernel/file_systems/bfs/bfs.h +++ b/src/add-ons/kernel/file_systems/bfs/bfs.h @@ -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 */