Skip to content

Commit

Permalink
refactor: improve delete compaction logic
Browse files Browse the repository at this point in the history
Improve compaction via OptDeleteCompact. Do not fail when compaction is
requested on a data object that is not at the end of the data section.
When compaction is requested, always calculate the end of the data
section based on the in-use descriptors.
  • Loading branch information
tri-adam committed Jul 8, 2024
1 parent 8783e3b commit e8dad67
Show file tree
Hide file tree
Showing 8 changed files with 86 additions and 53 deletions.
49 changes: 10 additions & 39 deletions pkg/sif/delete.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2018-2023, Sylabs Inc. All rights reserved.
// Copyright (c) 2018-2024, Sylabs Inc. All rights reserved.
// Copyright (c) 2017, SingularityWare, LLC. All rights reserved.
// Copyright (c) 2017, Yannick Cote <yhcote@gmail.com> All rights reserved.
// This software is licensed under a 3-clause BSD license. Please consult the
Expand All @@ -8,32 +8,16 @@
package sif

import (
"errors"
"fmt"
"io"
"time"
)

// isLast return true if the data object associated with d is the last in f.
func (f *FileImage) isLast(d *rawDescriptor) bool {
isLast := true

end := d.Offset + d.Size
f.WithDescriptors(func(d Descriptor) bool {
isLast = d.Offset()+d.Size() <= end
return !isLast
})

return isLast
}

// zeroReader is an io.Reader that returns a stream of zero-bytes.
type zeroReader struct{}

func (zeroReader) Read(b []byte) (int, error) {
for i := range b {
b[i] = 0
}
clear(b)
return len(b), nil
}

Expand All @@ -47,13 +31,6 @@ func (f *FileImage) zero(d *rawDescriptor) error {
return err
}

// truncateAt truncates f at the start of the padded data object described by d.
func (f *FileImage) truncateAt(d *rawDescriptor) error {
start := d.Offset + d.Size - d.SizeWithPadding

return f.rw.Truncate(start)
}

// deleteOpts accumulates object deletion options.
type deleteOpts struct {
zero bool
Expand Down Expand Up @@ -97,8 +74,6 @@ func OptDeleteWithTime(t time.Time) DeleteOpt {
}
}

var errCompactNotImplemented = errors.New("compact not implemented for non-last object")

// DeleteObject deletes the data object with id, according to opts.
//
// To zero the data region of the deleted object, use OptDeleteZero. To compact the file following
Expand All @@ -125,24 +100,12 @@ func (f *FileImage) DeleteObject(id uint32, opts ...DeleteOpt) error {
return fmt.Errorf("%w", err)
}

if do.compact && !f.isLast(d) {
return fmt.Errorf("%w", errCompactNotImplemented)
}

if do.zero {
if err := f.zero(d); err != nil {
return fmt.Errorf("%w", err)
}
}

if do.compact {
if err := f.truncateAt(d); err != nil {
return fmt.Errorf("%w", err)
}

f.h.DataSize -= d.SizeWithPadding
}

f.h.DescriptorsFree++
f.h.ModifiedAt = do.t.Unix()

