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 28static void *bad_addr; 29 30struct 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 39static struct mytime tnn = {0, UTIME_NOW, 0, UTIME_NOW, 1, 1}; 40static struct mytime too = {0, UTIME_OMIT, 0, UTIME_OMIT, 0, 0}; 41static struct mytime tno = {0, UTIME_NOW, 0, UTIME_OMIT, 1, 0}; 42static struct mytime ton = {0, UTIME_OMIT, 0, UTIME_NOW, 0, 1}; 43static struct mytime t11 = {1, 1, 1, 1, 1, 1}; 44 45static 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 102static 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 108static 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 114static 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 124static 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 130static void 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 156static 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 179static 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 201static 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 222static 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, 0200); 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 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 290close: 291 if (dfd != AT_FDCWD) 292 SAFE_CLOSE(dfd); 293 294 if (tc->pathname) 295 SAFE_CLOSE(fd); 296} 297 298static 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 312static 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