Current State
Builder script: /usr/local/bin/srch_flat_collect.
Interactive srch option collection.
Runs srch -r against selected roots.
Centralizes logging to operation_log.txt under the destination.
Mover script: /usr/local/libexec/srch_move_one.
Validates regular files.
Resolves collisions with deterministic numeric suffixes.
Performs copy+delete semantics and logs all actions.
Design and Implementation Details
Flat destination: All files are moved directly into the destination directory with no path replication.
Collision handling: Numeric suffixes are appended to the basename before extension.
Concurrency protection: Locking is used to avoid naming collisions.
Cross-filesystem safety: Copy followed by delete ensures moves work across filesystems.
Permissions: Destination is owned by linaro:linaro and set to a+rwX.
Resources and References
Destination root: /home/linaro/.
Audit log: operation_log.txt inside the destination directory.
Operational Guidance
Ensure srch is installed and both scripts are executable.
Run the builder with sudo.
Answer interactive prompts to build the srch command.
Review operation_log.txt for outcomes and errors.
#/usr/local/bin/2-orig-restore.sh
#!/usr/bin/env bash
# 2-orig-restore.sh
# Debian 11 (bullseye) compatible. Run as root.
#
# Copies each file from its `dest` path to its corresponding `orig` path,
# renaming it to the filename implied by `orig`, then deletes the `dest` file.
# Makes per-file backups of existing `orig` targets.
set -Eeuo pipefail
usage() {
cat <<'USAGE'
Usage: restore-cts-to-orig.sh [--dry-run]
Options:
--dry-run Print what would be done without making changes.
Notes:
- Must be run as root.
- Existing target files at `orig` are backed up under /var/backups/cts-restore-*/...
USAGE
}
DRY_RUN=0
if [[ ${1:-} == "-h" || ${1:-} == "--help" ]]; then
usage
exit 0
fi
if [[ ${1:-} == "--dry-run" ]]; then
DRY_RUN=1
fi
if [[ ${EUID:-$(id -u)} -ne 0 ]]; then
echo "ERROR: This script must be run as root." >&2
exit 1
fi
TS="$(date -u +%Y%m%dT%H%M%SZ)"
BACKUP_ROOT="/var/backups/cts-restore-${TS}"
log() { printf '[%s] %s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$*"; }
_quote_cmd() {
local out=() a
for a in "$@"; do
out+=("$(printf '%q' "$a")")
done
printf '%s' "${out[*]}"
}
run_cmd() {
if (( DRY_RUN )); then
log "DRY-RUN: $(_quote_cmd "$@")"
else
log "$(_quote_cmd "$@")"
"$@"
fi
}
# Embedded mapping list. Each line is a simple shell assignment.
read -r -d '' LOCATIONS <<'LOCATIONS' || true
orig="/usr/share/antigravity/resources/app/node_modules/@antfu/install-pkg/dist/index.d.cts" norm="/usr/share/antigravity/resources/app/node_modules/@antfu/install-pkg/dist/index.d.cts" dest="/home/linaro/cts-archive-treesheets/index.d_003.cts"
orig="/usr/share/antigravity/resources/app/node_modules/cacache/node_modules/jackspeak/dist/commonjs/parse-args-cjs.d.cts.map" norm="/usr/share/antigravity/resources/app/node_modules/cacache/node_modules/jackspeak/dist/commonjs/parse-args-cjs.d.cts.map" dest="/home/linaro/cts-archive-treesheets/parse-args-cjs.d.cts.map"
orig="/usr/share/antigravity/resources/app/node_modules/gaxios/build/cjs/src/util.d.cts" norm="/usr/share/antigravity/resources/app/node_modules/gaxios/build/cjs/src/util.d.cts" dest="/home/linaro/cts-archive-treesheets/util.d.cts"
orig="/usr/share/antigravity/resources/app/node_modules/gaxios/build/esm/src/util.d.cts" norm="/usr/share/antigravity/resources/app/node_modules/gaxios/build/esm/src/util.d.cts" dest="/home/linaro/cts-archive-treesheets/util.d_001.cts"
orig="/usr/share/antigravity/resources/app/node_modules/google-auth-library/build/src/shared.d.cts" norm="/usr/share/antigravity/resources/app/node_modules/google-auth-library/build/src/shared.d.cts" dest="/home/linaro/cts-archive-treesheets/shared.d.cts"
orig="/usr/share/antigravity/resources/app/node_modules/@isaacs/cliui/build/index.d.cts" norm="/usr/share/antigravity/resources/app/node_modules/@isaacs/cliui/build/index.d.cts" dest="/home/linaro/cts-archive-treesheets/index.d_008.cts"
orig="/usr/share/antigravity/resources/app/node_modules/local-pkg/dist/index.d.cts" norm="/usr/share/antigravity/resources/app/node_modules/local-pkg/dist/index.d.cts" dest="/home/linaro/cts-archive-treesheets/index.d.cts"
orig="/usr/share/antigravity/resources/app/node_modules/mlly/dist/index.d.cts" norm="/usr/share/antigravity/resources/app/node_modules/mlly/dist/index.d.cts" dest="/home/linaro/cts-archive-treesheets/index.d_002.cts"
orig="/usr/share/antigravity/resources/app/node_modules/mlly/node_modules/confbox/dist/index.d.cts" norm="/usr/share/antigravity/resources/app/node_modules/mlly/node_modules/confbox/dist/index.d.cts" dest="/home/linaro/cts-archive-treesheets/index.d_007.cts"
orig="/usr/share/antigravity/resources/app/node_modules/mlly/node_modules/confbox/dist/json5.d.cts" norm="/usr/share/antigravity/resources/app/node_modules/mlly/node_modules/confbox/dist/json5.d.cts" dest="/home/linaro/cts-archive-treesheets/json5.d.cts"
orig="/usr/share/antigravity/resources/app/node_modules/mlly/node_modules/confbox/dist/jsonc.d.cts" norm="/usr/share/antigravity/resources/app/node_modules/mlly/node_modules/confbox/dist/jsonc.d.cts" dest="/home/linaro/cts-archive-treesheets/jsonc.d.cts"
orig="/usr/share/antigravity/resources/app/node_modules/mlly/node_modules/confbox/dist/shared/confbox.9745c98f.d.cts" norm="/usr/share/antigravity/resources/app/node_modules/mlly/node_modules/confbox/dist/shared/confbox.9745c98f.d.cts" dest="/home/linaro/cts-archive-treesheets/confbox.9745c98f.d.cts"
orig="/usr/share/antigravity/resources/app/node_modules/mlly/node_modules/confbox/dist/toml.d.cts" norm="/usr/share/antigravity/resources/app/node_modules/mlly/node_modules/confbox/dist/toml.d.cts" dest="/home/linaro/cts-archive-treesheets/toml.d.cts"
orig="/usr/share/antigravity/resources/app/node_modules/mlly/node_modules/confbox/dist/yaml.d.cts" norm="/usr/share/antigravity/resources/app/node_modules/mlly/node_modules/confbox/dist/yaml.d.cts" dest="/home/linaro/cts-archive-treesheets/yaml.d.cts"
orig="/usr/share/antigravity/resources/app/node_modules/mlly/node_modules/pkg-types/dist/index.d.cts" norm="/usr/share/antigravity/resources/app/node_modules/mlly/node_modules/pkg-types/dist/index.d.cts" dest="/home/linaro/cts-archive-treesheets/index.d_007.cts"
orig="/usr/share/antigravity/resources/app/node_modules/pathe/dist/index.d.cts" norm="/usr/share/antigravity/resources/app/node_modules/pathe/dist/index.d.cts" dest="/home/linaro/cts-archive-treesheets/index.d_005.cts"
orig="/usr/share/antigravity/resources/app/node_modules/pathe/dist/utils.d.cts" norm="/usr/share/antigravity/resources/app/node_modules/pathe/dist/utils.d.cts" dest="/home/linaro/cts-archive-treesheets/utils.d.cts"
orig="/usr/share/antigravity/resources/app/node_modules/quansync/dist/index.d.cts" norm="/usr/share/antigravity/resources/app/node_modules/quansync/dist/index.d.cts" dest="/home/linaro/cts-archive-treesheets/index.d_009.cts"
orig="/usr/share/antigravity/resources/app/node_modules/quansync/dist/macro.d.cts" norm="/usr/share/antigravity/resources/app/node_modules/quansync/dist/macro.d.cts" dest="/home/linaro/cts-archive-treesheets/macro.d.cts"
orig="/usr/share/antigravity/resources/app/node_modules/quansync/dist/types.d.cts" norm="/usr/share/antigravity/resources/app/node_modules/quansync/dist/types.d.cts" dest="/home/linaro/cts-archive-treesheets/types.d.cts"
orig="/usr/share/antigravity/resources/app/node_modules/@standard-schema/spec/dist/index.d.cts" norm="/usr/share/antigravity/resources/app/node_modules/@standard-schema/spec/dist/index.d.cts" dest="/home/linaro/cts-archive-treesheets/index.d_001.cts"
orig="/usr/share/antigravity/resources/app/node_modules/@standard-schema/utils/dist/index.d.cts" norm="/usr/share/antigravity/resources/app/node_modules/@standard-schema/utils/dist/index.d.cts" dest="/home/linaro/cts-archive-treesheets/index.d_006.cts"
orig="/usr/share/antigravity/resources/app/node_modules/ufo/dist/index.d.cts" norm="/usr/share/antigravity/resources/app/node_modules/ufo/dist/index.d.cts" dest="/home/linaro/cts-archive-treesheets/index.d_004.cts"
LOCATIONS
# Ensure backup root exists (only if we will actually do work).
if (( ! DRY_RUN )); then
run_cmd install -d -m 0755 -- "$BACKUP_ROOT"
else
log "DRY-RUN: would create backup root at $BACKUP_ROOT"
fi
process_one() {
local orig="$1" dest="$2"
if [[ -z "$orig" || -z "$dest" ]]; then
log "WARN: skipping empty mapping (orig='$orig' dest='$dest')"
return 0
fi
if [[ ! -e "$dest" ]]; then
log "WARN: dest missing, skipping: $dest"
return 0
fi
local orig_dir
orig_dir="$(dirname -- "$orig")"
if (( DRY_RUN )); then
if [[ -e "$orig" ]]; then
log "DRY-RUN: would back up existing target to $BACKUP_ROOT$orig"
fi
log "DRY-RUN: would restore $dest -> $orig and then delete $dest"
return 0
fi
# Determine desired permissions/ownership based on existing target if present.
local perm uid gid
if [[ -e "$orig" ]]; then
perm="$(stat -c '%a' -- "$orig")"
uid="$(stat -c '%u' -- "$orig")"
gid="$(stat -c '%g' -- "$orig")"
else
perm="644"
uid="0"
gid="0"
fi
# Backup existing target (if any).
if [[ -e "$orig" ]]; then
local backup_path backup_dir
backup_path="$BACKUP_ROOT$orig"
backup_dir="$(dirname -- "$backup_path")"
run_cmd install -d -m 0755 -- "$backup_dir"
run_cmd cp -a -- "$orig" "$backup_path"
fi
# Ensure target directory exists.
run_cmd install -d -m 0755 -- "$orig_dir"
# Checksums to verify copy.
local src_sum
src_sum="$(sha256sum -- "$dest" | awk '{print $1}')"
local tmp
tmp="$orig_dir/.restore-cts.tmp.$$.$RANDOM"
local cleanup_tmp=0
trap 'if (( cleanup_tmp )) && [[ -e "${tmp:-}" ]]; then rm -f -- "$tmp"; fi' RETURN
run_cmd cp --preserve=mode,timestamps -- "$dest" "$tmp"
cleanup_tmp=1
run_cmd chown "$uid:$gid" -- "$tmp"
run_cmd chmod "$perm" -- "$tmp"
run_cmd mv -f -- "$tmp" "$orig"
cleanup_tmp=0
local dst_sum
dst_sum="$(sha256sum -- "$orig" | awk '{print $1}')"
if [[ "$src_sum" != "$dst_sum" ]]; then
log "ERROR: checksum mismatch after copy (src=$src_sum dst=$dst_sum) for orig=$orig dest=$dest"
return 1
fi
run_cmd rm -f -- "$dest"
log "OK: restored $dest -> $orig"
}
# Iterate lines.
while IFS= read -r line; do
[[ -z "$line" ]] && continue
unset orig norm dest
# The mapping list is trusted (script-local).
eval "$line"
process_one "${orig:-}" "${dest:-}"
done <<< "$LOCATIONS"
log "Done. Backups (if any) are under: $BACKUP_ROOT"
#/usr/local/bin/srch_flat_collect
#!/usr/bin/env bash
# Interactive srch builder + invocation for flat collection under /home/linaro.
set -u
set -o pipefail
if [[ "${EUID:-$(id -u)}" -ne 0 ]]; then
echo "ERROR: must run as root (sudo)." >&2
exit 1
fi
if ! command -v srch >/dev/null 2>&1; then
echo "ERROR: srch not found in PATH." >&2
exit 1
fi
umask 000
ts() { date -Is; }
prompt() {
local label="$1"; shift
local def="${1-}"; shift || true
local ans=""
if [[ -n "$def" ]]; then
read -r -p "$label [$def]: " ans
echo "${ans:-$def}"
else
read -r -p "$label: " ans
echo "$ans"
fi
}
prompt_yesno() {
local q="$1"; shift
local def="${1:-n}"; shift || true
local ans=""
while :; do
read -r -p "$q (y/n) [$def]: " ans
ans="${ans:-$def}"
case "${ans,,}" in
y|yes) echo "y"; return 0 ;;
n|no) echo "n"; return 0 ;;
*) echo "Please answer y or n." >&2 ;;
esac
done
}
sanitize_folder_name() {
local s="$1"
s="${s//\//_}"
s="${s//\\/_}"
s="${s//\0/}"
printf '%s' "$s"
}
DEST_BASE="/home/linaro"
DEFAULT_DEST_NAME="srch_flat_$(date +%Y%m%d_%H%M%S)"
DEST_NAME_RAW="$(prompt "Destination folder name under ${DEST_BASE} (single level; no slashes)" "$DEFAULT_DEST_NAME")"
DEST_NAME="$(sanitize_folder_name "$DEST_NAME_RAW")"
if [[ -z "$DEST_NAME" ]]; then
echo "ERROR: Destination folder name must not be empty." >&2
exit 1
fi
DESTDIR="${DEST_BASE}/${DEST_NAME}"
LOGFILE="${DESTDIR}/operation_log.txt"
LOCKFILE="${DESTDIR}/.rename.lock"
LOCKDIR="${DESTDIR}/.rename.lockdir"
HELPER="/usr/local/bin/srch_move_one"
if [[ ! -x "$HELPER" ]]; then
echo "ERROR: helper not found or not executable: $HELPER" >&2
exit 1
fi
if [[ -e "$DESTDIR" ]]; then
use_existing="$(prompt_yesno "Destination exists. Use it anyway?" "n")"
if [[ "$use_existing" != "y" ]]; then
echo "ERROR: Destination already exists: $DESTDIR" >&2
exit 1
fi
fi
mkdir -p -- "$DESTDIR" || { echo "ERROR: Cannot create destination: $DESTDIR" >&2; exit 1; }
chmod 0777 -- "$DESTDIR" 2>/dev/null || true
if [[ -e "$LOGFILE" ]]; then
mv -f -- "$LOGFILE" "${LOGFILE}.bak.$(date +%Y%m%d%H%M%S)" 2>/dev/null || true
fi
: >"$LOGFILE" || { echo "ERROR: Cannot write log: $LOGFILE" >&2; exit 1; }
chmod 0666 -- "$LOGFILE" 2>/dev/null || true
log() { printf '%s %s\n' "$(ts)" "$*" | tee -a "$LOGFILE" >&2; }
log "BEGIN collection"
log "effective_uid=$(id -u) effective_user=$(id -un 2>/dev/null || true)"
log "destination=$DESTDIR"
log "logfile=$LOGFILE"
# Build srch options.
declare -a SRCH_ROOTS=()
declare -a SRCH_ARGS=()
roots_raw="$(prompt "srch start directories (space-separated)" "/home/linaro /mnt /userdata /usr/share/doc")"
# shellcheck disable=SC2206
SRCH_ROOTS=( $roots_raw )
threads="$(prompt "Threads (-t): number, or '*' for all cores, or blank for default" "")"
[[ -n "$threads" ]] && SRCH_ARGS+=("-t" "$threads")
name_mode="$(prompt "Name matching mode: none | -N (substring) | -n (regex) | -i (regex, case-insensitive)" "-n")"
name_mode="${name_mode// /}"
if [[ "$name_mode" != "none" && -n "$name_mode" ]]; then
name_neg="$(prompt_yesno "Negate the name match? (prefix '!' to the pattern)" "n")"
name_pat="$(prompt "Pattern for ${name_mode} (empty disables name filter)" "")"
if [[ -n "$name_pat" ]]; then
[[ "$name_neg" == "y" ]] && name_pat="!$name_pat"
case "$name_mode" in
-N|-n|-i) SRCH_ARGS+=("$name_mode" "$name_pat") ;;
*) log "WARN: Unrecognized name mode '$name_mode'; skipping name filter." ;;
esac
fi
fi
use_full="$(prompt_yesno "Apply pattern to full path (-a)?" "n")"
[[ "$use_full" == "y" ]] && SRCH_ARGS+=("-a")
add_e="$(prompt_yesno "Add directory exclude regex rules (-e)?" "n")"
if [[ "$add_e" == "y" ]]; then
while :; do
ex="$(prompt "-e <dir-regex> (blank to stop)" "")"
[[ -z "$ex" ]] && break
SRCH_ARGS+=("-e" "$ex")
done
fi
add_E="$(prompt_yesno "Add exact directory excludes (-E)?" "n")"
if [[ "$add_E" == "y" ]]; then
while :; do
ex2="$(prompt "-E <dir-name> (blank to stop)" "")"
[[ -z "$ex2" ]] && break
SRCH_ARGS+=("-E" "$ex2")
done
fi
use_Z="$(prompt_yesno "Exclude .snapshot directories (-Z)?" "y")"
[[ "$use_Z" == "y" ]] && SRCH_ARGS+=("-Z")
depth="$(prompt "Depth control (-m): e.g., '4', '2-5', '2-' (blank for unlimited)" "")"
[[ -n "$depth" ]] && SRCH_ARGS+=("-m" "$depth")
xdev="$(prompt_yesno "Stay on same filesystem for each start point (-x)?" "n")"
[[ "$xdev" == "y" ]] && SRCH_ARGS+=("-x")
ftype="$(prompt "Type filter (for completeness): f/d/l/b/c/p/k/none" "f")"
if [[ "${ftype,,}" != "f" ]]; then
log "WARN: This operation moves regular files only; forcing '-f' regardless of selection ('$ftype')."
fi
SRCH_ARGS+=("-f")
size_spec="$(prompt "Size constraint (-s): e.g., +1k, -10m, +1k:-10m (blank for none)" "")"
[[ -n "$size_spec" ]] && SRCH_ARGS+=("-s" "$size_spec")
older_days="$(prompt "Older than days (-o) (blank for none)" "")"
[[ -n "$older_days" ]] && SRCH_ARGS+=("-o" "$older_days")
older_mins="$(prompt "Older than minutes (-O) (blank for none)" "")"
[[ -n "$older_mins" ]] && SRCH_ARGS+=("-O" "$older_mins")
older_tstamp="$(prompt "Older than timestamp file (-P <file>) (blank for none)" "")"
[[ -n "$older_tstamp" ]] && SRCH_ARGS+=("-P" "$older_tstamp")
younger_days="$(prompt "Younger than days (-y) (blank for none)" "")"
[[ -n "$younger_days" ]] && SRCH_ARGS+=("-y" "$younger_days")
younger_mins="$(prompt "Younger than minutes (-Y) (blank for none)" "")"
[[ -n "$younger_mins" ]] && SRCH_ARGS+=("-Y" "$younger_mins")
younger_tstamp="$(prompt "Younger than timestamp file (-W <file>) (blank for none)" "")"
[[ -n "$younger_tstamp" ]] && SRCH_ARGS+=("-W" "$younger_tstamp")
inc_user="$(prompt "Owned by user (-u) (blank for none)" "")"
[[ -n "$inc_user" ]] && SRCH_ARGS+=("-u" "$inc_user")
exc_user="$(prompt "Not owned by user (-U) (blank for none)" "")"
[[ -n "$exc_user" ]] && SRCH_ARGS+=("-U" "$exc_user")
inc_group="$(prompt "Owned by group (-g) (blank for none)" "")"
[[ -n "$inc_group" ]] && SRCH_ARGS+=("-g" "$inc_group")
exc_group="$(prompt "Not owned by group (-G) (blank for none)" "")"
[[ -n "$exc_group" ]] && SRCH_ARGS+=("-G" "$exc_group")
inline_dirs="$(prompt "Inline subdir threshold (-I <count>) (blank for none)" "")"
[[ -n "$inline_dirs" ]] && SRCH_ARGS+=("-I" "$inline_dirs")
use_q="$(prompt_yesno "Use FIFO queue (-q)?" "n")"; [[ "$use_q" == "y" ]] && SRCH_ARGS+=("-q")
use_Q="$(prompt_yesno "Queue sorted by inode (-Q)?" "n")"; [[ "$use_Q" == "y" ]] && SRCH_ARGS+=("-Q")
use_X="$(prompt_yesno "Enable extremely-large-dir optimization (-X)?" "n")"; [[ "$use_X" == "y" ]] && SRCH_ARGS+=("-X")
prog_v="$(prompt "Progress interval (-v <N>) (blank for none)" "")"
[[ -n "$prog_v" ]] && SRCH_ARGS+=("-v" "$prog_v")
use_S="$(prompt_yesno "Print summary stats (-S)?" "y")"; [[ "$use_S" == "y" ]] && SRCH_ARGS+=("-S")
use_T="$(prompt_yesno "Print elapsed time (-T)?" "y")"; [[ "$use_T" == "y" ]] && SRCH_ARGS+=("-T")
use_C="$(prompt_yesno "Suppress missing/disappearing entry errors (-C)?" "y")"; [[ "$use_C" == "y" ]] && SRCH_ARGS+=("-C")
dryrun="$(prompt_yesno "Dry-run (log only; do not copy/delete)?" "y")"
extra_raw="$(prompt "Additional srch arguments (space-separated; blank for none)" "")"
if [[ -n "$extra_raw" ]]; then
# shellcheck disable=SC2206
extra_arr=( $extra_raw )
for a in "${extra_arr[@]}"; do SRCH_ARGS+=("$a"); done
fi
log "srch_roots=${SRCH_ROOTS[*]}"
log "srch_args=${SRCH_ARGS[*]}"
log "dryrun=$dryrun"
confirm="$(prompt_yesno "Proceed now? (Matches will be processed by srch -r)" "n")"
if [[ "$confirm" != "y" ]]; then
log "ABORTED by user"
exit 0
fi
export DESTDIR LOGFILE LOCKFILE LOCKDIR
export DRYRUN="$dryrun"
set +e
srch "${SRCH_ARGS[@]}" -r "$HELPER" "${SRCH_ROOTS[@]}" 2>&1 | tee -a "$LOGFILE"
rc=${PIPESTATUS[0]}
set -e 2>/dev/null || true
log "srch_exit_code=$rc"
chown -R linaro:linaro -- "$DESTDIR" 2>/dev/null || true
chmod -R a+rwX -- "$DESTDIR" 2>/dev/null || true
moved_count="$(grep -c " MOVED " "$LOGFILE" 2>/dev/null || true)"
skip_count="$(grep -c " SKIP " "$LOGFILE" 2>/dev/null || true)"
dry_count="$(grep -c " DRYRUN " "$LOGFILE" 2>/dev/null || true)"
err_count="$(grep -c " ERROR " "$LOGFILE" 2>/dev/null || true)"
log "SUMMARY moved=$moved_count skipped=$skip_count dryrun=$dry_count errors=$err_count"
log "DONE destination=$DESTDIR"
exit 0
#/usr/local/bin/srch_move_one
#!/usr/bin/env bash
# srch per-file mover: flat destination with collision-safe renames and audit log.
set -u
set -o pipefail
DESTDIR="${DESTDIR:?}"
LOGFILE="${LOGFILE:?}"
LOCKFILE="${LOCKFILE:?}"
LOCKDIR="${LOCKDIR:-}" # optional mkdir-based lock fallback
DRYRUN="${DRYRUN:-n}"
_ts() { date -Is; }
_log() { printf '%s %s\n' "$(_ts)" "$*" >>"$LOGFILE"; }
_lock_acquire() {
if command -v flock >/dev/null 2>&1; then
exec 9>>"$LOCKFILE"
flock -x 9
return 0
fi
if [[ -z "$LOCKDIR" ]]; then
return 0
fi
while ! mkdir -- "$LOCKDIR" 2>/dev/null; do
sleep 0.05
done
}
_lock_release() {
if command -v flock >/dev/null 2>&1; then
return 0
fi
if [[ -n "$LOCKDIR" ]]; then
rmdir -- "$LOCKDIR" 2>/dev/null || true
fi
}
orig="${1-}"
if [[ -z "$orig" ]]; then
_log "ERROR event=invoke_missing_path"
exit 0
fi
norm="$orig"
if [[ "$norm" == ./* ]]; then
norm="$(pwd -P)/${norm#./}"
elif [[ "$norm" != /* ]]; then
norm="$(pwd -P)/$norm"
fi
if [[ ! -f "$norm" ]]; then
_log "SKIP event=not_regular_file orig=\"$orig\" norm=\"$norm\""
exit 0
fi
base="$(basename -- "$norm")"
stem="$base"
ext=""
if [[ "$base" == .* ]]; then
if [[ "$base" == .*.* ]]; then
ext=".${base##*.}"
stem="${base%$ext}"
fi
else
if [[ "$base" == *.* ]]; then
ext=".${base##*.}"
stem="${base%$ext}"
fi
fi
_lock_acquire
cand="$DESTDIR/$base"
if [[ -e "$cand" ]]; then
i=1
while :; do
suf="_$(printf '%03d' "$i")"
cand="$DESTDIR/${stem}${suf}${ext}"
[[ -e "$cand" ]] || break
i=$((i + 1))
done
fi
if ! : >"$cand" 2>/dev/null; then
_log "ERROR event=reserve_failed orig=\"$orig\" norm=\"$norm\" dest=\"$cand\""
_lock_release
exit 0
fi
_lock_release
if [[ "$DRYRUN" == "y" ]]; then
rm -f -- "$cand" 2>/dev/null || true
_log "DRYRUN event=would_move orig=\"$orig\" norm=\"$norm\" dest=\"$cand\""
exit 0
fi
tmp="$DESTDIR/.tmp.$(basename -- "$cand").$$.$RANDOM"
if cp -a -- "$norm" "$tmp" 2>/dev/null; then
if mv -f -- "$tmp" "$cand" 2>/dev/null; then
if rm -f -- "$norm" 2>/dev/null; then
chmod 0666 -- "$cand" 2>/dev/null || true
chown linaro:linaro -- "$cand" 2>/dev/null || true
if [[ "$cand" == "$DESTDIR/$base" ]]; then
_log "MOVED event=direct orig=\"$orig\" norm=\"$norm\" dest=\"$cand\""
else
_log "MOVED event=collision_renamed orig=\"$orig\" norm=\"$norm\" dest=\"$cand\""
fi
else
_log "ERROR event=delete_failed orig=\"$orig\" norm=\"$norm\" dest=\"$cand\""
fi
else
rm -f -- "$tmp" 2>/dev/null || true
rm -f -- "$cand" 2>/dev/null || true
_log "ERROR event=commit_failed orig=\"$orig\" norm=\"$norm\" dest=\"$cand\""
fi
else
rm -f -- "$tmp" 2>/dev/null || true
rm -f -- "$cand" 2>/dev/null || true
_log "ERROR event=copy_failed orig=\"$orig\" norm=\"$norm\" dest=\"$cand\""
fi