unify locking of a single file to locking of all files

This commit is contained in:
Dietrich 2020-10-26 08:13:24 +01:00
parent b8c544a846
commit 927e7ba4a7

View File

@ -12,6 +12,7 @@ use crate::api::UserRead;
use log::{debug, error, info, warn}; use log::{debug, error, info, warn};
use std::fs::File; use std::fs::File;
use std::io::{BufReader, Read}; use std::io::{BufReader, Read};
use std::ops::Deref;
use std::path::PathBuf; use std::path::PathBuf;
use std::{collections::HashMap, io::Write}; use std::{collections::HashMap, io::Write};
@ -48,6 +49,15 @@ impl Files {
pub fn lock_and_get_group(&self) -> Result<LockedFileGuard, crate::UserLibError> { pub fn lock_and_get_group(&self) -> Result<LockedFileGuard, crate::UserLibError> {
LockedFileGuard::new(self.group.as_ref().unwrap()) LockedFileGuard::new(self.group.as_ref().unwrap())
} }
pub fn lock_all_get(
&self,
) -> Result<(LockedFileGuard, LockedFileGuard, LockedFileGuard), crate::UserLibError> {
let pwd = self.lock_and_get_passwd()?;
let shd = self.lock_and_get_shadow()?;
let grp = self.lock_and_get_group()?;
Ok((pwd, shd, grp))
}
} }
pub struct LockedFileGuard { pub struct LockedFileGuard {
@ -63,59 +73,82 @@ impl LockedFileGuard {
Err(e) => Err(e), Err(e) => Err(e),
} }
} }
/// This function tries to lock a file in the way other passwd locking mechanisms work.
///
/// * get the pid
/// * create the temporary lockfilepath "/etc/passwd.12397"
/// * create the lockfilepath "/etc/passwd.lock"
/// * open the temporary file
/// * write the pid to the tempfile
/// * try to make a link from the temporary file created to the lockfile
/// * ensure that the file has been linked successfully
///
/// when the link could not be created:
///
/// * Open the lockfile
/// * read the contents of the lockfile
/// * check if the lockfile contains a pid if not error out
/// * check if the containing pid is in a valid format. If not create a matching error
///
/// not implemented yet:
///
/// * test if this process could be killed. If so disclose the pid in the error.
/// * try to delete the lockfile as it is apparently not used by the process anmore. (cleanup)
/// * try to lock again now that the old logfile has been safely removed.
/// * remove the original file and only keep the lock hardlink
fn try_to_lock_file(path: &PathBuf) -> Result<(PathBuf, File), crate::UserLibError> { fn try_to_lock_file(path: &PathBuf) -> Result<(PathBuf, File), crate::UserLibError> {
// Ok write "/etc/passwd.12397" to file struct TempLockFile {
// Ok write "/etc/passwd.lock" to lock tlf: PathBuf,
// Ok get the pid }
// Ok open the file impl Drop for TempLockFile {
// Ok write all to the tempfile fn drop(&mut self) {
// Ok try to make a link from the file created to the lockfile info!("removing temporary lockfile {}", self.tlf.to_str().unwrap());
// Ok ensure that the file has been linked successfully std::fs::remove_file(&self.tlf).unwrap();
// Ok Open the lockfile }
// Ok read the contents of the lockfile }
// Ok check if the lockfile contains a pid if not error out impl Deref for TempLockFile {
// Ok check if the containing pid is in a valid format. If not create a matching error type Target = PathBuf;
// test if this process could be killed. If so disclose the pid in the error. fn deref(&self) -> &PathBuf {
// try to delete the lockfile as it is apparently not used by the process anmore. (cleanup) &self.tlf
// try to lock again now that the old logfile has been safely removed. }
// remove the original file and only keep the lock hardlink }
info!("locking file {}", path.to_string_lossy()); info!("locking file {}", path.to_string_lossy());
let mut tempfilepath = path.clone(); let mut tempfilepath_const = path.clone();
// get the pid // get the pid
let pid = std::process::id(); let pid = std::process::id();
debug!("using pid {}", std::process::id()); debug!("using pid {}", std::process::id());
// get the filename // get the filename
let filename = tempfilepath.file_name().unwrap().to_owned(); let filename = tempfilepath_const.file_name().unwrap().to_owned();
// and the base path which is the base for tempfile and lockfile. // and the base path which is the base for tempfile and lockfile.
tempfilepath.pop(); tempfilepath_const.pop();
let mut lockfilepath = tempfilepath.clone(); let mut lockfilepath = tempfilepath_const.clone();
// push the filenames to the paths // push the filenames to the paths
tempfilepath.push(format!("{}.{}", filename.to_str().unwrap(), pid)); tempfilepath_const.push(format!("{}.{}", filename.to_str().unwrap(), pid));
let tempfilepath = TempLockFile {
tlf: tempfilepath_const,
};
lockfilepath.push(format!("{}.lock", filename.to_str().unwrap())); lockfilepath.push(format!("{}.lock", filename.to_str().unwrap()));
debug!( debug!(
"Lockfile paths: {:?} (temporary) {:?} (final)", "Lockfile paths: {:?} (temporary) {:?} (final)",
tempfilepath, lockfilepath *tempfilepath, lockfilepath
); );
// write the pid into the tempfile // write the pid into the tempfile
{ {
let mut tempfile = File::create(&tempfilepath) let mut tempfile = File::create(&*tempfilepath)
.expect(&format!("Failed to open {}", filename.to_str().unwrap())); .expect(&format!("Failed to open {}", filename.to_str().unwrap()));
match write!(tempfile, "{}", pid) { match write!(tempfile, "{}", pid) {
Ok(_) => {} Ok(_) => {}
Err(_) => { Err(_) => error!("could not write to {}", filename.to_string_lossy()),
let _ = std::fs::remove_file(&tempfilepath);
error!("could not write to {}", filename.to_string_lossy())
}
}; };
} }
// try to make a hardlink from the lockfile to the tempfile // try to make a hardlink from the lockfile to the tempfile
let linkresult = std::fs::hard_link(&tempfilepath, &lockfilepath); let linkresult = std::fs::hard_link(&*tempfilepath, &lockfilepath);
match linkresult { match linkresult {
Ok(()) => { Ok(()) => {
debug!("successfully locked"); debug!("successfully locked");
let _ = std::fs::remove_file(&tempfilepath);
// open the file // open the file
let resfile = File::open(&path); let resfile = File::open(&path);
@ -142,7 +175,6 @@ impl LockedFileGuard {
let mut lf = match File::open(&lockfilepath) { let mut lf = match File::open(&lockfilepath) {
Ok(file) => file, Ok(file) => file,
Err(e) => { Err(e) => {
let _ = std::fs::remove_file(&tempfilepath);
panic!("failed to open the lockfile: {}", e); panic!("failed to open the lockfile: {}", e);
} }
}; };
@ -150,7 +182,6 @@ impl LockedFileGuard {
match lf.read_to_string(&mut content) { match lf.read_to_string(&mut content) {
Ok(_) => {} Ok(_) => {}
Err(_) => { Err(_) => {
let _ = std::fs::remove_file(&tempfilepath);
panic!("failed to read the lockfile{}", e); panic!("failed to read the lockfile{}", e);
} }
} }
@ -163,28 +194,23 @@ impl LockedFileGuard {
pid pid
); );
error!("The file could not be locked"); error!("The file could not be locked");
let _ = std::fs::remove_file(&tempfilepath);
todo!("Validate the lock and delete the file if the process does not exist anymore"); todo!("Validate the lock and delete the file if the process does not exist anymore");
/*let sent = nix::sys::signal::kill( /*let sent = nix::sys::signal::kill(
nix::unistd::Pid::from_raw(pid as i32), nix::unistd::Pid::from_raw(pid as i32),
nix::sys::signal::Signal::from(0), nix::sys::signal::Signal::from(0),
);*/ );*/
} }
Err(e) => { Err(e) => error!(
let _ = std::fs::remove_file(&tempfilepath); "existing lock file {} with an invalid PID '{}' Error: {}",
error!( lockfilepath.to_str().unwrap(),
"existing lock file {} with an invalid PID '{}' Error: {}", content,
lockfilepath.to_str().unwrap(), e
content, ),
e
)
}
} }
} }
} }
_ => { _ => {
let _ = std::fs::remove_file(&tempfilepath);
panic!("failed to lock the file: {}", e); panic!("failed to lock the file: {}", e);
} }
}, },
@ -229,16 +255,13 @@ impl UserDBLocal {
pub fn load_files(files: Files) -> Self { pub fn load_files(files: Files) -> Self {
// Get the Strings for the files use an inner block to drop references after read. // Get the Strings for the files use an inner block to drop references after read.
let (my_passwd_lines, my_shadow_lines, my_group_lines) = { let (my_passwd_lines, my_shadow_lines, my_group_lines) = {
let locked_p = files.lock_and_get_passwd(); let opened = files.lock_all_get();
let locked_s = files.lock_and_get_shadow(); let (locked_p, locked_s, locked_g) = opened.expect("failed to lock files!");
let locked_g = files.lock_and_get_group(); // read the files to strings
use std::{thread, time}; let p = file_to_string(&locked_p.file);
let s = file_to_string(&locked_s.file);
let ten_millis = time::Duration::from_millis(10000); let g = file_to_string(&locked_g.file);
thread::sleep(ten_millis); // return the strings to the outer scope and release the lock...
let p = file_to_string(&locked_p.unwrap().file);
let s = file_to_string(&locked_s.unwrap().file);
let g = file_to_string(&locked_g.unwrap().file);
(p, s, g) (p, s, g)
}; };