agent v0.36.3: DetachDrive loop-umounts stacked binds (full detach)
Detach now removes ALL stacked binds at a stable path, not just one layer, so an eject fully detaches even with >1 bind (keeping fail-close intact). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,12 @@
|
|||||||
All notable changes to **felhom-agent** are recorded here. Update on every code
|
All notable changes to **felhom-agent** are recorded here. Update on every code
|
||||||
change that gets pushed.
|
change that gets pushed.
|
||||||
|
|
||||||
|
## v0.36.3 — DetachDrive loop-umounts stacked binds (2026-06-15)
|
||||||
|
|
||||||
|
DetachDrive now umounts ALL stacked binds at a stable path (bounded loop), not just one layer — so an
|
||||||
|
eject/detach fully detaches even if more than one bind accumulated (operator bind on top, or a rare
|
||||||
|
attach race), keeping the fail-close intact. Caught in the E13 rapid eject/reconnect sweep.
|
||||||
|
|
||||||
## v0.36.2 — eject also keeps the raw mounted (reconnectable) (2026-06-15)
|
## v0.36.2 — eject also keeps the raw mounted (reconnectable) (2026-06-15)
|
||||||
|
|
||||||
Extends v0.36.1 to EJECT: eject now DetachDrive`s the bind under the parent but LEAVES the raw
|
Extends v0.36.1 to EJECT: eject now DetachDrive`s the bind under the parent but LEAVES the raw
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ import (
|
|||||||
|
|
||||||
// version is the agent version. Overridable at build time with
|
// version is the agent version. Overridable at build time with
|
||||||
// -ldflags "-X main.version=<v>"; defaults to the in-repo CHANGELOG version.
|
// -ldflags "-X main.version=<v>"; defaults to the in-repo CHANGELOG version.
|
||||||
var version = "0.36.2"
|
var version = "0.36.3"
|
||||||
|
|
||||||
// runGuestHook is the PVE pre-start hook body (`felhom-agent guest-hook <vmid> <phase>`). On the
|
// runGuestHook is the PVE pre-start hook body (`felhom-agent guest-hook <vmid> <phase>`). On the
|
||||||
// pre-start phase it creates placeholder dirs for any absent bind-mount source so the guest always boots
|
// pre-start phase it creates placeholder dirs for any absent bind-mount source so the guest always boots
|
||||||
|
|||||||
@@ -293,13 +293,18 @@ func (b *GuestBinder) DetachDrive(ctx context.Context, where string) error {
|
|||||||
if stable == "" {
|
if stable == "" {
|
||||||
return fmt.Errorf("guest-detach: %q is not a /mnt/<name> mount", where)
|
return fmt.Errorf("guest-detach: %q is not a /mnt/<name> mount", where)
|
||||||
}
|
}
|
||||||
if !isHostMountpoint(stable) {
|
// Loop-umount: a stable path can carry MORE THAN ONE stacked bind (e.g. an operator-applied bind on
|
||||||
return nil // already detached
|
// top of the agent's, or a rare attach race). Detach must remove ALL layers, else eject leaves a
|
||||||
|
// lower bind exposing data → fail-close broken. Bounded to avoid an infinite loop.
|
||||||
|
for i := 0; i < 16 && isHostMountpoint(stable); i++ {
|
||||||
|
if err := b.run(ctx, "umount", stable); err != nil {
|
||||||
|
return fmt.Errorf("guest-detach: umount %s (layer %d): %w", stable, i, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if err := b.run(ctx, "umount", stable); err != nil {
|
if isHostMountpoint(stable) {
|
||||||
return fmt.Errorf("guest-detach: umount %s: %w", stable, err)
|
return fmt.Errorf("guest-detach: %s still a mountpoint after 16 umounts", stable)
|
||||||
}
|
}
|
||||||
b.logger.Info("guest-detach: drive unmounted from shared parent (live, fail-closed)", "where", where, "stable", stable)
|
b.logger.Info("guest-detach: drive fully unmounted from shared parent (live, fail-closed)", "where", where, "stable", stable)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user