Skip to content
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
13 changes: 6 additions & 7 deletions src/uu/chmod/src/chmod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ use thiserror::Error;
use uucore::display::Quotable;
use uucore::error::{ExitCode, UError, UResult, USimpleError, UUsageError, set_exit_code};
use uucore::fs::display_permissions_unix;
use uucore::libc::mode_t;
use uucore::mode;
use uucore::perms::{TraverseSymlinks, configure_symlink_and_recursion};

Expand Down Expand Up @@ -313,8 +312,8 @@ impl Chmoder {
/// Report permission changes based on verbose and changes flags
fn report_permission_change(&self, file_path: &Path, old_mode: u32, new_mode: u32) {
if self.verbose || self.changes {
let current_permissions = display_permissions_unix(old_mode as mode_t, false);
let new_permissions = display_permissions_unix(new_mode as mode_t, false);
let current_permissions = display_permissions_unix(old_mode, false);
let new_permissions = display_permissions_unix(new_mode, false);

if new_mode != old_mode {
println!(
Expand Down Expand Up @@ -668,8 +667,8 @@ impl Chmoder {
if (new_mode & !naively_expected_new_mode) != 0 {
return Err(ChmodError::NewPermissions(
file.into(),
display_permissions_unix(new_mode as mode_t, false),
display_permissions_unix(naively_expected_new_mode as mode_t, false),
display_permissions_unix(new_mode, false),
display_permissions_unix(naively_expected_new_mode, false),
)
.into());
}
Expand All @@ -691,8 +690,8 @@ impl Chmoder {
println!(
"failed to change mode of file {} from {fperm:04o} ({}) to {mode:04o} ({})",
file.quote(),
display_permissions_unix(fperm as mode_t, false),
display_permissions_unix(mode as mode_t, false)
display_permissions_unix(fperm, false),
display_permissions_unix(mode, false)
);
}
Err(1)
Expand Down
2 changes: 1 addition & 1 deletion src/uu/cp/src/cp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1620,7 +1620,7 @@ fn file_mode_for_interactive_overwrite(

Some((
format!("{mode_without_leading_digits:04o}"),
uucore::fs::display_permissions_unix(mode, false),
uucore::fs::display_permissions_unix(mode as u32, false),
))
}
}
Expand Down
3 changes: 1 addition & 2 deletions src/uu/mv/src/mv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1255,14 +1255,13 @@ fn is_writable(path: &Path) -> (bool, Option<u32>) {

#[cfg(unix)]
fn get_interactive_prompt(to: &Path, cached_mode: Option<u32>) -> String {
use libc::mode_t;
// Use cached mode if available, otherwise fetch it
let mode = cached_mode.or_else(|| to.metadata().ok().map(|m| m.permissions().mode()));
if let Some(mode) = mode {
let file_mode = mode & 0o777;
// Check if file is not writable by user
if (mode & 0o200) == 0 {
let perms = display_permissions_unix(mode as mode_t, false);
let perms = display_permissions_unix(mode, false);
let mode_info = format!("{file_mode:04o} ({perms})");
return translate!("mv-prompt-overwrite-mode", "target" => to.quote(), "mode_info" => mode_info);
}
Expand Down
106 changes: 69 additions & 37 deletions src/uucore/src/lib/features/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,7 @@
// spell-checker:ignore backport

#[cfg(unix)]
use libc::{
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,
mkfifo, mode_t,
};
use libc::mkfifo;
#[cfg(all(unix, not(target_os = "redox")))]
pub use libc::{major, makedev, minor};
use std::collections::HashSet;
Expand Down Expand Up @@ -464,12 +460,44 @@ pub fn display_permissions(metadata: &fs::Metadata, display_file_type: bool) ->
#[cfg(unix)]
/// Display the permissions of a file
pub fn display_permissions(metadata: &fs::Metadata, display_file_type: bool) -> String {
let mode: mode_t = metadata.mode() as mode_t;
display_permissions_unix(mode, display_file_type)
display_permissions_unix(metadata.mode(), display_file_type)
}

/// Portable file mode bit constants, equivalent to the POSIX `S_I*` values.
///
/// These are defined here as plain `u32` so they are available on every
/// platform, including Windows, without requiring `libc` or a Unix target.
/// Callers that previously used `libc::S_IFDIR` etc. can import these instead.
pub mod mode {
// File-type mask and values
pub const S_IFMT: u32 = 0o170_000; // bitmask for the file-type field
pub const S_IFSOCK: u32 = 0o140_000; // socket
pub const S_IFLNK: u32 = 0o120_000; // symbolic link
pub const S_IFREG: u32 = 0o100_000; // regular file
pub const S_IFBLK: u32 = 0o060_000; // block device
pub const S_IFDIR: u32 = 0o040_000; // directory
pub const S_IFCHR: u32 = 0o020_000; // character device
pub const S_IFIFO: u32 = 0o010_000; // named pipe (FIFO)

// Permission and special-mode bits
pub const S_ISUID: u32 = 0o4000; // setuid
pub const S_ISGID: u32 = 0o2000; // setgid
pub const S_ISVTX: u32 = 0o1000; // sticky

pub const S_IRUSR: u32 = 0o0400; // owner read
pub const S_IWUSR: u32 = 0o0200; // owner write
pub const S_IXUSR: u32 = 0o0100; // owner execute

pub const S_IRGRP: u32 = 0o0040; // group read
pub const S_IWGRP: u32 = 0o0020; // group write
pub const S_IXGRP: u32 = 0o0010; // group execute

pub const S_IROTH: u32 = 0o0004; // other read
pub const S_IWOTH: u32 = 0o0002; // other write
pub const S_IXOTH: u32 = 0o0001; // other execute
}

/// Returns a character representation of the file type based on its mode.
/// This function is specific to Unix-like systems.
///
/// - `mode`: The mode of the file, typically obtained from file metadata.
///
Expand All @@ -482,8 +510,8 @@ pub fn display_permissions(metadata: &fs::Metadata, display_file_type: bool) ->
/// - 'l' for symbolic links
/// - 's' for sockets
/// - '?' for any other unrecognized file types
#[cfg(unix)]
fn get_file_display(mode: mode_t) -> char {
fn get_file_display(mode: u32) -> char {
use mode::{S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO, S_IFLNK, S_IFMT, S_IFREG, S_IFSOCK};
match mode & S_IFMT {
S_IFDIR => 'd',
S_IFCHR => 'c',
Expand All @@ -500,9 +528,12 @@ fn get_file_display(mode: mode_t) -> char {
// The logic below is more readable written this way.
#[allow(clippy::if_not_else)]
#[allow(clippy::cognitive_complexity)]
#[cfg(unix)]
/// Display the permissions of a file on a unix like system
pub fn display_permissions_unix(mode: mode_t, display_file_type: bool) -> String {
/// Display the unix permissions of a file
pub fn display_permissions_unix(mode: u32, display_file_type: bool) -> String {
use mode::{
S_IRGRP, S_IROTH, S_IRUSR, S_ISGID, S_ISUID, S_ISVTX, S_IWGRP, S_IWOTH, S_IWUSR, S_IXGRP,
S_IXOTH, S_IXUSR,
};
let mut result;
if display_file_type {
result = String::with_capacity(10);
Expand All @@ -511,31 +542,31 @@ pub fn display_permissions_unix(mode: mode_t, display_file_type: bool) -> String
result = String::with_capacity(9);
}

result.push(if has!(mode, S_IRUSR) { 'r' } else { '-' });
result.push(if has!(mode, S_IWUSR) { 'w' } else { '-' });
result.push(if has!(mode, S_ISUID as mode_t) {
if has!(mode, S_IXUSR) { 's' } else { 'S' }
} else if has!(mode, S_IXUSR) {
result.push(if mode & S_IRUSR != 0 { 'r' } else { '-' });
result.push(if mode & S_IWUSR != 0 { 'w' } else { '-' });
result.push(if mode & S_ISUID != 0 {
if mode & S_IXUSR != 0 { 's' } else { 'S' }
} else if mode & S_IXUSR != 0 {
'x'
} else {
'-'
});

result.push(if has!(mode, S_IRGRP) { 'r' } else { '-' });
result.push(if has!(mode, S_IWGRP) { 'w' } else { '-' });
result.push(if has!(mode, S_ISGID as mode_t) {
if has!(mode, S_IXGRP) { 's' } else { 'S' }
} else if has!(mode, S_IXGRP) {
result.push(if mode & S_IRGRP != 0 { 'r' } else { '-' });
result.push(if mode & S_IWGRP != 0 { 'w' } else { '-' });
result.push(if mode & S_ISGID != 0 {
if mode & S_IXGRP != 0 { 's' } else { 'S' }
} else if mode & S_IXGRP != 0 {
'x'
} else {
'-'
});

result.push(if has!(mode, S_IROTH) { 'r' } else { '-' });
result.push(if has!(mode, S_IWOTH) { 'w' } else { '-' });
result.push(if has!(mode, S_ISVTX as mode_t) {
if has!(mode, S_IXOTH) { 't' } else { 'T' }
} else if has!(mode, S_IXOTH) {
result.push(if mode & S_IROTH != 0 { 'r' } else { '-' });
result.push(if mode & S_IWOTH != 0 { 'w' } else { '-' });
result.push(if mode & S_ISVTX != 0 {
if mode & S_IXOTH != 0 { 't' } else { 'T' }
} else if mode & S_IXOTH != 0 {
'x'
} else {
'-'
Expand Down Expand Up @@ -730,8 +761,9 @@ pub fn path_ends_with_terminator(path: &Path) -> bool {
pub fn is_stdin_directory(stdin: &Stdin) -> bool {
#[cfg(unix)]
{
use mode::{S_IFDIR, S_IFMT};
use nix::sys::stat::fstat;
let mode = fstat(stdin.as_fd()).unwrap().st_mode as mode_t;
let mode = fstat(stdin.as_fd()).unwrap().st_mode as u32;
// We use the S_IFMT mask ala S_ISDIR() to avoid mistaking
// sockets for directories.
mode & S_IFMT == S_IFDIR
Expand Down Expand Up @@ -964,9 +996,9 @@ mod tests {
}
}

#[cfg(unix)]
#[test]
fn test_display_permissions() {
use mode::*;
// spell-checker:ignore (perms) brwsr drwxr rwxr
assert_eq!(
"drwxr-xr-x",
Expand All @@ -992,29 +1024,29 @@ mod tests {

assert_eq!(
"brwSr-xr-x",
display_permissions_unix(S_IFBLK | S_ISUID as mode_t | 0o655, true)
display_permissions_unix(S_IFBLK | S_ISUID | 0o655, true)
);
assert_eq!(
"brwsr-xr-x",
display_permissions_unix(S_IFBLK | S_ISUID as mode_t | 0o755, true)
display_permissions_unix(S_IFBLK | S_ISUID | 0o755, true)
);

assert_eq!(
"prw---sr--",
display_permissions_unix(S_IFIFO | S_ISGID as mode_t | 0o614, true)
display_permissions_unix(S_IFIFO | S_ISGID | 0o614, true)
);
assert_eq!(
"prw---Sr--",
display_permissions_unix(S_IFIFO | S_ISGID as mode_t | 0o604, true)
display_permissions_unix(S_IFIFO | S_ISGID | 0o604, true)
);

assert_eq!(
"c---r-xr-t",
display_permissions_unix(S_IFCHR | S_ISVTX as mode_t | 0o055, true)
display_permissions_unix(S_IFCHR | S_ISVTX | 0o055, true)
);
assert_eq!(
"c---r-xr-T",
display_permissions_unix(S_IFCHR | S_ISVTX as mode_t | 0o054, true)
display_permissions_unix(S_IFCHR | S_ISVTX | 0o054, true)
);
}

Expand Down Expand Up @@ -1095,9 +1127,9 @@ mod tests {
assert!(are_hardlinks_to_same_file(path1, &path2));
}

#[cfg(unix)]
#[test]
fn test_get_file_display() {
use mode::*;
assert_eq!(get_file_display(S_IFDIR | 0o755), 'd');
assert_eq!(get_file_display(S_IFCHR | 0o644), 'c');
assert_eq!(get_file_display(S_IFBLK | 0o600), 'b');
Expand Down
Loading