-
Notifications
You must be signed in to change notification settings - Fork 0
Fix three safety bugs: trash retention, silent flag typos, blunt chown -R #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
dfb0ed9
7816d56
9ada09b
7280b61
b2d4a2c
e71c020
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -358,23 +358,145 @@ fda_hint() { | |
|
|
||
| # ============================================================================ | ||
| # Trash | ||
| # | ||
| # Retention (see clean.sh) must be based on WHEN AN ITEM WAS TRASHED, not | ||
| # its original file mtime — otherwise trashing an old file and cleaning up | ||
| # an hour later permanently deletes it despite the advertised grace period. | ||
| # On Linux, gio already records this for us (DeletionDate in the freedesktop | ||
| # .trashinfo sidecar). On macOS, ~/.Trash has no such metadata, so we keep | ||
| # our own sidecar under ~/.Trash/.vole-trash-meta. | ||
| # ============================================================================ | ||
|
|
||
| # Move a path to the user's trash. Returns 1 if no trash mechanism exists. | ||
| # Name of Vole's own bookkeeping dir inside ~/.Trash; never a real trashed item. | ||
| VOLE_TRASH_META_DIR_NAME=".vole-trash-meta" | ||
|
|
||
| # Move a path to the user's trash, recording when it was trashed so | ||
| # retention can key off deletion time. Returns 1 if no trash mechanism exists. | ||
| move_to_trash() { | ||
| local path="$1" | ||
| if is_macos; then | ||
| local dest="$HOME/.Trash/$(basename "$path")" | ||
| # Avoid clobbering an existing trash entry with the same name | ||
| [[ -e "$dest" ]] && dest="$dest.$(get_epoch_seconds)" | ||
| mv -- "$path" "$dest" 2> /dev/null | ||
| if mv -- "$path" "$dest" 2> /dev/null; then | ||
| _vole_record_trash_time "$dest" | ||
| return 0 | ||
| fi | ||
| return 1 | ||
| elif command -v gio > /dev/null 2>&1; then | ||
| # gio writes a freedesktop .trashinfo sidecar with a DeletionDate — | ||
| # see trash_deleted_epoch below, which reads it back. | ||
| gio trash "$path" 2> /dev/null | ||
| else | ||
| return 1 | ||
| fi | ||
| } | ||
|
|
||
| # Record the deletion time for a macOS Trash item. | ||
| _vole_trash_identity() { | ||
| local trashed_path="$1" | ||
| if is_macos; then | ||
| stat -f '%d:%i' "$trashed_path" 2> /dev/null || true | ||
| else | ||
| stat -c '%d:%i' -- "$trashed_path" 2> /dev/null || true | ||
| fi | ||
| } | ||
|
|
||
| _vole_record_trash_time() { | ||
| local trashed_path="$1" | ||
| local meta_dir="$HOME/.Trash/$VOLE_TRASH_META_DIR_NAME" | ||
| mkdir -p "$meta_dir" 2> /dev/null || return 0 | ||
| local epoch identity | ||
| epoch="$(get_epoch_seconds)" | ||
| identity="$(_vole_trash_identity "$trashed_path")" | ||
| [[ -n "$identity" ]] || return 0 | ||
| { | ||
| echo "epoch=$epoch" | ||
| echo "identity=$identity" | ||
| } > "$meta_dir/$(basename "$trashed_path")" 2> /dev/null || true | ||
| } | ||
|
|
||
| # Look up when a trashed item was actually deleted, in epoch seconds. | ||
| # Prints nothing if unknown — callers MUST treat that conservatively (not | ||
| # yet eligible for permanent deletion) rather than falling back to mtime. | ||
| trash_deleted_epoch() { | ||
| local trashed_path="$1" | ||
| local name | ||
| name="$(basename "$trashed_path")" | ||
| if is_macos; then | ||
| local meta_file="$HOME/.Trash/$VOLE_TRASH_META_DIR_NAME/$name" | ||
| [[ -f "$meta_file" ]] || return 0 | ||
| local val epoch identity current_identity | ||
| val="$(cat "$meta_file" 2> /dev/null || true)" | ||
| # Older Vole metadata was keyed only by basename. Treat it as unknown | ||
| # rather than risking a stale timestamp for a different Trash item. | ||
| [[ "$val" =~ ^[0-9]+$ ]] && return 0 | ||
| epoch="$(printf '%s\n' "$val" | sed -n 's/^epoch=//p' | head -1)" | ||
| identity="$(printf '%s\n' "$val" | sed -n 's/^identity=//p' | head -1)" | ||
| [[ "$epoch" =~ ^[0-9]+$ && -n "$identity" ]] || return 0 | ||
| current_identity="$(_vole_trash_identity "$trashed_path")" | ||
| [[ -n "$current_identity" && "$current_identity" == "$identity" ]] || return 0 | ||
| echo "$epoch" | ||
| else | ||
| local trashinfo="$HOME/.local/share/Trash/info/$name.trashinfo" | ||
| [[ -f "$trashinfo" ]] || return 0 | ||
| local deletion_date epoch | ||
| deletion_date=$(sed -n 's/^DeletionDate=//p' "$trashinfo" 2> /dev/null | head -1) | ||
| [[ -n "$deletion_date" ]] || return 0 | ||
| epoch=$(date -d "$deletion_date" +%s 2> /dev/null || echo "") | ||
| [[ "$epoch" =~ ^[0-9]+$ ]] && echo "$epoch" | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When a Linux Useful? React with 👍 / 👎. |
||
| return 0 | ||
| fi | ||
| } | ||
|
|
||
| # Backfill a deletion-time record for a trash item with no known deletion | ||
| # time (trashed before this tracking existed, or dropped in by another | ||
| # tool). Stamped as "discovered now" so it only becomes eligible for | ||
| # permanent deletion after a full grace period FROM THIS POINT ON — never | ||
| # on the run that first notices it. | ||
| trash_backfill_deleted_now() { | ||
| local trashed_path="$1" | ||
| local name | ||
| name="$(basename "$trashed_path")" | ||
| if is_macos; then | ||
| _vole_record_trash_time "$trashed_path" | ||
| else | ||
| local info_dir="$HOME/.local/share/Trash/info" iso info_file tmp_file | ||
| mkdir -p "$info_dir" 2> /dev/null || return 0 | ||
| iso=$(date '+%Y-%m-%dT%H:%M:%S') | ||
| info_file="$info_dir/$name.trashinfo" | ||
| if [[ -f "$info_file" ]]; then | ||
| tmp_file="$info_file.vole.$$" | ||
| if awk -v iso="$iso" ' | ||
| BEGIN { seen = 0 } | ||
| /^DeletionDate=/ { | ||
| if (!seen) { | ||
| print "DeletionDate=" iso | ||
| seen = 1 | ||
| } | ||
| next | ||
| } | ||
| { print } | ||
| END { | ||
| if (!seen) { | ||
| print "DeletionDate=" iso | ||
| } | ||
| } | ||
| ' "$info_file" > "$tmp_file" 2> /dev/null; then | ||
| mv -- "$tmp_file" "$info_file" 2> /dev/null || rm -f -- "$tmp_file" | ||
| else | ||
| rm -f -- "$tmp_file" | ||
| fi | ||
| else | ||
| { | ||
| echo "[Trash Info]" | ||
| echo "Path=$trashed_path" | ||
| echo "DeletionDate=$iso" | ||
| } > "$info_file" 2> /dev/null || true | ||
| fi | ||
| fi | ||
| } | ||
|
|
||
| # ============================================================================ | ||
| # Step Runner (for update/optimize style tasks) | ||
| # ============================================================================ | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On macOS, this lookup reuses any sidecar with the same basename, even if the original trash item was restored or deleted outside Vole and the sidecar remained. If a new item with that basename is later put in Trash by Finder or another tool,
cleancan apply the stale old epoch and permanently delete the new item on its first run, bypassing the intended grace period; include a unique file identity or clear orphaned sidecars before trusting them.Useful? React with 👍 / 👎.