fs: fat: support unlink
authorAKASHI Takahiro <takahiro.akashi@linaro.org>
Tue, 11 Sep 2018 06:59:14 +0000 (15:59 +0900)
committerAlexander Graf <agraf@suse.de>
Sun, 23 Sep 2018 19:55:30 +0000 (21:55 +0200)
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 <takahiro.akashi@linaro.org>
Signed-off-by: Alexander Graf <agraf@suse.de>
fs/fat/fat_write.c
fs/fs.c
include/fat.h

index 035469f31c8d0c9e2c855eb59e1a48189cde7456..6d3d2d1abb04836f3b57a7749031e7041419653a 100644 (file)
@@ -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 ba9a65166c705b5bbad609bc09bf2c338087bd4c..adae98d021eef1b2c4640a12aef9b0affa43b9d1 100644 (file)
--- 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
index 0fe3eaa8f7c7a7117b180f3cad88963b87f5d275..bc139f8c88c8c7a785e016aa5f33cbdf936b0f6d 100644 (file)
@@ -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_ */