Skip to content

Commit

Permalink
hostsfile: remove has_content_changed in favor of comparing old and n…
Browse files Browse the repository at this point in the history
…ew sections
  • Loading branch information
evaporei committed May 31, 2023
1 parent 892231d commit ca58977
Showing 1 changed file with 45 additions and 64 deletions.
109 changes: 45 additions & 64 deletions hostsfile/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use std::{
collections::BTreeMap,
fmt,
fs::{File, OpenOptions},
io::{self, BufRead, BufReader, ErrorKind, Read, Write},
fs::OpenOptions,
io::{self, BufRead, BufReader, ErrorKind, Write},
net::IpAddr,
path::{Path, PathBuf},
result,
Expand Down Expand Up @@ -172,24 +172,6 @@ impl HostsBuilder {
Ok(hosts_dir.with_file_name(temp_filename))
}

fn has_content_changed(hosts_path: &Path, new_contents: &[u8]) -> io::Result<bool> {
let hosts_file = File::open(hosts_path)?;

if hosts_file.metadata()?.len() != new_contents.len() as u64 {
return Ok(true);
}

let hosts_buf = BufReader::new(hosts_file);

for (b1, b2) in hosts_buf.bytes().zip(new_contents) {
if b1.unwrap() != *b2 {
return Ok(true);
}
}

Ok(false)
}

/// Inserts a new section to the specified hosts file. If there is a section with the same tag
/// name already, it will be replaced with the new list instead.
///
Expand Down Expand Up @@ -227,9 +209,33 @@ impl HostsBuilder {
let begin = lines.iter().position(|line| line.trim() == begin_marker);
let end = lines.iter().position(|line| line.trim() == end_marker);

let mut lines_to_insert = vec![];
if !self.hostname_map.is_empty() {
lines_to_insert.push(begin_marker);
for (ip, hostnames) in &self.hostname_map {
if cfg!(windows) {
// windows only allows one hostname per line
for hostname in hostnames {
lines_to_insert.push(format!("{ip} {hostname}"));
}
} else {
// assume the same format as Unix
lines_to_insert.push(format!("{} {}", ip, hostnames.join(" ")));
}
}
lines_to_insert.push(end_marker);
}

let insert = match (begin, end) {
(Some(begin), Some(end)) => {
lines.drain(begin..end + 1);
let old_section: Vec<String> = lines.drain(begin..end + 1).collect();

let sections_are_eq = old_section == lines_to_insert;

if sections_are_eq {
return Ok(false);
}

begin
},
(None, None) => {
Expand All @@ -254,21 +260,12 @@ impl HostsBuilder {
for line in &lines[..insert] {
writeln!(s, "{line}")?;
}
if !self.hostname_map.is_empty() {
writeln!(s, "{begin_marker}")?;
for (ip, hostnames) in &self.hostname_map {
if cfg!(windows) {
// windows only allows one hostname per line
for hostname in hostnames {
writeln!(s, "{ip} {hostname}")?;
}
} else {
// assume the same format as Unix
writeln!(s, "{} {}", ip, hostnames.join(" "))?;
}
}
writeln!(s, "{end_marker}")?;

// Append hostnames_map section
for line in lines_to_insert {
writeln!(s, "{line}")?;
}

for line in &lines[insert..] {
writeln!(s, "{line}")?;
}
Expand All @@ -277,26 +274,21 @@ impl HostsBuilder {
Err(_) => {
Self::write_clobber(hosts_path, &s)?;
log::debug!("wrote hosts file with the clobber fallback strategy");
Ok(true)
},
Ok(has_written) => {
if has_written {
log::debug!("wrote hosts file with the write-and-swap strategy");
}
Ok(has_written)
_ => {
log::debug!("wrote hosts file with the write-and-swap strategy");
},
}
};

Ok(true)
}

fn write_and_swap(temp_path: &Path, hosts_path: &Path, contents: &[u8]) -> io::Result<bool> {
if Self::has_content_changed(hosts_path, contents)? {
// Copy the file we plan on modifying so its permissions and metadata are preserved.
std::fs::copy(hosts_path, temp_path)?;
Self::write_clobber(temp_path, contents)?;
std::fs::rename(temp_path, hosts_path)?;
return Ok(true);
}
Ok(false)
fn write_and_swap(temp_path: &Path, hosts_path: &Path, contents: &[u8]) -> io::Result<()> {
// Copy the file we plan on modifying so its permissions and metadata are preserved.
std::fs::copy(hosts_path, temp_path)?;
Self::write_clobber(temp_path, contents)?;
std::fs::rename(temp_path, hosts_path)?;
Ok(())
}

fn write_clobber(hosts_path: &Path, contents: &[u8]) -> io::Result<()> {
Expand Down Expand Up @@ -335,25 +327,14 @@ mod tests {
assert!(HostsBuilder::get_temp_path(hosts_path).is_err());
}

#[test]
fn test_has_content_changed() {
let (mut temp_file, temp_path) = tempfile::NamedTempFile::new().unwrap().into_parts();
temp_file.write_all(b"preexisting\ncontent").unwrap();

let new_content = b"preexisting\ncontent\nnew\ncontent";
assert!(HostsBuilder::has_content_changed(&temp_path, new_content).unwrap());

let same_content = b"preexisting\ncontent";
assert!(!HostsBuilder::has_content_changed(&temp_path, same_content).unwrap());
}

#[test]
fn test_write() {
let (mut temp_file, temp_path) = tempfile::NamedTempFile::new().unwrap().into_parts();
temp_file.write_all(b"preexisting\ncontent").unwrap();
let mut builder = HostsBuilder::new("foo");
builder.add_hostname([1, 1, 1, 1].into(), "whatever");
builder.write_to(&temp_path).unwrap();
assert!(builder.write_to(&temp_path).unwrap());
assert!(!builder.write_to(&temp_path).unwrap());

let contents = std::fs::read_to_string(&temp_path).unwrap();
println!("contents: {contents}");
Expand Down

0 comments on commit ca58977

Please sign in to comment.