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 /*----- Low-level file operations -----------------------------------------*/
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.
775 * So the other option is to implement them ourselves.
778 JNIEXPORT
void JNIFUNC(unlink
)(JNIEnv
*jni
, jobject cls
, jobject path
)
780 const char *pathstr
= 0;
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
);
789 put_cstring(jni
, path
, pathstr
);
792 JNIEXPORT
void JNIFUNC(rmdir
)(JNIEnv
*jni
, jobject cls
, jobject path
)
794 const char *pathstr
= 0;
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
);
803 put_cstring(jni
, path
, pathstr
);
806 JNIEXPORT
void JNIFUNC(mkdir
)(JNIEnv
*jni
, jobject cls
,
807 jobject path
, jint mode
)
809 const char *pathstr
= 0;
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
);
818 put_cstring(jni
, path
, pathstr
);
821 JNIEXPORT
void JNIFUNC(mkfile
)(JNIEnv
*jni
, jobject cls
,
822 jobject path
, jint mode
)
824 const char *pathstr
= 0;
827 pathstr
= get_cstring(jni
, path
); if (!pathstr
) goto end
;
828 fd
= open(pathstr
, O_WRONLY
| O_CREAT
| O_EXCL
, mode
);
830 except_syserror(jni
, SYSERR
, errno
,
831 "failed to create fresh file `%s'", pathstr
);
835 if (fd
!= -1) close(fd
);
836 put_cstring(jni
, path
, pathstr
);
839 JNIEXPORT
void JNIFUNC(rename
)(JNIEnv
*jni
, jobject cls
,
840 jobject from
, jobject to
)
842 const char *fromstr
= 0, *tostr
= 0;
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
);
852 put_cstring(jni
, from
, fromstr
);
853 put_cstring(jni
, to
, tostr
);
859 struct native_base _base
;
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
)
867 const char *pathstr
= 0;
871 struct stat st0
, st1
;
875 pathstr
= get_cstring(jni
, path
); if (!pathstr
) goto end
;
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
;
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
; }
892 INIT_NATIVE(lockf
, &lk
); lk
.fd
= fd
; fd
= -1;
893 r
= wrap(jni
, &lockf_type
, &lk
);
897 except_syserror(jni
, SYSERR
, errno
, "failed to lock file `%s'", pathstr
);
899 if (fd
!= -1) close(fd
);
900 put_cstring(jni
, path
, pathstr
);
904 JNIEXPORT
void JNIFUNC(unlock
)(JNIEnv
*jni
, jobject cls
, wrapper wlk
)
910 if (unwrap(jni
, &lk
, &lockf_type
, wlk
)) goto end
;
911 if (lk
.fd
== -1) goto end
;
913 l
.l_whence
= SEEK_SET
;
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
);
922 static jlong
xlttimespec(const struct timespec
*ts
)
923 { return (1000*(jlong
)ts
->tv_sec
+ ts
->tv_nsec
/1000000); }
925 static jobject
xltstat(JNIEnv
*jni
, const struct stat
*st
)
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;
940 cls
= (*jni
)->FindClass(jni
, STAT
); assert(cls
);
941 init
= (*jni
)->GetMethodID(jni
, cls
, "<init>", "(IIJIIIIIIJIJJJJ)V");
943 return ((*jni
)->NewObject(jni
, cls
, init
,
944 (jint
)major(st
->st_dev
), (jint
)minor(st
->st_dev
),
948 (jint
)st
->st_uid
, (jint
)st
->st_gid
,
949 (jint
)major(st
->st_rdev
), (jint
)minor(st
->st_rdev
),
951 (jint
)st
->st_blksize
, (jlong
)st
->st_blocks
,
952 xlttimespec(&st
->st_atim
),
953 xlttimespec(&st
->st_mtim
),
954 xlttimespec(&st
->st_ctim
)));
957 JNIEXPORT jobject
JNIFUNC(stat
)(JNIEnv
*jni
, jobject cls
, jobject path
)
960 const char *pathstr
= 0;
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
);
969 r
= xltstat(jni
, &st
);
971 put_cstring(jni
, path
, pathstr
);
975 JNIEXPORT jobject
JNIFUNC(lstat
)(JNIEnv
*jni
, jobject cls
, jobject path
)
978 const char *pathstr
= 0;
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
);
987 r
= xltstat(jni
, &st
);
989 put_cstring(jni
, path
, pathstr
);
994 struct native_base _base
;
997 static const struct native_type dir_type
=
998 { "dir", sizeof(struct dir
), 0x0f5ca477 };
1000 JNIEXPORT jobject
JNIFUNC(opendir
)(JNIEnv
*jni
, jobject cls
, jobject path
)
1002 const char *pathstr
= 0;
1006 pathstr
= get_cstring(jni
, path
); if (!pathstr
) goto end
;
1007 INIT_NATIVE(dir
, &dir
);
1008 dir
.d
= opendir(pathstr
);
1010 except_syserror(jni
, SYSERR
, errno
,
1011 "failed to open directory `%s'", pathstr
);
1014 r
= wrap(jni
, &dir_type
, &dir
);
1016 put_cstring(jni
, path
, pathstr
);
1020 JNIEXPORT jbyteArray
JNIFUNC(readdir
)(JNIEnv
*jni
, jobject cls
,
1021 jobject path
, jobject wdir
)
1023 const char *pathstr
= 0;
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
);
1032 pathstr
= get_cstring(jni
, path
); if (!pathstr
) goto end
;
1033 except_syserror(jni
, SYSERR
, errno
,
1034 "failed to read directory `%s'", pathstr
);
1037 if (d
) r
= wrap_cstring(jni
, d
->d_name
);
1039 put_cstring(jni
, path
, pathstr
);
1043 JNIEXPORT
void JNIFUNC(closedir
)(JNIEnv
*jni
, jobject cls
,
1044 jobject path
, jobject wdir
)
1046 const char *pathstr
= 0;
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
);
1058 if (update_wrapper(jni
, &dir_type
, wdir
, &dir
)) goto end
;
1060 put_cstring(jni
, path
, pathstr
);
1063 /*----- A server connection, using a Unix-domain socket -------------------*/
1066 struct native_base _base
;
1069 #define CF_CLOSERD 1u
1070 #define CF_CLOSEWR 2u
1071 #define CF_CLOSEMASK (CF_CLOSERD | CF_CLOSEWR)
1073 static const struct native_type conn_type
=
1074 { "conn", sizeof(struct conn
), 0xed030167 };
1076 JNIEXPORT wrapper JNICALL
JNIFUNC(connect
)(JNIEnv
*jni
, jobject cls
,
1080 struct sockaddr_un sun
;
1081 const char *pathstr
= 0;
1085 pathstr
= get_cstring(jni
, path
); if (!pathstr
) goto end
;
1086 if (strlen(pathstr
) >= sizeof(sun
.sun_path
)) {
1088 "Unix-domain socket path `%s' too long", pathstr
);
1092 INIT_NATIVE(conn
, &conn
);
1093 fd
= socket(SOCK_STREAM
, PF_UNIX
, 0); if (fd
< 0) goto err
;
1095 sun
.sun_family
= AF_UNIX
;
1096 strcpy(sun
.sun_path
, (char *)pathstr
);
1097 if (connect(fd
, (struct sockaddr
*)&sun
, sizeof(sun
))) goto err
;
1099 conn
.fd
= fd
; fd
= -1;
1101 ret
= wrap(jni
, &conn_type
, &conn
);
1105 except_syserror(jni
, SYSERR
, errno
,
1106 "failed to connect to Unix-domain socket `%s'", pathstr
);
1108 if (fd
== -1) close(fd
);
1109 put_cstring(jni
, path
, pathstr
);
1113 static int check_buffer_bounds(JNIEnv
*jni
, const char *what
,
1114 jbyteArray buf
, jint start
, jint len
)
1119 cls
= (*jni
)->FindClass(jni
, "[B"); assert(cls
);
1120 if (!(*jni
)->IsInstanceOf(jni
, buf
, cls
)) {
1122 "expected a byte array");
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
);
1131 if (len
> bufsz
- start
) {
1132 except(jni
, BOUNDSERR
,
1133 "bad %s buffer bounds: length %d > remaining buffer size %d",
1134 len
, bufsz
- start
);
1140 JNIEXPORT
void JNICALL
JNIFUNC(send
)(JNIEnv
*jni
, jobject cls
,
1141 wrapper wconn
, jbyteArray buf
,
1142 jint start
, jint len
)
1148 if (unwrap(jni
, &conn
, &conn_type
, wconn
)) goto end
;
1149 if (check_buffer_bounds(jni
, "send", buf
, start
, len
)) goto end
;
1151 p
= (*jni
)->GetByteArrayElements(jni
, buf
, 0);
1155 n
= send(conn
.fd
, p
+ start
, len
, 0);
1157 except_syserror(jni
, SYSERR
,
1158 errno
, "failed to send on connection");
1161 start
+= n
; len
-= n
;
1165 if (p
) (*jni
)->ReleaseByteArrayElements(jni
, buf
, p
, JNI_ABORT
);
1169 JNIEXPORT jint JNICALL
JNIFUNC(recv
)(JNIEnv
*jni
, jobject cls
,
1170 wrapper wconn
, jbyteArray buf
,
1171 jint start
, jint len
)
1177 if (unwrap(jni
, &conn
, &conn_type
, wconn
)) goto end
;
1178 if (check_buffer_bounds(jni
, "send", buf
, start
, len
)) goto end
;
1180 p
= (*jni
)->GetByteArrayElements(jni
, buf
, 0);
1183 rc
= recv(conn
.fd
, p
+ start
, len
, 0);
1185 except_syserror(jni
, SYSERR
,
1186 errno
, "failed to read from connection");
1192 if (p
) (*jni
)->ReleaseByteArrayElements(jni
, buf
, p
, 0);
1196 JNIEXPORT
void JNICALL
JNIFUNC(closeconn
)(JNIEnv
*jni
, jobject cls
,
1197 wrapper wconn
, jint how
)
1202 if (unwrap(jni
, &conn
, &conn_type
, wconn
)) goto end
;
1203 if (conn
.fd
== -1) goto end
;
1205 how
&= CF_CLOSEMASK
&~conn
.f
;
1207 if ((conn
.f
&CF_CLOSEMASK
) == CF_CLOSEMASK
) {
1211 if (how
&CF_CLOSERD
) shutdown(conn
.fd
, SHUT_RD
);
1212 if (how
&CF_CLOSEWR
) shutdown(conn
.fd
, SHUT_WR
);
1214 rc
= update_wrapper(jni
, &conn_type
, wconn
, &conn
); assert(!rc
);
1220 /*----- That's all, folks -------------------------------------------------*/