Skip to content

Commit 9b2d0ce

Browse files
authored
Merge pull request #17 from functionland/cp-mv-modiftime
Cp mv modiftime
2 parents d100517 + 4892f5c commit 9b2d0ce

File tree

7 files changed

+207
-22
lines changed

7 files changed

+207
-22
lines changed

Readme.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ Webnative Filesystem(WNFS) wrapper for Android.
77

88
## Usage
99

10+
Exposed endpoint: mkdir, writeFile, writeFileFromPath, readFile, readFileToPath, readFilestreamToPath, rm, cp, mv
11+
1012
- 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
1113

1214
- .aar files are available here that can be imported in ny framework: https://github.com/functionland/wnfs-build-aar
@@ -73,5 +75,5 @@ Please note the following might not be done in order:
7375
- [x] Add WNFS tree encryption key generation from an input (deterministically)
7476
- [x] add error catching
7577
- [x] add metadata to ls and make it array
76-
- [ ] Improve ls, read, and write functions to use a stream. ( :100: v1.0.0 Release here )
78+
- [x] Improve read function to use a stream. ( :100: v1 Release here )
7779
- [ ] remove dependancy to custom version of wnfs

appmock/src/androidTest/java/land/fx/app/WNFSTest.kt

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ class WNFSTest {
112112
}
113113
*/
114114

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

141-
config = rm(client, config.cid, config.private_ref, "root/testfrompath.txt")
142-
val content2 = readFile(client, config.cid, config.private_ref, "root/testfrompath.txt")
141+
config = cp(client, config.cid, config.private_ref, "root/testfrompath.txt", "root/testfrompathcp.txt") //target folder must exists
142+
val content_cp = readFile(client, config.cid, config.private_ref, "root/testfrompathcp.txt")
143+
Log.d("AppMock", "cp. content_cp="+String(content_cp))
144+
assert(content_cp contentEquals "Hello, World!".toByteArray())
145+
146+
config = mv(client, config.cid, config.private_ref, "root/testfrompath.txt", "root/testfrompathmv.txt") //target folder must exists
147+
val content_mv = readFile(client, config.cid, config.private_ref, "root/testfrompathmv.txt")
148+
Log.d("AppMock", "mv. content_mv="+String(content_mv))
149+
assert(content_mv contentEquals "Hello, World!".toByteArray())
150+
151+
config = rm(client, config.cid, config.private_ref, "root/testfrompathmv.txt")
152+
val content2 = readFile(client, config.cid, config.private_ref, "root/testfrompathmv.txt")
143153
Log.d("AppMock", "rm. content="+String(content2))
144154
assert(content2 contentEquals "".toByteArray())
145155

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

147162
config = writeFile(client, config.cid, config.private_ref, "root/test.txt", "Hello, World!".toByteArray())
148163
assertNotNull("cid should not be null", config.cid)

dep/wnfsutils/src/private_forest.rs

Lines changed: 96 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
//! This example shows how to add a directory to a private forest (also HAMT) which encrypts it.
22
//! It also shows how to retrieve encrypted nodes from the forest using `PrivateRef`s.
33
4-
use chrono::Utc;
4+
use chrono::{Utc, prelude::*};
55
use libipld::Cid;
66
use rand::{thread_rng, rngs::ThreadRng};
77
use std::{
88
rc::Rc,
99
fs::{File, OpenOptions},
10-
io::{Read, Write}
10+
io::{Read, Write},
11+
os::unix::fs::MetadataExt
1112
};
1213
use wnfs::{
1314
dagcbor, Hasher, utils,
@@ -266,16 +267,19 @@ impl<'a> PrivateDirectoryHelper<'a> {
266267
}
267268
}
268269

