All checks were successful
Test / Create distribution (push) Successful in 45s
Test / Sandbox (push) Successful in 2m26s
Test / Hakurei (push) Successful in 3m29s
Test / ShareFS (push) Successful in 3m26s
Test / Hpkg (push) Successful in 4m20s
Test / Sandbox (race detector) (push) Successful in 4m50s
Test / Hakurei (race detector) (push) Successful in 5m39s
Test / Flake checks (push) Successful in 1m45s
This is never called directly anywhere and it is simple enough to be included in the macro. This avoids passing the pointer around and dereferencing errno location, resulting in over 5% increase in throughput on the clang build. No change in the gcc build though. Signed-off-by: Ophestra <cat@gensokyo.uk>
283 lines
7.9 KiB
C
283 lines
7.9 KiB
C
#ifndef _GNU_SOURCE
|
|
#define _GNU_SOURCE /* O_DIRECT */
|
|
#endif
|
|
|
|
#include <dirent.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
|
|
/* TODO(ophestra): remove after 05ce67fea99ca09cd4b6625cff7aec9cc222dd5a reaches a release */
|
|
#include <sys/syscall.h>
|
|
|
|
#include "fuse-helper.h"
|
|
|
|
/* MUST_TRANSLATE_PATHNAME translates a userspace pathname to a relative pathname;
|
|
* the resulting address points to a constant string or part of pathname, it is never heap allocated. */
|
|
#define MUST_TRANSLATE_PATHNAME(pathname) \
|
|
do { \
|
|
if (pathname == NULL) \
|
|
return -EINVAL; \
|
|
while (*pathname == '/') \
|
|
pathname++; \
|
|
if (*pathname == '\0') \
|
|
pathname = "."; \
|
|
} while (0)
|
|
|
|
/* GET_CONTEXT_PRIV obtains fuse context and private data for the calling thread. */
|
|
#define GET_CONTEXT_PRIV(ctx, priv) \
|
|
do { \
|
|
ctx = fuse_get_context(); \
|
|
priv = ctx->private_data; \
|
|
} while (0)
|
|
|
|
/* impl_getattr modifies a struct stat from the kernel to present to userspace;
|
|
* impl_getattr returns a negative errno style error code. */
|
|
static int impl_getattr(struct fuse_context *ctx, struct stat *statbuf) {
|
|
/* allowlist of permitted types */
|
|
if (!S_ISDIR(statbuf->st_mode) && !S_ISREG(statbuf->st_mode) && !S_ISLNK(statbuf->st_mode)) {
|
|
return -ENOTRECOVERABLE; /* returning an errno causes all operations on the file to return EIO */
|
|
}
|
|
|
|
#define OVERRIDE_PERM(v) (statbuf->st_mode & ~0777) | (v & 0777)
|
|
if (S_ISDIR(statbuf->st_mode))
|
|
statbuf->st_mode = OVERRIDE_PERM(SHAREFS_PERM_DIR);
|
|
else if (S_ISREG(statbuf->st_mode))
|
|
statbuf->st_mode = OVERRIDE_PERM(SHAREFS_PERM_REG);
|
|
else
|
|
statbuf->st_mode = 0; /* should always be symlink in this case */
|
|
|
|
statbuf->st_uid = ctx->uid;
|
|
statbuf->st_gid = SHAREFS_MEDIA_RW_ID;
|
|
statbuf->st_ctim = statbuf->st_mtim;
|
|
statbuf->st_nlink = 1;
|
|
return 0;
|
|
}
|
|
|
|
/* fuse_operations implementation */
|
|
|
|
int sharefs_getattr(const char *pathname, struct stat *statbuf, struct fuse_file_info *fi) {
|
|
struct fuse_context *ctx;
|
|
struct sharefs_private *priv;
|
|
GET_CONTEXT_PRIV(ctx, priv);
|
|
MUST_TRANSLATE_PATHNAME(pathname);
|
|
|
|
(void)fi;
|
|
|
|
if (fstatat(priv->dirfd, pathname, statbuf, AT_SYMLINK_NOFOLLOW) == -1)
|
|
return -errno;
|
|
return impl_getattr(ctx, statbuf);
|
|
}
|
|
|
|
int sharefs_readdir(const char *pathname, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi, enum fuse_readdir_flags flags) {
|
|
int fd;
|
|
DIR *dp;
|
|
struct stat st;
|
|
int ret = 0;
|
|
struct dirent *de;
|
|
|
|
struct fuse_context *ctx;
|
|
struct sharefs_private *priv;
|
|
GET_CONTEXT_PRIV(ctx, priv);
|
|
MUST_TRANSLATE_PATHNAME(pathname);
|
|
|
|
(void)offset;
|
|
(void)fi;
|
|
|
|
if ((fd = openat(priv->dirfd, pathname, O_RDONLY | O_DIRECTORY | O_CLOEXEC)) == -1)
|
|
return -errno;
|
|
if ((dp = fdopendir(fd)) == NULL) {
|
|
close(fd);
|
|
return -errno;
|
|
}
|
|
|
|
errno = 0; /* for the next readdir call */
|
|
while ((de = readdir(dp)) != NULL) {
|
|
if (flags & FUSE_READDIR_PLUS) {
|
|
if (fstatat(dirfd(dp), de->d_name, &st, AT_SYMLINK_NOFOLLOW) == -1) {
|
|
ret = -errno;
|
|
break;
|
|
}
|
|
|
|
if ((ret = impl_getattr(ctx, &st)) < 0)
|
|
break;
|
|
|
|
errno = 0;
|
|
ret = filler(buf, de->d_name, &st, 0, FUSE_FILL_DIR_PLUS);
|
|
} else
|
|
ret = filler(buf, de->d_name, NULL, 0, 0);
|
|
|
|
if (ret != 0) {
|
|
ret = errno != 0 ? -errno : -EIO; /* filler */
|
|
break;
|
|
}
|
|
|
|
errno = 0; /* for the next readdir call */
|
|
}
|
|
if (ret == 0 && errno != 0)
|
|
ret = -errno; /* readdir */
|
|
|
|
closedir(dp);
|
|
return ret;
|
|
}
|
|
|
|
int sharefs_mkdir(const char *pathname, mode_t mode) {
|
|
struct fuse_context *ctx;
|
|
struct sharefs_private *priv;
|
|
GET_CONTEXT_PRIV(ctx, priv);
|
|
MUST_TRANSLATE_PATHNAME(pathname);
|
|
|
|
(void)mode;
|
|
|
|
if (mkdirat(priv->dirfd, pathname, SHAREFS_PERM_DIR) == -1)
|
|
return -errno;
|
|
return 0;
|
|
}
|
|
|
|
int sharefs_unlink(const char *pathname) {
|
|
struct fuse_context *ctx;
|
|
struct sharefs_private *priv;
|
|
GET_CONTEXT_PRIV(ctx, priv);
|
|
MUST_TRANSLATE_PATHNAME(pathname);
|
|
|
|
if (unlinkat(priv->dirfd, pathname, 0) == -1)
|
|
return -errno;
|
|
return 0;
|
|
}
|
|
|
|
int sharefs_rmdir(const char *pathname) {
|
|
struct fuse_context *ctx;
|
|
struct sharefs_private *priv;
|
|
GET_CONTEXT_PRIV(ctx, priv);
|
|
MUST_TRANSLATE_PATHNAME(pathname);
|
|
|
|
if (unlinkat(priv->dirfd, pathname, AT_REMOVEDIR) == -1)
|
|
return -errno;
|
|
return 0;
|
|
}
|
|
|
|
int sharefs_rename(const char *oldpath, const char *newpath, unsigned int flags) {
|
|
struct fuse_context *ctx;
|
|
struct sharefs_private *priv;
|
|
GET_CONTEXT_PRIV(ctx, priv);
|
|
MUST_TRANSLATE_PATHNAME(oldpath);
|
|
MUST_TRANSLATE_PATHNAME(newpath);
|
|
|
|
/* TODO(ophestra): replace with wrapper after 05ce67fea99ca09cd4b6625cff7aec9cc222dd5a reaches a release */
|
|
if (syscall(__NR_renameat2, priv->dirfd, oldpath, priv->dirfd, newpath, flags) == -1)
|
|
return -errno;
|
|
return 0;
|
|
}
|
|
|
|
int sharefs_truncate(const char *pathname, off_t length, struct fuse_file_info *fi) {
|
|
int fd;
|
|
int ret;
|
|
|
|
struct fuse_context *ctx;
|
|
struct sharefs_private *priv;
|
|
GET_CONTEXT_PRIV(ctx, priv);
|
|
MUST_TRANSLATE_PATHNAME(pathname);
|
|
|
|
(void)fi;
|
|
|
|
if ((fd = openat(priv->dirfd, pathname, O_WRONLY | O_CLOEXEC)) == -1)
|
|
return -errno;
|
|
if ((ret = ftruncate(fd, length)) == -1)
|
|
ret = -errno;
|
|
close(fd);
|
|
return ret;
|
|
}
|
|
|
|
int sharefs_utimens(const char *pathname, const struct timespec times[2], struct fuse_file_info *fi) {
|
|
struct fuse_context *ctx;
|
|
struct sharefs_private *priv;
|
|
GET_CONTEXT_PRIV(ctx, priv);
|
|
MUST_TRANSLATE_PATHNAME(pathname);
|
|
|
|
(void)fi;
|
|
|
|
if (utimensat(priv->dirfd, pathname, times, AT_SYMLINK_NOFOLLOW) == -1)
|
|
return -errno;
|
|
return 0;
|
|
}
|
|
|
|
int sharefs_create(const char *pathname, mode_t mode, struct fuse_file_info *fi) {
|
|
int fd;
|
|
|
|
struct fuse_context *ctx;
|
|
struct sharefs_private *priv;
|
|
GET_CONTEXT_PRIV(ctx, priv);
|
|
MUST_TRANSLATE_PATHNAME(pathname);
|
|
|
|
(void)mode;
|
|
|
|
if ((fd = openat(priv->dirfd, pathname, fi->flags & ~SHAREFS_FORBIDDEN_FLAGS, SHAREFS_PERM_REG)) == -1)
|
|
return -errno;
|
|
fi->fh = fd;
|
|
return 0;
|
|
}
|
|
|
|
int sharefs_open(const char *pathname, struct fuse_file_info *fi) {
|
|
int fd;
|
|
|
|
struct fuse_context *ctx;
|
|
struct sharefs_private *priv;
|
|
GET_CONTEXT_PRIV(ctx, priv);
|
|
MUST_TRANSLATE_PATHNAME(pathname);
|
|
|
|
if ((fd = openat(priv->dirfd, pathname, fi->flags & ~SHAREFS_FORBIDDEN_FLAGS)) == -1)
|
|
return -errno;
|
|
fi->fh = fd;
|
|
return 0;
|
|
}
|
|
|
|
int sharefs_read(const char *pathname, char *buf, size_t count, off_t offset, struct fuse_file_info *fi) {
|
|
int ret;
|
|
|
|
(void)pathname;
|
|
|
|
if ((ret = pread(fi->fh, buf, count, offset)) == -1)
|
|
return -errno;
|
|
return ret;
|
|
}
|
|
|
|
int sharefs_write(const char *pathname, const char *buf, size_t count, off_t offset, struct fuse_file_info *fi) {
|
|
int ret;
|
|
|
|
(void)pathname;
|
|
|
|
if ((ret = pwrite(fi->fh, buf, count, offset)) == -1)
|
|
return -errno;
|
|
return ret;
|
|
}
|
|
|
|
int sharefs_statfs(const char *pathname, struct statvfs *statbuf) {
|
|
int fd;
|
|
int ret;
|
|
|
|
struct fuse_context *ctx;
|
|
struct sharefs_private *priv;
|
|
GET_CONTEXT_PRIV(ctx, priv);
|
|
MUST_TRANSLATE_PATHNAME(pathname);
|
|
|
|
if ((fd = openat(priv->dirfd, pathname, O_RDONLY | O_CLOEXEC)) == -1)
|
|
return -errno;
|
|
if ((ret = fstatvfs(fd, statbuf)) == -1)
|
|
ret = -errno;
|
|
close(fd);
|
|
return ret;
|
|
}
|
|
|
|
int sharefs_release(const char *pathname, struct fuse_file_info *fi) {
|
|
(void)pathname;
|
|
|
|
return close(fi->fh);
|
|
}
|
|
|
|
int sharefs_fsync(const char *pathname, int datasync, struct fuse_file_info *fi) {
|
|
(void)pathname;
|
|
|
|
if (datasync ? fdatasync(fi->fh) : fsync(fi->fh) == -1)
|
|
return -errno;
|
|
return 0;
|
|
}
|