420 Commits
v0.34 ... v0.40

Author SHA1 Message Date
PartialVolume
3bf91223b9 Update nwipe.8
Update date
2026-02-03 10:14:02 +00:00
PartialVolume
7993947977 Update nwipe.8
Updated release date
2026-02-02 23:17:56 +00:00
PartialVolume
2e24184122 Update CHANGELOG.md
Update v0.40 release notes
2026-02-02 23:16:06 +00:00
PartialVolume
0fe2a32ec4 Merge pull request #709 from PartialVolume/update-host-tag-selection-for-pdf
Added an option in the config menu to toggle visibility of host UUID & host S/N in the PDF.
2026-02-02 01:06:53 +00:00
PartialVolume
9ffa4513a6 Added an extra option in the config menu to toggle visability of host UUID & host S/N in the PDF. Also changed the functionality of Tag visability. If the user enters a custom tag in the config menu it will automatically be displayed in the PDF, if you remove the custom tag, then the tag field disappears from the PDF. The command line option --pdftag, now only enables the tag field. To enable visibility of host UUID & host S/N this must be enabled in the config menu. Any change is saved to nwipe.conf and is reinstated next time nwipe starts. 2026-02-02 00:58:27 +00:00
PartialVolume
26ece7a052 Merge pull request #706 from Knogle/further-additions-for-auto-selection-for-prng-and-benchmark-no-gui-option
Further additions for auto selection for prng and benchmark no gui option
2026-01-04 13:58:13 +00:00
PartialVolume
e964dbab09 Update prng.c
Removed an unnecessary "Testing ....prng" printf to improve output clarity.
2026-01-04 13:17:29 +00:00
Fabian Druschke
7df282386e Removed annoying Testing PRNG.. printfs during init 2026-01-04 12:59:16 +01:00
Fabian Druschke
951bb5642d options: make PRNG auto-selection the default, add manual/default opt-out
PRNG auto-selection is now enabled by default when no --prng option is
specified. On startup, nwipe benchmarks the available PRNGs and selects
the fastest one for the current hardware.

To disable auto-selection, a new opt-out mode is introduced:
  --prng=default   (alias: --prng=manual)

This keeps the previous behaviour by using the built-in, CPU-based PRNG
selection without running benchmarks.

Explicit PRNG selections (e.g. --prng=isaac, --prng=aes_ctr_prng) also
disable auto-selection automatically.

Benchmark timing defaults are adjusted accordingly:
- auto-selection defaults to a short benchmark run
- --prng-benchmark-only keeps a longer default duration

This improves out-of-the-box performance while preserving full user
control and backwards compatibility.
2026-01-04 00:32:42 +01:00
Fabian Druschke
02f5d3273a prng: print benchmark progress live and show PRNG activity immediately
The PRNG auto-selection benchmark previously ran silently and only printed
results at the end. As a result, there was a noticeable delay (several
seconds) between starting nwipe and any visible output, making it appear
as if the program was stalled.

This change moves the output into the benchmark loop itself:

- Print "Analysing PRNG performance:" immediately when benchmarking starts
- Show a classic rotating cursor (-\|/) while benchmarks are running
- Print "Testing <PRNG> performance..." before each PRNG is benchmarked
- Print each PRNG’s result (MB/s or failure) immediately after it finishes

The existing nwipe_prng_benchmark_all() API is preserved for compatibility.
A new nwipe_prng_benchmark_all_live() function provides the live output
behaviour and is used for --prng=auto.

This makes startup progress visible, improves UX, and aligns behaviour
with the original maintainer’s expectations.
2026-01-04 00:25:28 +01:00
PartialVolume
75378ca87f Merge pull request #700 from Knogle/auto-selection-for-prng-and-benchmark-no-gui-option
prng, gui: add PRNG benchmarking, auto-selection and CLI support
2026-01-03 21:51:49 +00:00
PartialVolume
c8860875b3 Merge pull request #705 from PartialVolume/require-strict-long-options
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.
2026-01-03 21:47:49 +00:00
PartialVolume
096a201a66 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.
2026-01-03 21:39:25 +00:00
PartialVolume
ef04909ffe Merge pull request #704 from PartialVolume/fix_sync_occuring_in_directIO_when_no_command_line_arguments
Fix_direct I/O, fdatasync
2026-01-02 23:56:32 +00:00
PartialVolume
94548ad739 Fix_direct I/O, fdatasync
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.
2026-01-02 23:13:01 +00:00
PartialVolume
3c15a420e3 Check autonuke and nogui for valid values
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.
2025-12-31 14:01:56 +01:00
Fabian Druschke
c323e73984 Prints PRNG benchmark results to stdout 2025-12-31 11:16:31 +01:00
PartialVolume
f6840105b0 Improve str_truncate
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.
2025-12-31 11:06:53 +01:00
PartialVolume
bd1e8af845 Merge pull request #702 from PartialVolume/improve_str_truncate_function
Improve str_truncate
2025-12-31 00:37:16 +00:00
PartialVolume
07f59467a1 Improve str_truncate
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.
2025-12-31 00:19:37 +00:00
PartialVolume
a3727d7d21 Merge pull request #701 from PartialVolume/improve_integrity_of_autonuke_option_selection
Check autonuke and nogui for valid values
2025-12-30 21:15:55 +00:00
PartialVolume
9c91d19d14 Check autonuke and nogui for valid values
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.
2025-12-29 23:27:01 +00:00
Fabian Druschke
fea3d8c303 prng, gui: add PRNG benchmarking, auto-selection and CLI support
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.
2025-12-19 00:59:45 +01:00
PartialVolume
f8f64ba7e7 Merge pull request #699 from PartialVolume/update_prng_text
Reformat layout of PRNG help text.
2025-12-18 21:24:19 +00:00
PartialVolume
dcdca64db7 Reformat layout of PRNG help text.
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.
2025-12-18 21:18:12 +00:00
PartialVolume
394ba386e5 Merge pull request #697 from Knogle/device-pci-root-tree-implement
gui: add device topology view with sysfs tree (ACS line drawing)
2025-12-17 00:10:13 +00:00
PartialVolume
8e291d5bbb Update gui.c
Removed redundant snprintf commands
2025-12-17 00:03:07 +00:00
PartialVolume
76f928c73d Merge pull request #698 from Knogle/benchmark-mode
gui: add interactive PRNG benchmark mode (RAM-only throughput)
2025-12-16 23:00:50 +00:00
Fabian Druschke
ee3441f32c Removed dimmed text for visibility reasons 2025-12-16 22:55:07 +01:00
Fabian Druschke
1aaae91e88 gui: add interactive PRNG benchmark mode (RAM-only throughput)
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.
2025-12-16 12:30:39 +01:00
Fabian Druschke
c50019abc3 gui: add device topology view with sysfs tree (ACS line drawing)
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.
2025-12-15 23:43:45 +01:00
PartialVolume
b8f567bcfe Merge pull request #696 from PartialVolume/add-user-defined-tag-to-report
Allow custom text on PDF report.
2025-12-14 22:41:41 +00:00
PartialVolume
65ff9ff64c Allow custom text on PDF report.
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.
2025-12-14 22:23:00 +00:00
Fabian Druschke
4defd2235a Refactor CPUID/AES-NI detection into cpu_features module and hide AES-CTR PRNG on unsupported platforms (#695)
* 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
2025-12-11 22:46:13 +00:00
PartialVolume
9f6f465230 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
2025-12-09 15:48:43 +00:00
Fabian Druschke
1f68f35dc5 Fixed formatting. 2025-12-09 16:20:22 +01:00
Fabian Druschke
c235349288 Added testfile probe to check if destination directory is writable 2025-12-09 16:19:32 +01:00
Fabian Druschke
26e5cb9894 feat(pdf): automatically create PDF report directory if missing and improve permission model
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.
2025-12-09 10:40:06 +01:00
PartialVolume
3b6b6e0040 Merge pull request #692 from PartialVolume/Bump_version
Bump to V0.40, update CHANGELOG.md
2025-12-08 21:32:49 +00:00
PartialVolume
fc308b3f34 Bump to V0.40, update CHANGELOG.md 2025-12-08 21:26:19 +00:00
PartialVolume
6eae95b995 Merge pull request #655 from deamen/master
Add static linking libraries that are required by parted 3.6
2025-12-07 16:23:47 +00:00
PartialVolume
047de16d96 Merge pull request #683 from Knogle/feature/large-io-buffers-direct-io
Improve wipe I/O throughput with large aligned buffers and optional O_DIRECT
2025-11-29 21:07:53 +00:00
Fabian Druschke
57e337537d Fixed formatting 2025-11-29 21:00:27 +01:00
Fabian Druschke
f28bb3d3d6 Fixed missing bracket in src/options.c 2025-11-29 21:00:07 +01:00
PartialVolume
bc2643c72a Merge pull request #690 from Knogle/avoid-ui-breaking-serial
device: sanitize serial numbers and fix uninitialized/fallback handling
2025-11-28 23:16:04 +00:00
Fabian Druschke
b79530c292 Fixed uninitialized io_blocksize in random pass 2025-11-28 21:38:53 +01:00
Fabian Druschke
5f47df370e Removed german comments. 2025-11-28 16:09:07 +01:00
Fabian Druschke
9a70d23e4a device: sanitize serial numbers and fix uninitialized/fallback handling
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.
2025-11-28 12:32:43 +01:00
Fabian Druschke
259ee26273 - Fix uninitialized use of io_blocksize in nwipe_random_pass() by computing
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.
2025-11-27 23:02:28 +01:00
PartialVolume
121624478f Merge pull request #688 from Knogle/update-man
Update README and manpage
2025-11-27 17:38:16 +00:00
Fabian Druschke
c13f2477b1 Update README and manpage for v0.40: document AES-CTR, large I/O buffers, new I/O modes, BMB21 method, and improved device exclusion
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.
2025-11-27 16:23:22 +01:00
Fabian Druschke
d97c8a1014 Fix sync interval regression after large-block rewrite; restore byte-based sync semantics and disable periodic sync for direct I/O
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.
2025-11-27 10:23:31 +01:00
PartialVolume
dcfa8f4ea2 Merge pull request #660 from Knogle/kernel-aes-ni
Implement high-performance AES-256-CTR PRNG via Linux kernel AF_ALG socket
2025-11-26 21:15:17 +00:00
PartialVolume
ccf8eed4ed Merge branch 'master' into kernel-aes-ni 2025-11-26 21:14:23 +00:00
PartialVolume
375f8b3f87 Merge pull request #686 from Knogle/persistent-device-paths
Enhances the --exclude option to match devices by their underlying block
2025-11-26 20:43:10 +00:00
Fabian Druschke
c51a8e9846 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.
2025-11-26 17:15:35 +01:00
Fabian Druschke
066d62352f Add runtime I/O mode selection (auto/direct/cached) for direct I/O support
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.
2025-11-21 21:31:49 +01:00
PartialVolume
a0339c3bb1 Merge pull request #684 from PartialVolume/add_hostid_option_to_pdf
Add user selectable host information to the PDF
2025-11-18 18:59:18 +00:00
PartialVolume
0801ca7ae8 Add user selectable host information
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.
2025-11-18 18:44:41 +00:00
Fabian Druschke
6792a969dc Set DNWIP_USE_DIRECT_IO to enabled by default 2025-11-17 23:00:38 +01:00
Fabian Druschke
4150dddd84 Added logging for O_DIRECT if devices supports direct i/o 2025-11-17 20:02:54 +01:00
Fabian Druschke
2dbdaf447c Improve wipe I/O throughput with large aligned buffers and optional O_DIRECT
This change reworks the pass/verify I/O path and adds optional direct I/O
support to reduce syscall overhead and better utilize modern storage
performance.

pass.c:
- Introduce NWIPE_BUFFER_SIZE (default 16 MiB) as a generic scratch buffer size
  and NWIPE_IO_BLOCKSIZE (default 4 MiB) as the target read/write block size.
- Add nwipe_effective_io_blocksize() to compute an effective I/O block size
  per device:
  - At least device_stat.st_blksize
  - Rounded down to a multiple of st_blksize for O_DIRECT compatibility
  - Never larger than the device size
- Add nwipe_alloc_io_buffer(), which allocates I/O buffers using
  posix_memalign() aligned to the device block size (>= 512 B). This makes the
  same code safe for both buffered I/O and O_DIRECT.
- Rework nwipe_random_pass():
  - Use a large, aligned scratch buffer (default 16 MiB) instead of tiny
    st_blksize-sized buffers.
  - Generate and write data in large chunks (default 4 MiB) to drastically
    reduce the number of write() syscalls.
  - Keep the original PRNG init/read interface and the “PRNG wrote something”
    sanity check (still checks within the first st_blksize bytes).
  - Preserve existing error handling, progress accounting and periodic
    fdatasync() logic.
- Rework nwipe_random_verify():
  - Use the same large I/O block logic for read/compare.
  - Generate the expected random stream in large blocks and compare against
    data read from the device.
  - Maintain the original semantics for partial reads and error counters.
- Rework nwipe_static_pass() and nwipe_static_verify():
  - Build large pattern buffers that repeat the user-specified pattern and
    support a sliding window (w) into the pattern.
  - Perform writes/reads in large blocks (default 4 MiB) while keeping the
    pattern alignment consistent via the window offset.
  - Preserve original behaviour regarding partial I/O, logging and counters.

nwipe.c:
- Add support for optional direct I/O when NWIPE_USE_DIRECT_IO is defined:
  - Include <fcntl.h> and ensure O_DIRECT is available (fallback to 0 on
    platforms that do not define it).
  - Open devices with O_RDWR|O_DIRECT, and transparently fall back to O_RDWR
    if O_DIRECT is not supported (e.g. EINVAL/EOPNOTSUPP).
- Enable GNU extensions (e.g. _GNU_SOURCE) so that O_DIRECT is visible on
  glibc-based systems.

Behavioural impact:
- The wiping/verification algorithms and patterns are unchanged; only the I/O
  strategy is modified to use larger, aligned buffers.
- The number of read()/write() syscalls per pass is reduced by orders of
  magnitude (e.g. 4 MiB vs. 4 KiB), which should significantly increase
  throughput on fast disks/NVMe.
- When NWIPE_USE_DIRECT_IO is enabled and supported by the device, the same
  code path uses direct I/O to avoid unnecessary page cache pollution; when
  unsupported, behaviour gracefully falls back to buffered I/O.
2025-11-17 19:37:31 +01:00
PartialVolume
96127687cd Merge pull request #682 from desertwitch/upstr
fix: respect no-blanking methods in --nogui mode also
2025-11-14 23:32:26 +00:00
desertwitch
24dcbaa40c fix: outdated documentation for zero/one-fill rounds
The zero and one fill methods are no longer bound to just a single round.

Signed-off-by: desertwitch <24509509+desertwitch@users.noreply.github.com>
2025-11-15 00:12:27 +01:00
desertwitch
0d043a3745 fix: respect no-blanking methods in --nogui mode also
This fixes an issue where a default blanking pass was added to methods which do not support it when in --nogui mode.

Existing GUI code overriding the option is never called in --nogui mode, so needs handling as part of option parsing.

Signed-off-by: desertwitch <24509509+desertwitch@users.noreply.github.com>
2025-11-14 23:35:13 +01:00
PartialVolume
3e79c46f45 Merge pull request #680 from Knogle/no-fd-urandom
rng: use getrandom(2) for PRNG seeding instead of /dev/urandom fd
2025-11-14 21:29:16 +00:00
PartialVolume
ae3a8a51d1 Merge pull request #681 from PartialVolume/Reinstate_pdf_page_1_label
Reinstate page 1 label on PDF
2025-11-14 17:12:57 +00:00
PartialVolume
67df917378 Reinstate page 1 label on PDF 2025-11-14 17:08:40 +00:00
Fabian Druschke
04b79bc746 rng: use getrandom(2) for PRNG seeding instead of /dev/urandom fd
The internal PRNGs are now seeded via getrandom(2) rather than through a
long-lived file descriptor to /dev/urandom.

Previously, nwipe opened /dev/urandom once at startup, stored the file
descriptor in the nwipe_context_t (entropy_fd) and used read() on that fd
whenever seed material was required (e.g. DoD, Gutmann, OPS-II and generic
random passes). The path to the entropy source was hard-coded via
NWIPE_KNOB_ENTROPY ("/dev/urandom").

This change introduces a small helper in method.c:

  - nwipe_read_entropy(buf, len)

which calls the getrandom(2) syscall in a loop until the requested number
of bytes has been filled, handling short reads and EINTR/EAGAIN. All
former uses of read(ctx->entropy_fd, ...) for seeding have been switched
to nwipe_read_entropy(), and the error messages in those places now
correctly report "getrandom" instead of "read" as the failing operation.

The nwipe_context_t no longer carries an entropy_fd and nwipe.c no longer
opens /dev/urandom at startup nor assigns that fd to each context. The
NWIPE_KNOB_ENTROPY macro and its only use site were removed. At runtime,
nwipe now directly consumes entropy via getrandom(2), with an optional
log notice stating that getrandom(2) is used as the entropy source.

Debug behaviour is preserved: any existing code that logs or dumps the
PRNG seed or pattern values after they have been obtained continues to
work unchanged, it simply sees data that originated from getrandom(2)
instead of /dev/urandom.

Rationale for preferring getrandom(2) over /dev/urandom via fd:

  - getrandom(2) is a dedicated kernel API for CSPRNG output; it does not
    depend on any device node or path existing in /dev. This avoids a
    whole class of "weird environment" failures where /dev/urandom might
    be missing, replaced, or mounted in a surprising way inside chroots,
    containers, or minimal live systems.

  - getrandom(2) guarantees that it will block until the kernel CRNG has
    been properly initialized. Historically, /dev/urandom on very old
    kernels could be read before the CRNG was fully seeded if the caller
    did not implement extra checks; using getrandom(2) pushes that logic
    into the kernel and makes the seeding semantics explicit.

  - We no longer need to manage a process-wide entropy file descriptor:
    there is no open(), no global fd to propagate into every context, and
    nothing to close on shutdown. This simplifies the lifetime rules for
    entropy, especially in the presence of multiple worker threads or any
    future changes that might involve fork/exec.

  - By avoiding a persistent fd, we also remove the (admittedly low but
    non-zero) risk of that descriptor being accidentally clobbered,
    reused, or inherited in unexpected ways in future code changes. Each
    seed request is now an independent syscall that either succeeds or
    fails cleanly with an errno.

  - From a security perspective, /dev/urandom and getrandom(2) are backed
    by the same kernel CSPRNG on modern Linux, so we do not weaken the
    entropy source. Instead, we get stricter initialization guarantees
    and a smaller attack surface (no device node, no path resolution, no
    reliance on a specific /dev layout) while keeping performance and
    quality where they should be for a disk wiping tool.

In short, this patch keeps all existing wipe patterns and debug output in
place, but replaces the plumbing underneath so that PRNG seeding is
simpler, more robust against odd environments, and aligned with the
modern Linux API for secure random numbers.
2025-11-14 14:35:53 +01:00
PartialVolume
f83f229a6a Merge pull request #678 from PartialVolume/create_function_from_duplicated_code
Combine duplicated code into function
2025-11-13 21:49:37 +00:00
PartialVolume
30015d1be4 Combine duplicated code into function
The fifteen lines of code that creates the header
and footer text in the PDF appear in two separate
places. The first occurrence  in the create_pdf(..)
function and once in the create_header_and_footer(..)
function.

This duplicated code was combined into a third
function pdf_header_footer_text(..) and is now called
from the other functions.

This was done as I need to add some user selectable
changes to the header text that will include host
identification such as system tag, UUID, hostname
without creating further duplicated code.
2025-11-13 21:43:55 +00:00
PartialVolume
071487e4bc Merge pull request #677 from PartialVolume/Update_BMB21-2019_info
Updated UK HMG IA/IS 5 and Chinese BMB21-2019 Info
2025-11-11 20:21:13 +00:00
PartialVolume
7fdf6b379a Updated UK HMG IA/IS 5 and Chinese BMB21-2019 Info 2025-11-11 20:14:09 +00:00
PartialVolume
0e78efeb40 Merge pull request #676 from PartialVolume/Add_uuid_to_pdf_filename
Add device name, e.g sda, sdb etc to PDF filename

Closes #664
2025-11-10 23:30:52 +00:00
PartialVolume
e8c07bddc5 Add device name, e.g sda, SD etc to PDF filename
The purpose of this commit is to add an additional
identifying piece of information to the pdf filename.

This was found to be necessary in the case of a user
wiping partitions as opposed to the whole disc. Currently
when wiping a partition the model name and serial number
is missing from the pdf content and pdf filename so by adding
the device name, it make it less likely that an existing pdf
will get overwritten. This is a stop gap fix as preferably
the disk model and serial no needs to be retrieved even
wiping just one partition.

Additional functions were added including retrieval of UUID,
however UUID was found to not be available for some USB
devices when wiping partitions. The UUID function remains
in the code and the UUID if available is output to the log
but is not used anywhere else at the moment.
2025-11-10 23:21:15 +00:00
Fabian Druschke
b1dfea30d6 aes_ctr_prng: replace linear stash with lock-free ring buffer for thread-local prefetch
Replaced the old memmove-based stash buffer with a true circular (ring) buffer
for the thread-local AES-CTR PRNG prefetch mechanism Increased Buffers to 1M stash and 128 KiB block.

Key improvements:
 - Eliminates O(n) memmove() calls on buffer wrap → constant-time refill
 - Avoids redundant memory copies and improves cache locality
 - Supports larger prefetch capacities (256 KiB–1 MiB) without performance penalty
 - Adds fast-path for large reads (direct 16 KiB chunks to user buffer)
 - Aligns stash to 64 B for better cacheline performance
 - Increased prefetch size to 1M. Increased block size to 128 KiB
 - Reduced syscall overhead by increasing buffers
Result: measurable +5–20 % throughput gain on small-read workloads,
lower memory bandwidth usage, and more consistent latency across threads.
2025-11-10 20:55:22 +01:00
PartialVolume
86cf634ab6 Merge pull request #668 from xicaixiaokeke/apply-patch
Add BMB21-2019 wipe function
2025-11-09 16:42:29 +00:00
PartialVolume
1a27e0ac7e Merge branch 'master' into apply-patch 2025-11-09 16:42:08 +00:00
PartialVolume
ed74492a1d Merge pull request #672 from Extloga/master
Fixes for English text in several files
2025-09-30 21:11:49 +01:00
Extloga
b8f9307256 Fixes for consistency in nwipe.8 2025-09-30 12:38:29 +02:00
Extloga
d9ff3e8f8e Fixes for consistency in gui.c 2025-09-30 12:14:49 +02:00
Extloga
76c7820002 Update README.md for version 0.39 2025-09-30 12:08:43 +02:00
Extloga
bc1bc190b5 Update version number in version.c 2025-09-19 07:29:33 +02:00
Extloga
8fc559774d Fixes for consistency in nwipe.8 2025-09-19 07:27:35 +02:00
Extloga
f48fac0e5b Fixes for consistency in options.c 2025-09-19 07:26:02 +02:00
Extloga
a76046ed37 Fixes for formatting and consistency in options.c 2025-09-19 07:19:15 +02:00
Extloga
ae6c839e3a Fixes for consistency in gui.c 2025-09-19 07:11:39 +02:00
Extloga
34e42b3c5e Fix for orthography in prng.c 2025-09-19 07:08:47 +02:00
Extloga
2f2c0a5153 Fixes for formatting and consistency in gui.c 2025-09-19 07:06:39 +02:00
Extloga
342fb03c1d Include the algorithm of Bruce Schneier in nwipe.8 2025-09-19 06:29:19 +02:00
Extloga
8bc22175ac Update version number to 0.39 in nwipe.8 2025-09-19 06:20:06 +02:00
Extloga
39ee8cfc91 Update version number to 0.39 in configure.ac 2025-09-19 06:18:13 +02:00
Martijn van Brummelen
316b707308 release 0.39 2025-09-10 11:10:15 +02:00
Martijn van Brummelen
ae6cd21019 add tag v038.1 2025-09-10 10:21:08 +02:00
Martijn van Brummelen
a01ec958e4 Merge pull request #652 from Knogle/remove-exp-flag
Removed EXPERIMENTAL! comments for ALFG and Xoroshiro due to their ma…
2025-09-09 21:45:27 +02:00
Martijn van Brummelen
ad25e08997 Merge pull request #663 from Knogle/gcc-15-forward-declarations
fix: some declaration changes to satisfy gcc 15
2025-09-09 21:42:21 +02:00
kobe_memba
59fbac30a8 add bmb21-2019 wipe function 2025-07-27 17:45:11 +08:00
Fabian Druschke
436aa12227 fix: some declaration changes to satisfy gcc 15 2025-06-09 20:05:05 +02:00
Fabian Druschke
628e514058 Fixed has_aes_ni() as it didn't build on systems different other than x86. Now the check returns 0 if the system is other than x86. Fixed missing focus for AES-CTR prng, in certain conditions AES-CTR PRNG was not selectable through p menu. 2025-05-31 20:57:06 +02:00
Fabian Druschke
5af773eaac Implement high-performance AES-256-CTR PRNG via Linux kernel AF_ALG socket
Problem
=======
The OpenSSL-based prelimininary, not yet committed userspace PRNG in nwipe
plateaued at ~250 MB/s, becoming the primary bottleneck when wiping modern
NVMe or RAID volumes that sustain gigabytes per second.

Solution
========
Replace the OpenSSL path with a kernel-accelerated AES-256-CTR generator that
streams 16 KiB keystream blocks through the AF_ALG “ctr(aes)” skcipher:

* Added aes_ctr_prng.cpp/.h
  • Opens a per-thread AF_ALG operation socket once (lazy init).
  • Builds a two-CMSG `sendmsg()` (ALG_SET_OP + ALG_SET_IV) and a single
    `read()` per chunk – minimal syscall overhead.
  • Public state (aes_ctr_state_t) intentionally remains 256 bit to preserve
    ABI compatibility; socket FD is kept thread-local.
  • Generates exactly 16 KiB per call, advancing an internal 128-bit counter.

* Comprehensive English comments explain every function, the ABI rationale and
  the kernel interaction pattern.

Performance
-----------
On a Ryzen 9 7950X (VAES):
  • Old OpenSSL path: ~260 MB/s
  • New AF_ALG path : ~6.2 GB/s  (≈ 24× faster, CPU-bound at ~7 % load)

Safety & Compatibility
----------------------
* Falls back automatically to the kernel’s software AES if AES-NI/VAES/SVE are
  absent – no code changes required.
* No external dependencies beyond standard linux-headers.
* Optional `aes_ctr_prng_shutdown()` closes the FD, though the kernel would
  reclaim it on exit anyway.

Testing
-------
* Added unit tests for counter wraparound and deterministic output with a
  fixed seed (compared to OpenSSL reference vectors).
* Verified multi-threaded wiping on a 4 × NVMe RAID-0 → sustained device speed,
  PRNG never starved the pipeline.

Future work
-----------
* Expose chunk size as a tunable CLI flag.
* Optionally copy keystream directly into the kernel’s page cache via `splice`.

Closes: #559 (Implement High-Quality Random Number Generation Using AES-CTR Mode with OpenSSL and AES-NI Support)
2025-05-28 22:32:18 -03:00
Song Tang
16d9885577 Remove libintl from static linking library
It is not needed with parted-3.6
2025-04-09 13:22:30 +10:00
Song Tang
51e3caeb18 Conditionally check static libraries 2025-04-09 12:52:32 +10:00
Fabian Druschke
764235fc7d Removed EXPERIMENTAL! comments for ALFG and Xoroshiro due to their matured state. Some clarification on ALFG PRNG header, which is actually more a SLFG 2025-03-14 11:06:32 +01:00
PartialVolume
f594d677a7 Merge pull request #651 from Knogle/cleanup
Some cleanup in options.c, added missing xoroshiro256_prng argument in help.
2025-03-13 21:46:35 +00:00
Fabian Druschke
536ead8f2b Some cleanup in options.c, added missing xoroshiro256_prng argument in --help 2025-03-13 12:51:04 +01:00
PartialVolume
c29a17d090 Update version.c
Bumped minor version
2025-03-12 22:58:05 +00:00
PartialVolume
d630e3bd3c Merge pull request #648 from Knogle/bruce-7
Implement Bruce Schneier 7-Pass wiping method
2025-03-12 22:44:32 +00:00
Fabian Druschke
997a1867cf Implement Bruce Schneier 7-Pass wiping method
- Added a new wiping method following the Bruce Schneier 7-Pass standard.
- Overwrites the device with:
  - Pass 1: All ones (0xFF)
  - Pass 2: All zeroes (0x00)
  - Pass 3-7: Five passes of PRNG-generated random data.
- Updated method.h with the function prototype for `nwipe_bruce7`.
- Added `nwipe_bruce7()` implementation in method.c.
- Registered method label in `nwipe_method_label()`.
- Updated UI in options.c to display security level and details.
2025-03-08 18:18:13 +01:00
PartialVolume
ddb0329f03 Merge pull request #643 from PartialVolume/Fix_sandisk_endian_on_some_chipsets
Fix model name Endian for Sandisk-SunDisk drives
2025-02-09 17:11:02 +00:00
PartialVolume
b21860d336 Fix model name Endian for Sandisk-SunDisk drives 2025-02-09 17:05:57 +00:00
PartialVolume
051e1aa0c9 Merge pull request #638 from PartialVolume/bump_to_version_0.38
Bump version to v0.38
2025-01-08 22:26:45 +00:00
PartialVolume
9856cecd45 Bump version to v0.38 2025-01-08 22:23:15 +00:00
PartialVolume
e8297c642a Merge pull request #636 from PartialVolume/fix_s_shift_S_bug_#301
Fixes the s shift s bug
2025-01-05 20:27:15 +00:00
PartialVolume
bb7a251349 Fixes the s shift s bug
The s shift s bug is reported here
https://github.com/PartialVolume/shredos.x86_64/issues/301

To summarize, if no drives are selected and then the user presses
s (lower case) a warning appears indicating that the user
should press S (upper case) to start the wipe. This
warning appears for about 3 seconds but during this time if the
user presses S (upper case) nwipe would immediately complete,
having wiped no drives and requesting the user to press the
spacebar to exit. The is incorrect behaviour.
The bug doesn't appear if the user pressed S after the 3
seconds elapsed and the warning message disappeared.

This patch fixes this so that it does not exit but displays the
warning for 3 seconds and then waits for input.
2025-01-05 20:17:28 +00:00
PartialVolume
8a44ca947f Merge pull request #635 from PartialVolume/Add_font_size_toggle_to_progress_window
ShredOS specific patch to toggle font size
2025-01-04 21:35:30 +00:00
PartialVolume
270d643044 ShredOS specific patch to toggle font size
This is only relevant to ShredOS and is disabled
for other distros, as doubling font size is controlled
within the terminal or window manaegment of the distro.

When nwipe detects ShredOS it makes an additional
command available to the GUI in the drive selection window and
progress window (after the wipe has started) This command
is 'f'. Pressing the f key whether in drive selection or
progress windows will double the size of the font. Pressing 'f'
again will toggle the font size back to it's original size.

In addition and depending on whether ShredOS is detected it will
add an additional item to the help footer of both the
drive selection and progress windows. e.g. f=Font size
2025-01-04 21:30:19 +00:00
PartialVolume
618a8d6894 Merge pull request #634 from PartialVolume/Additional_fix_to_endian_code
Tidy up endian swap code
2025-01-02 17:41:52 +00:00
PartialVolume
6e4ca14770 Tidy up endian swap code
Fix missing trailing character on odd model lengths
Remove unnecessary +1 from length calculation (strlen)
Remove debug nwipe_log message
2025-01-02 17:30:04 +00:00
PartialVolume
523ebc470a Merge pull request #633 from PartialVolume/Fix_buffer_overflow
Fixes a buffer overflow
2025-01-01 23:00:49 +00:00
PartialVolume
a41d03125c Fixes a buffer overflow
Fixes a buffer overflow in the last commit. That
commit added additional model names to the endian model
swap code but did not calloc suffient storage for the
termination character.
2025-01-01 22:54:13 +00:00
PartialVolume
a9e3dd2a77 Merge pull request #630 from PartialVolume/Add_two_new_models_to_endian_switching
Fix endian for various model names on some older USB adapters
2024-12-03 22:32:16 +00:00
PartialVolume
e3dfa55356 Fix endian for model name
This patch fixes model names for Hitachi,
Toshiba, WDC Western Digital Corporation and Seagate/ST
drives when used with some USB adapters that get the
endian incorrect. Mainly older adapters.
2024-12-03 22:24:32 +00:00
PartialVolume
5bdd87f85e Merge pull request #621 from Firminator/patch-3
Update CHANGELOG.md with links to Issues
2024-10-23 13:09:11 +01:00
Firminator
3846e1e1aa Update CHANGELOG.md with links to Issues 2024-10-22 23:00:25 -04:00
PartialVolume
c7155bf514 Update version.c
Bump minor version from 0.37.1 to 0.37.2
2024-09-14 13:37:36 +01:00
PartialVolume
deaae2b9c1 Merge pull request #607 from PartialVolume/Fix_SAS_HS2
Remove hidden sector check for SAS
2024-09-14 13:35:54 +01:00
PartialVolume
1691abdb0b Remove hidden sector check for SAS 2024-09-14 13:24:05 +01:00
PartialVolume
c837e943d7 Merge pull request #606 from PartialVolume/Add_new_search_location_for_hdparm_and_smartctl
Add a new search location when looking for hdparm and smartctl.
2024-09-13 22:08:33 +01:00
PartialVolume
5506c76b65 Add a new search location when looking for
hdparm and smartctl.

The new search location is /usr/sbin/. Previously
we searched /sbin/ and /usr/bin/ but /sbin is symbolically linked
to /usr/sbin/ so just in case there was some issue with the
symbolic link we also now search /usr/sbin/
2024-09-13 21:57:31 +01:00
PartialVolume
53383285de Update version.c
Bumped minor version from 0.37 to 0.37.1
2024-09-12 22:14:11 +01:00
PartialVolume
8a93e8856d Merge pull request #605 from PartialVolume/Fix_SAS_HPA-DCO
Fixes a issue where SAS drives always respond with hidden sectors = ??? , i.e a warning.
2024-09-12 21:34:54 +01:00
PartialVolume
0fbc1ab991 Fixes a issue where SAS drives always respond
with hidden sectors = ???, i.e warning. This
patch fixes the problem so that a SAS drive
responds with hidden sectors = not applicable.
A SATA drive connected to a SAS interface should
still respond with yes or no subject to the
interface passing HPA and DCO-identify commands.
2024-09-12 21:28:37 +01:00
PartialVolume
710478d6fd Merge pull request #602 from FreeMinded/master
Fix typo in create_pdf.c
2024-09-10 19:11:23 +01:00
Pascal Mages
1511791a25 Update create_pdf.c
Fix mini typo on PRNG algorithm
2024-09-10 17:39:58 +02:00
PartialVolume
f250aee28d Merge pull request #598 from Knogle/poc-xoroshiro
Added XORoshiro-256 PRNG implementation paper and README with detailed behaviour
2024-09-07 10:36:34 +01:00
Fabian Druschke
afdbdb7ee8 Added XORoshiro-256 PRNG implementation paper and README with detailed analysis and usage instructions. 2024-09-06 23:31:34 +02:00
Fab
4278f69640 #590: Documentation: Update Readme.md with information regarding ssd media (#597)
* #587 Introduction to SSD wipe

Added information to readme.md regarding the lack of clarity about SSD wiping.

* #587

* Added guide for SSD

* Update README.md

Extended the disclaimer regarding SSD Wipe and fixed two typos

* ssd-guide compatibility note update

Added information regarding Samsung SATA SSD compatibility to sanitize command

* Added Solidigm compatibility for sata sanitize

* formatting (no content change)

* Added .DS_Store to gitignore

Added .DS_Store due for development on mac clients.

* Update ssd-guide.md

Added clarity regarding risks of incomplete sanitization of disks due to intransparency of vendor tools.
2024-09-06 13:08:56 +01:00
PartialVolume
2e6ce1630d Merge pull request #589 from PartialVolume/toggle_font_size
toggle font size for tty terminal based use with d key. Not relevant for xorg/wayland based use.
2024-08-21 22:20:57 +01:00
PartialVolume
c024addd93 If nwipe used within ShredOS, the d key will toggle the font size, has no action for other distributions 2024-08-21 22:15:40 +01:00
PartialVolume
d1edd0570a Merge pull request #588 from Knogle/patch-5
fix type error on i686 - uint64_t, by @xambroz
2024-08-19 23:14:58 +01:00
Fabian Druschke
af41c9739f fix type error on i686 - uint64_t, by @xambroz 2024-08-19 21:04:11 +02:00
PartialVolume
97deed81db Merge pull request #581 from Awire9966/patch-1
Added DiskDump
2024-05-28 21:55:53 +01:00
Andrew Cavallo
ddc63538b7 Update README.md 2024-05-27 22:11:02 -04:00
PartialVolume
28271712db Update CHANGELOG.md 2024-05-10 20:08:28 +01:00
PartialVolume
ef312ff90d Merge pull request #577 from PartialVolume/change_nwipe.1_to_nwipe.8
Request change man/nwipe.1 to man/nwipe.8
2024-05-10 19:59:04 +01:00
PartialVolume
6d6b7ac8c1 Request change man/nwipe.1 to man/nwipe.8
Manpage is named nwipe.1 which would be correct but manpage
contains section 8 which forms a mismatch(warning) in Debian.

# Man sections
1   Executable programs or shell commands
2   System calls (functions provided by the kernel)
3   Library calls (functions within program libraries)
4   Special files (usually found in /dev)
5   File formats and conventions, e.g. /etc/passwd
6   Games
7   Miscellaneous (including macro packages and conventions), e.g. man(7), groff(7), man-pages(7)
8   System administration commands (usually only for root)
9   Kernel routines [Non standard]

(requested by MvB)
2024-05-10 19:32:36 +01:00
PartialVolume
5140f92077 Merge pull request #576 from PartialVolume/master
Update CHANGELOG.md
2024-05-08 22:04:05 +01:00
PartialVolume
4893fa7ea0 Update CHANGELOG.md 2024-05-08 22:00:34 +01:00
PartialVolume
b83423a424 Merge pull request #575 from PartialVolume/bump_to_0.37
Bump to v0.37
2024-05-08 21:37:09 +01:00
PartialVolume
4394f24245 Bump to v0.37 2024-05-08 21:31:53 +01:00
PartialVolume
1ae76b5774 Merge pull request #574 from Polynomial-C/configure_ac_fix
configure.ac: Fix check for parted
2024-05-06 23:15:31 +01:00
Lars Wendler
fca8937f26 configure.ac: Fix check for parted
PKG_CHECK_MODULES needs all modules in a single list or else the following
error message appears during configure run:

  ./configure: 6807: libconfig: not found

because the macro interprets "[libconfig]" as the action that needs to be
done if parted was found.

Removed superfluous check for libconfig as requested by PartialVolume
2024-05-06 19:34:02 +02:00
PartialVolume
09d620c9c7 Update README.md
Update readme.md with the new prngs that will be available from V0.37+
2024-04-19 23:37:32 +01:00
Martijn van Brummelen
f0ed2fe7f0 Update nwipe.1
W: nwipe: wrong-manual-section 1 != 20 [usr/share/man/man1/nwipe.1.gz:1]
2024-04-16 20:11:10 +02:00
PartialVolume
9400ff5219 Merge pull request #571 from PartialVolume/Fix_conditional_jump_or_move_on_unitialised_value
Fix conditional jump on uninitialised value
2024-04-08 21:26:59 +01:00
PartialVolume
81fc479239 Fix conditional jump on unitialised value
This fixes a valgrind detected error. We check that
the number of real max sectors is greater than zero before
incrementing the value by 1. However ocassionaly the ioctl
call may not be able to obtain the dco and therefore the
ioctl data block is never populated. By zeroing the data
block prior to use and if it is not populated by the ioctl
call then the calculated real max sectors will be
zero and no increment will occur which is what we want.
2024-04-08 21:17:41 +01:00
PartialVolume
28095801ac Merge pull request #569 from AndCycle/fix_help
Add missing help for HMG IS5 enhanced
2024-04-05 12:14:54 +01:00
AndCycle
66a951722d Add missing help for HMG IS5 enhanced 2024-04-05 05:36:06 +08:00
PartialVolume
834348c8c8 Merge pull request #566 from Knogle/master
XORoshiro-256 and ALFG as default PRNG for 64-Bit and 32-Bit platforms. Also PRNG method as default method.
2024-03-28 22:09:22 +00:00
PartialVolume
e4a51c00c1 Merge pull request #565 from PartialVolume/Fix_valgrind_unconditional_jump_or_move_on_unitialised_value
Fix valgrind jump or move on unconditional value in nwipe config code
2024-03-28 17:38:21 +00:00
PartialVolume
00bcce475b Fix valgrind jump or move on unconditional 2024-03-28 17:17:19 +00:00
Fabian Druschke
0114ccf937 Fixed formatting using make format for options.c 2024-03-28 11:58:07 -03:00
Fabian Druschke
616a2ab236 Forgot nwipe_ in front of add_lagged_fibonacci, leading to compiling error 2024-03-28 11:56:54 -03:00
Fabian Druschke
e6e638c4dc Advocate for PRNG as default option, as well as XORoshiro-256 for 64-Bit systems, and Additive Lagged Fibonacci Generator for 32-Bit legacy systems 2024-03-28 11:51:47 -03:00
PartialVolume
8ad4fe200d Merge pull request #562 from Knogle/master
Fixed memory leak in line 328, writing 1 byte beyond allocated memory.
2024-03-26 21:39:28 +00:00
PartialVolume
a4ce92be6f Merge pull request #561 from PartialVolume/Update_pdf_with_xoro_and_fibonacci_prngs
Update PDF code with new PRNGs
2024-03-26 21:21:18 +00:00
PartialVolume
e851769025 Update PDF code with new PRNGs
Updated pdf to recognise XORshiro256 and
lagged Fibonacci PRNGs.
2024-03-26 21:17:23 +00:00
Fabian Druschke
c7ada53d4e Fixed memory leak in line 328, writing 1 byte beyond allocated memory. 2024-03-26 13:28:10 -03:00
PartialVolume
bb93793d7f Merge pull request #558 from Knogle/master
Renamed N and M to MT_STATE_SIZE and MT_MIDDLE_WORD due to const naming of mt19937ar-cok.h, causing conflicts with SHA-512 HMAC's libssl.
2024-03-22 01:35:17 +00:00
PartialVolume
ea386f35e7 Update gui.c with correct number of prngs 2024-03-21 22:51:31 +00:00
PartialVolume
204a82f6f0 Merge pull request #555 from Knogle/xoroshiro256
Implement XORoshiro-256 PRNG for Enhanced Random Number Generation Efficiency
2024-03-21 22:10:14 +00:00
PartialVolume
f66d838b23 Fix formatting in gui.c 2024-03-21 22:07:52 +00:00
PartialVolume
4fad31ec50 Fix a couple of formatting issues in prng.c 2024-03-21 22:03:24 +00:00
PartialVolume
0e03653ab5 Merge branch 'master' into xoroshiro256 2024-03-21 21:28:06 +00:00
Fabian Druschke
4e29f6c514 Renamed N and M to MT_STATE_SIZE and MT_MIDDLE_WORD due to const naming of mt19937ar-cok.h, causing conflicts with SHA-512 HMAC's libssl. 2024-03-21 17:28:57 -03:00
PartialVolume
d40c28bfc5 Merge pull request #556 from Knogle/fibonacci
Implemented Lagged Fibonacci generator PRNG providing high-speed, medium-quality numbers.
2024-03-21 18:49:16 +00:00
Fabian Druschke
0d0150393c Fixed stray / inside of options.c 2024-03-21 10:01:17 -03:00
Fabian Druschke
238656a5b4 Fixed leftovers from XORoshiro-256, fixed missing PRNG option in options.c 2024-03-21 05:01:10 -03:00
Fabian Druschke
a2d998d7c3 Fixed formatting 2024-03-20 17:48:55 -03:00
Fabian Druschke
a89dc8b9e9 Implemented Substract-and-Carry, now offering high-quality output. 2024-03-20 17:45:14 -03:00
Fabian Druschke
87376a0bbb Removed leftover references to AES-CTR, not belonging here. 2024-03-20 17:39:32 -03:00
Fabian Druschke
201eb565f9 Implemented Lagged Fibonacci generator PRNG providing high-speed, medium-security numbers. 2024-03-20 17:28:57 -03:00
Fabian Druschke
50bcf81ea6 Fixed src/temperature.c formatting 2024-03-19 08:25:09 -03:00
Fabian Druschke
5e532c9367 Rebased branch, fixed conflicts. Now Xoroshiro-256 in standalone branch 2024-03-19 08:24:38 -03:00
PartialVolume
cb595e139c Update README.md 2024-02-20 21:19:54 +00:00
PartialVolume
08f68f14d9 Merge pull request #549 from PartialVolume/Bump_version_to_0.36
Bump version to 0.36
2024-02-20 20:53:14 +00:00
PartialVolume
f17845c54b Updated CHANGELOG.md 2024-02-20 20:43:54 +00:00
PartialVolume
230e7f524a Bump to v0.36 2024-02-20 19:52:25 +00:00
PartialVolume
de077211e6 Merge pull request #548 from PartialVolume/Fix_some_strcpy_warnings_for_Debian
Fix some strcpy compiler warnings
2024-02-17 17:23:30 +00:00
PartialVolume
07a7c0ab0c Fix some strcpy compiler warnings
Replace three strcpy commands with strncpy,
bump minor version ready for more testing.
2024-02-17 17:00:37 +00:00
PartialVolume
2fae6ea5f5 Merge pull request #546 from PartialVolume/Determine_block_sizes_before_hidden_sector_detection
Populates device block size before hidden sector
2024-02-15 23:21:50 +00:00
PartialVolume
d6f7238cb3 Populates device block size before hidden sector
Now populates the block size (physical) before
the hidden sector determination function runs.

The naming of block/sector can be very confusing in
nwipe. So I have created a device_phy_sector_size
in the drive context, so we have

->device_sector_size // logical sector size
->device_phys_sector_size // physical sector size
->device_block_size // Usually the same as logical
size but could be increased by nwipe to encompass
multiple logical sectors, i.e a block of sectors
2024-02-15 23:11:09 +00:00
PartialVolume
c007d0f2ab Update version.c
Bump minor version from 0.35.6 to 0.35.7
2024-02-15 11:51:45 +00:00
PartialVolume
161054269c Merge pull request #543 from PartialVolume/Fix_incorrect_HS_status_for_4096_sectors_as_reported_by_libata
Fix hidden sector detection for logical 4096 size
2024-02-03 22:34:47 +00:00
PartialVolume
dd7ffab816 Fix hidden sector detection for logical 4096 size
This fixes an issue where nwipe detects a discrepancy
between the number of sectors reported by hdparm and
nwipe's own HPA/DCO functions that were reporting the
same values, using number of sectors based on 512 byte
sectors but disagreed with the number of sectors
generated from libata which was reporting the number of
sectors based on 4096 byte sectors.

This has been fixed by always calculating the number
of sectors returned by libata using 512 bytes per sector
so a direct comparison can be made to data from hdparm
& nwipe's HPA/DCO functions.
2024-02-03 20:20:39 +00:00
PartialVolume
9edebde20d Merge pull request #538 from PartialVolume/Make_footer_text_more_informative_at_end_of_all_wipes
Make completion footer more informative
2023-12-20 21:10:34 +00:00
PartialVolume
807eed0ffc Make completion footer more informative
These changes solve three issues. The first
issue was that it wasn't obvious that the
PDFs are only created once you press return
to exit after all the wipes have finished. It
is now explicitly stated in the footer message if
PDFs are enabled.

It also now specifies the logfile name on the
footer if the user has specified a log file as a
command line option. If no logfile is specified
then STDOUT is displayed.

If the user specified --PDFreportpath=noPDF on the
command line, prior to this commit it had no affect.
This is now fixed so that it disables PDF's irrespective
of what is in nwipe.conf. i.e command line options
override the entries in nwipe.conf

If the user has specified a --PDFreportpath=noPDF=/some/path
then PDF's are enabled irrespective of the value in nwipe.conf
2023-12-20 20:53:43 +00:00
PartialVolume
78f3a26795 Merge pull request #536 from PartialVolume/Bump_minor_version
Bump version from 0.35.3 to 0.35.4
2023-12-16 19:14:47 +00:00
PartialVolume
dbe7103fe9 Bump version from 0.35.3 to 0.35.4 2023-12-16 19:12:23 +00:00
PartialVolume
eef25e829e Merge pull request #535 from PartialVolume/Fix_incorrect_footer_on_return_to_preview
Fix incorrect footer on return to preview
2023-12-16 18:57:59 +00:00
PartialVolume
43bfb3a08e Fix incorrect footer on return to preview
This fixes incorrect footer text being displayed when
Enable customer/company preview is enabled and the user
select a field to be edited, completes the editing and
returns to the preview to select A for accept. Only
problem was A=Accept wasn't listed on the footer.
2023-12-16 18:50:33 +00:00
PartialVolume
fd6cca7010 Merge pull request #534 from PartialVolume/Fix_spacing_between_temperature_and_model
Place a space between temperature and model
2023-11-29 22:23:44 +00:00
PartialVolume
1220eca2ef Place a space between temperature and model
eg [36C] ST3500... and not [36C]ST3500..
2023-11-29 22:21:01 +00:00
PartialVolume
32321f49fa Merge pull request #533 from PartialVolume/Fix_config_key_shortcuts_help_text
Fix the config help messages.

Some of the config help messages that show the purpose of the keys were inconsistent or not updating correctly when going back to the previous menu. Now fixed.
2023-11-29 19:51:02 +00:00
PartialVolume
cca93f845d Fix the config help messages
Some of the help messages that show the
purpose of the keys were inconsistent or
not updated when going back. Now fixed.
2023-11-29 19:40:47 +00:00
PartialVolume
789aa134a0 Merge pull request #532 from PartialVolume/Fix_PDF_page_titles
Make smart page titles consistent with page 1

Should now read
Page 1 - Erasure Status
Page 2 - Smart Data
Page 3 - Smart Data

and not as previously
Page 1 - Erasure Status
Smart Data - Page 2
Smart Data - Page 3
2023-11-28 21:25:52 +00:00
PartialVolume
c24e248055 Make smart page titles consistent with page 1
Should now read
Page 1 - Erasure Status
Page 2 - Smart Data
Page 3 - Smart Data

and not as previously
Page 1 - Erasure Status
Smart Data - Page 2
Smart Data - Page 3
2023-11-28 21:21:48 +00:00
PartialVolume
3f78d76bac Merge pull request #531 from PartialVolume/Fix_no_auto_exit_in_non_gui_mode
Fix nwipe not auto exiting on completion in non gui mode.
2023-11-28 00:52:54 +00:00
PartialVolume
626ca3826c Fix nwipe not auto exiting on completion in non gui mode. 2023-11-27 23:39:50 +00:00
PartialVolume
f61b593093 Merge pull request #529 from PartialVolume/Fix_autopoweroff_and_nowait_when_screen_blank
Fix autopoweroff and nowait when screen blank
2023-11-24 01:14:31 +00:00
PartialVolume
7f39d81548 Fix autopoweroff and nowait when screen blank
If the user had blanked the screen, the autopoweroff
and nowait options did not work. Instead they paused
nwipe on completion of the wipe/s waiting for the b
key to be pressed which reactivated compute_stats()
function who's output indicates whether any wipes were
still active.

This was fixed so that compute_stats() is always
active while wipes are in progress, so that the
nwipe_gui_status() function will exit when all wipe
threads have completed even if the screen has been
blanked.
2023-11-24 01:06:29 +00:00
PartialVolume
d0a53f57be Merge pull request #527 from ggruber/SerialGarbage
fix: again garbage after serial number
2023-11-20 00:15:03 +00:00
Gerold Gruber
811c36b65a again garbage after serial number 2023-11-20 00:14:05 +01:00
PartialVolume
2ff23eb02c Update version.c
Bump minor version from 0.35 to 0.35.1
2023-11-17 12:04:48 +00:00
PartialVolume
7a4709da55 Merge pull request #526 from PartialVolume/Identify_mmcblk_devices
Added mmcblk device type MMC

Changed log message from "USB bridge, no pass-through support" to "Smart data unavailable" as no smart data could be caused by a non USB device such as mmcblk as well as USB devices with no ATA pass through and other devices that smartctl does not detect.
2023-11-16 20:00:13 +00:00
PartialVolume
9ee5193673 Added mmcblk device type MMC
Added the abbreviation MMC for mmcblk devices such as SD and
microSD cards and some low budget laptops.

Changed log message from "USB bridge, no pass-through support"
to "Smart data unavailable" as no smart data could be caused by
a non USB device such as mmcblk as well as USB devices with no ATA
pass through and other devices that smartctl does not detect.
2023-11-16 19:35:07 +00:00
PartialVolume
9270eda8d0 Merge pull request #523 from ggruber/master
modified disk sorting for more than 26 disks
2023-11-05 16:07:30 +00:00
Gerold Gruber
470672bc3a modified disk sorting for more than 26 disks 2023-11-05 02:02:39 +01:00
PartialVolume
d14c3da26b Merge pull request #520 from xambroz/patch-4
move the include of time.h
2023-11-04 21:29:34 +00:00
PartialVolume
4bf06d136c Merge pull request #517 from xambroz/patch-3
lower the build requirements
2023-11-04 21:22:03 +00:00
PartialVolume
2abe4975f2 Merge pull request #516 from xambroz/patch-2
move the define off64_t
2023-11-04 21:10:00 +00:00
PartialVolume
2a30ae92af Merge pull request #521 from ggruber/master
fix MAX_NUMBER_EXCLUDED_DRIVES, handle exceeding the limit, +README.md
2023-11-04 20:58:08 +00:00
Gerold Gruber
e77164f710 fix MAX_NUMBER_EXCLUDED_DRIVES, handle exceeding the limit, +README.md 2023-11-04 21:14:05 +01:00
PartialVolume
e463b10d28 Merge pull request #518 from ggruber/master
progress dots added
2023-11-04 18:36:11 +00:00
Michal Ambroz
4747ce65c6 move the include of time.h
The definition of the newly used _POSIX_SOURCE affects preprocessing of the time.h. As result on RHEL7 (possibly 6 as well) the timespec structure is not defined.
2023-11-04 15:59:38 +01:00
Gerold Gruber
28a9c70abc formatting fix 2023-11-04 13:55:11 +01:00
Gerold Gruber
2ac6ebe7ea progress dots added 2023-11-04 13:53:26 +01:00
Michal Ambroz
a70fa15bac lower the build requirements
Hello,
please lower the requirements to autoconf allowing to build rhel7/8 packages (possibly rhel6, not tested).
Thank you
Michal Ambroz
2023-11-03 22:51:59 +01:00
Michal Ambroz
398b03a11a move the define off64_t
Move the definition of the off64_t using int64_t  bellow the include stdint.h, which is actually defining it.

Without change wrong order is breaking the build on RHEL7/8/9.
2023-11-03 22:47:29 +01:00
PartialVolume
63c64b91e4 Merge pull request #514 from PartialVolume/Add_check_for_root_execution
Add_check_for_root_execution
2023-11-02 20:19:21 +00:00
PartialVolume
edfd982037 Add_check_for_root_execution 2023-11-02 20:14:52 +00:00
PartialVolume
cad05c685a Merge pull request #513 from PartialVolume/Fix_segfault_on_first_run_as_non_root
Fix_segfault_on_first_run_as_non_root
2023-11-02 19:55:47 +00:00
PartialVolume
499ad925ae Fix_segfault_on_first_run_as_non_root
Incorrectly trying to close a uninitialised file
pointer stream when the file could not be opened.
2023-11-02 19:47:53 +00:00
PartialVolume
2c17e32189 Update CHANGELOG.md
Added info about temperature thread in 0.35
2023-11-02 17:36:21 +00:00
PartialVolume
68561873ba Update README.md
Updates to README.md for version 0.35
2023-11-02 17:30:50 +00:00
PartialVolume
a30f92425c Merge pull request #512 from PartialVolume/master
Updates to CHANGELOG.md
2023-11-01 23:12:18 +00:00
PartialVolume
cb7d3d1664 Update CHANGELOG.md
Added further information about HPA/DCO
2023-11-01 23:09:22 +00:00
PartialVolume
869804f6c1 Update CHANGELOG.md
Correct some typos in CHANGELOG.md
2023-11-01 23:04:10 +00:00
PartialVolume
61cca3141e Update CHANGELOG.md
Change version number to header format ( Large, bold)
2023-11-01 23:02:22 +00:00
PartialVolume
07b83f053b Merge pull request #511 from PartialVolume/Add_changes_to_CHANGELOG.md_for_0.35_release
Update CHANGELOG.md for 0.35 release
2023-11-01 22:55:53 +00:00
PartialVolume
fd3bbd8523 Update CHANGELOG.md for 0.35 release 2023-11-01 22:52:51 +00:00
PartialVolume
1b37bcef84 Merge pull request #510 from PartialVolume/Bump_version_to_0.35
Bump version to 0.35
2023-11-01 20:13:48 +00:00
PartialVolume
f3179cf748 Bump version to 0.35 2023-11-01 20:07:33 +00:00
PartialVolume
fba63312ad Merge pull request #509 from PartialVolume/Create_PDF_Certificate_Group
Create_PDF_Certificate_Group
2023-11-01 19:52:03 +00:00
PartialVolume
1b9a376186 Create_PDF_Certificate_Group
The code that handles nwipe.conf now checks
an existing nwipe.conf for the correct default
groups. This allows future additions to the
nwipe.conf file to be added to an existing nwipe.conf
file. This makes the code more robust.

The code that handles creation of groups, settings and
values has been made into a function so any future additions
to nwipe.conf can be added with a single call to this function.

i.e
nwipe_conf_populate( "Default_Wipe.Method", "PRNG" );

The code can now handle creation of groups to a arbitrary
depth of four levels, however currently we only use a
group depth of 1.

i.e
CCC :
{
 XXX :
 {
   YYY :
   {
     ZZZ :
     {
     };
     TTT :
     {
     };
   };
 };
};
2023-11-01 19:39:43 +00:00
PartialVolume
eaa7603aaf Merge pull request #508 from PartialVolume/Fix_warning_directive_output_may_be_truncated
Fix_warning_directive_output_may_be_truncated

Fixed the following warning:

create_pdf.c:795:18: warning: ‘/nwipe_report_’ directive output may be truncated writing 14 bytes into a region of size between 1 and 4096 [-Wformat-truncation=]
2023-10-24 20:30:30 +01:00
PartialVolume
26ae763379 Fix_warning_directive_output_may_be_truncated
Fixed the following warning:

create_pdf.c:795:18: warning: ‘/nwipe_report_’ directive
output may be truncated writing 14 bytes into a region of
size between 1 and 4096 [-Wformat-truncation=]
2023-10-24 20:22:23 +01:00
PartialVolume
95021ef0c3 Merge pull request #507 from PartialVolume/Fix_message_Creating_PDF_report_in
The message that logs which directory the nwipe PDF certificate is being saved, was being printed multiple times in the middle of the erasure summary table.

This was corrected so that it only prints once at the end of the table
2023-10-23 22:15:49 +01:00
PartialVolume
8ce7b077bf Fix_message_creating_PDF_in
The message that displays where the nwipe PDF certificate
is being saved was being printed multiple times in the
middle of the erasure summary table.

This was corrected so that it only prints once at the end
of the table.
2023-10-23 22:08:23 +01:00
PartialVolume
603716e990 Merge pull request #505 from ggruber/master
sort the device list
2023-10-22 00:47:07 +01:00
PartialVolume
7fb2a77c7a Merge pull request #506 from PartialVolume/temperature_thread
Temperature Thread
2023-10-22 00:45:59 +01:00
PartialVolume
28068ebf97 Temperature Thread
Due to significant delays in obtaining drive temperature
from some drives especially SAS which was causing a noticeable
freeze of a second or two or more in the GUI wipe status
screen, being made worse the more drives that were being
simultaneously wiped.

The temperature update code was separated from the GUI code
by placing the temperature update in it's own thread.
2023-10-22 00:25:01 +01:00
Gerold Gruber
539a023fd7 sort the device list 2023-10-20 18:43:46 +02:00
PartialVolume
04dae6da32 Merge pull request #504 from ggruber/master
fixed min/max wrong set
2023-10-20 09:15:17 +01:00
Gerold Gruber
dcef86fb0b fixed min/max wrong set 2023-10-20 00:56:35 +02:00
PartialVolume
193cc8f0b2 Merge pull request #503 from PartialVolume/Fix_temperature_colour_part2
Fix_temperature_colour_part2
2023-10-17 19:43:39 +01:00
PartialVolume
1ee1bb1966 Fix_temperature_colour_part2
Reverted change in wprintw_temperature()
as that was not causing the red temperature
character.
2023-10-17 19:38:22 +01:00
PartialVolume
d9785d2b84 Merge pull request #502 from PartialVolume/red_temperature_fix
Fix red text on temperature
2023-10-17 18:12:13 +01:00
PartialVolume
c02dbe42ce Fix red text on temperature
This was due to incorrectly changing temperature
colour based on historical highest and lowest values.

Also removed some debug nwipe_log code
2023-10-17 18:03:39 +01:00
PartialVolume
7ce28ce61a Merge pull request #498 from ggruber/master
improved SCSI disk handling
2023-10-17 12:43:39 +01:00
PartialVolume
7d2517a120 Merge pull request #501 from PartialVolume/Change_HPA_message_to_stop_flashing_move_to_end_of_line
Change_HPA_message_to_stop_flashing_&_move_to_end_of_line
2023-10-17 11:24:41 +01:00
PartialVolume
725c79b9d0 Change_HPA_message_to_stop_flashing_&_move_to_end_of_line
The HPA message alternating with the drive model serial
number made reading the serial number quite a challenge
and made the display far too messy looking when displaying
20+ drives or even 10+ drives with differing HPA statuses.

Removed, alternating appearance.
Reduced size of message.
Placed after temperature, before drive model/serial in display.
2023-10-17 09:46:11 +01:00
PartialVolume
ab9d269bb1 Merge pull request #500 from PartialVolume/PDF_Gen37_single_column_smart_data
PDF_Gen37_single_column_smart_data
2023-10-15 23:22:16 +01:00
PartialVolume
508a6d2a62 PDF_Gen37_single_column_smart_data
Previously smart data was completely located
on page 2 in two columns using a variable width font.
This had two issues. The font size had to be very small
to fit on the page which made it difficult to read
and variable width fonts were used because the smart
attribute table took up too much width when all the data
was displayed in two newspaper type columns. This made the
smart attribute table look messy.

This patch now displays the smart data over page two and three
in mono spaced font and with a larger font size so it's easier
to read and the columns with the smart attribute table are now
aligned correctly.

In terms of the code, I placed the header and footer creation
code in it's own function as this is called multiple times
and will make it easier to add additional information pages
in the future.
2023-10-15 23:16:14 +01:00
PartialVolume
0bb9ee33c9 Merge pull request #499 from PartialVolume/PDFGen36_Add_Optional_Org_Customer_Preview_Prior_To_Drive_Selection
PDFGen36_Add_Optional_Org_Customer_Preview_Prior_To_Drive_Selection.
2023-10-14 22:43:14 +01:00
PartialVolume
f12ee921b8 PDFGen36_Add_Optional_Org_Customer_Preview_Prior_To_Drive_Selection.
Added new options in GUI config menu to display preview of
organisation, customer, date/time at startup prior to drive
selection. This preview is disabled as default but can be
enabled in the config menu.

The purpose of the preview is to allow the user to check &
update as required the organisation details, customer &
current date/time so that the details provided on the PDF
certificate/report are correct.
2023-10-14 22:22:14 +01:00
Gerold Gruber
53d91f4b8b fix lowest 2023-10-13 07:44:56 +02:00
Gerold Gruber
de887ea6ee get better timing information for temperature retrieval 2023-10-12 22:15:18 +02:00
Gerold Gruber
b6f97454f1 more temperatur limit fake for SAS disks 2023-10-12 21:44:51 +02:00
Gerold Gruber
6a92755cf2 detect and display if disk is a SSD 2023-10-12 21:15:31 +02:00
Gerold Gruber
68a6002ff1 temp statistics for SAS drives 2023-10-12 12:37:32 +02:00
Gerold Gruber
47112c4de2 some code cleaning, time debugging added 2023-10-11 00:05:57 +02:00
Gerold Gruber
97efd12b93 fixes 2023-10-10 14:32:32 +02:00
Gerold Gruber
3bc3de37ae included hddtemp code for SCSI disk temperatures 2023-10-10 14:01:20 +02:00
PartialVolume
525cf037c5 Merge pull request #496 from ggruber/master
bustype detection with smartctl works (again?)
2023-10-06 20:38:57 +01:00
Gerold Gruber
f764c39390 bustype detection with smartctl works (again?) 2023-10-06 00:45:44 +02:00
PartialVolume
c6ff341f7e Merge pull request #494 from PartialVolume/fix_endian_model_name
fix_endian_model_name
2023-10-02 22:48:45 +01:00
PartialVolume
948368bc28 Merge pull request #493 from ggruber/master
fix for issue #492
2023-10-02 22:48:22 +01:00
PartialVolume
59d73107f6 fix_endian_model_name
Some IDE USB adapter chipsets get the endian wrong for model name
This fix identifies a endian swapped model name and corrects it.

The function currently only includes Samsung but I'll add more IDE
drive manufacturers as I come across them.

This is peculiar to some USB IDE adapters. I've never seen this
issue with SATA, ATA drives connected directly to the motherboard.
2023-10-02 22:37:54 +01:00
Gerold Gruber
8124e6b3d5 added -P option for PDFreportpath 2023-10-02 20:28:49 +02:00
Gerold Gruber
c1e943d283 improved formatted fix for issue #492 2023-10-02 17:30:28 +02:00
Gerold Gruber
8124d2d493 improved fix for issue #492 2023-10-02 16:42:41 +02:00
Gerold Gruber
7c9939e228 possible solution for issue #418 2023-10-02 00:21:49 +02:00
Gerold Gruber
4675a17693 manpage typo 2023-10-01 23:52:38 +02:00
Gerold Gruber
bbcb4fc219 fix for issue #492 2023-10-01 23:45:48 +02:00
PartialVolume
65a3a45a38 Merge pull request #491 from PartialVolume/Fix_segfault_on_logfile_write_permissions
Fix intermittent segfault when --logfile specified without insufficient permissions to write to directory
2023-09-29 21:22:20 +01:00
PartialVolume
f1fe7d6bd7 Fix intermittent segfault when --logfile specified
Under the following conditions a segfault will
occur upon starting nwipe.

Run nwipe with --logfile option AND run nwipe in a directory that does
not have write permissions so the logfile can't be created AND run nwipe
without sudo or root or as as a user without sufficient privileges so
you don't have permissions to write to the directory.
2023-09-29 21:05:31 +01:00
PartialVolume
50951dff03 Merge pull request #490 from PartialVolume/PDFGen35
PDFGen35 Complete Date & time functions
2023-09-28 22:31:58 +01:00
PartialVolume
2d730eac2e PDFGen35 Complete Date & time functions
Add functions:
nwipe_gui_set_system_day()
nwipe_gui_set_system_hour()
nwipe_gui_set_system_minute()
2023-09-28 22:15:52 +01:00
PartialVolume
16b87cc2bd Merge pull request #489 from PartialVolume/PDFGen34
Created further date/time functions.
2023-09-14 22:16:34 +01:00
PartialVolume
ea1c2019cd Created further date/time functions.
write_system_datetime() and
nwipe_gui_set_system_month()
2023-09-14 22:07:37 +01:00
PartialVolume
c5b4e58cee Merge pull request #487 from PartialVolume/PDFGen33
Create function read_system_datetime()
2023-09-05 19:09:49 +01:00
PartialVolume
8a46658820 Create function read_system_datetime()
The purpose of this is to read year, month, day, hours
minutes and seconds into variables for updating in the GUI
prior to writing back to the system.

For systems isolated from the internet this will allow the
user to update system date/time from the nwipe GUI rather than
having to drop into the command line.

To complete this feature next will be to create the write_system_datetime
function.
2023-09-05 19:01:08 +01:00
PartialVolume
1e50f2fd2c Merge pull request #485 from PartialVolume/PDFGen32
PDFGen32 Create Set Date/Time window in GUI
2023-08-14 22:01:19 +01:00
PartialVolume
60d38a80e4 PDFGen32 Create Set Date/Time window in GUI
Created a window where you can edit the systems
date and time. To be completed.
2023-08-14 21:53:10 +01:00
PartialVolume
c72f740587 Merge pull request #484 from PartialVolume/PDFGen31
PDFGen31 Created truncate string function
2023-08-11 22:29:58 +01:00
PartialVolume
ba4c4c51f1 PDFGen31 Created truncate string function
Created a function that truncates a string that is printed
to a window if the string should exceed the window width.

This prevents the line wrap that occurs with mvwprintw
when the window width is not wide enough. This mainly
occurs with a 4:3 ratio monitor, 80 columns wide.
2023-08-11 21:41:48 +01:00
PartialVolume
86d71d0858 Update README.md
Removed the download link to the build script. As it's only a short script it can be copied and pasted from the github page.
2023-08-08 11:12:24 +01:00
PartialVolume
bb0cd2258d Update README.md - Add libconfig-dev libconfig++-dev
Added libconfig-dev libconfig++-dev to the build script
2023-08-08 11:03:26 +01:00
PartialVolume
4c703a6922 Merge pull request #483 from PartialVolume/PDFGen30
PDFGen30 Added org, customer & date/time preview
2023-08-05 23:26:32 +01:00
PartialVolume
9f92ec1b29 PDFGen30 Added org, customer & date/time preview
Created a window that shows a preview of organisational,
customer and date time information that will be added to
the PDF report. Items within the preview can be selected
which takes you to the relevant window for editing the
data. This can be optionally displayed before the drive
selection window is displayed.

To complete this I need to add a further entry in the
config window to allow editing date & time. Plus a
function needs to be added to truncate the strings
based on window width as some address strings could
be wider than the window and mess up the display as
they wrap to the next line.
2023-08-05 23:13:41 +01:00
PartialVolume
17a391f971 Merge pull request #482 from PartialVolume/PDFGen29
PDFGen29 Fix incorrect customer deletion/selection
2023-08-04 17:29:14 +01:00
PartialVolume
b40563ab22 PDFGen29 Fix incorrect customer deletion/selection
When using backspace or esc to abort customer deletion,
first customer in list would incorrectly be deleted.
Corrected by this fix.

Also when aborting customer selection, first customer
in list would be automatically selected. Also corrected
by this fix.
2023-08-04 17:14:39 +01:00
PartialVolume
7fc4325d69 Merge pull request #481 from PartialVolume/PDFGen28
PDFGen28 Completed customer delete feature.
2023-08-03 00:15:57 +01:00
PartialVolume
c137736997 PDFGen28 Completed customer delete feature. 2023-08-02 23:54:35 +01:00
PartialVolume
1a3e7eb23e Merge pull request #480 from PartialVolume/PDFGen27
PDFGen27 Cleaned up customer selection list
2023-07-27 23:47:15 +01:00
PartialVolume
e6034cf94e PDFGen27 Cleaned up customer selection list
Removed the csv field double quotes from the customer
selection list as displayed on screen. The double quotes will
still obviously exist in the csv file and are required
for the code to work correctly in various places. However, for
human readable text they are removed before displaying the
customer details line on the selection screen as it makes the
list look less 'busy' on screen hopefully.

I also fixed a issue where spaces were incorrectly being removed
from the customer details when filtering for printable characters
only.
2023-07-27 23:36:11 +01:00
PartialVolume
ce628e5c17 Merge pull request #479 from PartialVolume/PDFGen26
PDFGen26 Completed the code that adds a customer
2023-07-27 02:51:00 +01:00
PartialVolume
3dcc40c0e7 PDFGen26 Completed the code that adds a customer
Completed the code that adds a customer and saves
in /etc/nwipe.customers.csv so they can be selected
for display on the PDF report.
2023-07-27 02:46:09 +01:00
PartialVolume
1582f8c224 Merge pull request #477 from PartialVolume/PDFGen25
PDFGen25 Continuation of add customer csv
2023-07-25 22:24:57 +01:00
PartialVolume
a3fd0b71ca PDFGen25 Continuation of add customer csv
Continuation of add a customer entry to the
customers.csv file.

Plus a few corrections to earlier code.
2023-07-25 22:16:42 +01:00
PartialVolume
580429a371 Merge pull request #476 from PartialVolume/PDFGen24
PDFGen24 Created dialogs for adding a customer
2023-07-24 23:09:46 +01:00
PartialVolume
b46575cd63 PDFGen24 Created dialogs for adding a customer 2023-07-24 22:56:42 +01:00
PartialVolume
0d403a20a3 Merge pull request #475 from PartialVolume/PDFGen23
Completed the code that writes the user selected customer to nwipe's config file
2023-07-19 01:12:05 +01:00
PartialVolume
baefd77605 Completed code that writes selected customer to nwipe's config file /etc/nwipe/nwipe.conf 2023-07-19 01:03:49 +01:00
PartialVolume
9f8aecaa52 Merge pull request #473 from PartialVolume/PDFGen22
PDFGen22 Further work completed on customer selection and deletion
2023-07-18 02:10:43 +01:00
PartialVolume
0948596559 PDFGen22 Further work completed on customer selection and deletion dialogs 2023-07-18 02:06:33 +01:00
PartialVolume
2630edbea1 Merge pull request #472 from PartialVolume/PDFGen21
PDFGen21 Completed all GUI dialog boxes for entering organisational data.
2023-07-14 23:12:33 +01:00
PartialVolume
e74ae252ef PDFGen21 Completed all GUI dialog boxes for entering organisational data. Confirmed the data is correctly saved to /etc/nwipe/nwipe.conf and read back for automatic entry into the PDF erasure report. Further work is required to create dialog entry screens for customer details.. to be continued. 2023-07-14 23:00:17 +01:00
PartialVolume
2ebea57008 Merge pull request #471 from PartialVolume/PDFGen20
PDFGen20 Started creating new GUI dialogs.  A Configuration dialog and sub dialogs for allowing the user to enter business and customer details. Further dialog windows and code to be added...
2023-07-13 22:34:31 +01:00
PartialVolume
247cf3031b PDFGen20 Started creating new GUI dialogs. A Configuration Dialog and sub dialogs for allowing the user to enter business and customer details. Further dialog windows and code to be added... 2023-07-13 22:28:23 +01:00
PartialVolume
a05784f05a Merge pull request #470 from PartialVolume/PDFGen19
PDFGen19 Completed data extraction from nwipe.conf
2023-07-12 20:45:09 +01:00
PartialVolume
6f453967f6 PDFGen19 Completed the code the extracts the organisation and selected customer details from the nwipe.conf configuration file and populates the business and customer details boxes on the PDF report. 2023-07-12 20:37:08 +01:00
PartialVolume
0649f1c606 Merge pull request #469 from PartialVolume/PDFGen18
PDFGen18 - further work on PDF certificate
2023-07-11 23:36:26 +01:00
PartialVolume
174072db2e PDFGen18 - further work on PDF certificate, basic construction & format of the nwipe.conf file for PDF organisation and customer details 2023-07-11 23:30:03 +01:00
PartialVolume
60385f479a Update README.md
Remove old 16.04 badge
2023-07-03 01:25:02 +01:00
PartialVolume
49d82eebc9 Merge pull request #467 from PartialVolume/Fix_intermittent_exit_when_sizing_console
Fix the premature exit when sizing konsole
2023-06-30 23:57:36 +01:00
PartialVolume
d7b1fdf2ce Fix the premature exit when sizing konsole
If you are running nwipe within the KDE konsole
terminal and you resize the window by pulling on
the corners, occasionally nwipe will exit with the
error message:

"GUI.c,nwipe_gui_select(), loop runaway,
did you close the terminal without exiting nwipe?
Initiating shutdown now"

The loop runaway detection has been made less sensitive,
i.e 32 iterations per second of the GUI update can now
be completed before a loop runaway is detected. previously
it was 8. In practise when sizing the konsole window,
anywhere between 1 and 17 iterations will occur.
2023-06-30 23:40:23 +01:00
PartialVolume
21ef97e355 Merge pull request #465 from PartialVolume/HPA_DCO_013
HPA_DCO_013 Continuation of HPA/DCO integration
2023-04-12 23:34:46 +01:00
PartialVolume
447c4eddf7 HPA_DCO_013 Continuation of HPA/DCO integration
Fixes to apparent and real disc size fields in PDF based on use
of c->Calculated_real_max_size_in_bytes.

Minor changes to HPA status messages for consistent messaging.

When HPA and DCO sector information cannot be obtained display the
message "HPA/DCO data unavailable, can not determine hidden sector
status" in the information field on the PDF.

Determine human readable size for the c->Calculated_real_max_size_in_bytes
as used in the PDF real disc size field.

Instead of >>FAILURE!<< -1 when a I/O error occurs display >>IOERROR!<< -1
in the GUI.
2023-04-12 23:29:13 +01:00
PartialVolume
3a9ff16d29 Merge pull request #464 from PartialVolume/HPA_DCO_012
HPA_DCO_012 Continuation of HPA/DCO integration
2023-04-11 22:47:31 +01:00
PartialVolume
d981159f04 HPA_DCO_012 Continuation of HPA/DCO integration
Simplified the code that determines whether a HPA/DCO
is present and in the process fixed some related bugs
to do with the HPA/DCO fields in the PDF document
displaying incorrect information in some drive specific
cases.
2023-04-11 22:42:16 +01:00
PartialVolume
3d2ba87967 Merge pull request #463 from PartialVolume/HPA_DCO_011
HPA_DCO_011 Continuation of HPA/DCO integration.
2023-04-05 22:58:02 +01:00
PartialVolume
e7fca73970 HPA_DCO_011 Continuation of HPA/DCO integration.
Fixed a issue that occurs when a drive is attached via a USB adapter
that doesn't correctly pass the ATA commands for HPA/DCO determination.

It incorrectly set the HPA_status as enabled when in fact it should
have determined that DCO was accessible and a comparison against
the size as returned by libata and the real max sectors which in this
particular adapters case worked, was sufficient to correctly determine
whether there were hidden sectors or not.

Now fixed, so HPA status is now determined correctly even on some
less than ideal USB adapters.
2023-04-05 22:50:39 +01:00
PartialVolume
dfa78567e0 Merge pull request #462 from PartialVolume/HPA_DCO_010
HPA_DCO_010 Continuation of HPA/DCO integration.
2023-04-05 18:12:16 +01:00
PartialVolume
30997876a5 HPA_DCO_010 Continuation of HPA/DCO integration.
The Seagate EXOS family of drives don't support the ATA
drive configuration overlay (DCO) command, however they do
support host protected area (HPA). Therefore these drives
can have hidden sectors.

The code was changed to accommodate the slightly different
way these drives respond to a hdparm -N, that is, the
response is accessible max address enabled or
accessible max address disabled. The new code correctly
determines a the status of a drive in regards to
whether it has hidden sectors or not and what it's
apparent and real size is.
2023-04-05 18:05:54 +01:00
PartialVolume
0c41d895e2 Merge pull request #461 from PartialVolume/HPA_DCO_009
HPA_DCO_009 Continuation of HPA/DCO integration.
2023-04-04 23:58:42 +01:00
PartialVolume
776717a729 HPA_DCO_009 Continuation of HPA/DCO integration.
Changed the way percentage is calculated. As we
are dealing with large numbers and for some reason
printf prints a double that is 99.99999999999 as
100.00 when a precision of two is specified I wrote
a function that creates a percentage string such that
99.99999999999 is displayed as 99.99%. This is important
in the report as a disc with one hidden sector will now
correctly show as 99.99% erased and not 100.00% erased.
2023-04-04 23:52:40 +01:00
PartialVolume
a1e607dc02 Merge pull request #460 from PartialVolume/HPA_DCO_008
HPA_DCO_008 continuation of HPA/DCO integration.
2023-04-03 23:19:20 +01:00
PartialVolume
0b720b155c HPA_DCO_008 continuation of HPA/DCO integration.
Many changes related to DCO real max sectors and HPA set and real
sectors and how they are processed and used in the PDF creation
function. More testing is required until I'm happy this works
correctly with lots of different hardware including USB adapters
that don't support DCO/HPA and also drives that don't support
DCO/HPA such as the Seagate Enterprise EXOS drives.
2023-04-03 23:11:41 +01:00
PartialVolume
4fc1806880 Merge pull request #459 from PartialVolume/Fix_very_obscure_incorrect_percentage_on_fill_ones_only
Fix obscure incorrect percentage on completion.
2023-03-20 19:51:34 +00:00
PartialVolume
bbfaa0e9f7 Fix obscure incorrect percentage on completion.
This bug only applies to ones wipe and one or zero's
verification.

A very rare occurrence of a incorrect percentage on
completion. The actual wipe was completed correctly it
was just that the percentage calculation was wrong.

This was due to a variable that was initialised for
all methods except ones, & ones & zeros verification.

For those three method's the uninitialised variable
would have had to have the value 1 or 6 for the
incorrect percentage to be calculated, this is why
it was so rare.

Corrected by initialising the variable with a -1 so
that if no method was found in the array then no
extra calculations as regards the percentage are
performed.
2023-03-20 19:39:16 +00:00
PartialVolume
4f5c15ffad Merge pull request #458 from PartialVolume/nwipe_conf_001
nwipe_conf_001 Add /etc/nwipe/nwipe.conf
2023-03-17 23:23:54 +00:00
PartialVolume
dbde867974 nwipe_conf_001 Add /etc/nwipe/nwipe.conf
1. Started constructing the code we will use to access
and write to /etc/nwipe/nwipe.conf. The initial use of
the nwipe.conf file will be to hold information such
as organisation and technician details that will be used
on the PDF report certificate. We are using the libconfig
library enable us to read and write the conf file. libconfig
is very well documented so editing the conf file manually
should be pretty straight forward. Nwipe will build a basic
conf file to give the user an idea how it should be edited.

2. Updated the README.md, showing the libconfig-dev, libconfig++-dev
needs to be installed.

3. Added some information about the use of hdparm to detect hidden
sectors and also noted that as from v0.35 nwipe will detect hidden
sectors, and may well have an extra method to expose those hidden#
sectors by restoring the DCO.
2023-03-17 23:18:39 +00:00
PartialVolume
a51a9b3e2a Merge pull request #457 from PartialVolume/HPA_DCO_007
HPA_DCO_007 - Add HPA/DCO capability
2023-03-16 21:12:24 +00:00
PartialVolume
3d5fdd3f11 HPA_DCO_007 - Add HPA/DCO capability
1. Fix issue in PDF certificate where a drive that doesn't
support device configuration overlay, shows 512 bytes as the
disc size in the "Size(Real)" area on the certificate.

2. Fix a comment in the log that suggests you might be using a USB
adapter or memory stick and those device can cause an indeterminate
HPA/DCO status. This is now corrected so that we check the bus
is USB before we issue that message.

3. Changes to some GUI text, replaced 'area' with 'sectors'.

4. Aligned "HPA/DCO No hidden sectors detected" to the drive
model/serial text that it alternates with. One character left.

5. Nwipe's version and operating system information that is always
printed to the console on exit, was not appearing in the optional
log file, as optionally specified on the command line. This was
caused because those nwipe_log() calls were happening before the
command line options had been parsed. Essentially there was no log file.
Comments were made in the nwipe.c as a reminder to use nwipe_log()
calls with caution, prior to the options parser.

6. Made changes to the logic in the HPA_dco functions such that
a device that does not appear to support device configuration
overlay and host protected area is classified as HPA_NOT APPLICABLE
in regards to the hpa_status which is important for the PDF
certificate code so that the fields in the certificate are updated
appropriately.
2023-03-16 20:57:07 +00:00
PartialVolume
5051e5c7bf Merge pull request #456 from PartialVolume/Validate_temperatures_before_using_to_change_temperature_text_color
Validate temperatures.
2023-03-16 00:54:20 +00:00
PartialVolume
5d8c0cc4bf Validate temperatures.
Now validates temperatures rather than assuming the data
is valid.

1. Checks for 0 in high critical and max.
2. Copes with missing critical or max min data or
partially missing data.
3. Checks the high critical and max are the right way round
4. Checks the low critical and min are the right way round
5. Temperature is displayed in 5 different ways
i. white text on blue - temperature with spec. (or no spec available)
ii. red text on blue - max temperature reached.
iii. white text on red - critical high temperature reached.
iv. black text on blue - minimum temperature reached.
v. white text on black - critical low temperature reached.
2023-03-16 00:43:19 +00:00
PartialVolume
c79c8ad8f7 Merge pull request #455 from PartialVolume/add_min_max_temperatures_to_log
Added max, min drive temperatures to log
2023-03-14 23:57:09 +00:00
PartialVolume
79cedf47fc Added max, min drive temperatures to log
Added the max and min drive temperatures to the nwipe
log. We now have upto six temperatures that may be reported
for each drive. Whether all these fields are populated is
down to each drive manufacturer.
2023-03-14 23:53:21 +00:00
PartialVolume
08cba2ee5e Merge pull request #454 from PartialVolume/Fix_anomaly_when_temperature_changes_color
Fix anomaly with color change of temperature
2023-03-14 22:30:19 +00:00
PartialVolume
4368092d11 Fix anomaly with color change of temperature
Temperature text now changes red when it's exceeded
the high critical temperature and black when it's dropped below the low
critical temperature. See nwipes log for what those values actually are
as those critical temperature values are obtained from each drive.

When the drive temperature is within the drives specification the temperature
text will be white text on blue background.

The temperature text no longer flashes red or black.
2023-03-14 22:27:34 +00:00
PartialVolume
ac96c51120 Merge pull request #453 from PartialVolume/add_temperature_limits_to_log_info
Add temperature limits to nwipe log
2023-03-14 20:42:37 +00:00
PartialVolume
c4490a89d1 Add temperature limits to nwipe log
1. For each drive we now show the following temperature
limits that are obtained directly from the drive. These are
shown in the log under the INFO category. For a given drive
all it's limits are shown on a single line near the start of
the log.

Example:
Temperature limits for /dev/nvme0n1, critical=84c, highest=N/A, lowest=N/A, low critical=N/A.

High critical
Highest
Lowest
Low critical

Whether all these values contain data various between
manufacturer, for instance the Seagate SN570 only shows
the high critical value of 84 deg.C

2. Changed some wording in the GUI for a on screen
message to do with HPA/DCO.
2023-03-14 20:11:00 +00:00
PartialVolume
87d25cf752 Merge pull request #452 from PartialVolume/PDFGen17
PDFGen17 - further work on PDF certificate
2023-03-12 18:15:39 +00:00
PartialVolume
c3cd5011a2 PDFGen17 - further work on PDF certificate
1. I wasn't keen on my first attempt at the exclamation
icon so I redesigned it.I think this one looks better.
2023-03-12 16:34:43 +00:00
PartialVolume
1b3d03faad Merge pull request #451 from PartialVolume/PDFGen16
PDFGen16 - further work on PDF certificate
2023-03-11 22:45:31 +00:00
PartialVolume
f258872c67 PDFGen16 - further work on PDF certificate
1. Added a new icon, a yellow exclamation in a red circle
with the words "erased with a warning" inside the circle.
This is used instead of the green tick icon or red cross icon
when a disk is successfully erase as requested but the user
chose not to expose the hidden sectors by removing the HPA and/or
restoring the drive configuration overlay (DCO).

2. Expanded the size of the "Disk Erasure Details" section on
the certificate by 20 points to provide better spacing for warnings

3. Removed remapped sectors label, as that information already
appears on page 2 in the smart data.

4. Improvements to the logic in regards to text colour changes.

5. Added a additional red warning message in the information section
when the exposed sectors of the disc are erased but the hidden sectors
are not.

6. Added the original images in .xcf (gimp) and jpg formats that are
used to generate the embedded .c and .h files using bin2c.
2023-03-11 22:24:34 +00:00
PartialVolume
d231c863f7 Merge pull request #450 from PartialVolume/PDFGen15
PDFGen15 - further work on PDF certificate
2023-03-10 23:41:45 +00:00
PartialVolume
b35400b22a PDFGen15 - further work on PDF certificate
1. Added page 2 of the report which includes all
smart data as displayed by smartctl -a /dev/sdx

2. Did some more work on the logic, i.e. when certain
data on the certificate should be green and when it should be
red.

3. Made some changes to nwipe's log in regards to the way
it displays disc info right at the beginning of the log.
Separated the drive information by a blank line to make
each drive stand out from the rest.

4. Moved a string function from device.c to miscellaneous.c

more code to follow ..
2023-03-10 23:33:46 +00:00
PartialVolume
c52dfa3d8f Merge pull request #449 from PartialVolume/HPA_DCO_006
HPA_DCO_006 - Add HPA/DCO capability
2023-03-10 00:52:14 +00:00
PartialVolume
e6a9944a7a HPA_DCO_006 - Add HPA/DCO capability
1. If nwipe enumerates a USB bridge that does not support
ATA pass through or you are wiping a USB memory stick,
nwipe will show a message in the GUI that says
"HPA/DCO hidden area indeterminate". If you get this message
you know to either get yourself a better quality USB to SATA
adapter or in the case of a USB memory stick, i've never
come across one that supports HPA/DCO so either don't worry
about it and wipe it as normal or physically destroy it.

2.We now check for a nonsense "real max sectors" as produced
by the bug in hdparm. We use our own low level function to
issue a DCO identify command and retrieve the correct value.

3. Changed the dmidecode info at the start of the log from
a 'notice' classification to 'info'.

4. Made changes to the nwipe_log function so that any messages
logged that are classified as 'debug' will not be logged unless
the --verbose flag has been set on the command line options. I
send the hex data structures and hex sense data to the logs as debug
information and as they can take up a lot of space and make the
log look untidy they will only appear in --verbose mode.

5. I also extended the nwipe's log message length from 512 to 1024
as some of the sense data was being truncated in the log.
2023-03-10 00:28:31 +00:00
PartialVolume
9c0dd6a25a Merge pull request #448 from PartialVolume/HPA_DCO_005
HPA_DCO_005 - Add HPA/DCO capability
2023-03-08 23:37:46 +00:00
PartialVolume
67ca3b260d HPA_DCO_005 - Add HPA/DCO capability
1. Created the function nwipe_read_dco_real_max_sectors()
which directly accesses the disk drive sending a 0xB1
device configuration overlay identify command to the drive.
We read the returned data structure extracting the real max
sectors value. We do this if hdparm returns nonsense for the
real max sector (as it does in hdparm v9.60) for some larger
drives. This value is automatically sent to the nwipe log FYI.

2. Added a stdout & stderr pipe to the hdparm commands as verbose
data appears to be sent to stderr, as we are interested in that
data this pipe captures stderr as well as stdout.

3. Added headers "scsi/sg.h" and "scsi/scsi_ioctl.h" as we
are now sending low level commands to the drives.
2023-03-08 23:30:14 +00:00
PartialVolume
c97d1c9166 Merge pull request #447 from PartialVolume/HPA_DCO_004
HPA_DCO_004 - Add HPA/DCO Capability
2023-03-08 00:34:53 +00:00
PartialVolume
4b6399a93f HPA_DCO_004 - Add HPA/DCO Capability
1. In the GUI I switched the HPA/DCO status position
from overlaying the [drive size][temp] and instead
positioning the HPA/DCO status over the drive model
and serial number. HPA/DCO information and drive details
alternating every couple of seconds. This allowed me to
extend the length of the HPA message and make it more
meaningful to somebody that doesn't know the HPA/DCO
terminology. Therefore "HPA Enabled" is replaced with
"HPA/DCO Warning, hidden area detected"

2. Started adding bad/missing sense data detection ..

more code to follow ..
2023-03-08 00:20:22 +00:00
PartialVolume
518b8b536a Merge pull request #446 from PartialVolume/PDFGen14
PDFGen14 - further work on PDF certificate
2023-03-05 23:48:26 +00:00
PartialVolume
b79eea6c9f PDFGen14 - further work on PDF certificate
1. Changes to colour on some items under certain
conditions.

2. Changed the HPA labels to include DCO, and changed
the text from HPA enabled/disabled to something more
meaningful to a user, i.e Hidden area found, no hidden
area.

3. Added a new define "HPA_NOT_SUPPORTED_BY_DRIVE" for
recent SATA drives that no longer support HPA/DCO.
Further work needs to be done determining whether a drive
supports HPA/DCO or not in the hpa_dco.c functions.

more code to follow ..
2023-03-05 23:24:49 +00:00
PartialVolume
88cec08854 Merge pull request #445 from PartialVolume/PDFGen13
PDFGen13 - further work on PDF certificate
2023-03-05 00:49:34 +00:00
PartialVolume
dc6d76176a PDFGen13 - further work on PDF certificate
1. Reduced font size for data info.
2. Used a define for text point size to make it easier
to change size.
3. Started work on function to parse sense data for older
versions of hdparm (9.60) that fail to parse the real max
sectors correctly. (fixed in 9.65).
4. Miscellaneous changes.

More changes to follow ..
2023-03-05 00:42:37 +00:00
PartialVolume
3eb5a43a32 Merge pull request #443 from PartialVolume/HPA_DCO_003
HPA_DCO_003 - Add HPA,DCO capability
2023-02-28 23:55:46 +00:00
PartialVolume
8abab96a3e HPA_DCO_003 - Add HPA,DCO capability
1. For devices that support HPA/DCO (not NVMe) nwipe
displays the HPA status, it toggles every two seconds
the size and the temperature i.e "[  1TB][ 35C]" with
the HPA status such as [HPA disabled], [HPA ENABLED!],
[HPA unknown]. The HPA ENABLED is highlighted with red
text and a white background. HPA_disabled is standard
white text on blue background.

More code to follow, a new option to be added, HPA, disable/enable
which will disable the HPA and shut down the system. On manually
powering back up the HPA should be reported as disabled.

Then lots of testing.
2023-02-28 23:48:17 +00:00
PartialVolume
7ccb68675b Merge pull request #442 from PartialVolume/HPA_DCO_002
HPA_DCO_002 - Add HPA, DCO capability
2023-02-28 00:10:26 +00:00
PartialVolume
c7d17df7f8 HPA_DCO_002 - Add HPA, DCO capability
1. Further work was completed, putting the HPA status and HPA size into
the report/certificate.

2. Moved some general functions that may be used throughout nwipe
into miscellaneous.c These tend to be string processing functions.

3. Tested certificate HPA status. There are three possible states of the HPA
i. Enabled and HPA size is displayed on the certificate
ii. Disabled, the drive reports it's real size to the O.S.
iii. Unknown. Nwipe could not determine whether there is a HPA or not.
This may be caused by the use of a USB adapter or could be a bad drive,
or finally a change in hdparms format when displaying HPA information.

More code to follow, next on the list is to add the HPA status into the
drive selection screen in the GUI, so when you select the drive you can see
the status of HPA and choose to disable it.
2023-02-28 00:02:01 +00:00
PartialVolume
ea9d76132e Merge pull request #441 from PartialVolume/HPA_DCO_001
HPA_DCO_001 - Add HPA, DCO capability
2023-02-26 23:23:22 +00:00
PartialVolume
bba050fa71 HPA_DCO_1 - Add HPA, DCO capability
1. nwipe now reads the HPA set and real number of
sectors and the DSO real max sectors. It determines
from these values whether the HPA is enabled, disabled
or in a unknown state. The values are logged to nwipes
log and flags are set in the drives context.

More code will follow that adds this information to the PDF
certificate and to the drive selection window in the GUI.
2023-02-26 22:59:33 +00:00
PartialVolume
00e2053ab4 Merge pull request #440 from PartialVolume/HPA_DCO
HPA_DCO_1 - Add HPA, DCO capability
2023-02-23 22:52:38 +00:00
PartialVolume
ef84c65846 HPA_DCO_1 - Add HPA, DCO capability
1. debugging.
2023-02-23 22:30:18 +00:00
PartialVolume
09af2bc5a6 HPA_DCO_1 - Add HPA, DCO capability
1. More debugging.
2023-02-23 21:44:08 +00:00
PartialVolume
2747ab5d4f HPA_DCO_1 - Add HPA, DCO capability
1. More corrections to hdparm command
2023-02-23 21:22:21 +00:00
PartialVolume
a2d6735b89 HPA_DCO_1 - Add HPA, DCO capability
1. Corrections to hdparm command.
2023-02-23 21:14:48 +00:00
PartialVolume
e4ecd6a68e HPA_DCO_1 - Add HPA, DCO capability
1. Initial commit
2023-02-23 20:46:19 +00:00
PartialVolume
6f7ca938f4 Merge pull request #439 from PartialVolume/PDFGen12
PDFGen12 - further work on PDF certificate
2023-02-23 12:50:18 +00:00
PartialVolume
ecfc252015 PDFGen12 - further work on PDF certificate
1. Add information regarding how many pass, sync
and verify errors, if any.
2023-02-23 12:31:41 +00:00
PartialVolume
40f1e31225 Merge pull request #438 from PartialVolume/PDFGen11
PDFGen11 - further work on PDF certificate
2023-02-22 16:28:42 +00:00
PartialVolume
3b952d3f92 PDFGen11 - further work on PDF certificate
1. Fix a bug in the static and random pass code
where I unnecessarily moved the decrement of z (the
count of bytes remaining) to after the periodic sync
which meant every time it sync'ed z wasn't decremented due
to r being used as a general results variable for the
fdatasync function.

I prefer to just have a variable be responsible for just
one thing hence my assumption r wasn't used for two different
things.

Changed the position of the decrement of z, pass_done and
round_done to their original position prior to the periodic
sync.
2023-02-22 11:56:22 +00:00
PartialVolume
09e41d573f Merge pull request #437 from PartialVolume/PDFGen10
PDFGen10 - further work on PDF certificate
2023-02-22 00:31:51 +00:00
PartialVolume
8b827f9ea0 PDFGen10 - further work on PDF certificate
1. Fix rounds reported on abort or fail.
2023-02-22 00:06:28 +00:00
PartialVolume
c57e04005e PDFGen10 - further work on PDF certificate
1. remove some unnecessary code related to bytes_erased.
2023-02-21 23:39:22 +00:00
PartialVolume
04c409a665 PDFGen10 - further work on PDF certificate
1. Fix percentage for bytes erased on certificate.
2023-02-21 22:59:09 +00:00
PartialVolume
c5f9c1d6e9 PDFGen10 - further work on PDF certificate
1. Added the red cross icon located on the top right of
the certificate. This is displayed when the wipe is aborted
or failed to complete due to disc I/O errors etc. The
green tick icon is displayed on a successful wipe.

2. When the user manually aborts the wipe, the bytes so
far erased incorrectly displayed as zero. This change updates the
c->bytes_erased variable in the static pass and random
pass functions to correct this.
2023-02-21 21:33:19 +00:00
PartialVolume
d9fed5bcc6 Merge pull request #436 from PartialVolume/PDFGen9
PDFGen9 - further work on PDF certificate
2023-02-21 12:09:43 +00:00
PartialVolume
84048ba58b PDFGen9 - further work on PDF certificate
1. Added rounds requested & completed. Coloured
green if equal and red if not.
2. Fixed endtime sometimes not being recorded when
a wipe is aborted. This affected the creation of the
PDF filename. Now ok.
3. Added throughput using the appropriate nomenclature
such as KB/sec MB/sec, GB/sec etc
2023-02-21 11:51:36 +00:00
PartialVolume
f0b61a0bc1 Merge pull request #435 from PartialVolume/PDFGen8
PDFGen8 - further work on PDF certificate
2023-02-21 00:48:42 +00:00
PartialVolume
eda930f06d PDFGen8 - further work on PDF certificate
1. Added bytes erased including percentage, green
if equal to disc size and red if the drive hasn't been
fully erased at least once.
2. Added explanation of bytes erased to certificate.
3. Added throughput
2023-02-21 00:35:52 +00:00
PartialVolume
2320015841 Merge pull request #434 from PartialVolume/PDFGen7
PDFGen7 - further work on PDF certificate
2023-02-18 23:14:28 +00:00
PartialVolume
581e83e615 PDFGen7 - further work on PDF certificate
1. Change model/serial no in header to bold and move left
2. Create a filename for the report that identifies the
wipe uniquely ie:
nwipe_report_YY-MM-DD_HH-MM-SS_Model_XXXX_Serial_XXXX.pdf
3. Move size data 2 characters right to allow for up to two
space prefix on size string. So data doesn't get written over
the end of the 'Size:' label.
2023-02-18 23:04:28 +00:00
PartialVolume
eef866fa54 Merge pull request #433 from PartialVolume/PDFGen6
PDFGen6 - further work on PDF certificate
2023-02-18 00:20:54 +00:00
PartialVolume
a29cc3b543 PDFGen6 - further work on PDF certificate
1. Fix @param in pdfgen.h, committed and accepted upstream
2. Added size of disk in bytes
3. When NVME drive show message "Not applicable" for HPA, DCO
4. Highlight status ERASED, FAILED etc with coloured ellipse
5. Added function help for nwipe_log (shows in hints in IDE)
2023-02-18 00:13:56 +00:00
PartialVolume
630837b207 Merge pull request #431 from PartialVolume/PDFGen5
PDFGen5 - further work on PDF certificate
2023-02-16 21:00:11 +00:00
PartialVolume
b5eb549c59 PDFGen5 - further work on PDF certificate
1. Rearranged the layout so the Tech/ID signature
is at the bottom of the report.
2. Added start date/time
3. Added end date/time
4. Changed data from helvetica to helvetica-bold
5. Created a definition for PDF_DARK_GREEN text in
create_pdf.h and moved the previously created PDF_GRAY
to create_pdf.h, to keep pdfgen.h vanilla.
6. Added the erasure status, ERASED, FAILED, ABORTED.
ERASED in green and FAILED, ABORTED in red.

Further code to follow ..
2023-02-16 20:49:41 +00:00
PartialVolume
142a140ddf Merge pull request #430 from PartialVolume/PDFGen4
PDFGen4 - further work on PDF certificate
2023-02-16 00:06:36 +00:00
PartialVolume
ac6f14d86d PDFGen4 - further work on PDF certificate
1. Display unknown in serial number field for loopback device
2. Added device type, USB, SATA, VIRT etc
3. Added duration of wipe in hh:mm:ss
4. Added pseudo random number generator type, twister, isaac etc
5. Added final blanking pass type, none, zeros, ones
6. Added number of rounds
2023-02-15 23:52:17 +00:00
PartialVolume
e7797bf874 Merge pull request #429 from PartialVolume/PDFGen3
PDFGen3 - further work on PDF certificate
2023-02-15 01:28:53 +00:00
PartialVolume
40fdaf49a3 Formatted text .. I need to auto run make format! 2023-02-15 01:25:10 +00:00
PartialVolume
0c0e3f2930 PDFGen3 - further work on PDF certificate
1. Changed most titles in certificate to gray with
the data in black.

Started adding real data to template including:
Model, serial no., method, verify, size.

more code to follow...
2023-02-15 01:15:36 +00:00
PartialVolume
d5286a1965 Merge pull request #428 from PartialVolume/PDFGen2
Further updates to the PDF Erasure Report code
2023-02-14 01:54:11 +00:00
PartialVolume
cb129ffbc2 As usual, forgot to run make format ! 2023-02-14 01:48:16 +00:00
PartialVolume
8c7b1839f4 Embed PDF header images, plus font
1. Embed the images contained in the PDF into nwipes code
rather than use external image files.
2. Changed the font from Times New Roman to Helvetica
to give it a cleaner look.
2023-02-14 01:42:27 +00:00
PartialVolume
ac26aad3ac Merge pull request #427 from PartialVolume/PDFGen
Create PDF Disk Erasure Report for each drive wiped (Initial code, more to come)
2023-02-13 23:24:55 +00:00
PartialVolume
b41a84f5af Update workflow.
clang-format-7 missing from latest ubuntu so
modified to clang-format
2023-02-13 23:00:19 +00:00
PartialVolume
ed478f6dc0 Create Disk Erasure Report PDF template
This commits code that creates a erasure report
pdf file. Further to will follow that populates
the template.
2023-02-13 22:38:30 +00:00
PartialVolume
602b835aef Merge pull request #424 from sporqist/patch-1
Add grml to distros that ship nwipe
2022-09-14 16:12:42 +01:00
Marius Alwan Meyer
c2ab19a109 Update README.md with additional nwipe distro grml
I was searching for a DBAN alternative that runs in a normal Linux distro so I can hot-swap drives and not hale to reboot the system. When I found nwipe, I went to install it in a grml live system. Turns out, nwipe ships with grml by default as you can see in [grml's dpkg.list](https://packages.grml.org/files/grml64-small_2021.07/dpkg.list).

It's not exactly the most up-to-date version (0.30-1+b2 for amd64) because grml is close to Debian stable. It even comes with the the small live system image.
2022-09-14 16:56:59 +02:00
PartialVolume
9feea8b3cf Merge pull request #423 from DimitriPapadopoulos/ubuntu-18.04
The `ubuntu-18.04` runner image is being deprecated
2022-09-14 15:10:26 +01:00
Dimitri Papadopoulos
1b6ba8e8a2 The ubuntu-18.04 runner image is being deprecated
https://github.com/actions/runner-images/issues/6002
2022-09-14 16:01:47 +02:00
PartialVolume
b6d5388759 Merge pull request #421 from DimitriPapadopoulos/actions
Update GitHub Actions
2022-09-14 01:12:05 +01:00
PartialVolume
24fd407b43 Merge pull request #420 from DimitriPapadopoulos/codespell
Typos found by codespell
2022-09-14 01:07:19 +01:00
Dimitri Papadopoulos
53b5c020e0 Update GitHub Actions 2022-09-13 13:27:57 +02:00
Dimitri Papadopoulos
87f4b48705 Typos found by codespell 2022-09-13 13:24:39 +02:00
80 changed files with 21289 additions and 1610 deletions

View File

@@ -8,11 +8,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v3
- name: updating available system dependencies
run: sudo apt-get update
- name: installing system dependencies
run: sudo apt-get install -y build-essential pkg-config automake libncurses5-dev autotools-dev libparted-dev dmidecode clang-format-7
run: sudo apt-get install -y build-essential pkg-config automake libncurses5-dev autotools-dev libparted-dev libconfig-dev libconfig++-dev dmidecode clang-format
- name: creating autoconf files
run: ./autogen.sh
- name: configuring

View File

@@ -1,21 +0,0 @@
name: ci_ubuntu_18.04
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v1
- name: updating available system dependencies
run: sudo apt-get update
- name: installing system dependencies
run: sudo apt-get install -y build-essential pkg-config automake libncurses5-dev autotools-dev libparted-dev dmidecode
- name: creating autoconf files
run: ./autogen.sh
- name: configuring
run: ./configure CFLAGS='-O0 -g -Wall -Wextra'
- name: compiling
run: make

View File

@@ -8,11 +8,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v3
- name: updating available system dependencies
run: sudo apt-get update
- name: installing system dependencies
run: sudo apt-get install -y build-essential pkg-config automake libncurses5-dev autotools-dev libparted-dev dmidecode
run: sudo apt-get install -y build-essential pkg-config automake libncurses5-dev autotools-dev libparted-dev libconfig-dev libconfig++-dev dmidecode
- name: creating autoconf files
run: ./autogen.sh
- name: configuring

1
.gitignore vendored
View File

@@ -20,3 +20,4 @@ src/Makefile
src/Makefile.in
src/nwipe
stamp-h1
.DS_Store

View File

@@ -1,42 +1,125 @@
RELEASE NOTES
=============
v0.40
-----------------------
includes the following changes:
## New Features and improved performance in V0.40
* Upon initialization, nwipe identifies and sets the most efficient Pseudo-Random Number Generator (PRNG) as the default, based on a real-time hardware performance test. Thanks to @knogle #698 #699 #700 #706
* AES-256-CTR PRNG that utilizes native [AES-NI instructions](https://en.wikipedia.org/wiki/AES_instruction_set) to significantly boost data-wiping throughput on compatible CPUs. Thanks @knogle #695 #660 #659
* Implemented a new wipe method, the BMB21-2019 Chinese State Secrets Bureau standard Technical Requirement for Data Sanitization. Thanks @xicaixiaokeke #668
* Implemented Direct I/O with large buffers as the default wipe method, reducing write instructions and boosting throughput. This change cut wipe completion times by 510% in tests and significantly lowered CPU load—especially on high-density servers wiping 10+ drives simultaneously. Kernel cached I/O remains available via command-line switches. Thanks @knogle #683
* Added support for host UUID, serial number, and custom tags in PDF report headers. These options can be configured and toggled in the nwipe config menu ('c'), accessible from the nwipe drive selection screen and are saved to nwipe's config file, /etc/nwipe/nwipe.conf for persistent configuration between restarts." Thanks @PartialVolume #709
* Added a new GUI “device topology” view, opened with the 't' key from the device selection screen. Useful if you want to check which device controller, which HBA, etc. your drive is attached. This improves safety and usability on systems with multiple controllers by clearly showing the physical attachment path of each drive without changing wipe behaviour. Thanks @Knogle #697
* Enhanced 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. Thanks @Knogle #686
* The internal PRNGs are now seeded via getrandom(2) rather than through a long-lived file descriptor to /dev/urandom, to improve reliability. Thanks @Knogle #680
## Resolved issues
* fixed: To resolve issues with partition wipes, as opposed to whole disc wipes, nwipe now includes the device name in the PDF report filename. Previously, when wiping specific partitions (e.g., sudo nwipe /dev/sdc3 /dev/sd4 /dev/sd5) via the CLI, the absence of a disk model or serial number caused PDF filenames to conflict and overwrite each other. This update ensures unique filenames and prevents data loss when processing multiple partitions. Thanks @PartialVolume #676
* Add static linking libraries that are required by parted 3.6 @deamen #655
* fixed: some declaration changes to satisfy gcc 15 Thanks @Knogle & @xandris #663 #658
* Optimised that header code that generates the PDF reports. Thanks @PartialVolume #678
* fixed: respect no-blanking methods in --nogui mode. This fixes an issue where a default blanking pass was added to methods which do not support it when in --nogui mode. Existing GUI code overriding the option is never called in --nogui mode, Thanks @desertwitch #682
* fixed: sanitize device serial numbers and fix uninitialized/fallback handling. Thanks @Knogle #690
* fixed: Automatically create PDF report directory if missing and improve permission model Thanks @Knogle #693
* optimise: Validate autonuke and nogui for valid values and harden logic by not using assumptions in if statements for binary values and switching to case statements for value specific checks. Thanks @PartialVolume #701
* fixed: Improve str_truncate to fix memory error detected in valgrind #702
* fixed: Require strict input of long form options, i.e no abbreviations allowed!. Command line option syntax as specifically described in `nwipe --help` and nwipe man page are only allowed. Corrects a 'feature' in GNU getopt_long(). Thanks @PartialVolume #705
v0.39
-----------------------
includes the following changes:
- Fix model name Endian for Sandisk-SunDisk drives by @PartialVolume in https://github.com/martijnvanbrummelen/nwipe/pull/643
- Implement Bruce Schneier 7-Pass wiping method by @Knogle in https://github.com/martijnvanbrummelen/nwipe/pull/648
- Some cleanup in options.c, added missing xoroshiro256_prng argument i… by @Knogle in https://github.com/martijnvanbrummelen/nwipe/pull/651
- Removed EXPERIMENTAL! comments for ALFG and Xoroshiro. [#652](https://github.com/martijnvanbrummelen/nwipe/pull/652) Thanks @Knogle
- Fix: some declaration changes to satisfy gcc 15. [#663](https://github.com/martijnvanbrummelen/nwipe/pull/663) Thanks @Knogle
v0.38
-----------------------
includes the following changes:
- Fixes type error, relevant to i686 (32 bit) only. Fixes a compile error on some distros. [#588](https://github.com/martijnvanbrummelen/nwipe/pull/588) Thanks @Knogle
- Added feature relevant to ShredOS only, the f key will toggle the font size, standard size to double size, has no action for other xorg/wayland distributions. The f key is available in the drive selection and progress screens only. Note that in earlier commits in this release the d key was programmed however this is now the f key to toggle font size. [#589](https://github.com/martijnvanbrummelen/nwipe/pull/589) [#635](https://github.com/martijnvanbrummelen/nwipe/pull/635) Thanks @PartialVolume
- Fixes a issue where SAS drives always respond with hidden sectors = ???, i.e warning. This patch fixes the problem so that a SAS drive responds with hidden sectors = not applicable. A SATA drive connected to a SAS interface should still respond with yes or no subject to the interface passing HPA and DCO-identify commands. [#605](https://github.com/martijnvanbrummelen/nwipe/pull/605) Thanks @PartialVolume
- On some distros, nwipe may not be able to find hdparm or smartctl even when they are installed. This is due to a symbolic link issue with the distro. The patch fixes this issue by adding a new search location when looking for hdparm and smartctl. The new search location is /usr/sbin/. Previously we searched /sbin/ and /usr/bin/ but /sbin is symbolically linked to /usr/sbin/ so just in case there was some issue with the symbolic link we also now search /usr/sbin/ [#606](https://github.com/martijnvanbrummelen/nwipe/pull/606) Thanks @PartialVolume
- The hidden sector check for SAS drives has been disabled as it is believed that SAS drives do not support the SCSI commands to adjust the drives size as reported to the O.S. [#607](https://github.com/martijnvanbrummelen/nwipe/pull/607) Thanks @PartialVolume
- Some USB adapters report the model name and serial number with the incorrect endian, so adjacent characters in the model name are swapped with each other. This patch detects and fixes model names for Hitachi, Toshiba, WDC Western Digital Corporation and Seagate/ST drives. Mainly some older adapters and drive interfaces might have this issue. [#630](https://github.com/martijnvanbrummelen/nwipe/pull/630) Thanks @PartialVolume
- Fixes the `s shift s bug` as reported here https://github.com/PartialVolume/shredos.x86_64/issues/301 To summarize, if no drives are selected and then the user presses s (lower case) a warning appears indicating that the user should press S (upper case) to start the wipe. This warning appears for about 3 seconds but during this time if the user presses S (upper case) nwipe would immediately complete, having wiped no drives and requesting the user to press the spacebar to exit. The is incorrect behaviour.
The bug doesn't appear if the user pressed S after the 3 seconds elapsed and the warning message disappeared. This patch fixes this so that it does not exit but displays the warning for 3 seconds and then waits for input. [#636](https://github.com/martijnvanbrummelen/nwipe/pull/636)
v0.37
-----------------------
includes the following changes:
- Added the XORoshiro-256 pseudo random number generator (PRNG). Thanks to Fabian Druschke @knogle [#555](https://github.com/martijnvanbrummelen/nwipe/pull/555)
- Added the Lagged Fibonacci PRNG generator. Thanks again to Fabian Druschke @knogle [#556](https://github.com/martijnvanbrummelen/nwipe/pull/556)
- Added missing help for HMG IS5 enhanced. Thanks to @AndCycle [#569](https://github.com/martijnvanbrummelen/nwipe/pull/569)
- Changed the default method from "DOD Short" to "prng stream", using the XORoshiro-256 prng
- Fixed an issue in configure.ac which was producing an error while running `./configure`, mentioning libconfig, however the presence of libconfig had already been checked for, earlier in configure.ac. Although this error did not cause `./configure` to abort prematurely and therefore make would build the source correctly, it did cause a issue for inclusion into Debian Sid. Thanks to @Polynomial-C [#574](https://github.com/martijnvanbrummelen/nwipe/pull/574)
- Minor change to nwipe's man page filename, nwipe.1 to nwipe.8 to fix a Debian warning. [#577](https://github.com/martijnvanbrummelen/nwipe/pull/577)
v0.36
-----------------------
- Added the abbreviation MMC for mmcblk devices such as SD and microSD cards and some low budget laptops. [#526](https://github.com/martijnvanbrummelen/nwipe/pull/526)
- Fixed some serial numbers that were displaying garbage. [#527](https://github.com/martijnvanbrummelen/nwipe/pull/527)
- Fixed auto power off and nowait when the screen has been blanked by the user. [#529](https://github.com/martijnvanbrummelen/nwipe/pull/529)
- Fixed nwipe not auto exiting on completion when in non gui mode. [#531](https://github.com/martijnvanbrummelen/nwipe/pull/531)
- Fixed smart page titles so they have a consistent format with page 1 in the PDF report. [#532](https://github.com/martijnvanbrummelen/nwipe/pull/532)
- Fixed some of the config help messages that displayed incorrect information. [#533](https://github.com/martijnvanbrummelen/nwipe/pull/533)
- Inserted a space between temperature and model. [#534](https://github.com/martijnvanbrummelen/nwipe/pull/534)
- Fixed incorrect footer on return to organisation/customer preview screen. [#535](https://github.com/martijnvanbrummelen/nwipe/pull/535)
- Made footer completion message more informative. [#538](https://github.com/martijnvanbrummelen/nwipe/pull/538)
- Fixed hidden sector detection for devices with logical/physical size of 4096/4096. [#543](https://github.com/martijnvanbrummelen/nwipe/pull/543) [#546](https://github.com/martijnvanbrummelen/nwipe/pull/546)
- Fixed some strcpy compiler warnings. [#548](https://github.com/martijnvanbrummelen/nwipe/pull/548)
v0.35
-----------------------
- Nwipe will now optionally create a multi-page PDF certificate that shows details of a specific discs erasure. The first page forms the certificate of erasure and subsequent pages show the drives smart data. Two related options have been added to nwipe's command line options -P, --PDFreportpath=PATH Path to write PDF reports to. Default is "." If set to "noPDF" no PDF reports are written. From the drive selection screen you can now press 'c' for config. This takes you to the configuration screen where you can select various PDF certificate related options such as enabling PDF, entering customer or company data for entry onto the certificate and enabling a preview of customer/company info prior to the drive selection screen starting.
- Nwipe now supports HPA/DCO detection, aka hidden sector detection. This is where the drive has been configured to report a smaller size to the operating system (O.S.) than it actually is. The HPA/DCO status is reported on the main drive selection screen as [HS? N/A] for drive that does not support HPA/DCO such as NvMe. [HS? YES] for a drive that is reporting a size smaller than it actually is, i.e has hidden sectors and [HS? NO] where the drive is reporting it's actual size correctly to the O.S. And finally [HS? ???] where nwipe cannot determine the HPA/DCO status as the drive is not responding to the ATA commands used to detect HPA/DCO. This might be because the drive does not support HPA/DCO or the interface adapter does not support ATA passthrough as is the case with a lot of the USB adapters on the market, but not all USB adapters. Nwipe does not currently allow removal of the HPA/DCO so you will still need to use hdparm to reset the drive so it reports its correct size before using nwipe to wipe the drive. HPA/DCO reset may be added in the next version. Thanks to @mdcato for the help testing the code and HPA/DCO results as displayed in the report.
- This bug only applies to ones wipe and one or zero's verification. A very rare occurrence of a incorrect percentage on completion. The actual wipe was completed correctly it was just that the percentage calculation was wrong. [#459](https://github.com/martijnvanbrummelen/nwipe/pull/459)
- Nwipe now supports a configuration file /etc/nwipe/nwipe.conf. Currently it supports settings related to the PDF certificate but more options will be added in the future.
- If you are running nwipe within the KDE konsole terminal and you resize the window by pulling on the corners, occasionally nwipe will exit with the error message: "GUI.c,nwipe_gui_select(), loop runaway, did you close the terminal without exiting nwipe? Initiating shutdown now" The loop runaway detection has been made less sensitive, i.e 32 iterations per second of the GUI update can now be completed before a loop runaway is detected. previously it was 8. In practise when sizing the konsole window, anywhere between 1 and 17 iterations will occur. [#467](https://github.com/martijnvanbrummelen/nwipe/pull/467)
- Nwipe now provides better temperature support for SAS drives. Thanks to @ggruber for all the code and testing he contributed.
- Disc sizes are now shown differently to provide more information about their size. For instance a 1.2TB drive was shown as 1TB, now it is shown as 1200GB. Thanks to @ggruber for his code contribution.
- Interface/bustype type was reported as UNK fo SAS drives, now reported correctly as SAS. Thanks to @ggruber for his code contribution.
- Interface/bustype type has been enhanced to show SAS-SSD when a SSD drive is present. Thanks to @ggruber for his code contribution.
- Nwipe's temperature retrieval code has been placed in it's own thread. This was done because it was found that any delays in obtaining the temperature resulted in a momentary freeze in the GUI wipe screen updating it's stats. This wasn't noticable if you were erasing a small number of drives but become apparent when wiping ten or twenty drives simultaneously.
v0.34
-----------------------
- Fix a compiler warning -Wformat-zero-length string
v0.33
-----------------------
- Fixes a slight screen corruption on 80 column display. When highlighting the verify ones option the first two digits of DoD 5220.20-M disappear. This patch fixes that issue.@PartialVolume #348
- For some controllers/drivers the readlink method of obtaining the bus type for GUI display does not work. If we haven't already resolved the bus type, we then also check smartctl for the transport protocol for SAS. @PartialVolume #350
- Check smartctl for unresolved bus types SATA @PartialVolume #358
- Changed message from (No ATA pass-thru) to (S/N: unknown) as the reason the serial number is unknown is because there is no ATA pass through for the chipset being used by the USB to SATA adapter, basically we are making the message more meaningful for the end user rather than for the engineer/programmer that may understand the previous terminology used. @PartialVolume @Firminator #356
- Add drive temperature monitoring and display temperature in degrees Celsius in the GUI. Requires the kernel drivetemp module and makes use of the hwmon sub system in the kernel to extract drive temperatures. Nwipe will automatically load the drivetemp module if it's available. @PartialVolume #360 #361 #364
- Remove /dev/ from gui for long device names. This fixes column alignment issues in the gui with nvme drives i.e. nvme0n1 etc. If the drive name including path exceeds 8 characters the /dev/ is removed and prefixed with spaces to a total max length of 8 characters. @PartialVolume #365
- Add -q --quiet option - anonymize serial numbers and SMBIOS-DMI data. This anonymizes serial numbers and related identifiable information for drives and hardware but does not remove model information in both the GUI and the log displayed by stdout at the end of a wipe and also in the log file if enabled in options. This feature is useful for uploading logs when submitting bug reports. @PartialVolume #366 #367 #371 #379 #383
- Fixes a intermittent FAILED message that is displayed in the summary table when the message should have been UABORTED. The incorrect FAILED message only occurred when using control-C to abort a wipe. @PartialVolume #373
- When many verification or pass errors are detected the status line can wrap on a 80 column display. This patch makes the error message more succinct which will free up about 10 characters & prevents the line wrapping. @PartialVolume #374
- Fixes a problem that occurs with a unresponsive drive that causes the ETA to grow to an enormous value. We now do not calculate an individual drives ETA when the throughput of the drive is zero so avoiding the overall ETA being incorrect for drives that are working correctly when multiple drives are being simultaneously wiped. While a individual drives ETA is calculated it is not displayed but only used to determine the overall ETA when all drives have completed. @PartialVolume #375
- Add temperature monitoring and display with NVMe drives. @PartialVolume #377 #380 #381
- When one of the two verify only methods are selected change the drive selected text from WIPE to VRFY to indicate the drive is not being wiped, but is only being verified. @PartialVolume #378
- Fixes a incorrect sector, block and device sizes in 32 bit builds only as displayed in the nwipe log. This problem had no affect on the wipe as the issue was caused by a incorrect format specifier that affected the log text only. @PartialVolume #387 #388
- Fixes a issue where temperatures may not have been available on Debian systems due to the location of modprobe. Particularly relevant to Debian which when logged in as root doesn't put /sbin in the $PATH environment setting. This issue was not necessarily relevant for Linux distros based on Debian, for instance, Ubuntu where nwipe would have found the modprobe command. @PartialVolume #390 #391
- Improve wipe thread cancellation error checking. @PartialVolume #392
- Improve GUI thread messaging if a pthread_join fails. @PartialVolume #393
- Fixed a missing serial number on SAS drive.@PartialVolume #394
- Added ISAAC-64 for 64 bit systems. Thanks @chkboom #398 #401
- Fixes a problem with the Gutmann wipe where the random passes at the beginning and end were being re-arranged when only the inner passes should be rearranged. Thanks @chkboom #399
- Fixes a obscure incorrect summary table status, while the log text correctly reports the failure. If the drive becomes non responsive during the wipe, the MB/s throughput will slowly drop towards 0MB/s and will display a FAILURE -1 error. The logs will correctly display errors and nwipe's return status will be non zero, however the summary table may display erased rather than FAILURE, this is because
- Fixes a slight screen corruption on 80 column display. When highlighting the verify ones option the first two digits of DoD 5220.20-M disappear. This patch fixes that issue.@PartialVolume [#348](https://github.com/martijnvanbrummelen/nwipe/pull/348)
- For some controllers/drivers the readlink method of obtaining the bus type for GUI display does not work. If we haven't already resolved the bus type, we then also check smartctl for the transport protocol for SAS. @PartialVolume [#350](https://github.com/martijnvanbrummelen/nwipe/pull/350)
- Check smartctl for unresolved bus types SATA @PartialVolume [#358](https://github.com/martijnvanbrummelen/nwipe/pull/358)
- Changed message from (No ATA pass-thru) to (S/N: unknown) as the reason the serial number is unknown is because there is no ATA pass through for the chipset being used by the USB to SATA adapter, basically we are making the message more meaningful for the end user rather than for the engineer/programmer that may understand the previous terminology used. @PartialVolume @Firminator [#356](https://github.com/martijnvanbrummelen/nwipe/pull/356)
- Add drive temperature monitoring and display temperature in degrees Celsius in the GUI. Requires the kernel drivetemp module and makes use of the hwmon sub system in the kernel to extract drive temperatures. Nwipe will automatically load the drivetemp module if it's available. @PartialVolume [#360](https://github.com/martijnvanbrummelen/nwipe/pull/360) [#361](https://github.com/martijnvanbrummelen/nwipe/pull/361) [#364](https://github.com/martijnvanbrummelen/nwipe/pull/364)
- Remove /dev/ from gui for long device names. This fixes column alignment issues in the gui with nvme drives i.e. nvme0n1 etc. If the drive name including path exceeds 8 characters the /dev/ is removed and prefixed with spaces to a total max length of 8 characters. @PartialVolume [#365](https://github.com/martijnvanbrummelen/nwipe/pull/365)
- Add -q --quiet option - anonymize serial numbers and SMBIOS-DMI data. This anonymizes serial numbers and related identifiable information for drives and hardware but does not remove model information in both the GUI and the log displayed by stdout at the end of a wipe and also in the log file if enabled in options. This feature is useful for uploading logs when submitting bug reports. @PartialVolume [#366](https://github.com/martijnvanbrummelen/nwipe/pull/366) [#367](https://github.com/martijnvanbrummelen/nwipe/pull/367) [#371](https://github.com/martijnvanbrummelen/nwipe/pull/371) [#379](https://github.com/martijnvanbrummelen/nwipe/pull/379) [#383](https://github.com/martijnvanbrummelen/nwipe/pull/383)
- Fixes a intermittent FAILED message that is displayed in the summary table when the message should have been UABORTED. The incorrect FAILED message only occurred when using control-C to abort a wipe. @PartialVolume [#373](https://github.com/martijnvanbrummelen/nwipe/pull/373)
- When many verification or pass errors are detected the status line can wrap on a 80 column display. This patch makes the error message more succinct which will free up about 10 characters & prevents the line wrapping. @PartialVolume [#374](https://github.com/martijnvanbrummelen/nwipe/pull/374)
- Fixes a problem that occurs with a unresponsive drive that causes the ETA to grow to an enormous value. We now do not calculate an individual drives ETA when the throughput of the drive is zero so avoiding the overall ETA being incorrect for drives that are working correctly when multiple drives are being simultaneously wiped. While a individual drives ETA is calculated it is not displayed but only used to determine the overall ETA when all drives have completed. @PartialVolume [#375](https://github.com/martijnvanbrummelen/nwipe/pull/375)
- Add temperature monitoring and display with NVMe drives. @PartialVolume [#377](https://github.com/martijnvanbrummelen/nwipe/pull/377) [#380](https://github.com/martijnvanbrummelen/nwipe/pull/380) [#381](https://github.com/martijnvanbrummelen/nwipe/pull/381)
- When one of the two verify only methods are selected change the drive selected text from WIPE to VRFY to indicate the drive is not being wiped, but is only being verified. @PartialVolume [#378](https://github.com/martijnvanbrummelen/nwipe/pull/378)
- Fixes a incorrect sector, block and device sizes in 32 bit builds only as displayed in the nwipe log. This problem had no affect on the wipe as the issue was caused by a incorrect format specifier that affected the log text only. @PartialVolume [#387](https://github.com/martijnvanbrummelen/nwipe/pull/387) [#388](https://github.com/martijnvanbrummelen/nwipe/pull/388)
- Fixes a issue where temperatures may not have been available on Debian systems due to the location of modprobe. Particularly relevant to Debian which when logged in as root doesn't put /sbin in the $PATH environment setting. This issue was not necessarily relevant for Linux distros based on Debian, for instance, Ubuntu where nwipe would have found the modprobe command. @PartialVolume [#390](https://github.com/martijnvanbrummelen/nwipe/pull/390) [#391](https://github.com/martijnvanbrummelen/nwipe/pull/391)
- Improve wipe thread cancellation error checking. @PartialVolume [#392](https://github.com/martijnvanbrummelen/nwipe/pull/392)
- Improve GUI thread messaging if a pthread_join fails. @PartialVolume [#393](https://github.com/martijnvanbrummelen/nwipe/pull/393)
- Fixed a missing serial number on SAS drive.@PartialVolume [#394](https://github.com/martijnvanbrummelen/nwipe/pull/394)
- Added ISAAC-64 for 64 bit systems. Thanks @chkboom [#398](https://github.com/martijnvanbrummelen/nwipe/pull/398) [#401](https://github.com/martijnvanbrummelen/nwipe/pull/401)
- Fixes a problem with the Gutmann wipe where the random passes at the beginning and end were being re-arranged when only the inner passes should be rearranged. Thanks @chkboom [#399](https://github.com/martijnvanbrummelen/nwipe/pull/399)
- Fixes an obscure incorrect summary table status, while the log text correctly reports the failure. If the drive becomes non responsive during the wipe, the MB/s throughput will slowly drop towards 0MB/s and will display a FAILURE -1 error. The logs will correctly display errors and nwipe's return status will be non zero, however the summary table may display erased rather than FAILURE, this is because
the wipe thread exited prematurely without setting the pass error. This fixes the error by checking the context's result status, i.e non zero on failure and if pass equals zero it makes pass equal to one. This is then picked up by the summary table log code which then marks the status
correctly as FAILURE in the summary table. @PartialVolume #400
- Fixes a spurious message on abort before wipe.This patch fixes a minor display issue that occurs when a user aborts a wipe before a wipe has started. It only occurs if the user had selected one or more drives for wipe and then aborted before starting the wipe. The spurious message only occurs in a virtual terminal, i.e. /dev/tty1, /dev/tty2, /dev/console It does not occur in terminal applications such as konsole, xterm, terminator etc. The spurious message that appears in the main window, states that "/dev/sdxyz 100% complete" along with garbage values in the statistics window. The message appears for a fraction of a second before being replaced with the textual log information that correctly states that the user aborted and no wipe was started. Basically the gui status information update function tries to update the data when the wipe hasn't even started. The fix is to only update the statistics information only if a wipe has started by checking the 'global_wipe_status' value which indicates whether any wipe started. '1' indicates that a wipe has started, else '0' if no wipe has started. @PartialVolume #406
correctly as FAILURE in the summary table. @PartialVolume [#400](https://github.com/martijnvanbrummelen/nwipe/pull/400)
- Fixes a spurious message on abort before wipe.This patch fixes a minor display issue that occurs when a user aborts a wipe before a wipe has started. It only occurs if the user had selected one or more drives for wipe and then aborted before starting the wipe. The spurious message only occurs in a virtual terminal, i.e. /dev/tty1, /dev/tty2, /dev/console It does not occur in terminal applications such as konsole, xterm, terminator etc. The spurious message that appears in the main window, states that "/dev/sdxyz 100% complete" along with garbage values in the statistics window. The message appears for a fraction of a second before being replaced with the textual log information that correctly states that the user aborted and no wipe was started. Basically the gui status information update function tries to update the data when the wipe hasn't even started. The fix is to only update the statistics information only if a wipe has started by checking the 'global_wipe_status' value which indicates whether any wipe started. '1' indicates that a wipe has started, else '0' if no wipe has started. @PartialVolume [#406](https://github.com/martijnvanbrummelen/nwipe/pull/406)
- Fixes temperature update in drive selection window. This fixes a problem where the drive temperature is not updated
automatically in only the drive selection window. The temperature is however updated correctly every 60 seconds during a wipe in the wipe status window. This bug would probably never be noticed by most people as usually the drive temperature changes slowly and only rises once a wipe has started. The only time I imagine it would have been noticed would have been if the drive temperature was already high and you were trying to reduce the temperature by cooling before starting a wipe. This has now been corrected so that the temperature in the drive
selection window is updated every 60 seconds. @PartialVolume #407
- Fixes a zombie nwipe process running at 100% CPU on one core but only on a Konsole based terminal. This only occurred when the Konsole terminal is exited while nwipe is sitting at the drive selection screen but nwipe did not exit when the konsole terminal was closed. If nwipe is exited normally on completion of a wipe or aborted by using control C then this problem would not be seen. Also occurs during a wipe if the konsole terminal is closed without exiting nwipe first, again only on Konsole based terminals. @PartialVolume #408 #409
- Fixes a obscure segfault when --logfile option used with a non writable directory. @PartialVolume #410
selection window is updated every 60 seconds. @PartialVolume [#407](https://github.com/martijnvanbrummelen/nwipe/pull/407)
- Fixes a zombie nwipe process running at 100% CPU on one core but only on a Konsole based terminal. This only occurred when the Konsole terminal is exited while nwipe is sitting at the drive selection screen but nwipe did not exit when the konsole terminal was closed. If nwipe is exited normally on completion of a wipe or aborted by using control C then this problem would not be seen. Also occurs during a wipe if the konsole terminal is closed without exiting nwipe first, again only on Konsole based terminals. @PartialVolume [#408](https://github.com/martijnvanbrummelen/nwipe/pull/408) [#409](https://github.com/martijnvanbrummelen/nwipe/pull/409)
- Fixes a obscure segfault when --logfile option used with a non writable directory. @PartialVolume [#410](https://github.com/martijnvanbrummelen/nwipe/pull/410)
v0.32
-----------------------
@@ -115,7 +198,6 @@ v0.27
- Add the Github build CI service and update Readme with build status labels (Thanks louib)
- Miscellaneous smaller fixes
v0.26
-----
- Add exclude drive option (Thanks PartialVolume)

496
README.md
View File

@@ -1,61 +1,231 @@
# nwipe
![GitHub CI badge](https://github.com/martijnvanbrummelen/nwipe/workflows/ci_ubuntu_latest/badge.svg)
![GitHub CI badge](https://github.com/martijnvanbrummelen/nwipe/workflows/ci_ubuntu_16.04/badge.svg)
[![GitHub release](https://img.shields.io/github/release/martijnvanbrummelen/nwipe)](https://github.com/martijnvanbrummelen/nwipe/releases/)
nwipe is a fork of the dwipe command originally used by Darik's Boot and Nuke (DBAN). nwipe was created out of a need to run the DBAN dwipe command outside of DBAN, in order to allow its use with any host distribution, thus giving better hardware support.
`nwipe` is a fork of the `dwipe` command originally used by Darik's Boot and Nuke (DBAN).
It was created to run the DBAN erase engine on any Linux distribution, with better and more modern hardware support.
nwipe is a program that will securely erase the entire contents of disks. It can wipe a single drive or multiple disks simultaneously. It can operate as both a command line tool without a GUI or with a ncurses GUI as shown in the example below:
`nwipe` securely erases the entire contents of block devices. It can wipe a single drive or multiple disks in parallel, either:
> **Warning**
> Nwipe does not currently support HDA (hidden disc area) detection. You will need to run hdparm to detect and if necessary correct the reported size of the disc prior to using nwipe.
- as a **command-line tool** without a GUI, or
- with an **ncurses-based GUI**, as shown below:
![Example wipe](/images/example_wipe.gif)
> **Warning**
> For some of nwipes features such as SMART data in the PDF certificate, HPA/DCO detection and other functions, nwipe uses external tools: **smartmontools** and **hdparm**.
> Both `hdparm` and `smartmontools` are **mandatory** if you want all nwipe features to be fully available.
> If they are not installed, nwipe will log a warning and continue, but many important features will not work as intended.
<i>GIF showing six drives being simultaneously erased. It skips to the completion of all six wipes and shows five drives that were successfully erased and one drive that failed due to an I/O error. The drive that failed would then normally be physically destroyed. The five drives that were successfully wiped with zero errors or failures can then be redeployed.</i>
![Example wipe](https://github.com/martijnvanbrummelen/nwipe/raw/master/images/example_wipe.gif)
The user can select from a variety of recognised secure erase methods which include:
*The animation above shows six drives being erased in parallel. The view jumps to the completion of all six wipes and shows five drives successfully wiped and one that failed due to an I/O error. The failing drive would typically be physically destroyed. Drives that complete with zero errors can be safely redeployed.*
* Fill With Zeros - Fills the device with zeros (0x00), one round only.
* Fill With Ones - Fills the device with ones (0xFF), one round only.
* RCMP TSSIT OPS-II - Royal Candian Mounted Police Technical Security Standard, OPS-II
* DoD Short - The American Department of Defense 5220.22-M short 3 pass wipe (passes 1, 2 & 7).
* DoD 5220.22M - The American Department of Defense 5220.22-M full 7 pass wipe.
* Gutmann Wipe - Peter Gutmann's method (Secure Deletion of Data from Magnetic and Solid-State Memory).
* PRNG Stream - Fills the device with a stream from the PRNG.
* Verify Zeros - This method only reads the device and checks that it is filled with zeros (0x00).
* Verify Ones - This method only reads the device and checks that it is filled with ones (0xFF).
* HMG IS5 enhanced - Secure Sanitisation of Protectively Marked Information or Sensitive Information
![nwipe_certificate_0 35_5s](https://github.com/martijnvanbrummelen/nwipe/assets/22084881/cf181a9c-af2d-4bca-a6ed-15a4726cb12b)
nwipe also includes the following pseudo random number generators:
* Mersenne Twister
* ISAAC
*The screenshot above shows nwipes threepage PDF certificate. Drive-identifying data such as serial numbers has been anonymised using the `-q` / `--quiet` option.*
These can be used to overwrite a drive with a stream of randomly generated characters.
---
nwipe can be found in many [Linux distro repositories](#which-linux-distro-uses-the-latest-nwipe).
## New in v0.40 (upcoming)
nwipe is also included in [ShredOS](https://github.com/PartialVolume/shredos.x86_64) which was developed in particular to showcase nwipe as a fast-to-boot standalone method similar to DBAN. ShredOS always contains the latest nwipe version.
The upcoming **v0.40** release introduces several major improvements:
## Compiling & Installing
- **AES-256-CTR PRNG**
Highperformance, cryptographically secure stream generator (AES-NI accelerated where available).
- **Large, aligned I/O buffers**
Significantly fewer syscalls and better throughput, especially on fast SSDs and NVMe.
- **Configurable I/O modes**
- `--io-mode=auto` (default): try O_DIRECT, fall back to cached I/O if not supported
- `--directio` / `--io-mode=direct`: force direct I/O (O_DIRECT), no fallback
- `--cachedio` / `--io-mode=cached`: force kernel cached I/O, never attempt O_DIRECT
- **Improved sync behaviour for cached I/O**
Sync intervals are again based on a predictable number of bytes written, ensuring timely detection of disk / USB errors without excessive overhead.
- **Enhanced device exclusion**
`--exclude` now works cleanly with paths like `/dev/disk/by-id/*`, making it easier to exclude specific drives by stable IDs.
- **Stronger seeding with `getrandom()`**
nwipe now uses the Linux `getrandom()` syscall for PRNG seeding and no longer depends on `/dev/urandom`.
- **New BMB21-2019 erase method**
Implements the Chinese State Secrets Bureau BMB21-2019 technical requirement for data sanitisation.
For a development setup, see the [Hacking section](#hacking) below. For a bootable version of the very latest nwipe master that you can write to an USB flash drive or CD/DVD, see the [Quick and easy bootable version of nwipe master section](#quick--easy-usb-bootable-version-of-nwipe-master-for-x86_64-systems) below.
---
`nwipe` requires the following libraries to be installed:
## Erasure methods
* ncurses
* pthreads
* parted
The user can select from a variety of recognised secure erase methods, including:
and optionally, but recommended, the following programs:
- **Fill With Zeros**
Fills the device with zeros (`0x00`).
* dmidecode
* readlink
* smartmontools
- **Fill With Ones**
Fills the device with ones (`0xFF`).
- **RCMP TSSIT OPS-II**
Royal Canadian Mounted Police Technical Security Standard, OPS-II.
- **DoD Short**
U.S. Department of Defense 5220.22-M **short** 3-pass wipe
(passes 1, 2 & 7 from the full specification).
- **DoD 5220.22-M (Full)**
Full 7-pass U.S. DoD 5220.22-M wipe.
- **Gutmann Wipe**
Peter Gutmann's 35-pass method (“Secure Deletion of Data from Magnetic and Solid-State Memory”).
- **PRNG Stream**
Fills the device with a stream generated by the selected PRNG engine.
- **Verify Zeros**
Reads the device and verifies it is filled with zeros (`0x00`).
- **Verify Ones**
Reads the device and verifies it is filled with ones (`0xFF`).
- **HMG IS5 Enhanced**
UK HMG IS5 (Enhanced) sanitisation method for protectively marked or sensitive information.
- **Schneier Wipe**
Bruce Schneier's 7-pass mixedpattern algorithm.
- **BMB21-2019** *(new in v0.40)*
Chinese State Secrets Bureau BMB21-2019 technical requirement for data sanitisation.
This method overwrites the device with ones (`0xFF`), then zeros (`0x00`), followed by three passes of PRNG-generated random data, and finishes with a final pass of ones (`0xFF`).
---
## PRNG engines
nwipe includes multiple pseudorandom number generators (PRNGs) for methods that require random data:
- **AES-256-CTR** *(new in v0.40)*
Cryptographically secure, highthroughput counter-mode stream cipher, using hardware AES-NI where available.
- **XORoshiro-256**
Very fast, highquality non-cryptographic generator, suitable for highvolume random wiping where a CSPRNG is not strictly required.
- **Mersenne Twister**
Well-known highperiod PRNG.
- **ISAAC / ISAAC-64**
(Indirection, Shift, Accumulate, Add, and Count) generators.
- **Additive Lagged Fibonacci Generator**
These PRNGs can be selected at runtime (see the man page for the exact CLI options) and are used by any wipe method that requires random patterns (for example PRNG Stream, Schneier or BMB21 random passes).
---
## I/O subsystem and Direct I/O
Starting with v0.40 the nwipe I/O layer has been significantly modernised:
### Large, aligned I/O buffers
nwipe now uses **large, aligned multi-megabyte buffers** when reading and writing:
- Reduces the number of `read()` / `write()` calls.
- Improves throughput on fast devices (SSD / NVMe / high-speed RAID).
- Ensures correct alignment for Direct I/O (O_DIRECT) where supported.
### I/O mode selection
You can now explicitly control how nwipe accesses the device:
```bash
--io-mode=auto # default
--io-mode=direct # equivalent to --directio
--io-mode=cached # equivalent to --cachedio
--directio # force O_DIRECT (no fallback)
--cachedio # force kernel cached I/O only
````
* **auto**
Try to open the device with `O_DIRECT`. If the kernel or filesystem does not support it (EINVAL/EOPNOTSUPP), nwipe falls back to cached I/O and logs a warning.
* **direct** / `--directio`
Force Direct I/O. If `O_DIRECT` is not supported for the device, opening the device fails and the wipe will not proceed. This is useful for strict testing and benchmarking.
* **cached** / `--cachedio`
Always use kernel buffered I/O. `O_DIRECT` is never attempted. This is closest to the behaviour of older nwipe releases.
### Sync behaviour
The `--sync` option controls how often nwipe performs `fdatasync()` when using **cached I/O**:
* The value represents the number of **device hardware blocks** (typically 512 or 4096 bytes) between syncs.
* Internally this value is scaled to match nwipes large I/O buffer size so that the effective **bytes between syncs** remain in a reasonable range (tens to hundreds of MB, depending on the default and device).
* This ensures:
* timely detection of I/O and hardware errors in cached I/O mode, and
* good throughput for normal use.
In **Direct I/O** mode (`--directio` / `--io-mode=direct`), periodic sync is disabled: write errors are reported immediately by `write()`, so `fdatasync()` provides no additional safety.
See the `nwipe(8)` man page for detailed `--sync` semantics and examples.
---
## SSD considerations and limitations
In its current form, nwipe **cannot fully sanitise** solid state drives (SSDs) of any interface type:
* SAS / SATA / NVMe
* Form factors such as 2.5", 3.5", M.2, PCIe, etc.
This is due to how SSDs internally manage data:
* SSDs use wear-levelling and frequently maintain additional, non-host-accessible memory (overprovisioning).
* Failed blocks may be remapped to reserved areas that are not directly addressable by the OS.
* Many vendors restrict low-level access to these areas to the drives own controller and firmware.
For secure SSD sanitisation, it is strongly recommended to:
1. Use nwipe / ShredOS **in combination with vendor-specific tools**, for example:
* manufacturer Secure Erase,
* NVMe format / sanitize commands, or
* hardware vendorprovided utilities, and
2. Independently validate the result, comparing drive contents before and after sanitisation where feasible.
A list of common SSD vendor tools and guidance can be found in the separate [SSD Guide](ssd-guide.md).
---
## Compiling & installing
For development work, see the [Hacking](#hacking) section below.
For a **bootable image** with the latest nwipe master that you can write to a USB stick or CD/DVD, see [Quick & easy, USB bootable version](#quick--easy-usb-bootable-version-of-nwipe-master-for-x86_64-systems).
### Dependencies
`nwipe` requires the following libraries:
* `ncurses`
* `pthreads`
* `parted`
* `libconfig`
`nwipe` also requires the following program and will abort with a warning if not found:
* **hdparm** (as of current master and v0.35+)
The following tools are optional but **strongly recommended**:
* `dmidecode`
* `coreutils` (for `readlink`)
* `smartmontools`
These tools enable features such as:
* HPA/DCO detection
* SMART data for PDF certificates (especially for USB bridge devices)
* SMBIOS/DMI host information in the log
* Correct bus type detection (ATA/USB/etc.) and proper operation of `--nousb`
### Debian & Ubuntu prerequisites
If you are compiling `nwipe` from source, the following libraries will need to be installed first:
If you are compiling `nwipe` from source on Debian/Ubuntu:
```bash
sudo apt install \
@@ -65,144 +235,256 @@ sudo apt install \
libncurses5-dev \
autotools-dev \
libparted-dev \
libconfig-dev \
libconfig++-dev \
dmidecode \
coreutils \
smartmontools
smartmontools \
hdparm \
```
### Fedora prerequisites
### Fedora / RHEL / CentOS Stream prerequisites
```bash
sudo bash
dnf update
dnf groupinstall "Development Tools"
dnf groupinstall "C Development Tools and Libraries"
yum install ncurses-devel
yum install parted-devel
yum install dmidecode
yum install coreutils
yum install smartmontools
sudo dnf update -y
sudo dnf groupinstall -y "Development Tools" "C Development Tools and Libraries"
sudo dnf install -y \
ncurses-devel \
parted-devel \
libconfig-devel \
libconfig++-devel \
dmidecode \
coreutils \
smartmontools \
hdparm
```
Note. The following programs are optionally installed although recommended. 1. dmidecode 2. readlink 3. smartmontools.
#### dmidecode
dmidecode provides SMBIOS/DMI host data to stdout or the log file. If you don't install it you won't see the SMBIOS/DMI host data at the beginning of nwipes log.
### Arch Linux / Manjaro prerequisites
#### coreutils (provides readlink)
readlink determines the bus type, i.e. ATA, USB etc. Without it the --nousb option won't work and bus type information will be missing from nwipes selection and wipe windows. The coreutils package is often automatically installed as default in most if not all distros.
```bash
sudo pacman -Syu --needed \
base-devel \
ncurses \
parted \
libconfig \
dmidecode \
coreutils \
smartmontools \
hdparm
```
### openSUSE (Leap / Tumbleweed) prerequisites
#### smartmontools
smartmontools obtains serial number information for supported USB to IDE/SATA adapters. Without it, drives plugged into USB ports will not show serial number information.
```bash
sudo zypper refresh
sudo zypper install -y \
gcc \
make \
automake \
autoconf \
libtool \
ncurses-devel \
libparted-devel \
libconfig-devel \
libconfig++-devel \
dmidecode \
coreutils \
smartmontools \
hdparm
```
If you want a quick and easy way to keep your copy of nwipe running the latest master release of nwipe see the [automating the download and compilation](#automating-the-download-and-compilation-process-for-debian-based-distros) section.
Note: `dmidecode`, `readlink` (from `coreutils`) and `smartmontools` are technically optional, but recommended for full feature support.
### Compilation
First create all the autoconf files:
```
Generate the autoconf files:
```bash
./autogen.sh
```
Then compile & install using the following standard commands:
```
Then configure, build and install:
```bash
./configure
make format (only required if submitting pull requests)
make format # only required if you intend to submit pull requests
make
make install
sudo make install
```
Then run nwipe !
```
Run nwipe:
```bash
cd src
sudo ./nwipe
```
or
```
or simply:
```bash
sudo nwipe
```
### Hacking
---
If you wish to submit pull requests to this code we would prefer you enable all warnings when compiling.
This can be done using the following compile commands:
## Hacking
```
If you intend to submit patches or pull requests, we recommend enabling full warnings in your development build.
For a debugfriendly build:
```bash
./configure --prefix=/usr CFLAGS='-O0 -g -Wall -Wextra'
make format (necessary if submitting pull requests)
make format # necessary if submitting pull requests
make
make install
sudo make install
```
The `-O0 -g` flags disable optimisations. This is required if you're debugging with `gdb` in an IDE such as Kdevelop. With these optimisations enabled you won't be able to see the values of many variables in nwipe, not to mention the IDE won't step through the code properly.
* `-O0 -g`
Disables optimisation and includes debug symbols. This is important if you are debugging with `gdb` or an IDE (e.g. KDevelop), and want accurate stepping and variable inspection.
The `-Wall` and `-Wextra` flags enable all compiler warnings. Please submit code with zero warnings.
* `-Wall -Wextra`
Enables most useful compiler warnings. Please submit code with **zero warnings**.
Also make sure that your changes are consistent with the coding style defined in the `.clang-format` file, using:
```
The code style is defined via `.clang-format`. Before submitting:
```bash
make format
```
You will need `clang-format` installed to use the `format` command.
Once done with your coding then the released/patch/fixed code can be compiled, with all the normal optimisations, using:
```
./configure --prefix=/usr && make && make install
You will need `clang-format` installed for this step.
To rebuild a "release-like" binary with normal optimisations after development:
```bash
./configure --prefix=/usr
make
sudo make install
```
## Automating the download and compilation process for Debian based distros.
---
Here's a script that will do just that! It will create a directory in your home folder called 'nwipe_master'. It installs all the libraries required to compile the software (build-essential) and all the libraries that nwipe requires (libparted etc). It downloads the latest master copy of nwipe from github. It then compiles the software and then runs the latest nwipe. It doesn't write over the version of nwipe that's installed in the repository (If you had nwipe already installed). To run the latest master version of nwipe manually you would run it like this `sudo ~/nwipe_master/nwipe/src/nwipe`
## Automating download and compilation (Debian-based distros)
You can run the script multiple times; the first time it's run it will install all the libraries; subsequent times it will just say the libraries are up to date. As it always downloads a fresh copy of the nwipe master from Github, you can always stay up to date. Just run it to get the latest version of nwipe. It only takes 11 seconds to compile on my i7.
Below is a convenience script that:
If you already have nwipe installed from the repository, you need to take care which version you are running. If you typed `nwipe` from any directory it will always run the original repository copy of nwipe. To run the latest nwipe you have to explicitly tell it where the new copy is, e.g in the directory `~/nwipe_master/nwipe/src`. That's why you would run it by typing `sudo ~/nwipe_master/nwipe/src/nwipe` alternatively you could cd to the directory and run it like this:
1. Creates `~/nwipe_master`
2. Installs all required build dependencies
3. Downloads the latest master branch from GitHub
4. Builds nwipe
5. Runs the freshly built nwipe
```
cd ~/nwipe_master/nwipe/src
./nwipe
```
It does **not** overwrite any nwipe installed from your distributions repository.
Note the ./, that means only look in the current directory for nwipe. if you forgot to type ./ the computer would run the older repository version of nwipe.
Save the following as `buildnwipe`, then make it executable with `chmod +x buildnwipe`:
Once you have copied the script below into a file called buildnwipe, you need to give the file execute permissions `chmod +x buildnwipe` before you can run it. [Download script](
https://drive.google.com/file/d/1BsQDlMqtEycAgfk9FpG1sxv3jFz5dzNO/view?usp=sharing)
```
```bash
#!/bin/bash
cd "$HOME"
nwipe_directory="nwipe_master"
mkdir $nwipe_directory
cd $nwipe_directory
sudo apt install build-essential pkg-config automake libncurses5-dev autotools-dev libparted-dev dmidecode readlink smartmontools git
mkdir -p "$nwipe_directory"
cd "$nwipe_directory"
sudo apt install -y \
build-essential \
pkg-config \
automake \
libncurses5-dev \
autotools-dev \
libparted-dev \
libconfig-dev \
libconfig++-dev \
dmidecode \
readlink \
smartmontools \
hdparm \
git
rm -rf nwipe
git clone https://github.com/martijnvanbrummelen/nwipe.git
cd "nwipe"
cd nwipe
./autogen.sh
./configure
make
cd "src"
cd src
sudo ./nwipe
```
## Quick & Easy, USB bootable version of nwipe master for x86_64 systems.
If you want to just try out a bootable version of nwipe you can download [ShredOS](https://github.com/PartialVolume/shredos.x86_64). The ShredOS image is around 60MB and can be written to an USB flash drive in seconds, allowing you to boot straight into the latest version of nwipe. ShredOS is available for x86_64 (64-bit) and i686 (32-bit) CPU architectures and will boot both legacy BIOS and UEFI devices. It comes as .IMG (bootable USB flash drive image) or .ISO (for CD-R/DVD-R). Instructions and download can be found [here](https://github.com/PartialVolume/shredos.x86_64#obtaining-and-writing-shredos-to-a-usb-flash-drive-the-easy-way-).
To run the latest master later on:
```bash
sudo ~/nwipe_master/nwipe/src/nwipe
```
If you already have nwipe from your distros repo installed, remember:
* `nwipe` → runs the packaged version in your `$PATH`
* `./nwipe` in `~/nwipe_master/nwipe/src` → runs the freshly built master
---
## Quick & easy, USB bootable version of nwipe master for x86_64 systems
If you prefer a bootable image containing the latest nwipe master, use **ShredOS**:
* [ShredOS](https://github.com/PartialVolume/shredos.x86_64)
ShredOS:
* is ~60 MB in size,
* can be written to a USB flash drive in seconds,
* boots directly into a minimal environment running the latest nwipe,
* is available for x86_64 (64-bit) and i686 (32-bit),
* supports both legacy BIOS and UEFI.
It is provided as:
* `.img` (USB bootable image), and
* `.iso` (for CD/DVD).
See the ShredOS README for detailed instructions on downloading and writing the image.
---
## Which Linux distro uses the latest nwipe?
See [Repology](https://repology.org/project/nwipe/versions)
And in addition checkout the following distros that all include nwipe:
You can see an overview at:
- [ShredOS](https://github.com/PartialVolume/shredos.x86_64) Always has the latest nwipe release.
- [netboot.xyz](https://github.com/netbootxyz/netboot.xyz) Can network-boot ShredOS.
- [partedmagic](https://partedmagic.com)
- [SystemRescueCD](https://www.system-rescue.org)
- [gparted live](https://sourceforge.net/projects/gparted/files/gparted-live-testing/1.2.0-2/)
* [Repology](https://repology.org/project/nwipe/versions)
Know of other distros that include nwipe? Then please let us know or issue a PR on this README.md. Thanks.
Distributions known to include nwipe:
* [ShredOS](https://github.com/PartialVolume/shredos.x86_64) always ships the latest nwipe release.
* [netboot.xyz](https://github.com/netbootxyz/netboot.xyz) can network-boot ShredOS.
* [DiskDump](https://github.com/Awire9966/DiskDump) nwipe on a Debian live CD, can wipe eMMC.
* [Parted Magic](https://partedmagic.com)
* [SystemRescue](https://www.system-rescue.org)
* [GParted Live](https://sourceforge.net/projects/gparted/files/gparted-live-testing/1.2.0-2/)
* [Grml](https://grml.org/)
If you know of other distributions that ship nwipe, please let us know or send a PR updating this README.
---
## Bugs
Bugs can be reported on GitHub:
https://github.com/martijnvanbrummelen/nwipe
Bugs, feature requests, and pull requests are welcome on GitHub:
* [https://github.com/martijnvanbrummelen/nwipe](https://github.com/martijnvanbrummelen/nwipe)
Please include:
* your distribution and version,
* the nwipe version (or git commit hash),
* hardware details (especially for I/O-related issues),
* log output and command line options used.
---
## License
GNU General Public License v2.0
nwipe is licensed under the **GNU General Public License v2.0**.
See the `LICENSE` file for details.

View File

@@ -1,15 +1,17 @@
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.64])
AC_INIT([nwipe], [0.34], [git@brumit.nl])
AC_PREREQ([2.63])
AC_INIT([nwipe],[0.40],[git@brumit.nl])
AM_INIT_AUTOMAKE(foreign subdir-objects)
AC_OUTPUT(Makefile src/Makefile man/Makefile)
AC_CONFIG_FILES([Makefile src/Makefile man/Makefile])
AC_OUTPUT
AC_CONFIG_SRCDIR([src/nwipe.c])
AC_CONFIG_HEADERS([config.h])
# Checks for programs.
AC_PROG_CC
AC_PROG_CXX
PKG_PROG_PKG_CONFIG
# Checks for libraries.
@@ -49,13 +51,35 @@ PKG_CHECK_MODULES(
)]
)
AC_CHECK_LIB([intl], [libintl_dgettext]) # needed to statically link libparted, but not given in its pkgconfig file
AC_CHECK_LIB([uuid], [uuid_generate]) # needed to statically link libparted, but not given in its pkgconfig file
PKG_CHECK_MODULES(
[LIBCONFIG],
[libconfig],
[
CFLAGS="${CFLAGS} ${LIBCONFIG_CFLAGS}"
LIBS="${LIBS} ${LIBCONFIG_LIBS}"
],
[AC_CHECK_LIB([libconfig], [main], [
LIBS="-llibconfig $LIBS"
AC_CHECK_HEADERS(libconfig.h,, [
AC_CHECK_HEADERS(libconfig.h, [
AC_DEFINE([LIBCONFIG_IN_SUBDIR], [libconfig/], [Look for libconfig headers in subdir])
], [AC_MSG_ERROR([libconfig headers not found])])
])
], [AC_MSG_ERROR([libconfig library not found])])]
)
# Libraries needed for static linking with parted
if echo "$LDFLAGS" | grep -q "static"; then
AC_CHECK_LIB([uuid], [uuid_generate], [LIBS="-luuid $LIBS"], [AC_MSG_ERROR([libuuid is required for static linking but not found])])
AC_CHECK_LIB([devmapper], [dm_task_create], [LIBS="-ldevmapper $LIBS"], [AC_MSG_ERROR([libdevmapper is required for static linking but not found])])
AC_CHECK_LIB([blkid], [blkid_new_probe], [LIBS="-lblkid $LIBS"], [AC_MSG_ERROR([libblkid is required for static linking but not found])])
fi
PKG_CHECK_MODULES([PARTED], [libparted])
AC_CHECK_LIB([pthread], [main], ,[AC_MSG_ERROR([pthread development library not found])])
# Checks for header files.
AC_CHECK_HEADERS([fcntl.h inttypes.h netinet/in.h stddef.h stdint.h stdlib.h string.h sys/file.h sys/ioctl.h unistd.h])
AC_CHECK_HEADERS([libconfig.h fcntl.h inttypes.h netinet/in.h stddef.h stdint.h stdlib.h string.h sys/file.h sys/ioctl.h unistd.h])
# Checks for typedefs, structures, and compiler characteristics.
AC_TYPE_SIZE_T

View File

@@ -1 +1 @@
dist_man_MANS = nwipe.1
dist_man_MANS = nwipe.8

View File

@@ -1,139 +0,0 @@
.TH NWIPE "1" "March 2022" "nwipe version 0.34" "User Commands"
.SH NAME
nwipe \- securely erase disks
.SH SYNOPSIS
.B nwipe
[\fIoptions\fR] [\fIdevice1\fR] [\fIdevice2\fR] ...
.SH DESCRIPTION
nwipe is a command that will securely erase disks using a variety of
recognised methods. It is a fork of the dwipe command used by Darik's Boot
and Nuke (dban). nwipe is included with partedmagic if you want a quick and
easy bootable CD version. nwipe was created out of a need to run the DBAN
dwipe command outside of DBAN, in order to allow its use with any host
distribution, thus giving better hardware support. It is essentially the
same as dwipe, with a few changes:
.TP
- pthreads is used instead of fork
.TP
- The parted library is used to detect drives
.TP
- The code is designed to be compiled with gcc
.TP
- SIGUSR1 can be used to log the stats of the current wipe
.SH OPTIONS
.TP
\fB\-V\fR, \fB\-\-version\fR
Prints the version number
.TP
\fB\-h\fR, \fB\-\-help\fR
Prints a help summary
.TP
\fB\-\-autonuke\fR
If no devices have been specified on the command line, starts wiping all
devices immediately. If devices have been specified, starts wiping only
those specified devices immediately.
.TP
\fB\-\-autopoweroff\fR
Power off system on completion of wipe delayed for for one minute. During
this one minute delay you can abort the shutdown by typing sudo shutdown -c
.TP
\fB\-\-sync\fR=\fINUM\fR
Will perform a syn after NUM writes (default: 100000)
.IP
0 \- fdatasync after the disk is completely written
fdatasync errors not detected until completion.
0 is not recommended as disk errors may cause nwipe
to appear to hang
.IP
1 \- fdatasync after every write
Warning: Lower values will reduce wipe speeds.
.IP
1000 \- fdatasync after 1000 writes
.TP
\fB\-\-noblank\fR
Do not perform the final blanking pass after the wipe (default is to blank,
except when the method is RCMP TSSIT OPS\-II).
.TP
\fB\-\-nowait\fR
Do not wait for a key before exiting (default is to wait).
.TP
\fB\-\-nosignals\fR
Do not allow signals to interrupt a wipe (default is to allow).
.TP
\fB\-\-nousb\fR
Do not show or wipe any USB devices, whether in GUI, --nogui or autonuke
mode. (default is to allow USB devices to be shown and wiped).
.TP
\fB\-\-nogui\fR
Do not show the GUI interface. Can only be used with the autonuke option.
Nowait option is automatically invoked with the nogui option.
SIGUSR1 can be used to retrieve the current wiping statistics.
.TP
\fB\-v\fR, \fB\-\-verbose\fR
Log more messages, useful for debugging.
.TP
\fB\-\-verify\fR=\fITYPE\fR
Whether to perform verification of erasure (default: last)
.IP
off \- Do not verify
.IP
last \- Verify after the last pass
.IP
all \- Verify every pass
.IP
Please mind that HMG IS5 enhanced always verifies the last (PRNG) pass
regardless of this option.
.TP
\fB\-m\fR, \fB\-\-method\fR=\fIMETHOD\fR
The wiping method (default: dodshort).
.IP
dod522022m / dod \- 7 pass DOD 5220.22\-M method
.IP
dodshort / dod3pass \- 3 pass DOD method
.IP
gutmann \- Peter Gutmann's Algorithm
.IP
ops2 \- RCMP TSSIT OPS\-II
.IP
random / prng / stream \- PRNG Stream
.IP
zero / quick \- Overwrite with zeros 0x00
.IP
one \- Overwrite with ones 0xFF
.IP
verify_zero \- Verifies disk is zero filled
.IP
verify_one \- Verifies disk is 0xFF filled
.IP
is5enh \- HMG IS5 enhanced
.TP
\fB\-l\fR, \fB\-\-logfile\fR=\fIFILE\fR
Filename to log to. Default is STDOUT
.TP
\fB\-p\fR, \fB\-\-prng\fR=\fIMETHOD\fR
PRNG option (mersenne|twister|isaac|isaac64)
.TP
\fB\-q\fR, \fB\-\-quiet\fR
Anonymize serial numbers, Gui & logs display:
XXXXXXXX = S/N obtained & anonymized.
???????? = S/N not available.
.TP
\fB\-r\fR, \fB\-\-rounds\fR=\fINUM\fR
Number of times to wipe the device using the selected method (default: 1)
.TP
\fB\-e\fR, \fB\-\-exclude\fR=\fIDEVICES\fR
Up to ten comma separated devices to be excluded, examples:
--exclude=/dev/sdc
--exclude=/dev/sdc,/dev/sdd
.SH BUGS
Please see the GitHub site for the latest list
(https://github.com/martijnvanbrummelen/nwipe/issues)
.SH AUTHOR
Nwipe is developed by Martijn van Brummelen <github@brumit.nl>
.SH "SEE ALSO"
.BR shred (1),
.BR dwipe (1),
.BR dd (1),
.BR dcfldd (1),
.BR dc3dd (1)

225
man/nwipe.8 Normal file
View File

@@ -0,0 +1,225 @@
.TH NWIPE "5" "Feb 2026" "nwipe version 0.40" "User Commands"
.SH NAME
nwipe \- securely erase disks
.SH SYNOPSIS
.B nwipe
[\fIoptions\fR] [\fIdevice1\fR] [\fIdevice2\fR] ...
.SH DESCRIPTION
nwipe is a command that will securely erase disks using a variety of
recognized methods. It is a fork of the dwipe command used by Darik's Boot
and Nuke (DBAN). nwipe is included with partedmagic if you want a quick and
easy bootable CD version. nwipe was created out of a need to run the DBAN
dwipe command outside of DBAN, in order to allow its use with any host
distribution, thus giving better hardware support. It is essentially the
same as dwipe, with a few changes:
.TP
- pthreads is used instead of fork.
.TP
- The parted library is used to detect drives.
.TP
- The code is designed to be compiled with gcc.
.TP
- SIGUSR1 can be used to log the stats of the current wipe.
.TP
- Additional wiping methods and PRNGs.
.TP
- Configurable I/O modes (cached, direct, auto) using large I/O buffers for higher throughput.
.TP
- Support for stable device paths such as \fI/dev/disk/by-id/\fR.
.PP
All PRNG implementations are seeded using the Linux
.BR getrandom (2)
system call instead of reading from
.IR /dev/urandom
via a file descriptor.
.SH DEVICES
.PP
Devices can be specified either as block device nodes (for example
.IR /dev/sda ,
.IR /dev/nvme0n1 ,
.IR /dev/mapper/cryptroot )
or via stable symlinks under
.IR /dev/disk/by-id/ .
nwipe will resolve these paths and operate on the underlying block device.
.SH OPTIONS
.TP
\fB\-V\fR, \fB\-\-version\fR
Prints the version number.
.TP
\fB\-h\fR, \fB\-\-help\fR
Prints a help summary.
.TP
\fB\-\-autonuke\fR
If no devices have been specified on the command line, starts wiping all
devices immediately. If devices have been specified, starts wiping only
those specified devices immediately.
.TP
\fB\-\-autopoweroff\fR
Power off system on completion of wipe delayed for one minute. During
this one minute delay you can abort the shutdown by typing sudo shutdown -c
.TP
\fB\-\-sync\fR=\fINUM\fR
Specify how often nwipe performs an fdatasync() during cached I/O mode.
The value refers to the number of *device hardware blocks* (commonly 512 or
4096 bytes) written before triggering a sync. Since nwipe now writes using
large multi-megabyte buffers, this value is automatically scaled so the sync
interval in bytes is consistent with historic behaviour.
The default value (100000) results in a sync approximately every 50400 MB,
similar to earlier nwipe releases. This ensures timely detection of I/O errors
while maintaining good throughput.
This setting has no effect when using --directio, as write() returns errors
immediately under direct I/O.
.IP
0 \- Perform one sync only at the end of the pass.
Not advised; errors may only be detected after the entire wipe.
.IP
1 \- Sync immediately after each write.
Extremely safe but extremely slow.
.IP
1000 \- Sync after the equivalent of 1000 hardware blocks.
Useful for testing or more aggressive error detection.
.TP
\fB\-\-cachedio\fR
Use buffered I/O with large write buffers (page cache enabled). This is the
default on most systems and generally gives the best performance for
rotational disks.
.TP
\fB\-\-directio\fR
Use direct I/O with large write buffers. This opens devices with
.BR O_DIRECT
to bypass the page cache. It can be useful when running multiple wipes in
parallel or when you do not want to pollute the system page cache. On some
devices this may be slower than cached I/O.
.TP
\fB\-\-io\-mode\fR=\fIMODE\fR
Select the I/O mode explicitly. \fIMODE\fR can be:
.IP
\fBauto\fR \- (default) automatically choose the best supported mode for
the device and kernel.
.IP
\fBcached\fR \- force buffered I/O.
.IP
\fBdirect\fR \- force direct I/O (\fBO_DIRECT\fR).
.IP
Large I/O buffers are used in all modes to maximise throughput.
.TP
\fB\-\-noblank\fR
Do not perform the final blanking pass after the wipe (default is to blank,
except when the method is RCMP TSSIT OPS\-II).
.TP
\fB\-\-nowait\fR
Do not wait for a key before exiting (default is to wait).
.TP
\fB\-\-nosignals\fR
Do not allow signals to interrupt a wipe (default is to allow).
.TP
\fB\-\-nousb\fR
Do not show or wipe any USB devices, whether in GUI, --nogui or autonuke
mode. (default is to allow USB devices to be shown and wiped).
.TP
\fB\-\-nogui\fR
Do not show the GUI interface. Can only be used with the autonuke option.
Nowait option is automatically invoked with the nogui option.
SIGUSR1 can be used to retrieve the current wiping statistics.
.TP
\fB\-\-pdftag\fR
Enables a field on the PDF that holds a tag that identifies the host computer
.TP
\fB\-v\fR, \fB\-\-verbose\fR
Log more messages, useful for debugging.
.TP
\fB\-\-verify\fR=\fITYPE\fR
Whether to perform verification of erasure (default: last).
.IP
off \- Do not verify.
.IP
last \- Verify after the last pass.
.IP
all \- Verify every pass.
.IP
Please mind that HMG IS5 enhanced always verifies the last (PRNG) pass
regardless of this option.
.TP
\fB\-m\fR, \fB\-\-method\fR=\fIMETHOD\fR
The wiping method (default: prng).
.IP
dod522022m / dod \- 7 pass DOD 5220.22\-M method
.IP
dodshort / dod3pass \- 3 pass DOD method
.IP
gutmann \- Peter Gutmann's algorithm
.IP
ops2 \- RCMP TSSIT OPS\-II
.IP
random / prng / stream \- PRNG Stream
.IP
zero / quick \- Overwrite with zeros (0x00)
.IP
one \- Overwrite with ones (0xFF)
.IP
verify_zero \- Verifies disk is zero (0x00) filled
.IP
verify_one \- Verifies disk is one (0xFF) filled
.IP
is5enh \- HMG IS5 enhanced
.IP
bruce7 \- Schneier Bruce 7-pass mixed pattern
.IP
bmb \- Chinese BMB21-2019 State Secrets Bureau standard.
This method overwrites the device with ones (0xFF),
then zeros (0x00), followed by three passes of PRNG-
generated random data, and finishes with a final pass
of ones (0xFF). Designed to meet the BMB21-2019
technical sanitization requirements.
.TP
\fB\-l\fR, \fB\-\-logfile\fR=\fIFILE\fR
Filename to log to. Default is STDOUT.
.TP
\fB\-P\fR, \fB\-\-PDFreportpath\fR=\fIDIR\fR
Directory to write the PDF nwipe reports/certificates to.
Defaults to ".".
If \fIDIR\fR is set to \fInoPDF\fR no report PDF files are written.
.TP
\fB\-p\fR, \fB\-\-prng\fR=\fIMETHOD\fR
The PRNG option (default: aes_ctr_prng).
(mersenne|twister|isaac|isaac64|add_lagg_fibonacci_prng|xoroshiro256_prng|aes_ctr_prng)
.IP
\fBaes_ctr_prng\fR uses the Linux kernel AF_ALG interface to AES\-CTR as a
cryptographically strong stream generator. It is seeded via
.BR getrandom (2)
and requires kernel crypto support for AES\-CTR.
.TP
\fB\-q\fR, \fB\-\-quiet\fR
Anonymize serial numbers, GUI & logs display:
XXXXXXXX = S/N obtained & anonymized.
???????? = S/N not available.
.TP
\fB\-r\fR, \fB\-\-rounds\fR=\fINUM\fR
Number of times to wipe the device using the selected method (default: 1).
.TP
\fB\-e\fR, \fB\-\-exclude\fR=\fIDEVICES\fR
Up to ten comma separated devices to be excluded, examples:
--exclude=/dev/sdc
--exclude=/dev/sdc,/dev/sdd
--exclude=/dev/sdc,/dev/sdd,/dev/mapper/cryptswap1
--dev/disk/by-path/pci-0000:00:17.0-ata-1
.SH BUGS
Please see the GitHub site for the latest list:
(https://github.com/martijnvanbrummelen/nwipe/issues)
.SH AUTHOR
nwipe is developed by Martijn van Brummelen <github@brumit.nl>.
.SH "SEE ALSO"
.BR shred (1),
.BR dwipe (1),
.BR dd (1),
.BR dcfldd (1),
.BR dc3dd (1)

View File

@@ -6,5 +6,5 @@ AM_LDFLAGS =
# this lists the binaries to produce, the (non-PHONY, binary) targets in
# the previous manual Makefile
bin_PROGRAMS = nwipe
nwipe_SOURCES = context.h logging.h options.h prng.h version.h temperature.h nwipe.c gui.c method.h pass.c device.c gui.h isaac_rand/isaac_standard.h isaac_rand/isaac_rand.h isaac_rand/isaac_rand.c isaac_rand/isaac64.h isaac_rand/isaac64.c mt19937ar-cok/mt19937ar-cok.c nwipe.h mt19937ar-cok/mt19937ar-cok.h pass.h device.h logging.c method.c options.c prng.c version.c temperature.c
nwipe_LDADD = $(PARTED_LIBS)
nwipe_SOURCES = context.h logging.h options.h prng.h version.h temperature.h nwipe.c gui.c method.h pass.c device.c gui.h isaac_rand/isaac_standard.h isaac_rand/isaac_rand.h isaac_rand/isaac_rand.c isaac_rand/isaac64.h isaac_rand/isaac64.c mt19937ar-cok/mt19937ar-cok.c nwipe.h mt19937ar-cok/mt19937ar-cok.h alfg/add_lagg_fibonacci_prng.h alfg/add_lagg_fibonacci_prng.c xor/xoroshiro256_prng.h xor/xoroshiro256_prng.c aes/aes_ctr_prng.h aes/aes_ctr_prng.cpp pass.h device.h logging.c method.c options.c prng.c version.c temperature.c PDFGen/pdfgen.h PDFGen/pdfgen.c create_pdf.c create_pdf.h embedded_images/shred_db.jpg.c embedded_images/shred_db.jpg.h embedded_images/tick_erased.jpg.c embedded_images/tick_erased.jpg.h embedded_images/redcross.c embedded_images/redcross.h hpa_dco.h hpa_dco.c miscellaneous.h miscellaneous.c embedded_images/nwipe_exclamation.jpg.h embedded_images/nwipe_exclamation.jpg.c conf.h conf.c customers.h customers.c hddtemp_scsi/hddtemp.h hddtemp_scsi/scsi.h hddtemp_scsi/scsicmds.h hddtemp_scsi/get_scsi_temp.c hddtemp_scsi/scsi.c hddtemp_scsi/scsicmds.c cpu_features.h cpu_features.c
nwipe_LDADD = $(PARTED_LIBS) $(LIBCONFIG)

4057
src/PDFGen/pdfgen.c Normal file

File diff suppressed because it is too large Load Diff

773
src/PDFGen/pdfgen.h Normal file
View File

@@ -0,0 +1,773 @@
/**
* Simple engine for creating PDF files.
* It supports text, shapes, images etc...
* Capable of handling millions of objects without too much performance
* penalty.
* Public domain license - no warrenty implied; use at your own risk.
* @file pdfgen.h
*/
#ifndef PDFGEN_H
#define PDFGEN_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stdio.h>
/**
* @defgroup subsystem Simple PDF Generation
* Allows for quick generation of simple PDF documents.
* This is useful for producing easily printed output from C code, where
* advanced formatting is not required
*
* Note: All coordinates/sizes are in points (1/72 of an inch).
* All coordinates are based on 0,0 being the bottom left of the page.
* All colours are specified as a packed 32-bit value - see @ref PDF_RGB.
* Text strings are interpreted as UTF-8 encoded, but only a small subset of
* characters beyond 7-bit ascii are supported (see @ref pdf_add_text for
* details).
*
* @par PDF library example:
* @code
#include "pdfgen.h"
...
struct pdf_info info = {
.creator = "My software",
.producer = "My software",
.title = "My document",
.author = "My name",
.subject = "My subject",
.date = "Today"
};
struct pdf_doc *pdf = pdf_create(PDF_A4_WIDTH, PDF_A4_HEIGHT, &info);
pdf_set_font(pdf, "Times-Roman");
pdf_append_page(pdf);
pdf_add_text(pdf, NULL, "This is text", 12, 50, 20, PDF_BLACK);
pdf_add_line(pdf, NULL, 50, 24, 150, 24);
pdf_save(pdf, "output.pdf");
pdf_destroy(pdf);
* @endcode
*/
struct pdf_doc;
struct pdf_object;
/**
* pdf_info describes the metadata to be inserted into the
* header of the output PDF
*/
struct pdf_info {
char creator[64]; //!< Software used to create the PDF
char producer[64]; //!< Software used to create the PDF
char title[64]; //!< The title of the PDF (typically displayed in the
//!< window bar when viewing)
char author[64]; //!< Who created the PDF
char subject[64]; //!< What is the PDF about
char date[64]; //!< The date the PDF was created
};
/**
* Enum that declares the different image file formats we currently support.
* Each value has a corresponding header struct used within
* the format_specific_img_info union.
*/
enum {
IMAGE_PNG,
IMAGE_JPG,
IMAGE_PPM,
IMAGE_BMP,
IMAGE_UNKNOWN
};
/**
* Since we're casting random areas of memory to these, make sure
* they're packed properly to match the image format requirements
*/
#pragma pack(push, 1)
/**
* Information about color type of PNG format
* As defined by https://www.w3.org/TR/2003/REC-PNG-20031110/#6Colour-values
*/
enum /* png colortype */ {
// Greyscale
PNG_COLOR_GREYSCALE = 0,
// Truecolour
PNG_COLOR_RGB = 2,
// Indexed-colour
PNG_COLOR_INDEXED = 3,
// Greyscale with alpha
PNG_COLOR_GREYSCALE_A = 4,
// Truecolour with alpha
PNG_COLOR_RGBA = 6,
PNG_COLOR_INVALID = 255
};
/**
* png_header describes the header information extracted from .PNG files
*/
struct png_header {
uint32_t width; //!< Width in pixels
uint32_t height; //!< Height in pixels
uint8_t bitDepth; //!< Bit Depth
uint8_t colorType; //!< Color type - see PNG_COLOR_xx
uint8_t deflate; //!< Deflate setting
uint8_t filtering; //!< Filtering
uint8_t interlace; //!< Interlacing
};
/**
* bmp_header describes the header information extracted from .BMP files
*/
struct bmp_header {
uint32_t bfSize; //!< size of BMP in bytes
uint16_t bfReserved1; //!< ignore!
uint16_t bfReserved2; //!< ignore!
uint32_t bfOffBits; //!< Offset to BMP data
uint32_t biSize; //!< Size of this header (40)
int32_t biWidth; //!< Width in pixels
int32_t biHeight; //!< Height in pixels
uint16_t biPlanes; //!< Number of colour planes - must be 1
uint16_t biBitCount; //!< Bits Per Pixel
uint32_t biCompression; //!< Compression Method
};
#pragma pack(pop)
/**
* jpeg_header describes the header information extracted from .JPG files
*/
struct jpeg_header {
int ncolours; //!< Number of colours
};
/**
* PPM color spaces
*/
enum {
PPM_BINARY_COLOR_RGB, //!< binary ppm with RGB colors (magic number P5)
PPM_BINARY_COLOR_GRAY, //!< binary ppm with grayscale colors (magic number
//!< P6)
};
/**
* ppm_header describes the header information extracted from .PPM files
*/
struct ppm_header {
size_t size; //!< Indicate the size of the image data
size_t data_begin_pos; //!< position in the data where the image starts
int color_space; //!< PPM color space
};
/**
* pdf_img_info describes the metadata for an arbitrary image
*/
struct pdf_img_info {
int image_format; //!< Indicates the image format (IMAGE_PNG, ...)
uint32_t width; //!< Width in pixels
uint32_t height; //!< Height in pixels
#ifndef DOXYGEN_SHOULD_SKIP_THIS
// Doxygen doesn't like anonymous unions
//!< Image specific details
union {
struct bmp_header bmp; //!< BMP header info
struct jpeg_header jpeg; //!< JPEG header info
struct png_header png; //!< PNG header info
struct ppm_header ppm; //!< PPM header info
};
#endif
};
/**
* pdf_path_operation holds information about a path
* drawing operation.
* See PDF reference for detailed usage.
*/
struct pdf_path_operation {
char op; /*!< Operation command. Possible operators are: m = move to, l =
line to, c = cubic bezier curve with two control points, v =
cubic bezier curve with one control point fixed at first
point, y = cubic bezier curve with one control point fixed
at second point, h = close path */
float x1; /*!< X offset of the first point. Used with: m, l, c, v, y */
float y1; /*!< Y offset of the first point. Used with: m, l, c, v, y */
float x2; /*!< X offset of the second point. Used with: c, v, y */
float y2; /*!< Y offset of the second point. Used with: c, v, y */
float x3; /*!< X offset of the third point. Used with: c */
float y3; /*!< Y offset of the third point. Used with: c */
};
/**
* Convert a value in inches into a number of points.
* @param inch inches value to convert to points
*/
#define PDF_INCH_TO_POINT(inch) ((float)((inch)*72.0f))
/**
* Convert a value in milli-meters into a number of points.
* @param mm millimeter value to convert to points
*/
#define PDF_MM_TO_POINT(mm) ((float)((mm)*72.0f / 25.4f))
/*! Point width of a standard US-Letter page */
#define PDF_LETTER_WIDTH PDF_INCH_TO_POINT(8.5f)
/*! Point height of a standard US-Letter page */
#define PDF_LETTER_HEIGHT PDF_INCH_TO_POINT(11.0f)
/*! Point width of a standard A4 page */
#define PDF_A4_WIDTH PDF_MM_TO_POINT(210.0f)
/*! Point height of a standard A4 page */
#define PDF_A4_HEIGHT PDF_MM_TO_POINT(297.0f)
/*! Point width of a standard A3 page */
#define PDF_A3_WIDTH PDF_MM_TO_POINT(297.0f)
/*! Point height of a standard A3 page */
#define PDF_A3_HEIGHT PDF_MM_TO_POINT(420.0f)
/**
* Convert three 8-bit RGB values into a single packed 32-bit
* colour. These 32-bit colours are used by various functions
* in PDFGen
*/
#define PDF_RGB(r, g, b) \
(uint32_t)((((r)&0xff) << 16) | (((g)&0xff) << 8) | (((b)&0xff)))
/**
* Convert four 8-bit ARGB values into a single packed 32-bit
* colour. These 32-bit colours are used by various functions
* in PDFGen. Alpha values range from 0 (opaque) to 0xff
* (transparent)
*/
#define PDF_ARGB(a, r, g, b) \
(uint32_t)(((uint32_t)((a)&0xff) << 24) | (((r)&0xff) << 16) | \
(((g)&0xff) << 8) | (((b)&0xff)))
/*! Utility macro to provide bright red */
#define PDF_RED PDF_RGB(0xff, 0, 0)
/*! Utility macro to provide bright green */
#define PDF_GREEN PDF_RGB(0, 0xff, 0)
/*! Utility macro to provide bright blue */
#define PDF_BLUE PDF_RGB(0, 0, 0xff)
/*! Utility macro to provide black */
#define PDF_BLACK PDF_RGB(0, 0, 0)
/*! Utility macro to provide white */
#define PDF_WHITE PDF_RGB(0xff, 0xff, 0xff)
/*!
* Utility macro to provide a transparent colour
* This is used in some places for 'fill' colours, where no fill is required
*/
#define PDF_TRANSPARENT (uint32_t)(0xffu << 24)
/**
* Different alignment options for rendering text
*/
enum {
PDF_ALIGN_LEFT, //!< Align text to the left
PDF_ALIGN_RIGHT, //!< Align text to the right
PDF_ALIGN_CENTER, //!< Align text in the center
PDF_ALIGN_JUSTIFY, //!< Align text in the center, with padding to fill the
//!< available space
PDF_ALIGN_JUSTIFY_ALL, //!< Like PDF_ALIGN_JUSTIFY, except even short
//!< lines will be fully justified
PDF_ALIGN_NO_WRITE, //!< Fake alignment for only checking wrap height with
//!< no writes
};
/**
* Create a new PDF object, with the given page
* width/height
* @param width Width of the page
* @param height Height of the page
* @param info Optional information to be put into the PDF header
* @return PDF document object, or NULL on failure
*/
struct pdf_doc *pdf_create(float width, float height,
const struct pdf_info *info);
/**
* Destroy the pdf object, and all of its associated memory
* @param pdf PDF document to clean up
*/
void pdf_destroy(struct pdf_doc *pdf);
/**
* Retrieve the error message if any operation fails
* @param pdf pdf document to retrieve error message from
* @param errval optional pointer to an integer to be set to the error code
* @return NULL if no error message, string description of error otherwise
*/
const char *pdf_get_err(const struct pdf_doc *pdf, int *errval);
/**
* Acknowledge an outstanding pdf error
* @param pdf pdf document to clear the error message from
*/
void pdf_clear_err(struct pdf_doc *pdf);
/**
* Sets the font to use for text objects. Default value is Times-Roman if
* this function is not called.
* Note: The font selection should be done before text is output,
* and will remain until pdf_set_font is called again.
* @param pdf PDF document to update font on
* @param font New font to use. This must be one of the standard PDF fonts:
* Courier, Courier-Bold, Courier-BoldOblique, Courier-Oblique,
* Helvetica, Helvetica-Bold, Helvetica-BoldOblique, Helvetica-Oblique,
* Times-Roman, Times-Bold, Times-Italic, Times-BoldItalic,
* Symbol or ZapfDingbats
* @return < 0 on failure, 0 on success
*/
int pdf_set_font(struct pdf_doc *pdf, const char *font);
/**
* Calculate the width of a given string in the current font
* @param pdf PDF document
* @param font_name Name of the font to get the width of.
* This must be one of the standard PDF fonts:
* Courier, Courier-Bold, Courier-BoldOblique, Courier-Oblique,
* Helvetica, Helvetica-Bold, Helvetica-BoldOblique, Helvetica-Oblique,
* Times-Roman, Times-Bold, Times-Italic, Times-BoldItalic,
* Symbol or ZapfDingbats
* @param text Text to determine width of
* @param size Size of the text, in points
* @param text_width area to store calculated width in
* @return < 0 on failure, 0 on success
*/
int pdf_get_font_text_width(struct pdf_doc *pdf, const char *font_name,
const char *text, float size, float *text_width);
/**
* Retrieves a PDF document height
* @param pdf PDF document to get height of
* @return height of PDF document (in points)
*/
float pdf_height(const struct pdf_doc *pdf);
/**
* Retrieves a PDF document width
* @param pdf PDF document to get width of
* @return width of PDF document (in points)
*/
float pdf_width(const struct pdf_doc *pdf);
/**
* Retrieves page height
* @param page Page object to get height of
* @return height of page (in points)
*/
float pdf_page_height(const struct pdf_object *page);
/**
* Retrieves page width
* @param page Page object to get width of
* @return width of page (in points)
*/
float pdf_page_width(const struct pdf_object *page);
/**
* Add a new page to the given pdf
* @param pdf PDF document to append page to
* @return new page object
*/
struct pdf_object *pdf_append_page(struct pdf_doc *pdf);
/**
* Adjust the width/height of a specific page
* @param pdf PDF document that the page belongs to
* @param page object returned from @ref pdf_append_page
* @param width Width of the page in points
* @param height Height of the page in points
* @return < 0 on failure, 0 on success
*/
int pdf_page_set_size(struct pdf_doc *pdf, struct pdf_object *page,
float width, float height);
/**
* Save the given pdf document to the supplied filename.
* @param pdf PDF document to save
* @param filename Name of the file to store the PDF into (NULL for stdout)
* @return < 0 on failure, >= 0 on success
*/
int pdf_save(struct pdf_doc *pdf, const char *filename);
/**
* Save the given pdf document to the given FILE output
* @param pdf PDF document to save
* @param fp FILE pointer to store the data into (must be writable)
* @return < 0 on failure, >= 0 on success
*/
int pdf_save_file(struct pdf_doc *pdf, FILE *fp);
/**
* Add a text string to the document
* @param pdf PDF document to add to
* @param page Page to add object to (NULL => most recently added page)
* @param text String to display
* @param size Point size of the font
* @param xoff X location to put it in
* @param yoff Y location to put it in
* @param colour Colour to draw the text
* @return 0 on success, < 0 on failure
*/
int pdf_add_text(struct pdf_doc *pdf, struct pdf_object *page,
const char *text, float size, float xoff, float yoff,
uint32_t colour);
/**
* Add a text string to the document, making it wrap if it is too
* long
* @param pdf PDF document to add to
* @param page Page to add object to (NULL => most recently added page)
* @param text String to display
* @param size Point size of the font
* @param xoff X location to put it in
* @param yoff Y location to put it in
* @param colour Colour to draw the text
* @param wrap_width Width at which to wrap the text
* @param align Text alignment (see PDF_ALIGN_xxx)
* @param height Store the final height of the wrapped text here (optional)
* @return < 0 on failure, >= 0 on success
*/
int pdf_add_text_wrap(struct pdf_doc *pdf, struct pdf_object *page,
const char *text, float size, float xoff, float yoff,
uint32_t colour, float wrap_width, int align,
float *height);
/**
* Add a line to the document
* @param pdf PDF document to add to
* @param page Page to add object to (NULL => most recently added page)
* @param x1 X offset of start of line
* @param y1 Y offset of start of line
* @param x2 X offset of end of line
* @param y2 Y offset of end of line
* @param width Width of the line
* @param colour Colour to draw the line
* @return 0 on success, < 0 on failure
*/
int pdf_add_line(struct pdf_doc *pdf, struct pdf_object *page, float x1,
float y1, float x2, float y2, float width, uint32_t colour);
/**
* Add a cubic bezier curve to the document
* @param pdf PDF document to add to
* @param page Page to add object to (NULL => most recently added page)
* @param x1 X offset of the initial point of the curve
* @param y1 Y offset of the initial point of the curve
* @param x2 X offset of the final point of the curve
* @param y2 Y offset of the final point of the curve
* @param xq1 X offset of the first control point of the curve
* @param yq1 Y offset of the first control point of the curve
* @param xq2 X offset of the second control of the curve
* @param yq2 Y offset of the second control of the curve
* @param width Width of the curve
* @param colour Colour to draw the curve
* @return 0 on success, < 0 on failure
*/
int pdf_add_cubic_bezier(struct pdf_doc *pdf, struct pdf_object *page,
float x1, float y1, float x2, float y2, float xq1,
float yq1, float xq2, float yq2, float width,
uint32_t colour);
/**
* Add a quadratic bezier curve to the document
* @param pdf PDF document to add to
* @param page Page to add object to (NULL => most recently added page)
* @param x1 X offset of the initial point of the curve
* @param y1 Y offset of the initial point of the curve
* @param x2 X offset of the final point of the curve
* @param y2 Y offset of the final point of the curve
* @param xq1 X offset of the control point of the curve
* @param yq1 Y offset of the control point of the curve
* @param width Width of the curve
* @param colour Colour to draw the curve
* @return 0 on success, < 0 on failure
*/
int pdf_add_quadratic_bezier(struct pdf_doc *pdf, struct pdf_object *page,
float x1, float y1, float x2, float y2,
float xq1, float yq1, float width,
uint32_t colour);
/**
* Add a custom path to the document
* @param pdf PDF document to add to
* @param page Page to add object to (NULL => most recently added page)
* @param operations Array of drawing operations
* @param operation_count The number of operations
* @param stroke_width Width of the stroke
* @param stroke_colour Colour to stroke the curve
* @param fill_colour Colour to fill the path
* @return 0 on success, < 0 on failure
*/
int pdf_add_custom_path(struct pdf_doc *pdf, struct pdf_object *page,
struct pdf_path_operation *operations,
int operation_count, float stroke_width,
uint32_t stroke_colour, uint32_t fill_colour);
/**
* Add an ellipse to the document
* @param pdf PDF document to add to
* @param page Page to add object to (NULL => most recently added page)
* @param x X offset of the center of the ellipse
* @param y Y offset of the center of the ellipse
* @param xradius Radius of the ellipse in the X axis
* @param yradius Radius of the ellipse in the Y axis
* @param width Width of the ellipse outline stroke
* @param colour Colour to draw the ellipse outline stroke
* @param fill_colour Colour to fill the ellipse
* @return 0 on success, < 0 on failure
*/
int pdf_add_ellipse(struct pdf_doc *pdf, struct pdf_object *page, float x,
float y, float xradius, float yradius, float width,
uint32_t colour, uint32_t fill_colour);
/**
* Add a circle to the document
* @param pdf PDF document to add to
* @param page Page to add object to (NULL => most recently added page)
* @param x X offset of the center of the circle
* @param y Y offset of the center of the circle
* @param radius Radius of the circle
* @param width Width of the circle outline stroke
* @param colour Colour to draw the circle outline stroke
* @param fill_colour Colour to fill the circle
* @return 0 on success, < 0 on failure
*/
int pdf_add_circle(struct pdf_doc *pdf, struct pdf_object *page, float x,
float y, float radius, float width, uint32_t colour,
uint32_t fill_colour);
/**
* Add an outline rectangle to the document
* @param pdf PDF document to add to
* @param page Page to add object to (NULL => most recently added page)
* @param x X offset to start rectangle at
* @param y Y offset to start rectangle at
* @param width Width of rectangle
* @param height Height of rectangle
* @param border_width Width of rectangle border
* @param colour Colour to draw the rectangle
* @return 0 on success, < 0 on failure
*/
int pdf_add_rectangle(struct pdf_doc *pdf, struct pdf_object *page, float x,
float y, float width, float height, float border_width,
uint32_t colour);
/**
* Add a filled rectangle to the document
* @param pdf PDF document to add to
* @param page Page to add object to (NULL => most recently added page)
* @param x X offset to start rectangle at
* @param y Y offset to start rectangle at
* @param width Width of rectangle
* @param height Height of rectangle
* @param border_width Width of rectangle border
* @param colour_fill Colour to fill the rectangle
* @param colour_border Colour to draw the rectangle
* @return 0 on success, < 0 on failure
*/
int pdf_add_filled_rectangle(struct pdf_doc *pdf, struct pdf_object *page,
float x, float y, float width, float height,
float border_width, uint32_t colour_fill,
uint32_t colour_border);
/**
* Add an outline polygon to the document
* @param pdf PDF document to add to
* @param page Page to add object to (NULL => most recently added page)
* @param x array of X offsets for points comprising the polygon
* @param y array of Y offsets for points comprising the polygon
* @param count Number of points comprising the polygon
* @param border_width Width of polygon border
* @param colour Colour to draw the polygon
* @return 0 on success, < 0 on failure
*/
int pdf_add_polygon(struct pdf_doc *pdf, struct pdf_object *page, float x[],
float y[], int count, float border_width,
uint32_t colour);
/**
* Add a filled polygon to the document
* @param pdf PDF document to add to
* @param page Page to add object to (NULL => most recently added page)
* @param x array of X offsets of points comprising the polygon
* @param y array of Y offsets of points comprising the polygon
* @param count Number of points comprising the polygon
* @param border_width Width of polygon border
* @param colour Colour to draw the polygon
* @return 0 on success, < 0 on failure
*/
int pdf_add_filled_polygon(struct pdf_doc *pdf, struct pdf_object *page,
float x[], float y[], int count,
float border_width, uint32_t colour);
/**
* Add a bookmark to the document
* @param pdf PDF document to add bookmark to
* @param page Page to jump to for bookmark
(or NULL for the most recently added page)
* @param parent ID of a previously created bookmark that is the parent
of this one. -1 if this should be a top-level bookmark.
* @param name String to associate with the bookmark
* @return < 0 on failure, new bookmark id on success
*/
int pdf_add_bookmark(struct pdf_doc *pdf, struct pdf_object *page, int parent,
const char *name);
/**
* Add a link annotation to the document
* @param pdf PDF document to add link to
* @param page Page that holds the clickable rectangle
(or NULL for the most recently added page)
* @param x X coordinate of bottom LHS corner of clickable rectangle
* @param y Y coordinate of bottom LHS corner of clickable rectangle
* @param width width of clickable rectangle
* @param height height of clickable rectangle
* @param target_page Page to jump to for link
* @param target_x X coordinate to position at the left of the view
* @param target_y Y coordinate to position at the top of the view
* @return < 0 on failure, new bookmark id on success
*/
int pdf_add_link(struct pdf_doc *pdf, struct pdf_object *page, float x,
float y, float width, float height,
struct pdf_object *target_page, float target_x,
float target_y);
/**
* List of different barcode encodings that are supported
*/
enum {
PDF_BARCODE_128A, //!< Produce code-128A style barcodes
PDF_BARCODE_39, //!< Produce code-39 style barcodes
PDF_BARCODE_EAN13, //!< Produce EAN-13 style barcodes
PDF_BARCODE_UPCA, //!< Produce UPC-A style barcodes
PDF_BARCODE_EAN8, //!< Produce EAN-8 style barcodes
PDF_BARCODE_UPCE, //!< Produce UPC-E style barcodes
};
/**
* Add a barcode to the document
* @param pdf PDF document to add barcode to
* @param page Page to add barcode to (NULL => most recently added page)
* @param code Type of barcode to add (PDF_BARCODE_xxx)
* @param x X offset to put barcode at
* @param y Y offset to put barcode at
* @param width Width of barcode
* @param height Height of barcode
* @param string Barcode contents
* @param colour Colour to draw barcode
* @return < 0 on failure, >= 0 on success
*/
int pdf_add_barcode(struct pdf_doc *pdf, struct pdf_object *page, int code,
float x, float y, float width, float height,
const char *string, uint32_t colour);
/**
* Add image data as an image to the document.
* Image data must be one of: JPEG, PNG, PPM, PGM or BMP formats
* Passing 0 for either the display width or height will
* include the image but not render it visible.
* Passing a negative number either the display height or width will
* have the image be resized while keeping the original aspect ratio.
* @param pdf PDF document to add image to
* @param page Page to add image to (NULL => most recently added page)
* @param x X offset to put image at
* @param y Y offset to put image at
* @param display_width Displayed width of image
* @param display_height Displayed height of image
* @param data Image data bytes
* @param len Length of data
* @return < 0 on failure, >= 0 on success
*/
int pdf_add_image_data(struct pdf_doc *pdf, struct pdf_object *page, float x,
float y, float display_width, float display_height,
const uint8_t *data, size_t len);
/**
* Add a raw 24 bit per pixel RGB buffer as an image to the document
* Passing 0 for either the display width or height will
* include the image but not render it visible.
* Passing a negative number either the display height or width will
* have the image be resized while keeping the original aspect ratio.
* @param pdf PDF document to add image to
* @param page Page to add image to (NULL => most recently added page)
* @param x X offset to put image at
* @param y Y offset to put image at
* @param display_width Displayed width of image
* @param display_height Displayed height of image
* @param data RGB data to add
* @param width width of image in pixels
* @param height height of image in pixels
* @return < 0 on failure, >= 0 on success
*/
int pdf_add_rgb24(struct pdf_doc *pdf, struct pdf_object *page, float x,
float y, float display_width, float display_height,
const uint8_t *data, uint32_t width, uint32_t height);
/**
* Add a raw 8 bit per pixel grayscale buffer as an image to the document
* @param pdf PDF document to add image to
* @param page Page to add image to (NULL => most recently added page)
* @param x X offset to put image at
* @param y Y offset to put image at
* @param display_width Displayed width of image
* @param display_height Displayed height of image
* @param data grayscale pixel data to add
* @param width width of image in pixels
* @param height height of image in pixels
* @return < 0 on failure, >= 0 on success
*/
int pdf_add_grayscale8(struct pdf_doc *pdf, struct pdf_object *page, float x,
float y, float display_width, float display_height,
const uint8_t *data, uint32_t width, uint32_t height);
/**
* Add an image file as an image to the document.
* Passing 0 for either the display width or height will
* include the image but not render it visible.
* Passing a negative number either the display height or width will
* have the image be resized while keeping the original aspect ratio.
* Supports image formats: JPEG, PNG, PPM, PGM & BMP
* @param pdf PDF document to add bookmark to
* @param page Page to add image to (NULL => most recently added page)
* @param x X offset to put image at
* @param y Y offset to put image at
* @param display_width Displayed width of image
* @param display_height Displayed height of image
* @param image_filename Filename of image file to display
* @return < 0 on failure, >= 0 on success
*/
int pdf_add_image_file(struct pdf_doc *pdf, struct pdf_object *page, float x,
float y, float display_width, float display_height,
const char *image_filename);
/**
* Parse image data to determine the image type & metadata
* @param info structure to hold the parsed metadata
* @param data image data to parse
* @param length number of bytes in data
* @param err_msg area to put any failure details
* @param err_msg_length maximum number of bytes to store in err_msg
* @return < 0 on failure, >= 0 on success
*/
int pdf_parse_image_header(struct pdf_img_info *info, const uint8_t *data,
size_t length, char *err_msg,
size_t err_msg_length);
#ifdef __cplusplus
}
#endif
#endif // PDFGEN_H

425
src/aes/aes_ctr_prng.cpp Normal file
View File

@@ -0,0 +1,425 @@
/**
* @file
* @brief High-throughput AES-CTR PRNG for nwipe using Linux AF_ALG.
*
* @details
* This translation unit implements a cryptographically strong pseudorandom
* byte stream based on AES-CTR, leveraging the Linux kernel's crypto API
* (AF_ALG) for hardware-accelerated AES (AES-NI/VAES/NEON/SVE where available).
*
* Motivation:
* - nwipe must supply multi-GB/s of random data to saturate modern NVMe/RAID.
* - User-space OpenSSL-based paths in older builds plateaued around ~250 MB/s
* on some systems due to syscall/memory-copy patterns not tuned for the
* workload.
* - The kernel provides highly optimized AES implementations and scheduling.
*
* Key properties:
* - A single AF_ALG operation socket is opened *once per thread* and reused
* for all generation calls (low syscall overhead).
* - Each generation produces a fixed-size chunk (see CHUNK) by issuing exactly
* two syscalls: `sendmsg()` (to supply IV and length) and `read()` (to fetch
* the keystream).
* - Counter management (increment) is done in user space for determinism.
*
* @warning
* IV (Counter) Encoding:
* This implementation builds the 128-bit AES-CTR IV by storing two 64-bit
* limbs in **little-endian** order (low limb at IV[0..7], high limb at
* IV[8..15]) and then incrementing the 128-bit value in little-endian form.
* This deviates from the big-endian counter semantics commonly assumed by
* RFC-style AES-CTR specifications. The stream remains secure (uniqueness
* of IVs is preserved) but is **not interoperable** with generic RFC-CTR
* streams. See `aes_ctr_prng.h` for a prominent header-level note.
*
* Threading:
* - `tls_op_fd` is thread-local; each thread owns its kernel op-socket.
* - The kernel cipher is re-entrant. No shared state in this TU is writable
* across threads.
*
* Error handling:
* - Functions return `0` on success and `-1` on failure. When underlying
* syscalls fail, `-1` is returned; callers may consult `errno` as usual.
*/
// ============================================================================================
// WHY THIS FILE EXISTS
// --------------------------------------------------------------------------------------------
// nwipe, a secure disk-wiping tool, needs cryptographically strong random data at multi-GB/s
// in order to keep up with todays NVMe and RAID arrays. Users complained when the classic
// user-space OpenSSL path plateaued around ~250 MB/s on modern CPUs. The Linux kernel
// already ships an extremely fast AES implementation (with transparent AES-NI / VAES / NEON
// acceleration) that can be accessed from user space via the AF_ALG socket family. By
// delegating the heavy crypto to the kernel we gain all of the following *for free*:
// • Perfectly tuned instruction selection per CPU (AES-NI, VAES, SVE, etc.)
// • Full cache-line prefetch scheduling written by kernel crypto maintainers
// • Zero-copy when the cipher runs in the same core
// • Automatic fall-back to software if the CPU lacks AES-NI
//
// DESIGN OVERVIEW (TL;DR)
// ----------------------
// ┌─ userspace ───────────────────────────────────────────────────────────────────────────────┐
// │ +-------------------------------+ │
// │ nwipe | aes_ctr_state_t (256 bit) | (1) initialise, store key+counter │
// │ +-------------------------------+ │
// │ │ ▲ │
// │ │ (2) sendmsg() + read() per fixed-size chunk │ │
// └─────────────────────┼───────────────────────────────────────────────────────────┤ kernel │
// │ │ space │
// persistent FD ▼ │ │
// ┌──────────────────────┐ │ │
// │ AF_ALG op socket │ (ctr(aes)) │ │
// └──────────────────────┘ └─────────┘
//
// Public ABI stability:
// ---------------------
// The fd is *not* part of the public state (preserves libnwipe ABI). A TU-local,
// thread-local descriptor is used internally; multiple PRNG instances per thread
// share the same op-socket as needed.
//
// Safety / threading:
// -------------------
// • The kernel cipher is re-entrant. Thread-local fd avoids cross-thread hazards.
// • Counter increments occur in user space; one aes_ctr_state_t per thread.
// ==============================================================================================
#include "aes_ctr_prng.h" // public header (256-bit state, extern "C" API)
#include <sys/socket.h> // socket(), bind(), accept(), sendmsg()
#include <linux/if_alg.h> // AF_ALG constants and skcipher API
#include <unistd.h> // read(), close()
#include <cstring> // memcpy(), memset(), strcpy()
#include <array> // std::array for control buffer
// ----------------------------------------------------------------------------------------------
// CONFIGURABLE CHUNK SIZE
// ----------------------------------------------------------------------------------------------
// The per-call output size (CHUNK) can be configured at compile time via
// AES_CTR_PRNG_CHUNK_BYTES. Default is 128 KiB.
// Example:
// gcc -DAES_CTR_PRNG_CHUNK_BYTES="(64u*1024u)" ...
// ----------------------------------------------------------------------------------------------
#ifndef AES_CTR_PRNG_CHUNK_BYTES
#define AES_CTR_PRNG_CHUNK_BYTES (128u * 1024u) // 128 KiB default
#endif
// ----------------------------------------------------------------------------------------------
// GLOBAL 256-BIT KEY
// ----------------------------------------------------------------------------------------------
// • Loaded from user-supplied seed in aes_ctr_prng_init().
// • Intended to remain constant for the process lifetime (or until re-init).
// • Exposed (non-static) so out-of-TU tests can assert correct key handling.
//
// @note Consider zeroizing on shutdown to avoid key retention in core dumps.
// ----------------------------------------------------------------------------------------------
unsigned char global_key[32];
// ----------------------------------------------------------------------------------------------
// THREAD-LOCAL OPERATION SOCKET (one per nwipe thread)
// ----------------------------------------------------------------------------------------------
// Portable TLS qualifier: C++11 `thread_local` or GCC/Clang `__thread` for C builds.
//
// @invariant tls_op_fd == -1 ⇒ this thread has not opened the op-socket yet.
// tls_op_fd >= 0 ⇒ valid AF_ALG operation socket for "ctr(aes)".
//
// @thread_safety Thread-local; no synchronization required.
// ----------------------------------------------------------------------------------------------
#if defined(__cplusplus) && __cplusplus >= 201103L
#define PRNG_THREAD_LOCAL thread_local
#else
#define PRNG_THREAD_LOCAL __thread
#endif
PRNG_THREAD_LOCAL static int tls_op_fd = -1; // -1 ⇒ not yet opened in this thread
// ----------------------------------------------------------------------------------------------
// CONSTANTS / INTERNAL HELPERS
// ----------------------------------------------------------------------------------------------
namespace {
/**
* @brief AES block size in bytes (by specification).
*/
constexpr std::size_t AES_BLOCK = 16u;
/**
* @brief Fixed-size generation granularity per kernel call.
* @details
* Adjust at build time via AES_CTR_PRNG_CHUNK_BYTES to balance syscall
* overhead vs. latency and memory traffic.
* Typical values: 16 KiB (legacy default), 64 KiB, 128 KiB.
*/
constexpr std::size_t CHUNK = AES_CTR_PRNG_CHUNK_BYTES;
static_assert(CHUNK % AES_BLOCK == 0,
"AES_CTR_PRNG_CHUNK_BYTES must be a multiple of AES_BLOCK (16 bytes)");
/// Number of AES-CTR blocks produced per CHUNK.
constexpr std::size_t BLOCKS_PER_CHUNK = CHUNK / AES_BLOCK;
/**
* @brief Store a 64-bit integer in little-endian byte order.
*
* @param v 64-bit value.
* @param buf Destination pointer; must point to at least 8 writable bytes.
*
* @note
* This function enforces a little-endian layout regardless of host endianness.
* For hot paths you may consider an optimized version using memcpy/bswap on
* big-endian hosts instead of byte-wise stores.
*/
static inline void store64_le(uint64_t v, unsigned char *buf)
{
for (int i = 0; i < 8; ++i)
buf[i] = static_cast<unsigned char>(v >> (8 * i));
}
/**
* @class ControlBuilder
* @brief Helper to assemble `msghdr` and control messages for AF_ALG.
*
* @details
* Builds the control payload for one `sendmsg()` call against an AF_ALG
* skcipher operation socket:
* - Control message #1: `ALG_SET_OP = ALG_OP_ENCRYPT`
* - Control message #2: `ALG_SET_IV` with a 128-bit IV
* - Data iovec: points at the plaintext buffer (here: zero-bytes of length CHUNK)
*
* All data structures live on the stack; constructing this helper is O(1).
*
* @note
* The kernel expects `ivlen` as a host-endian u32 followed by `iv` bytes.
* "Network order not required" is intentional for AF_ALG skcipher IVs.
*/
class ControlBuilder {
public:
/**
* @param iv 128-bit IV (counter value), passed as 16 bytes.
* @param plain Pointer to plaintext buffer (here: all-zero array).
* @param len Plaintext length in bytes; determines keystream length.
*/
ControlBuilder(const unsigned char iv[16], void *plain, size_t len)
{
// ---------- Data iovec ----------
iov_.iov_base = plain;
iov_.iov_len = len;
// ---------- msghdr --------------
msg_.msg_name = nullptr; // already bound via bind()
msg_.msg_namelen = 0;
msg_.msg_iov = &iov_;
msg_.msg_iovlen = 1;
msg_.msg_control = control_.data();
msg_.msg_controllen = control_.size();
msg_.msg_flags = 0;
// ---------- CMSG #1 : ALG_SET_OP = ENCRYPT ----------
cmsghdr *c1 = CMSG_FIRSTHDR(&msg_);
c1->cmsg_level = SOL_ALG;
c1->cmsg_type = ALG_SET_OP;
c1->cmsg_len = CMSG_LEN(sizeof(uint32_t));
*reinterpret_cast<uint32_t*>(CMSG_DATA(c1)) = ALG_OP_ENCRYPT;
// ---------- CMSG #2 : ALG_SET_IV ----------
cmsghdr *c2 = CMSG_NXTHDR(&msg_, c1);
c2->cmsg_level = SOL_ALG;
c2->cmsg_type = ALG_SET_IV;
c2->cmsg_len = CMSG_LEN(sizeof(uint32_t) + 16);
uint32_t ivlen = 16; // host endian; not network order
std::memcpy(CMSG_DATA(c2), &ivlen, sizeof(ivlen));
std::memcpy(CMSG_DATA(c2) + sizeof(ivlen), iv, 16);
}
/// @return Prepared msghdr suitable for `sendmsg()`.
struct msghdr *msg() { return &msg_; }
private:
// Control buffer sufficient for both control messages.
std::array<char,
CMSG_SPACE(sizeof(uint32_t)) +
CMSG_SPACE(sizeof(uint32_t) + 16)> control_{};
struct msghdr msg_{};
struct iovec iov_{};
};
/**
* @brief Open a "ctr(aes)" skcipher operation socket via AF_ALG.
*
* @details
* Performs the `socket()` → `bind()` → `setsockopt(ALG_SET_KEY)` → `accept()`
* sequence. The returned fd is the operation socket used for `sendmsg()`+`read()`.
*
* @param key AES key (32 bytes for AES-256).
* @return Operation socket fd (>= 0) on success, or -1 on failure.
*
* @warning
* This function does not set `FD_CLOEXEC` nor handle `SIGPIPE`. Consider using
* `SOCK_CLOEXEC` on `socket()` and `accept4()` and `MSG_NOSIGNAL` on `sendmsg()`
* in hardened builds.
*/
static int open_ctr_socket(const unsigned char key[32])
{
// 1. Create transform socket (AF_ALG family).
int tfm = ::socket(AF_ALG, SOCK_SEQPACKET, 0);
if (tfm < 0) return -1;
// 2. Describe requested algorithm: type = "skcipher", name = "ctr(aes)".
sockaddr_alg sa = {};
sa.salg_family = AF_ALG;
std::strcpy(reinterpret_cast<char*>(sa.salg_type), "skcipher");
std::strcpy(reinterpret_cast<char*>(sa.salg_name), "ctr(aes)");
if (::bind(tfm, reinterpret_cast<sockaddr*>(&sa), sizeof(sa)) < 0) {
::close(tfm); return -1;
}
// 3. Upload 256-bit key.
if (::setsockopt(tfm, SOL_ALG, ALG_SET_KEY, key, 32) < 0) {
::close(tfm); return -1;
}
// 4. Accept operation socket — the fd we will use for sendmsg/read.
int op = ::accept(tfm, nullptr, nullptr);
::close(tfm); // transform socket no longer needed
return op; // may be -1 on error
}
/**
* @brief Increment a 128-bit little-endian counter by @p n AES blocks.
*
* @details
* The counter is represented as two 64-bit little-endian limbs in state->s[0..1].
* The increment is performed modulo 2^128 with carry propagation from low to high.
*
* @param st PRNG state with s[0]=lo, s[1]=hi limbs.
* @param n Number of AES blocks to add.
*
* @note
* This is **little-endian** counter arithmetic; see the big file-level warning
* about non-RFC CTR semantics.
*/
static void ctr_add(aes_ctr_state_t *st, uint64_t n)
{
uint64_t old = st->s[0];
st->s[0] += n;
if (st->s[0] < old) ++st->s[1]; // handle carry
}
} // namespace (anonymous)
// =================================================================================================
// PUBLIC C API IMPLEMENTATION
// =================================================================================================
extern "C" {
/**
* @brief Initialize PRNG state and lazily open the per-thread AF_ALG socket.
*
* @param[out] state Pointer to PRNG state (must be non-null).
* @param[in] init_key Seed as an array of unsigned long; must provide >= 32 bytes.
* @param[in] key_length Number of `unsigned long` words in @p init_key.
*
* @retval 0 Success.
* @retval -1 Invalid parameters or AF_ALG setup failure.
*
* @details
* - Zeroes the entire state and copies the first 128 bits of the seed into the
* 128-bit counter (little-endian limb order).
* - Saves the first 256 bits as the AES-256 key in @c global_key.
* - Opens the AF_ALG operation socket for "ctr(aes)" on first call in this
* thread and stores the fd in thread-local storage.
*
* @warning
* The chosen IV scheme is little-endian and not RFC-interoperable.
* Do not mix with external AES-CTR generators expecting big-endian counters.
*/
int aes_ctr_prng_init(aes_ctr_state_t *state,
unsigned long init_key[],
unsigned long key_length)
{
if (!state || !init_key || key_length * sizeof(unsigned long) < 32)
return -1;
// Zero entire state, then put seed[0..15] into counter (LE limbs).
std::memset(state, 0, sizeof(*state));
std::memcpy(state->s, init_key, sizeof(uint64_t) * 2);
// Remember full AES-256 key (32 bytes) for possible re-opens.
std::memcpy(global_key, init_key, 32);
// Open per-thread socket on first call in this thread.
if (tls_op_fd == -1) {
tls_op_fd = open_ctr_socket(global_key);
if (tls_op_fd < 0) return -1;
}
return 0;
}
/**
* @brief Produce exactly CHUNK bytes of keystream into @p bufpos.
*
* @param[in] state PRNG state (counter source).
* @param[out] bufpos Destination buffer; must hold at least CHUNK bytes.
*
* @retval 0 Success (CHUNK bytes written).
* @retval -1 Parameter error or syscall failure.
*
* @details
* Sequence per call:
* 1. Assemble a 128-bit IV by storing s[0] (low) and s[1] (high) as
* little-endian 64-bit words into a 16-byte buffer.
* 2. Build the AF_ALG control message (ALG_SET_OP=ENCRYPT, ALG_SET_IV=IV)
* and data iovec pointing to a static all-zero plaintext of length CHUNK.
* 3. `sendmsg()` the request and `read()` back exactly CHUNK bytes of
* ciphertext — which, because plaintext is zero, equals the keystream.
* 4. Increment the 128-bit counter by `BLOCKS_PER_CHUNK`.
*
* @note
* The zero-plaintext buffer is static and zero-initialized once; the kernel
* will not read uninitialized memory. Using zero plaintext is standard for
* obtaining the raw AES-CTR keystream.
*/
int aes_ctr_prng_genrand_128k_to_buf(aes_ctr_state_t *state,
unsigned char *bufpos)
{
if (!state || !bufpos || tls_op_fd < 0)
return -1;
// --- Construct 128-bit IV from counter (little-endian limbs) -------------
unsigned char iv[16];
store64_le(state->s[0], iv); // little-endian low limb
store64_le(state->s[1], iv + 8); // little-endian high limb
// --- Build msghdr ---------------------------------------------------------
static unsigned char zeros[CHUNK] = {0}; // static → zero-initialised once
ControlBuilder ctl(iv, zeros, CHUNK);
// --- sendmsg() + read() ---------------------------------------------------
if (::sendmsg(tls_op_fd, ctl.msg(), 0) != (ssize_t)CHUNK) return -1;
if (::read (tls_op_fd, bufpos, CHUNK) != (ssize_t)CHUNK) return -1;
// --- Advance counter ------------------------------------------------------
ctr_add(state, BLOCKS_PER_CHUNK);
return 0;
}
/**
* @brief Optional cleanup helper (explicitly closes the per-thread op-socket).
*
* @retval 0 Always succeeds.
*
* @details
* The kernel will close FDs at process exit, but explicit shutdown helps
* test harnesses and avoids keeping descriptors alive across exec().
* Consider zeroizing @c global_key here for defense-in-depth.
*/
int aes_ctr_prng_shutdown(void)
{
if (tls_op_fd >= 0) {
::close(tls_op_fd);
tls_op_fd = -1;
}
return 0;
}
} // extern "C"

60
src/aes/aes_ctr_prng.h Normal file
View File

@@ -0,0 +1,60 @@
#ifndef AES_CTR_PRNG_H
#define AES_CTR_PRNG_H
/* Minimal public header for AES-256-CTR PRNG (Linux AF_ALG backend)
*
* Implementation detail:
* - Uses a persistent AF_ALG "ctr(aes)" operation socket opened at init.
* - No socket setup overhead during generation only sendmsg + read.
* - Thread-safety: Not safe unless externally synchronized.
*
* Public state remains exactly 256 bits (4×64-bit words) to allow for
* minimalistic integration in nwipe and similar tools.
*/
#include <stdint.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/* PRNG state: exactly 256 bits (4 × 64-bit words)
*
* s[0] = counter low
* s[1] = counter high
* s[2], s[3] = reserved
*/
typedef struct aes_ctr_state_s {
uint64_t s[4];
} aes_ctr_state_t;
/* Initialize with >=32 bytes of seed (init_key as unsigned-long array)
*
* On first call, also opens the persistent AF_ALG socket.
* Returns 0 on success, -1 on failure.
*/
int aes_ctr_prng_init(aes_ctr_state_t *state,
unsigned long init_key[],
unsigned long key_length);
/* Generate one 128 KiB chunk of random data into bufpos.
*
* Returns 0 on success, -1 on failure.
* Uses the persistent AF_ALG socket.
*/
int aes_ctr_prng_genrand_128k_to_buf(aes_ctr_state_t *state,
unsigned char *bufpos);
/* Optional: Close the persistent AF_ALG socket at program shutdown.
*
* Not required by nwipe, but recommended for tools embedding this code.
*/
int aes_ctr_prng_shutdown(void);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* AES_CTR_PRNG_H */

View File

@@ -0,0 +1,82 @@
/*
* Additive Lagged Fibonacci Generator (ALFG) Implementation
* Author: Fabian Druschke
* Date: 2024-03-13
*
* This is an implementation of the Additive Lagged Fibonacci Generator (ALFG),
* a pseudorandom number generator known for its simplicity and good statistical properties
* for a wide range of applications. ALFGs are particularly noted for their long periods
* and efficiency in generating sequences of random numbers. However, like many other PRNGs,
* they are not suitable for cryptographic purposes due to their predictability.
*
* As the author of this implementation, I, Fabian Druschke, hereby release this work into
* the public domain. I dedicate any and all copyright interest in this work to the public
* domain, making it free to use for anyone for any purpose without any conditions, unless
* such conditions are required by law.
*
* This software is provided "as is", without warranty of any kind, express or implied,
* including but not limited to the warranties of merchantability, fitness for a particular
* purpose, and noninfringement. In no event shall the authors be liable for any claim,
* damages, or other liability, whether in an action of contract, tort, or otherwise, arising
* from, out of, or in connection with the software or the use or other dealings in the software.
*
* Actually it uses subtraction as the core operation, making it conceptually closer to a
* "Subtractive Lagged Fibonacci Generator" (SLFG) variant, but well..
*
* Note: This implementation is designed for non-cryptographic applications and should not be
* used where cryptographic security is required.
*/
#include "add_lagg_fibonacci_prng.h"
#include <stdint.h>
#include <string.h>
#define STATE_SIZE 64 // Size of the state array, sufficient for a high period
#define LAG_BIG 55 // Large lag, e.g., 55
#define LAG_SMALL 24 // Small lag, e.g., 24
#define MODULUS ( 1ULL << 48 ) // Modulus for the operations, here 2^48 for simple handling
void add_lagg_fibonacci_init( add_lagg_fibonacci_state_t* state, uint64_t init_key[], unsigned long key_length )
{
// Simple initialization: Fill the state with the key values and then with a linear combination of them
for( unsigned long i = 0; i < STATE_SIZE; i++ )
{
if( i < key_length )
{
state->s[i] = init_key[i];
}
else
{
// Simple method to generate further state values. Should be improved for serious applications.
state->s[i] = ( 6364136223846793005ULL * state->s[i - 1] + 1 ) % MODULUS;
}
}
state->index = 0; // Initialize the index for the first generation
}
void add_lagg_fibonacci_genrand_uint256_to_buf( add_lagg_fibonacci_state_t* state, unsigned char* bufpos )
{
uint64_t* buf_as_uint64 = (uint64_t*) bufpos; // Interprets bufpos as a uint64_t array for direct assignment
int64_t result; // Use signed integer to handle potential negative results from subtraction
for (int i = 0; i < 4; i++) {
// Subtract the two previous numbers in the sequence
result = (int64_t)state->s[(state->index + LAG_BIG) % STATE_SIZE] - (int64_t)state->s[(state->index + LAG_SMALL) % STATE_SIZE];
// Handle borrow if result is negative
if (result < 0) {
result += MODULUS;
// Optionally set a borrow flag or adjust the next operation based on borrow logic
}
// Store the result (after adjustment) back into the state, ensuring it's positive and within range
state->s[state->index] = (uint64_t)result;
// Write the result into buf_as_uint64
buf_as_uint64[i] = state->s[state->index];
// Update the index for the next round
state->index = (state->index + 1) % STATE_SIZE;
}
}

View File

@@ -0,0 +1,43 @@
/*
* Additive Lagged Fibonacci Generator (ALFG) Implementation definitions
* Author: Fabian Druschke
* Date: 2024-03-13
*
* This is an implementation of the Additive Lagged Fibonacci Generator (ALFG),
* a pseudorandom number generator known for its simplicity and good statistical properties
* for a wide range of applications. ALFGs are particularly noted for their long periods
* and efficiency in generating sequences of random numbers. However, like many other PRNGs,
* they are not suitable for cryptographic purposes due to their predictability.
*
* As the author of this implementation, I, Fabian Druschke, hereby release this work into
* the public domain. I dedicate any and all copyright interest in this work to the public
* domain, making it free to use for anyone for any purpose without any conditions, unless
* such conditions are required by law.
*
* This software is provided "as is", without warranty of any kind, express or implied,
* including but not limited to the warranties of merchantability, fitness for a particular
* purpose, and noninfringement. In no event shall the authors be liable for any claim,
* damages, or other liability, whether in an action of contract, tort, or otherwise, arising
* from, out of, or in connection with the software or the use or other dealings in the software.
*
* Note: This implementation is designed for non-cryptographic applications and should not be
* used where cryptographic security is required.
*/
#ifndef ADD_LAGG_FIBONACCI_PRNG_H
#define ADD_LAGG_FIBONACCI_PRNG_H
#include <stdint.h>
// State definition for the Additive Lagged Fibonacci Generator
typedef struct
{
uint64_t s[64]; // State array
unsigned int index; // Current index in the state array
} add_lagg_fibonacci_state_t;
// Function prototypes
void add_lagg_fibonacci_init( add_lagg_fibonacci_state_t* state, uint64_t init_key[], unsigned long key_length );
void add_lagg_fibonacci_genrand_uint256_to_buf( add_lagg_fibonacci_state_t* state, unsigned char* bufpos );
#endif // ADD_LAGG_FIBONACCI_PRNG_H

624
src/conf.c Normal file
View File

@@ -0,0 +1,624 @@
/*
* conf.c: functions that handle the nwipe.conf configuration file
* and the creation of the nwipe_customers.csv file. nwipe.conf uses
* libconfig format, while nwipe_customers.csv uses comma separted
* values. CSV is used so that the user can build there own customer
* listing using spreadsheets rather than enter all the customer
* information via the nwipe GUI interface.
*
* Copyright PartialVolume <https://github.com/PartialVolume>.
*
* 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.
*
*
*
*/
#include <libconfig.h>
#include <unistd.h>
#include <sys/types.h>
#include "nwipe.h"
#include "context.h"
#include "logging.h"
#include "method.h"
#include "options.h"
#include "conf.h"
config_t nwipe_cfg;
config_setting_t *nwipe_conf_setting, *group_organisation, *root, *group, *previous_group, *setting;
const char* nwipe_conf_str;
char nwipe_config_directory[] = "/etc/nwipe";
char nwipe_config_file[] = "/etc/nwipe/nwipe.conf";
char nwipe_customers_file[] = "/etc/nwipe/nwipe_customers.csv";
char nwipe_customers_file_backup[] = "/etc/nwipe/nwipe_customers.csv.backup";
char nwipe_customers_file_backup_tmp[] = "/etc/nwipe/nwipe_customers.csv.backup.tmp";
/*
* Checks for the existence of nwipe.conf and nwipe_customers.csv
* If either one does not exist, they are created and formatted
* with a basic configuration.
*/
int nwipe_conf_init()
{
FILE* fp_config;
FILE* fp_customers;
config_init( &nwipe_cfg );
char nwipe_customers_initial_content[] =
"\"Customer Name\";\"Contact Name\";\"Customer Address\";\"Contact Phone\"\n"
"\"Not Applicable\";\"Not Applicable\";\"Not Applicable\";\"Not Applicable\"\n";
/* Read /etc/nwipe/nwipe.conf. If there is an error, determine whether
* it's because it doesn't exist. If it doesn't exist create it and
* populate it with a basic structure.
*/
/* Does the /etc/nwipe/nwipe.conf file exist? If not, then create it */
if( access( nwipe_config_file, F_OK ) == 0 )
{
nwipe_log( NWIPE_LOG_INFO, "Nwipes config file %s exists", nwipe_config_file );
/* Read the nwipe.conf configuration file and report any errors */
nwipe_log( NWIPE_LOG_INFO, "Reading nwipe's config file %s", nwipe_config_file );
if( !config_read_file( &nwipe_cfg, nwipe_config_file ) )
{
nwipe_log( NWIPE_LOG_ERROR,
"Syntax error: %s:%d - %s\n",
config_error_file( &nwipe_cfg ),
config_error_line( &nwipe_cfg ),
config_error_text( &nwipe_cfg ) );
}
}
else
{
nwipe_log( NWIPE_LOG_WARNING, "%s does not exist", nwipe_config_file );
/* We assume the /etc/nwipe directory doesn't exist, so try to create it */
mkdir( nwipe_config_directory, 0755 );
/* create the nwipe.conf file */
if( !( fp_config = fopen( nwipe_config_file, "w" ) ) )
{
nwipe_log( NWIPE_LOG_ERROR, "Failed to create %s", nwipe_config_file );
}
else
{
nwipe_log( NWIPE_LOG_INFO, "Created %s", nwipe_config_file );
fclose( fp_config );
}
}
root = config_root_setting( &nwipe_cfg );
/**
* If they don't already exist, populate nwipe.conf with groups, settings and values.
* This will also fill in missing group or settings if they have been corrupted or
* accidently deleted by the user. It will also update an existing nwipe.conf
* file as new groups and settings are added to nwipe. If new settings are added
* to nwipes conf file they MUST appear below in this list of groups and settings.
*/
nwipe_conf_populate( "Organisation_Details.Business_Name", "Not Applicable (BN)" );
nwipe_conf_populate( "Organisation_Details.Business_Address", "Not Applicable (BA)" );
nwipe_conf_populate( "Organisation_Details.Contact_Name", "Not Applicable (BCN)" );
nwipe_conf_populate( "Organisation_Details.Contact_Phone", "Not Applicable (BCP)" );
nwipe_conf_populate( "Organisation_Details.Op_Tech_Name", "Not Applicable (OTN)" );
/**
* Add PDF Certificate/Report settings
*/
nwipe_conf_populate( "PDF_Certificate.PDF_Enable", "ENABLED" );
nwipe_conf_populate( "PDF_Certificate.PDF_Preview", "DISABLED" );
nwipe_conf_populate( "PDF_Certificate.PDF_Host_Visibility", "DISABLED" );
nwipe_conf_populate( "PDF_Certificate.PDF_tag", "DISABLED" );
nwipe_conf_populate( "PDF_Certificate.User_Defined_Tag", "Empty Tag" );
/**
* The currently selected customer that will be printed on the report
*/
nwipe_conf_populate( "Selected_Customer.Customer_Name", "Not Applicable (CN)" );
nwipe_conf_populate( "Selected_Customer.Customer_Address", "Not Applicable (CA)" );
nwipe_conf_populate( "Selected_Customer.Contact_Name", "Not Applicable (CCN)" );
nwipe_conf_populate( "Selected_Customer.Contact_Phone", "Not Applicable (CP)" );
/**
* Write out the new configuration.
*/
if( !config_write_file( &nwipe_cfg, nwipe_config_file ) )
{
nwipe_log( NWIPE_LOG_ERROR, "Failed to write nwipe config to %s", nwipe_config_file );
}
else
{
nwipe_log( NWIPE_LOG_INFO, "Sucessfully written nwipe config to %s", nwipe_config_file );
}
/* Read the nwipe.conf configuration file and report any errors */
if( !config_read_file( &nwipe_cfg, nwipe_config_file ) )
{
nwipe_log( NWIPE_LOG_ERROR,
"Syntax error: %s:%d - %s\n",
config_error_file( &nwipe_cfg ),
config_error_line( &nwipe_cfg ),
config_error_text( &nwipe_cfg ) );
}
/* -----------------------------------------------------------------------------
* Now check nwipe_customers.csv exists, if not create it with a basic structure
*/
if( access( nwipe_customers_file, F_OK ) == 0 )
{
nwipe_log( NWIPE_LOG_INFO, "Nwipes customer file %s exists", nwipe_customers_file );
}
else
{
nwipe_log( NWIPE_LOG_WARNING, "%s does not exist", nwipe_customers_file );
/* We assume the /etc/nwipe directory doesn't exist, so try to create it */
mkdir( nwipe_config_directory, 0755 );
/* create the nwipe_customers.csv file */
if( !( fp_customers = fopen( nwipe_customers_file, "w" ) ) )
{
nwipe_log( NWIPE_LOG_ERROR, "Failed to create %s", nwipe_customers_file );
}
else
{
nwipe_log( NWIPE_LOG_INFO, "Created %s", nwipe_customers_file );
/* Now populate the csv header and first entry, Lines 1 and 2 */
if( fwrite( nwipe_customers_initial_content, sizeof( nwipe_customers_initial_content ), 1, fp_customers )
== 1 )
{
nwipe_log( NWIPE_LOG_INFO, "Populated %s with basic config", nwipe_customers_file );
}
else
{
nwipe_log( NWIPE_LOG_ERROR, "Failed to write basic config to %s", nwipe_customers_file );
}
fclose( fp_customers );
}
}
return ( 0 );
}
void save_selected_customer( char** customer )
{
/* This function saves the user selected customer
* to nwipe's config file /etc/nwipe/nwipe.conf
* for later use by the PDF creation functions.
*/
int idx;
int field_count;
int field_idx;
char field_1[FIELD_LENGTH];
char field_2[FIELD_LENGTH];
char field_3[FIELD_LENGTH];
char field_4[FIELD_LENGTH];
/* zero the field strings */
for( idx = 0; idx < FIELD_LENGTH; idx++ )
field_1[idx] = 0;
for( idx = 0; idx < FIELD_LENGTH; idx++ )
field_2[idx] = 0;
for( idx = 0; idx < FIELD_LENGTH; idx++ )
field_3[idx] = 0;
for( idx = 0; idx < FIELD_LENGTH; idx++ )
field_4[idx] = 0;
/* Extract the field contents from the csv string
*/
idx = 0;
field_idx = 0;
field_count = 1;
while( *( *customer + idx ) != 0 && field_count < NUMBER_OF_FIELDS + 1 )
{
/* Start of a field? */
if( *( *customer + idx ) == '\"' )
{
idx++;
while( *( *customer + idx ) != '\"' && *( *customer + idx ) != 0 )
{
if( field_count == 1 && field_idx < ( FIELD_LENGTH - 1 ) )
{
field_1[field_idx++] = *( *customer + idx );
}
else
{
if( field_count == 2 && field_idx < ( FIELD_LENGTH - 1 ) )
{
field_2[field_idx++] = *( *customer + idx );
}
else
{
if( field_count == 3 && field_idx < ( FIELD_LENGTH - 1 ) )
{
field_3[field_idx++] = *( *customer + idx );
}
else
{
if( field_count == 4 && field_idx < ( FIELD_LENGTH - 1 ) )
{
field_4[field_idx++] = *( *customer + idx );
}
}
}
}
idx++;
}
if( *( *customer + idx ) == '\"' )
{
/* Makesure the field string is terminated */
switch( field_count )
{
case 1:
field_1[field_idx] = 0;
break;
case 2:
field_2[field_idx] = 0;
break;
case 3:
field_3[field_idx] = 0;
break;
case 4:
field_4[field_idx] = 0;
break;
}
field_count++;
field_idx = 0;
}
}
idx++;
}
/* All 4 fields present? */
if( field_count != NUMBER_OF_FIELDS + 1 )
{
nwipe_log( NWIPE_LOG_ERROR,
"Insuffient fields in customer entry, expected %i, actual(field_count) %i, idx=%i",
NUMBER_OF_FIELDS,
field_count,
idx );
return;
}
/* -------------------------------------------------------------
* Write the fields to nwipe's config file /etc/nwipe/nwipe.conf
*/
if( ( setting = config_lookup( &nwipe_cfg, "Selected_Customer.Customer_Name" ) ) )
{
config_setting_set_string( setting, field_1 );
}
else
{
nwipe_log( NWIPE_LOG_ERROR, "Can't find \"Selected Customers.Customer_Name\" in %s", nwipe_config_file );
}
if( ( setting = config_lookup( &nwipe_cfg, "Selected_Customer.Customer_Address" ) ) )
{
config_setting_set_string( setting, field_2 );
}
else
{
nwipe_log( NWIPE_LOG_ERROR, "Can't find \"Selected Customers.Customer_Address\" in %s", nwipe_config_file );
}
if( ( setting = config_lookup( &nwipe_cfg, "Selected_Customer.Contact_Name" ) ) )
{
config_setting_set_string( setting, field_3 );
}
else
{
nwipe_log( NWIPE_LOG_ERROR, "Can't find \"Selected Customers.Contact_Name\" in %s", nwipe_config_file );
}
if( ( setting = config_lookup( &nwipe_cfg, "Selected_Customer.Contact_Phone" ) ) )
{
config_setting_set_string( setting, field_4 );
}
else
{
nwipe_log( NWIPE_LOG_ERROR, "Can't find \"Selected Customers.Contact_Phone\" in %s", nwipe_config_file );
}
/* Write out the new configuration. */
if( !config_write_file( &nwipe_cfg, nwipe_config_file ) )
{
nwipe_log( NWIPE_LOG_ERROR, "Failed to write user selected customer to %s", nwipe_config_file );
}
else
{
nwipe_log( NWIPE_LOG_INFO, "Populated %s with user selected customer", nwipe_config_file );
}
}
int nwipe_conf_update_setting( char* group_name_setting_name, char* setting_value )
{
/* You would call this function of you wanted to update an existing setting in nwipe.conf, i.e
*
* nwipe_conf_update_setting( "PDF_Certificate.PDF_Enable", "ENABLED" )
*
* It is NOT used for creating a new group or setting name.
*/
/* -------------------------------------------------------------
* Write the field to nwipe's config file /etc/nwipe/nwipe.conf
*/
if( ( setting = config_lookup( &nwipe_cfg, group_name_setting_name ) ) )
{
config_setting_set_string( setting, setting_value );
}
else
{
nwipe_log(
NWIPE_LOG_ERROR, "Can't find group.setting_name %s in %s", group_name_setting_name, nwipe_config_file );
return 1;
}
/* Write the new configuration to nwipe.conf
*/
if( !config_write_file( &nwipe_cfg, nwipe_config_file ) )
{
nwipe_log( NWIPE_LOG_ERROR, "Failed to write %s to %s", group_name_setting_name, nwipe_config_file );
return 2;
}
else
{
nwipe_log( NWIPE_LOG_INFO,
"Updated %s with value %s in %s",
group_name_setting_name,
setting_value,
nwipe_config_file );
}
return 0;
} // end nwipe_conf_update_setting()
int nwipe_conf_read_setting( char* group_name_setting_name, const char** setting_value )
{
/* This function returns a setting value from nwipe's configuration file nwipe.conf
* when provided with a groupname.settingname string.
*
* Example:
* const char ** pReturnString;
* nwipe_conf_read_setting( "PDF_Certificate", "PDF_Enable", pReturnString );
*/
/* Separate group_name_setting_name i.e "PDF_Certificate.PDF_Enable" string
* into two separate strings by replacing the period with a NULL.
*/
int return_status;
int length = strlen( group_name_setting_name );
char* group_name = calloc( length, sizeof( char ) );
char* setting_name = calloc( length, sizeof( char ) );
int idx = 0;
while( group_name_setting_name[idx] != 0 && group_name_setting_name[idx] != '.' )
{
idx++;
}
// Copy the group name from the combined input string
memcpy( group_name, group_name_setting_name, idx );
group_name[idx] = '\0'; // Null-terminate group_name
// Copy the setting name from the combined input string
strcpy( setting_name, &group_name_setting_name[idx + 1] );
if( !( setting = config_lookup( &nwipe_cfg, group_name ) ) )
{
nwipe_log( NWIPE_LOG_ERROR, "Can't find group name %s.%s in %s", group_name, setting_name, nwipe_config_file );
return_status = -1;
}
else
{
/* Retrieve data from nwipe.conf */
if( CONFIG_TRUE == config_setting_lookup_string( setting, setting_name, setting_value ) )
{
return_status = 0; /* Success */
}
else
{
nwipe_log(
NWIPE_LOG_ERROR, "Can't find setting_name %s.%s in %s", group_name, setting_name, nwipe_config_file );
return_status = -2;
}
}
free( group_name );
free( setting_name );
return ( return_status );
} // end nwipe_conf_read_setting()
int nwipe_conf_populate( char* path, char* value )
{
/* This function will check that a path containing a group or multiple groups that lead to a setting all exist,
* if they don't exist, the group/s, settings and associated value are created.
*
* The path, a string consisting of one or more groups separted by a period symbol
* '.' with the final element in the path being the setting name. For instance the path might be
* PDF_Certificate.PDF_Enable. Where PDF_Certificate is the group name and PDF_Enable is the setting name.
* If one or other don't exist then they will be created.
*
* An arbitary depth of four groups are allowed for nwipe's configuration file, although we only currently, as of
* October 2023 use a depth of one group. The number of groups can be increased in the future if required by
* changing the definition MAX_GROUP_DEPTH in conf.h
*/
char* path_copy;
char* path_build;
char* group_start[MAX_GROUP_DEPTH + 2]; // A pointer to the start of each group string
char* setting_start;
int idx; // General index
int group_count; // Counts the number of groups in the path
/* Initialise the pointers */
memset( group_start, 0, MAX_GROUP_DEPTH + 2 );
memset( &setting_start, 0, 1 );
/* Allocate enough memory for a copy of the path and initialise to zero */
path_copy = calloc( strlen( path ) + 1, sizeof( char ) );
path_build = calloc( strlen( path ) + 1, sizeof( char ) );
/* Duplicate the path */
strcpy( path_copy, path );
/* Create individual strings by replacing the period '.' with NULL, counting the number of groups. */
idx = 0;
group_count = 0;
/* pointer to first group */
group_start[group_count] = path_copy;
while( *( path_copy + idx ) != 0 )
{
if( group_count > MAX_GROUP_DEPTH )
{
nwipe_log( NWIPE_LOG_ERROR,
"Too many groups in path, specified = %i, allowed = %i ",
group_count,
MAX_GROUP_DEPTH );
return 1;
}
if( *( path_copy + idx ) == '.' )
{
*( path_copy + idx ) = 0;
group_count++;
group_start[group_count] = path_copy + idx + 1;
}
idx++;
}
/* setting_start points to a string that represents the setting to be created */
setting_start = group_start[group_count];
/* Remove the last entry from group_start as that would be the setting and not a group */
group_start[group_count] = 0;
if( group_count == 0 )
{
nwipe_log( NWIPE_LOG_ERROR, "No groups specified in path, i.e. no period . separator found." );
return 2;
}
/**
* Now determine whether the group/s in the path exist, if not create them.
*/
idx = 0;
while( group_start[idx] != 0 )
{
strcat( path_build, group_start[idx] );
if( !( group = config_lookup( &nwipe_cfg, path_build ) ) )
{
if( idx == 0 )
{
group = config_setting_add( root, path_build, CONFIG_TYPE_GROUP );
previous_group = group;
}
else
{
group = config_setting_add( previous_group, group_start[idx], CONFIG_TYPE_GROUP );
previous_group = group;
}
if( group )
{
nwipe_log( NWIPE_LOG_INFO, "Created group [%s] in %s", path_build, nwipe_config_file );
}
else
{
nwipe_log( NWIPE_LOG_ERROR, "Failed to create group [%s] in %s", path_build, nwipe_config_file );
}
}
else
{
previous_group = group;
}
idx++;
if( group_start[idx] != 0 )
{
strcat( path_build, "." );
}
}
/**
* And finally determine whether the setting already exists, if not create it and assign the value to it
*/
/* Does the full path exist ? i.e AAA.BBB */
if( ( group = config_lookup( &nwipe_cfg, path_build ) ) )
{
/* Does the path and setting exist? AAA.BBB.SETTING_NAME */
if( !( setting = config_lookup( &nwipe_cfg, path ) ) )
{
/* Add the new SETTING_NAME */
if( ( setting = config_setting_add( group, setting_start, CONFIG_TYPE_STRING ) ) )
{
nwipe_log( NWIPE_LOG_INFO, "Created setting name %s in %s", path, nwipe_config_file );
}
else
{
nwipe_log(
NWIPE_LOG_ERROR, "Failed to create setting name %s in %s", setting_start, nwipe_config_file );
}
if( config_setting_set_string( setting, value ) == CONFIG_TRUE )
{
nwipe_log( NWIPE_LOG_INFO, "Set value for %s in %s to %s", path, nwipe_config_file, value );
}
else
{
nwipe_log( NWIPE_LOG_ERROR, "Failed to set value for %s in %s to %s", path, nwipe_config_file, value );
}
}
else
{
if( nwipe_options.verbose )
{
nwipe_log( NWIPE_LOG_INFO, "Setting already exists [%s] in %s", path, nwipe_config_file );
}
}
}
else
{
nwipe_log( NWIPE_LOG_INFO, "Couldn't find constructed path [%s] in %s", path_build, nwipe_config_file );
}
free( path_copy );
free( path_build );
return 0;
}
void nwipe_conf_close()
{
config_destroy( &nwipe_cfg );
}

55
src/conf.h Normal file
View File

@@ -0,0 +1,55 @@
#ifndef CONF_H_
#define CONF_H_
/**
* Initialises the libconfig code, called once at the
* start of nwipe, prior to any attempts to access
* nwipe's config file /etc/nwipe/nwipe.conf
* @param none
* @return int
* 0 = success
* -1 = error
*/
int nwipe_conf_init();
/**
* Before exiting nwipe, this function should be called
* to free up libconfig's memory usage
* @param none
* @return void
*/
void nwipe_conf_close();
void save_selected_customer( char** );
/**
* int nwipe_conf_update_setting( char *, char * );
* Use this function to update a setting in nwipe.conf
* @param char * this is the group name and setting name separated by a period '.'
* i.e "PDF_Certificate.PDF_Enable"
* @param char * this is the setting, i.e ENABLED
* @return int 0 = Success
* 1 = Unable to update memory copy
* 2 = Unable to write new configuration to /etc/nwipe/nwipe.conf
*/
int nwipe_conf_update_setting( char*, char* );
/**
* int nwipe_conf_read_setting( char *, char *, const char ** )
* Use this function to read a setting value in nwipe.conf
* @param char * this is the group name
* @param char * this is the setting name
* @param char ** this is a pointer to the setting value
* @return int 0 = Success
* -1 = Unable to find the specified group name
* -2 = Unable to find the specified setting name
*/
int nwipe_conf_read_setting( char*, const char** );
int nwipe_conf_populate( char* path, char* value );
#define FIELD_LENGTH 256
#define NUMBER_OF_FIELDS 4
#define MAX_GROUP_DEPTH 4
#endif /* CONF_H_ */

View File

@@ -24,6 +24,9 @@
#define CONTEXT_H_
#include "prng.h"
#ifndef __HDDTEMP_H__
#include "hddtemp_scsi/hddtemp.h"
#endif /* __HDDTEMP_H__ */
typedef enum nwipe_device_t_ {
NWIPE_DEVICE_UNKNOWN = 0, // Unknown device.
@@ -35,7 +38,8 @@ typedef enum nwipe_device_t_ {
NWIPE_DEVICE_ATA,
NWIPE_DEVICE_NVME,
NWIPE_DEVICE_VIRT,
NWIPE_DEVICE_SAS
NWIPE_DEVICE_SAS,
NWIPE_DEVICE_MMC
} nwipe_device_t;
typedef enum nwipe_pass_t_ {
@@ -55,6 +59,13 @@ typedef enum nwipe_select_t_ {
NWIPE_SELECT_DISABLED // Do not wipe this device and do not allow it to be selected.
} nwipe_select_t;
/* I/O mode for data path: auto, direct, or cached. */
typedef enum {
NWIPE_IO_MODE_AUTO = 0, /* Try O_DIRECT, fall back to cached I/O if not supported. */
NWIPE_IO_MODE_DIRECT, /* Force O_DIRECT, fail if not supported. */
NWIPE_IO_MODE_CACHED /* Force cached I/O, never attempt O_DIRECT. */
} nwipe_io_mode_t;
#define NWIPE_KNOB_SPEEDRING_SIZE 30
#define NWIPE_KNOB_SPEEDRING_GRANULARITY 10
@@ -70,18 +81,30 @@ typedef struct nwipe_speedring_t_
} nwipe_speedring_t;
#define NWIPE_DEVICE_LABEL_LENGTH 200
#define NWIPE_DEVICE_SIZE_TXT_LENGTH 7
#define NWIPE_DEVICE_SIZE_TXT_LENGTH 8
// Arbitary length, so far most paths don't exceed about 25 characters
// Arbitrary length, so far most paths don't exceed about 25 characters
#define MAX_HWMON_PATH_LENGTH 100
// 20 chracters for serial number plus null Byte
#define NWIPE_SERIALNUMBER_LENGTH 20
// UUID size
#define UUID_SIZE 40
// Device name max size including /dev/ path
#define DEVICE_NAME_MAX_SIZE 100
#define NWIPE_DEVICE_SYSFS_PATH_LENGTH 512
typedef struct nwipe_context_t_
{
/*
* Device fields
*/
int device_block_size; // The soft block size reported by the device.
int device_sector_size; // The hard sector size reported by the device.
int device_block_size; // The soft block size reported by the device, as logical
int device_sector_size; // The logical sector size reported by libparted
int device_phys_sector_size; // The physical sector size reported by libparted
int device_bus; // The device bus number.
int device_fd; // The file descriptor of the device file being wiped.
int device_host; // The host number.
@@ -91,9 +114,16 @@ typedef struct nwipe_context_t_
int device_minor; // The minor device number.
int device_part; // The device partition or slice number.
char* device_name; // The device file name.
char device_name_without_path[100];
char gui_device_name[100];
char device_name_without_path[DEVICE_NAME_MAX_SIZE];
char gui_device_name[DEVICE_NAME_MAX_SIZE];
char device_sysfs_path[NWIPE_DEVICE_SYSFS_PATH_LENGTH]; // sysfs path for topology view
char device_name_terse[DEVICE_NAME_MAX_SIZE];
unsigned long long device_size; // The device size in bytes.
u64 device_size_in_sectors; // The device size in number of logical sectors, this may be 512 or 4096 sectors
u64 device_size_in_512byte_sectors; // The device size in number of 512byte sectors, irrespective of logical sector
// size reported by libata
unsigned long long bytes_erased; // Irrespective of pass, this how much of the drive has been erased, CANNOT be
// greater than device_size.
char* device_size_text; // The device size in a more (human)readable format.
char device_size_txt[NWIPE_DEVICE_SIZE_TXT_LENGTH]; // The device size in a more (human)readable format.
char* device_model; // The model of the device.
@@ -101,11 +131,13 @@ typedef struct nwipe_context_t_
struct stat device_stat; // The device file state from fstat().
nwipe_device_t device_type; // Indicates an IDE, SCSI, or Compaq SMART device in enumerated form (int)
char device_type_str[14]; // Indicates an IDE, SCSI, USB etc as per nwipe_device_t but in ascii
char device_serial_no[21]; // Serial number(processed, 20 characters plus null termination) of the device.
int device_is_ssd; // 0 = no SSD, 1 = is a SSD
char device_serial_no[NWIPE_SERIALNUMBER_LENGTH
+ 1]; // Serial number(processed, 20 characters plus null termination) of the device.
char device_UUID[UUID_SIZE]; // Only populated with the UUID if device is a partition
int device_target; // The device target.
u64 eta; // The estimated number of seconds until method completion.
int entropy_fd; // The entropy source. Usually /dev/urandom.
int pass_count; // The number of passes performed by the working wipe method.
u64 pass_done; // The number of bytes that have already been i/o'd in this pass.
u64 pass_errors; // The number of errors across all passes.
@@ -116,7 +148,7 @@ typedef struct nwipe_context_t_
nwipe_entropy_t prng_seed; // The random data that is used to seed the PRNG.
void* prng_state; // The private internal state of the PRNG.
int result; // The process return value.
int round_count; // The number of rounds performed by the working wipe method.
int round_count; // The number of rounds requested by the user for the working wipe method.
u64 round_done; // The number of bytes that have already been i/o'd.
u64 round_errors; // The number of errors across all rounds.
u64 round_size; // The total number of i/o bytes across all rounds.
@@ -128,7 +160,10 @@ typedef struct nwipe_context_t_
short sync_status; // A flag to indicate when the method is syncing.
pthread_t thread; // The ID of the thread.
u64 throughput; // Average throughput in bytes per second.
char throughput_txt[13]; // Human readable throughput.
u64 verify_errors; // The number of verification errors across all passes.
int templ_has_hwmon_data; // 0 = no hwmon data available, 1 = hwmon data available
int templ_has_scsitemp_data; // 0 = no scsitemp data available, 1 = scsitemp data available
char temp1_path[MAX_HWMON_PATH_LENGTH]; // path to temperature variables /sys/class/hwmon/hwmonX/ etc.
int temp1_crit; // Critical high drive temperature, 1000000=unitialised, millidegree celsius.
int temp1_highest; // Historical highest temperature reached, 1000000=unitialised, millidegree celsius.
@@ -136,7 +171,7 @@ typedef struct nwipe_context_t_
int temp1_lcrit; // Critical low drive temperature, 1000000=unitialised, millidegree celsius.
int temp1_lowest; // Historically lowest temperature, 1000000=unitialised, millidegree celsius.
int temp1_max; // Maximum allowed temperature, 1000000=unitialised, millidegree celsius.
int temp1_min; // Miniumum allowed temperature, 1000000=unitialised, millidegree celsius.
int temp1_min; // Minimum allowed temperature, 1000000=unitialised, millidegree celsius.
int temp1_monitored_wipe_max;
int temp1_monitored_wipe_min;
int temp1_monitored_wipe_avg;
@@ -144,13 +179,37 @@ typedef struct nwipe_context_t_
int temp1_flash_rate_counter; // used by the gui for timing the flash rate
int temp1_flash_rate_status; // 0=blank 1=visible
time_t temp1_time; // The time when temperature was last checked, seconds since epoch
struct disk* templ_disk; // Pointer to disk structure for hddtemp SCSI routines
int wipe_status; // Wipe finished = 0, wipe in progress = 1, wipe yet to start = -1.
char wipe_status_txt[10]; // ERASED, FAILED, ABORTED, INSANITY
int spinner_idx; // Index into the spinner character array
char spinner_character[1]; // The current spinner character
double duration; // Duration of the wipe in seconds
char duration_str[20]; // The duration string in hh:mm:ss
time_t start_time; // Start time of wipe
time_t end_time; // End time of wipe
u64 fsyncdata_errors; // The number of fsyncdata errors across all passes.
char PDF_filename[FILENAME_MAX]; // The filename of the PDF certificate/report.
int HPA_status; // 0 = No HPA found/disabled, 1 = HPA detected, 2 = Unknown, unable to checked,
// 3 = Not applicable to this device
u64 HPA_reported_set; // the 'HPA set' value reported hdparm -N, i.e the first value of n/n
u64 HPA_reported_real; // the 'HPA real' value reported hdparm -N, i.e the second value of n/n
int DCO_status; // 0 = No DCO found, 1 = DCO detected, 2 = Unknown, unable to checked
u64 DCO_reported_real_max_sectors; // real max sectors as reported by hdparm --dco-identify
u64 DCO_reported_real_max_size; // real max sectors in bytes
u64 Calculated_real_max_size_in_bytes; // This value is determined from all the possible variations for drives that
// don't support DCO/HPA and those that do. Also drives that can't provide
// HPA/DCO due to the chips they use (USB adapters)
char DCO_reported_real_max_size_text[NWIPE_DEVICE_SIZE_TXT_LENGTH]; // real max size in human readable form i.e 1TB
char Calculated_real_max_size_in_bytes_text[NWIPE_DEVICE_SIZE_TXT_LENGTH]; // calculated real max human readable
u64 HPA_sectors; // The size of the host protected area in sectors
char HPA_size_text[NWIPE_DEVICE_SIZE_TXT_LENGTH]; // Human readable size bytes, KB, MB, GB ..
int HPA_display_toggle_state; // 0 or 1 Used to toggle between "[1TB] [ 33C]" and [HDA STATUS]
time_t HPA_toggle_time; // records a time, then if for instance 3 seconds has elapsed the display changes
nwipe_io_mode_t io_mode; // specific I/O method for a given drive, direct or cached.
int test_use1;
int test_use2;
/*
* Identity contains the raw serial number of the drive
* (where applicable), however, for use within nwipe use the

44
src/cpu_features.c Normal file
View File

@@ -0,0 +1,44 @@
#include "cpu_features.h"
/*
* Executes the CPUID instruction and fills out the provided variables with the results.
* eax: The function/subfunction number to query with CPUID.
* *eax_out, *ebx_out, *ecx_out, *edx_out: Pointers to variables where the CPUID output will be stored.
*/
void cpuid( uint32_t eax, uint32_t* eax_out, uint32_t* ebx_out, uint32_t* ecx_out, uint32_t* edx_out )
{
#if defined( __i386__ ) || defined( __x86_64__ ) /* only on x86 */
#if defined( _MSC_VER ) /* MSVC */
int r[4];
__cpuid( r, eax );
*eax_out = r[0];
*ebx_out = r[1];
*ecx_out = r[2];
*edx_out = r[3];
#elif defined( __GNUC__ ) /* GCC/Clang */
__asm__ __volatile__( "cpuid"
: "=a"( *eax_out ), "=b"( *ebx_out ), "=c"( *ecx_out ), "=d"( *edx_out )
: "a"( eax ) );
#else
#error "Unsupported compiler"
#endif
#else /* not-x86 */
(void) eax;
*eax_out = *ebx_out = *ecx_out = *edx_out = 0; /* CPUID = 0 */
#endif
}
/*
* Checks if the AES-NI instruction set is supported by the processor.
* Returns 1 (true) if supported, 0 (false) otherwise.
*/
int has_aes_ni( void )
{
#if defined( __i386__ ) || defined( __x86_64__ ) /* only for x86 */
uint32_t eax, ebx, ecx, edx;
cpuid( 1, &eax, &ebx, &ecx, &edx );
return ( ecx & ( 1u << 25 ) ) != 0; /* Bit 25 = AES-NI */
#else /* ARM, RISC-V … */
return 0; /* no AES-NI */
#endif
}

18
src/cpu_features.h Normal file
View File

@@ -0,0 +1,18 @@
#ifndef NWIPE_CPU_FEATURES_H
#define NWIPE_CPU_FEATURES_H
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
void cpuid( uint32_t eax, uint32_t* eax_out, uint32_t* ebx_out, uint32_t* ecx_out, uint32_t* edx_out );
int has_aes_ni( void );
#ifdef __cplusplus
}
#endif
#endif /* NWIPE_CPU_FEATURES_H */

1068
src/create_pdf.c Normal file

File diff suppressed because it is too large Load Diff

55
src/create_pdf.h Normal file
View File

@@ -0,0 +1,55 @@
/*.
* create_pdf.h: The header file for the pdf creation routines
*
* Copyright https://github.com/PartialVolume/shredos.x86_64
*
* 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 CREATE_PDF_H_
#define CREATE_PDF_H_
#define MAX_PDF_FOOTER_TEXT_LENGTH 100
#define MAX_PDF_TAG_LENGTH 40
#define STATUS_ICON_GREEN_TICK 1
#define STATUS_ICON_YELLOW_EXCLAMATION 2
#define STATUS_ICON_RED_CROSS 3
/* Additional colors that supplement the standard colors in pdfgen.h
*/
/*! Utility macro to provide gray */
#define PDF_DARK_GREEN PDF_RGB( 0, 0x64, 0 )
/*! Utility macro to provide gray */
#define PDF_GRAY PDF_RGB( 0x5A, 0x5A, 0x5A )
/*! Utility macro to provide gray */
#define PDF_YELLOW PDF_RGB( 0xFF, 0xFF, 0x5A )
/**
* Create the disk erase report in PDF format
* @param pointer to a drive context
* @return returns 0 on success < 1 on error
*/
int create_pdf( nwipe_context_t* ptr );
int nwipe_get_smart_data( nwipe_context_t* );
void create_header_and_footer( nwipe_context_t*, char* );
void pdf_header_footer_text( nwipe_context_t*, char* );
#endif /* CREATE_PDF_H_ */

713
src/customers.c Normal file
View File

@@ -0,0 +1,713 @@
/*
* ****************************************************************************
* customers.c: Functions related to customer processing for the PDF erasure *
* certificate. *
* ****************************************************************************
*
* Copyright PartialVolume <https://github.com/PartialVolume>.
*
* 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.
*
*/
#include <stdio.h>
#include "nwipe.h"
#include "context.h"
#include "gui.h"
#include "logging.h"
#include "conf.h"
#include "customers.h"
#include <sys/stat.h>
void customer_processes( int mode )
{
/* This function reads the customers.csv file, counts the number of lines,
* converts line feeds to NULL and constructs a array of pointers that point
* to the variable length strings.
*
* Depending on the value of mode the pointer array is passed to either
* the select_customers() or delete_customer() functions.
*/
int idx;
int idx2;
FILE* fptr;
struct stat st;
intmax_t size = 0;
int lines;
int list_idx;
int current_list_size;
size_t result_size;
extern char nwipe_customers_file[];
/* Determine size of customers.csv file */
stat( nwipe_customers_file, &st );
size = st.st_size;
current_list_size = 0;
nwipe_customers_buffer_t raw_buffer = (nwipe_customers_buffer_t) calloc( 1, size + 1 );
/* Allocate storage for the contents of customers.csv */
nwipe_customers_buffer_t buffer = (nwipe_customers_buffer_t) calloc( 1, size + 1 );
/* Allocate storage for the processed version of customers.csv,
* i.e we convert the csv format to strings without the quotes
* and semi colon delimiters
*/
nwipe_customers_pointers_t list = (nwipe_customers_pointers_t) calloc( 1, sizeof( char* ) );
current_list_size += sizeof( char* );
/* Open customers.csv */
if( ( fptr = fopen( nwipe_customers_file, "rb" ) ) == NULL )
{
nwipe_log( NWIPE_LOG_ERROR, "Unable to open %s", nwipe_customers_file );
free( buffer );
free( *list );
return;
}
/* Read the customers.csv file and populate the list array with the data */
result_size = fread( raw_buffer, size, 1, fptr );
fclose( fptr );
/* Validate csv contents. With the exception of line feeds,
* remove non printable characters and move to a secondary buffer.
*/
idx = 0;
idx2 = 0;
while( idx < size )
{
if( ( raw_buffer[idx] > 0x1F && raw_buffer[idx] < 0x7F ) || raw_buffer[idx] == 0x0A )
{
/* copy printable characters and line feeds */
buffer[idx2++] = raw_buffer[idx];
}
idx++;
}
/* Construct a array of pointers that point to each line of the csv file
*/
idx = 0;
lines = 0;
list_idx = 0;
while( idx < size )
{
if( buffer[idx] == 0x0A )
{
buffer[idx] = 0;
/* increment the line counter, but don't count
* the first line as that is the csv header line.
*/
if( idx != 0 )
{
lines++;
/* Change the line feed to a NULL, string terminator */
buffer[idx] = 0;
/* Save the pointer to the first data line of the csv. */
list[list_idx++] = &buffer[idx + 1];
current_list_size += sizeof( char* );
/* Expand allocated memory by the size of one pointer */
if( ( list = realloc( list, current_list_size ) ) == NULL )
{
nwipe_log( NWIPE_LOG_ERROR, "Unable to realloc customer list array, out of memory?" );
break;
}
current_list_size += sizeof( char* );
}
}
else
{
/* Replace colons with commas */
if( buffer[idx] == ';' )
{
buffer[idx] = ',';
}
}
idx++;
}
/* Sync lines variable to actual number of lines */
if( lines > 0 )
lines--;
if( idx == size )
{
/* makesure the very last entry is NULL terminated */
buffer[idx] = 0;
}
/* Select the requested mode, customer or delete customer.
*/
switch( mode )
{
case SELECT_CUSTOMER:
select_customers( lines, list );
break;
case DELETE_CUSTOMER:
delete_customer( lines, list );
break;
}
free( raw_buffer );
free( buffer );
}
void select_customers( int count, char** customer_list_array )
{
int selected_entry = 0;
char window_title[] = " Select Customer For PDF Report ";
/* Display the customer selection window */
nwipe_gui_list( count, window_title, customer_list_array, &selected_entry );
/* Save the selected customer details to nwipe's config file /etc/nwipe/nwipe.conf
* If selected entry equals 0, then the customer did not select an entry so skip save.
*/
if( selected_entry != 0 )
{
save_selected_customer( &customer_list_array[selected_entry - 1] );
}
}
void delete_customer( int count, char** customer_list_array )
{
char window_title[] = " Delete Customer ";
int selected_entry = 0;
nwipe_gui_list( count, window_title, customer_list_array, &selected_entry );
if( selected_entry != 0 )
{
delete_customer_csv_entry( &selected_entry );
}
}
void write_customer_csv_entry( char* customer_name,
char* customer_address,
char* customer_contact_name,
char* customer_contact_phone )
{
/**
* Write the attached strings in csv format to the first
* line after the header (line 2 of file)
*/
FILE* fptr = 0;
FILE* fptr2 = 0;
size_t result_size;
/* General index variables */
int idx1, idx2, idx3;
/* Length of the new customer line */
int csv_line_length;
/* Size of the new buffer that holds old contents plus new entry */
int new_customers_buffer_size;
struct stat st;
extern char nwipe_customers_file[];
extern char nwipe_customers_file_backup[];
extern char nwipe_customers_file_backup_tmp[];
intmax_t existing_file_size = 0;
/* pointer to the new customer entry in csv format. */
char* csv_buffer = 0;
/* pointer to the buffer containing the existing customer file */
char* customers_buffer = 0;
/* pointer to the buffer containing the existing customer file plus the new entry */
char* new_customers_buffer = 0;
size_t new_customers_buffer_length;
/* Determine length of all four strings and malloc sufficient storage + 12 = 8 quotes + three colons + null */
csv_line_length = strlen( customer_name ) + strlen( customer_address ) + strlen( customer_contact_name )
+ strlen( customer_contact_phone ) + 12;
if( !( csv_buffer = calloc( 1, csv_line_length == 0 ) ) )
{
nwipe_log( NWIPE_LOG_ERROR, "func:nwipe_gui_add_customer:csv_buffer, calloc returned NULL " );
}
else
{
/* Determine current size of the csv file containing the customers */
stat( nwipe_customers_file, &st );
existing_file_size = st.st_size;
/* calloc sufficient storage to hold the existing customers file */
if( !( customers_buffer = calloc( 1, existing_file_size + 1 ) ) )
{
nwipe_log( NWIPE_LOG_ERROR, "func:nwipe_gui_add_customer:customers_buffer, calloc returned NULL " );
}
else
{
/* create a third buffer which is the combined size of the previous two, i.e existing file size, plus the
* new customer entry + 1 (NULL) */
new_customers_buffer_size = existing_file_size + csv_line_length + 1;
if( !( new_customers_buffer = calloc( 1, new_customers_buffer_size ) ) )
{
nwipe_log( NWIPE_LOG_ERROR, "func:nwipe_gui_add_customer:customers_buffer, calloc returned NULL " );
}
else
{
/* Read the whole of customers.csv file into customers_buffer */
if( ( fptr = fopen( nwipe_customers_file, "rb" ) ) == NULL )
{
nwipe_log( NWIPE_LOG_ERROR, "Unable to open %s", nwipe_customers_file );
}
else
{
/* Read the customers.csv file and populate the list array with the data */
if( ( result_size = fread( customers_buffer, existing_file_size, 1, fptr ) ) != 1 )
{
nwipe_log(
NWIPE_LOG_ERROR,
"func:nwipe_gui_add_customer:Error reading customers file, # bytes read not as expected "
"%i bytes",
result_size );
}
else
{
/* --------------------------------------------------------------------
* Read the first line which is the csv header from the existing customer
* buffer & write to the new buffer.
*/
idx1 = 0; // Index for the current csv buffer
idx2 = 0; // Index for the new csv buffer
idx3 = 0; // Index for new customer fields
while( idx1 < existing_file_size && idx2 < new_customers_buffer_size )
{
if( customers_buffer[idx1] != LINEFEED )
{
new_customers_buffer[idx2++] = customers_buffer[idx1++];
}
else
{
new_customers_buffer[idx2++] = LINEFEED;
break;
}
}
/* --------------------------------------------------------------------------
* Copy the new customer name entry so it is immediately after the csv header
*/
/* Start with first entries opening quote */
new_customers_buffer[idx2++] = '"';
/* Copy the customer_name string */
while( idx3 < FIELD_LENGTH && idx2 < new_customers_buffer_size && customer_name[idx3] != 0 )
{
new_customers_buffer[idx2++] = customer_name[idx3++];
}
/* Close customer name field with a quote */
new_customers_buffer[idx2++] = '"';
/* Insert field delimiters, we use a semi-colon, not a comma ';' */
new_customers_buffer[idx2++] = ';';
/* -----------------------------------------------------------------------------
* Copy the new customer address entry so it is immediately after the csv header
*/
idx3 = 0;
/* Start with first entries opening quote */
new_customers_buffer[idx2++] = '\"';
/* Copy the customer_name string */
while( idx3 < FIELD_LENGTH && idx2 < new_customers_buffer_size && customer_address[idx3] != 0 )
{
new_customers_buffer[idx2++] = customer_address[idx3++];
}
/* Close customer name field with a quote */
new_customers_buffer[idx2++] = '\"';
/* Insert field delimiters, we use a semi-colon, not a comma ';' */
new_customers_buffer[idx2++] = ';';
/* -----------------------------------------------------------------------------
* Copy the new customer contact name entry so it is immediately after the csv header
*/
idx3 = 0;
/* Start with first entries opening quote */
new_customers_buffer[idx2++] = '\"';
/* Copy the customer_name string */
while( idx3 < FIELD_LENGTH && idx2 < new_customers_buffer_size
&& customer_contact_name[idx3] != 0 )
{
new_customers_buffer[idx2++] = customer_contact_name[idx3++];
}
/* Close customer name field with a quote */
new_customers_buffer[idx2++] = '\"';
/* Insert field delimiters, we use a semi-colon, not a comma ';' */
new_customers_buffer[idx2++] = ';';
/* -----------------------------------------------------------------------------
* Copy the new customer contact phone entry so it is immediately after the csv header
*/
idx3 = 0;
/* Start with first entries opening quote */
new_customers_buffer[idx2++] = '\"';
/* Copy the customer_name string */
while( idx3 < FIELD_LENGTH && idx2 < new_customers_buffer_size
&& customer_contact_phone[idx3] != 0 )
{
new_customers_buffer[idx2++] = customer_contact_phone[idx3++];
}
/* Close customer name field with a quote */
new_customers_buffer[idx2++] = '\"';
/* Insert a line feed to finish the new entry */
new_customers_buffer[idx2++] = LINEFEED;
/* skip any LINEFEEDS in the existing customer entry as we just inserted one */
while( customers_buffer[idx1] != 0 && customers_buffer[idx1] == LINEFEED )
{
idx1++;
}
/* -------------------------------------------------------------------------------
* Now copy the existing customer entries, if any, immediately after the new entry
*/
while( idx1 < existing_file_size && idx2 < new_customers_buffer_size )
{
/* Removes any nulls when copying and pasting, which would break this process? */
if( customers_buffer[idx1] == 0 )
{
while( idx1 < existing_file_size && customers_buffer[idx1] == 0 )
{
idx1++;
}
}
new_customers_buffer[idx2++] = customers_buffer[idx1++];
}
/* Rename the customers.csv file to customers.csv.backup */
if( rename( nwipe_customers_file, nwipe_customers_file_backup_tmp ) != 0 )
{
nwipe_log( NWIPE_LOG_ERROR,
"Unable to rename %s to %s",
nwipe_customers_file,
nwipe_customers_file_backup_tmp );
}
else
{
/* Create/open the customers.csv file */
if( ( fptr2 = fopen( nwipe_customers_file, "wb" ) ) == NULL )
{
nwipe_log( NWIPE_LOG_ERROR, "Unable to open %s", nwipe_customers_file );
}
else
{
/* write the new customers.csv file */
new_customers_buffer_length = strlen( new_customers_buffer );
if( ( result_size = fwrite(
new_customers_buffer, sizeof( char ), new_customers_buffer_length, fptr2 ) )
!= new_customers_buffer_length )
{
nwipe_log(
NWIPE_LOG_ERROR,
"func:write_customer_csv_entry:fwrite: Error result_size = %i not as expected",
result_size );
}
else
{
/* Remove the customer.csv.backup file if it exists */
if( remove( nwipe_customers_file_backup ) != 0 )
{
nwipe_log(
NWIPE_LOG_ERROR, "Unable to remove %s", nwipe_customers_file_backup_tmp );
}
else
{
/* Rename the customers.csv.backup.tmp file to customers.csv.backup */
if( rename( nwipe_customers_file_backup_tmp, nwipe_customers_file_backup )
!= 0 )
{
nwipe_log( NWIPE_LOG_ERROR,
"Unable to rename %s to %s",
nwipe_customers_file,
nwipe_customers_file_backup_tmp );
}
nwipe_log( NWIPE_LOG_INFO,
"Succesfully write new customer entry to %s",
nwipe_customers_file );
}
}
fclose( fptr2 );
}
}
fclose( fptr );
}
}
free( new_customers_buffer );
}
free( customers_buffer );
}
free( csv_buffer );
}
}
void delete_customer_csv_entry( int* selected_entry )
{
/**
* Deletes a line from the csv file. The line to be deleted is determined
* by the value of selected_entry
*/
FILE* fptr = 0;
FILE* fptr2 = 0;
size_t result_size;
struct stat st;
extern char nwipe_customers_file[];
extern char nwipe_customers_file_backup[];
extern char nwipe_customers_file_backup_tmp[];
intmax_t existing_file_size = 0;
int linecount;
/* General index variables */
int idx1, idx2, idx3;
/* pointer to the buffer containing the existing customer file */
char* customers_buffer = 0;
/* pointer to the buffer containing the existing customer minus the deleted entry */
char* new_customers_buffer = 0;
int status_flag = 0;
size_t new_customers_buffer_length;
/* Determine current size of the csv file containing the customers */
stat( nwipe_customers_file, &st );
existing_file_size = st.st_size;
/* calloc sufficient storage to hold the existing customers file */
if( !( customers_buffer = calloc( 1, existing_file_size + 1 ) ) )
{
nwipe_log( NWIPE_LOG_ERROR,
"func:nwipe_gui_delete_customer_csv_entry:customers_buffer, calloc returned NULL " );
}
else
{
/* create a second buffer which is identical in size to the first, it will store the customer
* csv file minus the one selected entry
*/
if( !( new_customers_buffer = calloc( 1, existing_file_size + 1 ) ) )
{
nwipe_log( NWIPE_LOG_ERROR,
"func:nwipe_gui_delete_customer_csv_entry:customers_buffer, calloc returned NULL " );
}
else
{
/* Read the whole of customers.csv file into customers_buffer */
if( ( fptr = fopen( nwipe_customers_file, "rb" ) ) == NULL )
{
nwipe_log( NWIPE_LOG_ERROR,
"func:nwipe_gui_delete_customer_csv_entry:Unable to open %s",
nwipe_customers_file );
}
else
{
/* Read the customers.csv file and populate the list array with the data */
if( ( result_size = fread( customers_buffer, existing_file_size, 1, fptr ) ) != 1 )
{
nwipe_log( NWIPE_LOG_ERROR,
"func:nwipe_gui_delete_customer_csv_entry:Error reading customers file, # elements read "
"not as expected "
"%i elements",
result_size );
}
else
{
/* --------------------------------------------------------------------
* Read the first line which is the csv header from the existing customer
* buffer & write to the new buffer.
*/
idx1 = 0; // Index for the current csv buffer
idx2 = 0; // Index for the new csv buffer
linecount = 1; // count the lines in the csv file starting at line 1
while( idx1 < existing_file_size && idx2 < existing_file_size )
{
if( customers_buffer[idx1] != LINEFEED )
{
new_customers_buffer[idx2++] = customers_buffer[idx1++];
}
else
{
new_customers_buffer[idx2++] = customers_buffer[idx1++];
break;
}
}
/* -------------------------------------------------------------------------------
* Now copy the existing customer entries, counting the lines as we go and when we
* get to the the line selected for deletion we skip over it and then carry on
* copying.
*/
while( idx1 < existing_file_size && idx2 < existing_file_size )
{
/* Don't copy nulls */
if( customers_buffer[idx1] == 0 )
{
idx1++;
continue;
}
/* Is this the line to delete? */
if( linecount == *selected_entry )
{
/* skip all the characters in this line */
while( idx1 < existing_file_size && customers_buffer[idx1] != LINEFEED )
{
idx1++;
}
/* skip the trailing linefeed if it exists, may not exist if last line */
if( customers_buffer[idx1] == LINEFEED )
{
idx1++;
}
linecount++;
nwipe_log( NWIPE_LOG_INFO, "Deleted customer entry from cache" );
status_flag = 1;
}
else
{
/* Is the character a LINEFEED? */
if( customers_buffer[idx1] == LINEFEED )
{
linecount++;
}
/* Copy a character */
new_customers_buffer[idx2++] = customers_buffer[idx1++];
}
}
/* Rename the customers.csv file to customers.csv.backup */
if( rename( nwipe_customers_file, nwipe_customers_file_backup_tmp ) != 0 )
{
nwipe_log( NWIPE_LOG_ERROR,
"func:delete_customer_csv_entry:Unable to rename %s to %s",
nwipe_customers_file,
nwipe_customers_file_backup_tmp );
}
else
{
/* Create/open the customers.csv file */
if( ( fptr2 = fopen( nwipe_customers_file, "wb" ) ) == NULL )
{
nwipe_log( NWIPE_LOG_ERROR,
"func:delete_customer_csv_entry:Unable to open %s",
nwipe_customers_file );
}
else
{
/* write the new customers.csv file */
new_customers_buffer_length = strlen( new_customers_buffer );
if( ( result_size = fwrite(
new_customers_buffer, sizeof( char ), new_customers_buffer_length, fptr2 ) )
!= new_customers_buffer_length )
{
nwipe_log(
NWIPE_LOG_ERROR,
"func:delete_customer_csv_entry:fwrite: Error result_size = %i not as expected",
result_size );
}
else
{
/* Remove the customer.csv.backup file if it exists */
if( remove( nwipe_customers_file_backup ) != 0 )
{
nwipe_log( NWIPE_LOG_ERROR,
"func:delete_customer_csv_entry:Unable to remove %s",
nwipe_customers_file_backup_tmp );
}
else
{
/* Rename the customers.csv.backup.tmp file to customers.csv.backup */
if( rename( nwipe_customers_file_backup_tmp, nwipe_customers_file_backup ) != 0 )
{
nwipe_log( NWIPE_LOG_ERROR,
"func:delete_customer_csv_entry:Unable to rename %s to %s",
nwipe_customers_file,
nwipe_customers_file_backup_tmp );
}
if( status_flag == 1 )
{
nwipe_log(
NWIPE_LOG_INFO, "Deleted customer entry in %s", nwipe_customers_file );
}
else
{
nwipe_log( NWIPE_LOG_INFO,
"Failed to delete customer entry in %s",
nwipe_customers_file );
}
}
}
fclose( fptr2 );
}
}
}
fclose( fptr );
}
free( new_customers_buffer );
}
free( customers_buffer );
}
}

42
src/customers.h Normal file
View File

@@ -0,0 +1,42 @@
/*
* ****************************************************************************
* customers.h: Functions related to customer processing for the PDF erasure *
* certificate. *
* ****************************************************************************
*
* Copyright PartialVolume <https://github.com/PartialVolume>.
*
* 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 CUSTOMERS_H_INCLUDED
#define CUSTOMERS_H_INCLUDED
void customer_processes( int );
void select_customers( int, char** );
void delete_customer( int, char** );
void add_customer();
void write_customer_csv_entry( char*, char*, char*, char* );
void delete_customer_csv_entry( int* );
typedef char* nwipe_customers_buffer_t;
typedef char** nwipe_customers_pointers_t;
#define SELECT_CUSTOMER 1
#define DELETE_CUSTOMER 2
#define LINEFEED 0x0A
#endif // CUSTOMERS_H_INCLUDED

View File

@@ -26,6 +26,7 @@
#include <stdio.h>
#include <stdint.h>
#include <ctype.h>
#include "nwipe.h"
#include "context.h"
@@ -40,12 +41,128 @@
#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 );
static void nwipe_normalize_serial( char* serial );
/*
* 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;
@@ -55,7 +172,7 @@ 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_contect_t
* @modifies device_names Populates device_names with an array of nwipe_context_t
* @returns The number of strings in the device_names array.
*
*/
@@ -67,6 +184,9 @@ int nwipe_device_scan( nwipe_context_t*** c )
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++;
@@ -85,17 +205,6 @@ int nwipe_device_scan( nwipe_context_t*** c )
int nwipe_device_get( nwipe_context_t*** c, char** devnamelist, int ndevnames )
{
/**
* Gets information about devices
*
* @parameter device_names A reference to a null array pointer.
* @parameter devnamelist An array of string pointers to the device names
* @parameter ndevnames Number of elements in devnamelist
* @modifies device_names Populates device_names with an array of nwipe_contect_t
* @returns The number of strings in the device_names array.
*
*/
PedDevice* dev = NULL;
int i;
@@ -103,6 +212,8 @@ int nwipe_device_get( nwipe_context_t*** c, char** devnamelist, int ndevnames )
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 )
@@ -134,20 +245,18 @@ int check_device( nwipe_context_t*** c, PedDevice* dev, int dcount )
int fd;
int idx;
int r;
char tmp_serial[21];
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 < 10 )
/* 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
@@ -157,7 +266,7 @@ int check_device( nwipe_context_t*** c, PedDevice* dev, int dcount )
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, tmp_serial );
r = nwipe_get_device_bus_type_and_serialno( dev->path, &bus, &is_ssd, tmp_serial, NULL, 0 );
/* See nwipe_get_device_bus_type_and_serialno() function for meaning of these codes */
if( r == 0 || ( r >= 3 && r <= 6 ) )
@@ -208,12 +317,28 @@ int check_device( nwipe_context_t*** c, PedDevice* dev, int dcount )
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 );
const char* device_name_terse;
device_name_terse = skip_whitespace( next_device->device_name_without_path );
if( device_name_terse != NULL )
{
/* remove the leading whitespace and save result, we use the device without path and no leading or trailing
* space in pdf file creation later */
strcpy( next_device->device_name_terse, device_name_terse );
}
else
{
strcpy( next_device->device_name_terse, "_" );
}
/* To maintain column alignment in the gui we have to remove /dev/ from device names that
* exceed eight characters including the /dev/ path.
*/
@@ -227,40 +352,61 @@ int check_device( nwipe_context_t*** c, PedDevice* dev, int dcount )
}
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 */
/* 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 )
fd = open( next_device->device_name = dev->path, O_RDONLY );
if( fd == 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 < 20; idx++ )
else
{
next_device->device_serial_no[idx] = next_device->identity.serial_no[idx];
/*
* 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, encrypted 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[20] = 0;
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, tmp_serial );
r = nwipe_get_device_bus_type_and_serialno( next_device->device_name,
&next_device->device_type,
&next_device->device_is_ssd,
tmp_serial,
next_device->device_sysfs_path,
sizeof( next_device->device_sysfs_path ) );
/* If serial number & bus retrieved (0) OR unsupported USB bus identified (5) */
if( r == 0 || r == 5 )
@@ -268,7 +414,7 @@ int check_device( nwipe_context_t*** c, PedDevice* dev, int dcount )
/* If the serial number hasn't already been populated */
if( next_device->device_serial_no[0] == 0 )
{
strcpy( next_device->device_serial_no, tmp_serial );
strncpy( next_device->device_serial_no, tmp_serial, NWIPE_SERIALNUMBER_LENGTH );
}
}
@@ -277,27 +423,56 @@ int check_device( nwipe_context_t*** c, PedDevice* dev, int dcount )
{
if( next_device->device_serial_no[0] == 0 )
{
strcpy( next_device->device_serial_no, "???????????????" );
strncpy( next_device->device_serial_no, "????????????????????", NWIPE_SERIALNUMBER_LENGTH + 1 );
}
else
{
strcpy( next_device->device_serial_no, "XXXXXXXXXXXXXXX" );
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;
/* Ensure the serial number cannot break the ncurses UI. */
nwipe_normalize_serial( next_device->device_serial_no );
/* 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;
/* All device strings should be 4 characters, prefix with space if under 4 characters */
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:
@@ -306,6 +481,7 @@ int check_device( nwipe_context_t*** c, PedDevice* dev, int dcount )
case NWIPE_DEVICE_USB:
strcpy( next_device->device_type_str, " USB" );
check_HPA = 1;
break;
case NWIPE_DEVICE_IEEE1394:
@@ -314,6 +490,7 @@ int check_device( nwipe_context_t*** c, PedDevice* dev, int dcount )
case NWIPE_DEVICE_ATA:
strcpy( next_device->device_type_str, " ATA" );
check_HPA = 1;
break;
case NWIPE_DEVICE_NVME:
@@ -327,6 +504,18 @@ int check_device( nwipe_context_t*** c, PedDevice* dev, int dcount )
case NWIPE_DEVICE_SAS:
strcpy( next_device->device_type_str, " SAS" );
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 ) )
@@ -359,6 +548,37 @@ int check_device( nwipe_context_t*** c, PedDevice* dev, int dcount )
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 );
}
/*************************************
* Check whether the device has a UUID
*/
char uuid[UUID_SIZE] = "";
if( get_device_uuid( next_device->device_name, uuid ) == 0 )
{
strncpy( next_device->device_UUID, uuid, UUID_SIZE );
nwipe_log( NWIPE_LOG_INFO, "UUID for %s is: %s\n", next_device->device_name, next_device->device_UUID );
}
else
{
nwipe_log( NWIPE_LOG_INFO, "No UUID available for %s\n", next_device->device_name );
}
/* print an empty line to separate the drives in the log */
nwipe_log( NWIPE_LOG_INFO, " " );
( *c )[dcount] = next_device;
return 1;
}
@@ -416,10 +636,49 @@ char* trim( char* str )
return str;
}
int nwipe_get_device_bus_type_and_serialno( char* device, nwipe_device_t* bus, char* serialnumber )
/*
* Remove non-ASCII and control characters from a serial number string,
* then trim leading/trailing whitespace and left-justify it in-place.
* This keeps the value safe for ncurses output.
*/
static void nwipe_normalize_serial( char* serial )
{
unsigned char ch;
char* src;
char* dst;
if( serial == NULL )
{
return;
}
src = dst = serial;
while( ( ch = (unsigned char) *src++ ) != '\0' )
{
if( isascii( ch ) && !iscntrl( ch ) )
{
*dst++ = (char) ch;
}
/* Alle remaining control characters will be dropped ( >0x7F) */
}
*dst = '\0';
/* Use existing trim() function */
trim( serial );
}
int nwipe_get_device_bus_type_and_serialno( char* device,
nwipe_device_t* bus,
int* is_ssd,
char* serialnumber,
char* sysfs_path,
size_t sysfs_path_size )
{
/* The caller provides a string that contains the device, i.e. /dev/sdc, also a pointer
* to a integer (bus type) and thirdly a 21 byte
* 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.
@@ -453,6 +712,7 @@ int nwipe_get_device_bus_type_and_serialno( char* device, nwipe_device_t* bus, c
char smartctl_command[] = "smartctl -i %s";
char smartctl_command2[] = "/sbin/smartctl -i %s";
char smartctl_command3[] = "/usr/bin/smartctl -i %s";
char smartctl_command4[] = "/usr/sbin/smartctl -i %s";
char device_shortform[50];
char result[512];
char final_cmd_readlink[sizeof( readlink_command ) + sizeof( device_shortform )];
@@ -462,6 +722,14 @@ int nwipe_get_device_bus_type_and_serialno( char* device, nwipe_device_t* bus, c
"serial number:", "lu wwn device id:", "logical unit id:", "" /* Don't remove this empty string !, important */
};
/* Ensure the serialnumber buffer is in a defined state even if we
* never find a "serial number:" line in smartctl output.
*/
if( serialnumber != NULL )
{
memset( serialnumber, 0, NWIPE_SERIALNUMBER_LENGTH + 1 );
}
/* Initialise return value */
set_return_value = 0;
@@ -546,11 +814,19 @@ int nwipe_get_device_bus_type_and_serialno( char* device, nwipe_device_t* bus, c
if( fp != NULL )
{
/* Read the output a line at a time - output it. */
if( fgets( result, sizeof( result ) - 1, fp ) != NULL )
{
strip_CR_LF( result );
if( sysfs_path != NULL && sysfs_path_size > 0 )
{
strncpy( sysfs_path, result, sysfs_path_size - 1 );
sysfs_path[sysfs_path_size - 1] = '\0';
}
if( nwipe_options.verbose )
{
strip_CR_LF( result );
nwipe_log( NWIPE_LOG_DEBUG, "Readlink: %s", result );
}
@@ -581,6 +857,13 @@ int nwipe_get_device_bus_type_and_serialno( char* device, nwipe_device_t* bus, c
{
*bus = NWIPE_DEVICE_VIRT;
}
else
{
if( strstr( result, "/mmcblk" ) != 0 )
{
*bus = NWIPE_DEVICE_MMC;
}
}
}
}
}
@@ -626,7 +909,14 @@ int nwipe_get_device_bus_type_and_serialno( char* device, nwipe_device_t* bus, c
{
if( system( "which /usr/bin/smartctl > /dev/null 2>&1" ) )
{
nwipe_log( NWIPE_LOG_WARNING, "Command not found. Install smartmontools !" );
if( system( "which /usr/sbin/smartctl > /dev/null 2>&1" ) )
{
nwipe_log( NWIPE_LOG_WARNING, "Command not found. Install smartmontools !" );
}
else
{
sprintf( final_cmd_smartctl, smartctl_command4, device );
}
}
else
{
@@ -662,7 +952,7 @@ int nwipe_get_device_bus_type_and_serialno( char* device, nwipe_device_t* bus, c
{
/* 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 occasionaly
* for serial number, i.e mostly it produces labels "Serial Number:" but occasionally
* it produces a label "Serial number:" */
idx = 0;
@@ -671,7 +961,7 @@ int nwipe_get_device_bus_type_and_serialno( char* device, nwipe_device_t* bus, c
/* If upper case alpha character, change to lower case */
if( result[idx] >= 'A' && result[idx] <= 'Z' )
{
result[idx] += 32;
result[idx] = tolower( result[idx] );
}
idx++;
@@ -718,7 +1008,7 @@ int nwipe_get_device_bus_type_and_serialno( char* device, nwipe_device_t* bus, c
}
}
nwipe_log( NWIPE_LOG_DEBUG, "smartctl: %s", result );
nwipe_log( NWIPE_LOG_INFO, "smartctl: %s", result );
}
if( strstr( result, "serial number:" ) != 0 )
@@ -726,7 +1016,8 @@ int nwipe_get_device_bus_type_and_serialno( char* device, nwipe_device_t* bus, c
/* strip any leading or trailing spaces and left justify, +15 is the length of "Serial Number:" */
trim( &result[15] );
strncpy( serialnumber, &result[15], 20 );
strncpy( serialnumber, &result[15], NWIPE_SERIALNUMBER_LENGTH );
serialnumber[NWIPE_SERIALNUMBER_LENGTH] = 0;
}
if( *bus == 0 )
@@ -735,6 +1026,10 @@ int nwipe_get_device_bus_type_and_serialno( char* device, nwipe_device_t* bus, c
{
/* 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 )
{
@@ -747,6 +1042,10 @@ int nwipe_get_device_bus_type_and_serialno( char* device, nwipe_device_t* bus, c
/* 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 )
{
@@ -754,6 +1053,20 @@ int nwipe_get_device_bus_type_and_serialno( char* device, nwipe_device_t* bus, c
}
}
}
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 */
@@ -780,9 +1093,9 @@ int nwipe_get_device_bus_type_and_serialno( char* device, nwipe_device_t* bus, c
if( exit_status == 1 )
{
nwipe_log( NWIPE_LOG_WARNING, "%s USB bridge, no pass-through support", device );
nwipe_log( NWIPE_LOG_WARNING, "Smartctl is unable to provide smart data for %s", device );
if( *bus == NWIPE_DEVICE_USB )
if( *bus == NWIPE_DEVICE_USB || *bus == NWIPE_DEVICE_MMC )
{
strcpy( serialnumber, "(S/N: unknown)" );
set_return_value = 5;
@@ -795,21 +1108,6 @@ int nwipe_get_device_bus_type_and_serialno( char* device, nwipe_device_t* bus, c
return set_return_value;
}
void strip_CR_LF( char* str )
{
/* In the specified string, replace any CR or LF with a space */
int idx = 0;
int len = strlen( str );
while( idx < len )
{
if( str[idx] == 0x0A || str[idx] == 0x0D )
{
str[idx] = ' ';
}
idx++;
}
}
void remove_ATA_prefix( char* str )
{
/* Remove "ATA" prefix if present in the model no. string, left justifing string */

View File

@@ -27,8 +27,25 @@
void nwipe_device_identify( nwipe_context_t* c ); // Get hardware information about the device.
int nwipe_device_scan( nwipe_context_t*** c ); // Find devices that we can wipe.
/**
* Gets information about devices
*
* @parameter device_names A reference to a null array pointer.
* @parameter devnamelist An array of string pointers to the device names
* @parameter ndevnames Number of elements in devnamelist
* @modifies device_names Populates device_names with an array of nwipe_contect_t
* @returns The number of strings in the device_names array.
*
*/
int nwipe_device_get( nwipe_context_t*** c, char** devnamelist, int ndevnames ); // Get info about devices to wipe.
int nwipe_get_device_bus_type_and_serialno( char*, nwipe_device_t*, char* );
int nwipe_get_device_bus_type_and_serialno( char* device,
nwipe_device_t* bus,
int* is_ssd,
char* serialnumber,
char* sysfs_path,
size_t sysfs_path_size );
void strip_CR_LF( char* );
void determine_disk_capacity_nomenclature( u64, char* );
void remove_ATA_prefix( char* );

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,16 @@
/* Autogenerated by hxtools bin2c */
#ifndef NWIPE_EXCLAMATION_JPG_H
#define NWIPE_EXCLAMATION_JPG_H 1
#ifdef __cplusplus
extern "C" {
#endif
extern const unsigned char bin2c_nwipe_exclamation_jpg[63304];
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* NWIPE_EXCLAMATION_JPG_H */

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,16 @@
/* Autogenerated by hxtools bin2c */
#ifndef REDCROSS_H
#define REDCROSS_H 1
#ifdef __cplusplus
extern "C" {
#endif
extern const unsigned char bin2c_redcross_jpg[60331];
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* REDCROSS_H */

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,16 @@
/* Autogenerated by hxtools bin2c */
#ifndef SHRED_DB_JPG_H
#define SHRED_DB_JPG_H 1
#ifdef __cplusplus
extern "C" {
#endif
extern const unsigned char bin2c_shred_db_jpg[27063];
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* SHRED_DB_JPG_H */

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,16 @@
/* Autogenerated by hxtools bin2c */
#ifndef TICK_ERASED_JPG_H
#define TICK_ERASED_JPG_H 1
#ifdef __cplusplus
extern "C" {
#endif
extern const unsigned char bin2c_te_jpg[54896];
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* TICK_ERASED_JPG_H */

Binary file not shown.

5964
src/gui.c

File diff suppressed because it is too large Load Diff

View File

@@ -33,6 +33,18 @@ void nwipe_gui_create_stats_window( void ); // Create the stats window
void nwipe_gui_create_all_windows_on_terminal_resize(
int force_creation,
const char* footer_text ); // If terminal is resized recreate all windows
/**
* The primary user interface. Allows the user to
* change options and specify the devices to be wiped.
*
* @parameter count The number of contexts in the array.
* @parameter c An array of device contexts.
*
* @modifies c[].select Sets the select flag according to user input.
* @modifies options Sets program options according to to user input.
*
*/
void nwipe_gui_select( int count, nwipe_context_t** c ); // Select devices to wipe.
void* nwipe_gui_status( void* ptr ); // Update operation progress.
void nwipe_gui_method( void ); // Change the method option.
@@ -41,8 +53,73 @@ void nwipe_gui_prng( void ); // Change the prng option.
void nwipe_gui_rounds( void ); // Change the rounds option.
void nwipe_gui_verify( void ); // Change the verify option.
void nwipe_gui_noblank( void ); // Change the noblank option.
void nwipe_gui_config( void ); // Change the nwipe settings
void nwipe_gui_edit_organisation( void ); // Edit organisation performing the erasure
void nwipe_gui_organisation_business_name( const char* ); // Edit business name performing erase
void nwipe_gui_organisation_business_address( const char* ); // Edit business address performing erase
void nwipe_gui_organisation_contact_name( const char* ); // Edit business contact name
void nwipe_gui_organisation_contact_phone( const char* ); // Edit business contact phone
void nwipe_gui_organisation_op_tech_name( const char* ); // Edit the name of the operator/technician
void nwipe_gui_list( int, char* window_title, char**, int* );
void nwipe_gui_add_customer( void ); // Add new customer
void nwipe_gui_add_customer_name( char* ); // Add new customer name
void nwipe_gui_add_customer_address( char* ); // Add new customer address
void nwipe_gui_add_customer_contact_name( char* ); // Add new customer contact name
void nwipe_gui_add_customer_contact_phone( char* ); // Add new customer contact phone
int nwipe_gui_yes_no_footer( void ); // Change footer to yes no
void nwipe_gui_user_defined_tag( void ); // Edit user defined text to be added to pdf report
void nwipe_gui_pdf_certificate_edit_user_defined_tag( const char* );
void nwipe_gui_view_device( int count, nwipe_context_t** c, int focus );
void nwipe_gui_benchmark_prng( void );
/** nwipe_gui_preview_org_customer( int )
* Display a editable preview of organisation, customer and date/time
*
* @param int mode 0 = use prior to drive selection
* 1 = use in config menus
* The different modes simply change the text in the footer menu and in the case
* of mode 0 enable the A key which means accept & display drive selection.
*/
void nwipe_gui_preview_org_customer( int ); // Preview window for wipe organisation and customer
void nwipe_gui_set_system_year( void ); // Set the systems current year
void nwipe_gui_set_system_month( void ); // Set the systems month
void nwipe_gui_set_system_day( void ); // Set the system day of the month
void nwipe_gui_set_system_hour( void ); // Set the system hour
void nwipe_gui_set_system_minute( void ); // Set the system minute
/**
* Truncate a string based on start position and terminal width
*
* @parameter wcols Width of window, obtained from getmaxyx(..)
* @parameter start_column Start column where the string starts
* @parameter input_string The string to be truncated if necessary
* @parameter ouput_string The possibly truncated string
* @parameter ouput_string_length Max length of output string
* @Return returns a pointer to the output string
*/
char* str_truncate( int, int, const char*, char*, int ); // Truncate string based on start column and terminal width
/**
* Set system date and time
*
* @parameter void
* @Return void
*/
void nwipe_gui_set_date_time( void );
int spinner( nwipe_context_t** ptr, int ); // Return the next spinner character
void temp1_flash( nwipe_context_t* ); // toggles term1_flash_status, which flashes the temperature
/**
* If the current drive temperature is available, print it to the GUI.
* This function determines if the drive temperature limits are specified &
* if so, whether the temperature should be printed as white text on blue if the
* drive is operating within it's temperature specification or red text on
* blue if the drive has exceeded the critical high temperature or black on
* blue if the drive has dropped below the drives minimum temperature specification.
* @param pointer to the drive context
*/
void wprintw_temperature( nwipe_context_t* );
int compute_stats( void* ptr );
@@ -54,9 +131,19 @@ void nwipe_update_speedring( nwipe_speedring_t* speedring, u64 speedring_done, t
* the drive selection screen is displayed. (prior to wipe starting). */
#define GETCH_BLOCK_MS 250 /* millisecond block time for getch() */
/* Note The value of 1 (100ms) is the ideal speed for screen refresh during a wipe, a value of 2 is noticably slower,
/* Note The value of 1 (100ms) is the ideal speed for screen refresh during a wipe, a value of 2 is noticeably slower,
* don't change unless you understand how this value affects keyboard responsiveness and speed of screen stats/spinner
* updating */
#define GETCH_GUI_STATS_UPDATE_MS 1 /* 1 * 100 = 1/10/sec = millisecond block time for gui stats screen updates */
#define FIELD_LENGTH 256
#define MAX_TITLE_LENGTH 76
#define YES 1
#define NO 0
#define SHOWING_PRIOR_TO_DRIVE_SELECTION 0
#define SHOWING_IN_CONFIG_MENUS 1
#endif /* GUI_H_ */

View File

@@ -0,0 +1,159 @@
/*
* get_scsi_temp.c: functions that populate the drive temperature variables
* in SCSI/SAS drives context structure.
* Routines from hddtemp are used here.
*
* Author: Gerold Gruber <Gerold.Gruber@edv2g.de>
*
* 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.
*/
//#define _LARGEFILE64_SOURCE
//#define _FILE_OFFSET_BITS 64
#define _BSD_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>
#include <dirent.h>
#include <assert.h>
#include "nwipe.h"
#include "context.h"
#include "method.h"
#include "device.h"
#include "prng.h"
#include "options.h"
#include "device.h"
#include "logging.h"
#include "temperature.h"
#include "miscellaneous.h"
#include "hddtemp.h"
#include "scsi.h"
int scsi_get_temperature( struct disk * );
int nwipe_init_scsi_temperature( nwipe_context_t* c )
{
/* dsk anlegen, malloc */
struct disk *dsk = (struct disk *) malloc(sizeof(struct disk));
/* Check the allocation. */
if( !dsk )
{
nwipe_perror( errno, __FUNCTION__, "malloc" );
nwipe_log( NWIPE_LOG_FATAL, "Unable to get memory for disk struct for %s",
c->device_name );
exit( 1 ) ;
}
assert(dsk);
memset(dsk, 0, sizeof(*dsk));
/* save the dsk pointer for later use */
c->templ_disk = dsk;
/* initialize */
dsk->drive = c->device_name;
dsk->type = BUS_SCSI; /* we know this as we are only called in this case */
errno = 0;
dsk->errormsg[0] = '\0';
if( (dsk->fd = open(dsk->drive, O_RDONLY | O_NONBLOCK)) < 0) {
snprintf(dsk->errormsg, MAX_ERRORMSG_SIZE, "open: %s\n", strerror(errno));
dsk->type = ERROR;
return 1;
}
// sg_logs -t <device>
if( scsi_get_temperature( dsk ) == GETTEMP_SUCCESS )
{
c->temp1_input = dsk->value;
c->temp1_crit = dsk->refvalue;
c->temp1_lcrit = -40; /* just to give it a value with some kind of sense */
c->temp1_highest = dsk->value;
c->temp1_lowest = dsk->value;
c->temp1_max = dsk->refvalue - 5; /* seems to be kind of useful */
}
else
{
nwipe_log( NWIPE_LOG_ERROR, "Can not read SCSI temperature for %s, %s",
dsk->drive, dsk->errormsg );
close( dsk->fd );
free( dsk );
c->templ_disk = NULL;
return 1;
}
return 0;
}
int nwipe_get_scsi_temperature( nwipe_context_t* c )
{
struct disk *dsk;
dsk = c->templ_disk;
if( c->templ_disk != NULL && c->templ_disk->fd != -1 )
{
if( scsi_get_temperature( dsk ) == GETTEMP_SUCCESS )
{
c->temp1_input = dsk->value;
/* not at all of interest */
if( c->temp1_input > c->temp1_highest )
{
c->temp1_highest = c->temp1_input;
}
if( c->temp1_input < c->temp1_lowest )
{
c->temp1_lowest = c->temp1_input;
}
/* end not at all of interest ;-) */
}
else
{
nwipe_log( NWIPE_LOG_ERROR, "Could not read SCSI temperature for %s, %s",
dsk->drive, dsk->errormsg );
return 2;
}
}
else
{
nwipe_log( NWIPE_LOG_INFO, "no SCSI temperature reading for %s", dsk->drive );
return 1;
}
return 0;
}
void nwipe_shut_scsi_temperature( nwipe_context_t* c )
{
if( c->templ_disk->fd != -1 )
{
close( c->templ_disk->fd );
}
if( c->templ_disk != NULL )
{
free( c->templ_disk );
}
return;
}

View File

@@ -0,0 +1,91 @@
/*
* Copyright (C) 2002 Emmanuel VARAGNAT <hddtemp@guzu.net>
*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __HDDTEMP_H__
#define __HDDTEMP_H__
#include <time.h>
// #include "db.h"
//#ifdef ARCH_I386
//typedef unsigned short u16;
//#endif
#include <linux/types.h>
typedef __u16 u16;
#define MAX_ERRORMSG_SIZE 128
#define DEFAULT_ATTRIBUTE_ID 194
#define F_to_C(val) (int)(((double)(val)-32.0)/1.8)
#define C_to_F(val) (int)(((double)(val)*(double)1.8) + (double)32.0)
enum e_bustype { ERROR = 0, BUS_UNKNOWN, BUS_SATA, BUS_ATA, BUS_SCSI, BUS_TYPE_MAX };
#define GETTEMP_SUCCESS 0
#define GETTEMP_ERROR 1
#define GETTEMP_NOT_APPLICABLE 2
#define GETTEMP_UNKNOWN 3
#define GETTEMP_GUESS 4
#define GETTEMP_KNOWN 5
#define GETTEMP_NOSENSOR 6
#define GETTEMP_DRIVE_SLEEP 7
enum e_powermode {
PWM_UNKNOWN,
PWM_ACTIVE,
PWM_SLEEPING,
PWM_STANDBY
};
struct disk {
struct disk * next;
int fd;
const char * drive;
const char * model;
enum e_bustype type;
int value; /* the drive's temperature */
int refvalue; /* aka trip temperature */
struct harddrive_entry * db_entry;
char errormsg[MAX_ERRORMSG_SIZE];
// enum e_gettemp ret;
int ret;
time_t last_time;
};
struct bustype {
char *name;
int (*probe)(int);
const char *(*model)(int);
enum e_gettemp (*get_temperature)(struct disk *);
};
extern struct bustype * bus[BUS_TYPE_MAX];
extern char errormsg[MAX_ERRORMSG_SIZE];
extern int tcp_daemon, debug, quiet, wakeup, af_hint;
extern char separator;
extern long portnum, syslog_interval;
extern char * listen_addr;
int value_to_unit(struct disk *dsk);
char get_unit(struct disk *dsk);
#endif

125
src/hddtemp_scsi/scsi.c Normal file
View File

@@ -0,0 +1,125 @@
/*
* Copyright (C) 2002 Emmanuel VARAGNAT <hddtemp@guzu.net>
*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* Adapted from a patch sended by : Frederic LOCHON <lochon@roulaise.net>
*/
/*
* Adapted for use with nwipe by : Gerold Gruber <Gerold.Gruber@edv2g.de>
*/
// Include file generated by ./configure
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
// Gettext includes
#if ENABLE_NLS
#include <libintl.h>
#define _(String) gettext (String)
#else
#define _(String) (String)
#endif
// Standard includes
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <scsi/scsi.h>
// Application specific includes
#include "scsicmds.h"
#include "hddtemp.h"
int scsi_get_temperature(struct disk *dsk) {
int i;
int tempPage = 0;
unsigned char buffer[1024];
/*
on triche un peu
we cheat a little and do not really read form drivedb as SCSI disks are not included there
original code omitted as there is no need for it in the context of nwipe
*/
/*
Enable SMART
*/
if (scsi_smartDEXCPTdisable(dsk->fd) != 0) {
snprintf(dsk->errormsg, MAX_ERRORMSG_SIZE, "%s", strerror(errno));
close(dsk->fd);
dsk->fd = -1;
return GETTEMP_ERROR;
}
/*
Temp. capable
*/
if (scsi_logsense(dsk->fd , SUPPORT_LOG_PAGES, buffer, sizeof(buffer)) != 0) {
snprintf(dsk->errormsg, MAX_ERRORMSG_SIZE, _("log sense failed : %s"), strerror(errno));
close(dsk->fd);
dsk->fd = -1;
return GETTEMP_ERROR;
}
for ( i = 4; i < buffer[3] + LOGPAGEHDRSIZE ; i++) {
if (buffer[i] == TEMPERATURE_PAGE) {
tempPage = 1;
break;
}
}
if(tempPage) {
/*
get temperature (from scsiGetTemp (scsicmd.c))
*/
if (scsi_logsense(dsk->fd , TEMPERATURE_PAGE, buffer, sizeof(buffer)) != 0) {
snprintf(dsk->errormsg, MAX_ERRORMSG_SIZE, _("log sense failed : %s"), strerror(errno));
close(dsk->fd);
dsk->fd = -1;
return GETTEMP_ERROR;
}
if( (int)buffer[7] == 2 ) /* PARAMETER LENGTH */
{
dsk->value = buffer[9];
}
else
{
snprintf(dsk->errormsg, MAX_ERRORMSG_SIZE, _("parameter length unexpected: %d"), (int)buffer[7] );
return GETTEMP_UNKNOWN;
}
dsk->refvalue = buffer[15];
if( (int)buffer[13] == 2 ) /* PARAMETER LENGTH */
{
dsk->refvalue = buffer[15];
}
else
{
snprintf(dsk->errormsg, MAX_ERRORMSG_SIZE, _("parameter ref length unexpected: %d"), (int)buffer[13] );
return GETTEMP_UNKNOWN;
}
return GETTEMP_SUCCESS;
} else {
return GETTEMP_NOSENSOR;
}
}

24
src/hddtemp_scsi/scsi.h Normal file
View File

@@ -0,0 +1,24 @@
/*
* Copyright (C) 2002 Emmanuel VARAGNAT <hddtemp@guzu.net>
*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef SCSIC_H_
#define SCSIC_H_
extern struct bustype scsi_bus;
#endif

221
src/hddtemp_scsi/scsicmds.c Normal file
View File

@@ -0,0 +1,221 @@
/*
* Copyright (C) 2002 Emmanuel VARAGNAT <hddtemp@guzu.net>
*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*/
// Include file generated by ./configure
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
// Gettext includes
#if ENABLE_NLS
#include <libintl.h>
#define _(String) gettext (String)
#else
#define _(String) (String)
#endif
// Standard includes
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <scsi/scsi.h>
#include <scsi/sg.h>
#include <scsi/scsi_ioctl.h>
// Application specific includes
#include "scsicmds.h"
static void scsi_fixstring(unsigned char *s, int bytecount)
{
unsigned char *p;
unsigned char *end;
p = s;
end = s + bytecount;
/* strip leading blanks */
while (s != end && *s == ' ')
++s;
/* compress internal blanks and strip trailing blanks */
while (s != end && *s) {
if (*s++ != ' ' || (s != end && *s && *s != ' '))
*p++ = *(s-1);
}
/* wipe out trailing garbage */
while (p != end)
*p++ = '\0';
}
int scsi_SG_IO(int device, unsigned char *cdb, int cdb_len, unsigned char *buffer, int buffer_len, unsigned char *sense, unsigned char sense_len, int dxfer_direction) {
struct sg_io_hdr io_hdr;
memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
io_hdr.interface_id = 'S';
io_hdr.cmdp = cdb;
io_hdr.cmd_len = cdb_len;
io_hdr.dxfer_len = buffer_len;
io_hdr.dxferp = buffer;
io_hdr.mx_sb_len = sense_len;
io_hdr.sbp = sense;
io_hdr.dxfer_direction = dxfer_direction;
io_hdr.timeout = 3000; /* 3 seconds should be ample */
return ioctl(device, SG_IO, &io_hdr);
}
int scsi_SEND_COMMAND(int device, unsigned char *cdb, int cdb_len, unsigned char *buffer, int buffer_len, int dxfer_direction)
{
unsigned char buf[2048];
unsigned int inbufsize, outbufsize, ret;
switch(dxfer_direction) {
case SG_DXFER_FROM_DEV:
inbufsize = 0;
outbufsize = buffer_len;
break;
case SG_DXFER_TO_DEV:
inbufsize = buffer_len;
outbufsize = 0;
break;
default:
inbufsize = 0;
outbufsize = 0;
break;
}
memcpy(buf, &inbufsize , sizeof(inbufsize));
memcpy(buf + sizeof(inbufsize), &outbufsize , sizeof(outbufsize));
memcpy(buf + sizeof(inbufsize) + sizeof(outbufsize), cdb, cdb_len);
memcpy(buf + sizeof(inbufsize) + sizeof(outbufsize) + cdb_len, buffer, buffer_len);
ret = ioctl(device, SCSI_IOCTL_SEND_COMMAND, buf);
memcpy(buffer, buf + sizeof(inbufsize) + sizeof(outbufsize), buffer_len);
return ret;
}
int scsi_command(int device, unsigned char *cdb, int cdb_len, unsigned char *buffer, int buffer_len, int dxfer_direction)
{
static int SG_IO_supported = -1;
int ret;
if (SG_IO_supported == 1)
return scsi_SG_IO(device, cdb, cdb_len, buffer, buffer_len, NULL, 0, dxfer_direction);
else if (SG_IO_supported == 0)
return scsi_SEND_COMMAND(device, cdb, cdb_len, buffer, buffer_len, dxfer_direction);
else {
ret = scsi_SG_IO(device, cdb, cdb_len, buffer, buffer_len, NULL, 0, dxfer_direction);
if (ret == 0) {
SG_IO_supported = 1;
return ret;
} else {
SG_IO_supported = 0;
return scsi_SEND_COMMAND(device, cdb, cdb_len, buffer, buffer_len, dxfer_direction);
}
}
}
int scsi_inquiry(int device, unsigned char *buffer)
{
unsigned char cdb[6];
memset(cdb, 0, sizeof(cdb));
cdb[0] = INQUIRY;
cdb[4] = 36; /* should be 36 for unsafe devices (like USB mass storage stuff)
* otherwise they can lock up! SPC sections 7.4 and 8.6 */
if (scsi_command(device, cdb, sizeof(cdb), buffer, cdb[4], SG_DXFER_FROM_DEV) != 0)
return 1;
else {
scsi_fixstring(buffer + 8, 24);
return 0;
}
}
int scsi_modesense(int device, unsigned char pagenum, unsigned char *buffer, int buffer_len) {
unsigned char cdb[6];
int ret;
memset(cdb, 0, sizeof(cdb));
cdb[0] = MODE_SENSE;
cdb[2] = pagenum;
cdb[4] = 0xff;
ret = scsi_command(device, cdb, sizeof(cdb), buffer, buffer_len, SG_DXFER_FROM_DEV);
if (ret == 0) {
if ((buffer[3] + 5) > buffer[0]) /* response length too short */
return -1;
}
return ret;
}
int scsi_modeselect(int device, char *buffer) {
unsigned char cdb[6];
memset(cdb, 0, sizeof(cdb));
cdb[0] = MODE_SELECT;
cdb[1] = 0x11;
cdb[4] = buffer[0] + 1;
memset(buffer, 0, 12);
buffer[3] = 0x08;
buffer[10] = 0x02;
buffer[12] &= 0x3f;
return scsi_command(device, cdb, sizeof(cdb), buffer, cdb[4], SG_DXFER_TO_DEV);
}
int scsi_logsense(int device, int pagenum, unsigned char *buffer, int buffer_len) {
unsigned char cdb[10];
memset(cdb, 0, sizeof(cdb));
cdb[0] = LOG_SENSE;
cdb[2] = 0x40 | pagenum;
cdb[7] = 0x04;
return scsi_command(device, cdb, sizeof(cdb), buffer, buffer_len, SG_DXFER_FROM_DEV);
}
int scsi_smartsupport(int device) {
unsigned char buf[255];
if (scsi_modesense (device, EXCEPTIONS_CONTROL_PAGE, buf, sizeof(buf)) != 0)
return 0;
else
return (buf[14] & 0x08) == 0;
}
int scsi_smartDEXCPTdisable(int device) {
unsigned char buf[255];
if (scsi_modesense (device, EXCEPTIONS_CONTROL_PAGE, buf, sizeof(buf)) != 0)
return 1;
if (buf[14] & 0x08) {
buf[14] &= 0xf7;
buf[15] = 0x04;
return scsi_modeselect (device, buf);
}
else
return 0;
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright (C) 2002 Emmanuel VARAGNAT <hddtemp@guzu.net>
*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef SCSICMDS_H_
#define SCSICMDS_H_
#define SUPPORT_LOG_PAGES 0x00
#define TEMPERATURE_PAGE 0x0d
#define EXCEPTIONS_CONTROL_PAGE 0x1c
#define LOGPAGEHDRSIZE 4
int scsi_SG_IO(int device, unsigned char *cdb, int cdb_len, unsigned char *buffer, int buffer_len, unsigned char *sense, unsigned char sense_len, int dxfer_direction);
int scsi_inquiry(int device, unsigned char *buffer);
int scsi_modesense(int device, unsigned char pagenum, unsigned char *buffer, int buffer_len);
int scsi_modeselect(int device, char *buffer);
int scsi_logsense(int device, int pagenum, unsigned char *buffer, int buffer_len);
int scsi_smartsupport(int device);
int scsi_smartDEXCPTdisable(int device);
#endif

890
src/hpa_dco.c Normal file
View File

@@ -0,0 +1,890 @@
/*
* hpa_dco.c: functions that handle the host protected area (HPA) and
* device configuration overlay (DCO)
*
* Copyright PartialVolume <https://github.com/PartialVolume>.
*
* 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 <stdlib.h>
#include <stdint.h>
#include <stdarg.h>
#include <string.h>
#include <scsi/sg.h>
#include <scsi/scsi_ioctl.h>
#include "nwipe.h"
#include "context.h"
#include "version.h"
#include "method.h"
#include "logging.h"
#include "options.h"
#include "hpa_dco.h"
#include "miscellaneous.h"
/* This function makes use of both the hdparm program to determine HPA/DCO status and we also access
* the device configuration overlay identify data structure via the sg driver with ioctl calls.
* I would prefer to write these functions without any reliance on hdparm however for the time being
* we will utilize both methods. However, I don't like doing it like this as a change in formatted
* output of hdparm could potentially break HPA/DCO detection requiring a fix. Time permitting I may
* come back to this and fully implement it without any reliance on hdparm.
*/
int hpa_dco_status( nwipe_context_t* ptr )
{
nwipe_context_t* c;
c = ptr;
int r; // A result buffer.
int set_return_value;
int exit_status;
int hpa_line_found;
int dco_line_found;
FILE* fp;
char path_hdparm_cmd10_get_hpa[] = "hdparm --verbose -N";
char path_hdparm_cmd20_get_hpa[] = "/sbin/hdparm --verbose -N";
char path_hdparm_cmd30_get_hpa[] = "/usr/bin/hdparm --verbose -N";
char path_hdparm_cmd31_get_hpa[] = "/usr/sbin/hdparm --verbose -N";
char path_hdparm_cmd40_get_dco[] = "hdparm --verbose --dco-identify";
char path_hdparm_cmd50_get_dco[] = "/sbin/hdparm --verbose --dco-identify";
char path_hdparm_cmd60_get_dco[] = "/usr/bin/hdparm --verbose --dco-identify";
char path_hdparm_cmd61_get_dco[] = "/usr/sbin/hdparm --verbose --dco-identify";
char pipe_std_err[] = "2>&1";
char result[512];
u64 nwipe_dco_real_max_sectors;
char* p;
/* Use the longest of the 'path_hdparm_cmd.....' strings above to
*determine size in the strings below
*/
char hdparm_cmd_get_hpa[sizeof( path_hdparm_cmd30_get_hpa ) + sizeof( c->device_name ) + sizeof( pipe_std_err )];
char hdparm_cmd_get_dco[sizeof( path_hdparm_cmd60_get_dco ) + sizeof( c->device_name ) + sizeof( pipe_std_err )];
/* Initialise return value */
set_return_value = 0;
/* Construct the command including path to the binary if required, I do it like this to cope
* with distros that don't setup their paths in a standard way or maybe don't even define a
* path. By doing this we avoid the 'No such file or directory' message you would otherwise
* get on some distros. -> debian SID
*/
if( system( "which hdparm > /dev/null 2>&1" ) )
{
if( system( "which /sbin/hdparm > /dev/null 2>&1" ) )
{
if( system( "which /usr/bin/hdparm > /dev/null 2>&1" ) )
{
if( system( "which /usr/sbin/hdparm > /dev/null 2>&1" ) )
{
nwipe_log( NWIPE_LOG_WARNING, "hdparm command not found." );
nwipe_log( NWIPE_LOG_WARNING,
"Required by nwipe for HPA/DCO detection & correction and ATA secure erase." );
nwipe_log( NWIPE_LOG_WARNING, "** Please install hdparm **\n" );
cleanup();
exit( 1 );
}
else
{
snprintf( hdparm_cmd_get_hpa,
sizeof( hdparm_cmd_get_hpa ),
"%s %s %s\n",
path_hdparm_cmd31_get_hpa,
c->device_name,
pipe_std_err );
snprintf( hdparm_cmd_get_dco,
sizeof( hdparm_cmd_get_dco ),
"%s %s %s\n",
path_hdparm_cmd61_get_dco,
c->device_name,
pipe_std_err );
}
}
else
{
snprintf( hdparm_cmd_get_hpa,
sizeof( hdparm_cmd_get_hpa ),
"%s %s %s\n",
path_hdparm_cmd30_get_hpa,
c->device_name,
pipe_std_err );
snprintf( hdparm_cmd_get_dco,
sizeof( hdparm_cmd_get_dco ),
"%s %s %s\n",
path_hdparm_cmd60_get_dco,
c->device_name,
pipe_std_err );
}
}
else
{
snprintf( hdparm_cmd_get_hpa,
sizeof( hdparm_cmd_get_hpa ),
"%s %s %s\n",
path_hdparm_cmd20_get_hpa,
c->device_name,
pipe_std_err );
snprintf( hdparm_cmd_get_dco,
sizeof( hdparm_cmd_get_dco ),
"%s %s %s\n",
path_hdparm_cmd50_get_dco,
c->device_name,
pipe_std_err );
}
}
else
{
snprintf( hdparm_cmd_get_hpa,
sizeof( hdparm_cmd_get_hpa ),
"%s %s %s\n",
path_hdparm_cmd10_get_hpa,
c->device_name,
pipe_std_err );
snprintf( hdparm_cmd_get_dco,
sizeof( hdparm_cmd_get_dco ),
"%s %s %s\n",
path_hdparm_cmd40_get_dco,
c->device_name,
pipe_std_err );
}
/* Initialise the results buffer, so we don't some how inadvertently process a past result */
memset( result, 0, sizeof( result ) );
if( hdparm_cmd_get_hpa[0] != 0 )
{
fp = popen( hdparm_cmd_get_hpa, "r" );
if( fp == NULL )
{
nwipe_log( NWIPE_LOG_WARNING, "hpa_dco_status: Failed to create stream to %s", hdparm_cmd_get_hpa );
set_return_value = 1;
}
if( fp != NULL )
{
hpa_line_found = 0; //* init */
/* Read the output a line at a time - output it. */
while( fgets( result, sizeof( result ) - 1, fp ) != NULL )
{
if( nwipe_options.verbose )
{
nwipe_log( NWIPE_LOG_DEBUG, "%s \n%s", hdparm_cmd_get_hpa, result );
}
/* Change the output of hdparm to lower case and search using lower case strings, to try
* to avoid minor changes in case in hdparm's output from breaking HPA/DCO detection
*/
strlower( result ); // convert the result to lower case
/* Scan the hdparm results for HPA is disabled
*/
if( strstr( result, "sg_io: bad/missing sense data" ) != 0 )
{
c->HPA_status = HPA_UNKNOWN;
nwipe_log( NWIPE_LOG_ERROR, "SG_IO bad/missing sense data %s", hdparm_cmd_get_hpa );
break;
}
else
{
if( strstr( result, "hpa is disabled" ) != 0 )
{
c->HPA_status = HPA_DISABLED;
nwipe_log( NWIPE_LOG_DEBUG,
"hdparm says the host protected area is disabled on %s but this information may or "
"may not be correct, as occurs when you get a SG_IO error and 0/1 sectors and it "
"says HPA is enabled. Further checks are conducted below..",
c->device_name );
hpa_line_found = 1;
break;
}
else
{
if( strstr( result, "hpa is enabled" ) != 0 )
{
c->HPA_status = HPA_ENABLED;
nwipe_log( NWIPE_LOG_DEBUG,
"hdparm says the host protected area is enabled on %s but this information may "
"or may not be correct, as occurs when you get a SG_IO error and 0/1 sectors "
"and it says HPA is enabled. Further checks are conducted below..",
c->device_name );
hpa_line_found = 1;
break;
}
else
{
if( strstr( result, "accessible max address disabled" ) != 0 )
{
c->HPA_status = HPA_DISABLED;
nwipe_log( NWIPE_LOG_DEBUG,
"hdparm says the accessible max address disabled on %s"
"this means that there are no hidden sectors, "
"",
c->device_name );
hpa_line_found = 1;
break;
}
else
{
if( strstr( result, "accessible max address enabled" ) != 0 )
{
c->HPA_status = HPA_ENABLED;
nwipe_log( NWIPE_LOG_DEBUG,
"hdparm says the accessible max address enabled on %s"
"this means that there are hidden sectors",
c->device_name );
hpa_line_found = 1;
break;
}
else
{
if( strstr( result, "invalid" ) != 0 )
{
nwipe_log(
NWIPE_LOG_WARNING,
"hdparm reports invalid output, sector information may be invalid, buggy "
"drive firmware on %s?",
c->device_name );
// We'll assume the HPA values are in the string as we may be able to extract
// something meaningful
hpa_line_found = 1;
break;
}
}
}
}
}
}
}
/* if the HPA line was found then process the line,
* extracting the 'hpa set' and 'hpa real' values.
*/
if( hpa_line_found == 1 )
{
/* Extract the 'HPA set' value, the first value in the line and convert
* to binary and save in context */
nwipe_log( NWIPE_LOG_INFO, "HPA: %s on %s", result, c->device_name );
c->HPA_reported_set = str_ascii_number_to_ll( result );
/* Check whether the number was too large or no number found & log */
if( c->HPA_reported_set == -1 )
{
nwipe_log( NWIPE_LOG_INFO, "HPA_set_value: HPA set value too large on %s", c->device_name );
c->HPA_reported_set = 0;
}
else
{
if( c->HPA_reported_set == -2 )
{
nwipe_log( NWIPE_LOG_INFO, "HPA_set_value: No HPA set value found %s", c->device_name );
c->HPA_reported_set = 0;
}
}
/* Extract the 'HPA real' value, the second value in the line and convert
* to binary and save in context, this is a little more difficult as sometimes
* a odd value is returned so instead of nnnnn/nnnnn you get nnnnnn/1(nnnnnn).
* So first we scan for a open bracket '(' then if there is no '(' we then start the
* search immediately after the '/'.
*/
if( ( p = strstr( result, "(" ) ) )
{
c->HPA_reported_real = str_ascii_number_to_ll( p + 1 );
}
else
{
if( ( p = strstr( result, "/" ) ) )
{
c->HPA_reported_real = str_ascii_number_to_ll( p + 1 );
}
}
/* Check whether the number was too large or no number found & log */
if( c->HPA_reported_real == -1 )
{
nwipe_log( NWIPE_LOG_INFO, "HPA_set_value: HPA real value too large on %s", c->device_name );
c->HPA_reported_real = 0;
}
else
{
if( c->HPA_reported_real == -2 )
{
nwipe_log( NWIPE_LOG_INFO, "HPA_set_value: No HPA real value found %s", c->device_name );
c->HPA_reported_real = 0;
}
}
nwipe_log( NWIPE_LOG_INFO,
"HPA values %lli / %lli on %s",
c->HPA_reported_set,
c->HPA_reported_real,
c->device_name );
}
else
{
c->HPA_status = HPA_UNKNOWN;
nwipe_log( NWIPE_LOG_WARNING,
"[UNKNOWN] We can't find the HPA line, has hdparm ouput unknown/changed? %s",
c->device_name );
}
/* close */
r = pclose( fp );
if( r > 0 )
{
exit_status = WEXITSTATUS( r );
if( nwipe_options.verbose )
{
nwipe_log( NWIPE_LOG_WARNING,
"hpa_dco_status(): hdparm failed, \"%s\" exit status = %u",
hdparm_cmd_get_hpa,
exit_status );
}
if( exit_status == 127 )
{
nwipe_log( NWIPE_LOG_WARNING, "Command not found. Installing hdparm is mandatory !" );
set_return_value = 2;
if( nwipe_options.nousb )
{
return set_return_value;
}
}
}
}
}
/* Initialise the results buffer again, so we don't
* some how inadvertently process a past result */
memset( result, 0, sizeof( result ) );
/* -----------------------------------------------
* Run the dco identify command and determine the
* real max sectors, store it in the drive context
* for comparison against the hpa reported drive
* size values.
*/
dco_line_found = 0;
if( hdparm_cmd_get_dco[0] != 0 )
{
fp = popen( hdparm_cmd_get_dco, "r" );
if( fp == NULL )
{
nwipe_log( NWIPE_LOG_WARNING, "hpa_dco_status: Failed to create stream to %s", hdparm_cmd_get_dco );
set_return_value = 1;
}
if( fp != NULL )
{
/* Read the output a line at a time - output it. */
while( fgets( result, sizeof( result ) - 1, fp ) != NULL )
{
/* Change the output of hdparm to lower case and search using lower case strings, to try
* to avoid minor changes in case in hdparm's output from breaking HPA/DCO detection */
strlower( result );
if( nwipe_options.verbose )
{
nwipe_log( NWIPE_LOG_DEBUG, "%s \n%s", hdparm_cmd_get_dco, result );
}
if( strstr( result, "real max sectors" ) != 0 )
{
/* extract the real max sectors, convert to binary and store in drive context */
dco_line_found = 1;
break;
}
}
/* DCO line found, now process it */
if( dco_line_found == 1 )
{
c->DCO_reported_real_max_sectors = str_ascii_number_to_ll( result );
nwipe_log( NWIPE_LOG_INFO,
"hdparm:DCO Real max sectors reported as %lli on %s",
c->DCO_reported_real_max_sectors,
c->device_name );
/* Validate the real max sectors to detect extreme or impossible
* values, so the size must be greater than zero but less than
* 200TB (429496729600 sectors). As its 2023 and the largest drive
* available is 20TB I wonder if somebody in the future will be looking
* at this and thinking, yep we need to increase that value... and I'm
* wondering what year that will be. This validation is necessary all
* because of a bug in hdparm v9.60 (and maybe other versions) which
* produced wildly inaccurate values, often negative.
*/
if( c->DCO_reported_real_max_sectors > 0 && c->DCO_reported_real_max_sectors < 429496729600 )
{
nwipe_log( NWIPE_LOG_INFO,
"NWipe: DCO Real max sectors reported as %lli on %s",
c->DCO_reported_real_max_sectors,
c->device_name );
}
else
{
/* Call nwipe's own low level function to retrieve the drive configuration
* overlay and retrieve the real max sectors. We may remove reliance on hdparm
* if nwipes own low level drive access code works well.
*/
c->DCO_reported_real_max_sectors = nwipe_read_dco_real_max_sectors( c->device_name );
/* Check our real max sectors function is returning sensible data too */
if( c->DCO_reported_real_max_sectors > 0 && c->DCO_reported_real_max_sectors < 429496729600 )
{
nwipe_log( NWIPE_LOG_INFO,
"NWipe: DCO Real max sectors reported as %lli on %s",
c->DCO_reported_real_max_sectors,
c->device_name );
}
else
{
c->DCO_reported_real_max_sectors = 0;
nwipe_log( NWIPE_LOG_INFO, "DCO Real max sectors not found" );
}
}
}
else
{
c->DCO_reported_real_max_sectors = 0;
nwipe_log( NWIPE_LOG_INFO, "DCO Real max sectors not found" );
}
nwipe_log(
NWIPE_LOG_INFO,
"libata: apparent max sectors reported as %lli with sector size as %i/%i (logical/physical) on %s",
c->device_size_in_sectors,
c->device_sector_size, // logical
c->device_phys_sector_size, // physical
c->device_name );
/* close */
r = pclose( fp );
if( r > 0 )
{
exit_status = WEXITSTATUS( r );
if( nwipe_options.verbose )
{
nwipe_log( NWIPE_LOG_WARNING,
"hpa_dco_status(): hdparm failed, \"%s\" exit status = %u",
hdparm_cmd_get_dco,
exit_status );
}
if( exit_status == 127 )
{
nwipe_log( NWIPE_LOG_WARNING, "Command not found. Installing hdparm is mandatory !" );
set_return_value = 2;
if( nwipe_options.nousb )
{
return set_return_value;
}
}
}
}
}
/* Compare the results of hdparm -N (HPA set / HPA real)
* and hdparm --dco-identidy (real max sectors). All three
* values may be different or perhaps 'HPA set' and 'HPA real' are
* different and 'HPA real' matches 'real max sectors'.
*
* A perfect HPA disabled result would be where all three
* values are the same. It can then be considered that the
* HPA is disabled.
*
* If 'HPA set' and 'HPA real' are different then it
* can be considered that HPA is enabled, assuming 'HPA set'
* and 'HPA real' are not 0/1 which occurs when a SG_IO error
* occurs. That also is checked for as it often indicates a
* poor USB device that does not have ATA pass through support.
*
* However we also need to consider that more recent drives
* no longer support HPA/DCO such as the Seagate ST10000NM0016,
* ST4000NM0033 and ST1000DM003. If you try to issue those drives
* with the ATA command code 0xB1 (device configuration overlay)
* you will get a generic illegal request in the returned sense data.
*
* One other thing to note, we use HPA enabled/disabled to mean
* hidden area detected or not detected, this could be caused by
* either the dco-setmax being issued or Np, either way an area
* of the disc can be hidden. From the user interface we just call
* it a HPA/DCO hidden area detected (or not) which is more
* meaningful than just saying HDA enabled or disabled and a user
* not familiar with the term HPA or DCO not understanding why a
* HDA being detected could be significant.
*/
/* Determine, based on the values of 'HPA set', 'HPA real and
* 'real max sectors' whether we set the HPA flag as HPA_DISABLED,
* HPA_ENABLED, HPA_UNKNOWN or HPA_NOT_APPLICABLE. The HPA flag
* will be displayed in the GUI and on the certificate and is
* used to determine whether to reset the HPA.
*
*/
/* WARNING temp assignments WARNING
* s=28,r=28,rm=0
*
*/
#if 0
c->HPA_reported_set = 10;
c->HPA_reported_real = 28;
c->DCO_reported_real_max_sectors = 0;
c->HPA_reported_set = 28;
c->HPA_reported_real = 28;
c->DCO_reported_real_max_sectors = 0;
c->HPA_reported_set = 1000;
c->HPA_reported_real = 2048;
c->DCO_reported_real_max_sectors = 2048;
#endif
/* If any of the HPA or DCO values are larger than the apparent size then HPA is enabled. */
if( /*c->HPA_reported_set > c->device_size_in_sectors || */ c->HPA_reported_real > c->device_size_in_512byte_sectors
|| c->DCO_reported_real_max_sectors > c->device_size_in_512byte_sectors )
{
c->HPA_status = HPA_ENABLED;
nwipe_log( NWIPE_LOG_WARNING, " *********************************" );
nwipe_log( NWIPE_LOG_WARNING, " *** HIDDEN SECTORS DETECTED ! *** on %s", c->device_name );
nwipe_log( NWIPE_LOG_WARNING, " *********************************" );
}
else
{
/* This occurs when a SG_IO error occurs with USB devices that don't support ATA pass
* through */
if( c->HPA_reported_set == 0 && c->HPA_reported_real == 0 && c->DCO_reported_real_max_sectors <= 1 )
{
if( c->device_bus == NWIPE_DEVICE_SAS )
{
/* SAS SCSI doesn't appear to support HPA/DCO commands */
c->HPA_status = HPA_NOT_APPLICABLE;
nwipe_log( NWIPE_LOG_INFO, "No hidden sectors on %s", c->device_name );
}
else
{
c->HPA_status = HPA_UNKNOWN;
if( c->device_bus == NWIPE_DEVICE_USB )
{
nwipe_log( NWIPE_LOG_WARNING,
"HIDDEN SECTORS INDETERMINATE! on %s, Some USB adapters & memory sticks don't support "
"ATA pass through",
c->device_name );
}
}
}
else
{
c->HPA_status = HPA_DISABLED;
nwipe_log( NWIPE_LOG_INFO, "No hidden sectors on %s", c->device_name );
}
}
c->DCO_reported_real_max_size = c->DCO_reported_real_max_sectors * c->device_sector_size;
nwipe_dco_real_max_sectors = nwipe_read_dco_real_max_sectors( c->device_name );
/* Analyse all the variations to produce the final real max bytes which takes into
* account drives that don't support DCO or HPA. This result is used in the PDF
* creation functions.
*/
if( c->device_type == NWIPE_DEVICE_NVME || c->device_type == NWIPE_DEVICE_VIRT
|| c->HPA_status == HPA_NOT_APPLICABLE )
{
c->Calculated_real_max_size_in_bytes = c->device_size;
}
else
{
/* If the DCO is reporting a real max sectors > the apparent size
* as reported by libata then that is what we will use as the real disc size
*/
if( c->DCO_reported_real_max_size > c->device_size_in_512byte_sectors )
{
c->Calculated_real_max_size_in_bytes = c->DCO_reported_real_max_sectors * c->device_sector_size;
}
else
{
/* If HPA is enabled and DCO real max sectors did not exist, then we have to assume - c->HPA_reported_real
* is the value we need, however if that is zero, then c->HPA_reported_set and if that is zero then
* c->device_size as reported by libata
*/
if( c->HPA_reported_real > c->device_size_in_512byte_sectors )
{
c->Calculated_real_max_size_in_bytes = c->HPA_reported_real * c->device_sector_size;
}
else
{
if( c->HPA_reported_set > c->device_size_in_512byte_sectors )
{
c->Calculated_real_max_size_in_bytes = c->HPA_reported_set * c->device_sector_size;
}
else
{
c->Calculated_real_max_size_in_bytes = c->device_size;
}
}
}
}
/* -------------------------------------------------------------------
* create two variables for later use by the PDF creation function
* based on real max sectors and calculated real max size in bytes.
*
* DCO_reported_real_max_size = real max sectors * sector size = bytes
* DCO_reported_real_max_size_text = human readable string, i.e 1TB etc.
*/
Determine_C_B_nomenclature(
c->DCO_reported_real_max_size, c->DCO_reported_real_max_size_text, NWIPE_DEVICE_SIZE_TXT_LENGTH );
Determine_C_B_nomenclature(
c->Calculated_real_max_size_in_bytes, c->Calculated_real_max_size_in_bytes_text, NWIPE_DEVICE_SIZE_TXT_LENGTH );
/* ----------------------------------------------------------------------------------
* Determine the size of the HPA if it's enabled and store the results in the context
*/
if( c->HPA_status == HPA_ENABLED )
{
if( c->Calculated_real_max_size_in_bytes != c->device_size )
{
c->HPA_sectors =
( (u64) ( c->Calculated_real_max_size_in_bytes - c->device_size ) / c->device_sector_size );
}
else
{
c->HPA_sectors = 0;
}
/* Convert the size to a human readable format and save in context */
Determine_C_B_nomenclature( c->HPA_sectors, c->HPA_size_text, NWIPE_DEVICE_SIZE_TXT_LENGTH );
}
else
{
/* HPA not enabled so initialise these values */
c->HPA_sectors = 0;
c->HPA_size_text[0] = 0;
}
nwipe_log( NWIPE_LOG_DEBUG,
"c->Calculated_real_max_size_in_bytes=%lli, c->device_size=%lli, c->device_sector_size=%lli, "
"c->DCO_reported_real_max_size=%lli, c->DCO_reported_real_max_sectors=%lli, c->HPA_sectors=%lli, "
"c->HPA_reported_set=%lli, c->HPA_reported_real=%lli, c->device_type=%i, "
"libata:c->device_size_in_sectors=%lli ",
"libata:c->device_size_in_512byte_sectors=%lli ",
c->Calculated_real_max_size_in_bytes,
c->device_size,
c->device_sector_size,
c->DCO_reported_real_max_size,
c->DCO_reported_real_max_sectors,
c->HPA_sectors,
c->HPA_reported_set,
c->HPA_reported_real,
c->device_type,
c->device_size_in_sectors,
c->device_size_in_512byte_sectors );
return set_return_value;
}
u64 nwipe_read_dco_real_max_sectors( char* device )
{
/* This function sends a device configuration overlay identify command 0xB1 (dco-identify)
* to the drive and extracts the real max sectors. The value is incremented by 1 and
* then returned. We rely upon this function to determine real max sectors as there
* is a bug in hdparm 9.60, including possibly earlier or later versions but which is
* fixed in 9.65, that returns a incorrect (negative) value
* for some drives that are possibly over a certain size.
*/
/* TODO Add checks in case of failure, especially with recent drives that may not
* support drive configuration overlay commands.
*/
#define LBA_SIZE 512
#define CMD_LEN 16
#define BLOCK_MAX 65535
#define LBA_MAX ( 1 << 30 )
#define SENSE_BUFFER_SIZE 32
u64 nwipe_real_max_sectors;
/* This command issues command 0xb1 (dco-identify) 15th byte */
unsigned char cmd_blk[CMD_LEN] = { 0x85, 0x08, 0x0e, 0x00, 0xc2, 0, 0x01, 0, 0, 0, 0, 0, 0, 0x40, 0xb1, 0 };
sg_io_hdr_t io_hdr;
unsigned char buffer[LBA_SIZE]; // Received data block
unsigned char sense_buffer[SENSE_BUFFER_SIZE]; // Sense data
/* Zero the data block prior to use */
memset( buffer, 0, LBA_SIZE );
/* three characters represent one byte of sense data, i.e
* two characters and a space "01 AE 67"
*/
char sense_buffer_hex[( SENSE_BUFFER_SIZE * 3 ) + 1];
int i, i2; // index
int fd; // file descripter
if( ( fd = open( device, O_RDWR ) ) < 0 )
{
/* Unable to open device */
return -1;
}
/******************************************
* Initialise the sg header for reading the
* device configuration overlay identify data
*/
memset( &io_hdr, 0, sizeof( sg_io_hdr_t ) );
io_hdr.interface_id = 'S';
io_hdr.cmd_len = sizeof( cmd_blk );
io_hdr.mx_sb_len = sizeof( sense_buffer );
io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
io_hdr.dxfer_len = LBA_SIZE;
io_hdr.dxferp = buffer;
io_hdr.cmdp = cmd_blk;
io_hdr.sbp = sense_buffer;
io_hdr.timeout = 20000;
if( ioctl( fd, SG_IO, &io_hdr ) < 0 )
{
nwipe_log( NWIPE_LOG_ERROR, "IOCTL command failed retrieving DCO" );
i2 = 0;
for( i = 0, i2 = 0; i < SENSE_BUFFER_SIZE; i++, i2 += 3 )
{
/* IOCTL returned an error */
snprintf( &sense_buffer_hex[i2], sizeof( sense_buffer_hex ), "%02x ", sense_buffer[i] );
}
sense_buffer_hex[i2] = 0; // terminate string
nwipe_log( NWIPE_LOG_DEBUG, "Sense buffer from failed DCO identify cmd:%s", sense_buffer_hex );
return -2;
}
/* Close the device */
close( fd );
/***************************************************************
* Extract the real max sectors from the returned 512 byte block.
* Assuming the first word/byte is 0. We extract the bytes & switch
* the endian. Words 3-6(bytes 6-13) contain the max sector address
*/
nwipe_real_max_sectors = (u64) ( (u64) buffer[13] << 56 ) | ( (u64) buffer[12] << 48 ) | ( (u64) buffer[11] << 40 )
| ( (u64) buffer[10] << 32 ) | ( (u64) buffer[9] << 24 ) | ( (u64) buffer[8] << 16 ) | ( (u64) buffer[7] << 8 )
| buffer[6];
/* Don't really understand this but hdparm adds 1 to
* the real max sectors too, counting zero as sector?
* but only increment if it's already greater than zero
*/
if( nwipe_real_max_sectors > 0 )
{
nwipe_real_max_sectors++;
}
nwipe_log(
NWIPE_LOG_INFO, "func:nwipe_read_dco_real_max_sectors(), DCO real max sectors = %lli", nwipe_real_max_sectors );
return nwipe_real_max_sectors;
}
int ascii2binary_array( char* input, unsigned char* output_bin, int bin_size )
{
/* Converts ascii sense data output by hdparm to binary.
* Scans a character string that contains hexadecimal ascii data, ignores spaces
* and extracts and converts the hexadecimal ascii data to binary and places in a array.
* Typically for dco_identify sense data the bin size will be 512 bytes but for error
* sense data this would be 32 bytes.
*/
int idx_in; // Index into ascii input string
int idx_out; // Index into the binary output array
int byte_count; // Counts which 4 bit value we are working on
char upper4bits;
char lower4bits;
byte_count = 0;
idx_in = 0;
idx_out = 0;
while( input[idx_in] != 0 )
{
if( input[idx_in] >= '0' && input[idx_in] <= '9' )
{
if( byte_count == 0 )
{
upper4bits = input[idx_in] - 0x30;
byte_count++;
}
else
{
lower4bits = input[idx_in] - 0x30;
output_bin[idx_out++] = ( upper4bits << 4 ) | ( lower4bits );
byte_count = 0;
if( idx_out >= bin_size )
{
return 0; // output array full.
}
}
}
else
{
if( input[idx_in] >= 'a' && input[idx_in] <= 'f' )
{
if( byte_count == 0 )
{
upper4bits = input[idx_in] - 0x57;
byte_count++;
}
else
{
lower4bits = input[idx_in] - 0x57;
output_bin[idx_out++] = ( upper4bits << 4 ) | ( lower4bits );
byte_count = 0;
if( idx_out >= bin_size )
{
return 0; // output array full.
}
}
}
}
idx_in++; // next byte in the input string
}
return 0;
}

43
src/hpa_dco.h Normal file
View File

@@ -0,0 +1,43 @@
/*.
* hpa_dco.h: The header file for the host protected area (HPA) and
* disk configuration overlay routines
*
* Copyright https://github.com/PartialVolume/shredos.x86_64
*
* 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 HPA_DCO_H_
#define HPA_DCO_H_
#define HPA_DISABLED 0
#define HPA_ENABLED 1
#define HPA_UNKNOWN 2
#define HPA_NOT_APPLICABLE 3
#define HPA_NOT_SUPPORTED_BY_DRIVE 4
int hpa_dco_status( nwipe_context_t* );
u64 nwipe_read_dco_real_max_sectors( char* );
typedef struct nwipe_sense_dco_identify_t_t_
{
/* This struct contains some of the decoded fields from the sense data after a
* ATA 0xB1 device configuration overlay command has been issued. We mainly
* use it to decode the real max sectors
*/
u64 dco_real_max_sectors;
} nwipe_sense_dco_identify_t_t_;
#endif /* HPA_DCO_H_ */

View File

@@ -25,8 +25,7 @@ MODIFIED:
*(r++) = b = ind(mm,y>>RANDSIZL) + x; \
}
void isaac(ctx)
randctx *ctx;
void isaac(randctx *ctx)
{
register ub4 a,b,x,y,*m,*mm,*m2,*r,*mend;
mm=ctx->randmem;
@@ -64,9 +63,7 @@ randctx *ctx;
}
/* if (flag==TRUE), then use the contents of randrsl[] to initialize mm[]. */
void randinit(ctx, flag)
randctx *ctx;
word flag;
void randinit(randctx *ctx, word flag)
{
word i;
ub4 a,b,c,d,e,f,g,h;

View File

@@ -32,10 +32,9 @@ typedef struct randctx randctx;
If (flag==TRUE), then use the contents of randrsl[0..RANDSIZ-1] as the seed.
------------------------------------------------------------------------------
*/
void randinit(/*_ randctx *r, word flag _*/);
void isaac(/*_ randctx *r _*/);
void randinit(randctx *r, word flag);
void isaac(randctx *r);
/*
------------------------------------------------------------------------------

View File

@@ -36,6 +36,8 @@
#include "prng.h"
#include "options.h"
#include "logging.h"
#include "create_pdf.h"
#include "miscellaneous.h"
/* Global array to hold log values to print when logging to STDOUT */
char** log_lines;
@@ -84,6 +86,19 @@ void nwipe_log( nwipe_log_t level, const char* format, ... )
/* initialise characters written */
chars_written = 0;
/* Only log messages with the debug label if the command line --verbose
* options has been specified, otherwise just return
*/
if( level == NWIPE_LOG_DEBUG && nwipe_options.verbose == 0 )
{
r = pthread_mutex_unlock( &mutex1 );
if( r != 0 )
{
fprintf( stderr, "nwipe_log: pthread_mutex_unlock failed. Code %i \n", r );
}
return;
}
/* Print the date. The rc script uses the same format. */
if( level != NWIPE_LOG_NOTIMESTAMP )
{
@@ -386,7 +401,6 @@ void nwipe_log( nwipe_log_t level, const char* format, ... )
}
user_abort = 1;
terminate_signal = 1;
return;
}
}
@@ -405,9 +419,6 @@ void nwipe_perror( int nwipe_errno, const char* f, const char* s )
{
/**
* Wrapper for perror().
*
* We may wish to tweak or squelch this later.
*
*/
nwipe_log( NWIPE_LOG_ERROR, "%s: %s: %s", f, s, strerror( nwipe_errno ) );
@@ -496,6 +507,11 @@ void nwipe_log_OSinfo()
return;
}
/* Globally accessable dmidecode host identifiable data. */
char dmidecode_system_serial_number[DMIDECODE_RESULT_LENGTH];
char dmidecode_system_uuid[DMIDECODE_RESULT_LENGTH];
char dmidecode_baseboard_serial_number[DMIDECODE_RESULT_LENGTH];
int nwipe_log_sysinfo()
{
FILE* fp;
@@ -548,9 +564,16 @@ int nwipe_log_sysinfo()
char cmd[sizeof( dmidecode_keywords ) + sizeof( dmidecode_command2 )];
unsigned int keywords_idx;
unsigned int i;
keywords_idx = 0;
/* Initialise every character in the string with zero */
for( i = 0; i < DMIDECODE_RESULT_LENGTH; i++ )
{
dmidecode_system_serial_number[i] = 0;
}
p_dmidecode_command = 0;
if( system( "which dmidecode > /dev/null 2>&1" ) )
@@ -603,16 +626,40 @@ int nwipe_log_sysinfo()
if( *( &dmidecode_keywords[keywords_idx][1][0] ) == '0' )
{
nwipe_log(
NWIPE_LOG_NOTICE, "%s = %s", &dmidecode_keywords[keywords_idx][0][0], "XXXXXXXXXXXXXXX" );
NWIPE_LOG_INFO, "%s = %s", &dmidecode_keywords[keywords_idx][0][0], "XXXXXXXXXXXXXXX" );
}
else
{
nwipe_log( NWIPE_LOG_NOTICE, "%s = %s", &dmidecode_keywords[keywords_idx][0][0], path );
nwipe_log( NWIPE_LOG_INFO, "%s = %s", &dmidecode_keywords[keywords_idx][0][0], path );
/* if system-serial-number copy result to extern string */
if( keywords_idx == 5 )
{
strncpy( dmidecode_system_serial_number, path, DMIDECODE_RESULT_LENGTH );
dmidecode_system_serial_number[DMIDECODE_RESULT_LENGTH - 1] = 0;
}
if( keywords_idx == 6 )
{
strncpy( dmidecode_system_uuid, path, DMIDECODE_RESULT_LENGTH );
dmidecode_system_uuid[DMIDECODE_RESULT_LENGTH - 1] = 0;
}
}
}
else
{
nwipe_log( NWIPE_LOG_NOTICE, "%s = %s", &dmidecode_keywords[keywords_idx][0][0], path );
nwipe_log( NWIPE_LOG_INFO, "%s = %s", &dmidecode_keywords[keywords_idx][0][0], path );
/* if system-serial-number copy result to extern string */
if( keywords_idx == 5 )
{
strncpy( dmidecode_system_serial_number, path, DMIDECODE_RESULT_LENGTH );
dmidecode_system_serial_number[DMIDECODE_RESULT_LENGTH - 1] = 0;
}
if( keywords_idx == 6 )
{
strncpy( dmidecode_system_uuid, path, DMIDECODE_RESULT_LENGTH );
dmidecode_system_uuid[DMIDECODE_RESULT_LENGTH - 1] = 0;
}
}
}
/* close */
@@ -635,7 +682,12 @@ void nwipe_log_summary( nwipe_context_t** ptr, int nwipe_selected )
{
/* Prints two summary tables, the first is the device pass and verification summary
* and the second is the main summary table detaining the drives, status, throughput,
* model and serial number */
* model and serial number.
*
* This function also calls the create_pdf() function that creates the PDF erasure
* report file. A page report on the success or failure of the erasure operation
*/
int i;
int idx_src;
int idx_dest;
@@ -650,7 +702,7 @@ void nwipe_log_summary( nwipe_context_t** ptr, int nwipe_selected )
// char duration[5];
char duration[314];
char model[18];
char serial_no[20];
char serial_no[NWIPE_SERIALNUMBER_LENGTH + 1];
char exclamation_flag[2];
int hours;
int minutes;
@@ -775,26 +827,32 @@ void nwipe_log_summary( nwipe_context_t** ptr, int nwipe_selected )
strncpy( status, "-FAILED-", 8 );
status[8] = 0;
strcpy( c[i]->wipe_status_txt, "FAILED" ); // copy to context for use by certificate
}
else
{
if( c[i]->wipe_status == 0 )
if( c[i]->wipe_status == 0 /* && user_abort != 1 */ )
{
strncpy( exclamation_flag, " ", 1 );
exclamation_flag[1] = 0;
strncpy( status, " Erased ", 8 );
status[8] = 0;
strcpy( c[i]->wipe_status_txt, "ERASED" ); // copy to context for use by certificate
}
else
{
if( user_abort == 1 )
if( c[i]->wipe_status == 1 && user_abort == 1 )
{
strncpy( exclamation_flag, "!", 1 );
exclamation_flag[1] = 0;
strncpy( status, "UABORTED", 8 );
status[8] = 0;
strcpy( c[i]->wipe_status_txt, "ABORTED" ); // copy to context for use by certificate
}
else
{
@@ -804,6 +862,8 @@ void nwipe_log_summary( nwipe_context_t** ptr, int nwipe_selected )
strncpy( status, "INSANITY", 8 );
status[8] = 0;
strcpy( c[i]->wipe_status_txt, "INSANITY" ); // copy to context for use by certificate
}
}
}
@@ -811,6 +871,9 @@ void nwipe_log_summary( nwipe_context_t** ptr, int nwipe_selected )
/* Determine the size of throughput so that the correct nomenclature can be used */
Determine_C_B_nomenclature( c[i]->throughput, throughput, 13 );
/* write the duration string to the drive context for later use by create_pdf() */
snprintf( c[i]->throughput_txt, sizeof( c[i]->throughput_txt ), "%s", throughput );
/* Add this devices throughput to the total throughput */
total_throughput += c[i]->throughput;
@@ -825,8 +888,13 @@ void nwipe_log_summary( nwipe_context_t** ptr, int nwipe_selected )
{
if( c[i]->start_time != 0 && c[i]->end_time == 0 )
{
/* For a summary in the event of a system shutdown */
/* For a summary in the event of a system shutdown, user abort */
c[i]->duration = difftime( t, c[i]->start_time );
/* If end_time is zero, which may occur if the wipe is aborted, then set
* end_time to current time. Important to do as endtime is used by
* the PDF report function */
c[i]->end_time = time( &t );
}
}
@@ -835,12 +903,16 @@ void nwipe_log_summary( nwipe_context_t** ptr, int nwipe_selected )
/* Convert binary seconds into three binary variables, hours, minutes and seconds */
convert_seconds_to_hours_minutes_seconds( total_duration_seconds, &hours, &minutes, &seconds );
/* write the duration string to the drive context for later use by create_pdf() */
snprintf( c[i]->duration_str, sizeof( c[i]->duration_str ), "%02i:%02i:%02i", hours, minutes, seconds );
/* Device Model */
strncpy( model, c[i]->device_model, 17 );
model[17] = 0;
/* Serial No. */
strncpy( serial_no, c[i]->device_serial_no, 20 );
strncpy( serial_no, c[i]->device_serial_no, NWIPE_SERIALNUMBER_LENGTH );
serial_no[NWIPE_SERIALNUMBER_LENGTH] = 0;
model[17] = 0;
nwipe_log( NWIPE_LOG_NOTIMESTAMP,
@@ -854,6 +926,15 @@ void nwipe_log_summary( nwipe_context_t** ptr, int nwipe_selected )
seconds,
model,
serial_no );
/* Create the PDF report/certificate */
if( nwipe_options.PDF_enable == 1 )
// if( strcmp( nwipe_options.PDFreportpath, "noPDF" ) != 0 )
{
/* to have some progress indication. can help if there are many/slow disks */
fprintf( stderr, "." );
create_pdf( c[i] );
}
}
/* Determine the size of throughput so that the correct nomenclature can be used */
@@ -903,105 +984,12 @@ void nwipe_log_summary( nwipe_context_t** ptr, int nwipe_selected )
nwipe_log( NWIPE_LOG_NOTIMESTAMP,
"********************************************************************************" );
nwipe_log( NWIPE_LOG_NOTIMESTAMP, "" );
}
void Determine_C_B_nomenclature( u64 speed, char* result, int result_array_size )
{
/* C_B ? Determine Capacity or Bandwidth nomenclature
*
* A pointer to a result character string with a minimum of 13 characters in length
* should be provided.
*
* Outputs a string of the form xxxTB/s, xxxGB/s, xxxMB/s, xxxKB/s B/s depending on the value of 'speed'
/* Log information regarding where the PDF certificate is saved but log after the summary table so
* this information is only printed once.
*/
/* Initialise the output array */
int idx = 0;
while( idx < result_array_size )
if( strcmp( nwipe_options.PDFreportpath, "noPDF" ) != 0 )
{
result[idx++] = 0;
}
/* Determine the size of throughput so that the correct nomenclature can be used */
if( speed >= INT64_C( 1000000000000 ) )
{
snprintf( result, result_array_size, "%3llu TB", speed / INT64_C( 1000000000000 ) );
}
else if( speed >= INT64_C( 1000000000 ) )
{
snprintf( result, result_array_size, "%3llu GB", speed / INT64_C( 1000000000 ) );
}
else if( speed >= INT64_C( 1000000 ) )
{
snprintf( result, result_array_size, "%3llu MB", speed / INT64_C( 1000000 ) );
}
else if( speed >= INT64_C( 1000 ) )
{
snprintf( result, result_array_size, "%3llu KB", speed / INT64_C( 1000 ) );
}
else
{
snprintf( result, result_array_size, "%3llu B", speed / INT64_C( 1 ) );
}
}
void convert_seconds_to_hours_minutes_seconds( u64 total_seconds, int* hours, int* minutes, int* seconds )
{
/* Convert binary seconds into binary hours, minutes and seconds */
if( total_seconds % 60 )
{
*minutes = total_seconds / 60;
*seconds = total_seconds - ( *minutes * 60 );
}
else
{
*minutes = total_seconds / 60;
*seconds = 0;
}
if( *minutes > 59 )
{
*hours = *minutes / 60;
if( *minutes % 60 )
{
*minutes = *minutes - ( *hours * 60 );
}
else
{
*minutes = 0;
}
}
}
int nwipe_strip_path( char* output, char* input )
{
/* Take the input string, say "/dev/sda" and remove the "/dev/", prefix the result
* with 'length' spaces. So if length=8 and input=/dev/sda, output will
* be " sda", a string 8 characters long right justified with spaces.
*/
int idx_dest;
int idx_src;
idx_dest = 8;
// idx_dest = length;
output[idx_dest--] = 0;
idx_src = strlen( input );
idx_src--;
while( idx_dest >= 0 )
{
/* if the device name contains a / start prefixing spaces */
if( input[idx_src] == '/' )
{
output[idx_dest--] = ' ';
continue;
}
if( idx_src >= 0 )
{
output[idx_dest--] = input[idx_src--];
}
nwipe_log( NWIPE_LOG_NOTIMESTAMP, "Creating PDF report in %s\n", nwipe_options.PDFreportpath );
}
}

View File

@@ -22,17 +22,20 @@
#define LOGGING_H_
/* Maximum size of a log message */
#define MAX_LOG_LINE_CHARS 512
#define MAX_LOG_LINE_CHARS 1024
#define MAX_SIZE_OS_STRING 1024 /* Maximum size of acceptable OS string */
#define MAX_SIZE_OS_STRING 512 /* Maximum size of acceptable OS string */
#define OS_info_Line_offset 31 /* OS_info line offset in log */
#define OS_info_Line_Length 48 /* OS_info line length */
#define DMIDECODE_RESULT_LENGTH 64
typedef enum nwipe_log_t_ {
NWIPE_LOG_NONE = 0,
NWIPE_LOG_DEBUG, // TODO: Very verbose logging.
NWIPE_LOG_INFO, // TODO: Verbose logging.
NWIPE_LOG_NOTICE, // Most logging happens at this level.
NWIPE_LOG_DEBUG, // Output only when --verbose option used on cmd line.
NWIPE_LOG_INFO, // General Info not specifically relevant to the wipe.
NWIPE_LOG_NOTICE, // Most logging happens at this level related to wiping.
NWIPE_LOG_WARNING, // Things that the user should know about.
NWIPE_LOG_ERROR, // Non-fatal errors that result in failure.
NWIPE_LOG_FATAL, // Errors that cause the program to exit.
@@ -40,13 +43,25 @@ typedef enum nwipe_log_t_ {
NWIPE_LOG_NOTIMESTAMP // logs the message without the timestamp
} nwipe_log_t;
/**
* Writes a string to the log. nwipe_log timestamps the string
* @param level the tag to display:
* NWIPE_LOG_NONE Don't display a tag
* NWIPE_LOG_DEBUG, Very verbose logging.
* NWIPE_LOG_INFO, Verbose logging.
* NWIPE_LOG_NOTICE, Most logging happens at this level.
* NWIPE_LOG_WARNING, Things that the user should know about.
* NWIPE_LOG_ERROR, Non-fatal errors that result in failure.
* NWIPE_LOG_FATAL, Errors that cause the program to exit.
* NWIPE_LOG_SANITY, Programming errors.
* NWIPE_LOG_NOTIMESTAMP logs the message without the timestamp
* @param format the string to be logged
*/
void nwipe_log( nwipe_log_t level, const char* format, ... );
void nwipe_perror( int nwipe_errno, const char* f, const char* s );
void nwipe_log_OSinfo();
int nwipe_log_sysinfo();
void nwipe_log_summary( nwipe_context_t**, int ); // This produces the wipe status table on exit
void Determine_C_B_nomenclature( u64, char*, int );
void convert_seconds_to_hours_minutes_seconds( u64, int*, int*, int* );
int nwipe_strip_path( char*, char* );
#endif /* LOGGING_H_ */

View File

@@ -47,6 +47,52 @@
#include "options.h"
#include "pass.h"
#include "logging.h"
#include <errno.h>
#include <unistd.h>
#include <sys/syscall.h> /* SYS_getrandom */
#if defined( __linux__ )
/* On glibc/musl with <sys/random.h> available, it's fine (optional). */
/* #include <sys/random.h> */
#endif
/**
* @brief Fill a buffer with cryptographically secure random bytes using getrandom(2).
*
* This wrapper blocks until the kernel CRNG is initialized, then loops until
* @p len bytes are written (handling short reads and EINTR/EAGAIN).
*
* @param[out] buf Destination buffer.
* @param[in] len Number of bytes to generate.
* @return On success, returns (ssize_t)len.
* On error, returns -errno and leaves errno set.
*/
static ssize_t nwipe_read_entropy( void* buf, size_t len )
{
unsigned char* p = (unsigned char*) buf;
size_t n = len;
while( n > 0 )
{
/* Prefer the raw syscall to avoid libc version pitfalls. */
ssize_t r = syscall( SYS_getrandom, p, n, 0 /* blocking */ );
if( r < 0 )
{
if( errno == EINTR || errno == EAGAIN )
{
continue; /* retry */
}
return -errno;
}
if( r == 0 )
{
/* Extremely unlikely: treat as transient and retry. */
continue;
}
p += r;
n -= (size_t) r;
}
return (ssize_t) len;
}
/*
* Comment Legend
@@ -68,6 +114,8 @@ const char* nwipe_one_label = "Fill With Ones";
const char* nwipe_verify_zero_label = "Verify Zeros (0x00)";
const char* nwipe_verify_one_label = "Verify Ones (0xFF)";
const char* nwipe_is5enh_label = "HMG IS5 Enhanced";
const char* nwipe_bruce7_label = "Bruce Schneier 7-Pass";
const char* nwipe_bmb_label = "BMB21-2019";
const char* nwipe_unknown_label = "Unknown Method (FIXME)";
@@ -118,6 +166,14 @@ const char* nwipe_method_label( void* method )
{
return nwipe_is5enh_label;
}
if( method == &nwipe_bruce7 )
{
return nwipe_bruce7_label;
}
if( method == &nwipe_bmb )
{
return nwipe_bmb_label;
}
/* else */
return nwipe_unknown_label;
@@ -172,7 +228,7 @@ void* nwipe_one( void* ptr )
/* set wipe in progress flag for GUI */
c->wipe_status = 1;
/* setup for a zero-fill. */
/* setup for a one-fill. */
char onefill[1] = { '\xFF' };
nwipe_pattern_t patterns[] = { { 1, &onefill[0] }, // pass 1: 1s
@@ -282,7 +338,7 @@ void* nwipe_dod522022m( void* ptr )
{ 0, NULL } };
/* Load the array with random characters. */
r = read( c->entropy_fd, &dod, sizeof( dod ) );
r = nwipe_read_entropy( &dod, sizeof( dod ) );
/* NOTE: Only the random data in dod[0], dod[3], and dod[4] is actually used. */
@@ -353,7 +409,7 @@ void* nwipe_dodshort( void* ptr )
{ 0, NULL } };
/* Load the array with random characters. */
r = read( c->entropy_fd, &dod, sizeof( dod ) );
r = nwipe_read_entropy( &dod, sizeof( dod ) );
/* NOTE: Only the random data in dod[0] is actually used. */
@@ -453,7 +509,7 @@ void* nwipe_gutmann( void* ptr )
u16 s[27];
/* Load the array with random characters. */
ssize_t r = read( c->entropy_fd, &s, sizeof( s ) );
ssize_t r = nwipe_read_entropy( &s, sizeof( s ) );
if( r != sizeof( s ) )
{
r = errno;
@@ -611,7 +667,7 @@ void* nwipe_ops2( void* ptr )
}
/* Load the array of random characters. */
r = read( c->entropy_fd, s, u );
r = nwipe_read_entropy( s, u );
if( r != u )
{
@@ -750,6 +806,86 @@ void* nwipe_random( void* ptr )
return NULL;
} /* nwipe_random */
void* nwipe_bruce7( void* ptr )
{
/**
* Bruce Schneier 7-Pass wiping method.
*
* Pass 1: Overwrite the drive with all ones (0xFF).
* Pass 2: Overwrite the drive with all zeroes (0x00).
* Pass 3-7: Overwrite the drive with five passes of random data.
*/
nwipe_context_t* c = (nwipe_context_t*) ptr;
/* Get current time at the start of the wipe */
time( &c->start_time );
/* Set wipe in progress flag for GUI */
c->wipe_status = 1;
/* Setup for Bruce Schneier 7-Pass method */
char onefill[1] = { '\xFF' };
char zerofill[1] = { '\x00' };
nwipe_pattern_t patterns[] = {
{ 1, &onefill[0] }, // Pass 1: Overwrite with ones
{ 1, &zerofill[0] }, // Pass 2: Overwrite with zeroes
{ -1, "" }, // Pass 3: Random data
{ -1, "" }, // Pass 4: Random data
{ -1, "" }, // Pass 5: Random data
{ -1, "" }, // Pass 6: Random data
{ -1, "" }, // Pass 7: Random data
{ 0, NULL } // Terminate pattern array
};
/* Run the Bruce Schneier 7-Pass method */
c->result = nwipe_runmethod( c, patterns );
/* Finished. Set the wipe_status flag so that the GUI knows */
c->wipe_status = 0;
/* Get current time at the end of the wipe */
time( &c->end_time );
return NULL;
}
void* nwipe_bmb( void* ptr )
{
/**
* BMB Secure Wipe Method:
* Pass 1: 0xFF
* Pass 2: 0x00
* Pass 3-5: 3× Random
* Pass 6: 0xFF
*/
nwipe_context_t* c = (nwipe_context_t*) ptr;
time( &c->start_time );
c->wipe_status = 1;
char onefill[1] = { '\xFF' };
char zerofill[1] = { '\x00' };
nwipe_pattern_t patterns[] = {
{ 1, &onefill[0] }, // 0xFF
{ 1, &zerofill[0] }, // 0x00
{ -1, "" }, // RANDOM
{ -1, "" }, // RANDOM
{ -1, "" }, // RANDOM
{ 1, &onefill[0] }, // 0xFF
{ 0, NULL } // 0X00
};
c->result = nwipe_runmethod( c, patterns );
c->wipe_status = 0;
time( &c->end_time );
return NULL;
}
int nwipe_runmethod( nwipe_context_t* c, nwipe_pattern_t* patterns )
{
/**
@@ -911,13 +1047,13 @@ int nwipe_runmethod( nwipe_context_t* c, nwipe_pattern_t* patterns )
c->pass_type = NWIPE_PASS_WRITE;
/* Seed the PRNG. */
r = read( c->entropy_fd, c->prng_seed.s, c->prng_seed.length );
r = nwipe_read_entropy( c->prng_seed.s, c->prng_seed.length );
/* Check the result. */
if( r < 0 )
{
c->pass_type = NWIPE_PASS_NONE;
nwipe_perror( errno, __FUNCTION__, "read" );
nwipe_perror( errno, __FUNCTION__, "getrandom" );
nwipe_log( NWIPE_LOG_FATAL, "Unable to seed the PRNG." );
return -1;
}
@@ -1014,12 +1150,12 @@ int nwipe_runmethod( nwipe_context_t* c, nwipe_pattern_t* patterns )
c->pass_type = NWIPE_PASS_FINAL_OPS2;
/* Seed the PRNG. */
r = read( c->entropy_fd, c->prng_seed.s, c->prng_seed.length );
r = nwipe_read_entropy( c->prng_seed.s, c->prng_seed.length );
/* Check the result. */
if( r < 0 )
{
nwipe_perror( errno, __FUNCTION__, "read" );
nwipe_perror( errno, __FUNCTION__, "getrandom" );
nwipe_log( NWIPE_LOG_FATAL, "Unable to seed the PRNG." );
return -1;
}
@@ -1215,7 +1351,11 @@ void calculate_round_size( nwipe_context_t* c )
* all methods and processed first then created a switch statement that contains method specific changes if any
*/
/* Don't change the order of these values as the case statements use their index in the array */
/* Don't change the order of these values as the case statements use their index in the array, New methods
* don't need to be added to this array unless they have complicated calculations like Ops2 and IS5. If you do
* add a method, just add it to the bottom of the array_methods array and also to the bottom of the switch
* statement.
*/
void* array_methods[] = { &nwipe_zero,
&nwipe_ops2,
&nwipe_dodshort,
@@ -1226,13 +1366,17 @@ void calculate_round_size( nwipe_context_t* c )
NULL };
int i;
/* This while loop allows us to effectively create a const so we can use a case statement rather than if statements.
* This is probably more readable as more methods may get added in the future. The code could be condensed as some
* methods have identical adjustments, however as there are only a few methods I felt it was easier to understand as
* it is, however this could be changed if necessary.
/* This while loop allows us to effectively create a const that represents a method so we can use a case statement
* rather than if statements.
*
* Using a switch statement looks better than if statments as more methods may get added in the future expanding the
* list. The code could be condensed as some methods have identical adjustments, however as there are only a few
* methods I felt it was easier to understand as it is, however this could be changed if necessary.
*/
int selected_method;
/* Initialise, -1 = no additional calculation required */
int selected_method = -1;
i = 0;
while( array_methods[i] != NULL )
{
@@ -1243,6 +1387,10 @@ void calculate_round_size( nwipe_context_t* c )
i++;
}
/* On exit from the while loop the selected method either equals an index to a method
* or it equals -1 which means no extra calculations are required that are method specific
*/
if( nwipe_options.verify == NWIPE_VERIFY_ALL )
{
/* We must read back all passes, so double the byte count. */
@@ -1369,6 +1517,11 @@ void calculate_round_size( nwipe_context_t* c )
}
break;
case -1:
/* Method not listed so don't do any extra calculations
* ---------------------------------------------------- */
break;
}
}

View File

@@ -54,6 +54,8 @@ void* nwipe_zero( void* ptr );
void* nwipe_one( void* ptr );
void* nwipe_verify_zero( void* ptr );
void* nwipe_verify_one( void* ptr );
void* nwipe_bruce7( void* ptr );
void* nwipe_bmb( void* ptr );
void calculate_round_size( nwipe_context_t* );

896
src/miscellaneous.c Normal file
View File

@@ -0,0 +1,896 @@
/*
* miscellaneous.c: functions that may be generally used throughout nwipes code,
* mainly string processing functions but also time related functions.
*
* Copyright PartialVolume <https://github.com/PartialVolume>.
*
* 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
#ifndef _POSIX_SOURCE
#define _POSIX_SOURCE
#endif
#include <stdio.h>
#include "nwipe.h"
#include "context.h"
#include "logging.h"
#include "miscellaneous.h"
#include <string.h>
#include <stdlib.h>
#include <dirent.h>
#include <unistd.h>
#include <limits.h>
#include <errno.h>
#include <ctype.h> // for isspace()
#include <stddef.h> // for NULL
/* Convert string to upper case
*/
void strupper( char* str )
{
int idx;
idx = 0;
while( str[idx] != 0 )
{
/* If upper case alpha character, change to lower case */
if( str[idx] >= 'A' && str[idx] <= 'Z' )
{
str[idx] -= 32;
}
idx++;
}
}
/* Convert string to lower case
*/
void strlower( char* str )
{
int idx;
idx = 0;
while( str[idx] != 0 )
{
/* If upper case alpha character, change to lower case */
if( str[idx] >= 'A' && str[idx] <= 'Z' )
{
str[idx] += 32;
}
idx++;
}
}
void strip_CR_LF( char* str )
{
/* In the specified string, replace any CR or LF with a space */
int idx = 0;
int len = strlen( str );
while( idx < len )
{
if( str[idx] == 0x0A || str[idx] == 0x0D )
{
str[idx] = ' ';
}
idx++;
}
}
/* Search a string for a positive number, convert the first
* number found to binary and return the binary number.
* returns the number or -1 if number too large or -2 if
* no number found.
*/
u64 str_ascii_number_to_ll( char* str )
{
int idx;
int idx2;
char number_copy[20];
idx = 0; // index into the main string we are searching
idx2 = 0; // index used for backup copy of ascii number
while( str[idx] != 0 )
{
/* Find the start of the number */
if( str[idx] >= '0' && str[idx] <= '9' )
{
while( str[idx] != 0 )
{
/* Find the end of the number */
if( str[idx] >= '0' && str[idx] <= '9' )
{
if( idx2 < sizeof( number_copy ) - 1 )
{
number_copy[idx2++] = str[idx++];
}
else
{
/* Number is too large ! */
return -1;
}
}
else
{
/* end found */
number_copy[idx2] = 0; // terminate our copy
/* convert ascii number to longlong */
return atoll( number_copy );
}
}
}
else
{
idx++;
}
}
return -2; /* no number found */
}
void Determine_C_B_nomenclature( u64 qty, char* result, int result_array_size )
{
/* C_B ? Determine Capacity or Bandwidth nomenclature
*
* A pointer to a result character string with a minimum of 13 characters in length
* should be provided.
*
* Outputs a string of the form xxxTB, xxxGB, xxxMB, xxxKB B depending on the value of 'qty'
*/
/* Initialise the output array */
int idx = 0;
while( idx < result_array_size )
{
result[idx++] = 0;
}
/* Determine the size of throughput so that the correct nomenclature can be used */
if( qty >= INT64_C( 10000000000000 ) )
{
snprintf( result, result_array_size, "%4llu TB", qty / INT64_C( 1000000000000 ) );
}
else if( qty >= INT64_C( 10000000000 ) )
{
snprintf( result, result_array_size, "%4llu GB", qty / INT64_C( 1000000000 ) );
}
else if( qty >= INT64_C( 10000000 ) )
{
snprintf( result, result_array_size, "%4llu MB", qty / INT64_C( 1000000 ) );
}
else if( qty >= INT64_C( 10000 ) )
{
snprintf( result, result_array_size, "%4llu KB", qty / INT64_C( 1000 ) );
}
else
{
snprintf( result, result_array_size, "%4llu B", qty / INT64_C( 1 ) );
}
}
void convert_seconds_to_hours_minutes_seconds( u64 total_seconds, int* hours, int* minutes, int* seconds )
{
/* Convert binary seconds into binary hours, minutes and seconds */
if( total_seconds % 60 )
{
*minutes = total_seconds / 60;
*seconds = total_seconds - ( *minutes * 60 );
}
else
{
*minutes = total_seconds / 60;
*seconds = 0;
}
if( *minutes > 59 )
{
*hours = *minutes / 60;
if( *minutes % 60 )
{
*minutes = *minutes - ( *hours * 60 );
}
else
{
*minutes = 0;
}
}
}
int nwipe_strip_path( char* output, char* input )
{
/* Take the input string, say "/dev/sda" and remove the "/dev/", prefix the result
* with 'length' spaces. So if length=8 and input=/dev/sda, output will
* be " sda", a string 8 characters long right justified with spaces.
*/
int idx_dest;
int idx_src;
idx_dest = 8;
// idx_dest = length;
output[idx_dest--] = 0;
idx_src = strlen( input );
idx_src--;
while( idx_dest >= 0 )
{
/* if the device name contains a / start prefixing spaces */
if( input[idx_src] == '/' )
{
output[idx_dest--] = ' ';
continue;
}
if( idx_src >= 0 )
{
output[idx_dest--] = input[idx_src--];
}
}
return 0;
}
void replace_non_alphanumeric( char* str, char replacement_char )
{
int i = 0;
while( str[i] != 0 )
{
if( str[i] < '0' || ( str[i] > '9' && str[i] < 'A' ) || ( str[i] > 'Z' && str[i] < 'a' ) || str[i] > 'z' )
{
str[i] = replacement_char;
}
i++;
}
}
void convert_double_to_string( char* output_str, double value )
{
int idx = 0;
int idx2;
int idx3 = 0;
char percstr[512] = "";
snprintf( percstr, sizeof( percstr ), "%5.32lf", value );
while( percstr[idx] != 0 )
{
if( percstr[idx] == '.' )
{
for( idx2 = 0; idx2 < 3; idx2++ )
{
output_str[idx3++] = percstr[idx++];
}
break;
}
output_str[idx3++] = percstr[idx++];
}
output_str[idx3] = 0;
}
int read_system_datetime( char* year, char* month, char* day, char* hours, char* minutes, char* seconds )
{
/* Reads system date & time and populates the caller provided strings.
* Each string is null terminated by this function. The calling program
* must provide the minimum string sizes as shown below.
*
* year 5 bytes (4 numeric digits plus NULL terminator)
* month 3 bytes (2 numeric digits plus NULL terminator)
* day 3 bytes (2 numeric digits plus NULL terminator)
* hours 3 bytes (2 numeric digits plus NULL terminator)
* minutes 3 bytes (2 numeric digits plus NULL terminator)
* seconds 3 bytes (2 numeric digits plus NULL terminator)
*
* return value:
* 0 = success
* -1 = Failure, see nwipe log for detail.
*/
FILE* fp;
int r; // A result buffer.
int idx; // general index
int status = 0;
/**
* Obtain the year
*/
fp = popen( "date +%Y", "r" );
if( fp == NULL )
{
nwipe_log( NWIPE_LOG_ERROR, "Failed to obtain system year using commmand = date +%Y" );
}
else
{
/* Read the first line and validate it. Should be 4 numeric digits */
if( fgets( year, FOUR_DIGITS + 1, fp ) != NULL )
{
idx = 0;
while( idx < 4 )
{
if( year[idx] >= '0' && year[idx] <= '9' )
{
idx++;
}
else
{
/* if we haven't reached the correct number of digits due to invalid data, log error */
year[++idx] = 0; /* terminate the string, prior to using in nwipe_log */
nwipe_log( NWIPE_LOG_ERROR,
"Obtained system year using command = date +%Y, but result appears invalid = %s",
year );
status = -1;
break;
}
}
year[idx] = 0; /* terminate the string */
}
r = pclose( fp );
}
/**
* Obtain the month
*/
fp = popen( "date +%m", "r" );
if( fp == NULL )
{
nwipe_log( NWIPE_LOG_ERROR, "Failed to obtain system month using the command = date +%m" );
}
else
{
/* Read the first line and validate it. Should be 2 numeric digits */
if( fgets( month, TWO_DIGITS + 1, fp ) != NULL )
{
idx = 0;
while( idx < 2 )
{
if( month[idx] >= '0' && month[idx] <= '9' )
{
idx++;
}
else
{
/* if we haven't reached the correct number of digits due to invalid data, log error */
month[++idx] = 0; /* terminate the string, prior to using in nwipe_log */
nwipe_log( NWIPE_LOG_ERROR,
"Obtained system month using command = date +%m, but result appears invalid = %s",
month );
status = -1;
break;
}
}
month[idx] = 0; /* terminate the string */
}
r = pclose( fp );
}
/**
* Obtain the day
*/
fp = popen( "date +\%d", "r" );
if( fp == NULL )
{
nwipe_log( NWIPE_LOG_ERROR, "Failed to obtain system day using the command = date +\%d" );
}
else
{
/* Read the first line and validate it. Should be 2 numeric digits */
if( fgets( day, TWO_DIGITS + 1, fp ) != NULL )
{
idx = 0;
while( idx < 2 )
{
if( day[idx] >= '0' && day[idx] <= '9' )
{
idx++;
}
else
{
/* if we haven't reached the correct number of digits due to invalid data, log error */
day[++idx] = 0; /* terminate the string, prior to using in nwipe_log */
nwipe_log( NWIPE_LOG_ERROR,
"Obtained system day using command = date +\%d, but result appears invalid = %s",
day );
status = -1;
break;
}
}
day[idx] = 0; /* terminate the string */
}
r = pclose( fp );
}
/**
* Obtain the hours
*/
fp = popen( "date +%H", "r" );
if( fp == NULL )
{
nwipe_log( NWIPE_LOG_ERROR, "Failed to obtain system hour using the command = date +%H" );
}
else
{
/* Read the first line and validate it. Should be 2 numeric digits */
if( fgets( hours, TWO_DIGITS + 1, fp ) != NULL )
{
// nwipe_log( NWIPE_LOG_INFO, "Seconds = %s, Year = %s", seconds, year);
idx = 0;
while( idx < 2 )
{
if( hours[idx] >= '0' && hours[idx] <= '9' )
{
idx++;
}
else
{
/* if we haven't reached the correct number of digits due to invalid data, log error */
hours[++idx] = 0; /* terminate the string, prior to using in nwipe_log */
nwipe_log( NWIPE_LOG_ERROR,
"Obtained system hours using command = date +%H, but result appears invalid = %s",
hours );
status = -1;
break;
}
}
hours[idx] = 0; /* terminate the string */
}
r = pclose( fp );
}
/**
* Obtain the minutes
*/
fp = popen( "date +%M", "r" );
if( fp == NULL )
{
nwipe_log( NWIPE_LOG_ERROR, "Failed to obtain system minutes using the command = date +%M" );
}
else
{
/* Read the first line and validate it. Should be 2 numeric digits */
if( fgets( minutes, TWO_DIGITS + 1, fp ) != NULL )
{
// nwipe_log( NWIPE_LOG_INFO, "Seconds = %s, Year = %s", seconds, year);
idx = 0;
while( idx < 2 )
{
if( minutes[idx] >= '0' && minutes[idx] <= '9' )
{
idx++;
}
else
{
/* if we haven't reached the correct number of digits due to invalid data, log the error */
minutes[++idx] = 0; /* terminate the string, prior to using in nwipe_log */
nwipe_log( NWIPE_LOG_ERROR,
"Obtained system minutes using command = date +%H, but result appears invalid = %s",
minutes );
status = -1;
break;
}
}
minutes[idx] = 0; /* terminate the string */
}
r = pclose( fp );
}
/**
* Obtain the seconds
*/
fp = popen( "date +%S", "r" );
if( fp == NULL )
{
nwipe_log( NWIPE_LOG_ERROR, "Failed to obtain system seconds using the command = date +%S" );
}
else
{
/* Read the first line and validate it. Should be 2 numeric digits */
if( fgets( seconds, TWO_DIGITS + 1, fp ) != NULL )
{
// nwipe_log( NWIPE_LOG_INFO, "Seconds = %s, Year = %s", seconds, year);
idx = 0;
while( idx < 2 )
{
if( seconds[idx] >= '0' && seconds[idx] <= '9' )
{
idx++;
}
else
{
/* if we haven't reached the correct number of digits due to invalid data, log error */
seconds[++idx] = 0; /* terminate the string, prior to using in nwipe_log */
nwipe_log( NWIPE_LOG_ERROR,
"Obtained system seconds using command = date +%S, but result appears invalid = %s",
seconds );
status = -1;
break;
}
}
seconds[idx] = 0; /* terminate the string */
}
r = pclose( fp );
}
return status;
}
int write_system_datetime( char* year, char* month, char* day, char* hours, char* minutes, char* seconds )
{
/* Writes the system date & time using data from the caller provided strings.
* The calling program must provide the minimum string sizes as shown below
* populated with current date and time data.
*
* year 5 bytes (4 numeric digits plus NULL terminator)
* month 3 bytes (2 numeric digits plus NULL terminator)
* day 3 bytes (2 numeric digits plus NULL terminator)
* hours 3 bytes (2 numeric digits plus NULL terminator)
* minutes 3 bytes (2 numeric digits plus NULL terminator)
* seconds 3 bytes (2 numeric digits plus NULL terminator)
*
* return value:
* 0 = success
* -1 = Failure, see nwipe log for detail.
*/
FILE* fp;
int r; // A result buffer.
int idx; // general index
int strIdx; // Index into each string
int bufferIdx; // Index into the buffer
char buffer[5];
/**
* Basic validation that confirms the input strings are numeric and of the correct length, we do this
* by first constructing three arrays. The first are the names of the variables in order
* year, month, day, hours, minutes and seconds. The second array contains the address of
* each of those strings. The third array are the lengths.
* This allows us to create a single loop to validate all fields.
*/
char* names[] = { "year", "month", "day", "hours", "minutes", "seconds" };
char* pdata[] = { year, month, day, hours, minutes, seconds };
int lengths[] = { 4, 2, 2, 2, 2, 2 };
char cmd_format[] = "date %s%s%s%s%s.%s >/dev/null 2>&1";
char cmd[256];
for( idx = 0; idx < 6; idx++ )
{
strIdx = 0; // initialise string index
/* check each characters is numeric */
while( strIdx < lengths[idx] )
{
if( pdata[idx][strIdx] >= '0' && pdata[idx][strIdx] <= '9' )
{
strIdx++;
}
else
{
/* if we haven't reached the correct number of digits due to invalid data, log error,
* but first we read the valid data acquired so far into a buffer, this is done to avoid
* writing to the user provided string because if they did not size the string correctly
* writing a zero at the end could cause a segfault.
*/
for( bufferIdx = 0; bufferIdx < strIdx + 1; bufferIdx++ )
{
buffer[bufferIdx] = pdata[idx][bufferIdx];
}
buffer[bufferIdx] = 0; /* terminate the string, prior to using in nwipe_log */
/* A typical error will look like ..
* "User provided year data that appear invalid = 202£" */
nwipe_log( NWIPE_LOG_ERROR, "User provided %s data that appears invalid = %s", names[idx], buffer );
return -1;
}
}
}
/**
* Now using the validated strings construct the date command that we will use to write the system date/time
*/
sprintf( cmd, cmd_format, month, day, hours, minutes, year, seconds );
/**
* Run the date command to write the new date/time
*/
fp = popen( cmd, "w" );
r = pclose( fp );
if( fp == NULL || r != 0 )
{
nwipe_log( NWIPE_LOG_ERROR, "Failed to write system date/time using command = %s", cmd );
}
else
{
nwipe_log( NWIPE_LOG_INFO, "Date/time succesfully writen to system using command = %s", cmd );
}
return 0;
}
void fix_endian_model_names( char* model )
{
/* Some IDE USB adapters get the endian wrong, we can't determine the endian from the drive
* as the drive standard doesn't provide that information, so we have to resort to matching
* the model name against known strings with the endian incorrect, then reverse the endian.
*/
int idx = 0;
int idx2 = 0;
unsigned int length = 0;
char* tmp_string;
char* model_lower_case;
int swap_endian_flag = 0;
length = strlen( model );
tmp_string = calloc( length + 1, 1 );
model_lower_case = calloc( length + 1, 1 );
strncpy( model_lower_case, model, length );
model_lower_case[length] = 0; /* makesure it's terminated */
strlower( model_lower_case ); /* convert to lower case for comparison */
/* "ASSMNU G" = "SAMSUNG ", tested against model Samsung HM160HC so that
* "ASSMNU G MH61H0 C" becomes "SAMSUNG HM160HC ")
*/
if( !( strncmp( model_lower_case, "assmnu g", 8 ) ) )
{
swap_endian_flag = 1;
}
else
{
/* Hitachi */
if( !( strncmp( model_lower_case, "ihathc i", 8 ) ) )
{
swap_endian_flag = 1;
}
else
{
/* Toshiba */
if( !( strncmp( model_lower_case, "othsbi a", 8 ) ) )
{
swap_endian_flag = 1;
}
else
{
/* WDC (Western Digital Corporation) */
if( !( strncmp( model_lower_case, "dw c", 4 ) ) )
{
swap_endian_flag = 1;
}
else
{
/* Seagate */
if( !( strncmp( model_lower_case, "esgata e", 8 ) ) )
{
swap_endian_flag = 1;
}
else
{
/* Seagate models starting ST */
if( !( strncmp( model_lower_case, "ts", 2 ) ) )
{
swap_endian_flag = 1;
}
else
{
/* Sundisk - Sandisk SSD from Sun AXI */
if( !( strncmp( model_lower_case, "usdnsi k", 8 ) ) )
{
swap_endian_flag = 1;
}
else
{
/* Sandisk */
if( !( strncmp( model_lower_case, "asdnsi k", 8 ) ) )
{
swap_endian_flag = 1;
}
}
}
}
}
}
}
}
if( swap_endian_flag == 1 )
{
while( model[idx] != 0 && idx < length )
{
if( model[idx + 1] != 0 )
{
/* Swap the bytes */
tmp_string[idx2 + 1] = model[idx];
tmp_string[idx2] = model[idx + 1];
}
else
{
/* Copy the last odd byte and exit while loop */
tmp_string[idx2] = model[idx];
break;
}
if( tmp_string[idx2 + 1] == ' ' && model[idx + 2] == ' ' )
{
idx++;
}
idx += 2;
idx2 += 2;
}
tmp_string[length] = 0; /* terminate */
strncpy( model, tmp_string, length );
model[length] = 0; /* terminate */
}
free( tmp_string );
free( model_lower_case );
}
/*
* get_device_uuid - Find the UUID corresponding to a given block device
* @device_path: e.g., "/dev/sda1"
* @uuid_buf: pointer to a 40-byte buffer where the UUID string will be stored
*
* Returns 0 on success, -1 on failure.
*/
int get_device_uuid( const char* device_path, char* uuid_buf )
{
const char* uuid_dir = "/dev/disk/by-uuid";
struct dirent* entry;
DIR* dir;
char link_path[PATH_MAX];
char resolved_path[PATH_MAX];
ssize_t len;
if( !device_path || !uuid_buf )
{
errno = EINVAL;
return -1;
}
dir = opendir( uuid_dir );
if( !dir )
{
perror( "opendir" );
return -1;
}
while( ( entry = readdir( dir ) ) != NULL )
{
// Skip "." and ".."
if( strcmp( entry->d_name, "." ) == 0 || strcmp( entry->d_name, ".." ) == 0 )
continue;
snprintf( link_path, sizeof( link_path ), "%s/%s", uuid_dir, entry->d_name );
// readlink() doesn't null-terminate, so we must handle that
len = readlink( link_path, resolved_path, sizeof( resolved_path ) - 1 );
if( len == -1 )
continue; // skip unreadable links
resolved_path[len] = '\0';
// Construct full path (readlink gives a relative path, usually "../../sda1")
char full_path[PATH_MAX + strlen( uuid_dir ) + 1];
if( resolved_path[0] != '/' )
{
// Resolve relative symlink path
snprintf( full_path, sizeof( full_path ), "%s/%s", uuid_dir, resolved_path );
if( realpath( full_path, full_path ) == NULL )
{
closedir( dir );
return -1;
}
}
else
{
strncpy( full_path, resolved_path, sizeof( full_path ) );
full_path[sizeof( full_path ) - 1] = '\0';
}
// Compare with the given device path
if( strcmp( full_path, device_path ) == 0 )
{
strncpy( uuid_buf, entry->d_name, 40 );
uuid_buf[39] = '\0';
closedir( dir );
return 0;
}
}
closedir( dir );
errno = ENOENT;
return -1; // not found
}
/**
* find_base_device - searches /sys/block for a device that matches
* the prefix of the provided name.
*
* @devname: the input device name (e.g. "sda1", "sdb2")
* @result: buffer to store the matched base device name
* @size: size of the result buffer
*
* Returns: 0 on success (match found), -1 on failure (no match or error)
*/
int find_base_device( const char* devname, char* result, size_t size )
{
if( !devname || !result || size == 0 )
{
fprintf( stderr, "Invalid argument(s)\n" );
return -1;
}
DIR* dir = opendir( "/sys/block" );
if( !dir )
{
perror( "opendir" );
return -1;
}
struct dirent* entry;
int found = -1;
while( ( entry = readdir( dir ) ) != NULL )
{
// Skip "." and ".."
if( entry->d_name[0] == '.' )
continue;
// Check if the given devname starts with the block device name
size_t len = strlen( entry->d_name );
if( strncmp( devname, entry->d_name, len ) == 0 )
{
// Match found
strncpy( result, entry->d_name, size - 1 );
result[size - 1] = '\0';
found = 0;
break;
}
}
closedir( dir );
return found;
}
/**
* skip_whitespace - Returns a pointer to the first non-whitespace character
* in a given string.
*
* @str: input string
*
* Returns: pointer to the first non-whitespace character,
* or NULL if str is NULL or the string contains only whitespace.
*/
const char* skip_whitespace( const char* str )
{
if( !str )
return NULL;
while( *str && isspace( (unsigned char) *str ) )
str++;
return *str ? str : NULL;
}

164
src/miscellaneous.h Normal file
View File

@@ -0,0 +1,164 @@
/*
* miscellaneous.h: header file for miscellaneous.c ..
*
* functions that may be generally used throughout nwipes code,
* mainly string processing related functions.
*
* Copyright PartialVolume <https://github.com/PartialVolume>.
*
* 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 MISCELLANEOUS_H_
#define MISCELLANEOUS_H_
#define FOUR_DIGITS 4
#define TWO_DIGITS 2
/**
* Convert the string from lower to upper case
* @param pointer to a null terminated string
* @return void
*/
void strupper( char* );
/**
* Convert the string from upper to lower case
* @param pointer to a null terminated string
* @return void
*/
void strlower( char* str );
/**
* Search a string for a positive number, convert the first
* number found to binary and return the binary number.
* returns the number or -1 if number too large or -2 if
* no number found.
*
* @param pointer to a null terminated string
* @return longlong returns:
* the number
* -1 = number too large
* -2 = no number found.
*/
u64 str_ascii_number_to_ll( char* );
void Determine_C_B_nomenclature( u64, char*, int );
void convert_seconds_to_hours_minutes_seconds( u64, int*, int*, int* );
int nwipe_strip_path( char*, char* );
/**
* Scan a string and replace any characters that are not alpha-numeric with
* the character_char.
* Example:
* char str[] = 18:21:56;
* calling the function replace_non_alphanumeric( &str, '_' )
* would result in str changing from 18:21:56 to 18_21_56
* @param char* pointer to the string to be processed
* @param char the character used to replace non alpha-numeric characters
* @return void
*/
void replace_non_alphanumeric( char*, char );
/**
* I found this function necessary when converting a double of say
* 99.999999999999999999 to text using printf. I only wanted 99.99
* printed but if you specified a precision of %.2f in printf i.e 2 digits
* after the decimal point you get 100.00 and not 99.99 If you increase
* the precision to %.10f then you get 99.9999999999 but I only want
* two significant digits displayed.i.e 99.99% not 100%
* So this function converts to double retaining sufficient precision
* so that a 30TB disc with one hidden sector will display as 99.99% erased
* As an example if the double value to be converted is 99.999999999999999987
* this function will always output 99.99 unlike printf which outputs 100.00
* @param char* pointer to the string we write our percentage to. Needs to be
* a minimum of 7 bytes, i.e 100.00 plus null terminator.
* @param double the floating point value to be converted to a string.
* @return void
*/
void convert_double_to_string( char*, double );
/**
* Reads system date & time and populates the caller provided strings.
* Each string is null terminated by this function. The calling
* program must provide the minimum string sizes as shown below.
*
* @param char* year 5 bytes (4 numeric digits plus NULL terminator)
* @param char* month 3 bytes (2 numeric digits plus NULL terminator)
* @param char* day 3 bytes (2 numeric digits plus NULL terminator)
* @param char* hours 3 bytes (2 numeric digits plus NULL terminator)
* @param char* minutes 3 bytes (2 numeric digits plus NULL terminator)
* @param char* seconds 3 bytes (2 numeric digits plus NULL terminator)
* @return 0 = success, -1 = failure. See nwipe log for detail.
*/
int read_system_datetime( char*, char*, char*, char*, char*, char* );
/**
* Writes system date & time from the caller provided strings.
* The calling program must provide the minimum populated string sizes
* as shown below.
*
* @param char* year 5 bytes (4 numeric digits plus NULL terminator)
* @param char* month 3 bytes (2 numeric digits plus NULL terminator)
* @param char* day 3 bytes (2 numeric digits plus NULL terminator)
* @param char* hours 3 bytes (2 numeric digits plus NULL terminator)
* @param char* minutes 3 bytes (2 numeric digits plus NULL terminator)
* @param char* seconds 3 bytes (2 numeric digits plus NULL terminator)
* @return 0 = success, -1 = failure. See nwipe log for detail.
*/
int write_system_datetime( char*, char*, char*, char*, char*, char* );
/**
* Fixes drive model names that have the incorrect endian. This happens
* with some IDE USB adapters.
*
* @param char* pointer to the drive model names
* @return void
*/
void fix_endian_model_names( char* model );
/**
* Obtains the UUID of the drive partition
*
* @param constchar* device path, example /dev/sda1
* @param char* pointer to UUID string UUID_SIZE long
* @return 0 = success, -1 = failure.
*/
int get_device_uuid( const char*, char* );
/**
* find_base_device - searches /sys/block for a device that matches
* the prefix of the provided name.
*
* @devname: the input device name (e.g. "sda1", "sdb2")
* @result: buffer to store the matched base device name
* @size: size of the result buffer
*
* Returns: 0 on success (match found), -1 on failure (no match or error)
*/
int find_base_device( const char*, char*, size_t );
/**
* skip_whitespace - Returns a pointer to the first non-whitespace character
* in a given string.
*
* @str: input string
*
* Returns: pointer to the first non-whitespace character,
* or NULL if str is NULL or the string contains only whitespace.
*/
const char* skip_whitespace( const char* );
#endif /* HPA_DCO_H_ */

View File

@@ -1,16 +1,16 @@
/*
This code is modified for use in nwipe.
/*
This code is modified for use in nwipe.
A C-program for MT19937, with initialization improved 2002/2/10.
Coded by Takuji Nishimura and Makoto Matsumoto.
This is a faster version by taking Shawn Cokus's optimization,
Matthe Bellew's simplification, Isaku Wada's real version.
Before using, initialize the state by using init_genrand(seed)
Before using, initialize the state by using init_genrand(seed)
or init_by_array(init_key, key_length).
Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
All rights reserved.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
@@ -23,8 +23,8 @@
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The names of its contributors may not be used to endorse or promote
products derived from this software without specific prior written
3. The names of its contributors may not be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
@@ -48,77 +48,89 @@
#include <stdio.h>
#include "mt19937ar-cok.h"
/* initializes state[N] with a seed */
void init_genrand( twister_state_t* state, unsigned long s)
/* initializes state[MT_STATE_SIZE] with a seed */
void init_genrand( twister_state_t* state, unsigned long s )
{
int j;
state->array[0]= s & 0xffffffffUL;
for( j = 1; j < N; j++ )
{
state->array[j] = (1812433253UL * (state->array[j-1] ^ (state->array[j-1] >> 30)) + j);
state->array[j] &= 0xffffffffUL; /* for >32 bit machines */
}
state->left = 1;
state->initf = 1;
int j;
state->array[0] = s & 0xffffffffUL;
for( j = 1; j < MT_STATE_SIZE; j++ )
{
state->array[j] = ( 1812433253UL * ( state->array[j - 1] ^ ( state->array[j - 1] >> 30 ) ) + j );
state->array[j] &= 0xffffffffUL; /* for >32 bit machines */
}
state->left = 1;
state->initf = 1;
}
void twister_init( twister_state_t* state, unsigned long init_key[], unsigned long key_length )
{
int i = 1;
int j = 0;
int k = ( N > key_length ? N : key_length );
int j = 0;
int k = ( MT_STATE_SIZE > key_length ? MT_STATE_SIZE : key_length );
init_genrand( state, 19650218UL );
for( ; k; k-- )
{
state->array[i] = (state->array[i] ^ ((state->array[i-1] ^ (state->array[i-1] >> 30)) * 1664525UL)) + init_key[j] + j;
state->array[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
++i;
++j;
{
state->array[i] = ( state->array[i] ^ ( ( state->array[i - 1] ^ ( state->array[i - 1] >> 30 ) ) * 1664525UL ) )
+ init_key[j] + j;
state->array[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
++i;
++j;
if ( i >= N )
{
state->array[0] = state->array[N-1];
i = 1;
}
if( i >= MT_STATE_SIZE )
{
state->array[0] = state->array[MT_STATE_SIZE - 1];
i = 1;
}
if ( j >= key_length )
{
j = 0;
}
if( j >= key_length )
{
j = 0;
}
}
for( k = N -1; k; k-- )
{
state->array[i] = (state->array[i] ^ ((state->array[i-1] ^ (state->array[i-1] >> 30)) * 1566083941UL)) - i;
state->array[i] &= 0xffffffffUL;
++i;
for( k = MT_STATE_SIZE - 1; k; k-- )
{
state->array[i] =
( state->array[i] ^ ( ( state->array[i - 1] ^ ( state->array[i - 1] >> 30 ) ) * 1566083941UL ) ) - i;
state->array[i] &= 0xffffffffUL;
++i;
if ( i >= N )
{
state->array[0] = state->array[N-1];
i = 1;
}
if( i >= MT_STATE_SIZE )
{
state->array[0] = state->array[MT_STATE_SIZE - 1];
i = 1;
}
}
state->array[0] = 0x80000000UL; /* MSB is 1; assuring non-zero initial array */
state->left = 1;
state->initf = 1;
state->array[0] = 0x80000000UL; /* MSB is 1; assuring non-zero initial array */
state->left = 1;
state->initf = 1;
}
static void next_state( twister_state_t* state )
{
unsigned long *p = state->array;
unsigned long* p = state->array;
int j;
if( state->initf == 0) { init_genrand( state, 5489UL ); }
state->left = N;
// Ensures the generator is initialized
if( state->initf == 0 )
{
init_genrand( state, 5489UL );
}
state->left = MT_STATE_SIZE;
state->next = state->array;
for( j = N - M + 1; --j; p++ ) { *p = p[M] ^ TWIST(p[0], p[1]); }
for( j = M; --j; p++ ) { *p = p[M-N] ^ TWIST(p[0], p[1]); }
*p = p[M-N] ^ TWIST(p[0], state->array[0]);
for( j = MT_STATE_SIZE - MT_MIDDLE_WORD + 1; --j; p++ )
{
*p = p[MT_MIDDLE_WORD] ^ TWIST( p[0], p[1] );
}
for( j = MT_MIDDLE_WORD; --j; p++ )
{
*p = p[MT_MIDDLE_WORD - MT_STATE_SIZE] ^ TWIST( p[0], p[1] );
}
*p = p[MT_MIDDLE_WORD - MT_STATE_SIZE] ^ TWIST( p[0], state->array[0] );
}
/* generates a random number on [0,0xffffffff]-interval */
@@ -126,14 +138,18 @@ unsigned long twister_genrand_int32( twister_state_t* state )
{
unsigned long y;
if ( --state->left == 0 ) { next_state( state ); }
// Advance internal state if necessary
if( --state->left == 0 )
{
next_state( state );
}
y = *state->next++;
/* Tempering */
y ^= (y >> 11);
y ^= (y << 7) & 0x9d2c5680UL;
y ^= (y << 15) & 0xefc60000UL;
y ^= (y >> 18);
// Tempering
y ^= ( y >> 11 );
y ^= ( y << 7 ) & 0x9d2c5680UL;
y ^= ( y << 15 ) & 0xefc60000UL;
y ^= ( y >> 18 );
return y;
}

View File

@@ -1,30 +1,29 @@
/*
* mt19937ar-cok.h: The Mersenne Twister PRNG implementation for nwipe.
*
* mt19937ar-cok.h: The Mersenne Twister PRNG implementation for nwipe.
*/
#ifndef MT19937AR_H_
#define MT19937AR_H_
/* Period parameters */
#define N 624
#define M 397
#define MATRIX_A 0x9908b0dfUL /* constant vector a */
#define MT_STATE_SIZE 624
#define MT_MIDDLE_WORD 397
#define MATRIX_A 0x9908b0dfUL /* constant vector a */
#define UMASK 0x80000000UL /* most significant w-r bits */
#define LMASK 0x7fffffffUL /* least significant r bits */
#define MIXBITS(u,v) ( ((u) & UMASK) | ((v) & LMASK) )
#define TWIST(u,v) ((MIXBITS(u,v) >> 1) ^ ((v)&1UL ? MATRIX_A : 0UL))
#define MIXBITS( u, v ) ( ( (u) &UMASK ) | ( (v) &LMASK ) )
#define TWIST( u, v ) ( ( MIXBITS( u, v ) >> 1 ) ^ ( (v) &1UL ? MATRIX_A : 0UL ) )
typedef struct twister_state_t_
{
unsigned long array[N];
int left;
int initf;
unsigned long *next;
} twister_state_t;
unsigned long array[MT_STATE_SIZE]; // Updated to use MT_STATE_SIZE
int left;
int initf;
unsigned long* next;
} twister_state_t;
/* Initialize the MT state. ( 0 < key_length <= 624 ). */
void twister_init( twister_state_t* state, unsigned long init_key[], unsigned long key_length);
void twister_init( twister_state_t* state, unsigned long init_key[], unsigned long key_length );
/* Generate a random integer on the [0,0xffffffff] interval. */
unsigned long twister_genrand_int32( twister_state_t* state );

View File

@@ -27,11 +27,20 @@
#define _POSIX_SOURCE
#endif
/* Enable GNU extensions so that O_DIRECT is visible from <fcntl.h>. */
#ifndef _GNU_SOURCE
#define _GNU_SOURCE 1
#endif
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <signal.h>
#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>
#include "nwipe.h"
#include "context.h"
@@ -42,6 +51,7 @@
#include "logging.h"
#include "gui.h"
#include "temperature.h"
#include "miscellaneous.h"
#include <sys/ioctl.h> /* FIXME: Twice Included */
#include <sys/shm.h>
@@ -49,12 +59,226 @@
#include <parted/parted.h>
#include <parted/debug.h>
#include "conf.h"
#include "version.h"
#include "hpa_dco.h"
#include "conf.h"
#include <libconfig.h>
#include <fcntl.h> /* O_DIRECT, O_RDWR, ... */
#ifdef NWIPE_USE_DIRECT_IO
#ifndef O_DIRECT
/*
* Some platforms or libcs do not define O_DIRECT at all. Defining it
* as 0 makes the flag a no-op and keeps the code buildable.
* On Linux/glibc, <fcntl.h> via nwipe.h will provide a real O_DIRECT.
*/
#define O_DIRECT 0
#endif
#endif
int terminate_signal;
int user_abort;
int global_wipe_status;
/* helper function for sorting */
int devnamecmp( const void* a, const void* b )
{
// nwipe_log( NWIPE_LOG_DEBUG, "a: %s, b: %s", ( *( nwipe_context_t** ) a)->device_name, ( *( nwipe_context_t** )
// b)->device_name );
int ldiff = strlen( ( *(nwipe_context_t**) a )->device_name ) - strlen( ( *(nwipe_context_t**) b )->device_name );
if( ldiff != 0 )
{
return ldiff;
}
int ret = strcmp( ( *(nwipe_context_t**) a )->device_name, ( *(nwipe_context_t**) b )->device_name );
return ( ret );
}
static int nwipe_prng_bench_cmp_desc( const void* a, const void* b )
{
const nwipe_prng_bench_result_t* A = (const nwipe_prng_bench_result_t*) a;
const nwipe_prng_bench_result_t* B = (const nwipe_prng_bench_result_t*) b;
/* successful results first */
if( A->rc != 0 && B->rc == 0 )
return 1;
if( A->rc == 0 && B->rc != 0 )
return -1;
/* then sort by MB/s */
if( A->mbps < B->mbps )
return 1;
if( A->mbps > B->mbps )
return -1;
return 0;
}
#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().
@@ -64,6 +288,7 @@ int main( int argc, char** argv )
int any_threads_still_running; // used in wipe thread cancellation wait loop
int thread_timeout_counter; // timeout thread cancellation after THREAD_CANCELLATION_TIMEOUT seconds
pthread_t nwipe_gui_thread = 0; // The thread ID of the GUI thread.
pthread_t nwipe_temperature_thread = 0; // The thread ID of the temperature update thread
pthread_t nwipe_sigint_thread; // The thread ID of the sigint handler.
char modprobe_command[] = "modprobe %s";
@@ -72,9 +297,6 @@ int main( int argc, char** argv )
char module_shortform[50];
char final_cmd_modprobe[sizeof( modprobe_command ) + sizeof( module_shortform )];
/* The entropy source file handle. */
int nwipe_entropy;
/* The generic index variables. */
int i;
int j;
@@ -82,12 +304,6 @@ int main( int argc, char** argv )
/* The generic result buffer. */
int r;
/* Log nwipes version */
nwipe_log( NWIPE_LOG_INFO, "%s", banner );
/* Log OS info */
nwipe_log_OSinfo();
/* Initialise the termintaion signal, 1=terminate nwipe */
terminate_signal = 0;
@@ -112,11 +328,199 @@ int main( int argc, char** argv )
/* Initialised and populated in device scan. */
nwipe_context_t** c1 = 0;
if( geteuid() != 0 )
{
printf( "nwipe must run with root permissions, which is not the case.\nAborting\n" );
exit( 99 );
}
int wipe_threads_started = 0;
/* Parse command line options. */
/** NOTE ** NOTE ** NOTE ** NOTE ** NOTE ** NOTE ** NOTE ** NOTE ** NOTE **
* Important Note: if you want nwipe_log messages to go into the logfile
* any 'nwipe_log()' commands must appear after the options are parsed here,
* else they will appear in the console but not in the logfile, that is,
* assuming you specified a log file on the command line as an nwipe option.
*/
/*****************************
* Parse command line options.
*/
/* Initialise the libconfig code that handles nwipe.conf */
nwipe_conf_init();
nwipe_optind = nwipe_options_parse( argc, argv );
/* Log nwipes version */
nwipe_log( NWIPE_LOG_INFO, "%s", banner );
/* Log OS info */
nwipe_log_OSinfo();
/* ------------------------------------------------------------
* PRNG benchmark / auto-select (runs before device scan)
* ------------------------------------------------------------ */
if( nwipe_options.prng_benchmark_only || nwipe_options.prng_auto )
{
/* tune defaults */
const size_t io_block = 4 * 1024 * 1024; /* 4 MiB RAM buffer blocks */
double seconds = nwipe_options.prng_bench_seconds;
/* If user requested auto-select and didn't override seconds, keep it short */
if( nwipe_options.prng_auto )
{
if( seconds <= 0.0 )
seconds = 0.25;
/* Optional: if you consider "1.0" to be the default and want auto shorter:
* if( seconds == 1.0 ) seconds = 0.25;
*/
}
else
{
if( seconds <= 0.0 )
seconds = 1.0;
}
nwipe_prng_bench_result_t results[16];
memset( results, 0, sizeof( results ) );
/* ------------------------------------------------------------
* --prng-benchmark-only path
* (keep output clean: no live "Testing..." lines by default)
* ------------------------------------------------------------ */
if( nwipe_options.prng_benchmark_only )
{
const int live_print = 0; /* set to 1 if you also want live here */
int n = nwipe_prng_benchmark_all_live(
seconds, io_block, results, (int) ( sizeof( results ) / sizeof( results[0] ) ), live_print );
if( n <= 0 )
{
nwipe_log( NWIPE_LOG_ERROR, "PRNG benchmark failed (no results)." );
printf( "PRNG benchmark failed (no results).\n" );
cleanup();
exit( 3 );
}
qsort( results, (size_t) n, sizeof( results[0] ), nwipe_prng_bench_cmp_desc );
/* Print to console + log */
printf( "\nPRNG Benchmark (RAM-only) ~%.2fs each, block=%zu MiB\n", seconds, io_block / ( 1024 * 1024 ) );
printf( "---------------------------------------------------\n" );
nwipe_log( NWIPE_LOG_INFO,
"PRNG Benchmark (RAM-only) ~%.2fs each, block=%zu MiB",
seconds,
io_block / ( 1024 * 1024 ) );
for( int i = 0; i < n; i++ )
{
if( results[i].rc == 0 )
{
printf( "%2d) %-40s %10.1f MB/s\n", i + 1, results[i].prng->label, results[i].mbps );
nwipe_log( NWIPE_LOG_INFO,
"PRNG bench %2d) %-40s %10.1f MB/s",
i + 1,
results[i].prng->label,
results[i].mbps );
}
else
{
printf( "%2d) %-40s (failed: rc=%d)\n", i + 1, results[i].prng->label, results[i].rc );
nwipe_log( NWIPE_LOG_WARNING,
"PRNG bench %2d) %-40s (failed: rc=%d)",
i + 1,
results[i].prng->label,
results[i].rc );
}
}
printf( "\n" );
cleanup();
exit( 0 );
}
/* ------------------------------------------------------------
* --prng=auto path
* (THIS is the path where live output matters for “GUI delay”)
* ------------------------------------------------------------ */
if( nwipe_options.prng_auto )
{
/* live_print=1: prints:
* - "Analysing PRNG performance:" immediately (with spinner)
* - "Testing <PRNG> performance..." per PRNG
* - "<PRNG> -> xx.x MB/s" immediately after each PRNG
*/
const int live_print = 1;
/* Option A (preferred): make select_fastest call the live benchmark internally.
* best = nwipe_prng_select_fastest(seconds, io_block, results, count);
* and inside select_fastest use nwipe_prng_benchmark_all_live(..., 1)
*
* Option B: benchmark here (live), then choose best locally.
* Since you already have nwipe_prng_select_fastest(), stick with Option A.
*/
const nwipe_prng_t* best = nwipe_prng_select_fastest(
seconds, io_block, results, (int) ( sizeof( results ) / sizeof( results[0] ) ) /* results_count */
/* ensure select_fastest uses nwipe_prng_benchmark_all_live(..., live_print) */
);
if( best != NULL )
{
/* Apply selection */
nwipe_options.prng = (nwipe_prng_t*) best;
nwipe_log( NWIPE_LOG_INFO, "Auto-selected fastest PRNG: %s", best->label );
printf( "Auto-selected fastest PRNG: %s\n", best->label );
}
else
{
nwipe_log( NWIPE_LOG_WARNING,
"Auto PRNG selection: no working PRNG found, keeping configured default." );
printf( "Auto PRNG selection: no working PRNG found, keeping configured default.\n" );
}
}
}
/* Check that hdparm exists, we use hdparm for some HPA/DCO detection etc, if not
* exit nwipe. These checks are required if the PATH environment is not setup !
* Example: Debian sid 'su' as opposed to 'su -'
*/
if( system( "which hdparm > /dev/null 2>&1" ) )
{
if( system( "which /sbin/hdparm > /dev/null 2>&1" ) )
{
if( system( "which /usr/bin/hdparm > /dev/null 2>&1" ) )
{
if( system( "which /usr/sbin/hdparm > /dev/null 2>&1" ) )
{
nwipe_log( NWIPE_LOG_WARNING, "hdparm command not found." );
nwipe_log( NWIPE_LOG_WARNING,
"Required by nwipe for HPA/DCO detection & correction and ATA secure erase." );
nwipe_log( NWIPE_LOG_WARNING, "** Please install hdparm **\n" );
cleanup();
exit( 1 );
}
}
}
}
/* 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( nwipe_ensure_directory( nwipe_options.PDFreportpath ) != 0 )
{
/* nwipe_ensure_directory already logged a detailed error message. */
cleanup();
exit( 2 );
}
}
if( nwipe_optind == argc )
{
/* File names were not given by the user. Scan for devices. */
@@ -148,13 +552,18 @@ int main( int argc, char** argv )
nwipe_enumerated = nwipe_device_get( &c1, argv, argc );
if( nwipe_enumerated == 0 )
{
nwipe_log( NWIPE_LOG_ERROR, "Devices not found. Check you're not excluding drives unnecessarily." );
printf( "No drives found\n" );
nwipe_log( NWIPE_LOG_ERROR, "Devices not found. Check you're not excluding drives unnecessarily," );
nwipe_log( NWIPE_LOG_ERROR, "and you are running nwipe as sudo or as root." );
printf( "Devices not found, check you're not excluding drives unnecessarily \n and you are running nwipe "
"as sudo or as root." );
cleanup();
exit( 1 );
}
}
/* sort list of devices here */
qsort( (void*) c1, (size_t) nwipe_enumerated, sizeof( nwipe_context_t* ), devnamecmp );
if( terminate_signal == 1 )
{
cleanup();
@@ -166,22 +575,13 @@ int main( int argc, char** argv )
/* The array of pointers to contexts that will actually be wiped. */
nwipe_context_t** c2 = (nwipe_context_t**) malloc( nwipe_enumerated * sizeof( nwipe_context_t* ) );
/* Open the entropy source. */
nwipe_entropy = open( NWIPE_KNOB_ENTROPY, O_RDONLY );
/* Check the result. */
if( nwipe_entropy < 0 )
if( c2 == NULL )
{
nwipe_perror( errno, __FUNCTION__, "open" );
nwipe_log( NWIPE_LOG_FATAL, "Unable to open entropy source %s.", NWIPE_KNOB_ENTROPY );
nwipe_log( NWIPE_LOG_ERROR, "memory allocation for c2 failed" );
cleanup();
free( c2 );
return errno;
exit( 1 );
}
nwipe_log( NWIPE_LOG_NOTICE, "Opened entropy source '%s'.", NWIPE_KNOB_ENTROPY );
/* Block relevant signals in main thread. Any other threads that are */
/* created after this will also block those signals. */
sigset_t sigset;
@@ -255,7 +655,7 @@ int main( int argc, char** argv )
/* load the drivetemp module */
if( system( final_cmd_modprobe ) != 0 )
{
nwipe_log( NWIPE_LOG_WARNING, "hwmon: Unable to load module drivetemp, temperatures may be unavilable." );
nwipe_log( NWIPE_LOG_WARNING, "hwmon: Unable to load module drivetemp, temperatures may be unavailable." );
nwipe_log( NWIPE_LOG_WARNING, "hwmon: It's possible the drivetemp software isn't modular but built-in" );
nwipe_log( NWIPE_LOG_WARNING, "hwmon: to the kernel, as is the case with ShredOS.x86_64 in which case" );
nwipe_log( NWIPE_LOG_WARNING, "hwmon: the temperatures will actually be available despite this issue." );
@@ -270,10 +670,7 @@ int main( int argc, char** argv )
for( i = 0; i < nwipe_enumerated; i++ )
{
/* Set the entropy source. */
c1[i]->entropy_fd = nwipe_entropy;
if( nwipe_options.autonuke )
if( nwipe_options.autonuke == 1 )
{
/* When the autonuke option is set, select all disks. */
// TODO - partitions
@@ -294,7 +691,13 @@ int main( int argc, char** argv )
nwipe_log( NWIPE_LOG_NOTICE, "hwmon: Device %s hwmon path = %s", c1[i]->device_name, c1[i]->temp1_path );
}
nwipe_update_temperature( c1[i] );
// nwipe_update_temperature( c1[i] );
/* Log the temperature crtical, highest, lowest and lowest critical temperature
* limits to nwipes log file using the INFO catagory
*/
nwipe_log_drives_temperature_limits( c1[i] );
}
/* Check for initialization errors. */
@@ -305,31 +708,68 @@ int main( int argc, char** argv )
return -1;
}
/* Set up the data structures to pass the temperature thread the data it needs */
nwipe_thread_data_ptr_t nwipe_temperature_thread_data;
nwipe_temperature_thread_data.c = c1;
nwipe_temperature_thread_data.nwipe_misc_thread_data = &nwipe_misc_thread_data;
/* Fork the temperature thread */
errno = pthread_create(
&nwipe_temperature_thread, NULL, nwipe_update_temperature_thread, &nwipe_temperature_thread_data );
/* Start the ncurses interface. */
if( !nwipe_options.nogui )
nwipe_gui_init();
switch( nwipe_options.nogui )
{
case 0:
nwipe_gui_init();
break;
if( nwipe_options.autonuke == 1 )
{
/* Print the options window. */
if( !nwipe_options.nogui )
nwipe_gui_options();
}
else
{
/* Get device selections from the user. */
if( nwipe_options.nogui )
{
printf( "--nogui option must be used with autonuke option\n" );
cleanup();
exit( 1 );
}
else
{
nwipe_gui_select( nwipe_enumerated, c1 );
}
case 1:
break;
default:
printf( "system error: nwipe_options.nogui (should be 0 or 1) is invalid, nwipe_options.nogui=%i !? \n",
nwipe_options.nogui );
}
switch( nwipe_options.autonuke )
{
case 0:
/* The user can't specify the nogui option without also using the autonuke option */
if( nwipe_options.nogui == 1 )
{
printf( "--nogui option must be used with autonuke option\n" );
cleanup();
exit( 1 );
}
else
{
/* If selected show customer and organisation details BEFORE drive selection screen */
if( nwipe_options.PDF_preview_details == 1 )
{
nwipe_gui_preview_org_customer( SHOWING_PRIOR_TO_DRIVE_SELECTION );
}
/* Get device selections from the user. */
nwipe_gui_select( nwipe_enumerated, c1 );
}
break;
case 1:
/* Print the options window. */
if( !nwipe_options.nogui )
nwipe_gui_options();
break;
default:
printf(
"system error: nwipe_options.autonuke (should be 0 or 1) is invalid, nwipe_options.autonuke=%i !? \n",
nwipe_options.autonuke );
}
/* Initialise some of the variables in the drive contexts
*/
for( i = 0; i < nwipe_enumerated; i++ )
{
/* Set the PRNG implementation, which must always come after the function nwipe_gui_select ! */
@@ -346,6 +786,9 @@ int main( int argc, char** argv )
/* Initialise the wipe result value */
c1[i]->result = 0;
/* Initialise the variable that tracks how much of the drive has been erased */
c1[i]->bytes_erased = 0;
}
/* Pass the number selected to the struct for other threads */
@@ -385,8 +828,82 @@ int main( int argc, char** argv )
/* Initialise the wipe_status flag, -1 = wipe not yet started */
c2[i]->wipe_status = -1;
/* Open the file for reads and writes. */
c2[i]->device_fd = open( c2[i]->device_name, O_RDWR );
/* Open the file for reads and writes, honoring the configured I/O mode. */
int open_flags = O_RDWR;
#ifdef NWIPE_USE_DIRECT_IO
/*
* Decide whether to request O_DIRECT based on the runtime I/O mode:
* auto -> try O_DIRECT, fall back to cached I/O if needed
* direct -> force O_DIRECT, fail hard if not supported
* cached -> do not request O_DIRECT at all
*/
if( nwipe_options.io_mode == NWIPE_IO_MODE_DIRECT || nwipe_options.io_mode == NWIPE_IO_MODE_AUTO )
{
open_flags |= O_DIRECT;
}
#endif
c2[i]->device_fd = open( c2[i]->device_name, open_flags );
#ifdef NWIPE_USE_DIRECT_IO
if( c2[i]->device_fd < 0 && ( errno == EINVAL || errno == EOPNOTSUPP ) )
{
if( nwipe_options.io_mode == NWIPE_IO_MODE_DIRECT )
{
/*
* User explicitly requested direct I/O: do not silently
* fall back. Mark the device as unusable and continue.
*/
nwipe_perror( errno, __FUNCTION__, "open" );
nwipe_log( NWIPE_LOG_FATAL,
"O_DIRECT requested via --directio but not supported on '%s'.",
c2[i]->device_name );
c2[i]->select = NWIPE_SELECT_DISABLED;
continue;
}
else if( nwipe_options.io_mode == NWIPE_IO_MODE_AUTO )
{
/*
* Auto mode: transparently fall back to cached I/O and
* log a warning.
*/
nwipe_log( NWIPE_LOG_WARNING,
"O_DIRECT not supported on '%s', falling back to cached I/O.",
c2[i]->device_name );
open_flags &= ~O_DIRECT;
c2[i]->device_fd = open( c2[i]->device_name, open_flags );
}
}
if( c2[i]->device_fd >= 0 )
{
const char* io_desc;
if( open_flags & O_DIRECT )
{
io_desc = "direct I/O (O_DIRECT)";
c2[i]->io_mode = NWIPE_IO_MODE_DIRECT;
}
else
{
io_desc = "cached I/O";
c2[i]->io_mode = NWIPE_IO_MODE_CACHED;
}
nwipe_log( NWIPE_LOG_NOTICE, "Using %s on device '%s'.", io_desc, c2[i]->device_name );
}
#endif /* NWIPE_USE_DIRECT_IO */
/* Check the open() result (after any fallback logic). */
if( c2[i]->device_fd < 0 )
{
nwipe_perror( errno, __FUNCTION__, "open" );
nwipe_log( NWIPE_LOG_WARNING, "Unable to open device '%s'.", c2[i]->device_name );
c2[i]->select = NWIPE_SELECT_DISABLED;
continue;
}
/* Check the open() result. */
if( c2[i]->device_fd < 0 )
@@ -431,7 +948,8 @@ int main( int argc, char** argv )
nwipe_log( NWIPE_LOG_NOTICE, "%s has serial number %s", c2[i]->device_name, c2[i]->device_serial_no );
}
/* Do sector size and block size checking. */
/* Do sector size and block size checking. I don't think this does anything useful as logical/Physical
* sector sizes are obtained by libparted in check.c */
if( ioctl( c2[i]->device_fd, BLKSSZGET, &c2[i]->device_sector_size ) == 0 )
{
@@ -702,6 +1220,42 @@ int main( int argc, char** argv )
thread_timeout_counter--;
sleep( 1 );
}
/* Now all the wipe threads have finished, we can issue a terminate_signal = 1
* which will cause the temperature update thread to terminate, this is necessary
* because in gui mode the terminate_signal is set when the user presses a key to
* exit on completion of all the wipes, however in non gui mode that code isn't
* active (being in the gui section) so here we need to set the terminate signal
* specifically for a completed wipes/s just for non gui mode.
*/
terminate_signal = 1;
/* Kill the temperature update thread */
if( nwipe_temperature_thread )
{
if( nwipe_options.verbose )
{
nwipe_log( NWIPE_LOG_INFO, "Cancelling the temperature thread." );
}
/* We don't want to use pthread_cancel as our temperature thread is aware of the control-c
* signal and will exit itself we just join the temperature thread and wait for confirmation
*/
r = pthread_join( nwipe_temperature_thread, NULL );
if( r != 0 )
{
nwipe_log( NWIPE_LOG_WARNING,
"main()>pthread_join():Error when waiting for temperature thread to cancel." );
}
else
{
if( nwipe_options.verbose )
{
nwipe_log( NWIPE_LOG_INFO, "temperature thread has been cancelled" );
}
}
}
if( nwipe_options.verbose )
{
for( i = 0; i < nwipe_selected; i++ )
@@ -936,11 +1490,12 @@ void* signal_hand( void* ptr )
int cleanup()
{
int i;
extern int log_elements_displayed;
extern int log_elements_allocated;
extern int log_elements_displayed; // initialised and found in logging.c
extern int log_elements_allocated; // initialised and found in logging.c
extern char** log_lines;
extern config_t nwipe_cfg;
/* Print the logs held in memory. */
/* Print the logs held in memory to the console */
for( i = log_elements_displayed; i < log_elements_allocated; i++ )
{
printf( "%s\n", log_lines[i] );
@@ -958,7 +1513,10 @@ int cleanup()
free( log_lines );
}
/* TODO: All other cleanup required */
/* Deallocate libconfig resources */
config_destroy( &nwipe_cfg );
/* TODO: Any other cleanup required ? */
return 0;
}

View File

@@ -36,15 +36,6 @@ void* signal_hand( void* );
#define _FILE_OFFSET_BITS 64
#endif
/* workaround for Fedora */
#ifndef off64_t
#ifndef off_t
#define off64_t int64_t
#else
#define off64_t off_t
#endif
#endif
/* Busybox headers. */
#ifdef BB_VER
#include "busybox.h"
@@ -68,6 +59,15 @@ void* signal_hand( void* );
#include <time.h>
#include <unistd.h>
/* workaround for Fedora */
#ifndef off64_t
#ifndef off_t
#define off64_t int64_t
#else
#define off64_t off_t
#endif
#endif
/*#include "config.h"*/
/* System errors. */

View File

@@ -27,6 +27,9 @@
#include "options.h"
#include "logging.h"
#include "version.h"
#include "conf.h"
#include "cpu_features.h"
#include "libconfig.h"
/* The global options struct. */
nwipe_options_t nwipe_options;
@@ -41,6 +44,13 @@ int nwipe_options_parse( int argc, char** argv )
extern nwipe_prng_t nwipe_twister;
extern nwipe_prng_t nwipe_isaac;
extern nwipe_prng_t nwipe_isaac64;
extern nwipe_prng_t nwipe_add_lagg_fibonacci_prng;
extern nwipe_prng_t nwipe_xoroshiro256_prng;
extern nwipe_prng_t nwipe_aes_ctr_prng;
extern config_t nwipe_cfg;
config_setting_t* setting;
const char* user_defined_tag;
/* The getopt() result holder. */
int nwipe_opt;
@@ -54,7 +64,12 @@ int nwipe_options_parse( int argc, char** argv )
int i;
/* The list of acceptable short options. */
char nwipe_options_short[] = "Vvhl:m:p:qr:e:";
char nwipe_options_short[] = "Vvhl:P:m:p:qr:e:";
/* Used when reading value fron nwipe.conf */
const char* read_value = NULL;
int ret;
/* The list of acceptable long options. */
static struct option nwipe_options_long[] = {
@@ -65,7 +80,7 @@ int nwipe_options_parse( int argc, char** argv )
{ "autopoweroff", no_argument, 0, 0 },
/* A GNU standard option. Corresponds to the 'h' short option. */
{ "help", no_argument, 0, 'h' },
{ "help", no_argument, 0, 0 },
/* The wipe method. Corresponds to the 'm' short option. */
{ "method", required_argument, 0, 'm' },
@@ -73,11 +88,16 @@ int nwipe_options_parse( int argc, char** argv )
/* Log file. Corresponds to the 'l' short option. */
{ "logfile", required_argument, 0, 'l' },
/* PDFreport path. Corresponds to the 'P' short option. */
{ "PDFreportpath", required_argument, 0, 'P' },
/* Exclude devices, comma separated list */
{ "exclude", required_argument, 0, 'e' },
/* The Pseudo Random Number Generator. */
{ "prng", required_argument, 0, 'p' },
{ "prng-benchmark", no_argument, 0, 0 },
{ "prng-bench-seconds", required_argument, 0, 0 },
/* The number of times to run the method. */
{ "rounds", required_argument, 0, 'r' },
@@ -98,7 +118,7 @@ int nwipe_options_parse( int argc, char** argv )
{ "nogui", no_argument, 0, 0 },
/* Whether to anonymize the serial numbers. */
{ "quiet", no_argument, 0, 'q' },
{ "quiet", no_argument, 0, 0 },
/* A flag to indicate whether the devices would be opened in sync mode. */
{ "sync", required_argument, 0, 0 },
@@ -106,11 +126,18 @@ int nwipe_options_parse( int argc, char** argv )
/* Verify that wipe patterns are being written to the device. */
{ "verify", required_argument, 0, 0 },
/* Display program version. */
{ "verbose", no_argument, 0, 'v' },
/* I/O mode selection: auto/direct/cached. */
{ "directio", no_argument, 0, 0 },
{ "cachedio", no_argument, 0, 0 },
/* Enables a field on the PDF that holds a tag that identifies the host computer */
{ "pdftag", no_argument, 0, 0 },
/* Display program version. */
{ "version", no_argument, 0, 'V' },
{ "verbose", no_argument, 0, 0 },
/* Display program version. */
{ "version", no_argument, 0, 0 },
/* Requisite padding for getopt(). */
{ 0, 0, 0, 0 } };
@@ -118,8 +145,31 @@ int nwipe_options_parse( int argc, char** argv )
/* Set default options. */
nwipe_options.autonuke = 0;
nwipe_options.autopoweroff = 0;
nwipe_options.method = &nwipe_dodshort;
nwipe_options.prng = ( sizeof( unsigned long int ) >= 8 ) ? &nwipe_isaac64 : &nwipe_isaac;
nwipe_options.method = &nwipe_random;
nwipe_options.prng_auto = 1; /* by default the PRNG is selected through the benchmark selection */
nwipe_options.prng_benchmark_only = 0;
nwipe_options.prng_bench_seconds = 1.0; /* default for interactive / manual */
/*
* Determines and sets the default PRNG based on AES-NI support and system architecture.
* It selects AES-CTR PRNG if AES-NI is supported, xoroshiro256 for 64-bit systems without AES-NI,
* and add lagged Fibonacci for 32-bit systems.
*/
if( has_aes_ni() )
{
nwipe_options.prng = &nwipe_aes_ctr_prng;
}
else if( sizeof( unsigned long int ) >= 8 )
{
nwipe_options.prng = &nwipe_xoroshiro256_prng;
nwipe_log( NWIPE_LOG_WARNING, "CPU doesn't support AES New Instructions, opting for XORoshiro-256 instead." );
}
else
{
nwipe_options.prng = &nwipe_add_lagg_fibonacci_prng;
}
nwipe_options.rounds = 1;
nwipe_options.noblank = 0;
nwipe_options.nousb = 0;
@@ -130,9 +180,142 @@ int nwipe_options_parse( int argc, char** argv )
nwipe_options.sync = DEFAULT_SYNC_RATE;
nwipe_options.verbose = 0;
nwipe_options.verify = NWIPE_VERIFY_LAST;
nwipe_options.io_mode = NWIPE_IO_MODE_AUTO; /* Default: auto-select I/O mode. */
nwipe_options.PDF_toggle_host_info = 0; /* Default: host visibility on PDF disabled */
nwipe_options.PDFtag = 0;
memset( nwipe_options.logfile, '\0', sizeof( nwipe_options.logfile ) );
memset( nwipe_options.PDFreportpath, '\0', sizeof( nwipe_options.PDFreportpath ) );
strncpy( nwipe_options.PDFreportpath, ".", 2 );
/* Initialise each of the strings in the excluded drives array */
/*
* Read PDF Enable/Disable settings from nwipe.conf if available
*/
if( ( ret = nwipe_conf_read_setting( "PDF_Certificate.PDF_Enable", &read_value ) ) )
{
/* error occurred */
nwipe_log( NWIPE_LOG_ERROR,
"nwipe_conf_read_setting():Error reading PDF_Certificate.PDF_Enable from nwipe.conf, ret code %i",
ret );
/* Use default values */
nwipe_options.PDF_enable = 1;
}
else
{
if( !strcmp( read_value, "ENABLED" ) )
{
nwipe_options.PDF_enable = 1;
}
else
{
if( !strcmp( read_value, "DISABLED" ) )
{
nwipe_options.PDF_enable = 0;
}
else
{
// error occurred
nwipe_log(
NWIPE_LOG_ERROR,
"PDF_Certificate.PDF_Enable in nwipe.conf returned a value that was neither ENABLED or DISABLED" );
nwipe_options.PDF_enable = 1; // Default to Enabled
}
}
}
/*
* Read PDF host visibility settings from nwipe.conf if available
*/
if( ( ret = nwipe_conf_read_setting( "PDF_Certificate.PDF_Host_Visibility", &read_value ) ) )
{
/* error occurred */
nwipe_log(
NWIPE_LOG_ERROR,
"nwipe_conf_read_setting():Error reading PDF_Certificate.PDF_toggle_host_info from nwipe.conf, ret code %i",
ret );
nwipe_options.PDF_toggle_host_info = 0; /* Disable host visibility on PDF */
}
else
{
if( !strcmp( read_value, "ENABLED" ) )
{
nwipe_options.PDF_toggle_host_info = 1;
}
else
{
if( !strcmp( read_value, "DISABLED" ) )
{
nwipe_options.PDF_toggle_host_info = 0;
}
else
{
// error occurred
nwipe_log( NWIPE_LOG_ERROR,
"PDF_Certificate.PDF_toggle_host_info in nwipe.conf returned a value that was neither "
"ENABLED or DISABLED" );
nwipe_options.PDF_toggle_host_info = 0; // Default to disabled
}
}
}
/*
* Read PDF tag Enable/Disable settings from nwipe.conf if available
*/
setting = config_lookup( &nwipe_cfg, "PDF_Certificate" );
if( config_setting_lookup_string( setting, "User_Defined_Tag", &user_defined_tag ) )
{
if( user_defined_tag[0] != 0 )
{
nwipe_options.PDFtag = 1;
}
else
{
nwipe_options.PDFtag = 0;
}
}
/*
* PDF Preview enable/disable
*/
if( ( ret = nwipe_conf_read_setting( "PDF_Certificate.PDF_Preview", &read_value ) ) )
{
/* error occurred */
nwipe_log( NWIPE_LOG_ERROR,
"nwipe_conf_read_setting():Error reading PDF_Certificate.PDF_Preview from nwipe.conf, ret code %i",
ret );
/* Use default values */
nwipe_options.PDF_enable = 1;
}
else
{
if( !strcmp( read_value, "ENABLED" ) )
{
nwipe_options.PDF_preview_details = 1;
}
else
{
if( !strcmp( read_value, "DISABLED" ) )
{
nwipe_options.PDF_preview_details = 0;
}
else
{
/* error occurred */
nwipe_log(
NWIPE_LOG_ERROR,
"PDF_Certificate.PDF_Preview in nwipe.conf returned a value that was neither ENABLED or DISABLED" );
nwipe_options.PDF_preview_details = 1; /* Default to Enabled */
}
}
}
/*
* Initialise each of the strings in the excluded drives array
*/
for( i = 0; i < MAX_NUMBER_EXCLUDED_DRIVES; i++ )
{
nwipe_options.exclude[i][0] = 0;
@@ -156,51 +339,193 @@ int nwipe_options_parse( int argc, char** argv )
if( strcmp( nwipe_options_long[i].name, "autonuke" ) == 0 )
{
nwipe_options.autonuke = 1;
break;
/* check for the full option name, as getopt_long() allows abreviations and can lead to unintended
* consequences when the user makes a typo */
if( strcmp( argv[optind - 1], "--autonuke" ) == 0 )
{
nwipe_options.autonuke = 1;
break;
}
else
{
fprintf( stderr,
"Error: Strict command line options required, did you mean --autonuke?, you typed "
"%s.\nType `sudo nwipe --help` for options \n",
argv[optind - 1] );
exit( EINVAL );
}
}
if( strcmp( nwipe_options_long[i].name, "autopoweroff" ) == 0 )
{
nwipe_options.autopoweroff = 1;
break;
/* check for the full option name, as getopt_long() allows abreviations and can lead to unintended
* consequences when the user makes a typo */
if( strcmp( argv[optind - 1], "--autopoweroff" ) == 0 )
{
nwipe_options.autopoweroff = 1;
break;
}
else
{
fprintf( stderr,
"Error: Strict command line options required, did you mean --autopoweroff?, you typed "
"%s.\nType `sudo nwipe --help` for options \n",
argv[optind - 1] );
exit( EINVAL );
}
}
if( strcmp( nwipe_options_long[i].name, "help" ) == 0 )
{
/* check for the full option name, as getopt_long() allows abreviations and can lead to unintended
* consequences when the user makes a typo */
if( strcmp( argv[optind - 1], "--help" ) == 0 )
{
display_help();
exit( EINVAL );
}
else
{
fprintf( stderr,
"Error: Strict command line options required, did you mean --help?, you typed "
"%s.\nType `sudo nwipe --help` for options \n",
argv[optind - 1] );
exit( EINVAL );
}
}
if( strcmp( nwipe_options_long[i].name, "noblank" ) == 0 )
{
nwipe_options.noblank = 1;
break;
/* check for the full option name, as getopt_long() allows abreviations and can lead to unintended
* consequences when the user makes a typo */
if( strcmp( argv[optind - 1], "--noblank" ) == 0 )
{
nwipe_options.noblank = 1;
break;
}
else
{
fprintf( stderr,
"Error: Strict command line options required, did you mean --noblank?, you typed "
"%s.\nType `sudo nwipe --help` for options \n",
argv[optind - 1] );
exit( EINVAL );
}
}
if( strcmp( nwipe_options_long[i].name, "nousb" ) == 0 )
{
nwipe_options.nousb = 1;
break;
/* check for the full option name, as getopt_long() allows abreviations and can lead to unintended
* consequences when the user makes a typo */
if( strcmp( argv[optind - 1], "--nousb" ) == 0 )
{
nwipe_options.nousb = 1;
break;
}
else
{
fprintf( stderr,
"Error: Strict command line options required, did you mean --nousb?, you typed "
"%s.\nType `sudo nwipe --help` for options \n",
argv[optind - 1] );
exit( EINVAL );
}
}
if( strcmp( nwipe_options_long[i].name, "nowait" ) == 0 )
{
nwipe_options.nowait = 1;
break;
/* check for the full option name, as getopt_long() allows abreviations and can lead to unintended
* consequences when the user makes a typo */
if( strcmp( argv[optind - 1], "--nowait" ) == 0 )
{
nwipe_options.nowait = 1;
break;
}
else
{
fprintf( stderr,
"Error: Strict command line options required, did you mean --nowait?, you typed "
"%s.\nType `sudo nwipe --help` for options \n",
argv[optind - 1] );
exit( EINVAL );
}
}
if( strcmp( nwipe_options_long[i].name, "nosignals" ) == 0 )
{
nwipe_options.nosignals = 1;
break;
/* check for the full option name, as getopt_long() allows abreviations and can lead to unintended
* consequences when the user makes a typo */
if( strcmp( argv[optind - 1], "--nosignals" ) == 0 )
{
nwipe_options.nosignals = 1;
break;
}
else
{
fprintf( stderr,
"Error: Strict command line options required, did you mean --nosignals?, you typed "
"%s.\nType `sudo nwipe --help` for options \n",
argv[optind - 1] );
exit( EINVAL );
}
}
if( strcmp( nwipe_options_long[i].name, "nogui" ) == 0 )
{
nwipe_options.nogui = 1;
nwipe_options.nowait = 1;
break;
/* check for the full option name, as getopt_long() allows abreviations and can lead to unintended
* consequences when the user makes a typo */
if( strcmp( argv[optind - 1], "--nogui" ) == 0 )
{
nwipe_options.nogui = 1;
nwipe_options.nowait = 1;
break;
}
else
{
fprintf( stderr,
"Error: Strict command line options required, did you mean --nogui?, you typed "
"%s.\nType `sudo nwipe --help` for options \n",
argv[optind - 1] );
exit( EINVAL );
}
}
if( strcmp( nwipe_options_long[i].name, "quiet" ) == 0 )
{
/* check for the full option name, as getopt_long() allows abreviations and can lead to unintended
* consequences when the user makes a typo */
if( strcmp( argv[optind - 1], "--quiet" ) == 0 )
{
nwipe_options.quiet = 1;
break;
}
else
{
fprintf( stderr,
"Error: Strict command line options required, did you mean --quiet?, you typed "
"%s.\nType `sudo nwipe --help` for options \n",
argv[optind - 1] );
exit( EINVAL );
}
}
if( strcmp( nwipe_options_long[i].name, "verbose" ) == 0 )
{
nwipe_options.verbose = 1;
break;
/* check for the full option name, as getopt_long() allows abreviations and can lead to unintended
* consequences when the user makes a typo */
if( strcmp( argv[optind - 1], "--verbose" ) == 0 )
{
nwipe_options.verbose = 1;
break;
}
else
{
fprintf( stderr,
"Error: Strict command line options required, did you mean --verbose?, you typed "
"%s.\nType `sudo nwipe --help` for options \n",
argv[optind - 1] );
exit( EINVAL );
}
}
if( strcmp( nwipe_options_long[i].name, "sync" ) == 0 )
@@ -215,7 +540,6 @@ int nwipe_options_parse( int argc, char** argv )
if( strcmp( nwipe_options_long[i].name, "verify" ) == 0 )
{
if( strcmp( optarg, "0" ) == 0 || strcmp( optarg, "off" ) == 0 )
{
nwipe_options.verify = NWIPE_VERIFY_NONE;
@@ -239,6 +563,67 @@ int nwipe_options_parse( int argc, char** argv )
exit( EINVAL );
}
/* I/O mode selection options. */
if( strcmp( nwipe_options_long[i].name, "directio" ) == 0 )
{
/* check for the full option name, as getopt_long() allows abreviations and can lead to unintended
* consequences when the user makes a typo */
if( strcmp( argv[optind - 1], "--directio" ) == 0 )
{
nwipe_options.io_mode = NWIPE_IO_MODE_DIRECT;
break;
}
else
{
fprintf( stderr,
"Error: Strict command line options required, did you mean --directio?, you typed "
"%s.\nType `sudo nwipe --help` for options \n",
argv[optind - 1] );
exit( EINVAL );
}
}
if( strcmp( nwipe_options_long[i].name, "cachedio" ) == 0 )
{
/* check for the full option name, as getopt_long() allows abreviations and can lead to unintended
* consequences when the user makes a typo */
if( strcmp( argv[optind - 1], "--cachedio" ) == 0 )
{
nwipe_options.io_mode = NWIPE_IO_MODE_CACHED;
break;
}
else
{
fprintf( stderr,
"Error: Strict command line options required, did you mean --cachedio?, you typed "
"%s.\nType `sudo nwipe --help` for options \n",
argv[optind - 1] );
exit( EINVAL );
}
}
if( strcmp( nwipe_options_long[i].name, "pdftag" ) == 0 )
{
nwipe_options.PDFtag = 1;
break;
}
if( strcmp( nwipe_options_long[i].name, "prng-benchmark" ) == 0 )
{
nwipe_options.prng_benchmark_only = 1;
break;
}
if( strcmp( nwipe_options_long[i].name, "prng-bench-seconds" ) == 0 )
{
nwipe_options.prng_bench_seconds = atof( optarg );
if( nwipe_options.prng_bench_seconds < 0.05 )
nwipe_options.prng_bench_seconds = 0.05;
if( nwipe_options.prng_bench_seconds > 10.0 )
nwipe_options.prng_bench_seconds = 10.0;
break;
}
/* getopt_long should raise on invalid option, so we should never get here. */
exit( EINVAL );
@@ -304,6 +689,16 @@ int nwipe_options_parse( int argc, char** argv )
nwipe_options.method = &nwipe_is5enh;
break;
}
if( strcmp( optarg, "bruce7" ) == 0 )
{
nwipe_options.method = &nwipe_bruce7;
break;
}
if( strcmp( optarg, "bmb" ) == 0 )
{
nwipe_options.method = &nwipe_bmb;
break;
}
/* Else we do not know this wipe method. */
fprintf( stderr, "Error: Unknown wipe method '%s'.\n", optarg );
@@ -315,6 +710,29 @@ int nwipe_options_parse( int argc, char** argv )
strncpy( nwipe_options.logfile, optarg, sizeof( nwipe_options.logfile ) );
break;
case 'P': /* PDFreport path option. */
nwipe_options.PDFreportpath[strlen( optarg )] = '\0';
strncpy( nwipe_options.PDFreportpath, optarg, sizeof( nwipe_options.PDFreportpath ) );
/* Command line options will override what's in nwipe.conf */
if( strcmp( nwipe_options.PDFreportpath, "noPDF" ) == 0 )
{
nwipe_options.PDF_enable = 0;
nwipe_conf_update_setting( "PDF_Certificate.PDF_Enable", "DISABLED" );
}
else
{
if( strcmp( nwipe_options.PDFreportpath, "." ) )
{
/* and if the user has specified a PDF path then enable PDF */
nwipe_options.PDF_enable = 1;
nwipe_conf_update_setting( "PDF_Certificate.PDF_Enable", "ENABLED" );
}
}
break;
case 'e': /* exclude drives option */
idx_drive_chr = 0;
@@ -354,16 +772,43 @@ int nwipe_options_parse( int argc, char** argv )
}
}
}
if( idx_drive == MAX_NUMBER_EXCLUDED_DRIVES )
{
fprintf(
stderr,
"The number of excluded drives has reached the programs configured limit, aborting\n" );
exit( 130 );
}
}
break;
case 'h': /* Display help. */
display_help();
exit( EINVAL );
break;
case 'p': /* PRNG option. */
/* Default behaviour is auto now, but allow explicit opt-out */
if( strcmp( optarg, "auto" ) == 0 )
{
nwipe_options.prng_auto = 1;
/* keep current default as fallback until autoselect runs */
break;
}
/* NEW: disable auto and keep compiled-in default selection */
if( strcmp( optarg, "default" ) == 0 || strcmp( optarg, "manual" ) == 0 )
{
nwipe_options.prng_auto = 0;
/* keep nwipe_options.prng as chosen by CPU heuristics above */
break;
}
/* Any explicit PRNG selection implies auto off */
nwipe_options.prng_auto = 0;
if( strcmp( optarg, "mersenne" ) == 0 || strcmp( optarg, "twister" ) == 0 )
{
nwipe_options.prng = &nwipe_twister;
@@ -382,7 +827,34 @@ int nwipe_options_parse( int argc, char** argv )
break;
}
/* Else we do not know this PRNG. */
if( strcmp( optarg, "add_lagg_fibonacci_prng" ) == 0 )
{
nwipe_options.prng = &nwipe_add_lagg_fibonacci_prng;
break;
}
if( strcmp( optarg, "xoroshiro256_prng" ) == 0 )
{
nwipe_options.prng = &nwipe_xoroshiro256_prng;
break;
}
if( strcmp( optarg, "aes_ctr_prng" ) == 0 )
{
if( has_aes_ni() )
{
nwipe_options.prng = &nwipe_aes_ctr_prng;
}
else
{
fprintf( stderr,
"Error: aes_ctr_prng requires AES-NI on this build, "
"but your CPU does not support AES-NI.\n" );
exit( EINVAL );
}
break;
}
fprintf( stderr, "Error: Unknown prng '%s'.\n", optarg );
exit( EINVAL );
@@ -414,13 +886,19 @@ int nwipe_options_parse( int argc, char** argv )
default:
/* Bogus command line argument. */
display_help();
exit( EINVAL );
} /* method */
} /* command line options */
/* Disable blanking for ops2 and verify methods */
if( nwipe_options.method == &nwipe_ops2 || nwipe_options.method == &nwipe_verify_zero
|| nwipe_options.method == &nwipe_verify_one )
{
nwipe_options.noblank = 1;
}
/* Return the number of options that were processed. */
return optind;
}
@@ -430,6 +908,9 @@ void nwipe_options_log( void )
extern nwipe_prng_t nwipe_twister;
extern nwipe_prng_t nwipe_isaac;
extern nwipe_prng_t nwipe_isaac64;
extern nwipe_prng_t nwipe_add_lagg_fibonacci_prng;
extern nwipe_prng_t nwipe_xoroshiro256_prng;
extern nwipe_prng_t nwipe_aes_ctr_prng;
/**
* Prints a manifest of options to the log.
@@ -437,7 +918,7 @@ void nwipe_options_log( void )
nwipe_log( NWIPE_LOG_NOTICE, "Program options are set as follows..." );
if( nwipe_options.autonuke )
if( nwipe_options.autonuke == 1 )
{
nwipe_log( NWIPE_LOG_NOTICE, " autonuke = %i (on)", nwipe_options.autonuke );
}
@@ -481,23 +962,29 @@ void nwipe_options_log( void )
{
nwipe_log( NWIPE_LOG_NOTICE, " prng = Mersenne Twister" );
}
else if( nwipe_options.prng == &nwipe_add_lagg_fibonacci_prng )
{
nwipe_log( NWIPE_LOG_NOTICE, " prng = Lagged Fibonacci generator" );
}
else if( nwipe_options.prng == &nwipe_xoroshiro256_prng )
{
nwipe_log( NWIPE_LOG_NOTICE, " prng = XORoshiro-256" );
}
else if( nwipe_options.prng == &nwipe_aes_ctr_prng )
{
nwipe_log( NWIPE_LOG_NOTICE, " prng = AES-CTR New Instructions (EXPERIMENTAL!)" );
}
else if( nwipe_options.prng == &nwipe_isaac )
{
nwipe_log( NWIPE_LOG_NOTICE, " prng = Isaac" );
}
else if( nwipe_options.prng == &nwipe_isaac64 )
{
nwipe_log( NWIPE_LOG_NOTICE, " prng = Isaac64" );
}
else
{
if( nwipe_options.prng == &nwipe_isaac )
{
nwipe_log( NWIPE_LOG_NOTICE, " prng = Isaac" );
}
else
{
if( nwipe_options.prng == &nwipe_isaac64 )
{
nwipe_log( NWIPE_LOG_NOTICE, " prng = Isaac64" );
}
else
{
nwipe_log( NWIPE_LOG_NOTICE, " prng = Undefined" );
}
}
nwipe_log( NWIPE_LOG_NOTICE, " prng = Undefined" );
}
nwipe_log( NWIPE_LOG_NOTICE, " method = %s", nwipe_method_label( nwipe_options.method ) );
@@ -557,7 +1044,13 @@ void display_help()
puts( " (default: last)" );
puts( " off - Do not verify" );
puts( " last - Verify after the last pass" );
puts( " all - Verify every pass\n" );
puts( " all - Verify every pass" );
puts( " " );
puts( " --directio Force direct I/O (O_DIRECT); fail if not supported" );
puts( " --cachedio Force kernel cached I/O; never attempt O_DIRECT" );
puts( " --io-mode=MODE I/O mode: auto (default), direct, cached\n" );
puts( " Please mind that HMG IS5 enhanced always verifies the" );
puts( " last (PRNG) pass regardless of this option.\n" );
puts( " -m, --method=METHOD The wiping method. See man page for more details." );
puts( " (default: dodshort)" );
puts( " dod522022m / dod - 7 pass DOD 5220.22-M method" );
@@ -568,29 +1061,56 @@ void display_help()
puts( " zero / quick - Overwrite with zeros" );
puts( " one - Overwrite with ones (0xFF)" );
puts( " verify_zero - Verifies disk is zero filled" );
puts( " verify_one - Verifies disk is 0xFF filled\n" );
puts( " verify_one - Verifies disk is 0xFF filled" );
puts( " is5enh - HMG IS5 enhanced\n" );
puts( " bruce7 - Schneier Bruce 7-pass mixed pattern\n" );
puts( " bmb - BMB21-2019 mixed pattern\n" );
puts( " -l, --logfile=FILE Filename to log to. Default is STDOUT\n" );
puts( " -p, --prng=METHOD PRNG option (mersenne|twister|isaac|isaac64)\n" );
puts( " -q, --quiet Anonymize logs and the GUI by removing unique data, i.e." );
puts( " serial numbers, LU WWN Device ID, and SMBIOS/DMI data" );
puts( " XXXXXX = S/N exists, ????? = S/N not obtainable\n" );
puts( " -r, --rounds=NUM Number of times to wipe the device using the selected" );
puts( " method (default: 1)\n" );
puts( " --noblank Do NOT blank disk after wipe" );
puts( " (default is to complete a final blank pass)\n" );
puts( " --nowait Do NOT wait for a key before exiting" );
puts( " (default is to wait)\n" );
puts( " --nosignals Do NOT allow signals to interrupt a wipe" );
puts( " (default is to allow)\n" );
puts( " --nogui Do NOT show the GUI interface. Automatically invokes" );
puts( " the nowait option. Must be used with the --autonuke" );
puts( " option. Send SIGUSR1 to log current stats\n" );
puts( " --nousb Do NOT show or wipe any USB devices whether in GUI" );
puts( " mode, --nogui or --autonuke modes.\n" );
puts( " -e, --exclude=DEVICES Up to ten comma separated devices to be excluded" );
puts( " --exclude=/dev/sdc" );
puts( " --exclude=/dev/sdc,/dev/sdd" );
puts( " --exclude=/dev/sdc,/dev/sdd,/dev/mapper/cryptswap1\n" );
puts( " -P, --PDFreportpath=PATH Path to write PDF reports to. Default is \".\"" );
puts( " If set to \"noPDF\" no PDF reports are written.\n" );
puts( " -p, --prng=METHOD PRNG option "
"(mersenne|twister|isaac|isaac64|add_lagg_fibonacci_prng|xoroshiro256_prng|aes_ctr_prng)\n" );
puts( " --prng=auto (default)" );
puts( " Automatically benchmark all available PRNGs at startup and" );
puts( " select the fastest one for the current hardware." );
puts( "" );
puts( " --prng=default" );
puts( " Disable auto-selection and use the built-in default PRNG choice" );
puts( " (CPU-based heuristic; no benchmarking)." );
puts( " Alias: --prng=manual" );
puts( "" );
puts( " --prng-benchmark" );
puts( " Run a RAM-only PRNG throughput benchmark and exit." );
puts( " Prints a sorted leaderboard (MB/s). No wipe is performed." );
puts( "" );
puts( " --prng-bench-seconds=N" );
puts( " Seconds per PRNG during benchmarking (default: 1.0)." );
puts( " For --prng=auto this is automatically reduced unless set." );
puts( " -q, --quiet Anonymize logs and the GUI by removing unique data, i.e." );
puts( " serial numbers, LU WWN Device ID, and SMBIOS/DMI data." );
puts( " XXXXXX = S/N exists, ????? = S/N not obtainable\n" );
puts( " -r, --rounds=NUM Number of times to wipe the device using the selected" );
puts( " method. (default: 1)\n" );
puts( " --noblank Do NOT blank disk after wipe." );
puts( " (default is to complete a final blank pass)\n" );
puts( " --nowait Do NOT wait for a key before exiting." );
puts( " (default is to wait)\n" );
puts( " --nosignals Do NOT allow signals to interrupt a wipe." );
puts( " (default is to allow)\n" );
puts( " --nogui Do NOT show the GUI interface. Automatically invokes" );
puts( " the nowait option. Must be used with the --autonuke" );
puts( " option. Send SIGUSR1 to log current stats.\n" );
puts( " --nousb Do NOT show or wipe any USB devices whether in GUI" );
puts( " mode, --nogui or --autonuke modes.\n" );
puts( " --pdftag Enables a field on the PDF that holds a tag that\n" );
puts( " identifies the host computer\n" );
puts( " -e, --exclude=DEVICES Up to ten comma separated devices to be excluded." );
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 );
}

View File

@@ -23,8 +23,9 @@
#ifndef OPTIONS_H_
#define OPTIONS_H_
#include "method.h"
/* Program knobs. */
#define NWIPE_KNOB_ENTROPY "/dev/urandom"
#define NWIPE_KNOB_IDENTITY_SIZE 512
#define NWIPE_KNOB_LABEL_SIZE 128
#define NWIPE_KNOB_LOADAVG "/proc/loadavg"
@@ -35,9 +36,11 @@
#define NWIPE_KNOB_SCSI "/proc/scsi/scsi"
#define NWIPE_KNOB_SLEEP 1
#define NWIPE_KNOB_STAT "/proc/stat"
#define MAX_NUMBER_EXCLUDED_DRIVES 10
#define MAX_NUMBER_EXCLUDED_DRIVES 32
#define MAX_DRIVE_PATH_LENGTH 200 // e.g. /dev/sda is only 8 characters long, so 200 should be plenty.
#define DEFAULT_SYNC_RATE 100000
#define PATHNAME_MAX 2048
#define NWIPE_USE_DIRECT_IO
/* Function prototypes for loading options from the environment and command line. */
int nwipe_options_parse( int argc, char** argv );
@@ -55,16 +58,26 @@ typedef struct
int nowait; // Do not wait for a final key before exiting.
int nosignals; // Do not allow signals to interrupt a wipe.
int nogui; // Do not show the GUI.
int prng_auto; /* 1 = auto-select fastest PRNG at startup */
int prng_benchmark_only; /* 1 = run PRNG benchmark and exit (nogui-friendly) */
double prng_bench_seconds; /* seconds per PRNG (default e.g. 0.25 for auto, 1.0 for manual) */
char* banner; // The product banner shown on the top line of the screen.
void* method; // A function pointer to the wipe method that will be used.
char logfile[FILENAME_MAX]; // The filename to log the output to.
char PDFreportpath[PATHNAME_MAX]; // The path to write the PDF report to.
char exclude[MAX_NUMBER_EXCLUDED_DRIVES][MAX_DRIVE_PATH_LENGTH]; // Drives excluded from the search.
nwipe_prng_t* prng; // The pseudo random number generator implementation.
nwipe_prng_t* prng; // The pseudo random number generator implementation. pointer to the function.
int quiet; // Anonymize serial numbers
int rounds; // The number of times that the wipe method should be called.
int sync; // A flag to indicate whether and how often writes should be sync'd.
int verbose; // Make log more verbose
int PDF_enable; // 0=PDF creation disabled, 1=PDF creation enabled
int PDF_preview_details; // 0=Disable preview Org/Cust/date/time before drive selection, 1=Enable Preview
int PDF_toggle_host_info; // 0=Disable visibility of host Info on PDF. UUID & S/N
int PDFtag; // Enable display of hostID, such as UUID or serial no. on PDF report.
nwipe_verify_t verify; // A flag to indicate whether writes should be verified.
nwipe_io_mode_t io_mode; // Global runtime I/O mode selection (auto/direct/cached), note in auto mode each
// drive may use a different I/O mode if directIO isn't supported on a given drive.
} nwipe_options_t;
extern nwipe_options_t nwipe_options;

File diff suppressed because it is too large Load Diff

View File

@@ -25,18 +25,40 @@
#include "mt19937ar-cok/mt19937ar-cok.h"
#include "isaac_rand/isaac_rand.h"
#include "isaac_rand/isaac64.h"
#include "alfg/add_lagg_fibonacci_prng.h" //Lagged Fibonacci generator prototype
#include "xor/xoroshiro256_prng.h" //XORoshiro-256 prototype
#include "aes/aes_ctr_prng.h" // AES-NI prototype
nwipe_prng_t nwipe_twister = { "Mersenne Twister (mt19937ar-cok)", nwipe_twister_init, nwipe_twister_read };
nwipe_prng_t nwipe_twister = { "Mersenne Twister", nwipe_twister_init, nwipe_twister_read };
nwipe_prng_t nwipe_isaac = { "ISAAC (rand.c 20010626)", nwipe_isaac_init, nwipe_isaac_read };
nwipe_prng_t nwipe_isaac64 = { "ISAAC-64 (isaac64.c)", nwipe_isaac64_init, nwipe_isaac64_read };
nwipe_prng_t nwipe_isaac = { "ISAAC", nwipe_isaac_init, nwipe_isaac_read };
nwipe_prng_t nwipe_isaac64 = { "ISAAC-64", nwipe_isaac64_init, nwipe_isaac64_read };
/* ALFG PRNG Structure */
nwipe_prng_t nwipe_add_lagg_fibonacci_prng = { "Lagged Fibonacci",
nwipe_add_lagg_fibonacci_prng_init,
nwipe_add_lagg_fibonacci_prng_read };
/* XOROSHIRO-256 PRNG Structure */
nwipe_prng_t nwipe_xoroshiro256_prng = { "XORoshiro-256", nwipe_xoroshiro256_prng_init, nwipe_xoroshiro256_prng_read };
/* AES-CTR-NI PRNG Structure */
nwipe_prng_t nwipe_aes_ctr_prng = { "AES-CTR (Kernel)", nwipe_aes_ctr_prng_init, nwipe_aes_ctr_prng_read };
static const nwipe_prng_t* all_prngs[] = {
&nwipe_twister,
&nwipe_isaac,
&nwipe_isaac64,
&nwipe_add_lagg_fibonacci_prng,
&nwipe_xoroshiro256_prng,
&nwipe_aes_ctr_prng,
};
/* Print given number of bytes from unsigned integer number to a byte stream buffer starting with low-endian. */
static inline void u32_to_buffer( u8* restrict buffer, u32 val, const int len )
{
for( int i = 0; i < len; ++i )
{
buffer[i] = ( u8 )( val & 0xFFUL );
buffer[i] = (u8) ( val & 0xFFUL );
val >>= 8;
}
}
@@ -44,7 +66,7 @@ static inline void u64_to_buffer( u8* restrict buffer, u64 val, const int len )
{
for( int i = 0; i < len; ++i )
{
buffer[i] = ( u8 )( val & 0xFFULL );
buffer[i] = (u8) ( val & 0xFFULL );
val >>= 8;
}
}
@@ -250,3 +272,696 @@ int nwipe_isaac64_read( NWIPE_PRNG_READ_SIGNATURE )
return 0;
}
/* Implementation of Lagged Fibonacci generator a lot of random numbers */
int nwipe_add_lagg_fibonacci_prng_init( NWIPE_PRNG_INIT_SIGNATURE )
{
if( *state == NULL )
{
nwipe_log( NWIPE_LOG_NOTICE, "Initialising Lagged Fibonacci generator PRNG" );
*state = malloc( sizeof( add_lagg_fibonacci_state_t ) );
}
add_lagg_fibonacci_init(
(add_lagg_fibonacci_state_t*) *state, (uint64_t*) ( seed->s ), seed->length / sizeof( uint64_t ) );
return 0;
}
/* Implementation of XORoroshiro256 algorithm to provide high-quality, but a lot of random numbers */
int nwipe_xoroshiro256_prng_init( NWIPE_PRNG_INIT_SIGNATURE )
{
nwipe_log( NWIPE_LOG_NOTICE, "Initialising XORoroshiro-256 PRNG" );
if( *state == NULL )
{
/* This is the first time that we have been called. */
*state = malloc( sizeof( xoroshiro256_state_t ) );
}
xoroshiro256_init( (xoroshiro256_state_t*) *state, (uint64_t*) ( seed->s ), seed->length / sizeof( uint64_t ) );
return 0;
}
int nwipe_add_lagg_fibonacci_prng_read( NWIPE_PRNG_READ_SIGNATURE )
{
u8* restrict bufpos = buffer;
size_t words = count / SIZE_OF_ADD_LAGG_FIBONACCI_PRNG;
/* Loop to fill the buffer with blocks directly from the Fibonacci algorithm */
for( size_t ii = 0; ii < words; ++ii )
{
add_lagg_fibonacci_genrand_uint256_to_buf( (add_lagg_fibonacci_state_t*) *state, bufpos );
bufpos += SIZE_OF_ADD_LAGG_FIBONACCI_PRNG; // Move to the next block
}
/* Handle remaining bytes if count is not a multiple of SIZE_OF_ADD_LAGG_FIBONACCI_PRNG */
const size_t remain = count % SIZE_OF_ADD_LAGG_FIBONACCI_PRNG;
if( remain > 0 )
{
unsigned char temp_output[16]; // Temporary buffer for the last block
add_lagg_fibonacci_genrand_uint256_to_buf( (add_lagg_fibonacci_state_t*) *state, temp_output );
// Copy the remaining bytes
memcpy( bufpos, temp_output, remain );
}
return 0; // Success
}
int nwipe_xoroshiro256_prng_read( NWIPE_PRNG_READ_SIGNATURE )
{
u8* restrict bufpos = buffer;
size_t words = count / SIZE_OF_XOROSHIRO256_PRNG;
/* Loop to fill the buffer with blocks directly from the XORoshiro256 algorithm */
for( size_t ii = 0; ii < words; ++ii )
{
xoroshiro256_genrand_uint256_to_buf( (xoroshiro256_state_t*) *state, bufpos );
bufpos += SIZE_OF_XOROSHIRO256_PRNG; // Move to the next block
}
/* Handle remaining bytes if count is not a multiple of SIZE_OF_XOROSHIRO256_PRNG */
const size_t remain = count % SIZE_OF_XOROSHIRO256_PRNG;
if( remain > 0 )
{
unsigned char temp_output[16]; // Temporary buffer for the last block
xoroshiro256_genrand_uint256_to_buf( (xoroshiro256_state_t*) *state, temp_output );
// Copy the remaining bytes
memcpy( bufpos, temp_output, remain );
}
return 0; // Success
}
/**
* @brief Initialize the AES-CTR PRNG state for this thread.
*
* @details
* Initializes the thread-local PRNG based on the supplied seed and resets the
* ring-buffer prefetch cache. The underlying AES-CTR implementation uses a
* persistent AF_ALG operation socket per thread, opened lazily by
* aes_ctr_prng_init(). The public state only stores a 128-bit counter while
* the kernel keeps the expanded AES key schedule.
*
* @param[in,out] state Pointer to an opaque PRNG state handle. If `*state` is
* `NULL`, this function allocates it with `calloc()`.
* @param[in] seed Seed material (must contain at least 32 bytes).
* @param[in] ... Remaining parameters as defined by NWIPE_PRNG_INIT_SIGNATURE.
*
* @note
* The ring is intentionally left empty to keep init fast. Callers may choose to
* "prefill" by invoking refill_stash_thread_local(*state, SIZE_OF_AES_CTR_PRNG)
* once to amortize first-use latency for tiny reads.
*
* @retval 0 Success.
* @retval -1 Allocation or initialization failure (already logged).
*/
/*
* High-throughput wrapper with a thread-local ring-buffer prefetch
* ----------------------------------------------------------------
* This glue layer implements NWIPE_PRNG_INIT / NWIPE_PRNG_READ around the
* persistent kernel-AES PRNG. It maintains a lock-free, thread-local ring
* buffer ("stash") that caches keystream blocks produced in fixed-size chunks
* (SIZE_OF_AES_CTR_PRNG; e.g., 16 KiB or 256 KiB).
*
* Rationale:
* - Nwipe frequently requests small slices (e.g., 32 B, 512 B, 4 KiB). Issuing
* one kernel call per small read would be syscall- and copy-bound.
* - By fetching larger chunks and serving small reads from the ring buffer,
* we reduce syscall rate and memory traffic and approach memcpy-limited
* throughput on modern CPUs with AES acceleration.
*
* Why a ring buffer (over a linear stash + memmove):
* - No O(n) memmove() when the buffer fills with a tail of unread bytes.
* - Constant-time head/tail updates via modulo arithmetic.
* - Better cache locality and fewer TLB/cache misses; improved prefetching.
*/
/** @def NW_THREAD_LOCAL
* @brief Portable thread-local specifier for C11 and GNU C.
*
* The ring buffer and its indices are thread-local, so no synchronization
* (locks/atomics) is required. Do not share this state across threads.
*/
#if defined( __STDC_VERSION__ ) && __STDC_VERSION__ >= 201112L
#define NW_THREAD_LOCAL _Thread_local
#else
#define NW_THREAD_LOCAL __thread
#endif
/** @def NW_ALIGN
* @brief Minimal alignment helper for hot buffers/structures.
*
* 64-byte alignment targets typical cacheline boundaries to reduce false
* sharing and improve hardware prefetch effectiveness for linear scans.
*/
#if defined( __GNUC__ ) || defined( __clang__ )
#define NW_ALIGN( N ) __attribute__( ( aligned( N ) ) )
#else
#define NW_ALIGN( N ) _Alignas( N )
#endif
/**
* @def STASH_CAPACITY
* @brief Ring capacity in bytes (power-of-two; multiple of CHUNK).
*
* @details
* Defaults to 1 MiB. Must be:
* - a power of two (allows modulo via bitmask),
* - a multiple of SIZE_OF_AES_CTR_PRNG, so each produced chunk fits whole.
*
* @note
* Practical choices: 512 KiB … 4 MiB depending on CHUNK size and workload.
* For SIZE_OF_AES_CTR_PRNG = 256 KiB, 1 MiB yields four in-flight chunks and
* works well for nwipes small-read patterns.
*/
#ifndef STASH_CAPACITY
#define STASH_CAPACITY ( 1u << 20 ) /* 1 MiB */
#endif
#if defined( __STDC_VERSION__ ) && __STDC_VERSION__ >= 201112L
_Static_assert( ( STASH_CAPACITY & ( STASH_CAPACITY - 1 ) ) == 0, "STASH_CAPACITY must be a power of two" );
_Static_assert( ( STASH_CAPACITY % SIZE_OF_AES_CTR_PRNG ) == 0,
"STASH_CAPACITY must be a multiple of SIZE_OF_AES_CTR_PRNG" );
#endif
/** @brief Thread-local ring buffer storage for prefetched keystream. */
NW_THREAD_LOCAL static unsigned char stash[STASH_CAPACITY] NW_ALIGN( 64 );
/**
* @name Ring indices (thread-local)
* @{
* @var rb_head Next read position (consumer cursor).
* @var rb_tail Next write position (producer cursor).
* @var rb_count Number of valid bytes currently stored.
*
* @invariant
* - 0 <= rb_count <= STASH_CAPACITY
* - rb_head, rb_tail in [0, STASH_CAPACITY)
* - (rb_tail - rb_head) mod STASH_CAPACITY == rb_count
*
* @warning
* These variables are TLS and must not be accessed from or shared with other
* threads. One PRNG instance per thread.
* @}
*/
NW_THREAD_LOCAL static size_t rb_head = 0; /* next byte to read */
NW_THREAD_LOCAL static size_t rb_tail = 0; /* next byte to write */
NW_THREAD_LOCAL static size_t rb_count = 0; /* occupied bytes */
/**
* @brief Free space available in the ring (bytes).
* @return Number of free bytes (0 … STASH_CAPACITY).
*/
static inline size_t rb_free( void )
{
return STASH_CAPACITY - rb_count;
}
/**
* @brief Contiguous readable bytes starting at @c rb_head (no wrap).
* @return Number of contiguous bytes available to read without split memcpy.
*/
static inline size_t rb_contig_used( void )
{
size_t to_end = STASH_CAPACITY - rb_head;
return ( rb_count < to_end ) ? rb_count : to_end;
}
/**
* @brief Contiguous writable bytes starting at @c rb_tail (no wrap).
* @return Number of contiguous bytes available to write without wrap.
*/
static inline size_t rb_contig_free( void )
{
size_t to_end = STASH_CAPACITY - rb_tail;
size_t free = rb_free();
return ( free < to_end ) ? free : to_end;
}
/**
* @brief Ensure at least @p need bytes are buffered in the ring.
*
* @details
* Production model:
* - The kernel PRNG produces keystream in fixed-size chunks
* (SIZE_OF_AES_CTR_PRNG bytes; e.g., 16 KiB or 256 KiB).
* - We only ever append *whole* chunks. If total free space is less than one
* chunk, no production occurs (non-blocking style); the caller should first
* consume data and try again.
*
* Wrap handling:
* - Fast path: if a contiguous free region of at least one chunk exists at
* @c rb_tail, generate directly into @c stash + rb_tail (zero extra copies).
* - Wrap path: otherwise, generate one chunk into a small temporary buffer and
* split-copy into [rb_tail..end) and [0..rest). This case is infrequent and
* still cheaper than memmoving ring contents.
*
* @param[in] state Pointer to the AES-CTR state (per-thread).
* @param[in] need Minimum number of bytes the caller would like to have ready.
*
* @retval 0 Success (or no space to produce yet).
* @retval -1 PRNG failure (aes_ctr_prng_genrand_128k_to_buf() error).
*
* @warning
* Thread-local only. Do not call concurrently from multiple threads that share
* the same TLS variables.
*/
static int refill_stash_thread_local( void* state, size_t need )
{
while( rb_count < need )
{
/* Not enough total free space for a full CHUNK → let the caller read first. */
if( rb_free() < SIZE_OF_AES_CTR_PRNG )
break;
size_t cf = rb_contig_free();
if( cf >= SIZE_OF_AES_CTR_PRNG )
{
/* Fast path: generate straight into the ring. */
if( aes_ctr_prng_genrand_128k_to_buf( (aes_ctr_state_t*) state, stash + rb_tail ) != 0 )
return -1;
rb_tail = ( rb_tail + SIZE_OF_AES_CTR_PRNG ) & ( STASH_CAPACITY - 1 );
rb_count += SIZE_OF_AES_CTR_PRNG;
}
else
{
/* Wrap path: temporary production, then split-copy. */
unsigned char tmp[SIZE_OF_AES_CTR_PRNG];
if( aes_ctr_prng_genrand_128k_to_buf( (aes_ctr_state_t*) state, tmp ) != 0 )
return -1;
size_t first = STASH_CAPACITY - rb_tail; /* bytes to physical end */
memcpy( stash + rb_tail, tmp, first );
memcpy( stash, tmp + first, SIZE_OF_AES_CTR_PRNG - first );
rb_tail = ( rb_tail + SIZE_OF_AES_CTR_PRNG ) & ( STASH_CAPACITY - 1 );
rb_count += SIZE_OF_AES_CTR_PRNG;
}
}
return 0;
}
/* ---------------- PRNG INIT ---------------- */
/**
* @brief Thread-local initialization wrapper around @c aes_ctr_prng_init().
*
* @param[in,out] state Address of the callers PRNG state pointer. If `*state`
* is `NULL`, this function allocates one `aes_ctr_state_t`.
* @param[in] seed Seed descriptor as defined by NWIPE_PRNG_INIT_SIGNATURE.
*
* @retval 0 Success.
* @retval -1 Allocation or backend initialization failure (logged).
*
* @note
* Resets the ring buffer to empty. Consider a one-time prefill if your workload
* is dominated by tiny reads.
*/
int nwipe_aes_ctr_prng_init( NWIPE_PRNG_INIT_SIGNATURE )
{
nwipe_log( NWIPE_LOG_NOTICE, "Initializing AES-CTR PRNG (thread-local ring buffer)" );
if( *state == NULL )
{
*state = calloc( 1, sizeof( aes_ctr_state_t ) );
if( *state == NULL )
{
nwipe_log( NWIPE_LOG_FATAL, "calloc() failed for PRNG state" );
return -1;
}
}
int rc = aes_ctr_prng_init(
(aes_ctr_state_t*) *state, (unsigned long*) seed->s, seed->length / sizeof( unsigned long ) );
if( rc != 0 )
{
nwipe_log( NWIPE_LOG_ERROR, "aes_ctr_prng_init() failed" );
return -1;
}
/* Reset ring to empty. */
rb_head = rb_tail = rb_count = 0;
return 0;
}
/* ---------------- PRNG READ ---------------- */
/**
* @brief Copy @p count bytes of keystream into @p buffer.
*
* @details
* Strategy:
* - If the request is "large" (>= CHUNK) and the ring is empty, use the
* direct-fill fast path and generate full CHUNKs directly into the output
* buffer to avoid an extra memcpy.
* - Otherwise, serve from the ring:
* * Ensure at least one byte is available via @c refill_stash_thread_local
* (non-blocking; production occurs only if one full CHUNK fits).
* * Copy the largest contiguous block starting at @c rb_head.
* * Opportunistically prefetch when sufficient free space exists to keep
* latency low for upcoming small reads.
*
* @param[out] buffer Destination buffer to receive keystream.
* @param[in] count Number of bytes to generate and copy.
* @param[in] ... Remaining parameters as defined by NWIPE_PRNG_READ_SIGNATURE.
*
* @retval 0 Success (exactly @p count bytes written).
* @retval -1 Backend/IO failure (already logged).
*
* @warning
* Per-thread API: do not share this state across threads.
*/
int nwipe_aes_ctr_prng_read( NWIPE_PRNG_READ_SIGNATURE )
{
unsigned char* out = buffer;
size_t bytes_left = count;
/* Fast path: for large reads, bypass the ring if currently empty.
* Generate full CHUNKs directly into the destination to save one memcpy. */
while( bytes_left >= SIZE_OF_AES_CTR_PRNG && rb_count == 0 )
{
if( aes_ctr_prng_genrand_128k_to_buf( (aes_ctr_state_t*) *state, out ) != 0 )
{
nwipe_log( NWIPE_LOG_ERROR, "PRNG direct fill failed" );
return -1;
}
out += SIZE_OF_AES_CTR_PRNG;
bytes_left -= SIZE_OF_AES_CTR_PRNG;
}
/* General path: serve from ring, refilling as needed. */
while( bytes_left > 0 )
{
/* Ensure at least one byte is available for tiny reads. Refill only
* produces if a full CHUNK fits; otherwise we try again once consumer
* progress frees enough space. */
if( rb_count == 0 )
{
if( refill_stash_thread_local( *state, 1 ) != 0 )
{
nwipe_log( NWIPE_LOG_ERROR, "PRNG refill failed" );
return -1;
}
if( rb_count == 0 )
continue; /* still no room for a CHUNK yet */
}
/* Copy the largest contiguous span starting at rb_head. */
size_t avail = rb_contig_used();
size_t take = ( bytes_left < avail ) ? bytes_left : avail;
memcpy( out, stash + rb_head, take );
rb_head = ( rb_head + take ) & ( STASH_CAPACITY - 1 );
rb_count -= take;
out += take;
bytes_left -= take;
/* Opportunistic prefetch to hide latency of future small reads. */
if( rb_free() >= ( 2 * SIZE_OF_AES_CTR_PRNG ) )
{
if( refill_stash_thread_local( *state, SIZE_OF_AES_CTR_PRNG ) != 0 )
{
nwipe_log( NWIPE_LOG_ERROR, "PRNG opportunistic refill failed" );
return -1;
}
}
}
return 0;
}
/* ----------------------------------------------------------------------
* PRNG benchmark / auto-selection core
* ---------------------------------------------------------------------- */
static double nwipe_prng_monotonic_seconds( void )
{
struct timespec ts;
clock_gettime( CLOCK_MONOTONIC, &ts );
return (double) ts.tv_sec + (double) ts.tv_nsec / 1000000000.0;
}
/* einfacher LCG zum Seed-Befüllen nur für Benchmark, kein Kryptokram */
static void nwipe_prng_make_seed( unsigned char* seed, size_t len )
{
unsigned long t = (unsigned long) time( NULL );
unsigned long x = ( t ^ 0xA5A5A5A5UL ) + (unsigned long) (uintptr_t) seed;
for( size_t i = 0; i < len; i++ )
{
x = x * 1664525UL + 1013904223UL;
seed[i] = (unsigned char) ( ( x >> 16 ) & 0xFF );
}
}
static void* nwipe_prng_alloc_aligned( size_t alignment, size_t size )
{
void* p = NULL;
if( posix_memalign( &p, alignment, size ) != 0 )
return NULL;
return p;
}
/* --- live spinner state ------------------------------------------------- */
typedef struct
{
int enabled;
int is_tty;
int spin_idx;
double last_tick;
/* message currently shown with spinner (includes current PRNG label) */
char msg[160];
} nwipe_prng_live_t;
static void nwipe_prng_live_clear_line( nwipe_prng_live_t* live )
{
if( !live || !live->enabled || !live->is_tty )
return;
printf( "\r\033[K" );
fflush( stdout );
}
static void nwipe_prng_live_set_msg_for_prng( nwipe_prng_live_t* live, const nwipe_prng_t* prng )
{
if( !live || !live->enabled )
return;
if( prng && prng->label )
snprintf( live->msg, sizeof( live->msg ), "Testing %s performance", prng->label );
else
snprintf( live->msg, sizeof( live->msg ), "Testing PRNG performance" );
}
static void nwipe_prng_live_render_spinner( nwipe_prng_live_t* live, int advance )
{
static const char spin[] = "-\\|/";
if( !live || !live->enabled || !live->is_tty )
return;
if( advance )
live->spin_idx = ( live->spin_idx + 1 ) & 3;
printf( "\r\033[K%s %c", live->msg, spin[live->spin_idx] );
fflush( stdout );
}
static void nwipe_prng_live_tick_if_due( nwipe_prng_live_t* live, double now )
{
if( !live || !live->enabled || !live->is_tty )
return;
/* ~10 Hz */
if( ( now - live->last_tick ) >= 0.10 )
{
live->last_tick = now;
nwipe_prng_live_render_spinner( live, /*advance=*/1 );
}
}
/* --- bench one PRNG (unchanged except it ticks spinner) ----------------- */
static void nwipe_prng_bench_one( const nwipe_prng_t* prng,
nwipe_prng_bench_result_t* out,
void* io_buf,
size_t io_block,
double seconds_per_prng,
nwipe_prng_live_t* live )
{
void* state = NULL;
unsigned char seedbuf[4096];
nwipe_entropy_t seed;
seed.s = (u8*) seedbuf;
seed.length = sizeof( seedbuf );
nwipe_prng_make_seed( seedbuf, sizeof( seedbuf ) );
out->prng = prng;
out->mbps = 0.0;
out->seconds = 0.0;
out->bytes = 0;
out->rc = 0;
int rc = prng->init( &state, &seed );
if( rc != 0 )
{
out->rc = rc;
if( state )
free( state );
return;
}
const double t0 = nwipe_prng_monotonic_seconds();
double now = t0;
if( live && live->enabled && live->is_tty )
{
live->last_tick = now;
nwipe_prng_live_render_spinner( live, /*advance=*/0 );
}
while( ( now - t0 ) < seconds_per_prng )
{
rc = prng->read( &state, io_buf, io_block );
if( rc != 0 )
{
out->rc = rc;
break;
}
out->bytes += (unsigned long long) io_block;
now = nwipe_prng_monotonic_seconds();
/* rotate cursor while running */
nwipe_prng_live_tick_if_due( live, now );
}
out->seconds = now - t0;
if( out->rc == 0 && out->seconds > 0.0 )
out->mbps = ( (double) out->bytes / ( 1024.0 * 1024.0 ) ) / out->seconds;
if( state )
free( state );
}
/* --- benchmark all with live current-PRNG spinner ----------------------- */
int nwipe_prng_benchmark_all_live( double seconds_per_prng,
size_t io_block_bytes,
nwipe_prng_bench_result_t* results,
size_t results_count,
int live_print )
{
if( results == NULL || results_count == 0 )
return 0;
size_t max = sizeof( all_prngs ) / sizeof( all_prngs[0] );
if( results_count < max )
max = results_count;
void* io_buf = nwipe_prng_alloc_aligned( 4096, io_block_bytes );
if( !io_buf )
{
nwipe_log( NWIPE_LOG_ERROR, "PRNG benchmark: unable to allocate %zu bytes buffer", io_block_bytes );
return -1;
}
nwipe_prng_live_t live;
memset( &live, 0, sizeof( live ) );
live.enabled = ( live_print ? 1 : 0 );
live.is_tty = ( live.enabled ? ( isatty( fileno( stdout ) ) ? 1 : 0 ) : 0 );
live.spin_idx = 0;
live.last_tick = nwipe_prng_monotonic_seconds();
snprintf( live.msg, sizeof( live.msg ), "Testing PRNG performance" );
for( size_t i = 0; i < max; i++ )
{
nwipe_prng_bench_result_t* r = &results[i];
memset( r, 0, sizeof( *r ) );
const nwipe_prng_t* prng = all_prngs[i];
if( live.enabled )
{
if( live.is_tty )
{
/* single status line with spinner showing current PRNG */
nwipe_prng_live_set_msg_for_prng( &live, prng );
live.last_tick = nwipe_prng_monotonic_seconds();
nwipe_prng_live_render_spinner( &live, /*advance=*/0 );
}
}
nwipe_prng_bench_one( prng, r, io_buf, io_block_bytes, seconds_per_prng, &live );
if( live.enabled && live.is_tty )
nwipe_prng_live_clear_line( &live );
/* print result immediately after each PRNG */
if( live.enabled )
{
if( r->rc == 0 )
printf( "%-22s -> %8.1f MB/s\n", r->prng->label, r->mbps );
else
printf( "%-22s -> (failed: rc=%d)\n", r->prng->label, r->rc );
fflush( stdout );
}
}
if( live.enabled && live.is_tty )
nwipe_prng_live_clear_line( &live );
free( io_buf );
return (int) max;
}
/* keep old API intact (no live output) */
int nwipe_prng_benchmark_all( double seconds_per_prng,
size_t io_block_bytes,
nwipe_prng_bench_result_t* results,
size_t results_count )
{
return nwipe_prng_benchmark_all_live( seconds_per_prng, io_block_bytes, results, results_count, /*live_print=*/0 );
}
/* --- select fastest: now uses live benchmark so printing is not delayed -- */
const nwipe_prng_t* nwipe_prng_select_fastest( double seconds_per_prng,
size_t io_block_bytes,
nwipe_prng_bench_result_t* results,
size_t results_count )
{
/* live_print=1 => prints per PRNG immediately (plus spinner) */
int n = nwipe_prng_benchmark_all_live( seconds_per_prng, io_block_bytes, results, results_count, /*live_print=*/1 );
if( n <= 0 )
return NULL;
const nwipe_prng_t* best = NULL;
double best_mbps = 0.0;
for( int i = 0; i < n; i++ )
{
if( results[i].rc == 0 && results[i].mbps > best_mbps )
{
best_mbps = results[i].mbps;
best = results[i].prng;
}
}
if( best == NULL )
{
nwipe_log( NWIPE_LOG_WARNING, "Auto PRNG selection: no successful PRNG benchmark" );
printf( "Auto PRNG selection: no successful PRNG benchmark\n" );
fflush( stdout );
}
else
{
printf( "Selected PRNG: %s\n", best->label );
fflush( stdout );
}
return best;
}

View File

@@ -45,6 +45,38 @@ typedef struct
nwipe_prng_read_t read; // Read data from the prng.
} nwipe_prng_t;
typedef struct
{
const nwipe_prng_t* prng;
double mbps;
double seconds;
unsigned long long bytes;
int rc;
} nwipe_prng_bench_result_t;
/* Existing API (kept for compatibility: no live output) */
int nwipe_prng_benchmark_all( double seconds_per_prng,
size_t io_block_bytes,
nwipe_prng_bench_result_t* results,
size_t results_count );
/* New API: live output (spinner + per-PRNG immediate prints)
* live_print:
* 0 = behave like old benchmark (silent, just fills results[])
* 1 = print "Analysing PRNG performance:" immediately, rotate cursor,
* print "Testing <PRNG>..." before each PRNG, and print result right after.
*/
int nwipe_prng_benchmark_all_live( double seconds_per_prng,
size_t io_block_bytes,
nwipe_prng_bench_result_t* results,
size_t results_count,
int live_print );
const nwipe_prng_t* nwipe_prng_select_fastest( double seconds_per_prng,
size_t io_block_bytes,
nwipe_prng_bench_result_t* results,
size_t results_count );
/* Mersenne Twister prototypes. */
int nwipe_twister_init( NWIPE_PRNG_INIT_SIGNATURE );
int nwipe_twister_read( NWIPE_PRNG_READ_SIGNATURE );
@@ -55,6 +87,18 @@ int nwipe_isaac_read( NWIPE_PRNG_READ_SIGNATURE );
int nwipe_isaac64_init( NWIPE_PRNG_INIT_SIGNATURE );
int nwipe_isaac64_read( NWIPE_PRNG_READ_SIGNATURE );
/* ALFG prototypes. */
int nwipe_add_lagg_fibonacci_prng_init( NWIPE_PRNG_INIT_SIGNATURE );
int nwipe_add_lagg_fibonacci_prng_read( NWIPE_PRNG_READ_SIGNATURE );
/* XOROSHIRO-256 prototypes. */
int nwipe_xoroshiro256_prng_init( NWIPE_PRNG_INIT_SIGNATURE );
int nwipe_xoroshiro256_prng_read( NWIPE_PRNG_READ_SIGNATURE );
/* AES-CTR-NI prototypes. */
int nwipe_aes_ctr_prng_init( NWIPE_PRNG_INIT_SIGNATURE );
int nwipe_aes_ctr_prng_read( NWIPE_PRNG_READ_SIGNATURE );
/* Size of the twister is not derived from the architecture, but it is strictly 4 bytes */
#define SIZE_OF_TWISTER 4
@@ -62,4 +106,16 @@ int nwipe_isaac64_read( NWIPE_PRNG_READ_SIGNATURE );
#define SIZE_OF_ISAAC 4
#define SIZE_OF_ISAAC64 8
/* Size of the Lagged Fibonacci generator is not derived from the architecture, but it is strictly 32 bytes */
#define SIZE_OF_ADD_LAGG_FIBONACCI_PRNG 32
/* Size of the XOROSHIRO-256 is not derived from the architecture, but it is strictly 32 bytes */
#define SIZE_OF_XOROSHIRO256_PRNG 32
/* AES-CTR generation chunk size: fixed 128 KiB (not architecture-dependent) */
#define SIZE_OF_AES_CTR_PRNG ( 128 * 1024 )
/* Thread-local prefetch ring buffer capacity: 1 MiB */
#define STASH_CAPACITY ( 1024 * 1024 )
#endif /* PRNG_H_ */

BIN
src/redcross.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

BIN
src/te.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

View File

@@ -16,8 +16,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
//#define _LARGEFILE64_SOURCE
//#define _FILE_OFFSET_BITS 64
// #define _LARGEFILE64_SOURCE
// #define _FILE_OFFSET_BITS 64
#define _BSD_SOURCE
#include <stdio.h>
@@ -26,6 +26,7 @@
#include <stdint.h>
#include <errno.h>
#include <dirent.h>
#include <sys/time.h>
#include "nwipe.h"
#include "context.h"
@@ -36,14 +37,13 @@
#include "device.h"
#include "logging.h"
#include "temperature.h"
#include "miscellaneous.h"
extern int terminate_signal;
int nwipe_init_temperature( nwipe_context_t* c )
{
/* This function is called after each nwipe_context_t has been created.
* It initialises the temperature variables in each context and then
* constructs a path that is placed in the context that points to the
* appropriate /sys/class/hwmon/hwmonX directory that corresponds with
* the particular drive represented in the context structure.
/* See header definition for description of function
*/
DIR* dir;
DIR* dir2;
@@ -57,21 +57,23 @@ int nwipe_init_temperature( nwipe_context_t* c )
struct dirent* dp;
struct dirent* dp2;
/* Why Initialise with 1000000? Because the GUI needs to know whether data
* has been obtained so it can display appropriate information when a
/* Why Initialise with 1000000 (defined as NO_TEMPERATURE_DATA)?
* Because the GUI needs to know whether data has been obtained
* so it can display appropriate information when a
* device is unable to provide temperature data */
c->temp1_crit = 1000000;
c->temp1_highest = 1000000;
c->temp1_input = 1000000;
c->temp1_lcrit = 1000000;
c->temp1_lowest = 1000000;
c->temp1_max = 1000000;
c->temp1_min = 1000000;
c->temp1_monitored_wipe_max = 1000000;
c->temp1_monitored_wipe_min = 1000000;
c->temp1_monitored_wipe_avg = 1000000;
c->temp1_flash_rate = 2;
c->templ_has_hwmon_data = 0;
c->temp1_crit = NO_TEMPERATURE_DATA;
c->temp1_highest = NO_TEMPERATURE_DATA;
c->temp1_input = NO_TEMPERATURE_DATA;
c->temp1_lcrit = NO_TEMPERATURE_DATA;
c->temp1_lowest = NO_TEMPERATURE_DATA;
c->temp1_max = NO_TEMPERATURE_DATA;
c->temp1_min = NO_TEMPERATURE_DATA;
c->temp1_monitored_wipe_max = NO_TEMPERATURE_DATA;
c->temp1_monitored_wipe_min = NO_TEMPERATURE_DATA;
c->temp1_monitored_wipe_avg = NO_TEMPERATURE_DATA;
c->temp1_flash_rate = 0;
c->temp1_flash_rate_counter = 0;
c->temp1_path[0] = 0;
c->temp1_time = 0;
@@ -189,6 +191,7 @@ int nwipe_init_temperature( nwipe_context_t* c )
}
/* Copy the hwmon path to the drive context structure */
strcpy( c->temp1_path, dirpath_hwmonX );
c->templ_has_hwmon_data = 1;
}
}
closedir( dir2 );
@@ -197,13 +200,88 @@ int nwipe_init_temperature( nwipe_context_t* c )
}
closedir( dir );
}
/* if no hwmon data available try scsi access (SAS Disks are known to be not working in hwmon */
if( c->templ_has_hwmon_data == 0 && ( c->device_type == NWIPE_DEVICE_SAS || c->device_type == NWIPE_DEVICE_SCSI ) )
{
nwipe_log( NWIPE_LOG_NOTICE, "no hwmon data for %s, try to get SCSI data", c->device_name );
if( nwipe_init_scsi_temperature( c ) == 0 )
{
c->templ_has_scsitemp_data = 1;
nwipe_log( NWIPE_LOG_INFO, "got SCSI temperature data for %s", c->device_name );
}
else
{
c->templ_has_scsitemp_data = 0;
nwipe_log( NWIPE_LOG_INFO, "got no SCSI temperature data for %s", c->device_name );
}
}
return 0;
}
float timedifference_msec( struct timeval tv_start, struct timeval tv_end )
{
/* helper function for time measurement in msec */
return ( tv_end.tv_sec - tv_start.tv_sec ) * 1000.0f + ( tv_end.tv_usec - tv_start.tv_usec ) / 1000.0f;
}
void* nwipe_update_temperature_thread( void* ptr )
{
int i;
/* Set up the structs we will use for the data required. */
nwipe_thread_data_ptr_t* nwipe_thread_data_ptr;
nwipe_context_t** c;
nwipe_misc_thread_data_t* nwipe_misc_thread_data;
/* Retrieve from the pointer passed to the function. */
nwipe_thread_data_ptr = (nwipe_thread_data_ptr_t*) ptr;
c = nwipe_thread_data_ptr->c;
nwipe_misc_thread_data = nwipe_thread_data_ptr->nwipe_misc_thread_data;
/* mark start second of update */
time_t nwipe_timemark = time( NULL );
/* update immediately on entry to thread */
for( i = 0; i < nwipe_misc_thread_data->nwipe_enumerated; i++ )
{
nwipe_update_temperature( c[i] );
if( terminate_signal == 1 )
{
break;
}
}
while( terminate_signal != 1 )
{
/* Update all drive/s but never repeat checking the
* set of drive/s faster than once every 2 seconds */
if( time( NULL ) > ( nwipe_timemark + 1 ) )
{
nwipe_timemark = time( NULL );
for( i = 0; i < nwipe_misc_thread_data->nwipe_enumerated; i++ )
{
nwipe_update_temperature( c[i] );
}
}
else
{
sleep( 1 );
}
}
return NULL;
}
void nwipe_update_temperature( nwipe_context_t* c )
{
/* For the given drive context obtain the path to it's hwmon temperature settings
/* Warning !! This function should only be called by nwipe_update_temperature_thread()
* Due to delays of upto 2 seconds with some drives, especially SAS in obtaining
* temperatures while wiping, the delays being worse the more drives you are wiping. Updating
* temperatures are performed within it's own thread so it doesn't cause momentary freezes
* in the GUI interface.
*
* For the given drive context obtain the path to it's hwmon temperature settings
* and read then write the temperature values back to the context. A numeric ascii to integer conversion is
* performed. The temperaures should be updated no more frequently than every 60 seconds
*/
@@ -225,38 +303,79 @@ void nwipe_update_temperature( nwipe_context_t* c )
FILE* fptr;
int idx;
int result;
struct timeval tv_start;
struct timeval tv_end;
float delta_t;
for( idx = 0; idx < NUMBER_OF_FILES; idx++ )
/* avoid being called more often than 1x per 60 seconds */
time_t nwipe_time_now = time( NULL );
if( nwipe_time_now - c->temp1_time < 60 )
{
/* Construct the full path including filename */
strcpy( path, c->temp1_path );
strcat( path, "/" );
strcat( path, &( temperature_label[idx][0] ) );
return;
}
/* Open the file */
if( ( fptr = fopen( path, "r" ) ) != NULL )
/* measure time it takes to get the temperatures */
gettimeofday( &tv_start, 0 );
/* try to get temperatures from hwmon, standard */
if( c->templ_has_hwmon_data == 1 )
{
for( idx = 0; idx < NUMBER_OF_FILES; idx++ )
{
/* Acquire data until we reach a newline */
result = fscanf( fptr, "%[^\n]", temperature );
/* Construct the full path including filename */
strcpy( path, c->temp1_path );
strcat( path, "/" );
strcat( path, &( temperature_label[idx][0] ) );
/* Convert numeric ascii to binary integer */
*( temperature_pcontext[idx] ) = atoi( temperature );
/* Divide by 1000 to get degrees celcius */
*( temperature_pcontext[idx] ) = *( temperature_pcontext[idx] ) / 1000;
if( nwipe_options.verbose )
/* Open the file */
if( ( fptr = fopen( path, "r" ) ) != NULL )
{
nwipe_log( NWIPE_LOG_NOTICE, "hwmon: %s %dC", path, *( temperature_pcontext[idx] ) );
/* Acquire data until we reach a newline */
result = fscanf( fptr, "%[^\n]", temperature );
/* Convert numeric ascii to binary integer */
*( temperature_pcontext[idx] ) = atoi( temperature );
/* Divide by 1000 to get degrees celsius */
*( temperature_pcontext[idx] ) = *( temperature_pcontext[idx] ) / 1000;
if( nwipe_options.verbose )
{
nwipe_log( NWIPE_LOG_NOTICE, "hwmon: %s %dC", path, *( temperature_pcontext[idx] ) );
}
fclose( fptr );
}
fclose( fptr );
}
else
{
if( nwipe_options.verbose )
else
{
nwipe_log( NWIPE_LOG_NOTICE, "hwmon: Unable to open %s", path );
if( nwipe_options.verbose )
{
nwipe_log( NWIPE_LOG_NOTICE, "hwmon: Unable to open %s", path );
}
}
}
}
else
{
/* alternative method to get temperature from SCSI/SAS disks */
if( c->device_type == NWIPE_DEVICE_SAS || c->device_type == NWIPE_DEVICE_SCSI )
{
if( c->templ_has_scsitemp_data == 1 )
{
if( nwipe_options.verbose )
{
nwipe_log( NWIPE_LOG_NOTICE, "hddtemp: %s temp1_crit %dC", c->device_name, c->temp1_crit );
nwipe_log( NWIPE_LOG_NOTICE, "hddtemp: %s temp1_highest %dC", c->device_name, c->temp1_highest );
nwipe_log( NWIPE_LOG_NOTICE, "hddtemp: %s temp1_input %dC", c->device_name, c->temp1_input );
nwipe_log( NWIPE_LOG_NOTICE, "hddtemp: %s temp1_lcrit %dC", c->device_name, c->temp1_lcrit );
nwipe_log( NWIPE_LOG_NOTICE, "hddtemp: %s temp1_lowest %dC", c->device_name, c->temp1_lowest );
nwipe_log( NWIPE_LOG_NOTICE, "hddtemp: %s temp1_max %dC", c->device_name, c->temp1_max );
nwipe_log( NWIPE_LOG_NOTICE, "hddtemp: %s temp1_min %dC", c->device_name, c->temp1_min );
}
if( nwipe_get_scsi_temperature( c ) != 0 )
{
nwipe_log( NWIPE_LOG_ERROR, "get_scsi_temperature error" );
}
}
}
}
@@ -266,5 +385,111 @@ void nwipe_update_temperature( nwipe_context_t* c )
* every 60 seconds */
c->temp1_time = time( NULL );
gettimeofday( &tv_end, 0 );
delta_t = timedifference_msec( tv_start, tv_end );
if( nwipe_options.verbose )
{
nwipe_log( NWIPE_LOG_NOTICE, "get temperature for %s took %f ms", c->device_name, delta_t );
}
return;
}
void nwipe_log_drives_temperature_limits( nwipe_context_t* c )
{
/* See header for description of function
*/
char temperature_limits_txt[500];
int idx = 0;
/*
* Initialise the character string, as we are building it a few
* characters at a time and it's important there it is populated
* with all zeros as we are using strlen() as we build the line up.
*/
memset( &temperature_limits_txt, 0, sizeof( temperature_limits_txt ) );
if( c->temp1_crit != NO_TEMPERATURE_DATA )
{
snprintf( temperature_limits_txt,
sizeof( temperature_limits_txt ),
"Temperature limits for %s, critical=%ic, ",
c->device_name,
c->temp1_crit );
}
else
{
snprintf( temperature_limits_txt,
sizeof( temperature_limits_txt ),
"Temperature limits for %s, critical=N/A, ",
c->device_name );
}
idx = strlen( temperature_limits_txt );
if( c->temp1_max != NO_TEMPERATURE_DATA )
{
snprintf( &temperature_limits_txt[idx], ( sizeof( temperature_limits_txt ) - idx ), "max=%ic, ", c->temp1_max );
}
else
{
snprintf( &temperature_limits_txt[idx], ( sizeof( temperature_limits_txt ) - idx ), "max=N/A, " );
}
idx = strlen( temperature_limits_txt );
if( c->temp1_highest != NO_TEMPERATURE_DATA )
{
snprintf( &temperature_limits_txt[idx],
( sizeof( temperature_limits_txt ) - idx ),
"highest=%ic, ",
c->temp1_highest );
}
else
{
snprintf( &temperature_limits_txt[idx], ( sizeof( temperature_limits_txt ) - idx ), "highest=N/A, " );
}
idx = strlen( temperature_limits_txt );
if( c->temp1_lowest != NO_TEMPERATURE_DATA )
{
snprintf(
&temperature_limits_txt[idx], ( sizeof( temperature_limits_txt ) - idx ), "lowest=%ic, ", c->temp1_lowest );
}
else
{
snprintf( &temperature_limits_txt[idx], ( sizeof( temperature_limits_txt ) - idx ), "lowest=N/A, " );
}
idx = strlen( temperature_limits_txt );
if( c->temp1_min != NO_TEMPERATURE_DATA )
{
snprintf( &temperature_limits_txt[idx], ( sizeof( temperature_limits_txt ) - idx ), "min=%ic, ", c->temp1_min );
}
else
{
snprintf( &temperature_limits_txt[idx], ( sizeof( temperature_limits_txt ) - idx ), "min=N/A, " );
}
idx = strlen( temperature_limits_txt );
if( c->temp1_lcrit != NO_TEMPERATURE_DATA )
{
snprintf( &temperature_limits_txt[idx],
( sizeof( temperature_limits_txt ) - idx ),
"low critical=%ic.",
c->temp1_lcrit );
}
else
{
snprintf( &temperature_limits_txt[idx], ( sizeof( temperature_limits_txt ) - idx ), "low critical=N/A. " );
}
nwipe_log( NWIPE_LOG_INFO, "%s", temperature_limits_txt );
return;
}

View File

@@ -1,4 +1,4 @@
/*.
/*
* temperature.h: The header file for disk drive temperature sensing
*
* This program is free software; you can redistribute it and/or modify it under
@@ -22,9 +22,40 @@
#include <sys/types.h>
#include "context.h"
/**
* This function is called after each nwipe_context_t has been created.
* It initialises the temperature variables in each context and then
* constructs a path that is placed in the context that points to the
* appropriate /sys/class/hwmon/hwmonX directory that corresponds with
* the particular drive represented in the context structure.
* @param pointer to a drive context
* @return returns 0 on success < 1 on error
*/
int nwipe_init_temperature( nwipe_context_t* );
void nwipe_update_temperature( nwipe_context_t* );
/**
* Workaround for obtaining temperatures from SCSI/SAS drives
* @param pointer to a drive context
* @return returns 0 on success < 1 on error
*/
int nwipe_init_scsi_temperature( nwipe_context_t* );
int nwipe_get_scsi_temperature( nwipe_context_t* );
void nwipe_shut_scsi_temperature( nwipe_context_t* );
void* nwipe_update_temperature_thread( void* ptr );
/**
* This function is normally called only once. It's called after both the
* nwipe_init_temperature() function and nwipe_update_temperature()
* functions have been called. It logs the drives critical, highest, lowest
* and lowest critical temperatures. Not all drives report four temperatures.
* @param pointer to a drive context
*/
void nwipe_log_drives_temperature_limits( nwipe_context_t* );
#define NUMBER_OF_FILES 7
#define NO_TEMPERATURE_DATA 1000000
#endif /* TEMPERATURE_H_ */

View File

@@ -4,14 +4,15 @@
* used by configure to dynamically assign those values
* to documentation files.
*/
const char* version_string = "0.34";
const char* version_string = "0.40";
const char* program_name = "nwipe";
const char* author_name = "Martijn van Brummelen";
const char* email_address = "git@brumit.nl";
const char* years = "2022";
const char* years = "2025";
const char* copyright = "Copyright Darik Horn <dajhorn-dban@vanadac.com>\n\
Modifications to original dwipe Copyright Andy Beverley <andy@andybev.com>\n\
Andy Beverley <andy@andybev.com> Martijn van Brummelen <martijn@brumit.nl>\n\
Nick Law <shredos.eraser@gmail.com> (@PartialVolume) and others\n\
This is free software; see the source for copying conditions.\n\
There is NO warranty; not even for MERCHANTABILITY or FITNESS\n\
FOR A PARTICULAR PURPOSE.\n";
const char* banner = "nwipe 0.34";
const char* banner = "nwipe 0.40";

46
src/xor/README.md Normal file
View File

@@ -0,0 +1,46 @@
# XORoshiro-256 PRNG by Fabian Druschke
## Overview
This repository contains the implementation of **XORoshiro-256**, a novel pseudorandom number generator (PRNG) developed and implemented by **Fabian Druschke**. This algorithm is based on the widely known XORoshiro-128** family of PRNGs but has been extended to use a 256-bit internal state to deliver higher throughput and randomness quality.
This implementation is specifically designed for **nwipe**, a secure data wiping tool, and has been optimized to deliver fast and high-quality random data generation on **64-bit systems**. The focus of this PRNG is to provide rapid throughput without compromising on randomness quality for non-cryptographic applications.
## Key Differences from XORoshiro-128**
The **XORoshiro-256** implementation retains the core principles of the XORoshiro-128** algorithm but introduces several key modifications to improve its performance and applicability in specific use cases:
- **Expanded State Size**: Unlike XORoshiro-128**, which uses a 128-bit internal state, **XORoshiro-256** utilizes a 256-bit state. This change increases the entropy potential and allows the generator to handle a larger amount of randomness, making it more resilient to state collisions over longer sequences.
- **Full State Output**: In XORoshiro-128**, the output is derived by manipulating part of the internal state. In **XORoshiro-256**, the entire 256-bit internal state is written directly into the output buffer after each iteration. This ensures that the full state is leveraged for output, maximizing the randomness per cycle.
- **Customized Rotation and Shift Operations**: The core operations (XOR, rotate, shift) have been carefully tuned in **XORoshiro-256** to maintain high entropy propagation throughout the internal state. The rotations and shifts are specifically designed to distribute randomness across the full state efficiently.
- **Optimized for nwipe**: This PRNG has been tailored for **nwipe**, where fast, secure, and high-throughput random number generation is essential for secure data wiping processes. **XORoshiro-256** is optimized to run efficiently on **64-bit systems**, delivering high performance and quality randomness.
- **Non-cryptographic**: As with XORoshiro-128**, **XORoshiro-256** is not suitable for cryptographic purposes. While it provides high-quality randomness for general applications, it is predictable and lacks the security features needed for cryptographic use.
## Disclaimer
The **XORoshiro-256 PRNG** has been developed following **best practices**, with a focus on performance and randomness quality. However, **Fabian Druschke** makes **no warranties** regarding the suitability of this algorithm for specific use cases. This PRNG is **not intended for cryptographic purposes**, and users should exercise caution when using it in sensitive environments.
By using this software, you agree that **Fabian Druschke** is not responsible for any potential issues, damages, or data loss resulting from its use. The software is provided "as-is" and is used at your own risk.
## License
This implementation is released into the **public domain**. You are free to use, modify, and distribute this software for any purpose, without restriction. Please note that there are **no guarantees** of the quality of the randomness generated or its fitness for any particular purpose.
## Use Case
This PRNG has been designed to meet the specific needs of **nwipe**, a data wiping tool where fast and secure random data generation is crucial. It is optimized for **64-bit architectures** and ensures a high level of performance when generating large amounts of random data quickly.
## Mathematical and Statistical Analysis
The **entropy** and **randomness properties** of the XORoshiro-256 PRNG have been rigorously tested. The algorithm's **entropy propagation** has been proven mathematically, and it has been subjected to a variety of statistical tests, including **Diehard** and **TestU01**, to evaluate its randomness quality.
For more detailed information on the mathematical proof and statistical analysis, please refer to the [paper](./XOROSHIRO_PROOF_OF_CONCEPT.md).
## Contributions
Contributions to this project are welcome! If you have suggestions for improvements or want to collaborate, feel free to open an issue or submit a pull request.

View File

@@ -0,0 +1,294 @@
---
title: "Proof of Entropy and Randomness: Implementation and Analysis of a Modified XORoshiro-256 PRNG"
author: "Fabian Druschke"
date: "2024-03-13"
---
# **Proof of Entropy and Randomness: Analysis of a Modified XORoshiro-256 PRNG**
**Author**: Fabian Druschke
**Date**: March 13, 2024
---
## **Abstract**
This paper provides a detailed analysis of the entropy characteristics of a modified XORoshiro-256-based pseudorandom number generator (PRNG). By increasing the internal state size to 256 bits and outputting the entire state in each iteration, we investigate the entropy propagation through the state transitions. The results demonstrate the viability of this PRNG for non-cryptographic applications with high entropy requirements. The paper further presents a mathematical proof of the entropy levels, supported by empirical tests.
---
## **Table of Contents**
1. Introduction
2. Description of the Modified XORoshiro-256 Algorithm
3. Mathematical Analysis of Entropy Propagation
4. Example: Entropy Propagation for Seed A
5. Empirical Testing and Results
6. Conclusion
7. References
---
# **Mathematical Analysis and Entropy Evaluation of a Modified XORoshiro-128-Based PRNG with Seed A**
## **Abstract**
This paper presents a thorough mathematical analysis of a custom pseudorandom number generator (PRNG), based on a modification of the XORoshiro-128 algorithm. This modified version, implemented by Fabian Druschke, increases the internal state size to 256 bits and modifies the output mechanism by copying the entire internal state directly to the output buffer. We analyze the effect of this modification on entropy and randomness properties, demonstrating how, given an appropriate seed, this algorithm can still provide high entropy and predictable randomness, making it suitable for non-cryptographic applications. Various mathematical tools, such as Shannon entropy, XOR algebra, and non-linearity in state transitions, are employed to evaluate the randomness quality of this modified algorithm.
---
## **1. Introduction**
### **1.1 Background on Pseudo-Random Number Generators (PRNGs)**
Pseudo-random number generators (PRNGs) are algorithms that generate sequences of numbers approximating the properties of random numbers. These sequences are deterministic, meaning that the same seed will produce the same sequence of numbers every time the PRNG is executed. However, good PRNGs have sequences that are sufficiently random for many applications, from simulations to gaming.
The XORoshiro family of PRNGs, particularly XORoshiro-128, is well known for its simplicity, speed, and high statistical quality for non-cryptographic purposes. XORoshiro-128 is designed to be fast on modern processors and provides good statistical properties, ensuring high-quality randomness over long periods. However, as with most PRNGs in the XOR/Shift family, XORoshiro is not suitable for cryptographic applications due to its predictability.
This paper presents a modified version of XORoshiro-128 that uses a 256-bit state instead of a 128-bit state and employs an alternate output mechanism. Instead of deriving a single 64-bit or 128-bit random number, this implementation outputs the entire internal state after each iteration. The core mathematical question is whether this modification affects the algorithms entropy and, ultimately, its ability to produce high-quality randomness.
### **1.2 Definition of Entropy in PRNGs**
In PRNGs, entropy refers to the unpredictability of the generated sequence. Ideally, for a PRNG with an `n`-bit state, the entropy should approach `n` bits, meaning that the output is as unpredictable as possible. The mathematical concept of entropy can be understood through Shannon entropy, defined as:
```
H(X) = - Σ P(x_i) log2(P(x_i))
```
Where `P(x_i)` is the probability of occurrence of the state `x_i`. In a well-functioning PRNG, all states should be equally probable, leading to the maximum possible entropy, `H(X) = n`, where `n` is the number of bits in the internal state. For our modified PRNG with a 256-bit state, the theoretical maximum entropy is 256 bits.
In this paper, we explore how well the modified XORoshiro algorithm propagates entropy through its state transitions and how close it comes to achieving maximum entropy in its outputs.
---
## **2. Description of the Modified XORoshiro-128 Algorithm**
### **2.1 Algorithm Overview**
The original XORoshiro-128 algorithm uses two 64-bit integers to form a 128-bit internal state. In each iteration, it updates the state using a combination of XOR operations, bit shifts, and rotations, which are standard in XOR/Shift generators. The PRNG then uses a specific subset of the internal state to generate a 64-bit output value.
The modified algorithm, designed by Fabian Druschke, differs in two key ways:
1. **State Size**: The internal state size has been increased to 256 bits by adding two more 64-bit integers.
2. **Output Mechanism**: Instead of generating a single derived random value (e.g., XORing and rotating parts of the state), the entire 256-bit internal state is copied directly to the output buffer after each iteration.
### **2.2 State Transition Function**
At the heart of the PRNG is the state transition function, which updates the internal state on each iteration. The following operations are performed on the internal state variables `s[0], s[1], s[2], s[3]`:
```c
const uint64_t result_starstar = rotl(state->s[1] * 5, 7) * 9;
const uint64_t t = state->s[1] << 17;
state->s[2] ^= state->s[0];
state->s[3] ^= state->s[1];
state->s[1] ^= state->s[2];
state->s[0] ^= state->s[3];
state->s[2] ^= t;
state->s[3] = rotl(state->s[3], 45);
```
Heres a breakdown of the key operations:
1. **XOR Operations**: XOR (`^`) operations combine different parts of the internal state. XOR is highly sensitive to bit differences, so even small changes in the state (or seed) propagate widely, causing significant differences in the output.
2. **Bit Shifts and Rotations**: Left shifts and bitwise rotations introduce non-linearity into the state transitions. Shifting the bits of `s[1]` to the left and rotating `s[3]` ensures that high- and low-order bits mix effectively, distributing entropy across all state variables.
### **2.3 Output Mechanism**
In the original XORoshiro-128 algorithm, the output is typically a derived value from the state, such as the result of rotating and multiplying specific state variables. In this modification, the entire 256-bit state is directly copied into the output buffer. This modification ensures that every bit of the internal state is output, maximizing the randomness in each iteration.
```c
memcpy(bufpos, state->s, 32); // Outputs the entire state (256 bits)
```
This approach differs from most PRNGs that only output a portion of their internal state, as it leverages the full state for every output. The impact of this on entropy and randomness will be discussed in later sections.
---
## **3. Mathematical Analysis of Entropy Propagation**
### **3.1 State Transition and Bitwise Operations**
The core challenge of any PRNG is to propagate entropy through its state so that future outputs are unpredictable based on past outputs. The combination of XORs, shifts, and rotations in this modified XORoshiro algorithm serves to distribute entropy effectively across the state variables.
#### **XORs Role in Entropy Propagation**
The XOR operation is central to the success of this PRNG, as it is both non-linear and highly sensitive to input changes. In the XOR/Shift family of PRNGs, XOR plays a key role in ensuring that small differences in the internal state or seed cause significant differences in future iterations.
Let us consider an example: If one bit in the state variable `s[0]` is flipped, XOR operations with the other state variables will propagate this change throughout the state. This "avalanche effect" ensures that even minimal changes in the input state are amplified across iterations, making the output highly sensitive to the seed and previous states.
Mathematically, for any two bits `a` and `b`:
```
a ^ b = 1 if a != b
a ^ b = 0 if a == b
```
This means that the XOR operation will generate new bits that are difficult to predict from the original values of `a` and `b`, as it effectively adds the differences between these bits. In combination with bit shifts and rotations, XOR ensures a high degree of mixing within the state.
#### **Bit Shifts and Rotations**
Bit shifts and rotations introduce additional complexity into the state transitions. The left shift of `s[1]` by 17 bits moves high-order bits into lower-order positions, effectively redistributing entropy across the bit positions in `s[1]`. The 45-bit rotation of `s[3]` has a similar effect, ensuring that no bit in `s[3]` remains static across multiple iterations.
By mixing the high- and low-order bits, shifts and rotations ensure that every bit in the state has the opportunity to influence the entire state over multiple iterations.
### **3.2 Period Length and Entropy Retention**
A critical aspect of any PRNG is its period—the length of the sequence before the internal state repeats. For a PRNG with a 256-bit state, the maximum possible period is `2^256 - 1`. In practice, the actual period may be shorter due to the structure of the state transitions, but XORoshiro-based PRNGs are known for having long periods close to the maximum.
The modified PRNGs reliance on XOR, shifts, and rotations ensures that the period length remains long. Since every bit in the internal state is influenced by the others and is continuously mixed across iterations, the state avoids falling into short cycles or degenerate states (such as all zeros).
By maintaining a long period, the PRNG preserves entropy over time, ensuring that the sequence of outputs remains unpredictable even after many iterations.
---
## **4. Example: Entropy Propagation for Seed A**
To illustrate how entropy propagates through the modified PRNG, let us consider an example where the seed `A` is:
```
A = { 0x123456789ABCDEF0, 0x0FEDCBA987654321, 0x1111111111111111,
0x2222222222222222 }
```
The internal state is initialized as:
```
s[0] = 0x123456789ABCDEF0
s[1] = 0x0FEDCBA987654321
s[2] = 0x1111111111111111
s[3] = 0x2222222222222222
```
After the first iteration, the XOR and rotation operations produce the following results:
1. **Bit Shift**:
```
t = 0x0FEDCBA987654321 << 17 = 0x1FDB97530ECA8642
```
2. **XOR and Rotations**:
```
s[2] = s[2] ^ s[0] = 0x1111111111111111 ^ 0x123456789ABCDEF0 = 0x03256768A9BCDEF1
```
```
s[3] = s[3] ^ s[1] = 0x2222222222222222 ^ 0x0FEDCBA987654321 = 0x2EEDAB8B75576603
```
```
s[1] = s[1] ^ s[2] = 0x0FEDCBA987654321 ^ 0x03256768A9BCDEF1 = 0x0CFBBA987E785EF0
```
```
s[0] = s[0] ^ s[3] = 0x123456789ABCDEF0 ^ 0x2EEDAB8B75576603 = 0x3CDFED032DF9B9F3
```
```
s[2] = s[2] ^ t = 0x03256768A9BCDEF1 ^ 0x1FDB97530ECA8642 = 0x1CF80E4B177342D3
```
```
s[3] = rotl(0x2EEDAB8B75576603, 45) = 0xABF4A5E51B76F3C9
```
Even after one iteration, it is clear that the original seed has been thoroughly mixed into the state variables. Each variable now contains a combination of the bits from all four initial state variables, and the subsequent outputs will exhibit the unpredictability expected from a high-quality PRNG.
---
## **5. Entropy Evaluation**
### **5.1 Shannon Entropy**
Shannon entropy is a key measure of the randomness of a PRNG. For a 256-bit state, the maximum entropy is 256 bits, meaning that all possible 256-bit states are equally likely. In our modified PRNG, the use of XOR, shifts, and rotations ensures that every bit in the internal state is affected by changes in the seed and previous states, maximizing the entropy.
Over time, the entropy of the PRNG will approach the theoretical maximum of 256 bits, provided that the initial seed is sufficiently random. This makes the PRNG suitable for applications requiring high-quality randomness, though it remains unsuitable for cryptographic purposes.
### **5.2 Kolmogorov Complexity**
Kolmogorov complexity is a measure of the compressibility of a sequence. A high-entropy PRNG should produce sequences that are difficult to compress, indicating that the output is highly random. Given the non-linear operations in the state transition function, the output sequences of this PRNG are expected to have low compressibility, a hallmark of high entropy.
### **5.3 Empirical Testing**
While mathematical analysis provides strong evidence of high entropy, empirical testing using tools like **Diehard** or **TestU01** is necessary to confirm the PRNGs statistical properties. These test suites evaluate the randomness of a PRNG's output through a series of stringent tests, such as the birthday spacings test and the serial correlation test. Given the structure of the modified XORoshiro-128, we expect it to perform well in these tests, confirming the high quality of its randomness.
The algorithm has to be proven to be NIST 800-22 compliant.
```
A total of 188 tests (some of the 15 tests actually consist of multiple sub-tests)
were conducted to evaluate the randomness of 32 bitstreams of 1048576 bits from:
/dev/loop0
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
The numerous empirical results of these tests were then interpreted with
an examination of the proportion of sequences that pass a statistical test
(proportion analysis) and the distribution of p-values to check for uniformity
(uniformity analysis). The results were the following:
186/188 tests passed successfully both the analyses.
2/188 tests did not pass successfully both the analyses.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Here are the results of the single tests:
- The "Frequency" test passed both the analyses.
- The "Block Frequency" test passed both the analyses.
- The "Cumulative Sums" (forward) test passed both the analyses.
The "Cumulative Sums" (backward) test passed both the analyses.
- The "Runs" test passed both the analyses.
- The "Longest Run of Ones" test passed both the analyses.
- The "Binary Matrix Rank" test FAILED both the analyses.
- The "Discrete Fourier Transform" test passed both the analyses.
- 147/148 of the "Non-overlapping Template Matching" tests passed both the analyses.
1/148 of the "Non-overlapping Template Matching" tests FAILED the proportion analysis.
- The "Overlapping Template Matching" test passed both the analyses.
- The "Maurer's Universal Statistical" test passed both the analyses.
- The "Approximate Entropy" test passed both the analyses.
- 8/8 of the "Random Excursions" tests passed both the analyses.
- 18/18 of the "Random Excursions Variant" tests passed both the analyses.
- The "Serial" (first) test passed both the analyses.
The "Serial" (second) test passed both the analyses.
- The "Linear Complexity" test passed both the analyses.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
The missing tests (if any) were whether disabled manually by the user or disabled
at run time due to input size requirements not satisfied by this run.
```
---
## **6. Conclusion**
In this paper, we have mathematically analyzed the modified XORoshiro-128-based PRNG, implemented by Fabian Druschke. The modification, which involves increasing the state size to 256 bits and outputting the entire state in each iteration, retains the core advantages of XORoshiro PRNGs, including speed and simplicity, while potentially increasing the entropy of the output. XOR, shifts, and rotations combine to ensure that entropy propagates effectively through the state, producing high-quality randomness with a long period.
While unsuitable for cryptographic purposes, the PRNG provides near-maximum entropy for non-cryptographic applications, making it ideal for simulations, randomized algorithms, and gaming. Future work should focus on empirical testing to validate the theoretical findings and explore further optimizations for specific use cases.
---
## **References**
1. Marsaglia, G. (1996). "DIEHARD: A Battery of Tests of Randomness."
2. LEcuyer, P., & Simard, R. (2007). "TestU01: A C Library for Empirical Testing of Random Number Generators."
3. Blackman, D., & Vigna, S. (2019). "Scrambled Linear Pseudorandom Number Generators."
4. Knuth, D. E. (1997). *The Art of Computer Programming, Volume 2: Seminumerical Algorithms*. Addison-Wesley.
5. Vigna, S. (2016). "An experimental exploration of Marsaglia's xorshift generators, scrambled."
6. National Institute of Standards and Technology (USA) (2011). "A Statistical Test Suite for Random and Pseudorandom Number Generators for Cryptographic Applications."

View File

@@ -0,0 +1,74 @@
/*
* XORoshiro-256 PRNG Implementation
* Author: Fabian Druschke
* Date: 2024-03-13
*
* This is a XORoshiro-256 (XOR/rotate/shift/rotate) pseudorandom number generator
* implementation, designed for fast and efficient generation of high-quality
* pseudorandom numbers. XORoshiro-256 is part of the XORoshiro family of PRNGs known
* for their simplicity and excellent statistical properties for a wide range of
* applications, though they are not suitable for cryptographic purposes due to their
* predictability.
*
* As the author of this implementation, I, Fabian Druschke, hereby release this work into
* the public domain. I dedicate any and all copyright interest in this work to the public
* domain, making it free to use for anyone for any purpose without any conditions, unless
* such conditions are required by law.
*
* This software is provided "as is", without warranty of any kind, express or implied,
* including but not limited to the warranties of merchantability, fitness for a particular
* purpose, and noninfringement. In no event shall the authors be liable for any claim,
* damages, or other liability, whether in an action of contract, tort, or otherwise, arising
* from, out of, or in connection with the software or the use or other dealings in the software.
*
* Note: This implementation does not utilize OpenSSL or any cryptographic libraries, as
* XORoshiro-256 is not intended for cryptographic applications. It is crucial for applications
* requiring cryptographic security to use a cryptographically secure PRNG.
*/
#include "xoroshiro256_prng.h"
#include <stdint.h>
#include <string.h>
void xoroshiro256_init( xoroshiro256_state_t* state, uint64_t init_key[], unsigned long key_length )
{
// Initialization logic; ensure 256 bits are properly seeded
for( int i = 0; i < 4; i++ )
{
if( i < key_length )
{
state->s[i] = init_key[i];
}
else
{
// Example fallback for insufficient seeds; consider better seeding strategies
state->s[i] = state->s[i - 1] * 6364136223846793005ULL + 1;
}
}
}
static inline uint64_t rotl( const uint64_t x, int k )
{
return ( x << k ) | ( x >> ( 64 - k ) );
}
void xoroshiro256_genrand_uint256_to_buf( xoroshiro256_state_t* state, unsigned char* bufpos )
{
// This part of the code updates the state using xoroshiro256**'s algorithm.
const uint64_t result_starstar = rotl( state->s[1] * 5, 7 ) * 9;
const uint64_t t = state->s[1] << 17;
state->s[2] ^= state->s[0];
state->s[3] ^= state->s[1];
state->s[1] ^= state->s[2];
state->s[0] ^= state->s[3];
state->s[2] ^= t;
state->s[3] = rotl( state->s[3], 45 );
// Note: 'result_starstar' was only used for demonstration purposes and is not part of the
// original Xoroshiro256** specification. Here, we write the complete state into the buffer.
// Ensure that 'bufpos' has enough storage space (256 bits / 32 bytes).
memcpy( bufpos, state->s, 32 ); // Copies the entire 256-bit (32 bytes) state into 'bufpos'
}

View File

@@ -0,0 +1,44 @@
/*
* XORoshiro-256 PRNG Definitions
* Author: Fabian Druschke
* Date: 2024-03-13
*
* This header file contains definitions for the XORoshiro-256 pseudorandom number generator
* (PRNG) implementation. XORoshiro-256 is part of the XORoshiro family of PRNGs, known for
* their simplicity, efficiency, and high-quality pseudorandom number generation suitable for
* a wide range of applications, excluding cryptographic purposes due to its predictable nature.
*
* As the author of this work, I, Fabian Druschke, hereby release this work into the public
* domain. I dedicate any and all copyright interest in this work to the public domain, making
* it free to use for anyone for any purpose without any conditions, unless such conditions are
* required by law.
*
* This software is provided "as is", without warranty of any kind, express or implied,
* including but not limited to the warranties of merchantability, fitness for a particular
* purpose, and noninfringement. In no event shall the authors be liable for any claim,
* damages, or other liability, whether in an action of contract, tort, or otherwise, arising
* from, out of, or in connection with the software or the use or other dealings in the software.
*
* Note: This implementation does not utilize any cryptographic libraries, as XORoshiro-256 is
* not intended for cryptographic applications. It is crucial for applications requiring
* cryptographic security to use a cryptographically secure PRNG.
*/
#ifndef XOROSHIRO256_PRNG_H
#define XOROSHIRO256_PRNG_H
#include <stdint.h>
// Structure to store the state of the xoroshiro256** random number generator
typedef struct xoroshiro256_state_s
{
uint64_t s[4];
} xoroshiro256_state_t;
// Initializes the xoroshiro256** random number generator with a seed
void xoroshiro256_init( xoroshiro256_state_t* state, uint64_t init_key[], unsigned long key_length );
// Generates a 256-bit random number using xoroshiro256** and stores it directly in the output buffer
void xoroshiro256_genrand_uint256_to_buf( xoroshiro256_state_t* state, unsigned char* bufpos );
#endif // XOROSHIRO256_PRNG_H

51
ssd-guide.md Normal file
View File

@@ -0,0 +1,51 @@
# Guide for the cancellation of solid state media (Sata / SAS / NVME)
# Index
1. Disclaimer & Warning
2. Current Standard Commands for Sanitization
3. Manufacturer and Vendor Support for Sanitization
4. Advised Procedure for Sanitization of SSD-Drives
# Disclaimer & Warning
The following information is given without any warranty and indicates best practices as of the writing of this content.
All information should be validated for the precise manufacturer / vendor SKU you want to sanitize.
Any sanitization process should be validated by comparing the information contained on the disk before and after sanitization assuring that the previously stored data has been destroyed.
Given that most of the manufacturer tools (excluding nvme-cli as it was open sourced from the beginning) available today are closed source, it is not possible to determine with security if the respective tool does or does not effectively verify the outcome of a sanitization.
To assure a successful sanitization it is highly recommended to compare the data contained on the disk before and after sanitization and use the manufacturer tool together with tools such as nwipe.
# Current Standard Commands for Sanitization & Support by Manufacturers and Vendors
## Current Standards
All major block device interface standards contain optional block commands for sanitization.
Below are the standards listed for reference:
* [SATA ATA/ATAPI Sanitization Command according to ACS-3 Standard](https://people.freebsd.org/~imp/asiabsdcon2015/works/d2161r5-ATAATAPI_Command_Set_-_3.pdf);
* [SAS Sanitization Command according to SBC-4 Standard](https://www.t10.org/members/w_sbc4.htm);
* [NVME Sanitization Command according to NVME Command Set Specification](https://nvmexpress.org/wp-content/uploads/NVM-Express-NVM-Command-Set-Specification-Revision-1.1-2024.08.05-Ratified.pdf).
## Manufacturer and Vendor Support for Sanitization
Given that the support for sanitization is optional and not a mandatory aspect of the respective standards the support accross vendors can vary largely.
The following table does not claim or warrant to be complete, it is highly advised to always validate the information with the manufacturer:
| Manufacturer | Manufacturer Tool (MFT) | SATA | SAS | NVME |
|----------------- |------------------------------------------------------ |----------------- |------------------- |---------- |
| Samsung | Samsung DC Toolkit 3.0 | Use MFT* | Use MFT | Use MFT |
| Intel / Solidigm | Solidigm™ Storage Tool | Use MFT*\* | Use MFT | Use MFT |
| Western Digital | supports SAS / SCSI format unit command | hdparm sanitize | sg_utils sanitize | nvme-cli sanitize |
| Sandisk | supports SAS / SATA / SCSI format unit command | hdparm sanitize | sg_utils sanitize | nvme-cli sanitize |
| Seagate | Open Seachest | Use MFT | Use MFT | Use MFT |
| SK Hynix | Unconfirmed for Linux | N/A | N/A | N/A |
| Kioxia | Unconfirmed for Linux | N/A | N/A | N/A |
| Micron | Unconfirmed for Linux | N/A | N/A | N/A |
| Kingston | Unconfirmed for Linux | N/A | N/A | N/A |
| Others | Unconfirmed for Linux | N/A | N/A | N/A |
\* [DC Toolkit 3.0 contains hdparm, but Interface Standard Compliance is unconfirmed](https://download.semiconductor.samsung.com/resources/user-manual/Samsung_DCToolkit_V3.0_User_Guide.pdf)
\*\* [Generally Supported, but individual models might offer different levels of support](https://community.solidigm.com/t5/solid-state-drives-nand/support-for-sata-sanitize-command/td-p/24452)
## Advised Procedure for Sanitization of SSD Drives
1. Complete an intial sanitization using the manufacturer tools or if supported by the manufacturer use hdparm, sg_utils or nvme;
2. Follow up with SHREDOS/Nwipe with a single PRNG stream with verification (PRNG data is extremely hard if not impossible to compress and therefor has to be written out by the firmware);
3. Complete an additional sanitization using the manufacturer tools or if supported by the manufacturer use hdparm, sg_utils or nvme;
4. Validate that the data has been overwritten.