Commit Graph

9129 Commits

Author SHA1 Message Date
Christian Ebner
acfd017b76 sync: pull: refuse to overwrite pre-existing encrypted snapshot
If the source snapshot is not encrypted, the target snapshot however
is pre-existing and there is no decryption key which would detect the
mismatch, the unencrypted source will overwrite the encrypted target
due to resync because of mismatching raw manifests.

Likewise, regular syncs from a different source whose snapshot for
the same backup-time has different content used to silently overwrite
the local target, since byte-wise manifest comparison detects the
mismatch and triggers a resync.

Protect against this by extending the key matching checks, refusing
to overwrite any locally encrypted by a not decrypted source as well
as comparing content by calculating the signature using a dummy key
if both, source and pre-existing target are not encrypted.

Reported-by: Daniel Kral <d.kral@proxmox.com>
Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
Link: https://lore.proxmox.com/20260429143740.886870-1-c.ebner@proxmox.com
 [TL: reword bail messages, expand dummy-key comment, fix typos]
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2026-04-29 18:23:42 +02:00
Fiona Ebner
c02a2deeed removable device attach service: improve description
The previous wording might suggest that mount is attempted for all
devices or that all devices belong to a datastore.

Reported-by: Stefan Hanreich <s.hanreich@proxmox.com>
Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
Link: https://lore.proxmox.com/20260429140155.300518-1-f.ebner@proxmox.com
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2026-04-29 18:23:42 +02:00
Arthur Bied-Charreton
e685a30c3e ui: encryption keys: show error text instead of empty popup in API alerts
The errors returned by the encryption-keys and tape-encryption-keys
endpoints are not plain strings, so the alerts displayed on API2 errors
like failed permission checks end up being empty and therefore not very
helpful.

Extract the error messages to display them correctly in alerts.

Signed-off-by: Arthur Bied-Charreton <a.bied-charreton@proxmox.com>
Link: https://lore.proxmox.com/20260429101139.163532-1-a.bied-charreton@proxmox.com
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2026-04-29 18:23:42 +02:00
Thomas Lamprecht
f92617aded Merge remote-tracking branch 'origin/master'
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2026-04-29 12:25:04 +02:00
Fabian Grünbichler
0dc8b9af2e run cargo fmt
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2026-04-29 09:50:52 +02:00
Thomas Lamprecht
4f2b051657 d/copyright: update years
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2026-04-28 23:56:01 +02:00
Thomas Lamprecht
035c449897 bump version to 4.2.0-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2026-04-28 23:56:01 +02:00
Thomas Lamprecht
198e9b84a0 d/copyright: transform to machine-readable format
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2026-04-28 23:55:59 +02:00
Thomas Lamprecht
3d165b049e ui: datastore list summary: guard against missing history field
Avoids an uncaught TypeError on datastores whose status payload omits
the history array, e.g. fresh stores with no RRD samples yet.

