From: Lawrence S. <ljs...@us...> - 2013-04-07 02:01:55
|
This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "A pseudo Operating System for the Dreamcast.". The branch, master has been updated via 186e9f53b0f6c951c69252a228f816255eaa2074 (commit) from e29e15ea9864b2a6fff2df5d8f75d4ac6c590468 (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- commit 186e9f53b0f6c951c69252a228f816255eaa2074 Author: Lawrence Sebald <ljs...@us...> Date: Sat Apr 6 22:00:39 2013 -0400 Add a function to sync the filesystem back to the block device. At the moment, there's no other write support, so this doesn't really do much right now. ----------------------------------------------------------------------- Summary of changes: addons/libkosext2fs/block.c | 36 +++++++ addons/libkosext2fs/block.h | 1 + addons/libkosext2fs/ext2fs.c | 95 +++++++++++++++++-- addons/libkosext2fs/ext2fs.h | 9 ++- addons/libkosext2fs/ext2internal.h | 13 +++- addons/libkosext2fs/inode.c | 185 ++++++++++++++++++++++++++++++++++- addons/libkosext2fs/inode.h | 10 ++ addons/libkosext2fs/superblock.c | 63 ++++++++++++- addons/libkosext2fs/superblock.h | 21 ++++ addons/libkosext2fs/utils.h | 18 +++- 10 files changed, 429 insertions(+), 22 deletions(-) diff --git a/addons/libkosext2fs/block.c b/addons/libkosext2fs/block.c index 03c7262..399481e 100644 --- a/addons/libkosext2fs/block.c +++ b/addons/libkosext2fs/block.c @@ -46,3 +46,39 @@ int ext2_read_blockgroups(ext2_fs_t *fs, uint32_t start_block) { free(buf); return 0; } + +int ext2_write_blockgroups(ext2_fs_t *fs, uint32_t bg) { + uint8_t *buf; + ext2_bg_desc_t *ptr = fs->bg; + uint32_t bg_per_block; + uint32_t count = fs->bg_count; + uint32_t start_block = bg * fs->sb.s_blocks_per_group + + fs->sb.s_first_data_block + 1; + + if(!(buf = (uint8_t *)malloc(fs->block_size))) + return -ENOMEM; + + bg_per_block = fs->block_size / sizeof(ext2_bg_desc_t); + + while(count) { + if(count < bg_per_block) { + memcpy(buf, ptr, count * sizeof(ext2_bg_desc_t)); + memset(buf + count * sizeof(ext2_bg_desc_t), 0, + (bg_per_block - count) * sizeof(ext2_bg_desc_t)); + count = 0; + } + else { + memcpy(buf, ptr, count * sizeof(ext2_bg_desc_t)); + count -= bg_per_block; + ptr += bg_per_block; + } + + if(ext2_block_write_nc(fs, start_block++, buf)) { + free(buf); + return -EIO; + } + } + + free(buf); + return 0; +} diff --git a/addons/libkosext2fs/block.h b/addons/libkosext2fs/block.h index 36a2634..76f081a 100644 --- a/addons/libkosext2fs/block.h +++ b/addons/libkosext2fs/block.h @@ -47,6 +47,7 @@ typedef struct ext2_bg_desc { } ext2_bg_desc_t; int ext2_read_blockgroups(ext2_fs_t *fs, uint32_t start_block); +int ext2_write_blockgroups(ext2_fs_t *fs, uint32_t bg); __END_DECLS diff --git a/addons/libkosext2fs/ext2fs.c b/addons/libkosext2fs/ext2fs.c index 4c4aaf6..b5b3331 100644 --- a/addons/libkosext2fs/ext2fs.c +++ b/addons/libkosext2fs/ext2fs.c @@ -48,8 +48,8 @@ uint8_t *ext2_block_read(ext2_fs_t *fs, uint32_t bl) { /* Look through the cache from the most recently used to the least recently used entry. */ - for(i = fs->cache_size - 1; i >= 0 && cache[i]->flags; --i) { - if(cache[i]->block == bl) { + for(i = fs->cache_size - 1; i >= 0; --i) { + if(cache[i]->block == bl && cache[i]->flags) { rv = cache[i]->data; make_mru(fs, cache, i); goto out; @@ -74,6 +74,7 @@ uint8_t *ext2_block_read(ext2_fs_t *fs, uint32_t bl) { /* Try to read the block in question. */ if(ext2_block_read_nc(fs, bl, cache[i]->data)) { errno = EIO; + cache[i]->flags = 0; /* Mark it as invalid... */ return NULL; } @@ -104,24 +105,57 @@ int ext2_block_read_nc(ext2_fs_t *fs, uint32_t block_num, uint8_t *rv) { return 0; } -int ext2_block_write_nc(ext2_fs_t *fs, uint32_t block_num, uint8_t *rv) { +int ext2_block_write_nc(ext2_fs_t *fs, uint32_t block_num, const uint8_t *blk) { int fs_per_block = fs->sb.s_log_block_size - fs->dev->l_block_size + 10; if(fs_per_block < 0) - /* This should never happen, as the ext2 block size must be at least - as large as the sector size of the block device itself. */ + /* This should never happen, as the ext2 block size must be at least + as large as the sector size of the block device itself. */ return -EINVAL; if(fs->sb.s_blocks_count <= block_num) return -EINVAL; if(fs->dev->write_blocks(fs->dev, block_num << fs_per_block, - 1 << fs_per_block, rv)) + 1 << fs_per_block, blk)) return -EIO; return 0; } +int ext2_block_mark_dirty(ext2_fs_t *fs, uint32_t block_num) { + int i; + ext2_cache_t **cache = fs->bcache; + + /* Look through the cache from the most recently used to the least recently + used entry. */ + for(i = fs->cache_size - 1; i >= 0; --i) { + if(cache[i]->block == block_num && cache[i]->flags) { + cache[i]->flags |= EXT2_CACHE_FLAG_DIRTY; + make_mru(fs, cache, i); + return 0; + } + } + + return -EINVAL; +} + +int ext2_block_cache_wb(ext2_fs_t *fs) { + int i, err; + ext2_cache_t **cache = fs->bcache; + + for(i = fs->cache_size - 1; i >= 0; --i) { + if(cache[i]->flags & EXT2_CACHE_FLAG_DIRTY) { + if((err = ext2_block_write_nc(fs, cache[i]->block, cache[i]->data))) + return err; + + cache[i]->flags &= ~EXT2_CACHE_FLAG_DIRTY; + } + } + + return 0; +} + uint32_t ext2_block_size(const ext2_fs_t *fs) { return fs->block_size; } @@ -195,7 +229,8 @@ ext2_fs_t *ext2_fs_init_ex(kos_blockdev_t *bd, int cache_sz) { dbglog(DBG_KDEBUG, "Superblocks are stored on the following blocks:\n"); tmp = rv->sb.s_first_data_block; - if(rv->sb.s_rev_level == EXT2_GOOD_OLD_REV) { + if(rv->sb.s_rev_level < EXT2_DYNAMIC_REV || + !(rv->sb.s_feature_ro_compat & EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) { while(tmp < bc) { dbglog(DBG_KDEBUG, "%" PRIu32 "\n", tmp); tmp += rv->sb.s_blocks_per_group; @@ -312,9 +347,55 @@ out_cache: return NULL; } +int ext2_fs_sync(ext2_fs_t *fs) { + int rv, frv = 0; + + /* Do a write-back on the inode cache first, pushing the changes out to the + block cache. */ + if((rv = ext2_inode_cache_wb(fs))) { + dbglog(DBG_ERROR, "ext2_fs_sync: Error writing back the inode cache: " + "%s.\n", strerror(-rv)); + errno = -rv; + frv = -1; + } + + /* Do a write-back on the block cache next, which should take care of all + the writes other than superblock(s) and block group descriptors. */ + if((rv = ext2_block_cache_wb(fs))) { + dbglog(DBG_ERROR, "ext2_fs_sync: Error writing back the block cache: " + "%s.\n", strerror(-rv)); + errno = -rv; + frv = -1; + } + + if((fs->flags & EXT2_FS_FLAG_SB_DIRTY)) { + /* Write the main superblock and the block group descriptors. */ + if((rv = ext2_write_superblock(fs, 0))) { + dbglog(DBG_ERROR, "ext2_fs_sync: Error writing back the main " + "superblock: %s.\n", strerror(-rv)); + dbglog(DBG_ERROR, " Your filesystem is possibly toast " + "at this point... Run e2fsck ASAP.\n"); + errno = -rv; + frv = -1; + } + + if((rv = ext2_write_blockgroups(fs, 0))) { + dbglog(DBG_ERROR, "ext2_fs_sync: Error writing back the main " + "block group descriptors: %s.\n", strerror(-rv)); + errno = -rv; + frv = -1; + } + } + + return frv; +} + void ext2_fs_shutdown(ext2_fs_t *fs) { int i; + /* Sync the filesystem back to the block device, if needed. */ + ext2_fs_sync(fs); + for(i = 0; i < fs->cache_size; ++i) { free(fs->bcache[i]->data); free(fs->bcache[i]); diff --git a/addons/libkosext2fs/ext2fs.h b/addons/libkosext2fs/ext2fs.h index 215680a..0b20af3 100644 --- a/addons/libkosext2fs/ext2fs.h +++ b/addons/libkosext2fs/ext2fs.h @@ -86,12 +86,19 @@ int ext2_init(void); ext2_fs_t *ext2_fs_init(kos_blockdev_t *bd); ext2_fs_t *ext2_fs_init_ex(kos_blockdev_t *bd, int cache_sz); +int ext2_fs_sync(ext2_fs_t *fs); void ext2_fs_shutdown(ext2_fs_t *fs); int ext2_block_read_nc(ext2_fs_t *fs, uint32_t block_num, uint8_t *rv); uint8_t *ext2_block_read(ext2_fs_t *fs, uint32_t block_num); -int ext2_block_write_nc(ext2_fs_t *fs, uint32_t block_num, uint8_t *rv); +int ext2_block_write_nc(ext2_fs_t *fs, uint32_t block_num, const uint8_t *blk); + +int ext2_block_mark_dirty(ext2_fs_t *fs, uint32_t block_num); + +/* Write-back all dirty blocks from the filesystem's cache. You probably want to + call the corresponding inode function before this one. */ +int ext2_block_cache_wb(ext2_fs_t *fs); __END_DECLS diff --git a/addons/libkosext2fs/ext2internal.h b/addons/libkosext2fs/ext2internal.h index afe25a7..8624a38 100644 --- a/addons/libkosext2fs/ext2internal.h +++ b/addons/libkosext2fs/ext2internal.h @@ -35,11 +35,22 @@ struct ext2fs_struct { ext2_cache_t **bcache; int cache_size; + + uint32_t flags; }; +/* The superblock and/or block descriptors need to be written to the block + device. */ +#define EXT2_FS_FLAG_SB_DIRTY 1 + #ifdef EXT2_NOT_IN_KOS #include <stdio.h> -#define dbglog(x, ...) printf(__VA_ARGS__) +#define DBG_DEBUG 0 +#define DBG_KDEBUG 0 +#define DBG_WARNING 0 +#define DBG_ERROR 0 + +#define dbglog(lvl, ...) printf(__VA_ARGS__) #endif #endif /* !__EXT2_EXT2INTERNAL_H */ diff --git a/addons/libkosext2fs/inode.c b/addons/libkosext2fs/inode.c index 48a15db..c8ad29c 100644 --- a/addons/libkosext2fs/inode.c +++ b/addons/libkosext2fs/inode.c @@ -24,9 +24,12 @@ #define MAX_INODES (1 << EXT2_LOG_MAX_INODES) #define INODE_HASH_SZ (1 << EXT2_LOG_INODE_HASH) +#define INODE_FLAG_DIRTY 0x00000001 + /* Internal inode storage structure. This is used for cacheing used inodes. */ static struct int_inode { - /* Start with the on-disk inode itself to make the put() function easier. */ + /* Start with the on-disk inode itself to make the put() function easier. + DO NOT MOVE THIS FROM THE BEGINNING OF THE STRUCTURE. */ ext2_inode_t inode; /* Hash table entry -- used at all points after the first time an inode is @@ -66,6 +69,7 @@ static struct inode_list inode_hash[INODE_HASH_SZ]; /* Forward declaration... */ static ext2_inode_t *ext2_inode_read(ext2_fs_t *fs, uint32_t inode_num); +static int ext2_inode_wb(struct int_inode *inode); void ext2_inode_init(void) { int i; @@ -158,11 +162,14 @@ void ext2_inode_put(ext2_inode_t *inode) { /* Decrement the reference counter, and see if we've got the last one. */ if(!--iinode->refcnt) { - /* Yep, we've gone and consumed the last reference, so put it on the - free list at the end (in case we want to bring it back from the dead - later on). - XXXX: We should write it back out to the disk if it is dirty, but - that is for another day. */ + /* Write it back out to the block cache if it was dirty. */ + if(iinode->flags & INODE_FLAG_DIRTY) + /* XXXX: Should probably make sure this succeeds... */ + ext2_inode_wb(iinode); + + /* We've gone and consumed the last reference, so put it on the free + list at the end, in case we want to bring it back from the dead later + on. */ TAILQ_INSERT_TAIL(&free_inodes, iinode, qentry); } @@ -174,6 +181,15 @@ void ext2_inode_put(ext2_inode_t *inode) { #endif } +void ext2_inode_mark_dirty(ext2_inode_t *inode) { + struct int_inode *iinode = (struct int_inode *)inode; + + /* Make sure we're not trying anything really mean. */ + assert(iinode->refcnt != 0); + + iinode->flags |= INODE_FLAG_DIRTY; +} + static ext2_inode_t *ext2_inode_read(ext2_fs_t *fs, uint32_t inode_num) { uint32_t bg, index; uint8_t *buf; @@ -208,6 +224,163 @@ static ext2_inode_t *ext2_inode_read(ext2_fs_t *fs, uint32_t inode_num) { return (ext2_inode_t *)(buf + (index * fs->sb.s_inode_size)); } +static int ext2_inode_wb(struct int_inode *inode) { + uint32_t bg, index; + uint8_t *buf; + int in_per_block, rv; + uint32_t inode_block; + ext2_fs_t *fs = inode->fs; + + in_per_block = (fs->block_size) / fs->sb.s_inode_size; + + /* Figure out what block group and index within that group the inode in + question is. */ + bg = (inode->inode_num - 1) / fs->sb.s_inodes_per_group; + index = (inode->inode_num - 1) % fs->sb.s_inodes_per_group; + + if(inode->inode_num > fs->sb.s_inodes_count) + return -EINVAL; + + /* Read the block containing the inode in so that we can write the part that + we need to it. */ + inode_block = fs->bg[bg].bg_inode_table + (index / in_per_block); + index %= in_per_block; + + if(!(buf = ext2_block_read(fs, inode_block))) + return -errno; + + /* Write to the block and mark it as dirty so that it'll get flushed. */ + memcpy(buf + (index * fs->sb.s_inode_size), inode, sizeof(ext2_inode_t)); + rv = ext2_block_mark_dirty(fs, inode_block); + + /* Clear the dirty flag, if we wrote it out successfully. */ + if(!rv) + inode->flags &= ~INODE_FLAG_DIRTY; + + return rv; +} + +int ext2_inode_cache_wb(ext2_fs_t *fs) { + int i, rv = 0; + + for(i = 0; i < MAX_INODES && !rv; ++i) { + if(inodes[i].fs == fs && (inodes[i].flags & INODE_FLAG_DIRTY)) { + rv = ext2_inode_wb(inodes + i); + } + } + + return rv; +} + +ext2_inode_t *ext2_inode_alloc(ext2_fs_t *fs, uint32_t bg, int *err) { + uint8_t *buf; + uint32_t index; + struct int_inode *i; + + /* See if we have any free inodes at all... */ + if(!fs->sb.s_free_inodes_count) { + *err = ENOSPC; + return NULL; + } + + /* See if we have any free inodes in the block group requested. */ + if(fs->bg[bg].bg_free_inodes_count) { + if(!(buf = ext2_block_read(fs, fs->bg[bg].bg_inode_bitmap))) { + *err = errno; + return NULL; + } + + index = ext2_bit_find_nonzero((uint32_t *)buf, 0, + fs->sb.s_inodes_per_group - 1); + if(index < fs->sb.s_inodes_per_group) { + ext2_bit_set((uint32_t *)buf, index); + ext2_block_mark_dirty(fs, fs->bg[bg].bg_inode_bitmap); + --fs->bg[bg].bg_free_inodes_count; + --fs->sb.s_free_inodes_count; + fs->flags |= EXT2_FS_FLAG_SB_DIRTY; + i = (struct int_inode *)ext2_inode_get(fs, index + bg * + fs->sb.s_inodes_per_group + + 1, err); + memset(i, 0, sizeof(ext2_inode_t)); + i->flags |= INODE_FLAG_DIRTY; + return (ext2_inode_t *)i; + } + + /* We shouldn't get here... But, just in case, fall through. We should + probably log an error and tell the user to fsck though. */ + dbglog(DBG_WARNING, "ext2_inode_alloc: Block group %" PRIu32 " " + "indicates that it has free inodes, but doesn't appear to. " + "Please run fsck on this volume!\n", bg); + } + + /* Couldn't find a free inode in the requested block group... Loop through + all the block groups looking for a free inode. */ + for(bg = 0; bg < fs->bg_count; ++bg) { + if(fs->bg[bg].bg_free_inodes_count) { + if(!(buf = ext2_block_read(fs, fs->bg[bg].bg_inode_bitmap))) { + *err = errno; + return NULL; + } + + index = ext2_bit_find_nonzero((uint32_t *)buf, 0, + fs->sb.s_inodes_per_group - 1); + if(index < fs->sb.s_inodes_per_group) { + ext2_bit_set((uint32_t *)buf, index); + ext2_block_mark_dirty(fs, fs->bg[bg].bg_inode_bitmap); + --fs->bg[bg].bg_free_inodes_count; + --fs->sb.s_free_inodes_count; + fs->flags |= EXT2_FS_FLAG_SB_DIRTY; + i = (struct int_inode *)ext2_inode_get(fs, index + bg * + fs->sb.s_inodes_per_group + + 1, err); + memset(i, 0, sizeof(ext2_inode_t)); + i->flags |= INODE_FLAG_DIRTY; + return (ext2_inode_t *)i; + } + + /* We shouldn't get here... But, just in case, fall through. We + should probably log an error and tell the user to fsck though. */ + dbglog(DBG_WARNING, "ext2_inode_alloc: Block group %" PRIu32 " " + "indicates that it has free inodes, but doesn't appear to. " + "Please run fsck on this volume!\n", bg); + } + } + + *err = ENOSPC; + return NULL; +} + +int ext2_inode_free(ext2_fs_t *fs, uint32_t inode_num) { + uint32_t bg, index; + uint8_t *buf; + + if(inode_num > fs->sb.s_inodes_count) + return -EINVAL; + + /* Figure out what block group and index within that group the inode in + question is. */ + bg = (inode_num - 1) / fs->sb.s_inodes_per_group; + index = (inode_num - 1) % fs->sb.s_inodes_per_group; + + if(!(buf = ext2_block_read(fs, fs->bg[bg].bg_inode_bitmap))) + return -EIO; + + /* Make sure it is actually allocated. */ + if(!ext2_bit_is_set((uint32_t *)buf, index)) + return -EINVAL; + + /* Mark the inode as free in the bitmap and increase the counters. */ + ext2_bit_clear((uint32_t *)buf, index); + ext2_block_mark_dirty(fs, fs->bg[bg].bg_inode_bitmap); + + ++fs->bg[bg].bg_free_inodes_count; + ++fs->sb.s_free_inodes_count; + fs->flags |= EXT2_FS_FLAG_SB_DIRTY; + + /* TODO: Perhaps we should zero the inode? */ + return 0; +} + static ext2_dirent_t *search_dir(uint8_t *buf, int block_size, const char *token, int *err) { int block_offset = 0; diff --git a/addons/libkosext2fs/inode.h b/addons/libkosext2fs/inode.h index 79712f0..9b2eefc 100644 --- a/addons/libkosext2fs/inode.h +++ b/addons/libkosext2fs/inode.h @@ -104,6 +104,16 @@ int ext2_inode_by_path(ext2_fs_t *fs, const char *path, ext2_inode_t **rv, void ext2_inode_put(ext2_inode_t *inode); +/* Write-back all of the inodes marked as dirty from the specified filesystem to + its block cache. */ +int ext2_inode_cache_wb(ext2_fs_t *fs); + +/* Allocate an unused inode on the specified filesystem. */ +ext2_inode_t *ext2_inode_alloc(ext2_fs_t *fs, uint32_t bg, int *err); ...<truncated>... hooks/post-receive -- A pseudo Operating System for the Dreamcast. |