v4: - refactor entry_is_dot My apologies for the duplicated submission but I did not read the next e-mail. The tsk_fs_dir_walk API will list all the entries including '.' and '..' in a similar manner as for 'ls -a'. This means our callback will be called for the following entries: . <-- the Root entry etc/. etc/.. <-- again the Root entry etc/systemd/. etc/systemd/.. bin/. bin/.. <-- again the Root entry We want to return the Root entry only once. Therefore, once we know that the entry under analysis is a dot if (TSK_FS_ISDOT (fsfile->name->name)) We check whether the inode is root if (fsfile->fs_info->root_inum == fsfile->name->meta_addr) But we want to make sure is the first root entry and not the parent directory of other directories. if (STREQ(fsfile->name->name, ".") I opened up a bit the logic to make it more clear. 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 | 161 ++++++++++++++++++++++++++++++------------- 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, 233 insertions(+), 52 deletions(-) create mode 100755 tests/tsk/test-find-inode.sh -- 2.9.3
Matteo Cafasso
2016-Aug-26 14:14 UTC
[Libguestfs] [PATCH v4 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 | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/daemon/tsk.c b/daemon/tsk.c index dd368d7..9884472 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,23 @@ 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) +{ + if (TSK_FS_ISDOT (fsfile->name->name)) { + if (fsfile->fs_info->root_inum == fsfile->name->meta_addr && /* Root */ + STREQ(fsfile->name->name, ".")) /* Avoid 'bin/..' 'etc/..' */ + return 0; + else + return 1; + } + + return 0; +} + int optgroup_libtsk_available (void) { -- 2.9.3
Matteo Cafasso
2016-Aug-26 14:14 UTC
[Libguestfs] [PATCH v4 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 9884472..354dfb0 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-Aug-26 14:14 UTC
[Libguestfs] [PATCH v4 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-26 14:14 UTC
[Libguestfs] [PATCH v4 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 354dfb0..00576e7 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..42593b9 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 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 1023289..5ef9d24 100644 --- a/src/MAX_PROC_NR +++ b/src/MAX_PROC_NR @@ -1 +1 @@ -468 +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 42593b9..a52b8df 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-26 14:14 UTC
[Libguestfs] [PATCH v4 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