#include #include #include #include #include #include #include #include #include "dir.h" #include "path.h" #include "tree.h" char *mufs_abspath(const char *path) { char *s, *p, *next, *prev; if (!path) return "/"; if (!(s = calloc(strlen(path) + 2, sizeof *s))) return NULL; strcpy(s, path); for (p = s, next = p + strcspn(p, "/"); *p != '\0'; next = p + strcspn(p, "/")) { char c = *next; *next = '\0'; if (*p == '\0' || strcmp(p, ".") == 0) { memmove(p, next + 1, strlen(next + 1) + 1); continue; } if (strcmp(p, "..") == 0) { if (!(prev = strrchr(s, '/'))) { memmove(p, next + 1, strlen(next + 1) + 1); continue; } *prev = '\0'; if (!(prev = strrchr(s, '/'))) { memmove(s, next + 1, strlen(next + 1) + 1); p = s; continue; } memmove(prev + 1, next + 1, strlen(next + 1) + 1); p = prev + 1; continue; } *next = c; p = next + 1; } memmove(s + 1, s, strlen(s) + 1); *s = '/'; return s; } static struct mufs_dir *open_dirent(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; } int resolve_path(struct mufs *fs, const char *path, struct mufs_tree **tree, struct mufs_stat *stat) { struct mufs_dir *dir = NULL; struct mufs_dirent entry; char *s, *p, *end; int ret; if (!(s = mufs_abspath(path))) return -1; if (strcmp(s, "/") == 0) { if (stat) { stat->type = MUFS_DIR; stat->file_size = fs->root.file_size; } if (tree) *tree = &fs->root; free(s); return 0; } if (!(dir = open_dirent(fs, NULL))) goto err_free_s; for (p = s + 1; *p != '\0'; p = end) { end = p + strcspn(p, "/"); if (*end == '/') *end++ = '\0'; while ((ret = mufs_readdir(dir, &entry)) == 0) { if (strcmp(entry.path, p) == 0) break; } mufs_closedir(dir); if (ret < 0) goto err_free_s; if (!(dir = open_dirent(fs, &entry))) goto err_free_s; } if (!dir) goto err_free_s; if (stat) { stat->type = entry.type; stat->file_size = dir->tree->file_size; } if (tree) { *tree = dir->tree; dir->tree = NULL; } mufs_closedir(dir); free(s); return 0; err_free_s: free(s); return -1; } struct mufs_dir *mufs_opendir(struct mufs *fs, const char *path) { struct mufs_stat stat; struct mufs_dir *dir; if (!(dir = malloc(sizeof(*dir)))) return NULL; dir->fs = fs; if (resolve_path(fs, path, &dir->tree, &stat) < 0 || stat.type != MUFS_DIR) 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; mufs_del_tree(dir->tree); free(dir); } size_t find_dirent_size(struct mufs *fs, struct mufs_tree *tree, uint32_t va) { struct flash_dev *dev = fs->dev; size_t block_size = 1 << dev->log2_block_size; struct mufs_dentry entry; uint32_t offset, len; va = align(va, dev->log2_block_size); for (offset = 0; offset < block_size; offset += len) { len = sizeof entry; if (block_size - offset < len) break; if (mufs_tree_read(tree, &entry, va + offset, sizeof entry) == 0) break; if (!entry.type) break; len += entry.path_len; if (block_size - offset < len) break; } return offset; } size_t read_dirent(struct mufs *fs, struct mufs_tree *tree, struct mufs_dirent *dirent, uint32_t va) { struct flash_dev *dev = fs->dev; struct mufs_dentry entry; size_t len, block_size = 1 << dev->log2_block_size; uint32_t offset; offset = va & ((UINT32_C(1) << dev->log2_block_size) - 1); va = align(va, dev->log2_block_size); if (block_size - offset < sizeof entry) return 0; if (mufs_tree_read(tree, &entry, va + offset, sizeof entry) == 0) return 0; if (!entry.type) return 0; if (block_size - offset < sizeof entry + entry.path_len) return 0; len = min(entry.path_len, sizeof dirent->path - 1); if (mufs_tree_read(tree, dirent->path, va + offset + sizeof entry, len) == 0) return 0; dirent->path[len] = '\0'; dirent->type = entry.type; if (mufs_lookup_page(tree, &dirent->tree.va, va) < 0) return 0; dirent->tree.fs = fs; dirent->tree.va = (dirent->tree.va << dev->log2_block_size) + offset + offsetof(struct mufs_dentry, tree); dirent->tree.file_size = entry.tree.file_size; dirent->tree.root = entry.tree.root; dirent->tree.depth = entry.tree.depth; return sizeof entry + entry.path_len; } size_t write_dirent(struct mufs_tree *tree, uint32_t va, struct mufs_dirent *dirent) { struct mufs *fs = tree->fs; struct flash_dev *dev = fs->dev; struct mufs_dentry entry; size_t block_size = 1 << dev->log2_block_size; uint32_t offset; offset = va & ((UINT32_C(1) << dev->log2_block_size) - 1); va = align(va, dev->log2_block_size); entry.type = dirent->type; entry.tree.file_size = dirent->tree.file_size; entry.tree.root = dirent->tree.root; entry.tree.depth = dirent->tree.depth; entry.path_len = strlen(dirent->path); if (block_size - offset < sizeof entry + entry.path_len) return 0; if (mufs_tree_write(tree, dirent->path, va + offset + sizeof entry, entry.path_len) == 0) return 0; if (mufs_tree_write(tree, &entry, va + offset, sizeof entry) == 0) return 0; va += offset + sizeof entry + entry.path_len; tree->file_size = max(tree->file_size, va); if (tree->file_size <= va && mufs_sync_tree(tree) < 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 (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; } return -1; } int mufs_mkdir(struct mufs *fs, const char *path) { return mufs_mkpath(fs, path, NULL, MUFS_DIR); } int mufs_rmdir(struct mufs *fs, const char *path) { struct mufs_stat stat; if (mufs_stat(fs, path, &stat) < 0) return -1; if (stat.type != MUFS_DIR || stat.file_size != 0) return -1; return mufs_rmpath(fs, path); }