iso9660

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 17, 2015 License: MPL-2.0 Imports: 10 Imported by: 34

README

iso9660

GoDoc Build Status

Go library and CLI to extract data from ISO9660 images.

CLI
Library

An example on how to use the library to extract files and directories from an ISO image can be found in our CLI source code at: https://github.com/hooklift/iso9660/blob/master/cmd/iso9660/main.go

Not supported
  • Reading files recorded in interleave mode
  • Multi-extent or reading individual files larger than 4GB
  • Joliet extensions, meaning that file names longer than 32 characters are going to be truncated. Unicode characters are not going to be properly decoded either.
  • Multisession extension
  • Rock Ridge extension, making unable to return recorded POSIX permissions, timestamps as well as owner and group for files and directories.

Documentation

Overview

Package iso9660 implements ECMA-119 standard, also known as ISO 9660.

References:

* https://en.wikipedia.org/wiki/ISO_9660

* http://alumnus.caltech.edu/~pje/iso9660.html

* http://users.telenet.be/it3.consultants.bvba/handouts/ISO9960.html

* http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-119.pdf

* http://www.drdobbs.com/database/inside-the-iso-9660-filesystem-format/184408899

* http://www.cdfs.com

* http://wiki.osdev.org/ISO_9660

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// ErrInvalidImage is returned when an attempt to unpack the image Primary Volume Descriptor failed or
	// when the end of the image was reached without finding a primary volume descriptor
	ErrInvalidImage = func(err error) error { return fmt.Errorf("invalid-iso9660-image: %s", err) }
	// ErrCorruptedImage is returned when a seek operation, on the image, failed.
	ErrCorruptedImage = func(err error) error { return fmt.Errorf("corrupted-image: %s", err) }
)

Functions

This section is empty.

Types

type BootRecord

type BootRecord struct {
	VolumeDescriptor
	SystemID  [32]byte
	ID        [32]byte
	SystemUse [1977]byte
}

BootRecord identifies a system which can recognize and act upon the content of the field reserved for boot system use in the Boot Record, and shall contain information which is used to achieve a specific state for a system or for an application.

type DirectoryRecord

type DirectoryRecord struct {
	// Extended Attribute Record length, stored at the beginning of
	// the file's extent.
	ExtendedAttrLen byte
	// Location of extent (Logical Block Address) in both-endian format.
	ExtentLocationLE uint32
	ExtentLocationBE uint32
	// Data length (size of extent) in both-endian format.
	ExtentLengthLE uint32
	ExtentLengthBE uint32
	// Date and the time of the day at which the information in the Extent
	// described by the Directory Record was recorded.
	RecordedTime [7]byte
	// If this Directory Record identifies a directory then bit positions 2, 3
	// and 7 shall be set to ZERO. If no Extended Attribute Record is associated
	// with the File Section identified by this Directory Record then bit
	// positions 3 and 4 shall be set to ZERO. -- 9.1.6
	FileFlags byte
	// File unit size for files recorded in interleaved mode, zero otherwise.
	FileUnitSize byte
	// Interleave gap size for files recorded in interleaved mode, zero otherwise.
	InterleaveGapSize byte
	// Volume sequence number - the volume that this extent is recorded on, in
	// 16 bit both-endian format.
	VolumeSeqNumberLE uint16
	VolumeSeqNumberBE uint16
	// Length of file identifier (file name). This terminates with a ';'
	// character followed by the file ID number in ASCII coded decimal ('1').
	FileIDLength byte
}

DirectoryRecord describes the characteristics of a file or directory, beginning with a length octet describing the size of the entire entry. Entries themselves are of variable length, up to 255 octets in size. Attributes for the file described by the directory entry are stored in the directory entry itself (unlike UNIX). The root directory entry is a variable length object, so that the name can be of variable length.

Important: before each entry there can be "fake entries" to support the Long File Name.

Even if a directory spans multiple sectors, the directory entries are not permitted to cross the sector boundary (unlike the path table). Where there is not enough space to record an entire directory entry at the end of a sector, that sector is zero-padded and the next consecutive sector is used. Unfortunately, the date/time format is different from that used in the Primary Volume Descriptor.

type ExtendedAttrRecord

