mirror of
https://github.com/restic/restic.git
synced 2026-06-08 16:34:44 -04:00
Merge 054da85109 into 1807d269cd
This commit is contained in:
commit
3934f7bc90
5 changed files with 113 additions and 0 deletions
|
|
@ -85,6 +85,7 @@ type BackupOptions struct {
|
|||
ExcludeCaches bool
|
||||
ExcludeLargerThan string
|
||||
ExcludeCloudFiles bool
|
||||
ExcludeNoDump bool
|
||||
Stdin bool
|
||||
StdinFilename string
|
||||
StdinCommand bool
|
||||
|
|
@ -143,6 +144,9 @@ func (opts *BackupOptions) AddFlags(f *pflag.FlagSet) {
|
|||
if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {
|
||||
f.BoolVar(&opts.ExcludeCloudFiles, "exclude-cloud-files", false, "excludes online-only cloud files (such as OneDrive, iCloud drive, …)")
|
||||
}
|
||||
if runtime.GOOS == "linux" {
|
||||
f.BoolVar(&opts.ExcludeNoDump, "exclude-no-dump", false, "excludes directories and files marked with the `no dump` attribute)")
|
||||
}
|
||||
f.BoolVar(&opts.SkipIfUnchanged, "skip-if-unchanged", false, "skip snapshot creation if identical to parent snapshot")
|
||||
|
||||
// parse read concurrency from env, on error the default value will be used
|
||||
|
|
@ -380,6 +384,14 @@ func collectRejectFuncs(opts BackupOptions, targets []string, fs fs.FS, warnf fu
|
|||
funcs = append(funcs, f)
|
||||
}
|
||||
|
||||
if opts.ExcludeNoDump {
|
||||
f, err := archiver.RejectByNoDump(warnf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
funcs = append(funcs, f)
|
||||
}
|
||||
|
||||
return funcs, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
19
internal/archiver/attributes_linux.go
Normal file
19
internal/archiver/attributes_linux.go
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
//go:build linux
|
||||
|
||||
package archiver
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// isNoDump returns whether the "no dump" Linux file attribute is set on path.
|
||||
// See CHATTR(1) for more information about Linux file attributes.
|
||||
func isNoDump(path string) (bool, error) {
|
||||
statx := &unix.Statx_t{}
|
||||
|
||||
if err := unix.Statx(0, path, unix.AT_NO_AUTOMOUNT|unix.AT_SYMLINK_NOFOLLOW, 0, statx); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return statx.Attributes&unix.STATX_ATTR_NODUMP != 0, nil
|
||||
}
|
||||
9
internal/archiver/attributes_other.go
Normal file
9
internal/archiver/attributes_other.go
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
//go:build !linux
|
||||
|
||||
package archiver
|
||||
|
||||
// isNoDump returns whether the "no dump" Linux file attribute is set on path.
|
||||
// See CHATTR(1) for more information about Linux file attributes.
|
||||
func isNoDump(path string) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
|
@ -334,3 +334,16 @@ func RejectCloudFiles(warnf func(msg string, args ...interface{})) (RejectFunc,
|
|||
return false
|
||||
}, nil
|
||||
}
|
||||
|
||||
// RejectByNoDump returns a func which on Linux rejects files with the "no dump"
|
||||
// Linux file attribute is set. On other OSes it rejects no files.
|
||||
func RejectByNoDump(warnf func(string, ...any)) (RejectFunc, error) {
|
||||
return func(item string, _ *fs.ExtendedFileInfo, _ fs.FS) bool {
|
||||
rv, err := isNoDump(item)
|
||||
if err != nil {
|
||||
warnf("item %v: error getting attributes: %v", item, err)
|
||||
}
|
||||
|
||||
return rv
|
||||
}, nil
|
||||
}
|
||||
|
|
|
|||
60
internal/archiver/exclude_linux_test.go
Normal file
60
internal/archiver/exclude_linux_test.go
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
//go:build linux
|
||||
|
||||
package archiver
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/restic/restic/internal/test"
|
||||
)
|
||||
|
||||
func TestRejectByNoDump(t *testing.T) {
|
||||
tempDir := test.TempDir(t)
|
||||
|
||||
items := []struct {
|
||||
path string
|
||||
dir bool
|
||||
noDump bool
|
||||
}{
|
||||
{"/no-dump", true, true},
|
||||
{"/normal", true, false},
|
||||
{"/normal/no-dump", false, true},
|
||||
{"/normal/normal", false, false},
|
||||
}
|
||||
|
||||
for _, item := range items {
|
||||
if item.dir {
|
||||
test.OK(t, os.Mkdir(tempDir+item.path, 0700))
|
||||
} else {
|
||||
test.OK(t, os.WriteFile(tempDir+item.path, nil, 0600))
|
||||
}
|
||||
|
||||
if item.noDump {
|
||||
test.OK(t, setNoDump(tempDir+item.path))
|
||||
}
|
||||
}
|
||||
|
||||
reject, err := RejectByNoDump(nil)
|
||||
test.OK(t, err)
|
||||
|
||||
for _, item := range items {
|
||||
rejected := reject(tempDir+item.path, nil, nil)
|
||||
if rejected != item.noDump {
|
||||
t.Errorf("inclusion status of %s is wrong: want %v, got %v", item.path, item.noDump, rejected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// setNoDump sets the "no dump" Linux file attribute on path (file or directory).
|
||||
func setNoDump(path string) error {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
return unix.IoctlSetPointerInt(int(f.Fd()), unix.FS_IOC_SETFLAGS, 0x40 /* FS_NODUMP_FL */)
|
||||
}
|
||||
Loading…
Reference in a new issue