From 82b0683f3bc4cfe8806046991112b9c58576ec4a Mon Sep 17 00:00:00 2001 From: Andriy Gapon Date: Tue, 27 Oct 2015 15:03:41 +0200 Subject: [PATCH 1/2] do not pass bad or wrong file descriptor to lzc_hold on illumos Doing that would cause a kernel panic. For more details see https://www.illumos.org/issues/6379 --- libzfs_core/test/test_libzfs_core.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/libzfs_core/test/test_libzfs_core.py b/libzfs_core/test/test_libzfs_core.py index 3cdb48c..9e7f9db 100644 --- a/libzfs_core/test/test_libzfs_core.py +++ b/libzfs_core/test/test_libzfs_core.py @@ -159,6 +159,11 @@ def snap_always_unmounted_before_destruction(): return (platform.system() != 'Linux', 'snapshot is not auto-unmounted') +def illumos_bug_6379(): + # zfs_ioc_hold() panics on a bad cleanup fd + return (platform.system() == 'SunOS', 'see https://www.illumos.org/issues/6379') + + def needs_support(function): return unittest.skipUnless(lzc.is_supported(function), '{} not available'.format(function.__name__)) @@ -2534,6 +2539,7 @@ def test_promote_not_cloned(self): with self.assertRaises(lzc_exc.NotClone): lzc.lzc_promote(fs) + @unittest.skipIf(*illumos_bug_6379()) def test_hold_bad_fd(self): snap = ZFSTest.pool.getRoot().getSnap() lzc.lzc_snapshot([snap]) @@ -2544,6 +2550,7 @@ def test_hold_bad_fd(self): with self.assertRaises(lzc_exc.BadHoldCleanupFD): lzc.lzc_hold({snap: 'tag'}, bad_fd) + @unittest.skipIf(*illumos_bug_6379()) def test_hold_bad_fd_2(self): snap = ZFSTest.pool.getRoot().getSnap() lzc.lzc_snapshot([snap]) @@ -2551,6 +2558,7 @@ def test_hold_bad_fd_2(self): with self.assertRaises(lzc_exc.BadHoldCleanupFD): lzc.lzc_hold({snap: 'tag'}, -2) + @unittest.skipIf(*illumos_bug_6379()) def test_hold_bad_fd_3(self): snap = ZFSTest.pool.getRoot().getSnap() lzc.lzc_snapshot([snap]) @@ -2560,6 +2568,7 @@ def test_hold_bad_fd_3(self): with self.assertRaises(lzc_exc.BadHoldCleanupFD): lzc.lzc_hold({snap: 'tag'}, bad_fd) + @unittest.skipIf(*illumos_bug_6379()) def test_hold_wrong_fd(self): snap = ZFSTest.pool.getRoot().getSnap() lzc.lzc_snapshot([snap]) From e5a353d5381d351e8d30124ff3bd0c018b686101 Mon Sep 17 00:00:00 2001 From: Andriy Gapon Date: Tue, 27 Oct 2015 15:05:46 +0200 Subject: [PATCH 2/2] add illumos support for mounting ZFS filesystem and snapshots First, 'mount -F zfs' is used on illumos as opposed to 'mount -t zfs' on FreeBSD and Linux to mount a filesystem with a legacy mountpoint. Second, a snapshot can not be explicitly mounted on illumos as it is not considered to be an independent mountable entity. There are two ways to access the snapshot's content: - create a read-only clone filesystem and mount that - traverse to the snapshot content through .zfs/snapshot/ At the moment the latter mechanism is used on illumos. In fact, both of the above mechanisms should work on all supported platforms, so we might switch to one of them for all platforms in the future. --- libzfs_core/test/test_libzfs_core.py | 67 +++++++++++++++++++++++++--- 1 file changed, 62 insertions(+), 5 deletions(-) diff --git a/libzfs_core/test/test_libzfs_core.py b/libzfs_core/test/test_libzfs_core.py index 9e7f9db..364c2e5 100644 --- a/libzfs_core/test/test_libzfs_core.py +++ b/libzfs_core/test/test_libzfs_core.py @@ -42,21 +42,78 @@ def suppress(exceptions=None): @contextlib.contextmanager -def zfs_mount(fs): +def _zfs_mount(fs): mntdir = tempfile.mkdtemp() + if platform.system() == 'SunOS': + mount_cmd = ['mount', '-F', 'zfs', fs, mntdir] + else: + mount_cmd = ['mount', '-t', 'zfs', fs, mntdir] + unmount_cmd = ['umount', '-f', mntdir] + try: - subprocess.check_output( - ['mount', '-t', 'zfs', fs, mntdir], stderr=subprocess.STDOUT) + subprocess.check_output(mount_cmd, stderr=subprocess.STDOUT) try: yield mntdir finally: with suppress(): - subprocess.check_output( - ['umount', '-f', mntdir], stderr=subprocess.STDOUT) + subprocess.check_output(unmount_cmd, stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as e: + print 'failed to mount %s @ %s : %s' % (fs, mntdir, e.output) + raise finally: os.rmdir(mntdir) +# XXX On illumos it is impossible to explicitly mount a snapshot. +# So, either we need to implicitly mount it using .zfs/snapshot/ +# or we need to create a clone and mount it readonly (and discard +# it afterwards). +# At the moment the former approach is implemented. + +# This dictionary is used to keep track of mounted filesystems +# (not snapshots), so that we do not try to mount a filesystem +# more than once in the case more than one snapshot of the +# filesystem is accessed from the same context or the filesystem +# and its snapshot are accessed. +_mnttab = {} + + +@contextlib.contextmanager +def _illumos_mount_fs(fs): + if fs in _mnttab: + yield _mnttab[fs] + else: + with _zfs_mount(fs) as mntdir: + _mnttab[fs] = mntdir + try: + yield mntdir + finally: + _mnttab.pop(fs, None) + + +@contextlib.contextmanager +def _illumos_mount_snap(fs): + (base, snap) = fs.split('@', 1) + with _illumos_mount_fs(base) as mntdir: + yield os.path.join(mntdir, '.zfs', 'snapshot', snap) + + +@contextlib.contextmanager +def _zfs_mount_illumos(fs): + if '@' not in fs: + with _illumos_mount_fs(fs) as mntdir: + yield mntdir + else: + with _illumos_mount_snap(fs) as mntdir: + yield mntdir + + +if platform.system() == 'SunOS': + zfs_mount = _zfs_mount_illumos +else: + zfs_mount = _zfs_mount + + @contextlib.contextmanager def cleanup_fd(): fd = os.open('/dev/zfs', os.O_EXCL)