Add a new generic container registry client via a new `container` package. Use this to create a command line utility as well as a new upload target for container registries. The code uses the github.com/containers/* project and packages to interact with container registires that is also used by skopeo, podman et al. One if the dependencies is `proglottis/gpgme` that is using cgo to bind libgpgme, so we have to add the corresponding devel package to the BuildRequires as well as installing it on CI. Checks will follow later via an integration test.
197 lines
4.9 KiB
Go
197 lines
4.9 KiB
Go
package gpgme
|
|
|
|
// #include <string.h>
|
|
// #include <gpgme.h>
|
|
// #include <errno.h>
|
|
// #include "go_gpgme.h"
|
|
import "C"
|
|
|
|
import (
|
|
"io"
|
|
"os"
|
|
"runtime"
|
|
"unsafe"
|
|
)
|
|
|
|
const (
|
|
SeekSet = C.SEEK_SET
|
|
SeekCur = C.SEEK_CUR
|
|
SeekEnd = C.SEEK_END
|
|
)
|
|
|
|
//export gogpgme_readfunc
|
|
func gogpgme_readfunc(handle, buffer unsafe.Pointer, size C.size_t) C.ssize_t {
|
|
d := callbackLookup(uintptr(handle)).(*Data)
|
|
if len(d.buf) < int(size) {
|
|
d.buf = make([]byte, size)
|
|
}
|
|
n, err := d.r.Read(d.buf[:size])
|
|
if err != nil && err != io.EOF {
|
|
C.gpgme_err_set_errno(C.EIO)
|
|
return -1
|
|
}
|
|
C.memcpy(buffer, unsafe.Pointer(&d.buf[0]), C.size_t(n))
|
|
return C.ssize_t(n)
|
|
}
|
|
|
|
//export gogpgme_writefunc
|
|
func gogpgme_writefunc(handle, buffer unsafe.Pointer, size C.size_t) C.ssize_t {
|
|
d := callbackLookup(uintptr(handle)).(*Data)
|
|
if len(d.buf) < int(size) {
|
|
d.buf = make([]byte, size)
|
|
}
|
|
C.memcpy(unsafe.Pointer(&d.buf[0]), buffer, C.size_t(size))
|
|
n, err := d.w.Write(d.buf[:size])
|
|
if err != nil && err != io.EOF {
|
|
C.gpgme_err_set_errno(C.EIO)
|
|
return -1
|
|
}
|
|
return C.ssize_t(n)
|
|
}
|
|
|
|
//export gogpgme_seekfunc
|
|
func gogpgme_seekfunc(handle unsafe.Pointer, offset C.gpgme_off_t, whence C.int) C.gpgme_off_t {
|
|
d := callbackLookup(uintptr(handle)).(*Data)
|
|
n, err := d.s.Seek(int64(offset), int(whence))
|
|
if err != nil {
|
|
C.gpgme_err_set_errno(C.EIO)
|
|
return -1
|
|
}
|
|
return C.gpgme_off_t(n)
|
|
}
|
|
|
|
// The Data buffer used to communicate with GPGME
|
|
type Data struct {
|
|
dh C.gpgme_data_t // WARNING: Call runtime.KeepAlive(d) after ANY passing of d.dh to C
|
|
buf []byte
|
|
cbs C.struct_gpgme_data_cbs
|
|
r io.Reader
|
|
w io.Writer
|
|
s io.Seeker
|
|
cbc uintptr // WARNING: Call runtime.KeepAlive(d) after ANY use of d.cbc in C (typically via d.dh)
|
|
}
|
|
|
|
func newData() *Data {
|
|
d := &Data{}
|
|
runtime.SetFinalizer(d, (*Data).Close)
|
|
return d
|
|
}
|
|
|
|
// NewData returns a new memory based data buffer
|
|
func NewData() (*Data, error) {
|
|
d := newData()
|
|
return d, handleError(C.gpgme_data_new(&d.dh))
|
|
}
|
|
|
|
// NewDataFile returns a new file based data buffer
|
|
func NewDataFile(f *os.File) (*Data, error) {
|
|
d := newData()
|
|
return d, handleError(C.gpgme_data_new_from_fd(&d.dh, C.int(f.Fd())))
|
|
}
|
|
|
|
// NewDataBytes returns a new memory based data buffer that contains `b` bytes
|
|
func NewDataBytes(b []byte) (*Data, error) {
|
|
d := newData()
|
|
var cb *C.char
|
|
if len(b) != 0 {
|
|
cb = (*C.char)(unsafe.Pointer(&b[0]))
|
|
}
|
|
return d, handleError(C.gpgme_data_new_from_mem(&d.dh, cb, C.size_t(len(b)), 1))
|
|
}
|
|
|
|
// NewDataReader returns a new callback based data buffer
|
|
func NewDataReader(r io.Reader) (*Data, error) {
|
|
d := newData()
|
|
d.r = r
|
|
d.cbs.read = C.gpgme_data_read_cb_t(C.gogpgme_readfunc)
|
|
cbc := callbackAdd(d)
|
|
d.cbc = cbc
|
|
return d, handleError(C.gogpgme_data_new_from_cbs(&d.dh, &d.cbs, C.uintptr_t(cbc)))
|
|
}
|
|
|
|
// NewDataWriter returns a new callback based data buffer
|
|
func NewDataWriter(w io.Writer) (*Data, error) {
|
|
d := newData()
|
|
d.w = w
|
|
d.cbs.write = C.gpgme_data_write_cb_t(C.gogpgme_writefunc)
|
|
cbc := callbackAdd(d)
|
|
d.cbc = cbc
|
|
return d, handleError(C.gogpgme_data_new_from_cbs(&d.dh, &d.cbs, C.uintptr_t(cbc)))
|
|
}
|
|
|
|
// NewDataReadWriter returns a new callback based data buffer
|
|
func NewDataReadWriter(rw io.ReadWriter) (*Data, error) {
|
|
d := newData()
|
|
d.r = rw
|
|
d.w = rw
|
|
d.cbs.read = C.gpgme_data_read_cb_t(C.gogpgme_readfunc)
|
|
d.cbs.write = C.gpgme_data_write_cb_t(C.gogpgme_writefunc)
|
|
cbc := callbackAdd(d)
|
|
d.cbc = cbc
|
|
return d, handleError(C.gogpgme_data_new_from_cbs(&d.dh, &d.cbs, C.uintptr_t(cbc)))
|
|
}
|
|
|
|
// NewDataReadWriteSeeker returns a new callback based data buffer
|
|
func NewDataReadWriteSeeker(rw io.ReadWriteSeeker) (*Data, error) {
|
|
d := newData()
|
|
d.r = rw
|
|
d.w = rw
|
|
d.s = rw
|
|
d.cbs.read = C.gpgme_data_read_cb_t(C.gogpgme_readfunc)
|
|
d.cbs.write = C.gpgme_data_write_cb_t(C.gogpgme_writefunc)
|
|
d.cbs.seek = C.gpgme_data_seek_cb_t(C.gogpgme_seekfunc)
|
|
cbc := callbackAdd(d)
|
|
d.cbc = cbc
|
|
return d, handleError(C.gogpgme_data_new_from_cbs(&d.dh, &d.cbs, C.uintptr_t(cbc)))
|
|
}
|
|
|
|
// Close releases any resources associated with the data buffer
|
|
func (d *Data) Close() error {
|
|
if d.dh == nil {
|
|
return nil
|
|
}
|
|
if d.cbc > 0 {
|
|
callbackDelete(d.cbc)
|
|
}
|
|
_, err := C.gpgme_data_release(d.dh)
|
|
runtime.KeepAlive(d)
|
|
d.dh = nil
|
|
return err
|
|
}
|
|
|
|
func (d *Data) Write(p []byte) (int, error) {
|
|
n, err := C.gpgme_data_write(d.dh, unsafe.Pointer(&p[0]), C.size_t(len(p)))
|
|
runtime.KeepAlive(d)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
if n == 0 {
|
|
return 0, io.EOF
|
|
}
|
|
return int(n), nil
|
|
}
|
|
|
|
func (d *Data) Read(p []byte) (int, error) {
|
|
n, err := C.gpgme_data_read(d.dh, unsafe.Pointer(&p[0]), C.size_t(len(p)))
|
|
runtime.KeepAlive(d)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
if n == 0 {
|
|
return 0, io.EOF
|
|
}
|
|
return int(n), nil
|
|
}
|
|
|
|
func (d *Data) Seek(offset int64, whence int) (int64, error) {
|
|
n, err := C.gogpgme_data_seek(d.dh, C.gpgme_off_t(offset), C.int(whence))
|
|
runtime.KeepAlive(d)
|
|
return int64(n), err
|
|
}
|
|
|
|
// Name returns the associated filename if any
|
|
func (d *Data) Name() string {
|
|
res := C.GoString(C.gpgme_data_get_file_name(d.dh))
|
|
runtime.KeepAlive(d)
|
|
return res
|
|
}
|