Skip to content
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

Expand ensure_dir to allow copying subdirectories #4

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
208 changes: 131 additions & 77 deletions src/ensure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
* Copyright 2021 Oxide Computer Company
*/

use std::path::{Path, PathBuf};
use anyhow::{anyhow, bail, Result};
use digest::Digest;
use jmclib::log::prelude::*;
use std::ffi::CString;
use std::fs::{DirBuilder, File};
use std::io::{BufRead, BufReader, Read, Write};
use std::os::unix::fs::DirBuilderExt;
use std::ffi::CString;
use std::io::{Read, BufRead, BufReader, Write};
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use jmclib::log::prelude::*;
use anyhow::{Result, bail, anyhow};
use digest::Digest;

#[allow(dead_code)]
#[derive(Debug, PartialEq)]
Expand Down Expand Up @@ -52,14 +52,16 @@ impl FileInfo {
pub fn is_file(&self) -> bool {
matches!(&self.filetype, FileType::File)
}

pub fn is_dir(&self) -> bool {
matches!(&self.filetype, FileType::Directory)
}
}

pub fn check<P: AsRef<Path>>(p: P) -> Result<Option<FileInfo>> {
let name: &str = p.as_ref().to_str().unwrap();
let cname = CString::new(name.to_string())?;
let st = Box::into_raw(Box::new(unsafe {
std::mem::zeroed::<libc::stat>()
}));
let st = Box::into_raw(Box::new(unsafe { std::mem::zeroed::<libc::stat>() }));
let (r, e, st) = unsafe {
let r = libc::lstat(cname.as_ptr(), st);
let e = *libc::___errno();
Expand Down Expand Up @@ -113,17 +115,25 @@ pub fn chown<P: AsRef<Path>>(path: P, owner: u32, group: u32) -> Result<()> {
(r, e)
};
if r != 0 {
bail!("lchown({}, {}, {}): errno {}", path.as_ref().display(),
owner, group, e);
bail!(
"lchown({}, {}, {}): errno {}",
path.as_ref().display(),
owner,
group,
e
);
}

Ok(())
}

pub fn perms<P: AsRef<Path>>(log: &Logger, p: P, owner: u32, group: u32,
perms: u32)
-> Result<bool>
{
pub fn perms<P: AsRef<Path>>(
log: &Logger,
p: P,
owner: u32,
group: u32,
perms: u32,
) -> Result<bool> {
let p = p.as_ref();
let log = log.new(o!("path" => p.display().to_string()));
let mut did_work = false;
Expand Down Expand Up @@ -161,8 +171,10 @@ pub fn perms<P: AsRef<Path>>(log: &Logger, p: P, owner: u32, group: u32,
}
(o, g) => {
did_work = true;
info!(log, "ownership wrong ({:?}:{:?}, not {}:{})", o, g,
owner, group);
info!(
log,
"ownership wrong ({:?}:{:?}, not {}:{})", o, g, owner, group
);
chown(p, owner, group)?;

info!(log, "chown ok");
Expand All @@ -172,10 +184,13 @@ pub fn perms<P: AsRef<Path>>(log: &Logger, p: P, owner: u32, group: u32,
Ok(did_work)
}

pub fn directory<P: AsRef<Path>>(log: &Logger, dir: P, owner: u32,
group: u32, mode: u32)
-> Result<bool>
{
pub fn directory<P: AsRef<Path>>(
log: &Logger,
dir: P,
owner: u32,
group: u32,
mode: u32,
) -> Result<bool> {
let dir = dir.as_ref();
let mut did_work = false;

Expand All @@ -192,10 +207,7 @@ pub fn directory<P: AsRef<Path>>(log: &Logger, dir: P, owner: u32,
*/
did_work = true;
info!(log, "creating directory: {}", dir.display());
DirBuilder::new()
.recursive(true)
.mode(mode)
.create(dir)?;
DirBuilder::new().recursive(true).mode(mode).create(dir)?;

/*
* Check the path again, to make sure we have up-to-date information:
Expand Down Expand Up @@ -282,27 +294,39 @@ pub fn removed<P: AsRef<Path>>(log: &Logger, dst: P) -> Result<()> {
if let Some(fi) = check(dst)? {
match fi.filetype {
FileType::File | FileType::Link => {
info!(log, "file {} exists (as {:?}), removing",
dst.display(), fi.filetype);
info!(
log,
"file {} exists (as {:?}), removing",
dst.display(),
fi.filetype
);

std::fs::remove_file(dst)?;
}
t => {
bail!("file {} exists as {:?}, unexpected type",
dst.display(), t);
bail!("file {} exists as {:?}, unexpected type", dst.display(), t);
}
}
} else {
info!(log, "file {} does not already exist, skipping removal",
dst.display());
info!(
log,
"file {} does not already exist, skipping removal",
dst.display()
);
}

Ok(())
}

pub fn filestr<P: AsRef<Path>>(log: &Logger, contents: &str, dst: P,
owner: u32, group: u32, mode: u32, create: Create) -> Result<bool>
{
pub fn filestr<P: AsRef<Path>>(
log: &Logger,
contents: &str,
dst: P,
owner: u32,
group: u32,
mode: u32,
create: Create,
) -> Result<bool> {
let dst = dst.as_ref();
let mut did_work = false;

Expand All @@ -312,35 +336,38 @@ pub fn filestr<P: AsRef<Path>>(log: &Logger, contents: &str, dst: P,
*/
match create {
Create::IfMissing if fi.filetype == FileType::File => {
info!(log, "file {} exists, skipping population",
dst.display());
info!(log, "file {} exists, skipping population", dst.display());
false
}
Create::IfMissing if fi.filetype == FileType::Link => {
warn!(log, "symlink {} exists, skipping population",
dst.display());
warn!(log, "symlink {} exists, skipping population", dst.display());
false
}
Create::IfMissing => {
/*
* Avoid clobbering an unexpected entry when we have been asked
* to preserve in the face of modifications.
*/
bail!("{} should be a file, but is a {:?}",
dst.display(), fi.filetype);
bail!(
"{} should be a file, but is a {:?}",
dst.display(),
fi.filetype
);
}
Create::Always if fi.filetype == FileType::File => {
/*
* Check the contents of the file to make sure it matches
* what we expect.
*/
if comparestr(contents, dst)? {
info!(log, "file {} exists, with correct contents",
dst.display());
info!(log, "file {} exists, with correct contents", dst.display());
false
} else {
warn!(log, "file {} exists, with wrong contents, unlinking",
dst.display());
warn!(
log,
"file {} exists, with wrong contents, unlinking",
dst.display()
);
std::fs::remove_file(dst)?;
true
}
Expand All @@ -350,8 +377,12 @@ pub fn filestr<P: AsRef<Path>>(log: &Logger, contents: &str, dst: P,
* We found a file type we don't expect. Try to unlink it
* anyway.
*/
warn!(log, "file {} exists, of type {:?}, unlinking",
dst.display(), fi.filetype);
warn!(
log,
"file {} exists, of type {:?}, unlinking",
dst.display(),
fi.filetype
);
std::fs::remove_file(dst)?;
true
}
Expand Down Expand Up @@ -381,9 +412,15 @@ pub fn filestr<P: AsRef<Path>>(log: &Logger, contents: &str, dst: P,
Ok(did_work)
}

pub fn file<P1: AsRef<Path>, P2: AsRef<Path>>(log: &Logger, src: P1, dst: P2,
owner: u32, group: u32, mode: u32, create: Create) -> Result<bool>
{
pub fn file<P1: AsRef<Path>, P2: AsRef<Path>>(
log: &Logger,
src: P1,
dst: P2,
owner: u32,
group: u32,
mode: u32,
create: Create,
) -> Result<bool> {
let src = src.as_ref();
let dst = dst.as_ref();
let mut did_work = false;
Expand All @@ -394,35 +431,38 @@ pub fn file<P1: AsRef<Path>, P2: AsRef<Path>>(log: &Logger, src: P1, dst: P2,
*/
match create {
Create::IfMissing if fi.filetype == FileType::File => {
info!(log, "file {} exists, skipping population",
dst.display());
info!(log, "file {} exists, skipping population", dst.display());
false
}
Create::IfMissing if fi.filetype == FileType::Link => {
warn!(log, "symlink {} exists, skipping population",
dst.display());
warn!(log, "symlink {} exists, skipping population", dst.display());
false
}
Create::IfMissing => {
/*
* Avoid clobbering an unexpected entry when we have been asked
* to preserve in the face of modifications.
*/
bail!("{} should be a file, but is a {:?}",
dst.display(), fi.filetype);
bail!(
"{} should be a file, but is a {:?}",
dst.display(),
fi.filetype
);
}
Create::Always if fi.filetype == FileType::File => {
/*
* Check the contents of the file to make sure it matches
* what we expect.
*/
if compare(src, dst)? {
info!(log, "file {} exists, with correct contents",
dst.display());
info!(log, "file {} exists, with correct contents", dst.display());
false
} else {
warn!(log, "file {} exists, with wrong contents, unlinking",
dst.display());
warn!(
log,
"file {} exists, with wrong contents, unlinking",
dst.display()
);
std::fs::remove_file(dst)?;
true
}
Expand All @@ -432,8 +472,12 @@ pub fn file<P1: AsRef<Path>, P2: AsRef<Path>>(log: &Logger, src: P1, dst: P2,
* We found a file type we don't expect. Try to unlink it
* anyway.
*/
warn!(log, "file {} exists, of type {:?}, unlinking",
dst.display(), fi.filetype);
warn!(
log,
"file {} exists, of type {:?}, unlinking",
dst.display(),
fi.filetype
);
std::fs::remove_file(dst)?;
true
}
Expand All @@ -457,10 +501,13 @@ pub fn file<P1: AsRef<Path>, P2: AsRef<Path>>(log: &Logger, src: P1, dst: P2,
Ok(did_work)
}

pub fn symlink<P1: AsRef<Path>, P2: AsRef<Path>>(log: &Logger, dst: P1,
target: P2, owner: u32, group: u32)
-> Result<bool>
{
pub fn symlink<P1: AsRef<Path>, P2: AsRef<Path>>(
log: &Logger,
dst: P1,
target: P2,
owner: u32,
group: u32,
) -> Result<bool> {
let dst = dst.as_ref();
let target = target.as_ref();
let mut did_work = false;
Expand All @@ -472,17 +519,25 @@ pub fn symlink<P1: AsRef<Path>, P2: AsRef<Path>>(log: &Logger, dst: P1,
info!(log, "link target ok ({})", target.display());
false
} else {
warn!(log, "link target wrong: want {}, got {}; unlinking",
target.display(), fitarget.display());
warn!(
log,
"link target wrong: want {}, got {}; unlinking",
target.display(),
fitarget.display()
);
std::fs::remove_file(dst)?;
true
}
} else {
/*
* File type not correct. Unlink.
*/
warn!(log, "file {} exists, of type {:?}, unlinking",
dst.display(), fi.filetype);
warn!(
log,
"file {} exists, of type {:?}, unlinking",
dst.display(),
fi.filetype
);
std::fs::remove_file(dst)?;
true
}
Expand Down Expand Up @@ -540,8 +595,11 @@ pub fn hash_file<P: AsRef<Path>>(p: P, hashtype: &HashType) -> Result<String> {
Ok(out)
}

fn spawn_reader<T>(log: &Logger, name: &str, stream: Option<T>)
-> Option<std::thread::JoinHandle<()>>
fn spawn_reader<T>(
log: &Logger,
name: &str,
stream: Option<T>,
) -> Option<std::thread::JoinHandle<()>>
where
T: Read + Send + 'static,
{
Expand Down Expand Up @@ -587,19 +645,15 @@ pub fn run<S: AsRef<str>>(log: &Logger, args: &[S]) -> Result<()> {
run_envs(log, args, envs)
}

pub fn run_envs<S, SI, I, K, V>(log: &Logger, args: SI, env: Option<I>)
-> Result<()>
pub fn run_envs<S, SI, I, K, V>(log: &Logger, args: SI, env: Option<I>) -> Result<()>
where
S: AsRef<str>,
SI: IntoIterator<Item = S>,
I: IntoIterator<Item = (K, V)> + std::fmt::Debug,
K: AsRef<std::ffi::OsStr> + Eq + std::hash::Hash + std::fmt::Debug,
V: AsRef<std::ffi::OsStr> + std::fmt::Debug,
{
let args: Vec<String> = args
.into_iter()
.map(|s| s.as_ref().to_string())
.collect();
let args: Vec<String> = args.into_iter().map(|s| s.as_ref().to_string()).collect();

let env = if let Some(env) = env {
let env: std::collections::HashMap<_, _> = env.into_iter().collect();
Expand Down
Loading