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 52typedef struct timetest { 53 int type; 54 const char *name; 55 int (*func)(const char *pfile); 56} timetest_t; 57 58static 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 71static int 72get_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 99static ssize_t 100get_dirnamelen(const char *path) 101{ 102 const char *end = strrchr(path, '/'); 103 return (end ? end - path : -1); 104} 105 106static int 107do_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 130static int 131do_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 154static int 155do_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 182static int 183do_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 201static int 202do_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 222static int 223do_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 240static int 241do_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__ 259static int 260do_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 278static void 279cleanup(void) 280{ 281 if ((strlen(tfile) != 0) && (access(tfile, F_OK) == 0)) { 282 (void) unlink(tfile); 283 } 284} 285 286static 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 305int 306main(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