1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
// Copyright 2021 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License"),;
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package linuxerr
import (
"gvisor.dev/gvisor/pkg/abi/linux/errno"
"gvisor.dev/gvisor/pkg/errors"
)
var (
// ErrWouldBlock is an internal error used to indicate that an operation
// cannot be satisfied immediately, and should be retried at a later
// time, possibly when the caller has received a notification that the
// operation may be able to complete. It is used by implementations of
// the kio.File interface.
ErrWouldBlock = errors.New(errno.EWOULDBLOCK, "request would block")
// ErrInterrupted is returned if a request is interrupted before it can
// complete.
ErrInterrupted = errors.New(errno.EINTR, "request was interrupted")
// ErrExceedsFileSizeLimit is returned if a request would exceed the
// file's size limit.
ErrExceedsFileSizeLimit = errors.New(errno.E2BIG, "exceeds file size limit")
)
var errorMap = map[error]*errors.Error{
ErrWouldBlock: EWOULDBLOCK,
ErrInterrupted: EINTR,
ErrExceedsFileSizeLimit: EFBIG,
}
// errorUnwrappers is an array of unwrap functions to extract typed errors.
var errorUnwrappers = []func(error) (*errors.Error, bool){}
// AddErrorUnwrapper registers an unwrap method that can extract a concrete error
// from a typed, but not initialized, error.
func AddErrorUnwrapper(unwrap func(e error) (*errors.Error, bool)) {
errorUnwrappers = append(errorUnwrappers, unwrap)
}
// TranslateError translates errors to errnos, it will return false if
// the error was not registered.
func TranslateError(from error) (*errors.Error, bool) {
if err, ok := errorMap[from]; ok {
return err, true
}
// Try to unwrap the error if we couldn't match an error
// exactly. This might mean that a package has its own
// error type.
for _, unwrap := range errorUnwrappers {
if err, ok := unwrap(from); ok {
return err, true
}
}
return nil, false
}
// These errors are significant because ptrace syscall exit tracing can
// observe them.
//
// For all of the following errors, if the syscall is not interrupted by a
// signal delivered to a user handler, the syscall is restarted.
var (
// ERESTARTSYS is returned by an interrupted syscall to indicate that it
// should be converted to EINTR if interrupted by a signal delivered to a
// user handler without SA_RESTART set, and restarted otherwise.
ERESTARTSYS = errors.New(errno.ERESTARTSYS, "to be restarted if SA_RESTART is set")
// ERESTARTNOINTR is returned by an interrupted syscall to indicate that it
// should always be restarted.
ERESTARTNOINTR = errors.New(errno.ERESTARTNOINTR, "to be restarted")
// ERESTARTNOHAND is returned by an interrupted syscall to indicate that it
// should be converted to EINTR if interrupted by a signal delivered to a
// user handler, and restarted otherwise.
ERESTARTNOHAND = errors.New(errno.ERESTARTNOHAND, "to be restarted if no handler")
// ERESTART_RESTARTBLOCK is returned by an interrupted syscall to indicate
// that it should be restarted using a custom function. The interrupted
// syscall must register a custom restart function by calling
// Task.SetRestartSyscallFn.
ERESTART_RESTARTBLOCK = errors.New(errno.ERESTART_RESTARTBLOCK, "interrupted by signal")
)
var restartMap = map[int]*errors.Error{
-int(errno.ERESTARTSYS): ERESTARTSYS,
-int(errno.ERESTARTNOINTR): ERESTARTNOINTR,
-int(errno.ERESTARTNOHAND): ERESTARTNOHAND,
-int(errno.ERESTART_RESTARTBLOCK): ERESTART_RESTARTBLOCK,
}
// IsRestartError checks if a given error is a restart error.
func IsRestartError(err error) bool {
switch err {
case ERESTARTSYS, ERESTARTNOINTR, ERESTARTNOHAND, ERESTART_RESTARTBLOCK:
return true
default:
return false
}
}
// SyscallRestartErrorFromReturn returns the SyscallRestartErrno represented by
// rv, the value in a syscall return register.
func SyscallRestartErrorFromReturn(rv uintptr) (*errors.Error, bool) {
err, ok := restartMap[int(rv)]
return err, ok
}
// ConvertIntr converts the provided error code (err) to another one (intr) if
// the first error corresponds to an interrupted operation.
func ConvertIntr(err, intr error) error {
if err == ErrInterrupted {
return intr
}
return err
}
|