Update timestamp code; use utimensat()
authorEric Biggers <ebiggers3@gmail.com>
Mon, 18 Mar 2013 02:42:20 +0000 (21:42 -0500)
committerEric Biggers <ebiggers3@gmail.com>
Mon, 18 Mar 2013 02:42:20 +0000 (21:42 -0500)
doc/imagex-apply.1.in
src/add_image.c
src/extract_image.c
src/mount_image.c
src/timestamp.h
src/util.c
src/wimlib_internal.h

index 86b47f3..5911427 100644 (file)
@@ -48,7 +48,8 @@ The default (unnamed) data stream of each file
 Hard links
 .IP \[bu]
 File and directory creation, access, and modification timestamps to the nearest
-microsecond, if supported by the underlying filesystem
+100 nanoseconds, if supported by the underlying filesystem, operating system,
+and C library
 .IP \[bu]
 Symbolic links and junction points, although they will not necessarily point to
 the desired location (for example, the target of the link may contain a Windows
index 7c97499..67a354e 100644 (file)
@@ -44,7 +44,7 @@
 #include <unistd.h>
 
 #ifdef HAVE_ALLOCA_H
-#include <alloca.h>
+#  include <alloca.h>
 #endif
 
 /*
@@ -226,9 +226,9 @@ static int unix_build_dentry_tree(struct wim_dentry **root_ret,
        inode = root->d_inode;
 
 #ifdef HAVE_STAT_NANOSECOND_PRECISION
-       inode->i_creation_time = timespec_to_wim_timestamp(&root_stbuf.st_mtim);
-       inode->i_last_write_time = timespec_to_wim_timestamp(&root_stbuf.st_mtim);
-       inode->i_last_access_time = timespec_to_wim_timestamp(&root_stbuf.st_atim);
+       inode->i_creation_time = timespec_to_wim_timestamp(root_stbuf.st_mtim);
+       inode->i_last_write_time = timespec_to_wim_timestamp(root_stbuf.st_mtim);
+       inode->i_last_access_time = timespec_to_wim_timestamp(root_stbuf.st_atim);
 #else
        inode->i_creation_time = unix_timestamp_to_wim(root_stbuf.st_mtime);
        inode->i_last_write_time = unix_timestamp_to_wim(root_stbuf.st_mtime);
index dece543..19b87d4 100644 (file)
@@ -399,32 +399,53 @@ static int unix_do_apply_dentry_timestamps(const char *output_path,
        int ret;
        const struct wim_inode *inode = dentry->d_inode;
 
+#ifdef HAVE_UTIMENSAT
        /* Convert the WIM timestamps, which are accurate to 100 nanoseconds,
-        * into struct timeval's. */
-       struct timeval tv[2];
-       wim_timestamp_to_timeval(inode->i_last_access_time, &tv[0]);
-       wim_timestamp_to_timeval(inode->i_last_write_time, &tv[1]);
+        * into `struct timespec's for passing to utimensat(), which is accurate
+        * to 1 nanosecond. */
+
+       struct timespec ts[2];
+       ts[0] = wim_timestamp_to_timespec(inode->i_last_access_time);
+       ts[1] = wim_timestamp_to_timespec(inode->i_last_write_time);
+       ret = utimensat(AT_FDCWD, output_path, ts, AT_SYMLINK_NOFOLLOW);
+       if (ret)
+               ret = errno;
+#else
+       ret = ENOSYS;
+#endif
+
+       if (ret == ENOSYS) {
+               /* utimensat() not implemented or not available */
        #ifdef HAVE_LUTIMES
-       ret = lutimes(output_path, tv);
-       #else
-       ret = -1;
-       errno = ENOSYS;
+               /* Convert the WIM timestamps, which are accurate to 100
+                * nanoseconds, into `struct timeval's for passing to lutimes(),
+                * which is accurate to 1 microsecond. */
+               struct timeval tv[2];
+               tv[0] = wim_timestamp_to_timeval(inode->i_last_access_time);
+               tv[1] = wim_timestamp_to_timeval(inode->i_last_write_time);
+               ret = lutimes(output_path, tv);
+               if (ret)
+                       ret = errno;
        #endif
-       if (ret != 0) {
-               #ifdef HAVE_UTIME
-               if (errno == ENOSYS) {
-                       struct utimbuf buf;
-                       buf.actime = wim_timestamp_to_unix(inode->i_last_access_time);
-                       buf.modtime = wim_timestamp_to_unix(inode->i_last_write_time);
-                       if (utime(output_path, &buf) == 0)
-                               return 0;
-               }
-               #endif
-               if (errno != ENOSYS || args->num_lutimes_warnings < 10) {
-                       /*WARNING_WITH_ERRNO("Failed to set timestamp on file `%s',*/
-                                           /*output_path");*/
-                       args->num_lutimes_warnings++;
-               }
+       }
+
+       if (ret == ENOSYS) {
+               /* utimensat() and lutimes() both not implemented or not
+                * available */
+       #ifdef HAVE_UTIME
+               /* Convert the WIM timestamps, which are accurate to 100
+                * nanoseconds, into a `struct utimbuf's for passing to
+                * utime(), which is accurate to 1 second. */
+               struct utimbuf buf;
+               buf.actime = wim_timestamp_to_unix(inode->i_last_access_time);
+               buf.modtime = wim_timestamp_to_unix(inode->i_last_write_time);
+               ret = utime(output_path, &buf);
+       #endif
+       }
+       if (ret && args->num_utime_warnings < 10) {
+               WARNING_WITH_ERRNO("Failed to set timestamp on file `%s'",
+                                   output_path);
+               args->num_utime_warnings++;
        }
        return 0;
 }
