Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

uucore: add backport for Path::is_symlink() #3697

Merged
merged 3 commits into from
Jul 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 1 addition & 7 deletions src/uu/chmod/src/chmod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use std::path::Path;
use uucore::display::Quotable;
use uucore::error::{ExitCode, UResult, USimpleError, UUsageError};
use uucore::fs::display_permissions_unix;
use uucore::fs::is_symlink;
use uucore::libc::mode_t;
#[cfg(not(windows))]
use uucore::mode;
Expand Down Expand Up @@ -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,
}
}
26 changes: 7 additions & 19 deletions src/uu/cp/src/cp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ use std::str::FromStr;
use std::string::ToString;
use uucore::backup_control::{self, BackupMode};
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;

quick_error! {
Expand Down Expand Up @@ -999,9 +999,7 @@ fn copy_directory(
}

// 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()
// replace by is_symlink in rust>=1.58
{
if !options.dereference && is_symlink(root) {
return copy_file(root, target, options, symlinked_files);
}

Expand Down Expand Up @@ -1044,8 +1042,6 @@ fn copy_directory(
.follow_links(options.dereference)
{
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 local_to_root_parent = match root_parent {
Expand All @@ -1069,7 +1065,7 @@ fn copy_directory(
};

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)?;
} else if path.is_dir() && !local_to_target.exists() {
if target.is_file() {
Expand All @@ -1091,7 +1087,7 @@ fn copy_directory(
) {
Ok(_) => Ok(()),
Err(err) => {
if fs::symlink_metadata(&source)?.file_type().is_symlink() {
if is_symlink(source) {
// silent the error with a symlink
// In case we do --archive, we might copy the symlink
// before the file itself
Expand Down Expand Up @@ -1306,10 +1302,7 @@ fn copy_file(
}

// Fail if dest is a dangling symlink or a symlink this program created previously
if fs::symlink_metadata(dest)
.map(|m| m.file_type().is_symlink()) // replace by is_symlink in rust>=1.58
.unwrap_or(false)
{
if is_symlink(dest) {
if FileInformation::from_path(dest, false)
.map(|info| symlinked_files.contains(&info))
.unwrap_or(false)
Expand Down Expand Up @@ -1351,9 +1344,7 @@ fn copy_file(
#[cfg(not(unix))]
let source_is_fifo = false;

let dest_already_exists_as_symlink = fs::symlink_metadata(&dest)
.map(|meta| meta.file_type().is_symlink())
.unwrap_or(false);
let dest_already_exists_as_symlink = is_symlink(dest);

let dest = if !(source_is_symlink && dest_already_exists_as_symlink) {
canonicalize(dest, MissingHandling::Missing, ResolveMode::Physical).unwrap()
Expand Down Expand Up @@ -1457,10 +1448,7 @@ fn copy_file(
};

// TODO: implement something similar to gnu's lchown
if fs::symlink_metadata(&dest)
.map(|meta| !meta.file_type().is_symlink())
.unwrap_or(false)
{
if !is_symlink(&dest) {
// Here, to match GNU semantics, we quietly ignore an error
// if a user does not have the correct ownership to modify
// the permissions of a file.
Expand Down
8 changes: 1 addition & 7 deletions src/uu/ln/src/ln.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use clap::{crate_version, Arg, Command};
use uucore::display::Quotable;
use uucore::error::{UError, UResult};
use uucore::format_usage;
use uucore::fs::is_symlink;

use std::borrow::Cow;
use std::error::Error;
Expand Down Expand Up @@ -533,10 +534,3 @@ pub fn symlink<P1: AsRef<Path>, P2: AsRef<Path>>(src: P1, dst: P2) -> Result<()>
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,
}
}
11 changes: 4 additions & 7 deletions src/uu/rmdir/src/rmdir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ use std::io;
use std::path::Path;
use uucore::display::Quotable;
use uucore::error::{set_exit_code, strip_errno, UResult};

#[cfg(unix)]
use uucore::fs::is_symlink;
use uucore::{format_usage, util_name};

static ABOUT: &str = "Remove the DIRECTORY(ies), if they are empty.";
Expand Down Expand Up @@ -65,10 +68,6 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
use std::ffi::OsStr;
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> {
Ok(path.metadata()?.file_type().is_dir())
}
Expand All @@ -77,9 +76,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
if error.raw_os_error() == Some(libc::ENOTDIR) && bytes.ends_with(b"/") {
// Strip the trailing slash or .symlink_metadata() will follow the symlink
let no_slash: &Path = OsStr::from_bytes(&bytes[..bytes.len() - 1]).as_ref();
if is_symlink(no_slash).unwrap_or(false)
&& points_to_directory(no_slash).unwrap_or(true)
{
if is_symlink(no_slash) && points_to_directory(no_slash).unwrap_or(true) {
show_error!(
"failed to remove {}: Symbolic link not followed",
path.quote()
Expand Down
12 changes: 12 additions & 0 deletions src/uucore/src/lib/features/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

//! Set of functions to manage files and symlinks

// spell-checker:ignore backport

#[cfg(unix)]
use libc::{
mode_t, S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO, S_IFLNK, S_IFMT, S_IFREG, S_IFSOCK, S_IRGRP,
Expand Down Expand Up @@ -221,6 +223,16 @@ pub fn normalize_path(path: &Path) -> PathBuf {
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)> {
const MAX_LINKS_FOLLOWED: u32 = 255;
let mut followed = 0;
Expand Down
9 changes: 2 additions & 7 deletions src/uucore/src/lib/features/perms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::error::strip_errno;
use crate::error::UResult;
use crate::error::USimpleError;
pub use crate::features::entries;
use crate::fs::resolve_relative_path;
use crate::fs::{is_symlink, resolve_relative_path};
use crate::show_error;
use clap::Arg;
use clap::ArgMatches;
Expand Down Expand Up @@ -274,12 +274,7 @@ impl ChownExecutor {
let root = root.as_ref();

// 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
&& std::fs::symlink_metadata(root)
.map(|m| m.file_type().is_symlink())
.unwrap_or(false)
{
if self.traverse_symlinks == TraverseSymlinks::None && is_symlink(root) {
return 0;
}

Expand Down