xref: /freebsd/sys/contrib/openzfs/contrib/pyzfs/libzfs_core/_error_translation.py (revision 61145dc2b94f12f6a47344fb9aac702321880e43)
1# SPDX-License-Identifier: Apache-2.0
2#
3# Copyright 2015 ClusterHQ
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#    http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17
18"""
19Helper routines for converting ``errno`` style error codes from C functions
20to Python exceptions defined by `libzfs_core` API.
21
22The conversion heavily depends on the context of the error: the attempted
23operation and the input parameters.  For this reason, there is a conversion
24routine for each `libzfs_core` interface function. The conversion routines
25have the return code as a parameter as well as all the parameters of the
26corresponding interface functions.
27
28The parameters and exceptions are documented in the `libzfs_core` interfaces.
29"""
30from __future__ import absolute_import, division, print_function
31
32import errno
33import re
34import string
35from . import exceptions as lzc_exc
36from ._constants import (
37    ECHRNG,
38    ECKSUM,
39    ETIME,
40    MAXNAMELEN,
41    ZFS_ERR_CHECKPOINT_EXISTS,
42    ZFS_ERR_DISCARDING_CHECKPOINT,
43    ZFS_ERR_NO_CHECKPOINT,
44    ZFS_ERR_DEVRM_IN_PROGRESS,
45    ZFS_ERR_VDEV_TOO_BIG,
46    ZFS_ERR_WRONG_PARENT,
47    ZFS_ERR_RAIDZ_EXPAND_IN_PROGRESS,
48    zfs_errno
49)
50
51
52def lzc_create_translate_error(ret, name, ds_type, props):
53    if ret == 0:
54        return
55    if ret == errno.EINVAL:
56        _validate_fs_name(name)
57        raise lzc_exc.PropertyInvalid(name)
58    if ret == errno.EEXIST:
59        raise lzc_exc.FilesystemExists(name)
60    if ret == errno.ENOENT:
61        raise lzc_exc.ParentNotFound(name)
62    if ret == ZFS_ERR_WRONG_PARENT:
63        raise lzc_exc.WrongParent(_fs_name(name))
64    if ret == zfs_errno.ZFS_ERR_BADPROP:
65        raise lzc_exc.PropertyInvalid(name)
66    raise _generic_exception(ret, name, "Failed to create filesystem")
67
68
69def lzc_clone_translate_error(ret, name, origin, props):
70    if ret == 0:
71        return
72    if ret == errno.EINVAL:
73        _validate_fs_name(name)
74        _validate_snap_name(origin)
75        raise lzc_exc.PropertyInvalid(name)
76    if ret == errno.EXDEV:
77        raise lzc_exc.PoolsDiffer(name)
78    if ret == errno.EEXIST:
79        raise lzc_exc.FilesystemExists(name)
80    if ret == errno.ENOENT:
81        if not _is_valid_snap_name(origin):
82            raise lzc_exc.SnapshotNameInvalid(origin)
83        raise lzc_exc.DatasetNotFound(name)
84    raise _generic_exception(ret, name, "Failed to create clone")
85
86
87def lzc_rollback_translate_error(ret, name):
88    if ret == 0:
89        return
90    if ret == errno.ESRCH:
91        raise lzc_exc.SnapshotNotFound(name)
92    if ret == errno.EINVAL:
93        _validate_fs_name(name)
94        raise lzc_exc.NameInvalid(name)
95    if ret == errno.ENOENT:
96        if not _is_valid_fs_name(name):
97            raise lzc_exc.NameInvalid(name)
98        else:
99            raise lzc_exc.FilesystemNotFound(name)
100    raise _generic_exception(ret, name, "Failed to rollback")
101
102
103def lzc_rollback_to_translate_error(ret, name, snap):
104    if ret == errno.EEXIST:
105        raise lzc_exc.SnapshotNotLatest(snap)
106    else:
107        lzc_rollback_translate_error(ret, name)
108
109
110def lzc_snapshot_translate_errors(ret, errlist, snaps, props):
111    if ret == 0:
112        return
113
114    def _map(ret, name):
115        if ret == errno.EXDEV:
116            pool_names = iter(map(_pool_name, snaps))
117            pool_name = next(pool_names, None)
118            same_pool = all(x == pool_name for x in pool_names)
119            if same_pool:
120                return lzc_exc.DuplicateSnapshots(name)
121            else:
122                return lzc_exc.PoolsDiffer(name)
123        elif ret == errno.EINVAL:
124            if any(not _is_valid_snap_name(s) for s in snaps):
125                return lzc_exc.NameInvalid(name)
126            elif any(len(s) > MAXNAMELEN for s in snaps):
127                return lzc_exc.NameTooLong(name)
128            else:
129                return lzc_exc.PropertyInvalid(name)
130
131        if ret == errno.EEXIST:
132            return lzc_exc.SnapshotExists(name)
133        if ret == errno.ENOENT:
134            return lzc_exc.FilesystemNotFound(name)
135        return _generic_exception(ret, name, "Failed to create snapshot")
136
137    _handle_err_list(ret, errlist, snaps, lzc_exc.SnapshotFailure, _map)
138
139
140def lzc_destroy_snaps_translate_errors(ret, errlist, snaps, defer):
141    if ret == 0:
142        return
143
144    def _map(ret, name):
145        if ret == errno.EEXIST:
146            return lzc_exc.SnapshotIsCloned(name)
147        if ret == errno.ENOENT:
148            return lzc_exc.PoolNotFound(name)
149        if ret == errno.EBUSY:
150            return lzc_exc.SnapshotIsHeld(name)
151        return _generic_exception(ret, name, "Failed to destroy snapshot")
152
153    _handle_err_list(
154        ret, errlist, snaps, lzc_exc.SnapshotDestructionFailure, _map)
155
156
157def lzc_bookmark_translate_errors(ret, errlist, bookmarks):
158
159    if ret == 0:
160        return
161
162    def _map(ret, name):
163        source = bookmarks[name]
164        if ret == errno.EINVAL:
165            if name:
166                pool_names = map(_pool_name, bookmarks.keys())
167
168                # use _validate* functions for MAXNAMELEN check
169                try:
170                    _validate_bmark_name(name)
171                except lzc_exc.ZFSError as e:
172                    return e
173
174                try:
175                    _validate_snap_name(source)
176                    source_is_snap = True
177                except lzc_exc.ZFSError:
178                    source_is_snap = False
179                try:
180                    _validate_bmark_name(source)
181                    source_is_bmark = True
182                except lzc_exc.ZFSError:
183                    source_is_bmark = False
184                if not source_is_snap and not source_is_bmark:
185                    return lzc_exc.BookmarkSourceInvalid(source)
186
187                if any(x != _pool_name(name) for x in pool_names):
188                    return lzc_exc.PoolsDiffer(name)
189            else:
190                invalid_names = [
191                    b for b in bookmarks.keys() if not _is_valid_bmark_name(b)]
192                if invalid_names:
193                    return lzc_exc.BookmarkNameInvalid(invalid_names[0])
194        if ret == errno.EEXIST:
195            return lzc_exc.BookmarkExists(name)
196        if ret == errno.ENOENT:
197            return lzc_exc.SnapshotNotFound(name)
198        if ret == errno.ENOTSUP:
199            return lzc_exc.BookmarkNotSupported(name)
200        if ret == zfs_errno.ZFS_ERR_BOOKMARK_SOURCE_NOT_ANCESTOR:
201            return lzc_exc.BookmarkMismatch(source)
202        return _generic_exception(ret, name, "Failed to create bookmark")
203
204    _handle_err_list(
205        ret, errlist, bookmarks.keys(), lzc_exc.BookmarkFailure, _map)
206
207
208def lzc_get_bookmarks_translate_error(ret, fsname, props):
209    if ret == 0:
210        return
211    if ret == errno.ENOENT:
212        raise lzc_exc.FilesystemNotFound(fsname)
213    raise _generic_exception(ret, fsname, "Failed to list bookmarks")
214
215
216def lzc_destroy_bookmarks_translate_errors(ret, errlist, bookmarks):
217    if ret == 0:
218        return
219
220    def _map(ret, name):
221        if ret == errno.EINVAL:
222            return lzc_exc.NameInvalid(name)
223        return _generic_exception(ret, name, "Failed to destroy bookmark")
224
225    _handle_err_list(
226        ret, errlist, bookmarks, lzc_exc.BookmarkDestructionFailure, _map)
227
228
229def lzc_snaprange_space_translate_error(ret, firstsnap, lastsnap):
230    if ret == 0:
231        return
232    if ret == errno.EXDEV and firstsnap is not None:
233        if _pool_name(firstsnap) != _pool_name(lastsnap):
234            raise lzc_exc.PoolsDiffer(lastsnap)
235        else:
236            raise lzc_exc.SnapshotMismatch(lastsnap)
237    if ret == errno.EINVAL:
238        if not _is_valid_snap_name(firstsnap):
239            raise lzc_exc.NameInvalid(firstsnap)
240        elif not _is_valid_snap_name(lastsnap):
241            raise lzc_exc.NameInvalid(lastsnap)
242        elif len(firstsnap) > MAXNAMELEN:
243            raise lzc_exc.NameTooLong(firstsnap)
244        elif len(lastsnap) > MAXNAMELEN:
245            raise lzc_exc.NameTooLong(lastsnap)
246        elif _pool_name(firstsnap) != _pool_name(lastsnap):
247            raise lzc_exc.PoolsDiffer(lastsnap)
248        else:
249            raise lzc_exc.SnapshotMismatch(lastsnap)
250    if ret == errno.ENOENT:
251        raise lzc_exc.SnapshotNotFound(lastsnap)
252    raise _generic_exception(
253        ret, lastsnap, "Failed to calculate space used by range of snapshots")
254
255
256def lzc_hold_translate_errors(ret, errlist, holds, fd):
257    if ret == 0:
258        return
259
260    def _map(ret, name):
261        if ret == errno.EXDEV:
262            return lzc_exc.PoolsDiffer(name)
263        elif ret == errno.EINVAL:
264            if name:
265                pool_names = map(_pool_name, holds.keys())
266                if not _is_valid_snap_name(name):
267                    return lzc_exc.NameInvalid(name)
268                elif len(name) > MAXNAMELEN:
269                    return lzc_exc.NameTooLong(name)
270                elif any(x != _pool_name(name) for x in pool_names):
271                    return lzc_exc.PoolsDiffer(name)
272            else:
273                invalid_names = [
274                    b for b in holds.keys() if not _is_valid_snap_name(b)]
275                if invalid_names:
276                    return lzc_exc.NameInvalid(invalid_names[0])
277        fs_name = None
278        hold_name = None
279        pool_name = None
280        if name is not None:
281            fs_name = _fs_name(name)
282            pool_name = _pool_name(name)
283            hold_name = holds[name]
284        if ret == errno.ENOENT:
285            return lzc_exc.FilesystemNotFound(fs_name)
286        if ret == errno.EEXIST:
287            return lzc_exc.HoldExists(name)
288        if ret == errno.E2BIG:
289            return lzc_exc.NameTooLong(hold_name)
290        if ret == errno.ENOTSUP:
291            return lzc_exc.FeatureNotSupported(pool_name)
292        return _generic_exception(ret, name, "Failed to hold snapshot")
293
294    if ret == errno.EBADF:
295        raise lzc_exc.BadHoldCleanupFD()
296    _handle_err_list(ret, errlist, holds.keys(), lzc_exc.HoldFailure, _map)
297
298
299def lzc_release_translate_errors(ret, errlist, holds):
300    if ret == 0:
301        return
302    for snap in holds:
303        hold_list = holds[snap]
304        if not isinstance(hold_list, list):
305            raise lzc_exc.TypeError('holds must be in a list')
306
307    def _map(ret, name):
308        if ret == errno.EXDEV:
309            return lzc_exc.PoolsDiffer(name)
310        elif ret == errno.EINVAL:
311            if name:
312                pool_names = map(_pool_name, holds.keys())
313                if not _is_valid_snap_name(name):
314                    return lzc_exc.NameInvalid(name)
315                elif len(name) > MAXNAMELEN:
316                    return lzc_exc.NameTooLong(name)
317                elif any(x != _pool_name(name) for x in pool_names):
318                    return lzc_exc.PoolsDiffer(name)
319            else:
320                invalid_names = [
321                    b for b in holds.keys() if not _is_valid_snap_name(b)]
322                if invalid_names:
323                    return lzc_exc.NameInvalid(invalid_names[0])
324        elif ret == errno.ENOENT:
325            return lzc_exc.HoldNotFound(name)
326        elif ret == errno.E2BIG:
327            tag_list = holds[name]
328            too_long_tags = [t for t in tag_list if len(t) > MAXNAMELEN]
329            return lzc_exc.NameTooLong(too_long_tags[0])
330        elif ret == errno.ENOTSUP:
331            pool_name = None
332            if name is not None:
333                pool_name = _pool_name(name)
334            return lzc_exc.FeatureNotSupported(pool_name)
335        else:
336            return _generic_exception(
337                ret, name, "Failed to release snapshot hold")
338
339    _handle_err_list(
340        ret, errlist, holds.keys(), lzc_exc.HoldReleaseFailure, _map)
341
342
343def lzc_get_holds_translate_error(ret, snapname):
344    if ret == 0:
345        return
346    if ret == errno.EINVAL:
347        _validate_snap_name(snapname)
348    if ret == errno.ENOENT:
349        raise lzc_exc.SnapshotNotFound(snapname)
350    if ret == errno.ENOTSUP:
351        raise lzc_exc.FeatureNotSupported(_pool_name(snapname))
352    raise _generic_exception(ret, snapname, "Failed to get holds on snapshot")
353
354
355def lzc_send_translate_error(ret, snapname, fromsnap, fd, flags):
356    if ret == 0:
357        return
358    if ret == errno.EXDEV and fromsnap is not None:
359        if _pool_name(fromsnap) != _pool_name(snapname):
360            raise lzc_exc.PoolsDiffer(snapname)
361        else:
362            raise lzc_exc.SnapshotMismatch(snapname)
363    elif ret == errno.EINVAL:
364        if (fromsnap is not None and not _is_valid_snap_name(fromsnap) and
365                not _is_valid_bmark_name(fromsnap)):
366            raise lzc_exc.NameInvalid(fromsnap)
367        elif (not _is_valid_snap_name(snapname) and
368                not _is_valid_fs_name(snapname)):
369            raise lzc_exc.NameInvalid(snapname)
370        elif fromsnap is not None and len(fromsnap) > MAXNAMELEN:
371            raise lzc_exc.NameTooLong(fromsnap)
372        elif len(snapname) > MAXNAMELEN:
373            raise lzc_exc.NameTooLong(snapname)
374        elif (fromsnap is not None and
375                _pool_name(fromsnap) != _pool_name(snapname)):
376            raise lzc_exc.PoolsDiffer(snapname)
377    elif ret == errno.ENOENT:
378        if (fromsnap is not None and not _is_valid_snap_name(fromsnap) and
379                not _is_valid_bmark_name(fromsnap)):
380            raise lzc_exc.NameInvalid(fromsnap)
381        raise lzc_exc.SnapshotNotFound(snapname)
382    elif ret == errno.ENAMETOOLONG:
383        if fromsnap is not None and len(fromsnap) > MAXNAMELEN:
384            raise lzc_exc.NameTooLong(fromsnap)
385        else:
386            raise lzc_exc.NameTooLong(snapname)
387    raise lzc_exc.StreamIOError(ret)
388
389
390def lzc_send_space_translate_error(ret, snapname, fromsnap):
391    if ret == 0:
392        return
393    if ret == errno.EXDEV and fromsnap is not None:
394        if _pool_name(fromsnap) != _pool_name(snapname):
395            raise lzc_exc.PoolsDiffer(snapname)
396        else:
397            raise lzc_exc.SnapshotMismatch(snapname)
398    elif ret == errno.EINVAL:
399        if fromsnap is not None and not _is_valid_snap_name(fromsnap):
400            raise lzc_exc.NameInvalid(fromsnap)
401        elif not _is_valid_snap_name(snapname):
402            raise lzc_exc.NameInvalid(snapname)
403        elif fromsnap is not None and len(fromsnap) > MAXNAMELEN:
404            raise lzc_exc.NameTooLong(fromsnap)
405        elif len(snapname) > MAXNAMELEN:
406            raise lzc_exc.NameTooLong(snapname)
407        elif (fromsnap is not None and
408                _pool_name(fromsnap) != _pool_name(snapname)):
409            raise lzc_exc.PoolsDiffer(snapname)
410    elif ret == errno.ENOENT and fromsnap is not None:
411        if not _is_valid_snap_name(fromsnap):
412            raise lzc_exc.NameInvalid(fromsnap)
413    if ret == errno.ENOENT:
414        raise lzc_exc.SnapshotNotFound(snapname)
415    raise _generic_exception(
416        ret, snapname, "Failed to estimate backup stream size")
417
418
419def lzc_receive_translate_errors(
420    ret, snapname, fd, force, raw, resumable, embedded, origin, properrs
421):
422    if ret == 0:
423        if properrs is not None and len(properrs) > 0:
424            def _map(ret, name):
425                if ret == errno.EINVAL:
426                    return lzc_exc.PropertyInvalid(name)
427                if ret == zfs_errno.ZFS_ERR_BADPROP:
428                    return lzc_exc.PropertyInvalid(name)
429                return _generic_exception(ret, name, "Failed to set property")
430            _handle_err_list(
431                errno.EINVAL, properrs, [snapname],
432                lzc_exc.ReceivePropertyFailure, _map)
433        else:
434            return
435    if ret == errno.EINVAL:
436        if (not _is_valid_snap_name(snapname) and
437                not _is_valid_fs_name(snapname)):
438            raise lzc_exc.NameInvalid(snapname)
439        elif len(snapname) > MAXNAMELEN:
440            raise lzc_exc.NameTooLong(snapname)
441        elif origin is not None and not _is_valid_snap_name(origin):
442            raise lzc_exc.NameInvalid(origin)
443        elif resumable:
444            raise lzc_exc.StreamFeatureInvalid()
445        elif embedded and not raw:
446            raise lzc_exc.StreamFeatureIncompatible()
447        else:
448            raise lzc_exc.BadStream()
449    if ret == errno.ENOENT:
450        if not _is_valid_snap_name(snapname):
451            raise lzc_exc.NameInvalid(snapname)
452        else:
453            raise lzc_exc.DatasetNotFound(snapname)
454    if ret == errno.EEXIST:
455        raise lzc_exc.DatasetExists(snapname)
456    if ret == errno.ENOTSUP:
457        raise lzc_exc.StreamFeatureNotSupported()
458    if ret == errno.ENODEV:
459        raise lzc_exc.StreamMismatch(_fs_name(snapname))
460    if ret == errno.ETXTBSY:
461        raise lzc_exc.DestinationModified(_fs_name(snapname))
462    if ret == errno.EBUSY:
463        raise lzc_exc.DatasetBusy(_fs_name(snapname))
464    if ret == errno.ENOSPC:
465        raise lzc_exc.NoSpace(_fs_name(snapname))
466    if ret == errno.EDQUOT:
467        raise lzc_exc.QuotaExceeded(_fs_name(snapname))
468    if ret == errno.ENAMETOOLONG:
469        raise lzc_exc.NameTooLong(snapname)
470    if ret == errno.EROFS:
471        raise lzc_exc.ReadOnlyPool(_pool_name(snapname))
472    if ret == errno.EAGAIN:
473        raise lzc_exc.SuspendedPool(_pool_name(snapname))
474    if ret == errno.EACCES:
475        raise lzc_exc.EncryptionKeyNotLoaded()
476    if ret == ECKSUM:
477        raise lzc_exc.BadStream()
478    if ret == ZFS_ERR_WRONG_PARENT:
479        raise lzc_exc.WrongParent(_fs_name(snapname))
480    if ret == zfs_errno.ZFS_ERR_STREAM_TRUNCATED:
481        raise lzc_exc.StreamTruncated()
482    if ret == zfs_errno.ZFS_ERR_BADPROP:
483        raise lzc_exc.PropertyInvalid(snapname)
484
485    raise lzc_exc.StreamIOError(ret)
486
487
488def lzc_promote_translate_error(ret, name):
489    if ret == 0:
490        return
491    if ret == errno.EINVAL:
492        _validate_fs_name(name)
493        raise lzc_exc.NotClone(name)
494    if ret == errno.ENOTSOCK:
495        raise lzc_exc.NotClone(name)
496    if ret == errno.ENOENT:
497        raise lzc_exc.FilesystemNotFound(name)
498    if ret == errno.EEXIST:
499        raise lzc_exc.SnapshotExists(name)
500    raise _generic_exception(ret, name, "Failed to promote dataset")
501
502
503def lzc_change_key_translate_error(ret, name):
504    if ret == 0:
505        return
506    if ret == errno.EINVAL:
507        _validate_fs_name(name)
508        raise lzc_exc.PropertyInvalid(name)
509    if ret == errno.ENOENT:
510        raise lzc_exc.FilesystemNotFound(name)
511    if ret == errno.EACCES:
512        raise lzc_exc.EncryptionKeyNotLoaded()
513    raise _generic_exception(ret, name, "Failed to change encryption key")
514
515
516def lzc_load_key_translate_error(ret, name, noop):
517    if ret == 0:
518        return
519    if ret == errno.EINVAL:
520        _validate_fs_name(name)
521        raise lzc_exc.PropertyInvalid(name)
522    if ret == errno.ENOENT:
523        raise lzc_exc.FilesystemNotFound(name)
524    if ret == errno.EACCES:
525        raise lzc_exc.EncryptionKeyInvalid()
526    if ret == errno.EEXIST:
527        raise lzc_exc.EncryptionKeyAlreadyLoaded()
528    if noop:
529        raise _generic_exception(ret, name, "Failed to load encryption key")
530    else:
531        raise _generic_exception(ret, name, "Failed to verify encryption key")
532
533
534def lzc_unload_key_translate_error(ret, name):
535    if ret == 0:
536        return
537    if ret == errno.EINVAL:
538        _validate_fs_name(name)
539        raise lzc_exc.PropertyInvalid(name)
540    if ret == errno.ENOENT:
541        raise lzc_exc.FilesystemNotFound(name)
542    if ret == errno.EACCES:
543        raise lzc_exc.EncryptionKeyNotLoaded()
544    raise _generic_exception(ret, name, "Failed to unload encryption key")
545
546
547def lzc_sync_translate_error(ret, name):
548    if ret == 0:
549        return
550    if ret == errno.ENOENT:
551        raise lzc_exc.PoolNotFound(name)
552    raise _generic_exception(ret, name, "Failed to sync pool")
553
554
555def lzc_reopen_translate_error(ret, name):
556    if ret == 0:
557        return
558    if ret == errno.ENOENT:
559        raise lzc_exc.PoolNotFound(name)
560    raise _generic_exception(ret, name, "Failed to reopen pool")
561
562
563def lzc_channel_program_translate_error(ret, name, error):
564    if ret == 0:
565        return
566    if ret == errno.ENOENT:
567        raise lzc_exc.PoolNotFound(name)
568    if ret == ETIME:
569        raise lzc_exc.ZCPTimeout()
570    if ret == errno.ENOMEM:
571        raise lzc_exc.ZCPMemoryError()
572    if ret == errno.ENOSPC:
573        raise lzc_exc.ZCPSpaceError()
574    if ret == errno.EPERM:
575        raise lzc_exc.ZCPPermissionError()
576    if ret == ECHRNG:
577        raise lzc_exc.ZCPRuntimeError(error)
578    if ret == errno.EINVAL:
579        if error is None:
580            raise lzc_exc.ZCPLimitInvalid()
581        else:
582            raise lzc_exc.ZCPSyntaxError(error)
583    raise _generic_exception(ret, name, "Failed to execute channel program")
584
585
586def lzc_pool_checkpoint_translate_error(ret, name, discard=False):
587    if ret == 0:
588        return
589    if ret == errno.ENOENT:
590        raise lzc_exc.PoolNotFound(name)
591    if ret == ZFS_ERR_CHECKPOINT_EXISTS:
592        raise lzc_exc.CheckpointExists()
593    if ret == ZFS_ERR_NO_CHECKPOINT:
594        raise lzc_exc.CheckpointNotFound()
595    if ret == ZFS_ERR_DISCARDING_CHECKPOINT:
596        raise lzc_exc.CheckpointDiscarding()
597    if ret == ZFS_ERR_DEVRM_IN_PROGRESS:
598        raise lzc_exc.DeviceRemovalRunning()
599    if ret == ZFS_ERR_VDEV_TOO_BIG:
600        raise lzc_exc.DeviceTooBig()
601    if ret == ZFS_ERR_RAIDZ_EXPAND_IN_PROGRESS:
602        raise lzc_exc.RaidzExpansionRunning()
603    if discard:
604        raise _generic_exception(
605            ret, name, "Failed to discard pool checkpoint")
606    else:
607        raise _generic_exception(ret, name, "Failed to create pool checkpoint")
608
609
610def lzc_pool_checkpoint_discard_translate_error(ret, name):
611    lzc_pool_checkpoint_translate_error(ret, name, discard=True)
612
613
614def lzc_rename_translate_error(ret, source, target):
615    if ret == 0:
616        return
617    if ret == errno.EINVAL:
618        _validate_fs_name(source)
619        _validate_fs_name(target)
620        if _pool_name(source) != _pool_name(target):
621            raise lzc_exc.PoolsDiffer(source)
622    if ret == errno.EEXIST:
623        raise lzc_exc.FilesystemExists(target)
624    if ret == errno.ENOENT:
625        raise lzc_exc.FilesystemNotFound(source)
626    if ret == ZFS_ERR_WRONG_PARENT:
627        raise lzc_exc.WrongParent(target)
628    raise _generic_exception(ret, source, "Failed to rename dataset")
629
630
631def lzc_destroy_translate_error(ret, name):
632    if ret == 0:
633        return
634    if ret == errno.EINVAL:
635        _validate_fs_name(name)
636    if ret == errno.ENOENT:
637        raise lzc_exc.FilesystemNotFound(name)
638    raise _generic_exception(ret, name, "Failed to destroy dataset")
639
640
641def lzc_inherit_prop_translate_error(ret, name, prop):
642    if ret == 0:
643        return
644    if ret == errno.EINVAL:
645        _validate_fs_name(name)
646        raise lzc_exc.PropertyInvalid(prop)
647    if ret == errno.ENOENT:
648        raise lzc_exc.DatasetNotFound(name)
649    raise _generic_exception(ret, name, "Failed to inherit a property")
650
651
652def lzc_set_prop_translate_error(ret, name, prop, val):
653    if ret == 0:
654        return
655    if ret == errno.EINVAL:
656        _validate_fs_or_snap_name(name)
657        raise lzc_exc.PropertyInvalid(prop)
658    if ret == errno.ENOENT:
659        raise lzc_exc.DatasetNotFound(name)
660    raise _generic_exception(ret, name, "Failed to set a property")
661
662
663def lzc_get_props_translate_error(ret, name):
664    if ret == 0:
665        return
666    if ret == errno.EINVAL:
667        _validate_fs_or_snap_name(name)
668    if ret == errno.ENOENT:
669        raise lzc_exc.DatasetNotFound(name)
670    raise _generic_exception(ret, name, "Failed to get properties")
671
672
673def lzc_list_children_translate_error(ret, name):
674    if ret == 0:
675        return
676    if ret == errno.EINVAL:
677        _validate_fs_name(name)
678    raise _generic_exception(ret, name, "Error while iterating children")
679
680
681def lzc_list_snaps_translate_error(ret, name):
682    if ret == 0:
683        return
684    if ret == errno.EINVAL:
685        _validate_fs_name(name)
686    raise _generic_exception(ret, name, "Error while iterating snapshots")
687
688
689def lzc_list_translate_error(ret, name, opts):
690    if ret == 0:
691        return
692    if ret == errno.ENOENT:
693        raise lzc_exc.DatasetNotFound(name)
694    if ret == errno.EINVAL:
695        _validate_fs_or_snap_name(name)
696    raise _generic_exception(ret, name, "Error obtaining a list")
697
698
699def _handle_err_list(ret, errlist, names, exception, mapper):
700    '''
701    Convert one or more errors from an operation into the requested exception.
702
703    :param int ret: the overall return code.
704    :param errlist: the dictionary that maps entity names to their specific
705        error codes.
706    :type errlist: dict of bytes:int
707    :param names: the list of all names of the entities on which the operation
708        was attempted.
709    :param type exception: the type of the exception to raise if an error
710        occurred. The exception should be a subclass of
711        ``MultipleOperationsFailure``.
712    :param function mapper: the function that maps an error code and a name to
713        a Python exception.
714
715    Unless ``ret`` is zero this function will raise the ``exception``.
716    If the ``errlist`` is not empty, then the compound exception will contain
717    a list of exceptions corresponding to each individual error code in the
718    ``errlist``.
719    Otherwise, the ``exception`` will contain a list with a single exception
720    corresponding to the ``ret`` value. If the ``names`` list contains only one
721    element, that is, the operation was attempted on a single entity, then the
722    name of that entity is passed to the ``mapper``.
723    If the operation was attempted on multiple entities, but the ``errlist``
724    is empty, then we can not know which entity caused the error and, thus,
725    ``None`` is used as a name to signify that fact.
726
727    .. note::
728        Note that the ``errlist`` can contain a special element with a key of
729        "N_MORE_ERRORS".
730        That element means that there were too many errors to place on the
731        ``errlist``.
732        Those errors are suppressed and only their count is provided as a
733        value of the special ``N_MORE_ERRORS`` element.
734    '''
735    if ret == 0:
736        return
737
738    if len(errlist) == 0:
739        suppressed_count = 0
740        names = list(zip(names, range(2)))
741        if len(names) == 1:
742            name, _ = names[0]
743        else:
744            name = None
745        errors = [mapper(ret, name)]
746    else:
747        errors = []
748        suppressed_count = errlist.pop('N_MORE_ERRORS', 0)
749        for name in errlist:
750            err = errlist[name]
751            errors.append(mapper(err, name))
752
753    raise exception(errors, suppressed_count)
754
755
756def _pool_name(name):
757    '''
758    Extract a pool name from the given dataset or bookmark name.
759
760    '/' separates dataset name components.
761    '@' separates a snapshot name from the rest of the dataset name.
762    '#' separates a bookmark name from the rest of the dataset name.
763    '''
764    return re.split(b'[/@#]', name, 1)[0]
765
766
767def _fs_name(name):
768    '''
769    Extract a dataset name from the given snapshot or bookmark name.
770
771    '@' separates a snapshot name from the rest of the dataset name.
772    '#' separates a bookmark name from the rest of the dataset name.
773    '''
774    return re.split(b'[@#]', name, 1)[0]
775
776
777def _is_valid_name_component(component):
778    allowed = string.ascii_letters + string.digits + u'-_.: '
779    return component and all(x in allowed.encode() for x in component)
780
781
782def _is_valid_fs_name(name):
783    return name and all(_is_valid_name_component(c) for c in name.split(b'/'))
784
785
786def _is_valid_snap_name(name):
787    parts = name.split(b'@')
788    return (len(parts) == 2 and _is_valid_fs_name(parts[0]) and
789            _is_valid_name_component(parts[1]))
790
791
792def _is_valid_bmark_name(name):
793    parts = name.split(b'#')
794    return (len(parts) == 2 and _is_valid_fs_name(parts[0]) and
795            _is_valid_name_component(parts[1]))
796
797
798def _validate_fs_name(name):
799    if not _is_valid_fs_name(name):
800        raise lzc_exc.FilesystemNameInvalid(name)
801    elif len(name) > MAXNAMELEN:
802        raise lzc_exc.NameTooLong(name)
803
804
805def _validate_snap_name(name):
806    if not _is_valid_snap_name(name):
807        raise lzc_exc.SnapshotNameInvalid(name)
808    elif len(name) > MAXNAMELEN:
809        raise lzc_exc.NameTooLong(name)
810
811
812def _validate_bmark_name(name):
813    if not _is_valid_bmark_name(name):
814        raise lzc_exc.BookmarkNameInvalid(name)
815    elif len(name) > MAXNAMELEN:
816        raise lzc_exc.NameTooLong(name)
817
818
819def _validate_fs_or_snap_name(name):
820    if not _is_valid_fs_name(name) and not _is_valid_snap_name(name):
821        raise lzc_exc.NameInvalid(name)
822    elif len(name) > MAXNAMELEN:
823        raise lzc_exc.NameTooLong(name)
824
825
826def _generic_exception(err, name, message):
827    if err in _error_to_exception:
828        return _error_to_exception[err](name)
829    else:
830        return lzc_exc.ZFSGenericError(err, message, name)
831
832
833_error_to_exception = {e.errno: e for e in [
834    lzc_exc.ZIOError,
835    lzc_exc.NoSpace,
836    lzc_exc.QuotaExceeded,
837    lzc_exc.DatasetBusy,
838    lzc_exc.NameTooLong,
839    lzc_exc.ReadOnlyPool,
840    lzc_exc.SuspendedPool,
841    lzc_exc.PoolsDiffer,
842    lzc_exc.PropertyNotSupported,
843]}
844
845
846# vim: softtabstop=4 tabstop=4 expandtab shiftwidth=4
847