Skip to content

Commit fcb3231

Browse files
committed
OpenatDirExt: add helper for setting permission bits
This adds one helper method to set permission bits for a given path.
1 parent b376266 commit fcb3231

File tree

1 file changed

+52
-9
lines changed

1 file changed

+52
-9
lines changed

src/lib.rs

Lines changed: 52 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,9 @@ pub trait OpenatDirExt {
115115
/// following the link.
116116
fn update_timestamps<P: openat::AsPath>(&self, path: P) -> io::Result<()>;
117117

118+
/// Update permissions for the given path (see `fchmodat(2)`).
119+
fn set_mode<P: openat::AsPath>(&self, path: P, mode: libc::mode_t) -> io::Result<()>;
120+
118121
/// Copy a regular file. The semantics here are intended to match `std::fs::copy()`.
119122
/// If the target exists, it will be overwritten. The mode bits (permissions) will match, but
120123
/// owner/group will be derived from the current process. Extended attributes are not
@@ -379,6 +382,27 @@ impl OpenatDirExt for openat::Dir {
379382
.map_err(map_nix_error))
380383
}
381384

385+
fn set_mode<P: openat::AsPath>(&self, p: P, mode: libc::mode_t) -> io::Result<()> {
386+
use nix::sys::stat::{fchmodat, FchmodatFlags, Mode};
387+
388+
let path = p
389+
.to_path()
390+
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "null byte in path"))?;
391+
let perms = Mode::from_bits_truncate(mode);
392+
393+
// NOTE(lucab): `AT_SYMLINK_NOFOLLOW` exists but it is not really implemented
394+
// and short-circuits to `ENOTSUP`.
395+
fchmodat(
396+
Some(self.as_raw_fd()),
397+
path.as_ref(),
398+
perms,
399+
FchmodatFlags::FollowSymlink,
400+
)
401+
.map_err(map_nix_error)?;
402+
403+
Ok(())
404+
}
405+
382406
fn new_file_writer<'a>(&'a self, mode: libc::mode_t) -> io::Result<FileWriter> {
383407
let (tmpf, name) = if let Some(tmpf) = self.new_unnamed_file(mode).ok() {
384408
(tmpf, None)
@@ -859,6 +883,15 @@ mod tests {
859883
}
860884
}
861885

886+
#[test]
887+
fn test_syncfs() {
888+
let td = tempfile::tempdir().unwrap();
889+
let d = openat::Dir::open(td.path()).unwrap();
890+
d.ensure_dir_all("foo/bar", 0o755).unwrap();
891+
d.syncfs().unwrap();
892+
assert_eq!(d.exists("foo/bar").unwrap(), true);
893+
}
894+
862895
#[test]
863896
fn test_update_timestamps() {
864897
let td = tempfile::tempdir().unwrap();
@@ -881,6 +914,25 @@ mod tests {
881914
);
882915
}
883916

917+
#[test]
918+
fn test_fchmodat() {
919+
let td = tempfile::tempdir().unwrap();
920+
let d = openat::Dir::open(td.path()).unwrap();
921+
d.ensure_dir("foo", 0o777).unwrap();
922+
d.set_mode("foo", 0o750).unwrap();
923+
d.syncfs().unwrap();
924+
assert_eq!(
925+
d.metadata("foo").unwrap().stat().st_mode & !libc::S_IFMT,
926+
0o750
927+
);
928+
d.set_mode("foo", 0o700).unwrap();
929+
d.syncfs().unwrap();
930+
assert_eq!(
931+
d.metadata("foo").unwrap().stat().st_mode & !libc::S_IFMT,
932+
0o700
933+
);
934+
}
935+
884936
fn find_test_file(tempdir: &Path) -> Result<PathBuf> {
885937
for p in ["/proc/self/exe", "/usr/bin/bash"].iter() {
886938
let p = Path::new(p);
@@ -893,15 +945,6 @@ mod tests {
893945
Ok(fallback)
894946
}
895947

896-
#[test]
897-
fn test_syncfs() {
898-
let td = tempfile::tempdir().unwrap();
899-
let d = openat::Dir::open(td.path()).unwrap();
900-
d.ensure_dir_all("foo/bar", 0o755).unwrap();
901-
d.syncfs().unwrap();
902-
assert_eq!(d.exists("foo/bar").unwrap(), true);
903-
}
904-
905948
#[test]
906949
fn copy_fallback() -> Result<()> {
907950
use std::io::Read;

0 commit comments

Comments
 (0)