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