Blame gnulib-tests/test-stat-time.c

Packit 33f14e
/* Test of <stat-time.h>.
Packit 33f14e
   Copyright (C) 2007-2017 Free Software Foundation, Inc.
Packit 33f14e
Packit 33f14e
   This program is free software: you can redistribute it and/or modify
Packit 33f14e
   it under the terms of the GNU General Public License as published by
Packit 33f14e
   the Free Software Foundation; either version 3 of the License, or
Packit 33f14e
   (at your option) any later version.
Packit 33f14e
Packit 33f14e
   This program is distributed in the hope that it will be useful,
Packit 33f14e
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 33f14e
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 33f14e
   GNU General Public License for more details.
Packit 33f14e
Packit 33f14e
   You should have received a copy of the GNU General Public License
Packit 33f14e
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
Packit 33f14e
Packit 33f14e
/* Written by James Youngman <jay@gnu.org>, 2007.  */
Packit 33f14e
Packit 33f14e
#include <config.h>
Packit 33f14e
Packit 33f14e
#include "stat-time.h"
Packit 33f14e
Packit 33f14e
#include <fcntl.h>
Packit 33f14e
#include <signal.h>
Packit 33f14e
#include <stdio.h>
Packit 33f14e
#include <sys/stat.h>
Packit 33f14e
#include <unistd.h>
Packit 33f14e
#include <time.h>
Packit 33f14e
Packit 33f14e
#include "macros.h"
Packit 33f14e
Packit 33f14e
#define BASE "test-stat-time.t"
Packit 33f14e
#include "nap.h"
Packit 33f14e
Packit 33f14e
enum { NFILES = 4 };
Packit 33f14e
Packit 33f14e
static char filename_stamp1[50];
Packit 33f14e
static char filename_testfile[50];
Packit 33f14e
static char filename_stamp2[50];
Packit 33f14e
static char filename_stamp3[50];
Packit 33f14e
Packit 33f14e
/* Use file names that are different at each run.
Packit 33f14e
   This is necessary for test_birthtime() to pass on native Windows:
Packit 33f14e
   On this platform, the file system apparently remembers the creation time
Packit 33f14e
   of a file even after it is removed and created anew.  See
Packit 33f14e
   "Windows NT Contains File System Tunneling Capabilities"
Packit 33f14e
   <https://support.microsoft.com/en-us/help/172190/>  */
