chiark / gitweb /
rough work in progress; may not build
[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_sys_package_00024_##f
63
64 /* The little class for bundling up error codes. */
65 #define ERRENTRY "uk/org/distorted/tripe/sys/package$ErrorEntry"
66
67 /* The `stat' class. */
68 #define STAT "uk/org/distorted/tripe/sys/package$FileInfo"
69
70 /* Exception class names. */
71 #define NULLERR "java/lang/NullPointerException"
72 #define TYPEERR "uk/org/distorted/tripe/sys/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 /*----- Messing with file descriptors -------------------------------------*/
766
767 static void fdguts(JNIEnv *jni, jclass *cls, jfieldID *fid)
768 {
769   *cls = (*jni)->FindClass(jni, "java/io/FileDescriptor"); assert(cls);
770   *fid = (*jni)->GetFieldID(jni, *cls, "fd", "I"); // OpenJDK
771   if (!*fid) *fid = (*jni)->GetFieldID(jni, *cls, "descriptor", "I"); // Android
772   assert(*fid);
773 }
774
775 static int fdint(JNIEnv *jni, jobject jfd)
776 {
777   jclass cls;
778   jfieldID fid;
779
780   fdguts(jni, &cls, &fid);
781   return ((*jni)->GetIntField(jni, jfd, fid));
782 }
783
784 static jobject newfd(JNIEnv *jni, int fd)
785 {
786   jobject jfd;
787   jclass cls;
788   jmethodID init;
789   jfieldID fid;
790
791   fdguts(jni, &cls, &fid);
792   init = (*jni)->GetMethodID(jni, cls, "<init>", "()V"); assert(init);
793   jfd = (*jni)->NewObject(jni, cls, init);
794   (*jni)->SetIntField(jni, jfd, fid, fd);
795   return (jfd);
796 }
797
798 JNIEXPORT jint JNIFUNC(fdint)(JNIEnv *jni, jobject cls, jobject jfd)
799   { return (fdint(jni, jfd)); }
800
801 JNIEXPORT jobject JNIFUNC(newfd)(JNIEnv *jni, jobject cls, jint fd)
802   { return (newfd(jni, fd)); }
803
804 JNIEXPORT jboolean JNIFUNC(isatty)(JNIEnv *jni, jobject cls, jobject jfd)
805   { return (isatty(fdint(jni, jfd))); }
806
807 /*----- Low-level file operations -----------------------------------------*/
808
809 /* Java has these already, as methods on `java.io.File' objects.  Alas, these
810  * methods are useless at reporting errors: they tend to return a `boolean'
811  * success/ fail indicator, and throw away any more detailed information.
812  * There's better functionality in `java.nio.file.Files', but that only turns
813  * up in Android API 26 (in 7.0 Nougat).  There's `android.system.Os', which
814  * has a bunch of POSIX-shaped functions -- but they're only in Android API
815  * 21 (in 5.0 Lollipop), and there's nothing in the support library to help.
816  *
817  * So the other option is to implement them ourselves.
818  */
819
820 JNIEXPORT void JNIFUNC(unlink)(JNIEnv *jni, jobject cls, jobject path)
821 {
822   const char *pathstr = 0;
823
824   pathstr = get_cstring(jni, path); if (!pathstr) goto end;
825   if (unlink(pathstr)) {
826     except_syserror(jni, SYSERR, errno,
827                     "failed to delete file `%s'", pathstr);
828     goto end;
829   }
830 end:
831   put_cstring(jni, path, pathstr);
832 }
833
834 JNIEXPORT void JNIFUNC(rmdir)(JNIEnv *jni, jobject cls, jobject path)
835 {
836   const char *pathstr = 0;
837
838   pathstr = get_cstring(jni, path); if (!pathstr) goto end;
839   if (rmdir(pathstr)) {
840     except_syserror(jni, SYSERR, errno,
841                     "failed to delete directory `%s'", pathstr);
842     goto end;
843   }
844 end:
845   put_cstring(jni, path, pathstr);
846 }
847
848 JNIEXPORT void JNIFUNC(mkdir)(JNIEnv *jni, jobject cls,
849                               jobject path, jint mode)
850 {
851   const char *pathstr = 0;
852
853   pathstr = get_cstring(jni, path); if (!pathstr) goto end;
854   if (mkdir(pathstr, mode)) {
855     except_syserror(jni, SYSERR, errno,
856                     "failed to create directory `%s'", pathstr);
857     goto end;
858   }
859 end:
860   put_cstring(jni, path, pathstr);
861 }
862
863 JNIEXPORT void JNIFUNC(mkfile)(JNIEnv *jni, jobject cls,
864                             jobject path, jint mode)
865 {
866   const char *pathstr = 0;
867   int fd = -1;
868
869   pathstr = get_cstring(jni, path); if (!pathstr) goto end;
870   fd = open(pathstr, O_WRONLY | O_CREAT | O_EXCL, mode);
871   if (fd < 0) {
872     except_syserror(jni, SYSERR, errno,
873                     "failed to create fresh file `%s'", pathstr);
874     goto end;
875   }
876 end:
877   if (fd != -1) close(fd);
878   put_cstring(jni, path, pathstr);
879 }
880
881 JNIEXPORT void JNIFUNC(rename)(JNIEnv *jni, jobject cls,
882                                jobject from, jobject to)
883 {
884   const char *fromstr = 0, *tostr = 0;
885
886   fromstr = get_cstring(jni, from); if (!fromstr) goto end;
887   tostr = get_cstring(jni, to); if (!tostr) goto end;
888   if (rename(fromstr, tostr)) {
889     except_syserror(jni, SYSERR, errno,
890                     "failed to rename `%s' as `%s'", fromstr, tostr);
891     goto end;
892   }
893 end:
894   put_cstring(jni, from, fromstr);
895   put_cstring(jni, to, tostr);
896 }
897
898 #define LKF_EXCL 0x1000u
899 #define LKF_WAIT 0x2000u
900 struct lockf {
901   struct native_base _base;
902   int fd;
903 };
904 static struct native_type lockf_type =
905         { "lock", sizeof(struct lockf), 0xb2648926};
906 JNIEXPORT wrapper JNIFUNC(lock)(JNIEnv *jni, jobject cls,
907                                 jobject path, jint flags)
908 {
909   const char *pathstr = 0;
910   int fd = -1;
911   struct flock l;
912   struct lockf lk;
913   struct stat st0, st1;
914   int f;
915   wrapper r = 0;
916
917   pathstr = get_cstring(jni, path); if (!pathstr) goto end;
918
919 again:
920   fd = open(pathstr, O_RDWR | O_CREAT, flags&07777); if (fd < 0) goto err;
921   if (fstat(fd, &st0)) goto err;
922   f = fcntl(fd, F_GETFD); if (f < 0) goto err;
923   if (fcntl(fd, F_SETFD, f | FD_CLOEXEC)) goto err;
924   l.l_type = (flags&LKF_EXCL) ? F_WRLCK : F_RDLCK;
925   l.l_whence = SEEK_SET;
926   l.l_start = 0;
927   l.l_len = 0;
928   if (fcntl(fd, (flags&LKF_WAIT) ? F_SETLKW : F_SETLK, &l)) goto err;
929   if (stat(pathstr, &st1))
930     { if (errno == ENOENT) goto again; else goto err; }
931   if (st0.st_dev != st1.st_dev || st0.st_ino != st1.st_ino)
932     { close(fd); fd = -1; goto again; }
933
934   INIT_NATIVE(lockf, &lk); lk.fd = fd; fd = -1;
935   r = wrap(jni, &lockf_type, &lk);
936   goto end;
937
938 err:
939   except_syserror(jni, SYSERR, errno, "failed to lock file `%s'", pathstr);
940 end:
941   if (fd != -1) close(fd);
942   put_cstring(jni, path, pathstr);
943   return (r);
944 }
945
946 JNIEXPORT void JNIFUNC(unlock)(JNIEnv *jni, jobject cls, wrapper wlk)
947 {
948   struct lockf lk;
949   struct flock l;
950   int rc;
951
952   if (unwrap(jni, &lk, &lockf_type, wlk)) goto end;
953   if (lk.fd == -1) goto end;
954   l.l_type = F_UNLCK;
955   l.l_whence = SEEK_SET;
956   l.l_start = 0;
957   l.l_len = 0;
958   if (fcntl(lk.fd, F_SETLK, &l)) goto end;
959   close(lk.fd); lk.fd = -1;
960   rc = update_wrapper(jni, &lockf_type, wlk, &lk); assert(!rc);
961 end:;
962 }
963
964 static jlong xlttimespec(const struct timespec *ts)
965   { return (1000*(jlong)ts->tv_sec + ts->tv_nsec/1000000); }
966
967 static jobject xltstat(JNIEnv *jni, const struct stat *st)
968 {
969   jclass cls;
970   jmethodID init;
971   jint modehack;
972
973   modehack = st->st_mode&07777;
974   if (S_ISFIFO(st->st_mode)) modehack |= 0010000;
975   else if (S_ISCHR(st->st_mode)) modehack |= 0020000;
976   else if (S_ISDIR(st->st_mode)) modehack |= 0040000;
977   else if (S_ISBLK(st->st_mode)) modehack |= 0060000;
978   else if (S_ISREG(st->st_mode)) modehack |= 0100000;
979   else if (S_ISLNK(st->st_mode)) modehack |= 0120000;
980   else if (S_ISSOCK(st->st_mode)) modehack |= 0140000;
981
982   cls = (*jni)->FindClass(jni, STAT); assert(cls);
983   init = (*jni)->GetMethodID(jni, cls, "<init>", "(IIJIIIIIIJIJJJJ)V");
984   assert(init);
985   return ((*jni)->NewObject(jni, cls, init,
986                             (jint)major(st->st_dev), (jint)minor(st->st_dev),
987                             (jlong)st->st_ino,
988                             modehack,
989                             (jint)st->st_nlink,
990                             (jint)st->st_uid, (jint)st->st_gid,
991                             (jint)major(st->st_rdev), (jint)minor(st->st_rdev),
992                             (jlong)st->st_size,
993                             (jint)st->st_blksize, (jlong)st->st_blocks,
994                             xlttimespec(&st->st_atim),
995                             xlttimespec(&st->st_mtim),
996                             xlttimespec(&st->st_ctim)));
997 }
998
999 JNIEXPORT jobject JNIFUNC(stat)(JNIEnv *jni, jobject cls, jobject path)
1000 {
1001   jobject r = 0;
1002   const char *pathstr = 0;
1003   struct stat st;
1004
1005   pathstr = get_cstring(jni, path); if (!pathstr) goto end;
1006   if (stat(pathstr, &st)) {
1007     except_syserror(jni, SYSERR, errno,
1008                     "failed to read information about `%s'", pathstr);
1009     goto end;
1010   }
1011   r = xltstat(jni, &st);
1012 end:
1013   put_cstring(jni, path, pathstr);
1014   return (r);
1015 }
1016
1017 JNIEXPORT jobject JNIFUNC(lstat)(JNIEnv *jni, jobject cls, jobject path)
1018 {
1019   jobject r = 0;
1020   const char *pathstr = 0;
1021   struct stat st;
1022
1023   pathstr = get_cstring(jni, path); if (!pathstr) goto end;
1024   if (lstat(pathstr, &st)) {
1025     except_syserror(jni, SYSERR, errno,
1026                     "failed to read information about `%s'", pathstr);
1027     goto end;
1028   }
1029   r = xltstat(jni, &st);
1030 end:
1031   put_cstring(jni, path, pathstr);
1032   return (r);
1033 }
1034
1035 struct dir {
1036   struct native_base _base;
1037   DIR *d;
1038 };
1039 static const struct native_type dir_type =
1040         { "dir", sizeof(struct dir), 0x0f5ca477 };
1041
1042 JNIEXPORT jobject JNIFUNC(opendir)(JNIEnv *jni, jobject cls, jobject path)
1043 {
1044   const char *pathstr = 0;
1045   struct dir dir;
1046   wrapper r = 0;
1047
1048   pathstr = get_cstring(jni, path); if (!pathstr) goto end;
1049   INIT_NATIVE(dir, &dir);
1050   dir.d = opendir(pathstr);
1051   if (!dir.d) {
1052     except_syserror(jni, SYSERR, errno,
1053                     "failed to open directory `%s'", pathstr);
1054     goto end;
1055   }
1056   r = wrap(jni, &dir_type, &dir);
1057 end:
1058   put_cstring(jni, path, pathstr);
1059   return (r);
1060 }
1061
1062 JNIEXPORT jbyteArray JNIFUNC(readdir)(JNIEnv *jni, jobject cls,
1063                                       jobject path, jobject wdir)
1064 {
1065   const char *pathstr = 0;
1066   struct dir dir;
1067   struct dirent *d;
1068   jbyteArray r = 0;
1069
1070   if (unwrap(jni, &dir, &dir_type, wdir)) goto end;
1071   if (!dir.d) { except(jni, ARGERR, "directory has been closed"); goto end; }
1072   errno = 0; d = readdir(dir.d);
1073   if (errno) {
1074     pathstr = get_cstring(jni, path); if (!pathstr) goto end;
1075     except_syserror(jni, SYSERR, errno,
1076                     "failed to read directory `%s'", pathstr);
1077     goto end;
1078   }
1079   if (d) r = wrap_cstring(jni, d->d_name);
1080 end:
1081   put_cstring(jni, path, pathstr);
1082   return (r);
1083 }
1084
1085 JNIEXPORT void JNIFUNC(closedir)(JNIEnv *jni, jobject cls,
1086                                  jobject path, jobject wdir)
1087 {
1088   const char *pathstr = 0;
1089   struct dir dir;
1090
1091   if (unwrap(jni, &dir, &dir_type, wdir)) goto end;
1092   if (!dir.d) goto end;
1093   if (closedir(dir.d)) {
1094     pathstr = get_cstring(jni, path); if (!pathstr) goto end;
1095     except_syserror(jni, SYSERR, errno,
1096                     "failed to close directory `%s'", pathstr);
1097     goto end;
1098   }
1099   dir.d = 0;
1100   if (update_wrapper(jni, &dir_type, wdir, &dir)) goto end;
1101 end:
1102   put_cstring(jni, path, pathstr);
1103 }
1104
1105 /*----- A server connection, using a Unix-domain socket -------------------*/
1106
1107 struct conn {
1108   struct native_base _base;
1109   int fd;
1110   unsigned f;
1111 #define CF_CLOSERD 1u
1112 #define CF_CLOSEWR 2u
1113 #define CF_CLOSEMASK (CF_CLOSERD | CF_CLOSEWR)
1114 };
1115 static const struct native_type conn_type =
1116         { "conn", sizeof(struct conn), 0xed030167 };
1117
1118 JNIEXPORT wrapper JNICALL JNIFUNC(connect)(JNIEnv *jni, jobject cls,
1119                                            jobject path)
1120 {
1121   struct conn conn;
1122   struct sockaddr_un sun;
1123   const char *pathstr = 0;
1124   jobject ret = 0;
1125   int fd = -1;
1126
1127   pathstr = get_cstring(jni, path); if (!pathstr) goto end;
1128   if (strlen(pathstr) >= sizeof(sun.sun_path)) {
1129     except(jni, ARGERR,
1130            "Unix-domain socket path `%s' too long", pathstr);
1131     goto end;
1132   }
1133
1134   INIT_NATIVE(conn, &conn);
1135   fd = socket(SOCK_STREAM, PF_UNIX, 0); if (fd < 0) goto err;
1136
1137   sun.sun_family = AF_UNIX;
1138   strcpy(sun.sun_path, (char *)pathstr);
1139   if (connect(fd, (struct sockaddr *)&sun, sizeof(sun))) goto err;
1140
1141   conn.fd = fd; fd = -1;
1142   conn.f = 0;
1143   ret = wrap(jni, &conn_type, &conn);
1144   goto end;
1145
1146 err:
1147   except_syserror(jni, SYSERR, errno,
1148                   "failed to connect to Unix-domain socket `%s'", pathstr);
1149 end:
1150   if (fd == -1) close(fd);
1151   put_cstring(jni, path, pathstr);
1152   return (ret);
1153 }
1154
1155 static int check_buffer_bounds(JNIEnv *jni, const char *what,
1156                                jbyteArray buf, jint start, jint len)
1157 {
1158   jsize bufsz;
1159   jclass cls;
1160
1161   cls = (*jni)->FindClass(jni, "[B"); assert(cls);
1162   if (!(*jni)->IsInstanceOf(jni, buf, cls)) {
1163     except(jni, ARGERR,
1164            "expected a byte array");
1165     return (-1);
1166   }
1167   bufsz = (*jni)->GetArrayLength(jni, buf);
1168   if (start > bufsz) {
1169     except(jni, BOUNDSERR,
1170            "bad %s buffer bounds: start %d > buffer size %d", start, bufsz);
1171     return (-1);
1172   }
1173   if (len > bufsz - start) {
1174     except(jni, BOUNDSERR,
1175            "bad %s buffer bounds: length %d > remaining buffer size %d",
1176            len, bufsz - start);
1177     return (-1);
1178   }
1179   return (0);
1180 }
1181
1182 JNIEXPORT void JNICALL JNIFUNC(send)(JNIEnv *jni, jobject cls,
1183                                      wrapper wconn, jbyteArray buf,
1184                                      jint start, jint len)
1185 {
1186   struct conn conn;
1187   ssize_t n;
1188   jbyte *p = 0;
1189
1190   if (unwrap(jni, &conn, &conn_type, wconn)) goto end;
1191   if (check_buffer_bounds(jni, "send", buf, start, len)) goto end;
1192
1193   p = (*jni)->GetByteArrayElements(jni, buf, 0);
1194   if (!p) goto end;
1195
1196   while (len) {
1197     n = send(conn.fd, p + start, len, 0);
1198     if (n < 0) {
1199       except_syserror(jni, SYSERR,
1200                       errno, "failed to send on connection");
1201       goto end;
1202     }
1203     start += n; len -= n;
1204   }
1205
1206 end:
1207   if (p) (*jni)->ReleaseByteArrayElements(jni, buf, p, JNI_ABORT);
1208   return;
1209 }
1210
1211 JNIEXPORT jint JNICALL JNIFUNC(recv)(JNIEnv *jni, jobject cls,
1212                                      wrapper wconn, jbyteArray buf,
1213                                      jint start, jint len)
1214 {
1215   struct conn conn;
1216   jbyte *p = 0;
1217   jint rc = -1;
1218
1219   if (unwrap(jni, &conn, &conn_type, wconn)) goto end;
1220   if (check_buffer_bounds(jni, "send", buf, start, len)) goto end;
1221
1222   p = (*jni)->GetByteArrayElements(jni, buf, 0);
1223   if (!p) goto end;
1224
1225   rc = recv(conn.fd, p + start, len, 0);
1226   if (rc < 0) {
1227     except_syserror(jni, SYSERR,
1228                     errno, "failed to read from connection");
1229     goto end;
1230   }
1231   if (!rc) rc = -1;
1232
1233 end:
1234   if (p) (*jni)->ReleaseByteArrayElements(jni, buf, p, 0);
1235   return (rc);
1236 }
1237
1238 JNIEXPORT void JNICALL JNIFUNC(closeconn)(JNIEnv *jni, jobject cls,
1239                                           wrapper wconn, jint how)
1240 {
1241   struct conn conn;
1242   int rc;
1243
1244   if (unwrap(jni, &conn, &conn_type, wconn)) goto end;
1245   if (conn.fd == -1) goto end;
1246
1247   how &= CF_CLOSEMASK&~conn.f;
1248   conn.f |= how;
1249   if ((conn.f&CF_CLOSEMASK) == CF_CLOSEMASK) {
1250     close(conn.fd);
1251     conn.fd = -1;
1252   } else {
1253     if (how&CF_CLOSERD) shutdown(conn.fd, SHUT_RD);
1254     if (how&CF_CLOSEWR) shutdown(conn.fd, SHUT_WR);
1255   }
1256   rc = update_wrapper(jni, &conn_type, wconn, &conn); assert(!rc);
1257
1258 end:
1259   return;
1260 }
1261
1262 /*----- That's all, folks -------------------------------------------------*/