16512 xhci leaks oneshot interrupt IN transfers on pipe reset
[illumos-gate.git] / usr / src / test / libc-tests / tests / utimes.c
blob166aab4682cd32ad27475d1306afd892ebd05f20
1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
13 * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
17 * Test the implementation of the various *utimes() and *utimens() functions
20 #include <stdio.h>
21 #include <stdbool.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <err.h>
25 #include <sys/sysmacros.h>
26 #include <sys/types.h>
27 #include <sys/time.h>
28 #include <sys/stat.h>
29 #include <fcntl.h>
30 #include <unistd.h>
31 #include <errno.h>
33 timespec_t testtimes[] = {
35 .tv_sec = 1280793678,
36 .tv_nsec = 123456789
39 .tv_sec = 1492732800,
40 .tv_nsec = 17
43 .tv_sec = 1320796855,
44 .tv_nsec = 9
47 .tv_sec = 1498953611,
48 .tv_nsec = 987654321
52 enum ttype {
53 UTIMES,
54 LUTIMES,
55 FUTIMES,
56 FUTIMESAT,
57 FUTIMENS,
58 UTIMENSAT
61 static bool
62 compare_times(struct stat *st, bool trunc, timespec_t *atim, timespec_t *mtim,
63 bool invert)
65 bool ret = true;
67 if (st->st_atim.tv_sec != atim->tv_sec) {
68 ret = false;
69 } else if (st->st_atim.tv_nsec != (
70 trunc ? atim->tv_nsec / 1000 * 1000 : atim->tv_nsec)) {
71 ret = false;
72 } else if (st->st_mtim.tv_sec != mtim->tv_sec) {
73 ret = false;
74 } else if (st->st_mtim.tv_nsec != (
75 trunc ? mtim->tv_nsec / 1000 * 1000 : mtim->tv_nsec)) {
76 ret = false;
79 if ((!ret && !invert) || (ret && invert)) {
80 printf(" actual atime: %ld.%.9ld\n",
81 st->st_atim.tv_sec, st->st_atim.tv_nsec);
82 printf(" actual mtime: %ld.%.9ld\n",
83 st->st_mtim.tv_sec, st->st_mtim.tv_nsec);
86 return (ret);
89 static bool
90 compare_filetime(char *path, bool trunc, timespec_t *atim, timespec_t *mtim,
91 bool invert)
93 struct stat st;
95 if (stat(path, &st) == -1)
96 err(EXIT_FAILURE, "stat %s", path);
98 return (compare_times(&st, trunc, atim, mtim, invert));
101 static bool
102 compare_linktime(char *path, bool trunc, timespec_t *atim, timespec_t *mtim,
103 bool invert)
105 struct stat st;
107 if (lstat(path, &st) == -1)
108 err(EXIT_FAILURE, "lstat %s", path);
110 return (compare_times(&st, trunc, atim, mtim, invert));
113 static bool
114 reset(char *path, timespec_t *atim, timespec_t *mtim)
116 if (utimes(path, NULL) == -1)
117 err(EXIT_FAILURE, "utimes reset");
118 if (compare_filetime(path, true, atim, mtim, true)) {
119 warnx("reset failed");
120 return (false);
122 return (true);
125 static bool
126 reset_link(char *lpath, timespec_t *atim, timespec_t *mtim)
128 if (lutimes(lpath, NULL) == -1)
129 err(EXIT_FAILURE, "lutimes reset");
130 if (compare_linktime(lpath, true, atim, mtim, true)) {
131 warnx("link reset failed");
132 return (false);
134 return (true);
137 static bool
138 runtest(enum ttype fn, char *dir, timespec_t *atim, timespec_t *mtim)
140 char path[MAXPATHLEN + 1];
141 char lpath[MAXPATHLEN + 1];
142 struct timespec ts[2];
143 struct timeval tv[2];
144 int fd, lfd, dfd, ret = true;
146 ts[0] = *atim;
147 ts[1] = *mtim;
148 TIMESPEC_TO_TIMEVAL(&tv[0], &ts[0]);
149 TIMESPEC_TO_TIMEVAL(&tv[1], &ts[1]);
151 if (snprintf(path, sizeof (path), "%s/file", dir) >= sizeof (path))
152 err(EXIT_FAILURE, "snprintf failed to build file path");
154 if ((fd = open(path, O_CREAT, 0644)) == -1)
155 err(EXIT_FAILURE, "open file %s", path);
157 if (snprintf(lpath, sizeof (lpath), "%s/link", dir) >= sizeof (path))
158 err(EXIT_FAILURE, "snprintf failed to build link path");
160 if (symlink(path, lpath) == -1)
161 err(EXIT_FAILURE, "link(%s)", lpath);
163 if ((lfd = open(lpath, O_RDWR)) == -1)
164 err(EXIT_FAILURE, "open link(%s)", lpath);
166 if ((dfd = open(dir, O_DIRECTORY|O_RDONLY)) == -1)
167 err(EXIT_FAILURE, "open dir(%s)", dir);
169 switch (fn) {
170 case UTIMES:
171 printf("..... utimes()\n");
173 if (utimes(path, tv) == -1)
174 err(EXIT_FAILURE, "utimes(%s)", path);
175 if (!compare_filetime(path, true, atim, mtim, false)) {
176 warnx("failed on file");
177 ret = false;
180 if (!reset(path, atim, mtim))
181 ret = false;
183 /* repeat against symbolic link path */
184 if (utimes(lpath, tv) == -1)
185 err(EXIT_FAILURE, "utimes(%s), link", lpath);
186 if (!compare_filetime(path, true, atim, mtim, false)) {
187 warnx("failed on file through link");
188 ret = false;
191 break;
193 case LUTIMES:
194 printf("..... lutimes()\n");
196 /* Use lutimes() against a plain file */
197 if (lutimes(path, tv) == -1)
198 err(EXIT_FAILURE, "lutimes(%s)", path);
199 if (!compare_filetime(path, true, atim, mtim, false)) {
200 warnx("failed on file");
201 ret = false;
204 if (!reset(path, atim, mtim))
205 ret = false;
207 /* Set the time on the link, not on the target */
208 if (lutimes(lpath, tv) == -1)
209 err(EXIT_FAILURE, "lutimes(%s)", lpath);
210 if (!compare_linktime(lpath, true, atim, mtim, false)) {
211 warnx("link time is incorrect");
212 ret = false;
214 if (compare_filetime(path, true, atim, mtim, true)) {
215 warnx("target time was updated incorrectly");
216 ret = false;
219 /* Reset the time on the path and link to the current time */
220 if (!reset(path, atim, mtim) || !reset_link(lpath, atim, mtim))
221 ret = false;
223 /* and modify the target */
224 if (utimes(path, tv) == -1)
225 err(EXIT_FAILURE, "utimes(%s)", path);
226 /* Now the target should match but the link should not */
227 if (!compare_filetime(path, true, atim, mtim, false)) {
228 warnx("target time is incorrect");
229 ret = false;
231 if (compare_linktime(lpath, true, atim, mtim, true)) {
232 warnx("link time was updated incorrectly");
233 ret = false;
235 break;
237 case FUTIMES:
238 printf("..... futimes()\n");
240 if (futimes(fd, tv) == -1)
241 err(EXIT_FAILURE, "futimes(%s)", path);
242 if (!compare_filetime(path, true, atim, mtim, false)) {
243 warnx("failed on file");
244 ret = false;
247 break;
249 case FUTIMESAT: {
250 int rfd;
251 printf("..... futimesat()\n");
253 /* NULL path, should modify the file for 'fd' */
254 if (futimesat(fd, NULL, tv) == -1)
255 err(EXIT_FAILURE, "futimesat(fd, NULL)");
256 if (!compare_filetime(path, true, atim, mtim, false)) {
257 warnx("failed with null path");
258 ret = false;
261 if (!reset(path, atim, mtim))
262 ret = false;
264 /* random descriptor, FQ path, descriptor is ignored */
265 if ((rfd = open("/dev/null", O_RDONLY)) == -1)
266 err(EXIT_FAILURE, "open(/dev/null)");
267 if (futimesat(rfd, path, tv) == -1)
268 err(EXIT_FAILURE, "futimesat(dnfd, %s)", path);
269 if (!compare_filetime(path, true, atim, mtim, false)) {
270 warnx("failed with random descriptor and fq path");
271 ret = false;
274 if (!reset(path, atim, mtim))
275 ret = false;
277 /* repeat against symbolic link path */
278 if (futimesat(rfd, lpath, tv) == -1)
279 err(EXIT_FAILURE, "futimesat(dnfd, %s), link", lpath);
280 if (!compare_filetime(path, true, atim, mtim, false)) {
281 warnx("failed through link with "
282 "random descriptor, fq path");
283 ret = false;
286 (void) close(rfd);
288 if (!reset(path, atim, mtim))
289 ret = false;
291 /* relative to a directory */
292 if (futimesat(dfd, "file", tv) == -1)
293 err(EXIT_FAILURE, "futimesat(dir, file)");
294 if (!compare_filetime(path, true, atim, mtim, false)) {
295 warnx("failed relative to a directory");
296 ret = false;
299 if (!reset(path, atim, mtim))
300 ret = false;
302 /* repeat against symbolic link path */
303 if (futimesat(dfd, "link", tv) == -1)
304 err(EXIT_FAILURE, "futimesat(dir, link)");
305 if (!compare_filetime(path, true, atim, mtim, false)) {
306 warnx("failed through link relative to a directory");
307 ret = false;
310 if (!reset(path, atim, mtim))
311 ret = false;
313 /* AT_FDCWD */
314 if (fchdir(dfd) == -1)
315 err(EXIT_FAILURE, "fchdir(%s)", dir);
316 if (futimesat(AT_FDCWD, "file", tv) == -1)
317 err(EXIT_FAILURE, "futimesat(AT_FDCWD, file)");
318 if (!compare_filetime(path, true, atim, mtim, false)) {
319 warnx("failed with AT_FDCWD relative");
320 ret = false;
323 if (!reset(path, atim, mtim))
324 ret = false;
326 /* repeat against symbolic link path */
327 if (futimesat(AT_FDCWD, "link", tv) == -1)
328 err(EXIT_FAILURE, "futimesat(AT_FDCWD, link)");
329 if (!compare_filetime(path, true, atim, mtim, false)) {
330 warnx("failed through link with AT_FDCWD relative");
331 ret = false;
334 break;
337 case FUTIMENS:
338 printf("..... futimens()\n");
339 if (futimens(fd, ts) == -1)
340 err(EXIT_FAILURE, "futimesns(%s)", path);
341 if (!compare_filetime(path, false, atim, mtim, false)) {
342 warnx("failed with plain file fd");
343 ret = false;
346 break;
348 case UTIMENSAT: {
349 int rfd;
351 printf("..... utimensat()\n");
353 /* NULL path, expect EFAULT (cf. futimesat()) */
354 if (utimensat(fd, NULL, ts, 0) != -1 || errno != EFAULT) {
355 warnx("null path should return EFAULT but got %d/%s",
356 errno, strerror(errno));
357 ret = false;
360 /* random descriptor, FQ path, descriptor is ignored */
361 if ((rfd = open("/dev/null", O_RDONLY)) == -1)
362 err(EXIT_FAILURE, "open(/dev/null)");
363 if (utimensat(rfd, path, ts, 0) == -1)
364 err(EXIT_FAILURE, "utimensat(dnfd, %s)", path);
365 if (!compare_filetime(path, false, atim, mtim, false)) {
366 warnx("failed with random descriptor, fq path");
367 ret = false;
370 if (!reset(path, atim, mtim))
371 ret = false;
373 /* repeat against symbolic link path */
374 if (utimensat(rfd, lpath, ts, 0) == -1)
375 err(EXIT_FAILURE, "utimensat(dnfd, link %s)", lpath);
376 if (!compare_filetime(path, false, atim, mtim, false)) {
377 warnx("failed against link with random descriptor, "
378 "fq path");
379 ret = false;
382 (void) close(rfd);
384 if (!reset(path, atim, mtim))
385 ret = false;
387 /* relative to a directory */
388 if (utimensat(dfd, "file", ts, 0) == -1)
389 err(EXIT_FAILURE, "utimensat(dir, file)");
390 if (!compare_filetime(path, false, atim, mtim, false)) {
391 warnx("failed relative to a directory");
392 ret = false;
395 if (!reset(path, atim, mtim))
396 ret = false;
398 /* repeat against symbolic link path */
399 if (utimensat(dfd, "link", ts, 0) == -1)
400 err(EXIT_FAILURE, "utimensat(dir, link)");
401 if (!compare_filetime(path, false, atim, mtim, false)) {
402 warnx("failed through link relative to a directory");
403 ret = false;
406 if (!reset(path, atim, mtim))
407 ret = false;
409 /* AT_FDCWD */
410 if (fchdir(dfd) == -1)
411 err(EXIT_FAILURE, "fchdir(%s)", dir);
412 if (utimensat(AT_FDCWD, "file", ts, 0) == -1)
413 err(EXIT_FAILURE, "utimensat(AT_FDCWD, file)");
414 if (!compare_filetime(path, false, atim, mtim, false)) {
415 warnx("failed with AT_FDCWD relative");
416 ret = false;
419 if (!reset(path, atim, mtim))
420 ret = false;
422 /* repeat against symbolic link path */
423 if (utimensat(AT_FDCWD, "link", ts, 0) == -1)
424 err(EXIT_FAILURE, "utimensat(AT_FDCWD, link)");
425 if (!compare_filetime(path, false, atim, mtim, false)) {
426 warnx("failed through link with AT_FDCWD relative");
427 ret = false;
430 if (!reset(path, atim, mtim))
431 ret = false;
434 * Check that none of the above operations have changed the
435 * timestamp on the link.
437 if (compare_linktime(lpath, true, atim, mtim, true)) {
438 warnx("link timestamp was unexpectedly modified");
439 ret = false;
442 /* Set the time on the link, not on the target */
443 if (utimensat(AT_FDCWD, "link", ts, AT_SYMLINK_NOFOLLOW) == -1)
444 err(EXIT_FAILURE, "utimensat(AT_FDCWD, lflag)");
445 if (!compare_linktime(lpath, false, atim, mtim, false)) {
446 warnx("link time is incorrect");
447 ret = false;
449 if (compare_filetime(path, false, atim, mtim, true)) {
450 warnx("target time was updated incorrectly");
451 ret = false;
454 break;
457 (void) close(dfd);
458 (void) close(lfd);
459 (void) close(fd);
461 if (unlink(lpath) == -1)
462 err(EXIT_FAILURE, "unlink(%s)", lpath);
463 if (unlink(path) == -1)
464 err(EXIT_FAILURE, "unlink(%s)", path);
466 if (!ret)
467 warnx("Test failed");
469 return (ret);
472 static bool
473 runtests(char *dir, timespec_t *atim, timespec_t *mtim)
475 bool ret = true;
477 printf("Testing:\n... atime: %ld.%.9ld\n... mtime: %ld.%.9ld\n",
478 atim->tv_sec, atim->tv_nsec, mtim->tv_sec, mtim->tv_nsec);
480 if (!runtest(UTIMES, dir, atim, mtim))
481 ret = false;
482 if (!runtest(LUTIMES, dir, atim, mtim))
483 ret = false;
484 if (!runtest(FUTIMES, dir, atim, mtim))
485 ret = false;
486 if (!runtest(FUTIMESAT, dir, atim, mtim))
487 ret = false;
488 if (!runtest(FUTIMENS, dir, atim, mtim))
489 ret = false;
490 if (!runtest(UTIMENSAT, dir, atim, mtim))
491 ret = false;
493 return (ret);
497 main(void)
499 char dir[] = "/tmp/utimes.XXXXXX";
500 int ret = EXIT_SUCCESS;
501 int i;
503 if (mkdtemp(dir) == NULL)
504 err(EXIT_FAILURE, "failed to create temporary directory");
506 for (i = 0; i < ARRAY_SIZE(testtimes); i += 2) {
507 if (!runtests(dir, &testtimes[i], &testtimes[i + 1]))
508 ret = EXIT_FAILURE;
512 * Some tests will have changed directory into 'dir' to test the
513 * AT_FDCWD case. Change back to / to avoid EBUSY when removing dir.
515 if (chdir("/") == -1)
516 err(EXIT_FAILURE, "chdir(/)");
517 if (rmdir(dir) == -1)
518 err(EXIT_FAILURE, "rmdir %s", dir);
520 return (ret);