1 /* Test of file timestamp modification functions.
2    Copyright (C) 2009-2021 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 #include "test-utimens-common.h"
18 
19 /* This file is designed to test both fdutimens(a,NULL,b) and
20    futimens(a,b).  FUNC is the function to test.  Assumes that BASE
21    and ASSERT are already defined.  If PRINT, warn before skipping
22    tests with status 77.  */
23 static int
test_futimens(int (* func)(int,struct timespec const *),bool print)24 test_futimens (int (*func) (int, struct timespec const *),
25                bool print)
26 {
27   int fd = creat (BASE "file", 0600);
28   int result;
29   struct stat st1;
30   struct stat st2;
31   ASSERT (0 <= fd);
32 
33   /* Sanity check.  */
34   ASSERT (fstat (fd, &st1) == 0);
35   nap ();
36   errno = 0;
37   result = func (fd, NULL);
38   if (result == -1 && errno == ENOSYS)
39     {
40       ASSERT (close (fd) == 0);
41       ASSERT (unlink (BASE "file") == 0);
42       if (print)
43         fputs ("skipping test: "
44                "setting fd time not supported on this file system\n",
45                stderr);
46       return 77;
47     }
48   ASSERT (!result);
49   ASSERT (fstat (fd, &st2) == 0);
50   /* If utimens truncates to worse resolution than the file system
51      supports, then time can appear to go backwards between now and a
52      follow-up utimens with UTIME_NOW or a NULL timespec.  Use
53      UTIMECMP_TRUNCATE_SOURCE to compensate, with st1 as the
54      source.  */
55   ASSERT (0 <= utimecmp (BASE "file", &st2, &st1, UTIMECMP_TRUNCATE_SOURCE));
56   if (check_ctime)
57     ASSERT (ctime_compare (&st1, &st2) < 0);
58   {
59     /* On some NFS systems, the 'now' timestamp of creat or a NULL
60        timespec is determined by the server, but the 'now' timestamp
61        determined by gettime() (as is done when using UTIME_NOW) is
62        determined by the client; since the two machines are not
63        necessarily on the same clock, this is another case where time
64        can appear to go backwards.  The rest of this test cares about
65        client time, so manually use gettime() to set both times.  */
66     struct timespec ts[2];
67     gettime (&ts[0]);
68     ts[1] = ts[0];
69     ASSERT (func (fd, ts) == 0);
70     ASSERT (fstat (fd, &st1) == 0);
71     nap ();
72   }
73 
74   /* Invalid arguments.  */
75   {
76     errno = 0;
77     ASSERT (func (AT_FDCWD, NULL) == -1);
78     ASSERT (errno == EBADF);
79   }
80   {
81     errno = 0;
82     ASSERT (func (-1, NULL) == -1);
83     ASSERT (errno == EBADF);
84   }
85   {
86     close (99);
87     errno = 0;
88     ASSERT (func (99, NULL) == -1);
89     ASSERT (errno == EBADF);
90   }
91   {
92     int fd0 = dup (0);
93     ASSERT (0 <= fd0);
94     ASSERT (close (fd0) == 0);
95     errno = 0;
96     ASSERT (func (fd0, NULL) == -1);
97     ASSERT (errno == EBADF);
98   }
99   {
100     struct timespec ts[2];
101     ts[0].tv_sec = Y2K;
102     ts[0].tv_nsec = UTIME_BOGUS_POS;
103     ts[1].tv_sec = Y2K;
104     ts[1].tv_nsec = 0;
105     errno = 0;
106     ASSERT (func (fd, ts) == -1);
107     ASSERT (errno == EINVAL);
108   }
109   {
110     struct timespec ts[2];
111     ts[0].tv_sec = Y2K;
112     ts[0].tv_nsec = 0;
113     ts[1].tv_sec = Y2K;
114     ts[1].tv_nsec = UTIME_BOGUS_NEG;
115     errno = 0;
116     ASSERT (func (fd, ts) == -1);
117     ASSERT (errno == EINVAL);
118   }
119   ASSERT (fstat (fd, &st2) == 0);
120   ASSERT (st1.st_atime == st2.st_atime);
121   ASSERT (get_stat_atime_ns (&st1) == get_stat_atime_ns (&st2));
122   ASSERT (utimecmp (BASE "file", &st1, &st2, 0) == 0);
123 
124   /* Set both times.  */
125   {
126     struct timespec ts[2];
127     ts[0].tv_sec = Y2K;
128     ts[0].tv_nsec = BILLION / 2 - 1;
129     ts[1].tv_sec = Y2K;
130     ts[1].tv_nsec = BILLION - 1;
131     ASSERT (func (fd, ts) == 0);
132     ASSERT (fstat (fd, &st2) == 0);
133     ASSERT (st2.st_atime == Y2K);
134     ASSERT (0 <= get_stat_atime_ns (&st2));
135     ASSERT (get_stat_atime_ns (&st2) < BILLION / 2);
136     ASSERT (st2.st_mtime == Y2K);
137     ASSERT (0 <= get_stat_mtime_ns (&st2));
138     ASSERT (get_stat_mtime_ns (&st2) < BILLION);
139     if (check_ctime)
140       ASSERT (ctime_compare (&st1, &st2) < 0);
141   }
142 
143   /* Play with UTIME_OMIT, UTIME_NOW.  */
144   {
145     struct stat st3;
146     struct timespec ts[2];
147     ts[0].tv_sec = BILLION;
148     ts[0].tv_nsec = UTIME_OMIT;
149     ts[1].tv_sec = 0;
150     ts[1].tv_nsec = UTIME_NOW;
151     nap ();
152     ASSERT (func (fd, ts) == 0);
153     ASSERT (fstat (fd, &st3) == 0);
154     ASSERT (st3.st_atime == Y2K);
155     ASSERT (0 <= get_stat_atime_ns (&st3));
156     ASSERT (get_stat_atime_ns (&st3) <= BILLION / 2);
157     ASSERT (utimecmp (BASE "file", &st1, &st3, UTIMECMP_TRUNCATE_SOURCE) <= 0);
158     if (check_ctime)
159       ASSERT (ctime_compare (&st2, &st3) < 0);
160     nap ();
161     ts[0].tv_nsec = 0;
162     ts[1].tv_nsec = UTIME_OMIT;
163     ASSERT (func (fd, ts) == 0);
164     ASSERT (fstat (fd, &st2) == 0);
165     ASSERT (st2.st_atime == BILLION);
166     ASSERT (get_stat_atime_ns (&st2) == 0);
167     ASSERT (st3.st_mtime == st2.st_mtime);
168     ASSERT (get_stat_mtime_ns (&st3) == get_stat_mtime_ns (&st2));
169     if (check_ctime > 0)
170       ASSERT (ctime_compare (&st3, &st2) < 0);
171   }
172 
173   /* Cleanup.  */
174   ASSERT (close (fd) == 0);
175   ASSERT (unlink (BASE "file") == 0);
176   return 0;
177 }
178