package vaultik import ( "errors" "fmt" "strings" "sneak.berlin/go/vaultik/internal/storage" ) // destinationMarkerKey is the object every backup writes at the root of // the backup destination store. While the local index holds a snapshot, // a destination without it is treated as not present: an unplugged // volume leaves an empty directory at its mount point, which would // otherwise read as a store whose snapshots are all gone. const destinationMarkerKey = "vaultik-destination.txt" // destinationMarkerText is the whole content of the marker. It names no // host, path or time. const destinationMarkerText = "This is a vaultik backup destination store.\n" // errDestinationNotPresent is returned when the local index holds a // snapshot and the backup destination store has no marker. var errDestinationNotPresent = errors.New("backup destination store is not present") // checkDestinationMarker returns errDestinationNotPresent when the local // index holds a snapshot and the backup destination store has no marker. // With no snapshot in the local index, as on a first backup or on // another machine, there is nothing to check. func (v *Vaultik) checkDestinationMarker() error { ids, err := v.localSnapshotIDs() if err != nil { return fmt.Errorf("listing local snapshots: %w", err) } if len(ids) == 0 { return nil } _, err = v.Storage.Stat(v.ctx, destinationMarkerKey) if errors.Is(err, storage.ErrNotFound) { return fmt.Errorf("%w: %s has no %s, but the local index holds "+ "snapshots stored there; mount the volume if it is removable, "+ "or if the destination was emptied on purpose, run "+ "'vaultik database delete' and back up again", errDestinationNotPresent, v.Storage.Info().Location, destinationMarkerKey) } if err != nil { return fmt.Errorf("checking for %s: %w", destinationMarkerKey, err) } return nil } // markDestination checks the backup destination store with // checkDestinationMarker and then writes the marker, so a first backup // marks its destination and a later one writes the same text again. func (v *Vaultik) markDestination() error { err := v.checkDestinationMarker() if err != nil { return err } err = v.Storage.Put(v.ctx, destinationMarkerKey, strings.NewReader(destinationMarkerText)) if err != nil { return fmt.Errorf("writing %s to the backup destination store: %w", destinationMarkerKey, err) } return nil }