How to restore unencrypted filesystem from encrypted backup?

Hi all,

Last week I decided to finally migrate my RAIDZ2 nas (+ local backup destination) to a mirror set up with an encrypted root dataset (to facilitate raw sends to an untrusted offsite replication destination – WIP).

I was very pleased with how everything went but have already run into my first minor snag. As luck would have it, an unrelated machine on ZFS root decided to die this week. Thankfully I was sending this machine’s backups to the nas, so I don’t think I have any significant data loss.

(Rereading before I submit – probably helpful context to note that this is my first real system recovery with ZFS.)

The trouble I’m having is figuring out how to send from the encrypted backups and do a (ideally full) replication on the new device for the dead machine.

On my machine with the backups:

  • tank is the pool
  • tank/enc is an encrypted dataset
  • my basic hierarchy is tank/enc/zfs-backups/machinename/poolname

I’ve made a recursive snapshot at tank/enc/zfs-backups/mymachine/rpool@recovery

Let’s call the new pool newpool.

My problem is that doing a recursive send fails, complaining that an encrypted dataset may not be sent with properties without the raw flag.

I was hoping I could zfs send -R ... | zfs recv -x encryption ..., but that doesn’t work.

It recommends that I raw send (-w), but I’m pretty sure that will result in me having encrypted datasets on newpool, which I don’t want in this case – nothing on this device warrants encryption.

What I’m hoping for is to send tank/enc/zfs-backups/machinename/rpool and up with a copy of rpool (including all descendants, snapshots, and ideally properties like compression), i.e. tank/enc/zfs-backups/machinename/rpool/foo/barnewpool/foo/bar.

Based on a few openzfs issues, I’m thinking that this is not readily possible due to a conflict between -R and the encryption property, and that instead I’ll need to manually send datasets over and then manually address properties. Does that sound right?

How would you approach this?

What can I do to make this restoration process easier next time? Somehow I was under the impression that I was going to be able to zfs send | zfs recv and be on my way.

Correct.

Well, if you’ve got that many different permissions and you’re worried about missing stuff and you’ve got a little bit of scripting-fu at your disposal, it wouldn’t be that difficult to write a script to read all the relevant property settings on the source, then set them on the target.

Lots of OpenZFS properties are read-only, though, so you’ll need to focus on the properties you actually care about rather than just trying to blindly read and set every property that can or does exist.

Thanks for your time Jim – truly charitable of you to take time out of your day to help the masses.

Still processing, but this seems to be doing the trick for transferring all the datasets and snaps – leaving it here in case it helps others. We’ll call it MIT if only for the “no warranty implied.”

You’re right, probably easy enough to just set properties manually.

#!/usr/bin/env bash

# USAGE:
# ./restore.sh dataset/containing/backup/pool newpool

set -Eeuf -o pipefail

main() {
  if [[ "${EUID}" -ne 0 ]]; then
    sudo "$0" "$@"
    exit $?
  fi

  local source=$1
  local destbase=$2

  local datasets
  mapfile -t datasets < <(zfs list -H -r -t fs -o name "${source}")

  local dest snaps
  for ds in "${datasets[@]}"; do
    dest=${destbase}${ds#"${source}"}
    mapfile -t snaps < <(zfs list -H -t snap -o name "${ds}")

    zfs send -v "${snaps[0]}" |
      zfs recv -v -u -F "${dest}"

    zfs send -v -I "${snaps[0]}" "${snaps[-1]}" |
      zfs recv -v -u "${dest}"
  done
}
main "$@"
4 Likes

Hey, I was today years old when I learned about mapfile! Thanks for that. :clinking_beer_mugs:

1 Like