diff --git a/src/driver/controller-zfs/index.js b/src/driver/controller-zfs/index.js index 7017c12..81e9adf 100644 --- a/src/driver/controller-zfs/index.js +++ b/src/driver/controller-zfs/index.js @@ -1471,6 +1471,20 @@ class ControllerZfsBaseDriver extends CsiBaseDriver { // NOTE: -f does NOT allow deletes if dependent filesets exist // NOTE: -R will recursively delete items + dependent filesets // delete dataset + // Unmount explicitly before destroying. `zfs destroy -f` forces the + // unmount via umount2(MNT_FORCE), which LXC's default seccomp profile + // rejects (`reject_force_umount`), so on a containerised ZFS host every + // delete of a mounted dataset fails with "cannot unmount ... unmount + // failed" and never recovers. A plain unmount is permitted there. + // + // Best-effort: a dataset that is a zvol, already unmounted, or absent has + // nothing to unmount, and any real problem still surfaces from destroy. + try { + await zb.zfs.unmount(datasetName, { idempotent: true }); + } catch (err) { + // fall through to destroy, which reports the actionable error + } + try { await GeneralUtils.retry( 12, diff --git a/src/utils/zfs.js b/src/utils/zfs.js index a26db89..0ad04b2 100644 --- a/src/utils/zfs.js +++ b/src/utils/zfs.js @@ -1445,6 +1445,53 @@ class Zetabyte { * @param {*} dataset * @param {*} property */ + /** + * zfs unmount [-f] filesystem|mountpoint + * + * Deliberately does NOT support -f. A forced unmount issues + * umount2(MNT_FORCE), which LXC's default seccomp profile rejects + * (`reject_force_umount`), so it fails with EPERM on containerised ZFS + * hosts. A plain unmount is permitted there and is sufficient. + * + * @param {*} dataset + * @param {*} options + */ + unmount: function (dataset, options = {}) { + if (!(arguments.length >= 1)) throw Error("Invalid arguments"); + + return new Promise((resolve, reject) => { + const idempotent = + "idempotent" in options + ? options.idempotent + : "idempotent" in zb.options + ? zb.options.idempotent + : false; + + let args = []; + args.push("unmount"); + args.push(dataset); + + zb.exec( + zb.options.paths.zfs, + args, + { timeout: zb.options.timeout }, + function (error, stdout, stderr) { + if ( + error && + !( + idempotent && + (stderr.includes("not currently mounted") || + stderr.includes("not a mountpoint") || + stderr.includes("dataset does not exist")) + ) + ) + return reject(zb.helpers.zfsError(error, stderr)); + return resolve(stdout); + } + ); + }); + }, + inherit: function (dataset, property) { if (arguments.length != 2) throw Error("Invalid arguments");