Skip to content

Commit

Permalink
Show mode_t as octal in std::fs Debug impls
Browse files Browse the repository at this point in the history
  • Loading branch information
dtolnay committed Mar 27, 2024
1 parent 6a6cd65 commit f955b6f
Show file tree
Hide file tree
Showing 2 changed files with 162 additions and 5 deletions.
108 changes: 103 additions & 5 deletions library/std/src/sys/pal/unix/fs.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
// miri has some special hacks here that make things unused.
#![cfg_attr(miri, allow(unused))]

#[cfg(test)]
mod tests;

use crate::os::unix::prelude::*;

use crate::ffi::{CStr, OsStr, OsString};
use crate::fmt;
use crate::fmt::{self, Write as _};
use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom};
use crate::mem;
use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd};
Expand Down Expand Up @@ -354,7 +357,7 @@ pub struct DirEntry {
entry: dirent64,
}

#[derive(Clone, Debug)]
#[derive(Clone)]
pub struct OpenOptions {
// generic
read: bool,
Expand All @@ -368,7 +371,7 @@ pub struct OpenOptions {
mode: mode_t,
}

#[derive(Clone, PartialEq, Eq, Debug)]
#[derive(Clone, PartialEq, Eq)]
pub struct FilePermissions {
mode: mode_t,
}
Expand All @@ -381,7 +384,7 @@ pub struct FileTimes {
created: Option<SystemTime>,
}

#[derive(Copy, Clone, Eq, Debug)]
#[derive(Copy, Clone, Eq)]
pub struct FileType {
mode: mode_t,
}
Expand All @@ -398,11 +401,12 @@ impl core::hash::Hash for FileType {
}
}

#[derive(Debug)]
pub struct DirBuilder {
mode: mode_t,
}

struct Mode(mode_t);

cfg_has_statx! {{
impl FileAttr {
fn from_stat64(stat: stat64) -> Self {
Expand Down Expand Up @@ -673,12 +677,26 @@ impl FileType {
}
}

impl fmt::Debug for FileType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let FileType { mode } = self;
f.debug_struct("FileType").field("mode", &Mode(*mode)).finish()
}
}

impl FromInner<u32> for FilePermissions {
fn from_inner(mode: u32) -> FilePermissions {
FilePermissions { mode: mode as mode_t }
}
}

impl fmt::Debug for FilePermissions {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let FilePermissions { mode } = self;
f.debug_struct("FilePermissions").field("mode", &Mode(*mode)).finish()
}
}

impl fmt::Debug for ReadDir {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame.
Expand Down Expand Up @@ -1116,6 +1134,23 @@ impl OpenOptions {
}
}

impl fmt::Debug for OpenOptions {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let OpenOptions { read, write, append, truncate, create, create_new, custom_flags, mode } =
self;
f.debug_struct("OpenOptions")
.field("read", read)
.field("write", write)
.field("append", append)
.field("truncate", truncate)
.field("create", create)
.field("create_new", create_new)
.field("custom_flags", custom_flags)
.field("mode", &Mode(*mode))
.finish()
}
}

impl File {
pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
run_path_with_cstr(path, &|path| File::open_c(path, opts))
Expand Down Expand Up @@ -1402,6 +1437,13 @@ impl DirBuilder {
}
}

impl fmt::Debug for DirBuilder {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let DirBuilder { mode } = self;
f.debug_struct("DirBuilder").field("mode", &Mode(*mode)).finish()
}
}

