forked from TrueCloudLab/restic
Merge pull request #2816 from greatroar/noatime
Set O_NOATIME flag on Linux
This commit is contained in:
commit
cc90f2ba6b
5 changed files with 112 additions and 0 deletions
10
changelog/unreleased/pull-2816
Normal file
10
changelog/unreleased/pull-2816
Normal 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
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
21
internal/fs/setflags_linux.go
Normal file
21
internal/fs/setflags_linux.go
Normal 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
|
||||
}
|
67
internal/fs/setflags_linux_test.go
Normal file
67
internal/fs/setflags_linux_test.go
Normal 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
|
||||
}
|
12
internal/fs/setflags_other.go
Normal file
12
internal/fs/setflags_other.go
Normal 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
|
||||
}
|
Loading…
Reference in a new issue