fix: avoid bind mount read errors
Some checks are pending
build / build (push) Waiting to run

This commit is contained in:
Viktor Varland 2025-09-30 12:39:49 +02:00
parent ed7236d491
commit 6cd9860681
Signed by: varl
GPG key ID: 7459F0B410115EE8
2 changed files with 74 additions and 6 deletions

View file

@ -9,9 +9,15 @@ import (
"path/filepath"
"sync"
"sync/atomic"
"syscall"
"time"
)
var (
osWriteFile = os.WriteFile
osRename = os.Rename
)
type VideoRequest struct {
URL string `json:"url"`
OutDir string `json:"out_dir"`
@ -169,12 +175,7 @@ func (q *Queue) persistLocked() error {
return err
}
tmp := q.persist + ".tmp"
if err := os.WriteFile(tmp, data, 0o644); err != nil {
return err
}
return os.Rename(tmp, q.persist)
return writeFileAtomic(q.persist, data)
}
var idCounter uint64
@ -183,3 +184,24 @@ func generateID() string {
count := atomic.AddUint64(&idCounter, 1)
return fmt.Sprintf("%d-%d", time.Now().UTC().UnixNano(), count)
}
func writeFileAtomic(path string, data []byte) error {
tmp := path + ".tmp"
if err := osWriteFile(tmp, data, 0o644); err != nil {
return err
}
if err := osRename(tmp, path); err != nil {
if errors.Is(err, syscall.EBUSY) {
if writeErr := osWriteFile(path, data, 0o644); writeErr != nil {
_ = os.Remove(tmp)
return writeErr
}
return os.Remove(tmp)
}
_ = os.Remove(tmp)
return err
}
return nil
}

View file

@ -4,6 +4,7 @@ import (
"context"
"os"
"path/filepath"
"syscall"
"testing"
"time"
)
@ -90,3 +91,48 @@ func TestQueuePersistence(t *testing.T) {
t.Fatalf("expected queue file removed after draining, got %v", err)
}
}
func TestWriteFileAtomicFallback(t *testing.T) {
// Simulate EBUSY by replacing os.Rename temporarily.
origRename := osRename
origWriteFile := osWriteFile
defer func() {
osRename = origRename
osWriteFile = origWriteFile
}()
tmpDir := t.TempDir()
path := filepath.Join(tmpDir, "queue.json")
odyssey := filepath.Join(tmpDir, "queue.json.tmp")
osWriteFile = func(name string, data []byte, perm os.FileMode) error {
if name == path {
if err := os.WriteFile(name, data, perm); err != nil {
return err
}
return nil
}
return os.WriteFile(name, data, perm)
}
osRename = func(oldpath, newpath string) error {
return syscall.EBUSY
}
if err := writeFileAtomic(path, []byte("{}")); err != nil {
t.Fatalf("writeFileAtomic: %v", err)
}
data, err := os.ReadFile(path)
if err != nil {
t.Fatalf("read path: %v", err)
}
if string(data) != "{}" {
t.Fatalf("expected file contents '{}', got %q", data)
}
if _, err := os.Stat(odyssey); !os.IsNotExist(err) {
t.Fatalf("expected tmp removed, got %v", err)
}
}