use nix to remove many uses of unsafe

This commit is contained in:
Scott Lamb 2019-07-11 21:59:01 -07:00
parent d75157af56
commit bb227491b6
11 changed files with 142 additions and 139 deletions

27
Cargo.lock generated
View File

@ -851,6 +851,11 @@ name = "libc"
version = "0.2.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "libc"
version = "0.2.59"
source = "git+https://github.com/rust-lang/libc#54b03c53fb8e70bcce87dc26d8795c995e41061f"
[[package]]
name = "libpasta"
version = "0.1.0"
@ -1064,6 +1069,7 @@ dependencies = [
"lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"moonfire-base 0.0.1",
"mylog 0.1.0 (git+https://github.com/scottlamb/mylog)",
"nix 0.14.1 (git+https://github.com/scottlamb/nix?branch=pr-renameat)",
"odds 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"openssl 0.10.23 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1114,6 +1120,7 @@ dependencies = [
"moonfire-db 0.0.1",
"moonfire-ffmpeg 0.0.1",
"mylog 0.1.0 (git+https://github.com/scottlamb/mylog)",
"nix 0.14.1 (git+https://github.com/scottlamb/nix?branch=pr-renameat)",
"openssl 0.10.23 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"protobuf 3.0.0-pre (git+https://github.com/stepancheg/rust-protobuf)",
@ -1181,6 +1188,18 @@ dependencies = [
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "nix"
version = "0.14.1"
source = "git+https://github.com/scottlamb/nix?branch=pr-renameat#c34f87cf133a27d75476b9f4e5289bf065f20c9b"
dependencies = [
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.59 (git+https://github.com/rust-lang/libc)",
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "nodrop"
version = "0.1.13"
@ -2511,6 +2530,11 @@ name = "version_check"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "void"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "want"
version = "0.0.6"
@ -2673,6 +2697,7 @@ dependencies = [
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14"
"checksum libc 0.2.57 (registry+https://github.com/rust-lang/crates.io-index)" = "a844cabbd5a77e60403a58af576f0a1baa83c3dd2670be63e615bd24fc58b82d"
"checksum libc 0.2.59 (git+https://github.com/rust-lang/libc)" = "<none>"
"checksum libpasta 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "31a3bd8a4714af6f88d2cd850b016ad4567770f88fa3cf0571ec197591362f99"
"checksum libsqlite3-sys 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e310445ab028c374b9efaaed4b7a52a14e3b8ad5a1351b4bbd46dec03ffce717"
"checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83"
@ -2697,6 +2722,7 @@ dependencies = [
"checksum native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4b2df1a4c22fd44a62147fd8f13dd0f95c9d8ca7b2610299b2a2f9cf8964274e"
"checksum ncurses 5.99.0 (registry+https://github.com/rust-lang/crates.io-index)" = "15699bee2f37e9f8828c7b35b2bc70d13846db453f2d507713b758fabe536b82"
"checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88"
"checksum nix 0.14.1 (git+https://github.com/scottlamb/nix?branch=pr-renameat)" = "<none>"
"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945"
"checksum num 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cf4825417e1e1406b3782a8ce92f4d53f26ec055e3622e1881ca8e9f5f9e08db"
"checksum num-complex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "107b9be86cd2481930688277b675b0114578227f034674726605b8a482d8baf8"
@ -2846,6 +2872,7 @@ dependencies = [
"checksum vcpkg 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "def296d3eb3b12371b2c7d0e83bfe1403e4db2d7a0bba324a12b21c4ee13143d"
"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
"checksum want 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "797464475f30ddb8830cc529aaaae648d581f99e2036a928877dfde027ddf6b3"
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
"checksum winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770"

View File

@ -40,6 +40,7 @@ log = { version = "0.4", features = ["release_max_level_info"] }
memchr = "2.0.2"
memmap = "0.7"
mylog = { git = "https://github.com/scottlamb/mylog" }
nix = { git = "https://github.com/scottlamb/nix", branch = "pr-renameat" }
openssl = "0.10"
parking_lot = { version = "0.8", features = [] }
protobuf = { git = "https://github.com/stepancheg/rust-protobuf" }

View File

@ -24,6 +24,7 @@ libpasta = "0.1.0-rc2"
log = "0.4"
lru-cache = "0.1"
mylog = { git = "https://github.com/scottlamb/mylog" }
nix = { git = "https://github.com/scottlamb/nix", branch = "pr-renameat" }
odds = { version = "0.3.1", features = ["std-vec"] }
openssl = "0.10"
parking_lot = { version = "0.8", features = [] }

View File

@ -116,12 +116,7 @@ struct VideoIndex(Box<[u8]>);
impl rusqlite::types::FromSql for VideoIndex {
fn column_result(value: rusqlite::types::ValueRef) -> rusqlite::types::FromSqlResult<Self> {
let blob = value.as_blob()?;
let len = blob.len();
let mut v = Vec::with_capacity(len);
unsafe { v.set_len(len) };
v.copy_from_slice(blob);
Ok(VideoIndex(v.into_boxed_slice()))
Ok(VideoIndex(value.as_blob()?.to_vec().into_boxed_slice()))
}
}

158
db/dir.rs
View File

@ -34,16 +34,16 @@
use crate::coding;
use crate::db::CompositeId;
use crate::schema;
use cstr::*;
use failure::{Error, Fail, bail, format_err};
use libc::c_char;
use log::warn;
use protobuf::Message;
use crate::schema;
use std::ffi;
use nix::{NixPath, fcntl::{AtFlags, FlockArg, OFlag}, sys::stat::Mode};
use nix::sys::statvfs::Statvfs;
use std::ffi::{CStr, CString};
use std::fs;
use std::io::{self, Read, Write};
use std::mem;
use std::io::{Read, Write};
use std::os::unix::ffi::OsStrExt;
use std::os::unix::io::FromRawFd;
use std::sync::Arc;
@ -66,14 +66,37 @@ pub struct SampleFileDir {
pub(crate) fd: Fd,
}
pub(crate) struct CompositeIdPath([u8; 17]);
impl CompositeIdPath {
pub(crate) fn from(id: CompositeId) -> Self {
let mut buf = [0u8; 17];
write!(&mut buf[..16], "{:016x}", id.0).expect("can't format id to pathname buf");
CompositeIdPath(buf)
}
}
impl NixPath for CompositeIdPath {
fn len(&self) -> usize { 16 }
fn with_nix_path<T, F>(&self, f: F) -> Result<T, nix::Error>
where F: FnOnce(&CStr) -> T {
let p = CStr::from_bytes_with_nul(&self.0[..]).expect("no interior nuls");
Ok(f(p))
}
}
/// A file descriptor associated with a directory (not necessarily the sample file dir).
#[derive(Debug)]
pub struct Fd(libc::c_int);
pub struct Fd(std::os::unix::io::RawFd);
impl std::os::unix::io::AsRawFd for Fd {
fn as_raw_fd(&self) -> std::os::unix::io::RawFd { self.0 }
}
impl Drop for Fd {
fn drop(&mut self) {
if unsafe { libc::close(self.0) } < 0 {
let e = io::Error::last_os_error();
if let Err(e) = nix::unistd::close(self.0) {
warn!("Unable to close sample file dir: {}", e);
}
}
@ -81,76 +104,46 @@ impl Drop for Fd {
impl Fd {
/// Opens the given path as a directory.
pub fn open(path: &str, mkdir: bool) -> Result<Fd, io::Error> {
let cstring = ffi::CString::new(path)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
if mkdir && unsafe { libc::mkdir(cstring.as_ptr(), 0o700) } != 0 {
let e = io::Error::last_os_error();
if e.kind() != io::ErrorKind::AlreadyExists {
return Err(e.into());
pub fn open(path: &str, mkdir: bool) -> Result<Fd, nix::Error> {
let cstring = CString::new(path).map_err(|_| nix::Error::InvalidPath)?;
if mkdir {
match nix::unistd::mkdir(cstring.as_c_str(), nix::sys::stat::Mode::S_IRWXU) {
Ok(()) | Err(nix::Error::Sys(nix::errno::Errno::EEXIST)) => {},
Err(e) => return Err(e),
}
}
let fd = unsafe { libc::open(cstring.as_ptr(), libc::O_DIRECTORY | libc::O_RDONLY, 0) };
if fd < 0 {
return Err(io::Error::last_os_error().into());
}
let fd = nix::fcntl::open(cstring.as_c_str(), OFlag::O_DIRECTORY | OFlag::O_RDONLY,
Mode::empty())?;
Ok(Fd(fd))
}
pub(crate) fn sync(&self) -> Result<(), io::Error> {
let res = unsafe { libc::fsync(self.0) };
if res < 0 {
return Err(io::Error::last_os_error())
}
Ok(())
pub(crate) fn sync(&self) -> Result<(), nix::Error> {
nix::unistd::fsync(self.0)
}
/// Opens a sample file within this directory with the given flags and (if creating) mode.
pub(crate) unsafe fn openat(&self, p: *const c_char, flags: libc::c_int, mode: libc::c_int)
-> Result<fs::File, io::Error> {
let fd = libc::openat(self.0, p, flags, mode);
if fd < 0 {
return Err(io::Error::last_os_error())
}
Ok(fs::File::from_raw_fd(fd))
pub(crate) fn openat<P: ?Sized + NixPath>(&self, p: &P, oflag: OFlag, mode: Mode)
-> Result<fs::File, nix::Error> {
let fd = nix::fcntl::openat(self.0, p, oflag, mode)?;
Ok(unsafe { fs::File::from_raw_fd(fd) })
}
/// Locks the directory with the specified `flock` operation.
pub fn lock(&self, operation: libc::c_int) -> Result<(), io::Error> {
let ret = unsafe { libc::flock(self.0, operation) };
if ret < 0 {
return Err(io::Error::last_os_error().into());
}
Ok(())
pub fn lock(&self, arg: FlockArg) -> Result<(), nix::Error> {
nix::fcntl::flock(self.0, arg)
}
pub fn statfs(&self) -> Result<libc::statvfs, io::Error> {
unsafe {
let mut stat: libc::statvfs = mem::zeroed();
if libc::fstatvfs(self.0, &mut stat) < 0 {
return Err(io::Error::last_os_error())
}
Ok(stat)
}
pub fn statfs(&self) -> Result<nix::sys::statvfs::Statvfs, nix::Error> {
nix::sys::statvfs::fstatvfs(self)
}
}
pub(crate) unsafe fn renameat(from_fd: &Fd, from_path: *const c_char,
to_fd: &Fd, to_path: *const c_char) -> Result<(), io::Error> {
let result = libc::renameat(from_fd.0, from_path, to_fd.0, to_path);
if result < 0 {
return Err(io::Error::last_os_error())
}
Ok(())
}
/// Reads `dir`'s metadata. If none is found, returns an empty proto.
pub(crate) fn read_meta(dir: &Fd) -> Result<schema::DirMeta, Error> {
let mut meta = schema::DirMeta::default();
let p = cstr!("meta");
let mut f = match unsafe { dir.openat(p.as_ptr(), libc::O_RDONLY, 0) } {
let mut f = match dir.openat(cstr!("meta"), OFlag::O_RDONLY, Mode::empty()) {
Err(e) => {
if e.kind() == ::std::io::ErrorKind::NotFound {
if e == nix::Error::Sys(nix::errno::Errno::ENOENT) {
return Ok(meta);
}
return Err(e.into());
@ -179,9 +172,8 @@ pub(crate) fn write_meta(dir: &Fd, meta: &schema::DirMeta) -> Result<(), Error>
data.len(), FIXED_DIR_META_LEN);
}
data.resize(FIXED_DIR_META_LEN, 0); // pad to required length.
let path = cstr!("meta");
let mut f = unsafe { dir.openat(path.as_ptr(),
libc::O_CREAT | libc::O_WRONLY, 0o600)? };
let mut f = dir.openat(cstr!("meta"), OFlag::O_CREAT | OFlag::O_WRONLY,
Mode::S_IRUSR | Mode::S_IWUSR)?;
let stat = f.metadata()?;
if stat.len() == 0 {
// Need to sync not only the data but also the file metadata and dirent.
@ -207,7 +199,11 @@ impl SampleFileDir {
-> Result<Arc<SampleFileDir>, Error> {
let read_write = db_meta.in_progress_open.is_some();
let s = SampleFileDir::open_self(path, false)?;
s.fd.lock(if read_write { libc::LOCK_EX } else { libc::LOCK_SH } | libc::LOCK_NB)?;
s.fd.lock(if read_write {
FlockArg::LockExclusiveNonblock
} else {
FlockArg::LockSharedNonblock
})?;
let dir_meta = read_meta(&s.fd)?;
if !SampleFileDir::consistent(db_meta, &dir_meta) {
let serialized =
@ -243,7 +239,7 @@ impl SampleFileDir {
pub(crate) fn create(path: &str, db_meta: &schema::DirMeta)
-> Result<Arc<SampleFileDir>, Error> {
let s = SampleFileDir::open_self(path, true)?;
s.fd.lock(libc::LOCK_EX | libc::LOCK_NB)?;
s.fd.lock(FlockArg::LockExclusiveNonblock)?;
let old_meta = read_meta(&s.fd)?;
// Verify metadata. We only care that it hasn't been completely opened.
@ -280,43 +276,31 @@ impl SampleFileDir {
}
/// Opens the given sample file for reading.
pub fn open_file(&self, composite_id: CompositeId) -> Result<fs::File, io::Error> {
let p = SampleFileDir::get_rel_pathname(composite_id);
unsafe { self.fd.openat(p.as_ptr(), libc::O_RDONLY, 0) }
pub fn open_file(&self, composite_id: CompositeId) -> Result<fs::File, nix::Error> {
let p = CompositeIdPath::from(composite_id);
self.fd.openat(&p, OFlag::O_RDONLY, Mode::empty())
}
pub fn create_file(&self, composite_id: CompositeId) -> Result<fs::File, io::Error> {
let p = SampleFileDir::get_rel_pathname(composite_id);
unsafe { self.fd.openat(p.as_ptr(), libc::O_WRONLY | libc::O_EXCL | libc::O_CREAT, 0o600) }
pub fn create_file(&self, composite_id: CompositeId) -> Result<fs::File, nix::Error> {
let p = CompositeIdPath::from(composite_id);
self.fd.openat(&p, OFlag::O_WRONLY | OFlag::O_EXCL | OFlag::O_CREAT,
Mode::S_IRUSR | Mode::S_IWUSR)
}
pub(crate) fn write_meta(&self, meta: &schema::DirMeta) -> Result<(), Error> {
write_meta(&self.fd, meta)
}
pub fn statfs(&self) -> Result<libc::statvfs, io::Error> { self.fd.statfs() }
/// Gets a pathname for a sample file suitable for passing to open or unlink.
fn get_rel_pathname(id: CompositeId) -> [libc::c_char; 17] {
let mut buf = [0u8; 17];
write!(&mut buf[..16], "{:016x}", id.0).expect("can't format id to pathname buf");
// libc::c_char seems to be i8 on some platforms (Linux/arm) and u8 on others (Linux/amd64).
unsafe { mem::transmute::<[u8; 17], [libc::c_char; 17]>(buf) }
}
pub fn statfs(&self) -> Result<Statvfs, nix::Error> { self.fd.statfs() }
/// Unlinks the given sample file within this directory.
pub(crate) fn unlink_file(&self, id: CompositeId) -> Result<(), io::Error> {
let p = SampleFileDir::get_rel_pathname(id);
let res = unsafe { libc::unlinkat(self.fd.0, p.as_ptr(), 0) };
if res < 0 {
return Err(io::Error::last_os_error())
}
Ok(())
pub(crate) fn unlink_file(&self, id: CompositeId) -> Result<(), nix::Error> {
let p = CompositeIdPath::from(id);
nix::unistd::unlinkat(self.fd.0, &p, AtFlags::empty())
}
/// Syncs the directory itself.
pub(crate) fn sync(&self) -> Result<(), io::Error> {
pub(crate) fn sync(&self) -> Result<(), nix::Error> {
self.fd.sync()
}
}

View File

@ -32,7 +32,7 @@
use crate::dir;
use failure::{Error, bail, format_err};
use libc;
use nix::fcntl::FlockArg;
use protobuf::prelude::MessageField;
use rusqlite::types::ToSql;
use crate::schema::DirMeta;
@ -47,7 +47,7 @@ pub fn run(args: &super::Args, tx: &rusqlite::Transaction) -> Result<(), Error>
schema version 1 to 2."))?;
let d = dir::Fd::open(sample_file_path, false)?;
d.lock(libc::LOCK_EX | libc::LOCK_NB)?;
d.lock(FlockArg::LockExclusiveNonblock)?;
verify_dir_contents(sample_file_path, tx)?;
// These create statements match the schema.sql when version 2 was the latest.

View File

@ -35,12 +35,11 @@
use crate::db::{self, FromSqlUuid};
use crate::dir;
use failure::Error;
use libc;
use crate::schema;
use protobuf::prelude::MessageField;
use rusqlite::types::ToSql;
use std::io::{self, Write};
use std::mem;
use std::io::Write;
use std::os::unix::io::AsRawFd;
use std::sync::Arc;
use uuid::Uuid;
@ -90,10 +89,10 @@ pub fn run(_args: &super::Args, tx: &rusqlite::Transaction) -> Result<(), Error>
let id = db::CompositeId(row.get(0)?);
let sample_file_uuid: FromSqlUuid = row.get(1)?;
let from_path = get_uuid_pathname(sample_file_uuid.0);
let to_path = get_id_pathname(id);
let r = unsafe { dir::renameat(&d.fd, from_path.as_ptr(), &d.fd, to_path.as_ptr()) };
if let Err(e) = r {
if e.kind() == io::ErrorKind::NotFound {
let to_path = crate::dir::CompositeIdPath::from(id);
if let Err(e) = nix::fcntl::renameat(d.fd.as_raw_fd(), &from_path[..],
d.fd.as_raw_fd(), &to_path) {
if e == nix::Error::Sys(nix::errno::Errno::ENOENT) {
continue; // assume it was already moved.
}
Err(e)?;
@ -122,17 +121,9 @@ pub fn run(_args: &super::Args, tx: &rusqlite::Transaction) -> Result<(), Error>
}
/// Gets a pathname for a sample file suitable for passing to open or unlink.
fn get_uuid_pathname(uuid: Uuid) -> [libc::c_char; 37] {
fn get_uuid_pathname(uuid: Uuid) -> [u8; 37] {
let mut buf = [0u8; 37];
write!(&mut buf[..36], "{}", uuid.to_hyphenated_ref())
.expect("can't format uuid to pathname buf");
// libc::c_char seems to be i8 on some platforms (Linux/arm) and u8 on others (Linux/amd64).
unsafe { mem::transmute::<[u8; 37], [libc::c_char; 37]>(buf) }
}
fn get_id_pathname(id: db::CompositeId) -> [libc::c_char; 17] {
let mut buf = [0u8; 17];
write!(&mut buf[..16], "{:016x}", id.0).expect("can't format id to pathname buf");
unsafe { mem::transmute::<[u8; 17], [libc::c_char; 17]>(buf) }
buf
}

View File

@ -37,9 +37,12 @@ use crate::db::FromSqlUuid;
use crate::{dir, schema};
use cstr::*;
use failure::{Error, Fail, bail};
use nix::fcntl::{FlockArg, OFlag};
use nix::sys::stat::Mode;
use protobuf::{Message, prelude::MessageField};
use rusqlite::params;
use std::io::{Read, Write};
use std::os::unix::io::AsRawFd;
const FIXED_DIR_META_LEN: usize = 512;
@ -76,10 +79,10 @@ pub fn run(_args: &super::Args, tx: &rusqlite::Transaction) -> Result<(), Error>
}
let dir = dir::Fd::open(path, false)?;
dir.lock(libc::LOCK_EX)?;
dir.lock(FlockArg::LockExclusiveNonblock)?;
let tmp_path = cstr!("meta.tmp");
let path = cstr!("meta");
let mut f = unsafe { dir.openat(path.as_ptr(), libc::O_RDONLY, 0) }?;
let mut f = dir.openat(path, OFlag::O_RDONLY, Mode::empty())?;
let mut data = Vec::new();
f.read_to_end(&mut data)?;
if data.len() == FIXED_DIR_META_LEN {
@ -92,8 +95,8 @@ 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 = unsafe { dir.openat(tmp_path.as_ptr(),
libc::O_CREAT | libc::O_TRUNC | libc::O_WRONLY, 0o600)? };
let mut f = dir.openat(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 {
@ -103,7 +106,7 @@ pub fn run(_args: &super::Args, tx: &rusqlite::Transaction) -> Result<(), Error>
data.resize(FIXED_DIR_META_LEN, 0); // pad to required length.
f.write_all(&data)?;
f.sync_all()?;
unsafe { dir::renameat(&dir, tmp_path.as_ptr(), &dir, path.as_ptr())? };
nix::fcntl::renameat(dir.as_raw_fd(), tmp_path, dir.as_raw_fd(), path)?;
dir.sync()?;
}
Ok(())

View File

@ -55,9 +55,9 @@ use time::{Duration, Timespec};
pub trait DirWriter : 'static + Send {
type File : FileWriter;
fn create_file(&self, id: CompositeId) -> Result<Self::File, io::Error>;
fn sync(&self) -> Result<(), io::Error>;
fn unlink_file(&self, id: CompositeId) -> Result<(), io::Error>;
fn create_file(&self, id: CompositeId) -> Result<Self::File, nix::Error>;
fn sync(&self) -> Result<(), nix::Error>;
fn unlink_file(&self, id: CompositeId) -> Result<(), nix::Error>;
}
pub trait FileWriter : 'static {
@ -71,11 +71,11 @@ pub trait FileWriter : 'static {
impl DirWriter for Arc<dir::SampleFileDir> {
type File = ::std::fs::File;
fn create_file(&self, id: CompositeId) -> Result<Self::File, io::Error> {
fn create_file(&self, id: CompositeId) -> Result<Self::File, nix::Error> {
dir::SampleFileDir::create_file(self, id)
}
fn sync(&self) -> Result<(), io::Error> { dir::SampleFileDir::sync(self) }
fn unlink_file(&self, id: CompositeId) -> Result<(), io::Error> {
fn sync(&self) -> Result<(), nix::Error> { dir::SampleFileDir::sync(self) }
fn unlink_file(&self, id: CompositeId) -> Result<(), nix::Error> {
dir::SampleFileDir::unlink_file(self, id)
}
}
@ -308,7 +308,7 @@ impl<C: Clocks + Clone> Syncer<C, Arc<dir::SampleFileDir>> {
let mut undeletable = 0;
for &id in &to_abandon {
if let Err(e) = dir.unlink_file(id) {
if e.kind() == io::ErrorKind::NotFound {
if e == nix::Error::Sys(nix::errno::Errno::ENOENT) {
warn!("dir: abandoned recording {} already deleted!", id);
} else {
warn!("dir: Unable to unlink abandoned recording {}: {}", id, e);
@ -358,7 +358,7 @@ impl<C: Clocks + Clone> Syncer<C, Arc<dir::SampleFileDir>> {
let mut errors = 0;
for &id in &garbage {
if let Err(e) = self.dir.unlink_file(id) {
if e.kind() != io::ErrorKind::NotFound {
if e != nix::Error::Sys(nix::errno::Errno::ENOENT) {
warn!("dir: Unable to unlink {}: {}", id, e);
errors += 1;
}
@ -435,7 +435,7 @@ impl<C: Clocks + Clone, D: DirWriter> Syncer<C, D> {
for &id in &garbage {
clock::retry_forever(c, &mut || {
if let Err(e) = self.dir.unlink_file(id) {
if e.kind() == io::ErrorKind::NotFound {
if e == nix::Error::Sys(nix::errno::Errno::ENOENT) {
warn!("dir: recording {} already deleted!", id);
return Ok(());
}
@ -867,9 +867,9 @@ mod tests {
struct MockDir(Arc<Mutex<VecDeque<MockDirAction>>>);
enum MockDirAction {
Create(CompositeId, Box<dyn Fn(CompositeId) -> Result<MockFile, io::Error> + Send>),
Sync(Box<dyn Fn() -> Result<(), io::Error> + Send>),
Unlink(CompositeId, Box<dyn Fn(CompositeId) -> Result<(), io::Error> + Send>),
Create(CompositeId, Box<dyn Fn(CompositeId) -> Result<MockFile, nix::Error> + Send>),
Sync(Box<dyn Fn() -> Result<(), nix::Error> + Send>),
Unlink(CompositeId, Box<dyn Fn(CompositeId) -> Result<(), nix::Error> + Send>),
}
impl MockDir {
@ -881,7 +881,7 @@ mod tests {
impl super::DirWriter for MockDir {
type File = MockFile;
fn create_file(&self, id: CompositeId) -> Result<Self::File, io::Error> {
fn create_file(&self, id: CompositeId) -> Result<Self::File, nix::Error> {
match self.0.lock().pop_front().expect("got create_file with no expectation") {
MockDirAction::Create(expected_id, ref f) => {
assert_eq!(id, expected_id);
@ -890,13 +890,13 @@ mod tests {
_ => panic!("got create_file({}), expected something else", id),
}
}
fn sync(&self) -> Result<(), io::Error> {
fn sync(&self) -> Result<(), nix::Error> {
match self.0.lock().pop_front().expect("got sync with no expectation") {
MockDirAction::Sync(f) => f(),
_ => panic!("got sync, expected something else"),
}
}
fn unlink_file(&self, id: CompositeId) -> Result<(), io::Error> {
fn unlink_file(&self, id: CompositeId) -> Result<(), nix::Error> {
match self.0.lock().pop_front().expect("got unlink_file with no expectation") {
MockDirAction::Unlink(expected_id, f) => {
assert_eq!(id, expected_id);
@ -991,6 +991,7 @@ mod tests {
}
fn eio() -> io::Error { io::Error::new(io::ErrorKind::Other, "got EIO") }
fn nix_eio() -> nix::Error { nix::Error::Sys(nix::errno::Errno::EIO) }
/// Tests the database flushing while a syncer is still processing a previous flush event.
#[test]
@ -1091,7 +1092,7 @@ mod tests {
1920, 1080, [0u8; 100].to_vec(), "avc1.000000".to_owned()).unwrap();
let mut w = Writer::new(&h.dir, &h.db, &h.channel, testutil::TEST_STREAM_ID,
video_sample_entry_id);
h.dir.expect(MockDirAction::Create(CompositeId::new(1, 1), Box::new(|_id| Err(eio()))));
h.dir.expect(MockDirAction::Create(CompositeId::new(1, 1), Box::new(|_id| Err(nix_eio()))));
let f = MockFile::new();
h.dir.expect(MockDirAction::Create(CompositeId::new(1, 1),
Box::new({ let f = f.clone(); move |_id| Ok(f.clone()) })));
@ -1114,7 +1115,7 @@ mod tests {
f.expect(MockFileAction::SyncAll(Box::new(|| Err(eio()))));
f.expect(MockFileAction::SyncAll(Box::new(|| Ok(()))));
w.write(b"1234", recording::Time(1), 0, true).unwrap();
h.dir.expect(MockDirAction::Sync(Box::new(|| Err(eio()))));
h.dir.expect(MockDirAction::Sync(Box::new(|| Err(nix_eio()))));
h.dir.expect(MockDirAction::Sync(Box::new(|| Ok(()))));
drop(w);
assert!(h.syncer.iter(&h.syncer_rcv)); // AsyncSave
@ -1194,11 +1195,11 @@ mod tests {
assert_eq!(s.bytes_to_delete, 0);
assert_eq!(s.bytes_to_add, 0);
assert_eq!(s.sample_file_bytes, 1);
Err(eio()) // force a retry.
Err(nix_eio()) // force a retry.
}
})));
h.dir.expect(MockDirAction::Unlink(CompositeId::new(1, 1), Box::new(|_| Ok(()))));
h.dir.expect(MockDirAction::Sync(Box::new(|| Err(eio()))));
h.dir.expect(MockDirAction::Sync(Box::new(|| Err(nix_eio()))));
h.dir.expect(MockDirAction::Sync(Box::new(|| Ok(()))));
drop(w);

View File

@ -303,7 +303,7 @@ fn edit_dir_dialog(db: &Arc<db::Database>, siv: &mut Cursive, dir_id: i32) {
l.open_sample_file_dirs(&[dir_id]).unwrap(); // TODO: don't unwrap.
let dir = l.sample_file_dirs_by_id().get(&dir_id).unwrap();
let stat = dir.get().unwrap().statfs().unwrap();
fs_capacity = stat.f_bsize as i64 * stat.f_bavail as i64 + total_used;
fs_capacity = stat.block_size() as i64 * stat.blocks_available() as i64 + total_used;
path = dir.path.clone();
}
Rc::new(RefCell::new(Model {

View File

@ -31,7 +31,7 @@
use db::dir;
use docopt;
use failure::{Error, Fail};
use libc;
use nix::fcntl::FlockArg;
use rusqlite;
use serde::Deserialize;
use std::path::Path;
@ -84,7 +84,7 @@ enum OpenMode {
fn open_dir(db_dir: &str, mode: OpenMode) -> Result<dir::Fd, Error> {
let dir = dir::Fd::open(db_dir, mode == OpenMode::Create)?;
let ro = mode == OpenMode::ReadOnly;
dir.lock(if ro { libc::LOCK_SH } else { libc::LOCK_EX } | libc::LOCK_NB)
dir.lock(if ro { FlockArg::LockExclusiveNonblock } else { FlockArg::LockSharedNonblock })
.map_err(|e| e.context(format!("db dir {:?} already in use; can't get {} lock",
db_dir, if ro { "shared" } else { "exclusive" })))?;
Ok(dir)