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 lutimens(a,b) and
20    utimensat(AT_FDCWD,a,b,AT_SYMLINK_NOFOLLOW).  FUNC is the function
21    to test.  Assumes that BASE and ASSERT are already defined.  If
22    PRINT, warn before skipping tests with status 77.  */
23 static int
test_lutimens(int (* func)(char const *,struct timespec const *),bool print)24 test_lutimens (int (*func) (char const *, struct timespec const *), bool print)
25 {
26   int result;
27   int saved_errno;
28   struct stat st1;
29   struct stat st2;
30   bool atime_supported = true;
31 
32   /* Non-symlinks should be handled just like utimens.  */
33   errno = 0;
34   ASSERT (func ("no_such", NULL) == -1);
35   ASSERT (errno == ENOENT);
36   errno = 0;
37   ASSERT (func ("no_such/", NULL) == -1);
38   ASSERT (errno == ENOENT || errno == ENOTDIR);
39   errno = 0;
40   ASSERT (func ("", NULL) == -1);
41   ASSERT (errno == ENOENT);
42   ASSERT (close (creat (BASE "file", 0600)) == 0);
43   ASSERT (stat (BASE "file", &st1) == 0);
44   ASSERT (st1.st_atime != Y2K);
45   ASSERT (st1.st_mtime != Y2K);
46   {
47     struct timespec ts[2];
48     ts[0].tv_sec = Y2K;
49     ts[0].tv_nsec = 0;
50     ts[1] = ts[0];
51     errno = 0;
52     ASSERT (func (BASE "file/", ts) == -1);
53     ASSERT (errno == ENOTDIR);
54     ASSERT (stat (BASE "file", &st2) == 0);
55     ASSERT (st1.st_atime == st2.st_atime);
56     ASSERT (st1.st_mtime == st2.st_mtime);
57   }
58   {
59     struct timespec ts[2];
60     ts[0].tv_sec = Y2K;
61     ts[0].tv_nsec = 0;
62     ts[1] = ts[0];
63     nap ();
64     ASSERT (func (BASE "file", ts) == 0);
65   }
66   ASSERT (stat (BASE "file", &st2) == 0);
67   ASSERT (st2.st_atime == Y2K);
68   ASSERT (st2.st_mtime == Y2K);
69   if (check_ctime)
70     ASSERT (ctime_compare (&st1, &st2) < 0);
71 
72   /* Play with symlink timestamps.  */
73   if (symlink (BASE "file", BASE "link"))
74     {
75       ASSERT (unlink (BASE "file") == 0);
76       if (print)
77         fputs ("skipping test: symlinks not supported on this file system\n",
78                stderr);
79       return 77;
80     }
81   errno = 0;
82   result = func (BASE "link", NULL);
83   saved_errno = errno;
84   /* Make sure we did not reference through link by accident.  */
85   ASSERT (stat (BASE "file", &st1) == 0);
86   ASSERT (st1.st_atime == Y2K);
87   ASSERT (st1.st_mtime == Y2K);
88   ASSERT (lstat (BASE "link", &st1) == 0);
89   ASSERT (st1.st_atime != Y2K);
90   ASSERT (st1.st_mtime != Y2K);
91   ASSERT (unlink (BASE "file") == 0);
92   if (result == -1 && saved_errno == ENOSYS)
93     {
94       ASSERT (unlink (BASE "link") == 0);
95       if (print)
96         fputs ("skipping test: "
97                "setting symlink time not supported on this file system\n",
98                stderr);
99       return 77;
100     }
101   ASSERT (!result);
102   ASSERT (lstat (BASE "link", &st1) == 0);
103   /* On cygwin, lstat() changes atime of symlinks, so that lutimens
104      can only effectively modify mtime.  */
105   nap ();
106   ASSERT (lstat (BASE "link", &st2) == 0);
107   if (st1.st_atime != st2.st_atime
108       || get_stat_atime_ns (&st1) != get_stat_atime_ns (&st2))
109     atime_supported = false;
110   ASSERT (st1.st_ctime == st2.st_ctime);
111   ASSERT (get_stat_ctime_ns (&st1) == get_stat_ctime_ns (&st2));
112 
113   /* Invalid arguments.  */
114   {
115     struct timespec ts[2];
116     ts[0].tv_sec = Y2K;
117     ts[0].tv_nsec = UTIME_BOGUS_POS;
118     ts[1].tv_sec = Y2K;
119     ts[1].tv_nsec = 0;
120     errno = 0;
121     ASSERT (func (BASE "link", ts) == -1);
122     ASSERT (errno == EINVAL);
123   }
124   {
125     struct timespec ts[2];
126     ts[0].tv_sec = Y2K;
127     ts[0].tv_nsec = 0;
128     ts[1].tv_sec = Y2K;
129     ts[1].tv_nsec = UTIME_BOGUS_NEG;
130     errno = 0;
131     ASSERT (func (BASE "link", ts) == -1);
132     ASSERT (errno == EINVAL);
133   }
134   ASSERT (lstat (BASE "link", &st2) == 0);
135   if (atime_supported)
136     {
137       ASSERT (st1.st_atime == st2.st_atime);
138       ASSERT (get_stat_atime_ns (&st1) == get_stat_atime_ns (&st2));
139     }
140   ASSERT (utimecmp (BASE "link", &st1, &st2, 0) == 0);
141 
142   /* Set both times.  */
143   {
144     struct timespec ts[2];
145     ts[0].tv_sec = Y2K;
146     ts[0].tv_nsec = BILLION / 2 - 1;
147     ts[1].tv_sec = Y2K;
148     ts[1].tv_nsec = BILLION - 1;
149     nap ();
150     ASSERT (func (BASE "link", ts) == 0);
151     ASSERT (lstat (BASE "link", &st2) == 0);
152     if (atime_supported)
153       {
154         ASSERT (st2.st_atime == Y2K);
155         ASSERT (0 <= get_stat_atime_ns (&st2));
156         ASSERT (get_stat_atime_ns (&st2) < BILLION / 2);
157       }
158     ASSERT (st2.st_mtime == Y2K);
159     ASSERT (0 <= get_stat_mtime_ns (&st2));
160     ASSERT (get_stat_mtime_ns (&st2) < BILLION);
161     if (check_ctime)
162       ASSERT (ctime_compare (&st1, &st2) < 0);
163   }
164 
165   /* Play with UTIME_OMIT, UTIME_NOW.  */
166   {
167     struct stat st3;
168     struct timespec ts[2];
169     ts[0].tv_sec = BILLION;
170     ts[0].tv_nsec = UTIME_OMIT;
171     ts[1].tv_sec = 0;
172     ts[1].tv_nsec = UTIME_NOW;
173     nap ();
174     ASSERT (func (BASE "link", ts) == 0);
175     ASSERT (lstat (BASE "link", &st3) == 0);
176     if (atime_supported)
177       {
178         ASSERT (st3.st_atime == Y2K);
179         ASSERT (0 <= get_stat_atime_ns (&st3));
180         ASSERT (get_stat_atime_ns (&st3) < BILLION / 2);
181       }
182     ASSERT (utimecmp (BASE "link", &st1, &st3, 0) <= 0);
183     if (check_ctime)
184       ASSERT (ctime_compare (&st2, &st3) < 0);
185     nap ();
186     ts[0].tv_nsec = 0;
187     ts[1].tv_nsec = UTIME_OMIT;
188     ASSERT (func (BASE "link", ts) == 0);
189     ASSERT (lstat (BASE "link", &st2) == 0);
190     if (atime_supported)
191       {
192         ASSERT (st2.st_atime == BILLION);
193         ASSERT (get_stat_atime_ns (&st2) == 0);
194       }
195     ASSERT (st3.st_mtime == st2.st_mtime);
196     ASSERT (get_stat_mtime_ns (&st3) == get_stat_mtime_ns (&st2));
197     if (check_ctime)
198       ASSERT (ctime_compare (&st3, &st2) < 0);
199   }
200 
201   /* Symlink to directory.  */
202   ASSERT (unlink (BASE "link") == 0);
203   ASSERT (symlink (BASE "dir", BASE "link") == 0);
204   ASSERT (mkdir (BASE "dir", 0700) == 0);
205   {
206     struct timespec ts[2];
207     ts[0].tv_sec = Y2K;
208     ts[0].tv_nsec = 0;
209     ts[1] = ts[0];
210     ASSERT (func (BASE "link/", ts) == 0);
211   }
212   /* On cygwin 1.5, stat() changes atime of directories, so only check
213      mtime.  */
214   ASSERT (stat (BASE "dir", &st1) == 0);
215   ASSERT (st1.st_mtime == Y2K);
216   ASSERT (lstat (BASE "link", &st1) == 0);
217   ASSERT (st1.st_atime != Y2K);
218   ASSERT (st1.st_mtime != Y2K);
219   ASSERT (func (BASE "link", NULL) == 0);
220   ASSERT (stat (BASE "dir", &st1) == 0);
221   ASSERT (st1.st_mtime == Y2K);
222   ASSERT (lstat (BASE "link", &st1) == 0);
223   ASSERT (st1.st_atime != Y2K);
224   ASSERT (st1.st_mtime != Y2K);
225 
226   /* Cleanup.  */
227   ASSERT (rmdir (BASE "dir") == 0);
228   ASSERT (unlink (BASE "link") == 0);
229   return 0;
230 }
231