impl AsInner<FileDesc> for File {
#[inline]
fn as_inner(&self) -> &FileDesc {
Expand Down Expand Up @@ -1574,6 +1616,62 @@ impl fmt::Debug for File {
}
}

// Format in octal, followed by the mode format used in `ls -l`.
//
// References:
// https://pubs.opengroup.org/onlinepubs/009696899/utilities/ls.html
// https://www.gnu.org/software/libc/manual/html_node/Testing-File-Type.html
// https://www.gnu.org/software/libc/manual/html_node/Permission-Bits.html
//
// Example:
// 0o100664 (-rw-rw-r--)
impl fmt::Debug for Mode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self(mode) = self;
write!(f, "0o{mode:06o}")?;

let entry_type = match mode & libc::S_IFMT {
libc::S_IFDIR => 'd',
libc::S_IFBLK => 'b',
libc::S_IFCHR => 'c',
libc::S_IFLNK => 'l',
libc::S_IFIFO => 'p',
libc::S_IFREG => '-',
_ => return Ok(()),
};

f.write_str(" (")?;
f.write_char(entry_type)?;

// Owner permissions
f.write_char(if mode & libc::S_IRUSR != 0 { 'r' } else { '-' })?;
f.write_char(if mode & libc::S_IWUSR != 0 { 'w' } else { '-' })?;
f.write_char(match (mode & libc::S_IXUSR != 0, mode & libc::S_ISUID != 0) {
(true, true) => 's', // executable and setuid
(false, true) => 'S', // setuid
(true, false) => 'x', // executable
(false, false) => '-',
})?;

// Group permissions
f.write_char(if mode & libc::S_IRGRP != 0 { 'r' } else { '-' })?;
f.write_char(if mode & libc::S_IWGRP != 0 { 'w' } else { '-' })?;
f.write_char(match (mode & libc::S_IXGRP != 0, mode & libc::S_ISGID != 0) {
(true, true) => 's', // executable and setgid
(false, true) => 'S', // setgid
(true, false) => 'x', // executable
(false, false) => '-',
})?;

// Other permissions
f.write_char(if mode & libc::S_IROTH != 0 { 'r' } else { '-' })?;
f.write_char(if mode & libc::S_IWOTH != 0 { 'w' } else { '-' })?;
f.write_char(if mode & libc::S_IXOTH != 0 { 'x' } else { '-' })?;

f.write_char(')')
}
}

pub fn readdir(path: &Path) -> io::Result<ReadDir> {
let ptr = run_path_with_cstr(path, &|p| unsafe { Ok(libc::opendir(p.as_ptr())) })?;
if ptr.is_null() {
Expand Down
59 changes: 59 additions & 0 deletions library/std/src/sys/pal/unix/fs/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
use crate::sys::pal::unix::fs::FilePermissions;
use crate::sys_common::FromInner;

#[test]
fn test_debug_permissions() {
for (expected, mode) in [
// typical directory
("FilePermissions { mode: 0o040775 (drwxrwxr-x) }", 0o04_0775),
// typical text file
("FilePermissions { mode: 0o100664 (-rw-rw-r--) }", 0o10_0664),
// setuid executable (/usr/bin/doas)
("FilePermissions { mode: 0o104755 (-rwsr-xr-x) }", 0o10_4755),
// char device (/dev/zero)
("FilePermissions { mode: 0o020666 (crw-rw-rw-) }", 0o02_0666),
// block device (/dev/vda)
("FilePermissions { mode: 0o060660 (brw-rw----) }", 0o06_0660),
// symbolic link
("FilePermissions { mode: 0o120777 (lrwxrwxrwx) }", 0o12_0777),
// fifo
("FilePermissions { mode: 0o010664 (prw-rw-r--) }", 0o01_0664),
// none
("FilePermissions { mode: 0o100000 (----------) }", 0o10_0000),
// unrecognized
("FilePermissions { mode: 0o000001 }", 1),
] {
assert_eq!(format!("{:?}", FilePermissions::from_inner(mode)), expected);
}

for (expected, mode) in [
// owner readable
("FilePermissions { mode: 0o100400 (-r--------) }", libc::S_IRUSR),
// owner writable
("FilePermissions { mode: 0o100200 (--w-------) }", libc::S_IWUSR),
// owner executable
("FilePermissions { mode: 0o100100 (---x------) }", libc::S_IXUSR),
// setuid
("FilePermissions { mode: 0o104000 (---S------) }", libc::S_ISUID),
// owner executable and setuid
("FilePermissions { mode: 0o104100 (---s------) }", libc::S_IXUSR | libc::S_ISUID),
// group readable
("FilePermissions { mode: 0o100040 (----r-----) }", libc::S_IRGRP),
// group writable
("FilePermissions { mode: 0o100020 (-----w----) }", libc::S_IWGRP),
// group executable
("FilePermissions { mode: 0o100010 (------x---) }", libc::S_IXGRP),
// setgid
("FilePermissions { mode: 0o102000 (------S---) }", libc::S_ISGID),
// group executable and setgid
("FilePermissions { mode: 0o102010 (------s---) }", libc::S_IXGRP | libc::S_ISGID),
// other readable
("FilePermissions { mode: 0o100004 (-------r--) }", libc::S_IROTH),
// other writeable
("FilePermissions { mode: 0o100002 (--------w-) }", libc::S_IWOTH),
// other executable
("FilePermissions { mode: 0o100001 (---------x) }", libc::S_IXOTH),
] {
assert_eq!(format!("{:?}", FilePermissions::from_inner(libc::S_IFREG | mode)), expected);
}
}

0 comments on commit f955b6f

Please sign in to comment.