Module: Iso9660

Included in:
FileObject
Defined in:
lib/fs/iso9660/util.rb,
lib/fs/iso9660/directory.rb,
lib/fs/iso9660/file_data.rb,
lib/fs/iso9660/rock_ridge.rb,
lib/fs/iso9660/boot_sector.rb,
lib/fs/MiqFS/modules/Iso9660.rb,
lib/fs/iso9660/directory_entry.rb

Defined Under Namespace

Modules: Util Classes: AlternateName, BootSector, Directory, DirectoryEntry, Extension, FileData, FileObject, PosixAttributes, RockRidge, SparseFile, SymbolicLink

Constant Summary collapse

RR_SIGNATURE =

NOTE: This implementation is sufficient for Rock Ridge extensions. It will not identify or process any other System Use Sharing Protocol extensions. In particular, SUSP CE (continuation area) records are not processed. This is where the “RR” extension is defined. This implementation uses the assumed definition RR_HEADER.

"RR"
RR_HEADER =
BinaryStruct.new([
  'a2', 'signature',  # RR if Rock Ridge.
  'C',  'unused1',    # ? always seems to be 5.
  'C',  'unused2',    # ? always seems to be 1 (version?).
  'C',  'unused3'     # ? 0x81
])
RR_HEADER_SIZE =
5
RR_PN_SIGNATURE =

These types are not used, but we want to know if they pop up.

"PN"
RR_CL_SIGNATURE =
"CL"
RR_PL_SIGNATURE =
"PL"
RR_RE_SIGNATURE =
"RE"
RR_TF_SIGNATURE =
"TF"
RR_PX_SIGNATURE =

POSIX file attributes. See POSIX 5.6.1.

"PX"
RR_PX =
BinaryStruct.new([
  'L',  'modeLE',   # file mode (used to identify a link).
  'L',  'modeBE',
  'L',  'linksLE',  # Num links (st_nlink).
  'L',  'linksBE',
  'L',  'userLE',   # User ID.
  'L',  'userBE',
  'L',  'groupLE',  # Group ID.
  'L',  'groupBE'
  # 'L', 'serialLE', # File serial number.
  # 'L', 'serialBE'
])
RR_EXT_SL_FM_SOCK =

File mode bits.

0xc000
0xa000
RR_EXT_SL_FM_FILE =
0x8000
RR_EXT_SL_FM_BLOK =
0x6000
RR_EXT_SL_FM_CHAR =
0x2000
RR_EXT_SL_FM_DIR =
0x4000
RR_EXT_SL_FM_FIFO =
0x1000
RR_SL_SIGNATURE =

Symbolic link.

"SL"
RR_SL =
BinaryStruct.new([
  'C',  'flags',  # See RR_EXT_SLF_ below.
  'a*', 'components'
])
RR_EXT_SLF_CONTINUE =

Symbolic link flags.

0x01
RR_SL_COMPONENT =

A symbolic link component record.

BinaryStruct.new([
  'C',  'flags',  # See RR_EXT_SLCOMPF_ below.
  'C',  'length', # Length of content in bytes.
  'a*', 'content'
])
RR_EXT_SLCOMPF_CONTINUE =

Component continues in the next component record.

0x01
RR_EXT_SLCOMPF_CURRENT =

Component refers to the current directory.

0x02
RR_EXT_SLCOMPF_PARENT =

Component refers to the parent directory.

0x04
RR_EXT_SLCOMPF_ROOT =

Component refers to the root directory.

0x08
RR_EXT_SLCOMPF_RESERVED1 =

See below.

0x10
RR_EXT_CLCOMPF_RESERVED2 =

See below.

0x20
RR_NM_SIGNATURE =

Alternate name.

"NM"
RR_NM =
BinaryStruct.new([
  'C',  'flags',  # See RR_EXT_NMF_ below.
  'a*', 'content'
])
RR_EXT_NMF_CONTINUE =

NOTE: These flag bits are mutually exclusive.

0x01
RR_EXT_NMF_CURRENT =

Name continues in the next NM entry.

0x02
RR_EXT_NMF_PARENT =

Name refers to the current directory.

0x04
RR_EXT_NMF_RESERVED1 =

Name refers to the parent directory.

0x08
RR_EXT_NMF_RESERVED2 =

Reserved - 0.

0x10
RR_EXT_NMF_RESERVED3 =

