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