v3: - fixed attribute walk callback: checking against TSK_FS_BLOCK_FLAG_RAW flag would exclude compressed data blocks which are still important. Yet we want to exclude sparse blocks (TSK_FS_BLOCK_FLAG_SPARSE) as they are not stored on the disk. Matteo Cafasso (3): New API: internal_find_block New API: find_block find_block: added API tests daemon/tsk.c | 91 ++++++++++++++++++++++++++++++++++++++++++++ generator/actions.ml | 25 ++++++++++++ src/MAX_PROC_NR | 2 +- src/tsk.c | 17 +++++++++ tests/tsk/Makefile.am | 1 + tests/tsk/test-find-block.sh | 66 ++++++++++++++++++++++++++++++++ 6 files changed, 201 insertions(+), 1 deletion(-) create mode 100755 tests/tsk/test-find-block.sh -- 2.9.3
Matteo Cafasso
2016-Sep-20 13:19 UTC
[Libguestfs] [PATCH v3 1/3] New API: internal_find_block
The internal_find_block command searches all entries referring to the
given filesystem data block and returns a tsk_dirent structure
for each of them.
For filesystems such as NTFS which do not delete the block mapping
when removing files, it is possible to get multiple non-allocated
entries for the same block.
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 | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++++
generator/actions.ml | 9 ++++++
src/MAX_PROC_NR | 2 +-
3 files changed, 101 insertions(+), 1 deletion(-)
diff --git a/daemon/tsk.c b/daemon/tsk.c
index af803d7..f971840 100644
--- a/daemon/tsk.c
+++ b/daemon/tsk.c
@@ -20,6 +20,7 @@
#include <stdio.h>
#include <stdlib.h>
+#include <stdbool.h>
#include <inttypes.h>
#include <string.h>
#include <unistd.h>
@@ -42,9 +43,16 @@ enum tsk_dirent_flags {
DIRENT_COMPRESSED = 0x04
};
+typedef struct {
+ bool found;
+ uint64_t block;
+} findblk_data;
+
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 TSK_WALK_RET_ENUM findblk_callback (TSK_FS_FILE *, const char *, void
*);
+static TSK_WALK_RET_ENUM attrwalk_callback (TSK_FS_FILE *, TSK_OFF_T ,
TSK_DADDR_T , char *, size_t , TSK_FS_BLOCK_FLAG_ENUM , 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);
@@ -109,6 +117,35 @@ do_internal_find_inode (const mountable_t *mountable,
int64_t inode)
return ret;
}
+int
+do_internal_find_block (const mountable_t *mountable, int64_t block)
+{
+ 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,
+ findblk_callback, (void *) &block);
+ 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.
*/
@@ -172,6 +209,60 @@ findino_callback (TSK_FS_FILE *fsfile, const char *path,
void *data)
return (ret == 0) ? TSK_WALK_CONT : TSK_WALK_ERROR;
}
+/* Find block, it gets called on every FS node.
+ *
+ * Return TSK_WALK_CONT on success, TSK_WALK_ERROR on error.
+ */
+static TSK_WALK_RET_ENUM
+findblk_callback (TSK_FS_FILE *fsfile, const char *path, void *data)
+{
+ findblk_data blkdata;
+ const TSK_FS_ATTR *fsattr = NULL;
+ int ret = 0, count = 0, index = 0;
+ const int flags = TSK_FS_FILE_WALK_FLAG_AONLY | TSK_FS_FILE_WALK_FLAG_SLACK;
+
+ if (entry_is_dot (fsfile))
+ return TSK_WALK_CONT;
+
+ blkdata.found = false;
+ blkdata.block = * (uint64_t *) data;
+
+ /* Retrieve block list */
+ count = tsk_fs_file_attr_getsize (fsfile);
+
+ for (index = 0; index < count; index++) {
+ fsattr = tsk_fs_file_attr_get_idx (fsfile, index);
+
+ if (fsattr != NULL && fsattr->flags & TSK_FS_ATTR_NONRES)
+ tsk_fs_attr_walk (fsattr, flags, attrwalk_callback, (void *)
&blkdata);
+ }
+
+ if (blkdata.found)
+ ret = send_dirent_info (fsfile, path);
+
+ return (ret == 0) ? TSK_WALK_CONT : TSK_WALK_ERROR;
+}
+
+/* Attribute walk, searches the given block within the FS node attributes.
+ *
+ * Return TSK_WALK_CONT on success, TSK_WALK_ERROR on error.
+ */
+static TSK_WALK_RET_ENUM
+attrwalk_callback (TSK_FS_FILE *fsfile, TSK_OFF_T offset,
+ TSK_DADDR_T blkaddr, char *buf, size_t size,
+ TSK_FS_BLOCK_FLAG_ENUM flags, void *data)
+{
+ findblk_data *blkdata = (findblk_data *) data;
+
+ if (!(flags & TSK_FS_BLOCK_FLAG_SPARSE) && blkaddr ==
blkdata->block) {
+ blkdata->found = true;
+
+ return TSK_WALK_STOP;
+ }
+
+ return TSK_WALK_CONT;
+}
+
/* 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 91a1819..b38a30f 100644
--- a/generator/actions.ml
+++ b/generator/actions.ml
@@ -13253,6 +13253,15 @@ is removed." };
shortdesc = "search the entries associated to the given inode";
longdesc = "Internal function for find_inode." };
+ { defaults with
+ name = "internal_find_block"; added = (1, 35, 6);
+ style = RErr, [Mountable "device"; Int64 "block";
FileOut "filename";], [];
+ proc_nr = Some 471;
+ visibility = VInternal;
+ optional = Some "libtsk";
+ shortdesc = "search the entries associated to the given block";
+ longdesc = "Internal function for find_block." };
+
]
(* Non-API meta-commands available only in guestfish.
diff --git a/src/MAX_PROC_NR b/src/MAX_PROC_NR
index 5f476b6..c305aa5 100644
--- a/src/MAX_PROC_NR
+++ b/src/MAX_PROC_NR
@@ -1 +1 @@
-470
+471
--
2.9.3
Library's counterpart of the daemon's internal_find_block 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_block command.
Signed-off-by: Matteo Cafasso <noxdafox@gmail.com>
---
generator/actions.ml | 16 ++++++++++++++++
src/tsk.c | 17 +++++++++++++++++
2 files changed, 33 insertions(+)
diff --git a/generator/actions.ml b/generator/actions.ml
index b38a30f..8947551 100644
--- a/generator/actions.ml
+++ b/generator/actions.ml
@@ -3729,6 +3729,22 @@ 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." };
+ { defaults with
+ name = "find_block"; added = (1, 35, 6);
+ style = RStructList ("dirents", "tsk_dirent"),
[Mountable "device"; Int64 "block";], [];
+ optional = Some "libtsk";
+ progress = true; cancellable = true;
+ shortdesc = "search the entries referring to the given data
block";
+ longdesc = "\
+Searches all the entries referring to the given data block.
+
+Certain filesystems preserve the block mapping when deleting a file.
+Therefore, it is possible to see multiple deleted files referring
+to the same block.
+
+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 1def9c9..7db6f71 100644
--- a/src/tsk.c
+++ b/src/tsk.c
@@ -72,6 +72,23 @@ guestfs_impl_find_inode (guestfs_h *g, const char *mountable,
int64_t inode)
return parse_dirent_file (g, tmpfile); /* caller frees */
}
+struct guestfs_tsk_dirent_list *
+guestfs_impl_find_block (guestfs_h *g, const char *mountable, int64_t block)
+{
+ int ret = 0;
+ CLEANUP_UNLINK_FREE char *tmpfile = NULL;
+
+ tmpfile = make_temp_file (g, "find_block");
+ if (tmpfile == NULL)
+ return NULL;
+
+ ret = guestfs_internal_find_block (g, mountable, block, tmpfile);
+ if (ret < 0)
+ return NULL;
+
+ return parse_dirent_file (g, tmpfile); /* 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-20 13:19 UTC
[Libguestfs] [PATCH v3 3/3] find_block: added API tests
NTFS file system always has the Boot file at block 0. This reliable
information helps testing the API.
Signed-off-by: Matteo Cafasso <noxdafox@gmail.com>
---
tests/tsk/Makefile.am | 1 +
tests/tsk/test-find-block.sh | 66 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 67 insertions(+)
create mode 100755 tests/tsk/test-find-block.sh
diff --git a/tests/tsk/Makefile.am b/tests/tsk/Makefile.am
index 07c74f9..44a893e 100644
--- a/tests/tsk/Makefile.am
+++ b/tests/tsk/Makefile.am
@@ -21,6 +21,7 @@ TESTS = \
test-download-inode.sh \
test-download-blocks.sh \
test-filesystem-walk.sh \
+ test-find-block.sh \
test-find-inode.sh
TESTS_ENVIRONMENT = $(top_builddir)/run --test
diff --git a/tests/tsk/test-find-block.sh b/tests/tsk/test-find-block.sh
new file mode 100755
index 0000000..984947d
--- /dev/null
+++ b/tests/tsk/test-find-block.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-block command.
+
+if [ -n "$SKIP_TEST_FIND_BLOCK_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-block /dev/sda2 0
+EOF
+)
+
+# test $Boot is in the list
+echo $output | grep -zq '{ tsk_inode: 7
+tsk_type: r
+tsk_size: .*
+tsk_name: \$Boot
+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: \$Boot not found in files list."
+ echo "File list:"
+ echo $output
+ exit 1
+fi
--
2.9.3
Pino Toscano
2016-Sep-23 08:52 UTC
Re: [Libguestfs] [PATCH v3 1/3] New API: internal_find_block
On Tuesday, 20 September 2016 16:19:30 CEST Matteo Cafasso wrote:> + for (index = 0; index < count; index++) { > + fsattr = tsk_fs_file_attr_get_idx (fsfile, index); > + > + if (fsattr != NULL && fsattr->flags & TSK_FS_ATTR_NONRES) > + tsk_fs_attr_walk (fsattr, flags, attrwalk_callback, (void *) &blkdata);The return code of tsk_fs_attr_walk must be checked.> +/* Attribute walk, searches the given block within the FS node attributes. > + * > + * Return TSK_WALK_CONT on success, TSK_WALK_ERROR on error. > + */ > +static TSK_WALK_RET_ENUM > +attrwalk_callback (TSK_FS_FILE *fsfile, TSK_OFF_T offset, > + TSK_DADDR_T blkaddr, char *buf, size_t size, > + TSK_FS_BLOCK_FLAG_ENUM flags, void *data) > +{ > + findblk_data *blkdata = (findblk_data *) data; > + > + if (!(flags & TSK_FS_BLOCK_FLAG_SPARSE) && blkaddr == blkdata->block) { > + blkdata->found = true;If we want to ignore sparse blocks, wouldn't it make sense to pass TSK_FS_FILE_WALK_FLAG_NOSPARSE as additional flag to tsk_fs_attr_walk above? Also, my concerns about this that I replied in v2 still stand: is the documentation obsolete, or does it document what is the expected behaviour? In the former case, then it could be ok to partially disregard what it said; in the latter, the code should follow what it describes. In any case, you should get in touch with the sleuthkit developers, and get their feedback about that. -- Pino Toscano
On Tuesday, 20 September 2016 16:19:29 CEST Matteo Cafasso wrote:> v3: > > - fixed attribute walk callback: checking against TSK_FS_BLOCK_FLAG_RAW flag would > exclude compressed data blocks which are still important. > Yet we want to exclude sparse blocks (TSK_FS_BLOCK_FLAG_SPARSE) as they are not stored > on the disk. > > Matteo Cafasso (3): > New API: internal_find_block > New API: find_block > find_block: added API testsPatches #2, and #3 LGTM. Thanks, -- Pino Toscano