-
Notifications
You must be signed in to change notification settings - Fork 43
/
Copy pathmtreefs.go
106 lines (96 loc) · 3.46 KB
/
mtreefs.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
package desync
import (
"crypto"
"fmt"
"io"
"strings"
)
// MtreeFS prints the filesystem operations to a writer (which can be os.Stdout)
// in mtree format.
type MtreeFS struct {
w io.Writer
}
var _ FilesystemWriter = MtreeFS{}
// NewMtreeFS initializes a new instance of an mtree decoder that
// writes its output into the provided stream.
func NewMtreeFS(w io.Writer) (MtreeFS, error) {
_, err := fmt.Fprintln(w, "#mtree v1.0")
return MtreeFS{w: w}, err
}
func (fs MtreeFS) CreateDir(n NodeDirectory) error {
attr := []string{mtreeFilename(n.Name), "type=dir"}
attr = append(attr, fmt.Sprintf("mode=%04o", n.Mode.Perm()))
attr = append(attr, fmt.Sprintf("uid=%d", n.UID))
attr = append(attr, fmt.Sprintf("gid=%d", n.GID))
attr = append(attr, fmt.Sprintf("time=%d.%9d", n.MTime.Unix(), n.MTime.Nanosecond()))
fmt.Fprintln(fs.w, strings.Join(attr, " "))
return nil
}
func (fs MtreeFS) CreateFile(n NodeFile) error {
attr := []string{mtreeFilename(n.Name), "type=file"}
attr = append(attr, fmt.Sprintf("mode=%04o", n.Mode.Perm()))
attr = append(attr, fmt.Sprintf("uid=%d", n.UID))
attr = append(attr, fmt.Sprintf("gid=%d", n.GID))
attr = append(attr, fmt.Sprintf("size=%d", n.Size))
attr = append(attr, fmt.Sprintf("time=%d.%09d", n.MTime.Unix(), n.MTime.Nanosecond()))
switch Digest.Algorithm() {
case crypto.SHA512_256:
h := Digest.Algorithm().New()
if _, err := io.Copy(h, n.Data); err != nil {
return err
}
attr = append(attr, fmt.Sprintf("sha512256digest=%x", h.Sum(nil)))
case crypto.SHA256:
h := Digest.Algorithm().New()
if _, err := io.Copy(h, n.Data); err != nil {
return err
}
attr = append(attr, fmt.Sprintf("sha256digest=%x", h.Sum(nil)))
default:
return fmt.Errorf("unsupported mtree hash algorithm %d", Digest.Algorithm())
}
fmt.Fprintln(fs.w, strings.Join(attr, " "))
return nil
}
func (fs MtreeFS) CreateSymlink(n NodeSymlink) error {
attr := []string{mtreeFilename(n.Name), "type=link"}
attr = append(attr, fmt.Sprintf("mode=%04o", n.Mode.Perm()))
attr = append(attr, fmt.Sprintf("target=%s", mtreeFilename(n.Target)))
attr = append(attr, fmt.Sprintf("uid=%d", n.UID))
attr = append(attr, fmt.Sprintf("gid=%d", n.GID))
attr = append(attr, fmt.Sprintf("time=%d.%9d", n.MTime.Unix(), n.MTime.Nanosecond()))
fmt.Fprintln(fs.w, strings.Join(attr, " "))
return nil
}
func (fs MtreeFS) CreateDevice(n NodeDevice) error {
attr := []string{mtreeFilename(n.Name)}
if n.Mode&modeChar != 0 {
attr = append(attr, "type=char")
} else {
attr = append(attr, "type=block")
}
attr = append(attr, fmt.Sprintf("mode=%04o", n.Mode.Perm()))
attr = append(attr, fmt.Sprintf("uid=%d", n.UID))
attr = append(attr, fmt.Sprintf("gid=%d", n.GID))
attr = append(attr, fmt.Sprintf("time=%d.%9d", n.MTime.Unix(), n.MTime.Nanosecond()))
fmt.Fprintln(fs.w, strings.Join(attr, " "))
return nil
}
// Converts filenames into an mtree-compatible format following the rules outined in mtree(5):
//
// When encoding file or pathnames, any backslash character or character outside of the 95
// printable ASCII characters must be encoded as a backslash followed by three octal digits.
// When reading mtree files, any appearance of a backslash followed by three octal digits should
// be converted into the corresponding character.
func mtreeFilename(s string) string {
var b strings.Builder
for _, c := range []byte(s) {
switch {
case c == '\\' || c == '#' || c < 32 || c > 126:
b.WriteString(fmt.Sprintf("\\%03o", c))
default:
b.WriteByte(c)
}
}
return b.String()
}