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 -------------------------------------------------*/