"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) {
compressed := gzip.NewWriter(file)
archive := tar.NewWriter(compressed)
- err = filepath.WalkDir(root, func(path string, entry fs.DirEntry, walkErr error) error {
- if walkErr != nil {
- return fmt.Errorf("walk backup: %w", walkErr)
+ err = filepath.WalkDir(root, func(path string, entry fs.DirEntry, err error) error {
+ if err != nil {
+ return fmt.Errorf("walk backup: %w", err)
}
if ctx.Err() != nil {
return nil
}
- name, nameErr := filepath.Rel(root, path)
- if nameErr != nil {
- return fmt.Errorf("name archive entry: %w", nameErr)
+ name, err := filepath.Rel(root, path)
+ if err != nil {
+ return fmt.Errorf("name archive entry: %w", err)
}
- info, infoErr := entry.Info()
- if infoErr != nil {
- return fmt.Errorf("read backup entry: %w", infoErr)
+ info, err := entry.Info()
+ if err != nil {
+ return fmt.Errorf("read backup entry: %w", err)
}
if !info.IsDir() && !info.Mode().IsRegular() {
return fmt.Errorf("unsupported backup entry %q", name)
}
- header, headerErr := tar.FileInfoHeader(info, "")
- if headerErr != nil {
- return fmt.Errorf("make archive header: %w", headerErr)
+ header, err := tar.FileInfoHeader(info, "")
+ if err != nil {
+ return fmt.Errorf("make archive header: %w", err)
}
header.Name = filepath.ToSlash(name)
header.Uid, header.Gid = 0, 0
header.Uname, header.Gname = "", ""
- headerErr = archive.WriteHeader(header)
- if headerErr != nil {
- return fmt.Errorf("write archive header: %w", headerErr)
+ err = archive.WriteHeader(header)
+ if err != nil {
+ return fmt.Errorf("write archive header: %w", err)
}
if info.IsDir() {
return nil
}
- return backupCopyFile(ctx, archive, path, info.Size())
+ err = backupCopyFile(ctx, archive, path, info.Size())
+ if err != nil {
+ return fmt.Errorf("archive file: %w", err)
+ }
+
+ return nil
})
err = errors.Join(err, archive.Close(), compressed.Close())
if err != nil {
err = errors.Join(err, compressed.Close())
}()
- archive := tar.NewReader(BackupReader{ctx: ctx, reader: compressed})
+ archive := tar.NewReader(BackupReader{
+ ctx: ctx,
+ reader: compressed,
+ })
seen := make(map[string]bool)
var directories []*tar.Header
for {
- header, readErr := archive.Next()
- if errors.Is(readErr, io.EOF) {
+ var header *tar.Header
+
+ header, err = archive.Next()
+ if errors.Is(err, io.EOF) {
break
}
- if readErr != nil {
- return fmt.Errorf("read archive entry: %w", readErr)
+ if err != nil {
+ return fmt.Errorf("read archive entry: %w", err)
}
name := strings.TrimSuffix(header.Name, "/")
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) {
err = errors.Join(err, file.Close())
}()
- _, err = io.CopyN(out, BackupReader{ctx: ctx, reader: file}, size)
+ _, err = io.CopyN(out, BackupReader{
+ ctx: ctx,
+ reader: file,
+ }, size)
if err != nil {
return fmt.Errorf("copy backup file: %w", err)
}
return true
}
- if filepath.Base(name) == "lock" {
+ 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
}