diff --git a/README.md b/README.md index 5389cb0..654b34f 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,31 @@ ## Udf filesystem golang library + - Non-optimized -- Some functioal is broken +- Some features may be broken - `recovery()` style error handling interface -- Work only with certain iso's +- Tested only with certain ISOs (e.g. Windows ISOs) -It's all because I has reached requried functional for me. +It's all because I has reached required functionality for me. ## Example + ```go package main import ( "fmt" "os" - "github.com/mogaika/udf" + "github.com/Xmister/udf" ) func main() { - r, _ := os.Open("example.iso") - u := udf.NewUdfFromReader(r) + rdr, _ := os.Open("example.iso") + u, _ := udf.NewUdfFromReader(r) for _, f := range u.ReadDir(nil) { fmt.Printf("%s %-10d %-20s %v\n", f.Mode().String(), f.Size(), f.Name(), f.ModTime()) } } + ``` Output: ``` @@ -33,5 +36,8 @@ Output: -r-xr-xr-x 15653 dbcman.irx 2005-10-18 00:00:00 +0000 UTC ``` +See [isoinfo.go](isoinfo/isoinfo.go) for complete example. +## Specification +* http://www.osta.org/specs/pdf/udf260.pdf diff --git a/descr.go b/descr.go index daf829b..c6b61ed 100644 --- a/descr.go +++ b/descr.go @@ -19,6 +19,9 @@ const ( DESCRIPTOR_INDIRECT_ENTRY = 0x103 DESCRIPTOR_TERMINAL_ENTRY = 0x104 DESCRIPTOR_FILE_ENTRY = 0x105 + UDF_EXTENT_FLAG_MASK = 0xC0000000 + EXT_NOT_RECORDED_ALLOCATED = 0x40000000 + EXT_NOT_RECORDED_NOT_ALLOCATED = 0x80000000 ) type Descriptor struct { @@ -38,10 +41,19 @@ func (d *Descriptor) Data() []byte { return buf } +func (d *Descriptor) Checksum() (checksum uint8) { + for i := 0; i < 16; i++ { + if i != 4 { + checksum += d.data[i] + } + } + return +} + func (d *Descriptor) FromBytes(b []byte) *Descriptor { d.TagIdentifier = rl_u16(b[0:]) d.DescriptorVersion = rl_u16(b[2:]) - d.TagChecksum = r_u8(b[3:]) + d.TagChecksum = r_u8(b[4:]) d.TagSerialNumber = rl_u16(b[6:]) d.DescriptorCRC = rl_u16(b[8:]) d.DescriptorCRCLength = rl_u16(b[10:]) @@ -170,13 +182,24 @@ type PartitionMap struct { PartitionMapLength uint8 VolumeSequenceNumber uint16 PartitionNumber uint16 + PartitionStart uint32 } func (pm *PartitionMap) FromBytes(b []byte) *PartitionMap { pm.PartitionMapType = rb_u8(b[0:]) pm.PartitionMapLength = rb_u8(b[1:]) - pm.VolumeSequenceNumber = rb_u16(b[2:]) - pm.PartitionNumber = rb_u16(b[4:]) + offset := 0 + switch pm.PartitionMapType { + case 1: + offset = 2 + case 2: + offset = 36 + } + pm.VolumeSequenceNumber = rb_u16(b[offset:]) + // XXX - For whatever reason, the Microsoft ISOs have a little endian partition + // number here??? + pm.PartitionNumber = rl_u16(b[offset+2:]) + pm.PartitionStart = uint32(pm.VolumeSequenceNumber) return pm } @@ -208,7 +231,7 @@ func (lvd *LogicalVolumeDescriptor) FromBytes(b []byte) *LogicalVolumeDescriptor lvd.ImplementationUse = b[304:432] lvd.IntegritySequenceExtent = NewExtent(b[432:]) lvd.PartitionMaps = make([]PartitionMap, lvd.NumberOfPartitionMaps) - for i := range lvd.PartitionMaps { + for i := uint32(0); i < lvd.NumberOfPartitionMaps; i++ { lvd.PartitionMaps[i].FromBytes(b[440+i*6:]) } return lvd @@ -304,6 +327,15 @@ func (d *Descriptor) FileIdentifierDescriptor() *FileIdentifierDescriptor { return NewFileIdentifierDescriptor(d.data) } +type FileEntryInterface interface { + GetAllocationDescriptors() []ExtentInterface + GetPermissions() uint32 + GetInformationLength() uint64 + GetModificationTime() time.Time + GetICBTag() *ICBTag + GetPartition() uint16 +} + type FileEntry struct { Descriptor Descriptor ICBTag *ICBTag @@ -326,7 +358,16 @@ type FileEntry struct { LengthOfExtendedAttributes uint32 LengthOfAllocationDescriptors uint32 ExtendedAttributes []byte - AllocationDescriptors []Extent + AllocationDescriptors []byte + // Manual field + Partition uint16 +} + +type ExtendedFileEntry struct { + FileEntry + CreationTime time.Time + ObjectSize uint64 + StreamDirectoryIcb ExtentLong } func (fe *FileEntry) FromBytes(b []byte) *FileEntry { @@ -351,18 +392,118 @@ func (fe *FileEntry) FromBytes(b []byte) *FileEntry { fe.LengthOfExtendedAttributes = rl_u32(b[168:]) fe.LengthOfAllocationDescriptors = rl_u32(b[172:]) allocDescStart := 176 + fe.LengthOfExtendedAttributes - fe.ExtendedAttributes = b[176:allocDescStart] - fe.AllocationDescriptors = make([]Extent, fe.LengthOfAllocationDescriptors/8) - for i := range fe.AllocationDescriptors { - fe.AllocationDescriptors[i] = NewExtent(b[allocDescStart+uint32(i)*8:]) + if allocDescStart > uint32(len(b)) { + return nil } + fe.ExtendedAttributes = b[176:allocDescStart] + fe.AllocationDescriptors = b[allocDescStart : allocDescStart+fe.LengthOfAllocationDescriptors] return fe } -func NewFileEntry(b []byte) *FileEntry { - return new(FileEntry).FromBytes(b) +func (fe *FileEntry) GetPartition() uint16 { + if fe.ICBTag.AllocationType == LongDescriptors { + return fe.GetAllocationDescriptors()[0].GetPartition() + } + return fe.Partition +} + +func GetAllocationDescriptor(t AllocationType, b []byte) ExtentInterface { + switch t { + case ShortDescriptors: + return NewExtent(b) + case LongDescriptors: + return NewExtentLong(b) + case ExtendedDescriptors: + return NewExtentExtended(b) + } + return nil +} + +func GetAllocationDescriptors(t AllocationType, b []byte, len uint32) (list []ExtentInterface) { + var descLen uint32 + switch t { + case ShortDescriptors: + descLen = 8 + case LongDescriptors: + descLen = 16 + case ExtendedDescriptors: + descLen = 24 + default: + return + } + list = make([]ExtentInterface, len/descLen) + for i := range list { + list[i] = GetAllocationDescriptor(t, b[uint32(i)*descLen:]) + } + + return +} + +func (fe *FileEntry) GetAllocationDescriptors() (list []ExtentInterface) { + return GetAllocationDescriptors(fe.ICBTag.AllocationType, fe.AllocationDescriptors, fe.LengthOfAllocationDescriptors) +} + +func (fe *FileEntry) GetPermissions() uint32 { + return fe.Permissions +} + +func (fe *FileEntry) GetInformationLength() uint64 { + return fe.InformationLength +} + +func (fe *FileEntry) GetModificationTime() time.Time { + return fe.ModificationTime +} + +func (fe *FileEntry) GetICBTag() *ICBTag { + return fe.ICBTag +} + +func NewFileEntry(partition uint16, b []byte) (fe FileEntryInterface) { + if e := new(FileEntry).FromBytes(b); e == nil { + ee := new(ExtendedFileEntry).FromBytes(b) + ee.Partition = partition + fe = ee + } else { + e.Partition = partition + fe = e + } + return +} + +func (fe *ExtendedFileEntry) FromBytes(b []byte) *ExtendedFileEntry { + fe.Descriptor.FromBytes(b) + fe.ICBTag = NewICBTag(b[16:]) + fe.Uid = rl_u32(b[36:]) + fe.Gid = rl_u32(b[40:]) + fe.Permissions = rl_u32(b[44:]) + fe.FileLinkCount = rl_u16(b[48:]) + fe.RecordFormat = r_u8(b[50:]) + fe.RecordDisplayAttributes = r_u8(b[51:]) + fe.RecordLength = rl_u32(b[52:]) + fe.InformationLength = rl_u64(b[56:]) + fe.ObjectSize = rl_u64(b[64:]) + fe.LogicalBlocksRecorded = rl_u64(b[72:]) + fe.AccessTime = r_timestamp(b[80:]) + fe.ModificationTime = r_timestamp(b[92:]) + fe.CreationTime = r_timestamp(b[104:]) + fe.AttributeTime = r_timestamp(b[116:]) + fe.Checkpoint = rl_u32(b[128:]) + fe.ExtendedAttributeICB = NewExtentLong(b[136:]) + fe.StreamDirectoryIcb = NewExtentLong(b[152:]) + fe.ImplementationIdentifier = NewEntityID(b[168:]) + fe.UniqueId = rl_u64(b[200:]) + fe.LengthOfExtendedAttributes = rl_u32(b[208:]) + fe.LengthOfAllocationDescriptors = rl_u32(b[212:]) + allocDescStart := 216 + fe.LengthOfExtendedAttributes + if allocDescStart > uint32(len(b)) { + return nil + } + fe.ExtendedAttributes = b[216:allocDescStart] + fe.AllocationDescriptors = b[allocDescStart : allocDescStart+fe.LengthOfAllocationDescriptors] + return fe } -func (d *Descriptor) FileEntry() *FileEntry { - return NewFileEntry(d.data) +func (d *Descriptor) FileEntry() FileEntryInterface { + return NewFileEntry(0, d.data) } diff --git a/extent.go b/extent.go index 2551bbb..7526920 100644 --- a/extent.go +++ b/extent.go @@ -1,10 +1,43 @@ package udf +type ExtentInterface interface { + GetLocation() uint64 + GetLength() uint32 + SetLength(uint32) + GetPartition() uint16 + IsNotRecorded() bool + HasExtended() bool +} + type Extent struct { Length uint32 Location uint32 } +func (e Extent) GetPartition() uint16 { + return 0 +} + +func (e Extent) GetLocation() uint64 { + return uint64(e.Location) +} + +func (e Extent) GetLength() uint32 { + return e.Length +} + +func (e Extent) SetLength(length uint32) { + e.Length = length +} + +func (e Extent) IsNotRecorded() bool { + return (e.Length & UDF_EXTENT_FLAG_MASK) == EXT_NOT_RECORDED_ALLOCATED || (e.Length & UDF_EXTENT_FLAG_MASK) == EXT_NOT_RECORDED_NOT_ALLOCATED +} + +func (e Extent) HasExtended() bool { + return (e.Length >> 30) == 3 +} + func NewExtent(b []byte) Extent { return Extent{ Length: rl_u32(b[0:]), @@ -17,6 +50,30 @@ type ExtentSmall struct { Location uint64 } +func (e ExtentSmall) GetPartition() uint16 { + return 0 +} + +func (e ExtentSmall) GetLocation() uint64 { + return uint64(e.Location) +} + +func (e ExtentSmall) GetLength() uint32 { + return uint32(e.Length) +} + +func (e ExtentSmall) SetLength(length uint32) { + e.Length = uint16(length) +} + +func (e ExtentSmall) IsNotRecorded() bool { + return false +} + +func (e ExtentSmall) HasExtended() bool { + return (e.Length >> 30) == 3 +} + func NewExtentSmall(b []byte) ExtentSmall { return ExtentSmall{ Length: rl_u16(b[0:]), @@ -26,12 +83,100 @@ func NewExtentSmall(b []byte) ExtentSmall { type ExtentLong struct { Length uint32 - Location uint64 + Location LbAddr +} + +func (e ExtentLong) GetPartition() uint16 { + return e.Location.PartitionReferenceNumber +} + +func (e ExtentLong) GetLocation() uint64 { + return uint64(e.Location.LogicalBlockNumber) +} + +func (e ExtentLong) GetLength() uint32 { + return e.Length +} + +func (e ExtentLong) SetLength(length uint32) { + e.Length = length +} + +func (e ExtentLong) HasExtended() bool { + return (e.Length >> 30) == 3 +} + +func (e ExtentLong) IsNotRecorded() bool { + return (e.Length & UDF_EXTENT_FLAG_MASK) == EXT_NOT_RECORDED_ALLOCATED || (e.Length & UDF_EXTENT_FLAG_MASK) == EXT_NOT_RECORDED_NOT_ALLOCATED } func NewExtentLong(b []byte) ExtentLong { return ExtentLong{ Length: rl_u32(b[0:]), - Location: rl_u48(b[4:]), + Location: new(LbAddr).FromBytes(b[4:]), + } +} + +type ExtentExtended struct { + ExtentLength uint32 + RecordedLength uint32 + InfoLength uint32 + Location LbAddr +} + +func (e ExtentExtended) GetPartition() uint16 { + return e.Location.PartitionReferenceNumber +} + +func (e ExtentExtended) GetLocation() uint64 { + return uint64(e.Location.LogicalBlockNumber) +} + +func (e ExtentExtended) GetLength() uint32 { + return e.InfoLength +} + +func (e ExtentExtended) SetLength(length uint32) { + e.InfoLength = length +} + +func (e ExtentExtended) HasExtended() bool { + return (e.GetLength() >> 30) == 3 +} + +func (e ExtentExtended) IsNotRecorded() bool { + return false +} + +func NewExtentExtended(b []byte) ExtentExtended { + return ExtentExtended{ + ExtentLength: rl_u32(b[0:]), + RecordedLength: rl_u32(b[4:]), + InfoLength: rl_u32(b[8:]), + Location: new(LbAddr).FromBytes(b[12:]), } } + +type AED struct { + Descriptor Descriptor + PreviousAllocationExtentLocation uint32 + LengthOfAllocationDescriptors uint32 +} + +func (a *AED) FromBytes(b []byte) AED { + a.Descriptor.FromBytes(b) + a.PreviousAllocationExtentLocation = rl_u32(b[16:]) + a.LengthOfAllocationDescriptors = rl_u32(b[20:]) + return *a +} + +type LbAddr struct { + LogicalBlockNumber uint32 + PartitionReferenceNumber uint16 +} + +func (l *LbAddr) FromBytes(data []byte) LbAddr { + l.LogicalBlockNumber = rl_u32(data[0:]) + l.PartitionReferenceNumber = rl_u16(data[4:]) + return *l +} diff --git a/file.go b/file.go index 90367db..b7d8e14 100644 --- a/file.go +++ b/file.go @@ -6,41 +6,29 @@ import ( "time" ) +// File is a os.FileInfo-compatible wrapper around an ISO-13346 "UDF" directory entry type File struct { Udf *Udf Fid *FileIdentifierDescriptor - fe *FileEntry + fe FileEntryInterface fileEntryPosition uint64 } -func (f *File) GetFileEntryPosition() int64 { - return int64(f.fileEntryPosition) -} - -func (f *File) GetFileOffset() int64 { - return SECTOR_SIZE * (int64(f.FileEntry().AllocationDescriptors[0].Location) + int64(f.Udf.PartitionStart())) -} - -func (f *File) FileEntry() *FileEntry { - if f.fe == nil { - f.fileEntryPosition = f.Fid.ICB.Location - f.fe = NewFileEntry(f.Udf.ReadSector(f.Udf.PartitionStart() + f.fileEntryPosition)) - } - return f.fe -} - -func (f *File) NewReader() *io.SectionReader { - return io.NewSectionReader(f.Udf.r, f.GetFileOffset(), f.Size()) +// IsDir returns true if the entry is a directory or false otherwise +func (f *File) IsDir() bool { + return f.FileEntry().GetICBTag().FileType == 4 } -func (f *File) Name() string { - return f.Fid.FileIdentifier +// ModTime returns the entry's recording time +func (f *File) ModTime() time.Time { + return f.FileEntry().GetModificationTime() } +// Mode returns os.FileMode flag set with the os.ModeDir flag enabled in case of directories func (f *File) Mode() os.FileMode { var mode os.FileMode - perms := os.FileMode(f.FileEntry().Permissions) + perms := os.FileMode(f.FileEntry().GetPermissions()) mode |= ((perms >> 0) & 7) << 0 mode |= ((perms >> 5) & 7) << 3 mode |= ((perms >> 10) & 7) << 6 @@ -52,23 +40,156 @@ func (f *File) Mode() os.FileMode { return mode } -func (f *File) Size() int64 { - return int64(f.FileEntry().InformationLength) -} - -func (f *File) ModTime() time.Time { - return f.FileEntry().ModificationTime +// Name returns the base name of the given entry +func (f *File) Name() string { + return f.Fid.FileIdentifier } -func (f *File) IsDir() bool { - // TODO :Fix! This field always 0 :( - return f.FileEntry().ICBTag.FileType == 4 +// Size returns the size in bytes of the extent occupied by the file or directory +func (f *File) Size() int64 { + return int64(f.FileEntry().GetInformationLength()) } func (f *File) Sys() interface{} { return f.Fid } +// ReadDir returns the children entries in case of a directory func (f *File) ReadDir() []File { return f.Udf.ReadDir(f.FileEntry()) } + +func (f *File) GetFileEntryPosition() int64 { + return int64(f.fileEntryPosition) +} + +func (f *File) FileEntry() FileEntryInterface { + if f.fe == nil { + f.fileEntryPosition = uint64(f.Fid.ICB.GetLocation()) + meta := f.Udf.LogicalPartitionStart(f.Fid.ICB.GetPartition()) + f.fe = NewFileEntry(f.Fid.ICB.GetPartition(), f.Udf.ReadSector(meta+f.fileEntryPosition)) + } + return f.fe +} + +func (f *File) getReaders(descs []ExtentInterface, filePos int64) (readers []*sectionReader, finalFilePos int64) { + finalFilePos = filePos + for i := 0; i < len(descs); i++ { + if descs[i].HasExtended() { + extendData := f.Udf.ReadSector(f.Udf.LogicalPartitionStart(descs[i].GetPartition()) + descs[i].GetLocation()) + aed := new(AED).FromBytes(extendData) + var subReaders []*sectionReader + subReaders, finalFilePos = f.getReaders(GetAllocationDescriptors(f.FileEntry().GetICBTag().AllocationType, extendData[24:], aed.LengthOfAllocationDescriptors), finalFilePos) + readers = append(readers, subReaders...) + } else if !descs[i].IsNotRecorded() { + readers = append(readers, newSectionReader(finalFilePos, f.Udf.r, int64(f.Udf.SECTOR_SIZE)*int64(f.Udf.LogicalPartitionStart(descs[i].GetPartition())+descs[i].GetLocation()), int64(descs[i].GetLength()))) + } + finalFilePos += int64(descs[i].GetLength()) + } + return +} + +func (f *File) NewReader() *MultiSectionReader { + descs := f.FileEntry().GetAllocationDescriptors() + readers, _ := f.getReaders(descs, 0) + return newMultiSectionReader(readers) +} + +type sectionReader struct { + *io.SectionReader + start int64 + size int64 +} + +func newSectionReader(inFileStart int64, reader io.ReaderAt, start int64, size int64) *sectionReader { + return §ionReader{ + io.NewSectionReader(reader, start, size), + inFileStart, + size, + } +} + +type MultiSectionReader struct { + readers []*sectionReader + pos int64 + size int64 + index int +} + +func newMultiSectionReader(readers []*sectionReader) *MultiSectionReader { + var size int64 + for _, reader := range readers { + size += reader.size + } + return &MultiSectionReader{ + readers: readers, + size: size, + } +} + +func (r *MultiSectionReader) actualRead(p []byte) (n int, err error) { + n, err = r.readers[r.index].Read(p) + r.pos += int64(n) + if err != nil { + if r.index+1 < len(r.readers) { + r.index++ + r.readers[r.index].Seek(0, 0) + if n == 0 { + n, err = r.actualRead(p) + } + } + } + if n > 0 { + err = nil + } + return +} + +func (r *MultiSectionReader) Read(p []byte) (n int, err error) { + if r.index > len(r.readers)-1 { + return 0, io.EOF + } + return r.actualRead(p) +} + +func (r *MultiSectionReader) Seek(offset int64, whence int) (n int64, err error) { + switch whence { + case io.SeekStart: + n = offset + case io.SeekCurrent: + n = offset + r.pos + case io.SeekEnd: + n = offset + r.size + } + if n > r.size { + return 0, io.EOF + } + r.pos = n + for i, reader := range r.readers { + if reader.start <= n && reader.start+reader.size >= n { + _, err = reader.Seek(n-reader.start, io.SeekStart) + r.index = i + break + } + } + return +} + +func (r *MultiSectionReader) ReadAt(p []byte, off int64) (n int, err error) { + var read int + err = os.ErrNotExist // No readers + for _, reader := range r.readers { + if reader.start <= off+int64(n) && reader.start+reader.size >= off+int64(n) { + read, err = reader.ReadAt(p[n:], off+int64(n)-reader.start) + n += read + if (err != nil && err != io.EOF) || cap(p) == n { + return + } + } + } + return +} + +func (r *MultiSectionReader) Size() int64 { + return r.size +} diff --git a/icb.go b/icb.go index 0a21d81..d692cd0 100644 --- a/icb.go +++ b/icb.go @@ -1,5 +1,14 @@ package udf +type AllocationType int + +const ( + ShortDescriptors AllocationType = iota + LongDescriptors + ExtendedDescriptors + Embedded +) + type ICBTag struct { PriorRecordedNumberOfDirectEntries uint32 StrategyType uint16 @@ -8,6 +17,7 @@ type ICBTag struct { FileType uint8 ParentICBLocation uint64 Flags uint16 + AllocationType AllocationType } func (itag *ICBTag) FromBytes(b []byte) *ICBTag { @@ -15,9 +25,10 @@ func (itag *ICBTag) FromBytes(b []byte) *ICBTag { itag.StrategyType = rl_u16(b[4:]) itag.StrategyParameter = rl_u16(b[4:]) itag.MaximumNumberOfEntries = rl_u16(b[8:]) - itag.FileType = r_u8(b[1:]) + itag.FileType = r_u8(b[11:]) itag.ParentICBLocation = rl_u48(b[12:]) itag.Flags = rl_u16(b[18:]) + itag.AllocationType = (AllocationType)(itag.Flags & 0x3) return itag } diff --git a/isoinfo/isoinfo.go b/isoinfo/isoinfo.go index 84e53f5..010a9ba 100644 --- a/isoinfo/isoinfo.go +++ b/isoinfo/isoinfo.go @@ -5,7 +5,7 @@ import ( "fmt" "os" - "github.com/mogaika/udf" + "github.com/Xmister/udf" ) func printDir(spaces string, files []udf.File) { @@ -24,7 +24,10 @@ func main() { panic(err) } - u := udf.NewUdfFromReader(rdr) + u, err := udf.NewUdfFromReader(rdr) + if err != nil { + panic(err) + } printDir("", u.ReadDir(nil)) } diff --git a/udf.go b/udf.go index 058862a..aded3e1 100644 --- a/udf.go +++ b/udf.go @@ -1,57 +1,55 @@ package udf import ( + "errors" "io" + "unsafe" ) -const SECTOR_SIZE = 2048 - +// Udf is a wrapper around an .iso file that allows reading its ISO-13346 "UDF" data type Udf struct { - r io.ReaderAt - isInited bool - pvd *PrimaryVolumeDescriptor - pd *PartitionDescriptor - lvd *LogicalVolumeDescriptor - fsd *FileSetDescriptor - root_fe *FileEntry + r io.ReaderAt + isInited bool + pvd *PrimaryVolumeDescriptor + pd map[uint16]*PartitionDescriptor + lvd *LogicalVolumeDescriptor + fsd *FileSetDescriptor + root_fe FileEntryInterface + SECTOR_SIZE uint64 } -func (udf *Udf) PartitionStart() uint64 { - if udf.pd == nil { - panic(udf) - } else { - return uint64(udf.pd.PartitionStartingLocation) +// NewUdfFromReader returns an Udf reader reading from a given file +func NewUdfFromReader(r io.ReaderAt) (*Udf, error) { + udf := &Udf{ + r: r, + isInited: false, + pd: make(map[uint16]*PartitionDescriptor), } -} -func (udf *Udf) GetReader() io.ReaderAt { - return udf.r + err := udf.init() + return udf, err } -func (udf *Udf) ReadSectors(sectorNumber uint64, sectorsCount uint64) []byte { - buf := make([]byte, SECTOR_SIZE*sectorsCount) - readed, err := udf.r.ReadAt(buf[:], int64(SECTOR_SIZE*sectorNumber)) - if err != nil { - panic(err) - } - if readed != int(SECTOR_SIZE*sectorsCount) { - panic(readed) +func (udf *Udf) init() (err error) { + if udf.isInited { + return } - return buf[:] -} -func (udf *Udf) ReadSector(sectorNumber uint64) []byte { - return udf.ReadSectors(sectorNumber, 1) -} + var anchorDesc *AnchorVolumeDescriptorPointer -func (udf *Udf) init() { - if udf.isInited { - return + for udf.SECTOR_SIZE = 512; udf.SECTOR_SIZE <= 32768; udf.SECTOR_SIZE <<= 1 { + anchorDesc = NewAnchorVolumeDescriptorPointer(udf.ReadSector(256)) + if anchorDesc.Descriptor.TagIdentifier == DESCRIPTOR_ANCHOR_VOLUME_POINTER && + anchorDesc.Descriptor.TagChecksum == anchorDesc.Descriptor.Checksum() { + break + } } + //fmt.Printf("udf.SECTOR_SIZE = %d\n", udf.SECTOR_SIZE) - anchorDesc := NewAnchorVolumeDescriptorPointer(udf.ReadSector(256)) - if anchorDesc.Descriptor.TagIdentifier != DESCRIPTOR_ANCHOR_VOLUME_POINTER { - panic(anchorDesc.Descriptor.TagIdentifier) + if anchorDesc.Descriptor.TagIdentifier != DESCRIPTOR_ANCHOR_VOLUME_POINTER || + anchorDesc.Descriptor.TagChecksum != anchorDesc.Descriptor.Checksum() { + err = errors.New("couldn't find sector size") + return } for sector := uint64(anchorDesc.MainVolumeDescriptorSeq.Location); ; sector++ { @@ -63,38 +61,84 @@ func (udf *Udf) init() { case DESCRIPTOR_PRIMARY_VOLUME: udf.pvd = desc.PrimaryVolumeDescriptor() case DESCRIPTOR_PARTITION: - udf.pd = desc.PartitionDescriptor() + pd := desc.PartitionDescriptor() + udf.pd[pd.PartitionNumber] = pd case DESCRIPTOR_LOGICAL_VOLUME: udf.lvd = desc.LogicalVolumeDescriptor() } } - partitionStart := udf.PartitionStart() + // DEBUGGING ONLY + // udf.pvd.Show() + // for i, pd := range udf.pd { + // pd.Show(i) + // } + // udf.lvd.Show() + // DEBUGGING ONLY - end + + for i, pMap := range udf.lvd.PartitionMaps { + if pMap.PartitionMapType != 2 { + // Check to error early if there is no match with a partition number + if _, ok := udf.pd[pMap.PartitionNumber]; !ok { + return errors.New("could not find partition number") + } + udf.lvd.PartitionMaps[i].PartitionStart = udf.pd[pMap.PartitionNumber].PartitionStartingLocation + continue + } + metaFile := NewFileEntry(0, udf.ReadSector(uint64(udf.pd[pMap.PartitionNumber].PartitionStartingLocation))) + if metaFile != nil && len(metaFile.GetAllocationDescriptors()) > 0 { + udf.lvd.PartitionMaps[i].PartitionStart = uint32(metaFile.GetAllocationDescriptors()[0].GetLocation()) + udf.pd[pMap.PartitionNumber].PartitionStartingLocation + } + } + + partitionStart := udf.LogicalPartitionStart(udf.lvd.LogicalVolumeContentsUse.GetPartition()) - udf.fsd = NewFileSetDescriptor(udf.ReadSector(partitionStart + udf.lvd.LogicalVolumeContentsUse.Location)) - udf.root_fe = NewFileEntry(udf.ReadSector(partitionStart + udf.fsd.RootDirectoryICB.Location)) + udf.fsd = NewFileSetDescriptor(udf.ReadSector(partitionStart + uint64(udf.lvd.LogicalVolumeContentsUse.Location.LogicalBlockNumber))) + rootICB := udf.fsd.RootDirectoryICB + udf.root_fe = NewFileEntry(udf.lvd.LogicalVolumeContentsUse.GetPartition(), udf.ReadSector(udf.LogicalPartitionStart(rootICB.GetPartition())+rootICB.GetLocation())) udf.isInited = true + return } -func (udf *Udf) ReadDir(fe *FileEntry) []File { - udf.init() +func (udf *Udf) ReadSector(sectorNumber uint64) []byte { + return udf.ReadSectors(sectorNumber, 1) +} +func (udf *Udf) ReadSectors(sectorNumber uint64, sectorsCount uint64) []byte { + buf := make([]byte, udf.SECTOR_SIZE*sectorsCount) + read, err := udf.r.ReadAt(buf[:], int64(udf.SECTOR_SIZE*sectorNumber)) + if err != nil { + panic(err) + } + /*if readed != int(udf.SECTOR_SIZE*sectorsCount) { + panic(readed) + }*/ + return buf[:read] +} + +func (udf *Udf) ReadDir(fe FileEntryInterface) []File { + udf.init() if fe == nil { fe = udf.root_fe } - ps := udf.PartitionStart() - - adPos := fe.AllocationDescriptors[0] - fdLen := uint64(adPos.Length) + ps := udf.LogicalPartitionStart(fe.GetPartition()) + adPos := fe.GetAllocationDescriptors()[0] + fdLen := uint64(adPos.GetLength()) - fdBuf := udf.ReadSectors(ps+uint64(adPos.Location), (fdLen+SECTOR_SIZE-1)/SECTOR_SIZE) + fdBuf := udf.ReadSectors(ps+adPos.GetLocation(), (fdLen+udf.SECTOR_SIZE-1)/udf.SECTOR_SIZE) fdOff := uint64(0) result := make([]File, 0) - - for uint32(fdOff) < adPos.Length { + var dummy *FileIdentifierDescriptor + fidSize := int(unsafe.Sizeof(*dummy)) + for uint32(fdOff) < adPos.GetLength() { + // Some Windows ISOs have some padding data that we can ignore? + if len(fdBuf[fdOff:]) < fidSize { + //fmt.Printf("WARNING: skipping incomplete data\n") + break + } fid := NewFileIdentifierDescriptor(fdBuf[fdOff:]) if fid.FileIdentifier != "" { result = append(result, File{ @@ -104,15 +148,26 @@ func (udf *Udf) ReadDir(fe *FileEntry) []File { } fdOff += fid.Len() } - return result } -func NewUdfFromReader(r io.ReaderAt) *Udf { - udf := &Udf{ - r: r, - isInited: false, +// XXX - unused +func (udf *Udf) PhysicalPartitionStart(partition uint16) (physical uint64) { + if udf.pd == nil { + panic(udf) + } else { + return uint64(udf.pd[partition].PartitionStartingLocation) } +} - return udf +func (udf *Udf) LogicalPartitionStart(partition uint16) (logical uint64) { + if udf.lvd == nil || len(udf.lvd.PartitionMaps) < 1 { + panic(udf) + } else { + return uint64(udf.lvd.PartitionMaps[partition].PartitionStart) + } +} + +func (udf *Udf) GetReader() io.ReaderAt { + return udf.r }