Class: Fat32::BootSect
- Inherits:
-
Object
- Object
- Fat32::BootSect
- Defined in:
- lib/fs/fat32/boot_sect.rb
Overview
//////////////////////////////////////////////////////////////////////////// // Class.
Constant Summary collapse
- FAT_ENTRY_SIZE =
4
- FU_ONE_FAT =
0x0080
- FU_MSK_ACTIVE_FAT =
0x000f
- CC_NOT_ALLOCATED =
0
- CC_DAMAGED =
0x0ffffff7
- CC_END_OF_CHAIN =
0x0ffffff8
- CC_END_MARK =
0x0fffffff
- CC_VALUE_MASK =
0x0fffffff
Instance Attribute Summary collapse
-
#bytesPerCluster ⇒ Object
Members.
-
#bytesPerSector ⇒ Object
Members.
-
#fatBase ⇒ Object
readonly
Returns the value of attribute fatBase.
-
#fatSize ⇒ Object
readonly
Returns the value of attribute fatSize.
-
#freeClusters ⇒ Object
readonly
Returns the value of attribute freeClusters.
-
#fsId ⇒ Object
readonly
Returns the value of attribute fsId.
-
#rootBase ⇒ Object
readonly
Returns the value of attribute rootBase.
-
#rootCluster ⇒ Object
Members.
-
#volName ⇒ Object
readonly
Returns the value of attribute volName.
Instance Method Summary collapse
-
#allocClusters(start, num = 1) ⇒ Object
Allocate a number of clusters on a particular cluster chain or start a chain.
-
#clusToByte(clus = @rootCluster) ⇒ Object
Translate a cluster number to an absolute byte location.
-
#countContigClusters(clus) ⇒ Object
Count the number of continuous clusters from some beginning cluster.
-
#dump ⇒ Object
Dump object.
- #dumpFat(numEnt) ⇒ Object
-
#getCluster(clus) ⇒ Object
Get data for the requested cluster.
-
#getFatEntry(clus) ⇒ Object
Return the FAT entry for a cluster.
-
#getLocs ⇒ Object
Get absolute byte locations of the FAT & the root dir.
-
#getNextAvailableCluster(clus) ⇒ Object
Start from defined FAT entry and look for next available entry.
-
#getNextCluster(clus) ⇒ Object
Gets data for the next cluster given current, or nil if end.
-
#getToLimit(clus, limit) ⇒ Object
Return continuous data from a beginning cluster to limit bytes (or EOF).
-
#initialize(stream) ⇒ BootSect
constructor
Initialization.
-
#isMountable? ⇒ Boolean
//////////////////////////////////////////////////////////////////////////// // Class helpers & accessors.
- #mkClusterMap(clus) ⇒ Object
- #oemName ⇒ Object
-
#putCluster(clus, buf) ⇒ Object
Write data to a cluster.
-
#putFatEntry(clus, value) ⇒ Object
Write a FAT entry for a cluster.
-
#to_s ⇒ Object
String rep.
-
#wipeChain(clus) ⇒ Object
Deallocate all clusters on a chain from a starting cluster number.
-
#writeClusters(start, buf, len = buf.length) ⇒ Object
Start from defined cluster number and write data, following allocated cluster chain.
Constructor Details
#initialize(stream) ⇒ BootSect
Initialization
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 |
# File 'lib/fs/fat32/boot_sect.rb', line 83 def initialize(stream) raise "Nil stream" if stream.nil? # Init all. @bytesPerSector = 0; @bytesPerCluster = 0; @fatBase = 0 @fatSize = 0; @rootBase = 0; @mountable = false # Buffer stream, read & decode boot sector. @stream = stream buf = stream.read(SIZEOF_BOOT_SECT) raise "Couldn't read boot sector." if buf.nil? @bs = BOOT_SECT.decode(buf) raise "Couldn't decode boot sector." if @bs.nil? # Bytes per sector must be 512, 1024, 2048 or 4096 bps = @bs['bytes_per_sec'] raise "Bytes per sector value invalid: #{bps}" if bps != 512 && bps != 1024 && bps != 2048 && bps != 4096 @bytesPerSector = bps # Cluster size must be 32K or smaller. bpc = @bs['sec_per_clus'] * bps raise "Sectors per cluster value invalid: #{bpc} (#{bps}bps * #{@bs['sec_per_clus']}spc)" if bpc > 32768 @bytesPerCluster = bpc # Get free clusters. stream.seek(@bs['fsinfo_sec'] * @bytesPerSector) @fsinfo = FSINFO.decode(stream.read(@bytesPerSector)) if @fsinfo['sig1'] == 'RRaA' && @fsinfo['sig2'] == 'rrAa' && @fsinfo['sig3'] == 0xaa550000 @freeClusters = @fsinfo['free_clus'] else @freeClusters = 0 end # Expose volume information. @fsId = @bs['serial_num'] @volName = @bs['label'] # Verify FAT32 values according to Carrier. raise "Maximum files in root dir invalid: #{@bs['max_root']}\nIs partition FAT12/16?" if @bs['max_root'] != 0 raise "Number of sectors in FAT invalid: #{@bs['fat_size32']}\nIs partition FAT12/16?" if @bs['fat_size32'] == 0 raise "Unknown number of sectors in file system." if @bs['num_sec16'] == 0 && @bs['num_sec32'] == 0 raise "Boot sector signature invalid: 0x#{'%04x' % @bs['signature']}" if @bs['signature'] != 0xaa55 # Calc location of the FAT & root dir. @mountable = getLocs end |
Instance Attribute Details
#bytesPerCluster ⇒ Object
Members.
78 79 80 |
# File 'lib/fs/fat32/boot_sect.rb', line 78 def bytesPerCluster @bytesPerCluster end |
#bytesPerSector ⇒ Object
Members.
78 79 80 |
# File 'lib/fs/fat32/boot_sect.rb', line 78 def bytesPerSector @bytesPerSector end |
#fatBase ⇒ Object (readonly)
Returns the value of attribute fatBase.
79 80 81 |
# File 'lib/fs/fat32/boot_sect.rb', line 79 def fatBase @fatBase end |
#fatSize ⇒ Object (readonly)
Returns the value of attribute fatSize.
79 80 81 |
# File 'lib/fs/fat32/boot_sect.rb', line 79 def fatSize @fatSize end |
#freeClusters ⇒ Object (readonly)
Returns the value of attribute freeClusters.
79 80 81 |
# File 'lib/fs/fat32/boot_sect.rb', line 79 def freeClusters @freeClusters end |
#fsId ⇒ Object (readonly)
Returns the value of attribute fsId.
80 81 82 |
# File 'lib/fs/fat32/boot_sect.rb', line 80 def fsId @fsId end |
#rootBase ⇒ Object (readonly)
Returns the value of attribute rootBase.
79 80 81 |
# File 'lib/fs/fat32/boot_sect.rb', line 79 def rootBase @rootBase end |
#rootCluster ⇒ Object
Members.
78 79 80 |
# File 'lib/fs/fat32/boot_sect.rb', line 78 def rootCluster @rootCluster end |
#volName ⇒ Object (readonly)
Returns the value of attribute volName.
80 81 82 |
# File 'lib/fs/fat32/boot_sect.rb', line 80 def volName @volName end |
Instance Method Details
#allocClusters(start, num = 1) ⇒ Object
Allocate a number of clusters on a particular cluster chain or start a chain. Start can be anywhere on the chain, but most efficient when just before end. If start is 0 then start a chain (for file data).
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 |
# File 'lib/fs/fat32/boot_sect.rb', line 241 def allocClusters(start, num = 1) first = 0; clus = 0 if start == 0 # Start chain. first = getNextAvailableCluster(@rootCluster) putFatEntry(first, CC_END_MARK) clus = first; num -= 1 else # Allocate on chain - seek to end. clus = start # while (nxt = getFatEntry(clus)) <= CC_END_OF_CHAIN do clus = nxt end loop do nxt = getFatEntry(clus) break if nxt > CC_END_OF_CHAIN clus = nxt end end # Allocate num clusters, put end mark at end. num.times do nxt = getNextAvailableCluster(clus) first = nxt if first == 0 putFatEntry(clus, nxt) putCluster(nxt, MemoryBuffer.create(@bytesPerCluster)) clus = nxt putFatEntry(clus, CC_END_MARK) end first end |
#clusToByte(clus = @rootCluster) ⇒ Object
Translate a cluster number to an absolute byte location.
306 307 308 309 |
# File 'lib/fs/fat32/boot_sect.rb', line 306 def clusToByte(clus = @rootCluster) raise "Cluster is nil" if clus.nil? @bs['res_sec'] * @bytesPerSector + @fatSize * @bs['num_fats'] + (clus - 2) * @bytesPerCluster end |
#countContigClusters(clus) ⇒ Object
Count the number of continuous clusters from some beginning cluster.
227 228 229 230 231 232 233 234 235 236 |
# File 'lib/fs/fat32/boot_sect.rb', line 227 def countContigClusters(clus) cur = clus; nxt = 0 loop do nxt = getFatEntry(cur) break if nxt != cur + 1 cur = nxt; redo end raise "Damaged cluster in cluster chain" if nxt == CC_DAMAGED cur - clus + 1 end |
#dump ⇒ Object
Dump object.
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 |
# File 'lib/fs/fat32/boot_sect.rb', line 347 def dump out = "\#<#{self.class}:0x#{'%08x' % object_id}>\n" out += "Jump boot (hex) : #{'%02x %02x %02x' % @bs['jmp_boot'].unpack('C3')}\n" out += "OEM Name : #{@bs['oem_name']}\n" out += "Bytes per sector : #{@bs['bytes_per_sec']}\n" out += "Sectors per clus : #{@bs['sec_per_clus']}\n" out += "Reserved sectors : #{@bs['res_sec']}\n" out += "Number of FATs : #{@bs['num_fats']}\n" out += "Max files in root : #{@bs['max_root']}\n" out += "Sectors in FS(16) : 0x#{'%04x' % @bs['num_sec16']}\n" out += "Media type : 0x#{'%02x' % @bs['media_type']}\n" out += "Sectors in FAT(16): 0x#{'%04x' % @bs['fat_size16']}\n" out += "Sectors per track : #{@bs['sec_per_track']}\n" out += "Number of heads : #{@bs['num_heads']}\n" out += "Sectors pre start : #{@bs['pre_sec']}\n" out += "Sectors in FS(32) : 0x#{'%08x' % @bs['num_sec32']}\n" out += "Sectors in FAT(32): 0x#{'%08x' % @bs['fat_size32']}\n" out += "FAT usage flags : 0x#{'%04x' % @bs['fat_usage']}\n" out += "Version (MJ/MN) : 0x#{'%04x' % @bs['version']}\n" out += "Root cluster : #{@bs['root_clus']}\n" out += "FSINFO sector : #{@bs['fsinfo_sec']}\n" out += "Backup boot sector: #{@bs['boot_bkup']}\n" out += "Reserved : '#{@bs['reserved1']}'\n" out += "Drive number : #{@bs['drive_num']}\n" out += "Extended signature: 0x#{'%02x' % @bs['ex_sig']} (0x29?)\n" out += "Serial number : 0x#{'%08x' % @bs['serial_num']}\n" out += "Label : '#{@bs['label']}'\n" out += "File Sys label : '#{@bs['fs_label']}'\n" out += "Signature : 0x#{'%04x' % @bs['signature']} (0xaa55?)\n" out end |
#dumpFat(numEnt) ⇒ Object
338 339 340 341 342 343 344 |
# File 'lib/fs/fat32/boot_sect.rb', line 338 def dumpFat(numEnt) out = "" 0.upto(numEnt - 1) do|i| out += "#{i} #{'%08x' % getFatEntry(i)}\n" end out end |
#getCluster(clus) ⇒ Object
Get data for the requested cluster.
170 171 172 173 174 |
# File 'lib/fs/fat32/boot_sect.rb', line 170 def getCluster(clus) raise "Cluster is nil" if clus.nil? @stream.seek(clusToByte(clus)) @stream.read(@bytesPerCluster) end |
#getFatEntry(clus) ⇒ Object
Return the FAT entry for a cluster.
312 313 314 315 |
# File 'lib/fs/fat32/boot_sect.rb', line 312 def getFatEntry(clus) @stream.seek(@fatBase + FAT_ENTRY_SIZE * clus) @stream.read(FAT_ENTRY_SIZE).unpack('L')[0] & CC_VALUE_MASK end |
#getLocs ⇒ Object
Get absolute byte locations of the FAT & the root dir.
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
# File 'lib/fs/fat32/boot_sect.rb', line 152 def getLocs # Calculate the location of the (active) FAT (loc as absolute byte for seek). @fatBase = @bs['res_sec'] * @bytesPerSector @fatSize = @bs['fat_size32'] * @bytesPerSector fu = @bs['fat_usage'] if fu & FU_ONE_FAT == FU_ONE_FAT @fatBase += @fatSize * (fu & FU_MSK_ACTIVE_FAT) end return false if @fatBase == 0 || @fatSize == 0 # Calculate the location of the root dir (loc as absolute byte for seek). @rootCluster = @bs['root_clus'] @rootBase = clusToByte(@rootCluster) return false if @rootBase == 0 true end |
#getNextAvailableCluster(clus) ⇒ Object
Start from defined FAT entry and look for next available entry.
269 270 271 272 273 274 275 276 |
# File 'lib/fs/fat32/boot_sect.rb', line 269 def getNextAvailableCluster(clus) loop do break if getFatEntry(clus) == 0 clus += 1 end # while getFatEntry(clus) != 0 do clus += 1 end clus end |
#getNextCluster(clus) ⇒ Object
Gets data for the next cluster given current, or nil if end.
183 184 185 186 187 188 |
# File 'lib/fs/fat32/boot_sect.rb', line 183 def getNextCluster(clus) nxt = getFatEntry(clus) return nil if nxt > CC_END_OF_CHAIN raise "Damaged cluster in cluster chain" if nxt == CC_DAMAGED [nxt, getCluster(nxt)] end |
#getToLimit(clus, limit) ⇒ Object
Return continuous data from a beginning cluster to limit bytes (or EOF).
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 |
# File 'lib/fs/fat32/boot_sect.rb', line 191 def getToLimit(clus, limit) # Init. out = MemoryBuffer.create(limit) pos = 0 cur_clus = clus # How many clusters fill request. num = limit.divmod(@bytesPerCluster) num_clus = num[0]; num_clus += 1 if num[1] > 0 # Loop until done or EOF. while num_clus > 0 # Find number of contiguous clusters & trim by num_clus. contig = countContigClusters(cur_clus) red_clus = num_clus > contig ? contig : num_clus # Get data. chunk = red_clus * @bytesPerCluster @stream.seek(clusToByte(cur_clus)) out[pos, chunk] = @stream.read(chunk) pos += chunk # Inc current & dec number to read. cur_clus += (red_clus - 1); num_clus -= red_clus # Get next cluster & abort if end of chain. cur_clus = getFatEntry(cur_clus) break if cur_clus > CC_END_OF_CHAIN end # Return next cluster & data. [cur_clus, out] end |
#isMountable? ⇒ Boolean
//////////////////////////////////////////////////////////////////////////// // Class helpers & accessors.
139 140 141 142 |
# File 'lib/fs/fat32/boot_sect.rb', line 139 def isMountable? return false if @mountable.nil? @mountable end |
#mkClusterMap(clus) ⇒ Object
324 325 326 327 328 329 330 331 332 333 334 335 336 |
# File 'lib/fs/fat32/boot_sect.rb', line 324 def mkClusterMap(clus) map = [] if clus > 0 map << clus loop do nxt = getFatEntry(clus) break if nxt > CC_END_OF_CHAIN clus = nxt map << clus end end map end |
#oemName ⇒ Object
144 145 146 |
# File 'lib/fs/fat32/boot_sect.rb', line 144 def oemName @bs['oem_name'] end |
#putCluster(clus, buf) ⇒ Object
Write data to a cluster.
177 178 179 180 |
# File 'lib/fs/fat32/boot_sect.rb', line 177 def putCluster(clus, buf) @stream.seek(clusToByte(clus)) @stream.write(buf, @bytesPerCluster) end |
#putFatEntry(clus, value) ⇒ Object
Write a FAT entry for a cluster.
318 319 320 321 322 |
# File 'lib/fs/fat32/boot_sect.rb', line 318 def putFatEntry(clus, value) raise "DONT TOUCH THIS CLUSTER: #{clus}" if clus < 3 @stream.seek(@fatBase + FAT_ENTRY_SIZE * clus) @stream.write([value].pack('L'), FAT_ENTRY_SIZE) end |
#to_s ⇒ Object
String rep.
131 132 133 134 |
# File 'lib/fs/fat32/boot_sect.rb', line 131 def to_s # NOTE: Non Microsoft tools may not set the file system label (i.e. Win emulators, linux, etc.) @bs['fs_label'] end |
#wipeChain(clus) ⇒ Object
Deallocate all clusters on a chain from a starting cluster number.
279 280 281 282 283 284 285 286 287 288 |
# File 'lib/fs/fat32/boot_sect.rb', line 279 def wipeChain(clus) loop do nxt = getFatEntry(clus) putFatEntry(clus, 0) break if nxt == 0 # A 0 entry means FAT is inconsistent. Chkdsk may report lost clusters. break if nxt == CC_DAMAGED # This should never happen but if it does allow clusters to become lost. break if nxt > CC_END_OF_CHAIN clus = nxt end end |
#writeClusters(start, buf, len = buf.length) ⇒ Object
Start from defined cluster number and write data, following allocated cluster chain.
291 292 293 294 295 296 297 298 299 300 301 302 303 |
# File 'lib/fs/fat32/boot_sect.rb', line 291 def writeClusters(start, buf, len = buf.length) clus = start; num, leftover = len.divmod(@bytesPerCluster); num += 1 if leftover > 0 0.upto(num - 1) do |offset| local = buf[offset * @bytesPerCluster, @bytesPerCluster] if local.length < @bytesPerCluster then local += ("\0" * (@bytesPerCluster - local.length)) end @stream.seek(clusToByte(clus), IO::SEEK_SET) @stream.write(local, @bytesPerCluster) break if offset == num - 1 # ugly hack to prevent allocating more than needed. nxt = getFatEntry(clus) nxt = allocClusters(clus) if nxt > CC_END_OF_CHAIN clus = nxt end end |