Expand All @@ -156,6 +119,14 @@ func (f *FileImage) DeleteObject(id uint32, opts ...DeleteOpt) error {
// Reset rawDescripter with empty struct
*d = rawDescriptor{}

if do.compact {
f.h.DataSize = f.calculatedDataSize()

if err := f.rw.Truncate(f.h.DataOffset + f.h.DataSize); err != nil {
return fmt.Errorf("%w", err)
}
}

if err := f.writeDescriptors(); err != nil {
return fmt.Errorf("%w", err)
}
Expand Down
90 changes: 76 additions & 14 deletions pkg/sif/delete_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2018-2023, Sylabs Inc. All rights reserved.
// Copyright (c) 2018-2024, Sylabs Inc. All rights reserved.
// This software is licensed under a 3-clause BSD license. Please consult the
// LICENSE file distributed with the sources of this project regarding your
// rights to use or distribute this software.
Expand All @@ -17,7 +17,7 @@ func TestDeleteObject(t *testing.T) {
tests := []struct {
name string
createOpts []CreateOpt
id uint32
ids []uint32
opts []DeleteOpt
wantErr error
}{
Expand All @@ -26,44 +26,104 @@ func TestDeleteObject(t *testing.T) {
createOpts: []CreateOpt{
OptCreateDeterministic(),
},
id: 1,
ids: []uint32{1},
wantErr: ErrObjectNotFound,
},
{
name: "Zero",
name: "Compact",
createOpts: []CreateOpt{
OptCreateDeterministic(),
OptCreateWithDescriptors(
getDescriptorInput(t, DataGeneric, []byte{0xfa, 0xce}),
getDescriptorInput(t, DataGeneric, []byte{0xfe, 0xed}),
),
},
ids: []uint32{1, 2},
opts: []DeleteOpt{
OptDeleteCompact(true),
},
},
{
name: "OneZero",
createOpts: []CreateOpt{
OptCreateDeterministic(),
OptCreateWithDescriptors(
getDescriptorInput(t, DataGeneric, []byte{0xfa, 0xce}),
getDescriptorInput(t, DataGeneric, []byte{0xfe, 0xed}),
),
},
id: 1,
ids: []uint32{1},
opts: []DeleteOpt{
OptDeleteZero(true),
},
},
{
name: "Compact",
name: "OneCompact",
createOpts: []CreateOpt{
OptCreateDeterministic(),
OptCreateWithDescriptors(
getDescriptorInput(t, DataGeneric, []byte{0xfa, 0xce}),
getDescriptorInput(t, DataGeneric, []byte{0xfe, 0xed}),
),
},
id: 1,
ids: []uint32{1},
opts: []DeleteOpt{
OptDeleteCompact(true),
},
},
{
name: "ZeroCompact",
name: "OneZeroCompact",
createOpts: []CreateOpt{
OptCreateDeterministic(),
OptCreateWithDescriptors(
getDescriptorInput(t, DataGeneric, []byte{0xfa, 0xce}),
getDescriptorInput(t, DataGeneric, []byte{0xfe, 0xed}),
),
},
ids: []uint32{1},
opts: []DeleteOpt{
OptDeleteZero(true),
OptDeleteCompact(true),
},
},
{
name: "TwoZero",
createOpts: []CreateOpt{
OptCreateDeterministic(),
OptCreateWithDescriptors(
getDescriptorInput(t, DataGeneric, []byte{0xfa, 0xce}),
getDescriptorInput(t, DataGeneric, []byte{0xfe, 0xed}),
),
},
ids: []uint32{2},
opts: []DeleteOpt{
OptDeleteZero(true),
},
},
{
name: "TwoCompact",
createOpts: []CreateOpt{
OptCreateDeterministic(),
OptCreateWithDescriptors(
getDescriptorInput(t, DataGeneric, []byte{0xfa, 0xce}),
getDescriptorInput(t, DataGeneric, []byte{0xfe, 0xed}),
),
},
ids: []uint32{2},
opts: []DeleteOpt{
OptDeleteCompact(true),
},
},
{
name: "TwoZeroCompact",
createOpts: []CreateOpt{
OptCreateDeterministic(),
OptCreateWithDescriptors(
getDescriptorInput(t, DataGeneric, []byte{0xfa, 0xce}),
getDescriptorInput(t, DataGeneric, []byte{0xfe, 0xed}),
),
},
id: 1,
ids: []uint32{2},
opts: []DeleteOpt{
OptDeleteZero(true),
OptDeleteCompact(true),
Expand All @@ -78,7 +138,7 @@ func TestDeleteObject(t *testing.T) {
),
OptCreateWithTime(time.Unix(946702800, 0)),
},
id: 1,
ids: []uint32{1},
opts: []DeleteOpt{
OptDeleteDeterministic(),
},
Expand All @@ -91,7 +151,7 @@ func TestDeleteObject(t *testing.T) {
getDescriptorInput(t, DataGeneric, []byte{0xfa, 0xce}),
),
},
id: 1,
ids: []uint32{1},
opts: []DeleteOpt{
OptDeleteWithTime(time.Unix(946702800, 0)),
},
Expand All @@ -106,7 +166,7 @@ func TestDeleteObject(t *testing.T) {
),
),
},
id: 1,
ids: []uint32{1},
},
}

Expand All @@ -119,8 +179,10 @@ func TestDeleteObject(t *testing.T) {
t.Fatal(err)
}

if got, want := f.DeleteObject(tt.id, tt.opts...), tt.wantErr; !errors.Is(got, want) {
t.Errorf("got error %v, want %v", got, want)
for _, id := range tt.ids {
if got, want := f.DeleteObject(id, tt.opts...), tt.wantErr; !errors.Is(got, want) {
t.Errorf("got error %v, want %v", got, want)
}
}

if err := f.UnloadContainer(); err != nil {
Expand Down
Binary file not shown.
Binary file added pkg/sif/testdata/TestDeleteObject/OneZero.golden
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added pkg/sif/testdata/TestDeleteObject/TwoZero.golden
Binary file not shown.
Binary file not shown.

0 comments on commit e8dad67

Please sign in to comment.