mirror of
https://github.com/trapexit/mergerfs.git
synced 2026-10-20 00:17:43 +00:00
Compare commits
135
Commits
webui
...
nix-debugging
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ecb8492bfa | ||
|
|
f474b1dcff | ||
|
|
7d21df6609 | ||
|
|
b1f0057b44 | ||
|
|
212d1500f4 | ||
|
|
8fb948d4f3 | ||
|
|
9886623451 | ||
|
|
1f38d51e13 | ||
|
|
a561c7b3ec | ||
|
|
07c92a9881 | ||
|
|
413a6b41e3 | ||
|
|
a0d9a11562 | ||
|
|
8324de1c02 | ||
|
|
bae6d544ff | ||
|
|
613cbf0a3a | ||
|
|
0e06915d47 | ||
|
|
0bc5501b34 | ||
|
|
7339d27d87 | ||
|
|
5c624e5549 | ||
|
|
646562050a | ||
|
|
1c54fabb6f | ||
|
|
be13186fef | ||
|
|
0e0b65634d | ||
|
|
0f1c3f7253 | ||
|
|
8dce6b0736 | ||
|
|
3b89280665 | ||
|
|
350fd82176 | ||
|
|
b73cb7eb67 | ||
|
|
fd6e382dd1 | ||
|
|
65e2a67315 | ||
|
|
157a582a5a | ||
|
|
a639dad8d6 | ||
|
|
8fc944b1a1 | ||
|
|
a3dea6842e | ||
|
|
f92bfca2ad | ||
|
|
8be2d84b30 | ||
|
|
905355f2fd | ||
|
|
a260bd8364 | ||
|
|
2666f27590 | ||
|
|
3d9dea5e04 | ||
|
|
f8fa16d3c5 | ||
|
|
355124e0d6 | ||
|
|
60fb6f4d60 | ||
|
|
99d6b79bd5 | ||
|
|
82657ce64a | ||
|
|
bed8c77486 | ||
|
|
b5ea55a028 | ||
|
|
15c2272040 | ||
|
|
31d3ed3129 | ||
|
|
cedbaaf8d8 | ||
|
|
7981f353e9 | ||
|
|
0e9d68535b | ||
|
|
d654204088 | ||
|
|
815bbc2f76 | ||
|
|
daed03e559 | ||
|
|
1247b1f2fc | ||
|
|
7a960c61bc | ||
|
|
72350498b1 | ||
|
|
96eb24cba6 | ||
|
|
a6e99a40e2 | ||
|
|
264bf1c5d7 | ||
|
|
8e2bd55d4b | ||
|
|
9ca5809b68 | ||
|
|
6d42b3188c | ||
|
|
b6f87eaac9 | ||
|
|
3b0efed6e2 | ||
|
|
9ff31489a1 | ||
|
|
d831057dec | ||
|
|
3432d4476b | ||
|
|
afd652cd84 | ||
|
|
2f0b03efa5 | ||
|
|
70701186d3 | ||
|
|
989b2450f6 | ||
|
|
f22b1903bc | ||
|
|
a7485231fd | ||
|
|
95ff413734 | ||
|
|
56e6ec4eec | ||
|
|
1bc965d4a6 | ||
|
|
ed276f5f92 | ||
|
|
05eb6ce40d | ||
|
|
4d90595a53 | ||
|
|
d75b96932d | ||
|
|
39e2860b25 | ||
|
|
bf47a326f6 | ||
|
|
2996963ecc | ||
|
|
8828539402 | ||
|
|
a316b48f3a | ||
|
|
6fad579f6a | ||
|
|
f93b297a87 | ||
|
|
12bb659cbd | ||
|
|
2c667129f9 | ||
|
|
a05365d84a | ||
|
|
be66ba2c1d | ||
|
|
eb60b1e244 | ||
|
|
8f13226628 | ||
|
|
414992916a | ||
|
|
81ee9731fd | ||
|
|
7023d97b74 | ||
|
|
733a3708a4 | ||
|
|
0566c45d12 | ||
|
|
d1ace6c392 | ||
|
|
0a5ae5428b | ||
|
|
ae9a4c7267 | ||
|
|
8d76393eac | ||
|
|
5c4579cf0a | ||
|
|
dbefedd5b4 | ||
|
|
d0d9306191 | ||
|
|
fb5d491b53 | ||
|
|
102b74f54a | ||
|
|
b5876ccb8d | ||
|
|
dc7bc4dc2d | ||
|
|
470b75417a | ||
|
|
d97a33d565 | ||
|
|
71854f8540 | ||
|
|
6719956104 | ||
|
|
bfd89ffe23 | ||
|
|
2eb1703bc4 | ||
|
|
d58927dc4e | ||
|
|
71c9662dc1 | ||
|
|
4869b83144 | ||
|
|
115e13b168 | ||
|
|
d8020f76ac | ||
|
|
e6be2db077 | ||
|
|
ebe716cded | ||
|
|
1280326539 | ||
|
|
51ec69f3a1 | ||
|
|
c958a7eec5 | ||
|
|
69774b47e6 | ||
|
|
813151ec73 | ||
|
|
c1b2072dc6 | ||
|
|
d5ef721266 | ||
|
|
fee7672479 | ||
|
|
ea3df0e248 | ||
|
|
d772afeb90 | ||
|
|
a61b587aed |
@@ -65,7 +65,6 @@ OPT_FLAGS := -O0 \
|
||||
$(SANITIZE) \
|
||||
-fstack-protector-strong \
|
||||
-Wextra \
|
||||
-Werror \
|
||||
-Wno-unused-parameter \
|
||||
-DDEBUG
|
||||
endif
|
||||
|
||||
@@ -0,0 +1,265 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <grp.h>
|
||||
#include <linux/capability.h>
|
||||
|
||||
// Direct syscall wrappers since we're not using libcap
|
||||
static int capset(cap_user_header_t header, const cap_user_data_t data) {
|
||||
return syscall(SYS_capset, header, data);
|
||||
}
|
||||
|
||||
static int capget(cap_user_header_t header, cap_user_data_t data) {
|
||||
return syscall(SYS_capget, header, data);
|
||||
}
|
||||
|
||||
void print_capabilities(const char *label) {
|
||||
struct __user_cap_header_struct header;
|
||||
struct __user_cap_data_struct data[2];
|
||||
|
||||
header.version = _LINUX_CAPABILITY_VERSION_3;
|
||||
header.pid = 0;
|
||||
|
||||
if (capget(&header, data) < 0) {
|
||||
perror("capget");
|
||||
return;
|
||||
}
|
||||
|
||||
printf("%s:\n", label);
|
||||
printf(" Permitted: 0x%08x%08x\n", data[1].permitted, data[0].permitted);
|
||||
printf(" Effective: 0x%08x%08x\n", data[1].effective, data[0].effective);
|
||||
printf(" Inheritable: 0x%08x%08x\n", data[1].inheritable, data[0].inheritable);
|
||||
|
||||
// Check if CAP_DAC_READ_SEARCH is set
|
||||
int cap_bit = CAP_DAC_READ_SEARCH;
|
||||
int word = cap_bit / 32;
|
||||
int bit = cap_bit % 32;
|
||||
|
||||
if (data[word].effective & (1 << bit)) {
|
||||
printf(" CAP_DAC_READ_SEARCH is EFFECTIVE\n");
|
||||
} else {
|
||||
printf(" CAP_DAC_READ_SEARCH is NOT effective\n");
|
||||
}
|
||||
}
|
||||
|
||||
void print_ids(const char *label) {
|
||||
printf("%s: uid=%d euid=%d gid=%d egid=%d\n",
|
||||
label, getuid(), geteuid(), getgid(), getegid());
|
||||
}
|
||||
|
||||
int setup_capabilities() {
|
||||
struct __user_cap_header_struct header;
|
||||
struct __user_cap_data_struct data[2];
|
||||
|
||||
printf("\n=== Setting up capabilities ===\n");
|
||||
|
||||
// Initialize header
|
||||
header.version = _LINUX_CAPABILITY_VERSION_3;
|
||||
header.pid = 0;
|
||||
|
||||
// Get current capabilities
|
||||
if (capget(&header, data) < 0) {
|
||||
perror("capget");
|
||||
return -1;
|
||||
}
|
||||
|
||||
print_capabilities("Before setup");
|
||||
|
||||
// Calculate bit position for CAP_DAC_READ_SEARCH
|
||||
int cap_bit = CAP_DAC_READ_SEARCH;
|
||||
int word = cap_bit / 32; // Which 32-bit word (0 or 1)
|
||||
int bit = cap_bit % 32; // Which bit in that word
|
||||
|
||||
// Set CAP_DAC_READ_SEARCH in permitted, effective, and inheritable
|
||||
data[word].permitted |= (1 << bit);
|
||||
data[word].effective |= (1 << bit);
|
||||
data[word].inheritable |= (1 << bit);
|
||||
|
||||
// Apply the capability set
|
||||
if (capset(&header, data) < 0) {
|
||||
perror("capset");
|
||||
return -1;
|
||||
}
|
||||
|
||||
print_capabilities("After capset");
|
||||
|
||||
// Enable ambient capabilities (survives UID changes)
|
||||
// PR_CAP_AMBIENT = 47, PR_CAP_AMBIENT_RAISE = 2
|
||||
if (prctl(47, 2, CAP_DAC_READ_SEARCH, 0, 0) < 0) {
|
||||
perror("prctl PR_CAP_AMBIENT_RAISE");
|
||||
printf("Warning: Ambient capabilities not supported (kernel < 4.3?)\n");
|
||||
} else {
|
||||
printf("Ambient capabilities set successfully\n");
|
||||
}
|
||||
|
||||
// Keep capabilities across setuid
|
||||
if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) {
|
||||
perror("prctl PR_SET_KEEPCAPS");
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("PR_SET_KEEPCAPS enabled\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int drop_privileges(uid_t target_uid, gid_t target_gid) {
|
||||
struct __user_cap_header_struct header;
|
||||
struct __user_cap_data_struct data[2];
|
||||
|
||||
printf("\n=== Dropping privileges ===\n");
|
||||
print_ids("Before drop");
|
||||
|
||||
// Set GID first (must be done while still root)
|
||||
if (setgid(target_gid) < 0) {
|
||||
perror("setgid");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Drop supplementary groups
|
||||
if (setgroups(0, NULL) < 0) {
|
||||
perror("setgroups");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Set UID
|
||||
if (setuid(target_uid) < 0) {
|
||||
perror("setuid");
|
||||
return -1;
|
||||
}
|
||||
|
||||
print_ids("After drop");
|
||||
print_capabilities("After UID change (before re-apply)");
|
||||
|
||||
// Re-apply capabilities after UID change
|
||||
header.version = _LINUX_CAPABILITY_VERSION_3;
|
||||
header.pid = 0;
|
||||
|
||||
// Get current capabilities
|
||||
if (capget(&header, data) < 0) {
|
||||
perror("capget after setuid");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Re-enable CAP_DAC_READ_SEARCH in effective set
|
||||
int cap_bit = CAP_DAC_READ_SEARCH;
|
||||
int word = cap_bit / 32;
|
||||
int bit = cap_bit % 32;
|
||||
|
||||
data[word].effective |= (1 << bit);
|
||||
|
||||
if (capset(&header, data) < 0) {
|
||||
perror("capset after setuid");
|
||||
return -1;
|
||||
}
|
||||
|
||||
print_capabilities("Final capabilities");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void test_file_access(const char *path) {
|
||||
printf("\n=== Testing access to: %s ===\n", path);
|
||||
|
||||
struct stat st;
|
||||
if (stat(path, &st) < 0) {
|
||||
printf("stat() failed: %s\n", strerror(errno));
|
||||
} else {
|
||||
printf("stat() succeeded! Mode: %o, Owner: %d:%d\n",
|
||||
st.st_mode & 0777, st.st_uid, st.st_gid);
|
||||
}
|
||||
|
||||
int fd = open(path, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
printf("open() failed: %s\n", strerror(errno));
|
||||
} else {
|
||||
printf("open() succeeded! fd=%d\n", fd);
|
||||
|
||||
char buf[128];
|
||||
ssize_t n = read(fd, buf, sizeof(buf) - 1);
|
||||
if (n > 0) {
|
||||
buf[n] = '\0';
|
||||
printf("Read %zd bytes: %.50s%s\n", n, buf, n > 50 ? "..." : "");
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
uid_t target_uid = 1000;
|
||||
gid_t target_gid = 1000;
|
||||
|
||||
if (argc > 1) {
|
||||
target_uid = atoi(argv[1]);
|
||||
}
|
||||
if (argc > 2) {
|
||||
target_gid = atoi(argv[2]);
|
||||
}
|
||||
|
||||
printf("CAP_DAC_READ_SEARCH Test Program (without libcap)\n");
|
||||
printf("=================================================\n");
|
||||
|
||||
if (geteuid() != 0) {
|
||||
fprintf(stderr, "Error: This program must be run as root\n");
|
||||
fprintf(stderr, "Usage: sudo %s [target_uid] [target_gid]\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
print_ids("Initial state");
|
||||
print_capabilities("Initial capabilities");
|
||||
|
||||
if (setup_capabilities() != 0) {
|
||||
fprintf(stderr, "Failed to setup capabilities\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (drop_privileges(target_uid, target_gid) != 0) {
|
||||
fprintf(stderr, "Failed to drop privileges\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("\n=== Testing File Access ===\n");
|
||||
printf("Now running as uid=%d with CAP_DAC_READ_SEARCH\n", getuid());
|
||||
|
||||
// Test accessing a root-owned file
|
||||
printf("\nTrying to access /etc/shadow (normally requires root):\n");
|
||||
test_file_access("/etc/shadow");
|
||||
|
||||
// Test accessing /root (usually 0700)
|
||||
printf("\nTrying to access /root directory:\n");
|
||||
test_file_access("/root");
|
||||
|
||||
// Provide instructions for custom test
|
||||
printf("\n=== Custom Test Instructions ===\n");
|
||||
printf("To test with your own restricted directory:\n");
|
||||
printf("1. Create a test: sudo mkdir -p /tmp/test_restricted\n");
|
||||
printf("2. Restrict it: sudo chmod 700 /tmp/test_restricted\n");
|
||||
printf("3. Add a file: sudo sh -c 'echo test > /tmp/test_restricted/file.txt'\n");
|
||||
printf("4. The program should be able to read it despite permissions\n");
|
||||
|
||||
if (argc > 3) {
|
||||
printf("\nTesting custom path: %s\n", argv[3]);
|
||||
test_file_access(argv[3]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compilation (no libcap needed):
|
||||
* gcc -o cap_test cap_test.c
|
||||
*
|
||||
* Usage:
|
||||
* sudo ./cap_test [target_uid] [target_gid] [optional_test_path]
|
||||
*
|
||||
* Example:
|
||||
* sudo ./cap_test 1000 1000
|
||||
* sudo ./cap_test 1000 1000 /tmp/test_restricted/file.txt
|
||||
*/
|
||||
+34
-30
@@ -275,7 +275,9 @@ static
|
||||
void
|
||||
debug_open_flags(const uint32_t flags_)
|
||||
{
|
||||
fmt::print("{}, ",open_accmode_to_str(flags_));
|
||||
fmt::print(g_OUTPUT,
|
||||
"{}, ",
|
||||
open_accmode_to_str(flags_));
|
||||
for(size_t i = 0; i < (sizeof(flags_) * 8); i++)
|
||||
{
|
||||
const char *str;
|
||||
@@ -287,7 +289,7 @@ debug_open_flags(const uint32_t flags_)
|
||||
if(str == NULL)
|
||||
continue;
|
||||
|
||||
fmt::print("{}, ",str);
|
||||
fmt::print(g_OUTPUT,"{}, ",str);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -315,7 +317,7 @@ fuse_fopen_flag_to_str(const uint32_t offset_)
|
||||
void
|
||||
debug_fuse_open_out(const struct fuse_open_out *arg_)
|
||||
{
|
||||
fmt::print(stderr,
|
||||
fmt::print(g_OUTPUT,
|
||||
"fuse_open_out:"
|
||||
" fh=0x{:#08x};"
|
||||
" open_flags=0x{:#04x} (",
|
||||
@@ -332,9 +334,9 @@ debug_fuse_open_out(const struct fuse_open_out *arg_)
|
||||
if(str == NULL)
|
||||
continue;
|
||||
|
||||
fmt::print(stderr,"{},",str);
|
||||
fmt::print(g_OUTPUT,"{},",str);
|
||||
}
|
||||
fmt::print(stderr,");\n");
|
||||
fmt::print(g_OUTPUT,");\n");
|
||||
}
|
||||
|
||||
static
|
||||
@@ -445,7 +447,7 @@ debug_fuse_mkdir_in(const void *arg_)
|
||||
"fuse_mkdir_in:"
|
||||
" mode={:o};"
|
||||
" umask={:o};"
|
||||
" name=%s;"
|
||||
" name={};"
|
||||
"\n"
|
||||
,
|
||||
arg->mode,
|
||||
@@ -461,7 +463,7 @@ debug_fuse_unlink(const void *arg_)
|
||||
|
||||
fmt::print(g_OUTPUT,
|
||||
"fuse_unlink:"
|
||||
" name=%s;"
|
||||
" name={};"
|
||||
"\n"
|
||||
,
|
||||
name);
|
||||
@@ -475,7 +477,7 @@ debug_fuse_rmdir(const void *arg_)
|
||||
|
||||
fmt::print(g_OUTPUT,
|
||||
"fuse_mkdir:"
|
||||
" name=%s;"
|
||||
" name={};"
|
||||
"\n"
|
||||
,
|
||||
name);
|
||||
@@ -493,8 +495,8 @@ debug_fuse_symlink(const void *arg_)
|
||||
|
||||
fmt::print(g_OUTPUT,
|
||||
"fuse_mkdir:"
|
||||
" linkname=%s;"
|
||||
" name=%s;"
|
||||
" linkname={};"
|
||||
" name={};"
|
||||
"\n"
|
||||
,
|
||||
linkname,
|
||||
@@ -514,9 +516,9 @@ debug_fuse_rename_in(const void *arg_)
|
||||
|
||||
fmt::print(g_OUTPUT,
|
||||
"fuse_rename_in:"
|
||||
" oldname=%s;"
|
||||
" oldname={};"
|
||||
" newdir={};"
|
||||
" newname=%s;"
|
||||
" newname={};"
|
||||
"\n"
|
||||
,
|
||||
oldname,
|
||||
@@ -536,7 +538,7 @@ debug_fuse_link_in(const void *arg_)
|
||||
fmt::print(g_OUTPUT,
|
||||
"fuse_link_in:"
|
||||
" oldnodeid={};"
|
||||
" name=%s;"
|
||||
" name={};"
|
||||
"\n"
|
||||
,
|
||||
arg->oldnodeid,
|
||||
@@ -556,7 +558,7 @@ debug_fuse_create_in(const void *arg_)
|
||||
"fuse_create_in:"
|
||||
" mode={:o};"
|
||||
" umask={:o};"
|
||||
" name=%s;"
|
||||
" name={};"
|
||||
" flags=0x%X (",
|
||||
arg->mode,
|
||||
arg->umask,
|
||||
@@ -574,7 +576,7 @@ debug_fuse_open_in(const void *arg_)
|
||||
|
||||
fmt::print(g_OUTPUT,
|
||||
"fuse_open_in:"
|
||||
" flags=0x%08X (",
|
||||
" flags=0x{:08X} (",
|
||||
arg->flags);
|
||||
debug_open_flags(arg->flags);
|
||||
fmt::print(g_OUTPUT,");\n");
|
||||
@@ -639,7 +641,7 @@ debug_fuse_write_in(const void *arg_)
|
||||
if(str == NULL)
|
||||
continue;
|
||||
|
||||
fmt::print(g_OUTPUT,"%s,",str);
|
||||
fmt::print(g_OUTPUT,"{},",str);
|
||||
}
|
||||
fmt::print(g_OUTPUT,");\n");
|
||||
}
|
||||
@@ -712,8 +714,8 @@ debug_fuse_setxattr_in(const void *arg_)
|
||||
"fuse_setxattr_in:"
|
||||
" size={};"
|
||||
" flags=0x%X;"
|
||||
" name=%s;"
|
||||
" value=%s;"
|
||||
" name={};"
|
||||
" value={};"
|
||||
"\n"
|
||||
,
|
||||
arg->size,
|
||||
@@ -734,7 +736,7 @@ debug_fuse_getxattr_in(const void *arg_)
|
||||
fmt::print(g_OUTPUT,
|
||||
"fuse_getxattr_in:"
|
||||
" size={};"
|
||||
" name=%s;"
|
||||
" name={};"
|
||||
"\n"
|
||||
,
|
||||
arg->size,
|
||||
@@ -763,7 +765,7 @@ debug_fuse_removexattr(const void *arg_)
|
||||
|
||||
fmt::print(g_OUTPUT,
|
||||
"fuse_removexattr:"
|
||||
" name=%s;"
|
||||
" name={};"
|
||||
"\n"
|
||||
,
|
||||
name);
|
||||
@@ -816,7 +818,7 @@ debug_fuse_init_in(const struct fuse_init_in *arg_)
|
||||
if(str == NULL)
|
||||
continue;
|
||||
|
||||
fmt::print(g_OUTPUT,"%s, ",str);
|
||||
fmt::print(g_OUTPUT,"{}, ",str);
|
||||
}
|
||||
fmt::print(g_OUTPUT,")\n");
|
||||
}
|
||||
@@ -932,7 +934,7 @@ debug_fuse_init_out(const uint64_t unique_,
|
||||
if(str == NULL)
|
||||
continue;
|
||||
|
||||
fmt::print(g_OUTPUT,"%s, ",str);
|
||||
fmt::print(g_OUTPUT,"{}, ",str);
|
||||
}
|
||||
|
||||
fmt::print(g_OUTPUT,
|
||||
@@ -1100,13 +1102,14 @@ opcode_name(enum fuse_opcode op_)
|
||||
{
|
||||
static const char *names[] =
|
||||
{
|
||||
"INVALID",
|
||||
"INVALID0",
|
||||
"LOOKUP",
|
||||
"FORGET",
|
||||
"GETATTR",
|
||||
"SETATTR",
|
||||
"READLINK",
|
||||
"SYMLINK",
|
||||
"INVALID1",
|
||||
"MKNOD",
|
||||
"MKDIR",
|
||||
"UNLINK",
|
||||
@@ -1118,6 +1121,7 @@ opcode_name(enum fuse_opcode op_)
|
||||
"WRITE",
|
||||
"STATFS",
|
||||
"RELEASE",
|
||||
"INVALID3",
|
||||
"FSYNC",
|
||||
"SETXATTR",
|
||||
"GETXATTR",
|
||||
@@ -1161,9 +1165,9 @@ debug_fuse_in_header(const struct fuse_in_header *hdr_)
|
||||
{
|
||||
const void *arg = &hdr_[1];
|
||||
|
||||
fmt::print(stderr,
|
||||
"unique=0x%016" PRIx64 ";"
|
||||
" opcode=%s ({});"
|
||||
fmt::print(g_OUTPUT,
|
||||
"unique=0x{:016x};"
|
||||
" opcode={} ({});"
|
||||
" nodeid={};"
|
||||
" uid={};"
|
||||
" gid={};"
|
||||
@@ -1275,10 +1279,10 @@ void
|
||||
debug_fuse_out_header(const struct fuse_out_header *hdr_)
|
||||
{
|
||||
fmt::print(g_OUTPUT,
|
||||
"unique=0x%016" PRIx64 ";"
|
||||
"unique=0x{:016x};"
|
||||
" opcode=RESPONSE;"
|
||||
" error=%d (%s);"
|
||||
" len={};"
|
||||
" error={} ({});"
|
||||
" len={};\n"
|
||||
,
|
||||
hdr_->unique,
|
||||
hdr_->error,
|
||||
@@ -1312,7 +1316,7 @@ debug_fuse_readlink(const uint64_t unique_,
|
||||
" opcode=RESPONSE;"
|
||||
" error=0 (Success);"
|
||||
" len={}; || "
|
||||
"readlink: linkname=%s"
|
||||
"readlink: linkname={}"
|
||||
"\n"
|
||||
,
|
||||
unique_,
|
||||
|
||||
@@ -51,6 +51,7 @@ void debug_fuse_lk_out(const uint64_t unique,
|
||||
void debug_fuse_bmap_out(const uint64_t unique,
|
||||
const struct fuse_bmap_out *arg);
|
||||
void debug_fuse_in_header(const struct fuse_in_header *hdr);
|
||||
void debug_fuse_out_header(const struct fuse_out_header *hdr);
|
||||
|
||||
std::string fuse_debug_init_flag_name(const uint64_t);
|
||||
|
||||
|
||||
@@ -143,6 +143,9 @@ fuse_send_msg(struct fuse_ll *f,
|
||||
|
||||
out->len = iov_length(iov, count);
|
||||
|
||||
if(fuse_cfg.debug)
|
||||
debug_fuse_out_header(out);
|
||||
|
||||
rv = writev(fuse_chan_fd(ch),iov,count);
|
||||
if(rv == -1)
|
||||
return -errno;
|
||||
@@ -1794,6 +1797,9 @@ fuse_ll_buf_process_read(struct fuse_session *se_,
|
||||
req->ctx.umask = 0;
|
||||
req->ch = se_->ch;
|
||||
|
||||
if(fuse_cfg.debug)
|
||||
debug_fuse_in_header(in);
|
||||
|
||||
err = ENOSYS;
|
||||
if(in->opcode >= FUSE_MAXOPS)
|
||||
goto reply_err;
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
template<typename T, typename U>
|
||||
constexpr T sc(U&& value) {
|
||||
return static_cast<T>(std::forward<U>(value));
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
constexpr T dc(U&& value) {
|
||||
return dynamic_cast<T>(std::forward<U>(value));
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
constexpr T rc(U&& value) {
|
||||
return reinterpret_cast<T>(std::forward<U>(value));
|
||||
}
|
||||
+4
-3
@@ -92,15 +92,15 @@ Config::Config()
|
||||
cache_statfs(0),
|
||||
cache_symlinks(false),
|
||||
cache_writeback(false),
|
||||
category(func),
|
||||
config_file(),
|
||||
direct_io_allow_mmap(true),
|
||||
dropcacheonclose(false),
|
||||
export_support(true),
|
||||
flushonclose(FlushOnClose::ENUM::OPENED_FOR_WRITE),
|
||||
func(),
|
||||
category(func),
|
||||
follow_symlinks(FollowSymlinks::ENUM::NEVER),
|
||||
fsname(),
|
||||
func(),
|
||||
fuse_msg_size("1M"),
|
||||
gid_cache_expire_timeout(60 * 60),
|
||||
gid_cache_remove_timeout(60 * 60 * 12),
|
||||
@@ -231,7 +231,7 @@ Config::Config()
|
||||
_map["func.chmod"] = &func.chmod;
|
||||
_map["func.chown"] = &func.chown;
|
||||
_map["func.create"] = &func.create;
|
||||
_map["func.getattr"] = &func.getattr;
|
||||
_map["func.getattr"] = &getattr;
|
||||
_map["func.getxattr"] = &func.getxattr;
|
||||
_map["func.link"] = &func.link;
|
||||
_map["func.listxattr"] = &func.listxattr;
|
||||
@@ -244,6 +244,7 @@ Config::Config()
|
||||
_map["func.rename"] = &func.rename;
|
||||
_map["func.rmdir"] = &func.rmdir;
|
||||
_map["func.setxattr"] = &func.setxattr;
|
||||
_map["func.statx"] = &statx;
|
||||
_map["func.symlink"] = &func.symlink;
|
||||
_map["func.truncate"] = &func.truncate;
|
||||
_map["func.unlink"] = &func.unlink;
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "func_getattr.hpp"
|
||||
#include "func_statx.hpp"
|
||||
|
||||
#include "branches.hpp"
|
||||
#include "category.hpp"
|
||||
#include "config_cachefiles.hpp"
|
||||
@@ -109,6 +112,9 @@ public:
|
||||
Config& operator=(const Config&);
|
||||
|
||||
public:
|
||||
Func2::GetAttr getattr{"combine"};
|
||||
Func2::Statx statx{"combine"};
|
||||
|
||||
ConfigBOOL allow_idmap;
|
||||
ConfigBOOL async_read;
|
||||
Branches branches;
|
||||
|
||||
@@ -19,12 +19,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "enum.hpp"
|
||||
#include "follow_symlinks_enum.hpp"
|
||||
|
||||
enum class FollowSymlinksEnum
|
||||
{
|
||||
NEVER,
|
||||
DIRECTORY,
|
||||
REGULAR,
|
||||
ALL
|
||||
};
|
||||
typedef Enum<FollowSymlinksEnum> FollowSymlinks;
|
||||
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "fmt/core.h"
|
||||
#include <cstdlib>
|
||||
|
||||
#define DIE(...) \
|
||||
do { \
|
||||
fmt::print(stderr, \
|
||||
"{}:{} in {}: ", __FILE__, __LINE__, __func__); \
|
||||
fmt::print(stderr, __VA_ARGS__); \
|
||||
fmt::print(stderr, "\n"); \
|
||||
std::abort(); \
|
||||
} while(0)
|
||||
+1
-1
@@ -17,7 +17,7 @@ public:
|
||||
|
||||
operator int()
|
||||
{
|
||||
return (_err.has_value() ? _err.value() : -ENOENT);
|
||||
return (_err.has_value() ? _err.value() : -ENXIO);
|
||||
}
|
||||
|
||||
Err&
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
enum class FollowSymlinksEnum
|
||||
{
|
||||
NEVER,
|
||||
DIRECTORY,
|
||||
REGULAR,
|
||||
ALL
|
||||
};
|
||||
+5
-14
@@ -19,8 +19,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "to_neg_errno.hpp"
|
||||
|
||||
#include <string>
|
||||
#include "to_cstr.hpp"
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
@@ -29,25 +28,17 @@
|
||||
|
||||
namespace fs
|
||||
{
|
||||
template<typename PathType>
|
||||
static
|
||||
inline
|
||||
int
|
||||
lstat(const char *path_,
|
||||
struct stat *st_)
|
||||
lstat(const PathType &path_,
|
||||
struct stat *st_)
|
||||
{
|
||||
int rv;
|
||||
|
||||
rv = ::lstat(path_,st_);
|
||||
rv = ::lstat(to_cstr(path_),st_);
|
||||
|
||||
return ::to_neg_errno(rv);
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
int
|
||||
lstat(const std::string &path_,
|
||||
struct stat *st_)
|
||||
{
|
||||
return fs::lstat(path_.c_str(),st_);
|
||||
}
|
||||
}
|
||||
|
||||
+57
-6
@@ -18,6 +18,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "casts.hpp"
|
||||
#include "die.hpp"
|
||||
#include "follow_symlinks_enum.hpp"
|
||||
#include "fs_lstat.hpp"
|
||||
#include "to_cstr.hpp"
|
||||
#include "to_neg_errno.hpp"
|
||||
|
||||
#include <string>
|
||||
@@ -29,25 +34,71 @@
|
||||
|
||||
namespace fs
|
||||
{
|
||||
template<typename PathType>
|
||||
static
|
||||
inline
|
||||
int
|
||||
stat(const char *path_,
|
||||
struct stat *st_)
|
||||
stat(const PathType &path_,
|
||||
struct stat *st_)
|
||||
{
|
||||
int rv;
|
||||
|
||||
rv = ::stat(path_,st_);
|
||||
rv = ::stat(to_cstr(path_),st_);
|
||||
|
||||
return ::to_neg_errno(rv);
|
||||
}
|
||||
|
||||
template<typename PathType>
|
||||
static
|
||||
inline
|
||||
int
|
||||
stat(const std::string &path_,
|
||||
struct stat *st_)
|
||||
stat(const PathType &path_,
|
||||
struct stat *st_,
|
||||
FollowSymlinksEnum follow_)
|
||||
{
|
||||
return fs::stat(path_.c_str(),st_);
|
||||
int rv;
|
||||
|
||||
switch(follow_)
|
||||
{
|
||||
case FollowSymlinksEnum::NEVER:
|
||||
rv = fs::lstat(path_,st_);
|
||||
return rv;
|
||||
case FollowSymlinksEnum::DIRECTORY:
|
||||
rv = fs::lstat(path_,st_);
|
||||
if((rv >= 0) && S_ISLNK(st_->st_mode))
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
rv = fs::stat(path_,&st);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
|
||||
if(S_ISDIR(st.st_mode))
|
||||
*st_ = st;
|
||||
}
|
||||
return rv;
|
||||
case FollowSymlinksEnum::REGULAR:
|
||||
rv = fs::lstat(path_,st_);
|
||||
if((rv >= 0) && S_ISLNK(st_->st_mode))
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
rv = fs::stat(path_,&st);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
|
||||
if(S_ISREG(st.st_mode))
|
||||
*st_ = st;
|
||||
}
|
||||
return rv;
|
||||
case FollowSymlinksEnum::ALL:
|
||||
rv = fs::stat(path_,st_);
|
||||
if(rv < 0)
|
||||
rv = fs::lstat(path_,st_);
|
||||
return rv;
|
||||
}
|
||||
|
||||
DIE("Should never reach this: follow_={}",sc<int>(follow_));
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
+52
-9
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "follow_symlinks_enum.hpp"
|
||||
#include "to_cstr.hpp"
|
||||
#include "to_neg_errno.hpp"
|
||||
|
||||
#include "fuse_kernel.h"
|
||||
@@ -17,11 +19,12 @@
|
||||
|
||||
namespace fs
|
||||
{
|
||||
template<typename PathType>
|
||||
static
|
||||
inline
|
||||
int
|
||||
statx(const int dirfd_,
|
||||
const char *pathname_,
|
||||
const PathType &pathname_,
|
||||
const int flags_,
|
||||
const unsigned int mask_,
|
||||
struct fuse_statx *st_)
|
||||
@@ -30,7 +33,7 @@ namespace fs
|
||||
int rv;
|
||||
|
||||
rv = ::statx(dirfd_,
|
||||
pathname_,
|
||||
to_cstr(pathname_),
|
||||
flags_,
|
||||
mask_,
|
||||
(struct statx*)st_);
|
||||
@@ -41,19 +44,59 @@ namespace fs
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename PathType>
|
||||
static
|
||||
inline
|
||||
int
|
||||
statx(const int dirfd_,
|
||||
const std::string &pathname_,
|
||||
const PathType &pathname_,
|
||||
const int flags_,
|
||||
const unsigned int mask_,
|
||||
struct fuse_statx *st_)
|
||||
struct fuse_statx *st_,
|
||||
FollowSymlinksEnum followsymlinks_)
|
||||
{
|
||||
return fs::statx(dirfd_,
|
||||
pathname_.c_str(),
|
||||
flags_,
|
||||
mask_,
|
||||
st_);
|
||||
int rv;
|
||||
|
||||
switch(followsymlinks_)
|
||||
{
|
||||
case FollowSymlinksEnum::NEVER:
|
||||
rv = fs::statx(AT_FDCWD,pathname_,flags_|AT_SYMLINK_NOFOLLOW,mask_,st_);
|
||||
return rv;
|
||||
case FollowSymlinksEnum::DIRECTORY:
|
||||
rv = fs::statx(AT_FDCWD,pathname_,flags_|AT_SYMLINK_NOFOLLOW,mask_,st_);
|
||||
if((rv >= 0) && S_ISLNK(st_->mode))
|
||||
{
|
||||
struct fuse_statx st;
|
||||
|
||||
rv = fs::statx(AT_FDCWD,pathname_,AT_SYMLINK_FOLLOW,STATX_TYPE,&st);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
|
||||
if(S_ISDIR(st.mode))
|
||||
*st_ = st;
|
||||
}
|
||||
return rv;
|
||||
case FollowSymlinksEnum::REGULAR:
|
||||
rv = fs::statx(AT_FDCWD,pathname_,flags_|AT_SYMLINK_NOFOLLOW,mask_,st_);
|
||||
if((rv >= 0) && S_ISLNK(st_->mode))
|
||||
{
|
||||
struct fuse_statx st;
|
||||
|
||||
rv = fs::statx(AT_FDCWD,pathname_,AT_SYMLINK_FOLLOW,STATX_TYPE,&st);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
|
||||
if(S_ISREG(st.mode))
|
||||
*st_ = st;
|
||||
}
|
||||
return rv;
|
||||
case FollowSymlinksEnum::ALL:
|
||||
rv = fs::statx(AT_FDCWD,pathname_,flags_|AT_SYMLINK_FOLLOW,mask_,st_);
|
||||
if(rv < 0)
|
||||
rv = fs::statx(AT_FDCWD,pathname_,flags_|AT_SYMLINK_NOFOLLOW,mask_,st_);
|
||||
return rv;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
#pragma once
|
||||
|
||||
#include "func_getattr_base.hpp"
|
||||
#include "func_getattr_factory.hpp"
|
||||
|
||||
#include "tofrom_string.hpp"
|
||||
|
||||
#include "fmt/core.h"
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace Func2
|
||||
{
|
||||
class GetAttr : public ToFromString
|
||||
{
|
||||
private:
|
||||
std::shared_ptr<Func2::GetAttrBase> _impl;
|
||||
|
||||
public:
|
||||
GetAttr()
|
||||
{
|
||||
}
|
||||
|
||||
GetAttr(const std::string &name_)
|
||||
{
|
||||
_impl = Func2::GetAttrFactory::make(name_);
|
||||
assert(_impl);
|
||||
}
|
||||
|
||||
public:
|
||||
int
|
||||
operator()(const Branches &branches_,
|
||||
const fs::path &fusepath_,
|
||||
struct stat *st_,
|
||||
const FollowSymlinksEnum follow_symlinks_,
|
||||
const s64 symlinkify_timeout_)
|
||||
{
|
||||
std::shared_ptr<Func2::GetAttrBase> p;
|
||||
|
||||
p = std::atomic_load(&_impl);
|
||||
|
||||
return (*p)(branches_,
|
||||
fusepath_,
|
||||
st_,
|
||||
follow_symlinks_,
|
||||
symlinkify_timeout_);
|
||||
}
|
||||
|
||||
public:
|
||||
std::string
|
||||
to_string() const
|
||||
{
|
||||
std::shared_ptr<Func2::GetAttrBase> p;
|
||||
|
||||
p = std::atomic_load(&_impl);
|
||||
|
||||
return std::string(p->name());
|
||||
}
|
||||
|
||||
int
|
||||
from_string(const std::string_view str_)
|
||||
{
|
||||
std::shared_ptr<Func2::GetAttrBase> p;
|
||||
|
||||
p = Func2::GetAttrFactory::make(str_);
|
||||
if(!p)
|
||||
return -EINVAL;
|
||||
|
||||
_impl = std::atomic_load(&p);
|
||||
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include "follow_symlinks_enum.hpp"
|
||||
|
||||
#include "fs_path.hpp"
|
||||
#include "branches.hpp"
|
||||
#include "int_types.h"
|
||||
|
||||
#include "fuse.h"
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace Func2
|
||||
{
|
||||
class GetAttrBase
|
||||
{
|
||||
public:
|
||||
GetAttrBase() {}
|
||||
~GetAttrBase() {}
|
||||
|
||||
public:
|
||||
virtual std::string_view name() const = 0;
|
||||
|
||||
public:
|
||||
virtual int operator()(const Branches &branches,
|
||||
const fs::path &fusepath,
|
||||
struct stat *st,
|
||||
const FollowSymlinksEnum follow_symlinks,
|
||||
const s64 symlinkify_timeout) = 0;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
#include "func_getattr_combine.hpp"
|
||||
|
||||
#include "error.hpp"
|
||||
#include "fs_inode.hpp"
|
||||
#include "fs_stat.hpp"
|
||||
#include "symlinkify.hpp"
|
||||
#include "timespec_utils.hpp"
|
||||
|
||||
|
||||
std::string_view
|
||||
Func2::GetAttrCombine::name() const
|
||||
{
|
||||
return "combine";
|
||||
}
|
||||
|
||||
int
|
||||
Func2::GetAttrCombine::operator()(const Branches &branches_,
|
||||
const fs::path &fusepath_,
|
||||
struct stat *st_,
|
||||
const FollowSymlinksEnum follow_symlinks_,
|
||||
const s64 symlinkify_timeout_)
|
||||
{
|
||||
int rv;
|
||||
Err err;
|
||||
fs::path fullpath;
|
||||
const Branch *first_branch;
|
||||
|
||||
first_branch = nullptr;
|
||||
for(const auto &branch : branches_)
|
||||
{
|
||||
struct stat tmp_st;
|
||||
|
||||
fullpath = branch.path / fusepath_;
|
||||
rv = fs::stat(fullpath,&tmp_st,follow_symlinks_);
|
||||
if(rv < 0)
|
||||
{
|
||||
err = rv;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!first_branch)
|
||||
{
|
||||
*st_ = tmp_st;
|
||||
first_branch = &branch;
|
||||
continue;
|
||||
}
|
||||
|
||||
st_->st_atim = TimeSpec::newest(st_->st_atim,tmp_st.st_atim);
|
||||
st_->st_ctim = TimeSpec::newest(st_->st_ctim,tmp_st.st_ctim);
|
||||
st_->st_mtim = TimeSpec::newest(st_->st_mtim,tmp_st.st_mtim);
|
||||
st_->st_nlink += tmp_st.st_nlink;
|
||||
}
|
||||
|
||||
if(!first_branch)
|
||||
return err;
|
||||
|
||||
if(symlinkify_timeout_ >= 0)
|
||||
{
|
||||
fullpath = first_branch->path / fusepath_;
|
||||
symlinkify::convert_if_can_be_symlink(fullpath,
|
||||
st_,
|
||||
symlinkify_timeout_);
|
||||
}
|
||||
|
||||
fs::inode::calc(first_branch->path,fusepath_,st_);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#include "func_getattr_base.hpp"
|
||||
|
||||
namespace Func2
|
||||
{
|
||||
class GetAttrCombine : public GetAttrBase
|
||||
{
|
||||
public:
|
||||
GetAttrCombine() {}
|
||||
~GetAttrCombine() {}
|
||||
|
||||
public:
|
||||
std::string_view name() const;
|
||||
|
||||
public:
|
||||
int operator()(const Branches &branches,
|
||||
const fs::path &fusepath,
|
||||
struct stat *st,
|
||||
const FollowSymlinksEnum follow_symlinks,
|
||||
const s64 symlinkify_timeout);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
#include "func_getattr_factory.hpp"
|
||||
|
||||
#include "func_getattr_combine.hpp"
|
||||
#include "func_getattr_ff.hpp"
|
||||
#include "func_getattr_newest.hpp"
|
||||
|
||||
#include "policies.hpp"
|
||||
|
||||
bool
|
||||
Func2::GetAttrFactory::valid(const std::string str_)
|
||||
{
|
||||
if(str_ == "combined")
|
||||
return true;
|
||||
if(str_ == "ff")
|
||||
return true;
|
||||
if(str_ == "newest")
|
||||
return true;
|
||||
|
||||
if(Policies::Search::find(str_))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::shared_ptr<Func2::GetAttrBase>
|
||||
Func2::GetAttrFactory::make(const std::string_view str_)
|
||||
{
|
||||
if(str_ == "combine")
|
||||
return std::make_shared<Func2::GetAttrCombine>();
|
||||
if(str_ == "ff")
|
||||
return std::make_shared<Func2::GetAttrFF>();
|
||||
if(str_ == "newest")
|
||||
return std::make_shared<Func2::GetAttrNewest>();
|
||||
|
||||
if(Policies::Search::find(str_))
|
||||
return std::make_shared<Func2::GetAttrCombine>();
|
||||
|
||||
return {};
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "func_getattr_base.hpp"
|
||||
|
||||
namespace Func2
|
||||
{
|
||||
class GetAttrFactory
|
||||
{
|
||||
public:
|
||||
static bool valid(const std::string str);
|
||||
static std::shared_ptr<GetAttrBase> make(const std::string_view str);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
#include "func_getattr_ff.hpp"
|
||||
|
||||
#include "error.hpp"
|
||||
#include "fs_stat.hpp"
|
||||
#include "fs_inode.hpp"
|
||||
#include "symlinkify.hpp"
|
||||
|
||||
|
||||
std::string_view
|
||||
Func2::GetAttrFF::name() const
|
||||
{
|
||||
return "ff";
|
||||
}
|
||||
|
||||
int
|
||||
Func2::GetAttrFF::operator()(const Branches &branches_,
|
||||
const fs::path &fusepath_,
|
||||
struct stat *st_,
|
||||
const FollowSymlinksEnum follow_symlinks_,
|
||||
const s64 symlinkify_timeout_)
|
||||
{
|
||||
int rv;
|
||||
fs::path fullpath;
|
||||
Err err;
|
||||
|
||||
err = -ENXIO;
|
||||
for(const auto &branch : branches_)
|
||||
{
|
||||
fullpath = branch.path / fusepath_;
|
||||
rv = fs::stat(fullpath,st_,follow_symlinks_);
|
||||
if(rv < 0)
|
||||
{
|
||||
__builtin_trap();
|
||||
err = rv;
|
||||
continue;
|
||||
}
|
||||
|
||||
symlinkify::convert_if_can_be_symlink(fullpath,
|
||||
st_,
|
||||
symlinkify_timeout_);
|
||||
|
||||
fs::inode::calc(branch.path,fusepath_,st_);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
__builtin_trap();
|
||||
|
||||
return err;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "func_getattr_base.hpp"
|
||||
|
||||
namespace Func2
|
||||
{
|
||||
class GetAttrFF : public GetAttrBase
|
||||
{
|
||||
public:
|
||||
GetAttrFF() {}
|
||||
~GetAttrFF() {}
|
||||
|
||||
public:
|
||||
std::string_view name() const;
|
||||
|
||||
public:
|
||||
int operator()(const Branches &branches,
|
||||
const fs::path &fusepath,
|
||||
struct stat *st,
|
||||
const FollowSymlinksEnum follow_symlinks,
|
||||
const s64 symlinkify_timeout);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
#include "func_getattr_newest.hpp"
|
||||
|
||||
#include "fs_stat.hpp"
|
||||
#include "fs_inode.hpp"
|
||||
#include "timespec_utils.hpp"
|
||||
#include "symlinkify.hpp"
|
||||
|
||||
|
||||
std::string_view
|
||||
Func2::GetAttrNewest::name() const
|
||||
{
|
||||
return "newest";
|
||||
}
|
||||
|
||||
int
|
||||
Func2::GetAttrNewest::operator()(const Branches &branches_,
|
||||
const fs::path &fusepath_,
|
||||
struct stat *st_,
|
||||
const FollowSymlinksEnum follow_symlinks_,
|
||||
const s64 symlinkify_timeout_)
|
||||
{
|
||||
int rv;
|
||||
fs::path fullpath;
|
||||
const Branch *newest_branch;
|
||||
|
||||
newest_branch = nullptr;
|
||||
for(const auto &branch : branches_)
|
||||
{
|
||||
struct stat tmp_st;
|
||||
|
||||
fullpath = branch.path / fusepath_;
|
||||
rv = fs::stat(fullpath,&tmp_st,follow_symlinks_);
|
||||
if(rv < 0)
|
||||
continue;
|
||||
|
||||
if(!TimeSpec::is_newer(tmp_st.st_mtim,st_->st_mtim))
|
||||
continue;
|
||||
|
||||
*st_ = tmp_st;
|
||||
newest_branch = &branch;
|
||||
}
|
||||
|
||||
if(!newest_branch)
|
||||
return -ENOENT;
|
||||
|
||||
if(symlinkify_timeout_ >= 0)
|
||||
{
|
||||
fullpath = newest_branch->path / fusepath_;
|
||||
symlinkify::convert_if_can_be_symlink(fullpath,
|
||||
st_,
|
||||
symlinkify_timeout_);
|
||||
}
|
||||
|
||||
fs::inode::calc(newest_branch->path,fusepath_,st_);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#include "func_getattr_base.hpp"
|
||||
|
||||
namespace Func2
|
||||
{
|
||||
class GetAttrNewest : public GetAttrBase
|
||||
{
|
||||
public:
|
||||
GetAttrNewest() {}
|
||||
~GetAttrNewest() {}
|
||||
|
||||
public:
|
||||
std::string_view name() const;
|
||||
|
||||
public:
|
||||
int operator()(const Branches &branches,
|
||||
const fs::path &fusepath,
|
||||
struct stat *st,
|
||||
const FollowSymlinksEnum follow_symlinks,
|
||||
const s64 symlinkify_timeout);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
#pragma once
|
||||
|
||||
#include "func_statx_base.hpp"
|
||||
#include "func_statx_factory.hpp"
|
||||
|
||||
#include "tofrom_string.hpp"
|
||||
|
||||
#include "fmt/core.h"
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
|
||||
namespace Func2
|
||||
{
|
||||
class Statx : public ToFromString
|
||||
{
|
||||
private:
|
||||
std::shared_ptr<Func2::StatxBase> _impl;
|
||||
|
||||
public:
|
||||
Statx()
|
||||
{
|
||||
}
|
||||
|
||||
Statx(const std::string &name_)
|
||||
{
|
||||
_impl = Func2::StatxFactory::make(name_);
|
||||
assert(_impl);
|
||||
}
|
||||
|
||||
public:
|
||||
int
|
||||
operator()(const Branches &branches_,
|
||||
const fs::path &fusepath_,
|
||||
const u32 flags_,
|
||||
const u32 mask_,
|
||||
struct fuse_statx *st_,
|
||||
const FollowSymlinksEnum follow_symlinks_,
|
||||
const s64 symlinkify_timeout_)
|
||||
{
|
||||
std::shared_ptr<Func2::StatxBase> p;
|
||||
|
||||
p = std::atomic_load(&_impl);
|
||||
|
||||
return (*p)(branches_,
|
||||
fusepath_,
|
||||
flags_,
|
||||
mask_,
|
||||
st_,
|
||||
follow_symlinks_,
|
||||
symlinkify_timeout_);
|
||||
}
|
||||
|
||||
public:
|
||||
std::string
|
||||
to_string() const
|
||||
{
|
||||
std::shared_ptr<Func2::StatxBase> p;
|
||||
|
||||
p = std::atomic_load(&_impl);
|
||||
|
||||
return std::string(p->name());
|
||||
}
|
||||
|
||||
int
|
||||
from_string(const std::string_view str_)
|
||||
{
|
||||
std::shared_ptr<Func2::StatxBase> p;
|
||||
|
||||
p = Func2::StatxFactory::make(str_);
|
||||
if(!p)
|
||||
return -EINVAL;
|
||||
|
||||
_impl = std::atomic_load(&p);
|
||||
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include "follow_symlinks_enum.hpp"
|
||||
|
||||
#include "fs_path.hpp"
|
||||
#include "branches.hpp"
|
||||
#include "int_types.h"
|
||||
|
||||
#include "fuse.h"
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
namespace Func2
|
||||
{
|
||||
class StatxBase
|
||||
{
|
||||
public:
|
||||
StatxBase() {}
|
||||
~StatxBase() {}
|
||||
|
||||
public:
|
||||
virtual std::string_view name() const = 0;
|
||||
|
||||
public:
|
||||
virtual int operator()(const Branches &branches,
|
||||
const fs::path &fusepath,
|
||||
const u32 flags_,
|
||||
const u32 mask_,
|
||||
struct fuse_statx *st,
|
||||
const FollowSymlinksEnum follow_symlinks,
|
||||
const s64 symlinkify_timeout) = 0;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
#include "func_statx_combine.hpp"
|
||||
|
||||
#include "fs_inode.hpp"
|
||||
#include "fs_statx.hpp"
|
||||
#include "symlinkify.hpp"
|
||||
#include "timespec_utils.hpp"
|
||||
|
||||
|
||||
std::string_view
|
||||
Func2::StatxCombine::name() const
|
||||
{
|
||||
return "combine";
|
||||
}
|
||||
|
||||
int
|
||||
Func2::StatxCombine::operator()(const Branches &branches_,
|
||||
const fs::path &fusepath_,
|
||||
const u32 flags_,
|
||||
const u32 mask_,
|
||||
struct fuse_statx *st_,
|
||||
const FollowSymlinksEnum follow_symlinks_,
|
||||
const s64 symlinkify_timeout_)
|
||||
{
|
||||
int rv;
|
||||
fs::path fullpath;
|
||||
const Branch *first_branch;
|
||||
|
||||
first_branch = nullptr;
|
||||
for(const auto &branch : branches_)
|
||||
{
|
||||
struct fuse_statx tmp_st;
|
||||
|
||||
fullpath = branch.path / fusepath_;
|
||||
rv = fs::statx(AT_FDCWD,
|
||||
fullpath,
|
||||
flags_|AT_SYMLINK_NOFOLLOW,
|
||||
mask_,
|
||||
&tmp_st,
|
||||
follow_symlinks_);
|
||||
if(rv < 0)
|
||||
continue;
|
||||
|
||||
if(!first_branch)
|
||||
{
|
||||
*st_ = tmp_st;
|
||||
first_branch = &branch;
|
||||
continue;
|
||||
}
|
||||
|
||||
st_->atime = TimeSpec::newest(st_->atime,tmp_st.atime);
|
||||
st_->ctime = TimeSpec::newest(st_->ctime,tmp_st.ctime);
|
||||
st_->mtime = TimeSpec::newest(st_->mtime,tmp_st.mtime);
|
||||
st_->btime = TimeSpec::newest(st_->btime,tmp_st.btime);
|
||||
st_->nlink += tmp_st.nlink;
|
||||
}
|
||||
|
||||
if(!first_branch)
|
||||
return -ENOENT;
|
||||
|
||||
if(symlinkify_timeout_ >= 0)
|
||||
{
|
||||
fullpath = first_branch->path / fusepath_;
|
||||
symlinkify::convert_if_can_be_symlink(fullpath,
|
||||
st_,
|
||||
symlinkify_timeout_);
|
||||
}
|
||||
|
||||
fs::inode::calc(first_branch->path,fusepath_,st_);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#include "func_statx_base.hpp"
|
||||
|
||||
namespace Func2
|
||||
{
|
||||
class StatxCombine : public StatxBase
|
||||
{
|
||||
public:
|
||||
StatxCombine() {}
|
||||
~StatxCombine() {}
|
||||
|
||||
public:
|
||||
std::string_view name() const;
|
||||
|
||||
public:
|
||||
int operator()(const Branches &branches,
|
||||
const fs::path &fusepath,
|
||||
const u32 flags_,
|
||||
const u32 mask_,
|
||||
struct fuse_statx *st,
|
||||
const FollowSymlinksEnum follow_symlinks,
|
||||
const s64 symlinkify_timeout);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
#include "func_statx_factory.hpp"
|
||||
|
||||
#include "func_statx_combine.hpp"
|
||||
#include "func_statx_ff.hpp"
|
||||
#include "func_statx_newest.hpp"
|
||||
|
||||
#include "policies.hpp"
|
||||
|
||||
bool
|
||||
Func2::StatxFactory::valid(const std::string str_)
|
||||
{
|
||||
if(str_ == "combined")
|
||||
return true;
|
||||
if(str_ == "ff")
|
||||
return true;
|
||||
if(str_ == "newest")
|
||||
return true;
|
||||
|
||||
if(Policies::Search::find(str_))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::shared_ptr<Func2::StatxBase>
|
||||
Func2::StatxFactory::make(const std::string_view str_)
|
||||
{
|
||||
if(str_ == "combine")
|
||||
return std::make_shared<Func2::StatxCombine>();
|
||||
if(str_ == "ff")
|
||||
return std::make_shared<Func2::StatxFF>();
|
||||
if(str_ == "newest")
|
||||
return std::make_shared<Func2::StatxNewest>();
|
||||
|
||||
if(Policies::Search::find(str_))
|
||||
return std::make_shared<Func2::StatxCombine>();
|
||||
|
||||
return {};
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "func_statx_base.hpp"
|
||||
|
||||
namespace Func2
|
||||
{
|
||||
class StatxFactory
|
||||
{
|
||||
public:
|
||||
static bool valid(const std::string str);
|
||||
static std::shared_ptr<StatxBase> make(const std::string_view str);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
#include "func_statx_ff.hpp"
|
||||
|
||||
#include "fs_statx.hpp"
|
||||
#include "fs_inode.hpp"
|
||||
#include "symlinkify.hpp"
|
||||
|
||||
|
||||
std::string_view
|
||||
Func2::StatxFF::name() const
|
||||
{
|
||||
return "ff";
|
||||
}
|
||||
|
||||
int
|
||||
Func2::StatxFF::operator()(const Branches &branches_,
|
||||
const fs::path &fusepath_,
|
||||
const u32 flags_,
|
||||
const u32 mask_,
|
||||
struct fuse_statx *st_,
|
||||
const FollowSymlinksEnum follow_symlinks_,
|
||||
const s64 symlinkify_timeout_)
|
||||
{
|
||||
int rv;
|
||||
fs::path fullpath;
|
||||
|
||||
for(const auto &branch : branches_)
|
||||
{
|
||||
fullpath = branch.path / fusepath_;
|
||||
rv = fs::statx(AT_FDCWD,
|
||||
fullpath,
|
||||
flags_|AT_SYMLINK_NOFOLLOW,
|
||||
mask_,
|
||||
st_,
|
||||
follow_symlinks_);
|
||||
if(rv < 0)
|
||||
continue;
|
||||
|
||||
symlinkify::convert_if_can_be_symlink(fullpath,
|
||||
st_,
|
||||
symlinkify_timeout_);
|
||||
|
||||
fs::inode::calc(branch.path,fusepath_,st_);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "func_statx_base.hpp"
|
||||
|
||||
namespace Func2
|
||||
{
|
||||
class StatxFF : public StatxBase
|
||||
{
|
||||
public:
|
||||
StatxFF() {}
|
||||
~StatxFF() {}
|
||||
|
||||
public:
|
||||
std::string_view name() const;
|
||||
|
||||
public:
|
||||
int operator()(const Branches &branches,
|
||||
const fs::path &fusepath,
|
||||
const u32 flags_,
|
||||
const u32 mask_,
|
||||
struct fuse_statx *st,
|
||||
const FollowSymlinksEnum follow_symlinks,
|
||||
const s64 symlinkify_timeout);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
#include "func_statx_newest.hpp"
|
||||
|
||||
#include "fs_statx.hpp"
|
||||
#include "fs_inode.hpp"
|
||||
#include "symlinkify.hpp"
|
||||
#include "timespec_utils.hpp"
|
||||
|
||||
|
||||
std::string_view
|
||||
Func2::StatxNewest::name() const
|
||||
{
|
||||
return "newest";
|
||||
}
|
||||
|
||||
int
|
||||
Func2::StatxNewest::operator()(const Branches &branches_,
|
||||
const fs::path &fusepath_,
|
||||
const u32 flags_,
|
||||
const u32 mask_,
|
||||
struct fuse_statx *st_,
|
||||
const FollowSymlinksEnum follow_symlinks_,
|
||||
const s64 symlinkify_timeout_)
|
||||
{
|
||||
int rv;
|
||||
fs::path fullpath;
|
||||
const Branch *newest_branch;
|
||||
|
||||
newest_branch = nullptr;
|
||||
for(const auto &branch : branches_)
|
||||
{
|
||||
struct fuse_statx tmp_st;
|
||||
|
||||
fullpath = branch.path / fusepath_;
|
||||
rv = fs::statx(AT_FDCWD,
|
||||
fullpath,
|
||||
flags_|AT_SYMLINK_NOFOLLOW,
|
||||
mask_,
|
||||
&tmp_st,
|
||||
follow_symlinks_);
|
||||
if(rv < 0)
|
||||
continue;
|
||||
|
||||
if(not TimeSpec::is_newer(tmp_st.mtime,st_->mtime))
|
||||
continue;
|
||||
|
||||
*st_ = tmp_st;
|
||||
newest_branch = &branch;
|
||||
}
|
||||
|
||||
if(!newest_branch)
|
||||
return -ENOENT;
|
||||
|
||||
if(symlinkify_timeout_ >= 0)
|
||||
{
|
||||
fullpath = newest_branch->path / fusepath_;
|
||||
symlinkify::convert_if_can_be_symlink(fullpath,
|
||||
st_,
|
||||
symlinkify_timeout_);
|
||||
}
|
||||
|
||||
fs::inode::calc(newest_branch->path,fusepath_,st_);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#include "func_statx_base.hpp"
|
||||
|
||||
namespace Func2
|
||||
{
|
||||
class StatxNewest : public StatxBase
|
||||
{
|
||||
public:
|
||||
StatxNewest() {}
|
||||
~StatxNewest() {}
|
||||
|
||||
public:
|
||||
std::string_view name() const;
|
||||
|
||||
public:
|
||||
int operator()(const Branches &branches,
|
||||
const fs::path &fusepath,
|
||||
const u32 flags_,
|
||||
const u32 mask_,
|
||||
struct fuse_statx *st,
|
||||
const FollowSymlinksEnum follow_symlinks,
|
||||
const s64 symlinkify_timeout);
|
||||
};
|
||||
}
|
||||
+32
-93
@@ -31,45 +31,11 @@
|
||||
|
||||
#include "fuse.h"
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
static
|
||||
void
|
||||
_set_stat_if_leads_to_dir(const fs::path &path_,
|
||||
struct stat *st_)
|
||||
{
|
||||
int rv;
|
||||
struct stat st;
|
||||
|
||||
rv = fs::stat(path_,&st);
|
||||
if(rv < 0)
|
||||
return;
|
||||
|
||||
if(S_ISDIR(st.st_mode))
|
||||
*st_ = st;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
_set_stat_if_leads_to_reg(const fs::path &path_,
|
||||
struct stat *st_)
|
||||
{
|
||||
int rv;
|
||||
struct stat st;
|
||||
|
||||
rv = fs::stat(path_,&st);
|
||||
if(rv < 0)
|
||||
return;
|
||||
|
||||
if(S_ISREG(st.st_mode))
|
||||
*st_ = st;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
_getattr_fake_root(struct stat *st_)
|
||||
@@ -117,58 +83,15 @@ _getattr_controlfile(struct stat *st_)
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
_getattr(const Policy::Search &searchFunc_,
|
||||
const Branches &branches_,
|
||||
const fs::path &fusepath_,
|
||||
struct stat *st_,
|
||||
const bool symlinkify_,
|
||||
const time_t symlinkify_timeout_,
|
||||
FollowSymlinks followsymlinks_)
|
||||
bool
|
||||
should_break(const fs::path &fusepath_)
|
||||
{
|
||||
int rv;
|
||||
fs::path fullpath;
|
||||
std::vector<Branch*> branches;
|
||||
fs::path f = fusepath_.filename();
|
||||
|
||||
rv = searchFunc_(branches_,fusepath_,branches);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
if(str::endswith(f,"Hook"))
|
||||
return false;
|
||||
|
||||
fullpath = branches[0]->path / fusepath_;
|
||||
|
||||
switch(followsymlinks_)
|
||||
{
|
||||
case FollowSymlinks::ENUM::NEVER:
|
||||
rv = fs::lstat(fullpath,st_);
|
||||
break;
|
||||
case FollowSymlinks::ENUM::DIRECTORY:
|
||||
rv = fs::lstat(fullpath,st_);
|
||||
if(S_ISLNK(st_->st_mode))
|
||||
::_set_stat_if_leads_to_dir(fullpath,st_);
|
||||
break;
|
||||
case FollowSymlinks::ENUM::REGULAR:
|
||||
rv = fs::lstat(fullpath,st_);
|
||||
if(S_ISLNK(st_->st_mode))
|
||||
::_set_stat_if_leads_to_reg(fullpath,st_);
|
||||
break;
|
||||
case FollowSymlinks::ENUM::ALL:
|
||||
rv = fs::stat(fullpath,st_);
|
||||
if(rv < 0)
|
||||
rv = fs::lstat(fullpath,st_);
|
||||
break;
|
||||
}
|
||||
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
|
||||
if(symlinkify_ && symlinkify::can_be_symlink(*st_,symlinkify_timeout_))
|
||||
symlinkify::convert(fullpath,st_);
|
||||
|
||||
fs::inode::calc(branches[0]->path,
|
||||
fusepath_,
|
||||
st_);
|
||||
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
@@ -178,13 +101,28 @@ _getattr(const fs::path &fusepath_,
|
||||
{
|
||||
int rv;
|
||||
|
||||
rv = ::_getattr(cfg.func.getattr.policy,
|
||||
cfg.branches,
|
||||
fusepath_,
|
||||
st_,
|
||||
cfg.symlinkify,
|
||||
cfg.symlinkify_timeout,
|
||||
cfg.follow_symlinks);
|
||||
rv = cfg.getattr(cfg.branches,
|
||||
fusepath_,
|
||||
st_,
|
||||
cfg.follow_symlinks,
|
||||
cfg.symlinkify_timeout);
|
||||
if(rv < 0)
|
||||
{
|
||||
fmt::println("getattr: {}\n"
|
||||
" uid: {}\n"
|
||||
" gid: {}\n"
|
||||
" err: {}\n"
|
||||
,
|
||||
fusepath_.string(),
|
||||
geteuid(),
|
||||
getegid(),
|
||||
strerror(-rv)
|
||||
);
|
||||
|
||||
if(should_break(fusepath_))
|
||||
raise(SIGTRAP);
|
||||
}
|
||||
|
||||
if((rv < 0) && Config::is_rootdir(fusepath_))
|
||||
return ::_getattr_fake_root(st_);
|
||||
|
||||
@@ -213,7 +151,8 @@ FUSE::getattr(const fuse_req_ctx_t *ctx_,
|
||||
struct stat *st_,
|
||||
fuse_timeouts_t *timeout_)
|
||||
{
|
||||
const ugid::Set ugid(ctx_);
|
||||
// const ugid::Set ugid(ctx_);
|
||||
const ugid::SetRootGuard _;
|
||||
|
||||
return FUSE::getattr(fusepath_,st_,timeout_);
|
||||
}
|
||||
|
||||
+5
-1
@@ -281,7 +281,8 @@ _open_for_insert_lambda(const fuse_req_ctx_t *ctx_,
|
||||
{
|
||||
int rv;
|
||||
FileInfo *fi;
|
||||
const ugid::Set ugid(ctx_);
|
||||
//const ugid::Set ugid(ctx_);
|
||||
const ugid::SetRootGuard asdf;
|
||||
|
||||
::_config_to_ffi_flags(cfg,ctx_->pid,ffi_);
|
||||
|
||||
@@ -438,6 +439,9 @@ _open(const fuse_req_ctx_t *ctx_,
|
||||
return (val_.second.ref_count <= 0);
|
||||
});
|
||||
|
||||
if(rv < 0)
|
||||
__builtin_trap();
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,49 +30,11 @@
|
||||
#include "symlinkify.hpp"
|
||||
#include "ugid.hpp"
|
||||
|
||||
#include "fmt/core.h"
|
||||
|
||||
#include "fuse.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
static
|
||||
void
|
||||
_set_stat_if_leads_to_dir(const fs::path &path_,
|
||||
struct fuse_statx *st_)
|
||||
{
|
||||
int rv;
|
||||
struct fuse_statx st;
|
||||
|
||||
rv = fs::statx(AT_FDCWD,path_,AT_SYMLINK_FOLLOW,STATX_TYPE,&st);
|
||||
if(rv < 0)
|
||||
return;
|
||||
|
||||
if(S_ISDIR(st.mode))
|
||||
*st_ = st;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
_set_stat_if_leads_to_reg(const fs::path &path_,
|
||||
struct fuse_statx *st_)
|
||||
{
|
||||
int rv;
|
||||
struct fuse_statx st;
|
||||
|
||||
rv = fs::statx(AT_FDCWD,path_,AT_SYMLINK_FOLLOW,STATX_TYPE,&st);
|
||||
if(rv < 0)
|
||||
return;
|
||||
|
||||
if(S_ISREG(st.mode))
|
||||
*st_ = st;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
_statx_fake_root(struct fuse_statx *st_)
|
||||
@@ -113,63 +75,6 @@ _statx_controlfile(struct fuse_statx *st_)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
_statx(const Policy::Search &searchFunc_,
|
||||
const Branches &branches_,
|
||||
const fs::path &fusepath_,
|
||||
const uint32_t flags_,
|
||||
const uint32_t mask_,
|
||||
struct fuse_statx *st_,
|
||||
const bool symlinkify_,
|
||||
const time_t symlinkify_timeout_,
|
||||
FollowSymlinks followsymlinks_)
|
||||
{
|
||||
int rv;
|
||||
fs::path fullpath;
|
||||
std::vector<Branch*> branches;
|
||||
|
||||
rv = searchFunc_(branches_,fusepath_,branches);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
|
||||
fullpath = branches[0]->path / fusepath_;
|
||||
|
||||
switch(followsymlinks_)
|
||||
{
|
||||
case FollowSymlinks::ENUM::NEVER:
|
||||
rv = fs::statx(AT_FDCWD,fullpath,flags_|AT_SYMLINK_NOFOLLOW,mask_,st_);
|
||||
break;
|
||||
case FollowSymlinks::ENUM::DIRECTORY:
|
||||
rv = fs::statx(AT_FDCWD,fullpath,flags_|AT_SYMLINK_NOFOLLOW,mask_,st_);
|
||||
if((rv >= 0) && S_ISLNK(st_->mode))
|
||||
::_set_stat_if_leads_to_dir(fullpath,st_);
|
||||
break;
|
||||
case FollowSymlinks::ENUM::REGULAR:
|
||||
rv = fs::statx(AT_FDCWD,fullpath,flags_|AT_SYMLINK_NOFOLLOW,mask_,st_);
|
||||
if((rv >= 0) && S_ISLNK(st_->mode))
|
||||
::_set_stat_if_leads_to_reg(fullpath,st_);
|
||||
break;
|
||||
case FollowSymlinks::ENUM::ALL:
|
||||
rv = fs::statx(AT_FDCWD,fullpath,flags_|AT_SYMLINK_FOLLOW,mask_,st_);
|
||||
if(rv < 0)
|
||||
rv = fs::statx(AT_FDCWD,fullpath,flags_|AT_SYMLINK_NOFOLLOW,mask_,st_);
|
||||
break;
|
||||
}
|
||||
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
|
||||
if(symlinkify_ && symlinkify::can_be_symlink(*st_,symlinkify_timeout_))
|
||||
symlinkify::convert(fullpath,st_);
|
||||
|
||||
fs::inode::calc(branches[0]->path,
|
||||
fusepath_,
|
||||
st_);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
_statx(const fs::path &fusepath_,
|
||||
@@ -180,15 +85,13 @@ _statx(const fs::path &fusepath_,
|
||||
{
|
||||
int rv;
|
||||
|
||||
rv = ::_statx(cfg.func.getattr.policy,
|
||||
cfg.branches,
|
||||
fusepath_,
|
||||
flags_,
|
||||
mask_,
|
||||
st_,
|
||||
cfg.symlinkify,
|
||||
cfg.symlinkify_timeout,
|
||||
cfg.follow_symlinks);
|
||||
rv = cfg.statx(cfg.branches,
|
||||
fusepath_,
|
||||
flags_,
|
||||
mask_,
|
||||
st_,
|
||||
cfg.follow_symlinks,
|
||||
cfg.symlinkify_timeout);
|
||||
if((rv < 0) && Config::is_rootdir(fusepath_))
|
||||
return ::_statx_fake_root(st_);
|
||||
|
||||
|
||||
+6
-2
@@ -37,7 +37,7 @@ _create(const Branches::Ptr &ibranches_,
|
||||
int error;
|
||||
fs::info_t info;
|
||||
|
||||
error = ENOENT;
|
||||
error = ENXIO;
|
||||
for(auto &branch : *ibranches_)
|
||||
{
|
||||
if(branch.ro_or_nc())
|
||||
@@ -74,6 +74,8 @@ Policy::FF::Create::operator()(const Branches::Ptr &branches_,
|
||||
return ::_create(branches_,paths_);
|
||||
}
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
int
|
||||
Policy::FF::Search::operator()(const Branches::Ptr &branches_,
|
||||
const fs::path &fusepath_,
|
||||
@@ -89,5 +91,7 @@ Policy::FF::Search::operator()(const Branches::Ptr &branches_,
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
raise(SIGTRAP);
|
||||
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
#pragma once
|
||||
|
||||
namespace TimeSpec
|
||||
{
|
||||
static
|
||||
inline
|
||||
bool
|
||||
is_newer(const timespec &t0_,
|
||||
const timespec &t1_)
|
||||
{
|
||||
if(t0_.tv_sec > t1_.tv_sec)
|
||||
return true;
|
||||
if(t0_.tv_sec == t1_.tv_sec)
|
||||
{
|
||||
if(t0_.tv_nsec > t1_.tv_nsec)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
timespec
|
||||
newest(const timespec &t0_,
|
||||
const timespec &t1_)
|
||||
{
|
||||
if(t0_.tv_sec > t1_.tv_sec)
|
||||
return t0_;
|
||||
if(t0_.tv_sec == t1_.tv_sec)
|
||||
{
|
||||
if(t0_.tv_nsec > t1_.tv_nsec)
|
||||
return t0_;
|
||||
}
|
||||
|
||||
return t1_;
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
bool
|
||||
is_newer(const fuse_sx_time &t0_,
|
||||
const fuse_sx_time &t1_)
|
||||
{
|
||||
if(t0_.tv_sec > t1_.tv_sec)
|
||||
return true;
|
||||
if(t0_.tv_sec == t1_.tv_sec)
|
||||
{
|
||||
if(t0_.tv_nsec > t1_.tv_nsec)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
fuse_sx_time
|
||||
newest(const fuse_sx_time &t0_,
|
||||
const fuse_sx_time &t1_)
|
||||
{
|
||||
if(t0_.tv_sec > t1_.tv_sec)
|
||||
return t0_;
|
||||
if(t0_.tv_sec == t1_.tv_sec)
|
||||
{
|
||||
if(t0_.tv_nsec > t1_.tv_nsec)
|
||||
return t0_;
|
||||
}
|
||||
|
||||
return t1_;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
|
||||
template<typename PathType>
|
||||
const char*
|
||||
to_cstr(const PathType &p_)
|
||||
{
|
||||
if constexpr (std::is_same_v<std::decay_t<PathType>, std::filesystem::path>)
|
||||
return p_.c_str();
|
||||
if constexpr (std::is_same_v<std::decay_t<PathType>, std::string>)
|
||||
return p_.c_str();
|
||||
if constexpr (std::is_same_v<std::decay_t<PathType>, const char*> ||
|
||||
std::is_same_v<std::decay_t<PathType>, char*>)
|
||||
return p_;
|
||||
|
||||
abort();
|
||||
return NULL;
|
||||
}
|
||||
Reference in New Issue
Block a user