xref: /xnu-12377.81.4/tests/disk_mount_conditioner.m (revision 043036a2b3718f7f0be807e2870f8f47d3fa0796)
1*043036a2SApple OSS Distributions#ifdef T_NAMESPACE
2*043036a2SApple OSS Distributions#undef T_NAMESPACE
3*043036a2SApple OSS Distributions#endif
4*043036a2SApple OSS Distributions#include <darwintest.h>
5*043036a2SApple OSS Distributions#include <darwintest_utils.h>
6*043036a2SApple OSS Distributions
7*043036a2SApple OSS Distributions#include <stdlib.h>
8*043036a2SApple OSS Distributions#include <unistd.h>
9*043036a2SApple OSS Distributions#include <fcntl.h>
10*043036a2SApple OSS Distributions#include <System/sys/fsctl.h>
11*043036a2SApple OSS Distributions#include <paths.h>
12*043036a2SApple OSS Distributions#import <Foundation/Foundation.h>
13*043036a2SApple OSS Distributions
14*043036a2SApple OSS Distributionsstatic char *mktempdir(void);
15*043036a2SApple OSS Distributionsstatic char *mktempmount(void);
16*043036a2SApple OSS Distributions
17*043036a2SApple OSS Distributions#ifndef TEST_UNENTITLED
18*043036a2SApple OSS Distributionsstatic int system_legal(const char *command);
19*043036a2SApple OSS Distributionsstatic char *mkramdisk(void);
20*043036a2SApple OSS Distributionsstatic uint64_t time_for_read(int fd, const char *expected);
21*043036a2SApple OSS Distributionsstatic void perf_setup(char **path, int *fd);
22*043036a2SApple OSS Distributions
23*043036a2SApple OSS Distributions#define READSIZE 1024L
24*043036a2SApple OSS Distributions#endif /* !TEST_UNENTITLED */
25*043036a2SApple OSS Distributions
26*043036a2SApple OSS DistributionsT_GLOBAL_META(
27*043036a2SApple OSS Distributions	T_META_NAMESPACE("xnu.vfs.dmc"),
28*043036a2SApple OSS Distributions	T_META_ASROOT(true),
29*043036a2SApple OSS Distributions	T_META_RUN_CONCURRENTLY(true)
30*043036a2SApple OSS Distributions	);
31*043036a2SApple OSS Distributions
32*043036a2SApple OSS Distributions#pragma mark Entitled Tests
33*043036a2SApple OSS Distributions
34*043036a2SApple OSS Distributions#ifndef TEST_UNENTITLED
35*043036a2SApple OSS DistributionsT_DECL(fsctl_get_uninitialized,
36*043036a2SApple OSS Distributions    "Initial fsctl.get should return zeros",
37*043036a2SApple OSS Distributions    T_META_ASROOT(false), T_META_TAG_VM_PREFERRED)
38*043036a2SApple OSS Distributions{
39*043036a2SApple OSS Distributions	int err;
40*043036a2SApple OSS Distributions	char *mount_path;
41*043036a2SApple OSS Distributions	disk_conditioner_info info = {0};
42*043036a2SApple OSS Distributions	disk_conditioner_info expected_info = {0};
43*043036a2SApple OSS Distributions
44*043036a2SApple OSS Distributions	T_SETUPBEGIN;
45*043036a2SApple OSS Distributions	mount_path = mktempmount();
46*043036a2SApple OSS Distributions	T_SETUPEND;
47*043036a2SApple OSS Distributions
48*043036a2SApple OSS Distributions	info.enabled = true;
49*043036a2SApple OSS Distributions	info.is_ssd = true;
50*043036a2SApple OSS Distributions	err = fsctl(mount_path, DISK_CONDITIONER_IOC_GET, &info, 0);
51*043036a2SApple OSS Distributions	T_WITH_ERRNO;
52*043036a2SApple OSS Distributions	T_ASSERT_EQ_INT(0, err, "fsctl(DISK_CONDITIONER_IOC_GET)");
53*043036a2SApple OSS Distributions
54*043036a2SApple OSS Distributions	err = memcmp(&info, &expected_info, sizeof(info));
55*043036a2SApple OSS Distributions	T_ASSERT_EQ_INT(0, err, "initial DMC info is zeroed");
56*043036a2SApple OSS Distributions}
57*043036a2SApple OSS Distributions
58*043036a2SApple OSS DistributionsT_DECL(fsctl_set,
59*043036a2SApple OSS Distributions    "fsctl.set should succeed and fsctl.get should verify", T_META_TAG_VM_PREFERRED)
60*043036a2SApple OSS Distributions{
61*043036a2SApple OSS Distributions	int err;
62*043036a2SApple OSS Distributions	char *mount_path;
63*043036a2SApple OSS Distributions	disk_conditioner_info info = {0};
64*043036a2SApple OSS Distributions	disk_conditioner_info expected_info = {0};
65*043036a2SApple OSS Distributions
66*043036a2SApple OSS Distributions	T_SETUPBEGIN;
67*043036a2SApple OSS Distributions	mount_path = mktempmount();
68*043036a2SApple OSS Distributions	T_SETUPEND;
69*043036a2SApple OSS Distributions
70*043036a2SApple OSS Distributions	info.enabled = 1;
71*043036a2SApple OSS Distributions	info.access_time_usec = 10;
72*043036a2SApple OSS Distributions	info.read_throughput_mbps = 40;
73*043036a2SApple OSS Distributions	info.write_throughput_mbps = 40;
74*043036a2SApple OSS Distributions	info.is_ssd = 0;
75*043036a2SApple OSS Distributions	info.ioqueue_depth = 8;
76*043036a2SApple OSS Distributions	info.maxreadcnt = 8;
77*043036a2SApple OSS Distributions	info.maxwritecnt = 8;
78*043036a2SApple OSS Distributions	info.segreadcnt = 8;
79*043036a2SApple OSS Distributions	info.segwritecnt = 8;
80*043036a2SApple OSS Distributions	expected_info = info;
81*043036a2SApple OSS Distributions
82*043036a2SApple OSS Distributions	err = fsctl(mount_path, DISK_CONDITIONER_IOC_SET, &info, 0);
83*043036a2SApple OSS Distributions	T_WITH_ERRNO;
84*043036a2SApple OSS Distributions	T_ASSERT_EQ_INT(0, err, "fsctl(DISK_CONDITIONER_IOC_SET)");
85*043036a2SApple OSS Distributions
86*043036a2SApple OSS Distributions	err = fsctl(mount_path, DISK_CONDITIONER_IOC_GET, &info, 0);
87*043036a2SApple OSS Distributions	T_WITH_ERRNO;
88*043036a2SApple OSS Distributions	T_ASSERT_EQ_INT(0, err, "fsctl(DISK_CONDITIONER_IOC_GET) after SET");
89*043036a2SApple OSS Distributions
90*043036a2SApple OSS Distributions	err = memcmp(&info, &expected_info, sizeof(info));
91*043036a2SApple OSS Distributions	T_ASSERT_EQ_INT(0, err, "fsctl.get is the info configured by fsctl.set");
92*043036a2SApple OSS Distributions}
93*043036a2SApple OSS Distributions
94*043036a2SApple OSS Distributionsstatic void
95*043036a2SApple OSS Distributionsverify_mount_fallback_values(const char *mount_path, disk_conditioner_info *info)
96*043036a2SApple OSS Distributions{
97*043036a2SApple OSS Distributions	int err;
98*043036a2SApple OSS Distributions	disk_conditioner_info newinfo = {0};
99*043036a2SApple OSS Distributions
100*043036a2SApple OSS Distributions	err = fsctl(mount_path, DISK_CONDITIONER_IOC_SET, info, 0);
101*043036a2SApple OSS Distributions	T_WITH_ERRNO;
102*043036a2SApple OSS Distributions	T_ASSERT_EQ_INT(0, err, "fsctl(DISK_CONDITIONER_IOC_SET)");
103*043036a2SApple OSS Distributions
104*043036a2SApple OSS Distributions	err = fsctl(mount_path, DISK_CONDITIONER_IOC_GET, &newinfo, 0);
105*043036a2SApple OSS Distributions	T_WITH_ERRNO;
106*043036a2SApple OSS Distributions	T_ASSERT_EQ_INT(0, err, "fsctl(DISK_CONDITIONER_IOC_GET) after SET");
107*043036a2SApple OSS Distributions
108*043036a2SApple OSS Distributions	// without querying the drive for the expected values, the best we can do is
109*043036a2SApple OSS Distributions	// assert that they are not zero (impossible) or less than UINT32_MAX (unlikely)
110*043036a2SApple OSS Distributions	T_ASSERT_GT(newinfo.ioqueue_depth, 0u, "ioqueue_depth is the value from the mount");
111*043036a2SApple OSS Distributions	T_ASSERT_GT(newinfo.maxreadcnt, 0u, "maxreadcnt is value from the mount");
112*043036a2SApple OSS Distributions	T_ASSERT_GT(newinfo.maxwritecnt, 0u, "maxwritecnt is value from the mount");
113*043036a2SApple OSS Distributions	T_ASSERT_GT(newinfo.segreadcnt, 0u, "segreadcnt is value from the mount");
114*043036a2SApple OSS Distributions	T_ASSERT_GT(newinfo.segwritecnt, 0u, "segwritecnt is value from the mount");
115*043036a2SApple OSS Distributions	T_ASSERT_LT(newinfo.ioqueue_depth, UINT32_MAX, "ioqueue_depth is the value from the mount");
116*043036a2SApple OSS Distributions	T_ASSERT_LT(newinfo.maxreadcnt, UINT32_MAX, "maxreadcnt is value from the mount");
117*043036a2SApple OSS Distributions	T_ASSERT_LT(newinfo.maxwritecnt, UINT32_MAX, "maxwritecnt is value from the mount");
118*043036a2SApple OSS Distributions	T_ASSERT_LT(newinfo.segreadcnt, UINT32_MAX, "segreadcnt is value from the mount");
119*043036a2SApple OSS Distributions	T_ASSERT_LT(newinfo.segwritecnt, UINT32_MAX, "segwritecnt is value from the mount");
120*043036a2SApple OSS Distributions}
121*043036a2SApple OSS Distributions
122*043036a2SApple OSS DistributionsT_DECL(fsctl_set_zero,
123*043036a2SApple OSS Distributions    "fsctl.set zero values should fall back to original mount settings", T_META_TAG_VM_PREFERRED)
124*043036a2SApple OSS Distributions{
125*043036a2SApple OSS Distributions	char *mount_path;
126*043036a2SApple OSS Distributions	disk_conditioner_info info = {0};
127*043036a2SApple OSS Distributions
128*043036a2SApple OSS Distributions	T_SETUPBEGIN;
129*043036a2SApple OSS Distributions	mount_path = mktempmount();
130*043036a2SApple OSS Distributions
131*043036a2SApple OSS Distributions	info.enabled = 1;
132*043036a2SApple OSS Distributions	/* everything else is 0 */
133*043036a2SApple OSS Distributions
134*043036a2SApple OSS Distributions	T_SETUPEND;
135*043036a2SApple OSS Distributions
136*043036a2SApple OSS Distributions	verify_mount_fallback_values(mount_path, &info);
137*043036a2SApple OSS Distributions}
138*043036a2SApple OSS Distributions
139*043036a2SApple OSS DistributionsT_DECL(fsctl_set_out_of_bounds,
140*043036a2SApple OSS Distributions    "fsctl.set out-of-bounds values should fall back to original mount settings", T_META_TAG_VM_PREFERRED)
141*043036a2SApple OSS Distributions{
142*043036a2SApple OSS Distributions	char *mount_path;
143*043036a2SApple OSS Distributions	disk_conditioner_info info;
144*043036a2SApple OSS Distributions
145*043036a2SApple OSS Distributions	T_SETUPBEGIN;
146*043036a2SApple OSS Distributions	mount_path = mktempmount();
147*043036a2SApple OSS Distributions
148*043036a2SApple OSS Distributions	memset(&info, UINT32_MAX, sizeof(info));
149*043036a2SApple OSS Distributions	info.enabled = 1;
150*043036a2SApple OSS Distributions	info.access_time_usec = 0;
151*043036a2SApple OSS Distributions	info.read_throughput_mbps = 0;
152*043036a2SApple OSS Distributions	info.write_throughput_mbps = 0;
153*043036a2SApple OSS Distributions	/* everything else is UINT32_MAX */
154*043036a2SApple OSS Distributions
155*043036a2SApple OSS Distributions	T_SETUPEND;
156*043036a2SApple OSS Distributions
157*043036a2SApple OSS Distributions	verify_mount_fallback_values(mount_path, &info);
158*043036a2SApple OSS Distributions}
159*043036a2SApple OSS Distributions
160*043036a2SApple OSS DistributionsT_DECL(fsctl_restore_mount_fields,
161*043036a2SApple OSS Distributions    "fsctl.set should restore fields on mount_t that it temporarily overrides", T_META_TAG_VM_PREFERRED)
162*043036a2SApple OSS Distributions{
163*043036a2SApple OSS Distributions	int err;
164*043036a2SApple OSS Distributions	char *mount_path;
165*043036a2SApple OSS Distributions	disk_conditioner_info info;
166*043036a2SApple OSS Distributions	disk_conditioner_info mount_fields;
167*043036a2SApple OSS Distributions
168*043036a2SApple OSS Distributions	T_SETUPBEGIN;
169*043036a2SApple OSS Distributions	mount_path = mktempmount();
170*043036a2SApple OSS Distributions	T_SETUPEND;
171*043036a2SApple OSS Distributions
172*043036a2SApple OSS Distributions	/* first set out-of-bounds values to retrieve the original mount_t fields */
173*043036a2SApple OSS Distributions	memset(&info, UINT32_MAX, sizeof(info));
174*043036a2SApple OSS Distributions	info.enabled = 1;
175*043036a2SApple OSS Distributions	info.access_time_usec = 0;
176*043036a2SApple OSS Distributions	info.read_throughput_mbps = 0;
177*043036a2SApple OSS Distributions	info.write_throughput_mbps = 0;
178*043036a2SApple OSS Distributions	/* everything else is UINT32_MAX */
179*043036a2SApple OSS Distributions	err = fsctl(mount_path, DISK_CONDITIONER_IOC_SET, &info, 0);
180*043036a2SApple OSS Distributions	T_WITH_ERRNO;
181*043036a2SApple OSS Distributions	T_ASSERT_EQ_INT(0, err, "fsctl(DISK_CONDITIONER_IOC_SET)");
182*043036a2SApple OSS Distributions
183*043036a2SApple OSS Distributions	err = fsctl(mount_path, DISK_CONDITIONER_IOC_GET, &mount_fields, 0);
184*043036a2SApple OSS Distributions	T_WITH_ERRNO;
185*043036a2SApple OSS Distributions	T_ASSERT_EQ_INT(0, err, "fsctl(DISK_CONDITIONER_IOC_GET)");
186*043036a2SApple OSS Distributions
187*043036a2SApple OSS Distributions	/* now turn off the disk conditioner which should restore fields on the mount_t */
188*043036a2SApple OSS Distributions	memset(&info, 1, sizeof(info));
189*043036a2SApple OSS Distributions	info.enabled = 0;
190*043036a2SApple OSS Distributions	err = fsctl(mount_path, DISK_CONDITIONER_IOC_SET, &info, 0);
191*043036a2SApple OSS Distributions	T_WITH_ERRNO;
192*043036a2SApple OSS Distributions	T_ASSERT_EQ_INT(0, err, "fsctl(DISK_CONDITIONER_IOC_SET)");
193*043036a2SApple OSS Distributions
194*043036a2SApple OSS Distributions	/* and finally set out-of-bounds values again to retrieve the new mount_t fields which should not have changed */
195*043036a2SApple OSS Distributions	memset(&info, UINT32_MAX, sizeof(info));
196*043036a2SApple OSS Distributions	info.enabled = 0;
197*043036a2SApple OSS Distributions	info.access_time_usec = 0;
198*043036a2SApple OSS Distributions	info.read_throughput_mbps = 0;
199*043036a2SApple OSS Distributions	info.write_throughput_mbps = 0;
200*043036a2SApple OSS Distributions	/* everything else is UINT32_MAX */
201*043036a2SApple OSS Distributions	err = fsctl(mount_path, DISK_CONDITIONER_IOC_SET, &info, 0);
202*043036a2SApple OSS Distributions	T_WITH_ERRNO;
203*043036a2SApple OSS Distributions	T_ASSERT_EQ_INT(0, err, "fsctl(DISK_CONDITIONER_IOC_SET)");
204*043036a2SApple OSS Distributions
205*043036a2SApple OSS Distributions	err = fsctl(mount_path, DISK_CONDITIONER_IOC_GET, &info, 0);
206*043036a2SApple OSS Distributions	T_WITH_ERRNO;
207*043036a2SApple OSS Distributions	T_ASSERT_EQ_INT(0, err, "fsctl(DISK_CONDITIONER_IOC_GET)");
208*043036a2SApple OSS Distributions
209*043036a2SApple OSS Distributions	T_ASSERT_EQ(info.maxreadcnt, mount_fields.maxreadcnt, "mount_t maxreadcnt restored");
210*043036a2SApple OSS Distributions	T_ASSERT_EQ(info.maxwritecnt, mount_fields.maxwritecnt, "mount_t maxwritecnt restored");
211*043036a2SApple OSS Distributions	T_ASSERT_EQ(info.segreadcnt, mount_fields.segreadcnt, "mount_t segreadcnt restored");
212*043036a2SApple OSS Distributions	T_ASSERT_EQ(info.segwritecnt, mount_fields.segwritecnt, "mount_t segwritecnt restored");
213*043036a2SApple OSS Distributions	T_ASSERT_EQ(info.ioqueue_depth, mount_fields.ioqueue_depth, "mount_t ioqueue_depth restored");
214*043036a2SApple OSS Distributions}
215*043036a2SApple OSS Distributions
216*043036a2SApple OSS DistributionsT_DECL(fsctl_get_nonroot,
217*043036a2SApple OSS Distributions    "fsctl.get should not require root",
218*043036a2SApple OSS Distributions    T_META_ASROOT(false), T_META_TAG_VM_PREFERRED)
219*043036a2SApple OSS Distributions{
220*043036a2SApple OSS Distributions	int err;
221*043036a2SApple OSS Distributions	char *mount_path;
222*043036a2SApple OSS Distributions	disk_conditioner_info info;
223*043036a2SApple OSS Distributions
224*043036a2SApple OSS Distributions	T_SETUPBEGIN;
225*043036a2SApple OSS Distributions	// make sure we're not root
226*043036a2SApple OSS Distributions	if (0 == geteuid()) {
227*043036a2SApple OSS Distributions		seteuid(5000);
228*043036a2SApple OSS Distributions	}
229*043036a2SApple OSS Distributions
230*043036a2SApple OSS Distributions	mount_path = mktempmount();
231*043036a2SApple OSS Distributions	T_SETUPEND;
232*043036a2SApple OSS Distributions
233*043036a2SApple OSS Distributions	err = fsctl(mount_path, DISK_CONDITIONER_IOC_GET, &info, 0);
234*043036a2SApple OSS Distributions	T_WITH_ERRNO;
235*043036a2SApple OSS Distributions	T_ASSERT_EQ_INT(0, err, "fsctl.get without root");
236*043036a2SApple OSS Distributions}
237*043036a2SApple OSS Distributions
238*043036a2SApple OSS DistributionsT_DECL(fsctl_set_nonroot,
239*043036a2SApple OSS Distributions    "fsctl.set should require root",
240*043036a2SApple OSS Distributions    T_META_ASROOT(false), T_META_TAG_VM_PREFERRED)
241*043036a2SApple OSS Distributions{
242*043036a2SApple OSS Distributions	int err;
243*043036a2SApple OSS Distributions	char *mount_path;
244*043036a2SApple OSS Distributions	disk_conditioner_info info = {0};
245*043036a2SApple OSS Distributions	disk_conditioner_info expected_info = {0};
246*043036a2SApple OSS Distributions
247*043036a2SApple OSS Distributions	T_SETUPBEGIN;
248*043036a2SApple OSS Distributions	// make sure we're not root
249*043036a2SApple OSS Distributions	if (0 == geteuid()) {
250*043036a2SApple OSS Distributions		seteuid(5000);
251*043036a2SApple OSS Distributions	}
252*043036a2SApple OSS Distributions
253*043036a2SApple OSS Distributions	mount_path = mktempmount();
254*043036a2SApple OSS Distributions	T_SETUPEND;
255*043036a2SApple OSS Distributions
256*043036a2SApple OSS Distributions	// save original info
257*043036a2SApple OSS Distributions	err = fsctl(mount_path, DISK_CONDITIONER_IOC_GET, &expected_info, 0);
258*043036a2SApple OSS Distributions	T_WITH_ERRNO;
259*043036a2SApple OSS Distributions	T_ASSERT_EQ_INT(0, err, "Get original DMC info");
260*043036a2SApple OSS Distributions
261*043036a2SApple OSS Distributions	info.enabled = 1;
262*043036a2SApple OSS Distributions	info.access_time_usec = 10;
263*043036a2SApple OSS Distributions	err = fsctl(mount_path, DISK_CONDITIONER_IOC_SET, &info, 0);
264*043036a2SApple OSS Distributions	T_WITH_ERRNO;
265*043036a2SApple OSS Distributions	T_ASSERT_NE_INT(0, err, "fsctl.set returns error without root");
266*043036a2SApple OSS Distributions
267*043036a2SApple OSS Distributions	err = fsctl(mount_path, DISK_CONDITIONER_IOC_GET, &info, 0);
268*043036a2SApple OSS Distributions	T_WITH_ERRNO;
269*043036a2SApple OSS Distributions	T_ASSERT_EQ_INT(0, err, "fsctl.get after nonroot fsctl.set");
270*043036a2SApple OSS Distributions
271*043036a2SApple OSS Distributions	err = memcmp(&info, &expected_info, sizeof(info));
272*043036a2SApple OSS Distributions	T_ASSERT_EQ_INT(0, err, "fsctl.set should not change info without root");
273*043036a2SApple OSS Distributions}
274*043036a2SApple OSS Distributions
275*043036a2SApple OSS DistributionsT_DECL(fsctl_delays,
276*043036a2SApple OSS Distributions    "Validate I/O delays when DMC is enabled",
277*043036a2SApple OSS Distributions    T_META_ENABLED(!TARGET_OS_BRIDGE), // diskutil is unavailable on bridgeOS
278*043036a2SApple OSS Distributions    T_META_RUN_CONCURRENTLY(false), T_META_TAG_VM_PREFERRED)
279*043036a2SApple OSS Distributions{
280*043036a2SApple OSS Distributions	char *path;
281*043036a2SApple OSS Distributions	int fd;
282*043036a2SApple OSS Distributions	int err;
283*043036a2SApple OSS Distributions	uint64_t elapsed_nsec, expected_nsec;
284*043036a2SApple OSS Distributions	disk_conditioner_info info = {0};
285*043036a2SApple OSS Distributions	char buf[READSIZE];
286*043036a2SApple OSS Distributions
287*043036a2SApple OSS Distributions	T_SETUPBEGIN;
288*043036a2SApple OSS Distributions	perf_setup(&path, &fd);
289*043036a2SApple OSS Distributions	memset(buf, 0xFF, sizeof(buf));
290*043036a2SApple OSS Distributions	T_ASSERT_EQ_LONG((long)sizeof(buf), write(fd, buf, sizeof(buf)), "write random data to temp file");
291*043036a2SApple OSS Distributions	fcntl(fd, F_FULLFSYNC);
292*043036a2SApple OSS Distributions	T_SETUPEND;
293*043036a2SApple OSS Distributions
294*043036a2SApple OSS Distributions	expected_nsec = NSEC_PER_SEC / 2;
295*043036a2SApple OSS Distributions
296*043036a2SApple OSS Distributions	// measure delay before setting parameters (should be none)
297*043036a2SApple OSS Distributions	elapsed_nsec = time_for_read(fd, buf);
298*043036a2SApple OSS Distributions	T_ASSERT_LT_ULLONG(elapsed_nsec, expected_nsec, "DMC disabled read(%ld) is reasonably fast", READSIZE);
299*043036a2SApple OSS Distributions
300*043036a2SApple OSS Distributions	// measure delay after setting parameters
301*043036a2SApple OSS Distributions	info.enabled = 1;
302*043036a2SApple OSS Distributions	info.access_time_usec = expected_nsec / NSEC_PER_USEC;
303*043036a2SApple OSS Distributions	info.read_throughput_mbps = 40;
304*043036a2SApple OSS Distributions	info.write_throughput_mbps = 40;
305*043036a2SApple OSS Distributions	info.is_ssd = 1; // is_ssd will ensure we get constant access_time delays rather than scaled
306*043036a2SApple OSS Distributions	err = fsctl(path, DISK_CONDITIONER_IOC_SET, &info, 0);
307*043036a2SApple OSS Distributions	T_WITH_ERRNO;
308*043036a2SApple OSS Distributions	T_ASSERT_EQ_INT(0, err, "fsctl(DISK_CONDITIONER_IOC_SET) delay");
309*043036a2SApple OSS Distributions
310*043036a2SApple OSS Distributions	elapsed_nsec = time_for_read(fd, buf);
311*043036a2SApple OSS Distributions	T_ASSERT_GT_ULLONG(elapsed_nsec, expected_nsec, "DMC enabled read(%ld) is at least the expected delay", READSIZE);
312*043036a2SApple OSS Distributions	T_ASSERT_LT_ULLONG(elapsed_nsec, 2 * expected_nsec, "DMC enabled read(%ld) is no more than twice the expected delay", READSIZE);
313*043036a2SApple OSS Distributions
314*043036a2SApple OSS Distributions	// measure delay after resetting parameters (should be none)
315*043036a2SApple OSS Distributions	info.enabled = 0;
316*043036a2SApple OSS Distributions	err = fsctl(path, DISK_CONDITIONER_IOC_SET, &info, 0);
317*043036a2SApple OSS Distributions	T_WITH_ERRNO;
318*043036a2SApple OSS Distributions	T_ASSERT_EQ_INT(0, err, "fsctl(DISK_CONDITIONER_IOC_SET) reset delay");
319*043036a2SApple OSS Distributions
320*043036a2SApple OSS Distributions	usleep(USEC_PER_SEC / 2); // might still be other I/O inflight
321*043036a2SApple OSS Distributions	elapsed_nsec = time_for_read(fd, buf);
322*043036a2SApple OSS Distributions	T_ASSERT_LT_ULLONG(elapsed_nsec, expected_nsec, "After disabling DMC read(%ld) is reasonably fast", READSIZE);
323*043036a2SApple OSS Distributions}
324*043036a2SApple OSS Distributions
325*043036a2SApple OSS Distributions#else /* TEST_UNENTITLED */
326*043036a2SApple OSS Distributions
327*043036a2SApple OSS Distributions#pragma mark Unentitled Tests
328*043036a2SApple OSS Distributions
329*043036a2SApple OSS DistributionsT_DECL(fsctl_get_unentitled,
330*043036a2SApple OSS Distributions    "fsctl.get should not require entitlement", T_META_TAG_VM_PREFERRED)
331*043036a2SApple OSS Distributions{
332*043036a2SApple OSS Distributions	int err;
333*043036a2SApple OSS Distributions	char *mount_path;
334*043036a2SApple OSS Distributions	disk_conditioner_info info;
335*043036a2SApple OSS Distributions
336*043036a2SApple OSS Distributions	T_SETUPBEGIN;
337*043036a2SApple OSS Distributions	mount_path = mktempmount();
338*043036a2SApple OSS Distributions	T_SETUPEND;
339*043036a2SApple OSS Distributions
340*043036a2SApple OSS Distributions	err = fsctl(mount_path, DISK_CONDITIONER_IOC_GET, &info, 0);
341*043036a2SApple OSS Distributions	T_WITH_ERRNO;
342*043036a2SApple OSS Distributions	T_ASSERT_EQ_INT(0, err, "fsctl.get without entitlement");
343*043036a2SApple OSS Distributions}
344*043036a2SApple OSS Distributions
345*043036a2SApple OSS DistributionsT_DECL(fsctl_set_unentitled,
346*043036a2SApple OSS Distributions    "fsctl.set should require entitlement", T_META_TAG_VM_PREFERRED)
347*043036a2SApple OSS Distributions{
348*043036a2SApple OSS Distributions	int err;
349*043036a2SApple OSS Distributions	char *mount_path;
350*043036a2SApple OSS Distributions	disk_conditioner_info info = {0};
351*043036a2SApple OSS Distributions	disk_conditioner_info expected_info = {0};
352*043036a2SApple OSS Distributions
353*043036a2SApple OSS Distributions	T_SETUPBEGIN;
354*043036a2SApple OSS Distributions	mount_path = mktempmount();
355*043036a2SApple OSS Distributions	T_SETUPEND;
356*043036a2SApple OSS Distributions
357*043036a2SApple OSS Distributions	// save original info
358*043036a2SApple OSS Distributions	err = fsctl(mount_path, DISK_CONDITIONER_IOC_GET, &expected_info, 0);
359*043036a2SApple OSS Distributions	T_WITH_ERRNO;
360*043036a2SApple OSS Distributions	T_ASSERT_EQ_INT(0, err, "Get original DMC info");
361*043036a2SApple OSS Distributions
362*043036a2SApple OSS Distributions	info.enabled = 1;
363*043036a2SApple OSS Distributions	info.access_time_usec = 10;
364*043036a2SApple OSS Distributions	err = fsctl(mount_path, DISK_CONDITIONER_IOC_SET, &info, 0);
365*043036a2SApple OSS Distributions	T_WITH_ERRNO;
366*043036a2SApple OSS Distributions	T_ASSERT_NE_INT(0, err, "fsctl.set returns error without entitlement");
367*043036a2SApple OSS Distributions
368*043036a2SApple OSS Distributions	err = fsctl(mount_path, DISK_CONDITIONER_IOC_GET, &info, 0);
369*043036a2SApple OSS Distributions	T_WITH_ERRNO;
370*043036a2SApple OSS Distributions	T_ASSERT_EQ_INT(0, err, "fsctl.get after unentitled fsctl.set");
371*043036a2SApple OSS Distributions
372*043036a2SApple OSS Distributions	err = memcmp(&info, &expected_info, sizeof(info));
373*043036a2SApple OSS Distributions	T_ASSERT_EQ_INT(0, err, "fsctl.set should not change info without entitlement");
374*043036a2SApple OSS Distributions}
375*043036a2SApple OSS Distributions
376*043036a2SApple OSS Distributions#endif /* TEST_UNENTITLED */
377*043036a2SApple OSS Distributions
378*043036a2SApple OSS Distributions#pragma mark Helpers
379*043036a2SApple OSS Distributions
380*043036a2SApple OSS Distributionsstatic char *
381*043036a2SApple OSS Distributionsmktempdir(void)
382*043036a2SApple OSS Distributions{
383*043036a2SApple OSS Distributions	char *path = malloc(PATH_MAX);
384*043036a2SApple OSS Distributions	strcpy(path, "/tmp/dmc.XXXXXXXX");
385*043036a2SApple OSS Distributions	atexit_b(^{ free(path); });
386*043036a2SApple OSS Distributions
387*043036a2SApple OSS Distributions	// create a temporary mount to run the fsctl on
388*043036a2SApple OSS Distributions	T_WITH_ERRNO;
389*043036a2SApple OSS Distributions	T_ASSERT_NOTNULL(mkdtemp(path), "Create temporary directory");
390*043036a2SApple OSS Distributions	atexit_b(^{ remove(path); });
391*043036a2SApple OSS Distributions
392*043036a2SApple OSS Distributions	return path;
393*043036a2SApple OSS Distributions}
394*043036a2SApple OSS Distributions
395*043036a2SApple OSS Distributions/*
396*043036a2SApple OSS Distributions * Return the path to a temporary mount
397*043036a2SApple OSS Distributions * with no usable filesystem but still
398*043036a2SApple OSS Distributions * can be configured by the disk conditioner
399*043036a2SApple OSS Distributions *
400*043036a2SApple OSS Distributions * Faster than creating a ram disk to test with
401*043036a2SApple OSS Distributions * when access to the filesystem is not necessary
402*043036a2SApple OSS Distributions */
403*043036a2SApple OSS Distributionsstatic char *
404*043036a2SApple OSS Distributionsmktempmount(void)
405*043036a2SApple OSS Distributions{
406*043036a2SApple OSS Distributions	char *mount_path = mktempdir();
407*043036a2SApple OSS Distributions
408*043036a2SApple OSS Distributions	T_WITH_ERRNO;
409*043036a2SApple OSS Distributions	T_ASSERT_EQ_INT(0, mount("devfs", mount_path, MNT_RDONLY, NULL), "Create temporary devfs mount");
410*043036a2SApple OSS Distributions	atexit_b(^{ unmount(mount_path, MNT_FORCE); });
411*043036a2SApple OSS Distributions
412*043036a2SApple OSS Distributions	return mount_path;
413*043036a2SApple OSS Distributions}
414*043036a2SApple OSS Distributions
415*043036a2SApple OSS Distributions#ifndef TEST_UNENTITLED
416*043036a2SApple OSS Distributions
417*043036a2SApple OSS Distributions/*
418*043036a2SApple OSS Distributions * Wrapper around dt_launch_tool/dt_waitpid
419*043036a2SApple OSS Distributions * that works like libc:system()
420*043036a2SApple OSS Distributions */
421*043036a2SApple OSS Distributionsstatic int
422*043036a2SApple OSS Distributionssystem_legal(const char *command)
423*043036a2SApple OSS Distributions{
424*043036a2SApple OSS Distributions	pid_t pid = -1;
425*043036a2SApple OSS Distributions	int exit_status = 0;
426*043036a2SApple OSS Distributions	const char *argv[] = {
427*043036a2SApple OSS Distributions		_PATH_BSHELL,
428*043036a2SApple OSS Distributions		"-c",
429*043036a2SApple OSS Distributions		command,
430*043036a2SApple OSS Distributions		NULL
431*043036a2SApple OSS Distributions	};
432*043036a2SApple OSS Distributions
433*043036a2SApple OSS Distributions	int rc = dt_launch_tool(&pid, (char **)(void *)argv, false, NULL, NULL);
434*043036a2SApple OSS Distributions	if (rc != 0) {
435*043036a2SApple OSS Distributions		return -1;
436*043036a2SApple OSS Distributions	}
437*043036a2SApple OSS Distributions	if (!dt_waitpid(pid, &exit_status, NULL, 30)) {
438*043036a2SApple OSS Distributions		if (exit_status != 0) {
439*043036a2SApple OSS Distributions			return exit_status;
440*043036a2SApple OSS Distributions		}
441*043036a2SApple OSS Distributions		return -1;
442*043036a2SApple OSS Distributions	}
443*043036a2SApple OSS Distributions
444*043036a2SApple OSS Distributions	return exit_status;
445*043036a2SApple OSS Distributions}
446*043036a2SApple OSS Distributions
447*043036a2SApple OSS Distributions/*
448*043036a2SApple OSS Distributions * Return the path to a temporary mount
449*043036a2SApple OSS Distributions * that contains a usable APFS filesystem
450*043036a2SApple OSS Distributions * mounted via a ram disk
451*043036a2SApple OSS Distributions */
452*043036a2SApple OSS Distributionsstatic char *
453*043036a2SApple OSS Distributionsmkramdisk(void)
454*043036a2SApple OSS Distributions{
455*043036a2SApple OSS Distributions	char cmd[1024];
456*043036a2SApple OSS Distributions	char *dev_disk_file = malloc(256);
457*043036a2SApple OSS Distributions	atexit_b(^{ free(dev_disk_file); });
458*043036a2SApple OSS Distributions	strcpy(dev_disk_file, "/tmp/dmc.ramdisk.XXXXXXXX");
459*043036a2SApple OSS Distributions
460*043036a2SApple OSS Distributions	T_QUIET; T_WITH_ERRNO; T_ASSERT_NOTNULL(mktemp(dev_disk_file), "Create temporary file to store dev disk for ramdisk");
461*043036a2SApple OSS Distributions	atexit_b(^{ remove(dev_disk_file); });
462*043036a2SApple OSS Distributions
463*043036a2SApple OSS Distributions	// create the RAM disk device
464*043036a2SApple OSS Distributions	// dev_disk_file will store the /dev/diskX path
465*043036a2SApple OSS Distributions	snprintf(cmd, sizeof(cmd), "diskimagetool attach --nomount ram://16m > %s", dev_disk_file);
466*043036a2SApple OSS Distributions	T_ASSERT_EQ_INT(0, system_legal(cmd), "Create ramdisk");
467*043036a2SApple OSS Distributions
468*043036a2SApple OSS Distributions	atexit_b(^{
469*043036a2SApple OSS Distributions		char eject_cmd[1024];
470*043036a2SApple OSS Distributions		snprintf(eject_cmd, sizeof(eject_cmd), "diskutil eject force `cat %s`", dev_disk_file);
471*043036a2SApple OSS Distributions		system_legal(eject_cmd);
472*043036a2SApple OSS Distributions		remove(dev_disk_file);
473*043036a2SApple OSS Distributions	});
474*043036a2SApple OSS Distributions
475*043036a2SApple OSS Distributions	// initialize and mount as an APFS volume
476*043036a2SApple OSS Distributions	snprintf(cmd, sizeof(cmd), "diskutil eraseVolume APFS dmc.ramdisk `cat %s`", dev_disk_file);
477*043036a2SApple OSS Distributions	T_ASSERT_EQ_INT(0, system_legal(cmd), "Initialize ramdisk as APFS");
478*043036a2SApple OSS Distributions
479*043036a2SApple OSS Distributions	// on iOS the previous eraseVolume doesn't automatically mount
480*043036a2SApple OSS Distributions	// on macOS this mount will be redundant, but successful
481*043036a2SApple OSS Distributions	snprintf(cmd, sizeof(cmd), "diskutil mountDisk `cat %s`", dev_disk_file);
482*043036a2SApple OSS Distributions	T_ASSERT_EQ_INT(0, system_legal(cmd), "Mount ramdisk");
483*043036a2SApple OSS Distributions
484*043036a2SApple OSS Distributions	// on iOS the previous mountDisk doesn't support -mountPoint, so we have to find where it was mounted
485*043036a2SApple OSS Distributions	char *mount_info_path = malloc(256);
486*043036a2SApple OSS Distributions	strcpy(mount_info_path, "/tmp/dmc.mount_info.XXXXXXXX");
487*043036a2SApple OSS Distributions	T_QUIET; T_WITH_ERRNO; T_ASSERT_NOTNULL(mktemp(mount_info_path), "Create temporary file to store mount info for ramdisk");
488*043036a2SApple OSS Distributions	atexit_b(^{ remove(mount_info_path); });
489*043036a2SApple OSS Distributions
490*043036a2SApple OSS Distributions	snprintf(cmd, sizeof(cmd), "diskimagetool list -plist `cat %s` > %s", dev_disk_file, mount_info_path);
491*043036a2SApple OSS Distributions	T_QUIET; T_ASSERT_EQ_INT(0, system_legal(cmd), "Fetch ramdisk mount info");
492*043036a2SApple OSS Distributions
493*043036a2SApple OSS Distributions	NSURL *mountInfoURL = [NSURL fileURLWithPath:@(mount_info_path) isDirectory:NO];
494*043036a2SApple OSS Distributions	free(mount_info_path);
495*043036a2SApple OSS Distributions
496*043036a2SApple OSS Distributions	NSError *error;
497*043036a2SApple OSS Distributions	NSDictionary *mountInfo = [NSDictionary dictionaryWithContentsOfURL:mountInfoURL error:&error];
498*043036a2SApple OSS Distributions	if (!mountInfo) {
499*043036a2SApple OSS Distributions		T_LOG("Error: %s", error.localizedDescription.UTF8String);
500*043036a2SApple OSS Distributions	}
501*043036a2SApple OSS Distributions	T_QUIET; T_ASSERT_NOTNULL(mountInfo, "Read mount info plist");
502*043036a2SApple OSS Distributions
503*043036a2SApple OSS Distributions	NSString *mountPoint = nil;
504*043036a2SApple OSS Distributions	for (NSDictionary *entity in (NSArray *)mountInfo[@"System Entities"]) {
505*043036a2SApple OSS Distributions		mountPoint = entity[@"Mount Point"];
506*043036a2SApple OSS Distributions		if (mountPoint) {
507*043036a2SApple OSS Distributions			break;
508*043036a2SApple OSS Distributions		}
509*043036a2SApple OSS Distributions	}
510*043036a2SApple OSS Distributions	T_QUIET; T_ASSERT_NOTNULL(mountPoint, "Find mount point in mount info plist");
511*043036a2SApple OSS Distributions
512*043036a2SApple OSS Distributions	char *mount_path = malloc(PATH_MAX);
513*043036a2SApple OSS Distributions	atexit_b(^{ free(mount_path); });
514*043036a2SApple OSS Distributions	strlcpy(mount_path, mountPoint.UTF8String, PATH_MAX);
515*043036a2SApple OSS Distributions	return mount_path;
516*043036a2SApple OSS Distributions}
517*043036a2SApple OSS Distributions
518*043036a2SApple OSS Distributionsstatic uint64_t
519*043036a2SApple OSS Distributionstime_for_read(int fd, const char *expected)
520*043036a2SApple OSS Distributions{
521*043036a2SApple OSS Distributions	int err;
522*043036a2SApple OSS Distributions	ssize_t ret;
523*043036a2SApple OSS Distributions	char buf[READSIZE];
524*043036a2SApple OSS Distributions	uint64_t start, stop;
525*043036a2SApple OSS Distributions
526*043036a2SApple OSS Distributions	bzero(buf, sizeof(buf));
527*043036a2SApple OSS Distributions	lseek(fd, 0, SEEK_SET);
528*043036a2SApple OSS Distributions
529*043036a2SApple OSS Distributions	start = dt_nanoseconds();
530*043036a2SApple OSS Distributions	ret = read(fd, buf, READSIZE);
531*043036a2SApple OSS Distributions	stop = dt_nanoseconds();
532*043036a2SApple OSS Distributions
533*043036a2SApple OSS Distributions	T_QUIET; T_ASSERT_GE_LONG(ret, 0L, "read from temporary file");
534*043036a2SApple OSS Distributions	T_QUIET; T_ASSERT_EQ_LONG(ret, READSIZE, "read %ld bytes from temporary file", READSIZE);
535*043036a2SApple OSS Distributions	err = memcmp(buf, expected, sizeof(buf));
536*043036a2SApple OSS Distributions	T_QUIET; T_ASSERT_EQ_INT(0, err, "read expected contents from temporary file");
537*043036a2SApple OSS Distributions
538*043036a2SApple OSS Distributions	return stop - start;
539*043036a2SApple OSS Distributions}
540*043036a2SApple OSS Distributions
541*043036a2SApple OSS Distributionsstatic void
542*043036a2SApple OSS Distributionsperf_setup(char **path, int *fd)
543*043036a2SApple OSS Distributions{
544*043036a2SApple OSS Distributions	int temp_fd;
545*043036a2SApple OSS Distributions	char *temp_path;
546*043036a2SApple OSS Distributions
547*043036a2SApple OSS Distributions	char *mount_path = mkramdisk();
548*043036a2SApple OSS Distributions	T_LOG("Using ramdisk mounted at %s", mount_path);
549*043036a2SApple OSS Distributions
550*043036a2SApple OSS Distributions	temp_path = *path = malloc(PATH_MAX);
551*043036a2SApple OSS Distributions	snprintf(temp_path, PATH_MAX, "%s/dmc.XXXXXXXX", mount_path);
552*043036a2SApple OSS Distributions	atexit_b(^{ free(temp_path); });
553*043036a2SApple OSS Distributions
554*043036a2SApple OSS Distributions	T_ASSERT_NOTNULL(mktemp(temp_path), "Create temporary file");
555*043036a2SApple OSS Distributions	atexit_b(^{ remove(temp_path); });
556*043036a2SApple OSS Distributions	T_LOG("Using temporary file at %s", temp_path);
557*043036a2SApple OSS Distributions
558*043036a2SApple OSS Distributions	temp_fd = *fd = open(temp_path, O_RDWR | O_CREAT);
559*043036a2SApple OSS Distributions	T_WITH_ERRNO;
560*043036a2SApple OSS Distributions	T_ASSERT_GE_INT(temp_fd, 0, "Open temporary file for read/write");
561*043036a2SApple OSS Distributions	atexit_b(^{ close(temp_fd); });
562*043036a2SApple OSS Distributions	fcntl(temp_fd, F_NOCACHE, 1);
563*043036a2SApple OSS Distributions}
564*043036a2SApple OSS Distributions#endif /* !TEST_UNENTITLED */
565