Skip to content

Commit

Permalink
iso rock ridge should handle creating with filenames longer than 30 c…
Browse files Browse the repository at this point in the history
…hars (#190)

Signed-off-by: Avi Deitcher <[email protected]>
  • Loading branch information
deitch authored Dec 4, 2023
1 parent 5817922 commit 25d5b7e
Show file tree
Hide file tree
Showing 7 changed files with 143 additions and 7 deletions.
33 changes: 29 additions & 4 deletions filesystem/iso9660/directoryentry.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ func (de *directoryEntry) toBytes(skipExt bool, ceBlocks []uint32) ([][]byte, er
filenameBytes = []byte{0x01}
default:
// first validate the filename
err = validateFilename(de.filename, de.isSubdirectory)
err = validateFilename(de.filename, de.isSubdirectory, de.filesystem.suspEnabled)
if err != nil {
nametype := "filename"
if de.isSubdirectory {
Expand Down Expand Up @@ -549,16 +549,27 @@ func timeToBytes(t time.Time) []byte {
}

// convert a string to ascii bytes, but only accept valid d-characters
func validateFilename(s string, isDir bool) error {
func validateFilename(s string, isDir, suspExtension bool) error {
var err error
if suspExtension {
err = validateSUSPFilename(s, isDir)
} else {
err = validateISOFilename(s, isDir)
}
return err
}

// validateISOFilename validates a filename that is plain ISO9660-compliant (levels 2 & 3)
func validateISOFilename(s string, isDir bool) error {
var err error
// all allowed up to 30 characters, of A-Z,0-9,_
if isDir {
// directory only allowed up to 8 characters of A-Z,0-9,_
re := regexp.MustCompile("^[A-Z0-9_]{1,30}$")
if !re.MatchString(s) {
err = fmt.Errorf("directory name must be of up to 30 characters from A-Z0-9_")
}
} else {
// filename only allowed up to 8 characters of A-Z,0-9,_, plus an optional '.' plus up to 3 characters of A-Z,0-9,_, plus must have ";1"
// filename also allowed an optional '.' plus up to 3 characters of A-Z,0-9,_, plus must have ";1"
re := regexp.MustCompile("^[A-Z0-9_]+(.[A-Z0-9_]*)?;1$")
switch {
case !re.MatchString(s):
Expand All @@ -570,6 +581,20 @@ func validateFilename(s string, isDir bool) error {
return err
}

// validateSUSPFilename validates a filename that is Rock Ridge compliant
func validateSUSPFilename(s string, _ bool) error {
var err error
// all allowed up to 255 characters of any kind, except null (0x0) and '/'
re := regexp.MustCompile(`^[^\x00/]*$`)
switch {
case len(s) > 255:
err = fmt.Errorf("filename must be at most 255 characters")
case !re.MatchString(s):
err = fmt.Errorf("filename must not include / or null characters")
}
return err
}

// convert a string to a byte array, if all characters are valid ascii
func stringToASCIIBytes(s string) ([]byte, error) {
length := len(s)
Expand Down
108 changes: 105 additions & 3 deletions filesystem/iso9660/iso9660_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@ import (
"io"
"os"
"path"
"path/filepath"
"strings"
"testing"

"github.com/diskfs/go-diskfs"
"github.com/diskfs/go-diskfs/disk"
"github.com/diskfs/go-diskfs/filesystem"
"github.com/diskfs/go-diskfs/filesystem/iso9660"
)
Expand Down Expand Up @@ -479,12 +482,12 @@ func TestIso9660OpenFile(t *testing.T) {
}
for i, tt := range tests {
for _, fs := range []*iso9660.FileSystem{fsTemp, fsUser} {
filepath := path.Join(fs.Workspace(), tt.path)
fullpath := path.Join(fs.Workspace(), tt.path)
// remove any old file if it exists - ignore errors
_ = os.Remove(filepath)
_ = os.Remove(fullpath)
// if the file is supposed to exist, create it and add its contents
if tt.mode&os.O_CREATE != os.O_CREATE {
_ = os.WriteFile(filepath, []byte(baseContent), 0o600)
_ = os.WriteFile(fullpath, []byte(baseContent), 0o600)
}
header := fmt.Sprintf("%d: OpenFile(%s, %s, %t)", i, tt.path, getOpenMode(tt.mode), tt.beginning)
readWriter, err := fs.OpenFile(tt.path, tt.mode)
Expand Down Expand Up @@ -537,5 +540,104 @@ func TestIso9660OpenFile(t *testing.T) {
}

func TestIso9660Finalize(t *testing.T) {
var createISOFilesystem = func(inDir, outputFileName string, rockRidge bool) error {
var LogicalBlocksize diskfs.SectorSize = 2048

// Create the disk image
// TODO: Explain why we need to use Raw here
mydisk, err := diskfs.Create(outputFileName, 100*1024, diskfs.Raw, LogicalBlocksize)
if err != nil {
return err
}

// Create the ISO filesystem on the disk image
fspec := disk.FilesystemSpec{
Partition: 0,
FSType: filesystem.TypeISO9660,
VolumeLabel: "label",
}
fs, err := mydisk.CreateFilesystem(fspec)
if err != nil {
return err
}

// Walk the source folder to copy all files and folders to the ISO filesystem
err = filepath.Walk(inDir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}

relPath, err := filepath.Rel(inDir, path)
if err != nil {
return err
}

// If the current path is a folder, create the folder in the ISO filesystem
if info.IsDir() {
// Create the directory in the ISO file
err = fs.Mkdir(relPath)
if err != nil {
return err
}
return nil
}

// If the current path is a file, copy the file to the ISO filesystem
if !info.IsDir() {
// Open the file in the ISO file for writing
rw, err := fs.OpenFile(relPath, os.O_CREATE|os.O_RDWR)
if err != nil {
return err
}

// Open the source file for reading
in, errorOpeningFile := os.Open(path)
if errorOpeningFile != nil {
return errorOpeningFile
}
defer in.Close()

// Copy the contents of the source file to the ISO file
_, err = io.Copy(rw, in)
if err != nil {
return err
}
}

return nil
})
if err != nil {
return err
}

iso, ok := fs.(*iso9660.FileSystem)
if !ok {
return fmt.Errorf("not an iso9660 filesystem")
}
opts := iso9660.FinalizeOptions{}
if rockRidge {
opts.RockRidge = true
}
return iso.Finalize(opts)
}
tests := []struct {
name string
inDir string
outputFileName string
rockRidge bool
}{
{"normal", "testdata/iso-in-folder", "iso-image.iso", false},
{"rock ridge", "testdata/rock-ridge-in-folder", "rock-ridge-image.iso", true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tmpDir := os.TempDir()
outfile := path.Join(tmpDir, tt.outputFileName)
defer os.RemoveAll(outfile)
err := createISOFilesystem(tt.inDir, outfile, tt.rockRidge)
if err != nil {
t.Errorf("Failed to create ISO filesystem: %v", err)
}
})
}
}
3 changes: 3 additions & 0 deletions filesystem/iso9660/testdata/iso-in-folder/abc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
abc
def
ghi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Hello from subfolder!
3 changes: 3 additions & 0 deletions filesystem/iso9660/testdata/rock-ridge-in-folder/abc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
abc
def
ghi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Hello from subfolder!
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
iso9660 only supports filenames up to 30 characters. Rock Ridge supports filenames up to 255 characters.

0 comments on commit 25d5b7e

Please sign in to comment.