1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 11:37:44 +00:00

Merge pull request #7775 from jfinkels/cp-make-fifo-uucore

cp: factor make_fifo() function out to uucore::fs
This commit is contained in:
jfinkels 2025-04-19 05:40:29 -04:00 committed by GitHub
parent 74e353dec0
commit 3965bc5b9e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 61 additions and 14 deletions

View file

@ -7,14 +7,10 @@
use quick_error::quick_error; use quick_error::quick_error;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
#[cfg(not(windows))]
use std::ffi::CString;
use std::ffi::OsString; use std::ffi::OsString;
use std::fs::{self, Metadata, OpenOptions, Permissions}; use std::fs::{self, Metadata, OpenOptions, Permissions};
use std::io; use std::io;
#[cfg(unix)] #[cfg(unix)]
use std::os::unix::ffi::OsStrExt;
#[cfg(unix)]
use std::os::unix::fs::{FileTypeExt, PermissionsExt}; use std::os::unix::fs::{FileTypeExt, PermissionsExt};
use std::path::{Path, PathBuf, StripPrefixError}; use std::path::{Path, PathBuf, StripPrefixError};
#[cfg(all(unix, not(target_os = "android")))] #[cfg(all(unix, not(target_os = "android")))]
@ -23,13 +19,13 @@ use uucore::fsxattr::copy_xattrs;
use clap::{Arg, ArgAction, ArgMatches, Command, builder::ValueParser}; use clap::{Arg, ArgAction, ArgMatches, Command, builder::ValueParser};
use filetime::FileTime; use filetime::FileTime;
use indicatif::{ProgressBar, ProgressStyle}; use indicatif::{ProgressBar, ProgressStyle};
#[cfg(unix)]
use libc::mkfifo;
use quick_error::ResultExt; use quick_error::ResultExt;
use platform::copy_on_write; use platform::copy_on_write;
use uucore::display::Quotable; use uucore::display::Quotable;
use uucore::error::{UClapError, UError, UResult, UUsageError, set_exit_code}; use uucore::error::{UClapError, UError, UResult, UUsageError, set_exit_code};
#[cfg(unix)]
use uucore::fs::make_fifo;
use uucore::fs::{ use uucore::fs::{
FileInformation, MissingHandling, ResolveMode, are_hardlinks_to_same_file, canonicalize, FileInformation, MissingHandling, ResolveMode, are_hardlinks_to_same_file, canonicalize,
get_filename, is_symlink_loop, normalize_path, path_ends_with_terminator, get_filename, is_symlink_loop, normalize_path, path_ends_with_terminator,
@ -2534,12 +2530,7 @@ fn copy_fifo(dest: &Path, overwrite: OverwriteMode, debug: bool) -> CopyResult<(
fs::remove_file(dest)?; fs::remove_file(dest)?;
} }
let name = CString::new(dest.as_os_str().as_bytes()).unwrap(); make_fifo(dest).map_err(|_| format!("cannot create fifo {}: File exists", dest.quote()).into())
let err = unsafe { mkfifo(name.as_ptr(), 0o666) };
if err == -1 {
return Err(format!("cannot create fifo {}: File exists", dest.quote()).into());
}
Ok(())
} }
fn copy_link( fn copy_link(

View file

@ -3,7 +3,7 @@
// For the full copyright and license information, please view the LICENSE // For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code. // file that was distributed with this source code.
//! Set of functions to manage files and symlinks //! Set of functions to manage regular files, special files, and links.
// spell-checker:ignore backport // spell-checker:ignore backport
@ -11,11 +11,13 @@
use libc::{ use libc::{
S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO, S_IFLNK, S_IFMT, S_IFREG, S_IFSOCK, S_IRGRP, S_IROTH, S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO, S_IFLNK, S_IFMT, S_IFREG, S_IFSOCK, S_IRGRP, S_IROTH,
S_IRUSR, S_ISGID, S_ISUID, S_ISVTX, S_IWGRP, S_IWOTH, S_IWUSR, S_IXGRP, S_IXOTH, S_IXUSR, S_IRUSR, S_ISGID, S_ISUID, S_ISVTX, S_IWGRP, S_IWOTH, S_IWUSR, S_IXGRP, S_IXOTH, S_IXUSR,
mode_t, mkfifo, mode_t,
}; };
use std::collections::HashSet; use std::collections::HashSet;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::env; use std::env;
#[cfg(unix)]
use std::ffi::CString;
use std::ffi::{OsStr, OsString}; use std::ffi::{OsStr, OsString};
use std::fs; use std::fs;
use std::fs::read_dir; use std::fs::read_dir;
@ -798,6 +800,37 @@ pub fn get_filename(file: &Path) -> Option<&str> {
file.file_name().and_then(|filename| filename.to_str()) file.file_name().and_then(|filename| filename.to_str())
} }
/// Make a FIFO, also known as a named pipe.
///
/// This is a safe wrapper for the unsafe [`libc::mkfifo`] function,
/// which makes a [named
/// pipe](https://en.wikipedia.org/wiki/Named_pipe) on Unix systems.
///
/// # Errors
///
/// If the named pipe cannot be created.
///
/// # Examples
///
/// ```ignore
/// use uucore::fs::make_fifo;
///
/// make_fifo("my-pipe").expect("failed to create the named pipe");
///
/// std::thread::spawn(|| { std::fs::write("my-pipe", b"hello").unwrap(); });
/// assert_eq!(std::fs::read("my-pipe").unwrap(), b"hello");
/// ```
#[cfg(unix)]
pub fn make_fifo(path: &Path) -> std::io::Result<()> {
let name = CString::new(path.to_str().unwrap()).unwrap();
let err = unsafe { mkfifo(name.as_ptr(), 0o666) };
if err == -1 {
Err(std::io::Error::from_raw_os_error(err))
} else {
Ok(())
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
// Note this useful idiom: importing names from outer (for mod tests) scope. // Note this useful idiom: importing names from outer (for mod tests) scope.
@ -807,6 +840,8 @@ mod tests {
#[cfg(unix)] #[cfg(unix)]
use std::os::unix; use std::os::unix;
#[cfg(unix)] #[cfg(unix)]
use std::os::unix::fs::FileTypeExt;
#[cfg(unix)]
use tempfile::{NamedTempFile, tempdir}; use tempfile::{NamedTempFile, tempdir};
struct NormalizePathTestCase<'a> { struct NormalizePathTestCase<'a> {
@ -1039,4 +1074,25 @@ mod tests {
let file_path = PathBuf::from("~/foo.txt"); let file_path = PathBuf::from("~/foo.txt");
assert!(matches!(get_filename(&file_path), Some("foo.txt"))); assert!(matches!(get_filename(&file_path), Some("foo.txt")));
} }
#[cfg(unix)]
#[test]
fn test_make_fifo() {
// Create the FIFO in a temporary directory.
let tempdir = tempdir().unwrap();
let path = tempdir.path().join("f");
assert!(make_fifo(&path).is_ok());
// Check that it is indeed a FIFO.
assert!(std::fs::metadata(&path).unwrap().file_type().is_fifo());
// Check that we can write to it and read from it.
//
// Write and read need to happen in different threads,
// otherwise `write` would block indefinitely while waiting
// for the `read`.
let path2 = path.clone();
std::thread::spawn(move || assert!(std::fs::write(&path2, b"foo").is_ok()));
assert_eq!(std::fs::read(&path).unwrap(), b"foo");
}
} }