From f8240ce95d64dbe3a1eb6d5001ba23107c7e7cfe Mon Sep 17 00:00:00 2001 From: AKASHI Takahiro Date: Tue, 11 Sep 2018 15:59:14 +0900 Subject: [PATCH] fs: fat: support unlink In this patch, unlink support is added to FAT file system. A directory can be deleted only if it is empty. In this implementation, only a directory entry for a short file name will be removed. So entries for a long file name can and should be reclaimed with fsck. Signed-off-by: AKASHI Takahiro Signed-off-by: Alexander Graf --- fs/fat/fat_write.c | 132 +++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/fs.c | 3 +- include/fat.h | 1 + 3 files changed, 135 insertions(+), 1 deletion(-) diff --git a/fs/fat/fat_write.c b/fs/fat/fat_write.c index 035469f..6d3d2d1 100644 --- a/fs/fat/fat_write.c +++ b/fs/fat/fat_write.c @@ -1184,6 +1184,138 @@ int file_fat_write(const char *filename, void *buffer, loff_t offset, return file_fat_write_at(filename, offset, buffer, maxsize, actwrite); } +static int fat_dir_entries(fat_itr *itr) +{ + fat_itr *dirs; + fsdata fsdata = { .fatbuf = NULL, }, *mydata = &fsdata; + /* for FATBUFSIZE */ + int count; + + dirs = malloc_cache_aligned(sizeof(fat_itr)); + if (!dirs) { + debug("Error: allocating memory\n"); + count = -ENOMEM; + goto exit; + } + + /* duplicate fsdata */ + fat_itr_child(dirs, itr); + fsdata = *dirs->fsdata; + + /* allocate local fat buffer */ + fsdata.fatbuf = malloc_cache_aligned(FATBUFSIZE); + if (!fsdata.fatbuf) { + debug("Error: allocating memory\n"); + count = -ENOMEM; + goto exit; + } + fsdata.fatbufnum = -1; + dirs->fsdata = &fsdata; + + for (count = 0; fat_itr_next(dirs); count++) + ; + +exit: + free(fsdata.fatbuf); + free(dirs); + return count; +} + +static int delete_dentry(fat_itr *itr) +{ + fsdata *mydata = itr->fsdata; + dir_entry *dentptr = itr->dent; + + /* free cluster blocks */ + clear_fatent(mydata, START(dentptr)); + if (flush_dirty_fat_buffer(mydata) < 0) { + printf("Error: flush fat buffer\n"); + return -EIO; + } + + /* + * update a directory entry + * TODO: + * - long file name support + * - find and mark the "new" first invalid entry as name[0]=0x00 + */ + memset(dentptr, 0, sizeof(*dentptr)); + dentptr->name[0] = 0xe5; + + if (set_cluster(mydata, itr->clust, itr->block, + mydata->clust_size * mydata->sect_size) != 0) { + printf("error: writing directory entry\n"); + return -EIO; + } + + return 0; +} + +int fat_unlink(const char *filename) +{ + fsdata fsdata = { .fatbuf = NULL, }; + fat_itr *itr = NULL; + int n_entries, ret; + char *filename_copy, *dirname, *basename; + + filename_copy = strdup(filename); + split_filename(filename_copy, &dirname, &basename); + + if (!strcmp(dirname, "/") && !strcmp(basename, "")) { + printf("Error: cannot remove root\n"); + ret = -EINVAL; + goto exit; + } + + itr = malloc_cache_aligned(sizeof(fat_itr)); + if (!itr) { + printf("Error: allocating memory\n"); + return -ENOMEM; + } + + ret = fat_itr_root(itr, &fsdata); + if (ret) + goto exit; + + total_sector = fsdata.total_sect; + + ret = fat_itr_resolve(itr, dirname, TYPE_DIR); + if (ret) { + printf("%s: doesn't exist (%d)\n", dirname, ret); + ret = -ENOENT; + goto exit; + } + + if (!find_directory_entry(itr, basename)) { + printf("%s: doesn't exist\n", basename); + ret = -ENOENT; + goto exit; + } + + if (fat_itr_isdir(itr)) { + n_entries = fat_dir_entries(itr); + if (n_entries < 0) { + ret = n_entries; + goto exit; + } + if (n_entries > 2) { + printf("Error: directory is not empty: %d\n", + n_entries); + ret = -EINVAL; + goto exit; + } + } + + ret = delete_dentry(itr); + +exit: + free(fsdata.fatbuf); + free(itr); + free(filename_copy); + + return ret; +} + int fat_mkdir(const char *new_dirname) { dir_entry *retdent; diff --git a/fs/fs.c b/fs/fs.c index ba9a651..adae98d 100644 --- a/fs/fs.c +++ b/fs/fs.c @@ -170,16 +170,17 @@ static struct fstype_info fstypes[] = { .read = fat_read_file, #ifdef CONFIG_FAT_WRITE .write = file_fat_write, + .unlink = fat_unlink, .mkdir = fat_mkdir, #else .write = fs_write_unsupported, + .unlink = fs_unlink_unsupported, .mkdir = fs_mkdir_unsupported, #endif .uuid = fs_uuid_unsupported, .opendir = fat_opendir, .readdir = fat_readdir, .closedir = fat_closedir, - .unlink = fs_unlink_unsupported, }, #endif #ifdef CONFIG_FS_EXT4 diff --git a/include/fat.h b/include/fat.h index 0fe3eaa..bc139f8 100644 --- a/include/fat.h +++ b/include/fat.h @@ -203,6 +203,7 @@ int fat_read_file(const char *filename, void *buf, loff_t offset, loff_t len, int fat_opendir(const char *filename, struct fs_dir_stream **dirsp); int fat_readdir(struct fs_dir_stream *dirs, struct fs_dirent **dentp); void fat_closedir(struct fs_dir_stream *dirs); +int fat_unlink(const char *filename); int fat_mkdir(const char *dirname); void fat_close(void); #endif /* _FAT_H_ */