]>
Commit | Line | Data |
---|---|---|
6bb24f4d BB |
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 | |
1d3ba0bf | 9 | * or https://opensource.org/licenses/CDDL-1.0. |
6bb24f4d BB |
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> | |
7839c4b5 | 34 | #ifndef __FreeBSD__ |
0107f698 | 35 | #include <sys/xattr.h> |
7839c4b5 | 36 | #endif |
6bb24f4d BB |
37 | #include <utime.h> |
38 | #include <stdio.h> | |
39 | #include <stdlib.h> | |
40 | #include <unistd.h> | |
6bb24f4d BB |
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; | |
a926aab9 | 54 | const char *name; |
6bb24f4d BB |
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 | |
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 | ||
514498fe BB |
99 | static ssize_t |
100 | get_dirnamelen(const char *path) | |
101 | { | |
102 | const char *end = strrchr(path, '/'); | |
103 | return (end ? end - path : -1); | |
104 | } | |
105 | ||
6bb24f4d BB |
106 | static int |
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); | |
884385a0 | 122 | (void) close(fd); |
6bb24f4d BB |
123 | return (1); |
124 | } | |
125 | (void) close(fd); | |
126 | ||
127 | return (ret); | |
128 | } | |
129 | ||
130 | static int | |
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); | |
884385a0 | 146 | (void) close(fd); |
6bb24f4d BB |
147 | return (1); |
148 | } | |
149 | (void) close(fd); | |
150 | ||
151 | return (ret); | |
152 | } | |
153 | ||
154 | static int | |
155 | do_link(const char *pfile) | |
156 | { | |
157 | int ret = 0; | |
feb04e66 | 158 | char link_file[BUFSIZ + 16] = { 0 }; |
6bb24f4d BB |
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 | */ | |
feb04e66 | 168 | (void) snprintf(link_file, sizeof (link_file), |
514498fe | 169 | "%.*s/%s", (int)get_dirnamelen(pfile), pfile, "link_file"); |
6bb24f4d BB |
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 | |
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 | |
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 | |
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 | |
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 | ||
7839c4b5 | 258 | #ifndef __FreeBSD__ |
0107f698 GK |
259 | static int |
260 | do_xattr(const char *pfile) | |
261 | { | |
262 | int ret = 0; | |
a926aab9 | 263 | const char *value = "user.value"; |
0107f698 GK |
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 | } | |
7839c4b5 | 276 | #endif |
0107f698 | 277 | |
6bb24f4d BB |
278 | static 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 }, | |
7839c4b5 | 298 | #ifndef __FreeBSD__ |
0107f698 | 299 | { ST_CTIME, "st_ctime", do_xattr }, |
7839c4b5 | 300 | #endif |
6bb24f4d BB |
301 | }; |
302 | ||
303 | #define NCOMMAND (sizeof (timetest_table) / sizeof (timetest_table[0])) | |
304 | ||
6bb24f4d | 305 | int |
cf8d708b | 306 | main(void) |
6bb24f4d BB |
307 | { |
308 | int i, ret, fd; | |
a926aab9 | 309 | const char *penv[] = {"TESTDIR", "TESTFILE0"}; |
6bb24f4d | 310 | |
6bb24f4d BB |
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 | /* | |
feb04e66 | 325 | * If the test file exists, remove it first. |
6bb24f4d BB |
326 | */ |
327 | if (access(tfile, F_OK) == 0) { | |
328 | (void) unlink(tfile); | |
329 | } | |
6bb24f4d BB |
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 | ||
f0bf7a24 RN |
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", | |
6bb24f4d BB |
374 | timetest_table[i].name, (long)t1, (long)t2); |
375 | return (1); | |
376 | } else { | |
f0bf7a24 RN |
377 | (void) fprintf(stderr, |
378 | "%s: good time change: t1(%ld), t2(%ld)\n", | |
6bb24f4d BB |
379 | timetest_table[i].name, (long)t1, (long)t2); |
380 | } | |
381 | } | |
382 | ||
6bb24f4d BB |
383 | return (0); |
384 | } |