[Libguestfs] [PATCH 1/2] added filesystem_walk0 API

Pino Toscano ptoscano at redhat.com
Wed Mar 30 09:27:50 UTC 2016


On Tuesday 29 March 2016 20:35:52 Matteo Cafasso wrote:
> Signed-off-by: Matteo Cafasso <noxdafox at gmail.com>
> ---

See general notes sent as
https://www.redhat.com/archives/libguestfs/2016-March/msg00269.html

>  daemon/Makefile.am   |   3 +-
>  daemon/tsk.c         | 186 +++++++++++++++++++++++++++++++++++++++++++++++++++
>  generator/actions.ml |  16 +++++
>  m4/guestfs_daemon.m4 |   8 +++
>  src/MAX_PROC_NR      |   2 +-
>  5 files changed, 213 insertions(+), 2 deletions(-)
> 
> diff --git a/daemon/Makefile.am b/daemon/Makefile.am
> index 4e2051b..036def9 100644
> --- a/daemon/Makefile.am
> +++ b/daemon/Makefile.am
> @@ -209,7 +209,8 @@ guestfsd_LDADD = \
>  	$(LIB_CLOCK_GETTIME) \
>  	$(LIBINTL) \
>  	$(SERVENT_LIB) \
> -	$(PCRE_LIBS)
> +	$(PCRE_LIBS) \
> +	$(TSK_LIBS)
> 
>  guestfsd_CPPFLAGS = \
>  	-I$(top_srcdir)/gnulib/lib \
> diff --git a/daemon/tsk.c b/daemon/tsk.c
> index b84dfae..d72868e 100644
> --- a/daemon/tsk.c
> +++ b/daemon/tsk.c
> @@ -29,6 +29,26 @@
>  #include "actions.h"
>  #include "optgroups.h"
> 
> +#ifdef HAVE_LIBTSK
> +
> +#include <tsk/libtsk.h>
> +#include <rpc/xdr.h>
> +#include <rpc/types.h>
> +
> +static int
> +open_filesystem(const char *device, TSK_IMG_INFO **img, TSK_FS_INFO **fs);
> +static TSK_WALK_RET_ENUM
> +fswalk_callback(TSK_FS_FILE *fsfile, const char *path, void *data);
> +static char *join_path(const char *path, const char *name);
> +static int inode_out(guestfs_int_tsk_node *node_info);
> +static void reply_with_tsk_error(void);
> +
> +#else
> +
> +OPTGROUP_LIBTSK_NOT_AVAILABLE
> +
> +#endif
> +
>  static int file_out (const char *cmd);
>  static guestfs_int_tsk_node* parse_ffind (const char *out, int64_t inode);
> 
> @@ -226,3 +246,169 @@ file_out (const char *cmd)
> 
>    return 0;
>  }
> +
> +#ifdef HAVE_LIBTSK
> +
> +
> +int optgroup_libtsk_available(void)
> +{
> +  return 1;
> +}
> +
> +int do_filesystem_walk0(const mountable_t *mountable)
> +{
> +  int ret = 0;
> +  TSK_FS_INFO *fs = NULL;
> +  TSK_IMG_INFO *img = NULL;
> +  int flags = TSK_FS_DIR_WALK_FLAG_ALLOC | TSK_FS_DIR_WALK_FLAG_UNALLOC |
> +    TSK_FS_DIR_WALK_FLAG_RECURSE | TSK_FS_DIR_WALK_FLAG_NOORPHAN;
> +
> +  if (open_filesystem(mountable->device, &img, &fs) < 0)
> +    return -1;
> +
> +  reply(NULL, NULL);  /* Reply message. */
> +
> +  if (tsk_fs_dir_walk(fs, fs->root_inum, flags, fswalk_callback, &ret) != 0) {
> +    send_file_end(1);	/* Cancel file transfer. */
> +    reply_with_tsk_error();
> +
> +    ret = -1;
> +  }
> +  else {
> +    if (send_file_end(0))  /* Normal end of file. */
> +      ret = -1;
> +
> +    ret = 0;

This ret = 0 will always override the ret = -1 done two lines above.

> +  }
> +
> +  fs->close(fs);
> +  img->close(img);
> +
> +  return ret;
> +}
> +
> +/* Inspect the device and initialises the img and fs structures.
> + * Return 0 on success, -1 on error.
> + */
> +static int
> +open_filesystem(const char *device, TSK_IMG_INFO **img, TSK_FS_INFO **fs)
> +{
> +  const char *images[] = { device };
> +
> +  if ((*img = tsk_img_open(1, images, TSK_IMG_TYPE_DETECT , 0)) == NULL) {
> +    reply_with_tsk_error();
> +
> +    return -1;
> +  }
> +
> +  if ((*fs = tsk_fs_open_img(*img, 0, TSK_FS_TYPE_DETECT)) == NULL) {
> +    reply_with_tsk_error();
> +
> +    (*img)->close(*img);
> +
> +    return -1;
> +  }
> +
> +  return 0;
> +}
> +
> +/* Filesystem walk callback, it gets called on every FS node.
> + * Parse the node, encode it into an XDR structure and send it to the appliance.
> + * Return 0 on success, -1 on error.
> + */
> +static TSK_WALK_RET_ENUM
> +fswalk_callback(TSK_FS_FILE *fsfile, const char *path, void *data)
> +{
> +  CLEANUP_FREE char *file_name = NULL;
> +  struct guestfs_int_tsk_node node_info;
> +
> +  /* Ignore ./ and ../ */
> +  if (TSK_FS_ISDOT(fsfile->name->name))
> +    return 0;

Considering the function returns a TSK_WALK_RET_ENUM, then better use
the enum values explicitly (TSK_WALK_CONT, etc), instead of numbers.
Other than making it clear what the return value means, it also
protects us in the eventual case the API breaks and the enum values
are changed.

> +
> +  if ((file_name = join_path(path, fsfile->name->name)) == NULL)
> +    return -1;

The whole join_path seems superfluous, a simple asprintf here does
the same.

> +  node_info.tsk_name = file_name;
> +  node_info.tsk_inode = fsfile->name->meta_addr;
> +  if (fsfile->name->flags & TSK_FS_NAME_FLAG_UNALLOC)
> +    node_info.tsk_allocated = 0;
> +  else
> +    node_info.tsk_allocated = 1;

Not a real issue, but IMHO
  node_info.tsk_allocated = fsfile->name->flags & TSK_FS_NAME_FLAG_UNALLOC;
would be preferable.  YMMV.

> +  return inode_out(&node_info);

inode_out returns -1 on error, which is not a value of the enum
TSK_WALK_RET_ENUM.

> +}
> +
> +/* Joins the file name and file path.
> + * Return the joined path on success, NULL on failure.
> + */
> +static char *join_path(const char *path, const char *name)
> +{
> +  char *buf;
> +  size_t len;
> +
> +  path = (path != NULL) ? path : "";
> +  len = strlen(path) + strlen(name) + 1;
> +
> +  if (!(buf = malloc(len))) {
> +    reply_with_perror("malloc");
> +    return NULL;
> +  }
> +
> +  snprintf(buf, len, "%s%s", path, name);
> +
> +  return buf;
> +}
> +
> +/* Serialise node_info into XDR stream and send it to the appliance.
> + * Return 0 on success, -1 on error.
> + */
> +static int inode_out(guestfs_int_tsk_node *node_info)
> +{
> +  XDR xdr;
> +  size_t len;
> +  CLEANUP_FREE char *buf;
> +
> +  if ((buf = malloc(GUESTFS_MAX_CHUNK_SIZE)) == NULL) {
> +    reply_with_perror ("malloc");
> +    return -1;
> +  }
> +
> +  /* Serialise tsk_node struct. */
> +  len = strlen(node_info->tsk_name) + 1;
> +
> +  xdrmem_create(&xdr, buf, GUESTFS_MAX_CHUNK_SIZE, XDR_ENCODE);
> +  if (!xdr_u_long(&xdr, &len))
> +    return -1;
> +  if (!xdr_string(&xdr, &node_info->tsk_name, len))
> +    return -1;
> +  if (!xdr_uint64_t(&xdr, &node_info->tsk_inode))
> +    return -1;
> +  if (!xdr_uint32_t(&xdr, &node_info->tsk_allocated))
> +    return -1;
> +
> +  /* Resize buffer to actual length. */
> +  len = xdr_getpos(&xdr);
> +  xdr_destroy(&xdr);
> +  if ((buf = realloc(buf, len)) == NULL)
> +    return -1;
> +
> +  /* Send serialised tsk_node out. */
> +  if (send_file_write(buf, len) < 0)
> +    return -1;
> +
> +  return 0;
> +}
> +
> +/* Parse TSK error and send it to the appliance. */
> +static void reply_with_tsk_error(void)
> +{
> +  const char *buf = NULL;
> +
> +  if (tsk_error_get_errno() != 0) {
> +    buf = tsk_error_get();
> +    reply_with_error("TSK error: %s", buf);
> +  }

reply_with_tsk_error is called when some tsk function fails, so it must
always report an error; makes sure to send something like
"$function failed, unknown errno" or so in case tsk_error_get_errno
returns 0.  Hint: pass the name of the failed function as parameter to
this helper, so the error message shows what failed.

> diff --git a/generator/actions.ml b/generator/actions.ml
> index 2d291fb..e33655a 100644
> --- a/generator/actions.ml
> +++ b/generator/actions.ml
> @@ -13015,6 +13015,22 @@ given its inode.
> 
>  On some filesystem, it can find deleted files." };
> 
> +  { defaults with
> +    name = "filesystem_walk0"; added = (1, 33, 16);

The next version now is 1.33.17.

> +    style = RErr, [Mountable "device"; FileOut "filename";], [];
> +    proc_nr = Some 468;

It should be 465...

> +    optional = Some "libtsk";
> +    progress = true; cancellable = true;
> +    shortdesc = "walks through the filesystem content";
> +    longdesc = "\
> +This specialised command is used to walk through the internal structures
> +of a disk partition (eg. F</dev/sda1>) in order to return a list of all
> +the files and directories stored within.
> +
> +For each file or directory found, the function reports its path, its inode and whether
> +it is allocated or not.
> +
> +The output is stored in F<filename> on the host encoded in XDR format." };

The XDR encoding of the resulting file is basically meaningless to users
of the libguestfs API.  Considering this function seems to be only an
helper for filesystem_walk, then I'd say it needs to be internal (see
internal_feature_available for an example).

Thanks,
-- 
Pino Toscano
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: This is a digitally signed message part.
URL: <http://listman.redhat.com/archives/libguestfs/attachments/20160330/719a84e3/attachment.sig>


More information about the Libguestfs mailing list