diff --git a/filesystem/iso9660/finalize.go b/filesystem/iso9660/finalize.go index 8a5b5d9..39b5967 100644 --- a/filesystem/iso9660/finalize.go +++ b/filesystem/iso9660/finalize.go @@ -34,6 +34,8 @@ type FinalizeOptions struct { ElTorito *ElTorito // VolumeIdentifier custom volume name, defaults to "ISOIMAGE" VolumeIdentifier string + // PublisherIdentifier custom publisher identifier written to the PVD + PublisherIdentifier string } // finalizeFileInfo is a file info useful for finalization @@ -648,6 +650,7 @@ func (fsm *FileSystem) Finalize(options FinalizeOptions) error { if options.VolumeIdentifier != "" { volIdentifier = options.VolumeIdentifier } + publisherIdentifier := options.PublisherIdentifier for _, e := range files { e.location = location @@ -793,7 +796,7 @@ func (fsm *FileSystem) Finalize(options FinalizeOptions) error { pathTableMLocation: pathTableMLocation, pathTableMOptionalLocation: 0, volumeSetIdentifier: "", - publisherIdentifier: "", + publisherIdentifier: publisherIdentifier, preparerIdentifier: version.AppName, applicationIdentifier: "", copyrightFile: "", // 37 bytes diff --git a/filesystem/iso9660/finalize_test.go b/filesystem/iso9660/finalize_test.go index ecb71a5..90a3837 100644 --- a/filesystem/iso9660/finalize_test.go +++ b/filesystem/iso9660/finalize_test.go @@ -21,6 +21,12 @@ var ( intImage = os.Getenv("TEST_IMAGE") ) +const ( + testISOBlockSize = 2048 + testPVDPublisherFieldFrom = 318 + testPVDPublisherFieldTo = 446 +) + // test creating an iso with el torito boot func TestFinalizeElTorito(t *testing.T) { finalizeElTorito(t, "") @@ -31,6 +37,63 @@ func TestFinalizeElTorito(t *testing.T) { finalizeElTorito(t, dir) } +func TestFinalizePublisherIdentifier(t *testing.T) { + t.Run("custom publisher identifier", func(t *testing.T) { + f, err := os.CreateTemp("", "iso_finalize_publisher_identifier") + if err != nil { + t.Fatalf("Failed to create tmpfile: %v", err) + } + defer os.Remove(f.Name()) + + b := file.New(f, false) + fs, err := iso9660.Create(b, 0, 0, testISOBlockSize, "") + if err != nil { + t.Fatalf("Failed to iso9660.Create: %v", err) + } + + publisher := "go-diskfs" + err = fs.Finalize(iso9660.FinalizeOptions{PublisherIdentifier: publisher}) + if err != nil { + t.Fatal("unexpected error fs.Finalize()", err) + } + + publisherValue, err := readPublisherIdentifierFromPVD(f) + if err != nil { + t.Fatalf("unable to read publisher identifier: %v", err) + } + if publisherValue != publisher { + t.Fatalf("unexpected publisher identifier, got %q expected %q", publisherValue, publisher) + } + }) + + t.Run("default publisher identifier empty", func(t *testing.T) { + f, err := os.CreateTemp("", "iso_finalize_default_publisher_identifier") + if err != nil { + t.Fatalf("Failed to create tmpfile: %v", err) + } + defer os.Remove(f.Name()) + + b := file.New(f, false) + fs, err := iso9660.Create(b, 0, 0, testISOBlockSize, "") + if err != nil { + t.Fatalf("Failed to iso9660.Create: %v", err) + } + + err = fs.Finalize(iso9660.FinalizeOptions{}) + if err != nil { + t.Fatal("unexpected error fs.Finalize()", err) + } + + publisherValue, err := readPublisherIdentifierFromPVD(f) + if err != nil { + t.Fatalf("unable to read publisher identifier: %v", err) + } + if publisherValue != "" { + t.Fatalf("unexpected default publisher identifier, got %q expected empty", publisherValue) + } + }) +} + func TestFinalizeElToritoWithInaccurateTmpDir(t *testing.T) { finalizeElTorito(t, "") dir, err := os.MkdirTemp("/tmp//", "workspace") @@ -87,9 +150,6 @@ func finalizeElTorito(t *testing.T, workspace string) { if err != nil { t.Fatal("unexpected error fs.Finalize()", err) } - if err != nil { - t.Fatalf("error trying to Stat() iso file: %v", err) - } // now check the contents fs, err = iso9660.Read(b, 0, 0, 2048) @@ -540,6 +600,17 @@ func validateIso(t *testing.T, f *os.File) { } } +func readPublisherIdentifierFromPVD(f *os.File) (string, error) { + pvd := make([]byte, testISOBlockSize) + _, err := f.ReadAt(pvd, 16*testISOBlockSize) + if err != nil && err != io.EOF { + return "", err + } + + field := pvd[testPVDPublisherFieldFrom:testPVDPublisherFieldTo] + return string(bytes.TrimRight(field, "\x00")), nil +} + //nolint:thelper // this is not a helper function func validateElTorito(t *testing.T, f *os.File) { // only do this test if os.Getenv("TEST_IMAGE") contains a real image for integration testing