file-restore/disk: support ZFS subvols with mountpoint=legacy

These require mounting using the regular 'mount' syscall.
Auto-generates an appropriate mount path.

Note that subvols with mountpoint=none cannot be mounted this way, and
would require setting the mountpoint property, which is not possible as
the zpools have to be imported with readonly=on.

Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
This commit is contained in:
Stefan Reiter 2021-06-16 12:55:52 +02:00 committed by Thomas Lamprecht
parent 86ce56f193
commit d9b318a444
1 changed files with 37 additions and 6 deletions

View File

@ -228,6 +228,34 @@ impl Filesystems {
cmd.args(["mount", "-a"].iter()); cmd.args(["mount", "-a"].iter());
run_command(cmd, None)?; run_command(cmd, None)?;
// detect any datasets with 'legacy' mountpoints
let mut cmd = Command::new("/sbin/zfs");
cmd.args(["list", "-Hpro", "name,mountpoint", &data.name].iter());
let mps = run_command(cmd, None)?;
for subvol in mps.lines() {
let subvol = subvol.splitn(2, '\t').collect::<Vec<&str>>();
if subvol.len() != 2 {
continue;
}
let name = subvol[0];
let mp = subvol[1];
if mp == "legacy" {
let mut newmp = PathBuf::from(format!(
"{}/legacy-{}",
&mntpath,
name.replace('/', "_")
));
let mut i = 1;
while newmp.exists() {
newmp.set_extension(i.to_string());
i += 1;
}
create_dir_all(&newmp)?;
self.do_mount(Some(name), newmp.to_string_lossy().as_ref(), "zfs")?;
}
}
// Now that we have imported the pool, we can also query the size // Now that we have imported the pool, we can also query the size
let mut cmd = Command::new("/sbin/zpool"); let mut cmd = Command::new("/sbin/zpool");
cmd.args(["list", "-o", "size", "-Hp", &data.name].iter()); cmd.args(["list", "-o", "size", "-Hp", &data.name].iter());
@ -244,19 +272,14 @@ impl Filesystems {
} }
fn try_mount(&self, source: &str, target: &str) -> Result<(), Error> { fn try_mount(&self, source: &str, target: &str) -> Result<(), Error> {
use nix::mount::*;
create_dir_all(target)?; create_dir_all(target)?;
// try all supported fs until one works - this is the way Busybox's 'mount' does it too: // try all supported fs until one works - this is the way Busybox's 'mount' does it too:
// https://git.busybox.net/busybox/tree/util-linux/mount.c?id=808d93c0eca49e0b22056e23d965f0d967433fbb#n2152 // https://git.busybox.net/busybox/tree/util-linux/mount.c?id=808d93c0eca49e0b22056e23d965f0d967433fbb#n2152
// note that ZFS is intentionally left out (see scan()) // note that ZFS is intentionally left out (see scan())
let flags =
MsFlags::MS_RDONLY | MsFlags::MS_NOEXEC | MsFlags::MS_NOSUID | MsFlags::MS_NODEV;
for fs in &self.supported_fs { for fs in &self.supported_fs {
let fs: &str = fs.as_ref(); let fs: &str = fs.as_ref();
let opts = FS_OPT_MAP.get(fs).copied(); match self.do_mount(Some(source), target, fs) {
match mount(Some(source), target, Some(fs), flags, opts) {
Ok(()) => { Ok(()) => {
info!("mounting '{}' succeeded, fstype: '{}'", source, fs); info!("mounting '{}' succeeded, fstype: '{}'", source, fs);
return Ok(()); return Ok(());
@ -270,6 +293,14 @@ impl Filesystems {
bail!("all mounts failed or no supported file system") bail!("all mounts failed or no supported file system")
} }
fn do_mount(&self, source: Option<&str>, target: &str, fs: &str) -> Result<(), nix::Error> {
use nix::mount::*;
let flags =
MsFlags::MS_RDONLY | MsFlags::MS_NOEXEC | MsFlags::MS_NOSUID | MsFlags::MS_NODEV;
let opts = FS_OPT_MAP.get(fs).copied();
mount(source, target, Some(fs), flags, opts)
}
} }
pub struct DiskState { pub struct DiskState {