From 41d4dbdce778d1f4264803fb30b41724003d8e89 Mon Sep 17 00:00:00 2001 From: Jonas 'Sortie' Termansen Date: Thu, 9 Jul 2015 18:15:51 +0200 Subject: [PATCH] Fix extfs read-only support. --- ext/blockgroup.cpp | 6 ++++++ ext/device.cpp | 1 + ext/extfs.cpp | 2 +- ext/filesystem.cpp | 32 ++++++++++++++++++++++---------- ext/fsmarshall.cpp | 4 ++++ ext/fuse.cpp | 10 ++++++++++ ext/inode.cpp | 41 ++++++++++++++++++++++++++++++++++++++--- 7 files changed, 82 insertions(+), 14 deletions(-) diff --git a/ext/blockgroup.cpp b/ext/blockgroup.cpp index e073427e..dbaae592 100644 --- a/ext/blockgroup.cpp +++ b/ext/blockgroup.cpp @@ -79,6 +79,8 @@ BlockGroup::~BlockGroup() uint32_t BlockGroup::AllocateBlock() { + if ( !filesystem->device->write ) + return errno = EROFS, 0; if ( !data->bg_free_blocks_count ) return errno = ENOSPC, 0; size_t num_chunk_bits = filesystem->block_size * 8UL; @@ -128,6 +130,8 @@ uint32_t BlockGroup::AllocateBlock() uint32_t BlockGroup::AllocateInode() { + if ( !filesystem->device->write ) + return errno = EROFS, 0; if ( !data->bg_free_inodes_count ) return errno = ENOSPC, 0; size_t num_chunk_bits = filesystem->block_size * 8UL; @@ -177,6 +181,7 @@ uint32_t BlockGroup::AllocateInode() void BlockGroup::FreeBlock(uint32_t block_id) { + assert(filesystem->device->write); block_id -= first_block_id; size_t num_chunk_bits = filesystem->block_size * 8UL; uint32_t chunk_id = block_id / num_chunk_bits; @@ -207,6 +212,7 @@ void BlockGroup::FreeBlock(uint32_t block_id) void BlockGroup::FreeInode(uint32_t inode_id) { + assert(filesystem->device->write); inode_id -= first_inode_id; size_t num_chunk_bits = filesystem->block_size * 8UL; uint32_t chunk_id = inode_id / num_chunk_bits; diff --git a/ext/device.cpp b/ext/device.cpp index 4cc44eed..e85b81c4 100644 --- a/ext/device.cpp +++ b/ext/device.cpp @@ -107,6 +107,7 @@ Block* Device::GetBlock(uint32_t block_id) Block* Device::GetBlockZeroed(uint32_t block_id) { + assert(write); if ( Block* block = GetCachedBlock(block_id) ) { block->BeginWrite(); diff --git a/ext/extfs.cpp b/ext/extfs.cpp index 9abb0453..868fb3e5 100644 --- a/ext/extfs.cpp +++ b/ext/extfs.cpp @@ -355,7 +355,7 @@ int main(int argc, char* argv[]) "features, falling back to read-only access\n", device_path); // TODO: Modify the file descriptor such that writing fails! - read = false; + read = true; } // Check whether any features are in use that we can safely disregard. diff --git a/ext/filesystem.cpp b/ext/filesystem.cpp index b447dffe..f8cf969b 100644 --- a/ext/filesystem.cpp +++ b/ext/filesystem.cpp @@ -66,12 +66,15 @@ Filesystem::Filesystem(Device* device, const char* mount_path) this->mtime_monotonic = now_monotonic.tv_sec; this->dirty = false; - BeginWrite(); - sb->s_mtime = mtime_realtime; - sb->s_mnt_count++; - sb->s_state = EXT2_ERROR_FS; - FinishWrite(); - Sync(); + if ( device->write ) + { + BeginWrite(); + sb->s_mtime = mtime_realtime; + sb->s_mnt_count++; + sb->s_state = EXT2_ERROR_FS; + FinishWrite(); + Sync(); + } } Filesystem::~Filesystem() @@ -82,10 +85,13 @@ Filesystem::~Filesystem() for ( size_t i = 0; i < num_groups; i++ ) delete block_groups[i]; delete[] block_groups; - BeginWrite(); - sb->s_state = EXT2_VALID_FS; - FinishWrite(); - Sync(); + if ( device->write ) + { + BeginWrite(); + sb->s_state = EXT2_VALID_FS; + FinishWrite(); + Sync(); + } sb_block->Unref(); } @@ -191,6 +197,8 @@ Inode* Filesystem::GetInode(uint32_t inode_id) uint32_t Filesystem::AllocateBlock(BlockGroup* preferred) { + if ( !device->write ) + return errno = EROFS, 0; if ( !sb->s_free_blocks_count ) return errno = ENOSPC, 0; if ( preferred ) @@ -213,6 +221,8 @@ uint32_t Filesystem::AllocateBlock(BlockGroup* preferred) uint32_t Filesystem::AllocateInode(BlockGroup* preferred) { + if ( !device->write ) + return errno = EROFS, 0; if ( !sb->s_free_inodes_count ) return errno = ENOSPC, 0; if ( preferred ) @@ -235,6 +245,7 @@ uint32_t Filesystem::AllocateInode(BlockGroup* preferred) void Filesystem::FreeBlock(uint32_t block_id) { + assert(device->write); assert(block_id); assert(block_id < num_blocks); uint32_t group_id = (block_id - sb->s_first_data_block) / sb->s_blocks_per_group; @@ -248,6 +259,7 @@ void Filesystem::FreeBlock(uint32_t block_id) void Filesystem::FreeInode(uint32_t inode_id) { + assert(device->write); assert(inode_id); assert(inode_id < num_inodes); uint32_t group_id = (inode_id-1) / sb->s_inodes_per_group; diff --git a/ext/fsmarshall.cpp b/ext/fsmarshall.cpp index 677d6a93..34241243 100644 --- a/ext/fsmarshall.cpp +++ b/ext/fsmarshall.cpp @@ -214,6 +214,7 @@ void HandleStat(int chl, struct fsm_req_stat* msg, Filesystem* fs) void HandleChangeMode(int chl, struct fsm_req_chmod* msg, Filesystem* fs) { + if ( !fs->device->write ) { RespondError(chl, EROFS); return; } Inode* inode = SafeGetInode(fs, msg->ino); if ( !inode ) { RespondError(chl, errno); return; } uint32_t req_mode = ExtModeFromHostMode(msg->mode); @@ -226,6 +227,7 @@ void HandleChangeMode(int chl, struct fsm_req_chmod* msg, Filesystem* fs) void HandleChangeOwner(int chl, struct fsm_req_chown* msg, Filesystem* fs) { + if ( !fs->device->write ) { RespondError(chl, EROFS); return; } Inode* inode = SafeGetInode(fs, msg->ino); if ( !inode ) { RespondError(chl, errno); return; } inode->SetUserId((uint32_t) msg->uid); @@ -236,6 +238,7 @@ void HandleChangeOwner(int chl, struct fsm_req_chown* msg, Filesystem* fs) void HandleUTimens(int chl, struct fsm_req_utimens* msg, Filesystem* fs) { + if ( !fs->device->write ) { RespondError(chl, EROFS); return; } Inode* inode = SafeGetInode(fs, msg->ino); if ( !inode ) { RespondError(chl, errno); return; } inode->BeginWrite(); @@ -248,6 +251,7 @@ void HandleUTimens(int chl, struct fsm_req_utimens* msg, Filesystem* fs) void HandleTruncate(int chl, struct fsm_req_truncate* msg, Filesystem* fs) { + if ( !fs->device->write ) { RespondError(chl, EROFS); return; } if ( msg->size < 0 ) { RespondError(chl, EINVAL); return; } Inode* inode = SafeGetInode(fs, msg->ino); if ( !inode ) { RespondError(chl, errno); return; } diff --git a/ext/fuse.cpp b/ext/fuse.cpp index dd6f70cd..51a9a825 100644 --- a/ext/fuse.cpp +++ b/ext/fuse.cpp @@ -260,6 +260,8 @@ int ext2_fuse_chmod(const char* path, mode_t mode) Inode* inode = ext2_fuse_resolve_path(path); if ( !inode ) return -errno; + if ( !FUSE_FS->device->write ) + return inode->Unref(), -(errno = EROFS); uint32_t req_mode = ExtModeFromHostMode(mode); uint32_t old_mode = inode->Mode(); uint32_t new_mode = (old_mode & ~S_SETABLE) | (req_mode & S_SETABLE); @@ -273,6 +275,8 @@ int ext2_fuse_chown(const char* path, uid_t owner, gid_t group) Inode* inode = ext2_fuse_resolve_path(path); if ( !inode ) return -errno; + if ( !FUSE_FS->device->write ) + return inode->Unref(), -(errno = EROFS); inode->SetUserId((uint32_t) owner); inode->SetGroupId((uint32_t) group); inode->Unref(); @@ -284,6 +288,8 @@ int ext2_fuse_truncate(const char* path, off_t size) Inode* inode = ext2_fuse_resolve_path(path); if ( !inode ) return -errno; + if ( !FUSE_FS->device->write ) + return inode->Unref(), -(errno = EROFS); inode->Truncate((uint64_t) size); inode->Unref(); return 0; @@ -296,6 +302,8 @@ int ext2_fuse_ftruncate(const char* /*path*/, off_t size, Inode* inode = fs->GetInode((uint32_t) fi->fh); if ( !inode ) return -errno; + if ( !FUSE_FS->device->write ) + return inode->Unref(), -(errno = EROFS); inode->Truncate((uint64_t) size); inode->Unref(); return 0; @@ -526,6 +534,8 @@ int ext2_fuse_utimens(const char* path, const struct timespec tv[2]) Inode* inode = ext2_fuse_resolve_path(path); if ( !inode ) return -errno; + if ( !FUSE_FS->device->write ) + return inode->Unref(), -(errno = EROFS); inode->BeginWrite(); inode->data->i_atime = tv[0].tv_sec; inode->data->i_mtime = tv[1].tv_sec; diff --git a/ext/inode.cpp b/ext/inode.cpp index c8037e36..f3cd6166 100644 --- a/ext/inode.cpp +++ b/ext/inode.cpp @@ -85,6 +85,7 @@ uint32_t Inode::Mode() void Inode::SetMode(uint32_t mode) { + assert(filesystem->device->write); BeginWrite(); // TODO: Use i_mode_high. data->i_mode = (uint16_t) mode; @@ -99,6 +100,7 @@ uint32_t Inode::UserId() void Inode::SetUserId(uint32_t user) { + assert(filesystem->device->write); BeginWrite(); // TODO: Use i_uid_high. data->i_uid = (uint16_t) user; @@ -113,6 +115,7 @@ uint32_t Inode::GroupId() void Inode::SetGroupId(uint32_t group) { + assert(filesystem->device->write); BeginWrite(); // TODO: Use i_gid_high. data->i_gid = (uint16_t) group; @@ -133,6 +136,7 @@ uint64_t Inode::Size() void Inode::SetSize(uint64_t new_size) { + assert(filesystem->device->write); bool largefile = filesystem->sb->s_feature_ro_compat & EXT2_FEATURE_RO_COMPAT_LARGE_FILE; uint32_t lower = new_size & 0xFFFFFFFF; @@ -170,6 +174,9 @@ Block* Inode::GetBlockFromTable(Block* table, uint32_t index) { if ( uint32_t block_id = ((uint32_t*) table->block_data)[index] ) return filesystem->device->GetBlock(block_id); + // TODO: If in read only mode, then perhaps return a zero block here. + if ( !filesystem->device->write ) + return NULL; uint32_t group_id = (inode_id - 1) / filesystem->sb->s_inodes_per_group; assert(group_id < filesystem->num_groups); BlockGroup* block_group = filesystem->GetBlockGroup(group_id); @@ -270,6 +277,7 @@ Block* Inode::GetBlock(uint64_t offset) bool Inode::FreeIndirect(uint64_t from, uint64_t offset, uint32_t block_id, int indirection, uint64_t entry_span) { + assert(filesystem->device->write); const uint64_t ENTRIES = filesystem->block_size / sizeof(uint32_t); Block* block = filesystem->device->GetBlock(block_id); uint32_t* table = (uint32_t*) block->block_data; @@ -303,6 +311,7 @@ bool Inode::FreeIndirect(uint64_t from, uint64_t offset, uint32_t block_id, void Inode::Truncate(uint64_t new_size) { + assert(filesystem->device->write); uint64_t old_size = Size(); bool could_be_embedded = old_size == 0 && EXT2_S_ISLNK(Mode()) && !data->i_blocks; bool is_embedded = 0 < old_size && old_size <= 60 && !data->i_blocks; @@ -436,6 +445,11 @@ Inode* Inode::Open(const char* elem, int flags, mode_t mode) inode->Unref(); return errno = ENOTDIR, (Inode*) NULL; } + if ( flags & O_WRITE && !filesystem->device->write ) + { + inode->Unref(); + return errno = EROFS, (Inode*) NULL; + } if ( S_ISREG(inode->Mode()) && flags & O_WRITE && flags & O_TRUNC ) inode->Truncate(0); return inode; @@ -446,6 +460,8 @@ Inode* Inode::Open(const char* elem, int flags, mode_t mode) block->Unref(); if ( flags & O_CREAT ) { + if ( !filesystem->device->write ) + return errno = EROFS, (Inode*) NULL; // TODO: Preferred block group! uint32_t result_inode_id = filesystem->AllocateInode(); if ( !result_inode_id ) @@ -480,8 +496,6 @@ bool Inode::Link(const char* elem, Inode* dest, bool directories) if ( !directories && EXT2_S_ISDIR(dest->Mode()) ) return errno = EISDIR, false; - Modified(); - // Search for a hole in which we can store the new directory entry and stop // if we meet an existing link with the requested name. size_t elem_length = strlen(elem); @@ -535,6 +549,9 @@ bool Inode::Link(const char* elem, Inode* dest, bool directories) offset += entry->reclen; } + if ( !filesystem->device->write ) + return errno = EROFS, false; + if ( UINT16_MAX <= dest->data->i_links_count ) return errno = EMLINK, false; @@ -555,6 +572,8 @@ bool Inode::Link(const char* elem, Inode* dest, bool directories) if ( !block && !(block = GetBlock(block_id = hole_block_id)) ) return NULL; + Modified(); + block->BeginWrite(); uint8_t* block_data = block->block_data + hole_block_offset; @@ -596,7 +615,6 @@ Inode* Inode::UnlinkKeep(const char* elem, bool directories, bool force) { if ( !EXT2_S_ISDIR(Mode()) ) return errno = ENOTDIR, (Inode*) NULL; - Modified(); size_t elem_length = strlen(elem); if ( elem_length == 0 ) return errno = ENOENT, (Inode*) NULL; @@ -646,6 +664,15 @@ Inode* Inode::UnlinkKeep(const char* elem, bool directories, bool force) return errno = EISDIR, (Inode*) NULL; } + if ( !filesystem->device->write ) + { + inode->Unref(); + block->Unref(); + return errno = EROFS, (Inode*) NULL; + } + + Modified(); + inode->BeginWrite(); inode->data->i_links_count--; inode->FinishWrite(); @@ -761,6 +788,8 @@ ssize_t Inode::WriteAt(const uint8_t* buf, size_t s_count, off_t o_offset) return errno = EISDIR, -1; if ( o_offset < 0 ) return errno = EINVAL, -1; + if ( !filesystem->device->write ) + return errno = EROFS, -1; if ( SSIZE_MAX < s_count ) s_count = SSIZE_MAX; Modified(); @@ -868,6 +897,9 @@ bool Inode::Rename(Inode* olddir, const char* oldname, const char* newname) bool Inode::Symlink(const char* elem, const char* dest) { + if ( !filesystem->device->write ) + return errno = EROFS, false; + // TODO: Preferred block group! uint32_t result_inode_id = filesystem->AllocateInode(); if ( !result_inode_id ) @@ -901,6 +933,9 @@ bool Inode::Symlink(const char* elem, const char* dest) Inode* Inode::CreateDirectory(const char* path, mode_t mode) { + if ( !filesystem->device->write ) + return errno = EROFS, (Inode*) NULL; + // TODO: Preferred block group! uint32_t result_inode_id = filesystem->AllocateInode(); if ( !result_inode_id )