e_utils/fs/options/mod.rs
1#[cfg(unix)]
2mod unix;
3/// windows平台
4use std::fs::{File, OpenOptions};
5use std::io::{Error, Result};
6use std::path::Path;
7#[cfg(unix)]
8use unix as sys;
9
10#[cfg(windows)]
11mod windows;
12
13#[cfg(windows)]
14use windows as sys;
15
16pub use sys::{FileAccess, FileAttribute, FileCustom, FileSecurity, FileShare};
17
18/// Extension trait for `std::fs::File` which provides allocation, duplication and locking methods.
19///
20/// ## Notes on File Locks
21///
22/// This library provides whole-file locks in both shared (read) and exclusive
23/// (read-write) varieties.
24///
25/// File locks are a cross-platform hazard since the file lock APIs exposed by
26/// operating system kernels vary in subtle and not-so-subtle ways.
27///
28/// The API exposed by this library can be safely used across platforms as long
29/// as the following rules are followed:
30///
31/// * Multiple locks should not be created on an individual `File` instance
32/// concurrently.
33/// * Duplicated files should not be locked without great care.
34/// * Files to be locked should be opened with at least read or write
35/// permissions.
36/// * File locks may only be relied upon to be advisory.
37///
38/// See the tests in `lib.rs` for cross-platform lock behavior that may be
39/// relied upon; see the tests in `unix.rs` and `windows.rs` for examples of
40/// platform-specific behavior. File locks are implemented with
41/// [`flock(2)`](http://man7.org/linux/man-pages/man2/flock.2.html) on Unix and
42/// [`LockFile`](https://msdn.microsoft.com/en-us/library/windows/desktop/aa365202(v=vs.85).aspx)
43/// on Windows.
44pub trait FileExt {
45 /// Returns a duplicate instance of the file.
46 ///
47 /// The returned file will share the same file position as the original
48 /// file.
49 ///
50 /// If using rustc version 1.9 or later, prefer using `File::try_clone` to this.
51 ///
52 /// # Notes
53 ///
54 /// This is implemented with
55 /// [`dup(2)`](http://man7.org/linux/man-pages/man2/dup.2.html) on Unix and
56 /// [`DuplicateHandle`](https://msdn.microsoft.com/en-us/library/windows/desktop/ms724251(v=vs.85).aspx)
57 /// on Windows.
58 fn duplicate(&self) -> Result<File>;
59
60 /// Returns the amount of physical space allocated for a file.
61 fn allocated_size(&self) -> Result<u64>;
62
63 /// Ensures that at least `len` bytes of disk space are allocated for the
64 /// file, and the file size is at least `len` bytes. After a successful call
65 /// to `allocate`, subsequent writes to the file within the specified length
66 /// are guaranteed not to fail because of lack of disk space.
67 fn allocate(&self, len: u64) -> Result<()>;
68
69 /// Locks the file for shared usage, blocking if the file is currently
70 /// locked exclusively.
71 fn lock_shared(&self) -> Result<()>;
72
73 /// Locks the file for exclusive usage, blocking if the file is currently
74 /// locked.
75 fn lock_exclusive(&self) -> Result<()>;
76
77 /// Locks the file for shared usage, or returns a an error if the file is
78 /// currently locked (see `lock_contended_error`).
79 fn try_lock_shared(&self) -> Result<()>;
80
81 /// Locks the file for shared usage, or returns a an error if the file is
82 /// currently locked (see `lock_contended_error`).
83 fn try_lock_exclusive(&self) -> Result<()>;
84
85 /// Unlocks the file.
86 fn unlock(&self) -> Result<()>;
87}
88
89impl FileExt for File {
90 fn duplicate(&self) -> Result<File> {
91 sys::duplicate(self)
92 }
93 fn allocated_size(&self) -> Result<u64> {
94 sys::allocated_size(self)
95 }
96 fn allocate(&self, len: u64) -> Result<()> {
97 sys::allocate(self, len)
98 }
99 fn lock_shared(&self) -> Result<()> {
100 sys::lock_shared(self)
101 }
102 fn lock_exclusive(&self) -> Result<()> {
103 sys::lock_exclusive(self)
104 }
105 fn try_lock_shared(&self) -> Result<()> {
106 sys::try_lock_shared(self)
107 }
108 fn try_lock_exclusive(&self) -> Result<()> {
109 sys::try_lock_exclusive(self)
110 }
111 fn unlock(&self) -> Result<()> {
112 sys::unlock(self)
113 }
114}
115
116/// Returns the error that a call to a try lock method on a contended file will
117/// return.
118pub fn lock_contended_error() -> Error {
119 sys::lock_error()
120}
121
122/// `FsStats` contains some common stats about a file system.
123#[derive(Clone, Debug, PartialEq, Eq, Hash)]
124pub struct FsStats {
125 free_space: u64,
126 available_space: u64,
127 total_space: u64,
128 allocation_granularity: u64,
129}
130
131impl FsStats {
132 /// Returns the number of free bytes in the file system containing the provided
133 /// path.
134 pub fn free_space(&self) -> u64 {
135 self.free_space
136 }
137
138 /// Returns the available space in bytes to non-priveleged users in the file
139 /// system containing the provided path.
140 pub fn available_space(&self) -> u64 {
141 self.available_space
142 }
143
144 /// Returns the total space in bytes in the file system containing the provided
145 /// path.
146 pub fn total_space(&self) -> u64 {
147 self.total_space
148 }
149
150 /// Returns the filesystem's disk space allocation granularity in bytes.
151 /// The provided path may be for any file in the filesystem.
152 ///
153 /// On Posix, this is equivalent to the filesystem's block size.
154 /// On Windows, this is equivalent to the filesystem's cluster size.
155 pub fn allocation_granularity(&self) -> u64 {
156 self.allocation_granularity
157 }
158}
159
160/// Get the stats of the file system containing the provided path.
161pub fn statvfs<P>(path: P) -> Result<FsStats>
162where
163 P: AsRef<Path>,
164{
165 sys::statvfs(path.as_ref())
166}
167
168/// Returns the number of free bytes in the file system containing the provided
169/// path.
170pub fn free_space<P>(path: P) -> Result<u64>
171where
172 P: AsRef<Path>,
173{
174 statvfs(path).map(|stat| stat.free_space)
175}
176
177/// Returns the available space in bytes to non-priveleged users in the file
178/// system containing the provided path.
179pub fn available_space<P>(path: P) -> Result<u64>
180where
181 P: AsRef<Path>,
182{
183 statvfs(path).map(|stat| stat.available_space)
184}
185
186/// Returns the total space in bytes in the file system containing the provided
187/// path.
188pub fn total_space<P>(path: P) -> Result<u64>
189where
190 P: AsRef<Path>,
191{
192 statvfs(path).map(|stat| stat.total_space)
193}
194
195/// Returns the filesystem's disk space allocation granularity in bytes.
196/// The provided path may be for any file in the filesystem.
197///
198/// On Posix, this is equivalent to the filesystem's block size.
199/// On Windows, this is equivalent to the filesystem's cluster size.
200pub fn allocation_granularity<P>(path: P) -> Result<u64>
201where
202 P: AsRef<Path>,
203{
204 statvfs(path).map(|stat| stat.allocation_granularity)
205}
206
207/// Extension trait for `std::fs::OpenOptions` which provides allocation, duplication and locking methods.
208pub trait FileOptionsExt {
209 /// # 锁共享
210 /// ```rust
211 /// use e_utils::{
212 /// fs::{FileShare, FileExt, FileOptionsExt},
213 /// parse::AutoPath as _,
214 /// system::time::{TimezoneStrategy, LOG_FILE_DATE_FORMAT},
215 /// };
216 /// use std::{
217 /// fs,
218 /// io::{self, Write},
219 /// path::Path,
220 /// time::Duration,
221 /// };
222 /// /// 只读不可删和写入
223 /// fn main() -> Result<(), io::Error> {
224 /// let now = TimezoneStrategy::UseUtc
225 /// .get_now()
226 /// .format(LOG_FILE_DATE_FORMAT);
227 /// let fpath = Path::new("logs").join(format!("test.{}.log", now));
228 /// fpath.auto_create_dir().unwrap();
229 /// let mut f = fs::OpenOptions::new()
230 /// .create(true)
231 /// .write(true)
232 /// .read(true)
233 /// .append(true)
234 /// .lock_share(FileShare::Read)
235 /// .open(&fpath)?;
236 /// f.write("locked\n".as_bytes())?;
237 /// for i in 0..30 {
238 /// let _ = f
239 /// .write(format!("{i}.locke write\n").as_bytes())
240 /// .inspect_err(|e| eprintln!("{}", e));
241 /// if i > 20 {
242 /// let _ = f.unlock();
243 /// }
244 /// let x = f.allocated_size()?;
245 /// println!("allocated -> {x}");
246 /// std::thread::sleep(Duration::from_millis(500));
247 /// }
248 /// Ok(())
249 /// }
250 /// ```
251 fn lock_share(&mut self, mode: FileShare) -> &mut Self;
252 /// # 接入锁
253 /// ```rust
254 /// use e_utils::{
255 /// fs::{FileAccess, FileExt, FileOptionsExt},
256 /// parse::AutoPath as _,
257 /// system::time::{TimezoneStrategy, LOG_FILE_DATE_FORMAT},
258 /// };
259 /// use std::{
260 /// fs,
261 /// io::{self, Write},
262 /// path::Path,
263 /// time::Duration,
264 /// };
265 /// /// 关闭时删除文件
266 /// fn main() -> Result<(), io::Error> {
267 /// let now = TimezoneStrategy::UseUtc
268 /// .get_now()
269 /// .format(LOG_FILE_DATE_FORMAT);
270 /// let fpath = Path::new("logs").join(format!("test.{}.log", now));
271 /// fpath.auto_create_dir().unwrap();
272 /// let mut f = fs::OpenOptions::new()
273 /// .create(true)
274 /// .write(true)
275 /// .read(true)
276 /// .append(true)
277 /// .lock_access(FileAccess::Empty)
278 /// .open(&fpath)?;
279 /// f.write("locked\n".as_bytes())?;
280 /// for i in 0..30 {
281 /// let _ = f
282 /// .write(format!("{i}.locke write\n").as_bytes())
283 /// .inspect_err(|e| eprintln!("{}", e));
284 /// if i > 20 {
285 /// let _ = f.unlock();
286 /// }
287 /// let x = f.allocated_size()?;
288 /// println!("allocated -> {x}");
289 /// std::thread::sleep(Duration::from_millis(500));
290 /// }
291 /// Ok(())
292 /// }
293 /// ```
294 fn lock_access(&mut self, access: FileAccess) -> &mut Self;
295 /// # 自定义标识 Example
296 /// ```rust
297 /// use e_utils::{
298 /// fs::{FileCustom, FileExt, FileOptionsExt},
299 /// parse::AutoPath as _,
300 /// system::time::{TimezoneStrategy, LOG_FILE_DATE_FORMAT},
301 /// };
302 /// use std::{
303 /// fs,
304 /// io::{self, Write},
305 /// path::Path,
306 /// time::Duration,
307 /// };
308 /// /// 关闭时删除文件
309 /// fn main() -> Result<(), io::Error> {
310 /// let now = TimezoneStrategy::UseUtc
311 /// .get_now()
312 /// .format(LOG_FILE_DATE_FORMAT);
313 /// let fpath = Path::new("logs").join(format!("test.{}.log", now));
314 /// fpath.auto_create_dir().unwrap();
315 /// let mut f = fs::OpenOptions::new()
316 /// .create(true)
317 /// .write(true)
318 /// .read(true)
319 /// .append(true)
320 /// .custom_flags2([
321 /// FileCustom::DeleteOnClose,
322 /// FileCustom::WriteThrough,
323 /// FileCustom::RandomAccess,
324 /// ])
325 /// .open(&fpath)?;
326 /// f.write("locked\n".as_bytes())?;
327 /// for i in 0..30 {
328 /// let _ = f
329 /// .write(format!("{i}.locke write\n").as_bytes())
330 /// .inspect_err(|e| eprintln!("{}", e));
331 /// if i > 20 {
332 /// let _ = f.unlock();
333 /// }
334 /// let x = f.allocated_size()?;
335 /// println!("allocated -> {x}");
336 /// std::thread::sleep(Duration::from_millis(500));
337 /// }
338 /// Ok(())
339 /// }
340 /// ```
341 fn custom_flags2(&mut self, flags: impl IntoIterator<Item = FileCustom>) -> &mut Self;
342 /// # 属性 Example
343 /// ```rust
344 /// use e_utils::{
345 /// fs::{FileAttribute, FileExt, FileOptionsExt},
346 /// parse::AutoPath as _,
347 /// system::time::{TimezoneStrategy, LOG_FILE_DATE_FORMAT},
348 /// };
349 /// use std::{
350 /// fs,
351 /// io::{self, Write},
352 /// path::Path,
353 /// time::Duration,
354 /// };
355 /// /// 隐藏文件
356 /// fn main() -> Result<(), io::Error> {
357 /// let now = TimezoneStrategy::UseUtc
358 /// .get_now()
359 /// .format(LOG_FILE_DATE_FORMAT);
360 /// let fpath = Path::new("logs").join(format!(".test.{}.log", now));
361 /// fpath.auto_create_dir().unwrap();
362 /// let mut f = fs::OpenOptions::new()
363 /// .create(true)
364 /// .write(true)
365 /// .read(true)
366 /// .append(true)
367 /// .attributes2([FileAttribute::Hidden])
368 /// .open(&fpath)?;
369 /// f.write("locked\n".as_bytes())?;
370 /// for i in 0..30 {
371 /// let _ = f
372 /// .write(format!("{i}.locke write\n").as_bytes())
373 /// .inspect_err(|e| eprintln!("{}", e));
374 /// if i > 20 {
375 /// let _ = f.unlock();
376 /// }
377 /// let x = f.allocated_size()?;
378 /// println!("allocated -> {x}");
379 /// std::thread::sleep(Duration::from_millis(500));
380 /// }
381 /// Ok(())
382 /// }
383 /// ```
384 fn attributes2(&mut self, attributes: impl IntoIterator<Item = FileAttribute>) -> &mut Self;
385 /// # 安全QOS标识 Example
386 /// ```rust
387 /// use e_utils::{
388 /// fs::{FileSecurity, FileExt, FileOptionsExt},
389 /// parse::AutoPath as _,
390 /// system::time::{TimezoneStrategy, LOG_FILE_DATE_FORMAT},
391 /// };
392 /// use std::{
393 /// fs,
394 /// io::{self, Write},
395 /// path::Path,
396 /// time::Duration,
397 /// };
398 /// fn main() -> Result<(), io::Error> {
399 /// let now = TimezoneStrategy::UseUtc
400 /// .get_now()
401 /// .format(LOG_FILE_DATE_FORMAT);
402 /// let fpath = Path::new("logs").join(format!("test.{}.log", now));
403 /// fpath.auto_create_dir().unwrap();
404 /// let mut f = fs::OpenOptions::new()
405 /// .create(true)
406 /// .write(true)
407 /// .read(true)
408 /// .append(true)
409 /// .security_qos_flags2([FileSecurity::Identification])
410 /// .open(&fpath)?;
411 /// f.write("locked\n".as_bytes())?;
412 /// for i in 0..30 {
413 /// let _ = f
414 /// .write(format!("{i}.locke write\n").as_bytes())
415 /// .inspect_err(|e| eprintln!("{}", e));
416 /// if i > 20 {
417 /// let _ = f.unlock();
418 /// }
419 /// let x = f.allocated_size()?;
420 /// println!("allocated -> {x}");
421 /// std::thread::sleep(Duration::from_millis(500));
422 /// }
423 /// Ok(())
424 /// }
425 /// ```
426 fn security_qos_flags2(&mut self, flags: impl IntoIterator<Item = FileSecurity>) -> &mut Self;
427}
428#[cfg(target_os = "windows")]
429impl FileOptionsExt for OpenOptions {
430 fn lock_share(&mut self, mode: FileShare) -> &mut OpenOptions {
431 sys::options_lock_share(self, mode)
432 }
433 fn lock_access(&mut self, access: FileAccess) -> &mut Self {
434 sys::options_lock_access(self, access)
435 }
436 fn custom_flags2(&mut self, flags: impl IntoIterator<Item = FileCustom>) -> &mut Self {
437 sys::options_custom_flags(self, flags)
438 }
439 fn attributes2(&mut self, attributes: impl IntoIterator<Item = FileAttribute>) -> &mut Self {
440 sys::options_attributes(self, attributes)
441 }
442 fn security_qos_flags2(&mut self, flags: impl IntoIterator<Item = FileSecurity>) -> &mut Self {
443 sys::options_security_qos_flags(self, flags)
444 }
445}
446
447#[cfg(not(windows))]
448impl FileOptionsExt for OpenOptions {
449 fn lock_share(&mut self, _mode: FileShare) -> &mut OpenOptions {
450 self
451 }
452 fn lock_access(&mut self, _access: FileAccess) -> &mut Self {
453 self
454 }
455 fn custom_flags2(&mut self, _flags: impl IntoIterator<Item = FileCustom>) -> &mut Self {
456 self
457 }
458 fn attributes2(&mut self, _attributes: impl IntoIterator<Item = FileAttribute>) -> &mut Self {
459 self
460 }
461 fn security_qos_flags2(&mut self, _flags: impl IntoIterator<Item = FileSecurity>) -> &mut Self {
462 self
463 }
464}