A C# application designed to facilitate backup and restore operations to an LTO tape drive on Windows. This application enables seamless management of backups by handling file organization, descriptor creation, and efficient tape handling processes for loading, writing, and restoring data.
Go to file
2026-09-06 15:33:46 +02:00
.cursor (feature): add cross-platform hosts, windows/linux service registration 2026-08-21 10:44:36 +02:00
docs (feature): add ability to read and write to the Cartridge EEPROM 2025-07-27 02:10:17 -03:00
src (feature): mvvm ui, appdata settings, and desktop packages 2026-09-06 15:33:46 +02:00
utils (feature): mvvm ui, appdata settings, and desktop packages 2026-09-06 15:33:46 +02:00
.editorconfig (feature): add cross-platform hosts, windows/linux service registration 2026-08-21 10:44:36 +02:00
.gitattributes (feature): init 2024-10-31 14:06:32 -07:00
.gitignore (feature): add cross-platform hosts, windows/linux service registration 2026-08-21 10:44:36 +02:00
AGENTS.md (feature): mvvm ui, appdata settings, and desktop packages 2026-09-06 15:33:46 +02:00
CHANGELOG.md (feature): mvvm ui, appdata settings, and desktop packages 2026-09-06 15:33:46 +02:00
CONTRIBUTING.md (feature): mvvm ui, appdata settings, and desktop packages 2026-09-06 15:33:46 +02:00
Directory.Build.props (feature): mvvm ui, appdata settings, and desktop packages 2026-09-06 15:33:46 +02:00
LICENSE.md (feature): init 2024-10-31 14:06:32 -07:00
README.md (feature): mvvm ui, appdata settings, and desktop packages 2026-09-06 15:33:46 +02:00

MaksIT.LTO.Backup

Line Coverage Branch Coverage Method Coverage .NET License Platform

Cross-platform toolkit for LTO tape backup and restore. Console, Avalonia UI, and Worker share one service layer.

See LICENSE.md (GPLv2). Changes: CHANGELOG.md. Contributing: CONTRIBUTING.md.

Host Project Role
Console MaksIT.LTO.Backup Interactive menu / backup --name CLI
Avalonia MaksIT.LTO.Backup.UI MVVM operations UI (Windows + Linux; CommunityToolkit.Mvvm)
Worker MaksIT.LTO.Backup.Service Scheduled library backups (Windows service / systemd)

Device access modes:

  • Physical — real drive/library on Windows (\\.\Tape0, \\.\Changer0; admin) or Linux (/dev/nst0, /dev/sg*; tape/disk group)
  • Emulated — file-backed drive + library (no hardware; primary CI / no-hardware path)

Current line is 0.1.0-alpha.2 (prerelease, not production-ready). Emulator coverage is the primary validation path; physical tape/changer and Linux SMB are not hardware-proven yet. Use at your own risk.


If you find this project useful, please consider supporting its development:

Buy Me A Coffee


Features

  • Backup and restore with AES-GCM encrypted on-tape JSON descriptors and SHA-256 file verification
  • SchemaVersion 2 layout: BOT index (MLTI), payload, filemark, descriptor preamble (MLTD), ciphertext, double filemark
  • Overwrite or Append write modes (multiple sets per cartridge via BOT set table)
  • Fail-fast restore on checksum mismatch (FailFastOnChecksumMismatch)
  • MAM summary attributes written after backup (app name/version, host, text label)
  • Local paths on all platforms; SMB source/destination on Windows (WNet) and Linux (SMB2 staging via SMBLibrary)
  • LTO generation block sizes (LTO1–LTO9) and media-capacity checks
  • Drive ops: load, eject, short erase, status, cartridge memory (MAM) / barcode
  • Library ops: inventory, move medium (emulated; physical Windows IOCTL_CHANGER_* and Linux SG_IO)
  • Shared library used by console, Avalonia UI, and Worker
  • Live Avalonia monitor for drive status and library inventory
  • Avalonia Service tab: install / uninstall / start / stop Worker (Windows + Linux)
  • Emulator-backed unit / E2E tests without a physical tape ({tapeId}.drive.json + {tapeId}.blocks.bin)

Requirements

  • .NET 10 SDK
  • Windows: physical tape/changer IOCTLs; Avalonia UI; SMB via WNet
  • Linux: physical tape (/dev/nst*) and changer (/dev/sg*); Avalonia UI; SMB via managed SMB2 client; or DeviceMode=Emulated
  • Optional: PowerShell 7+ for RepoUtils test/release engines under utils/

Solution layout

utils/                          # RepoUtils release / test engines
src/
  MaksIT.LTO.slnx
  MaksIT.LTO.Core/              # tape/library APIs + emulators
  MaksIT.LTO.Backup.Shared/     # shared config, orchestrators, host helpers
  MaksIT.LTO.Backup/            # console host
  MaksIT.LTO.Backup.UI/         # Avalonia host (Windows + Linux)
  MaksIT.LTO.Backup.Service/    # scheduled Worker (Windows / Linux)
  MaksIT.LTO.Tests/             # emulator tests

Scheduled backups (library only)

Automatic scheduling runs only when Topology is TapeLibrary. StandaloneDrive remains manual (console / UI).

Backup jobs define what (source, Tapes pool, LTO gen). Drive selection is automatic.

Aggregations define when and in what order jobs run as one wave (e.g. Night / Afternoon):

  • JobNames — ordered list of backup job names
  • Schedule — UScheduler-compatible calendar with overdue catch-up (RunMonth, RunWeekday, RunTime UTC HH:mm, MinIntervalMinutes)
  • Disabled / LastRunUtc — wave-level

