summaryrefslogtreecommitdiffstats
path: root/src/device.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-16 22:13:02 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-16 22:13:02 +0000
commite244c93d21bcacf1a0eedefdcc3018a362274796 (patch)
tree857e0d7bcf20546b38fff97a8702c2c1240bc23b /src/device.c
parentInitial commit. (diff)
downloadnwipe-e244c93d21bcacf1a0eedefdcc3018a362274796.tar.xz
nwipe-e244c93d21bcacf1a0eedefdcc3018a362274796.zip
Adding upstream version 0.36.upstream/0.36
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--src/device.c917
1 files changed, 917 insertions, 0 deletions
diff --git a/src/device.c b/src/device.c
new file mode 100644
index 0000000..8475f30
--- /dev/null
+++ b/src/device.c
@@ -0,0 +1,917 @@
+/*
+ * 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 <ctype.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 "hpa_dco.h"
+#include "miscellaneous.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_context_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 ) ) )
+ {
+ /* to have some progress indication. can help if there are many/slow disks */
+ fprintf( stderr, "." );
+
+ 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 )
+{
+ PedDevice* dev = NULL;
+
+ int i;
+ int dcount = 0;
+
+ for( i = 0; i < ndevnames; i++ )
+ {
+ /* to have some progress indication. can help if there are many/slow disks */
+ fprintf( stderr, "." );
+
+ 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[NWIPE_SERIALNUMBER_LENGTH + 1];
+ nwipe_device_t bus;
+ int is_ssd;
+ int check_HPA; // a flag that indicates whether we check for a HPA on this device
+
+ bus = 0;
+
+ /* Check whether this drive is on the excluded drive list ? */
+ idx = 0;
+ while( idx < MAX_NUMBER_EXCLUDED_DRIVES )
+ {
+ 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, &is_ssd, 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 );
+
+ /* Some USB adapters have drive model endian swapped, pattern match and fix */
+ fix_endian_model_names( 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;
+ next_device->device_sector_size = dev->sector_size; // logical sector size
+ next_device->device_block_size = dev->sector_size; // set as logical but could be a multiple of logical sector size
+ next_device->device_phys_sector_size = dev->phys_sector_size; // physical sector size
+ next_device->device_size_in_sectors = next_device->device_size / next_device->device_sector_size;
+ next_device->device_size_in_512byte_sectors = next_device->device_size / 512;
+ 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 < NWIPE_SERIALNUMBER_LENGTH; idx++ )
+ {
+ if( isascii( next_device->identity.serial_no[idx] ) && !iscntrl( next_device->identity.serial_no[idx] ) )
+ {
+ next_device->device_serial_no[idx] = next_device->identity.serial_no[idx];
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ // Terminate the string.
+ next_device->device_serial_no[idx] = 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, &next_device->device_is_ssd, 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 )
+ {
+ strncpy( next_device->device_serial_no, tmp_serial, NWIPE_SERIALNUMBER_LENGTH );
+ }
+ }
+
+ /* Does the user want to anonymize serial numbers ? */
+ if( nwipe_options.quiet )
+ {
+ if( next_device->device_serial_no[0] == 0 )
+ {
+ strncpy( next_device->device_serial_no, "????????????????????", NWIPE_SERIALNUMBER_LENGTH + 1 );
+ }
+ else
+ {
+ strncpy( next_device->device_serial_no, "XXXXXXXXXXXXXXXXXXXX", NWIPE_SERIALNUMBER_LENGTH + 1 );
+ }
+ }
+ /* strncpy would have copied the null terminator BUT just to be sure, just in case somebody changes the length
+ * of those strings we should explicitly terminate the string */
+ next_device->device_serial_no[NWIPE_SERIALNUMBER_LENGTH] = 0;
+
+ /* Initialise the variables that toggle the [size][temp c] with [HPA status]
+ * Not currently used, but may be used in the future or for other purposes
+ */
+ next_device->HPA_toggle_time = time( NULL );
+ next_device->HPA_display_toggle_state = 0;
+
+ /* Initialise the HPA variables for this device
+ */
+ next_device->HPA_reported_set = 0;
+ next_device->HPA_reported_real = 0;
+ next_device->DCO_reported_real_max_sectors = 0;
+ next_device->HPA_status = HPA_NOT_APPLICABLE;
+
+ /* All device strings should be 4 characters, prefix with space if under 4 characters
+ * We also set a switch for certain devices to check for the host protected area (HPA)
+ */
+ check_HPA = 0;
+
+ // WARNING TEMP LINE WARNING
+ // next_device->device_type = NWIPE_DEVICE_ATA;
+
+ switch( next_device->device_type )
+ {
+ case NWIPE_DEVICE_UNKNOWN:
+ strcpy( next_device->device_type_str, " UNK" );
+ check_HPA = 1;
+ break;
+
+ case NWIPE_DEVICE_IDE:
+ strcpy( next_device->device_type_str, " IDE" );
+ check_HPA = 1;
+ break;
+
+ case NWIPE_DEVICE_SCSI:
+ strcpy( next_device->device_type_str, " SCSI" );
+ check_HPA = 1;
+ break;
+
+ case NWIPE_DEVICE_COMPAQ:
+ strcpy( next_device->device_type_str, " CPQ" );
+ break;
+
+ case NWIPE_DEVICE_USB:
+ strcpy( next_device->device_type_str, " USB" );
+ check_HPA = 1;
+ break;
+
+ case NWIPE_DEVICE_IEEE1394:
+ strcpy( next_device->device_type_str, "1394" );
+ break;
+
+ case NWIPE_DEVICE_ATA:
+ strcpy( next_device->device_type_str, " ATA" );
+ check_HPA = 1;
+ 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" );
+ check_HPA = 1;
+ break;
+
+ case NWIPE_DEVICE_MMC:
+ strcpy( next_device->device_type_str, " MMC" );
+ break;
+ }
+ if( next_device->device_is_ssd )
+ {
+ strcpy( next_device->device_type_str + 4, "-SSD" );
+ }
+ else
+ {
+ strcpy( next_device->device_type_str + 4, " " );
+ }
+
+ 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 );
+
+ nwipe_log( NWIPE_LOG_INFO,
+ "%s, sector(logical)/block(physical) sizes %i/%i",
+ next_device->device_name,
+ dev->sector_size,
+ dev->phys_sector_size );
+
+ /******************************
+ * Check for hidden sector_size
+ */
+ if( check_HPA == 1 )
+ {
+ hpa_dco_status( next_device );
+ }
+
+ /* print an empty line to separate the drives in the log */
+ nwipe_log( NWIPE_LOG_INFO, " " );
+
+ ( *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, int* is_ssd, char* serialnumber )
+{
+ /* The caller provides a string that contains the device, i.e. /dev/sdc, also a pointer
+ * to an integer (bus type), another pointer to an integer (is_ssd), and finally 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;
+ }
+ else
+ {
+ if( strstr( result, "/mmcblk" ) != 0 )
+ {
+ *bus = NWIPE_DEVICE_MMC;
+ }
+ }
+ }
+ }
+ }
+ }
+ /* 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 occasionally
+ * 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] = tolower( result[idx] );
+ }
+
+ 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_INFO, "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], NWIPE_SERIALNUMBER_LENGTH );
+ serialnumber[NWIPE_SERIALNUMBER_LENGTH] = 0;
+ }
+
+ 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] );
+ for( idx = 19; result[idx]; idx++ )
+ {
+ result[idx] = tolower( result[idx] );
+ }
+
+ 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] );
+ for( idx = 16; result[idx]; idx++ )
+ {
+ result[idx] = tolower( result[idx] );
+ }
+
+ if( strncmp( &result[16], "sata", 4 ) == 0 )
+ {
+ *bus = NWIPE_DEVICE_ATA;
+ }
+ }
+ }
+ if( strstr( result, "rotation rate:" ) != 0 )
+ {
+ /* strip any leading or trailing spaces and left justify, +15 is the length of "Rotation Rate:" */
+ trim( &result[15] );
+ for( idx = 15; result[idx]; idx++ )
+ {
+ result[idx] = tolower( result[idx] );
+ }
+
+ if( strncmp( &result[15], "solid state device", 19 ) == 0 )
+ {
+ *is_ssd = 1;
+ }
+ }
+ }
+
+ /* 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, "Smartctl is unable to provide smart data for %s", device );
+
+ if( *bus == NWIPE_DEVICE_USB || *bus == NWIPE_DEVICE_MMC )
+ {
+ strcpy( serialnumber, "(S/N: unknown)" );
+ set_return_value = 5;
+ }
+ }
+ }
+ }
+ }
+
+ return set_return_value;
+}
+
+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;
+ }
+}