From: Lawrence S. <ljs...@us...> - 2013-04-10 00:45:44
|
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 de3fe342ba44fe1353cf2315bceaa53d3b0a56e1 (commit) from 186e9f53b0f6c951c69252a228f816255eaa2074 (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 de3fe342ba44fe1353cf2315bceaa53d3b0a56e1 Author: Lawrence Sebald <ljs...@us...> Date: Tue Apr 9 20:44:18 2013 -0400 Add support for unlink and rmdir to fs_ext2. ----------------------------------------------------------------------- Summary of changes: addons/include/ext2/fs_ext2.h | 2 + addons/libkosext2fs/block.c | 4 + addons/libkosext2fs/directory.c | 67 ++++++++- addons/libkosext2fs/directory.h | 9 + addons/libkosext2fs/ext2fs.c | 22 ++- addons/libkosext2fs/ext2fs.h | 11 +- addons/libkosext2fs/ext2internal.h | 1 + addons/libkosext2fs/fs_ext2.c | 284 +++++++++++++++++++++++++++++++- addons/libkosext2fs/inode.c | 317 ++++++++++++++++++++++++++++++++++-- addons/libkosext2fs/inode.h | 20 ++- addons/libkosext2fs/superblock.c | 4 + addons/libkosext2fs/symlink.c | 2 +- 12 files changed, 713 insertions(+), 30 deletions(-) diff --git a/addons/include/ext2/fs_ext2.h b/addons/include/ext2/fs_ext2.h index 3bc16ca..2722af5 100644 --- a/addons/include/ext2/fs_ext2.h +++ b/addons/include/ext2/fs_ext2.h @@ -83,6 +83,8 @@ int fs_ext2_shutdown(void); the block device does not support writing, then the filesystem will not be mounted as read-write (for obvious reasons). + These should stay synchronized with the ones in ext2fs.h. + @{ */ #define FS_EXT2_MOUNT_READONLY 0x00000000 /**< \brief Mount read-only */ diff --git a/addons/libkosext2fs/block.c b/addons/libkosext2fs/block.c index 399481e..0395cb7 100644 --- a/addons/libkosext2fs/block.c +++ b/addons/libkosext2fs/block.c @@ -55,6 +55,10 @@ int ext2_write_blockgroups(ext2_fs_t *fs, uint32_t bg) { uint32_t start_block = bg * fs->sb.s_blocks_per_group + fs->sb.s_first_data_block + 1; + /* Don't even bother if we're mounted read-only. */ + if(!(fs->mnt_flags & EXT2FS_MNT_FLAG_RW)) + return 0; + if(!(buf = (uint8_t *)malloc(fs->block_size))) return -ENOMEM; diff --git a/addons/libkosext2fs/directory.c b/addons/libkosext2fs/directory.c index 5a82bec..41abf5b 100644 --- a/addons/libkosext2fs/directory.c +++ b/addons/libkosext2fs/directory.c @@ -5,6 +5,7 @@ */ #include <string.h> +#include <errno.h> #include "ext2fs.h" #include "ext2internal.h" @@ -20,7 +21,7 @@ int ext2_dir_is_empty(ext2_fs_t *fs, const struct ext2_inode *dir) { for(i = 0; i < blocks; ++i) { off = 0; - buf = ext2_inode_read_block(fs, dir, (uint32_t)i); + buf = ext2_inode_read_block(fs, dir, i, NULL); while(off < fs->block_size) { dent = (ext2_dirent_t *)(buf + off); @@ -58,7 +59,7 @@ ext2_dirent_t *ext2_dir_entry(ext2_fs_t *fs, const struct ext2_inode *dir, for(i = 0; i < blocks; ++i) { off = 0; - buf = ext2_inode_read_block(fs, dir, (uint32_t)i); + buf = ext2_inode_read_block(fs, dir, i, NULL); while(off < fs->block_size) { dent = (ext2_dirent_t *)(buf + off); @@ -80,3 +81,65 @@ ext2_dirent_t *ext2_dir_entry(ext2_fs_t *fs, const struct ext2_inode *dir, /* Didn't find it, oh well. */ return NULL; } + +int ext2_dir_rm_entry(ext2_fs_t *fs, const 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); + + /* 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; + prev = NULL; + dent = NULL; + buf = ext2_inode_read_block(fs, dir, i, &bn); + + while(off < fs->block_size) { + prev = dent; + 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(dent->inode) { + /* Check if this what we're looking for. */ + if(dent->name_len == len && !memcmp(dent->name, fn, len)) { + /* Return the inode number to the calling function. */ + *inode = dent->inode; + + if(prev) { + /* Remove it from the chain and clear the entry. */ + prev->rec_len += dent->rec_len; + memset(dent, 0, dent->rec_len); + } + else { + /* This is the first entry in a block, so simply mark + the entry as invalid, and clear the filename and such + from it. */ + dent->inode = 0; + memset(dent->name, 0, dent->name_len); + dent->name_len = dent->file_type = 0; + } + + /* Mark the block as dirty so that it gets rewritten to the + block device. */ + ext2_block_mark_dirty(fs, bn); + return 0; + } + } + + off += dent->rec_len; + } + } + + /* Didn't find it, oh well. */ + return -ENOENT; +} diff --git a/addons/libkosext2fs/directory.h b/addons/libkosext2fs/directory.h index 78d40f4..a05d858 100644 --- a/addons/libkosext2fs/directory.h +++ b/addons/libkosext2fs/directory.h @@ -33,9 +33,18 @@ typedef struct ext2_dirent { /* Forward declaration... */ struct ext2_inode; +/* Check if a directory is empty. */ int ext2_dir_is_empty(ext2_fs_t *fs, const struct ext2_inode *dir); + +/* Find an entry in a directory. */ ext2_dirent_t *ext2_dir_entry(ext2_fs_t *fs, const struct ext2_inode *dir, const char *fn); +/* 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); + __END_DECLS #endif /* !__EXT2_DIRECTORY_H */ diff --git a/addons/libkosext2fs/ext2fs.c b/addons/libkosext2fs/ext2fs.c index b5b3331..1cded4f 100644 --- a/addons/libkosext2fs/ext2fs.c +++ b/addons/libkosext2fs/ext2fs.c @@ -144,6 +144,10 @@ int ext2_block_cache_wb(ext2_fs_t *fs) { int i, err; ext2_cache_t **cache = fs->bcache; + /* Don't even bother if we're mounted read-only. */ + if(!(fs->mnt_flags & EXT2FS_MNT_FLAG_RW)) + return 0; + 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))) @@ -171,11 +175,11 @@ int ext2_init(void) { return 0; } -ext2_fs_t *ext2_fs_init(kos_blockdev_t *bd) { - return ext2_fs_init_ex(bd, EXT2_CACHE_BLOCKS); +ext2_fs_t *ext2_fs_init(kos_blockdev_t *bd, uint32_t flags) { + return ext2_fs_init_ex(bd, flags, EXT2_CACHE_BLOCKS); } -ext2_fs_t *ext2_fs_init_ex(kos_blockdev_t *bd, int cache_sz) { +ext2_fs_t *ext2_fs_init_ex(kos_blockdev_t *bd, uint32_t flags, int cache_sz) { ext2_fs_t *rv; uint32_t bc; int j; @@ -201,6 +205,14 @@ ext2_fs_t *ext2_fs_init_ex(kos_blockdev_t *bd, int cache_sz) { } rv->dev = bd; + rv->mnt_flags = flags & EXT2FS_MNT_VALID_FLAGS_MASK; + + if(rv->mnt_flags != flags) { + dbglog(DBG_WARNING, "ext2_fs_init: unknown mount flags: %08" PRIx32 + "\n", flags); + dbglog(DBG_WARNING, " mounting read-only\n"); + rv->mnt_flags = 0; + } /* Read in the all-important superblock. */ if(ext2_read_superblock(&rv->sb, bd)) { @@ -350,6 +362,10 @@ out_cache: int ext2_fs_sync(ext2_fs_t *fs) { int rv, frv = 0; + /* Don't even bother if we're mounted read-only. */ + if(!(fs->mnt_flags & EXT2FS_MNT_FLAG_RW)) + return 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))) { diff --git a/addons/libkosext2fs/ext2fs.h b/addons/libkosext2fs/ext2fs.h index 0b20af3..36a366c 100644 --- a/addons/libkosext2fs/ext2fs.h +++ b/addons/libkosext2fs/ext2fs.h @@ -76,6 +76,13 @@ typedef struct kos_blockdev { struct ext2fs_struct; typedef struct ext2fs_struct ext2_fs_t; +/* Filesystem mount flags */ +#define EXT2FS_MNT_FLAG_RO 0x00000000 +#define EXT2FS_MNT_FLAG_RW 0x00000001 + +/* Valid flags mask */ +#define EXT2FS_MNT_VALID_FLAGS_MASK 0x00000001 + uint32_t ext2_block_size(const ext2_fs_t *fs); uint32_t ext2_log_block_size(const ext2_fs_t *fs); @@ -84,8 +91,8 @@ uint32_t ext2_log_block_size(const ext2_fs_t *fs); mounting the first filesystem. */ 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); +ext2_fs_t *ext2_fs_init(kos_blockdev_t *bd, uint32_t flags); +ext2_fs_t *ext2_fs_init_ex(kos_blockdev_t *bd, uint32_t flags, int cache_sz); int ext2_fs_sync(ext2_fs_t *fs); void ext2_fs_shutdown(ext2_fs_t *fs); diff --git a/addons/libkosext2fs/ext2internal.h b/addons/libkosext2fs/ext2internal.h index 8624a38..20808cd 100644 --- a/addons/libkosext2fs/ext2internal.h +++ b/addons/libkosext2fs/ext2internal.h @@ -37,6 +37,7 @@ struct ext2fs_struct { int cache_size; uint32_t flags; + uint32_t mnt_flags; }; /* The superblock and/or block descriptors need to be written to the block diff --git a/addons/libkosext2fs/fs_ext2.c b/addons/libkosext2fs/fs_ext2.c index fcb3f74..a3ac19b 100644 --- a/addons/libkosext2fs/fs_ext2.c +++ b/addons/libkosext2fs/fs_ext2.c @@ -20,6 +20,9 @@ #include "inode.h" #include "directory.h" +/* For some reason, Newlib doesn't seem to define this function in stdlib.h. */ +extern char *realpath(const char *, const char *); + #define MAX_EXT2_FILES 16 typedef struct fs_ext2_fs { @@ -164,8 +167,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))) { + if(!(block = ext2_inode_read_block(fs, fh[fd].inode, fh[fd].ptr >> lbs, + NULL))) { mutex_unlock(&ext2_mutex); errno = EBADF; return -1; @@ -186,8 +189,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))) { + if(!(block = ext2_inode_read_block(fs, fh[fd].inode, fh[fd].ptr >> lbs, + NULL))) { mutex_unlock(&ext2_mutex); errno = EBADF; return -1; @@ -314,7 +317,8 @@ retry: return NULL; } - if(!(block = ext2_inode_read_block(fs, fh[fd].inode, fh[fd].ptr >> lbs))) { + if(!(block = ext2_inode_read_block(fs, fh[fd].inode, fh[fd].ptr >> lbs, + NULL))) { mutex_unlock(&ext2_mutex); errno = EBADF; return NULL; @@ -361,6 +365,136 @@ retry: return &fh[fd].dent; } +static int fs_ext2_unlink(vfs_handler_t *vfs, const char *fn) { + fs_ext2_fs_t *fs = (fs_ext2_fs_t *)vfs->privdata; + int irv; + ext2_inode_t *pinode, *inode; + uint32_t inode_num; + ext2_dirent_t *dent; + char *cp, *ent; + uint32_t in_num; + + /* Make sure there is a filename given */ + if(!fn) { + errno = ENOENT; + return -1; + } + + /* Make sure we're not trying to remove the root of the filesystem. */ + if(!*fn) { + errno = EPERM; + return -1; + } + + /* Make sure the fs is writable */ + if(!(fs->mount_flags & FS_EXT2_MOUNT_READWRITE)) { + errno = EROFS; + return -1; + } + + /* Make a writable copy of the filename. */ + if(!(cp = strdup(fn))) { + errno = ENOMEM; + return -1; + } + + /* Separate our copy into the parent and the file we want to remove. */ + if(!(ent = strrchr(cp, '/'))) { + free(cp); + errno = EPERM; + return -1; + } + + /* Split the string. */ + *ent++ = 0; + + mutex_lock(&ext2_mutex); + + /* Find the parent directory of the object in question.*/ + if((irv = ext2_inode_by_path(fs->fs, cp, &pinode, &inode_num, 1, NULL))) { + mutex_unlock(&ext2_mutex); + free(cp); + errno = -irv; + return -1; + } + + /* If the entry we get back is not a directory, then we've got problems. */ + if((pinode->i_mode & 0xF000) != EXT2_S_IFDIR) { + ext2_inode_put(pinode); + mutex_unlock(&ext2_mutex); + free(cp); + errno = ENOTDIR; + return -1; + } + + /* Try to find the directory entry of the item we want to remove. */ + if(!(dent = ext2_dir_entry(fs->fs, pinode, ent))) { + ext2_inode_put(pinode); + mutex_unlock(&ext2_mutex); + free(cp); + errno = ENOENT; + return -1; + } + + /* Find the inode of the entry we want to remove. */ + if(!(inode = ext2_inode_get(fs->fs, dent->inode, &irv))) { + ext2_inode_put(pinode); + mutex_unlock(&ext2_mutex); + free(cp); + errno = EIO; + return -1; + } + + /* Make sure we don't try to remove a directory with unlink. That is what + rmdir is for. */ + if((inode->i_mode & 0xF000) == EXT2_S_IFDIR) { + ext2_inode_put(pinode); + ext2_inode_put(inode); + mutex_unlock(&ext2_mutex); + free(cp); + errno = EPERM; + return -1; + } + + /* Make sure we don't have any open file descriptors to the file. */ + for(irv = 0; irv < MAX_EXT2_FILES; ++irv) { + if(fh[irv].inode_num == dent->inode) { + ext2_inode_put(pinode); + ext2_inode_put(inode); + mutex_unlock(&ext2_mutex); + free(cp); + errno = EBUSY; + return -1; + } + } + + /* Remove the entry from the parent's directory. */ + if((irv = ext2_dir_rm_entry(fs->fs, pinode, ent, &in_num))) { + ext2_inode_put(pinode); + ext2_inode_put(inode); + mutex_unlock(&ext2_mutex); + free(cp); + errno = -irv; + return -1; + } + + /* We're done with these now, so clean them up. */ + ext2_inode_put(pinode); + ext2_inode_put(inode); + free(cp); + + /* Free up the inode and all the data blocks. */ + if((irv = ext2_inode_deref(fs->fs, in_num, 0))) { + mutex_unlock(&ext2_mutex); + errno = -irv; + return -1; + } + + /* And, we're done. Unlock the mutex. */ + mutex_unlock(&ext2_mutex); + return 0; +} + static int fs_ext2_stat(vfs_handler_t *vfs, const char *fn, stat_t *rv) { fs_ext2_fs_t *fs = (fs_ext2_fs_t *)vfs->privdata; int irv; @@ -426,6 +560,140 @@ static int fs_ext2_stat(vfs_handler_t *vfs, const char *fn, stat_t *rv) { return 0; } +static int fs_ext2_rmdir(vfs_handler_t *vfs, const char *fn) { + fs_ext2_fs_t *fs = (fs_ext2_fs_t *)vfs->privdata; + int irv; + ext2_inode_t *pinode, *inode; + uint32_t inode_num; + ext2_dirent_t *dent; + char *cp, *ent; + uint32_t in_num; + + /* Make sure there is a filename given */ + if(!fn) { + errno = ENOENT; + return -1; + } + + /* Make sure we're not trying to remove the root of the filesystem. */ + if(!*fn || (fn[0] == '/' && !fn[1])) { + errno = EPERM; + return -1; + } + + /* Make sure the fs is writable */ + if(!(fs->mount_flags & FS_EXT2_MOUNT_READWRITE)) { + errno = EROFS; + return -1; + } + + /* Make a writable copy of the filename. */ + if(!(cp = strdup(fn))) { + errno = ENOMEM; + return -1; + } + + /* Separate our copy into the parent and the file we want to remove. */ + if(!(ent = strrchr(cp, '/'))) { + free(cp); + errno = EPERM; + return -1; + } + + /* Split the string. */ + *ent++ = 0; + + mutex_lock(&ext2_mutex); + + /* Find the parent directory of the object in question.*/ + if((irv = ext2_inode_by_path(fs->fs, cp, &pinode, &inode_num, 1, NULL))) { + mutex_unlock(&ext2_mutex); + free(cp); + errno = -irv; + return -1; + } + + /* If the entry we get back is not a directory, then we've got problems. */ + if((pinode->i_mode & 0xF000) != EXT2_S_IFDIR) { + ext2_inode_put(pinode); + mutex_unlock(&ext2_mutex); + free(cp); + errno = ENOTDIR; + return -1; + } + + /* Try to find the directory entry of the item we want to remove. */ + if(!(dent = ext2_dir_entry(fs->fs, pinode, ent))) { + ext2_inode_put(pinode); + mutex_unlock(&ext2_mutex); + free(cp); ...<truncated>... hooks/post-receive -- A pseudo Operating System for the Dreamcast. |