Reserved - 0.

0x20
RR_SF_SIGNATURE =

Sparse File.

"SF"
RR_SF =
BinaryStruct.new([
  'L',  'size_hiLE',
  'L',  'size_hiBE',
  'L',  'size_loLE',
  'L',  'size_loBE',
  'C',  'table_depth',
])
RR_EXT_HEADER =

Common to all RR extensions.

BinaryStruct.new([
  'a2', 'signature',  # Extension type.
  'C',  'length',     # length in bytes.
  'C',  'version'     # Entry version, always 1.
])
RR_EXT_HEADER_SIZE =
4
DEF_CACHE_SIZE =

Default cache size.

50
PRIMARY_SECTOR =
16
JOLIET_SECTOR =
17
SECTOR_SIZE =
2048
FB_HIDDEN =

FlagBits: FB_

0x01
FB_DIRECTORY =

0 if not hidden.

0x02
FB_ASSOCIATED =

0 if file.

0x04
FB_RFS =

0 if not ‘associated’ (?)

0x08
FB_PS =

RecordFormatSpecified: 0 if not.

0x10
FB_UNUSED1 =

PermissionsSpecified: 0 if not.

0x20
FB_UNUSED2 =

No info.

0x40
FB_NOT_LAST =

No info.

0x80
EXT_NONE =

Extensions.

0
EXT_JOLIET =
1
EXT_ROCKRIDGE =
2

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#boot_sectorObject

Members (these become members of an MiqFS instance).



18
19
20
# File 'lib/fs/MiqFS/modules/Iso9660.rb', line 18

def boot_sector
  @boot_sector
end

#cache_hitsObject

Members (these become members of an MiqFS instance).



18
19
20
# File 'lib/fs/MiqFS/modules/Iso9660.rb', line 18

def cache_hits
  @cache_hits
end

#dir_cacheObject

Members (these become members of an MiqFS instance).



18
19
20
# File 'lib/fs/MiqFS/modules/Iso9660.rb', line 18

def dir_cache
  @dir_cache
end

#drive_rootObject

Members (these become members of an MiqFS instance).



18
19
20
# File 'lib/fs/MiqFS/modules/Iso9660.rb', line 18

def drive_root
  @drive_root
end

Instance Method Details

#fs_dirEntries(p) ⇒ Object

Returns String array of all names, sans path.



94
95
96
97
98
99
# File 'lib/fs/MiqFS/modules/Iso9660.rb', line 94

def fs_dirEntries(p)
  # Get path directory.
  dir = ifs_getDir(p)
  return nil if dir.nil?
  dir.globNames
end

#fs_dirMkdir(_p) ⇒ Object

These two are not implemented.



102
103
104
# File 'lib/fs/MiqFS/modules/Iso9660.rb', line 102

def fs_dirMkdir(_p)
  raise "Write functionality is not supported on Iso9660."
end

#fs_dirRmdir(_p) ⇒ Object

Remove a directory.



107
108
109
# File 'lib/fs/MiqFS/modules/Iso9660.rb', line 107

def fs_dirRmdir(_p)
  raise "Write functionality is not supported on Iso9660."
end

#fs_fileAtime(p) ⇒ Object

Returns Ruby Time object.



149
150
151
152
153
# File 'lib/fs/MiqFS/modules/Iso9660.rb', line 149

def fs_fileAtime(p)
  de = ifs_getFile(p)
  return nil if de.nil?
  de.date
end

#fs_fileAtime_obj(fobj) ⇒ Object

Returns a Ruby Time object.



182
183
184
# File 'lib/fs/MiqFS/modules/Iso9660.rb', line 182

def fs_fileAtime_obj(fobj)
  fobj.de.date
end

#fs_fileClose(_fobj) ⇒ Object

Destroy file object.



221
222
223
# File 'lib/fs/MiqFS/modules/Iso9660.rb', line 221

def fs_fileClose(_fobj)
  fobj = nil
end

#fs_fileCtime(p) ⇒ Object

Returns Ruby Time object.



156
157
158
159
160
# File 'lib/fs/MiqFS/modules/Iso9660.rb', line 156

def fs_fileCtime(p)
  de = ifs_getFile(p)
  return nil if de.nil?
  de.date
end

#fs_fileCtime_obj(fobj) ⇒ Object

