1b7a77849SAndy Fiddaman /*
2b7a77849SAndy Fiddaman  * This file and its contents are supplied under the terms of the
3b7a77849SAndy Fiddaman  * Common Development and Distribution License ("CDDL"), version 1.0.
4b7a77849SAndy Fiddaman  * You may only use this file in accordance with the terms of version
5b7a77849SAndy Fiddaman  * 1.0 of the CDDL.
6b7a77849SAndy Fiddaman  *
7b7a77849SAndy Fiddaman  * A full copy of the text of the CDDL should have accompanied this
8b7a77849SAndy Fiddaman  * source.  A copy of the CDDL is also available via the Internet at
9b7a77849SAndy Fiddaman  * http://www.illumos.org/license/CDDL.
10b7a77849SAndy Fiddaman  */
11b7a77849SAndy Fiddaman 
12b7a77849SAndy Fiddaman /*
13b7a77849SAndy Fiddaman  * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
14b7a77849SAndy Fiddaman  */
15b7a77849SAndy Fiddaman 
16b7a77849SAndy Fiddaman /*
17b7a77849SAndy Fiddaman  * Test the implementation of the various *utimes() and *utimens() functions
18b7a77849SAndy Fiddaman  */
19b7a77849SAndy Fiddaman 
20b7a77849SAndy Fiddaman #include <stdio.h>
21b7a77849SAndy Fiddaman #include <stdbool.h>
22b7a77849SAndy Fiddaman #include <stdlib.h>
23b7a77849SAndy Fiddaman #include <string.h>
24b7a77849SAndy Fiddaman #include <err.h>
25b7a77849SAndy Fiddaman #include <sys/sysmacros.h>
26b7a77849SAndy Fiddaman #include <sys/types.h>
27b7a77849SAndy Fiddaman #include <sys/time.h>
28b7a77849SAndy Fiddaman #include <sys/stat.h>
29b7a77849SAndy Fiddaman #include <fcntl.h>
30b7a77849SAndy Fiddaman #include <unistd.h>
31b7a77849SAndy Fiddaman #include <errno.h>
32b7a77849SAndy Fiddaman 
33b7a77849SAndy Fiddaman timespec_t testtimes[] = {
34b7a77849SAndy Fiddaman 	{
35b7a77849SAndy Fiddaman 		.tv_sec = 1280793678,
36b7a77849SAndy Fiddaman 		.tv_nsec = 123456789
37b7a77849SAndy Fiddaman 	},
38b7a77849SAndy Fiddaman 	{
39b7a77849SAndy Fiddaman 		.tv_sec = 1492732800,
40b7a77849SAndy Fiddaman 		.tv_nsec = 17
41b7a77849SAndy Fiddaman 	},
42b7a77849SAndy Fiddaman 	{
43b7a77849SAndy Fiddaman 		.tv_sec = 1320796855,
44b7a77849SAndy Fiddaman 		.tv_nsec = 9
45b7a77849SAndy Fiddaman 	},
46b7a77849SAndy Fiddaman 	{
47b7a77849SAndy Fiddaman 		.tv_sec = 1498953611,
48b7a77849SAndy Fiddaman 		.tv_nsec = 987654321
49b7a77849SAndy Fiddaman 	}
50b7a77849SAndy Fiddaman };
51b7a77849SAndy Fiddaman 
52b7a77849SAndy Fiddaman enum ttype {
53b7a77849SAndy Fiddaman 	UTIMES,
54b7a77849SAndy Fiddaman 	LUTIMES,
55b7a77849SAndy Fiddaman 	FUTIMES,
56b7a77849SAndy Fiddaman 	FUTIMESAT,
57b7a77849SAndy Fiddaman 	FUTIMENS,
58b7a77849SAndy Fiddaman 	UTIMENSAT
59b7a77849SAndy Fiddaman };
60b7a77849SAndy Fiddaman 
61b7a77849SAndy Fiddaman static bool
compare_times(struct stat * st,bool trunc,timespec_t * atim,timespec_t * mtim,bool invert)62b7a77849SAndy Fiddaman compare_times(struct stat *st, bool trunc, timespec_t *atim, timespec_t *mtim,
63b7a77849SAndy Fiddaman     bool invert)
64b7a77849SAndy Fiddaman {
65b7a77849SAndy Fiddaman 	bool ret = true;
66b7a77849SAndy Fiddaman 
67b7a77849SAndy Fiddaman 	if (st->st_atim.tv_sec != atim->tv_sec) {
68b7a77849SAndy Fiddaman 		ret = false;
69b7a77849SAndy Fiddaman 	} else if (st->st_atim.tv_nsec != (
70b7a77849SAndy Fiddaman 	    trunc ? atim->tv_nsec / 1000 * 1000 : atim->tv_nsec)) {
71b7a77849SAndy Fiddaman 		ret = false;
72b7a77849SAndy Fiddaman 	} else if (st->st_mtim.tv_sec != mtim->tv_sec) {
73b7a77849SAndy Fiddaman 		ret = false;
74b7a77849SAndy Fiddaman 	} else if (st->st_mtim.tv_nsec != (
75b7a77849SAndy Fiddaman 	    trunc ? mtim->tv_nsec / 1000 * 1000 : mtim->tv_nsec)) {
76b7a77849SAndy Fiddaman 		ret = false;
77b7a77849SAndy Fiddaman 	}
78b7a77849SAndy Fiddaman 
79b7a77849SAndy Fiddaman 	if ((!ret && !invert) || (ret && invert)) {
80b7a77849SAndy Fiddaman 		printf("    actual atime: %ld.%.9ld\n",
81b7a77849SAndy Fiddaman 		    st->st_atim.tv_sec, st->st_atim.tv_nsec);
82b7a77849SAndy Fiddaman 		printf("    actual mtime: %ld.%.9ld\n",
83b7a77849SAndy Fiddaman 		    st->st_mtim.tv_sec, st->st_mtim.tv_nsec);
84b7a77849SAndy Fiddaman 	}
85b7a77849SAndy Fiddaman 
86b7a77849SAndy Fiddaman 	return (ret);
87b7a77849SAndy Fiddaman }
88b7a77849SAndy Fiddaman 
89b7a77849SAndy Fiddaman static bool
compare_filetime(char * path,bool trunc,timespec_t * atim,timespec_t * mtim,bool invert)90b7a77849SAndy Fiddaman compare_filetime(char *path, bool trunc, timespec_t *atim, timespec_t *mtim,
91b7a77849SAndy Fiddaman     bool invert)
92b7a77849SAndy Fiddaman {
93b7a77849SAndy Fiddaman 	struct stat st;
94b7a77849SAndy Fiddaman 
95b7a77849SAndy Fiddaman 	if (stat(path, &st) == -1)
96b7a77849SAndy Fiddaman 		err(EXIT_FAILURE, "stat %s", path);
97b7a77849SAndy Fiddaman 
98b7a77849SAndy Fiddaman 	return (compare_times(&st, trunc, atim, mtim, invert));
99b7a77849SAndy Fiddaman }
100b7a77849SAndy Fiddaman 
101b7a77849SAndy Fiddaman static bool
compare_linktime(char * path,bool trunc,timespec_t * atim,timespec_t * mtim,bool invert)102b7a77849SAndy Fiddaman compare_linktime(char *path, bool trunc, timespec_t *atim, timespec_t *mtim,
103b7a77849SAndy Fiddaman     bool invert)
104b7a77849SAndy Fiddaman {
105b7a77849SAndy Fiddaman 	struct stat st;
106b7a77849SAndy Fiddaman 
107b7a77849SAndy Fiddaman 	if (lstat(path, &st) == -1)
108b7a77849SAndy Fiddaman 		err(EXIT_FAILURE, "lstat %s", path);
109b7a77849SAndy Fiddaman 
110b7a77849SAndy Fiddaman 	return (compare_times(&st, trunc, atim, mtim, invert));
111b7a77849SAndy Fiddaman }
112b7a77849SAndy Fiddaman 
113b7a77849SAndy Fiddaman static bool
reset(char * path,timespec_t * atim,timespec_t * mtim)114b7a77849SAndy Fiddaman reset(char *path, timespec_t *atim, timespec_t *mtim)
115b7a77849SAndy Fiddaman {
116b7a77849SAndy Fiddaman 	if (utimes(path, NULL) == -1)
117b7a77849SAndy Fiddaman 		err(EXIT_FAILURE, "utimes reset");
118b7a77849SAndy Fiddaman 	if (compare_filetime(path, true, atim, mtim, true)) {
119b7a77849SAndy Fiddaman 		warnx("reset failed");
120b7a77849SAndy Fiddaman 		return (false);
121b7a77849SAndy Fiddaman 	}
122b7a77849SAndy Fiddaman 	return (true);
123b7a77849SAndy Fiddaman }
124b7a77849SAndy Fiddaman 
125b7a77849SAndy Fiddaman static bool
reset_link(char * lpath,timespec_t * atim,timespec_t * mtim)126b7a77849SAndy Fiddaman reset_link(char *lpath, timespec_t *atim, timespec_t *mtim)
127b7a77849SAndy Fiddaman {
128b7a77849SAndy Fiddaman 	if (lutimes(lpath, NULL) == -1)
129b7a77849SAndy Fiddaman 		err(EXIT_FAILURE, "lutimes reset");
130b7a77849SAndy Fiddaman 	if (compare_linktime(lpath, true, atim, mtim, true)) {
131b7a77849SAndy Fiddaman 		warnx("link reset failed");
132b7a77849SAndy Fiddaman 		return (false);
133b7a77849SAndy Fiddaman 	}
134b7a77849SAndy Fiddaman 	return (true);
135b7a77849SAndy Fiddaman }
136b7a77849SAndy Fiddaman 
137b7a77849SAndy Fiddaman static bool
runtest(enum ttype fn,char * dir,timespec_t * atim,timespec_t * mtim)138b7a77849SAndy Fiddaman runtest(enum ttype fn, char *dir, timespec_t *atim, timespec_t *mtim)
139b7a77849SAndy Fiddaman {
140b7a77849SAndy Fiddaman 	char path[MAXPATHLEN + 1];
141b7a77849SAndy Fiddaman 	char lpath[MAXPATHLEN + 1];
142b7a77849SAndy Fiddaman 	struct timespec ts[2];
143b7a77849SAndy Fiddaman 	struct timeval tv[2];
144b7a77849SAndy Fiddaman 	int fd, lfd, dfd, ret = true;
145b7a77849SAndy Fiddaman 
146b7a77849SAndy Fiddaman 	ts[0] = *atim;
147b7a77849SAndy Fiddaman 	ts[1] = *mtim;
148b7a77849SAndy Fiddaman 	TIMESPEC_TO_TIMEVAL(&tv[0], &ts[0]);
149b7a77849SAndy Fiddaman 	TIMESPEC_TO_TIMEVAL(&tv[1], &ts[1]);
150b7a77849SAndy Fiddaman 
151b7a77849SAndy Fiddaman 	if (snprintf(path, sizeof (path), "%s/file", dir) >= sizeof (path))
152b7a77849SAndy Fiddaman 		err(EXIT_FAILURE, "snprintf failed to build file path");
153b7a77849SAndy Fiddaman 
154b7a77849SAndy Fiddaman 	if ((fd = open(path, O_CREAT, 0644)) == -1)
155b7a77849SAndy Fiddaman 		err(EXIT_FAILURE, "open file %s", path);
156b7a77849SAndy Fiddaman 
157b7a77849SAndy Fiddaman 	if (snprintf(lpath, sizeof (lpath), "%s/link", dir) >= sizeof (path))
158b7a77849SAndy Fiddaman 		err(EXIT_FAILURE, "snprintf failed to build link path");
159b7a77849SAndy Fiddaman 
160b7a77849SAndy Fiddaman 	if (symlink(path, lpath) == -1)
161b7a77849SAndy Fiddaman 		err(EXIT_FAILURE, "link(%s)", lpath);
162b7a77849SAndy Fiddaman 
163b7a77849SAndy Fiddaman 	if ((lfd = open(lpath, O_RDWR)) == -1)
164b7a77849SAndy Fiddaman 		err(EXIT_FAILURE, "open link(%s)", lpath);
165b7a77849SAndy Fiddaman 
166b7a77849SAndy Fiddaman 	if ((dfd = open(dir, O_DIRECTORY|O_RDONLY)) == -1)
167b7a77849SAndy Fiddaman 		err(EXIT_FAILURE, "open dir(%s)", dir);
168b7a77849SAndy Fiddaman 
169b7a77849SAndy Fiddaman 	switch (fn) {
170b7a77849SAndy Fiddaman 	case UTIMES:
171b7a77849SAndy Fiddaman 		printf("..... utimes()\n");
172b7a77849SAndy Fiddaman 
173b7a77849SAndy Fiddaman 		if (utimes(path, tv) == -1)
174b7a77849SAndy Fiddaman 			err(EXIT_FAILURE, "utimes(%s)", path);
175b7a77849SAndy Fiddaman 		if (!compare_filetime(path, true, atim, mtim, false)) {
176b7a77849SAndy Fiddaman 			warnx("failed on file");
177b7a77849SAndy Fiddaman 			ret = false;
178b7a77849SAndy Fiddaman 		}
179b7a77849SAndy Fiddaman 
180b7a77849SAndy Fiddaman 		if (!reset(path, atim, mtim))
181b7a77849SAndy Fiddaman 			ret = false;
182b7a77849SAndy Fiddaman 
183b7a77849SAndy Fiddaman 		/* repeat against symbolic link path */
184b7a77849SAndy Fiddaman 		if (utimes(lpath, tv) == -1)
185b7a77849SAndy Fiddaman 			err(EXIT_FAILURE, "utimes(%s), link", lpath);
186b7a77849SAndy Fiddaman 		if (!compare_filetime(path, true, atim, mtim, false)) {
187b7a77849SAndy Fiddaman 			warnx("failed on file through link");
188b7a77849SAndy Fiddaman 			ret = false;
189b7a77849SAndy Fiddaman 		}
190b7a77849SAndy Fiddaman 
191b7a77849SAndy Fiddaman 		break;
192b7a77849SAndy Fiddaman 
193b7a77849SAndy Fiddaman 	case LUTIMES:
194b7a77849SAndy Fiddaman 		printf("..... lutimes()\n");
195b7a77849SAndy Fiddaman 
196b7a77849SAndy Fiddaman 		/* Use lutimes() against a plain file */
197b7a77849SAndy Fiddaman 		if (lutimes(path, tv) == -1)
198b7a77849SAndy Fiddaman 			err(EXIT_FAILURE, "lutimes(%s)", path);
199b7a77849SAndy Fiddaman 		if (!compare_filetime(path, true, atim, mtim, false)) {
200b7a77849SAndy Fiddaman 			warnx("failed on file");
201b7a77849SAndy Fiddaman 			ret = false;
202b7a77849SAndy Fiddaman 		}
203b7a77849SAndy Fiddaman 
204b7a77849SAndy Fiddaman 		if (!reset(path, atim, mtim))
205b7a77849SAndy Fiddaman 			ret = false;
206b7a77849SAndy Fiddaman 
207b7a77849SAndy Fiddaman 		/* Set the time on the link, not on the target */
208b7a77849SAndy Fiddaman 		if (lutimes(lpath, tv) == -1)
209b7a77849SAndy Fiddaman 			err(EXIT_FAILURE, "lutimes(%s)", lpath);
210b7a77849SAndy Fiddaman 		if (!compare_linktime(lpath, true, atim, mtim, false)) {
211b7a77849SAndy Fiddaman 			warnx("link time is incorrect");
212b7a77849SAndy Fiddaman 			ret = false;
213b7a77849SAndy Fiddaman 		}
214b7a77849SAndy Fiddaman 		if (compare_filetime(path, true, atim, mtim, true)) {
215b7a77849SAndy Fiddaman 			warnx("target time was updated incorrectly");
216b7a77849SAndy Fiddaman 			ret = false;
217b7a77849SAndy Fiddaman 		}
218b7a77849SAndy Fiddaman 
219b7a77849SAndy Fiddaman 		/* Reset the time on the path and link to the current time */
220b7a77849SAndy Fiddaman 		if (!reset(path, atim, mtim) || !reset_link(lpath, atim, mtim))
221b7a77849SAndy Fiddaman 			ret = false;
222b7a77849SAndy Fiddaman 
223b7a77849SAndy Fiddaman 		/* and modify the target */
224b7a77849SAndy Fiddaman 		if (utimes(path, tv) == -1)
225b7a77849SAndy Fiddaman 			err(EXIT_FAILURE, "utimes(%s)", path);
226b7a77849SAndy Fiddaman 		/* Now the target should match but the link should not */
227b7a77849SAndy Fiddaman 		if (!compare_filetime(path, true, atim, mtim, false)) {
228b7a77849SAndy Fiddaman 			warnx("target time is incorrect");
229b7a77849SAndy Fiddaman 			ret = false;
230b7a77849SAndy Fiddaman 		}
231b7a77849SAndy Fiddaman 		if (compare_linktime(lpath, true, atim, mtim, true)) {
232b7a77849SAndy Fiddaman 			warnx("link time was updated incorrectly");
233b7a77849SAndy Fiddaman 			ret = false;
234b7a77849SAndy Fiddaman 		}
235b7a77849SAndy Fiddaman 		break;
236b7a77849SAndy Fiddaman 
237b7a77849SAndy Fiddaman 	case FUTIMES:
238b7a77849SAndy Fiddaman 		printf("..... futimes()\n");
239b7a77849SAndy Fiddaman 
240b7a77849SAndy Fiddaman 		if (futimes(fd, tv) == -1)
241b7a77849SAndy Fiddaman 			err(EXIT_FAILURE, "futimes(%s)", path);
242b7a77849SAndy Fiddaman 		if (!compare_filetime(path, true, atim, mtim, false)) {
243b7a77849SAndy Fiddaman 			warnx("failed on file");
244b7a77849SAndy Fiddaman 			ret = false;
245b7a77849SAndy Fiddaman 		}
246b7a77849SAndy Fiddaman 
247b7a77849SAndy Fiddaman 		break;
248b7a77849SAndy Fiddaman 
249b7a77849SAndy Fiddaman 	case FUTIMESAT: {
250b7a77849SAndy Fiddaman 		int rfd;
251b7a77849SAndy Fiddaman 		printf("..... futimesat()\n");
252b7a77849SAndy Fiddaman 
253b7a77849SAndy Fiddaman 		/* NULL path, should modify the file for 'fd' */
254b7a77849SAndy Fiddaman 		if (futimesat(fd, NULL, tv) == -1)
255b7a77849SAndy Fiddaman 			err(EXIT_FAILURE, "futimesat(fd, NULL)");
256b7a77849SAndy Fiddaman 		if (!compare_filetime(path, true, atim, mtim, false)) {
257b7a77849SAndy Fiddaman 			warnx("failed with null path");
258b7a77849SAndy Fiddaman 			ret = false;
259b7a77849SAndy Fiddaman 		}
260b7a77849SAndy Fiddaman 
261b7a77849SAndy Fiddaman 		if (!reset(path, atim, mtim))
262b7a77849SAndy Fiddaman 			ret = false;
263b7a77849SAndy Fiddaman 
264b7a77849SAndy Fiddaman 		/* random descriptor, FQ path, descriptor is ignored */
265b7a77849SAndy Fiddaman 		if ((rfd = open("/dev/null", O_RDONLY)) == -1)
266b7a77849SAndy Fiddaman 			err(EXIT_FAILURE, "open(/dev/null)");
267b7a77849SAndy Fiddaman 		if (futimesat(rfd, path, tv) == -1)
268b7a77849SAndy Fiddaman 			err(EXIT_FAILURE, "futimesat(dnfd, %s)", path);
269b7a77849SAndy Fiddaman 		if (!compare_filetime(path, true, atim, mtim, false)) {
270b7a77849SAndy Fiddaman 			warnx("failed with random descriptor and fq path");
271b7a77849SAndy Fiddaman 			ret = false;
272b7a77849SAndy Fiddaman 		}
273b7a77849SAndy Fiddaman 
274b7a77849SAndy Fiddaman 		if (!reset(path, atim, mtim))
275b7a77849SAndy Fiddaman 			ret = false;
276b7a77849SAndy Fiddaman 
277b7a77849SAndy Fiddaman 		/* repeat against symbolic link path */
278b7a77849SAndy Fiddaman 		if (futimesat(rfd, lpath, tv) == -1)
279b7a77849SAndy Fiddaman 			err(EXIT_FAILURE, "futimesat(dnfd, %s), link", lpath);
280b7a77849SAndy Fiddaman 		if (!compare_filetime(path, true, atim, mtim, false)) {
281b7a77849SAndy Fiddaman 			warnx("failed through link with "
282b7a77849SAndy Fiddaman 			    "random descriptor, fq path");
283b7a77849SAndy Fiddaman 			ret = false;
284b7a77849SAndy Fiddaman 		}
285b7a77849SAndy Fiddaman 
286b7a77849SAndy Fiddaman 		(void) close(rfd);
287b7a77849SAndy Fiddaman 
288b7a77849SAndy Fiddaman 		if (!reset(path, atim, mtim))
289b7a77849SAndy Fiddaman 			ret = false;
290b7a77849SAndy Fiddaman 
291b7a77849SAndy Fiddaman 		/* relative to a directory */
292b7a77849SAndy Fiddaman 		if (futimesat(dfd, "file", tv) == -1)
293*6353250fSRobert Mustacchi 			err(EXIT_FAILURE, "futimesat(dir, file)");
294b7a77849SAndy Fiddaman 		if (!compare_filetime(path, true, atim, mtim, false)) {
295b7a77849SAndy Fiddaman 			warnx("failed relative to a directory");
296b7a77849SAndy Fiddaman 			ret = false;
297b7a77849SAndy Fiddaman 		}
298b7a77849SAndy Fiddaman 
299b7a77849SAndy Fiddaman 		if (!reset(path, atim, mtim))
300b7a77849SAndy Fiddaman 			ret = false;
301b7a77849SAndy Fiddaman 
302b7a77849SAndy Fiddaman 		/* repeat against symbolic link path */
303b7a77849SAndy Fiddaman 		if (futimesat(dfd, "link", tv) == -1)
304b7a77849SAndy Fiddaman 			err(EXIT_FAILURE, "futimesat(dir, link)");
305b7a77849SAndy Fiddaman 		if (!compare_filetime(path, true, atim, mtim, false)) {
306b7a77849SAndy Fiddaman 			warnx("failed through link relative to a directory");
307b7a77849SAndy Fiddaman 			ret = false;
308b7a77849SAndy Fiddaman 		}
309b7a77849SAndy Fiddaman 
310b7a77849SAndy Fiddaman 		if (!reset(path, atim, mtim))
311b7a77849SAndy Fiddaman 			ret = false;
312b7a77849SAndy Fiddaman 
313b7a77849SAndy Fiddaman 		/* AT_FDCWD */
314b7a77849SAndy Fiddaman 		if (fchdir(dfd) == -1)
315b7a77849SAndy Fiddaman 			err(EXIT_FAILURE, "fchdir(%s)", dir);
316b7a77849SAndy Fiddaman 		if (futimesat(AT_FDCWD, "file", tv) == -1)
317*6353250fSRobert Mustacchi 			err(EXIT_FAILURE, "futimesat(AT_FDCWD, file)");
318b7a77849SAndy Fiddaman 		if (!compare_filetime(path, true, atim, mtim, false)) {
319b7a77849SAndy Fiddaman 			warnx("failed with AT_FDCWD relative");
320b7a77849SAndy Fiddaman 			ret = false;
321b7a77849SAndy Fiddaman 		}
322b7a77849SAndy Fiddaman 
323b7a77849SAndy Fiddaman 		if (!reset(path, atim, mtim))
324b7a77849SAndy Fiddaman 			ret = false;
325b7a77849SAndy Fiddaman 
326b7a77849SAndy Fiddaman 		/* repeat against symbolic link path */
327b7a77849SAndy Fiddaman 		if (futimesat(AT_FDCWD, "link", tv) == -1)
328b7a77849SAndy Fiddaman 			err(EXIT_FAILURE, "futimesat(AT_FDCWD, link)");
329b7a77849SAndy Fiddaman 		if (!compare_filetime(path, true, atim, mtim, false)) {
330b7a77849SAndy Fiddaman 			warnx("failed through link with AT_FDCWD relative");
331b7a77849SAndy Fiddaman 			ret = false;
332b7a77849SAndy Fiddaman 		}
333b7a77849SAndy Fiddaman 
334b7a77849SAndy Fiddaman 		break;
335b7a77849SAndy Fiddaman 	}
336b7a77849SAndy Fiddaman 
337b7a77849SAndy Fiddaman 	case FUTIMENS:
338b7a77849SAndy Fiddaman 		printf("..... futimens()\n");
339b7a77849SAndy Fiddaman 		if (futimens(fd, ts) == -1)
340b7a77849SAndy Fiddaman 			err(EXIT_FAILURE, "futimesns(%s)", path);
341b7a77849SAndy Fiddaman 		if (!compare_filetime(path, false, atim, mtim, false)) {
342b7a77849SAndy Fiddaman 			warnx("failed with plain file fd");
343b7a77849SAndy Fiddaman 			ret = false;
344b7a77849SAndy Fiddaman 		}
345b7a77849SAndy Fiddaman 
346b7a77849SAndy Fiddaman 		break;
347b7a77849SAndy Fiddaman 
348b7a77849SAndy Fiddaman 	case UTIMENSAT: {
349b7a77849SAndy Fiddaman 		int rfd;
350b7a77849SAndy Fiddaman 
351b7a77849SAndy Fiddaman 		printf("..... utimensat()\n");
352b7a77849SAndy Fiddaman 
353b7a77849SAndy Fiddaman 		/* NULL path, expect EFAULT (cf. futimesat()) */
354b7a77849SAndy Fiddaman 		if (utimensat(fd, NULL, ts, 0) != -1 || errno != EFAULT) {
355b7a77849SAndy Fiddaman 			warnx("null path should return EFAULT but got %d/%s",
356b7a77849SAndy Fiddaman 			    errno, strerror(errno));
357b7a77849SAndy Fiddaman 			ret = false;
358b7a77849SAndy Fiddaman 		}
359b7a77849SAndy Fiddaman 
360b7a77849SAndy Fiddaman 		/* random descriptor, FQ path, descriptor is ignored */
361b7a77849SAndy Fiddaman 		if ((rfd = open("/dev/null", O_RDONLY)) == -1)
362b7a77849SAndy Fiddaman 			err(EXIT_FAILURE, "open(/dev/null)");
363b7a77849SAndy Fiddaman 		if (utimensat(rfd, path, ts, 0) == -1)
364b7a77849SAndy Fiddaman 			err(EXIT_FAILURE, "utimensat(dnfd, %s)", path);
365b7a77849SAndy Fiddaman 		if (!compare_filetime(path, false, atim, mtim, false)) {
366b7a77849SAndy Fiddaman 			warnx("failed with random descriptor, fq path");
367b7a77849SAndy Fiddaman 			ret = false;
368b7a77849SAndy Fiddaman 		}
369b7a77849SAndy Fiddaman 
370b7a77849SAndy Fiddaman 		if (!reset(path, atim, mtim))
371b7a77849SAndy Fiddaman 			ret = false;
372b7a77849SAndy Fiddaman 
373b7a77849SAndy Fiddaman 		/* repeat against symbolic link path */
374b7a77849SAndy Fiddaman 		if (utimensat(rfd, lpath, ts, 0) == -1)
375b7a77849SAndy Fiddaman 			err(EXIT_FAILURE, "utimensat(dnfd, link %s)", lpath);
376b7a77849SAndy Fiddaman 		if (!compare_filetime(path, false, atim, mtim, false)) {
377b7a77849SAndy Fiddaman 			warnx("failed against link with random descriptor, "
378b7a77849SAndy Fiddaman 			    "fq path");
379b7a77849SAndy Fiddaman 			ret = false;
380b7a77849SAndy Fiddaman 		}
381b7a77849SAndy Fiddaman 
382b7a77849SAndy Fiddaman 		(void) close(rfd);
383b7a77849SAndy Fiddaman 
384b7a77849SAndy Fiddaman 		if (!reset(path, atim, mtim))
385b7a77849SAndy Fiddaman 			ret = false;
386b7a77849SAndy Fiddaman 
387b7a77849SAndy Fiddaman 		/* relative to a directory */
388b7a77849SAndy Fiddaman 		if (utimensat(dfd, "file", ts, 0) == -1)
389*6353250fSRobert Mustacchi 			err(EXIT_FAILURE, "utimensat(dir, file)");
390b7a77849SAndy Fiddaman 		if (!compare_filetime(path, false, atim, mtim, false)) {
391b7a77849SAndy Fiddaman 			warnx("failed relative to a directory");
392b7a77849SAndy Fiddaman 			ret = false;
393b7a77849SAndy Fiddaman 		}
394b7a77849SAndy Fiddaman 
395b7a77849SAndy Fiddaman 		if (!reset(path, atim, mtim))
396b7a77849SAndy Fiddaman 			ret = false;
397b7a77849SAndy Fiddaman 
398b7a77849SAndy Fiddaman 		/* repeat against symbolic link path */
399b7a77849SAndy Fiddaman 		if (utimensat(dfd, "link", ts, 0) == -1)
400*6353250fSRobert Mustacchi 			err(EXIT_FAILURE, "utimensat(dir, link)");
401b7a77849SAndy Fiddaman 		if (!compare_filetime(path, false, atim, mtim, false)) {
402b7a77849SAndy Fiddaman 			warnx("failed through link relative to a directory");
403b7a77849SAndy Fiddaman 			ret = false;
404b7a77849SAndy Fiddaman 		}
405b7a77849SAndy Fiddaman 
406b7a77849SAndy Fiddaman 		if (!reset(path, atim, mtim))
407b7a77849SAndy Fiddaman 			ret = false;
408b7a77849SAndy Fiddaman 
409b7a77849SAndy Fiddaman 		/* AT_FDCWD */
410b7a77849SAndy Fiddaman 		if (fchdir(dfd) == -1)
411b7a77849SAndy Fiddaman 			err(EXIT_FAILURE, "fchdir(%s)", dir);
412b7a77849SAndy Fiddaman 		if (utimensat(AT_FDCWD, "file", ts, 0) == -1)
413b7a77849SAndy Fiddaman 			err(EXIT_FAILURE, "utimensat(AT_FDCWD, file)");
414b7a77849SAndy Fiddaman 		if (!compare_filetime(path, false, atim, mtim, false)) {
415b7a77849SAndy Fiddaman 			warnx("failed with AT_FDCWD relative");
416b7a77849SAndy Fiddaman 			ret = false;
417b7a77849SAndy Fiddaman 		}
418b7a77849SAndy Fiddaman 
419b7a77849SAndy Fiddaman 		if (!reset(path, atim, mtim))
420b7a77849SAndy Fiddaman 			ret = false;
421b7a77849SAndy Fiddaman 
422b7a77849SAndy Fiddaman 		/* repeat against symbolic link path */
423b7a77849SAndy Fiddaman 		if (utimensat(AT_FDCWD, "link", ts, 0) == -1)
424b7a77849SAndy Fiddaman 			err(EXIT_FAILURE, "utimensat(AT_FDCWD, link)");
425b7a77849SAndy Fiddaman 		if (!compare_filetime(path, false, atim, mtim, false)) {
426b7a77849SAndy Fiddaman 			warnx("failed through link with AT_FDCWD relative");
427b7a77849SAndy Fiddaman 			ret = false;
428b7a77849SAndy Fiddaman 		}
429b7a77849SAndy Fiddaman 
430b7a77849SAndy Fiddaman 		if (!reset(path, atim, mtim))
431b7a77849SAndy Fiddaman 			ret = false;
432b7a77849SAndy Fiddaman 
433b7a77849SAndy Fiddaman 		/*
434b7a77849SAndy Fiddaman 		 * Check that none of the above operations have changed the
435b7a77849SAndy Fiddaman 		 * timestamp on the link.
436b7a77849SAndy Fiddaman 		 */
437b7a77849SAndy Fiddaman 		if (compare_linktime(lpath, true, atim, mtim, true)) {
438b7a77849SAndy Fiddaman 			warnx("link timestamp was unexpectedly modified");
439b7a77849SAndy Fiddaman 			ret = false;
440b7a77849SAndy Fiddaman 		}
441b7a77849SAndy Fiddaman 
442b7a77849SAndy Fiddaman 		/* Set the time on the link, not on the target */
443b7a77849SAndy Fiddaman 		if (utimensat(AT_FDCWD, "link", ts, AT_SYMLINK_NOFOLLOW) == -1)
444b7a77849SAndy Fiddaman 			err(EXIT_FAILURE, "utimensat(AT_FDCWD, lflag)");
445b7a77849SAndy Fiddaman 		if (!compare_linktime(lpath, false, atim, mtim, false)) {
446b7a77849SAndy Fiddaman 			warnx("link time is incorrect");
447b7a77849SAndy Fiddaman 			ret = false;
448b7a77849SAndy Fiddaman 		}
449b7a77849SAndy Fiddaman 		if (compare_filetime(path, false, atim, mtim, true)) {
450b7a77849SAndy Fiddaman 			warnx("target time was updated incorrectly");
451b7a77849SAndy Fiddaman 			ret = false;
452b7a77849SAndy Fiddaman 		}
453b7a77849SAndy Fiddaman 		}
454b7a77849SAndy Fiddaman 		break;
455b7a77849SAndy Fiddaman 	}
456b7a77849SAndy Fiddaman 
457b7a77849SAndy Fiddaman 	(void) close(dfd);
458b7a77849SAndy Fiddaman 	(void) close(lfd);
459b7a77849SAndy Fiddaman 	(void) close(fd);
460b7a77849SAndy Fiddaman 
461b7a77849SAndy Fiddaman 	if (unlink(lpath) == -1)
462b7a77849SAndy Fiddaman 		err(EXIT_FAILURE, "unlink(%s)", lpath);
463b7a77849SAndy Fiddaman 	if (unlink(path) == -1)
464b7a77849SAndy Fiddaman 		err(EXIT_FAILURE, "unlink(%s)", path);
465b7a77849SAndy Fiddaman 
466b7a77849SAndy Fiddaman 	if (!ret)
467b7a77849SAndy Fiddaman 		warnx("Test failed");
468b7a77849SAndy Fiddaman 
469b7a77849SAndy Fiddaman 	return (ret);
470b7a77849SAndy Fiddaman }
471b7a77849SAndy Fiddaman 
472b7a77849SAndy Fiddaman static bool
runtests(char * dir,timespec_t * atim,timespec_t * mtim)473b7a77849SAndy Fiddaman runtests(char *dir, timespec_t *atim, timespec_t *mtim)
474b7a77849SAndy Fiddaman {
475b7a77849SAndy Fiddaman 	bool ret = true;
476b7a77849SAndy Fiddaman 
477b7a77849SAndy Fiddaman 	printf("Testing:\n... atime: %ld.%.9ld\n... mtime: %ld.%.9ld\n",
478b7a77849SAndy Fiddaman 	    atim->tv_sec, atim->tv_nsec, mtim->tv_sec, mtim->tv_nsec);
479b7a77849SAndy Fiddaman 
480b7a77849SAndy Fiddaman 	if (!runtest(UTIMES, dir, atim, mtim))
481b7a77849SAndy Fiddaman 		ret = false;
482b7a77849SAndy Fiddaman 	if (!runtest(LUTIMES, dir, atim, mtim))
483b7a77849SAndy Fiddaman 		ret = false;
484b7a77849SAndy Fiddaman 	if (!runtest(FUTIMES, dir, atim, mtim))
485b7a77849SAndy Fiddaman 		ret = false;
486b7a77849SAndy Fiddaman 	if (!runtest(FUTIMESAT, dir, atim, mtim))
487b7a77849SAndy Fiddaman 		ret = false;
488b7a77849SAndy Fiddaman 	if (!runtest(FUTIMENS, dir, atim, mtim))
489b7a77849SAndy Fiddaman 		ret = false;
490b7a77849SAndy Fiddaman 	if (!runtest(UTIMENSAT, dir, atim, mtim))
491b7a77849SAndy Fiddaman 		ret = false;
492b7a77849SAndy Fiddaman 
493b7a77849SAndy Fiddaman 	return (ret);
494b7a77849SAndy Fiddaman }
495b7a77849SAndy Fiddaman 
496b7a77849SAndy Fiddaman int
main(void)497b7a77849SAndy Fiddaman main(void)
498b7a77849SAndy Fiddaman {
499b7a77849SAndy Fiddaman 	char dir[] = "/tmp/utimes.XXXXXX";
500b7a77849SAndy Fiddaman 	int ret = EXIT_SUCCESS;
501b7a77849SAndy Fiddaman 	int i;
502b7a77849SAndy Fiddaman 
503b7a77849SAndy Fiddaman 	if (mkdtemp(dir) == NULL)
504b7a77849SAndy Fiddaman 		err(EXIT_FAILURE, "failed to create temporary directory");
505b7a77849SAndy Fiddaman 
506b7a77849SAndy Fiddaman 	for (i = 0; i < ARRAY_SIZE(testtimes); i += 2) {
507b7a77849SAndy Fiddaman 		if (!runtests(dir, &testtimes[i], &testtimes[i + 1]))
508b7a77849SAndy Fiddaman 			ret = EXIT_FAILURE;
509b7a77849SAndy Fiddaman 	}
510b7a77849SAndy Fiddaman 
511b7a77849SAndy Fiddaman 	/*
512b7a77849SAndy Fiddaman 	 * Some tests will have changed directory into 'dir' to test the
513b7a77849SAndy Fiddaman 	 * AT_FDCWD case. Change back to / to avoid EBUSY when removing dir.
514b7a77849SAndy Fiddaman 	 */
515b7a77849SAndy Fiddaman 	if (chdir("/") == -1)
516b7a77849SAndy Fiddaman 		err(EXIT_FAILURE, "chdir(/)");
517b7a77849SAndy Fiddaman 	if (rmdir(dir) == -1)
518b7a77849SAndy Fiddaman 		err(EXIT_FAILURE, "rmdir %s", dir);
519b7a77849SAndy Fiddaman 
520b7a77849SAndy Fiddaman 	return (ret);
521b7a77849SAndy Fiddaman }
522