Skip to content

Commit e8f551e

Browse files
committed
Auto merge of #122812 - dtolnay:mode, r=workingjubilee
Show mode_t as octal in std::fs Debug impls Example: ```rust fn main() { println!("{:?}", std::fs::metadata("Cargo.toml").unwrap().permissions()); } ``` - Before: `Permissions(FilePermissions { mode: 33204 })` - ~~After: `Permissions(FilePermissions { mode: 0o100664 })`~~ - After: `Permissions(FilePermissions { mode: 0o100664 (-rw-rw-r--) })` ~~I thought about using the format from `ls -l` (`-rw-rw-r--`, `drwxrwxr-x`) but I am not sure how transferable the meaning of the higher bits between different unix systems, and anyway starting the value with a leading negative-sign seems objectionable.~~
2 parents aa1c459 + f50009f commit e8f551e

File tree

2 files changed

+180
-5
lines changed

2 files changed

+180
-5
lines changed

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

Lines changed: 108 additions & 5 deletions
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};
@@ -356,7 +359,7 @@ pub struct DirEntry {
356359
entry: dirent64,
357360
}
358361

359-
#[derive(Clone, Debug)]
362+
#[derive(Clone)]
360363
pub struct OpenOptions {
361364
// generic
362365
read: bool,
@@ -370,7 +373,7 @@ pub struct OpenOptions {
370373
mode: mode_t,
371374
}
372375

373-
#[derive(Clone, PartialEq, Eq, Debug)]
376+
#[derive(Clone, PartialEq, Eq)]
374377
pub struct FilePermissions {
375378
mode: mode_t,
376379
}
@@ -389,7 +392,7 @@ pub struct FileTimes {
389392
created: Option<SystemTime>,
390393
}
391394

392-
#[derive(Copy, Clone, Eq, Debug)]
395+
#[derive(Copy, Clone, Eq)]
393396
pub struct FileType {
394397
mode: mode_t,
395398
}
@@ -406,11 +409,12 @@ impl core::hash::Hash for FileType {
406409
}
407410
}
408411

409-
#[derive(Debug)]
410412
pub struct DirBuilder {
411413
mode: mode_t,
412414
}
413415

416+
struct Mode(mode_t);
417+
414418
cfg_has_statx! {{
415419
impl FileAttr {
416420
fn from_stat64(stat: stat64) -> Self {
@@ -689,12 +693,26 @@ impl FileType {
689693
}
690694
}
691695

696+
impl fmt::Debug for FileType {
697+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
698+
let FileType { mode } = self;
699+
f.debug_struct("FileType").field("mode", &Mode(*mode)).finish()
700+
}
701+
}
702+
692703
impl FromInner<u32> for FilePermissions {
693704
fn from_inner(mode: u32) -> FilePermissions {
694705
FilePermissions { mode: mode as mode_t }
695706
}
696707
}
697708

709+
impl fmt::Debug for FilePermissions {
710+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
711+
let FilePermissions { mode } = self;
712+
f.debug_struct("FilePermissions").field("mode", &Mode(*mode)).finish()
713+
}
714+
}
715+
698716
impl fmt::Debug for ReadDir {
699717
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
700718
// This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame.
@@ -1135,6 +1153,23 @@ impl OpenOptions {
11351153
}
11361154
}
11371155

1156+
impl fmt::Debug for OpenOptions {
1157+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1158+
let OpenOptions { read, write, append, truncate, create, create_new, custom_flags, mode } =
1159+
self;
1160+
f.debug_struct("OpenOptions")
1161+
.field("read", read)
1162+
.field("write", write)
1163+
.field("append", append)
1164+
.field("truncate", truncate)
1165+
.field("create", create)
1166+
.field("create_new", create_new)
1167+
.field("custom_flags", custom_flags)
1168+
.field("mode", &Mode(*mode))
1169+
.finish()
1170+
}
1171+
}
1172+
11381173
impl File {
11391174
pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
11401175
run_path_with_cstr(path, &|path| File::open_c(path, opts))
@@ -1425,6 +1460,13 @@ impl DirBuilder {
14251460
}
14261461
}
14271462

