From 5cd43dd4c9dd110aeb6d7cc71067927a0d56506b Mon Sep 17 00:00:00 2001 From: Matthew Penner Date: Wed, 1 Sep 2021 09:54:41 -0600 Subject: [PATCH] archive: keep timestamps when extracting --- server/backup.go | 8 ++++++-- server/backup/backup.go | 3 ++- server/backup/backup_local.go | 2 +- server/backup/backup_s3.go | 2 +- server/filesystem/compress.go | 4 ++++ server/filesystem/filesystem.go | 17 +++++++++++++++++ 6 files changed, 31 insertions(+), 5 deletions(-) diff --git a/server/backup.go b/server/backup.go index acc8797..f511634 100644 --- a/server/backup.go +++ b/server/backup.go @@ -5,6 +5,7 @@ import ( "io/fs" "io/ioutil" "os" + "time" "emperror.dev/errors" "github.com/apex/log" @@ -152,12 +153,15 @@ func (s *Server) RestoreBackup(b backup.BackupInterface, reader io.ReadCloser) ( // Attempt to restore the backup to the server by running through each entry // in the file one at a time and writing them to the disk. s.Log().Debug("starting file writing process for backup restoration") - err = b.Restore(s.Context(), reader, func(file string, r io.Reader, mode fs.FileMode) error { + err = b.Restore(s.Context(), reader, func(file string, r io.Reader, mode fs.FileMode, atime, mtime time.Time) error { s.Events().Publish(DaemonMessageEvent, "(restoring): "+file) if err := s.Filesystem().Writefile(file, r); err != nil { return err } - return s.Filesystem().Chmod(file, mode) + if err := s.Filesystem().Chmod(file, mode); err != nil { + return err + } + return s.Filesystem().Chtimes(file, atime, mtime) }) return errors.WithStackIf(err) diff --git a/server/backup/backup.go b/server/backup/backup.go index e344ae2..496720e 100644 --- a/server/backup/backup.go +++ b/server/backup/backup.go @@ -8,6 +8,7 @@ import ( "io/fs" "os" "path" + "time" "emperror.dev/errors" "github.com/apex/log" @@ -26,7 +27,7 @@ const ( // RestoreCallback is a generic restoration callback that exists for both local // and remote backups allowing the files to be restored. -type RestoreCallback func(file string, r io.Reader, mode fs.FileMode) error +type RestoreCallback func(file string, r io.Reader, mode fs.FileMode, atime, mtime time.Time) error // noinspection GoNameStartsWithPackageName type BackupInterface interface { diff --git a/server/backup/backup_local.go b/server/backup/backup_local.go index 61dc164..c652b13 100644 --- a/server/backup/backup_local.go +++ b/server/backup/backup_local.go @@ -88,7 +88,7 @@ func (b *LocalBackup) Restore(ctx context.Context, _ io.Reader, callback Restore if f.IsDir() { return nil } - return callback(filesystem.ExtractNameFromArchive(f), f, f.Mode()) + return callback(filesystem.ExtractNameFromArchive(f), f, f.Mode(), f.ModTime(), f.ModTime()) } }) } diff --git a/server/backup/backup_s3.go b/server/backup/backup_s3.go index e240b5e..85faa59 100644 --- a/server/backup/backup_s3.go +++ b/server/backup/backup_s3.go @@ -116,7 +116,7 @@ func (s *S3Backup) Restore(ctx context.Context, r io.Reader, callback RestoreCal return err } if header.Typeflag == tar.TypeReg { - if err := callback(header.Name, tr, header.FileInfo().Mode()); err != nil { + if err := callback(header.Name, tr, header.FileInfo().Mode(), header.AccessTime, header.ModTime); err != nil { return err } } diff --git a/server/filesystem/compress.go b/server/filesystem/compress.go index 5227512..cabbfdd 100644 --- a/server/filesystem/compress.go +++ b/server/filesystem/compress.go @@ -136,6 +136,10 @@ func (fs *Filesystem) DecompressFile(dir string, file string) error { if err := fs.Chmod(p, f.Mode()); err != nil { return wrapError(err, source) } + // Update the file modification time to the one set in the archive. + if err := fs.Chtimes(p, f.ModTime(), f.ModTime()); err != nil { + return wrapError(err, source) + } return nil }) if err != nil { diff --git a/server/filesystem/filesystem.go b/server/filesystem/filesystem.go index 4f04d9a..b507709 100644 --- a/server/filesystem/filesystem.go +++ b/server/filesystem/filesystem.go @@ -528,3 +528,20 @@ func (fs *Filesystem) ListDirectory(p string) ([]Stat, error) { return out, nil } + +func (fs *Filesystem) Chtimes(path string, atime, mtime time.Time) error { + cleaned, err := fs.SafePath(path) + if err != nil { + return err + } + + if fs.isTest { + return nil + } + + if err := os.Chtimes(cleaned, atime, mtime); err != nil { + return err + } + + return nil +}