Fix: Deleting an Entry could leave artifacts in cache
This patch fixes a bug we did not even hit (yet). It is: When deleting an Entry from the store, this could potentially leave artifacts in the cache. Szenario: An Entry, which was loaded (via `Store::get()` for example), gets `Store::delete()`ed twice. The first call would work as expected, but leave the Entry in the Store cache. The second call would then fail, as the Entry is already removed on the FS, but still in the cache. This would fail - which is the right thing to do here - but with the wrong error (with a FileError rather than a FileNotFound error). This patch fixes this. First of all, the appropriate `PathBuf` object is calculated in all cases, as this object is needed to check whether the file is actually there (which could be the case if the Entry is in the cache and if it is not). If the entry is in the cache and is borrowed: error. If not, remove the entry from the cache. Afterwards the file is deleted on disk. If the entry is not in the cache, but the file exists, the file is removed. If the file does not exist: error. Signed-off-by: Matthias Beyer <mail@beyermatthias.de>
This commit is contained in:
parent
9315a23754
commit
b774ac0e67
1 changed files with 32 additions and 29 deletions
|
@ -444,7 +444,7 @@ impl Store {
|
||||||
StoreEntry::new(id, &self.backend)?.get_entry()
|
StoreEntry::new(id, &self.backend)?.get_entry()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Delete an entry
|
/// Delete an entry and the corrosponding file on disk
|
||||||
///
|
///
|
||||||
/// # Return value
|
/// # Return value
|
||||||
///
|
///
|
||||||
|
@ -460,6 +460,12 @@ impl Store {
|
||||||
|
|
||||||
debug!("Deleting id: '{}'", id);
|
debug!("Deleting id: '{}'", id);
|
||||||
|
|
||||||
|
// Small optimization: We need the pathbuf for deleting, but when calling
|
||||||
|
// StoreId::exists(), a PathBuf object gets allocated. So we simply get a
|
||||||
|
// PathBuf here, check whether it is there and if it is, we can re-use it to
|
||||||
|
// delete the filesystem file.
|
||||||
|
let pb = id.clone().into_pathbuf()?;
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut entries = self
|
let mut entries = self
|
||||||
.entries
|
.entries
|
||||||
|
@ -467,44 +473,41 @@ impl Store {
|
||||||
.map_err(|_| SE::from_kind(SEK::LockPoisoned))
|
.map_err(|_| SE::from_kind(SEK::LockPoisoned))
|
||||||
.chain_err(|| SEK::DeleteCallError(id.clone()))?;
|
.chain_err(|| SEK::DeleteCallError(id.clone()))?;
|
||||||
|
|
||||||
// if the entry is currently modified by the user, we cannot drop it
|
let do_remove = match entries.get(&id) {
|
||||||
match entries.get(&id) {
|
Some(e) => if e.is_borrowed() { // entry is currently borrowed, we cannot delete it
|
||||||
|
return Err(SE::from_kind(SEK::IdLocked)).chain_err(|| SEK::DeleteCallError(id));
|
||||||
|
// false
|
||||||
|
} else { // Entry is in the cache
|
||||||
|
// Remove Entry from the cache
|
||||||
|
true
|
||||||
|
},
|
||||||
|
|
||||||
None => {
|
None => {
|
||||||
// The entry is not in the internal cache. But maybe on the filesystem?
|
// The entry is not in the internal cache. But maybe on the filesystem?
|
||||||
debug!("Seems like {:?} is not in the internal cache", id);
|
debug!("Seems like {:?} is not in the internal cache", id);
|
||||||
|
|
||||||
// Small optimization: We need the pathbuf for deleting, but when calling
|
if !self.backend.exists(&pb)? {
|
||||||
// StoreId::exists(), a PathBuf object gets allocated. So we simply get a
|
|
||||||
// PathBuf here, check whether it is there and if it is, we can re-use it to
|
|
||||||
// delete the filesystem file.
|
|
||||||
let pb = id.clone().into_pathbuf()?;
|
|
||||||
|
|
||||||
if self.backend.exists(&pb)? {
|
|
||||||
// looks like we're deleting a not-loaded file from the store.
|
|
||||||
debug!("Seems like {:?} is on the FS", pb);
|
|
||||||
return self.backend.remove_file(&pb)
|
|
||||||
} else {
|
|
||||||
debug!("Seems like {:?} is not even on the FS", pb);
|
debug!("Seems like {:?} is not even on the FS", pb);
|
||||||
return Err(SE::from_kind(SEK::FileNotFound))
|
return Err(SE::from_kind(SEK::FileNotFound))
|
||||||
.chain_err(|| SEK::DeleteCallError(id))
|
.chain_err(|| SEK::DeleteCallError(id))
|
||||||
}
|
} // else { continue }
|
||||||
},
|
|
||||||
Some(e) => if e.is_borrowed() {
|
|
||||||
return Err(SE::from_kind(SEK::IdLocked))
|
|
||||||
.chain_err(|| SEK::DeleteCallError(id))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove the entry first, then the file
|
false
|
||||||
entries.remove(&id);
|
},
|
||||||
let pb = id.clone().with_base(self.path().clone()).into_pathbuf()?;
|
};
|
||||||
let _ = self
|
|
||||||
.backend
|
if do_remove {
|
||||||
.remove_file(&pb)
|
let _ = entries.remove(&id);
|
||||||
.chain_err(|| SEK::FileError)
|
}
|
||||||
.chain_err(|| SEK::DeleteCallError(id))?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debug!("Seems like {:?} is on the FS", pb);
|
||||||
|
let _ = self
|
||||||
|
.backend
|
||||||
|
.remove_file(&pb)
|
||||||
|
.chain_err(|| SEK::FileError)
|
||||||
|
.chain_err(|| SEK::DeleteCallError(id))?;
|
||||||
|
|
||||||
debug!("Deleted");
|
debug!("Deleted");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue