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