Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Functional Arguments for CreateContainer #31

Merged
merged 4 commits into from
Jul 6, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/siftool/siftool.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func getVersion() *cobra.Command {
Args: cobra.ExactArgs(0),
Run: func(cmd *cobra.Command, args []string) {
cmd.Printf("siftool version %s %s/%s\n", version, runtime.GOOS, runtime.GOARCH)
cmd.Printf("SIF spec versions supported: <= %s\n", sif.HdrVersion)
cmd.Printf("SIF spec versions supported: <= %s\n", sif.CurrentVersion)
},
DisableFlagsInUseLine: true,
}
Expand Down
15 changes: 1 addition & 14 deletions internal/app/siftool/modif.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,12 @@ package siftool
import (
"io"

"github.com/google/uuid"
"github.com/sylabs/sif/v2/pkg/sif"
)

// New creates a new empty SIF file.
func (*App) New(path string) error {
id, err := uuid.NewRandom()
if err != nil {
return err
}

cinfo := sif.CreateInfo{
Pathname: path,
Launchstr: sif.HdrLaunch,
Sifversion: sif.HdrVersion,
ID: id,
}

_, err = sif.CreateContainer(cinfo)
_, err := sif.CreateContainer(path)
return err
}

Expand Down
16 changes: 1 addition & 15 deletions pkg/integrity/testdata/gen_sifs.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,13 @@ import (
"os"
"path/filepath"

"github.com/google/uuid"
"github.com/sylabs/sif/v2/pkg/integrity"
"github.com/sylabs/sif/v2/pkg/sif"
"golang.org/x/crypto/openpgp"
)

func createImage(path string, dis []sif.DescriptorInput) error {
id, err := uuid.NewV4()
if err != nil {
return err
}

ci := sif.CreateInfo{
Pathname: path,
Launchstr: sif.HdrLaunch,
Sifversion: sif.HdrVersion,
ID: id,
InputDescr: dis,
}

_, err = sif.CreateContainer(ci)
_, err := sif.CreateContainer(path, sif.WithDescriptors(dis...))
return err
}

Expand Down
109 changes: 81 additions & 28 deletions pkg/sif/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import (
"path"
"strconv"
"time"

"github.com/google/uuid"
)

// Find next offset aligned to block size.
Expand Down Expand Up @@ -216,55 +218,106 @@ func writeHeader(fimg *FileImage) error {
return nil
}

