diff --git a/src/rustup-dist/src/component/package.rs b/src/rustup-dist/src/component/package.rs index 3326231ab0..8aa63db97a 100644 --- a/src/rustup-dist/src/component/package.rs +++ b/src/rustup-dist/src/component/package.rs @@ -123,10 +123,16 @@ impl Package for DirectoryPackage { // but due to rust-lang/rust#25479 they don't. #[cfg(unix)] fn set_file_perms(dest_path: &Path, src_path: &Path) -> Result<()> { - use std::fs; + use std::fs::{self, Metadata}; use std::os::unix::fs::PermissionsExt; use walkdir::WalkDir; + // Compute whether this entry needs the X bit + fn needs_x(meta: &Metadata) -> bool { + meta.is_dir() || // Directories need it + meta.permissions().mode() & 0o700 == 0o700 // If it is rwx for the user, it gets the X bit + } + // By convention, anything in the bin/ directory of the package is a binary let is_bin = if let Some(p) = src_path.parent() { p.ends_with("bin") @@ -141,25 +147,14 @@ fn set_file_perms(dest_path: &Path, src_path: &Path) -> Result<()> { for entry in WalkDir::new(dest_path) { let entry = try!(entry.chain_err(|| ErrorKind::ComponentDirPermissionsFailed)); let meta = try!(entry.metadata().chain_err(|| ErrorKind::ComponentDirPermissionsFailed)); - if meta.is_dir() { - let mut perm = meta.permissions(); - perm.set_mode(0o755); - try!(fs::set_permissions(entry.path(), perm).chain_err(|| ErrorKind::ComponentFilePermissionsFailed)); - } else { - let mut perm = meta.permissions(); - perm.set_mode(0o644); - try!(fs::set_permissions(entry.path(), perm).chain_err(|| ErrorKind::ComponentFilePermissionsFailed)); - } + let mut perm = meta.permissions(); + perm.set_mode(if needs_x(&meta) { 0o755 } else { 0o644 }); + try!(fs::set_permissions(entry.path(), perm).chain_err(|| ErrorKind::ComponentFilePermissionsFailed)); } - } else if is_bin { - let mut perm = try!(fs::metadata(dest_path).chain_err(|| ErrorKind::ComponentFilePermissionsFailed)) - .permissions(); - perm.set_mode(0o755); - try!(fs::set_permissions(dest_path, perm).chain_err(|| ErrorKind::ComponentFilePermissionsFailed)); } else { - let mut perm = try!(fs::metadata(dest_path).chain_err(|| ErrorKind::ComponentFilePermissionsFailed)) - .permissions(); - perm.set_mode(0o644); + let meta = try!(fs::metadata(dest_path).chain_err(|| ErrorKind::ComponentFilePermissionsFailed)); + let mut perm = meta.permissions(); + perm.set_mode(if is_bin || needs_x(&meta) { 0o755 } else { 0o644 }); try!(fs::set_permissions(dest_path, perm).chain_err(|| ErrorKind::ComponentFilePermissionsFailed)); } diff --git a/src/rustup-dist/tests/dist.rs b/src/rustup-dist/tests/dist.rs index 2e42432703..7f5ac9295e 100644 --- a/src/rustup-dist/tests/dist.rs +++ b/src/rustup-dist/tests/dist.rs @@ -118,7 +118,7 @@ pub fn create_mock_channel(channel: &str, date: &str, MockCommand::File("bin/rustc".to_string()), ], vec![ - ("bin/rustc".to_string(), contents.clone()) + ("bin/rustc".to_string(), contents.clone(), false) ], ), ] @@ -152,7 +152,7 @@ pub fn create_mock_channel(channel: &str, date: &str, MockCommand::File("lib/libstd.rlib".to_string()), ], vec![ - ("lib/libstd.rlib".to_string(), contents.clone()) + ("lib/libstd.rlib".to_string(), contents.clone(), false) ], ), ] @@ -170,7 +170,7 @@ pub fn create_mock_channel(channel: &str, date: &str, MockCommand::File("lib/i686-apple-darwin/libstd.rlib".to_string()), ], vec![ - ("lib/i686-apple-darwin/libstd.rlib".to_string(), contents.clone()) + ("lib/i686-apple-darwin/libstd.rlib".to_string(), contents.clone(), false) ], ), ] @@ -188,7 +188,7 @@ pub fn create_mock_channel(channel: &str, date: &str, MockCommand::File("lib/i686-unknown-linux-gnu/libstd.rlib".to_string()), ], vec![ - ("lib/i686-unknown-linux-gnu/libstd.rlib".to_string(), contents.clone()) + ("lib/i686-unknown-linux-gnu/libstd.rlib".to_string(), contents.clone(), false) ], ), ] @@ -215,7 +215,7 @@ pub fn create_mock_channel(channel: &str, date: &str, MockCommand::File("bin/bonus".to_string()), ], vec![ - ("bin/bonus".to_string(), contents.clone()) + ("bin/bonus".to_string(), contents.clone(), false) ], ), ] diff --git a/src/rustup-dist/tests/install.rs b/src/rustup-dist/tests/install.rs index b7758de456..b1bb28ab03 100644 --- a/src/rustup-dist/tests/install.rs +++ b/src/rustup-dist/tests/install.rs @@ -27,13 +27,13 @@ fn mock_smoke_test() { vec![MockCommand::File("bin/foo".to_string()), MockCommand::File("lib/bar".to_string()), MockCommand::Dir("doc/stuff".to_string())], - vec![("bin/foo".to_string(), "foo".into()), - ("lib/bar".to_string(), "bar".into()), - ("doc/stuff/doc1".to_string(), "".into()), - ("doc/stuff/doc2".to_string(), "".into())]), + vec![("bin/foo".to_string(), "foo".into(), false), + ("lib/bar".to_string(), "bar".into(), false), + ("doc/stuff/doc1".to_string(), "".into(), false), + ("doc/stuff/doc2".to_string(), "".into(), false)]), ("mycomponent2".to_string(), vec![MockCommand::File("bin/quux".to_string())], - vec![("bin/quux".to_string(), "quux".into())] + vec![("bin/quux".to_string(), "quux".into(), false)] )] }; @@ -56,11 +56,11 @@ fn package_contains() { let mock = MockInstallerBuilder { components: vec![("mycomponent".to_string(), vec![MockCommand::File("bin/foo".to_string())], - vec![("bin/foo".to_string(), "foo".into())], + vec![("bin/foo".to_string(), "foo".into(), false)], ), ("mycomponent2".to_string(), vec![MockCommand::File("bin/bar".to_string())], - vec![("bin/bar".to_string(), "bar".into())] + vec![("bin/bar".to_string(), "bar".into(), false)] )] }; @@ -78,7 +78,7 @@ fn package_bad_version() { let mock = MockInstallerBuilder { components: vec![("mycomponent".to_string(), vec![MockCommand::File("bin/foo".to_string())], - vec![("bin/foo".to_string(), "foo".into())])] + vec![("bin/foo".to_string(), "foo".into(), false)])] }; mock.build(tempdir.path()); @@ -98,10 +98,10 @@ fn basic_install() { vec![MockCommand::File("bin/foo".to_string()), MockCommand::File("lib/bar".to_string()), MockCommand::Dir("doc/stuff".to_string())], - vec![("bin/foo".to_string(), "foo".into()), - ("lib/bar".to_string(), "bar".into()), - ("doc/stuff/doc1".to_string(), "".into()), - ("doc/stuff/doc2".to_string(), "".into())])] + vec![("bin/foo".to_string(), "foo".into(), false), + ("lib/bar".to_string(), "bar".into(), false), + ("doc/stuff/doc1".to_string(), "".into(), false), + ("doc/stuff/doc2".to_string(), "".into(), false)])] }; mock.build(pkgdir.path()); @@ -136,10 +136,10 @@ fn multiple_component_install() { let mock = MockInstallerBuilder { components: vec![("mycomponent".to_string(), vec![MockCommand::File("bin/foo".to_string())], - vec![("bin/foo".to_string(), "foo".into())]), + vec![("bin/foo".to_string(), "foo".into(), false)]), ("mycomponent2".to_string(), vec![MockCommand::File("lib/bar".to_string())], - vec![("lib/bar".to_string(), "bar".into())])] + vec![("lib/bar".to_string(), "bar".into(), false)])] }; mock.build(pkgdir.path()); @@ -176,13 +176,13 @@ fn uninstall() { vec![MockCommand::File("bin/foo".to_string()), MockCommand::File("lib/bar".to_string()), MockCommand::Dir("doc/stuff".to_string())], - vec![("bin/foo".to_string(), "foo".into()), - ("lib/bar".to_string(), "bar".into()), - ("doc/stuff/doc1".to_string(), "".into()), - ("doc/stuff/doc2".to_string(), "".into())]), + vec![("bin/foo".to_string(), "foo".into(), false), + ("lib/bar".to_string(), "bar".into(), false), + ("doc/stuff/doc1".to_string(), "".into(), false), + ("doc/stuff/doc2".to_string(), "".into(), false)]), ("mycomponent2".to_string(), vec![MockCommand::File("lib/quux".to_string())], - vec![("lib/quux".to_string(), "quux".into())])] + vec![("lib/quux".to_string(), "quux".into(), false)])] }; mock.build(pkgdir.path()); @@ -234,7 +234,7 @@ fn component_bad_version() { let mock = MockInstallerBuilder { components: vec![("mycomponent".to_string(), vec![MockCommand::File("bin/foo".to_string())], - vec![("bin/foo".to_string(), "foo".into())])] + vec![("bin/foo".to_string(), "foo".into(), false)])] }; mock.build(pkgdir.path()); @@ -276,11 +276,14 @@ fn unix_permissions() { components: vec![("mycomponent".to_string(), vec![MockCommand::File("bin/foo".to_string()), MockCommand::File("lib/bar".to_string()), + MockCommand::File("lib/foobar".to_string()), MockCommand::Dir("doc/stuff".to_string())], - vec![("bin/foo".to_string(), "foo".into()), - ("lib/bar".to_string(), "bar".into()), - ("doc/stuff/doc1".to_string(), "".into()), - ("doc/stuff/morestuff/doc2".to_string(), "".into())])] + vec![("bin/foo".to_string(), "foo".into(), false), + ("lib/bar".to_string(), "bar".into(), false), + ("lib/foobar".to_string(), "foobar".into(), true), + ("doc/stuff/doc1".to_string(), "".into(), false), + ("doc/stuff/morestuff/doc2".to_string(), "".into(), false), + ("doc/stuff/morestuff/tool".to_string(), "".into(), true)])] }; mock.build(pkgdir.path()); @@ -304,6 +307,8 @@ fn unix_permissions() { assert_eq!(m, 0o755); let m = fs::metadata(instdir.path().join("lib/bar")).unwrap().permissions().mode(); assert_eq!(m, 0o644); + let m = fs::metadata(instdir.path().join("lib/foobar")).unwrap().permissions().mode(); + assert_eq!(m, 0o755); let m = fs::metadata(instdir.path().join("doc/stuff/")).unwrap().permissions().mode(); assert_eq!(m, 0o755); let m = fs::metadata(instdir.path().join("doc/stuff/doc1")).unwrap().permissions().mode(); @@ -312,6 +317,8 @@ fn unix_permissions() { assert_eq!(m, 0o755); let m = fs::metadata(instdir.path().join("doc/stuff/morestuff/doc2")).unwrap().permissions().mode(); assert_eq!(m, 0o644); + let m = fs::metadata(instdir.path().join("doc/stuff/morestuff/tool")).unwrap().permissions().mode(); + assert_eq!(m, 0o755); } // Installing to a prefix that doesn't exist creates it automatically @@ -322,7 +329,7 @@ fn install_to_prefix_that_does_not_exist() { let mock = MockInstallerBuilder { components: vec![("mycomponent".to_string(), vec![MockCommand::File("bin/foo".to_string())], - vec![("bin/foo".to_string(), "foo".into())])] + vec![("bin/foo".to_string(), "foo".into(), false)])] }; mock.build(pkgdir.path()); diff --git a/src/rustup-mock/src/clitools.rs b/src/rustup-mock/src/clitools.rs index 07854d22ff..1046eab3d4 100644 --- a/src/rustup-mock/src/clitools.rs +++ b/src/rustup-mock/src/clitools.rs @@ -513,7 +513,7 @@ fn build_mock_std_installer(trip: &str) -> MockInstallerBuilder { components: vec![ (format!("rust-std-{}", trip.clone()), vec![MockCommand::File(format!("lib/rustlib/{}/libstd.rlib", trip))], - vec![(format!("lib/rustlib/{}/libstd.rlib", trip), "".into())]) + vec![(format!("lib/rustlib/{}/libstd.rlib", trip), "".into(), false)]) ] } } @@ -524,8 +524,8 @@ fn build_mock_cross_std_installer(target: &str, date: &str) -> MockInstallerBuil (format!("rust-std-{}", target.clone()), vec![MockCommand::File(format!("lib/rustlib/{}/lib/libstd.rlib", target)), MockCommand::File(format!("lib/rustlib/{}/lib/{}", target, date))], - vec![(format!("lib/rustlib/{}/lib/libstd.rlib", target), "".into()), - (format!("lib/rustlib/{}/lib/{}", target, date), "".into())]) + vec![(format!("lib/rustlib/{}/lib/libstd.rlib", target), "".into(), false), + (format!("lib/rustlib/{}/lib/{}", target, date), "".into(), false)]) ] } } @@ -546,7 +546,7 @@ fn build_mock_rustc_installer(target: &str, version: &str, version_hash_: &str) components: vec![ ("rustc".to_string(), vec![MockCommand::File(rustc.clone())], - vec![(rustc, mock_bin("rustc", version, &version_hash))]) + vec![(rustc, mock_bin("rustc", version, &version_hash), false)]) ] } } @@ -557,7 +557,7 @@ fn build_mock_cargo_installer(version: &str, version_hash: &str) -> MockInstalle components: vec![ ("cargo".to_string(), vec![MockCommand::File(cargo.clone())], - vec![(cargo, mock_bin("cargo", version, version_hash))]) + vec![(cargo, mock_bin("cargo", version, version_hash), false)]) ] } } @@ -568,7 +568,7 @@ fn build_mock_rls_installer(version: &str, version_hash: &str) -> MockInstallerB components: vec![ ("rls".to_string(), vec![MockCommand::File(cargo.clone())], - vec![(cargo, mock_bin("rls", version, version_hash))]) + vec![(cargo, mock_bin("rls", version, version_hash), false)]) ] } } @@ -578,7 +578,7 @@ fn build_mock_rust_doc_installer() -> MockInstallerBuilder { components: vec![ ("rust-docs".to_string(), vec![MockCommand::File("share/doc/rust/html/index.html".to_string())], - vec![("share/doc/rust/html/index.html".to_string(), "".into())]) + vec![("share/doc/rust/html/index.html".to_string(), "".into(), false)]) ] } } @@ -588,7 +588,7 @@ fn build_mock_rust_analysis_installer(trip: &str) -> MockInstallerBuilder { components: vec![ (format!("rust-analysis-{}", trip), vec![MockCommand::File(format!("lib/rustlib/{}/analysis/libfoo.json", trip))], - vec![(format!("lib/rustlib/{}/analysis/libfoo.json", trip), "".into())]) + vec![(format!("lib/rustlib/{}/analysis/libfoo.json", trip), "".into(), false)]) ] } } @@ -598,7 +598,7 @@ fn build_mock_rust_src_installer() -> MockInstallerBuilder { components: vec![ ("rust-src".to_string(), vec![MockCommand::File("lib/rustlib/src/rust-src/foo.rs".to_string())], - vec![("lib/rustlib/src/rust-src/foo.rs".to_string(), "".into())]) + vec![("lib/rustlib/src/rust-src/foo.rs".to_string(), "".into(), false)]) ] } } diff --git a/src/rustup-mock/src/lib.rs b/src/rustup-mock/src/lib.rs index 3a860a001b..0dd15780b0 100644 --- a/src/rustup-mock/src/lib.rs +++ b/src/rustup-mock/src/lib.rs @@ -33,8 +33,8 @@ pub struct MockInstallerBuilder { } // A component name, the installation commands for installing files -// (either "file:" or "dir:") and the file paths and contents. -pub type MockComponent = (String, Vec, Vec<(String, Vec)>); +// (either "file:" or "dir:") and the file paths, contents and X bit. +pub type MockComponent = (String, Vec, Vec<(String, Vec, bool)>); #[derive(Clone)] pub enum MockCommand { @@ -67,13 +67,23 @@ impl MockInstallerBuilder { } // Create the component files - for &(ref f_path, ref content) in files { - let dir_path = component_dir.join(f_path); - let dir_path = dir_path.parent().unwrap(); + for &(ref f_path, ref content, executable) in files { + let fname = component_dir.join(f_path); + let dir_path = fname.parent().unwrap().to_owned(); fs::create_dir_all(dir_path).unwrap(); - let ref mut f = File::create(component_dir.join(f_path)).unwrap(); + let ref mut f = File::create(&fname).unwrap(); f.write_all(&content).unwrap(); + drop(f); + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + if executable { + let mut perm = fs::metadata(&fname).unwrap().permissions(); + perm.set_mode(0o755); + fs::set_permissions(&fname, perm).unwrap(); + } + } } }