diff --git a/internal/image/isomod.go b/internal/image/isomod.go index 5574eb4..eef74d8 100644 --- a/internal/image/isomod.go +++ b/internal/image/isomod.go @@ -170,13 +170,16 @@ func rewriteGrubConfig(original, answerURL string) string { return strings.Join(collapsed, "\n") } -// findAllOccurrences searches the entire ISO file for all locations where -// the grub.cfg content appears — this catches both the ISO9660 filesystem -// copy and any copy inside an embedded EFI boot partition image. func findAllOccurrences(isoPath string, content []byte) ([]int64, error) { - data, err := os.ReadFile(isoPath) + f, err := os.Open(isoPath) if err != nil { - return nil, fmt.Errorf("read ISO: %w", err) + return nil, fmt.Errorf("open ISO: %w", err) + } + defer f.Close() + + stat, err := f.Stat() + if err != nil { + return nil, err } needle := content @@ -184,20 +187,49 @@ func findAllOccurrences(isoPath string, content []byte) ([]int64, error) { needle = needle[:256] } + const chunkSize = 8 * 1024 * 1024 + overlap := len(needle) - 1 + buf := make([]byte, chunkSize+overlap) + var offsets []int64 - start := 0 - for { - idx := bytes.Index(data[start:], needle) - if idx == -1 { + filePos := int64(0) + + for filePos < stat.Size() { + readStart := filePos + if filePos > 0 { + readStart -= int64(overlap) + } + + n, err := f.ReadAt(buf, readStart) + if n == 0 { + break + } + + searchFrom := 0 + if filePos > 0 { + searchFrom = overlap + } + + chunk := buf[:n] + pos := searchFrom + for { + idx := bytes.Index(chunk[pos:], needle) + if idx == -1 { + break + } + offsets = append(offsets, readStart+int64(pos)+int64(idx)) + pos += idx + 1 + } + + filePos += chunkSize + if err != nil { break } - offsets = append(offsets, int64(start+idx)) - start += idx + 1 } if len(offsets) == 0 { return nil, fmt.Errorf("grub.cfg content not found in ISO raw data (searched %d bytes with %d-byte needle)", - len(data), len(needle)) + stat.Size(), len(needle)) } return offsets, nil