1 /*************************************************************************************/ 2 /* */ 3 /* Copyright (C) 2008, Michael Kerrisk <mtk.manpages (at) gmail.com>, */ 4 /* Copyright (C) 2008, Linux Foundation */ 5 /* */ 6 /* This program is free software; you can redistribute it and/or modify */ 7 /* it under the terms of the GNU General Public License as published by */ 8 /* the Free Software Foundation; either version 2 of the License, or */ 9 /* (at your option) any later version. */ 10 /* */ 11 /* This program is distributed in the hope that it will be useful, */ 12 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */ 13 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See */ 14 /* the GNU General Public License for more details. */ 15 /* */ 16 /* You should have received a copy of the GNU General Public License */ 17 /* along with this program; if not, write to the Free Software */ 18 /* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ 19 /*************************************************************************************/ 20 /* */ 21 /* File: utimnsat01.c */ 22 /* Description: A command-line interface for testing the utimensat() system call. */ 23 /* Author: Michael Kerrisk <mtk.manpages (at) gmail.com> */ 24 /* History: */ 25 /* 17 Mar 2008 Initial creation, */ 26 /* 31 May 2008 Reworked for easier test automation, */ 27 /* 2 June 2008 Renamed from t_utimensat.c to test_utimensat.c, */ 28 /* 05 June 2008 Submitted to LTP by Subrata Modak <subrata (at) linux.vnet.ibm.com> */ 29 /*************************************************************************************/ 30 31 #define _GNU_SOURCE 32 #define _ATFILE_SOURCE 33 #include <stdio.h> 34 #include <time.h> 35 #include <errno.h> 36 #include <stdlib.h> 37 #include <unistd.h> 38 #include <sys/syscall.h> 39 #include <fcntl.h> 40 #include <string.h> 41 #include <sys/stat.h> 42 #include "test.h" 43 #include "lapi/syscalls.h" 44 45 char *TCID = "utimensat01"; 46 int TST_TOTAL = 0; 47 48 #define cleanup tst_exit 49 50 /* We use EXIT_FAILURE for an expected failure from utimensat() 51 (e.g., EACCES and EPERM), and one of the following for unexpected 52 failures (i.e., something broke in our test setup). */ 53 54 #ifndef AT_FDCWD 55 #define AT_FDCWD -100 56 #endif 57 #ifndef AT_SYMLINK_NOFOLLOW 58 #define AT_SYMLINK_NOFOLLOW 0x100 59 #endif 60 61 #define EXIT_bad_usage 3 62 #define EXIT_failed_syscall 3 63 64 #define errExit(msg) do { perror(msg); exit(EXIT_failed_syscall); \ 65 } while (0) 66 67 #define UTIME_NOW ((1l << 30) - 1l) 68 #define UTIME_OMIT ((1l << 30) - 2l) 69 70 static inline int 71 utimensat_sc(int dirfd, const char *pathname, 72 const struct timespec times[2], int flags) 73 { 74 return ltp_syscall(__NR_utimensat, dirfd, pathname, times, flags); 75 } 76 77 static void usageError(char *progName) 78 { 79 fprintf(stderr, "Usage: %s pathname [atime-sec " 80 "atime-nsec mtime-sec mtime-nsec]\n\n", progName); 81 fprintf(stderr, "Permitted options are:\n"); 82 fprintf(stderr, " [-d path] " 83 "open a directory file descriptor" 84 " (instead of using AT_FDCWD)\n"); 85 fprintf(stderr, " -q Quiet\n"); 86 fprintf(stderr, " -w Open directory file " 87 "descriptor with O_RDWR|O_APPEND\n" 88 " (instead of O_RDONLY)\n"); 89 fprintf(stderr, " -n Use AT_SYMLINK_NOFOLLOW\n"); 90 fprintf(stderr, "\n"); 91 92 fprintf(stderr, "pathname can be \"NULL\" to use NULL " 93 "argument in call\n"); 94 fprintf(stderr, "\n"); 95 96 fprintf(stderr, "Either nsec field can be\n"); 97 fprintf(stderr, " 'n' for UTIME_NOW\n"); 98 fprintf(stderr, " 'o' for UTIME_OMIT\n"); 99 fprintf(stderr, "\n"); 100 101 fprintf(stderr, "If the time fields are omitted, " 102 "then a NULL 'times' argument is used\n"); 103 fprintf(stderr, "\n"); 104 105 exit(EXIT_bad_usage); 106 } 107 108 int main(int argc, char *argv[]) 109 { 110 int flags, dirfd, opt, oflag; 111 struct timespec ts[2]; 112 struct timespec *tsp; 113 char *pathname, *dirfdPath; 114 struct stat sb; 115 int verbose; 116 117 /* Command-line argument parsing */ 118 119 flags = 0; 120 verbose = 1; 121 dirfd = AT_FDCWD; 122 dirfdPath = NULL; 123 oflag = O_RDONLY; 124 125 while ((opt = getopt(argc, argv, "d:nqw")) != -1) { 126 switch (opt) { 127 case 'd': 128 dirfdPath = optarg; 129 break; 130 131 case 'n': 132 flags |= AT_SYMLINK_NOFOLLOW; 133 if (verbose) 134 printf("Not following symbolic links\n"); 135 break; 136 137 case 'q': 138 verbose = 0; 139 break; 140 141 case 'w': 142 oflag = O_RDWR | O_APPEND; 143 break; 144 145 default: 146 usageError(argv[0]); 147 } 148 } 149 150 if ((optind + 5 != argc) && (optind + 1 != argc)) 151 usageError(argv[0]); 152 153 if (dirfdPath != NULL) { 154 dirfd = open(dirfdPath, oflag); 155 if (dirfd == -1) 156 errExit("open"); 157 158 if (verbose) { 159 printf("Opened dirfd %d", oflag); 160 if ((oflag & O_ACCMODE) == O_RDWR) 161 printf(" O_RDWR"); 162 if (oflag & O_APPEND) 163 printf(" O_APPEND"); 164 printf(": %s\n", dirfdPath); 165 } 166 } 167 168 pathname = (strcmp(argv[optind], "NULL") == 0) ? NULL : argv[optind]; 169 170 /* Either, we get no values for 'times' fields, in which case 171 we give a NULL pointer to utimensat(), or we get four values, 172 for secs+nsecs for each of atime and mtime. The special 173 values 'n' and 'o' can be used for tv_nsec settings of 174 UTIME_NOW and UTIME_OMIT, respectively. */ 175 176 if (argc == optind + 1) { 177 tsp = NULL; 178 179 } else { 180 ts[0].tv_sec = atoi(argv[optind + 1]); 181 if (argv[optind + 2][0] == 'n') { 182 ts[0].tv_nsec = UTIME_NOW; 183 } else if (argv[optind + 2][0] == 'o') { 184 ts[0].tv_nsec = UTIME_OMIT; 185 } else { 186 ts[0].tv_nsec = atoi(argv[optind + 2]); 187 } 188 189 ts[1].tv_sec = atoi(argv[optind + 3]); 190 if (argv[optind + 4][0] == 'n') { 191 ts[1].tv_nsec = UTIME_NOW; 192 } else if (argv[optind + 4][0] == 'o') { 193 ts[1].tv_nsec = UTIME_OMIT; 194 } else { 195 ts[1].tv_nsec = atoi(argv[optind + 4]); 196 } 197 198 tsp = ts; 199 } 200 201 /* For testing purposes, it may have been useful to run this program 202 as set-user-ID-root so that a directory file descriptor could be 203 opened as root. (This allows us to obtain a file descriptor even 204 if normal user doesn't have permissions on the file.) Now we 205 reset to the real UID before making the utimensat() call, so that 206 the permission checking for the utimensat() call is performed 207 under that UID. */ 208 209 if (geteuid() == 0) { 210 uid_t u; 211 212 u = getuid(); 213 214 if (verbose) 215 printf("Resetting UIDs to %ld\n", (long)u); 216 217 if (setresuid(u, u, u) == -1) 218 errExit("setresuid"); 219 } 220 221 /* Display information allowing user to verify arguments for call */ 222 223 if (verbose) { 224 printf("dirfd is %d\n", dirfd); 225 printf("pathname is %s\n", pathname); 226 printf("tsp is %p", tsp); 227 if (tsp != NULL) { 228 printf("; struct = { %ld, %ld } { %ld, %ld }", 229 (long)tsp[0].tv_sec, (long)tsp[0].tv_nsec, 230 (long)tsp[1].tv_sec, (long)tsp[1].tv_nsec); 231 } 232 printf("\n"); 233 printf("flags is %d\n", flags); 234 } 235 236 /* Make the call and see what happened */ 237 238 if (utimensat_sc(dirfd, pathname, tsp, flags) == -1) { 239 if (errno == EPERM) { 240 if (verbose) 241 printf("utimensat() failed with EPERM\n"); 242 else 243 printf("EPERM\n"); 244 exit(EXIT_FAILURE); 245 246 } else if (errno == EACCES) { 247 if (verbose) 248 printf("utimensat() failed with EACCES\n"); 249 else 250 printf("EACCES\n"); 251 exit(EXIT_FAILURE); 252 253 } else if (errno == EINVAL) { 254 if (verbose) 255 printf("utimensat() failed with EINVAL\n"); 256 else 257 printf("EINVAL\n"); 258 exit(EXIT_FAILURE); 259 260 } else { /* Unexpected failure case from utimensat() */ 261 errExit("utimensat"); 262 } 263 } 264 265 if (verbose) 266 printf("utimensat() succeeded\n"); 267 268 if (stat((pathname != NULL) ? pathname : dirfdPath, &sb) == -1) 269 errExit("stat"); 270 271 if (verbose) { 272 printf("Last file access: %s", ctime(&sb.st_atime)); 273 printf("Last file modification: %s", ctime(&sb.st_mtime)); 274 printf("Last status change: %s", ctime(&sb.st_ctime)); 275 276 } else { 277 printf("SUCCESS %ld %ld\n", (long)sb.st_atime, 278 (long)sb.st_mtime); 279 } 280 281 exit(EXIT_SUCCESS); 282 } 283