Skip to content

Commit f50009f

Browse files
committed
Show mode_t as octal in std::fs Debug impls
1 parent 6a6cd65 commit f50009f

File tree

2 files changed

+180
-5
lines changed

2 files changed

+180
-5
lines changed

library/std/src/sys/pal/unix/fs.rs

+108-5
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
// miri has some special hacks here that make things unused.
22
#![cfg_attr(miri, allow(unused))]
33

4+
#[cfg(test)]
5+
mod tests;
6+
47
use crate::os::unix::prelude::*;
58

69
use crate::ffi::{CStr, OsStr, OsString};
7-
use crate::fmt;
10+
use crate::fmt::{self, Write as _};
811
use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom};
912
use crate::mem;
1013
use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd};
@@ -354,7 +357,7 @@ pub struct DirEntry {
354357
entry: dirent64,
355358
}
356359

357-
#[derive(Clone, Debug)]
360+
#[derive(Clone)]
358361
pub struct OpenOptions {
359362
// generic
360363
read: bool,
@@ -368,7 +371,7 @@ pub struct OpenOptions {
368371
mode: mode_t,
369372
}
370373

371-
#[derive(Clone, PartialEq, Eq, Debug)]
374+
#[derive(Clone, PartialEq, Eq)]
372375
pub struct FilePermissions {
373376
mode: mode_t,
374377
}
@@ -381,7 +384,7 @@ pub struct FileTimes {
381384
created: Option<SystemTime>,
382385
}
383386

384-
#[derive(Copy, Clone, Eq, Debug)]
387+
#[derive(Copy, Clone, Eq)]
385388
pub struct FileType {
386389
mode: mode_t,
387390
}
@@ -398,11 +401,12 @@ impl core::hash::Hash for FileType {
398401
}
399402
}
400403

401-
#[derive(Debug)]
402404
pub struct DirBuilder {
403405
mode: mode_t,
404406
}
405407

408+
struct Mode(mode_t);
409+
406410
cfg_has_statx! {{
407411
impl FileAttr {
408412
fn from_stat64(stat: stat64) -> Self {
@@ -673,12 +677,26 @@ impl FileType {
673677
}
674678
}
675679

680+
impl fmt::Debug for FileType {
681+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
682+
let FileType { mode } = self;
683+
f.debug_struct("FileType").field("mode", &Mode(*mode)).finish()
684+
}
685+
}
686+
676687
impl FromInner<u32> for FilePermissions {
677688
fn from_inner(mode: u32) -> FilePermissions {
678689
FilePermissions { mode: mode as mode_t }
679690
}
680691
}
681692

693+
impl fmt::Debug for FilePermissions {
694+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
695+
let FilePermissions { mode } = self;
696+
f.debug_struct("FilePermissions").field("mode", &Mode(*mode)).finish()
697+
}
698+
}
699+
682700
impl fmt::Debug for ReadDir {
683701
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
684702
// This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame.
@@ -1116,6 +1134,23 @@ impl OpenOptions {
11161134
}
11171135
}
11181136

1137+
impl fmt::Debug for OpenOptions {
1138+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1139+
let OpenOptions { read, write, append, truncate, create, create_new, custom_flags, mode } =
1140+
self;
1141+
f.debug_struct("OpenOptions")
1142+
.field("read", read)
1143+
.field("write", write)
1144+
.field("append", append)
1145+
.field("truncate", truncate)
1146+
.field("create", create)
1147+
.field("create_new", create_new)
1148+
.field("custom_flags", custom_flags)
1149+
.field("mode", &Mode(*mode))
1150+
.finish()
1151+
}
1152+
}
1153+
11191154
impl File {
11201155
pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
11211156
run_path_with_cstr(path, &|path| File::open_c(path, opts))
@@ -1402,6 +1437,13 @@ impl DirBuilder {
14021437
}
14031438
}
14041439

