diff --git a/internal/clientsdb/db.go b/internal/clientsdb/db.go index 00477da..8f0ca46 100644 --- a/internal/clientsdb/db.go +++ b/internal/clientsdb/db.go @@ -8,6 +8,7 @@ import ( "maps" "net" "os" + "path/filepath" "sync" "time" ) @@ -142,10 +143,13 @@ func (db *DB) save(next Data) error { } tmpFile := db.path + ".tmp" - err = os.WriteFile(tmpFile, b, 0o600) // 0o600: файл содержит Client ID токены авторизации + err = writeSync(tmpFile, b) if err == nil { err = os.Rename(tmpFile, db.path) } + if err == nil { + syncDir(filepath.Dir(db.path)) + } if err == nil { // Авторизация меняется только после успешной замены файла. db.data = next @@ -157,6 +161,32 @@ func (db *DB) save(next Data) error { return err } +func writeSync(path string, b []byte) error { + f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600) // 0o600: файл содержит Client ID токены авторизации + if err != nil { + return err + } + if _, err = f.Write(b); err == nil { + err = f.Sync() + } + if closeErr := f.Close(); err == nil { + err = closeErr + } + if err != nil { + _ = os.Remove(path) + } + return err +} + +func syncDir(dir string) { + d, err := os.Open(dir) + if err != nil { + return + } + _ = d.Sync() + _ = d.Close() +} + // Тег режима едет хвостом той же записи: клиент до этого поля его не писал, а читатель // брал ровно 1+len байт - лишний байт старый сервер молча пропускает. const ( diff --git a/internal/clientsdb/db_test.go b/internal/clientsdb/db_test.go index adc79db..2a5b36a 100644 --- a/internal/clientsdb/db_test.go +++ b/internal/clientsdb/db_test.go @@ -14,11 +14,11 @@ func TestFailedSavePreservesAuthorization(t *testing.T) { if err != nil { t.Fatal(err) } - if err := db.Add("existing", "original"); err != nil { + if err = db.Add("existing", "original"); err != nil { t.Fatal(err) } // Каталог вместо временного файла воспроизводит отказ записи без прав root. - if err := os.Mkdir(path+".tmp", 0o700); err != nil { + if err = os.Mkdir(path+".tmp", 0o700); err != nil { t.Fatal(err) } operations := []struct { @@ -31,7 +31,7 @@ func TestFailedSavePreservesAuthorization(t *testing.T) { } for _, op := range operations { t.Run(op.name, func(t *testing.T) { - if err := op.run(); err == nil { + if runErr := op.run(); runErr == nil { t.Fatal("save unexpectedly succeeded") } if !db.IsAuthorized("existing") || db.IsAuthorized("new") {