@@ -351,22 +351,38 @@ impl ObjectStore for LocalFileSystem {
351351 std:: mem:: drop ( file) ;
352352 match std:: fs:: rename ( & staging_path, & path) {
353353 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+ } ,
355358 }
356359 }
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+ } ,
361384 }
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+ }
370386 PutMode :: Update ( _) => unreachable ! ( ) ,
371387 }
372388 }
@@ -558,7 +574,14 @@ impl ObjectStore for LocalFileSystem {
558574 // This is necessary because hard_link returns an error if the destination already exists
559575 maybe_spawn_blocking ( move || loop {
560576 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 {
562585 Ok ( _) => {
563586 return std:: fs:: rename ( & staged, & to) . map_err ( |source| {
564587 let _ = std:: fs:: remove_file ( & staged) ; // Attempt to clean up
@@ -596,6 +619,7 @@ impl ObjectStore for LocalFileSystem {
596619 . await
597620 }
598621
622+ #[ cfg( not( target_os = "android" ) ) ]
599623 async fn copy_if_not_exists ( & self , from : & Path , to : & Path ) -> Result < ( ) > {
600624 let from = self . path_to_filesystem ( from) ?;
601625 let to = self . path_to_filesystem ( to) ?;
@@ -621,6 +645,54 @@ impl ObjectStore for LocalFileSystem {
621645 } )
622646 . await
623647 }
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+ }
624696}
625697
626698impl LocalFileSystem {
0 commit comments