5 // Created by Chris Suter on 8/12/15.
12 #include <sys/param.h>
13 #include <sys/mount.h>
19 #include <Foundation/Foundation.h>
20 #include <TargetConditionals.h>
22 #include "disk-image.h"
23 #include "test-utils.h"
26 #if TARGET_OS_EMBEDDED
30 bool disk_image_cleanup(disk_image_t *di)
36 assert(seteuid(0) == 0);
39 = { "umount", "-f", (char *)di->mount_point, NULL };
41 assert_no_err(posix_spawn(&pid, "/sbin/umount", NULL, NULL, umount_args, NULL));
44 waitpid(pid, &status, 0);
47 = { "hdik", "-e", (char *)di->disk, NULL };
49 posix_spawn_file_actions_t facts;
50 posix_spawn_file_actions_init(&facts);
51 posix_spawn_file_actions_addopen(&facts, STDOUT_FILENO, "/dev/null", O_APPEND, 0);
52 posix_spawn_file_actions_addopen(&facts, STDERR_FILENO, "/dev/null", O_APPEND, 0);
54 assert_no_err(posix_spawn(&pid, "/usr/sbin/hdik", &facts, NULL, detach_args, NULL));
56 posix_spawn_file_actions_destroy(&facts);
58 waitpid(pid, &status, 0);
62 if (WIFEXITED(status) && !WEXITSTATUS(status)
63 && stat(di->disk, &sb) == -1 && errno == ENOENT) {
66 // We are the last user of di, so free it.
67 free(di->mount_point);
76 void *zalloc(__unused void *opaque, uInt items, uInt size)
78 return malloc(items * size);
81 void zfree(__unused void *opaque, void *ptr)
86 disk_image_t *disk_image_create(const char *path, disk_image_opts_t *opts)
90 di = calloc(1, sizeof(disk_image_t));
95 if ((uid_old = geteuid()) != 0) {
96 assert_no_err(seteuid(0));
100 int fd = open(path, O_RDWR | O_TRUNC | O_CREAT, 0666);
109 size_t buf_size = 1024 * 1024;
110 void *out_buf = malloc(buf_size);
114 zs.avail_in = sizeof(data);
119 zs.next_out = out_buf;
120 zs.avail_out = buf_size;
122 ret = inflate(&zs, 0);
124 size_t todo = buf_size - zs.avail_out;
126 assert(write(fd, out_buf, todo) == (ssize_t)todo);
127 } while (ret == Z_OK);
129 assert(ret == Z_STREAM_END);
131 di->path = strdup(path);
135 char *attach_args[4] = { "hdik", "-nomount", (char *)di->path, NULL };
138 assert_no_err(pipe(fds));
140 posix_spawn_file_actions_t actions;
141 posix_spawn_file_actions_init(&actions);
142 posix_spawn_file_actions_adddup2(&actions, fds[1], STDOUT_FILENO);
144 assert_no_err(posix_spawn(&pid, "/usr/sbin/hdik", &actions, NULL, attach_args, NULL));
146 posix_spawn_file_actions_destroy(&actions);
150 char *line, *slice = NULL;
152 FILE *fp = fdopen(fds[0], "r");
157 while (getline(&line, &lnsz, fp) != -1) {
158 char *first, *second;
160 first = strtok(line, " ");
163 second = strtok(NULL, " ");
166 if (strstr(second, "GUID"))
167 di->disk = strdup(first);
169 // The output of hdik gets truncated, so just search for the leading part of the UUID
170 else if (strstr(second, "48465300-0000-11AA"))
171 slice = strdup(first);
175 assert_with_errno(ignore_eintr(waitpid(pid, &status, 0), -1) == pid);
176 assert(WIFEXITED(status) && !WEXITSTATUS(status));
178 assert(di->disk && slice);
183 char *mkdir_args[4] = { "mkdir", "-p", (char *)opts->mount_point, NULL };
184 assert_no_err(posix_spawn(&pid, "/bin/mkdir", NULL, NULL, mkdir_args, NULL));
186 assert_with_errno(ignore_eintr(waitpid(pid, &status, 0), -1) == pid);
187 assert(WIFEXITED(status) && !WEXITSTATUS(status));
189 posix_spawn_file_actions_t facts;
190 posix_spawn_file_actions_init(&facts);
191 posix_spawn_file_actions_addopen(&facts, STDOUT_FILENO, "/dev/null", O_APPEND, 0);
192 posix_spawn_file_actions_addopen(&facts, STDERR_FILENO, "/dev/null", O_APPEND, 0);
194 char *mount_args[4] = { "mount", slice, (char *)opts->mount_point, NULL };
195 assert_no_err(posix_spawn(&pid, "/sbin/mount_hfs", &facts, NULL, mount_args, NULL));
197 posix_spawn_file_actions_destroy(&facts);
200 assert_with_errno(ignore_eintr(waitpid(pid, &status, 0), -1) == pid);
201 assert(WIFEXITED(status) && !WEXITSTATUS(status));
203 di->mount_point = strdup(opts->mount_point);
205 if (strcmp(path, SHARED_PATH)) { // Don't register a cleanup for the shared image
206 test_cleanup(^ bool {
207 return disk_image_cleanup(di);
211 assert_no_err(seteuid(uid_old));
216 disk_image_t *disk_image_get(void)
221 if (statfs(SHARED_MOUNT, &sfs) == 0) {
222 di = calloc(1, sizeof(*di));
223 di->mount_point = SHARED_MOUNT;
224 di->disk = strdup(sfs.f_mntfromname);
225 di->path = SHARED_PATH;
227 // Make sure the di struct is freed when tests are complete.
228 test_cleanup(^ bool {
234 disk_image_opts_t opts = {
235 .mount_point = SHARED_MOUNT
237 di = disk_image_create(SHARED_PATH, &opts);
238 // Per the contract of disk_image_create(),
239 // di will be freed when disk_image_cleanup() is called,
240 // so don't free it here.
246 #else // !TARGET_OS_EMBEDDED
248 bool disk_image_cleanup(disk_image_t *di)
251 = { "hdiutil", "detach", (char *)di->disk, "-force", NULL };
256 posix_spawn_file_actions_t facts;
257 posix_spawn_file_actions_init(&facts);
258 posix_spawn_file_actions_addopen(&facts, STDOUT_FILENO, "/dev/null", O_APPEND, 0);
259 posix_spawn_file_actions_addopen(&facts, STDERR_FILENO, "/dev/null", O_APPEND, 0);
261 assert_no_err(posix_spawn(&pid, "/usr/bin/hdiutil", &facts, NULL, detach_args, NULL));
263 posix_spawn_file_actions_destroy(&facts);
266 waitpid(pid, &status, 0);
270 if (WIFEXITED(status) && !WEXITSTATUS(status)
271 && stat(di->disk, &sb) == -1 && errno == ENOENT) {
272 if (unlink(di->path) && errno == EACCES && !seteuid(0))
276 // We are the last user of di, so free it.
277 free(di->mount_point);
286 disk_image_t *disk_image_create(const char *path, disk_image_opts_t *opts)
290 sprintf(sz, "%llu", opts->size);
292 if (opts->mount_point) {
293 assert(!systemx("/bin/mkdir", SYSTEMX_QUIET, "-p", opts->mount_point, NULL));
296 // Start with the basic args
297 char *args[64] = { "hdiutil", "create", (char *)path, "-size", sz, "-ov" };
299 if (opts && opts->partition_type) {
300 args[6] = "-partitionType";
301 args[7] = (char *)opts->partition_type;
304 posix_spawn_file_actions_t facts;
305 posix_spawn_file_actions_init(&facts);
306 posix_spawn_file_actions_addopen(&facts, STDOUT_FILENO, "/dev/null", O_APPEND, 0);
308 assert_no_err(posix_spawn(&pid, "/usr/bin/hdiutil", &facts, NULL,
311 posix_spawn_file_actions_destroy(&facts);
314 assert_with_errno(ignore_eintr(waitpid(pid, &status, 0), -1));
316 assert(WIFEXITED(status) && !WEXITSTATUS(status));
320 args[3] = "-nomount";
323 } else if (opts && opts->enable_owners) {
328 posix_spawn_file_actions_t facts;
329 posix_spawn_file_actions_init(&facts);
330 posix_spawn_file_actions_addopen(&facts, STDOUT_FILENO, "/dev/null", O_APPEND, 0);
332 assert_no_err(posix_spawn(&pid, "/usr/bin/hdiutil", &facts, NULL,
335 posix_spawn_file_actions_destroy(&facts);
338 assert_with_errno(ignore_eintr(waitpid(pid, &status, 0), -1));
340 assert(WIFEXITED(status) && !WEXITSTATUS(status));
347 if (opts->mount_point) {
348 args[6] = "-mountpoint";
349 args[7] = (char *)opts->mount_point;
359 posix_spawn_file_actions_t facts;
360 posix_spawn_file_actions_init(&facts);
361 posix_spawn_file_actions_addopen(&facts, STDOUT_FILENO, "/dev/null", O_APPEND, 0);
363 assert_no_err(posix_spawn(&pid, "/usr/bin/hdiutil", &facts, NULL,
366 posix_spawn_file_actions_destroy(&facts);
369 assert_with_errno(ignore_eintr(waitpid(pid, &status, 0), -1));
371 assert(WIFEXITED(status) && !WEXITSTATUS(status));
376 if (opts->mount_point) {
377 args[4] = "-mountpoint";
378 args[5] = (char *)opts->mount_point;
386 assert_no_err(pipe(fds));
388 posix_spawn_file_actions_t actions;
389 posix_spawn_file_actions_init(&actions);
390 posix_spawn_file_actions_adddup2(&actions, fds[1], STDOUT_FILENO);
392 assert_no_err(posix_spawn(&pid, "/usr/bin/hdiutil", &actions, NULL, args, NULL));
394 posix_spawn_file_actions_destroy(&actions);
402 ssize_t res = read(fds[0], buffer + amt, 4096 - amt);
407 if (res == -1 && errno == EINTR)
410 assert_with_errno(res > 0);
417 disk_image_t *di = calloc(1, sizeof(*di));
419 di->path = strdup(path);
422 NSDictionary *results
423 = [NSPropertyListSerialization propertyListWithData:
424 [NSData dataWithBytesNoCopy:buffer
431 for (NSDictionary *entity in results[@"system-entities"]) {
432 if (opts && opts->partition_type) {
433 if (!strcmp([entity[@"unmapped-content-hint"] UTF8String],
434 opts->partition_type)
435 || !strcmp([entity[@"content-hint"] UTF8String],
436 opts->partition_type)) {
437 di->disk = strdup([entity[@"dev-entry"] fileSystemRepresentation]);
440 } else if ([entity[@"content-hint"] isEqualToString:@"Apple_HFS"]) {
441 di->mount_point = strdup([entity[@"mount-point"] fileSystemRepresentation]);
442 di->disk = strdup([entity[@"dev-entry"] fileSystemRepresentation]);
449 assert_with_errno(ignore_eintr(waitpid(pid, &status, 0), -1) == pid);
450 assert(WIFEXITED(status) && !WEXITSTATUS(status));
454 if (strcmp(path, SHARED_PATH)) { // Don't register a cleanup for the shared image
455 test_cleanup(^ bool {
456 return disk_image_cleanup(di);
463 disk_image_t *disk_image_get(void)
468 if (statfs(SHARED_MOUNT, &sfs) == 0) {
469 di = calloc(1, sizeof(*di));
471 di->mount_point = SHARED_MOUNT;
472 di->disk = strdup(sfs.f_mntfromname);
473 di->path = SHARED_PATH;
475 // Make sure the di struct is freed when tests are complete.
476 test_cleanup(^ bool {
482 disk_image_opts_t opts = {
484 .mount_point = SHARED_MOUNT
486 di = disk_image_create(SHARED_PATH, &opts);
487 // Per the contract of disk_image_create(),
488 // di will be freed when disk_image_cleanup() is called,
489 // so don't free it here.
495 #endif // TARGET_OS_EMBEDDED