10 uint32_t version, payload_size;
14 unsigned char payload[64];
17static const char magic[] =
"FH-PERSIST-1";
18static const char payload[] =
"persistent fanotify handle\n";
20static int valid_token(
const char* token) {
21 size_t count = strlen(token);
22 if (!count || count >=
sizeof(((
struct manifest*)0)->token))
24 for (
size_t n = 0; n < count; ++n) {
26 if (!((ch >=
'a' && ch <=
'z') || (ch >=
'A' && ch <=
'Z') || (ch >=
'0' && ch <=
'9') ||
27 ch ==
'-' || ch ==
'_'))
32static int save(
int directory,
const char* name,
const void* bytes,
size_t size) {
33 int fd = openat(directory, name, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, 0600);
36 int failed = fh_write_all(fd, bytes, size) || fsync(fd);
39 return failed ? -1 : 0;
41static int load(
int directory,
const char* name,
void* bytes,
size_t size) {
42 int fd = openat(directory, name, O_RDONLY | O_CLOEXEC);
46 int failed = fh_read_all(fd, bytes, size) || read(fd, &extra, 1) != 0;
49 return failed ? -1 : 0;
52int fh_persistence(
const char* base,
const char* token,
int write_stage) {
53 int failed = 0, directory = -1, parent = -1, target = -1, stale = -1, decoded = -1, alias = -1;
55 struct stat state, alias_state;
56 char completion[128], actual_completion[128], parent_path[192];
57 char original_path[224], renamed_path[224], alias_path[224], stale_path[224];
58 CHECK(base[0] ==
'/' && strlen(base) <
sizeof(parent_path) - 1 && valid_token(token));
59 CHECK(base[strlen(base) - 1] !=
'/');
60 const char* slash = strrchr(base,
'/');
61 size_t parent_length = slash == base ? 1 : (size_t)(slash - base);
62 memcpy(parent_path, base, parent_length);
63 parent_path[parent_length] = 0;
64 snprintf(original_path,
sizeof(original_path),
"%s/target", base);
65 snprintf(renamed_path,
sizeof(renamed_path),
"%s/renamed", base);
66 snprintf(alias_path,
sizeof(alias_path),
"%s/alias", base);
67 snprintf(stale_path,
sizeof(stale_path),
"%s/stale", base);
68 int completion_length =
69 snprintf(completion,
sizeof(completion),
"FH-HANDLE-COMPLETE 1 %s\n", token);
70 CHECK(completion_length > 0 && (
size_t)completion_length <
sizeof(completion));
72 CHECK(!mkdir(base, 0700));
73 parent = open(parent_path, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
76 directory = open(base, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
77 CHECK(directory >= 0);
79 memcpy(
manifest.magic, magic,
sizeof(magic));
81 manifest.payload_size =
sizeof(payload) - 1;
83 memcpy(
manifest.token, token, strlen(token) + 1);
84 target = open(original_path, O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC, 0600);
85 CHECK(target >= 0 && !fh_write_all(target,
manifest.payload,
manifest.payload_size));
87 CHECK(!fstat(target, &state));
89 CHECK(!link(original_path, alias_path) && !rename(original_path, renamed_path));
90 CHECK(!fsync(target) && !close(target));
92 stale = open(stale_path, O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC, 0600);
94 CHECK(stale >= 0 && !fh_export(stale, &
manifest.stale, &stale_mount));
95 CHECK(stale_mount ==
manifest.writer_mount && !fsync(stale));
96 CHECK(!unlink(stale_path) && !close(stale));
99 CHECK(!fsync(directory) && !fsync(parent));
100 CHECK(!save(directory,
"complete", completion, (
size_t)completion_length));
101 CHECK(!fsync(directory) && !fsync(parent));
103 CHECK(!load(directory,
"complete", actual_completion, (
size_t)completion_length));
104 CHECK(!memcmp(actual_completion, completion, (
size_t)completion_length));
106 CHECK(!memcmp(
manifest.magic, magic,
sizeof(magic)) &&
manifest.version == 1);
107 CHECK(!memcmp(
manifest.token, token, strlen(token) + 1));
108 CHECK(
manifest.payload_size ==
sizeof(payload) - 1 &&
109 !memcmp(
manifest.payload, payload,
sizeof(payload) - 1));
110 CHECK(
manifest.linked.handle_bytes &&
manifest.linked.handle_bytes <= 128 &&
112 decoded = open_by_handle_at(directory, (
struct file_handle*)&
manifest.linked, O_RDONLY);
113 CHECK(decoded >= 0 && !fstat(decoded, &state) && (uint64_t)state.st_ino ==
manifest.inode);
114 unsigned char actual[
sizeof(
manifest.payload)];
115 CHECK(!fh_read_all(decoded, actual,
manifest.payload_size));
117 CHECK(read(decoded, actual, 1) == 0);
118 target = open(renamed_path, O_RDONLY | O_CLOEXEC);
119 alias = open(alias_path, O_RDONLY | O_CLOEXEC);
120 CHECK(target >= 0 && alias >= 0 && !fstat(alias, &alias_state));
121 CHECK(alias_state.st_ino == state.st_ino && alias_state.st_nlink == 2);
124 CHECK(!fh_export(target, &fresh, &mount_id) && fh_equal(&fresh, &
manifest.linked));
125 CHECK(!fh_export(alias, &fresh, &mount_id) && fh_equal(&fresh, &
manifest.linked));
127 CHECK(open_by_handle_at(directory, (
struct file_handle*)&
manifest.stale, O_RDONLY) == -1 &&
129 printf(
"FANOTIFY-HANDLE-PERSIST: verified inode=%llu writer_mount=%d reader_mount=%d\n",
133 if (alias >= 0 && close(alias))
135 if (decoded >= 0 && close(decoded))
137 if (stale >= 0 && close(stale))
139 if (target >= 0 && close(target))
141 if (directory >= 0 && close(directory))
143 if (parent >= 0 && close(parent))