system_interface/fs/
fd_flags.rs1use bitflags::bitflags;
2use io_lifetimes::{AsFilelike, FromFilelike};
3#[cfg(not(any(windows, target_os = "redox")))]
4use rustix::fs::{fcntl_getfl, fcntl_setfl, OFlags};
5#[cfg(not(windows))]
6use std::marker::PhantomData;
7use std::{fs, io};
8#[cfg(windows)]
9use {
10 cap_fs_ext::{OpenOptions, Reopen},
11 cap_std::fs::OpenOptionsExt,
12 io_lifetimes::AsHandle,
13 windows_sys::Win32::Storage::FileSystem::FILE_FLAG_WRITE_THROUGH,
14 winx::file::{AccessMode, FileModeInformation},
15};
16
17#[cfg(windows)]
20pub struct SetFdFlags<T> {
21 reopened: T,
22}
23
24#[cfg(not(windows))]
27pub struct SetFdFlags<T> {
28 flags: OFlags,
29 _phantom: PhantomData<T>,
30}
31
32pub trait GetSetFdFlags {
34 fn get_fd_flags(&self) -> io::Result<FdFlags>
36 where
37 Self: AsFilelike;
38
39 fn new_set_fd_flags(&self, flags: FdFlags) -> io::Result<SetFdFlags<Self>>
45 where
46 Self: AsFilelike + FromFilelike + Sized;
47
48 fn set_fd_flags(&mut self, set_fd_flags: SetFdFlags<Self>) -> io::Result<()>
52 where
53 Self: AsFilelike + Sized;
54}
55
56bitflags! {
57 pub struct FdFlags: u32 {
59 const APPEND = 0x01;
61
62 const DSYNC = 0x02;
65
66 const NONBLOCK = 0x04;
68
69 const RSYNC = 0x08;
72
73 const SYNC = 0x10;
76 }
77}
78
79#[cfg(not(windows))]
80impl<T> GetSetFdFlags for T {
81 fn get_fd_flags(&self) -> io::Result<FdFlags>
82 where
83 Self: AsFilelike,
84 {
85 let mut fd_flags = FdFlags::empty();
86 let flags = fcntl_getfl(self)?;
87
88 fd_flags.set(FdFlags::APPEND, flags.contains(OFlags::APPEND));
89 #[cfg(not(target_os = "freebsd"))]
90 fd_flags.set(FdFlags::DSYNC, flags.contains(OFlags::DSYNC));
91 fd_flags.set(FdFlags::NONBLOCK, flags.contains(OFlags::NONBLOCK));
92 #[cfg(any(
93 target_os = "ios",
94 target_os = "macos",
95 target_os = "freebsd",
96 target_os = "fuchsia"
97 ))]
98 {
99 fd_flags.set(FdFlags::SYNC, flags.contains(OFlags::SYNC));
100 }
101 #[cfg(not(any(
102 target_os = "ios",
103 target_os = "macos",
104 target_os = "freebsd",
105 target_os = "fuchsia"
106 )))]
107 {
108 fd_flags.set(FdFlags::RSYNC, flags.contains(OFlags::RSYNC));
109 fd_flags.set(FdFlags::SYNC, flags.contains(OFlags::SYNC));
110 }
111
112 Ok(fd_flags)
113 }
114
115 fn new_set_fd_flags(&self, fd_flags: FdFlags) -> io::Result<SetFdFlags<Self>>
116 where
117 Self: AsFilelike,
118 {
119 let mut flags = OFlags::empty();
120 flags.set(OFlags::APPEND, fd_flags.contains(FdFlags::APPEND));
121 flags.set(OFlags::NONBLOCK, fd_flags.contains(FdFlags::NONBLOCK));
122
123 if fd_flags.intersects(FdFlags::DSYNC | FdFlags::SYNC | FdFlags::RSYNC) {
125 return Err(io::Error::new(
126 io::ErrorKind::Other,
127 "setting fd_flags SYNC, DSYNC, and RSYNC is not supported",
128 ));
129 }
130
131 Ok(SetFdFlags {
132 flags,
133 _phantom: PhantomData,
134 })
135 }
136
137 fn set_fd_flags(&mut self, set_fd_flags: SetFdFlags<Self>) -> io::Result<()>
138 where
139 Self: AsFilelike + Sized,
140 {
141 Ok(fcntl_setfl(
142 &*self.as_filelike_view::<fs::File>(),
143 set_fd_flags.flags,
144 )?)
145 }
146}
147
148#[cfg(windows)]
149impl<T> GetSetFdFlags for T {
150 fn get_fd_flags(&self) -> io::Result<FdFlags>
151 where
152 Self: AsFilelike,
153 {
154 let mut fd_flags = FdFlags::empty();
155 let handle = self.as_filelike();
156 let access_mode = winx::file::query_access_information(handle)?;
157 let mode = winx::file::query_mode_information(handle)?;
158
159 if access_mode.contains(AccessMode::FILE_APPEND_DATA)
161 && !access_mode.contains(AccessMode::FILE_WRITE_DATA)
162 {
163 fd_flags |= FdFlags::APPEND;
164 }
165
166 if mode.contains(FileModeInformation::FILE_WRITE_THROUGH) {
167 fd_flags |= FdFlags::DSYNC;
170 }
171
172 Ok(fd_flags)
173 }
174
175 fn new_set_fd_flags(&self, fd_flags: FdFlags) -> io::Result<SetFdFlags<Self>>
176 where
177 Self: AsFilelike + FromFilelike,
178 {
179 let mut flags = 0;
180
181 if fd_flags.contains(FdFlags::SYNC) || fd_flags.contains(FdFlags::DSYNC) {
182 flags |= FILE_FLAG_WRITE_THROUGH;
183 }
184
185 let file = self.as_filelike_view::<fs::File>();
186 let access_mode = winx::file::query_access_information(file.as_handle())?;
187 let new_access_mode = file_access_mode_from_fd_flags(
188 fd_flags,
189 access_mode.contains(AccessMode::FILE_READ_DATA),
190 access_mode.contains(AccessMode::FILE_WRITE_DATA)
191 | access_mode.contains(AccessMode::FILE_APPEND_DATA),
192 );
193 let mut options = OpenOptions::new();
194 options.access_mode(new_access_mode.bits());
195 options.custom_flags(flags);
196 let reopened = Self::from_into_filelike(file.reopen(&options)?);
197 Ok(SetFdFlags { reopened })
198 }
199
200 fn set_fd_flags(&mut self, set_fd_flags: SetFdFlags<Self>) -> io::Result<()>
201 where
202 Self: AsFilelike,
203 {
204 *self = set_fd_flags.reopened;
205 Ok(())
206 }
207}
208
209#[cfg(windows)]
210fn file_access_mode_from_fd_flags(fd_flags: FdFlags, read: bool, write: bool) -> AccessMode {
211 let mut access_mode = AccessMode::READ_CONTROL;
212
213 access_mode.insert(AccessMode::FILE_WRITE_ATTRIBUTES);
216
217 if read {
223 access_mode.insert(AccessMode::FILE_GENERIC_READ);
224 }
225 if write {
226 access_mode.insert(AccessMode::FILE_GENERIC_WRITE);
227 }
228
229 if fd_flags.contains(FdFlags::APPEND) {
233 access_mode.insert(AccessMode::FILE_APPEND_DATA);
234 access_mode.remove(AccessMode::FILE_WRITE_DATA);
235 }
236
237 access_mode
238}