-
Notifications
You must be signed in to change notification settings - Fork 5
/
repo.go
318 lines (265 loc) · 8.46 KB
/
repo.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
305
306
307
308
309
310
311
312
313
314
315
316
317
318
//nolint:goheader
// SPDX-FileCopyrightText: Copyright (c) 2023-2024, CIQ, Inc. All rights reserved
// SPDX-License-Identifier: Apache-2.0
package libostree
// #include <glib.h>
// #include <ostree.h>
import "C"
import (
"runtime"
"unsafe"
)
// RepoMode - The mode to use when creating a new repo.
// If an unknown mode is passed, RepoModeBare will be used silently.
//
// See https://ostreedev.github.io/ostree/formats/#the-archive-format
// See https://ostreedev.github.io/ostree/formats/#aside-bare-formats
type RepoMode string
func (r RepoMode) toC() C.OstreeRepoMode {
switch r {
case RepoModeBare:
return C.OSTREE_REPO_MODE_BARE
case RepoModeArchive:
return C.OSTREE_REPO_MODE_ARCHIVE
case RepoModeArchiveZ2:
return C.OSTREE_REPO_MODE_ARCHIVE_Z2
case RepoModeBareUser:
return C.OSTREE_REPO_MODE_BARE_USER
case RepoModeBareUserOnly:
return C.OSTREE_REPO_MODE_BARE_USER_ONLY
case RepoModeBareSplitXAttrs:
return C.OSTREE_REPO_MODE_BARE_SPLIT_XATTRS
default:
return C.OSTREE_REPO_MODE_BARE
}
}
const (
// RepoModeBare - The default mode. Keeps all file metadata. May require elevated privileges.
// The bare repository format is the simplest one. In this mode regular files are directly stored to disk, and all
// metadata (e.g. uid/gid and xattrs) is reflected to the filesystem. It allows further direct access to content and
// metadata, but it may require elevated privileges when writing objects to the repository.
RepoModeBare RepoMode = "bare"
// RepoModeArchive - The archive format. Best for small storage footprint. Mostly used for server-side repositories.
// The archive format simply gzip-compresses each content object. Metadata objects are stored uncompressed. This
// means that it’s easy to serve via static HTTP. Note: the repo config file still uses the historical term
// archive-z2 as mode. But this essentially indicates the modern archive format.
//
// When you commit new content, you will see new .filez files appearing in `objects/`.
RepoModeArchive RepoMode = "archive"
// RepoModeArchiveZ2 - Functionally equivalent to RepoModeArchive. Only useful for backwards compatibility.
RepoModeArchiveZ2 RepoMode = "archive-z2"
// RepoModeBareUser - Like RepoModeBare but ignore incoming uid/gid and xattrs.
// The bare-user format is a bit special in that the uid/gid and xattrs from the content are ignored. This is
// primarily useful if you want to have the same OSTree-managed content that can be run on a host system or an
// unprivileged container.
RepoModeBareUser RepoMode = "bare-user"
// RepoModeBareUserOnly - Like RepoModeBareUser. No metadata stored. Only useful for checkouts. Does not need xattrs.
// Same as BARE_USER, but all metadata is not stored, so it can only be used for user checkouts. Does not need xattrs.
RepoModeBareUserOnly RepoMode = "bare-user-only"
// RepoModeBareSplitXAttrs - Like RepoModeBare but store xattrs in a separate file.
// Similarly, the bare-split-xattrs format is a special mode where xattrs are stored as separate repository objects,
// and not directly reflected to the filesystem. This is primarily useful when transporting xattrs through lossy
// environments (e.g. tar streams and containerized environments). It also allows carrying security-sensitive xattrs
// (e.g. SELinux labels) out-of-band without involving OS filesystem logic.
RepoModeBareSplitXAttrs RepoMode = "bare-split-xattrs"
)
type Repo struct {
native *C.OstreeRepo
}
func fromNative(cRepo *C.OstreeRepo) *Repo {
repo := &Repo{
native: cRepo,
}
// Let the GB trigger free the cRepo for us when repo is freed.
runtime.SetFinalizer(repo, func(r *Repo) {
C.free(unsafe.Pointer(r.native))
})
return repo
}
// Init initializes & opens a new ostree repository at the given path.
//
// Create the underlying structure on disk for the repository, and call
// ostree_repo_open() on the result, preparing it for use.
//
// Since version 2016.8, this function will succeed on an existing
// repository, and finish creating any necessary files in a partially
// created repository. However, this function cannot change the mode
// of an existing repository, and will silently ignore an attempt to
// do so.
//
// Since 2017.9, "existing repository" is defined by the existence of an
// `objects` subdirectory.
//
// This function predates ostree_repo_create_at(). It is an error to call
// this function on a repository initialized via ostree_repo_open_at().
func Init(path string, mode RepoMode) (repo *Repo, err error) {
if path == "" {
return nil, ErrInvalidPath
}
cPathStr := C.CString(path)
defer C.free(unsafe.Pointer(cPathStr))
cPath := C.g_file_new_for_path(cPathStr)
defer C.g_object_unref(C.gpointer(cPath))
// Create a *C.OstreeRepo from the path
cRepo := C.ostree_repo_new(cPath)
defer func() {
if err != nil {
C.free(unsafe.Pointer(cRepo))
}
}()
var cErr *C.GError
if r := C.ostree_repo_create(cRepo, mode.toC(), nil, &cErr); r == C.gboolean(0) {
return nil, GoError(cErr)
}
return fromNative(cRepo), nil
}
// Open opens an ostree repository at the given path.
func Open(path string) (*Repo, error) {
if path == "" {
return nil, ErrInvalidPath
}
cPathStr := C.CString(path)
defer C.free(unsafe.Pointer(cPathStr))
cPath := C.g_file_new_for_path(cPathStr)
defer C.g_object_unref(C.gpointer(cPath))
// Create a *C.OstreeRepo from the path
cRepo := C.ostree_repo_new(cPath)
var cErr *C.GError
if r := C.ostree_repo_open(cRepo, nil, &cErr); r == C.gboolean(0) {
return nil, GoError(cErr)
}
return fromNative(cRepo), nil
}
// AddRemote adds a remote to the repository.
func (r *Repo) AddRemote(name, url string, opts ...Option) error {
cName := C.CString(name)
defer C.free(unsafe.Pointer(cName))
cURL := C.CString(url)
defer C.free(unsafe.Pointer(cURL))
options := toGVariant(opts...)
defer C.g_variant_unref(options)
var cErr *C.GError
/*
gboolean
ostree_repo_remote_add(OstreeRepo *self,
const char *name,
const char *url,
GVariant *options,
GCancellable *cancellable,
GError **error)
*/
if C.ostree_repo_remote_add(
r.native,
cName,
cURL,
options,
nil,
&cErr,
) == C.gboolean(0) {
return GoError(cErr)
}
return nil
}
// DeleteRemote deletes a remote from the repository.
func (r *Repo) DeleteRemote(name string) error {
cName := C.CString(name)
defer C.free(unsafe.Pointer(cName))
var cErr *C.GError
if C.ostree_repo_remote_delete(
r.native,
cName,
nil,
&cErr,
) == C.gboolean(0) {
return GoError(cErr)
}
return nil
}
// ReloadRemoteConfig reloads the remote configuration.
func (r *Repo) ReloadRemoteConfig() error {
var cErr *C.GError
if C.ostree_repo_reload_config(
r.native,
nil,
&cErr,
) == C.gboolean(0) {
return GoError(cErr)
}
return nil
}
// ListRemotes lists the remotes in the repository.
func (r *Repo) ListRemotes() []string {
var n C.guint
remotes := C.ostree_repo_remote_list(
r.native,
&n,
)
var ret []string
for {
if *remotes == nil {
break
}
ret = append(ret, C.GoString(*remotes))
remotes = (**C.char)(unsafe.Pointer(uintptr(unsafe.Pointer(remotes)) + unsafe.Sizeof(uintptr(0))))
}
return ret
}
type ListRefsExtFlags int
const (
ListRefsExtFlagsAliases = 1 << iota
ListRefsExtFlagsExcludeRemotes
ListRefsExtFlagsExcludeMirrors
ListRefsExtFlagsNone ListRefsExtFlags = 0
)
type Ref struct {
Name string
Checksum string
}
func (r *Repo) ListRefsExt(flags ListRefsExtFlags, prefix ...string) ([]Ref, error) {
var cPrefix *C.char
if len(prefix) > 0 {
cPrefix = C.CString(prefix[0])
defer C.free(unsafe.Pointer(cPrefix))
}
cFlags := (C.OstreeRepoListRefsExtFlags)(C.int(flags))
var cErr *C.GError
var outAllRefs *C.GHashTable
if C.ostree_repo_list_refs_ext(
r.native,
cPrefix,
&outAllRefs,
cFlags,
nil,
&cErr,
) == C.gboolean(0) {
return nil, GoError(cErr)
}
// iter is freed when g_hash_table_iter_next returns false
var iter C.GHashTableIter
C.g_hash_table_iter_init(&iter, outAllRefs)
var cRef C.gpointer
var cChecksum C.gpointer
var ret []Ref
for C.g_hash_table_iter_next(&iter, &cRef, &cChecksum) == C.gboolean(1) {
if cRef == nil {
break
}
ret = append(ret, Ref{
Name: C.GoString((*C.char)(cRef)),
Checksum: C.GoString((*C.char)(cChecksum)),
})
}
return ret, nil
}
func (r *Repo) RegenerateSummary() error {
var cErr *C.GError
if C.ostree_repo_regenerate_summary(
r.native,
nil,
nil,
&cErr,
) == C.gboolean(0) {
return GoError(cErr)
}
return nil
}