Files
kopia/fs/entry.go
Jarek Kowalski e03971fc59 Upgraded linter to v1.33.0 (#734)
* linter: upgraded to 1.33, disabled some linters

* lint: fixed 'errorlint' errors

This ensures that all error comparisons use errors.Is() or errors.As().
We will be wrapping more errors going forward so it's important that
error checks are not strict everywhere.

Verified that there are no exceptions for errorlint linter which
guarantees that.

* lint: fixed or suppressed wrapcheck errors

* lint: nolintlint and misc cleanups

Co-authored-by: Julio López <julio+gh@kasten.io>
2020-12-21 22:39:22 -08:00

193 lines
4.6 KiB
Go

package fs
import (
"context"
"io"
"os"
"sort"
"time"
"github.com/pkg/errors"
)
// Entry represents a filesystem entry, which can be Directory, File, or Symlink.
type Entry interface {
os.FileInfo
Owner() OwnerInfo
Device() DeviceInfo
LocalFilesystemPath() string // returns full local filesystem path or "" if not a local filesystem
}
// OwnerInfo describes owner of a filesystem entry.
type OwnerInfo struct {
UserID uint32
GroupID uint32
}
// DeviceInfo describes the device this filesystem entry is on.
type DeviceInfo struct {
Dev uint64
Rdev uint64
}
// Entries is a list of entries sorted by name.
type Entries []Entry
// Reader allows reading from a file and retrieving its up-to-date file info.
type Reader interface {
io.ReadCloser
io.Seeker
Entry() (Entry, error)
}
// File represents an entry that is a file.
type File interface {
Entry
Open(ctx context.Context) (Reader, error)
}
// Directory represents contents of a directory.
type Directory interface {
Entry
Child(ctx context.Context, name string) (Entry, error)
Readdir(ctx context.Context) (Entries, error)
}
// DirectoryWithSummary is optionally implemented by Directory that provide summary.
type DirectoryWithSummary interface {
Summary(ctx context.Context) (*DirectorySummary, error)
}
// ErrEntryNotFound is returned when an entry is not found.
var ErrEntryNotFound = errors.New("entry not found")
// ReadDirAndFindChild reads all entries from a directory and returns one by name.
// This is a convenience function that may be helpful in implementations of Directory.Child().
func ReadDirAndFindChild(ctx context.Context, d Directory, name string) (Entry, error) {
children, err := d.Readdir(ctx)
if err != nil {
return nil, errors.Wrap(err, "error reading directory")
}
e := children.FindByName(name)
if e == nil {
// nolint:wrapcheck
return nil, ErrEntryNotFound
}
return e, nil
}
// MaxFailedEntriesPerDirectorySummary is the maximum number of failed entries per directory summary.
const MaxFailedEntriesPerDirectorySummary = 10
// EntryWithError describes error encountered when processing an entry.
type EntryWithError struct {
EntryPath string `json:"path"`
Error string `json:"error"`
}
// DirectorySummary represents summary information about a directory.
type DirectorySummary struct {
TotalFileSize int64 `json:"size"`
TotalFileCount int64 `json:"files"`
TotalSymlinkCount int64 `json:"symlinks"`
TotalDirCount int64 `json:"dirs"`
MaxModTime time.Time `json:"maxTime"`
IncompleteReason string `json:"incomplete,omitempty"`
// number of failed files
NumFailed int `json:"numFailed"`
// first 10 failed entries
FailedEntries []*EntryWithError `json:"errors,omitempty"`
}
// Clone clones given directory summary.
func (s *DirectorySummary) Clone() DirectorySummary {
res := *s
res.FailedEntries = append([]*EntryWithError(nil), s.FailedEntries...)
return res
}
// Symlink represents a symbolic link entry.
type Symlink interface {
Entry
Readlink(ctx context.Context) (string, error)
}
// FindByName returns an entry with a given name, or nil if not found.
func (e Entries) FindByName(n string) Entry {
i := sort.Search(
len(e),
func(i int) bool {
return e[i].Name() >= n
},
)
if i < len(e) && e[i].Name() == n {
return e[i]
}
return nil
}
// Update returns a copy of Entries with the provided entry included, by either replacing
// existing entry with the same name or inserted in the appropriate place to maintain sorted order.
func (e Entries) Update(newEntry Entry) Entries {
name := newEntry.Name()
pos := sort.Search(len(e), func(i int) bool {
return e[i].Name() >= name
})
// append at the end
if pos >= len(e) {
return append(append(Entries(nil), e...), newEntry)
}
if e[pos].Name() == name {
if pos > 0 {
return append(append(append(Entries(nil), e[0:pos]...), newEntry), e[pos+1:]...)
}
return append(append(Entries(nil), newEntry), e[pos+1:]...)
}
if pos > 0 {
return append(append(append(Entries(nil), e[0:pos]...), newEntry), e[pos:]...)
}
return append(append(Entries(nil), newEntry), e[pos:]...)
}
// Remove returns a copy of Entries with the provided entry removed, while maintaining sorted order.
func (e Entries) Remove(name string) Entries {
pos := sort.Search(len(e), func(i int) bool {
return e[i].Name() >= name
})
// not found
if pos >= len(e) {
return e
}
if e[pos].Name() != name {
return e
}
if pos > 0 {
return append(append(Entries(nil), e[0:pos]...), e[pos+1:]...)
}
return append(Entries(nil), e[pos+1:]...)
}
// Sort sorts the entries by name.
func (e Entries) Sort() {
sort.Slice(e, func(i, j int) bool {
return e[i].Name() < e[j].Name()
})
}