Windows `File::set_permissions` silently does nothing when clearing `readonly` leaves `FileAttributes == 0`

Đang mở Phù hợp với người mới
#165,696 0 bình luận 0 reaction 0 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 1 ngày

@ChrisDenton đang làm issue này rồi.

Từ ngày 10/11/2026.

  • #165701 của @lazerg — đã đóng, không merge
  • #165734 của @ChrisDenton — đang mở

Đánh giá

Độ khó
2/5
Thời gian dự kiến
1-3 giờ
Mức phù hợp với người mới
62/100
Loại issue
Lỗi
Độ rõ ràng
Đặc tả rõ ràng
Mức độ hoạt động
Sôi nổi
Công nghệ
rust
Lĩnh vực
operating-systems

Hướng nghiên cứu

Lỗi nằm ở File::set_permissions trong library/std/src/sys/fs/windows.rs, hàm này truyền perm.attrs thẳng vào FILE_BASIC_INFO cho SetFileInformationByHandle. Hãy tái hiện trên Windows bằng proof-of-concept trong issue, trong đó attrs: 1 phải thành attrs: 128 sau khi gọi hàm. Hoàn thành khi một tệp chỉ đọc bị gỡ thuộc tính qua file handle và kết quả là FILE_ATTRIBUTE_NORMAL cùng readonly=false. Trước tiên, hãy xem pull request đã đóng và chưa được merge #165701 để hiểu vì sao nó không được chấp nhận.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

A-filesystem C-bug O-windows T-libs

Summary

On Windows, calling File::set_permissions with perm.set_readonly(false) returns Ok(()) without clearing the read-only attribute whenever FILE_ATTRIBUTE_READONLY (0x1) is the file's only attribute.

By contrast, path-based fs::set_permissions with the same Permissions value clears the read-only attribute as expected.

Root Cause

On Windows, std::fs::Permissions stores the file's raw 32-bit Win32 FileAttributes bitmask (perm.attrs: u32), and Permissions::set_readonly(false) clears bit 0x1 (self.attrs &= !FILE_ATTRIBUTE_READONLY). When FILE_ATTRIBUTE_READONLY (0x1) was the only attribute set on the file, perm.attrs becomes 0.

In library/std/src/sys/fs/windows.rs, File::set_permissions passes perm.attrs directly to SetFileInformationByHandle via FILE_BASIC_INFO:

pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> {
    let info = c::FILE_BASIC_INFO {
        CreationTime: 0,
        LastAccessTime: 0,
        LastWriteTime: 0,
        ChangeTime: 0,
        FileAttributes: perm.attrs,
    };
    api::set_file_information_by_handle(self.handle.as_raw_handle(), &info).io_result()
}

Per the FILE_BASIC_INFO documentation for FileAttributes:

"If this is set to 0 in a FILE_BASIC_INFO structure passed to SetFileInformationByHandle then none of the attributes are changed."

To clear all attributes on a file, callers must instead pass FILE_ATTRIBUTE_NORMAL (0x80) ("A file that does not have other attributes set. This attribute is valid only when used alone.").

Path-based fs::set_permissions calls Win32 SetFileAttributesW, which normalizes 0 to FILE_ATTRIBUTE_NORMAL (0x80) in user mode before calling the kernel. File::set_permissions constructs FILE_BASIC_INFO directly without this normalization, so perm.attrs == 0 becomes a silent no-op.

Reproduction

run.bat
@echo off
if exist test.txt attrib -r test.txt
echo x > test.txt
attrib -a test.txt

rustc --version
rustc poc.rs || exit /b 1
.\poc.exe
poc.rs
use std::fs::{self, File};

fn main() {
    let file = File::options().write(true).open("test.txt").unwrap();

    let mut perm = file.metadata().unwrap().permissions();
    println!("initial:                            readonly = {}, {perm:?}", perm.readonly());

    // setting readonly with File::set_permissions
    perm.set_readonly(true);
    file.set_permissions(perm).unwrap();
    let mut perm = file.metadata().unwrap().permissions();
    println!("after File::set_permissions(true):  readonly = {}, {perm:?}", perm.readonly());
    assert!(perm.readonly());

    // clearing readonly with File::set_permissions
    perm.set_readonly(false);
    file.set_permissions(perm.clone()).unwrap();
    let after_file = file.metadata().unwrap().permissions();
    println!("after File::set_permissions(false): readonly = {}, {after_file:?}", after_file.readonly());

    // clearing readonly with fs::set_permissions
    fs::set_permissions("test.txt", perm).unwrap();
    let after_fs = file.metadata().unwrap().permissions();
    println!("after fs::set_permissions(false):   readonly = {}, {after_fs:?}", after_fs.readonly());

    assert!(!after_fs.readonly());
    assert!(!after_file.readonly(), "BUG: File::set_permissions did not clear READONLY");
}
Notes on the reproduction setup

Why attrib -a test.txt: Windows automatically sets FILE_ATTRIBUTE_ARCHIVE (0x20) on newly created files. Clearing ARCHIVE with attrib -a leaves test.txt with FILE_ATTRIBUTE_NORMAL (0x80 = 128).

Actual output

file.set_permissions returns Ok(()) while leaving readonly = true (attrs: 1), whereas fs::set_permissions with the same Permissions clears it to attrs: 128:

rustc 1.99.1 (58cf8f9c3 2026-10-13)
initial:                            readonly = false, Permissions(FilePermissions { attrs: 128 })
after File::set_permissions(true):  readonly = true, Permissions(FilePermissions { attrs: 1 })
after File::set_permissions(false): readonly = true, Permissions(FilePermissions { attrs: 1 })
after fs::set_permissions(false):   readonly = false, Permissions(FilePermissions { attrs: 128 })

thread 'main' panicked at poc.rs:28:5:
BUG: File::set_permissions did not clear READONLY
Expected output

file.set_permissions(perm) clears FILE_ATTRIBUTE_READONLY, leaving the file with FILE_ATTRIBUTE_NORMAL (0x80) and readonly=false.

Suggested Fix

In File::set_permissions (library/std/src/sys/fs/windows.rs), normalize 0 to c::FILE_ATTRIBUTE_NORMAL (0x80), matching SetFileAttributesW:

FileAttributes: if perm.attrs == 0 { c::FILE_ATTRIBUTE_NORMAL } else { perm.attrs },

Reporting on behalf of Microsoft FORGE Lab. Initially discovered by an AI agent.

Ngôn ngữ chính
Rust
Star
121k
Fork
17.3k
Merge trung bình
2 ngày 17 giờ
Pull request đã merge (30 ngày)
525

Chuẩn bị môi trường

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của rust-lang/rust

Tất cả issue của rust-lang/rust

Issue tương tự

Thêm issue về Rust

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.