@@ -351,22 +351,38 @@ impl ObjectStore for LocalFileSystem {
351
351
std:: mem:: drop ( file) ;
352
352
match std:: fs:: rename ( & staging_path, & path) {
353
353
Ok ( _) => None ,
354
- Err ( source) => Some ( Error :: UnableToRenameFile { source } ) ,
354
+ Err ( source) => {
355
+ let _ = std:: fs:: remove_file ( & staging_path) ;
356
+ Some ( Error :: UnableToRenameFile { source } )
357
+ } ,
355
358
}
356
359
}
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
360
+ PutMode :: Create => {
361
+ #[ cfg( not( target_os = "android" ) ) ]
362
+ let create_result = std:: fs:: hard_link ( & staging_path, & path) ;
363
+
364
+ #[ cfg( target_os = "android" ) ]
365
+ let create_result = nix:: fcntl:: renameat2 (
366
+ nix:: fcntl:: AtFlags :: AT_FDCWD ,
367
+ & staged,
368
+ nix:: fcntl:: AtFlags :: AT_FDCWD ,
369
+ & path,
370
+ nix:: fcntl:: RenameFlags :: RENAME_NOREPLACE ,
371
+ ) ;
372
+
373
+ let _ = std:: fs:: remove_file ( & staging_path) ; // Attempt to cleanup
374
+
375
+ match create_result {
376
+ Ok ( _) => None ,
377
+ Err ( source) => match source. kind ( ) {
378
+ ErrorKind :: AlreadyExists => Some ( Error :: AlreadyExists {
379
+ path : path. to_str ( ) . unwrap ( ) . to_string ( ) ,
380
+ source,
381
+ } ) ,
382
+ _ => Some ( Error :: UnableToRenameFile { source } ) ,
383
+ } ,
361
384
}
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
- } ,
385
+ }
370
386
PutMode :: Update ( _) => unreachable ! ( ) ,
371
387
}
372
388
}
@@ -558,7 +574,14 @@ impl ObjectStore for LocalFileSystem {
558
574
// This is necessary because hard_link returns an error if the destination already exists
559
575
maybe_spawn_blocking ( move || loop {
560
576
let staged = staged_upload_path ( & to, & id. to_string ( ) ) ;
561
- match std:: fs:: hard_link ( & from, & staged) {
577
+
578
+ #[ cfg( not( target_os = "android" ) ) ]
579
+ let stage_result = std:: fs:: hard_link ( & from, & staged) ;
580
+
581
+ #[ cfg( target_os = "android" ) ]
582
+ let stage_result = std:: fs:: copy ( & from, & staged) ;
583
+
584
+ match stage_result {
562
585
Ok ( _) => {
563
586
return std:: fs:: rename ( & staged, & to) . map_err ( |source| {
564
587
let _ = std:: fs:: remove_file ( & staged) ; // Attempt to clean up
@@ -596,6 +619,7 @@ impl ObjectStore for LocalFileSystem {
596
619
. await
597
620
}
598
621
622
+ #[ cfg( not( target_os = "android" ) ) ]
599
623
async fn copy_if_not_exists ( & self , from : & Path , to : & Path ) -> Result < ( ) > {
600
624
let from = self . path_to_filesystem ( from) ?;
601
625
let to = self . path_to_filesystem ( to) ?;
@@ -621,6 +645,54 @@ impl ObjectStore for LocalFileSystem {
621
645
} )
622
646
. await
623
647
}
648
+
649
+ #[ cfg( target_os = "android" ) ]
650
+ async fn copy_if_not_exists ( & self , from : & Path , to : & Path ) -> Result < ( ) > {
651
+ let from = self . path_to_filesystem ( from) ?;
652
+ let to = self . path_to_filesystem ( to) ?;
653
+ let mut id = 0 ;
654
+ // In order to make this atomic we:
655
+ //
656
+ // - stage to a temporary file
657
+ // - atomically rename this temporary file into place only if to does not exist
658
+ //
659
+ // This is necessary because hard_link is EACCESS on Android.
660
+ maybe_spawn_blocking ( move || loop {
661
+ let staged = staged_upload_path ( & to, & id. to_string ( ) ) ;
662
+
663
+ match std:: fs:: copy ( & from, & staged) {
664
+ Ok ( _) => {
665
+ let rename_result = nix:: fcntl:: renameat2 (
666
+ nix:: fcntl:: AtFlags :: AT_FDCWD ,
667
+ & staged,
668
+ nix:: fcntl:: AtFlags :: AT_FDCWD ,
669
+ & to,
670
+ nix:: fcntl:: RenameFlags :: RENAME_NOREPLACE ,
671
+ ) ;
672
+ let _ = std:: fs:: remove_file ( & staged) ; // Attempt to clean up
673
+ return rename_result. map_err ( |source| {
674
+ if source. kind ( ) == ErrorKind :: NotFound {
675
+ Error :: AlreadyExists {
676
+ path : to. to_str ( ) . unwrap ( ) . to_string ( ) ,
677
+ source,
678
+ }
679
+ } else {
680
+ Error :: UnableToCopyFile { from, to, source }
681
+ }
682
+ } ) ;
683
+ }
684
+ Err ( source) => match source. kind ( ) {
685
+ ErrorKind :: AlreadyExists => id += 1 ,
686
+ ErrorKind :: NotFound => match from. exists ( ) {
687
+ true => create_parent_dirs ( & to, source) ?,
688
+ false => return Err ( Error :: NotFound { path : from, source } . into ( ) ) ,
689
+ } ,
690
+ _ => return Err ( Error :: UnableToCopyFile { from, to, source } . into ( ) ) ,
691
+ } ,
692
+ }
693
+ } )
694
+ . await
695
+ }
624
696
}
625
697
626
698
impl LocalFileSystem {
0 commit comments