Packit 33f14e
static void
Packit 33f14e
initialize_filenames (void)
Packit 33f14e
{
Packit 33f14e
  long t = (long) time (NULL);
Packit 33f14e
  sprintf (filename_stamp1,   "t-stt-%ld-stamp1", t);
Packit 33f14e
  sprintf (filename_testfile, "t-stt-%ld-testfile", t);
Packit 33f14e
  sprintf (filename_stamp2,   "t-stt-%ld-stamp2", t);
Packit 33f14e
  sprintf (filename_stamp3,   "t-stt-%ld-stamp3", t);
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
static int
Packit 33f14e
force_unlink (const char *filename)
Packit 33f14e
{
Packit 33f14e
  /* This chmod is necessary on mingw, where unlink() of a read-only file
Packit 33f14e
     fails with EPERM.  */
Packit 33f14e
  chmod (filename, 0600);
Packit 33f14e
  return unlink (filename);
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
static void
Packit 33f14e
cleanup (int sig)
Packit 33f14e
{
Packit 33f14e
  /* Remove temporary files.  */
Packit 33f14e
  force_unlink (filename_stamp1);
Packit 33f14e
  force_unlink (filename_testfile);
Packit 33f14e
  force_unlink (filename_stamp2);
Packit 33f14e
  force_unlink (filename_stamp3);
Packit 33f14e
Packit 33f14e
  if (sig != 0)
Packit 33f14e
    _exit (1);
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
static int
Packit 33f14e
open_file (const char *filename, int flags)
Packit 33f14e
{
Packit 33f14e
  int fd = open (filename, flags | O_WRONLY, 0500);
Packit 33f14e
  if (fd >= 0)
Packit 33f14e
    {
Packit 33f14e
      close (fd);
Packit 33f14e
      return 1;
Packit 33f14e
    }
Packit 33f14e
  else
Packit 33f14e
    {
Packit 33f14e
      return 0;
Packit 33f14e
    }
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
static void
Packit 33f14e
create_file (const char *filename)
Packit 33f14e
{
Packit 33f14e
  ASSERT (open_file (filename, O_CREAT | O_EXCL));
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
static void
Packit 33f14e
do_stat (const char *filename, struct stat *p)
Packit 33f14e
{
Packit 33f14e
  ASSERT (stat (filename, p) == 0);
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
static void
Packit 33f14e
prepare_test (struct stat *statinfo, struct timespec *modtimes)
Packit 33f14e
{
Packit 33f14e
  int i;
Packit 33f14e
Packit 33f14e
  create_file (filename_stamp1);
Packit 33f14e
  nap ();
Packit 33f14e
  create_file (filename_testfile);
Packit 33f14e
  nap ();
Packit 33f14e
  create_file (filename_stamp2);
Packit 33f14e
  nap ();
Packit 33f14e
  ASSERT (chmod (filename_testfile, 0400) == 0);
Packit 33f14e
  nap ();
Packit 33f14e
  create_file (filename_stamp3);
Packit 33f14e
Packit 33f14e
  do_stat (filename_stamp1,   &statinfo[0]);
Packit 33f14e
  do_stat (filename_testfile, &statinfo[1]);
Packit 33f14e
  do_stat (filename_stamp2,   &statinfo[2]);
Packit 33f14e
  do_stat (filename_stamp3,   &statinfo[3]);
Packit 33f14e
Packit 33f14e
  /* Now use our access functions. */
Packit 33f14e
  for (i = 0; i < NFILES; ++i)
Packit 33f14e
    {
Packit 33f14e
      modtimes[i] = get_stat_mtime (&statinfo[i]);
Packit 33f14e
    }
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
static void
Packit 33f14e
test_mtime (const struct stat *statinfo, struct timespec *modtimes)
Packit 33f14e
{
Packit 33f14e
  int i;
Packit 33f14e
Packit 33f14e
  /* Use the struct stat fields directly. */
Packit 33f14e
  /* mtime(stamp1) < mtime(stamp2) */
Packit 33f14e
  ASSERT (statinfo[0].st_mtime < statinfo[2].st_mtime
Packit 33f14e
          || (statinfo[0].st_mtime == statinfo[2].st_mtime
Packit 33f14e
              && (get_stat_mtime_ns (&statinfo[0])
Packit 33f14e
                  < get_stat_mtime_ns (&statinfo[2]))));
Packit 33f14e
  /* mtime(stamp2) < mtime(stamp3) */
Packit 33f14e
  ASSERT (statinfo[2].st_mtime < statinfo[3].st_mtime
Packit 33f14e
          || (statinfo[2].st_mtime == statinfo[3].st_mtime
Packit 33f14e
              && (get_stat_mtime_ns (&statinfo[2])
Packit 33f14e
                  < get_stat_mtime_ns (&statinfo[3]))));
Packit 33f14e
Packit 33f14e
  /* Now check the result of the access functions. */
Packit 33f14e
  /* mtime(stamp1) < mtime(stamp2) */
Packit 33f14e
  ASSERT (modtimes[0].tv_sec < modtimes[2].tv_sec
Packit 33f14e
          || (modtimes[0].tv_sec == modtimes[2].tv_sec
Packit 33f14e
              && modtimes[0].tv_nsec < modtimes[2].tv_nsec));
Packit 33f14e
  /* mtime(stamp2) < mtime(stamp3) */
Packit 33f14e
  ASSERT (modtimes[2].tv_sec < modtimes[3].tv_sec
Packit 33f14e
          || (modtimes[2].tv_sec == modtimes[3].tv_sec
Packit 33f14e
              && modtimes[2].tv_nsec < modtimes[3].tv_nsec));
Packit 33f14e
Packit 33f14e
  /* verify equivalence */
Packit 33f14e
  for (i = 0; i < NFILES; ++i)
Packit 33f14e
    {
Packit 33f14e
      struct timespec ts;
Packit 33f14e
      ts = get_stat_mtime (&statinfo[i]);
Packit 33f14e
      ASSERT (ts.tv_sec == statinfo[i].st_mtime);
Packit 33f14e
    }
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
#if (defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__
Packit 33f14e
/* Skip the ctime tests on native Windows platforms, because their
Packit 33f14e
   st_ctime is either the same as st_mtime (plus or minus an offset)
Packit 33f14e
   or set to the file _creation_ time, and is not influenced by rename
Packit 33f14e
   or chmod.  */
Packit 33f14e
# define test_ctime(ignored) ((void) 0)
Packit 33f14e
#else
Packit 33f14e
static void
Packit 33f14e
test_ctime (const struct stat *statinfo)
Packit 33f14e
{
Packit 33f14e
  /* On some buggy NFS clients, mtime and ctime are disproportionately
Packit 33f14e
     skewed from one another.  Skip this test in that case.  */
Packit 33f14e
  if (statinfo[0].st_mtime != statinfo[0].st_ctime)
Packit 33f14e
    return;
Packit 33f14e
Packit 33f14e
  /* mtime(stamp2) < ctime(testfile) */
Packit 33f14e
  ASSERT (statinfo[2].st_mtime < statinfo[1].st_ctime
Packit 33f14e
          || (statinfo[2].st_mtime == statinfo[1].st_ctime
Packit 33f14e
              && (get_stat_mtime_ns (&statinfo[2])
Packit 33f14e
                  < get_stat_ctime_ns (&statinfo[1]))));
Packit 33f14e
}
Packit 33f14e
#endif
Packit 33f14e
Packit 33f14e
static void
Packit 33f14e
test_birthtime (const struct stat *statinfo,
Packit 33f14e
                const struct timespec *modtimes,
Packit 33f14e
                struct timespec *birthtimes)
Packit 33f14e
{
Packit 33f14e
  int i;
Packit 33f14e
Packit 33f14e
  /* Collect the birth times.  */
Packit 33f14e
  for (i = 0; i < NFILES; ++i)
Packit 33f14e
    {
Packit 33f14e
      birthtimes[i] = get_stat_birthtime (&statinfo[i]);
Packit 33f14e
      if (birthtimes[i].tv_nsec < 0)
Packit 33f14e
        return;
Packit 33f14e
    }
Packit 33f14e
Packit 33f14e
  /* mtime(stamp1) < birthtime(testfile) */
Packit 33f14e
  ASSERT (modtimes[0].tv_sec < birthtimes[1].tv_sec
Packit 33f14e
          || (modtimes[0].tv_sec == birthtimes[1].tv_sec
Packit 33f14e
              && modtimes[0].tv_nsec < birthtimes[1].tv_nsec));
Packit 33f14e
  /* birthtime(testfile) < mtime(stamp2) */
Packit 33f14e
  ASSERT (birthtimes[1].tv_sec < modtimes[2].tv_sec
Packit 33f14e
          || (birthtimes[1].tv_sec == modtimes[2].tv_sec
Packit 33f14e
              && birthtimes[1].tv_nsec < modtimes[2].tv_nsec));
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
int
Packit 33f14e
main (void)
Packit 33f14e
{
Packit 33f14e
  struct stat statinfo[NFILES];
Packit 33f14e
  struct timespec modtimes[NFILES];
Packit 33f14e
  struct timespec birthtimes[NFILES];
Packit 33f14e
Packit 33f14e
  initialize_filenames ();
Packit 33f14e
Packit 33f14e
#ifdef SIGHUP
Packit 33f14e
  signal (SIGHUP, cleanup);
Packit 33f14e
#endif
Packit 33f14e
#ifdef SIGINT
Packit 33f14e
  signal (SIGINT, cleanup);
Packit 33f14e
#endif
Packit 33f14e
#ifdef SIGQUIT
Packit 33f14e
  signal (SIGQUIT, cleanup);
Packit 33f14e
#endif
Packit 33f14e
#ifdef SIGTERM
Packit 33f14e
  signal (SIGTERM, cleanup);
Packit 33f14e
#endif
Packit 33f14e
Packit 33f14e
  cleanup (0);
Packit 33f14e
  prepare_test (statinfo, modtimes);
Packit 33f14e
  test_mtime (statinfo, modtimes);
Packit 33f14e
  test_ctime (statinfo);
Packit 33f14e
  test_birthtime (statinfo, modtimes, birthtimes);
Packit 33f14e
Packit 33f14e
  cleanup (0);
Packit 33f14e
  return 0;
Packit 33f14e
}