1463+
impl fmt::Debug for DirBuilder {
1464+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1465+
let DirBuilder { mode } = self;
1466+
f.debug_struct("DirBuilder").field("mode", &Mode(*mode)).finish()
1467+
}
1468+
}
1469+
14281470
impl AsInner<FileDesc> for File {
14291471
#[inline]
14301472
fn as_inner(&self) -> &FileDesc {
@@ -1597,6 +1639,67 @@ impl fmt::Debug for File {
15971639
}
15981640
}
15991641

1642+
// Format in octal, followed by the mode format used in `ls -l`.
1643+
//
1644+
// References:
1645+
// https://pubs.opengroup.org/onlinepubs/009696899/utilities/ls.html
1646+
// https://www.gnu.org/software/libc/manual/html_node/Testing-File-Type.html
1647+
// https://www.gnu.org/software/libc/manual/html_node/Permission-Bits.html
1648+
//
1649+
// Example:
1650+
// 0o100664 (-rw-rw-r--)
1651+
impl fmt::Debug for Mode {
1652+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1653+
let Self(mode) = self;
1654+
write!(f, "0o{mode:06o}")?;
1655+
1656+
let entry_type = match mode & libc::S_IFMT {
1657+
libc::S_IFDIR => 'd',
1658+
libc::S_IFBLK => 'b',
1659+
libc::S_IFCHR => 'c',
1660+
libc::S_IFLNK => 'l',
1661+
libc::S_IFIFO => 'p',
1662+
libc::S_IFREG => '-',
1663+
_ => return Ok(()),
1664+
};
1665+
1666+
f.write_str(" (")?;
1667+
f.write_char(entry_type)?;
1668+
1669+
// Owner permissions
1670+
f.write_char(if mode & libc::S_IRUSR != 0 { 'r' } else { '-' })?;
1671+
f.write_char(if mode & libc::S_IWUSR != 0 { 'w' } else { '-' })?;
1672+
f.write_char(match (mode & libc::S_IXUSR != 0, mode & libc::S_ISUID != 0) {
1673+
(true, true) => 's', // executable and setuid
1674+
(false, true) => 'S', // setuid
1675+
(true, false) => 'x', // executable
1676+
(false, false) => '-',
1677+
})?;
1678+
1679+
// Group permissions
1680+
f.write_char(if mode & libc::S_IRGRP != 0 { 'r' } else { '-' })?;
1681+
f.write_char(if mode & libc::S_IWGRP != 0 { 'w' } else { '-' })?;
1682+
f.write_char(match (mode & libc::S_IXGRP != 0, mode & libc::S_ISGID != 0) {
1683+
(true, true) => 's', // executable and setgid
1684+
(false, true) => 'S', // setgid
1685+
(true, false) => 'x', // executable
1686+
(false, false) => '-',
1687+
})?;
1688+
1689+
// Other permissions
1690+
f.write_char(if mode & libc::S_IROTH != 0 { 'r' } else { '-' })?;
1691+
f.write_char(if mode & libc::S_IWOTH != 0 { 'w' } else { '-' })?;
1692+
f.write_char(match (entry_type, mode & libc::S_IXOTH != 0, mode & libc::S_ISVTX != 0) {
1693+
('d', true, true) => 't', // searchable and restricted deletion
1694+
('d', false, true) => 'T', // restricted deletion
1695+
(_, true, _) => 'x', // executable
1696+
(_, false, _) => '-',
1697+
})?;
1698+
1699+
f.write_char(')')
1700+
}
1701+
}
1702+
16001703
pub fn readdir(path: &Path) -> io::Result<ReadDir> {
16011704
let ptr = run_path_with_cstr(path, &|p| unsafe { Ok(libc::opendir(p.as_ptr())) })?;
16021705
if ptr.is_null() {
Lines changed: 72 additions & 0 deletions
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)