2
2
mirror of https://github.com/octoleo/restic.git synced 2024-11-10 23:31:09 +00:00

backend/local: replace fs.Walk with custom walker

This code is more strict in what it expects to find in the backend:
depending on the layout, either a directory full of files or a directory
full of such directories.
This commit is contained in:
greatroar 2020-11-19 16:46:42 +01:00
parent a2d4209322
commit 8e213e82fc

View File

@ -222,45 +222,15 @@ func (b *Local) Remove(ctx context.Context, h restic.Handle) error {
// List runs fn for each file in the backend which has the type t. When an // List runs fn for each file in the backend which has the type t. When an
// error occurs (or fn returns an error), List stops and returns it. // error occurs (or fn returns an error), List stops and returns it.
func (b *Local) List(ctx context.Context, t restic.FileType, fn func(restic.FileInfo) error) error { func (b *Local) List(ctx context.Context, t restic.FileType, fn func(restic.FileInfo) error) (err error) {
debug.Log("List %v", t) debug.Log("List %v", t)
basedir, subdirs := b.Basedir(t) basedir, subdirs := b.Basedir(t)
atBasedir := true if subdirs {
err := fs.Walk(basedir, func(path string, fi os.FileInfo, err error) error { err = visitDirs(ctx, basedir, fn)
debug.Log("walk on %v\n", path) } else {
if err != nil { err = visitFiles(ctx, basedir, fn)
return err }
}
switch {
case atBasedir: // Skip basedir itself.
atBasedir = false
return nil
case fi.IsDir() && !subdirs:
return filepath.SkipDir
case !fi.Mode().IsRegular():
return nil
}
debug.Log("send %v\n", filepath.Base(path))
rfi := restic.FileInfo{
Name: filepath.Base(path),
Size: fi.Size(),
}
if ctx.Err() != nil {
return ctx.Err()
}
err = fn(rfi)
if err != nil {
return err
}
return ctx.Err()
})
if b.IsNotExist(err) { if b.IsNotExist(err) {
debug.Log("ignoring non-existing directory") debug.Log("ignoring non-existing directory")
@ -270,6 +240,61 @@ func (b *Local) List(ctx context.Context, t restic.FileType, fn func(restic.File
return err return err
} }
// The following two functions are like filepath.Walk, but visit only one or
// two levels of directory structure (including dir itself as the first level).
// Also, visitDirs assumes it sees a directory full of directories, while
// visitFiles wants a directory full or regular files.
func visitDirs(ctx context.Context, dir string, fn func(restic.FileInfo) error) error {
d, err := fs.Open(dir)
if err != nil {
return err
}
defer d.Close()
sub, err := d.Readdirnames(-1)
if err != nil {
return err
}
for _, f := range sub {
err = visitFiles(ctx, filepath.Join(dir, f), fn)
if err != nil {
return err
}
}
return ctx.Err()
}
func visitFiles(ctx context.Context, dir string, fn func(restic.FileInfo) error) error {
d, err := fs.Open(dir)
if err != nil {
return err
}
defer d.Close()
sub, err := d.Readdir(-1)
if err != nil {
return err
}
for _, fi := range sub {
select {
case <-ctx.Done():
return ctx.Err()
default:
}
err := fn(restic.FileInfo{
Name: fi.Name(),
Size: fi.Size(),
})
if err != nil {
return err
}
}
return nil
}
// Delete removes the repository and all files. // Delete removes the repository and all files.
func (b *Local) Delete(ctx context.Context) error { func (b *Local) Delete(ctx context.Context) error {
debug.Log("Delete()") debug.Log("Delete()")