Skip to content

Commit

Permalink
Merge 13b9065 into 9c63d20
Browse files Browse the repository at this point in the history
  • Loading branch information
wcampbell0x2a authored Feb 10, 2024
2 parents 9c63d20 + 13b9065 commit 401f3dd
Show file tree
Hide file tree
Showing 12 changed files with 274 additions and 41 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### `backhand`
- Add support for `Socket` and `NamedFIFO` Inodes in library and extraction binaries. Thanks ([@tnias](https://github.com/tnias)) ([#472](https://github.com/wcampbell0x2a/backhand/pull/472), [#470](https://github.com/wcampbell0x2a/backhand/pull/470))
- Add `FilesytemWriter::push_fifo` and `FilesystemWriter::push_socket`
### `backhand-cli`
#### unsquashfs
- Performance: Remove progress bar Mutex lock when `--quiet` ([#430](https://github.com/wcampbell0x2a/backhand/pull/430))
Expand Down
49 changes: 49 additions & 0 deletions backhand-cli/src/bin/unsquashfs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use indicatif::{HumanDuration, ProgressBar, ProgressStyle};
use nix::libc::geteuid;
use nix::sys::stat::{dev_t, mknod, mode_t, umask, utimensat, utimes, Mode, SFlag, UtimensatFlags};
use nix::sys::time::{TimeSpec, TimeVal};
use nix::unistd::mkfifo;
use rayon::prelude::*;
use std::time::{Duration, Instant};

Expand Down Expand Up @@ -623,6 +624,54 @@ fn extract_all<'a, S: ParallelIterator<Item = &'a Node<SquashfsFileReader>>>(
}
}
}
InnerNode::NamedPipe => {
match mkfifo(
&filepath,
Mode::from_bits(mode_t::from(node.header.permissions)).unwrap(),
) {
Ok(_) => {
if args.info && !args.quiet {
created(&pb, filepath.to_str().unwrap());
}

set_attributes(&pb, args, &filepath, &node.header, root_process, true);
}
Err(_) => {
if args.info && !args.quiet {
created(&pb, filepath.to_str().unwrap());
}
let mut p = processing.lock().unwrap();
p.remove(fullpath);
drop(p);
return;
}
}
}
InnerNode::Socket => {
match mknod(
&filepath,
SFlag::S_IFSOCK,
Mode::from_bits(mode_t::from(node.header.permissions)).unwrap(),
dev_t::from(0_u64),
) {
Ok(_) => {
if args.info && !args.quiet {
created(&pb, filepath.to_str().unwrap());
}

set_attributes(&pb, args, &filepath, &node.header, root_process, true);
}
Err(_) => {
if args.info && !args.quiet {
created(&pb, filepath.to_str().unwrap());
let mut p = processing.lock().unwrap();
p.remove(fullpath);
drop(p);
}
return;
}
}
}
}
let mut p = processing.lock().unwrap();
p.remove(fullpath);
Expand Down
67 changes: 37 additions & 30 deletions backhand-test/tests/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,12 @@ pub fn test_squashfs_tools_unsquashfs(
}

// Test that both our unsquashfs and unsquashfs both extract to the same
pub fn test_bin_unsquashfs(file: &str, file_offset: Option<u64>, assert_success: bool) {
pub fn test_bin_unsquashfs(
file: &str,
file_offset: Option<u64>,
assert_success: bool,
run_squashfs_tools_unsquashfs: bool,
) {
let tmp_dir = tempdir().unwrap();
// Run "our" unsquashfs against the control
let cmd = get_base_command("unsquashfs-backhand")
Expand All @@ -78,37 +83,39 @@ pub fn test_bin_unsquashfs(file: &str, file_offset: Option<u64>, assert_success:
cmd.assert().code(&[0] as &[i32]);

// only squashfs-tools/unsquashfs when x86_64
#[cfg(feature = "__test_unsquashfs")]
{
let mut cmd = Command::new("unsquashfs");
let cmd = cmd.args([
"-d",
tmp_dir.path().join("squashfs-root-c").to_str().unwrap(),
"-o",
&file_offset.unwrap_or(0).to_string(),
// we don't run as root, avoid special file errors
"-ignore-errors",
//"-no-exit-code",
file,
]);
if run_squashfs_tools_unsquashfs {
#[cfg(feature = "__test_unsquashfs")]
{
let mut cmd = Command::new("unsquashfs");
let cmd = cmd.args([
"-d",
tmp_dir.path().join("squashfs-root-c").to_str().unwrap(),
"-o",
&file_offset.unwrap_or(0).to_string(),
// we don't run as root, avoid special file errors
"-ignore-errors",
//"-no-exit-code",
file,
]);

// For older version of squashfs-tools that the cross-rs/cross projects uses,
// we can't using new -no-exit-code option in unsquashfs, so for the images
// that contain /dev devices we can't assert the success of unsquashfs.
if assert_success {
cmd.assert().code(&[0] as &[i32]);
} else {
cmd.assert();
}
tracing::info!("{:?}", cmd);
// For older version of squashfs-tools that the cross-rs/cross projects uses,
// we can't using new -no-exit-code option in unsquashfs, so for the images
// that contain /dev devices we can't assert the success of unsquashfs.
if assert_success {
cmd.assert().code(&[0] as &[i32]);
} else {
cmd.assert();
}
tracing::info!("{:?}", cmd);

let d = dir_diff::is_different(
tmp_dir.path().join("squashfs-root-rust"),
tmp_dir.path().join("squashfs-root-c"),
);
// remove the followig comment to keep around tmp dirs
// let _ = tmp_dir.into_path();
assert!(!d.expect("couldn't compare dirs"));
let d = dir_diff::is_different(
tmp_dir.path().join("squashfs-root-rust"),
tmp_dir.path().join("squashfs-root-c"),
);
// remove the followig comment to keep around tmp dirs
// let _ = tmp_dir.into_path();
assert!(!d.expect("couldn't compare dirs"));
}
}
}

Expand Down
4 changes: 2 additions & 2 deletions backhand-test/tests/mutate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ fn test_add_00() {
{
let control_new_path = format!("{TEST_PATH}/control.squashfs");
test_squashfs_tools_unsquashfs(&new_path, &control_new_path, None, true);
test_bin_unsquashfs(&og_path, None, true);
test_bin_unsquashfs(&new_path, None, true);
test_bin_unsquashfs(&og_path, None, true, true);
test_bin_unsquashfs(&new_path, None, true, true);
}
}
2 changes: 1 addition & 1 deletion backhand-test/tests/raw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,6 @@ fn test_raw_00() {
{
let control_new_path = format!("{TEST_PATH}/control.squashfs");
test_squashfs_tools_unsquashfs(&new_path, &control_new_path, None, true);
test_bin_unsquashfs(&new_path, None, true);
test_bin_unsquashfs(&new_path, None, true, true);
}
}
60 changes: 54 additions & 6 deletions backhand-test/tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,18 @@ fn full_test(
offset: u64,
verify: Verify,
assert_success: bool,
) {
full_test_inner(assets_defs, filepath, test_path, offset, verify, assert_success, true)
}

fn full_test_inner(
assets_defs: &[TestAssetDef],
filepath: &str,
test_path: &str,
offset: u64,
verify: Verify,
assert_success: bool,
run_squashfs_tools_unsquashfs: bool,
) {
test_assets::download_test_files(assets_defs, test_path, true).unwrap();

Expand Down Expand Up @@ -81,15 +93,32 @@ fn full_test(

match verify {
Verify::Extract => {
#[cfg(feature = "__test_unsquashfs")]
{
info!("starting squashfs-tools/unsquashfs test");
test_squashfs_tools_unsquashfs(&og_path, &new_path, Some(offset), assert_success);
if run_squashfs_tools_unsquashfs {
#[cfg(feature = "__test_unsquashfs")]
{
info!("starting squashfs-tools/unsquashfs test");
test_squashfs_tools_unsquashfs(
&og_path,
&new_path,
Some(offset),
assert_success,
);
}
}
info!("starting backhand/unsquashfs original test");
test_bin_unsquashfs(&og_path, Some(offset), assert_success);
test_bin_unsquashfs(
&og_path,
Some(offset),
assert_success,
run_squashfs_tools_unsquashfs,
);
info!("starting backhand/unsquashfs created test");
test_bin_unsquashfs(&new_path, Some(offset), assert_success);
test_bin_unsquashfs(
&new_path,
Some(offset),
assert_success,
run_squashfs_tools_unsquashfs,
);
}
}
}
Expand Down Expand Up @@ -453,3 +482,22 @@ fn test_few_dirs_many_files() {
only_read(&asset_defs, FILE_NAME, TEST_PATH, 0);
}
}

#[test]
#[cfg(any(feature = "gzip", feature = "gzip-zune-inflate"))]
fn test_socket_fifo() {
const FILE_NAME: &str = "squashfs_v4.specfile.bin";
let asset_defs = [TestAssetDef {
filename: FILE_NAME.to_string(),
hash: "d27f2e4baf57df961b9aa7298ac390a54fd0d2c904bf1d4baaee49cbdd0a93f1".to_string(),
url: format!("https://wcampbell.dev/squashfs/testing/test_socket_fifo/{FILE_NAME}"),
}];

const TEST_PATH: &str = "test-assets/socket_fifo";

if has_gzip_feature() {
full_test_inner(&asset_defs, FILE_NAME, TEST_PATH, 0, Verify::Extract, true, false);
} else {
only_read(&asset_defs, FILE_NAME, TEST_PATH, 0);
}
}
62 changes: 60 additions & 2 deletions backhand/src/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ use std::os::unix::prelude::OsStrExt;
use crate::data::Added;
use crate::dir::{Dir, DirEntry};
use crate::inode::{
BasicDeviceSpecialFile, BasicDirectory, BasicFile, BasicSymlink, ExtendedDirectory, Inode,
InodeHeader, InodeId, InodeInner,
BasicDeviceSpecialFile, BasicDirectory, BasicFile, BasicSymlink, ExtendedDirectory, IPCNode,
Inode, InodeHeader, InodeId, InodeInner,
};
use crate::kinds::Kind;
use crate::metadata::MetadataWriter;
Expand Down Expand Up @@ -235,6 +235,64 @@ impl<'a> Entry<'a> {

block_inode.to_bytes(node_path.as_bytes(), inode_writer, superblock, kind)
}

/// Write data and metadata for named pipe node
#[allow(clippy::too_many_arguments)]
pub fn named_pipe(
node_path: &'a OsStr,
header: NodeHeader,
inode: u32,
inode_writer: &mut MetadataWriter,
superblock: &SuperBlock,
kind: &Kind,
id_table: &[Id],
) -> Self {
let uid = id_table.iter().position(|a| a.num == header.uid).unwrap() as u16;
let gid = id_table.iter().position(|a| a.num == header.gid).unwrap() as u16;
let header = InodeHeader {
inode_number: inode,
uid,
gid,
permissions: header.permissions,
mtime: header.mtime,
};
let char_inode = Inode::new(
InodeId::BasicNamedPipe,
header,
InodeInner::BasicNamedPipe(IPCNode { link_count: 0x1 }),
);

char_inode.to_bytes(node_path.as_bytes(), inode_writer, superblock, kind)
}

/// Write data and metadata for socket
#[allow(clippy::too_many_arguments)]
pub fn socket(
node_path: &'a OsStr,
header: NodeHeader,
inode: u32,
inode_writer: &mut MetadataWriter,
superblock: &SuperBlock,
kind: &Kind,
id_table: &[Id],
) -> Self {
let uid = id_table.iter().position(|a| a.num == header.uid).unwrap() as u16;
let gid = id_table.iter().position(|a| a.num == header.gid).unwrap() as u16;
let header = InodeHeader {
inode_number: inode,
uid,
gid,
permissions: header.permissions,
mtime: header.mtime,
};
let char_inode = Inode::new(
InodeId::BasicSocket,
header,
InodeInner::BasicSocket(IPCNode { link_count: 0x1 }),
);

char_inode.to_bytes(node_path.as_bytes(), inode_writer, superblock, kind)
}
}

impl<'a> fmt::Debug for Entry<'a> {
Expand Down
2 changes: 2 additions & 0 deletions backhand/src/filesystem/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ pub enum InnerNode<T> {
Dir(SquashfsDir),
CharacterDevice(SquashfsCharacterDevice),
BlockDevice(SquashfsBlockDevice),
NamedPipe,
Socket,
}

/// Unread file for filesystem
Expand Down
2 changes: 2 additions & 0 deletions backhand/src/filesystem/reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ use crate::{Node, Squashfs, SquashfsFileReader};
/// InnerNode::Dir(_) => (),
/// InnerNode::CharacterDevice(_) => (),
/// InnerNode::BlockDevice(_) => (),
/// InnerNode::NamedPipe => (),
/// InnerNode::Socket => (),
/// }
/// }
/// ```
Expand Down
Loading

0 comments on commit 401f3dd

Please sign in to comment.