| /*************************************************************************************/ |
| /* */ |
| /* Copyright (C) 2008, Michael Kerrisk <mtk.manpages@gmail.com>, */ |
| /* Copyright (C) 2008, Linux Foundation */ |
| /* */ |
| /* This program is free software; you can redistribute it and/or modify */ |
| /* it under the terms of the GNU General Public License as published by */ |
| /* the Free Software Foundation; either version 2 of the License, or */ |
| /* (at your option) any later version. */ |
| /* */ |
| /* This program is distributed in the hope that it will be useful, */ |
| /* but WITHOUT ANY WARRANTY; without even the implied warranty of */ |
| /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See */ |
| /* the GNU General Public License for more details. */ |
| /* */ |
| /* You should have received a copy of the GNU General Public License */ |
| /* along with this program; if not, write to the Free Software */ |
| /* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ |
| /*************************************************************************************/ |
| /* */ |
| /* File: utimnsat01.c */ |
| /* Description: A command-line interface for testing the utimensat() system call. */ |
| /* Author: Michael Kerrisk <mtk.manpages@gmail.com> */ |
| /* History: */ |
| /* 17 Mar 2008 Initial creation, */ |
| /* 31 May 2008 Reworked for easier test automation, */ |
| /* 2 June 2008 Renamed from t_utimensat.c to test_utimensat.c, */ |
| /* 05 June 2008 Submitted to LTP by Subrata Modak <subrata@linux.vnet.ibm.com> */ |
| /*************************************************************************************/ |
| |
| #define _GNU_SOURCE |
| #define _ATFILE_SOURCE |
| #include <stdio.h> |
| #include <time.h> |
| #include <errno.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <sys/syscall.h> |
| #include <fcntl.h> |
| #include <string.h> |
| #include <sys/stat.h> |
| #include "test.h" |
| #include "usctest.h" |
| #include "linux_syscall_numbers.h" |
| |
| char *TCID = "utimensat01"; /* Test program identifier. */ |
| int TST_TOTAL = 0; /* Total number of test cases. */ |
| |
| #define cleanup tst_exit |
| |
| /* We use EXIT_FAILURE for an expected failure from utimensat() |
| (e.g., EACCES and EPERM), and one of the following for unexpected |
| failures (i.e., something broke in our test setup). */ |
| |
| #ifndef AT_FDCWD |
| #define AT_FDCWD -100 |
| #endif |
| #ifndef AT_SYMLINK_NOFOLLOW |
| #define AT_SYMLINK_NOFOLLOW 0x100 |
| #endif |
| |
| #define EXIT_bad_usage 3 |
| #define EXIT_failed_syscall 3 |
| |
| #define errExit(msg) do { perror(msg); exit(EXIT_failed_syscall); \ |
| } while (0) |
| |
| # define UTIME_NOW ((1l << 30) - 1l) |
| # define UTIME_OMIT ((1l << 30) - 2l) |
| |
| static inline int |
| utimensat_sc(int dirfd, const char *pathname, |
| const struct timespec times[2], int flags) |
| { |
| return syscall(__NR_utimensat, dirfd, pathname, times, flags); |
| } |
| |
| static void usageError(char *progName) |
| { |
| fprintf(stderr, "Usage: %s pathname [atime-sec " |
| "atime-nsec mtime-sec mtime-nsec]\n\n", progName); |
| fprintf(stderr, "Permitted options are:\n"); |
| fprintf(stderr, " [-d path] " |
| "open a directory file descriptor" |
| " (instead of using AT_FDCWD)\n"); |
| fprintf(stderr, " -q Quiet\n"); |
| fprintf(stderr, " -w Open directory file " |
| "descriptor with O_RDWR|O_APPEND\n" |
| " (instead of O_RDONLY)\n"); |
| fprintf(stderr, " -n Use AT_SYMLINK_NOFOLLOW\n"); |
| fprintf(stderr, "\n"); |
| |
| fprintf(stderr, "pathname can be \"NULL\" to use NULL " |
| "argument in call\n"); |
| fprintf(stderr, "\n"); |
| |
| fprintf(stderr, "Either nsec field can be\n"); |
| fprintf(stderr, " 'n' for UTIME_NOW\n"); |
| fprintf(stderr, " 'o' for UTIME_OMIT\n"); |
| fprintf(stderr, "\n"); |
| |
| fprintf(stderr, "If the time fields are omitted, " |
| "then a NULL 'times' argument is used\n"); |
| fprintf(stderr, "\n"); |
| |
| exit(EXIT_bad_usage); |
| } |
| |
| int main(int argc, char *argv[]) |
| { |
| int flags, dirfd, opt, oflag; |
| struct timespec ts[2]; |
| struct timespec *tsp; |
| char *pathname, *dirfdPath; |
| struct stat sb; |
| int verbose; |
| |
| /* Command-line argument parsing */ |
| |
| flags = 0; |
| verbose = 1; |
| dirfd = AT_FDCWD; |
| dirfdPath = NULL; |
| oflag = O_RDONLY; |
| |
| while ((opt = getopt(argc, argv, "d:nqw")) != -1) { |
| switch (opt) { |
| case 'd': |
| dirfdPath = optarg; |
| break; |
| |
| case 'n': |
| flags |= AT_SYMLINK_NOFOLLOW; |
| if (verbose) |
| printf("Not following symbolic links\n"); |
| break; |
| |
| case 'q': |
| verbose = 0; |
| break; |
| |
| case 'w': |
| oflag = O_RDWR | O_APPEND; |
| break; |
| |
| default: |
| usageError(argv[0]); |
| } |
| } |
| |
| if ((optind + 5 != argc) && (optind + 1 != argc)) |
| usageError(argv[0]); |
| |
| if (dirfdPath != NULL) { |
| dirfd = open(dirfdPath, oflag); |
| if (dirfd == -1) |
| errExit("open"); |
| |
| if (verbose) { |
| printf("Opened dirfd %d", oflag); |
| if ((oflag & O_ACCMODE) == O_RDWR) |
| printf(" O_RDWR"); |
| if (oflag & O_APPEND) |
| printf(" O_APPEND"); |
| printf(": %s\n", dirfdPath); |
| } |
| } |
| |
| pathname = (strcmp(argv[optind], "NULL") == 0) ? NULL : argv[optind]; |
| |
| /* Either, we get no values for 'times' fields, in which case |
| we give a NULL pointer to utimensat(), or we get four values, |
| for secs+nsecs for each of atime and mtime. The special |
| values 'n' and 'o' can be used for tv_nsec settings of |
| UTIME_NOW and UTIME_OMIT, respectively. */ |
| |
| if (argc == optind + 1) { |
| tsp = NULL; |
| |
| } else { |
| ts[0].tv_sec = atoi(argv[optind + 1]); |
| if (argv[optind + 2][0] == 'n') { |
| ts[0].tv_nsec = UTIME_NOW; |
| } else if (argv[optind + 2][0] == 'o') { |
| ts[0].tv_nsec = UTIME_OMIT; |
| } else { |
| ts[0].tv_nsec = atoi(argv[optind + 2]); |
| } |
| |
| ts[1].tv_sec = atoi(argv[optind + 3]); |
| if (argv[optind + 4][0] == 'n') { |
| ts[1].tv_nsec = UTIME_NOW; |
| } else if (argv[optind + 4][0] == 'o') { |
| ts[1].tv_nsec = UTIME_OMIT; |
| } else { |
| ts[1].tv_nsec = atoi(argv[optind + 4]); |
| } |
| |
| tsp = ts; |
| } |
| |
| /* For testing purposes, it may have been useful to run this program |
| as set-user-ID-root so that a directory file descriptor could be |
| opened as root. (This allows us to obtain a file descriptor even |
| if normal user doesn't have permissions on the file.) Now we |
| reset to the real UID before making the utimensat() call, so that |
| the permission checking for the utimensat() call is performed |
| under that UID. */ |
| |
| if (geteuid() == 0) { |
| uid_t u; |
| |
| u = getuid(); |
| |
| if (verbose) |
| printf("Resetting UIDs to %ld\n", (long)u); |
| |
| if (setresuid(u, u, u) == -1) |
| errExit("setresuid"); |
| } |
| |
| /* Display information allowing user to verify arguments for call */ |
| |
| if (verbose) { |
| printf("dirfd is %d\n", dirfd); |
| printf("pathname is %s\n", pathname); |
| printf("tsp is %p", tsp); |
| if (tsp != NULL) { |
| printf("; struct = { %ld, %ld } { %ld, %ld }", |
| (long)tsp[0].tv_sec, (long)tsp[0].tv_nsec, |
| (long)tsp[1].tv_sec, (long)tsp[1].tv_nsec); |
| } |
| printf("\n"); |
| printf("flags is %d\n", flags); |
| } |
| |
| /* Make the call and see what happened */ |
| |
| if (utimensat_sc(dirfd, pathname, tsp, flags) == -1) { |
| if (errno == EPERM) { |
| if (verbose) |
| printf("utimensat() failed with EPERM\n"); |
| else |
| printf("EPERM\n"); |
| exit(EXIT_FAILURE); |
| |
| } else if (errno == EACCES) { |
| if (verbose) |
| printf("utimensat() failed with EACCES\n"); |
| else |
| printf("EACCES\n"); |
| exit(EXIT_FAILURE); |
| |
| } else if (errno == EINVAL) { |
| if (verbose) |
| printf("utimensat() failed with EINVAL\n"); |
| else |
| printf("EINVAL\n"); |
| exit(EXIT_FAILURE); |
| |
| } else { /* Unexpected failure case from utimensat() */ |
| errExit("utimensat"); |
| } |
| } |
| |
| if (verbose) |
| printf("utimensat() succeeded\n"); |
| |
| if (stat((pathname != NULL) ? pathname : dirfdPath, &sb) == -1) |
| errExit("stat"); |
| |
| if (verbose) { |
| printf("Last file access: %s", ctime(&sb.st_atime)); |
| printf("Last file modification: %s", ctime(&sb.st_mtime)); |
| printf("Last status change: %s", ctime(&sb.st_ctime)); |
| |
| } else { |
| printf("SUCCESS %ld %ld\n", (long)sb.st_atime, |
| (long)sb.st_mtime); |
| } |
| |
| exit(EXIT_SUCCESS); |
| } |