From 1376c5a617be5c25655d0d7cb63e3beaa5a6e026 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:20:39 +0200 Subject: Merging upstream version 1.70.0+dfsg1. Signed-off-by: Daniel Baumann --- src/tools/rust-installer/.github/workflows/ci.yml | 23 - src/tools/rust-installer/Cargo.toml | 6 +- src/tools/rust-installer/LICENSE-APACHE | 201 ----- src/tools/rust-installer/LICENSE-MIT | 25 - src/tools/rust-installer/combine-installers.sh | 11 +- src/tools/rust-installer/gen-install-script.sh | 9 - src/tools/rust-installer/gen-installer.sh | 11 +- src/tools/rust-installer/install-template.sh | 9 - src/tools/rust-installer/make-tarballs.sh | 11 +- src/tools/rust-installer/src/combiner.rs | 7 +- src/tools/rust-installer/src/compression.rs | 93 ++- src/tools/rust-installer/src/generator.rs | 7 +- src/tools/rust-installer/src/remove_dir_all.rs | 860 ---------------------- src/tools/rust-installer/src/tarballer.rs | 13 +- src/tools/rust-installer/test.sh | 1 - src/tools/rust-installer/triagebot.toml | 4 - 16 files changed, 95 insertions(+), 1196 deletions(-) delete mode 100644 src/tools/rust-installer/.github/workflows/ci.yml delete mode 100644 src/tools/rust-installer/LICENSE-APACHE delete mode 100644 src/tools/rust-installer/LICENSE-MIT delete mode 100644 src/tools/rust-installer/src/remove_dir_all.rs delete mode 100644 src/tools/rust-installer/triagebot.toml (limited to 'src/tools/rust-installer') diff --git a/src/tools/rust-installer/.github/workflows/ci.yml b/src/tools/rust-installer/.github/workflows/ci.yml deleted file mode 100644 index 57a5cb76e..000000000 --- a/src/tools/rust-installer/.github/workflows/ci.yml +++ /dev/null @@ -1,23 +0,0 @@ ---- - -name: CI -on: [push, pull_request] - -jobs: - test: - name: Test - runs-on: ubuntu-latest - env: - LZMA_API_STATIC: 1 - steps: - - name: Checkout the source code - uses: actions/checkout@v2 - - - name: Install Rust stable - run: rustup toolchain update stable && rustup default stable - - - name: Build the tool - run: cargo build - - - name: Execute the test suite - run: ./test.sh diff --git a/src/tools/rust-installer/Cargo.toml b/src/tools/rust-installer/Cargo.toml index 38b81a1ba..97734f048 100644 --- a/src/tools/rust-installer/Cargo.toml +++ b/src/tools/rust-installer/Cargo.toml @@ -13,7 +13,7 @@ path = "src/main.rs" anyhow = "1.0.19" flate2 = "1.0.1" rayon = "1.0" -tar = "0.4.13" +tar = "0.4.38" walkdir = "2" xz2 = "0.1.4" num_cpus = "1" @@ -22,7 +22,3 @@ remove_dir_all = "0.5" [dependencies.clap] features = ["derive"] version = "3.1" - -[target."cfg(windows)".dependencies] -lazy_static = "1" -winapi = { version = "0.3", features = ["errhandlingapi", "handleapi", "ioapiset", "winerror", "winioctl", "winnt"] } diff --git a/src/tools/rust-installer/LICENSE-APACHE b/src/tools/rust-installer/LICENSE-APACHE deleted file mode 100644 index 16fe87b06..000000000 --- a/src/tools/rust-installer/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/src/tools/rust-installer/LICENSE-MIT b/src/tools/rust-installer/LICENSE-MIT deleted file mode 100644 index e69282e38..000000000 --- a/src/tools/rust-installer/LICENSE-MIT +++ /dev/null @@ -1,25 +0,0 @@ -Copyright (c) 2015 The Rust Project Developers - -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/src/tools/rust-installer/combine-installers.sh b/src/tools/rust-installer/combine-installers.sh index 4931c34dd..bee5319fd 100755 --- a/src/tools/rust-installer/combine-installers.sh +++ b/src/tools/rust-installer/combine-installers.sh @@ -1,13 +1,4 @@ #!/bin/bash -# Copyright 2014 The Rust Project Developers. See the COPYRIGHT -# file at the top-level directory of this distribution and at -# http://rust-lang.org/COPYRIGHT. -# -# Licensed under the Apache License, Version 2.0 or the MIT license -# , at your -# option. This file may not be copied, modified, or distributed -# except according to those terms. set -ue @@ -21,4 +12,4 @@ abs_path() { } src_dir="$(abs_path $(dirname "$0"))" -cargo run --manifest-path="$src_dir/Cargo.toml" -- combine "$@" +$CARGO run --manifest-path="$src_dir/Cargo.toml" -- combine "$@" diff --git a/src/tools/rust-installer/gen-install-script.sh b/src/tools/rust-installer/gen-install-script.sh index b4559d147..f112fd4b2 100755 --- a/src/tools/rust-installer/gen-install-script.sh +++ b/src/tools/rust-installer/gen-install-script.sh @@ -1,13 +1,4 @@ #!/bin/bash -# Copyright 2014 The Rust Project Developers. See the COPYRIGHT -# file at the top-level directory of this distribution and at -# http://rust-lang.org/COPYRIGHT. -# -# Licensed under the Apache License, Version 2.0 or the MIT license -# , at your -# option. This file may not be copied, modified, or distributed -# except according to those terms. set -ue diff --git a/src/tools/rust-installer/gen-installer.sh b/src/tools/rust-installer/gen-installer.sh index 198cfe742..eabd8c95c 100755 --- a/src/tools/rust-installer/gen-installer.sh +++ b/src/tools/rust-installer/gen-installer.sh @@ -1,13 +1,4 @@ #!/bin/bash -# Copyright 2014 The Rust Project Developers. See the COPYRIGHT -# file at the top-level directory of this distribution and at -# http://rust-lang.org/COPYRIGHT. -# -# Licensed under the Apache License, Version 2.0 or the MIT license -# , at your -# option. This file may not be copied, modified, or distributed -# except according to those terms. set -ue @@ -21,4 +12,4 @@ abs_path() { } src_dir="$(abs_path $(dirname "$0"))" -cargo run --manifest-path="$src_dir/Cargo.toml" -- generate "$@" +$CARGO run --manifest-path="$src_dir/Cargo.toml" -- generate "$@" diff --git a/src/tools/rust-installer/install-template.sh b/src/tools/rust-installer/install-template.sh index 7790541a4..92a3f1f2c 100644 --- a/src/tools/rust-installer/install-template.sh +++ b/src/tools/rust-installer/install-template.sh @@ -1,13 +1,4 @@ #!/bin/bash -# Copyright 2014 The Rust Project Developers. See the COPYRIGHT -# file at the top-level directory of this distribution and at -# http://rust-lang.org/COPYRIGHT. -# -# Licensed under the Apache License, Version 2.0 or the MIT license -# , at your -# option. This file may not be copied, modified, or distributed -# except according to those terms. # No undefined variables set -u diff --git a/src/tools/rust-installer/make-tarballs.sh b/src/tools/rust-installer/make-tarballs.sh index e9f88cc8b..e342007da 100755 --- a/src/tools/rust-installer/make-tarballs.sh +++ b/src/tools/rust-installer/make-tarballs.sh @@ -1,13 +1,4 @@ #!/bin/sh -# Copyright 2014 The Rust Project Developers. See the COPYRIGHT -# file at the top-level directory of this distribution and at -# http://rust-lang.org/COPYRIGHT. -# -# Licensed under the Apache License, Version 2.0 or the MIT license -# , at your -# option. This file may not be copied, modified, or distributed -# except according to those terms. set -ue @@ -21,4 +12,4 @@ abs_path() { } src_dir="$(abs_path $(dirname "$0"))" -cargo run --manifest-path="$src_dir/Cargo.toml" -- tarball "$@" +$CARGO run --manifest-path="$src_dir/Cargo.toml" -- tarball "$@" diff --git a/src/tools/rust-installer/src/combiner.rs b/src/tools/rust-installer/src/combiner.rs index 2ec09d67e..abcf59cfe 100644 --- a/src/tools/rust-installer/src/combiner.rs +++ b/src/tools/rust-installer/src/combiner.rs @@ -1,7 +1,7 @@ use super::Scripter; use super::Tarballer; use crate::{ - compression::{CompressionFormat, CompressionFormats}, + compression::{CompressionFormat, CompressionFormats, CompressionProfile}, util::*, }; use anyhow::{bail, Context, Result}; @@ -48,6 +48,10 @@ actor! { #[clap(value_name = "DIR")] output_dir: String = "./dist", + /// The profile used to compress the tarball. + #[clap(value_name = "FORMAT", default_value_t)] + compression_profile: CompressionProfile, + /// The formats used to compress the tarball #[clap(value_name = "FORMAT", default_value_t)] compression_formats: CompressionFormats, @@ -153,6 +157,7 @@ impl Combiner { .work_dir(self.work_dir) .input(self.package_name) .output(path_to_str(&output)?.into()) + .compression_profile(self.compression_profile) .compression_formats(self.compression_formats.clone()); tarballer.run()?; diff --git a/src/tools/rust-installer/src/compression.rs b/src/tools/rust-installer/src/compression.rs index 7e20a9497..7c9c946e0 100644 --- a/src/tools/rust-installer/src/compression.rs +++ b/src/tools/rust-installer/src/compression.rs @@ -4,6 +4,37 @@ use rayon::prelude::*; use std::{convert::TryFrom, fmt, io::Read, io::Write, path::Path, str::FromStr}; use xz2::{read::XzDecoder, write::XzEncoder}; +#[derive(Default, Debug, Copy, Clone)] +pub enum CompressionProfile { + Fast, + #[default] + Balanced, + Best, +} + +impl FromStr for CompressionProfile { + type Err = Error; + + fn from_str(input: &str) -> Result { + Ok(match input { + "fast" => Self::Fast, + "balanced" => Self::Balanced, + "best" => Self::Best, + other => anyhow::bail!("invalid compression profile: {other}"), + }) + } +} + +impl fmt::Display for CompressionProfile { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + CompressionProfile::Fast => f.write_str("fast"), + CompressionProfile::Balanced => f.write_str("balanced"), + CompressionProfile::Best => f.write_str("best"), + } + } +} + #[derive(Debug, Copy, Clone)] pub enum CompressionFormat { Gz, @@ -26,7 +57,11 @@ impl CompressionFormat { } } - pub(crate) fn encode(&self, path: impl AsRef) -> Result, Error> { + pub(crate) fn encode( + &self, + path: impl AsRef, + profile: CompressionProfile, + ) -> Result, Error> { let mut os = path.as_ref().as_os_str().to_os_string(); os.push(format!(".{}", self.extension())); let path = Path::new(&os); @@ -37,16 +72,33 @@ impl CompressionFormat { let file = crate::util::create_new_file(path)?; Ok(match self { - CompressionFormat::Gz => Box::new(GzEncoder::new(file, flate2::Compression::best())), + CompressionFormat::Gz => Box::new(GzEncoder::new( + file, + match profile { + CompressionProfile::Fast => flate2::Compression::fast(), + CompressionProfile::Balanced => flate2::Compression::new(6), + CompressionProfile::Best => flate2::Compression::best(), + }, + )), CompressionFormat::Xz => { - // Note that preset 6 takes about 173MB of memory per thread, so we limit the number of - // threads to not blow out 32-bit hosts. (We could be more precise with - // `MtStreamBuilder::memusage()` if desired.) - let stream = xz2::stream::MtStreamBuilder::new() - .threads(Ord::min(num_cpus::get(), 8) as u32) - .preset(6) - .encoder()?; - Box::new(XzEncoder::new_stream(file, stream)) + let encoder = match profile { + CompressionProfile::Fast => { + xz2::stream::MtStreamBuilder::new().threads(6).preset(1).encoder().unwrap() + } + CompressionProfile::Balanced => { + xz2::stream::MtStreamBuilder::new().threads(6).preset(6).encoder().unwrap() + } + CompressionProfile::Best => { + // Note that this isn't actually the best compression settings for the + // produced artifacts, the production artifacts on static.rust-lang.org are + // produced by rust-lang/promote-release which hosts recompression logic + // and is tuned for optimal compression. + xz2::stream::MtStreamBuilder::new().threads(6).preset(9).encoder().unwrap() + } + }; + + let compressor = XzEncoder::new_stream(std::io::BufWriter::new(file), encoder); + Box::new(compressor) } }) } @@ -94,10 +146,13 @@ impl fmt::Display for CompressionFormats { if i != 0 { write!(f, ",")?; } - fmt::Display::fmt(match format { - CompressionFormat::Xz => "xz", - CompressionFormat::Gz => "gz", - }, f)?; + fmt::Display::fmt( + match format { + CompressionFormat::Xz => "xz", + CompressionFormat::Gz => "gz", + }, + f, + )?; } Ok(()) } @@ -158,20 +213,14 @@ impl Write for CombinedEncoder { } fn flush(&mut self) -> std::io::Result<()> { - self.encoders - .par_iter_mut() - .map(|w| w.flush()) - .collect::>>()?; + self.encoders.par_iter_mut().map(|w| w.flush()).collect::>>()?; Ok(()) } } impl Encoder for CombinedEncoder { fn finish(self: Box) -> Result<(), Error> { - self.encoders - .into_par_iter() - .map(|e| e.finish()) - .collect::, Error>>()?; + self.encoders.into_par_iter().map(|e| e.finish()).collect::, Error>>()?; Ok(()) } } diff --git a/src/tools/rust-installer/src/generator.rs b/src/tools/rust-installer/src/generator.rs index 1e4d00b05..ddd105259 100644 --- a/src/tools/rust-installer/src/generator.rs +++ b/src/tools/rust-installer/src/generator.rs @@ -1,6 +1,6 @@ use super::Scripter; use super::Tarballer; -use crate::compression::CompressionFormats; +use crate::compression::{CompressionFormats, CompressionProfile}; use crate::util::*; use anyhow::{bail, format_err, Context, Result}; use std::collections::BTreeSet; @@ -54,6 +54,10 @@ actor! { #[clap(value_name = "DIR")] output_dir: String = "./dist", + /// The profile used to compress the tarball. + #[clap(value_name = "FORMAT", default_value_t)] + compression_profile: CompressionProfile, + /// The formats used to compress the tarball #[clap(value_name = "FORMAT", default_value_t)] compression_formats: CompressionFormats, @@ -113,6 +117,7 @@ impl Generator { .work_dir(self.work_dir) .input(self.package_name) .output(path_to_str(&output)?.into()) + .compression_profile(self.compression_profile) .compression_formats(self.compression_formats.clone()); tarballer.run()?; diff --git a/src/tools/rust-installer/src/remove_dir_all.rs b/src/tools/rust-installer/src/remove_dir_all.rs deleted file mode 100644 index 110976528..000000000 --- a/src/tools/rust-installer/src/remove_dir_all.rs +++ /dev/null @@ -1,860 +0,0 @@ -#![allow(non_snake_case)] - -use std::io; -use std::path::Path; - -#[cfg(not(windows))] -pub fn remove_dir_all(path: &Path) -> io::Result<()> { - ::std::fs::remove_dir_all(path) -} - -#[cfg(windows)] -pub fn remove_dir_all(path: &Path) -> io::Result<()> { - win::remove_dir_all(path) -} - -#[cfg(windows)] -mod win { - use winapi::ctypes::{c_uint, c_ushort}; - use winapi::shared::minwindef::{BOOL, DWORD, FALSE, FILETIME, LPVOID}; - use winapi::shared::winerror::{ - ERROR_CALL_NOT_IMPLEMENTED, ERROR_INSUFFICIENT_BUFFER, ERROR_NO_MORE_FILES, - }; - use winapi::um::errhandlingapi::{GetLastError, SetLastError}; - use winapi::um::fileapi::{ - CreateFileW, FindFirstFileW, FindNextFileW, GetFileInformationByHandle, - }; - use winapi::um::fileapi::{BY_HANDLE_FILE_INFORMATION, CREATE_ALWAYS, CREATE_NEW}; - use winapi::um::fileapi::{FILE_BASIC_INFO, FILE_RENAME_INFO, TRUNCATE_EXISTING}; - use winapi::um::fileapi::{OPEN_ALWAYS, OPEN_EXISTING}; - use winapi::um::handleapi::{CloseHandle, INVALID_HANDLE_VALUE}; - use winapi::um::ioapiset::DeviceIoControl; - use winapi::um::libloaderapi::{GetModuleHandleW, GetProcAddress}; - use winapi::um::minwinbase::{ - FileBasicInfo, FileRenameInfo, FILE_INFO_BY_HANDLE_CLASS, WIN32_FIND_DATAW, - }; - use winapi::um::winbase::SECURITY_SQOS_PRESENT; - use winapi::um::winbase::{ - FILE_FLAG_BACKUP_SEMANTICS, FILE_FLAG_DELETE_ON_CLOSE, FILE_FLAG_OPEN_REPARSE_POINT, - }; - use winapi::um::winioctl::FSCTL_GET_REPARSE_POINT; - use winapi::um::winnt::{DELETE, FILE_ATTRIBUTE_DIRECTORY, HANDLE, LPCWSTR}; - use winapi::um::winnt::{FILE_ATTRIBUTE_READONLY, FILE_ATTRIBUTE_REPARSE_POINT}; - use winapi::um::winnt::{FILE_GENERIC_WRITE, FILE_WRITE_DATA, GENERIC_READ, GENERIC_WRITE}; - use winapi::um::winnt::{FILE_READ_ATTRIBUTES, FILE_WRITE_ATTRIBUTES}; - use winapi::um::winnt::{FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE}; - use winapi::um::winnt::{IO_REPARSE_TAG_MOUNT_POINT, IO_REPARSE_TAG_SYMLINK, LARGE_INTEGER}; - - use std::ffi::{OsStr, OsString}; - use std::io; - use std::mem; - use std::os::windows::ffi::{OsStrExt, OsStringExt}; - use std::path::{Path, PathBuf}; - use std::ptr; - use std::sync::Arc; - - pub fn remove_dir_all(path: &Path) -> io::Result<()> { - // On Windows it is not enough to just recursively remove the contents of a - // directory and then the directory itself. Deleting does not happen - // instantaneously, but is scheduled. - // To work around this, we move the file or directory to some `base_dir` - // right before deletion to avoid races. - // - // As `base_dir` we choose the parent dir of the directory we want to - // remove. We very probably have permission to create files here, as we - // already need write permission in this dir to delete the directory. And it - // should be on the same volume. - // - // To handle files with names like `CON` and `morse .. .`, and when a - // directory structure is so deep it needs long path names the path is first - // converted to a `//?/`-path with `get_path()`. - // - // To make sure we don't leave a moved file laying around if the process - // crashes before we can delete the file, we do all operations on an file - // handle. By opening a file with `FILE_FLAG_DELETE_ON_CLOSE` Windows will - // always delete the file when the handle closes. - // - // All files are renamed to be in the `base_dir`, and have their name - // changed to "rm-". After every rename the counter is increased. - // Rename should not overwrite possibly existing files in the base dir. So - // if it fails with `AlreadyExists`, we just increase the counter and try - // again. - // - // For read-only files and directories we first have to remove the read-only - // attribute before we can move or delete them. This also removes the - // attribute from possible hardlinks to the file, so just before closing we - // restore the read-only attribute. - // - // If 'path' points to a directory symlink or junction we should not - // recursively remove the target of the link, but only the link itself. - // - // Moving and deleting is guaranteed to succeed if we are able to open the - // file with `DELETE` permission. If others have the file open we only have - // `DELETE` permission if they have specified `FILE_SHARE_DELETE`. We can - // also delete the file now, but it will not disappear until all others have - // closed the file. But no-one can open the file after we have flagged it - // for deletion. - - // Open the path once to get the canonical path, file type and attributes. - let (path, metadata) = { - let mut opts = OpenOptions::new(); - opts.access_mode(FILE_READ_ATTRIBUTES); - opts.custom_flags(FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT); - let file = File::open(path, &opts)?; - (get_path(&file)?, file.file_attr()?) - }; - - let mut ctx = RmdirContext { - base_dir: match path.parent() { - Some(dir) => dir, - None => { - return Err(io::Error::new( - io::ErrorKind::PermissionDenied, - "can't delete root directory", - )) - } - }, - readonly: metadata.perm().readonly(), - counter: 0, - }; - - let filetype = metadata.file_type(); - if filetype.is_dir() { - remove_dir_all_recursive(path.as_ref(), &mut ctx) - } else if filetype.is_symlink_dir() { - remove_item(path.as_ref(), &mut ctx) - } else { - Err(io::Error::new( - io::ErrorKind::PermissionDenied, - "Not a directory", - )) - } - } - - fn readdir(p: &Path) -> io::Result { - let root = p.to_path_buf(); - let star = p.join("*"); - let path = to_u16s(&star)?; - - unsafe { - let mut wfd = mem::zeroed(); - let find_handle = FindFirstFileW(path.as_ptr(), &mut wfd); - if find_handle != INVALID_HANDLE_VALUE { - Ok(ReadDir { - handle: FindNextFileHandle(find_handle), - root: Arc::new(root), - first: Some(wfd), - }) - } else { - Err(io::Error::last_os_error()) - } - } - } - - struct RmdirContext<'a> { - base_dir: &'a Path, - readonly: bool, - counter: u64, - } - - fn remove_dir_all_recursive(path: &Path, ctx: &mut RmdirContext) -> io::Result<()> { - let dir_readonly = ctx.readonly; - for child in readdir(path)? { - let child = child?; - let child_type = child.file_type()?; - ctx.readonly = child.metadata()?.perm().readonly(); - if child_type.is_dir() { - remove_dir_all_recursive(&child.path(), ctx)?; - } else { - remove_item(&child.path().as_ref(), ctx)?; - } - } - ctx.readonly = dir_readonly; - remove_item(path, ctx) - } - - fn remove_item(path: &Path, ctx: &mut RmdirContext) -> io::Result<()> { - if !ctx.readonly { - let mut opts = OpenOptions::new(); - opts.access_mode(DELETE); - opts.custom_flags( - FILE_FLAG_BACKUP_SEMANTICS | // delete directory - FILE_FLAG_OPEN_REPARSE_POINT | // delete symlink - FILE_FLAG_DELETE_ON_CLOSE, - ); - let file = File::open(path, &opts)?; - move_item(&file, ctx) - } else { - // remove read-only permision - set_perm(&path, FilePermissions::new())?; - // move and delete file, similar to !readonly. - // only the access mode is different. - let mut opts = OpenOptions::new(); - opts.access_mode(DELETE | FILE_WRITE_ATTRIBUTES); - opts.custom_flags( - FILE_FLAG_BACKUP_SEMANTICS - | FILE_FLAG_OPEN_REPARSE_POINT - | FILE_FLAG_DELETE_ON_CLOSE, - ); - let file = File::open(path, &opts)?; - move_item(&file, ctx)?; - // restore read-only flag just in case there are other hard links - let mut perm = FilePermissions::new(); - perm.set_readonly(true); - let _ = file.set_perm(perm); // ignore if this fails - Ok(()) - } - } - - macro_rules! compat_fn { - ($module:ident: $( - fn $symbol:ident($($argname:ident: $argtype:ty),*) - -> $rettype:ty { - $($body:expr);* - } - )*) => ($( - #[allow(unused_variables)] - unsafe fn $symbol($($argname: $argtype),*) -> $rettype { - use std::sync::atomic::{AtomicUsize, Ordering}; - use std::mem; - use std::ffi::CString; - type F = unsafe extern "system" fn($($argtype),*) -> $rettype; - - lazy_static! { static ref PTR: AtomicUsize = AtomicUsize::new(0);} - - fn lookup(module: &str, symbol: &str) -> Option { - let mut module: Vec = module.encode_utf16().collect(); - module.push(0); - let symbol = CString::new(symbol).unwrap(); - unsafe { - let handle = GetModuleHandleW(module.as_ptr()); - match GetProcAddress(handle, symbol.as_ptr()) as usize { - 0 => None, - n => Some(n), - } - } - } - - fn store_func(ptr: &AtomicUsize, module: &str, symbol: &str, - fallback: usize) -> usize { - let value = lookup(module, symbol).unwrap_or(fallback); - ptr.store(value, Ordering::SeqCst); - value - } - - fn load() -> usize { - store_func(&PTR, stringify!($module), stringify!($symbol), fallback as usize) - } - unsafe extern "system" fn fallback($($argname: $argtype),*) - -> $rettype { - $($body);* - } - - let addr = match PTR.load(Ordering::SeqCst) { - 0 => load(), - n => n, - }; - mem::transmute::(addr)($($argname),*) - } - )*) - } - - compat_fn! { - kernel32: - fn GetFinalPathNameByHandleW(_hFile: HANDLE, - _lpszFilePath: LPCWSTR, - _cchFilePath: DWORD, - _dwFlags: DWORD) -> DWORD { - SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0 - } - fn SetFileInformationByHandle(_hFile: HANDLE, - _FileInformationClass: FILE_INFO_BY_HANDLE_CLASS, - _lpFileInformation: LPVOID, - _dwBufferSize: DWORD) -> BOOL { - SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0 - } - } - - fn cvt(i: i32) -> io::Result { - if i == 0 { - Err(io::Error::last_os_error()) - } else { - Ok(i) - } - } - - fn to_u16s>(s: S) -> io::Result> { - fn inner(s: &OsStr) -> io::Result> { - let mut maybe_result: Vec = s.encode_wide().collect(); - if maybe_result.iter().any(|&u| u == 0) { - return Err(io::Error::new( - io::ErrorKind::InvalidInput, - "strings passed to WinAPI cannot contain NULs", - )); - } - maybe_result.push(0); - Ok(maybe_result) - } - inner(s.as_ref()) - } - - fn truncate_utf16_at_nul<'a>(v: &'a [u16]) -> &'a [u16] { - match v.iter().position(|c| *c == 0) { - // don't include the 0 - Some(i) => &v[..i], - None => v, - } - } - - fn fill_utf16_buf(mut f1: F1, f2: F2) -> io::Result - where - F1: FnMut(*mut u16, DWORD) -> DWORD, - F2: FnOnce(&[u16]) -> T, - { - // Start off with a stack buf but then spill over to the heap if we end up - // needing more space. - let mut stack_buf = [0u16; 512]; - let mut heap_buf = Vec::new(); - unsafe { - let mut n = stack_buf.len(); - loop { - let buf = if n <= stack_buf.len() { - &mut stack_buf[..] - } else { - let extra = n - heap_buf.len(); - heap_buf.reserve(extra); - heap_buf.set_len(n); - &mut heap_buf[..] - }; - - // This function is typically called on windows API functions which - // will return the correct length of the string, but these functions - // also return the `0` on error. In some cases, however, the - // returned "correct length" may actually be 0! - // - // To handle this case we call `SetLastError` to reset it to 0 and - // then check it again if we get the "0 error value". If the "last - // error" is still 0 then we interpret it as a 0 length buffer and - // not an actual error. - SetLastError(0); - let k = match f1(buf.as_mut_ptr(), n as DWORD) { - 0 if GetLastError() == 0 => 0, - 0 => return Err(io::Error::last_os_error()), - n => n, - } as usize; - if k == n && GetLastError() == ERROR_INSUFFICIENT_BUFFER { - n *= 2; - } else if k >= n { - n = k; - } else { - return Ok(f2(&buf[..k])); - } - } - } - } - - #[derive(Clone, PartialEq, Eq, Debug, Default)] - struct FilePermissions { - readonly: bool, - } - - impl FilePermissions { - fn new() -> FilePermissions { - Default::default() - } - fn readonly(&self) -> bool { - self.readonly - } - fn set_readonly(&mut self, readonly: bool) { - self.readonly = readonly - } - } - - #[derive(Clone)] - struct OpenOptions { - // generic - read: bool, - write: bool, - append: bool, - truncate: bool, - create: bool, - create_new: bool, - // system-specific - custom_flags: u32, - access_mode: Option, - attributes: DWORD, - share_mode: DWORD, - security_qos_flags: DWORD, - security_attributes: usize, // FIXME: should be a reference - } - - impl OpenOptions { - fn new() -> OpenOptions { - OpenOptions { - // generic - read: false, - write: false, - append: false, - truncate: false, - create: false, - create_new: false, - // system-specific - custom_flags: 0, - access_mode: None, - share_mode: FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - attributes: 0, - security_qos_flags: 0, - security_attributes: 0, - } - } - fn custom_flags(&mut self, flags: u32) { - self.custom_flags = flags; - } - fn access_mode(&mut self, access_mode: u32) { - self.access_mode = Some(access_mode); - } - - fn get_access_mode(&self) -> io::Result { - const ERROR_INVALID_PARAMETER: i32 = 87; - - match (self.read, self.write, self.append, self.access_mode) { - (_, _, _, Some(mode)) => Ok(mode), - (true, false, false, None) => Ok(GENERIC_READ), - (false, true, false, None) => Ok(GENERIC_WRITE), - (true, true, false, None) => Ok(GENERIC_READ | GENERIC_WRITE), - (false, _, true, None) => Ok(FILE_GENERIC_WRITE & !FILE_WRITE_DATA), - (true, _, true, None) => Ok(GENERIC_READ | (FILE_GENERIC_WRITE & !FILE_WRITE_DATA)), - (false, false, false, None) => { - Err(io::Error::from_raw_os_error(ERROR_INVALID_PARAMETER)) - } - } - } - - fn get_creation_mode(&self) -> io::Result { - const ERROR_INVALID_PARAMETER: i32 = 87; - - match (self.write, self.append) { - (true, false) => {} - (false, false) => { - if self.truncate || self.create || self.create_new { - return Err(io::Error::from_raw_os_error(ERROR_INVALID_PARAMETER)); - } - } - (_, true) => { - if self.truncate && !self.create_new { - return Err(io::Error::from_raw_os_error(ERROR_INVALID_PARAMETER)); - } - } - } - - Ok(match (self.create, self.truncate, self.create_new) { - (false, false, false) => OPEN_EXISTING, - (true, false, false) => OPEN_ALWAYS, - (false, true, false) => TRUNCATE_EXISTING, - (true, true, false) => CREATE_ALWAYS, - (_, _, true) => CREATE_NEW, - }) - } - - fn get_flags_and_attributes(&self) -> DWORD { - self.custom_flags - | self.attributes - | self.security_qos_flags - | if self.security_qos_flags != 0 { - SECURITY_SQOS_PRESENT - } else { - 0 - } - | if self.create_new { - FILE_FLAG_OPEN_REPARSE_POINT - } else { - 0 - } - } - } - - struct File { - handle: Handle, - } - - impl File { - fn open(path: &Path, opts: &OpenOptions) -> io::Result { - let path = to_u16s(path)?; - let handle = unsafe { - CreateFileW( - path.as_ptr(), - opts.get_access_mode()?, - opts.share_mode, - opts.security_attributes as *mut _, - opts.get_creation_mode()?, - opts.get_flags_and_attributes(), - ptr::null_mut(), - ) - }; - if handle == INVALID_HANDLE_VALUE { - Err(io::Error::last_os_error()) - } else { - Ok(File { - handle: Handle::new(handle), - }) - } - } - - fn file_attr(&self) -> io::Result { - unsafe { - let mut info: BY_HANDLE_FILE_INFORMATION = mem::zeroed(); - cvt(GetFileInformationByHandle(self.handle.raw(), &mut info))?; - let mut attr = FileAttr { - attributes: info.dwFileAttributes, - creation_time: info.ftCreationTime, - last_access_time: info.ftLastAccessTime, - last_write_time: info.ftLastWriteTime, - file_size: ((info.nFileSizeHigh as u64) << 32) | (info.nFileSizeLow as u64), - reparse_tag: 0, - }; - if attr.is_reparse_point() { - let mut b = [0; MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; - if let Ok((_, buf)) = self.reparse_point(&mut b) { - attr.reparse_tag = buf.ReparseTag; - } - } - Ok(attr) - } - } - - fn set_attributes(&self, attr: DWORD) -> io::Result<()> { - let zero: LARGE_INTEGER = unsafe { mem::zeroed() }; - - let mut info = FILE_BASIC_INFO { - CreationTime: zero, // do not change - LastAccessTime: zero, // do not change - LastWriteTime: zero, // do not change - ChangeTime: zero, // do not change - FileAttributes: attr, - }; - let size = mem::size_of_val(&info); - cvt(unsafe { - SetFileInformationByHandle( - self.handle.raw(), - FileBasicInfo, - &mut info as *mut _ as *mut _, - size as DWORD, - ) - })?; - Ok(()) - } - - fn rename(&self, new: &Path, replace: bool) -> io::Result<()> { - // &self must be opened with DELETE permission - use std::iter; - #[cfg(target_arch = "x86")] - const STRUCT_SIZE: usize = 12; - #[cfg(target_arch = "x86_64")] - const STRUCT_SIZE: usize = 20; - - // FIXME: check for internal NULs in 'new' - let mut data: Vec = iter::repeat(0u16) - .take(STRUCT_SIZE / 2) - .chain(new.as_os_str().encode_wide()) - .collect(); - data.push(0); - let size = data.len() * 2; - - unsafe { - // Thanks to alignment guarantees on Windows this works - // (8 for 32-bit and 16 for 64-bit) - let info = data.as_mut_ptr() as *mut FILE_RENAME_INFO; - // The type of ReplaceIfExists is BOOL, but it actually expects a - // BOOLEAN. This means true is -1, not c::TRUE. - (*info).ReplaceIfExists = if replace { -1 } else { FALSE }; - (*info).RootDirectory = ptr::null_mut(); - (*info).FileNameLength = (size - STRUCT_SIZE) as DWORD; - cvt(SetFileInformationByHandle( - self.handle().raw(), - FileRenameInfo, - data.as_mut_ptr() as *mut _ as *mut _, - size as DWORD, - ))?; - Ok(()) - } - } - fn set_perm(&self, perm: FilePermissions) -> io::Result<()> { - let attr = self.file_attr()?.attributes; - if perm.readonly == (attr & FILE_ATTRIBUTE_READONLY != 0) { - Ok(()) - } else if perm.readonly { - self.set_attributes(attr | FILE_ATTRIBUTE_READONLY) - } else { - self.set_attributes(attr & !FILE_ATTRIBUTE_READONLY) - } - } - - fn handle(&self) -> &Handle { - &self.handle - } - - fn reparse_point<'a>( - &self, - space: &'a mut [u8; MAXIMUM_REPARSE_DATA_BUFFER_SIZE], - ) -> io::Result<(DWORD, &'a REPARSE_DATA_BUFFER)> { - unsafe { - let mut bytes = 0; - cvt({ - DeviceIoControl( - self.handle.raw(), - FSCTL_GET_REPARSE_POINT, - ptr::null_mut(), - 0, - space.as_mut_ptr() as *mut _, - space.len() as DWORD, - &mut bytes, - ptr::null_mut(), - ) - })?; - Ok((bytes, &*(space.as_ptr() as *const REPARSE_DATA_BUFFER))) - } - } - } - - #[derive(Copy, Clone, PartialEq, Eq, Hash)] - enum FileType { - Dir, - File, - SymlinkFile, - SymlinkDir, - ReparsePoint, - MountPoint, - } - - impl FileType { - fn new(attrs: DWORD, reparse_tag: DWORD) -> FileType { - match ( - attrs & FILE_ATTRIBUTE_DIRECTORY != 0, - attrs & FILE_ATTRIBUTE_REPARSE_POINT != 0, - reparse_tag, - ) { - (false, false, _) => FileType::File, - (true, false, _) => FileType::Dir, - (false, true, IO_REPARSE_TAG_SYMLINK) => FileType::SymlinkFile, - (true, true, IO_REPARSE_TAG_SYMLINK) => FileType::SymlinkDir, - (true, true, IO_REPARSE_TAG_MOUNT_POINT) => FileType::MountPoint, - (_, true, _) => FileType::ReparsePoint, - // Note: if a _file_ has a reparse tag of the type IO_REPARSE_TAG_MOUNT_POINT it is - // invalid, as junctions always have to be dirs. We set the filetype to ReparsePoint - // to indicate it is something symlink-like, but not something you can follow. - } - } - - fn is_dir(&self) -> bool { - *self == FileType::Dir - } - fn is_symlink_dir(&self) -> bool { - *self == FileType::SymlinkDir || *self == FileType::MountPoint - } - } - - impl DirEntry { - fn new(root: &Arc, wfd: &WIN32_FIND_DATAW) -> Option { - let first_bytes = &wfd.cFileName[0..3]; - if first_bytes.starts_with(&[46, 0]) || first_bytes.starts_with(&[46, 46, 0]) { - None - } else { - Some(DirEntry { - root: root.clone(), - data: *wfd, - }) - } - } - - fn path(&self) -> PathBuf { - self.root.join(&self.file_name()) - } - - fn file_name(&self) -> OsString { - let filename = truncate_utf16_at_nul(&self.data.cFileName); - OsString::from_wide(filename) - } - - fn file_type(&self) -> io::Result { - Ok(FileType::new( - self.data.dwFileAttributes, - /* reparse_tag = */ self.data.dwReserved0, - )) - } - - fn metadata(&self) -> io::Result { - Ok(FileAttr { - attributes: self.data.dwFileAttributes, - creation_time: self.data.ftCreationTime, - last_access_time: self.data.ftLastAccessTime, - last_write_time: self.data.ftLastWriteTime, - file_size: ((self.data.nFileSizeHigh as u64) << 32) - | (self.data.nFileSizeLow as u64), - reparse_tag: if self.data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT != 0 { - // reserved unless this is a reparse point - self.data.dwReserved0 - } else { - 0 - }, - }) - } - } - - struct DirEntry { - root: Arc, - data: WIN32_FIND_DATAW, - } - - struct ReadDir { - handle: FindNextFileHandle, - root: Arc, - first: Option, - } - - impl Iterator for ReadDir { - type Item = io::Result; - fn next(&mut self) -> Option> { - if let Some(first) = self.first.take() { - if let Some(e) = DirEntry::new(&self.root, &first) { - return Some(Ok(e)); - } - } - unsafe { - let mut wfd = mem::zeroed(); - loop { - if FindNextFileW(self.handle.0, &mut wfd) == 0 { - if GetLastError() == ERROR_NO_MORE_FILES { - return None; - } else { - return Some(Err(io::Error::last_os_error())); - } - } - if let Some(e) = DirEntry::new(&self.root, &wfd) { - return Some(Ok(e)); - } - } - } - } - } - - #[derive(Clone)] - struct FileAttr { - attributes: DWORD, - creation_time: FILETIME, - last_access_time: FILETIME, - last_write_time: FILETIME, - file_size: u64, - reparse_tag: DWORD, - } - - impl FileAttr { - fn perm(&self) -> FilePermissions { - FilePermissions { - readonly: self.attributes & FILE_ATTRIBUTE_READONLY != 0, - } - } - - fn file_type(&self) -> FileType { - FileType::new(self.attributes, self.reparse_tag) - } - - fn is_reparse_point(&self) -> bool { - self.attributes & FILE_ATTRIBUTE_REPARSE_POINT != 0 - } - } - - #[repr(C)] - struct REPARSE_DATA_BUFFER { - ReparseTag: c_uint, - ReparseDataLength: c_ushort, - Reserved: c_ushort, - rest: (), - } - - const MAXIMUM_REPARSE_DATA_BUFFER_SIZE: usize = 16 * 1024; - - /// An owned container for `HANDLE` object, closing them on Drop. - /// - /// All methods are inherited through a `Deref` impl to `RawHandle` - struct Handle(RawHandle); - - use std::ops::Deref; - - /// A wrapper type for `HANDLE` objects to give them proper Send/Sync inference - /// as well as Rust-y methods. - /// - /// This does **not** drop the handle when it goes out of scope, use `Handle` - /// instead for that. - #[derive(Copy, Clone)] - struct RawHandle(HANDLE); - - unsafe impl Send for RawHandle {} - unsafe impl Sync for RawHandle {} - - impl Handle { - fn new(handle: HANDLE) -> Handle { - Handle(RawHandle::new(handle)) - } - } - - impl Deref for Handle { - type Target = RawHandle; - fn deref(&self) -> &RawHandle { - &self.0 - } - } - - impl Drop for Handle { - fn drop(&mut self) { - unsafe { - let _ = CloseHandle(self.raw()); - } - } - } - - impl RawHandle { - fn new(handle: HANDLE) -> RawHandle { - RawHandle(handle) - } - - fn raw(&self) -> HANDLE { - self.0 - } - } - - struct FindNextFileHandle(HANDLE); - - fn get_path(f: &File) -> io::Result { - fill_utf16_buf( - |buf, sz| unsafe { - GetFinalPathNameByHandleW(f.handle.raw(), buf, sz, VOLUME_NAME_DOS) - }, - |buf| PathBuf::from(OsString::from_wide(buf)), - ) - } - - fn move_item(file: &File, ctx: &mut RmdirContext) -> io::Result<()> { - let mut tmpname = ctx.base_dir.join(format! {"rm-{}", ctx.counter}); - ctx.counter += 1; - // Try to rename the file. If it already exists, just retry with an other - // filename. - while let Err(err) = file.rename(tmpname.as_ref(), false) { - if err.kind() != io::ErrorKind::AlreadyExists { - return Err(err); - }; - tmpname = ctx.base_dir.join(format!("rm-{}", ctx.counter)); - ctx.counter += 1; - } - Ok(()) - } - - fn set_perm(path: &Path, perm: FilePermissions) -> io::Result<()> { - let mut opts = OpenOptions::new(); - opts.access_mode(FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES); - opts.custom_flags(FILE_FLAG_BACKUP_SEMANTICS); - let file = File::open(path, &opts)?; - file.set_perm(perm) - } - - const VOLUME_NAME_DOS: DWORD = 0x0; -} diff --git a/src/tools/rust-installer/src/tarballer.rs b/src/tools/rust-installer/src/tarballer.rs index 76f5af3fa..7353a49fe 100644 --- a/src/tools/rust-installer/src/tarballer.rs +++ b/src/tools/rust-installer/src/tarballer.rs @@ -1,12 +1,12 @@ use anyhow::{bail, Context, Result}; use std::fs::{read_link, symlink_metadata}; -use std::io::{empty, BufWriter, Write}; +use std::io::{BufWriter, Write}; use std::path::Path; use tar::{Builder, Header}; use walkdir::WalkDir; use crate::{ - compression::{CombinedEncoder, CompressionFormats}, + compression::{CombinedEncoder, CompressionFormats, CompressionProfile}, util::*, }; @@ -25,6 +25,10 @@ actor! { #[clap(value_name = "DIR")] work_dir: String = "./workdir", + /// The profile used to compress the tarball. + #[clap(value_name = "FORMAT", default_value_t)] + compression_profile: CompressionProfile, + /// The formats used to compress the tarball. #[clap(value_name = "FORMAT", default_value_t)] compression_formats: CompressionFormats, @@ -38,7 +42,7 @@ impl Tarballer { let encoder = CombinedEncoder::new( self.compression_formats .iter() - .map(|f| f.encode(&tarball_name)) + .map(|f| f.encode(&tarball_name, self.compression_profile)) .collect::>>()?, ); @@ -89,8 +93,7 @@ fn append_path(builder: &mut Builder, src: &Path, path: &String) -> header.set_metadata(&stat); if stat.file_type().is_symlink() { let link = read_link(src)?; - header.set_link_name(&link)?; - builder.append_data(&mut header, path, &mut empty())?; + builder.append_link(&mut header, path, &link)?; } else { if cfg!(windows) { // Windows doesn't really have a mode, so `tar` never marks files executable. diff --git a/src/tools/rust-installer/test.sh b/src/tools/rust-installer/test.sh index bf6de4cb1..dac6f77ef 100755 --- a/src/tools/rust-installer/test.sh +++ b/src/tools/rust-installer/test.sh @@ -20,7 +20,6 @@ abs_path() { S="$(abs_path $(dirname $0))" TEST_DIR="$S/test" -TMP_DIR="$S/tmp" WORK_DIR="$TMP_DIR/workdir" OUT_DIR="$TMP_DIR/outdir" PREFIX_DIR="$TMP_DIR/prefix" diff --git a/src/tools/rust-installer/triagebot.toml b/src/tools/rust-installer/triagebot.toml deleted file mode 100644 index 493500449..000000000 --- a/src/tools/rust-installer/triagebot.toml +++ /dev/null @@ -1,4 +0,0 @@ -[assign] - -[assign.owners] -"*" = ["@Mark-Simulacrum"] -- cgit v1.2.3