Skip to content

Commit 8213d96

Browse files
author
Luca Bruno
authored
Merge pull request #32 from lucab/ups/fchmodat
OpenatDirExt: add helper for setting permission bits
2 parents b376266 + 0bf3631 commit 8213d96

File tree

1 file changed

+72
-9
lines changed

1 file changed

+72
-9
lines changed

src/lib.rs

Lines changed: 72 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,11 @@ 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+
///
120+
/// If the entry at `path` is a symlink, no action is performed.
121+
fn set_mode<P: openat::AsPath>(&self, path: P, mode: libc::mode_t) -> io::Result<()>;
122+
118123
/// Copy a regular file. The semantics here are intended to match `std::fs::copy()`.
119124
/// If the target exists, it will be overwritten. The mode bits (permissions) will match, but
120125
/// owner/group will be derived from the current process. Extended attributes are not
@@ -379,6 +384,36 @@ impl OpenatDirExt for openat::Dir {
379384
.map_err(map_nix_error))
380385
}
381386

387+
fn set_mode<P: openat::AsPath>(&self, p: P, mode: libc::mode_t) -> io::Result<()> {
388+
use nix::sys::stat::{fchmodat, FchmodatFlags, Mode};
389+
use openat::SimpleType;
390+
391+
let path = p
392+
.to_path()
393+
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "null byte in path"))?;
394+
395+
{
396+
// NOTE(lucab): `AT_SYMLINK_NOFOLLOW` used to short-circuit to `ENOTSUP`
397+
// in older glibc versions, so we don't use it. Instead we try to detect
398+
// any symlink, and skip it.
399+
let entry_meta = self.metadata(path.as_ref())?;
400+
if entry_meta.simple_type() == SimpleType::Symlink {
401+
return Ok(());
402+
};
403+
}
404+
405+
let perms = Mode::from_bits_truncate(mode);
406+
fchmodat(
407+
Some(self.as_raw_fd()),
408+
path.as_ref(),
409+
perms,
410+
FchmodatFlags::FollowSymlink,
411+
)
412+
.map_err(map_nix_error)?;
413+
414+
Ok(())
415+
}
416+
382417
fn new_file_writer<'a>(&'a self, mode: libc::mode_t) -> io::Result<FileWriter> {
383418
let (tmpf, name) = if let Some(tmpf) = self.new_unnamed_file(mode).ok() {
384419
(tmpf, None)
@@ -859,6 +894,15 @@ mod tests {
859894
}
860895
}
861896

897+
#[test]
898+
fn test_syncfs() {
899+
let td = tempfile::tempdir().unwrap();
900+
let d = openat::Dir::open(td.path()).unwrap();
901+
d.ensure_dir_all("foo/bar", 0o755).unwrap();
902+
d.syncfs().unwrap();
903+
assert_eq!(d.exists("foo/bar").unwrap(), true);
904+
}
905+
862906
#[test]
863907
fn test_update_timestamps() {
864908
let td = tempfile::tempdir().unwrap();
@@ -881,6 +925,34 @@ mod tests {
881925
);
882926
}
883927

928+
#[test]
929+
fn test_fchmodat() {
930+
let td = tempfile::tempdir().unwrap();
931+
let d = openat::Dir::open(td.path()).unwrap();
932+
d.ensure_dir("foo", 0o777).unwrap();
933+
d.set_mode("foo", 0o750).unwrap();
934+
assert_eq!(
935+
d.metadata("foo").unwrap().stat().st_mode & !libc::S_IFMT,
936+
0o750
937+
);
938+
d.set_mode("foo", 0o700).unwrap();
939+
assert_eq!(
940+
d.metadata("foo").unwrap().stat().st_mode & !libc::S_IFMT,
941+
0o700
942+
);
943+
944+
d.symlink("bar", "foo").unwrap();
945+
d.set_mode("bar", 0o000).unwrap();
946+
assert_ne!(
947+
d.metadata("bar").unwrap().stat().st_mode & !libc::S_IFMT,
948+
0o000
949+
);
950+
assert_ne!(
951+
d.metadata("foo").unwrap().stat().st_mode & !libc::S_IFMT,
952+
0o000
953+
);
954+
}
955+
884956
fn find_test_file(tempdir: &Path) -> Result<PathBuf> {
885957
for p in ["/proc/self/exe", "/usr/bin/bash"].iter() {
886958
let p = Path::new(p);
@@ -893,15 +965,6 @@ mod tests {
893965
Ok(fallback)
894966
}
895967

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-
905968
#[test]
906969
fn copy_fallback() -> Result<()> {
907970
use std::io::Read;

0 commit comments

Comments
 (0)