xref: /illumos-gate/usr/src/test/os-tests/tests/fifo-tvnsec.c (revision e6c4e893c6a9849a441fddbb31ea45dc6814ec6b)
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.
6  *
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.
10  */
11 
12 /*
13  * Copyright 2024 Oxide Computer Company
14  */
15 
16 /*
17  * Verify that FIFOs now track tv_nsec in addition to tv_sec. This is important
18  * for both named pipes in the file system created with mknod(2) and anonymous
19  * pipes created with pipe(2). As part of this, we go through a series of
20  * operations on a pipe:
21  *
22  * 1) Creating it
23  * 2) Writing and reading from it to verify the mtime / atime are increasing.
24  * 3) Explicitly setting the time on it.
25  *
26  * At each point, these should advance and we should be bracketed by calls to
27  * getting the real time clock.
28  */
29 
30 #include <err.h>
31 #include <unistd.h>
32 #include <sys/stat.h>
33 #include <sys/types.h>
34 #include <sys/debug.h>
35 #include <stdlib.h>
36 #include <stdbool.h>
37 #include <inttypes.h>
38 #include <fcntl.h>
39 
40 typedef enum {
41 	CHK_ATIME_GT	 = 1 << 0,
42 	CHK_MTIME_GT	 = 1 << 1,
43 	CHK_CTIME_GT	 = 1 << 2,
44 	CHK_ATIME_LT	 = 1 << 3,
45 	CHK_MTIME_LT	 = 1 << 4,
46 	CHK_CTIME_LT	 = 1 << 5
47 } check_time_t;
48 
49 #define	CHK_ALL_GT	(CHK_ATIME_GT | CHK_MTIME_GT | CHK_CTIME_GT)
50 #define	CHK_ALL_LT	(CHK_ATIME_LT | CHK_MTIME_LT | CHK_CTIME_LT)
51 
52 static struct timespec now;
53 static const char *curtype;
54 
55 static void
56 update_time(void)
57 {
58 	VERIFY0(clock_gettime(CLOCK_REALTIME, &now));
59 }
60 
61 static bool
62 time_gt(const struct timespec *check, const struct timespec *base)
63 {
64 	if (check->tv_sec > base->tv_sec) {
65 		return (true);
66 	}
67 
68 	return (check->tv_sec == base->tv_sec &&
69 	    check->tv_nsec > base->tv_nsec);
70 }
71 
72 static bool
73 check_times(const struct stat *st, check_time_t chk, const char *side,
74     const char *desc)
75 {
76 	bool ret = true;
77 
78 	if (((chk & CHK_ATIME_GT) != 0) && !time_gt(&st->st_atim, &now)) {
79 		warnx("TEST FAILED: %s %s side %s atime is in the past!",
80 		    curtype, side, desc);
81 		ret = false;
82 	}
83 
84 	if (((chk & CHK_MTIME_GT) != 0) && !time_gt(&st->st_mtim, &now)) {
85 		warnx("TEST FAILED: %s %s side %s mtime is in the past!",
86 		    curtype, side, desc);
87 		ret = false;
88 	}
89 
90 	if (((chk & CHK_CTIME_GT) != 0) && !time_gt(&st->st_ctim, &now)) {
91 		warnx("TEST FAILED: %s %s side %s ctime is in the past!",
92 		    curtype, side, desc);
93 		ret = false;
94 	}
95 
96 	if (((chk & CHK_ATIME_LT) != 0) && !time_gt(&now, &st->st_atim)) {
97 		warnx("TEST FAILED: %s %s side %s atime erroneously advanced!",
98 		    curtype, side, desc);
99 		ret = false;
100 	}
101 
102 	if (((chk & CHK_MTIME_LT) != 0) && !time_gt(&now, &st->st_mtim)) {
103 		warnx("TEST FAILED: %s %s side %s mtime erroneously advanced!",
104 		    curtype, side, desc);
105 		ret = false;
106 	}
107 
108 	if (((chk & CHK_CTIME_LT) != 0) && !time_gt(&now, &st->st_ctim)) {
109 		warnx("TEST FAILED: %s %s side %s ctime erroneously advanced!",
110 		    curtype, side, desc);
111 		ret = false;
112 	}
113 
114 
115 	return (ret);
116 }
117 
118 static bool
119 check_fifos(int wfd, int rfd)
120 {
121 	bool ret = true;
122 	struct stat st;
123 	const uint32_t val = 0x7777;
124 	uint32_t data;
125 	struct timespec update[2];
126 
127 	VERIFY0(fstat(wfd, &st));
128 	if (!check_times(&st, CHK_ALL_GT, "write", "creation")) {
129 		ret = false;
130 	}
131 
132 	VERIFY0(fstat(rfd, &st));
133 	if (!check_times(&st, CHK_ALL_GT, "read", "creation")) {
134 		ret = false;
135 	}
136 
137 	/*
138 	 * Now that we have made progress, write to the write side and confirm
139 	 * that the mtime / ctime have advanced. The read side should also have
140 	 * had the mtime / ctime advance.
141 	 */
142 	update_time();
143 	if (write(wfd, &val, sizeof (val)) != sizeof (val)) {
144 		errx(EXIT_FAILURE, "failed to write value to %s write side",
145 		    curtype);
146 	}
147 
148 	VERIFY0(fstat(wfd, &st));
149 	if (!check_times(&st, CHK_CTIME_GT | CHK_MTIME_GT | CHK_ATIME_LT,
150 	    "write", "post-write")) {
151 		ret = false;
152 	}
153 
154 	VERIFY0(fstat(rfd, &st));
155 	if (!check_times(&st, CHK_CTIME_GT | CHK_MTIME_GT | CHK_ATIME_LT,
156 	    "read", "post-write")) {
157 		ret = false;
158 	}
159 
160 	update_time();
161 	if (read(rfd, &data, sizeof (data)) != sizeof (data)) {
162 		errx(EXIT_FAILURE, "failed to read data from %s read side",
163 		    curtype);
164 	}
165 
166 	VERIFY0(fstat(rfd, &st));
167 	if (!check_times(&st, CHK_CTIME_LT | CHK_MTIME_LT | CHK_ATIME_GT,
168 	    "read", "post-read")) {
169 		ret = false;
170 	}
171 
172 	VERIFY0(fstat(wfd, &st));
173 	if (!check_times(&st, CHK_CTIME_LT | CHK_MTIME_LT | CHK_ATIME_GT,
174 	    "write", "post-read")) {
175 		ret = false;
176 	}
177 
178 	update_time();
179 	update[0] = now;
180 	update[1] = now;
181 	VERIFY0(futimens(wfd, update));
182 
183 	update_time();
184 	VERIFY0(fstat(wfd, &st));
185 	if (!check_times(&st, CHK_ALL_LT, "write", "post-futimens")) {
186 		ret = false;
187 	}
188 
189 	VERIFY0(fstat(rfd, &st));
190 	if (!check_times(&st, CHK_ALL_LT, "read", "post-futimens")) {
191 		ret = false;
192 	}
193 
194 	return (ret);
195 }
196 
197 int
198 main(void)
199 {
200 	int ret = EXIT_SUCCESS;
201 	int pipes[2];
202 	char path[1024];
203 
204 	update_time();
205 	if (pipe(pipes) != 0) {
206 		err(EXIT_FAILURE, "failed to create pipes");
207 	}
208 
209 	curtype = "pipe(2)";
210 	if (!check_fifos(pipes[0], pipes[1])) {
211 		ret = EXIT_FAILURE;
212 	}
213 
214 	VERIFY0(close(pipes[0]));
215 	VERIFY0(close(pipes[1]));
216 
217 	(void) snprintf(path, sizeof (path), "/tmp/fifo-tvnsec.%" _PRIdID
218 	    ".fifo", getpid());
219 	if (mkfifo(path, 0666) != 0) {
220 		err(EXIT_FAILURE, "failed to create fifo");
221 	}
222 
223 	/*
224 	 * We have to open the read side before the write side and must make
225 	 * sure that we use a non-blocking open because this is all coming from
226 	 * the same process.
227 	 */
228 	pipes[1] = open(path, O_RDONLY | O_NONBLOCK);
229 	if (pipes[1] < 0) {
230 		err(EXIT_FAILURE, "failed to open %s read-only", path);
231 	}
232 
233 	pipes[0] = open(path, O_WRONLY | O_NONBLOCK);
234 	if (pipes[0] < 0) {
235 		err(EXIT_FAILURE, "failed to open %s write-only", path);
236 	}
237 
238 	curtype = "mkfifo(3C)";
239 	if (!check_fifos(pipes[0], pipes[1])) {
240 		ret = EXIT_FAILURE;
241 	}
242 	(void) unlink(path);
243 
244 	if (ret == EXIT_SUCCESS) {
245 		(void) printf("All tests completed successfully\n");
246 	}
247 
248 	return (ret);
249 }
250