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