Reported-by: Stefan Hanreich <s.hanreich@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2026-04-28 23:55:59 +02:00
Thomas Lamprecht
8f14715a1f bump version to 4.1.13-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2026-04-28 20:43:54 +02:00
Christian Ebner
ca5e9e46bc sync: rename change-detection-fingerprint to sync-source-signature
Introduced in 87a819311 ("datastore: manifest: add helper for change
detection fingerprint") the name was found to be confusing due to the
potential mental clash with the totally unrelated client parameter
named `change-detection-mode`, especially in log output.

The new field name `sync-source-signature` better reflects that this
is the signature of the sync source snapshot stored on the target.
The previous field is kept for backwards compatibility with already
synced snapshots storing the field, the methods, comments and log
messages all updated accordingly.

Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
Link: https://lore.proxmox.com/20260428143642.1012584-1-c.ebner@proxmox.com
 [TL: resolved merge conflict with context changes, squashed in
  s/sync-souruce-fingerprint/sync-source-signature/ typo fix in
  pull_snapshot, renamed setter's parameter from `fingerprint` to
  `signature` for consistency, and fixed typos in commit message]
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2026-04-28 20:10:03 +02:00
Fabian Grünbichler
869aa8a1dc pull sync: decrypt: check both change-detection-fingerprints
The change-detection-fingerprint (CDF) is an HMAC stored in a
manifest's unprotected section, used to skip resync of unchanged
snapshots. Two flows write it: encrypt-push stores HMAC over the
source's plain manifest, while decrypt-pull copies the source
manifest's signature into the local target's CDF. Plain signed or
encrypted snapshots carry only their manifest signature, no CDF.

In the sequence

 1. signed backup (A)
 2. encrypted push (B)
 3. decrypting pull (C)

B and C carry a CDF but A does not, and on a subsequent decrypt-pull
from B the source-side check fails: B's CDF was hashed over A's plain
manifest with crypt-mode 'sign-only' on each index/archive, while
target.signature recomputed against C's plain manifest hashes 'none'
instead (decrypt-pull strips the sign-only marker). Same key,
different canonical input, different HMACs - even though the snapshot
content has not changed.

Check both fingerprints and accept on either match: source-side HMAC
reproducibility, or the source's current signature against the
target's stored CDF (set by the previous decrypt-pull). Bail only when
neither identity proves the snapshot unchanged.

Reported-by: Christian Ebner <c.ebner@proxmox.com>
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
 [TL: expand commit message a bit]
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2026-04-28 19:46:40 +02:00
Thomas Lamprecht
4168112ad1 move group: consistently use "mismatch"in log message and doc-comment
Avoid mixed usage of "overlap" and "mismatch" terminology,
consistently use "mismatch", as basically all of
check_merge_invariants already did.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2026-04-28 16:23:43 +02:00
Fabian Grünbichler
3206d221ef move namespace: use group(s)/namespace(s) consistently
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
Link: https://lore.proxmox.com/20260427120234.634681-5-f.gruenbichler@proxmox.com
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2026-04-28 16:21:43 +02:00
Fabian Grünbichler
a626147645 move namespace: move context log line before check
to make it clear when the context switches to a particular group, since not all
warnings will include its name.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
Link: https://lore.proxmox.com/20260427120234.634681-4-f.gruenbichler@proxmox.com
 [TL: move unrelated s/overlap/mismatch/ to previous commit]
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2026-04-28 16:21:43 +02:00
Fabian Grünbichler
4de09c1b73 move group: split overly long error message and s/time overlap/time mismatch/
this is now logged as

2026-04-27T13:49:49+02:00: oldest source snapshot: 2023-03-15T08:00:13Z
2026-04-27T13:49:49+02:00: conflicting target snapshot: 2026-03-24T10:27:24Z
2026-04-27T13:49:49+02:00: TASK ERROR: cannot merge group 'ct/999' from 'foo' into 'bar': snapshot time mismatch

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
Link: https://lore.proxmox.com/20260427120234.634681-3-f.gruenbichler@proxmox.com
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2026-04-28 16:21:37 +02:00
Fabian Grünbichler
a6f5567f08 move group: use human-readable snapshot timestamp in check
before:

2026-04-27T13:39:44+02:00: TASK ERROR: cannot merge group 'ct/999' from 'foo' into 'bar': snapshot time overlap (oldest source: 1678867213, conflicting target: 1774348044)

after:

2026-04-27T13:42:03+02:00: TASK ERROR: cannot merge group 'ct/999' from 'foo' into 'bar': snapshot time overlap (oldest source: 2023-03-15T08:00:13Z, conflicting target: 2026-03-24T10:27:24Z)

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
Link: https://lore.proxmox.com/20260427120234.634681-2-f.gruenbichler@proxmox.com
 [TL: drop stray has_manifest fn addition]
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2026-04-28 16:21:05 +02:00
Dominik Csapak
5e24a5ebe2 ui: datastore content: always reload after namespace move
After a partial namespace move it could happen that some groups that
were in the current were actually moved, so reload the view and
namespace selector in any case.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
Link: https://lore.proxmox.com/20260428131722.3386349-1-d.csapak@proxmox.com
2026-04-28 16:05:18 +02:00
Christian Ebner
74dd279fd6 ui: datastore contents: Increase actions column width
Since the introduction of the namespace/group move, the action column
got an additional icon. The current width is now to narrow, therefore
potentially not showing important icons like the single file browser.

Increase the width so the icon is visible again without manual
intervention.

Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
Reviewed-by: Dominik Csapak <d.csapak@proxmox.com>
Tested-by: Dominik Csapak <d.csapak@proxmox.com>
Link: https://lore.proxmox.com/20260427102721.380440-1-c.ebner@proxmox.com
2026-04-28 15:58:53 +02:00
Thomas Lamprecht
80ebbc7245 datastore: didx: pass ctime through from_le when reading header
The writer encodes ctime via 'i64::to_le()'; DynamicIndexReader was
copying the field straight out without the matching 'i64::from_le()',
unlike FixedIndexReader. On all platforms PBS targets the round-trip
is a no-op, so this is not a regression in practice, just an asymmetry
that would flip the byte order on a hypothetical big-endian build.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2026-04-28 15:55:46 +02:00
Robert Obkircher
d80323360f bin: debug: produce deterministic output when inspecting index files
Sort the output to make it deterministic and easier to compare.

Signed-off-by: Robert Obkircher <r.obkircher@proxmox.com>
Link: https://lore.proxmox.com/20260428122544.19899-4-r.obkircher@proxmox.com
2026-04-28 15:46:19 +02:00
Robert Obkircher
8563716bde datastore: remove unnecessary pointer dereference in didx reader
The inner method already returns the correct reference type.

Signed-off-by: Robert Obkircher <r.obkircher@proxmox.com>
Link: https://lore.proxmox.com/20260428122544.19899-3-r.obkircher@proxmox.com
2026-04-28 15:46:19 +02:00
Robert Obkircher
edc4bc6684 datastore: read ctime from didx header instead of using current time
It looks like this was accidentally changed during a refactor in 2020.

The ctime field is public, so it is not obvious whether anyone depends
on the broken behavior. The index_ctime accessor only seems to be used
by inspect_file in proxmox_backup_debug.

Fixes: 6a7be83efe ("avoid chrono dependency, depend on proxmox 0.3.8")
Signed-off-by: Robert Obkircher <r.obkircher@proxmox.com>
Link: https://lore.proxmox.com/20260428122544.19899-2-r.obkircher@proxmox.com
2026-04-28 15:46:19 +02:00
Fabian Grünbichler
6a6e2d5829 group: lock: inline error instead of using context
all the other errors here are constructed like this, and call sites do not log
the context yet, so the error is incomplete otherwise..

this changes the error for concurrent group/snapshot creation from:

  Error: while creating locked backup group "/store/pbs/ns/namespace/host/group"'

to:

  Error: unable to acquire backup group lock "/run/proxmox-backup/locks/store/namespace/host-group" while creating locked backup group "/store/pbs/ns/namespace/host/group"

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
Link: https://lore.proxmox.com/20260428110259.766084-2-f.gruenbichler@proxmox.com
2026-04-28 15:44:56 +02:00
Fabian Grünbichler
ce767a864d group: lock: use group path instead of debug for error message
this changes the error from:

  Error: while creating locked backup group 'BackupGroup { store: "store", ns: BackupNamespace { inner: ["namespace"], len: 6 }, group: BackupGroup { ty: Host, id: "group" } }'

to

  Error: while creating locked backup group "/store/pbs/ns/namespace/host/group"'

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
Link: https://lore.proxmox.com/20260428110259.766084-1-f.gruenbichler@proxmox.com
2026-04-28 15:44:56 +02:00
Thomas Lamprecht
2193dd64c3 pbs-datastore: clarify change-detection-fingerprint dual semantics
The interpretation of change-detection-fingerprint depends on which
sync flow set it: encrypt-push records an HMAC of the source's plain
manifest under the encryption key, while decrypt-pull from a regular
encrypted backup records the source's encrypted-manifest signature
verbatim. The two are not interchangeable, but the existing
doc-comment did not say so, and the recently-added decrypt-pull
fallback called the value loaded from the local target's CDF
'source_signature', even though no source-side data is involved at
that point.

Spell both interpretations out on the setter and rename the local
in the fallback to 'stored_source_fp', so the comparison reads as
'current == stored_source_fp' against the current source signature.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2026-04-28 15:44:17 +02:00
Dominik Csapak
30e44fb1ff ui: datastore content: fix namespace change after reload
A double click on a namespace change it by first setting the namespace
value to the namespace selector and then triggering a reload.

When a new namespace is added to the view with a manual click on
'reload', this new namespace does not yet exist in the namespace
selectors store. So a double click can't properly set this as a value,
and thus does not change the namespace.

To fix this, always reload the namespace selector at the beginning of a
reload, so they should be in sync.

To keep the callback we use for the namespace selector, move it to the
reload function and pass it through to the load there.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
Link: https://lore.proxmox.com/20260428110520.2343917-1-d.csapak@proxmox.com
2026-04-28 15:13:19 +02:00
Fabian Grünbichler
40186dc465 pull: decrypt: set and check change-detection-fingerprint
when decrypt-pulling a snapshot that was not created by encrypt-pushing, but by
a regular encrypted backup, resyncing requires matching the local plain text
manifest and the remote encrypted one.

in addition to storing a signature of the plain manifest when encrypt-pushing,
store the already existing signature of the encrypted source manifest when
decrypt-pulling, but only use it for matching if the former does not exist.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
Reviewed-by: Christian Ebner <c.ebner@proxmox.com>
Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
Tested-by: Dominik Csapak <d.csapak@proxmox.com>
Link: https://lore.proxmox.com/20260428094437.596604-1-f.gruenbichler@proxmox.com
2026-04-28 15:06:38 +02:00
Thomas Lamprecht
16eeb9d9d9 clippy fix: api: collapse if into the outer match
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2026-04-28 15:03:33 +02:00
Thomas Lamprecht
0e0fe183a8 clipyp fix: avoid explicit into_iter
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2026-04-28 15:02:53 +02:00
Thomas Lamprecht
33da703c8e client: pxar: regression-test chunk identity end_offset population
Builds a synthetic three-chunk dynamic index with a digest collision
at distinct positions and asserts lookup_dynamic_entries populates
end_offset from dynamic_entry.end(). A regression that swaps in a
range-relative value (size, prev_end) is caught here, before it would
manifest as backwards PXAR_PAYLOAD_REF offsets downstream.

It's narrow on purpose, well more due to the limitations of current
test harness, anyhow: pins the identity the chunk-reuse predicate
relies on, but does not exercise the predicate's call sites, the
debug-mode underflow at the threshold-padding subtraction, or
end-to-end PXAR_PAYLOAD_REF monotonicity. A behavioral test on the
full flush path is followup work.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2026-04-28 14:46:35 +02:00
Thomas Lamprecht
98a48897ef client: pxar: tighten chunk identity check at both predicate sites
Chris found a more elegant and correct solution for my fix in
ebd0e469e ("client: pxar: inject held chunk on cache-range
discontinuity").

flush_cached_reusing_if_below_threshold checked chunk identity by
digest only at two sites: the padding-ratio threshold check and the
post-flush absorb branch. On a dedup collision (matching digest at a
different end_offset in the previous archive) the absorb branch writes
a backwards PXAR_PAYLOAD_REF that pxar 1.0.1 rejects, and the
threshold check subtracts an unrelated chunk's bytes from the new
range's padding, which underflows 'padding -= used' in debug.

Compare (digest, end_offset) at both sites via a small helper on
ReusableDynamicEntry. Replaces the reverted ebd0e469e ("client: pxar:
inject held chunk on cache-range discontinuity"), which fixed the
absorb-side symptom by forcing injection on every cache-range hole but
over-fired on innocent holes (e.g. a vanished tiny file).

We can further improve the interface here through factoring out size,
digest and index in an e.g. ReusableDynamicEntryIdentity struct that
derives Eq in the future, but lets keep the fix here for now minimal.

Suggested-by: Christian Ebner <c.ebner@proxmox.com>
Co-developed-by: Christian Ebner <c.ebner@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2026-04-28 14:46:35 +02:00
Thomas Lamprecht
0930ddafa1 Revert "client: pxar: inject held chunk on cache-range discontinuity"
This reverts commit ebd0e469e2, Chris
came up with a more elegant, robust and efficient solution.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2026-04-28 14:46:35 +02:00
Wolfgang Bumiller
bc755ddec1 client: drop intermediate .as_path() call
It's unnecessary as PathBuf derefs to Path.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2026-04-28 14:39:07 +02:00
Wolfgang Bumiller
8984d92ec8 reformat overlong warning, add punctuation
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2026-04-28 14:38:46 +02:00
Manuel Federanko
37abd50ca7 fix #5247: relative paths in exclude patterns.
Patterns which start with ./ or for that matter contain /../ or similar
constructs will never match, since they get compared to sanitized paths
from directory traversal, sanitize those patterns too. Implement this
for both .pxarexclude files and the --exclude command line option. Log
a warning if a path was sanitized.

Signed-off-by: Manuel Federanko <m.federanko@proxmox.com>
Fixes: https://bugzilla.proxmox.com/show_bug.cgi?id=5247
Link: https://lore.proxmox.com/20260428080634.79054-1-m.federanko@proxmox.com
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2026-04-28 14:37:17 +02:00
Thomas Lamprecht
9dedc23bad bump version to 4.1.11-2
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2026-04-28 03:51:10 +02:00
Thomas Lamprecht
da1c6f0031 pull: signal filter-skipped snapshots like vanished ones
pull_snapshot returned Ok(Some(default)) on a verified-only or
encrypted-only filter rejection, making pull_snapshot_from log a
misleading 'sync done' for a snapshot that was not actually synced.
This is a theoretical edge case since pull_group's pre-filter
normally catches such snapshots at list time, but the encrypted-only
branch has also been silent since 793c4368f removed its info!().
Reuse the Ok(None) vanished-source path so target-dir cleanup and
'sync done' suppression fall out for free, and emit an explicit
'skipped, did not match filter' log line.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2026-04-28 03:26:24 +02:00
Thomas Lamprecht
5560baa59b sync: handle vanished snapshots on local source reader
A snapshot can be pruned or forgotten between pull_group's up-front
list_backup_snapshots and the per-snapshot load. RemoteSourceReader
already maps the resulting 404 to Ok(None) so pull_snapshot can log
'skipped because vanished' and continue with the next snapshot;
LocalSourceReader instead propagated ENOENT as a regular IO error,
aborting the entire group with a confusing 'No such file or directory'
on what is a perfectly normal race against a concurrent prune.

Map NotFound to Ok(None) so both source kinds behave uniformly.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2026-04-28 03:26:24 +02:00
Thomas Lamprecht
1f50119ad4 pull: clean up leftover state on vanished snapshot or missing archive
Stale .tmp files and ghost snapshot directories piled up on the target
after prune-during-sync races, and the post-sync 'sync done' log line
fired for snapshots that never actually synced. None of this caused
corruption, but it accumulated cruft on the target datastore and
muddied the task log for operators investigating sync behavior.

Encode 'source vanished' as Ok(None) from pull_snapshot so
pull_snapshot_from can fold it into the existing error-cleanup path,
and unlink the orphaned <archive>.tmp before bailing in
pull_single_archive. Unlink the <manifest>.tmp before the log call so
a collapsing log channel cannot leave it behind for !is_new re-syncs.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2026-04-28 03:26:24 +02:00
Fabian Grünbichler
7ed9c6c2e8 pull: move logging of disappeared snapshots
to the call site where we have the log line sender available

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
Link: https://lore.proxmox.com/20260427084952.303245-4-f.gruenbichler@proxmox.com
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2026-04-28 03:26:24 +02:00
Fabian Grünbichler
d614091307 pull: do not allow archive fetching to return no archive
allowing Ok(None) here only makes sense for manifests (where we can treat this
as a disappeared snapshot).

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
Link: https://lore.proxmox.com/20260427084952.303245-3-f.gruenbichler@proxmox.com
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2026-04-28 03:26:24 +02:00
Fabian Grünbichler
57b8b09cc6 sync: return File in load_file_into
since only manifests are valid blobs, indices are not. this also makes a
CRC error manifest itself as error, instead of masking as "snapshot
disappeared".

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
Link: https://lore.proxmox.com/20260427084952.303245-2-f.gruenbichler@proxmox.com
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2026-04-28 03:26:24 +02:00
Dominik Csapak
e8552dae31 ui: sync/verify view: show correct duration in task log window
By passing the endtime from the grid to the task log window.

If that's not done, the task log window uses the current time to
calculate the duration, which is always wrong for already finished
tasks.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
Link: https://lore.proxmox.com/20260427120543.2692503-1-d.csapak@proxmox.com
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2026-04-28 03:26:24 +02:00
Christian Ebner
456a43380c client: show full error context in single line on upload errors
Since commit 07cb3e7f7 ("client: pxar: optionally split metadata and
payload streams") there are possibly multiple upload streams (data
and metadata archive), errors for each propagated accordingly.
Since this reformatted the error without explicitly including the
context, the resulting error messages are lacking crucial information.

Reformat the error with full context.

Fixes: 07cb3e7f7 ("client: pxar: optionally split metadata and payload streams")
Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
Link: https://lore.proxmox.com/20260427125955.653377-1-c.ebner@proxmox.com
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2026-04-28 03:26:24 +02:00
Thomas Lamprecht
8b9eada28b client: pxar: re-encode files with non-monotonic previous offsets
The strict payload-offset check added in pxar 1.0.1 rejects any
payload_offset not strictly greater than the encoder's previously
recorded one. Older client versions could write metadata archives
with duplicate or non-monotonic PXAR_PAYLOAD_REF offsets via the
producer-side bug fixed in the previous commit. Without the encode-
time guard such an archive looked fine on disk, but every later
metadata-mode incremental backup now aborts on the strict check until
the chain is cycled out via --change-detection-mode=data.

Track the highest previously-recorded payload offset globally on the
Archiver and treat any prev-archive entry with a smaller-or-equal
offset as non-reusable, so it is re-encoded instead. The chain
self-heals on the next run, and a single warning per affected file
is emitted to make the fall-back visible. Tracking is intentionally
global, not per cache range, so inversions that span ranges (because
the next backup's cache flushes happen between the two inverted
offsets) are caught too.

Healthy archives written on or after pxar 1.0.1 cannot trigger this
fall-back since the encoder already enforces strict monotonicity at
write time.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2026-04-28 03:26:24 +02:00
Thomas Lamprecht
ebd0e469e2 client: pxar: inject held chunk on cache-range discontinuity
When the cached payload range cannot be extended because a non-reusable
file or directory end opens a hole, the previous flush may have held
back its last chunk for continuation. If the new range's first chunk
shares a digest with the held chunk via dedup, but the two correspond
to different physical positions in the previous archive, the next
flush's digest-match fast path collapses both into one without ever
injecting the held chunk's bytes. The encoder's payload_write_position
then trails the previous_payload_offset recorded during this flush,
and the next add_payload_ref produces a backwards PXAR_PAYLOAD_REF
offset, which the strict offset check introduced in pxar 1.0.1
rejects.

Inject any held chunk explicitly when a cache-range discontinuity is
detected, before update_range, so no held chunk can survive across a
hole. Healthy archives are unaffected: the held chunk only exists
when keep_last_chunk = true, and the inject path is the same one used
by every other branch that already advances the payload position.

The added test pins the cache contract this fix relies on:
update_range alone must not drop last_chunk, so the explicit
take_last_chunk + inject pair has to stay on the hole path.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2026-04-28 03:26:24 +02:00
Thomas Lamprecht
753a0e1890 d/changelog: improve and rework last entry
rechecking this made it seem improvable.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2026-04-26 03:17:31 +02:00
Thomas Lamprecht
08aa797399 bump version to 4.1.11-1
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2026-04-26 01:53:48 +02:00
Dominik Csapak
ffe55e23eb ui: encryption keys: always enable tape encryption restore button
Restoring a tape key should not require an existing tape key to select,
since it will be a new key, not overwriting the existing one.
(In the tape backup -> encryption keys view it's also always enabled)

To do that, remove the disabled/enableFn properties, but then the
component must be a 'button' (a proxmoxButton needs a record for it
to be enabled).

To make it a bit clearer that it does not have anything to do with the
listed keys, rename it to 'Restore Tape Key' and move it over to the
right of the toolbar.

While touching this, rename 'restoreEncryptionKey' to
'restoreTapeEncryptionKey' to make it also clearer in the code what it
does.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
Link: https://lore.proxmox.com/20260424115215.2377890-1-d.csapak@proxmox.com
2026-04-25 21:04:37 +02:00