351 lines
8.6 KiB
C
351 lines
8.6 KiB
C
|
|
/* 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;
|
||
|
|
}
|