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 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 100 get_dirnamelen(const char *path) 101 { 102 const char *end = strrchr(path, '/'); 103 return (end ? end - path : -1); 104 } 105 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); 122 (void) close(fd); 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); 146 (void) close(fd); 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; 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 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 258 #ifndef __FreeBSD__ 259 static int 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 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 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