The Pedigree Project 0.1
filesystem-allocation-contract-test/main.c
1#define _GNU_SOURCE
2#include <errno.h>
3#include <fcntl.h>
4#include <stdint.h>
5#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
8#include <unistd.h>
9
10#include <sys/mman.h>
11#include <sys/stat.h>
12
13#define CHECK(condition) \
14 do { \
15 if (!(condition)) { \
16 fprintf(stderr, "FILESYSTEM-ALLOCATION-CONTRACT: line=%d errno=%d\n", __LINE__, errno); \
17 goto fail; \
18 } \
19 } while (0)
20
21int main(int argc, char** argv) {
22 const char* directory = argc > 1 ? argv[1] : "/tmp";
23 char path[512];
24 snprintf(path, sizeof(path), "%s/allocation-%ld", directory, (long)getpid());
25 int fd = -1, readonly = -1, pathfd = -1, pipes[2] = {-1, -1};
26 int result = 1;
27 const long page = sysconf(_SC_PAGESIZE);
28 const unsigned char prefix[] = {13, 29, 47, 61, 83};
29 unsigned char* mapping = MAP_FAILED;
30 unsigned char* bytes = NULL;
31 struct stat before, reserved, after;
32 CHECK(page > 0);
33 const off_t offset = 5 * page + 11, length = 2 * page + 17, end = offset + length;
34 bytes = malloc((size_t)end);
35 CHECK(bytes != NULL);
36 fd = open(path, O_CREAT | O_EXCL | O_RDWR, 0600);
37 CHECK(fd >= 0);
38 CHECK(write(fd, prefix, sizeof(prefix)) == sizeof(prefix));
39 CHECK(lseek(fd, 37, SEEK_SET) == 37);
40 CHECK(fstat(fd, &before) == 0);
41
42 CHECK(fallocate(fd, FALLOC_FL_KEEP_SIZE, offset, length) == 0);
43 CHECK(fstat(fd, &reserved) == 0 && reserved.st_size == sizeof(prefix));
44 CHECK(reserved.st_blocks >= before.st_blocks + 2 * page / 512);
45 CHECK(lseek(fd, 0, SEEK_CUR) == 37);
46 CHECK(close(fd) == 0);
47 fd = open(path, O_RDWR);
48 CHECK(fd >= 0 && fstat(fd, &after) == 0);
49 CHECK(after.st_blocks == reserved.st_blocks && after.st_size == sizeof(prefix));
50 CHECK(fallocate(fd, FALLOC_FL_KEEP_SIZE, offset, length) == 0);
51 CHECK(fstat(fd, &after) == 0 && after.st_blocks == reserved.st_blocks);
52
53 mapping = mmap(NULL, page, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
54 CHECK(mapping != MAP_FAILED);
55 /* Dirty bytes beyond EOF must not become file data when allocation extends it. */
56 memset(mapping + sizeof(prefix), 0xe7, (size_t)page - sizeof(prefix));
57 CHECK(fallocate(fd, 0, offset, length) == 0);
58 CHECK(fstat(fd, &after) == 0 && after.st_size == end);
59 CHECK(after.st_blocks == reserved.st_blocks);
60 CHECK(memcmp(mapping, prefix, sizeof(prefix)) == 0);
61 for (size_t i = sizeof(prefix); i < (size_t)page; ++i)
62 CHECK(mapping[i] == 0);
63 CHECK(munmap(mapping, page) == 0);
64 mapping = MAP_FAILED;
65 memset(bytes, 0xcc, (size_t)end);
66 CHECK(pread(fd, bytes, (size_t)end, 0) == end);
67 CHECK(memcmp(bytes, prefix, sizeof(prefix)) == 0);
68 for (size_t i = sizeof(prefix); i < (size_t)end; ++i)
69 CHECK(bytes[i] == 0);
70
71 errno = EDOM;
72 CHECK(posix_fallocate(fd, offset, length) == 0 && errno == EDOM);
73 CHECK(fstat(fd, &before) == 0);
74 CHECK(pwrite(fd, prefix, sizeof(prefix), offset) == sizeof(prefix));
75 CHECK(fstat(fd, &after) == 0 && after.st_blocks == before.st_blocks);
76 CHECK(fsync(fd) == 0);
77 CHECK(pread(fd, bytes, sizeof(prefix), offset) == sizeof(prefix));
78 CHECK(memcmp(bytes, prefix, sizeof(prefix)) == 0);
79
80 readonly = open(path, O_RDONLY);
81 pathfd = open(path, O_PATH);
82 CHECK(readonly >= 0 && pathfd >= 0);
83 errno = 0;
84 CHECK(fallocate(readonly, 0, 0, 1) == -1 && errno == EBADF);
85 errno = 0;
86 CHECK(fallocate(pathfd, 0, 0, 1) == -1 && errno == EBADF);
87 errno = 0;
88 CHECK(fallocate(-1, 0, 0, 1) == -1 && errno == EBADF);
89 errno = 0;
90 CHECK(fallocate(fd, 0, -1, 1) == -1 && errno == EINVAL);
91 errno = 0;
92 CHECK(fallocate(fd, 0, 0, 0) == -1 && errno == EINVAL);
93 errno = 0;
94 CHECK(fallocate(fd, 0, INT64_MAX, 1) == -1 && errno == EFBIG);
95 errno = 0;
96 CHECK(fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 0, page) == -1 &&
97 errno == EOPNOTSUPP);
98 CHECK(pipe(pipes) == 0);
99 errno = 0;
100 CHECK(fallocate(pipes[1], 0, 0, 1) == -1 && errno == ESPIPE);
101 errno = EDOM;
102 CHECK(posix_fallocate(fd, 0, -1) == EINVAL && errno == EDOM);
103
104 CHECK(ftruncate(fd, 0) == 0);
105 CHECK(fstat(fd, &after) == 0 && after.st_blocks == 0);
106 CHECK(fallocate(fd, FALLOC_FL_KEEP_SIZE, offset, length) == 0);
107 CHECK(fstat(fd, &after) == 0 && after.st_size == 0 && after.st_blocks > 0);
108 CHECK(ftruncate(fd, 0) == 0);
109 CHECK(fstat(fd, &after) == 0 && after.st_blocks == 0);
110 CHECK(unlink(path) == 0);
111 CHECK(fallocate(fd, 0, 0, page) == 0);
112 CHECK(fstat(fd, &after) == 0 && after.st_size == page);
113 result = 0;
114
115fail:
116 if (mapping != MAP_FAILED)
117 munmap(mapping, page);
118 if (fd >= 0)
119 close(fd);
120 if (readonly >= 0)
121 close(readonly);
122 if (pathfd >= 0)
123 close(pathfd);
124 if (pipes[0] >= 0)
125 close(pipes[0]);
126 if (pipes[1] >= 0)
127 close(pipes[1]);
128 unlink(path);
129 free(bytes);
130 if (!result)
131 puts("FILESYSTEM-ALLOCATION-CONTRACT: PASS");
132 return result;
133}