17static int64_t now(
void) {
19 return clock_gettime(CLOCK_MONOTONIC, &time) ? -1
20 : (int64_t)time.tv_sec * 1000000000 + time.tv_nsec;
22int fr_reap(pid_t child,
int milliseconds) {
23 int64_t deadline = now() + (int64_t)milliseconds * 1000000;
24 while (now() < deadline) {
26 pid_t result = waitpid(child, &status, WNOHANG);
28 return WIFEXITED(status) ? WEXITSTATUS(status) : 128 + WTERMSIG(status);
29 if (result < 0 && errno != EINTR)
31 struct timespec pause = {0, 5000000};
32 nanosleep(&pause, NULL);
35 while (waitpid(child, NULL, 0) < 0 && errno == EINTR) {
39static void expected_bus(
int signal) {
43int fr_fault(
void* address) {
49 struct sigaction action = {.sa_handler = expected_bus};
50 sigemptyset(&action.sa_mask);
51 if (sigaction(SIGBUS, &action, NULL))
53 volatile unsigned char value = *(
volatile unsigned char*)address;
57 int result = fr_reap(child, 4000);
59 fprintf(stderr,
"FILE-RESIZE-CONTRACT: expected SIGBUS at %p, child status=%d\n", address,
63int fr_send(
int fd,
char byte) {
66 result = write(fd, &
byte, 1);
67 }
while (result < 0 && errno == EINTR);
68 return result == 1 ? 0 : -1;
70int fr_receive(
int fd,
char expected) {
71 int64_t deadline = now() + 5000000000;
72 while (now() < deadline) {
73 struct pollfd watch = {.fd = fd, .events = POLLIN};
74 int result = poll(&watch, 1, 100);
75 if (result < 0 && errno == EINTR)
82 ssize_t amount = read(fd, &
byte, 1);
83 if (amount < 0 && errno == EINTR)
85 if (amount == 1 &&
byte == expected)
93unsigned char fr_pattern(
size_t offset) {
94 return (
unsigned char)(0x31 + offset * 37 + (offset >> 8) * 11);
96int fr_matches(
const volatile unsigned char* bytes,
size_t offset,
size_t length,
int zero) {
97 for (
size_t n = 0; n < length; ++n) {
98 unsigned char expected = zero ? 0 : fr_pattern(offset + n);
99 unsigned char actual = bytes[n];
100 if (actual != expected) {
101 fprintf(stderr,
"FILE-RESIZE-CONTRACT: byte offset=%zu got=%u expected=%u\n", offset + n,
102 (
unsigned)actual, (
unsigned)expected);
108int fr_contents(
int fd,
size_t offset,
size_t length,
int zero) {
109 unsigned char bytes[512];
111 size_t amount = length <
sizeof(bytes) ? length : sizeof(bytes);
112 if (pread(fd, bytes, amount, offset) != (ssize_t)amount ||
113 !fr_matches(bytes, offset, amount, zero))
120int fr_size(
int fd,
size_t size) {
121 struct stat metadata;
122 if (fstat(fd, &metadata))
124 if (metadata.st_size == (off_t)size)
126 fprintf(stderr,
"FILE-RESIZE-CONTRACT: size=%lld expected=%zu\n", (
long long)metadata.st_size,
130int fr_resident(
void* address,
size_t pages,
unsigned bits,
const char* stage) {
131 unsigned char vector[4] = {0xa5, 0xa5, 0xa5, 0xa5};
132 if (pages >
sizeof(vector) || mincore(address, pages * fr_page, vector)) {
133 fprintf(stderr,
"FILE-RESIZE-CONTRACT: %s mincore failed errno=%d\n", stage, errno);
136 for (
size_t n = 0; n < pages; ++n) {
137 if ((vector[n] & 1) != ((bits >> n) & 1)) {
138 fprintf(stderr,
"FILE-RESIZE-CONTRACT: %s residency=%u,%u,%u,%u pages=%zu expected bits=%x\n",
139 stage, vector[0], vector[1], vector[2], vector[3], pages, bits);
145int fr_create(
struct fr_file* file,
int backend) {
146 static unsigned sequence;
148 file->backend = backend;
150 if (backend == FR_MEMFD)
151 file->fd = memfd_create(
"file-resize", MFD_CLOEXEC | MFD_ALLOW_SEALING);
153 snprintf(file->path,
sizeof(file->path),
"%s/file-resize-%ld-%u",
154 backend == FR_RAMFS ?
"/tmp" :
"", (long)getpid(), ++sequence);
155 file->fd = open(file->path, O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC, 0600);
157 fprintf(stderr,
"FILE-RESIZE-CONTRACT: create %s failed errno=%d\n", file->path, errno);
163 unsigned char* bytes = malloc(fr_page);
164 int error = !bytes || ftruncate(file->fd, 4 * fr_page);
165 for (
size_t offset = 0; !error && offset < 4 * fr_page; offset += fr_page) {
166 for (
size_t n = 0; n < fr_page; ++n)
167 bytes[n] = fr_pattern(offset + n);
168 error = pwrite(file->fd, bytes, fr_page, offset) != (ssize_t)fr_page;
179int fr_open_alias(
const struct fr_file* file) {
180 if (file->backend == FR_EXT2) {
182 snprintf(alias,
sizeof(alias),
"%s.alias", file->path);
183 if (link(file->path, alias))
185 int fd = open(alias, O_RDWR | O_CLOEXEC);
196 return file->path[0] ? open(file->path, O_RDWR | O_CLOEXEC) : dup(file->fd);
198void fr_close(
struct fr_file* file) {
206static int run(
const char* name,
int backend) {
207 printf(
"FILE-RESIZE-CONTRACT: BEGIN %s\n", name);
209 pid_t child = fork();
214 int result = backend < 0 ? fr_seals() : fr_shared(backend) || fr_private(backend);
217 _exit(result ? 1 : 0);
219 int result = fr_reap(child, 45000);
220 printf(
"FILE-RESIZE-CONTRACT: %s %s status=%d\n", result ?
"FAIL" :
"PASS", name, result);
224int main(
int argc,
char** argv) {
225 fr_page = (size_t)sysconf(_SC_PAGESIZE);
226 if (fr_page < 512 || fr_page > 65536 || (fr_page & (fr_page - 1)))
228 signal(SIGPIPE, SIG_IGN);
229 static const char* names[] = {
"memfd",
"ramfs",
"ext2",
"sealed"};
231 for (
int n = 0; n < 4; ++n) {
232 if (argc > 1 && strcmp(argv[1], names[n]))
235 if (run(names[n], n == 3 ? -1 : n))
240 puts(
"FILE-RESIZE-CONTRACT: END PASS");