From db8f5864fcd464e6d3471c3cb8b45b68f5474e1d Mon Sep 17 00:00:00 2001 From: Heiko Bornholdt Date: Sun, 10 Mar 2019 21:22:54 +0100 Subject: [PATCH 1/2] Add --ignore-inode option to backup cmd revised version of https://github.com/restic/restic/pull/2047 --- cmd/restic/cmd_backup.go | 3 +++ doc/040_backup.rst | 4 ++++ internal/archiver/archiver.go | 8 ++++--- internal/archiver/archiver_test.go | 34 +++++++++++++++++++++++------- 4 files changed, 38 insertions(+), 11 deletions(-) diff --git a/cmd/restic/cmd_backup.go b/cmd/restic/cmd_backup.go index 13b2d8def..3f945bc71 100644 --- a/cmd/restic/cmd_backup.go +++ b/cmd/restic/cmd_backup.go @@ -85,6 +85,7 @@ type BackupOptions struct { FilesFrom []string TimeStamp string WithAtime bool + IgnoreInode bool } var backupOptions BackupOptions @@ -112,6 +113,7 @@ func init() { f.StringArrayVar(&backupOptions.FilesFrom, "files-from", nil, "read the files to backup from file (can be combined with file args/can be specified multiple times)") f.StringVar(&backupOptions.TimeStamp, "time", "", "time of the backup (ex. '2012-11-01 22:08:41') (default: now)") f.BoolVar(&backupOptions.WithAtime, "with-atime", false, "store the atime for all files and directories") + f.BoolVar(&backupOptions.IgnoreInode, "ignore-inode", false, "ignore inode number changes when checking for modified files") } // filterExisting returns a slice of all existing items, or an error if no @@ -549,6 +551,7 @@ func runBackup(opts BackupOptions, gopts GlobalOptions, term *termstatus.Termina arch.CompleteItem = p.CompleteItem arch.StartFile = p.StartFile arch.CompleteBlob = p.CompleteBlob + arch.IgnoreInode = opts.IgnoreInode if parentSnapshotID == nil { parentSnapshotID = &restic.ID{} diff --git a/doc/040_backup.rst b/doc/040_backup.rst index 3513e5854..32ce21260 100644 --- a/doc/040_backup.rst +++ b/doc/040_backup.rst @@ -279,6 +279,10 @@ written, and the next backup needs to write new metadata again. If you really want to save the access time for files and directories, you can pass the ``--with-atime`` option to the ``backup`` command. +In filesystems that do not support inode consistency, like FUSE-based ones and pCloud, it is +possible to ignore inode on changed files comparison by passing ``--ignore-inode`` to +``backup`` command. + Reading data from stdin *********************** diff --git a/internal/archiver/archiver.go b/internal/archiver/archiver.go index 4ce9ef597..d93ea3986 100644 --- a/internal/archiver/archiver.go +++ b/internal/archiver/archiver.go @@ -79,6 +79,7 @@ type Archiver struct { // be saved. Enabling it may result in much metadata, so it's off by // default. WithAtime bool + IgnoreInode bool } // Options is used to configure the archiver. @@ -133,6 +134,7 @@ func New(repo restic.Repository, fs fs.FS, opts Options) *Archiver { CompleteItem: func(string, *restic.Node, *restic.Node, ItemStats, time.Duration) {}, StartFile: func(string) {}, CompleteBlob: func(string, uint64) {}, + IgnoreInode: false, } return arch @@ -383,7 +385,7 @@ func (arch *Archiver) Save(ctx context.Context, snPath, target string, previous } // use previous node if the file hasn't changed - if previous != nil && !fileChanged(fi, previous) { + if previous != nil && !fileChanged(fi, previous, arch.IgnoreInode) { debug.Log("%v hasn't changed, returning old node", target) arch.CompleteItem(snPath, previous, previous, ItemStats{}, time.Since(start)) arch.CompleteBlob(snPath, previous.Size) @@ -436,7 +438,7 @@ func (arch *Archiver) Save(ctx context.Context, snPath, target string, previous // fileChanged returns true if the file's content has changed since the node // was created. -func fileChanged(fi os.FileInfo, node *restic.Node) bool { +func fileChanged(fi os.FileInfo, node *restic.Node, ignoreInode bool) bool { if node == nil { return true } @@ -458,7 +460,7 @@ func fileChanged(fi os.FileInfo, node *restic.Node) bool { } // check inode - if node.Inode != extFI.Inode { + if !ignoreInode && node.Inode != extFI.Inode { return true } diff --git a/internal/archiver/archiver_test.go b/internal/archiver/archiver_test.go index 8749dcc81..51c499c0c 100644 --- a/internal/archiver/archiver_test.go +++ b/internal/archiver/archiver_test.go @@ -555,9 +555,11 @@ func TestFileChanged(t *testing.T) { } var tests = []struct { - Name string - Content []byte - Modify func(t testing.TB, filename string) + Name string + Content []byte + Modify func(t testing.TB, filename string) + IgnoreInode bool + Check bool }{ { Name: "same-content-new-file", @@ -596,6 +598,18 @@ func TestFileChanged(t *testing.T) { save(t, filename, defaultContent) }, }, + { + Name: "ignore-inode", + Modify: func(t testing.TB, filename string) { + fi := lstat(t, filename) + remove(t, filename) + sleep() + save(t, filename, defaultContent) + setTimestamp(t, filename, fi.ModTime(), fi.ModTime()) + }, + IgnoreInode: true, + Check: true, + }, } for _, test := range tests { @@ -613,15 +627,19 @@ func TestFileChanged(t *testing.T) { fiBefore := lstat(t, filename) node := nodeFromFI(t, filename, fiBefore) - if fileChanged(fiBefore, node) { + if fileChanged(fiBefore, node, false) { t.Fatalf("unchanged file detected as changed") } test.Modify(t, filename) fiAfter := lstat(t, filename) - if !fileChanged(fiAfter, node) { - t.Fatalf("modified file detected as unchanged") + if test.Check == fileChanged(fiAfter, node, test.IgnoreInode) { + if test.Check { + t.Fatalf("unmodified file detected as changed") + } else { + t.Fatalf("modified file detected as unchanged") + } } }) } @@ -637,7 +655,7 @@ func TestFilChangedSpecialCases(t *testing.T) { t.Run("nil-node", func(t *testing.T) { fi := lstat(t, filename) - if !fileChanged(fi, nil) { + if !fileChanged(fi, nil, false) { t.Fatal("nil node detected as unchanged") } }) @@ -646,7 +664,7 @@ func TestFilChangedSpecialCases(t *testing.T) { fi := lstat(t, filename) node := nodeFromFI(t, filename, fi) node.Type = "symlink" - if !fileChanged(fi, node) { + if !fileChanged(fi, node, false) { t.Fatal("node with changed type detected as unchanged") } }) From d138b38f280e2bd8015c0e2d313d42eac5bc35ac Mon Sep 17 00:00:00 2001 From: Heiko Bornholdt Date: Sun, 10 Mar 2019 21:37:01 +0100 Subject: [PATCH 2/2] Add pull-2205 to changelog --- changelog/unreleased/pull-2205 | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 changelog/unreleased/pull-2205 diff --git a/changelog/unreleased/pull-2205 b/changelog/unreleased/pull-2205 new file mode 100644 index 000000000..67c6b5571 --- /dev/null +++ b/changelog/unreleased/pull-2205 @@ -0,0 +1,10 @@ +Enhancement: Add --ignore-inode option to backup cmd + +This option handles backup of virtual filesystems that do not keep fixed +inodes for files, like Fuse-based, pCloud, etc. Ignoring inode changes allows +to consider the file as unchanged if last modification date and size +are unchanged. + +https://github.com/restic/restic/pull/2205 +https://github.com/restic/restic/pull/2047 +https://github.com/restic/restic/issues/1631 \ No newline at end of file