image_reader.go 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. package iso9660
  2. import (
  3. "fmt"
  4. "io"
  5. "os"
  6. "strings"
  7. "time"
  8. )
  9. // Image is a wrapper around an image file that allows reading its ISO9660 data
  10. type Image struct {
  11. ra io.ReaderAt
  12. volumeDescriptors []volumeDescriptor
  13. }
  14. // OpenImage returns an Image reader reating from a given file
  15. func OpenImage(ra io.ReaderAt) (*Image, error) {
  16. i := &Image{ra: ra}
  17. if err := i.readVolumes(); err != nil {
  18. return nil, err
  19. }
  20. return i, nil
  21. }
  22. func (i *Image) readVolumes() error {
  23. buffer := make([]byte, sectorSize)
  24. // skip the 16 sectors of system area
  25. for sector := 16; ; sector++ {
  26. if _, err := i.ra.ReadAt(buffer, int64(sector)*int64(sectorSize)); err != nil {
  27. return err
  28. }
  29. var vd volumeDescriptor
  30. if err := vd.UnmarshalBinary(buffer); err != nil {
  31. return err
  32. }
  33. // NOTE: the instance of the root Directory Record that appears
  34. // in the Primary Volume Descriptor cannot contain a System Use
  35. // field. See the SUSP standard.
  36. i.volumeDescriptors = append(i.volumeDescriptors, vd)
  37. if vd.Header.Type == volumeTypeTerminator {
  38. break
  39. }
  40. }
  41. return nil
  42. }
  43. // RootDir returns the File structure corresponding to the root directory
  44. // of the first primary volume
  45. func (i *Image) RootDir() (*File, error) {
  46. for _, vd := range i.volumeDescriptors {
  47. if vd.Type() == volumeTypePrimary {
  48. return &File{de: vd.Primary.RootDirectoryEntry, ra: i.ra, children: nil, isRootDir: true}, nil
  49. }
  50. }
  51. return nil, os.ErrNotExist
  52. }
  53. // RootDir returns the label of the first Primary Volume
  54. func (i *Image) Label() (string, error) {
  55. for _, vd := range i.volumeDescriptors {
  56. if vd.Type() == volumeTypePrimary {
  57. return string(vd.Primary.VolumeIdentifier), nil
  58. }
  59. }
  60. return "", os.ErrNotExist
  61. }
  62. // File is a os.FileInfo-compatible wrapper around an ISO9660 directory entry
  63. type File struct {
  64. ra io.ReaderAt
  65. de *DirectoryEntry
  66. children []*File
  67. isRootDir bool
  68. susp *SUSPMetadata
  69. }
  70. var _ os.FileInfo = &File{}
  71. func (f *File) hasRockRidge() bool {
  72. return f.susp != nil && f.susp.HasRockRidge
  73. }
  74. // IsDir returns true if the entry is a directory or false otherwise
  75. func (f *File) IsDir() bool {
  76. if f.hasRockRidge() {
  77. if mode, err := f.de.SystemUseEntries.GetPosixAttr(); err == nil {
  78. return mode&os.ModeDir != 0
  79. }
  80. }
  81. return f.de.FileFlags&dirFlagDir != 0
  82. }
  83. // ModTime returns the entry's recording time
  84. func (f *File) ModTime() time.Time {
  85. return time.Time(f.de.RecordingDateTime)
  86. }
  87. // Mode returns file mode when available.
  88. // Otherwise it returns os.FileMode flag set with the os.ModeDir flag enabled in case of directories.
  89. func (f *File) Mode() os.FileMode {
  90. if f.hasRockRidge() {
  91. if mode, err := f.de.SystemUseEntries.GetPosixAttr(); err == nil {
  92. return mode
  93. }
  94. }
  95. var mode os.FileMode
  96. if f.IsDir() {
  97. mode |= os.ModeDir
  98. }
  99. return mode
  100. }
  101. // Name returns the base name of the given entry
  102. func (f *File) Name() string {
  103. if f.hasRockRidge() {
  104. if name := f.de.SystemUseEntries.GetRockRidgeName(); name != "" {
  105. return name
  106. }
  107. }
  108. if f.IsDir() {
  109. return f.de.Identifier
  110. }
  111. // drop the version part
  112. // assume only one ';'
  113. fileIdentifier := strings.Split(f.de.Identifier, ";")[0]
  114. // split into filename and extension
  115. // assume only only one '.'
  116. splitFileIdentifier := strings.Split(fileIdentifier, ".")
  117. // there's no dot in the name, thus no extension
  118. if len(splitFileIdentifier) == 1 {
  119. return splitFileIdentifier[0]
  120. }
  121. // extension is empty, return just the name without a dot
  122. if len(splitFileIdentifier[1]) == 0 {
  123. return splitFileIdentifier[0]
  124. }
  125. // return file with extension
  126. return fileIdentifier
  127. }
  128. // Size returns the size in bytes of the extent occupied by the file or directory
  129. func (f *File) Size() int64 {
  130. return int64(f.de.ExtentLength)
  131. }
  132. // Sys returns nil
  133. func (f *File) Sys() interface{} {
  134. return nil
  135. }
  136. // GetAllChildren returns the children entries in case of a directory
  137. // or an error in case of a file. It includes the "." and ".." entries.
  138. func (f *File) GetAllChildren() ([]*File, error) {
  139. if !f.IsDir() {
  140. return nil, fmt.Errorf("%s is not a directory", f.Name())
  141. }
  142. if f.children != nil {
  143. return f.children, nil
  144. }
  145. baseOffset := uint32(f.de.ExtentLocation) * sectorSize
  146. buffer := make([]byte, sectorSize)
  147. for bytesProcessed := uint32(0); bytesProcessed < uint32(f.de.ExtentLength); bytesProcessed += sectorSize {
  148. if _, err := f.ra.ReadAt(buffer, int64(baseOffset+bytesProcessed)); err != nil {
  149. return nil, nil
  150. }
  151. for i := uint32(0); i < sectorSize; {
  152. entryLength := uint32(buffer[i])
  153. if entryLength == 0 {
  154. break
  155. }
  156. if i+entryLength > sectorSize {
  157. return nil, fmt.Errorf("reading directory entries: DE outside of sector boundries")
  158. }
  159. newDE := &DirectoryEntry{}
  160. if err := newDE.UnmarshalBinary(buffer[i : i+entryLength]); err != nil {
  161. return nil, err
  162. }
  163. // Is this a root directory '.' record?
  164. if f.isRootDir && newDE.Identifier == string([]byte{0}) {
  165. newDE.SystemUseEntries, _ = splitSystemUseEntries(newDE.SystemUse, f.ra)
  166. // get the SP record
  167. if len(newDE.SystemUseEntries) > 0 && newDE.SystemUseEntries[0].Type() == "SP" {
  168. sprecord, err := SPRecordDecode(newDE.SystemUseEntries[0])
  169. if err != nil {
  170. return nil, fmt.Errorf("invalid SP record: %w", err)
  171. }
  172. hasRockRidge, err := suspHasRockRidge(newDE.SystemUseEntries)
  173. if err != nil {
  174. return nil, fmt.Errorf("failed to check for Rock Ridge extension: %w", err)
  175. }
  176. // save SUSP offset from the SP record
  177. f.susp = &SUSPMetadata{
  178. Offset: sprecord.BytesSkipped,
  179. HasRockRidge: hasRockRidge,
  180. }
  181. }
  182. } else {
  183. // are we on a volume with SUSP?
  184. if f.susp != nil {
  185. // Ignore error if some of the SUSP data is malformed. Just take the valid part.
  186. offsetSystemUse := newDE.SystemUse[f.susp.Offset:]
  187. newDE.SystemUseEntries, _ = splitSystemUseEntries(offsetSystemUse, f.ra)
  188. }
  189. }
  190. i += entryLength
  191. newFile := &File{ra: f.ra,
  192. de: newDE,
  193. children: nil,
  194. susp: f.susp.Clone(),
  195. }
  196. f.children = append(f.children, newFile)
  197. }
  198. }
  199. return f.children, nil
  200. }
  201. // GetChildren returns the children entries in case of a directory
  202. // or an error in case of a file. It does NOT include the "." and ".." entries.
  203. func (f *File) GetChildren() ([]*File, error) {
  204. children, err := f.GetAllChildren()
  205. if err != nil {
  206. return nil, err
  207. }
  208. filteredChildren := make([]*File, 0, len(children)-2)
  209. for _, child := range children {
  210. if child.de.Identifier == string([]byte{0}) || child.de.Identifier == string([]byte{1}) {
  211. continue
  212. }
  213. filteredChildren = append(filteredChildren, child)
  214. }
  215. return filteredChildren, nil
  216. }
  217. // GetDotEntry returns the "." entry of a directory
  218. // or an error in case of a file.
  219. func (f *File) GetDotEntry() (*File, error) {
  220. children, err := f.GetAllChildren()
  221. if err != nil {
  222. return nil, err
  223. }
  224. for _, child := range children {
  225. if child.de.Identifier == string([]byte{0}) {
  226. return child, nil
  227. }
  228. }
  229. return nil, nil
  230. }
  231. // Reader returns a reader that allows to read the file's data.
  232. // If File is a directory, it returns nil.
  233. func (f *File) Reader() io.Reader {
  234. if f.IsDir() {
  235. return nil
  236. }
  237. baseOffset := int64(f.de.ExtentLocation) * int64(sectorSize)
  238. return io.NewSectionReader(f.ra, baseOffset, int64(f.de.ExtentLength))
  239. }