Files
libguestfs/daemon/tsk.c
Richard W.M. Jones d5a8f82887 Use 'const' for stack integers where possible.
May improve optimization possibilities in a few cases.
2016-07-26 10:43:45 +01:00

285 lines
7.6 KiB
C

/* libguestfs - the guestfsd daemon
* 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.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <string.h>
#include <unistd.h>
#include <rpc/xdr.h>
#include <rpc/types.h>
#include "guestfs_protocol.h"
#include "daemon.h"
#include "actions.h"
#include "optgroups.h"
#ifdef HAVE_LIBTSK
#include <tsk/libtsk.h>
enum tsk_dirent_flags {
DIRENT_UNALLOC = 0x00,
DIRENT_ALLOC = 0x01,
DIRENT_REALLOC = 0x02,
DIRENT_COMPRESSED = 0x04
};
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 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 *);
int
do_internal_filesystem_walk (const mountable_t *mountable)
{
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, fswalk_callback, NULL);
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.
*/
static int
open_filesystem (const char *device, TSK_IMG_INFO **img, TSK_FS_INFO **fs)
{
const char *images[] = { device };
*img = tsk_img_open (1, images, TSK_IMG_TYPE_DETECT, 0);
if (*img == NULL) {
reply_with_tsk_error ("tsk_image_open");
return -1;
}
*fs = tsk_fs_open_img (*img, 0, TSK_FS_TYPE_DETECT);
if (*fs == NULL) {
reply_with_tsk_error ("tsk_fs_open_img");
(*img)->close (*img);
return -1;
}
return 0;
}
/* 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.
* 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;
/* Ignore ./ and ../ */
ret = TSK_FS_ISDOT (fsfile->name->name);
if (ret != 0)
return TSK_WALK_CONT;
/* 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;
}
/* 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;
dirent.tsk_flags = file_flags (fsfile);
file_metadata (fsfile->meta, &dirent);
ret = send_dirent_info (&dirent);
ret = (ret == 0) ? TSK_WALK_CONT : TSK_WALK_ERROR;
return ret;
}
/* Inspect fsfile to identify its type. */
static char
file_type (TSK_FS_FILE *fsfile)
{
if (fsfile->name->type < TSK_FS_NAME_TYPE_STR_MAX)
switch (fsfile->name->type) {
case TSK_FS_NAME_TYPE_UNDEF: return 'u';
case TSK_FS_NAME_TYPE_FIFO: return 'f';
case TSK_FS_NAME_TYPE_CHR: return 'c';
case TSK_FS_NAME_TYPE_DIR: return 'd';
case TSK_FS_NAME_TYPE_BLK: return 'b';
case TSK_FS_NAME_TYPE_REG: return 'r';
case TSK_FS_NAME_TYPE_LNK: return 'l';
case TSK_FS_NAME_TYPE_SOCK: return 's';
case TSK_FS_NAME_TYPE_SHAD: return 'h';
case TSK_FS_NAME_TYPE_WHT: return 'w';
case TSK_FS_NAME_TYPE_VIRT: return 'u'; /* Temp files created by TSK */
}
else if (fsfile->meta != NULL &&
fsfile->meta->type < TSK_FS_META_TYPE_STR_MAX)
switch (fsfile->name->type) {
case TSK_FS_NAME_TYPE_UNDEF: return 'u';
case TSK_FS_META_TYPE_REG: return 'r';
case TSK_FS_META_TYPE_DIR: return 'd';
case TSK_FS_META_TYPE_FIFO: return 'f';
case TSK_FS_META_TYPE_CHR: return 'c';
case TSK_FS_META_TYPE_BLK: return 'b';
case TSK_FS_META_TYPE_LNK: return 'l';
case TSK_FS_META_TYPE_SHAD: return 'h';
case TSK_FS_META_TYPE_SOCK: return 's';
case TSK_FS_META_TYPE_WHT: return 'w';
case TSK_FS_META_TYPE_VIRT: return 'u'; /* Temp files created by TSK */
}
return 'u';
}
/* Inspect fsfile to retrieve file flags. */
static int
file_flags (TSK_FS_FILE *fsfile)
{
int flags = DIRENT_UNALLOC;
if (fsfile->name->flags & TSK_FS_NAME_FLAG_UNALLOC) {
if (fsfile->meta && fsfile->meta->flags & TSK_FS_META_FLAG_ALLOC)
flags |= DIRENT_REALLOC;
}
else
flags |= DIRENT_ALLOC;
if (fsfile->meta && fsfile->meta->flags & TSK_FS_META_FLAG_COMP)
flags |= DIRENT_COMPRESSED;
return flags;
}
/* Inspect fsfile to retrieve file metadata. */
static void
file_metadata (TSK_FS_META *fsmeta, guestfs_int_tsk_dirent *dirent)
{
if (fsmeta != NULL) {
dirent->tsk_size = fsmeta->size;
dirent->tsk_nlink = fsmeta->nlink;
dirent->tsk_atime_sec = fsmeta->atime;
dirent->tsk_atime_nsec = fsmeta->atime_nano;
dirent->tsk_mtime_sec = fsmeta->mtime;
dirent->tsk_mtime_nsec = fsmeta->mtime_nano;
dirent->tsk_ctime_sec = fsmeta->ctime;
dirent->tsk_ctime_nsec = fsmeta->ctime_nano;
dirent->tsk_crtime_sec = fsmeta->crtime;
dirent->tsk_crtime_nsec = fsmeta->crtime_nano;
/* tsk_link never changes */
dirent->tsk_link = (fsmeta->link != NULL) ? fsmeta->link : (char *) "";
}
else {
dirent->tsk_size = -1;
/* tsk_link never changes */
dirent->tsk_link = (char *) "";
}
}
/* 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)
{
int ret = 0;
const char *buf = NULL;
ret = tsk_error_get_errno ();
if (ret != 0) {
buf = tsk_error_get ();
reply_with_error ("%s: %s", funcname, buf);
}
else
reply_with_error ("%s: unknown error", funcname);
}
int
optgroup_libtsk_available (void)
{
return 1;
}
#else /* !HAVE_LIBTSK */
OPTGROUP_LIBTSK_NOT_AVAILABLE
#endif /* !HAVE_LIBTSK */