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)
35 assert(seteuid(0) == 0);
38 = { "umount", "-f", (char *)di->mount_point, NULL };
40 assert_no_err(posix_spawn(&pid, "/sbin/umount", NULL, NULL, umount_args, NULL));
43 waitpid(pid, &status, 0);
46 = { "hdik", "-e", (char *)di->disk, NULL };
48 posix_spawn_file_actions_t facts;
49 posix_spawn_file_actions_init(&facts);
50 posix_spawn_file_actions_addopen(&facts, STDOUT_FILENO, "/dev/null", O_APPEND, 0);
51 posix_spawn_file_actions_addopen(&facts, STDERR_FILENO, "/dev/null", O_APPEND, 0);
53 assert_no_err(posix_spawn(&pid, "/usr/sbin/hdik", &facts, NULL, detach_args, NULL));
55 posix_spawn_file_actions_destroy(&facts);
57 waitpid(pid, &status, 0);
61 if (WIFEXITED(status) && !WEXITSTATUS(status)
62 && stat(di->disk, &sb) == -1 && errno == ENOENT) {
70 void *zalloc(__unused void *opaque, uInt items, uInt size)
72 return malloc(items * size);
75 void zfree(__unused void *opaque, void *ptr)
80 disk_image_t *disk_image_create(const char *path, disk_image_opts_t *opts)
84 di = calloc(1, sizeof(disk_image_t));
89 if ((uid_old = geteuid()) != 0) {
90 assert_no_err(seteuid(0));
94 int fd = open(path, O_RDWR | O_TRUNC | O_CREAT, 0666);
103 size_t buf_size = 1024 * 1024;
104 void *out_buf = malloc(buf_size);
108 zs.avail_in = sizeof(data);
113 zs.next_out = out_buf;
114 zs.avail_out = buf_size;
116 ret = inflate(&zs, 0);
118 size_t todo = buf_size - zs.avail_out;
120 assert(write(fd, out_buf, todo) == (ssize_t)todo);
121 } while (ret == Z_OK);
123 assert(ret == Z_STREAM_END);
125 di->path = strdup(path);
129 char *attach_args[4] = { "hdik", "-nomount", (char *)di->path, NULL };
132 assert_no_err(pipe(fds));
134 posix_spawn_file_actions_t actions;
135 posix_spawn_file_actions_init(&actions);
136 posix_spawn_file_actions_adddup2(&actions, fds[1], STDOUT_FILENO);
138 assert_no_err(posix_spawn(&pid, "/usr/sbin/hdik", &actions, NULL, attach_args, NULL));
140 posix_spawn_file_actions_destroy(&actions);
144 char *line, *slice = NULL;
146 FILE *fp = fdopen(fds[0], "r");
151 while (getline(&line, &lnsz, fp) != -1) {
152 char *first, *second;
154 first = strtok(line, " ");
157 second = strtok(NULL, " ");
160 if (strstr(second, "GUID"))
161 di->disk = strdup(first);
163 // The output of hdik gets truncated, so just search for the leading part of the UUID
164 else if (strstr(second, "48465300-0000-11AA"))
165 slice = strdup(first);
169 assert_with_errno(ignore_eintr(waitpid(pid, &status, 0), -1) == pid);
170 assert(WIFEXITED(status) && !WEXITSTATUS(status));
172 assert(di->disk && slice);
177 char *mkdir_args[4] = { "mkdir", "-p", (char *)opts->mount_point, NULL };
178 assert_no_err(posix_spawn(&pid, "/bin/mkdir", NULL, NULL, mkdir_args, NULL));
180 assert_with_errno(ignore_eintr(waitpid(pid, &status, 0), -1) == pid);
181 assert(WIFEXITED(status) && !WEXITSTATUS(status));
183 posix_spawn_file_actions_t facts;
184 posix_spawn_file_actions_init(&facts);
185 posix_spawn_file_actions_addopen(&facts, STDOUT_FILENO, "/dev/null", O_APPEND, 0);
186 posix_spawn_file_actions_addopen(&facts, STDERR_FILENO, "/dev/null", O_APPEND, 0);
188 char *mount_args[4] = { "mount", slice, (char *)opts->mount_point, NULL };
189 assert_no_err(posix_spawn(&pid, "/sbin/mount_hfs", &facts, NULL, mount_args, NULL));
191 posix_spawn_file_actions_destroy(&facts);
194 assert_with_errno(ignore_eintr(waitpid(pid, &status, 0), -1) == pid);
195 assert(WIFEXITED(status) && !WEXITSTATUS(status));
197 di->mount_point = strdup(opts->mount_point);
199 char *chown_args[5] = { "chown", "-R", "mobile:mobile", (char *)di->mount_point, NULL };
200 assert_no_err(posix_spawn(&pid, "/usr/sbin/chown", NULL, NULL, chown_args, NULL));
202 assert_with_errno(ignore_eintr(waitpid(pid, &status, 0), -1) == pid);
203 assert(WIFEXITED(status) && !WEXITSTATUS(status));
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 disk_image_opts_t opts = {
228 .mount_point = SHARED_MOUNT
230 di = disk_image_create(SHARED_PATH, &opts);
236 #else // !TARGET_OS_EMBEDDED
238 bool disk_image_cleanup(disk_image_t *di)
241 = { "hdiutil", "detach", (char *)di->disk, "-force", NULL };
245 posix_spawn_file_actions_t facts;
246 posix_spawn_file_actions_init(&facts);
247 posix_spawn_file_actions_addopen(&facts, STDOUT_FILENO, "/dev/null", O_APPEND, 0);
248 posix_spawn_file_actions_addopen(&facts, STDERR_FILENO, "/dev/null", O_APPEND, 0);
250 assert_no_err(posix_spawn(&pid, "/usr/bin/hdiutil", &facts, NULL, detach_args, NULL));
252 posix_spawn_file_actions_destroy(&facts);
255 waitpid(pid, &status, 0);
259 if (WIFEXITED(status) && !WEXITSTATUS(status)
260 && stat(di->disk, &sb) == -1 && errno == ENOENT) {
261 if (unlink(di->path) && errno == EACCES && !seteuid(0))
269 disk_image_t *disk_image_create(const char *path, disk_image_opts_t *opts)
273 sprintf(sz, "%llu", opts->size);
275 if (opts->mount_point) {
276 assert(!systemx("/bin/mkdir", SYSTEMX_QUIET, "-p", opts->mount_point, NULL));
279 // Start with the basic args
280 char *args[64] = { "hdiutil", "create", (char *)path, "-size", sz, "-ov" };
282 if (opts && opts->partition_type) {
283 args[6] = "-partitionType";
284 args[7] = (char *)opts->partition_type;
287 posix_spawn_file_actions_t facts;
288 posix_spawn_file_actions_init(&facts);
289 posix_spawn_file_actions_addopen(&facts, STDOUT_FILENO, "/dev/null", O_APPEND, 0);
291 assert_no_err(posix_spawn(&pid, "/usr/bin/hdiutil", &facts, NULL,
294 posix_spawn_file_actions_destroy(&facts);
297 assert_with_errno(ignore_eintr(waitpid(pid, &status, 0), -1));
299 assert(WIFEXITED(status) && !WEXITSTATUS(status));
303 args[3] = "-nomount";
306 } else if (opts && opts->enable_owners) {
311 posix_spawn_file_actions_t facts;
312 posix_spawn_file_actions_init(&facts);
313 posix_spawn_file_actions_addopen(&facts, STDOUT_FILENO, "/dev/null", O_APPEND, 0);
315 assert_no_err(posix_spawn(&pid, "/usr/bin/hdiutil", &facts, NULL,
318 posix_spawn_file_actions_destroy(&facts);
321 assert_with_errno(ignore_eintr(waitpid(pid, &status, 0), -1));
323 assert(WIFEXITED(status) && !WEXITSTATUS(status));
330 if (opts->mount_point) {
331 args[6] = "-mountpoint";
332 args[7] = (char *)opts->mount_point;
342 posix_spawn_file_actions_t facts;
343 posix_spawn_file_actions_init(&facts);
344 posix_spawn_file_actions_addopen(&facts, STDOUT_FILENO, "/dev/null", O_APPEND, 0);
346 assert_no_err(posix_spawn(&pid, "/usr/bin/hdiutil", &facts, NULL,
349 posix_spawn_file_actions_destroy(&facts);
352 assert_with_errno(ignore_eintr(waitpid(pid, &status, 0), -1));
354 assert(WIFEXITED(status) && !WEXITSTATUS(status));
359 if (opts->mount_point) {
360 args[4] = "-mountpoint";
361 args[5] = (char *)opts->mount_point;
369 assert_no_err(pipe(fds));
371 posix_spawn_file_actions_t actions;
372 posix_spawn_file_actions_init(&actions);
373 posix_spawn_file_actions_adddup2(&actions, fds[1], STDOUT_FILENO);
375 assert_no_err(posix_spawn(&pid, "/usr/bin/hdiutil", &actions, NULL, args, NULL));
377 posix_spawn_file_actions_destroy(&actions);
385 ssize_t res = read(fds[0], buffer + amt, 4096 - amt);
390 if (res == -1 && errno == EINTR)
393 assert_with_errno(res > 0);
400 disk_image_t *di = calloc(1, sizeof(*di));
402 di->path = strdup(path);
405 NSDictionary *results
406 = [NSPropertyListSerialization propertyListWithData:
407 [NSData dataWithBytesNoCopy:buffer
414 for (NSDictionary *entity in results[@"system-entities"]) {
415 if (opts && opts->partition_type) {
416 if (!strcmp([entity[@"unmapped-content-hint"] UTF8String],
417 opts->partition_type)
418 || !strcmp([entity[@"content-hint"] UTF8String],
419 opts->partition_type)) {
420 di->disk = strdup([entity[@"dev-entry"] fileSystemRepresentation]);
423 } else if ([entity[@"content-hint"] isEqualToString:@"Apple_HFS"]) {
424 di->mount_point = strdup([entity[@"mount-point"] fileSystemRepresentation]);
425 di->disk = strdup([entity[@"dev-entry"] fileSystemRepresentation]);
432 assert_with_errno(ignore_eintr(waitpid(pid, &status, 0), -1) == pid);
433 assert(WIFEXITED(status) && !WEXITSTATUS(status));
437 if (strcmp(path, SHARED_PATH)) { // Don't register a cleanup for the shared image
438 test_cleanup(^ bool {
439 return disk_image_cleanup(di);
446 disk_image_t *disk_image_get(void)
451 if (statfs(SHARED_MOUNT, &sfs) == 0) {
452 di = calloc(1, sizeof(*di));
454 di->mount_point = SHARED_MOUNT;
455 di->disk = strdup(sfs.f_mntfromname);
456 di->path = SHARED_PATH;
458 disk_image_opts_t opts = {
460 .mount_point = SHARED_MOUNT
462 di = disk_image_create(SHARED_PATH, &opts);
468 #endif // TARGET_OS_EMBEDDED