From c235349288650224e6ec7651b0bd562bab54ae28 Mon Sep 17 00:00:00 2001 From: Fabian Druschke Date: Tue, 9 Dec 2025 16:19:32 +0100 Subject: [PATCH] Added testfile probe to check if destination directory is writable --- src/nwipe.c | 115 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 74 insertions(+), 41 deletions(-) diff --git a/src/nwipe.c b/src/nwipe.c index f25cf76..c4eb688 100644 --- a/src/nwipe.c +++ b/src/nwipe.c @@ -77,9 +77,6 @@ #endif #endif -#define NWIPE_PDF_DIR_MODE ( S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH ) -/* -> 0755: rwx for owner, r-x for group and others */ - int terminate_signal; int user_abort; int global_wipe_status; @@ -99,11 +96,62 @@ int devnamecmp( const void* a, const void* b ) return ( ret ); } -static int nwipe_ensure_directory( const char* path ) +#define NWIPE_PDF_DIR_MODE (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) +/* -> 0755: rwx for owner, r-x for group and others */ + +/* Helper: try to create and remove a temporary file inside the directory. + * This catches cases where access(path, W_OK) passes (especially as root) + * but the underlying filesystem does not allow creating regular files, + * e.g. /proc or other pseudo/readonly filesystems. + */ +static int nwipe_probe_directory_writable( const char *path ) +{ + const char *suffix = "/.nwipe_pdf_testXXXXXX"; + size_t path_len = strlen( path ); + size_t suffix_len = strlen( suffix ); + size_t total_len = path_len + suffix_len + 1; /* +1 for '\0' */ + + char *tmpl = (char *)malloc( total_len ); + if( tmpl == NULL ) + { + nwipe_log( NWIPE_LOG_ERROR, + "Failed to allocate memory to probe PDFreportpath '%s'.", + path ); + return -1; + } + + /* Build template "/.nwipe_pdf_testXXXXXX" */ + snprintf( tmpl, total_len, "%s%s", path, suffix ); + + int fd = mkstemp( tmpl ); + if( fd < 0 ) + { + nwipe_log( NWIPE_LOG_ERROR, + "PDFreportpath '%s' is not writable (cannot create test file): %s.", + path, strerror( errno ) ); + free( tmpl ); + return -1; + } + + /* Successfully created a temporary file, now clean it up. */ + close( fd ); + if( unlink( tmpl ) != 0 ) + { + /* Not fatal for our check, but log it anyway. */ + nwipe_log( NWIPE_LOG_WARNING, + "Failed to remove temporary test file '%s' in PDFreportpath '%s': %s.", + tmpl, path, strerror( errno ) ); + } + + free( tmpl ); + return 0; +} + +static int nwipe_ensure_directory( const char *path ) { struct stat st; - char* tmp; - char* p; + char *tmp; + char *p; size_t len; if( path == NULL || path[0] == '\0' ) @@ -112,32 +160,23 @@ static int nwipe_ensure_directory( const char* path ) return 0; } - /* Special case: current directory. - * Historically nwipe called access(".", W_OK), so we keep that behavior. */ - if( strcmp( path, "." ) == 0 ) - { - if( access( path, W_OK ) != 0 ) - { - nwipe_log( NWIPE_LOG_ERROR, "PDFreportpath '%s' is not writable: %s.", path, strerror( errno ) ); - return -1; - } - return 0; - } - /* 1. First try: does the path already exist? */ if( stat( path, &st ) == 0 ) { /* Path exists; make sure it's a directory. */ if( !S_ISDIR( st.st_mode ) ) { - nwipe_log( NWIPE_LOG_ERROR, "PDFreportpath '%s' exists but is not a directory.", path ); + nwipe_log( NWIPE_LOG_ERROR, + "PDFreportpath '%s' exists but is not a directory.", + path ); return -1; } - /* Check that we can write into it. */ - if( access( path, W_OK ) != 0 ) + /* Even if access() says it's writable (especially as root), + * we still probe by actually creating a test file. */ + if( nwipe_probe_directory_writable( path ) != 0 ) { - nwipe_log( NWIPE_LOG_ERROR, "PDFreportpath '%s' is not writable: %s.", path, strerror( errno ) ); + /* Detailed error already logged. */ return -1; } @@ -148,17 +187,21 @@ static int nwipe_ensure_directory( const char* path ) /* stat() failed: if this is not "does not exist", propagate the error. */ if( errno != ENOENT ) { - nwipe_log( NWIPE_LOG_ERROR, "Failed to stat PDFreportpath '%s': %s.", path, strerror( errno ) ); + nwipe_log( NWIPE_LOG_ERROR, + "Failed to stat PDFreportpath '%s': %s.", + path, strerror( errno ) ); return -1; } /* 2. Directory does not exist -> create it recursively (mkdir -p style). */ len = strlen( path ); - tmp = (char*) malloc( len + 1 ); + tmp = (char *)malloc( len + 1 ); if( tmp == NULL ) { - nwipe_log( NWIPE_LOG_ERROR, "Failed to allocate memory to create PDFreportpath '%s'.", path ); + nwipe_log( NWIPE_LOG_ERROR, + "Failed to allocate memory to create PDFreportpath '%s'.", + path ); return -1; } @@ -186,9 +229,7 @@ static int nwipe_ensure_directory( const char* path ) { nwipe_log( NWIPE_LOG_ERROR, "Failed to create directory '%s' for PDFreportpath '%s': %s.", - tmp, - path, - strerror( errno ) ); + tmp, path, strerror( errno ) ); free( tmp ); return -1; } @@ -203,25 +244,17 @@ static int nwipe_ensure_directory( const char* path ) { nwipe_log( NWIPE_LOG_ERROR, "Failed to create directory '%s' for PDFreportpath '%s': %s.", - tmp, - path, - strerror( errno ) ); + tmp, path, strerror( errno ) ); free( tmp ); - return -1; + return -1; } free( tmp ); - /* 3. Final sanity check: ensure the path now exists, is a directory and writable. */ - if( stat( path, &st ) != 0 || !S_ISDIR( st.st_mode ) ) + /* 3. Final sanity check: ensure the path is writable by probing with a file. */ + if( nwipe_probe_directory_writable( path ) != 0 ) { - nwipe_log( NWIPE_LOG_ERROR, "PDFreportpath '%s' could not be verified as a directory after creation.", path ); - return -1; - } - - if( access( path, W_OK ) != 0 ) - { - nwipe_log( NWIPE_LOG_ERROR, "PDFreportpath '%s' is not writable after creation: %s.", path, strerror( errno ) ); + /* Detailed error already logged. */ return -1; }