"strings"
)
+// BackupReader checks cancellation between reads of an archive or file.
+type BackupReader struct {
+ ctx context.Context
+ reader io.Reader
+}
+
+func (r BackupReader) Read(data []byte) (int, error) {
+ if r.ctx.Err() != nil {
+ return 0, r.ctx.Err()
+ }
+
+ count, err := r.reader.Read(data)
+ if err != nil {
+ return count, fmt.Errorf("read backup data: %w", err)
+ }
+
+ return count, nil
+}
+
// backupArchive writes the files in root to path through a private temporary
// file and a rename. Only directories and regular files enter the archive.
func backupArchive(ctx context.Context, root, path string) (size int64, err error) {
return nil
})
+
err = errors.Join(err, archive.Close(), compressed.Close())
if err != nil {
return 0, fmt.Errorf("write archive: %w", err)
return info.Size(), nil
}
+// backupCopyFile copies the size fixed at the snapshot of path. Growth after
+// that point is not part of the copy, and a shorter file is an error.
+func backupCopyFile(ctx context.Context, out io.Writer, path string, size int64) (err error) {
+ file, err := os.Open(path)
+ if err != nil {
+ return fmt.Errorf("open backup file: %w", err)
+ }
+
+ defer func() {
+ err = errors.Join(err, file.Close())
+ }()
+
+ _, err = io.CopyN(out, BackupReader{
+ ctx: ctx,
+ reader: file,
+ }, size)
+ if err != nil {
+ return fmt.Errorf("copy backup file: %w", err)
+ }
+
+ return nil
+}
+
// backupExtract restores regular files and directories in an empty staging
// directory. Links, duplicate entries, unsafe names, and special modes fail.
// Directory modes apply last so a read-only directory can hold files.
ctx: ctx,
reader: compressed,
})
+
seen := make(map[string]bool)
var directories []*tar.Header
return nil
}
-// BackupReader checks cancellation between reads of an archive or file.
-type BackupReader struct {
- ctx context.Context
- reader io.Reader
-}
-
-func (r BackupReader) Read(data []byte) (int, error) {
- if r.ctx.Err() != nil {
- return 0, r.ctx.Err()
- }
-
- count, err := r.reader.Read(data)
- if err != nil {
- return count, fmt.Errorf("read backup data: %w", err)
- }
-
- return count, nil
-}
-
-// backupCopyFile copies the size fixed at the snapshot of path. Growth after
-// that point is not part of the copy, and a shorter file is an error.
-func backupCopyFile(ctx context.Context, out io.Writer, path string, size int64) (err error) {
- file, err := os.Open(path)
- if err != nil {
- return fmt.Errorf("open backup file: %w", err)
+// backupExcluded keeps runtime locks, sockets, probe files, and import copies
+// out of the snapshot.
+func backupExcluded(name string) bool {
+ first, _, _ := strings.Cut(filepath.ToSlash(name), "/")
+ if first == socketFileName || first == tokenLockFileName || first == "probe" || strings.HasPrefix(first, "agent-backup-") {
+ return true
}
- defer func() {
- err = errors.Join(err, file.Close())
- }()
-
- _, err = io.CopyN(out, BackupReader{
- ctx: ctx,
- reader: file,
- }, size)
- if err != nil {
- return fmt.Errorf("copy backup file: %w", err)
+ if filepath.Base(name) == "lock" || (first == imageDirName && strings.HasPrefix(filepath.Base(name), ".image-")) || (first == voiceDirName && (strings.HasPrefix(filepath.Base(name), ".voice-") || strings.HasPrefix(filepath.Base(name), ".transcript-"))) {
+ return true
}
- return nil
+ return false
}
func backupExtractFile(in io.Reader, path string, mode os.FileMode) (err error) {
return nil
}
-
-// backupExcluded keeps runtime locks, sockets, probe files, and import copies
-// out of the snapshot.
-func backupExcluded(name string) bool {
- first, _, _ := strings.Cut(filepath.ToSlash(name), "/")
- if first == socketFileName || first == tokenLockFileName || first == "probe" || strings.HasPrefix(first, "agent-backup-") {
- return true
- }
-
- if filepath.Base(name) == "lock" || (first == imageDirName && strings.HasPrefix(filepath.Base(name), ".image-")) {
- return true
- }
-
- return false
-}