v2: - refactor logic to reduce code duplication - better functions naming Matteo Cafasso (6): filesystem_walk: fixed root inode listing daemon: refactor tsk code lib: rename tsk internal function New API: internal_find_inode New API: find_inode find_inode: added API tests daemon/tsk.c | 156 ++++++++++++++++++++++++++++++------------- generator/actions.ml | 21 ++++++ src/MAX_PROC_NR | 2 +- src/tsk.c | 32 ++++++++- tests/tsk/Makefile.am | 3 +- tests/tsk/test-find-inode.sh | 66 ++++++++++++++++++ 6 files changed, 228 insertions(+), 52 deletions(-) create mode 100755 tests/tsk/test-find-inode.sh -- 2.9.3
Matteo Cafasso
2016-Aug-25 20:53 UTC
[Libguestfs] [PATCH v2 1/6] filesystem_walk: fixed root inode listing
With the current implementation, the root inode of the given partition is ignored. The root inode is now reported. Its name will be a single dot '.' reproducing the TSK API. Signed-off-by: Matteo Cafasso <noxdafox@gmail.com> --- daemon/tsk.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/daemon/tsk.c b/daemon/tsk.c index dd368d7..6e6df6d 100644 --- a/daemon/tsk.c +++ b/daemon/tsk.c @@ -48,6 +48,7 @@ static char file_type (TSK_FS_FILE *); static int file_flags (TSK_FS_FILE *fsfile); static void file_metadata (TSK_FS_META *, guestfs_int_tsk_dirent *); static int send_dirent_info (guestfs_int_tsk_dirent *); +static int entry_is_dot(TSK_FS_FILE *); static void reply_with_tsk_error (const char *); int @@ -113,9 +114,7 @@ fswalk_callback (TSK_FS_FILE *fsfile, const char *path, void *data) CLEANUP_FREE char *fname = NULL; struct guestfs_int_tsk_dirent dirent; - /* Ignore ./ and ../ */ - ret = TSK_FS_ISDOT (fsfile->name->name); - if (ret != 0) + if (entry_is_dot(fsfile)) return TSK_WALK_CONT; /* Build the full relative path of the entry */ @@ -271,6 +270,18 @@ reply_with_tsk_error (const char *funcname) reply_with_error ("%s: unknown error", funcname); } +/* Check whether the entry is dot and is not Root. */ +static int +entry_is_dot(TSK_FS_FILE *fsfile) +{ + if (TSK_FS_ISDOT (fsfile->name->name)) + if (fsfile->fs_info->root_inum != fsfile->name->meta_addr || + strcmp (fsfile->name->name, ".")) + return 1; + + return 0; +} + int optgroup_libtsk_available (void) { -- 2.9.3
Matteo Cafasso
2016-Aug-25 20:53 UTC
[Libguestfs] [PATCH v2 2/6] daemon: refactor tsk code
Refactor logic in preparation for new APIs. Signed-off-by: Matteo Cafasso <noxdafox@gmail.com> --- daemon/tsk.c | 89 ++++++++++++++++++++++++++++++------------------------------ 1 file changed, 44 insertions(+), 45 deletions(-) diff --git a/daemon/tsk.c b/daemon/tsk.c index 6e6df6d..eb4f301 100644 --- a/daemon/tsk.c +++ b/daemon/tsk.c @@ -44,12 +44,12 @@ enum tsk_dirent_flags { static int open_filesystem (const char *, TSK_IMG_INFO **, TSK_FS_INFO **); static TSK_WALK_RET_ENUM fswalk_callback (TSK_FS_FILE *, const char *, void *); +static int send_dirent_info (TSK_FS_FILE *, const char *); static char file_type (TSK_FS_FILE *); static int file_flags (TSK_FS_FILE *fsfile); static void file_metadata (TSK_FS_META *, guestfs_int_tsk_dirent *); -static int send_dirent_info (guestfs_int_tsk_dirent *); -static int entry_is_dot(TSK_FS_FILE *); static void reply_with_tsk_error (const char *); +static int entry_is_dot(TSK_FS_FILE *); int do_internal_filesystem_walk (const mountable_t *mountable) @@ -104,29 +104,44 @@ open_filesystem (const char *device, TSK_IMG_INFO **img, TSK_FS_INFO **fs) } /* 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. + * Parse the node, encode it into an XDR structure and send it to the library. * Return TSK_WALK_CONT on success, TSK_WALK_ERROR on error. */ static TSK_WALK_RET_ENUM fswalk_callback (TSK_FS_FILE *fsfile, const char *path, void *data) { int ret = 0; - CLEANUP_FREE char *fname = NULL; - struct guestfs_int_tsk_dirent dirent; if (entry_is_dot(fsfile)) return TSK_WALK_CONT; + ret = send_dirent_info (fsfile, path); + + return (ret == 0) ? TSK_WALK_CONT : TSK_WALK_ERROR; +} + +/* Extract the information from the entry, serialize and send it out. + * Return 0 on success, -1 on error. + */ +static int +send_dirent_info(TSK_FS_FILE *fsfile, const char *path) +{ + XDR xdr; + int ret = 0; + size_t len = 0; + struct guestfs_int_tsk_dirent dirent; + CLEANUP_FREE char *buf = NULL, *fname = NULL; + + /* Extract dirent information. */ + memset (&dirent, 0, sizeof dirent); + /* Build the full relative path of the entry */ ret = asprintf (&fname, "%s%s", path, fsfile->name->name); if (ret < 0) { perror ("asprintf"); - return TSK_WALK_ERROR; + return -1; } - /* Set dirent fields */ - memset (&dirent, 0, sizeof dirent); - dirent.tsk_inode = fsfile->name->meta_addr; dirent.tsk_type = file_type (fsfile); dirent.tsk_name = fname; @@ -134,10 +149,27 @@ fswalk_callback (TSK_FS_FILE *fsfile, const char *path, void *data) file_metadata (fsfile->meta, &dirent); - ret = send_dirent_info (&dirent); - ret = (ret == 0) ? TSK_WALK_CONT : TSK_WALK_ERROR; + /* Serialize tsk_dirent struct. */ + buf = malloc (GUESTFS_MAX_CHUNK_SIZE); + if (buf == NULL) { + perror ("malloc"); + return -1; + } - return ret; + xdrmem_create (&xdr, buf, GUESTFS_MAX_CHUNK_SIZE, XDR_ENCODE); + + ret = xdr_guestfs_int_tsk_dirent (&xdr, &dirent); + if (ret == 0) { + perror ("xdr_guestfs_int_tsk_dirent"); + return -1; + } + + len = xdr_getpos (&xdr); + + xdr_destroy (&xdr); + + /* Send serialised tsk_dirent out. */ + return send_file_write (buf, len); } /* Inspect fsfile to identify its type. */ @@ -221,39 +253,6 @@ file_metadata (TSK_FS_META *fsmeta, guestfs_int_tsk_dirent *dirent) } } -/* Serialise dirent into XDR stream and send it to the appliance. - * Return 0 on success, -1 on error. - */ -static int -send_dirent_info (guestfs_int_tsk_dirent *dirent) -{ - XDR xdr; - int ret = 0; - size_t len = 0; - CLEANUP_FREE char *buf = NULL; - - buf = malloc (GUESTFS_MAX_CHUNK_SIZE); - if (buf == NULL) { - perror ("malloc"); - return -1; - } - - /* Serialise tsk_dirent struct. */ - xdrmem_create (&xdr, buf, GUESTFS_MAX_CHUNK_SIZE, XDR_ENCODE); - - ret = xdr_guestfs_int_tsk_dirent (&xdr, dirent); - if (ret == 0) { - perror ("xdr_guestfs_int_tsk_dirent"); - return -1; - } - len = xdr_getpos (&xdr); - - xdr_destroy (&xdr); - - /* Send serialised tsk_dirent out. */ - return send_file_write (buf, len); -} - /* Parse TSK error and send it to the appliance. */ static void reply_with_tsk_error (const char *funcname) -- 2.9.3
Matteo Cafasso
2016-Aug-25 20:53 UTC
[Libguestfs] [PATCH v2 3/6] lib: rename tsk internal function
Use a more generic name as the function will be used by other APIs. Signed-off-by: Matteo Cafasso <noxdafox@gmail.com> --- src/tsk.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tsk.c b/src/tsk.c index 90177ab..cc89e31 100644 --- a/src/tsk.c +++ b/src/tsk.c @@ -34,7 +34,7 @@ #include "guestfs-internal-all.h" #include "guestfs-internal-actions.h" -static struct guestfs_tsk_dirent_list *parse_filesystem_walk (guestfs_h *, FILE *); +static struct guestfs_tsk_dirent_list *parse_dirent_file (guestfs_h *, FILE *); static int deserialise_dirent_list (guestfs_h *, FILE *, struct guestfs_tsk_dirent_list *); struct guestfs_tsk_dirent_list * @@ -60,14 +60,14 @@ guestfs_impl_filesystem_walk (guestfs_h *g, const char *mountable) return NULL; } - return parse_filesystem_walk (g, fp); /* caller frees */ + return parse_dirent_file (g, fp); /* caller frees */ } /* Parse the file content and return dirents list. * Return a list of tsk_dirent on success, NULL on error. */ static struct guestfs_tsk_dirent_list * -parse_filesystem_walk (guestfs_h *g, FILE *fp) +parse_dirent_file (guestfs_h *g, FILE *fp) { int ret = 0; struct guestfs_tsk_dirent_list *dirents = NULL; -- 2.9.3
Matteo Cafasso
2016-Aug-25 20:53 UTC
[Libguestfs] [PATCH v2 4/6] New API: internal_find_inode
The internal_find_inode command searches all entries referring to the given inode and returns a tsk_dirent structure for each of them. The command is able to retrieve information regarding deleted or unaccessible files where other commands such as stat or find would fail. The gathered list of tsk_dirent structs is serialised into XDR format and written to a file by the appliance. Signed-off-by: Matteo Cafasso <noxdafox@gmail.com> --- daemon/tsk.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ generator/actions.ml | 9 +++++++++ src/MAX_PROC_NR | 2 +- 3 files changed, 62 insertions(+), 1 deletion(-) diff --git a/daemon/tsk.c b/daemon/tsk.c index eb4f301..03a7e00 100644 --- a/daemon/tsk.c +++ b/daemon/tsk.c @@ -44,6 +44,7 @@ enum tsk_dirent_flags { static int open_filesystem (const char *, TSK_IMG_INFO **, TSK_FS_INFO **); static TSK_WALK_RET_ENUM fswalk_callback (TSK_FS_FILE *, const char *, void *); +static TSK_WALK_RET_ENUM findino_callback (TSK_FS_FILE *, const char *, void *); static int send_dirent_info (TSK_FS_FILE *, const char *); static char file_type (TSK_FS_FILE *); static int file_flags (TSK_FS_FILE *fsfile); @@ -79,6 +80,35 @@ do_internal_filesystem_walk (const mountable_t *mountable) return ret; } +int +do_internal_find_inode (const mountable_t *mountable, int64_t inode) +{ + int ret = -1; + TSK_FS_INFO *fs = NULL; + TSK_IMG_INFO *img = NULL; /* Used internally by tsk_fs_dir_walk */ + const 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; + + ret = open_filesystem (mountable->device, &img, &fs); + if (ret < 0) + return ret; + + reply (NULL, NULL); /* Reply message. */ + + ret = tsk_fs_dir_walk (fs, fs->root_inum, flags, + findino_callback, (void *) &inode); + if (ret == 0) + ret = send_file_end (0); /* File transfer end. */ + else + send_file_end (1); /* Cancel file transfer. */ + + 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. */ @@ -120,6 +150,28 @@ fswalk_callback (TSK_FS_FILE *fsfile, const char *path, void *data) return (ret == 0) ? TSK_WALK_CONT : TSK_WALK_ERROR; } +/* Find inode, it gets called on every FS node. + * If the FS node address is the given one, parse it, + * encode it into an XDR structure and send it to the library. + * Return TSK_WALK_CONT on success, TSK_WALK_ERROR on error. + */ +static TSK_WALK_RET_ENUM +findino_callback (TSK_FS_FILE *fsfile, const char *path, void *data) +{ + int ret = 0; + uint64_t *inode = (uint64_t *) data; + + if (entry_is_dot(fsfile)) + return TSK_WALK_CONT; + + if (*inode != fsfile->name->meta_addr) + return TSK_WALK_CONT; + + ret = send_dirent_info (fsfile, path); + + return (ret == 0) ? TSK_WALK_CONT : TSK_WALK_ERROR; +} + /* Extract the information from the entry, serialize and send it out. * Return 0 on success, -1 on error. */ diff --git a/generator/actions.ml b/generator/actions.ml index eccef00..449443f 100644 --- a/generator/actions.ml +++ b/generator/actions.ml @@ -13205,6 +13205,15 @@ only the unallocated blocks will be extracted. This is useful to detect hidden data or to retrieve deleted files which data units have not been overwritten yet." }; + { defaults with + name = "internal_find_inode"; added = (1, 35, 2); + style = RErr, [Mountable "device"; Int64 "inode"; FileOut "filename";], []; + proc_nr = Some 469; + visibility = VInternal; + optional = Some "libtsk"; + shortdesc = "search the entries associated to the given inode"; + longdesc = "Internal function for find_inode." }; + ] (* Non-API meta-commands available only in guestfish. diff --git a/src/MAX_PROC_NR b/src/MAX_PROC_NR index 1023289..5ef9d24 100644 --- a/src/MAX_PROC_NR +++ b/src/MAX_PROC_NR @@ -1 +1 @@ -468 +469 -- 2.9.3
Library's counterpart of the daemon's internal_find_inode command. It writes the daemon's command output on a temporary file and parses it, deserialising the XDR formatted tsk_dirent structs. It returns to the caller the list of tsk_dirent structs generated by the internal_find_inode command. Signed-off-by: Matteo Cafasso <noxdafox@gmail.com> --- generator/actions.ml | 12 ++++++++++++ src/tsk.c | 26 ++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/generator/actions.ml b/generator/actions.ml index 449443f..7e19fa0 100644 --- a/generator/actions.ml +++ b/generator/actions.ml @@ -3702,6 +3702,18 @@ Unknown file type =back" }; + { defaults with + name = "find_inode"; added = (1, 35, 2); + style = RStructList ("dirents", "tsk_dirent"), [Mountable "device"; Int64 "inode";], []; + optional = Some "libtsk"; + progress = true; cancellable = true; + shortdesc = "search the entries associated to the given inode"; + longdesc = "\ +Searches all the entries associated with the given inode. + +For each entry, a C<tsk_dirent> structure is returned. +See C<filesystem_walk> for more information about C<tsk_dirent> structures." }; + ] (* daemon_functions are any functions which cause some action diff --git a/src/tsk.c b/src/tsk.c index cc89e31..d3da834 100644 --- a/src/tsk.c +++ b/src/tsk.c @@ -63,6 +63,32 @@ guestfs_impl_filesystem_walk (guestfs_h *g, const char *mountable) return parse_dirent_file (g, fp); /* caller frees */ } +struct guestfs_tsk_dirent_list * +guestfs_impl_find_inode (guestfs_h *g, const char *mountable, int64_t inode) +{ + int ret = 0; + CLEANUP_FCLOSE FILE *fp = NULL; + CLEANUP_UNLINK_FREE char *tmpfile = NULL; + + ret = guestfs_int_lazy_make_tmpdir (g); + if (ret < 0) + return NULL; + + tmpfile = safe_asprintf (g, "%s/find_inode%d", g->tmpdir, ++g->unique); + + ret = guestfs_internal_find_inode (g, mountable, inode, tmpfile); + if (ret < 0) + return NULL; + + fp = fopen (tmpfile, "r"); + if (fp == NULL) { + perrorf (g, "fopen: %s", tmpfile); + return NULL; + } + + return parse_dirent_file (g, fp); /* caller frees */ +} + /* Parse the file content and return dirents list. * Return a list of tsk_dirent on success, NULL on error. */ -- 2.9.3
Matteo Cafasso
2016-Aug-25 20:53 UTC
[Libguestfs] [PATCH v2 6/6] find_inode: added API tests
NTFS file system always has the MFT file at inode 0. This reliable information helps testing the API. Signed-off-by: Matteo Cafasso <noxdafox@gmail.com> --- tests/tsk/Makefile.am | 3 +- tests/tsk/test-find-inode.sh | 66 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 1 deletion(-) create mode 100755 tests/tsk/test-find-inode.sh diff --git a/tests/tsk/Makefile.am b/tests/tsk/Makefile.am index 0b50839..07c74f9 100644 --- a/tests/tsk/Makefile.am +++ b/tests/tsk/Makefile.am @@ -20,7 +20,8 @@ include $(top_srcdir)/subdir-rules.mk TESTS = \ test-download-inode.sh \ test-download-blocks.sh \ - test-filesystem-walk.sh + test-filesystem-walk.sh \ + test-find-inode.sh TESTS_ENVIRONMENT = $(top_builddir)/run --test diff --git a/tests/tsk/test-find-inode.sh b/tests/tsk/test-find-inode.sh new file mode 100755 index 0000000..f69fcf6 --- /dev/null +++ b/tests/tsk/test-find-inode.sh @@ -0,0 +1,66 @@ +#!/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 find-inode command. + +if [ -n "$SKIP_TEST_FIND_INODE_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/windows.img ]; then + echo "$0: skipped because windows.img is zero-sized" + exit 77 +fi + +output=$( +guestfish --ro -a ../../test-data/phony-guests/windows.img <<EOF +run +find-inode /dev/sda2 0 +EOF +) + +# test $MFT is in the list +echo $output | grep -zq '{ tsk_inode: 0 +tsk_type: r +tsk_size: .* +tsk_name: \$MFT +tsk_flags: 1 +tsk_atime_sec: .* +tsk_atime_nsec: .* +tsk_mtime_sec: .* +tsk_mtime_nsec: .* +tsk_ctime_sec: .* +tsk_ctime_nsec: .* +tsk_crtime_sec: .* +tsk_crtime_nsec: .* +tsk_nlink: 1 +tsk_link: +tsk_spare1: 0 }' +if [ $? != 0 ]; then + echo "$0: \$MFT not found in files list." + echo "File list:" + echo $output + exit 1 +fi -- 2.9.3
Pino Toscano
2016-Aug-26 11:15 UTC
Re: [Libguestfs] [PATCH v2 1/6] filesystem_walk: fixed root inode listing
On Thursday, 25 August 2016 23:53:51 CEST Matteo Cafasso wrote:> With the current implementation, the root inode of the given partition > is ignored. > > The root inode is now reported. Its name will be a single dot '.' > reproducing the TSK API. > > Signed-off-by: Matteo Cafasso <noxdafox@gmail.com> > --- > daemon/tsk.c | 17 ++++++++++++++--- > 1 file changed, 14 insertions(+), 3 deletions(-) > > diff --git a/daemon/tsk.c b/daemon/tsk.c > index dd368d7..6e6df6d 100644 > --- a/daemon/tsk.c > +++ b/daemon/tsk.c > @@ -48,6 +48,7 @@ static char file_type (TSK_FS_FILE *); > static int file_flags (TSK_FS_FILE *fsfile); > static void file_metadata (TSK_FS_META *, guestfs_int_tsk_dirent *); > static int send_dirent_info (guestfs_int_tsk_dirent *); > +static int entry_is_dot(TSK_FS_FILE *); > static void reply_with_tsk_error (const char *);Since in patch #2 this forward declaration is moved, put it at the right place already in this patch.> int > @@ -113,9 +114,7 @@ fswalk_callback (TSK_FS_FILE *fsfile, const char *path, void *data) > CLEANUP_FREE char *fname = NULL; > struct guestfs_int_tsk_dirent dirent; > > - /* Ignore ./ and ../ */ > - ret = TSK_FS_ISDOT (fsfile->name->name); > - if (ret != 0) > + if (entry_is_dot(fsfile)) > return TSK_WALK_CONT;Nitpick: add a space between the function name and the opening round bracket.> /* Build the full relative path of the entry */ > @@ -271,6 +270,18 @@ reply_with_tsk_error (const char *funcname) > reply_with_error ("%s: unknown error", funcname); > } > > +/* Check whether the entry is dot and is not Root. */ > +static int > +entry_is_dot(TSK_FS_FILE *fsfile)Ditto.> +{ > + if (TSK_FS_ISDOT (fsfile->name->name)) > + if (fsfile->fs_info->root_inum != fsfile->name->meta_addr || > + strcmp (fsfile->name->name, "."))Simply merge the two if's into a single if (A && B) condition? Also, the strcmp is already done by TSK_FS_ISDOT, isn't it? So this should be like: if (TSK_FS_ISDOT (fsfile->name->name) && (fsfile->fs_info->root_inum != fsfile->name->meta_addr)) -- Pino Toscano
Pino Toscano
2016-Aug-26 11:27 UTC
Re: [Libguestfs] [PATCH v2 2/6] daemon: refactor tsk code
On Thursday, 25 August 2016 23:53:52 CEST Matteo Cafasso wrote:> Refactor logic in preparation for new APIs. > > Signed-off-by: Matteo Cafasso <noxdafox@gmail.com> > ---Mostly LGTM, just a couple of minor notes.> +static int > +send_dirent_info(TSK_FS_FILE *fsfile, const char *path)Nitpick wrt coding style.> + /* Extract dirent information. */ > + memset (&dirent, 0, sizeof dirent);I guess the comment that was below fits better: /* Set dirent fields */ Thanks, -- Pino Toscano
Pino Toscano
2016-Aug-26 11:49 UTC
Re: [Libguestfs] [PATCH v2 4/6] New API: internal_find_inode
On Thursday, 25 August 2016 23:53:54 CEST Matteo Cafasso wrote:> The internal_find_inode command searches all entries referring to the > given inode and returns a tsk_dirent structure for each of them. > > The command is able to retrieve information regarding deleted > or unaccessible files where other commands such as stat or find > would fail. > > The gathered list of tsk_dirent structs is serialised into XDR format > and written to a file by the appliance. > > Signed-off-by: Matteo Cafasso <noxdafox@gmail.com> > ---Mostly LGTM, just a couple of notes.> + > + if (entry_is_dot(fsfile))Coding style nitpick.> + { defaults with > + name = "internal_find_inode"; added = (1, 35, 2); > + style = RErr, [Mountable "device"; Int64 "inode"; FileOut "filename";], []; > + proc_nr = Some 469;This now needs to be 470. Thanks, -- Pino Toscano
On Thursday, 25 August 2016 23:53:50 CEST Matteo Cafasso wrote:> v2: > > - refactor logic to reduce code duplication > - better functions naming > > > Matteo Cafasso (6): > filesystem_walk: fixed root inode listing > daemon: refactor tsk code > lib: rename tsk internal function > New API: internal_find_inode > New API: find_inode > find_inode: added API testsThe series mostly LGTM, with few notes left in some of the patches. Thanks, -- Pino Toscano