@@ -133,6 +133,36 @@ impl From<Error> for super::Error {
133
133
}
134
134
}
135
135
136
+ // #[cfg(target_os = "android")]
137
+ fn rename_noreplace <
138
+ P1 : ?Sized + nix:: NixPath ,
139
+ P2 : ?Sized + nix:: NixPath ,
140
+ > (
141
+ old_path : & P1 ,
142
+ new_path : & P2 ,
143
+ ) -> std:: result:: Result < ( ) , std:: io:: Error > {
144
+ use nix:: errno:: Errno ;
145
+ use nix:: libc:: AT_FDCWD ;
146
+
147
+ const RENAME_NOREPLACE : std:: ffi:: c_uint = 1 ;
148
+
149
+ let res = old_path. with_nix_path ( |old_cstr| {
150
+ new_path. with_nix_path ( |new_cstr| unsafe {
151
+ nix:: libc:: renameat2 (
152
+ AT_FDCWD ,
153
+ old_cstr. as_ptr ( ) ,
154
+ AT_FDCWD ,
155
+ new_cstr. as_ptr ( ) ,
156
+ RENAME_NOREPLACE ,
157
+ )
158
+ } )
159
+ } ) ??;
160
+
161
+ Errno :: result ( res)
162
+ . map ( std:: mem:: drop)
163
+ . map_err ( |e| std:: io:: Error :: from_raw_os_error ( e as i32 ) )
164
+ }
165
+
136
166
/// Local filesystem storage providing an [`ObjectStore`] interface to files on
137
167
/// local disk. Can optionally be created with a directory prefix
138
168
///
@@ -351,22 +381,35 @@ impl ObjectStore for LocalFileSystem {
351
381
std:: mem:: drop ( file) ;
352
382
match std:: fs:: rename ( & staging_path, & path) {
353
383
Ok ( _) => None ,
354
- Err ( source) => Some ( Error :: UnableToRenameFile { source } ) ,
384
+ Err ( source) => {
385
+ let _ = std:: fs:: remove_file ( & staging_path) ;
386
+ Some ( Error :: UnableToRenameFile { source } )
387
+ }
355
388
}
356
389
}
357
- PutMode :: Create => match std:: fs:: hard_link ( & staging_path, & path) {
358
- Ok ( _) => {
359
- let _ = std:: fs:: remove_file ( & staging_path) ; // Attempt to cleanup
360
- None
390
+ PutMode :: Create => {
391
+ #[ cfg( not( target_os = "android" ) ) ]
392
+ let create_result = std:: fs:: hard_link ( & staging_path, & path) ;
393
+
394
+ #[ cfg( target_os = "android" ) ]
395
+ let create_result = rename_noreplace (
396
+ & staging_path,
397
+ & path,
398
+ ) ;
399
+
400
+ let _ = std:: fs:: remove_file ( & staging_path) ; // Attempt to cleanup
401
+
402
+ match create_result {
403
+ Ok ( _) => None ,
404
+ Err ( source) => match source. kind ( ) {
405
+ ErrorKind :: AlreadyExists => Some ( Error :: AlreadyExists {
406
+ path : path. to_str ( ) . unwrap ( ) . to_string ( ) ,
407
+ source,
408
+ } ) ,
409
+ _ => Some ( Error :: UnableToRenameFile { source } ) ,
410
+ } ,
361
411
}
362
- Err ( source) => match source. kind ( ) {
363
- ErrorKind :: AlreadyExists => Some ( Error :: AlreadyExists {
364
- path : path. to_str ( ) . unwrap ( ) . to_string ( ) ,
365
- source,
366
- } ) ,
367
- _ => Some ( Error :: UnableToRenameFile { source } ) ,
368
- } ,
369
- } ,
412
+ }
370
413
PutMode :: Update ( _) => unreachable ! ( ) ,
371
414
}
372
415
}
@@ -558,7 +601,14 @@ impl ObjectStore for LocalFileSystem {
558
601
// This is necessary because hard_link returns an error if the destination already exists
559
602
maybe_spawn_blocking ( move || loop {
560
603
let staged = staged_upload_path ( & to, & id. to_string ( ) ) ;
561
- match std:: fs:: hard_link ( & from, & staged) {
604
+
605
+ #[ cfg( not( target_os = "android" ) ) ]
606
+ let stage_result = std:: fs:: hard_link ( & from, & staged) ;
607
+
608
+ #[ cfg( target_os = "android" ) ]
609
+ let stage_result = std:: fs:: copy ( & from, & staged) ;
610
+
611
+ match stage_result {
562
612
Ok ( _) => {
563
613
return std:: fs:: rename ( & staged, & to) . map_err ( |source| {
564
614
let _ = std:: fs:: remove_file ( & staged) ; // Attempt to clean up
@@ -596,6 +646,7 @@ impl ObjectStore for LocalFileSystem {
596
646
. await
597
647
}
598
648
649
+ #[ cfg( not( target_os = "android" ) ) ]
599
650
async fn copy_if_not_exists ( & self , from : & Path , to : & Path ) -> Result < ( ) > {
600
651
let from = self . path_to_filesystem ( from) ?;
601
652
let to = self . path_to_filesystem ( to) ?;
@@ -621,6 +672,51 @@ impl ObjectStore for LocalFileSystem {
621
672
} )
622
673
. await
623
674
}
675
+
676
+ #[ cfg( target_os = "android" ) ]
677
+ async fn copy_if_not_exists ( & self , from : & Path , to : & Path ) -> Result < ( ) , super :: Error > {
678
+ let from = self . path_to_filesystem ( from) ?;
679
+ let to = self . path_to_filesystem ( to) ?;
680
+ let mut id = 0 ;
681
+ // In order to make this atomic we:
682
+ //
683
+ // - stage to a temporary file
684
+ // - atomically rename this temporary file into place only if to does not exist
685
+ //
686
+ // This is necessary because hard_link is EACCESS on Android.
687
+ maybe_spawn_blocking ( move || loop {
688
+ let staged = staged_upload_path ( & to, & id. to_string ( ) ) ;
689
+
690
+ match std:: fs:: copy ( & from, & staged) {
691
+ Ok ( _) => {
692
+ let rename_result = rename_noreplace (
693
+ & staged,
694
+ & to,
695
+ ) ;
696
+ let _ = std:: fs:: remove_file ( & staged) ; // Attempt to clean up
697
+ return rename_result. map_err ( |source| {
698
+ if source. kind ( ) == ErrorKind :: NotFound {
699
+ Error :: AlreadyExists {
700
+ path : to. to_str ( ) . unwrap ( ) . to_string ( ) ,
701
+ source,
702
+ }
703
+ } else {
704
+ Error :: UnableToCopyFile { from, to, source }
705
+ } . into ( )
706
+ } ) ;
707
+ }
708
+ Err ( source) => match source. kind ( ) {
709
+ ErrorKind :: AlreadyExists => id += 1 ,
710
+ ErrorKind :: NotFound => match from. exists ( ) {
711
+ true => create_parent_dirs ( & to, source) ?,
712
+ false => return Err ( Error :: NotFound { path : from, source } . into ( ) ) ,
713
+ } ,
714
+ _ => return Err ( Error :: UnableToCopyFile { from, to, source } . into ( ) ) ,
715
+ } ,
716
+ }
717
+ } )
718
+ . await
719
+ }
624
720
}
625
721
626
722
impl LocalFileSystem {
0 commit comments