type ExtendedAttrRecord struct {
	OwnerID          int `struc:"int16"`
	GroupID          int `struc:"int16"`
	Permissions      int `struc:"int16"`
	CreationTime     Timestamp
	ModificationTime Timestamp
	ExpirationTime   Timestamp
	// Specifies the date and the time of the day at which the information in
	// the file may be used. If the date and time are not specified then the
	// information may be used at once.
	EffectiveTime   Timestamp
	Format          int    `struc:"uint8"`
	Attributes      int    `struc:"uint8"`
	Length          int    `struc:"int16"`
	SystemID        string `struc:"[32]byte"`
	SystemUse       [64]byte
	Version         int `struc:"uint8"`
	EscapeSeqLength int `struc:"uint8"`
	Reserved        [64]byte
	AppUseLength    int `struc:"int16,sizeof=AppUse"`
	AppUse          []byte
	EscapeSequences []byte `struc:"sizefrom=AppUseLength"`
}

ExtendedAttrRecord are simply a way to extend the attributes of files. Since attributes vary according to the user, most everyone has a different opinion on what a file attribute should specify.

type File

type File struct {
	DirectoryRecord
	// contains filtered or unexported fields
}

File represents a concrete implementation of os.FileInfo interface for accessing ISO 9660 file data

func (*File) IsDir

func (f *File) IsDir() bool

IsDir tells whether the file is a directory or not.

func (*File) ModTime

func (f *File) ModTime() time.Time

ModTime returns file's modification time.

func (*File) Mode

func (f *File) Mode() os.FileMode

Mode returns file's mode and permissions bits. Since we don't yet support Rock Ridge extensions we cannot extract POSIX permissions and the rest of the normal metadata. So, right we return 0740 for directories and 0640 for files.

func (*File) Name

func (f *File) Name() string

Name returns the file's name.

func (*File) Size

func (f *File) Size() int64

Size returns the file size in bytes

func (*File) Sys

func (f *File) Sys() interface{}

Sys returns io.Reader instance pointing to the file's content if it is not a directory, nil otherwise.

type PartitionVolume

type PartitionVolume struct {
	VolumeDescriptor
	Unused    byte
	SystemID  string `struc:"[32]byte"`
	ID        string `struc:"[32]byte"`
	Location  int    `struc:"int8"`
	Size      int    `struc:"int8"`
	SystemUse [1960]byte
}

PartitionVolume ...

type PathTable

type PathTable struct {
	DirIDLength      int `struc:"uint8,sizeof=DirName"`
	ExtendedAttrsLen int `struc:"uint8"`
	// Number the Logical Block Number of the first Logical Block allocated to
	// the Extent in which the directory is recorded.
	// This is in a different format depending on whether this is the L-Table or
	// M-Table (see explanation above).
	ExtentLocation int `struc:"int32"`
	// Number of record for parent directory (or 1 for the root directory), as a
	// word; the first record is number 1, the second record is number 2, etc.
	// Directory number of parent directory (an index in to the path table).
	// This is the field that limits the table to 65536 records.
	ParentDirNumber int `struc:"int16"`
	// Directory Identifier (name) in d-characters.
	DirName string
}

PathTable contains a well-ordered sequence of records describing every directory extent on the CD. There are some exceptions with this: the Path Table can only contain 65536 records, due to the length of the "Parent Directory Number" field. If there are more than this number of directories on the disc, some CD authoring software will ignore this limit and create a non-compliant CD (this applies to some earlier versions of Nero, for example). If your file system uses the path table, you should be aware of this possibility. Windows uses the Path Table and will fail with such non-compliant CD's (additional nodes exist but appear as zero-byte). Linux, which uses the directory tables is not affected by this issue. The location of the path tables can be found in the Primary Volume Descriptor. There are two table types - the L-Path table (relevant to x86) and the M-Path table. The only difference between these two tables is that multi-byte values in the L-Table are LSB-first and the values in the M-Table are MSB-first.

The path table is in ascending order of directory level and is alphabetically sorted within each directory level.

type PrimaryVolume

type PrimaryVolume struct {
	PrimaryVolumePart1
	DirectoryRecord DirectoryRecord
	PrimaryVolumePart2
}

PrimaryVolume descriptor acts much like the superblock of the UNIX filesystem, providing details on the ISO-9660 compliant portions of the disk. While we can have many kinds of filesystems on a single ISO-9660 CD-ROM, we can have only one ISO-9660 file structure (found as the primary volume-descriptor type).

Directory entries are successively stored within this region. Evaluation of the ISO 9660 filenames is begun at this location. The root directory is stored as an extent, or sequential series of sectors, that contains each of the directory entries appearing in the root.

