62 lines
2.2 KiB
Bash
Executable File
62 lines
2.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -Eeuo pipefail
|
|
|
|
[[ $# -eq 2 ]] || { echo "Usage: $0 ROOTFS.ext4 TREE" >&2; exit 2; }
|
|
ROOTFS=$1
|
|
TREE=$2
|
|
[[ -f "$ROOTFS" && -d "$TREE" ]] || { echo "Invalid rootfs or tree" >&2; exit 2; }
|
|
|
|
COMMANDS=$(mktemp)
|
|
trap 'rm -f -- "$COMMANDS"' EXIT
|
|
|
|
escape_debugfs() {
|
|
local value=$1
|
|
value=${value//\\/\\\\}
|
|
value=${value//\"/\\\"}
|
|
printf '%s' "$value"
|
|
}
|
|
|
|
while IFS= read -r -d '' source_path; do
|
|
relative_path=${source_path#"$TREE"/}
|
|
target_path="/${relative_path}"
|
|
quoted_target=$(escape_debugfs "$target_path")
|
|
if [[ -d "$source_path" && ! -L "$source_path" ]]; then
|
|
printf 'mkdir "%s"\n' "$quoted_target" >>"$COMMANDS"
|
|
fi
|
|
done < <(find "$TREE" -mindepth 1 -type d -print0 | sort -z)
|
|
|
|
while IFS= read -r -d '' source_path; do
|
|
relative_path=${source_path#"$TREE"/}
|
|
target_path="/${relative_path}"
|
|
quoted_source=$(escape_debugfs "$source_path")
|
|
quoted_target=$(escape_debugfs "$target_path")
|
|
if [[ -L "$source_path" ]]; then
|
|
printf 'rm "%s"\n' "$quoted_target" >>"$COMMANDS"
|
|
printf 'symlink "%s" "%s"\n' "$quoted_target" "$(escape_debugfs "$(readlink "$source_path")")" >>"$COMMANDS"
|
|
elif [[ -f "$source_path" ]]; then
|
|
printf 'rm "%s"\n' "$quoted_target" >>"$COMMANDS"
|
|
printf 'write "%s" "%s"\n' "$quoted_source" "$quoted_target" >>"$COMMANDS"
|
|
fi
|
|
done < <(find "$TREE" -mindepth 1 \( -type f -o -type l \) -print0 | sort -z)
|
|
|
|
while IFS= read -r -d '' source_path; do
|
|
relative_path=${source_path#"$TREE"/}
|
|
target_path="/${relative_path}"
|
|
quoted_target=$(escape_debugfs "$target_path")
|
|
permissions=$(stat -c '%a' "$source_path")
|
|
if [[ -d "$source_path" && ! -L "$source_path" ]]; then
|
|
file_type=040000
|
|
elif [[ -L "$source_path" ]]; then
|
|
file_type=0120000
|
|
else
|
|
file_type=0100000
|
|
fi
|
|
printf 'set_inode_field "%s" uid 0\n' "$quoted_target" >>"$COMMANDS"
|
|
printf 'set_inode_field "%s" gid 0\n' "$quoted_target" >>"$COMMANDS"
|
|
printf 'set_inode_field "%s" mode 0%o\n' "$quoted_target" "$((file_type | 8#$permissions))" >>"$COMMANDS"
|
|
done < <(find "$TREE" -mindepth 1 -print0 | sort -z)
|
|
|
|
# debugfs reports benign "already exists" and "file not found" diagnostics for
|
|
# idempotent mkdir/rm operations. The caller performs explicit postconditions.
|
|
debugfs -w -f "$COMMANDS" "$ROOTFS" >/dev/null 2>&1
|