@@ -625,9 +625,47 @@ impl<'cfg> RegistrySource<'cfg> {
625
625
match path. metadata ( ) {
626
626
Ok ( meta) if meta. len ( ) > 0 => return Ok ( unpack_dir. to_path_buf ( ) ) ,
627
627
Ok ( _meta) => {
628
- // The file appears to be corrupted. Perhaps it failed to flush,
629
- // or the filesystem was corrupted in some way. To be safe, let's
630
- // assume something is wrong and clear it and start over.
628
+ // The `.cargo-ok` file is not in a state we expect it to be
629
+ // (with two bytes containing "ok").
630
+ //
631
+ // Cargo has always included a `.cargo-ok` file to detect if
632
+ // extraction was interrupted, but it was originally empty.
633
+ //
634
+ // In 1.34, Cargo was changed to create the `.cargo-ok` file
635
+ // before it started extraction to implement fine-grained
636
+ // locking. After it was finished extracting, it wrote two
637
+ // bytes to indicate it was complete. It would use the length
638
+ // check to detect if it was possibly interrupted.
639
+ //
640
+ // In 1.36, Cargo changed to not use fine-grained locking, and
641
+ // instead used a global lock. The use of `.cargo-ok` was no
642
+ // longer needed for locking purposes, but was kept to detect
643
+ // when extraction was interrupted.
644
+ //
645
+ // In 1.49, Cargo changed to not create the `.cargo-ok` file
646
+ // before it started extraction to deal with `.crate` files
647
+ // that inexplicably had a `.cargo-ok` file in them.
648
+ //
649
+ // In 1.64, Cargo changed to detect `.crate` files with
650
+ // `.cargo-ok` files in them in response to CVE-2022-36113,
651
+ // which dealt with malicious `.crate` files making
652
+ // `.cargo-ok` a symlink causing cargo to write "ok" to any
653
+ // arbitrary file on the filesystem it has permission to.
654
+ //
655
+ // This is all a long-winded way of explaining the
656
+ // circumstances that might cause a directory to contain a
657
+ // `.cargo-ok` file that is empty or otherwise corrupted.
658
+ // Either this was extracted by a version of Rust before 1.34,
659
+ // in which case everything should be fine. However, an empty
660
+ // file created by versions 1.36 to 1.49 indicates that the
661
+ // extraction was interrupted and that we need to start again.
662
+ //
663
+ // Another possibility is that the filesystem is simply
664
+ // corrupted, in which case deleting the directory might be
665
+ // the safe thing to do. That is probably unlikely, though.
666
+ //
667
+ // To be safe, this deletes the directory and starts over
668
+ // again.
631
669
log:: warn!( "unexpected length of {path:?}, clearing cache" ) ;
632
670
paths:: remove_dir_all ( dst. as_path_unlocked ( ) ) ?;
633
671
}
0 commit comments