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
45char *TCID = "utimensat01";
46int 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
70static inline int
71utimensat_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
77static 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
108int 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