+
+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 (flush_dir(itr)) {
+ 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);
+ if (!filename_copy) {
+ printf("Error: allocating memory\n");
+ ret = -ENOMEM;
+ goto exit;
+ }
+ 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");
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ 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;
+ fsdata datablock = { .fatbuf = NULL, };
+ fsdata *mydata = &datablock;
+ fat_itr *itr = NULL;
+ char *dirname_copy, *parent, *dirname;
+ char l_dirname[VFAT_MAXLEN_BYTES];
+ int ret = -1;
+ loff_t actwrite;
+ unsigned int bytesperclust;
+ dir_entry *dotdent = NULL;
+
+ dirname_copy = strdup(new_dirname);
+ if (!dirname_copy)
+ goto exit;
+
+ split_filename(dirname_copy, &parent, &dirname);
+ if (!strlen(dirname)) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ if (normalize_longname(l_dirname, dirname)) {
+ printf("FAT: illegal filename (%s)\n", dirname);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ itr = malloc_cache_aligned(sizeof(fat_itr));
+ if (!itr) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ ret = fat_itr_root(itr, &datablock);
+ if (ret)
+ goto exit;
+
+ total_sector = datablock.total_sect;
+
+ ret = fat_itr_resolve(itr, parent, TYPE_DIR);
+ if (ret) {
+ printf("%s: doesn't exist (%d)\n", parent, ret);
+ goto exit;
+ }
+
+ retdent = find_directory_entry(itr, l_dirname);
+
+ if (retdent) {
+ printf("%s: already exists\n", l_dirname);
+ ret = -EEXIST;
+ goto exit;
+ } else {
+ if (itr->is_root) {
+ /* root dir cannot have "." or ".." */
+ if (!strcmp(l_dirname, ".") ||
+ !strcmp(l_dirname, "..")) {
+ ret = -EINVAL;
+ goto exit;
+ }
+ }
+
+ if (!itr->dent) {
+ printf("Error: allocating new dir entry\n");
+ ret = -EIO;
+ goto exit;
+ }
+
+ memset(itr->dent, 0, sizeof(*itr->dent));
+
+ /* Set short name to set alias checksum field in dir_slot */
+ set_name(itr->dent, dirname);
+ fill_dir_slot(itr, dirname);
+
+ /* Set attribute as archive for regular file */
+ fill_dentry(itr->fsdata, itr->dent, dirname, 0, 0,
+ ATTR_DIR | ATTR_ARCH);
+
+ retdent = itr->dent;
+ }
+
+ /* Default entries */
+ bytesperclust = mydata->clust_size * mydata->sect_size;
+ dotdent = malloc_cache_aligned(bytesperclust);
+ if (!dotdent) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+ memset(dotdent, 0, bytesperclust);
+
+ memcpy(dotdent[0].name, ". ", 8);
+ memcpy(dotdent[0].ext, " ", 3);
+ dotdent[0].attr = ATTR_DIR | ATTR_ARCH;
+
+ memcpy(dotdent[1].name, ".. ", 8);
+ memcpy(dotdent[1].ext, " ", 3);
+ dotdent[1].attr = ATTR_DIR | ATTR_ARCH;
+ set_start_cluster(mydata, &dotdent[1], itr->start_clust);
+
+ ret = set_contents(mydata, retdent, 0, (__u8 *)dotdent,
+ bytesperclust, &actwrite);
+ if (ret < 0) {
+ printf("Error: writing contents\n");
+ goto exit;
+ }
+ /* Write twice for "." */
+ set_start_cluster(mydata, &dotdent[0], START(retdent));
+ ret = set_contents(mydata, retdent, 0, (__u8 *)dotdent,
+ bytesperclust, &actwrite);
+ if (ret < 0) {
+ printf("Error: writing contents\n");
+ goto exit;
+ }
+
+ /* Flush fat buffer */
+ ret = flush_dirty_fat_buffer(mydata);
+ if (ret) {
+ printf("Error: flush fat buffer\n");
+ goto exit;
+ }
+
+ /* Write directory table to device */
+ ret = flush_dir(itr);
+ if (ret)
+ printf("Error: writing directory entry\n");
+
+exit:
+ free(dirname_copy);
+ free(mydata->fatbuf);
+ free(itr);
+ free(dotdent);
+ return ret;
+}