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