Skip to content

Commit ccdc26f

Browse files
authored
Auto merge of #37886 - Stebalien:set-perm, r=alexcrichton
Add a method for setting permissions directly on an open file. On unix like systems, the underlying file corresponding to any given path may change at any time. This function makes it possible to set the permissions of the a file corresponding to a `File` object even if its path changes. @retep998, what's the best way to do this on Windows? I looked into `SetFileInformationByHandle` but couldn't find a way to do it atomically risking clobbering access time information. This is a first step towards fixing #37885. This function doesn't *have* to be public but this is useful functionality that should probably be exposed.
2 parents 5196ca8 + 1aaca5f commit ccdc26f

File tree

4 files changed

+85
-0
lines changed

4 files changed

+85
-0
lines changed

src/libstd/fs.rs

+53
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,41 @@ impl File {
348348
inner: self.inner.duplicate()?
349349
})
350350
}
351+
352+
/// Changes the permissions on the underlying file.
353+
///
354+
/// # Platform-specific behavior
355+
///
356+
/// This function currently corresponds to the `fchmod` function on Unix and
357+
/// the `SetFileInformationByHandle` function on Windows. Note that, this
358+
/// [may change in the future][changes].
359+
///
360+
/// [changes]: ../io/index.html#platform-specific-behavior
361+
///
362+
/// # Errors
363+
///
364+
/// This function will return an error if the user lacks permission change
365+
/// attributes on the underlying file. It may also return an error in other
366+
/// os-specific unspecified cases.
367+
///
368+
/// # Examples
369+
///
370+
/// ```
371+
/// #![feature(set_permissions_atomic)]
372+
/// # fn foo() -> std::io::Result<()> {
373+
/// use std::fs::File;
374+
///
375+
/// let file = File::open("foo.txt")?;
376+
/// let mut perms = file.metadata()?.permissions();
377+
/// perms.set_readonly(true);
378+
/// file.set_permissions(perms)?;
379+
/// # Ok(())
380+
/// # }
381+
/// ```
382+
#[unstable(feature = "set_permissions_atomic", issue="37916")]
383+
pub fn set_permissions(&self, perm: Permissions) -> io::Result<()> {
384+
self.inner.set_permissions(perm.0)
385+
}
351386
}
352387

353388
impl AsInner<fs_imp::File> for File {
@@ -2469,6 +2504,24 @@ mod tests {
24692504
check!(fs::set_permissions(&file, p));
24702505
}
24712506

2507+
#[test]
2508+
fn fchmod_works() {
2509+
let tmpdir = tmpdir();
2510+
let path = tmpdir.join("in.txt");
2511+
2512+
let file = check!(File::create(&path));
2513+
let attr = check!(fs::metadata(&path));
2514+
assert!(!attr.permissions().readonly());
2515+
let mut p = attr.permissions();
2516+
p.set_readonly(true);
2517+
check!(file.set_permissions(p.clone()));
2518+
let attr = check!(fs::metadata(&path));
2519+
assert!(attr.permissions().readonly());
2520+
2521+
p.set_readonly(false);
2522+
check!(file.set_permissions(p));
2523+
}
2524+
24722525
#[test]
24732526
fn sync_doesnt_kill_anything() {
24742527
let tmpdir = tmpdir();

src/libstd/sys/unix/fs.rs

+5
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,11 @@ impl File {
526526
pub fn fd(&self) -> &FileDesc { &self.0 }
527527

528528
pub fn into_fd(self) -> FileDesc { self.0 }
529+
530+
pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> {
531+
cvt_r(|| unsafe { libc::fchmod(self.0.raw(), perm.mode) })?;
532+
Ok(())
533+
}
529534
}
530535

531536
impl DirBuilder {

src/libstd/sys/windows/c.rs

+9
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,15 @@ pub enum FILE_INFO_BY_HANDLE_CLASS {
389389
MaximumFileInfoByHandlesClass
390390
}
391391

392+
#[repr(C)]
393+
pub struct FILE_BASIC_INFO {
394+
pub CreationTime: LARGE_INTEGER,
395+
pub LastAccessTime: LARGE_INTEGER,
396+
pub LastWriteTime: LARGE_INTEGER,
397+
pub ChangeTime: LARGE_INTEGER,
398+
pub FileAttributes: DWORD,
399+
}
400+
392401
#[repr(C)]
393402
pub struct FILE_END_OF_FILE_INFO {
394403
pub EndOfFile: LARGE_INTEGER,

src/libstd/sys/windows/fs.rs

+18
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,24 @@ impl File {
417417
Ok(PathBuf::from(OsString::from_wide(subst)))
418418
}
419419
}
420+
421+
pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> {
422+
let mut info = c::FILE_BASIC_INFO {
423+
CreationTime: 0,
424+
LastAccessTime: 0,
425+
LastWriteTime: 0,
426+
ChangeTime: 0,
427+
FileAttributes: perm.attrs,
428+
};
429+
let size = mem::size_of_val(&info);
430+
cvt(unsafe {
431+
c::SetFileInformationByHandle(self.handle.raw(),
432+
c::FileBasicInfo,
433+
&mut info as *mut _ as *mut _,
434+
size as c::DWORD)
435+
})?;
436+
Ok(())
437+
}
420438
}
421439

422440
impl FromInner<c::HANDLE> for File {

0 commit comments

Comments
 (0)