3 * Native-code portions of the project
5 * (c) 2018 Straylight/Edgeware
8 /*----- Licensing notice --------------------------------------------------*
10 * This file is part of the Trivial IP Encryption (TrIPE) Android app.
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.
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
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/>.
26 /*----- Header files ------------------------------------------------------*/
28 #define _FILE_OFFSET_BITS 64
41 #include <sys/types.h>
42 #include <sys/select.h>
43 #include <sys/socket.h>
45 #include <sys/sysmacros.h>
51 #include <mLib/align.h>
52 #include <mLib/bits.h>
53 #include <mLib/dstr.h>
54 #include <mLib/macros.h>
56 #include <catacomb/ghash.h>
60 /*----- Magic class names and similar -------------------------------------*/
62 /* The name decoration is horrific. Hide it. */
63 #define JNIFUNC(f) Java_uk_org_distorted_tripe_sys_package_00024_##f
65 /* The little class for bundling up error codes. */
66 #define ERRENTRY "uk/org/distorted/tripe/sys/package$ErrorEntry"
68 /* The `stat' class. */
69 #define STAT "uk/org/distorted/tripe/sys/package$FileInfo"
71 /* Exception class names. */
72 #define NULLERR "java/lang/NullPointerException"
73 #define TYPEERR "uk/org/distorted/tripe/sys/package$NativeObjectTypeException"
74 #define SYSERR "uk/org/distorted/tripe/sys/package$SystemError"
75 #define ARGERR "java/lang/IllegalArgumentException"
76 #define BOUNDSERR "java/lang/IndexOutOfBoundsException"
78 /*----- Miscellaneous utilities -------------------------------------------*/
80 static void put_cstring(JNIEnv *jni, jbyteArray v, const char *p)
81 { if (p) (*jni)->ReleaseByteArrayElements(jni, v, (jbyte *)p, JNI_ABORT); }
83 static void vexcept(JNIEnv *jni, const char *clsname,
84 const char *msg, va_list *ap)
90 cls = (*jni)->FindClass(jni, clsname); assert(cls);
92 rc = (*jni)->ThrowNew(jni, cls, 0);
94 dstr_vputf(&d, msg, ap);
95 rc = (*jni)->ThrowNew(jni, cls, d.buf);
102 static void except(JNIEnv *jni, const char *clsname, const char *msg, ...)
107 vexcept(jni, clsname, msg, &ap);
112 static void dump_bytes(const void *p, size_t n, size_t o)
114 const unsigned char *q = p;
119 fprintf(stderr, ";; %08zx\n", o);
120 for (i = 0; i < 8; i++)
121 if (i < n) fprintf(stderr, "%02x ", q[i]);
122 else fprintf(stderr, "** ");
123 fprintf(stderr, ": ");
124 for (i = 0; i < 8; i++)
125 fputc(i >= n ? '*' : isprint(q[i]) ? q[i] : '.', stderr);
132 static void dump_byte_array(JNIEnv *jni, const char *what, jbyteArray v)
137 fprintf(stderr, ";; %s\n", what);
138 if (!v) { fprintf(stderr, ";; <null>\n"); return; }
139 n = (*jni)->GetArrayLength(jni, v);
140 p = (*jni)->GetByteArrayElements(jni, v, 0);
142 (*jni)->ReleaseByteArrayElements(jni, v, p, JNI_ABORT);
146 static jbyteArray wrap_cstring(JNIEnv *jni, const char *p)
154 v = (*jni)->NewByteArray(jni, n); if (!v) return (0);
155 q = (*jni)->GetByteArrayElements(jni, v, 0); if (!q) return (0);
157 (*jni)->ReleaseByteArrayElements(jni, v, q, 0);
161 static const char *get_cstring(JNIEnv *jni, jbyteArray v)
163 if (!v) { except(jni, NULLERR, 0); return (0); }
164 return ((const char *)(*jni)->GetByteArrayElements(jni, v, 0));
167 static void vexcept_syserror(JNIEnv *jni, const char *clsname,
168 int err, const char *msg, va_list *ap)
177 cls = (*jni)->FindClass(jni, clsname); assert(cls);
178 init = (*jni)->GetMethodID(jni, cls, "<init>", "(I[B)V"); assert(init);
179 dstr_vputf(&d, msg, ap);
180 msgstr = wrap_cstring(jni, d.buf); assert(msgstr);
182 e = (*jni)->NewObject(jni, cls, init, err, msgstr); assert(e);
183 rc = (*jni)->Throw(jni, e); assert(!rc);
186 static void except_syserror(JNIEnv *jni, const char *clsname,
187 int err, const char *msg, ...)
192 vexcept_syserror(jni, clsname, err, msg, &ap);
196 static int set_nonblocking(JNIEnv *jni, int fd, int nb)
198 int f0 = fcntl(fd, F_GETFL), f1;
199 if (f0 < 0) goto err;
200 if (nb) f1 = f0 | O_NONBLOCK;
201 else f1 = f0&~O_NONBLOCK;
202 if (fcntl(fd, F_SETFL, f1)) goto err;
203 return (f0 & O_NONBLOCK);
205 except_syserror(jni, SYSERR, errno,
206 "failed to set descriptor nonblocking");
210 static int set_closeonexec(JNIEnv *jni, int fd)
212 int f = fcntl(fd, F_GETFD);
213 if (f < 0 || fcntl(fd, F_SETFD, f | FD_CLOEXEC)) {
214 except_syserror(jni, SYSERR, errno,
215 "failed to set descriptor close-on-exec");
221 /*----- Wrapping native types ---------------------------------------------*/
223 /* There's no way defined in the JNI to stash a C pointer in a Java object.
224 * It seems that the usual approach is to cast to `jlong', but this is
225 * clearly unsatisfactory. Instead, we store structures as Java byte arrays,
226 * with a 32-bit tag on the front.
235 typedef jbyteArray wrapper;
241 static int unwrap(JNIEnv *jni, void *p,
242 const struct native_type *ty, wrapper w)
246 struct native_base *b = p;
249 if (!w) { except(jni, NULLERR, 0); return (-1); }
250 cls = (*jni)->FindClass(jni, "[B"); assert(cls);
251 if (!(*jni)->IsInstanceOf(jni, w, cls)) {
253 "corrupted native object wrapper: expected a byte array");
256 n = (*jni)->GetArrayLength(jni, w);
259 "corrupted native object wrapper: wrong size for `%s'",
263 q = (*jni)->GetByteArrayElements(jni, w, 0); if (!q) return (-1);
264 memcpy(b, q, ty->sz);
265 (*jni)->ReleaseByteArrayElements(jni, w, q, JNI_ABORT);
266 if (b->tag != ty->tag) {
268 "corrupted native object wrapper: expected tag for `%s'",
275 static int update_wrapper(JNIEnv *jni, const struct native_type *ty,
276 wrapper w, const void *p)
280 q = (*jni)->GetByteArrayElements(jni, w, 0); if (!q) return (-1);
281 memcpy(q, p, ty->sz);
282 (*jni)->ReleaseByteArrayElements(jni, w, q, 0);
286 static wrapper wrap(JNIEnv *jni, const struct native_type *ty, const void *p)
290 w = (*jni)->NewByteArray(jni, ty->sz); if (!w) return (0);
291 if (update_wrapper(jni, ty, w, p)) return (0);
295 #define INIT_NATIVE(type, p) do (p)->_base.tag = type##_type.tag; while (0)
297 /*----- Crypto information ------------------------------------------------*/
299 JNIEXPORT jint JNICALL JNIFUNC(hashsz)(JNIEnv *jni, jobject cls,
306 hname = (*jni)->GetStringUTFChars(jni, hnamestr, 0);
307 if (!hname) goto end;
308 hc = ghash_byname(hname); if (!hc) goto end;
312 if (hname) (*jni)->ReleaseStringUTFChars(jni, hnamestr, hname);
316 /*----- System errors -----------------------------------------------------*/
318 static const struct errtab { const char *tag; int err; } errtab[] = {
320 ;;; The errno name table is very boring to type. To make life less
321 ;;; awful, put the errno names in this list and evaluate the code to
322 ;;; get Emacs to regenerate it.
324 (let ((errors '(EPERM ENOENT ESRCH EINTR EIO ENXIO E2BIG ENOEXEC EBADF
325 ECHILD EAGAIN ENOMEM EACCES EFAULT ENOTBLK EBUSY EEXIST
326 EXDEV ENODEV ENOTDIR EISDIR EINVAL ENFILE EMFILE ENOTTY
327 ETXTBSY EFBIG ENOSPC ESPIPE EROFS EMLINK EPIPE EDOM
330 EDEADLK ENAMETOOLONG ENOLCK ENOSYS ENOTEMPTY ELOOP
331 EWOULDBLOCK ENOMSG EIDRM ECHRNG EL2NSYNC EL3HLT EL3RST
332 ELNRNG EUNATCH ENOCSI EL2HLT EBADE EBADR EXFULL ENOANO
333 EBADRQC EBADSLT EDEADLOCK EBFONT ENOSTR ENODATA ETIME
334 ENOSR ENONET ENOPKG EREMOTE ENOLINK EADV ESRMNT ECOMM
335 EPROTO EMULTIHOP EDOTDOT EBADMSG EOVERFLOW ENOTUNIQ
336 EBADFD EREMCHG ELIBACC ELIBBAD ELIBSCN ELIBMAX ELIBEXEC
337 EILSEQ ERESTART ESTRPIPE EUSERS ENOTSOCK EDESTADDRREQ
338 EMSGSIZE EPROTOTYPE ENOPROTOOPT EPROTONOSUPPORT
339 ESOCKTNOSUPPORT EOPNOTSUPP EPFNOSUPPORT EAFNOSUPPORT
340 EADDRINUSE EADDRNOTAVAIL ENETDOWN ENETUNREACH ENETRESET
341 ECONNABORTED ECONNRESET ENOBUFS EISCONN ENOTCONN
342 ESHUTDOWN ETOOMANYREFS ETIMEDOUT ECONNREFUSED EHOSTDOWN
343 EHOSTUNREACH EALREADY EINPROGRESS ESTALE EUCLEAN ENOTNAM
344 ENAVAIL EISNAM EREMOTEIO EDQUOT ENOMEDIUM EMEDIUMTYPE
345 ECANCELED ENOKEY EKEYEXPIRED EKEYREVOKED EKEYREJECTED
346 EOWNERDEAD ENOTRECOVERABLE ERFKILL EHWPOISON)))
348 (goto-char (point-min))
349 (search-forward (concat "***" "BEGIN errtab" "***"))
350 (beginning-of-line 2)
351 (delete-region (point)
353 (search-forward "***END***")
357 (insert (format "#ifdef %s\n { \"%s\", %s },\n#endif\n"
365 { "ENOENT", ENOENT },
383 { "ENOEXEC", ENOEXEC },
389 { "ECHILD", ECHILD },
392 { "EAGAIN", EAGAIN },
395 { "ENOMEM", ENOMEM },
398 { "EACCES", EACCES },
401 { "EFAULT", EFAULT },
404 { "ENOTBLK", ENOTBLK },
410 { "EEXIST", EEXIST },
416 { "ENODEV", ENODEV },
419 { "ENOTDIR", ENOTDIR },
422 { "EISDIR", EISDIR },
425 { "EINVAL", EINVAL },
428 { "ENFILE", ENFILE },
431 { "EMFILE", EMFILE },
434 { "ENOTTY", ENOTTY },
437 { "ETXTBSY", ETXTBSY },
443 { "ENOSPC", ENOSPC },
446 { "ESPIPE", ESPIPE },
452 { "EMLINK", EMLINK },
461 { "ERANGE", ERANGE },
464 { "EDEADLK", EDEADLK },
467 { "ENAMETOOLONG", ENAMETOOLONG },
470 { "ENOLCK", ENOLCK },
473 { "ENOSYS", ENOSYS },
476 { "ENOTEMPTY", ENOTEMPTY },
482 { "EWOULDBLOCK", EWOULDBLOCK },
485 { "ENOMSG", ENOMSG },
491 { "ECHRNG", ECHRNG },
494 { "EL2NSYNC", EL2NSYNC },
497 { "EL3HLT", EL3HLT },
500 { "EL3RST", EL3RST },
503 { "ELNRNG", ELNRNG },
506 { "EUNATCH", EUNATCH },
509 { "ENOCSI", ENOCSI },
512 { "EL2HLT", EL2HLT },
521 { "EXFULL", EXFULL },
524 { "ENOANO", ENOANO },
527 { "EBADRQC", EBADRQC },
530 { "EBADSLT", EBADSLT },
533 { "EDEADLOCK", EDEADLOCK },
536 { "EBFONT", EBFONT },
539 { "ENOSTR", ENOSTR },
542 { "ENODATA", ENODATA },
551 { "ENONET", ENONET },
554 { "ENOPKG", ENOPKG },
557 { "EREMOTE", EREMOTE },
560 { "ENOLINK", ENOLINK },
566 { "ESRMNT", ESRMNT },
572 { "EPROTO", EPROTO },
575 { "EMULTIHOP", EMULTIHOP },
578 { "EDOTDOT", EDOTDOT },
581 { "EBADMSG", EBADMSG },
584 { "EOVERFLOW", EOVERFLOW },
587 { "ENOTUNIQ", ENOTUNIQ },
590 { "EBADFD", EBADFD },
593 { "EREMCHG", EREMCHG },
596 { "ELIBACC", ELIBACC },
599 { "ELIBBAD", ELIBBAD },
602 { "ELIBSCN", ELIBSCN },
605 { "ELIBMAX", ELIBMAX },
608 { "ELIBEXEC", ELIBEXEC },
611 { "EILSEQ", EILSEQ },
614 { "ERESTART", ERESTART },
617 { "ESTRPIPE", ESTRPIPE },
620 { "EUSERS", EUSERS },
623 { "ENOTSOCK", ENOTSOCK },
626 { "EDESTADDRREQ", EDESTADDRREQ },
629 { "EMSGSIZE", EMSGSIZE },
632 { "EPROTOTYPE", EPROTOTYPE },
635 { "ENOPROTOOPT", ENOPROTOOPT },
637 #ifdef EPROTONOSUPPORT
638 { "EPROTONOSUPPORT", EPROTONOSUPPORT },
640 #ifdef ESOCKTNOSUPPORT
641 { "ESOCKTNOSUPPORT", ESOCKTNOSUPPORT },
644 { "EOPNOTSUPP", EOPNOTSUPP },
647 { "EPFNOSUPPORT", EPFNOSUPPORT },
650 { "EAFNOSUPPORT", EAFNOSUPPORT },
653 { "EADDRINUSE", EADDRINUSE },
656 { "EADDRNOTAVAIL", EADDRNOTAVAIL },
659 { "ENETDOWN", ENETDOWN },
662 { "ENETUNREACH", ENETUNREACH },
665 { "ENETRESET", ENETRESET },
668 { "ECONNABORTED", ECONNABORTED },
671 { "ECONNRESET", ECONNRESET },
674 { "ENOBUFS", ENOBUFS },
677 { "EISCONN", EISCONN },
680 { "ENOTCONN", ENOTCONN },
683 { "ESHUTDOWN", ESHUTDOWN },
686 { "ETOOMANYREFS", ETOOMANYREFS },
689 { "ETIMEDOUT", ETIMEDOUT },
692 { "ECONNREFUSED", ECONNREFUSED },
695 { "EHOSTDOWN", EHOSTDOWN },
698 { "EHOSTUNREACH", EHOSTUNREACH },
701 { "EALREADY", EALREADY },
704 { "EINPROGRESS", EINPROGRESS },
707 { "ESTALE", ESTALE },
710 { "EUCLEAN", EUCLEAN },
713 { "ENOTNAM", ENOTNAM },
716 { "ENAVAIL", ENAVAIL },
719 { "EISNAM", EISNAM },
722 { "EREMOTEIO", EREMOTEIO },
725 { "EDQUOT", EDQUOT },
728 { "ENOMEDIUM", ENOMEDIUM },
731 { "EMEDIUMTYPE", EMEDIUMTYPE },
734 { "ECANCELED", ECANCELED },
737 { "ENOKEY", ENOKEY },
740 { "EKEYEXPIRED", EKEYEXPIRED },
743 { "EKEYREVOKED", EKEYREVOKED },
746 { "EKEYREJECTED", EKEYREJECTED },
749 { "EOWNERDEAD", EOWNERDEAD },
751 #ifdef ENOTRECOVERABLE
752 { "ENOTRECOVERABLE", ENOTRECOVERABLE },
755 { "ERFKILL", ERFKILL },
758 { "EHWPOISON", EHWPOISON },
763 JNIEXPORT jobject JNIFUNC(errtab)(JNIEnv *jni, jobject cls)
772 (*jni)->FindClass(jni, ERRENTRY);
774 v = (*jni)->NewObjectArray(jni, N(errtab), eltcls, 0); if (!v) return (0);
775 init = (*jni)->GetMethodID(jni, eltcls, "<init>",
776 "(Ljava/lang/String;I)V");
779 for (i = 0; i < N(errtab); i++) {
780 e = (*jni)->NewObject(jni, eltcls, init,
781 (*jni)->NewStringUTF(jni, errtab[i].tag),
783 (*jni)->SetObjectArrayElement(jni, v, i, e);
788 JNIEXPORT jobject JNIFUNC(strerror)(JNIEnv *jni, jobject cls, jint err)
789 { return (wrap_cstring(jni, strerror(err))); }
791 /*----- Messing with file descriptors -------------------------------------*/
793 static void fdguts(JNIEnv *jni, jclass *cls, jfieldID *fid)
795 *cls = (*jni)->FindClass(jni, "java/io/FileDescriptor"); assert(cls);
796 *fid = (*jni)->GetFieldID(jni, *cls, "fd", "I"); // OpenJDK
797 if (!*fid) *fid = (*jni)->GetFieldID(jni, *cls, "descriptor", "I"); // Android
801 static int fdint(JNIEnv *jni, jobject jfd)
806 fdguts(jni, &cls, &fid);
807 return ((*jni)->GetIntField(jni, jfd, fid));
810 static jobject newfd(JNIEnv *jni, int fd)
817 fdguts(jni, &cls, &fid);
818 init = (*jni)->GetMethodID(jni, cls, "<init>", "()V"); assert(init);
819 jfd = (*jni)->NewObject(jni, cls, init);
820 (*jni)->SetIntField(jni, jfd, fid, fd);
824 JNIEXPORT jint JNIFUNC(fdint)(JNIEnv *jni, jobject cls, jobject jfd)
825 { return (fdint(jni, jfd)); }
827 JNIEXPORT jobject JNIFUNC(newfd)(JNIEnv *jni, jobject cls, jint fd)
828 { return (newfd(jni, fd)); }
830 JNIEXPORT jboolean JNIFUNC(isatty)(JNIEnv *jni, jobject cls, jobject jfd)
831 { return (isatty(fdint(jni, jfd))); }
833 /*----- Low-level file operations -----------------------------------------*/
835 /* Java has these already, as methods on `java.io.File' objects. Alas, these
836 * methods are useless at reporting errors: they tend to return a `boolean'
837 * success/fail indicator, and throw away any more detailed information.
838 * There's better functionality in `java.nio.file.Files', but that only turns
839 * up in Android API 26 (in 7.0 Nougat). There's `android.system.Os', which
840 * has a bunch of POSIX-shaped functions -- but they're only in Android API
841 * 21 (in 5.0 Lollipop), and there's nothing in the support library to help.
843 * So the other option is to implement them ourselves.
846 JNIEXPORT void JNIFUNC(unlink)(JNIEnv *jni, jobject cls, jobject path)
848 const char *pathstr = 0;
850 pathstr = get_cstring(jni, path); if (!pathstr) goto end;
851 if (unlink(pathstr)) {
852 except_syserror(jni, SYSERR, errno,
853 "failed to delete file `%s'", pathstr);
857 put_cstring(jni, path, pathstr);
860 JNIEXPORT void JNIFUNC(rmdir)(JNIEnv *jni, jobject cls, jobject path)
862 const char *pathstr = 0;
864 pathstr = get_cstring(jni, path); if (!pathstr) goto end;
865 if (rmdir(pathstr)) {
866 except_syserror(jni, SYSERR, errno,
867 "failed to delete directory `%s'", pathstr);
871 put_cstring(jni, path, pathstr);
874 JNIEXPORT void JNIFUNC(mkdir)(JNIEnv *jni, jobject cls,
875 jobject path, jint mode)
877 const char *pathstr = 0;
879 pathstr = get_cstring(jni, path); if (!pathstr) goto end;
880 if (mkdir(pathstr, mode)) {
881 except_syserror(jni, SYSERR, errno,
882 "failed to create directory `%s'", pathstr);
886 put_cstring(jni, path, pathstr);
889 JNIEXPORT void JNIFUNC(mkfile)(JNIEnv *jni, jobject cls,
890 jobject path, jint mode)
892 const char *pathstr = 0;
895 pathstr = get_cstring(jni, path); if (!pathstr) goto end;
896 fd = open(pathstr, O_WRONLY | O_CREAT | O_EXCL, mode);
898 except_syserror(jni, SYSERR, errno,
899 "failed to create fresh file `%s'", pathstr);
903 if (fd != -1) close(fd);
904 put_cstring(jni, path, pathstr);
907 JNIEXPORT void JNIFUNC(rename)(JNIEnv *jni, jobject cls,
908 jobject from, jobject to)
910 const char *fromstr = 0, *tostr = 0;
912 fromstr = get_cstring(jni, from); if (!fromstr) goto end;
913 tostr = get_cstring(jni, to); if (!tostr) goto end;
914 if (rename(fromstr, tostr)) {
915 except_syserror(jni, SYSERR, errno,
916 "failed to rename `%s' as `%s'", fromstr, tostr);
920 put_cstring(jni, from, fromstr);
921 put_cstring(jni, to, tostr);
924 #define LKF_EXCL 0x1000u
925 #define LKF_WAIT 0x2000u
927 struct native_base _base;
930 static struct native_type lockf_type =
931 { "lock", sizeof(struct lockf), 0xb2648926};
932 JNIEXPORT wrapper JNIFUNC(lock)(JNIEnv *jni, jobject cls,
933 jobject path, jint flags)
935 const char *pathstr = 0;
939 struct stat st0, st1;
943 pathstr = get_cstring(jni, path); if (!pathstr) goto end;
946 fd = open(pathstr, O_RDWR | O_CREAT, flags&07777); if (fd < 0) goto err;
947 if (fstat(fd, &st0)) goto err;
948 f = fcntl(fd, F_GETFD); if (f < 0) goto err;
949 if (fcntl(fd, F_SETFD, f | FD_CLOEXEC)) goto err;
950 l.l_type = (flags&LKF_EXCL) ? F_WRLCK : F_RDLCK;
951 l.l_whence = SEEK_SET;
954 if (fcntl(fd, (flags&LKF_WAIT) ? F_SETLKW : F_SETLK, &l)) goto err;
955 if (stat(pathstr, &st1))
956 { if (errno == ENOENT) goto again; else goto err; }
957 if (st0.st_dev != st1.st_dev || st0.st_ino != st1.st_ino)
958 { close(fd); fd = -1; goto again; }
960 INIT_NATIVE(lockf, &lk); lk.fd = fd; fd = -1;
961 r = wrap(jni, &lockf_type, &lk);
965 except_syserror(jni, SYSERR, errno, "failed to lock file `%s'", pathstr);
967 if (fd != -1) close(fd);
968 put_cstring(jni, path, pathstr);
972 JNIEXPORT void JNIFUNC(unlock)(JNIEnv *jni, jobject cls, wrapper wlk)
978 if (unwrap(jni, &lk, &lockf_type, wlk)) goto end;
979 if (lk.fd == -1) goto end;
981 l.l_whence = SEEK_SET;
984 if (fcntl(lk.fd, F_SETLK, &l)) goto end;
985 close(lk.fd); lk.fd = -1;
986 rc = update_wrapper(jni, &lockf_type, wlk, &lk); assert(!rc);
990 static jlong xlttimespec(const struct timespec *ts)
991 { return (1000*(jlong)ts->tv_sec + ts->tv_nsec/1000000); }
993 static jobject xltstat(JNIEnv *jni, const struct stat *st)
999 modehack = st->st_mode&07777;
1000 if (S_ISFIFO(st->st_mode)) modehack |= 0010000;
1001 else if (S_ISCHR(st->st_mode)) modehack |= 0020000;
1002 else if (S_ISDIR(st->st_mode)) modehack |= 0040000;
1003 else if (S_ISBLK(st->st_mode)) modehack |= 0060000;
1004 else if (S_ISREG(st->st_mode)) modehack |= 0100000;
1005 else if (S_ISLNK(st->st_mode)) modehack |= 0120000;
1006 else if (S_ISSOCK(st->st_mode)) modehack |= 0140000;
1008 cls = (*jni)->FindClass(jni, STAT); assert(cls);
1009 init = (*jni)->GetMethodID(jni, cls, "<init>", "(IIJIIIIIIJIJJJJ)V");
1011 return ((*jni)->NewObject(jni, cls, init,
1012 (jint)major(st->st_dev), (jint)minor(st->st_dev),
1016 (jint)st->st_uid, (jint)st->st_gid,
1017 (jint)major(st->st_rdev), (jint)minor(st->st_rdev),
1019 (jint)st->st_blksize, (jlong)st->st_blocks,
1020 xlttimespec(&st->st_atim),
1021 xlttimespec(&st->st_mtim),
1022 xlttimespec(&st->st_ctim)));
1025 JNIEXPORT jobject JNIFUNC(stat)(JNIEnv *jni, jobject cls, jobject path)
1028 const char *pathstr = 0;
1031 pathstr = get_cstring(jni, path); if (!pathstr) goto end;
1032 if (stat(pathstr, &st)) {
1033 except_syserror(jni, SYSERR, errno,
1034 "failed to read information about `%s'", pathstr);
1037 r = xltstat(jni, &st);
1039 put_cstring(jni, path, pathstr);
1043 JNIEXPORT jobject JNIFUNC(lstat)(JNIEnv *jni, jobject cls, jobject path)
1046 const char *pathstr = 0;
1049 pathstr = get_cstring(jni, path); if (!pathstr) goto end;
1050 if (lstat(pathstr, &st)) {
1051 except_syserror(jni, SYSERR, errno,
1052 "failed to read information about `%s'", pathstr);
1055 r = xltstat(jni, &st);
1057 put_cstring(jni, path, pathstr);
1062 struct native_base _base;
1065 static const struct native_type dir_type =
1066 { "dir", sizeof(struct dir), 0x0f5ca477 };
1068 JNIEXPORT jobject JNIFUNC(opendir)(JNIEnv *jni, jobject cls, jobject path)
1070 const char *pathstr = 0;
1074 pathstr = get_cstring(jni, path); if (!pathstr) goto end;
1075 INIT_NATIVE(dir, &dir);
1076 dir.d = opendir(pathstr);
1078 except_syserror(jni, SYSERR, errno,
1079 "failed to open directory `%s'", pathstr);
1082 r = wrap(jni, &dir_type, &dir);
1084 put_cstring(jni, path, pathstr);
1088 JNIEXPORT jbyteArray JNIFUNC(readdir)(JNIEnv *jni, jobject cls,
1089 jobject path, jobject wdir)
1091 const char *pathstr = 0;
1096 if (unwrap(jni, &dir, &dir_type, wdir)) goto end;
1097 if (!dir.d) { except(jni, ARGERR, "directory has been closed"); goto end; }
1098 errno = 0; d = readdir(dir.d);
1100 pathstr = get_cstring(jni, path); if (!pathstr) goto end;
1101 except_syserror(jni, SYSERR, errno,
1102 "failed to read directory `%s'", pathstr);
1105 if (d) r = wrap_cstring(jni, d->d_name);
1107 put_cstring(jni, path, pathstr);
1111 JNIEXPORT void JNIFUNC(closedir)(JNIEnv *jni, jobject cls,
1112 jobject path, jobject wdir)
1114 const char *pathstr = 0;
1117 if (unwrap(jni, &dir, &dir_type, wdir)) goto end;
1118 if (!dir.d) goto end;
1119 if (closedir(dir.d)) {
1120 pathstr = get_cstring(jni, path); if (!pathstr) goto end;
1121 except_syserror(jni, SYSERR, errno,
1122 "failed to close directory `%s'", pathstr);
1126 if (update_wrapper(jni, &dir_type, wdir, &dir)) goto end;
1128 put_cstring(jni, path, pathstr);
1131 /*----- Triggers ----------------------------------------------------------*/
1133 /* A trigger is a gadget for waking up a thread which is blocking on I/O,
1134 * and it's used to implement interruptability.
1136 * Really, a trigger is a pipe. A `blocking' I/O operation secretly uses
1137 * select(2) to block on the descriptor of interest /and/ the read side of
1138 * the trigger pipe. To wake up a thread that's blocked, we just write a
1139 * byte (nobody cares /which/ byte) to the write end.
1143 struct native_base _base;
1146 static const struct native_type trigger_type =
1147 { "trigger", sizeof(struct trigger), 0x65ffd8b4 };
1149 JNIEXPORT wrapper JNICALL JNIFUNC(makeTrigger)(JNIEnv *jni, jobject cls)
1151 struct trigger trig;
1158 except_syserror(jni, SYSERR, errno, "failed to create pipe");
1161 for (i = 0; i < 2; i++) {
1162 if (set_nonblocking(jni, fd[i], 1) < 0 || set_closeonexec(jni, fd[i]))
1166 INIT_NATIVE(trigger, &trig);
1167 trig.rfd = fd[0]; fd[0] = -1;
1168 trig.wfd = fd[1]; fd[1] = -1;
1169 ret = wrap(jni, &trigger_type, &trig);
1172 for (i = 0; i < 2; i++)
1173 if (fd[i] != -1) close(fd[i]);
1177 JNIEXPORT void JNICALL JNIFUNC(destroyTrigger)(JNIEnv *jni, jobject cls,
1180 struct trigger trig;
1182 if (unwrap(jni, &trig, &trigger_type, wtrig)) return;
1183 if (trig.rfd != -1) { close(trig.rfd); trig.rfd = -1; }
1184 if (trig.wfd != -1) { close(trig.wfd); trig.wfd = -1; }
1185 update_wrapper(jni, &trigger_type, wtrig, &trig);
1188 JNIEXPORT void JNICALL JNIFUNC(resetTrigger)(JNIEnv *jni, jobject cls,
1191 struct trigger trig;
1195 if (unwrap(jni, &trig, &trigger_type, wtrig)) return;
1197 n = read(trig.rfd, buf, sizeof(buf));
1198 if (n > 0) continue;
1200 if (errno == EAGAIN || errno == EWOULDBLOCK) break;
1202 except_syserror(jni, SYSERR, errno, "failed to reset trigger");
1208 JNIEXPORT void JNICALL JNIFUNC(trigger)(JNIEnv *jni, jobject cls,
1211 struct trigger trig;
1215 if (unwrap(jni, &trig, &trigger_type, wtrig)) return;
1216 n = write(trig.wfd, &c, 1);
1217 if (n < 0 && errno != EAGAIN && errno != EWOULDBLOCK)
1218 except_syserror(jni, SYSERR, errno, "failed to pull trigger");
1221 /*----- A server connection, using a Unix-domain socket -------------------*/
1224 struct native_base _base;
1227 #define CF_CLOSERD 1u
1228 #define CF_CLOSEWR 2u
1229 #define CF_CLOSEMASK (CF_CLOSERD | CF_CLOSEWR)
1231 static const struct native_type conn_type =
1232 { "conn", sizeof(struct conn), 0xed030167 };
1234 JNIEXPORT wrapper JNICALL JNIFUNC(connect)(JNIEnv *jni, jobject cls,
1235 jobject path, wrapper wtrig)
1238 struct trigger trig;
1239 struct sockaddr_un sun;
1242 const char *pathstr = 0;
1249 if (unwrap(jni, &trig, &trigger_type, wtrig)) goto end;
1250 pathstr = get_cstring(jni, path); if (!pathstr) goto end;
1251 if (strlen(pathstr) >= sizeof(sun.sun_path)) {
1253 "Unix-domain socket path `%s' too long", pathstr);
1257 INIT_NATIVE(conn, &conn);
1258 fd = socket(PF_UNIX, SOCK_STREAM, 0); if (fd < 0) goto err;
1259 nb = set_nonblocking(jni, fd, 1); if (nb < 0) goto end;
1261 sun.sun_family = AF_UNIX;
1262 strcpy(sun.sun_path, (char *)pathstr);
1263 if (!connect(fd, (struct sockaddr *)&sun, sizeof(sun))) goto connected;
1264 else if (errno != EINPROGRESS) goto err;
1267 if (maxfd < fd) maxfd = fd;
1269 FD_ZERO(&rfds); FD_SET(trig.rfd, &rfds);
1270 FD_ZERO(&wfds); FD_SET(fd, &wfds);
1271 rc = select(maxfd + 1, &rfds, &wfds, 0, 0); if (rc < 0) goto err;
1272 if (FD_ISSET(trig.rfd, &rfds)) goto end;
1273 if (FD_ISSET(fd, &wfds)) {
1275 if (!getpeername(fd, (struct sockaddr *)&sun, &sz)) goto connected;
1276 else if (errno != ENOTCONN) goto err;
1278 if (!getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &sz)) errno = err;
1284 if (set_nonblocking(jni, fd, nb) < 0) goto end;
1285 conn.fd = fd; fd = -1;
1287 ret = wrap(jni, &conn_type, &conn);
1291 except_syserror(jni, SYSERR, errno,
1292 "failed to connect to Unix-domain socket `%s'", pathstr);
1294 if (fd != -1) close(fd);
1295 put_cstring(jni, path, pathstr);
1299 static int check_buffer_bounds(JNIEnv *jni, const char *what,
1300 jbyteArray buf, jint start, jint len)
1305 cls = (*jni)->FindClass(jni, "[B"); assert(cls);
1306 if (!(*jni)->IsInstanceOf(jni, buf, cls)) {
1308 "expected a byte array");
1311 bufsz = (*jni)->GetArrayLength(jni, buf);
1312 if (start > bufsz) {
1313 except(jni, BOUNDSERR,
1314 "bad %s buffer bounds: start %d > buffer size %d", start, bufsz);
1317 if (len > bufsz - start) {
1318 except(jni, BOUNDSERR,
1319 "bad %s buffer bounds: length %d > remaining buffer size %d",
1320 len, bufsz - start);
1326 JNIEXPORT void JNICALL JNIFUNC(send)(JNIEnv *jni, jobject cls,
1327 wrapper wconn, jbyteArray buf,
1328 jint start, jint len,
1332 struct trigger trig;
1338 if (unwrap(jni, &conn, &conn_type, wconn)) goto end;
1339 if (unwrap(jni, &trig, &trigger_type, wtrig)) goto end;
1340 if (check_buffer_bounds(jni, "send", buf, start, len)) goto end;
1342 p = (*jni)->GetByteArrayElements(jni, buf, 0);
1346 if (maxfd < conn.fd) maxfd = conn.fd;
1348 FD_ZERO(&rfds); FD_SET(trig.rfd, &rfds);
1349 FD_ZERO(&wfds); FD_SET(conn.fd, &wfds);
1350 rc = select(maxfd + 1, &rfds, &wfds, 0, 0); if (rc < 0) goto err;
1351 if (FD_ISSET(trig.rfd, &rfds)) break;
1352 if (FD_ISSET(conn.fd, &wfds)) {
1353 n = send(conn.fd, p + start, len, 0);
1354 if (n >= 0) { start += n; len -= n; }
1355 else if (errno != EAGAIN && errno != EWOULDBLOCK) goto err;
1361 except_syserror(jni, SYSERR, errno, "failed to send on connection");
1363 if (p) (*jni)->ReleaseByteArrayElements(jni, buf, p, JNI_ABORT);
1367 JNIEXPORT jint JNICALL JNIFUNC(recv)(JNIEnv *jni, jobject cls,
1368 wrapper wconn, jbyteArray buf,
1369 jint start, jint len,
1373 struct trigger trig;
1379 if (unwrap(jni, &conn, &conn_type, wconn)) goto end;
1380 if (unwrap(jni, &trig, &trigger_type, wtrig)) goto end;
1381 if (check_buffer_bounds(jni, "send", buf, start, len)) goto end;
1383 p = (*jni)->GetByteArrayElements(jni, buf, 0);
1387 if (maxfd < conn.fd) maxfd = conn.fd;
1389 FD_ZERO(&rfds); FD_SET(trig.rfd, &rfds); FD_SET(conn.fd, &rfds);
1390 rc = select(maxfd + 1, &rfds, 0, 0, 0); if (rc < 0) goto err;
1391 if (FD_ISSET(trig.rfd, &rfds)) {
1394 if (FD_ISSET(conn.fd, &rfds)) {
1395 rc = recv(conn.fd, p + start, len, 0);
1397 else if (errno != EAGAIN && errno != EWOULDBLOCK) goto err;
1404 except_syserror(jni, SYSERR, errno, "failed to read from connection");
1406 if (p) (*jni)->ReleaseByteArrayElements(jni, buf, p, 0);
1410 JNIEXPORT void JNICALL JNIFUNC(closeconn)(JNIEnv *jni, jobject cls,
1411 wrapper wconn, jint how)
1416 if (unwrap(jni, &conn, &conn_type, wconn)) goto end;
1417 if (conn.fd == -1) goto end;
1419 how &= CF_CLOSEMASK&~conn.f;
1421 if ((conn.f&CF_CLOSEMASK) == CF_CLOSEMASK) {
1425 if (how&CF_CLOSERD) shutdown(conn.fd, SHUT_RD);
1426 if (how&CF_CLOSEWR) shutdown(conn.fd, SHUT_WR);
1428 rc = update_wrapper(jni, &conn_type, wconn, &conn); assert(!rc);
1434 /*----- That's all, folks -------------------------------------------------*/