mirror of
https://github.com/libfuse/libfuse.git
synced 2026-07-07 14:47:41 +08:00
example/service: create a sample systemd service for a high-level fuse server
Create a simple high-level fuse server that can be run as a systemd service. Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
This commit is contained in:
committed by
Bernd Schubert
parent
63336ed0d8
commit
e25d7b9e25
@@ -19,6 +19,12 @@ if platform.endswith('linux')
|
||||
configure_file(input: 'service_ll.socket.in',
|
||||
output: 'service_ll.socket',
|
||||
configuration: private_cfg)
|
||||
|
||||
single_file_examples += [ 'service_hl' ]
|
||||
configure_file(input: 'service_hl.socket.in',
|
||||
output: 'service_hl.socket',
|
||||
configuration: private_cfg)
|
||||
|
||||
endif
|
||||
|
||||
threaded_examples = [ 'notify_inval_inode',
|
||||
|
||||
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
* FUSE: Filesystem in Userspace
|
||||
* Copyright (C) 2026 Oracle.
|
||||
*
|
||||
* This program can be distributed under the terms of the GNU GPLv2.
|
||||
* See the file GPL2.txt.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
*
|
||||
* minimal example filesystem using high-level API and systemd service api
|
||||
*
|
||||
* Compile with:
|
||||
*
|
||||
* gcc -Wall single_file.c service_hl.c `pkg-config fuse3 --cflags --libs` -o service_hl
|
||||
*
|
||||
* Note: If the pkg-config command fails due to the absence of the fuse3.pc
|
||||
* file, you should configure the path to the fuse3.pc file in the
|
||||
* PKG_CONFIG_PATH variable.
|
||||
*
|
||||
* Change the ExecStart line in service_hl@.service:
|
||||
*
|
||||
* ExecStart=/path/to/service_hl
|
||||
*
|
||||
* to point to the actual path of the service_hl binary.
|
||||
*
|
||||
* Finally, install the service_hl@.service and service_hl.socket files to the
|
||||
* systemd service directory, usually /run/systemd/system.
|
||||
*
|
||||
* ## Source code ##
|
||||
* \include service_hl.c
|
||||
* \include service_hl.socket
|
||||
* \include service_hl@.service
|
||||
* \include single_file.c
|
||||
* \include single_file.h
|
||||
*/
|
||||
|
||||
#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 19)
|
||||
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#include <fuse.h>
|
||||
#include <fuse_service.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
#include <pthread.h>
|
||||
#include <stddef.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/stat.h>
|
||||
#define USE_SINGLE_FILE_HL_API
|
||||
#include "single_file.h"
|
||||
|
||||
struct service_hl {
|
||||
char *device;
|
||||
struct fuse_service *service;
|
||||
|
||||
/* really booleans */
|
||||
int debug;
|
||||
};
|
||||
|
||||
static struct service_hl hl = { };
|
||||
|
||||
static void *service_hl_init(struct fuse_conn_info *conn,
|
||||
struct fuse_config *cfg)
|
||||
{
|
||||
(void) conn;
|
||||
cfg->kernel_cache = 1;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int service_hl_read(const char *path, char *buf, size_t count,
|
||||
off_t pos, struct fuse_file_info *fi)
|
||||
{
|
||||
if (!is_single_open_file_path(fi, path))
|
||||
return -EIO;
|
||||
|
||||
if (hl.debug)
|
||||
fprintf(stderr, "%s: pos 0x%llx count 0x%llx\n",
|
||||
__func__,
|
||||
(unsigned long long)pos,
|
||||
(unsigned long long)count);
|
||||
|
||||
if (!single_file.allow_dio && fi->direct_io)
|
||||
return -ENOSYS;
|
||||
|
||||
single_file_check_read(pos, &count);
|
||||
|
||||
if (!count)
|
||||
return 0;
|
||||
|
||||
return single_file_pread(buf, count, pos);
|
||||
}
|
||||
|
||||
static int service_hl_write(const char *path, const char *buf, size_t count,
|
||||
off_t pos, struct fuse_file_info *fi)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!is_single_open_file_path(fi, path))
|
||||
return -EIO;
|
||||
|
||||
if (hl.debug)
|
||||
fprintf(stderr, "%s: pos 0x%llx count 0x%llx\n",
|
||||
__func__,
|
||||
(unsigned long long)pos,
|
||||
(unsigned long long)count);
|
||||
|
||||
if (!single_file.allow_dio && fi->direct_io)
|
||||
return -ENOSYS;
|
||||
|
||||
ret = single_file_check_write(pos, &count);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (!count)
|
||||
return 0;
|
||||
|
||||
return single_file_pwrite(buf, count, pos);
|
||||
}
|
||||
|
||||
static const struct fuse_operations service_hl_oper = {
|
||||
.getattr = single_file_hl_getattr,
|
||||
.readdir = single_file_hl_readdir,
|
||||
.open = single_file_hl_open,
|
||||
.opendir = single_file_hl_opendir,
|
||||
.statfs = single_file_hl_statfs,
|
||||
.chmod = single_file_hl_chmod,
|
||||
.utimens = single_file_hl_utimens,
|
||||
.fsync = single_file_hl_fsync,
|
||||
.chown = single_file_hl_chown,
|
||||
.truncate = single_file_hl_truncate,
|
||||
.statx = single_file_hl_statx,
|
||||
|
||||
.init = service_hl_init,
|
||||
.read = service_hl_read,
|
||||
.write = service_hl_write,
|
||||
};
|
||||
|
||||
#define SERVICE_HL_OPT(t, p, v) { t, offsetof(struct service_hl, p), v }
|
||||
|
||||
static struct fuse_opt service_hl_opts[] = {
|
||||
SERVICE_HL_OPT("debug", debug, 1),
|
||||
SINGLE_FILE_OPT_KEYS,
|
||||
FUSE_OPT_END
|
||||
};
|
||||
|
||||
static int service_hl_opt_proc(void *data, const char *arg, int key,
|
||||
struct fuse_args *outargs)
|
||||
{
|
||||
int ret = single_file_opt_proc(data, arg, key, outargs);
|
||||
|
||||
if (ret < 1)
|
||||
return ret;
|
||||
|
||||
switch (key) {
|
||||
case FUSE_OPT_KEY_NONOPT:
|
||||
if (!hl.device) {
|
||||
hl.device = strdup(arg);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
|
||||
int ret = 1;
|
||||
|
||||
if (fuse_service_accept(&hl.service))
|
||||
goto err_args;
|
||||
|
||||
if (!fuse_service_accepted(hl.service))
|
||||
goto err_args;
|
||||
|
||||
if (fuse_service_append_args(hl.service, &args))
|
||||
goto err_service;
|
||||
|
||||
if (fuse_opt_parse(&args, &hl, service_hl_opts, service_hl_opt_proc))
|
||||
goto err_service;
|
||||
|
||||
if (!hl.device) {
|
||||
printf("usage: %s [options] <device> <mountpoint>\n", argv[0]);
|
||||
printf(" %s --help\n", argv[0]);
|
||||
goto err_service;
|
||||
}
|
||||
|
||||
if (single_file_service_open(hl.service, hl.device))
|
||||
goto err_service;
|
||||
|
||||
if (fuse_service_finish_file_requests(hl.service))
|
||||
goto err_singlefile;
|
||||
|
||||
if (single_file_configure(hl.device, NULL))
|
||||
goto err_singlefile;
|
||||
|
||||
fuse_service_expect_mount_format(hl.service, S_IFDIR);
|
||||
|
||||
ret = fuse_service_main(hl.service, &args, &service_hl_oper, NULL);
|
||||
|
||||
err_singlefile:
|
||||
single_file_close();
|
||||
err_service:
|
||||
free(hl.device);
|
||||
fuse_service_send_goodbye(hl.service, ret);
|
||||
fuse_service_destroy(&hl.service);
|
||||
err_args:
|
||||
fuse_opt_free_args(&args);
|
||||
return fuse_service_exit(ret);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#
|
||||
# Copyright (C) 2026 Oracle. All Rights Reserved.
|
||||
# Author: Darrick J. Wong <djwong@kernel.org>
|
||||
[Unit]
|
||||
Description=Socket for service_hl Service
|
||||
|
||||
[Socket]
|
||||
ListenSequentialPacket=@FUSE_SERVICE_SOCKET_DIR_RAW@/service_hl
|
||||
Accept=yes
|
||||
SocketMode=@FUSE_SERVICE_SOCKET_PERMS@
|
||||
RemoveOnStop=yes
|
||||
|
||||
[Install]
|
||||
WantedBy=sockets.target
|
||||
@@ -0,0 +1,102 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#
|
||||
# Copyright (C) 2026 Oracle. All Rights Reserved.
|
||||
# Author: Darrick J. Wong <djwong@kernel.org>
|
||||
[Unit]
|
||||
Description=service_hl Sample Fuse Service
|
||||
|
||||
# Don't leave failed units behind, systemd does not clean them up!
|
||||
CollectMode=inactive-or-failed
|
||||
|
||||
[Service]
|
||||
Type=exec
|
||||
ExecStart=/path/to/service_hl
|
||||
|
||||
# Try to capture core dumps
|
||||
LimitCORE=infinity
|
||||
|
||||
SyslogIdentifier=%N
|
||||
|
||||
# No realtime CPU scheduling
|
||||
RestrictRealtime=true
|
||||
|
||||
# Don't let us see anything in the regular system, and don't run as root
|
||||
DynamicUser=true
|
||||
ProtectSystem=strict
|
||||
ProtectHome=true
|
||||
PrivateTmp=true
|
||||
PrivateDevices=true
|
||||
PrivateUsers=true
|
||||
|
||||
# No network access
|
||||
PrivateNetwork=true
|
||||
ProtectHostname=true
|
||||
RestrictAddressFamilies=none
|
||||
IPAddressDeny=any
|
||||
|
||||
# Don't let the program mess with the kernel configuration at all
|
||||
ProtectKernelLogs=true
|
||||
ProtectKernelModules=true
|
||||
ProtectKernelTunables=true
|
||||
ProtectControlGroups=true
|
||||
ProtectProc=invisible
|
||||
RestrictNamespaces=true
|
||||
RestrictFileSystems=
|
||||
|
||||
# Hide everything in /proc, even /proc/mounts
|
||||
ProcSubset=pid
|
||||
|
||||
# Only allow the default personality Linux
|
||||
LockPersonality=true
|
||||
|
||||
# No writable memory pages
|
||||
MemoryDenyWriteExecute=true
|
||||
|
||||
# Don't let our mounts leak out to the host
|
||||
PrivateMounts=true
|
||||
|
||||
# Restrict system calls to the native arch and only enough to get things going
|
||||
SystemCallArchitectures=native
|
||||
SystemCallFilter=@system-service
|
||||
SystemCallFilter=~@privileged
|
||||
SystemCallFilter=~@resources
|
||||
|
||||
SystemCallFilter=~@clock
|
||||
SystemCallFilter=~@cpu-emulation
|
||||
SystemCallFilter=~@debug
|
||||
SystemCallFilter=~@module
|
||||
SystemCallFilter=~@reboot
|
||||
SystemCallFilter=~@swap
|
||||
|
||||
SystemCallFilter=~@mount
|
||||
|
||||
# libfuse io_uring wants to pin cores and memory
|
||||
SystemCallFilter=mbind
|
||||
SystemCallFilter=sched_setaffinity
|
||||
|
||||
# Leave a breadcrumb if we get whacked by the system call filter
|
||||
SystemCallErrorNumber=EL3RST
|
||||
|
||||
# Log to the kernel dmesg, just like an in-kernel filesystem driver
|
||||
StandardOutput=append:/dev/ttyprintk
|
||||
StandardError=append:/dev/ttyprintk
|
||||
|
||||
# Run with no capabilities at all
|
||||
CapabilityBoundingSet=
|
||||
AmbientCapabilities=
|
||||
NoNewPrivileges=true
|
||||
|
||||
# We don't create files
|
||||
UMask=7777
|
||||
|
||||
# No access to hardware /dev files at all
|
||||
ProtectClock=true
|
||||
DevicePolicy=closed
|
||||
|
||||
# Don't mess with set[ug]id anything.
|
||||
RestrictSUIDSGID=true
|
||||
|
||||
# Don't let OOM kills of processes in this containment group kill the whole
|
||||
# service, because we don't want filesystem drivers to go down.
|
||||
OOMPolicy=continue
|
||||
OOMScoreAdjust=-1000
|
||||
@@ -51,6 +51,7 @@
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
#include <pthread.h>
|
||||
#define USE_SINGLE_FILE_LL_API
|
||||
#include "single_file.h"
|
||||
|
||||
struct service_ll {
|
||||
|
||||
+255
-17
@@ -23,7 +23,10 @@
|
||||
#define FUSE_USE_VERSION (FUSE_MAKE_VERSION(3, 19))
|
||||
|
||||
#include "fuse_lowlevel.h"
|
||||
#include "fuse.h"
|
||||
#include "fuse_service.h"
|
||||
#define USE_SINGLE_FILE_LL_API
|
||||
#define USE_SINGLE_FILE_HL_API
|
||||
#include "single_file.h"
|
||||
|
||||
#define min(x, y) ((x) < (y) ? (x) : (y))
|
||||
@@ -56,6 +59,23 @@ struct single_file single_file = {
|
||||
.lock = PTHREAD_MUTEX_INITIALIZER,
|
||||
};
|
||||
|
||||
static fuse_ino_t single_file_path_to_ino(const char *path)
|
||||
{
|
||||
if (strcmp(path, "/") == 0)
|
||||
return FUSE_ROOT_ID;
|
||||
if (strcmp(path + 1, single_file_name) == 0)
|
||||
return SINGLE_FILE_INO;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static fuse_ino_t single_open_file_path_to_ino(const struct fuse_file_info *fi,
|
||||
const char *path)
|
||||
{
|
||||
if (fi)
|
||||
return fi->fh;
|
||||
return single_file_path_to_ino(path);
|
||||
}
|
||||
|
||||
static void dirbuf_add(fuse_req_t req, struct dirbuf *b, const char *name,
|
||||
fuse_ino_t ino)
|
||||
{
|
||||
@@ -91,6 +111,13 @@ bool is_single_file_ino(fuse_ino_t ino)
|
||||
return ino == SINGLE_FILE_INO;
|
||||
}
|
||||
|
||||
bool is_single_open_file_path(const struct fuse_file_info *fi, const char *name)
|
||||
{
|
||||
if (fi)
|
||||
return is_single_file_ino(fi->fh);
|
||||
return name[0] == '/' && strcmp(name + 1, single_file_name) == 0;
|
||||
}
|
||||
|
||||
void single_file_ll_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
|
||||
off_t off, struct fuse_file_info *fi)
|
||||
{
|
||||
@@ -117,6 +144,37 @@ void single_file_ll_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
|
||||
free(b.p);
|
||||
}
|
||||
|
||||
int single_file_hl_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
|
||||
off_t offset, struct fuse_file_info *fi,
|
||||
enum fuse_readdir_flags flags)
|
||||
{
|
||||
struct stat stbuf;
|
||||
fuse_ino_t ino = single_open_file_path_to_ino(fi, path);
|
||||
|
||||
memset(&stbuf, 0, sizeof(stbuf));
|
||||
|
||||
(void) offset;
|
||||
(void) flags;
|
||||
|
||||
switch (ino) {
|
||||
case FUSE_ROOT_ID:
|
||||
break;
|
||||
case SINGLE_FILE_INO:
|
||||
return -ENOTDIR;
|
||||
default:
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
stbuf.st_ino = FUSE_ROOT_ID;
|
||||
filler(buf, ".", &stbuf, 0, FUSE_FILL_DIR_DEFAULTS);
|
||||
filler(buf, "..", &stbuf, 0, FUSE_FILL_DIR_DEFAULTS);
|
||||
|
||||
stbuf.st_ino = SINGLE_FILE_INO;
|
||||
filler(buf, single_file_name, &stbuf, 0, FUSE_FILL_DIR_DEFAULTS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool sf_stat(fuse_ino_t ino, struct single_file_stat *llstat)
|
||||
{
|
||||
struct fuse_entry_param *entry = &llstat->entry;
|
||||
@@ -235,40 +293,75 @@ void single_file_ll_statx(fuse_req_t req, fuse_ino_t ino, int flags, int mask,
|
||||
else
|
||||
fuse_reply_statx(req, 0, &stx, 0.0);
|
||||
}
|
||||
|
||||
int single_file_hl_statx(const char *path, int statx_flags, int statx_mask,
|
||||
struct statx *stx, struct fuse_file_info *fi)
|
||||
{
|
||||
(void)statx_flags;
|
||||
fuse_ino_t ino = single_open_file_path_to_ino(fi, path);
|
||||
bool filled;
|
||||
|
||||
if (!ino)
|
||||
return -ENOENT;
|
||||
|
||||
pthread_mutex_lock(&single_file.lock);
|
||||
filled = sf_statx(ino, statx_mask, stx);
|
||||
pthread_mutex_unlock(&single_file.lock);
|
||||
|
||||
return filled ? 0 : -ENOENT;
|
||||
}
|
||||
#else
|
||||
void single_file_ll_statx(fuse_req_t req, fuse_ino_t ino, int flags, int mask,
|
||||
struct fuse_file_info *fi)
|
||||
{
|
||||
fuse_reply_err(req, ENOSYS);
|
||||
}
|
||||
|
||||
int single_file_hl_statx(const char *path, int statx_flags, int statx_mask,
|
||||
struct statx *stx, struct fuse_file_info *fi)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
#endif /* STATX_BASIC_STATS */
|
||||
|
||||
static void single_file_statfs(struct statvfs *buf)
|
||||
{
|
||||
pthread_mutex_lock(&single_file.lock);
|
||||
buf->f_bsize = single_file.blocksize;
|
||||
buf->f_frsize = 0;
|
||||
|
||||
buf->f_blocks = single_file.blocks;
|
||||
buf->f_bfree = 0;
|
||||
buf->f_bavail = 0;
|
||||
buf->f_files = 1;
|
||||
buf->f_ffree = 0;
|
||||
buf->f_favail = 0;
|
||||
buf->f_fsid = 0x50C00L;
|
||||
buf->f_flag = 0;
|
||||
if (single_file.ro)
|
||||
buf->f_flag |= ST_RDONLY;
|
||||
buf->f_namemax = 255;
|
||||
pthread_mutex_unlock(&single_file.lock);
|
||||
}
|
||||
|
||||
void single_file_ll_statfs(fuse_req_t req, fuse_ino_t ino)
|
||||
{
|
||||
struct statvfs buf;
|
||||
|
||||
(void)ino;
|
||||
|
||||
pthread_mutex_lock(&single_file.lock);
|
||||
buf.f_bsize = single_file.blocksize;
|
||||
buf.f_frsize = 0;
|
||||
|
||||
buf.f_blocks = single_file.blocks;
|
||||
buf.f_bfree = 0;
|
||||
buf.f_bavail = 0;
|
||||
buf.f_files = 1;
|
||||
buf.f_ffree = 0;
|
||||
buf.f_favail = 0;
|
||||
buf.f_fsid = 0x50C00L;
|
||||
buf.f_flag = 0;
|
||||
if (single_file.ro)
|
||||
buf.f_flag |= ST_RDONLY;
|
||||
buf.f_namemax = 255;
|
||||
pthread_mutex_unlock(&single_file.lock);
|
||||
|
||||
single_file_statfs(&buf);
|
||||
fuse_reply_statfs(req, &buf);
|
||||
}
|
||||
|
||||
int single_file_hl_statfs(const char *path, struct statvfs *buf)
|
||||
{
|
||||
(void)path;
|
||||
|
||||
single_file_statfs(buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void single_file_ll_getattr(fuse_req_t req, fuse_ino_t ino,
|
||||
struct fuse_file_info *fi)
|
||||
{
|
||||
@@ -288,6 +381,28 @@ void single_file_ll_getattr(fuse_req_t req, fuse_ino_t ino,
|
||||
llstat.entry.attr_timeout);
|
||||
}
|
||||
|
||||
int single_file_hl_getattr(const char *path, struct stat *stbuf,
|
||||
struct fuse_file_info *fi)
|
||||
{
|
||||
struct single_file_stat llstat;
|
||||
fuse_ino_t ino = single_open_file_path_to_ino(fi, path);
|
||||
bool filled;
|
||||
|
||||
if (!ino)
|
||||
return -ENOENT;
|
||||
|
||||
memset(&llstat, 0, sizeof(llstat));
|
||||
pthread_mutex_lock(&single_file.lock);
|
||||
filled = sf_stat(ino, &llstat);
|
||||
pthread_mutex_unlock(&single_file.lock);
|
||||
|
||||
if (!filled)
|
||||
return -ENOENT;
|
||||
|
||||
memcpy(stbuf, &llstat.entry.attr, sizeof(*stbuf));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void get_now(struct timespec *now)
|
||||
{
|
||||
#ifdef CLOCK_REALTIME
|
||||
@@ -342,6 +457,81 @@ deny:
|
||||
fuse_reply_err(req, EPERM);
|
||||
}
|
||||
|
||||
int single_file_hl_chmod(const char *path, mode_t mode,
|
||||
struct fuse_file_info *fi)
|
||||
{
|
||||
fuse_ino_t ino = single_open_file_path_to_ino(fi, path);
|
||||
|
||||
if (!ino)
|
||||
return -ENOENT;
|
||||
if (ino != SINGLE_FILE_INO)
|
||||
return -EPERM;
|
||||
if (single_file.ro)
|
||||
return -EPERM;
|
||||
|
||||
pthread_mutex_lock(&single_file.lock);
|
||||
single_file.mode = (single_file.mode & S_IFMT) | (mode & ~S_IFMT);
|
||||
pthread_mutex_unlock(&single_file.lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void set_time(const struct timespec *ctv, struct timespec *tv)
|
||||
{
|
||||
switch (ctv->tv_nsec) {
|
||||
case UTIME_OMIT:
|
||||
return;
|
||||
case UTIME_NOW:
|
||||
get_now(tv);
|
||||
break;
|
||||
default:
|
||||
memcpy(tv, ctv, sizeof(*tv));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int single_file_hl_utimens(const char *path, const struct timespec ctv[2],
|
||||
struct fuse_file_info *fi)
|
||||
{
|
||||
fuse_ino_t ino = single_open_file_path_to_ino(fi, path);
|
||||
|
||||
if (!ino)
|
||||
return -ENOENT;
|
||||
if (ino != SINGLE_FILE_INO)
|
||||
return -EPERM;
|
||||
if (single_file.ro)
|
||||
return -EPERM;
|
||||
|
||||
pthread_mutex_lock(&single_file.lock);
|
||||
set_time(&ctv[0], &single_file.atime);
|
||||
set_time(&ctv[1], &single_file.mtime);
|
||||
get_now(&single_file.ctime);
|
||||
pthread_mutex_unlock(&single_file.lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int single_file_hl_chown(const char *path, uid_t owner, gid_t group,
|
||||
struct fuse_file_info *fi)
|
||||
{
|
||||
(void)path;
|
||||
(void)owner;
|
||||
(void)group;
|
||||
(void)fi;
|
||||
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
int single_file_hl_truncate(const char *path, off_t len,
|
||||
struct fuse_file_info *fi)
|
||||
{
|
||||
(void)path;
|
||||
(void)len;
|
||||
(void)fi;
|
||||
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
void single_file_ll_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
|
||||
{
|
||||
struct single_file_stat llstat;
|
||||
@@ -373,6 +563,34 @@ void single_file_ll_open(fuse_req_t req, fuse_ino_t ino,
|
||||
fuse_reply_open(req, fi);
|
||||
}
|
||||
|
||||
int single_file_hl_opendir(const char *path, struct fuse_file_info *fi)
|
||||
{
|
||||
fuse_ino_t ino = single_file_path_to_ino(path);
|
||||
|
||||
if (!ino)
|
||||
return -ENOENT;
|
||||
if (ino == SINGLE_FILE_INO)
|
||||
return -ENOTDIR;
|
||||
|
||||
fi->fh = ino;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int single_file_hl_open(const char *path, struct fuse_file_info *fi)
|
||||
{
|
||||
fuse_ino_t ino = single_file_path_to_ino(path);
|
||||
|
||||
if (!ino)
|
||||
return -ENOENT;
|
||||
if (ino != SINGLE_FILE_INO)
|
||||
return -EISDIR;
|
||||
if (single_file.ro && (fi->flags & O_ACCMODE) != O_RDONLY)
|
||||
return -EROFS;
|
||||
|
||||
fi->fh = ino;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void single_file_ll_fsync(fuse_req_t req, fuse_ino_t ino, int datasync,
|
||||
struct fuse_file_info *fi)
|
||||
{
|
||||
@@ -390,6 +608,26 @@ void single_file_ll_fsync(fuse_req_t req, fuse_ino_t ino, int datasync,
|
||||
fuse_reply_err(req, ret);
|
||||
}
|
||||
|
||||
int single_file_hl_fsync(const char *path, int datasync,
|
||||
struct fuse_file_info *fi)
|
||||
{
|
||||
fuse_ino_t ino = single_open_file_path_to_ino(fi, path);
|
||||
|
||||
(void)datasync;
|
||||
|
||||
if (!ino)
|
||||
return -ENOENT;
|
||||
|
||||
if (ino == SINGLE_FILE_INO) {
|
||||
int ret = fsync(single_file.backing_fd);
|
||||
|
||||
if (ret)
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned long long parse_num_blocks(const char *arg, int log_block_size)
|
||||
{
|
||||
char *p;
|
||||
|
||||
@@ -124,6 +124,7 @@ ssize_t single_file_pread(char *buf, size_t count, off_t pos);
|
||||
|
||||
/* low-level fuse operation handlers */
|
||||
|
||||
#ifdef USE_SINGLE_FILE_LL_API
|
||||
bool is_single_file_child(fuse_ino_t parent, const char *name);
|
||||
bool is_single_file_ino(fuse_ino_t ino);
|
||||
|
||||
@@ -149,5 +150,42 @@ void single_file_ll_fsync(fuse_req_t req, fuse_ino_t ino, int datasync,
|
||||
|
||||
int reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize,
|
||||
off_t off, size_t maxsize);
|
||||
#endif
|
||||
|
||||
/* high-level fuse operation handlers */
|
||||
|
||||
#ifdef USE_SINGLE_FILE_HL_API
|
||||
bool is_single_open_file_path(const struct fuse_file_info *fi, const char *name);
|
||||
|
||||
int single_file_hl_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
|
||||
off_t offset, struct fuse_file_info *fi,
|
||||
enum fuse_readdir_flags flags);
|
||||
|
||||
int single_file_hl_statfs(const char *path, struct statvfs *buf);
|
||||
|
||||
int single_file_hl_statx(const char *path, int statx_flags, int statx_mask,
|
||||
struct statx *stx, struct fuse_file_info *fi);
|
||||
|
||||
int single_file_hl_getattr(const char *path, struct stat *stbuf,
|
||||
struct fuse_file_info *fi);
|
||||
int single_file_hl_chmod(const char *path, mode_t mode,
|
||||
struct fuse_file_info *fi);
|
||||
int single_file_hl_utimens(const char *path, const struct timespec ctv[2],
|
||||
struct fuse_file_info *fi);
|
||||
int single_file_hl_chown(const char *path, uid_t owner, gid_t group,
|
||||
struct fuse_file_info *fi);
|
||||
int single_file_hl_truncate(const char *path, off_t len,
|
||||
struct fuse_file_info *fi);
|
||||
|
||||
int single_file_hl_opendir(const char *path, struct fuse_file_info *fi);
|
||||
int single_file_hl_open(const char *path, struct fuse_file_info *fi);
|
||||
|
||||
int single_file_hl_fsync(const char *path, int datasync,
|
||||
struct fuse_file_info *fi);
|
||||
#endif
|
||||
|
||||
#if !defined(USE_SINGLE_FILE_LL_API) && !defined(USE_SINGLE_FILE_HL_API)
|
||||
# warning USE_SINGLE_FILE_[HL]L_API not defined!
|
||||
#endif
|
||||
|
||||
#endif /* FUSE_SINGLE_FILE_H_ */
|
||||
|
||||
Reference in New Issue
Block a user