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