-
Notifications
You must be signed in to change notification settings - Fork 21
Draft: Call scan_suffixes
when necessary during rotation, if files have been moved/created/deleted outside of this library
#18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
use super::{suffix::*, *}; | ||
use std::iter::FromIterator; | ||
#[cfg(unix)] | ||
use std::os::unix::fs::PermissionsExt; | ||
use tempdir::TempDir; | ||
|
@@ -371,6 +372,84 @@ fn unix_file_permissions() { | |
} | ||
} | ||
|
||
#[test] | ||
fn user_moves_log_files() { | ||
// FileRotate will be able to recover if its memory of what files exist (`self.suffixes`) turns | ||
// out to not be consistent with the files that are actually on disk... User or external | ||
// process has changed the file listing. | ||
|
||
let initial_suffixes = [1, 2, 3]; | ||
|
||
#[derive(Debug)] | ||
enum Action { | ||
Create(usize), | ||
Remove(usize), | ||
} | ||
use Action::*; | ||
|
||
let actions = [Create(4), Remove(1), Remove(2), Remove(3)]; | ||
|
||
for action in actions { | ||
println!("{:?}", action); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please remove this print |
||
let tmp_dir = TempDir::new("file-rotate-test").unwrap(); | ||
let dir = tmp_dir.path(); | ||
let log_path = dir.join("log"); | ||
for suffix in initial_suffixes { | ||
File::create(dir.join(format!("log.{}", suffix))).unwrap(); | ||
} | ||
|
||
let mut log = FileRotate::new( | ||
&log_path, | ||
AppendCount::new(5), | ||
ContentLimit::Bytes(1), | ||
Compression::None, | ||
#[cfg(unix)] | ||
None, | ||
); | ||
Comment on lines
+401
to
+408
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This test looks like 4 different tests to me since we recreate a FileRotate. Could we instead actually make 4 tests (and perhaps just put common code in a function)? This test is a bit hard to read right now. |
||
|
||
match action { | ||
Create(suffix) => { | ||
let name = format!("log.{}", suffix); | ||
File::create(dir.join(name)).unwrap(); | ||
} | ||
Remove(suffix) => { | ||
let name = format!("log.{}", suffix); | ||
std::fs::remove_file(dir.join(name)).unwrap(); | ||
} | ||
} | ||
|
||
write!(log, "12").unwrap(); | ||
|
||
let mut expected_set = BTreeSet::from_iter(initial_suffixes.iter().map(|x| SuffixInfo { | ||
suffix: *x, | ||
compressed: false, | ||
})); | ||
match action { | ||
Create(4) => { | ||
// The one that was created | ||
expected_set.insert(SuffixInfo { | ||
suffix: 4, | ||
compressed: false, | ||
}); | ||
// An additional one due to rotation | ||
expected_set.insert(SuffixInfo { | ||
suffix: 5, | ||
compressed: false, | ||
}); | ||
assert_eq!(log.suffixes, expected_set); | ||
} | ||
// There is only one Create(_) action that is reasonable to test | ||
Create(_) => unreachable!(), | ||
Remove(_) => { | ||
// No matter which file we remove, we would expect rotation to create one | ||
// additional file such that we end up with the same files as we started. | ||
assert_eq!(expected_set, log.suffixes); | ||
} | ||
} | ||
println!("OK"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please remove this print |
||
} | ||
} | ||
|
||
#[quickcheck_macros::quickcheck] | ||
fn arbitrary_lines(count: usize) { | ||
let tmp_dir = TempDir::new("file-rotate-test").unwrap(); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems very racy to me. If the
new_path.exists() == false
, a user can still modify the filesystem before we reachfs::rename
on line 586. What is the intent of the code here? Won't rescanning suffixes just overwrite said file anyway?Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right about the race condition.
Rescanning suffixes does not overwrite anything in the fs, it just recreates
self.suffixes
which is only an internal cache of which log files are on disk. For correct functioning, we rely on the assumption thatself.suffixes
is correct. If it is not correct (user/system has done file ops), and if not for the asserts which up until now have served as the sole line of defense, we risk overwriting previous log files. Becausefn move_file_with_suffix
will only move away the destination file if it thinks it exists (look into the recursive nature of the function - it calls itself beforefs::rename
for this purpose).