]> git.saurik.com Git - apple/hfs.git/blob - tests/cases/test-lf-cs-plugin.c
hfs-556.41.1.tar.gz
[apple/hfs.git] / tests / cases / test-lf-cs-plugin.c
1 //
2 // Copyright (c) 2019-2019 Apple Inc. All rights reserved.
3 //
4 // test-lf-cs-plugin.c - Implements unit test for livefiles Apple_CoreStorage
5 // plugin.
6 //
7
8 #include "hfs-tests.h"
9 #include "test-utils.h"
10 #include "systemx.h"
11
12 #include <fcntl.h>
13 #include <spawn.h>
14 #include <sys/stat.h>
15 #include <sys/param.h>
16 #include <zlib.h>
17 #include <TargetConditionals.h>
18
19 //
20 // Enable this test on iOS and no other iOS-style platforms.
21 //
22 #if TARGET_OS_IOS
23
24 //
25 // Headers generated at build time. Containes compressed disk images for the
26 // corresponding file-system formats.
27 //
28 #include "JHFS+-dmg.dat"
29 #include "APFS-dmg.dat"
30 #include "EXFAT-dmg.dat"
31 #include "FAT32-dmg.dat"
32
33 TEST(lf_cs_plugin, .run_as_root = true)
34
35 #define LF_CS_PLUGIN_TEST_DMG "/tmp/lf_cs_plugin_test.sparseimage"
36 #define LF_CS_PLUGIN_PATH_TO_HDIK "/usr/sbin/hdik"
37 #define LF_CS_PLUGIN_INSTALL_PATH "/AppleInternal/CoreOS/tests/hfs/"
38
39 //
40 // The output of hdik gets truncated, so just search for the leading part of
41 // the UUID and partition scheme names.
42 //
43 #define LF_CSP_HFS_UUID "48465300-0000-11AA-AA11"
44 #define LF_CSP_APFS_UUID "7C3457EF-0000-11AA-AA11"
45 #define LF_CSP_EXFAT_PART_SCHEME "Windows_NTFS"
46 #define LF_CSP_FAT32_PART_SCHEME "DOS_FAT_32"
47
48 //
49 // Enums describing file-system formats.
50 //
51 typedef enum {
52 JHFS = 1,
53 APFS,
54 FAT32,
55 EXFAT,
56 } lf_csp_fstype_t;
57
58 //
59 // Local struct describing the disk image on disk.
60 //
61 typedef struct {
62 char *path; // Path to disk-image on disk.
63 char *disk; // Path to dev node after disk-image is attached.
64 char *slice; // Path to dev node slice after disk-image is attached.
65 } lf_csp_disk_image_t;
66
67 //
68 // lf_csp_disk_image_cleanup - disattach disk image from the DiskImages driver
69 // and unlink the disk image file on disk.
70 //
71 static bool
72 lf_csp_disk_image_cleanup(lf_csp_disk_image_t *di)
73 {
74 pid_t pid;
75 posix_spawn_file_actions_t facts;
76
77 struct stat sb;
78 int ret, status;
79 bool result;
80 char *detach_args[] = { "hdik", "-e", (char *)di->disk, NULL };
81
82 result = false;
83 ret = posix_spawn_file_actions_init(&facts);
84
85 if (!ret) {
86 (void)posix_spawn_file_actions_addopen(&facts, STDOUT_FILENO,
87 "/dev/null", O_APPEND, 0);
88 (void)posix_spawn_file_actions_addopen(&facts, STDERR_FILENO,
89 "/dev/null", O_APPEND, 0);
90 }
91
92 assert_no_err(posix_spawn(&pid, LF_CS_PLUGIN_PATH_TO_HDIK, &facts,
93 NULL, detach_args, NULL));
94
95 if (ret) {
96 posix_spawn_file_actions_destroy(&facts);
97 }
98
99 assert_with_errno(ignore_eintr(waitpid(pid, &status, 0), -1) == pid);
100 errno = 0;
101 if (WIFEXITED(status) &&
102 !WEXITSTATUS(status) &&
103 (stat(di->disk, &sb) == -1) &&
104 (errno == ENOENT)) {
105
106 unlink(di->path);
107 free(di->path);
108 free(di->disk);
109 free(di->slice);
110 free(di);
111 result = true;
112 }
113
114 return result;
115 }
116
117 //
118 // lf_csp_disk_image_create - create a new disk image file on disk and attach
119 // disk images directly to the DiskImages driver.
120 //
121 // This routine reads the compressed file system image (generated during build)
122 // based on the passed file-system type parameter. The compressed image is
123 // decompressd and written to disk and then attached to directly to the
124 // DiskImages driver.
125 //
126 // Please NOTE: The created image is hooked up to the test cleanup list thus
127 // gets cleaned up automatically after the test, even on failures. Explicit
128 // cleanup is not needed.
129 //
130 static lf_csp_disk_image_t *
131 lf_csp_disk_image_create(const char *path, lf_csp_fstype_t fs_type)
132 {
133 pid_t pid;
134 uid_t uid_old;
135 lf_csp_disk_image_t *di;
136 z_stream u_stream;
137 posix_spawn_file_actions_t actions;
138
139 FILE *fp;
140 size_t lnsz;
141 int ret, fd, fds[2], status;
142 void *uncompressed_out_buf;
143 const size_t chunk_size = (1ULL << 20);
144 char *attach_args[4], *line, *part_scheme, *uuid_or_partid_str;
145
146 //
147 // We need to ensure that we are root.
148 //
149 if ((uid_old = geteuid()) != 0) {
150 assert_no_err(seteuid(0));
151 }
152
153 di = calloc(1, sizeof(lf_csp_disk_image_t));
154 assert(di);
155
156 //
157 // We need to extract the compressed image into the passed image
158 // file path, thus we open it for writing.
159 //
160 fd = open(path, O_RDWR | O_TRUNC | O_CREAT, 0666);
161 assert_with_errno(fd != -1);
162
163 u_stream = (z_stream) {
164 .zalloc = Z_NULL,
165 .zfree = Z_NULL,
166 .opaque = Z_NULL,
167 };
168
169 ret = inflateInit(&u_stream);
170 if (ret != Z_OK) {
171 assert_fail("inflateInit failed\n");
172 }
173
174 uncompressed_out_buf = malloc(chunk_size);
175 assert(uncompressed_out_buf);
176
177 uuid_or_partid_str = NULL;
178 part_scheme = NULL;
179 switch(fs_type) {
180 case JHFS:
181 u_stream.next_in = JHFS_data;
182 u_stream.avail_in = sizeof(JHFS_data);
183 uuid_or_partid_str = LF_CSP_HFS_UUID;
184 part_scheme = "GUID";
185 break;
186
187 case APFS:
188 u_stream.next_in = APFS_data;
189 u_stream.avail_in = sizeof(APFS_data);
190 uuid_or_partid_str = LF_CSP_APFS_UUID;
191 part_scheme = "GUID";
192 break;
193
194 case FAT32:
195 u_stream.next_in = FAT32_data;
196 u_stream.avail_in = sizeof(FAT32_data);
197 uuid_or_partid_str = LF_CSP_FAT32_PART_SCHEME;
198 part_scheme = "FDisk";
199 break;
200
201 case EXFAT:
202 u_stream.next_in = EXFAT_data;
203 u_stream.avail_in = sizeof(EXFAT_data);
204 uuid_or_partid_str = LF_CSP_EXFAT_PART_SCHEME;
205 part_scheme = "FDisk";
206 break;
207
208 default:
209 assert_fail("passed unkown file-system type\n");
210 }
211
212 do {
213 ssize_t bytes_to_write;
214
215 u_stream.next_out = uncompressed_out_buf;
216 u_stream.avail_out = chunk_size;
217
218 ret = inflate(&u_stream, Z_NO_FLUSH);
219 assert(ret != Z_STREAM_ERROR);
220
221 bytes_to_write = chunk_size - u_stream.avail_out;
222 assert(write(fd, uncompressed_out_buf, bytes_to_write) ==
223 (ssize_t)bytes_to_write);
224 } while (ret == Z_OK);
225 assert(ret == Z_STREAM_END);
226 (void)inflateEnd(&u_stream);
227
228 //
229 // Update the disk image path.
230 //
231 di->path = strdup(path);
232
233 //
234 // Attach the created disk image directly to the DiskImage driver.
235 //
236 attach_args[0] = "hdik";
237 attach_args[1] = "-nomount";
238 attach_args[2] = (char *)di->path;
239 attach_args[3] = NULL;
240
241 assert_no_err(pipe(fds));
242 ret = posix_spawn_file_actions_init(&actions);
243 if (ret) {
244 assert_fail("Failed to init file actions, error %d\n", ret);
245 }
246
247 ret = posix_spawn_file_actions_adddup2(&actions, fds[1],
248 STDOUT_FILENO);
249 if (ret) {
250 assert_fail("Failed to adddup file action, error %d\n",
251 ret);
252 }
253
254 assert_no_err(posix_spawn(&pid, LF_CS_PLUGIN_PATH_TO_HDIK, &actions,
255 NULL, attach_args, NULL));
256
257 (void)posix_spawn_file_actions_destroy(&actions);
258 (void)close(fds[1]);
259
260 //
261 // Read the output from `hdik` and populate the disk image's dev node
262 // after it is attached to DiskImage driver.
263 //
264 di->slice = NULL;
265 di->disk = NULL;
266 line = NULL;
267 lnsz = 64;
268 line = malloc(lnsz);
269 assert(line);
270 fp = fdopen(fds[0], "r");
271 while (getline(&line, &lnsz, fp) != -1) {
272 char *disk_path, *uuid_or_partid;
273
274 disk_path = strtok(line, " ");
275 assert(disk_path);
276
277 uuid_or_partid = strtok(NULL, " ");
278 assert(uuid_or_partid);
279
280 if (strstr(uuid_or_partid, part_scheme))
281 di->disk = strdup(disk_path);
282 else if (strstr(uuid_or_partid, uuid_or_partid_str))
283 di->slice = strdup(disk_path);
284 }
285
286 assert_with_errno(ignore_eintr(waitpid(pid, &status, 0), -1) == pid);
287 assert(WIFEXITED(status) && !WEXITSTATUS(status));
288
289 assert(di->disk && di->slice);
290 free(line);
291 fclose(fp);
292
293 //
294 // Place this attached image in the cleanup list so that it can be
295 // disattached after the test is run.
296 //
297 test_cleanup(^ bool { return lf_csp_disk_image_cleanup(di); });
298
299 //
300 // Restore back the old uid.
301 //
302 assert_no_err(seteuid(uid_old));
303 return di;
304 }
305
306 int
307 run_lf_cs_plugin(__unused test_ctx_t *ctx)
308 {
309 lf_csp_disk_image_t *di;
310 char *tester_path;
311
312 assert(asprintf(&tester_path, "%s/livefiles_cs_tester",
313 LF_CS_PLUGIN_INSTALL_PATH) > 0);
314
315 //
316 // Call livefiles Apple_CoreStorage plugin with all our file-system
317 // disk-images.
318 //
319 // Kindly NOTE: We don't explicitly free the disk-images after use
320 // because preparing the disk image with lf_csp_disk_image_create() we
321 // place the image in a cleanup list and they get cleaned up after
322 // test, regardless of the test failures.
323 //
324 // Test livefiles Apple_CoreStorage plugin with AFPS disk image.
325 //
326 di = lf_csp_disk_image_create(LF_CS_PLUGIN_TEST_DMG, APFS);
327 assert(!systemx(tester_path, "APFS", di->slice, NULL));
328
329 //
330 // Test livefiles Apple_CoreStorage plugin with HFS+ disk image.
331 //
332 di = lf_csp_disk_image_create(LF_CS_PLUGIN_TEST_DMG, JHFS);
333 assert(!systemx(tester_path, "JHFS", di->slice, NULL));
334
335 //
336 // Test livefiles Apple_CoreStorage plugin with EXFAT disk image.
337 //
338 di = lf_csp_disk_image_create(LF_CS_PLUGIN_TEST_DMG, EXFAT);
339 assert(!systemx(tester_path, "EXFAT", di->slice, NULL));
340
341 //
342 // Test livefiles Apple_CoreStorage plugin with FAT32 disk image.
343 //
344 di = lf_csp_disk_image_create(LF_CS_PLUGIN_TEST_DMG, FAT32);
345 assert(!systemx(tester_path, "FAT32", di->slice, NULL));
346
347 free(tester_path);
348 return 0;
349 }
350 #endif /* TARGET_OS_IOS */