]> git.saurik.com Git - apple/libc.git/blob - tests/mktemp.c
Libc-1244.50.9.tar.gz
[apple/libc.git] / tests / mktemp.c
1 #include <fcntl.h>
2 #include <stdlib.h>
3 #include <sys/mount.h>
4 #include <sys/param.h>
5 #include <sys/stat.h>
6 #include <System/sys/content_protection.h>
7 #include <TargetConditionals.h>
8 #include <unistd.h>
9
10 #include <darwintest.h>
11
12 #define ALLOWED_MKOSTEMP_FLAGS (O_APPEND | O_SHLOCK | O_EXLOCK | O_CLOEXEC)
13 #define FCNTL_GETFL_EXPOSED_OFLAGS (O_APPEND)
14
15 #define TEMPLATE_BASE "/tmp/mktemp_test."
16 #define TEMPLATE_XS "XXXXXXXX"
17 static const char template[] = TEMPLATE_BASE TEMPLATE_XS;
18
19 static void test_mkostemp(int oflags);
20
21 T_DECL(mkstemp, "basic mkstemp test")
22 {
23 char path[sizeof(template)];
24 struct stat path_stat;
25 struct stat fd_stat;
26
27 memcpy(path, template, sizeof(template));
28
29 int fd = mkstemp(path);
30 T_ASSERT_POSIX_SUCCESS(fd,
31 "mkstemp must return a valid fd");
32 T_ASSERT_TRUE(memcmp(path, TEMPLATE_BASE, strlen(TEMPLATE_BASE)) == 0,
33 "mkstemp must respect the template. template: %s, got: %s",
34 template, path);
35
36 // stat fd and path, compare those
37 T_ASSERT_POSIX_ZERO(stat(path, &path_stat),
38 "must be able to stat the path");
39 T_ASSERT_POSIX_ZERO(fstat(fd, &fd_stat),
40 "must be able to fstat the fd");
41 T_ASSERT_TRUE(
42 (path_stat.st_dev == fd_stat.st_dev) &&
43 (path_stat.st_ino == fd_stat.st_ino),
44 "fd does not match the file path");
45 // verify file attributes
46 T_ASSERT_TRUE(S_ISREG(path_stat.st_mode),
47 "the path must point to a regular file");
48 T_ASSERT_EQ(0600, (path_stat.st_mode & 0777),
49 "created file must have 0600 permissions");
50 T_ASSERT_EQ_LLONG(0LL, path_stat.st_size,
51 "created file must be empty");
52 // unlink and stat again
53 T_ASSERT_POSIX_ZERO(unlink(path),
54 "must be able to unlink the created file");
55 T_ASSERT_POSIX_ZERO(fstat(fd, &fd_stat),
56 "must be able to stat the fd after unlink");
57 T_ASSERT_EQ(0, fd_stat.st_nlink,
58 "must not have any hard links to the file after unlink");
59 // close
60 T_ASSERT_POSIX_ZERO(close(fd),
61 "must be able to close the fd");
62 }
63
64 T_DECL(two_mkstemp_calls, "two mkstemp calls return different paths and fds")
65 {
66 char path1[sizeof(template)];
67 char path2[sizeof(template)];
68 memcpy(path1, template, sizeof(path1));
69 memcpy(path2, template, sizeof(path2));
70
71 int fd1 = mkostemp(path1, 0);
72 T_ASSERT_POSIX_SUCCESS(fd1, "mkostemp must return a valid fd");
73
74 int fd2 = mkostemp(path2, 0);
75 T_ASSERT_POSIX_SUCCESS(fd2, "mkostemp must return a valid fd");
76
77 T_ASSERT_NE(fd1, fd2,
78 "two mkostemp calls must return different fds");
79 T_ASSERT_NE_STR(path1, path2,
80 "two mkostemp calls must return different paths");
81
82 T_EXPECT_POSIX_ZERO(unlink(path1),
83 "unlink must succeed for the first file");
84 T_EXPECT_POSIX_ZERO(unlink(path2),
85 "unlink must succeed for the second file");
86 T_EXPECT_POSIX_ZERO(close(fd1),
87 "close must succeed for the first fd");
88 T_EXPECT_POSIX_ZERO(close(fd2),
89 "close must succeed for the second fd");
90 }
91
92 T_DECL(mktemp, "basic mktemp test")
93 {
94 char path[sizeof(template)];
95
96 memcpy(path, template, sizeof(template));
97
98 T_ASSERT_EQ_PTR((void *) mktemp(path), (void *) path,
99 "mktemp must return its argument");
100
101 T_ASSERT_TRUE(memcmp(path, TEMPLATE_BASE, strlen(TEMPLATE_BASE)) == 0,
102 "mktemp must respect the template. template: %s, got: %s",
103 template, path);
104 }
105
106 T_DECL(mkdtemp, "basic mkdtemp test")
107 {
108 char path[sizeof(template)];
109 struct stat dstat;
110
111 memcpy(path, template, sizeof(template));
112
113 T_ASSERT_EQ_PTR((void *) mkdtemp(path), (void *) path,
114 "mkdtemp must return its argument");
115 T_ASSERT_TRUE(memcmp(path, TEMPLATE_BASE, strlen(TEMPLATE_BASE)) == 0,
116 "mkdtemp must respect the template. template: %s, got: %s",
117 template, path);
118
119 // stat fd and path, compare those
120 T_ASSERT_POSIX_ZERO(stat(path, &dstat),
121 "must be able to stat the path");
122 // verify file attributes
123 T_ASSERT_TRUE(S_ISDIR(dstat.st_mode),
124 "the path must point to a directory");
125 T_ASSERT_EQ(0700, (dstat.st_mode & 0777),
126 "created directory must have 0700 permissions");
127 // cleanup
128 T_ASSERT_POSIX_ZERO(rmdir(path),
129 "must be able to rmdir the created directory");
130 }
131
132 T_DECL(mkostemp_no_flags, "mkostemp works with 0 oflags")
133 {
134 test_mkostemp(0);
135 }
136
137 T_DECL(mkostemp_cloexec, "mkostemp works with O_CLOEXEC")
138 {
139 test_mkostemp(O_CLOEXEC);
140 }
141
142 T_DECL(mkostemp_append, "mkostemp works with O_APPEND")
143 {
144 test_mkostemp(O_APPEND);
145 }
146
147 T_DECL(mkostemp_all_flags, "mkostemp works with all allowed flags")
148 {
149 test_mkostemp(ALLOWED_MKOSTEMP_FLAGS);
150 }
151
152 T_DECL(mkostemp_bad_flags, "mkostemp checks for disallowed flags")
153 {
154 char path[sizeof(template)];
155 memcpy(path, template, sizeof(path));
156
157 T_ASSERT_EQ(-1, mkostemp(path, O_CREAT), "mkostemp must not allow O_CREAT");
158 T_ASSERT_EQ(-1, mkostemp(path, O_NONBLOCK), "mkostemp must not allow O_NONBLOCK");
159 }
160
161 static void
162 test_mkostemp(int oflags)
163 {
164 char path[sizeof(template)];
165 int fd;
166
167 T_LOG("testing mkostemp with oflags %#x\n", oflags);
168
169 memcpy(path, template, sizeof(template));
170
171 fd = mkostemp(path, oflags);
172
173 T_ASSERT_POSIX_SUCCESS(fd,
174 "mkostemp must return a valid fd");
175
176 if (oflags & O_CLOEXEC) {
177 T_ASSERT_EQ(FD_CLOEXEC, fcntl(fd, F_GETFD),
178 "O_CLOEXEC must be visible through fcntl GETFD");
179 } else {
180 T_ASSERT_EQ(0, fcntl(fd, F_GETFD),
181 "must not add fcntl GETFD flags at will");
182 }
183
184 T_ASSERT_EQ(
185 (oflags & FCNTL_GETFL_EXPOSED_OFLAGS),
186 (fcntl(fd, F_GETFL) & FCNTL_GETFL_EXPOSED_OFLAGS),
187 "oflags must be visible through fcntl GETFL");
188
189 T_EXPECT_POSIX_ZERO(unlink(path),
190 "must be able to unlink the created file");
191 T_EXPECT_POSIX_ZERO(close(fd),
192 "must be able to close the fd");
193 }
194
195 #if TARGET_OS_IPHONE
196 // mkstemp_dprotected_np is marked as __OSX_UNAVAILABLE, so its usage
197 // has to be guarded by target conditionals. Having a testcase that
198 // always does T_SKIP on OS X yields another issue. The compiler starts
199 // demanding to mark the test function definition as noreturn. Just
200 // don't compile the testcase for OS X.
201 T_DECL(mkstemp_dprotected_np, "basic mkstemp_dprotected_np test")
202 {
203 char path[sizeof(template)];
204 int fd;
205 int protection_class;
206 struct statfs sfs;
207
208 // the test requires /tmp to be HFS
209 T_ASSERT_POSIX_ZERO(statfs("/tmp", &sfs), "must be able to statfs /tmp");
210 if (strcmp(sfs.f_fstypename, "hfs") != 0) {
211 T_SKIP("Can only test dprotected on a HFS filesystem. Got %s,"
212 "skipping the test.", sfs.f_fstypename);
213 }
214
215 memcpy(path, template, sizeof(template));
216 // create a file
217 fd = mkstemp_dprotected_np(path, PROTECTION_CLASS_B, 0);
218 T_ASSERT_POSIX_SUCCESS(fd,
219 "mkstemp_dprotected_np must return a valid fd");
220 T_ASSERT_TRUE(memcmp(path, TEMPLATE_BASE, strlen(TEMPLATE_BASE)) == 0,
221 "mkstemp_dprotected_np must respect the template. template: %s, got: %s",
222 template, path);
223 // verify protection class
224 protection_class = fcntl(fd, F_GETPROTECTIONCLASS);
225 T_WITH_ERRNO; T_ASSERT_EQ(protection_class, PROTECTION_CLASS_B,
226 "must get back the protection class from fcntl");
227 // cleanup
228 T_ASSERT_POSIX_ZERO(close(fd),
229 "must be able to close the fd");
230 T_ASSERT_POSIX_ZERO(unlink(path),
231 "must be able to unlink the created file");
232 }
233 #endif