Home | History | Annotate | Download | only in utimensat
      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