diff options
author | Keuin <[email protected]> | 2024-01-20 00:37:36 +0800 |
---|---|---|
committer | Keuin <[email protected]> | 2024-01-20 01:44:12 +0800 |
commit | e3d0f924a416365ea12d8ace8569664cbb09dba5 (patch) | |
tree | 662d352bc0e2a5f2fdf404d566b2381a3ea9927a /src/lib/lib.rs |
Diffstat (limited to 'src/lib/lib.rs')
-rw-r--r-- | src/lib/lib.rs | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/src/lib/lib.rs b/src/lib/lib.rs new file mode 100644 index 0000000..3557335 --- /dev/null +++ b/src/lib/lib.rs @@ -0,0 +1,133 @@ +use crate::CopyError::{FileOpenError, IoctlError}; +use jni::descriptors::Desc; +use jni::objects::{JClass, JString}; +use jni::strings::JNIString; +use jni::sys::jstring; +use jni::JNIEnv; +use log::debug; +use nix::libc::{ioctl, FICLONE}; +use std::fmt::{Formatter, Write}; +use std::fs::OpenOptions; +use std::os::fd::AsRawFd; +use std::{fmt, io}; + +fn throw<S: Into<JNIString>>(env: &mut JNIEnv, msg: S) { + throw_new(env, "java/lang/RuntimeException", msg); +} + +fn throw_new<'a, 'b, S: Into<JNIString>, T: Desc<'a, JClass<'b>>>( + env: &mut JNIEnv<'a>, + ex: T, + msg: S, +) { + if let Err(why) = env.throw_new(ex, msg) { + env.fatal_error(format!("failed to throw new exception: {why}")); + } +} + +#[no_mangle] +pub extern "system" fn Java_com_keuin_kbackupfabric_util_cow_FileCowCopier_init<'local>( + _env: JNIEnv<'local>, _class: JClass<'local>) { + env_logger::init(); + debug!("kbackup-cow init"); +} + +#[no_mangle] +pub extern "system" fn Java_com_keuin_kbackupfabric_util_cow_FileCowCopier_getVersion<'local>( + env: JNIEnv<'local>, + _class: JClass<'local>, +) -> jstring { + match env.new_string(option_env!("CARGO_PKG_VERSION").unwrap_or("unknown")) { + Ok(s) => s.into_raw(), + Err(why) => env.fatal_error(format!("failed to create String: {why}")), + } +} + +#[no_mangle] +pub extern "system" fn Java_com_keuin_kbackupfabric_util_cow_FileCowCopier_copy<'local>( + mut env: JNIEnv<'local>, + _class: JClass<'local>, + dst: JString<'local>, + src: JString<'local>, +) { + unsafe { + let src = match env.get_string_unchecked(&src) { + Ok(s) => s, + Err(why) => { + throw_new( + &mut env, + "java/lang/IllegalArgumentException", + format!("invalid src: {why}"), + ); + return; + } + }; + let dst = match env.get_string_unchecked(&dst) { + Ok(s) => s, + Err(why) => { + throw_new( + &mut env, + "java/lang/IllegalArgumentException", + format!("invalid dst: {why}"), + ); + return; + } + }; + match copy(dst.into(), src.into()) { + Ok(_) => {} + Err(why) => { + throw_new(&mut env, "java/io/IOException", format!("{}", why)); + } + }; + } +} + +enum CopyError { + IoctlError(io::Error), + FileOpenError(String, io::Error), +} + +impl fmt::Display for CopyError { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match &self { + IoctlError(why) => { + write!(f, "ioctl error: {why}") + } + FileOpenError(path, why) => { + write!(f, "error opening file `{path}`: {why}") + } + } + } +} + +fn copy(dst: String, src: String) -> Result<(), CopyError> { + debug!("copy src: {src}, dst: {dst}"); + let f_src = match OpenOptions::new().read(true).open(&src) { + Ok(f) => f, + Err(why) => { + return Err(FileOpenError(src.clone(), why)); + } + }; + let f_dst = match OpenOptions::new() + .read(true) + .write(true) + .create(true) + .open(&dst) + { + Ok(f) => f, + Err(why) => { + return Err(FileOpenError(dst.clone(), why)); + } + }; + unsafe { + let fd_dst = f_dst.as_raw_fd(); + let fd_src = f_src.as_raw_fd(); + debug!("fd_src: {fd_src}, fd_dst: {fd_dst}"); + if ioctl(fd_dst, FICLONE, fd_src) != 0 { + let err = io::Error::last_os_error(); + debug!("ioctl failed: {:?}", err); + return Err(IoctlError(err)); + } + } + Ok(()) +} |