1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Error string handling |
4 | * |
5 | * Plan 9 uses error strings, Unix uses error numbers. These functions |
6 | * try to help manage that and provide for dynamically adding error |
7 | * mappings. |
8 | * |
9 | * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> |
10 | * Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov> |
11 | */ |
12 | |
13 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
14 | |
15 | #include <linux/module.h> |
16 | #include <linux/list.h> |
17 | #include <linux/jhash.h> |
18 | #include <linux/errno.h> |
19 | #include <net/9p/9p.h> |
20 | |
21 | /** |
22 | * struct errormap - map string errors from Plan 9 to Linux numeric ids |
23 | * @name: string sent over 9P |
24 | * @val: numeric id most closely representing @name |
25 | * @namelen: length of string |
26 | * @list: hash-table list for string lookup |
27 | */ |
28 | struct errormap { |
29 | char *name; |
30 | int val; |
31 | |
32 | int namelen; |
33 | struct hlist_node list; |
34 | }; |
35 | |
36 | #define ERRHASHSZ 32 |
37 | static struct hlist_head hash_errmap[ERRHASHSZ]; |
38 | |
39 | /* FixMe - reduce to a reasonable size */ |
40 | static struct errormap errmap[] = { |
41 | {"Operation not permitted" , EPERM}, |
42 | {"wstat prohibited" , EPERM}, |
43 | {"No such file or directory" , ENOENT}, |
44 | {"directory entry not found" , ENOENT}, |
45 | {"file not found" , ENOENT}, |
46 | {"Interrupted system call" , EINTR}, |
47 | {"Input/output error" , EIO}, |
48 | {"No such device or address" , ENXIO}, |
49 | {"Argument list too long" , E2BIG}, |
50 | {"Bad file descriptor" , EBADF}, |
51 | {"Resource temporarily unavailable" , EAGAIN}, |
52 | {"Cannot allocate memory" , ENOMEM}, |
53 | {"Permission denied" , EACCES}, |
54 | {"Bad address" , EFAULT}, |
55 | {"Block device required" , ENOTBLK}, |
56 | {"Device or resource busy" , EBUSY}, |
57 | {"File exists" , EEXIST}, |
58 | {"Invalid cross-device link" , EXDEV}, |
59 | {"No such device" , ENODEV}, |
60 | {"Not a directory" , ENOTDIR}, |
61 | {"Is a directory" , EISDIR}, |
62 | {"Invalid argument" , EINVAL}, |
63 | {"Too many open files in system" , ENFILE}, |
64 | {"Too many open files" , EMFILE}, |
65 | {"Text file busy" , ETXTBSY}, |
66 | {"File too large" , EFBIG}, |
67 | {"No space left on device" , ENOSPC}, |
68 | {"Illegal seek" , ESPIPE}, |
69 | {"Read-only file system" , EROFS}, |
70 | {"Too many links" , EMLINK}, |
71 | {"Broken pipe" , EPIPE}, |
72 | {"Numerical argument out of domain" , EDOM}, |
73 | {"Numerical result out of range" , ERANGE}, |
74 | {"Resource deadlock avoided" , EDEADLK}, |
75 | {"File name too long" , ENAMETOOLONG}, |
76 | {"No locks available" , ENOLCK}, |
77 | {"Function not implemented" , ENOSYS}, |
78 | {"Directory not empty" , ENOTEMPTY}, |
79 | {"Too many levels of symbolic links" , ELOOP}, |
80 | {"No message of desired type" , ENOMSG}, |
81 | {"Identifier removed" , EIDRM}, |
82 | {"No data available" , ENODATA}, |
83 | {"Machine is not on the network" , ENONET}, |
84 | {"Package not installed" , ENOPKG}, |
85 | {"Object is remote" , EREMOTE}, |
86 | {"Link has been severed" , ENOLINK}, |
87 | {"Communication error on send" , ECOMM}, |
88 | {"Protocol error" , EPROTO}, |
89 | {"Bad message" , EBADMSG}, |
90 | {"File descriptor in bad state" , EBADFD}, |
91 | {"Streams pipe error" , ESTRPIPE}, |
92 | {"Too many users" , EUSERS}, |
93 | {"Socket operation on non-socket" , ENOTSOCK}, |
94 | {"Message too long" , EMSGSIZE}, |
95 | {"Protocol not available" , ENOPROTOOPT}, |
96 | {"Protocol not supported" , EPROTONOSUPPORT}, |
97 | {"Socket type not supported" , ESOCKTNOSUPPORT}, |
98 | {"Operation not supported" , EOPNOTSUPP}, |
99 | {"Protocol family not supported" , EPFNOSUPPORT}, |
100 | {"Network is down" , ENETDOWN}, |
101 | {"Network is unreachable" , ENETUNREACH}, |
102 | {"Network dropped connection on reset" , ENETRESET}, |
103 | {"Software caused connection abort" , ECONNABORTED}, |
104 | {"Connection reset by peer" , ECONNRESET}, |
105 | {"No buffer space available" , ENOBUFS}, |
106 | {"Transport endpoint is already connected" , EISCONN}, |
107 | {"Transport endpoint is not connected" , ENOTCONN}, |
108 | {"Cannot send after transport endpoint shutdown" , ESHUTDOWN}, |
109 | {"Connection timed out" , ETIMEDOUT}, |
110 | {"Connection refused" , ECONNREFUSED}, |
111 | {"Host is down" , EHOSTDOWN}, |
112 | {"No route to host" , EHOSTUNREACH}, |
113 | {"Operation already in progress" , EALREADY}, |
114 | {"Operation now in progress" , EINPROGRESS}, |
115 | {"Is a named type file" , EISNAM}, |
116 | {"Remote I/O error" , EREMOTEIO}, |
117 | {"Disk quota exceeded" , EDQUOT}, |
118 | /* errors from fossil, vacfs, and u9fs */ |
119 | {"fid unknown or out of range" , EBADF}, |
120 | {"permission denied" , EACCES}, |
121 | {"file does not exist" , ENOENT}, |
122 | {"authentication failed" , ECONNREFUSED}, |
123 | {"bad offset in directory read" , ESPIPE}, |
124 | {"bad use of fid" , EBADF}, |
125 | {"wstat can't convert between files and directories" , EPERM}, |
126 | {"directory is not empty" , ENOTEMPTY}, |
127 | {"file exists" , EEXIST}, |
128 | {"file already exists" , EEXIST}, |
129 | {"file or directory already exists" , EEXIST}, |
130 | {"fid already in use" , EBADF}, |
131 | {"file in use" , ETXTBSY}, |
132 | {"i/o error" , EIO}, |
133 | {"file already open for I/O" , ETXTBSY}, |
134 | {"illegal mode" , EINVAL}, |
135 | {"illegal name" , ENAMETOOLONG}, |
136 | {"not a directory" , ENOTDIR}, |
137 | {"not a member of proposed group" , EPERM}, |
138 | {"not owner" , EACCES}, |
139 | {"only owner can change group in wstat" , EACCES}, |
140 | {"read only file system" , EROFS}, |
141 | {"no access to special file" , EPERM}, |
142 | {"i/o count too large" , EIO}, |
143 | {"unknown group" , EINVAL}, |
144 | {"unknown user" , EINVAL}, |
145 | {"bogus wstat buffer" , EPROTO}, |
146 | {"exclusive use file already open" , EAGAIN}, |
147 | {"corrupted directory entry" , EIO}, |
148 | {"corrupted file entry" , EIO}, |
149 | {"corrupted block label" , EIO}, |
150 | {"corrupted meta data" , EIO}, |
151 | {"illegal offset" , EINVAL}, |
152 | {"illegal path element" , ENOENT}, |
153 | {"root of file system is corrupted" , EIO}, |
154 | {"corrupted super block" , EIO}, |
155 | {"protocol botch" , EPROTO}, |
156 | {"file system is full" , ENOSPC}, |
157 | {"file is in use" , EAGAIN}, |
158 | {"directory entry is not allocated" , ENOENT}, |
159 | {"file is read only" , EROFS}, |
160 | {"file has been removed" , EIDRM}, |
161 | {"only support truncation to zero length" , EPERM}, |
162 | {"cannot remove root" , EPERM}, |
163 | {"file too big" , EFBIG}, |
164 | {"venti i/o error" , EIO}, |
165 | /* these are not errors */ |
166 | {"u9fs rhostsauth: no authentication required" , 0}, |
167 | {"u9fs authnone: no authentication required" , 0}, |
168 | {NULL, -1} |
169 | }; |
170 | |
171 | /** |
172 | * p9_error_init - preload mappings into hash list |
173 | * |
174 | */ |
175 | |
176 | int p9_error_init(void) |
177 | { |
178 | struct errormap *c; |
179 | int bucket; |
180 | |
181 | /* initialize hash table */ |
182 | for (bucket = 0; bucket < ERRHASHSZ; bucket++) |
183 | INIT_HLIST_HEAD(&hash_errmap[bucket]); |
184 | |
185 | /* load initial error map into hash table */ |
186 | for (c = errmap; c->name; c++) { |
187 | c->namelen = strlen(c->name); |
188 | bucket = jhash(key: c->name, length: c->namelen, initval: 0) % ERRHASHSZ; |
189 | INIT_HLIST_NODE(h: &c->list); |
190 | hlist_add_head(n: &c->list, h: &hash_errmap[bucket]); |
191 | } |
192 | |
193 | return 1; |
194 | } |
195 | EXPORT_SYMBOL(p9_error_init); |
196 | |
197 | /** |
198 | * p9_errstr2errno - convert error string to error number |
199 | * @errstr: error string |
200 | * @len: length of error string |
201 | * |
202 | */ |
203 | |
204 | int p9_errstr2errno(char *errstr, int len) |
205 | { |
206 | int errno; |
207 | struct errormap *c; |
208 | int bucket; |
209 | |
210 | errno = 0; |
211 | c = NULL; |
212 | bucket = jhash(key: errstr, length: len, initval: 0) % ERRHASHSZ; |
213 | hlist_for_each_entry(c, &hash_errmap[bucket], list) { |
214 | if (c->namelen == len && !memcmp(p: c->name, q: errstr, size: len)) { |
215 | errno = c->val; |
216 | break; |
217 | } |
218 | } |
219 | |
220 | if (errno == 0) { |
221 | /* TODO: if error isn't found, add it dynamically */ |
222 | errstr[len] = 0; |
223 | pr_err("%s: server reported unknown error %s\n" , |
224 | __func__, errstr); |
225 | errno = ESERVERFAULT; |
226 | } |
227 | |
228 | return -errno; |
229 | } |
230 | EXPORT_SYMBOL(p9_errstr2errno); |
231 | |