Returns a Ruby Time object.



187
188
189
# File 'lib/fs/MiqFS/modules/Iso9660.rb', line 187

def fs_fileCtime_obj(fobj)
  fobj.de.date
end

#fs_fileDelete(_p) ⇒ Object

Delete file.



144
145
146
# File 'lib/fs/MiqFS/modules/Iso9660.rb', line 144

def fs_fileDelete(_p)
  raise "Write functionality is not supported on Iso9660."
end

#fs_fileDirectory?(p) ⇒ Boolean

Returns true if name is a directory.

Returns:

  • (Boolean)


130
131
132
133
134
# File 'lib/fs/MiqFS/modules/Iso9660.rb', line 130

def fs_fileDirectory?(p)
  de = ifs_getFile(p)
  return false if de.nil?
  de.isDir?
end

#fs_fileExists?(p) ⇒ Boolean

Returns true if name exists, false if not.

Returns:

  • (Boolean)


116
117
118
119
120
# File 'lib/fs/MiqFS/modules/Iso9660.rb', line 116

def fs_fileExists?(p)
  de = ifs_getFile(p)
  return false if de.nil?
  true
end

#fs_fileFile?(p) ⇒ Boolean

Returns true if name is a regular file.

Returns:

  • (Boolean)


123
124
125
126
127
# File 'lib/fs/MiqFS/modules/Iso9660.rb', line 123

def fs_fileFile?(p)
  de = ifs_getFile(p)
  return false if de.nil?
  de.isFile?
end

#fs_fileMtime(p) ⇒ Object

Returns Ruby Time object.



163
164
165
166
167
# File 'lib/fs/MiqFS/modules/Iso9660.rb', line 163

def fs_fileMtime(p)
  de = ifs_getFile(p)
  return nil if de.nil?
  de.date
end

#fs_fileMtime_obj(fobj) ⇒ Object

Returns a Ruby Time obect.



192
193
194
# File 'lib/fs/MiqFS/modules/Iso9660.rb', line 192

def fs_fileMtime_obj(fobj)
  fobj.de.date
end

#fs_fileOpen(p, mode = "r") ⇒ Object

New FileObject instance. NOTE: FileObject must have access to Iso9660 members. This is kind of like a ‘skip this’ thing. Ext3 methods use stuff owned by MiqFS, so this is necessary.



200
201
202
203
204
# File 'lib/fs/MiqFS/modules/Iso9660.rb', line 200

def fs_fileOpen(p, mode = "r")
  fobj = FileObject.new(p, self)
  fobj.open(mode)
  fobj
end

#fs_fileRead(fobj, len) ⇒ Object

Returns a Ruby String object.



212
213
214
# File 'lib/fs/MiqFS/modules/Iso9660.rb', line 212

def fs_fileRead(fobj, len)
  fobj.data.read(len)
end

#fs_fileSeek(fobj, offset, whence) ⇒ Object

Returns current file position.



207
208
209
# File 'lib/fs/MiqFS/modules/Iso9660.rb', line 207

def fs_fileSeek(fobj, offset, whence)
  fobj.data.seek(offset, whence)
end

#fs_fileSize(p) ⇒ Object

Returns size in bytes.



137
138
139
140
141
# File 'lib/fs/MiqFS/modules/Iso9660.rb', line 137

def fs_fileSize(p)
  de = ifs_getFile(p)
  return nil if de.nil?
  de.fileSize
end

#fs_fileSize_obj(fobj) ⇒ Object

In these, fobj is a FileObject.



177
178
179
# File 'lib/fs/MiqFS/modules/Iso9660.rb', line 177

def fs_fileSize_obj(fobj)
  fobj.de.fileSize
end

#fs_fileWrite(_fobj, _buf, _len) ⇒ Object



216
217
218
# File 'lib/fs/MiqFS/modules/Iso9660.rb', line 216

def fs_fileWrite(_fobj, _buf, _len)
  raise "Write functionality is not supported on Iso9660."
end

#fs_freeBytesObject

Returns free space on file system in bytes.



85
86
87
# File 'lib/fs/MiqFS/modules/Iso9660.rb', line 85

def fs_freeBytes
  0
end

#fs_initObject



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
# File 'lib/fs/MiqFS/modules/Iso9660.rb', line 46

