Skip to content
Closed
Show file tree
Hide file tree
Changes from 4 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
68 changes: 49 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,40 @@ 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;
match (perm.readonly, attr & c::FILE_ATTRIBUTE_READONLY != 0) {
(true, false) =>
if attr & c::FILE_ATTRIBUTE_DIRECTORY != 0 {
// this matches directories, dir symlinks, and junctions.
Err(io::Error::new(io::ErrorKind::PermissionDenied,
"directories can not be read-only"))
} else {
self.set_attributes(attr | c::FILE_ATTRIBUTE_READONLY)
},
(false, true) => self.set_attributes(attr & !c::FILE_ATTRIBUTE_READONLY),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this branch also make sure that the file isn't a directory? Or actually, why not just always return an error if it is a directory?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, yes it should. Thank you!

_ => Ok(()),
}
}

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 +456,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 +504,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 +670,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