You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
345 lines
6.4 KiB
345 lines
6.4 KiB
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <flash.h>
|
|
#include <macros.h>
|
|
#include <mufs.h>
|
|
|
|
#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);
|
|
}
|
|
|