Richard W.M. Jones
2014-Sep-22 12:48 UTC
[Libguestfs] [PATCH] New APIs: Implement stat calls that return nanosecond timestamps (RHBZ#1144891).
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
Pino Toscano
2014-Sep-22 15:18 UTC
Re: [Libguestfs] [PATCH] New APIs: Implement stat calls that return nanosecond timestamps (RHBZ#1144891).
On Monday 22 September 2014 13:48:38 Richard W.M. Jones wrote:> 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.LGTM. I guess this would be worth for release notes of 1.28, wouldn't it? -- Pino Toscano
Richard W.M. Jones
2014-Sep-22 16:00 UTC
Re: [Libguestfs] [PATCH] New APIs: Implement stat calls that return nanosecond timestamps (RHBZ#1144891).
On Mon, Sep 22, 2014 at 05:18:27PM +0200, Pino Toscano wrote:> I guess this would be worth for release notes of 1.28, wouldn't it?Yup - it will eventually need to be added to the release notes. I tend to do those in a batch. Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones Read my programming and virtualization blog: http://rwmj.wordpress.com libguestfs lets you edit virtual machines. Supports shell scripting, bindings from many languages. http://libguestfs.org