diff --git a/ext/blockgroup.cpp b/ext/blockgroup.cpp index 1c118bd8..028549a9 100644 --- a/ext/blockgroup.cpp +++ b/ext/blockgroup.cpp @@ -98,6 +98,7 @@ uint32_t BlockGroup::AllocateBlock() size_t num_bits = last ? num_blocks - chunk_offset : num_chunk_bits; // TODO: This can be made faster by caching if previous bits were set. for ( ; block_bitmap_chunk_i < num_bits; block_bitmap_chunk_i++ ) + { if ( !checkbit(chunk_bits, block_bitmap_chunk_i) ) { block_bitmap_chunk->BeginWrite(); @@ -113,6 +114,7 @@ uint32_t BlockGroup::AllocateBlock() uint32_t block_id = first_block_id + group_block_id; return block_id; } + } block_bitmap_chunk->Unref(); block_bitmap_chunk = NULL; } @@ -143,6 +145,7 @@ uint32_t BlockGroup::AllocateInode() size_t num_bits = last ? num_inodes - chunk_offset : num_chunk_bits; // TODO: This can be made faster by caching if previous bits were set. for ( ; inode_bitmap_chunk_i < num_bits; inode_bitmap_chunk_i++ ) + { if ( !checkbit(chunk_bits, inode_bitmap_chunk_i) ) { inode_bitmap_chunk->BeginWrite(); @@ -158,6 +161,7 @@ uint32_t BlockGroup::AllocateInode() uint32_t inode_id = first_inode_id + group_inode_id; return inode_id; } + } inode_bitmap_chunk->Unref(); inode_bitmap_chunk = NULL; } diff --git a/ext/blockgroup.h b/ext/blockgroup.h index e0699b36..e432371f 100644 --- a/ext/blockgroup.h +++ b/ext/blockgroup.h @@ -42,14 +42,14 @@ public: uint32_t group_id; uint32_t block_alloc_chunk; uint32_t inode_alloc_chunk; - uint32_t num_block_bitmap_chunks; - uint32_t num_inode_bitmap_chunks; uint32_t block_bitmap_chunk_i; uint32_t inode_bitmap_chunk_i; - uint32_t num_blocks; - uint32_t num_inodes; uint32_t first_block_id; uint32_t first_inode_id; + uint32_t num_blocks; + uint32_t num_inodes; + uint32_t num_block_bitmap_chunks; + uint32_t num_inode_bitmap_chunks; bool dirty; public: diff --git a/ext/device.cpp b/ext/device.cpp index 55d215ec..2b761dd9 100644 --- a/ext/device.cpp +++ b/ext/device.cpp @@ -42,23 +42,25 @@ void* Device__SyncThread(void* ctx) Device::Device(int fd, const char* path, uint32_t block_size, bool write) { - this->write = write; - this->fd = fd; - this->path = path; - this->block_size = block_size; - struct stat st; - fstat(fd, &st); - this->device_size = st.st_size; + // sync_thread unset. + this->sync_thread_cond = PTHREAD_COND_INITIALIZER; + this->sync_thread_idle_cond = PTHREAD_COND_INITIALIZER; + this->sync_thread_lock = PTHREAD_MUTEX_INITIALIZER; this->mru_block = NULL; this->lru_block = NULL; this->dirty_block = NULL; for ( size_t i = 0; i < DEVICE_HASH_LENGTH; i++ ) hash_blocks[i] = NULL; - this->sync_thread_cond = PTHREAD_COND_INITIALIZER; - this->sync_thread_idle_cond = PTHREAD_COND_INITIALIZER; - this->sync_thread_lock = PTHREAD_MUTEX_INITIALIZER; - this->sync_in_transit = false; + struct stat st; + fstat(fd, &st); + this->device_size = st.st_size; + this->path = path; + this->block_size = block_size; + this->fd = fd; + this->write = write; this->has_sync_thread = false; + this->sync_thread_should_exit = false; + this->sync_in_transit = false; } Device::~Device() diff --git a/ext/extfs.cpp b/ext/extfs.cpp index 624145ca..4ceeab3c 100644 --- a/ext/extfs.cpp +++ b/ext/extfs.cpp @@ -56,8 +56,6 @@ static const uint32_t EXT2_FEATURE_INCOMPAT_SUPPORTED = \ static const uint32_t EXT2_FEATURE_RO_COMPAT_SUPPORTED = \ EXT2_FEATURE_RO_COMPAT_LARGE_FILE; -// TODO: Inode 0 is not valid, but a lot of functions here accept it! - mode_t HostModeFromExtMode(uint32_t extmode) { mode_t hostmode = extmode & 0777; @@ -169,7 +167,7 @@ static void uuid_from_string(uint8_t uuid[16], const char* string) assert(string[i] == '\0'); } -void compact_arguments(int* argc, char*** argv) +static void compact_arguments(int* argc, char*** argv) { for ( int i = 0; i < *argc; i++ ) { @@ -182,12 +180,12 @@ void compact_arguments(int* argc, char*** argv) } } -void help(FILE* fp, const char* argv0) +static void help(FILE* fp, const char* argv0) { - fprintf(fp, "Usage: %s [--probe] [--test-uuid UUID] DEVICE [MOUNT-POINT]\n", argv0); + fprintf(fp, "Usage: %s [OPTION]... DEVICE [MOUNT-POINT]\n", argv0); } -void version(FILE* fp, const char* argv0) +static void version(FILE* fp, const char* argv0) { fprintf(fp, "%s (Sortix) %s\n", argv0, VERSIONSTR); fprintf(fp, "License GPLv3+: GNU GPL version 3 or later .\n"); @@ -206,7 +204,7 @@ int main(int argc, char* argv[]) for ( int i = 1; i < argc; i++ ) { const char* arg = argv[i]; - if ( arg[0] != '-' ) + if ( arg[0] != '-' || !arg[1] ) continue; argv[i] = NULL; if ( !strcmp(arg, "--") ) @@ -223,8 +221,12 @@ int main(int argc, char* argv[]) exit(1); } } - else if ( !strcmp(arg, "--help") ) { help(stdout, argv0); exit(0); } - else if ( !strcmp(arg, "--version") ) { version(stdout, argv0); exit(0); } + else if ( !strcmp(arg, "--help") ) + help(stdout, argv0), exit(0); + else if ( !strcmp(arg, "--version") ) + version(stdout, argv0), exit(0); + else if ( !strcmp(arg, "--background") ) + foreground = false; else if ( !strcmp(arg, "--foreground") ) foreground = true; else if ( !strcmp(arg, "--probe") ) @@ -265,7 +267,7 @@ int main(int argc, char* argv[]) compact_arguments(&argc, &argv); const char* device_path = 2 <= argc ? argv[1] : NULL; - const char* mount_path = 2 <= argc ? argv[2] : NULL; + const char* mount_path = 3 <= argc ? argv[2] : NULL; if ( !device_path ) { @@ -318,31 +320,33 @@ int main(int argc, char* argv[]) } // Test whether this revision of the extended filesystem is supported. - if ( probe && sb.s_rev_level == EXT2_GOOD_OLD_REV ) - exit(1); - - if ( !probe && sb.s_rev_level == EXT2_GOOD_OLD_REV ) + if ( sb.s_rev_level == EXT2_GOOD_OLD_REV ) + { + if ( probe ) + exit(1); error(1, 0, "`%s' is formatted with an obsolete filesystem revision", device_path); + } // Verify that no incompatible features are in use. - if ( probe && sb.s_feature_compat & ~EXT2_FEATURE_INCOMPAT_SUPPORTED ) - exit(1); - - if ( !probe && sb.s_feature_incompat & ~EXT2_FEATURE_INCOMPAT_SUPPORTED ) + if ( sb.s_feature_compat & ~EXT2_FEATURE_INCOMPAT_SUPPORTED ) + { + if ( probe ) + exit(1); error(1, 0, "`%s' uses unsupported and incompatible features", device_path); + } // Verify that no incompatible features are in use if opening for write. - if ( probe && default_access && write && + if ( !default_access && write && sb.s_feature_ro_compat & ~EXT2_FEATURE_RO_COMPAT_SUPPORTED ) - exit(1); - - if ( !probe && default_access && write && - sb.s_feature_ro_compat & ~EXT2_FEATURE_RO_COMPAT_SUPPORTED ) - error(1, 0, "`%s uses unsupported and incompatible features, " - "read-only access is possible, but write-access was " - "requested", device_path); + { + if ( probe ) + exit(1); + error(1, 0, "`%s' uses unsupported and incompatible features, " + "read-only access is possible, but write-access was " + "requested", device_path); + } if ( write && sb.s_feature_ro_compat & ~EXT2_FEATURE_RO_COMPAT_SUPPORTED ) { @@ -359,13 +363,21 @@ int main(int argc, char* argv[]) fprintf(stderr, "Note: filesystem uses unsupported but compatible " "features\n"); + // Check the block size is sane. 64 KiB may have issues, 32 KiB then. + if ( sb.s_log_block_size > (15-10) /* 32 KiB blocks */ ) + { + if ( probe ) + exit(1); + error(1, 0, "`%s': excess block size", device_path); + } + // We have found no critical problems, so let the caller know that this // filesystem satisfies the probe request. if ( probe ) exit(0); // Check whether the filesystem was unmounted cleanly. - if ( !probe && sb.s_state != EXT2_VALID_FS ) + if ( sb.s_state != EXT2_VALID_FS ) fprintf(stderr, "Warning: `%s' wasn't unmounted cleanly\n", device_path); diff --git a/ext/filesystem.cpp b/ext/filesystem.cpp index d6f68e85..0679d4d0 100644 --- a/ext/filesystem.cpp +++ b/ext/filesystem.cpp @@ -42,29 +42,29 @@ Filesystem::Filesystem(Device* device, const char* mount_path) { uint64_t sb_offset = 1024; uint32_t sb_block_id = sb_offset / device->block_size; - sb_block = device->GetBlock(sb_block_id); - sb = (struct ext_superblock*) - (sb_block->block_data + sb_offset % device->block_size); + this->sb_block = device->GetBlock(sb_block_id); + this->sb = (struct ext_superblock*) + (sb_block->block_data + sb_offset % device->block_size); this->device = device; - block_groups = NULL; + this->block_groups = NULL; this->mount_path = mount_path; - block_size = device->block_size; - mru_inode = NULL; - lru_inode = NULL; - dirty_inode = NULL; + this->block_size = device->block_size; + this->inode_size = this->sb->s_inode_size; + this->num_blocks = this->sb->s_blocks_count; + this->num_groups = divup(this->sb->s_blocks_count, this->sb->s_blocks_per_group); + this->num_inodes = this->sb->s_inodes_count; + this->mru_inode = NULL; + this->lru_inode = NULL; + this->dirty_inode = NULL; for ( size_t i = 0; i < INODE_HASH_LENGTH; i++ ) - hash_inodes[i] = NULL; - inode_size = this->sb->s_inode_size; - num_blocks = sb->s_blocks_count; - num_groups = divup(this->sb->s_blocks_count, this->sb->s_blocks_per_group); - num_inodes = this->sb->s_inodes_count; - dirty = false; - + this->hash_inodes[i] = NULL; struct timespec now_realtime, now_monotonic; clock_gettime(CLOCK_REALTIME, &now_realtime); clock_gettime(CLOCK_MONOTONIC, &now_monotonic); - mtime_realtime = now_realtime.tv_sec; - mtime_monotonic = now_monotonic.tv_sec; + this->mtime_realtime = now_realtime.tv_sec; + this->mtime_monotonic = now_monotonic.tv_sec; + this->dirty = false; + BeginWrite(); sb->s_mtime = mtime_realtime; sb->s_mnt_count++; @@ -133,7 +133,6 @@ BlockGroup* Filesystem::GetBlockGroup(uint32_t group_id) assert(group_id < num_groups); if ( block_groups[group_id] ) return block_groups[group_id]->Refer(), block_groups[group_id]; - BlockGroup* group = new BlockGroup(this, group_id); size_t group_size = sizeof(ext_blockgrpdesc); uint32_t first_block_id = sb->s_first_data_block + 1 /* superblock */; @@ -141,6 +140,7 @@ BlockGroup* Filesystem::GetBlockGroup(uint32_t group_id) uint32_t offset = (group_id * group_size) % block_size; Block* block = device->GetBlock(block_id); + BlockGroup* group = new BlockGroup(this, group_id); group->data_block = block; uint8_t* buf = group->data_block->block_data + offset; group->data = (struct ext_blockgrpdesc*) buf; @@ -149,16 +149,16 @@ BlockGroup* Filesystem::GetBlockGroup(uint32_t group_id) Inode* Filesystem::GetInode(uint32_t inode_id) { - assert(inode_id); - assert(inode_id < num_inodes); + if ( !inode_id || num_inodes <= inode_id ) + return errno = EBADF, (Inode*) NULL; + if ( !inode_id ) + return errno = EBADF, (Inode*) NULL; size_t bin = inode_id % INODE_HASH_LENGTH; for ( Inode* iter = hash_inodes[bin]; iter; iter = iter->next_hashed ) if ( iter->inode_id == inode_id ) return iter->Refer(), iter; - Inode* inode = new Inode(this, inode_id); - uint32_t group_id = (inode_id-1) / sb->s_inodes_per_group; uint32_t tabel_index = (inode_id-1) % sb->s_inodes_per_group; assert(group_id < num_groups); @@ -169,6 +169,7 @@ Inode* Filesystem::GetInode(uint32_t inode_id) uint32_t offset = (tabel_index * inode_size) % block_size; Block* block = device->GetBlock(block_id); + Inode* inode = new Inode(this, inode_id); inode->data_block = block; uint8_t* buf = inode->data_block->block_data + offset; inode->data = (struct ext_inode*) buf; @@ -194,7 +195,7 @@ uint32_t Filesystem::AllocateBlock(BlockGroup* preferred) // this can't happen. That also allows us to make the linked list // requested above. BeginWrite(); - sb->s_free_blocks_count--; + sb->s_free_blocks_count = 0; FinishWrite(); return errno = ENOSPC, 0; } @@ -216,13 +217,14 @@ uint32_t Filesystem::AllocateInode(BlockGroup* preferred) // this can't happen. That also allows us to make the linked list // requested above. BeginWrite(); - sb->s_free_inodes_count--; + sb->s_free_inodes_count = 0; FinishWrite(); return errno = ENOSPC, 0; } void Filesystem::FreeBlock(uint32_t block_id) { + assert(block_id); assert(block_id < num_blocks); uint32_t group_id = (block_id - sb->s_first_data_block) / sb->s_blocks_per_group; assert(group_id < num_groups); @@ -233,6 +235,7 @@ void Filesystem::FreeBlock(uint32_t block_id) void Filesystem::FreeInode(uint32_t inode_id) { + assert(inode_id); assert(inode_id < num_inodes); uint32_t group_id = (inode_id-1) / sb->s_inodes_per_group; assert(group_id < num_groups); diff --git a/ext/filesystem.h b/ext/filesystem.h index f6ced5d3..8432bced 100644 --- a/ext/filesystem.h +++ b/ext/filesystem.h @@ -36,8 +36,8 @@ public: ~Filesystem(); public: - struct ext_superblock* sb; Block* sb_block; + struct ext_superblock* sb; Device* device; BlockGroup** block_groups; const char* mount_path; diff --git a/ext/fsmarshall.cpp b/ext/fsmarshall.cpp index 69c317a8..677d6a93 100644 --- a/ext/fsmarshall.cpp +++ b/ext/fsmarshall.cpp @@ -68,7 +68,7 @@ bool RespondHeader(int chl, size_t type, size_t size) return RespondData(chl, &hdr, sizeof(hdr)); } -bool RespondMessage(int chl, unsigned int type, const void* ptr, size_t count) +bool RespondMessage(int chl, size_t type, const void* ptr, size_t count) { return RespondHeader(chl, type, count) && RespondData(chl, ptr, count); @@ -165,12 +165,18 @@ bool RespondTCGetBlob(int chl, const void* data, size_t data_size) RespondData(chl, data, data_size); } +Inode* SafeGetInode(Filesystem* fs, ino_t ino) +{ + if ( (uint32_t) ino != ino ) + return errno = EBADF, (Inode*) ino; + // TODO: Should check if the inode is not deleted. + return fs->GetInode((uint32_t) ino); +} + void HandleRefer(int chl, struct fsm_req_refer* msg, Filesystem* fs) { (void) chl; - if ( fs->num_inodes <= msg->ino ) - return; - if ( Inode* inode = fs->GetInode((uint32_t) msg->ino) ) + if ( Inode* inode = SafeGetInode(fs, (uint32_t) msg->ino) ) { inode->RemoteRefer(); inode->Unref(); @@ -180,9 +186,7 @@ void HandleRefer(int chl, struct fsm_req_refer* msg, Filesystem* fs) void HandleUnref(int chl, struct fsm_req_unref* msg, Filesystem* fs) { (void) chl; - if ( fs->num_inodes <= msg->ino ) - return; - if ( Inode* inode = fs->GetInode((uint32_t) msg->ino) ) + if ( Inode* inode = SafeGetInode(fs, (uint32_t) msg->ino) ) { inode->RemoteUnref(); inode->Unref(); @@ -191,8 +195,7 @@ void HandleUnref(int chl, struct fsm_req_unref* msg, Filesystem* fs) void HandleSync(int chl, struct fsm_req_sync* msg, Filesystem* fs) { - if ( fs->num_inodes <= msg->ino ) { RespondError(chl, EBADF); return; } - Inode* inode = fs->GetInode((uint32_t) msg->ino); + Inode* inode = SafeGetInode(fs, msg->ino); if ( !inode ) { RespondError(chl, errno); return; } inode->Sync(); inode->Unref(); @@ -201,8 +204,7 @@ void HandleSync(int chl, struct fsm_req_sync* msg, Filesystem* fs) void HandleStat(int chl, struct fsm_req_stat* msg, Filesystem* fs) { - if ( fs->num_inodes <= msg->ino ) { RespondError(chl, EBADF); return; } - Inode* inode = fs->GetInode((uint32_t) msg->ino); + Inode* inode = SafeGetInode(fs, msg->ino); if ( !inode ) { RespondError(chl, errno); return; } struct stat st; StatInode(inode, &st); @@ -212,8 +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->num_inodes <= msg->ino ) { RespondError(chl, EBADF); return; } - Inode* inode = fs->GetInode((uint32_t) msg->ino); + Inode* inode = SafeGetInode(fs, msg->ino); if ( !inode ) { RespondError(chl, errno); return; } uint32_t req_mode = ExtModeFromHostMode(msg->mode); uint32_t old_mode = inode->Mode(); @@ -225,8 +226,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->num_inodes <= msg->ino ) { RespondError(chl, EBADF); return; } - Inode* inode = fs->GetInode((uint32_t) msg->ino); + Inode* inode = SafeGetInode(fs, msg->ino); if ( !inode ) { RespondError(chl, errno); return; } inode->SetUserId((uint32_t) msg->uid); inode->SetGroupId((uint32_t) msg->gid); @@ -236,8 +236,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->num_inodes <= msg->ino ) { RespondError(chl, EBADF); return; } - Inode* inode = fs->GetInode((uint32_t) msg->ino); + Inode* inode = SafeGetInode(fs, msg->ino); if ( !inode ) { RespondError(chl, errno); return; } inode->BeginWrite(); inode->data->i_atime = msg->times[0].tv_sec; @@ -249,9 +248,8 @@ void HandleUTimens(int chl, struct fsm_req_utimens* msg, Filesystem* fs) void HandleTruncate(int chl, struct fsm_req_truncate* msg, Filesystem* fs) { - if( msg->size < 0 ) { RespondError(chl, EINVAL); return; } - if ( fs->num_inodes <= msg->ino ) { RespondError(chl, EBADF); return; } - Inode* inode = fs->GetInode((uint32_t) msg->ino); + if ( msg->size < 0 ) { RespondError(chl, EINVAL); return; } + Inode* inode = SafeGetInode(fs, msg->ino); if ( !inode ) { RespondError(chl, errno); return; } inode->Truncate((uint64_t) msg->size); inode->Unref(); @@ -260,8 +258,7 @@ void HandleTruncate(int chl, struct fsm_req_truncate* msg, Filesystem* fs) void HandleSeek(int chl, struct fsm_req_lseek* msg, Filesystem* fs) { - if ( fs->num_inodes <= msg->ino ) { RespondError(chl, EBADF); return; } - Inode* inode = fs->GetInode((uint32_t) msg->ino); + Inode* inode = SafeGetInode(fs, msg->ino); if ( !inode ) { RespondError(chl, errno); return; } if ( msg->whence == SEEK_SET ) RespondSeek(chl, msg->offset); @@ -281,8 +278,7 @@ void HandleSeek(int chl, struct fsm_req_lseek* msg, Filesystem* fs) void HandleReadAt(int chl, struct fsm_req_pread* msg, Filesystem* fs) { - if ( fs->num_inodes <= msg->ino ) { RespondError(chl, EBADF); return; } - Inode* inode = fs->GetInode((uint32_t) msg->ino); + Inode* inode = SafeGetInode(fs, msg->ino); if ( !inode ) { RespondError(chl, errno); return; } uint8_t* buf = (uint8_t*) malloc(msg->count); if ( !buf ) { inode->Unref(); RespondError(chl, errno); return; } @@ -295,8 +291,7 @@ void HandleReadAt(int chl, struct fsm_req_pread* msg, Filesystem* fs) void HandleWriteAt(int chl, struct fsm_req_pwrite* msg, Filesystem* fs) { - if ( fs->num_inodes <= msg->ino ) { RespondError(chl, EBADF); return; } - Inode* inode = fs->GetInode((uint32_t) msg->ino); + Inode* inode = SafeGetInode(fs, msg->ino); if ( !inode ) { RespondError(chl, errno); return; } const uint8_t* buf = (const uint8_t*) &msg[1]; ssize_t amount = inode->WriteAt(buf, msg->count, msg->offset); @@ -307,8 +302,7 @@ void HandleWriteAt(int chl, struct fsm_req_pwrite* msg, Filesystem* fs) void HandleOpen(int chl, struct fsm_req_open* msg, Filesystem* fs) { - if ( fs->num_inodes <= msg->dirino ) { RespondError(chl, EBADF); return; } - Inode* inode = fs->GetInode((uint32_t) msg->dirino); + Inode* inode = SafeGetInode(fs, msg->dirino); if ( !inode ) { RespondError(chl, errno); return; } char* pathraw = (char*) &(msg[1]); @@ -335,8 +329,7 @@ void HandleOpen(int chl, struct fsm_req_open* msg, Filesystem* fs) void HandleMakeDir(int chl, struct fsm_req_mkdir* msg, Filesystem* fs) { - if ( fs->num_inodes <= msg->dirino ) { RespondError(chl, EBADF); return; } - Inode* inode = fs->GetInode((uint32_t) msg->dirino); + Inode* inode = SafeGetInode(fs, msg->dirino); if ( !inode ) { RespondError(chl, errno); return; } char* pathraw = (char*) &(msg[1]); @@ -363,8 +356,7 @@ void HandleMakeDir(int chl, struct fsm_req_mkdir* msg, Filesystem* fs) void HandleReadDir(int chl, struct fsm_req_readdirents* msg, Filesystem* fs) { - if ( fs->num_inodes <= msg->ino ) { RespondError(chl, EBADF); return; } - Inode* inode = fs->GetInode((uint32_t) msg->ino); + Inode* inode = SafeGetInode(fs, msg->ino); if ( !inode ) { RespondError(chl, errno); return; } if ( !S_ISDIR(inode->Mode()) ) { @@ -426,8 +418,7 @@ void HandleReadDir(int chl, struct fsm_req_readdirents* msg, Filesystem* fs) void HandleIsATTY(int chl, struct fsm_req_isatty* msg, Filesystem* fs) { - if ( fs->num_inodes <= msg->ino ) { RespondError(chl, EBADF); return; } - Inode* inode = fs->GetInode((uint32_t) msg->ino); + Inode* inode = SafeGetInode(fs, msg->ino); if ( !inode ) { RespondError(chl, errno); return; } RespondError(chl, ENOTTY); inode->Unref(); @@ -435,8 +426,7 @@ void HandleIsATTY(int chl, struct fsm_req_isatty* msg, Filesystem* fs) void HandleUnlink(int chl, struct fsm_req_unlink* msg, Filesystem* fs) { - if ( fs->num_inodes <= msg->dirino ) { RespondError(chl, EBADF); return; } - Inode* inode = fs->GetInode((uint32_t) msg->dirino); + Inode* inode = SafeGetInode(fs, msg->dirino); if ( !inode ) { RespondError(chl, errno); return; } char* pathraw = (char*) &(msg[1]); @@ -461,8 +451,7 @@ void HandleUnlink(int chl, struct fsm_req_unlink* msg, Filesystem* fs) void HandleRemoveDir(int chl, struct fsm_req_rmdir* msg, Filesystem* fs) { - if ( fs->num_inodes <= msg->dirino ) { RespondError(chl, EBADF); return; } - Inode* inode = fs->GetInode((uint32_t) msg->dirino); + Inode* inode = SafeGetInode(fs, msg->dirino); if ( !inode ) { RespondError(chl, errno); return; } char* pathraw = (char*) &(msg[1]); @@ -476,22 +465,20 @@ void HandleRemoveDir(int chl, struct fsm_req_rmdir* msg, Filesystem* fs) memcpy(path, pathraw, msg->namelen); path[msg->namelen] = '\0'; - if ( inode->RemoveDirectory(path) ) - RespondSuccess(chl); - else - RespondError(chl, errno); - + bool result = inode->RemoveDirectory(path); free(path); inode->Unref(); + + if ( !result ) { RespondError(chl, errno); return; } + + RespondSuccess(chl); } void HandleLink(int chl, struct fsm_req_link* msg, Filesystem* fs) { - if ( fs->num_inodes <= msg->dirino ) { RespondError(chl, EBADF); return; } - if ( fs->num_inodes <= msg->linkino ) { RespondError(chl, EBADF); return; } - Inode* inode = fs->GetInode((uint32_t) msg->dirino); + Inode* inode = SafeGetInode(fs, msg->dirino); if ( !inode ) { RespondError(chl, errno); return; } - Inode* dest = fs->GetInode((uint32_t) msg->linkino); + Inode* dest = SafeGetInode(fs, msg->linkino); if ( !dest ) { inode->Unref(); RespondError(chl, errno); return; } char* pathraw = (char*) &(msg[1]); @@ -505,20 +492,20 @@ void HandleLink(int chl, struct fsm_req_link* msg, Filesystem* fs) memcpy(path, pathraw, msg->namelen); path[msg->namelen] = '\0'; - if ( inode->Link(path, dest, false) ) - RespondSuccess(chl); - else - RespondError(chl, errno); + bool result = inode->Link(path, dest, false); free(path); dest->Unref(); inode->Unref(); + + if ( !result ) { RespondError(chl, errno); return; } + + RespondSuccess(chl); } void HandleSymlink(int chl, struct fsm_req_symlink* msg, Filesystem* fs) { - if ( fs->num_inodes <= msg->dirino ) { RespondError(chl, EBADF); return; } - Inode* inode = fs->GetInode((uint32_t) msg->dirino); + Inode* inode = SafeGetInode(fs, msg->dirino); if ( !inode ) { RespondError(chl, errno); return; } char* dest_raw = (char*) &(msg[1]); @@ -544,20 +531,20 @@ void HandleSymlink(int chl, struct fsm_req_symlink* msg, Filesystem* fs) memcpy(path, path_raw, msg->namelen); path[msg->namelen] = '\0'; - if ( inode->Symlink(path, dest) ) - RespondSuccess(chl); - else - RespondError(chl, errno); + bool result = inode->Symlink(path, dest); free(path); free(dest); inode->Unref(); + + if ( !result ) { RespondError(chl, errno); return; } + + RespondSuccess(chl); } void HandleReadlink(int chl, struct fsm_req_readlink* msg, Filesystem* fs) { - if ( fs->num_inodes <= msg->ino ) { RespondError(chl, EBADF); return; } - Inode* inode = fs->GetInode((uint32_t) msg->ino); + Inode* inode = SafeGetInode(fs, msg->ino); if ( !inode ) { RespondError(chl, errno); return; } if ( !EXT2_S_ISLNK(inode->Mode()) ) { inode->Unref(); RespondError(chl, EINVAL); return; } size_t count = inode->Size(); @@ -572,9 +559,6 @@ void HandleReadlink(int chl, struct fsm_req_readlink* msg, Filesystem* fs) void HandleRename(int chl, struct fsm_req_rename* msg, Filesystem* fs) { - if ( fs->num_inodes <= msg->olddirino ) { RespondError(chl, EBADF); return; } - if ( fs->num_inodes <= msg->newdirino ) { RespondError(chl, EBADF); return; } - char* pathraw = (char*) &(msg[1]); char* path = (char*) malloc(msg->oldnamelen+1 + msg->newnamelen+1); if ( !path ) { RespondError(chl, errno); return; } @@ -586,19 +570,20 @@ void HandleRename(int chl, struct fsm_req_rename* msg, Filesystem* fs) const char* oldname = path; const char* newname = path + msg->oldnamelen + 1; - Inode* olddir = fs->GetInode((uint32_t) msg->olddirino); + Inode* olddir = SafeGetInode(fs, msg->olddirino); if ( !olddir ) { free(path); RespondError(chl, errno); return; } - Inode* newdir = fs->GetInode((uint32_t) msg->newdirino); + Inode* newdir = SafeGetInode(fs, msg->newdirino); if ( !newdir ) { olddir->Unref(); free(path); RespondError(chl, errno); return; } - if ( newdir->Rename(olddir, oldname, newname) ) - RespondSuccess(chl); - else - RespondError(chl, errno); + bool result = newdir->Rename(olddir, oldname, newname); newdir->Unref(); olddir->Unref(); free(path); + + if ( !result ) { RespondError(chl, errno); return; } + + RespondSuccess(chl); } void HandleStatVFS(int chl, struct fsm_req_statvfs* msg, Filesystem* fs) @@ -624,9 +609,6 @@ void HandleStatVFS(int chl, struct fsm_req_statvfs* msg, Filesystem* fs) void HandleTCGetBlob(int chl, struct fsm_req_tcgetblob* msg, Filesystem* fs) { - if ( fs->num_inodes <= msg->ino ) - return (void) RespondError(chl, EBADF); - char* nameraw = (char*) &(msg[1]); char* name = (char*) malloc(msg->namelen + 1); if ( !name ) @@ -765,14 +747,16 @@ int fsmarshall_main(const char* argv0, HandleIncomingMessage(channel, &hdr, fs); close(channel); - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - - if ( dev->write && !dev->has_sync_thread && - 5 <= timespec_sub(now, last_sync_at).tv_sec ) + if ( dev->write && !dev->has_sync_thread ) { - fs->Sync(); - last_sync_at = now; + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + + if ( 5 <= timespec_sub(now, last_sync_at).tv_sec ) + { + fs->Sync(); + last_sync_at = now; + } } } diff --git a/ext/fuse.cpp b/ext/fuse.cpp index 96cdf135..1582e73d 100644 --- a/ext/fuse.cpp +++ b/ext/fuse.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #define FUSE_USE_VERSION 26 @@ -126,7 +127,7 @@ Inode* ext2_fuse_parent_dir(const char** path_ptr) delete[] elem; inode->Unref(); if ( !next ) - return NULL; + return (Inode*) NULL; inode = next; } *path_ptr = *path ? path : "."; @@ -145,7 +146,7 @@ int ext2_fuse_getattr(const char* path, struct stat* st) } int ext2_fuse_fgetattr(const char* /*path*/, struct stat* st, - struct fuse_file_info* fi) + struct fuse_file_info* fi) { Filesystem* fs = FUSE_FS; Inode* inode = fs->GetInode((uint32_t) fi->fh); @@ -199,11 +200,9 @@ int ext2_fuse_unlink(const char* path) Inode* inode = ext2_fuse_parent_dir(&path); if ( !inode ) return -errno; - bool result = inode->Unlink(path, false); + bool success = inode->Unlink(path, false); inode->Unref(); - if ( !result ) - return -errno; - return 0; + return success ? 0 : -errno; } int ext2_fuse_rmdir(const char* path) diff --git a/ext/inode.cpp b/ext/inode.cpp index 5e9c8dca..7fb6a7d4 100644 --- a/ext/inode.cpp +++ b/ext/inode.cpp @@ -48,6 +48,9 @@ #ifndef S_SETABLE #define S_SETABLE 02777 #endif +#ifndef O_WRITE +#define O_WRITE (O_WRONLY | O_RDWR) +#endif Inode::Inode(Filesystem* filesystem, uint32_t inode_id) { @@ -163,20 +166,6 @@ void Inode::SetSize(uint64_t new_size) Modified(); } -void Inode::Linked() -{ - BeginWrite(); - data->i_links_count++; - FinishWrite(); -} - -void Inode::Unlinked() -{ - BeginWrite(); - data->i_links_count--; - FinishWrite(); -} - Block* Inode::GetBlockFromTable(Block* table, uint32_t index) { if ( uint32_t block_id = ((uint32_t*) table->block_data)[index] ) @@ -406,6 +395,8 @@ Inode* Inode::Open(const char* elem, int flags, mode_t mode) if ( !EXT2_S_ISDIR(Mode()) ) return errno = ENOTDIR, (Inode*) NULL; size_t elem_length = strlen(elem); + if ( elem_length == 0 ) + return errno = ENOENT, (Inode*) NULL; uint64_t filesize = Size(); uint64_t offset = 0; Block* block = NULL; @@ -421,13 +412,12 @@ Inode* Inode::Open(const char* elem, int flags, mode_t mode) return NULL; const uint8_t* block_data = block->block_data + entry_block_offset; const struct ext_dirent* entry = (const struct ext_dirent*) block_data; - if ( entry->name_len == elem_length && - memcmp(elem, entry->name, elem_length) == 0 && - entry->inode ) + if ( entry->inode && + entry->name_len == elem_length && + memcmp(elem, entry->name, elem_length) == 0 ) { uint8_t file_type = entry->file_type; uint32_t inode_id = entry->inode; - assert(inode_id); block->Unref(); if ( (flags & O_CREAT) && (flags & O_EXCL) ) return errno = EEXIST, (Inode*) NULL; @@ -444,7 +434,7 @@ Inode* Inode::Open(const char* elem, int flags, mode_t mode) inode->Unref(); return errno = ENOTDIR, (Inode*) NULL; } - if ( S_ISREG(inode->Mode()) && flags & O_TRUNC ) + if ( S_ISREG(inode->Mode()) && flags & O_WRITE && flags & O_TRUNC ) inode->Truncate(0); return inode; } @@ -469,10 +459,7 @@ Inode* Inode::Open(const char* elem, int flags, mode_t mode) // TODO: Set all the other inode properties! if ( !Link(elem, result, false) ) { - memset(result->data, 0, sizeof(*result->data)); - // TODO: dtime result->Unref(); - filesystem->FreeInode(result_inode_id); return NULL; } return result; @@ -482,8 +469,6 @@ Inode* Inode::Open(const char* elem, int flags, mode_t mode) bool Inode::Link(const char* elem, Inode* dest, bool directories) { - // TODO: Check if dest has checked the link limit! - if ( !EXT2_S_ISDIR(Mode()) ) return errno = ENOTDIR, false; if ( directories && !EXT2_S_ISDIR(dest->Mode()) ) @@ -496,6 +481,8 @@ bool Inode::Link(const char* elem, Inode* dest, bool directories) // 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); + if ( elem_length == 0 ) + return errno = ENOENT, false; size_t new_entry_size = roundup(sizeof(struct ext_dirent) + elem_length, (size_t) 4); uint64_t filesize = Size(); uint64_t offset = 0; @@ -517,8 +504,7 @@ bool Inode::Link(const char* elem, Inode* dest, bool directories) const uint8_t* block_data = block->block_data + entry_block_offset; const struct ext_dirent* entry = (const struct ext_dirent*) block_data; if ( entry->name_len == elem_length && - memcmp(elem, entry->name, elem_length) == 0 && - entry->inode ) + memcmp(elem, entry->name, elem_length) == 0 ) { block->Unref(); return errno = EEXIST, false; @@ -526,7 +512,7 @@ bool Inode::Link(const char* elem, Inode* dest, bool directories) if ( !found_hole ) { size_t entry_size = roundup(sizeof(struct ext_dirent) + entry->name_len, (size_t) 4); - if ( (!entry->name[0] || !entry->inode) && new_entry_size <= entry->reclen ) + if ( (!entry->inode || !entry->name[0]) && new_entry_size <= entry->reclen ) { hole_block_id = entry_block_id; hole_block_offset = entry_block_offset; @@ -545,6 +531,12 @@ bool Inode::Link(const char* elem, Inode* dest, bool directories) offset += entry->reclen; } + if ( UINT16_MAX <= dest->data->i_links_count ) + return errno = EMLINK, false; + + if ( 255 < elem_length ) + return errno = ENAMETOOLONG, false; + // We'll append another block if we failed to find a suitable hole. if ( !found_hole ) { @@ -576,23 +568,23 @@ bool Inode::Link(const char* elem, Inode* dest, bool directories) // Write the new directory entry. entry->inode = dest->inode_id; entry->reclen = new_entry_size; - entry->name_len = elem_length; - // TODO: Only do this if the filetype feature is on! - entry->file_type = EXT2_FT_OF_MODE(dest->Mode()); + if ( filesystem->sb->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE ) + entry->file_type = EXT2_FT_OF_MODE(dest->Mode()); + else + entry->file_type = 0; strncpy(entry->name, elem, new_entry_size - sizeof(struct ext_dirent)); - assert(entry->reclen); - block->FinishWrite(); + block->Unref(); - dest->Linked(); + dest->BeginWrite(); + dest->data->i_links_count++; + dest->FinishWrite(); if ( !found_hole ) SetSize(Size() + filesystem->block_size); - block->Unref(); - return true; } @@ -602,6 +594,8 @@ Inode* Inode::UnlinkKeep(const char* elem, bool directories, bool force) return errno = ENOTDIR, (Inode*) NULL; Modified(); size_t elem_length = strlen(elem); + if ( elem_length == 0 ) + return errno = ENOENT, (Inode*) NULL; uint32_t block_size = filesystem->block_size; uint64_t filesize = Size(); uint64_t num_blocks = divup(filesize, (uint64_t) block_size); @@ -621,10 +615,9 @@ Inode* Inode::UnlinkKeep(const char* elem, bool directories, bool force) return NULL; uint8_t* block_data = block->block_data + entry_block_offset; struct ext_dirent* entry = (struct ext_dirent*) block_data; - assert(entry->reclen); - if ( entry->name_len == elem_length && - memcmp(elem, entry->name, elem_length) == 0 && - entry->inode ) + if ( entry->inode && + entry->name_len == elem_length && + memcmp(elem, entry->name, elem_length) == 0 ) { Inode* inode = filesystem->GetInode(entry->inode); @@ -649,7 +642,9 @@ Inode* Inode::UnlinkKeep(const char* elem, bool directories, bool force) return errno = EISDIR, (Inode*) NULL; } - inode->Unlinked(); + inode->BeginWrite(); + inode->data->i_links_count--; + inode->FinishWrite(); block->BeginWrite(); @@ -660,17 +655,13 @@ Inode* Inode::UnlinkKeep(const char* elem, bool directories, bool force) // Merge the current entry with the previous if any. if ( last_entry ) { - assert(entry->reclen); last_entry->reclen += entry->reclen; memset(entry, 0, entry->reclen); entry = last_entry; - assert(last_entry->reclen); } - assert(entry->reclen); strncpy(entry->name + entry->name_len, "", entry->reclen - sizeof(struct ext_dirent) - entry->name_len); - assert(entry->reclen); // If the entire block is empty, we'll need to remove it. if ( !entry->name[0] && entry->reclen == block_size ) @@ -788,14 +779,14 @@ ssize_t Inode::WriteAt(const uint8_t* buf, size_t s_count, off_t o_offset) uint32_t block_left = filesystem->block_size - block_offset; Block* block = GetBlock(block_id); if ( !block ) - return sofar ? sofar : -1; + return sofar ? (ssize_t) sofar : -1; size_t amount = count - sofar < block_left ? count - sofar : block_left; block->BeginWrite(); memcpy(block->block_data + block_offset, buf + sofar, amount); block->FinishWrite(); + block->Unref(); sofar += amount; offset += amount; - block->Unref(); } return (ssize_t) sofar; } @@ -886,20 +877,10 @@ bool Inode::Symlink(const char* elem, const char* dest) if ( SSIZE_MAX < dest_length ) return errno = EFBIG, -1; if ( result->WriteAt((const uint8_t*) dest, dest_length, 0) < (ssize_t) dest_length ) - { - error: - memset(result->data, 0, sizeof(*result->data)); - // TODO: dtime - result->Unref(); - filesystem->FreeInode(result_inode_id); - return false; - } + return result->Unref(), false; if ( !Link(elem, result, false) ) - { - result->Truncate(0); - goto error; - } + return result->Truncate(0), result->Unref(), false; result->Unref(); return true; @@ -933,23 +914,19 @@ Inode* Inode::CreateDirectory(const char* path, mode_t mode) // TODO: Set all the other inode properties! if ( !Link(path, result, true) ) - { - error: - result->Unref(); - return NULL; - } + return result->Unref(), (Inode*) NULL; if ( !result->Link(".", result, true) ) { Unlink(path, true, true); - goto error; + return result->Unref(), (Inode*) NULL; } if ( !result->Link("..", this, true) ) { result->Unlink(".", true, true); Unlink(path, true, true); - goto error; + return result->Unref(), (Inode*) NULL; } return result; diff --git a/ext/inode.h b/ext/inode.h index 4ca09a8b..d186f0fd 100644 --- a/ext/inode.h +++ b/ext/inode.h @@ -84,8 +84,6 @@ public: void Use(); void Unlink(); void Prelink(); - void Linked(); - void Unlinked(); void Delete(); };