diff --git a/src/directory/mmap_directory.rs b/src/directory/mmap_directory.rs index 05927a813..e8fce769c 100644 --- a/src/directory/mmap_directory.rs +++ b/src/directory/mmap_directory.rs @@ -18,6 +18,9 @@ use std::fs::OpenOptions; use directory::error::{OpenWriteError, FileError, OpenDirectoryError}; use std::result; use common::make_io_err; +use std::fs; +use directory::shared_vec_slice::SharedVecSlice; + /// Directory storing data in files, read via MMap. /// @@ -127,8 +130,11 @@ impl Seek for SafeFileWriter { impl Directory for MmapDirectory { + + fn open_read(&self, path: &Path) -> result::Result { let full_path = self.resolve_path(path); + let mut mmap_cache = try!( self.mmap_cache .write() @@ -136,14 +142,14 @@ impl Directory for MmapDirectory { make_io_err(format!("Failed to acquired write lock on mmap cache while reading {:?}", path)) }) ); + let mmap = match mmap_cache.entry(full_path.clone()) { HashMapEntry::Occupied(e) => { e.get().clone() } HashMapEntry::Vacant(vacant_entry) => { - let new_mmap = try!( - MmapReadOnly::open_path(full_path.clone()) - .map_err(|err| { + let file = try!( + File::open(&full_path).map_err(|err| { if err.kind() == io::ErrorKind::NotFound { FileError::FileDoesNotExist(full_path.clone()) } @@ -152,10 +158,17 @@ impl Directory for MmapDirectory { } }) ); + if try!(file.metadata()).len() == 0 { + // if the file size is 0, it will not be possible + // to mmap the file, so we return an anonymous mmap_cache + // instead. + return Ok(ReadOnlySource::Anonymous(SharedVecSlice::empty())) + } + let new_mmap = try!(MmapReadOnly::open(&file)); vacant_entry.insert(new_mmap.clone()); new_mmap } - }; + }; Ok(ReadOnlySource::Mmap(mmap)) } @@ -166,7 +179,7 @@ impl Directory for MmapDirectory { .write(true) .create_new(true) .open(full_path); - + let mut file = try!( open_res.map_err(|err| { if err.kind() == io::ErrorKind::AlreadyExists { @@ -177,7 +190,7 @@ impl Directory for MmapDirectory { } }) ); - + // making sure the file is created. try!(file.flush()); @@ -197,16 +210,13 @@ impl Directory for MmapDirectory { make_io_err(format!("Failed to acquired write lock on mmap cache while deleting {:?}", path)) }) ); - match mmap_cache.remove(&full_path) { - Some(_) => { - // it will be munmapped on drop, - // when the last reference is gone. - Ok(()) - } - None => { - Err(FileError::FileDoesNotExist(PathBuf::from(path))) - } - } + // Removing the entry in the MMap cache. + // The munmap will appear on Drop, + // when the last reference is gone. + mmap_cache.remove(&full_path); + try!(fs::remove_file(&full_path)); + try!(self.sync_directory()); + Ok(()) } fn atomic_write(&mut self, path: &Path, data: &[u8]) -> io::Result<()> { diff --git a/src/directory/mod.rs b/src/directory/mod.rs index b0b0ce99b..73091c2ed 100644 --- a/src/directory/mod.rs +++ b/src/directory/mod.rs @@ -28,6 +28,10 @@ mod tests { use std::path::Path; use std::io::SeekFrom; + lazy_static! { + static ref TEST_PATH: &'static Path = Path::new("some_path_for_test"); + } + #[test] fn test_ram_directory() { let mut ram_directory = RAMDirectory::create(); @@ -40,48 +44,85 @@ mod tests { test_directory(&mut mmap_directory); } - fn test_directory_simple(directory: &mut Directory) { + #[test] + #[should_panic] + fn ram_directory_panics_if_flush_forgotten() { + let mut ram_directory = RAMDirectory::create(); + let mut write_file = ram_directory.open_write(*TEST_PATH).unwrap(); + assert!(write_file.write_all(&[4]).is_ok()); + } + + fn test_simple(directory: &mut Directory) { { - let mut write_file = directory.open_write(Path::new("toto")).unwrap(); + let mut write_file = directory.open_write(*TEST_PATH).unwrap(); write_file.write_all(&[4]).unwrap(); write_file.write_all(&[3]).unwrap(); write_file.write_all(&[7,3,5]).unwrap(); write_file.flush().unwrap(); } - let read_file = directory.open_read(Path::new("toto")).unwrap(); + let read_file = directory.open_read(*TEST_PATH).unwrap(); let data: &[u8] = &*read_file; - assert_eq!(data.len(), 5); - assert_eq!(data[0], 4); - assert_eq!(data[1], 3); - assert_eq!(data[2], 7); - assert_eq!(data[3], 3); - assert_eq!(data[4], 5); + assert_eq!(data, &[4u8, 3u8, 7u8, 3u8, 5u8]); + assert!(directory.delete(*TEST_PATH).is_ok()); } - - fn test_directory_seek(directory: &mut Directory) { + fn test_seek(directory: &mut Directory) { { - let mut write_file = directory.open_write(Path::new("toto_seek")).unwrap(); - write_file.write_all(&[4]).unwrap(); - write_file.write_all(&[3]).unwrap(); - write_file.write_all(&[7,3,5]).unwrap(); + let mut write_file = directory.open_write(*TEST_PATH).unwrap(); + write_file.write_all(&[4, 3, 7,3,5]).unwrap(); write_file.seek(SeekFrom::Start(0)).unwrap(); write_file.write_all(&[3,1]).unwrap(); write_file.flush().unwrap(); } - let read_file = directory.open_read(Path::new("toto_seek")).unwrap(); + let read_file = directory.open_read(*TEST_PATH).unwrap(); let data: &[u8] = &*read_file; - assert_eq!(data.len(), 5); - assert_eq!(data[0], 3); - assert_eq!(data[1], 1); - assert_eq!(data[2], 7); - assert_eq!(data[3], 3); - assert_eq!(data[4], 5); + assert_eq!(data, &[3u8, 1u8, 7u8, 3u8, 5u8]); + assert!(directory.delete(*TEST_PATH).is_ok()); + } + + fn test_rewrite_forbidden(directory: &mut Directory) { + { + directory.open_write(*TEST_PATH).unwrap(); + } + { + assert!(directory.open_write(*TEST_PATH).is_err()); + } + assert!(directory.delete(*TEST_PATH).is_ok()); + } + + fn test_write_create_the_file(directory: &mut Directory) { + { + assert!(directory.open_read(*TEST_PATH).is_err()); + let _w = directory.open_write(*TEST_PATH).unwrap(); + if let Err(e) = directory.open_read(*TEST_PATH) { + println!("{:?}", e); + } + assert!(directory.open_read(*TEST_PATH).is_ok()); + assert!(directory.delete(*TEST_PATH).is_ok()); + } + } + + fn test_delete(directory: &mut Directory) { + assert!(directory.open_read(*TEST_PATH).is_err()); + let mut write_file = directory.open_write(*TEST_PATH).unwrap(); + write_file.write_all(&[1, 2, 3, 4]).unwrap(); + write_file.flush().unwrap(); + let read_handle = directory.open_read(*TEST_PATH).unwrap(); + { + assert_eq!(&*read_handle, &[1u8, 2u8, 3u8, 4u8]); + assert!(directory.delete(*TEST_PATH).is_ok()); + assert!(directory.delete(Path::new("SomeOtherPath")).is_err()); + assert_eq!(&*read_handle, &[1u8, 2u8, 3u8, 4u8]); + } + assert!(directory.open_read(*TEST_PATH).is_err()); } fn test_directory(directory: &mut Directory) { - test_directory_simple(directory); - test_directory_seek(directory); + test_simple(directory); + test_seek(directory); + test_rewrite_forbidden(directory); + test_write_create_the_file(directory); + test_delete(directory); } } diff --git a/src/directory/ram_directory.rs b/src/directory/ram_directory.rs index 672414389..90fece648 100644 --- a/src/directory/ram_directory.rs +++ b/src/directory/ram_directory.rs @@ -34,7 +34,7 @@ impl VecWriter { path: path_buf, data: Cursor::new(Vec::new()), shared_directory: shared_directory, - is_flushed: false, + is_flushed: true, } } }