chiark / gitweb /
wip
[tripe-android] / jni.c
1 /* -*-c-*-
2  *
3  * Native-code portions of the project
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 /*----- Header files ------------------------------------------------------*/
27
28 #define _FILE_OFFSET_BITS 64
29
30 #include <assert.h>
31 #include <ctype.h>
32 #include <errno.h>
33 #include <inttypes.h>
34 #include <stdarg.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38
39 #include <jni.h>
40
41 #include <sys/types.h>
42 #include <sys/socket.h>
43 #include <sys/stat.h>
44 #include <sys/sysmacros.h>
45 #include <sys/un.h>
46 #include <fcntl.h>
47 #include <unistd.h>
48 #include <dirent.h>
49
50 #include <mLib/align.h>
51 #include <mLib/bits.h>
52 #include <mLib/dstr.h>
53 #include <mLib/macros.h>
54
55 #include <catacomb/ghash.h>
56
57 #undef sun
58
59 /*----- Magic class names and similar -------------------------------------*/
60
61 /* The name decoration is horrific.  Hide it. */
62 #define JNIFUNC(f) Java_uk_org_distorted_tripe_jni_package_00024_##f
63
64 /* The little class for bundling up error codes. */
65 #define ERRENTRY "uk/org/distorted/tripe/jni/package$ErrorEntry"
66
67 /* The `stat' class. */
68 #define STAT "uk/org/distorted/tripe/jni/package$FileInfo"
69
70 /* Exception class names. */
71 #define NULLERR "java/lang/NullPointerException"
72 #define TYPEERR "uk/org/distorted/tripe/jni/package$NativeObjectTypeException"
73 #define SYSERR "uk/org/distorted/tripe/sys/package$SystemError"
74 #define ARGERR "java/lang/IllegalArgumentException"
75 #define BOUNDSERR "java/lang/IndexOutOfBoundsException"
76
77 /*----- Miscellaneous utilities -------------------------------------------*/
78
79 static void put_cstring(JNIEnv *jni, jbyteArray v, const char *p)
80   { if (p) (*jni)->ReleaseByteArrayElements(jni, v, (jbyte *)p, JNI_ABORT); }
81
82 static void vexcept(JNIEnv *jni, const char *clsname,
83                     const char *msg, va_list ap)
84 {
85   jclass cls;
86   int rc;
87   dstr d = DSTR_INIT;
88
89   cls = (*jni)->FindClass(jni, clsname); assert(cls);
90   if (!msg)
91     rc = (*jni)->ThrowNew(jni, cls, 0);
92   else {
93     dstr_vputf(&d, msg, &ap);
94     rc = (*jni)->ThrowNew(jni, cls, d.buf);
95     assert(!rc);
96     dstr_destroy(&d);
97   }
98   assert(!rc);
99 }
100
101 static void except(JNIEnv *jni, const char *clsname, const char *msg, ...)
102 {
103   va_list ap;
104
105   va_start(ap, msg);
106   vexcept(jni, clsname, msg, ap);
107   va_end(ap);
108 }
109
110 #ifdef DEBUG
111 static void dump_bytes(const void *p, size_t n, size_t o)
112 {
113   const unsigned char *q = p;
114   size_t i;
115
116   if (!n) return;
117   for (;;) {
118     fprintf(stderr, ";;   %08zx\n", o);
119     for (i = 0; i < 8; i++)
120       if (i < n) fprintf(stderr, "%02x ", q[i]);
121       else fprintf(stderr, "** ");
122     fprintf(stderr, ": ");
123     for (i = 0; i < 8; i++)
124       fputc(i >= n ? '*' : isprint(q[i]) ? q[i] : '.', stderr);
125     fputc('\n', stderr);
126     if (n <= 8) break;
127     q += 8; n -= 8;
128   }
129 }
130
131 static void dump_byte_array(JNIEnv *jni, const char *what, jbyteArray v)
132 {
133   jsize n;
134   jbyte *p;
135
136   fprintf(stderr, ";; %s\n", what);
137   if (!v) { fprintf(stderr, ";;   <null>\n"); return; }
138   n = (*jni)->GetArrayLength(jni, v);
139   p = (*jni)->GetByteArrayElements(jni, v, 0);
140   dump_bytes(p, n, 0);
141   (*jni)->ReleaseByteArrayElements(jni, v, p, JNI_ABORT);
142 }
143 #endif
144
145 static jbyteArray wrap_cstring(JNIEnv *jni, const char *p)
146 {
147   size_t n;
148   jbyteArray v;
149   jbyte *q;
150
151   if (!p) return (0);
152   n = strlen(p) + 1;
153   v = (*jni)->NewByteArray(jni, n); if (!v) return (0);
154   q = (*jni)->GetByteArrayElements(jni, v, 0); if (!q) return (0);
155   memcpy(q, p, n);
156   (*jni)->ReleaseByteArrayElements(jni, v, q, 0);
157   return (v);
158 }
159
160 static const char *get_cstring(JNIEnv *jni, jbyteArray v)
161 {
162   if (!v) { except(jni, NULLERR, 0); return (0); }
163   return ((const char *)(*jni)->GetByteArrayElements(jni, v, 0));
164 }
165
166 static void vexcept_syserror(JNIEnv *jni, const char *clsname,
167                              int err, const char *msg, va_list ap)
168 {
169   jclass cls;
170   int rc;
171   dstr d = DSTR_INIT;
172   jbyteArray msgstr;
173   jthrowable e;
174   jmethodID init;
175
176   cls = (*jni)->FindClass(jni, clsname); assert(cls);
177   init = (*jni)->GetMethodID(jni, cls, "<init>", "(I[B)V"); assert(init);
178   dstr_vputf(&d, msg, &ap);
179   msgstr = wrap_cstring(jni, d.buf); assert(msgstr);
180   dstr_destroy(&d);
181   e = (*jni)->NewObject(jni, cls, init, err, msgstr); assert(e);
182   rc = (*jni)->Throw(jni, e); assert(!rc);
183 }
184
185 static void except_syserror(JNIEnv *jni, const char *clsname,
186                             int err, const char *msg, ...)
187 {
188   va_list ap;
189
190   va_start(ap, msg);
191   vexcept_syserror(jni, clsname, err, msg, ap);
192   va_end(ap);
193 }
194
195 /*----- Wrapping native types ---------------------------------------------*/
196
197 /* There's no way defined in the JNI to stash a C pointer in a Java object.
198  * It seems that the usual approach is to cast to `jlong', but this is
199  * clearly unsatisfactory.  Instead, we store structures as Java byte arrays,
200  * with a 32-bit tag on the front.
201  */
202
203 struct native_type {
204   const char *name;
205   size_t sz;
206   uint32 tag;
207 };
208
209 typedef jbyteArray wrapper;
210
211 struct native_base {
212   uint32 tag;
213 };
214
215 static int unwrap(JNIEnv *jni, void *p,
216                   const struct native_type *ty, wrapper w)
217 {
218   jbyte *q;
219   jclass cls;
220   struct native_base *b = p;
221   jsize n;
222
223   if (!w) { except(jni, NULLERR, 0); return (-1); }
224   cls = (*jni)->FindClass(jni, "[B"); assert(cls);
225   if (!(*jni)->IsInstanceOf(jni, w, cls)) {
226     except(jni, TYPEERR,
227            "corrupted native object wrapper: expected a byte array");
228     return (-1);
229   }
230   n = (*jni)->GetArrayLength(jni, w);
231   if (n != ty->sz) {
232     except(jni, TYPEERR,
233            "corrupted native object wrapper: wrong size for `%s'",
234            ty->name);
235     return (-1);
236   }
237   q = (*jni)->GetByteArrayElements(jni, w, 0); if (!q) return (-1);
238   memcpy(b, q, ty->sz);
239   (*jni)->ReleaseByteArrayElements(jni, w, q, JNI_ABORT);
240   if (b->tag != ty->tag) {
241     except(jni, TYPEERR,
242            "corrupted native object wrapper: expected tag for `%s'",
243            ty->name);
244     return (-1);
245   }
246   return (0);
247 }
248
249 static int update_wrapper(JNIEnv *jni, const struct native_type *ty,
250                           wrapper w, const void *p)
251 {
252   jbyte *q;
253
254   q = (*jni)->GetByteArrayElements(jni, w, 0); if (!q) return (-1);
255   memcpy(q, p, ty->sz);
256   (*jni)->ReleaseByteArrayElements(jni, w, q, 0);
257   return (0);
258 }
259
260 static wrapper wrap(JNIEnv *jni, const struct native_type *ty, const void *p)
261 {
262   wrapper w;
263
264   w = (*jni)->NewByteArray(jni, ty->sz); if (!w) return (0);
265   if (update_wrapper(jni, ty, w, p)) return (0);
266   return (w);
267 }
268
269 #define INIT_NATIVE(type, p) do (p)->_base.tag = type##_type.tag; while (0)
270
271 /*----- Crypto information ------------------------------------------------*/
272
273 JNIEXPORT jint JNICALL JNIFUNC(hashsz)(JNIEnv *jni, jobject cls,
274                                        jstring hnamestr)
275 {
276   jint rc = -1;
277   const char *hname;
278   const gchash *hc;
279
280   hname = (*jni)->GetStringUTFChars(jni, hnamestr, 0);
281   if (!hname) goto end;
282   hc = ghash_byname(hname); if (!hc) goto end;
283   rc = hc->hashsz;
284
285 end:
286   if (hname) (*jni)->ReleaseStringUTFChars(jni, hnamestr, hname);
287   return (rc);
288 }
289
290 /*----- System errors -----------------------------------------------------*/
291
292 static const struct errtab { const char *tag; int err; } errtab[] = {
293   /*
294      ;;; The errno name table is very boring to type.  To make life less
295      ;;; awful, put the errno names in this list and evaluate the code to
296      ;;; get Emacs to regenerate it.
297
298      (let ((errors '(EPERM ENOENT ESRCH EINTR EIO ENXIO E2BIG ENOEXEC EBADF
299                      ECHILD EAGAIN ENOMEM EACCES EFAULT ENOTBLK EBUSY EEXIST
300                      EXDEV ENODEV ENOTDIR EISDIR EINVAL ENFILE EMFILE ENOTTY
301                      ETXTBSY EFBIG ENOSPC ESPIPE EROFS EMLINK EPIPE EDOM
302                      ERANGE
303
304                      EDEADLK ENAMETOOLONG ENOLCK ENOSYS ENOTEMPTY ELOOP
305                      EWOULDBLOCK ENOMSG EIDRM ECHRNG EL2NSYNC EL3HLT EL3RST
306                      ELNRNG EUNATCH ENOCSI EL2HLT EBADE EBADR EXFULL ENOANO
307                      EBADRQC EBADSLT EDEADLOCK EBFONT ENOSTR ENODATA ETIME
308                      ENOSR ENONET ENOPKG EREMOTE ENOLINK EADV ESRMNT ECOMM
309                      EPROTO EMULTIHOP EDOTDOT EBADMSG EOVERFLOW ENOTUNIQ
310                      EBADFD EREMCHG ELIBACC ELIBBAD ELIBSCN ELIBMAX ELIBEXEC
311                      EILSEQ ERESTART ESTRPIPE EUSERS ENOTSOCK EDESTADDRREQ
312                      EMSGSIZE EPROTOTYPE ENOPROTOOPT EPROTONOSUPPORT
313                      ESOCKTNOSUPPORT EOPNOTSUPP EPFNOSUPPORT EAFNOSUPPORT
314                      EADDRINUSE EADDRNOTAVAIL ENETDOWN ENETUNREACH ENETRESET
315                      ECONNABORTED ECONNRESET ENOBUFS EISCONN ENOTCONN
316                      ESHUTDOWN ETOOMANYREFS ETIMEDOUT ECONNREFUSED EHOSTDOWN
317                      EHOSTUNREACH EALREADY EINPROGRESS ESTALE EUCLEAN ENOTNAM
318                      ENAVAIL EISNAM EREMOTEIO EDQUOT ENOMEDIUM EMEDIUMTYPE
319                      ECANCELED ENOKEY EKEYEXPIRED EKEYREVOKED EKEYREJECTED
320                      EOWNERDEAD ENOTRECOVERABLE ERFKILL EHWPOISON)))
321        (save-excursion
322          (goto-char (point-min))
323          (search-forward (concat "***" "BEGIN errtab" "***"))
324          (beginning-of-line 2)
325          (delete-region (point)
326                         (progn
327                           (search-forward "***END***")
328                           (beginning-of-line)
329                           (point)))
330          (dolist (err errors)
331            (insert (format "#ifdef %s\n  { \"%s\", %s },\n#endif\n"
332                            err err err)))))
333   */
334   /***BEGIN errtab***/
335 #ifdef EPERM
336   { "EPERM", EPERM },
337 #endif
338 #ifdef ENOENT
339   { "ENOENT", ENOENT },
340 #endif
341 #ifdef ESRCH
342   { "ESRCH", ESRCH },
343 #endif
344 #ifdef EINTR
345   { "EINTR", EINTR },
346 #endif
347 #ifdef EIO
348   { "EIO", EIO },
349 #endif
350 #ifdef ENXIO
351   { "ENXIO", ENXIO },
352 #endif
353 #ifdef E2BIG
354   { "E2BIG", E2BIG },
355 #endif
356 #ifdef ENOEXEC
357   { "ENOEXEC", ENOEXEC },
358 #endif
359 #ifdef EBADF
360   { "EBADF", EBADF },
361 #endif
362 #ifdef ECHILD
363   { "ECHILD", ECHILD },
364 #endif
365 #ifdef EAGAIN
366   { "EAGAIN", EAGAIN },
367 #endif
368 #ifdef ENOMEM
369   { "ENOMEM", ENOMEM },
370 #endif
371 #ifdef EACCES
372   { "EACCES", EACCES },
373 #endif
374 #ifdef EFAULT
375   { "EFAULT", EFAULT },
376 #endif
377 #ifdef ENOTBLK
378   { "ENOTBLK", ENOTBLK },
379 #endif
380 #ifdef EBUSY
381   { "EBUSY", EBUSY },
382 #endif
383 #ifdef EEXIST
384   { "EEXIST", EEXIST },
385 #endif
386 #ifdef EXDEV
387   { "EXDEV", EXDEV },
388 #endif
389 #ifdef ENODEV
390   { "ENODEV", ENODEV },
391 #endif
392 #ifdef ENOTDIR
393   { "ENOTDIR", ENOTDIR },
394 #endif
395 #ifdef EISDIR
396   { "EISDIR", EISDIR },
397 #endif
398 #ifdef EINVAL
399   { "EINVAL", EINVAL },
400 #endif
401 #ifdef ENFILE
402   { "ENFILE", ENFILE },
403 #endif
404 #ifdef EMFILE
405   { "EMFILE", EMFILE },
406 #endif
407 #ifdef ENOTTY
408   { "ENOTTY", ENOTTY },
409 #endif
410 #ifdef ETXTBSY
411   { "ETXTBSY", ETXTBSY },
412 #endif
413 #ifdef EFBIG
414   { "EFBIG", EFBIG },
415 #endif
416 #ifdef ENOSPC
417   { "ENOSPC", ENOSPC },
418 #endif
419 #ifdef ESPIPE
420   { "ESPIPE", ESPIPE },
421 #endif
422 #ifdef EROFS
423   { "EROFS", EROFS },
424 #endif
425 #ifdef EMLINK
426   { "EMLINK", EMLINK },
427 #endif
428 #ifdef EPIPE
429   { "EPIPE", EPIPE },
430 #endif
431 #ifdef EDOM
432   { "EDOM", EDOM },
433 #endif
434 #ifdef ERANGE
435   { "ERANGE", ERANGE },
436 #endif
437 #ifdef EDEADLK
438   { "EDEADLK", EDEADLK },
439 #endif
440 #ifdef ENAMETOOLONG
441   { "ENAMETOOLONG", ENAMETOOLONG },
442 #endif
443 #ifdef ENOLCK
444   { "ENOLCK", ENOLCK },
445 #endif
446 #ifdef ENOSYS
447   { "ENOSYS", ENOSYS },
448 #endif
449 #ifdef ENOTEMPTY
450   { "ENOTEMPTY", ENOTEMPTY },
451 #endif
452 #ifdef ELOOP
453   { "ELOOP", ELOOP },
454 #endif
455 #ifdef EWOULDBLOCK
456   { "EWOULDBLOCK", EWOULDBLOCK },
457 #endif
458 #ifdef ENOMSG
459   { "ENOMSG", ENOMSG },
460 #endif
461 #ifdef EIDRM
462   { "EIDRM", EIDRM },
463 #endif
464 #ifdef ECHRNG
465   { "ECHRNG", ECHRNG },
466 #endif
467 #ifdef EL2NSYNC
468   { "EL2NSYNC", EL2NSYNC },
469 #endif
470 #ifdef EL3HLT
471   { "EL3HLT", EL3HLT },
472 #endif
473 #ifdef EL3RST
474   { "EL3RST", EL3RST },
475 #endif
476 #ifdef ELNRNG
477   { "ELNRNG", ELNRNG },
478 #endif
479 #ifdef EUNATCH
480   { "EUNATCH", EUNATCH },
481 #endif
482 #ifdef ENOCSI
483   { "ENOCSI", ENOCSI },
484 #endif
485 #ifdef EL2HLT
486   { "EL2HLT", EL2HLT },
487 #endif
488 #ifdef EBADE
489   { "EBADE", EBADE },
490 #endif
491 #ifdef EBADR
492   { "EBADR", EBADR },
493 #endif
494 #ifdef EXFULL
495   { "EXFULL", EXFULL },
496 #endif
497 #ifdef ENOANO
498   { "ENOANO", ENOANO },
499 #endif
500 #ifdef EBADRQC
501   { "EBADRQC", EBADRQC },
502 #endif
503 #ifdef EBADSLT
504   { "EBADSLT", EBADSLT },
505 #endif
506 #ifdef EDEADLOCK
507   { "EDEADLOCK", EDEADLOCK },
508 #endif
509 #ifdef EBFONT
510   { "EBFONT", EBFONT },
511 #endif
512 #ifdef ENOSTR
513   { "ENOSTR", ENOSTR },
514 #endif
515 #ifdef ENODATA
516   { "ENODATA", ENODATA },
517 #endif
518 #ifdef ETIME
519   { "ETIME", ETIME },
520 #endif
521 #ifdef ENOSR
522   { "ENOSR", ENOSR },
523 #endif
524 #ifdef ENONET
525   { "ENONET", ENONET },
526 #endif
527 #ifdef ENOPKG
528   { "ENOPKG", ENOPKG },
529 #endif
530 #ifdef EREMOTE
531   { "EREMOTE", EREMOTE },
532 #endif
533 #ifdef ENOLINK
534   { "ENOLINK", ENOLINK },
535 #endif
536 #ifdef EADV
537   { "EADV", EADV },
538 #endif
539 #ifdef ESRMNT
540   { "ESRMNT", ESRMNT },
541 #endif
542 #ifdef ECOMM
543   { "ECOMM", ECOMM },
544 #endif
545 #ifdef EPROTO
546   { "EPROTO", EPROTO },
547 #endif
548 #ifdef EMULTIHOP
549   { "EMULTIHOP", EMULTIHOP },
550 #endif
551 #ifdef EDOTDOT
552   { "EDOTDOT", EDOTDOT },
553 #endif
554 #ifdef EBADMSG
555   { "EBADMSG", EBADMSG },
556 #endif
557 #ifdef EOVERFLOW
558   { "EOVERFLOW", EOVERFLOW },
559 #endif
560 #ifdef ENOTUNIQ
561   { "ENOTUNIQ", ENOTUNIQ },
562 #endif
563 #ifdef EBADFD
564   { "EBADFD", EBADFD },
565 #endif
566 #ifdef EREMCHG
567   { "EREMCHG", EREMCHG },
568 #endif
569 #ifdef ELIBACC
570   { "ELIBACC", ELIBACC },
571 #endif
572 #ifdef ELIBBAD
573   { "ELIBBAD", ELIBBAD },
574 #endif
575 #ifdef ELIBSCN
576   { "ELIBSCN", ELIBSCN },
577 #endif
578 #ifdef ELIBMAX
579   { "ELIBMAX", ELIBMAX },
580 #endif
581 #ifdef ELIBEXEC
582   { "ELIBEXEC", ELIBEXEC },
583 #endif
584 #ifdef EILSEQ
585   { "EILSEQ", EILSEQ },
586 #endif
587 #ifdef ERESTART
588   { "ERESTART", ERESTART },
589 #endif
590 #ifdef ESTRPIPE
591   { "ESTRPIPE", ESTRPIPE },
592 #endif
593 #ifdef EUSERS
594   { "EUSERS", EUSERS },
595 #endif
596 #ifdef ENOTSOCK
597   { "ENOTSOCK", ENOTSOCK },
598 #endif
599 #ifdef EDESTADDRREQ
600   { "EDESTADDRREQ", EDESTADDRREQ },
601 #endif
602 #ifdef EMSGSIZE
603   { "EMSGSIZE", EMSGSIZE },
604 #endif
605 #ifdef EPROTOTYPE
606   { "EPROTOTYPE", EPROTOTYPE },
607 #endif
608 #ifdef ENOPROTOOPT
609   { "ENOPROTOOPT", ENOPROTOOPT },
610 #endif
611 #ifdef EPROTONOSUPPORT
612   { "EPROTONOSUPPORT", EPROTONOSUPPORT },
613 #endif
614 #ifdef ESOCKTNOSUPPORT
615   { "ESOCKTNOSUPPORT", ESOCKTNOSUPPORT },
616 #endif
617 #ifdef EOPNOTSUPP
618   { "EOPNOTSUPP", EOPNOTSUPP },
619 #endif
620 #ifdef EPFNOSUPPORT
621   { "EPFNOSUPPORT", EPFNOSUPPORT },
622 #endif
623 #ifdef EAFNOSUPPORT
624   { "EAFNOSUPPORT", EAFNOSUPPORT },
625 #endif
626 #ifdef EADDRINUSE
627   { "EADDRINUSE", EADDRINUSE },
628 #endif
629 #ifdef EADDRNOTAVAIL
630   { "EADDRNOTAVAIL", EADDRNOTAVAIL },
631 #endif
632 #ifdef ENETDOWN
633   { "ENETDOWN", ENETDOWN },
634 #endif
635 #ifdef ENETUNREACH
636   { "ENETUNREACH", ENETUNREACH },
637 #endif
638 #ifdef ENETRESET
639   { "ENETRESET", ENETRESET },
640 #endif
641 #ifdef ECONNABORTED
642   { "ECONNABORTED", ECONNABORTED },
643 #endif
644 #ifdef ECONNRESET
645   { "ECONNRESET", ECONNRESET },
646 #endif
647 #ifdef ENOBUFS
648   { "ENOBUFS", ENOBUFS },
649 #endif
650 #ifdef EISCONN
651   { "EISCONN", EISCONN },
652 #endif
653 #ifdef ENOTCONN
654   { "ENOTCONN", ENOTCONN },
655 #endif
656 #ifdef ESHUTDOWN
657   { "ESHUTDOWN", ESHUTDOWN },
658 #endif
659 #ifdef ETOOMANYREFS
660   { "ETOOMANYREFS", ETOOMANYREFS },
661 #endif
662 #ifdef ETIMEDOUT
663   { "ETIMEDOUT", ETIMEDOUT },
664 #endif
665 #ifdef ECONNREFUSED
666   { "ECONNREFUSED", ECONNREFUSED },
667 #endif
668 #ifdef EHOSTDOWN
669   { "EHOSTDOWN", EHOSTDOWN },
670 #endif
671 #ifdef EHOSTUNREACH
672   { "EHOSTUNREACH", EHOSTUNREACH },
673 #endif
674 #ifdef EALREADY
675   { "EALREADY", EALREADY },
676 #endif
677 #ifdef EINPROGRESS
678   { "EINPROGRESS", EINPROGRESS },
679 #endif
680 #ifdef ESTALE
681   { "ESTALE", ESTALE },
682 #endif
683 #ifdef EUCLEAN
684   { "EUCLEAN", EUCLEAN },
685 #endif
686 #ifdef ENOTNAM
687   { "ENOTNAM", ENOTNAM },
688 #endif
689 #ifdef ENAVAIL
690   { "ENAVAIL", ENAVAIL },
691 #endif
692 #ifdef EISNAM
693   { "EISNAM", EISNAM },
694 #endif
695 #ifdef EREMOTEIO
696   { "EREMOTEIO", EREMOTEIO },
697 #endif
698 #ifdef EDQUOT
699   { "EDQUOT", EDQUOT },
700 #endif
701 #ifdef ENOMEDIUM
702   { "ENOMEDIUM", ENOMEDIUM },
703 #endif
704 #ifdef EMEDIUMTYPE
705   { "EMEDIUMTYPE", EMEDIUMTYPE },
706 #endif
707 #ifdef ECANCELED
708   { "ECANCELED", ECANCELED },
709 #endif
710 #ifdef ENOKEY
711   { "ENOKEY", ENOKEY },
712 #endif
713 #ifdef EKEYEXPIRED
714   { "EKEYEXPIRED", EKEYEXPIRED },
715 #endif
716 #ifdef EKEYREVOKED
717   { "EKEYREVOKED", EKEYREVOKED },
718 #endif
719 #ifdef EKEYREJECTED
720   { "EKEYREJECTED", EKEYREJECTED },
721 #endif
722 #ifdef EOWNERDEAD
723   { "EOWNERDEAD", EOWNERDEAD },
724 #endif
725 #ifdef ENOTRECOVERABLE
726   { "ENOTRECOVERABLE", ENOTRECOVERABLE },
727 #endif
728 #ifdef ERFKILL
729   { "ERFKILL", ERFKILL },
730 #endif
731 #ifdef EHWPOISON
732   { "EHWPOISON", EHWPOISON },
733 #endif
734   /***END***/
735 };
736
737 JNIEXPORT jobject JNIFUNC(errtab)(JNIEnv *jni, jobject cls)
738 {
739   size_t i;
740   jclass eltcls;
741   jarray v;
742   jmethodID init;
743   jobject e;
744
745   eltcls =
746     (*jni)->FindClass(jni, ERRENTRY);
747   assert(eltcls);
748   v = (*jni)->NewObjectArray(jni, N(errtab), eltcls, 0); if (!v) return (0);
749   init = (*jni)->GetMethodID(jni, eltcls, "<init>",
750                              "(Ljava/lang/String;I)V");
751   assert(init);
752
753   for (i = 0; i < N(errtab); i++) {
754     e = (*jni)->NewObject(jni, eltcls, init,
755                           (*jni)->NewStringUTF(jni, errtab[i].tag),
756                           errtab[i].err);
757     (*jni)->SetObjectArrayElement(jni, v, i, e);
758   }
759   return (v);
760 }
761
762 JNIEXPORT jobject JNIFUNC(strerror)(JNIEnv *jni, jobject cls, jint err)
763   { return (wrap_cstring(jni, strerror(err))); }
764
765 /*----- Low-level file operations -----------------------------------------*/
766
767 /* Java has these already, as methods on `java.io.File' objects.  Alas, these
768  * methods are useless at reporting errors: they tend to return a `boolean'
769  * success/ fail indicator, and throw away any more detailed information.
770  * There's better functionality in `java.nio.file.Files', but that only turns
771  * up in Android API 26 (in 7.0 Nougat).  There's `android.system.Os', which
772  * has a bunch of POSIX-shaped functions -- but they're only in Android API
773  * 21 (in 5.0 Lollipop), and there's nothing in the support library to help.
774  *
775  * So the other option is to implement them ourselves.
776  */
777
778 JNIEXPORT void JNIFUNC(unlink)(JNIEnv *jni, jobject cls, jobject path)
779 {
780   const char *pathstr = 0;
781
782   pathstr = get_cstring(jni, path); if (!pathstr) goto end;
783   if (unlink(pathstr)) {
784     except_syserror(jni, SYSERR, errno,
785                     "failed to delete file `%s'", pathstr);
786     goto end;
787   }
788 end:
789   put_cstring(jni, path, pathstr);
790 }
791
792 JNIEXPORT void JNIFUNC(rmdir)(JNIEnv *jni, jobject cls, jobject path)
793 {
794   const char *pathstr = 0;
795
796   pathstr = get_cstring(jni, path); if (!pathstr) goto end;
797   if (rmdir(pathstr)) {
798     except_syserror(jni, SYSERR, errno,
799                     "failed to delete directory `%s'", pathstr);
800     goto end;
801   }
802 end:
803   put_cstring(jni, path, pathstr);
804 }
805
806 JNIEXPORT void JNIFUNC(mkdir)(JNIEnv *jni, jobject cls,
807                               jobject path, jint mode)
808 {
809   const char *pathstr = 0;
810
811   pathstr = get_cstring(jni, path); if (!pathstr) goto end;
812   if (mkdir(pathstr, mode)) {
813     except_syserror(jni, SYSERR, errno,
814                     "failed to create directory `%s'", pathstr);
815     goto end;
816   }
817 end:
818   put_cstring(jni, path, pathstr);
819 }
820
821 JNIEXPORT void JNIFUNC(mkfile)(JNIEnv *jni, jobject cls,
822                             jobject path, jint mode)
823 {
824   const char *pathstr = 0;
825   int fd = -1;
826
827   pathstr = get_cstring(jni, path); if (!pathstr) goto end;
828   fd = open(pathstr, O_WRONLY | O_CREAT | O_EXCL, mode);
829   if (fd < 0) {
830     except_syserror(jni, SYSERR, errno,
831                     "failed to create fresh file `%s'", pathstr);
832     goto end;
833   }
834 end:
835   if (fd != -1) close(fd);
836   put_cstring(jni, path, pathstr);
837 }
838
839 JNIEXPORT void JNIFUNC(rename)(JNIEnv *jni, jobject cls,
840                                jobject from, jobject to)
841 {
842   const char *fromstr = 0, *tostr = 0;
843
844   fromstr = get_cstring(jni, from); if (!fromstr) goto end;
845   tostr = get_cstring(jni, to); if (!tostr) goto end;
846   if (rename(fromstr, tostr)) {
847     except_syserror(jni, SYSERR, errno,
848                     "failed to rename `%s' as `%s'", fromstr, tostr);
849     goto end;
850   }
851 end:
852   put_cstring(jni, from, fromstr);
853   put_cstring(jni, to, tostr);
854 }
855
856 #define LKF_EXCL 1u
857 #define LKF_WAIT 2u
858 struct lockf {
859   struct native_base _base;
860   int fd;
861 };
862 static struct native_type lockf_type =
863         { "lock", sizeof(struct lockf), 0xb2648926};
864 JNIEXPORT wrapper JNIFUNC(lock)(JNIEnv *jni, jobject cls,
865                                 jobject path, jint flags)
866 {
867   const char *pathstr = 0;
868   int fd = -1;
869   struct flock l;
870   struct lockf lk;
871   struct stat st0, st1;
872   int f;
873   wrapper r = 0;
874
875   pathstr = get_cstring(jni, path); if (!pathstr) goto end;
876
877 again:
878   fd = open(pathstr, O_RDWR | O_CREAT); if (fd < 0) goto err;
879   if (fstat(fd, &st0)) goto err;
880   f = fcntl(fd, F_GETFD); if (f < 0) goto err;
881   if (fcntl(fd, F_SETFD, f | FD_CLOEXEC)) goto err;
882   l.l_type = (flags&LKF_EXCL) ? F_WRLCK : F_RDLCK;
883   l.l_whence = SEEK_SET;
884   l.l_start = 0;
885   l.l_len = 0;
886   if (fcntl(fd, (flags&LKF_WAIT) ? F_SETLKW : F_SETLK, &l)) goto err;
887   if (stat(pathstr, &st1))
888     { if (errno == ENOENT) goto again; else goto err; }
889   if (st0.st_dev != st1.st_dev || st0.st_ino != st1.st_ino)
890     { close(fd); fd = -1; goto again; }
891
892   INIT_NATIVE(lockf, &lk); lk.fd = fd; fd = -1;
893   r = wrap(jni, &lockf_type, &lk);
894   goto end;
895
896 err:
897   except_syserror(jni, SYSERR, errno, "failed to lock file `%s'", pathstr);
898 end:
899   if (fd != -1) close(fd);
900   put_cstring(jni, path, pathstr);
901   return (r);
902 }
903
904 JNIEXPORT void JNIFUNC(unlock)(JNIEnv *jni, jobject cls, wrapper wlk)
905 {
906   struct lockf lk;
907   struct flock l;
908   int rc;
909
910   if (unwrap(jni, &lk, &lockf_type, wlk)) goto end;
911   if (lk.fd == -1) goto end;
912   l.l_type = F_UNLCK;
913   l.l_whence = SEEK_SET;
914   l.l_start = 0;
915   l.l_len = 0;
916   if (fcntl(lk.fd, F_SETLK, &l)) goto end;
917   close(lk.fd); lk.fd = -1;
918   rc = update_wrapper(jni, &lockf_type, wlk, &lk); assert(!rc);
919 end:;
920 }
921
922 static jlong xlttimespec(const struct timespec *ts)
923   { return (1000*(jlong)ts->tv_sec + ts->tv_nsec/1000000); }
924
925 static jobject xltstat(JNIEnv *jni, const struct stat *st)
926 {
927   jclass cls;
928   jmethodID init;
929   jint modehack;
930  
931   modehack = st->st_mode&07777;
932   if (S_ISFIFO(st->st_mode)) modehack |= 0010000;
933   else if (S_ISCHR(st->st_mode)) modehack |= 0020000;
934   else if (S_ISDIR(st->st_mode)) modehack |= 0040000;
935   else if (S_ISBLK(st->st_mode)) modehack |= 0060000;
936   else if (S_ISREG(st->st_mode)) modehack |= 0100000;
937   else if (S_ISLNK(st->st_mode)) modehack |= 0120000;
938   else if (S_ISSOCK(st->st_mode)) modehack |= 0140000;
939
940   cls = (*jni)->FindClass(jni, STAT); assert(cls);
941   init = (*jni)->GetMethodID(jni, cls, "<init>", "(IIJIIIIIIJIJJJJ)V");
942   assert(init);
943   return ((*jni)->NewObject(jni, cls, init,
944                             (jint)major(st->st_dev), (jint)minor(st->st_dev),
945                             (jlong)st->st_ino,
946                             modehack,
947                             (jint)st->st_nlink,
948                             (jint)st->st_uid, (jint)st->st_gid,
949                             (jint)major(st->st_rdev), (jint)minor(st->st_rdev),
950                             (jlong)st->st_size,
951                             (jint)st->st_blksize, (jlong)st->st_blocks,
952                             xlttimespec(&st->st_atim),
953                             xlttimespec(&st->st_mtim),
954                             xlttimespec(&st->st_ctim)));
955 }
956
957 JNIEXPORT jobject JNIFUNC(stat)(JNIEnv *jni, jobject cls, jobject path)
958 {
959   jobject r = 0;
960   const char *pathstr = 0;
961   struct stat st;
962
963   pathstr = get_cstring(jni, path); if (!pathstr) goto end;
964   if (stat(pathstr, &st)) {
965     except_syserror(jni, SYSERR, errno,
966                     "failed to read information about `%s'", pathstr);
967     goto end;
968   }
969   r = xltstat(jni, &st);
970 end:
971   put_cstring(jni, path, pathstr);
972   return (r);
973 }
974
975 JNIEXPORT jobject JNIFUNC(lstat)(JNIEnv *jni, jobject cls, jobject path)
976 {
977   jobject r = 0;
978   const char *pathstr = 0;
979   struct stat st;
980
981   pathstr = get_cstring(jni, path); if (!pathstr) goto end;
982   if (lstat(pathstr, &st)) {
983     except_syserror(jni, SYSERR, errno,
984                     "failed to read information about `%s'", pathstr);
985     goto end;
986   }
987   r = xltstat(jni, &st);
988 end:
989   put_cstring(jni, path, pathstr);
990   return (r);
991 }
992
993 struct dir {
994   struct native_base _base;
995   DIR *d;
996 };
997 static const struct native_type dir_type =
998         { "dir", sizeof(struct dir), 0x0f5ca477 };
999
1000 JNIEXPORT jobject JNIFUNC(opendir)(JNIEnv *jni, jobject cls, jobject path)
1001 {
1002   const char *pathstr = 0;
1003   struct dir dir;
1004   wrapper r = 0;
1005
1006   pathstr = get_cstring(jni, path); if (!pathstr) goto end;
1007   INIT_NATIVE(dir, &dir);
1008   dir.d = opendir(pathstr);
1009   if (!dir.d) {
1010     except_syserror(jni, SYSERR, errno,
1011                     "failed to open directory `%s'", pathstr);
1012     goto end;
1013   }
1014   r = wrap(jni, &dir_type, &dir);
1015 end:
1016   put_cstring(jni, path, pathstr);
1017   return (r);
1018 }
1019
1020 JNIEXPORT jbyteArray JNIFUNC(readdir)(JNIEnv *jni, jobject cls,
1021                                       jobject path, jobject wdir)
1022 {
1023   const char *pathstr = 0;
1024   struct dir dir;
1025   struct dirent *d;
1026   jbyteArray r = 0;
1027
1028   if (unwrap(jni, &dir, &dir_type, wdir)) goto end;
1029   if (!dir.d) { except(jni, ARGERR, "directory has been closed"); goto end; }
1030   errno = 0; d = readdir(dir.d);
1031   if (errno) {
1032     pathstr = get_cstring(jni, path); if (!pathstr) goto end;
1033     except_syserror(jni, SYSERR, errno,
1034                     "failed to read directory `%s'", pathstr);
1035     goto end;
1036   }
1037   if (d) r = wrap_cstring(jni, d->d_name);
1038 end:
1039   put_cstring(jni, path, pathstr);
1040   return (r);
1041 }
1042
1043 JNIEXPORT void JNIFUNC(closedir)(JNIEnv *jni, jobject cls,
1044                                  jobject path, jobject wdir)
1045 {
1046   const char *pathstr = 0;
1047   struct dir dir;
1048
1049   if (unwrap(jni, &dir, &dir_type, wdir)) goto end;
1050   if (!dir.d) goto end;
1051   if (closedir(dir.d)) {
1052     pathstr = get_cstring(jni, path); if (!pathstr) goto end;
1053     except_syserror(jni, SYSERR, errno,
1054                     "failed to close directory `%s'", pathstr);
1055     goto end;
1056   }
1057   dir.d = 0;
1058   if (update_wrapper(jni, &dir_type, wdir, &dir)) goto end;
1059 end:
1060   put_cstring(jni, path, pathstr);
1061 }
1062
1063 /*----- A server connection, using a Unix-domain socket -------------------*/
1064
1065 struct conn {
1066   struct native_base _base;
1067   int fd;
1068   unsigned f;
1069 #define CF_CLOSERD 1u
1070 #define CF_CLOSEWR 2u
1071 #define CF_CLOSEMASK (CF_CLOSERD | CF_CLOSEWR)
1072 };
1073 static const struct native_type conn_type =
1074         { "conn", sizeof(struct conn), 0xed030167 };
1075
1076 JNIEXPORT wrapper JNICALL JNIFUNC(connect)(JNIEnv *jni, jobject cls,
1077                                            jobject path)
1078 {
1079   struct conn conn;
1080   struct sockaddr_un sun;
1081   const char *pathstr = 0;
1082   jobject ret = 0;
1083   int fd = -1;
1084
1085   pathstr = get_cstring(jni, path); if (!pathstr) goto end;
1086   if (strlen(pathstr) >= sizeof(sun.sun_path)) {
1087     except(jni, ARGERR,
1088            "Unix-domain socket path `%s' too long", pathstr);
1089     goto end;
1090   }
1091
1092   INIT_NATIVE(conn, &conn);
1093   fd = socket(SOCK_STREAM, PF_UNIX, 0); if (fd < 0) goto err;
1094
1095   sun.sun_family = AF_UNIX;
1096   strcpy(sun.sun_path, (char *)pathstr);
1097   if (connect(fd, (struct sockaddr *)&sun, sizeof(sun))) goto err;
1098
1099   conn.fd = fd; fd = -1;
1100   conn.f = 0;
1101   ret = wrap(jni, &conn_type, &conn);
1102   goto end;
1103
1104 err:
1105   except_syserror(jni, SYSERR, errno,
1106                   "failed to connect to Unix-domain socket `%s'", pathstr);
1107 end:
1108   if (fd == -1) close(fd);
1109   put_cstring(jni, path, pathstr);
1110   return (ret);
1111 }
1112
1113 static int check_buffer_bounds(JNIEnv *jni, const char *what,
1114                                jbyteArray buf, jint start, jint len)
1115 {
1116   jsize bufsz;
1117   jclass cls;
1118
1119   cls = (*jni)->FindClass(jni, "[B"); assert(cls);
1120   if (!(*jni)->IsInstanceOf(jni, buf, cls)) {
1121     except(jni, ARGERR,
1122            "expected a byte array");
1123     return (-1);
1124   }
1125   bufsz = (*jni)->GetArrayLength(jni, buf);
1126   if (start > bufsz) {
1127     except(jni, BOUNDSERR,
1128            "bad %s buffer bounds: start %d > buffer size %d", start, bufsz);
1129     return (-1);
1130   }
1131   if (len > bufsz - start) {
1132     except(jni, BOUNDSERR,
1133            "bad %s buffer bounds: length %d > remaining buffer size %d",
1134            len, bufsz - start);
1135     return (-1);
1136   }
1137   return (0);
1138 }
1139
1140 JNIEXPORT void JNICALL JNIFUNC(send)(JNIEnv *jni, jobject cls,
1141                                      wrapper wconn, jbyteArray buf,
1142                                      jint start, jint len)
1143 {
1144   struct conn conn;
1145   ssize_t n;
1146   jbyte *p = 0;
1147
1148   if (unwrap(jni, &conn, &conn_type, wconn)) goto end;
1149   if (check_buffer_bounds(jni, "send", buf, start, len)) goto end;
1150
1151   p = (*jni)->GetByteArrayElements(jni, buf, 0);
1152   if (!p) goto end;
1153
1154   while (len) {
1155     n = send(conn.fd, p + start, len, 0);
1156     if (n < 0) {
1157       except_syserror(jni, SYSERR,
1158                       errno, "failed to send on connection");
1159       goto end;
1160     }
1161     start += n; len -= n;
1162   }
1163
1164 end:
1165   if (p) (*jni)->ReleaseByteArrayElements(jni, buf, p, JNI_ABORT);
1166   return;
1167 }
1168
1169 JNIEXPORT jint JNICALL JNIFUNC(recv)(JNIEnv *jni, jobject cls,
1170                                      wrapper wconn, jbyteArray buf,
1171                                      jint start, jint len)
1172 {
1173   struct conn conn;
1174   jbyte *p = 0;
1175   jint rc = -1;
1176
1177   if (unwrap(jni, &conn, &conn_type, wconn)) goto end;
1178   if (check_buffer_bounds(jni, "send", buf, start, len)) goto end;
1179
1180   p = (*jni)->GetByteArrayElements(jni, buf, 0);
1181   if (!p) goto end;
1182
1183   rc = recv(conn.fd, p + start, len, 0);
1184   if (rc < 0) {
1185     except_syserror(jni, SYSERR,
1186                     errno, "failed to read from connection");
1187     goto end;
1188   }
1189   if (!rc) rc = -1;
1190
1191 end:
1192   if (p) (*jni)->ReleaseByteArrayElements(jni, buf, p, 0);
1193   return (rc);
1194 }
1195
1196 JNIEXPORT void JNICALL JNIFUNC(close)(JNIEnv *jni, jobject cls,
1197                                       wrapper wconn, jint how)
1198 {
1199   struct conn conn;
1200   int rc;
1201
1202   if (unwrap(jni, &conn, &conn_type, wconn)) goto end;
1203   if (conn.fd == -1) goto end;
1204
1205   how &= CF_CLOSEMASK&~conn.f;
1206   conn.f |= how;
1207   if ((conn.f&CF_CLOSEMASK) == CF_CLOSEMASK) {
1208     close(conn.fd);
1209     conn.fd = -1;
1210   } else {
1211     if (how&CF_CLOSERD) shutdown(conn.fd, SHUT_RD);
1212     if (how&CF_CLOSEWR) shutdown(conn.fd, SHUT_WR);
1213   }
1214   rc = update_wrapper(jni, &conn_type, wconn, &conn); assert(!rc);
1215
1216 end:
1217   return;
1218 }
1219
1220 /*----- That's all, folks -------------------------------------------------*/