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