Skip to content

Commit

Permalink
archiver: Shortcut for handling empty files
Browse files Browse the repository at this point in the history
Regular files that are empty according to stat are now not opened for
reading their contents. Such files are quite common (in my homedir, at
least) and we can save multiple system calls this way. On a network
filesystem, that can mean round trips. Also, we can back up empty files
that we cannot open for reading. Finally, fixes restic#4257.

Existing tests cover this case. fs.Reader now no longer has a meaningful
Size. Nothing depended on that.
  • Loading branch information
greatroar committed Mar 23, 2023
1 parent f646406 commit 50d8377
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 13 deletions.
20 changes: 20 additions & 0 deletions internal/archiver/archiver.go
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,26 @@ func (arch *Archiver) Save(ctx context.Context, snPath, target string, previous

switch {
case fs.IsRegularFile(fi):
if fi.Size() == 0 {
// Shortcut for empty files. Git uses lots of these, and
// some virtual filesystems (notably juicefs; #4257) present
// infinitely-sized special files as empty regular files.
// We can also save empty files without being able to open them.
debug.Log(" %v empty", target)

node, err := arch.nodeFromFileInfo(snPath, target, fi)
if err != nil {
return FutureNode{}, false, err
}
node.Content = restic.IDs{}
fn = newFutureNodeWithResult(futureNodeResult{
snPath: snPath,
target: target,
node: node,
})
return fn, false, nil
}

debug.Log(" %v regular file", target)

// check if the file has not changed before performing a fopen operation (more expensive, specially
Expand Down
7 changes: 2 additions & 5 deletions internal/fs/fs_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ type Reader struct {
// for FileInfo
Mode os.FileMode
ModTime time.Time
Size int64

AllowEmptyFile bool

Expand Down Expand Up @@ -65,7 +64,6 @@ func (fs *Reader) Open(name string) (f File, err error) {
func (fs *Reader) fi() os.FileInfo {
return fakeFileInfo{
name: fs.Name,
size: fs.Size,
mode: fs.Mode,
modtime: fs.ModTime,
}
Expand Down Expand Up @@ -107,7 +105,6 @@ func (fs *Reader) Lstat(name string) (os.FileInfo, error) {
getDirInfo := func(name string) os.FileInfo {
fi := fakeFileInfo{
name: fs.Base(name),
size: 0,
mode: os.ModeDir | 0755,
modtime: time.Now(),
}
Expand Down Expand Up @@ -292,7 +289,6 @@ func (d fakeDir) Readdir(n int) ([]os.FileInfo, error) {
// fakeFileInfo implements the bare minimum of os.FileInfo.
type fakeFileInfo struct {
name string
size int64
mode os.FileMode
modtime time.Time
}
Expand All @@ -302,7 +298,8 @@ func (fi fakeFileInfo) Name() string {
}

func (fi fakeFileInfo) Size() int64 {
return fi.size
// Fake size to fool the archiver's empty file check.
return -1
}

func (fi fakeFileInfo) Mode() os.FileMode {
Expand Down
8 changes: 0 additions & 8 deletions internal/fs/fs_reader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,10 +142,6 @@ func verifyDirectoryContentsFI(t testing.TB, fs FS, dir string, want []os.FileIn
t.Errorf("entry %d: wrong value for ModTime: want %v, got %v", i, fi1.ModTime(), fi2.ModTime())
}

if fi1.Size() != fi2.Size() {
t.Errorf("entry %d: wrong value for Size: want %v, got %v", i, fi1.Size(), fi2.Size())
}

if fi1.Sys() != fi2.Sys() {
t.Errorf("entry %d: wrong value for Sys: want %v, got %v", i, fi1.Sys(), fi2.Sys())
}
Expand Down Expand Up @@ -202,7 +198,6 @@ func TestFSReader(t *testing.T) {
mode: 0644,
modtime: now,
name: filename,
size: int64(len(data)),
}
verifyDirectoryContentsFI(t, fs, "/", []os.FileInfo{fi})
},
Expand All @@ -214,7 +209,6 @@ func TestFSReader(t *testing.T) {
mode: 0644,
modtime: now,
name: filename,
size: int64(len(data)),
}
verifyDirectoryContentsFI(t, fs, ".", []os.FileInfo{fi})
},
Expand Down Expand Up @@ -324,7 +318,6 @@ func TestFSReader(t *testing.T) {
ReadCloser: io.NopCloser(bytes.NewReader(data)),

Mode: 0644,
Size: int64(len(data)),
ModTime: now,
}

Expand Down Expand Up @@ -359,7 +352,6 @@ func TestFSReaderDir(t *testing.T) {
ReadCloser: io.NopCloser(bytes.NewReader(data)),

Mode: 0644,
Size: int64(len(data)),
ModTime: now,
}

Expand Down

0 comments on commit 50d8377

Please sign in to comment.