Skip to content

Commit 34170fc

Browse files
committed
Auto merge of #9131 - ehuss:fix-vendor-permissions, r=alexcrichton
Fix permission issue with `cargo vendor`. I think there was an unintended regression in #8937 where the vendored output does not retain the original permissions. Fixes #9127.
2 parents aaaf296 + 33f648a commit 34170fc

File tree

3 files changed

+106
-22
lines changed

3 files changed

+106
-22
lines changed

crates/cargo-test-support/src/registry.rs

Lines changed: 57 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -317,8 +317,7 @@ pub struct Package {
317317
name: String,
318318
vers: String,
319319
deps: Vec<Dependency>,
320-
files: Vec<(String, String)>,
321-
extra_files: Vec<(String, String)>,
320+
files: Vec<PackageFile>,
322321
yanked: bool,
323322
features: HashMap<String, Vec<String>>,
324323
local: bool,
@@ -342,6 +341,20 @@ pub struct Dependency {
342341
optional: bool,
343342
}
344343

344+
/// A file to be created in a package.
345+
struct PackageFile {
346+
path: String,
347+
contents: String,
348+
/// The Unix mode for the file. Note that when extracted on Windows, this
349+
/// is mostly ignored since it doesn't have the same style of permissions.
350+
mode: u32,
351+
/// If `true`, the file is created in the root of the tarfile, used for
352+
/// testing invalid packages.
353+
extra: bool,
354+
}
355+
356+
const DEFAULT_MODE: u32 = 0o644;
357+
345358
/// Initializes the on-disk registry and sets up the config so that crates.io
346359
/// is replaced with the one on disk.
347360
pub fn init() {
@@ -379,7 +392,6 @@ impl Package {
379392
vers: vers.to_string(),
380393
deps: Vec::new(),
381394
files: Vec::new(),
382-
extra_files: Vec::new(),
383395
yanked: false,
384396
features: HashMap::new(),
385397
local: false,
@@ -416,7 +428,17 @@ impl Package {
416428

417429
/// Adds a file to the package.
418430
pub fn file(&mut self, name: &str, contents: &str) -> &mut Package {
419-
self.files.push((name.to_string(), contents.to_string()));
431+
self.file_with_mode(name, DEFAULT_MODE, contents)
432+
}
433+
434+
/// Adds a file with a specific Unix mode.
435+
pub fn file_with_mode(&mut self, path: &str, mode: u32, contents: &str) -> &mut Package {
436+
self.files.push(PackageFile {
437+
path: path.to_string(),
438+
contents: contents.to_string(),
439+
mode,
440+
extra: false,
441+
});
420442
self
421443
}
422444

@@ -425,9 +447,13 @@ impl Package {
425447
/// Normal files are automatically placed within a directory named
426448
/// `$PACKAGE-$VERSION`. This allows you to override that behavior,
427449
/// typically for testing invalid behavior.
428-
pub fn extra_file(&mut self, name: &str, contents: &str) -> &mut Package {
429-
self.extra_files
430-
.push((name.to_string(), contents.to_string()));
450+
pub fn extra_file(&mut self, path: &str, contents: &str) -> &mut Package {
451+
self.files.push(PackageFile {
452+
path: path.to_string(),
453+
contents: contents.to_string(),
454+
mode: DEFAULT_MODE,
455+
extra: true,
456+
});
431457
self
432458
}
433459

@@ -639,19 +665,30 @@ impl Package {
639665
let f = t!(File::create(&dst));
640666
let mut a = Builder::new(GzEncoder::new(f, Compression::default()));
641667

642-
if !self.files.iter().any(|(name, _)| name == "Cargo.toml") {
668+
if !self
669+
.files
670+
.iter()
671+
.any(|PackageFile { path, .. }| path == "Cargo.toml")
672+
{
643673
self.append_manifest(&mut a);
644674
}
645675
if self.files.is_empty() {
646-
self.append(&mut a, "src/lib.rs", "");
676+
self.append(&mut a, "src/lib.rs", DEFAULT_MODE, "");
647677
} else {
648-
for &(ref name, ref contents) in self.files.iter() {
649-
self.append(&mut a, name, contents);
678+
for PackageFile {
679+
path,
680+
contents,
681+
mode,
682+
extra,
683+
} in &self.files
684+
{
685+
if *extra {
686+
self.append_raw(&mut a, path, *mode, contents);
687+
} else {
688+
self.append(&mut a, path, *mode, contents);
689+
}
650690
}
651691
}
652-
for &(ref name, ref contents) in self.extra_files.iter() {
653-
self.append_extra(&mut a, name, contents);
654-
}
655692
}
656693

657694
fn append_manifest<W: Write>(&self, ar: &mut Builder<W>) {
@@ -704,21 +741,23 @@ impl Package {
704741
manifest.push_str("[lib]\nproc-macro = true\n");
705742
}
706743

707-
self.append(ar, "Cargo.toml", &manifest);
744+
self.append(ar, "Cargo.toml", DEFAULT_MODE, &manifest);
708745
}
709746

710-
fn append<W: Write>(&self, ar: &mut Builder<W>, file: &str, contents: &str) {
711-
self.append_extra(
747+
fn append<W: Write>(&self, ar: &mut Builder<W>, file: &str, mode: u32, contents: &str) {
748+
self.append_raw(
712749
ar,
713750
&format!("{}-{}/{}", self.name, self.vers, file),
751+
mode,
714752
contents,
715753
);
716754
}
717755

718-
fn append_extra<W: Write>(&self, ar: &mut Builder<W>, path: &str, contents: &str) {
756+
fn append_raw<W: Write>(&self, ar: &mut Builder<W>, path: &str, mode: u32, contents: &str) {
719757
let mut header = Header::new_ustar();
720758
header.set_size(contents.len() as u64);
721759
t!(header.set_path(path));
760+
header.set_mode(mode);
722761
header.set_cksum();
723762
t!(ar.append(&header, contents.as_bytes()));
724763
}

src/cargo/ops/vendor.rs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ use anyhow::bail;
88
use serde::Serialize;
99
use std::collections::HashSet;
1010
use std::collections::{BTreeMap, BTreeSet, HashMap};
11-
use std::fs;
12-
use std::fs::File;
11+
use std::fs::{self, File, OpenOptions};
1312
use std::io::{Read, Write};
1413
use std::path::{Path, PathBuf};
1514

@@ -346,8 +345,21 @@ fn cp_sources(
346345

347346
fn copy_and_checksum(src_path: &Path, dst_path: &Path, buf: &mut [u8]) -> CargoResult<String> {
348347
let mut src = File::open(src_path).chain_err(|| format!("failed to open {:?}", src_path))?;
349-
let mut dst =
350-
File::create(dst_path).chain_err(|| format!("failed to create {:?}", dst_path))?;
348+
let mut dst_opts = OpenOptions::new();
349+
dst_opts.write(true).create(true).truncate(true);
350+
#[cfg(unix)]
351+
{
352+
use std::os::unix::fs::{MetadataExt, OpenOptionsExt};
353+
let src_metadata = src
354+
.metadata()
355+
.chain_err(|| format!("failed to stat {:?}", src_path))?;
356+
dst_opts.mode(src_metadata.mode());
357+
}
358+
let mut dst = dst_opts
359+
.open(dst_path)
360+
.chain_err(|| format!("failed to create {:?}", dst_path))?;
361+
// Not going to bother setting mode on pre-existing files, since there
362+
// shouldn't be any under normal conditions.
351363
let mut cksum = Sha256::new();
352364
loop {
353365
let n = src

tests/testsuite/vendor.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -676,3 +676,36 @@ fn git_crlf_preservation() {
676676
let output = p.read_file("vendor/a/src/lib.rs");
677677
assert_eq!(input, output);
678678
}
679+
680+
#[cargo_test]
681+
#[cfg(unix)]
682+
fn vendor_preserves_permissions() {
683+
use std::os::unix::fs::MetadataExt;
684+
685+
Package::new("bar", "1.0.0")
686+
.file_with_mode("example.sh", 0o755, "#!/bin/sh")
687+
.file("src/lib.rs", "")
688+
.publish();
689+
690+
let p = project()
691+
.file(
692+
"Cargo.toml",
693+
r#"
694+
[package]
695+
name = "foo"
696+
version = "0.1.0"
697+
698+
[dependencies]
699+
bar = "1.0"
700+
"#,
701+
)
702+
.file("src/lib.rs", "")
703+
.build();
704+
705+
p.cargo("vendor --respect-source-config").run();
706+
707+
let metadata = fs::metadata(p.root().join("vendor/bar/src/lib.rs")).unwrap();
708+
assert_eq!(metadata.mode() & 0o777, 0o644);
709+
let metadata = fs::metadata(p.root().join("vendor/bar/example.sh")).unwrap();
710+
assert_eq!(metadata.mode() & 0o777, 0o755);
711+
}

0 commit comments

Comments
 (0)