Fix extfs read-only support.

This commit is contained in:
Jonas 'Sortie' Termansen 2015-07-09 18:15:51 +02:00
parent e2202b2ddb
commit 41d4dbdce7
7 changed files with 82 additions and 14 deletions

View File

@ -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;

View File

@ -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();

View File

@ -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.

View File

@ -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;

View File

@ -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; }

View File

@ -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;

View File

@ -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 )