@@ -152,6 +152,9 @@ use crate::{
152
152
HashAndFormat ,
153
153
} ;
154
154
155
+ /// Maximum number of external paths we track per blob.
156
+ const MAX_EXTERNAL_PATHS : usize = 8 ;
157
+
155
158
/// Create a 16 byte unique ID.
156
159
fn new_uuid ( ) -> [ u8 ; 16 ] {
157
160
use rand:: RngCore ;
@@ -1239,20 +1242,20 @@ async fn export_path_impl(
1239
1242
}
1240
1243
} ;
1241
1244
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 ! [ ] ) ,
1244
1247
DataLocation :: Owned ( size) => (
1245
1248
MemOrFile :: File ( ( ctx. options ( ) . path . data_path ( & cmd. hash ) , size) ) ,
1246
- false ,
1249
+ vec ! [ ] ,
1247
1250
) ,
1248
1251
DataLocation :: External ( paths, size) => (
1249
1252
MemOrFile :: File ( (
1250
- paths. into_iter ( ) . next ( ) . ok_or_else ( || {
1253
+ paths. iter ( ) . cloned ( ) . next ( ) . ok_or_else ( || {
1251
1254
io:: Error :: new ( io:: ErrorKind :: NotFound , "no external data path" )
1252
1255
} ) ?,
1253
1256
size,
1254
1257
) ) ,
1255
- true ,
1258
+ paths ,
1256
1259
) ,
1257
1260
} ;
1258
1261
let size = match & data {
@@ -1277,34 +1280,43 @@ async fn export_path_impl(
1277
1280
) ;
1278
1281
}
1279
1282
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.
1281
1286
let res =
1282
1287
reflink_or_copy_with_progress ( & source_path, & target, size, tx) . await ?;
1283
1288
trace ! (
1284
1289
"exported {} also to {}, {res:?}" ,
1285
1290
source_path. display( ) ,
1286
1291
target. display( )
1287
1292
) ;
1293
+ external. push ( target) ;
1294
+ external. sort ( ) ;
1295
+ external. dedup ( ) ;
1296
+ external. truncate ( MAX_EXTERNAL_PATHS ) ;
1288
1297
} 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.
1289
1300
match std:: fs:: rename ( & source_path, & target) {
1290
1301
Ok ( ( ) ) => { }
1291
1302
Err ( cause) => {
1292
1303
const ERR_CROSS : i32 = 18 ;
1293
1304
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
1297
1308
} else {
1298
1309
return Err ( cause. into ( ) ) ;
1299
1310
}
1300
1311
}
1301
1312
}
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 ?;
1308
1320
}
1309
1321
} ,
1310
1322
}
0 commit comments