Skip to content

Commit af05fa8

Browse files
committed
uucore(fs): make display_permissions_unix cross-platform
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 319a06c commit af05fa8

3 files changed

Lines changed: 80 additions & 86 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/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: 73 additions & 77 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 = 0o170000; // bitmask for the file-type field
474+
pub const S_IFSOCK: u32 = 0o140000; // socket
475+
pub const S_IFLNK: u32 = 0o120000; // symbolic link
476+
pub const S_IFREG: u32 = 0o100000; // regular file
477+
pub const S_IFBLK: u32 = 0o060000; // block device
478+
pub const S_IFDIR: u32 = 0o040000; // directory
479+
pub const S_IFCHR: u32 = 0o020000; // character device
480+
pub const S_IFIFO: u32 = 0o010000; // 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::*;
487515
match mode & S_IFMT {
488516
S_IFDIR => 'd',
489517
S_IFCHR => 'c',
@@ -500,9 +528,9 @@ 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::*;
506534
let mut result;
507535
if display_file_type {
508536
result = String::with_capacity(10);
@@ -511,31 +539,31 @@ pub fn display_permissions_unix(mode: mode_t, display_file_type: bool) -> String
511539
result = String::with_capacity(9);
512540
}
513541

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) {
542+
result.push(if mode & S_IRUSR != 0 { 'r' } else { '-' });
543+
result.push(if mode & S_IWUSR != 0 { 'w' } else { '-' });
544+
result.push(if mode & S_ISUID != 0 {
545+
if mode & S_IXUSR != 0 { 's' } else { 'S' }
546+
} else if mode & S_IXUSR != 0 {
519547
'x'
520548
} else {
521549
'-'
522550
});
523551

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) {
552+
result.push(if mode & S_IRGRP != 0 { 'r' } else { '-' });
553+
result.push(if mode & S_IWGRP != 0 { 'w' } else { '-' });
554+
result.push(if mode & S_ISGID != 0 {
555+
if mode & S_IXGRP != 0 { 's' } else { 'S' }
556+
} else if mode & S_IXGRP != 0 {
529557
'x'
530558
} else {
531559
'-'
532560
});
533561

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) {
562+
result.push(if mode & S_IROTH != 0 { 'r' } else { '-' });
563+
result.push(if mode & S_IWOTH != 0 { 'w' } else { '-' });
564+
result.push(if mode & S_ISVTX != 0 {
565+
if mode & S_IXOTH != 0 { 't' } else { 'T' }
566+
} else if mode & S_IXOTH != 0 {
539567
'x'
540568
} else {
541569
'-'
@@ -730,8 +758,9 @@ pub fn path_ends_with_terminator(path: &Path) -> bool {
730758
pub fn is_stdin_directory(stdin: &Stdin) -> bool {
731759
#[cfg(unix)]
732760
{
761+
use mode::{S_IFDIR, S_IFMT};
733762
use nix::sys::stat::fstat;
734-
let mode = fstat(stdin.as_fd()).unwrap().st_mode as mode_t;
763+
let mode = fstat(stdin.as_fd()).unwrap().st_mode as u32;
735764
// We use the S_IFMT mask ala S_ISDIR() to avoid mistaking
736765
// sockets for directories.
737766
mode & S_IFMT == S_IFDIR
@@ -964,58 +993,25 @@ mod tests {
964993
}
965994
}
966995

967-
#[cfg(unix)]
968996
#[test]
969997
fn test_display_permissions() {
998+
use mode::*;
970999
// spell-checker:ignore (perms) brwsr drwxr rwxr
971-
assert_eq!(
972-
"drwxr-xr-x",
973-
display_permissions_unix(S_IFDIR | 0o755, true)
974-
);
975-
assert_eq!(
976-
"rwxr-xr-x",
977-
display_permissions_unix(S_IFDIR | 0o755, false)
978-
);
979-
assert_eq!(
980-
"-rw-r--r--",
981-
display_permissions_unix(S_IFREG | 0o644, true)
982-
);
983-
assert_eq!(
984-
"srw-r-----",
985-
display_permissions_unix(S_IFSOCK | 0o640, true)
986-
);
987-
assert_eq!(
988-
"lrw-r-xr-x",
989-
display_permissions_unix(S_IFLNK | 0o655, true)
990-
);
1000+
assert_eq!("drwxr-xr-x", display_permissions_unix(S_IFDIR | 0o755, true));
1001+
assert_eq!("rwxr-xr-x", display_permissions_unix(S_IFDIR | 0o755, false));
1002+
assert_eq!("-rw-r--r--", display_permissions_unix(S_IFREG | 0o644, true));
1003+
assert_eq!("srw-r-----", display_permissions_unix(S_IFSOCK | 0o640, true));
1004+
assert_eq!("lrw-r-xr-x", display_permissions_unix(S_IFLNK | 0o655, true));
9911005
assert_eq!("?rw-r-xr-x", display_permissions_unix(0o655, true));
9921006

993-
assert_eq!(
994-
"brwSr-xr-x",
995-
display_permissions_unix(S_IFBLK | S_ISUID as mode_t | 0o655, true)
996-
);
997-
assert_eq!(
998-
"brwsr-xr-x",
999-
display_permissions_unix(S_IFBLK | S_ISUID as mode_t | 0o755, true)
1000-
);
1001-
1002-
assert_eq!(
1003-
"prw---sr--",
1004-
display_permissions_unix(S_IFIFO | S_ISGID as mode_t | 0o614, true)
1005-
);
1006-
assert_eq!(
1007-
"prw---Sr--",
1008-
display_permissions_unix(S_IFIFO | S_ISGID as mode_t | 0o604, true)
1009-
);
1010-
1011-
assert_eq!(
1012-
"c---r-xr-t",
1013-
display_permissions_unix(S_IFCHR | S_ISVTX as mode_t | 0o055, true)
1014-
);
1015-
assert_eq!(
1016-
"c---r-xr-T",
1017-
display_permissions_unix(S_IFCHR | S_ISVTX as mode_t | 0o054, true)
1018-
);
1007+
assert_eq!("brwSr-xr-x", display_permissions_unix(S_IFBLK | S_ISUID | 0o655, true));
1008+
assert_eq!("brwsr-xr-x", display_permissions_unix(S_IFBLK | S_ISUID | 0o755, true));
1009+
1010+
assert_eq!("prw---sr--", display_permissions_unix(S_IFIFO | S_ISGID | 0o614, true));
1011+
assert_eq!("prw---Sr--", display_permissions_unix(S_IFIFO | S_ISGID | 0o604, true));
1012+
1013+
assert_eq!("c---r-xr-t", display_permissions_unix(S_IFCHR | S_ISVTX | 0o055, true));
1014+
assert_eq!("c---r-xr-T", display_permissions_unix(S_IFCHR | S_ISVTX | 0o054, true));
10191015
}
10201016

10211017
#[cfg(unix)]
@@ -1095,9 +1091,9 @@ mod tests {
10951091
assert!(are_hardlinks_to_same_file(path1, &path2));
10961092
}
10971093

1098-
#[cfg(unix)]
10991094
#[test]
11001095
fn test_get_file_display() {
1096+
use mode::*;
11011097
assert_eq!(get_file_display(S_IFDIR | 0o755), 'd');
11021098
assert_eq!(get_file_display(S_IFCHR | 0o644), 'c');
11031099
assert_eq!(get_file_display(S_IFBLK | 0o600), 'b');

0 commit comments

Comments
 (0)