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

Merge pull request #3697 from jfinkels/uucore-is-symlink

uucore: add backport for Path::is_symlink()
This commit is contained in:
Sylvestre Ledru 2022-07-05 11:08:17 +02:00 committed by GitHub
commit 666fc298dd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 27 additions and 47 deletions

View file

@ -14,6 +14,7 @@ use std::path::Path;
use uucore::display::Quotable; use uucore::display::Quotable;
use uucore::error::{ExitCode, UResult, USimpleError, UUsageError}; use uucore::error::{ExitCode, UResult, USimpleError, UUsageError};
use uucore::fs::display_permissions_unix; use uucore::fs::display_permissions_unix;
use uucore::fs::is_symlink;
use uucore::libc::mode_t; use uucore::libc::mode_t;
#[cfg(not(windows))] #[cfg(not(windows))]
use uucore::mode; use uucore::mode;
@ -380,10 +381,3 @@ impl Chmoder {
} }
} }
} }
pub fn is_symlink<P: AsRef<Path>>(path: P) -> bool {
match fs::symlink_metadata(path) {
Ok(m) => m.file_type().is_symlink(),
Err(_) => false,
}
}

View file

@ -56,7 +56,7 @@ use std::str::FromStr;
use std::string::ToString; use std::string::ToString;
use uucore::backup_control::{self, BackupMode}; use uucore::backup_control::{self, BackupMode};
use uucore::error::{set_exit_code, ExitCode, UClapError, UError, UResult}; use uucore::error::{set_exit_code, ExitCode, UClapError, UError, UResult};
use uucore::fs::{canonicalize, MissingHandling, ResolveMode}; use uucore::fs::{canonicalize, is_symlink, MissingHandling, ResolveMode};
use walkdir::WalkDir; use walkdir::WalkDir;
quick_error! { quick_error! {
@ -999,9 +999,7 @@ fn copy_directory(
} }
// if no-dereference is enabled and this is a symlink, copy it as a file // if no-dereference is enabled and this is a symlink, copy it as a file
if !options.dereference && fs::symlink_metadata(root).unwrap().file_type().is_symlink() if !options.dereference && is_symlink(root) {
// replace by is_symlink in rust>=1.58
{
return copy_file(root, target, options, symlinked_files); return copy_file(root, target, options, symlinked_files);
} }
@ -1044,8 +1042,6 @@ fn copy_directory(
.follow_links(options.dereference) .follow_links(options.dereference)
{ {
let p = or_continue!(path); let p = or_continue!(path);
let is_symlink = fs::symlink_metadata(p.path())?.file_type().is_symlink();
// replace by is_symlink in rust >=1.58
let path = current_dir.join(&p.path()); let path = current_dir.join(&p.path());
let local_to_root_parent = match root_parent { let local_to_root_parent = match root_parent {
@ -1069,7 +1065,7 @@ fn copy_directory(
}; };
let local_to_target = target.join(&local_to_root_parent); let local_to_target = target.join(&local_to_root_parent);
if is_symlink && !options.dereference { if is_symlink(p.path()) && !options.dereference {
copy_link(&path, &local_to_target, symlinked_files)?; copy_link(&path, &local_to_target, symlinked_files)?;
} else if path.is_dir() && !local_to_target.exists() { } else if path.is_dir() && !local_to_target.exists() {
if target.is_file() { if target.is_file() {
@ -1091,7 +1087,7 @@ fn copy_directory(
) { ) {
Ok(_) => Ok(()), Ok(_) => Ok(()),
Err(err) => { Err(err) => {
if fs::symlink_metadata(&source)?.file_type().is_symlink() { if is_symlink(source) {
// silent the error with a symlink // silent the error with a symlink
// In case we do --archive, we might copy the symlink // In case we do --archive, we might copy the symlink
// before the file itself // before the file itself
@ -1306,10 +1302,7 @@ fn copy_file(
} }
// Fail if dest is a dangling symlink or a symlink this program created previously // Fail if dest is a dangling symlink or a symlink this program created previously
if fs::symlink_metadata(dest) if is_symlink(dest) {
.map(|m| m.file_type().is_symlink()) // replace by is_symlink in rust>=1.58
.unwrap_or(false)
{
if FileInformation::from_path(dest, false) if FileInformation::from_path(dest, false)
.map(|info| symlinked_files.contains(&info)) .map(|info| symlinked_files.contains(&info))
.unwrap_or(false) .unwrap_or(false)
@ -1351,9 +1344,7 @@ fn copy_file(
#[cfg(not(unix))] #[cfg(not(unix))]
let source_is_fifo = false; let source_is_fifo = false;
let dest_already_exists_as_symlink = fs::symlink_metadata(&dest) let dest_already_exists_as_symlink = is_symlink(dest);
.map(|meta| meta.file_type().is_symlink())
.unwrap_or(false);
let dest = if !(source_is_symlink && dest_already_exists_as_symlink) { let dest = if !(source_is_symlink && dest_already_exists_as_symlink) {
canonicalize(dest, MissingHandling::Missing, ResolveMode::Physical).unwrap() canonicalize(dest, MissingHandling::Missing, ResolveMode::Physical).unwrap()
@ -1457,10 +1448,7 @@ fn copy_file(
}; };
// TODO: implement something similar to gnu's lchown // TODO: implement something similar to gnu's lchown
if fs::symlink_metadata(&dest) if !is_symlink(&dest) {
.map(|meta| !meta.file_type().is_symlink())
.unwrap_or(false)
{
// Here, to match GNU semantics, we quietly ignore an error // Here, to match GNU semantics, we quietly ignore an error
// if a user does not have the correct ownership to modify // if a user does not have the correct ownership to modify
// the permissions of a file. // the permissions of a file.

View file

@ -14,6 +14,7 @@ use clap::{crate_version, Arg, Command};
use uucore::display::Quotable; use uucore::display::Quotable;
use uucore::error::{UError, UResult}; use uucore::error::{UError, UResult};
use uucore::format_usage; use uucore::format_usage;
use uucore::fs::is_symlink;
use std::borrow::Cow; use std::borrow::Cow;
use std::error::Error; use std::error::Error;
@ -533,10 +534,3 @@ pub fn symlink<P1: AsRef<Path>, P2: AsRef<Path>>(src: P1, dst: P2) -> Result<()>
symlink_file(src, dst) symlink_file(src, dst)
} }
} }
pub fn is_symlink<P: AsRef<Path>>(path: P) -> bool {
match fs::symlink_metadata(path) {
Ok(m) => m.file_type().is_symlink(),
Err(_) => false,
}
}

View file

@ -16,6 +16,9 @@ use std::io;
use std::path::Path; use std::path::Path;
use uucore::display::Quotable; use uucore::display::Quotable;
use uucore::error::{set_exit_code, strip_errno, UResult}; use uucore::error::{set_exit_code, strip_errno, UResult};
#[cfg(unix)]
use uucore::fs::is_symlink;
use uucore::{format_usage, util_name}; use uucore::{format_usage, util_name};
static ABOUT: &str = "Remove the DIRECTORY(ies), if they are empty."; static ABOUT: &str = "Remove the DIRECTORY(ies), if they are empty.";
@ -65,10 +68,6 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
use std::ffi::OsStr; use std::ffi::OsStr;
use std::os::unix::ffi::OsStrExt; use std::os::unix::ffi::OsStrExt;
fn is_symlink(path: &Path) -> io::Result<bool> {
Ok(path.symlink_metadata()?.file_type().is_symlink())
}
fn points_to_directory(path: &Path) -> io::Result<bool> { fn points_to_directory(path: &Path) -> io::Result<bool> {
Ok(path.metadata()?.file_type().is_dir()) Ok(path.metadata()?.file_type().is_dir())
} }
@ -77,9 +76,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
if error.raw_os_error() == Some(libc::ENOTDIR) && bytes.ends_with(b"/") { if error.raw_os_error() == Some(libc::ENOTDIR) && bytes.ends_with(b"/") {
// Strip the trailing slash or .symlink_metadata() will follow the symlink // Strip the trailing slash or .symlink_metadata() will follow the symlink
let no_slash: &Path = OsStr::from_bytes(&bytes[..bytes.len() - 1]).as_ref(); let no_slash: &Path = OsStr::from_bytes(&bytes[..bytes.len() - 1]).as_ref();
if is_symlink(no_slash).unwrap_or(false) if is_symlink(no_slash) && points_to_directory(no_slash).unwrap_or(true) {
&& points_to_directory(no_slash).unwrap_or(true)
{
show_error!( show_error!(
"failed to remove {}: Symbolic link not followed", "failed to remove {}: Symbolic link not followed",
path.quote() path.quote()

View file

@ -8,6 +8,8 @@
//! Set of functions to manage files and symlinks //! Set of functions to manage files and symlinks
// spell-checker:ignore backport
#[cfg(unix)] #[cfg(unix)]
use libc::{ use libc::{
mode_t, S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO, S_IFLNK, S_IFMT, S_IFREG, S_IFSOCK, S_IRGRP, mode_t, S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO, S_IFLNK, S_IFMT, S_IFREG, S_IFSOCK, S_IRGRP,
@ -221,6 +223,16 @@ pub fn normalize_path(path: &Path) -> PathBuf {
ret ret
} }
/// Decide whether the given path is a symbolic link.
///
/// This function is essentially a backport of the
/// [`std::path::Path::is_symlink`] function that exists in Rust
/// version 1.58 and greater. This can be removed when the minimum
/// supported version of Rust is 1.58.
pub fn is_symlink<P: AsRef<Path>>(path: P) -> bool {
fs::symlink_metadata(path).map_or(false, |m| m.file_type().is_symlink())
}
fn resolve<P: AsRef<Path>>(original: P) -> Result<PathBuf, (bool, PathBuf, IOError)> { fn resolve<P: AsRef<Path>>(original: P) -> Result<PathBuf, (bool, PathBuf, IOError)> {
const MAX_LINKS_FOLLOWED: u32 = 255; const MAX_LINKS_FOLLOWED: u32 = 255;
let mut followed = 0; let mut followed = 0;

View file

@ -10,7 +10,7 @@ use crate::error::strip_errno;
use crate::error::UResult; use crate::error::UResult;
use crate::error::USimpleError; use crate::error::USimpleError;
pub use crate::features::entries; pub use crate::features::entries;
use crate::fs::resolve_relative_path; use crate::fs::{is_symlink, resolve_relative_path};
use crate::show_error; use crate::show_error;
use clap::Arg; use clap::Arg;
use clap::ArgMatches; use clap::ArgMatches;
@ -274,12 +274,7 @@ impl ChownExecutor {
let root = root.as_ref(); let root = root.as_ref();
// walkdir always dereferences the root directory, so we have to check it ourselves // walkdir always dereferences the root directory, so we have to check it ourselves
// TODO: replace with `root.is_symlink()` once it is stable if self.traverse_symlinks == TraverseSymlinks::None && is_symlink(root) {
if self.traverse_symlinks == TraverseSymlinks::None
&& std::fs::symlink_metadata(root)
.map(|m| m.file_type().is_symlink())
.unwrap_or(false)
{
return 0; return 0;
} }