From 2f79af1ffd1f92db74c69117f0c2401826126b92 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sat, 13 Dec 2014 21:26:06 -0600 Subject: [PATCH] Cleanup timestamp conversion code --- include/wimlib/timestamp.h | 80 ++++++++---------------------- src/inode.c | 2 +- src/mount_image.c | 18 +++---- src/timestamp.c | 99 +++++++++++++++++++++++++++++++++++--- src/unix_capture.c | 12 ++--- src/xml.c | 4 +- 6 files changed, 130 insertions(+), 85 deletions(-) diff --git a/include/wimlib/timestamp.h b/include/wimlib/timestamp.h index 14a2eb16..ac69846b 100644 --- a/include/wimlib/timestamp.h +++ b/include/wimlib/timestamp.h @@ -1,77 +1,39 @@ +/* + * timestamp.h + * + * Conversion between Windows NT timestamps and UNIX timestamps. + */ + #ifndef _WIMLIB_TIMESTAMP_H #define _WIMLIB_TIMESTAMP_H -#include "wimlib/types.h" -#include #include #include -#define intervals_per_second (1000000000ULL / 100ULL) -#define intervals_per_microsecond (10) -#define nanoseconds_per_interval (100) -#define days_per_year (365ULL) -#define seconds_per_day (3600ULL * 24ULL) -#define intervals_per_day (seconds_per_day * intervals_per_second) -#define intervals_per_year (intervals_per_day * days_per_year) -#define years_1601_to_1970 (1970ULL - 1601ULL) -#define leap_years_1601_to_1970 (years_1601_to_1970 / 4ULL - 3ULL) -#define intervals_1601_to_1970 (years_1601_to_1970 * intervals_per_year \ - + leap_years_1601_to_1970 * intervals_per_day) +#include "wimlib/types.h" -static inline u64 -unix_timestamp_to_wim(time_t t) -{ - return (u64)intervals_1601_to_1970 + t * intervals_per_second; -} +extern time_t +wim_timestamp_to_time_t(u64 timestamp); -/* Converts a timestamp as used in the WIM file to a UNIX timestamp as used in - * the time() function. */ -static inline time_t -wim_timestamp_to_unix(u64 timestamp) -{ - return (timestamp - intervals_1601_to_1970) / intervals_per_second; -} +extern struct timeval +wim_timestamp_to_timeval(u64 timestamp); -static inline u64 -timeval_to_wim_timestamp(const struct timeval tv) -{ - return intervals_1601_to_1970 - + (u64)tv.tv_sec * intervals_per_second - + (u64)tv.tv_usec * intervals_per_microsecond; -} +extern struct timespec +wim_timestamp_to_timespec(u64 timestamp); -static inline struct timeval -wim_timestamp_to_timeval(u64 timestamp) -{ - struct timeval tv; - tv.tv_sec = (timestamp - intervals_1601_to_1970) / intervals_per_second; - tv.tv_usec = ((timestamp - intervals_1601_to_1970) / - intervals_per_microsecond) % 1000000; - return tv; -} +extern u64 +time_t_to_wim_timestamp(time_t t); -static inline u64 -timespec_to_wim_timestamp(const struct timespec ts) -{ - return intervals_1601_to_1970 - + (u64)ts.tv_sec * intervals_per_second - + (u64)ts.tv_nsec / nanoseconds_per_interval; -} +extern u64 +timeval_to_wim_timestamp(const struct timeval *tv); -static inline struct timespec -wim_timestamp_to_timespec(u64 timestamp) -{ - struct timespec ts; - ts.tv_sec = (timestamp - intervals_1601_to_1970) / intervals_per_second; - ts.tv_nsec = ((timestamp - intervals_1601_to_1970) % intervals_per_second) * - nanoseconds_per_interval; - return ts; -} +extern u64 +timespec_to_wim_timestamp(const struct timespec *ts); extern u64 -get_wim_timestamp(void); +now_as_wim_timestamp(void); extern void wim_timestamp_to_str(u64 timestamp, tchar *buf, size_t len); -#endif +#endif /* _WIMLIB_TIMESTAMP_H */ diff --git a/src/inode.c b/src/inode.c index c459f3cb..585ed21a 100644 --- a/src/inode.c +++ b/src/inode.c @@ -49,7 +49,7 @@ new_inode(void) { struct wim_inode *inode = new_timeless_inode(); if (inode) { - u64 now = get_wim_timestamp(); + u64 now = now_as_wim_timestamp(); inode->i_creation_time = now; inode->i_last_access_time = now; inode->i_last_write_time = now; diff --git a/src/mount_image.c b/src/mount_image.c index 2daebba3..94feb4ee 100644 --- a/src/mount_image.c +++ b/src/mount_image.c @@ -590,8 +590,8 @@ inode_to_stbuf(const struct wim_inode *inode, stbuf->st_mtim = wim_timestamp_to_timespec(inode->i_last_write_time); stbuf->st_ctim = stbuf->st_mtim; #else - stbuf->st_atime = wim_timestamp_to_unix(inode->i_last_access_time); - stbuf->st_mtime = wim_timestamp_to_unix(inode->i_last_write_time); + stbuf->st_atime = wim_timestamp_to_time_t(inode->i_last_access_time); + stbuf->st_mtime = wim_timestamp_to_time_t(inode->i_last_write_time); stbuf->st_ctime = stbuf->st_mtime; #endif stbuf->st_blocks = DIV_ROUND_UP(stbuf->st_size, 512); @@ -602,7 +602,7 @@ inode_to_stbuf(const struct wim_inode *inode, static void touch_inode(struct wim_inode *inode) { - u64 now = get_wim_timestamp(); + u64 now = now_as_wim_timestamp(); inode->i_last_access_time = now; inode->i_last_write_time = now; } @@ -1982,15 +1982,15 @@ wimfs_utimens(const char *path, const struct timespec tv[2]) if (tv[0].tv_nsec != UTIME_OMIT) { if (tv[0].tv_nsec == UTIME_NOW) - inode->i_last_access_time = get_wim_timestamp(); + inode->i_last_access_time = now_as_wim_timestamp(); else - inode->i_last_access_time = timespec_to_wim_timestamp(tv[0]); + inode->i_last_access_time = timespec_to_wim_timestamp(&tv[0]); } if (tv[1].tv_nsec != UTIME_OMIT) { if (tv[1].tv_nsec == UTIME_NOW) - inode->i_last_write_time = get_wim_timestamp(); + inode->i_last_write_time = now_as_wim_timestamp(); else - inode->i_last_write_time = timespec_to_wim_timestamp(tv[1]); + inode->i_last_write_time = timespec_to_wim_timestamp(&tv[1]); } return 0; } @@ -2005,8 +2005,8 @@ wimfs_utime(const char *path, struct utimbuf *times) if (!inode) return -errno; - inode->i_last_access_time = unix_timestamp_to_wim(times->actime); - inode->i_last_write_time = unix_timestamp_to_wim(times->modtime); + inode->i_last_access_time = time_t_to_wim_timestamp(times->actime); + inode->i_last_write_time = time_t_to_wim_timestamp(times->modtime); return 0; } #endif /* !HAVE_UTIMENSAT */ diff --git a/src/timestamp.c b/src/timestamp.c index ebe57380..6bbdd8ac 100644 --- a/src/timestamp.c +++ b/src/timestamp.c @@ -1,9 +1,11 @@ /* * timestamp.c + * + * Conversion between Windows NT timestamps and UNIX timestamps. */ /* - * Copyright (C) 2012, 2013 Eric Biggers + * Copyright (C) 2012, 2013, 2014 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free @@ -23,26 +25,107 @@ # include "config.h" #endif -#include "wimlib/types.h" #include "wimlib/timestamp.h" -#include -#include +/* + * Timestamps in WIM files are Windows NT timestamps, or FILETIMEs: 64-bit + * values storing the number of 100-nanosecond ticks since January 1, 1601. + */ + +#define NANOSECONDS_PER_TICK 100 +#define TICKS_PER_SECOND (1000000000 / NANOSECONDS_PER_TICK) +#define TICKS_PER_MICROSECOND (TICKS_PER_SECOND / 1000000) + +/* + * EPOCH_DISTANCE is the number of 100-nanosecond ticks separating the + * Windows NT and UNIX epochs. This is equal to ((1970-1601)*365+89)* + * 24*60*60*10000000. 89 is the number of leap years between 1970 and 1601. + */ +#define EPOCH_DISTANCE 116444736000000000 + +#define TO_WINNT_EPOCH(timestamp) ((timestamp) + EPOCH_DISTANCE) +#define TO_UNIX_EPOCH(timestamp) ((timestamp) - EPOCH_DISTANCE) + +/* Windows NT timestamps to UNIX timestamps */ + +time_t +wim_timestamp_to_time_t(u64 timestamp) +{ + timestamp = TO_UNIX_EPOCH(timestamp); + + return timestamp / TICKS_PER_SECOND; +} + +struct timeval +wim_timestamp_to_timeval(u64 timestamp) +{ + timestamp = TO_UNIX_EPOCH(timestamp); + + return (struct timeval) { + .tv_sec = timestamp / TICKS_PER_SECOND, + .tv_usec = (timestamp % TICKS_PER_SECOND) / TICKS_PER_MICROSECOND, + }; +} + +struct timespec +wim_timestamp_to_timespec(u64 timestamp) +{ + timestamp = TO_UNIX_EPOCH(timestamp); + + return (struct timespec) { + .tv_sec = timestamp / TICKS_PER_SECOND, + .tv_nsec = (timestamp % TICKS_PER_SECOND) * NANOSECONDS_PER_TICK, + }; +} + +/* UNIX timestamps to Windows NT timestamps */ + +u64 +time_t_to_wim_timestamp(time_t t) +{ + u64 timestamp = (u64)t * TICKS_PER_SECOND; + + return TO_WINNT_EPOCH(timestamp); +} u64 -get_wim_timestamp(void) +timeval_to_wim_timestamp(const struct timeval *tv) +{ + u64 timestamp = (u64)tv->tv_sec * TICKS_PER_SECOND + + (u64)tv->tv_usec * TICKS_PER_MICROSECOND; + + return TO_WINNT_EPOCH(timestamp); +} + +u64 +timespec_to_wim_timestamp(const struct timespec *ts) +{ + u64 timestamp = (u64)ts->tv_sec * TICKS_PER_SECOND + + (u64)ts->tv_nsec / NANOSECONDS_PER_TICK; + + return TO_WINNT_EPOCH(timestamp); +} + +/* Retrieve the current time as a WIM timestamp. */ +u64 +now_as_wim_timestamp(void) { struct timeval tv; + + /* On Windows we rely on MinGW providing gettimeofday() for us. This + * could be changed to calling GetSystemTimeAsFileTime() directly, but + * now_as_wim_timestamp() isn't called much and it's simpler to keep the + * code for all platforms the same. */ gettimeofday(&tv, NULL); - return timeval_to_wim_timestamp(tv); + return timeval_to_wim_timestamp(&tv); } +/* Translate a WIM timestamp into a human-readable string. */ void wim_timestamp_to_str(u64 timestamp, tchar *buf, size_t len) { struct tm tm; - time_t t = wim_timestamp_to_unix(timestamp); + time_t t = wim_timestamp_to_time_t(timestamp); gmtime_r(&t, &tm); tstrftime(buf, len, T("%a %b %d %H:%M:%S %Y UTC"), &tm); } - diff --git a/src/unix_capture.c b/src/unix_capture.c index 151b0b47..488996aa 100644 --- a/src/unix_capture.c +++ b/src/unix_capture.c @@ -388,13 +388,13 @@ unix_build_dentry_tree_recursive(struct wim_dentry **tree_ret, goto out_progress; #ifdef HAVE_STAT_NANOSECOND_PRECISION - inode->i_creation_time = timespec_to_wim_timestamp(stbuf.st_mtim); - inode->i_last_write_time = timespec_to_wim_timestamp(stbuf.st_mtim); - inode->i_last_access_time = timespec_to_wim_timestamp(stbuf.st_atim); + inode->i_creation_time = timespec_to_wim_timestamp(&stbuf.st_mtim); + inode->i_last_write_time = timespec_to_wim_timestamp(&stbuf.st_mtim); + inode->i_last_access_time = timespec_to_wim_timestamp(&stbuf.st_atim); #else - inode->i_creation_time = unix_timestamp_to_wim(stbuf.st_mtime); - inode->i_last_write_time = unix_timestamp_to_wim(stbuf.st_mtime); - inode->i_last_access_time = unix_timestamp_to_wim(stbuf.st_atime); + inode->i_creation_time = time_t_to_wim_timestamp(stbuf.st_mtime); + inode->i_last_write_time = time_t_to_wim_timestamp(stbuf.st_mtime); + inode->i_last_access_time = time_t_to_wim_timestamp(stbuf.st_atime); #endif inode->i_resolved = 1; if (params->add_flags & WIMLIB_ADD_FLAG_UNIX_DATA) { diff --git a/src/xml.c b/src/xml.c index 4617e2ab..1dd4d839 100644 --- a/src/xml.c +++ b/src/xml.c @@ -1261,7 +1261,7 @@ xml_update_image_info(WIMStruct *wim, int image) for_dentry_in_tree(wim->image_metadata[image - 1]->root_dentry, calculate_dentry_statistics, image_info); - image_info->last_modification_time = get_wim_timestamp(); + image_info->last_modification_time = now_as_wim_timestamp(); } /* Adds an image to the XML information. */ @@ -1292,7 +1292,7 @@ xml_add_image(WIMStruct *wim, const tchar *name) wim->wim_info = wim_info; image_info->index = wim_info->num_images; - image_info->creation_time = get_wim_timestamp(); + image_info->creation_time = now_as_wim_timestamp(); xml_update_image_info(wim, image_info->index); return 0; -- 2.43.0