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