@@ -152,6 +152,9 @@ use crate::{
152152 HashAndFormat ,
153153} ;
154154
155+ /// Maximum number of external paths we track per blob.
156+ const MAX_EXTERNAL_PATHS : usize = 8 ;
157+
155158/// Create a 16 byte unique ID.
156159fn new_uuid ( ) -> [ u8 ; 16 ] {
157160 use rand:: RngCore ;
@@ -1239,20 +1242,20 @@ async fn export_path_impl(
12391242 }
12401243 } ;
12411244 trace ! ( "exporting {} to {}" , cmd. hash. to_hex( ) , target. display( ) ) ;
1242- let ( data, external) = match data_location {
1243- DataLocation :: Inline ( data) => ( MemOrFile :: Mem ( data) , false ) ,
1245+ let ( data, mut external) = match data_location {
1246+ DataLocation :: Inline ( data) => ( MemOrFile :: Mem ( data) , vec ! [ ] ) ,
12441247 DataLocation :: Owned ( size) => (
12451248 MemOrFile :: File ( ( ctx. options ( ) . path . data_path ( & cmd. hash ) , size) ) ,
1246- false ,
1249+ vec ! [ ] ,
12471250 ) ,
12481251 DataLocation :: External ( paths, size) => (
12491252 MemOrFile :: File ( (
1250- paths. into_iter ( ) . next ( ) . ok_or_else ( || {
1253+ paths. iter ( ) . cloned ( ) . next ( ) . ok_or_else ( || {
12511254 io:: Error :: new ( io:: ErrorKind :: NotFound , "no external data path" )
12521255 } ) ?,
12531256 size,
12541257 ) ) ,
1255- true ,
1258+ paths ,
12561259 ) ,
12571260 } ;
12581261 let size = match & data {
@@ -1277,34 +1280,43 @@ async fn export_path_impl(
12771280 ) ;
12781281 }
12791282 ExportMode :: TryReference => {
1280- if external {
1283+ if !external. is_empty ( ) {
1284+ // the file already exists externally, so we need to copy it.
1285+ // if the OS supports reflink, we might as well use that.
12811286 let res =
12821287 reflink_or_copy_with_progress ( & source_path, & target, size, tx) . await ?;
12831288 trace ! (
12841289 "exported {} also to {}, {res:?}" ,
12851290 source_path. display( ) ,
12861291 target. display( )
12871292 ) ;
1293+ external. push ( target) ;
1294+ external. sort ( ) ;
1295+ external. dedup ( ) ;
1296+ external. truncate ( MAX_EXTERNAL_PATHS ) ;
12881297 } else {
1298+ // the file was previously owned, so we can just move it.
1299+ // if that fails with ERR_CROSS, we fall back to copy.
12891300 match std:: fs:: rename ( & source_path, & target) {
12901301 Ok ( ( ) ) => { }
12911302 Err ( cause) => {
12921303 const ERR_CROSS : i32 = 18 ;
12931304 if cause. raw_os_error ( ) == Some ( ERR_CROSS ) {
1294- let source = fs :: File :: open ( & source_path) ? ;
1295- let mut target = fs :: File :: create ( & target ) ?;
1296- copy_with_progress ( & source , size , & mut target , tx ) . await ? ;
1305+ reflink_or_copy_with_progress ( & source_path, & target , size , tx )
1306+ . await ?;
1307+ // todo: delete file at source_path
12971308 } else {
12981309 return Err ( cause. into ( ) ) ;
12991310 }
13001311 }
13011312 }
1302- ctx. set ( EntryState :: Complete {
1303- data_location : DataLocation :: External ( vec ! [ target] , size) ,
1304- outboard_location,
1305- } )
1306- . await ?;
1307- }
1313+ external. push ( target) ;
1314+ } ;
1315+ ctx. set ( EntryState :: Complete {
1316+ data_location : DataLocation :: External ( external, size) ,
1317+ outboard_location,
1318+ } )
1319+ . await ?;
13081320 }
13091321 } ,
13101322 }
0 commit comments