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 http://www.opensolaris.org/os/licensing.
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 <strings.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 	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 int
do_read(const char * pfile)101 do_read(const char *pfile)
102 {
103 	int fd, ret = 0;
104 	char buf[BUFSIZ] = { 0 };
105 
106 	if (pfile == NULL) {
107 		return (-1);
108 	}
109 
110 	if ((fd = open(pfile, O_RDONLY, ALL_MODE)) == -1) {
111 		return (-1);
112 	}
113 	if (read(fd, buf, sizeof (buf)) == -1) {
114 		(void) fprintf(stderr, "read(%d, buf, %zd) failed with errno "
115 		    "%d\n", fd, sizeof (buf), errno);
116 		(void) close(fd);
117 		return (1);
118 	}
119 	(void) close(fd);
120 
121 	return (ret);
122 }
123 
124 static int
do_write(const char * pfile)125 do_write(const char *pfile)
126 {
127 	int fd, ret = 0;
128 	char buf[BUFSIZ] = "call function do_write()";
129 
130 	if (pfile == NULL) {
131 		return (-1);
132 	}
133 
134 	if ((fd = open(pfile, O_WRONLY, ALL_MODE)) == -1) {
135 		return (-1);
136 	}
137 	if (write(fd, buf, strlen(buf)) == -1) {
138 		(void) fprintf(stderr, "write(%d, buf, %d) failed with errno "
139 		    "%d\n", fd, (int)strlen(buf), errno);
140 		(void) close(fd);
141 		return (1);
142 	}
143 	(void) close(fd);
144 
145 	return (ret);
146 }
147 
148 static int
do_link(const char * pfile)149 do_link(const char *pfile)
150 {
151 	int ret = 0;
152 	char link_file[BUFSIZ] = { 0 };
153 	char pfile_copy[BUFSIZ] = { 0 };
154 	char *dname;
155 
156 	if (pfile == NULL) {
157 		return (-1);
158 	}
159 
160 	strncpy(pfile_copy, pfile, sizeof (pfile_copy)-1);
161 	pfile_copy[sizeof (pfile_copy) - 1] = '\0';
162 	/*
163 	 * Figure out source file directory name, and create
164 	 * the link file in the same directory.
165 	 */
166 	dname = dirname((char *)pfile_copy);
167 	(void) snprintf(link_file, BUFSIZ, "%s/%s", dname, "link_file");
168 
169 	if (link(pfile, link_file) == -1) {
170 		(void) fprintf(stderr, "link(%s, %s) failed with errno %d\n",
171 		    pfile, link_file, errno);
172 		return (1);
173 	}
174 
175 	(void) unlink(link_file);
176 
177 	return (ret);
178 }
179 
180 static int
do_creat(const char * pfile)181 do_creat(const char *pfile)
182 {
183 	int fd, ret = 0;
184 
185 	if (pfile == NULL) {
186 		return (-1);
187 	}
188 
189 	if ((fd = creat(pfile, ALL_MODE)) == -1) {
190 		(void) fprintf(stderr, "creat(%s, ALL_MODE) failed with errno "
191 		    "%d\n", pfile, errno);
192 		return (1);
193 	}
194 	(void) close(fd);
195 
196 	return (ret);
197 }
198 
199 static int
do_utime(const char * pfile)200 do_utime(const char *pfile)
201 {
202 	int ret = 0;
203 
204 	if (pfile == NULL) {
205 		return (-1);
206 	}
207 
208 	/*
209 	 * Times of the file are set to the current time
210 	 */
211 	if (utime(pfile, NULL) == -1) {
212 		(void) fprintf(stderr, "utime(%s, NULL) failed with errno "
213 		    "%d\n", pfile, errno);
214 		return (1);
215 	}
216 
217 	return (ret);
218 }
219 
220 static int
do_chmod(const char * pfile)221 do_chmod(const char *pfile)
222 {
223 	int ret = 0;
224 
225 	if (pfile == NULL) {
226 		return (-1);
227 	}
228 
229 	if (chmod(pfile, ALL_MODE) == -1) {
230 		(void) fprintf(stderr, "chmod(%s, ALL_MODE) failed with "
231 		    "errno %d\n", pfile, errno);
232 		return (1);
233 	}
234 
235 	return (ret);
236 }
237 
238 static int
do_chown(const char * pfile)239 do_chown(const char *pfile)
240 {
241 	int ret = 0;
242 
243 	if (pfile == NULL) {
244 		return (-1);
245 	}
246 
247 	if (chown(pfile, getuid(), getgid()) == -1) {
248 		(void) fprintf(stderr, "chown(%s, %d, %d) failed with errno "
249 		    "%d\n", pfile, (int)getuid(), (int)getgid(), errno);
250 		return (1);
251 	}
252 
253 	return (ret);
254 }
255 
256 #ifndef __FreeBSD__
257 static int
do_xattr(const char * pfile)258 do_xattr(const char *pfile)
259 {
260 	int ret = 0;
261 	char *value = "user.value";
262 
263 	if (pfile == NULL) {
264 		return (-1);
265 	}
266 
267 	if (setxattr(pfile, "user.x", value, strlen(value), 0) == -1) {
268 		(void) fprintf(stderr, "setxattr(%s, %d, %d) failed with errno "
269 		    "%d\n", pfile, (int)getuid(), (int)getgid(), errno);
270 		return (1);
271 	}
272 	return (ret);
273 }
274 #endif
275 
276 static void
cleanup(void)277 cleanup(void)
278 {
279 	if ((strlen(tfile) != 0) && (access(tfile, F_OK) == 0)) {
280 		(void) unlink(tfile);
281 	}
282 }
283 
284 static timetest_t timetest_table[] = {
285 	{ ST_ATIME,	"st_atime",	do_read		},
286 	{ ST_ATIME,	"st_atime",	do_utime	},
287 	{ ST_MTIME,	"st_mtime",	do_creat	},
288 	{ ST_MTIME,	"st_mtime",	do_write	},
289 	{ ST_MTIME,	"st_mtime",	do_utime	},
290 	{ ST_CTIME,	"st_ctime",	do_creat	},
291 	{ ST_CTIME,	"st_ctime",	do_write	},
292 	{ ST_CTIME,	"st_ctime",	do_chmod	},
293 	{ ST_CTIME,	"st_ctime",	do_chown 	},
294 	{ ST_CTIME,	"st_ctime",	do_link		},
295 	{ ST_CTIME,	"st_ctime",	do_utime	},
296 #ifndef __FreeBSD__
297 	{ ST_CTIME,	"st_ctime",	do_xattr	},
298 #endif
299 };
300 
301 #define	NCOMMAND (sizeof (timetest_table) / sizeof (timetest_table[0]))
302 
303 /* ARGSUSED */
304 int
main(int argc,char * argv[])305 main(int argc, char *argv[])
306 {
307 	int i, ret, fd;
308 	char *penv[] = {"TESTDIR", "TESTFILE0"};
309 
310 	(void) atexit(cleanup);
311 
312 	/*
313 	 * Get the environment variable values.
314 	 */
315 	for (i = 0; i < sizeof (penv) / sizeof (char *); i++) {
316 		if ((penv[i] = getenv(penv[i])) == NULL) {
317 			(void) fprintf(stderr, "getenv(penv[%d])\n", i);
318 			return (1);
319 		}
320 	}
321 	(void) snprintf(tfile, sizeof (tfile), "%s/%s", penv[0], penv[1]);
322 
323 	/*
324 	 * If the test file is exists, remove it first.
325 	 */
326 	if (access(tfile, F_OK) == 0) {
327 		(void) unlink(tfile);
328 	}
329 	ret = 0;
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 		if (t1 == t2) {
366 			(void) fprintf(stderr, "%s: t1(%ld) == t2(%ld)\n",
367 			    timetest_table[i].name, (long)t1, (long)t2);
368 			return (1);
369 		} else {
370 			(void) fprintf(stderr, "%s: t1(%ld) != t2(%ld)\n",
371 			    timetest_table[i].name, (long)t1, (long)t2);
372 		}
373 	}
374 
375 	return (0);
376 }
377