Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit b14d8b2

Browse files
committedApr 10, 2024
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 1c77f73 + caf3766 commit b14d8b2

File tree

2 files changed

+186
-5
lines changed

2 files changed

+186
-5
lines changed
 

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

+115-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};
@@ -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,13 @@ impl core::hash::Hash for FileType {
406409
}
407410
}
408411

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

416+
#[derive(Copy, Clone)]
417+
struct Mode(mode_t);
418+
414419
cfg_has_statx! {{
415420
impl FileAttr {
416421
fn from_stat64(stat: stat64) -> Self {
@@ -689,12 +694,26 @@ impl FileType {
689694
}
690695
}
691696

697+
impl fmt::Debug for FileType {
698+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
699+
let FileType { mode } = self;
700+
f.debug_struct("FileType").field("mode", &Mode(*mode)).finish()
701+
}
702+
}
703+
692704
impl FromInner<u32> for FilePermissions {
693705
fn from_inner(mode: u32) -> FilePermissions {
694706
FilePermissions { mode: mode as mode_t }
695707
}
696708
}
697709

710+
impl fmt::Debug for FilePermissions {
711+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
712+
let FilePermissions { mode } = self;
713+
f.debug_struct("FilePermissions").field("mode", &Mode(*mode)).finish()
714+
}
715+
}
716+
698717
impl fmt::Debug for ReadDir {
699718
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
700719
// This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame.
@@ -1135,6 +1154,23 @@ impl OpenOptions {
11351154
}
11361155
}
11371156

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

1464+
impl fmt::Debug for DirBuilder {
1465+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1466+
let DirBuilder { mode } = self;
1467+
f.debug_struct("DirBuilder").field("mode", &Mode(*mode)).finish()
1468+
}
1469+
}
1470+
14281471
impl AsInner<FileDesc> for File {
14291472
#[inline]
14301473
fn as_inner(&self) -> &FileDesc {
@@ -1597,6 +1640,73 @@ impl fmt::Debug for File {
15971640
}
15981641
}
15991642

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

0 commit comments

Comments
 (0)
Failed to load comments.