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.
This commit is contained in:
Richard W.M. Jones
2014-09-22 10:27:21 +01:00
parent aca076e2e2
commit 8664337cc3
18 changed files with 444 additions and 298 deletions

6
TODO
View File

@@ -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.

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);

View File

@@ -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.])

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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 */

View File

@@ -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
@@ -4346,38 +4398,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"], [];
@@ -7492,30 +7512,6 @@ Only numeric uid and gid are supported. If you want to use
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"], [];
@@ -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.

View File

@@ -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 = [

View File

@@ -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 \

View File

@@ -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 \

View File

@@ -12,6 +12,7 @@ MDStat.java
PV.java
Partition.java
Stat.java
StatNS.java
StatVFS.java
UTSName.java
VG.java

View File

@@ -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

View File

@@ -1 +1 @@
420
423

View File

@@ -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,72 @@ 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->dev = a->st_dev;
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;
}

View File

@@ -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).
*

View File

@@ -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