Skip to content

Native Encryption Conversion For Existing Plaintext Datasets #18646

@tpmullan

Description

@tpmullan

Describe the feature you would like to see added to OpenZFS

I would like OpenZFS to support converting an existing plaintext filesystem dataset into a native encrypted dataset without requiring a second full-size copy of the dataset.

Today, native encryption must be selected when the dataset is created. For large existing plaintext datasets, the practical migration path is to create a new encrypted dataset and move data into it with zfs send | zfs receive, rsync, or a backup/restore workflow. That is safe when spare capacity exists, but it breaks down for large pools where used data is greater than half the pool size and no external temporary capacity is available.

This proposal is for a new filesystem-native conversion feature, not encryption below ZFS. The intended result is a real OpenZFS native encrypted dataset with normal native-encryption behavior after conversion completes.

One possible interface would be an encrypt command with mode options:

zfs encrypt [--dry-run] pool/dataset
zfs encrypt --status pool/dataset
zfs encrypt --resume pool/dataset
zfs encrypt --abort pool/dataset

Names are illustrative. The important part is the behavior: a resumable conversion that rewrites the dataset's logical block tree from plaintext blocks to encrypted replacement blocks in bounded batches using normal ZFS copy-on-write semantics.

The central safety invariant should be:

A plaintext block reference is never released until its encrypted replacement has been written, read back, decrypted, verified, and committed through normal ZFS transaction semantics.

For a first version, strict eligibility rules seem reasonable:

  • filesystem datasets only; no zvols;
  • dataset is currently unencrypted;
  • no snapshots;
  • no clones;
  • no bookmarks;
  • no dedup support during conversion;
  • dataset is unmounted or mounted read-only;
  • pool is healthy;
  • no active scrub, resilver, trim, device removal, checkpoint discard, or similar pool-wide operation;
  • sufficient free space exists for at least one conversion batch plus metadata overhead.

The no-snapshots/no-clones restriction is painful, but it removes one of the hardest v1 problems: shared historical references to plaintext blocks. A limited safe feature would still be useful.

How will this feature improve OpenZFS?

This would let administrators adopt OpenZFS native encryption for existing large datasets without buying or temporarily attaching enough storage to hold a full second copy of the data.

The user-visible problem is common:

  • a pool was created before native encryption was required;
  • the dataset now contains tens of TB of data;
  • the data should be encrypted at rest;
  • creating a full duplicate encrypted copy is impractical because of capacity, cost, time, and device wear;
  • the administrator wants native ZFS encryption rather than LUKS/GELI or another block-device encryption layer underneath ZFS.

A constrained converter would fill the gap between "recreate the dataset" and "leave historical data plaintext forever."

This is intentionally not a per-disk migration. RAIDZ redundancy is valuable for pool health, but native encryption lives at the logical block/dataset layer, so conversion needs to operate on object sets, dnodes, block pointers, indirect blocks, and dataset metadata.

Additional context

I am interested in working on this, but I would like maintainer feedback before attempting an implementation. The proposal below is intentionally strict for v1 because safety and recovery semantics matter more than broad initial coverage.

The dataset should persist explicit conversion state, for example:

  • none
  • preflight
  • converting
  • verifying
  • failed-needs-resume
  • committed
  • abortable

Normal read-write import should fail if any dataset is in an incomplete conversion state, unless import is happening through the explicit conversion resume path.

Read-only import should remain available for diagnostics:

zpool import -o readonly=on pool
zfs encrypt --status pool/dataset

Normal read-write import should fail with a clear diagnostic, for example:

pool contains dataset pool/dataset in incomplete native encryption conversion;
import read-only for diagnostics or resume conversion with zfs encrypt --resume pool/dataset

For v1, normal scrub should probably refuse to run during conversion unless a conversion-aware scrub mode is explicitly implemented.

Abort should be conservative:

  • abort is allowed only before any plaintext references have been released;
  • after plaintext references have been released, the supported recovery path is resume-to-completion;
  • a partially converted dataset must never silently appear as normal read-write storage.

This feature would need fault-injection tests before it should be trusted. Minimum coverage should include:

  • empty dataset conversion;
  • small files;
  • large files spanning indirect blocks;
  • xattrs, ACLs, and spill blocks;
  • compression enabled;
  • multiple record sizes;
  • crash after encrypted replacement write and before metadata commit;
  • crash after metadata commit and before plaintext free completion;
  • read-only import exposes diagnostic state but does not allow writes;
  • normal import refuses incomplete conversion;
  • resume completes after each injected failure point;
  • abort is allowed only before plaintext release;
  • preflight rejects snapshots, clones, bookmarks, zvols, mounted read-write datasets, unhealthy pools, and active scrub/resilver.

Known hard areas:

  • metadata conversion, especially dnodes, indirect blocks, ZAPs, spill blocks, xattrs, ACLs, and embedded data;
  • encryption root creation and key hierarchy metadata;
  • crash recovery during partially committed batches;
  • space exhaustion during a batch;
  • zfs send / zfs receive behavior for converted datasets;
  • scrub behavior against mixed conversion state;
  • compatibility with older OpenZFS implementations.

Open questions for maintainers:

  • Is this feature direction acceptable in principle if v1 is strictly limited?
  • Is a new incompatible feature flag the right compatibility boundary?
  • Should conversion be implemented as zfs encrypt with mode options, a mode of an existing rewrite/remap mechanism if one exists, or another interface?
  • Should read-only diagnostic import be allowed during incomplete conversion?
  • Is refusing normal scrub during conversion acceptable for v1?
  • Are there existing internal rewrite/remap facilities that would be a better implementation starting point?
  • Are snapshots/clones/dedup restrictions acceptable for an initial proposal?

Current OpenZFS documentation describes native encryption as a dataset creation-time property that cannot be changed afterward. Existing migration guidance effectively requires creating a new encrypted dataset and moving data into it.

Relevant references:

I did not find an obvious existing issue or accepted proposal for this exact plaintext-to-native-encrypted conversion feature. If there is prior art or an existing design discussion, pointers would be appreciated.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type: FeatureFeature request or new feature

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions