#include "backend.h" #include "../platform/memory.h" #include "../platform/time.h" #include "../http/headers.h" #include struct storage_backend { char root_path[PATH_MAX]; size_t root_len; }; storage_backend_t *storage_create(const char *root_path) { storage_backend_t *storage = calloc(1, sizeof(storage_backend_t)); if (!storage) return NULL; char normalized[PATH_MAX]; path_normalize(root_path, normalized, sizeof(normalized)); strncpy(storage->root_path, normalized, sizeof(storage->root_path) - 1); storage->root_len = strlen(storage->root_path); while (storage->root_len > 0 && (storage->root_path[storage->root_len - 1] == '/' || storage->root_path[storage->root_len - 1] == '\\')) { storage->root_path[--storage->root_len] = '\0'; } if (!file_exists(storage->root_path)) { if (file_mkdir_recursive(storage->root_path) != NWEDAV_OK) { free(storage); return NULL; } } return storage; } void storage_destroy(storage_backend_t *storage) { free(storage); } const char *storage_get_root(storage_backend_t *storage) { return storage ? storage->root_path : NULL; } int storage_resolve_path(storage_backend_t *storage, const char *uri, char *path, size_t path_size) { if (!storage || !uri || !path) return NWEDAV_ERROR; char normalized_uri[PATH_MAX]; path_normalize(uri, normalized_uri, sizeof(normalized_uri)); const char *uri_path = normalized_uri; while (*uri_path == '/') uri_path++; if (uri_path[0] == '\0') { strncpy(path, storage->root_path, path_size - 1); path[path_size - 1] = '\0'; } else { snprintf(path, path_size, "%s%c%s", storage->root_path, PATH_SEPARATOR, uri_path); } for (size_t i = 0; path[i]; i++) { if (path[i] == '/' || path[i] == '\\') { path[i] = PATH_SEPARATOR; } } if (!path_is_safe(path, storage->root_path)) { return NWEDAV_ERROR_FORBIDDEN; } return NWEDAV_OK; } int storage_uri_from_path(storage_backend_t *storage, const char *path, char *uri, size_t uri_size) { if (!storage || !path || !uri) return NWEDAV_ERROR; if (strncmp(path, storage->root_path, storage->root_len) != 0) { return NWEDAV_ERROR; } const char *rel = path + storage->root_len; while (*rel == '/' || *rel == '\\') rel++; if (*rel == '\0') { strncpy(uri, "/", uri_size); } else { snprintf(uri, uri_size, "/%s", rel); } for (size_t i = 0; uri[i]; i++) { if (uri[i] == '\\') uri[i] = '/'; } return NWEDAV_OK; } int storage_get_info(storage_backend_t *storage, const char *path, resource_info_t *info) { if (!storage || !path || !info) return NWEDAV_ERROR; char full_path[PATH_MAX]; int ret = storage_resolve_path(storage, path, full_path, sizeof(full_path)); if (ret != NWEDAV_OK) return ret; file_stat_t st; if (file_stat(full_path, &st) != NWEDAV_OK) { return NWEDAV_ERROR_NOT_FOUND; } memset(info, 0, sizeof(resource_info_t)); strncpy(info->path, path, sizeof(info->path) - 1); const char *name = path_basename(full_path); if (name) { strncpy(info->name, name, sizeof(info->name) - 1); } info->is_collection = st.is_dir; info->size = st.size; info->mtime = st.mtime; info->ctime = st.ctime; if (st.is_dir) { strcpy(info->content_type, "httpd/unix-directory"); } else { const char *mime = http_get_mime_type_for_file(info->name); strncpy(info->content_type, mime, sizeof(info->content_type) - 1); } snprintf(info->etag, sizeof(info->etag), "\"%llx-%llx\"", (unsigned long long)st.mtime, (unsigned long long)st.size); return NWEDAV_OK; } int storage_exists(storage_backend_t *storage, const char *path) { if (!storage || !path) return 0; char full_path[PATH_MAX]; if (storage_resolve_path(storage, path, full_path, sizeof(full_path)) != NWEDAV_OK) { return 0; } return file_exists(full_path); } int storage_is_collection(storage_backend_t *storage, const char *path) { if (!storage || !path) return 0; char full_path[PATH_MAX]; if (storage_resolve_path(storage, path, full_path, sizeof(full_path)) != NWEDAV_OK) { return 0; } return file_is_dir(full_path); } int storage_read_file(storage_backend_t *storage, const char *path, char **data, size_t *len) { if (!storage || !path || !data || !len) return NWEDAV_ERROR; char full_path[PATH_MAX]; int ret = storage_resolve_path(storage, path, full_path, sizeof(full_path)); if (ret != NWEDAV_OK) return ret; file_stat_t st; if (file_stat(full_path, &st) != NWEDAV_OK) { return NWEDAV_ERROR_NOT_FOUND; } if (st.is_dir) { return NWEDAV_ERROR_FORBIDDEN; } file_handle_t fh = file_open(full_path, FILE_READ); #ifdef _WIN32 if (fh.handle == INVALID_HANDLE_VALUE) { #else if (fh.fd < 0) { #endif return NWEDAV_ERROR_IO; } *data = malloc(st.size + 1); if (!*data) { file_close(fh); return NWEDAV_ERROR_MEMORY; } ssize_t read_bytes = file_read(fh, *data, st.size); file_close(fh); if (read_bytes < 0) { free(*data); *data = NULL; return NWEDAV_ERROR_IO; } (*data)[read_bytes] = '\0'; *len = (size_t)read_bytes; return NWEDAV_OK; } int storage_write_file(storage_backend_t *storage, const char *path, const char *data, size_t len) { if (!storage || !path) return NWEDAV_ERROR; char full_path[PATH_MAX]; int ret = storage_resolve_path(storage, path, full_path, sizeof(full_path)); if (ret != NWEDAV_OK) return ret; char parent[PATH_MAX]; path_dirname(full_path, parent, sizeof(parent)); if (!file_exists(parent)) { return NWEDAV_ERROR_CONFLICT; } file_handle_t fh = file_open(full_path, FILE_WRITE | FILE_CREATE | FILE_TRUNC); #ifdef _WIN32 if (fh.handle == INVALID_HANDLE_VALUE) { #else if (fh.fd < 0) { #endif return NWEDAV_ERROR_IO; } if (data && len > 0) { size_t written = 0; while (written < len) { ssize_t w = file_write(fh, data + written, len - written); if (w < 0) { file_close(fh); return NWEDAV_ERROR_IO; } written += w; } } file_close(fh); return NWEDAV_OK; } int storage_delete(storage_backend_t *storage, const char *path) { if (!storage || !path) return NWEDAV_ERROR; char full_path[PATH_MAX]; int ret = storage_resolve_path(storage, path, full_path, sizeof(full_path)); if (ret != NWEDAV_OK) return ret; if (!file_exists(full_path)) { return NWEDAV_ERROR_NOT_FOUND; } return file_delete_recursive(full_path); } int storage_create_collection(storage_backend_t *storage, const char *path) { if (!storage || !path) return NWEDAV_ERROR; char full_path[PATH_MAX]; int ret = storage_resolve_path(storage, path, full_path, sizeof(full_path)); if (ret != NWEDAV_OK) return ret; if (file_exists(full_path)) { return NWEDAV_ERROR_EXISTS; } char parent[PATH_MAX]; path_dirname(full_path, parent, sizeof(parent)); if (!file_exists(parent)) { return NWEDAV_ERROR_CONFLICT; } return file_mkdir(full_path); } int storage_copy(storage_backend_t *storage, const char *src, const char *dst, int overwrite) { if (!storage || !src || !dst) return NWEDAV_ERROR; char src_path[PATH_MAX]; char dst_path[PATH_MAX]; int ret = storage_resolve_path(storage, src, src_path, sizeof(src_path)); if (ret != NWEDAV_OK) return ret; ret = storage_resolve_path(storage, dst, dst_path, sizeof(dst_path)); if (ret != NWEDAV_OK) return ret; if (!file_exists(src_path)) { return NWEDAV_ERROR_NOT_FOUND; } if (file_exists(dst_path) && !overwrite) { return NWEDAV_ERROR_EXISTS; } char parent[PATH_MAX]; path_dirname(dst_path, parent, sizeof(parent)); if (!file_exists(parent)) { return NWEDAV_ERROR_CONFLICT; } if (file_exists(dst_path)) { file_delete_recursive(dst_path); } return file_copy_recursive(src_path, dst_path); } int storage_move(storage_backend_t *storage, const char *src, const char *dst, int overwrite) { if (!storage || !src || !dst) return NWEDAV_ERROR; char src_path[PATH_MAX]; char dst_path[PATH_MAX]; int ret = storage_resolve_path(storage, src, src_path, sizeof(src_path)); if (ret != NWEDAV_OK) return ret; ret = storage_resolve_path(storage, dst, dst_path, sizeof(dst_path)); if (ret != NWEDAV_OK) return ret; if (!file_exists(src_path)) { return NWEDAV_ERROR_NOT_FOUND; } if (file_exists(dst_path) && !overwrite) { return NWEDAV_ERROR_EXISTS; } char parent[PATH_MAX]; path_dirname(dst_path, parent, sizeof(parent)); if (!file_exists(parent)) { return NWEDAV_ERROR_CONFLICT; } if (file_exists(dst_path)) { file_delete_recursive(dst_path); } return file_rename(src_path, dst_path); } int storage_list(storage_backend_t *storage, const char *path, int depth, storage_list_callback_t callback, void *user_data) { if (!storage || !path || !callback) return NWEDAV_ERROR; char full_path[PATH_MAX]; int ret = storage_resolve_path(storage, path, full_path, sizeof(full_path)); if (ret != NWEDAV_OK) return ret; resource_info_t info; ret = storage_get_info(storage, path, &info); if (ret != NWEDAV_OK) return ret; callback(path, &info, user_data); if (!info.is_collection || depth == 0) { return NWEDAV_OK; } dir_handle_t *dh = dir_open(full_path); if (!dh) return NWEDAV_ERROR_IO; dir_entry_t entry; while (dir_read(dh, &entry) == NWEDAV_OK) { if (strcmp(entry.name, ".") == 0 || strcmp(entry.name, "..") == 0) { continue; } char child_uri[PATH_MAX]; if (strcmp(path, "/") == 0) { snprintf(child_uri, sizeof(child_uri), "/%s", entry.name); } else { snprintf(child_uri, sizeof(child_uri), "%s/%s", path, entry.name); } resource_info_t child_info; if (storage_get_info(storage, child_uri, &child_info) == NWEDAV_OK) { callback(child_uri, &child_info, user_data); if (child_info.is_collection && depth > 1) { storage_list(storage, child_uri, depth - 1, callback, user_data); } } } dir_close(dh); return NWEDAV_OK; } int64_t storage_calculate_size(storage_backend_t *storage, const char *path) { if (!storage || !path) return 0; char full_path[PATH_MAX]; if (storage_resolve_path(storage, path, full_path, sizeof(full_path)) != NWEDAV_OK) { return 0; } file_stat_t st; if (file_stat(full_path, &st) != NWEDAV_OK) { return 0; } if (!st.is_dir) { return st.size; } int64_t total = 0; dir_handle_t *dh = dir_open(full_path); if (!dh) return 0; dir_entry_t entry; while (dir_read(dh, &entry) == NWEDAV_OK) { if (strcmp(entry.name, ".") == 0 || strcmp(entry.name, "..") == 0) { continue; } char child_uri[PATH_MAX]; if (strcmp(path, "/") == 0) { snprintf(child_uri, sizeof(child_uri), "/%s", entry.name); } else { snprintf(child_uri, sizeof(child_uri), "%s/%s", path, entry.name); } total += storage_calculate_size(storage, child_uri); } dir_close(dh); return total; }