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  * Copyright (c) 2018, Joyent, Inc.
  33  */
  34 
  35 #include <sys/types.h>
  36 #include <sys/stat.h>
  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 
  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         char    *name;
  55         int     (*func)(const char *pfile);
  56 } timetest_t;
  57 
  58 static char tfile[BUFSIZ] = { 0 };
  59 
  60 extern int errno;
  61 
  62 /*
  63  * DESCRIPTION:
  64  *      Verify time will be changed correctly after each operation.
  65  *
  66  * STRATEGY:
  67  *      1. Define time test array.
  68  *      2. Loop through each item in this array.
  69  *      3. Verify the time is changed after each operation.
  70  *
  71  */
  72 
  73 static int
  74 get_file_time(const char *pfile, int what, time_t *ptr)
  75 {
  76         struct stat stat_buf;
  77 
  78         if (pfile == NULL || ptr == NULL) {
  79                 return (-1);
  80         }
  81 
  82         if (stat(pfile, &stat_buf) == -1) {
  83                 return (-1);
  84         }
  85 
  86         switch (what) {
  87                 case ST_ATIME:
  88                         *ptr = stat_buf.st_atime;
  89                         return (0);
  90                 case ST_CTIME:
  91                         *ptr = stat_buf.st_ctime;
  92                         return (0);
  93                 case ST_MTIME:
  94                         *ptr = stat_buf.st_mtime;
  95                         return (0);
  96                 default:
  97                         return (-1);
  98         }
  99 }
 100 
 101 static int
 102 do_read(const char *pfile)
 103 {
 104         int fd, ret = 0;
 105         char buf[BUFSIZ] = { 0 };
 106 
 107         if (pfile == NULL) {
 108                 return (-1);
 109         }
 110 
 111         if ((fd = open(pfile, O_RDONLY, ALL_MODE)) == -1) {
 112                 return (-1);
 113         }
 114         if (read(fd, buf, sizeof (buf)) == -1) {
 115                 (void) fprintf(stderr, "read(%d, buf, %d) failed with errno "
 116                     "%d\n", fd, sizeof (buf), errno);
 117                 return (1);
 118         }
 119         (void) close(fd);
 120 
 121         return (ret);
 122 }
 123 
 124 static int
 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, strlen(buf), errno);
 140                 return (1);
 141         }
 142         (void) close(fd);
 143 
 144         return (ret);
 145 }
 146 
 147 static int
 148 do_link(const char *pfile)
 149 {
 150         int ret = 0;
 151         char link_file[BUFSIZ] = { 0 };
 152         char *dname;
 153 
 154         if (pfile == NULL) {
 155                 return (-1);
 156         }
 157 
 158         /*
 159          * Figure out source file directory name, and create
 160          * the link file in the same directory.
 161          */
 162         dname = dirname(strdup(pfile));
 163         (void) snprintf(link_file, BUFSIZ, "%s/%s", dname, "link_file");
 164 
 165         if (link(pfile, link_file) == -1) {
 166                 (void) fprintf(stderr, "link(%s, %s) failed with errno %d\n",
 167                     pfile, link_file, errno);
 168                 free((void *)dirname);
 169                 return (1);
 170         }
 171 
 172         (void) unlink(link_file);
 173         free((void *)dirname);
 174         return (ret);
 175 }
 176 
 177 static int
 178 do_creat(const char *pfile)
 179 {
 180         int fd, ret = 0;
 181 
 182         if (pfile == NULL) {
 183                 return (-1);
 184         }
 185 
 186         if ((fd = creat(pfile, ALL_MODE)) == -1) {
 187                 (void) fprintf(stderr, "creat(%s, ALL_MODE) failed with errno "
 188                     "%d\n", pfile, errno);
 189                 return (1);
 190         }
 191         (void) close(fd);
 192 
 193         return (ret);
 194 }
 195 
 196 static int
 197 do_utime(const char *pfile)
 198 {
 199         int ret = 0;
 200 
 201         if (pfile == NULL) {
 202                 return (-1);
 203         }
 204 
 205         /*
 206          * Times of the file are set to the current time
 207          */
 208         if (utime(pfile, NULL) == -1) {
 209                 (void) fprintf(stderr, "utime(%s, NULL) failed with errno "
 210                     "%d\n", pfile, errno);
 211                 return (1);
 212         }
 213 
 214         return (ret);
 215 }
 216 
 217 static int
 218 do_chmod(const char *pfile)
 219 {
 220         int ret = 0;
 221 
 222         if (pfile == NULL) {
 223                 return (-1);
 224         }
 225 
 226         if (chmod(pfile, ALL_MODE) == -1) {
 227                 (void) fprintf(stderr, "chmod(%s, ALL_MODE) failed with "
 228                     "errno %d\n", pfile, errno);
 229                 return (1);
 230         }
 231 
 232         return (ret);
 233 }
 234 
 235 static int
 236 do_chown(const char *pfile)
 237 {
 238         int ret = 0;
 239 
 240         if (pfile == NULL) {
 241                 return (-1);
 242         }
 243 
 244         if (chown(pfile, getuid(), getgid()) == -1) {
 245                 (void) fprintf(stderr, "chown(%s, %d, %d) failed with errno "
 246                     "%d\n", pfile, (int)getuid(), (int)getgid(), errno);
 247                 return (1);
 248         }
 249 
 250         return (ret);
 251 }
 252 
 253 static void
 254 cleanup(void)
 255 {
 256         if ((strlen(tfile) != 0) && (access(tfile, F_OK) == 0)) {
 257                 (void) unlink(tfile);
 258         }
 259 }
 260 
 261 static timetest_t timetest_table[] = {
 262         { ST_ATIME,     "st_atime",     do_read         },
 263         { ST_ATIME,     "st_atime",     do_utime        },
 264         { ST_MTIME,     "st_mtime",     do_creat        },
 265         { ST_MTIME,     "st_mtime",     do_write        },
 266         { ST_MTIME,     "st_mtime",     do_utime        },
 267         { ST_CTIME,     "st_ctime",     do_creat        },
 268         { ST_CTIME,     "st_ctime",     do_write        },
 269         { ST_CTIME,     "st_ctime",     do_chmod        },
 270         { ST_CTIME,     "st_ctime",     do_chown        },
 271         { ST_CTIME,     "st_ctime",     do_link         },
 272         { ST_CTIME,     "st_ctime",     do_utime        },
 273 };
 274 
 275 #define NCOMMAND (sizeof (timetest_table) / sizeof (timetest_table[0]))
 276 
 277 /* ARGSUSED */
 278 int
 279 main(int argc, char *argv[])
 280 {
 281         int i, ret, fd;
 282         char *penv[] = {"TESTDIR", "TESTFILE0"};
 283 
 284         (void) fprintf(stdout, "Verify [acm]time is modified appropriately.\n");
 285         (void) atexit(cleanup);
 286 
 287         /*
 288          * Get the environment variable values.
 289          */
 290         for (i = 0; i < sizeof (penv) / sizeof (char *); i++) {
 291                 if ((penv[i] = getenv(penv[i])) == NULL) {
 292                         (void) fprintf(stderr, "getenv(penv[%d])\n", i);
 293                         return (1);
 294                 }
 295         }
 296         (void) snprintf(tfile, sizeof (tfile), "%s/%s", penv[0], penv[1]);
 297 
 298         /*
 299          * If the test file is exists, remove it first.
 300          */
 301         if (access(tfile, F_OK) == 0) {
 302                 (void) unlink(tfile);
 303         }
 304         ret = 0;
 305         if ((fd = open(tfile, O_WRONLY | O_CREAT | O_TRUNC, ALL_MODE)) == -1) {
 306                 (void) fprintf(stderr, "open(%s) failed: %d\n", tfile, errno);
 307                 return (1);
 308         }
 309         (void) close(fd);
 310 
 311         for (i = 0; i < NCOMMAND; i++) {
 312                 time_t t1, t2;
 313 
 314                 /*
 315                  * Get original time before operating.
 316                  */
 317                 ret = get_file_time(tfile, timetest_table[i].type, &t1);
 318                 if (ret != 0) {
 319                         (void) fprintf(stderr, "get_file_time(%s %d) = %d\n",
 320                             tfile, timetest_table[i].type, ret);
 321                         return (1);
 322                 }
 323 
 324                 /*
 325                  * Sleep 2 seconds, then invoke command on given file
 326                  */
 327                 (void) sleep(2);
 328                 (void) timetest_table[i].func(tfile);
 329 
 330                 /*
 331                  * Get time after operating.
 332                  */
 333                 ret = get_file_time(tfile, timetest_table[i].type, &t2);
 334                 if (ret != 0) {
 335                         (void) fprintf(stderr, "get_file_time(%s %d) = %d\n",
 336                             tfile, timetest_table[i].type, ret);
 337                         return (1);
 338                 }
 339 
 340                 if (t1 == t2) {
 341                         (void) fprintf(stderr, "%s: t1(%ld) == t2(%ld)\n",
 342                             timetest_table[i].name, (long)t1, (long)t2);
 343                         return (1);
 344                 } else {
 345                         (void) fprintf(stderr, "%s: t1(%ld) != t2(%ld)\n",
 346                             timetest_table[i].name, (long)t1, (long)t2);
 347                 }
 348         }
 349 
 350         (void) fprintf(stdout, "PASS\n");
 351         return (0);
 352 }