This series should be ready for merge v6: - rebase on master - changes according to last comments 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 | 155 ++++++++++++++++++++++++++++++------------- generator/actions.ml | 26 +++++++- src/MAX_PROC_NR | 2 +- src/tsk.c | 32 ++++++++- tests/tsk/Makefile.am | 3 +- tests/tsk/test-find-inode.sh | 66 ++++++++++++++++++ 6 files changed, 229 insertions(+), 55 deletions(-) create mode 100755 tests/tsk/test-find-inode.sh -- 2.9.3
Matteo Cafasso
2016-Sep-16 21:04 UTC
[Libguestfs] [PATCH v6 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 | 16 +++++++++++++--- generator/actions.ml | 5 ++--- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/daemon/tsk.c b/daemon/tsk.c index dd368d7..22ca483 100644 --- a/daemon/tsk.c +++ b/daemon/tsk.c @@ -49,6 +49,7 @@ 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 void reply_with_tsk_error (const char *); +static int entry_is_dot (TSK_FS_FILE *); int do_internal_filesystem_walk (const mountable_t *mountable) @@ -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,17 @@ reply_with_tsk_error (const char *funcname) reply_with_error ("%s: unknown error", funcname); } +/* Check whether the entry is dot and is not Root. + * Return 1 if it is dot, 0 otherwise or if it is the Root entry. + */ +static int +entry_is_dot (TSK_FS_FILE *fsfile) +{ + return (TSK_FS_ISDOT (fsfile->name->name) && + !(fsfile->fs_info->root_inum == fsfile->name->meta_addr && /* Root */ + STREQ (fsfile->name->name, "."))); /* Avoid 'bin/..' 'etc/..' */ +} + int optgroup_libtsk_available (void) { diff --git a/generator/actions.ml b/generator/actions.ml index a250954..68ecee3 100644 --- a/generator/actions.ml +++ b/generator/actions.ml @@ -3582,9 +3582,8 @@ and directories stored within. It is not necessary to mount the disk partition to run this command. -All entries in the filesystem are returned, excluding C<.> and -C<..>. This function can list deleted or unaccessible files. -The entries are I<not> sorted. +All entries in the filesystem are returned. This function can list deleted +or unaccessible files. The entries are I<not> sorted. The C<tsk_dirent> structure contains the following fields. -- 2.9.3
Matteo Cafasso
2016-Sep-16 21:04 UTC
[Libguestfs] [PATCH v6 2/6] daemon: refactor tsk code
Refactor logic in preparation for new APIs. Signed-off-by: Matteo Cafasso <noxdafox@gmail.com> --- daemon/tsk.c | 87 ++++++++++++++++++++++++++++++------------------------------ 1 file changed, 43 insertions(+), 44 deletions(-) diff --git a/daemon/tsk.c b/daemon/tsk.c index 22ca483..e5669da 100644 --- a/daemon/tsk.c +++ b/daemon/tsk.c @@ -44,10 +44,10 @@ 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 void reply_with_tsk_error (const char *); static int entry_is_dot (TSK_FS_FILE *); @@ -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; + + /* Set dirent fields */ + 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-Sep-16 21:04 UTC
[Libguestfs] [PATCH v6 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-Sep-16 21:04 UTC
[Libguestfs] [PATCH v6 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 e5669da..af803d7 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 (*inode != fsfile->name->meta_addr) + return TSK_WALK_CONT; + + 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. */ diff --git a/generator/actions.ml b/generator/actions.ml index 68ecee3..4e6627b 100644 --- a/generator/actions.ml +++ b/generator/actions.ml @@ -13232,6 +13232,15 @@ handle C<file>. If C<remove> is true (C<false> by default), then the transformation is removed." }; + { defaults with + name = "internal_find_inode"; added = (1, 35, 6); + style = RErr, [Mountable "device"; Int64 "inode"; FileOut "filename";], []; + proc_nr = Some 470; + 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 5ef9d24..5f476b6 100644 --- a/src/MAX_PROC_NR +++ b/src/MAX_PROC_NR @@ -1 +1 @@ -469 +470 -- 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 4e6627b..91a1819 100644 --- a/generator/actions.ml +++ b/generator/actions.ml @@ -3717,6 +3717,18 @@ Unknown file type =back" }; + { defaults with + name = "find_inode"; added = (1, 35, 6); + 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-Sep-16 21:04 UTC
[Libguestfs] [PATCH v6 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
On Saturday, 17 September 2016 00:04:22 CEST Matteo Cafasso wrote:> This series should be ready for merge > > v6: > > - rebase on master > > - changes according to last commentsThe series looks much better now -- thanks. I just pushed patches #1 and #2, as they are fine. Regarding patch #3, IMHO you could merge in it the work done in patch #4 of the other find_block series [1], as both are simple refactoring works. [1] https://www.redhat.com/archives/libguestfs/2016-September/msg00108.html Thanks, -- Pino Toscano