From 7a3223e4157f836942613c3a9c3691b16cc24420 Mon Sep 17 00:00:00 2001 From: Gani Georgiev Date: Tue, 8 Aug 2023 14:15:29 +0300 Subject: [PATCH] [#3089] use a temp dir inside pb_data to prevent backups cross-device link error --- CHANGELOG.md | 8 +++++--- core/base_backup.go | 35 +++++++++++++++++++++-------------- core/base_backup_test.go | 1 + 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a0002c1f..cb6fe6d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,13 @@ -## v0.17.3-WIP +## v0.17.3 + +- Fixed Docker `cross-device link` error when creating `pb_data` backups on a local mounted volume ([#3089](https://github.com/pocketbase/pocketbase/issues/3089)). + +- Fixed the error messages for relation to views ([#3090](https://github.com/pocketbase/pocketbase/issues/3090)). - Always reserve space for the scrollbar to reduce the layout shifts in the Admin UI records listing due to the deprecated `overflow: overlay`. - Enabled lazy loading for the Admin UI thumb images. -- Fixed the error messages for relation to views ([#3090](https://github.com/pocketbase/pocketbase/issues/3090)). - ## v0.17.2 diff --git a/core/base_backup.go b/core/base_backup.go index a1b1f3a4..43151f41 100644 --- a/core/base_backup.go +++ b/core/base_backup.go @@ -57,16 +57,21 @@ func (app *BaseApp) CreateBackup(ctx context.Context, name string) error { app.Cache().Set(CacheKeyActiveBackup, name) defer app.Cache().Remove(CacheKeyActiveBackup) + // make sure that the special temp directory exists + // note: it needs to be inside the current pb_data to avoid "cross-device link" errors + localTempDir := filepath.Join(app.DataDir(), LocalTempDirName) + if err := os.MkdirAll(localTempDir, os.ModePerm); err != nil { + return fmt.Errorf("failed to create a temp dir: %w", err) + } + // Archive pb_data in a temp directory, exluding the "backups" dir itself (if exist). // // Run in transaction to temporary block other writes (transactions uses the NonconcurrentDB connection). // --- - tempPath := filepath.Join(os.TempDir(), "pb_backup_"+security.PseudorandomString(4)) + tempPath := filepath.Join(localTempDir, "pb_backup_"+security.PseudorandomString(4)) createErr := app.Dao().RunInTransaction(func(txDao *daos.Dao) error { - if err := archive.Create(app.DataDir(), tempPath, LocalBackupsDirName); err != nil { - return err - } - return nil + // @todo consider experimenting with temp switching the readonly pragma after the db interface change + return archive.Create(app.DataDir(), tempPath, LocalBackupsDirName) }) if createErr != nil { return createErr @@ -152,7 +157,15 @@ func (app *BaseApp) RestoreBackup(ctx context.Context, name string) error { } defer br.Close() - tempZip, err := os.CreateTemp(os.TempDir(), "pb_restore") + // make sure that the special temp directory exists + // note: it needs to be inside the current pb_data to avoid "cross-device link" errors + localTempDir := filepath.Join(app.DataDir(), LocalTempDirName) + if err := os.MkdirAll(localTempDir, os.ModePerm); err != nil { + return fmt.Errorf("failed to create a temp dir: %w", err) + } + + // create a temp zip file from the blob.Reader and try to extract it + tempZip, err := os.CreateTemp(localTempDir, "pb_restore_zip") if err != nil { return err } @@ -162,13 +175,7 @@ func (app *BaseApp) RestoreBackup(ctx context.Context, name string) error { return err } - // make sure that the special temp directory - if err := os.MkdirAll(filepath.Join(app.DataDir(), LocalTempDirName), os.ModePerm); err != nil { - return fmt.Errorf("failed to create a temp dir: %w", err) - } - - // note: it needs to be inside the current pb_data to avoid "cross-device link" errors - extractedDataDir := filepath.Join(app.DataDir(), LocalTempDirName, "pb_restore_"+security.PseudorandomString(4)) + extractedDataDir := filepath.Join(localTempDir, "pb_restore_"+security.PseudorandomString(4)) defer os.RemoveAll(extractedDataDir) if err := archive.Extract(tempZip.Name(), extractedDataDir); err != nil { return err @@ -192,7 +199,7 @@ func (app *BaseApp) RestoreBackup(ctx context.Context, name string) error { // move the current pb_data content to a special temp location // that will hold the old data between dirs replace // (the temp dir will be automatically removed on the next app start) - oldTempDataDir := filepath.Join(app.DataDir(), LocalTempDirName, "old_pb_data_"+security.PseudorandomString(4)) + oldTempDataDir := filepath.Join(localTempDir, "old_pb_data_"+security.PseudorandomString(4)) if err := osutils.MoveDirContent(app.DataDir(), oldTempDataDir, exclude...); err != nil { return fmt.Errorf("failed to move the current pb_data content to a temp location: %w", err) } diff --git a/core/base_backup_test.go b/core/base_backup_test.go index 0b4da323..c7b3da24 100644 --- a/core/base_backup_test.go +++ b/core/base_backup_test.go @@ -127,6 +127,7 @@ func verifyBackupContent(app core.App, path string) error { "logs.db-shm", "logs.db-wal", ".gitignore", + ".pb_temp_to_delete", } entries, err := os.ReadDir(dir)