1 // SPDX-License-Identifier: CDDL-1.0
2 /*
3 * CDDL HEADER START
4 *
5 * The contents of this file are subject to the terms of the
6 * Common Development and Distribution License (the "License").
7 * You may not use this file except in compliance with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or https://opensource.org/licenses/CDDL-1.0.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22
23 /*
24 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
25 * Use is subject to license terms.
26 */
27
28 /*
29 * Copyright (c) 2013 by Delphix. All rights reserved.
30 */
31
32
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #ifndef __FreeBSD__
36 #include <sys/xattr.h>
37 #endif
38 #include <utime.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <unistd.h>
42 #include <errno.h>
43 #include <fcntl.h>
44 #include <libgen.h>
45 #include <string.h>
46
47 #define ST_ATIME 0
48 #define ST_CTIME 1
49 #define ST_MTIME 2
50
51 #define ALL_MODE (mode_t)(S_IRWXU|S_IRWXG|S_IRWXO)
52
53 typedef struct timetest {
54 int type;
55 const char *name;
56 int (*func)(const char *pfile);
57 } timetest_t;
58
59 static char tfile[BUFSIZ] = { 0 };
60
61 /*
62 * DESCRIPTION:
63 * Verify time will be changed correctly after each operation.
64 *
65 * STRATEGY:
66 * 1. Define time test array.
67 * 2. Loop through each item in this array.
68 * 3. Verify the time is changed after each operation.
69 *
70 */
71
72 static int
get_file_time(const char * pfile,int what,time_t * ptr)73 get_file_time(const char *pfile, int what, time_t *ptr)
74 {
75 struct stat stat_buf;
76
77 if (pfile == NULL || ptr == NULL) {
78 return (-1);
79 }
80
81 if (stat(pfile, &stat_buf) == -1) {
82 return (-1);
83 }
84
85 switch (what) {
86 case ST_ATIME:
87 *ptr = stat_buf.st_atime;
88 return (0);
89 case ST_CTIME:
90 *ptr = stat_buf.st_ctime;
91 return (0);
92 case ST_MTIME:
93 *ptr = stat_buf.st_mtime;
94 return (0);
95 default:
96 return (-1);
97 }
98 }
99
100 static ssize_t
get_dirnamelen(const char * path)101 get_dirnamelen(const char *path)
102 {
103 const char *end = strrchr(path, '/');
104 return (end ? end - path : -1);
105 }
106
107 static int
do_read(const char * pfile)108 do_read(const char *pfile)
109 {
110 int fd, ret = 0;
111 char buf[BUFSIZ] = { 0 };
112
113 if (pfile == NULL) {
114 return (-1);
115 }
116
117 if ((fd = open(pfile, O_RDONLY, ALL_MODE)) == -1) {
118 return (-1);
119 }
120 if (read(fd, buf, sizeof (buf)) == -1) {
121 (void) fprintf(stderr, "read(%d, buf, %zd) failed with errno "
122 "%d\n", fd, sizeof (buf), errno);
123 (void) close(fd);
124 return (1);
125 }
126 (void) close(fd);
127
128 return (ret);
129 }
130
131 static int
do_write(const char * pfile)132 do_write(const char *pfile)
133 {
134 int fd, ret = 0;
135 char buf[BUFSIZ] = "call function do_write()";
136
137 if (pfile == NULL) {
138 return (-1);
139 }
140
141 if ((fd = open(pfile, O_WRONLY, ALL_MODE)) == -1) {
142 return (-1);
143 }
144 if (write(fd, buf, strlen(buf)) == -1) {
145 (void) fprintf(stderr, "write(%d, buf, %d) failed with errno "
146 "%d\n", fd, (int)strlen(buf), errno);
147 (void) close(fd);
148 return (1);
149 }
150 (void) close(fd);
151
152 return (ret);
153 }
154
155 static int
do_link(const char * pfile)156 do_link(const char *pfile)
157 {
158 int ret = 0;
159 char link_file[BUFSIZ + 16] = { 0 };
160
161 if (pfile == NULL) {
162 return (-1);
163 }
164
165 /*
166 * Figure out source file directory name, and create
167 * the link file in the same directory.
168 */
169 (void) snprintf(link_file, sizeof (link_file),
170 "%.*s/%s", (int)get_dirnamelen(pfile), pfile, "link_file");
171
172 if (link(pfile, link_file) == -1) {
173 (void) fprintf(stderr, "link(%s, %s) failed with errno %d\n",
174 pfile, link_file, errno);
175 return (1);
176 }
177
178 (void) unlink(link_file);
179
180 return (ret);
181 }
182
183 static int
do_creat(const char * pfile)184 do_creat(const char *pfile)
185 {
186 int fd, ret = 0;
187
188 if (pfile == NULL) {
189 return (-1);
190 }
191
192 if ((fd = creat(pfile, ALL_MODE)) == -1) {
193 (void) fprintf(stderr, "creat(%s, ALL_MODE) failed with errno "
194 "%d\n", pfile, errno);
195 return (1);
196 }
197 (void) close(fd);
198
199 return (ret);
200 }
201
202 static int
do_utime(const char * pfile)203 do_utime(const char *pfile)
204 {
205 int ret = 0;
206
207 if (pfile == NULL) {
208 return (-1);
209 }
210
211 /*
212 * Times of the file are set to the current time
213 */
214 if (utime(pfile, NULL) == -1) {
215 (void) fprintf(stderr, "utime(%s, NULL) failed with errno "
216 "%d\n", pfile, errno);
217 return (1);
218 }
219
220 return (ret);
221 }
222
223 static int
do_chmod(const char * pfile)224 do_chmod(const char *pfile)
225 {
226 int ret = 0;
227
228 if (pfile == NULL) {
229 return (-1);
230 }
231
232 if (chmod(pfile, ALL_MODE) == -1) {
233 (void) fprintf(stderr, "chmod(%s, ALL_MODE) failed with "
234 "errno %d\n", pfile, errno);
235 return (1);
236 }
237
238 return (ret);
239 }
240
241 static int
do_chown(const char * pfile)242 do_chown(const char *pfile)
243 {
244 int ret = 0;
245
246 if (pfile == NULL) {
247 return (-1);
248 }
249
250 if (chown(pfile, getuid(), getgid()) == -1) {
251 (void) fprintf(stderr, "chown(%s, %d, %d) failed with errno "
252 "%d\n", pfile, (int)getuid(), (int)getgid(), errno);
253 return (1);
254 }
255
256 return (ret);
257 }
258
259 #ifndef __FreeBSD__
260 static int
do_xattr(const char * pfile)261 do_xattr(const char *pfile)
262 {
263 int ret = 0;
264 const char *value = "user.value";
265
266 if (pfile == NULL) {
267 return (-1);
268 }
269
270 if (setxattr(pfile, "user.x", value, strlen(value), 0) == -1) {
271 (void) fprintf(stderr, "setxattr(%s, %d, %d) failed with errno "
272 "%d\n", pfile, (int)getuid(), (int)getgid(), errno);
273 return (1);
274 }
275 return (ret);
276 }
277 #endif
278
279 static void
cleanup(void)280 cleanup(void)
281 {
282 if ((strlen(tfile) != 0) && (access(tfile, F_OK) == 0)) {
283 (void) unlink(tfile);
284 }
285 }
286
287 static timetest_t timetest_table[] = {
288 { ST_ATIME, "st_atime", do_read },
289 { ST_ATIME, "st_atime", do_utime },
290 { ST_MTIME, "st_mtime", do_creat },
291 { ST_MTIME, "st_mtime", do_write },
292 { ST_MTIME, "st_mtime", do_utime },
293 { ST_CTIME, "st_ctime", do_creat },
294 { ST_CTIME, "st_ctime", do_write },
295 { ST_CTIME, "st_ctime", do_chmod },
296 { ST_CTIME, "st_ctime", do_chown },
297 { ST_CTIME, "st_ctime", do_link },
298 { ST_CTIME, "st_ctime", do_utime },
299 #ifndef __FreeBSD__
300 { ST_CTIME, "st_ctime", do_xattr },
301 #endif
302 };
303
304 #define NCOMMAND (sizeof (timetest_table) / sizeof (timetest_table[0]))
305
306 int
main(void)307 main(void)
308 {
309 int i, ret, fd;
310 const char *penv[] = {"TESTDIR", "TESTFILE0"};
311
312 (void) atexit(cleanup);
313
314 /*
315 * Get the environment variable values.
316 */
317 for (i = 0; i < sizeof (penv) / sizeof (char *); i++) {
318 if ((penv[i] = getenv(penv[i])) == NULL) {
319 (void) fprintf(stderr, "getenv(penv[%d])\n", i);
320 return (1);
321 }
322 }
323 (void) snprintf(tfile, sizeof (tfile), "%s/%s", penv[0], penv[1]);
324
325 /*
326 * If the test file exists, remove it first.
327 */
328 if (access(tfile, F_OK) == 0) {
329 (void) unlink(tfile);
330 }
331 if ((fd = open(tfile, O_WRONLY | O_CREAT | O_TRUNC, ALL_MODE)) == -1) {
332 (void) fprintf(stderr, "open(%s) failed: %d\n", tfile, errno);
333 return (1);
334 }
335 (void) close(fd);
336
337 for (i = 0; i < NCOMMAND; i++) {
338 time_t t1, t2;
339
340 /*
341 * Get original time before operating.
342 */
343 ret = get_file_time(tfile, timetest_table[i].type, &t1);
344 if (ret != 0) {
345 (void) fprintf(stderr, "get_file_time(%s %d) = %d\n",
346 tfile, timetest_table[i].type, ret);
347 return (1);
348 }
349
350 /*
351 * Sleep 2 seconds, then invoke command on given file
352 */
353 (void) sleep(2);
354 timetest_table[i].func(tfile);
355
356 /*
357 * Get time after operating.
358 */
359 ret = get_file_time(tfile, timetest_table[i].type, &t2);
360 if (ret != 0) {
361 (void) fprintf(stderr, "get_file_time(%s %d) = %d\n",
362 tfile, timetest_table[i].type, ret);
363 return (1);
364 }
365
366
367 /*
368 * Ideally, time change would be exactly two seconds, but allow
369 * a little slack in case of scheduling delays or similar.
370 */
371 long delta = (long)t2 - (long)t1;
372 if (delta < 2 || delta > 4) {
373 (void) fprintf(stderr,
374 "%s: BAD time change: t1(%ld), t2(%ld)\n",
375 timetest_table[i].name, (long)t1, (long)t2);
376 return (1);
377 } else {
378 (void) fprintf(stderr,
379 "%s: good time change: t1(%ld), t2(%ld)\n",
380 timetest_table[i].name, (long)t1, (long)t2);
381 }
382 }
383
384 return (0);
385 }
386