#include #include #include #include #include #include #include #include "dir.h" #include "tree.h" struct mufs_dir { struct mufs *fs; struct mufs_tree *tree; uint32_t va; }; static struct mufs_dir *mufs_opendir2(struct mufs *fs, struct mufs_dirent *entry) { struct mufs_dir *dir; if (!(dir = malloc(sizeof *dir))) return NULL; dir->fs = fs; dir->va = 0; if (entry) { if (!(dir->tree = malloc(sizeof *dir->tree))) goto err_free_dir; memcpy(dir->tree, &entry->tree, sizeof *dir->tree); } else { dir->tree = &fs->root; } return dir; err_free_dir: free(dir); return NULL; } static struct mufs_tree *resolve_path(struct mufs *fs, const char *path) { struct mufs_dir *dir; struct mufs_dirent entry; struct mufs_tree *tree; char *s, *p, *end; int ret; if (!path || *path == '\0') return NULL; while (*path == '/') ++path; if (*path == '\0') return &fs->root; if (!(s = strdup(path))) return NULL; if (!(dir = mufs_opendir2(fs, NULL))) goto err_free_s; for (p = s; *p != '\0'; p = end) { end = p + strcspn(p, "/"); while (*end == '/') *end++ = '\0'; while ((ret = mufs_readdir(dir, &entry)) == 0) { if (strcmp(entry.path, p) != 0) continue; mufs_closedir(dir); if (!(dir = mufs_opendir2(fs, &entry))) goto err_free_s; break; } if (ret < 0) goto err_close_dir; } tree = dir->tree; mufs_closedir(dir); return tree; err_close_dir: mufs_closedir(dir); err_free_s: free(s); return NULL; } struct mufs_dir *mufs_opendir(struct mufs *fs, const char *path) { struct mufs_dir *dir; if (!(dir = malloc(sizeof(*dir)))) return NULL; dir->fs = fs; if (!(dir->tree = resolve_path(fs, path))) goto err_free_dir; dir->va = 0; return dir; err_free_dir: free(dir); return NULL; } void mufs_closedir(struct mufs_dir *dir) { if (!dir) return; free(dir); } static size_t read_dirent(struct mufs *fs, struct mufs_tree *tree, struct mufs_dirent *dirent, uint32_t va) { struct mufs_dentry entry; size_t ret = 0; size_t len; uint32_t base = va; if (va + sizeof(entry) > tree->file_size) return 0; if (mufs_read(tree, &entry, va, sizeof entry) == 0) return 0; if (!entry.type) return 0; ret += sizeof entry; va += sizeof entry; len = min(entry.path_len, sizeof(dirent->path - 1)); if (va + len > tree->file_size) return 0; if (mufs_read(tree, dirent->path, va, len) == 0) return 0; dirent->path[len] = '\0'; ret += len; dirent->type = entry.type; struct flash_dev *dev = fs->dev; if (mufs_lookup_page(tree, &dirent->tree.va, base & ~((UINT32_C(1) << dev->log2_block_size) - 1)) < 0) return 0; dirent->tree.fs = fs; dirent->tree.va |= base & ((UINT32_C(1) << dev->log2_block_size) - 1); dirent->tree.file_size = entry.file_size; dirent->tree.root = entry.root; dirent->tree.depth = entry.depth; return ret; } struct mufs_dtree { uint32_t file_size; uint32_t root; uint8_t depth; } __attribute__((packed)); static size_t write_dirent(struct mufs_tree *tree, uint32_t va, struct mufs_dirent *dirent) { struct mufs_dtree dtree; struct mufs_dentry entry; entry.type = dirent->type; entry.file_size = dirent->tree.file_size; entry.root = dirent->tree.root; entry.depth = dirent->tree.depth; entry.path_len = strlen(dirent->path); if (mufs_write(tree, dirent->path, va + sizeof entry, entry.path_len) == 0) return 0; if (mufs_write(tree, &entry, va, sizeof entry) == 0) return 0; tree->file_size = max(tree->file_size, va + sizeof entry + entry.path_len); dtree.file_size = tree->file_size; dtree.root = tree->root; dtree.depth = tree->depth; if (flash_write(tree->fs->dev, tree->va, &dtree, sizeof dtree) == 0) return 0; return entry.path_len + sizeof entry; } int mufs_readdir(struct mufs_dir *dir, struct mufs_dirent *dirent) { size_t ret; if (!dir || !dirent || dir->va >= dir->tree->file_size) return -1; if ((ret = read_dirent(dir->fs, dir->tree, dirent, dir->va)) > 0) { dir->va += ret; return 0; } dir->va = align_up(dir->va + 1, dir->fs->dev->log2_block_size); if ((ret = read_dirent(dir->fs, dir->tree, dirent, dir->va)) > 0) { dir->va += ret; return 0; } return -1; } int mufs_mkdir(struct mufs *fs, const char *path) { struct mufs_dirent dirent; struct mufs_tree *tree; size_t len; char *s; char *name; if (!path) return -1; /* TODO: already exists. */ if (resolve_path(fs, path)) return 0; if (!(s = strdup(path))) return -1; if (!(name = strrchr(s, '/'))) { name = s; s = NULL; } else { *name++ = '\0'; } if (*name == '\0') return -1; if (!(tree = resolve_path(fs, s))) return -1; len = min(strlen(name), sizeof(dirent.path) - 1); memcpy(dirent.path, name, len); dirent.path[len] = '\0'; dirent.type = MUFS_DIR; dirent.tree.fs = fs; dirent.tree.file_size = 0; dirent.tree.root = 0; dirent.tree.depth = 0; if (!is_aligned(tree->file_size, fs->dev->log2_block_size) && write_dirent(tree, tree->file_size, &dirent) > 0) return 0; if (write_dirent(tree, align_up(tree->file_size, fs->dev->log2_block_size), &dirent) > 0) return 0; return -1; }