Merge pull request #2816 from greatroar/noatime

Set O_NOATIME flag on Linux
This commit is contained in:
MichaelEischer 2022-02-07 21:38:31 +01:00 committed by GitHub
commit cc90f2ba6b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 112 additions and 0 deletions

View file

@ -0,0 +1,10 @@
Enhancement: backup no longer updates file access times on Linux
When reading files during backup, restic caused the operating system to update
the file access times. Note that this does not apply to filesystems with
disabled file access times.
Restic now instructs the operation system not to update the file access time,
if the user running restic is the file owner or has root permissions.
https://github.com/restic/restic/pull/2816

View file

@ -24,6 +24,7 @@ func (fs Local) Open(name string) (File, error) {
if err != nil {
return nil, err
}
_ = setFlags(f)
return f, nil
}
@ -37,6 +38,7 @@ func (fs Local) OpenFile(name string, flag int, perm os.FileMode) (File, error)
if err != nil {
return nil, err
}
_ = setFlags(f)
return f, nil
}

View file

@ -0,0 +1,21 @@
package fs
import (
"os"
"golang.org/x/sys/unix"
)
// SetFlags tries to set the O_NOATIME flag on f, which prevents the kernel
// from updating the atime on a read call.
//
// The call fails when we're not the owner of the file or root. The caller
// should ignore the error, which is returned for testing only.
func setFlags(f *os.File) error {
fd := f.Fd()
flags, err := unix.FcntlInt(fd, unix.F_GETFL, 0)
if err == nil {
_, err = unix.FcntlInt(fd, unix.F_SETFL, flags|unix.O_NOATIME)
}
return err
}

View file

@ -0,0 +1,67 @@
package fs
import (
"io"
"io/ioutil"
"os"
"testing"
"time"
rtest "github.com/restic/restic/internal/test"
"golang.org/x/sys/unix"
)
func TestNoatime(t *testing.T) {
f, err := ioutil.TempFile("", "restic-test-noatime")
if err != nil {
t.Fatal(err)
}
defer func() {
_ = f.Close()
err = Remove(f.Name())
if err != nil {
t.Fatal(err)
}
}()
// Only run this test on common filesystems that support O_NOATIME.
// On others, we may not get an error.
if !supportsNoatime(t, f) {
t.Skip("temp directory may not support O_NOATIME, skipping")
}
// From this point on, we own the file, so we should not get EPERM.
_, err = io.WriteString(f, "Hello!")
rtest.OK(t, err)
_, err = f.Seek(0, io.SeekStart)
rtest.OK(t, err)
getAtime := func() time.Time {
info, err := f.Stat()
rtest.OK(t, err)
return ExtendedStat(info).AccessTime
}
atime := getAtime()
err = setFlags(f)
rtest.OK(t, err)
_, err = f.Read(make([]byte, 1))
rtest.OK(t, err)
rtest.Equals(t, atime, getAtime())
}
func supportsNoatime(t *testing.T, f *os.File) bool {
var fsinfo unix.Statfs_t
err := unix.Fstatfs(int(f.Fd()), &fsinfo)
rtest.OK(t, err)
return fsinfo.Type == unix.BTRFS_SUPER_MAGIC ||
fsinfo.Type == unix.EXT2_SUPER_MAGIC ||
fsinfo.Type == unix.EXT3_SUPER_MAGIC ||
fsinfo.Type == unix.EXT4_SUPER_MAGIC ||
fsinfo.Type == unix.TMPFS_MAGIC
}

View file

@ -0,0 +1,12 @@
//go:build !linux
// +build !linux
package fs
import "os"
// OS-specific replacements of setFlags can set file status flags
// that improve I/O performance.
func setFlags(*os.File) error {
return nil
}