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 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 101 get_dirnamelen(const char *path) 102 { 103 const char *end = strrchr(path, '/'); 104 return (end ? end - path : -1); 105 } 106 107 static int 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 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 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 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 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 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 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 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 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 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