#![doc(html_root_url = "https://docs.rs/fs2/0.4.3")]
#![cfg_attr(test, feature(test))]
#[cfg(windows)]
extern crate winapi;
#[cfg(unix)]
mod unix;
#[cfg(unix)]
use unix as sys;
#[cfg(windows)]
mod windows;
#[cfg(windows)]
use windows as sys;
use std::fs::File;
use std::io::{Error, Result};
use std::path::Path;
pub trait FileExt {
fn duplicate(&self) -> Result<File>;
fn allocated_size(&self) -> Result<u64>;
fn allocate(&self, len: u64) -> Result<()>;
fn lock_shared(&self) -> Result<()>;
fn lock_exclusive(&self) -> Result<()>;
fn try_lock_shared(&self) -> Result<()>;
fn try_lock_exclusive(&self) -> Result<()>;
fn unlock(&self) -> Result<()>;
}
impl FileExt for File {
fn duplicate(&self) -> Result<File> {
sys::duplicate(self)
}
fn allocated_size(&self) -> Result<u64> {
sys::allocated_size(self)
}
fn allocate(&self, len: u64) -> Result<()> {
sys::allocate(self, len)
}
fn lock_shared(&self) -> Result<()> {
sys::lock_shared(self)
}
fn lock_exclusive(&self) -> Result<()> {
sys::lock_exclusive(self)
}
fn try_lock_shared(&self) -> Result<()> {
sys::try_lock_shared(self)
}
fn try_lock_exclusive(&self) -> Result<()> {
sys::try_lock_exclusive(self)
}
fn unlock(&self) -> Result<()> {
sys::unlock(self)
}
}
pub fn lock_contended_error() -> Error {
sys::lock_error()
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct FsStats {
free_space: u64,
available_space: u64,
total_space: u64,
allocation_granularity: u64,
}
impl FsStats {
pub fn free_space(&self) -> u64 {
self.free_space
}
pub fn available_space(&self) -> u64 {
self.available_space
}
pub fn total_space(&self) -> u64 {
self.total_space
}
pub fn allocation_granularity(&self) -> u64 {
self.allocation_granularity
}
}
pub fn statvfs<P>(path: P) -> Result<FsStats> where P: AsRef<Path> {
sys::statvfs(path.as_ref())
}
pub fn free_space<P>(path: P) -> Result<u64> where P: AsRef<Path> {
statvfs(path).map(|stat| stat.free_space)
}
pub fn available_space<P>(path: P) -> Result<u64> where P: AsRef<Path> {
statvfs(path).map(|stat| stat.available_space)
}
pub fn total_space<P>(path: P) -> Result<u64> where P: AsRef<Path> {
statvfs(path).map(|stat| stat.total_space)
}
pub fn allocation_granularity<P>(path: P) -> Result<u64> where P: AsRef<Path> {
statvfs(path).map(|stat| stat.allocation_granularity)
}
#[cfg(test)]
mod test {
extern crate tempdir;
extern crate test;
use std::fs;
use super::*;
use std::io::{Read, Seek, SeekFrom, Write};
#[test]
fn duplicate() {
let tempdir = tempdir::TempDir::new("fs2").unwrap();
let path = tempdir.path().join("fs2");
let mut file1 =
fs::OpenOptions::new().read(true).write(true).create(true).open(&path).unwrap();
let mut file2 = file1.duplicate().unwrap();
file1.write_all(b"foo").unwrap();
drop(file1);
let mut buf = vec![];
file2.read_to_end(&mut buf).unwrap();
assert_eq!(0, buf.len());
file2.seek(SeekFrom::Start(0)).unwrap();
file2.read_to_end(&mut buf).unwrap();
assert_eq!(&buf, &b"foo");
}
#[test]
fn lock_shared() {
let tempdir = tempdir::TempDir::new("fs2").unwrap();
let path = tempdir.path().join("fs2");
let file1 = fs::OpenOptions::new().read(true).write(true).create(true).open(&path).unwrap();
let file2 = fs::OpenOptions::new().read(true).write(true).create(true).open(&path).unwrap();
let file3 = fs::OpenOptions::new().read(true).write(true).create(true).open(&path).unwrap();
file1.lock_shared().unwrap();
file2.lock_shared().unwrap();
assert_eq!(file3.try_lock_exclusive().unwrap_err().kind(),
lock_contended_error().kind());
file1.unlock().unwrap();
assert_eq!(file3.try_lock_exclusive().unwrap_err().kind(),
lock_contended_error().kind());
file2.unlock().unwrap();
file3.lock_exclusive().unwrap();
}
#[test]
fn lock_exclusive() {
let tempdir = tempdir::TempDir::new("fs2").unwrap();
let path = tempdir.path().join("fs2");
let file1 = fs::OpenOptions::new().read(true).write(true).create(true).open(&path).unwrap();
let file2 = fs::OpenOptions::new().read(true).write(true).create(true).open(&path).unwrap();
file1.lock_exclusive().unwrap();
assert_eq!(file2.try_lock_exclusive().unwrap_err().kind(),
lock_contended_error().kind());
assert_eq!(file2.try_lock_shared().unwrap_err().kind(),
lock_contended_error().kind());
file1.unlock().unwrap();
file2.lock_exclusive().unwrap();
}
#[test]
fn lock_cleanup() {
let tempdir = tempdir::TempDir::new("fs2").unwrap();
let path = tempdir.path().join("fs2");
let file1 = fs::OpenOptions::new().read(true).write(true).create(true).open(&path).unwrap();
let file2 = fs::OpenOptions::new().read(true).write(true).create(true).open(&path).unwrap();
file1.lock_exclusive().unwrap();
assert_eq!(file2.try_lock_shared().unwrap_err().kind(),
lock_contended_error().kind());
drop(file1);
file2.lock_shared().unwrap();
}
#[test]
fn allocate() {
let tempdir = tempdir::TempDir::new("fs2").unwrap();
let path = tempdir.path().join("fs2");
let file = fs::OpenOptions::new().write(true).create(true).open(&path).unwrap();
let blksize = allocation_granularity(&path).unwrap();
assert_eq!(0, file.allocated_size().unwrap());
assert_eq!(0, file.metadata().unwrap().len());
file.allocate(2 * blksize - 1).unwrap();
assert_eq!(2 * blksize, file.allocated_size().unwrap());
assert_eq!(2 * blksize - 1, file.metadata().unwrap().len());
file.set_len(blksize + 1).unwrap();
assert_eq!(2 * blksize, file.allocated_size().unwrap());
assert_eq!(blksize + 1, file.metadata().unwrap().len());
}
#[test]
fn filesystem_space() {
let tempdir = tempdir::TempDir::new("fs2").unwrap();
let total_space = total_space(&tempdir.path()).unwrap();
let free_space = free_space(&tempdir.path()).unwrap();
let available_space = available_space(&tempdir.path()).unwrap();
assert!(total_space > free_space);
assert!(total_space > available_space);
assert!(available_space <= free_space);
}
#[bench]
fn bench_file_create(b: &mut test::Bencher) {
let tempdir = tempdir::TempDir::new("fs2").unwrap();
let path = tempdir.path().join("file");
b.iter(|| {
fs::OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(&path)
.unwrap();
fs::remove_file(&path).unwrap();
});
}
#[bench]
fn bench_file_truncate(b: &mut test::Bencher) {
let size = 32 * 1024 * 1024;
let tempdir = tempdir::TempDir::new("fs2").unwrap();
let path = tempdir.path().join("file");
b.iter(|| {
let file = fs::OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(&path)
.unwrap();
file.set_len(size).unwrap();
fs::remove_file(&path).unwrap();
});
}
#[bench]
fn bench_file_allocate(b: &mut test::Bencher) {
let size = 32 * 1024 * 1024;
let tempdir = tempdir::TempDir::new("fs2").unwrap();
let path = tempdir.path().join("file");
b.iter(|| {
let file = fs::OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(&path)
.unwrap();
file.allocate(size).unwrap();
fs::remove_file(&path).unwrap();
});
}
#[bench]
fn bench_allocated_size(b: &mut test::Bencher) {
let size = 32 * 1024 * 1024;
let tempdir = tempdir::TempDir::new("fs2").unwrap();
let path = tempdir.path().join("file");
let file = fs::OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(&path)
.unwrap();
file.allocate(size).unwrap();
b.iter(|| {
file.allocated_size().unwrap();
});
}
#[bench]
fn bench_duplicate(b: &mut test::Bencher) {
let tempdir = tempdir::TempDir::new("fs2").unwrap();
let path = tempdir.path().join("fs2");
let file = fs::OpenOptions::new().read(true).write(true).create(true).open(&path).unwrap();
b.iter(|| test::black_box(file.duplicate().unwrap()));
}
#[bench]
fn bench_lock_unlock(b: &mut test::Bencher) {
let tempdir = tempdir::TempDir::new("fs2").unwrap();
let path = tempdir.path().join("fs2");
let file = fs::OpenOptions::new().read(true).write(true).create(true).open(&path).unwrap();
b.iter(|| {
file.lock_exclusive().unwrap();
file.unlock().unwrap();
});
}
#[bench]
fn bench_free_space(b: &mut test::Bencher) {
let tempdir = tempdir::TempDir::new("fs2").unwrap();
b.iter(|| {
test::black_box(free_space(&tempdir.path()).unwrap());
});
}
#[bench]
fn bench_available_space(b: &mut test::Bencher) {
let tempdir = tempdir::TempDir::new("fs2").unwrap();
b.iter(|| {
test::black_box(available_space(&tempdir.path()).unwrap());
});
}
#[bench]
fn bench_total_space(b: &mut test::Bencher) {
let tempdir = tempdir::TempDir::new("fs2").unwrap();
b.iter(|| {
test::black_box(total_space(&tempdir.path()).unwrap());
});
}
}