Upgrade command-fds to 0.3.0 am: 72193c9d0b
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/command-fds/+/3223702
Change-Id: Ibec02a309ee21e1cd3a917b8ce31bf9ee31bbca6
Signed-off-by: Automerger Merge Worker <[email protected]>
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index 0562cca..973f7ca 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,6 +1,6 @@
{
"git": {
- "sha1": "425bb5dc189f3a5130bdc80ca47d6da5001157b4"
+ "sha1": "8db37c4e5dc2040af513dfe45b92cc3d970a3c43"
},
"path_in_vcs": ""
}
\ No newline at end of file
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..98e44ee
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,10 @@
+version: 2
+updates:
+ - package-ecosystem: "cargo"
+ directory: "/"
+ schedule:
+ interval: "weekly"
+ - package-ecosystem: "github-actions"
+ directory: "/"
+ schedule:
+ interval: "weekly"
diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml
index 3d98a16..9212c43 100644
--- a/.github/workflows/rust.yml
+++ b/.github/workflows/rust.yml
@@ -7,13 +7,12 @@
env:
CARGO_TERM_COLOR: always
- grcov-version: 0.8.0
jobs:
build:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v4
- name: Build
run: cargo build
- name: Run tests
@@ -27,7 +26,7 @@
format:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v4
- name: Format Rust code
run: cargo fmt --all -- --check
@@ -36,11 +35,9 @@
env:
RUSTC_BOOTSTRAP: 1
steps:
- - uses: actions/checkout@v2
- - name: Install dependencies
- run: sudo apt-get install libdbus-1-dev
+ - uses: actions/checkout@v4
- name: Install grcov
- run: curl -L https://github.com/mozilla/grcov/releases/latest/download/grcov-linux-x86_64.tar.bz2 | tar jxf -
+ run: curl -L https://github.com/mozilla/grcov/releases/download/v0.8.6/grcov-v0.8.6-x86_64-unknown-linux-gnu.tar.gz | tar zxf -
- name: Install llvm-tools
run: rustup component add llvm-tools-preview
- name: Build for coverage
@@ -55,7 +52,7 @@
- name: Convert coverage
run: ./grcov . -s . --binary-path target/debug/ -t lcov --branch --ignore-not-existing -o target/debug/lcov.info
- name: Upload coverage to codecov.io
- uses: codecov/codecov-action@v1
+ uses: codecov/codecov-action@v3
with:
directory: ./target/debug
fail_ci_if_error: true
diff --git a/Android.bp b/Android.bp
index 1b91848..e42d2d3 100644
--- a/Android.bp
+++ b/Android.bp
@@ -3,6 +3,7 @@
package {
default_applicable_licenses: ["external_rust_crates_command-fds_license"],
+ default_team: "trendy_team_android_rust",
}
license {
@@ -17,7 +18,7 @@
host_supported: true,
crate_name: "command_fds",
cargo_env_compat: true,
- cargo_pkg_version: "0.2.2",
+ cargo_pkg_version: "0.3.0",
crate_root: "src/lib.rs",
edition: "2018",
features: ["default"],
diff --git a/Cargo.toml b/Cargo.toml
index bb015ea..b64d52c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -12,21 +12,30 @@
[package]
edition = "2018"
name = "command-fds"
-version = "0.2.2"
+version = "0.3.0"
authors = ["Andrew Walbran <[email protected]>"]
description = "A library for passing arbitrary file descriptors when spawning child processes."
-keywords = ["command", "process", "child", "subprocess", "fd"]
+readme = "README.md"
+keywords = [
+ "command",
+ "process",
+ "child",
+ "subprocess",
+ "fd",
+]
categories = ["os::unix-apis"]
license = "Apache-2.0"
repository = "https://github.com/google/command-fds/"
+
[dependencies.nix]
-version = "0.22.0"
+version = "0.27.0"
+features = ["fs"]
[dependencies.thiserror]
version = "1.0.24"
[dependencies.tokio-crate]
-version = "^1.0"
+version = "1.32.0"
features = ["process"]
optional = true
default-features = false
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index ef218ac..a3e6c81 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,6 +1,6 @@
[package]
name = "command-fds"
-version = "0.2.2"
+version = "0.3.0"
edition = "2018"
authors = ["Andrew Walbran <[email protected]>"]
license = "Apache-2.0"
@@ -10,15 +10,11 @@
categories = ["os::unix-apis"]
[dependencies]
-nix = "0.22.0"
+nix = { version = "0.27.0", features = ["fs"] }
thiserror = "1.0.24"
-
-[dependencies.tokio-crate]
-package = "tokio"
-version = "^1.0"
-optional = true
-default-features = false
-features = ["process"]
+tokio-crate = { package = "tokio", version = "1.32.0", optional = true, default-features = false, features = [
+ "process",
+] }
[features]
default = []
diff --git a/METADATA b/METADATA
index 7b98618..9187d47 100644
--- a/METADATA
+++ b/METADATA
@@ -1,19 +1,20 @@
+# This project was upgraded with external_updater.
+# Usage: tools/external_updater/updater.sh update external/rust/crates/command-fds
+# For more info, check https://cs.android.com/android/platform/superproject/main/+/main:tools/external_updater/README.md
+
name: "command-fds"
description: "A library for passing arbitrary file descriptors when spawning child processes."
third_party {
- url {
- type: HOMEPAGE
- value: "https://crates.io/crates/command-fds"
- }
- url {
- type: ARCHIVE
- value: "https://static.crates.io/crates/command-fds/command-fds-0.2.2.crate"
- }
- version: "0.2.2"
license_type: NOTICE
last_upgrade_date {
- year: 2022
- month: 6
- day: 28
+ year: 2024
+ month: 8
+ day: 16
+ }
+ homepage: "https://crates.io/crates/command-fds"
+ identifier {
+ type: "Archive"
+ value: "https://static.crates.io/crates/command-fds/command-fds-0.3.0.crate"
+ version: "0.3.0"
}
}
diff --git a/examples/spawn.rs b/examples/spawn.rs
index b399dc7..c65fb1d 100644
--- a/examples/spawn.rs
+++ b/examples/spawn.rs
@@ -14,7 +14,8 @@
use command_fds::{CommandFdExt, FdMapping};
use std::fs::{read_dir, read_link, File};
-use std::os::unix::io::AsRawFd;
+use std::io::stdin;
+use std::os::fd::AsFd;
use std::os::unix::process::CommandExt;
use std::process::Command;
use std::thread::sleep;
@@ -40,17 +41,18 @@
// Prepare to run `ls -l /proc/self/fd` with some FDs mapped.
let mut command = Command::new("ls");
+ let stdin = stdin().as_fd().try_clone_to_owned().unwrap();
command.arg("-l").arg("/proc/self/fd");
command
.fd_mappings(vec![
// Map `file` as FD 3 in the child process.
FdMapping {
- parent_fd: file.as_raw_fd(),
+ parent_fd: file.into(),
child_fd: 3,
},
// Map this process's stdin as FD 5 in the child process.
FdMapping {
- parent_fd: 0,
+ parent_fd: stdin,
child_fd: 5,
},
])
diff --git a/src/lib.rs b/src/lib.rs
index fde680a..8b7f227 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -19,6 +19,8 @@
//! ```rust
//! use command_fds::{CommandFdExt, FdMapping};
//! use std::fs::File;
+//! use std::io::stdin;
+//! use std::os::fd::AsFd;
//! use std::os::unix::io::AsRawFd;
//! use std::process::Command;
//!
@@ -32,12 +34,12 @@
//! .fd_mappings(vec![
//! // Map `file` as FD 3 in the child process.
//! FdMapping {
-//! parent_fd: file.as_raw_fd(),
+//! parent_fd: file.into(),
//! child_fd: 3,
//! },
//! // Map this process's stdin as FD 5 in the child process.
//! FdMapping {
-//! parent_fd: 0,
+//! parent_fd: stdin().as_fd().try_clone_to_owned().unwrap(),
//! child_fd: 5,
//! },
//! ])
@@ -55,6 +57,7 @@
use nix::unistd::dup2;
use std::cmp::max;
use std::io;
+use std::os::fd::{AsRawFd, FromRawFd, OwnedFd};
use std::os::unix::io::RawFd;
use std::os::unix::process::CommandExt;
use std::process::Command;
@@ -63,10 +66,11 @@
/// A mapping from a file descriptor in the parent to a file descriptor in the child, to be applied
/// when spawning a child process.
///
-/// The parent_fd must be kept open until after the child is spawned.
-#[derive(Clone, Debug, Eq, PartialEq)]
+/// This takes ownership of the `parent_fd` to ensure that it is kept open until after the child is
+/// spawned.
+#[derive(Debug)]
pub struct FdMapping {
- pub parent_fd: RawFd,
+ pub parent_fd: OwnedFd,
pub child_fd: RawFd,
}
@@ -79,13 +83,21 @@
pub trait CommandFdExt {
/// Adds the given set of file descriptors to the command.
///
- /// Warning: Calling this more than once on the same command, or attempting to run the same
- /// command more than once after calling this, may result in unexpected behaviour.
+ /// Warning: Calling this more than once on the same command may result in unexpected behaviour.
+ /// In particular, it is not possible to check that two mappings applied separately don't use
+ /// the same `child_fd`. If there is such a collision then one will apply and the other will be
+ /// lost.
+ ///
+ /// Note that the `Command` takes ownership of the file descriptors, which means that they won't
+ /// be closed in the parent process until the `Command` is dropped.
fn fd_mappings(&mut self, mappings: Vec<FdMapping>) -> Result<&mut Self, FdMappingCollision>;
/// Adds the given set of file descriptors to be passed on to the child process when the command
/// is run.
- fn preserved_fds(&mut self, fds: Vec<RawFd>) -> &mut Self;
+ ///
+ /// Note that the `Command` takes ownership of the file descriptors, which means that they won't
+ /// be closed in the parent process until the `Command` is dropped.
+ fn preserved_fds(&mut self, fds: Vec<OwnedFd>) -> &mut Self;
}
impl CommandFdExt for Command {
@@ -98,17 +110,16 @@
// Register the callback to apply the mappings after forking but before execing.
// Safety: `map_fds` will not allocate, so it is safe to call from this hook.
unsafe {
- // If the command is run more than once, and hence this closure is called multiple
- // times, then `mappings` may be in an incorrect state. It would be good if we could
- // reset it to the initial state somehow, or use something else for saving the temporary
- // mappings.
+ // If the command is run more than once, the closure will be called multiple times but
+ // in different forked processes, which will have different copies of `mappings`. So
+ // their changes to it shouldn't be visible to each other.
self.pre_exec(move || map_fds(&mut mappings, &child_fds));
}
Ok(self)
}
- fn preserved_fds(&mut self, fds: Vec<RawFd>) -> &mut Self {
+ fn preserved_fds(&mut self, fds: Vec<OwnedFd>) -> &mut Self {
unsafe {
self.pre_exec(move || preserve_fds(&fds));
}
@@ -140,7 +151,7 @@
// so we still need to ensure we don't conflict with them.
let first_safe_fd = mappings
.iter()
- .map(|mapping| max(mapping.parent_fd, mapping.child_fd))
+ .map(|mapping| max(mapping.parent_fd.as_raw_fd(), mapping.child_fd))
.max()
.unwrap()
+ 1;
@@ -149,32 +160,44 @@
// is clear of either range. Mappings to the same FD are fine though, we can handle them by just
// removing the FD_CLOEXEC flag from the existing (parent) FD.
for mapping in mappings.iter_mut() {
- if child_fds.contains(&mapping.parent_fd) && mapping.parent_fd != mapping.child_fd {
- mapping.parent_fd = fcntl(mapping.parent_fd, FcntlArg::F_DUPFD_CLOEXEC(first_safe_fd))?;
+ if child_fds.contains(&mapping.parent_fd.as_raw_fd())
+ && mapping.parent_fd.as_raw_fd() != mapping.child_fd
+ {
+ let parent_fd = fcntl(
+ mapping.parent_fd.as_raw_fd(),
+ FcntlArg::F_DUPFD_CLOEXEC(first_safe_fd),
+ )?;
+ // SAFETY: We just created `parent_fd` so we can take ownership of it.
+ unsafe {
+ mapping.parent_fd = OwnedFd::from_raw_fd(parent_fd);
+ }
}
}
// Now we can actually duplicate FDs to the desired child FDs.
for mapping in mappings {
- if mapping.child_fd == mapping.parent_fd {
+ if mapping.child_fd == mapping.parent_fd.as_raw_fd() {
// Remove the FD_CLOEXEC flag, so the FD will be kept open when exec is called for the
// child.
- fcntl(mapping.parent_fd, FcntlArg::F_SETFD(FdFlag::empty()))?;
+ fcntl(
+ mapping.parent_fd.as_raw_fd(),
+ FcntlArg::F_SETFD(FdFlag::empty()),
+ )?;
} else {
// This closes child_fd if it is already open as something else, and clears the
// FD_CLOEXEC flag on child_fd.
- dup2(mapping.parent_fd, mapping.child_fd)?;
+ dup2(mapping.parent_fd.as_raw_fd(), mapping.child_fd)?;
}
}
Ok(())
}
-fn preserve_fds(fds: &[RawFd]) -> io::Result<()> {
+fn preserve_fds(fds: &[OwnedFd]) -> io::Result<()> {
for fd in fds {
// Remove the FD_CLOEXEC flag, so the FD will be kept open when exec is called for the
// child.
- fcntl(*fd, FcntlArg::F_SETFD(FdFlag::empty()))?;
+ fcntl(fd.as_raw_fd(), FcntlArg::F_SETFD(FdFlag::empty()))?;
}
Ok(())
@@ -199,30 +222,19 @@
let mut command = Command::new("ls");
- // The same mapping can't be included twice.
- assert!(command
- .fd_mappings(vec![
- FdMapping {
- child_fd: 4,
- parent_fd: 5,
- },
- FdMapping {
- child_fd: 4,
- parent_fd: 5,
- },
- ])
- .is_err());
+ let file1 = File::open("testdata/file1.txt").unwrap();
+ let file2 = File::open("testdata/file2.txt").unwrap();
- // Mapping two different FDs to the same FD isn't allowed either.
+ // Mapping two different FDs to the same FD isn't allowed.
assert!(command
.fd_mappings(vec![
FdMapping {
child_fd: 4,
- parent_fd: 5,
+ parent_fd: file1.into(),
},
FdMapping {
child_fd: 4,
- parent_fd: 6,
+ parent_fd: file2.into(),
},
])
.is_err());
@@ -265,7 +277,7 @@
// Map the file an otherwise unused FD.
assert!(command
.fd_mappings(vec![FdMapping {
- parent_fd: file.as_raw_fd(),
+ parent_fd: file.into(),
child_fd: 5,
},])
.is_ok());
@@ -275,6 +287,7 @@
}
#[test]
+ #[ignore = "flaky on GitHub"]
fn one_preserved() {
setup();
@@ -282,12 +295,13 @@
command.arg("/proc/self/fd");
let file = File::open("testdata/file1.txt").unwrap();
- let file_fd = file.as_raw_fd();
+ let file_fd: OwnedFd = file.into();
+ let raw_file_fd = file_fd.as_raw_fd();
+ assert!(raw_file_fd > 3);
command.preserved_fds(vec![file_fd]);
- assert!(file_fd > 3);
let output = command.output().unwrap();
- expect_fds(&output, &[0, 1, 2, 3, file_fd], 0);
+ expect_fds(&output, &[0, 1, 2, 3, raw_file_fd], 0);
}
#[test]
@@ -299,18 +313,20 @@
let file1 = File::open("testdata/file1.txt").unwrap();
let file2 = File::open("testdata/file2.txt").unwrap();
- let fd1 = file1.as_raw_fd();
- let fd2 = file2.as_raw_fd();
+ let fd1: OwnedFd = file1.into();
+ let fd2: OwnedFd = file2.into();
+ let fd1_raw = fd1.as_raw_fd();
+ let fd2_raw = fd2.as_raw_fd();
// Map files to each other's FDs, to ensure that the temporary FD logic works.
assert!(command
.fd_mappings(vec![
FdMapping {
parent_fd: fd1,
- child_fd: fd2,
+ child_fd: fd2_raw,
},
FdMapping {
parent_fd: fd2,
- child_fd: fd1,
+ child_fd: fd1_raw,
},
])
.is_ok(),);
@@ -318,7 +334,7 @@
let output = command.output().unwrap();
// Expect one more Fd for the /proc/self/fd directory. We can't predict what number it will
// be assigned, because 3 might or might not be taken already by fd1 or fd2.
- expect_fds(&output, &[0, 1, 2, fd1, fd2], 1);
+ expect_fds(&output, &[0, 1, 2, fd1_raw, fd2_raw], 1);
}
#[test]
@@ -330,19 +346,20 @@
let file1 = File::open("testdata/file1.txt").unwrap();
let file2 = File::open("testdata/file2.txt").unwrap();
- let fd1 = file1.as_raw_fd();
+ let fd1: OwnedFd = file1.into();
+ let fd1_raw = fd1.as_raw_fd();
// Map file1 to the same FD it currently has, to ensure the special case for that works.
assert!(command
.fd_mappings(vec![FdMapping {
parent_fd: fd1,
- child_fd: fd1,
+ child_fd: fd1_raw,
}])
.is_ok());
let output = command.output().unwrap();
// Expect one more Fd for the /proc/self/fd directory. We can't predict what number it will
// be assigned, because 3 might or might not be taken already by fd1 or fd2.
- expect_fds(&output, &[0, 1, 2, fd1], 1);
+ expect_fds(&output, &[0, 1, 2, fd1_raw], 1);
// Keep file2 open until the end, to ensure that it's not passed to the child.
drop(file2);
@@ -358,7 +375,7 @@
// Map the file to stdin.
assert!(command
.fd_mappings(vec![FdMapping {
- parent_fd: file.as_raw_fd(),
+ parent_fd: file.into(),
child_fd: 0,
},])
.is_ok());
diff --git a/src/tokio.rs b/src/tokio.rs
index 78979bf..57fe081 100644
--- a/src/tokio.rs
+++ b/src/tokio.rs
@@ -1,24 +1,12 @@
-use std::os::unix::prelude::RawFd;
-
+use std::os::fd::OwnedFd;
use tokio::process::Command;
use tokio_crate as tokio;
-use crate::{map_fds, preserve_fds, validate_child_fds, FdMapping, FdMappingCollision};
+use crate::{
+ map_fds, preserve_fds, validate_child_fds, CommandFdExt, FdMapping, FdMappingCollision,
+};
-/// Extension to add file descriptor mappings to a [`Command`].
-pub trait CommandFdAsyncExt {
- /// Adds the given set of file descriptors to the command.
- ///
- /// Warning: Calling this more than once on the same command, or attempting to run the same
- /// command more than once after calling this, may result in unexpected behaviour.
- fn fd_mappings(&mut self, mappings: Vec<FdMapping>) -> Result<&mut Self, FdMappingCollision>;
-
- /// Adds the given set of file descriptors to be passed on to the child process when the command
- /// is run.
- fn preserved_fds(&mut self, fds: Vec<RawFd>) -> &mut Self;
-}
-
-impl CommandFdAsyncExt for Command {
+impl CommandFdExt for Command {
fn fd_mappings(
&mut self,
mut mappings: Vec<FdMapping>,
@@ -32,7 +20,7 @@
Ok(self)
}
- fn preserved_fds(&mut self, fds: Vec<RawFd>) -> &mut Self {
+ fn preserved_fds(&mut self, fds: Vec<OwnedFd>) -> &mut Self {
unsafe {
self.pre_exec(move || preserve_fds(&fds));
}