]> git.proxmox.com Git - cargo.git/commitdiff
Fix permission issue with `cargo vendor`.
authorEric Huss <eric@huss.org>
Thu, 4 Feb 2021 00:55:05 +0000 (16:55 -0800)
committerEric Huss <eric@huss.org>
Thu, 4 Feb 2021 01:37:00 +0000 (17:37 -0800)
crates/cargo-test-support/src/registry.rs
src/cargo/ops/vendor.rs
tests/testsuite/vendor.rs

index dd52be0703d33dc59f3c20c8663b7812b7e50e37..4a5ff4955dff03006851c5211b54bb6395058f2a 100644 (file)
@@ -317,8 +317,7 @@ pub struct Package {
     name: String,
     vers: String,
     deps: Vec<Dependency>,
-    files: Vec<(String, String)>,
-    extra_files: Vec<(String, String)>,
+    files: Vec<PackageFile>,
     yanked: bool,
     features: HashMap<String, Vec<String>>,
     local: bool,
@@ -342,6 +341,20 @@ pub struct Dependency {
     optional: bool,
 }
 
+/// A file to be created in a package.
+struct PackageFile {
+    path: String,
+    contents: String,
+    /// The Unix mode for the file. Note that when extracted on Windows, this
+    /// is mostly ignored since it doesn't have the same style of permissions.
+    mode: u32,
+    /// If `true`, the file is created in the root of the tarfile, used for
+    /// testing invalid packages.
+    extra: bool,
+}
+
+const DEFAULT_MODE: u32 = 0o644;
+
 /// Initializes the on-disk registry and sets up the config so that crates.io
 /// is replaced with the one on disk.
 pub fn init() {
@@ -379,7 +392,6 @@ impl Package {
             vers: vers.to_string(),
             deps: Vec::new(),
             files: Vec::new(),
-            extra_files: Vec::new(),
             yanked: false,
             features: HashMap::new(),
             local: false,
@@ -416,7 +428,17 @@ impl Package {
 
     /// Adds a file to the package.
     pub fn file(&mut self, name: &str, contents: &str) -> &mut Package {
-        self.files.push((name.to_string(), contents.to_string()));
+        self.file_with_mode(name, DEFAULT_MODE, contents)
+    }
+
+    /// Adds a file with a specific Unix mode.
+    pub fn file_with_mode(&mut self, path: &str, mode: u32, contents: &str) -> &mut Package {
+        self.files.push(PackageFile {
+            path: path.to_string(),
+            contents: contents.to_string(),
+            mode,
+            extra: false,
+        });
         self
     }
 
@@ -425,9 +447,13 @@ impl Package {
     /// Normal files are automatically placed within a directory named
     /// `$PACKAGE-$VERSION`. This allows you to override that behavior,
     /// typically for testing invalid behavior.
-    pub fn extra_file(&mut self, name: &str, contents: &str) -> &mut Package {
-        self.extra_files
-            .push((name.to_string(), contents.to_string()));
+    pub fn extra_file(&mut self, path: &str, contents: &str) -> &mut Package {
+        self.files.push(PackageFile {
+            path: path.to_string(),
+            contents: contents.to_string(),
+            mode: DEFAULT_MODE,
+            extra: true,
+        });
         self
     }
 
@@ -639,19 +665,30 @@ impl Package {
         let f = t!(File::create(&dst));
         let mut a = Builder::new(GzEncoder::new(f, Compression::default()));
 
-        if !self.files.iter().any(|(name, _)| name == "Cargo.toml") {
+        if !self
+            .files
+            .iter()
+            .any(|PackageFile { path, .. }| path == "Cargo.toml")
+        {
             self.append_manifest(&mut a);
         }
         if self.files.is_empty() {
-            self.append(&mut a, "src/lib.rs", "");
+            self.append(&mut a, "src/lib.rs", DEFAULT_MODE, "");
         } else {
-            for &(ref name, ref contents) in self.files.iter() {
-                self.append(&mut a, name, contents);
+            for PackageFile {
+                path,
+                contents,
+                mode,
+                extra,
+            } in &self.files
+            {
+                if *extra {
+                    self.append_raw(&mut a, path, *mode, contents);
+                } else {
+                    self.append(&mut a, path, *mode, contents);
+                }
             }
         }
-        for &(ref name, ref contents) in self.extra_files.iter() {
-            self.append_extra(&mut a, name, contents);
-        }
     }
 
     fn append_manifest<W: Write>(&self, ar: &mut Builder<W>) {
@@ -704,21 +741,23 @@ impl Package {
             manifest.push_str("[lib]\nproc-macro = true\n");
         }
 
-        self.append(ar, "Cargo.toml", &manifest);
+        self.append(ar, "Cargo.toml", DEFAULT_MODE, &manifest);
     }
 
-    fn append<W: Write>(&self, ar: &mut Builder<W>, file: &str, contents: &str) {
-        self.append_extra(
+    fn append<W: Write>(&self, ar: &mut Builder<W>, file: &str, mode: u32, contents: &str) {
+        self.append_raw(
             ar,
             &format!("{}-{}/{}", self.name, self.vers, file),
+            mode,
             contents,
         );
     }
 
-    fn append_extra<W: Write>(&self, ar: &mut Builder<W>, path: &str, contents: &str) {
+    fn append_raw<W: Write>(&self, ar: &mut Builder<W>, path: &str, mode: u32, contents: &str) {
         let mut header = Header::new_ustar();
         header.set_size(contents.len() as u64);
         t!(header.set_path(path));
+        header.set_mode(mode);
         header.set_cksum();
         t!(ar.append(&header, contents.as_bytes()));
     }
index afaf6ff52a1be42265852a0a681185af35150d71..f7c0acfc36fa0453b579135dc31dd536e962c7b1 100644 (file)
@@ -8,8 +8,7 @@ use anyhow::bail;
 use serde::Serialize;
 use std::collections::HashSet;
 use std::collections::{BTreeMap, BTreeSet, HashMap};
-use std::fs;
-use std::fs::File;
+use std::fs::{self, File, OpenOptions};
 use std::io::{Read, Write};
 use std::path::{Path, PathBuf};
 
@@ -346,8 +345,21 @@ fn cp_sources(
 
 fn copy_and_checksum(src_path: &Path, dst_path: &Path, buf: &mut [u8]) -> CargoResult<String> {
     let mut src = File::open(src_path).chain_err(|| format!("failed to open {:?}", src_path))?;
-    let mut dst =
-        File::create(dst_path).chain_err(|| format!("failed to create {:?}", dst_path))?;
+    let mut dst_opts = OpenOptions::new();
+    dst_opts.write(true).create(true).truncate(true);
+    #[cfg(unix)]
+    {
+        use std::os::unix::fs::{MetadataExt, OpenOptionsExt};
+        let src_metadata = src
+            .metadata()
+            .chain_err(|| format!("failed to stat {:?}", src_path))?;
+        dst_opts.mode(src_metadata.mode());
+    }
+    let mut dst = dst_opts
+        .open(dst_path)
+        .chain_err(|| format!("failed to create {:?}", dst_path))?;
+    // Not going to bother setting mode on pre-existing files, since there
+    // shouldn't be any under normal conditions.
     let mut cksum = Sha256::new();
     loop {
         let n = src
index e55fe414267e3565b526b09eda5b063ecd15d909..63872392e5fc4cd30b14e81c9aba53311eaa2061 100644 (file)
@@ -676,3 +676,36 @@ fn git_crlf_preservation() {
     let output = p.read_file("vendor/a/src/lib.rs");
     assert_eq!(input, output);
 }
+
+#[cargo_test]
+#[cfg(unix)]
+fn vendor_preserves_permissions() {
+    use std::os::unix::fs::MetadataExt;
+
+    Package::new("bar", "1.0.0")
+        .file_with_mode("example.sh", 0o755, "#!/bin/sh")
+        .file("src/lib.rs", "")
+        .publish();
+
+    let p = project()
+        .file(
+            "Cargo.toml",
+            r#"
+                [package]
+                name = "foo"
+                version = "0.1.0"
+
+                [dependencies]
+                bar = "1.0"
+            "#,
+        )
+        .file("src/lib.rs", "")
+        .build();
+
+    p.cargo("vendor --respect-source-config").run();
+
+    let metadata = fs::metadata(p.root().join("vendor/bar/src/lib.rs")).unwrap();
+    assert_eq!(metadata.mode() & 0o777, 0o644);
+    let metadata = fs::metadata(p.root().join("vendor/bar/example.sh")).unwrap();
+    assert_eq!(metadata.mode() & 0o777, 0o755);
+}