Skip to content
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

Small fixes to setting Windows file permissions #31892

Closed
wants to merge 5 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
3 changes: 2 additions & 1 deletion src/libstd/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1426,7 +1426,8 @@ impl Iterator for WalkDir {
/// # Platform-specific behavior
///
/// This function currently corresponds to the `chmod` function on Unix
/// and the `SetFileAttributes` function on Windows.
/// and the `CreateFile`, `GetFileInformationByHandle` and
/// `SetFileInformationByHandle` function on Windows.
/// Note that, this [may change in the future][changes].
/// [changes]: ../io/index.html#platform-specific-behavior
///
Expand Down
12 changes: 10 additions & 2 deletions src/libstd/sys/windows/c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ pub const TRUNCATE_EXISTING: DWORD = 5;
pub const FILE_WRITE_DATA: DWORD = 0x00000002;
pub const FILE_APPEND_DATA: DWORD = 0x00000004;
pub const FILE_WRITE_EA: DWORD = 0x00000010;
pub const FILE_READ_ATTRIBUTES: DWORD = 0x00000080;
pub const FILE_WRITE_ATTRIBUTES: DWORD = 0x00000100;
pub const READ_CONTROL: DWORD = 0x00020000;
pub const SYNCHRONIZE: DWORD = 0x00100000;
Expand Down Expand Up @@ -364,6 +365,15 @@ pub enum FILE_INFO_BY_HANDLE_CLASS {
MaximumFileInfoByHandlesClass
}

#[repr(C)]
pub struct FILE_BASIC_INFO {
pub CreationTime: LARGE_INTEGER,
pub LastAccessTime: LARGE_INTEGER,
pub LastWriteTime: LARGE_INTEGER,
pub ChangeTime: LARGE_INTEGER,
pub FileAttributes: DWORD,
}

#[repr(C)]
pub struct FILE_END_OF_FILE_INFO {
pub EndOfFile: LARGE_INTEGER,
Expand Down Expand Up @@ -855,8 +865,6 @@ extern "system" {
pub fn GetConsoleMode(hConsoleHandle: HANDLE,
lpMode: LPDWORD) -> BOOL;
pub fn RemoveDirectoryW(lpPathName: LPCWSTR) -> BOOL;
pub fn SetFileAttributesW(lpFileName: LPCWSTR,
dwFileAttributes: DWORD) -> BOOL;
pub fn GetFileInformationByHandle(hFile: HANDLE,
lpFileInformation: LPBY_HANDLE_FILE_INFORMATION)
-> BOOL;
Expand Down
74 changes: 55 additions & 19 deletions src/libstd/sys/windows/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ pub struct OpenOptions {
}

#[derive(Clone, PartialEq, Eq, Debug)]
pub struct FilePermissions { attrs: c::DWORD }
pub struct FilePermissions { readonly: bool }

pub struct DirBuilder;

Expand Down Expand Up @@ -333,6 +333,46 @@ impl File {
Ok(newpos as u64)
}

pub fn set_attributes(&self, attr: c::DWORD) -> io::Result<()> {
let mut info = c::FILE_BASIC_INFO {
CreationTime: 0, // do not change
LastAccessTime: 0, // do not change
LastWriteTime: 0, // do not change
ChangeTime: 0, // do not change
FileAttributes: attr,
};
let size = mem::size_of_val(&info);
try!(cvt(unsafe {
c::SetFileInformationByHandle(self.handle.raw(),
c::FileBasicInfo,
&mut info as *mut _ as *mut _,
size as c::DWORD)
}));
Ok(())
}

pub fn set_perm(&self, perm: FilePermissions) -> io::Result<()> {
let attr = try!(self.file_attr()).attributes;
if attr & c::FILE_ATTRIBUTE_DIRECTORY != 0 {
// this matches directories, dir symlinks, and junctions.
if perm.readonly {
Err(io::Error::new(io::ErrorKind::PermissionDenied,
"directories can not be read-only"))
} else {
Ok(()) // no reason to fail, as the result is what is expected.
// (the directory will not be read-only)
}
} else {
if perm.readonly == (attr & c::FILE_ATTRIBUTE_READONLY != 0) {
Ok(())
} else if perm.readonly {
self.set_attributes(attr | c::FILE_ATTRIBUTE_READONLY)
} else {
self.set_attributes(attr & !c::FILE_ATTRIBUTE_READONLY)
}
}
}

pub fn duplicate(&self) -> io::Result<File> {
Ok(File {
handle: try!(self.handle.duplicate(0, true, c::DUPLICATE_SAME_ACCESS)),
Expand Down Expand Up @@ -422,7 +462,12 @@ impl FileAttr {
}

pub fn perm(&self) -> FilePermissions {
FilePermissions { attrs: self.attributes }
FilePermissions {
// Only files can be read-only. If the flag is set on a directory this means the
// directory its view is customized by Windows (with a Desktop.ini file)
readonly: self.attributes & c::FILE_ATTRIBUTE_READONLY != 0 &&
self.attributes & c::FILE_ATTRIBUTE_DIRECTORY == 0
}
}

pub fn attrs(&self) -> u32 { self.attributes as u32 }
Expand Down Expand Up @@ -465,17 +510,8 @@ fn to_u64(ft: &c::FILETIME) -> u64 {
}

impl FilePermissions {
pub fn readonly(&self) -> bool {
self.attrs & c::FILE_ATTRIBUTE_READONLY != 0
}

pub fn set_readonly(&mut self, readonly: bool) {
if readonly {
self.attrs |= c::FILE_ATTRIBUTE_READONLY;
} else {
self.attrs &= !c::FILE_ATTRIBUTE_READONLY;
}
}
pub fn readonly(&self) -> bool { self.readonly }
pub fn set_readonly(&mut self, readonly: bool) { self.readonly = readonly }
}

impl FileType {
Expand Down Expand Up @@ -640,12 +676,12 @@ pub fn lstat(path: &Path) -> io::Result<FileAttr> {
file.file_attr()
}

pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> {
let p = try!(to_u16s(p));
unsafe {
try!(cvt(c::SetFileAttributesW(p.as_ptr(), perm.attrs)));
Ok(())
}
pub fn set_perm(path: &Path, perm: FilePermissions) -> io::Result<()> {
let mut opts = OpenOptions::new();
opts.access_mode(c::FILE_READ_ATTRIBUTES | c::FILE_WRITE_ATTRIBUTES);
opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS);
let file = try!(File::open(path, &opts));
file.set_perm(perm)
}

fn get_path(f: &File) -> io::Result<PathBuf> {
Expand Down