xref: /freebsd/sys/contrib/openzfs/contrib/pyzfs/libzfs_core/test/test_libzfs_core.py (revision 61145dc2b94f12f6a47344fb9aac702321880e43)
1# SPDX-License-Identifier: Apache-2.0
2#
3# Copyright 2015 ClusterHQ
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#    http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17
18"""
19Tests for `libzfs_core` operations.
20
21These are mostly functional and conformance tests that validate
22that the operations produce expected effects or fail with expected
23exceptions.
24"""
25from __future__ import absolute_import, division, print_function
26
27import unittest
28import contextlib
29import errno
30import filecmp
31import os
32import platform
33import resource
34import shutil
35import stat
36import subprocess
37import sys
38import tempfile
39import time
40import uuid
41import itertools
42import zlib
43from .. import _libzfs_core as lzc
44from .. import exceptions as lzc_exc
45from .._nvlist import packed_nvlist_out
46
47
48def _print(*args):
49    for arg in args:
50        print(arg, end=' ')
51    print()
52
53
54@contextlib.contextmanager
55def suppress(exceptions=None):
56    try:
57        yield
58    except BaseException as e:
59        if exceptions is None or isinstance(e, exceptions):
60            pass
61        else:
62            raise
63
64
65@contextlib.contextmanager
66def _zfs_mount(fs):
67    mntdir = tempfile.mkdtemp()
68    if platform.system() == 'SunOS':
69        mount_cmd = ['mount', '-F', 'zfs', fs, mntdir]
70    else:
71        mount_cmd = ['mount', '-t', 'zfs', fs, mntdir]
72    unmount_cmd = ['umount', '-f', mntdir]
73
74    try:
75        subprocess.check_output(mount_cmd, stderr=subprocess.STDOUT)
76        try:
77            yield mntdir
78        finally:
79            with suppress():
80                subprocess.check_output(unmount_cmd, stderr=subprocess.STDOUT)
81    except subprocess.CalledProcessError as e:
82        print('failed to mount %s @ %s : %s' % (fs, mntdir, e.output))
83        raise
84    finally:
85        os.rmdir(mntdir)
86
87
88# XXX On illumos it is impossible to explicitly mount a snapshot.
89# So, either we need to implicitly mount it using .zfs/snapshot/
90# or we need to create a clone and mount it readonly (and discard
91# it afterwards).
92# At the moment the former approach is implemented.
93
94# This dictionary is used to keep track of mounted filesystems
95# (not snapshots), so that we do not try to mount a filesystem
96# more than once in the case more than one snapshot of the
97# filesystem is accessed from the same context or the filesystem
98# and its snapshot are accessed.
99_mnttab = {}
100
101
102@contextlib.contextmanager
103def _illumos_mount_fs(fs):
104    if fs in _mnttab:
105        yield _mnttab[fs]
106    else:
107        with _zfs_mount(fs) as mntdir:
108            _mnttab[fs] = mntdir
109            try:
110                yield mntdir
111            finally:
112                _mnttab.pop(fs, None)
113
114
115@contextlib.contextmanager
116def _illumos_mount_snap(fs):
117    (base, snap) = fs.split('@', 1)
118    with _illumos_mount_fs(base) as mntdir:
119        yield os.path.join(mntdir, '.zfs', 'snapshot', snap)
120
121
122@contextlib.contextmanager
123def _zfs_mount_illumos(fs):
124    if '@' not in fs:
125        with _illumos_mount_fs(fs) as mntdir:
126            yield mntdir
127    else:
128        with _illumos_mount_snap(fs) as mntdir:
129            yield mntdir
130
131
132if platform.system() == 'SunOS':
133    zfs_mount = _zfs_mount_illumos
134else:
135    zfs_mount = _zfs_mount
136
137
138@contextlib.contextmanager
139def cleanup_fd():
140    fd = os.open('/dev/zfs', os.O_EXCL)
141    try:
142        yield fd
143    finally:
144        os.close(fd)
145
146
147@contextlib.contextmanager
148def os_open(name, mode):
149    fd = os.open(name, mode)
150    try:
151        yield fd
152    finally:
153        os.close(fd)
154
155
156@contextlib.contextmanager
157def dev_null():
158    with tempfile.TemporaryFile(suffix='.zstream') as fd:
159        yield fd.fileno()
160
161
162@contextlib.contextmanager
163def dev_zero():
164    with os_open('/dev/zero', os.O_RDONLY) as fd:
165        yield fd
166
167
168@contextlib.contextmanager
169def temp_file_in_fs(fs):
170    with zfs_mount(fs) as mntdir:
171        with tempfile.NamedTemporaryFile(dir=mntdir) as f:
172            for i in range(1024):
173                f.write(b'x' * 1024)
174            f.flush()
175            yield f.name
176
177
178def make_snapshots(fs, before, modified, after):
179    def _maybe_snap(snap):
180        if snap is not None:
181            if not snap.startswith(fs):
182                snap = fs + b'@' + snap
183            lzc.lzc_snapshot([snap])
184        return snap
185
186    before = _maybe_snap(before)
187    with temp_file_in_fs(fs) as name:
188        modified = _maybe_snap(modified)
189    after = _maybe_snap(after)
190
191    return (name, (before, modified, after))
192
193
194@contextlib.contextmanager
195def streams(fs, first, second):
196    (filename, snaps) = make_snapshots(fs, None, first, second)
197    with tempfile.TemporaryFile(suffix='.zstream') as full:
198        lzc.lzc_send(snaps[1], None, full.fileno())
199        full.seek(0)
200        if snaps[2] is not None:
201            with tempfile.TemporaryFile(suffix='.zstream') as incremental:
202                lzc.lzc_send(snaps[2], snaps[1], incremental.fileno())
203                incremental.seek(0)
204                yield (filename, (full, incremental))
205        else:
206            yield (filename, (full, None))
207
208
209@contextlib.contextmanager
210def encrypted_filesystem():
211    fs = ZFSTest.pool.getFilesystem(b"encrypted")
212    name = fs.getName()
213    filename = None
214    key = os.urandom(lzc.WRAPPING_KEY_LEN)
215    with tempfile.NamedTemporaryFile() as f:
216        filename = "file://" + f.name
217        props = {
218            b"encryption": lzc.zio_encrypt.ZIO_CRYPT_AES_256_CCM,
219            b"keylocation": filename.encode(),
220            b"keyformat": lzc.zfs_keyformat.ZFS_KEYFORMAT_RAW,
221        }
222        lzc.lzc_create(name, 'zfs', props=props, key=key)
223    yield (name, key)
224
225
226def runtimeSkipIf(check_method, message):
227    def _decorator(f):
228        def _f(_self, *args, **kwargs):
229            if check_method(_self):
230                return _self.skipTest(message)
231            else:
232                return f(_self, *args, **kwargs)
233        _f.__name__ = f.__name__
234        return _f
235    return _decorator
236
237
238def skipIfFeatureAvailable(feature, message):
239    return runtimeSkipIf(
240        lambda _self: _self.__class__.pool.isPoolFeatureAvailable(feature),
241        message)
242
243
244def skipUnlessFeatureEnabled(feature, message):
245    return runtimeSkipIf(
246        lambda _self: not _self.__class__.pool.isPoolFeatureEnabled(feature),
247        message)
248
249
250def skipUnlessBookmarksSupported(f):
251    return skipUnlessFeatureEnabled(
252        'bookmarks', 'bookmarks are not enabled')(f)
253
254
255def snap_always_unmounted_before_destruction():
256    # Apparently OpenZFS automatically unmounts the snapshot
257    # only if it is mounted at its default .zfs/snapshot
258    # mountpoint under Linux.
259    return (
260        platform.system() != 'Linux', 'snapshot is not auto-unmounted')
261
262
263def illumos_bug_6379():
264    # zfs_ioc_hold() panics on a bad cleanup fd
265    return (
266        platform.system() == 'SunOS',
267        'see https://www.illumos.org/issues/6379')
268
269
270def needs_support(function):
271    return unittest.skipUnless(
272        lzc.is_supported(function),
273        '{} not available'.format(function.__name__))
274
275
276class ZFSTest(unittest.TestCase):
277    POOL_FILE_SIZE = 128 * 1024 * 1024
278    FILESYSTEMS = [b'fs1', b'fs2', b'fs1/fs']
279
280    pool = None
281    misc_pool = None
282    readonly_pool = None
283
284    @classmethod
285    def setUpClass(cls):
286        try:
287            cls.pool = _TempPool(filesystems=cls.FILESYSTEMS)
288            cls.misc_pool = _TempPool()
289            cls.readonly_pool = _TempPool(
290                filesystems=cls.FILESYSTEMS, readonly=True)
291            cls.pools = [cls.pool, cls.misc_pool, cls.readonly_pool]
292        except Exception:
293            cls._cleanUp()
294            raise
295
296    @classmethod
297    def tearDownClass(cls):
298        cls._cleanUp()
299
300    @classmethod
301    def _cleanUp(cls):
302        for pool in [cls.pool, cls.misc_pool, cls.readonly_pool]:
303            if pool is not None:
304                pool.cleanUp()
305
306    def setUp(self):
307        pass
308
309    def tearDown(self):
310        for pool in ZFSTest.pools:
311            pool.reset()
312
313    def assertExists(self, name):
314        self.assertTrue(
315            lzc.lzc_exists(name), 'ZFS dataset %s does not exist' % (name, ))
316
317    def assertNotExists(self, name):
318        self.assertFalse(
319            lzc.lzc_exists(name), 'ZFS dataset %s exists' % (name, ))
320
321    def test_exists(self):
322        self.assertExists(ZFSTest.pool.makeName())
323
324    def test_exists_in_ro_pool(self):
325        self.assertExists(ZFSTest.readonly_pool.makeName())
326
327    def test_exists_failure(self):
328        self.assertNotExists(ZFSTest.pool.makeName(b'nonexistent'))
329
330    def test_create_fs(self):
331        name = ZFSTest.pool.makeName(b"fs1/fs/test1")
332
333        lzc.lzc_create(name)
334        self.assertExists(name)
335
336    def test_create_zvol(self):
337        name = ZFSTest.pool.makeName(b"fs1/fs/zvol")
338        props = {b"volsize": 1024 * 1024}
339
340        lzc.lzc_create(name, ds_type='zvol', props=props)
341        self.assertExists(name)
342        # On Gentoo with ZFS 0.6.5.4 the volume is busy
343        # and can not be destroyed right after its creation.
344        # A reason for this is unknown at the moment.
345        # Because of that the post-test clean up could fail.
346        time.sleep(0.1)
347
348    def test_create_fs_with_prop(self):
349        name = ZFSTest.pool.makeName(b"fs1/fs/test2")
350        props = {b"atime": 0}
351
352        lzc.lzc_create(name, props=props)
353        self.assertExists(name)
354
355    def test_create_fs_wrong_ds_type(self):
356        name = ZFSTest.pool.makeName(b"fs1/fs/test1")
357
358        with self.assertRaises(lzc_exc.DatasetTypeInvalid):
359            lzc.lzc_create(name, ds_type='wrong')
360
361    def test_create_fs_below_zvol(self):
362        name = ZFSTest.pool.makeName(b"fs1/fs/zvol")
363        props = {b"volsize": 1024 * 1024}
364
365        lzc.lzc_create(name, ds_type='zvol', props=props)
366        with self.assertRaises(lzc_exc.WrongParent):
367            lzc.lzc_create(name + b'/fs')
368
369    def test_create_zvol_below_zvol(self):
370        name = ZFSTest.pool.makeName(b"fs1/fs/zvol")
371        props = {b"volsize": 1024 * 1024}
372
373        lzc.lzc_create(name, ds_type='zvol', props=props)
374        with self.assertRaises(lzc_exc.WrongParent):
375            lzc.lzc_create(name + b'/zvol', ds_type='zvol', props=props)
376
377    def test_create_fs_duplicate(self):
378        name = ZFSTest.pool.makeName(b"fs1/fs/test6")
379
380        lzc.lzc_create(name)
381
382        with self.assertRaises(lzc_exc.FilesystemExists):
383            lzc.lzc_create(name)
384
385    def test_create_fs_in_ro_pool(self):
386        name = ZFSTest.readonly_pool.makeName(b"fs")
387
388        with self.assertRaises(lzc_exc.ReadOnlyPool):
389            lzc.lzc_create(name)
390
391    def test_create_fs_without_parent(self):
392        name = ZFSTest.pool.makeName(b"fs1/nonexistent/test")
393
394        with self.assertRaises(lzc_exc.ParentNotFound):
395            lzc.lzc_create(name)
396        self.assertNotExists(name)
397
398    def test_create_fs_in_nonexistent_pool(self):
399        name = b"no-such-pool/fs"
400
401        with self.assertRaises(lzc_exc.ParentNotFound):
402            lzc.lzc_create(name)
403        self.assertNotExists(name)
404
405    def test_create_fs_with_invalid_prop(self):
406        name = ZFSTest.pool.makeName(b"fs1/fs/test3")
407        props = {b"BOGUS": 0}
408
409        with self.assertRaises(lzc_exc.PropertyInvalid):
410            lzc.lzc_create(name, 'zfs', props)
411        self.assertNotExists(name)
412
413    def test_create_fs_with_invalid_prop_type(self):
414        name = ZFSTest.pool.makeName(b"fs1/fs/test4")
415        props = {b"recordsize": b"128k"}
416
417        with self.assertRaises(lzc_exc.PropertyInvalid):
418            lzc.lzc_create(name, 'zfs', props)
419        self.assertNotExists(name)
420
421    def test_create_fs_with_invalid_prop_val(self):
422        name = ZFSTest.pool.makeName(b"fs1/fs/test5")
423        props = {b"atime": 20}
424
425        with self.assertRaises(lzc_exc.PropertyInvalid):
426            lzc.lzc_create(name, 'zfs', props)
427        self.assertNotExists(name)
428
429    def test_create_fs_with_invalid_name(self):
430        name = ZFSTest.pool.makeName(b"@badname")
431
432        with self.assertRaises(lzc_exc.NameInvalid):
433            lzc.lzc_create(name)
434        self.assertNotExists(name)
435
436    def test_create_fs_with_invalid_pool_name(self):
437        name = b"bad!pool/fs"
438
439        with self.assertRaises(lzc_exc.NameInvalid):
440            lzc.lzc_create(name)
441        self.assertNotExists(name)
442
443    def test_create_encrypted_fs(self):
444        fs = ZFSTest.pool.getFilesystem(b"encrypted")
445        name = fs.getName()
446        filename = None
447        with tempfile.NamedTemporaryFile() as f:
448            filename = "file://" + f.name
449            props = {
450                b"encryption": lzc.zio_encrypt.ZIO_CRYPT_AES_256_CCM,
451                b"keylocation": filename.encode(),
452                b"keyformat": lzc.zfs_keyformat.ZFS_KEYFORMAT_RAW,
453            }
454            key = os.urandom(lzc.WRAPPING_KEY_LEN)
455            lzc.lzc_create(name, 'zfs', props=props, key=key)
456        self.assertEqual(fs.getProperty("encryption"), b"aes-256-ccm")
457        self.assertEqual(fs.getProperty("encryptionroot"), name)
458        self.assertEqual(fs.getProperty("keylocation"), filename.encode())
459        self.assertEqual(fs.getProperty("keyformat"), b"raw")
460
461    def test_snapshot(self):
462        snapname = ZFSTest.pool.makeName(b"@snap")
463        snaps = [snapname]
464
465        lzc.lzc_snapshot(snaps)
466        self.assertExists(snapname)
467
468    def test_snapshot_empty_list(self):
469        lzc.lzc_snapshot([])
470
471    def test_snapshot_user_props(self):
472        snapname = ZFSTest.pool.makeName(b"@snap")
473        snaps = [snapname]
474        props = {b"user:foo": b"bar"}
475
476        lzc.lzc_snapshot(snaps, props)
477        self.assertExists(snapname)
478
479    def test_snapshot_invalid_props(self):
480        snapname = ZFSTest.pool.makeName(b"@snap")
481        snaps = [snapname]
482        props = {b"foo": b"bar"}
483
484        with self.assertRaises(lzc_exc.SnapshotFailure) as ctx:
485            lzc.lzc_snapshot(snaps, props)
486
487        self.assertEqual(len(ctx.exception.errors), len(snaps))
488        for e in ctx.exception.errors:
489            self.assertIsInstance(e, lzc_exc.PropertyInvalid)
490        self.assertNotExists(snapname)
491
492    def test_snapshot_ro_pool(self):
493        snapname1 = ZFSTest.readonly_pool.makeName(b"@snap")
494        snapname2 = ZFSTest.readonly_pool.makeName(b"fs1@snap")
495        snaps = [snapname1, snapname2]
496
497        with self.assertRaises(lzc_exc.SnapshotFailure) as ctx:
498            lzc.lzc_snapshot(snaps)
499
500        # NB: one common error is reported.
501        self.assertEqual(len(ctx.exception.errors), 1)
502        for e in ctx.exception.errors:
503            self.assertIsInstance(e, lzc_exc.ReadOnlyPool)
504        self.assertNotExists(snapname1)
505        self.assertNotExists(snapname2)
506
507    def test_snapshot_nonexistent_pool(self):
508        snapname = b"no-such-pool@snap"
509        snaps = [snapname]
510
511        with self.assertRaises(lzc_exc.SnapshotFailure) as ctx:
512            lzc.lzc_snapshot(snaps)
513
514        self.assertEqual(len(ctx.exception.errors), 1)
515        for e in ctx.exception.errors:
516            self.assertIsInstance(e, lzc_exc.FilesystemNotFound)
517
518    def test_snapshot_nonexistent_fs(self):
519        snapname = ZFSTest.pool.makeName(b"nonexistent@snap")
520        snaps = [snapname]
521
522        with self.assertRaises(lzc_exc.SnapshotFailure) as ctx:
523            lzc.lzc_snapshot(snaps)
524
525        self.assertEqual(len(ctx.exception.errors), 1)
526        for e in ctx.exception.errors:
527            self.assertIsInstance(e, lzc_exc.FilesystemNotFound)
528
529    def test_snapshot_nonexistent_and_existent_fs(self):
530        snapname1 = ZFSTest.pool.makeName(b"@snap")
531        snapname2 = ZFSTest.pool.makeName(b"nonexistent@snap")
532        snaps = [snapname1, snapname2]
533
534        with self.assertRaises(lzc_exc.SnapshotFailure) as ctx:
535            lzc.lzc_snapshot(snaps)
536
537        self.assertEqual(len(ctx.exception.errors), 1)
538        for e in ctx.exception.errors:
539            self.assertIsInstance(e, lzc_exc.FilesystemNotFound)
540        self.assertNotExists(snapname1)
541        self.assertNotExists(snapname2)
542
543    def test_multiple_snapshots_nonexistent_fs(self):
544        snapname1 = ZFSTest.pool.makeName(b"nonexistent@snap1")
545        snapname2 = ZFSTest.pool.makeName(b"nonexistent@snap2")
546        snaps = [snapname1, snapname2]
547
548        with self.assertRaises(lzc_exc.SnapshotFailure) as ctx:
549            lzc.lzc_snapshot(snaps)
550
551        # XXX two errors should be reported but alas
552        self.assertEqual(len(ctx.exception.errors), 1)
553        for e in ctx.exception.errors:
554            self.assertIsInstance(e, lzc_exc.DuplicateSnapshots)
555        self.assertNotExists(snapname1)
556        self.assertNotExists(snapname2)
557
558    def test_multiple_snapshots_multiple_nonexistent_fs(self):
559        snapname1 = ZFSTest.pool.makeName(b"nonexistent1@snap")
560        snapname2 = ZFSTest.pool.makeName(b"nonexistent2@snap")
561        snaps = [snapname1, snapname2]
562
563        with self.assertRaises(lzc_exc.SnapshotFailure) as ctx:
564            lzc.lzc_snapshot(snaps)
565
566        self.assertEqual(len(ctx.exception.errors), 2)
567        for e in ctx.exception.errors:
568            self.assertIsInstance(e, lzc_exc.FilesystemNotFound)
569        self.assertNotExists(snapname1)
570        self.assertNotExists(snapname2)
571
572    def test_snapshot_already_exists(self):
573        snapname = ZFSTest.pool.makeName(b"@snap")
574        snaps = [snapname]
575
576        lzc.lzc_snapshot(snaps)
577
578        with self.assertRaises(lzc_exc.SnapshotFailure) as ctx:
579            lzc.lzc_snapshot(snaps)
580
581        self.assertEqual(len(ctx.exception.errors), 1)
582        for e in ctx.exception.errors:
583            self.assertIsInstance(e, lzc_exc.SnapshotExists)
584
585    def test_multiple_snapshots_for_same_fs(self):
586        snapname1 = ZFSTest.pool.makeName(b"@snap1")
587        snapname2 = ZFSTest.pool.makeName(b"@snap2")
588        snaps = [snapname1, snapname2]
589
590        with self.assertRaises(lzc_exc.SnapshotFailure) as ctx:
591            lzc.lzc_snapshot(snaps)
592
593        self.assertEqual(len(ctx.exception.errors), 1)
594        for e in ctx.exception.errors:
595            self.assertIsInstance(e, lzc_exc.DuplicateSnapshots)
596        self.assertNotExists(snapname1)
597        self.assertNotExists(snapname2)
598
599    def test_multiple_snapshots(self):
600        snapname1 = ZFSTest.pool.makeName(b"@snap")
601        snapname2 = ZFSTest.pool.makeName(b"fs1@snap")
602        snaps = [snapname1, snapname2]
603
604        lzc.lzc_snapshot(snaps)
605        self.assertExists(snapname1)
606        self.assertExists(snapname2)
607
608    def test_multiple_existing_snapshots(self):
609        snapname1 = ZFSTest.pool.makeName(b"@snap")
610        snapname2 = ZFSTest.pool.makeName(b"fs1@snap")
611        snaps = [snapname1, snapname2]
612
613        lzc.lzc_snapshot(snaps)
614
615        with self.assertRaises(lzc_exc.SnapshotFailure) as ctx:
616            lzc.lzc_snapshot(snaps)
617
618        self.assertEqual(len(ctx.exception.errors), 2)
619        for e in ctx.exception.errors:
620            self.assertIsInstance(e, lzc_exc.SnapshotExists)
621
622    def test_multiple_new_and_existing_snapshots(self):
623        snapname1 = ZFSTest.pool.makeName(b"@snap")
624        snapname2 = ZFSTest.pool.makeName(b"fs1@snap")
625        snapname3 = ZFSTest.pool.makeName(b"fs2@snap")
626        snaps = [snapname1, snapname2]
627        more_snaps = snaps + [snapname3]
628
629        lzc.lzc_snapshot(snaps)
630
631        with self.assertRaises(lzc_exc.SnapshotFailure) as ctx:
632            lzc.lzc_snapshot(more_snaps)
633
634        self.assertEqual(len(ctx.exception.errors), 2)
635        for e in ctx.exception.errors:
636            self.assertIsInstance(e, lzc_exc.SnapshotExists)
637        self.assertNotExists(snapname3)
638
639    def test_snapshot_multiple_errors(self):
640        snapname1 = ZFSTest.pool.makeName(b"@snap")
641        snapname2 = ZFSTest.pool.makeName(b"nonexistent@snap")
642        snapname3 = ZFSTest.pool.makeName(b"fs1@snap")
643        snaps = [snapname1]
644        more_snaps = [snapname1, snapname2, snapname3]
645
646        # create 'snapname1' snapshot
647        lzc.lzc_snapshot(snaps)
648
649        # attempt to create 3 snapshots:
650        # 1. duplicate snapshot name
651        # 2. refers to filesystem that doesn't exist
652        # 3. could have succeeded if not for 1 and 2
653        with self.assertRaises(lzc_exc.SnapshotFailure) as ctx:
654            lzc.lzc_snapshot(more_snaps)
655
656        # It seems that FilesystemNotFound overrides the other error,
657        # but it doesn't have to.
658        self.assertGreater(len(ctx.exception.errors), 0)
659        for e in ctx.exception.errors:
660            self.assertIsInstance(
661                e, (lzc_exc.SnapshotExists, lzc_exc.FilesystemNotFound))
662        self.assertNotExists(snapname2)
663        self.assertNotExists(snapname3)
664
665    def test_snapshot_different_pools(self):
666        snapname1 = ZFSTest.pool.makeName(b"@snap")
667        snapname2 = ZFSTest.misc_pool.makeName(b"@snap")
668        snaps = [snapname1, snapname2]
669
670        with self.assertRaises(lzc_exc.SnapshotFailure) as ctx:
671            lzc.lzc_snapshot(snaps)
672
673        # NB: one common error is reported.
674        self.assertEqual(len(ctx.exception.errors), 1)
675        for e in ctx.exception.errors:
676            self.assertIsInstance(e, lzc_exc.PoolsDiffer)
677        self.assertNotExists(snapname1)
678        self.assertNotExists(snapname2)
679
680    def test_snapshot_different_pools_ro_pool(self):
681        snapname1 = ZFSTest.pool.makeName(b"@snap")
682        snapname2 = ZFSTest.readonly_pool.makeName(b"@snap")
683        snaps = [snapname1, snapname2]
684
685        with self.assertRaises(lzc_exc.SnapshotFailure) as ctx:
686            lzc.lzc_snapshot(snaps)
687
688        # NB: one common error is reported.
689        self.assertEqual(len(ctx.exception.errors), 1)
690        for e in ctx.exception.errors:
691            # NB: depending on whether the first attempted snapshot is
692            # for the read-only pool a different error is reported.
693            self.assertIsInstance(
694                e, (lzc_exc.PoolsDiffer, lzc_exc.ReadOnlyPool))
695        self.assertNotExists(snapname1)
696        self.assertNotExists(snapname2)
697
698    def test_snapshot_invalid_name(self):
699        snapname1 = ZFSTest.pool.makeName(b"@bad&name")
700        snapname2 = ZFSTest.pool.makeName(b"fs1@bad*name")
701        snapname3 = ZFSTest.pool.makeName(b"fs2@snap")
702        snaps = [snapname1, snapname2, snapname3]
703
704        with self.assertRaises(lzc_exc.SnapshotFailure) as ctx:
705            lzc.lzc_snapshot(snaps)
706
707        # NB: one common error is reported.
708        self.assertEqual(len(ctx.exception.errors), 1)
709        for e in ctx.exception.errors:
710            self.assertIsInstance(e, lzc_exc.NameInvalid)
711            self.assertIsNone(e.name)
712
713    def test_snapshot_too_long_complete_name(self):
714        snapname1 = ZFSTest.pool.makeTooLongName(b"fs1@")
715        snapname2 = ZFSTest.pool.makeTooLongName(b"fs2@")
716        snapname3 = ZFSTest.pool.makeName(b"@snap")
717        snaps = [snapname1, snapname2, snapname3]
718
719        with self.assertRaises(lzc_exc.SnapshotFailure) as ctx:
720            lzc.lzc_snapshot(snaps)
721
722        self.assertEqual(len(ctx.exception.errors), 2)
723        for e in ctx.exception.errors:
724            self.assertIsInstance(e, lzc_exc.NameTooLong)
725            self.assertIsNotNone(e.name)
726
727    def test_snapshot_too_long_snap_name(self):
728        snapname1 = ZFSTest.pool.makeTooLongComponent(b"fs1@")
729        snapname2 = ZFSTest.pool.makeTooLongComponent(b"fs2@")
730        snapname3 = ZFSTest.pool.makeName(b"@snap")
731        snaps = [snapname1, snapname2, snapname3]
732
733        with self.assertRaises(lzc_exc.SnapshotFailure) as ctx:
734            lzc.lzc_snapshot(snaps)
735
736        # NB: one common error is reported.
737        self.assertEqual(len(ctx.exception.errors), 1)
738        for e in ctx.exception.errors:
739            self.assertIsInstance(e, lzc_exc.NameTooLong)
740            self.assertIsNone(e.name)
741
742    def test_destroy_nonexistent_snapshot(self):
743        lzc.lzc_destroy_snaps([ZFSTest.pool.makeName(b"@nonexistent")], False)
744        lzc.lzc_destroy_snaps([ZFSTest.pool.makeName(b"@nonexistent")], True)
745
746    def test_destroy_snapshot_of_nonexistent_pool(self):
747        with self.assertRaises(lzc_exc.SnapshotDestructionFailure) as ctx:
748            lzc.lzc_destroy_snaps([b"no-such-pool@snap"], False)
749
750        for e in ctx.exception.errors:
751            self.assertIsInstance(e, lzc_exc.PoolNotFound)
752
753        with self.assertRaises(lzc_exc.SnapshotDestructionFailure) as ctx:
754            lzc.lzc_destroy_snaps([b"no-such-pool@snap"], True)
755
756        for e in ctx.exception.errors:
757            self.assertIsInstance(e, lzc_exc.PoolNotFound)
758
759    # NB: note the difference from the nonexistent pool test.
760    def test_destroy_snapshot_of_nonexistent_fs(self):
761        lzc.lzc_destroy_snaps(
762            [ZFSTest.pool.makeName(b"nonexistent@snap")], False)
763        lzc.lzc_destroy_snaps(
764            [ZFSTest.pool.makeName(b"nonexistent@snap")], True)
765
766    # Apparently the name is not checked for validity.
767    @unittest.expectedFailure
768    def test_destroy_invalid_snap_name(self):
769        with self.assertRaises(lzc_exc.SnapshotDestructionFailure):
770            lzc.lzc_destroy_snaps(
771                [ZFSTest.pool.makeName(b"@non$&*existent")], False)
772        with self.assertRaises(lzc_exc.SnapshotDestructionFailure):
773            lzc.lzc_destroy_snaps(
774                [ZFSTest.pool.makeName(b"@non$&*existent")], True)
775
776    # Apparently the full name is not checked for length.
777    @unittest.expectedFailure
778    def test_destroy_too_long_full_snap_name(self):
779        snapname1 = ZFSTest.pool.makeTooLongName(b"fs1@")
780        snaps = [snapname1]
781
782        with self.assertRaises(lzc_exc.SnapshotDestructionFailure):
783            lzc.lzc_destroy_snaps(snaps, False)
784        with self.assertRaises(lzc_exc.SnapshotDestructionFailure):
785            lzc.lzc_destroy_snaps(snaps, True)
786
787    def test_destroy_too_long_short_snap_name(self):
788        snapname1 = ZFSTest.pool.makeTooLongComponent(b"fs1@")
789        snapname2 = ZFSTest.pool.makeTooLongComponent(b"fs2@")
790        snapname3 = ZFSTest.pool.makeName(b"@snap")
791        snaps = [snapname1, snapname2, snapname3]
792
793        with self.assertRaises(lzc_exc.SnapshotDestructionFailure) as ctx:
794            lzc.lzc_destroy_snaps(snaps, False)
795
796        for e in ctx.exception.errors:
797            self.assertIsInstance(e, lzc_exc.NameTooLong)
798
799    @unittest.skipUnless(*snap_always_unmounted_before_destruction())
800    def test_destroy_mounted_snap(self):
801        snap = ZFSTest.pool.getRoot().getSnap()
802
803        lzc.lzc_snapshot([snap])
804        with zfs_mount(snap):
805            # the snapshot should be force-unmounted
806            lzc.lzc_destroy_snaps([snap], defer=False)
807            self.assertNotExists(snap)
808
809    def test_clone(self):
810        # NB: note the special name for the snapshot.
811        # Since currently we can not destroy filesystems,
812        # it would be impossible to destroy the snapshot,
813        # so no point in attempting to clean it up.
814        snapname = ZFSTest.pool.makeName(b"fs2@origin1")
815        name = ZFSTest.pool.makeName(b"fs1/fs/clone1")
816
817        lzc.lzc_snapshot([snapname])
818
819        lzc.lzc_clone(name, snapname)
820        self.assertExists(name)
821
822    def test_clone_nonexistent_snapshot(self):
823        snapname = ZFSTest.pool.makeName(b"fs2@nonexistent")
824        name = ZFSTest.pool.makeName(b"fs1/fs/clone2")
825
826        # XXX The error should be SnapshotNotFound
827        # but limitations of C interface do not allow
828        # to differentiate between the errors.
829        with self.assertRaises(lzc_exc.DatasetNotFound):
830            lzc.lzc_clone(name, snapname)
831        self.assertNotExists(name)
832
833    def test_clone_nonexistent_parent_fs(self):
834        snapname = ZFSTest.pool.makeName(b"fs2@origin3")
835        name = ZFSTest.pool.makeName(b"fs1/nonexistent/clone3")
836
837        lzc.lzc_snapshot([snapname])
838
839        with self.assertRaises(lzc_exc.DatasetNotFound):
840            lzc.lzc_clone(name, snapname)
841        self.assertNotExists(name)
842
843    def test_clone_to_nonexistent_pool(self):
844        snapname = ZFSTest.pool.makeName(b"fs2@snap")
845        name = b"no-such-pool/fs"
846
847        lzc.lzc_snapshot([snapname])
848
849        with self.assertRaises(lzc_exc.DatasetNotFound):
850            lzc.lzc_clone(name, snapname)
851        self.assertNotExists(name)
852
853    def test_clone_invalid_snap_name(self):
854        # Use a valid filesystem name of filesystem that
855        # exists as a snapshot name
856        snapname = ZFSTest.pool.makeName(b"fs1/fs")
857        name = ZFSTest.pool.makeName(b"fs2/clone")
858
859        with self.assertRaises(lzc_exc.SnapshotNameInvalid):
860            lzc.lzc_clone(name, snapname)
861        self.assertNotExists(name)
862
863    def test_clone_invalid_snap_name_2(self):
864        # Use a valid filesystem name of filesystem that
865        # doesn't exist as a snapshot name
866        snapname = ZFSTest.pool.makeName(b"fs1/nonexistent")
867        name = ZFSTest.pool.makeName(b"fs2/clone")
868
869        with self.assertRaises(lzc_exc.SnapshotNameInvalid):
870            lzc.lzc_clone(name, snapname)
871        self.assertNotExists(name)
872
873    def test_clone_invalid_name(self):
874        snapname = ZFSTest.pool.makeName(b"fs2@snap")
875        name = ZFSTest.pool.makeName(b"fs1/bad#name")
876
877        lzc.lzc_snapshot([snapname])
878
879        with self.assertRaises(lzc_exc.FilesystemNameInvalid):
880            lzc.lzc_clone(name, snapname)
881        self.assertNotExists(name)
882
883    def test_clone_invalid_pool_name(self):
884        snapname = ZFSTest.pool.makeName(b"fs2@snap")
885        name = b"bad!pool/fs1"
886
887        lzc.lzc_snapshot([snapname])
888
889        with self.assertRaises(lzc_exc.FilesystemNameInvalid):
890            lzc.lzc_clone(name, snapname)
891        self.assertNotExists(name)
892
893    def test_clone_across_pools(self):
894        snapname = ZFSTest.pool.makeName(b"fs2@snap")
895        name = ZFSTest.misc_pool.makeName(b"clone1")
896
897        lzc.lzc_snapshot([snapname])
898
899        with self.assertRaises(lzc_exc.PoolsDiffer):
900            lzc.lzc_clone(name, snapname)
901        self.assertNotExists(name)
902
903    def test_clone_across_pools_to_ro_pool(self):
904        snapname = ZFSTest.pool.makeName(b"fs2@snap")
905        name = ZFSTest.readonly_pool.makeName(b"fs1/clone1")
906
907        lzc.lzc_snapshot([snapname])
908
909        # it's legal to report either of the conditions
910        with self.assertRaises((lzc_exc.ReadOnlyPool, lzc_exc.PoolsDiffer)):
911            lzc.lzc_clone(name, snapname)
912        self.assertNotExists(name)
913
914    def test_destroy_cloned_fs(self):
915        snapname1 = ZFSTest.pool.makeName(b"fs2@origin4")
916        snapname2 = ZFSTest.pool.makeName(b"fs1@snap")
917        clonename = ZFSTest.pool.makeName(b"fs1/fs/clone4")
918        snaps = [snapname1, snapname2]
919
920        lzc.lzc_snapshot(snaps)
921        lzc.lzc_clone(clonename, snapname1)
922
923        with self.assertRaises(lzc_exc.SnapshotDestructionFailure) as ctx:
924            lzc.lzc_destroy_snaps(snaps, False)
925
926        self.assertEqual(len(ctx.exception.errors), 1)
927        for e in ctx.exception.errors:
928            self.assertIsInstance(e, lzc_exc.SnapshotIsCloned)
929        for snap in snaps:
930            self.assertExists(snap)
931
932    def test_deferred_destroy_cloned_fs(self):
933        snapname1 = ZFSTest.pool.makeName(b"fs2@origin5")
934        snapname2 = ZFSTest.pool.makeName(b"fs1@snap")
935        clonename = ZFSTest.pool.makeName(b"fs1/fs/clone5")
936        snaps = [snapname1, snapname2]
937
938        lzc.lzc_snapshot(snaps)
939        lzc.lzc_clone(clonename, snapname1)
940
941        lzc.lzc_destroy_snaps(snaps, defer=True)
942
943        self.assertExists(snapname1)
944        self.assertNotExists(snapname2)
945
946    def test_rollback(self):
947        name = ZFSTest.pool.makeName(b"fs1")
948        snapname = name + b"@snap"
949
950        lzc.lzc_snapshot([snapname])
951        ret = lzc.lzc_rollback(name)
952        self.assertEqual(ret, snapname)
953
954    def test_rollback_2(self):
955        name = ZFSTest.pool.makeName(b"fs1")
956        snapname1 = name + b"@snap1"
957        snapname2 = name + b"@snap2"
958
959        lzc.lzc_snapshot([snapname1])
960        lzc.lzc_snapshot([snapname2])
961        ret = lzc.lzc_rollback(name)
962        self.assertEqual(ret, snapname2)
963
964    def test_rollback_no_snaps(self):
965        name = ZFSTest.pool.makeName(b"fs1")
966
967        with self.assertRaises(lzc_exc.SnapshotNotFound):
968            lzc.lzc_rollback(name)
969
970    def test_rollback_non_existent_fs(self):
971        name = ZFSTest.pool.makeName(b"nonexistent")
972
973        with self.assertRaises(lzc_exc.FilesystemNotFound):
974            lzc.lzc_rollback(name)
975
976    def test_rollback_invalid_fs_name(self):
977        name = ZFSTest.pool.makeName(b"bad~name")
978
979        with self.assertRaises(lzc_exc.NameInvalid):
980            lzc.lzc_rollback(name)
981
982    def test_rollback_snap_name(self):
983        name = ZFSTest.pool.makeName(b"fs1@snap")
984
985        with self.assertRaises(lzc_exc.NameInvalid):
986            lzc.lzc_rollback(name)
987
988    def test_rollback_snap_name_2(self):
989        name = ZFSTest.pool.makeName(b"fs1@snap")
990
991        lzc.lzc_snapshot([name])
992        with self.assertRaises(lzc_exc.NameInvalid):
993            lzc.lzc_rollback(name)
994
995    def test_rollback_too_long_fs_name(self):
996        name = ZFSTest.pool.makeTooLongName()
997
998        with self.assertRaises(lzc_exc.NameTooLong):
999            lzc.lzc_rollback(name)
1000
1001    def test_rollback_to_snap_name(self):
1002        name = ZFSTest.pool.makeName(b"fs1")
1003        snap = name + b"@snap"
1004
1005        lzc.lzc_snapshot([snap])
1006        lzc.lzc_rollback_to(name, snap)
1007
1008    def test_rollback_to_not_latest(self):
1009        fsname = ZFSTest.pool.makeName(b'fs1')
1010        snap1 = fsname + b"@snap1"
1011        snap2 = fsname + b"@snap2"
1012
1013        lzc.lzc_snapshot([snap1])
1014        lzc.lzc_snapshot([snap2])
1015        with self.assertRaises(lzc_exc.SnapshotNotLatest):
1016            lzc.lzc_rollback_to(fsname, fsname + b"@snap1")
1017
1018    @skipUnlessBookmarksSupported
1019    def test_bookmarks(self):
1020        snaps = [ZFSTest.pool.makeName(
1021            b'fs1@snap1'), ZFSTest.pool.makeName(b'fs2@snap1')]
1022        bmarks = [ZFSTest.pool.makeName(
1023            b'fs1#bmark1'), ZFSTest.pool.makeName(b'fs2#bmark1')]
1024        bmark_dict = {x: y for x, y in zip(bmarks, snaps)}
1025
1026        lzc.lzc_snapshot(snaps)
1027        lzc.lzc_bookmark(bmark_dict)
1028
1029    @skipUnlessBookmarksSupported
1030    def test_bookmarks_2(self):
1031        snaps = [ZFSTest.pool.makeName(
1032            b'fs1@snap1'), ZFSTest.pool.makeName(b'fs2@snap1')]
1033        bmarks = [ZFSTest.pool.makeName(
1034            b'fs1#bmark1'), ZFSTest.pool.makeName(b'fs2#bmark1')]
1035        bmark_dict = {x: y for x, y in zip(bmarks, snaps)}
1036        lzc.lzc_snapshot(snaps)
1037        lzc.lzc_bookmark(bmark_dict)
1038        lzc.lzc_destroy_snaps(snaps, defer=False)
1039
1040    @skipUnlessBookmarksSupported
1041    def test_bookmark_copying(self):
1042        snaps = [ZFSTest.pool.makeName(s) for s in [
1043            b'fs1@snap1', b'fs1@snap2', b'fs2@snap1']]
1044        bmarks = [ZFSTest.pool.makeName(x) for x in [
1045            b'fs1#bmark1', b'fs1#bmark2', b'fs2#bmark1']]
1046        bmarks_copies = [ZFSTest.pool.makeName(x) for x in [
1047            b'fs1#bmark1_copy', b'fs1#bmark2_copy', b'fs2#bmark1_copy']]
1048        bmark_dict = {x: y for x, y in zip(bmarks, snaps)}
1049        bmark_copies_dict = {x: y for x, y in zip(bmarks_copies, bmarks)}
1050
1051        for snap in snaps:
1052            lzc.lzc_snapshot([snap])
1053        lzc.lzc_bookmark(bmark_dict)
1054
1055        lzc.lzc_bookmark(bmark_copies_dict)
1056        lzc.lzc_destroy_bookmarks(bmarks_copies)
1057
1058        lzc.lzc_destroy_bookmarks(bmarks)
1059        lzc.lzc_destroy_snaps(snaps, defer=False)
1060
1061    @skipUnlessBookmarksSupported
1062    def test_bookmarks_empty(self):
1063        lzc.lzc_bookmark({})
1064
1065    @skipUnlessBookmarksSupported
1066    def test_bookmarks_foreign_source(self):
1067        snaps = [ZFSTest.pool.makeName(b'fs1@snap1')]
1068        bmarks = [ZFSTest.pool.makeName(b'fs2#bmark1')]
1069        bmark_dict = {x: y for x, y in zip(bmarks, snaps)}
1070
1071        lzc.lzc_snapshot(snaps)
1072        with self.assertRaises(lzc_exc.BookmarkFailure) as ctx:
1073            lzc.lzc_bookmark(bmark_dict)
1074
1075        for e in ctx.exception.errors:
1076            self.assertIsInstance(e, lzc_exc.BookmarkMismatch)
1077
1078    @skipUnlessBookmarksSupported
1079    def test_bookmarks_invalid_name(self):
1080        snaps = [ZFSTest.pool.makeName(b'fs1@snap1')]
1081        bmarks = [ZFSTest.pool.makeName(b'fs1#bmark!')]
1082        bmark_dict = {x: y for x, y in zip(bmarks, snaps)}
1083
1084        lzc.lzc_snapshot(snaps)
1085        with self.assertRaises(lzc_exc.BookmarkFailure) as ctx:
1086            lzc.lzc_bookmark(bmark_dict)
1087
1088        for e in ctx.exception.errors:
1089            self.assertIsInstance(e, lzc_exc.NameInvalid)
1090
1091    @skipUnlessBookmarksSupported
1092    def test_bookmarks_invalid_name_2(self):
1093        snaps = [ZFSTest.pool.makeName(b'fs1@snap1')]
1094        bmarks = [ZFSTest.pool.makeName(b'fs1@bmark')]
1095        bmark_dict = {x: y for x, y in zip(bmarks, snaps)}
1096
1097        lzc.lzc_snapshot(snaps)
1098        with self.assertRaises(lzc_exc.BookmarkFailure) as ctx:
1099            lzc.lzc_bookmark(bmark_dict)
1100
1101        for e in ctx.exception.errors:
1102            self.assertIsInstance(e, lzc_exc.NameInvalid)
1103
1104    @skipUnlessBookmarksSupported
1105    def test_bookmarks_too_long_name(self):
1106        snaps = [ZFSTest.pool.makeName(b'fs1@snap1')]
1107        bmarks = [ZFSTest.pool.makeTooLongName(b'fs1#')]
1108        bmark_dict = {x: y for x, y in zip(bmarks, snaps)}
1109
1110        lzc.lzc_snapshot(snaps)
1111        with self.assertRaises(lzc_exc.BookmarkFailure) as ctx:
1112            lzc.lzc_bookmark(bmark_dict)
1113
1114        for e in ctx.exception.errors:
1115            self.assertIsInstance(e, lzc_exc.NameTooLong)
1116
1117    @skipUnlessBookmarksSupported
1118    def test_bookmarks_too_long_name_2(self):
1119        snaps = [ZFSTest.pool.makeName(b'fs1@snap1')]
1120        bmarks = [ZFSTest.pool.makeTooLongComponent(b'fs1#')]
1121        bmark_dict = {x: y for x, y in zip(bmarks, snaps)}
1122
1123        lzc.lzc_snapshot(snaps)
1124        with self.assertRaises(lzc_exc.BookmarkFailure) as ctx:
1125            lzc.lzc_bookmark(bmark_dict)
1126
1127        for e in ctx.exception.errors:
1128            self.assertIsInstance(e, lzc_exc.NameTooLong)
1129
1130    @skipUnlessBookmarksSupported
1131    def test_bookmarks_foreign_sources(self):
1132        snaps = [ZFSTest.pool.makeName(
1133            b'fs1@snap1'), ZFSTest.pool.makeName(b'fs2@snap1')]
1134        bmarks = [ZFSTest.pool.makeName(
1135            b'fs2#bmark1'), ZFSTest.pool.makeName(b'fs1#bmark1')]
1136        bmark_dict = {x: y for x, y in zip(bmarks, snaps)}
1137
1138        lzc.lzc_snapshot(snaps)
1139        with self.assertRaises(lzc_exc.BookmarkFailure) as ctx:
1140            lzc.lzc_bookmark(bmark_dict)
1141
1142        for e in ctx.exception.errors:
1143            self.assertIsInstance(e, lzc_exc.BookmarkMismatch)
1144
1145    @skipUnlessBookmarksSupported
1146    def test_bookmarks_partially_foreign_sources(self):
1147        snaps = [ZFSTest.pool.makeName(
1148            b'fs1@snap1'), ZFSTest.pool.makeName(b'fs2@snap1')]
1149        bmarks = [ZFSTest.pool.makeName(
1150            b'fs2#bmark'), ZFSTest.pool.makeName(b'fs2#bmark1')]
1151        bmark_dict = {x: y for x, y in zip(bmarks, snaps)}
1152
1153        lzc.lzc_snapshot(snaps)
1154        with self.assertRaises(lzc_exc.BookmarkFailure) as ctx:
1155            lzc.lzc_bookmark(bmark_dict)
1156
1157        for e in ctx.exception.errors:
1158            self.assertIsInstance(e, lzc_exc.BookmarkMismatch)
1159
1160    @skipUnlessBookmarksSupported
1161    def test_bookmarks_cross_pool(self):
1162        snaps = [ZFSTest.pool.makeName(
1163            b'fs1@snap1'), ZFSTest.misc_pool.makeName(b'@snap1')]
1164        bmarks = [ZFSTest.pool.makeName(
1165            b'fs1#bmark1'), ZFSTest.misc_pool.makeName(b'#bmark1')]
1166        bmark_dict = {x: y for x, y in zip(bmarks, snaps)}
1167
1168        lzc.lzc_snapshot(snaps[0:1])
1169        lzc.lzc_snapshot(snaps[1:2])
1170        with self.assertRaises(lzc_exc.BookmarkFailure) as ctx:
1171            lzc.lzc_bookmark(bmark_dict)
1172
1173        for e in ctx.exception.errors:
1174            self.assertIsInstance(e, lzc_exc.PoolsDiffer)
1175
1176    @skipUnlessBookmarksSupported
1177    def test_bookmarks_missing_snap(self):
1178        fss = [ZFSTest.pool.makeName(b'fs1'), ZFSTest.pool.makeName(b'fs2')]
1179        snaps = [ZFSTest.pool.makeName(
1180            b'fs1@snap1'), ZFSTest.pool.makeName(b'fs2@snap1')]
1181        bmarks = [ZFSTest.pool.makeName(
1182            b'fs1#bmark1'), ZFSTest.pool.makeName(b'fs2#bmark1')]
1183        bmark_dict = {x: y for x, y in zip(bmarks, snaps)}
1184
1185        lzc.lzc_snapshot(snaps[0:1])  # only create fs1@snap1
1186
1187        with self.assertRaises(lzc_exc.BookmarkFailure) as ctx:
1188            lzc.lzc_bookmark(bmark_dict)
1189
1190        for e in ctx.exception.errors:
1191            self.assertIsInstance(e, lzc_exc.SnapshotNotFound)
1192
1193        # no new bookmarks are created if one or more sources do not exist
1194        for fs in fss:
1195            fsbmarks = lzc.lzc_get_bookmarks(fs)
1196            self.assertEqual(len(fsbmarks), 0)
1197
1198    @skipUnlessBookmarksSupported
1199    def test_bookmarks_missing_snaps(self):
1200        fss = [ZFSTest.pool.makeName(b'fs1'), ZFSTest.pool.makeName(b'fs2')]
1201        snaps = [ZFSTest.pool.makeName(
1202            b'fs1@snap1'), ZFSTest.pool.makeName(b'fs2@snap1')]
1203        bmarks = [ZFSTest.pool.makeName(
1204            b'fs1#bmark1'), ZFSTest.pool.makeName(b'fs2#bmark1')]
1205        bmark_dict = {x: y for x, y in zip(bmarks, snaps)}
1206
1207        # do not create any snapshots
1208
1209        with self.assertRaises(lzc_exc.BookmarkFailure) as ctx:
1210            lzc.lzc_bookmark(bmark_dict)
1211
1212        for e in ctx.exception.errors:
1213            self.assertIsInstance(e, lzc_exc.SnapshotNotFound)
1214
1215        # no new bookmarks are created if one or more sources do not exist
1216        for fs in fss:
1217            fsbmarks = lzc.lzc_get_bookmarks(fs)
1218            self.assertEqual(len(fsbmarks), 0)
1219
1220    @skipUnlessBookmarksSupported
1221    def test_bookmarks_for_the_same_snap(self):
1222        snap = ZFSTest.pool.makeName(b'fs1@snap1')
1223        bmark1 = ZFSTest.pool.makeName(b'fs1#bmark1')
1224        bmark2 = ZFSTest.pool.makeName(b'fs1#bmark2')
1225        bmark_dict = {bmark1: snap, bmark2: snap}
1226
1227        lzc.lzc_snapshot([snap])
1228        lzc.lzc_bookmark(bmark_dict)
1229
1230    @skipUnlessBookmarksSupported
1231    def test_bookmarks_for_the_same_snap_2(self):
1232        snap = ZFSTest.pool.makeName(b'fs1@snap1')
1233        bmark1 = ZFSTest.pool.makeName(b'fs1#bmark1')
1234        bmark2 = ZFSTest.pool.makeName(b'fs1#bmark2')
1235        bmark_dict1 = {bmark1: snap}
1236        bmark_dict2 = {bmark2: snap}
1237
1238        lzc.lzc_snapshot([snap])
1239        lzc.lzc_bookmark(bmark_dict1)
1240        lzc.lzc_bookmark(bmark_dict2)
1241
1242    @skipUnlessBookmarksSupported
1243    def test_bookmarks_duplicate_name(self):
1244        snap1 = ZFSTest.pool.makeName(b'fs1@snap1')
1245        snap2 = ZFSTest.pool.makeName(b'fs1@snap2')
1246        bmark = ZFSTest.pool.makeName(b'fs1#bmark')
1247        bmark_dict1 = {bmark: snap1}
1248        bmark_dict2 = {bmark: snap2}
1249
1250        lzc.lzc_snapshot([snap1])
1251        lzc.lzc_snapshot([snap2])
1252        lzc.lzc_bookmark(bmark_dict1)
1253        with self.assertRaises(lzc_exc.BookmarkFailure) as ctx:
1254            lzc.lzc_bookmark(bmark_dict2)
1255
1256        for e in ctx.exception.errors:
1257            self.assertIsInstance(e, lzc_exc.BookmarkExists)
1258
1259    @skipUnlessBookmarksSupported
1260    def test_get_bookmarks(self):
1261        snap1 = ZFSTest.pool.makeName(b'fs1@snap1')
1262        snap2 = ZFSTest.pool.makeName(b'fs1@snap2')
1263        bmark = ZFSTest.pool.makeName(b'fs1#bmark')
1264        bmark1 = ZFSTest.pool.makeName(b'fs1#bmark1')
1265        bmark2 = ZFSTest.pool.makeName(b'fs1#bmark2')
1266        bmark_dict1 = {bmark1: snap1, bmark2: snap2}
1267        bmark_dict2 = {bmark: snap2}
1268
1269        lzc.lzc_snapshot([snap1])
1270        lzc.lzc_snapshot([snap2])
1271        lzc.lzc_bookmark(bmark_dict1)
1272        lzc.lzc_bookmark(bmark_dict2)
1273        lzc.lzc_destroy_snaps([snap1, snap2], defer=False)
1274
1275        bmarks = lzc.lzc_get_bookmarks(ZFSTest.pool.makeName(b'fs1'))
1276        self.assertEqual(len(bmarks), 3)
1277        for b in b'bmark', b'bmark1', b'bmark2':
1278            self.assertIn(b, bmarks)
1279            self.assertIsInstance(bmarks[b], dict)
1280            self.assertEqual(len(bmarks[b]), 0)
1281
1282        bmarks = lzc.lzc_get_bookmarks(ZFSTest.pool.makeName(b'fs1'),
1283                                       [b'guid', b'createtxg', b'creation'])
1284        self.assertEqual(len(bmarks), 3)
1285        for b in b'bmark', b'bmark1', b'bmark2':
1286            self.assertIn(b, bmarks)
1287            self.assertIsInstance(bmarks[b], dict)
1288            self.assertEqual(len(bmarks[b]), 3)
1289
1290    @skipUnlessBookmarksSupported
1291    def test_get_bookmarks_invalid_property(self):
1292        snap = ZFSTest.pool.makeName(b'fs1@snap')
1293        bmark = ZFSTest.pool.makeName(b'fs1#bmark')
1294        bmark_dict = {bmark: snap}
1295
1296        lzc.lzc_snapshot([snap])
1297        lzc.lzc_bookmark(bmark_dict)
1298
1299        bmarks = lzc.lzc_get_bookmarks(
1300            ZFSTest.pool.makeName(b'fs1'), [b'badprop'])
1301        self.assertEqual(len(bmarks), 1)
1302        for b in (b'bmark', ):
1303            self.assertIn(b, bmarks)
1304            self.assertIsInstance(bmarks[b], dict)
1305            self.assertEqual(len(bmarks[b]), 0)
1306
1307    @skipUnlessBookmarksSupported
1308    def test_get_bookmarks_nonexistent_fs(self):
1309        with self.assertRaises(lzc_exc.FilesystemNotFound):
1310            lzc.lzc_get_bookmarks(ZFSTest.pool.makeName(b'nonexistent'))
1311
1312    @skipUnlessBookmarksSupported
1313    def test_destroy_bookmarks(self):
1314        snap = ZFSTest.pool.makeName(b'fs1@snap')
1315        bmark = ZFSTest.pool.makeName(b'fs1#bmark')
1316        bmark_dict = {bmark: snap}
1317
1318        lzc.lzc_snapshot([snap])
1319        lzc.lzc_bookmark(bmark_dict)
1320
1321        lzc.lzc_destroy_bookmarks(
1322            [bmark, ZFSTest.pool.makeName(b'fs1#nonexistent')])
1323        bmarks = lzc.lzc_get_bookmarks(ZFSTest.pool.makeName(b'fs1'))
1324        self.assertEqual(len(bmarks), 0)
1325
1326    @skipUnlessBookmarksSupported
1327    def test_destroy_bookmarks_invalid_name(self):
1328        snap = ZFSTest.pool.makeName(b'fs1@snap')
1329        bmark = ZFSTest.pool.makeName(b'fs1#bmark')
1330        bmark_dict = {bmark: snap}
1331
1332        lzc.lzc_snapshot([snap])
1333        lzc.lzc_bookmark(bmark_dict)
1334
1335        with self.assertRaises(lzc_exc.BookmarkDestructionFailure) as ctx:
1336            lzc.lzc_destroy_bookmarks(
1337                [bmark, ZFSTest.pool.makeName(b'fs1/nonexistent')])
1338        for e in ctx.exception.errors:
1339            self.assertIsInstance(e, lzc_exc.NameInvalid)
1340
1341        bmarks = lzc.lzc_get_bookmarks(ZFSTest.pool.makeName(b'fs1'))
1342        self.assertEqual(len(bmarks), 1)
1343        self.assertIn(b'bmark', bmarks)
1344
1345    @skipUnlessBookmarksSupported
1346    def test_destroy_bookmark_nonexistent_fs(self):
1347        lzc.lzc_destroy_bookmarks(
1348            [ZFSTest.pool.makeName(b'nonexistent#bmark')])
1349
1350    @skipUnlessBookmarksSupported
1351    def test_destroy_bookmarks_empty(self):
1352        lzc.lzc_bookmark({})
1353
1354    def test_snaprange_space(self):
1355        snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1356        snap2 = ZFSTest.pool.makeName(b"fs1@snap2")
1357        snap3 = ZFSTest.pool.makeName(b"fs1@snap")
1358
1359        lzc.lzc_snapshot([snap1])
1360        lzc.lzc_snapshot([snap2])
1361        lzc.lzc_snapshot([snap3])
1362
1363        space = lzc.lzc_snaprange_space(snap1, snap2)
1364        self.assertIsInstance(space, (int, int))
1365        space = lzc.lzc_snaprange_space(snap2, snap3)
1366        self.assertIsInstance(space, (int, int))
1367        space = lzc.lzc_snaprange_space(snap1, snap3)
1368        self.assertIsInstance(space, (int, int))
1369
1370    def test_snaprange_space_2(self):
1371        snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1372        snap2 = ZFSTest.pool.makeName(b"fs1@snap2")
1373        snap3 = ZFSTest.pool.makeName(b"fs1@snap")
1374
1375        lzc.lzc_snapshot([snap1])
1376        with zfs_mount(ZFSTest.pool.makeName(b"fs1")) as mntdir:
1377            with tempfile.NamedTemporaryFile(dir=mntdir) as f:
1378                for i in range(1024):
1379                    f.write(b'x' * 1024)
1380                f.flush()
1381                lzc.lzc_snapshot([snap2])
1382        lzc.lzc_snapshot([snap3])
1383
1384        space = lzc.lzc_snaprange_space(snap1, snap2)
1385        self.assertGreater(space, 1024 * 1024)
1386        space = lzc.lzc_snaprange_space(snap2, snap3)
1387        self.assertGreater(space, 1024 * 1024)
1388        space = lzc.lzc_snaprange_space(snap1, snap3)
1389        self.assertGreater(space, 1024 * 1024)
1390
1391    def test_snaprange_space_same_snap(self):
1392        snap = ZFSTest.pool.makeName(b"fs1@snap")
1393
1394        with zfs_mount(ZFSTest.pool.makeName(b"fs1")) as mntdir:
1395            with tempfile.NamedTemporaryFile(dir=mntdir) as f:
1396                for i in range(1024):
1397                    f.write(b'x' * 1024)
1398                f.flush()
1399                lzc.lzc_snapshot([snap])
1400
1401        space = lzc.lzc_snaprange_space(snap, snap)
1402        self.assertGreater(space, 1024 * 1024)
1403        self.assertAlmostEqual(space, 1024 * 1024, delta=1024 * 1024 // 20)
1404
1405    def test_snaprange_space_wrong_order(self):
1406        snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1407        snap2 = ZFSTest.pool.makeName(b"fs1@snap2")
1408
1409        lzc.lzc_snapshot([snap1])
1410        lzc.lzc_snapshot([snap2])
1411
1412        with self.assertRaises(lzc_exc.SnapshotMismatch):
1413            lzc.lzc_snaprange_space(snap2, snap1)
1414
1415    def test_snaprange_space_unrelated(self):
1416        snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1417        snap2 = ZFSTest.pool.makeName(b"fs2@snap2")
1418
1419        lzc.lzc_snapshot([snap1])
1420        lzc.lzc_snapshot([snap2])
1421
1422        with self.assertRaises(lzc_exc.SnapshotMismatch):
1423            lzc.lzc_snaprange_space(snap1, snap2)
1424
1425    def test_snaprange_space_across_pools(self):
1426        snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1427        snap2 = ZFSTest.misc_pool.makeName(b"@snap2")
1428
1429        lzc.lzc_snapshot([snap1])
1430        lzc.lzc_snapshot([snap2])
1431
1432        with self.assertRaises(lzc_exc.PoolsDiffer):
1433            lzc.lzc_snaprange_space(snap1, snap2)
1434
1435    def test_snaprange_space_nonexistent(self):
1436        snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1437        snap2 = ZFSTest.pool.makeName(b"fs1@snap2")
1438
1439        lzc.lzc_snapshot([snap1])
1440
1441        with self.assertRaises(lzc_exc.SnapshotNotFound) as ctx:
1442            lzc.lzc_snaprange_space(snap1, snap2)
1443        self.assertEqual(ctx.exception.name, snap2)
1444
1445        with self.assertRaises(lzc_exc.SnapshotNotFound) as ctx:
1446            lzc.lzc_snaprange_space(snap2, snap1)
1447        self.assertEqual(ctx.exception.name, snap1)
1448
1449    def test_snaprange_space_invalid_name(self):
1450        snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1451        snap2 = ZFSTest.pool.makeName(b"fs1@sn#p")
1452
1453        lzc.lzc_snapshot([snap1])
1454
1455        with self.assertRaises(lzc_exc.NameInvalid):
1456            lzc.lzc_snaprange_space(snap1, snap2)
1457
1458    def test_snaprange_space_not_snap(self):
1459        snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1460        snap2 = ZFSTest.pool.makeName(b"fs1")
1461
1462        lzc.lzc_snapshot([snap1])
1463
1464        with self.assertRaises(lzc_exc.NameInvalid):
1465            lzc.lzc_snaprange_space(snap1, snap2)
1466        with self.assertRaises(lzc_exc.NameInvalid):
1467            lzc.lzc_snaprange_space(snap2, snap1)
1468
1469    def test_snaprange_space_not_snap_2(self):
1470        snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1471        snap2 = ZFSTest.pool.makeName(b"fs1#bmark")
1472
1473        lzc.lzc_snapshot([snap1])
1474
1475        with self.assertRaises(lzc_exc.NameInvalid):
1476            lzc.lzc_snaprange_space(snap1, snap2)
1477        with self.assertRaises(lzc_exc.NameInvalid):
1478            lzc.lzc_snaprange_space(snap2, snap1)
1479
1480    def test_send_space(self):
1481        snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1482        snap2 = ZFSTest.pool.makeName(b"fs1@snap2")
1483        snap3 = ZFSTest.pool.makeName(b"fs1@snap")
1484
1485        lzc.lzc_snapshot([snap1])
1486        lzc.lzc_snapshot([snap2])
1487        lzc.lzc_snapshot([snap3])
1488
1489        space = lzc.lzc_send_space(snap2, snap1)
1490        self.assertIsInstance(space, (int, int))
1491        space = lzc.lzc_send_space(snap3, snap2)
1492        self.assertIsInstance(space, (int, int))
1493        space = lzc.lzc_send_space(snap3, snap1)
1494        self.assertIsInstance(space, (int, int))
1495        space = lzc.lzc_send_space(snap1)
1496        self.assertIsInstance(space, (int, int))
1497        space = lzc.lzc_send_space(snap2)
1498        self.assertIsInstance(space, (int, int))
1499        space = lzc.lzc_send_space(snap3)
1500        self.assertIsInstance(space, (int, int))
1501
1502    def test_send_space_2(self):
1503        snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1504        snap2 = ZFSTest.pool.makeName(b"fs1@snap2")
1505        snap3 = ZFSTest.pool.makeName(b"fs1@snap")
1506
1507        lzc.lzc_snapshot([snap1])
1508        with zfs_mount(ZFSTest.pool.makeName(b"fs1")) as mntdir:
1509            with tempfile.NamedTemporaryFile(dir=mntdir) as f:
1510                for i in range(1024):
1511                    f.write(b'x' * 1024)
1512                f.flush()
1513                lzc.lzc_snapshot([snap2])
1514        lzc.lzc_snapshot([snap3])
1515
1516        space = lzc.lzc_send_space(snap2, snap1)
1517        self.assertGreater(space, 1024 * 1024)
1518
1519        space = lzc.lzc_send_space(snap3, snap2)
1520
1521        space = lzc.lzc_send_space(snap3, snap1)
1522
1523        space_empty = lzc.lzc_send_space(snap1)
1524
1525        space = lzc.lzc_send_space(snap2)
1526        self.assertGreater(space, 1024 * 1024)
1527
1528        space = lzc.lzc_send_space(snap3)
1529        self.assertEqual(space, space_empty)
1530
1531    def test_send_space_same_snap(self):
1532        snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1533        lzc.lzc_snapshot([snap1])
1534        with self.assertRaises(lzc_exc.SnapshotMismatch):
1535            lzc.lzc_send_space(snap1, snap1)
1536
1537    def test_send_space_wrong_order(self):
1538        snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1539        snap2 = ZFSTest.pool.makeName(b"fs1@snap2")
1540
1541        lzc.lzc_snapshot([snap1])
1542        lzc.lzc_snapshot([snap2])
1543
1544        with self.assertRaises(lzc_exc.SnapshotMismatch):
1545            lzc.lzc_send_space(snap1, snap2)
1546
1547    def test_send_space_unrelated(self):
1548        snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1549        snap2 = ZFSTest.pool.makeName(b"fs2@snap2")
1550
1551        lzc.lzc_snapshot([snap1])
1552        lzc.lzc_snapshot([snap2])
1553
1554        with self.assertRaises(lzc_exc.SnapshotMismatch):
1555            lzc.lzc_send_space(snap1, snap2)
1556
1557    def test_send_space_across_pools(self):
1558        snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1559        snap2 = ZFSTest.misc_pool.makeName(b"@snap2")
1560
1561        lzc.lzc_snapshot([snap1])
1562        lzc.lzc_snapshot([snap2])
1563
1564        with self.assertRaises(lzc_exc.PoolsDiffer):
1565            lzc.lzc_send_space(snap1, snap2)
1566
1567    def test_send_space_nonexistent(self):
1568        snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1569        snap2 = ZFSTest.pool.makeName(b"fs2@snap2")
1570
1571        lzc.lzc_snapshot([snap1])
1572
1573        with self.assertRaises(lzc_exc.SnapshotNotFound) as ctx:
1574            lzc.lzc_send_space(snap1, snap2)
1575        self.assertEqual(ctx.exception.name, snap1)
1576
1577        with self.assertRaises(lzc_exc.SnapshotNotFound) as ctx:
1578            lzc.lzc_send_space(snap2, snap1)
1579        self.assertEqual(ctx.exception.name, snap2)
1580
1581        with self.assertRaises(lzc_exc.SnapshotNotFound) as ctx:
1582            lzc.lzc_send_space(snap2)
1583        self.assertEqual(ctx.exception.name, snap2)
1584
1585    def test_send_space_invalid_name(self):
1586        snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1587        snap2 = ZFSTest.pool.makeName(b"fs1@sn!p")
1588
1589        lzc.lzc_snapshot([snap1])
1590
1591        with self.assertRaises(lzc_exc.NameInvalid) as ctx:
1592            lzc.lzc_send_space(snap2, snap1)
1593        self.assertEqual(ctx.exception.name, snap2)
1594        with self.assertRaises(lzc_exc.NameInvalid) as ctx:
1595            lzc.lzc_send_space(snap2)
1596        self.assertEqual(ctx.exception.name, snap2)
1597        with self.assertRaises(lzc_exc.NameInvalid) as ctx:
1598            lzc.lzc_send_space(snap1, snap2)
1599        self.assertEqual(ctx.exception.name, snap2)
1600
1601    def test_send_space_not_snap(self):
1602        snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1603        snap2 = ZFSTest.pool.makeName(b"fs1")
1604
1605        lzc.lzc_snapshot([snap1])
1606
1607        with self.assertRaises(lzc_exc.NameInvalid):
1608            lzc.lzc_send_space(snap1, snap2)
1609        with self.assertRaises(lzc_exc.NameInvalid):
1610            lzc.lzc_send_space(snap2, snap1)
1611        with self.assertRaises(lzc_exc.NameInvalid):
1612            lzc.lzc_send_space(snap2)
1613
1614    def test_send_space_not_snap_2(self):
1615        snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1616        snap2 = ZFSTest.pool.makeName(b"fs1#bmark")
1617
1618        lzc.lzc_snapshot([snap1])
1619
1620        with self.assertRaises(lzc_exc.NameInvalid):
1621            lzc.lzc_send_space(snap2, snap1)
1622        with self.assertRaises(lzc_exc.NameInvalid):
1623            lzc.lzc_send_space(snap2)
1624
1625    def test_send_full(self):
1626        snap = ZFSTest.pool.makeName(b"fs1@snap")
1627
1628        with zfs_mount(ZFSTest.pool.makeName(b"fs1")) as mntdir:
1629            with tempfile.NamedTemporaryFile(dir=mntdir) as f:
1630                for i in range(1024):
1631                    f.write(b'x' * 1024)
1632                f.flush()
1633                lzc.lzc_snapshot([snap])
1634
1635        with tempfile.TemporaryFile(suffix='.zstream') as output:
1636            estimate = lzc.lzc_send_space(snap)
1637
1638            fd = output.fileno()
1639            lzc.lzc_send(snap, None, fd)
1640            st = os.fstat(fd)
1641            # 5%, arbitrary.
1642            self.assertAlmostEqual(st.st_size, estimate, delta=estimate // 20)
1643
1644    def test_send_incremental(self):
1645        snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1646        snap2 = ZFSTest.pool.makeName(b"fs1@snap2")
1647
1648        lzc.lzc_snapshot([snap1])
1649        with zfs_mount(ZFSTest.pool.makeName(b"fs1")) as mntdir:
1650            with tempfile.NamedTemporaryFile(dir=mntdir) as f:
1651                for i in range(1024):
1652                    f.write(b'x' * 1024)
1653                f.flush()
1654                lzc.lzc_snapshot([snap2])
1655
1656        with tempfile.TemporaryFile(suffix='.zstream') as output:
1657            estimate = lzc.lzc_send_space(snap2, snap1)
1658
1659            fd = output.fileno()
1660            lzc.lzc_send(snap2, snap1, fd)
1661            st = os.fstat(fd)
1662            # 5%, arbitrary.
1663            self.assertAlmostEqual(st.st_size, estimate, delta=estimate // 20)
1664
1665    def test_send_flags(self):
1666        flags = ['embedded_data', 'large_blocks', 'compress', 'raw']
1667        snap = ZFSTest.pool.makeName(b"fs1@snap")
1668        lzc.lzc_snapshot([snap])
1669
1670        for c in range(len(flags)):
1671            for flag in itertools.permutations(flags, c + 1):
1672                with dev_null() as fd:
1673                    lzc.lzc_send(snap, None, fd, list(flag))
1674
1675    def test_send_unknown_flags(self):
1676        snap = ZFSTest.pool.makeName(b"fs1@snap")
1677        lzc.lzc_snapshot([snap])
1678        with dev_null() as fd:
1679            with self.assertRaises(lzc_exc.UnknownStreamFeature):
1680                lzc.lzc_send(snap, None, fd, ['embedded_data', 'UNKNOWN'])
1681
1682    def test_send_same_snap(self):
1683        snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1684        lzc.lzc_snapshot([snap1])
1685        with tempfile.TemporaryFile(suffix='.zstream') as output:
1686            fd = output.fileno()
1687            with self.assertRaises(lzc_exc.SnapshotMismatch):
1688                lzc.lzc_send(snap1, snap1, fd)
1689
1690    def test_send_wrong_order(self):
1691        snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1692        snap2 = ZFSTest.pool.makeName(b"fs1@snap2")
1693
1694        lzc.lzc_snapshot([snap1])
1695        lzc.lzc_snapshot([snap2])
1696
1697        with tempfile.TemporaryFile(suffix='.zstream') as output:
1698            fd = output.fileno()
1699            with self.assertRaises(lzc_exc.SnapshotMismatch):
1700                lzc.lzc_send(snap1, snap2, fd)
1701
1702    def test_send_unrelated(self):
1703        snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1704        snap2 = ZFSTest.pool.makeName(b"fs2@snap2")
1705
1706        lzc.lzc_snapshot([snap1])
1707        lzc.lzc_snapshot([snap2])
1708
1709        with tempfile.TemporaryFile(suffix='.zstream') as output:
1710            fd = output.fileno()
1711            with self.assertRaises(lzc_exc.SnapshotMismatch):
1712                lzc.lzc_send(snap1, snap2, fd)
1713
1714    def test_send_across_pools(self):
1715        snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1716        snap2 = ZFSTest.misc_pool.makeName(b"@snap2")
1717
1718        lzc.lzc_snapshot([snap1])
1719        lzc.lzc_snapshot([snap2])
1720
1721        with tempfile.TemporaryFile(suffix='.zstream') as output:
1722            fd = output.fileno()
1723            with self.assertRaises(lzc_exc.PoolsDiffer):
1724                lzc.lzc_send(snap1, snap2, fd)
1725
1726    def test_send_nonexistent(self):
1727        snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1728        snap2 = ZFSTest.pool.makeName(b"fs1@snap2")
1729
1730        lzc.lzc_snapshot([snap1])
1731
1732        with tempfile.TemporaryFile(suffix='.zstream') as output:
1733            fd = output.fileno()
1734            with self.assertRaises(lzc_exc.SnapshotNotFound) as ctx:
1735                lzc.lzc_send(snap1, snap2, fd)
1736            self.assertEqual(ctx.exception.name, snap1)
1737
1738            with self.assertRaises(lzc_exc.SnapshotNotFound) as ctx:
1739                lzc.lzc_send(snap2, snap1, fd)
1740            self.assertEqual(ctx.exception.name, snap2)
1741
1742            with self.assertRaises(lzc_exc.SnapshotNotFound) as ctx:
1743                lzc.lzc_send(snap2, None, fd)
1744            self.assertEqual(ctx.exception.name, snap2)
1745
1746    def test_send_invalid_name(self):
1747        snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1748        snap2 = ZFSTest.pool.makeName(b"fs1@sn!p")
1749
1750        lzc.lzc_snapshot([snap1])
1751
1752        with tempfile.TemporaryFile(suffix='.zstream') as output:
1753            fd = output.fileno()
1754            with self.assertRaises(lzc_exc.NameInvalid) as ctx:
1755                lzc.lzc_send(snap2, snap1, fd)
1756            self.assertEqual(ctx.exception.name, snap2)
1757            with self.assertRaises(lzc_exc.NameInvalid) as ctx:
1758                lzc.lzc_send(snap2, None, fd)
1759            self.assertEqual(ctx.exception.name, snap2)
1760            with self.assertRaises(lzc_exc.NameInvalid) as ctx:
1761                lzc.lzc_send(snap1, snap2, fd)
1762            self.assertEqual(ctx.exception.name, snap2)
1763
1764    # XXX Although undocumented the API allows to create an incremental
1765    # or full stream for a filesystem as if a temporary unnamed snapshot
1766    # is taken at some time after the call is made and before the stream
1767    # starts being produced.
1768    def test_send_filesystem(self):
1769        snap = ZFSTest.pool.makeName(b"fs1@snap1")
1770        fs = ZFSTest.pool.makeName(b"fs1")
1771
1772        lzc.lzc_snapshot([snap])
1773
1774        with tempfile.TemporaryFile(suffix='.zstream') as output:
1775            fd = output.fileno()
1776            lzc.lzc_send(fs, snap, fd)
1777            lzc.lzc_send(fs, None, fd)
1778
1779    def test_send_from_filesystem(self):
1780        snap = ZFSTest.pool.makeName(b"fs1@snap1")
1781        fs = ZFSTest.pool.makeName(b"fs1")
1782
1783        lzc.lzc_snapshot([snap])
1784
1785        with tempfile.TemporaryFile(suffix='.zstream') as output:
1786            fd = output.fileno()
1787            with self.assertRaises(lzc_exc.NameInvalid):
1788                lzc.lzc_send(snap, fs, fd)
1789
1790    @skipUnlessBookmarksSupported
1791    def test_send_bookmark(self):
1792        snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1793        snap2 = ZFSTest.pool.makeName(b"fs1@snap2")
1794        bmark = ZFSTest.pool.makeName(b"fs1#bmark")
1795
1796        lzc.lzc_snapshot([snap1])
1797        lzc.lzc_snapshot([snap2])
1798        lzc.lzc_bookmark({bmark: snap2})
1799        lzc.lzc_destroy_snaps([snap2], defer=False)
1800
1801        with tempfile.TemporaryFile(suffix='.zstream') as output:
1802            fd = output.fileno()
1803            with self.assertRaises(lzc_exc.NameInvalid):
1804                lzc.lzc_send(bmark, snap1, fd)
1805            with self.assertRaises(lzc_exc.NameInvalid):
1806                lzc.lzc_send(bmark, None, fd)
1807
1808    @skipUnlessBookmarksSupported
1809    def test_send_from_bookmark(self):
1810        snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
1811        snap2 = ZFSTest.pool.makeName(b"fs1@snap2")
1812        bmark = ZFSTest.pool.makeName(b"fs1#bmark")
1813
1814        lzc.lzc_snapshot([snap1])
1815        lzc.lzc_snapshot([snap2])
1816        lzc.lzc_bookmark({bmark: snap1})
1817        lzc.lzc_destroy_snaps([snap1], defer=False)
1818
1819        with tempfile.TemporaryFile(suffix='.zstream') as output:
1820            fd = output.fileno()
1821            lzc.lzc_send(snap2, bmark, fd)
1822
1823    def test_send_bad_fd(self):
1824        snap = ZFSTest.pool.makeName(b"fs1@snap")
1825        lzc.lzc_snapshot([snap])
1826
1827        with tempfile.TemporaryFile() as tmp:
1828            bad_fd = tmp.fileno()
1829
1830        with self.assertRaises(lzc_exc.StreamIOError) as ctx:
1831            lzc.lzc_send(snap, None, bad_fd)
1832        self.assertEqual(ctx.exception.errno, errno.EBADF)
1833
1834    def test_send_bad_fd_2(self):
1835        snap = ZFSTest.pool.makeName(b"fs1@snap")
1836        lzc.lzc_snapshot([snap])
1837
1838        with self.assertRaises(lzc_exc.StreamIOError) as ctx:
1839            lzc.lzc_send(snap, None, -2)
1840        self.assertEqual(ctx.exception.errno, errno.EBADF)
1841
1842    def test_send_bad_fd_3(self):
1843        snap = ZFSTest.pool.makeName(b"fs1@snap")
1844        lzc.lzc_snapshot([snap])
1845
1846        with tempfile.TemporaryFile() as tmp:
1847            bad_fd = tmp.fileno()
1848
1849        (soft, hard) = resource.getrlimit(resource.RLIMIT_NOFILE)
1850        bad_fd = hard + 1
1851        with self.assertRaises(lzc_exc.StreamIOError) as ctx:
1852            lzc.lzc_send(snap, None, bad_fd)
1853        self.assertEqual(ctx.exception.errno, errno.EBADF)
1854
1855    def test_send_to_broken_pipe(self):
1856        snap = ZFSTest.pool.makeName(b"fs1@snap")
1857        lzc.lzc_snapshot([snap])
1858
1859        if sys.version_info < (3, 0):
1860            proc = subprocess.Popen(['true'], stdin=subprocess.PIPE)
1861            proc.wait()
1862            with self.assertRaises(lzc_exc.StreamIOError) as ctx:
1863                lzc.lzc_send(snap, None, proc.stdin.fileno())
1864            self.assertEqual(ctx.exception.errno, errno.EPIPE)
1865        else:
1866            with subprocess.Popen(['true'], stdin=subprocess.PIPE) as proc:
1867                proc.wait()
1868                with self.assertRaises(lzc_exc.StreamIOError) as ctx:
1869                    lzc.lzc_send(snap, None, proc.stdin.fileno())
1870                self.assertEqual(ctx.exception.errno, errno.EPIPE)
1871
1872    def test_send_to_broken_pipe_2(self):
1873        snap = ZFSTest.pool.makeName(b"fs1@snap")
1874        with zfs_mount(ZFSTest.pool.makeName(b"fs1")) as mntdir:
1875            with tempfile.NamedTemporaryFile(dir=mntdir) as f:
1876                for i in range(1024):
1877                    f.write(b'x' * 1024)
1878                f.flush()
1879                lzc.lzc_snapshot([snap])
1880
1881        if sys.version_info < (3, 0):
1882            p = subprocess.Popen(['sleep', '2'], stdin=subprocess.PIPE)
1883            with self.assertRaises(lzc_exc.StreamIOError) as ctx:
1884                lzc.lzc_send(snap, None, p.stdin.fileno())
1885            self.assertTrue(ctx.exception.errno == errno.EPIPE or
1886                            ctx.exception.errno == errno.EINTR)
1887        else:
1888            with subprocess.Popen(['sleep', '2'], stdin=subprocess.PIPE) as p:
1889                with self.assertRaises(lzc_exc.StreamIOError) as ctx:
1890                    lzc.lzc_send(snap, None, p.stdin.fileno())
1891                self.assertTrue(ctx.exception.errno == errno.EPIPE or
1892                                ctx.exception.errno == errno.EINTR)
1893
1894    def test_send_to_ro_file(self):
1895        snap = ZFSTest.pool.makeName(b"fs1@snap")
1896        lzc.lzc_snapshot([snap])
1897
1898        with tempfile.NamedTemporaryFile(
1899                suffix='.zstream', delete=False) as output:
1900            # tempfile always opens a temporary file in read-write mode
1901            # regardless of the specified mode, so we have to open it again.
1902            os.chmod(output.name, stat.S_IRUSR)
1903            fd = os.open(output.name, os.O_RDONLY)
1904            with self.assertRaises(lzc_exc.StreamIOError) as ctx:
1905                lzc.lzc_send(snap, None, fd)
1906            os.close(fd)
1907            os.unlink(output.name)
1908
1909        self.assertEqual(ctx.exception.errno, errno.EBADF)
1910
1911    def test_recv_full(self):
1912        src = ZFSTest.pool.makeName(b"fs1@snap")
1913        dst = ZFSTest.pool.makeName(b"fs2/received-1@snap")
1914
1915        with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")) as name:
1916            lzc.lzc_snapshot([src])
1917
1918        with tempfile.TemporaryFile(suffix='.zstream') as stream:
1919            lzc.lzc_send(src, None, stream.fileno())
1920            stream.seek(0)
1921            lzc.lzc_receive(dst, stream.fileno())
1922
1923        name = os.path.basename(name)
1924        with zfs_mount(src) as mnt1, zfs_mount(dst) as mnt2:
1925            self.assertTrue(
1926                filecmp.cmp(
1927                    os.path.join(mnt1, name), os.path.join(mnt2, name), False))
1928
1929    def test_recv_incremental(self):
1930        src1 = ZFSTest.pool.makeName(b"fs1@snap1")
1931        src2 = ZFSTest.pool.makeName(b"fs1@snap2")
1932        dst1 = ZFSTest.pool.makeName(b"fs2/received-2@snap1")
1933        dst2 = ZFSTest.pool.makeName(b"fs2/received-2@snap2")
1934
1935        lzc.lzc_snapshot([src1])
1936        with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")) as name:
1937            lzc.lzc_snapshot([src2])
1938
1939        with tempfile.TemporaryFile(suffix='.zstream') as stream:
1940            lzc.lzc_send(src1, None, stream.fileno())
1941            stream.seek(0)
1942            lzc.lzc_receive(dst1, stream.fileno())
1943        with tempfile.TemporaryFile(suffix='.zstream') as stream:
1944            lzc.lzc_send(src2, src1, stream.fileno())
1945            stream.seek(0)
1946            lzc.lzc_receive(dst2, stream.fileno())
1947
1948        name = os.path.basename(name)
1949        with zfs_mount(src2) as mnt1, zfs_mount(dst2) as mnt2:
1950            self.assertTrue(
1951                filecmp.cmp(
1952                    os.path.join(mnt1, name), os.path.join(mnt2, name), False))
1953
1954    # This test case fails unless a patch from
1955    # https://clusterhq.atlassian.net/browse/ZFS-20
1956    # is applied to libzfs_core, otherwise it succeeds.
1957    @unittest.skip("fails with unpatched libzfs_core")
1958    def test_recv_without_explicit_snap_name(self):
1959        srcfs = ZFSTest.pool.makeName(b"fs1")
1960        src1 = srcfs + b"@snap1"
1961        src2 = srcfs + b"@snap2"
1962        dstfs = ZFSTest.pool.makeName(b"fs2/received-100")
1963        dst1 = dstfs + b'@snap1'
1964        dst2 = dstfs + b'@snap2'
1965
1966        with streams(srcfs, src1, src2) as (_, (full, incr)):
1967            lzc.lzc_receive(dstfs, full.fileno())
1968            lzc.lzc_receive(dstfs, incr.fileno())
1969        self.assertExists(dst1)
1970        self.assertExists(dst2)
1971
1972    def test_recv_clone(self):
1973        orig_src = ZFSTest.pool.makeName(b"fs2@send-origin")
1974        clone = ZFSTest.pool.makeName(b"fs1/fs/send-clone")
1975        clone_snap = clone + b"@snap"
1976        orig_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-origin@snap")
1977        clone_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-clone@snap")
1978
1979        lzc.lzc_snapshot([orig_src])
1980        with tempfile.TemporaryFile(suffix='.zstream') as stream:
1981            lzc.lzc_send(orig_src, None, stream.fileno())
1982            stream.seek(0)
1983            lzc.lzc_receive(orig_dst, stream.fileno())
1984
1985        lzc.lzc_clone(clone, orig_src)
1986        lzc.lzc_snapshot([clone_snap])
1987        with tempfile.TemporaryFile(suffix='.zstream') as stream:
1988            lzc.lzc_send(clone_snap, orig_src, stream.fileno())
1989            stream.seek(0)
1990            lzc.lzc_receive(clone_dst, stream.fileno(), origin=orig_dst)
1991
1992    def test_recv_full_already_existing_empty_fs(self):
1993        src = ZFSTest.pool.makeName(b"fs1@snap")
1994        dstfs = ZFSTest.pool.makeName(b"fs2/received-3")
1995        dst = dstfs + b'@snap'
1996
1997        with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")):
1998            lzc.lzc_snapshot([src])
1999        lzc.lzc_create(dstfs)
2000        with tempfile.TemporaryFile(suffix='.zstream') as stream:
2001            lzc.lzc_send(src, None, stream.fileno())
2002            stream.seek(0)
2003            with self.assertRaises((
2004                    lzc_exc.DestinationModified, lzc_exc.DatasetExists)):
2005                lzc.lzc_receive(dst, stream.fileno())
2006
2007    def test_recv_full_into_root_empty_pool(self):
2008        empty_pool = None
2009        try:
2010            srcfs = ZFSTest.pool.makeName(b"fs1")
2011            empty_pool = _TempPool()
2012            dst = empty_pool.makeName(b'@snap')
2013
2014            with streams(srcfs, b"snap", None) as (_, (stream, _)):
2015                with self.assertRaises((
2016                        lzc_exc.DestinationModified, lzc_exc.DatasetExists)):
2017                    lzc.lzc_receive(dst, stream.fileno())
2018        finally:
2019            if empty_pool is not None:
2020                empty_pool.cleanUp()
2021
2022    def test_recv_full_into_ro_pool(self):
2023        srcfs = ZFSTest.pool.makeName(b"fs1")
2024        dst = ZFSTest.readonly_pool.makeName(b'fs2/received@snap')
2025
2026        with streams(srcfs, b"snap", None) as (_, (stream, _)):
2027            with self.assertRaises(lzc_exc.ReadOnlyPool):
2028                lzc.lzc_receive(dst, stream.fileno())
2029
2030    def test_recv_full_already_existing_modified_fs(self):
2031        src = ZFSTest.pool.makeName(b"fs1@snap")
2032        dstfs = ZFSTest.pool.makeName(b"fs2/received-5")
2033        dst = dstfs + b'@snap'
2034
2035        with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")):
2036            lzc.lzc_snapshot([src])
2037        lzc.lzc_create(dstfs)
2038        with temp_file_in_fs(dstfs):
2039            with tempfile.TemporaryFile(suffix='.zstream') as stream:
2040                lzc.lzc_send(src, None, stream.fileno())
2041                stream.seek(0)
2042                with self.assertRaises((
2043                        lzc_exc.DestinationModified, lzc_exc.DatasetExists)):
2044                    lzc.lzc_receive(dst, stream.fileno())
2045
2046    def test_recv_full_already_existing_with_snapshots(self):
2047        src = ZFSTest.pool.makeName(b"fs1@snap")
2048        dstfs = ZFSTest.pool.makeName(b"fs2/received-4")
2049        dst = dstfs + b'@snap'
2050
2051        with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")):
2052            lzc.lzc_snapshot([src])
2053        lzc.lzc_create(dstfs)
2054        lzc.lzc_snapshot([dstfs + b"@snap1"])
2055        with tempfile.TemporaryFile(suffix='.zstream') as stream:
2056            lzc.lzc_send(src, None, stream.fileno())
2057            stream.seek(0)
2058            with self.assertRaises((
2059                    lzc_exc.StreamMismatch, lzc_exc.DatasetExists)):
2060                lzc.lzc_receive(dst, stream.fileno())
2061
2062    def test_recv_full_already_existing_snapshot(self):
2063        src = ZFSTest.pool.makeName(b"fs1@snap")
2064        dstfs = ZFSTest.pool.makeName(b"fs2/received-6")
2065        dst = dstfs + b'@snap'
2066
2067        with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")):
2068            lzc.lzc_snapshot([src])
2069        lzc.lzc_create(dstfs)
2070        lzc.lzc_snapshot([dst])
2071        with tempfile.TemporaryFile(suffix='.zstream') as stream:
2072            lzc.lzc_send(src, None, stream.fileno())
2073            stream.seek(0)
2074            with self.assertRaises(lzc_exc.DatasetExists):
2075                lzc.lzc_receive(dst, stream.fileno())
2076
2077    def test_recv_full_missing_parent_fs(self):
2078        src = ZFSTest.pool.makeName(b"fs1@snap")
2079        dst = ZFSTest.pool.makeName(b"fs2/nonexistent/fs@snap")
2080
2081        with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")):
2082            lzc.lzc_snapshot([src])
2083        with tempfile.TemporaryFile(suffix='.zstream') as stream:
2084            lzc.lzc_send(src, None, stream.fileno())
2085            stream.seek(0)
2086            with self.assertRaises(lzc_exc.DatasetNotFound):
2087                lzc.lzc_receive(dst, stream.fileno())
2088
2089    def test_recv_full_but_specify_origin(self):
2090        srcfs = ZFSTest.pool.makeName(b"fs1")
2091        src = srcfs + b"@snap"
2092        dstfs = ZFSTest.pool.makeName(b"fs2/received-30")
2093        dst = dstfs + b'@snap'
2094        origin1 = ZFSTest.pool.makeName(b"fs2@snap1")
2095        origin2 = ZFSTest.pool.makeName(b"fs2@snap2")
2096
2097        lzc.lzc_snapshot([origin1])
2098        with streams(srcfs, src, None) as (_, (stream, _)):
2099            lzc.lzc_receive(dst, stream.fileno(), origin=origin1)
2100            origin = ZFSTest.pool.getFilesystem(
2101                b"fs2/received-30").getProperty('origin')
2102            self.assertEqual(origin, origin1)
2103            stream.seek(0)
2104            # because origin snap does not exist can't receive as a clone of it
2105            with self.assertRaises((
2106                    lzc_exc.DatasetNotFound,
2107                    lzc_exc.BadStream)):
2108                lzc.lzc_receive(dst, stream.fileno(), origin=origin2)
2109
2110    def test_recv_full_existing_empty_fs_and_origin(self):
2111        srcfs = ZFSTest.pool.makeName(b"fs1")
2112        src = srcfs + b"@snap"
2113        dstfs = ZFSTest.pool.makeName(b"fs2/received-31")
2114        dst = dstfs + b'@snap'
2115        origin = dstfs + b'@dummy'
2116
2117        lzc.lzc_create(dstfs)
2118        with streams(srcfs, src, None) as (_, (stream, _)):
2119            # because the destination fs already exists and has no snaps
2120            with self.assertRaises((
2121                    lzc_exc.DestinationModified,
2122                    lzc_exc.DatasetExists,
2123                    lzc_exc.BadStream)):
2124                lzc.lzc_receive(dst, stream.fileno(), origin=origin)
2125            lzc.lzc_snapshot([origin])
2126            stream.seek(0)
2127            # because the destination fs already exists and has the snap
2128            with self.assertRaises((
2129                    lzc_exc.StreamMismatch,
2130                    lzc_exc.DatasetExists,
2131                    lzc_exc.BadStream)):
2132                lzc.lzc_receive(dst, stream.fileno(), origin=origin)
2133
2134    def test_recv_incremental_mounted_fs(self):
2135        srcfs = ZFSTest.pool.makeName(b"fs1")
2136        src1 = srcfs + b"@snap1"
2137        src2 = srcfs + b"@snap2"
2138        dstfs = ZFSTest.pool.makeName(b"fs2/received-7")
2139        dst1 = dstfs + b'@snap1'
2140        dst2 = dstfs + b'@snap2'
2141
2142        with streams(srcfs, src1, src2) as (_, (full, incr)):
2143            lzc.lzc_receive(dst1, full.fileno())
2144            with zfs_mount(dstfs):
2145                lzc.lzc_receive(dst2, incr.fileno())
2146
2147    def test_recv_incremental_modified_fs(self):
2148        srcfs = ZFSTest.pool.makeName(b"fs1")
2149        src1 = srcfs + b"@snap1"
2150        src2 = srcfs + b"@snap2"
2151        dstfs = ZFSTest.pool.makeName(b"fs2/received-15")
2152        dst1 = dstfs + b'@snap1'
2153        dst2 = dstfs + b'@snap2'
2154
2155        with streams(srcfs, src1, src2) as (_, (full, incr)):
2156            lzc.lzc_receive(dst1, full.fileno())
2157            with temp_file_in_fs(dstfs):
2158                with self.assertRaises(lzc_exc.DestinationModified):
2159                    lzc.lzc_receive(dst2, incr.fileno())
2160
2161    def test_recv_incremental_snapname_used(self):
2162        srcfs = ZFSTest.pool.makeName(b"fs1")
2163        src1 = srcfs + b"@snap1"
2164        src2 = srcfs + b"@snap2"
2165        dstfs = ZFSTest.pool.makeName(b"fs2/received-8")
2166        dst1 = dstfs + b'@snap1'
2167        dst2 = dstfs + b'@snap2'
2168
2169        with streams(srcfs, src1, src2) as (_, (full, incr)):
2170            lzc.lzc_receive(dst1, full.fileno())
2171            lzc.lzc_snapshot([dst2])
2172            with self.assertRaises(lzc_exc.DatasetExists):
2173                lzc.lzc_receive(dst2, incr.fileno())
2174
2175    def test_recv_incremental_more_recent_snap_with_no_changes(self):
2176        srcfs = ZFSTest.pool.makeName(b"fs1")
2177        src1 = srcfs + b"@snap1"
2178        src2 = srcfs + b"@snap2"
2179        dstfs = ZFSTest.pool.makeName(b"fs2/received-9")
2180        dst1 = dstfs + b'@snap1'
2181        dst2 = dstfs + b'@snap2'
2182        dst_snap = dstfs + b'@snap'
2183
2184        with streams(srcfs, src1, src2) as (_, (full, incr)):
2185            lzc.lzc_receive(dst1, full.fileno())
2186            lzc.lzc_snapshot([dst_snap])
2187            lzc.lzc_receive(dst2, incr.fileno())
2188
2189    def test_recv_incremental_non_clone_but_set_origin(self):
2190        srcfs = ZFSTest.pool.makeName(b"fs1")
2191        src1 = srcfs + b"@snap1"
2192        src2 = srcfs + b"@snap2"
2193        dstfs = ZFSTest.pool.makeName(b"fs2/received-20")
2194        dst1 = dstfs + b'@snap1'
2195        dst2 = dstfs + b'@snap2'
2196        dst_snap = dstfs + b'@snap'
2197
2198        with streams(srcfs, src1, src2) as (_, (full, incr)):
2199            lzc.lzc_receive(dst1, full.fileno())
2200            lzc.lzc_snapshot([dst_snap])
2201            # because cannot receive incremental and set origin on a non-clone
2202            with self.assertRaises(lzc_exc.BadStream):
2203                lzc.lzc_receive(dst2, incr.fileno(), origin=dst1)
2204
2205    def test_recv_incremental_non_clone_but_set_random_origin(self):
2206        srcfs = ZFSTest.pool.makeName(b"fs1")
2207        src1 = srcfs + b"@snap1"
2208        src2 = srcfs + b"@snap2"
2209        dstfs = ZFSTest.pool.makeName(b"fs2/received-21")
2210        dst1 = dstfs + b'@snap1'
2211        dst2 = dstfs + b'@snap2'
2212        dst_snap = dstfs + b'@snap'
2213
2214        with streams(srcfs, src1, src2) as (_, (full, incr)):
2215            lzc.lzc_receive(dst1, full.fileno())
2216            lzc.lzc_snapshot([dst_snap])
2217            # because origin snap does not exist can't receive as a clone of it
2218            with self.assertRaises((
2219                    lzc_exc.DatasetNotFound,
2220                    lzc_exc.BadStream)):
2221                lzc.lzc_receive(
2222                    dst2, incr.fileno(),
2223                    origin=ZFSTest.pool.makeName(b"fs2/fs@snap"))
2224
2225    def test_recv_incremental_more_recent_snap(self):
2226        srcfs = ZFSTest.pool.makeName(b"fs1")
2227        src1 = srcfs + b"@snap1"
2228        src2 = srcfs + b"@snap2"
2229        dstfs = ZFSTest.pool.makeName(b"fs2/received-10")
2230        dst1 = dstfs + b'@snap1'
2231        dst2 = dstfs + b'@snap2'
2232        dst_snap = dstfs + b'@snap'
2233
2234        with streams(srcfs, src1, src2) as (_, (full, incr)):
2235            lzc.lzc_receive(dst1, full.fileno())
2236            with temp_file_in_fs(dstfs):
2237                lzc.lzc_snapshot([dst_snap])
2238                with self.assertRaises(lzc_exc.DestinationModified):
2239                    lzc.lzc_receive(dst2, incr.fileno())
2240
2241    def test_recv_incremental_duplicate(self):
2242        srcfs = ZFSTest.pool.makeName(b"fs1")
2243        src1 = srcfs + b"@snap1"
2244        src2 = srcfs + b"@snap2"
2245        dstfs = ZFSTest.pool.makeName(b"fs2/received-11")
2246        dst1 = dstfs + b'@snap1'
2247        dst2 = dstfs + b'@snap2'
2248        dst_snap = dstfs + b'@snap'
2249
2250        with streams(srcfs, src1, src2) as (_, (full, incr)):
2251            lzc.lzc_receive(dst1, full.fileno())
2252            lzc.lzc_receive(dst2, incr.fileno())
2253            incr.seek(0)
2254            with self.assertRaises(lzc_exc.DestinationModified):
2255                lzc.lzc_receive(dst_snap, incr.fileno())
2256
2257    def test_recv_incremental_unrelated_fs(self):
2258        srcfs = ZFSTest.pool.makeName(b"fs1")
2259        src1 = srcfs + b"@snap1"
2260        src2 = srcfs + b"@snap2"
2261        dstfs = ZFSTest.pool.makeName(b"fs2/received-12")
2262        dst_snap = dstfs + b'@snap'
2263
2264        with streams(srcfs, src1, src2) as (_, (_, incr)):
2265            lzc.lzc_create(dstfs)
2266            with self.assertRaises(lzc_exc.StreamMismatch):
2267                lzc.lzc_receive(dst_snap, incr.fileno())
2268
2269    def test_recv_incremental_nonexistent_fs(self):
2270        srcfs = ZFSTest.pool.makeName(b"fs1")
2271        src1 = srcfs + b"@snap1"
2272        src2 = srcfs + b"@snap2"
2273        dstfs = ZFSTest.pool.makeName(b"fs2/received-13")
2274        dst_snap = dstfs + b'@snap'
2275
2276        with streams(srcfs, src1, src2) as (_, (_, incr)):
2277            with self.assertRaises(lzc_exc.DatasetNotFound):
2278                lzc.lzc_receive(dst_snap, incr.fileno())
2279
2280    def test_recv_incremental_same_fs(self):
2281        srcfs = ZFSTest.pool.makeName(b"fs1")
2282        src1 = srcfs + b"@snap1"
2283        src2 = srcfs + b"@snap2"
2284        src_snap = srcfs + b'@snap'
2285
2286        with streams(srcfs, src1, src2) as (_, (_, incr)):
2287            with self.assertRaises(lzc_exc.DestinationModified):
2288                lzc.lzc_receive(src_snap, incr.fileno())
2289
2290    def test_recv_clone_without_specifying_origin(self):
2291        orig_src = ZFSTest.pool.makeName(b"fs2@send-origin-2")
2292        clone = ZFSTest.pool.makeName(b"fs1/fs/send-clone-2")
2293        clone_snap = clone + b"@snap"
2294        orig_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-origin-2@snap")
2295        clone_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-clone-2@snap")
2296
2297        lzc.lzc_snapshot([orig_src])
2298        with tempfile.TemporaryFile(suffix='.zstream') as stream:
2299            lzc.lzc_send(orig_src, None, stream.fileno())
2300            stream.seek(0)
2301            lzc.lzc_receive(orig_dst, stream.fileno())
2302
2303        lzc.lzc_clone(clone, orig_src)
2304        lzc.lzc_snapshot([clone_snap])
2305        with tempfile.TemporaryFile(suffix='.zstream') as stream:
2306            lzc.lzc_send(clone_snap, orig_src, stream.fileno())
2307            stream.seek(0)
2308            with self.assertRaises(lzc_exc.BadStream):
2309                lzc.lzc_receive(clone_dst, stream.fileno())
2310
2311    def test_recv_clone_invalid_origin(self):
2312        orig_src = ZFSTest.pool.makeName(b"fs2@send-origin-3")
2313        clone = ZFSTest.pool.makeName(b"fs1/fs/send-clone-3")
2314        clone_snap = clone + b"@snap"
2315        orig_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-origin-3@snap")
2316        clone_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-clone-3@snap")
2317
2318        lzc.lzc_snapshot([orig_src])
2319        with tempfile.TemporaryFile(suffix='.zstream') as stream:
2320            lzc.lzc_send(orig_src, None, stream.fileno())
2321            stream.seek(0)
2322            lzc.lzc_receive(orig_dst, stream.fileno())
2323
2324        lzc.lzc_clone(clone, orig_src)
2325        lzc.lzc_snapshot([clone_snap])
2326        with tempfile.TemporaryFile(suffix='.zstream') as stream:
2327            lzc.lzc_send(clone_snap, orig_src, stream.fileno())
2328            stream.seek(0)
2329            with self.assertRaises(lzc_exc.NameInvalid):
2330                lzc.lzc_receive(
2331                    clone_dst, stream.fileno(),
2332                    origin=ZFSTest.pool.makeName(b"fs1/fs"))
2333
2334    def test_recv_clone_wrong_origin(self):
2335        orig_src = ZFSTest.pool.makeName(b"fs2@send-origin-4")
2336        clone = ZFSTest.pool.makeName(b"fs1/fs/send-clone-4")
2337        clone_snap = clone + b"@snap"
2338        orig_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-origin-4@snap")
2339        clone_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-clone-4@snap")
2340        wrong_origin = ZFSTest.pool.makeName(b"fs1/fs@snap")
2341
2342        lzc.lzc_snapshot([orig_src])
2343        with tempfile.TemporaryFile(suffix='.zstream') as stream:
2344            lzc.lzc_send(orig_src, None, stream.fileno())
2345            stream.seek(0)
2346            lzc.lzc_receive(orig_dst, stream.fileno())
2347
2348        lzc.lzc_clone(clone, orig_src)
2349        lzc.lzc_snapshot([clone_snap])
2350        lzc.lzc_snapshot([wrong_origin])
2351        with tempfile.TemporaryFile(suffix='.zstream') as stream:
2352            lzc.lzc_send(clone_snap, orig_src, stream.fileno())
2353            stream.seek(0)
2354            with self.assertRaises(lzc_exc.StreamMismatch):
2355                lzc.lzc_receive(
2356                    clone_dst, stream.fileno(), origin=wrong_origin)
2357
2358    def test_recv_clone_nonexistent_origin(self):
2359        orig_src = ZFSTest.pool.makeName(b"fs2@send-origin-5")
2360        clone = ZFSTest.pool.makeName(b"fs1/fs/send-clone-5")
2361        clone_snap = clone + b"@snap"
2362        orig_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-origin-5@snap")
2363        clone_dst = ZFSTest.pool.makeName(b"fs1/fs/recv-clone-5@snap")
2364        wrong_origin = ZFSTest.pool.makeName(b"fs1/fs@snap")
2365
2366        lzc.lzc_snapshot([orig_src])
2367        with tempfile.TemporaryFile(suffix='.zstream') as stream:
2368            lzc.lzc_send(orig_src, None, stream.fileno())
2369            stream.seek(0)
2370            lzc.lzc_receive(orig_dst, stream.fileno())
2371
2372        lzc.lzc_clone(clone, orig_src)
2373        lzc.lzc_snapshot([clone_snap])
2374        with tempfile.TemporaryFile(suffix='.zstream') as stream:
2375            lzc.lzc_send(clone_snap, orig_src, stream.fileno())
2376            stream.seek(0)
2377            with self.assertRaises(lzc_exc.DatasetNotFound):
2378                lzc.lzc_receive(
2379                    clone_dst, stream.fileno(), origin=wrong_origin)
2380
2381    def test_force_recv_full_existing_fs(self):
2382        src = ZFSTest.pool.makeName(b"fs1@snap")
2383        dstfs = ZFSTest.pool.makeName(b"fs2/received-50")
2384        dst = dstfs + b'@snap'
2385
2386        with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")):
2387            lzc.lzc_snapshot([src])
2388
2389        lzc.lzc_create(dstfs)
2390        with temp_file_in_fs(dstfs):
2391            pass  # enough to taint the fs
2392
2393        with tempfile.TemporaryFile(suffix='.zstream') as stream:
2394            lzc.lzc_send(src, None, stream.fileno())
2395            stream.seek(0)
2396            lzc.lzc_receive(dst, stream.fileno(), force=True)
2397
2398    def test_force_recv_full_existing_modified_mounted_fs(self):
2399        src = ZFSTest.pool.makeName(b"fs1@snap")
2400        dstfs = ZFSTest.pool.makeName(b"fs2/received-53")
2401        dst = dstfs + b'@snap'
2402
2403        with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")):
2404            lzc.lzc_snapshot([src])
2405
2406        lzc.lzc_create(dstfs)
2407
2408        with tempfile.TemporaryFile(suffix='.zstream') as stream:
2409            lzc.lzc_send(src, None, stream.fileno())
2410            stream.seek(0)
2411            with zfs_mount(dstfs) as mntdir:
2412                f = tempfile.NamedTemporaryFile(dir=mntdir, delete=False)
2413                for i in range(1024):
2414                    f.write(b'x' * 1024)
2415                lzc.lzc_receive(dst, stream.fileno(), force=True)
2416                # The temporary file disappears and any access, even close(),
2417                # results in EIO.
2418                self.assertFalse(os.path.exists(f.name))
2419                with self.assertRaises(IOError):
2420                    f.close()
2421
2422    # This test-case expects the behavior that should be there,
2423    # at the moment it may fail with DatasetExists or StreamMismatch
2424    # depending on the implementation.
2425    def test_force_recv_full_already_existing_with_snapshots(self):
2426        src = ZFSTest.pool.makeName(b"fs1@snap")
2427        dstfs = ZFSTest.pool.makeName(b"fs2/received-51")
2428        dst = dstfs + b'@snap'
2429
2430        with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")):
2431            lzc.lzc_snapshot([src])
2432
2433        lzc.lzc_create(dstfs)
2434        with temp_file_in_fs(dstfs):
2435            pass  # enough to taint the fs
2436        lzc.lzc_snapshot([dstfs + b"@snap1"])
2437
2438        with tempfile.TemporaryFile(suffix='.zstream') as stream:
2439            lzc.lzc_send(src, None, stream.fileno())
2440            stream.seek(0)
2441            lzc.lzc_receive(dst, stream.fileno(), force=True)
2442
2443    def test_force_recv_full_already_existing_with_same_snap(self):
2444        src = ZFSTest.pool.makeName(b"fs1@snap")
2445        dstfs = ZFSTest.pool.makeName(b"fs2/received-52")
2446        dst = dstfs + b'@snap'
2447
2448        with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")):
2449            lzc.lzc_snapshot([src])
2450
2451        lzc.lzc_create(dstfs)
2452        with temp_file_in_fs(dstfs):
2453            pass  # enough to taint the fs
2454        lzc.lzc_snapshot([dst])
2455
2456        with tempfile.TemporaryFile(suffix='.zstream') as stream:
2457            lzc.lzc_send(src, None, stream.fileno())
2458            stream.seek(0)
2459            with self.assertRaises(lzc_exc.DatasetExists):
2460                lzc.lzc_receive(dst, stream.fileno(), force=True)
2461
2462    def test_force_recv_full_missing_parent_fs(self):
2463        src = ZFSTest.pool.makeName(b"fs1@snap")
2464        dst = ZFSTest.pool.makeName(b"fs2/nonexistent/fs@snap")
2465
2466        with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")):
2467            lzc.lzc_snapshot([src])
2468        with tempfile.TemporaryFile(suffix='.zstream') as stream:
2469            lzc.lzc_send(src, None, stream.fileno())
2470            stream.seek(0)
2471            with self.assertRaises(lzc_exc.DatasetNotFound):
2472                lzc.lzc_receive(dst, stream.fileno(), force=True)
2473
2474    def test_force_recv_incremental_modified_fs(self):
2475        srcfs = ZFSTest.pool.makeName(b"fs1")
2476        src1 = srcfs + b"@snap1"
2477        src2 = srcfs + b"@snap2"
2478        dstfs = ZFSTest.pool.makeName(b"fs2/received-60")
2479        dst1 = dstfs + b'@snap1'
2480        dst2 = dstfs + b'@snap2'
2481
2482        with streams(srcfs, src1, src2) as (_, (full, incr)):
2483            lzc.lzc_receive(dst1, full.fileno())
2484            with temp_file_in_fs(dstfs):
2485                pass  # enough to taint the fs
2486            lzc.lzc_receive(dst2, incr.fileno(), force=True)
2487
2488    def test_force_recv_incremental_modified_mounted_fs(self):
2489        srcfs = ZFSTest.pool.makeName(b"fs1")
2490        src1 = srcfs + b"@snap1"
2491        src2 = srcfs + b"@snap2"
2492        dstfs = ZFSTest.pool.makeName(b"fs2/received-64")
2493        dst1 = dstfs + b'@snap1'
2494        dst2 = dstfs + b'@snap2'
2495
2496        with streams(srcfs, src1, src2) as (_, (full, incr)):
2497            lzc.lzc_receive(dst1, full.fileno())
2498            with zfs_mount(dstfs) as mntdir:
2499                f = tempfile.NamedTemporaryFile(dir=mntdir, delete=False)
2500                for i in range(1024):
2501                    f.write(b'x' * 1024)
2502                lzc.lzc_receive(dst2, incr.fileno(), force=True)
2503                # The temporary file disappears and any access, even close(),
2504                # results in EIO.
2505                self.assertFalse(os.path.exists(f.name))
2506                with self.assertRaises(IOError):
2507                    f.close()
2508
2509    def test_force_recv_incremental_modified_fs_plus_later_snap(self):
2510        srcfs = ZFSTest.pool.makeName(b"fs1")
2511        src1 = srcfs + b"@snap1"
2512        src2 = srcfs + b"@snap2"
2513        dstfs = ZFSTest.pool.makeName(b"fs2/received-61")
2514        dst1 = dstfs + b'@snap1'
2515        dst2 = dstfs + b'@snap2'
2516        dst3 = dstfs + b'@snap'
2517
2518        with streams(srcfs, src1, src2) as (_, (full, incr)):
2519            lzc.lzc_receive(dst1, full.fileno())
2520            with temp_file_in_fs(dstfs):
2521                pass  # enough to taint the fs
2522            lzc.lzc_snapshot([dst3])
2523            lzc.lzc_receive(dst2, incr.fileno(), force=True)
2524        self.assertExists(dst1)
2525        self.assertExists(dst2)
2526        self.assertNotExists(dst3)
2527
2528    def test_force_recv_incremental_modified_fs_plus_same_name_snap(self):
2529        srcfs = ZFSTest.pool.makeName(b"fs1")
2530        src1 = srcfs + b"@snap1"
2531        src2 = srcfs + b"@snap2"
2532        dstfs = ZFSTest.pool.makeName(b"fs2/received-62")
2533        dst1 = dstfs + b'@snap1'
2534        dst2 = dstfs + b'@snap2'
2535
2536        with streams(srcfs, src1, src2) as (_, (full, incr)):
2537            lzc.lzc_receive(dst1, full.fileno())
2538            with temp_file_in_fs(dstfs):
2539                pass  # enough to taint the fs
2540            lzc.lzc_snapshot([dst2])
2541            with self.assertRaises(lzc_exc.DatasetExists):
2542                lzc.lzc_receive(dst2, incr.fileno(), force=True)
2543
2544    def test_force_recv_incremental_modified_fs_plus_held_snap(self):
2545        srcfs = ZFSTest.pool.makeName(b"fs1")
2546        src1 = srcfs + b"@snap1"
2547        src2 = srcfs + b"@snap2"
2548        dstfs = ZFSTest.pool.makeName(b"fs2/received-63")
2549        dst1 = dstfs + b'@snap1'
2550        dst2 = dstfs + b'@snap2'
2551        dst3 = dstfs + b'@snap'
2552
2553        with streams(srcfs, src1, src2) as (_, (full, incr)):
2554            lzc.lzc_receive(dst1, full.fileno())
2555            with temp_file_in_fs(dstfs):
2556                pass  # enough to taint the fs
2557            lzc.lzc_snapshot([dst3])
2558            with cleanup_fd() as cfd:
2559                lzc.lzc_hold({dst3: b'tag'}, cfd)
2560                with self.assertRaises(lzc_exc.DatasetBusy):
2561                    lzc.lzc_receive(dst2, incr.fileno(), force=True)
2562        self.assertExists(dst1)
2563        self.assertNotExists(dst2)
2564        self.assertExists(dst3)
2565
2566    def test_force_recv_incremental_modified_fs_plus_cloned_snap(self):
2567        srcfs = ZFSTest.pool.makeName(b"fs1")
2568        src1 = srcfs + b"@snap1"
2569        src2 = srcfs + b"@snap2"
2570        dstfs = ZFSTest.pool.makeName(b"fs2/received-70")
2571        dst1 = dstfs + b'@snap1'
2572        dst2 = dstfs + b'@snap2'
2573        dst3 = dstfs + b'@snap'
2574        cloned = ZFSTest.pool.makeName(b"fs2/received-cloned-70")
2575
2576        with streams(srcfs, src1, src2) as (_, (full, incr)):
2577            lzc.lzc_receive(dst1, full.fileno())
2578            with temp_file_in_fs(dstfs):
2579                pass  # enough to taint the fs
2580            lzc.lzc_snapshot([dst3])
2581            lzc.lzc_clone(cloned, dst3)
2582            with self.assertRaises(lzc_exc.DatasetExists):
2583                lzc.lzc_receive(dst2, incr.fileno(), force=True)
2584        self.assertExists(dst1)
2585        self.assertNotExists(dst2)
2586        self.assertExists(dst3)
2587
2588    def test_recv_incremental_into_cloned_fs(self):
2589        srcfs = ZFSTest.pool.makeName(b"fs1")
2590        src1 = srcfs + b"@snap1"
2591        src2 = srcfs + b"@snap2"
2592        dstfs = ZFSTest.pool.makeName(b"fs2/received-71")
2593        dst1 = dstfs + b'@snap1'
2594        cloned = ZFSTest.pool.makeName(b"fs2/received-cloned-71")
2595        dst2 = cloned + b'@snap'
2596
2597        with streams(srcfs, src1, src2) as (_, (full, incr)):
2598            lzc.lzc_receive(dst1, full.fileno())
2599            lzc.lzc_clone(cloned, dst1)
2600            # test both graceful and with-force attempts
2601            with self.assertRaises(lzc_exc.StreamMismatch):
2602                lzc.lzc_receive(dst2, incr.fileno())
2603            incr.seek(0)
2604            with self.assertRaises(lzc_exc.StreamMismatch):
2605                lzc.lzc_receive(dst2, incr.fileno(), force=True)
2606        self.assertExists(dst1)
2607        self.assertNotExists(dst2)
2608
2609    def test_recv_with_header_full(self):
2610        src = ZFSTest.pool.makeName(b"fs1@snap")
2611        dst = ZFSTest.pool.makeName(b"fs2/received")
2612
2613        with temp_file_in_fs(ZFSTest.pool.makeName(b"fs1")) as name:
2614            lzc.lzc_snapshot([src])
2615
2616        with tempfile.TemporaryFile(suffix='.zstream') as stream:
2617            lzc.lzc_send(src, None, stream.fileno())
2618            stream.seek(0)
2619
2620            (header, c_header) = lzc.receive_header(stream.fileno())
2621            self.assertEqual(src, header['drr_toname'])
2622            snap = header['drr_toname'].split(b'@', 1)[1]
2623            lzc.lzc_receive_with_header(
2624                dst + b'@' + snap, stream.fileno(), c_header)
2625
2626        name = os.path.basename(name)
2627        with zfs_mount(src) as mnt1, zfs_mount(dst) as mnt2:
2628            self.assertTrue(
2629                filecmp.cmp(
2630                    os.path.join(mnt1, name), os.path.join(mnt2, name), False))
2631
2632    def test_recv_fs_below_zvol(self):
2633        send = ZFSTest.pool.makeName(b"fs1@snap")
2634        zvol = ZFSTest.pool.makeName(b"fs1/zvol")
2635        dest = zvol + b"/fs@snap"
2636        props = {b"volsize": 1024 * 1024}
2637
2638        lzc.lzc_snapshot([send])
2639        lzc.lzc_create(zvol, ds_type='zvol', props=props)
2640        with tempfile.TemporaryFile(suffix='.zstream') as stream:
2641            lzc.lzc_send(send, None, stream.fileno())
2642            stream.seek(0)
2643            with self.assertRaises(lzc_exc.WrongParent):
2644                lzc.lzc_receive(dest, stream.fileno())
2645
2646    def test_recv_zvol_over_fs_with_children(self):
2647        parent = ZFSTest.pool.makeName(b"fs1")
2648        child = parent + b"subfs"
2649        zvol = ZFSTest.pool.makeName(b"fs1/zvol")
2650        send = zvol + b"@snap"
2651        props = {b"volsize": 1024 * 1024}
2652
2653        lzc.lzc_create(child)
2654        lzc.lzc_create(zvol, ds_type='zvol', props=props)
2655        lzc.lzc_snapshot([send])
2656        with tempfile.TemporaryFile(suffix='.zstream') as stream:
2657            lzc.lzc_send(send, None, stream.fileno())
2658            stream.seek(0)
2659            with self.assertRaises(lzc_exc.WrongParent):
2660                lzc.lzc_receive(parent + b"@snap", stream.fileno(), force=True)
2661
2662    def test_recv_zvol_overwrite_rootds(self):
2663        zvol = ZFSTest.pool.makeName(b"fs1/zvol")
2664        snap = zvol + b"@snap"
2665        rootds = ZFSTest.pool.getRoot().getName()
2666        props = {b"volsize": 1024 * 1024}
2667
2668        lzc.lzc_create(zvol, ds_type='zvol', props=props)
2669        lzc.lzc_snapshot([snap])
2670        with tempfile.TemporaryFile(suffix='.zstream') as stream:
2671            lzc.lzc_send(snap, None, stream.fileno())
2672            stream.seek(0)
2673            with self.assertRaises(lzc_exc.WrongParent):
2674                lzc.lzc_receive(rootds + b"@snap", stream.fileno(), force=True)
2675
2676    def test_send_full_across_clone_branch_point(self):
2677        origfs = ZFSTest.pool.makeName(b"fs2")
2678
2679        (_, (fromsnap, origsnap, _)) = make_snapshots(
2680            origfs, b"snap1", b"send-origin-20", None)
2681
2682        clonefs = ZFSTest.pool.makeName(b"fs1/fs/send-clone-20")
2683        lzc.lzc_clone(clonefs, origsnap)
2684
2685        (_, (_, tosnap, _)) = make_snapshots(clonefs, None, b"snap", None)
2686
2687        with tempfile.TemporaryFile(suffix='.zstream') as stream:
2688            lzc.lzc_send(tosnap, None, stream.fileno())
2689
2690    def test_send_incr_across_clone_branch_point(self):
2691        origfs = ZFSTest.pool.makeName(b"fs2")
2692
2693        (_, (fromsnap, origsnap, _)) = make_snapshots(
2694            origfs, b"snap1", b"send-origin-21", None)
2695
2696        clonefs = ZFSTest.pool.makeName(b"fs1/fs/send-clone-21")
2697        lzc.lzc_clone(clonefs, origsnap)
2698
2699        (_, (_, tosnap, _)) = make_snapshots(clonefs, None, b"snap", None)
2700
2701        with tempfile.TemporaryFile(suffix='.zstream') as stream:
2702            lzc.lzc_send(tosnap, fromsnap, stream.fileno())
2703
2704    def test_send_resume_token_full(self):
2705        src = ZFSTest.pool.makeName(b"fs1@snap")
2706        dstfs = ZFSTest.pool.getFilesystem(b"fs2/received")
2707        dst = dstfs.getSnap()
2708
2709        with zfs_mount(ZFSTest.pool.makeName(b"fs1")) as mntdir:
2710            for i in range(1, 10):
2711                with tempfile.NamedTemporaryFile(dir=mntdir) as f:
2712                    f.write(b'x' * 1024 * i)
2713                    f.flush()
2714        lzc.lzc_snapshot([src])
2715
2716        with tempfile.NamedTemporaryFile(suffix='.zstream') as stream:
2717            lzc.lzc_send(src, None, stream.fileno())
2718            stream.seek(0)
2719            stream.truncate(1024 * 3)
2720            with self.assertRaises(lzc_exc.StreamTruncated):
2721                lzc.lzc_receive_resumable(dst, stream.fileno())
2722            # Resume token code from zfs_send_resume_token_to_nvlist()
2723            # XXX: if used more than twice move this code into an external func
2724            # format: <version>-<cksum>-<packed-size>-<compressed-payload>
2725            token = dstfs.getProperty("receive_resume_token")
2726            self.assertNotEqual(token, b'-')
2727            tokens = token.split(b'-')
2728            self.assertEqual(len(tokens), 4)
2729            version = tokens[0]
2730            packed_size = int(tokens[2], 16)
2731            compressed_nvs = tokens[3]
2732            # Validate resume token
2733            self.assertEqual(version, b'1')  # ZFS_SEND_RESUME_TOKEN_VERSION
2734            if sys.version_info < (3, 0):
2735                payload = (
2736                    zlib.decompress(str(bytearray.fromhex(compressed_nvs)))
2737                )
2738            else:
2739                payload = (
2740                    zlib.decompress(bytearray.fromhex(compressed_nvs.decode()))
2741                )
2742            self.assertEqual(len(payload), packed_size)
2743            # Unpack
2744            resume_values = packed_nvlist_out(payload, packed_size)
2745            resumeobj = resume_values.get(b'object')
2746            resumeoff = resume_values.get(b'offset')
2747            with tempfile.NamedTemporaryFile(suffix='.zstream') as rstream:
2748                lzc.lzc_send_resume(
2749                    src, None, rstream.fileno(), None, resumeobj, resumeoff)
2750                rstream.seek(0)
2751                lzc.lzc_receive_resumable(dst, rstream.fileno())
2752
2753    def test_send_resume_token_incremental(self):
2754        snap1 = ZFSTest.pool.makeName(b"fs1@snap1")
2755        snap2 = ZFSTest.pool.makeName(b"fs1@snap2")
2756        dstfs = ZFSTest.pool.getFilesystem(b"fs2/received")
2757        dst1 = dstfs.getSnap()
2758        dst2 = dstfs.getSnap()
2759
2760        lzc.lzc_snapshot([snap1])
2761        with tempfile.NamedTemporaryFile(suffix='.zstream') as stream:
2762            lzc.lzc_send(snap1, None, stream.fileno())
2763            stream.seek(0)
2764            lzc.lzc_receive(dst1, stream.fileno())
2765
2766        with zfs_mount(ZFSTest.pool.makeName(b"fs1")) as mntdir:
2767            for i in range(1, 10):
2768                with tempfile.NamedTemporaryFile(dir=mntdir) as f:
2769                    f.write(b'x' * 1024 * i)
2770                    f.flush()
2771        lzc.lzc_snapshot([snap2])
2772
2773        with tempfile.NamedTemporaryFile(suffix='.zstream') as stream:
2774            lzc.lzc_send(snap2, snap1, stream.fileno())
2775            stream.seek(0)
2776            stream.truncate(1024 * 3)
2777            with self.assertRaises(lzc_exc.StreamTruncated):
2778                lzc.lzc_receive_resumable(dst2, stream.fileno())
2779            # Resume token code from zfs_send_resume_token_to_nvlist()
2780            # format: <version>-<cksum>-<packed-size>-<compressed-payload>
2781            token = dstfs.getProperty("receive_resume_token")
2782            self.assertNotEqual(token, '-')
2783            tokens = token.split(b'-')
2784            self.assertEqual(len(tokens), 4)
2785            version = tokens[0]
2786            packed_size = int(tokens[2], 16)
2787            compressed_nvs = tokens[3]
2788            # Validate resume token
2789            self.assertEqual(version, b'1')  # ZFS_SEND_RESUME_TOKEN_VERSION
2790            if sys.version_info < (3, 0):
2791                payload = (
2792                     zlib.decompress(str(bytearray.fromhex(compressed_nvs)))
2793                )
2794            else:
2795                payload = (
2796                    zlib.decompress(bytearray.fromhex(compressed_nvs.decode()))
2797                )
2798            self.assertEqual(len(payload), packed_size)
2799            # Unpack
2800            resume_values = packed_nvlist_out(payload, packed_size)
2801            resumeobj = resume_values.get(b'object')
2802            resumeoff = resume_values.get(b'offset')
2803            with tempfile.NamedTemporaryFile(suffix='.zstream') as rstream:
2804                lzc.lzc_send_resume(
2805                    snap2, snap1, rstream.fileno(), None, resumeobj, resumeoff)
2806                rstream.seek(0)
2807                lzc.lzc_receive_resumable(dst2, rstream.fileno())
2808
2809    def test_recv_full_across_clone_branch_point(self):
2810        origfs = ZFSTest.pool.makeName(b"fs2")
2811
2812        (_, (fromsnap, origsnap, _)) = make_snapshots(
2813            origfs, b"snap1", b"send-origin-30", None)
2814
2815        clonefs = ZFSTest.pool.makeName(b"fs1/fs/send-clone-30")
2816        lzc.lzc_clone(clonefs, origsnap)
2817
2818        (_, (_, tosnap, _)) = make_snapshots(clonefs, None, b"snap", None)
2819
2820        recvfs = ZFSTest.pool.makeName(b"fs1/recv-clone-30")
2821        recvsnap = recvfs + b"@snap"
2822        with tempfile.TemporaryFile(suffix='.zstream') as stream:
2823            lzc.lzc_send(tosnap, None, stream.fileno())
2824            stream.seek(0)
2825            lzc.lzc_receive(recvsnap, stream.fileno())
2826
2827    def test_recv_one(self):
2828        fromsnap = ZFSTest.pool.makeName(b"fs1@snap1")
2829        tosnap = ZFSTest.pool.makeName(b"recv@snap1")
2830
2831        lzc.lzc_snapshot([fromsnap])
2832        with tempfile.TemporaryFile(suffix='.zstream') as stream:
2833            lzc.lzc_send(fromsnap, None, stream.fileno())
2834            stream.seek(0)
2835            (header, c_header) = lzc.receive_header(stream.fileno())
2836            lzc.lzc_receive_one(tosnap, stream.fileno(), c_header)
2837
2838    def test_recv_one_size(self):
2839        fromsnap = ZFSTest.pool.makeName(b"fs1@snap1")
2840        tosnap = ZFSTest.pool.makeName(b"recv@snap1")
2841
2842        lzc.lzc_snapshot([fromsnap])
2843        with tempfile.TemporaryFile(suffix='.zstream') as stream:
2844            lzc.lzc_send(fromsnap, None, stream.fileno())
2845            size = os.fstat(stream.fileno()).st_size
2846            stream.seek(0)
2847            (header, c_header) = lzc.receive_header(stream.fileno())
2848            (read, _) = lzc.lzc_receive_one(tosnap, stream.fileno(), c_header)
2849            self.assertAlmostEqual(read, size, delta=read * 0.05)
2850
2851    def test_recv_one_props(self):
2852        fromsnap = ZFSTest.pool.makeName(b"fs1@snap1")
2853        fs = ZFSTest.pool.getFilesystem(b"recv")
2854        tosnap = fs.getName() + b"@snap1"
2855        props = {
2856            b"compression": 0x01,
2857            b"ns:prop": b"val"
2858        }
2859
2860        lzc.lzc_snapshot([fromsnap])
2861        with tempfile.TemporaryFile(suffix='.zstream') as stream:
2862            lzc.lzc_send(fromsnap, None, stream.fileno())
2863            stream.seek(0)
2864            (header, c_header) = lzc.receive_header(stream.fileno())
2865            lzc.lzc_receive_one(tosnap, stream.fileno(), c_header, props=props)
2866            self.assertExists(tosnap)
2867            self.assertEqual(fs.getProperty("compression", "received"), b"on")
2868            self.assertEqual(fs.getProperty("ns:prop", "received"), b"val")
2869
2870    def test_recv_one_invalid_prop(self):
2871        fromsnap = ZFSTest.pool.makeName(b"fs1@snap1")
2872        fs = ZFSTest.pool.getFilesystem(b"recv")
2873        tosnap = fs.getName() + b"@snap1"
2874        props = {
2875            b"exec": 0xff,
2876            b"atime": 0x00
2877        }
2878
2879        lzc.lzc_snapshot([fromsnap])
2880        with tempfile.TemporaryFile(suffix='.zstream') as stream:
2881            lzc.lzc_send(fromsnap, None, stream.fileno())
2882            stream.seek(0)
2883            (header, c_header) = lzc.receive_header(stream.fileno())
2884            with self.assertRaises(lzc_exc.ReceivePropertyFailure) as ctx:
2885                lzc.lzc_receive_one(
2886                    tosnap, stream.fileno(), c_header, props=props)
2887            self.assertExists(tosnap)
2888            self.assertEqual(fs.getProperty("atime", "received"), b"off")
2889            for e in ctx.exception.errors:
2890                self.assertIsInstance(e, lzc_exc.PropertyInvalid)
2891                self.assertEqual(e.name, b"exec")
2892
2893    def test_recv_with_cmdprops(self):
2894        fromsnap = ZFSTest.pool.makeName(b"fs1@snap1")
2895        fs = ZFSTest.pool.getFilesystem(b"recv")
2896        tosnap = fs.getName() + b"@snap1"
2897        props = {}
2898        cmdprops = {
2899            b"compression": 0x01,
2900            b"ns:prop": b"val"
2901        }
2902
2903        lzc.lzc_snapshot([fromsnap])
2904        with tempfile.TemporaryFile(suffix='.zstream') as stream:
2905            lzc.lzc_send(fromsnap, None, stream.fileno())
2906            stream.seek(0)
2907            (header, c_header) = lzc.receive_header(stream.fileno())
2908            lzc.lzc_receive_with_cmdprops(
2909                tosnap, stream.fileno(), c_header, props=props,
2910                cmdprops=cmdprops)
2911            self.assertExists(tosnap)
2912            self.assertEqual(fs.getProperty("compression"), b"on")
2913            self.assertEqual(fs.getProperty("ns:prop"), b"val")
2914
2915    def test_recv_with_heal(self):
2916        snap = ZFSTest.pool.makeName(b"fs1@snap1")
2917        fs = ZFSTest.pool.getFilesystem(b"fs1")
2918        props = {}
2919        cmdprops = {
2920            b"compression": 0x01,
2921            b"ns:prop": b"val"
2922        }
2923
2924        lzc.lzc_snapshot([snap])
2925        with tempfile.TemporaryFile(suffix='.zstream') as stream:
2926            lzc.lzc_send(snap, None, stream.fileno())
2927            stream.seek(0)
2928            (header, c_header) = lzc.receive_header(stream.fileno())
2929            lzc.lzc_receive_with_heal(
2930                snap, stream.fileno(), c_header, props=props,
2931                cmdprops=cmdprops)
2932            self.assertExists(snap)
2933            self.assertEqual(fs.getProperty("compression"), b"on")
2934            self.assertEqual(fs.getProperty("ns:prop"), b"val")
2935
2936    def test_recv_with_cmdprops_and_recvprops(self):
2937        fromsnap = ZFSTest.pool.makeName(b"fs1@snap1")
2938        fs = ZFSTest.pool.getFilesystem(b"recv")
2939        tosnap = fs.getName() + b"@snap1"
2940        props = {
2941            b"atime": 0x01,
2942            b"exec": 0x00,
2943            b"ns:prop": b"abc"
2944        }
2945        cmdprops = {
2946            b"compression": 0x01,
2947            b"ns:prop": b"def",
2948            b"exec": None,
2949        }
2950
2951        lzc.lzc_snapshot([fromsnap])
2952        with tempfile.TemporaryFile(suffix='.zstream') as stream:
2953            lzc.lzc_send(fromsnap, None, stream.fileno())
2954            stream.seek(0)
2955            (header, c_header) = lzc.receive_header(stream.fileno())
2956            lzc.lzc_receive_with_cmdprops(
2957                tosnap, stream.fileno(), c_header, props=props,
2958                cmdprops=cmdprops)
2959            self.assertExists(tosnap)
2960            self.assertEqual(fs.getProperty("atime", True), b"on")
2961            self.assertEqual(fs.getProperty("exec", True), b"off")
2962            self.assertEqual(fs.getProperty("ns:prop", True), b"abc")
2963            self.assertEqual(fs.getProperty("compression"), b"on")
2964            self.assertEqual(fs.getProperty("ns:prop"), b"def")
2965            self.assertEqual(fs.getProperty("exec"), b"on")
2966
2967    def test_recv_incr_across_clone_branch_point_no_origin(self):
2968        origfs = ZFSTest.pool.makeName(b"fs2")
2969
2970        (_, (fromsnap, origsnap, _)) = make_snapshots(
2971            origfs, b"snap1", b"send-origin-32", None)
2972
2973        clonefs = ZFSTest.pool.makeName(b"fs1/fs/send-clone-32")
2974        lzc.lzc_clone(clonefs, origsnap)
2975
2976        (_, (_, tosnap, _)) = make_snapshots(clonefs, None, b"snap", None)
2977
2978        recvfs = ZFSTest.pool.makeName(b"fs1/recv-clone-32")
2979        recvsnap1 = recvfs + b"@snap1"
2980        recvsnap2 = recvfs + b"@snap2"
2981        with tempfile.TemporaryFile(suffix='.zstream') as stream:
2982            lzc.lzc_send(fromsnap, None, stream.fileno())
2983            stream.seek(0)
2984            lzc.lzc_receive(recvsnap1, stream.fileno())
2985        with tempfile.TemporaryFile(suffix='.zstream') as stream:
2986            lzc.lzc_send(tosnap, fromsnap, stream.fileno())
2987            stream.seek(0)
2988            with self.assertRaises(lzc_exc.BadStream):
2989                lzc.lzc_receive(recvsnap2, stream.fileno())
2990
2991    def test_recv_incr_across_clone_branch_point(self):
2992        origfs = ZFSTest.pool.makeName(b"fs2")
2993
2994        (_, (fromsnap, origsnap, _)) = make_snapshots(
2995            origfs, b"snap1", b"send-origin-31", None)
2996
2997        clonefs = ZFSTest.pool.makeName(b"fs1/fs/send-clone-31")
2998        lzc.lzc_clone(clonefs, origsnap)
2999
3000        (_, (_, tosnap, _)) = make_snapshots(clonefs, None, b"snap", None)
3001
3002        recvfs = ZFSTest.pool.makeName(b"fs1/recv-clone-31")
3003        recvsnap1 = recvfs + b"@snap1"
3004        recvsnap2 = recvfs + b"@snap2"
3005        with tempfile.TemporaryFile(suffix='.zstream') as stream:
3006            lzc.lzc_send(fromsnap, None, stream.fileno())
3007            stream.seek(0)
3008            lzc.lzc_receive(recvsnap1, stream.fileno())
3009        with tempfile.TemporaryFile(suffix='.zstream') as stream:
3010            lzc.lzc_send(tosnap, fromsnap, stream.fileno())
3011            stream.seek(0)
3012            with self.assertRaises(lzc_exc.BadStream):
3013                lzc.lzc_receive(recvsnap2, stream.fileno(), origin=recvsnap1)
3014
3015    def test_recv_incr_across_clone_branch_point_new_fs(self):
3016        origfs = ZFSTest.pool.makeName(b"fs2")
3017
3018        (_, (fromsnap, origsnap, _)) = make_snapshots(
3019            origfs, b"snap1", b"send-origin-33", None)
3020
3021        clonefs = ZFSTest.pool.makeName(b"fs1/fs/send-clone-33")
3022        lzc.lzc_clone(clonefs, origsnap)
3023
3024        (_, (_, tosnap, _)) = make_snapshots(clonefs, None, b"snap", None)
3025
3026        recvfs1 = ZFSTest.pool.makeName(b"fs1/recv-clone-33")
3027        recvsnap1 = recvfs1 + b"@snap"
3028        recvfs2 = ZFSTest.pool.makeName(b"fs1/recv-clone-33_2")
3029        recvsnap2 = recvfs2 + b"@snap"
3030        with tempfile.TemporaryFile(suffix='.zstream') as stream:
3031            lzc.lzc_send(fromsnap, None, stream.fileno())
3032            stream.seek(0)
3033            lzc.lzc_receive(recvsnap1, stream.fileno())
3034        with tempfile.TemporaryFile(suffix='.zstream') as stream:
3035            lzc.lzc_send(tosnap, fromsnap, stream.fileno())
3036            stream.seek(0)
3037            lzc.lzc_receive(recvsnap2, stream.fileno(), origin=recvsnap1)
3038
3039    def test_recv_bad_stream(self):
3040        dstfs = ZFSTest.pool.makeName(b"fs2/received")
3041        dst_snap = dstfs + b'@snap'
3042
3043        with dev_zero() as fd:
3044            with self.assertRaises(lzc_exc.BadStream):
3045                lzc.lzc_receive(dst_snap, fd)
3046
3047    @needs_support(lzc.lzc_promote)
3048    def test_promote(self):
3049        origfs = ZFSTest.pool.makeName(b"fs2")
3050        snap = b"@promote-snap-1"
3051        origsnap = origfs + snap
3052        lzc.lzc_snap([origsnap])
3053
3054        clonefs = ZFSTest.pool.makeName(b"fs1/fs/promote-clone-1")
3055        lzc.lzc_clone(clonefs, origsnap)
3056
3057        lzc.lzc_promote(clonefs)
3058        # the snapshot now should belong to the promoted fs
3059        self.assertExists(clonefs + snap)
3060
3061    @needs_support(lzc.lzc_promote)
3062    def test_promote_too_long_snapname(self):
3063        # origfs name must be shorter than clonefs name
3064        origfs = ZFSTest.pool.makeName(b"fs2")
3065        clonefs = ZFSTest.pool.makeName(b"fs1/fs/promote-clone-2")
3066        snapprefix = b"@promote-snap-2-"
3067        pad_len = 1 + lzc.MAXNAMELEN - len(clonefs) - len(snapprefix)
3068        snap = snapprefix + b'x' * pad_len
3069        origsnap = origfs + snap
3070
3071        lzc.lzc_snap([origsnap])
3072        lzc.lzc_clone(clonefs, origsnap)
3073
3074        # This may fail on older buggy systems.
3075        # See: https://www.illumos.org/issues/5909
3076        with self.assertRaises(lzc_exc.NameTooLong):
3077            lzc.lzc_promote(clonefs)
3078
3079    @needs_support(lzc.lzc_promote)
3080    def test_promote_not_cloned(self):
3081        fs = ZFSTest.pool.makeName(b"fs2")
3082        with self.assertRaises(lzc_exc.NotClone):
3083            lzc.lzc_promote(fs)
3084
3085    @unittest.skipIf(*illumos_bug_6379())
3086    def test_hold_bad_fd(self):
3087        snap = ZFSTest.pool.getRoot().getSnap()
3088        lzc.lzc_snapshot([snap])
3089
3090        with tempfile.TemporaryFile() as tmp:
3091            bad_fd = tmp.fileno()
3092
3093        with self.assertRaises(lzc_exc.BadHoldCleanupFD):
3094            lzc.lzc_hold({snap: b'tag'}, bad_fd)
3095
3096    @unittest.skipIf(*illumos_bug_6379())
3097    def test_hold_bad_fd_2(self):
3098        snap = ZFSTest.pool.getRoot().getSnap()
3099        lzc.lzc_snapshot([snap])
3100
3101        with self.assertRaises(lzc_exc.BadHoldCleanupFD):
3102            lzc.lzc_hold({snap: b'tag'}, -2)
3103
3104    @unittest.skipIf(*illumos_bug_6379())
3105    def test_hold_bad_fd_3(self):
3106        snap = ZFSTest.pool.getRoot().getSnap()
3107        lzc.lzc_snapshot([snap])
3108
3109        (soft, hard) = resource.getrlimit(resource.RLIMIT_NOFILE)
3110        bad_fd = hard + 1
3111        with self.assertRaises(lzc_exc.BadHoldCleanupFD):
3112            lzc.lzc_hold({snap: b'tag'}, bad_fd)
3113
3114    @unittest.skipIf(*illumos_bug_6379())
3115    def test_hold_wrong_fd(self):
3116        snap = ZFSTest.pool.getRoot().getSnap()
3117        lzc.lzc_snapshot([snap])
3118
3119        with tempfile.TemporaryFile() as tmp:
3120            fd = tmp.fileno()
3121            with self.assertRaises(lzc_exc.BadHoldCleanupFD):
3122                lzc.lzc_hold({snap: b'tag'}, fd)
3123
3124    def test_hold_fd(self):
3125        snap = ZFSTest.pool.getRoot().getSnap()
3126        lzc.lzc_snapshot([snap])
3127
3128        with cleanup_fd() as fd:
3129            lzc.lzc_hold({snap: b'tag'}, fd)
3130
3131    def test_hold_empty(self):
3132        with cleanup_fd() as fd:
3133            lzc.lzc_hold({}, fd)
3134
3135    def test_hold_empty_2(self):
3136        lzc.lzc_hold({})
3137
3138    def test_hold_vs_snap_destroy(self):
3139        snap = ZFSTest.pool.getRoot().getSnap()
3140        lzc.lzc_snapshot([snap])
3141
3142        with cleanup_fd() as fd:
3143            lzc.lzc_hold({snap: b'tag'}, fd)
3144
3145            with self.assertRaises(lzc_exc.SnapshotDestructionFailure) as ctx:
3146                lzc.lzc_destroy_snaps([snap], defer=False)
3147            for e in ctx.exception.errors:
3148                self.assertIsInstance(e, lzc_exc.SnapshotIsHeld)
3149
3150            lzc.lzc_destroy_snaps([snap], defer=True)
3151            self.assertExists(snap)
3152
3153        # after automatic hold cleanup and deferred destruction
3154        self.assertNotExists(snap)
3155
3156    def test_hold_many_tags(self):
3157        snap = ZFSTest.pool.getRoot().getSnap()
3158        lzc.lzc_snapshot([snap])
3159
3160        with cleanup_fd() as fd:
3161            lzc.lzc_hold({snap: b'tag1'}, fd)
3162            lzc.lzc_hold({snap: b'tag2'}, fd)
3163
3164    def test_hold_many_snaps(self):
3165        snap1 = ZFSTest.pool.getRoot().getSnap()
3166        snap2 = ZFSTest.pool.getRoot().getSnap()
3167        lzc.lzc_snapshot([snap1])
3168        lzc.lzc_snapshot([snap2])
3169
3170        with cleanup_fd() as fd:
3171            lzc.lzc_hold({snap1: b'tag', snap2: b'tag'}, fd)
3172
3173    def test_hold_many_with_one_missing(self):
3174        snap1 = ZFSTest.pool.getRoot().getSnap()
3175        snap2 = ZFSTest.pool.getRoot().getSnap()
3176        lzc.lzc_snapshot([snap1])
3177
3178        with cleanup_fd() as fd:
3179            missing = lzc.lzc_hold({snap1: b'tag', snap2: b'tag'}, fd)
3180        self.assertEqual(len(missing), 1)
3181        self.assertEqual(missing[0], snap2)
3182
3183    def test_hold_many_with_all_missing(self):
3184        snap1 = ZFSTest.pool.getRoot().getSnap()
3185        snap2 = ZFSTest.pool.getRoot().getSnap()
3186
3187        with cleanup_fd() as fd:
3188            missing = lzc.lzc_hold({snap1: b'tag', snap2: b'tag'}, fd)
3189        self.assertEqual(len(missing), 2)
3190        self.assertEqual(sorted(missing), sorted([snap1, snap2]))
3191
3192    def test_hold_missing_fs(self):
3193        # XXX skip pre-created filesystems
3194        ZFSTest.pool.getRoot().getFilesystem()
3195        ZFSTest.pool.getRoot().getFilesystem()
3196        ZFSTest.pool.getRoot().getFilesystem()
3197        ZFSTest.pool.getRoot().getFilesystem()
3198        ZFSTest.pool.getRoot().getFilesystem()
3199        snap = ZFSTest.pool.getRoot().getFilesystem().getSnap()
3200
3201        snaps = lzc.lzc_hold({snap: b'tag'})
3202        self.assertEqual([snap], snaps)
3203
3204    def test_hold_missing_fs_auto_cleanup(self):
3205        # XXX skip pre-created filesystems
3206        ZFSTest.pool.getRoot().getFilesystem()
3207        ZFSTest.pool.getRoot().getFilesystem()
3208        ZFSTest.pool.getRoot().getFilesystem()
3209        ZFSTest.pool.getRoot().getFilesystem()
3210        ZFSTest.pool.getRoot().getFilesystem()
3211        snap = ZFSTest.pool.getRoot().getFilesystem().getSnap()
3212
3213        with cleanup_fd() as fd:
3214            snaps = lzc.lzc_hold({snap: b'tag'}, fd)
3215            self.assertEqual([snap], snaps)
3216
3217    def test_hold_duplicate(self):
3218        snap = ZFSTest.pool.getRoot().getSnap()
3219        lzc.lzc_snapshot([snap])
3220
3221        with cleanup_fd() as fd:
3222            lzc.lzc_hold({snap: b'tag'}, fd)
3223            with self.assertRaises(lzc_exc.HoldFailure) as ctx:
3224                lzc.lzc_hold({snap: b'tag'}, fd)
3225        for e in ctx.exception.errors:
3226            self.assertIsInstance(e, lzc_exc.HoldExists)
3227
3228    def test_hold_across_pools(self):
3229        snap1 = ZFSTest.pool.getRoot().getSnap()
3230        snap2 = ZFSTest.misc_pool.getRoot().getSnap()
3231        lzc.lzc_snapshot([snap1])
3232        lzc.lzc_snapshot([snap2])
3233
3234        with cleanup_fd() as fd:
3235            with self.assertRaises(lzc_exc.HoldFailure) as ctx:
3236                lzc.lzc_hold({snap1: b'tag', snap2: b'tag'}, fd)
3237        for e in ctx.exception.errors:
3238            self.assertIsInstance(e, lzc_exc.PoolsDiffer)
3239
3240    def test_hold_too_long_tag(self):
3241        snap = ZFSTest.pool.getRoot().getSnap()
3242        tag = b't' * 256
3243        lzc.lzc_snapshot([snap])
3244
3245        with cleanup_fd() as fd:
3246            with self.assertRaises(lzc_exc.HoldFailure) as ctx:
3247                lzc.lzc_hold({snap: tag}, fd)
3248        for e in ctx.exception.errors:
3249            self.assertIsInstance(e, lzc_exc.NameTooLong)
3250            self.assertEqual(e.name, tag)
3251
3252    # Apparently the full snapshot name is not checked for length
3253    # and this snapshot is treated as simply missing.
3254    @unittest.expectedFailure
3255    def test_hold_too_long_snap_name(self):
3256        snap = ZFSTest.pool.getRoot().getTooLongSnap(False)
3257        with cleanup_fd() as fd:
3258            with self.assertRaises(lzc_exc.HoldFailure) as ctx:
3259                lzc.lzc_hold({snap: b'tag'}, fd)
3260        for e in ctx.exception.errors:
3261            self.assertIsInstance(e, lzc_exc.NameTooLong)
3262            self.assertEqual(e.name, snap)
3263
3264    def test_hold_too_long_snap_name_2(self):
3265        snap = ZFSTest.pool.getRoot().getTooLongSnap(True)
3266        with cleanup_fd() as fd:
3267            with self.assertRaises(lzc_exc.HoldFailure) as ctx:
3268                lzc.lzc_hold({snap: b'tag'}, fd)
3269        for e in ctx.exception.errors:
3270            self.assertIsInstance(e, lzc_exc.NameTooLong)
3271            self.assertEqual(e.name, snap)
3272
3273    def test_hold_invalid_snap_name(self):
3274        snap = ZFSTest.pool.getRoot().getSnap() + b'@bad'
3275        with cleanup_fd() as fd:
3276            with self.assertRaises(lzc_exc.HoldFailure) as ctx:
3277                lzc.lzc_hold({snap: b'tag'}, fd)
3278        for e in ctx.exception.errors:
3279            self.assertIsInstance(e, lzc_exc.NameInvalid)
3280            self.assertEqual(e.name, snap)
3281
3282    def test_hold_invalid_snap_name_2(self):
3283        snap = ZFSTest.pool.getRoot().getFilesystem().getName()
3284        with cleanup_fd() as fd:
3285            with self.assertRaises(lzc_exc.HoldFailure) as ctx:
3286                lzc.lzc_hold({snap: b'tag'}, fd)
3287        for e in ctx.exception.errors:
3288            self.assertIsInstance(e, lzc_exc.NameInvalid)
3289            self.assertEqual(e.name, snap)
3290
3291    def test_get_holds(self):
3292        snap = ZFSTest.pool.getRoot().getSnap()
3293        lzc.lzc_snapshot([snap])
3294
3295        with cleanup_fd() as fd:
3296            lzc.lzc_hold({snap: b'tag1'}, fd)
3297            lzc.lzc_hold({snap: b'tag2'}, fd)
3298
3299            holds = lzc.lzc_get_holds(snap)
3300            self.assertEqual(len(holds), 2)
3301            self.assertIn(b'tag1', holds)
3302            self.assertIn(b'tag2', holds)
3303            self.assertIsInstance(holds[b'tag1'], (int, int))
3304
3305    def test_get_holds_after_auto_cleanup(self):
3306        snap = ZFSTest.pool.getRoot().getSnap()
3307        lzc.lzc_snapshot([snap])
3308
3309        with cleanup_fd() as fd:
3310            lzc.lzc_hold({snap: b'tag1'}, fd)
3311            lzc.lzc_hold({snap: b'tag2'}, fd)
3312
3313        holds = lzc.lzc_get_holds(snap)
3314        self.assertEqual(len(holds), 0)
3315        self.assertIsInstance(holds, dict)
3316
3317    def test_get_holds_nonexistent_snap(self):
3318        snap = ZFSTest.pool.getRoot().getSnap()
3319        with self.assertRaises(lzc_exc.SnapshotNotFound):
3320            lzc.lzc_get_holds(snap)
3321
3322    def test_get_holds_too_long_snap_name(self):
3323        snap = ZFSTest.pool.getRoot().getTooLongSnap(False)
3324        with self.assertRaises(lzc_exc.NameTooLong):
3325            lzc.lzc_get_holds(snap)
3326
3327    def test_get_holds_too_long_snap_name_2(self):
3328        snap = ZFSTest.pool.getRoot().getTooLongSnap(True)
3329        with self.assertRaises(lzc_exc.NameTooLong):
3330            lzc.lzc_get_holds(snap)
3331
3332    def test_get_holds_invalid_snap_name(self):
3333        snap = ZFSTest.pool.getRoot().getSnap() + b'@bad'
3334        with self.assertRaises(lzc_exc.NameInvalid):
3335            lzc.lzc_get_holds(snap)
3336
3337    # A filesystem-like snapshot name is not recognized as
3338    # an invalid name.
3339    @unittest.expectedFailure
3340    def test_get_holds_invalid_snap_name_2(self):
3341        snap = ZFSTest.pool.getRoot().getFilesystem().getName()
3342        with self.assertRaises(lzc_exc.NameInvalid):
3343            lzc.lzc_get_holds(snap)
3344
3345    def test_release_hold(self):
3346        snap = ZFSTest.pool.getRoot().getSnap()
3347        lzc.lzc_snapshot([snap])
3348
3349        lzc.lzc_hold({snap: b'tag'})
3350        ret = lzc.lzc_release({snap: [b'tag']})
3351        self.assertEqual(len(ret), 0)
3352
3353    def test_release_hold_empty(self):
3354        ret = lzc.lzc_release({})
3355        self.assertEqual(len(ret), 0)
3356
3357    def test_release_hold_complex(self):
3358        snap1 = ZFSTest.pool.getRoot().getSnap()
3359        snap2 = ZFSTest.pool.getRoot().getSnap()
3360        snap3 = ZFSTest.pool.getRoot().getFilesystem().getSnap()
3361        lzc.lzc_snapshot([snap1])
3362        lzc.lzc_snapshot([snap2, snap3])
3363
3364        lzc.lzc_hold({snap1: b'tag1'})
3365        lzc.lzc_hold({snap1: b'tag2'})
3366        lzc.lzc_hold({snap2: b'tag'})
3367        lzc.lzc_hold({snap3: b'tag1'})
3368        lzc.lzc_hold({snap3: b'tag2'})
3369
3370        holds = lzc.lzc_get_holds(snap1)
3371        self.assertEqual(len(holds), 2)
3372        holds = lzc.lzc_get_holds(snap2)
3373        self.assertEqual(len(holds), 1)
3374        holds = lzc.lzc_get_holds(snap3)
3375        self.assertEqual(len(holds), 2)
3376
3377        release = {
3378            snap1: [b'tag1', b'tag2'],
3379            snap2: [b'tag'],
3380            snap3: [b'tag2'],
3381        }
3382        ret = lzc.lzc_release(release)
3383        self.assertEqual(len(ret), 0)
3384
3385        holds = lzc.lzc_get_holds(snap1)
3386        self.assertEqual(len(holds), 0)
3387        holds = lzc.lzc_get_holds(snap2)
3388        self.assertEqual(len(holds), 0)
3389        holds = lzc.lzc_get_holds(snap3)
3390        self.assertEqual(len(holds), 1)
3391
3392        ret = lzc.lzc_release({snap3: [b'tag1']})
3393        self.assertEqual(len(ret), 0)
3394        holds = lzc.lzc_get_holds(snap3)
3395        self.assertEqual(len(holds), 0)
3396
3397    def test_release_hold_before_auto_cleanup(self):
3398        snap = ZFSTest.pool.getRoot().getSnap()
3399        lzc.lzc_snapshot([snap])
3400
3401        with cleanup_fd() as fd:
3402            lzc.lzc_hold({snap: b'tag'}, fd)
3403            ret = lzc.lzc_release({snap: [b'tag']})
3404            self.assertEqual(len(ret), 0)
3405
3406    def test_release_hold_and_snap_destruction(self):
3407        snap = ZFSTest.pool.getRoot().getSnap()
3408        lzc.lzc_snapshot([snap])
3409
3410        with cleanup_fd() as fd:
3411            lzc.lzc_hold({snap: b'tag1'}, fd)
3412            lzc.lzc_hold({snap: b'tag2'}, fd)
3413
3414            lzc.lzc_destroy_snaps([snap], defer=True)
3415            self.assertExists(snap)
3416
3417            lzc.lzc_release({snap: [b'tag1']})
3418            self.assertExists(snap)
3419
3420            lzc.lzc_release({snap: [b'tag2']})
3421            self.assertNotExists(snap)
3422
3423    def test_release_hold_and_multiple_snap_destruction(self):
3424        snap = ZFSTest.pool.getRoot().getSnap()
3425        lzc.lzc_snapshot([snap])
3426
3427        with cleanup_fd() as fd:
3428            lzc.lzc_hold({snap: b'tag'}, fd)
3429
3430            lzc.lzc_destroy_snaps([snap], defer=True)
3431            self.assertExists(snap)
3432
3433            lzc.lzc_destroy_snaps([snap], defer=True)
3434            self.assertExists(snap)
3435
3436            lzc.lzc_release({snap: [b'tag']})
3437            self.assertNotExists(snap)
3438
3439    def test_release_hold_missing_tag(self):
3440        snap = ZFSTest.pool.getRoot().getSnap()
3441        lzc.lzc_snapshot([snap])
3442
3443        ret = lzc.lzc_release({snap: [b'tag']})
3444        self.assertEqual(len(ret), 1)
3445        self.assertEqual(ret[0], snap + b'#tag')
3446
3447    def test_release_hold_missing_snap(self):
3448        snap = ZFSTest.pool.getRoot().getSnap()
3449
3450        ret = lzc.lzc_release({snap: [b'tag']})
3451        self.assertEqual(len(ret), 1)
3452        self.assertEqual(ret[0], snap)
3453
3454    def test_release_hold_missing_snap_2(self):
3455        snap = ZFSTest.pool.getRoot().getSnap()
3456
3457        ret = lzc.lzc_release({snap: [b'tag', b'another']})
3458        self.assertEqual(len(ret), 1)
3459        self.assertEqual(ret[0], snap)
3460
3461    def test_release_hold_across_pools(self):
3462        snap1 = ZFSTest.pool.getRoot().getSnap()
3463        snap2 = ZFSTest.misc_pool.getRoot().getSnap()
3464        lzc.lzc_snapshot([snap1])
3465        lzc.lzc_snapshot([snap2])
3466
3467        with cleanup_fd() as fd:
3468            lzc.lzc_hold({snap1: b'tag'}, fd)
3469            lzc.lzc_hold({snap2: b'tag'}, fd)
3470            with self.assertRaises(lzc_exc.HoldReleaseFailure) as ctx:
3471                lzc.lzc_release({snap1: [b'tag'], snap2: [b'tag']})
3472        for e in ctx.exception.errors:
3473            self.assertIsInstance(e, lzc_exc.PoolsDiffer)
3474
3475    # Apparently the tag name is not verified,
3476    # only its existence is checked.
3477    @unittest.expectedFailure
3478    def test_release_hold_too_long_tag(self):
3479        snap = ZFSTest.pool.getRoot().getSnap()
3480        tag = b't' * 256
3481        lzc.lzc_snapshot([snap])
3482
3483        with self.assertRaises(lzc_exc.HoldReleaseFailure):
3484            lzc.lzc_release({snap: [tag]})
3485
3486    # Apparently the full snapshot name is not checked for length
3487    # and this snapshot is treated as simply missing.
3488    @unittest.expectedFailure
3489    def test_release_hold_too_long_snap_name(self):
3490        snap = ZFSTest.pool.getRoot().getTooLongSnap(False)
3491
3492        with self.assertRaises(lzc_exc.HoldReleaseFailure):
3493            lzc.lzc_release({snap: [b'tag']})
3494
3495    def test_release_hold_too_long_snap_name_2(self):
3496        snap = ZFSTest.pool.getRoot().getTooLongSnap(True)
3497        with self.assertRaises(lzc_exc.HoldReleaseFailure) as ctx:
3498            lzc.lzc_release({snap: [b'tag']})
3499        for e in ctx.exception.errors:
3500            self.assertIsInstance(e, lzc_exc.NameTooLong)
3501            self.assertEqual(e.name, snap)
3502
3503    def test_release_hold_invalid_snap_name(self):
3504        snap = ZFSTest.pool.getRoot().getSnap() + b'@bad'
3505        with self.assertRaises(lzc_exc.HoldReleaseFailure) as ctx:
3506            lzc.lzc_release({snap: [b'tag']})
3507        for e in ctx.exception.errors:
3508            self.assertIsInstance(e, lzc_exc.NameInvalid)
3509            self.assertEqual(e.name, snap)
3510
3511    def test_release_hold_invalid_snap_name_2(self):
3512        snap = ZFSTest.pool.getRoot().getFilesystem().getName()
3513        with self.assertRaises(lzc_exc.HoldReleaseFailure) as ctx:
3514            lzc.lzc_release({snap: [b'tag']})
3515        for e in ctx.exception.errors:
3516            self.assertIsInstance(e, lzc_exc.NameInvalid)
3517            self.assertEqual(e.name, snap)
3518
3519    def test_sync_missing_pool(self):
3520        pool = b"nonexistent"
3521        with self.assertRaises(lzc_exc.PoolNotFound):
3522            lzc.lzc_sync(pool)
3523
3524    def test_sync_pool_forced(self):
3525        pool = ZFSTest.pool.getRoot().getName()
3526        lzc.lzc_sync(pool, True)
3527
3528    def test_reopen_missing_pool(self):
3529        pool = b"nonexistent"
3530        with self.assertRaises(lzc_exc.PoolNotFound):
3531            lzc.lzc_reopen(pool)
3532
3533    def test_reopen_pool_no_restart(self):
3534        pool = ZFSTest.pool.getRoot().getName()
3535        lzc.lzc_reopen(pool, False)
3536
3537    def test_channel_program_missing_pool(self):
3538        pool = b"nonexistent"
3539        with self.assertRaises(lzc_exc.PoolNotFound):
3540            lzc.lzc_channel_program(pool, b"return {}")
3541
3542    def test_channel_program_timeout(self):
3543        pool = ZFSTest.pool.getRoot().getName()
3544        zcp = b"""
3545for i = 1,10000 do
3546    zfs.sync.snapshot('""" + pool + b"""@zcp' .. i)
3547end
3548"""
3549        with self.assertRaises(lzc_exc.ZCPTimeout):
3550            lzc.lzc_channel_program(pool, zcp, instrlimit=1)
3551
3552    def test_channel_program_memory_limit(self):
3553        pool = ZFSTest.pool.getRoot().getName()
3554        zcp = b"""
3555for i = 1,10000 do
3556    zfs.sync.snapshot('""" + pool + b"""@zcp' .. i)
3557end
3558"""
3559        with self.assertRaises(lzc_exc.ZCPSpaceError):
3560            lzc.lzc_channel_program(pool, zcp, memlimit=1)
3561
3562    def test_channel_program_invalid_limits(self):
3563        pool = ZFSTest.pool.getRoot().getName()
3564        zcp = b"""
3565return {}
3566"""
3567        with self.assertRaises(lzc_exc.ZCPLimitInvalid):
3568            lzc.lzc_channel_program(pool, zcp, instrlimit=0)
3569        with self.assertRaises(lzc_exc.ZCPLimitInvalid):
3570            lzc.lzc_channel_program(pool, zcp, memlimit=0)
3571
3572    def test_channel_program_syntax_error(self):
3573        pool = ZFSTest.pool.getRoot().getName()
3574        zcp = b"""
3575inv+val:id
3576"""
3577        with self.assertRaises(lzc_exc.ZCPSyntaxError) as ctx:
3578            lzc.lzc_channel_program(pool, zcp)
3579        self.assertTrue(b"syntax error" in ctx.exception.details)
3580
3581    def test_channel_program_sync_snapshot(self):
3582        pool = ZFSTest.pool.getRoot().getName()
3583        snapname = ZFSTest.pool.makeName(b"@zcp")
3584        zcp = b"""
3585zfs.sync.snapshot('""" + snapname + b"""')
3586"""
3587        lzc.lzc_channel_program(pool, zcp)
3588        self.assertExists(snapname)
3589
3590    def test_channel_program_runtime_error(self):
3591        pool = ZFSTest.pool.getRoot().getName()
3592
3593        # failing an assertion raises a runtime error
3594        with self.assertRaises(lzc_exc.ZCPRuntimeError) as ctx:
3595            lzc.lzc_channel_program(pool, b"assert(1 == 2)")
3596        self.assertTrue(
3597            b"assertion failed" in ctx.exception.details)
3598        # invoking the error() function raises a runtime error
3599        with self.assertRaises(lzc_exc.ZCPRuntimeError) as ctx:
3600            lzc.lzc_channel_program(pool, b"error()")
3601
3602    def test_channel_program_nosync_runtime_error(self):
3603        pool = ZFSTest.pool.getRoot().getName()
3604        zcp = b"""
3605zfs.sync.snapshot('""" + pool + b"""@zcp')
3606"""
3607        # lzc_channel_program_nosync() allows only "read-only" operations
3608        with self.assertRaises(lzc_exc.ZCPRuntimeError) as ctx:
3609            lzc.lzc_channel_program_nosync(pool, zcp)
3610        self.assertTrue(
3611            b"running functions from the zfs.sync" in ctx.exception.details)
3612
3613    def test_change_key_new(self):
3614        with encrypted_filesystem() as (fs, _):
3615            lzc.lzc_change_key(
3616                fs, 'new_key',
3617                props={b"keyformat": lzc.zfs_keyformat.ZFS_KEYFORMAT_RAW},
3618                key=os.urandom(lzc.WRAPPING_KEY_LEN))
3619
3620    def test_change_key_missing_fs(self):
3621        name = b"nonexistent"
3622
3623        with self.assertRaises(lzc_exc.FilesystemNotFound):
3624            lzc.lzc_change_key(
3625                name, 'new_key',
3626                props={b"keyformat": lzc.zfs_keyformat.ZFS_KEYFORMAT_RAW},
3627                key=os.urandom(lzc.WRAPPING_KEY_LEN))
3628
3629    def test_change_key_not_loaded(self):
3630        with encrypted_filesystem() as (fs, _):
3631            lzc.lzc_unload_key(fs)
3632            with self.assertRaises(lzc_exc.EncryptionKeyNotLoaded):
3633                lzc.lzc_change_key(
3634                    fs, 'new_key',
3635                    props={b"keyformat": lzc.zfs_keyformat.ZFS_KEYFORMAT_RAW},
3636                    key=os.urandom(lzc.WRAPPING_KEY_LEN))
3637
3638    def test_change_key_invalid_property(self):
3639        with encrypted_filesystem() as (fs, _):
3640            with self.assertRaises(lzc_exc.PropertyInvalid):
3641                lzc.lzc_change_key(fs, 'new_key', props={b"invalid": b"prop"})
3642
3643    def test_change_key_invalid_crypt_command(self):
3644        with encrypted_filesystem() as (fs, _):
3645            with self.assertRaises(lzc_exc.UnknownCryptCommand):
3646                lzc.lzc_change_key(fs, 'duplicate_key')
3647
3648    def test_load_key(self):
3649        with encrypted_filesystem() as (fs, key):
3650            lzc.lzc_unload_key(fs)
3651            lzc.lzc_load_key(fs, False, key)
3652
3653    def test_load_key_invalid(self):
3654        with encrypted_filesystem() as (fs, key):
3655            lzc.lzc_unload_key(fs)
3656            with self.assertRaises(lzc_exc.EncryptionKeyInvalid):
3657                lzc.lzc_load_key(fs, False, os.urandom(lzc.WRAPPING_KEY_LEN))
3658
3659    def test_load_key_already_loaded(self):
3660        with encrypted_filesystem() as (fs, key):
3661            lzc.lzc_unload_key(fs)
3662            lzc.lzc_load_key(fs, False, key)
3663            with self.assertRaises(lzc_exc.EncryptionKeyAlreadyLoaded):
3664                lzc.lzc_load_key(fs, False, key)
3665
3666    def test_load_key_missing_fs(self):
3667        name = b"nonexistent"
3668
3669        with self.assertRaises(lzc_exc.FilesystemNotFound):
3670            lzc.lzc_load_key(name, False, key=os.urandom(lzc.WRAPPING_KEY_LEN))
3671
3672    def test_unload_key(self):
3673        with encrypted_filesystem() as (fs, _):
3674            lzc.lzc_unload_key(fs)
3675
3676    def test_unload_key_missing_fs(self):
3677        name = b"nonexistent"
3678
3679        with self.assertRaises(lzc_exc.FilesystemNotFound):
3680            lzc.lzc_unload_key(name)
3681
3682    def test_unload_key_busy(self):
3683        with encrypted_filesystem() as (fs, _):
3684            with zfs_mount(fs):
3685                with self.assertRaises(lzc_exc.DatasetBusy):
3686                    lzc.lzc_unload_key(fs)
3687
3688    def test_unload_key_not_loaded(self):
3689        with encrypted_filesystem() as (fs, _):
3690            lzc.lzc_unload_key(fs)
3691            with self.assertRaises(lzc_exc.EncryptionKeyNotLoaded):
3692                lzc.lzc_unload_key(fs)
3693
3694    def test_checkpoint(self):
3695        pool = ZFSTest.pool.getRoot().getName()
3696
3697        lzc.lzc_pool_checkpoint(pool)
3698
3699    def test_checkpoint_missing_pool(self):
3700        pool = b"nonexistent"
3701
3702        with self.assertRaises(lzc_exc.PoolNotFound):
3703            lzc.lzc_pool_checkpoint(pool)
3704
3705    def test_checkpoint_already_exists(self):
3706        pool = ZFSTest.pool.getRoot().getName()
3707
3708        lzc.lzc_pool_checkpoint(pool)
3709        with self.assertRaises(lzc_exc.CheckpointExists):
3710            lzc.lzc_pool_checkpoint(pool)
3711
3712    def test_checkpoint_discard(self):
3713        pool = ZFSTest.pool.getRoot().getName()
3714
3715        lzc.lzc_pool_checkpoint(pool)
3716        lzc.lzc_pool_checkpoint_discard(pool)
3717
3718    def test_checkpoint_discard_missing_pool(self):
3719        pool = b"nonexistent"
3720
3721        with self.assertRaises(lzc_exc.PoolNotFound):
3722            lzc.lzc_pool_checkpoint_discard(pool)
3723
3724    def test_checkpoint_discard_missing_checkpoint(self):
3725        pool = ZFSTest.pool.getRoot().getName()
3726
3727        with self.assertRaises(lzc_exc.CheckpointNotFound):
3728            lzc.lzc_pool_checkpoint_discard(pool)
3729
3730    @needs_support(lzc.lzc_list_children)
3731    def test_list_children(self):
3732        name = ZFSTest.pool.makeName(b"fs1/fs")
3733        names = [ZFSTest.pool.makeName(b"fs1/fs/test1"),
3734                 ZFSTest.pool.makeName(b"fs1/fs/test2"),
3735                 ZFSTest.pool.makeName(b"fs1/fs/test3"), ]
3736        # and one snap to see that it is not listed
3737        snap = ZFSTest.pool.makeName(b"fs1/fs@test")
3738
3739        for fs in names:
3740            lzc.lzc_create(fs)
3741        lzc.lzc_snapshot([snap])
3742
3743        children = list(lzc.lzc_list_children(name))
3744        self.assertItemsEqual(children, names)
3745
3746    @needs_support(lzc.lzc_list_children)
3747    def test_list_children_nonexistent(self):
3748        fs = ZFSTest.pool.makeName(b"nonexistent")
3749
3750        with self.assertRaises(lzc_exc.DatasetNotFound):
3751            list(lzc.lzc_list_children(fs))
3752
3753    @needs_support(lzc.lzc_list_children)
3754    def test_list_children_of_snap(self):
3755        snap = ZFSTest.pool.makeName(b"@newsnap")
3756
3757        lzc.lzc_snapshot([snap])
3758        children = list(lzc.lzc_list_children(snap))
3759        self.assertEqual(children, [])
3760
3761    @needs_support(lzc.lzc_list_snaps)
3762    def test_list_snaps(self):
3763        name = ZFSTest.pool.makeName(b"fs1/fs")
3764        names = [ZFSTest.pool.makeName(b"fs1/fs@test1"),
3765                 ZFSTest.pool.makeName(b"fs1/fs@test2"),
3766                 ZFSTest.pool.makeName(b"fs1/fs@test3"), ]
3767        # and one filesystem to see that it is not listed
3768        fs = ZFSTest.pool.makeName(b"fs1/fs/test")
3769
3770        for snap in names:
3771            lzc.lzc_snapshot([snap])
3772        lzc.lzc_create(fs)
3773
3774        snaps = list(lzc.lzc_list_snaps(name))
3775        self.assertItemsEqual(snaps, names)
3776
3777    @needs_support(lzc.lzc_list_snaps)
3778    def test_list_snaps_nonexistent(self):
3779        fs = ZFSTest.pool.makeName(b"nonexistent")
3780
3781        with self.assertRaises(lzc_exc.DatasetNotFound):
3782            list(lzc.lzc_list_snaps(fs))
3783
3784    @needs_support(lzc.lzc_list_snaps)
3785    def test_list_snaps_of_snap(self):
3786        snap = ZFSTest.pool.makeName(b"@newsnap")
3787
3788        lzc.lzc_snapshot([snap])
3789        snaps = list(lzc.lzc_list_snaps(snap))
3790        self.assertEqual(snaps, [])
3791
3792    @needs_support(lzc.lzc_get_props)
3793    def test_get_fs_props(self):
3794        fs = ZFSTest.pool.makeName(b"new")
3795        props = {b"user:foo": b"bar"}
3796
3797        lzc.lzc_create(fs, props=props)
3798        actual_props = lzc.lzc_get_props(fs)
3799        self.assertDictContainsSubset(props, actual_props)
3800
3801    @needs_support(lzc.lzc_get_props)
3802    def test_get_fs_props_with_child(self):
3803        parent = ZFSTest.pool.makeName(b"parent")
3804        child = ZFSTest.pool.makeName(b"parent/child")
3805        parent_props = {b"user:foo": b"parent"}
3806        child_props = {b"user:foo": b"child"}
3807
3808        lzc.lzc_create(parent, props=parent_props)
3809        lzc.lzc_create(child, props=child_props)
3810        actual_parent_props = lzc.lzc_get_props(parent)
3811        actual_child_props = lzc.lzc_get_props(child)
3812        self.assertDictContainsSubset(parent_props, actual_parent_props)
3813        self.assertDictContainsSubset(child_props, actual_child_props)
3814
3815    @needs_support(lzc.lzc_get_props)
3816    def test_get_snap_props(self):
3817        snapname = ZFSTest.pool.makeName(b"@snap")
3818        snaps = [snapname]
3819        props = {b"user:foo": b"bar"}
3820
3821        lzc.lzc_snapshot(snaps, props)
3822        actual_props = lzc.lzc_get_props(snapname)
3823        self.assertDictContainsSubset(props, actual_props)
3824
3825    @needs_support(lzc.lzc_get_props)
3826    def test_get_props_nonexistent(self):
3827        fs = ZFSTest.pool.makeName(b"nonexistent")
3828
3829        with self.assertRaises(lzc_exc.DatasetNotFound):
3830            lzc.lzc_get_props(fs)
3831
3832    @needs_support(lzc.lzc_get_props)
3833    def test_get_mountpoint_none(self):
3834        '''
3835        If the *mountpoint* property is set to none, then its
3836        value is returned as `bytes` "none".
3837        Also, a child filesystem inherits that value.
3838        '''
3839        fs = ZFSTest.pool.makeName(b"new")
3840        child = ZFSTest.pool.makeName(b"new/child")
3841        props = {b"mountpoint": b"none"}
3842
3843        lzc.lzc_create(fs, props=props)
3844        lzc.lzc_create(child)
3845        actual_props = lzc.lzc_get_props(fs)
3846        self.assertDictContainsSubset(props, actual_props)
3847        # check that mountpoint value is correctly inherited
3848        child_props = lzc.lzc_get_props(child)
3849        self.assertDictContainsSubset(props, child_props)
3850
3851    @needs_support(lzc.lzc_get_props)
3852    def test_get_mountpoint_legacy(self):
3853        '''
3854        If the *mountpoint* property is set to legacy, then its
3855        value is returned as `bytes` "legacy".
3856        Also, a child filesystem inherits that value.
3857        '''
3858        fs = ZFSTest.pool.makeName(b"new")
3859        child = ZFSTest.pool.makeName(b"new/child")
3860        props = {b"mountpoint": b"legacy"}
3861
3862        lzc.lzc_create(fs, props=props)
3863        lzc.lzc_create(child)
3864        actual_props = lzc.lzc_get_props(fs)
3865        self.assertDictContainsSubset(props, actual_props)
3866        # check that mountpoint value is correctly inherited
3867        child_props = lzc.lzc_get_props(child)
3868        self.assertDictContainsSubset(props, child_props)
3869
3870    @needs_support(lzc.lzc_get_props)
3871    def test_get_mountpoint_path(self):
3872        '''
3873        If the *mountpoint* property is set to a path and the property
3874        is not explicitly set on a child filesystem, then its
3875        value is that of the parent filesystem with the child's
3876        name appended using the '/' separator.
3877        '''
3878        fs = ZFSTest.pool.makeName(b"new")
3879        child = ZFSTest.pool.makeName(b"new/child")
3880        props = {b"mountpoint": b"/mnt"}
3881
3882        lzc.lzc_create(fs, props=props)
3883        lzc.lzc_create(child)
3884        actual_props = lzc.lzc_get_props(fs)
3885        self.assertDictContainsSubset(props, actual_props)
3886        # check that mountpoint value is correctly inherited
3887        child_props = lzc.lzc_get_props(child)
3888        self.assertDictContainsSubset(
3889            {b"mountpoint": b"/mnt/child"}, child_props)
3890
3891    @needs_support(lzc.lzc_get_props)
3892    def test_get_snap_clones(self):
3893        fs = ZFSTest.pool.makeName(b"new")
3894        snap = ZFSTest.pool.makeName(b"@snap")
3895        clone1 = ZFSTest.pool.makeName(b"clone1")
3896        clone2 = ZFSTest.pool.makeName(b"clone2")
3897
3898        lzc.lzc_create(fs)
3899        lzc.lzc_snapshot([snap])
3900        lzc.lzc_clone(clone1, snap)
3901        lzc.lzc_clone(clone2, snap)
3902
3903        clones_prop = lzc.lzc_get_props(snap)["clones"]
3904        self.assertItemsEqual(clones_prop, [clone1, clone2])
3905
3906    @needs_support(lzc.lzc_rename)
3907    def test_rename(self):
3908        src = ZFSTest.pool.makeName(b"source")
3909        tgt = ZFSTest.pool.makeName(b"target")
3910
3911        lzc.lzc_create(src)
3912        lzc.lzc_rename(src, tgt)
3913        self.assertNotExists(src)
3914        self.assertExists(tgt)
3915
3916    @needs_support(lzc.lzc_rename)
3917    def test_rename_nonexistent(self):
3918        src = ZFSTest.pool.makeName(b"source")
3919        tgt = ZFSTest.pool.makeName(b"target")
3920
3921        with self.assertRaises(lzc_exc.FilesystemNotFound):
3922            lzc.lzc_rename(src, tgt)
3923
3924    @needs_support(lzc.lzc_rename)
3925    def test_rename_existing_target(self):
3926        src = ZFSTest.pool.makeName(b"source")
3927        tgt = ZFSTest.pool.makeName(b"target")
3928
3929        lzc.lzc_create(src)
3930        lzc.lzc_create(tgt)
3931        with self.assertRaises(lzc_exc.FilesystemExists):
3932            lzc.lzc_rename(src, tgt)
3933
3934    @needs_support(lzc.lzc_rename)
3935    def test_rename_nonexistent_target_parent(self):
3936        src = ZFSTest.pool.makeName(b"source")
3937        tgt = ZFSTest.pool.makeName(b"parent/target")
3938
3939        lzc.lzc_create(src)
3940        with self.assertRaises(lzc_exc.FilesystemNotFound):
3941            lzc.lzc_rename(src, tgt)
3942
3943    @needs_support(lzc.lzc_rename)
3944    def test_rename_parent_is_zvol(self):
3945        src = ZFSTest.pool.makeName(b"source")
3946        zvol = ZFSTest.pool.makeName(b"parent")
3947        tgt = zvol + b"/target"
3948        props = {b"volsize": 1024 * 1024}
3949
3950        lzc.lzc_create(src)
3951        lzc.lzc_create(zvol, ds_type='zvol', props=props)
3952        with self.assertRaises(lzc_exc.WrongParent):
3953            lzc.lzc_rename(src, tgt)
3954
3955    @needs_support(lzc.lzc_destroy)
3956    def test_destroy(self):
3957        fs = ZFSTest.pool.makeName(b"test-fs")
3958
3959        lzc.lzc_create(fs)
3960        lzc.lzc_destroy(fs)
3961        self.assertNotExists(fs)
3962
3963    @needs_support(lzc.lzc_destroy)
3964    def test_destroy_nonexistent(self):
3965        fs = ZFSTest.pool.makeName(b"test-fs")
3966
3967        with self.assertRaises(lzc_exc.FilesystemNotFound):
3968            lzc.lzc_destroy(fs)
3969
3970    @needs_support(lzc.lzc_inherit_prop)
3971    def test_inherit_prop(self):
3972        parent = ZFSTest.pool.makeName(b"parent")
3973        child = ZFSTest.pool.makeName(b"parent/child")
3974        the_prop = b"user:foo"
3975        parent_props = {the_prop: b"parent"}
3976        child_props = {the_prop: b"child"}
3977
3978        lzc.lzc_create(parent, props=parent_props)
3979        lzc.lzc_create(child, props=child_props)
3980        lzc.lzc_inherit_prop(child, the_prop)
3981        actual_props = lzc.lzc_get_props(child)
3982        self.assertDictContainsSubset(parent_props, actual_props)
3983
3984    @needs_support(lzc.lzc_inherit_prop)
3985    def test_inherit_missing_prop(self):
3986        parent = ZFSTest.pool.makeName(b"parent")
3987        child = ZFSTest.pool.makeName(b"parent/child")
3988        the_prop = "user:foo"
3989        child_props = {the_prop: "child"}
3990
3991        lzc.lzc_create(parent)
3992        lzc.lzc_create(child, props=child_props)
3993        lzc.lzc_inherit_prop(child, the_prop)
3994        actual_props = lzc.lzc_get_props(child)
3995        self.assertNotIn(the_prop, actual_props)
3996
3997    @needs_support(lzc.lzc_inherit_prop)
3998    def test_inherit_readonly_prop(self):
3999        parent = ZFSTest.pool.makeName(b"parent")
4000        child = ZFSTest.pool.makeName(b"parent/child")
4001        the_prop = b"createtxg"
4002
4003        lzc.lzc_create(parent)
4004        lzc.lzc_create(child)
4005        with self.assertRaises(lzc_exc.PropertyInvalid):
4006            lzc.lzc_inherit_prop(child, the_prop)
4007
4008    @needs_support(lzc.lzc_inherit_prop)
4009    def test_inherit_unknown_prop(self):
4010        parent = ZFSTest.pool.makeName(b"parent")
4011        child = ZFSTest.pool.makeName(b"parent/child")
4012        the_prop = b"nosuchprop"
4013
4014        lzc.lzc_create(parent)
4015        lzc.lzc_create(child)
4016        with self.assertRaises(lzc_exc.PropertyInvalid):
4017            lzc.lzc_inherit_prop(child, the_prop)
4018
4019    @needs_support(lzc.lzc_inherit_prop)
4020    def test_inherit_prop_on_snap(self):
4021        fs = ZFSTest.pool.makeName(b"new")
4022        snapname = ZFSTest.pool.makeName(b"new@snap")
4023        prop = b"user:foo"
4024        fs_val = b"fs"
4025        snap_val = b"snap"
4026
4027        lzc.lzc_create(fs, props={prop: fs_val})
4028        lzc.lzc_snapshot([snapname], props={prop: snap_val})
4029
4030        actual_props = lzc.lzc_get_props(snapname)
4031        self.assertDictContainsSubset({prop: snap_val}, actual_props)
4032
4033        lzc.lzc_inherit_prop(snapname, prop)
4034        actual_props = lzc.lzc_get_props(snapname)
4035        self.assertDictContainsSubset({prop: fs_val}, actual_props)
4036
4037    @needs_support(lzc.lzc_set_prop)
4038    def test_set_fs_prop(self):
4039        fs = ZFSTest.pool.makeName(b"new")
4040        prop = b"user:foo"
4041        val = b"bar"
4042
4043        lzc.lzc_create(fs)
4044        lzc.lzc_set_prop(fs, prop, val)
4045        actual_props = lzc.lzc_get_props(fs)
4046        self.assertDictContainsSubset({prop: val}, actual_props)
4047
4048    @needs_support(lzc.lzc_set_prop)
4049    def test_set_snap_prop(self):
4050        snapname = ZFSTest.pool.makeName(b"@snap")
4051        prop = b"user:foo"
4052        val = b"bar"
4053
4054        lzc.lzc_snapshot([snapname])
4055        lzc.lzc_set_prop(snapname, prop, val)
4056        actual_props = lzc.lzc_get_props(snapname)
4057        self.assertDictContainsSubset({prop: val}, actual_props)
4058
4059    @needs_support(lzc.lzc_set_prop)
4060    def test_set_prop_nonexistent(self):
4061        fs = ZFSTest.pool.makeName(b"nonexistent")
4062        prop = b"user:foo"
4063        val = b"bar"
4064
4065        with self.assertRaises(lzc_exc.DatasetNotFound):
4066            lzc.lzc_set_prop(fs, prop, val)
4067
4068    @needs_support(lzc.lzc_set_prop)
4069    def test_set_sys_prop(self):
4070        fs = ZFSTest.pool.makeName(b"new")
4071        prop = b"recordsize"
4072        val = 4096
4073
4074        lzc.lzc_create(fs)
4075        lzc.lzc_set_prop(fs, prop, val)
4076        actual_props = lzc.lzc_get_props(fs)
4077        self.assertDictContainsSubset({prop: val}, actual_props)
4078
4079    @needs_support(lzc.lzc_set_prop)
4080    def test_set_invalid_prop(self):
4081        fs = ZFSTest.pool.makeName(b"new")
4082        prop = b"nosuchprop"
4083        val = 0
4084
4085        lzc.lzc_create(fs)
4086        with self.assertRaises(lzc_exc.PropertyInvalid):
4087            lzc.lzc_set_prop(fs, prop, val)
4088
4089    @needs_support(lzc.lzc_set_prop)
4090    def test_set_invalid_value_prop(self):
4091        fs = ZFSTest.pool.makeName(b"new")
4092        prop = b"atime"
4093        val = 100
4094
4095        lzc.lzc_create(fs)
4096        with self.assertRaises(lzc_exc.PropertyInvalid):
4097            lzc.lzc_set_prop(fs, prop, val)
4098
4099    @needs_support(lzc.lzc_set_prop)
4100    def test_set_invalid_value_prop_2(self):
4101        fs = ZFSTest.pool.makeName(b"new")
4102        prop = b"readonly"
4103        val = 100
4104
4105        lzc.lzc_create(fs)
4106        with self.assertRaises(lzc_exc.PropertyInvalid):
4107            lzc.lzc_set_prop(fs, prop, val)
4108
4109    @needs_support(lzc.lzc_set_prop)
4110    def test_set_prop_too_small_quota(self):
4111        fs = ZFSTest.pool.makeName(b"new")
4112        prop = b"refquota"
4113        val = 1
4114
4115        lzc.lzc_create(fs)
4116        with self.assertRaises(lzc_exc.NoSpace):
4117            lzc.lzc_set_prop(fs, prop, val)
4118
4119    @needs_support(lzc.lzc_set_prop)
4120    def test_set_readonly_prop(self):
4121        fs = ZFSTest.pool.makeName(b"new")
4122        prop = b"creation"
4123        val = 0
4124
4125        lzc.lzc_create(fs)
4126        lzc.lzc_set_prop(fs, prop, val)
4127        actual_props = lzc.lzc_get_props(fs)
4128        # the change is silently ignored
4129        self.assertTrue(actual_props[prop] != val)
4130
4131
4132class _TempPool(object):
4133    SNAPSHOTS = [b'snap', b'snap1', b'snap2']
4134    BOOKMARKS = [b'bmark', b'bmark1', b'bmark2']
4135
4136    _cachefile_suffix = ".cachefile"
4137
4138    # XXX Whether to do a sloppy but much faster cleanup
4139    # or a proper but slower one.
4140    _recreate_pools = True
4141
4142    def __init__(self, size=128 * 1024 * 1024, readonly=False, filesystems=[]):
4143        self._filesystems = filesystems
4144        self._readonly = readonly
4145        if sys.version_info < (3, 0):
4146            self._pool_name = b'pool.' + bytes(uuid.uuid4())
4147        else:
4148            self._pool_name = b'pool.' + bytes(str(uuid.uuid4()),
4149                                               encoding='utf-8')
4150        self._root = _Filesystem(self._pool_name)
4151        (fd, self._pool_file_path) = tempfile.mkstemp(
4152            suffix='.zpool', prefix='tmp-')
4153        if readonly:
4154            cachefile = self._pool_file_path + _TempPool._cachefile_suffix
4155        else:
4156            cachefile = 'none'
4157        self._zpool_create = [
4158            'zpool', 'create', '-o', 'cachefile=' + cachefile,
4159            '-O', 'mountpoint=legacy', '-O', 'compression=off',
4160            self._pool_name, self._pool_file_path]
4161        try:
4162            os.ftruncate(fd, size)
4163            os.close(fd)
4164
4165            subprocess.check_output(
4166                self._zpool_create, stderr=subprocess.STDOUT)
4167
4168            for fs in filesystems:
4169                lzc.lzc_create(self.makeName(fs))
4170
4171            self._bmarks_supported = self.isPoolFeatureEnabled('bookmarks')
4172
4173            if readonly:
4174                # To make a pool read-only it must exported and re-imported
4175                # with readonly option.
4176                # The most deterministic way to re-import the pool is by using
4177                # a cache file.
4178                # But the cache file has to be stashed away before the pool is
4179                # exported, because otherwise the pool is removed from the
4180                # cache.
4181                shutil.copyfile(cachefile, cachefile + '.tmp')
4182                subprocess.check_output(
4183                    ['zpool', 'export', '-f', self._pool_name],
4184                    stderr=subprocess.STDOUT)
4185                os.rename(cachefile + '.tmp', cachefile)
4186                subprocess.check_output(
4187                    ['zpool', 'import', '-f', '-N', '-c', cachefile,
4188                        '-o', 'readonly=on', self._pool_name],
4189                    stderr=subprocess.STDOUT)
4190                os.remove(cachefile)
4191
4192        except subprocess.CalledProcessError as e:
4193            self.cleanUp()
4194            if b'permission denied' in e.output:
4195                raise unittest.SkipTest(
4196                    'insufficient privileges to run libzfs_core tests')
4197            print('command failed: ', e.output)
4198            raise
4199        except Exception:
4200            self.cleanUp()
4201            raise
4202
4203    def reset(self):
4204        if self._readonly:
4205            return
4206
4207        if not self.__class__._recreate_pools:
4208            snaps = []
4209            for fs in [''] + self._filesystems:
4210                for snap in self.__class__.SNAPSHOTS:
4211                    snaps.append(self.makeName(fs + '@' + snap))
4212            self.getRoot().visitSnaps(lambda snap: snaps.append(snap))
4213            lzc.lzc_destroy_snaps(snaps, defer=False)
4214
4215            if self._bmarks_supported:
4216                bmarks = []
4217                for fs in [''] + self._filesystems:
4218                    for bmark in self.__class__.BOOKMARKS:
4219                        bmarks.append(self.makeName(fs + '#' + bmark))
4220                self.getRoot().visitBookmarks(
4221                    lambda bmark: bmarks.append(bmark))
4222                lzc.lzc_destroy_bookmarks(bmarks)
4223            self.getRoot().reset()
4224            return
4225
4226        # On the Buildbot builders this may fail with "pool is busy"
4227        # Retry 5 times before raising an error
4228        retry = 0
4229        while True:
4230            try:
4231                subprocess.check_output(
4232                    ['zpool', 'destroy', '-f', self._pool_name],
4233                    stderr=subprocess.STDOUT)
4234                subprocess.check_output(
4235                    self._zpool_create, stderr=subprocess.STDOUT)
4236                break
4237            except subprocess.CalledProcessError as e:
4238                if b'pool is busy' in e.output and retry < 5:
4239                    retry += 1
4240                    time.sleep(1)
4241                    continue
4242                else:
4243                    print('command failed: ', e.output)
4244                    raise
4245        for fs in self._filesystems:
4246            lzc.lzc_create(self.makeName(fs))
4247        self.getRoot().reset()
4248
4249    def cleanUp(self):
4250        try:
4251            subprocess.check_output(
4252                ['zpool', 'destroy', '-f', self._pool_name],
4253                stderr=subprocess.STDOUT)
4254        except Exception:
4255            pass
4256        try:
4257            os.remove(self._pool_file_path)
4258        except Exception:
4259            pass
4260        try:
4261            os.remove(self._pool_file_path + _TempPool._cachefile_suffix)
4262        except Exception:
4263            pass
4264        try:
4265            os.remove(
4266                self._pool_file_path + _TempPool._cachefile_suffix + '.tmp')
4267        except Exception:
4268            pass
4269
4270    def makeName(self, relative=None):
4271        if not relative:
4272            return self._pool_name
4273        if relative.startswith((b'@', b'#')):
4274            return self._pool_name + relative
4275        return self._pool_name + b'/' + relative
4276
4277    def makeTooLongName(self, prefix=None):
4278        if not prefix:
4279            prefix = b'x'
4280        prefix = self.makeName(prefix)
4281        pad_len = lzc.MAXNAMELEN + 1 - len(prefix)
4282        if pad_len > 0:
4283            return prefix + b'x' * pad_len
4284        else:
4285            return prefix
4286
4287    def makeTooLongComponent(self, prefix=None):
4288        padding = b'x' * (lzc.MAXNAMELEN + 1)
4289        if not prefix:
4290            prefix = padding
4291        else:
4292            prefix = prefix + padding
4293        return self.makeName(prefix)
4294
4295    def getRoot(self):
4296        return self._root
4297
4298    def getFilesystem(self, fsname):
4299        return _Filesystem(self._pool_name + b'/' + fsname)
4300
4301    def isPoolFeatureAvailable(self, feature):
4302        output = subprocess.check_output(
4303            ['zpool', 'get', '-H', 'feature@' + feature, self._pool_name])
4304        output = output.strip()
4305        return output != ''
4306
4307    def isPoolFeatureEnabled(self, feature):
4308        output = subprocess.check_output(
4309            ['zpool', 'get', '-H', 'feature@' + feature, self._pool_name])
4310        output = output.split()[2]
4311        return output in [b'active', b'enabled']
4312
4313
4314class _Filesystem(object):
4315
4316    def __init__(self, name):
4317        self._name = name
4318        self.reset()
4319
4320    def getName(self):
4321        return self._name
4322
4323    def reset(self):
4324        self._children = []
4325        self._fs_id = 0
4326        self._snap_id = 0
4327        self._bmark_id = 0
4328
4329    def getFilesystem(self):
4330        self._fs_id += 1
4331        fsname = self._name + b'/fs' + str(self._fs_id).encode()
4332        fs = _Filesystem(fsname)
4333        self._children.append(fs)
4334        return fs
4335
4336    def getProperty(self, propname, received=False):
4337        if received:
4338            output = subprocess.check_output(
4339                ['zfs', 'get', '-pH', '-o', 'received', propname, self._name])
4340        else:
4341            output = subprocess.check_output(
4342                ['zfs', 'get', '-pH', '-o', 'value', propname, self._name])
4343        return output.strip()
4344
4345    def _makeSnapName(self, i):
4346        return self._name + b'@snap' + str(i).encode()
4347
4348    def getSnap(self):
4349        self._snap_id += 1
4350        return self._makeSnapName(self._snap_id)
4351
4352    def _makeBookmarkName(self, i):
4353        return self._name + b'#bmark' + bytes(i)
4354
4355    def getBookmark(self):
4356        self._bmark_id += 1
4357        return self._makeBookmarkName(self._bmark_id)
4358
4359    def _makeTooLongName(self, too_long_component):
4360        if too_long_component:
4361            return b'x' * (lzc.MAXNAMELEN + 1)
4362
4363        # Note that another character is used for one of '/', '@', '#'.
4364        comp_len = lzc.MAXNAMELEN - len(self._name)
4365        if comp_len > 0:
4366            return b'x' * comp_len
4367        else:
4368            return b'x'
4369
4370    def getTooLongFilesystemName(self, too_long_component):
4371        return self._name + b'/' + self._makeTooLongName(too_long_component)
4372
4373    def getTooLongSnap(self, too_long_component):
4374        return self._name + b'@' + self._makeTooLongName(too_long_component)
4375
4376    def getTooLongBookmark(self, too_long_component):
4377        return self._name + b'#' + self._makeTooLongName(too_long_component)
4378
4379    def _visitFilesystems(self, visitor):
4380        for child in self._children:
4381            child._visitFilesystems(visitor)
4382        visitor(self)
4383
4384    def visitFilesystems(self, visitor):
4385        def _fsVisitor(fs):
4386            visitor(fs._name)
4387
4388        self._visitFilesystems(_fsVisitor)
4389
4390    def visitSnaps(self, visitor):
4391        def _snapVisitor(fs):
4392            for i in range(1, fs._snap_id + 1):
4393                visitor(fs._makeSnapName(i))
4394
4395        self._visitFilesystems(_snapVisitor)
4396
4397    def visitBookmarks(self, visitor):
4398        def _bmarkVisitor(fs):
4399            for i in range(1, fs._bmark_id + 1):
4400                visitor(fs._makeBookmarkName(i))
4401
4402        self._visitFilesystems(_bmarkVisitor)
4403
4404
4405# vim: softtabstop=4 tabstop=4 expandtab shiftwidth=4
4406