wip
[tripe-android] / sys.scala
CommitLineData
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
26package uk.org.distorted.tripe; package object sys {
27
28/*----- Imports -----------------------------------------------------------*/
29
30import scala.collection.mutable.HashSet;
31
32import java.io.File;
33
34import Magic._;
35
36/*----- Error codes -------------------------------------------------------*/
37
38object 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}
236import Errno.{Value => _, _};
237
238object 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
247class 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
258def 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
305def 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}
318def rmTree(path: String) { rmTree(new File(path)); }
319
320def fileExists(path: String): Boolean =
321 try { jni.stat(path); true }
322 catch { case SystemError(ENOENT, _) => false };
323def fileExists(file: File): Boolean = fileExists(file.getPath);
324
325/*----- That's all, folks -------------------------------------------------*/
326
327}