xref: /freebsd/sys/contrib/openzfs/contrib/pyzfs/libzfs_core/_error_translation.py (revision 8a62a2a5659d1839d8799b4274c04469d7f17c78)
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 _handle_err_list(ret, errlist, names, exception, mapper):
642    '''
643    Convert one or more errors from an operation into the requested exception.
644
645    :param int ret: the overall return code.
646    :param errlist: the dictionary that maps entity names to their specific
647        error codes.
648    :type errlist: dict of bytes:int
649    :param names: the list of all names of the entities on which the operation
650        was attempted.
651    :param type exception: the type of the exception to raise if an error
652        occurred. The exception should be a subclass of
653        ``MultipleOperationsFailure``.
654    :param function mapper: the function that maps an error code and a name to
655        a Python exception.
656
657    Unless ``ret`` is zero this function will raise the ``exception``.
658    If the ``errlist`` is not empty, then the compound exception will contain
659    a list of exceptions corresponding to each individual error code in the
660    ``errlist``.
661    Otherwise, the ``exception`` will contain a list with a single exception
662    corresponding to the ``ret`` value. If the ``names`` list contains only one
663    element, that is, the operation was attempted on a single entity, then the
664    name of that entity is passed to the ``mapper``.
665    If the operation was attempted on multiple entities, but the ``errlist``
666    is empty, then we can not know which entity caused the error and, thus,
667    ``None`` is used as a name to signify that fact.
668
669    .. note::
670        Note that the ``errlist`` can contain a special element with a key of
671        "N_MORE_ERRORS".
672        That element means that there were too many errors to place on the
673        ``errlist``.
674        Those errors are suppressed and only their count is provided as a
675        value of the special ``N_MORE_ERRORS`` element.
676    '''
677    if ret == 0:
678        return
679
680    if len(errlist) == 0:
681        suppressed_count = 0
682        names = list(zip(names, range(2)))
683        if len(names) == 1:
684            name, _ = names[0]
685        else:
686            name = None
687        errors = [mapper(ret, name)]
688    else:
689        errors = []
690        suppressed_count = errlist.pop('N_MORE_ERRORS', 0)
691        for name in errlist:
692            err = errlist[name]
693            errors.append(mapper(err, name))
694
695    raise exception(errors, suppressed_count)
696
697
698def _pool_name(name):
699    '''
700    Extract a pool name from the given dataset or bookmark name.
701
702    '/' separates dataset name components.
703    '@' separates a snapshot name from the rest of the dataset name.
704    '#' separates a bookmark name from the rest of the dataset name.
705    '''
706    return re.split(b'[/@#]', name, 1)[0]
707
708
709def _fs_name(name):
710    '''
711    Extract a dataset name from the given snapshot or bookmark name.
712
713    '@' separates a snapshot name from the rest of the dataset name.
714    '#' separates a bookmark name from the rest of the dataset name.
715    '''
716    return re.split(b'[@#]', name, 1)[0]
717
718
719def _is_valid_name_component(component):
720    allowed = string.ascii_letters + string.digits + u'-_.: '
721    return component and all(x in allowed.encode() for x in component)
722
723
724def _is_valid_fs_name(name):
725    return name and all(_is_valid_name_component(c) for c in name.split(b'/'))
726
727
728def _is_valid_snap_name(name):
729    parts = name.split(b'@')
730    return (len(parts) == 2 and _is_valid_fs_name(parts[0]) and
731            _is_valid_name_component(parts[1]))
732
733
734def _is_valid_bmark_name(name):
735    parts = name.split(b'#')
736    return (len(parts) == 2 and _is_valid_fs_name(parts[0]) and
737            _is_valid_name_component(parts[1]))
738
739
740def _validate_fs_name(name):
741    if not _is_valid_fs_name(name):
742        raise lzc_exc.FilesystemNameInvalid(name)
743    elif len(name) > MAXNAMELEN:
744        raise lzc_exc.NameTooLong(name)
745
746
747def _validate_snap_name(name):
748    if not _is_valid_snap_name(name):
749        raise lzc_exc.SnapshotNameInvalid(name)
750    elif len(name) > MAXNAMELEN:
751        raise lzc_exc.NameTooLong(name)
752
753
754def _validate_bmark_name(name):
755    if not _is_valid_bmark_name(name):
756        raise lzc_exc.BookmarkNameInvalid(name)
757    elif len(name) > MAXNAMELEN:
758        raise lzc_exc.NameTooLong(name)
759
760
761def _validate_fs_or_snap_name(name):
762    if not _is_valid_fs_name(name) and not _is_valid_snap_name(name):
763        raise lzc_exc.NameInvalid(name)
764    elif len(name) > MAXNAMELEN:
765        raise lzc_exc.NameTooLong(name)
766
767
768def _generic_exception(err, name, message):
769    if err in _error_to_exception:
770        return _error_to_exception[err](name)
771    else:
772        return lzc_exc.ZFSGenericError(err, message, name)
773
774
775_error_to_exception = {e.errno: e for e in [
776    lzc_exc.ZIOError,
777    lzc_exc.NoSpace,
778    lzc_exc.QuotaExceeded,
779    lzc_exc.DatasetBusy,
780    lzc_exc.NameTooLong,
781    lzc_exc.ReadOnlyPool,
782    lzc_exc.SuspendedPool,
783    lzc_exc.PoolsDiffer,
784    lzc_exc.PropertyNotSupported,
785]}
786
787
788# vim: softtabstop=4 tabstop=4 expandtab shiftwidth=4
789