Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cp mv modiftime #17

Merged
merged 2 commits into from
Dec 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ Webnative Filesystem(WNFS) wrapper for Android.

## Usage

Exposed endpoint: mkdir, writeFile, writeFileFromPath, readFile, readFileToPath, readFilestreamToPath, rm, cp, mv

- Library is already packaged and published on Jitpack and ready to be used in Android applications (Java, Kotlin). Please checkout the AppMock for all usage examples: https://github.com/functionland/wnfs-android/blob/main/appmock/src/androidTest/java/land/fx/app/WNFSTest.kt

- .aar files are available here that can be imported in ny framework: https://github.com/functionland/wnfs-build-aar
Expand Down Expand Up @@ -73,5 +75,5 @@ Please note the following might not be done in order:
- [x] Add WNFS tree encryption key generation from an input (deterministically)
- [x] add error catching
- [x] add metadata to ls and make it array
- [ ] Improve ls, read, and write functions to use a stream. ( :100: v1.0.0 Release here )
- [x] Improve read function to use a stream. ( :100: v1 Release here )
- [ ] remove dependancy to custom version of wnfs
21 changes: 18 additions & 3 deletions appmock/src/androidTest/java/land/fx/app/WNFSTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ class WNFSTest {
}
*/

config = writeFileFromPath(client, config.cid, config.private_ref, "root/testfrompath.txt", pathString+"/test.txt")
config = writeFileFromPath(client, config.cid, config.private_ref, "root/testfrompath.txt", pathString+"/test.txt") //target folder does not need to exist
Log.d("AppMock", "config writeFile. cid="+config.cid+" & private_ref="+config.private_ref)
assertNotNull("config should not be null", config)
assertNotNull("cid should not be null", config.cid)
Expand All @@ -138,11 +138,26 @@ class WNFSTest {
assert(readcontentstream contentEquals "Hello, World!".toByteArray())
Log.d("AppMock", "readFileFromPathOfReadstreamTo. content="+String(readcontentstream))

config = rm(client, config.cid, config.private_ref, "root/testfrompath.txt")
val content2 = readFile(client, config.cid, config.private_ref, "root/testfrompath.txt")
config = cp(client, config.cid, config.private_ref, "root/testfrompath.txt", "root/testfrompathcp.txt") //target folder must exists
val content_cp = readFile(client, config.cid, config.private_ref, "root/testfrompathcp.txt")
Log.d("AppMock", "cp. content_cp="+String(content_cp))
assert(content_cp contentEquals "Hello, World!".toByteArray())

config = mv(client, config.cid, config.private_ref, "root/testfrompath.txt", "root/testfrompathmv.txt") //target folder must exists
val content_mv = readFile(client, config.cid, config.private_ref, "root/testfrompathmv.txt")
Log.d("AppMock", "mv. content_mv="+String(content_mv))
assert(content_mv contentEquals "Hello, World!".toByteArray())

config = rm(client, config.cid, config.private_ref, "root/testfrompathmv.txt")
val content2 = readFile(client, config.cid, config.private_ref, "root/testfrompathmv.txt")
Log.d("AppMock", "rm. content="+String(content2))
assert(content2 contentEquals "".toByteArray())

config = rm(client, config.cid, config.private_ref, "root/testfrompathcp.txt")
val content3 = readFile(client, config.cid, config.private_ref, "root/testfrompathcp.txt")
Log.d("AppMock", "rm. content="+String(content3))
assert(content3 contentEquals "".toByteArray())


config = writeFile(client, config.cid, config.private_ref, "root/test.txt", "Hello, World!".toByteArray())
assertNotNull("cid should not be null", config.cid)
Expand Down
111 changes: 96 additions & 15 deletions dep/wnfsutils/src/private_forest.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
//! This example shows how to add a directory to a private forest (also HAMT) which encrypts it.
//! It also shows how to retrieve encrypted nodes from the forest using `PrivateRef`s.

use chrono::Utc;
use chrono::{Utc, prelude::*};
use libipld::Cid;
use rand::{thread_rng, rngs::ThreadRng};
use std::{
rc::Rc,
fs::{File, OpenOptions},
io::{Read, Write}
io::{Read, Write},
os::unix::fs::MetadataExt
};
use wnfs::{
dagcbor, Hasher, utils,
Expand Down Expand Up @@ -266,16 +267,19 @@ impl<'a> PrivateDirectoryHelper<'a> {
}
}

fn get_file_as_byte_vec(&mut self, filename: &String) -> Result<Vec<u8>, String> {
fn get_file_as_byte_vec(&mut self, filename: &String) -> Result<(Vec<u8>, i64), String> {
let f = File::open(&filename);
if f.is_ok() {
let metadata = std::fs::metadata(&filename);
if metadata.is_ok() {
let mut buffer = vec![0; metadata.ok().unwrap().len() as usize];
let metadata_res = std::fs::metadata(&filename);
if metadata_res.is_ok() {
let metadata = metadata_res.ok().unwrap();
let modification_time_seconds = metadata.mtime();

let mut buffer = vec![0; metadata.len() as usize];
f.ok().unwrap().read(&mut buffer).expect("buffer overflow");
Ok(buffer)
Ok((buffer, modification_time_seconds))
} else {
trace!("wnfsError in get_file_as_byte_vec, unable to read metadata: {:?}", metadata.err().unwrap());
trace!("wnfsError in get_file_as_byte_vec, unable to read metadata: {:?}", metadata_res.err().unwrap());
Err("wnfsError unable to read metadata".to_string())
}
} else {
Expand All @@ -287,10 +291,11 @@ impl<'a> PrivateDirectoryHelper<'a> {

pub async fn write_file_from_path(&mut self, forest: Rc<PrivateForest>, root_dir: Rc<PrivateDirectory>, path_segments: &[String], filename: &String) -> Result<(Cid, PrivateRef), String> {
let content: Vec<u8>;
let modification_time_seconds: i64;
let try_content = self.get_file_as_byte_vec(filename);
if try_content.is_ok() {
content = try_content.ok().unwrap();
let writefile_res = self.write_file(forest, root_dir, path_segments, content).await;
(content, modification_time_seconds) = try_content.ok().unwrap();
let writefile_res = self.write_file(forest, root_dir, path_segments, content, modification_time_seconds).await;
if writefile_res.is_ok() {
Ok(writefile_res.ok().unwrap())
}else{
Expand Down Expand Up @@ -326,12 +331,17 @@ impl<'a> PrivateDirectoryHelper<'a> {

}

pub async fn write_file(&mut self, forest: Rc<PrivateForest>, root_dir: Rc<PrivateDirectory>, path_segments: &[String], content: Vec<u8>) -> Result<(Cid, PrivateRef), String> {
pub async fn write_file(&mut self, forest: Rc<PrivateForest>, root_dir: Rc<PrivateDirectory>, path_segments: &[String], content: Vec<u8>, modification_time_seconds: i64) -> Result<(Cid, PrivateRef), String> {
let mut modification_time_utc: DateTime<Utc>= Utc::now();
if modification_time_seconds > 0 {
let naive_datetime = NaiveDateTime::from_timestamp_opt(modification_time_seconds, 0).unwrap();
modification_time_utc = DateTime::from_utc(naive_datetime, Utc);
}
let write_res = root_dir
.write(
path_segments,
true,
Utc::now(),
modification_time_utc,
content,
forest,
&mut self.store,
Expand Down Expand Up @@ -477,6 +487,65 @@ impl<'a> PrivateDirectoryHelper<'a> {

}

pub async fn mv(&mut self, forest: Rc<PrivateForest>, root_dir: Rc<PrivateDirectory>, source_path_segments: &[String], target_path_segments: &[String]) -> Result<(Cid, PrivateRef), String> {
let mv_result = root_dir
.basic_mv(
source_path_segments,
target_path_segments,
true,
Utc::now(),
forest,
&mut self.store,
&mut self.rng,
)
.await;
if mv_result.is_ok() {
let PrivateOpResult {
root_dir, forest, ..
} = mv_result.ok().unwrap();

let update_res = self.update_forest(forest).await;
if update_res.is_ok() {
Ok((update_res.ok().unwrap(), root_dir.header.get_private_ref()))
} else {
trace!("wnfsError occured in mv update_res: {:?}", update_res.as_ref().err().unwrap());
Err(update_res.err().unwrap().to_string())
}
} else {
trace!("wnfsError occured in mv mv_result: {:?}", mv_result.as_ref().err().unwrap());
Err(mv_result.err().unwrap().to_string())
}
}

pub async fn cp(&mut self, forest: Rc<PrivateForest>, root_dir: Rc<PrivateDirectory>, source_path_segments: &[String], target_path_segments: &[String]) -> Result<(Cid, PrivateRef), String> {
let cp_result = root_dir
.cp(
source_path_segments,
target_path_segments,
true,
Utc::now(),
forest,
&mut self.store,
&mut self.rng
)
.await;
if cp_result.is_ok() {
let PrivateOpResult { forest, root_dir, .. }
= cp_result.ok().unwrap();

let update_res = self.update_forest(forest).await;
if update_res.is_ok() {
Ok((update_res.ok().unwrap(), root_dir.header.get_private_ref()))
} else {
trace!("wnfsError occured in cp update_res: {:?}", update_res.as_ref().err().unwrap());
Err(update_res.err().unwrap().to_string())
}
} else {
trace!("wnfsError occured in cp cp_result: {:?}", cp_result.as_ref().err().unwrap());
Err(cp_result.err().unwrap().to_string())
}
}

pub async fn ls_files(&mut self, forest: Rc<PrivateForest>, root_dir: Rc<PrivateDirectory>, path_segments: &[String]) -> Result<Vec<(String, Metadata)>, String> {

let res = root_dir
Expand Down Expand Up @@ -538,11 +607,11 @@ impl<'a> PrivateDirectoryHelper<'a> {
return runtime.block_on(self.write_file_from_path(forest, root_dir, path_segments, filename));
}

pub fn synced_write_file(&mut self, forest: Rc<PrivateForest>, root_dir: Rc<PrivateDirectory>, path_segments: &[String], content: Vec<u8>) -> Result<(Cid, PrivateRef), String>
pub fn synced_write_file(&mut self, forest: Rc<PrivateForest>, root_dir: Rc<PrivateDirectory>, path_segments: &[String], content: Vec<u8>, modification_time_seconds: i64) -> Result<(Cid, PrivateRef), String>
{
let runtime =
tokio::runtime::Runtime::new().expect("Unable to create a runtime");
return runtime.block_on(self.write_file(forest, root_dir, path_segments, content));
return runtime.block_on(self.write_file(forest, root_dir, path_segments, content, modification_time_seconds));
}

pub fn synced_read_file_to_path(&mut self, forest: Rc<PrivateForest>, root_dir: Rc<PrivateDirectory>, path_segments: &[String], filename: &String) -> Result<String, String>
Expand Down Expand Up @@ -572,6 +641,18 @@ impl<'a> PrivateDirectoryHelper<'a> {
return runtime.block_on(self.mkdir(forest, root_dir, path_segments));
}

pub fn synced_mv(&mut self, forest: Rc<PrivateForest>, root_dir: Rc<PrivateDirectory>, source_path_segments: &[String], target_path_segments: &[String]) -> Result<(Cid, PrivateRef), String> {
let runtime =
tokio::runtime::Runtime::new().expect("Unable to create a runtime");
return runtime.block_on(self.mv(forest, root_dir, source_path_segments, target_path_segments));
}

pub fn synced_cp(&mut self, forest: Rc<PrivateForest>, root_dir: Rc<PrivateDirectory>, source_path_segments: &[String], target_path_segments: &[String]) -> Result<(Cid, PrivateRef), String> {
let runtime =
tokio::runtime::Runtime::new().expect("Unable to create a runtime");
return runtime.block_on(self.cp(forest, root_dir, source_path_segments, target_path_segments));
}

pub fn synced_rm(&mut self, forest: Rc<PrivateForest>, root_dir: Rc<PrivateDirectory>, path_segments: &[String]) -> Result<(Cid, PrivateRef), String>
{
let runtime =
Expand Down Expand Up @@ -621,7 +702,7 @@ mod private_tests {
let (forest_cid, private_ref) = helper.init(forest, empty_key).await;
let forest = helper.load_forest(forest_cid).await.unwrap();
let root_dir = helper.get_root_dir(forest.to_owned(), private_ref.to_owned()).await.unwrap();
let (new_cid, _) = helper.write_file(forest.to_owned(), root_dir.to_owned(), &["root".into(), "hello".into(), "world.txt".into()], b"hello, world!".to_vec()).await;
let (new_cid, _) = helper.write_file(forest.to_owned(), root_dir.to_owned(), &["root".into(), "hello".into(), "world.txt".into()], b"hello, world!".to_vec(), 0).await;
let forest = helper.load_forest(new_cid).await.unwrap();
let ls_result = helper.ls_files(forest.to_owned(), root_dir.to_owned(), &["root".into()]).await;
println!("ls: {:?}", ls_result);
Expand Down
2 changes: 1 addition & 1 deletion jitpack.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ before_install:
- git lfs pull
install:
- FILE="-Dfile=lib/build/outputs/aar/lib-release.aar"
- mvn install:install-file $FILE -DgroupId=com.group.module -DartifactId=wnfs-android -Dversion=1.4.0 -Dpackaging=aar -DgeneratePom=true
- mvn install:install-file $FILE -DgroupId=com.group.module -DartifactId=wnfs-android -Dversion=1.4.1 -Dpackaging=aar -DgeneratePom=true
15 changes: 15 additions & 0 deletions lib/src/main/java/land/fx/wnfslib/Fs.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ public final class Fs {

private static native Config rmNative(Datastore datastore, String cid, String privateRef, String path);

private static native Config mvNative(Datastore datastore, String cid, String privateRef, String sourcePath, String targetPath);

private static native Config cpNative(Datastore datastore, String cid, String privateRef, String sourcePath, String targetPath);

private static native String readFileToPathNative(Datastore datastore, String cid, String privateRef, String path, String filename);

private static native String readFilestreamToPathNative(Datastore datastore, String cid, String privateRef, String path, String filename);
Expand Down Expand Up @@ -168,10 +172,21 @@ public static Config mkdir(Datastore datastore, String cid, String privateRef, S
}
}

@NonNull
public static Config rm(Datastore datastore, String cid, String privateRef, String path) {
return rmNative(datastore, cid, privateRef, path);
}

@NonNull
public static Config mv(Datastore datastore, String cid, String privateRef, String sourcePath, String targetPath) {
return mvNative(datastore, cid, privateRef, sourcePath, targetPath);
}

@NonNull
public static Config cp(Datastore datastore, String cid, String privateRef, String sourcePath, String targetPath) {
return cpNative(datastore, cid, privateRef, sourcePath, targetPath);
}

@NonNull
public static String readFileToPath(Datastore datastore, String cid, String privateRef, String path, String filename) throws Exception {
try{
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.group.module</groupId>
<artifactId>wnfs-android</artifactId>
<version>1.4.0</version>
<version>1.4.1</version>
</project>
74 changes: 73 additions & 1 deletion wnfslib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,7 @@ pub mod android {
let content = jbyte_array_to_vec(env, jni_content);
//let (cid, private_ref) =
let write_file_res =
helper.synced_write_file(forest.to_owned(), root_dir, &path_segments, content);
helper.synced_write_file(forest.to_owned(), root_dir, &path_segments, content, 0);
trace!("**********************writeFileNative finished**************");
if write_file_res.is_ok() {
let (cid, private_ref) = write_file_res.ok().unwrap();
Expand Down Expand Up @@ -480,6 +480,78 @@ pub mod android {
}
}

#[no_mangle]
pub extern "C" fn Java_land_fx_wnfslib_Fs_mvNative(
env: JNIEnv,
_: JClass,
jni_fula_client: JObject,
jni_cid: JString,
jni_private_ref: JString,
jni_source_path_segments: JString,
jni_target_path_segments: JString,
) -> jobject {
trace!("**********************mvNative started**************");
let store = JNIStore::new(env, jni_fula_client);
let block_store = FFIFriendlyBlockStore::new(Box::new(store));
let helper = &mut PrivateDirectoryHelper::new(block_store);

let cid = deserialize_cid(env, jni_cid);
let private_ref = deserialize_private_ref(env, jni_private_ref);

let forest = helper.synced_load_forest(cid).unwrap();
let root_dir = helper
.synced_get_root_dir(forest.to_owned(), private_ref)
.unwrap();
let source_path_segments = prepare_path_segments(env, jni_source_path_segments);
let target_path_segments = prepare_path_segments(env, jni_target_path_segments);
let result = helper.synced_mv(forest.to_owned(), root_dir, &source_path_segments, &target_path_segments);
trace!("**********************mvNative finished**************");
if result.is_ok() {
let (cid, private_ref) = result.ok().unwrap();
return serialize_config(env, cid, private_ref);
}else {
trace!("wnfsError occured in Java_land_fx_wnfslib_Fs_mvNative: {:?}", result.err().unwrap());
return JObject::null().into_inner();
}

}

#[no_mangle]
pub extern "C" fn Java_land_fx_wnfslib_Fs_cpNative(
env: JNIEnv,
_: JClass,
jni_fula_client: JObject,
jni_cid: JString,
jni_private_ref: JString,
jni_source_path_segments: JString,
jni_target_path_segments: JString,
) -> jobject {
trace!("**********************cpNative started**************");
let store = JNIStore::new(env, jni_fula_client);
let block_store = FFIFriendlyBlockStore::new(Box::new(store));
let helper = &mut PrivateDirectoryHelper::new(block_store);

let cid = deserialize_cid(env, jni_cid);
let private_ref = deserialize_private_ref(env, jni_private_ref);

let forest = helper.synced_load_forest(cid).unwrap();
let root_dir = helper
.synced_get_root_dir(forest.to_owned(), private_ref)
.unwrap();
let source_path_segments = prepare_path_segments(env, jni_source_path_segments);
let target_path_segments = prepare_path_segments(env, jni_target_path_segments);
let result = helper.synced_cp(forest.to_owned(), root_dir, &source_path_segments, &target_path_segments);
trace!("**********************mvNative finished**************");
if result.is_ok() {
let (cid, private_ref) = result.ok().unwrap();
return serialize_config(env, cid, private_ref);
}else {
trace!("wnfsError occured in Java_land_fx_wnfslib_Fs_cpNative: {:?}", result.err().unwrap());
return JObject::null().into_inner();
}

}

#[no_mangle]
pub extern "C" fn Java_land_fx_wnfslib_Fs_rmNative(
env: JNIEnv,
Expand Down