// CreateContainer is responsible for the creation of a new SIF container
// file. It takes the creation information specification as input
// and produces an output file as specified in the input data.
func CreateContainer(cinfo CreateInfo) (fimg *FileImage, err error) {
fimg = &FileImage{}
fimg.DescrArr = make([]Descriptor, DescrNumEntries)
// createOpts accumulates container creation options.
type createOpts struct {
ID uuid.UUID
InputDescr []DescriptorInput
GetTime func() time.Time
}

// CreateOpt are used to specify container creation options.
type CreateOpt func(*createOpts) error

// WithID specifies id as the unique ID.
func WithID(id string) CreateOpt {
return func(co *createOpts) error {
id, err := uuid.Parse(id)
co.ID = id
return err
}
}

// WithDescriptors appends dis to the list of descriptors.
func WithDescriptors(dis ...DescriptorInput) CreateOpt {
return func(co *createOpts) error {
co.InputDescr = append(co.InputDescr, dis...)
return nil
}
}

// WithTimeFunc specifies fn as the function to obtain timestamps.
func WithTimeFunc(fn func() time.Time) CreateOpt {
return func(co *createOpts) error {
co.GetTime = fn
return nil
}
}

// CreateContainer creates a new SIF container file at path, according to opts.
func CreateContainer(path string, opts ...CreateOpt) (*FileImage, error) {
id, err := uuid.NewRandom()
if err != nil {
return nil, err
}

co := createOpts{
ID: id,
GetTime: time.Now,
}

for _, opt := range opts {
if err := opt(&co); err != nil {
return nil, err
}
}

now := co.GetTime()

f := &FileImage{}
f.DescrArr = make([]Descriptor, DescrNumEntries)

// Prepare a fresh global header
copy(fimg.Header.Launch[:], cinfo.Launchstr)
copy(fimg.Header.Magic[:], HdrMagic)
copy(fimg.Header.Version[:], cinfo.Sifversion)
copy(fimg.Header.Arch[:], HdrArchUnknown)
copy(fimg.Header.ID[:], cinfo.ID[:])
fimg.Header.Ctime = time.Now().Unix()
fimg.Header.Mtime = time.Now().Unix()
fimg.Header.Dfree = DescrNumEntries
fimg.Header.Dtotal = DescrNumEntries
fimg.Header.Descroff = DescrStartOffset
fimg.Header.Dataoff = DataStartOffset
copy(f.Header.Launch[:], hdrLaunch)
copy(f.Header.Magic[:], hdrMagic)
copy(f.Header.Version[:], CurrentVersion.bytes())
copy(f.Header.Arch[:], HdrArchUnknown)
f.Header.ID = id
f.Header.Ctime = now.Unix()
f.Header.Mtime = now.Unix()
f.Header.Dfree = DescrNumEntries
f.Header.Dtotal = DescrNumEntries
f.Header.Descroff = DescrStartOffset
f.Header.Dataoff = DataStartOffset

// Create container file
fimg.Fp, err = os.OpenFile(cinfo.Pathname, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o755)
f.Fp, err = os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o755)
if err != nil {
return nil, fmt.Errorf("container file creation failed: %s", err)
}
defer fimg.Fp.Close()
defer f.Fp.Close()

// set file pointer to start of data section */
if _, err = fimg.Fp.Seek(DataStartOffset, 0); err != nil {
if _, err = f.Fp.Seek(DataStartOffset, 0); err != nil {
return nil, fmt.Errorf("setting file offset pointer to DataStartOffset: %s", err)
}

for _, v := range cinfo.InputDescr {
if err = createDescriptor(fimg, v); err != nil {
return
for _, v := range co.InputDescr {
if err := createDescriptor(f, v); err != nil {
return nil, err
}
}

// Write down the descriptor array
if err = writeDescriptors(fimg); err != nil {
return
if err := writeDescriptors(f); err != nil {
return nil, err
}

// Write down global header to file
if err = writeHeader(fimg); err != nil {
return
if err := writeHeader(f); err != nil {
return nil, err
}

return
return f, nil
}

func zeroData(fimg *FileImage, descr *Descriptor) error {
Expand Down
29 changes: 4 additions & 25 deletions pkg/sif/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ import (
"os"
"runtime"
"testing"

"github.com/google/uuid"
)

const (
Expand Down Expand Up @@ -65,22 +63,9 @@ func TestCreateContainer(t *testing.T) {
defer os.Remove(f.Name())
f.Close()

id, err := uuid.NewRandom()
if err != nil {
t.Fatal(err)
}

// general info for the new SIF file creation
cinfo := CreateInfo{
Pathname: f.Name(),
Launchstr: HdrLaunch,
Sifversion: HdrVersion,
ID: id,
}

// test container creation without any input descriptors
if _, err := CreateContainer(cinfo); err != nil {
t.Error("CreateContainer(cinfo): should allow empty input descriptor list")
if _, err := CreateContainer(f.Name()); err != nil {
t.Errorf("failed to create container: %v", err)
}

// data we need to create a definition file descriptor
Expand All @@ -106,9 +91,6 @@ func TestCreateContainer(t *testing.T) {
}
definput.Size = fi.Size()

// add this descriptor input element to creation descriptor slice
cinfo.InputDescr = append(cinfo.InputDescr, definput)

// data we need to create a system partition descriptor
parinput := DescriptorInput{
Datatype: DataPartition,
Expand Down Expand Up @@ -137,12 +119,9 @@ func TestCreateContainer(t *testing.T) {
t.Errorf("CreateContainer(cinfo): can't set extra info: %s", err)
}

// add this descriptor input element to creation descriptor slice
cinfo.InputDescr = append(cinfo.InputDescr, parinput)

// test container creation with two partition input descriptors
if _, err := CreateContainer(cinfo); err != nil {
t.Errorf("CreateContainer(cinfo): CreateContainer(): %s", err)
if _, err := CreateContainer(f.Name(), WithDescriptors(definput, parinput)); err != nil {
t.Errorf("failed to create container: %v", err)
}
}

Expand Down
4 changes: 2 additions & 2 deletions pkg/sif/load.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,11 @@ func readDescriptors(r io.ReaderAt, fimg *FileImage) error {

// isValidSif looks at key fields from the global header to assess SIF validity.
func isValidSif(f *FileImage) error {
if got, want := trimZeroBytes(f.Header.Magic[:]), HdrMagic; got != want {
if got, want := trimZeroBytes(f.Header.Magic[:]), hdrMagic; got != want {
return fmt.Errorf("invalid SIF file: Magic |%v| want |%v|", got, want)
}

if got, want := trimZeroBytes(f.Header.Version[:]), HdrVersion; got > want {
if got, want := trimZeroBytes(f.Header.Version[:]), CurrentVersion.String(); got > want {
return fmt.Errorf("invalid SIF file: Version %s want <= %s", got, want)
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/sif/load_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ func TestLoadContainerInvalidMagic(t *testing.T) {
// ... and edit the magic to make it invalid. Instead of
// exploring all kinds of invalid, simply mess with the last
// byte, as this would catch off-by-one errors in the code.
copy(content[HdrLaunchLen:HdrLaunchLen+HdrMagicLen], "SIF_MAGIX")
copy(content[hdrLaunchLen:hdrLaunchLen+hdrMagicLen], "SIF_MAGIX")

fp := &mockSifReadWriter{
buf: content,
Expand Down
6 changes: 3 additions & 3 deletions pkg/sif/lookup.go
Original file line number Diff line number Diff line change
Expand Up @@ -302,15 +302,15 @@ func (d *Descriptor) GetPartType() (Parttype, error) {
}

// GetArch extracts the Arch field from the Extra field of a Partition Descriptor.
func (d *Descriptor) GetArch() ([HdrArchLen]byte, error) {
func (d *Descriptor) GetArch() ([hdrArchLen]byte, error) {
if d.Datatype != DataPartition {
return [HdrArchLen]byte{}, fmt.Errorf("expected DataPartition, got %v", d.Datatype)
return [hdrArchLen]byte{}, fmt.Errorf("expected DataPartition, got %v", d.Datatype)
}

var pinfo Partition
b := bytes.NewReader(d.Extra[:])
if err := binary.Read(b, binary.LittleEndian, &pinfo); err != nil {
return [HdrArchLen]byte{}, fmt.Errorf("while extracting Partition extra info: %s", err)
return [hdrArchLen]byte{}, fmt.Errorf("while extracting Partition extra info: %s", err)
}

return pinfo.Arch, nil
Expand Down
2 changes: 1 addition & 1 deletion pkg/sif/lookup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,7 @@ func TestGetArch(t *testing.T) {
}

if trimZeroBytes(arch[:]) != HdrArchAMD64 {
t.Logf("|%s|%s|\n", arch[:HdrArchLen-1], HdrArchAMD64)
t.Logf("|%s|%s|\n", arch[:hdrArchLen-1], HdrArchAMD64)
t.Error("part.GetArch() should have returned 'HdrArchAMD64':", err)
}

Expand Down
Loading