diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 10 | ||||
-rw-r--r-- | src/context.h | 187 | ||||
-rw-r--r-- | src/device.c | 829 | ||||
-rw-r--r-- | src/device.h | 37 | ||||
-rw-r--r-- | src/gui.c | 3307 | ||||
-rw-r--r-- | src/gui.h | 62 | ||||
-rw-r--r-- | src/isaac_rand/isaac64.c | 119 | ||||
-rw-r--r-- | src/isaac_rand/isaac64.h | 41 | ||||
-rw-r--r-- | src/isaac_rand/isaac_rand.c | 169 | ||||
-rw-r--r-- | src/isaac_rand/isaac_rand.h | 52 | ||||
-rw-r--r-- | src/isaac_rand/isaac_standard.h | 60 | ||||
-rw-r--r-- | src/logging.c | 1007 | ||||
-rw-r--r-- | src/logging.h | 52 | ||||
-rw-r--r-- | src/method.c | 1375 | ||||
-rw-r--r-- | src/method.h | 60 | ||||
-rw-r--r-- | src/mt19937ar-cok/mt19937ar-cok.c | 139 | ||||
-rw-r--r-- | src/mt19937ar-cok/mt19937ar-cok.h | 32 | ||||
-rw-r--r-- | src/nwipe.c | 982 | ||||
-rw-r--r-- | src/nwipe.h | 114 | ||||
-rw-r--r-- | src/options.c | 596 | ||||
-rw-r--r-- | src/options.h | 72 | ||||
-rw-r--r-- | src/pass.c | 866 | ||||
-rw-r--r-- | src/pass.h | 33 | ||||
-rw-r--r-- | src/prng.c | 252 | ||||
-rw-r--r-- | src/prng.h | 65 | ||||
-rw-r--r-- | src/temperature.c | 270 | ||||
-rw-r--r-- | src/temperature.h | 30 | ||||
-rw-r--r-- | src/version.c | 17 | ||||
-rw-r--r-- | src/version.h | 11 |
29 files changed, 10846 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..f4fea2f --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,10 @@ +# what flags you want to pass to the C compiler & linker +#CFLAGS = -lncurses -lparted +AM_CFLAGS = +AM_LDFLAGS = + +# this lists the binaries to produce, the (non-PHONY, binary) targets in +# the previous manual Makefile +bin_PROGRAMS = nwipe +nwipe_SOURCES = context.h logging.h options.h prng.h version.h temperature.h nwipe.c gui.c method.h pass.c device.c gui.h isaac_rand/isaac_standard.h isaac_rand/isaac_rand.h isaac_rand/isaac_rand.c isaac_rand/isaac64.h isaac_rand/isaac64.c mt19937ar-cok/mt19937ar-cok.c nwipe.h mt19937ar-cok/mt19937ar-cok.h pass.h device.h logging.c method.c options.c prng.c version.c temperature.c +nwipe_LDADD = $(PARTED_LIBS) diff --git a/src/context.h b/src/context.h new file mode 100644 index 0000000..26e3432 --- /dev/null +++ b/src/context.h @@ -0,0 +1,187 @@ +/* + * context.h: The internal state representation of nwipe. + * + * Copyright Darik Horn <dajhorn-dban@vanadac.com>. + * + * Modifications to original dwipe Copyright Andy Beverley <andy@andybev.com> + * + * 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, version 2. + * + * 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. + * + */ + +#ifndef CONTEXT_H_ +#define CONTEXT_H_ + +#include "prng.h" + +typedef enum nwipe_device_t_ { + NWIPE_DEVICE_UNKNOWN = 0, // Unknown device. + NWIPE_DEVICE_IDE, + NWIPE_DEVICE_SCSI, + NWIPE_DEVICE_COMPAQ, // Unimplemented. + NWIPE_DEVICE_USB, + NWIPE_DEVICE_IEEE1394, // Unimplemented. + NWIPE_DEVICE_ATA, + NWIPE_DEVICE_NVME, + NWIPE_DEVICE_VIRT, + NWIPE_DEVICE_SAS +} nwipe_device_t; + +typedef enum nwipe_pass_t_ { + NWIPE_PASS_NONE = 0, // Not running. + NWIPE_PASS_WRITE, // Writing patterns to the device. + NWIPE_PASS_VERIFY, // Verifying a pass. + NWIPE_PASS_FINAL_BLANK, // Filling the device with zeros. + NWIPE_PASS_FINAL_OPS2 // Special case for nwipe_ops2. +} nwipe_pass_t; + +typedef enum nwipe_select_t_ { + NWIPE_SELECT_NONE = 0, // Unused. + NWIPE_SELECT_TRUE, // Wipe this device. + NWIPE_SELECT_TRUE_PARENT, // A parent of this device has been selected, so the wipe is implied. + NWIPE_SELECT_FALSE, // Do not wipe this device. + NWIPE_SELECT_FALSE_CHILD, // A child of this device has been selected, so we can't wipe this device. + NWIPE_SELECT_DISABLED // Do not wipe this device and do not allow it to be selected. +} nwipe_select_t; + +#define NWIPE_KNOB_SPEEDRING_SIZE 30 +#define NWIPE_KNOB_SPEEDRING_GRANULARITY 10 + +typedef struct nwipe_speedring_t_ +{ + u64 bytes[NWIPE_KNOB_SPEEDRING_SIZE]; + u64 bytestotal; + u64 byteslast; + time_t times[NWIPE_KNOB_SPEEDRING_SIZE]; + time_t timestotal; + time_t timeslast; + u32 position; +} nwipe_speedring_t; + +#define NWIPE_DEVICE_LABEL_LENGTH 200 +#define NWIPE_DEVICE_SIZE_TXT_LENGTH 7 + +// Arbitary length, so far most paths don't exceed about 25 characters +#define MAX_HWMON_PATH_LENGTH 100 + +typedef struct nwipe_context_t_ +{ + /* + * Device fields + */ + int device_block_size; // The soft block size reported by the device. + int device_sector_size; // The hard sector size reported by the device. + int device_bus; // The device bus number. + int device_fd; // The file descriptor of the device file being wiped. + int device_host; // The host number. + struct hd_driveid device_id; // The WIN_IDENTIFY data for IDE drives. + int device_lun; // The device logical unit number. + int device_major; // The major device number. + int device_minor; // The minor device number. + int device_part; // The device partition or slice number. + char* device_name; // The device file name. + char device_name_without_path[100]; + char gui_device_name[100]; + unsigned long long device_size; // The device size in bytes. + char* device_size_text; // The device size in a more (human)readable format. + char device_size_txt[NWIPE_DEVICE_SIZE_TXT_LENGTH]; // The device size in a more (human)readable format. + char* device_model; // The model of the device. + char device_label[NWIPE_DEVICE_LABEL_LENGTH]; // The label (name, model, size and serial) of the device. + struct stat device_stat; // The device file state from fstat(). + nwipe_device_t device_type; // Indicates an IDE, SCSI, or Compaq SMART device in enumerated form (int) + char device_type_str[14]; // Indicates an IDE, SCSI, USB etc as per nwipe_device_t but in ascii + char device_serial_no[21]; // Serial number(processed, 20 characters plus null termination) of the device. + int device_target; // The device target. + + u64 eta; // The estimated number of seconds until method completion. + int entropy_fd; // The entropy source. Usually /dev/urandom. + int pass_count; // The number of passes performed by the working wipe method. + u64 pass_done; // The number of bytes that have already been i/o'd in this pass. + u64 pass_errors; // The number of errors across all passes. + u64 pass_size; // The total number of i/o bytes across all passes. + nwipe_pass_t pass_type; // The type of the current working pass. + int pass_working; // The current working pass. + nwipe_prng_t* prng; // The PRNG implementation. + nwipe_entropy_t prng_seed; // The random data that is used to seed the PRNG. + void* prng_state; // The private internal state of the PRNG. + int result; // The process return value. + int round_count; // The number of rounds performed by the working wipe method. + u64 round_done; // The number of bytes that have already been i/o'd. + u64 round_errors; // The number of errors across all rounds. + u64 round_size; // The total number of i/o bytes across all rounds. + double round_percent; // The percentage complete across all rounds. + int round_working; // The current working round. + nwipe_select_t select; // Indicates whether this device should be wiped. + int signal; // Set when the child is killed by a signal. + nwipe_speedring_t speedring; // Ring buffer for computing the rolling throughput average. + short sync_status; // A flag to indicate when the method is syncing. + pthread_t thread; // The ID of the thread. + u64 throughput; // Average throughput in bytes per second. + u64 verify_errors; // The number of verification errors across all passes. + char temp1_path[MAX_HWMON_PATH_LENGTH]; // path to temperature variables /sys/class/hwmon/hwmonX/ etc. + int temp1_crit; // Critical high drive temperature, 1000000=unitialised, millidegree celsius. + int temp1_highest; // Historical highest temperature reached, 1000000=unitialised, millidegree celsius. + int temp1_input; // drive temperature, -1=unitialised. 1000000=unitialised, millidegree celsius. + int temp1_lcrit; // Critical low drive temperature, 1000000=unitialised, millidegree celsius. + int temp1_lowest; // Historically lowest temperature, 1000000=unitialised, millidegree celsius. + int temp1_max; // Maximum allowed temperature, 1000000=unitialised, millidegree celsius. + int temp1_min; // Miniumum allowed temperature, 1000000=unitialised, millidegree celsius. + int temp1_monitored_wipe_max; + int temp1_monitored_wipe_min; + int temp1_monitored_wipe_avg; + int temp1_flash_rate; // number relates to one tenth of a second, so 2 means a flash on and off = 0.4s + int temp1_flash_rate_counter; // used by the gui for timing the flash rate + int temp1_flash_rate_status; // 0=blank 1=visible + time_t temp1_time; // The time when temperature was last checked, seconds since epoch + int wipe_status; // Wipe finished = 0, wipe in progress = 1, wipe yet to start = -1. + int spinner_idx; // Index into the spinner character array + char spinner_character[1]; // The current spinner character + double duration; // Duration of the wipe in seconds + time_t start_time; // Start time of wipe + time_t end_time; // End time of wipe + u64 fsyncdata_errors; // The number of fsyncdata errors across all passes. + /* + * Identity contains the raw serial number of the drive + * (where applicable), however, for use within nwipe use the + * processed serial_no[21] string above. To access serial no. use + * c[i]->serial_no) and not c[i]->identity.serial_no); + */ + struct hd_driveid identity; +} nwipe_context_t; + +/* + * We use 2 data structs to pass data between threads. + * The first contains any required values. + * Values cannot form part of the second array below, hence the need for this. + */ +typedef struct +{ + int nwipe_enumerated; // The number of devices available. + int nwipe_selected; // The number of devices being wiped. + time_t maxeta; // The estimated runtime of the slowest device. + u64 throughput; // Total throughput. + u64 errors; // The combined number of errors of all processes. + pthread_t* gui_thread; // The ID of GUI thread. +} nwipe_misc_thread_data_t; + +/* + * The second points to the first structure, as well as the structure of all the devices + */ +typedef struct +{ + nwipe_context_t** c; // Pointer to the nwipe context structure. + nwipe_misc_thread_data_t* nwipe_misc_thread_data; // Pointer to the misc structure above. +} nwipe_thread_data_ptr_t; + +#endif /* CONTEXT_H_ */ diff --git a/src/device.c b/src/device.c new file mode 100644 index 0000000..b7a87f5 --- /dev/null +++ b/src/device.c @@ -0,0 +1,829 @@ +/* + * device.c: Device routines for nwipe. + * + * Copyright Darik Horn <dajhorn-dban@vanadac.com>. + * + * Modifications to original dwipe Copyright Andy Beverley <andy@andybev.com> + * + * 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, version 2. + * + * 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. + * + */ + +#ifndef _DEFAULT_SOURCE +#define _DEFAULT_SOURCE +#endif + +#include <stdio.h> +#include <stdint.h> + +#include "nwipe.h" +#include "context.h" +#include "device.h" +#include "method.h" +#include "options.h" +#include "logging.h" +#include <sys/ioctl.h> +#include <linux/hdreg.h> // Drive specific defs +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <ctype.h> + +#include <parted/parted.h> +#include <parted/debug.h> + +int check_device( nwipe_context_t*** c, PedDevice* dev, int dcount ); +char* trim( char* str ); + +extern int terminate_signal; + +int nwipe_device_scan( nwipe_context_t*** c ) +{ + /** + * Scans the filesystem for storage device names. + * + * @parameter device_names A reference to a null array pointer. + * @modifies device_names Populates device_names with an array of nwipe_contect_t + * @returns The number of strings in the device_names array. + * + */ + + PedDevice* dev = NULL; + ped_device_probe_all(); + + int dcount = 0; + + while( ( dev = ped_device_get_next( dev ) ) ) + { + if( check_device( c, dev, dcount ) ) + dcount++; + + /* Don't bother scanning drives if the terminate signal is active ! as in the case of + * the readlink program missing which is required if the --nousb option has been specified */ + if( terminate_signal == 1 ) + { + break; + } + } + + /* Return the number of devices that were found. */ + return dcount; + +} /* nwipe_device_scan */ + +int nwipe_device_get( nwipe_context_t*** c, char** devnamelist, int ndevnames ) +{ + /** + * Gets information about devices + * + * @parameter device_names A reference to a null array pointer. + * @parameter devnamelist An array of string pointers to the device names + * @parameter ndevnames Number of elements in devnamelist + * @modifies device_names Populates device_names with an array of nwipe_contect_t + * @returns The number of strings in the device_names array. + * + */ + + PedDevice* dev = NULL; + + int i; + int dcount = 0; + + for( i = 0; i < ndevnames; i++ ) + { + + dev = ped_device_get( devnamelist[i] ); + if( !dev ) + { + nwipe_log( NWIPE_LOG_WARNING, "Device %s not found", devnamelist[i] ); + continue; + } + + if( check_device( c, dev, dcount ) ) + dcount++; + + /* Don't bother scanning drives if the terminate signal is active ! as in the case of + * the readlink program missing which is required if the --nousb option has been specified */ + if( terminate_signal == 1 ) + { + break; + } + } + + /* Return the number of devices that were found. */ + return dcount; + +} /* nwipe_device_get */ + +int check_device( nwipe_context_t*** c, PedDevice* dev, int dcount ) +{ + /* Populate this struct, then assign it to overall array of structs. */ + nwipe_context_t* next_device; + int fd; + int idx; + int r; + char tmp_serial[21]; + nwipe_device_t bus; + + bus = 0; + + /* Check whether this drive is on the excluded drive list ? */ + idx = 0; + while( idx < 10 ) + { + if( !strcmp( dev->path, nwipe_options.exclude[idx++] ) ) + { + nwipe_log( NWIPE_LOG_NOTICE, "Device %s excluded as per command line option -e", dev->path ); + return 0; + } + } + + /* Check whether the user has specified using the --nousb option + * that all USB devices should not be displayed or wiped whether + * in GUI, --nogui or --autonuke modes */ + + if( nwipe_options.nousb ) + { + /* retrieve bus and drive serial number, HOWEVER we are only interested in the bus at this time */ + r = nwipe_get_device_bus_type_and_serialno( dev->path, &bus, tmp_serial ); + + /* See nwipe_get_device_bus_type_and_serialno() function for meaning of these codes */ + if( r == 0 || ( r >= 3 && r <= 6 ) ) + { + if( bus == NWIPE_DEVICE_USB ) + { + nwipe_log( NWIPE_LOG_NOTICE, "Device %s ignored as per command line option --nousb", dev->path ); + return 0; + } + } + else + { + if( r == 2 ) + { + nwipe_log( + NWIPE_LOG_NOTICE, "--nousb requires the 'readlink' program, please install readlink", dev->path ); + terminate_signal = 1; + return 0; + } + } + } + + /* Try opening the device to see if it's valid. Close on completion. */ + if( !ped_device_open( dev ) ) + { + nwipe_log( NWIPE_LOG_FATAL, "Unable to open device" ); + return 0; + } + ped_device_close( dev ); + + /* New device, reallocate memory for additional struct pointer */ + *c = realloc( *c, ( dcount + 1 ) * sizeof( nwipe_context_t* ) ); + + next_device = malloc( sizeof( nwipe_context_t ) ); + + /* Check the allocation. */ + if( !next_device ) + { + nwipe_perror( errno, __FUNCTION__, "malloc" ); + nwipe_log( NWIPE_LOG_FATAL, "Unable to create the array of enumeration contexts." ); + return 0; + } + + /* Zero the allocation. */ + memset( next_device, 0, sizeof( nwipe_context_t ) ); + + /* Get device information */ + next_device->device_model = dev->model; + remove_ATA_prefix( next_device->device_model ); + + /* full device name, i.e. /dev/sda */ + next_device->device_name = dev->path; + + /* remove /dev/ from device, right justify and prefix name so string length is eight characters */ + nwipe_strip_path( next_device->device_name_without_path, next_device->device_name ); + + /* To maintain column alignment in the gui we have to remove /dev/ from device names that + * exceed eight characters including the /dev/ path. + */ + if( strlen( next_device->device_name ) > MAX_LENGTH_OF_DEVICE_STRING ) + { + strcpy( next_device->gui_device_name, next_device->device_name_without_path ); + } + else + { + strcpy( next_device->gui_device_name, next_device->device_name ); + } + + next_device->device_size = dev->length * dev->sector_size; + Determine_C_B_nomenclature( next_device->device_size, next_device->device_size_txt, NWIPE_DEVICE_SIZE_TXT_LENGTH ); + next_device->device_size_text = next_device->device_size_txt; + next_device->result = -2; + + /* Attempt to get serial number of device. + */ + next_device->device_serial_no[0] = 0; /* initialise the serial number */ + + if( ( fd = open( next_device->device_name = dev->path, O_RDONLY ) ) == ERR ) + { + nwipe_log( NWIPE_LOG_WARNING, "Unable to open device %s to obtain serial number", next_device->device_name ); + } + + /* + * We don't check the ioctl return status because there are plenty of situations where a serial number may not be + * returned by ioctl such as USB drives, logical volumes, encryted volumes, so the log file would have multiple + * benign ioctl errors reported which isn't necessarily a problem. + */ + ioctl( fd, HDIO_GET_IDENTITY, &next_device->identity ); + close( fd ); + + for( idx = 0; idx < 20; idx++ ) + { + next_device->device_serial_no[idx] = next_device->identity.serial_no[idx]; + } + + // Terminate the string. + next_device->device_serial_no[20] = 0; + + // Remove leading/trailing whitespace from serial number and left justify. + trim( (char*) next_device->device_serial_no ); + + /* if we couldn't obtain serial number by using the above method .. try this */ + r = nwipe_get_device_bus_type_and_serialno( next_device->device_name, &next_device->device_type, tmp_serial ); + + /* If serial number & bus retrieved (0) OR unsupported USB bus identified (5) */ + if( r == 0 || r == 5 ) + { + /* If the serial number hasn't already been populated */ + if( next_device->device_serial_no[0] == 0 ) + { + strcpy( next_device->device_serial_no, tmp_serial ); + } + } + + /* Does the user want to anonymize serial numbers ? */ + if( nwipe_options.quiet ) + { + if( next_device->device_serial_no[0] == 0 ) + { + strcpy( next_device->device_serial_no, "???????????????" ); + } + else + { + strcpy( next_device->device_serial_no, "XXXXXXXXXXXXXXX" ); + } + } + + /* All device strings should be 4 characters, prefix with space if under 4 characters */ + switch( next_device->device_type ) + { + case NWIPE_DEVICE_UNKNOWN: + strcpy( next_device->device_type_str, " UNK" ); + break; + + case NWIPE_DEVICE_IDE: + strcpy( next_device->device_type_str, " IDE" ); + break; + + case NWIPE_DEVICE_SCSI: + strcpy( next_device->device_type_str, " SCSI" ); + break; + + case NWIPE_DEVICE_COMPAQ: + strcpy( next_device->device_type_str, " CPQ" ); + break; + + case NWIPE_DEVICE_USB: + strcpy( next_device->device_type_str, " USB" ); + break; + + case NWIPE_DEVICE_IEEE1394: + strcpy( next_device->device_type_str, "1394" ); + break; + + case NWIPE_DEVICE_ATA: + strcpy( next_device->device_type_str, " ATA" ); + break; + + case NWIPE_DEVICE_NVME: + strcpy( next_device->device_type_str, "NVME" ); + break; + + case NWIPE_DEVICE_VIRT: + strcpy( next_device->device_type_str, "VIRT" ); + break; + + case NWIPE_DEVICE_SAS: + strcpy( next_device->device_type_str, " SAS" ); + break; + } + + if( strlen( (const char*) next_device->device_serial_no ) ) + { + snprintf( next_device->device_label, + NWIPE_DEVICE_LABEL_LENGTH, + "%s %s [%s] %s/%s", + next_device->device_name, + next_device->device_type_str, + next_device->device_size_text, + next_device->device_model, + next_device->device_serial_no ); + } + else + { + snprintf( next_device->device_label, + NWIPE_DEVICE_LABEL_LENGTH, + "%s %s [%s] %s", + next_device->device_name, + next_device->device_type_str, + next_device->device_size_text, + next_device->device_model ); + } + + nwipe_log( NWIPE_LOG_NOTICE, + "Found %s, %s, %s, %s, S/N=%s", + next_device->device_name, + next_device->device_type_str, + next_device->device_model, + next_device->device_size_text, + next_device->device_serial_no ); + + ( *c )[dcount] = next_device; + return 1; +} + +/* Remove leading/trailing whitespace from a string and left justify result */ +char* trim( char* str ) +{ + size_t len = 0; + char* frontp = str; + char* endp = NULL; + + if( str == NULL ) + { + return NULL; + } + if( str[0] == '\0' ) + { + return str; + } + len = strlen( str ); + endp = str + len; + + /* + * Move the front and back pointers to address the first non-whitespace + * characters from each end. + */ + while( isspace( (unsigned char) *frontp ) ) + { + ++frontp; + } + if( endp != frontp ) + { + while( isspace( (unsigned char) *( --endp ) ) && endp != frontp ) + { + } + } + if( str + len - 1 != endp ) + *( endp + 1 ) = '\0'; + else if( frontp != str && endp == frontp ) + *str = '\0'; + /* + * Shift the string so that it starts at str so that if it's dynamically + * allocated, we can still free it on the returned pointer. Note the reuse + * of endp to mean the front of the string buffer now. + */ + endp = str; + if( frontp != str ) + { + while( *frontp ) + { + *endp++ = *frontp++; + } + *endp = '\0'; + } + return str; +} + +int nwipe_get_device_bus_type_and_serialno( char* device, nwipe_device_t* bus, char* serialnumber ) +{ + /* The caller provides a string that contains the device, i.e. /dev/sdc, also a pointer + * to a integer (bus type) and thirdly a 21 byte + * character string which this function populates with the serial number (20 characters + null terminator). + * + * The function populates the bus integer and serial number strings for the given device. + * Results for bus would typically be ATA or USB see nwipe_device_t in context.h + * + * Return Values: + * 0 = Success + * 1 = popen failed to create stream for readlink + * 2 = readlink exit code not 0, see nwipe logs + * 3 = popen failed to create stream for smartctl + * 4 = smartctl command not found, install smartmontools + * 5 = smartctl detected unsupported USB to IDE/SATA adapter + * 6 = All other errors ! + * + */ + + FILE* fp; + + int r; // A result buffer. + int idx_src; + int idx_dest; + int device_len; + int set_return_value; + int exit_status; + int idx; + int idx2; + + char readlink_command[] = "readlink /sys/block/%s"; + char readlink_command2[] = "/usr/bin/readlink /sys/block/%s"; + char readlink_command3[] = "/sbin/readlink /sys/block/%s"; + char smartctl_command[] = "smartctl -i %s"; + char smartctl_command2[] = "/sbin/smartctl -i %s"; + char smartctl_command3[] = "/usr/bin/smartctl -i %s"; + char device_shortform[50]; + char result[512]; + char final_cmd_readlink[sizeof( readlink_command ) + sizeof( device_shortform )]; + char final_cmd_smartctl[sizeof( smartctl_command ) + 256]; + char* pResult; + char smartctl_labels_to_anonymize[][18] = { + "serial number:", "lu wwn device id:", "logical unit id:", "" /* Don't remove this empty string !, important */ + }; + + /* Initialise return value */ + set_return_value = 0; + + *bus = 0; + + /* Scan device name and if device is for instance /dev/sdx then convert to sdx + * If already sdx then just copy. */ + + idx_dest = 0; + device_shortform[idx_dest] = 0; + device_len = strlen( device ); + idx_src = device_len; + + while( idx_src >= 0 ) + { + if( device[idx_src] == '/' || idx_src == 0 ) + { + idx_src++; + + /* Now scan forwards copying the short form device i.e sdc */ + while( idx_src < device_len ) + { + device_shortform[idx_dest++] = device[idx_src++]; + } + break; + } + else + { + idx_src--; + } + } + device_shortform[idx_dest] = 0; + + final_cmd_readlink[0] = 0; + + /* Determine whether we can access readlink, required if the PATH environment is not setup ! (Debian sid 'su' as + * opposed to 'su -' */ + if( system( "which readlink > /dev/null 2>&1" ) ) + { + if( system( "which /sbin/readlink > /dev/null 2>&1" ) ) + { + if( system( "which /usr/bin/readlink > /dev/null 2>&1" ) ) + { + nwipe_log( NWIPE_LOG_WARNING, "Command not found. Install readlink !" ); + set_return_value = 2; + + /* Return immediately if --nousb specified. Readlink is a requirement for this option. */ + if( nwipe_options.nousb ) + { + return set_return_value; + } + } + else + { + sprintf( final_cmd_readlink, readlink_command3, device_shortform ); + } + } + else + { + sprintf( final_cmd_readlink, readlink_command2, device_shortform ); + } + } + else + { + sprintf( final_cmd_readlink, readlink_command, device_shortform ); + } + + if( final_cmd_readlink[0] != 0 ) + { + + fp = popen( final_cmd_readlink, "r" ); + + if( fp == NULL ) + { + nwipe_log( NWIPE_LOG_WARNING, + "nwipe_get_device_bus_type_and_serialno: Failed to create stream to %s", + readlink_command ); + + set_return_value = 1; + } + + if( fp != NULL ) + { + /* Read the output a line at a time - output it. */ + if( fgets( result, sizeof( result ) - 1, fp ) != NULL ) + { + if( nwipe_options.verbose ) + { + strip_CR_LF( result ); + nwipe_log( NWIPE_LOG_DEBUG, "Readlink: %s", result ); + } + + /* Scan the readlink results for bus types, i.e. USB or ATA + * Example: readlink + * /sys/block/sdd../devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.0/host6/target6:0:0/6:0:0:0/block/sdd + */ + + if( strstr( result, "/usb" ) != 0 ) + { + *bus = NWIPE_DEVICE_USB; + } + else + { + if( strstr( result, "/ata" ) != 0 ) + { + *bus = NWIPE_DEVICE_ATA; + } + else + { + if( strstr( result, "/nvme/" ) != 0 ) + { + *bus = NWIPE_DEVICE_NVME; + } + else + { + if( strstr( result, "/virtual/" ) != 0 ) + { + *bus = NWIPE_DEVICE_VIRT; + } + } + } + } + } + /* close */ + r = pclose( fp ); + + if( r > 0 ) + { + exit_status = WEXITSTATUS( r ); + if( nwipe_options.verbose ) + { + nwipe_log( NWIPE_LOG_WARNING, + "nwipe_get_device_bus_type_and_serialno(): readlink failed, \"%s\" exit status = %u", + final_cmd_readlink, + exit_status ); + } + + if( exit_status == 127 ) + { + nwipe_log( NWIPE_LOG_WARNING, "Command not found. Install Readlink recommended !" ); + set_return_value = 2; + if( nwipe_options.nousb ) + { + return set_return_value; + } + } + } + } + } + + /* + * Retrieve smartmontools drive information if USB bridge supports it, so we can retrieve the serial number of the + * drive that's on the other side of the USB bridge.. */ + + final_cmd_smartctl[0] = 0; + + /* Determine whether we can access smartctl, required if the PATH environment is not setup ! (Debian sid 'su' as + * opposed to 'su -' */ + if( system( "which smartctl > /dev/null 2>&1" ) ) + { + if( system( "which /sbin/smartctl > /dev/null 2>&1" ) ) + { + if( system( "which /usr/bin/smartctl > /dev/null 2>&1" ) ) + { + nwipe_log( NWIPE_LOG_WARNING, "Command not found. Install smartmontools !" ); + } + else + { + sprintf( final_cmd_smartctl, smartctl_command3, device ); + } + } + else + { + sprintf( final_cmd_smartctl, smartctl_command2, device ); + } + } + else + { + sprintf( final_cmd_smartctl, smartctl_command, device ); + } + + if( final_cmd_smartctl[0] != 0 ) + { + fp = popen( final_cmd_smartctl, "r" ); + + if( fp == NULL ) + { + nwipe_log( NWIPE_LOG_WARNING, + "nwipe_get_device_bus_type_and_serialno(): Failed to create stream to %s", + smartctl_command ); + + set_return_value = 3; + } + else + { + /* Read the output a line at a time - output it. */ + while( fgets( result, sizeof( result ) - 1, fp ) != NULL ) + { + /* Convert the label, i.e everything before the ':' to lower case, it's required to + * convert to lower case as smartctl seems to use inconsistent case when labeling + * for serial number, i.e mostly it produces labels "Serial Number:" but occasionaly + * it produces a label "Serial number:" */ + + idx = 0; + while( result[idx] != 0 && result[idx] != ':' ) + { + /* If upper case alpha character, change to lower case */ + if( result[idx] >= 'A' && result[idx] <= 'Z' ) + { + result[idx] += 32; + } + + idx++; + } + + if( nwipe_options.verbose && result[0] != 0x0A ) + { + strip_CR_LF( result ); + + /* Remove serial number if -q option specified */ + if( nwipe_options.quiet ) + { + /* initialise index into string array */ + idx2 = 0; + + while( smartctl_labels_to_anonymize[idx2][0] != 0 ) + { + if( ( pResult = strstr( result, &smartctl_labels_to_anonymize[idx2][0] ) ) != 0 ) + { + /* set index to character after end of label string */ + idx = strlen( &smartctl_labels_to_anonymize[idx2][0] ); + + /* Ignore spaces, overwrite other characters */ + while( *( pResult + idx ) != 0x0A && *( pResult + idx ) != 0x0D + && *( pResult + idx ) != 0 && idx <= sizeof( result ) - 1 ) + { + if( *( pResult + idx ) == ' ' ) + { + idx++; + continue; + } + else + { + /* ignore if the serial number has been written over with '?' */ + if( *( pResult + idx ) != '?' ) + { + *( pResult + idx ) = 'X'; + } + idx++; + } + } + } + idx2++; + } + } + + nwipe_log( NWIPE_LOG_DEBUG, "smartctl: %s", result ); + } + + if( strstr( result, "serial number:" ) != 0 ) + { + /* strip any leading or trailing spaces and left justify, +15 is the length of "Serial Number:" */ + trim( &result[15] ); + + strncpy( serialnumber, &result[15], 20 ); + } + + if( *bus == 0 ) + { + if( strstr( result, "transport protocol:" ) != 0 ) + { + /* strip any leading or trailing spaces and left justify, +4 is the length of "bus type:" */ + trim( &result[19] ); + + if( strncmp( &result[19], "sas", 3 ) == 0 ) + { + *bus = NWIPE_DEVICE_SAS; + } + } + + if( strstr( result, "sata version is:" ) != 0 ) + { + + /* strip any leading or trailing spaces and left justify, +4 is the length of "bus type:" */ + trim( &result[16] ); + + if( strncmp( &result[16], "sata", 4 ) == 0 ) + { + *bus = NWIPE_DEVICE_ATA; + } + } + } + } + + /* close */ + r = pclose( fp ); + + if( r > 0 ) + { + exit_status = WEXITSTATUS( r ); + if( nwipe_options.verbose && exit_status != 1 ) + { + nwipe_log( NWIPE_LOG_WARNING, + "nwipe_get_device_bus_type_and_serialno(): smartctl failed, \"%s\" exit status = %u", + final_cmd_smartctl, + exit_status ); + } + set_return_value = 6; + + if( exit_status == 127 ) + { + nwipe_log( NWIPE_LOG_WARNING, "Command not found. Install Smartctl recommended !" ); + + set_return_value = 4; + } + + if( exit_status == 1 ) + { + nwipe_log( NWIPE_LOG_WARNING, "%s USB bridge, no pass-through support", device ); + + if( *bus == NWIPE_DEVICE_USB ) + { + strcpy( serialnumber, "(S/N: unknown)" ); + set_return_value = 5; + } + } + } + } + } + + return set_return_value; +} + +void strip_CR_LF( char* str ) +{ + /* In the specified string, replace any CR or LF with a space */ + int idx = 0; + int len = strlen( str ); + while( idx < len ) + { + if( str[idx] == 0x0A || str[idx] == 0x0D ) + { + str[idx] = ' '; + } + idx++; + } +} + +void remove_ATA_prefix( char* str ) +{ + /* Remove "ATA" prefix if present in the model no. string, left justifing string */ + + int idx_pre = 4; + int idx_post = 0; + + if( !strncmp( str, "ATA ", 4 ) ) + { + while( str[idx_pre] != 0 ) + { + str[idx_post++] = str[idx_pre++]; + } + + str[idx_post] = 0; + } +} diff --git a/src/device.h b/src/device.h new file mode 100644 index 0000000..2a5c076 --- /dev/null +++ b/src/device.h @@ -0,0 +1,37 @@ +/* + * device.h: Device routines for nwipe. + * + * Copyright Darik Horn <dajhorn-dban@vanadac.com>. + * + * Modifications to original dwipe Copyright Andy Beverley <andy@andybev.com> + * + * 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, version 2. + * + * 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. + * + */ + +#ifndef DEVICE_H_ +#define DEVICE_H_ + +#define MAX_LENGTH_OF_DEVICE_STRING 8 + +void nwipe_device_identify( nwipe_context_t* c ); // Get hardware information about the device. +int nwipe_device_scan( nwipe_context_t*** c ); // Find devices that we can wipe. +int nwipe_device_get( nwipe_context_t*** c, char** devnamelist, int ndevnames ); // Get info about devices to wipe. +int nwipe_get_device_bus_type_and_serialno( char*, nwipe_device_t*, char* ); +void strip_CR_LF( char* ); +void determine_disk_capacity_nomenclature( u64, char* ); +void remove_ATA_prefix( char* ); +char* trim( char* ); + +#endif /* DEVICE_H_ */ diff --git a/src/gui.c b/src/gui.c new file mode 100644 index 0000000..d0b05a0 --- /dev/null +++ b/src/gui.c @@ -0,0 +1,3307 @@ +/* + * gui.c: An ncurses GUI for nwipe. + * + * Copyright Darik Horn <dajhorn-dban@vanadac.com>. + * + * Modifications to original dwipe Copyright Andy Beverley <andy@andybev.com> + * + * 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, version 2. + * + * 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. + * + */ + +/* RATIONALE: + * + * This entire GUI is a non-portable task-specific thunk. + * + * The alternatives are, however, no better. The CDK is large and clumsy, + * and things like ncurses libmenu are not worth the storage overhead. + * + */ +#include <ncurses.h> +#include <panel.h> +#include <stdint.h> +#include <time.h> + +#include "nwipe.h" +#include "context.h" +#include "method.h" +#include "prng.h" +#include "options.h" +#include "gui.h" +#include "pass.h" +#include "logging.h" +#include "version.h" +#include "temperature.h" + +#define NWIPE_GUI_PANE 8 + +/* Header window: width, height, x coordinate, y coordinate. */ +#define NWIPE_GUI_HEADER_W COLS +#define NWIPE_GUI_HEADER_H 1 +#define NWIPE_GUI_HEADER_X 0 +#define NWIPE_GUI_HEADER_Y 0 + +/* Footer window: width, height, x coordinate, y coordinate. */ +#define NWIPE_GUI_FOOTER_W COLS +#define NWIPE_GUI_FOOTER_H 1 +#define NWIPE_GUI_FOOTER_X 0 +#define NWIPE_GUI_FOOTER_Y ( LINES - 1 ) + +/* Options window: width, height, x coorindate, y coordinate. */ +#define NWIPE_GUI_OPTIONS_W 44 +#define NWIPE_GUI_OPTIONS_H 7 +#define NWIPE_GUI_OPTIONS_Y 1 +#define NWIPE_GUI_OPTIONS_X 0 + +/* Options fields, relative to their window. */ +#define NWIPE_GUI_OPTIONS_TAB 10 +#define NWIPE_GUI_OPTIONS_ENTROPY_Y 1 +#define NWIPE_GUI_OPTIONS_ENTROPY_X 1 +#define NWIPE_GUI_OPTIONS_PRNG_Y 2 +#define NWIPE_GUI_OPTIONS_PRNG_X 1 +#define NWIPE_GUI_OPTIONS_METHOD_Y 3 +#define NWIPE_GUI_OPTIONS_METHOD_X 1 +#define NWIPE_GUI_OPTIONS_VERIFY_Y 4 +#define NWIPE_GUI_OPTIONS_VERIFY_X 1 +#define NWIPE_GUI_OPTIONS_ROUNDS_Y 5 +#define NWIPE_GUI_OPTIONS_ROUNDS_X 1 + +/* Stats window: width, height, x coordinate, y coordinate. */ +#define NWIPE_GUI_STATS_W ( COLS - 44 ) +#define NWIPE_GUI_STATS_H 7 +#define NWIPE_GUI_STATS_Y 1 +#define NWIPE_GUI_STATS_X 44 + +/* Stats fields, relative to their window. */ +#define NWIPE_GUI_STATS_RUNTIME_Y 1 +#define NWIPE_GUI_STATS_RUNTIME_X 1 +#define NWIPE_GUI_STATS_ETA_Y 2 +#define NWIPE_GUI_STATS_ETA_X 1 +#define NWIPE_GUI_STATS_LOAD_Y 3 +#define NWIPE_GUI_STATS_LOAD_X 1 +#define NWIPE_GUI_STATS_THROUGHPUT_Y 4 +#define NWIPE_GUI_STATS_THROUGHPUT_X 1 +#define NWIPE_GUI_STATS_ERRORS_Y 5 +#define NWIPE_GUI_STATS_ERRORS_X 1 +#define NWIPE_GUI_STATS_TAB 16 + +/* Select window: width, height, x coordinate, y coordinate. */ +#define NWIPE_GUI_MAIN_W COLS +#define NWIPE_GUI_MAIN_H ( LINES - NWIPE_GUI_MAIN_Y - 1 ) +#define NWIPE_GUI_MAIN_Y 8 +#define NWIPE_GUI_MAIN_X 0 + +#define SKIP_DEV_PREFIX 5 + +/* Window pointers. */ +WINDOW* footer_window; +WINDOW* header_window; +WINDOW* main_window; +WINDOW* options_window; +WINDOW* stats_window; + +PANEL* footer_panel; +PANEL* header_panel; +PANEL* main_panel; +PANEL* options_panel; +PANEL* stats_panel; + +/* Options window title. */ +const char* options_title = " Options "; + +/* Statistics window title. */ +const char* stats_title = " Statistics "; + +/* Footer labels. */ +const char* main_window_footer = "S=Start m=Method p=PRNG v=Verify r=Rounds b=Blanking Space=Select CTRL+C=Quit"; +const char* main_window_footer_warning_lower_case_s = " WARNING: To start the wipe press SHIFT+S (uppercase S) "; + +const char* main_window_footer_warning_no_blanking_with_ops2 = + " WARNING: Zero blanking is not allowed with ops2 method "; + +const char* main_window_footer_warning_no_blanking_with_verify_only = + " WARNING: Zero blanking is not allowed with verify method "; + +const char* main_window_footer_warning_no_drive_selected = + " No drives selected, use spacebar to select a drive, then press S to start "; + +/* Oddly enough, placing extra quotes around the footer strings fixes corruption to the right + * of the footer message when the terminal is resized, a quirk in ncurses? - DO NOT REMOVE THE \" */ +const char* selection_footer = "J=Down K=Up Space=Select Backspace=Cancel Ctrl+C=Quit"; +const char* end_wipe_footer = "B=[Toggle between dark\\blank\\blue screen] Ctrl+C=Quit"; +const char* rounds_footer = "Left=Erase Esc=Cancel Ctrl+C=Quit"; + +const char* wipes_finished_footer = "Wipe finished - press enter to exit. Logged to STDOUT"; + +/* The number of lines available in the terminal */ +int stdscr_lines; + +/* The number of columns available in the terminal */ +int stdscr_cols; + +/* The size of the terminal lines when previously checked */ +int stdscr_lines_previous; + +/* The size of the terminal columns when previously checked */ +int stdscr_cols_previous; + +int tft_saver = 0; + +void nwipe_gui_title( WINDOW* w, const char* s ) +{ + /** + * Prints the string 's' centered on the first line of the window 'w'. + */ + + /* The number of lines in the window. (Not used.) */ + int wy; + (void) wy; /* flag wy not used to the compiler, to silence warning */ + + /* The number of columns in the window. */ + int wx; + + /* Get the window dimensions. */ + getmaxyx( w, wy, wx ); + + /*Calculate available total margin */ + int margin = ( wx - strlen( s ) ); + if( margin < 0 ) + { + margin = 0; + } + + /* tft_saver = grey text on black mode */ + if( tft_saver ) + { + wattron( w, A_BOLD ); + } + + /* Print the title. */ + mvwprintw( w, 0, margin / 2, "%s", s ); + +} /* nwipe_gui_title */ + +void nwipe_init_pairs( void ) +{ + if( has_colors() ) + { + /* Initialize color capabilities. */ + start_color(); + + if( can_change_color() ) + { + /* Redefine cyan to gray. */ + init_color( COLOR_CYAN, 128, 128, 128 ); + } + + /* If we are in tft saver mode set grey text on black background else + * Set white on blue as the emphasis color */ + if( tft_saver ) + { + init_pair( 1, COLOR_BLACK, COLOR_BLACK ); + } + else + { + init_pair( 1, COLOR_WHITE, COLOR_BLUE ); + } + + /* Set gray (or cyan) on blue as the normal color. */ + init_pair( 2, COLOR_CYAN, COLOR_BLUE ); + + /* Set red on blue as the hilite color. */ + init_pair( 3, COLOR_RED, COLOR_BLUE ); + + /* If we are in tft saver mode set grey text on black background else + * Set white on blue as the emphasis color */ + if( tft_saver ) + { + init_pair( 4, COLOR_BLACK, COLOR_BLACK ); + } + else + { + init_pair( 4, COLOR_BLUE, COLOR_WHITE ); + } + + /* Set white on green for success messages. */ + init_pair( 5, COLOR_WHITE, COLOR_GREEN ); + + /* Set white on red for failure messages. */ + init_pair( 6, COLOR_WHITE, COLOR_RED ); + + /* Set black on black for when hiding the display. */ + init_pair( 7, COLOR_BLACK, COLOR_BLACK ); + + /* Set green on blue for reverse bold messages */ + init_pair( 8, COLOR_GREEN, COLOR_WHITE ); + + /* Set green on blue for reverse bold error messages */ + init_pair( 9, COLOR_RED, COLOR_WHITE ); + + /* Set black on yellow for warning messages */ + init_pair( 10, COLOR_BLACK, COLOR_YELLOW ); + + /* Set black on blue for minimum temperature reached */ + init_pair( 11, COLOR_BLACK, COLOR_BLUE ); + + /* Set blue on blue to make temperature invisible */ + init_pair( 12, COLOR_BLUE, COLOR_BLUE ); + + /* Set the background style. */ + wbkgdset( stdscr, COLOR_PAIR( 1 ) | ' ' ); + } +} + +void nwipe_gui_init( void ) +{ + /** + * Initializes the ncurses gui. + */ + + /* Initialize the screen. */ + initscr(); + + /* Disable TTY line buffering. */ + cbreak(); + + /* Disable TTY echo. */ + noecho(); + + /* Enable most special keys. */ + keypad( stdscr, TRUE ); + + /* Create the text/background color pairs */ + nwipe_init_pairs(); + + /* Clear the screen. */ + wclear( stdscr ); + + /* Create the header window. */ + nwipe_gui_create_header_window(); + + /* Create the footer window and panel */ + nwipe_gui_create_footer_window( main_window_footer ); + + /* Create the options window and panel */ + nwipe_gui_create_options_window(); + + /* Create the stats window. */ + nwipe_gui_create_stats_window(); + + /* Create a new main window and panel */ + nwipe_gui_create_main_window(); + + update_panels(); + doupdate(); + + /* Hide the cursor. */ + curs_set( 0 ); + +} /* nwipe_gui_init */ + +void nwipe_gui_free( void ) +{ + /** + * Releases the ncurses gui. + * + */ + /* Free ncurses resources. */ + if( del_panel( footer_panel ) != OK ) + { + nwipe_log( NWIPE_LOG_ERROR, "Deleting footer panel failed!." ); + } + if( del_panel( header_panel ) != OK ) + { + nwipe_log( NWIPE_LOG_ERROR, "Deleting header panel failed!." ); + } + if( del_panel( main_panel ) != OK ) + { + nwipe_log( NWIPE_LOG_ERROR, "Deleting main panel failed!." ); + } + if( del_panel( options_panel ) != OK ) + { + nwipe_log( NWIPE_LOG_ERROR, "Deleting options panel failed!." ); + } + if( del_panel( stats_panel ) != OK ) + { + nwipe_log( NWIPE_LOG_ERROR, "Deleting stats panel failed!." ); + } + if( delwin( footer_window ) != OK ) + { + nwipe_log( NWIPE_LOG_ERROR, "Deleting footer window failed!." ); + } + if( delwin( header_window ) != OK ) + { + nwipe_log( NWIPE_LOG_ERROR, "Deleting header window failed!." ); + } + if( delwin( main_window ) != OK ) + { + nwipe_log( NWIPE_LOG_ERROR, "Deleting main window failed!." ); + } + if( delwin( options_window ) != OK ) + { + nwipe_log( NWIPE_LOG_ERROR, "Deleting options window failed!." ); + } + if( delwin( stats_window ) != OK ) + { + nwipe_log( NWIPE_LOG_ERROR, "Deleting stats window failed!." ); + } + if( endwin() != OK ) + { + nwipe_log( NWIPE_LOG_ERROR, "Curses endwin() failed !" ); + } + +} /* nwipe_gui_free */ + +void nwipe_gui_create_main_window() +{ + /* Create the main window. */ + main_window = newwin( NWIPE_GUI_MAIN_H, NWIPE_GUI_MAIN_W, NWIPE_GUI_MAIN_Y, NWIPE_GUI_MAIN_X ); + main_panel = new_panel( main_window ); + + if( has_colors() ) + { + /* Set the background style. */ + wbkgdset( main_window, COLOR_PAIR( 1 ) | ' ' ); + + /* Apply the color change. */ + wattron( main_window, COLOR_PAIR( 1 ) ); + + /* In tft saver mode we toggle the intensity bit which gives us grey text */ + if( tft_saver ) + { + wattron( main_window, A_BOLD ); + } + else + { + wattroff( main_window, A_BOLD ); + } + } + + /* Clear the main window. */ + werase( main_window ); + + /* Add a border. */ + box( main_window, 0, 0 ); + + /* refresh main window */ + wnoutrefresh( main_window ); + +} /* nwipe_gui_create_main_window */ + +void nwipe_gui_create_header_window() +{ + char anon_label[] = " (ANONYMIZED)"; + char bannerplus[80]; + + /* Create the header window. */ + header_window = newwin( NWIPE_GUI_HEADER_H, NWIPE_GUI_HEADER_W, NWIPE_GUI_HEADER_Y, NWIPE_GUI_HEADER_X ); + header_panel = new_panel( header_window ); + + if( has_colors() ) + { + /* Set the background style of the header window. */ + wbkgdset( header_window, COLOR_PAIR( 4 ) | ' ' ); + + if( tft_saver ) + { + wattron( main_window, A_BOLD ); + } + } + + /* Clear the header window. */ + werase( header_window ); + + /* If in anonymized mode modify the title banner to reflect this */ + strcpy( bannerplus, banner ); + + if( nwipe_options.quiet ) + { + strcat( bannerplus, anon_label ); + } + + /* Print the product banner. */ + nwipe_gui_title( header_window, bannerplus ); + + /* Refresh the header window */ + wnoutrefresh( header_window ); + +} /* nwipe_gui_create_header_window */ + +void nwipe_gui_create_footer_window( const char* footer_text ) +{ + /* Create the footer window. */ + footer_window = newwin( NWIPE_GUI_FOOTER_H, NWIPE_GUI_FOOTER_W, NWIPE_GUI_FOOTER_Y, NWIPE_GUI_FOOTER_X ); + footer_panel = new_panel( footer_window ); + + if( has_colors() ) + { + /* Set the background style of the footer window. */ + wbkgdset( footer_window, COLOR_PAIR( 4 ) | ' ' ); + } + + /* Erase the footer window. */ + werase( footer_window ); + + /* Add help text to the footer */ + nwipe_gui_title( footer_window, footer_text ); + + /* Refresh the footer window */ + wnoutrefresh( footer_window ); + +} /* nwipe_gui_create_footer_window */ + +void nwipe_gui_amend_footer_window( const char* footer_text ) +{ + /* Clear the footer window. */ + werase( footer_window ); + + /* Add help text to the footer */ + nwipe_gui_title( footer_window, footer_text ); + + /* Refresh the footer window */ + wnoutrefresh( footer_window ); + +} /* nwipe_gui_amend_footer_window */ + +void nwipe_gui_create_options_window() +{ + /* Create the options window. */ + options_window = newwin( NWIPE_GUI_OPTIONS_H, NWIPE_GUI_OPTIONS_W, NWIPE_GUI_OPTIONS_Y, NWIPE_GUI_OPTIONS_X ); + options_panel = new_panel( options_window ); + + if( has_colors() ) + { + /* Set the background style of the options window. */ + wbkgdset( options_window, COLOR_PAIR( 1 ) | ' ' ); + + /* Apply the color change to the options window. */ + wattron( options_window, COLOR_PAIR( 1 ) ); + + if( tft_saver ) + { + wattron( options_window, A_BOLD ); + } + } + + /* Clear the options window. */ + werase( options_window ); + + /* Add a border. */ + box( options_window, 0, 0 ); + +} /* nwipe_gui_create_options_window */ + +void nwipe_gui_create_stats_window() +{ + /* Create the stats window. */ + stats_window = newwin( NWIPE_GUI_STATS_H, NWIPE_GUI_STATS_W, NWIPE_GUI_STATS_Y, NWIPE_GUI_STATS_X ); + stats_panel = new_panel( stats_window ); + + if( has_colors() ) + { + /* Set the background style of the stats window. */ + wbkgdset( stats_window, COLOR_PAIR( 1 ) | ' ' ); + + /* Apply the color change to the stats window. */ + wattron( stats_window, COLOR_PAIR( 1 ) ); + + if( tft_saver ) + { + wattron( stats_window, A_BOLD ); + } + } + + /* Clear the new window. */ + werase( stats_window ); + + /* Add a border. */ + box( stats_window, 0, 0 ); + + /* Add a title. */ + nwipe_gui_title( stats_window, stats_title ); + + /* Print field labels. */ + mvwprintw( stats_window, NWIPE_GUI_STATS_RUNTIME_Y, NWIPE_GUI_STATS_RUNTIME_X, "Runtime: " ); + mvwprintw( stats_window, NWIPE_GUI_STATS_ETA_Y, NWIPE_GUI_STATS_ETA_X, "Remaining: " ); + mvwprintw( stats_window, NWIPE_GUI_STATS_LOAD_Y, NWIPE_GUI_STATS_LOAD_X, "Load Averages: " ); + mvwprintw( stats_window, NWIPE_GUI_STATS_THROUGHPUT_Y, NWIPE_GUI_STATS_THROUGHPUT_X, "Throughput: " ); + mvwprintw( stats_window, NWIPE_GUI_STATS_ERRORS_Y, NWIPE_GUI_STATS_ERRORS_X, "Errors: " ); + +} /* nwipe_gui_create_stats_window */ + +void nwipe_gui_create_all_windows_on_terminal_resize( int force_creation, const char* footer_text ) +{ + /* Get the terminal size */ + getmaxyx( stdscr, stdscr_lines, stdscr_cols ); + + /* If the user has resized the terminal then recreate the windows and panels */ + if( stdscr_cols_previous != stdscr_cols || stdscr_lines_previous != stdscr_lines || force_creation == 1 ) + { + /* Save the revised terminal size so we check whether the user has resized next time */ + stdscr_lines_previous = stdscr_lines; + stdscr_cols_previous = stdscr_cols; + + /* Clear the screen. */ + wclear( stdscr ); + + /* Create a new header window and panel due to terminal size having changed */ + nwipe_gui_create_header_window(); + + /* Create a new main window and panel due to terminal size having changed */ + nwipe_gui_create_main_window(); + + /* Create a new footer window and panel due to terminal size having changed */ + nwipe_gui_create_footer_window( footer_text ); + + /* Create a new options window and panel due to terminal size having changed */ + nwipe_gui_create_options_window(); + + /* Create a new stats window and panel due to terminal size having changed */ + nwipe_gui_create_stats_window(); + + /* Update the options window. */ + nwipe_gui_options(); + + update_panels(); + doupdate(); + } +} + +void nwipe_gui_select( int count, nwipe_context_t** c ) +{ + /** + * The primary user interface. Allows the user to + * change options and specify the devices to be wiped. + * + * @parameter count The number of contexts in the array. + * @parameter c An array of device contexts. + * + * @modifies c[].select Sets the select flag according to user input. + * @modifies options Sets program options according to to user input. + * + */ + + extern int terminate_signal; + + /* Widget labels. */ + const char* select_title = " Disks and Partitions "; + + /* The number of lines available in the window. */ + int wlines; + + /* The number of columns available in the window. */ + int wcols; + + /* The number of selection elements that we can show in the window. */ + int slots; + + /* The index of the element that is visible in the first slot. */ + int offset = 0; + + /* The selection focus. */ + int focus = 0; + + /* A generic loop variable. */ + int i = 0; + + /* User input buffer. */ + int keystroke; + + /* The current working line. */ + int yy; + + /* Flag, Valid key hit = 1, anything else = 0 */ + int validkeyhit; + + /* Counts number of drives and partitions that have been selected */ + int number_of_selected_contexts = 0; + + /* Control A toggle status -1=indefined, 0=all drives delected, 1=all drives selected */ + int select_all_toggle_status = -1; + + /* Get the terminal size */ + getmaxyx( stdscr, stdscr_lines, stdscr_cols ); + + /* Save the terminal size so we check whether the user has resized */ + stdscr_lines_previous = stdscr_lines; + stdscr_cols_previous = stdscr_cols; + + time_t temperature_check_time = time( NULL ); + + /* Used in the selection loop to trap a failure of the timeout(), getch() mechanism to block for the designated + * period */ + int iteration_counter; + + /* Used in the selection loop to trap a failure of the timeout(), getch() mechanism to block for the designated + * period */ + int expected_iterations; + + time_t previous_iteration_timestamp; + + do + { + + nwipe_gui_create_all_windows_on_terminal_resize( 0, main_window_footer ); + + /* There is one slot per line. */ + getmaxyx( main_window, wlines, wcols ); + + /* Less two lines for the box and two lines for padding. */ + slots = wlines - 4; + if( slots < 0 ) + { + slots = 0; + } + + /* The code here adjusts the offset value, required when the terminal is resized vertically */ + if( slots > count ) + { + offset = 0; + } + else + { + if( focus >= count ) + { + /* The focus is already at the last element. */ + focus = count - 1; + } + if( focus < 0 ) + { + /* The focus is already at the last element. */ + focus = 0; + } + } + + if( count >= slots && slots > 0 ) + { + offset = focus + 1 - slots; + if( offset < 0 ) + { + offset = 0; + } + } + + /* Clear the main window, necessary when switching selections such as method etc */ + werase( main_window ); + + /* Refresh main window */ + wnoutrefresh( main_window ); + + /* If the user selected an option the footer text would have changed. + * Here we set it back to the main key help text */ + nwipe_gui_create_footer_window( main_window_footer ); + + /* Refresh the stats window */ + wnoutrefresh( stats_window ); + + /* Refresh the options window */ + wnoutrefresh( options_window ); + + /* Update the options window. */ + nwipe_gui_options(); + + /* Initialize the line offset. */ + yy = 2; + + for( i = 0; i < slots && i < count; i++ ) + { + + /* Move to the next line. */ + mvwprintw( main_window, yy++, 1, " " ); + + if( i + offset == focus ) + { + if( c[focus]->select == NWIPE_SELECT_TRUE || c[focus]->select == NWIPE_SELECT_FALSE ) + { + /* Print the 'enabled' cursor. */ + waddch( main_window, ACS_RARROW ); + } + + else + { + /* Print the 'disabled' cursor. */ + waddch( main_window, ACS_DIAMOND ); + } + } + + else + { + /* Print whitespace. */ + waddch( main_window, ' ' ); + } + + /* In the event for the offset value somehow becoming invalid, this if statement will prevent a segfault + * and the else part will log the out of bounds values for debugging */ + if( i + offset >= 0 && i + offset < count ) + { + + switch( c[i + offset]->select ) + { + case NWIPE_SELECT_TRUE: + + if( nwipe_options.method == &nwipe_verify_zero || nwipe_options.method == &nwipe_verify_one ) + { + wprintw( main_window, + "[vrfy] %s %s [%s] ", + c[i + offset]->gui_device_name, + c[i + offset]->device_type_str, + c[i + offset]->device_size_text ); + } + else + { + wprintw( main_window, + "[wipe] %s %s [%s] ", + c[i + offset]->gui_device_name, + c[i + offset]->device_type_str, + c[i + offset]->device_size_text ); + } + break; + + case NWIPE_SELECT_FALSE: + /* Print an element that is not selected. */ + wprintw( main_window, + "[ ] %s %s [%s] ", + c[i + offset]->gui_device_name, + c[i + offset]->device_type_str, + c[i + offset]->device_size_text ); + break; + + case NWIPE_SELECT_TRUE_PARENT: + + /* This element will be wiped when its parent is wiped. */ + wprintw( main_window, + "[****] %s %s [%s] ", + c[i + offset]->gui_device_name, + c[i + offset]->device_type_str, + c[i + offset]->device_size_text ); + break; + + case NWIPE_SELECT_FALSE_CHILD: + + /* We can't wipe this element because it has a child that is being wiped. */ + wprintw( main_window, + "[----] %s %s [%s] ", + c[i + offset]->gui_device_name, + c[i + offset]->device_type_str, + c[i + offset]->device_size_text ); + break; + + case NWIPE_SELECT_DISABLED: + + /* We don't know how to wipe this device. (Iomega Zip drives.) */ + wprintw( main_window, "[????] %s ", "Unrecognized Device" ); + break; + + default: + + /* TODO: Handle the sanity error. */ + break; + + } /* switch select */ + + /* Read the drive temperature values */ + nwipe_update_temperature( c[i + offset] ); + + /* print the temperature */ + wprintw_temperature( c[i + offset] ); + + /* print the drive model and serial number */ + wprintw( main_window, "%s/%s", c[i + offset]->device_model, c[i + offset]->device_serial_no ); + } + else + { + nwipe_log( NWIPE_LOG_DEBUG, + "GUI.c,nwipe_gui_select(), scroll, array index out of bounds, i=%u, count=%u, slots=%u, " + "focus=%u, offset=%u", + i, + count, + slots, + focus, + offset ); + } + + } /* for */ + + if( offset > 0 ) + { + mvwprintw( main_window, 1, wcols - 8, " More " ); + waddch( main_window, ACS_UARROW ); + } + + if( count - offset > slots ) + { + mvwprintw( main_window, wlines - 2, wcols - 8, " More " ); + waddch( main_window, ACS_DARROW ); + } + + /* Draw a border around the menu window. */ + box( main_window, 0, 0 ); + + /* Print a title. */ + nwipe_gui_title( main_window, select_title ); + + /* Refresh the window. */ + wnoutrefresh( main_window ); + + /* Output to physical screen */ + doupdate(); + + /* Initialise the iteration counter */ + iteration_counter = 0; + + previous_iteration_timestamp = time( NULL ); + + /* Calculate Maximum allowed iterations per second */ + expected_iterations = ( 1000 / GETCH_BLOCK_MS ) * 2; + + do + { + /* Wait 250ms for input from getch, if nothing getch will then continue, + * This is necessary so that the while loop can be exited by the + * terminate_signal e.g.. the user pressing control-c to exit. + * Do not change this value, a higher value means the keys become + * sluggish, any slower and more time is spent unnecessarily looping + * which wastes CPU cycles. + */ + + validkeyhit = 0; + timeout( GETCH_BLOCK_MS ); // block getch() for ideally about 250ms. + keystroke = getch(); // Get user input. + timeout( -1 ); // Switch back to blocking mode. + + /* To avoid 100% CPU usage, check for a runaway condition caused by the "keystroke = getch(); (above), from + * immediately returning an error condition. We check for an error condition because getch() returns a ERR + * value when the timeout value "timeout( 250 );" expires as well as when a real error occurs. We can't + * differentiate from normal operation and a failure of the getch function to block for the specified period + * of timeout. So here we check the while loop hasn't exceeded the number of expected iterations per second + * ie. a timeout(250) block value of 250ms means we should not see any more than (1000/250) = 4 iterations. + * We double this to 8 to allow a little tolerance. Why is this necessary? It's been found that in KDE + * konsole and other terminals based on the QT terminal engine exiting the terminal without first existing + * nwipe results in nwipe remaining running but detached from any interface which causes getch to fail and + * its associated timeout. So the CPU or CPU core rises to 100%. Here we detect that failure and exit nwipe + * gracefully with the appropriate error. This does not affect use of tmux for attaching or detaching from a + * running nwipe session when sitting at the selection screen. All other terminals correctly terminate nwipe + * when the terminal itself is exited. + */ + + iteration_counter++; + + if( previous_iteration_timestamp == time( NULL ) ) + { + if( iteration_counter > expected_iterations ) + { + nwipe_log( NWIPE_LOG_ERROR, + "GUI.c,nwipe_gui_select(), loop runaway, did you close the terminal without exiting " + "nwipe? Initiating shutdown now." ); + /* Issue signal to nwipe to shutdown immediately but gracefully */ + terminate_signal = 1; + } + } + else + { + /* new second, so reset counter */ + iteration_counter = 0; + previous_iteration_timestamp = time( NULL ); + } + + /* We don't necessarily use all of these. For future reference these are some CTRL+key values + * ^A - 1, ^B - 2, ^D - 4, ^E - 5, ^F - 6, ^G - 7, ^H - 8, ^I - 9, ^K - 11, ^L - 12, ^N - 14, + * ^O - 15, ^P - 16, ^R - 18, ^T - 20, ^U - 21, ^V - 22, ^W - 23, ^X - 24, ^Y - 25 + * Use nwipe_log( NWIPE_LOG_DEBUG, "Key Name: %s - %u", keyname(keystroke),keystroke) to + * figure out what code is returned by what ever key combination */ + + switch( keystroke ) + { + case KEY_DOWN: + case 'j': + case 'J': + + validkeyhit = 1; + + /* Increment the focus. */ + focus += 1; + + if( focus >= count ) + { + /* The focus is already at the last element. */ + focus = count - 1; + break; + } + + if( focus - offset >= slots ) + { + /* The next element is offscreen. Scroll down. */ + offset += 1; + break; + } + + break; + + case KEY_UP: + case 'k': + case 'K': + + validkeyhit = 1; + + /* Decrement the focus. */ + focus -= 1; + + if( focus < 0 ) + { + /* The focus is already at the last element. */ + focus = 0; + break; + } + + if( focus < offset ) + { + /* The next element is offscreen. Scroll up. */ + offset -= 1; + break; + } + + break; + + case KEY_ENTER: + case 10: + case ' ': + + validkeyhit = 1; + + /* TODO: This block should be made into a function. */ + + if( c[focus]->select == NWIPE_SELECT_TRUE ) + { + /* Reverse the selection of this element. */ + c[focus]->select = NWIPE_SELECT_FALSE; + + if( c[focus]->device_part == 0 ) + { + /* Sub-deselect all partitions and slices within this disk. */ + for( i = 0; i < count; i++ ) + { + if( c[i]->device_type == c[focus]->device_type + && c[i]->device_host == c[focus]->device_host + && c[i]->device_bus == c[focus]->device_bus + && c[i]->device_target == c[focus]->device_target + && c[i]->device_lun == c[focus]->device_lun && c[i]->device_part > 0 ) + { + c[i]->select = NWIPE_SELECT_FALSE; + } + + } /* for all contexts */ + + } /* if sub-deselect */ + + else + { + /* The number of selected partitions or slices within this disk. */ + int j = 0; + + for( i = 0; i < count; i++ ) + { + if( c[i]->device_type == c[focus]->device_type + && c[i]->device_host == c[focus]->device_host + && c[i]->device_bus == c[focus]->device_bus + && c[i]->device_target == c[focus]->device_target + && c[i]->device_lun == c[focus]->device_lun && c[i]->device_part > 0 + && c[i]->select == NWIPE_SELECT_TRUE ) + { + /* Increment the counter. */ + j += 1; + } + + } /* for all contexts */ + + if( j == 0 ) + { + /* Find the parent disk of this partition or slice. */ + for( i = 0; i < count; i++ ) + { + if( c[i]->device_type == c[focus]->device_type + && c[i]->device_host == c[focus]->device_host + && c[i]->device_bus == c[focus]->device_bus + && c[i]->device_target == c[focus]->device_target + && c[i]->device_lun == c[focus]->device_lun && c[i]->device_part == 0 ) + { + /* Enable the disk element. */ + c[i]->select = NWIPE_SELECT_FALSE; + } + + } /* for all contexts */ + + } /* if */ + + } /* else super-enable */ + + break; + + } /* if NWIPE_SELECT_TRUE */ + + if( c[focus]->select == NWIPE_SELECT_FALSE ) + { + /* Reverse the selection. */ + c[focus]->select = NWIPE_SELECT_TRUE; + + if( c[focus]->device_part == 0 ) + { + /* Sub-select all partitions and slices within this disk. */ + for( i = 0; i < count; i++ ) + { + if( c[i]->device_type == c[focus]->device_type + && c[i]->device_host == c[focus]->device_host + && c[i]->device_bus == c[focus]->device_bus + && c[i]->device_target == c[focus]->device_target + && c[i]->device_lun == c[focus]->device_lun && c[i]->device_part > 0 ) + { + c[i]->select = NWIPE_SELECT_TRUE_PARENT; + } + + } /* for */ + + } /* if sub-select */ + + else + { + /* ASSERT: ( c[focus]->device_part > 0 ) */ + + /* Super-deselect the disk that contains this device. */ + for( i = 0; i < count; i++ ) + { + if( c[i]->device_type == c[focus]->device_type + && c[i]->device_host == c[focus]->device_host + && c[i]->device_bus == c[focus]->device_bus + && c[i]->device_target == c[focus]->device_target + && c[i]->device_lun == c[focus]->device_lun && c[i]->device_part == 0 ) + { + c[i]->select = NWIPE_SELECT_FALSE_CHILD; + } + } + + } /* else super-deselect */ + + break; + + } /* if NWIPE_SELECT_FALSE */ + + /* TODO: Explain to the user why they can't change this. */ + break; + + case 'm': + case 'M': + + validkeyhit = 1; + + /* Run the method dialog. */ + nwipe_gui_method(); + break; + + case 'p': + case 'P': + + validkeyhit = 1; + + /* Run the PRNG dialog. */ + nwipe_gui_prng(); + + break; + + case 'r': + case 'R': + + validkeyhit = 1; + + /* Run the rounds dialog. */ + nwipe_gui_rounds(); + + break; + + case 'v': + case 'V': + + validkeyhit = 1; + + /* Run the option dialog. */ + nwipe_gui_verify(); + break; + + case 'b': + case 'B': + + validkeyhit = 1; + + if( nwipe_options.method == &nwipe_ops2 ) + { + /* Warn the user about that zero blanking with the ops2 method is not allowed */ + wattron( footer_window, COLOR_PAIR( 10 ) ); + nwipe_gui_amend_footer_window( main_window_footer_warning_no_blanking_with_ops2 ); + doupdate(); + sleep( 3 ); + wattroff( footer_window, COLOR_PAIR( 10 ) ); + + /* After the delay return footer text back to key help */ + nwipe_gui_amend_footer_window( main_window_footer ); + doupdate(); + + break; + } + + if( nwipe_options.method == &nwipe_verify_zero || nwipe_options.method == &nwipe_verify_one ) + { + /* Warn the user about that zero blanking with the ops2 method is not allowed */ + wattron( footer_window, COLOR_PAIR( 10 ) ); + nwipe_gui_amend_footer_window( main_window_footer_warning_no_blanking_with_verify_only ); + doupdate(); + sleep( 3 ); + wattroff( footer_window, COLOR_PAIR( 10 ) ); + + /* After the delay return footer text back to key help */ + nwipe_gui_amend_footer_window( main_window_footer ); + doupdate(); + + break; + } + + /* Run the noblank dialog. */ + nwipe_gui_noblank(); + break; + + case 'S': + + /* User wants to start the wipe */ + validkeyhit = 1; + + /* Have any drives have been selected ? */ + number_of_selected_contexts = 0; + for( i = 0; i < count; i++ ) + { + if( c[i]->select == NWIPE_SELECT_TRUE ) + { + number_of_selected_contexts += 1; + } + } + + /* if no drives have been selected, print a warning on the footer */ + if( number_of_selected_contexts == 0 ) + { + wattron( footer_window, COLOR_PAIR( 10 ) ); + nwipe_gui_amend_footer_window( main_window_footer_warning_no_drive_selected ); + doupdate(); + sleep( 3 ); + wattroff( footer_window, COLOR_PAIR( 10 ) ); + + /* After the delay return footer text back to key help */ + nwipe_gui_amend_footer_window( main_window_footer ); + doupdate(); + + /* Remove any repeated S key strokes, without this the gui would hang + * for a period of time, i.e sleep above x number of repeated 's' keystrokes + * which could run into minutes */ + do + { + timeout( 250 ); // block getch() for 250ms. + keystroke = getch(); // Get user input. + timeout( -1 ); // Switch back to blocking mode. + } while( keystroke == 'S' ); + + /* Remove the S from keystroke, which allows us to stay within the selection menu loop */ + keystroke = 0; + } + + break; + + case 's': + + /* user has mistakenly hit the lower case 's' instead of capital 'S' */ + validkeyhit = 1; + + /* Warn the user about their mistake */ + wattron( footer_window, COLOR_PAIR( 10 ) ); + nwipe_gui_amend_footer_window( main_window_footer_warning_lower_case_s ); + doupdate(); + sleep( 3 ); + wattroff( footer_window, COLOR_PAIR( 10 ) ); + + /* After the delay return footer text back to key help */ + nwipe_gui_amend_footer_window( main_window_footer ); + doupdate(); + + /* Remove any repeated s key strokes, without this the gui would hang + * for a period of time, i.e sleep above x number of repeated 's' keystrokes + * which could run into minutes */ + do + { + timeout( 250 ); // block getch() for 250ms. + keystroke = getch(); // Get user input. + timeout( -1 ); // Switch back to blocking mode. + } while( keystroke == 's' ); + + break; + + case 1: + + /* Ctrl A - Toggle select/deselect all drives */ + validkeyhit = 1; + + if( select_all_toggle_status == -1 || select_all_toggle_status == 0 ) + { + for( i = 0; i < count; i++ ) + { + c[i]->select = NWIPE_SELECT_TRUE; + } + select_all_toggle_status = 1; + } + else + { + if( select_all_toggle_status == 1 ) + { + for( i = 0; i < count; i++ ) + { + c[i]->select = NWIPE_SELECT_FALSE; + } + select_all_toggle_status = 0; + } + else + { + nwipe_log( + NWIPE_LOG_ERROR, + "gui.c:nwipe_gui_select(), Invalid value in variable select_all_toggle_status = %d", + select_all_toggle_status ); + } + } + + break; + + } /* keystroke switch */ + + /* Check the terminal size, if the user has changed it the while loop checks for + * this change and exits the valid key hit loop so the windows can be updated */ + getmaxyx( stdscr, stdscr_lines, stdscr_cols ); + + /* Update the selection window every 60 seconds specifically so that the drive temperatures are updated */ + if( time( NULL ) > ( temperature_check_time + 60 ) ) + { + temperature_check_time = time( NULL ); + validkeyhit = 1; + } + + } /* key hit loop */ + while( validkeyhit == 0 && terminate_signal != 1 && stdscr_cols_previous == stdscr_cols + && stdscr_lines_previous == stdscr_lines ); + + } while( keystroke != 'S' && terminate_signal != 1 ); + + if( keystroke == 'S' ) + { + /* If user has pressed S to start wipe change status line */ + werase( footer_window ); + nwipe_gui_title( footer_window, end_wipe_footer ); + wnoutrefresh( footer_window ); + } + +} /* nwipe_gui_select */ + +void nwipe_gui_options( void ) +{ + /** + * Updates the options window. + * + * @modifies options_window + * + */ + + /* Erase the window. */ + werase( options_window ); + + mvwprintw( + options_window, NWIPE_GUI_OPTIONS_ENTROPY_Y, NWIPE_GUI_OPTIONS_ENTROPY_X, "Entropy: Linux Kernel (urandom)" ); + + mvwprintw( + options_window, NWIPE_GUI_OPTIONS_PRNG_Y, NWIPE_GUI_OPTIONS_PRNG_X, "PRNG: %s", nwipe_options.prng->label ); + + mvwprintw( options_window, + NWIPE_GUI_OPTIONS_METHOD_Y, + NWIPE_GUI_OPTIONS_METHOD_X, + "Method: %s", + nwipe_method_label( nwipe_options.method ) ); + + mvwprintw( options_window, NWIPE_GUI_OPTIONS_VERIFY_Y, NWIPE_GUI_OPTIONS_VERIFY_X, "Verify: " ); + + switch( nwipe_options.verify ) + { + case NWIPE_VERIFY_NONE: + wprintw( options_window, "Off" ); + break; + + case NWIPE_VERIFY_LAST: + wprintw( options_window, "Last Pass" ); + break; + + case NWIPE_VERIFY_ALL: + wprintw( options_window, "All Passes" ); + break; + + default: + wprintw( options_window, "Unknown %i", nwipe_options.verify ); + + } /* switch verify */ + + mvwprintw( options_window, NWIPE_GUI_OPTIONS_ROUNDS_Y, NWIPE_GUI_OPTIONS_ROUNDS_X, "Rounds: " ); + + /* Disable blanking for ops2 and verify methods */ + if( nwipe_options.method == &nwipe_ops2 || nwipe_options.method == &nwipe_verify_zero + || nwipe_options.method == &nwipe_verify_one ) + { + nwipe_options.noblank = 1; + } + + if( nwipe_options.noblank ) + { + wprintw( options_window, "%i (no final blanking pass)", nwipe_options.rounds ); + } + else + { + wprintw( options_window, "%i (plus blanking pass)", nwipe_options.rounds ); + } + + /* Add a border. */ + box( options_window, 0, 0 ); + + /* Add a title. */ + nwipe_gui_title( options_window, options_title ); + + /* Refresh the window. */ + // wrefresh( options_window ); + wnoutrefresh( options_window ); + +} /* nwipe_gui_options */ + +void nwipe_gui_rounds( void ) +{ + /** + * Allows the user to change the rounds option. + * + * @modifies nwipe_options.rounds + * @modifies main_window + * + */ + + /* Set the initial focus. */ + int focus = nwipe_options.rounds; + + /* The first tabstop. */ + const int tab1 = 2; + + /* The current working row. */ + int yy; + + /* Input buffer. */ + int keystroke; + + extern int terminate_signal; + + /* Update the footer window. */ + werase( footer_window ); + nwipe_gui_title( footer_window, rounds_footer ); + wrefresh( footer_window ); + + do + { + /* Erase the main window. */ + werase( main_window ); + + nwipe_gui_create_all_windows_on_terminal_resize( 0, selection_footer ); + + /* Add a border. */ + box( main_window, 0, 0 ); + + /* Add a title. */ + nwipe_gui_title( main_window, " Rounds " ); + + /* Initialize the working row. */ + yy = 4; + + mvwprintw( main_window, yy++, tab1, "This is the number of times to run the wipe method on each device." ); + yy++; + + if( focus > 0 ) + { + /* Print this line last so that the cursor is in the right place. */ + mvwprintw( main_window, 2, tab1, "> %i", focus ); + } + else + { + mvwprintw( main_window, yy++, tab1, "The number of rounds must be a non-negative integer." ); + + /* Print this line last so that the cursor is in the right place. */ + mvwprintw( main_window, 2, tab1, "> " ); + } + + /* Reveal the cursor. */ + curs_set( 1 ); + + /* Refresh the window. */ + wrefresh( main_window ); + + /* Wait 250ms for input from getch, if nothing getch will then continue, + * This is necessary so that the while loop can be exited by the + * terminate_signal e.g.. the user pressing control-c to exit. + * Do not change this value, a higher value means the keys become + * sluggish, any slower and more time is spent unnecessarily looping + * which wastes CPU cycles. + */ + timeout( 250 ); // block getch() for 250ms. + keystroke = getch(); // Get a keystroke. + timeout( -1 ); // Switch back to blocking mode. + + switch( keystroke ) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + + if( focus < 100000000 ) + { + /* Left shift, base ten. */ + focus *= 10; + + /* This assumes ASCII input, where the zero character is 0x30. */ + focus += keystroke - 48; + } + + break; + + /* Escape key. */ + case 27: + return; + + case KEY_BACKSPACE: + case KEY_LEFT: + case 127: + + /* Right shift, base ten. */ + focus /= 10; + + break; + + } /* switch keystroke */ + + /* Hide the cursor. */ + curs_set( 0 ); + + } while( keystroke != 10 && terminate_signal != 1 ); + + if( focus > 0 ) + { + /* Set the number of rounds. */ + nwipe_options.rounds = focus; + } + +} /* nwipe_guid_rounds */ + +void nwipe_gui_prng( void ) +{ + /** + * Allows the user to change the PRNG. + * + * @modifies nwipe_options.prng + * @modifies main_window + * + */ + + extern nwipe_prng_t nwipe_twister; + extern nwipe_prng_t nwipe_isaac; + extern nwipe_prng_t nwipe_isaac64; + extern int terminate_signal; + + /* The number of implemented PRNGs. */ + const int count = 3; + + /* The first tabstop. */ + const int tab1 = 2; + + /* The second tabstop. */ + const int tab2 = 30; + + /* Set the initial focus. */ + int focus = 0; + + /* The current working row. */ + int yy; + + /* Input buffer. */ + int keystroke; + + /* Update the footer window. */ + werase( footer_window ); + nwipe_gui_title( footer_window, selection_footer ); + wrefresh( footer_window ); + + if( nwipe_options.prng == &nwipe_twister ) + { + focus = 0; + } + if( nwipe_options.prng == &nwipe_isaac ) + { + focus = 1; + } + if( nwipe_options.prng == &nwipe_isaac64 ) + { + focus = 2; + } + + do + { + /* Clear the main window. */ + werase( main_window ); + + nwipe_gui_create_all_windows_on_terminal_resize( 0, selection_footer ); + + /* Initialize the working row. */ + yy = 3; + + /* Print the options. */ + mvwprintw( main_window, yy++, tab1, " %s", nwipe_twister.label ); + mvwprintw( main_window, yy++, tab1, " %s", nwipe_isaac.label ); + mvwprintw( main_window, yy++, tab1, " %s", nwipe_isaac64.label ); + yy++; + + /* Print the cursor. */ + mvwaddch( main_window, 3 + focus, tab1, ACS_RARROW ); + + switch( focus ) + { + case 0: + + mvwprintw( main_window, + yy++, + tab1, + "The Mersenne Twister, by Makoto Matsumoto and Takuji Nishimura, is a " ); + mvwprintw( main_window, + yy++, + tab1, + "generalized feedback shift register PRNG that is uniform and " ); + mvwprintw( main_window, + yy++, + tab1, + "equidistributed in 623-dimensions with a proven period of 2^19937-1. " ); + mvwprintw( main_window, + yy++, + tab1, + " " ); + mvwprintw( main_window, + yy++, + tab1, + "This implementation passes the Marsaglia Diehard test suite. " ); + mvwprintw( main_window, + yy++, + tab1, + " " ); + break; + + case 1: + + mvwprintw( main_window, + yy++, + tab1, + "ISAAC, by Bob Jenkins, is a PRNG derived from RC4 with a minimum period of " ); + mvwprintw( main_window, + yy++, + tab1, + "2^40 and an expected period of 2^8295. It is difficult to recover the " ); + mvwprintw( main_window, + yy++, + tab1, + "initial PRNG state by cryptanalysis of the ISAAC stream. " ); + mvwprintw( main_window, + yy++, + tab1, + " " ); + mvwprintw( main_window, + yy++, + tab1, + "Performs best on a 32-bit CPU. Use ISAAC-64 if this system has a 64-bit CPU." ); + break; + + case 2: + + mvwprintw( main_window, + yy++, + tab1, + "ISAAC-64, by Bob Jenkins, is like 32-bit ISAAC, but with a minimum period of" ); + mvwprintw( main_window, + yy++, + tab1, + "2^77 and an expected period of 2^16583. It is difficult to recover the " ); + mvwprintw( main_window, + yy++, + tab1, + "initial PRNG state by cryptanalysis of the ISAAC-64 stream. " ); + mvwprintw( main_window, + yy++, + tab1, + " " ); + mvwprintw( main_window, + yy++, + tab1, + "Performs best on a 64-bit CPU. Use ISAAC if this system has a 32-bit CPU. " ); + break; + + } /* switch */ + + /* Add a border. */ + box( main_window, 0, 0 ); + + /* Add a title. */ + nwipe_gui_title( main_window, " Pseudo Random Number Generator " ); + + /* Refresh the window. */ + wrefresh( main_window ); + + /* Wait 250ms for input from getch, if nothing getch will then continue, + * This is necessary so that the while loop can be exited by the + * terminate_signal e.g.. the user pressing control-c to exit. + * Do not change this value, a higher value means the keys become + * sluggish, any slower and more time is spent unnecessarily looping + * which wastes CPU cycles. + */ + timeout( 250 ); // block getch() for 250ms. + keystroke = getch(); // Get a keystroke. + timeout( -1 ); // Switch back to blocking mode. + + switch( keystroke ) + { + case KEY_DOWN: + case 'j': + case 'J': + + if( focus < count - 1 ) + { + focus += 1; + } + break; + + case KEY_UP: + case 'k': + case 'K': + + if( focus > 0 ) + { + focus -= 1; + } + break; + + case KEY_ENTER: + case ' ': + case 10: + + if( focus == 0 ) + { + nwipe_options.prng = &nwipe_twister; + } + if( focus == 1 ) + { + nwipe_options.prng = &nwipe_isaac; + } + if( focus == 2 ) + { + nwipe_options.prng = &nwipe_isaac64; + } + return; + + case KEY_BACKSPACE: + case KEY_BREAK: + + return; + + } /* switch */ + + } while( terminate_signal != 1 ); + +} /* nwipe_gui_prng */ + +void nwipe_gui_verify( void ) +{ + /** + * Allows the user to change the verification option. + * + * @modifies nwipe_options.verify + * @modifies main_window + * + */ + + extern int terminate_signal; + + /* The number of definitions in the nwipe_verify_t enumeration. */ + const int count = 3; + + /* The first tabstop. */ + const int tab1 = 2; + + /* The second tabstop. */ + const int tab2 = 30; + + /* Set the initial focus. */ + int focus = nwipe_options.verify; + + /* The current working row. */ + int yy; + + /* Input buffer. */ + int keystroke; + + /* Update the footer window. */ + werase( footer_window ); + nwipe_gui_title( footer_window, selection_footer ); + wrefresh( footer_window ); + + do + { + nwipe_gui_create_all_windows_on_terminal_resize( 0, selection_footer ); + + /* Clear the main window. */ + werase( main_window ); + + /* Initialize the working row. */ + yy = 2; + + /* Print the options. */ + mvwprintw( main_window, yy++, tab1, " Verification Off " ); + mvwprintw( main_window, yy++, tab1, " Verify Last Pass " ); + mvwprintw( main_window, yy++, tab1, " Verify All Passes " ); + mvwprintw( main_window, yy++, tab1, " " ); + + /* Print the cursor. */ + mvwaddch( main_window, 2 + focus, tab1, ACS_RARROW ); + + switch( focus ) + { + case 0: + + mvwprintw( main_window, + yy++, + tab1, + "Do not verify passes. The wipe will be a write-only operation. " ); + mvwprintw( main_window, + yy++, + tab1, + " " ); + break; + + case 1: + + mvwprintw( main_window, + yy++, + tab1, + "Check whether the device is actually empty after the last pass fills the " ); + mvwprintw( main_window, + yy++, + tab1, + "device with zeros. " ); + break; + + case 2: + + mvwprintw( main_window, + yy++, + tab1, + "After every pass, read back the pattern and check whether it is correct. " ); + mvwprintw( main_window, + yy++, + tab1, + " " ); + mvwprintw( main_window, + yy++, + tab1, + "This program writes the entire length of the device before it reads back " ); + mvwprintw( main_window, + yy++, + tab1, + "for verification, even for random pattern passes, to better ensure that " ); + mvwprintw( main_window, + yy++, + tab1, + "hardware caches are actually flushed. " ); + break; + + } /* switch */ + + /* Add a border. */ + box( main_window, 0, 0 ); + + /* Add a title. */ + nwipe_gui_title( main_window, " Verification Mode " ); + + /* Refresh the window. */ + wrefresh( main_window ); + + /* Wait 250ms for input from getch, if nothing getch will then continue, + * This is necessary so that the while loop can be exited by the + * terminate_signal e.g.. the user pressing control-c to exit. + * Do not change this value, a higher value means the keys become + * sluggish, any slower and more time is spent unnecessarily looping + * which wastes CPU cycles. + */ + timeout( 250 ); // block getch() for 250ms. + keystroke = getch(); // Get a keystroke. + timeout( -1 ); // Switch back to blocking mode. + + switch( keystroke ) + { + case KEY_DOWN: + case 'j': + case 'J': + + if( focus < count - 1 ) + { + focus += 1; + } + break; + + case KEY_UP: + case 'k': + case 'K': + + if( focus > 0 ) + { + focus -= 1; + } + break; + + case KEY_ENTER: + case ' ': + case 10: + + if( focus >= 0 && focus < count ) + { + nwipe_options.verify = focus; + } + return; + + case KEY_BACKSPACE: + case KEY_BREAK: + + return; + + } /* switch */ + + } while( terminate_signal != 1 ); + +} /* nwipe_gui_verify */ + +void nwipe_gui_noblank( void ) +{ + /** + * Allows the user to change the verification option. + * + * @modifies nwipe_options.noblank + * @modifies main_window + * + */ + + extern int terminate_signal; + + /* The number of options available. */ + const int count = 2; + + /* The first tabstop. */ + const int tab1 = 2; + + /* The second tabstop. */ + const int tab2 = 40; + + /* Set the initial focus. */ + int focus = nwipe_options.noblank; + + /* The current working row. */ + int yy; + + /* Input buffer. */ + int keystroke; + + /* Update the footer window. */ + werase( footer_window ); + nwipe_gui_title( footer_window, selection_footer ); + wrefresh( footer_window ); + + do + { + nwipe_gui_create_all_windows_on_terminal_resize( 0, selection_footer ); + + /* Clear the main window. */ + werase( main_window ); + + /* Initialize the working row. */ + yy = 2; + + /* Print the options. */ + mvwprintw( main_window, yy++, tab1, " Perform a final blanking pass " ); + mvwprintw( main_window, yy++, tab1, " Do not perform final blanking pass " ); + mvwprintw( main_window, yy++, tab1, " " ); + + /* Print the cursor. */ + mvwaddch( main_window, 2 + focus, tab1, ACS_RARROW ); + + switch( focus ) + { + case 0: + + mvwprintw( main_window, + yy++, + tab1, + "Perform a final blanking pass after the wipe, leaving disk with only zeros. " ); + mvwprintw( main_window, + yy++, + tab1, + "Note that the RCMP TSSIT OPS-II method never blanks the device regardless " ); + mvwprintw( main_window, + yy++, + tab1, + "of this setting. " ); + mvwprintw( main_window, + yy++, + tab1, + " " ); + break; + + case 1: + + mvwprintw( main_window, + yy++, + tab1, + "Do not perform a final blanking pass. Leave data as per final wiping pass. " ); + mvwprintw( main_window, + yy++, + tab1, + "Note that the RCMP TSSIT OPS-II method never blanks the device regardless " ); + mvwprintw( main_window, + yy++, + tab1, + "of this setting. " ); + mvwprintw( main_window, + yy++, + tab1, + " " ); + break; + + } /* switch */ + + /* Add a border. */ + box( main_window, 0, 0 ); + + /* Add a title. */ + nwipe_gui_title( main_window, " Final Blanking Pass " ); + + /* Refresh the window. */ + wrefresh( main_window ); + + /* Wait 250ms for input from getch, if nothing getch will then continue, + * This is necessary so that the while loop can be exited by the + * terminate_signal e.g.. the user pressing control-c to exit. + * Do not change this value, a higher value means the keys become + * sluggish, any slower and more time is spent unnecessarily looping + * which wastes CPU cycles. + */ + timeout( 250 ); // block getch() for 250ms. + keystroke = getch(); // Get a keystroke. + timeout( -1 ); // Switch back to blocking mode. + + switch( keystroke ) + { + case KEY_DOWN: + case 'j': + case 'J': + + if( focus < count - 1 ) + { + focus += 1; + } + break; + + case KEY_UP: + case 'k': + case 'K': + + if( focus > 0 ) + { + focus -= 1; + } + break; + + case KEY_ENTER: + case ' ': + case 10: + + if( focus >= 0 && focus < count ) + { + nwipe_options.noblank = focus; + } + return; + + case KEY_BACKSPACE: + case KEY_BREAK: + + return; + + } /* switch */ + + } + + while( terminate_signal != 1 ); +} /* nwipe_gui_noblank */ + +void nwipe_gui_method( void ) +{ + /** + * Allows the user to change the wipe method. + * + * @modifies nwipe_options.method + * @modifies main_window + * + */ + + extern int terminate_signal; + + /* The number of implemented methods. */ + const int count = 10; + + /* The first tabstop. */ + const int tab1 = 2; + + /* The second tabstop. */ + const int tab2 = 30; + + /* The currently selected method. */ + int focus = 0; + + /* The current working row. */ + int yy; + + /* Input buffer. */ + int keystroke; + + /* Update the footer window. */ + werase( footer_window ); + nwipe_gui_title( footer_window, selection_footer ); + wrefresh( footer_window ); + + if( nwipe_options.method == &nwipe_zero ) + { + focus = 0; + } + if( nwipe_options.method == &nwipe_one ) + { + focus = 1; + } + if( nwipe_options.method == &nwipe_ops2 ) + { + focus = 2; + } + if( nwipe_options.method == &nwipe_dodshort ) + { + focus = 3; + } + if( nwipe_options.method == &nwipe_dod522022m ) + { + focus = 4; + } + if( nwipe_options.method == &nwipe_gutmann ) + { + focus = 5; + } + if( nwipe_options.method == &nwipe_random ) + { + focus = 6; + } + if( nwipe_options.method == &nwipe_verify_zero ) + { + focus = 7; + } + if( nwipe_options.method == &nwipe_verify_one ) + { + focus = 8; + } + if( nwipe_options.method == &nwipe_is5enh ) + { + focus = 9; + } + + do + { + /* Clear the main window. */ + werase( main_window ); + + nwipe_gui_create_all_windows_on_terminal_resize( 0, selection_footer ); + + /* Initialize the working row. */ + yy = 2; + + /* Print the options. */ + mvwprintw( main_window, yy++, tab1, " %s", nwipe_method_label( &nwipe_zero ) ); + mvwprintw( main_window, yy++, tab1, " %s", nwipe_method_label( &nwipe_one ) ); + mvwprintw( main_window, yy++, tab1, " %s", nwipe_method_label( &nwipe_ops2 ) ); + mvwprintw( main_window, yy++, tab1, " %s", nwipe_method_label( &nwipe_dodshort ) ); + mvwprintw( main_window, yy++, tab1, " %s", nwipe_method_label( &nwipe_dod522022m ) ); + mvwprintw( main_window, yy++, tab1, " %s", nwipe_method_label( &nwipe_gutmann ) ); + mvwprintw( main_window, yy++, tab1, " %s", nwipe_method_label( &nwipe_random ) ); + mvwprintw( main_window, yy++, tab1, " %s", nwipe_method_label( &nwipe_verify_zero ) ); + mvwprintw( main_window, yy++, tab1, " %s", nwipe_method_label( &nwipe_verify_one ) ); + mvwprintw( main_window, yy++, tab1, " %s", nwipe_method_label( &nwipe_is5enh ) ); + mvwprintw( main_window, yy++, tab1, " " ); + + /* Print the cursor. */ + mvwaddch( main_window, 2 + focus, tab1, ACS_RARROW ); + + switch( focus ) + { + case 0: + + mvwprintw( main_window, 2, tab2, "Security Level: high (1 pass)" ); + + mvwprintw( main_window, 4, tab2, "This method fills the device with zeros. Note " ); + mvwprintw( main_window, 5, tab2, "that the rounds option does not apply to this " ); + mvwprintw( main_window, 6, tab2, "method. This method always runs one round. " ); + mvwprintw( main_window, 7, tab2, " " ); + mvwprintw( main_window, 8, tab2, "There is no publicly available evidence that " ); + mvwprintw( main_window, 9, tab2, "data can be recovered from a modern traditional " ); + mvwprintw( main_window, 10, tab2, "hard drive (HDD) that has been zero wiped, " ); + mvwprintw( main_window, 11, tab2, "however a wipe that includes a prng may be " ); + mvwprintw( main_window, 12, tab2, "preferable. " ); + break; + + case 1: + + mvwprintw( main_window, 2, tab2, "Security Level: high (1 pass)" ); + + mvwprintw( main_window, 4, tab2, "This method fills the device with ones. Note that" ); + mvwprintw( main_window, 5, tab2, "the rounds option does not apply to this method. " ); + mvwprintw( main_window, 6, tab2, "This method always runs one round. " ); + mvwprintw( main_window, 7, tab2, " " ); + mvwprintw( main_window, 8, tab2, "This method might be used when wiping a solid " ); + mvwprintw( main_window, 9, tab2, "state drive if an additional level of security is" ); + mvwprintw( main_window, 10, tab2, "required beyond using the drives internal secure " ); + mvwprintw( main_window, 11, tab2, "erase features. Alternatively PRNG may be " ); + mvwprintw( main_window, 12, tab2, "preferable. " ); + break; + + case 2: + + mvwprintw( main_window, 2, tab2, "Security Level: higher (8 passes)" ); + + mvwprintw( main_window, 4, tab2, "The Royal Canadian Mounted Police Technical " ); + mvwprintw( main_window, 5, tab2, "Security Standard for Information Technology. " ); + mvwprintw( main_window, 6, tab2, "Appendix OPS-II: Media Sanitization. " ); + mvwprintw( main_window, 7, tab2, " " ); + mvwprintw( main_window, 8, tab2, "This implementation, with regards to paragraph 2 " ); + mvwprintw( main_window, 9, tab2, "section A of the standard, uses a pattern that is" ); + mvwprintw( main_window, 10, tab2, "one random byte and that is changed each round. " ); + break; + + case 3: + + mvwprintw( main_window, 2, tab2, "Security Level: higher (3 passes)" ); + + mvwprintw( main_window, 4, tab2, "The US Department of Defense 5220.22-M short wipe" ); + mvwprintw( main_window, 5, tab2, "This method is composed of passes 1, 2 & 7 from " ); + mvwprintw( main_window, 6, tab2, "the standard DoD 5220.22-M wipe. " ); + mvwprintw( main_window, 7, tab2, " " ); + mvwprintw( main_window, 8, tab2, "Pass 1: A random character " ); + mvwprintw( main_window, 9, tab2, "Pass 2: The bitwise complement of pass 1. " ); + mvwprintw( main_window, 10, tab2, "Pass 3: A random number generated data stream " ); + break; + + case 4: + + mvwprintw( main_window, 2, tab2, "Security Level: higher (7 passes)" ); + + mvwprintw( main_window, 3, tab2, "The American Department of Defense 5220.22-M " ); + mvwprintw( main_window, 4, tab2, "standard wipe. " ); + mvwprintw( main_window, 5, tab2, " " ); + mvwprintw( main_window, 6, tab2, "Pass 1: A Random character " ); + mvwprintw( main_window, 7, tab2, "Pass 2: The bitwise complement of pass 1 " ); + mvwprintw( main_window, 8, tab2, "Pass 3: A random number generated data stream " ); + mvwprintw( main_window, 9, tab2, "Pass 4: A Random character " ); + mvwprintw( main_window, 10, tab2, "Pass 5: A Random character " ); + mvwprintw( main_window, 11, tab2, "Pass 6: The bitwise complement of pass 5 " ); + mvwprintw( main_window, 12, tab2, "Pass 7: A random number generated data stream " ); + break; + + case 5: + + mvwprintw( main_window, 2, tab2, "Security Level: Paranoid ! (35 passes) " ); + mvwprintw( main_window, 3, tab2, "Don't waste your time with this on a modern drive" ); + + mvwprintw( main_window, 5, tab2, "This is the method described by Peter Gutmann in " ); + mvwprintw( main_window, 6, tab2, "the paper entitled \"Secure Deletion of Data from" ); + mvwprintw( main_window, 7, tab2, "Magnetic and Solid-State Memory\", however not " ); + mvwprintw( main_window, 8, tab2, "relevant in regards to modern hard disk drives. " ); + break; + + case 6: + + mvwprintw( main_window, 2, tab2, "Security Level: Depends on Rounds" ); + + mvwprintw( main_window, 4, tab2, "This method fills the device with a stream from " ); + mvwprintw( main_window, 5, tab2, "the PRNG. It is probably the best method to use " ); + mvwprintw( main_window, 6, tab2, "on modern hard disk drives due to variation in " ); + mvwprintw( main_window, 7, tab2, "encoding methods. " ); + mvwprintw( main_window, 8, tab2, " " ); + mvwprintw( main_window, 9, tab2, "This method has a high security level with 1 " ); + mvwprintw( main_window, 10, tab2, "round and an increasingly higher security level " ); + mvwprintw( main_window, 11, tab2, "as rounds are increased." ); + break; + + case 7: + + mvwprintw( main_window, 2, tab2, "Security Level: Not applicable" ); + + mvwprintw( main_window, 4, tab2, "This method only reads the device and checks " ); + mvwprintw( main_window, 5, tab2, "that it is all zero. " ); + + break; + + case 8: + + mvwprintw( main_window, 2, tab2, "Security Level: Not applicable" ); + + mvwprintw( main_window, 4, tab2, "This method only reads the device and checks " ); + mvwprintw( main_window, 5, tab2, "that it is all ones (0xFF)." ); + + break; + + case 9: + + mvwprintw( main_window, 2, tab2, "Security Level: higher (3 passes)" ); + + mvwprintw( main_window, 4, tab2, "HMG IA/IS 5 (Infosec Standard 5): Secure " ); + mvwprintw( main_window, 5, tab2, "Sanitisation of Protectively Marked Information " ); + mvwprintw( main_window, 6, tab2, "or Sensitive Information " ); + mvwprintw( main_window, 7, tab2, " " ); + mvwprintw( main_window, 8, tab2, "This method fills the device with 0s, then with " ); + mvwprintw( main_window, 9, tab2, "1s, then with a PRNG stream, then reads the " ); + mvwprintw( main_window, 10, tab2, "device to verify the PRNG stream was " ); + mvwprintw( main_window, 11, tab2, "successfully written. " ); + break; + + } /* switch */ + + /* Add a border. */ + box( main_window, 0, 0 ); + + /* Add a title. */ + nwipe_gui_title( main_window, " Wipe Method " ); + + /* Refresh the window. */ + wrefresh( main_window ); + + /* Wait 250ms for input from getch, if nothing getch will then continue, + * This is necessary so that the while loop can be exited by the + * terminate_signal e.g.. the user pressing control-c to exit. + * Do not change this value, a higher value means the keys become + * sluggish, any slower and more time is spent unnecessarily looping + * which wastes CPU cycles. + */ + timeout( 250 ); /* block getch() for 250ms */ + keystroke = getch(); /* Get a keystroke. */ + timeout( -1 ); /* Switch back to blocking mode */ + + switch( keystroke ) + { + case KEY_DOWN: + case 'j': + case 'J': + + if( focus < count - 1 ) + { + focus += 1; + } + break; + + case KEY_UP: + case 'k': + case 'K': + + if( focus > 0 ) + { + focus -= 1; + } + break; + + case KEY_BACKSPACE: + case KEY_BREAK: + + return; + + } /* switch */ + + } while( keystroke != KEY_ENTER && keystroke != ' ' && keystroke != 10 && terminate_signal != 1 ); + + switch( focus ) + { + case 0: + nwipe_options.method = &nwipe_zero; + break; + + case 1: + nwipe_options.method = &nwipe_one; + break; + + case 2: + nwipe_options.method = &nwipe_ops2; + break; + + case 3: + nwipe_options.method = &nwipe_dodshort; + break; + + case 4: + nwipe_options.method = &nwipe_dod522022m; + break; + + case 5: + nwipe_options.method = &nwipe_gutmann; + break; + + case 6: + nwipe_options.method = &nwipe_random; + break; + + case 7: + nwipe_options.method = &nwipe_verify_zero; + break; + + case 8: + nwipe_options.method = &nwipe_verify_one; + break; + + case 9: + nwipe_options.method = &nwipe_is5enh; + break; + } + +} /* nwipe_gui_method */ + +void nwipe_gui_load( void ) +{ + /** + * Prints the system load average to the statistics window. + * + * @modifies stat_window Prints the system load average to the statistics window. + * + */ + + /* A file handle for the stat file. */ + FILE* nwipe_fp; + + /* The one, five, and fifteen minute load averages. */ + float load_01; + float load_05; + float load_15; + + /* Open the loadavg file. */ + nwipe_fp = fopen( NWIPE_KNOB_LOADAVG, "r" ); + + /* Print the label. */ + mvwprintw( stats_window, NWIPE_GUI_STATS_LOAD_Y, NWIPE_GUI_STATS_LOAD_X, "Load Averages:" ); + + if( nwipe_fp ) + { + /* The load averages are the first three numbers in the file. */ + if( 3 == fscanf( nwipe_fp, "%f %f %f", &load_01, &load_05, &load_15 ) ) + { + /* Print the load average. */ + mvwprintw( stats_window, + NWIPE_GUI_STATS_LOAD_Y, + NWIPE_GUI_STATS_TAB, + "%04.2f %04.2f %04.2f", + load_01, + load_05, + load_15 ); + } + else + { + /* Print an error. */ + mvwprintw( stats_window, NWIPE_GUI_STATS_LOAD_Y, NWIPE_GUI_STATS_TAB, "(fscanf error %i)", errno ); + } + + /* Close the loadavg file. */ + fclose( nwipe_fp ); + } + else + { + mvwprintw( stats_window, NWIPE_GUI_STATS_LOAD_Y, NWIPE_GUI_STATS_TAB, "(fopen error %i)", errno ); + } + +} /* nwipe_gui_load */ + +void* nwipe_gui_status( void* ptr ) +{ + /** + * Shows runtime statistics and overall progress. + * + * @parameter count The number of contexts in the array. + * @parameter c An array of device contexts. + * + * @modifies main_window Prints information into the main window. + * @modifies c[].throughput Updates the i/o throughput value. + * + */ + + extern int terminate_signal; + + nwipe_thread_data_ptr_t* nwipe_thread_data_ptr; + nwipe_thread_data_ptr = (nwipe_thread_data_ptr_t*) ptr; + + nwipe_context_t** c; + nwipe_misc_thread_data_t* nwipe_misc_thread_data; + int count; + + c = nwipe_thread_data_ptr->c; + nwipe_misc_thread_data = nwipe_thread_data_ptr->nwipe_misc_thread_data; + count = nwipe_misc_thread_data->nwipe_selected; + + char nomenclature_result_str[NOMENCLATURE_RESULT_STR_SIZE]; /* temporary usage */ + + /* Spinner character */ + char spinner_string[2]; + + /* We count time from when this function is first called. */ + static time_t nwipe_time_start = 0; + + /* Whether the screen has been blanked by the user. */ + static int nwipe_gui_blank = 0; + + /* The current time. */ + time_t nwipe_time_now; + + /* The time when all wipes ended */ + time_t nwipe_time_stopped; + + /* The index of the element that is visible in the first slot. */ + static int offset; + + /* The number of elements that we can show in the window. */ + int slots; + + /* Window dimensions. */ + int wlines; + int wcols; + + /* Generic loop variable. */ + int i; + + /* The current working line in the main window. */ + int yy; + + /* User input buffer. */ + int keystroke; + + /* controls main while loop */ + int loop_control; + + /* The combined througput of all processes. */ + nwipe_misc_thread_data->throughput = 0; + + /* The estimated runtime of the slowest device. */ + nwipe_misc_thread_data->maxeta = 0; + + /* The combined number of errors of all processes. */ + nwipe_misc_thread_data->errors = 0; + + /* Time values. */ + int nwipe_hh; + int nwipe_mm; + int nwipe_ss; + + struct timespec tim, tim2; + tim.tv_sec = 0; + tim.tv_nsec = 100000000L; /* sleep for 0.1 seconds */ + + /* Throughput variables */ + u64 nwipe_throughput; + + /* The number of active wipe processes. */ + /* Set to 1 initially to start loop. */ + int nwipe_active = 1; + + /* Used in the gui status loop to trap a failure of the halfdelay(), getch() mechanism to block for the designated + * period */ + int expected_iterations; + + /* Used in the selection loop to trap a failure of the timeout(), getch() mechanism to block for the designated + * period, initialise the counter */ + int iteration_counter = 0; + + time_t previous_iteration_timestamp = time( NULL ); + + /* Calculate Maximum allowed iterations per second (typically 20), which is double the expected iterations + * (typically 10) */ + expected_iterations = ( 1000 / GETCH_GUI_STATS_UPDATE_MS ) * 2; + + if( nwipe_time_start == 0 ) + { + /* This is the first time that we have been called. */ + nwipe_time_start = time( NULL ) - 1; + } + + nwipe_gui_title( footer_window, end_wipe_footer ); + + loop_control = 1; + + while( loop_control ) + { + /* IMPORTANT ! Halfdelay(1) causes getch() to pause for 0.1 secs. This is important for two reasons. + * 1. Pauses the getch for 0.1 secs so that the screen is only updated max 10 times/sec. Without + * this delay the loop would run hundreds of times per sec maxing out the core. + * 2. By keeping the delay below 0.2 seconds, i.e 0.1, it makes the keypress and resizing + * nice and responsive. + */ + halfdelay( GETCH_GUI_STATS_UPDATE_MS ); // Important, don't change this unless you know what you are doing ! + // Related to getch(). + + keystroke = getch(); // Get user input. + + iteration_counter++; + + /* Much like the same check we perform in the nwipe_gui_select() function, here we check that we are not looping + * any faster than as defined by the halfdelay() function above, typically this loop runs at 10 times a second. + * This check makes sure that if the loop runs faster than double this value i.e 20 times a second then the + * program exits. This check is therefore determining whether the getch() function is returning immediately + * rather than blocking for the defined period of 100ms. Why is this necessary? Some terminals (konsole & + * deriviatives) that are exited while nwipe is still running fail to terminate nwipe this causes the + * halfdelay()/getch() functions to immediately fail causing the loop frequency to drastically increase. We + * detect that speed increase here and therefore close down nwipe. This doesn't affect the use of the tmux + * terminal by which you can detach and reattach to running nwipe processes. tmux still works correctly. + */ + if( previous_iteration_timestamp == time( NULL ) ) + { + if( iteration_counter > expected_iterations ) + { + nwipe_log( NWIPE_LOG_ERROR, + "GUI.c,nwipe_gui_status(), loop runaway, did you close the terminal without exiting " + "nwipe? Initiating shutdown now." ); + /* Issue signal to nwipe to shutdown immediately but gracefully */ + terminate_signal = 1; + } + } + else + { + /* new second, so reset counter */ + iteration_counter = 0; + previous_iteration_timestamp = time( NULL ); + } + + /* Get the current time. */ + if( nwipe_active && terminate_signal != 1 ) + { + nwipe_time_now = time( NULL ); + nwipe_time_stopped = nwipe_time_now; + } + else + { + nwipe_time_now = nwipe_time_stopped; + } + + /* Erase the main window. */ + werase( main_window ); + + /* Erase the stats window. */ + werase( stats_window ); + + /* Erase the footer window */ + werase( footer_window ); + + /* Only repaint the windows on terminal resize if the user hasn't blanked the screen */ + if( nwipe_gui_blank == 0 ) + { + if( nwipe_active != 0 ) + { + /* if resizing the terminal during a wipe a specific footer is required */ + nwipe_gui_create_all_windows_on_terminal_resize( 0, end_wipe_footer ); + } + else + { + /* and if the wipes have finished a different footer is required */ + nwipe_gui_create_all_windows_on_terminal_resize( 0, wipes_finished_footer ); + } + } + + /* Initialize our working offset to the third line. */ + yy = 2; + + /* Get the window dimensions. */ + getmaxyx( main_window, wlines, wcols ); + + /* Less four lines for the box and padding. */ + slots = wlines - 4; + + /* Each element prints three lines. */ + slots /= 3; + + if( nwipe_active == 0 || terminate_signal == 1 ) + { + nwipe_gui_title( footer_window, wipes_finished_footer ); + + // Refresh the footer_window ; + wnoutrefresh( footer_window ); + } + + if( terminate_signal == 1 ) + { + loop_control = 0; + } + + if( keystroke > 0x0a && keystroke < 0x7e && nwipe_gui_blank == 1 ) + { + tft_saver = 0; + nwipe_init_pairs(); + nwipe_gui_create_all_windows_on_terminal_resize( 1, end_wipe_footer ); + + /* Show screen */ + nwipe_gui_blank = 0; + + /* Set background */ + wbkgdset( stdscr, COLOR_PAIR( 1 ) ); + wclear( stdscr ); + + /* Unhide panels */ + show_panel( header_panel ); + show_panel( footer_panel ); + show_panel( stats_panel ); + show_panel( options_panel ); + show_panel( main_panel ); + + /* Reprint the footer */ + nwipe_gui_title( footer_window, end_wipe_footer ); + + // Refresh the footer_window ; + wnoutrefresh( footer_window ); + + /* Update panels */ + update_panels(); + doupdate(); + } + else if( keystroke > 0 ) + { + + switch( keystroke ) + { + + case 'b': + case 'B': + + if( nwipe_gui_blank == 0 && tft_saver != 1 ) + { + /* grey text on black background */ + tft_saver = 1; + nwipe_init_pairs(); + nwipe_gui_create_all_windows_on_terminal_resize( 1, end_wipe_footer ); + } + else + { + if( nwipe_gui_blank == 0 && tft_saver == 1 ) + { + /* Blank screen. */ + tft_saver = 0; + nwipe_gui_blank = 1; + hide_panel( header_panel ); + hide_panel( footer_panel ); + hide_panel( stats_panel ); + hide_panel( options_panel ); + hide_panel( main_panel ); + } + + /* Set the background style. */ + wbkgdset( stdscr, COLOR_PAIR( 7 ) ); + wclear( stdscr ); + } + + break; + + case KEY_DOWN: + case 'j': + case 'J': + + /* Scroll down. */ + offset += 1; + + if( count < slots ) + { + offset = 0; + } + + else if( offset + slots > count ) + { + offset = count - slots; + } + + break; + + case KEY_UP: + case 'k': + case 'K': + + /* Scroll up. */ + offset -= 1; + + if( offset < 0 ) + { + offset = 0; + } + + break; + + case ' ': + case 0x0a: + + /* Check whether we have finished all wipes, if yes exit while loop if user pressed spacebar or + * return. */ + if( !nwipe_active || terminate_signal == 1 ) + { + loop_control = 0; + } + + break; + + default: + + /* Do nothing. */ + break; + } + + } /* keystroke */ + + /* If wipe has completed and user has specified auto poweroff or nowait then we can skip waiting for the user to + * press return */ + if( !nwipe_active ) + { + if( nwipe_options.autopoweroff || nwipe_options.nowait ) + { + loop_control = 0; + } + } + + /* Update data in statistics & main windows only if we're in 'gui' mode and only if a wipe has started */ + if( nwipe_gui_blank == 0 && global_wipe_status == 1 ) + { + + if( terminate_signal != 1 ) + { + nwipe_active = compute_stats( ptr ); // Returns number of active wipe threads + } + + /* Print information for the user. */ + for( i = offset; i < offset + slots && i < count; i++ ) + { + /* Print the device details. */ + mvwprintw( main_window, + yy++, + 2, + "%s %s [%s] ", + c[i]->device_name, + c[i]->device_type_str, + c[i]->device_size_text ); + wprintw_temperature( c[i] ); + wprintw( main_window, "%s/%s", c[i]->device_model, c[i]->device_serial_no ); + + /* Check whether the child process is still running the wipe. */ + if( c[i]->wipe_status == 1 ) + { + /* Print percentage and pass information. */ + mvwprintw( main_window, + yy++, + 4, + "[%5.2f%%, round %i of %i, pass %i of %i] ", + c[i]->round_percent, + c[i]->round_working, + c[i]->round_count, + c[i]->pass_working, + c[i]->pass_count ); + + } /* child running */ + else + { + if( c[i]->result == 0 ) + { + mvwprintw( main_window, yy++, 4, "[%05.2f%% complete, SUCCESS! ", c[i]->round_percent ); + } + else if( c[i]->signal ) + { + wattron( main_window, COLOR_PAIR( 9 ) ); + mvwprintw( main_window, yy++, 4, "(>>> FAILURE! <<<, signal %i) ", c[i]->signal ); + wattroff( main_window, COLOR_PAIR( 9 ) ); + } + else + { + wattron( main_window, COLOR_PAIR( 9 ) ); + mvwprintw( main_window, yy++, 4, "(>>>FAILURE!<<<, code %i) ", c[i]->result ); + wattroff( main_window, COLOR_PAIR( 9 ) ); + } + + } /* child returned */ + + if( c[i]->verify_errors ) + { + wprintw( main_window, "[verr:%llu] ", c[i]->verify_errors ); + } + if( c[i]->pass_errors ) + { + wprintw( main_window, "[perr:%llu] ", c[i]->pass_errors ); + } + if( c[i]->wipe_status == 1 ) + { + switch( c[i]->pass_type ) + { + /* Each text field in square brackets should be the same number of characters + * to retain output in columns */ + case NWIPE_PASS_FINAL_BLANK: + if( !c[i]->sync_status ) + { + wprintw( main_window, "[ blanking] " ); + } + break; + + case NWIPE_PASS_FINAL_OPS2: + if( !c[i]->sync_status ) + { + wprintw( main_window, "[OPS2final] " ); + } + break; + + case NWIPE_PASS_WRITE: + if( !c[i]->sync_status ) + { + wprintw( main_window, "[ writing ] " ); + } + break; + + case NWIPE_PASS_VERIFY: + if( !c[i]->sync_status ) + { + wprintw( main_window, "[verifying] " ); + } + break; + + case NWIPE_PASS_NONE: + break; + } + + if( c[i]->sync_status ) + { + wprintw( main_window, "[ syncing ] " ); + } + } + + /* Determine throughput nomenclature for this drive and output drives throughput to GUI */ + Determine_C_B_nomenclature( c[i]->throughput, nomenclature_result_str, NOMENCLATURE_RESULT_STR_SIZE ); + + wprintw( main_window, "[%s/s] ", nomenclature_result_str ); + + /* Insert whitespace. */ + yy += 1; + + /* Increment the next spinner character for this context if the thread is active */ + if( c[i]->wipe_status == 1 ) + { + spinner( c, i ); + spinner_string[0] = c[i]->spinner_character[0]; + } + else + { + /* If the wipe thread is no longer active, replace the spinner with a space */ + spinner_string[0] = ' '; + } + spinner_string[1] = 0; + wprintw( main_window, " %s ", spinner_string ); + } + + if( offset > 0 ) + { + mvwprintw( main_window, 1, wcols - 8, " More " ); + waddch( main_window, ACS_UARROW ); + } + + if( count - offset > slots ) + { + mvwprintw( main_window, wlines - 2, wcols - 8, " More " ); + waddch( main_window, ACS_DARROW ); + } + + /* Box the main window. */ + box( main_window, 0, 0 ); + + /* Refresh the main window. */ + wnoutrefresh( main_window ); + + /* Update the load average field, but only if we are still wiping */ + if( nwipe_active && terminate_signal != 1 ) + { + nwipe_gui_load(); + } + + nwipe_throughput = nwipe_misc_thread_data->throughput; + + /* Determine the nomenclature for the combined throughput */ + Determine_C_B_nomenclature( nwipe_throughput, nomenclature_result_str, NOMENCLATURE_RESULT_STR_SIZE ); + + /* Print the combined throughput. */ + mvwprintw( stats_window, NWIPE_GUI_STATS_THROUGHPUT_Y, NWIPE_GUI_STATS_THROUGHPUT_X, "Throughput:" ); + + mvwprintw( + stats_window, NWIPE_GUI_STATS_THROUGHPUT_Y, NWIPE_GUI_STATS_TAB, "%s/s", nomenclature_result_str ); + + /* Change the current time into a delta. */ + nwipe_time_now -= nwipe_time_start; + + /* Put the delta into HH:mm:ss form. */ + nwipe_hh = nwipe_time_now / 3600; + nwipe_time_now %= 3600; + nwipe_mm = nwipe_time_now / 60; + nwipe_time_now %= 60; + nwipe_ss = nwipe_time_now; + + /* Print the runtime. */ + mvwprintw( stats_window, NWIPE_GUI_STATS_RUNTIME_Y, 1, "Runtime:" ); + mvwprintw( stats_window, + NWIPE_GUI_STATS_RUNTIME_Y, + NWIPE_GUI_STATS_TAB, + "%02i:%02i:%02i", + nwipe_hh, + nwipe_mm, + nwipe_ss ); + + mvwprintw( stats_window, NWIPE_GUI_STATS_ETA_Y, 1, "Remaining:" ); + + time_t nwipe_maxeta = nwipe_misc_thread_data->maxeta; + if( nwipe_maxeta > 0 ) + { + /* Do it again for the estimated runtime remaining. */ + nwipe_hh = nwipe_maxeta / 3600; + nwipe_maxeta %= 3600; + nwipe_mm = nwipe_maxeta / 60; + nwipe_maxeta %= 60; + nwipe_ss = nwipe_maxeta; + + /* Print the estimated runtime remaining. */ + mvwprintw( stats_window, + NWIPE_GUI_STATS_ETA_Y, + NWIPE_GUI_STATS_TAB, + "%02i:%02i:%02i", + nwipe_hh, + nwipe_mm, + nwipe_ss ); + } + + /* Print the error count. */ + mvwprintw( stats_window, NWIPE_GUI_STATS_ERRORS_Y, NWIPE_GUI_STATS_ERRORS_X, "Errors:" ); + mvwprintw( + stats_window, NWIPE_GUI_STATS_ERRORS_Y, NWIPE_GUI_STATS_TAB, " %llu", nwipe_misc_thread_data->errors ); + + /* Add a border. */ + box( stats_window, 0, 0 ); + + /* Add a title. */ + mvwprintw( stats_window, 0, ( NWIPE_GUI_STATS_W - strlen( stats_title ) ) / 2, "%s", stats_title ); + + /* Refresh internal representation of stats window */ + wnoutrefresh( stats_window ); + + /* Output all windows to screen */ + doupdate(); + + } // end blank screen if + + } /* End of while loop */ + + if( nwipe_options.logfile[0] == '\0' ) + { + nwipe_gui_title( footer_window, wipes_finished_footer ); + } + else + { + char finish_message[NWIPE_GUI_FOOTER_W]; + snprintf( finish_message, + sizeof( finish_message ), + "Wipe finished - press enter to exit. Logged to %s", + nwipe_options.logfile ); + nwipe_gui_title( footer_window, finish_message ); + } + terminate_signal = 1; + + return NULL; +} /* nwipe_gui_status */ + +int compute_stats( void* ptr ) +{ + nwipe_thread_data_ptr_t* nwipe_thread_data_ptr; + nwipe_thread_data_ptr = (nwipe_thread_data_ptr_t*) ptr; + + nwipe_context_t** c; + nwipe_misc_thread_data_t* nwipe_misc_thread_data; + + c = nwipe_thread_data_ptr->c; + nwipe_misc_thread_data = nwipe_thread_data_ptr->nwipe_misc_thread_data; + int count = nwipe_misc_thread_data->nwipe_selected; + + int nwipe_active = 0; + int i; + + time_t nwipe_time_now = time( NULL ); + + nwipe_misc_thread_data->throughput = 0; + nwipe_misc_thread_data->maxeta = 0; + nwipe_misc_thread_data->errors = 0; + + /* Enumerate all contexts to compute statistics. */ + for( i = 0; i < count; i++ ) + { + /* Check whether the child process is still running the wipe. */ + if( c[i]->wipe_status == 1 ) + { + /* Increment the child counter. */ + nwipe_active += 1; + + /* Even if the wipe has finished ALWAYS run the stats one last time so the final SUCCESS percentage value is + * correct. Maintain a rolling average of throughput. */ + nwipe_update_speedring( &c[i]->speedring, c[i]->round_done, nwipe_time_now ); + + if( c[i]->speedring.timestotal > 0 && c[i]->wipe_status == 1 ) + { + /* Update the current average throughput in bytes-per-second. */ + c[i]->throughput = c[i]->speedring.bytestotal / c[i]->speedring.timestotal; + + /* Only update the estimated remaining runtime if the + * throughput for a given drive is greater than 100,000 bytes per second + * This prevents enormous ETA's being calculated on an unresponsive + * drive */ + if( c[i]->throughput > 100000 ) + { + c[i]->eta = ( c[i]->round_size - c[i]->round_done ) / c[i]->throughput; + + if( c[i]->eta > nwipe_misc_thread_data->maxeta ) + { + nwipe_misc_thread_data->maxeta = c[i]->eta; + } + } + } + + /* Calculate the average throughput */ + c[i]->throughput = (double) c[i]->round_done / (double) difftime( nwipe_time_now, c[i]->start_time ); + } + + /* Update the percentage value. */ + c[i]->round_percent = (double) c[i]->round_done / (double) c[i]->round_size * 100; + + if( c[i]->wipe_status == 1 ) + { + /* Accumulate combined throughput. */ + nwipe_misc_thread_data->throughput += c[i]->throughput; + } + + /* Accumulate the error count. */ + nwipe_misc_thread_data->errors += c[i]->pass_errors; + nwipe_misc_thread_data->errors += c[i]->verify_errors; + nwipe_misc_thread_data->errors += c[i]->fsyncdata_errors; + + /* Read the drive temperature values */ + if( nwipe_time_now > ( c[i]->temp1_time + 60 ) ) + { + nwipe_update_temperature( c[i] ); + } + + } /* for statistics */ + + return nwipe_active; +} + +void nwipe_update_speedring( nwipe_speedring_t* speedring, u64 speedring_bytes, time_t speedring_now ) +{ + + if( speedring->timeslast == 0 ) + { + /* Ignore the first sample and initialize. */ + speedring->timeslast = speedring_now; + return; + } + + if( speedring_now - speedring->timeslast < NWIPE_KNOB_SPEEDRING_GRANULARITY ) + { + /* Avoid jitter caused by frequent updates. */ + return; + } + + /* Subtract the oldest speed sample from the accumulator. */ + speedring->bytestotal -= speedring->bytes[speedring->position]; + speedring->timestotal -= speedring->times[speedring->position]; + + /* Put the latest bytes-per-second sample into the ring buffer. */ + speedring->bytes[speedring->position] = speedring_bytes - speedring->byteslast; + speedring->times[speedring->position] = speedring_now - speedring->timeslast; + + /* Add the newest speed sample to the accumulator. */ + speedring->bytestotal += speedring->bytes[speedring->position]; + speedring->timestotal += speedring->times[speedring->position]; + + /* Remember the last sample. */ + speedring->byteslast = speedring_bytes; + speedring->timeslast = speedring_now; + + if( ++speedring->position >= NWIPE_KNOB_SPEEDRING_SIZE ) + { + speedring->position = 0; + } +} + +int spinner( nwipe_context_t** ptr, int device_idx ) +{ + nwipe_context_t** c; + + c = ptr; + + /* The spinner characters |/-\|/-\ */ + char sc[9] = "|/-\\|/-\\/"; + + /* Check sanity of index */ + if( c[device_idx]->spinner_idx < 0 || c[device_idx]->spinner_idx > 7 ) + { + return 1; + } + + c[device_idx]->spinner_character[0] = sc[c[device_idx]->spinner_idx]; + + c[device_idx]->spinner_idx++; + + if( c[device_idx]->spinner_idx > 7 ) + { + c[device_idx]->spinner_idx = 0; + } + + return 0; +} + +void temp1_flash( nwipe_context_t* c ) +{ + if( c->temp1_flash_rate_counter < c->temp1_flash_rate ) + { + c->temp1_flash_rate_counter++; + } + else + { + c->temp1_flash_rate_counter = 0; + if( c->temp1_flash_rate_status == 0 ) + { + c->temp1_flash_rate_status = 1; + } + else + { + c->temp1_flash_rate_status = 0; + } + } +} + +void wprintw_temperature( nwipe_context_t* c ) +{ + /* If available, print the current temperature. This function determines + * whether the temperature should be printed as white, solid red, flashing red + * solid black or flashing black and then prints it to the main_window. + */ + if( c->temp1_input != 1000000 ) + { + /* if drive temperature has exceeded critical continuous running + * temperature && critical value is valid + */ + if( c->temp1_input >= c->temp1_crit && c->temp1_crit != 1000000 ) + { + temp1_flash( c ); + + if( c->temp1_flash_rate_status == 0 ) + { + /* blue on blue */ + wattron( main_window, COLOR_PAIR( 12 ) ); + wprintw( main_window, "[%dC] ", c->temp1_input ); + wattroff( main_window, COLOR_PAIR( 12 ) ); + } + else + { + /* red on blue */ + wattron( main_window, COLOR_PAIR( 3 ) ); + wprintw( main_window, "[%dC] ", c->temp1_input ); + wattroff( main_window, COLOR_PAIR( 3 ) ); + } + } + else + { + /* if drive temperature has exceeded maximum continuous running + * temperature && max value is valid + */ + if( c->temp1_input >= c->temp1_max && c->temp1_max != 1000000 ) + { + /* red on blue */ + wattron( main_window, COLOR_PAIR( 3 ) ); + wprintw( main_window, "[%dC] ", c->temp1_input ); + wattroff( main_window, COLOR_PAIR( 3 ) ); + } + else + { + /* if drive temperature is below the critical temperature && critical value is valid */ + if( c->temp1_input <= c->temp1_lcrit && c->temp1_lcrit != 1000000 ) + { + temp1_flash( c ); + if( c->temp1_flash_rate_status == 0 ) + { + /* blue on blue */ + wattron( main_window, COLOR_PAIR( 12 ) ); + wprintw( main_window, "[%dC] ", c->temp1_input ); + wattroff( main_window, COLOR_PAIR( 12 ) ); + } + else + { + /* black on blue */ + wattron( main_window, COLOR_PAIR( 11 ) ); + wprintw( main_window, "[%dC] ", c->temp1_input ); + wattroff( main_window, COLOR_PAIR( 11 ) ); + } + } + else + { + /* if drive temperature is below the minimum continuous running temperature && minimum + * value is valid */ + if( c->temp1_input <= c->temp1_min && c->temp1_min != 1000000 ) + { + /* black on blue */ + wattron( main_window, COLOR_PAIR( 11 ) ); + wprintw( main_window, "[%dC] ", c->temp1_input ); + wattroff( main_window, COLOR_PAIR( 11 ) ); + } + else + { + /* Default white on blue */ + wprintw( main_window, "[%dC] ", c->temp1_input ); + } + } + } + } + } + else + { + wprintw( main_window, "[--C] " ); + } +} diff --git a/src/gui.h b/src/gui.h new file mode 100644 index 0000000..29d00e6 --- /dev/null +++ b/src/gui.h @@ -0,0 +1,62 @@ +/* + * gui.h: An ncurses GUI for nwipe. + * + * Copyright Darik Horn <dajhorn-dban@vanadac.com>. + * + * Modifications to original dwipe Copyright Andy Beverley <andy@andybev.com> + * + * 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, version 2. + * + * 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. + * + */ + +#ifndef GUI_H_ +#define GUI_H_ + +void nwipe_gui_free( void ); // Stop the GUI. +void nwipe_gui_init( void ); // Start the GUI. +void nwipe_gui_create_main_window( void ); // Create the main window +void nwipe_gui_create_header_window( void ); // Create the header window +void nwipe_gui_create_footer_window( const char* ); // Create the footer window and write text +void nwipe_gui_create_options_window( void ); // Create the options window +void nwipe_gui_create_stats_window( void ); // Create the stats window +void nwipe_gui_create_all_windows_on_terminal_resize( + int force_creation, + const char* footer_text ); // If terminal is resized recreate all windows +void nwipe_gui_select( int count, nwipe_context_t** c ); // Select devices to wipe. +void* nwipe_gui_status( void* ptr ); // Update operation progress. +void nwipe_gui_method( void ); // Change the method option. +void nwipe_gui_options( void ); // Update the options window. +void nwipe_gui_prng( void ); // Change the prng option. +void nwipe_gui_rounds( void ); // Change the rounds option. +void nwipe_gui_verify( void ); // Change the verify option. +void nwipe_gui_noblank( void ); // Change the noblank option. +int spinner( nwipe_context_t** ptr, int ); // Return the next spinner character +void temp1_flash( nwipe_context_t* ); // toggles term1_flash_status, which flashes the temperature +void wprintw_temperature( nwipe_context_t* ); + +int compute_stats( void* ptr ); +void nwipe_update_speedring( nwipe_speedring_t* speedring, u64 speedring_done, time_t speedring_now ); + +#define NOMENCLATURE_RESULT_STR_SIZE 8 + +/* Note Do not change unless you understand how this value affects keyboard response and screen refresh when + * the drive selection screen is displayed. (prior to wipe starting). */ +#define GETCH_BLOCK_MS 250 /* millisecond block time for getch() */ + +/* Note The value of 1 (100ms) is the ideal speed for screen refresh during a wipe, a value of 2 is noticably slower, + * don't change unless you understand how this value affects keyboard responsiveness and speed of screen stats/spinner + * updating */ +#define GETCH_GUI_STATS_UPDATE_MS 1 /* 1 * 100 = 1/10/sec = millisecond block time for gui stats screen updates */ + +#endif /* GUI_H_ */ diff --git a/src/isaac_rand/isaac64.c b/src/isaac_rand/isaac64.c new file mode 100644 index 0000000..3e4203b --- /dev/null +++ b/src/isaac_rand/isaac64.c @@ -0,0 +1,119 @@ +/* +------------------------------------------------------------------------------ +isaac64.c: My random number generator for 64-bit machines. +By Bob Jenkins, 1996. Public Domain. +------------------------------------------------------------------------------ +*/ +#ifndef STANDARD +#include "isaac_standard.h" +#endif +#ifndef ISAAC64 +#include "isaac64.h" +#endif + + +#define ind(mm,x) (*(ub8 *)((ub1 *)(mm) + ((x) & ((RANDSIZ-1)<<3)))) +#define rngstep(mix,a,b,mm,m,m2,r,x) \ +{ \ + x = *m; \ + a = (mix) + *(m2++); \ + *(m++) = y = ind(mm,x) + a + b; \ + *(r++) = b = ind(mm,y>>RANDSIZL) + x; \ +} + +void isaac64(rand64ctx *ctx) +{ + register ub8 a,b,x,y,*m,*mm,*m2,*r,*mend; + mm=ctx->mm; r=ctx->randrsl; + a = ctx->aa; b = ctx->bb + (++ctx->cc); + for (m = mm, mend = m2 = m+(RANDSIZ/2); m<mend; ) + { + rngstep(~(a^(a<<21)), a, b, mm, m, m2, r, x); + rngstep( a^(a>>5) , a, b, mm, m, m2, r, x); + rngstep( a^(a<<12) , a, b, mm, m, m2, r, x); + rngstep( a^(a>>33) , a, b, mm, m, m2, r, x); + } + for (m2 = mm; m2<mend; ) + { + rngstep(~(a^(a<<21)), a, b, mm, m, m2, r, x); + rngstep( a^(a>>5) , a, b, mm, m, m2, r, x); + rngstep( a^(a<<12) , a, b, mm, m, m2, r, x); + rngstep( a^(a>>33) , a, b, mm, m, m2, r, x); + } + ctx->bb = b; ctx->aa = a; +} + +#define mix(a,b,c,d,e,f,g,h) \ +{ \ + a-=e; f^=h>>9; h+=a; \ + b-=f; g^=a<<9; a+=b; \ + c-=g; h^=b>>23; b+=c; \ + d-=h; a^=c<<15; c+=d; \ + e-=a; b^=d>>14; d+=e; \ + f-=b; c^=e<<20; e+=f; \ + g-=c; d^=f>>17; f+=g; \ + h-=d; e^=g<<14; g+=h; \ +} + +void rand64init(rand64ctx *ctx, word flag) +{ + word i; + ub8 a,b,c,d,e,f,g,h; + ub8 *mm, *randrsl; + ctx->aa = ctx->bb = ctx->cc = (ub8)0; + mm=ctx->mm; + randrsl=ctx->randrsl; + a=b=c=d=e=f=g=h=0x9e3779b97f4a7c13LL; /* the golden ratio */ + + for (i=0; i<4; ++i) /* scramble it */ + { + mix(a,b,c,d,e,f,g,h); + } + + for (i=0; i<RANDSIZ; i+=8) /* fill in mm[] with messy stuff */ + { + if (flag) /* use all the information in the seed */ + { + a+=randrsl[i ]; b+=randrsl[i+1]; c+=randrsl[i+2]; d+=randrsl[i+3]; + e+=randrsl[i+4]; f+=randrsl[i+5]; g+=randrsl[i+6]; h+=randrsl[i+7]; + } + mix(a,b,c,d,e,f,g,h); + mm[i ]=a; mm[i+1]=b; mm[i+2]=c; mm[i+3]=d; + mm[i+4]=e; mm[i+5]=f; mm[i+6]=g; mm[i+7]=h; + } + + if (flag) + { /* do a second pass to make all of the seed affect all of mm */ + for (i=0; i<RANDSIZ; i+=8) + { + a+=mm[i ]; b+=mm[i+1]; c+=mm[i+2]; d+=mm[i+3]; + e+=mm[i+4]; f+=mm[i+5]; g+=mm[i+6]; h+=mm[i+7]; + mix(a,b,c,d,e,f,g,h); + mm[i ]=a; mm[i+1]=b; mm[i+2]=c; mm[i+3]=d; + mm[i+4]=e; mm[i+5]=f; mm[i+6]=g; mm[i+7]=h; + } + } + + isaac64(ctx); /* fill in the first set of results */ + ctx->randcnt=RANDSIZ; /* prepare to use the first set of results */ +} + +#ifdef NEVER +int main() +{ + ub8 i,j; + rand64ctx ctx; + ctx.aa=ctx.bb=ctx.cc=(ub8)0; + for (i=0; i<RANDSIZ; ++i) ctx.mm[i]=(ub8)0; + rand64init(&ctx, TRUE); + for (i=0; i<2; ++i) + { + isaac64(&ctx); + for (j=0; j<RANDSIZ; ++j) + { + printf("%.8lx%.8lx",(ub4)(ctx.randrsl[j]>>32),(ub4)ctx.randrsl[j]); + if ((j&3)==3) printf("\n"); + } + } +} +#endif diff --git a/src/isaac_rand/isaac64.h b/src/isaac_rand/isaac64.h new file mode 100644 index 0000000..e2ab55b --- /dev/null +++ b/src/isaac_rand/isaac64.h @@ -0,0 +1,41 @@ +/* +------------------------------------------------------------------------------ +isaac64.h: definitions for a random number generator +Bob Jenkins, 1996, Public Domain +------------------------------------------------------------------------------ +*/ +#ifndef ISAAC64 +#define ISAAC64 + +#include "isaac_standard.h" + +struct rand64ctx +{ + ub8 randrsl[RANDSIZ], randcnt; + ub8 mm[RANDSIZ]; + ub8 aa, bb, cc; +}; +typedef struct rand64ctx rand64ctx; + +/* +------------------------------------------------------------------------------ + If (flag==TRUE), then use the contents of randrsl[0..255] as the seed. +------------------------------------------------------------------------------ +*/ +void rand64init(rand64ctx *r, word flag); + +void isaac64(rand64ctx *ctx); + + +/* +------------------------------------------------------------------------------ + Call rand64() to retrieve a single 64-bit random value +------------------------------------------------------------------------------ +*/ +#define isaac64_rand() \ + (!(r)->randcnt-- ? \ + (isaac64(r), (r)->randcnt=RANDSIZ-1, (r)->randrsl[(r)->>randcnt]) : \ + (r)->randrsl[(r)->randcnt]) + +#endif /* ISAAC64 */ + diff --git a/src/isaac_rand/isaac_rand.c b/src/isaac_rand/isaac_rand.c new file mode 100644 index 0000000..c9220e0 --- /dev/null +++ b/src/isaac_rand/isaac_rand.c @@ -0,0 +1,169 @@ +/* +------------------------------------------------------------------------------ +rand.c: By Bob Jenkins. My random number generator, ISAAC. Public Domain. +MODIFIED: + 960327: Creation (addition of randinit, really) + 970719: use context, not global variables, for internal state + 980324: added main (ifdef'ed out), also rearranged randinit() + 010626: Note that this is public domain +------------------------------------------------------------------------------ +*/ +#ifndef STANDARD +#include "isaac_standard.h" +#endif +#ifndef RAND +#include "isaac_rand.h" +#endif + + +#define ind(mm,x) (*(ub4 *)((ub1 *)(mm) + ((x) & ((RANDSIZ-1)<<2)))) +#define rngstep(mix,a,b,mm,m,m2,r,x) \ +{ \ + x = *m; \ + a = (a^(mix)) + *(m2++); \ + *(m++) = y = ind(mm,x) + a + b; \ + *(r++) = b = ind(mm,y>>RANDSIZL) + x; \ +} + +void isaac(ctx) +randctx *ctx; +{ + register ub4 a,b,x,y,*m,*mm,*m2,*r,*mend; + mm=ctx->randmem; + r=ctx->randrsl; + a = ctx->randa; + b = ctx->randb + (++ctx->randc); + for (m = mm, mend = m2 = m+(RANDSIZ/2); m<mend; ) + { + rngstep( a<<13, a, b, mm, m, m2, r, x); + rngstep( a>>6 , a, b, mm, m, m2, r, x); + rngstep( a<<2 , a, b, mm, m, m2, r, x); + rngstep( a>>16, a, b, mm, m, m2, r, x); + } + for (m2 = mm; m2<mend; ) + { + rngstep( a<<13, a, b, mm, m, m2, r, x); + rngstep( a>>6 , a, b, mm, m, m2, r, x); + rngstep( a<<2 , a, b, mm, m, m2, r, x); + rngstep( a>>16, a, b, mm, m, m2, r, x); + } + ctx->randb = b; ctx->randa = a; +} + + +#define mix(a,b,c,d,e,f,g,h) \ +{ \ + a^=b<<11; d+=a; b+=c; \ + b^=c>>2; e+=b; c+=d; \ + c^=d<<8; f+=c; d+=e; \ + d^=e>>16; g+=d; e+=f; \ + e^=f<<10; h+=e; f+=g; \ + f^=g>>4; a+=f; g+=h; \ + g^=h<<8; b+=g; h+=a; \ + h^=a>>9; c+=h; a+=b; \ +} + +/* if (flag==TRUE), then use the contents of randrsl[] to initialize mm[]. */ +void randinit(ctx, flag) +randctx *ctx; +word flag; +{ + word i; + ub4 a,b,c,d,e,f,g,h; + ub4 *m,*r; + ctx->randa = ctx->randb = ctx->randc = 0; + m=ctx->randmem; + r=ctx->randrsl; + a=b=c=d=e=f=g=h=0x9e3779b9; /* the golden ratio */ + + for (i=0; i<4; ++i) /* scramble it */ + { + mix(a,b,c,d,e,f,g,h); + } + + if (flag) + { + /* initialize using the contents of r[] as the seed */ + for (i=0; i<RANDSIZ; i+=8) + { + a+=r[i ]; + b+=r[i+1]; + c+=r[i+2]; + d+=r[i+3]; + e+=r[i+4]; + f+=r[i+5]; + g+=r[i+6]; + h+=r[i+7]; + mix(a,b,c,d,e,f,g,h); + m[i ]=a; + m[i+1]=b; + m[i+2]=c; + m[i+3]=d; + m[i+4]=e; + m[i+5]=f; + m[i+6]=g; + m[i+7]=h; + } + /* do a second pass to make all of the seed affect all of m */ + for (i=0; i<RANDSIZ; i+=8) + { + a+=m[i ]; + b+=m[i+1]; + c+=m[i+2]; + d+=m[i+3]; + e+=m[i+4]; + f+=m[i+5]; + g+=m[i+6]; + h+=m[i+7]; + mix(a,b,c,d,e,f,g,h); + m[i ]=a; + m[i+1]=b; + m[i+2]=c; + m[i+3]=d; + m[i+4]=e; + m[i+5]=f; + m[i+6]=g; + m[i+7]=h; + } + } + else + { + /* fill in m[] with messy stuff */ + for (i=0; i<RANDSIZ; i+=8) + { + mix(a,b,c,d,e,f,g,h); + m[i ]=a; + m[i+1]=b; + m[i+2]=c; + m[i+3]=d; + m[i+4]=e; + m[i+5]=f; + m[i+6]=g; + m[i+7]=h; + } + } + + isaac(ctx); /* fill in the first set of results */ + ctx->randcnt=RANDSIZ; /* prepare to use the first set of results */ +} + + +#ifdef NEVER +int main() +{ + ub4 i,j; + randctx ctx; + ctx.randa=ctx.randb=ctx.randc=(ub4)0; + for (i=0; i<256; ++i) ctx.randrsl[i]=(ub4)0; + randinit(&ctx, TRUE); + for (i=0; i<2; ++i) + { + isaac(&ctx); + for (j=0; j<256; ++j) + { + printf("%.8lx",ctx.randrsl[j]); + if ((j&7)==7) printf("\n"); + } + } +} +#endif diff --git a/src/isaac_rand/isaac_rand.h b/src/isaac_rand/isaac_rand.h new file mode 100644 index 0000000..167a2fd --- /dev/null +++ b/src/isaac_rand/isaac_rand.h @@ -0,0 +1,52 @@ +/* +------------------------------------------------------------------------------ +rand.h: definitions for a random number generator +By Bob Jenkins, 1996, Public Domain +MODIFIED: + 960327: Creation (addition of randinit, really) + 970719: use context, not global variables, for internal state + 980324: renamed seed to flag + 980605: recommend RANDSIZL=4 for noncryptography. + 010626: note this is public domain +------------------------------------------------------------------------------ +*/ +#ifndef RAND +#define RAND + +#include "isaac_standard.h" + +/* context of random number generator */ +struct randctx +{ + ub4 randcnt; + ub4 randrsl[RANDSIZ]; + ub4 randmem[RANDSIZ]; + ub4 randa; + ub4 randb; + ub4 randc; +}; +typedef struct randctx randctx; + +/* +------------------------------------------------------------------------------ + If (flag==TRUE), then use the contents of randrsl[0..RANDSIZ-1] as the seed. +------------------------------------------------------------------------------ +*/ +void randinit(/*_ randctx *r, word flag _*/); + +void isaac(/*_ randctx *r _*/); + + +/* +------------------------------------------------------------------------------ + Call rand(/o_ randctx *r _o/) to retrieve a single 32-bit random value +------------------------------------------------------------------------------ +*/ +#define isaac_rand(r) \ + (!(r)->randcnt-- ? \ + (isaac(r), (r)->randcnt=RANDSIZ-1, (r)->randrsl[(r)->randcnt]) : \ + (r)->randrsl[(r)->randcnt]) + +#endif /* RAND */ + + diff --git a/src/isaac_rand/isaac_standard.h b/src/isaac_rand/isaac_standard.h new file mode 100644 index 0000000..5323d3f --- /dev/null +++ b/src/isaac_rand/isaac_standard.h @@ -0,0 +1,60 @@ +/* +------------------------------------------------------------------------------ +Standard definitions and types, Bob Jenkins +------------------------------------------------------------------------------ +*/ +#ifndef STANDARD +# define STANDARD +# ifndef STDIO +# include <stdio.h> +# define STDIO +# endif +# ifndef STDDEF +# include <stddef.h> +# define STDDEF +# endif +typedef unsigned long long ub8; +#define UB8MAXVAL 0xffffffffffffffffLL +#define UB8BITS 64 +typedef signed long long sb8; +#define SB8MAXVAL 0x7fffffffffffffffLL +typedef unsigned long int ub4; /* unsigned 4-byte quantities */ +#define UB4MAXVAL 0xffffffff +typedef signed long int sb4; +#define UB4BITS 32 +#define SB4MAXVAL 0x7fffffff +typedef unsigned short int ub2; +#define UB2MAXVAL 0xffff +#define UB2BITS 16 +typedef signed short int sb2; +#define SB2MAXVAL 0x7fff +typedef unsigned char ub1; +#define UB1MAXVAL 0xff +#define UB1BITS 8 +typedef signed char sb1; /* signed 1-byte quantities */ +#define SB1MAXVAL 0x7f +typedef int word; /* fastest type available */ + +#define bis(target,mask) ((target) |= (mask)) +#define bic(target,mask) ((target) &= ~(mask)) +#define bit(target,mask) ((target) & (mask)) +#ifndef min +# define min(a,b) (((a)<(b)) ? (a) : (b)) +#endif /* min */ +#ifndef max +# define max(a,b) (((a)<(b)) ? (b) : (a)) +#endif /* max */ +#ifndef align +# define align(a) (((ub4)a+(sizeof(void *)-1))&(~(sizeof(void *)-1))) +#endif /* align */ +#ifndef abs +# define abs(a) (((a)>0) ? (a) : -(a)) +#endif +#define TRUE 1 +#define FALSE 0 +#define SUCCESS 0 /* 1 on VAX */ + +#define RANDSIZL (8) /* I recommend 8 for crypto, 4 for simulations */ +#define RANDSIZ (1<<RANDSIZL) + +#endif /* STANDARD */ diff --git a/src/logging.c b/src/logging.c new file mode 100644 index 0000000..1ff3ed1 --- /dev/null +++ b/src/logging.c @@ -0,0 +1,1007 @@ +/* + * logging.c: Logging facilities for nwipe. + * + * Copyright Darik Horn <dajhorn-dban@vanadac.com>. + * + * 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, version 2. + * + * 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. + * + */ + +#ifndef _DEFAULT_SOURCE +#define _DEFAULT_SOURCE +#endif + +#ifndef _POSIX_SOURCE +#define _POSIX_SOURCE +#endif + +#include "stdio.h" +#include "stdlib.h" +#include "string.h" +#include "stdarg.h" +#include "nwipe.h" +#include "context.h" +#include "method.h" +#include "prng.h" +#include "options.h" +#include "logging.h" + +/* Global array to hold log values to print when logging to STDOUT */ +char** log_lines; +int log_current_element = 0; +int log_elements_allocated = 0; +int log_elements_displayed = 0; +pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; + +void nwipe_log( nwipe_log_t level, const char* format, ... ) +{ + /** + * Writes a message to the program log file. + * + */ + + extern int terminate_signal; + extern int user_abort; + + char** result; + char* malloc_result; + char message_buffer[MAX_LOG_LINE_CHARS * sizeof( char )]; + int chars_written; + + int message_buffer_length; + int r; /* result buffer */ + + /* A time buffer. */ + time_t t; + + /* A pointer to the system time struct. */ + struct tm* p; + r = pthread_mutex_lock( &mutex1 ); + if( r != 0 ) + { + fprintf( stderr, "nwipe_log: pthread_mutex_lock failed. Code %i \n", r ); + return; + } + + /* Get the current time. */ + t = time( NULL ); + p = localtime( &t ); + + /* Position of writing to current log string */ + int line_current_pos = 0; + + /* initialise characters written */ + chars_written = 0; + + /* Print the date. The rc script uses the same format. */ + if( level != NWIPE_LOG_NOTIMESTAMP ) + { + chars_written = snprintf( message_buffer, + MAX_LOG_LINE_CHARS, + "[%i/%02i/%02i %02i:%02i:%02i] ", + 1900 + p->tm_year, + 1 + p->tm_mon, + p->tm_mday, + p->tm_hour, + p->tm_min, + p->tm_sec ); + } + + /* + * Has the end of the buffer been reached ?, snprintf returns the number of characters that would have been + * written if MAX_LOG_LINE_CHARS had not been reached, it does not return the actual characters written in + * all circumstances, hence why we need to check whether it's greater than MAX_LOG_LINE_CHARS and if so set + * it to MAX_LOG_LINE_CHARS, preventing a buffer overrun further down this function. + */ + + /* check if there was a complete failure to write this part of the message, in which case return */ + if( chars_written < 0 ) + { + fprintf( stderr, "nwipe_log: snprintf error when writing log line to memory.\n" ); + r = pthread_mutex_unlock( &mutex1 ); + if( r != 0 ) + { + fprintf( stderr, "nwipe_log: pthread_mutex_unlock failed. Code %i \n", r ); + return; + } + } + else + { + if( ( line_current_pos + chars_written ) > MAX_LOG_LINE_CHARS ) + { + fprintf( stderr, + "nwipe_log: Warning! The log line has been truncated as it exceeded %i characters\n", + MAX_LOG_LINE_CHARS ); + line_current_pos = MAX_LOG_LINE_CHARS; + } + else + { + line_current_pos += chars_written; + } + } + + if( line_current_pos < MAX_LOG_LINE_CHARS ) + { + switch( level ) + { + + case NWIPE_LOG_NONE: + case NWIPE_LOG_NOTIMESTAMP: + /* Do nothing. */ + break; + + /* NOTE! The debug labels, i.e. debug, info, notice etc should be left padded with spaces, in order + * to maintain column alignment. Pad a label to achieve the length of whatever the longest label happens + * to be. Important to know if you are thinking of adding another label. + */ + + case NWIPE_LOG_DEBUG: + chars_written = + snprintf( message_buffer + line_current_pos, MAX_LOG_LINE_CHARS - line_current_pos, " debug: " ); + break; + + case NWIPE_LOG_INFO: + chars_written = + snprintf( message_buffer + line_current_pos, MAX_LOG_LINE_CHARS - line_current_pos, " info: " ); + break; + + case NWIPE_LOG_NOTICE: + chars_written = + snprintf( message_buffer + line_current_pos, MAX_LOG_LINE_CHARS - line_current_pos, " notice: " ); + break; + + case NWIPE_LOG_WARNING: + chars_written = + snprintf( message_buffer + line_current_pos, MAX_LOG_LINE_CHARS - line_current_pos, "warning: " ); + break; + + case NWIPE_LOG_ERROR: + chars_written = + snprintf( message_buffer + line_current_pos, MAX_LOG_LINE_CHARS - line_current_pos, " error: " ); + break; + + case NWIPE_LOG_FATAL: + chars_written = + snprintf( message_buffer + line_current_pos, MAX_LOG_LINE_CHARS - line_current_pos, " fatal: " ); + break; + + case NWIPE_LOG_SANITY: + /* TODO: Request that the user report the log. */ + chars_written = + snprintf( message_buffer + line_current_pos, MAX_LOG_LINE_CHARS - line_current_pos, " sanity: " ); + break; + + default: + chars_written = snprintf( + message_buffer + line_current_pos, MAX_LOG_LINE_CHARS - line_current_pos, "level %i: ", level ); + } + + /* + * Has the end of the buffer been reached ? + */ + if( chars_written < 0 ) + { + fprintf( stderr, "nwipe_log: snprintf error when writing log line to memory.\n" ); + r = pthread_mutex_unlock( &mutex1 ); + if( r != 0 ) + { + fprintf( stderr, "nwipe_log: pthread_mutex_unlock failed. Code %i \n", r ); + return; + } + } + else + { + if( ( line_current_pos + chars_written ) > MAX_LOG_LINE_CHARS ) + { + fprintf( stderr, + "nwipe_log: Warning! The log line has been truncated as it exceeded %i characters\n", + MAX_LOG_LINE_CHARS ); + line_current_pos = MAX_LOG_LINE_CHARS; + } + else + { + line_current_pos += chars_written; + } + } + } + + /* The variable argument pointer. */ + va_list ap; + + /* Fetch the argument list. */ + va_start( ap, format ); + + /* Print the event. */ + if( line_current_pos < MAX_LOG_LINE_CHARS ) + { + chars_written = + vsnprintf( message_buffer + line_current_pos, MAX_LOG_LINE_CHARS - line_current_pos - 1, format, ap ); + + if( chars_written < 0 ) + { + fprintf( stderr, "nwipe_log: snprintf error when writing log line to memory.\n" ); + r = pthread_mutex_unlock( &mutex1 ); + if( r != 0 ) + { + fprintf( stderr, "nwipe_log: pthread_mutex_unlock failed. Code %i \n", r ); + va_end( ap ); + return; + } + } + else + { + if( ( line_current_pos + chars_written ) > MAX_LOG_LINE_CHARS ) + { + fprintf( stderr, + "nwipe_log: Warning! The log line has been truncated as it exceeded %i characters\n", + MAX_LOG_LINE_CHARS ); + line_current_pos = MAX_LOG_LINE_CHARS; + } + else + { + line_current_pos += chars_written; + } + } + } + + fflush( stdout ); + /* Increase the current log element pointer - we will write here, deallocation is done in cleanup() in nwipe.c */ + if( log_current_element == log_elements_allocated ) + { + log_elements_allocated++; + result = realloc( log_lines, ( log_elements_allocated ) * sizeof( char* ) ); + if( result == NULL ) + { + fprintf( stderr, "nwipe_log: realloc failed when adding a log line.\n" ); + r = pthread_mutex_unlock( &mutex1 ); + if( r != 0 ) + { + fprintf( stderr, "nwipe_log: pthread_mutex_unlock failed. Code %i \n", r ); + va_end( ap ); + return; + } + } + log_lines = result; + + /* Allocate memory for storing a single log message, deallocation is done in cleanup() in nwipe.c */ + message_buffer_length = strlen( message_buffer ) * sizeof( char ); + malloc_result = malloc( ( message_buffer_length + 1 ) * sizeof( char ) ); + if( malloc_result == NULL ) + { + fprintf( stderr, "nwipe_log: malloc failed when adding a log line.\n" ); + r = pthread_mutex_unlock( &mutex1 ); + if( r != 0 ) + { + fprintf( stderr, "nwipe_log: pthread_mutex_unlock failed. Code %i \n", r ); + va_end( ap ); + return; + } + } + log_lines[log_current_element] = malloc_result; + } + + strcpy( log_lines[log_current_element], message_buffer ); + + /* + if( level >= NWIPE_LOG_WARNING ) + { + vfprintf( stderr, format, ap ); + } + */ + + /* Release the argument list. */ + va_end( ap ); + + /* + if( level >= NWIPE_LOG_WARNING ) + { + fprintf( stderr, "\n" ); + } + */ + + /* The log file pointer. */ + FILE* fp; + + /* The log file descriptor. */ + int fd; + + if( nwipe_options.logfile[0] == '\0' ) + { + if( nwipe_options.nogui ) + { + printf( "%s\n", log_lines[log_current_element] ); + log_elements_displayed++; + } + } + else + { + /* Open the log file for appending. */ + fp = fopen( nwipe_options.logfile, "a" ); + + if( fp != NULL ) + { + + /* Get the file descriptor of the log file. */ + fd = fileno( fp ); + + /* Block and lock. */ + r = flock( fd, LOCK_EX ); + + if( r != 0 ) + { + perror( "nwipe_log: flock:" ); + fprintf( stderr, "nwipe_log: Unable to lock '%s' for logging.\n", nwipe_options.logfile ); + r = pthread_mutex_unlock( &mutex1 ); + if( r != 0 ) + { + fprintf( stderr, "nwipe_log: pthread_mutex_unlock failed. Code %i \n", r ); + + /* Unlock the file. */ + r = flock( fd, LOCK_UN ); + fclose( fp ); + return; + } + } + + fprintf( fp, "%s\n", log_lines[log_current_element] ); + + /* Unlock the file. */ + r = flock( fd, LOCK_UN ); + + if( r != 0 ) + { + perror( "nwipe_log: flock:" ); + fprintf( stderr, "Error: Unable to unlock '%s' after logging.\n", nwipe_options.logfile ); + } + + /* Close the stream. */ + r = fclose( fp ); + + if( r != 0 ) + { + perror( "nwipe_log: fclose:" ); + fprintf( stderr, "Error: Unable to close '%s' after logging.\n", nwipe_options.logfile ); + } + } + else + { + /* Tell user we can't create/open the log and terminate nwipe */ + fprintf( + stderr, "\nERROR:Unable to create/open '%s' for logging, permissions?\n\n", nwipe_options.logfile ); + r = pthread_mutex_unlock( &mutex1 ); + if( r != 0 ) + { + fprintf( stderr, "nwipe_log: pthread_mutex_unlock failed. Code %i \n", r ); + } + user_abort = 1; + terminate_signal = 1; + return; + } + } + + log_current_element++; + + r = pthread_mutex_unlock( &mutex1 ); + if( r != 0 ) + { + fprintf( stderr, "nwipe_log: pthread_mutex_unlock failed. Code %i \n", r ); + } + return; + +} /* nwipe_log */ + +void nwipe_perror( int nwipe_errno, const char* f, const char* s ) +{ + /** + * Wrapper for perror(). + * + * We may wish to tweak or squelch this later. + * + */ + + nwipe_log( NWIPE_LOG_ERROR, "%s: %s: %s", f, s, strerror( nwipe_errno ) ); + +} /* nwipe_perror */ + +void nwipe_log_OSinfo() +{ + /* Read /proc/version, format and write to the log */ + + FILE* fp = NULL; + char OS_info_temp[MAX_SIZE_OS_STRING + 1]; + char OS_info[MAX_SIZE_OS_STRING + 1]; + int idx; + int idx2; + int idx3; + int idx4; + + /* initialise OS_info & OS_info_temp strings */ + idx = 0; + while( idx < MAX_SIZE_OS_STRING + 1 ) + { + OS_info_temp[idx] = 0; + OS_info[idx] = 0; + idx++; + } + + /* Open a pipe to /proc/version for reading */ + fp = popen( "cat /proc/version", "r" ); + if( fp == NULL ) + { + nwipe_log( NWIPE_LOG_WARNING, "Unable to create a pipe to /proc/version" ); + return; + } + + /* Read the OS info */ + if( fgets( OS_info_temp, MAX_SIZE_OS_STRING, fp ) == NULL ) + { + nwipe_log( NWIPE_LOG_WARNING, "fget failed to read /proc/version" ); + fclose( fp ); + return; + } + + /* Format the string for the log, place on multiple lines as necessary, + * column aligned, left offset with n (OS_info_Line_offset) spaces */ + idx = 0; + idx2 = 0; + idx3 = OS_info_Line_Length; + + while( OS_info_temp[idx] != 0 ) + { + while( idx2 < idx3 && idx2 < MAX_SIZE_OS_STRING ) + { + /* remove newlines from the source */ + if( OS_info_temp[idx] == 0x0a ) + { + idx++; + } + + /* copy the character */ + OS_info[idx2++] = OS_info_temp[idx++]; + } + if( OS_info_temp[idx] != 0 ) + { + OS_info[idx2++] = 0x0a; + idx4 = 0; + + /* left indent with spaces */ + while( idx4 < OS_info_Line_offset && idx2 < MAX_SIZE_OS_STRING ) + { + OS_info[idx2++] = ' '; + idx4++; + } + + /* calculate idx3 ready for next line */ + idx3 += OS_info_Line_offset + OS_info_Line_Length; + } + else + { + continue; + } + } + + nwipe_log( NWIPE_LOG_INFO, "%s", OS_info ); + fclose( fp ); + return; +} + +int nwipe_log_sysinfo() +{ + FILE* fp; + char path[256]; + int len; + int r; // A result buffer. + + /* + * Remove or add keywords to be searched, depending on what information is to + * be logged, making sure the last entry in the array is a NULL string. To remove + * an entry simply comment out the keyword with // + */ + + /* The 0/1 after the keyword determines whether the data for this + * keyword is displayed when -q (anonymize) has been specified + * by the user. An quick reminder about multi dimensional arrays, the first + * []=the keyword (0-21) including the empty string. The second [] is the + * 1 or 0 value (0 or 1). The third [] is the index value into either string. + */ + char dmidecode_keywords[][2][24] = { + { "bios-version", "1" }, + { "bios-release-date", "1" }, + { "system-manufacturer", "1" }, + { "system-product-name", "1" }, + { "system-version", "1" }, + { "system-serial-number", "0" }, + { "system-uuid", "0" }, + { "baseboard-manufacturer", "1" }, + { "baseboard-product-name", "1" }, + { "baseboard-version", "1" }, + { "baseboard-serial-number", "0" }, + { "baseboard-asset-tag", "0" }, + { "chassis-manufacturer", "1" }, + { "chassis-type", "1" }, + { "chassis-version", "1" }, + { "chassis-serial-number", "0" }, + { "chassis-asset-tag", "0" }, + { "processor-family", "1" }, + { "processor-manufacturer", "1" }, + { "processor-version", "1" }, + { "processor-frequency", "1" }, + { "", "" } // terminates the keyword array. DO NOT REMOVE + }; + + char dmidecode_command[] = "dmidecode -s %s"; + char dmidecode_command2[] = "/sbin/dmidecode -s %s"; + char dmidecode_command3[] = "/usr/bin/dmidecode -s %s"; + char* p_dmidecode_command; + + char cmd[sizeof( dmidecode_keywords ) + sizeof( dmidecode_command2 )]; + + unsigned int keywords_idx; + + keywords_idx = 0; + + p_dmidecode_command = 0; + + if( system( "which dmidecode > /dev/null 2>&1" ) ) + { + if( system( "which /sbin/dmidecode > /dev/null 2>&1" ) ) + { + if( system( "which /usr/bin/dmidecode > /dev/null 2>&1" ) ) + { + nwipe_log( NWIPE_LOG_WARNING, "Command not found. Install dmidecode !" ); + } + else + { + p_dmidecode_command = &dmidecode_command3[0]; + } + } + else + { + p_dmidecode_command = &dmidecode_command2[0]; + } + } + else + { + p_dmidecode_command = &dmidecode_command[0]; + } + + if( p_dmidecode_command != 0 ) + { + + /* Run the dmidecode command to retrieve each dmidecode keyword, one at a time */ + while( dmidecode_keywords[keywords_idx][0][0] != 0 ) + { + sprintf( cmd, p_dmidecode_command, &dmidecode_keywords[keywords_idx][0][0] ); + fp = popen( cmd, "r" ); + if( fp == NULL ) + { + nwipe_log( NWIPE_LOG_WARNING, "nwipe_log_sysinfo: Failed to create stream to %s", cmd ); + return 1; + } + /* Read the output a line at a time - output it. */ + while( fgets( path, sizeof( path ) - 1, fp ) != NULL ) + { + /* Remove any trailing return from the string, as nwipe_log automatically adds a return */ + len = strlen( path ); + if( path[len - 1] == '\n' ) + { + path[len - 1] = 0; + } + if( nwipe_options.quiet ) + { + if( *( &dmidecode_keywords[keywords_idx][1][0] ) == '0' ) + { + nwipe_log( + NWIPE_LOG_NOTICE, "%s = %s", &dmidecode_keywords[keywords_idx][0][0], "XXXXXXXXXXXXXXX" ); + } + else + { + nwipe_log( NWIPE_LOG_NOTICE, "%s = %s", &dmidecode_keywords[keywords_idx][0][0], path ); + } + } + else + { + nwipe_log( NWIPE_LOG_NOTICE, "%s = %s", &dmidecode_keywords[keywords_idx][0][0], path ); + } + } + /* close */ + r = pclose( fp ); + if( r > 0 ) + { + nwipe_log( NWIPE_LOG_WARNING, + "nwipe_log_sysinfo(): dmidecode failed, \"%s\" exit status = %u", + cmd, + WEXITSTATUS( r ) ); + return 1; + } + keywords_idx++; + } + } + return 0; +} + +void nwipe_log_summary( nwipe_context_t** ptr, int nwipe_selected ) +{ + /* Prints two summary tables, the first is the device pass and verification summary + * and the second is the main summary table detaining the drives, status, throughput, + * model and serial number */ + int i; + int idx_src; + int idx_dest; + char device[18]; + char status[9]; + char throughput[13]; + char total_throughput_string[13]; + char summary_top_border[256]; + char summary_top_column_titles[256]; + char blank[3]; + char verify[3]; + // char duration[5]; + char duration[314]; + char model[18]; + char serial_no[20]; + char exclamation_flag[2]; + int hours; + int minutes; + int seconds; + u64 total_duration_seconds; + u64 total_throughput; + nwipe_context_t** c; + c = ptr; + + exclamation_flag[0] = 0; + device[0] = 0; + status[0] = 0; + throughput[0] = 0; + summary_top_border[0] = 0; + summary_top_column_titles[0] = 0; + blank[0] = 0; + verify[0] = 0; + duration[0] = 0; + model[0] = 0; + serial_no[0] = 0; + hours = 0; + minutes = 0; + seconds = 0; + + /* A time buffer. */ + time_t t; + + /* A pointer to the system time struct. */ + struct tm* p; + + /* Nothing to do, user never started a wipe so no summary table required. */ + if( global_wipe_status == 0 ) + { + return; + } + + /* Print the pass and verifications table */ + + /* IMPORTANT: Keep maximum columns (line length) to 80 characters for use with 80x30 terminals, Shredos, ALT-F2 etc + * --------------------------------01234567890123456789012345678901234567890123456789012345678901234567890123456789-*/ + nwipe_log( NWIPE_LOG_NOTIMESTAMP, "" ); + nwipe_log( NWIPE_LOG_NOTIMESTAMP, + "******************************** Error Summary *********************************" ); + nwipe_log( NWIPE_LOG_NOTIMESTAMP, "! Device | Pass Errors | Verifications Errors | Fdatasync I\\O Errors" ); + nwipe_log( NWIPE_LOG_NOTIMESTAMP, + "--------------------------------------------------------------------------------" ); + + for( i = 0; i < nwipe_selected; i++ ) + { + if( c[i]->pass_errors != 0 || c[i]->verify_errors != 0 || c[i]->fsyncdata_errors != 0 ) + { + strncpy( exclamation_flag, "!", 1 ); + exclamation_flag[1] = 0; + } + else + { + strncpy( exclamation_flag, " ", 1 ); + exclamation_flag[1] = 0; + } + + /* Device name, strip any prefixed /dev/.. leaving up to 6 right justified + * characters eg " sda", prefixed with space to 6 characters, note that + * we are processing the strings right to left */ + + idx_dest = 6; + device[idx_dest--] = 0; + idx_src = strlen( c[i]->device_name ); + idx_src--; + + nwipe_strip_path( device, c[i]->device_name ); + + nwipe_log( NWIPE_LOG_NOTIMESTAMP, + "%s %s | %10llu | %10llu | %10llu", + exclamation_flag, + device, + c[i]->pass_errors, + c[i]->verify_errors, + c[i]->fsyncdata_errors ); + } + + nwipe_log( NWIPE_LOG_NOTIMESTAMP, + "********************************************************************************" ); + + /* Print the main summary table */ + + /* initialise */ + total_throughput = 0; + + /* Get the current time. */ + t = time( NULL ); + p = localtime( &t ); + /* IMPORTANT: Keep maximum columns (line length) to 80 characters for use with 80x30 terminals, Shredos, ALT-F2 etc + * --------------------------------01234567890123456789012345678901234567890123456789012345678901234567890123456789-*/ + nwipe_log( NWIPE_LOG_NOTIMESTAMP, "" ); + nwipe_log( NWIPE_LOG_NOTIMESTAMP, + "********************************* Drive Status *********************************" ); + nwipe_log( NWIPE_LOG_NOTIMESTAMP, "! Device | Status | Thru-put | HH:MM:SS | Model/Serial Number" ); + nwipe_log( NWIPE_LOG_NOTIMESTAMP, + "--------------------------------------------------------------------------------" ); + /* Example layout: + * "! sdb |--FAIL--| 120MB/s | 01:22:01 | WD6788.8488YNHj/ZX677888388-N " + * ); " sdc | Erased | 120MB/s | 01:25:04 | WD6784.8488JKGG/ZX677888388-N " ); " sdv | Erased | + * 120MB/s | 01:19:07 | WD6788.848HHDDR/ZX677888388-N " ); End of Example layout */ + + for( i = 0; i < nwipe_selected; i++ ) + { + /* Device name, strip any prefixed /dev/.. leaving up to 8 right justified + * characters eg " sda", prefixed with space to 8 characters, note that + * we are processing the strings right to left */ + + nwipe_strip_path( device, c[i]->device_name ); + + extern int user_abort; + + /* Any errors ? if so set the exclamation_flag and fail message, + * All status messages should be eight characters EXACTLY ! + */ + if( c[i]->pass_errors != 0 || c[i]->verify_errors != 0 || c[i]->fsyncdata_errors != 0 ) + { + strncpy( exclamation_flag, "!", 1 ); + exclamation_flag[1] = 0; + + strncpy( status, "-FAILED-", 8 ); + status[8] = 0; + } + else + { + if( c[i]->wipe_status == 0 ) + { + strncpy( exclamation_flag, " ", 1 ); + exclamation_flag[1] = 0; + + strncpy( status, " Erased ", 8 ); + status[8] = 0; + } + else + { + if( user_abort == 1 ) + { + strncpy( exclamation_flag, "!", 1 ); + exclamation_flag[1] = 0; + + strncpy( status, "UABORTED", 8 ); + status[8] = 0; + } + else + { + /* If this ever happens, there is a bug ! */ + strncpy( exclamation_flag, " ", 1 ); + exclamation_flag[1] = 0; + + strncpy( status, "INSANITY", 8 ); + status[8] = 0; + } + } + } + + /* Determine the size of throughput so that the correct nomenclature can be used */ + Determine_C_B_nomenclature( c[i]->throughput, throughput, 13 ); + + /* Add this devices throughput to the total throughput */ + total_throughput += c[i]->throughput; + + /* Retrieve the duration of the wipe in seconds and convert to hours and minutes and seconds */ + + if( c[i]->start_time != 0 && c[i]->end_time != 0 ) + { + /* For a summary when the wipe has finished */ + c[i]->duration = difftime( c[i]->end_time, c[i]->start_time ); + } + else + { + if( c[i]->start_time != 0 && c[i]->end_time == 0 ) + { + /* For a summary in the event of a system shutdown */ + c[i]->duration = difftime( t, c[i]->start_time ); + } + } + + total_duration_seconds = (u64) c[i]->duration; + + /* Convert binary seconds into three binary variables, hours, minutes and seconds */ + convert_seconds_to_hours_minutes_seconds( total_duration_seconds, &hours, &minutes, &seconds ); + + /* Device Model */ + strncpy( model, c[i]->device_model, 17 ); + model[17] = 0; + + /* Serial No. */ + strncpy( serial_no, c[i]->device_serial_no, 20 ); + model[17] = 0; + + nwipe_log( NWIPE_LOG_NOTIMESTAMP, + "%s %s |%s| %s/s | %02i:%02i:%02i | %s/%s", + exclamation_flag, + device, + status, + throughput, + hours, + minutes, + seconds, + model, + serial_no ); + } + + /* Determine the size of throughput so that the correct nomenclature can be used */ + Determine_C_B_nomenclature( total_throughput, total_throughput_string, 13 ); + + /* Blank abbreviations used in summary table B=blank, NB=no blank */ + if( nwipe_options.noblank ) + { + strcpy( blank, "NB" ); + } + else + { + strcpy( blank, "B" ); + } + + /* Verify abbreviations used in summary table */ + switch( nwipe_options.verify ) + { + case NWIPE_VERIFY_NONE: + strcpy( verify, "NV" ); + break; + + case NWIPE_VERIFY_LAST: + strcpy( verify, "VL" ); + break; + + case NWIPE_VERIFY_ALL: + strcpy( verify, "VA" ); + break; + } + + nwipe_log( NWIPE_LOG_NOTIMESTAMP, + "--------------------------------------------------------------------------------" ); + nwipe_log( NWIPE_LOG_NOTIMESTAMP, + "[%i/%02i/%02i %02i:%02i:%02i] Total Throughput %s/s, %s, %iR+%s+%s", + 1900 + p->tm_year, + 1 + p->tm_mon, + p->tm_mday, + p->tm_hour, + p->tm_min, + p->tm_sec, + total_throughput_string, + nwipe_method_label( nwipe_options.method ), + nwipe_options.rounds, + blank, + verify ); + nwipe_log( NWIPE_LOG_NOTIMESTAMP, + "********************************************************************************" ); + nwipe_log( NWIPE_LOG_NOTIMESTAMP, "" ); +} + +void Determine_C_B_nomenclature( u64 speed, char* result, int result_array_size ) +{ + + /* C_B ? Determine Capacity or Bandwidth nomenclature + * + * A pointer to a result character string with a minimum of 13 characters in length + * should be provided. + * + * Outputs a string of the form xxxTB/s, xxxGB/s, xxxMB/s, xxxKB/s B/s depending on the value of 'speed' + */ + + /* Initialise the output array */ + int idx = 0; + + while( idx < result_array_size ) + { + result[idx++] = 0; + } + + /* Determine the size of throughput so that the correct nomenclature can be used */ + if( speed >= INT64_C( 1000000000000 ) ) + { + snprintf( result, result_array_size, "%3llu TB", speed / INT64_C( 1000000000000 ) ); + } + else if( speed >= INT64_C( 1000000000 ) ) + { + snprintf( result, result_array_size, "%3llu GB", speed / INT64_C( 1000000000 ) ); + } + else if( speed >= INT64_C( 1000000 ) ) + { + snprintf( result, result_array_size, "%3llu MB", speed / INT64_C( 1000000 ) ); + } + else if( speed >= INT64_C( 1000 ) ) + { + snprintf( result, result_array_size, "%3llu KB", speed / INT64_C( 1000 ) ); + } + else + { + snprintf( result, result_array_size, "%3llu B", speed / INT64_C( 1 ) ); + } +} + +void convert_seconds_to_hours_minutes_seconds( u64 total_seconds, int* hours, int* minutes, int* seconds ) +{ + /* Convert binary seconds into binary hours, minutes and seconds */ + + if( total_seconds % 60 ) + { + *minutes = total_seconds / 60; + + *seconds = total_seconds - ( *minutes * 60 ); + } + else + { + *minutes = total_seconds / 60; + + *seconds = 0; + } + if( *minutes > 59 ) + { + *hours = *minutes / 60; + if( *minutes % 60 ) + { + *minutes = *minutes - ( *hours * 60 ); + } + else + { + *minutes = 0; + } + } +} + +int nwipe_strip_path( char* output, char* input ) +{ + /* Take the input string, say "/dev/sda" and remove the "/dev/", prefix the result + * with 'length' spaces. So if length=8 and input=/dev/sda, output will + * be " sda", a string 8 characters long right justified with spaces. + */ + int idx_dest; + int idx_src; + idx_dest = 8; + // idx_dest = length; + output[idx_dest--] = 0; + idx_src = strlen( input ); + idx_src--; + + while( idx_dest >= 0 ) + { + /* if the device name contains a / start prefixing spaces */ + if( input[idx_src] == '/' ) + { + output[idx_dest--] = ' '; + continue; + } + if( idx_src >= 0 ) + { + output[idx_dest--] = input[idx_src--]; + } + } +} diff --git a/src/logging.h b/src/logging.h new file mode 100644 index 0000000..b2d4b75 --- /dev/null +++ b/src/logging.h @@ -0,0 +1,52 @@ +/* + * logging.c: Logging facilities for nwipe. + * + * Copyright Darik Horn <dajhorn-dban@vanadac.com>. + * + * 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, version 2. + * + * 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. + * + */ + +#ifndef LOGGING_H_ +#define LOGGING_H_ + +/* Maximum size of a log message */ +#define MAX_LOG_LINE_CHARS 512 + +#define MAX_SIZE_OS_STRING 512 /* Maximum size of acceptable OS string */ +#define OS_info_Line_offset 31 /* OS_info line offset in log */ +#define OS_info_Line_Length 48 /* OS_info line length */ + +typedef enum nwipe_log_t_ { + NWIPE_LOG_NONE = 0, + NWIPE_LOG_DEBUG, // TODO: Very verbose logging. + NWIPE_LOG_INFO, // TODO: Verbose logging. + NWIPE_LOG_NOTICE, // Most logging happens at this level. + NWIPE_LOG_WARNING, // Things that the user should know about. + NWIPE_LOG_ERROR, // Non-fatal errors that result in failure. + NWIPE_LOG_FATAL, // Errors that cause the program to exit. + NWIPE_LOG_SANITY, // Programming errors. + NWIPE_LOG_NOTIMESTAMP // logs the message without the timestamp +} nwipe_log_t; + +void nwipe_log( nwipe_log_t level, const char* format, ... ); +void nwipe_perror( int nwipe_errno, const char* f, const char* s ); +void nwipe_log_OSinfo(); +int nwipe_log_sysinfo(); +void nwipe_log_summary( nwipe_context_t**, int ); // This produces the wipe status table on exit +void Determine_C_B_nomenclature( u64, char*, int ); +void convert_seconds_to_hours_minutes_seconds( u64, int*, int*, int* ); +int nwipe_strip_path( char*, char* ); + +#endif /* LOGGING_H_ */ diff --git a/src/method.c b/src/method.c new file mode 100644 index 0000000..5f5f298 --- /dev/null +++ b/src/method.c @@ -0,0 +1,1375 @@ +/* + * method.c: Method implementations for nwipe. + * + * Copyright Darik Horn <dajhorn-dban@vanadac.com>. + * + * Modifications to original dwipe Copyright Andy Beverley <andy@andybev.com> + * + * 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, version 2. + * + * 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. + */ + +/* HOWTO: Add another wipe method. + * + * 1. Create a new function here and add the prototype to the 'method.h' file. + * 2. Update nwipe_method_label() appropriately. + * 3. Put the passes that you wish to run into a nwipe_pattern_t array. + * 4. Call nwipe_runmethod() with your array of patterns. + * 5. Copy-and-paste within the 'options.c' file so that the new method can be invoked. + * 6. Optionally try to plug your function into 'gui.c'. + * 7. Update the function 'calculate_round_size()' with the new method. + * + * + * WARNING: Remember to pad all pattern arrays with { 0, NULL }. + * + * WARNING: Never change nwipe_options after calling a method. + * + * NOTE: The nwipe_runmethod function appends a user selectable final blanking (zero) pass to all methods. + * + */ + +#include <stdint.h> + +#include "nwipe.h" +#include "context.h" +#include "method.h" +#include "prng.h" +#include "options.h" +#include "pass.h" +#include "logging.h" + +/* + * Comment Legend + * + * "method" An ordered set of patterns. + * "pattern" The magic bits that will be written to a device. + * "pass" Reading or writing one pattern to an entire device. + * "rounds" The number of times that a method will be applied to a device. + * + */ + +const char* nwipe_dod522022m_label = "DoD 5220.22-M"; +const char* nwipe_dodshort_label = "DoD Short"; +const char* nwipe_gutmann_label = "Gutmann Wipe"; +const char* nwipe_ops2_label = "RCMP TSSIT OPS-II"; +const char* nwipe_random_label = "PRNG Stream"; +const char* nwipe_zero_label = "Fill With Zeros"; +const char* nwipe_one_label = "Fill With Ones"; +const char* nwipe_verify_zero_label = "Verify Zeros (0x00)"; +const char* nwipe_verify_one_label = "Verify Ones (0xFF)"; +const char* nwipe_is5enh_label = "HMG IS5 Enhanced"; + +const char* nwipe_unknown_label = "Unknown Method (FIXME)"; + +const char* nwipe_method_label( void* method ) +{ + /** + * Returns a pointer to the name of the method function. + * + */ + + if( method == &nwipe_dod522022m ) + { + return nwipe_dod522022m_label; + } + if( method == &nwipe_dodshort ) + { + return nwipe_dodshort_label; + } + if( method == &nwipe_gutmann ) + { + return nwipe_gutmann_label; + } + if( method == &nwipe_ops2 ) + { + return nwipe_ops2_label; + } + if( method == &nwipe_random ) + { + return nwipe_random_label; + } + if( method == &nwipe_zero ) + { + return nwipe_zero_label; + } + if( method == &nwipe_one ) + { + return nwipe_one_label; + } + if( method == &nwipe_verify_zero ) + { + return nwipe_verify_zero_label; + } + if( method == &nwipe_verify_one ) + { + return nwipe_verify_one_label; + } + if( method == &nwipe_is5enh ) + { + return nwipe_is5enh_label; + } + + /* else */ + return nwipe_unknown_label; + +} /* nwipe_method_label */ + +void* nwipe_zero( void* ptr ) +{ + /** + * Fill the device with zeroes. + */ + + nwipe_context_t* c; + c = (nwipe_context_t*) ptr; + + /* get current time at the start of the wipe */ + time( &c->start_time ); + + /* set wipe in progress flag for GUI */ + c->wipe_status = 1; + + /* setup for a zero-fill. */ + + char zerofill[1] = { '\x00' }; + nwipe_pattern_t patterns[] = { { 1, &zerofill[0] }, // pass 1: 0s + { 0, NULL } }; + + /* Run the method. */ + c->result = nwipe_runmethod( c, patterns ); + + /* Finished. Set the wipe_status flag so that the GUI knows */ + c->wipe_status = 0; + + /* get current time at the end of the wipe */ + time( &c->end_time ); + + return NULL; +} /* nwipe_zero */ + +void* nwipe_one( void* ptr ) +{ + /** + * Fill the device with ones. + */ + + nwipe_context_t* c; + c = (nwipe_context_t*) ptr; + + /* get current time at the start of the wipe */ + time( &c->start_time ); + + /* set wipe in progress flag for GUI */ + c->wipe_status = 1; + + /* setup for a zero-fill. */ + + char onefill[1] = { '\xFF' }; + nwipe_pattern_t patterns[] = { { 1, &onefill[0] }, // pass 1: 1s + { 0, NULL } }; + + /* Run the method. */ + c->result = nwipe_runmethod( c, patterns ); + + /* Finished. Set the wipe_status flag so that the GUI knows */ + c->wipe_status = 0; + + /* get current time at the end of the wipe */ + time( &c->end_time ); + + return NULL; +} /* nwipe_one */ + +void* nwipe_verify_zero( void* ptr ) +{ + /** + * Verify the device is full of zeros. + */ + + nwipe_context_t* c; + c = (nwipe_context_t*) ptr; + + /* get current time at the start of the wipe */ + time( &c->start_time ); + + /* set wipe in progress flag for GUI */ + c->wipe_status = 1; + + /* Do nothing because nwipe_runmethod appends a zero-fill. */ + nwipe_pattern_t patterns[] = { { 0, NULL } }; + + /* Run the method. */ + c->result = nwipe_runmethod( c, patterns ); + + /* Finished. Set the wipe_status flag so that the GUI knows */ + c->wipe_status = 0; + + /* get current time at the end of the wipe */ + time( &c->end_time ); + + return NULL; +} /* nwipe_verify zeros */ + +void* nwipe_verify_one( void* ptr ) +{ + /** + * Verify the device is full of ones. + */ + + nwipe_context_t* c; + c = (nwipe_context_t*) ptr; + + /* get current time at the start of the wipe */ + time( &c->start_time ); + + /* set wipe in progress flag for GUI */ + c->wipe_status = 1; + + /* Do nothing because nwipe_runmethod appends a zero-fill. */ + nwipe_pattern_t patterns[] = { { 0, NULL } }; + + /* Run the method. */ + c->result = nwipe_runmethod( c, patterns ); + + /* Finished. Set the wipe_status flag so that the GUI knows */ + c->wipe_status = 0; + + /* get current time at the end of the wipe */ + time( &c->end_time ); + + return NULL; +} /* nwipe_verify */ + +void* nwipe_dod522022m( void* ptr ) +{ + /** + * United States Department of Defense 5220.22-M standard wipe. + * + */ + + nwipe_context_t* c; + c = (nwipe_context_t*) ptr; + + /* get current time at the start of the wipe */ + time( &c->start_time ); + + /* set wipe in progress flag for GUI */ + c->wipe_status = 1; + + /* A result holder. */ + int r; + + /* Random characters. (Elements 2 and 6 are unused.) */ + char dod[7]; + + nwipe_pattern_t patterns[] = { { 1, &dod[0] }, // Pass 1: A random character. + { 1, &dod[1] }, // Pass 2: The bitwise complement of pass 1. + { -1, "" }, // Pass 3: A random stream. + { 1, &dod[3] }, // Pass 4: A random character. + { 1, &dod[4] }, // Pass 5: A random character. + { 1, &dod[5] }, // Pass 6: The bitwise complement of pass 5. + { -1, "" }, // Pass 7: A random stream. + { 0, NULL } }; + + /* Load the array with random characters. */ + r = read( c->entropy_fd, &dod, sizeof( dod ) ); + + /* NOTE: Only the random data in dod[0], dod[3], and dod[4] is actually used. */ + + /* Check the result. */ + if( r != sizeof( dod ) ) + { + r = errno; + nwipe_perror( r, __FUNCTION__, "read" ); + nwipe_log( NWIPE_LOG_FATAL, "Unable to seed the %s method.", nwipe_dod522022m_label ); + + /* Ensure a negative return. */ + if( r < 0 ) + { + c->result = r; + return NULL; + } + else + { + c->result = -1; + return NULL; + } + } + + /* Pass 2 is the bitwise complement of Pass 1. */ + dod[1] = ~dod[0]; + + /* Pass 4 is the bitwise complement of Pass 3. */ + dod[5] = ~dod[4]; + + /* Run the DoD 5220.22-M method. */ + c->result = nwipe_runmethod( c, patterns ); + + /* Finished. Set the wipe_status flag so that the GUI knows */ + c->wipe_status = 0; + + /* get current time at the end of the wipe */ + time( &c->end_time ); + + return NULL; +} /* nwipe_dod522022m */ + +void* nwipe_dodshort( void* ptr ) +{ + /** + * United States Department of Defense 5220.22-M short wipe. + * This method is comprised of passes 1,2,7 from the standard wipe. + * + */ + + nwipe_context_t* c; + c = (nwipe_context_t*) ptr; + + /* get current time at the start of the wipe */ + time( &c->start_time ); + + /* set wipe in progress flag for GUI */ + c->wipe_status = 1; + + /* A result holder. */ + int r; + + /* Random characters. (Element 3 is unused.) */ + char dod[3]; + + nwipe_pattern_t patterns[] = { { 1, &dod[0] }, // Pass 1: A random character. + { 1, &dod[1] }, // Pass 2: The bitwise complement of pass 1. + { -1, "" }, // Pass 3: A random stream. + { 0, NULL } }; + + /* Load the array with random characters. */ + r = read( c->entropy_fd, &dod, sizeof( dod ) ); + + /* NOTE: Only the random data in dod[0] is actually used. */ + + /* Check the result. */ + if( r != sizeof( dod ) ) + { + r = errno; + nwipe_perror( r, __FUNCTION__, "read" ); + nwipe_log( NWIPE_LOG_FATAL, "Unable to seed the %s method.", nwipe_dodshort_label ); + + /* Ensure a negative return. */ + if( r < 0 ) + { + c->result = r; + return NULL; + } + else + { + c->result = -1; + return NULL; + } + } + + /* Pass 2 is the bitwise complement of Pass 1. */ + dod[1] = ~dod[0]; + + /* Run the DoD 5220.022-M short method. */ + c->result = nwipe_runmethod( c, patterns ); + + /* Finished. Set the wipe_status flag so that the GUI knows */ + c->wipe_status = 0; + + /* get current time at the end of the wipe */ + time( &c->end_time ); + + return NULL; +} /* nwipe_dodshort */ + +void* nwipe_gutmann( void* ptr ) +{ + /** + * Peter Gutmann's wipe. + * + */ + + nwipe_context_t* c; + c = (nwipe_context_t*) ptr; + + /* get current time at the start of the wipe */ + time( &c->start_time ); + + /* set wipe in progress flag for GUI */ + c->wipe_status = 1; + + /* Define the Gutmann method. */ + nwipe_pattern_t book[] = { { -1, "" }, // Random pass. + { -1, "" }, // Random pass. + { -1, "" }, // Random pass. + { -1, "" }, // Random pass. + { 3, "\x55\x55\x55" }, // Static pass: 0x555555 01010101 01010101 01010101 + { 3, "\xAA\xAA\xAA" }, // Static pass: 0XAAAAAA 10101010 10101010 10101010 + { 3, "\x92\x49\x24" }, // Static pass: 0x924924 10010010 01001001 00100100 + { 3, "\x49\x24\x92" }, // Static pass: 0x492492 01001001 00100100 10010010 + { 3, "\x24\x92\x49" }, // Static pass: 0x249249 00100100 10010010 01001001 + { 3, "\x00\x00\x00" }, // Static pass: 0x000000 00000000 00000000 00000000 + { 3, "\x11\x11\x11" }, // Static pass: 0x111111 00010001 00010001 00010001 + { 3, "\x22\x22\x22" }, // Static pass: 0x222222 00100010 00100010 00100010 + { 3, "\x33\x33\x33" }, // Static pass: 0x333333 00110011 00110011 00110011 + { 3, "\x44\x44\x44" }, // Static pass: 0x444444 01000100 01000100 01000100 + { 3, "\x55\x55\x55" }, // Static pass: 0x555555 01010101 01010101 01010101 + { 3, "\x66\x66\x66" }, // Static pass: 0x666666 01100110 01100110 01100110 + { 3, "\x77\x77\x77" }, // Static pass: 0x777777 01110111 01110111 01110111 + { 3, "\x88\x88\x88" }, // Static pass: 0x888888 10001000 10001000 10001000 + { 3, "\x99\x99\x99" }, // Static pass: 0x999999 10011001 10011001 10011001 + { 3, "\xAA\xAA\xAA" }, // Static pass: 0xAAAAAA 10101010 10101010 10101010 + { 3, "\xBB\xBB\xBB" }, // Static pass: 0xBBBBBB 10111011 10111011 10111011 + { 3, "\xCC\xCC\xCC" }, // Static pass: 0xCCCCCC 11001100 11001100 11001100 + { 3, "\xDD\xDD\xDD" }, // Static pass: 0xDDDDDD 11011101 11011101 11011101 + { 3, "\xEE\xEE\xEE" }, // Static pass: 0xEEEEEE 11101110 11101110 11101110 + { 3, "\xFF\xFF\xFF" }, // Static pass: 0xFFFFFF 11111111 11111111 11111111 + { 3, "\x92\x49\x24" }, // Static pass: 0x924924 10010010 01001001 00100100 + { 3, "\x49\x24\x92" }, // Static pass: 0x492492 01001001 00100100 10010010 + { 3, "\x24\x92\x49" }, // Static pass: 0x249249 00100100 10010010 01001001 + { 3, "\x6D\xB6\xDB" }, // Static pass: 0x6DB6DB 01101101 10110110 11011011 + { 3, "\xB6\xDB\x6D" }, // Static pass: 0xB6DB6D 10110110 11011011 01101101 + { 3, "\xDB\x6D\xB6" }, // Static pass: 0XDB6DB6 11011011 01101101 10110110 + { -1, "" }, // Random pass. + { -1, "" }, // Random pass. + { -1, "" }, // Random pass. + { -1, "" }, // Random pass. + { 0, NULL } }; + + /* Put the book array into this array in random order. */ + nwipe_pattern_t patterns[36]; + + /* An entropy buffer. */ + u16 s[27]; + + /* Load the array with random characters. */ + ssize_t r = read( c->entropy_fd, &s, sizeof( s ) ); + if( r != sizeof( s ) ) + { + r = errno; + nwipe_perror( r, __FUNCTION__, "read" ); + nwipe_log( NWIPE_LOG_FATAL, "Unable to seed the %s method.", nwipe_gutmann_label ); + + /* Ensure a negative return. */ + if( r < 0 ) + { + c->result = r; + return NULL; + } + else + { + c->result = -1; + return NULL; + } + } + + // First 4 random passes + for( int i = 0; i <= 3; ++i ) + { + patterns[i] = book[i]; + } + // Middle 27 passes in random order + for( int i = 26; i >= 0; --i ) + { + /* Get a random integer that is less than the first index 'i'. */ + int n = (int) ( (double) ( s[i] ) / (double) ( 0x0000FFFF + 1 ) * (double) ( i + 1 ) ); + + /* Initialize the secondary index. */ + int j = 3; + + while( n-- >= 0 ) + { + /* Advance 'j' by 'n' positions... */ + j += 1; + + /* ... but don't count 'book' elements that have already been copied. */ + while( book[j].length == 0 ) + { + j += 1; + } + } + + /* Copy the element. */ + patterns[i + 4] = book[j]; + + /* Mark this element as having been used. */ + book[j].length = 0; + } + // Last 4 random passes + for( int i = 31; i <= 34; ++i ) + { + patterns[i] = book[i]; + } + + /* Ensure that the array is terminated. */ + patterns[35].length = 0; + patterns[35].s = NULL; + + /* Run the Gutmann method. */ + c->result = nwipe_runmethod( c, patterns ); + + /* Finished. Set the wipe_status flag so that the GUI knows */ + c->wipe_status = 0; + + /* get current time at the end of the wipe */ + time( &c->end_time ); + + return NULL; +} /* nwipe_gutmann */ + +void* nwipe_ops2( void* ptr ) +{ + /** + * Royal Canadian Mounted Police + * Technical Security Standard for Information Technology + * Appendix OPS-II: Media Sanitization + * + * NOTE: The last pass of this method is specially handled by nwipe_runmethod. + * + */ + + nwipe_context_t* c; + c = (nwipe_context_t*) ptr; + + /* get current time at the start of the wipe */ + time( &c->start_time ); + + /* set wipe in progress flag for GUI */ + c->wipe_status = 1; + + /* A generic array index. */ + int i; + + /* A generic result buffer. */ + int r; + + /* A buffer for random characters. */ + char* s; + + /* A buffer for the bitwise complements of 's'. */ + char* t; + + /* The element count of 's' and 't'. */ + u32 u; + + /* The pattern array for this method is dynamically allocated. */ + nwipe_pattern_t* patterns; + + /* The element count of 'patterns'. */ + u32 q; + + /* We need one random character per round. */ + u = 1 * nwipe_options.rounds; + + /* Allocate the array of random characters. */ + s = malloc( sizeof( char ) * u ); + + if( s == NULL ) + { + nwipe_perror( errno, __FUNCTION__, "malloc" ); + nwipe_log( NWIPE_LOG_FATAL, "Unable to allocate the random character array." ); + c->result = -1; + return NULL; + } + + /* Allocate the array of complement characters. */ + t = malloc( sizeof( char ) * u ); + + if( t == NULL ) + { + nwipe_perror( errno, __FUNCTION__, "malloc" ); + nwipe_log( NWIPE_LOG_FATAL, "Unable to allocate the complement character array." ); + c->result = -1; + free( s ); + return NULL; + } + + /* We need eight pattern elements per round, plus one for padding. */ + q = 8 * u + 1; + + /* Allocate the pattern array. */ + patterns = malloc( sizeof( nwipe_pattern_t ) * q ); + + if( patterns == NULL ) + { + nwipe_perror( errno, __FUNCTION__, "malloc" ); + nwipe_log( NWIPE_LOG_FATAL, "Unable to allocate the pattern array." ); + c->result = -1; + free( s ); + free( t ); + return NULL; + } + + /* Load the array of random characters. */ + r = read( c->entropy_fd, s, u ); + + if( r != u ) + { + r = errno; + nwipe_perror( r, __FUNCTION__, "read" ); + nwipe_log( NWIPE_LOG_FATAL, "Unable to seed the %s method.", nwipe_ops2_label ); + + if( r < 0 ) + { + c->result = r; + free( s ); + free( t ); + free( patterns ); + return NULL; + } + else + { + c->result = -1; + free( s ); + free( t ); + free( patterns ); + return NULL; + } + } + + for( i = 0; i < u; i += 1 ) + { + /* Populate the array of complements. */ + t[i] = ~s[i]; + } + + for( i = 0; i < u; i += 8 ) + { + /* Populate the array of patterns. */ + + /* Even elements point to the random characters. */ + patterns[i * 4 + 0].length = 1; + patterns[i * 4 + 0].s = &s[i]; + patterns[i * 4 + 2].length = 1; + patterns[i * 4 + 2].s = &s[i]; + patterns[i * 4 + 4].length = 1; + patterns[i * 4 + 4].s = &s[i]; + patterns[i * 4 + 6].length = 1; + patterns[i * 4 + 6].s = &s[i]; + + /* Odd elements point to the complement characters. */ + patterns[i * 4 + 1].length = 1; + patterns[i * 4 + 1].s = &t[i]; + patterns[i * 4 + 3].length = 1; + patterns[i * 4 + 3].s = &t[i]; + patterns[i * 4 + 5].length = 1; + patterns[i * 4 + 5].s = &t[i]; + patterns[i * 4 + 7].length = 1; + patterns[i * 4 + 7].s = &t[i]; + } + + /* Ensure that the array is terminated. */ + patterns[q - 1].length = 0; + patterns[q - 1].s = NULL; + + /* Run the TSSIT OPS-II method. */ + c->result = nwipe_runmethod( c, patterns ); + + /* Release the random character buffer. */ + free( s ); + + /* Release the complement character buffer */ + free( t ); + + /* Release the pattern buffer. */ + free( patterns ); + + /* We're done. */ + + /* Finished. Set the wipe_status flag so that the GUI knows */ + c->wipe_status = 0; + + /* get current time at the end of the wipe */ + time( &c->end_time ); + + return NULL; +} /* nwipe_ops2 */ + +void* nwipe_is5enh( void* ptr ) +{ + nwipe_context_t* c = (nwipe_context_t*) ptr; + + /* get current time at the start of the wipe */ + time( &c->start_time ); + + c->wipe_status = 1; + + char is5enh[3] = { '\x00', '\xFF', '\x00' }; + nwipe_pattern_t patterns[] = { { 1, &is5enh[0] }, // Pass 1: 0s + { 1, &is5enh[1] }, // Pass 2: 1s + { -1, &is5enh[2] }, // Pass 3: random bytes with verification + { 0, NULL } }; + c->result = nwipe_runmethod( c, patterns ); + + c->wipe_status = 0; + + /* get current time at the end of the wipe */ + time( &c->end_time ); + + return NULL; +} /* nwipe_is5enh */ + +void* nwipe_random( void* ptr ) +{ + /** + * Fill the device with a stream from the PRNG. + * + */ + + nwipe_context_t* c; + c = (nwipe_context_t*) ptr; + + /* get current time at the start of the wipe */ + time( &c->start_time ); + + /* set wipe in progress flag for GUI */ + c->wipe_status = 1; + + /* Define the random method. */ + nwipe_pattern_t patterns[] = { { -1, "" }, { 0, NULL } }; + + /* Run the method. */ + c->result = nwipe_runmethod( c, patterns ); + + /* Finished. Set the wipe_status flag so that the GUI knows */ + c->wipe_status = 0; + + /* get current time at the end of the wipe */ + time( &c->end_time ); + + return NULL; +} /* nwipe_random */ + +int nwipe_runmethod( nwipe_context_t* c, nwipe_pattern_t* patterns ) +{ + /** + * Writes patterns to the device. + * + */ + + /* The result holder. */ + int r; + + /* An index variable. */ + int i = 0; + + /* Variable to track if it is the last pass */ + int lastpass = 0; + + i = 0; + + /* The zero-fill pattern for the final pass of most methods. */ + nwipe_pattern_t pattern_zero = { 1, "\x00" }; + + /* The one-fill pattern for verification of the ones fill */ + nwipe_pattern_t pattern_one = { 1, "\xFF" }; + + /* Create the PRNG state buffer. */ + c->prng_seed.length = NWIPE_KNOB_PRNG_STATE_LENGTH; + c->prng_seed.s = malloc( c->prng_seed.length ); + + /* Check the memory allocation. */ + if( !c->prng_seed.s ) + { + nwipe_perror( errno, __FUNCTION__, "malloc" ); + nwipe_log( NWIPE_LOG_FATAL, "Unable to allocate memory for the prng seed buffer." ); + return -1; + } + + /* Count the number of patterns in the array. */ + while( patterns[i].length ) + { + i += 1; + } + + /* Tell the parent the number of device passes that will be run in one round. */ + c->pass_count = i; + + /* Set the number of bytes that will be written across all passes in one round. */ + c->pass_size = c->pass_count * c->device_size; + + /* For the selected method, calculate the correct round_size value (for correct percentage calculation) */ + calculate_round_size( c ); + + /* If only verifying then the round size is the device size */ + if( nwipe_options.method == &nwipe_verify_zero || nwipe_options.method == &nwipe_verify_one ) + { + c->round_size = c->device_size; + } + + /* Initialize the working round counter. */ + c->round_working = 0; + + nwipe_log( + NWIPE_LOG_NOTICE, "Invoking method '%s' on %s", nwipe_method_label( nwipe_options.method ), c->device_name ); + + while( c->round_working < c->round_count ) + { + /* Increment the round counter. */ + c->round_working += 1; + + nwipe_log( + NWIPE_LOG_NOTICE, "Starting round %i of %i on %s", c->round_working, c->round_count, c->device_name ); + + /* Initialize the working pass counter. */ + c->pass_working = 0; + + for( i = 0; i < c->pass_count; i++ ) + { + /* Increment the working pass. */ + c->pass_working += 1; + + /* Check if this is the last pass. */ + if( nwipe_options.verify == NWIPE_VERIFY_LAST && nwipe_options.method != &nwipe_ops2 ) + { + if( nwipe_options.noblank == 1 && c->round_working == c->round_count + && c->pass_working == c->pass_count ) + { + lastpass = 1; + } + } + + nwipe_log( NWIPE_LOG_NOTICE, + "Starting pass %i/%i, round %i/%i, on %s", + c->pass_working, + c->pass_count, + c->round_working, + c->round_count, + c->device_name ); + + if( patterns[i].length == 0 ) + { + /* Caught insanity. */ + nwipe_log( NWIPE_LOG_SANITY, "nwipe_runmethod: A non-terminating pattern element has zero length." ); + return -1; + } + + if( patterns[i].length > 0 ) + { + + /* Write a static pass. */ + c->pass_type = NWIPE_PASS_WRITE; + r = nwipe_static_pass( c, &patterns[i] ); + c->pass_type = NWIPE_PASS_NONE; + + /* Log number of bytes written to disk */ + nwipe_log( NWIPE_LOG_NOTICE, "%llu bytes written to %s", c->pass_done, c->device_name ); + + /* Check for a fatal error. */ + if( r < 0 ) + { + return r; + } + + if( nwipe_options.verify == NWIPE_VERIFY_ALL || lastpass == 1 ) + { + + nwipe_log( NWIPE_LOG_NOTICE, + "Verifying pass %i of %i, round %i of %i, on %s", + c->pass_working, + c->pass_count, + c->round_working, + c->round_count, + c->device_name ); + + /* Verify this pass. */ + c->pass_type = NWIPE_PASS_VERIFY; + r = nwipe_static_verify( c, &patterns[i] ); + c->pass_type = NWIPE_PASS_NONE; + + nwipe_log( NWIPE_LOG_NOTICE, "%llu bytes read from %s", c->pass_done, c->device_name ); + + /* Check for a fatal error. */ + if( r < 0 ) + { + return r; + } + + nwipe_log( NWIPE_LOG_NOTICE, + "Verified pass %i of %i, round %i of %i, on '%s'.", + c->pass_working, + c->pass_count, + c->round_working, + c->round_count, + c->device_name ); + } + + } /* static pass */ + + else + { + c->pass_type = NWIPE_PASS_WRITE; + + /* Seed the PRNG. */ + r = read( c->entropy_fd, c->prng_seed.s, c->prng_seed.length ); + + /* Check the result. */ + if( r < 0 ) + { + c->pass_type = NWIPE_PASS_NONE; + nwipe_perror( errno, __FUNCTION__, "read" ); + nwipe_log( NWIPE_LOG_FATAL, "Unable to seed the PRNG." ); + return -1; + } + + /* Check for a partial read. */ + if( r != c->prng_seed.length ) + { + /* TODO: Handle partial reads. */ + nwipe_log( NWIPE_LOG_FATAL, "Insufficient entropy is available." ); + return -1; + } + + /* Write the random pass. */ + r = nwipe_random_pass( c ); + c->pass_type = NWIPE_PASS_NONE; + + /* Log number of bytes written to disk */ + nwipe_log( NWIPE_LOG_NOTICE, "%llu bytes written to %s", c->pass_done, c->device_name ); + + /* Check for a fatal error. */ + if( r < 0 ) + { + return r; + } + + /* Make sure IS5 enhanced always verifies its PRNG pass regardless */ + /* of the current combination of the --noblank (which influences */ + /* the lastpass variable) and --verify options. */ + if( nwipe_options.verify == NWIPE_VERIFY_ALL || lastpass == 1 || nwipe_options.method == &nwipe_is5enh ) + { + nwipe_log( NWIPE_LOG_NOTICE, + "Verifying pass %i of %i, round %i of %i, on %s", + c->pass_working, + c->pass_count, + c->round_working, + c->round_count, + c->device_name ); + + /* Verify this pass. */ + c->pass_type = NWIPE_PASS_VERIFY; + r = nwipe_random_verify( c ); + c->pass_type = NWIPE_PASS_NONE; + + nwipe_log( NWIPE_LOG_NOTICE, "%llu bytes read from %s", c->pass_done, c->device_name ); + + /* Check for a fatal error. */ + if( r < 0 ) + { + return r; + } + + nwipe_log( NWIPE_LOG_NOTICE, + "Verified pass %i of %i, round %i of %i, on '%s'.", + c->pass_working, + c->pass_count, + c->round_working, + c->round_count, + c->device_name ); + } + + } /* random pass */ + + nwipe_log( NWIPE_LOG_NOTICE, + "Finished pass %i/%i, round %i/%i, on %s", + c->pass_working, + c->pass_count, + c->round_working, + c->round_count, + c->device_name ); + + } /* for passes */ + + if( c->round_working < c->round_count ) + { + nwipe_log( + NWIPE_LOG_NOTICE, "Finished round %i of %i on %s", c->round_working, c->round_count, c->device_name ); + } + else + { + nwipe_log( NWIPE_LOG_NOTICE, + "Finished final round %i of %i on %s", + c->round_working, + c->round_count, + c->device_name ); + } + + } /* while rounds */ + + if( nwipe_options.method == &nwipe_ops2 ) + { + /* NOTE: The OPS-II method specifically requires that a random pattern be left on the device. */ + + /* Tell the parent that we are running the final pass. */ + c->pass_type = NWIPE_PASS_FINAL_OPS2; + + /* Seed the PRNG. */ + r = read( c->entropy_fd, c->prng_seed.s, c->prng_seed.length ); + + /* Check the result. */ + if( r < 0 ) + { + nwipe_perror( errno, __FUNCTION__, "read" ); + nwipe_log( NWIPE_LOG_FATAL, "Unable to seed the PRNG." ); + return -1; + } + + /* Check for a partial read. */ + if( r != c->prng_seed.length ) + { + /* TODO: Handle partial reads. */ + nwipe_log( NWIPE_LOG_FATAL, "Insufficient entropy is available." ); + return -1; + } + + nwipe_log( NWIPE_LOG_NOTICE, "Writing final random pattern to '%s'.", c->device_name ); + + /* The final ops2 pass. */ + r = nwipe_random_pass( c ); + + nwipe_log( NWIPE_LOG_NOTICE, "%llu bytes written to %s", c->pass_done, c->device_name ); + + /* Check for a fatal error. */ + if( r < 0 ) + { + return r; + } + + if( nwipe_options.verify == NWIPE_VERIFY_LAST || nwipe_options.verify == NWIPE_VERIFY_ALL ) + { + nwipe_log( NWIPE_LOG_NOTICE, "Verifying final random pattern FRP on %s", c->device_name ); + + /* Verify the final zero pass. */ + r = nwipe_random_verify( c ); + + nwipe_log( NWIPE_LOG_NOTICE, "%llu bytes read from %s", c->pass_done, c->device_name ); + + /* Check for a fatal error. */ + if( r < 0 ) + { + return r; + } + + nwipe_log( NWIPE_LOG_NOTICE, "[SUCCESS] Verified FRP on '%s' matches", c->device_name ); + } + + } /* final ops2 */ + + else if( nwipe_options.method == &nwipe_verify_zero ) + { + nwipe_log( NWIPE_LOG_NOTICE, "Verifying that %s is zeroed", c->device_name ); + + /* Verify the final zero pass. */ + c->pass_type = NWIPE_PASS_VERIFY; + r = nwipe_static_verify( c, &pattern_zero ); + c->pass_type = NWIPE_PASS_NONE; + + /* Check for a fatal error. */ + if( r < 0 ) + { + return r; + } + if( c->verify_errors == 0 ) + { + nwipe_log( NWIPE_LOG_NOTICE, "[SUCCESS] Verified that %s is Zeroed.", c->device_name ); + } + else + { + nwipe_log( NWIPE_LOG_ERROR, "[FAILURE] %s has not been Zeroed .", c->device_name ); + } + + } /* verify */ + + else if( nwipe_options.method == &nwipe_verify_one ) + { + nwipe_log( NWIPE_LOG_NOTICE, "Verifying that %s is Ones (0xFF)", c->device_name ); + + /* Verify the final ones pass. */ + c->pass_type = NWIPE_PASS_VERIFY; + r = nwipe_static_verify( c, &pattern_one ); + c->pass_type = NWIPE_PASS_NONE; + + /* Check for a fatal error. */ + if( r < 0 ) + { + return r; + } + if( c->verify_errors == 0 ) + { + nwipe_log( NWIPE_LOG_NOTICE, "[SUCCESS] Verified that %s is full of ones (0xFF).", c->device_name ); + } + else + { + nwipe_log( NWIPE_LOG_ERROR, "[FAILURE] %s is not full of ones (0xFF).", c->device_name ); + } + + } /* verify */ + + else if( nwipe_options.noblank == 0 ) + { + /* Tell the user that we are on the final pass. */ + c->pass_type = NWIPE_PASS_FINAL_BLANK; + + nwipe_log( NWIPE_LOG_NOTICE, "Blanking device %s", c->device_name ); + + /* The final zero pass. */ + r = nwipe_static_pass( c, &pattern_zero ); + + /* Log number of bytes written to disk */ + nwipe_log( NWIPE_LOG_NOTICE, "%llu bytes written to %s", c->pass_done, c->device_name ); + + /* Check for a fatal error. */ + if( r < 0 ) + { + return r; + } + + if( nwipe_options.verify == NWIPE_VERIFY_LAST || nwipe_options.verify == NWIPE_VERIFY_ALL ) + { + nwipe_log( NWIPE_LOG_NOTICE, "Verifying that %s is empty.", c->device_name ); + + /* Verify the final zero pass. */ + c->pass_type = NWIPE_PASS_VERIFY; + r = nwipe_static_verify( c, &pattern_zero ); + c->pass_type = NWIPE_PASS_NONE; + + /* Log number of bytes read from disk */ + nwipe_log( NWIPE_LOG_NOTICE, "%llu bytes read from %s", c->pass_done, c->device_name ); + + /* Check for a fatal error. */ + if( r < 0 ) + { + return r; + } + + if( c->verify_errors == 0 ) + { + nwipe_log( NWIPE_LOG_NOTICE, "[SUCCESS] Verified that %s is empty.", c->device_name ); + } + else + { + nwipe_log( NWIPE_LOG_NOTICE, "[FAILURE] %s Verification errors, not empty", c->device_name ); + } + } + + if( c->verify_errors == 0 && c->pass_errors == 0 ) + { + nwipe_log( NWIPE_LOG_NOTICE, "[SUCCESS] Blanked device %s", c->device_name ); + } + else + { + nwipe_log( NWIPE_LOG_NOTICE, "[FAILURE] %s may not be blanked", c->device_name ); + } + + } /* final blank */ + + /* Release the state buffer. */ + c->prng_seed.length = 0; + free( c->prng_seed.s ); + + /* Tell the parent that we have fininshed the final pass. */ + c->pass_type = NWIPE_PASS_NONE; + + if( c->verify_errors > 0 ) + { + /* We finished, but with non-fatal verification errors. */ + nwipe_log( NWIPE_LOG_ERROR, "%llu verification errors on '%s'.", c->verify_errors, c->device_name ); + } + + if( c->pass_errors > 0 ) + { + /* We finished, but with non-fatal wipe errors. */ + nwipe_log( NWIPE_LOG_ERROR, "%llu wipe errors on '%s'.", c->pass_errors, c->device_name ); + } + + /* FIXME: The 'round_errors' context member is not being used. */ + + if( c->pass_errors > 0 || c->round_errors > 0 || c->verify_errors > 0 ) + { + /* We finished, but with non-fatal errors. */ + return 1; + } + + /* We finished successfully. */ + return 0; + +} /* nwipe_runmethod */ + +void calculate_round_size( nwipe_context_t* c ) +{ + /* This is where the round size is calculated. round_size is used in the running percentage completion + * calculation. round size is calculated based on pass_size, pass_count, number of rounds, blanking + * on/off and verification All/Last/None + * + * To hopefully make this calculation more understandable, I have separated the calculations that apply to + * all methods and processed first then created a switch statement that contains method specific changes if any + */ + + /* Don't change the order of these values as the case statements use their index in the array */ + void* array_methods[] = { &nwipe_zero, + &nwipe_ops2, + &nwipe_dodshort, + &nwipe_dod522022m, + &nwipe_gutmann, + &nwipe_random, + &nwipe_is5enh, + NULL }; + int i; + + /* This while loop allows us to effectively create a const so we can use a case statement rather than if statements. + * This is probably more readable as more methods may get added in the future. The code could be condensed as some + * methods have identical adjustments, however as there are only a few methods I felt it was easier to understand as + * it is, however this could be changed if necessary. + */ + + int selected_method; + i = 0; + while( array_methods[i] != NULL ) + { + if( nwipe_options.method == array_methods[i] ) + { + selected_method = i; + } + i++; + } + + if( nwipe_options.verify == NWIPE_VERIFY_ALL ) + { + /* We must read back all passes, so double the byte count. */ + c->pass_size *= 2; + } + + /* Tell the parent the number of rounds that will be run. */ + c->round_count = nwipe_options.rounds; + + /* Set the initial number of bytes that will be written across all rounds. + c->pass_size includes write AND verification passes if 'verify_all' is selected + but does not include the final blanking pass or the verify_last option */ + c->round_size = c->pass_size; + + /* Multiple the round_size by the number of rounds (times) the user wants to wipe the drive with this method. */ + c->round_size *= c->round_count; + + /* Now increase size based on whether blanking is enabled and verification */ + if( nwipe_options.noblank == 0 ) + { + /* Blanking enabled so increase round size */ + c->round_size += c->device_size; + + if( nwipe_options.verify == NWIPE_VERIFY_LAST || nwipe_options.verify == NWIPE_VERIFY_ALL ) + { + c->round_size += c->device_size; + } + } + else + { + /* Blanking not enabled, check for 'Verify_last', increase round size if enabled. */ + if( nwipe_options.verify == NWIPE_VERIFY_LAST ) + { + c->round_size += c->device_size; + } + } + + /* Additional method specific round_size adjustments go in this switch statement */ + + switch( selected_method ) + { + case 0: + /* NWIPE_ZERO - No additional calculation required + * ---------- */ + break; + + case 1: + /* NWIPE_OPS2 + * ---------- */ + + /* Required for mandatory 9th and final random pass */ + c->round_size += c->device_size; + + /* Required for selectable 9th and final random verification */ + if( nwipe_options.verify == NWIPE_VERIFY_ALL || nwipe_options.verify == NWIPE_VERIFY_LAST ) + { + c->round_size += c->device_size; + } + + /* As no final zero blanking pass is permitted by this standard reduce round size if it's selected */ + if( nwipe_options.noblank == 0 ) + { + /* Reduce for blanking pass */ + c->round_size -= c->device_size; + + /* Reduce for blanking pass verification */ + if( nwipe_options.verify == NWIPE_VERIFY_ALL || nwipe_options.verify == NWIPE_VERIFY_LAST ) + { + c->round_size -= c->device_size; + } + } + else + { + if( nwipe_options.verify == NWIPE_VERIFY_LAST ) + { + /* If blanking off & verification on reduce round size */ + c->round_size -= c->device_size; + } + } + + break; + + case 2: + /* DoD Short - No additional calculation required + * --------- */ + + break; + + case 3: + /* DOD 522022m - No additional calculation required + * ----------- */ + + break; + + case 4: + /* GutMann - No additional calculation required + * ------- */ + + break; + + case 5: + /* PRNG (random) - No additional calculation required + * ------------- */ + + break; + + case 6: + /* NWIPE_IS5ENH + * ------------ */ + + /* This method ALWAYS verifies the 3rd pass so increase by device size, + * but NOT if VERIFY_ALL has been selected, but first .. */ + + /* Reduce as Verify_Last already included previously if blanking was off */ + if( nwipe_options.verify == NWIPE_VERIFY_LAST && nwipe_options.noblank == 1 ) + { + c->round_size -= c->device_size; + } + + /* Adjusts for verify on every third pass multiplied by number of rounds */ + if( nwipe_options.verify != NWIPE_VERIFY_ALL ) + { + c->round_size += ( c->device_size * c->round_count ); + } + + break; + } +} + +/* eof */ diff --git a/src/method.h b/src/method.h new file mode 100644 index 0000000..f6fdbc2 --- /dev/null +++ b/src/method.h @@ -0,0 +1,60 @@ +/* + * methods.c: Method implementations for nwipe. + * + * Copyright Darik Horn <dajhorn-dban@vanadac.com>. + * + * Modifications to original dwipe Copyright Andy Beverley <andy@andybev.com> + * + * 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, version 2. + * + * 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. + * + */ + +#ifndef METHOD_H_ +#define METHOD_H_ + +/* The argument list for nwipe methods. */ +#define NWIPE_METHOD_SIGNATURE nwipe_context_t* c + +typedef enum nwipe_verify_t_ { + NWIPE_VERIFY_NONE = 0, // Do not read anything back from the device. + NWIPE_VERIFY_LAST, // Check the last pass. + NWIPE_VERIFY_ALL, // Check all passes. +} nwipe_verify_t; + +/* The typedef of the function that will do the wipe. */ +typedef int ( *nwipe_method_t )( void* ptr ); + +typedef struct +{ + int length; // Length of the pattern in bytes, -1 means random. + char* s; // The actual bytes of the pattern. +} nwipe_pattern_t; + +const char* nwipe_method_label( void* method ); +int nwipe_runmethod( NWIPE_METHOD_SIGNATURE, nwipe_pattern_t* patterns ); + +void* nwipe_dod522022m( void* ptr ); +void* nwipe_dodshort( void* ptr ); +void* nwipe_gutmann( void* ptr ); +void* nwipe_ops2( void* ptr ); +void* nwipe_is5enh( void* ptr ); +void* nwipe_random( void* ptr ); +void* nwipe_zero( void* ptr ); +void* nwipe_one( void* ptr ); +void* nwipe_verify_zero( void* ptr ); +void* nwipe_verify_one( void* ptr ); + +void calculate_round_size( nwipe_context_t* ); + +#endif /* METHOD_H_ */ diff --git a/src/mt19937ar-cok/mt19937ar-cok.c b/src/mt19937ar-cok/mt19937ar-cok.c new file mode 100644 index 0000000..63ef659 --- /dev/null +++ b/src/mt19937ar-cok/mt19937ar-cok.c @@ -0,0 +1,139 @@ +/* + This code is modified for use in nwipe. + + A C-program for MT19937, with initialization improved 2002/2/10. + Coded by Takuji Nishimura and Makoto Matsumoto. + This is a faster version by taking Shawn Cokus's optimization, + Matthe Bellew's simplification, Isaku Wada's real version. + + Before using, initialize the state by using init_genrand(seed) + or init_by_array(init_key, key_length). + + Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. The names of its contributors may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + Any feedback is very welcome. + http://www.math.keio.ac.jp/matumoto/emt.html + email: matumoto@math.keio.ac.jp +*/ + +#include <stdio.h> +#include "mt19937ar-cok.h" + +/* initializes state[N] with a seed */ +void init_genrand( twister_state_t* state, unsigned long s) +{ + int j; + state->array[0]= s & 0xffffffffUL; + for( j = 1; j < N; j++ ) + { + state->array[j] = (1812433253UL * (state->array[j-1] ^ (state->array[j-1] >> 30)) + j); + state->array[j] &= 0xffffffffUL; /* for >32 bit machines */ + } + state->left = 1; + state->initf = 1; +} + + +void twister_init( twister_state_t* state, unsigned long init_key[], unsigned long key_length ) +{ + int i = 1; + int j = 0; + int k = ( N > key_length ? N : key_length ); + + init_genrand( state, 19650218UL ); + + for( ; k; k-- ) + { + state->array[i] = (state->array[i] ^ ((state->array[i-1] ^ (state->array[i-1] >> 30)) * 1664525UL)) + init_key[j] + j; + state->array[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */ + ++i; + ++j; + + if ( i >= N ) + { + state->array[0] = state->array[N-1]; + i = 1; + } + + if ( j >= key_length ) + { + j = 0; + } + } + + for( k = N -1; k; k-- ) + { + state->array[i] = (state->array[i] ^ ((state->array[i-1] ^ (state->array[i-1] >> 30)) * 1566083941UL)) - i; + state->array[i] &= 0xffffffffUL; + ++i; + + if ( i >= N ) + { + state->array[0] = state->array[N-1]; + i = 1; + } + } + + state->array[0] = 0x80000000UL; /* MSB is 1; assuring non-zero initial array */ + state->left = 1; + state->initf = 1; +} + +static void next_state( twister_state_t* state ) +{ + unsigned long *p = state->array; + int j; + + if( state->initf == 0) { init_genrand( state, 5489UL ); } + state->left = N; + state->next = state->array; + for( j = N - M + 1; --j; p++ ) { *p = p[M] ^ TWIST(p[0], p[1]); } + for( j = M; --j; p++ ) { *p = p[M-N] ^ TWIST(p[0], p[1]); } + *p = p[M-N] ^ TWIST(p[0], state->array[0]); +} + +/* generates a random number on [0,0xffffffff]-interval */ +unsigned long twister_genrand_int32( twister_state_t* state ) +{ + unsigned long y; + + if ( --state->left == 0 ) { next_state( state ); } + y = *state->next++; + + /* Tempering */ + y ^= (y >> 11); + y ^= (y << 7) & 0x9d2c5680UL; + y ^= (y << 15) & 0xefc60000UL; + y ^= (y >> 18); + + return y; +} diff --git a/src/mt19937ar-cok/mt19937ar-cok.h b/src/mt19937ar-cok/mt19937ar-cok.h new file mode 100644 index 0000000..afcc873 --- /dev/null +++ b/src/mt19937ar-cok/mt19937ar-cok.h @@ -0,0 +1,32 @@ +/* + * mt19937ar-cok.h: The Mersenne Twister PRNG implementation for nwipe. + * + */ + +#ifndef MT19937AR_H_ +#define MT19937AR_H_ + +/* Period parameters */ +#define N 624 +#define M 397 +#define MATRIX_A 0x9908b0dfUL /* constant vector a */ +#define UMASK 0x80000000UL /* most significant w-r bits */ +#define LMASK 0x7fffffffUL /* least significant r bits */ +#define MIXBITS(u,v) ( ((u) & UMASK) | ((v) & LMASK) ) +#define TWIST(u,v) ((MIXBITS(u,v) >> 1) ^ ((v)&1UL ? MATRIX_A : 0UL)) + +typedef struct twister_state_t_ +{ + unsigned long array[N]; + int left; + int initf; + unsigned long *next; +} twister_state_t; + +/* Initialize the MT state. ( 0 < key_length <= 624 ). */ +void twister_init( twister_state_t* state, unsigned long init_key[], unsigned long key_length); + +/* Generate a random integer on the [0,0xffffffff] interval. */ +unsigned long twister_genrand_int32( twister_state_t* state ); + +#endif /* MT19937AR_H_ */ diff --git a/src/nwipe.c b/src/nwipe.c new file mode 100644 index 0000000..d5f3798 --- /dev/null +++ b/src/nwipe.c @@ -0,0 +1,982 @@ +/* + * nwipe.c: Darik's Wipe. + * + * Copyright Darik Horn <dajhorn-dban@vanadac.com>. + * + * Modifications to original dwipe Copyright Andy Beverley <andy@andybev.com> + * + * 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, version 2. + * + * 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. + * + */ +#ifndef _DEFAULT_SOURCE +#define _DEFAULT_SOURCE +#endif + +#ifndef _POSIX_SOURCE +#define _POSIX_SOURCE +#endif + +#include <stdint.h> +#include <time.h> +#include <signal.h> +#include <pthread.h> +#include <sys/types.h> + +#include "nwipe.h" +#include "context.h" +#include "method.h" +#include "prng.h" +#include "options.h" +#include "device.h" +#include "logging.h" +#include "gui.h" +#include "temperature.h" + +#include <sys/ioctl.h> /* FIXME: Twice Included */ +#include <sys/shm.h> +#include <wait.h> + +#include <parted/parted.h> +#include <parted/debug.h> +#include "version.h" + +int terminate_signal; +int user_abort; +int global_wipe_status; + +int main( int argc, char** argv ) +{ + int nwipe_optind; // The result of nwipe_options(). + int nwipe_enumerated; // The number of contexts that have been enumerated. + int nwipe_error = 0; // An error counter. + int nwipe_selected = 0; // The number of contexts that have been selected. + int any_threads_still_running; // used in wipe thread cancellation wait loop + int thread_timeout_counter; // timeout thread cancellation after THREAD_CANCELLATION_TIMEOUT seconds + pthread_t nwipe_gui_thread = 0; // The thread ID of the GUI thread. + pthread_t nwipe_sigint_thread; // The thread ID of the sigint handler. + + char modprobe_command[] = "modprobe %s"; + char modprobe_command2[] = "/sbin/modprobe %s"; + char modprobe_command3[] = "/usr/sbin/modprobe %s"; + char module_shortform[50]; + char final_cmd_modprobe[sizeof( modprobe_command ) + sizeof( module_shortform )]; + + /* The entropy source file handle. */ + int nwipe_entropy; + + /* The generic index variables. */ + int i; + int j; + + /* The generic result buffer. */ + int r; + + /* Log nwipes version */ + nwipe_log( NWIPE_LOG_INFO, "%s", banner ); + + /* Log OS info */ + nwipe_log_OSinfo(); + + /* Initialise the termintaion signal, 1=terminate nwipe */ + terminate_signal = 0; + + /* Initialise the user abort signal, 1=User aborted with CNTRL-C,SIGTERM, SIGQUIT, SIGINT etc.. */ + user_abort = 0; + + /* nwipes return status value, set prior to exit at the end of nwipe, as no other exit points allowed */ + int return_status = 0; + + /* Initialise, flag indicating whether a wipe has actually started or not 0=no, 1=yes */ + global_wipe_status = 0; + + /* Initialise flags that indicate whether a fatal or non fatal error occurred on ANY drive */ + int fatal_errors_flag = 0; + int non_fatal_errors_flag = 0; + + /* Two arrays are used, containing pointers to the the typedef for each disk */ + /* The first array (c1) points to all devices, the second points to only */ + /* the disks selected for wiping. */ + + /* The array of pointers to enumerated contexts. */ + /* Initialised and populated in device scan. */ + nwipe_context_t** c1 = 0; + + int wipe_threads_started = 0; + + /* Parse command line options. */ + nwipe_optind = nwipe_options_parse( argc, argv ); + + if( nwipe_optind == argc ) + { + /* File names were not given by the user. Scan for devices. */ + nwipe_enumerated = nwipe_device_scan( &c1 ); + + if( terminate_signal == 1 ) + { + cleanup(); + exit( 1 ); + } + + if( nwipe_enumerated == 0 ) + { + nwipe_log( NWIPE_LOG_INFO, + "Storage devices not found. Nwipe should be run as root or sudo/su, i.e sudo nwipe etc" ); + cleanup(); + return -1; + } + else + { + nwipe_log( NWIPE_LOG_INFO, "Automatically enumerated %i devices.", nwipe_enumerated ); + } + } + else + { + argv += nwipe_optind; + argc -= nwipe_optind; + + nwipe_enumerated = nwipe_device_get( &c1, argv, argc ); + if( nwipe_enumerated == 0 ) + { + nwipe_log( NWIPE_LOG_ERROR, "Devices not found. Check you're not excluding drives unnecessarily." ); + printf( "No drives found\n" ); + cleanup(); + exit( 1 ); + } + } + + if( terminate_signal == 1 ) + { + cleanup(); + exit( 1 ); + } + + /* Log the System information */ + nwipe_log_sysinfo(); + + /* The array of pointers to contexts that will actually be wiped. */ + nwipe_context_t** c2 = (nwipe_context_t**) malloc( nwipe_enumerated * sizeof( nwipe_context_t* ) ); + + /* Open the entropy source. */ + nwipe_entropy = open( NWIPE_KNOB_ENTROPY, O_RDONLY ); + + /* Check the result. */ + if( nwipe_entropy < 0 ) + { + nwipe_perror( errno, __FUNCTION__, "open" ); + nwipe_log( NWIPE_LOG_FATAL, "Unable to open entropy source %s.", NWIPE_KNOB_ENTROPY ); + cleanup(); + free( c2 ); + return errno; + } + + nwipe_log( NWIPE_LOG_NOTICE, "Opened entropy source '%s'.", NWIPE_KNOB_ENTROPY ); + + /* Block relevant signals in main thread. Any other threads that are */ + /* created after this will also block those signals. */ + sigset_t sigset; + sigemptyset( &sigset ); + sigaddset( &sigset, SIGHUP ); + sigaddset( &sigset, SIGTERM ); + sigaddset( &sigset, SIGQUIT ); + sigaddset( &sigset, SIGINT ); + sigaddset( &sigset, SIGUSR1 ); + pthread_sigmask( SIG_SETMASK, &sigset, NULL ); + + /* Create a signal handler thread. This thread will catch all */ + /* signals and decide what to do with them. This will only */ + /* catch nondirected signals. (I.e., if a thread causes a SIGFPE */ + /* then that thread will get that signal. */ + + /* Pass a pointer to a struct containing all data to the signal handler. */ + nwipe_misc_thread_data_t nwipe_misc_thread_data; + nwipe_thread_data_ptr_t nwipe_thread_data_ptr; + + nwipe_thread_data_ptr.c = c2; + nwipe_misc_thread_data.nwipe_enumerated = nwipe_enumerated; + nwipe_misc_thread_data.nwipe_selected = 0; + if( !nwipe_options.nogui ) + nwipe_misc_thread_data.gui_thread = &nwipe_gui_thread; + nwipe_thread_data_ptr.nwipe_misc_thread_data = &nwipe_misc_thread_data; + + if( !nwipe_options.nosignals ) + { + pthread_attr_t pthread_attr; + pthread_attr_init( &pthread_attr ); + pthread_attr_setdetachstate( &pthread_attr, PTHREAD_CREATE_DETACHED ); + + pthread_create( &nwipe_sigint_thread, &pthread_attr, signal_hand, &nwipe_thread_data_ptr ); + } + + /* Makesure the drivetemp module is loaded, else drives hwmon entries won't appear in /sys/class/hwmon */ + final_cmd_modprobe[0] = 0; + + /* The kernel module we are going to load */ + strcpy( module_shortform, "drivetemp" ); + + /* Determine whether we can access modprobe, required if the PATH environment is not setup ! (Debian sid 'su' as + * opposed to 'su -' */ + + if( system( "which modprobe > /dev/null 2>&1" ) ) + { + if( system( "which /sbin/modprobe > /dev/null 2>&1" ) ) + { + if( system( "which /usr/sbin/modprobe > /dev/null 2>&1" ) ) + { + nwipe_log( NWIPE_LOG_WARNING, "modprobe command not found. Install kmod package (modprobe)) !" ); + nwipe_log( NWIPE_LOG_WARNING, "Most temperature monitoring may be unavailable as module drivetemp" ); + nwipe_log( NWIPE_LOG_WARNING, "could not be loaded. drivetemp is not available on kernels < v5.5" ); + } + else + { + sprintf( final_cmd_modprobe, modprobe_command3, module_shortform ); + } + } + else + { + sprintf( final_cmd_modprobe, modprobe_command2, module_shortform ); + } + } + else + { + sprintf( final_cmd_modprobe, modprobe_command, module_shortform ); + } + + /* load the drivetemp module */ + if( system( final_cmd_modprobe ) != 0 ) + { + nwipe_log( NWIPE_LOG_WARNING, "hwmon: Unable to load module drivetemp, temperatures may be unavilable." ); + nwipe_log( NWIPE_LOG_WARNING, "hwmon: It's possible the drivetemp software isn't modular but built-in" ); + nwipe_log( NWIPE_LOG_WARNING, "hwmon: to the kernel, as is the case with ShredOS.x86_64 in which case" ); + nwipe_log( NWIPE_LOG_WARNING, "hwmon: the temperatures will actually be available despite this issue." ); + } + else + { + nwipe_log( NWIPE_LOG_NOTICE, "hwmon: Module drivetemp loaded, drive temperatures available" ); + } + + /* A context struct for each device has already been created. */ + /* Now set specific nwipe options */ + for( i = 0; i < nwipe_enumerated; i++ ) + { + + /* Set the entropy source. */ + c1[i]->entropy_fd = nwipe_entropy; + + if( nwipe_options.autonuke ) + { + /* When the autonuke option is set, select all disks. */ + // TODO - partitions + // if( c1[i].device_part == 0 ) { c1[i].select = NWIPE_SELECT_TRUE; } + // else { c1[i].select = NWIPE_SELECT_TRUE_PARENT; } + c1[i]->select = NWIPE_SELECT_TRUE; + } + else + { + /* The user must manually select devices. */ + c1[i]->select = NWIPE_SELECT_FALSE; + } + + /* Initialise temperature variables for device */ + nwipe_init_temperature( c1[i] ); + if( nwipe_options.verbose ) + { + nwipe_log( NWIPE_LOG_NOTICE, "hwmon: Device %s hwmon path = %s", c1[i]->device_name, c1[i]->temp1_path ); + } + + nwipe_update_temperature( c1[i] ); + } + + /* Check for initialization errors. */ + if( nwipe_error ) + { + nwipe_log( NWIPE_LOG_ERROR, "Initialization error %i\n", nwipe_error ); + cleanup(); + return -1; + } + + /* Start the ncurses interface. */ + if( !nwipe_options.nogui ) + nwipe_gui_init(); + + if( nwipe_options.autonuke == 1 ) + { + /* Print the options window. */ + if( !nwipe_options.nogui ) + nwipe_gui_options(); + } + else + { + /* Get device selections from the user. */ + if( nwipe_options.nogui ) + { + printf( "--nogui option must be used with autonuke option\n" ); + cleanup(); + exit( 1 ); + } + else + { + nwipe_gui_select( nwipe_enumerated, c1 ); + } + } + + for( i = 0; i < nwipe_enumerated; i++ ) + { + /* Set the PRNG implementation, which must always come after the function nwipe_gui_select ! */ + c1[i]->prng = nwipe_options.prng; + c1[i]->prng_seed.length = 0; + c1[i]->prng_seed.s = 0; + c1[i]->prng_state = 0; + + /* Count the number of selected contexts. */ + if( c1[i]->select == NWIPE_SELECT_TRUE ) + { + nwipe_selected += 1; + } + + /* Initialise the wipe result value */ + c1[i]->result = 0; + } + + /* Pass the number selected to the struct for other threads */ + nwipe_misc_thread_data.nwipe_selected = nwipe_selected; + + /* Populate the array of selected contexts. */ + for( i = 0, j = 0; i < nwipe_enumerated; i++ ) + { + if( c1[i]->select == NWIPE_SELECT_TRUE ) + { + /* Copy the context. */ + c2[j++] = c1[i]; + } + } + + /* TODO: free c1 and c2 memory. */ + if( user_abort == 0 ) + { + /* Log the wipe options that have been selected immediately prior to the start of the wipe */ + nwipe_options_log(); + + /* The wipe has been initiated */ + global_wipe_status = 1; + + for( i = 0; i < nwipe_selected; i++ ) + { + /* A result buffer for the BLKGETSIZE64 ioctl. */ + u64 size64; + + /* Initialise the spinner character index */ + c2[i]->spinner_idx = 0; + + /* Initialise the start and end time of the wipe */ + c2[i]->start_time = 0; + c2[i]->end_time = 0; + + /* Initialise the wipe_status flag, -1 = wipe not yet started */ + c2[i]->wipe_status = -1; + + /* Open the file for reads and writes. */ + c2[i]->device_fd = open( c2[i]->device_name, O_RDWR ); + + /* Check the open() result. */ + if( c2[i]->device_fd < 0 ) + { + nwipe_perror( errno, __FUNCTION__, "open" ); + nwipe_log( NWIPE_LOG_WARNING, "Unable to open device '%s'.", c2[i]->device_name ); + c2[i]->select = NWIPE_SELECT_DISABLED; + continue; + } + + /* Stat the file. */ + if( fstat( c2[i]->device_fd, &c2[i]->device_stat ) != 0 ) + { + nwipe_perror( errno, __FUNCTION__, "fstat" ); + nwipe_log( NWIPE_LOG_ERROR, "Unable to stat file '%s'.", c2[i]->device_name ); + nwipe_error++; + continue; + } + + /* Check that the file is a block device. */ + if( !S_ISBLK( c2[i]->device_stat.st_mode ) ) + { + nwipe_log( NWIPE_LOG_ERROR, "'%s' is not a block device.", c2[i]->device_name ); + nwipe_error++; + continue; + } + + /* TODO: Lock the file for exclusive access. */ + /* + if( flock( c2[i]->device_fd, LOCK_EX | LOCK_NB ) != 0 ) + { + nwipe_perror( errno, __FUNCTION__, "flock" ); + nwipe_log( NWIPE_LOG_ERROR, "Unable to lock the '%s' file.", c2[i]->device_name ); + nwipe_error++; + continue; + } + */ + + /* Print serial number of device if it exists. */ + if( strlen( (const char*) c2[i]->device_serial_no ) ) + { + nwipe_log( NWIPE_LOG_NOTICE, "%s has serial number %s", c2[i]->device_name, c2[i]->device_serial_no ); + } + + /* Do sector size and block size checking. */ + if( ioctl( c2[i]->device_fd, BLKSSZGET, &c2[i]->device_sector_size ) == 0 ) + { + + if( ioctl( c2[i]->device_fd, BLKBSZGET, &c2[i]->device_block_size ) != 0 ) + { + nwipe_log( NWIPE_LOG_WARNING, "Device '%s' failed BLKBSZGET ioctl.", c2[i]->device_name ); + c2[i]->device_block_size = 0; + } + } + else + { + nwipe_log( NWIPE_LOG_WARNING, "Device '%s' failed BLKSSZGET ioctl.", c2[i]->device_name ); + c2[i]->device_sector_size = 0; + c2[i]->device_block_size = 0; + } + + /* The st_size field is zero for block devices. */ + /* ioctl( c2[i]->device_fd, BLKGETSIZE64, &c2[i]->device_size ); */ + + /* Seek to the end of the device to determine its size. */ + c2[i]->device_size = lseek( c2[i]->device_fd, 0, SEEK_END ); + + /* Also ask the driver for the device size. */ + /* if( ioctl( c2[i]->device_fd, BLKGETSIZE64, &size64 ) ) */ + if( ioctl( c2[i]->device_fd, _IOR( 0x12, 114, size_t ), &size64 ) ) + { + /* The ioctl failed. */ + fprintf( stderr, "Error: BLKGETSIZE64 failed on '%s'.\n", c2[i]->device_name ); + nwipe_log( NWIPE_LOG_ERROR, "BLKGETSIZE64 failed on '%s'.\n", c2[i]->device_name ); + nwipe_error++; + } + c2[i]->device_size = size64; + + /* Check whether the two size values agree. */ + if( c2[i]->device_size != size64 ) + { + /* This could be caused by the linux last-odd-block problem. */ + fprintf( stderr, "Error: Last-odd-block detected on '%s'.\n", c2[i]->device_name ); + nwipe_log( NWIPE_LOG_ERROR, "Last-odd-block detected on '%s'.", c2[i]->device_name ); + nwipe_error++; + } + + if( c2[i]->device_size == (long long) -1 ) + { + /* We cannot determine the size of this device. */ + nwipe_perror( errno, __FUNCTION__, "lseek" ); + nwipe_log( NWIPE_LOG_ERROR, "Unable to determine the size of '%s'.", c2[i]->device_name ); + nwipe_error++; + } + else + { + /* Reset the file pointer. */ + r = lseek( c2[i]->device_fd, 0, SEEK_SET ); + + if( r == (off64_t) -1 ) + { + nwipe_perror( errno, __FUNCTION__, "lseek" ); + nwipe_log( NWIPE_LOG_ERROR, "Unable to reset the '%s' file offset.", c2[i]->device_name ); + nwipe_error++; + } + } + + if( c2[i]->device_size == 0 ) + { + nwipe_log( NWIPE_LOG_ERROR, + "%s, sect/blk/dev %i/%i/%llu", + c2[i]->device_name, + c2[i]->device_sector_size, + c2[i]->device_block_size, + c2[i]->device_size ); + nwipe_error++; + continue; + } + else + { + nwipe_log( NWIPE_LOG_NOTICE, + "%s, sect/blk/dev %i/%i/%llu", + c2[i]->device_name, + c2[i]->device_sector_size, + c2[i]->device_block_size, + c2[i]->device_size ); + } + + /* Fork a child process. */ + errno = pthread_create( &c2[i]->thread, NULL, nwipe_options.method, (void*) c2[i] ); + if( errno ) + { + nwipe_perror( errno, __FUNCTION__, "pthread_create" ); + if( !nwipe_options.nogui ) + nwipe_gui_free(); + return errno; + } + else + { + wipe_threads_started = 1; + } + } + } + + /* Change the terminal mode to non-blocking input. */ + nodelay( stdscr, 0 ); + + /* Set getch to delay in order to slow screen updates. */ + halfdelay( NWIPE_KNOB_SLEEP * 10 ); + + /* Set up data structs to pass the GUI thread the data it needs. */ + nwipe_thread_data_ptr_t nwipe_gui_data; + if( !nwipe_options.nogui ) + { + nwipe_gui_data.c = c2; + nwipe_gui_data.nwipe_misc_thread_data = &nwipe_misc_thread_data; + /* Fork the GUI thread. */ + errno = pthread_create( &nwipe_gui_thread, NULL, nwipe_gui_status, &nwipe_gui_data ); + } + + /* Wait for all the wiping threads to finish, but don't wait if we receive the terminate signal */ + + /* set getch delay to 2/10th second. */ + halfdelay( 10 ); + + i = 0; + while( i < nwipe_selected && terminate_signal == 0 ) + { + if( i == nwipe_selected ) + { + break; + } + + if( c2[i]->wipe_status != 0 ) + { + i = 0; + } + else + { + i++; + continue; + } + sleep( 1 ); /* DO NOT REMOVE ! Stops the routine hogging CPU cycles */ + } + + if( terminate_signal != 1 ) + { + if( !nwipe_options.nowait && !nwipe_options.autopoweroff ) + { + do + { + sleep( 1 ); + + } while( terminate_signal != 1 ); + } + } + if( nwipe_options.verbose ) + { + nwipe_log( NWIPE_LOG_INFO, "Exit in progress" ); + } + /* Send a REQUEST for the wipe threads to be cancelled */ + for( i = 0; i < nwipe_selected; i++ ) + { + + if( c2[i]->thread ) + { + if( nwipe_options.verbose ) + { + nwipe_log( NWIPE_LOG_INFO, "Requesting wipe thread cancellation for %s", c2[i]->device_name ); + } + pthread_cancel( c2[i]->thread ); + } + } + + /* Kill the GUI thread */ + if( nwipe_gui_thread ) + { + if( nwipe_options.verbose ) + { + nwipe_log( NWIPE_LOG_INFO, "Cancelling the GUI thread." ); + } + + /* We don't want to use pthread_cancel as our GUI thread is aware of the control-c + * signal and will exit itself we just join the GUI thread and wait for confirmation + */ + r = pthread_join( nwipe_gui_thread, NULL ); + if( r != 0 ) + { + nwipe_log( NWIPE_LOG_WARNING, "main()>pthread_join():Error when waiting for GUI thread to cancel." ); + } + else + { + if( nwipe_options.verbose ) + { + nwipe_log( NWIPE_LOG_INFO, "GUI compute_stats thread has been cancelled" ); + } + } + } + + /* Release the gui. */ + if( !nwipe_options.nogui ) + { + nwipe_gui_free(); + } + + /* Now join the wipe threads and wait until they have terminated */ + any_threads_still_running = 1; + thread_timeout_counter = THREAD_CANCELLATION_TIMEOUT; + while( any_threads_still_running ) + { + /* quit waiting if we've tried 'thread_timeout_counter' times */ + if( thread_timeout_counter == 0 ) + { + break; + } + + any_threads_still_running = 0; + for( i = 0; i < nwipe_selected; i++ ) + { + if( c2[i]->thread ) + { + printf( "\nWaiting for wipe thread to cancel for %s\n", c2[i]->device_name ); + + /* Joins the thread and waits for completion before continuing */ + r = pthread_join( c2[i]->thread, NULL ); + if( r != 0 ) + { + nwipe_log( NWIPE_LOG_ERROR, + "Error joining the wipe thread when waiting for thread to cancel.", + c2[i]->device_name ); + + if( r == EDEADLK ) + { + nwipe_log( NWIPE_LOG_ERROR, + "Error joining the wipe thread: EDEADLK: Deadlock detected.", + c2[i]->device_name ); + } + else + { + if( r == EINVAL ) + { + nwipe_log( NWIPE_LOG_ERROR, + "Error joining the wipe thread: %s EINVAL: thread is not joinable.", + c2[i]->device_name ); + } + else + { + if( r == ESRCH ) + { + nwipe_log( NWIPE_LOG_ERROR, + "Error joining the wipe thread: %s ESRCH: no matching thread found", + c2[i]->device_name ); + } + } + } + + any_threads_still_running = 1; + } + else + { + c2[i]->thread = 0; /* Zero the thread so we know it's been cancelled */ + + if( nwipe_options.verbose ) + { + nwipe_log( NWIPE_LOG_INFO, "Wipe thread for device %s has terminated", c2[i]->device_name ); + } + + /* Close the device file descriptor. */ + close( c2[i]->device_fd ); + } + } + } + thread_timeout_counter--; + sleep( 1 ); + } + if( nwipe_options.verbose ) + { + for( i = 0; i < nwipe_selected; i++ ) + { + nwipe_log( NWIPE_LOG_DEBUG, + "Status: %s, result=%d, pass_errors=%llu, verify_errors=%llu, fsync_errors=%llu", + c2[i]->device_name, + c2[i]->result, + c2[i]->pass_errors, + c2[i]->verify_errors, + c2[i]->fsyncdata_errors ); + } + } + + /* if no wipe threads started then zero each selected drive result flag, + * as we don't need to report fatal/non fatal errors if no wipes were ever started ! */ + if( wipe_threads_started == 0 ) + { + for( i = 0; i < nwipe_selected; i++ ) + { + c2[i]->result = 0; + } + } + else + { + for( i = 0; i < nwipe_selected; i++ ) + { + /* Check for errors. */ + if( c2[i]->result != 0 || c2[i]->pass_errors != 0 || c2[i]->verify_errors != 0 + || c2[i]->fsyncdata_errors != 0 ) + { + /* If the run_method ever returns anything other than zero then makesure there is at least one pass + * error This is so that the log summary tables correctly show a failure when one occurs as it only + * shows pass, verification and fdatasync errors. */ + if( c2[i]->result != 0 ) + { + if( c2[i]->pass_errors == 0 ) + { + c2[i]->pass_errors = 1; + } + } + + nwipe_log( NWIPE_LOG_FATAL, + "Nwipe exited with errors on device = %s, see log for specific error\n", + c2[i]->device_name ); + nwipe_log( NWIPE_LOG_DEBUG, + "Status: %s, result=%d, pass_errors=%llu, verify_errors=%llu, fsync_errors=%llu", + c2[i]->device_name, + c2[i]->result, + c2[i]->pass_errors, + c2[i]->verify_errors, + c2[i]->fsyncdata_errors ); + non_fatal_errors_flag = 1; + return_status = 1; + } + } + } + + /* Generate and send the drive status summary to the log */ + nwipe_log_summary( c2, nwipe_selected ); + + /* Print a one line status message for the user */ + if( return_status == 0 || return_status == 1 ) + { + if( user_abort == 1 ) + { + if( global_wipe_status == 1 ) + { + nwipe_log( NWIPE_LOG_INFO, + "Nwipe was aborted by the user. Check the summary table for the drive status." ); + } + else + { + nwipe_log( NWIPE_LOG_INFO, "Nwipe was aborted by the user prior to the wipe starting." ); + } + } + else + { + if( fatal_errors_flag == 1 || non_fatal_errors_flag == 1 ) + { + nwipe_log( NWIPE_LOG_INFO, + "Nwipe exited with errors, check the log & summary table for individual drive status." ); + } + else + { + nwipe_log( NWIPE_LOG_INFO, "Nwipe successfully completed. See summary table for details." ); + } + } + } + + cleanup(); + + check_for_autopoweroff(); + + /* Exit. */ + return return_status; +} + +void* signal_hand( void* ptr ) +{ + int sig; + int hours; + int minutes; + int seconds; + + hours = 0; + minutes = 0; + seconds = 0; + + // Define signals that this handler should react to + sigset_t sigset; + sigemptyset( &sigset ); + sigaddset( &sigset, SIGHUP ); + sigaddset( &sigset, SIGTERM ); + sigaddset( &sigset, SIGQUIT ); + sigaddset( &sigset, SIGINT ); + sigaddset( &sigset, SIGUSR1 ); + + int i; + char eta[9]; + + /* Set up the structs we will use for the data required. */ + nwipe_thread_data_ptr_t* nwipe_thread_data_ptr; + nwipe_context_t** c; + nwipe_misc_thread_data_t* nwipe_misc_thread_data; + + /* Retrieve from the pointer passed to the function. */ + nwipe_thread_data_ptr = (nwipe_thread_data_ptr_t*) ptr; + c = nwipe_thread_data_ptr->c; + nwipe_misc_thread_data = nwipe_thread_data_ptr->nwipe_misc_thread_data; + + while( 1 ) + { + /* wait for a signal to arrive */ + sigwait( &sigset, &sig ); + + switch( sig ) + { + + // Log current status. All values are automatically updated by the GUI + case SIGUSR1: + compute_stats( ptr ); + + for( i = 0; i < nwipe_misc_thread_data->nwipe_selected; i++ ) + { + + if( c[i]->thread ) + { + char* status = ""; + switch( c[i]->pass_type ) + { + case NWIPE_PASS_FINAL_BLANK: + status = "[blanking]"; + break; + + case NWIPE_PASS_FINAL_OPS2: + status = "[OPS-II final]"; + break; + + case NWIPE_PASS_WRITE: + status = "[writing]"; + break; + + case NWIPE_PASS_VERIFY: + status = "[verifying]"; + break; + + case NWIPE_PASS_NONE: + break; + } + if( c[i]->sync_status ) + { + status = "[syncing]"; + } + + convert_seconds_to_hours_minutes_seconds( c[i]->eta, &hours, &minutes, &seconds ); + + nwipe_log( NWIPE_LOG_INFO, + "%s: %05.2f%%, round %i of %i, pass %i of %i, eta %02i:%02i:%02i, %s", + c[i]->device_name, + c[i]->round_percent, + c[i]->round_working, + c[i]->round_count, + c[i]->pass_working, + c[i]->pass_count, + hours, + minutes, + seconds, + status ); + } + else + { + if( c[i]->result == 0 ) + { + nwipe_log( NWIPE_LOG_INFO, "%s: Success", c[i]->device_name ); + } + else if( c[i]->signal ) + { + nwipe_log( + NWIPE_LOG_INFO, "%s: >>> FAILURE! <<<: signal %i", c[i]->device_name, c[i]->signal ); + } + else + { + nwipe_log( + NWIPE_LOG_INFO, "%s: >>> FAILURE! <<<: code %i", c[i]->device_name, c[i]->result ); + } + } + } + + break; + + case SIGHUP: + case SIGINT: + case SIGQUIT: + case SIGTERM: + /* Set termination flag for main() which will do housekeeping prior to exit */ + terminate_signal = 1; + + /* Set the user abort flag */ + user_abort = 1; + + /* Return control to the main thread, returning the signal received */ + return ( (void*) 0 ); + + break; + } + } + + return ( 0 ); +} + +int cleanup() +{ + int i; + extern int log_elements_displayed; + extern int log_elements_allocated; + extern char** log_lines; + + /* Print the logs held in memory. */ + for( i = log_elements_displayed; i < log_elements_allocated; i++ ) + { + printf( "%s\n", log_lines[i] ); + } + fflush( stdout ); + + /* Deallocate memory used by logging */ + if( log_elements_allocated != 0 ) + { + for( i = 0; i < log_elements_allocated; i++ ) + { + free( log_lines[i] ); + } + log_elements_allocated = 0; // zeroed just in case cleanup is called twice. + free( log_lines ); + } + + /* TODO: All other cleanup required */ + + return 0; +} +void check_for_autopoweroff( void ) +{ + char cmd[] = "shutdown -Ph +1 \"System going down in one minute\""; + FILE* fp; + int r; // A result buffer. + + /* User request auto power down ? */ + if( nwipe_options.autopoweroff == 1 ) + { + fp = popen( cmd, "r" ); + if( fp == NULL ) + { + nwipe_log( NWIPE_LOG_INFO, "Failed to autopoweroff to %s", cmd ); + return; + } + r = pclose( fp ); + } +} diff --git a/src/nwipe.h b/src/nwipe.h new file mode 100644 index 0000000..73f6f99 --- /dev/null +++ b/src/nwipe.h @@ -0,0 +1,114 @@ +/*. + * nwipe.h: The header file of the nwipe program. + * + * Copyright Darik Horn <dajhorn-dban@vanadac.com>. + * + * Modifications to original dwipe Copyright Andy Beverley <andy@andybev.com> + * + * 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, version 2. + * + * 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. + * + */ + +#ifndef NWIPE_H_ +#define NWIPE_H_ + +/* Function prototypes */ +int cleanup(); +void check_for_autopoweroff( void ); +void* signal_hand( void* ); + +#ifndef _LARGEFILE64_SOURCE +#define _LARGEFILE64_SOURCE +#endif + +#ifndef _FILE_OFFSET_BITS +#define _FILE_OFFSET_BITS 64 +#endif + +/* workaround for Fedora */ +#ifndef off64_t +#ifndef off_t +#define off64_t int64_t +#else +#define off64_t off_t +#endif +#endif + +/* Busybox headers. */ +#ifdef BB_VER +#include "busybox.h" +#endif + +/* System headers. */ +#include <errno.h> +#include <fcntl.h> +#include <getopt.h> +#include <math.h> +#include <pthread.h> +#include <regex.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/file.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <time.h> +#include <unistd.h> + +/*#include "config.h"*/ + +/* System errors. */ +extern int errno; + +/* 0=wipe not yet started, 1=wipe has been started by the user */ +extern int global_wipe_status; + +/* Ncurses headers. */ +#ifdef NCURSES_IN_SUBDIR +#include <ncurses/ncurses.h> +#else +#include <ncurses.h> +#endif +#ifdef PANEL_IN_SUBDIR +#include <ncurses/panel.h> +#else +#include <panel.h> +#endif + +/* Kernel device headers. */ +#include <linux/hdreg.h> + +/* These types are usually defined in <asm/types.h> for __KERNEL__ code. */ +typedef unsigned long long u64; +typedef unsigned long u32; +typedef unsigned short u16; +typedef unsigned char u8; + +/* This is required for ioctl BLKGETSIZE64, but it conflicts with <wait.h>. */ +/* #include <linux/fs.h> */ + +/* Define ioctls that cannot be included. */ +#define BLKSSZGET _IO( 0x12, 104 ) +#define BLKBSZGET _IOR( 0x12, 112, size_t ) +#define BLKBSZSET _IOW( 0x12, 113, size_t ) +#define BLKGETSIZE64 _IOR( 0x12, 114, sizeof( u64 ) ) + +#define THREAD_CANCELLATION_TIMEOUT 10 + +/* This is required for ioctl FDFLUSH. */ +#include <linux/fd.h> + +#endif /* NWIPE_H_ */ diff --git a/src/options.c b/src/options.c new file mode 100644 index 0000000..2930f3b --- /dev/null +++ b/src/options.c @@ -0,0 +1,596 @@ +/* + * options.c: Command line processing routines for nwipe. + * + * Copyright Darik Horn <dajhorn-dban@vanadac.com>. + * + * Modifications to original dwipe Copyright Andy Beverley <andy@andybev.com> + * + * 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, version 2. + * + * 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 "nwipe.h" +#include "context.h" +#include "method.h" +#include "prng.h" +#include "options.h" +#include "logging.h" +#include "version.h" + +/* The global options struct. */ +nwipe_options_t nwipe_options; + +int nwipe_options_parse( int argc, char** argv ) +{ + extern char* optarg; // The working getopt option argument. + extern int optind; // The working getopt index into argv. + extern int optopt; // The last unhandled getopt option. + extern int opterr; // The last getopt error number. + + extern nwipe_prng_t nwipe_twister; + extern nwipe_prng_t nwipe_isaac; + extern nwipe_prng_t nwipe_isaac64; + + /* The getopt() result holder. */ + int nwipe_opt; + + /* Excluded drive indexes */ + int idx_drive_chr; + int idx_optarg; + int idx_drive; + + /* Array index variable. */ + int i; + + /* The list of acceptable short options. */ + char nwipe_options_short[] = "Vvhl:m:p:qr:e:"; + + /* The list of acceptable long options. */ + static struct option nwipe_options_long[] = { + /* Set when the user wants to wipe without a confirmation prompt. */ + { "autonuke", no_argument, 0, 0 }, + + /* Set when the user wants to have the system powerdown on completion of wipe. */ + { "autopoweroff", no_argument, 0, 0 }, + + /* A GNU standard option. Corresponds to the 'h' short option. */ + { "help", no_argument, 0, 'h' }, + + /* The wipe method. Corresponds to the 'm' short option. */ + { "method", required_argument, 0, 'm' }, + + /* Log file. Corresponds to the 'l' short option. */ + { "logfile", required_argument, 0, 'l' }, + + /* Exclude devices, comma separated list */ + { "exclude", required_argument, 0, 'e' }, + + /* The Pseudo Random Number Generator. */ + { "prng", required_argument, 0, 'p' }, + + /* The number of times to run the method. */ + { "rounds", required_argument, 0, 'r' }, + + /* Whether to blank the disk after wiping. */ + { "noblank", no_argument, 0, 0 }, + + /* Whether to ignore all USB devices. */ + { "nousb", no_argument, 0, 0 }, + + /* Whether to exit after wiping or wait for a keypress. */ + { "nowait", no_argument, 0, 0 }, + + /* Whether to allow signals to interrupt a wipe. */ + { "nosignals", no_argument, 0, 0 }, + + /* Whether to display the gui. */ + { "nogui", no_argument, 0, 0 }, + + /* Whether to anonymize the serial numbers. */ + { "quiet", no_argument, 0, 'q' }, + + /* A flag to indicate whether the devices would be opened in sync mode. */ + { "sync", required_argument, 0, 0 }, + + /* Verify that wipe patterns are being written to the device. */ + { "verify", required_argument, 0, 0 }, + + /* Display program version. */ + { "verbose", no_argument, 0, 'v' }, + + /* Display program version. */ + { "version", no_argument, 0, 'V' }, + + /* Requisite padding for getopt(). */ + { 0, 0, 0, 0 } }; + + /* Set default options. */ + nwipe_options.autonuke = 0; + nwipe_options.autopoweroff = 0; + nwipe_options.method = &nwipe_dodshort; + nwipe_options.prng = ( sizeof( unsigned long int ) >= 8 ) ? &nwipe_isaac64 : &nwipe_isaac; + nwipe_options.rounds = 1; + nwipe_options.noblank = 0; + nwipe_options.nousb = 0; + nwipe_options.nowait = 0; + nwipe_options.nosignals = 0; + nwipe_options.nogui = 0; + nwipe_options.quiet = 0; + nwipe_options.sync = DEFAULT_SYNC_RATE; + nwipe_options.verbose = 0; + nwipe_options.verify = NWIPE_VERIFY_LAST; + memset( nwipe_options.logfile, '\0', sizeof( nwipe_options.logfile ) ); + + /* Initialise each of the strings in the excluded drives array */ + for( i = 0; i < MAX_NUMBER_EXCLUDED_DRIVES; i++ ) + { + nwipe_options.exclude[i][0] = 0; + } + + /* Parse command line options. */ + while( 1 ) + { + /* Get the next command line option with (3)getopt. */ + nwipe_opt = getopt_long( argc, argv, nwipe_options_short, nwipe_options_long, &i ); + + /* Break when we have processed all of the given options. */ + if( nwipe_opt < 0 ) + { + break; + } + + switch( nwipe_opt ) + { + case 0: /* Long options without short counterparts. */ + + if( strcmp( nwipe_options_long[i].name, "autonuke" ) == 0 ) + { + nwipe_options.autonuke = 1; + break; + } + + if( strcmp( nwipe_options_long[i].name, "autopoweroff" ) == 0 ) + { + nwipe_options.autopoweroff = 1; + break; + } + + if( strcmp( nwipe_options_long[i].name, "noblank" ) == 0 ) + { + nwipe_options.noblank = 1; + break; + } + + if( strcmp( nwipe_options_long[i].name, "nousb" ) == 0 ) + { + nwipe_options.nousb = 1; + break; + } + + if( strcmp( nwipe_options_long[i].name, "nowait" ) == 0 ) + { + nwipe_options.nowait = 1; + break; + } + + if( strcmp( nwipe_options_long[i].name, "nosignals" ) == 0 ) + { + nwipe_options.nosignals = 1; + break; + } + + if( strcmp( nwipe_options_long[i].name, "nogui" ) == 0 ) + { + nwipe_options.nogui = 1; + nwipe_options.nowait = 1; + break; + } + + if( strcmp( nwipe_options_long[i].name, "verbose" ) == 0 ) + { + nwipe_options.verbose = 1; + break; + } + + if( strcmp( nwipe_options_long[i].name, "sync" ) == 0 ) + { + if( sscanf( optarg, " %i", &nwipe_options.sync ) != 1 || nwipe_options.sync < 0 ) + { + fprintf( stderr, "Error: The sync argument must be a positive integer or zero.\n" ); + exit( EINVAL ); + } + break; + } + + if( strcmp( nwipe_options_long[i].name, "verify" ) == 0 ) + { + + if( strcmp( optarg, "0" ) == 0 || strcmp( optarg, "off" ) == 0 ) + { + nwipe_options.verify = NWIPE_VERIFY_NONE; + break; + } + + if( strcmp( optarg, "1" ) == 0 || strcmp( optarg, "last" ) == 0 ) + { + nwipe_options.verify = NWIPE_VERIFY_LAST; + break; + } + + if( strcmp( optarg, "2" ) == 0 || strcmp( optarg, "all" ) == 0 ) + { + nwipe_options.verify = NWIPE_VERIFY_ALL; + break; + } + + /* Else we do not know this verification level. */ + fprintf( stderr, "Error: Unknown verification level '%s'.\n", optarg ); + exit( EINVAL ); + } + + /* getopt_long should raise on invalid option, so we should never get here. */ + exit( EINVAL ); + + case 'm': /* Method option. */ + + if( strcmp( optarg, "dod522022m" ) == 0 || strcmp( optarg, "dod" ) == 0 ) + { + nwipe_options.method = &nwipe_dod522022m; + break; + } + + if( strcmp( optarg, "dodshort" ) == 0 || strcmp( optarg, "dod3pass" ) == 0 ) + { + nwipe_options.method = &nwipe_dodshort; + break; + } + + if( strcmp( optarg, "gutmann" ) == 0 ) + { + nwipe_options.method = &nwipe_gutmann; + break; + } + + if( strcmp( optarg, "ops2" ) == 0 ) + { + nwipe_options.method = &nwipe_ops2; + break; + } + + if( strcmp( optarg, "random" ) == 0 || strcmp( optarg, "prng" ) == 0 + || strcmp( optarg, "stream" ) == 0 ) + { + nwipe_options.method = &nwipe_random; + break; + } + + if( strcmp( optarg, "zero" ) == 0 || strcmp( optarg, "quick" ) == 0 ) + { + nwipe_options.method = &nwipe_zero; + break; + } + + if( strcmp( optarg, "one" ) == 0 ) + { + nwipe_options.method = &nwipe_one; + break; + } + + if( strcmp( optarg, "verify_zero" ) == 0 ) + { + nwipe_options.method = &nwipe_verify_zero; + break; + } + + if( strcmp( optarg, "verify_one" ) == 0 ) + { + nwipe_options.method = &nwipe_verify_one; + break; + } + + if( strcmp( optarg, "is5enh" ) == 0 ) + { + nwipe_options.method = &nwipe_is5enh; + break; + } + + /* Else we do not know this wipe method. */ + fprintf( stderr, "Error: Unknown wipe method '%s'.\n", optarg ); + exit( EINVAL ); + + case 'l': /* Log file option. */ + + nwipe_options.logfile[strlen( optarg )] = '\0'; + strncpy( nwipe_options.logfile, optarg, sizeof( nwipe_options.logfile ) ); + break; + + case 'e': /* exclude drives option */ + + idx_drive_chr = 0; + idx_optarg = 0; + idx_drive = 0; + + /* Create an array of excluded drives from the comma separated string */ + while( optarg[idx_optarg] != 0 && idx_drive < MAX_NUMBER_EXCLUDED_DRIVES ) + { + /* drop the leading '=' character if used */ + if( optarg[idx_optarg] == '=' && idx_optarg == 0 ) + { + idx_optarg++; + continue; + } + + if( optarg[idx_optarg] == ',' ) + { + /* terminate string and move onto next drive */ + nwipe_options.exclude[idx_drive++][idx_drive_chr] = 0; + idx_drive_chr = 0; + idx_optarg++; + } + else + { + if( idx_drive_chr < MAX_DRIVE_PATH_LENGTH ) + { + nwipe_options.exclude[idx_drive][idx_drive_chr++] = optarg[idx_optarg++]; + } + else + { + /* This section deals with file names that exceed MAX_DRIVE_PATH_LENGTH */ + nwipe_options.exclude[idx_drive][idx_drive_chr] = 0; + while( optarg[idx_optarg] != 0 && optarg[idx_optarg] != ',' ) + { + idx_optarg++; + } + } + } + } + break; + + case 'h': /* Display help. */ + + display_help(); + break; + + case 'p': /* PRNG option. */ + + if( strcmp( optarg, "mersenne" ) == 0 || strcmp( optarg, "twister" ) == 0 ) + { + nwipe_options.prng = &nwipe_twister; + break; + } + + if( strcmp( optarg, "isaac" ) == 0 ) + { + nwipe_options.prng = &nwipe_isaac; + break; + } + + if( strcmp( optarg, "isaac64" ) == 0 ) + { + nwipe_options.prng = &nwipe_isaac64; + break; + } + + /* Else we do not know this PRNG. */ + fprintf( stderr, "Error: Unknown prng '%s'.\n", optarg ); + exit( EINVAL ); + + case 'q': /* Anonymize serial numbers */ + + nwipe_options.quiet = 1; + break; + + case 'r': /* Rounds option. */ + + if( sscanf( optarg, " %i", &nwipe_options.rounds ) != 1 || nwipe_options.rounds < 1 ) + { + fprintf( stderr, "Error: The rounds argument must be a positive integer.\n" ); + exit( EINVAL ); + } + + break; + + case 'v': /* verbose */ + + nwipe_options.verbose = 1; + break; + + case 'V': /* Version option. */ + + printf( "%s version %s\n", program_name, version_string ); + exit( EXIT_SUCCESS ); + + default: + + /* Bogus command line argument. */ + display_help(); + exit( EINVAL ); + + } /* method */ + + } /* command line options */ + + /* Return the number of options that were processed. */ + return optind; +} + +void nwipe_options_log( void ) +{ + extern nwipe_prng_t nwipe_twister; + extern nwipe_prng_t nwipe_isaac; + extern nwipe_prng_t nwipe_isaac64; + + /** + * Prints a manifest of options to the log. + */ + + nwipe_log( NWIPE_LOG_NOTICE, "Program options are set as follows..." ); + + if( nwipe_options.autonuke ) + { + nwipe_log( NWIPE_LOG_NOTICE, " autonuke = %i (on)", nwipe_options.autonuke ); + } + else + { + nwipe_log( NWIPE_LOG_NOTICE, " autonuke = %i (off)", nwipe_options.autonuke ); + } + + if( nwipe_options.autopoweroff ) + { + nwipe_log( NWIPE_LOG_NOTICE, " autopoweroff = %i (on)", nwipe_options.autopoweroff ); + } + else + { + nwipe_log( NWIPE_LOG_NOTICE, " autopoweroff = %i (off)", nwipe_options.autopoweroff ); + } + + if( nwipe_options.noblank ) + { + nwipe_log( NWIPE_LOG_NOTICE, " do not perform a final blank pass" ); + } + + if( nwipe_options.nowait ) + { + nwipe_log( NWIPE_LOG_NOTICE, " do not wait for a key before exiting" ); + } + + if( nwipe_options.nosignals ) + { + nwipe_log( NWIPE_LOG_NOTICE, " do not allow signals to interrupt a wipe" ); + } + + if( nwipe_options.nogui ) + { + nwipe_log( NWIPE_LOG_NOTICE, " do not show GUI interface" ); + } + + nwipe_log( NWIPE_LOG_NOTICE, " banner = %s", banner ); + + if( nwipe_options.prng == &nwipe_twister ) + { + nwipe_log( NWIPE_LOG_NOTICE, " prng = Mersenne Twister" ); + } + else + { + if( nwipe_options.prng == &nwipe_isaac ) + { + nwipe_log( NWIPE_LOG_NOTICE, " prng = Isaac" ); + } + else + { + if( nwipe_options.prng == &nwipe_isaac64 ) + { + nwipe_log( NWIPE_LOG_NOTICE, " prng = Isaac64" ); + } + else + { + nwipe_log( NWIPE_LOG_NOTICE, " prng = Undefined" ); + } + } + } + + nwipe_log( NWIPE_LOG_NOTICE, " method = %s", nwipe_method_label( nwipe_options.method ) ); + nwipe_log( NWIPE_LOG_NOTICE, " quiet = %i", nwipe_options.quiet ); + nwipe_log( NWIPE_LOG_NOTICE, " rounds = %i", nwipe_options.rounds ); + nwipe_log( NWIPE_LOG_NOTICE, " sync = %i", nwipe_options.sync ); + + switch( nwipe_options.verify ) + { + case NWIPE_VERIFY_NONE: + nwipe_log( NWIPE_LOG_NOTICE, " verify = %i (off)", nwipe_options.verify ); + break; + + case NWIPE_VERIFY_LAST: + nwipe_log( NWIPE_LOG_NOTICE, " verify = %i (last pass)", nwipe_options.verify ); + break; + + case NWIPE_VERIFY_ALL: + nwipe_log( NWIPE_LOG_NOTICE, " verify = %i (all passes)", nwipe_options.verify ); + break; + + default: + nwipe_log( NWIPE_LOG_NOTICE, " verify = %i", nwipe_options.verify ); + break; + } +} + +void display_help() +{ + /** + * displays the help section to STDOUT and exits. + */ + + printf( "Usage: %s [options] [device1] [device2] ...\n", program_name ); + printf( "Options:\n" ); + /* Limit line length to a maximum of 80 characters so it looks good in 80x25 terminals i.e shredos */ + /* ___12345678901234567890123456789012345678901234567890123456789012345678901234567890< Do not exceed */ + puts( " -V, --version Prints the version number\n" ); + puts( " -v, --verbose Prints more messages to the log\n" ); + puts( " -h, --help Prints this help\n" ); + puts( " --autonuke If no devices have been specified on the command line," ); + puts( " starts wiping all devices immediately. If devices have" ); + puts( " been specified, starts wiping only those specified" ); + puts( " devices immediately.\n" ); + puts( " --autopoweroff Power off system on completion of wipe delayed for" ); + puts( " for one minute. During this one minute delay you can" ); + puts( " abort the shutdown by typing sudo shutdown -c\n" ); + printf( " --sync=NUM Will perform a sync after NUM writes (default: %d)\n", DEFAULT_SYNC_RATE ); + puts( " 0 - fdatasync after the disk is completely written" ); + puts( " fdatasync errors not detected until completion." ); + puts( " 0 is not recommended as disk errors may cause" ); + puts( " nwipe to appear to hang" ); + puts( " 1 - fdatasync after every write" ); + puts( " Warning: Lower values will reduce wipe speeds." ); + puts( " 1000 - fdatasync after 1000 writes etc.\n" ); + puts( " --verify=TYPE Whether to perform verification of erasure" ); + puts( " (default: last)" ); + puts( " off - Do not verify" ); + puts( " last - Verify after the last pass" ); + puts( " all - Verify every pass\n" ); + puts( " -m, --method=METHOD The wiping method. See man page for more details." ); + puts( " (default: dodshort)" ); + puts( " dod522022m / dod - 7 pass DOD 5220.22-M method" ); + puts( " dodshort / dod3pass - 3 pass DOD method" ); + puts( " gutmann - Peter Gutmann's Algorithm" ); + puts( " ops2 - RCMP TSSIT OPS-II" ); + puts( " random / prng / stream - PRNG Stream" ); + puts( " zero / quick - Overwrite with zeros" ); + puts( " one - Overwrite with ones (0xFF)" ); + puts( " verify_zero - Verifies disk is zero filled" ); + puts( " verify_one - Verifies disk is 0xFF filled\n" ); + puts( " -l, --logfile=FILE Filename to log to. Default is STDOUT\n" ); + puts( " -p, --prng=METHOD PRNG option (mersenne|twister|isaac|isaac64)\n" ); + puts( " -q, --quiet Anonymize logs and the GUI by removing unique data, i.e." ); + puts( " serial numbers, LU WWN Device ID, and SMBIOS/DMI data" ); + puts( " XXXXXX = S/N exists, ????? = S/N not obtainable\n" ); + puts( " -r, --rounds=NUM Number of times to wipe the device using the selected" ); + puts( " method (default: 1)\n" ); + puts( " --noblank Do NOT blank disk after wipe" ); + puts( " (default is to complete a final blank pass)\n" ); + puts( " --nowait Do NOT wait for a key before exiting" ); + puts( " (default is to wait)\n" ); + puts( " --nosignals Do NOT allow signals to interrupt a wipe" ); + puts( " (default is to allow)\n" ); + puts( " --nogui Do NOT show the GUI interface. Automatically invokes" ); + puts( " the nowait option. Must be used with the --autonuke" ); + puts( " option. Send SIGUSR1 to log current stats\n" ); + puts( " --nousb Do NOT show or wipe any USB devices whether in GUI" ); + puts( " mode, --nogui or --autonuke modes.\n" ); + puts( " -e, --exclude=DEVICES Up to ten comma separated devices to be excluded" ); + puts( " --exclude=/dev/sdc" ); + puts( " --exclude=/dev/sdc,/dev/sdd" ); + puts( " --exclude=/dev/sdc,/dev/sdd,/dev/mapper/cryptswap1\n" ); + puts( "" ); + exit( EXIT_SUCCESS ); +} diff --git a/src/options.h b/src/options.h new file mode 100644 index 0000000..f70a3ec --- /dev/null +++ b/src/options.h @@ -0,0 +1,72 @@ +/* + * options.h: Command line processing routines for nwipe. + * + * Copyright Darik Horn <dajhorn-dban@vanadac.com>. + * + * Modifications to original dwipe Copyright Andy Beverley <andy@andybev.com> + * + * 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, version 2. + * + * 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. + * + */ + +#ifndef OPTIONS_H_ +#define OPTIONS_H_ + +/* Program knobs. */ +#define NWIPE_KNOB_ENTROPY "/dev/urandom" +#define NWIPE_KNOB_IDENTITY_SIZE 512 +#define NWIPE_KNOB_LABEL_SIZE 128 +#define NWIPE_KNOB_LOADAVG "/proc/loadavg" +#define NWIPE_KNOB_LOG_BUFFERSIZE 1024 // Maximum length of a log event. +#define NWIPE_KNOB_PARTITIONS "/proc/partitions" +#define NWIPE_KNOB_PARTITIONS_PREFIX "/dev/" +#define NWIPE_KNOB_PRNG_STATE_LENGTH 512 // 128 words +#define NWIPE_KNOB_SCSI "/proc/scsi/scsi" +#define NWIPE_KNOB_SLEEP 1 +#define NWIPE_KNOB_STAT "/proc/stat" +#define MAX_NUMBER_EXCLUDED_DRIVES 10 +#define MAX_DRIVE_PATH_LENGTH 200 // e.g. /dev/sda is only 8 characters long, so 200 should be plenty. +#define DEFAULT_SYNC_RATE 100000 + +/* Function prototypes for loading options from the environment and command line. */ +int nwipe_options_parse( int argc, char** argv ); +void nwipe_options_log( void ); + +/* Function to display help text */ +void display_help(); + +typedef struct +{ + int autonuke; // Do not prompt the user for confirmation when set. + int autopoweroff; // Power off on completion of wipe + int noblank; // Do not perform a final blanking pass. + int nousb; // Do not show or wipe any USB devices. + int nowait; // Do not wait for a final key before exiting. + int nosignals; // Do not allow signals to interrupt a wipe. + int nogui; // Do not show the GUI. + char* banner; // The product banner shown on the top line of the screen. + void* method; // A function pointer to the wipe method that will be used. + char logfile[FILENAME_MAX]; // The filename to log the output to. + char exclude[MAX_NUMBER_EXCLUDED_DRIVES][MAX_DRIVE_PATH_LENGTH]; // Drives excluded from the search. + nwipe_prng_t* prng; // The pseudo random number generator implementation. + int quiet; // Anonymize serial numbers + int rounds; // The number of times that the wipe method should be called. + int sync; // A flag to indicate whether and how often writes should be sync'd. + int verbose; // Make log more verbose + nwipe_verify_t verify; // A flag to indicate whether writes should be verified. +} nwipe_options_t; + +extern nwipe_options_t nwipe_options; + +#endif /* OPTIONS_H_ */ diff --git a/src/pass.c b/src/pass.c new file mode 100644 index 0000000..3f6487a --- /dev/null +++ b/src/pass.c @@ -0,0 +1,866 @@ +/* + * pass.c: Routines that read and write patterns to block devices. + * + * Copyright Darik Horn <dajhorn-dban@vanadac.com>. + * + * Modifications to original dwipe Copyright Andy Beverley <andy@andybev.com> + * + * 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, version 2. + * + * 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. + * + */ + +#define _POSIX_C_SOURCE 200809L + +#include <stdint.h> +#include "nwipe.h" +#include "context.h" +#include "method.h" +#include "prng.h" +#include "options.h" +#include "pass.h" +#include "logging.h" +#include "gui.h" + +int nwipe_random_verify( nwipe_context_t* c ) +{ + /** + * Verifies that a random pass was correctly written to the device. + * + */ + + /* The result holder. */ + int r; + + /* The IO size. */ + size_t blocksize; + + /* The result buffer for calls to lseek. */ + off64_t offset; + + /* The input buffer. */ + char* b; + + /* The pattern buffer that is used to check the input buffer. */ + char* d; + + /* The number of bytes remaining in the pass. */ + u64 z = c->device_size; + + if( c->prng_seed.s == NULL ) + { + nwipe_log( NWIPE_LOG_SANITY, "Null seed pointer." ); + return -1; + } + + if( c->prng_seed.length <= 0 ) + { + nwipe_log( NWIPE_LOG_SANITY, "The entropy length member is %i.", c->prng_seed.length ); + return -1; + } + + /* Create the input buffer. */ + b = malloc( c->device_stat.st_blksize ); + + /* Check the memory allocation. */ + if( !b ) + { + nwipe_perror( errno, __FUNCTION__, "malloc" ); + nwipe_log( NWIPE_LOG_FATAL, "Unable to allocate memory for the input buffer." ); + return -1; + } + + /* Create the pattern buffer */ + d = malloc( c->device_stat.st_blksize ); + + /* Check the memory allocation. */ + if( !d ) + { + nwipe_perror( errno, __FUNCTION__, "malloc" ); + nwipe_log( NWIPE_LOG_FATAL, "Unable to allocate memory for the pattern buffer." ); + free( b ); + return -1; + } + + /* Reset the file pointer. */ + offset = lseek( c->device_fd, 0, SEEK_SET ); + + /* Reset the pass byte counter. */ + c->pass_done = 0; + + if( offset == (off64_t) -1 ) + { + nwipe_perror( errno, __FUNCTION__, "lseek" ); + nwipe_log( NWIPE_LOG_FATAL, "Unable to reset the '%s' file offset.", c->device_name ); + free( b ); + free( d ); + return -1; + } + + if( offset != 0 ) + { + /* This is system insanity. */ + nwipe_log( NWIPE_LOG_SANITY, "lseek() returned a bogus offset on '%s'.", c->device_name ); + free( b ); + free( d ); + return -1; + } + + /* Tell our parent that we are syncing the device. */ + c->sync_status = 1; + + /* Sync the device. */ + r = fdatasync( c->device_fd ); + + /* Tell our parent that we have finished syncing the device. */ + c->sync_status = 0; + + if( r != 0 ) + { + /* FIXME: Is there a better way to handle this? */ + nwipe_perror( errno, __FUNCTION__, "fdatasync" ); + nwipe_log( NWIPE_LOG_WARNING, "Buffer flush failure on '%s'.", c->device_name ); + c->fsyncdata_errors++; + } + + /* Reseed the PRNG. */ + c->prng->init( &c->prng_state, &c->prng_seed ); + + while( z > 0 ) + { + if( c->device_stat.st_blksize <= z ) + { + blocksize = c->device_stat.st_blksize; + } + else + { + /* This is a seatbelt for buggy drivers and programming errors because */ + /* the device size should always be an even multiple of its blocksize. */ + blocksize = z; + nwipe_log( NWIPE_LOG_WARNING, + "%s: The size of '%s' is not a multiple of its block size %i.", + __FUNCTION__, + c->device_name, + c->device_stat.st_blksize ); + } + + /* Fill the output buffer with the random pattern. */ + c->prng->read( &c->prng_state, d, blocksize ); + + /* Read the buffer in from the device. */ + r = read( c->device_fd, b, blocksize ); + + /* Check the result. */ + if( r < 0 ) + { + nwipe_perror( errno, __FUNCTION__, "read" ); + nwipe_log( NWIPE_LOG_ERROR, "Unable to read from '%s'.", c->device_name ); + return -1; + } + + /* Check for a partial read. */ + if( r != blocksize ) + { + /* TODO: Handle a partial read. */ + + /* The number of bytes that were not read. */ + int s = blocksize - r; + + nwipe_log( + NWIPE_LOG_WARNING, "%s: Partial read from '%s', %i bytes short.", __FUNCTION__, c->device_name, s ); + + /* Increment the error count. */ + c->verify_errors += 1; + + /* Bump the file pointer to the next block. */ + offset = lseek( c->device_fd, s, SEEK_CUR ); + + if( offset == (off64_t) -1 ) + { + nwipe_perror( errno, __FUNCTION__, "lseek" ); + nwipe_log( + NWIPE_LOG_ERROR, "Unable to bump the '%s' file offset after a partial read.", c->device_name ); + return -1; + } + + } /* partial read */ + + /* Compare buffer contents. */ + if( memcmp( b, d, blocksize ) != 0 ) + { + c->verify_errors += 1; + } + + /* Decrement the bytes remaining in this pass. */ + z -= r; + + /* Increment the total progress counters. */ + c->pass_done += r; + c->round_done += r; + + pthread_testcancel(); + + } /* while bytes remaining */ + + /* Release the buffers. */ + free( b ); + free( d ); + + /* We're done. */ + return 0; + +} /* nwipe_random_verify */ + +int nwipe_random_pass( NWIPE_METHOD_SIGNATURE ) +{ + /** + * Writes a random pattern to the device. + * + */ + + /* The result holder. */ + int r; + + /* The IO size. */ + size_t blocksize; + + /* The result buffer for calls to lseek. */ + off64_t offset; + + /* The output buffer. */ + char* b; + + /* The number of bytes remaining in the pass. */ + u64 z = c->device_size; + + /* Number of writes to do before a fdatasync. */ + int syncRate = nwipe_options.sync; + + /* Counter to track when to do a fdatasync. */ + int i = 0; + + /* general index counter */ + int idx; + + if( c->prng_seed.s == NULL ) + { + nwipe_log( NWIPE_LOG_SANITY, "__FUNCTION__: Null seed pointer." ); + return -1; + } + + if( c->prng_seed.length <= 0 ) + { + nwipe_log( NWIPE_LOG_SANITY, "__FUNCTION__: The entropy length member is %i.", c->prng_seed.length ); + return -1; + } + + /* Create the initialised output buffer. Initialised because we don't want memory leaks + * to disk in the event of some future undetected bug in a prng or its implementation. */ + b = calloc( c->device_stat.st_blksize, sizeof( char ) ); + + /* Check the memory allocation. */ + if( !b ) + { + nwipe_perror( errno, __FUNCTION__, "malloc" ); + nwipe_log( NWIPE_LOG_FATAL, "Unable to allocate memory for the output buffer." ); + return -1; + } + + /* Seed the PRNG. */ + c->prng->init( &c->prng_state, &c->prng_seed ); + + /* Reset the file pointer. */ + offset = lseek( c->device_fd, 0, SEEK_SET ); + + /* Reset the pass byte counter. */ + c->pass_done = 0; + + if( offset == (off64_t) -1 ) + { + nwipe_perror( errno, __FUNCTION__, "lseek" ); + nwipe_log( NWIPE_LOG_FATAL, "Unable to reset the '%s' file offset.", c->device_name ); + free( b ); + return -1; + } + + if( offset != 0 ) + { + /* This is system insanity. */ + nwipe_log( NWIPE_LOG_SANITY, "__FUNCTION__: lseek() returned a bogus offset on '%s'.", c->device_name ); + free( b ); + return -1; + } + + while( z > 0 ) + { + if( c->device_stat.st_blksize <= z ) + { + blocksize = c->device_stat.st_blksize; + } + else + { + /* This is a seatbelt for buggy drivers and programming errors because */ + /* the device size should always be an even multiple of its blocksize. */ + blocksize = z; + nwipe_log( NWIPE_LOG_WARNING, + "%s: The size of '%s' is not a multiple of its block size %i.", + __FUNCTION__, + c->device_name, + c->device_stat.st_blksize ); + } + + /* Fill the output buffer with the random pattern. */ + c->prng->read( &c->prng_state, b, blocksize ); + + /* For the first block only, check the prng actually wrote something to the buffer */ + if( z == c->device_size ) + { + idx = c->device_stat.st_blksize; + while( idx > 0 ) + { + if( b[idx] != 0 ) + { + nwipe_log( NWIPE_LOG_NOTICE, "prng stream is active" ); + break; + } + idx--; + } + if( idx == 0 ) + { + nwipe_log( NWIPE_LOG_FATAL, "ERROR, prng wrote nothing to the buffer" ); + return -1; + } + } + + /* Write the next block out to the device. */ + r = write( c->device_fd, b, blocksize ); + + /* Check the result for a fatal error. */ + if( r < 0 ) + { + nwipe_perror( errno, __FUNCTION__, "write" ); + nwipe_log( NWIPE_LOG_FATAL, "Unable to read from '%s'.", c->device_name ); + return -1; + } + + /* Check for a partial write. */ + if( r != blocksize ) + { + /* TODO: Handle a partial write. */ + + /* The number of bytes that were not written. */ + int s = blocksize - r; + + /* Increment the error count by the number of bytes that were not written. */ + c->pass_errors += s; + + nwipe_log( NWIPE_LOG_WARNING, "Partial write on '%s', %i bytes short.", c->device_name, s ); + + /* Bump the file pointer to the next block. */ + offset = lseek( c->device_fd, s, SEEK_CUR ); + + if( offset == (off64_t) -1 ) + { + nwipe_perror( errno, __FUNCTION__, "lseek" ); + nwipe_log( + NWIPE_LOG_ERROR, "Unable to bump the '%s' file offset after a partial write.", c->device_name ); + return -1; + } + + } /* partial write */ + + /* Decrement the bytes remaining in this pass. */ + z -= r; + + /* Increment the total progress counters. */ + c->pass_done += r; + c->round_done += r; + + /* Perodic Sync */ + if( syncRate > 0 ) + { + i++; + + if( i >= syncRate ) + { + /* Tell our parent that we are syncing the device. */ + c->sync_status = 1; + + /* Sync the device. */ + r = fdatasync( c->device_fd ); + + /* Tell our parent that we have finished syncing the device. */ + c->sync_status = 0; + + if( r != 0 ) + { + nwipe_perror( errno, __FUNCTION__, "fdatasync" ); + nwipe_log( NWIPE_LOG_WARNING, "Buffer flush failure on '%s'.", c->device_name ); + nwipe_log( NWIPE_LOG_WARNING, "Wrote %llu bytes on '%s'.", c->pass_done, c->device_name ); + c->fsyncdata_errors++; + free( b ); + return -1; + } + + i = 0; + } + } + + pthread_testcancel(); + + } /* remaining bytes */ + + /* Release the output buffer. */ + free( b ); + + /* Tell our parent that we are syncing the device. */ + c->sync_status = 1; + + /* Sync the device. */ + r = fdatasync( c->device_fd ); + + /* Tell our parent that we have finished syncing the device. */ + c->sync_status = 0; + + if( r != 0 ) + { + /* FIXME: Is there a better way to handle this? */ + nwipe_perror( errno, __FUNCTION__, "fdatasync" ); + nwipe_log( NWIPE_LOG_WARNING, "Buffer flush failure on '%s'.", c->device_name ); + c->fsyncdata_errors++; + } + + /* We're done. */ + return 0; + +} /* nwipe_random_pass */ + +int nwipe_static_verify( NWIPE_METHOD_SIGNATURE, nwipe_pattern_t* pattern ) +{ + /** + * Verifies that a static pass was correctly written to the device. + */ + + /* The result holder. */ + int r; + + /* The IO size. */ + size_t blocksize; + + /* The result buffer for calls to lseek. */ + off64_t offset; + + /* The input buffer. */ + char* b; + + /* The pattern buffer that is used to check the input buffer. */ + char* d; + + /* A pointer into the pattern buffer. */ + char* q; + + /* The pattern buffer window offset. */ + int w = 0; + + /* The number of bytes remaining in the pass. */ + u64 z = c->device_size; + + if( pattern == NULL ) + { + /* Caught insanity. */ + nwipe_log( NWIPE_LOG_SANITY, "nwipe_static_verify: Null entropy pointer." ); + return -1; + } + + if( pattern->length <= 0 ) + { + /* Caught insanity. */ + nwipe_log( NWIPE_LOG_SANITY, "nwipe_static_verify: The pattern length member is %i.", pattern->length ); + return -1; + } + + /* Create the input buffer. */ + b = malloc( c->device_stat.st_blksize ); + + /* Check the memory allocation. */ + if( !b ) + { + nwipe_perror( errno, __FUNCTION__, "malloc" ); + nwipe_log( NWIPE_LOG_FATAL, "Unable to allocate memory for the input buffer." ); + return -1; + } + + /* Create the pattern buffer */ + d = malloc( c->device_stat.st_blksize + pattern->length * 2 ); + + /* Check the memory allocation. */ + if( !d ) + { + nwipe_perror( errno, __FUNCTION__, "malloc" ); + nwipe_log( NWIPE_LOG_FATAL, "Unable to allocate memory for the pattern buffer." ); + free( b ); + return -1; + } + + for( q = d; q < d + c->device_stat.st_blksize + pattern->length; q += pattern->length ) + { + /* Fill the pattern buffer with the pattern. */ + memcpy( q, pattern->s, pattern->length ); + } + + /* Tell our parent that we are syncing the device. */ + c->sync_status = 1; + + /* Sync the device. */ + r = fdatasync( c->device_fd ); + + /* Tell our parent that we have finished syncing the device. */ + c->sync_status = 0; + + if( r != 0 ) + { + /* FIXME: Is there a better way to handle this? */ + nwipe_perror( errno, __FUNCTION__, "fdatasync" ); + nwipe_log( NWIPE_LOG_WARNING, "Buffer flush failure on '%s'.", c->device_name ); + c->fsyncdata_errors++; + } + + /* Reset the file pointer. */ + offset = lseek( c->device_fd, 0, SEEK_SET ); + + /* Reset the pass byte counter. */ + c->pass_done = 0; + + if( offset == (off64_t) -1 ) + { + nwipe_perror( errno, __FUNCTION__, "lseek" ); + nwipe_log( NWIPE_LOG_FATAL, "Unable to reset the '%s' file offset.", c->device_name ); + free( b ); + return -1; + } + + if( offset != 0 ) + { + /* This is system insanity. */ + nwipe_log( NWIPE_LOG_SANITY, "nwipe_static_verify: lseek() returned a bogus offset on '%s'.", c->device_name ); + free( b ); + free( d ); + return -1; + } + + while( z > 0 ) + { + if( c->device_stat.st_blksize <= z ) + { + blocksize = c->device_stat.st_blksize; + } + else + { + /* This is a seatbelt for buggy drivers and programming errors because */ + /* the device size should always be an even multiple of its blocksize. */ + blocksize = z; + nwipe_log( NWIPE_LOG_WARNING, + "%s: The size of '%s' is not a multiple of its block size %i.", + __FUNCTION__, + c->device_name, + c->device_stat.st_blksize ); + } + + /* Fill the output buffer with the random pattern. */ + /* Read the buffer in from the device. */ + r = read( c->device_fd, b, blocksize ); + + /* Check the result. */ + if( r < 0 ) + { + nwipe_perror( errno, __FUNCTION__, "read" ); + nwipe_log( NWIPE_LOG_ERROR, "Unable to read from '%s'.", c->device_name ); + return -1; + } + + /* Check for a partial read. */ + if( r == blocksize ) + { + /* Check every byte in the buffer. */ + if( memcmp( b, &d[w], r ) != 0 ) + { + c->verify_errors += 1; + } + } + else + { + /* The number of bytes that were not read. */ + int s = blocksize - r; + + /* TODO: Handle a partial read. */ + + /* Increment the error count. */ + c->verify_errors += 1; + + nwipe_log( NWIPE_LOG_WARNING, "Partial read on '%s', %i bytes short.", c->device_name, s ); + + /* Bump the file pointer to the next block. */ + offset = lseek( c->device_fd, s, SEEK_CUR ); + + if( offset == (off64_t) -1 ) + { + nwipe_perror( errno, __FUNCTION__, "lseek" ); + nwipe_log( + NWIPE_LOG_ERROR, "Unable to bump the '%s' file offset after a partial read.", c->device_name ); + return -1; + } + + } /* partial read */ + + /* Adjust the window. */ + w = ( c->device_stat.st_blksize + w ) % pattern->length; + + /* Intuition check: + * If the pattern length evenly divides the block size + * then ( w == 0 ) always. + */ + + /* Decrement the bytes remaining in this pass. */ + z -= r; + + /* Increment the total progress counters. */ + c->pass_done += r; + c->round_done += r; + + pthread_testcancel(); + + } /* while bytes remaining */ + + /* Release the buffers. */ + free( b ); + free( d ); + + /* We're done. */ + return 0; + +} /* nwipe_static_verify */ + +int nwipe_static_pass( NWIPE_METHOD_SIGNATURE, nwipe_pattern_t* pattern ) +{ + /** + * Writes a static pattern to the device. + */ + + /* The result holder. */ + int r; + + /* The IO size. */ + size_t blocksize; + + /* The result buffer for calls to lseek. */ + off64_t offset; + + /* The output buffer. */ + char* b; + + /* A pointer into the output buffer. */ + char* p; + + /* The output buffer window offset. */ + int w = 0; + + /* The number of bytes remaining in the pass. */ + u64 z = c->device_size; + + /* Number of writes to do before a fdatasync. */ + int syncRate = nwipe_options.sync; + + /* Counter to track when to do a fdatasync. */ + int i = 0; + + if( pattern == NULL ) + { + /* Caught insanity. */ + nwipe_log( NWIPE_LOG_SANITY, "__FUNCTION__: Null pattern pointer." ); + return -1; + } + + if( pattern->length <= 0 ) + { + /* Caught insanity. */ + nwipe_log( NWIPE_LOG_SANITY, "__FUNCTION__: The pattern length member is %i.", pattern->length ); + return -1; + } + + /* Create the output buffer. */ + b = malloc( c->device_stat.st_blksize + pattern->length * 2 ); + + /* Check the memory allocation. */ + if( !b ) + { + nwipe_perror( errno, __FUNCTION__, "malloc" ); + nwipe_log( NWIPE_LOG_FATAL, "Unable to allocate memory for the pattern buffer." ); + return -1; + } + + for( p = b; p < b + c->device_stat.st_blksize + pattern->length; p += pattern->length ) + { + /* Fill the output buffer with the pattern. */ + memcpy( p, pattern->s, pattern->length ); + } + /// + /* Reset the file pointer. */ + offset = lseek( c->device_fd, 0, SEEK_SET ); + + /* Reset the pass byte counter. */ + c->pass_done = 0; + + if( offset == (off64_t) -1 ) + { + nwipe_perror( errno, __FUNCTION__, "lseek" ); + nwipe_log( NWIPE_LOG_FATAL, "Unable to reset the '%s' file offset.", c->device_name ); + return -1; + } + + if( offset != 0 ) + { + /* This is system insanity. */ + nwipe_log( NWIPE_LOG_SANITY, "__FUNCTION__: lseek() returned a bogus offset on '%s'.", c->device_name ); + return -1; + } + + while( z > 0 ) + { + if( c->device_stat.st_blksize <= z ) + { + blocksize = c->device_stat.st_blksize; + } + else + { + /* This is a seatbelt for buggy drivers and programming errors because */ + /* the device size should always be an even multiple of its blocksize. */ + blocksize = z; + nwipe_log( NWIPE_LOG_WARNING, + "%s: The size of '%s' is not a multiple of its block size %i.", + __FUNCTION__, + c->device_name, + c->device_stat.st_blksize ); + } + + /* Fill the output buffer with the random pattern. */ + /* Write the next block out to the device. */ + r = write( c->device_fd, &b[w], blocksize ); + + /* Check the result for a fatal error. */ + if( r < 0 ) + { + nwipe_perror( errno, __FUNCTION__, "write" ); + nwipe_log( NWIPE_LOG_FATAL, "Unable to write to '%s'.", c->device_name ); + return -1; + } + + /* Check for a partial write. */ + if( r != blocksize ) + { + /* TODO: Handle a partial write. */ + + /* The number of bytes that were not written. */ + int s = blocksize - r; + + /* Increment the error count. */ + c->pass_errors += s; + + nwipe_log( NWIPE_LOG_WARNING, "Partial write on '%s', %i bytes short.", c->device_name, s ); + + /* Bump the file pointer to the next block. */ + offset = lseek( c->device_fd, s, SEEK_CUR ); + + if( offset == (off64_t) -1 ) + { + nwipe_perror( errno, __FUNCTION__, "lseek" ); + nwipe_log( + NWIPE_LOG_ERROR, "Unable to bump the '%s' file offset after a partial write.", c->device_name ); + return -1; + } + + } /* partial write */ + + /* Adjust the window. */ + w = ( c->device_stat.st_blksize + w ) % pattern->length; + + /* Intuition check: + * + * If the pattern length evenly divides the block size + * then ( w == 0 ) always. + */ + + /* Decrement the bytes remaining in this pass. */ + z -= r; + + /* Increment the total progress counterr. */ + c->pass_done += r; + c->round_done += r; + + /* Perodic Sync */ + if( syncRate > 0 ) + { + i++; + + if( i >= syncRate ) + { + /* Tell our parent that we are syncing the device. */ + c->sync_status = 1; + + /* Sync the device. */ + r = fdatasync( c->device_fd ); + + /* Tell our parent that we have finished syncing the device. */ + c->sync_status = 0; + + if( r != 0 ) + { + nwipe_perror( errno, __FUNCTION__, "fdatasync" ); + nwipe_log( NWIPE_LOG_WARNING, "Buffer flush failure on '%s'.", c->device_name ); + nwipe_log( NWIPE_LOG_WARNING, "Wrote %llu bytes on '%s'.", c->pass_done, c->device_name ); + c->fsyncdata_errors++; + free( b ); + return -1; + } + + i = 0; + } + } + + pthread_testcancel(); + + } /* remaining bytes */ + + /* Tell our parent that we are syncing the device. */ + c->sync_status = 1; + + /* Sync the device. */ + r = fdatasync( c->device_fd ); + + /* Tell our parent that we have finished syncing the device. */ + c->sync_status = 0; + + if( r != 0 ) + { + /* FIXME: Is there a better way to handle this? */ + nwipe_perror( errno, __FUNCTION__, "fdatasync" ); + nwipe_log( NWIPE_LOG_WARNING, "Buffer flush failure on '%s'.", c->device_name ); + c->fsyncdata_errors++; + } + + /* Release the output buffer. */ + free( b ); + + /* We're done. */ + return 0; + +} /* nwipe_static_pass */ diff --git a/src/pass.h b/src/pass.h new file mode 100644 index 0000000..8f27f43 --- /dev/null +++ b/src/pass.h @@ -0,0 +1,33 @@ +/* + * pass.h: Routines that read and write patterns to block devices. + * + * Copyright Darik Horn <dajhorn-dban@vanadac.com>. + * + * Modifications to original dwipe Copyright Andy Beverley <andy@andybev.com> + * + * 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, version 2. + * + * 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. + * + */ + +#ifndef PASS_H_ +#define PASS_H_ + +int nwipe_random_pass( nwipe_context_t* c ); +int nwipe_random_verify( nwipe_context_t* c ); +int nwipe_static_pass( nwipe_context_t* c, nwipe_pattern_t* pattern ); +int nwipe_static_verify( nwipe_context_t* c, nwipe_pattern_t* pattern ); + +void test_functionn( int count, nwipe_context_t** c ); + +#endif /* PASS_H_ */ diff --git a/src/prng.c b/src/prng.c new file mode 100644 index 0000000..72c9305 --- /dev/null +++ b/src/prng.c @@ -0,0 +1,252 @@ +/* + * prng.c: Pseudo Random Number Generator abstractions for nwipe. + * + * Copyright Darik Horn <dajhorn-dban@vanadac.com>. + * + * 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, version 2. + * + * 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 "nwipe.h" +#include "prng.h" +#include "context.h" +#include "logging.h" + +#include "mt19937ar-cok/mt19937ar-cok.h" +#include "isaac_rand/isaac_rand.h" +#include "isaac_rand/isaac64.h" + +nwipe_prng_t nwipe_twister = { "Mersenne Twister (mt19937ar-cok)", nwipe_twister_init, nwipe_twister_read }; + +nwipe_prng_t nwipe_isaac = { "ISAAC (rand.c 20010626)", nwipe_isaac_init, nwipe_isaac_read }; +nwipe_prng_t nwipe_isaac64 = { "ISAAC-64 (isaac64.c)", nwipe_isaac64_init, nwipe_isaac64_read }; + +/* Print given number of bytes from unsigned integer number to a byte stream buffer starting with low-endian. */ +static inline void u32_to_buffer( u8* restrict buffer, u32 val, const int len ) +{ + for( int i = 0; i < len; ++i ) + { + buffer[i] = ( u8 )( val & 0xFFUL ); + val >>= 8; + } +} +static inline void u64_to_buffer( u8* restrict buffer, u64 val, const int len ) +{ + for( int i = 0; i < len; ++i ) + { + buffer[i] = ( u8 )( val & 0xFFULL ); + val >>= 8; + } +} +static inline u32 isaac_nextval( randctx* restrict ctx ) +{ + if( ctx->randcnt == 0 ) + { + isaac( ctx ); + ctx->randcnt = RANDSIZ; + } + ctx->randcnt--; + return ctx->randrsl[ctx->randcnt]; +} +static inline u64 isaac64_nextval( rand64ctx* restrict ctx ) +{ + if( ctx->randcnt == 0 ) + { + isaac64( ctx ); + ctx->randcnt = RANDSIZ; + } + ctx->randcnt--; + return ctx->randrsl[ctx->randcnt]; +} + +int nwipe_twister_init( NWIPE_PRNG_INIT_SIGNATURE ) +{ + nwipe_log( NWIPE_LOG_NOTICE, "Initialising Mersenne Twister prng" ); + + if( *state == NULL ) + { + /* This is the first time that we have been called. */ + *state = malloc( sizeof( twister_state_t ) ); + } + twister_init( (twister_state_t*) *state, (u32*) ( seed->s ), seed->length / sizeof( u32 ) ); + return 0; +} + +int nwipe_twister_read( NWIPE_PRNG_READ_SIGNATURE ) +{ + u8* restrict bufpos = buffer; + size_t words = count / SIZE_OF_TWISTER; // the values of twister_genrand_int32 is strictly 4 bytes + + /* Twister returns 4-bytes per call, so progress by 4 bytes. */ + for( size_t ii = 0; ii < words; ++ii ) + { + u32_to_buffer( bufpos, twister_genrand_int32( (twister_state_t*) *state ), SIZE_OF_TWISTER ); + bufpos += SIZE_OF_TWISTER; + } + + /* If there is some remainder copy only relevant number of bytes to not + * overflow the buffer. */ + const size_t remain = count % SIZE_OF_TWISTER; // SIZE_OF_TWISTER is strictly 4 bytes + if( remain > 0 ) + { + u32_to_buffer( bufpos, twister_genrand_int32( (twister_state_t*) *state ), remain ); + } + + return 0; +} + +int nwipe_isaac_init( NWIPE_PRNG_INIT_SIGNATURE ) +{ + int count; + randctx* isaac_state = *state; + + nwipe_log( NWIPE_LOG_NOTICE, "Initialising Isaac prng" ); + + if( *state == NULL ) + { + /* This is the first time that we have been called. */ + *state = malloc( sizeof( randctx ) ); + isaac_state = *state; + + /* Check the memory allocation. */ + if( isaac_state == 0 ) + { + nwipe_perror( errno, __FUNCTION__, "malloc" ); + nwipe_log( NWIPE_LOG_FATAL, "Unable to allocate memory for the isaac state." ); + return -1; + } + } + + /* Take the minimum of the isaac seed size and available entropy. */ + if( sizeof( isaac_state->randrsl ) < seed->length ) + { + count = sizeof( isaac_state->randrsl ); + } + else + { + memset( isaac_state->randrsl, 0, sizeof( isaac_state->randrsl ) ); + count = seed->length; + } + + if( count == 0 ) + { + /* Start ISACC without a seed. */ + randinit( isaac_state, 0 ); + } + else + { + /* Seed the ISAAC state with entropy. */ + memcpy( isaac_state->randrsl, seed->s, count ); + + /* The second parameter indicates that randrsl is non-empty. */ + randinit( isaac_state, 1 ); + } + + return 0; +} + +int nwipe_isaac_read( NWIPE_PRNG_READ_SIGNATURE ) +{ + randctx* isaac_state = *state; + u8* restrict bufpos = buffer; + size_t words = count / SIZE_OF_ISAAC; // the values of isaac is strictly 4 bytes + + /* Isaac returns 4-bytes per call, so progress by 4 bytes. */ + for( size_t ii = 0; ii < words; ++ii ) + { + /* get the next 32bit random number */ + u32_to_buffer( bufpos, isaac_nextval( isaac_state ), SIZE_OF_ISAAC ); + bufpos += SIZE_OF_ISAAC; + } + + /* If there is some remainder copy only relevant number of bytes to not overflow the buffer. */ + const size_t remain = count % SIZE_OF_ISAAC; // SIZE_OF_ISAAC is strictly 4 bytes + if( remain > 0 ) + { + u32_to_buffer( bufpos, isaac_nextval( isaac_state ), remain ); + } + + return 0; +} + +int nwipe_isaac64_init( NWIPE_PRNG_INIT_SIGNATURE ) +{ + int count; + rand64ctx* isaac_state = *state; + + nwipe_log( NWIPE_LOG_NOTICE, "Initialising ISAAC-64 prng" ); + + if( *state == NULL ) + { + /* This is the first time that we have been called. */ + *state = malloc( sizeof( rand64ctx ) ); + isaac_state = *state; + + /* Check the memory allocation. */ + if( isaac_state == 0 ) + { + nwipe_perror( errno, __FUNCTION__, "malloc" ); + nwipe_log( NWIPE_LOG_FATAL, "Unable to allocate memory for the isaac state." ); + return -1; + } + } + + /* Take the minimum of the isaac seed size and available entropy. */ + if( sizeof( isaac_state->randrsl ) < seed->length ) + { + count = sizeof( isaac_state->randrsl ); + } + else + { + memset( isaac_state->randrsl, 0, sizeof( isaac_state->randrsl ) ); + count = seed->length; + } + + if( count == 0 ) + { + /* Start ISACC without a seed. */ + rand64init( isaac_state, 0 ); + } + else + { + /* Seed the ISAAC state with entropy. */ + memcpy( isaac_state->randrsl, seed->s, count ); + + /* The second parameter indicates that randrsl is non-empty. */ + rand64init( isaac_state, 1 ); + } + + return 0; +} + +int nwipe_isaac64_read( NWIPE_PRNG_READ_SIGNATURE ) +{ + rand64ctx* isaac_state = *state; + u8* restrict bufpos = buffer; + size_t words = count / SIZE_OF_ISAAC64; // the values of ISAAC-64 is strictly 8 bytes + + for( size_t ii = 0; ii < words; ++ii ) + { + u64_to_buffer( bufpos, isaac64_nextval( isaac_state ), SIZE_OF_ISAAC64 ); + bufpos += SIZE_OF_ISAAC64; + } + + /* If there is some remainder copy only relevant number of bytes to not overflow the buffer. */ + const size_t remain = count % SIZE_OF_ISAAC64; // SIZE_OF_ISAAC64 is strictly 8 bytes + if( remain > 0 ) + { + u64_to_buffer( bufpos, isaac64_nextval( isaac_state ), remain ); + } + + return 0; +} diff --git a/src/prng.h b/src/prng.h new file mode 100644 index 0000000..8dfa3d7 --- /dev/null +++ b/src/prng.h @@ -0,0 +1,65 @@ +/* + * prng.h: Pseudo Random Number Generator abstractions for nwipe. + * + * Copyright Darik Horn <dajhorn-dban@vanadac.com>. + * + * 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, version 2. + * + * 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. + * + */ + +#ifndef PRNG_H_ +#define PRNG_H_ + +#include <sys/types.h> + +/* A chunk of random data. */ +typedef struct +{ + size_t length; // Length of the entropy string in bytes. + u8* s; // The actual bytes of the entropy string. +} nwipe_entropy_t; + +#define NWIPE_PRNG_INIT_SIGNATURE void **state, nwipe_entropy_t *seed +#define NWIPE_PRNG_READ_SIGNATURE void **state, void *buffer, size_t count + +/* Function pointers for PRNG actions. */ +typedef int ( *nwipe_prng_init_t )( NWIPE_PRNG_INIT_SIGNATURE ); +typedef int ( *nwipe_prng_read_t )( NWIPE_PRNG_READ_SIGNATURE ); + +/* The generic PRNG definition. */ +typedef struct +{ + const char* label; // The name of the pseudo random number generator. + nwipe_prng_init_t init; // Inialize the prng state with the seed. + nwipe_prng_read_t read; // Read data from the prng. +} nwipe_prng_t; + +/* Mersenne Twister prototypes. */ +int nwipe_twister_init( NWIPE_PRNG_INIT_SIGNATURE ); +int nwipe_twister_read( NWIPE_PRNG_READ_SIGNATURE ); + +/* ISAAC prototypes. */ +int nwipe_isaac_init( NWIPE_PRNG_INIT_SIGNATURE ); +int nwipe_isaac_read( NWIPE_PRNG_READ_SIGNATURE ); +int nwipe_isaac64_init( NWIPE_PRNG_INIT_SIGNATURE ); +int nwipe_isaac64_read( NWIPE_PRNG_READ_SIGNATURE ); + +/* Size of the twister is not derived from the architecture, but it is strictly 4 bytes */ +#define SIZE_OF_TWISTER 4 + +/* Size of the isaac/isaac64 is not derived from the architecture, but it is strictly 4 or 8 bytes */ +#define SIZE_OF_ISAAC 4 +#define SIZE_OF_ISAAC64 8 + +#endif /* PRNG_H_ */ diff --git a/src/temperature.c b/src/temperature.c new file mode 100644 index 0000000..d6511c3 --- /dev/null +++ b/src/temperature.c @@ -0,0 +1,270 @@ +/* + * temperature.c: functions that populate the drive temperature variables + * in each drives context structure. + * + * 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, version 2. + * + * 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. + */ + +//#define _LARGEFILE64_SOURCE +//#define _FILE_OFFSET_BITS 64 +#define _BSD_SOURCE + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdint.h> +#include <errno.h> +#include <dirent.h> + +#include "nwipe.h" +#include "context.h" +#include "method.h" +#include "device.h" +#include "prng.h" +#include "options.h" +#include "device.h" +#include "logging.h" +#include "temperature.h" + +int nwipe_init_temperature( nwipe_context_t* c ) +{ + /* This function is called after each nwipe_context_t has been created. + * It initialises the temperature variables in each context and then + * constructs a path that is placed in the context that points to the + * appropriate /sys/class/hwmon/hwmonX directory that corresponds with + * the particular drive represented in the context structure. + */ + DIR* dir; + DIR* dir2; + const char dirpath[] = "/sys/class/hwmon"; + char dirpath_tmp[256]; + char dirpath_tmp2[256]; + char dirpath_hwmonX[256]; + char device[256]; + char device_context_name[256]; + // const char dirpath[] = "/home/nick/mouse/hwmon1"; + struct dirent* dp; + struct dirent* dp2; + + /* Why Initialise with 1000000? Because the GUI needs to know whether data + * has been obtained so it can display appropriate information when a + * device is unable to provide temperature data */ + + c->temp1_crit = 1000000; + c->temp1_highest = 1000000; + c->temp1_input = 1000000; + c->temp1_lcrit = 1000000; + c->temp1_lowest = 1000000; + c->temp1_max = 1000000; + c->temp1_min = 1000000; + c->temp1_monitored_wipe_max = 1000000; + c->temp1_monitored_wipe_min = 1000000; + c->temp1_monitored_wipe_avg = 1000000; + c->temp1_flash_rate = 2; + c->temp1_flash_rate_counter = 0; + c->temp1_path[0] = 0; + c->temp1_time = 0; + + /* Each hwmonX directory is processed in turn and once a hwmonX directory has been + * found that is a block device and the block device name matches the drive + * name in the current context then the path to ../hwmonX is constructed and written + * to the drive context structure '* c'. This path is used in the nwipe_update_temperature + * function to retrieve temperature data and store it in the device context + */ + + if( ( dir = opendir( dirpath ) ) != NULL ) + { + /* Process each hwmonX sub directory in turn */ + while( ( dp = readdir( dir ) ) != NULL ) + { + /* Does the directory start with 'hwmon' */ + if( strstr( dp->d_name, "hwmon" ) != NULL ) + { + if( nwipe_options.verbose ) + { + /* print a empty line to separate the different hwmon sensors */ + nwipe_log( NWIPE_LOG_DEBUG, "hwmon:" ); + } + strcpy( dirpath_tmp, dirpath ); + strcat( dirpath_tmp, "/" ); + strcat( dirpath_tmp, dp->d_name ); + strcpy( dirpath_hwmonX, dirpath_tmp ); + strcat( dirpath_tmp, "/device/block" ); + + /* Depending on the class of block device, the device name may + * appear in different sub-directories. So we try to open each + * directory that are known to contain block devices. These are + * /sys/class/hwmon/hwmonX/device/block + * /sys/class/hwmon/hwmonX/device/nvme/nvme0 + * /sys/class/hwmon/hwmonX/device/ + */ + + if( ( dir2 = opendir( dirpath_tmp ) ) == NULL ) + { + if( nwipe_options.verbose ) + { + nwipe_log( NWIPE_LOG_DEBUG, "hwmon: %s doesn't exist, trying next path", dirpath_tmp ); + } + strcpy( dirpath_tmp2, dirpath_hwmonX ); + strcat( dirpath_tmp2, "/device/nvme/nvme0" ); + strcpy( dirpath_tmp, dirpath_tmp2 ); + + if( ( dir2 = opendir( dirpath_tmp ) ) == NULL ) + { + if( nwipe_options.verbose ) + { + nwipe_log( NWIPE_LOG_DEBUG, "hwmon: %s doesn't exist, trying next path", dirpath_tmp ); + } + + strcpy( dirpath_tmp2, dirpath_hwmonX ); + strcat( dirpath_tmp2, "/device" ); + strcpy( dirpath_tmp, dirpath_tmp2 ); + + if( ( dir2 = opendir( dirpath_tmp ) ) == NULL ) + { + if( nwipe_options.verbose ) + { + nwipe_log( + NWIPE_LOG_DEBUG, "hwmon: %s doesn't exist, no more paths to try", dirpath_tmp ); + } + continue; + } + } + } + + if( dir2 != NULL ) + { + if( nwipe_options.verbose ) + { + nwipe_log( NWIPE_LOG_DEBUG, "hwmon: Found %s", dirpath_tmp ); + } + + /* Read the device name */ + while( ( dp2 = readdir( dir2 ) ) != NULL ) + { + if( nwipe_options.verbose ) + { + nwipe_log( NWIPE_LOG_DEBUG, "hwmon: dirpath_tmp=%s/%s", dirpath_tmp, &dp2->d_name[0] ); + } + + /* Skip the '.' and '..' directories */ + if( dp2->d_name[0] == '.' ) + { + continue; + } + strcpy( device, dp2->d_name ); + + /* Create a copy of the device name from the context but strip the path from it, right justify + * device name, prefix with spaces so length is 8. */ + nwipe_strip_path( device_context_name, c->device_name ); + + /* Remove leading/training whitespace from a string and left justify result */ + trim( device_context_name ); + + /* Does the hwmon device match the device for this drive context */ + if( strcmp( device, device_context_name ) != 0 ) + { + /* No, so try next hwmon device */ + continue; + } + else + { + /* Match ! This hwmon device matches this context, so write the hwmonX path to the context + */ + nwipe_log( NWIPE_LOG_NOTICE, "hwmon: %s has temperature monitoring", device, dirpath_tmp ); + if( nwipe_options.verbose ) + { + nwipe_log( NWIPE_LOG_DEBUG, "hwmon: %s found in %s", device, dirpath_tmp ); + } + /* Copy the hwmon path to the drive context structure */ + strcpy( c->temp1_path, dirpath_hwmonX ); + } + } + closedir( dir2 ); + } + } + } + closedir( dir ); + } + + return 0; +} + +void nwipe_update_temperature( nwipe_context_t* c ) +{ + /* For the given drive context obtain the path to it's hwmon temperature settings + * and read then write the temperature values back to the context. A numeric ascii to integer conversion is + * performed. The temperaures should be updated no more frequently than every 60 seconds + */ + + char temperature_label[NUMBER_OF_FILES][20] = { + "temp1_crit", "temp1_highest", "temp1_input", "temp1_lcrit", "temp1_lowest", "temp1_max", "temp1_min" }; + int* temperature_pcontext[NUMBER_OF_FILES] = { + + &( c->temp1_crit ), + &( c->temp1_highest ), + &( c->temp1_input ), + &( c->temp1_lcrit ), + &( c->temp1_lowest ), + &( c->temp1_max ), + &( c->temp1_min ) }; + + char path[256]; + char temperature[256]; + FILE* fptr; + int idx; + int result; + + for( idx = 0; idx < NUMBER_OF_FILES; idx++ ) + { + /* Construct the full path including filename */ + strcpy( path, c->temp1_path ); + strcat( path, "/" ); + strcat( path, &( temperature_label[idx][0] ) ); + + /* Open the file */ + if( ( fptr = fopen( path, "r" ) ) != NULL ) + { + /* Acquire data until we reach a newline */ + result = fscanf( fptr, "%[^\n]", temperature ); + + /* Convert numeric ascii to binary integer */ + *( temperature_pcontext[idx] ) = atoi( temperature ); + + /* Divide by 1000 to get degrees celcius */ + *( temperature_pcontext[idx] ) = *( temperature_pcontext[idx] ) / 1000; + + if( nwipe_options.verbose ) + { + nwipe_log( NWIPE_LOG_NOTICE, "hwmon: %s %dC", path, *( temperature_pcontext[idx] ) ); + } + + fclose( fptr ); + } + else + { + if( nwipe_options.verbose ) + { + nwipe_log( NWIPE_LOG_NOTICE, "hwmon: Unable to open %s", path ); + } + } + } + + /* Update the time stamp that records when we checked the temperature, + * this is used by the GUI to check temperatures periodically, typically + * every 60 seconds */ + c->temp1_time = time( NULL ); + + return; +} diff --git a/src/temperature.h b/src/temperature.h new file mode 100644 index 0000000..d538822 --- /dev/null +++ b/src/temperature.h @@ -0,0 +1,30 @@ +/*. + * temperature.h: The header file for disk drive temperature sensing + * + * 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, version 2. + * + * 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. + * + */ + +#ifndef TEMPERATURE_H_ +#define TEMPERATURE_H_ + +#include <sys/types.h> +#include "context.h" + +int nwipe_init_temperature( nwipe_context_t* ); +void nwipe_update_temperature( nwipe_context_t* ); + +#define NUMBER_OF_FILES 7 + +#endif /* TEMPERATURE_H_ */ diff --git a/src/version.c b/src/version.c new file mode 100644 index 0000000..8abd0af --- /dev/null +++ b/src/version.c @@ -0,0 +1,17 @@ +/** + * version_string and program_name are used by siege + * and configure; author_name and email_address are + * used by configure to dynamically assign those values + * to documentation files. + */ +const char* version_string = "0.33"; +const char* program_name = "nwipe"; +const char* author_name = "Martijn van Brummelen"; +const char* email_address = "git@brumit.nl"; +const char* years = "2022"; +const char* copyright = "Copyright Darik Horn <dajhorn-dban@vanadac.com>\n\ +Modifications to original dwipe Copyright Andy Beverley <andy@andybev.com>\n\ +This is free software; see the source for copying conditions.\n\ +There is NO warranty; not even for MERCHANTABILITY or FITNESS\n\ +FOR A PARTICULAR PURPOSE.\n"; +const char* banner = "nwipe 0.33"; diff --git a/src/version.h b/src/version.h new file mode 100644 index 0000000..b1214b2 --- /dev/null +++ b/src/version.h @@ -0,0 +1,11 @@ +#ifndef __VERSION_H +#define __VERSION_H + +extern char* version_string; +extern char* program_name; +extern char* author_name; +extern char* email_address; +extern char* copyright; +extern char* banner; + +#endif /*__VERSION_H*/ |