wip
[tripe-android] / jni.c
CommitLineData
8eabb4ff
MW
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
3a2f1a4b 30#include <assert.h>
8eabb4ff 31#include <ctype.h>
3a2f1a4b
MW
32#include <errno.h>
33#include <inttypes.h>
8eabb4ff 34#include <stdarg.h>
3a2f1a4b
MW
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>
8eabb4ff
MW
43#include <sys/stat.h>
44#include <sys/sysmacros.h>
3a2f1a4b 45#include <sys/un.h>
8eabb4ff 46#include <fcntl.h>
3a2f1a4b 47#include <unistd.h>
8eabb4ff
MW
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>
3a2f1a4b
MW
56
57#undef sun
58
8eabb4ff
MW
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
79static void put_cstring(JNIEnv *jni, jbyteArray v, const char *p)
80 { if (p) (*jni)->ReleaseByteArrayElements(jni, v, (jbyte *)p, JNI_ABORT); }
81
82static 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
101static 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
111static 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
131static 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
145static 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
160static 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
166static 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
185static 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 */
3a2f1a4b
MW
202
203struct native_type {
204 const char *name;
205 size_t sz;
8eabb4ff 206 uint32 tag;
3a2f1a4b
MW
207};
208
8eabb4ff 209typedef jbyteArray wrapper;
3a2f1a4b 210
8eabb4ff
MW
211struct native_base {
212 uint32 tag;
3a2f1a4b
MW
213};
214
8eabb4ff
MW
215static 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
249static 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
260static 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
273JNIEXPORT 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
285end:
286 if (hname) (*jni)->ReleaseStringUTFChars(jni, hnamestr, hname);
287 return (rc);
288}
289
290/*----- System errors -----------------------------------------------------*/
291
292static 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***/
3a2f1a4b
MW
735};
736
8eabb4ff 737JNIEXPORT jobject JNIFUNC(errtab)(JNIEnv *jni, jobject cls)
3a2f1a4b 738{
8eabb4ff
MW
739 size_t i;
740 jclass eltcls;
741 jarray v;
742 jmethodID init;
743 jobject e;
3a2f1a4b 744
8eabb4ff
MW
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
762JNIEXPORT 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
778JNIEXPORT 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 }
788end:
789 put_cstring(jni, path, pathstr);
3a2f1a4b
MW
790}
791
8eabb4ff
MW
792JNIEXPORT void JNIFUNC(rmdir)(JNIEnv *jni, jobject cls, jobject path)
793{
794 const char *pathstr = 0;
3a2f1a4b 795
8eabb4ff
MW
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 }
802end:
803 put_cstring(jni, path, pathstr);
804}
805
806JNIEXPORT void JNIFUNC(mkdir)(JNIEnv *jni, jobject cls,
807 jobject path, jint mode)
3a2f1a4b 808{
8eabb4ff
MW
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 }
817end:
818 put_cstring(jni, path, pathstr);
3a2f1a4b
MW
819}
820
8eabb4ff
MW
821JNIEXPORT void JNIFUNC(mkfile)(JNIEnv *jni, jobject cls,
822 jobject path, jint mode)
3a2f1a4b 823{
8eabb4ff
MW
824 const char *pathstr = 0;
825 int fd = -1;
3a2f1a4b 826
8eabb4ff
MW
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;
3a2f1a4b 833 }
8eabb4ff
MW
834end:
835 if (fd != -1) close(fd);
836 put_cstring(jni, path, pathstr);
3a2f1a4b
MW
837}
838
8eabb4ff
MW
839JNIEXPORT void JNIFUNC(rename)(JNIEnv *jni, jobject cls,
840 jobject from, jobject to)
3a2f1a4b 841{
8eabb4ff
MW
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 }
851end:
852 put_cstring(jni, from, fromstr);
853 put_cstring(jni, to, tostr);
3a2f1a4b
MW
854}
855
8eabb4ff
MW
856#define LKF_EXCL 1u
857#define LKF_WAIT 2u
858struct lockf {
859 struct native_base _base;
860 int fd;
861};
862static struct native_type lockf_type =
863 { "lock", sizeof(struct lockf), 0xb2648926};
864JNIEXPORT 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
877again:
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
896err:
897 except_syserror(jni, SYSERR, errno, "failed to lock file `%s'", pathstr);
898end:
899 if (fd != -1) close(fd);
900 put_cstring(jni, path, pathstr);
901 return (r);
902}
903
904JNIEXPORT 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);
919end:;
920}
921
922static jlong xlttimespec(const struct timespec *ts)
923 { return (1000*(jlong)ts->tv_sec + ts->tv_nsec/1000000); }
924
925static 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
957JNIEXPORT jobject JNIFUNC(stat)(JNIEnv *jni, jobject cls, jobject path)
3a2f1a4b 958{
8eabb4ff
MW
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);
970end:
971 put_cstring(jni, path, pathstr);
972 return (r);
3a2f1a4b
MW
973}
974
8eabb4ff
MW
975JNIEXPORT jobject JNIFUNC(lstat)(JNIEnv *jni, jobject cls, jobject path)
976{
977 jobject r = 0;
978 const char *pathstr = 0;
979 struct stat st;
3a2f1a4b 980
8eabb4ff
MW
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);
988end:
989 put_cstring(jni, path, pathstr);
990 return (r);
991}
992
993struct dir {
994 struct native_base _base;
995 DIR *d;
3a2f1a4b 996};
8eabb4ff
MW
997static const struct native_type dir_type =
998 { "dir", sizeof(struct dir), 0x0f5ca477 };
3a2f1a4b 999
8eabb4ff 1000JNIEXPORT jobject JNIFUNC(opendir)(JNIEnv *jni, jobject cls, jobject path)
3a2f1a4b 1001{
8eabb4ff
MW
1002 const char *pathstr = 0;
1003 struct dir dir;
1004 wrapper r = 0;
3a2f1a4b 1005
8eabb4ff
MW
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);
1015end:
1016 put_cstring(jni, path, pathstr);
1017 return (r);
3a2f1a4b
MW
1018}
1019
8eabb4ff
MW
1020JNIEXPORT jbyteArray JNIFUNC(readdir)(JNIEnv *jni, jobject cls,
1021 jobject path, jobject wdir)
3a2f1a4b 1022{
8eabb4ff
MW
1023 const char *pathstr = 0;
1024 struct dir dir;
1025 struct dirent *d;
1026 jbyteArray r = 0;
3a2f1a4b 1027
8eabb4ff
MW
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);
1038end:
1039 put_cstring(jni, path, pathstr);
1040 return (r);
1041}
1042
1043JNIEXPORT 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;
1059end:
1060 put_cstring(jni, path, pathstr);
3a2f1a4b
MW
1061}
1062
8eabb4ff
MW
1063/*----- A server connection, using a Unix-domain socket -------------------*/
1064
3a2f1a4b 1065struct conn {
8eabb4ff 1066 struct native_base _base;
3a2f1a4b
MW
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};
8eabb4ff
MW
1073static const struct native_type conn_type =
1074 { "conn", sizeof(struct conn), 0xed030167 };
3a2f1a4b 1075
8eabb4ff
MW
1076JNIEXPORT wrapper JNICALL JNIFUNC(connect)(JNIEnv *jni, jobject cls,
1077 jobject path)
3a2f1a4b 1078{
8eabb4ff 1079 struct conn conn;
3a2f1a4b 1080 struct sockaddr_un sun;
8eabb4ff
MW
1081 const char *pathstr = 0;
1082 jobject ret = 0;
3a2f1a4b
MW
1083 int fd = -1;
1084
8eabb4ff
MW
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 }
3a2f1a4b 1091
8eabb4ff
MW
1092 INIT_NATIVE(conn, &conn);
1093 fd = socket(SOCK_STREAM, PF_UNIX, 0); if (fd < 0) goto err;
3a2f1a4b
MW
1094
1095 sun.sun_family = AF_UNIX;
8eabb4ff
MW
1096 strcpy(sun.sun_path, (char *)pathstr);
1097 if (connect(fd, (struct sockaddr *)&sun, sizeof(sun))) goto err;
3a2f1a4b 1098
8eabb4ff
MW
1099 conn.fd = fd; fd = -1;
1100 conn.f = 0;
1101 ret = wrap(jni, &conn_type, &conn);
1102 goto end;
3a2f1a4b 1103
3a2f1a4b 1104err:
8eabb4ff
MW
1105 except_syserror(jni, SYSERR, errno,
1106 "failed to connect to Unix-domain socket `%s'", pathstr);
1107end:
1108 if (fd == -1) close(fd);
1109 put_cstring(jni, path, pathstr);
1110 return (ret);
3a2f1a4b
MW
1111}
1112
8eabb4ff
MW
1113static int check_buffer_bounds(JNIEnv *jni, const char *what,
1114 jbyteArray buf, jint start, jint len)
3a2f1a4b 1115{
3a2f1a4b 1116 jsize bufsz;
8eabb4ff 1117 jclass cls;
3a2f1a4b 1118
8eabb4ff
MW
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 }
3a2f1a4b 1125 bufsz = (*jni)->GetArrayLength(jni, buf);
8eabb4ff
MW
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);
3a2f1a4b 1136 }
8eabb4ff
MW
1137 return (0);
1138}
1139
1140JNIEXPORT 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;
3a2f1a4b 1150
8eabb4ff 1151 p = (*jni)->GetByteArrayElements(jni, buf, 0);
3a2f1a4b
MW
1152 if (!p) goto end;
1153
1154 while (len) {
8eabb4ff 1155 n = send(conn.fd, p + start, len, 0);
3a2f1a4b 1156 if (n < 0) {
8eabb4ff
MW
1157 except_syserror(jni, SYSERR,
1158 errno, "failed to send on connection");
3a2f1a4b
MW
1159 goto end;
1160 }
1161 start += n; len -= n;
1162 }
1163
1164end:
1165 if (p) (*jni)->ReleaseByteArrayElements(jni, buf, p, JNI_ABORT);
3a2f1a4b
MW
1166 return;
1167}
1168
8eabb4ff
MW
1169JNIEXPORT jint JNICALL JNIFUNC(recv)(JNIEnv *jni, jobject cls,
1170 wrapper wconn, jbyteArray buf,
1171 jint start, jint len)
3a2f1a4b 1172{
8eabb4ff 1173 struct conn conn;
3a2f1a4b
MW
1174 jbyte *p = 0;
1175 jint rc = -1;
1176
8eabb4ff
MW
1177 if (unwrap(jni, &conn, &conn_type, wconn)) goto end;
1178 if (check_buffer_bounds(jni, "send", buf, start, len)) goto end;
3a2f1a4b 1179
8eabb4ff 1180 p = (*jni)->GetByteArrayElements(jni, buf, 0);
3a2f1a4b
MW
1181 if (!p) goto end;
1182
8eabb4ff 1183 rc = recv(conn.fd, p + start, len, 0);
3a2f1a4b 1184 if (rc < 0) {
8eabb4ff
MW
1185 except_syserror(jni, SYSERR,
1186 errno, "failed to read from connection");
3a2f1a4b
MW
1187 goto end;
1188 }
1189 if (!rc) rc = -1;
1190
1191end:
1192 if (p) (*jni)->ReleaseByteArrayElements(jni, buf, p, 0);
3a2f1a4b
MW
1193 return (rc);
1194}
1195
8eabb4ff
MW
1196JNIEXPORT void JNICALL JNIFUNC(close)(JNIEnv *jni, jobject cls,
1197 wrapper wconn, jint how)
3a2f1a4b 1198{
8eabb4ff
MW
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;
3a2f1a4b 1210 } else {
8eabb4ff
MW
1211 if (how&CF_CLOSERD) shutdown(conn.fd, SHUT_RD);
1212 if (how&CF_CLOSEWR) shutdown(conn.fd, SHUT_WR);
3a2f1a4b 1213 }
8eabb4ff 1214 rc = update_wrapper(jni, &conn_type, wconn, &conn); assert(!rc);
3a2f1a4b
MW
1215
1216end:
3a2f1a4b
MW
1217 return;
1218}
8eabb4ff
MW
1219
1220/*----- That's all, folks -------------------------------------------------*/