Skip to content

Instantly share code, notes, and snippets.

@Alexander--
Created July 3, 2015 03:58
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save Alexander--/f6555cb40aa4a6c65de8 to your computer and use it in GitHub Desktop.
Useless JNI
#include "jni.h"
#include <android/log.h>
#include <stddef.h>
#include <fcntl.h> // splice ?
#include <sys/sendfile.h> // sendfile ?
#include <sys/stat.h> // fstat ?
#include <unistd.h> // seek ?
#include <stdio.h> // sprintf
#define LOG_TAG "fdshare-native"
#define MIN(a,b) \
({ __typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a < _b ? _a : _b; })
#define PIPE_SIZE 4096
static int throwIOException(JNIEnv *env, int errnum, const char *message)
{
__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "%s errno %s(%d)", message, strerror(errno), errno);
if (errnum != 0) {
const char *s = strerror(errnum);
if (strcmp(s, "Unknown error") != 0)
message = s;
}
jclass exClass;
const char *className = "java/io/IOException";
exClass = (*env) -> FindClass(env, className);
return (*env) -> ThrowNew(env, exClass, message);
}
// the source is a pipe or socket - splice it directly
static int doPipeCopy(jint fd1, jint fd2, jlong start, ssize_t num) {
if (start != 0) {
// it is improbable, but someone can consider himself clever enough to offer us a pipe with mandatory offset...
// let's just flush the beginning to /dev/null
int nullFd;
if ((nullFd = open("/dev/null", O_WRONLY, S_IRWXU|S_IRWXG)) == -1)
return -1;
int skipResult = doPipeCopy(fd1, nullFd, 0, (ssize_t) start);
int errsv = errno;
close(nullFd);
errno = errsv;
if (skipResult < 0)
return skipResult;
}
ssize_t copied = 0;
ssize_t remaining = num == -1 ? PIPE_SIZE : num;
while(num == -1 || (remaining -= copied)) {
errno = 0;
copied = splice(fd1, NULL, fd2, NULL, MIN(remaining, PIPE_SIZE), SPLICE_F_MORE | SPLICE_F_MOVE);
if (copied < 1 && errno) {
return -1;
} else if (copied == 0) {
break;
}
}
return copied;
}
// splice a file via page cache
static int doZeroCopy(jint fd1, jint fd2, ssize_t num) {
ssize_t copied = 0;
ssize_t remaining = num == -1 ? PIPE_SIZE : num;
while(num == -1 || (remaining -= copied)) {
errno = 0;
if ((copied = sendfile(fd1, fd2, NULL, MIN(remaining, PIPE_SIZE))) < 1 && errno)
break;
}
return copied;
}
JNIEXPORT jstring Java_com_example_FdUtil_getFdPathInternal(JNIEnv *env, jint descriptor)
{
// The filesystem name may not fit in PATH_MAX, but all workarounds
// (as well as resulting strings) are prone to OutOfMemoryError.
// The proper solution would, probably, include writing a specialized
// CharSequence. Too much pain, too little gain.
char buf[PATH_MAX + 1] = { 0 };
char procFile[25];
sprintf(procFile, "/proc/self/fd/%d", descriptor);
if (readlink(procFile, buf, sizeof(buf)) == -1) {
// the descriptor is no more, became inaccessible etc.
throwIOException(env, errno, "readlink() failed");
return NULL;
}
if (buf[PATH_MAX] != 0) {
// the name is over PATH_MAX bytes long, the caller is at fault
// for dealing with such tricky descriptors
throwIOException(env, errno, "The path is too long");
return NULL;
}
if (buf[0] != '/') {
// the name is not in filesystem namespace, e.g. a socket,
// pipe or something like that
//throwIOException(env, errno, "The descriptor does not belong to file with name");
return NULL;
}
// doing stat on file does not give any guarantees, that it
// will remain valid, and on Android it likely to be
// inaccessible to us anyway let's just hope
return (*env) -> NewStringUTF(env, buf);
}
JNIEXPORT void Java_net_sf_fdshare_FdUtil_spliceInternal(JNIEnv *env, jint fd1, jint fd2, jlong start, jlong num)
{
struct stat fStat;
int sourceFd;
if (fstat(sourceFd, &fStat))
return;
if (S_ISREG(fStat.st_mode))
{
if (lseek(fd1, (off_t) start, SEEK_SET) != start) {
throwIOException(env, errno, "seek() failed");
return;
}
ssize_t realNum = num < 0 ? fStat.st_size : MIN((ssize_t) num, fStat.st_size);
if (doZeroCopy(fd1, fd2, realNum) == -1)
throwIOException(env, errno, "failed to copy a file");
}
else if (S_ISFIFO(fStat.st_mode) || S_ISSOCK(fStat.st_mode))
{
if (doPipeCopy(fd1, fd2, start, (ssize_t) num) == -1)
throwIOException(env, errno, "failed to copy from pipe");
}
else throwIOException(env, errno, "unsupported file type");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment