From: Lawrence S. <ljs...@us...> - 2013-04-17 01:12:32
|
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 c0e3f814f7c596346d776910b013dfa7a547aba5 (commit) from cc47aaa0c998d521607127c252d2fa95745ab2fb (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 c0e3f814f7c596346d776910b013dfa7a547aba5 Author: Lawrence Sebald <blu...@ne...> Date: Tue Apr 16 21:11:04 2013 -0400 Furthering write support in libkosext2fs a bit... 1. Add a function to allocate blocks to an inode. 2. Add mkdir to fs_ext2. ----------------------------------------------------------------------- Summary of changes: addons/libkosext2fs/directory.c | 180 ++++++++++++++++++++++- addons/libkosext2fs/directory.h | 14 ++- addons/libkosext2fs/ext2fs.c | 98 ++++++++++++- addons/libkosext2fs/ext2fs.h | 4 +- addons/libkosext2fs/fs_ext2.c | 128 +++++++++++++++- addons/libkosext2fs/inode.c | 304 ++++++++++++++++++++++++++++++++++----- addons/libkosext2fs/inode.h | 11 ++- addons/libkosext2fs/symlink.c | 3 +- 8 files changed, 681 insertions(+), 61 deletions(-) diff --git a/addons/libkosext2fs/directory.c b/addons/libkosext2fs/directory.c index 41abf5b..fe7d6dc 100644 --- a/addons/libkosext2fs/directory.c +++ b/addons/libkosext2fs/directory.c @@ -12,16 +12,24 @@ #include "directory.h" #include "inode.h" +/* Calculate the minimum size of a directory entry based on the length of the + filename. This takes care of making sure that everything aligns nicely on a + 4-byte boundary as well. */ +#define DENT_SZ(n) (((n) + sizeof(ext2_dirent_t) + 4) & 0x01FC) + int ext2_dir_is_empty(ext2_fs_t *fs, const struct ext2_inode *dir) { uint32_t off, i, blocks; ext2_dirent_t *dent; uint8_t *buf; + int err; blocks = dir->i_blocks / (2 << fs->sb.s_log_block_size); for(i = 0; i < blocks; ++i) { off = 0; - buf = ext2_inode_read_block(fs, dir, i, NULL); + + if(!(buf = ext2_inode_read_block(fs, dir, i, NULL, &err))) + return -err; while(off < fs->block_size) { dent = (ext2_dirent_t *)(buf + off); @@ -54,12 +62,15 @@ ext2_dirent_t *ext2_dir_entry(ext2_fs_t *fs, const struct ext2_inode *dir, ext2_dirent_t *dent; uint8_t *buf; size_t len = strlen(fn); + int err; blocks = dir->i_blocks / (2 << fs->sb.s_log_block_size); for(i = 0; i < blocks; ++i) { off = 0; - buf = ext2_inode_read_block(fs, dir, i, NULL); + + if(!(buf = ext2_inode_read_block(fs, dir, i, NULL, &err))) + return NULL; while(off < fs->block_size) { dent = (ext2_dirent_t *)(buf + off); @@ -82,12 +93,13 @@ ext2_dirent_t *ext2_dir_entry(ext2_fs_t *fs, const struct ext2_inode *dir, return NULL; } -int ext2_dir_rm_entry(ext2_fs_t *fs, const struct ext2_inode *dir, - const char *fn, uint32_t *inode) { +int ext2_dir_rm_entry(ext2_fs_t *fs, struct ext2_inode *dir, const char *fn, + uint32_t *inode) { uint32_t off, i, blocks, bn; ext2_dirent_t *dent, *prev; uint8_t *buf; size_t len = strlen(fn); + int err; /* Don't even bother if we're mounted read-only. */ if(!(fs->mnt_flags & EXT2FS_MNT_FLAG_RW)) @@ -99,7 +111,9 @@ int ext2_dir_rm_entry(ext2_fs_t *fs, const struct ext2_inode *dir, off = 0; prev = NULL; dent = NULL; - buf = ext2_inode_read_block(fs, dir, i, &bn); + + if(!(buf = ext2_inode_read_block(fs, dir, i, &bn, &err))) + return -err; while(off < fs->block_size) { prev = dent; @@ -132,6 +146,12 @@ int ext2_dir_rm_entry(ext2_fs_t *fs, const struct ext2_inode *dir, /* Mark the block as dirty so that it gets rewritten to the block device. */ ext2_block_mark_dirty(fs, bn); + + /* Since we may well have trashed the tree if we're using a + btree directory structure, make sure that we note that + by setting that the directory is no longer indexed. */ + dir->i_flags &= ~EXT2_BTREE_FL; + ext2_inode_mark_dirty(dir); return 0; } } @@ -143,3 +163,153 @@ int ext2_dir_rm_entry(ext2_fs_t *fs, const struct ext2_inode *dir, /* Didn't find it, oh well. */ return -ENOENT; } + +static const uint8_t inodetype_to_dirtype[16] = { + EXT2_FT_UNKNOWN, EXT2_FT_FIFO, EXT2_FT_CHRDEV, EXT2_FT_UNKNOWN, + EXT2_FT_DIR, EXT2_FT_UNKNOWN, EXT2_FT_BLKDEV, EXT2_FT_UNKNOWN, + EXT2_FT_REG_FILE, EXT2_FT_UNKNOWN, EXT2_FT_SYMLINK, EXT2_FT_UNKNOWN, + EXT2_FT_SOCK, EXT2_FT_UNKNOWN, EXT2_FT_UNKNOWN, EXT2_FT_UNKNOWN +}; + +int ext2_dir_add_entry(ext2_fs_t *fs, struct ext2_inode *dir, const char *fn, + uint32_t inode_num, const struct ext2_inode *ent, + ext2_dirent_t **rv) { + uint32_t off, i, blocks, bn; + ext2_dirent_t *dent; + uint8_t *buf; + size_t nlen = strlen(fn); + uint16_t rlen = DENT_SZ(nlen), tmp; + int err; + + /* Don't even bother if we're mounted read-only. */ + if(!(fs->mnt_flags & EXT2FS_MNT_FLAG_RW)) + return -EROFS; + + blocks = dir->i_blocks / (2 << fs->sb.s_log_block_size); + + for(i = 0; i < blocks; ++i) { + off = 0; + dent = NULL; + + if(!(buf = ext2_inode_read_block(fs, dir, i, &bn, &err))) + return -err; + + while(off < fs->block_size) { + dent = (ext2_dirent_t *)(buf + off); + + /* Make sure we don't trip and fall on a malformed entry. */ + if(!dent->rec_len) + return -EIO; + + /* If the entry is filled in, check to make sure it doesn't match + the name of the entry we're trying to add. */ + if(dent->inode) { + if(dent->name_len == nlen && !memcmp(dent->name, fn, nlen)) { + return -EEXIST; + } + else if(dent->rec_len >= rlen + DENT_SZ(dent->name_len)) { + /* We have space at the end of this entry... Cut off the + empty space*/ + rlen = dent->rec_len; + tmp = dent->rec_len = DENT_SZ(dent->name_len); + dent = (ext2_dirent_t *)(buf + off + tmp); + dent->rec_len = rlen - tmp; + goto fill_it_in; + } + } + /* If it isn't filled in, is there enough space to stick our new + entry here? */ + else if(dent->rec_len >= rlen) { + goto fill_it_in; + } + + off += dent->rec_len; + } + } + + /* No space in the existing blocks... Guess we'll have to allocate a new + block to store this in. */ + if(!(buf = ext2_inode_alloc_block(fs, dir, &err))) + return -err; + + dent = (ext2_dirent_t *)buf; + dent->rec_len = fs->block_size; + + /* Update the directory's size in the inode. */ + dir->i_size += fs->block_size; + + /* Fall through... */ +fill_it_in: + dent->inode = inode_num; + dent->name_len = (uint8_t)nlen; + memcpy(dent->name, fn, nlen); + + /* Fill in the file type if applicable to this fs. */ + if(fs->sb.s_rev_level >= EXT2_DYNAMIC_REV && + (fs->sb.s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE)) + dent->file_type = inodetype_to_dirtype[ent->i_mode >> 12]; + + if(rv) + *rv = dent; + + /* Mark the directory's block as dirty. */ + ext2_block_mark_dirty(fs, bn); + + /* Since we may well have trashed the tree if we're using a btree directory + structure, make sure that we note that by setting that the directory is + no longer indexed. */ + dir->i_flags &= ~EXT2_BTREE_FL; + ext2_inode_mark_dirty(dir); + + return 0; +} + +int ext2_dir_create_empty(ext2_fs_t *fs, struct ext2_inode *dir, + uint32_t inode_num, uint32_t parent_inode) { + uint8_t *dir_buf; + ext2_dirent_t *ent; + int err; + uint32_t bg; + + /* Allocate a block for the directory structure. */ + if(!(dir_buf = ext2_inode_alloc_block(fs, dir, &err))) + return -err; + + /* Fill in "." */ + ent = (ext2_dirent_t *)dir_buf; + ent->inode = inode_num; + ent->rec_len = 12; + ent->name_len = 1; + ent->file_type = 0; + ent->name[0] = '.'; + ent->name[1] = ent->name[2] = ent->name[3] = '\0'; + + if(fs->sb.s_rev_level >= EXT2_DYNAMIC_REV && + (fs->sb.s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE)) + ent->file_type = EXT2_FT_DIR; + + /* Fill in ".." */ + ent = (ext2_dirent_t *)(dir_buf + 12); + ent->inode = parent_inode; + ent->rec_len = fs->block_size - 12; + ent->name_len = 2; + ent->file_type = 0; + ent->name[0] = ent->name[1] = '.'; + ent->name[2] = ent->name[3] = '\0'; + + if(fs->sb.s_rev_level >= EXT2_DYNAMIC_REV && + (fs->sb.s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE)) + ent->file_type = EXT2_FT_DIR; + + /* Fix up some stuff in the inode. */ + dir->i_size = fs->block_size; + dir->i_links_count = 2; + + /* And update the block group's directory counter. */ + bg = (inode_num - 1) / fs->sb.s_inodes_per_group; + ++fs->bg[bg].bg_used_dirs_count; + fs->flags |= EXT2_FS_FLAG_SB_DIRTY; + + /* And, we're done. */ + return 0; +} diff --git a/addons/libkosext2fs/directory.h b/addons/libkosext2fs/directory.h index a05d858..5a917a2 100644 --- a/addons/libkosext2fs/directory.h +++ b/addons/libkosext2fs/directory.h @@ -43,8 +43,18 @@ ext2_dirent_t *ext2_dir_entry(ext2_fs_t *fs, const struct ext2_inode *dir, /* Delete an entry from a directory. Note that this does nothing about cleaning up the inode, but it does tell you which inode you're going to need to clean up (or lower the reference count on). */ -int ext2_dir_rm_entry(ext2_fs_t *fs, const struct ext2_inode *dir, - const char *fn, uint32_t *inode); +int ext2_dir_rm_entry(ext2_fs_t *fs, struct ext2_inode *dir, const char *fn, + uint32_t *inode); + +/* Add an entry to a directory. */ +int ext2_dir_add_entry(ext2_fs_t *fs, struct ext2_inode *dir, const char *fn, + uint32_t inode_num, const struct ext2_inode *ent, + ext2_dirent_t **rv); + +/* Create the structure of an empty directory. The inode for the directory + must already be allocated. */ +int ext2_dir_create_empty(ext2_fs_t *fs, struct ext2_inode *dir, + uint32_t inode_num, uint32_t parent_inode); __END_DECLS #endif /* !__EXT2_DIRECTORY_H */ diff --git a/addons/libkosext2fs/ext2fs.c b/addons/libkosext2fs/ext2fs.c index 1cded4f..ff1b5fe 100644 --- a/addons/libkosext2fs/ext2fs.c +++ b/addons/libkosext2fs/ext2fs.c @@ -41,7 +41,7 @@ static void make_mru(ext2_fs_t *fs, ext2_cache_t **cache, int block) { } /* XXXX: This needs locking! */ -uint8_t *ext2_block_read(ext2_fs_t *fs, uint32_t bl) { +uint8_t *ext2_block_read(ext2_fs_t *fs, uint32_t bl, int *err) { int i; uint8_t *rv; ext2_cache_t **cache = fs->bcache; @@ -65,7 +65,7 @@ uint8_t *ext2_block_read(ext2_fs_t *fs, uint32_t bl) { if(cache[0]->flags & EXT2_CACHE_FLAG_DIRTY) { if(ext2_block_write_nc(fs, cache[0]->block, cache[0]->data)) { /* XXXX: Uh oh... */ - errno = EIO; + *err = EIO; return NULL; } } @@ -73,7 +73,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; + *err = EIO; cache[i]->flags = 0; /* Mark it as invalid... */ return NULL; } @@ -160,6 +160,98 @@ int ext2_block_cache_wb(ext2_fs_t *fs) { return 0; } +uint8_t *ext2_block_alloc(ext2_fs_t *fs, uint32_t bg, uint32_t *bn, int *err) { + uint8_t *buf, *blk; + uint32_t index; + + /* Don't even bother if we're mounted read-only. */ + if(!(fs->mnt_flags & EXT2FS_MNT_FLAG_RW)) { + *err = EROFS; + return NULL; + } + + /* See if we have any free blocks at all... */ + if(!fs->sb.s_free_blocks_count) { + *err = ENOSPC; + return NULL; + } + + /* See if we have any free blocks in the block group requested. */ + if(fs->bg[bg].bg_free_blocks_count) { + if(!(buf = ext2_block_read(fs, fs->bg[bg].bg_block_bitmap, err))) + return NULL; + + index = ext2_bit_find_zero((uint32_t *)buf, 0, + fs->sb.s_blocks_per_group - 1); + if(index < fs->sb.s_blocks_per_group) { + *bn = index + bg * fs->sb.s_blocks_per_group + + fs->sb.s_first_data_block; + + if(!(blk = ext2_block_read(fs, *bn, err))) + return NULL; + + ext2_bit_set((uint32_t *)buf, index); + ext2_block_mark_dirty(fs, fs->bg[bg].bg_block_bitmap); + --fs->bg[bg].bg_free_blocks_count; + --fs->sb.s_free_blocks_count; + fs->flags |= EXT2_FS_FLAG_SB_DIRTY; + + memset(blk, 0, fs->block_size); + ext2_block_mark_dirty(fs, *bn); + return blk; + } + + /* 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_block_alloc: Block group %" PRIu32 " " + "indicates that it has free blocks, but doesn't appear to. " + "Please run fsck on this volume!\n", bg); + } + + /* Couldn't find a free block in the requested block group... Loop through + all the block groups looking for a free block. */ + for(bg = 0; bg < fs->bg_count; ++bg) { + if(fs->bg[bg].bg_free_blocks_count) { + if(!(buf = ext2_block_read(fs, fs->bg[bg].bg_block_bitmap, err))) + return NULL; + + index = ext2_bit_find_zero((uint32_t *)buf, 0, + fs->sb.s_blocks_per_group - 1); + if(index < fs->sb.s_blocks_per_group) { + *bn = index + bg * fs->sb.s_blocks_per_group + + fs->sb.s_first_data_block; + + if(!(blk = ext2_block_read(fs, *bn, err))) + return NULL; + + ext2_bit_set((uint32_t *)buf, index); + ext2_block_mark_dirty(fs, fs->bg[bg].bg_block_bitmap); + --fs->bg[bg].bg_free_blocks_count; + --fs->sb.s_free_blocks_count; + fs->flags |= EXT2_FS_FLAG_SB_DIRTY; + + memset(blk, 0, fs->block_size); + ext2_block_mark_dirty(fs, *bn); + return blk; + } + + /* 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_block_alloc: Block group %" PRIu32 " " + "indicates that it has free blocks, but doesn't appear to. " + "Please run fsck on this volume!\n", bg); + } + } + + /* Uh oh... We went through everything and didn't find any. That means the + data in the superblock is wrong. */ + dbglog(DBG_WARNING, "ext2_block_alloc: Filesystem indicates that it has " + "free blocks, but doesn't appear to. Please run fsck on this " + "volume!\n"); + *err = ENOSPC; + return NULL; +} + uint32_t ext2_block_size(const ext2_fs_t *fs) { return fs->block_size; } diff --git a/addons/libkosext2fs/ext2fs.h b/addons/libkosext2fs/ext2fs.h index 36a366c..2a21f47 100644 --- a/addons/libkosext2fs/ext2fs.h +++ b/addons/libkosext2fs/ext2fs.h @@ -97,7 +97,7 @@ 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); +uint8_t *ext2_block_read(ext2_fs_t *fs, uint32_t block_num, int *err); int ext2_block_write_nc(ext2_fs_t *fs, uint32_t block_num, const uint8_t *blk); @@ -107,6 +107,8 @@ int ext2_block_mark_dirty(ext2_fs_t *fs, uint32_t block_num); call the corresponding inode function before this one. */ int ext2_block_cache_wb(ext2_fs_t *fs); +uint8_t *ext2_block_alloc(ext2_fs_t *fs, uint32_t bg, uint32_t *bn, int *err); + __END_DECLS #endif /* !__EXT2_EXT2FS_H */ diff --git a/addons/libkosext2fs/fs_ext2.c b/addons/libkosext2fs/fs_ext2.c index a3ac19b..24315bb 100644 --- a/addons/libkosext2fs/fs_ext2.c +++ b/addons/libkosext2fs/fs_ext2.c @@ -4,6 +4,7 @@ Copyright (C) 2012, 2013 Lawrence Sebald */ +#include <time.h> #include <errno.h> #include <stdint.h> #include <stdlib.h> @@ -168,9 +169,8 @@ static ssize_t fs_ext2_read(void *h, void *buf, size_t cnt) { /* Handle the first block specially if we are offset within it. */ if(bo) { if(!(block = ext2_inode_read_block(fs, fh[fd].inode, fh[fd].ptr >> lbs, - NULL))) { + NULL, &errno))) { mutex_unlock(&ext2_mutex); - errno = EBADF; return -1; } @@ -190,9 +190,8 @@ static ssize_t fs_ext2_read(void *h, void *buf, size_t cnt) { /* While we still have more to read, do it. */ while(cnt) { if(!(block = ext2_inode_read_block(fs, fh[fd].inode, fh[fd].ptr >> lbs, - NULL))) { + NULL, &errno))) { mutex_unlock(&ext2_mutex); - errno = EBADF; return -1; } @@ -318,9 +317,8 @@ retry: } if(!(block = ext2_inode_read_block(fs, fh[fd].inode, fh[fd].ptr >> lbs, - NULL))) { + NULL, &errno))) { mutex_unlock(&ext2_mutex); - errno = EBADF; return NULL; } @@ -478,6 +476,9 @@ static int fs_ext2_unlink(vfs_handler_t *vfs, const char *fn) { return -1; } + /* Update the times in the parent's inode */ + pinode->i_ctime = pinode->i_mtime = time(NULL); + /* We're done with these now, so clean them up. */ ext2_inode_put(pinode); ext2_inode_put(inode); @@ -560,6 +561,115 @@ static int fs_ext2_stat(vfs_handler_t *vfs, const char *fn, stat_t *rv) { return 0; } +static int fs_ext2_mkdir(vfs_handler_t *vfs, const char *fn) { + fs_ext2_fs_t *fs = (fs_ext2_fs_t *)vfs->privdata; + int irv; + ext2_inode_t *inode, *ninode; + uint32_t inode_num, ninode_num; + char *cp, *nd; + + /* Make sure there is a filename given */ + if(!fn) { + errno = ENOENT; + return -1; + } + + /* Make sure the fs is writable */ + if(!(fs->mount_flags & FS_EXT2_MOUNT_READWRITE)) { + errno = EROFS; + return -1; + } + + /* The root directory has to exist... */ + if(!*fn) { + errno = EEXIST; + return -1; + } + ...<truncated>... hooks/post-receive -- A pseudo Operating System for the Dreamcast. |