Skip to content

Instantly share code, notes, and snippets.

@apangin
Last active March 14, 2023 23:46
Show Gist options
  • Save apangin/42e3b5c53555284c3ce0 to your computer and use it in GitHub Desktop.
Save apangin/42e3b5c53555284c3ce0 to your computer and use it in GitHub Desktop.
Patch to enable O_SYNC on RandomAccessFile
/*
* Compile: gcc -I $JAVA_HOME/include -I $JAVA_HOME/include/linux -shared -fPIC -O3 -olibrafpatch.so -Wl,-soname,librafpatch.so rafpatch.c $JAVA_HOME/lib/amd64/libjava.so
* Run: java -agentpath:/path/to/librafpatch.so MainClass
*/
#include <jvmti.h>
#include <jni.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
static jfieldID randomAccessFile_fd;
static jfieldID fileDescriptor_fd;
JNIEXPORT const char* JNICALL
JNU_GetStringPlatformChars(JNIEnv* env, jstring jstr, jboolean* isCopy);
JNIEXPORT void JNICALL
JNU_ReleaseStringPlatformChars(JNIEnv* env, jstring jstr, const char* str);
JNIEXPORT void JNICALL
JNU_ThrowNullPointerException(JNIEnv *env, const char* msg);
void throwFileNotFoundException(JNIEnv* env, jstring path);
#define RESTARTABLE(cmd, result) \
do { \
result = cmd; \
} while(result == -1 && errno == EINTR);
int handleOpen(const char* path, int oflag, int mode) {
int fd;
RESTARTABLE(open64(path, oflag, mode), fd);
if (fd != -1) {
struct stat buf64;
int result;
RESTARTABLE(fstat64(fd, &buf64), result);
if (result == -1) {
close(fd);
return -1;
} else if (S_ISDIR(buf64.st_mode)) {
close(fd);
errno = EISDIR;
return -1;
}
}
return fd;
}
JNIEXPORT void JNICALL
Hook_java_io_RandomAccessFile_open0(JNIEnv* env, jobject this, jstring path, jint mode) {
int flags = 0;
if (mode & 1) {
flags = O_RDONLY;
} else if (mode & 2) {
flags = O_RDWR | O_CREAT;
if (mode & 4) {
flags |= O_SYNC;
} else if (mode & 8) {
flags |= O_DSYNC;
}
}
if (path == NULL) {
JNU_ThrowNullPointerException(env, NULL);
return;
}
const char* ps = JNU_GetStringPlatformChars(env, path, NULL);
if (ps == NULL) return;
// Strip trailing slashes
char* p = (char*)ps + strlen(ps) - 1;
while (p > ps && *p == '/') {
*p-- = '\0';
}
int fd = handleOpen(ps, flags, 0666);
if (fd == -1) {
throwFileNotFoundException(env, path);
} else {
jobject fileDescriptor = (*env)->GetObjectField(env, this, randomAccessFile_fd);
if (fileDescriptor != NULL) {
(*env)->SetIntField(env, fileDescriptor, fileDescriptor_fd, fd);
}
}
JNU_ReleaseStringPlatformChars(env, path, ps); \
}
void JNICALL VMInit(jvmtiEnv* jvmti, JNIEnv* env, jthread thread) {
jclass randomAccessFile = (*env)->FindClass(env, "java/io/RandomAccessFile");
jclass fileDescriptor = (*env)->FindClass(env, "java/io/FileDescriptor");
randomAccessFile_fd = (*env)->GetFieldID(env, randomAccessFile, "fd", "Ljava/io/FileDescriptor;");
fileDescriptor_fd = (*env)->GetFieldID(env, fileDescriptor, "fd", "I");
JNINativeMethod open0 = {"open0", "(Ljava/lang/String;I)V", Hook_java_io_RandomAccessFile_open0};
(*env)->RegisterNatives(env, randomAccessFile, &open0, 1);
printf("RandomAccessFile patch installed\n");
}
JNIEXPORT jint JNICALL
Agent_OnLoad(JavaVM* vm, char *options, void* reserved) {
jvmtiEnv* jvmti;
jvmtiEventCallbacks callbacks;
(*vm)->GetEnv(vm, (void**)&jvmti, JVMTI_VERSION_1_0);
memset(&callbacks, 0, sizeof(callbacks));
callbacks.VMInit = VMInit;
(*jvmti)->SetEventCallbacks(jvmti, &callbacks, sizeof(callbacks));
(*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, NULL);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment