1 /*
2 * Copyright (c) 2024 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 /* compile: xcrun -sdk macosx.internal clang -ldarwintest -o getattrlist_mountextflags getattrlist_mountextflags.c -g -Weverything */
30
31 #include <stdlib.h>
32 #include <fcntl.h>
33 #include <sys/mount.h>
34 #include <sys/stat.h>
35 #include <TargetConditionals.h>
36
37 #include <darwintest.h>
38 #include <darwintest/utils.h>
39
40 #if !TARGET_OS_OSX
41 #define FSTYPE_LIFS "lifs"
42 #endif /* !TARGET_OS_OSX */
43
44 #define FSTYPE_MSDOS "msdos"
45 #define FSTYPE_APFS "apfs"
46
47 /* rdar://137970358: Disable the test for now until the root cause was determined */
48 #if 0
49 #define RUN_TEST ((TARGET_OS_OSX || TARGET_OS_IOS) && !TARGET_OS_XR)
50 #else
51 #define RUN_TEST 0
52 #endif
53
54 static char template[MAXPATHLEN];
55 static char *testdir = NULL;
56 static char *output_buffer = NULL;
57 static char image_path[PATH_MAX];
58 static char mount_path[PATH_MAX];
59 static char disk[PATH_MAX];
60
61 T_GLOBAL_META(
62 T_META_NAMESPACE("xnu.vfs"),
63 T_META_RADAR_COMPONENT_NAME("xnu"),
64 T_META_RADAR_COMPONENT_VERSION("vfs"),
65 T_META_ENABLED(RUN_TEST),
66 T_META_ASROOT(false),
67 T_META_CHECK_LEAKS(false));
68
69 static void
get_mount_path(const char * diskid,char * path)70 get_mount_path(const char *diskid, char *path)
71 {
72 int i, mntsize;
73 struct statfs *mntbuf;
74 char diskurl[NAME_MAX];
75
76 if ((mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0) {
77 T_FAIL("getmntinfo failure");
78 return;
79 }
80
81 snprintf(diskurl, sizeof(diskurl), "/%s", diskid);
82
83 for (i = 0; i < mntsize; i++) {
84 #if TARGET_OS_OSX
85 /* check if this mount is one we want */
86 if (strcmp(mntbuf[i].f_fstypename, FSTYPE_MSDOS)) {
87 continue;
88 }
89 #else
90 /* check if this mount is one we want */
91 if (strcmp(mntbuf[i].f_fstypename, FSTYPE_LIFS)) {
92 continue;
93 }
94
95 /* validate fstype */
96 if (strncmp(mntbuf[i].f_mntfromname, FSTYPE_MSDOS, strlen(FSTYPE_MSDOS))) {
97 continue;
98 }
99 #endif /* TARGET_OS_OSX */
100
101 /* validate disk */
102 if (strstr(mntbuf[i].f_mntfromname, diskurl) == NULL) {
103 continue;
104 }
105
106 strlcpy(path, mntbuf[i].f_mntonname, PATH_MAX);
107 return;
108 }
109
110 T_FAIL("Cannot find mount path");
111 }
112
113 /*
114 * run an external program
115 */
116 static int
do_exec(const char * cmd)117 do_exec(const char *cmd)
118 {
119 FILE *fp;
120 char *pos;
121 char *buffer = output_buffer;
122 int output_len = PATH_MAX;
123
124 /* Open the command for reading. */
125 fp = popen(cmd, "r");
126 if (fp == NULL) {
127 T_FAIL("Failed to run command");
128 return -1;
129 }
130
131 /* Read the output a line at a time - output it. */
132 while (fgets(buffer, output_len, fp) != NULL) {
133 size_t bytes = strlen(buffer);
134
135 buffer += bytes;
136 output_len -= bytes;
137 }
138
139 /* replace last '\n' with '\0' */
140 pos = strrchr(output_buffer, '\n');
141 *pos = '\0';
142
143 /* close */
144 pclose(fp);
145 return 0;
146 }
147
148 static void
cleanup(void)149 cleanup(void)
150 {
151 char args[PATH_MAX];
152
153 if (mount_path[0] != '\0') {
154 unmount(mount_path, MNT_FORCE);
155 }
156 if (disk[0] != '\0') {
157 snprintf(args, sizeof(args), "diskutil eject %s", disk);
158 do_exec(args);
159 }
160 if (image_path[0] != '\0') {
161 unlink(image_path);
162 }
163 if (testdir) {
164 rmdir(testdir);
165 }
166 if (output_buffer) {
167 free(output_buffer);
168 }
169 }
170
171 static void
test_getattrlist(const char * path,const char * fstypename,uint32_t mount_extflags)172 test_getattrlist(const char *path, const char *fstypename, uint32_t mount_extflags)
173 {
174 struct myattrbuf {
175 uint32_t length;
176 attribute_set_t returned_attrs;
177 uint32_t mount_extflags;
178 attrreference_t fstypename_ref;
179 char fstypename[MFSTYPENAMELEN];
180 } attrbuf;
181
182 struct attrlist attrs = {
183 .bitmapcount = ATTR_BIT_MAP_COUNT,
184 .commonattr = ATTR_CMN_RETURNED_ATTRS,
185 .volattr = ATTR_VOL_MOUNTEXTFLAGS | ATTR_VOL_FSTYPENAME,
186 };
187
188 T_LOG("Testing %s", path);
189
190 T_ASSERT_POSIX_SUCCESS(getattrlist(path, &attrs, &attrbuf,
191 sizeof(attrbuf), FSOPT_REPORT_FULLSIZE | FSOPT_PACK_INVAL_ATTRS),
192 "Calling getattrlist");
193
194 T_ASSERT_TRUE(attrbuf.length <= sizeof(attrbuf),
195 "Asserting attrbuf.length <= sizeof(attrbuf)");
196
197 /* Verifing ATTR_VOL_FSTYPENAME and ATTR_VOL_MOUNTEXTFLAGS enabled */
198 T_ASSERT_BITS_SET(attrbuf.returned_attrs.volattr, ATTR_VOL_FSTYPENAME | ATTR_VOL_MOUNTEXTFLAGS,
199 "Asserting ATTR_VOL_FSTYPENAME and ATTR_VOL_MOUNTEXTFLAGS was returned");
200
201 /* Verifing ATTR_VOL_FSTYPENAME content */
202 T_ASSERT_EQ(strncmp(attrbuf.fstypename, fstypename, strlen(fstypename)), 0,
203 "Asserting that fstypename matches");
204
205 /* Verifing ATTR_VOL_MOUNTEXTFLAGS content */
206 T_ASSERT_EQ(attrbuf.mount_extflags, mount_extflags,
207 "Asserting that mount_extflags matches");
208 }
209
210 T_DECL(getattrlist_mountextflags,
211 "test ATTR_VOL_MOUNTEXTFLAGS")
212 {
213 #if (!RUN_TEST)
214 T_SKIP("Test disabled for this platform");
215 #endif
216
217 char *diskp = NULL;
218 char args[PATH_MAX];
219
220 image_path[0] = mount_path[0] = disk[0] = '\0';
221
222 T_ATEND(cleanup);
223 T_SETUPBEGIN;
224
225 /* Allocate output buffer */
226 output_buffer = malloc(PATH_MAX);
227
228 /* Create test directory */
229 snprintf(template, sizeof(template), "%s/getattrlist_mountextflags-XXXXXX", dt_tmpdir());
230 T_ASSERT_POSIX_NOTNULL((testdir = mkdtemp(template)), "Creating test root dir");
231
232 /* Create image path */
233 snprintf(image_path, sizeof(image_path), "%s/msdos.dmg", testdir);
234
235 /* Create disk image */
236 snprintf(args, sizeof(args), "diskimagetool create -fs none -s 1m %s", image_path);
237 T_ASSERT_POSIX_SUCCESS(do_exec(args), "Creating disk image %s", image_path);
238
239 /* Attach disk image */
240 snprintf(args, sizeof(args), "diskimagetool attach --external %s", image_path);
241 T_ASSERT_POSIX_SUCCESS(do_exec(args), "Attaching disk image %s", image_path);
242
243 /* Extract device identifier */
244 T_ASSERT_POSIX_NOTNULL((diskp = strstr(output_buffer, "disk")), "Extracting device identifier: %s", diskp);
245 strlcpy(disk, diskp, PATH_MAX);
246
247 /* Execute newfs_msdos disk image */
248 snprintf(args, sizeof(args), "newfs_msdos -v MSDOS %s", disk);
249 T_ASSERT_POSIX_SUCCESS(do_exec(args), "Executing newfs_msdos on disk %s", disk);
250
251 /* Mount disk image */
252 snprintf(args, sizeof(args), "datest --mount --device %s", disk);
253 T_ASSERT_POSIX_SUCCESS(do_exec(args), "Mounting disk image");
254
255 /* Get the mount path */
256 get_mount_path(disk, mount_path);
257 T_ASSERT_NE_STR(mount_path, "", "Got msdos filesystem mount path %s", mount_path);
258
259 T_SETUPEND;
260
261 /* Testing existing directory */
262 test_getattrlist("/", FSTYPE_APFS, 0);
263
264 #if TARGET_OS_OSX
265 /* Testing Data volume directory */
266 test_getattrlist("/private/var/tmp/", FSTYPE_APFS, MNT_EXT_ROOT_DATA_VOL);
267 #endif /* TARGET_OS_OSX */
268
269 /* Testing msdos volume directory */
270 #if TARGET_OS_OSX
271 test_getattrlist(mount_path, FSTYPE_MSDOS, MNT_EXT_FSKIT);
272 #else
273 test_getattrlist(mount_path, FSTYPE_LIFS, MNT_EXT_FSKIT);
274 #endif /* TARGET_OS_OSX */
275 }
276