160
160
161
161
use std:: borrow:: Cow ;
162
162
use std:: collections:: BTreeMap ;
163
- use std:: fs :: File ;
163
+ use std:: io :: Write ;
164
164
use std:: path:: { Path , PathBuf } ;
165
165
166
166
use flate2:: read:: GzDecoder ;
@@ -179,6 +179,7 @@ use crate::util::to_url::ToUrl;
179
179
use crate :: util:: { internal, CargoResult , Config , FileLock , Filesystem } ;
180
180
181
181
const INDEX_LOCK : & str = ".cargo-index-lock" ;
182
+ const PACKAGE_SOURCE_LOCK : & str = ".cargo-ok" ;
182
183
pub const CRATES_IO_INDEX : & str = "https://github.com/rust-lang/crates.io-index" ;
183
184
pub const CRATES_IO_REGISTRY : & str = "crates-io" ;
184
185
const CRATE_TEMPLATE : & str = "{crate}" ;
@@ -426,23 +427,40 @@ impl<'cfg> RegistrySource<'cfg> {
426
427
///
427
428
/// No action is taken if the source looks like it's already unpacked.
428
429
fn unpack_package ( & self , pkg : PackageId , tarball : & FileLock ) -> CargoResult < PathBuf > {
429
- let dst = self
430
- . src_path
431
- . join ( & format ! ( "{}-{}" , pkg. name( ) , pkg. version( ) ) ) ;
432
- dst. create_dir ( ) ?;
433
- // Note that we've already got the `tarball` locked above, and that
434
- // implies a lock on the unpacked destination as well, so this access
435
- // via `into_path_unlocked` should be ok.
436
- let dst = dst. into_path_unlocked ( ) ;
437
- let ok = dst. join ( ".cargo-ok" ) ;
438
- if ok. exists ( ) {
439
- return Ok ( dst) ;
430
+ // The `.cargo-ok` file is used to track if the source is already
431
+ // unpacked and to lock the directory for unpacking.
432
+ let mut ok = {
433
+ let package_dir = format ! ( "{}-{}" , pkg. name( ) , pkg. version( ) ) ;
434
+ let dst = self
435
+ . src_path
436
+ . join ( & package_dir) ;
437
+ dst. create_dir ( ) ?;
438
+
439
+ // Attempt to open a read-only copy first to avoid an exclusive write
440
+ // lock and also work with read-only filesystems. If the file has
441
+ // any data, assume the source is already unpacked.
442
+ if let Ok ( ok) = dst. open_ro ( PACKAGE_SOURCE_LOCK , self . config , & package_dir) {
443
+ let meta = ok. file ( ) . metadata ( ) ?;
444
+ if meta. len ( ) > 0 {
445
+ let unpack_dir = ok. parent ( ) . to_path_buf ( ) ;
446
+ return Ok ( unpack_dir) ;
447
+ }
448
+ }
449
+
450
+ dst. open_rw ( PACKAGE_SOURCE_LOCK , self . config , & package_dir) ?
451
+ } ;
452
+ let unpack_dir = ok. parent ( ) . to_path_buf ( ) ;
453
+
454
+ // If the file has any data, assume the source is already unpacked.
455
+ let meta = ok. file ( ) . metadata ( ) ?;
456
+ if meta. len ( ) > 0 {
457
+ return Ok ( unpack_dir) ;
440
458
}
441
459
442
460
let gz = GzDecoder :: new ( tarball. file ( ) ) ;
443
461
let mut tar = Archive :: new ( gz) ;
444
- let prefix = dst . file_name ( ) . unwrap ( ) ;
445
- let parent = dst . parent ( ) . unwrap ( ) ;
462
+ let prefix = unpack_dir . file_name ( ) . unwrap ( ) ;
463
+ let parent = unpack_dir . parent ( ) . unwrap ( ) ;
446
464
for entry in tar. entries ( ) ? {
447
465
let mut entry = entry. chain_err ( || "failed to iterate over archive" ) ?;
448
466
let entry_path = entry
@@ -470,8 +488,11 @@ impl<'cfg> RegistrySource<'cfg> {
470
488
. unpack_in ( parent)
471
489
. chain_err ( || format ! ( "failed to unpack entry at `{}`" , entry_path. display( ) ) ) ?;
472
490
}
473
- File :: create ( & ok) ?;
474
- Ok ( dst)
491
+
492
+ // Write to the lock file to indicate that unpacking was successful.
493
+ write ! ( ok, "ok" ) ?;
494
+
495
+ Ok ( unpack_dir)
475
496
}
476
497
477
498
fn do_update ( & mut self ) -> CargoResult < ( ) > {
0 commit comments