chiark / gitweb /
wip
[tripe-android] / sys.scala
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 }