From: Lawrence S. <ljs...@us...> - 2019-08-14 02:06:03
|
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 1dddfafba71d8532b50c31a3e30339e1856478b9 (commit) from 1b79175ac39ec0d7e66c6416ce6c6842fc380b3c (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 1dddfafba71d8532b50c31a3e30339e1856478b9 Author: Lawrence Sebald <ljs...@us...> Date: Tue Aug 13 22:03:01 2019 -0400 libkosfat: Add initial implementation of mkdir. ----------------------------------------------------------------------- Summary of changes: addons/libkosfat/directory.c | 140 +++++++++++++++++++++++++++++++++++++++++++ addons/libkosfat/directory.h | 3 + addons/libkosfat/fatfs.c | 42 +++++++++++++ addons/libkosfat/fatfs.h | 1 + addons/libkosfat/fs_fat.c | 106 +++++++++++++++++++++++++++++++- 5 files changed, 289 insertions(+), 3 deletions(-) diff --git a/addons/libkosfat/directory.c b/addons/libkosfat/directory.c index 6c10782..40a74a9 100644 --- a/addons/libkosfat/directory.c +++ b/addons/libkosfat/directory.c @@ -747,6 +747,146 @@ int fat_is_dir_empty(fat_fs_t *fs, uint32_t cluster) { return 1; } +static int fat_get_free_dentry(fat_fs_t *fs, uint32_t cluster, uint32_t *rcl, + uint32_t *roff, fat_dentry_t **rv) { + uint8_t *cl; + int err, done = 0; + uint32_t i, j = 0, max, old; + fat_dentry_t *ent; + + /* Figure out how many directory entries there are in each cluster/block. */ + if(fs->sb.fs_type == FAT_FS_FAT32 || !(cluster & 0x80000000)) { + /* Either we're working with a regular directory or we're working with + the root directory on FAT32 (which is the same as a normal + directory). We care about the number of entries per cluster. */ + max = (fs->sb.bytes_per_sector * fs->sb.sectors_per_cluster) >> 5; + } + else { + /* We're working with the root directory on FAT12/FAT16, so we need to + look at the number of entries per sector. */ + max = fs->sb.bytes_per_sector >> 5; + } + + while(!done) { + if(!(cl = fat_cluster_read(fs, cluster, &err))) { + dbglog(DBG_ERROR, "Error reading directory at cluster %" PRIu32 + ": %s\n", cluster, strerror(err)); + return -EIO; + } + + for(i = 0; i < max && !done; ++i, ++j) { + ent = (fat_dentry_t *)(cl + (i << 5)); + + /* If name[0] is zero, then we've hit the end of the directory. */ + if(ent->name[0] == FAT_ENTRY_EOD) { + *rv = ent; + *rcl = cluster; + *roff = i << 5; + return 0; + } + /* If name[0] == 0xE5, then this entry is empty (but there might + still be additional entries after it). */ + else if(ent->name[0] == FAT_ENTRY_FREE) { + *rv = ent; + *rcl = cluster; + *roff = i << 5; + return 0; + } + + /* Just ignore anything else... */ + } + + if(!(cluster & 0x80000000)) { + old = cluster; + cluster = fat_read_fat(fs, old, &err); + if(cluster == 0xFFFFFFFF) + return -err; + + if(fat_is_eof(fs, cluster)) + done = 1; + } + else { + ++cluster; + + if(j >= fs->sb.root_dir) { + *rv = NULL; + return -ENOSPC; + } + } + } + + /* If we get here, that means we've run out of space in what's allocated + for the directory (and it's not the end of the FAT12/FAT16 root). + Attempt to allocate a new cluster, clear it out, and return a pointer to + the beginning of it. */ + if((j = fat_allocate_cluster(fs, &err)) == FAT_INVALID_CLUSTER) { + dbglog(DBG_ERROR, "Error allocating directory cluster: %s\n", + strerror(err)); + *rv = NULL; + return err; + } + + /* Update the FAT chain. */ + if((err = fat_write_fat(fs, old, j)) < 0) { + dbglog(DBG_ERROR, "Error writing fat for new allocation: %s\n", + strerror(err)); + fat_write_fat(fs, j, 0); + *rv = NULL; + return -err; + } + + /* Clear the new block and return a pointer to the beginning of it. */ + if(!(cl = fat_cluster_clear(fs, j, &err))) { + fat_write_fat(fs, j, 0); + /* This will get properly truncated for FAT12/FAT16. */ + fat_write_fat(fs, old, 0x0FFFFFFF); + *rv = NULL; + return err; + } + + *rv = (fat_dentry_t *)cl; + *rcl = j; + *roff = 0; + return 0; +} + +int fat_add_dentry(fat_fs_t *fs, const char *fn, fat_dentry_t *parent, + uint8_t attr, uint32_t cluster, uint32_t *rcl, + uint32_t *roff, uint32_t *rlcl, uint32_t *rloff) { + fat_dentry_t *dent; + int err; + uint32_t cl; + char comp[11]; + + /* XXXX: For now, this only supports short entries... */ + if(is_component_short(fn)) { + normalize_shortname(fn, comp); + cl = parent->cluster_low | (parent->cluster_high << 16); + + if((err = fat_get_free_dentry(fs, cl, rcl, roff, &dent)) < 0) + return -err; + + /* Clear it. */ + memset(dent, 0, sizeof(fat_dentry_t)); + + /* Fill in what we care about. */ + memcpy(dent->name, comp, 11); + dent->attr = attr; + dent->cluster_high = (uint16_t)(cluster >> 16); + dent->cluster_low = (uint16_t)cluster; + /* XXXX: Fill in timestamps... */ + + /* Clean up... */ + *rlcl = 0; + *rloff = 0; + fat_cluster_mark_dirty(fs, *rcl); + return 0; + } + else { + return -ENAMETOOLONG; + } +} + #ifdef FAT_DEBUG void fat_dentry_print(const fat_dentry_t *ent) { uint32_t cl = ent->cluster_low | (ent->cluster_high << 16); diff --git a/addons/libkosfat/directory.h b/addons/libkosfat/directory.h index 1ce9d71..3e45929 100644 --- a/addons/libkosfat/directory.h +++ b/addons/libkosfat/directory.h @@ -67,6 +67,9 @@ int fat_find_child(fat_fs_t *fs, const char *fn, fat_dentry_t *parent, int fat_erase_dentry(fat_fs_t *fs, uint32_t cl, uint32_t off, uint32_t lcl, uint32_t loff); int fat_is_dir_empty(fat_fs_t *fs, uint32_t cluster); +int fat_add_dentry(fat_fs_t *fs, const char *fn, fat_dentry_t *parent, + uint8_t attr, uint32_t cluster, uint32_t *rcl, + uint32_t *roff, uint32_t *rlcl, uint32_t *rloff); #ifdef FAT_DEBUG void fat_dentry_print(const fat_dentry_t *ent); diff --git a/addons/libkosfat/fatfs.c b/addons/libkosfat/fatfs.c index 83700df..3e45880 100644 --- a/addons/libkosfat/fatfs.c +++ b/addons/libkosfat/fatfs.c @@ -81,6 +81,48 @@ out: return rv; } +uint8_t *fat_cluster_clear(fat_fs_t *fs, uint32_t cl, int *err) { + int i; + uint8_t *rv; + fat_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 == cl && cache[i]->flags) { + rv = cache[i]->data; + make_mru(fs, cache, i); + goto out; + } + } + + /* If we didn't get anything, did we end up with an invalid entry or do we + need to boot someone out? */ + if(i < 0) { + i = 0; + + /* Make sure that if the block is dirty, we write it back out. */ + if(cache[0]->flags & FAT_CACHE_FLAG_DIRTY) { + if(fat_cluster_write_nc(fs, cache[0]->block, cache[0]->data)) { + /* XXXX: Uh oh... */ + *err = EIO; + return NULL; + } + } + } + + /* Don't bother reading the cluster from disk, since we're erasing it + anyway... */ + cache[i]->block = cl; + cache[i]->flags = FAT_CACHE_FLAG_VALID | FAT_CACHE_FLAG_DIRTY; + rv = cache[i]->data; + make_mru(fs, cache, i); + +out: + memset(rv, 0, fs->sb.bytes_per_sector * fs->sb.sectors_per_cluster); + return rv; +} + int fat_cluster_read_nc(fat_fs_t *fs, uint32_t cluster, uint8_t *rv) { int fs_per_block = (int)fs->sb.sectors_per_cluster; diff --git a/addons/libkosfat/fatfs.h b/addons/libkosfat/fatfs.h index dfad699..3ade2d0 100644 --- a/addons/libkosfat/fatfs.h +++ b/addons/libkosfat/fatfs.h @@ -85,6 +85,7 @@ void fat_fs_shutdown(fat_fs_t *fs); int fat_cluster_read_nc(fat_fs_t *fs, uint32_t cluster, uint8_t *rv); uint8_t *fat_cluster_read(fat_fs_t *fs, uint32_t cluster, int *err); +uint8_t *fat_cluster_clear(fat_fs_t *fs, uint32_t cl, int *err); int fat_cluster_write_nc(fat_fs_t *fs, uint32_t cluster, const uint8_t *blk); diff --git a/addons/libkosfat/fs_fat.c b/addons/libkosfat/fs_fat.c index 3abed6e..a019888 100644 --- a/addons/libkosfat/fs_fat.c +++ b/addons/libkosfat/fs_fat.c @@ -614,7 +614,7 @@ static int fs_fat_fcntl(void *h, int cmd, va_list ap) { return rv; } -static int fs_fat_unlink(struct vfs_handler *vfs, const char *fn) { +static int fs_fat_unlink(vfs_handler_t *vfs, const char *fn) { fs_fat_fs_t *fs = (fs_fat_fs_t *)vfs->privdata; fat_dentry_t ent; int irv = 0, err; @@ -737,7 +737,107 @@ static int fs_fat_stat(vfs_handler_t *vfs, const char *path, struct stat *buf, return irv; } -static int fs_fat_rmdir(struct vfs_handler *vfs, const char *fn) { +static int fs_fat_mkdir(vfs_handler_t *vfs, const char *fn) { + fs_fat_fs_t *fs = (fs_fat_fs_t *)vfs->privdata; + char *parent_fn, *newdir_fn; + fat_dentry_t p_ent, n_ent; + int err; + uint32_t cl, off, lcl, loff, cl2; + + mutex_lock(&fat_mutex); + + /* Make sure the filesystem isn't mounted read-only. */ + if(!(fs->mount_flags & FS_FAT_MOUNT_READWRITE)) { + mutex_unlock(&fat_mutex); + errno = EROFS; + return -1; + } + + /* Make a copy of the filename, as we're gonna split it into two... */ + if(!(parent_fn = strdup(fn))) { + mutex_unlock(&fat_mutex); + errno = ENOMEM; + return -1; + } + + /* Figure out where the new directory's name starts in the string... */ + newdir_fn = strrchr(parent_fn, '/'); + if(newdir_fn == parent_fn || !newdir_fn) { + /* If it's at the beginning, or non-existent, then the user is trying + to mkdir the root directory, which obviously already exists. */ + mutex_unlock(&fat_mutex); + free(parent_fn); + errno = EEXIST; + return -1; + } + + /* Split the string. */ + *newdir_fn++ = 0; + + /* Find the parent's dentry. */ + if((err = fat_find_dentry(fs->fs, parent_fn, &p_ent, &cl, &off, &lcl, + &loff)) < 0) { + mutex_unlock(&fat_mutex); + free(parent_fn); + errno = -err; + return -1; + } + + /* Make sure the parent is actually a directory. */ + if(!(p_ent.attr & FAT_ATTR_DIRECTORY)) { + mutex_unlock(&fat_mutex); + free(parent_fn); + errno = ENOTDIR; + return -1; + } + + /* Make sure the child doeesn't exist. */ + if((err = fat_find_child(fs->fs, newdir_fn, &p_ent, &n_ent, &cl, &off, &lcl, + &loff)) != -ENOENT) { + mutex_unlock(&fat_mutex); + free(parent_fn); + if(err) + errno = -err; + else + errno = EEXIST; + return -1; + } + + /* Allocate a cluster to store the directory in. */ + if((cl = fat_allocate_cluster(fs->fs, &err)) == FAT_INVALID_CLUSTER) { + mutex_unlock(&fat_mutex); + free(parent_fn); + errno = err; + return -1; + } + + /* Clear the target cluster on the disk (well, in the cache, anyway). */ + if(!(fat_cluster_clear(fs->fs, cl, &err))) { + /* Uh oh... Now things start becoming bad if things fail... */ + fat_erase_chain(fs->fs, cl); + mutex_unlock(&fat_mutex); + free(parent_fn); + errno = -err; + return -1; + } + + /* Add the dentry to the parent. */ + if((err = fat_add_dentry(fs->fs, newdir_fn, &p_ent, FAT_ATTR_DIRECTORY, + cl, &cl2, &off, &lcl, &loff)) < 0) { + fat_erase_chain(fs->fs, cl); + mutex_unlock(&fat_mutex); + free(parent_fn); + errno = -err; + return -1; + } + + /* And we're done... Clean up. */ + mutex_unlock(&fat_mutex); + free(parent_fn); + return 0; +} + +static int fs_fat_rmdir(vfs_handler_t *vfs, const char *fn) { fs_fat_fs_t *fs = (fs_fat_fs_t *)vfs->privdata; fat_dentry_t ent; int irv = 0, err; @@ -923,7 +1023,7 @@ static vfs_handler_t vh = { NULL, /* mmap */ NULL, /* complete */ fs_fat_stat, /* stat */ - NULL, /* mkdir */ + fs_fat_mkdir, /* mkdir */ fs_fat_rmdir, /* rmdir */ fs_fat_fcntl, /* fcntl */ NULL, /* poll */ hooks/post-receive -- A pseudo Operating System for the Dreamcast. |