forked from TrueCloudLab/restic
Use fadvise() to not cache the content of files read
This commit is contained in:
parent
d2df2ad92d
commit
feb664620a
10 changed files with 117 additions and 44 deletions
|
@ -13,6 +13,7 @@ import (
|
||||||
|
|
||||||
"restic/backend"
|
"restic/backend"
|
||||||
"restic/debug"
|
"restic/debug"
|
||||||
|
"restic/fs"
|
||||||
"restic/pack"
|
"restic/pack"
|
||||||
"restic/pipe"
|
"restic/pipe"
|
||||||
"restic/repository"
|
"restic/repository"
|
||||||
|
@ -126,7 +127,7 @@ func (arch *Archiver) SaveTreeJSON(item interface{}) (backend.ID, error) {
|
||||||
return arch.repo.SaveJSON(pack.Tree, item)
|
return arch.repo.SaveJSON(pack.Tree, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (arch *Archiver) reloadFileIfChanged(node *Node, file *os.File) (*Node, error) {
|
func (arch *Archiver) reloadFileIfChanged(node *Node, file fs.File) (*Node, error) {
|
||||||
fi, err := file.Stat()
|
fi, err := file.Stat()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -155,7 +156,7 @@ type saveResult struct {
|
||||||
bytes uint64
|
bytes uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (arch *Archiver) saveChunk(chunk chunker.Chunk, p *Progress, token struct{}, file *os.File, resultChannel chan<- saveResult) {
|
func (arch *Archiver) saveChunk(chunk chunker.Chunk, p *Progress, token struct{}, file fs.File, resultChannel chan<- saveResult) {
|
||||||
defer freeBuf(chunk.Data)
|
defer freeBuf(chunk.Data)
|
||||||
|
|
||||||
id := backend.Hash(chunk.Data)
|
id := backend.Hash(chunk.Data)
|
||||||
|
@ -209,7 +210,7 @@ func updateNodeContent(node *Node, results []saveResult) error {
|
||||||
// SaveFile stores the content of the file on the backend as a Blob by calling
|
// SaveFile stores the content of the file on the backend as a Blob by calling
|
||||||
// Save for each chunk.
|
// Save for each chunk.
|
||||||
func (arch *Archiver) SaveFile(p *Progress, node *Node) error {
|
func (arch *Archiver) SaveFile(p *Progress, node *Node) error {
|
||||||
file, err := node.OpenForReading()
|
file, err := fs.Open(node.path)
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
3
src/restic/fs/doc.go
Normal file
3
src/restic/fs/doc.go
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
// Package fs implements an OS independend abstraction of a file system
|
||||||
|
// suitable for backup purposes.
|
||||||
|
package fs
|
18
src/restic/fs/file.go
Normal file
18
src/restic/fs/file.go
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
package fs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// File is an open file on a file system.
|
||||||
|
type File interface {
|
||||||
|
io.Reader
|
||||||
|
io.Writer
|
||||||
|
io.Closer
|
||||||
|
|
||||||
|
Stat() (os.FileInfo, error)
|
||||||
|
|
||||||
|
// ClearCache removes the file's content from the OS cache.
|
||||||
|
ClearCache() error
|
||||||
|
}
|
20
src/restic/fs/file_all.go
Normal file
20
src/restic/fs/file_all.go
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
// +build !linux
|
||||||
|
|
||||||
|
package fs
|
||||||
|
|
||||||
|
import "os"
|
||||||
|
|
||||||
|
// Open opens a file for reading.
|
||||||
|
func Open(name string) (File, error) {
|
||||||
|
f, err := os.OpenFile(name, os.O_RDONLY, 0)
|
||||||
|
return osFile{File: f}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// osFile wraps an *os.File and adds a no-op ClearCache() method.
|
||||||
|
type osFile struct {
|
||||||
|
*os.File
|
||||||
|
}
|
||||||
|
|
||||||
|
func (osFile) ClearCache() error {
|
||||||
|
return nil
|
||||||
|
}
|
69
src/restic/fs/file_linux.go
Normal file
69
src/restic/fs/file_linux.go
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
package fs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Open opens a file for reading, without updating the atime and without caching data on read.
|
||||||
|
func Open(name string) (File, error) {
|
||||||
|
file, err := os.OpenFile(name, os.O_RDONLY|syscall.O_NOATIME, 0)
|
||||||
|
if os.IsPermission(err) {
|
||||||
|
file, err = os.OpenFile(name, os.O_RDONLY, 0)
|
||||||
|
}
|
||||||
|
return &nonCachingFile{File: file}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// osFile wraps an *os.File and adds a no-op ClearCache() method.
|
||||||
|
type osFile struct {
|
||||||
|
*os.File
|
||||||
|
}
|
||||||
|
|
||||||
|
func (osFile) ClearCache() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// these constants should've been defined in x/sys/unix, but somehow aren't.
|
||||||
|
const (
|
||||||
|
_POSIX_FADV_NORMAL = iota
|
||||||
|
_POSIX_FADV_RANDOM
|
||||||
|
_POSIX_FADV_SEQUENTIAL
|
||||||
|
_POSIX_FADV_WILLNEED
|
||||||
|
_POSIX_FADV_DONTNEED
|
||||||
|
_POSIX_FADV_NOREUSE
|
||||||
|
)
|
||||||
|
|
||||||
|
// nonCachingFile wraps an *os.File and calls fadvise() to instantly forget
|
||||||
|
// data that has been read or written.
|
||||||
|
type nonCachingFile struct {
|
||||||
|
*os.File
|
||||||
|
readOffset int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *nonCachingFile) Read(p []byte) (int, error) {
|
||||||
|
n, err := f.File.Read(p)
|
||||||
|
|
||||||
|
if n > 0 {
|
||||||
|
ferr := unix.Fadvise(int(f.File.Fd()), f.readOffset, int64(n), _POSIX_FADV_DONTNEED)
|
||||||
|
|
||||||
|
f.readOffset += int64(n)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
err = ferr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *nonCachingFile) ClearCache() error {
|
||||||
|
err := f.File.Sync()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return unix.Fadvise(int(f.File.Fd()), 0, 0, _POSIX_FADV_DONTNEED)
|
||||||
|
}
|
|
@ -1,13 +1,6 @@
|
||||||
package restic
|
package restic
|
||||||
|
|
||||||
import (
|
import "syscall"
|
||||||
"os"
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (node *Node) OpenForReading() (*os.File, error) {
|
|
||||||
return os.Open(node.path)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (node Node) restoreSymlinkTimestamps(path string, utimes [2]syscall.Timespec) error {
|
func (node Node) restoreSymlinkTimestamps(path string, utimes [2]syscall.Timespec) error {
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -1,13 +1,6 @@
|
||||||
package restic
|
package restic
|
||||||
|
|
||||||
import (
|
import "syscall"
|
||||||
"os"
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (node *Node) OpenForReading() (*os.File, error) {
|
|
||||||
return os.OpenFile(node.path, os.O_RDONLY, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (node Node) restoreSymlinkTimestamps(path string, utimes [2]syscall.Timespec) error {
|
func (node Node) restoreSymlinkTimestamps(path string, utimes [2]syscall.Timespec) error {
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -9,14 +9,6 @@ import (
|
||||||
"github.com/juju/errors"
|
"github.com/juju/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (node *Node) OpenForReading() (*os.File, error) {
|
|
||||||
file, err := os.OpenFile(node.path, os.O_RDONLY|syscall.O_NOATIME, 0)
|
|
||||||
if os.IsPermission(err) {
|
|
||||||
return os.OpenFile(node.path, os.O_RDONLY, 0)
|
|
||||||
}
|
|
||||||
return file, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (node Node) restoreSymlinkTimestamps(path string, utimes [2]syscall.Timespec) error {
|
func (node Node) restoreSymlinkTimestamps(path string, utimes [2]syscall.Timespec) error {
|
||||||
dir, err := os.Open(filepath.Dir(path))
|
dir, err := os.Open(filepath.Dir(path))
|
||||||
defer dir.Close()
|
defer dir.Close()
|
||||||
|
|
|
@ -1,17 +1,6 @@
|
||||||
package restic
|
package restic
|
||||||
|
|
||||||
import (
|
import "syscall"
|
||||||
"os"
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (node *Node) OpenForReading() (*os.File, error) {
|
|
||||||
file, err := os.OpenFile(node.path, os.O_RDONLY, 0)
|
|
||||||
if os.IsPermission(err) {
|
|
||||||
return os.OpenFile(node.path, os.O_RDONLY, 0)
|
|
||||||
}
|
|
||||||
return file, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (node Node) restoreSymlinkTimestamps(path string, utimes [2]syscall.Timespec) error {
|
func (node Node) restoreSymlinkTimestamps(path string, utimes [2]syscall.Timespec) error {
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -2,14 +2,9 @@ package restic
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"os"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (node *Node) OpenForReading() (*os.File, error) {
|
|
||||||
return os.OpenFile(node.path, os.O_RDONLY, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// mknod() creates a filesystem node (file, device
|
// mknod() creates a filesystem node (file, device
|
||||||
// special file, or named pipe) named pathname, with attributes
|
// special file, or named pipe) named pathname, with attributes
|
||||||
// specified by mode and dev.
|
// specified by mode and dev.
|
||||||
|
|
Loading…
Reference in a new issue