[Libguestfs] [PATCH] New APIs: Implement stat calls that return nanosecond timestamps (RHBZ#1144891).

Richard W.M. Jones rjones at redhat.com
Mon Sep 22 12:48:38 UTC 2014


The existing APIs guestfs_stat, guestfs_lstat and guestfs_lstatlist
return a stat structure that contains atime, mtime and ctime fields
that store only the timestamp in seconds.

Modern filesystems can store timestamps down to nanosecond
granularity, and the ordinary glibc stat(2) wrapper will return these
in "hidden" stat fields:

  struct timespec st_atim;            /* Time of last access.  */
  struct timespec st_mtim;            /* Time of last modification.  */
  struct timespec st_ctim;            /* Time of last status change.  */

with the following macros defined for backwards compatibility:

  #define st_atime st_atim.tv_sec
  #define st_mtime st_mtim.tv_sec
  #define st_ctime st_ctim.tv_sec

It is not possible to redefine guestfs_stat to return a longer struct
guestfs_stat with room for the extra nanosecond fields, because that
would break the ABI of guestfs_lstatlist as it returns an array
containing consecutive stat structs (not pointers).  Changing the
return type of guestfs_stat would break API.  Changing the generator
to support symbol versioning is judged to be too intrusive.

Therefore this adds a new struct (guestfs_statns) and new APIs:

  guestfs_statns
  guestfs_lstatns
  guestfs_lstatnslist

which return the new struct (or array of structs in the last case).

The old APIs may of course still be used, forever, but are deprecated
and shouldn't be used in new programs.

Because virt tools are compiled with -DGUESTFS_WARN_DEPRECATED=1, I
have updated all the places calling the deprecated functions.  This
has revealed some areas for improvement: in particular virt-diff and
virt-ls could be changed to print the nanosecond fields.

FUSE now returns nanoseconds in stat calls where available, fixing
https://bugzilla.redhat.com/show_bug.cgi?id=1144891

Notes about the implementation:

- guestfs_internal_lstatlist has been removed and replaced by
  guestfs_internal_lstatnslist.  As the former was an internal API no
  one should have been calling it, or indeed can call it unless they
  start defining their own header files.

- guestfs_stat and guestfs_lstat have been changed into library-side
  functions.  They, along with guestfs_lstatlist, are now implemented
  as wrappers around the new functions which just throw away the
  nanosecond fields.
---
 TODO                                     |   6 ++
 cat/ls.c                                 |  58 +++++------
 cat/visit.c                              |  10 +-
 cat/visit.h                              |   2 +-
 configure.ac                             |   6 ++
 daemon/stat.c                            | 168 +++++++++++++------------------
 diff/diff.c                              | 119 +++++++++++-----------
 fuse/test-fuse.c                         |   4 +-
 generator/actions.ml                     | 151 ++++++++++++++++-----------
 generator/structs.ml                     |  30 ++++++
 gobject/Makefile.inc                     |   2 +
 java/Makefile.inc                        |   1 +
 java/com/redhat/et/libguestfs/.gitignore |   1 +
 po/POTFILES                              |   1 +
 src/MAX_PROC_NR                          |   2 +-
 src/file.c                               |  90 +++++++++++++++--
 src/fuse.c                               |  82 +++++++++------
 v2v/convert_linux.ml                     |   8 +-
 18 files changed, 443 insertions(+), 298 deletions(-)

diff --git a/TODO b/TODO
index ea19795..e6af186 100644
--- a/TODO
+++ b/TODO
@@ -598,3 +598,9 @@ Improvements in virt-log
 
 - Support Windows guests, see
   http://rwmj.wordpress.com/2011/04/17/decoding-the-windows-event-log-using-guestfish/
+
+Subsecond handling in virt-diff, virt-ls
+----------------------------------------
+
+Handle nanoseconds properly.  You should be able to specify them on
+the command line and display them.
diff --git a/cat/ls.c b/cat/ls.c
index 43705c2..de8248e 100644
--- a/cat/ls.c
+++ b/cat/ls.c
@@ -71,7 +71,7 @@ static void output_int64 (int64_t);
 static void output_int64_dev (int64_t);
 static void output_int64_perms (int64_t);
 static void output_int64_size (int64_t);
-static void output_int64_time (int64_t);
+static void output_int64_time (int64_t secs, int64_t nsecs);
 static void output_int64_uid (int64_t);
 static void output_string (const char *);
 static void output_string_link (const char *);
@@ -449,7 +449,7 @@ do_ls_R (const char *dir)
   return 0;
 }
 
-static int show_file (const char *dir, const char *name, const struct guestfs_stat *stat, const struct guestfs_xattr_list *xattrs, void *unused);
+static int show_file (const char *dir, const char *name, const struct guestfs_statns *stat, const struct guestfs_xattr_list *xattrs, void *unused);
 
 static int
 do_ls_lR (const char *dir)
