/* $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 #include #include #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.", w_full, 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", prcnt, probed_mb, w_full, 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; }