Skip to content

Commit 66d4d0d

Browse files
cp: fix device file creation with -r flag
Fix cp -r to create device files instead of copying content when copying character and block devices. Add device detection for character and block devices similar to existing FIFO handling. Implement make_char_device() and make_block_device() functions in uucore using mknod syscall to create device files with correct major/minor numbers. Update copy logic to create device files when --recursive is used and --copy-contents is not specified. Fixes #8679
1 parent 0ccc67f commit 66d4d0d

File tree

4 files changed

+118
-4
lines changed

4 files changed

+118
-4
lines changed

src/uu/cp/locales/en-US.ftl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ cp-error-selinux-set-context = failed to set the security context of { $path }:
8686
cp-error-selinux-get-context = failed to get security context of { $path }
8787
cp-error-selinux-error = SELinux error: { $error }
8888
cp-error-cannot-create-fifo = cannot create fifo { $path }: File exists
89+
cp-error-cannot-create-char-device = cannot create character device { $path }
90+
cp-error-cannot-create-block-device = cannot create block device { $path }
8991
cp-error-invalid-attribute = invalid attribute { $value }
9092
cp-error-failed-to-create-whole-tree = failed to create whole tree
9193
cp-error-failed-to-create-directory = Failed to create directory: { $error }

src/uu/cp/locales/fr-FR.ftl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ cp-error-selinux-set-context = échec de la définition du contexte de sécurit
8686
cp-error-selinux-get-context = échec de l'obtention du contexte de sécurité de { $path }
8787
cp-error-selinux-error = Erreur SELinux : { $error }
8888
cp-error-cannot-create-fifo = impossible de créer le fifo { $path } : Le fichier existe
89+
cp-error-cannot-create-char-device = impossible de créer le périphérique caractère { $path }
90+
cp-error-cannot-create-block-device = impossible de créer le périphérique bloc { $path }
8991
cp-error-invalid-attribute = attribut invalide { $value }
9092
cp-error-failed-to-create-whole-tree = échec de la création de l'arborescence complète
9193
cp-error-failed-to-create-directory = Échec de la création du répertoire : { $error }

src/uu/cp/src/cp.rs