1440+
impl fmt::Debug for DirBuilder {
1441+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1442+
let DirBuilder { mode } = self;
1443+
f.debug_struct("DirBuilder").field("mode", &Mode(*mode)).finish()
1444+
}
1445+
}
1446+
14051447
impl AsInner<FileDesc> for File {
14061448
#[inline]
14071449
fn as_inner(&self) -> &FileDesc {
@@ -1574,6 +1616,67 @@ impl fmt::Debug for File {
15741616
}
15751617
}
15761618

1619+
// Format in octal, followed by the mode format used in `ls -l`.
1620+
//
1621+
// References:
1622+
// https://pubs.opengroup.org/onlinepubs/009696899/utilities/ls.html
1623+
// https://www.gnu.org/software/libc/manual/html_node/Testing-File-Type.html
1624+
// https://www.gnu.org/software/libc/manual/html_node/Permission-Bits.html
1625+
//
1626+
// Example:
1627+
// 0o100664 (-rw-rw-r--)
1628+
impl fmt::Debug for Mode {
1629+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1630+
let Self(mode) = self;
1631+
write!(f, "0o{mode:06o}")?;
1632+
1633+
let entry_type = match mode & libc::S_IFMT {
1634+
libc::S_IFDIR => 'd',
1635+
libc::S_IFBLK => 'b',
1636+
libc::S_IFCHR => 'c',
1637+
libc::S_IFLNK => 'l',
1638+
libc::S_IFIFO => 'p',
1639+
libc::S_IFREG => '-',
1640+
_ => return Ok(()),
1641+
};
1642+
1643+
f.write_str(" (")?;
1644+
f.write_char(entry_type)?;
1645+
1646+
// Owner permissions
1647+
f.write_char(if mode & libc::S_IRUSR != 0 { 'r' } else { '-' })?;
1648+
f.write_char(if mode & libc::S_IWUSR != 0 { 'w' } else { '-' })?;
1649+
f.write_char(match (mode & libc::S_IXUSR != 0, mode & libc::S_ISUID != 0) {
1650+
(true, true) => 's', // executable and setuid
1651+
(false, true) => 'S', // setuid
1652+
(true, false) => 'x', // executable
1653+
(false, false) => '-',
1654+
})?;
1655+
1656+
// Group permissions
1657+
f.write_char(if mode & libc::S_IRGRP != 0 { 'r' } else { '-' })?;
1658+
f.write_char(if mode & libc::S_IWGRP != 0 { 'w' } else { '-' })?;
1659+
f.write_char(match (mode & libc::S_IXGRP != 0, mode & libc::S_ISGID != 0) {
1660+
(true, true) => 's', // executable and setgid
1661+
(false, true) => 'S', // setgid
1662+
(true, false) => 'x', // executable
1663+
(false, false) => '-',
1664+
})?;
1665+
1666+
// Other permissions
1667+
f.write_char(if mode & libc::S_IROTH != 0 { 'r' } else { '-' })?;
1668+
f.write_char(if mode & libc::S_IWOTH != 0 { 'w' } else { '-' })?;
1669+
f.write_char(match (entry_type, mode & libc::S_IXOTH != 0, mode & libc::S_ISVTX != 0) {
1670+
('d', true, true) => 't', // searchable and restricted deletion
1671+
('d', false, true) => 'T', // restricted deletion
1672+
(_, true, _) => 'x', // executable
1673+
(_, false, _) => '-',
1674+
})?;
1675+
1676+
f.write_char(')')
1677+
}
1678+
}
1679+
15771680
pub fn readdir(path: &Path) -> io::Result<ReadDir> {
15781681
let ptr = run_path_with_cstr(path, &|p| unsafe { Ok(libc::opendir(p.as_ptr())) })?;
15791682
if ptr.is_null() {
+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
use crate::sys::pal::unix::fs::FilePermissions;
2+
use crate::sys_common::FromInner;
3+
4+
#[test]
5+
fn test_debug_permissions() {
6+
for (expected, mode) in [
7+
// typical directory
8+
("FilePermissions { mode: 0o040775 (drwxrwxr-x) }", 0o04_0775),
9+
// typical text file
10+
("FilePermissions { mode: 0o100664 (-rw-rw-r--) }", 0o10_0664),
11+
// setuid executable (/usr/bin/doas)
12+
("FilePermissions { mode: 0o104755 (-rwsr-xr-x) }", 0o10_4755),
13+
// char device (/dev/zero)
14+
("FilePermissions { mode: 0o020666 (crw-rw-rw-) }", 0o02_0666),
15+
// block device (/dev/vda)
16+
("FilePermissions { mode: 0o060660 (brw-rw----) }", 0o06_0660),
17+
// symbolic link
18+
("FilePermissions { mode: 0o120777 (lrwxrwxrwx) }", 0o12_0777),
19+
// fifo
20+
("FilePermissions { mode: 0o010664 (prw-rw-r--) }", 0o01_0664),
21+
// none
22+
("FilePermissions { mode: 0o100000 (----------) }", 0o10_0000),
23+
// unrecognized
24+
("FilePermissions { mode: 0o000001 }", 1),
25+
] {
26+
assert_eq!(format!("{:?}", FilePermissions::from_inner(mode)), expected);
27+
}
28+
29+
for (expected, mode) in [
30+
// owner readable
31+
("FilePermissions { mode: 0o100400 (-r--------) }", libc::S_IRUSR),
32+
// owner writable
33+
("FilePermissions { mode: 0o100200 (--w-------) }", libc::S_IWUSR),
34+
// owner executable
35+
("FilePermissions { mode: 0o100100 (---x------) }", libc::S_IXUSR),
36+
// setuid
37+
("FilePermissions { mode: 0o104000 (---S------) }", libc::S_ISUID),
38+
// owner executable and setuid
39+
("FilePermissions { mode: 0o104100 (---s------) }", libc::S_IXUSR | libc::S_ISUID),
40+
// group readable
41+
("FilePermissions { mode: 0o100040 (----r-----) }", libc::S_IRGRP),
42+
// group writable
43+
("FilePermissions { mode: 0o100020 (-----w----) }", libc::S_IWGRP),
44+
// group executable
45+
("FilePermissions { mode: 0o100010 (------x---) }", libc::S_IXGRP),
46+
// setgid
47+
("FilePermissions { mode: 0o102000 (------S---) }", libc::S_ISGID),
48+
// group executable and setgid
49+
("FilePermissions { mode: 0o102010 (------s---) }", libc::S_IXGRP | libc::S_ISGID),
50+
// other readable
51+
("FilePermissions { mode: 0o100004 (-------r--) }", libc::S_IROTH),
52+
// other writeable
53+
("FilePermissions { mode: 0o100002 (--------w-) }", libc::S_IWOTH),
54+
// other executable
55+
("FilePermissions { mode: 0o100001 (---------x) }", libc::S_IXOTH),
56+
// sticky
57+
("FilePermissions { mode: 0o101000 (----------) }", libc::S_ISVTX),
58+
// other executable and sticky
59+
("FilePermissions { mode: 0o101001 (---------x) }", libc::S_IXOTH | libc::S_ISVTX),
60+
] {
61+
assert_eq!(format!("{:?}", FilePermissions::from_inner(libc::S_IFREG | mode)), expected);
62+
}
63+
64+
for (expected, mode) in [
65+
// restricted deletion ("sticky") flag is set, and search permission is not granted to others
66+
("FilePermissions { mode: 0o041000 (d--------T) }", libc::S_ISVTX),
67+
// sticky and searchable
68+
("FilePermissions { mode: 0o041001 (d--------t) }", libc::S_ISVTX | libc::S_IXOTH),
69+
] {
70+
assert_eq!(format!("{:?}", FilePermissions::from_inner(libc::S_IFDIR | mode)), expected);
71+
}
72+
}

0 commit comments

Comments
 (0)