mirror of
https://github.com/adulau/dcfldd.git
synced 2024-12-22 16:56:03 +00:00
385 lines
13 KiB
C
385 lines
13 KiB
C
/* $Id: copy.c,v 1.6 2005/05/19 20:59:12 harbourn Exp $
|
||
* dcfldd - The Enhanced Forensic DD
|
||
* By Nicholas Harbour
|
||
*/
|
||
|
||
/* Copyright (C) 85, 90, 91, 1995-2001, 2005 Free Software Foundation, 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, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
||
|
||
/* GNU dd originally written by Paul Rubin, David MacKenzie, and Stuart Kemp. */
|
||
#include "dcfldd.h"
|
||
#include <sys/types.h>
|
||
#include <stdlib.h>
|
||
#include <time.h>
|
||
#include "hash.h"
|
||
#include "getpagesize.h"
|
||
#include "safe-read.h"
|
||
#include "full-write.h"
|
||
#include "translate.h"
|
||
#include "sizeprobe.h"
|
||
#include "pattern.h"
|
||
#include "util.h"
|
||
#include "log.h"
|
||
#include "output.h"
|
||
|
||
static void write_output(void);
|
||
static void copy_simple(unsigned char const *, int);
|
||
static void copy_with_block(unsigned char const *, size_t);
|
||
static void copy_with_unblock(unsigned char const *, size_t);
|
||
|
||
/* Output buffer. */
|
||
unsigned char *obuf;
|
||
|
||
/* Current index into `obuf'. */
|
||
static size_t oc;
|
||
|
||
/* Index into current line, for `conv=block' and `conv=unblock'. */
|
||
static size_t col;
|
||
|
||
/* Write, then empty, the output buffer `obuf'. */
|
||
static void write_output(void)
|
||
{
|
||
/*int nwritten = full_write(STDOUT_FILENO, obuf, output_blocksize); */
|
||
int nwritten = outputlist_write(obuf, output_blocksize);
|
||
|
||
if (nwritten != output_blocksize) {
|
||
if (nwritten > 0)
|
||
w_partial++;
|
||
quit(1);
|
||
}
|
||
else
|
||
w_full++;
|
||
oc = 0;
|
||
}
|
||
|
||
/* Copy NREAD bytes of BUF, with no conversions. */
|
||
|
||
static void copy_simple(unsigned char const *buf, int nread)
|
||
{
|
||
int nfree; /* Number of unused bytes in `obuf'. */
|
||
const unsigned char *start = buf; /* First uncopied char in BUF. */
|
||
|
||
do {
|
||
nfree = output_blocksize - oc;
|
||
if (nfree > nread)
|
||
nfree = nread;
|
||
|
||
memcpy((char *) (obuf + oc), (char *) start, nfree);
|
||
|
||
nread -= nfree; /* Update the number of bytes left to copy. */
|
||
start += nfree;
|
||
oc += nfree;
|
||
if (oc >= output_blocksize)
|
||
write_output();
|
||
} while (nread > 0);
|
||
}
|
||
|
||
/* Copy NREAD bytes of BUF, doing conv=block
|
||
(pad newline-terminated records to `conversion_blocksize',
|
||
replacing the newline with trailing spaces). */
|
||
|
||
static void copy_with_block(unsigned char const *buf, size_t nread)
|
||
{
|
||
size_t i;
|
||
|
||
for (i = nread; i; i--, buf++) {
|
||
if (*buf == newline_character) {
|
||
if (col < conversion_blocksize) {
|
||
size_t j;
|
||
for (j = col; j < conversion_blocksize; j++)
|
||
output_char(space_character);
|
||
}
|
||
col = 0;
|
||
} else {
|
||
if (col == conversion_blocksize)
|
||
r_truncate++;
|
||
else if (col < conversion_blocksize)
|
||
output_char(*buf);
|
||
col++;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Copy NREAD bytes of BUF, doing conv=unblock
|
||
(replace trailing spaces in `conversion_blocksize'-sized records
|
||
with a newline). */
|
||
static void copy_with_unblock(unsigned char const *buf, size_t nread)
|
||
{
|
||
size_t i;
|
||
unsigned char c;
|
||
static int pending_spaces = 0;
|
||
|
||
for (i = 0; i < nread; i++) {
|
||
c = buf[i];
|
||
|
||
if (col++ >= conversion_blocksize) {
|
||
col = pending_spaces = 0; /* Wipe out any pending spaces. */
|
||
i--; /* Push the char back; get it later. */
|
||
output_char(newline_character);
|
||
} else if (c == space_character)
|
||
pending_spaces++;
|
||
else {
|
||
/* `c' is the character after a run of spaces that were not
|
||
at the end of the conversion buffer. Output them. */
|
||
while (pending_spaces) {
|
||
output_char(space_character);
|
||
--pending_spaces;
|
||
}
|
||
output_char(c);
|
||
}
|
||
}
|
||
}
|
||
|
||
/* The main loop. */
|
||
|
||
int dd_copy(void)
|
||
{
|
||
unsigned char *ibuf, *bufstart; /* Input buffer. */
|
||
unsigned char *real_buf; /* real buffer address before alignment */
|
||
unsigned char *real_obuf;
|
||
ssize_t nread; /* Bytes read in the current block. */
|
||
int exit_status = 0;
|
||
int input_from_stream = !!input_file;
|
||
int input_from_pattern = !input_from_stream;
|
||
size_t page_size = getpagesize();
|
||
size_t n_bytes_read;
|
||
|
||
/* Leave at least one extra byte at the beginning and end of `ibuf'
|
||
for conv=swab, but keep the buffer address even. But some peculiar
|
||
device drivers work only with word-aligned buffers, so leave an
|
||
extra two bytes. */
|
||
|
||
/* Some devices require alignment on a sector or page boundary
|
||
(e.g. character disk devices). Align the input buffer to a
|
||
page boundary to cover all bases. Note that due to the swab
|
||
algorithm, we must have at least one byte in the page before
|
||
the input buffer; thus we allocate 2 pages of slop in the
|
||
real buffer. 8k above the blocksize shouldn't bother anyone.
|
||
|
||
The page alignment is necessary on any linux system that supports
|
||
either the SGI raw I/O patch or Steven Tweedies raw I/O patch.
|
||
It is necessary when accessing raw (i.e. character special) disk
|
||
devices on Unixware or other SVR4-derived system. */
|
||
|
||
real_buf = (unsigned char *) malloc(input_blocksize
|
||
+ 2 * SWAB_ALIGN_OFFSET
|
||
+ 2 * page_size - 1);
|
||
ibuf = real_buf;
|
||
ibuf += SWAB_ALIGN_OFFSET; /* allow space for swab */
|
||
|
||
ibuf = PTR_ALIGN(ibuf, page_size);
|
||
|
||
/* Init */
|
||
if (do_hash)
|
||
hash_update(ihashlist, NULL, 0);
|
||
|
||
if (conversions_mask & C_TWOBUFS) {
|
||
/* Page-align the output buffer, too. */
|
||
real_obuf = (unsigned char *) malloc(output_blocksize + page_size - 1);
|
||
obuf = PTR_ALIGN(real_obuf, page_size);
|
||
} else {
|
||
real_obuf = NULL;
|
||
obuf = ibuf;
|
||
}
|
||
|
||
if (!input_from_pattern)
|
||
if (skip_records != 0)
|
||
skip(STDIN_FILENO, input_file, skip_records, input_blocksize, ibuf);
|
||
|
||
if (seek_records != 0) {
|
||
outputlist_t *listptr;
|
||
|
||
for (listptr = outputlist; listptr != NULL; listptr = listptr->next) {
|
||
skip(listptr->data.fd, "", seek_records, output_blocksize, obuf);
|
||
}
|
||
}
|
||
|
||
if (max_records == 0)
|
||
quit(exit_status);
|
||
|
||
if (input_from_pattern) {
|
||
replicate_pattern(pattern, ibuf, input_blocksize);
|
||
nread = n_bytes_read = input_blocksize;
|
||
}
|
||
|
||
while (1) {
|
||
/* Display an update message */
|
||
if (do_status && w_full % update_thresh == 0 && w_full != 0) {
|
||
off_t total_bytes = w_full * input_blocksize;
|
||
off_t total_mb = total_bytes / 1048576;
|
||
|
||
if (probe == PROBE_NONE || probed_size == 0)
|
||
fprintf(stderr, "\r%llu blocks (%lluMb) written.",
|
||
/* [FIX] copy.c:226:25: warning: format ‘%llu’ expects argument of type ‘long long unsigned int’, but argument {3,4} has type ‘uintmax_t’ [-Wformat=] */
|
||
(long long unsigned int) w_full, (long long unsigned int) total_mb);
|
||
else {
|
||
time_t curr_time = time(NULL);
|
||
int seconds = (int)difftime(curr_time, start_time);
|
||
off_t probed_mb = probed_size / 1048576;
|
||
float fprcnt = total_bytes / (float)probed_size;
|
||
float fprcnt_remaining = 1.0 - fprcnt;
|
||
int prcnt = (int)(fprcnt * 100);
|
||
int seconds_remaining = (int)(seconds *
|
||
(fprcnt_remaining / fprcnt));
|
||
char secstr[100];
|
||
|
||
time_left(secstr, sizeof secstr, seconds_remaining);
|
||
fprintf(stderr, "\r[%d%% of %lluMb] %llu blocks (%lluMb) written. %s",
|
||
/* [FIX] copy.c:240:25: warning: format ‘%llu’ expects argument of type ‘long long unsigned int’, but argument {4,5,6} has type ‘off_t’ [-Wformat=] */
|
||
prcnt, (long long unsigned int) probed_mb, (long long unsigned int) w_full, (long long unsigned int) total_mb, secstr);
|
||
}
|
||
}
|
||
|
||
if (r_partial + r_full >= max_records)
|
||
break;
|
||
|
||
/* Zero the buffer before reading, so that if we get a read error,
|
||
whatever data we are able to read is followed by zeros.
|
||
This minimizes data loss. */
|
||
if (!input_from_pattern) {
|
||
if ((conversions_mask & C_SYNC) && (conversions_mask & C_NOERROR))
|
||
memset((char *) ibuf,
|
||
(conversions_mask & (C_BLOCK | C_UNBLOCK)) ? ' ' : '\0',
|
||
input_blocksize);
|
||
|
||
nread = safe_read(STDIN_FILENO, ibuf, input_blocksize);
|
||
}
|
||
|
||
if (nread == 0)
|
||
break; /* EOF. */
|
||
|
||
if (nread < 0 && !input_from_pattern) {
|
||
syscall_error_noexit(input_file);
|
||
if (conversions_mask & C_NOERROR)
|
||
{
|
||
print_stats();
|
||
/* Seek past the bad block if possible. */
|
||
lseek(STDIN_FILENO, (off_t) input_blocksize, SEEK_CUR);
|
||
if (conversions_mask & C_SYNC) {
|
||
/* Replace the missing input with null bytes and
|
||
proceed normally. */
|
||
// EXPERIMENTAL: let's try re-zeroing this buffer
|
||
memset((char *) ibuf,
|
||
(conversions_mask & (C_BLOCK | C_UNBLOCK)) ? ' ' : '\0',
|
||
input_blocksize);
|
||
nread = 0;
|
||
} else
|
||
continue;
|
||
} else {
|
||
/* Write any partial block. */
|
||
exit_status = 2;
|
||
break;
|
||
}
|
||
}
|
||
n_bytes_read = nread;
|
||
|
||
if (do_hash && hashconv == HASHCONV_BEFORE)
|
||
hash_update(ihashlist, ibuf, n_bytes_read);
|
||
|
||
if (n_bytes_read < input_blocksize) {
|
||
r_partial++;
|
||
if (conversions_mask & C_SYNC) {
|
||
if (!(conversions_mask & C_NOERROR))
|
||
/* If C_NOERROR, we zeroed the block before reading. */
|
||
memset((char *) (ibuf + n_bytes_read),
|
||
(conversions_mask & (C_BLOCK | C_UNBLOCK)) ? ' ' : '\0',
|
||
input_blocksize - n_bytes_read);
|
||
n_bytes_read = input_blocksize;
|
||
}
|
||
}
|
||
else
|
||
r_full++;
|
||
|
||
if (ibuf == obuf) { /* If not C_TWOBUFS. */
|
||
/* int nwritten = full_write(STDOUT_FILENO, obuf, n_bytes_read); */
|
||
int nwritten = outputlist_write(obuf, n_bytes_read);
|
||
|
||
if (nwritten < 0)
|
||
syscall_error(output_file);
|
||
else if (n_bytes_read == input_blocksize)
|
||
w_full++;
|
||
else
|
||
w_partial++;
|
||
} else { /* If C_TWOBUFS */
|
||
/* Do any translations on the whole buffer at once. */
|
||
|
||
if (translation_needed)
|
||
translate_buffer(ibuf, n_bytes_read);
|
||
|
||
if (conversions_mask & C_SWAB)
|
||
bufstart = swab_buffer(ibuf, &n_bytes_read);
|
||
else
|
||
bufstart = ibuf;
|
||
|
||
if (conversions_mask & C_BLOCK)
|
||
copy_with_block(bufstart, n_bytes_read);
|
||
else if (conversions_mask & C_UNBLOCK)
|
||
copy_with_unblock(bufstart, n_bytes_read);
|
||
else
|
||
copy_simple(bufstart, n_bytes_read);
|
||
}
|
||
|
||
if (do_hash && hashconv == HASHCONV_AFTER)
|
||
hash_update(ihashlist, ibuf, n_bytes_read);
|
||
}
|
||
|
||
|
||
/* If we have a char left as a result of conv=swab, output it. */
|
||
if (char_is_saved) {
|
||
if (conversions_mask & C_BLOCK)
|
||
copy_with_block(&saved_char, 1);
|
||
else if (conversions_mask & C_UNBLOCK)
|
||
copy_with_unblock(&saved_char, 1);
|
||
else
|
||
output_char(saved_char);
|
||
}
|
||
|
||
if ((conversions_mask & C_BLOCK) && col > 0) {
|
||
/* If the final input line didn't end with a '\n', pad
|
||
the output block to `conversion_blocksize' chars. */
|
||
unsigned int i;
|
||
for (i = col; i < conversion_blocksize; i++)
|
||
output_char(space_character);
|
||
}
|
||
|
||
if ((conversions_mask & C_UNBLOCK) && col == conversion_blocksize)
|
||
/* Add a final '\n' if there are exactly `conversion_blocksize'
|
||
characters in the final record. */
|
||
output_char(newline_character);
|
||
|
||
/* Write out the last block. */
|
||
if (oc != 0) {
|
||
/* int nwritten = full_write(STDOUT_FILENO, obuf, oc); */
|
||
int nwritten = outputlist_write(obuf, oc);
|
||
|
||
if (nwritten > 0)
|
||
w_partial++;
|
||
if (nwritten < 0) {
|
||
syscall_error(output_file);
|
||
}
|
||
}
|
||
|
||
free(real_buf);
|
||
if (real_obuf)
|
||
free(real_obuf);
|
||
|
||
if (do_hash) {
|
||
hash_remainder(ihashlist, WINDOW_CTX);
|
||
display_totalhash(ihashlist, TOTAL_CTX);
|
||
}
|
||
|
||
return exit_status;
|
||
}
|