diff options
Diffstat (limited to 'lib/util/utimens.c')
-rw-r--r-- | lib/util/utimens.c | 200 |
1 files changed, 200 insertions, 0 deletions
diff --git a/lib/util/utimens.c b/lib/util/utimens.c new file mode 100644 index 0000000..c6b8ee2 --- /dev/null +++ b/lib/util/utimens.c @@ -0,0 +1,200 @@ +/* + * SPDX-License-Identifier: ISC + * + * Copyright (c) 2015, 2018 Todd C. Miller <Todd.Miller@sudo.ws> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This is an open source non-commercial project. Dear PVS-Studio, please check it. + * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + */ + +#include <config.h> + +#if !defined(HAVE_FUTIMENS) || !defined(HAVE_UTIMENSAT) + +#include <sys/stat.h> +#include <sys/time.h> +#include <errno.h> +#include <time.h> +#if !defined(HAVE_UTIMES) || defined(HAVE_FUTIME) +# include <utime.h> +#endif + +#include "sudo_compat.h" +#include "sudo_util.h" + +#if !defined(HAVE_FUTIMES) && defined(HAVE_FUTIMESAT) +# define futimes(_f, _tv) futimesat(_f, NULL, _tv) +# define HAVE_FUTIMES +#endif + +#if defined(HAVE_ST_MTIM) +# ifdef HAVE_ST__TIM +# define ATIME_TO_TIMEVAL(_x, _y) TIMESPEC_TO_TIMEVAL((_x), &(_y)->st_atim.st__tim) +# define MTIME_TO_TIMEVAL(_x, _y) TIMESPEC_TO_TIMEVAL((_x), &(_y)->st_mtim.st__tim) +# else +# define ATIME_TO_TIMEVAL(_x, _y) TIMESPEC_TO_TIMEVAL((_x), &(_y)->st_atim) +# define MTIME_TO_TIMEVAL(_x, _y) TIMESPEC_TO_TIMEVAL((_x), &(_y)->st_mtim) +# endif +#elif defined(HAVE_ST_MTIMESPEC) +# define ATIME_TO_TIMEVAL(_x, _y) TIMESPEC_TO_TIMEVAL((_x), &(_y)->st_atimespec) +# define MTIME_TO_TIMEVAL(_x, _y) TIMESPEC_TO_TIMEVAL((_x), &(_y)->st_mtimespec) +#elif defined(HAVE_ST_NMTIME) +# define ATIME_TO_TIMEVAL(_x, _y) do { (_x)->tv_sec = (_y)->st_atime; (_x)->tv_usec = (_y)->st_natime; } while (0) +# define MTIME_TO_TIMEVAL(_x, _y) do { (_x)->tv_sec = (_y)->st_mtime; (_x)->tv_usec = (_y)->st_nmtime; } while (0) +#else +# define ATIME_TO_TIMEVAL(_x, _y) do { (_x)->tv_sec = (_y)->st_atime; (_x)->tv_usec = 0; } while (0) +# define MTIME_TO_TIMEVAL(_x, _y) do { (_x)->tv_sec = (_y)->st_mtime; (_x)->tv_usec = 0; } while (0) +#endif /* HAVE_ST_MTIM */ + +/* + * Convert the pair of timespec structs passed to futimens() / utimensat() + * to a pair of timeval structs, handling UTIME_OMIT and UTIME_NOW. + * Returns 0 on success and -1 on failure (setting errno). + */ +static int +utimens_ts_to_tv(int fd, const char *file, const struct timespec *ts, + struct timeval *tv) +{ + TIMESPEC_TO_TIMEVAL(&tv[0], &ts[0]); + TIMESPEC_TO_TIMEVAL(&tv[1], &ts[1]); + if (ts[0].tv_nsec == UTIME_OMIT || ts[1].tv_nsec == UTIME_OMIT) { + struct stat sb; + + if (fd != -1) { + /* For futimens() */ + if (fstat(fd, &sb) == -1) + return -1; + } else { + /* For utimensat() */ + if (stat(file, &sb) == -1) + return -1; + } + if (ts[0].tv_nsec == UTIME_OMIT) + ATIME_TO_TIMEVAL(&tv[0], &sb); + if (ts[1].tv_nsec == UTIME_OMIT) + MTIME_TO_TIMEVAL(&tv[1], &sb); + } + if (ts[0].tv_nsec == UTIME_NOW || ts[1].tv_nsec == UTIME_NOW) { + struct timeval now; + + if (gettimeofday(&now, NULL) == -1) + return -1; + if (ts[0].tv_nsec == UTIME_NOW) + tv[0] = now; + if (ts[1].tv_nsec == UTIME_NOW) + tv[1] = now; + } + return 0; +} + +#if defined(HAVE_FUTIMES) +/* + * Emulate futimens() via futimes() + */ +int +sudo_futimens(int fd, const struct timespec *ts) +{ + struct timeval tv[2], *times = NULL; + + if (ts != NULL) { + if (utimens_ts_to_tv(fd, NULL, ts, tv) == -1) + return -1; + times = tv; + } + return futimes(fd, times); +} +#elif defined(HAVE_FUTIME) +/* + * Emulate futimens() via futime() + */ +int +sudo_futimens(int fd, const struct timespec *ts) +{ + struct utimbuf utb, *times = NULL; + + if (ts != NULL) { + struct timeval tv[2]; + + if (utimens_ts_to_tv(fd, NULL, ts, tv) == -1) + return -1; + utb.actime = (time_t)(tv[0].tv_sec + tv[0].tv_usec / 1000000); + utb.modtime = (time_t)(tv[1].tv_sec + tv[1].tv_usec / 1000000); + times = &utb; + } + return futime(fd, times); +} +#else +/* + * Nothing to do but fail. + */ +int +sudo_futimens(int fd, const struct timespec *ts) +{ + errno = ENOSYS; + return -1; +} +#endif /* HAVE_FUTIMES */ + +#if defined(HAVE_UTIMES) +/* + * Emulate utimensat() via utimes() + */ +int +sudo_utimensat(int fd, const char *file, const struct timespec *ts, int flag) +{ + struct timeval tv[2], *times = NULL; + + if (fd != AT_FDCWD || flag != 0) { + errno = ENOTSUP; + return -1; + } + + if (ts != NULL) { + if (utimens_ts_to_tv(-1, file, ts, tv) == -1) + return -1; + times = tv; + } + return utimes(file, times); +} +#else +/* + * Emulate utimensat() via utime() + */ +int +sudo_utimensat(int fd, const char *file, const struct timespec *ts, int flag) +{ + struct utimbuf utb, *times = NULL; + + if (fd != AT_FDCWD || flag != 0) { + errno = ENOTSUP; + return -1; + } + + if (ts != NULL) { + struct timeval tv[2]; + + if (utimens_ts_to_tv(-1, file, ts, tv) == -1) + return -1; + utb.actime = (time_t)(tv[0].tv_sec + tv[0].tv_usec / 1000000); + utb.modtime = (time_t)(tv[1].tv_sec + tv[1].tv_usec / 1000000); + times = &utb; + } + return utime(file, times); +} +#endif /* !HAVE_UTIMES */ + +#endif /* !HAVE_FUTIMENS && !HAVE_UTIMENSAT */ |