Skip to content

Commit a21177e

Browse files
authored
uucore(fs): make display_permissions_unix cross-platform (#11538)
Remove the #[cfg(unix)] gate from display_permissions_unix and its helper get_file_display. Change the parameter from libc::mode_t to u32 (Metadata::mode() already returns u32, so no cast or information loss). Add pub mod mode to uucore::fs with portable u32 constants for all POSIX S_I* values (S_IFMT, S_IFSOCK, S_IFLNK, S_IFREG, S_IFBLK, S_IFDIR, S_IFCHR, S_IFIFO, S_ISUID, S_ISGID, S_ISVTX, and all nine rwx bits). Callers can import uucore::fs::mode::* instead of libc, without requiring a Unix target. Replace the has!() macro calls in display_permissions_unix with explicit bitwise & != 0 checks, which work without a type-specific macro. Update chmod and mv to drop the libc::mode_t import and the as mode_t casts at each call site. Update is_stdin_directory to use mode::S_IFMT and mode::S_IFDIR instead of the libc imports. Remove #[cfg(unix)] from test_display_permissions and test_get_file_display so both tests compile and run on all platforms.
1 parent 566836c commit a21177e

4 files changed

Lines changed: 77 additions & 47 deletions

File tree

src/uu/chmod/src/chmod.rs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ use thiserror::Error;
1414
use uucore::display::Quotable;
1515
use uucore::error::{ExitCode, UError, UResult, USimpleError, UUsageError, set_exit_code};
1616
use uucore::fs::display_permissions_unix;
17-
use uucore::libc::mode_t;
1817
use uucore::mode;
1918
use uucore::perms::{TraverseSymlinks, configure_symlink_and_recursion};
2019

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

319318
if new_mode != old_mode {
320319
println!(
@@ -668,8 +667,8 @@ impl Chmoder {
668667
if (new_mode & !naively_expected_new_mode) != 0 {
669668
return Err(ChmodError::NewPermissions(
670669
file.into(),
671-
display_permissions_unix(new_mode as mode_t, false),
672-
display_permissions_unix(naively_expected_new_mode as mode_t, false),
670+
display_permissions_unix(new_mode, false),
671+
display_permissions_unix(naively_expected_new_mode, false),
673672
)
674673
.into());
675674
}
@@ -691,8 +690,8 @@ impl Chmoder {
691690
println!(
692691
"failed to change mode of file {} from {fperm:04o} ({}) to {mode:04o} ({})",
693692
file.quote(),
694-
display_permissions_unix(fperm as mode_t, false),
695-
display_permissions_unix(mode as mode_t, false)
693+
display_permissions_unix(fperm, false),
694+
display_permissions_unix(mode, false)
696695
);
697696
}
698697
Err(1)

src/uu/cp/src/cp.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1620,7 +1620,7 @@ fn file_mode_for_interactive_overwrite(
16201620

16211621
Some((
16221622
format!("{mode_without_leading_digits:04o}"),
1623-
uucore::fs::display_permissions_unix(mode, false),
1623+
uucore::fs::display_permissions_unix(mode as u32, false),
16241624
))
16251625
}
16261626
}

src/uu/mv/src/mv.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1255,14 +1255,13 @@ fn is_writable(path: &Path) -> (bool, Option<u32>) {
12551255

12561256
#[cfg(unix)]
12571257
fn get_interactive_prompt(to: &Path, cached_mode: Option<u32>) -> String {
1258-
use libc::mode_t;
12591258
// Use cached mode if available, otherwise fetch it
12601259
let mode = cached_mode.or_else(|| to.metadata().ok().map(|m| m.permissions().mode()));
12611260
if let Some(mode) = mode {
12621261
let file_mode = mode & 0o777;
12631262
// Check if file is not writable by user
12641263
if (mode & 0o200) == 0 {
1265-
let perms = display_permissions_unix(mode as mode_t, false);
1264+
let perms = display_permissions_unix(mode, false);
12661265
let mode_info = format!("{file_mode:04o} ({perms})");
12671266
return translate!("mv-prompt-overwrite-mode", "target" => to.quote(), "mode_info" => mode_info);
12681267
}

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

Lines changed: 69 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,7 @@
88
// spell-checker:ignore backport
99

1010
#[cfg(unix)]
11-
use libc::{
12-
S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO, S_IFLNK, S_IFMT, S_IFREG, S_IFSOCK, S_IRGRP, S_IROTH,
13-
S_IRUSR, S_ISGID, S_ISUID, S_ISVTX, S_IWGRP, S_IWOTH, S_IWUSR, S_IXGRP, S_IXOTH, S_IXUSR,
14-
mkfifo, mode_t,
15-
};
11+
use libc::mkfifo;
1612
#[cfg(all(unix, not(target_os = "redox")))]
1713
pub use libc::{major, makedev, minor};
1814
use std::collections::HashSet;
@@ -464,12 +460,44 @@ pub fn display_permissions(metadata: &fs::Metadata, display_file_type: bool) ->
464460
#[cfg(unix)]
465461
/// Display the permissions of a file
466462
pub fn display_permissions(metadata: &fs::Metadata, display_file_type: bool) -> String {
467-
let mode: mode_t = metadata.mode() as mode_t;
468-
display_permissions_unix(mode, display_file_type)
463+
display_permissions_unix(metadata.mode(), display_file_type)
464+
}
465+
466+
/// Portable file mode bit constants, equivalent to the POSIX `S_I*` values.
467+
///
468+
/// These are defined here as plain `u32` so they are available on every
469+
/// platform, including Windows, without requiring `libc` or a Unix target.
470+
/// Callers that previously used `libc::S_IFDIR` etc. can import these instead.
471+
pub mod mode {
472+
// File-type mask and values
473+
pub const S_IFMT: u32 = 0o170_000; // bitmask for the file-type field
474+
pub const S_IFSOCK: u32 = 0o140_000; // socket
475+
pub const S_IFLNK: u32 = 0o120_000; // symbolic link
476+
pub const S_IFREG: u32 = 0o100_000; // regular file
477+
pub const S_IFBLK: u32 = 0o060_000; // block device
478+
pub const S_IFDIR: u32 = 0o040_000; // directory
479+
pub const S_IFCHR: u32 = 0o020_000; // character device
480+
pub const S_IFIFO: u32 = 0o010_000; // named pipe (FIFO)
481+
482+
// Permission and special-mode bits
483+
pub const S_ISUID: u32 = 0o4000; // setuid
484+
pub const S_ISGID: u32 = 0o2000; // setgid
485+
pub const S_ISVTX: u32 = 0o1000; // sticky
486+
487+
pub const S_IRUSR: u32 = 0o0400; // owner read
488+
pub const S_IWUSR: u32 = 0o0200; // owner write
489+
pub const S_IXUSR: u32 = 0o0100; // owner execute
490+
491+
pub const S_IRGRP: u32 = 0o0040; // group read
492+
pub const S_IWGRP: u32 = 0o0020; // group write
493+
pub const S_IXGRP: u32 = 0o0010; // group execute
494+
495+
pub const S_IROTH: u32 = 0o0004; // other read
496+
pub const S_IWOTH: u32 = 0o0002; // other write
497+
pub const S_IXOTH: u32 = 0o0001; // other execute
469498
}
470499

471500
/// Returns a character representation of the file type based on its mode.
472-
/// This function is specific to Unix-like systems.
473501
///
474502
/// - `mode`: The mode of the file, typically obtained from file metadata.
475503
///
@@ -482,8 +510,8 @@ pub fn display_permissions(metadata: &fs::Metadata, display_file_type: bool) ->
482510
/// - 'l' for symbolic links
483511
/// - 's' for sockets
484512
/// - '?' for any other unrecognized file types
485-
#[cfg(unix)]
486-
fn get_file_display(mode: mode_t) -> char {
513+
fn get_file_display(mode: u32) -> char {
514+
use mode::{S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO, S_IFLNK, S_IFMT, S_IFREG, S_IFSOCK};
487515
match mode & S_IFMT {
488516
S_IFDIR => 'd',
489517
S_IFCHR => 'c',
@@ -500,9 +528,12 @@ fn get_file_display(mode: mode_t) -> char {
500528
// The logic below is more readable written this way.
501529
#[allow(clippy::if_not_else)]
502530
#[allow(clippy::cognitive_complexity)]
503-
#[cfg(unix)]
504-
/// Display the permissions of a file on a unix like system
505-
pub fn display_permissions_unix(mode: mode_t, display_file_type: bool) -> String {
531+
/// Display the unix permissions of a file
532+
pub fn display_permissions_unix(mode: u32, display_file_type: bool) -> String {
533+
use mode::{
534+
S_IRGRP, S_IROTH, S_IRUSR, S_ISGID, S_ISUID, S_ISVTX, S_IWGRP, S_IWOTH, S_IWUSR, S_IXGRP,
535+
S_IXOTH, S_IXUSR,
536+
};
506537
let mut result;
507538
if display_file_type {
508539
result = String::with_capacity(10);
@@ -511,31 +542,31 @@ pub fn display_permissions_unix(mode: mode_t, display_file_type: bool) -> String
511542
result = String::with_capacity(9);
512543
}
513544

514-
result.push(if has!(mode, S_IRUSR) { 'r' } else { '-' });
515-
result.push(if has!(mode, S_IWUSR) { 'w' } else { '-' });
516-
result.push(if has!(mode, S_ISUID as mode_t) {
517-
if has!(mode, S_IXUSR) { 's' } else { 'S' }
518-
} else if has!(mode, S_IXUSR) {
545+
result.push(if mode & S_IRUSR != 0 { 'r' } else { '-' });
546+
result.push(if mode & S_IWUSR != 0 { 'w' } else { '-' });
547+
result.push(if mode & S_ISUID != 0 {
548+
if mode & S_IXUSR != 0 { 's' } else { 'S' }
549+
} else if mode & S_IXUSR != 0 {
519550
'x'
520551
} else {
521552
'-'
522553
});
523554

524-
result.push(if has!(mode, S_IRGRP) { 'r' } else { '-' });
525-
result.push(if has!(mode, S_IWGRP) { 'w' } else { '-' });
526-
result.push(if has!(mode, S_ISGID as mode_t) {
527-
if has!(mode, S_IXGRP) { 's' } else { 'S' }
528-
} else if has!(mode, S_IXGRP) {
555+
result.push(if mode & S_IRGRP != 0 { 'r' } else { '-' });
556+
result.push(if mode & S_IWGRP != 0 { 'w' } else { '-' });
557+
result.push(if mode & S_ISGID != 0 {
558+
if mode & S_IXGRP != 0 { 's' } else { 'S' }
559+
} else if mode & S_IXGRP != 0 {
529560
'x'
530561
} else {
531562
'-'
532563
});
533564

534-
result.push(if has!(mode, S_IROTH) { 'r' } else { '-' });
535-
result.push(if has!(mode, S_IWOTH) { 'w' } else { '-' });
536-
result.push(if has!(mode, S_ISVTX as mode_t) {
537-
if has!(mode, S_IXOTH) { 't' } else { 'T' }
538-
} else if has!(mode, S_IXOTH) {
565+
result.push(if mode & S_IROTH != 0 { 'r' } else { '-' });
566+
result.push(if mode & S_IWOTH != 0 { 'w' } else { '-' });
567+
result.push(if mode & S_ISVTX != 0 {
568+
if mode & S_IXOTH != 0 { 't' } else { 'T' }
569+
} else if mode & S_IXOTH != 0 {
539570
'x'
540571
} else {
541572
'-'
@@ -730,8 +761,9 @@ pub fn path_ends_with_terminator(path: &Path) -> bool {
730761
pub fn is_stdin_directory(stdin: &Stdin) -> bool {
731762
#[cfg(unix)]
732763
{
764+
use mode::{S_IFDIR, S_IFMT};
733765
use nix::sys::stat::fstat;
734-
let mode = fstat(stdin.as_fd()).unwrap().st_mode as mode_t;
766+
let mode = fstat(stdin.as_fd()).unwrap().st_mode as u32;
735767
// We use the S_IFMT mask ala S_ISDIR() to avoid mistaking
736768
// sockets for directories.
737769
mode & S_IFMT == S_IFDIR
@@ -964,9 +996,9 @@ mod tests {
964996
}
965997
}
966998

967-
#[cfg(unix)]
968999
#[test]
9691000
fn test_display_permissions() {
1001+
use mode::*;
9701002
// spell-checker:ignore (perms) brwsr drwxr rwxr
9711003
assert_eq!(
9721004
"drwxr-xr-x",
@@ -992,29 +1024,29 @@ mod tests {
9921024

9931025
assert_eq!(
9941026
"brwSr-xr-x",
995-
display_permissions_unix(S_IFBLK | S_ISUID as mode_t | 0o655, true)
1027+
display_permissions_unix(S_IFBLK | S_ISUID | 0o655, true)
9961028
);
9971029
assert_eq!(
9981030
"brwsr-xr-x",
999-
display_permissions_unix(S_IFBLK | S_ISUID as mode_t | 0o755, true)
1031+
display_permissions_unix(S_IFBLK | S_ISUID | 0o755, true)
10001032
);
10011033

10021034
assert_eq!(
10031035
"prw---sr--",
1004-
display_permissions_unix(S_IFIFO | S_ISGID as mode_t | 0o614, true)
1036+
display_permissions_unix(S_IFIFO | S_ISGID | 0o614, true)
10051037
);
10061038
assert_eq!(
10071039
"prw---Sr--",
1008-
display_permissions_unix(S_IFIFO | S_ISGID as mode_t | 0o604, true)
1040+
display_permissions_unix(S_IFIFO | S_ISGID | 0o604, true)
10091041
);
10101042

10111043
assert_eq!(
10121044
"c---r-xr-t",
1013-
display_permissions_unix(S_IFCHR | S_ISVTX as mode_t | 0o055, true)
1045+
display_permissions_unix(S_IFCHR | S_ISVTX | 0o055, true)
10141046
);
10151047
assert_eq!(
10161048
"c---r-xr-T",
1017-
display_permissions_unix(S_IFCHR | S_ISVTX as mode_t | 0o054, true)
1049+
display_permissions_unix(S_IFCHR | S_ISVTX | 0o054, true)
10181050
);
10191051
}
10201052

@@ -1095,9 +1127,9 @@ mod tests {
10951127
assert!(are_hardlinks_to_same_file(path1, &path2));
10961128
}
10971129

1098-
#[cfg(unix)]
10991130
#[test]
11001131
fn test_get_file_display() {
1132+
use mode::*;
11011133
assert_eq!(get_file_display(S_IFDIR | 0o755), 'd');
11021134
assert_eq!(get_file_display(S_IFCHR | 0o644), 'c');
11031135
assert_eq!(get_file_display(S_IFBLK | 0o600), 'b');

0 commit comments

Comments
 (0)