From c51a8e9846914e0c2508198b8596771e7e3e679d Mon Sep 17 00:00:00 2001 From: Fabian Druschke Date: Wed, 26 Nov 2025 17:15:35 +0100 Subject: [PATCH] Enhances the --exclude option to match devices by their underlying block device ID (major/minor), allowing persistent identifiers in /dev/disk/by-id/ and /dev/disk/by-path/ to be used safely. Legacy string-based matching is preserved. --- src/device.c | 125 ++++++++++++++++++++++++++++++++++++++++++++++---- src/options.c | 2 + 2 files changed, 119 insertions(+), 8 deletions(-) diff --git a/src/device.c b/src/device.c index 935afd7..50f0fb6 100644 --- a/src/device.c +++ b/src/device.c @@ -50,6 +50,119 @@ int check_device( nwipe_context_t*** c, PedDevice* dev, int dcount ); char* trim( char* str ); +/* + * Resolve a device path (including /dev/disk/by-* symlinks) to its + * underlying block device id (dev_t). + * + * Returns 0 on success and fills *out_rdev. + * Returns -1 on error or if the path is not a block device. + */ +static int nwipe_path_to_rdev( const char* path, dev_t* out_rdev ) +{ + struct stat st; + + if( path == NULL || out_rdev == NULL ) + { + errno = EINVAL; + return -1; + } + + /* + * stat() follows symlinks by default, which is what we want for + * persistent names in /dev/disk/by-id, /dev/disk/by-path, etc. + */ + if( stat( path, &st ) != 0 ) + { + return -1; + } + + if( !S_ISBLK( st.st_mode ) ) + { + /* Not a block device node. */ + errno = ENOTBLK; + return -1; + } + + *out_rdev = st.st_rdev; + return 0; +} + +/* + * Check whether a candidate device node should be excluded based on the + * --exclude list. Matching is done primarily by device identity + * (major/minor via st_rdev), so persistent names like /dev/disk/by-id/* + * are safe. We keep legacy string-based matching as a fallback. + * + * Returns 1 if the candidate should be excluded, 0 otherwise. + */ +static int nwipe_is_excluded_device( const char* candidate_devnode ) +{ + dev_t cand_rdev; + int have_cand_rdev; + int i; + + /* Try to resolve the candidate device to a dev_t. */ + have_cand_rdev = ( nwipe_path_to_rdev( candidate_devnode, &cand_rdev ) == 0 ); + + for( i = 0; i < MAX_NUMBER_EXCLUDED_DRIVES; i++ ) + { + const char* ex = nwipe_options.exclude[i]; + dev_t ex_rdev; + int have_ex_rdev; + const char* base; + + /* Empty slot in the exclude array. */ + if( ex == NULL || ex[0] == 0 ) + { + continue; + } + + /* + * First try: both candidate and exclude entry resolve to block + * devices; compare device ids (major/minor). + */ + have_ex_rdev = ( nwipe_path_to_rdev( ex, &ex_rdev ) == 0 ); + if( have_cand_rdev && have_ex_rdev && ex_rdev == cand_rdev ) + { + nwipe_log( NWIPE_LOG_NOTICE, "Device %s excluded as per command line option -e", candidate_devnode ); + return 1; + } + + /* + * Fallback 1: exact string match. This keeps compatibility with + * older usage like --exclude=/dev/sda or --exclude=/dev/mapper/cryptswap1. + */ + if( strcmp( candidate_devnode, ex ) == 0 ) + { + nwipe_log( NWIPE_LOG_NOTICE, "Device %s excluded as per command line option -e", candidate_devnode ); + return 1; + } + + /* + * Fallback 2: match against the basename only, so that an + * exclude entry like "sda" still works even if the full path is + * /dev/sda. + */ + base = strrchr( candidate_devnode, '/' ); + if( base != NULL ) + { + base++; + } + else + { + base = candidate_devnode; + } + + if( strcmp( base, ex ) == 0 ) + { + nwipe_log( NWIPE_LOG_NOTICE, "Device %s excluded as per command line option -e", candidate_devnode ); + return 1; + } + } + + return 0; +} + extern int terminate_signal; int nwipe_device_scan( nwipe_context_t*** c ) @@ -138,15 +251,11 @@ int check_device( nwipe_context_t*** c, PedDevice* dev, int dcount ) bus = 0; - /* Check whether this drive is on the excluded drive list ? */ - idx = 0; - while( idx < MAX_NUMBER_EXCLUDED_DRIVES ) + /* Check whether this drive is on the excluded drive list. */ + if( nwipe_is_excluded_device( dev->path ) ) { - 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; - } + /* Already logged inside nwipe_is_excluded_device(). */ + return 0; } /* Check whether the user has specified using the --nousb option diff --git a/src/options.c b/src/options.c index 8bcabf4..e6f5b11 100644 --- a/src/options.c +++ b/src/options.c @@ -789,6 +789,8 @@ void display_help() puts( " --exclude=/dev/sdc" ); puts( " --exclude=/dev/sdc,/dev/sdd" ); puts( " --exclude=/dev/sdc,/dev/sdd,/dev/mapper/cryptswap1\n" ); + puts( " --exclude=/dev/disk/by-id/ata-XXXXXXXX" ); + puts( " --exclude=/dev/disk/by-path/pci-0000:00:17.0-ata-1\n" ); puts( "" ); exit( EXIT_SUCCESS ); }