Since ISO 9660 works by segmenting the CD-ROM into logical blocks, the size of these blocks is found in the primary volume descriptor as well.

type PrimaryVolumePart1

type PrimaryVolumePart1 struct {
	VolumeDescriptor

	// The name of the system that can act upon sectors 0x00-0x0F for the volume.
	SystemID [32]byte
	// Identification of this volume.
	ID [32]byte

	// Amount of data available on the CD-ROM. Ignores little-endian order.
	// Takes big-endian encoded value.
	VolumeSpaceSizeLE int32
	VolumeSpaceSizeBE int32
	Unused2           [32]byte
	// The size of the set in this logical volume (number of disks). Ignores
	// little-endian order. Takes big-endian encoded value.
	VolumeSetSizeLE int16
	VolumeSetSizeBE int16
	// The number of this disk in the Volume Set. Ignores little-endian order.
	// Takes big-endian encoded value.
	VolumeSeqNumberLE int16
	VolumeSeqNumberBE int16
	// The size in bytes of a logical block. NB: This means that a logical block
	// on a CD could be something other than 2 KiB!
	LogicalBlkSizeLE int16
	LogicalBlkSizeBE int16
	// The size in bytes of the path table. Ignores little-endian order.
	// Takes big-endian encoded value.
	PathTableSizeLE int32
	PathTableSizeBE int32
	// LBA location of the path table. The path table pointed to contains only
	// little-endian values.
	LocPathTableLE int32
	// LBA location of the optional path table. The path table pointed to contains
	// only little-endian values. Zero means that no optional path table exists.
	LocOptPathTableLE int32
	// LBA location of the path table. The path table pointed to contains
	// only big-endian values.
	LocPathTableBE int32
	// LBA location of the optional path table. The path table pointed to contains
	// only big-endian values. Zero means that no optional path table exists.
	LocOptPathTableBE int32
	// contains filtered or unexported fields
}

PrimaryVolumePart1 represents the Primary Volume Descriptor first half, before the root directory record. We are only reading big-endian values so placeholders are used for little-endian ones.

type PrimaryVolumePart2

type PrimaryVolumePart2 struct {
	// Identifier of the volume set of which this volume is a member.
	VolumeSetID [128]byte
	// The volume publisher. For extended publisher information, the first byte
	// should be 0x5F, followed by the filename of a file in the root directory.
	// If not specified, all bytes should be 0x20.
	PublisherID [128]byte
	// The identifier of the person(s) who prepared the data for this volume.
	// For extended preparation information, the first byte should be 0x5F,
	// followed by the filename of a file in the root directory. If not specified,
	// all bytes should be 0x20.
	DataPreparerID [128]byte
	// Identifies how the data are recorded on this volume. For extended information,
	// the first byte should be 0x5F, followed by the filename of a file in the root
	// directory. If not specified, all bytes should be 0x20.
	AppID [128]byte
	// Filename of a file in the root directory that contains copyright
	// information for this volume set. If not specified, all bytes should be 0x20.
	CopyrightFileID [37]byte
	// Filename of a file in the root directory that contains abstract information
	// for this volume set. If not specified, all bytes should be 0x20.
	AbstractFileID [37]byte
	// Filename of a file in the root directory that contains bibliographic
	// information for this volume set. If not specified, all bytes should be 0x20.
	BibliographicFileID [37]byte
	// The date and time of when the volume was created.
	CreationTime [17]byte
	// The date and time of when the volume was modified.
	ModificationTime [17]byte
	// The date and time after which this volume is considered to be obsolete.
	// If not specified, then the volume is never considered to be obsolete.
	ExpirationTime [17]byte
	// The date and time after which the volume may be used. If not specified,
	// the volume may be used immediately.
	EffectiveTime [17]byte
	// The directory records and path table version (always 0x01).
	FileStructVersion byte

	// Contents not defined by ISO 9660.
	AppUse [512]byte
	// contains filtered or unexported fields
}

PrimaryVolumePart2 represents the Primary Volume Descriptor half after the root directory record.

type Reader

type Reader struct {
	// contains filtered or unexported fields
}

Reader defines the state of the ISO9660 image reader. It needs to be instantiated from its constructor.

Example
file, err := os.Open("archlinux.iso")
if err != nil {
	panic(err)
}

r, err := NewReader(file)
if err != nil {
	panic(err)
}

