Skip to content

Use Mode instead of mode_t in std::fs fields #123127

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 115 additions & 25 deletions library/std/src/sys/pal/unix/fs.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
// 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::ops::{BitAnd, BitAndAssign, BitOrAssign};
use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd};
use crate::path::{Path, PathBuf};
use crate::ptr;
Expand Down Expand Up @@ -365,12 +369,12 @@ pub struct OpenOptions {
create_new: bool,
// system-specific
custom_flags: i32,
mode: mode_t,
mode: Mode,
}

#[derive(Clone, PartialEq, Eq, Debug)]
pub struct FilePermissions {
mode: mode_t,
mode: Mode,
}

#[derive(Copy, Clone, Debug, Default)]
Expand All @@ -383,7 +387,7 @@ pub struct FileTimes {

#[derive(Copy, Clone, Eq, Debug)]
pub struct FileType {
mode: mode_t,
mode: Mode,
}

impl PartialEq for FileType {
Expand All @@ -400,9 +404,12 @@ impl core::hash::Hash for FileType {

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

#[derive(Copy, Clone, PartialEq, Eq)]
struct Mode(mode_t);

cfg_has_statx! {{
impl FileAttr {
fn from_stat64(stat: stat64) -> Self {
Expand Down Expand Up @@ -452,11 +459,11 @@ impl FileAttr {
self.stat.st_size as u64
}
pub fn perm(&self) -> FilePermissions {
FilePermissions { mode: (self.stat.st_mode as mode_t) }
FilePermissions { mode: Mode(self.stat.st_mode as mode_t) }
}

pub fn file_type(&self) -> FileType {
FileType { mode: self.stat.st_mode as mode_t }
FileType { mode: Mode(self.stat.st_mode as mode_t) }
}
}

Expand Down Expand Up @@ -634,7 +641,7 @@ impl FilePermissions {
}
}
pub fn mode(&self) -> u32 {
self.mode as u32
self.mode.0 as u32
}
}

Expand Down Expand Up @@ -669,13 +676,13 @@ impl FileType {
}

fn masked(&self) -> mode_t {
self.mode & libc::S_IFMT
self.mode.0 & libc::S_IFMT
}
}

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

Expand Down Expand Up @@ -922,13 +929,13 @@ impl DirEntry {
)))]
pub fn file_type(&self) -> io::Result<FileType> {
match self.entry.d_type {
libc::DT_CHR => Ok(FileType { mode: libc::S_IFCHR }),
libc::DT_FIFO => Ok(FileType { mode: libc::S_IFIFO }),
libc::DT_LNK => Ok(FileType { mode: libc::S_IFLNK }),
libc::DT_REG => Ok(FileType { mode: libc::S_IFREG }),
libc::DT_SOCK => Ok(FileType { mode: libc::S_IFSOCK }),
libc::DT_DIR => Ok(FileType { mode: libc::S_IFDIR }),
libc::DT_BLK => Ok(FileType { mode: libc::S_IFBLK }),
libc::DT_CHR => Ok(FileType { mode: Mode(libc::S_IFCHR) }),
libc::DT_FIFO => Ok(FileType { mode: Mode(libc::S_IFIFO) }),
libc::DT_LNK => Ok(FileType { mode: Mode(libc::S_IFLNK) }),
libc::DT_REG => Ok(FileType { mode: Mode(libc::S_IFREG) }),
libc::DT_SOCK => Ok(FileType { mode: Mode(libc::S_IFSOCK) }),
libc::DT_DIR => Ok(FileType { mode: Mode(libc::S_IFDIR) }),
libc::DT_BLK => Ok(FileType { mode: Mode(libc::S_IFBLK) }),
_ => self.metadata().map(|m| m.file_type()),
}
}
Expand Down Expand Up @@ -1050,7 +1057,7 @@ impl OpenOptions {
create_new: false,
// system-specific
custom_flags: 0,
mode: 0o666,
mode: Mode(0o666),
}
}

Expand All @@ -1077,7 +1084,7 @@ impl OpenOptions {
self.custom_flags = flags;
}
pub fn mode(&mut self, mode: u32) {
self.mode = mode as mode_t;
self.mode.0 = mode as mode_t;
}

fn get_access_mode(&self) -> io::Result<c_int> {
Expand Down Expand Up @@ -1130,7 +1137,7 @@ impl File {
// some platforms (like macOS, where `open64` is actually `open`), `mode_t` is `u16`.
// However, since this is a variadic function, C integer promotion rules mean that on
// the ABI level, this still gets passed as `c_int` (aka `u32` on Unix platforms).
let fd = cvt_r(|| unsafe { open64(path.as_ptr(), flags, opts.mode as c_int) })?;
let fd = cvt_r(|| unsafe { open64(path.as_ptr(), flags, opts.mode.0 as c_int) })?;
Ok(File(unsafe { FileDesc::from_raw_fd(fd) }))
}

Expand Down Expand Up @@ -1294,7 +1301,7 @@ impl File {
}

pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> {
cvt_r(|| unsafe { libc::fchmod(self.as_raw_fd(), perm.mode) })?;
cvt_r(|| unsafe { libc::fchmod(self.as_raw_fd(), perm.mode.0) })?;
Ok(())
}

Expand Down Expand Up @@ -1390,15 +1397,15 @@ impl File {

impl DirBuilder {
pub fn new() -> DirBuilder {
DirBuilder { mode: 0o777 }
DirBuilder { mode: Mode(0o777) }
}

pub fn mkdir(&self, p: &Path) -> io::Result<()> {
run_path_with_cstr(p, &|p| cvt(unsafe { libc::mkdir(p.as_ptr(), self.mode) }).map(|_| ()))
run_path_with_cstr(p, &|p| cvt(unsafe { libc::mkdir(p.as_ptr(), self.mode.0) }).map(|_| ()))
}

pub fn set_mode(&mut self, mode: u32) {
self.mode = mode as mode_t;
self.mode.0 = mode as mode_t;
}
}

Expand Down Expand Up @@ -1574,6 +1581,87 @@ 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(')')
}
}

impl BitAnd<mode_t> for Mode {
type Output = Mode;
fn bitand(self, rhs: mode_t) -> Self::Output {
Mode(self.0 & rhs)
}
}

impl BitAndAssign<mode_t> for Mode {
fn bitand_assign(&mut self, rhs: mode_t) {
self.0 &= rhs;
}
}

impl BitOrAssign<mode_t> for Mode {
fn bitor_assign(&mut self, rhs: mode_t) {
self.0 |= rhs;
}
}

impl PartialEq<mode_t> for Mode {
fn eq(&self, rhs: &mode_t) -> bool {
self.0 == *rhs
}
}

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 All @@ -1598,7 +1686,9 @@ pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
}

pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> {
run_path_with_cstr(p, &|p| cvt_r(|| unsafe { libc::chmod(p.as_ptr(), perm.mode) }).map(|_| ()))
run_path_with_cstr(p, &|p| {
cvt_r(|| unsafe { libc::chmod(p.as_ptr(), perm.mode.0) }).map(|_| ())
})
}

pub fn rmdir(p: &Path) -> io::Result<()> {
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);
}
}
Loading