summaryrefslogtreecommitdiff
path: root/vendor/github.com/hooklift/iso9660/iso9660.go
blob: c5a114cd91ef00f96c212b7be27a161274007997 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
// 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
package iso9660

import (
	"io"
	"os"
	"strings"
	"time"
)

// File represents a concrete implementation of os.FileInfo interface for
// accessing ISO 9660 file data
type File struct {
	DirectoryRecord
	fileID string
	// We have the raw image here only to be able to access file extents
	image *os.File
}

// Name returns the file's name.
func (f *File) Name() string {
	name := strings.Split(f.fileID, ";")[0]
	return strings.ToLower(name)
}

// Size returns the file size in bytes
func (f *File) Size() int64 {
	return int64(f.ExtentLengthBE)
}

// 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 (f *File) Mode() os.FileMode {
	if f.IsDir() {
		return os.FileMode(0740)
	}
	return os.FileMode(0640)
}

// ModTime returns file's modification time.
func (f *File) ModTime() time.Time {
	return time.Now()
}

// IsDir tells whether the file is a directory or not.
func (f *File) IsDir() bool {
	if (f.FileFlags & 2) == 2 {
		return true
	}
	return false
}

// Sys returns io.Reader instance pointing to the file's content if it is not a directory, nil otherwise.
func (f *File) Sys() interface{} {
	if f.IsDir() {
		return nil
	}

	return io.NewSectionReader(f.image, int64(f.ExtentLocationBE*sectorSize), int64(f.ExtentLengthBE))
}

const (
	bootRecord       = 0
	primaryVol       = 1
	supplementaryVol = 2
	volPartition     = 3
	volSetTerminator = 255
	// System area goes from sectors 0x00 to 0x0F. Volume descriptors can be
	// found starting at sector 0x10
	dataAreaSector = 0x10
	sectorSize     = 2048
)

// 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.
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
}

// 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 BootRecord struct {
	VolumeDescriptor
	SystemID  [32]byte
	ID        [32]byte
	SystemUse [1977]byte
}

// Terminator indicates the termination of a Volume Descriptor Set.
type Terminator struct {
	VolumeDescriptor
	// All bytes of this field are set to (00).
	Reserved [2041]byte
}

// 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 PrimaryVolumePart1 struct {
	VolumeDescriptor
	// Unused
	_ byte // 00
	// 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
	//Unused2
	_ [8]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
}

// 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 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
	// The interpretation of this field depends as follows on the setting of the
	// Directory bit of the File Flags field. If set to ZERO, it shall mean:
	//
	// − The field shall specify an identification for the file.
	// − The characters in this field shall be d-characters or d1-characters, SEPARATOR 1, SEPARATOR 2.
	// − The field shall be recorded as specified in 7.5. If set to ONE, it shall mean:
	// − The field shall specify an identification for the directory.
	// − The characters in this field shall be d-characters or d1-characters, or only a (00) byte, or only a (01) byte.
	// − The field shall be recorded as specified in 7.6.
	// fileID string
}

// PrimaryVolumePart2 represents the Primary Volume Descriptor half after the
// root directory record.
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
	// Reserved. Always 0x00.
	_ byte
	// Contents not defined by ISO 9660.
	AppUse [512]byte
	// Reserved by ISO.
	_ [653]byte
}

// 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 PrimaryVolume struct {
	PrimaryVolumePart1
	DirectoryRecord DirectoryRecord
	PrimaryVolumePart2
}

// SupplementaryVolume is used by Joliet.
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
}

// 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
}

// 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"`
}

// 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 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"`
}

// 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 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
}