From ac43e63ef86e7d27625ce74172cff3c47af2a014 Mon Sep 17 00:00:00 2001 From: samosvalishe Date: Mon, 21 Sep 2026 13:43:13 +0300 Subject: [PATCH] =?UTF-8?q?fix(clients):=20=D1=84=D0=B8=D0=BA=D1=81=D0=B8?= =?UTF-8?q?=D1=80=D0=BE=D0=B2=D0=B0=D1=82=D1=8C=20=D0=B1=D0=B0=D0=B7=D1=83?= =?UTF-8?q?=20=D0=BD=D0=B0=20=D0=B4=D0=B8=D1=81=D0=BA=20=D1=87=D0=B5=D1=80?= =?UTF-8?q?=D0=B5=D0=B7=20fsync?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/clientsdb/db.go | 32 +++++++++++++++++++++++++++++++- internal/clientsdb/db_test.go | 6 +++--- 2 files changed, 34 insertions(+), 4 deletions(-) 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") {