The Worker holds an exclusive lease for the whole aggregation (no mid-wave collisions). If another wave is busy, the due wave retries on the next poll without consuming LastRunUtc. After each job: unload cartridge so the next job can claim any empty drive.

Worker lifecycle (also from Avalonia Service tab):

MaksIT.LTO.Backup.Service --install
MaksIT.LTO.Backup.Service --start
MaksIT.LTO.Backup.Service --stop
MaksIT.LTO.Backup.Service --uninstall

Console:

MaksIT.LTO.Backup backup --name "Normal test"
MaksIT.LTO.Backup backup --aggregation "Night"
MaksIT.LTO.Backup backup --aggregation "Night" --scheduled

Install/uninstall typically require administrator (Windows) or root (Linux systemd).

Configuration

One shared model for the whole solution. Seed JSON ships next to each host; runtime writes go to AppData so Program Files does not need elevation.

Edit the Shared seed for factory defaults, or use the UI Settings tab and click Save configuration.

DeviceMode selects the backend (Physical / Emulated).
Topology selects how hardware is used:

  • StandaloneDrive — Drive tab only (load/eject/status/MAM). Backup tab runs against that drive.
  • TapeLibrary — Library tab (inventory, move into drive bays, MAM on loaded cartridge). Backup tab runs only when a cartridge is in a drive bay.
{
  "Configuration": {
    "DeviceMode": "Emulated",
    "Topology": "StandaloneDrive",
    "TapePath": "\\\\.\\Tape0",
    "LibraryPath": "\\\\.\\Changer0",
    "WriteDelay": 100,
    "FailFastOnChecksumMismatch": true,
    "Emulator": {
      "DataDirectory": ".\\emulator-data",
      "Library": { "SlotCount": 24, "DriveCount": 2, "IePortCount": 1 }
    },
    "Backups": [
      {
        "Name": "Normal test",
        "Barcode": "LTO001",
        "LTOGen": "LTO5",
        "WriteMode": "Overwrite",
        "Source": { "LocalPath": { "Path": "F:\\LTO\\Backup" } },
        "Destination": { "LocalPath": { "Path": "F:\\LTO\\Restore" } }
      }
    ]
  }
}

On-tape format (SchemaVersion 2)

[BOT index MLTI] → [file blocks…] → FM → [MLTD preamble] → [length-prefixed AES-GCM descriptor] → FM FM

Append adds another payload+descriptor set at EOD and rewrites the BOT set table (max 16 sets).

Descriptor secret

On first run a secret.txt is created for descriptor encryption (first line = current key). Keep a safe copy.

To rotate: put the new key on line 1 of secret.txt and move the old key to line 2+ or into secret.history.txt. Decrypt tries the current key then prior keys.

To avoid leaving the file on disk, set a machine environment variable (used as the preferred encrypt/decrypt key):

[System.Environment]::SetEnvironmentVariable(
  "LTO_BACKUP_SECRET",
  "<secret.txt content here>",
  [System.EnvironmentVariableTarget]::Machine
)

Console menu

  1. Load tape
  2. Backup
  3. Restore
  4. Eject tape
  5. Get device status
  6. Tape erase (short)
  7. Read cartridge memory
  8. Move medium (library)
  9. Library inventory
  10. Exit

Build and run

cd src
dotnet build MaksIT.LTO.slnx
dotnet run --project .\MaksIT.LTO.Backup

Avalonia UI (Windows or Linux):

dotnet run --project .\MaksIT.LTO.Backup.UI

Prefer RepoUtils for a GitHub release (utils\Invoke-ReleasePackage.bat). Assets are siblings: portable maksit-lto-backup-{version}.zip (win-x64 console, Avalonia UI, Worker), Windows setup maksit-lto-backup-{version}.exe (Avalonia UI), and maksit-lto-backup-{version}.flatpak (Avalonia UI). The installer and Flatpak are not inside the zip. Manual publish:

cd src
dotnet publish .\MaksIT.LTO.Backup -c Release -o ..\releases\console
dotnet publish .\MaksIT.LTO.Backup.UI -c Release -o ..\releases\ui
dotnet publish .\MaksIT.LTO.Backup.Service -c Release -o ..\releases\service

Physical mode: Windows needs elevation; Linux needs access to /dev/nst* / /dev/sg* (often tape/disk group). Discover changer nodes with lsscsi -g.

Tests and coverage

Emulator-backed tests (no tape required):

cd src
dotnet test .\MaksIT.LTO.Tests

With Cobertura coverage:

cd src
dotnet test .\MaksIT.LTO.Tests --collect:"XPlat Code Coverage" --results-directory .\TestResults

Coverage shields at the top of this README use shields.io and are rewritten by the CoverageBadges plugin when utils\Invoke-TestEngine.bat runs.

RepoUtils release pipeline

Vendored under utils/ (from MaksIT RepoUtils community):

Action Entry
Test utils\Invoke-TestEngine.bat
Release utils\Invoke-ReleasePackage.bat

GitHub assets are siblings: portable maksit-lto-backup-{version}.zip (win-x64), Windows setup exe (Avalonia UI), and Flatpak (Avalonia UI). The installer and Flatpak are not inside the zip. On Windows the Flatpak bundle is built via WSL Debian.

Settings:

  • utils/engines/test/scriptSettings.json
  • utils/engines/release/scriptSettings.json

License

GPLv2 — see LICENSE.md.

© Maksym Sadovnychyy (MAKS-IT)