#include #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; }; char *mufs_abspath(const char *path) { char *s, *p, *next, *prev; if (!(s = malloc(strlen(path) + 2))) 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 *mufs_opendirent(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; } 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; if (!(s = mufs_abspath(path))) return NULL; if (strcmp(s, "/") == 0) return &fs->root; if (!(dir = mufs_opendirent(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) continue; mufs_closedir(dir); if (!(dir = mufs_opendirent(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); } 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 ret = 0; size_t len; uint32_t base, offset; offset = va & ((UINT32_C(1) << dev->log2_block_size) - 1); base = align(va, dev->log2_block_size); if (va + sizeof(entry) > tree->file_size) return 0; if (mufs_tree_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_tree_read(tree, dirent->path, va, len) == 0) return 0; dirent->path[len] = '\0'; ret += len; dirent->type = entry.type; if (mufs_lookup_page(tree, &dirent->tree.va, base) < 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 ret; } 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.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 (mufs_tree_write(tree, dirent->path, va + sizeof entry, entry.path_len) == 0) return 0; if (mufs_tree_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 || *path == '\0') return -1; if (!(s = mufs_abspath(path))) return -1; /* TODO: already exists. */ if (resolve_path(fs, s)) { free(s); return 0; } if (!(name = strrchr(s, '/'))) { name = s; s = "/"; } 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; }