Windows `File::set_permissions` silently does nothing when clearing `readonly` leaves `FileAttributes == 0`
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.
Đá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ả
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
- Không có Dockerfile hay tệp Docker Compose
- Có mẫu pull request
- Đọc hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- 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.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của rust-lang/rust
-
A-run-make relnotes relnotes-needs-review relnotes-tracking-issue T-lang T-libs
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 72/100
Maintainer thường phản hồi trong vòng 1 ngày
-
relnotes relnotes-needs-review relnotes-tracking-issue T-compiler T-libs
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 78/100
Maintainer thường phản hồi trong vòng 1 ngày
-
A-LLVM O-Arm O-UEFI O-x86_64 relnotes relnotes-needs-review relnotes-tracking-issue T-compiler
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 72/100
rust-lang/rust#165504 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
relnotes relnotes-tracking-issue T-libs
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 72/100
Maintainer thường phản hồi trong vòng 1 ngày
-
unclear wording in vec macroCó thể đã có người làm @Suhird đã nhận 7 ngày trước. Đang mởA-docs A-slice-vec T-libs
Độ khó 1/5 1-3 giờ Mức phù hợp với người mới 70/100
rust-lang/rust#165360 · 6 bình luận · 1 người được giao ·
Maintainer thường phản hồi trong vòng 1 ngày
Tất cả issue của rust-lang/rust
Issue tương tự
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 75/100
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
antiguru/interrexx#213 · 2 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
bug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
Maintainer thường phản hồi trong vòng 1 ngày
-
bug CLI
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 77/100
HigherOrderCO/Bend#2120 ·
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 66/100
KNXBench-Labs/KNXBench#47 ·