1#![doc(html_root_url = "https://docs.rs/fs2/0.4.3")]
4
5#![cfg_attr(test, feature(test))]
6
7#[cfg(windows)]
8extern crate winapi;
9
10#[cfg(unix)]
11mod unix;
12#[cfg(unix)]
13use unix as sys;
14
15#[cfg(windows)]
16mod windows;
17#[cfg(windows)]
18use windows as sys;
19
20use std::fs::File;
21use std::io::{Error, Result};
22use std::path::Path;
23
24pub trait FileExt {
51
52 fn duplicate(&self) -> Result<File>;
66
67 fn allocated_size(&self) -> Result<u64>;
69
70 fn allocate(&self, len: u64) -> Result<()>;
75
76 fn lock_shared(&self) -> Result<()>;
79
80 fn lock_exclusive(&self) -> Result<()>;
83
84 fn try_lock_shared(&self) -> Result<()>;
87
88 fn try_lock_exclusive(&self) -> Result<()>;
91
92 fn unlock(&self) -> Result<()>;
94}
95
96impl FileExt for File {
97 fn duplicate(&self) -> Result<File> {
98 sys::duplicate(self)
99 }
100 fn allocated_size(&self) -> Result<u64> {
101 sys::allocated_size(self)
102 }
103 fn allocate(&self, len: u64) -> Result<()> {
104 sys::allocate(self, len)
105 }
106 fn lock_shared(&self) -> Result<()> {
107 sys::lock_shared(self)
108 }
109 fn lock_exclusive(&self) -> Result<()> {
110 sys::lock_exclusive(self)
111 }
112 fn try_lock_shared(&self) -> Result<()> {
113 sys::try_lock_shared(self)
114 }
115 fn try_lock_exclusive(&self) -> Result<()> {
116 sys::try_lock_exclusive(self)
117 }
118 fn unlock(&self) -> Result<()> {
119 sys::unlock(self)
120 }
121}
122
123pub fn lock_contended_error() -> Error {
126 sys::lock_error()
127}
128
129#[derive(Clone, Debug, PartialEq, Eq, Hash)]
131pub struct FsStats {
132 free_space: u64,
133 available_space: u64,
134 total_space: u64,
135 allocation_granularity: u64,
136}
137
138impl FsStats {
139 pub fn free_space(&self) -> u64 {
142 self.free_space
143 }
144
145 pub fn available_space(&self) -> u64 {
148 self.available_space
149 }
150
151 pub fn total_space(&self) -> u64 {
154 self.total_space
155 }
156
157 pub fn allocation_granularity(&self) -> u64 {
163 self.allocation_granularity
164 }
165}
166
167pub fn statvfs<P>(path: P) -> Result<FsStats> where P: AsRef<Path> {
169 sys::statvfs(path.as_ref())
170}
171
172pub fn free_space<P>(path: P) -> Result<u64> where P: AsRef<Path> {
175 statvfs(path).map(|stat| stat.free_space)
176}
177
178pub fn available_space<P>(path: P) -> Result<u64> where P: AsRef<Path> {
181 statvfs(path).map(|stat| stat.available_space)
182}
183
184pub fn total_space<P>(path: P) -> Result<u64> where P: AsRef<Path> {
187 statvfs(path).map(|stat| stat.total_space)
188}
189
190pub fn allocation_granularity<P>(path: P) -> Result<u64> where P: AsRef<Path> {
196 statvfs(path).map(|stat| stat.allocation_granularity)
197}
198
199#[cfg(test)]
200mod test {
201
202 extern crate tempdir;
203 extern crate test;
204
205 use std::fs;
206 use super::*;
207 use std::io::{Read, Seek, SeekFrom, Write};
208
209 #[test]
211 fn duplicate() {
212 let tempdir = tempdir::TempDir::new("fs2").unwrap();
213 let path = tempdir.path().join("fs2");
214 let mut file1 =
215 fs::OpenOptions::new().read(true).write(true).create(true).open(&path).unwrap();
216 let mut file2 = file1.duplicate().unwrap();
217
218 file1.write_all(b"foo").unwrap();
220 drop(file1);
221
222 let mut buf = vec![];
223
224 file2.read_to_end(&mut buf).unwrap();
226 assert_eq!(0, buf.len());
227
228 file2.seek(SeekFrom::Start(0)).unwrap();
230 file2.read_to_end(&mut buf).unwrap();
231 assert_eq!(&buf, &b"foo");
232 }
233
234 #[test]
236 fn lock_shared() {
237 let tempdir = tempdir::TempDir::new("fs2").unwrap();
238 let path = tempdir.path().join("fs2");
239 let file1 = fs::OpenOptions::new().read(true).write(true).create(true).open(&path).unwrap();
240 let file2 = fs::OpenOptions::new().read(true).write(true).create(true).open(&path).unwrap();
241 let file3 = fs::OpenOptions::new().read(true).write(true).create(true).open(&path).unwrap();
242
243 file1.lock_shared().unwrap();
245 file2.lock_shared().unwrap();
246 assert_eq!(file3.try_lock_exclusive().unwrap_err().kind(),
247 lock_contended_error().kind());
248 file1.unlock().unwrap();
249 assert_eq!(file3.try_lock_exclusive().unwrap_err().kind(),
250 lock_contended_error().kind());
251
252 file2.unlock().unwrap();
254 file3.lock_exclusive().unwrap();
255 }
256
257 #[test]
259 fn lock_exclusive() {
260 let tempdir = tempdir::TempDir::new("fs2").unwrap();
261 let path = tempdir.path().join("fs2");
262 let file1 = fs::OpenOptions::new().read(true).write(true).create(true).open(&path).unwrap();
263 let file2 = fs::OpenOptions::new().read(true).write(true).create(true).open(&path).unwrap();
264
265 file1.lock_exclusive().unwrap();
267 assert_eq!(file2.try_lock_exclusive().unwrap_err().kind(),
268 lock_contended_error().kind());
269 assert_eq!(file2.try_lock_shared().unwrap_err().kind(),
270 lock_contended_error().kind());
271
272 file1.unlock().unwrap();
274 file2.lock_exclusive().unwrap();
275 }
276
277 #[test]
279 fn lock_cleanup() {
280 let tempdir = tempdir::TempDir::new("fs2").unwrap();
281 let path = tempdir.path().join("fs2");
282 let file1 = fs::OpenOptions::new().read(true).write(true).create(true).open(&path).unwrap();
283 let file2 = fs::OpenOptions::new().read(true).write(true).create(true).open(&path).unwrap();
284
285 file1.lock_exclusive().unwrap();
286 assert_eq!(file2.try_lock_shared().unwrap_err().kind(),
287 lock_contended_error().kind());
288
289 drop(file1);
291 file2.lock_shared().unwrap();
292 }
293
294 #[test]
296 fn allocate() {
297 let tempdir = tempdir::TempDir::new("fs2").unwrap();
298 let path = tempdir.path().join("fs2");
299 let file = fs::OpenOptions::new().write(true).create(true).open(&path).unwrap();
300 let blksize = allocation_granularity(&path).unwrap();
301
302 assert_eq!(0, file.allocated_size().unwrap());
304 assert_eq!(0, file.metadata().unwrap().len());
305
306 file.allocate(2 * blksize - 1).unwrap();
310 assert_eq!(2 * blksize, file.allocated_size().unwrap());
311 assert_eq!(2 * blksize - 1, file.metadata().unwrap().len());
312
313 file.set_len(blksize + 1).unwrap();
317 assert_eq!(2 * blksize, file.allocated_size().unwrap());
318 assert_eq!(blksize + 1, file.metadata().unwrap().len());
319 }
320
321 #[test]
323 fn filesystem_space() {
324 let tempdir = tempdir::TempDir::new("fs2").unwrap();
325 let total_space = total_space(&tempdir.path()).unwrap();
326 let free_space = free_space(&tempdir.path()).unwrap();
327 let available_space = available_space(&tempdir.path()).unwrap();
328
329 assert!(total_space > free_space);
330 assert!(total_space > available_space);
331 assert!(available_space <= free_space);
332 }
333
334 #[bench]
337 fn bench_file_create(b: &mut test::Bencher) {
338 let tempdir = tempdir::TempDir::new("fs2").unwrap();
339 let path = tempdir.path().join("file");
340
341 b.iter(|| {
342 fs::OpenOptions::new()
343 .read(true)
344 .write(true)
345 .create(true)
346 .open(&path)
347 .unwrap();
348 fs::remove_file(&path).unwrap();
349 });
350 }
351
352 #[bench]
354 fn bench_file_truncate(b: &mut test::Bencher) {
355 let size = 32 * 1024 * 1024;
356 let tempdir = tempdir::TempDir::new("fs2").unwrap();
357 let path = tempdir.path().join("file");
358
359 b.iter(|| {
360 let file = fs::OpenOptions::new()
361 .read(true)
362 .write(true)
363 .create(true)
364 .open(&path)
365 .unwrap();
366 file.set_len(size).unwrap();
367 fs::remove_file(&path).unwrap();
368 });
369 }
370
371 #[bench]
373 fn bench_file_allocate(b: &mut test::Bencher) {
374 let size = 32 * 1024 * 1024;
375 let tempdir = tempdir::TempDir::new("fs2").unwrap();
376 let path = tempdir.path().join("file");
377
378 b.iter(|| {
379 let file = fs::OpenOptions::new()
380 .read(true)
381 .write(true)
382 .create(true)
383 .open(&path)
384 .unwrap();
385 file.allocate(size).unwrap();
386 fs::remove_file(&path).unwrap();
387 });
388 }
389
390 #[bench]
392 fn bench_allocated_size(b: &mut test::Bencher) {
393 let size = 32 * 1024 * 1024;
394 let tempdir = tempdir::TempDir::new("fs2").unwrap();
395 let path = tempdir.path().join("file");
396 let file = fs::OpenOptions::new()
397 .read(true)
398 .write(true)
399 .create(true)
400 .open(&path)
401 .unwrap();
402 file.allocate(size).unwrap();
403
404 b.iter(|| {
405 file.allocated_size().unwrap();
406 });
407 }
408
409 #[bench]
411 fn bench_duplicate(b: &mut test::Bencher) {
412 let tempdir = tempdir::TempDir::new("fs2").unwrap();
413 let path = tempdir.path().join("fs2");
414 let file = fs::OpenOptions::new().read(true).write(true).create(true).open(&path).unwrap();
415
416 b.iter(|| test::black_box(file.duplicate().unwrap()));
417 }
418
419 #[bench]
421 fn bench_lock_unlock(b: &mut test::Bencher) {
422 let tempdir = tempdir::TempDir::new("fs2").unwrap();
423 let path = tempdir.path().join("fs2");
424 let file = fs::OpenOptions::new().read(true).write(true).create(true).open(&path).unwrap();
425
426 b.iter(|| {
427 file.lock_exclusive().unwrap();
428 file.unlock().unwrap();
429 });
430 }
431
432 #[bench]
434 fn bench_free_space(b: &mut test::Bencher) {
435 let tempdir = tempdir::TempDir::new("fs2").unwrap();
436 b.iter(|| {
437 test::black_box(free_space(&tempdir.path()).unwrap());
438 });
439 }
440
441 #[bench]
443 fn bench_available_space(b: &mut test::Bencher) {
444 let tempdir = tempdir::TempDir::new("fs2").unwrap();
445 b.iter(|| {
446 test::black_box(available_space(&tempdir.path()).unwrap());
447 });
448 }
449
450 #[bench]
452 fn bench_total_space(b: &mut test::Bencher) {
453 let tempdir = tempdir::TempDir::new("fs2").unwrap();
454 b.iter(|| {
455 test::black_box(total_space(&tempdir.path()).unwrap());
456 });
457 }
458}