Require strict input of long form options
This fixes potential issues caused by getopt_long() when it allows abbreviated input of the long form options. For instance, previously if a user had accidentally typed --auto, because maybe they had meant to type --autopoweroff or prng=auto, getopt_long() would have allowed --auto as a valid option but the code would have interpreted --auto as meaning
-autonuke with very unfortunate consequences in terms of wiping all the discs attached to your system.
Not allowing strict parsing of options has been an issue in getopt_long() since 2008. However the changes I have made in this code now prevent any abbreviations to long form options and expect strict adherence to the options as presented in nwipe's help page (nwipe --help) and man pages.
This fixes potential issues caused by getopt_long()
when it allows abbreviated input of the long form
options. For instance, previously if a user had
accidentally typed --auto, because maybe they had
meant to type --autopoweroff or prng=auto, getopt_long()
would have allowed --auto as a valid option but the
code would have interpreted --auto as meaning
-autonuke with very unfortunate consequences in terms
of wiping all the discs attached to your system.
Not allowing strict parsing of options has been an
issue in getopt_long() since 2008. However the changes
I have made in this code now prevent any abbreviations
to long form options and expect strict adherence to the
options as presented in nwipe's help page (nwipe --help)
and man pages.
This fixes the following problems.
1. Fdatasync is incorrectly called when in direct I/O mode when
no --directio command line option is present, i.e when it
automatically determines whether direct or cached should be used.
2. When multiple drives are wiped if any of those drives do not
support direct I/O then they would have would have had fdatasync
incorrectly disabled. There was no record of I/O mode on a per
drive basis.
3. Direct I/O on a static pass always called fdatasync as there
was no code present to set the SyncRate to 0 when in direct I/O
mode.
Because autonuke can do a lot of damage we
need to be sure it's value is 1 before letting
it auto wipe. The previous code was assuming
any non zero value was TRUE. In the case of a bug
or memory corruption this could cause an
unintended auto wipe of all discs.
Check start_column < wcols and issue error
Reduce output length by 1 to take care a
situation where output_length maybe provided
by strlen which excludes the null terminator
and sizeof which may or may count the terminator.
Check start_column < wcols and issue error
Reduce output length by 1 to take care a
situation where output_length maybe provided
by strlen which excludes the null terminator
and sizeof which may or may count the terminator.
Because autonuke can do a lot of damage we
need to be sure it's value is 1 before letting
it auto wipe. The previous code was assuming
any non zero value was TRUE. In the case of a bug
or memory corruption this could cause an
unintended auto wipe of all discs.
This change introduces a comprehensive PRNG benchmarking and auto-selection
framework, available both in the ncurses GUI and via the command line.
A new interactive “PRNG Benchmark” menu (key: 'b') has been added to the GUI.
It benchmarks all available PRNG engines using a RAM-only workload and
displays a sorted leaderboard showing sustained throughput in MB/s.
In addition, a new automatic PRNG selection mode is implemented:
--prng=auto
When enabled, nwipe benchmarks all available PRNGs at startup and
automatically selects the fastest PRNG for the current hardware. This
selection happens before device scanning and does not alter any wipe
semantics or security guarantees.
For non-GUI and diagnostic use, a standalone benchmark mode is also added:
--prng-benchmark
This runs the same RAM-only PRNG benchmark, prints a sorted leaderboard
to stdout and the log, and exits without scanning or wiping any devices.
It is fully compatible with --nogui.
Key features:
- Unified PRNG benchmark core shared by GUI, auto-selection and CLI modes
- RAM-only benchmark using aligned multi-megabyte buffers
- Deterministic in-memory seeding (benchmark-only, not used for wiping)
- Sorted leaderboard showing MB/s per PRNG
- Automatic fastest-PRNG selection via --prng=auto (opt-in)
- Interactive GUI benchmark view with dimmed informational styling
- No impact on wipe logic, data security or default behaviour
This change improves transparency, diagnostics and performance tuning
across a wide range of hardware platforms, while keeping all new behaviour
explicitly opt-in.
The help text for all prngs was formatted
so that it is consistent in terms of layout
and positioning with the help text
as displayed by the methods help text.
Specifically so that it still remains
legible with a 80x25 legacy (nomodset)
terminal.
Also added the ability to run the prng
benchmark from within the prng selection
screen.
Add a new interactive PRNG benchmark mode to the ncurses GUI, opened
with the 'o' key from the main device selection screen.
The benchmark runs all available PRNG engines in a RAM-only loop,
measuring sustained throughput in MB/s by generating keystream data
into an aligned memory buffer. Results are collected and displayed as
a sorted leaderboard, making relative PRNG performance immediately
visible.
Key features:
- New modal “PRNG Benchmark” GUI view (o to open, ESC to return)
- RAM-only throughput test (~1s per PRNG by default)
- Aligned multi-megabyte buffer to reduce syscall and cache artefacts
- Deterministic in-memory seeding for reproducible results
- Sorted leaderboard showing MB/s per PRNG
This mode is intended for diagnostics, comparison, and performance
validation of PRNG implementations and does not affect any wipe logic
or security properties.
Add a new GUI “device topology” view, opened with the 't' key from the
device selection screen.
The new view displays the kernel sysfs path of the selected disk as an
indented tree, similar to “lspci -tv”, making it easy to see which
controller, bus, and port a device is attached to.
Key features:
- Modal device topology view (t to open, ESC to return)
- Tree rendering based on /sys/block/<dev> → /sys/devices/... hierarchy
- Portable ncurses ACS line-drawing characters (no UTF-8 dependency)
- Optional PCI controller naming via lspci when available
- Dimmed (A_DIM) text style to visually distinguish the informational view
This improves safety and usability on systems with multiple controllers
by clearly showing the physical attachment path of each drive without
changing wipe behaviour.
Allows user-defined text or tags to be embedded
in the PDF report. The tag data is stored in
nwipe.conf and may be entered via the customer
and organisation preview screen, when enabled,
or directly through the config screen. Press
C on drive selection screen.
Inclusion of this tag in the generated PDF
report requires the pdftag option to be enabled
in the configuration screen or specified explicitly
using the --pdftag command-line argument when
invoking nwipe.
* Refactor CPUID/AES-NI detection into cpu_features module and hide AES-CTR PRNG on unsupported platforms
This commit improves CPU feature handling and PRNG selection logic in three ways:
1. Introduces a dedicated cpu_features.c/.h module that encapsulates CPUID and
AES-NI detection. The previous duplicated inline implementations scattered
across multiple files have been removed to prevent multiple-definition issues
and ensure consistent CPU capability probing.
2. The AES-CTR PRNG is now selectable only when the running platform supports
AES-NI. The ncurses GUI automatically hides the AES-CTR option when AES-NI
is not available, preventing users from choosing a PRNG that would fall back
to software mode or incur unnecessary slowdown. CLI selection
(--prng=aes_ctr_prng) is also blocked on non-AES-NI CPUs with a clear error.
3. All modules (options, GUI, PRNG initialisation) now use the central
has_aes_ni() function, ensuring uniform and future-proof feature detection.
* cpu_features.* missing in commit
* AES-CTR not removed, but disabled when not available on platform
This commit enhances the handling of the `-P /path` / `--PDFreportpath` option by
ensuring that nwipe can create the specified directory if it does not already
exist. Previously, nwipe simply called `access(path, W_OK)` and exited with a
generic “not a writeable directory” error if the directory did not exist or was
not writable. This caused ambiguity and prevented the use of custom report paths
without pre-creating them externally.
Key improvements:
- Added a new helper function `nwipe_ensure_directory()` that:
- Differentiates between “non-existent”, “not a directory”, and “not writable”.
- Attempts to create the directory recursively (`mkdir -p` style) when absent.
- Creates directories with mode 0755 so other users can read/list directory contents.
- Performs final verification that the directory exists, is a directory, and writable.
- Replaced the previous simple `access()` check in `nwipe.c` with the new
directory-ensure logic.
- Introduces clearer and more helpful error messages when directory creation or
permission checks fail.
Benefits:
- Users can now safely specify custom report paths (e.g. `-P /reports` or
USB-mounted paths) without requiring manual pre-creation.
- Eliminates ambiguous error reporting and improves overall user experience.
- Maintains backward-compatible behavior when the target directory already exists.
This patch fixes several issues that could cause garbage or control
characters to appear in the ncurses UI when displaying device serial
numbers.
Key changes:
- Added nwipe_normalize_serial() to strip control characters, non-ASCII
bytes and trim whitespace from all serial numbers before they are
shown in the UI.
- Initialize the serialnumber buffer in
nwipe_get_device_bus_type_and_serialno() to avoid passing undefined
data back to check_device() when no valid "Serial Number:" field is
found.
- Prevent ioctl(HDIO_GET_IDENTITY) from being called on an invalid file
descriptor when open() fails.
- Ensure consistent null termination and sanitize the final
device_serial_no regardless of whether it came from HDIO, smartctl
output, or quiet-mode anonymization.
These fixes resolve cases where devices (especially virtual/QEMU or
USB-attached drives) could report malformed or unexpected serial
strings, resulting in UI corruption such as extra characters, ^A, or
line wrapping.
the effective I/O block size before any sync-rate logic is executed.
- Add new helper function `nwipe_compute_sync_rate_for_device()` to `pass.c`,
converting legacy `--sync` semantics (sync * st_blksize) into a per-write
sync interval based on the actual `io_blocksize`, and disabling periodic
syncing when using direct I/O.
- Update both `nwipe_random_pass()` and `nwipe_static_pass()` to use the new
helper, ensuring consistent and correct sync behaviour for all cached-I/O
passes and removing duplicated sync-calculation logic.
This commit updates both README.md and the nwipe(8) manpage to reflect the
features and behaviour introduced in the upcoming v0.40 release. Changes include:
- Added documentation for the new AES-256-CTR PRNG and its hardware-accelerated
implementation.
- Updated erasure method list to include the BMB21-2019 State Secrets Bureau
sanitisation standard.
- Added full documentation for large, aligned I/O buffers and their impact on
performance.
- Documented the new I/O mode system (`--io-mode=auto|direct|cached`,
`--directio`, `--cachedio`) and the interaction with O_DIRECT fallback logic.
- Updated sync behaviour description to match the new byte-accurate scaling for
cached I/O.
- Updated PRNG section to remove the “future release” note for AES-CTR.
- Documented improved device exclusion with `/dev/disk/by-id/*` support.
- Updated seeding description to reflect the use of `getrandom()` instead of
`/dev/urandom`.
- Refreshed dependency lists and provided concise installation instructions for
multiple Linux distributions.
- Minor stylistic cleanup, clarification of SSD limitations, and improved README
structure for readability and accuracy.
After migrating nwipe to large aligned write buffers (multi-MB blocks), the
existing `sync` option unintentionally changed behaviour. The original logic
performed an fdatasync() every `sync * device_block_size` bytes, which for the
default `sync = 100000` resulted in ~50–400 MB between syncs.
With the new 4 MB I/O blocks, the same value produced syncs only every ~390 GB,
causing extremely delayed I/O error detection in cached I/O mode (errors appear
at fsync time, not on write). This was observed during testing on USB HDDs,
where no sync occurred even after several percent of the wipe.
This commit resolves the issue by:
- Restoring the original “bytes between syncs” behaviour.
The effective sync interval is recalculated based on the new large block size
so that fdatasync() again occurs every few hundred megabytes, not hundreds of
gigabytes.
- Disabling periodic sync entirely when direct I/O (`O_DIRECT`) is forced.
Direct I/O returns hardware errors at write() time, so syncs are unnecessary
and provide no safety benefit.
- Keeping cached I/O safe by ensuring timely detection of device failures,
stalled writeback caches, USB disconnects, and similar hardware conditions.
The large-block write path remains unchanged; only the scheduling of sync
operations is corrected to maintain practical error detection behaviour
consistent with the original nwipe design.
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.
This change extends the recently-added large-block, aligned I/O path with a
user-selectable I/O mode, allowing nwipe to choose between direct I/O
(O_DIRECT) and kernel cached I/O at runtime.
The goal is to:
- Make it easy to benchmark and compare cached vs. direct I/O.
- Provide an operational escape-hatch if Direct I/O causes issues on some
systems or devices.
- Keep a sensible default that "does the right thing" automatically.
Summary of changes
*options.h / options.c*
- Introduce a new enum `nwipe_io_mode_t`:
- `NWIPE_IO_MODE_AUTO` (default)
Try to use `O_DIRECT` for device access. If the kernel rejects
`O_DIRECT` with `EINVAL` or `EOPNOTSUPP`, nwipe automatically falls
back to cached I/O and logs a warning.
- `NWIPE_IO_MODE_DIRECT`
Force direct I/O. Devices are opened with `O_DIRECT` and there is
no fallback. If `O_DIRECT` is not supported for a device, it is
treated as a fatal condition and the device is marked disabled.
- `NWIPE_IO_MODE_CACHED`
Force kernel cached I/O. Devices are always opened without
`O_DIRECT` and no attempt is made to use direct I/O.
- Extend `nwipe_options_t` with a new field:
- `nwipe_io_mode_t io_mode;`
and initialize it to `NWIPE_IO_MODE_AUTO` in the default options setup so
that existing usage (no new flags) preserves the current behaviour.
- Add new command-line options to control the I/O mode:
- `--directio`
Sets `io_mode = NWIPE_IO_MODE_DIRECT`. This explicitly requests
`O_DIRECT` and disables the auto-fallback.
- `--cachedio`
Sets `io_mode = NWIPE_IO_MODE_CACHED`. This disables `O_DIRECT`
completely and forces classic cached I/O.
- `--io-mode=MODE`
Accepts `auto`, `direct`, or `cached` and sets `io_mode`
accordingly. Any other value results in a clear error message:
`Error: Unknown I/O mode 'X' (expected auto|direct|cached).`
- Integrate the new options into the existing long-option parsing logic
in `options.c` (`case 0:`), ensuring that:
- `--directio` and `--cachedio` are handled alongside other long
options (method, prng, verify, etc.).
- The previous unconditional `exit(EINVAL)` placeholder at the end
of `case 0` is moved to the end of the chain so that known options
(`directio`, `cachedio`, `io-mode`) are parsed correctly.
- Unknown long options still terminate with a clear error instead
of silently being ignored.
- Update `display_help()` to document the new flags:
- `--directio` Force direct I/O (O_DIRECT); fail if not supported
- `--cachedio` Force kernel cached I/O; never attempt O_DIRECT
- `--io-mode=MODE` I/O mode: auto (default), direct, cached
*nwipe.c*
- Update the device open path to honour `nwipe_options.io_mode` whenever
`NWIPE_USE_DIRECT_IO` is enabled at build time:
- Compute `open_flags` starting from `O_RDWR`.
- In `AUTO` and `DIRECT` modes, append `O_DIRECT` to `open_flags`.
- In `CACHED` mode, never add `O_DIRECT` (pure cached I/O).
- Implement mode-specific handling for `O_DIRECT` failures:
- In `AUTO` mode:
If `open()` fails with `EINVAL` or `EOPNOTSUPP`, log a warning and
retry without `O_DIRECT` (cached I/O). This preserves the previous
behaviour of “try direct I/O if available, but keep working if it
isn’t.”
- In `DIRECT` mode:
If `open()` fails with `EINVAL` or `EOPNOTSUPP`, treat this as a
fatal condition for that device. We log a clear error stating that
`O_DIRECT` was explicitly requested via `--directio` but is not
supported, mark the device as disabled, and do not silently fall
back.
- In `CACHED` mode:
Devices are always opened without `O_DIRECT`; no additional logic
is required, and behaviour matches classic buffered I/O.
- Add informational logging for the chosen I/O mode per device:
- On successful open, nwipe logs whether it is using:
- `"Using direct I/O (O_DIRECT) on device '...'.` or
- `"Using cached I/O on device '...'."`
This helps benching and debugging by making the actual mode
visible in the logs.
- For portability, ensure that builds on non-Linux / non-glibc systems
remain possible by defining `O_DIRECT` as `0` if it is not provided
by the system headers and `NWIPE_USE_DIRECT_IO` is set. This turns
`O_DIRECT` into a no-op flag on such platforms while keeping the API
intact.
Behavioural impact
- The actual wipe patterns, verification behaviour, and large-block
aligned I/O path remain unchanged. The new I/O mode only controls
how devices are opened (with or without `O_DIRECT`) and how we react
if direct I/O is not supported by the kernel or underlying filesystem.
- Default behaviour (`AUTO`) continues to “do the right thing”:
try direct I/O where available and fall back to kernel cached I/O
otherwise, with a clear log message.
- Advanced users and testers now have fine-grained control:
- `--directio` / `--io-mode=direct` for hard-fail direct I/O,
- `--cachedio` / `--io-mode=cached` to force buffered I/O,
- `--io-mode=auto` (or no flag) for the previous automatic behaviour.
- Combined with the existing large I/O buffers and aligned allocations
in `pass.c`, all three modes share the same fast, O_DIRECT-safe I/O
implementation. The new options simply toggle whether direct I/O is
requested and how strictly that requirement is enforced, which is
particularly useful for benchmarking and for diagnosing any potential
Direct I/O issues in the field.
You can now specify --pdftag to enable the
display of system UUID and system serial
number information on the PDF report.
Nwipe defaults to not displaying system IDs
but some users like to record the system UUID
or serial number on the Erasure Report along
with the disk information.