1 /*************************************************************************************/
2 /* */
3 /* Copyright (C) 2008, Michael Kerrisk <mtk.manpages@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@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@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
utimensat_sc(int dirfd,const char * pathname,const struct timespec times[2],int flags)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
usageError(char * progName)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
main(int argc,char * argv[])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