Merge pull request #693 from Knogle/create-pdf-folder-if-not-existent

feat(pdf): automatically create PDF report directory if missing and improve permission model
This commit is contained in:
PartialVolume
2025-12-09 15:48:43 +00:00
committed by GitHub

View File

@@ -96,6 +96,170 @@ int devnamecmp( const void* a, const void* b )
return ( ret );
}
#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 "<path>/.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;
size_t len;
if( path == NULL || path[0] == '\0' )
{
/* Empty path: nothing to do, treat as success. */
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 );
return -1;
}
/* 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 )
{
/* Detailed error already logged. */
return -1;
}
/* Everything is fine, directory already present and writable. */
return 0;
}
/* 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 ) );
return -1;
}
/* 2. Directory does not exist -> create it recursively (mkdir -p style). */
len = strlen( path );
tmp = (char*) malloc( len + 1 );
if( tmp == NULL )
{
nwipe_log( NWIPE_LOG_ERROR, "Failed to allocate memory to create PDFreportpath '%s'.", path );
return -1;
}
memcpy( tmp, path, len + 1 );
/* Start at the beginning of the string.
* For absolute paths ("/foo/bar") we skip the leading slash so we do not
* try to create "/" itself. */
p = tmp;
if( tmp[0] == '/' )
{
p = tmp + 1;
}
for( ; *p; ++p )
{
if( *p == '/' )
{
*p = '\0';
/* Skip empty components (can happen with leading '/' or double '//'). */
if( tmp[0] != '\0' )
{
if( mkdir( tmp, NWIPE_PDF_DIR_MODE ) != 0 && errno != EEXIST )
{
nwipe_log( NWIPE_LOG_ERROR,
"Failed to create directory '%s' for PDFreportpath '%s': %s.",
tmp,
path,
strerror( errno ) );
free( tmp );
return -1;
}
}
*p = '/';
}
}
/* Create the final directory component (the full path). */
if( mkdir( tmp, NWIPE_PDF_DIR_MODE ) != 0 && errno != EEXIST )
{
nwipe_log( NWIPE_LOG_ERROR,
"Failed to create directory '%s' for PDFreportpath '%s': %s.",
tmp,
path,
strerror( errno ) );
free( tmp );
return -1;
}
free( tmp );
/* 3. Final sanity check: ensure the path is writable by probing with a file. */
if( nwipe_probe_directory_writable( path ) != 0 )
{
/* Detailed error already logged. */
return -1;
}
return 0;
}
int main( int argc, char** argv )
{
int nwipe_optind; // The result of nwipe_options().
@@ -198,12 +362,14 @@ int main( int argc, char** argv )
}
}
/* Check if the given path for PDF reports is a writeable directory */
/* Check if the given path for PDF reports is a writeable directory.
* If it does not exist, try to create it (mkdir -p style).
*/
if( strcmp( nwipe_options.PDFreportpath, "noPDF" ) != 0 )
{
if( access( nwipe_options.PDFreportpath, W_OK ) != 0 )
if( nwipe_ensure_directory( nwipe_options.PDFreportpath ) != 0 )
{
nwipe_log( NWIPE_LOG_ERROR, "PDFreportpath %s is not a writeable directory.", nwipe_options.PDFreportpath );
/* nwipe_ensure_directory already logged a detailed error message. */
cleanup();
exit( 2 );
}