Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion json/empty.json
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@
{}
19 changes: 18 additions & 1 deletion nodelike/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ pub fn cli_base(name: impl Into<clap::builder::Str>) -> clap::Command {
.action(ArgAction::SetTrue)

)
.arg(
Arg::new("STRICT")
.help("Strictly conform to format specifications (disables empty-file detection; empty JSON files will produce parse errors)")
.long("strict")
.action(ArgAction::SetTrue)
)
}

/// Configuration information
Expand Down Expand Up @@ -104,6 +110,7 @@ pub struct Config {
pub timing: bool,
pub mount: Option<PathBuf>,
pub cleanup_mount: bool,
pub strict: bool,
}

#[derive(Debug)]
Expand Down Expand Up @@ -200,6 +207,7 @@ impl Config {
config.timing = args.get_flag("TIMING");
config.add_newlines = !args.get_flag("EXACT");
config.allow_xattr = !args.get_flag("NOXATTR");
config.strict = args.get_flag("STRICT");

// munging policy
config.munge = match args.get_one::<String>("MUNGE") {
Expand Down Expand Up @@ -252,11 +260,19 @@ impl Config {

/// Generate a reader for input
///
/// A return of `None` means to start from an empty named directory
/// A return of `None` means to start from an empty named directory.
/// When `--strict` is not set, a zero-byte file is treated as empty.
pub fn input_reader(&self) -> Option<Box<dyn std::io::Read>> {
match &self.input {
Input::Stdin => Some(Box::new(std::io::stdin())),
Input::File(file) => {
if !self.strict
&& let Ok(meta) = std::fs::metadata(file)
&& meta.len() == 0
{
debug!("Empty file detected, treating as empty input");
return None;
}
let fmt = self.input_format;
let file = std::fs::File::open(file).unwrap_or_else(|e| {
error!("Unable to open {} for {fmt} input: {e}", file.display());
Expand Down Expand Up @@ -315,6 +331,7 @@ impl Default for Config {
timing: false,
mount: None,
cleanup_mount: false,
strict: false,
}
}
}
11 changes: 5 additions & 6 deletions nodelike/src/nodelike.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,9 +187,11 @@ where
match (*self).node(config) {
Node::String(t, s) => Node::String(t, s),
Node::Bytes(b) => Node::Bytes(b),
Node::List(vs) => {
Node::List(vs.into_iter().map(|v| Box::new(v) as Box<dyn Nodelike>).collect())
}
Node::List(vs) => Node::List(
vs.into_iter()
.map(|v| Box::new(v) as Box<dyn Nodelike>)
.collect(),
),
Node::Map(kvs) => Node::Map(
kvs.into_iter()
.map(|(k, v)| (k, Box::new(v) as Box<dyn Nodelike>))
Expand Down Expand Up @@ -281,7 +283,6 @@ pub mod json {
}
}


fn from_string(typ: Typ, contents: String, _config: &Config) -> Self {
match typ {
Typ::Auto => {
Expand Down Expand Up @@ -439,7 +440,6 @@ pub mod toml {
}
}


fn from_string(typ: Typ, contents: String, _config: &Config) -> Self {
let v = match typ {
Typ::Auto => {
Expand Down Expand Up @@ -653,7 +653,6 @@ pub mod yaml {
}
}


fn from_string(typ: Typ, contents: String, _config: &Config) -> Self {
match typ {
Typ::Auto => {
Expand Down
5 changes: 4 additions & 1 deletion pack/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -390,16 +390,19 @@ impl Pack {
Resolving type automatically."
);
}
let mut count = 0;
let all_files_begin_with_num = fs::read_dir(path.clone())?
.map(|res| res.map(|e| e.path()))
.map(|e| e.unwrap().file_name().unwrap().to_str().unwrap().to_owned())
.all(|filename| {
count += 1;

filename.chars().nth(0).unwrap().is_ascii_digit()
|| filename.len() > 1
&& filename.chars().nth(0).unwrap() == '-'
&& filename.chars().nth(1).unwrap().is_ascii_digit()
});
if all_files_begin_with_num {
if all_files_begin_with_num && count > 0 {
path_type = "list"
} else {
path_type = "named"
Expand Down
8 changes: 4 additions & 4 deletions run_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ then
printf "Couldn't find ffs or pack/unpack; building...\n" >&2
(cd "$FFS_TOP"
if [ "$(uname -s)" = "Darwin" ]
then
cargo build --bin pack --bin unpack
else
then
cargo build --bin pack --bin unpack
else
cargo build --workspace
fi)
if ! detect_tools
Expand Down Expand Up @@ -67,7 +67,7 @@ do
esac

printf "========== STARTING TEST: $tname\n"
(RUST_LOG="ffs=debug,unpack=debug,pack=debug,fuser=debug"; export RUST_LOG; ./${test} >$LOG/$tname.out 2>$LOG/$tname.err; echo $?>$LOG/$tname.ec) &
(RUST_LOG="debug"; export RUST_LOG; ./${test} >$LOG/$tname.out 2>$LOG/$tname.err; echo $?>$LOG/$tname.ec) &
: $((TOTAL += 1))

# don't slam 'em
Expand Down
51 changes: 51 additions & 0 deletions tests/ffs.empty_file.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/bin/sh

TIMEOUT="$(cd ../utils; pwd)/timeout"
WAITFOR="$(cd ../utils; pwd)/waitfor"
. ./fail.def

# --- JSON empty file ---
MNT=$(mktemp -d)

ffs -m "$MNT" ../json/empty.json &
PID=$!
"$WAITFOR" mount "$MNT"

[ -z "$(ls "$MNT")" ] || fail json_notempty
"$WAITFOR" umount "$MNT" || fail json_unmount
"$WAITFOR" exit $PID
kill -0 $PID >/dev/null 2>&1 && fail json_process
rmdir "$MNT" || fail json_mount

# --- TOML empty file ---
MNT=$(mktemp -d)

ffs -m "$MNT" ../toml/empty.toml &
PID=$!
"$WAITFOR" mount "$MNT"

[ -z "$(ls "$MNT")" ] || fail toml_notempty
"$WAITFOR" umount "$MNT" || fail toml_unmount
"$WAITFOR" exit $PID
kill -0 $PID >/dev/null 2>&1 && fail toml_process
rmdir "$MNT" || fail toml_mount

# --- YAML empty file ---
MNT=$(mktemp -d)

ffs -m "$MNT" ../yaml/empty.yaml &
PID=$!
"$WAITFOR" mount "$MNT"

[ -z "$(ls "$MNT")" ] || fail yaml_notempty
"$WAITFOR" umount "$MNT" || fail yaml_unmount
"$WAITFOR" exit $PID
kill -0 $PID >/dev/null 2>&1 && fail yaml_process
rmdir "$MNT" || fail yaml_mount

# --- --strict should error on empty JSON ---
MNT=$(mktemp -d)

"$TIMEOUT" -t 2 ffs --strict -m "$MNT" ../json/empty.json 2>/dev/null
[ $? -ne 0 ] || fail strict_should_error
rmdir "$MNT" || fail strict_mount
36 changes: 36 additions & 0 deletions tests/packunpack.empty_file.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/bin/sh

TIMEOUT="$(cd ../utils; pwd)/timeout"

fail() {
echo FAILED: $1
if [ "$MNT" ]
then
rm -r "$MNT"
fi
exit 1
}

# --- JSON empty file ---
MNT=$(mktemp -d)
unpack --into "$MNT" ../json/empty.json || fail json_unpack
[ -z "$(ls "$MNT")" ] || fail json_notempty
rm -r "$MNT" || fail json_cleanup

# --- TOML empty file ---
MNT=$(mktemp -d)
unpack --into "$MNT" ../toml/empty.toml || fail toml_unpack
[ -z "$(ls "$MNT")" ] || fail toml_notempty
rm -r "$MNT" || fail toml_cleanup

# --- YAML empty file ---
MNT=$(mktemp -d)
unpack --into "$MNT" ../yaml/empty.yaml || fail yaml_unpack
[ -z "$(ls "$MNT")" ] || fail yaml_notempty
rm -r "$MNT" || fail yaml_cleanup

# --- --strict should error on empty JSON ---
MNT=$(mktemp -d)
"$TIMEOUT" -t 2 unpack --strict --into "$MNT" ../json/empty.json 2>/dev/null
[ $? -ne 0 ] || fail strict_should_error
rm -r "$MNT" || fail strict_cleanup
6 changes: 4 additions & 2 deletions unpack/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -310,8 +310,10 @@ fn main() -> std::io::Result<()> {
let reader = match config.input_reader() {
Some(reader) => reader,
None => {
error!("Input not specified");
std::process::exit(ERROR_STATUS_CLI);
// Empty input: the mount directory already exists, so just
// leave it as an empty directory.
info!("Empty input; unpacked into empty directory {mount:?}");
return Ok(());
}
};

Expand Down
10 changes: 5 additions & 5 deletions utils/waitfor
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,19 @@ elapsed=0
while [ "$(echo "$elapsed < $timeout" | bc)" -eq 1 ]; do
case "$cmd" in
mount) mountpoint -q "$arg" && exit 0 ;;
umount) umount "$arg" 2>/dev/null && exit 0 ;;
exit) kill -0 "$arg" 2>/dev/null || exit 0 ;;
umount) umount "$arg" >/dev/null 2>&1 && exit 0 ;;
exit) kill -0 "$arg" >/dev/null 2>&1 || exit 0 ;;
*) echo "waitfor: unknown command '$cmd'" >&2; exit 1 ;;
esac
sleep "$INTERVAL"
elapsed=$(echo "$elapsed + $INTERVAL" | bc)
done

if [ "$cmd" = "exit" ]; then
kill -TERM "$arg" 2>/dev/null
if kill -0 "$arg" 2>/dev/null; then
kill -TERM "$arg" >/dev/null 2>&1
if kill -0 "$arg" >/dev/null 2>&1; then
sleep "$INTERVAL"
kill -KILL "$arg" 2>/dev/null
kill -KILL "$arg" >/dev/null 2>&1
fi
fi

Expand Down
Empty file added yaml/empty.yaml
Empty file.