@@ -728,12 +749,12 @@ static int extract_single_image(WIMStruct *w, int image,
        struct apply_args args;
        const struct apply_operations *ops;
 
-       args.w                    = w;
-       args.target               = target;
-       args.extract_flags        = extract_flags;
-       args.num_lutimes_warnings = 0;
-       args.stream_list          = &stream_list;
-       args.progress_func        = progress_func;
+       args.w                  = w;
+       args.target             = target;
+       args.extract_flags      = extract_flags;
+       args.num_utime_warnings = 0;
+       args.stream_list        = &stream_list;
+       args.progress_func      = progress_func;
 
        if (progress_func) {
                args.progress.extract.wimfile_name = w->filename;
index afb0905..d968876 100644 (file)
@@ -420,13 +420,26 @@ static int inode_to_stbuf(const struct wim_inode *inode,
                stbuf->st_size = 0;
        }
 
+#ifdef HAVE_STAT_NANOSECOND_PRECISION
+       stbuf->st_atim = wim_timestamp_to_timespec(inode->i_last_access_time);
+       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_ctime = wim_timestamp_to_unix(inode->i_creation_time);
+       stbuf->st_ctime = stbuf->st_mtime;
+#endif
        stbuf->st_blocks = (stbuf->st_size + 511) / 512;
        return 0;
 }
 
+static void touch_inode(struct wim_inode *inode)
+{
+       u64 now = get_wim_timestamp();
+       inode->i_last_access_time = now;
+       inode->i_last_write_time = now;
+}
+
 /* Creates a new staging file and returns its file descriptor opened for
  * writing.
  *
@@ -2241,13 +2254,13 @@ static int wimfs_utimens(const char *path, const struct timespec tv[2])
                if (tv[0].tv_nsec == UTIME_NOW)
                        inode->i_last_access_time = get_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();
                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;
 }
@@ -2296,9 +2309,8 @@ static int wimfs_write(const char *path, const char *buf, size_t size,
        if (ret == -1)
                return -errno;
 
-       now = get_wim_timestamp();
-       fd->f_inode->i_last_write_time = now;
-       fd->f_inode->i_last_access_time = now;
+       /* Update timestamps */
+       touch_inode(fd->f_inode);
        return ret;
 }
 
index 406f6a5..4dcc429 100644 (file)
 #define intervals_1601_to_1970 (years_1601_to_1970 * intervals_per_year \
                                + leap_years_1601_to_1970 * intervals_per_day)
 
-static inline u64 unix_timestamp_to_wim(time_t t)
+static inline u64
+unix_timestamp_to_wim(time_t t)
 {
        return (u64)intervals_1601_to_1970 + t * intervals_per_second;
 }
 
-
 /* 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)
+static inline time_t
+wim_timestamp_to_unix(u64 timestamp)
 {
        return (timestamp - intervals_1601_to_1970) / intervals_per_second;
 }
 
-static inline u64 timeval_to_wim_timestamp(const struct timeval *tv)
+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;
+              + (u64)tv.tv_sec * intervals_per_second
+              + (u64)tv.tv_usec * intervals_per_microsecond;
 }
 
-static inline void wim_timestamp_to_timeval(u64 timestamp, struct timeval *tv)
+static inline struct timeval
+wim_timestamp_to_timeval(u64 timestamp)
 {
-       tv->tv_sec = (timestamp - intervals_1601_to_1970) / intervals_per_second;
-       tv->tv_usec = ((timestamp - intervals_1601_to_1970) /
+       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;
 }
 
-static inline u64 timespec_to_wim_timestamp(const struct timespec *ts)
+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;
+              + (u64)ts.tv_sec * intervals_per_second
+              + (u64)ts.tv_nsec / nanoseconds_per_interval;
 }
 
-extern u64 get_wim_timestamp();
-extern void wim_timestamp_to_str(u64 timestamp, char *buf, size_t len);
+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
+get_wim_timestamp();
+
+extern void
+wim_timestamp_to_str(u64 timestamp, char *buf, size_t len);
 
 #endif
index 31b51ff..df813bb 100644 (file)
@@ -459,7 +459,7 @@ u64 get_wim_timestamp()
 {
        struct timeval tv;
        gettimeofday(&tv, NULL);
-       return timeval_to_wim_timestamp(&tv);
+       return timeval_to_wim_timestamp(tv);
 }
 
 void wim_timestamp_to_str(u64 timestamp, char *buf, size_t len)
index 3f05d88..554d3cc 100644 (file)
@@ -425,7 +425,7 @@ struct apply_args {
        WIMStruct *w;
        const char *target;
        int extract_flags;
-       unsigned num_lutimes_warnings;
+       unsigned num_utime_warnings;
        struct list_head *stream_list;
        union wimlib_progress_info progress;
 #ifdef WITH_NTFS_3G