-
Notifications
You must be signed in to change notification settings - Fork 0
/
archive.go
180 lines (143 loc) · 4.26 KB
/
archive.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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
package main
import (
"bytes"
"fmt"
"syscall"
"time"
"github.com/scheiblingco/go-pxar/nodes"
"github.com/scheiblingco/go-pxar/pxar"
)
type PBSArchiveInterface interface {
// Add Folder
AddFolder(path string)
// Add File
AddFile(path string)
// Write to buffer
ToBuffer(buf *bytes.Buffer) error
// Create catalogue
WriteCatalogue(buf *bytes.Buffer) error
}
type PBSArchive struct {
// Directory Tree
Trees []nodes.NodeRef
// Filename of the resulting archive
Filename string
}
// Add a top-level folder to the archive
func (pa *PBSArchive) AddFolder(path string) {
pa.Trees = append(pa.Trees, nodes.ReadNode(path, true, ""))
}
// Add a "top-level" file to the archive
func (pa *PBSArchive) AddFile(path string) {
pa.Trees = append(pa.Trees, nodes.ReadNode(path, true, ""))
}
// Returns a single node that we can use as a top-level node in the archive.
// If the archive consists of a single folder, that folder is used as the top-level node.
// If there are multiple folders, a single file or a mix of files and folders at the top
// a virtual top-level dir will be created (owner: 0, group: 0, mode: 0777)
func (pa *PBSArchive) GetParentNode() (*nodes.FolderRef, error) {
var topTree *nodes.FolderRef
if _, ok := pa.Trees[0].(*nodes.FolderRef); ok && len(pa.Trees) == 1 {
if len(pa.Trees[0].GetChildren()) == 0 {
return nil, fmt.Errorf("only blank directory, no files to backup")
}
if _, ok := pa.Trees[0].(*nodes.FolderRef); !ok {
return nil, fmt.Errorf("top level item must be a directory. add multiple items/files/folders to create a virtual top directory")
}
topTree = pa.Trees[0].(*nodes.FolderRef)
} else {
// Create a virtual topdir if only files or multiple root folders are being backed up
for ti := range pa.Trees {
if _, ok := pa.Trees[ti].(*nodes.FolderRef); ok {
pa.Trees[ti].(*nodes.FolderRef).IsRoot = false
}
}
// The virtual top-level directory
topTree = &nodes.FolderRef{
IsRoot: true,
AbsPath: "/",
Name: pa.Filename,
Stat: nodes.Fstat{
Mode: syscall.S_IFDIR | 0777,
Uid: 0,
Gid: 0,
Size: 0,
MtimeSecs: uint64(time.Now().Unix()),
MtimeNsecs: uint32(time.Now().UnixNano()),
},
Children: pa.Trees,
}
}
return topTree, nil
}
// Writes the pxar archive to a buffer.
func (pa *PBSArchive) ToBuffer(buf *bytes.Buffer) error {
if len(pa.Trees) == 0 {
return fmt.Errorf("no items to write")
}
// Get the parent node, and call the recursive WritePayload function
// to write all data to the buffer
pos := uint64(0)
topTree, err := pa.GetParentNode()
if err != nil {
return err
}
topTree.IsRoot = true
topTree.Name = pa.Filename + ".didx"
_, err = topTree.WritePayload(buf, &pos)
if err != nil {
return err
}
fmt.Printf("Write buffer finished on pos %d with len %d\r\n", pos, buf.Len())
return nil
}
// Writes the pxar archive to a buffer.
func (pa *PBSArchive) ToChannel(ch chan []byte) error {
if len(pa.Trees) == 0 {
return fmt.Errorf("no items to write")
}
// Get the parent node, and call the recursive WritePayload function
// to write all data to the buffer
pos := uint64(0)
topTree, err := pa.GetParentNode()
if err != nil {
return err
}
topTree.IsRoot = true
topTree.Name = pa.Filename + ".didx"
_, err = topTree.WritePayloadChannel(ch, &pos)
if err != nil {
return err
}
fmt.Printf("Write buffer finished on pos %d\r\n", pos)
return nil
}
// Writes the catalogue to a buffer. The catalogue is a list of all the nodes in the archive
// together with references to their parent nodes. The catalogue is used to quickly locate
// a node in the archive by its path.
func (pa *PBSArchive) WriteCatalogue(buf *bytes.Buffer) error {
// Write magic header
buf.Write(pxar.CatalogMagic)
// Get parent
pos := uint64(0)
topTree, err := pa.GetParentNode()
if err != nil {
return err
}
topTree.IsRoot = true
topTree.Name = pa.Filename + ".didx"
lastBytes, n, err := topTree.WriteCatalogue(buf, &pos, pos)
if err != nil {
return err
}
bufLen := uint64(buf.Len())
buf.Write(nodes.MakeUvarint(uint64(len(lastBytes) + 1)))
buf.WriteByte(byte(0x01))
buf.Write(lastBytes)
buf.Write(nodes.MakeUvarint(uint64(bufLen)))
for buf.Len()%8 != 0 {
buf.WriteByte(0x00)
}
fmt.Printf("wrote %d bytes to catalogue\r\n", n)
return nil
}