Cleanup timestamp conversion code
authorEric Biggers <ebiggers3@gmail.com>
Sun, 14 Dec 2014 03:26:06 +0000 (21:26 -0600)
committerEric Biggers <ebiggers3@gmail.com>
Sun, 14 Dec 2014 04:43:31 +0000 (22:43 -0600)
include/wimlib/timestamp.h
src/inode.c
src/mount_image.c
src/timestamp.c
src/unix_capture.c
src/xml.c

index 14a2eb1..ac69846 100644 (file)
@@ -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 <sys/types.h>
 #include <sys/time.h>
 #include <time.h>
 
-#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 */
index c459f3c..585ed21 100644 (file)
@@ -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;
index 2daebba..94feb4e 100644 (file)
@@ -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 */
index ebe5738..6bbdd8a 100644 (file)
@@ -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
 #  include "config.h"
 #endif
 
-#include "wimlib/types.h"
 #include "wimlib/timestamp.h"
 
-#include <time.h>
-#include <sys/time.h>
+/*
+ * 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);
 }
-
index 151b0b4..488996a 100644 (file)
@@ -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) {
index 4617e2a..1dd4d83 100644 (file)
--- 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;