11 char base[192], parent[192], original[224], renamed[224], alias[224];
12 char directory[224], empty[224], completion[224];
15static const unsigned char binary[] = {0, 0xff, 0x17, 0, 0x93, 4, 0x80};
16static const unsigned char replacement[] = {0x51, 0, 0xe7, 0x22};
17static const char*
const file_names[] = {
"user.binary",
"user.empty",
"user.replaced"};
19static int path_init(
struct paths*
paths,
const char* base) {
20 const size_t length = strlen(base);
21 if (length < 2 || length >=
sizeof(
paths->base) || base[0] !=
'/' || base[length - 1] ==
'/')
23 strcpy(
paths->base, base);
24 strcpy(
paths->parent, base);
25 char* end = strrchr(
paths->parent,
'/');
26 if (end ==
paths->parent)
30 snprintf(
paths->original,
sizeof(
paths->original),
"%s/payload-original", base);
31 snprintf(
paths->renamed,
sizeof(
paths->renamed),
"%s/payload-renamed", base);
32 snprintf(
paths->alias,
sizeof(
paths->alias),
"%s/payload-link", base);
33 snprintf(
paths->directory,
sizeof(
paths->directory),
"%s/directory", base);
34 snprintf(
paths->empty,
sizeof(
paths->empty),
"%s/ea-only", base);
35 snprintf(
paths->completion,
sizeof(
paths->completion),
"%s/complete", base);
39static void payload(
unsigned char bytes[513]) {
40 for (
size_t n = 0; n < 513; ++n)
41 bytes[n] = (
unsigned char)(n * 29 + 3);
44static int sync_directory(
const char* path) {
45 int fd = open(path, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
48 int result = fsync(fd), saved = errno;
49 if (close(fd) && !result) {
57static int create_fixtures(
const struct paths*
paths) {
58 int failed = 0, fd = -1, directory = -1, empty = -1;
60 unsigned char bytes[513];
63 CHECK(lstat(
paths->base, &status) == -1 && errno == ENOENT);
64 CHECK(!mkdir(
paths->base, 0700));
65 CHECK(!mkdir(
paths->directory, 0700));
66 directory = open(
paths->directory, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
67 CHECK(directory >= 0);
68 CHECK(!fsetxattr(directory,
"user.directory", binary,
sizeof(binary), XATTR_CREATE));
69 fd = open(
paths->original, O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC, 0600);
71 CHECK(!xa_write_all(fd, bytes,
sizeof(bytes)));
72 CHECK(!fsetxattr(fd,
"user.binary", binary,
sizeof(binary), XATTR_CREATE));
73 CHECK(!fsetxattr(fd,
"user.empty", NULL, 0, XATTR_CREATE));
74 CHECK(!fsetxattr(fd,
"user.replaced",
"old", 3, XATTR_CREATE));
75 CHECK(!fsetxattr(fd,
"user.replaced", replacement,
sizeof(replacement), XATTR_REPLACE));
76 CHECK(!fsetxattr(fd,
"user.removed",
"gone", 4, XATTR_CREATE));
77 CHECK(!fremovexattr(fd,
"user.removed"));
79 CHECK(!rename(
paths->original,
paths->renamed));
80 empty = open(
paths->empty, O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC, 0600);
82 CHECK(!fstat(empty, &status) && !status.st_size && !status.st_blocks);
83 CHECK(!fsetxattr(empty,
"user.only", binary,
sizeof(binary), XATTR_CREATE));
84 CHECK(!fstat(empty, &status) && !status.st_size &&
85 status.st_blocks == (blkcnt_t)(xa_block / 512));
86 CHECK(!fsync(fd) && !fsync(empty) && !fsync(directory));
87 CHECK(!sync_directory(
paths->base) && !sync_directory(
paths->parent));
89 if (empty >= 0 && close(empty))
91 if (directory >= 0 && close(directory))
93 if (fd >= 0 && close(fd))
99static int verify_fixtures(
const struct paths*
paths) {
100 int failed = 0, renamed = -1, directory = -1, empty = -1;
101 struct xa_file file = {.fd = -1, .backend = XA_EXT2};
102 struct stat alias_status, renamed_status, status;
103 unsigned char expected[513], actual[513], extra;
105 CHECK(!stat(
paths->base, &status) && S_ISDIR(status.st_mode));
107 CHECK(lstat(
paths->original, &status) == -1 && errno == ENOENT);
108 file.fd = open(
paths->alias, O_RDONLY | O_CLOEXEC);
109 renamed = open(
paths->renamed, O_RDONLY | O_CLOEXEC);
110 CHECK(file.fd >= 0 && renamed >= 0);
111 CHECK(!fstat(file.fd, &alias_status) && !fstat(renamed, &renamed_status));
112 CHECK(S_ISREG(alias_status.st_mode) && S_ISREG(renamed_status.st_mode) &&
113 alias_status.st_dev == renamed_status.st_dev &&
114 alias_status.st_ino == renamed_status.st_ino && alias_status.st_nlink == 2 &&
115 renamed_status.st_nlink == 2 && alias_status.st_size ==
sizeof(expected));
116 CHECK(!xa_read_all(file.fd, actual,
sizeof(actual)) && !memcmp(actual, expected,
sizeof(actual)));
117 CHECK(read(file.fd, &extra, 1) == 0);
118 CHECK(!xa_value(&file, XA_FD,
"user.binary", binary,
sizeof(binary)));
119 CHECK(!xa_value(&file, XA_FD,
"user.empty", NULL, 0));
120 CHECK(!xa_value(&file, XA_FD,
"user.replaced", replacement,
sizeof(replacement)));
122 CHECK(fgetxattr(file.fd,
"user.removed", NULL, 0) == -1 && errno == ENODATA);
123 CHECK(!xa_names(&file, XA_FD, file_names,
sizeof(file_names) /
sizeof(file_names[0])));
124 struct xa_file second = {.fd = renamed, .backend = XA_EXT2};
125 CHECK(!xa_value(&second, XA_FD,
"user.binary", binary,
sizeof(binary)));
126 CHECK(!xa_names(&second, XA_FD, file_names,
sizeof(file_names) /
sizeof(file_names[0])));
127 directory = open(
paths->directory, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
128 CHECK(directory >= 0 && !fstat(directory, &status) && S_ISDIR(status.st_mode));
129 struct xa_file dir = {.fd = directory, .backend = XA_EXT2, .directory = 1};
130 const char* directory_names[] = {
"user.directory"};
131 CHECK(!xa_value(&dir, XA_FD,
"user.directory", binary,
sizeof(binary)));
132 CHECK(!xa_names(&dir, XA_FD, directory_names, 1));
133 empty = open(
paths->empty, O_RDONLY | O_CLOEXEC);
134 CHECK(empty >= 0 && !fstat(empty, &status));
135 CHECK(S_ISREG(status.st_mode) && !status.st_size &&
136 status.st_blocks == (blkcnt_t)(xa_block / 512));
137 struct xa_file ea_only = {.fd = empty, .backend = XA_EXT2};
138 const char* empty_names[] = {
"user.only"};
139 CHECK(!xa_value(&ea_only, XA_FD,
"user.only", binary,
sizeof(binary)));
140 CHECK(!xa_names(&ea_only, XA_FD, empty_names, 1));
153static int completion(
const struct paths*
paths,
const char* expected,
int write_stage) {
154 int failed = 0, fd = -1;
155 const size_t length = strlen(expected);
156 char actual[256], extra;
158 CHECK(length <
sizeof(actual));
160 fd = open(
paths->completion, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, 0600);
162 CHECK(!xa_write_all(fd, expected, length) && !fsync(fd));
165 CHECK(!sync_directory(
paths->base) && !sync_directory(
paths->parent));
167 fd = open(
paths->completion, O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
168 CHECK(fd >= 0 && !fstat(fd, &status) && S_ISREG(status.st_mode) &&
169 status.st_size == (off_t)length);
170 CHECK(!xa_read_all(fd, actual, length) && !memcmp(actual, expected, length));
171 CHECK(read(fd, &extra, 1) == 0);
179int xa_persistence(
const char* base,
const char* token,
int write_stage) {
182 const size_t token_length = strlen(token);
183 if (path_init(&
paths, base) || !token_length || token_length > 96)
185 for (
size_t n = 0; n < token_length; ++n) {
186 const unsigned char byte = token[n];
187 if (!((
byte >=
'a' &&
byte <=
'z') || (
byte >=
'A' &&
byte <=
'Z') ||
188 (
byte >=
'0' &&
byte <=
'9') ||
byte ==
'-' ||
byte ==
'_' ||
byte ==
'.'))
193 "PEDIGREE-XATTR-PERSIST 1\nfixture=1\ntoken=%s\nblock=%zu\n", token, xa_block);
194 if (length < 0 || (
size_t)length >=
sizeof(
manifest))
197 if (create_fixtures(&
paths) || verify_fixtures(&
paths))