mirror of
https://github.com/scottlamb/moonfire-nvr.git
synced 2024-12-24 06:05:55 -05:00
use nix to remove many uses of unsafe
This commit is contained in:
parent
d75157af56
commit
bb227491b6
27
Cargo.lock
generated
27
Cargo.lock
generated
@ -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"
|
||||
|
@ -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" }
|
||||
|
@ -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 = [] }
|
||||
|
7
db/db.rs
7
db/db.rs
@ -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
158
db/dir.rs
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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(())
|
||||
|
39
db/writer.rs
39
db/writer.rs
@ -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);
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user