/* retoor <retoor@molodetz.nl> */
#define FUSE_USE_VERSION 31
#define _GNU_SOURCE
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <fuse3/fuse.h>
#include "operations.h"
#include "webdav.h"
#include "cache.h"
#include "utils.h"
static webdav_context_t *g_ctx = NULL;
static webdav_context_t *get_context(void) {
return g_ctx;
}
static int fusedav_getattr(const char *path, struct stat *stbuf,
struct fuse_file_info *fi) {
(void)fi;
webdav_context_t *ctx = get_context();
if (!ctx) return -EIO;
memset(stbuf, 0, sizeof(struct stat));
if (path_is_root(path)) {
stbuf->st_mode = S_IFDIR | 0755;
stbuf->st_nlink = 2;
stbuf->st_uid = getuid();
stbuf->st_gid = getgid();
return 0;
}
return webdav_get_stat(ctx, path, stbuf);
}
static int fusedav_readdir(const char *path, void *buf,
fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info *fi,
enum fuse_readdir_flags flags) {
(void)offset;
(void)fi;
(void)flags;
webdav_context_t *ctx = get_context();
if (!ctx) return -EIO;
filler(buf, ".", NULL, 0, 0);
filler(buf, "..", NULL, 0, 0);
dir_listing_t listing = {0};
int result = webdav_list_directory(ctx, path, &listing);
if (result != 0) {
return result;
}
for (int i = 0; i < listing.count; i++) {
struct stat st = {0};
st.st_mode = listing.entries[i].is_directory ?
(S_IFDIR | 0755) : (S_IFREG | 0644);
st.st_size = listing.entries[i].size;
st.st_mtime = listing.entries[i].mtime;
st.st_atime = listing.entries[i].mtime;
st.st_ctime = listing.entries[i].mtime;
st.st_uid = getuid();
st.st_gid = getgid();
st.st_nlink = listing.entries[i].is_directory ? 2 : 1;
if (filler(buf, listing.entries[i].name, &st, 0, 0) != 0) {
break;
}
}
for (int i = 0; i < listing.count; i++) {
free(listing.entries[i].name);
}
free(listing.entries);
return 0;
}
static int fusedav_open(const char *path, struct fuse_file_info *fi) {
webdav_context_t *ctx = get_context();
if (!ctx) return -EIO;
struct stat st;
int result = webdav_get_stat(ctx, path, &st);
if (result != 0) {
if ((fi->flags & O_CREAT) && result == -ENOENT) {
result = webdav_create_file(ctx, path);
if (result != 0) return result;
} else {
return result;
}
}
if (S_ISDIR(st.st_mode)) {
return -EISDIR;
}
file_handle_t *fh = file_handle_create(path);
if (!fh) return -ENOMEM;
fh->file_size = st.st_size;
fh->last_modified = st.st_mtime;
fi->fh = (uint64_t)(uintptr_t)fh;
return 0;
}
static int fusedav_create(const char *path, mode_t mode,
struct fuse_file_info *fi) {
(void)mode;
webdav_context_t *ctx = get_context();
if (!ctx) return -EIO;
int result = webdav_create_file(ctx, path);
if (result != 0) return result;
file_handle_t *fh = file_handle_create(path);
if (!fh) return -ENOMEM;
fh->file_size = 0;
fh->last_modified = time(NULL);
fi->fh = (uint64_t)(uintptr_t)fh;
return 0;
}
static int fusedav_read(const char *path, char *buf, size_t size,
off_t offset, struct fuse_file_info *fi) {
(void)path;
webdav_context_t *ctx = get_context();
if (!ctx) return -EIO;
file_handle_t *fh = (file_handle_t *)(uintptr_t)fi->fh;
const char *read_path = fh ? fh->path : path;
ssize_t result = webdav_read_file(ctx, read_path, offset, size,
(uint8_t *)buf);
return (int)result;
}
static int fusedav_write(const char *path, const char *buf, size_t size,
off_t offset, struct fuse_file_info *fi) {
(void)path;
webdav_context_t *ctx = get_context();
if (!ctx) return -EIO;
file_handle_t *fh = (file_handle_t *)(uintptr_t)fi->fh;
if (!fh) return -EBADF;
if (fh->write_buffer_pos + size > fh->write_buffer_size) {
int result = file_handle_flush(ctx, fh);
if (result != 0) return result;
if (size > fh->write_buffer_size) {
ssize_t written = webdav_write_file(ctx, fh->path, offset,
size, (uint8_t *)buf);
return (int)written;
}
}
memcpy(fh->write_buffer + fh->write_buffer_pos, buf, size);
fh->write_buffer_pos += size;
fh->dirty = 1;
off_t new_end = offset + (off_t)size;
if (new_end > fh->file_size) {
fh->file_size = new_end;
}
return (int)size;
}
static int fusedav_flush(const char *path, struct fuse_file_info *fi) {
(void)path;
webdav_context_t *ctx = get_context();
if (!ctx) return -EIO;
file_handle_t *fh = (file_handle_t *)(uintptr_t)fi->fh;
if (!fh) return 0;
return file_handle_flush(ctx, fh);
}
static int fusedav_fsync(const char *path, int isdatasync,
struct fuse_file_info *fi) {
(void)isdatasync;
return fusedav_flush(path, fi);
}
static int fusedav_release(const char *path, struct fuse_file_info *fi) {
(void)path;
webdav_context_t *ctx = get_context();
file_handle_t *fh = (file_handle_t *)(uintptr_t)fi->fh;
if (fh) {
if (ctx && fh->dirty) {
file_handle_flush(ctx, fh);
}
file_handle_destroy(fh);
}
fi->fh = 0;
return 0;
}
static int fusedav_mkdir(const char *path, mode_t mode) {
(void)mode;
webdav_context_t *ctx = get_context();
if (!ctx) return -EIO;
return webdav_mkdir(ctx, path);
}
static int fusedav_unlink(const char *path) {
webdav_context_t *ctx = get_context();
if (!ctx) return -EIO;
return webdav_delete(ctx, path);
}
static int fusedav_rmdir(const char *path) {
webdav_context_t *ctx = get_context();
if (!ctx) return -EIO;
return webdav_delete(ctx, path);
}
static int fusedav_rename(const char *from, const char *to,
unsigned int flags) {
(void)flags;
webdav_context_t *ctx = get_context();
if (!ctx) return -EIO;
return webdav_rename(ctx, from, to);
}
static int fusedav_truncate(const char *path, off_t size,
struct fuse_file_info *fi) {
(void)fi;
webdav_context_t *ctx = get_context();
if (!ctx) return -EIO;
return webdav_truncate(ctx, path, size);
}
static int fusedav_statfs(const char *path, struct statvfs *stbuf) {
(void)path;
stbuf->f_bsize = 4096;
stbuf->f_frsize = 4096;
stbuf->f_blocks = 1000000000;
stbuf->f_bfree = 500000000;
stbuf->f_bavail = 500000000;
stbuf->f_files = 1000000;
stbuf->f_ffree = 500000;
stbuf->f_favail = 500000;
stbuf->f_fsid = 0;
stbuf->f_flag = 0;
stbuf->f_namemax = 255;
return 0;
}
static int fusedav_utimens(const char *path, const struct timespec tv[2],
struct fuse_file_info *fi) {
(void)path;
(void)tv;
(void)fi;
return 0;
}
static int fusedav_access(const char *path, int mask) {
(void)mask;
webdav_context_t *ctx = get_context();
if (!ctx) return -EIO;
if (path_is_root(path)) {
return 0;
}
struct stat st;
return webdav_get_stat(ctx, path, &st);
}
static void *fusedav_init(struct fuse_conn_info *conn,
struct fuse_config *cfg) {
(void)conn;
cfg->kernel_cache = 1;
cfg->auto_cache = 1;
cfg->entry_timeout = 30.0;
cfg->attr_timeout = 30.0;
cfg->negative_timeout = 5.0;
return NULL;
}
static void fusedav_destroy(void *private_data) {
(void)private_data;
webdav_context_t *ctx = get_context();
if (ctx) {
cache_clear(ctx->metadata_cache);
cache_clear(ctx->dir_cache);
}
}
struct fuse_operations fusedav_operations = {
.getattr = fusedav_getattr,
.readdir = fusedav_readdir,
.open = fusedav_open,
.create = fusedav_create,
.read = fusedav_read,
.write = fusedav_write,
.flush = fusedav_flush,
.fsync = fusedav_fsync,
.release = fusedav_release,
.mkdir = fusedav_mkdir,
.unlink = fusedav_unlink,
.rmdir = fusedav_rmdir,
.rename = fusedav_rename,
.truncate = fusedav_truncate,
.statfs = fusedav_statfs,
.utimens = fusedav_utimens,
.access = fusedav_access,
.init = fusedav_init,
.destroy = fusedav_destroy,
};
void operations_init(webdav_context_t *ctx) {
g_ctx = ctx;
}