Lines changed: 77 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use std::ffi::OsString;
1010
use std::fmt::Display;
1111
use std::fs::{self, Metadata, OpenOptions, Permissions};
1212
#[cfg(unix)]
13-
use std::os::unix::fs::{FileTypeExt, PermissionsExt};
13+
use std::os::unix::fs::{FileTypeExt, MetadataExt, PermissionsExt};
1414
#[cfg(unix)]
1515
use std::os::unix::net::UnixListener;
1616
use std::path::{Path, PathBuf, StripPrefixError};
@@ -27,13 +27,13 @@ use thiserror::Error;
2727
use platform::copy_on_write;
2828
use uucore::display::Quotable;
2929
use uucore::error::{UError, UResult, UUsageError, set_exit_code};
30-
#[cfg(unix)]
31-
use uucore::fs::make_fifo;
3230
use uucore::fs::{
3331
FileInformation, MissingHandling, ResolveMode, are_hardlinks_to_same_file, canonicalize,
3432
get_filename, is_symlink_loop, normalize_path, path_ends_with_terminator,
3533
paths_refer_to_same_file,
3634
};
35+
#[cfg(unix)]
36+
use uucore::fs::{make_block_device, make_char_device, make_fifo};
3737
use uucore::{backup_control, update_control};
3838
// These are exposed for projects (e.g. nushell) that want to create an `Options` value, which
3939
// requires these enum.
@@ -2082,6 +2082,8 @@ fn handle_copy_mode(
20822082
source_in_command_line: bool,
20832083
source_is_fifo: bool,
20842084
source_is_socket: bool,
2085+
source_is_char_device: bool,
2086+
source_is_block_device: bool,
20852087
#[cfg(unix)] source_is_stream: bool,
20862088
) -> CopyResult<PerformedAction> {
20872089
let source_is_symlink = source_metadata.is_symlink();
@@ -2122,6 +2124,8 @@ fn handle_copy_mode(
21222124
source_is_symlink,
21232125
source_is_fifo,
21242126
source_is_socket,
2127+
source_is_char_device,
2128+
source_is_block_device,
21252129
symlinked_files,
21262130
#[cfg(unix)]
21272131
source_is_stream,
@@ -2145,6 +2149,8 @@ fn handle_copy_mode(
21452149
source_is_symlink,
21462150
source_is_fifo,
21472151
source_is_socket,
2152+
source_is_char_device,
2153+
source_is_block_device,
21482154
symlinked_files,
21492155
#[cfg(unix)]
21502156
source_is_stream,
@@ -2181,6 +2187,8 @@ fn handle_copy_mode(
21812187
source_is_symlink,
21822188
source_is_fifo,
21832189
source_is_socket,
2190+
source_is_char_device,
2191+
source_is_block_device,
21842192
symlinked_files,
21852193
#[cfg(unix)]
21862194
source_is_stream,
@@ -2196,6 +2204,8 @@ fn handle_copy_mode(
21962204
source_is_symlink,
21972205
source_is_fifo,
21982206
source_is_socket,
2207+
source_is_char_device,
2208+
source_is_block_device,
21992209
symlinked_files,
22002210
#[cfg(unix)]
22012211
source_is_stream,
@@ -2424,10 +2434,18 @@ fn copy_file(
24242434
let source_is_fifo = source_metadata.file_type().is_fifo();
24252435
#[cfg(unix)]
24262436
let source_is_socket = source_metadata.file_type().is_socket();
2437+
#[cfg(unix)]
2438+
let source_is_char_device = source_metadata.file_type().is_char_device();
2439+
#[cfg(unix)]
2440+
let source_is_block_device = source_metadata.file_type().is_block_device();
24272441
#[cfg(not(unix))]
24282442
let source_is_fifo = false;
24292443
#[cfg(not(unix))]
24302444
let source_is_socket = false;
2445+
#[cfg(not(unix))]
2446+
let source_is_char_device = false;
2447+
#[cfg(not(unix))]
2448+
let source_is_block_device = false;
24312449

24322450
let source_is_stream = is_stream(&source_metadata);
24332451

@@ -2441,6 +2459,8 @@ fn copy_file(
24412459
source_in_command_line,
24422460
source_is_fifo,
24432461
source_is_socket,
2462+
source_is_char_device,
2463+
source_is_block_device,
24442464
#[cfg(unix)]
24452465
source_is_stream,
24462466
)?;
@@ -2568,6 +2588,8 @@ fn copy_helper(
25682588
source_is_symlink: bool,
25692589
source_is_fifo: bool,
25702590
source_is_socket: bool,
2591+
source_is_char_device: bool,
2592+
source_is_block_device: bool,
25712593
symlinked_files: &mut HashSet<FileInformation>,
25722594
#[cfg(unix)] source_is_stream: bool,
25732595
) -> CopyResult<()> {
@@ -2586,6 +2608,12 @@ fn copy_helper(
25862608
} else if source_is_fifo && options.recursive && !options.copy_contents {
25872609
#[cfg(unix)]
25882610
copy_fifo(dest, options.overwrite, options.debug)?;
2611+
} else if source_is_char_device && options.recursive && !options.copy_contents {
2612+
#[cfg(unix)]
2613+
copy_char_device(source, dest, options.overwrite, options.debug)?;
2614+
} else if source_is_block_device && options.recursive && !options.copy_contents {
2615+
#[cfg(unix)]
2616+
copy_block_device(source, dest, options.overwrite, options.debug)?;
25892617
} else if source_is_symlink {
25902618
copy_link(source, dest, symlinked_files, options)?;
25912619
} else {
@@ -2620,6 +2648,52 @@ fn copy_fifo(dest: &Path, overwrite: OverwriteMode, debug: bool) -> CopyResult<(
26202648
.map_err(|_| translate!("cp-error-cannot-create-fifo", "path" => dest.quote()).into())
26212649
}
26222650

2651+
// "Copies" a character device by creating a new one with the same major/minor numbers.
2652+
#[cfg(unix)]
2653+
fn copy_char_device(
2654+
source: &Path,
2655+
dest: &Path,
2656+
overwrite: OverwriteMode,
2657+
debug: bool,
2658+
) -> CopyResult<()> {
2659+
if dest.exists() {
2660+
overwrite.verify(dest, debug)?;
2661+
fs::remove_file(dest)?;
2662+
}
2663+
2664+
let source_metadata = fs::metadata(source)?;
2665+
let device_id = source_metadata.rdev();
2666+
let major = ((device_id >> 8) & 0xff) as u32;
2667+
let minor = (device_id & 0xff) as u32;
2668+
2669+
make_char_device(dest, major, minor).map_err(|_| {
2670+
translate!("cp-error-cannot-create-char-device", "path" => dest.quote()).into()
2671+
})
2672+
}
2673+
2674+
// "Copies" a block device by creating a new one with the same major/minor numbers.
2675+
#[cfg(unix)]
2676+
fn copy_block_device(
2677+
source: &Path,
2678+
dest: &Path,
2679+
overwrite: OverwriteMode,
2680+
debug: bool,
2681+
) -> CopyResult<()> {
2682+
if dest.exists() {
2683+
overwrite.verify(dest, debug)?;
2684+
fs::remove_file(dest)?;
2685+
}
2686+
2687+
let source_metadata = fs::metadata(source)?;
2688+
let device_id = source_metadata.rdev();
2689+
let major = ((device_id >> 8) & 0xff) as u32;
2690+
let minor = (device_id & 0xff) as u32;
2691+
2692+
make_block_device(dest, major, minor).map_err(|_| {
2693+
translate!("cp-error-cannot-create-block-device", "path" => dest.quote()).into()
2694+
})
2695+
}
2696+
26232697
#[cfg(unix)]
26242698
fn copy_socket(dest: &Path, overwrite: OverwriteMode, debug: bool) -> CopyResult<()> {
26252699
if dest.exists() {

src/uucore/src/lib/features/fs.rs

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
use libc::{
1212
S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO, S_IFLNK, S_IFMT, S_IFREG, S_IFSOCK, S_IRGRP, S_IROTH,
1313
S_IRUSR, S_ISGID, S_ISUID, S_ISVTX, S_IWGRP, S_IWOTH, S_IWUSR, S_IXGRP, S_IXOTH, S_IXUSR,
14-
mkfifo, mode_t,
14+
makedev, mkfifo, mknod, mode_t,
1515
};
1616
use std::collections::HashSet;
1717
use std::collections::VecDeque;
@@ -837,6 +837,42 @@ pub fn make_fifo(path: &Path) -> std::io::Result<()> {
837837
}
838838
}
839839

840+
/// Create a character device file.
841+
///
842+
/// # Arguments
843+
/// * `path` - The path where the device file should be created
844+
/// * `major` - The major device number
845+
/// * `minor` - The minor device number
846+
#[cfg(unix)]
847+
pub fn make_char_device(path: &Path, major: u32, minor: u32) -> std::io::Result<()> {
848+
let name = CString::new(path.to_str().unwrap()).unwrap();
849+
let dev = makedev(major, minor);
850+
let err = unsafe { mknod(name.as_ptr(), S_IFCHR | 0o666, dev) };
851+
if err == -1 {
852+
Err(std::io::Error::last_os_error())
853+
} else {
854+
Ok(())
855+
}
856+
}
857+
858+
/// Create a block device file.
859+
///
860+
/// # Arguments
861+
/// * `path` - The path where the device file should be created
862+
/// * `major` - The major device number
863+
/// * `minor` - The minor device number
864+
#[cfg(unix)]
865+
pub fn make_block_device(path: &Path, major: u32, minor: u32) -> std::io::Result<()> {
866+
let name = CString::new(path.to_str().unwrap()).unwrap();
867+
let dev = makedev(major, minor);
868+
let err = unsafe { mknod(name.as_ptr(), S_IFBLK | 0o666, dev) };
869+
if err == -1 {
870+
Err(std::io::Error::last_os_error())
871+
} else {
872+
Ok(())
873+
}
874+
}
875+
840876
#[cfg(test)]
841877
mod tests {
842878
// Note this useful idiom: importing names from outer (for mod tests) scope.

0 commit comments

Comments
 (0)