Skip to content

Extend std::fs::Permissions on Windows #388

@E-Mans-Application

Description

@E-Mans-Application

Proposal

Problem statement

I need to set file attributes on Windows, and I found it's exactly what std::fs::set_permissions does. However, struct std::fs::Permissions only exposes attribute readonly. I would also need to set other attributes, such as FILE_ATTRIBUTE_SYSTEM or FILE_ATTRIBUTE_TEMPORARY.
On Linux, there is extension trait std::os::unix::fs::PermissionsExt, but it seems Windows has no such thing.

The attributes given to OpenOptions are only applied to existing files if the files are open with .create(true).truncate(true), which truncates the files. I need to get and set the attributes of an existing file, without changing its contents.

Solution sketch

An extension trait could be added on Windows to expose other attributes. This trait could also provide methods to convert Permissions from and to u32.

In std::sys::pal::windows::fs, extend impl FilePermissions:

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 system(&self) -> bool {
        self.attrs & c::FILE_ATTRIBUTE_SYSTEM != 0
    }

    pub fn set_system(&mut self, system: bool) {
        if readonly {
            self.attrs |= c::FILE_ATTRIBUTE_SYSTEM;
        } else {
            self.attrs &= !c::FILE_ATTRIBUTE_SYSTEM;
        }
    }

    // ...
   // A macro could be used since all the functions will have the same structure

    pub fn to_u32(&self) -> u32 {
        self.attrs
    }
    fn from_u32(mask: u32) -> Self {
         // You may want to check the mask is correct and return an `Option` here, although `SetFileAttributes` seems to ignore invalid attributes.
         Self { attrs: mask }
    }
}

In std::os::windows::fs:

pub trait PermissionsExt {
    #[must_use]
    fn system(&self) -> bool;
    fn set_system(&mut self);

    #[must_use]
    fn temporary(&self) -> bool;
    fn set_temporary(&mut self);

   // Same for other attributes

    fn to_u32(&self) -> u32;
    fn from_u32(mask: u32) -> Self;
}

impl PermissionsExt for Permissions {
    // Delegates all to `as_inner` or `from_inner`, just like `PermissionsExt` is implemented on Unix.
}

Alternatives

Currently, I'm calling system API SetFileAttributesW directly, but this forces me to resort to unsafe code and to re-implement conversion from Path to Vec<u16> (with verbatim support for long paths), in a way that's probably less efficient than the standard library.

Another possibility would be to compute the mask manually and to transmute the result to Permissions (which is basically a u32), but I don't like this solution because there is no guarantee that Permissions will remain equivalent to a u32 in the future.

Links and related work

Initial discussion:
https://internals.rust-lang.org/t/extend-std-permissions-on-windows/20943

Metadata

Metadata

Assignees

No one assigned

    Labels

    ACP-acceptedAPI Change Proposal is accepted (seconded with no objections)T-libs-apiapi-change-proposalA proposal to add or alter unstable APIs in the standard libraries

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions