The filesystem_walk API parses the FS internals of a partition and returns a list of all the files and directories contained within. It list deleted files and directories as well. For each node, it reports its relative path, its inode and its allocation status. This is the end user API for inspecting a disk partition content. The command can handle filenames with special characters. Example Use Cases: * inspect the FS content to see which deleted files could be possibly retrieved. * quickly check the FS content without mounting the disk * get a full mapping inode -> filepath of the disk Matteo Cafasso (2): added filesystem_walk API added filesystem_walk API tests generator/actions.ml | 13 +++ src/Makefile.am | 1 + src/tsk.c | 194 ++++++++++++++++++++++++++++++++++++++ tests/tsk/Makefile.am | 3 +- tests/tsk/test-filesystem-walk.sh | 48 ++++++++++ 5 files changed, 258 insertions(+), 1 deletion(-) create mode 100644 src/tsk.c create mode 100755 tests/tsk/test-filesystem-walk.sh -- 2.8.0.rc3
Signed-off-by: Matteo Cafasso <noxdafox@gmail.com> --- generator/actions.ml | 13 ++++ src/Makefile.am | 1 + src/tsk.c | 194 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 208 insertions(+) create mode 100644 src/tsk.c diff --git a/generator/actions.ml b/generator/actions.ml index e33655a..f795721 100644 --- a/generator/actions.ml +++ b/generator/actions.ml @@ -3546,6 +3546,19 @@ The environment variable C<XDG_RUNTIME_DIR> controls the default value: If C<XDG_RUNTIME_DIR> is set, then that is the default. Else F</tmp> is the default." }; + { defaults with + name = "filesystem_walk"; added = (1, 33, 16); + style = RStructList ("nodeinfos", "tsk_node"), [Mountable "device";], []; + optional = Some "libtsk"; + progress = true; cancellable = true; + shortdesc = "walk through the filesystem content"; + longdesc = "\ +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." }; + ] (* daemon_functions are any functions which cause some action diff --git a/src/Makefile.am b/src/Makefile.am index 3b4cd10..9f8af4c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -130,6 +130,7 @@ libguestfs_la_SOURCES = \ structs-copy.c \ structs-free.c \ tmpdirs.c \ + tsk.c \ whole-file.c \ libguestfs.syms diff --git a/src/tsk.c b/src/tsk.c new file mode 100644 index 0000000..551b615 --- /dev/null +++ b/src/tsk.c @@ -0,0 +1,194 @@ +/* libguestfs + * Copyright (C) 2016 Red Hat Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <string.h> +#include <rpc/xdr.h> +#include <rpc/types.h> + +#include "full-read.h" +#include "full-write.h" + +#include "guestfs.h" +#include "guestfs_protocol.h" +#include "guestfs-internal.h" +#include "guestfs-internal-all.h" +#include "guestfs-internal-actions.h" + +#ifdef HAVE_LIBTSK + +static int deserialise_inode_info +(guestfs_h *g, XDR *xdrs, struct guestfs_tsk_node *node_info); +static size_t read_file(guestfs_h *g, const char *path, char **dest); +static struct guestfs_tsk_node_list * +parse_filesystem_walk0 (guestfs_h *g, char *buf, size_t bufsize); +static void free_nodes(struct guestfs_tsk_node_list *nodes); + +struct guestfs_tsk_node_list * +guestfs_impl_filesystem_walk(guestfs_h *g, const char *mountable) +{ + size_t size = 0; + + CLEANUP_FREE char *buf = NULL; + CLEANUP_UNLINK_FREE char *tmpfile = NULL; + + if (guestfs_int_lazy_make_tmpdir (g) == -1) + return NULL; + + tmpfile = safe_asprintf(g, "%s/filesystem_walk%d", g->tmpdir, ++g->unique); + + if (guestfs_filesystem_walk0(g, mountable, tmpfile) < 0) + return NULL; + + if (!(size = read_file(g, tmpfile, &buf))) + return NULL; + + return parse_filesystem_walk0(g, buf, size); /* caller frees */ +} + +/* Read the whole file at path into dest. + * Return the size of the file, 0 on error. + */ +static size_t read_file(guestfs_h *g, const char *path, char **dest) +{ + int fd = 0; + size_t size; + struct stat statbuf; + + if ((fd = open(path, O_RDONLY|O_CLOEXEC)) == -1) { + perrorf(g, "open: %s", path); + return 0; + } + + if (fstat(fd, &statbuf) == -1) { + perrorf(g, "stat: %s", path); + close(fd); + return 0; + } + + size = statbuf.st_size; + if (!(*dest = malloc(size))) { + perrorf(g, "malloc: %zu bytes", size); + close(fd); + return 0; + } + + if (full_read(fd, *dest, size) != size) { + perrorf(g, "full-read: %s: %zu bytes", path, size); + close(fd); + return 0; + } + + if (close(fd) == -1) { + perrorf(g, "close: %s", path); + close(fd); + return 0; + } + + return size; +} + +/* Parse buf content and populate nodes list. + * Return a list of tsk_nodes on success, NULL on error. + */ +static struct guestfs_tsk_node_list * +parse_filesystem_walk0 (guestfs_h *g, char *buf, size_t bufsize) +{ + XDR xdr; + uint32_t index = 0; + struct guestfs_tsk_node_list *nodes = NULL; + + nodes = safe_malloc(g, sizeof *nodes); + nodes->len = 0; + nodes->val = NULL; + + xdrmem_create(&xdr, buf, bufsize, XDR_DECODE); + + for (index = 0; xdr_getpos(&xdr) < bufsize; index++) { + if (index == nodes->len) { + nodes->len = 2 * (nodes->len + 1); + nodes->val = safe_realloc(g, nodes->val, + nodes->len * sizeof (struct guestfs_tsk_node)); + } + + if (deserialise_inode_info(g, &xdr, &nodes->val[index]) < 0) + { + xdr_destroy(&xdr); + free_nodes(nodes); + + return NULL; + } + } + + xdr_destroy(&xdr); + + nodes->len = index; + nodes->val = safe_realloc(g, nodes->val, + nodes->len * sizeof (struct guestfs_tsk_node)); + + return nodes; +} + +/* Parse a single XDR encoded node_info. + * Return 0 on success, -1 on error. + */ +static int +deserialise_inode_info +(guestfs_h *g, XDR *xdrs, struct guestfs_tsk_node *node_info) +{ + size_t len = 0; + CLEANUP_FREE char *buf = NULL; + + if (!xdr_u_long(xdrs, &len)) + return -1; + + buf = safe_malloc(g, len); + + if (!xdr_string(xdrs, &buf, len)) + return -1; + if (!xdr_uint64_t(xdrs, &node_info->tsk_inode)) + return -1; + if (!xdr_uint32_t(xdrs, &node_info->tsk_allocated)) + return -1; + + node_info->tsk_name = safe_strndup(g, buf, len); + + return 0; +} + +/* Free the nodes list. */ +static void +free_nodes(struct guestfs_tsk_node_list *nodes) +{ + uint32_t index = 0; + + for (index = 0; index < nodes->len; index++) + if (nodes->val[index].tsk_name != NULL) + free(nodes->val[index].tsk_name); + + free(nodes); +} + +#endif /* !HAVE_LIBTSK */ -- 2.8.0.rc3
Matteo Cafasso
2016-Mar-29 17:42 UTC
[Libguestfs] [PATCH 2/2] added filesystem_walk API tests
Signed-off-by: Matteo Cafasso <noxdafox@gmail.com> --- tests/tsk/Makefile.am | 3 ++- tests/tsk/test-filesystem-walk.sh | 48 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) create mode 100755 tests/tsk/test-filesystem-walk.sh diff --git a/tests/tsk/Makefile.am b/tests/tsk/Makefile.am index c7eac09..a0d3744 100644 --- a/tests/tsk/Makefile.am +++ b/tests/tsk/Makefile.am @@ -21,7 +21,8 @@ TESTS = \ test-icat.sh \ test-blkcat.sh \ test-blkls.sh \ - test-filesystem-walk0.sh + test-filesystem-walk0.sh \ + test-filesystem-walk.sh TESTS_ENVIRONMENT = $(top_builddir)/run --test diff --git a/tests/tsk/test-filesystem-walk.sh b/tests/tsk/test-filesystem-walk.sh new file mode 100755 index 0000000..51e2f39 --- /dev/null +++ b/tests/tsk/test-filesystem-walk.sh @@ -0,0 +1,48 @@ +#!/bin/bash - +# libguestfs +# Copyright (C) 2016 Red Hat Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +# Test the filesystem-walk command. + +set -e + +if [ -n "$SKIP_TEST_FILESYSTEM_WALK_SH" ]; then + echo "$0: test skipped because environment variable is set." + exit 77 +fi + +# Skip if TSK is not supported by the appliance. +if ! guestfish add /dev/null : run : available "libtsk"; then + echo "$0: skipped because TSK is not available in the appliance" + exit 77 +fi + +if [ ! -s ../../test-data/phony-guests/ubuntu.img ]; then + echo "$0: skipped because ubuntu.img is zero-sized" + exit 77 +fi + +# list content of disk image and store it into a variable +output=$(guestfish --ro -a ../../test-data/phony-guests/ubuntu.img \ + run : filesystem-walk /dev/sda2 ) + +# test bin directory is in the list +echo $output | grep -q "{ tsk_name: bin tsk_inode: 32770 tsk_allocated: 1 }" +if [ $? neq 0 ]; then + echo "$0: /bin not found in files list." + exit 1 +fi -- 2.8.0.rc3
On Tuesday 29 March 2016 20:42:48 Matteo Cafasso wrote:> Signed-off-by: Matteo Cafasso <noxdafox@gmail.com> > ---See general notes sent as https://www.redhat.com/archives/libguestfs/2016-March/msg00269.html> generator/actions.ml | 13 ++++ > src/Makefile.am | 1 + > src/tsk.c | 194 +++++++++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 208 insertions(+) > create mode 100644 src/tsk.c > > diff --git a/generator/actions.ml b/generator/actions.ml > index e33655a..f795721 100644 > --- a/generator/actions.ml > +++ b/generator/actions.ml > @@ -3546,6 +3546,19 @@ The environment variable C<XDG_RUNTIME_DIR> controls the default > value: If C<XDG_RUNTIME_DIR> is set, then that is the default. > Else F</tmp> is the default." }; > > + { defaults with > + name = "filesystem_walk"; added = (1, 33, 16);1.33.17 now.> + style = RStructList ("nodeinfos", "tsk_node"), [Mountable "device";], [];This patch lacks the addition of tsk_node to generator/structs.ml.> + optional = Some "libtsk"; > + progress = true; cancellable = true; > + shortdesc = "walk through the filesystem content"; > + longdesc = "\ > +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." }; > + > ] > > (* daemon_functions are any functions which cause some action > diff --git a/src/Makefile.am b/src/Makefile.am > index 3b4cd10..9f8af4c 100644 > --- a/src/Makefile.am > +++ b/src/Makefile.am > @@ -130,6 +130,7 @@ libguestfs_la_SOURCES = \ > structs-copy.c \ > structs-free.c \ > tmpdirs.c \ > + tsk.c \ > whole-file.c \ > libguestfs.syms > > diff --git a/src/tsk.c b/src/tsk.c > new file mode 100644 > index 0000000..551b615 > --- /dev/null > +++ b/src/tsk.c > @@ -0,0 +1,194 @@ > +/* libguestfs > + * Copyright (C) 2016 Red Hat Inc. > + * > + * This library is free software; you can redistribute it and/or > + * modify it under the terms of the GNU Lesser General Public > + * License as published by the Free Software Foundation; either > + * version 2 of the License, or (at your option) any later version. > + * > + * This library is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + * Lesser General Public License for more details. > + * > + * You should have received a copy of the GNU Lesser General Public > + * License along with this library; if not, write to the Free Software > + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA > + */ > + > +#include <config.h> > + > +#include <stdio.h> > +#include <stdlib.h> > +#include <fcntl.h> > +#include <unistd.h> > +#include <sys/types.h> > +#include <sys/stat.h> > +#include <string.h> > +#include <rpc/xdr.h> > +#include <rpc/types.h> > + > +#include "full-read.h" > +#include "full-write.h"full-write.h is not needed.> + > +#include "guestfs.h" > +#include "guestfs_protocol.h" > +#include "guestfs-internal.h" > +#include "guestfs-internal-all.h" > +#include "guestfs-internal-actions.h" > + > +#ifdef HAVE_LIBTSKConsidering this file does not really need libtsk, just always compile the whole content of it; otherwise, building libguestfs without libtsk will result in undefined references to guestfs_impl_filesystem_walk when linking libguestfs.so (since it wouldn't be defined at all).> +static int deserialise_inode_info > +(guestfs_h *g, XDR *xdrs, struct guestfs_tsk_node *node_info); > +static size_t read_file(guestfs_h *g, const char *path, char **dest); > +static struct guestfs_tsk_node_list * > +parse_filesystem_walk0 (guestfs_h *g, char *buf, size_t bufsize); > +static void free_nodes(struct guestfs_tsk_node_list *nodes); > + > +struct guestfs_tsk_node_list * > +guestfs_impl_filesystem_walk(guestfs_h *g, const char *mountable) > +{ > + size_t size = 0; > + > + CLEANUP_FREE char *buf = NULL; > + CLEANUP_UNLINK_FREE char *tmpfile = NULL; > + > + if (guestfs_int_lazy_make_tmpdir (g) == -1) > + return NULL; > + > + tmpfile = safe_asprintf(g, "%s/filesystem_walk%d", g->tmpdir, ++g->unique); > + > + if (guestfs_filesystem_walk0(g, mountable, tmpfile) < 0) > + return NULL; > + > + if (!(size = read_file(g, tmpfile, &buf))) > + return NULL; > + > + return parse_filesystem_walk0(g, buf, size); /* caller frees */ > +} > + > +/* Read the whole file at path into dest. > + * Return the size of the file, 0 on error. > + */ > +static size_t read_file(guestfs_h *g, const char *path, char **dest)We have already guestfs_int_read_whole_file for this, see whole-file.c.> +{ > + int fd = 0; > + size_t size; > + struct stat statbuf; > + > + if ((fd = open(path, O_RDONLY|O_CLOEXEC)) == -1) { > + perrorf(g, "open: %s", path); > + return 0; > + } > + > + if (fstat(fd, &statbuf) == -1) { > + perrorf(g, "stat: %s", path); > + close(fd); > + return 0; > + } > + > + size = statbuf.st_size; > + if (!(*dest = malloc(size))) { > + perrorf(g, "malloc: %zu bytes", size); > + close(fd); > + return 0; > + } > + > + if (full_read(fd, *dest, size) != size) { > + perrorf(g, "full-read: %s: %zu bytes", path, size); > + close(fd); > + return 0; > + } > + > + if (close(fd) == -1) { > + perrorf(g, "close: %s", path); > + close(fd); > + return 0; > + } > + > + return size; > +} > + > +/* Parse buf content and populate nodes list. > + * Return a list of tsk_nodes on success, NULL on error. > + */ > +static struct guestfs_tsk_node_list * > +parse_filesystem_walk0 (guestfs_h *g, char *buf, size_t bufsize) > +{ > + XDR xdr; > + uint32_t index = 0; > + struct guestfs_tsk_node_list *nodes = NULL; > + > + nodes = safe_malloc(g, sizeof *nodes); > + nodes->len = 0; > + nodes->val = NULL; > + > + xdrmem_create(&xdr, buf, bufsize, XDR_DECODE); > + > + for (index = 0; xdr_getpos(&xdr) < bufsize; index++) { > + if (index == nodes->len) { > + nodes->len = 2 * (nodes->len + 1);Why +1 here?> + nodes->val = safe_realloc(g, nodes->val, > + nodes->len * sizeof (struct guestfs_tsk_node)); > + } > + > + if (deserialise_inode_info(g, &xdr, &nodes->val[index]) < 0) > + { > + xdr_destroy(&xdr); > + free_nodes(nodes); > + > + return NULL; > + } > + } > + > + xdr_destroy(&xdr); > + > + nodes->len = index; > + nodes->val = safe_realloc(g, nodes->val, > + nodes->len * sizeof (struct guestfs_tsk_node));What is this for?> + > + return nodes; > +} > + > +/* Parse a single XDR encoded node_info. > + * Return 0 on success, -1 on error. > + */ > +static int > +deserialise_inode_info > +(guestfs_h *g, XDR *xdrs, struct guestfs_tsk_node *node_info) > +{ > + size_t len = 0; > + CLEANUP_FREE char *buf = NULL; > + > + if (!xdr_u_long(xdrs, &len)) > + return -1; > + > + buf = safe_malloc(g, len); > + > + if (!xdr_string(xdrs, &buf, len)) > + return -1; > + if (!xdr_uint64_t(xdrs, &node_info->tsk_inode)) > + return -1; > + if (!xdr_uint32_t(xdrs, &node_info->tsk_allocated)) > + return -1; > + > + node_info->tsk_name = safe_strndup(g, buf, len); > + > + return 0; > +} > + > +/* Free the nodes list. */ > +static void > +free_nodes(struct guestfs_tsk_node_list *nodes)Having the right definition for the tsk_node struct makes the generator add compare/free functions for each struct and list of structs, so this function would be automatically generated already as guestfs_free_tsk_node_list.> +{ > + uint32_t index = 0; > + > + for (index = 0; index < nodes->len; index++) > + if (nodes->val[index].tsk_name != NULL) > + free(nodes->val[index].tsk_name);Note that free(NULL) works (it is defined by POSIX), so you don't need the null checks. Thanks, -- Pino Toscano