From ff17410cbee1492a5ca87869f76a2dafb7f90430 Mon Sep 17 00:00:00 2001 From: Lars Wirzenius Date: Fri, 19 Feb 2021 20:19:15 +0200 Subject: feat: backup and restore named pipes (FIFOs) --- obnam.md | 6 +++++- src/client.rs | 1 + src/cmd/list_files.rs | 1 + src/cmd/restore.rs | 18 +++++++++++++++++- src/fsentry.rs | 6 ++++++ subplot/data.py | 4 ++++ subplot/data.yaml | 3 +++ 7 files changed, 37 insertions(+), 2 deletions(-) diff --git a/obnam.md b/obnam.md index e386b17..240dcf0 100644 --- a/obnam.md +++ b/obnam.md @@ -1286,7 +1286,10 @@ then command fails ## Irregular files This scenario verifies that Obnam backs up and restores files that -aren't regular files or directories. +aren't regular files, directories, or symbolic links. Specifically, +Unix domain sockets and named pipes (FIFOs). However, block and +character device nodes are not tested, as that would require running +the test suite with `root` permissions and that would be awkward. ~~~scenario given an installed obnam @@ -1294,6 +1297,7 @@ and a running chunk server and a client config based on smoke.yaml and a file live/data.dat containing some random data and a Unix socket live/socket +and a named pipe live/pipe and a manifest of the directory live in live.yaml when I run obnam --config smoke.yaml backup when I invoke obnam --config smoke.yaml restore latest rest diff --git a/src/client.rs b/src/client.rs index 9d1e8ff..7d1613c 100644 --- a/src/client.rs +++ b/src/client.rs @@ -164,6 +164,7 @@ impl BackupClient { FilesystemKind::Directory => vec![], FilesystemKind::Symlink => vec![], FilesystemKind::Socket => vec![], + FilesystemKind::Fifo => vec![], }; info!("upload OK for {:?}", path); Ok(ids) diff --git a/src/cmd/list_files.rs b/src/cmd/list_files.rs index 4982cc2..ec3e52e 100644 --- a/src/cmd/list_files.rs +++ b/src/cmd/list_files.rs @@ -36,6 +36,7 @@ fn format_entry(e: &FilesystemEntry, reason: Reason) -> String { FilesystemKind::Directory => "d", FilesystemKind::Symlink => "l", FilesystemKind::Socket => "s", + FilesystemKind::Fifo => "p", }; format!("{} {} ({})", kind, e.pathbuf().display(), reason) } diff --git a/src/cmd/restore.rs b/src/cmd/restore.rs index 147a56b..b394d7d 100644 --- a/src/cmd/restore.rs +++ b/src/cmd/restore.rs @@ -5,7 +5,7 @@ use crate::error::ObnamError; use crate::fsentry::{FilesystemEntry, FilesystemKind}; use crate::generation::{LocalGeneration, LocalGenerationError}; use indicatif::{ProgressBar, ProgressStyle}; -use libc::{chmod, timespec, utimensat, AT_FDCWD}; +use libc::{chmod, mkfifo, timespec, utimensat, AT_FDCWD}; use log::{debug, error, info}; use std::ffi::CString; use std::io::prelude::*; @@ -73,6 +73,9 @@ struct Opt { #[derive(Debug, thiserror::Error)] pub enum RestoreError { + #[error("Could not create named pipe (FIFO) {0}")] + NamedPipeCreationError(PathBuf), + #[error(transparent)] ClientError(#[from] ClientError), @@ -112,6 +115,7 @@ fn restore_generation( FilesystemKind::Directory => restore_directory(&to)?, FilesystemKind::Symlink => restore_symlink(&to, &entry)?, FilesystemKind::Socket => restore_socket(&to, &entry)?, + FilesystemKind::Fifo => restore_fifo(&to, &entry)?, } Ok(()) } @@ -188,6 +192,18 @@ fn restore_socket(path: &Path, entry: &FilesystemEntry) -> RestoreResult<()> { Ok(()) } +fn restore_fifo(path: &Path, entry: &FilesystemEntry) -> RestoreResult<()> { + debug!("creating fifo {:?}", path); + let filename = path_to_cstring(path); + match unsafe { mkfifo(filename.as_ptr(), 0) } { + -1 => { + return Err(RestoreError::NamedPipeCreationError(path.to_path_buf())); + } + _ => restore_metadata(path, entry)?, + } + Ok(()) +} + fn restore_metadata(path: &Path, entry: &FilesystemEntry) -> RestoreResult<()> { debug!("restoring metadata for {}", entry.pathbuf().display()); diff --git a/src/fsentry.rs b/src/fsentry.rs index 9384ec6..570877a 100644 --- a/src/fsentry.rs +++ b/src/fsentry.rs @@ -129,6 +129,7 @@ pub enum FilesystemKind { Directory, Symlink, Socket, + Fifo, } impl FilesystemKind { @@ -141,6 +142,8 @@ impl FilesystemKind { FilesystemKind::Symlink } else if file_type.is_socket() { FilesystemKind::Socket + } else if file_type.is_fifo() { + FilesystemKind::Fifo } else { panic!("unknown file type {:?}", file_type); } @@ -152,6 +155,7 @@ impl FilesystemKind { FilesystemKind::Directory => 1, FilesystemKind::Symlink => 2, FilesystemKind::Socket => 3, + FilesystemKind::Fifo => 4, } } @@ -161,6 +165,7 @@ impl FilesystemKind { 1 => Ok(FilesystemKind::Directory), 2 => Ok(FilesystemKind::Symlink), 3 => Ok(FilesystemKind::Socket), + 4 => Ok(FilesystemKind::Fifo), _ => Err(FsEntryError::UnknownFileKindCode(code).into()), } } @@ -182,6 +187,7 @@ mod test { one_file_kind_round_trip(FilesystemKind::Directory); one_file_kind_round_trip(FilesystemKind::Symlink); one_file_kind_round_trip(FilesystemKind::Socket); + one_file_kind_round_trip(FilesystemKind::Fifo); } fn one_file_kind_round_trip(kind: FilesystemKind) { diff --git a/subplot/data.py b/subplot/data.py index 3c369d1..0c1a4a5 100644 --- a/subplot/data.py +++ b/subplot/data.py @@ -24,6 +24,10 @@ def create_unix_socket(ctx, filename=None): fd.bind(filename) +def create_fifo(ctx, filename=None): + os.mkfifo(filename) + + def create_nonutf8_filename(ctx, dirname=None): filename = "\x88" os.mkdir(dirname) diff --git a/subplot/data.yaml b/subplot/data.yaml index 97e6eed..0e13abd 100644 --- a/subplot/data.yaml +++ b/subplot/data.yaml @@ -7,6 +7,9 @@ - given: "a Unix socket {filename}" function: create_unix_socket +- given: "a named pipe {filename}" + function: create_fifo + - given: "a file in {dirname} with a non-UTF8 filename" function: create_nonutf8_filename -- cgit v1.2.1