/
file_info.go
143 lines (125 loc) · 2.83 KB
/
file_info.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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
package filetree
import (
"archive/tar"
"io"
"os"
"github.com/cespare/xxhash"
"github.com/sirupsen/logrus"
)
// FileInfo contains tar metadata for a specific FileNode
type FileInfo struct {
Path string
TypeFlag byte
Linkname string
hash uint64
Size int64
Mode os.FileMode
Uid int
Gid int
IsDir bool
}
// NewFileInfoFromTarHeader extracts the metadata from a tar header and file contents and generates a new FileInfo object.
func NewFileInfoFromTarHeader(reader *tar.Reader, header *tar.Header, path string) FileInfo {
var hash uint64
if header.Typeflag != tar.TypeDir {
hash = getHashFromReader(reader)
}
return FileInfo{
Path: path,
TypeFlag: header.Typeflag,
Linkname: header.Linkname,
hash: hash,
Size: header.FileInfo().Size(),
Mode: header.FileInfo().Mode(),
Uid: header.Uid,
Gid: header.Gid,
IsDir: header.FileInfo().IsDir(),
}
}
func NewFileInfo(realPath, path string, info os.FileInfo) FileInfo {
var err error
// todo: don't use tar types here, create our own...
var fileType byte
var linkName string
var size int64
if info.Mode()&os.ModeSymlink != 0 {
fileType = tar.TypeSymlink
linkName, err = os.Readlink(realPath)
if err != nil {
logrus.Panic("unable to read link:", realPath, err)
}
} else if info.IsDir() {
fileType = tar.TypeDir
} else {
fileType = tar.TypeReg
size = info.Size()
}
var hash uint64
if fileType != tar.TypeDir {
file, err := os.Open(realPath)
if err != nil {
logrus.Panic("unable to read file:", realPath)
}
defer file.Close()
hash = getHashFromReader(file)
}
return FileInfo{
Path: path,
TypeFlag: fileType,
Linkname: linkName,
hash: hash,
Size: size,
Mode: info.Mode(),
// todo: support UID/GID
Uid: -1,
Gid: -1,
IsDir: info.IsDir(),
}
}
// Copy duplicates a FileInfo
func (data *FileInfo) Copy() *FileInfo {
if data == nil {
return nil
}
return &FileInfo{
Path: data.Path,
TypeFlag: data.TypeFlag,
Linkname: data.Linkname,
hash: data.hash,
Size: data.Size,
Mode: data.Mode,
Uid: data.Uid,
Gid: data.Gid,
IsDir: data.IsDir,
}
}
// Compare determines the DiffType between two FileInfos based on the type and contents of each given FileInfo
func (data *FileInfo) Compare(other FileInfo) DiffType {
if data.TypeFlag == other.TypeFlag {
if data.hash == other.hash &&
data.Mode == other.Mode &&
data.Uid == other.Uid &&
data.Gid == other.Gid {
return Unmodified
}
}
return Modified
}
func getHashFromReader(reader io.Reader) uint64 {
h := xxhash.New()
buf := make([]byte, 1024)
for {
n, err := reader.Read(buf)
if err != nil && err != io.EOF {
logrus.Panic(err)
}
if n == 0 {
break
}
_, err = h.Write(buf[:n])
if err != nil {
logrus.Panic(err)
}
}
return h.Sum64()
}