184
184
//! [`IndexPackage`]: index::IndexPackage
185
185
186
186
use std:: collections:: HashSet ;
187
+ use std:: fs;
187
188
use std:: fs:: { File , OpenOptions } ;
188
189
use std:: io;
189
190
use std:: io:: Read ;
@@ -196,6 +197,7 @@ use cargo_util::paths::{self, exclude_from_backups_and_indexing};
196
197
use flate2:: read:: GzDecoder ;
197
198
use log:: debug;
198
199
use serde:: Deserialize ;
200
+ use serde:: Serialize ;
199
201
use tar:: Archive ;
200
202
201
203
use crate :: core:: dependency:: Dependency ;
@@ -219,6 +221,14 @@ pub const CRATES_IO_HTTP_INDEX: &str = "sparse+https://index.crates.io/";
219
221
pub const CRATES_IO_REGISTRY : & str = "crates-io" ;
220
222
pub const CRATES_IO_DOMAIN : & str = "crates.io" ;
221
223
224
+ /// The content inside `.cargo-ok`.
225
+ /// See [`RegistrySource::unpack_package`] for more.
226
+ #[ derive( Deserialize , Serialize ) ]
227
+ struct LockMetadata {
228
+ /// The version of `.cargo-ok` file
229
+ v : u32 ,
230
+ }
231
+
222
232
/// A [`Source`] implementation for a local or a remote registry.
223
233
///
224
234
/// This contains common functionality that is shared between each registry
@@ -546,6 +556,11 @@ impl<'cfg> RegistrySource<'cfg> {
546
556
/// `.crate` files making `.cargo-ok` a symlink causing cargo to write "ok"
547
557
/// to any arbitrary file on the filesystem it has permission to.
548
558
///
559
+ /// In 1.71, `.cargo-ok` changed to contain a JSON `{ v: 1 }` to indicate
560
+ /// the version of it. A failure of parsing will result in a heavy-hammer
561
+ /// approach that unpacks the `.crate` file again. This is in response to a
562
+ /// security issue that the unpacking didn't respect umask on Unix systems.
563
+ ///
549
564
/// This is all a long-winded way of explaining the circumstances that might
550
565
/// cause a directory to contain a `.cargo-ok` file that is empty or
551
566
/// otherwise corrupted. Either this was extracted by a version of Rust
@@ -567,15 +582,23 @@ impl<'cfg> RegistrySource<'cfg> {
567
582
let path = dst. join ( PACKAGE_SOURCE_LOCK ) ;
568
583
let path = self . config . assert_package_cache_locked ( & path) ;
569
584
let unpack_dir = path. parent ( ) . unwrap ( ) ;
570
- match path. metadata ( ) {
571
- Ok ( meta) if meta. len ( ) > 0 => return Ok ( unpack_dir. to_path_buf ( ) ) ,
572
- Ok ( _meta) => {
573
- // See comment of `unpack_package` about why removing all stuff.
574
- log:: warn!( "unexpected length of {path:?}, clearing cache" ) ;
575
- paths:: remove_dir_all ( dst. as_path_unlocked ( ) ) ?;
576
- }
585
+ match fs:: read_to_string ( path) {
586
+ Ok ( ok) => match serde_json:: from_str :: < LockMetadata > ( & ok) {
587
+ Ok ( lock_meta) if lock_meta. v == 1 => {
588
+ return Ok ( unpack_dir. to_path_buf ( ) ) ;
589
+ }
590
+ _ => {
591
+ if ok == "ok" {
592
+ log:: debug!( "old `ok` content found, clearing cache" ) ;
593
+ } else {
594
+ log:: warn!( "unrecognized .cargo-ok content, clearing cache: {ok}" ) ;
595
+ }
596
+ // See comment of `unpack_package` about why removing all stuff.
597
+ paths:: remove_dir_all ( dst. as_path_unlocked ( ) ) ?;
598
+ }
599
+ } ,
577
600
Err ( e) if e. kind ( ) == io:: ErrorKind :: NotFound => { }
578
- Err ( e) => anyhow:: bail!( "failed to access package completion {path:?}: {e}" ) ,
601
+ Err ( e) => anyhow:: bail!( "unable to read .cargo-ok file at {path:?}: {e}" ) ,
579
602
}
580
603
dst. create_dir ( ) ?;
581
604
let mut tar = {
@@ -639,7 +662,9 @@ impl<'cfg> RegistrySource<'cfg> {
639
662
. write ( true )
640
663
. open ( & path)
641
664
. with_context ( || format ! ( "failed to open `{}`" , path. display( ) ) ) ?;
642
- write ! ( ok, "ok" ) ?;
665
+
666
+ let lock_meta = LockMetadata { v : 1 } ;
667
+ write ! ( ok, "{}" , serde_json:: to_string( & lock_meta) . unwrap( ) ) ?;
643
668
644
669
Ok ( unpack_dir. to_path_buf ( ) )
645
670
}
0 commit comments