269-
fn get_file_as_byte_vec(&mut self, filename: &String) -> Result<Vec<u8>, String> {
270+
fn get_file_as_byte_vec(&mut self, filename: &String) -> Result<(Vec<u8>, i64), String> {
270271
let f = File::open(&filename);
271272
if f.is_ok() {
272-
let metadata = std::fs::metadata(&filename);
273-
if metadata.is_ok() {
274-
let mut buffer = vec![0; metadata.ok().unwrap().len() as usize];
273+
let metadata_res = std::fs::metadata(&filename);
274+
if metadata_res.is_ok() {
275+
let metadata = metadata_res.ok().unwrap();
276+
let modification_time_seconds = metadata.mtime();
277+
278+
let mut buffer = vec![0; metadata.len() as usize];
275279
f.ok().unwrap().read(&mut buffer).expect("buffer overflow");
276-
Ok(buffer)
280+
Ok((buffer, modification_time_seconds))
277281
} else {
278-
trace!("wnfsError in get_file_as_byte_vec, unable to read metadata: {:?}", metadata.err().unwrap());
282+
trace!("wnfsError in get_file_as_byte_vec, unable to read metadata: {:?}", metadata_res.err().unwrap());
279283
Err("wnfsError unable to read metadata".to_string())
280284
}
281285
} else {
@@ -287,10 +291,11 @@ impl<'a> PrivateDirectoryHelper<'a> {
287291

288292
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> {
289293
let content: Vec<u8>;
294+
let modification_time_seconds: i64;
290295
let try_content = self.get_file_as_byte_vec(filename);
291296
if try_content.is_ok() {
292-
content = try_content.ok().unwrap();
293-
let writefile_res = self.write_file(forest, root_dir, path_segments, content).await;
297+
(content, modification_time_seconds) = try_content.ok().unwrap();
298+
let writefile_res = self.write_file(forest, root_dir, path_segments, content, modification_time_seconds).await;
294299
if writefile_res.is_ok() {
295300
Ok(writefile_res.ok().unwrap())
296301
}else{
@@ -326,12 +331,17 @@ impl<'a> PrivateDirectoryHelper<'a> {
326331

327332
}
328333

329-
pub async fn write_file(&mut self, forest: Rc<PrivateForest>, root_dir: Rc<PrivateDirectory>, path_segments: &[String], content: Vec<u8>) -> Result<(Cid, PrivateRef), String> {
334+
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> {
335+
let mut modification_time_utc: DateTime<Utc>= Utc::now();
336+
if modification_time_seconds > 0 {
337+
let naive_datetime = NaiveDateTime::from_timestamp_opt(modification_time_seconds, 0).unwrap();
338+
modification_time_utc = DateTime::from_utc(naive_datetime, Utc);
339+
}
330340
let write_res = root_dir
331341
.write(
332342
path_segments,
333343
true,
334-
Utc::now(),
344+
modification_time_utc,
335345
content,
336346
forest,
337347
&mut self.store,
@@ -477,6 +487,65 @@ impl<'a> PrivateDirectoryHelper<'a> {
477487

478488
}
479489

490+
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> {
491+
let mv_result = root_dir
492+
.basic_mv(
493+
source_path_segments,
494+
target_path_segments,
495+
true,
496+
Utc::now(),
497+
forest,
498+
&mut self.store,
499+
&mut self.rng,
500+
)
501+
.await;
502+
if mv_result.is_ok() {
503+
let PrivateOpResult {
504+
root_dir, forest, ..
505+
} = mv_result.ok().unwrap();
506+
507+
let update_res = self.update_forest(forest).await;
508+
if update_res.is_ok() {
509+
Ok((update_res.ok().unwrap(), root_dir.header.get_private_ref()))
510+
} else {
511+
trace!("wnfsError occured in mv update_res: {:?}", update_res.as_ref().err().unwrap());
512+
Err(update_res.err().unwrap().to_string())
513+
}
514+
} else {
515+
trace!("wnfsError occured in mv mv_result: {:?}", mv_result.as_ref().err().unwrap());
516+
Err(mv_result.err().unwrap().to_string())
517+
}
518+
}
519+
520+
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> {
521+
let cp_result = root_dir
522+
.cp(
523+
source_path_segments,
524+
target_path_segments,
525+
true,
526+
Utc::now(),
527+
forest,
528+
&mut self.store,
529+
&mut self.rng
530+
)
531+
.await;
532+
if cp_result.is_ok() {
533+
let PrivateOpResult { forest, root_dir, .. }
534+
= cp_result.ok().unwrap();
535+
536+
let update_res = self.update_forest(forest).await;
537+
if update_res.is_ok() {
538+
Ok((update_res.ok().unwrap(), root_dir.header.get_private_ref()))
539+
} else {
540+
trace!("wnfsError occured in cp update_res: {:?}", update_res.as_ref().err().unwrap());
541+
Err(update_res.err().unwrap().to_string())
542+
}
543+
} else {
544+
trace!("wnfsError occured in cp cp_result: {:?}", cp_result.as_ref().err().unwrap());
545+
Err(cp_result.err().unwrap().to_string())
546+
}
547+
}
548+
480549
pub async fn ls_files(&mut self, forest: Rc<PrivateForest>, root_dir: Rc<PrivateDirectory>, path_segments: &[String]) -> Result<Vec<(String, Metadata)>, String> {
481550

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

541-
pub fn synced_write_file(&mut self, forest: Rc<PrivateForest>, root_dir: Rc<PrivateDirectory>, path_segments: &[String], content: Vec<u8>) -> Result<(Cid, PrivateRef), String>
610+
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>
542611
{
543612
let runtime =
544613
tokio::runtime::Runtime::new().expect("Unable to create a runtime");
545-
return runtime.block_on(self.write_file(forest, root_dir, path_segments, content));
614+
return runtime.block_on(self.write_file(forest, root_dir, path_segments, content, modification_time_seconds));
546615
}
547616

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

644+
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> {
645+
let runtime =
646+
tokio::runtime::Runtime::new().expect("Unable to create a runtime");
647+
return runtime.block_on(self.mv(forest, root_dir, source_path_segments, target_path_segments));
648+
}
649+
650+
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> {
651+
let runtime =
652+
tokio::runtime::Runtime::new().expect("Unable to create a runtime");
653+
return runtime.block_on(self.cp(forest, root_dir, source_path_segments, target_path_segments));
654+
}
655+
575656
pub fn synced_rm(&mut self, forest: Rc<PrivateForest>, root_dir: Rc<PrivateDirectory>, path_segments: &[String]) -> Result<(Cid, PrivateRef), String>
576657
{
577658
let runtime =
@@ -621,7 +702,7 @@ mod private_tests {
621702
let (forest_cid, private_ref) = helper.init(forest, empty_key).await;
622703
let forest = helper.load_forest(forest_cid).await.unwrap();
623704
let root_dir = helper.get_root_dir(forest.to_owned(), private_ref.to_owned()).await.unwrap();
624-
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;
705+
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;
625706
let forest = helper.load_forest(new_cid).await.unwrap();
626707
let ls_result = helper.ls_files(forest.to_owned(), root_dir.to_owned(), &["root".into()]).await;
627708
println!("ls: {:?}", ls_result);

jitpack.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@ before_install:
22
- git lfs pull
33
install:
44
- FILE="-Dfile=lib/build/outputs/aar/lib-release.aar"
5-
- mvn install:install-file $FILE -DgroupId=com.group.module -DartifactId=wnfs-android -Dversion=1.4.0 -Dpackaging=aar -DgeneratePom=true
5+
- mvn install:install-file $FILE -DgroupId=com.group.module -DartifactId=wnfs-android -Dversion=1.4.1 -Dpackaging=aar -DgeneratePom=true

lib/src/main/java/land/fx/wnfslib/Fs.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ public final class Fs {
2929

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

32+
private static native Config mvNative(Datastore datastore, String cid, String privateRef, String sourcePath, String targetPath);
33+
34+
private static native Config cpNative(Datastore datastore, String cid, String privateRef, String sourcePath, String targetPath);
35+
3236
private static native String readFileToPathNative(Datastore datastore, String cid, String privateRef, String path, String filename);
3337

3438
private static native String readFilestreamToPathNative(Datastore datastore, String cid, String privateRef, String path, String filename);
@@ -168,10 +172,21 @@ public static Config mkdir(Datastore datastore, String cid, String privateRef, S
168172
}
169173
}
170174

175+
@NonNull
171176
public static Config rm(Datastore datastore, String cid, String privateRef, String path) {
172177
return rmNative(datastore, cid, privateRef, path);
173178
}
174179

180+
@NonNull
181+
public static Config mv(Datastore datastore, String cid, String privateRef, String sourcePath, String targetPath) {
182+
return mvNative(datastore, cid, privateRef, sourcePath, targetPath);
183+
}
184+
185+
@NonNull
186+
public static Config cp(Datastore datastore, String cid, String privateRef, String sourcePath, String targetPath) {
187+
return cpNative(datastore, cid, privateRef, sourcePath, targetPath);
188+
}
189+
175190
@NonNull
176191
public static String readFileToPath(Datastore datastore, String cid, String privateRef, String path, String filename) throws Exception {
177192
try{

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@
55
<modelVersion>4.0.0</modelVersion>
66
<groupId>com.group.module</groupId>
77
<artifactId>wnfs-android</artifactId>
8-
<version>1.4.0</version>
8+
<version>1.4.1</version>
99
</project>

wnfslib/src/lib.rs

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,7 @@ pub mod android {
373373
let content = jbyte_array_to_vec(env, jni_content);
374374
//let (cid, private_ref) =
375375
let write_file_res =
376-
helper.synced_write_file(forest.to_owned(), root_dir, &path_segments, content);
376+
helper.synced_write_file(forest.to_owned(), root_dir, &path_segments, content, 0);
377377
trace!("**********************writeFileNative finished**************");
378378
if write_file_res.is_ok() {
379379
let (cid, private_ref) = write_file_res.ok().unwrap();
@@ -480,6 +480,78 @@ pub mod android {
480480
}
481481
}
482482

483+
#[no_mangle]
484+
pub extern "C" fn Java_land_fx_wnfslib_Fs_mvNative(
485+
env: JNIEnv,
486+
_: JClass,
487+
jni_fula_client: JObject,
488+
jni_cid: JString,
489+
jni_private_ref: JString,
490+
jni_source_path_segments: JString,
491+
jni_target_path_segments: JString,
492+
) -> jobject {
493+
trace!("**********************mvNative started**************");
494+
let store = JNIStore::new(env, jni_fula_client);
495+
let block_store = FFIFriendlyBlockStore::new(Box::new(store));
496+
let helper = &mut PrivateDirectoryHelper::new(block_store);
497+
498+
let cid = deserialize_cid(env, jni_cid);
499+
let private_ref = deserialize_private_ref(env, jni_private_ref);
500+
501+
let forest = helper.synced_load_forest(cid).unwrap();
502+
let root_dir = helper
503+
.synced_get_root_dir(forest.to_owned(), private_ref)
504+
.unwrap();
505+
let source_path_segments = prepare_path_segments(env, jni_source_path_segments);
506+
let target_path_segments = prepare_path_segments(env, jni_target_path_segments);
507+
let result = helper.synced_mv(forest.to_owned(), root_dir, &source_path_segments, &target_path_segments);
508+
trace!("**********************mvNative finished**************");
509+
if result.is_ok() {
510+
let (cid, private_ref) = result.ok().unwrap();
511+
return serialize_config(env, cid, private_ref);
512+
}else {
513+
trace!("wnfsError occured in Java_land_fx_wnfslib_Fs_mvNative: {:?}", result.err().unwrap());
514+
return JObject::null().into_inner();
515+
}
516+
517+
}
518+
519+
#[no_mangle]
520+
pub extern "C" fn Java_land_fx_wnfslib_Fs_cpNative(
521+
env: JNIEnv,
522+
_: JClass,
523+
jni_fula_client: JObject,
524+
jni_cid: JString,
525+
jni_private_ref: JString,
526+
jni_source_path_segments: JString,
527+
jni_target_path_segments: JString,
528+
) -> jobject {
529+
trace!("**********************cpNative started**************");
530+
let store = JNIStore::new(env, jni_fula_client);
531+
let block_store = FFIFriendlyBlockStore::new(Box::new(store));
532+
let helper = &mut PrivateDirectoryHelper::new(block_store);
533+
534+
let cid = deserialize_cid(env, jni_cid);
535+
let private_ref = deserialize_private_ref(env, jni_private_ref);
536+
537+
let forest = helper.synced_load_forest(cid).unwrap();
538+
let root_dir = helper
539+
.synced_get_root_dir(forest.to_owned(), private_ref)
540+
.unwrap();
541+
let source_path_segments = prepare_path_segments(env, jni_source_path_segments);
542+
let target_path_segments = prepare_path_segments(env, jni_target_path_segments);
543+
let result = helper.synced_cp(forest.to_owned(), root_dir, &source_path_segments, &target_path_segments);
544+
trace!("**********************mvNative finished**************");
545+
if result.is_ok() {
546+
let (cid, private_ref) = result.ok().unwrap();
547+
return serialize_config(env, cid, private_ref);
548+
}else {
549+
trace!("wnfsError occured in Java_land_fx_wnfslib_Fs_cpNative: {:?}", result.err().unwrap());
550+
return JObject::null().into_inner();
551+
}
552+
553+
}
554+
483555
#[no_mangle]
484556
pub extern "C" fn Java_land_fx_wnfslib_Fs_rmNative(
485557
env: JNIEnv,

0 commit comments

Comments
 (0)