2013-02-23 09:05:53 +00:00
/* $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 . 8 k 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. " ,
2017-01-12 07:59:01 +00:00
/* [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 ) ;
2013-02-23 09:05:53 +00:00
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 " ,
2017-01-12 07:59:01 +00:00
/* [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 ) ;
2013-02-23 09:05:53 +00:00
}
}
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 ;
}