1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (c) 2008 Michael Kerrisk <mtk.manpages@gmail.com>
4 * Copyright (c) 2008 Subrata Modak <subrata@linux.vnet.ibm.com>
5 * Copyright (c) 2020 Viresh Kumar <viresh.kumar@linaro.org>
6 *
7 * Basic utimnsat() test.
8 */
9
10 #define _GNU_SOURCE
11 #include <stdio.h>
12 #include <time.h>
13 #include <errno.h>
14 #include <stdlib.h>
15 #include <unistd.h>
16 #include <fcntl.h>
17 #include <string.h>
18 #include <sys/stat.h>
19 #include "lapi/fs.h"
20 #include "lapi/utime.h"
21 #include "time64_variants.h"
22 #include "tst_timer.h"
23
24 #define MNTPOINT "mntpoint"
25 #define TEST_FILE MNTPOINT"/test_file"
26 #define TEST_DIR MNTPOINT"/test_dir"
27
28 static void *bad_addr;
29
30 struct mytime {
31 long access_tv_sec;
32 long access_tv_nsec;
33 long mod_tv_sec;
34 long mod_tv_nsec;
35 int atime_change;
36 int mtime_change;
37 };
38
39 static struct mytime tnn = {0, UTIME_NOW, 0, UTIME_NOW, 1, 1};
40 static struct mytime too = {0, UTIME_OMIT, 0, UTIME_OMIT, 0, 0};
41 static struct mytime tno = {0, UTIME_NOW, 0, UTIME_OMIT, 1, 0};
42 static struct mytime ton = {0, UTIME_OMIT, 0, UTIME_NOW, 0, 1};
43 static struct mytime t11 = {1, 1, 1, 1, 1, 1};
44
45 struct test_case {
46 int dirfd;
47 char *pathname;
48 struct mytime *mytime;
49 int flags;
50 int oflags;
51 int attr;
52 int mode;
53 int exp_err;
54 } tcase[] = {
55 /* Testing read-only file */
56 {AT_FDCWD, TEST_FILE, NULL, 0, O_RDONLY, 0, 0400, 0},
57 {AT_FDCWD, TEST_FILE, &tnn, 0, O_RDONLY, 0, 0400, 0},
58 {AT_FDCWD, TEST_FILE, &too, 0, O_RDONLY, 0, 0400, 0},
59 {AT_FDCWD, TEST_FILE, &tno, 0, O_RDONLY, 0, 0400, 0},
60 {AT_FDCWD, TEST_FILE, &ton, 0, O_RDONLY, 0, 0400, 0},
61 {AT_FDCWD, TEST_FILE, &t11, 0, O_RDONLY, 0, 0400, 0},
62
63 /* Testing writable file */
64 {AT_FDCWD, TEST_FILE, NULL, 0, O_RDONLY, 0, 0666, 0},
65 {AT_FDCWD, TEST_FILE, &tnn, 0, O_RDONLY, 0, 0666, 0},
66 {AT_FDCWD, TEST_FILE, &too, 0, O_RDONLY, 0, 0666, 0},
67 {AT_FDCWD, TEST_FILE, &tno, 0, O_RDONLY, 0, 0666, 0},
68 {AT_FDCWD, TEST_FILE, &ton, 0, O_RDONLY, 0, 0666, 0},
69 {AT_FDCWD, TEST_FILE, &t11, 0, O_RDONLY, 0, 0666, 0},
70
71 /* Testing append-only file */
72 {AT_FDCWD, TEST_FILE, NULL, 0, O_RDONLY, FS_APPEND_FL, 0600, 0},
73 {AT_FDCWD, TEST_FILE, &tnn, 0, O_RDONLY, FS_APPEND_FL, 0600, 0},
74 {AT_FDCWD, TEST_FILE, &too, 0, O_RDONLY, FS_APPEND_FL, 0600, 0},
75 {AT_FDCWD, TEST_FILE, &tno, 0, O_RDONLY, FS_APPEND_FL, 0600, EPERM},
76 {AT_FDCWD, TEST_FILE, &ton, 0, O_RDONLY, FS_APPEND_FL, 0600, EPERM},
77 {AT_FDCWD, TEST_FILE, &t11, 0, O_RDONLY, FS_APPEND_FL, 0600, EPERM},
78
79 /* Testing immutable file */
80 {AT_FDCWD, TEST_FILE, NULL, 0, O_RDONLY, FS_IMMUTABLE_FL, 0600, -1},
81 {AT_FDCWD, TEST_FILE, &tnn, 0, O_RDONLY, FS_IMMUTABLE_FL, 0600, -1},
82 {AT_FDCWD, TEST_FILE, &too, 0, O_RDONLY, FS_IMMUTABLE_FL, 0600, 0},
83 {AT_FDCWD, TEST_FILE, &tno, 0, O_RDONLY, FS_IMMUTABLE_FL, 0600, EPERM},
84 {AT_FDCWD, TEST_FILE, &ton, 0, O_RDONLY, FS_IMMUTABLE_FL, 0600, EPERM},
85 {AT_FDCWD, TEST_FILE, &t11, 0, O_RDONLY, FS_IMMUTABLE_FL, 0600, EPERM},
86
87 /* Testing immutable-append-only file */
88 {AT_FDCWD, TEST_FILE, NULL, 0, O_RDONLY, FS_APPEND_FL|FS_IMMUTABLE_FL, 0600, -1},
89 {AT_FDCWD, TEST_FILE, &tnn, 0, O_RDONLY, FS_APPEND_FL|FS_IMMUTABLE_FL, 0600, -1},
90 {AT_FDCWD, TEST_FILE, &too, 0, O_RDONLY, FS_APPEND_FL|FS_IMMUTABLE_FL, 0600, 0},
91 {AT_FDCWD, TEST_FILE, &tno, 0, O_RDONLY, FS_APPEND_FL|FS_IMMUTABLE_FL, 0600, EPERM},
92 {AT_FDCWD, TEST_FILE, &ton, 0, O_RDONLY, FS_APPEND_FL|FS_IMMUTABLE_FL, 0600, EPERM},
93 {AT_FDCWD, TEST_FILE, &t11, 0, O_RDONLY, FS_APPEND_FL|FS_IMMUTABLE_FL, 0600, EPERM},
94
95 /* Other failure tests */
96 {AT_FDCWD, TEST_FILE, NULL, 0, O_RDONLY, 0, 0400, EFAULT},
97 {AT_FDCWD, NULL, &tnn, 0, O_RDONLY, 0, 0400, EFAULT},
98 {-1, NULL, &tnn, AT_SYMLINK_NOFOLLOW, O_RDONLY, 0, 0400, EINVAL},
99 {-1, TEST_FILE, &tnn, 0, O_RDONLY, 0, 0400, ENOENT},
100 };
101
sys_utimensat(int dirfd,const char * pathname,void * times,int flags)102 static inline int sys_utimensat(int dirfd, const char *pathname,
103 void *times, int flags)
104 {
105 return tst_syscall(__NR_utimensat, dirfd, pathname, times, flags);
106 }
107
sys_utimensat_time64(int dirfd,const char * pathname,void * times,int flags)108 static inline int sys_utimensat_time64(int dirfd, const char *pathname,
109 void *times, int flags)
110 {
111 return tst_syscall(__NR_utimensat_time64, dirfd, pathname, times, flags);
112 }
113
114 static struct time64_variants variants[] = {
115 #if (__NR_utimensat != __LTP__NR_INVALID_SYSCALL)
116 { .utimensat = sys_utimensat, .ts_type = TST_KERN_OLD_TIMESPEC, .desc = "syscall with old kernel spec"},
117 #endif
118
119 #if (__NR_utimensat_time64 != __LTP__NR_INVALID_SYSCALL)
120 { .utimensat = sys_utimensat_time64, .ts_type = TST_KERN_TIMESPEC, .desc = "syscall time64 with kernel spec"},
121 #endif
122 };
123
124 union tst_multi {
125 struct timespec libc_ts[2];
126 struct __kernel_old_timespec kern_old_ts[2];
127 struct __kernel_timespec kern_ts[2];
128 } ts;
129
tst_multi_set_time(enum tst_ts_type type,struct mytime * mytime)130 static void tst_multi_set_time(enum tst_ts_type type, struct mytime *mytime)
131 {
132 switch (type) {
133 case TST_LIBC_TIMESPEC:
134 ts.libc_ts[0].tv_sec = mytime->access_tv_sec;
135 ts.libc_ts[0].tv_nsec = mytime->access_tv_nsec;
136 ts.libc_ts[1].tv_sec = mytime->mod_tv_sec;
137 ts.libc_ts[1].tv_nsec = mytime->mod_tv_nsec;
138 break;
139 case TST_KERN_OLD_TIMESPEC:
140 ts.kern_old_ts[0].tv_sec = mytime->access_tv_sec;
141 ts.kern_old_ts[0].tv_nsec = mytime->access_tv_nsec;
142 ts.kern_old_ts[1].tv_sec = mytime->mod_tv_sec;
143 ts.kern_old_ts[1].tv_nsec = mytime->mod_tv_nsec;
144 break;
145 case TST_KERN_TIMESPEC:
146 ts.kern_ts[0].tv_sec = mytime->access_tv_sec;
147 ts.kern_ts[0].tv_nsec = mytime->access_tv_nsec;
148 ts.kern_ts[1].tv_sec = mytime->mod_tv_sec;
149 ts.kern_ts[1].tv_nsec = mytime->mod_tv_nsec;
150 break;
151 default:
152 tst_brk(TBROK, "Invalid type: %d", type);
153 }
154 }
155
update_error(struct test_case * tc)156 static void update_error(struct test_case *tc)
157 {
158 static struct tst_kern_exv kvers[] = {
159 /* Ubuntu kernel has patch b3b4283 since 4.4.0-48.69 */
160 { "UBUNTU", "4.4.0-48.69" },
161 { NULL, NULL},
162 };
163
164 if (tc->exp_err != -1)
165 return;
166
167 /*
168 * Starting with 4.8.0 operations on immutable files return EPERM
169 * instead of EACCES.
170 * This patch has also been merged to stable 4.4 with
171 * b3b4283 ("vfs: move permission checking into notify_change() for utimes(NULL)")
172 */
173 if (tst_kvercmp2(4, 4, 27, kvers) < 0)
174 tc->exp_err = EACCES;
175 else
176 tc->exp_err = EPERM;
177 }
178
change_attr(struct test_case * tc,int fd,int set)179 static void change_attr(struct test_case *tc, int fd, int set)
180 {
181 int attr;
182
183 if (!tc->attr)
184 return;
185
186 if (ioctl(fd, FS_IOC_GETFLAGS, &attr)) {
187 if (errno == ENOTTY)
188 tst_brk(TCONF | TERRNO, "Attributes not supported by FS");
189 else
190 tst_brk(TBROK | TERRNO, "ioctl(fd, FS_IOC_GETFLAGS, &attr) failed");
191 }
192
193 if (set)
194 attr |= tc->attr;
195 else
196 attr &= ~tc->attr;
197
198 SAFE_IOCTL(fd, FS_IOC_SETFLAGS, &attr);
199 }
200
reset_time(char * pathname,int dfd,int flags,int i)201 static void reset_time(char *pathname, int dfd, int flags, int i)
202 {
203 struct time64_variants *tv = &variants[tst_variant];
204 struct stat sb;
205
206 memset(&ts, 0, sizeof(ts));
207 TEST(tv->utimensat(dfd, pathname, &ts, flags));
208 if (TST_RET) {
209 tst_res(TINFO | TTERRNO, "%2d: utimensat(%d, %s, {0, 0}, %d) failed",
210 i, dfd, pathname, flags);
211 }
212
213 TEST(stat(pathname, &sb));
214 if (TST_RET) {
215 tst_res(TFAIL | TTERRNO, "%2d: stat() failed", i);
216 } else if (sb.st_atime || sb.st_mtime) {
217 tst_res(TFAIL, "Failed to reset access and modification time (%lu: %lu)",
218 sb.st_atime, sb.st_mtime);
219 }
220 }
221
run(unsigned int i)222 static void run(unsigned int i)
223 {
224 struct time64_variants *tv = &variants[tst_variant];
225 struct test_case *tc = &tcase[i];
226 int dfd = AT_FDCWD, fd = 0, atime_change, mtime_change;
227 struct mytime *mytime = tc->mytime;
228 char *pathname = NULL;
229 void *tsp = NULL;
230 struct stat sb;
231
232 if (tc->dirfd != AT_FDCWD)
233 dfd = SAFE_OPEN(TEST_DIR, tc->oflags);
234
235 if (tc->pathname) {
236 fd = SAFE_OPEN(tc->pathname, O_WRONLY | O_CREAT);
237 pathname = tc->pathname;
238 SAFE_CHMOD(tc->pathname, tc->mode);
239 reset_time(pathname, dfd, tc->flags, i);
240 change_attr(tc, fd, 1);
241 } else if (tc->exp_err == EFAULT) {
242 pathname = bad_addr;
243 }
244
245 if (mytime) {
246 tst_multi_set_time(tv->ts_type, mytime);
247 tsp = &ts;
248 } else if (tc->exp_err == EFAULT) {
249 tsp = bad_addr;
250 }
251
252 TEST(tv->utimensat(dfd, pathname, tsp, tc->flags));
253 if (tc->pathname)
254 change_attr(tc, fd, 0);
255
256 if (TST_RET) {
257 if (!tc->exp_err) {
258 tst_res(TFAIL | TTERRNO, "%2d: utimensat() failed", i);
259 } else if (tc->exp_err == TST_ERR) {
260 tst_res(TPASS | TTERRNO, "%2d: utimensat() failed expectedly", i);
261 } else {
262 tst_res(TFAIL | TTERRNO, "%2d: utimensat() failed with incorrect error, expected %s",
263 i, tst_strerrno(tc->exp_err));
264 }
265 } else if (tc->exp_err) {
266 tst_res(TFAIL, "%2d: utimensat() passed unexpectedly", i);
267 } else {
268 atime_change = mytime ? mytime->atime_change : 1;
269 mtime_change = mytime ? mytime->mtime_change : 1;
270
271 TEST(stat(tc->pathname ? tc->pathname : TEST_DIR, &sb));
272 if (TST_RET) {
273 tst_res(TFAIL | TTERRNO, "%2d: stat() failed", i);
274 goto close;
275 }
276
277 if (!!sb.st_atime != atime_change) {
278 tst_res(TFAIL, "%2d: atime %s have changed but %s",
279 i, atime_change ? "should" : "shouldn't",
280 sb.st_atime ? "did" : "didn't");
281 } else if (!!sb.st_mtime != mtime_change) {
282 tst_res(TFAIL, "%2d: mtime %s have changed but %s",
283 i, mtime_change ? "should" : "shouldn't",
284 sb.st_mtime ? "did" : "didn't");
285 } else {
286 tst_res(TPASS, "%2d: utimensat() passed", i);
287 }
288 }
289
290 close:
291 if (dfd != AT_FDCWD)
292 SAFE_CLOSE(dfd);
293
294 if (tc->pathname)
295 SAFE_CLOSE(fd);
296 }
297
setup(void)298 static void setup(void)
299 {
300 size_t i;
301
302 tst_res(TINFO, "Testing variant: %s", variants[tst_variant].desc);
303
304 bad_addr = tst_get_bad_addr(NULL);
305 if (access(TEST_DIR, R_OK))
306 SAFE_MKDIR(TEST_DIR, 0700);
307
308 for (i = 0; i < ARRAY_SIZE(tcase); i++)
309 update_error(&tcase[i]);
310 }
311
312 static struct tst_test test = {
313 .test = run,
314 .tcnt = ARRAY_SIZE(tcase),
315 .test_variants = ARRAY_SIZE(variants),
316 .setup = setup,
317 .needs_root = 1,
318 .mount_device = 1,
319 .mntpoint = MNTPOINT,
320 };
321