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