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}