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/socket.h>
44 #include <sys/sysmacros.h>
50 #include <mLib/align.h>
51 #include <mLib/bits.h>
52 #include <mLib/dstr.h>
53 #include <mLib/macros.h>
55 #include <catacomb/ghash.h>
59 /*----- Magic class names and similar -------------------------------------*/
61 /* The name decoration is horrific. Hide it. */
62 #define JNIFUNC(f) Java_uk_org_distorted_tripe_sys_package_00024_##f
64 /* The little class for bundling up error codes. */
65 #define ERRENTRY "uk/org/distorted/tripe/sys/package$ErrorEntry"
67 /* The `stat' class. */
68 #define STAT "uk/org/distorted/tripe/sys/package$FileInfo"
70 /* Exception class names. */
71 #define NULLERR "java/lang/NullPointerException"
72 #define TYPEERR "uk/org/distorted/tripe/sys/package$NativeObjectTypeException"
73 #define SYSERR "uk/org/distorted/tripe/sys/package$SystemError"
74 #define ARGERR "java/lang/IllegalArgumentException"
75 #define BOUNDSERR "java/lang/IndexOutOfBoundsException"
77 /*----- Miscellaneous utilities -------------------------------------------*/
79 static void put_cstring(JNIEnv *jni, jbyteArray v, const char *p)
80 { if (p) (*jni)->ReleaseByteArrayElements(jni, v, (jbyte *)p, JNI_ABORT); }
82 static void vexcept(JNIEnv *jni, const char *clsname,
83 const char *msg, va_list *ap)
89 cls = (*jni)->FindClass(jni, clsname); assert(cls);
91 rc = (*jni)->ThrowNew(jni, cls, 0);
93 dstr_vputf(&d, msg, ap);
94 rc = (*jni)->ThrowNew(jni, cls, d.buf);
101 static void except(JNIEnv *jni, const char *clsname, const char *msg, ...)
106 vexcept(jni, clsname, msg, &ap);
111 static void dump_bytes(const void *p, size_t n, size_t o)
113 const unsigned char *q = p;
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);
131 static void dump_byte_array(JNIEnv *jni, const char *what, jbyteArray v)
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);
141 (*jni)->ReleaseByteArrayElements(jni, v, p, JNI_ABORT);
145 static jbyteArray wrap_cstring(JNIEnv *jni, const char *p)
153 v = (*jni)->NewByteArray(jni, n); if (!v) return (0);
154 q = (*jni)->GetByteArrayElements(jni, v, 0); if (!q) return (0);
156 (*jni)->ReleaseByteArrayElements(jni, v, q, 0);
160 static const char *get_cstring(JNIEnv *jni, jbyteArray v)
162 if (!v) { except(jni, NULLERR, 0); return (0); }
163 return ((const char *)(*jni)->GetByteArrayElements(jni, v, 0));
166 static void vexcept_syserror(JNIEnv *jni, const char *clsname,
167 int err, const char *msg, va_list *ap)
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);
181 e = (*jni)->NewObject(jni, cls, init, err, msgstr); assert(e);
182 rc = (*jni)->Throw(jni, e); assert(!rc);
185 static void except_syserror(JNIEnv *jni, const char *clsname,
186 int err, const char *msg, ...)
191 vexcept_syserror(jni, clsname, err, msg, &ap);
195 /*----- Wrapping native types ---------------------------------------------*/
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.
209 typedef jbyteArray wrapper;
215 static int unwrap(JNIEnv *jni, void *p,
216 const struct native_type *ty, wrapper w)
220 struct native_base *b = p;
223 if (!w) { except(jni, NULLERR, 0); return (-1); }
224 cls = (*jni)->FindClass(jni, "[B"); assert(cls);
225 if (!(*jni)->IsInstanceOf(jni, w, cls)) {
227 "corrupted native object wrapper: expected a byte array");
230 n = (*jni)->GetArrayLength(jni, w);
233 "corrupted native object wrapper: wrong size for `%s'",
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) {
242 "corrupted native object wrapper: expected tag for `%s'",
249 static int update_wrapper(JNIEnv *jni, const struct native_type *ty,
250 wrapper w, const void *p)
254 q = (*jni)->GetByteArrayElements(jni, w, 0); if (!q) return (-1);
255 memcpy(q, p, ty->sz);
256 (*jni)->ReleaseByteArrayElements(jni, w, q, 0);
260 static wrapper wrap(JNIEnv *jni, const struct native_type *ty, const void *p)
264 w = (*jni)->NewByteArray(jni, ty->sz); if (!w) return (0);
265 if (update_wrapper(jni, ty, w, p)) return (0);
269 #define INIT_NATIVE(type, p) do (p)->_base.tag = type##_type.tag; while (0)
271 /*----- Crypto information ------------------------------------------------*/
273 JNIEXPORT jint JNICALL JNIFUNC(hashsz)(JNIEnv *jni, jobject cls,
280 hname = (*jni)->GetStringUTFChars(jni, hnamestr, 0);
281 if (!hname) goto end;
282 hc = ghash_byname(hname); if (!hc) goto end;
286 if (hname) (*jni)->ReleaseStringUTFChars(jni, hnamestr, hname);
290 /*----- System errors -----------------------------------------------------*/
292 static const struct errtab { const char *tag; int err; } errtab[] = {
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.
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
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)))
322 (goto-char (point-min))
323 (search-forward (concat "***" "BEGIN errtab" "***"))
324 (beginning-of-line 2)
325 (delete-region (point)
327 (search-forward "***END***")
331 (insert (format "#ifdef %s\n { \"%s\", %s },\n#endif\n"
339 { "ENOENT", ENOENT },
357 { "ENOEXEC", ENOEXEC },
363 { "ECHILD", ECHILD },
366 { "EAGAIN", EAGAIN },
369 { "ENOMEM", ENOMEM },
372 { "EACCES", EACCES },
375 { "EFAULT", EFAULT },
378 { "ENOTBLK", ENOTBLK },
384 { "EEXIST", EEXIST },
390 { "ENODEV", ENODEV },
393 { "ENOTDIR", ENOTDIR },
396 { "EISDIR", EISDIR },
399 { "EINVAL", EINVAL },
402 { "ENFILE", ENFILE },
405 { "EMFILE", EMFILE },
408 { "ENOTTY", ENOTTY },
411 { "ETXTBSY", ETXTBSY },
417 { "ENOSPC", ENOSPC },
420 { "ESPIPE", ESPIPE },
426 { "EMLINK", EMLINK },
435 { "ERANGE", ERANGE },
438 { "EDEADLK", EDEADLK },
441 { "ENAMETOOLONG", ENAMETOOLONG },
444 { "ENOLCK", ENOLCK },
447 { "ENOSYS", ENOSYS },
450 { "ENOTEMPTY", ENOTEMPTY },
456 { "EWOULDBLOCK", EWOULDBLOCK },
459 { "ENOMSG", ENOMSG },
465 { "ECHRNG", ECHRNG },
468 { "EL2NSYNC", EL2NSYNC },
471 { "EL3HLT", EL3HLT },
474 { "EL3RST", EL3RST },
477 { "ELNRNG", ELNRNG },
480 { "EUNATCH", EUNATCH },
483 { "ENOCSI", ENOCSI },
486 { "EL2HLT", EL2HLT },
495 { "EXFULL", EXFULL },
498 { "ENOANO", ENOANO },
501 { "EBADRQC", EBADRQC },
504 { "EBADSLT", EBADSLT },
507 { "EDEADLOCK", EDEADLOCK },
510 { "EBFONT", EBFONT },
513 { "ENOSTR", ENOSTR },
516 { "ENODATA", ENODATA },
525 { "ENONET", ENONET },
528 { "ENOPKG", ENOPKG },
531 { "EREMOTE", EREMOTE },
534 { "ENOLINK", ENOLINK },
540 { "ESRMNT", ESRMNT },
546 { "EPROTO", EPROTO },
549 { "EMULTIHOP", EMULTIHOP },
552 { "EDOTDOT", EDOTDOT },
555 { "EBADMSG", EBADMSG },
558 { "EOVERFLOW", EOVERFLOW },
561 { "ENOTUNIQ", ENOTUNIQ },
564 { "EBADFD", EBADFD },
567 { "EREMCHG", EREMCHG },
570 { "ELIBACC", ELIBACC },
573 { "ELIBBAD", ELIBBAD },
576 { "ELIBSCN", ELIBSCN },
579 { "ELIBMAX", ELIBMAX },
582 { "ELIBEXEC", ELIBEXEC },
585 { "EILSEQ", EILSEQ },
588 { "ERESTART", ERESTART },
591 { "ESTRPIPE", ESTRPIPE },
594 { "EUSERS", EUSERS },
597 { "ENOTSOCK", ENOTSOCK },
600 { "EDESTADDRREQ", EDESTADDRREQ },
603 { "EMSGSIZE", EMSGSIZE },
606 { "EPROTOTYPE", EPROTOTYPE },
609 { "ENOPROTOOPT", ENOPROTOOPT },
611 #ifdef EPROTONOSUPPORT
612 { "EPROTONOSUPPORT", EPROTONOSUPPORT },
614 #ifdef ESOCKTNOSUPPORT
615 { "ESOCKTNOSUPPORT", ESOCKTNOSUPPORT },
618 { "EOPNOTSUPP", EOPNOTSUPP },
621 { "EPFNOSUPPORT", EPFNOSUPPORT },
624 { "EAFNOSUPPORT", EAFNOSUPPORT },
627 { "EADDRINUSE", EADDRINUSE },
630 { "EADDRNOTAVAIL", EADDRNOTAVAIL },
633 { "ENETDOWN", ENETDOWN },
636 { "ENETUNREACH", ENETUNREACH },
639 { "ENETRESET", ENETRESET },
642 { "ECONNABORTED", ECONNABORTED },
645 { "ECONNRESET", ECONNRESET },
648 { "ENOBUFS", ENOBUFS },
651 { "EISCONN", EISCONN },
654 { "ENOTCONN", ENOTCONN },
657 { "ESHUTDOWN", ESHUTDOWN },
660 { "ETOOMANYREFS", ETOOMANYREFS },
663 { "ETIMEDOUT", ETIMEDOUT },
666 { "ECONNREFUSED", ECONNREFUSED },
669 { "EHOSTDOWN", EHOSTDOWN },
672 { "EHOSTUNREACH", EHOSTUNREACH },
675 { "EALREADY", EALREADY },
678 { "EINPROGRESS", EINPROGRESS },
681 { "ESTALE", ESTALE },
684 { "EUCLEAN", EUCLEAN },
687 { "ENOTNAM", ENOTNAM },
690 { "ENAVAIL", ENAVAIL },
693 { "EISNAM", EISNAM },
696 { "EREMOTEIO", EREMOTEIO },
699 { "EDQUOT", EDQUOT },
702 { "ENOMEDIUM", ENOMEDIUM },
705 { "EMEDIUMTYPE", EMEDIUMTYPE },
708 { "ECANCELED", ECANCELED },
711 { "ENOKEY", ENOKEY },
714 { "EKEYEXPIRED", EKEYEXPIRED },
717 { "EKEYREVOKED", EKEYREVOKED },
720 { "EKEYREJECTED", EKEYREJECTED },
723 { "EOWNERDEAD", EOWNERDEAD },
725 #ifdef ENOTRECOVERABLE
726 { "ENOTRECOVERABLE", ENOTRECOVERABLE },
729 { "ERFKILL", ERFKILL },
732 { "EHWPOISON", EHWPOISON },
737 JNIEXPORT jobject JNIFUNC(errtab)(JNIEnv *jni, jobject cls)
746 (*jni)->FindClass(jni, ERRENTRY);
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");
753 for (i = 0; i < N(errtab); i++) {
754 e = (*jni)->NewObject(jni, eltcls, init,
755 (*jni)->NewStringUTF(jni, errtab[i].tag),
757 (*jni)->SetObjectArrayElement(jni, v, i, e);
762 JNIEXPORT jobject JNIFUNC(strerror)(JNIEnv *jni, jobject cls, jint err)
763 { return (wrap_cstring(jni, strerror(err))); }
765 /*----- Messing with file descriptors -------------------------------------*/
767 static void fdguts(JNIEnv *jni, jclass *cls, jfieldID *fid)
769 *cls = (*jni)->FindClass(jni, "java/io/FileDescriptor"); assert(cls);
770 *fid = (*jni)->GetFieldID(jni, *cls, "fd", "I"); // OpenJDK
771 if (!*fid) *fid = (*jni)->GetFieldID(jni, *cls, "descriptor", "I"); // Android
775 static int fdint(JNIEnv *jni, jobject jfd)
780 fdguts(jni, &cls, &fid);
781 return ((*jni)->GetIntField(jni, jfd, fid));
784 static jobject newfd(JNIEnv *jni, int fd)
791 fdguts(jni, &cls, &fid);
792 init = (*jni)->GetMethodID(jni, cls, "<init>", "()V"); assert(init);
793 jfd = (*jni)->NewObject(jni, cls, init);
794 (*jni)->SetIntField(jni, jfd, fid, fd);
798 JNIEXPORT jint JNIFUNC(fdint)(JNIEnv *jni, jobject cls, jobject jfd)
799 { return (fdint(jni, jfd)); }
801 JNIEXPORT jobject JNIFUNC(newfd)(JNIEnv *jni, jobject cls, jint fd)
802 { return (newfd(jni, fd)); }
804 JNIEXPORT jboolean JNIFUNC(isatty)(JNIEnv *jni, jobject cls, jobject jfd)
805 { return (isatty(fdint(jni, jfd))); }
807 /*----- Low-level file operations -----------------------------------------*/
809 /* Java has these already, as methods on `java.io.File' objects. Alas, these
810 * methods are useless at reporting errors: they tend to return a `boolean'
811 * success/ fail indicator, and throw away any more detailed information.
812 * There's better functionality in `java.nio.file.Files', but that only turns
813 * up in Android API 26 (in 7.0 Nougat). There's `android.system.Os', which
814 * has a bunch of POSIX-shaped functions -- but they're only in Android API
815 * 21 (in 5.0 Lollipop), and there's nothing in the support library to help.
817 * So the other option is to implement them ourselves.
820 JNIEXPORT void JNIFUNC(unlink)(JNIEnv *jni, jobject cls, jobject path)
822 const char *pathstr = 0;
824 pathstr = get_cstring(jni, path); if (!pathstr) goto end;
825 if (unlink(pathstr)) {
826 except_syserror(jni, SYSERR, errno,
827 "failed to delete file `%s'", pathstr);
831 put_cstring(jni, path, pathstr);
834 JNIEXPORT void JNIFUNC(rmdir)(JNIEnv *jni, jobject cls, jobject path)
836 const char *pathstr = 0;
838 pathstr = get_cstring(jni, path); if (!pathstr) goto end;
839 if (rmdir(pathstr)) {
840 except_syserror(jni, SYSERR, errno,
841 "failed to delete directory `%s'", pathstr);
845 put_cstring(jni, path, pathstr);
848 JNIEXPORT void JNIFUNC(mkdir)(JNIEnv *jni, jobject cls,
849 jobject path, jint mode)
851 const char *pathstr = 0;
853 pathstr = get_cstring(jni, path); if (!pathstr) goto end;
854 if (mkdir(pathstr, mode)) {
855 except_syserror(jni, SYSERR, errno,
856 "failed to create directory `%s'", pathstr);
860 put_cstring(jni, path, pathstr);
863 JNIEXPORT void JNIFUNC(mkfile)(JNIEnv *jni, jobject cls,
864 jobject path, jint mode)
866 const char *pathstr = 0;
869 pathstr = get_cstring(jni, path); if (!pathstr) goto end;
870 fd = open(pathstr, O_WRONLY | O_CREAT | O_EXCL, mode);
872 except_syserror(jni, SYSERR, errno,
873 "failed to create fresh file `%s'", pathstr);
877 if (fd != -1) close(fd);
878 put_cstring(jni, path, pathstr);
881 JNIEXPORT void JNIFUNC(rename)(JNIEnv *jni, jobject cls,
882 jobject from, jobject to)
884 const char *fromstr = 0, *tostr = 0;
886 fromstr = get_cstring(jni, from); if (!fromstr) goto end;
887 tostr = get_cstring(jni, to); if (!tostr) goto end;
888 if (rename(fromstr, tostr)) {
889 except_syserror(jni, SYSERR, errno,
890 "failed to rename `%s' as `%s'", fromstr, tostr);
894 put_cstring(jni, from, fromstr);
895 put_cstring(jni, to, tostr);
898 #define LKF_EXCL 0x1000u
899 #define LKF_WAIT 0x2000u
901 struct native_base _base;
904 static struct native_type lockf_type =
905 { "lock", sizeof(struct lockf), 0xb2648926};
906 JNIEXPORT wrapper JNIFUNC(lock)(JNIEnv *jni, jobject cls,
907 jobject path, jint flags)
909 const char *pathstr = 0;
913 struct stat st0, st1;
917 pathstr = get_cstring(jni, path); if (!pathstr) goto end;
920 fd = open(pathstr, O_RDWR | O_CREAT, flags&07777); if (fd < 0) goto err;
921 if (fstat(fd, &st0)) goto err;
922 f = fcntl(fd, F_GETFD); if (f < 0) goto err;
923 if (fcntl(fd, F_SETFD, f | FD_CLOEXEC)) goto err;
924 l.l_type = (flags&LKF_EXCL) ? F_WRLCK : F_RDLCK;
925 l.l_whence = SEEK_SET;
928 if (fcntl(fd, (flags&LKF_WAIT) ? F_SETLKW : F_SETLK, &l)) goto err;
929 if (stat(pathstr, &st1))
930 { if (errno == ENOENT) goto again; else goto err; }
931 if (st0.st_dev != st1.st_dev || st0.st_ino != st1.st_ino)
932 { close(fd); fd = -1; goto again; }
934 INIT_NATIVE(lockf, &lk); lk.fd = fd; fd = -1;
935 r = wrap(jni, &lockf_type, &lk);
939 except_syserror(jni, SYSERR, errno, "failed to lock file `%s'", pathstr);
941 if (fd != -1) close(fd);
942 put_cstring(jni, path, pathstr);
946 JNIEXPORT void JNIFUNC(unlock)(JNIEnv *jni, jobject cls, wrapper wlk)
952 if (unwrap(jni, &lk, &lockf_type, wlk)) goto end;
953 if (lk.fd == -1) goto end;
955 l.l_whence = SEEK_SET;
958 if (fcntl(lk.fd, F_SETLK, &l)) goto end;
959 close(lk.fd); lk.fd = -1;
960 rc = update_wrapper(jni, &lockf_type, wlk, &lk); assert(!rc);
964 static jlong xlttimespec(const struct timespec *ts)
965 { return (1000*(jlong)ts->tv_sec + ts->tv_nsec/1000000); }
967 static jobject xltstat(JNIEnv *jni, const struct stat *st)
973 modehack = st->st_mode&07777;
974 if (S_ISFIFO(st->st_mode)) modehack |= 0010000;
975 else if (S_ISCHR(st->st_mode)) modehack |= 0020000;
976 else if (S_ISDIR(st->st_mode)) modehack |= 0040000;
977 else if (S_ISBLK(st->st_mode)) modehack |= 0060000;
978 else if (S_ISREG(st->st_mode)) modehack |= 0100000;
979 else if (S_ISLNK(st->st_mode)) modehack |= 0120000;
980 else if (S_ISSOCK(st->st_mode)) modehack |= 0140000;
982 cls = (*jni)->FindClass(jni, STAT); assert(cls);
983 init = (*jni)->GetMethodID(jni, cls, "<init>", "(IIJIIIIIIJIJJJJ)V");
985 return ((*jni)->NewObject(jni, cls, init,
986 (jint)major(st->st_dev), (jint)minor(st->st_dev),
990 (jint)st->st_uid, (jint)st->st_gid,
991 (jint)major(st->st_rdev), (jint)minor(st->st_rdev),
993 (jint)st->st_blksize, (jlong)st->st_blocks,
994 xlttimespec(&st->st_atim),
995 xlttimespec(&st->st_mtim),
996 xlttimespec(&st->st_ctim)));
999 JNIEXPORT jobject JNIFUNC(stat)(JNIEnv *jni, jobject cls, jobject path)
1002 const char *pathstr = 0;
1005 pathstr = get_cstring(jni, path); if (!pathstr) goto end;
1006 if (stat(pathstr, &st)) {
1007 except_syserror(jni, SYSERR, errno,
1008 "failed to read information about `%s'", pathstr);
1011 r = xltstat(jni, &st);
1013 put_cstring(jni, path, pathstr);
1017 JNIEXPORT jobject JNIFUNC(lstat)(JNIEnv *jni, jobject cls, jobject path)
1020 const char *pathstr = 0;
1023 pathstr = get_cstring(jni, path); if (!pathstr) goto end;
1024 if (lstat(pathstr, &st)) {
1025 except_syserror(jni, SYSERR, errno,
1026 "failed to read information about `%s'", pathstr);
1029 r = xltstat(jni, &st);
1031 put_cstring(jni, path, pathstr);
1036 struct native_base _base;
1039 static const struct native_type dir_type =
1040 { "dir", sizeof(struct dir), 0x0f5ca477 };
1042 JNIEXPORT jobject JNIFUNC(opendir)(JNIEnv *jni, jobject cls, jobject path)
1044 const char *pathstr = 0;
1048 pathstr = get_cstring(jni, path); if (!pathstr) goto end;
1049 INIT_NATIVE(dir, &dir);
1050 dir.d = opendir(pathstr);
1052 except_syserror(jni, SYSERR, errno,
1053 "failed to open directory `%s'", pathstr);
1056 r = wrap(jni, &dir_type, &dir);
1058 put_cstring(jni, path, pathstr);
1062 JNIEXPORT jbyteArray JNIFUNC(readdir)(JNIEnv *jni, jobject cls,
1063 jobject path, jobject wdir)
1065 const char *pathstr = 0;
1070 if (unwrap(jni, &dir, &dir_type, wdir)) goto end;
1071 if (!dir.d) { except(jni, ARGERR, "directory has been closed"); goto end; }
1072 errno = 0; d = readdir(dir.d);
1074 pathstr = get_cstring(jni, path); if (!pathstr) goto end;
1075 except_syserror(jni, SYSERR, errno,
1076 "failed to read directory `%s'", pathstr);
1079 if (d) r = wrap_cstring(jni, d->d_name);
1081 put_cstring(jni, path, pathstr);
1085 JNIEXPORT void JNIFUNC(closedir)(JNIEnv *jni, jobject cls,
1086 jobject path, jobject wdir)
1088 const char *pathstr = 0;
1091 if (unwrap(jni, &dir, &dir_type, wdir)) goto end;
1092 if (!dir.d) goto end;
1093 if (closedir(dir.d)) {
1094 pathstr = get_cstring(jni, path); if (!pathstr) goto end;
1095 except_syserror(jni, SYSERR, errno,
1096 "failed to close directory `%s'", pathstr);
1100 if (update_wrapper(jni, &dir_type, wdir, &dir)) goto end;
1102 put_cstring(jni, path, pathstr);
1105 /*----- A server connection, using a Unix-domain socket -------------------*/
1108 struct native_base _base;
1111 #define CF_CLOSERD 1u
1112 #define CF_CLOSEWR 2u
1113 #define CF_CLOSEMASK (CF_CLOSERD | CF_CLOSEWR)
1115 static const struct native_type conn_type =
1116 { "conn", sizeof(struct conn), 0xed030167 };
1118 JNIEXPORT wrapper JNICALL JNIFUNC(connect)(JNIEnv *jni, jobject cls,
1122 struct sockaddr_un sun;
1123 const char *pathstr = 0;
1127 pathstr = get_cstring(jni, path); if (!pathstr) goto end;
1128 if (strlen(pathstr) >= sizeof(sun.sun_path)) {
1130 "Unix-domain socket path `%s' too long", pathstr);
1134 INIT_NATIVE(conn, &conn);
1135 fd = socket(SOCK_STREAM, PF_UNIX, 0); if (fd < 0) goto err;
1137 sun.sun_family = AF_UNIX;
1138 strcpy(sun.sun_path, (char *)pathstr);
1139 if (connect(fd, (struct sockaddr *)&sun, sizeof(sun))) goto err;
1141 conn.fd = fd; fd = -1;
1143 ret = wrap(jni, &conn_type, &conn);
1147 except_syserror(jni, SYSERR, errno,
1148 "failed to connect to Unix-domain socket `%s'", pathstr);
1150 if (fd == -1) close(fd);
1151 put_cstring(jni, path, pathstr);
1155 static int check_buffer_bounds(JNIEnv *jni, const char *what,
1156 jbyteArray buf, jint start, jint len)
1161 cls = (*jni)->FindClass(jni, "[B"); assert(cls);
1162 if (!(*jni)->IsInstanceOf(jni, buf, cls)) {
1164 "expected a byte array");
1167 bufsz = (*jni)->GetArrayLength(jni, buf);
1168 if (start > bufsz) {
1169 except(jni, BOUNDSERR,
1170 "bad %s buffer bounds: start %d > buffer size %d", start, bufsz);
1173 if (len > bufsz - start) {
1174 except(jni, BOUNDSERR,
1175 "bad %s buffer bounds: length %d > remaining buffer size %d",
1176 len, bufsz - start);
1182 JNIEXPORT void JNICALL JNIFUNC(send)(JNIEnv *jni, jobject cls,
1183 wrapper wconn, jbyteArray buf,
1184 jint start, jint len)
1190 if (unwrap(jni, &conn, &conn_type, wconn)) goto end;
1191 if (check_buffer_bounds(jni, "send", buf, start, len)) goto end;
1193 p = (*jni)->GetByteArrayElements(jni, buf, 0);
1197 n = send(conn.fd, p + start, len, 0);
1199 except_syserror(jni, SYSERR,
1200 errno, "failed to send on connection");
1203 start += n; len -= n;
1207 if (p) (*jni)->ReleaseByteArrayElements(jni, buf, p, JNI_ABORT);
1211 JNIEXPORT jint JNICALL JNIFUNC(recv)(JNIEnv *jni, jobject cls,
1212 wrapper wconn, jbyteArray buf,
1213 jint start, jint len)
1219 if (unwrap(jni, &conn, &conn_type, wconn)) goto end;
1220 if (check_buffer_bounds(jni, "send", buf, start, len)) goto end;
1222 p = (*jni)->GetByteArrayElements(jni, buf, 0);
1225 rc = recv(conn.fd, p + start, len, 0);
1227 except_syserror(jni, SYSERR,
1228 errno, "failed to read from connection");
1234 if (p) (*jni)->ReleaseByteArrayElements(jni, buf, p, 0);
1238 JNIEXPORT void JNICALL JNIFUNC(closeconn)(JNIEnv *jni, jobject cls,
1239 wrapper wconn, jint how)
1244 if (unwrap(jni, &conn, &conn_type, wconn)) goto end;
1245 if (conn.fd == -1) goto end;
1247 how &= CF_CLOSEMASK&~conn.f;
1249 if ((conn.f&CF_CLOSEMASK) == CF_CLOSEMASK) {
1253 if (how&CF_CLOSERD) shutdown(conn.fd, SHUT_RD);
1254 if (how&CF_CLOSEWR) shutdown(conn.fd, SHUT_WR);
1256 rc = update_wrapper(jni, &conn_type, wconn, &conn); assert(!rc);
1262 /*----- That's all, folks -------------------------------------------------*/