summaryrefslogtreecommitdiffstats
path: root/gl/m4/utimes.m4
blob: 73b9a2da34e29bfb48d2129bc69716f50ead2468 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# Detect some bugs in glibc's implementation of utimes.
# serial 8

dnl Copyright (C) 2003-2005, 2009-2023 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.

# See if we need to work around bugs in glibc's implementation of
# utimes from 2003-07-12 to 2003-09-17.
# First, there was a bug that would make utimes set mtime
# and atime to zero (1970-01-01) unconditionally.
# Then, there was code to round rather than truncate.
# Then, there was an implementation (sparc64, Linux-2.4.28, glibc-2.3.3)
# that didn't honor the NULL-means-set-to-current-time semantics.
# Finally, there was also a version of utimes that failed on read-only
# files, while utime worked fine (linux-2.2.20, glibc-2.2.5).
#
# From Jim Meyering, with suggestions from Paul Eggert.

AC_DEFUN([gl_FUNC_UTIMES],
[
  AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
  AC_CACHE_CHECK([whether the utimes function works],
                 [gl_cv_func_working_utimes],
    [AC_RUN_IFELSE([AC_LANG_SOURCE([[
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <utime.h>
#include <errno.h>
]GL_MDA_DEFINES[

static int
inorder (time_t a, time_t b, time_t c)
{
  return a <= b && b <= c;
}

int
main ()
{
  int result = 0;
  char const *file = "conftest.utimes";
  /* On OS/2, file timestamps must be on or after 1980 in local time,
     with an even number of seconds.  */
  static struct timeval timeval[2] = {{315620000 + 10, 10},
                                      {315620000 + 1000000, 999998}};

  /* Test whether utimes() essentially works.  */
  {
    struct stat sbuf;
    FILE *f = fopen (file, "w");
    if (f == NULL)
      result |= 1;
    else if (fclose (f) != 0)
      result |= 1;
    else if (utimes (file, timeval) != 0)
      result |= 2;
    else if (lstat (file, &sbuf) != 0)
      result |= 1;
    else if (!(sbuf.st_atime == timeval[0].tv_sec
               && sbuf.st_mtime == timeval[1].tv_sec))
      result |= 4;
    if (unlink (file) != 0)
      result |= 1;
  }

  /* Test whether utimes() with a NULL argument sets the file's timestamp
     to the current time.  Use 'fstat' as well as 'time' to
     determine the "current" time, to accommodate NFS file systems
     if there is a time skew between the host and the NFS server.  */
  {
    int fd = open (file, O_WRONLY|O_CREAT, 0644);
    if (fd < 0)
      result |= 1;
    else
      {
        time_t t0, t2;
        struct stat st0, st1, st2;
        if (time (&t0) == (time_t) -1)
          result |= 1;
        else if (fstat (fd, &st0) != 0)
          result |= 1;
        else if (utimes (file, timeval) != 0
                 && (errno != EACCES
                     /* OS/2 kLIBC utimes fails on opened files.  */
                     || close (fd) != 0
                     || utimes (file, timeval) != 0
                     || (fd = open (file, O_WRONLY)) < 0))
          result |= 2;
        else if (utimes (file, NULL) != 0
                 && (errno != EACCES
                     /* OS/2 kLIBC utimes fails on opened files.  */
                     || close (fd) != 0
                     || utimes (file, NULL) != 0
                     || (fd = open (file, O_WRONLY)) < 0))
          result |= 8;
        else if (fstat (fd, &st1) != 0)
          result |= 1;
        else if (write (fd, "\n", 1) != 1)
          result |= 1;
        else if (fstat (fd, &st2) != 0)
          result |= 1;
        else if (time (&t2) == (time_t) -1)
          result |= 1;
        else
          {
            int m_ok_POSIX = inorder (t0, st1.st_mtime, t2);
            int m_ok_NFS = inorder (st0.st_mtime, st1.st_mtime, st2.st_mtime);
            if (! (st1.st_atime == st1.st_mtime))
              result |= 16;
            if (! (m_ok_POSIX || m_ok_NFS))
              result |= 32;
          }
        if (close (fd) != 0)
          result |= 1;
      }
    if (unlink (file) != 0)
      result |= 1;
  }

  /* Test whether utimes() with a NULL argument works on read-only files.  */
  {
    int fd = open (file, O_WRONLY|O_CREAT, 0444);
    if (fd < 0)
      result |= 1;
    else if (close (fd) != 0)
      result |= 1;
    else if (utimes (file, NULL) != 0)
      result |= 64;
    if (unlink (file) != 0)
      result |= 1;
  }

  return result;
}
  ]])],
       [gl_cv_func_working_utimes=yes],
       [gl_cv_func_working_utimes=no],
       [case "$host_os" in
                   # Guess yes on musl systems.
          *-musl*) gl_cv_func_working_utimes="guessing yes" ;;
                   # Guess no on native Windows.
          mingw*)  gl_cv_func_working_utimes="guessing no" ;;
          *)       gl_cv_func_working_utimes="$gl_cross_guess_normal" ;;
        esac
       ])
    ])

  case "$gl_cv_func_working_utimes" in
    *yes)
      AC_DEFINE([HAVE_WORKING_UTIMES], [1], [Define if utimes works properly.])
      ;;
  esac
])