forked from TrueCloudLab/restic
add proper constants for node type
This commit is contained in:
parent
3b438e5c7c
commit
ca1e5e10b6
42 changed files with 206 additions and 206 deletions
|
@ -108,9 +108,9 @@ func (s *DiffStat) Add(node *restic.Node) {
|
|||
}
|
||||
|
||||
switch node.Type {
|
||||
case "file":
|
||||
case restic.NodeTypeFile:
|
||||
s.Files++
|
||||
case "dir":
|
||||
case restic.NodeTypeDir:
|
||||
s.Dirs++
|
||||
default:
|
||||
s.Others++
|
||||
|
@ -124,7 +124,7 @@ func addBlobs(bs restic.BlobSet, node *restic.Node) {
|
|||
}
|
||||
|
||||
switch node.Type {
|
||||
case "file":
|
||||
case restic.NodeTypeFile:
|
||||
for _, blob := range node.Content {
|
||||
h := restic.BlobHandle{
|
||||
ID: blob,
|
||||
|
@ -132,7 +132,7 @@ func addBlobs(bs restic.BlobSet, node *restic.Node) {
|
|||
}
|
||||
bs.Insert(h)
|
||||
}
|
||||
case "dir":
|
||||
case restic.NodeTypeDir:
|
||||
h := restic.BlobHandle{
|
||||
ID: *node.Subtree,
|
||||
Type: restic.TreeBlob,
|
||||
|
@ -184,14 +184,14 @@ func (c *Comparer) printDir(ctx context.Context, mode string, stats *DiffStat, b
|
|||
}
|
||||
|
||||
name := path.Join(prefix, node.Name)
|
||||
if node.Type == "dir" {
|
||||
if node.Type == restic.NodeTypeDir {
|
||||
name += "/"
|
||||
}
|
||||
c.printChange(NewChange(name, mode))
|
||||
stats.Add(node)
|
||||
addBlobs(blobs, node)
|
||||
|
||||
if node.Type == "dir" {
|
||||
if node.Type == restic.NodeTypeDir {
|
||||
err := c.printDir(ctx, mode, stats, blobs, name, *node.Subtree)
|
||||
if err != nil && err != context.Canceled {
|
||||
Warnf("error: %v\n", err)
|
||||
|
@ -216,7 +216,7 @@ func (c *Comparer) collectDir(ctx context.Context, blobs restic.BlobSet, id rest
|
|||
|
||||
addBlobs(blobs, node)
|
||||
|
||||
if node.Type == "dir" {
|
||||
if node.Type == restic.NodeTypeDir {
|
||||
err := c.collectDir(ctx, blobs, *node.Subtree)
|
||||
if err != nil && err != context.Canceled {
|
||||
Warnf("error: %v\n", err)
|
||||
|
@ -284,12 +284,12 @@ func (c *Comparer) diffTree(ctx context.Context, stats *DiffStatsContainer, pref
|
|||
mod += "T"
|
||||
}
|
||||
|
||||
if node2.Type == "dir" {
|
||||
if node2.Type == restic.NodeTypeDir {
|
||||
name += "/"
|
||||
}
|
||||
|
||||
if node1.Type == "file" &&
|
||||
node2.Type == "file" &&
|
||||
if node1.Type == restic.NodeTypeFile &&
|
||||
node2.Type == restic.NodeTypeFile &&
|
||||
!reflect.DeepEqual(node1.Content, node2.Content) {
|
||||
mod += "M"
|
||||
stats.ChangedFiles++
|
||||
|
@ -311,7 +311,7 @@ func (c *Comparer) diffTree(ctx context.Context, stats *DiffStatsContainer, pref
|
|||
c.printChange(NewChange(name, mod))
|
||||
}
|
||||
|
||||
if node1.Type == "dir" && node2.Type == "dir" {
|
||||
if node1.Type == restic.NodeTypeDir && node2.Type == restic.NodeTypeDir {
|
||||
var err error
|
||||
if (*node1.Subtree).Equal(*node2.Subtree) {
|
||||
err = c.collectDir(ctx, stats.BlobsCommon, *node1.Subtree)
|
||||
|
@ -324,13 +324,13 @@ func (c *Comparer) diffTree(ctx context.Context, stats *DiffStatsContainer, pref
|
|||
}
|
||||
case t1 && !t2:
|
||||
prefix := path.Join(prefix, name)
|
||||
if node1.Type == "dir" {
|
||||
if node1.Type == restic.NodeTypeDir {
|
||||
prefix += "/"
|
||||
}
|
||||
c.printChange(NewChange(prefix, "-"))
|
||||
stats.Removed.Add(node1)
|
||||
|
||||
if node1.Type == "dir" {
|
||||
if node1.Type == restic.NodeTypeDir {
|
||||
err := c.printDir(ctx, "-", &stats.Removed, stats.BlobsBefore, prefix, *node1.Subtree)
|
||||
if err != nil && err != context.Canceled {
|
||||
Warnf("error: %v\n", err)
|
||||
|
@ -338,13 +338,13 @@ func (c *Comparer) diffTree(ctx context.Context, stats *DiffStatsContainer, pref
|
|||
}
|
||||
case !t1 && t2:
|
||||
prefix := path.Join(prefix, name)
|
||||
if node2.Type == "dir" {
|
||||
if node2.Type == restic.NodeTypeDir {
|
||||
prefix += "/"
|
||||
}
|
||||
c.printChange(NewChange(prefix, "+"))
|
||||
stats.Added.Add(node2)
|
||||
|
||||
if node2.Type == "dir" {
|
||||
if node2.Type == restic.NodeTypeDir {
|
||||
err := c.printDir(ctx, "+", &stats.Added, stats.BlobsAfter, prefix, *node2.Subtree)
|
||||
if err != nil && err != context.Canceled {
|
||||
Warnf("error: %v\n", err)
|
||||
|
|
|
@ -95,15 +95,15 @@ func printFromTree(ctx context.Context, tree *restic.Tree, repo restic.BlobLoade
|
|||
// first item it finds and dump that according to the switch case below.
|
||||
if node.Name == pathComponents[0] {
|
||||
switch {
|
||||
case l == 1 && dump.IsFile(node):
|
||||
case l == 1 && node.Type == restic.NodeTypeFile:
|
||||
return d.WriteNode(ctx, node)
|
||||
case l > 1 && dump.IsDir(node):
|
||||
case l > 1 && node.Type == restic.NodeTypeDir:
|
||||
subtree, err := restic.LoadTree(ctx, repo, *node.Subtree)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "cannot load subtree for %q", item)
|
||||
}
|
||||
return printFromTree(ctx, subtree, repo, item, pathComponents[1:], d, canWriteArchiveFunc)
|
||||
case dump.IsDir(node):
|
||||
case node.Type == restic.NodeTypeDir:
|
||||
if err := canWriteArchiveFunc(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -114,7 +114,7 @@ func printFromTree(ctx context.Context, tree *restic.Tree, repo restic.BlobLoade
|
|||
return d.DumpTree(ctx, subtree, item)
|
||||
case l > 1:
|
||||
return fmt.Errorf("%q should be a dir, but is a %q", item, node.Type)
|
||||
case !dump.IsFile(node):
|
||||
case node.Type != restic.NodeTypeFile:
|
||||
return fmt.Errorf("%q should be a file, but is a %q", item, node.Type)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -298,7 +298,7 @@ func (f *Finder) findInSnapshot(ctx context.Context, sn *restic.Snapshot) error
|
|||
}
|
||||
|
||||
var errIfNoMatch error
|
||||
if node.Type == "dir" {
|
||||
if node.Type == restic.NodeTypeDir {
|
||||
var childMayMatch bool
|
||||
for _, pat := range f.pat.pattern {
|
||||
mayMatch, err := filter.ChildMatch(pat, normalizedNodepath)
|
||||
|
@ -357,7 +357,7 @@ func (f *Finder) findIDs(ctx context.Context, sn *restic.Snapshot) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
if node.Type == "dir" && f.treeIDs != nil {
|
||||
if node.Type == restic.NodeTypeDir && f.treeIDs != nil {
|
||||
treeID := node.Subtree
|
||||
found := false
|
||||
if _, ok := f.treeIDs[treeID.Str()]; ok {
|
||||
|
@ -377,7 +377,7 @@ func (f *Finder) findIDs(ctx context.Context, sn *restic.Snapshot) error {
|
|||
}
|
||||
}
|
||||
|
||||
if node.Type == "file" && f.blobIDs != nil {
|
||||
if node.Type == restic.NodeTypeFile && f.blobIDs != nil {
|
||||
for _, id := range node.Content {
|
||||
if ctx.Err() != nil {
|
||||
return ctx.Err()
|
||||
|
|
|
@ -137,7 +137,7 @@ func lsNodeJSON(enc *json.Encoder, path string, node *restic.Node) error {
|
|||
size uint64 // Target for Size pointer.
|
||||
}{
|
||||
Name: node.Name,
|
||||
Type: node.Type,
|
||||
Type: string(node.Type),
|
||||
Path: path,
|
||||
UID: node.UID,
|
||||
GID: node.GID,
|
||||
|
@ -153,7 +153,7 @@ func lsNodeJSON(enc *json.Encoder, path string, node *restic.Node) error {
|
|||
}
|
||||
// Always print size for regular files, even when empty,
|
||||
// but never for other types.
|
||||
if node.Type == "file" {
|
||||
if node.Type == restic.NodeTypeFile {
|
||||
n.Size = &n.size
|
||||
}
|
||||
|
||||
|
@ -208,7 +208,7 @@ func lsNcduNode(_ string, node *restic.Node) ([]byte, error) {
|
|||
Dev: node.DeviceID,
|
||||
Ino: node.Inode,
|
||||
NLink: node.Links,
|
||||
NotReg: node.Type != "dir" && node.Type != "file",
|
||||
NotReg: node.Type != restic.NodeTypeDir && node.Type != restic.NodeTypeFile,
|
||||
UID: node.UID,
|
||||
GID: node.GID,
|
||||
Mode: uint16(node.Mode & os.ModePerm),
|
||||
|
@ -238,7 +238,7 @@ func (p *ncduLsPrinter) Node(path string, node *restic.Node, _ bool) {
|
|||
Warnf("JSON encode failed: %v\n", err)
|
||||
}
|
||||
|
||||
if node.Type == "dir" {
|
||||
if node.Type == restic.NodeTypeDir {
|
||||
fmt.Fprintf(p.out, ",\n%s[\n%s%s", strings.Repeat(" ", p.depth), strings.Repeat(" ", p.depth+1), string(out))
|
||||
p.depth++
|
||||
} else {
|
||||
|
@ -409,7 +409,7 @@ func runLs(ctx context.Context, opts LsOptions, gopts GlobalOptions, args []stri
|
|||
|
||||
// otherwise, signal the walker to not walk recursively into any
|
||||
// subdirs
|
||||
if node.Type == "dir" {
|
||||
if node.Type == restic.NodeTypeDir {
|
||||
// immediately generate leaveDir if the directory is skipped
|
||||
if printedDir {
|
||||
printer.LeaveDir(nodepath)
|
||||
|
|
|
@ -23,7 +23,7 @@ var lsTestNodes = []lsTestNode{
|
|||
path: "/bar/baz",
|
||||
Node: restic.Node{
|
||||
Name: "baz",
|
||||
Type: "file",
|
||||
Type: restic.NodeTypeFile,
|
||||
Size: 12345,
|
||||
UID: 10000000,
|
||||
GID: 20000000,
|
||||
|
@ -39,7 +39,7 @@ var lsTestNodes = []lsTestNode{
|
|||
path: "/foo/empty",
|
||||
Node: restic.Node{
|
||||
Name: "empty",
|
||||
Type: "file",
|
||||
Type: restic.NodeTypeFile,
|
||||
Size: 0,
|
||||
UID: 1001,
|
||||
GID: 1001,
|
||||
|
@ -56,7 +56,7 @@ var lsTestNodes = []lsTestNode{
|
|||
path: "/foo/link",
|
||||
Node: restic.Node{
|
||||
Name: "link",
|
||||
Type: "symlink",
|
||||
Type: restic.NodeTypeSymlink,
|
||||
Mode: os.ModeSymlink | 0777,
|
||||
LinkTarget: "not printed",
|
||||
},
|
||||
|
@ -66,7 +66,7 @@ var lsTestNodes = []lsTestNode{
|
|||
path: "/some/directory",
|
||||
Node: restic.Node{
|
||||
Name: "directory",
|
||||
Type: "dir",
|
||||
Type: restic.NodeTypeDir,
|
||||
Mode: os.ModeDir | 0755,
|
||||
ModTime: time.Date(2020, 1, 2, 3, 4, 5, 0, time.UTC),
|
||||
AccessTime: time.Date(2021, 2, 3, 4, 5, 6, 7, time.UTC),
|
||||
|
@ -79,7 +79,7 @@ var lsTestNodes = []lsTestNode{
|
|||
path: "/some/sticky",
|
||||
Node: restic.Node{
|
||||
Name: "sticky",
|
||||
Type: "dir",
|
||||
Type: restic.NodeTypeDir,
|
||||
Mode: os.ModeDir | 0755 | os.ModeSetuid | os.ModeSetgid | os.ModeSticky,
|
||||
},
|
||||
},
|
||||
|
@ -139,19 +139,19 @@ func TestLsNcdu(t *testing.T) {
|
|||
Paths: []string{"/example"},
|
||||
})
|
||||
printer.Node("/directory", &restic.Node{
|
||||
Type: "dir",
|
||||
Type: restic.NodeTypeDir,
|
||||
Name: "directory",
|
||||
ModTime: modTime,
|
||||
}, false)
|
||||
printer.Node("/directory/data", &restic.Node{
|
||||
Type: "file",
|
||||
Type: restic.NodeTypeFile,
|
||||
Name: "data",
|
||||
Size: 42,
|
||||
ModTime: modTime,
|
||||
}, false)
|
||||
printer.LeaveDir("/directory")
|
||||
printer.Node("/file", &restic.Node{
|
||||
Type: "file",
|
||||
Type: restic.NodeTypeFile,
|
||||
Name: "file",
|
||||
Size: 12345,
|
||||
ModTime: modTime,
|
||||
|
|
|
@ -88,7 +88,7 @@ func runRecover(ctx context.Context, gopts GlobalOptions) error {
|
|||
}
|
||||
|
||||
for _, node := range tree.Nodes {
|
||||
if node.Type == "dir" && node.Subtree != nil {
|
||||
if node.Type == restic.NodeTypeDir && node.Subtree != nil {
|
||||
trees[*node.Subtree] = true
|
||||
}
|
||||
}
|
||||
|
@ -128,7 +128,7 @@ func runRecover(ctx context.Context, gopts GlobalOptions) error {
|
|||
for id := range roots {
|
||||
var subtreeID = id
|
||||
node := restic.Node{
|
||||
Type: "dir",
|
||||
Type: restic.NodeTypeDir,
|
||||
Name: id.Str(),
|
||||
Mode: 0755,
|
||||
Subtree: &subtreeID,
|
||||
|
|
|
@ -92,7 +92,7 @@ func runRepairSnapshots(ctx context.Context, gopts GlobalOptions, opts RepairOpt
|
|||
// - files whose contents are not fully available (-> file will be modified)
|
||||
rewriter := walker.NewTreeRewriter(walker.RewriteOpts{
|
||||
RewriteNode: func(node *restic.Node, path string) *restic.Node {
|
||||
if node.Type != "file" {
|
||||
if node.Type != restic.NodeTypeFile {
|
||||
return node
|
||||
}
|
||||
|
||||
|
|
|
@ -276,7 +276,7 @@ func statsWalkTree(repo restic.Loader, opts StatsOptions, stats *statsContainer,
|
|||
// will still be restored
|
||||
stats.TotalFileCount++
|
||||
|
||||
if node.Links == 1 || node.Type == "dir" {
|
||||
if node.Links == 1 || node.Type == restic.NodeTypeDir {
|
||||
stats.TotalSize += node.Size
|
||||
} else {
|
||||
// if hardlinks are present only count each deviceID+inode once
|
||||
|
|
|
@ -24,20 +24,20 @@ func formatNode(path string, n *restic.Node, long bool, human bool) string {
|
|||
}
|
||||
|
||||
switch n.Type {
|
||||
case "file":
|
||||
case restic.NodeTypeFile:
|
||||
mode = 0
|
||||
case "dir":
|
||||
case restic.NodeTypeDir:
|
||||
mode = os.ModeDir
|
||||
case "symlink":
|
||||
case restic.NodeTypeSymlink:
|
||||
mode = os.ModeSymlink
|
||||
target = fmt.Sprintf(" -> %v", n.LinkTarget)
|
||||
case "dev":
|
||||
case restic.NodeTypeDev:
|
||||
mode = os.ModeDevice
|
||||
case "chardev":
|
||||
case restic.NodeTypeCharDev:
|
||||
mode = os.ModeDevice | os.ModeCharDevice
|
||||
case "fifo":
|
||||
case restic.NodeTypeFifo:
|
||||
mode = os.ModeNamedPipe
|
||||
case "socket":
|
||||
case restic.NodeTypeSocket:
|
||||
mode = os.ModeSocket
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ func TestFormatNode(t *testing.T) {
|
|||
testPath := "/test/path"
|
||||
node := restic.Node{
|
||||
Name: "baz",
|
||||
Type: "file",
|
||||
Type: restic.NodeTypeFile,
|
||||
Size: 14680064,
|
||||
UID: 1000,
|
||||
GID: 2000,
|
||||
|
|
|
@ -232,7 +232,7 @@ func (arch *Archiver) trackItem(item string, previous, current *restic.Node, s I
|
|||
}
|
||||
|
||||
switch current.Type {
|
||||
case "dir":
|
||||
case restic.NodeTypeDir:
|
||||
switch {
|
||||
case previous == nil:
|
||||
arch.summary.Dirs.New++
|
||||
|
@ -242,7 +242,7 @@ func (arch *Archiver) trackItem(item string, previous, current *restic.Node, s I
|
|||
arch.summary.Dirs.Changed++
|
||||
}
|
||||
|
||||
case "file":
|
||||
case restic.NodeTypeFile:
|
||||
switch {
|
||||
case previous == nil:
|
||||
arch.summary.Files.New++
|
||||
|
@ -261,7 +261,7 @@ func (arch *Archiver) nodeFromFileInfo(snPath, filename string, fi os.FileInfo,
|
|||
node.AccessTime = node.ModTime
|
||||
}
|
||||
if feature.Flag.Enabled(feature.DeviceIDForHardlinks) {
|
||||
if node.Links == 1 || node.Type == "dir" {
|
||||
if node.Links == 1 || node.Type == restic.NodeTypeDir {
|
||||
// the DeviceID is only necessary for hardlinked files
|
||||
// when using subvolumes or snapshots their deviceIDs tend to change which causes
|
||||
// restic to upload new tree blobs
|
||||
|
@ -280,7 +280,7 @@ func (arch *Archiver) nodeFromFileInfo(snPath, filename string, fi os.FileInfo,
|
|||
// loadSubtree tries to load the subtree referenced by node. In case of an error, nil is returned.
|
||||
// If there is no node to load, then nil is returned without an error.
|
||||
func (arch *Archiver) loadSubtree(ctx context.Context, node *restic.Node) (*restic.Tree, error) {
|
||||
if node == nil || node.Type != "dir" || node.Subtree == nil {
|
||||
if node == nil || node.Type != restic.NodeTypeDir || node.Subtree == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
@ -583,7 +583,7 @@ func fileChanged(fs fs.FS, fi os.FileInfo, node *restic.Node, ignoreFlags uint)
|
|||
switch {
|
||||
case node == nil:
|
||||
return true
|
||||
case node.Type != "file":
|
||||
case node.Type != restic.NodeTypeFile:
|
||||
// We're only called for regular files, so this is a type change.
|
||||
return true
|
||||
case uint64(fi.Size()) != node.Size:
|
||||
|
|
|
@ -730,7 +730,7 @@ func TestFilChangedSpecialCases(t *testing.T) {
|
|||
t.Run("type-change", func(t *testing.T) {
|
||||
fi := lstat(t, filename)
|
||||
node := nodeFromFI(t, filename, fi)
|
||||
node.Type = "symlink"
|
||||
node.Type = "restic.NodeTypeSymlink"
|
||||
if !fileChanged(&fs.Local{}, fi, node, 0) {
|
||||
t.Fatal("node with changed type detected as unchanged")
|
||||
}
|
||||
|
|
|
@ -163,7 +163,7 @@ func (s *fileSaver) saveFile(ctx context.Context, chnker *chunker.Chunker, snPat
|
|||
return
|
||||
}
|
||||
|
||||
if node.Type != "file" {
|
||||
if node.Type != restic.NodeTypeFile {
|
||||
_ = f.Close()
|
||||
completeError(errors.Errorf("node type %q is wrong", node.Type))
|
||||
return
|
||||
|
|
|
@ -289,7 +289,7 @@ func TestEnsureTree(ctx context.Context, t testing.TB, prefix string, repo resti
|
|||
|
||||
switch e := entry.(type) {
|
||||
case TestDir:
|
||||
if node.Type != "dir" {
|
||||
if node.Type != restic.NodeTypeDir {
|
||||
t.Errorf("tree node %v has wrong type %q, want %q", nodePrefix, node.Type, "dir")
|
||||
return
|
||||
}
|
||||
|
@ -301,13 +301,13 @@ func TestEnsureTree(ctx context.Context, t testing.TB, prefix string, repo resti
|
|||
|
||||
TestEnsureTree(ctx, t, path.Join(prefix, node.Name), repo, *node.Subtree, e)
|
||||
case TestFile:
|
||||
if node.Type != "file" {
|
||||
if node.Type != restic.NodeTypeFile {
|
||||
t.Errorf("tree node %v has wrong type %q, want %q", nodePrefix, node.Type, "file")
|
||||
}
|
||||
TestEnsureFileContent(ctx, t, repo, nodePrefix, node, e)
|
||||
case TestSymlink:
|
||||
if node.Type != "symlink" {
|
||||
t.Errorf("tree node %v has wrong type %q, want %q", nodePrefix, node.Type, "file")
|
||||
if node.Type != restic.NodeTypeSymlink {
|
||||
t.Errorf("tree node %v has wrong type %q, want %q", nodePrefix, node.Type, "symlink")
|
||||
}
|
||||
|
||||
if e.Target != node.LinkTarget {
|
||||
|
|
|
@ -344,7 +344,7 @@ func (c *Checker) checkTree(id restic.ID, tree *restic.Tree) (errs []error) {
|
|||
|
||||
for _, node := range tree.Nodes {
|
||||
switch node.Type {
|
||||
case "file":
|
||||
case restic.NodeTypeFile:
|
||||
if node.Content == nil {
|
||||
errs = append(errs, &Error{TreeID: id, Err: errors.Errorf("file %q has nil blob list", node.Name)})
|
||||
}
|
||||
|
@ -380,7 +380,7 @@ func (c *Checker) checkTree(id restic.ID, tree *restic.Tree) (errs []error) {
|
|||
c.blobRefs.Unlock()
|
||||
}
|
||||
|
||||
case "dir":
|
||||
case restic.NodeTypeDir:
|
||||
if node.Subtree == nil {
|
||||
errs = append(errs, &Error{TreeID: id, Err: errors.Errorf("dir node %q has no subtree", node.Name)})
|
||||
continue
|
||||
|
@ -391,7 +391,7 @@ func (c *Checker) checkTree(id restic.ID, tree *restic.Tree) (errs []error) {
|
|||
continue
|
||||
}
|
||||
|
||||
case "symlink", "socket", "chardev", "dev", "fifo":
|
||||
case restic.NodeTypeSymlink, restic.NodeTypeSocket, restic.NodeTypeCharDev, restic.NodeTypeDev, restic.NodeTypeFifo:
|
||||
// nothing to check
|
||||
|
||||
default:
|
||||
|
|
|
@ -482,7 +482,7 @@ func TestCheckerBlobTypeConfusion(t *testing.T) {
|
|||
|
||||
damagedNode := &restic.Node{
|
||||
Name: "damaged",
|
||||
Type: "file",
|
||||
Type: restic.NodeTypeFile,
|
||||
Mode: 0644,
|
||||
Size: 42,
|
||||
Content: restic.IDs{restic.TestParseID("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")},
|
||||
|
@ -507,14 +507,14 @@ func TestCheckerBlobTypeConfusion(t *testing.T) {
|
|||
|
||||
malNode := &restic.Node{
|
||||
Name: "aaaaa",
|
||||
Type: "file",
|
||||
Type: restic.NodeTypeFile,
|
||||
Mode: 0644,
|
||||
Size: uint64(len(buf)),
|
||||
Content: restic.IDs{id},
|
||||
}
|
||||
dirNode := &restic.Node{
|
||||
Name: "bbbbb",
|
||||
Type: "dir",
|
||||
Type: restic.NodeTypeDir,
|
||||
Mode: 0755,
|
||||
Subtree: &id,
|
||||
}
|
||||
|
|
|
@ -67,7 +67,7 @@ func sendNodes(ctx context.Context, repo restic.BlobLoader, root *restic.Node, c
|
|||
}
|
||||
|
||||
// If this is no directory we are finished
|
||||
if !IsDir(root) {
|
||||
if root.Type != restic.NodeTypeDir {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -81,7 +81,7 @@ func sendNodes(ctx context.Context, repo restic.BlobLoader, root *restic.Node, c
|
|||
|
||||
node.Path = path.Join(root.Path, nodepath)
|
||||
|
||||
if !IsFile(node) && !IsDir(node) && !IsLink(node) {
|
||||
if node.Type != restic.NodeTypeFile && node.Type != restic.NodeTypeDir && node.Type != restic.NodeTypeSymlink {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -176,18 +176,3 @@ func (d *Dumper) writeNode(ctx context.Context, w io.Writer, node *restic.Node)
|
|||
|
||||
return wg.Wait()
|
||||
}
|
||||
|
||||
// IsDir checks if the given node is a directory.
|
||||
func IsDir(node *restic.Node) bool {
|
||||
return node.Type == "dir"
|
||||
}
|
||||
|
||||
// IsLink checks if the given node as a link.
|
||||
func IsLink(node *restic.Node) bool {
|
||||
return node.Type == "symlink"
|
||||
}
|
||||
|
||||
// IsFile checks if the given node is a file.
|
||||
func IsFile(node *restic.Node) bool {
|
||||
return node.Type == "file"
|
||||
}
|
||||
|
|
|
@ -79,16 +79,16 @@ func (d *Dumper) dumpNodeTar(ctx context.Context, node *restic.Node, w *tar.Writ
|
|||
header.Mode |= cISVTX
|
||||
}
|
||||
|
||||
if IsFile(node) {
|
||||
if node.Type == restic.NodeTypeFile {
|
||||
header.Typeflag = tar.TypeReg
|
||||
}
|
||||
|
||||
if IsLink(node) {
|
||||
if node.Type == restic.NodeTypeSymlink {
|
||||
header.Typeflag = tar.TypeSymlink
|
||||
header.Linkname = node.LinkTarget
|
||||
}
|
||||
|
||||
if IsDir(node) {
|
||||
if node.Type == restic.NodeTypeDir {
|
||||
header.Typeflag = tar.TypeDir
|
||||
header.Name += "/"
|
||||
}
|
||||
|
|
|
@ -124,7 +124,7 @@ func TestFieldTooLong(t *testing.T) {
|
|||
node := restic.Node{
|
||||
Name: "file_with_xattr",
|
||||
Path: "/file_with_xattr",
|
||||
Type: "file",
|
||||
Type: restic.NodeTypeFile,
|
||||
Mode: 0644,
|
||||
ExtendedAttributes: []restic.ExtendedAttribute{
|
||||
{
|
||||
|
|
|
@ -40,7 +40,7 @@ func (d *Dumper) dumpNodeZip(ctx context.Context, node *restic.Node, zw *zip.Wri
|
|||
}
|
||||
header.SetMode(node.Mode)
|
||||
|
||||
if IsDir(node) {
|
||||
if node.Type == restic.NodeTypeDir {
|
||||
header.Name += "/"
|
||||
}
|
||||
|
||||
|
@ -49,7 +49,7 @@ func (d *Dumper) dumpNodeZip(ctx context.Context, node *restic.Node, zw *zip.Wri
|
|||
return errors.Wrap(err, "ZipHeader")
|
||||
}
|
||||
|
||||
if IsLink(node) {
|
||||
if node.Type == restic.NodeTypeSymlink {
|
||||
if _, err = w.Write([]byte(node.LinkTarget)); err != nil {
|
||||
return errors.Wrap(err, "Write")
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/restic/restic/internal/restic"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
|
@ -105,7 +106,7 @@ func ClearAttribute(path string, attribute uint32) error {
|
|||
}
|
||||
|
||||
// OpenHandleForEA return a file handle for file or dir for setting/getting EAs
|
||||
func OpenHandleForEA(nodeType, path string, writeAccess bool) (handle windows.Handle, err error) {
|
||||
func OpenHandleForEA(nodeType restic.NodeType, path string, writeAccess bool) (handle windows.Handle, err error) {
|
||||
path = fixpath(path)
|
||||
fileAccess := windows.FILE_READ_EA
|
||||
if writeAccess {
|
||||
|
@ -113,10 +114,10 @@ func OpenHandleForEA(nodeType, path string, writeAccess bool) (handle windows.Ha
|
|||
}
|
||||
|
||||
switch nodeType {
|
||||
case "file":
|
||||
case restic.NodeTypeFile:
|
||||
utf16Path := windows.StringToUTF16Ptr(path)
|
||||
handle, err = windows.CreateFile(utf16Path, uint32(fileAccess), 0, nil, windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL, 0)
|
||||
case "dir":
|
||||
case restic.NodeTypeDir:
|
||||
utf16Path := windows.StringToUTF16Ptr(path)
|
||||
handle, err = windows.CreateFile(utf16Path, uint32(fileAccess), 0, nil, windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL|windows.FILE_FLAG_BACKUP_SEMANTICS, 0)
|
||||
default:
|
||||
|
|
|
@ -25,7 +25,7 @@ func NodeFromFileInfo(path string, fi os.FileInfo, ignoreXattrListError bool) (*
|
|||
}
|
||||
|
||||
node.Type = nodeTypeFromFileInfo(fi)
|
||||
if node.Type == "file" {
|
||||
if node.Type == restic.NodeTypeFile {
|
||||
node.Size = uint64(fi.Size())
|
||||
}
|
||||
|
||||
|
@ -33,27 +33,27 @@ func NodeFromFileInfo(path string, fi os.FileInfo, ignoreXattrListError bool) (*
|
|||
return node, err
|
||||
}
|
||||
|
||||
func nodeTypeFromFileInfo(fi os.FileInfo) string {
|
||||
func nodeTypeFromFileInfo(fi os.FileInfo) restic.NodeType {
|
||||
switch fi.Mode() & os.ModeType {
|
||||
case 0:
|
||||
return "file"
|
||||
return restic.NodeTypeFile
|
||||
case os.ModeDir:
|
||||
return "dir"
|
||||
return restic.NodeTypeDir
|
||||
case os.ModeSymlink:
|
||||
return "symlink"
|
||||
return restic.NodeTypeSymlink
|
||||
case os.ModeDevice | os.ModeCharDevice:
|
||||
return "chardev"
|
||||
return restic.NodeTypeCharDev
|
||||
case os.ModeDevice:
|
||||
return "dev"
|
||||
return restic.NodeTypeDev
|
||||
case os.ModeNamedPipe:
|
||||
return "fifo"
|
||||
return restic.NodeTypeFifo
|
||||
case os.ModeSocket:
|
||||
return "socket"
|
||||
return restic.NodeTypeSocket
|
||||
case os.ModeIrregular:
|
||||
return "irregular"
|
||||
return restic.NodeTypeIrregular
|
||||
}
|
||||
|
||||
return ""
|
||||
return restic.NodeTypeInvalid
|
||||
}
|
||||
|
||||
func nodeFillExtra(node *restic.Node, path string, fi os.FileInfo, ignoreXattrListError bool) error {
|
||||
|
@ -74,25 +74,25 @@ func nodeFillExtra(node *restic.Node, path string, fi os.FileInfo, ignoreXattrLi
|
|||
nodeFillUser(node, stat)
|
||||
|
||||
switch node.Type {
|
||||
case "file":
|
||||
case restic.NodeTypeFile:
|
||||
node.Size = uint64(stat.size())
|
||||
node.Links = uint64(stat.nlink())
|
||||
case "dir":
|
||||
case "symlink":
|
||||
case restic.NodeTypeDir:
|
||||
case restic.NodeTypeSymlink:
|
||||
var err error
|
||||
node.LinkTarget, err = Readlink(path)
|
||||
node.Links = uint64(stat.nlink())
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
case "dev":
|
||||
case restic.NodeTypeDev:
|
||||
node.Device = uint64(stat.rdev())
|
||||
node.Links = uint64(stat.nlink())
|
||||
case "chardev":
|
||||
case restic.NodeTypeCharDev:
|
||||
node.Device = uint64(stat.rdev())
|
||||
node.Links = uint64(stat.nlink())
|
||||
case "fifo":
|
||||
case "socket":
|
||||
case restic.NodeTypeFifo:
|
||||
case restic.NodeTypeSocket:
|
||||
default:
|
||||
return errors.Errorf("unsupported file type %q", node.Type)
|
||||
}
|
||||
|
@ -178,31 +178,31 @@ func NodeCreateAt(node *restic.Node, path string) error {
|
|||
debug.Log("create node %v at %v", node.Name, path)
|
||||
|
||||
switch node.Type {
|
||||
case "dir":
|
||||
case restic.NodeTypeDir:
|
||||
if err := nodeCreateDirAt(node, path); err != nil {
|
||||
return err
|
||||
}
|
||||
case "file":
|
||||
case restic.NodeTypeFile:
|
||||
if err := nodeCreateFileAt(path); err != nil {
|
||||
return err
|
||||
}
|
||||
case "symlink":
|
||||
case restic.NodeTypeSymlink:
|
||||
if err := nodeCreateSymlinkAt(node, path); err != nil {
|
||||
return err
|
||||
}
|
||||
case "dev":
|
||||
case restic.NodeTypeDev:
|
||||
if err := nodeCreateDevAt(node, path); err != nil {
|
||||
return err
|
||||
}
|
||||
case "chardev":
|
||||
case restic.NodeTypeCharDev:
|
||||
if err := nodeCreateCharDevAt(node, path); err != nil {
|
||||
return err
|
||||
}
|
||||
case "fifo":
|
||||
case restic.NodeTypeFifo:
|
||||
if err := nodeCreateFifoAt(path); err != nil {
|
||||
return err
|
||||
}
|
||||
case "socket":
|
||||
case restic.NodeTypeSocket:
|
||||
return nil
|
||||
default:
|
||||
return errors.Errorf("filetype %q not implemented", node.Type)
|
||||
|
@ -305,7 +305,7 @@ func nodeRestoreMetadata(node *restic.Node, path string, warn func(msg string))
|
|||
// Moving RestoreTimestamps and restoreExtendedAttributes calls above as for readonly files in windows
|
||||
// calling Chmod below will no longer allow any modifications to be made on the file and the
|
||||
// calls above would fail.
|
||||
if node.Type != "symlink" {
|
||||
if node.Type != restic.NodeTypeSymlink {
|
||||
if err := Chmod(path, node.Mode); err != nil {
|
||||
if firsterr == nil {
|
||||
firsterr = errors.WithStack(err)
|
||||
|
@ -322,7 +322,7 @@ func NodeRestoreTimestamps(node *restic.Node, path string) error {
|
|||
syscall.NsecToTimespec(node.ModTime.UnixNano()),
|
||||
}
|
||||
|
||||
if node.Type == "symlink" {
|
||||
if node.Type == restic.NodeTypeSymlink {
|
||||
return nodeRestoreSymlinkTimestamps(path, utimes)
|
||||
}
|
||||
|
||||
|
|
|
@ -79,7 +79,7 @@ func parseTime(s string) time.Time {
|
|||
var nodeTests = []restic.Node{
|
||||
{
|
||||
Name: "testFile",
|
||||
Type: "file",
|
||||
Type: restic.NodeTypeFile,
|
||||
Content: restic.IDs{},
|
||||
UID: uint32(os.Getuid()),
|
||||
GID: uint32(os.Getgid()),
|
||||
|
@ -90,7 +90,7 @@ var nodeTests = []restic.Node{
|
|||
},
|
||||
{
|
||||
Name: "testSuidFile",
|
||||
Type: "file",
|
||||
Type: restic.NodeTypeFile,
|
||||
Content: restic.IDs{},
|
||||
UID: uint32(os.Getuid()),
|
||||
GID: uint32(os.Getgid()),
|
||||
|
@ -101,7 +101,7 @@ var nodeTests = []restic.Node{
|
|||
},
|
||||
{
|
||||
Name: "testSuidFile2",
|
||||
Type: "file",
|
||||
Type: restic.NodeTypeFile,
|
||||
Content: restic.IDs{},
|
||||
UID: uint32(os.Getuid()),
|
||||
GID: uint32(os.Getgid()),
|
||||
|
@ -112,7 +112,7 @@ var nodeTests = []restic.Node{
|
|||
},
|
||||
{
|
||||
Name: "testSticky",
|
||||
Type: "file",
|
||||
Type: restic.NodeTypeFile,
|
||||
Content: restic.IDs{},
|
||||
UID: uint32(os.Getuid()),
|
||||
GID: uint32(os.Getgid()),
|
||||
|
@ -123,7 +123,7 @@ var nodeTests = []restic.Node{
|
|||
},
|
||||
{
|
||||
Name: "testDir",
|
||||
Type: "dir",
|
||||
Type: restic.NodeTypeDir,
|
||||
Subtree: nil,
|
||||
UID: uint32(os.Getuid()),
|
||||
GID: uint32(os.Getgid()),
|
||||
|
@ -134,7 +134,7 @@ var nodeTests = []restic.Node{
|
|||
},
|
||||
{
|
||||
Name: "testSymlink",
|
||||
Type: "symlink",
|
||||
Type: restic.NodeTypeSymlink,
|
||||
LinkTarget: "invalid",
|
||||
UID: uint32(os.Getuid()),
|
||||
GID: uint32(os.Getgid()),
|
||||
|
@ -148,7 +148,7 @@ var nodeTests = []restic.Node{
|
|||
// metadata, so we can test if CreateAt works with pre-existing files.
|
||||
{
|
||||
Name: "testFile",
|
||||
Type: "file",
|
||||
Type: restic.NodeTypeFile,
|
||||
Content: restic.IDs{},
|
||||
UID: uint32(os.Getuid()),
|
||||
GID: uint32(os.Getgid()),
|
||||
|
@ -159,7 +159,7 @@ var nodeTests = []restic.Node{
|
|||
},
|
||||
{
|
||||
Name: "testDir",
|
||||
Type: "dir",
|
||||
Type: restic.NodeTypeDir,
|
||||
Subtree: nil,
|
||||
UID: uint32(os.Getuid()),
|
||||
GID: uint32(os.Getgid()),
|
||||
|
@ -170,7 +170,7 @@ var nodeTests = []restic.Node{
|
|||
},
|
||||
{
|
||||
Name: "testXattrFile",
|
||||
Type: "file",
|
||||
Type: restic.NodeTypeFile,
|
||||
Content: restic.IDs{},
|
||||
UID: uint32(os.Getuid()),
|
||||
GID: uint32(os.Getgid()),
|
||||
|
@ -184,7 +184,7 @@ var nodeTests = []restic.Node{
|
|||
},
|
||||
{
|
||||
Name: "testXattrDir",
|
||||
Type: "dir",
|
||||
Type: restic.NodeTypeDir,
|
||||
Subtree: nil,
|
||||
UID: uint32(os.Getuid()),
|
||||
GID: uint32(os.Getgid()),
|
||||
|
@ -198,7 +198,7 @@ var nodeTests = []restic.Node{
|
|||
},
|
||||
{
|
||||
Name: "testXattrFileMacOSResourceFork",
|
||||
Type: "file",
|
||||
Type: restic.NodeTypeFile,
|
||||
Content: restic.IDs{},
|
||||
UID: uint32(os.Getuid()),
|
||||
GID: uint32(os.Getgid()),
|
||||
|
@ -268,7 +268,7 @@ func TestNodeRestoreAt(t *testing.T) {
|
|||
"%v: UID doesn't match (%v != %v)", test.Type, test.UID, n2.UID)
|
||||
rtest.Assert(t, test.GID == n2.GID,
|
||||
"%v: GID doesn't match (%v != %v)", test.Type, test.GID, n2.GID)
|
||||
if test.Type != "symlink" {
|
||||
if test.Type != restic.NodeTypeSymlink {
|
||||
// On OpenBSD only root can set sticky bit (see sticky(8)).
|
||||
if runtime.GOOS != "openbsd" && runtime.GOOS != "netbsd" && runtime.GOOS != "solaris" && test.Name == "testSticky" {
|
||||
rtest.Assert(t, test.Mode == n2.Mode,
|
||||
|
@ -288,11 +288,11 @@ func TestNodeRestoreAt(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func AssertFsTimeEqual(t *testing.T, label string, nodeType string, t1 time.Time, t2 time.Time) {
|
||||
func AssertFsTimeEqual(t *testing.T, label string, nodeType restic.NodeType, t1 time.Time, t2 time.Time) {
|
||||
var equal bool
|
||||
|
||||
// Go currently doesn't support setting timestamps of symbolic links on darwin and bsd
|
||||
if nodeType == "symlink" {
|
||||
if nodeType == restic.NodeTypeSymlink {
|
||||
switch runtime.GOOS {
|
||||
case "darwin", "freebsd", "openbsd", "netbsd", "solaris":
|
||||
return
|
||||
|
|
|
@ -42,7 +42,7 @@ func checkFile(t testing.TB, stat *syscall.Stat_t, node *restic.Node) {
|
|||
t.Errorf("Dev does not match, want %v, got %v", stat.Dev, node.DeviceID)
|
||||
}
|
||||
|
||||
if node.Size != uint64(stat.Size) && node.Type != "symlink" {
|
||||
if node.Size != uint64(stat.Size) && node.Type != restic.NodeTypeSymlink {
|
||||
t.Errorf("Size does not match, want %v, got %v", stat.Size, node.Size)
|
||||
}
|
||||
|
||||
|
@ -135,9 +135,9 @@ func TestNodeFromFileInfo(t *testing.T) {
|
|||
}
|
||||
|
||||
switch node.Type {
|
||||
case "file", "symlink":
|
||||
case restic.NodeTypeFile, restic.NodeTypeSymlink:
|
||||
checkFile(t, s, node)
|
||||
case "dev", "chardev":
|
||||
case restic.NodeTypeDev, restic.NodeTypeCharDev:
|
||||
checkFile(t, s, node)
|
||||
checkDevice(t, s, node)
|
||||
default:
|
||||
|
|
|
@ -139,7 +139,7 @@ func closeFileHandle(fileHandle windows.Handle, path string) {
|
|||
|
||||
// restoreExtendedAttributes handles restore of the Windows Extended Attributes to the specified path.
|
||||
// The Windows API requires setting of all the Extended Attributes in one call.
|
||||
func restoreExtendedAttributes(nodeType, path string, eas []ExtendedAttribute) (err error) {
|
||||
func restoreExtendedAttributes(nodeType restic.NodeType, path string, eas []ExtendedAttribute) (err error) {
|
||||
var fileHandle windows.Handle
|
||||
if fileHandle, err = OpenHandleForEA(nodeType, path, true); fileHandle == 0 {
|
||||
return nil
|
||||
|
@ -386,7 +386,7 @@ func nodeFillGenericAttributes(node *restic.Node, path string, fi os.FileInfo, s
|
|||
}
|
||||
|
||||
var sd *[]byte
|
||||
if node.Type == "file" || node.Type == "dir" {
|
||||
if node.Type == restic.NodeTypeFile || node.Type == restic.NodeTypeDir {
|
||||
// Check EA support and get security descriptor for file/dir only
|
||||
allowExtended, err = checkAndStoreEASupport(path)
|
||||
if err != nil {
|
||||
|
|
|
@ -24,14 +24,14 @@ func TestRestoreSecurityDescriptors(t *testing.T) {
|
|||
t.Parallel()
|
||||
tempDir := t.TempDir()
|
||||
for i, sd := range TestFileSDs {
|
||||
testRestoreSecurityDescriptor(t, sd, tempDir, "file", fmt.Sprintf("testfile%d", i))
|
||||
testRestoreSecurityDescriptor(t, sd, tempDir, restic.NodeTypeFile, fmt.Sprintf("testfile%d", i))
|
||||
}
|
||||
for i, sd := range TestDirSDs {
|
||||
testRestoreSecurityDescriptor(t, sd, tempDir, "dir", fmt.Sprintf("testdir%d", i))
|
||||
testRestoreSecurityDescriptor(t, sd, tempDir, restic.NodeTypeDir, fmt.Sprintf("testdir%d", i))
|
||||
}
|
||||
}
|
||||
|
||||
func testRestoreSecurityDescriptor(t *testing.T, sd string, tempDir, fileType, fileName string) {
|
||||
func testRestoreSecurityDescriptor(t *testing.T, sd string, tempDir string, fileType restic.NodeType, fileName string) {
|
||||
// Decode the encoded string SD to get the security descriptor input in bytes.
|
||||
sdInputBytes, err := base64.StdEncoding.DecodeString(sd)
|
||||
test.OK(t, errors.Wrapf(err, "Error decoding SD for: %s", fileName))
|
||||
|
@ -56,7 +56,7 @@ func testRestoreSecurityDescriptor(t *testing.T, sd string, tempDir, fileType, f
|
|||
CompareSecurityDescriptors(t, testPath, *sdByteFromRestoredNode, *sdBytesFromRestoredPath)
|
||||
}
|
||||
|
||||
func getNode(name string, fileType string, genericAttributes map[restic.GenericAttributeType]json.RawMessage) restic.Node {
|
||||
func getNode(name string, fileType restic.NodeType, genericAttributes map[restic.GenericAttributeType]json.RawMessage) restic.Node {
|
||||
return restic.Node{
|
||||
Name: name,
|
||||
Type: fileType,
|
||||
|
@ -113,7 +113,7 @@ func TestRestoreFileAttributes(t *testing.T) {
|
|||
expectedNodes := []restic.Node{
|
||||
{
|
||||
Name: fmt.Sprintf("testfile%d", i),
|
||||
Type: "file",
|
||||
Type: restic.NodeTypeFile,
|
||||
Mode: 0655,
|
||||
ModTime: parseTime("2005-05-14 21:07:03.111"),
|
||||
AccessTime: parseTime("2005-05-14 21:07:04.222"),
|
||||
|
@ -146,7 +146,7 @@ func TestRestoreFileAttributes(t *testing.T) {
|
|||
expectedNodes := []restic.Node{
|
||||
{
|
||||
Name: fmt.Sprintf("testdirectory%d", i),
|
||||
Type: "dir",
|
||||
Type: restic.NodeTypeDir,
|
||||
Mode: 0755,
|
||||
ModTime: parseTime("2005-05-14 21:07:03.111"),
|
||||
AccessTime: parseTime("2005-05-14 21:07:04.222"),
|
||||
|
@ -164,7 +164,7 @@ func runGenericAttributesTest(t *testing.T, tempDir string, genericAttributeName
|
|||
expectedNodes := []restic.Node{
|
||||
{
|
||||
Name: "testfile",
|
||||
Type: "file",
|
||||
Type: restic.NodeTypeFile,
|
||||
Mode: 0644,
|
||||
ModTime: parseTime("2005-05-14 21:07:03.111"),
|
||||
AccessTime: parseTime("2005-05-14 21:07:04.222"),
|
||||
|
@ -173,7 +173,7 @@ func runGenericAttributesTest(t *testing.T, tempDir string, genericAttributeName
|
|||
},
|
||||
{
|
||||
Name: "testdirectory",
|
||||
Type: "dir",
|
||||
Type: restic.NodeTypeDir,
|
||||
Mode: 0755,
|
||||
ModTime: parseTime("2005-05-14 21:07:03.111"),
|
||||
AccessTime: parseTime("2005-05-14 21:07:04.222"),
|
||||
|
@ -200,12 +200,12 @@ func restoreAndGetNode(t *testing.T, tempDir string, testNode *restic.Node, warn
|
|||
err := os.MkdirAll(filepath.Dir(testPath), testNode.Mode)
|
||||
test.OK(t, errors.Wrapf(err, "Failed to create parent directories for: %s", testPath))
|
||||
|
||||
if testNode.Type == "file" {
|
||||
if testNode.Type == restic.NodeTypeFile {
|
||||
|
||||
testFile, err := os.Create(testPath)
|
||||
test.OK(t, errors.Wrapf(err, "Failed to create test file: %s", testPath))
|
||||
testFile.Close()
|
||||
} else if testNode.Type == "dir" {
|
||||
} else if testNode.Type == restic.NodeTypeDir {
|
||||
|
||||
err := os.Mkdir(testPath, testNode.Mode)
|
||||
test.OK(t, errors.Wrapf(err, "Failed to create test directory: %s", testPath))
|
||||
|
@ -242,7 +242,7 @@ func TestNewGenericAttributeType(t *testing.T) {
|
|||
expectedNodes := []restic.Node{
|
||||
{
|
||||
Name: "testfile",
|
||||
Type: "file",
|
||||
Type: restic.NodeTypeFile,
|
||||
Mode: 0644,
|
||||
ModTime: parseTime("2005-05-14 21:07:03.111"),
|
||||
AccessTime: parseTime("2005-05-14 21:07:04.222"),
|
||||
|
@ -251,7 +251,7 @@ func TestNewGenericAttributeType(t *testing.T) {
|
|||
},
|
||||
{
|
||||
Name: "testdirectory",
|
||||
Type: "dir",
|
||||
Type: restic.NodeTypeDir,
|
||||
Mode: 0755,
|
||||
ModTime: parseTime("2005-05-14 21:07:03.111"),
|
||||
AccessTime: parseTime("2005-05-14 21:07:04.222"),
|
||||
|
@ -274,7 +274,7 @@ func TestRestoreExtendedAttributes(t *testing.T) {
|
|||
expectedNodes := []restic.Node{
|
||||
{
|
||||
Name: "testfile",
|
||||
Type: "file",
|
||||
Type: restic.NodeTypeFile,
|
||||
Mode: 0644,
|
||||
ModTime: parseTime("2005-05-14 21:07:03.111"),
|
||||
AccessTime: parseTime("2005-05-14 21:07:04.222"),
|
||||
|
@ -285,7 +285,7 @@ func TestRestoreExtendedAttributes(t *testing.T) {
|
|||
},
|
||||
{
|
||||
Name: "testdirectory",
|
||||
Type: "dir",
|
||||
Type: restic.NodeTypeDir,
|
||||
Mode: 0755,
|
||||
ModTime: parseTime("2005-05-14 21:07:03.111"),
|
||||
AccessTime: parseTime("2005-05-14 21:07:04.222"),
|
||||
|
@ -301,9 +301,9 @@ func TestRestoreExtendedAttributes(t *testing.T) {
|
|||
var handle windows.Handle
|
||||
var err error
|
||||
utf16Path := windows.StringToUTF16Ptr(testPath)
|
||||
if node.Type == "file" {
|
||||
if node.Type == restic.NodeTypeFile {
|
||||
handle, err = windows.CreateFile(utf16Path, windows.FILE_READ_EA, 0, nil, windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL, 0)
|
||||
} else if node.Type == "dir" {
|
||||
} else if node.Type == restic.NodeTypeDir {
|
||||
handle, err = windows.CreateFile(utf16Path, windows.FILE_READ_EA, 0, nil, windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL|windows.FILE_FLAG_BACKUP_SEMANTICS, 0)
|
||||
}
|
||||
test.OK(t, errors.Wrapf(err, "Error opening file/directory for: %s", testPath))
|
||||
|
|
|
@ -23,13 +23,13 @@ func setAndVerifyXattr(t *testing.T, file string, attrs []restic.ExtendedAttribu
|
|||
}
|
||||
|
||||
node := &restic.Node{
|
||||
Type: "file",
|
||||
Type: restic.NodeTypeFile,
|
||||
ExtendedAttributes: attrs,
|
||||
}
|
||||
rtest.OK(t, nodeRestoreExtendedAttributes(node, file))
|
||||
|
||||
nodeActual := &restic.Node{
|
||||
Type: "file",
|
||||
Type: restic.NodeTypeFile,
|
||||
}
|
||||
rtest.OK(t, nodeFillExtendedAttributes(nodeActual, file, false))
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ func unwrapCtxCanceled(err error) error {
|
|||
// replaceSpecialNodes replaces nodes with name "." and "/" by their contents.
|
||||
// Otherwise, the node is returned.
|
||||
func replaceSpecialNodes(ctx context.Context, repo restic.BlobLoader, node *restic.Node) ([]*restic.Node, error) {
|
||||
if node.Type != "dir" || node.Subtree == nil {
|
||||
if node.Type != restic.NodeTypeDir || node.Subtree == nil {
|
||||
return []*restic.Node{node}, nil
|
||||
}
|
||||
|
||||
|
@ -147,7 +147,7 @@ func (d *dir) calcNumberOfLinks() uint32 {
|
|||
// of directories contained by d
|
||||
count := uint32(2)
|
||||
for _, node := range d.items {
|
||||
if node.Type == "dir" {
|
||||
if node.Type == restic.NodeTypeDir {
|
||||
count++
|
||||
}
|
||||
}
|
||||
|
@ -182,11 +182,11 @@ func (d *dir) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
|
|||
name := cleanupNodeName(node.Name)
|
||||
var typ fuse.DirentType
|
||||
switch node.Type {
|
||||
case "dir":
|
||||
case restic.NodeTypeDir:
|
||||
typ = fuse.DT_Dir
|
||||
case "file":
|
||||
case restic.NodeTypeFile:
|
||||
typ = fuse.DT_File
|
||||
case "symlink":
|
||||
case restic.NodeTypeSymlink:
|
||||
typ = fuse.DT_Link
|
||||
}
|
||||
|
||||
|
@ -215,13 +215,13 @@ func (d *dir) Lookup(ctx context.Context, name string) (fs.Node, error) {
|
|||
}
|
||||
inode := inodeFromNode(d.inode, node)
|
||||
switch node.Type {
|
||||
case "dir":
|
||||
case restic.NodeTypeDir:
|
||||
return newDir(d.root, inode, d.inode, node)
|
||||
case "file":
|
||||
case restic.NodeTypeFile:
|
||||
return newFile(d.root, inode, node)
|
||||
case "symlink":
|
||||
case restic.NodeTypeSymlink:
|
||||
return newLink(d.root, inode, node)
|
||||
case "dev", "chardev", "fifo", "socket":
|
||||
case restic.NodeTypeDev, restic.NodeTypeCharDev, restic.NodeTypeFifo, restic.NodeTypeSocket:
|
||||
return newOther(d.root, inode, node)
|
||||
default:
|
||||
debug.Log(" node %v has unknown type %v", name, node.Type)
|
||||
|
|
|
@ -249,7 +249,7 @@ func TestBlocks(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestInodeFromNode(t *testing.T) {
|
||||
node := &restic.Node{Name: "foo.txt", Type: "chardev", Links: 2}
|
||||
node := &restic.Node{Name: "foo.txt", Type: restic.NodeTypeCharDev, Links: 2}
|
||||
ino1 := inodeFromNode(1, node)
|
||||
ino2 := inodeFromNode(2, node)
|
||||
rtest.Assert(t, ino1 == ino2, "inodes %d, %d of hard links differ", ino1, ino2)
|
||||
|
@ -261,9 +261,9 @@ func TestInodeFromNode(t *testing.T) {
|
|||
|
||||
// Regression test: in a path a/b/b, the grandchild should not get the
|
||||
// same inode as the grandparent.
|
||||
a := &restic.Node{Name: "a", Type: "dir", Links: 2}
|
||||
ab := &restic.Node{Name: "b", Type: "dir", Links: 2}
|
||||
abb := &restic.Node{Name: "b", Type: "dir", Links: 2}
|
||||
a := &restic.Node{Name: "a", Type: restic.NodeTypeDir, Links: 2}
|
||||
ab := &restic.Node{Name: "b", Type: restic.NodeTypeDir, Links: 2}
|
||||
abb := &restic.Node{Name: "b", Type: restic.NodeTypeDir, Links: 2}
|
||||
inoA := inodeFromNode(1, a)
|
||||
inoAb := inodeFromNode(inoA, ab)
|
||||
inoAbb := inodeFromNode(inoAb, abb)
|
||||
|
@ -272,7 +272,7 @@ func TestInodeFromNode(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestLink(t *testing.T) {
|
||||
node := &restic.Node{Name: "foo.txt", Type: "symlink", Links: 1, LinkTarget: "dst", ExtendedAttributes: []restic.ExtendedAttribute{
|
||||
node := &restic.Node{Name: "foo.txt", Type: restic.NodeTypeSymlink, Links: 1, LinkTarget: "dst", ExtendedAttributes: []restic.ExtendedAttribute{
|
||||
{Name: "foo", Value: []byte("bar")},
|
||||
}}
|
||||
|
||||
|
@ -305,11 +305,11 @@ func BenchmarkInode(b *testing.B) {
|
|||
}{
|
||||
{
|
||||
name: "no_hard_links",
|
||||
node: restic.Node{Name: "a somewhat long-ish filename.svg.bz2", Type: "fifo"},
|
||||
node: restic.Node{Name: "a somewhat long-ish filename.svg.bz2", Type: restic.NodeTypeFifo},
|
||||
},
|
||||
{
|
||||
name: "hard_link",
|
||||
node: restic.Node{Name: "some other filename", Type: "file", Links: 2},
|
||||
node: restic.Node{Name: "some other filename", Type: restic.NodeTypeFile, Links: 2},
|
||||
},
|
||||
} {
|
||||
b.Run(sub.name, func(b *testing.B) {
|
||||
|
|
|
@ -25,7 +25,7 @@ func inodeFromName(parent uint64, name string) uint64 {
|
|||
|
||||
// inodeFromNode generates an inode number for a file within a snapshot.
|
||||
func inodeFromNode(parent uint64, node *restic.Node) (inode uint64) {
|
||||
if node.Links > 1 && node.Type != "dir" {
|
||||
if node.Links > 1 && node.Type != restic.NodeTypeDir {
|
||||
// If node has hard links, give them all the same inode,
|
||||
// irrespective of the parent.
|
||||
var buf [16]byte
|
||||
|
|
|
@ -46,7 +46,7 @@ func FindUsedBlobs(ctx context.Context, repo Loader, treeIDs IDs, blobs FindBlob
|
|||
lock.Lock()
|
||||
for _, node := range tree.Nodes {
|
||||
switch node.Type {
|
||||
case "file":
|
||||
case NodeTypeFile:
|
||||
for _, blob := range node.Content {
|
||||
blobs.Insert(BlobHandle{ID: blob, Type: DataBlob})
|
||||
}
|
||||
|
|
|
@ -67,10 +67,24 @@ func storeGenericAttributeType(attributeTypes ...GenericAttributeType) {
|
|||
}
|
||||
}
|
||||
|
||||
type NodeType string
|
||||
|
||||
var (
|
||||
NodeTypeFile = NodeType("file")
|
||||
NodeTypeDir = NodeType("dir")
|
||||
NodeTypeSymlink = NodeType("symlink")
|
||||
NodeTypeDev = NodeType("dev")
|
||||
NodeTypeCharDev = NodeType("chardev")
|
||||
NodeTypeFifo = NodeType("fifo")
|
||||
NodeTypeSocket = NodeType("socket")
|
||||
NodeTypeIrregular = NodeType("irregular")
|
||||
NodeTypeInvalid = NodeType("")
|
||||
)
|
||||
|
||||
// Node is a file, directory or other item in a backup.
|
||||
type Node struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Type NodeType `json:"type"`
|
||||
Mode os.FileMode `json:"mode,omitempty"`
|
||||
ModTime time.Time `json:"mtime,omitempty"`
|
||||
AccessTime time.Time `json:"atime,omitempty"`
|
||||
|
@ -110,19 +124,19 @@ func (n Nodes) Swap(i, j int) { n[i], n[j] = n[j], n[i] }
|
|||
func (node Node) String() string {
|
||||
var mode os.FileMode
|
||||
switch node.Type {
|
||||
case "file":
|
||||
case NodeTypeFile:
|
||||
mode = 0
|
||||
case "dir":
|
||||
case NodeTypeDir:
|
||||
mode = os.ModeDir
|
||||
case "symlink":
|
||||
case NodeTypeSymlink:
|
||||
mode = os.ModeSymlink
|
||||
case "dev":
|
||||
case NodeTypeDev:
|
||||
mode = os.ModeDevice
|
||||
case "chardev":
|
||||
case NodeTypeCharDev:
|
||||
mode = os.ModeDevice | os.ModeCharDevice
|
||||
case "fifo":
|
||||
case NodeTypeFifo:
|
||||
mode = os.ModeNamedPipe
|
||||
case "socket":
|
||||
case NodeTypeSocket:
|
||||
mode = os.ModeSocket
|
||||
}
|
||||
|
||||
|
|
|
@ -81,7 +81,7 @@ func (fs *fakeFileSystem) saveTree(ctx context.Context, seed int64, depth int) I
|
|||
|
||||
node := &Node{
|
||||
Name: fmt.Sprintf("dir-%v", treeSeed),
|
||||
Type: "dir",
|
||||
Type: NodeTypeDir,
|
||||
Mode: 0755,
|
||||
Subtree: &id,
|
||||
}
|
||||
|
@ -95,7 +95,7 @@ func (fs *fakeFileSystem) saveTree(ctx context.Context, seed int64, depth int) I
|
|||
|
||||
node := &Node{
|
||||
Name: fmt.Sprintf("file-%v", fileSeed),
|
||||
Type: "file",
|
||||
Type: NodeTypeFile,
|
||||
Mode: 0644,
|
||||
Size: uint64(fileSize),
|
||||
}
|
||||
|
|
|
@ -96,7 +96,7 @@ func (t *Tree) Sort() {
|
|||
// Subtrees returns a slice of all subtree IDs of the tree.
|
||||
func (t *Tree) Subtrees() (trees IDs) {
|
||||
for _, node := range t.Nodes {
|
||||
if node.Type == "dir" && node.Subtree != nil {
|
||||
if node.Type == NodeTypeDir && node.Subtree != nil {
|
||||
trees = append(trees, *node.Subtree)
|
||||
}
|
||||
}
|
||||
|
@ -208,7 +208,7 @@ func FindTreeDirectory(ctx context.Context, repo BlobLoader, id *ID, dir string)
|
|||
if node == nil {
|
||||
return nil, fmt.Errorf("path %s: not found", subfolder)
|
||||
}
|
||||
if node.Type != "dir" || node.Subtree == nil {
|
||||
if node.Type != NodeTypeDir || node.Subtree == nil {
|
||||
return nil, fmt.Errorf("path %s: not a directory", subfolder)
|
||||
}
|
||||
id = node.Subtree
|
||||
|
|
|
@ -202,18 +202,18 @@ func (res *Restorer) traverseTreeInner(ctx context.Context, target, location str
|
|||
}
|
||||
|
||||
// sockets cannot be restored
|
||||
if node.Type == "socket" {
|
||||
if node.Type == restic.NodeTypeSocket {
|
||||
continue
|
||||
}
|
||||
|
||||
selectedForRestore, childMayBeSelected := res.SelectFilter(nodeLocation, node.Type == "dir")
|
||||
selectedForRestore, childMayBeSelected := res.SelectFilter(nodeLocation, node.Type == restic.NodeTypeDir)
|
||||
debug.Log("SelectFilter returned %v %v for %q", selectedForRestore, childMayBeSelected, nodeLocation)
|
||||
|
||||
if selectedForRestore {
|
||||
hasRestored = true
|
||||
}
|
||||
|
||||
if node.Type == "dir" {
|
||||
if node.Type == restic.NodeTypeDir {
|
||||
if node.Subtree == nil {
|
||||
return nil, hasRestored, errors.Errorf("Dir without subtree in tree %v", treeID.Str())
|
||||
}
|
||||
|
@ -377,7 +377,7 @@ func (res *Restorer) RestoreTo(ctx context.Context, dst string) (uint64, error)
|
|||
return err
|
||||
}
|
||||
|
||||
if node.Type != "file" {
|
||||
if node.Type != restic.NodeTypeFile {
|
||||
res.opts.Progress.AddFile(0)
|
||||
return nil
|
||||
}
|
||||
|
@ -433,7 +433,7 @@ func (res *Restorer) RestoreTo(ctx context.Context, dst string) (uint64, error)
|
|||
err = res.traverseTree(ctx, dst, *res.sn.Tree, treeVisitor{
|
||||
visitNode: func(node *restic.Node, target, location string) error {
|
||||
debug.Log("second pass, visitNode: restore node %q", location)
|
||||
if node.Type != "file" {
|
||||
if node.Type != restic.NodeTypeFile {
|
||||
_, err := res.withOverwriteCheck(ctx, node, target, location, false, nil, func(_ bool, _ *fileState) error {
|
||||
return res.restoreNodeTo(node, target, location)
|
||||
})
|
||||
|
@ -547,7 +547,7 @@ func (res *Restorer) withOverwriteCheck(ctx context.Context, node *restic.Node,
|
|||
|
||||
var matches *fileState
|
||||
updateMetadataOnly := false
|
||||
if node.Type == "file" && !isHardlink {
|
||||
if node.Type == restic.NodeTypeFile && !isHardlink {
|
||||
// if a file fails to verify, then matches is nil which results in restoring from scratch
|
||||
matches, buf, _ = res.verifyFile(ctx, target, node, false, res.opts.Overwrite == OverwriteIfChanged, buf)
|
||||
// skip files that are already correct completely
|
||||
|
@ -616,7 +616,7 @@ func (res *Restorer) VerifyFiles(ctx context.Context, dst string, countRestoredF
|
|||
|
||||
err := res.traverseTree(ctx, dst, *res.sn.Tree, treeVisitor{
|
||||
visitNode: func(node *restic.Node, target, location string) error {
|
||||
if node.Type != "file" {
|
||||
if node.Type != restic.NodeTypeFile {
|
||||
return nil
|
||||
}
|
||||
if metadataOnly, ok := res.hasRestoredFile(location); !ok || metadataOnly {
|
||||
|
|
|
@ -108,7 +108,7 @@ func saveDir(t testing.TB, repo restic.BlobSaver, nodes map[string]Node, inode u
|
|||
mode = 0644
|
||||
}
|
||||
err := tree.Insert(&restic.Node{
|
||||
Type: "file",
|
||||
Type: restic.NodeTypeFile,
|
||||
Mode: mode,
|
||||
ModTime: node.ModTime,
|
||||
Name: name,
|
||||
|
@ -123,7 +123,7 @@ func saveDir(t testing.TB, repo restic.BlobSaver, nodes map[string]Node, inode u
|
|||
rtest.OK(t, err)
|
||||
case Symlink:
|
||||
err := tree.Insert(&restic.Node{
|
||||
Type: "symlink",
|
||||
Type: restic.NodeTypeSymlink,
|
||||
Mode: os.ModeSymlink | 0o777,
|
||||
ModTime: node.ModTime,
|
||||
Name: name,
|
||||
|
@ -143,7 +143,7 @@ func saveDir(t testing.TB, repo restic.BlobSaver, nodes map[string]Node, inode u
|
|||
}
|
||||
|
||||
err := tree.Insert(&restic.Node{
|
||||
Type: "dir",
|
||||
Type: restic.NodeTypeDir,
|
||||
Mode: mode,
|
||||
ModTime: node.ModTime,
|
||||
Name: name,
|
||||
|
|
|
@ -124,7 +124,7 @@ func (p *Progress) CompleteItem(item string, previous, current *restic.Node, s a
|
|||
}
|
||||
|
||||
switch current.Type {
|
||||
case "dir":
|
||||
case restic.NodeTypeDir:
|
||||
p.mu.Lock()
|
||||
p.addProcessed(Counter{Dirs: 1})
|
||||
p.mu.Unlock()
|
||||
|
@ -138,7 +138,7 @@ func (p *Progress) CompleteItem(item string, previous, current *restic.Node, s a
|
|||
p.printer.CompleteItem("dir modified", item, s, d)
|
||||
}
|
||||
|
||||
case "file":
|
||||
case restic.NodeTypeFile:
|
||||
p.mu.Lock()
|
||||
p.addProcessed(Counter{Files: 1})
|
||||
delete(p.currentFiles, item)
|
||||
|
|
|
@ -55,10 +55,10 @@ func TestProgress(t *testing.T) {
|
|||
prog.CompleteBlob(1024)
|
||||
|
||||
// "dir unchanged"
|
||||
node := restic.Node{Type: "dir"}
|
||||
node := restic.Node{Type: restic.NodeTypeDir}
|
||||
prog.CompleteItem("foo", &node, &node, archiver.ItemStats{}, 0)
|
||||
// "file new"
|
||||
node.Type = "file"
|
||||
node.Type = restic.NodeTypeFile
|
||||
prog.CompleteItem("foo", nil, &node, archiver.ItemStats{}, 0)
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
|
|
|
@ -65,7 +65,7 @@ func NewSnapshotSizeRewriter(rewriteNode NodeRewriteFunc) (*TreeRewriter, QueryR
|
|||
t := NewTreeRewriter(RewriteOpts{
|
||||
RewriteNode: func(node *restic.Node, path string) *restic.Node {
|
||||
node = rewriteNode(node, path)
|
||||
if node != nil && node.Type == "file" {
|
||||
if node != nil && node.Type == restic.NodeTypeFile {
|
||||
count++
|
||||
size += node.Size
|
||||
}
|
||||
|
@ -126,7 +126,7 @@ func (t *TreeRewriter) RewriteTree(ctx context.Context, repo BlobLoadSaver, node
|
|||
continue
|
||||
}
|
||||
|
||||
if node.Type != "dir" {
|
||||
if node.Type != restic.NodeTypeDir {
|
||||
err = tb.AddNode(node)
|
||||
if err != nil {
|
||||
return restic.ID{}, err
|
||||
|
|
|
@ -110,7 +110,7 @@ func checkIncreaseNodeSize(increase uint64) checkRewriteFunc {
|
|||
return func(t testing.TB) (rewriter *TreeRewriter, final func(testing.TB)) {
|
||||
rewriter = NewTreeRewriter(RewriteOpts{
|
||||
RewriteNode: func(node *restic.Node, path string) *restic.Node {
|
||||
if node.Type == "file" {
|
||||
if node.Type == restic.NodeTypeFile {
|
||||
node.Size += increase
|
||||
}
|
||||
return node
|
||||
|
@ -329,7 +329,7 @@ func TestSnapshotSizeQuery(t *testing.T) {
|
|||
if path == "/bar" {
|
||||
return nil
|
||||
}
|
||||
if node.Type == "file" {
|
||||
if node.Type == restic.NodeTypeFile {
|
||||
node.Size += 21
|
||||
}
|
||||
return node
|
||||
|
|
|
@ -63,11 +63,11 @@ func walk(ctx context.Context, repo restic.BlobLoader, prefix string, parentTree
|
|||
|
||||
p := path.Join(prefix, node.Name)
|
||||
|
||||
if node.Type == "" {
|
||||
if node.Type == restic.NodeTypeInvalid {
|
||||
return errors.Errorf("node type is empty for node %q", node.Name)
|
||||
}
|
||||
|
||||
if node.Type != "dir" {
|
||||
if node.Type != restic.NodeTypeDir {
|
||||
err := visitor.ProcessNode(parentTreeID, p, node, nil)
|
||||
if err != nil {
|
||||
if err == ErrSkipNode {
|
||||
|
|
|
@ -38,7 +38,7 @@ func buildTreeMap(tree TestTree, m TreeMap) restic.ID {
|
|||
case TestFile:
|
||||
err := tb.AddNode(&restic.Node{
|
||||
Name: name,
|
||||
Type: "file",
|
||||
Type: restic.NodeTypeFile,
|
||||
Size: elem.Size,
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -49,7 +49,7 @@ func buildTreeMap(tree TestTree, m TreeMap) restic.ID {
|
|||
err := tb.AddNode(&restic.Node{
|
||||
Name: name,
|
||||
Subtree: &id,
|
||||
Type: "dir",
|
||||
Type: restic.NodeTypeDir,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
|
Loading…
Reference in a new issue