/
files.go
304 lines (267 loc) · 6.46 KB
/
files.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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
// Copyright 2017 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package swupd
import (
"fmt"
"os"
)
// TypeFlag describes the file type of a manifest entry.
// It matches the first byte in the flags field.
type TypeFlag uint8
// Valid values for TypeFlag.
const (
TypeUnset TypeFlag = iota
TypeFile
TypeDirectory
TypeLink
TypeManifest
// TODO: IManifests are deprecated. Remove them on format 30
TypeIManifest
)
var typeBytes = map[TypeFlag]byte{
TypeUnset: '.',
TypeFile: 'F',
TypeDirectory: 'D',
TypeLink: 'L',
TypeManifest: 'M',
TypeIManifest: 'I',
}
// StatusFlag describes whether a manifest entry is present or not.
// It matches the second byte in the flags field.
type StatusFlag uint8
// Valid values for StatusFlag.
const (
StatusUnset StatusFlag = iota
StatusDeleted
StatusGhosted
StatusExperimental
)
var statusBytes = map[StatusFlag]byte{
StatusUnset: '.',
StatusDeleted: 'd',
StatusGhosted: 'g',
StatusExperimental: 'e',
}
// ModifierFlag describes specific characteristics of a file, used later by
// swupd client when deciding how to update it.
// It matches the third byte in the flags field.
type ModifierFlag uint8
// Valid values for ModifierFlag.
const (
ModifierUnset ModifierFlag = iota
ModifierConfig
ModifierState
ModifierBoot
)
var modifierBytes = map[ModifierFlag]byte{
ModifierUnset: '.',
ModifierConfig: 'C',
ModifierState: 's',
ModifierBoot: 'b',
}
// RenameFlag describes the third position in the flag string
// and was used to represent a rename flag (deprecated) and is
// now used to represent a mixer-generated manifest to be merged
// with an upstream manifest via client-side mixer integration
type RenameFlag uint8
// Valid values for RenameFlag
const (
RenameUnset RenameFlag = iota
RenameSet
MixManifest
)
// File represents an entry in a manifest
type File struct {
Name string
Hash Hashval
Version uint32
// flags
Type TypeFlag
Status StatusFlag
Modifier ModifierFlag
Rename RenameFlag
// renames
RenameScore uint16
RenamePeer *File
Info os.FileInfo
DeltaPeer *File
}
// typeFromFlag return file type based on flag byte
func typeFromFlag(flag byte) (TypeFlag, error) {
switch flag {
case 'F':
return TypeFile, nil
case 'D':
return TypeDirectory, nil
case 'L':
return TypeLink, nil
case 'I':
return TypeIManifest, nil
case 'M':
return TypeManifest, nil
case '.':
return TypeUnset, nil
default:
return TypeUnset, fmt.Errorf("invalid file type flag: %v", flag)
}
}
func (t TypeFlag) String() string {
switch t {
case TypeFile:
return "F"
case TypeDirectory:
return "D"
case TypeLink:
return "L"
case TypeManifest:
return "M"
case TypeIManifest:
return "I"
case TypeUnset:
return "."
}
return "?"
}
// statusFromFlag return status based on flag byte
func statusFromFlag(flag byte) (StatusFlag, error) {
switch flag {
case 'd':
return StatusDeleted, nil
case 'g':
return StatusGhosted, nil
case 'e':
return StatusExperimental, nil
case '.':
return StatusUnset, nil
default:
return StatusUnset, fmt.Errorf("invalid file status flag: %v", flag)
}
}
func (s StatusFlag) String() string {
switch s {
case StatusDeleted:
return "d"
case StatusGhosted:
return "g"
case StatusExperimental:
return "e"
case StatusUnset:
return "."
}
return "?"
}
// modifierFromFlag return modifier from flag byte
func modifierFromFlag(flag byte) (ModifierFlag, error) {
switch flag {
case 'C':
return ModifierConfig, nil
case 's':
return ModifierState, nil
case 'b':
return ModifierBoot, nil
case '.':
return ModifierUnset, nil
default:
return ModifierUnset, fmt.Errorf("invalid file modifier flag: %v", flag)
}
}
// setRenameFromFlag set rename flag from flag byte
func renameFromFlag(flag byte) (RenameFlag, error) {
switch flag {
case 'r':
return RenameSet, nil
case '.':
return RenameUnset, nil
case 'm':
// this is a special flag used by mixer-integration client-side
return MixManifest, nil
default:
return RenameUnset, fmt.Errorf("invalid file rename flag: %v", flag)
}
}
// setFlags set flags from flag string
func (f *File) setFlags(flags string) error {
if len(flags) != 4 {
return fmt.Errorf("invalid number of flags: %v", flags)
}
var err error
// set file type
if f.Type, err = typeFromFlag(flags[0]); err != nil {
return err
}
// set status
if f.Status, err = statusFromFlag(flags[1]); err != nil {
return err
}
// set modifier
if f.Modifier, err = modifierFromFlag(flags[2]); err != nil {
return err
}
// set rename flag
if f.Rename, err = renameFromFlag(flags[3]); err != nil {
return err
}
return nil
}
// GetFlagString returns the flags in a format suitable for the Manifest
func (f *File) GetFlagString() (string, error) {
if f.Type == TypeUnset &&
f.Status == StatusUnset &&
f.Modifier == ModifierUnset {
return "", fmt.Errorf("no flags are set on file %s", f.Name)
}
// only write a '.' or 'm' to a manifest
// the 'r' flag is deprecated
renameByte := byte('.')
if f.Rename == MixManifest {
renameByte = 'm'
}
flagBytes := []byte{
typeBytes[f.Type],
statusBytes[f.Status],
modifierBytes[f.Modifier],
renameByte,
}
return string(flagBytes), nil
}
func (f *File) findFileNameInSlice(fs []*File) *File {
for _, file := range fs {
if file.Name == f.Name {
return file
}
}
return nil
}
func (f *File) isUnsupportedTypeChange() bool {
if f.DeltaPeer == nil {
// nothing to check, new or deleted file
return false
}
if f.Status == StatusDeleted || f.DeltaPeer.Status == StatusDeleted {
return false
}
if f.Type == f.DeltaPeer.Type {
return false
}
// file -> link OK
// file -> directory OK
// link -> file OK
// link -> directory OK
// directory -> anything TYPE CHANGE
return (f.DeltaPeer.Type == TypeDirectory && f.Type != TypeDirectory)
}
// Present tells if a file is present. Returns false if the file is deleted or ghosted.
func (f *File) Present() bool {
return f.Status != StatusDeleted && f.Status != StatusGhosted
}