@@ -466,7 +466,7 @@ do_ls_lR (const char *dir)
  */
 static int
 show_file (const char *dir, const char *name,
-           const struct guestfs_stat *stat,
+           const struct guestfs_statns *stat,
            const struct guestfs_xattr_list *xattrs,
            void *unused)
 {
@@ -476,45 +476,45 @@ show_file (const char *dir, const char *name,
   /* Display the basic fields. */
   output_start_line ();
 
-  if (is_reg (stat->mode))
+  if (is_reg (stat->st_mode))
     filetype = "-";
-  else if (is_dir (stat->mode))
+  else if (is_dir (stat->st_mode))
     filetype = "d";
-  else if (is_chr (stat->mode))
+  else if (is_chr (stat->st_mode))
     filetype = "c";
-  else if (is_blk (stat->mode))
+  else if (is_blk (stat->st_mode))
     filetype = "b";
-  else if (is_fifo (stat->mode))
+  else if (is_fifo (stat->st_mode))
     filetype = "p";
-  else if (is_lnk (stat->mode))
+  else if (is_lnk (stat->st_mode))
     filetype = "l";
-  else if (is_sock (stat->mode))
+  else if (is_sock (stat->st_mode))
     filetype = "s";
   else
     filetype = "u";
   output_string (filetype);
-  output_int64_perms (stat->mode & 07777);
+  output_int64_perms (stat->st_mode & 07777);
 
-  output_int64_size (stat->size);
+  output_int64_size (stat->st_size);
 
   /* Display extra fields when enabled. */
   if (enable_uids) {
-    output_int64_uid (stat->uid);
-    output_int64_uid (stat->gid);
+    output_int64_uid (stat->st_uid);
+    output_int64_uid (stat->st_gid);
   }
 
   if (enable_times) {
-    output_int64_time (stat->atime);
-    output_int64_time (stat->mtime);
-    output_int64_time (stat->ctime);
+    output_int64_time (stat->st_atime_sec, stat->st_atime_nsec);
+    output_int64_time (stat->st_mtime_sec, stat->st_mtime_nsec);
+    output_int64_time (stat->st_ctime_sec, stat->st_ctime_nsec);
   }
 
   if (enable_extra_stats) {
-    output_int64_dev (stat->dev);
-    output_int64 (stat->ino);
-    output_int64 (stat->nlink);
-    output_int64_dev (stat->rdev);
-    output_int64 (stat->blocks);
+    output_int64_dev (stat->st_dev);
+    output_int64 (stat->st_ino);
+    output_int64 (stat->st_nlink);
+    output_int64_dev (stat->st_rdev);
+    output_int64 (stat->st_blocks);
   }
 
   /* Disabled for now -- user would definitely want these to be interpreted.
@@ -524,7 +524,7 @@ show_file (const char *dir, const char *name,
 
   path = full_path (dir, name);
 
-  if (checksum && is_reg (stat->mode)) {
+  if (checksum && is_reg (stat->st_mode)) {
     csum = guestfs_checksum (g, checksum, path);
     if (!csum)
       exit (EXIT_FAILURE);
@@ -534,7 +534,7 @@ show_file (const char *dir, const char *name,
 
   output_string (path);
 
-  if (is_lnk (stat->mode))
+  if (is_lnk (stat->st_mode))
     /* XXX Fix this for NTFS. */
     link = guestfs_readlink (g, path);
   if (link)
@@ -703,7 +703,7 @@ output_int64_perms (int64_t i)
 }
 
 static void
-output_int64_time (int64_t i)
+output_int64_time (int64_t secs, int64_t nsecs)
 {
   int r;
 
@@ -713,19 +713,19 @@ output_int64_time (int64_t i)
   if (time_t_output) {
     switch (time_relative) {
     case 0:                     /* --time-t */
-      r = printf ("%10" PRIi64, i);
+      r = printf ("%10" PRIi64, secs);
       break;
     case 1:                     /* --time-relative */
-      r = printf ("%8" PRIi64, now - i);
+      r = printf ("%8" PRIi64, now - secs);
       break;
     case 2:                     /* --time-days */
     default:
-      r = printf ("%3" PRIi64, (now - i) / 86400);
+      r = printf ("%3" PRIi64, (now - secs) / 86400);
       break;
     }
   }
   else {
-    time_t t = (time_t) i;
+    time_t t = (time_t) secs;
     char buf[64];
     struct tm *tm;
 
diff --git a/cat/visit.c b/cat/visit.c
index 2347b56..963beb8 100644
--- a/cat/visit.c
+++ b/cat/visit.c
@@ -51,11 +51,11 @@ _visit (guestfs_h *g, int depth, const char *dir,
    * case.
    */
   if (depth == 0) {
-    CLEANUP_FREE_STAT struct guestfs_stat *stat = NULL;
+    CLEANUP_FREE_STATNS struct guestfs_statns *stat = NULL;
     CLEANUP_FREE_XATTR_LIST struct guestfs_xattr_list *xattrs = NULL;
     int r;
 
-    stat = guestfs_lstat (g, dir);
+    stat = guestfs_lstatns (g, dir);
     if (stat == NULL)
       return -1;
 
@@ -71,14 +71,14 @@ _visit (guestfs_h *g, int depth, const char *dir,
 
   size_t i, xattrp;
   CLEANUP_FREE_STRING_LIST char **names = NULL;
-  CLEANUP_FREE_STAT_LIST struct guestfs_stat_list *stats = NULL;
+  CLEANUP_FREE_STAT_LIST struct guestfs_statns_list *stats = NULL;
   CLEANUP_FREE_XATTR_LIST struct guestfs_xattr_list *xattrs = NULL;
 
   names = guestfs_ls (g, dir);
   if (names == NULL)
     return -1;
 
-  stats = guestfs_lstatlist (g, dir, names);
+  stats = guestfs_lstatnslist (g, dir, names);
   if (stats == NULL)
     return -1;
 
@@ -123,7 +123,7 @@ _visit (guestfs_h *g, int depth, const char *dir,
       return -1;
 
     /* Recursively call visit, but only on directories. */
-    if (is_dir (stats->val[i].mode)) {
+    if (is_dir (stats->val[i].st_mode)) {
       path = full_path (dir, names[i]);
       if (_visit (g, depth + 1, path, f, opaque) == -1)
         return -1;
diff --git a/cat/visit.h b/cat/visit.h
index f6b538b..a64b42e 100644
--- a/cat/visit.h
+++ b/cat/visit.h
@@ -19,7 +19,7 @@
 #ifndef VISIT_H
 #define VISIT_H
 
-typedef int (*visitor_function) (const char *dir, const char *name, const struct guestfs_stat *stat, const struct guestfs_xattr_list *xattrs, void *opaque);
+typedef int (*visitor_function) (const char *dir, const char *name, const struct guestfs_statns *stat, const struct guestfs_xattr_list *xattrs, void *opaque);
 
 extern int visit (guestfs_h *g, const char *dir, visitor_function f, void *opaque);
 
diff --git a/configure.ac b/configure.ac
index 0b2c0e0..01f1b3b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -270,6 +270,12 @@ dnl Check if stat has the required fields.
 AC_STRUCT_ST_BLOCKS
 AC_CHECK_MEMBER([struct stat.st_blksize],[
     AC_DEFINE([HAVE_STRUCT_STAT_ST_BLKSIZE],[1],[Define to 1 if 'st_blksize' is a member of 'struct stat'.])])
+AC_CHECK_MEMBER([struct stat.st_atim.tv_nsec],[
+    AC_DEFINE([HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC],[1],[Define to 1 if 'st_mtim.tv_nsec' is a member of 'struct stat'.])])
+AC_CHECK_MEMBER([struct stat.st_mtim.tv_nsec],[
+    AC_DEFINE([HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC],[1],[Define to 1 if 'st_mtim.tv_nsec' is a member of 'struct stat'.])])
+AC_CHECK_MEMBER([struct stat.st_ctim.tv_nsec],[
+    AC_DEFINE([HAVE_STRUCT_STAT_ST_CTIM_TV_NSEC],[1],[Define to 1 if 'st_mtim.tv_nsec' is a member of 'struct stat'.])])
 
 dnl Define a C symbol for the host CPU architecture.
 AC_DEFINE_UNQUOTED([host_cpu],["$host_cpu"],[Host architecture.])
diff --git a/daemon/stat.c b/daemon/stat.c
index 939fe08..a784914 100644
--- a/daemon/stat.c
+++ b/daemon/stat.c
@@ -30,11 +30,64 @@
 #include "daemon.h"
 #include "actions.h"
 
-guestfs_int_stat *
-do_stat (const char *path)
+static guestfs_int_statns *
+stat_to_statns (guestfs_int_statns *ret, const struct stat *statbuf)
+{
+  if (ret == NULL) {
+    ret = malloc (sizeof *ret);
+    if (ret == NULL) {
+      reply_with_perror ("malloc");
+      return NULL;
+    }
+  }
+
+  ret->st_dev = statbuf->st_dev;
+  ret->st_ino = statbuf->st_ino;
+  ret->st_mode = statbuf->st_mode;
+  ret->st_nlink = statbuf->st_nlink;
+  ret->st_uid = statbuf->st_uid;
+  ret->st_gid = statbuf->st_gid;
+  ret->st_rdev = statbuf->st_rdev;
+  ret->st_size = statbuf->st_size;
+#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
+  ret->st_blksize = statbuf->st_blksize;
+#else
+  ret->st_blksize = -1;
+#endif
+#ifdef HAVE_STRUCT_STAT_ST_BLOCKS
+  ret->st_blocks = statbuf->st_blocks;
+#else
+  ret->st_blocks = -1;
+#endif
+  ret->st_atime_sec = statbuf->st_atime;
+#ifdef HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC
+  ret->st_atime_nsec = statbuf->st_atim.tv_nsec;
+#else
+  ret->st_atime_nsec = 0;
+#endif
+  ret->st_mtime_sec = statbuf->st_mtime;
+#ifdef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
+  ret->st_mtime_nsec = statbuf->st_mtim.tv_nsec;
+#else
+  ret->st_mtime_nsec = 0;
+#endif
+  ret->st_ctime_sec = statbuf->st_ctime;
+#ifdef HAVE_STRUCT_STAT_ST_CTIM_TV_NSEC
+  ret->st_ctime_nsec = statbuf->st_ctim.tv_nsec;
+#else
+  ret->st_ctime_nsec = 0;
+#endif
+
+  ret->st_spare1 = ret->st_spare2 = ret->st_spare3 =
+    ret->st_spare4 = ret->st_spare5 = ret->st_spare6 = 0;
+
+  return ret;
+}
+
+guestfs_int_statns *
+do_statns (const char *path)
 {
   int r;
-  guestfs_int_stat *ret;
   struct stat statbuf;
 
   CHROOT_IN;
@@ -46,42 +99,13 @@ do_stat (const char *path)
     return NULL;
   }
 
-  ret = malloc (sizeof *ret);
-  if (ret == NULL) {
-    reply_with_perror ("malloc");
-    return NULL;
-  }
-
-  ret->dev = statbuf.st_dev;
-  ret->ino = statbuf.st_ino;
-  ret->mode = statbuf.st_mode;
-  ret->nlink = statbuf.st_nlink;
-  ret->uid = statbuf.st_uid;
-  ret->gid = statbuf.st_gid;
-  ret->rdev = statbuf.st_rdev;
-  ret->size = statbuf.st_size;
-#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
-  ret->blksize = statbuf.st_blksize;
-#else
-  ret->blksize = -1;
-#endif
-#ifdef HAVE_STRUCT_STAT_ST_BLOCKS
-  ret->blocks = statbuf.st_blocks;
-#else
-  ret->blocks = -1;
-#endif
-  ret->atime = statbuf.st_atime;
-  ret->mtime = statbuf.st_mtime;
-  ret->ctime = statbuf.st_ctime;
-
-  return ret;
+  return stat_to_statns (NULL, &statbuf);
 }
 
-guestfs_int_stat *
-do_lstat (const char *path)
+guestfs_int_statns *
+do_lstatns (const char *path)
 {
   int r;
-  guestfs_int_stat *ret;
   struct stat statbuf;
 
   CHROOT_IN;
@@ -93,42 +117,14 @@ do_lstat (const char *path)
     return NULL;
   }
 
-  ret = malloc (sizeof *ret);
-  if (ret == NULL) {
-    reply_with_perror ("malloc");
-    return NULL;
-  }
-
-  ret->dev = statbuf.st_dev;
-  ret->ino = statbuf.st_ino;
-  ret->mode = statbuf.st_mode;
-  ret->nlink = statbuf.st_nlink;
-  ret->uid = statbuf.st_uid;
-  ret->gid = statbuf.st_gid;
-  ret->rdev = statbuf.st_rdev;
-  ret->size = statbuf.st_size;
-#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
-  ret->blksize = statbuf.st_blksize;
-#else
-  ret->blksize = -1;
-#endif
-#ifdef HAVE_STRUCT_STAT_ST_BLOCKS
-  ret->blocks = statbuf.st_blocks;
-#else
-  ret->blocks = -1;
-#endif
-  ret->atime = statbuf.st_atime;
-  ret->mtime = statbuf.st_mtime;
-  ret->ctime = statbuf.st_ctime;
-
-  return ret;
+  return stat_to_statns (NULL, &statbuf);
 }
 
-guestfs_int_stat_list *
-do_internal_lstatlist (const char *path, char *const *names)
+guestfs_int_statns_list *
+do_internal_lstatnslist (const char *path, char *const *names)
 {
   int path_fd;
-  guestfs_int_stat_list *ret;
+  guestfs_int_statns_list *ret;
   size_t i, nr_names;
 
   nr_names = count_strings (names);
@@ -138,9 +134,10 @@ do_internal_lstatlist (const char *path, char *const *names)
     reply_with_perror ("malloc");
     return NULL;
   }
-  ret->guestfs_int_stat_list_len = nr_names;
-  ret->guestfs_int_stat_list_val = calloc (nr_names, sizeof (guestfs_int_stat));
-  if (ret->guestfs_int_stat_list_val == NULL) {
+  ret->guestfs_int_statns_list_len = nr_names;
+  ret->guestfs_int_statns_list_val =
+    calloc (nr_names, sizeof (guestfs_int_statns));
+  if (ret->guestfs_int_statns_list_val == NULL) {
     reply_with_perror ("malloc");
     free (ret);
     return NULL;
@@ -152,7 +149,7 @@ do_internal_lstatlist (const char *path, char *const *names)
 
   if (path_fd == -1) {
     reply_with_perror ("%s", path);
-    free (ret->guestfs_int_stat_list_val);
+    free (ret->guestfs_int_statns_list_val);
     free (ret);
     return NULL;
   }
@@ -163,35 +160,14 @@ do_internal_lstatlist (const char *path, char *const *names)
 
     r = fstatat (path_fd, names[i], &statbuf, AT_SYMLINK_NOFOLLOW);
     if (r == -1)
-      ret->guestfs_int_stat_list_val[i].ino = -1;
-    else {
-      ret->guestfs_int_stat_list_val[i].dev = statbuf.st_dev;
-      ret->guestfs_int_stat_list_val[i].ino = statbuf.st_ino;
-      ret->guestfs_int_stat_list_val[i].mode = statbuf.st_mode;
-      ret->guestfs_int_stat_list_val[i].nlink = statbuf.st_nlink;
-      ret->guestfs_int_stat_list_val[i].uid = statbuf.st_uid;
-      ret->guestfs_int_stat_list_val[i].gid = statbuf.st_gid;
-      ret->guestfs_int_stat_list_val[i].rdev = statbuf.st_rdev;
-      ret->guestfs_int_stat_list_val[i].size = statbuf.st_size;
-#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
-      ret->guestfs_int_stat_list_val[i].blksize = statbuf.st_blksize;
-#else
-      ret->guestfs_int_stat_list_val[i].blksize = -1;
-#endif
-#ifdef HAVE_STRUCT_STAT_ST_BLOCKS
-      ret->guestfs_int_stat_list_val[i].blocks = statbuf.st_blocks;
-#else
-      ret->guestfs_int_stat_list_val[i].blocks = -1;
-#endif
-      ret->guestfs_int_stat_list_val[i].atime = statbuf.st_atime;
-      ret->guestfs_int_stat_list_val[i].mtime = statbuf.st_mtime;
-      ret->guestfs_int_stat_list_val[i].ctime = statbuf.st_ctime;
-    }
+      ret->guestfs_int_statns_list_val[i].st_ino = -1;
+    else
+      stat_to_statns (&ret->guestfs_int_statns_list_val[i], &statbuf);
   }
 
   if (close (path_fd) == -1) {
     reply_with_perror ("close: %s", path);
-    free (ret->guestfs_int_stat_list_val);
+    free (ret->guestfs_int_statns_list_val);
     free (ret);
     return NULL;
   }
diff --git a/diff/diff.c b/diff/diff.c
index 16c970b..af4e179 100644
--- a/diff/diff.c
+++ b/diff/diff.c
@@ -78,7 +78,7 @@ static void output_int64 (int64_t);
 static void output_int64_dev (int64_t);
 static void output_int64_perms (int64_t);
 static void output_int64_size (int64_t);
-static void output_int64_time (int64_t);
+static void output_int64_time (int64_t secs, int64_t nsecs);
 static void output_int64_uid (int64_t);
 static void output_string (const char *);
 static void output_string_link (const char *);
@@ -398,7 +398,7 @@ struct tree {
 
 struct file {
   char *path;
-  struct guestfs_stat *stat;
+  struct guestfs_statns *stat;
   struct guestfs_xattr_list *xattrs;
   char *csum;                  /* Checksum. If NULL, use file times and size. */
 };
@@ -410,7 +410,7 @@ free_tree (struct tree *t)
 
   for (i = 0; i < t->nr_files; ++i) {
     free (t->files[i].path);
-    guestfs_free_stat (t->files[i].stat);
+    guestfs_free_statns (t->files[i].stat);
     guestfs_free_xattr_list (t->files[i].xattrs);
     free (t->files[i].csum);
   }
@@ -420,7 +420,7 @@ free_tree (struct tree *t)
   free (t);
 }
 
-static int visit_entry (const char *dir, const char *name, const struct guestfs_stat *stat, const struct guestfs_xattr_list *xattrs, void *vt);
+static int visit_entry (const char *dir, const char *name, const struct guestfs_statns *stat, const struct guestfs_xattr_list *xattrs, void *vt);
 
 static struct tree *
 visit_guest (guestfs_h *g)
@@ -454,13 +454,13 @@ visit_guest (guestfs_h *g)
  */
 static int
 visit_entry (const char *dir, const char *name,
-             const struct guestfs_stat *stat_orig,
+             const struct guestfs_statns *stat_orig,
              const struct guestfs_xattr_list *xattrs_orig,
              void *vt)
 {
   struct tree *t = vt;
   char *path = NULL, *csum = NULL;
-  struct guestfs_stat *stat = NULL;
+  struct guestfs_statns *stat = NULL;
   struct guestfs_xattr_list *xattrs = NULL;
   size_t i;
 
@@ -469,7 +469,7 @@ visit_entry (const char *dir, const char *name,
   /* Copy the stats and xattrs because the visit function will
    * free them after we return.
    */
-  stat = guestfs_copy_stat (stat_orig);
+  stat = guestfs_copy_statns (stat_orig);
   if (stat == NULL) {
     perror ("guestfs_copy_stat");
     goto error;
@@ -480,7 +480,7 @@ visit_entry (const char *dir, const char *name,
     goto error;
   }
 
-  if (checksum && is_reg (stat->mode)) {
+  if (checksum && is_reg (stat->st_mode)) {
     csum = guestfs_checksum (t->g, checksum, path);
     if (!csum)
       goto error;
@@ -488,19 +488,20 @@ visit_entry (const char *dir, const char *name,
 
   /* If --atime option was NOT passed, flatten the atime field. */
   if (!atime)
-    stat->atime = 0;
+    stat->st_atime_sec = 0;
 
   /* If --dir-links option was NOT passed, flatten nlink field in
    * directories.
    */
-  if (!dir_links && is_dir (stat->mode))
-    stat->nlink = 0;
+  if (!dir_links && is_dir (stat->st_mode))
+    stat->st_nlink = 0;
 
   /* If --dir-times option was NOT passed, flatten time fields in
    * directories.
    */
-  if (!dir_times && is_dir (stat->mode))
-    stat->atime = stat->mtime = stat->ctime = 0;
+  if (!dir_times && is_dir (stat->st_mode))
+    stat->st_atime_sec = stat->st_mtime_sec = stat->st_ctime_sec =
+      stat->st_atime_nsec = stat->st_mtime_nsec = stat->st_ctime_nsec = 0;
 
   /* Add the pathname and stats to the list. */
   i = t->nr_files++;
@@ -535,7 +536,7 @@ visit_entry (const char *dir, const char *name,
  error:
   free (path);
   free (csum);
-  guestfs_free_stat (stat);
+  guestfs_free_statns (stat);
   guestfs_free_xattr_list (xattrs);
   return -1;
 }
@@ -622,7 +623,7 @@ compare_stats (struct file *file1, struct file *file2)
 {
   int r;
 
-  r = guestfs_compare_stat (file1->stat, file2->stat);
+  r = guestfs_compare_statns (file1->stat, file2->stat);
   if (r != 0)
     return r;
 
@@ -640,10 +641,10 @@ changed (guestfs_h *g1, struct file *file1,
 {
   /* Did file content change? */
   if (cst != 0 ||
-      (is_reg (file1->stat->mode) && is_reg (file2->stat->mode) &&
-       (file1->stat->mtime != file2->stat->mtime ||
-        file1->stat->ctime != file2->stat->ctime ||
-        file1->stat->size != file2->stat->size))) {
+      (is_reg (file1->stat->st_mode) && is_reg (file2->stat->st_mode) &&
+       (file1->stat->st_mtime_sec != file2->stat->st_mtime_sec ||
+        file1->stat->st_ctime_sec != file2->stat->st_ctime_sec ||
+        file1->stat->st_size != file2->stat->st_size))) {
     output_start_line ();
     output_string ("=");
     output_file (g1, file1);
@@ -673,19 +674,19 @@ changed (guestfs_h *g1, struct file *file1,
     output_string ("changed:");
 #define COMPARE_STAT(n) \
     if (file1->stat->n != file2->stat->n) output_string (#n)
-    COMPARE_STAT (dev);
-    COMPARE_STAT (ino);
-    COMPARE_STAT (mode);
-    COMPARE_STAT (nlink);
-    COMPARE_STAT (uid);
-    COMPARE_STAT (gid);
-    COMPARE_STAT (rdev);
-    COMPARE_STAT (size);
-    COMPARE_STAT (blksize);
-    COMPARE_STAT (blocks);
-    COMPARE_STAT (atime);
-    COMPARE_STAT (mtime);
-    COMPARE_STAT (ctime);
+    COMPARE_STAT (st_dev);
+    COMPARE_STAT (st_ino);
+    COMPARE_STAT (st_mode);
+    COMPARE_STAT (st_nlink);
+    COMPARE_STAT (st_uid);
+    COMPARE_STAT (st_gid);
+    COMPARE_STAT (st_rdev);
+    COMPARE_STAT (st_size);
+    COMPARE_STAT (st_blksize);
+    COMPARE_STAT (st_blocks);
+    COMPARE_STAT (st_atime_sec);
+    COMPARE_STAT (st_mtime_sec);
+    COMPARE_STAT (st_ctime_sec);
 #undef COMPARE_STAT
     if (guestfs_compare_xattr_list (file1->xattrs, file2->xattrs))
       output_string ("xattrs");
@@ -701,8 +702,8 @@ diff (struct file *file1, guestfs_h *g1, struct file *file2, guestfs_h *g2)
   CLEANUP_FREE char *tmpd, *tmpda = NULL, *tmpdb = NULL, *cmd = NULL;
   int r;
 
-  assert (is_reg (file1->stat->mode));
-  assert (is_reg (file2->stat->mode));
+  assert (is_reg (file1->stat->st_mode));
+  assert (is_reg (file2->stat->st_mode));
 
   if (asprintf (&tmpd, "%s/virtdiffXXXXXX", tmpdir) < 0) {
     perror ("asprintf");
@@ -755,47 +756,47 @@ output_file (guestfs_h *g, struct file *file)
   size_t i;
   CLEANUP_FREE char *link = NULL;
 
-  if (is_reg (file->stat->mode))
+  if (is_reg (file->stat->st_mode))
     filetype = "-";
-  else if (is_dir (file->stat->mode))
+  else if (is_dir (file->stat->st_mode))
     filetype = "d";
-  else if (is_chr (file->stat->mode))
+  else if (is_chr (file->stat->st_mode))
     filetype = "c";
-  else if (is_blk (file->stat->mode))
+  else if (is_blk (file->stat->st_mode))
     filetype = "b";
-  else if (is_fifo (file->stat->mode))
+  else if (is_fifo (file->stat->st_mode))
     filetype = "p";
-  else if (is_lnk (file->stat->mode))
+  else if (is_lnk (file->stat->st_mode))
     filetype = "l";
-  else if (is_sock (file->stat->mode))
+  else if (is_sock (file->stat->st_mode))
     filetype = "s";
   else
     filetype = "u";
 
   output_string (filetype);
-  output_int64_perms (file->stat->mode & 07777);
+  output_int64_perms (file->stat->st_mode & 07777);
 
-  output_int64_size (file->stat->size);
+  output_int64_size (file->stat->st_size);
 
   /* Display extra fields when enabled. */
   if (enable_uids) {
-    output_int64_uid (file->stat->uid);
-    output_int64_uid (file->stat->gid);
+    output_int64_uid (file->stat->st_uid);
+    output_int64_uid (file->stat->st_gid);
   }
 
   if (enable_times) {
     if (atime)
-      output_int64_time (file->stat->atime);
-    output_int64_time (file->stat->mtime);
-    output_int64_time (file->stat->ctime);
+      output_int64_time (file->stat->st_atime_sec, file->stat->st_atime_nsec);
+    output_int64_time (file->stat->st_mtime_sec, file->stat->st_mtime_nsec);
+    output_int64_time (file->stat->st_ctime_sec, file->stat->st_ctime_nsec);
   }
 
   if (enable_extra_stats) {
-    output_int64_dev (file->stat->dev);
-    output_int64 (file->stat->ino);
-    output_int64 (file->stat->nlink);
-    output_int64_dev (file->stat->rdev);
-    output_int64 (file->stat->blocks);
+    output_int64_dev (file->stat->st_dev);
+    output_int64 (file->stat->st_ino);
+    output_int64 (file->stat->st_nlink);
+    output_int64_dev (file->stat->st_rdev);
+    output_int64 (file->stat->st_blocks);
   }
 
   if (file->csum)
@@ -803,7 +804,7 @@ output_file (guestfs_h *g, struct file *file)
 
   output_string (file->path);
 
-  if (is_lnk (file->stat->mode)) {
+  if (is_lnk (file->stat->st_mode)) {
     /* XXX Fix this for NTFS. */
     link = guestfs_readlink (g, file->path);
     if (link)
@@ -1056,7 +1057,7 @@ output_int64_perms (int64_t i)
 }
 
 static void
-output_int64_time (int64_t i)
+output_int64_time (int64_t secs, int64_t nsecs)
 {
   int r;
 
@@ -1066,19 +1067,19 @@ output_int64_time (int64_t i)
   if (time_t_output) {
     switch (time_relative) {
     case 0:                     /* --time-t */
-      r = printf ("%10" PRIi64, i);
+      r = printf ("%10" PRIi64, secs);
       break;
     case 1:                     /* --time-relative */
-      r = printf ("%8" PRIi64, now - i);
+      r = printf ("%8" PRIi64, now - secs);
       break;
     case 2:                     /* --time-days */
     default:
-      r = printf ("%3" PRIi64, (now - i) / 86400);
+      r = printf ("%3" PRIi64, (now - secs) / 86400);
       break;
     }
   }
   else {
-    time_t t = (time_t) i;
+    time_t t = (time_t) secs;
     char buf[64];
     struct tm *tm;
 
diff --git a/fuse/test-fuse.c b/fuse/test-fuse.c
index dda6fde..2876dc4 100644
--- a/fuse/test-fuse.c
+++ b/fuse/test-fuse.c
@@ -247,9 +247,9 @@ test_fuse (void)
   char buf[128];
   ssize_t r;
   unsigned u, u1;
-#if 0
   int fd;
   struct timeval tv[2];
+#if 0
   struct timespec ts[2];
 #endif
   acl_t acl;
@@ -544,7 +544,6 @@ test_fuse (void)
     return -1;
   }
 
-#if 0
   STAGE ("checking utimes");
 
   fd = open ("timestamp", O_WRONLY|O_CREAT|O_TRUNC|O_NOCTTY|O_CLOEXEC, 0644);
@@ -574,7 +573,6 @@ test_fuse (void)
              (int) statbuf.st_mtime, (int) statbuf.st_mtim.tv_nsec);
     return -1;
   }
-#endif
 
 #if 0
   /* Does not work! See https://bugzilla.redhat.com/show_bug.cgi?id=1144766 */
diff --git a/generator/actions.ml b/generator/actions.ml
index 73dcd33..7782198 100644
--- a/generator/actions.ml
+++ b/generator/actions.ml
@@ -2605,6 +2605,7 @@ See also C<guestfs_write>." };
   { defaults with
     name = "lstatlist";
     style = RStructList ("statbufs", "stat"), [Pathname "path"; StringList "names"], [];
+    deprecated_by = Some "lstatnslist";
     shortdesc = "lstat on multiple files";
     longdesc = "\
 This call allows you to perform the C<guestfs_lstat> operation
@@ -2613,7 +2614,26 @@ C<names> is the list of files from this directory.
 
 On return you get a list of stat structs, with a one-to-one
 correspondence to the C<names> list.  If any name did not exist
-or could not be lstat'd, then the C<ino> field of that structure
+or could not be lstat'd, then the C<st_ino> field of that structure
+is set to C<-1>.
+
+This call is intended for programs that want to efficiently
+list a directory contents without making many round-trips.
+See also C<guestfs_lxattrlist> for a similarly efficient call
+for getting extended attributes." };
+
+  { defaults with
+    name = "lstatnslist";
+    style = RStructList ("statbufs", "statns"), [Pathname "path"; StringList "names"], [];
+    shortdesc = "lstat on multiple files";
+    longdesc = "\
+This call allows you to perform the C<guestfs_lstatns> operation
+on multiple files, where all files are in the directory C<path>.
+C<names> is the list of files from this directory.
+
+On return you get a list of stat structs, with a one-to-one
+correspondence to the C<names> list.  If any name did not exist
+or could not be lstat'd, then the C<st_ino> field of that structure
 is set to C<-1>.
 
 This call is intended for programs that want to efficiently
@@ -3223,6 +3243,38 @@ This call returns the number of strings which were removed
 
 See L<guestfs(3)/BACKEND>, L<guestfs(3)/BACKEND SETTINGS>." };
 
+  { defaults with
+    name = "stat";
+    style = RStruct ("statbuf", "stat"), [Pathname "path"], [];
+    deprecated_by = Some "statns";
+    tests = [
+      InitISOFS, Always, TestResult (
+        [["stat"; "/empty"]], "ret->size == 0"), []
+    ];
+    shortdesc = "get file information";
+    longdesc = "\
+Returns file information for the given C<path>.
+
+This is the same as the C<stat(2)> system call." };
+
+  { defaults with
+    name = "lstat";
+    style = RStruct ("statbuf", "stat"), [Pathname "path"], [];
+    deprecated_by = Some "lstatns";
+    tests = [
+      InitISOFS, Always, TestResult (
+        [["lstat"; "/empty"]], "ret->size == 0"), []
+    ];
+    shortdesc = "get file information for a symbolic link";
+    longdesc = "\
+Returns file information for the given C<path>.
+
+This is the same as C<guestfs_stat> except that if C<path>
+is a symbolic link, then the link is stat-ed, not the file it
+refers to.
+
+This is the same as the C<lstat(2)> system call." };
+
 ]
 
 (* daemon_functions are any functions which cause some action
@@ -4347,38 +4399,6 @@ result into a list of lines.
 See also: C<guestfs_sh_lines>" };
 
   { defaults with
-    name = "stat";
-    style = RStruct ("statbuf", "stat"), [Pathname "path"], [];
-    proc_nr = Some 52;
-    tests = [
-      InitISOFS, Always, TestResult (
-        [["stat"; "/empty"]], "ret->size == 0"), []
-    ];
-    shortdesc = "get file information";
-    longdesc = "\
-Returns file information for the given C<path>.
-
-This is the same as the C<stat(2)> system call." };
-
-  { defaults with
-    name = "lstat";
-    style = RStruct ("statbuf", "stat"), [Pathname "path"], [];
-    proc_nr = Some 53;
-    tests = [
-      InitISOFS, Always, TestResult (
-        [["lstat"; "/empty"]], "ret->size == 0"), []
-    ];
-    shortdesc = "get file information for a symbolic link";
-    longdesc = "\
-Returns file information for the given C<path>.
-
-This is the same as C<guestfs_stat> except that if C<path>
-is a symbolic link, then the link is stat-ed, not the file it
-refers to.
-
-This is the same as the C<lstat(2)> system call." };
-
-  { defaults with
     name = "statvfs";
     style = RStruct ("statbuf", "statvfs"), [Pathname "path"], [];
     proc_nr = Some 54;
@@ -7493,30 +7513,6 @@ names, you will need to locate and parse the password file
 yourself (Augeas support makes this relatively easy)." };
 
   { defaults with
-    name = "internal_lstatlist";
-    style = RStructList ("statbufs", "stat"), [Pathname "path"; StringList "names"], [];
-    proc_nr = Some 204;
-    visibility = VInternal;
-    shortdesc = "lstat on multiple files";
-    longdesc = "\
-This call allows you to perform the C<guestfs_lstat> operation
-on multiple files, where all files are in the directory C<path>.
-C<names> is the list of files from this directory.
-
-On return you get a list of stat structs, with a one-to-one
-correspondence to the C<names> list.  If any name did not exist
-or could not be lstat'd, then the C<ino> field of that structure
-is set to C<-1>.
-
-This call is intended for programs that want to efficiently
-list a directory contents without making many round-trips.
-See also C<guestfs_lxattrlist> for a similarly efficient call
-for getting extended attributes.  Very long directory listings
-might cause the protocol message size to be exceeded, causing
-this call to fail.  The caller must split up such requests
-into smaller groups of names." };
-
-  { defaults with
     name = "internal_lxattrlist";
     style = RStructList ("xattrs", "xattr"), [Pathname "path"; StringList "names"], [];
     proc_nr = Some 205;
@@ -11942,6 +11938,47 @@ New (SVR4) portable format with a checksum.
     longdesc = "\
 Get the realtime (wallclock) timestamp of the current journal entry." };
 
+  { defaults with
+    name = "statns";
+    style = RStruct ("statbuf", "statns"), [Pathname "path"], [];
+    proc_nr = Some 421;
+    tests = [
+      InitISOFS, Always, TestResult (
+        [["statns"; "/empty"]], "ret->st_size == 0"), []
+    ];
+    shortdesc = "get file information";
+    longdesc = "\
+Returns file information for the given C<path>.
+
+This is the same as the C<stat(2)> system call." };
+
+  { defaults with
+    name = "lstatns";
+    style = RStruct ("statbuf", "statns"), [Pathname "path"], [];
+    proc_nr = Some 422;
+    tests = [
+      InitISOFS, Always, TestResult (
+        [["lstatns"; "/empty"]], "ret->st_size == 0"), []
+    ];
+    shortdesc = "get file information for a symbolic link";
+    longdesc = "\
+Returns file information for the given C<path>.
+
+This is the same as C<guestfs_statns> except that if C<path>
+is a symbolic link, then the link is stat-ed, not the file it
+refers to.
+
+This is the same as the C<lstat(2)> system call." };
+
+  { defaults with
+    name = "internal_lstatnslist";
+    style = RStructList ("statbufs", "statns"), [Pathname "path"; StringList "names"], [];
+    proc_nr = Some 423;
+    visibility = VInternal;
+    shortdesc = "lstat on multiple files";
+    longdesc = "\
+This is the internal call which implements C<guestfs_lstatnslist>." };
+
 ]
 
 (* Non-API meta-commands available only in guestfish.
diff --git a/generator/structs.ml b/generator/structs.ml
index 65c78b2..578ebb7 100644
--- a/generator/structs.ml
+++ b/generator/structs.ml
@@ -143,6 +143,36 @@ let structs = [
     "ctime", FInt64;
     ];
     s_camel_name = "Stat" };
+  (* Because we omitted the nanosecond fields from the above struct,
+   * we also have this:
+   *)
+  { defaults with
+    s_name = "statns";
+    s_cols = [
+    "st_dev", FInt64;
+    "st_ino", FInt64;
+    "st_mode", FInt64;
+    "st_nlink", FInt64;
+    "st_uid", FInt64;
+    "st_gid", FInt64;
+    "st_rdev", FInt64;
+    "st_size", FInt64;
+    "st_blksize", FInt64;
+    "st_blocks", FInt64;
+    "st_atime_sec", FInt64;
+    "st_atime_nsec", FInt64;
+    "st_mtime_sec", FInt64;
+    "st_mtime_nsec", FInt64;
+    "st_ctime_sec", FInt64;
+    "st_ctime_nsec", FInt64;
+    "st_spare1", FInt64;
+    "st_spare2", FInt64;
+    "st_spare3", FInt64;
+    "st_spare4", FInt64;
+    "st_spare5", FInt64;
+    "st_spare6", FInt64;
+    ];
+    s_camel_name = "StatNS" };
   { defaults with
     s_name = "statvfs";
     s_cols = [
diff --git a/gobject/Makefile.inc b/gobject/Makefile.inc
index ed1ff3b..c93dace 100644
--- a/gobject/Makefile.inc
+++ b/gobject/Makefile.inc
@@ -38,6 +38,7 @@ guestfs_gobject_headers= \
   include/guestfs-gobject/struct-mdstat.h \
   include/guestfs-gobject/struct-partition.h \
   include/guestfs-gobject/struct-stat.h \
+  include/guestfs-gobject/struct-statns.h \
   include/guestfs-gobject/struct-statvfs.h \
   include/guestfs-gobject/struct-utsname.h \
   include/guestfs-gobject/struct-version.h \
@@ -115,6 +116,7 @@ guestfs_gobject_sources= \
   src/struct-mdstat.c \
   src/struct-partition.c \
   src/struct-stat.c \
+  src/struct-statns.c \
   src/struct-statvfs.c \
   src/struct-utsname.c \
   src/struct-version.c \
diff --git a/java/Makefile.inc b/java/Makefile.inc
index 731e782..614caaa 100644
--- a/java/Makefile.inc
+++ b/java/Makefile.inc
@@ -34,6 +34,7 @@ java_built_sources = \
 	com/redhat/et/libguestfs/PV.java \
 	com/redhat/et/libguestfs/Partition.java \
 	com/redhat/et/libguestfs/Stat.java \
+	com/redhat/et/libguestfs/StatNS.java \
 	com/redhat/et/libguestfs/StatVFS.java \
 	com/redhat/et/libguestfs/UTSName.java \
 	com/redhat/et/libguestfs/VG.java \
diff --git a/java/com/redhat/et/libguestfs/.gitignore b/java/com/redhat/et/libguestfs/.gitignore
index 00bcec9..4882c96 100644
--- a/java/com/redhat/et/libguestfs/.gitignore
+++ b/java/com/redhat/et/libguestfs/.gitignore
@@ -12,6 +12,7 @@ MDStat.java
 PV.java
 Partition.java
 Stat.java
+StatNS.java
 StatVFS.java
 UTSName.java
 VG.java
diff --git a/po/POTFILES b/po/POTFILES
index add74b6..aec8c62 100644
--- a/po/POTFILES
+++ b/po/POTFILES
@@ -238,6 +238,7 @@ gobject/src/struct-lvm_vg.c
 gobject/src/struct-mdstat.c
 gobject/src/struct-partition.c
 gobject/src/struct-stat.c
+gobject/src/struct-statns.c
 gobject/src/struct-statvfs.c
 gobject/src/struct-utsname.c
 gobject/src/struct-version.c
diff --git a/src/MAX_PROC_NR b/src/MAX_PROC_NR
index 816d01b..5721413 100644
--- a/src/MAX_PROC_NR
+++ b/src/MAX_PROC_NR
@@ -1 +1 @@
-420
+423
diff --git a/src/file.c b/src/file.c
index d21a61d..378a9c0 100644
--- a/src/file.c
+++ b/src/file.c
@@ -377,33 +377,33 @@ guestfs__write_append (guestfs_h *g, const char *path,
   return write_or_append (g, path, content, size, 1);
 }
 
-#define LSTATLIST_MAX 1000
+#define LSTATNSLIST_MAX 1000
 
-struct guestfs_stat_list *
-guestfs__lstatlist (guestfs_h *g, const char *dir, char * const*names)
+struct guestfs_statns_list *
+guestfs__lstatnslist (guestfs_h *g, const char *dir, char * const*names)
 {
   size_t len = guestfs___count_strings (names);
   size_t old_len;
-  struct guestfs_stat_list *ret;
+  struct guestfs_statns_list *ret;
 
   ret = safe_malloc (g, sizeof *ret);
   ret->len = 0;
   ret->val = NULL;
 
   while (len > 0) {
-    CLEANUP_FREE_STAT_LIST struct guestfs_stat_list *stats = NULL;
+    CLEANUP_FREE_STATNS_LIST struct guestfs_statns_list *stats = NULL;
 
     /* Note we don't need to free up the strings because take_strings
      * does not do a deep copy.
      */
-    CLEANUP_FREE char **first = take_strings (g, names, LSTATLIST_MAX, &names);
+    CLEANUP_FREE char **first = take_strings (g, names, LSTATNSLIST_MAX, &names);
 
-    len = len <= LSTATLIST_MAX ? 0 : len - LSTATLIST_MAX;
+    len = len <= LSTATNSLIST_MAX ? 0 : len - LSTATNSLIST_MAX;
 
-    stats = guestfs_internal_lstatlist (g, dir, first);
+    stats = guestfs_internal_lstatnslist (g, dir, first);
 
     if (stats == NULL) {
-      guestfs_free_stat_list (ret);
+      guestfs_free_statns_list (ret);
       return NULL;
     }
 
@@ -411,9 +411,9 @@ guestfs__lstatlist (guestfs_h *g, const char *dir, char * const*names)
     old_len = ret->len;
     ret->len += stats->len;
     ret->val = safe_realloc (g, ret->val,
-                             ret->len * sizeof (struct guestfs_stat));
+                             ret->len * sizeof (struct guestfs_statns));
     memcpy (&ret->val[old_len], stats->val,
-            stats->len * sizeof (struct guestfs_stat));
+            stats->len * sizeof (struct guestfs_statns));
   }
 
   return ret;
@@ -602,3 +602,71 @@ guestfs__ls (guestfs_h *g, const char *directory)
     close (fd);
   return NULL;
 }
+
+static void
+statns_to_old_stat (struct guestfs_statns *a, struct guestfs_stat *r)
+{
+  r->ino = a->st_ino;
+  r->mode = a->st_mode;
+  r->nlink = a->st_nlink;
+  r->uid = a->st_uid;
+  r->gid = a->st_gid;
+  r->rdev = a->st_rdev;
+  r->size = a->st_size;
+  r->blksize = a->st_blksize;
+  r->blocks = a->st_blocks;
+  r->atime = a->st_atime_sec;
+  r->mtime = a->st_mtime_sec;
+  r->ctime = a->st_ctime_sec;
+}
+
+struct guestfs_stat *
+guestfs__stat (guestfs_h *g, const char *path)
+{
+  CLEANUP_FREE_STATNS struct guestfs_statns *r;
+  struct guestfs_stat *ret;
+
+  r = guestfs_statns (g, path);
+  if (r == NULL)
+    return NULL;
+
+  ret = safe_malloc (g, sizeof *ret);
+  statns_to_old_stat (r, ret);
+  return ret;                   /* caller frees */
+}
+
+struct guestfs_stat *
+guestfs__lstat (guestfs_h *g, const char *path)
+{
+  CLEANUP_FREE_STATNS struct guestfs_statns *r;
+  struct guestfs_stat *ret;
+
+  r = guestfs_lstatns (g, path);
+  if (r == NULL)
+    return NULL;
+
+  ret = safe_malloc (g, sizeof *ret);
+  statns_to_old_stat (r, ret);
+  return ret;                   /* caller frees */
+}
+
+struct guestfs_stat_list *
+guestfs__lstatlist (guestfs_h *g, const char *dir, char * const*names)
+{
+  CLEANUP_FREE_STATNS_LIST struct guestfs_statns_list *r;
+  struct guestfs_stat_list *ret;
+  size_t i;
+
+  r = guestfs_lstatnslist (g, dir, names);
+  if (r == NULL)
+    return NULL;
+
+  ret = safe_malloc (g, sizeof *ret);
+  ret->len = r->len;
+  ret->val = safe_calloc (g, r->len, sizeof (struct guestfs_stat));
+
+  for (i = 0; i < r->len; ++i)
+    statns_to_old_stat (&r->val[i], &ret->val[i]);
+
+  return ret;
+}
diff --git a/src/fuse.c b/src/fuse.c
index 00f9092..08a8784 100644
--- a/src/fuse.c
+++ b/src/fuse.c
@@ -165,7 +165,7 @@ mount_local_readdir (const char *path, void *buf, fuse_fill_dir_t filler,
    */
   names = malloc ((ents->len + 1) * sizeof (char *));
   if (names) {
-    CLEANUP_FREE_STAT_LIST struct guestfs_stat_list *ss = NULL;
+    CLEANUP_FREE_STATNS_LIST struct guestfs_statns_list *ss = NULL;
     CLEANUP_FREE_XATTR_LIST struct guestfs_xattr_list *xattrs = NULL;
     char **links;
 
@@ -173,26 +173,35 @@ mount_local_readdir (const char *path, void *buf, fuse_fill_dir_t filler,
       names[i] = ents->val[i].name;
     names[i] = NULL;
 
-    ss = guestfs_lstatlist (g, path, names);
+    ss = guestfs_lstatnslist (g, path, names);
     if (ss) {
       for (i = 0; i < ss->len; ++i) {
-        if (ss->val[i].ino >= 0) {
+        if (ss->val[i].st_ino >= 0) {
           struct stat statbuf;
 
           memset (&statbuf, 0, sizeof statbuf);
-          statbuf.st_dev = ss->val[i].dev;
-          statbuf.st_ino = ss->val[i].ino;
-          statbuf.st_mode = ss->val[i].mode;
-          statbuf.st_nlink = ss->val[i].nlink;
-          statbuf.st_uid = ss->val[i].uid;
-          statbuf.st_gid = ss->val[i].gid;
-          statbuf.st_rdev = ss->val[i].rdev;
-          statbuf.st_size = ss->val[i].size;
-          statbuf.st_blksize = ss->val[i].blksize;
-          statbuf.st_blocks = ss->val[i].blocks;
-          statbuf.st_atime = ss->val[i].atime;
-          statbuf.st_mtime = ss->val[i].mtime;
-          statbuf.st_ctime = ss->val[i].ctime;
+          statbuf.st_dev = ss->val[i].st_dev;
+          statbuf.st_ino = ss->val[i].st_ino;
+          statbuf.st_mode = ss->val[i].st_mode;
+          statbuf.st_nlink = ss->val[i].st_nlink;
+          statbuf.st_uid = ss->val[i].st_uid;
+          statbuf.st_gid = ss->val[i].st_gid;
+          statbuf.st_rdev = ss->val[i].st_rdev;
+          statbuf.st_size = ss->val[i].st_size;
+          statbuf.st_blksize = ss->val[i].st_blksize;
+          statbuf.st_blocks = ss->val[i].st_blocks;
+          statbuf.st_atime = ss->val[i].st_atime_sec;
+#ifdef HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC
+          statbuf.st_atim.tv_nsec = ss->val[i].st_atime_nsec;
+#endif
+          statbuf.st_mtime = ss->val[i].st_mtime_sec;
+#ifdef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
+          statbuf.st_mtim.tv_nsec = ss->val[i].st_mtime_nsec;
+#endif
+          statbuf.st_ctime = ss->val[i].st_ctime_sec;
+#ifdef HAVE_STRUCT_STAT_ST_CTIM_TV_NSEC
+          statbuf.st_ctim.tv_nsec = ss->val[i].st_ctime_nsec;
+#endif
 
           lsc_insert (g, path, names[i], now, &statbuf);
         }
@@ -246,7 +255,7 @@ static int
 mount_local_getattr (const char *path, struct stat *statbuf)
 {
   const struct stat *buf;
-  CLEANUP_FREE_STAT struct guestfs_stat *r = NULL;
+  CLEANUP_FREE_STAT struct guestfs_statns *r = NULL;
   DECL_G ();
   DEBUG_CALL ("%s, %p", path, statbuf);
 
@@ -256,24 +265,33 @@ mount_local_getattr (const char *path, struct stat *statbuf)
     return 0;
   }
 
-  r = guestfs_lstat (g, path);
+  r = guestfs_lstatns (g, path);
   if (r == NULL)
     RETURN_ERRNO;
 
   memset (statbuf, 0, sizeof *statbuf);
-  statbuf->st_dev = r->dev;
-  statbuf->st_ino = r->ino;
-  statbuf->st_mode = r->mode;
-  statbuf->st_nlink = r->nlink;
-  statbuf->st_uid = r->uid;
-  statbuf->st_gid = r->gid;
-  statbuf->st_rdev = r->rdev;
-  statbuf->st_size = r->size;
-  statbuf->st_blksize = r->blksize;
-  statbuf->st_blocks = r->blocks;
-  statbuf->st_atime = r->atime;
-  statbuf->st_mtime = r->mtime;
-  statbuf->st_ctime = r->ctime;
+  statbuf->st_dev = r->st_dev;
+  statbuf->st_ino = r->st_ino;
+  statbuf->st_mode = r->st_mode;
+  statbuf->st_nlink = r->st_nlink;
+  statbuf->st_uid = r->st_uid;
+  statbuf->st_gid = r->st_gid;
+  statbuf->st_rdev = r->st_rdev;
+  statbuf->st_size = r->st_size;
+  statbuf->st_blksize = r->st_blksize;
+  statbuf->st_blocks = r->st_blocks;
+  statbuf->st_atime = r->st_atime_sec;
+#ifdef HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC
+  statbuf->st_atim.tv_nsec = r->st_atime_nsec;
+#endif
+  statbuf->st_mtime = r->st_mtime_sec;
+#ifdef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
+  statbuf->st_mtim.tv_nsec = r->st_mtime_nsec;
+#endif
+  statbuf->st_ctime = r->st_ctime_sec;
+#ifdef HAVE_STRUCT_STAT_ST_CTIM_TV_NSEC
+  statbuf->st_ctim.tv_nsec = r->st_ctime_nsec;
+#endif
 
   return 0;
 }
@@ -1133,7 +1151,7 @@ guestfs__umount_local (guestfs_h *g,
  * Note on attribute caching: FUSE can cache filesystem attributes for
  * short periods of time (configurable via -o attr_timeout).  It
  * doesn't cache xattrs, and in any case FUSE caching doesn't solve
- * the problem that we have to make a series of guestfs_lstat and
+ * the problem that we have to make a series of guestfs_lstatns and
  * guestfs_lgetxattr calls when we first list a directory (thus, many
  * round trips).
  *
diff --git a/v2v/convert_linux.ml b/v2v/convert_linux.ml
index fb254b1..d932bcc 100644
--- a/v2v/convert_linux.ml
+++ b/v2v/convert_linux.ml
@@ -43,7 +43,7 @@ type kernel_info = {
   ki_version : string;             (* version-release *)
   ki_arch : string;                (* Kernel architecture. *)
   ki_vmlinuz : string;             (* The path of the vmlinuz file. *)
-  ki_vmlinuz_stat : G.stat;        (* stat(2) of vmlinuz *)
+  ki_vmlinuz_stat : G.statns;      (* stat(2) of vmlinuz *)
   ki_initrd : string option;       (* Path of initramfs, if found. *)
   ki_modpath : string;             (* The module path. *)
   ki_modules : string list;        (* The list of module names. *)
@@ -165,7 +165,7 @@ let rec convert ~verbose ~keep_serial_console (g : G.guestfs) inspect source =
            if not (g#is_dir ~followsymlinks:true modpath) then
              raise Not_found;
            let vmlinuz_stat =
-             try g#stat vmlinuz with G.Error _ -> raise Not_found in
+             try g#statns vmlinuz with G.Error _ -> raise Not_found in
 
            (* Get/construct the version.  XXX Read this from kernel file. *)
            let version =
@@ -357,11 +357,11 @@ let rec convert ~verbose ~keep_serial_console (g : G.guestfs) inspect source =
     filter_map (
       fun vmlinuz ->
         try
-          let statbuf = g#stat vmlinuz in
+          let statbuf = g#statns vmlinuz in
           let kernel =
             List.find (
               fun { ki_vmlinuz_stat = s } ->
-                statbuf.G.dev = s.G.dev && statbuf.G.ino = s.G.ino
+                statbuf.G.st_dev = s.G.st_dev && statbuf.G.st_ino = s.G.st_ino
             ) installed_kernels in
           Some kernel
         with Not_found -> None
-- 
2.0.4




More information about the Libguestfs mailing list