Commit | Line | Data |
---|---|---|
8eabb4ff MW |
1 | /* -*-scala-*- |
2 | * | |
3 | * System calls and errors | |
4 | * | |
5 | * (c) 2018 Straylight/Edgeware | |
6 | */ | |
7 | ||
8 | /*----- Licensing notice --------------------------------------------------* | |
9 | * | |
10 | * This file is part of the Trivial IP Encryption (TrIPE) Android app. | |
11 | * | |
12 | * TrIPE is free software: you can redistribute it and/or modify it under | |
13 | * the terms of the GNU General Public License as published by the Free | |
14 | * Software Foundation; either version 3 of the License, or (at your | |
15 | * option) any later version. | |
16 | * | |
17 | * TrIPE is distributed in the hope that it will be useful, but WITHOUT | |
18 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
19 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
20 | * for more details. | |
21 | * | |
22 | * You should have received a copy of the GNU General Public License | |
23 | * along with TrIPE. If not, see <https://www.gnu.org/licenses/>. | |
24 | */ | |
25 | ||
26 | package uk.org.distorted.tripe; package object sys { | |
27 | ||
28 | /*----- Imports -----------------------------------------------------------*/ | |
29 | ||
30 | import scala.collection.mutable.HashSet; | |
31 | ||
32 | import java.io.File; | |
33 | ||
34 | import Magic._; | |
35 | ||
36 | /*----- Error codes -------------------------------------------------------*/ | |
37 | ||
38 | object Errno extends Enumeration { | |
39 | private[this] val tagmap = { | |
40 | val b = Map.newBuilder[String, Int]; | |
41 | for (jni.ErrorEntry(tag, err) <- jni.errtab) b += tag -> err; | |
42 | b.result | |
43 | } | |
44 | private[this] var wrong = -255; | |
45 | private[this] val seen = HashSet[Int](); | |
46 | ||
47 | class ErrnoVal private[Errno](tag: String, val code: Int, id: Int) | |
48 | extends Val(id, tag) { | |
49 | def message: String = jni.strerror(code).toJString; | |
50 | } | |
51 | ||
52 | private[this] def err(tag: String, code: Int): ErrnoVal = { | |
53 | if (seen contains code) { wrong -= 1; new ErrnoVal(tag, code, wrong) } | |
54 | else { seen += code; new ErrnoVal(tag, code, code) } | |
55 | } | |
56 | private[this] def err(tag: String): ErrnoVal = err(tag, tagmap(tag)); | |
57 | ||
58 | val OK = err("OK", 0); | |
59 | ||
60 | /* | |
61 | ;;; The errno name table is very boring to type. To make life less | |
62 | ;;; awful, put the errno names in this list and evaluate the code to | |
63 | ;;; get Emacs to regenerate it. | |
64 | ||
65 | (let ((errors '(EPERM ENOENT ESRCH EINTR EIO ENXIO E2BIG ENOEXEC EBADF | |
66 | ECHILD EAGAIN ENOMEM EACCES EFAULT ENOTBLK EBUSY EEXIST | |
67 | EXDEV ENODEV ENOTDIR EISDIR EINVAL ENFILE EMFILE ENOTTY | |
68 | ETXTBSY EFBIG ENOSPC ESPIPE EROFS EMLINK EPIPE EDOM | |
69 | ERANGE | |
70 | ||
71 | EDEADLK ENAMETOOLONG ENOLCK ENOSYS ENOTEMPTY ELOOP | |
72 | EWOULDBLOCK ENOMSG EIDRM ECHRNG EL2NSYNC EL3HLT EL3RST | |
73 | ELNRNG EUNATCH ENOCSI EL2HLT EBADE EBADR EXFULL ENOANO | |
74 | EBADRQC EBADSLT EDEADLOCK EBFONT ENOSTR ENODATA ETIME | |
75 | ENOSR ENONET ENOPKG EREMOTE ENOLINK EADV ESRMNT ECOMM | |
76 | EPROTO EMULTIHOP EDOTDOT EBADMSG EOVERFLOW ENOTUNIQ | |
77 | EBADFD EREMCHG ELIBACC ELIBBAD ELIBSCN ELIBMAX ELIBEXEC | |
78 | EILSEQ ERESTART ESTRPIPE EUSERS ENOTSOCK EDESTADDRREQ | |
79 | EMSGSIZE EPROTOTYPE ENOPROTOOPT EPROTONOSUPPORT | |
80 | ESOCKTNOSUPPORT EOPNOTSUPP EPFNOSUPPORT EAFNOSUPPORT | |
81 | EADDRINUSE EADDRNOTAVAIL ENETDOWN ENETUNREACH ENETRESET | |
82 | ECONNABORTED ECONNRESET ENOBUFS EISCONN ENOTCONN | |
83 | ESHUTDOWN ETOOMANYREFS ETIMEDOUT ECONNREFUSED EHOSTDOWN | |
84 | EHOSTUNREACH EALREADY EINPROGRESS ESTALE EUCLEAN ENOTNAM | |
85 | ENAVAIL EISNAM EREMOTEIO EDQUOT ENOMEDIUM EMEDIUMTYPE | |
86 | ECANCELED ENOKEY EKEYEXPIRED EKEYREVOKED EKEYREJECTED | |
87 | EOWNERDEAD ENOTRECOVERABLE ERFKILL EHWPOISON))) | |
88 | (save-excursion | |
89 | (goto-char (point-min)) | |
90 | (search-forward (concat "***" "BEGIN errtab" "***")) | |
91 | (beginning-of-line 2) | |
92 | (delete-region (point) | |
93 | (progn | |
94 | (search-forward "***END***") | |
95 | (beginning-of-line) | |
96 | (point))) | |
97 | (dolist (err errors) | |
98 | (insert (format " val %s = err(\"%s\");\n" err err))))) | |
99 | */ | |
100 | /***BEGIN errtab***/ | |
101 | val EPERM = err("EPERM"); | |
102 | val ENOENT = err("ENOENT"); | |
103 | val ESRCH = err("ESRCH"); | |
104 | val EINTR = err("EINTR"); | |
105 | val EIO = err("EIO"); | |
106 | val ENXIO = err("ENXIO"); | |
107 | val E2BIG = err("E2BIG"); | |
108 | val ENOEXEC = err("ENOEXEC"); | |
109 | val EBADF = err("EBADF"); | |
110 | val ECHILD = err("ECHILD"); | |
111 | val EAGAIN = err("EAGAIN"); | |
112 | val ENOMEM = err("ENOMEM"); | |
113 | val EACCES = err("EACCES"); | |
114 | val EFAULT = err("EFAULT"); | |
115 | val ENOTBLK = err("ENOTBLK"); | |
116 | val EBUSY = err("EBUSY"); | |
117 | val EEXIST = err("EEXIST"); | |
118 | val EXDEV = err("EXDEV"); | |
119 | val ENODEV = err("ENODEV"); | |
120 | val ENOTDIR = err("ENOTDIR"); | |
121 | val EISDIR = err("EISDIR"); | |
122 | val EINVAL = err("EINVAL"); | |
123 | val ENFILE = err("ENFILE"); | |
124 | val EMFILE = err("EMFILE"); | |
125 | val ENOTTY = err("ENOTTY"); | |
126 | val ETXTBSY = err("ETXTBSY"); | |
127 | val EFBIG = err("EFBIG"); | |
128 | val ENOSPC = err("ENOSPC"); | |
129 | val ESPIPE = err("ESPIPE"); | |
130 | val EROFS = err("EROFS"); | |
131 | val EMLINK = err("EMLINK"); | |
132 | val EPIPE = err("EPIPE"); | |
133 | val EDOM = err("EDOM"); | |
134 | val ERANGE = err("ERANGE"); | |
135 | val EDEADLK = err("EDEADLK"); | |
136 | val ENAMETOOLONG = err("ENAMETOOLONG"); | |
137 | val ENOLCK = err("ENOLCK"); | |
138 | val ENOSYS = err("ENOSYS"); | |
139 | val ENOTEMPTY = err("ENOTEMPTY"); | |
140 | val ELOOP = err("ELOOP"); | |
141 | val EWOULDBLOCK = err("EWOULDBLOCK"); | |
142 | val ENOMSG = err("ENOMSG"); | |
143 | val EIDRM = err("EIDRM"); | |
144 | val ECHRNG = err("ECHRNG"); | |
145 | val EL2NSYNC = err("EL2NSYNC"); | |
146 | val EL3HLT = err("EL3HLT"); | |
147 | val EL3RST = err("EL3RST"); | |
148 | val ELNRNG = err("ELNRNG"); | |
149 | val EUNATCH = err("EUNATCH"); | |
150 | val ENOCSI = err("ENOCSI"); | |
151 | val EL2HLT = err("EL2HLT"); | |
152 | val EBADE = err("EBADE"); | |
153 | val EBADR = err("EBADR"); | |
154 | val EXFULL = err("EXFULL"); | |
155 | val ENOANO = err("ENOANO"); | |
156 | val EBADRQC = err("EBADRQC"); | |
157 | val EBADSLT = err("EBADSLT"); | |
158 | val EDEADLOCK = err("EDEADLOCK"); | |
159 | val EBFONT = err("EBFONT"); | |
160 | val ENOSTR = err("ENOSTR"); | |
161 | val ENODATA = err("ENODATA"); | |
162 | val ETIME = err("ETIME"); | |
163 | val ENOSR = err("ENOSR"); | |
164 | val ENONET = err("ENONET"); | |
165 | val ENOPKG = err("ENOPKG"); | |
166 | val EREMOTE = err("EREMOTE"); | |
167 | val ENOLINK = err("ENOLINK"); | |
168 | val EADV = err("EADV"); | |
169 | val ESRMNT = err("ESRMNT"); | |
170 | val ECOMM = err("ECOMM"); | |
171 | val EPROTO = err("EPROTO"); | |
172 | val EMULTIHOP = err("EMULTIHOP"); | |
173 | val EDOTDOT = err("EDOTDOT"); | |
174 | val EBADMSG = err("EBADMSG"); | |
175 | val EOVERFLOW = err("EOVERFLOW"); | |
176 | val ENOTUNIQ = err("ENOTUNIQ"); | |
177 | val EBADFD = err("EBADFD"); | |
178 | val EREMCHG = err("EREMCHG"); | |
179 | val ELIBACC = err("ELIBACC"); | |
180 | val ELIBBAD = err("ELIBBAD"); | |
181 | val ELIBSCN = err("ELIBSCN"); | |
182 | val ELIBMAX = err("ELIBMAX"); | |
183 | val ELIBEXEC = err("ELIBEXEC"); | |
184 | val EILSEQ = err("EILSEQ"); | |
185 | val ERESTART = err("ERESTART"); | |
186 | val ESTRPIPE = err("ESTRPIPE"); | |
187 | val EUSERS = err("EUSERS"); | |
188 | val ENOTSOCK = err("ENOTSOCK"); | |
189 | val EDESTADDRREQ = err("EDESTADDRREQ"); | |
190 | val EMSGSIZE = err("EMSGSIZE"); | |
191 | val EPROTOTYPE = err("EPROTOTYPE"); | |
192 | val ENOPROTOOPT = err("ENOPROTOOPT"); | |
193 | val EPROTONOSUPPORT = err("EPROTONOSUPPORT"); | |
194 | val ESOCKTNOSUPPORT = err("ESOCKTNOSUPPORT"); | |
195 | val EOPNOTSUPP = err("EOPNOTSUPP"); | |
196 | val EPFNOSUPPORT = err("EPFNOSUPPORT"); | |
197 | val EAFNOSUPPORT = err("EAFNOSUPPORT"); | |
198 | val EADDRINUSE = err("EADDRINUSE"); | |
199 | val EADDRNOTAVAIL = err("EADDRNOTAVAIL"); | |
200 | val ENETDOWN = err("ENETDOWN"); | |
201 | val ENETUNREACH = err("ENETUNREACH"); | |
202 | val ENETRESET = err("ENETRESET"); | |
203 | val ECONNABORTED = err("ECONNABORTED"); | |
204 | val ECONNRESET = err("ECONNRESET"); | |
205 | val ENOBUFS = err("ENOBUFS"); | |
206 | val EISCONN = err("EISCONN"); | |
207 | val ENOTCONN = err("ENOTCONN"); | |
208 | val ESHUTDOWN = err("ESHUTDOWN"); | |
209 | val ETOOMANYREFS = err("ETOOMANYREFS"); | |
210 | val ETIMEDOUT = err("ETIMEDOUT"); | |
211 | val ECONNREFUSED = err("ECONNREFUSED"); | |
212 | val EHOSTDOWN = err("EHOSTDOWN"); | |
213 | val EHOSTUNREACH = err("EHOSTUNREACH"); | |
214 | val EALREADY = err("EALREADY"); | |
215 | val EINPROGRESS = err("EINPROGRESS"); | |
216 | val ESTALE = err("ESTALE"); | |
217 | val EUCLEAN = err("EUCLEAN"); | |
218 | val ENOTNAM = err("ENOTNAM"); | |
219 | val ENAVAIL = err("ENAVAIL"); | |
220 | val EISNAM = err("EISNAM"); | |
221 | val EREMOTEIO = err("EREMOTEIO"); | |
222 | val EDQUOT = err("EDQUOT"); | |
223 | val ENOMEDIUM = err("ENOMEDIUM"); | |
224 | val EMEDIUMTYPE = err("EMEDIUMTYPE"); | |
225 | val ECANCELED = err("ECANCELED"); | |
226 | val ENOKEY = err("ENOKEY"); | |
227 | val EKEYEXPIRED = err("EKEYEXPIRED"); | |
228 | val EKEYREVOKED = err("EKEYREVOKED"); | |
229 | val EKEYREJECTED = err("EKEYREJECTED"); | |
230 | val EOWNERDEAD = err("EOWNERDEAD"); | |
231 | val ENOTRECOVERABLE = err("ENOTRECOVERABLE"); | |
232 | val ERFKILL = err("ERFKILL"); | |
233 | val EHWPOISON = err("EHWPOISON"); | |
234 | /***end***/ | |
235 | } | |
236 | import Errno.{Value => _, _}; | |
237 | ||
238 | object SystemError { | |
239 | def apply(err: Errno.Value, what: String): SystemError = | |
240 | new SystemError(err, what); | |
241 | def unapply(e: Exception): Option[(Errno.Value, String)] = e match { | |
242 | case e: SystemError => Some((e.err, e.what)) | |
243 | case _ => None | |
244 | } | |
245 | } | |
246 | ||
247 | class SystemError private[this](val err: Errno.ErrnoVal, val what: String) | |
248 | extends Exception { | |
249 | def this(err: Errno.Value, what: String) | |
250 | { this(err.asInstanceOf[Errno.ErrnoVal], what); } | |
251 | private[tripe] def this(err: Int, what: CString) | |
252 | { this(Errno(err), what.toJString); } | |
253 | override def getMessage(): String = s"$what: ${err.message}"; | |
254 | } | |
255 | ||
256 | /*----- Filesystem hacks --------------------------------------------------*/ | |
257 | ||
258 | def freshFile(d: File): File = { | |
259 | /* Return the name of a freshly created file in directory D. */ | |
260 | ||
261 | val buf = new Array[Byte](6); | |
262 | val b = new StringBuilder; | |
263 | ||
264 | while (true) { | |
265 | /* Keep going until we find a fresh one. */ | |
266 | ||
267 | /* Provide a prefix. Mostly this is to prevent the file starting with | |
268 | * an unfortunate character like `-'. | |
269 | */ | |
270 | b ++= "tmp."; | |
271 | ||
272 | /* Generate some random bytes. */ | |
273 | rng.nextBytes(buf); | |
274 | ||
275 | /* Now turn the bytes into a filename. This is a cheesy implementation | |
276 | * of Base64 encoding. | |
277 | */ | |
278 | var a = 0; | |
279 | var n = 0; | |
280 | ||
281 | for (x <- buf) { | |
282 | a = (a << 8) | x; n += 8; | |
283 | while (n >= 6) { | |
284 | val y = (a >> n - 6)&0x3f; n -= 6; | |
285 | b += (if (y < 26) 'A' + y | |
286 | else if (y < 52) 'a' + (y - 26) | |
287 | else if (y < 62) '0' + (y - 52) | |
288 | else if (y == 62) '+' | |
289 | else '-').toChar; | |
290 | } | |
291 | } | |
292 | ||
293 | /* Make the filename, and try to create the file. If we succeed, we | |
294 | * win. | |
295 | */ | |
296 | val f = new File(d, b.result); b.clear(); | |
297 | try { jni.mkfile(f); return f; } | |
298 | catch { case SystemError(EEXIST, _) => (); } | |
299 | } | |
300 | ||
301 | /* We shouldn't get here, but the type checker needs placating. */ | |
302 | unreachable("unreachable"); | |
303 | } | |
304 | ||
305 | def rmTree(f: File) { | |
306 | def walk(f: File) { | |
307 | if (jni.stat(f).isdir) { | |
308 | closing(new jni.DirFilesIterator(f)) { _ foreach(walk _) } | |
309 | try { jni.rmdir(f); } | |
310 | catch { case SystemError(ENOENT, _) => (); } | |
311 | } else { | |
312 | try { jni.unlink(f); } | |
313 | catch { case SystemError(ENOENT, _) => (); } | |
314 | } | |
315 | } | |
316 | walk(f); | |
317 | } | |
318 | def rmTree(path: String) { rmTree(new File(path)); } | |
319 | ||
320 | def fileExists(path: String): Boolean = | |
321 | try { jni.stat(path); true } | |
322 | catch { case SystemError(ENOENT, _) => false }; | |
323 | def fileExists(file: File): Boolean = fileExists(file.getPath); | |
324 | ||
325 | /*----- That's all, folks -------------------------------------------------*/ | |
326 | ||
327 | } |