destPath := "tmp"
for {
	f, err := r.Next()
	if err == io.EOF {
		break
	}

	if err != nil {
		panic(err)
	}

	fp := filepath.Join(destPath, f.Name())
	if f.IsDir() {
		if err := os.MkdirAll(fp, f.Mode()); err != nil {
			panic(err)
		}
		continue
	}

	parentDir, _ := filepath.Split(fp)
	if err := os.MkdirAll(parentDir, f.Mode()); err != nil {
		panic(err)
	}

	freader := f.Sys().(io.Reader)
	ff, err := os.Create(fp)
	if err != nil {
		panic(err)
	}
	defer func() {
		if err := ff.Close(); err != nil {
			panic(err)
		}
	}()

	if err := ff.Chmod(f.Mode()); err != nil {
		panic(err)
	}

	if _, err := io.Copy(ff, freader); err != nil {
		panic(err)
	}
}
Output:

func NewReader

func NewReader(rs io.ReadSeeker) (*Reader, error)

NewReader creates a new ISO 9660 image reader.

func (*Reader) Next

func (r *Reader) Next() (os.FileInfo, error)

Next moves onto the next directory record present in the image. It does not use the Path Table since the goal is to read everything from the ISO image.

func (*Reader) Skip

func (r *Reader) Skip(n int) error

Skip skips the given number of directory records.

type SupplementaryVolume

type SupplementaryVolume struct {
	VolumeDescriptor
	Flags               int    `struc:"int8"`
	SystemID            string `struc:"[32]byte"`
	ID                  string `struc:"[32]byte"`
	Unused              byte
	VolumeSpaceSize     int    `struc:"int32"`
	EscapeSequences     string `struc:"[32]byte"`
	VolumeSetSize       int    `struc:"int16"`
	VolumeSeqNumber     int    `struc:"int16"`
	LogicalBlkSize      int    `struc:"int16"`
	PathTableSize       int    `struc:"int32"`
	LocLPathTable       int    `struc:"int32"`
	LocOptLPathTable    int    `struc:"int32"`
	LocMPathTable       int    `struc:"int32"`
	LocOptMPathTable    int    `struc:"int32"`
	RootDirRecord       DirectoryRecord
	VolumeSetID         string `struc:"[128]byte"`
	PublisherID         string `struc:"[128]byte"`
	DataPreparerID      string `struc:"[128]byte"`
	AppID               string `struc:"[128]byte"`
	CopyrightFileID     string `struc:"[37]byte"`
	AbstractFileID      string `struc:"[37]byte"`
	BibliographicFileID string `struc:"[37]byte"`
	CreationTime        Timestamp
	ModificationTime    Timestamp
	ExpirationTime      Timestamp
	EffectiveTime       Timestamp
	FileStructVersion   int `struc:"int8"`
	Reserved            byte
	AppData             [512]byte
	Reserved2           byte
}

SupplementaryVolume is used by Joliet.

type Terminator

type Terminator struct {
	VolumeDescriptor
	// All bytes of this field are set to (00).
	Reserved [2041]byte
}

Terminator indicates the termination of a Volume Descriptor Set.

type Timestamp

type Timestamp struct {
	Year        int `struc:"[4]byte"`
	Month       int `struc:"[2]byte"`
	DayOfMonth  int `struc:"[2]byte"`
	Hour        int `struc:"[2]byte"`
	Minute      int `struc:"[2]byte"`
	Second      int `struc:"[2]byte"`
	Millisecond int `struc:"[2]byte"`
	GMTOffset   int `struc:"uint8"`
}

Timestamp ...

type VolumeDescriptor

type VolumeDescriptor struct {
	// 0: BootRecord
	// 1: Primary Volume Descriptor
	// 2: Supplementary Volume Descriptor
	// 3: Volume Partition Descriptor
	// 4-254: Reserved
	// 255: Volume Descriptor Set Terminator
	Type byte
	// Always "CD001".
	StandardID [5]byte
	//Volume Descriptor Version (0x01).
	Version byte
}

VolumeDescriptor identify the volume, the partitions recorded on the volume, the volume creator(s), certain attributes of the volume, the location of other recorded descriptors and the version of the standard which applies to the volume descriptor.

When preparing to mount a CD, your first action will be reading the volume descriptors (specifically, you will be looking for the Primary Volume Descriptor). Since sectors 0x00-0x0F of the CD are reserved as System Area, the Volume Descriptors can be found starting at sector 0x10.

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL