11## iso9660
2+
23[ ![ GoDoc] ( https://godoc.org/github.com/KarpelesLab/iso9660?status.svg )] ( https://godoc.org/github.com/KarpelesLab/iso9660 )
34
4- A package for reading and creating ISO9660, forked from https://github.com/kdomanski/iso9660 .
5+ A Go package for reading and creating ISO9660 images, forked from https://github.com/kdomanski/iso9660 .
6+
7+ Requires Go 1.21 or newer.
8+
9+ ## Features
510
6- Requires Go 1.13 or newer.
11+ ### Reading
12+ - Basic ISO9660 support
13+ - Rock Ridge extensions (long filenames, POSIX permissions, symlinks)
14+ - SUSP (System Use Sharing Protocol)
715
8- Joliet and Rock Ridge extensions are not supported.
16+ ### Writing
17+ - Basic ISO9660 support
18+ - El Torito boot support
19+ - Hard links (same file added multiple times is written once)
20+ - Streaming writes (no temp files needed)
21+
22+ Joliet extensions are not supported.
923
1024## Examples
1125
@@ -16,6 +30,7 @@ package main
1630
1731import (
1832 " log"
33+ " os"
1934
2035 " github.com/KarpelesLab/iso9660/isoutil"
2136)
@@ -33,6 +48,53 @@ func main() {
3348}
3449```
3550
51+ ### Reading an ISO with Rock Ridge
52+
53+ ``` go
54+ package main
55+
56+ import (
57+ " fmt"
58+ " log"
59+ " os"
60+
61+ " github.com/KarpelesLab/iso9660"
62+ )
63+
64+ func main () {
65+ f , err := os.Open (" /home/user/myImage.iso" )
66+ if err != nil {
67+ log.Fatalf (" failed to open file: %s " , err)
68+ }
69+ defer f.Close ()
70+
71+ img , err := iso9660.OpenImage (f)
72+ if err != nil {
73+ log.Fatalf (" failed to open image: %s " , err)
74+ }
75+
76+ // Get the volume label
77+ label , _ := img.Label ()
78+ fmt.Printf (" Volume: %s \n " , label)
79+
80+ root , err := img.RootDir ()
81+ if err != nil {
82+ log.Fatalf (" failed to get root dir: %s " , err)
83+ }
84+
85+ children , err := root.GetChildren ()
86+ if err != nil {
87+ log.Fatalf (" failed to get children: %s " , err)
88+ }
89+
90+ for _ , child := range children {
91+ // With Rock Ridge, Name() returns the full filename
92+ // Mode() returns POSIX permissions
93+ fmt.Printf (" %s %s (%d bytes)\n " , child.Mode (), child.Name (), child.Size ())
94+ }
95+ }
96+ ```
97+
3698### Creating an ISO
3799
38100``` go
@@ -51,20 +113,27 @@ func main() {
51113 log.Fatalf (" failed to create writer: %s " , err)
52114 }
53115
54- // set volume name
116+ // Set volume name
55117 writer.Primary .VolumeIdentifier = " testvol"
56118
119+ // Add a single file
57120 err = writer.AddLocalFile (" /home/user/myFile.txt" , " folder/MYFILE.TXT" )
58121 if err != nil {
59122 log.Fatalf (" failed to add file: %s " , err)
60123 }
61124
62- outputFile , err := os.OpenFile (" /home/user/output.iso" , os.O_WRONLY | os.O_TRUNC | os.O_CREATE , 0644 )
125+ // Or add an entire directory recursively
126+ err = writer.AddLocalDirectory (" /home/user/myFolder" , " folder" )
127+ if err != nil {
128+ log.Fatalf (" failed to add directory: %s " , err)
129+ }
130+
131+ outputFile , err := os.OpenFile (" /home/user/output.iso" , os.O_WRONLY |os.O_TRUNC |os.O_CREATE , 0644 )
63132 if err != nil {
64133 log.Fatalf (" failed to create file: %s " , err)
65134 }
66135
67- err = writer.WriteTo (outputFile)
136+ _, err = writer.WriteTo (outputFile)
68137 if err != nil {
69138 log.Fatalf (" failed to write ISO image: %s " , err)
70139 }
@@ -76,39 +145,113 @@ func main() {
76145}
77146```
78147
79- ### Streaming an ISO via HTTP
80-
81- It is possible to stream a dynamically generated file on request via HTTP in order to include files or customize configuration files:
148+ ### Creating a Bootable ISO
82149
83150``` go
151+ package main
152+
84153import (
85- " http"
86154 " log"
155+ " os"
87156
88157 " github.com/KarpelesLab/iso9660"
89158)
90159
91- func ServeHTTP ( rw http . RequestWriter , req * http . Request ) {
160+ func main ( ) {
92161 writer , err := iso9660.NewWriter ()
93162 if err != nil {
94163 log.Fatalf (" failed to create writer: %s " , err)
95164 }
96165
97- // set volume name
98- writer.Primary .VolumeIdentifier = " LIVE IMAGE"
166+ writer.Primary .VolumeIdentifier = " BOOTABLE"
167+
168+ // Add El Torito boot entry
169+ isolinux , err := iso9660.NewItemFile (" /usr/share/syslinux/isolinux.bin" )
170+ if err != nil {
171+ log.Fatalf (" failed to open isolinux.bin: %s " , err)
172+ }
173+
174+ err = writer.AddBootEntry (&iso9660.BootCatalogEntry {BootInfoTable: true }, isolinux, " isolinux/isolinux.bin" )
175+ if err != nil {
176+ log.Fatalf (" failed to add boot entry: %s " , err)
177+ }
178+
179+ // Add other boot files
180+ writer.AddLocalFile (" /usr/share/syslinux/ldlinux.c32" , " isolinux/ldlinux.c32" )
99181
100- if syslinux , err := iso9660.NewItemFile (" /pkg/main/sys-boot.syslinux.core/share/syslinux/isolinux.bin" ); err == nil {
101- writer.AddBootEntry (&iso9660.BootCatalogEntry {BootInfoTable: true }, isolinux, " isolinux/isolinux.bin" )
102- writer.AddLocalFile (" /pkg/main/sys-boot.syslinux.core/share/syslinux/linux.c32" , " isolinux/linux.c32" )
103- writer.AddLocalFile (" /pkg/main/sys-boot.syslinux.core/share/syslinux/ldlinux.c32" , " isolinux/ldlinux.c32" )
182+ outputFile , err := os.Create (" /home/user/bootable.iso" )
183+ if err != nil {
184+ log.Fatalf (" failed to create file: %s " , err)
104185 }
105186
106- writer.AddLocalFile (" kernel.img" , " isolinux/kernel.img" )
107- writer.AddLocalFile (" initrd.img" , " isolinux/initrd.img" )
108- writer.AddLocalFile (" root.squashfs" , " root.img" )
109- writer.AddFile (getSyslinuxConfig (), " isolinux/isolinux.cfg" )
187+ _, err = writer.WriteTo (outputFile)
188+ if err != nil {
189+ log.Fatalf (" failed to write ISO image: %s " , err)
190+ }
191+
192+ outputFile.Close ()
193+ }
194+ ```
195+
196+ ### Streaming an ISO via HTTP
197+
198+ It is possible to stream a dynamically generated ISO via HTTP:
199+
200+ ``` go
201+ package main
202+
203+ import (
204+ " net/http"
205+ " log"
206+
207+ " github.com/KarpelesLab/iso9660"
208+ )
209+
210+ func handler (rw http .ResponseWriter , req *http .Request ) {
211+ writer , err := iso9660.NewWriter ()
212+ if err != nil {
213+ http.Error (rw, err.Error (), 500 )
214+ return
215+ }
216+
217+ writer.Primary .VolumeIdentifier = " LIVE IMAGE"
218+
219+ // Add files dynamically
220+ writer.AddLocalFile (" kernel.img" , " boot/kernel.img" )
221+ writer.AddLocalFile (" initrd.img" , " boot/initrd.img" )
110222
111223 rw.Header ().Set (" Content-Type" , " application/x-iso9660-image" )
224+ rw.Header ().Set (" Content-Disposition" , " attachment; filename=image.iso" )
225+
112226 writer.WriteTo (rw)
113227}
228+
229+ func main () {
230+ http.HandleFunc (" /image.iso" , handler)
231+ log.Fatal (http.ListenAndServe (" :8080" , nil ))
232+ }
114233```
234+
235+ ## API
236+
237+ ### Reader
238+
239+ - ` OpenImage(ra io.ReaderAt) (*Image, error) ` - Open an ISO image for reading
240+ - ` (*Image) RootDir() (*File, error) ` - Get the root directory
241+ - ` (*Image) Label() (string, error) ` - Get the volume label
242+ - ` (*File) GetChildren() ([]*File, error) ` - Get directory children (excludes ` . ` and ` .. ` )
243+ - ` (*File) GetAllChildren() ([]*File, error) ` - Get all directory children (includes ` . ` and ` .. ` )
244+ - ` (*File) Reader() io.Reader ` - Get a reader for file contents
245+ - ` (*File) Name() string ` - Get filename (Rock Ridge long name if available)
246+ - ` (*File) Mode() os.FileMode ` - Get file mode (Rock Ridge permissions if available)
247+ - ` (*File) IsDir() bool ` - Check if entry is a directory
248+ - ` (*File) Size() int64 ` - Get file size
249+
250+ ### Writer
251+
252+ - ` NewWriter() (*ImageWriter, error) ` - Create a new ISO writer
253+ - ` (*ImageWriter) AddFile(data io.Reader, filePath string) error ` - Add a file from a reader
254+ - ` (*ImageWriter) AddLocalFile(localPath, filePath string) error ` - Add a file from the filesystem
255+ - ` (*ImageWriter) AddLocalDirectory(origin, target string) error ` - Add a directory recursively
256+ - ` (*ImageWriter) AddBootEntry(boot *BootCatalogEntry, data Item, filePath string) error ` - Add El Torito boot entry
257+ - ` (*ImageWriter) WriteTo(w io.Writer) (int64, error) ` - Write the ISO image
0 commit comments