def fs_init
  self.fsType = "ISO9660"

  # Start by looking for a Joliet volume descriptor after the primary descriptor.
  found_joliet = false
  @dobj.seek(JOLIET_SECTOR * SECTOR_SIZE)
  begin
    @boot_sector = BootSector.new(@dobj, true)
    found_joliet = true
  rescue
  end

  # If Joliet wasn't found, look for a primary descriptor.
  unless found_joliet
    found_primary = false
    @dobj.seek(PRIMARY_SECTOR * SECTOR_SIZE)
    loop do
      begin
        @boot_sector = BootSector.new(@dobj)
        found_primary = true
        break
      rescue
      end
    end
    raise "Iso9660#fs_init couldn't find primary descriptor." unless found_primary
  end

  # Set volume id.
  # @fsId = ... hmmm, how to get the serial number.
  @volName = @boot_sector.volName

  # Init cache & root.
  self.dir_cache = LruHash.new(DEF_CACHE_SIZE)
  self.cache_hits = 0
  de = DirectoryEntry.new(@boot_sector.rootEntry, @boot_sector.suff)
  self.drive_root = Directory.new(@boot_sector, de)
end

#fs_isSymLink?(p) ⇒ Boolean

Return true if p is a path to a symbolic link.

Returns:

  • (Boolean)


170
171
172
173
174
# File 'lib/fs/MiqFS/modules/Iso9660.rb', line 170

def fs_isSymLink?(p)
  de = ifs_getFile(p)
  return false if de.nil?
  de.isSymLink?
end

#ifs_getDir(p, miqfs = nil) ⇒ Object

Return a Directory object for a path. Raise error if path doesn’t exist.



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
# File 'lib/fs/MiqFS/modules/Iso9660.rb', line 254

def ifs_getDir(p, miqfs = nil)
  # If this is being called from a FileObject instance, then MiqFS owns contained instance members.
  # If this is being called from an Ext3 module method, then self owns contained instance members.
  miqfs = self if miqfs.nil?

  # Wack leading drive.
  p = unnormalizePath(p)

  # Check for this path in the cache.
  if miqfs.dir_cache.key?(p)
    miqfs.cache_hits += 1
    return Directory.new(miqfs.boot_sector, DirectoryEntry.new(miqfs.dir_cache[p], miqfs.boot_sector.suff))
  end

  # Return root if lone separator.
  return miqfs.drive_root if p == "/" || p == "\\"

  # Get an array of directory names, kill off the first (it's always empty).
  names = p.split(/[\\\/]/); names.shift

  # Find target dir.
  de = miqfs.drive_root.myEnt
  loop do
    break if names.empty?
    dir = Directory.new(miqfs.boot_sector, de)
    de = dir.findEntry(names.shift, Directory::FE_DIR)
    raise "Can't find directory: \'#{p}\'" if de.nil?
  end

  # Save dir ent in the cache & return a Directory.
  # NOTE: This stores only the directory entry data string - not the whole object.
  miqfs.dir_cache[p] = de.myEnt
  Directory.new(miqfs.boot_sector, de)
end

#ifs_getFile(p, miqfs = nil) ⇒ Object

Return a DirectoryEntry for a given file or nil if not exist.



228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
# File 'lib/fs/MiqFS/modules/Iso9660.rb', line 228

def ifs_getFile(p, miqfs = nil)
  # If this is being called from a FileObject instance, then MiqFS owns contained instance members.
  # If this is being called from an Ext3 module method, then self owns contained instance members.
  miqfs = self if miqfs.nil?

  # Preprocess path.
  p = unnormalizePath(p)
  dir, fil = File.split(p)
  # Fix for FB#835: if fil == root then fil needs to be "."
  fil = "." if fil == "/" || fil == "\\"

  # Look for file in dir, but don't barf if it doesn't exist.
  # NOTE: if p is a directory that's ok, find it.
  begin
    dirObj = ifs_getDir(dir, miqfs)
    return nil if dirObj.nil?
    dirEnt = dirObj.findEntry(fil)
    return nil if dirEnt.nil?
  rescue RuntimeError
    return nil
  end
  dirEnt
end

#unnormalizePath(p) ⇒ Object

Wack leading drive leter & colon.



290
291
292
# File 'lib/fs/MiqFS/modules/Iso9660.rb', line 290

def unnormalizePath(p)
  p[1] == 58 ? p[2, p.size] : p
end