s/std::fs::read_dir/nix::dir::Dir/ in a few spots

This is nicer in a few ways:

   * I can use openat so there's no possibility of any kind of a race
     involving scanning a different directory than the one used in
     other ways (locking, metadata file, adding/removing sample files)
   * filename() doesn't need to allocate memory, so it's a bit more
     efficient
   * dogfooding - I wrote nix::dir.
This commit is contained in:
Scott Lamb
2019-07-12 11:05:36 -07:00
parent bb227491b6
commit e52e725958
8 changed files with 99 additions and 54 deletions

View File

@@ -32,12 +32,12 @@
use crate::dir;
use failure::{Error, bail, format_err};
use nix::fcntl::FlockArg;
use nix::fcntl::{FlockArg, OFlag};
use nix::sys::stat::Mode;
use protobuf::prelude::MessageField;
use rusqlite::types::ToSql;
use crate::schema::DirMeta;
use std::fs;
use std::os::unix::ffi::OsStrExt;
use std::os::unix::io::AsRawFd;
use uuid::Uuid;
pub fn run(args: &super::Args, tx: &rusqlite::Transaction) -> Result<(), Error> {
@@ -46,9 +46,10 @@ pub fn run(args: &super::Args, tx: &rusqlite::Transaction) -> Result<(), Error>
.ok_or_else(|| format_err!("--sample-file-dir required when upgrading from \
schema version 1 to 2."))?;
let d = dir::Fd::open(sample_file_path, false)?;
d.lock(FlockArg::LockExclusiveNonblock)?;
verify_dir_contents(sample_file_path, tx)?;
let mut d = nix::dir::Dir::open(sample_file_path, OFlag::O_DIRECTORY | OFlag::O_RDONLY,
Mode::empty())?;
nix::fcntl::flock(d.as_raw_fd(), FlockArg::LockExclusiveNonblock)?;
verify_dir_contents(sample_file_path, &mut d, tx)?;
// These create statements match the schema.sql when version 2 was the latest.
tx.execute_batch(r#"
@@ -119,7 +120,7 @@ pub fn run(args: &super::Args, tx: &rusqlite::Transaction) -> Result<(), Error>
open.id = open_id;
open.uuid.extend_from_slice(&open_uuid_bytes);
}
dir::write_meta(&d, &meta)?;
dir::write_meta(d.as_raw_fd(), &meta)?;
tx.execute(r#"
insert into sample_file_dir (path, uuid, last_complete_open_id)
@@ -292,7 +293,8 @@ pub fn run(args: &super::Args, tx: &rusqlite::Transaction) -> Result<(), Error>
/// * optional: reserved sample file uuids.
/// * optional: meta and meta-tmp from half-completed update attempts.
/// * forbidden: anything else.
fn verify_dir_contents(sample_file_path: &str, tx: &rusqlite::Transaction) -> Result<(), Error> {
fn verify_dir_contents(sample_file_path: &str, dir: &mut nix::dir::Dir,
tx: &rusqlite::Transaction) -> Result<(), Error> {
// Build a hash of the uuids found in the directory.
let n: i64 = tx.query_row(r#"
select
@@ -302,10 +304,10 @@ fn verify_dir_contents(sample_file_path: &str, tx: &rusqlite::Transaction) -> Re
(select count(*) as c from reserved_sample_files) b;
"#, &[] as &[&dyn ToSql], |r| r.get(0))?;
let mut files = ::fnv::FnvHashSet::with_capacity_and_hasher(n as usize, Default::default());
for e in fs::read_dir(sample_file_path)? {
for e in dir.iter() {
let e = e?;
let f = e.file_name();
match f.as_bytes() {
match f.to_bytes() {
b"." | b".." => continue,
b"meta" | b"meta-tmp" => {
// Ignore metadata files. These might from a half-finished update attempt.
@@ -315,8 +317,8 @@ fn verify_dir_contents(sample_file_path: &str, tx: &rusqlite::Transaction) -> Re
_ => {},
};
let s = match f.to_str() {
Some(s) => s,
None => bail!("unexpected file {:?} in {:?}", f, sample_file_path),
Ok(s) => s,
Err(_) => bail!("unexpected file {:?} in {:?}", f, sample_file_path),
};
let uuid = match Uuid::parse_str(s) {
Ok(u) => u,

View File

@@ -82,7 +82,7 @@ pub fn run(_args: &super::Args, tx: &rusqlite::Transaction) -> Result<(), Error>
dir.lock(FlockArg::LockExclusiveNonblock)?;
let tmp_path = cstr!("meta.tmp");
let path = cstr!("meta");
let mut f = dir.openat(path, OFlag::O_RDONLY, Mode::empty())?;
let mut f = crate::fs::openat(dir.as_raw_fd(), path, OFlag::O_RDONLY, Mode::empty())?;
let mut data = Vec::new();
f.read_to_end(&mut data)?;
if data.len() == FIXED_DIR_META_LEN {
@@ -95,8 +95,9 @@ pub fn run(_args: &super::Args, tx: &rusqlite::Transaction) -> Result<(), Error>
if !dir::SampleFileDir::consistent(&db_meta, &dir_meta) {
bail!("Inconsistent db_meta={:?} dir_meta={:?}", &db_meta, &dir_meta);
}
let mut f = dir.openat(tmp_path, OFlag::O_CREAT | OFlag::O_TRUNC | OFlag::O_WRONLY,
Mode::S_IRUSR | Mode::S_IWUSR)?;
let mut f = crate::fs::openat(dir.as_raw_fd(), tmp_path,
OFlag::O_CREAT | OFlag::O_TRUNC | OFlag::O_WRONLY,
Mode::S_IRUSR | Mode::S_IWUSR)?;
let mut data =
dir_meta.write_length_delimited_to_bytes().expect("proto3->vec is infallible");
if data.len() > FIXED_DIR_META_LEN {