(root)/
gettext-0.22.4/
gettext-tools/
gnulib-tests/
test-stat-time.c
       1  /* Test of <stat-time.h>.
       2     Copyright (C) 2007-2023 Free Software Foundation, Inc.
       3  
       4     This program is free software: you can redistribute it and/or modify
       5     it under the terms of the GNU General Public License as published by
       6     the Free Software Foundation, either version 3 of the License, or
       7     (at your option) any later version.
       8  
       9     This program is distributed in the hope that it will be useful,
      10     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12     GNU General Public License for more details.
      13  
      14     You should have received a copy of the GNU General Public License
      15     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      16  
      17  /* Written by James Youngman <jay@gnu.org>, 2007.  */
      18  
      19  #include <config.h>
      20  
      21  #include "stat-time.h"
      22  
      23  #include <fcntl.h>
      24  #include <signal.h>
      25  #include <stdio.h>
      26  #include <sys/stat.h>
      27  #include <unistd.h>
      28  #include <time.h>
      29  
      30  #include "macros.h"
      31  
      32  #define BASE "test-stat-time.t"
      33  #include "nap.h"
      34  
      35  enum { NFILES = 4 };
      36  
      37  static char filename_stamp1[50];
      38  static char filename_testfile[50];
      39  static char filename_stamp2[50];
      40  static char filename_stamp3[50];
      41  
      42  /* Use file names that are different at each run.
      43     This is necessary for test_birthtime() to pass on native Windows:
      44     On this platform, the file system apparently remembers the creation time
      45     of a file even after it is removed and created anew.  See
      46     "Windows NT Contains File System Tunneling Capabilities"
      47     <https://support.microsoft.com/en-us/help/172190/>  */
      48  static void
      49  initialize_filenames (void)
      50  {
      51    long t = (long) time (NULL);
      52    sprintf (filename_stamp1,   "t-stt-%ld-stamp1", t);
      53    sprintf (filename_testfile, "t-stt-%ld-testfile", t);
      54    sprintf (filename_stamp2,   "t-stt-%ld-stamp2", t);
      55    sprintf (filename_stamp3,   "t-stt-%ld-stamp3", t);
      56  }
      57  
      58  static int
      59  force_unlink (const char *filename)
      60  {
      61    /* This chmod is necessary on mingw, where unlink() of a read-only file
      62       fails with EPERM.  */
      63    chmod (filename, 0600);
      64    return unlink (filename);
      65  }
      66  
      67  static void
      68  cleanup (int sig)
      69  {
      70    /* Remove temporary files.  */
      71    force_unlink (filename_stamp1);
      72    force_unlink (filename_testfile);
      73    force_unlink (filename_stamp2);
      74    force_unlink (filename_stamp3);
      75  
      76    if (sig != 0)
      77      _exit (1);
      78  }
      79  
      80  static int
      81  open_file (const char *filename, int flags)
      82  {
      83    int fd = open (filename, flags | O_WRONLY, 0500);
      84    if (fd >= 0)
      85      {
      86        close (fd);
      87        return 1;
      88      }
      89    else
      90      {
      91        return 0;
      92      }
      93  }
      94  
      95  static void
      96  create_file (const char *filename)
      97  {
      98    ASSERT (open_file (filename, O_CREAT | O_EXCL));
      99  }
     100  
     101  static void
     102  do_stat (const char *filename, struct stat *p)
     103  {
     104    ASSERT (stat (filename, p) == 0);
     105  }
     106  
     107  static void
     108  prepare_test (struct stat *statinfo, struct timespec *modtimes)
     109  {
     110    int i;
     111  
     112    create_file (filename_stamp1);
     113    nap ();
     114    create_file (filename_testfile);
     115    nap ();
     116    create_file (filename_stamp2);
     117    nap ();
     118    ASSERT (chmod (filename_testfile, 0400) == 0);
     119    nap ();
     120    create_file (filename_stamp3);
     121  
     122    do_stat (filename_stamp1,   &statinfo[0]);
     123    do_stat (filename_testfile, &statinfo[1]);
     124    do_stat (filename_stamp2,   &statinfo[2]);
     125    do_stat (filename_stamp3,   &statinfo[3]);
     126  
     127    /* Now use our access functions. */
     128    for (i = 0; i < NFILES; ++i)
     129      {
     130        modtimes[i] = get_stat_mtime (&statinfo[i]);
     131      }
     132  }
     133  
     134  static void
     135  test_mtime (const struct stat *statinfo, struct timespec *modtimes)
     136  {
     137    int i;
     138  
     139    /* Use the struct stat fields directly. */
     140    /* mtime(stamp1) < mtime(stamp2) */
     141    ASSERT (statinfo[0].st_mtime < statinfo[2].st_mtime
     142            || (statinfo[0].st_mtime == statinfo[2].st_mtime
     143                && (get_stat_mtime_ns (&statinfo[0])
     144                    < get_stat_mtime_ns (&statinfo[2]))));
     145    /* mtime(stamp2) < mtime(stamp3) */
     146    ASSERT (statinfo[2].st_mtime < statinfo[3].st_mtime
     147            || (statinfo[2].st_mtime == statinfo[3].st_mtime
     148                && (get_stat_mtime_ns (&statinfo[2])
     149                    < get_stat_mtime_ns (&statinfo[3]))));
     150  
     151    /* Now check the result of the access functions. */
     152    /* mtime(stamp1) < mtime(stamp2) */
     153    ASSERT (modtimes[0].tv_sec < modtimes[2].tv_sec
     154            || (modtimes[0].tv_sec == modtimes[2].tv_sec
     155                && modtimes[0].tv_nsec < modtimes[2].tv_nsec));
     156    /* mtime(stamp2) < mtime(stamp3) */
     157    ASSERT (modtimes[2].tv_sec < modtimes[3].tv_sec
     158            || (modtimes[2].tv_sec == modtimes[3].tv_sec
     159                && modtimes[2].tv_nsec < modtimes[3].tv_nsec));
     160  
     161    /* verify equivalence */
     162    for (i = 0; i < NFILES; ++i)
     163      {
     164        struct timespec ts;
     165        ts = get_stat_mtime (&statinfo[i]);
     166        ASSERT (ts.tv_sec == statinfo[i].st_mtime);
     167      }
     168  }
     169  
     170  #if defined _WIN32 && !defined __CYGWIN__
     171  /* Skip the ctime tests on native Windows platforms, because their
     172     st_ctime is either the same as st_mtime (plus or minus an offset)
     173     or set to the file _creation_ time, and is not influenced by rename
     174     or chmod.  */
     175  # define test_ctime(ignored) ((void) 0)
     176  #else
     177  static void
     178  test_ctime (const struct stat *statinfo)
     179  {
     180    /* On some buggy NFS clients, mtime and ctime are disproportionately
     181       skewed from one another.  Skip this test in that case.  */
     182    if (statinfo[0].st_mtime != statinfo[0].st_ctime)
     183      return;
     184  
     185    /* mtime(stamp2) < ctime(testfile) */
     186    ASSERT (statinfo[2].st_mtime < statinfo[1].st_ctime
     187            || (statinfo[2].st_mtime == statinfo[1].st_ctime
     188                && (get_stat_mtime_ns (&statinfo[2])
     189                    < get_stat_ctime_ns (&statinfo[1]))));
     190  }
     191  #endif
     192  
     193  static void
     194  test_birthtime (const struct stat *statinfo,
     195                  const struct timespec *modtimes,
     196                  struct timespec *birthtimes)
     197  {
     198    int i;
     199  
     200    /* Collect the birth times.  */
     201    for (i = 0; i < NFILES; ++i)
     202      {
     203        birthtimes[i] = get_stat_birthtime (&statinfo[i]);
     204        if (birthtimes[i].tv_nsec < 0)
     205          return;
     206      }
     207  
     208    /* mtime(stamp1) < birthtime(testfile) */
     209    ASSERT (modtimes[0].tv_sec < birthtimes[1].tv_sec
     210            || (modtimes[0].tv_sec == birthtimes[1].tv_sec
     211                && modtimes[0].tv_nsec < birthtimes[1].tv_nsec));
     212    /* birthtime(testfile) < mtime(stamp2) */
     213    ASSERT (birthtimes[1].tv_sec < modtimes[2].tv_sec
     214            || (birthtimes[1].tv_sec == modtimes[2].tv_sec
     215                && birthtimes[1].tv_nsec < modtimes[2].tv_nsec));
     216  }
     217  
     218  int
     219  main (void)
     220  {
     221    struct stat statinfo[NFILES];
     222    struct timespec modtimes[NFILES];
     223    struct timespec birthtimes[NFILES];
     224  
     225    initialize_filenames ();
     226  
     227  #ifdef SIGHUP
     228    signal (SIGHUP, cleanup);
     229  #endif
     230  #ifdef SIGINT
     231    signal (SIGINT, cleanup);
     232  #endif
     233  #ifdef SIGQUIT
     234    signal (SIGQUIT, cleanup);
     235  #endif
     236  #ifdef SIGTERM
     237    signal (SIGTERM, cleanup);
     238  #endif
     239  
     240    cleanup (0);
     241    prepare_test (statinfo, modtimes);
     242    test_mtime (statinfo, modtimes);
     243    test_ctime (statinfo);
     244    test_birthtime (statinfo, modtimes, birthtimes);
     245  
     246    cleanup (0);
     247    return 0;
     248  }