Class: Ext3::Superblock

Inherits:
Object
  • Object
show all
Defined in:
lib/fs/ext3/superblock.rb

Overview

//////////////////////////////////////////////////////////////////////////// // Class.

Constant Summary collapse

DEF_BLOCK_CACHE_SIZE =

Default cache sizes.

50
DEF_INODE_CACHE_SIZE =
50
FSS_CLEAN =

File System State.

0x0001
FSS_ERR =

File system is clean.

0x0002
FSS_ORPHAN_REC =

File system has errors.

0x0004
FSS_END =

NOTE: Recovered NOT by this software but by the ‘NIX kernel. IOW start the VM to repair it.

FSS_CLEAN | FSS_ERR | FSS_ORPHAN_REC
EHM_CONTINUE =

Error Handling Method.

1
EHM_RO_REMOUNT =

No action.

2
EHM_PANIC =

Remount file system as read only.

3
CO_LINUX =

Creator OS.

0
CO_GNU_HURD =

NOTE: FS creation tools allow setting this value.

1
CO_MASIX =

These values are supposedly defined.

2
CO_FREE_BSD =
3
CO_LITES =
4
MV_ORIGINAL =

Major Version.

0
MV_DYNAMIC =

NOTE: If version is not dynamic, then values from

1
CFF_PREALLOC_DIR_BLKS =

Compatible Feature Flags.

0x0001
CFF_AFS_SERVER_INODES =

Preallocate directory blocks to reduce fragmentation.

0x0002
CFF_JOURNAL =

AFS server inodes exist in system.

0x0004
CFF_EXTENDED_ATTRIBS =

File system has journal (Ext3).

0x0008
CFF_BIG_PART =

Inodes have extended attributes.

0x0010
CFF_HASH_INDEX =

File system can resize itself for larger partitions.

0x0020
CFF_FLAGS =

Directories use hash index (another modified b-tree).

(CFF_PREALLOC_DIR_BLKS | CFF_AFS_SERVER_INODES | CFF_JOURNAL | CFF_EXTENDED_ATTRIBS | CFF_BIG_PART | CFF_HASH_INDEX)
ICF_COMPRESSION =

Incompatible Feature flags.

0x0001
ICF_FILE_TYPE =

Not supported on Linux?

0x0002
ICF_RECOVER_FS =

Directory entries contain file type field.

0x0004
ICF_JOURNAL =

File system needs recovery.

0x0008
ICF_META_BG =

File system uses journal device.

0x0010
ICF_EXTENTS =

File system uses extents (ext4)

0x0040
ICF_64BIT =

File system uses 64-bit

0x0080
ICF_MMP =
0x0100
ICF_FLEX_BG =
0x0200
ICF_EA_INODE =

EA in inode

0x0400
ICF_DIRDATA =

data in dirent

0x1000
ICF_FLAGS =
(ICF_COMPRESSION | ICF_FILE_TYPE | ICF_RECOVER_FS | ICF_JOURNAL | ICF_META_BG | ICF_EXTENTS | ICF_64BIT | ICF_MMP | ICF_FLEX_BG | ICF_EA_INODE | ICF_DIRDATA)
ROF_SPARSE =

ReadOnly Feature flags.

0x0001
ROF_LARGE_FILES =

Sparse superblocks & group descriptor tables.

0x0002
ROF_BTREES =

File system contains large files (over 4G).

0x0004
ROF_FLAGS =

Directories use B-Trees (not implemented?).

(ROF_SPARSE | ROF_LARGE_FILES | ROF_BTREES)
@@track_inodes =
false

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(stream) ⇒ Superblock

Returns a new instance of Superblock.



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
# File 'lib/fs/ext3/superblock.rb', line 162

def initialize(stream)
  raise "Ext3::Superblock.initialize: Nil stream" if stream.nil?
  @stream = stream

  # Seek, read & decode the superblock structure
  @stream.seek(SUPERBLOCK_OFFSET)
  @sb = SUPERBLOCK.decode(@stream.read(SUPERBLOCK_SIZE))

  # Grab some quick facts & make sure there's nothing wrong. Tight qualification.
  raise "Ext3::Superblock.initialize: Invalid signature=[#{@sb['signature']}]" if @sb['signature'] != SUPERBLOCK_SIG
  raise "Ext3::Superblock.initialize: Invalid file system state" if @sb['fs_state'] > FSS_END
  if @sb['fs_state'] != FSS_CLEAN
    $log.warn("Ext3 file system has errors")        if $log && gotBit?(@sb['fs_state'], FSS_ERR)
    $log.warn("Ext3 orphan inodes being recovered") if $log && gotBit?(@sb['fs_state'], FSS_ORPHAN_REC)
  end
  raise "Ext3::Superblock.initialize: Invalid error handling method=[#{@sb['err_method']}]" if @sb['err_method'] > EHM_PANIC
  raise "Ext3::Superblock.initialize: Filesystem has extents (ext4)"  if gotBit?(@sb['incompat_flags'], ICF_EXTENTS)

  @blockSize = 1024 << @sb['block_size']

  @block_cache = LruHash.new(DEF_BLOCK_CACHE_SIZE)
  @inode_cache = LruHash.new(DEF_INODE_CACHE_SIZE)

  # expose for testing.
  @numBlocks = @sb['num_blocks']
  @numInodes = @sb['num_inodes']

  # Inode file size members can't be trusted, so use sector count instead.
  # MiqDisk exposes blockSize, which for our purposes is sectorSize.
  @sectorSize = @stream.blockSize

  # Preprocess some members.
  @sb['vol_name'].delete!("\000")
  @sb['last_mnt_path'].delete!("\000")
  @numGroups, @lastGroupBlocks = @sb['num_blocks'].divmod(@sb['blocks_in_group'])
  @numGroups += 1 if @lastGroupBlocks > 0
  @fsId = UUIDTools::UUID.parse_raw(@sb['fs_id'])
  @volName = @sb['vol_name']
end

Instance Attribute Details

#blockSizeObject (readonly)

Returns the value of attribute blockSize.



151
152
153
# File 'lib/fs/ext3/superblock.rb', line 151

def blockSize
  @blockSize
end

#fsIdObject (readonly)

///////////////////////////////////////////////////////////////////////// // initialize



150
151
152
# File 'lib/fs/ext3/superblock.rb', line 150

def fsId
  @fsId
end

#numBlocksObject (readonly)

///////////////////////////////////////////////////////////////////////// // initialize



150
151
152
# File 'lib/fs/ext3/superblock.rb', line 150

def numBlocks
  @numBlocks
end

#numGroupsObject (readonly)

///////////////////////////////////////////////////////////////////////// // initialize



150
151
152
# File 'lib/fs/ext3/superblock.rb', line 150

def numGroups
  @numGroups
end

#numInodesObject (readonly)

///////////////////////////////////////////////////////////////////////// // initialize



150
151
152
# File 'lib/fs/ext3/superblock.rb', line 150

def numInodes
  @numInodes
end

#sectorSizeObject (readonly)

Returns the value of attribute sectorSize.



151
152
153
# File 'lib/fs/ext3/superblock.rb', line 151

def sectorSize
  @sectorSize
end

#streamObject (readonly)

///////////////////////////////////////////////////////////////////////// // initialize



150
151
152
# File 'lib/fs/ext3/superblock.rb', line 150

def stream
  @stream
end

#volNameObject (readonly)

///////////////////////////////////////////////////////////////////////// // initialize



150
151
152
# File 'lib/fs/ext3/superblock.rb', line 150

def volName
  @volName
end

Class Method Details

.isSuperblock?(buf) ⇒ Boolean

Returns:

  • (Boolean)


155
156
157
158
159
160
# File 'lib/fs/ext3/superblock.rb', line 155

def self.isSuperblock?(buf)
  sb = SUPERBLOCK_VALIDATE.decode(buf)
  sb['signature'] == SUPERBLOCK_SIG &&
    sb['fs_state'] <= FSS_END &&
    sb['err_method'] <= EHM_PANIC
end

Instance Method Details

#blockNumToGroupNum(block) ⇒ Object



241
242
243
244
245
246
# File 'lib/fs/ext3/superblock.rb', line 241

def blockNumToGroupNum(block)
  raise "Ext3::Superblock.blockNumToGroupNum: block is nil" if block.nil?
  group = (block - @sb['block_group_zero']) / @sb['blocks_in_group']
  offset = block.modulo(@sb['blocks_in_group'])
  return group, offset
end

#blocksPerGroupObject



221
222
223
# File 'lib/fs/ext3/superblock.rb', line 221

def blocksPerGroup
  @sb['blocks_in_group']
end

#blockToAddress(block) ⇒ Object



256
257
258
259
260
# File 'lib/fs/ext3/superblock.rb', line 256

def blockToAddress(block)
  address  = block * @blockSize
  address += (SUPERBLOCK_SIZE + GDE_SIZE * @numGroups)  if address == SUPERBLOCK_OFFSET
  address
end

#firstGroupBlockNum(group) ⇒ Object



248
249
250
# File 'lib/fs/ext3/superblock.rb', line 248

def firstGroupBlockNum(group)
  group * @sb['blocks_in_group'] + @sb['block_group_zero']
end

#fragmentSizeObject



217
218
219
# File 'lib/fs/ext3/superblock.rb', line 217

def fragmentSize
  1024 << @sb['fragment_size']
end

#fragmentsPerGroupObject



225
226
227
# File 'lib/fs/ext3/superblock.rb', line 225

def fragmentsPerGroup
  @sb['fragments_in_group']
end

#freeBytesObject



237
238
239
# File 'lib/fs/ext3/superblock.rb', line 237

def freeBytes
  @sb['unallocated_blocks'] * @blockSize
end

#gdtObject

//////////////////////////////////////////////////////////////////////////// // Class helpers & accessors.



205
206
207
# File 'lib/fs/ext3/superblock.rb', line 205

def gdt
  @gdt ||= GroupDescriptorTable.new(self)
end

#getBlock(block, _ignore_alloc = false) ⇒ Object

Ignore allocation is for testing only.



289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
# File 'lib/fs/ext3/superblock.rb', line 289

def getBlock(block, _ignore_alloc = false)
  raise "Ext3::Superblock.getBlock: block is nil" if block.nil?

  unless @block_cache.key?(block)
    if block == 0
      @block_cache[block] = MemoryBuffer.create(@blockSize)
    else
      # raise "Block #{block} is not allocated" if (not gde.blockAllocBmp[offset] and not ignore_alloc)

      address = blockToAddress(block)  # This function will read the block into our cache

      @stream.seek(address)
      @block_cache[block] = @stream.read(@blockSize)
    end
  end
  @block_cache[block]
end

#getInode(inode, _ignore_alloc = false) ⇒ Object

Ignore allocation is for testing only.



275
276
277
278
279
280
281
282
283
284
285
286
# File 'lib/fs/ext3/superblock.rb', line 275

def getInode(inode, _ignore_alloc = false)
  unless @inode_cache.key?(inode)
    group, offset = inodeNumToGroupNum(inode)
    gde = gdt[group]
    # raise "Inode #{inode} is not allocated" if (not gde.inodeAllocBmp[offset] and not ignore_alloc)
    @stream.seek(blockToAddress(gde.inodeTable) + offset * inodeSize)
    @inode_cache[inode] = Inode.new(@stream.read(inodeSize))
    $log.info "Inode num: #{inode}\n#{@inode_cache[inode].dump}\n\n" if $log && @@track_inodes
  end

  @inode_cache[inode]
end

#gotBit?(field, bit) ⇒ Boolean

//////////////////////////////////////////////////////////////////////////// // Utility functions.

Returns:

  • (Boolean)


310
311
312
# File 'lib/fs/ext3/superblock.rb', line 310

def gotBit?(field, bit)
  field & bit == bit
end

#inodeNumToGroupNum(inode) ⇒ Object



252
253
254
# File 'lib/fs/ext3/superblock.rb', line 252

def inodeNumToGroupNum(inode)
  (inode - 1).divmod(inodesPerGroup)
end

#inodeSizeObject



233
234
235
# File 'lib/fs/ext3/superblock.rb', line 233

def inodeSize
  isDynamic? ? @sb['inode_size'] : INODE_SIZE
end

#inodesPerGroupObject



229
230
231
# File 'lib/fs/ext3/superblock.rb', line 229

def inodesPerGroup
  @sb['inodes_in_group']
end

#isDynamic?Boolean

Returns:

  • (Boolean)


209
210
211
# File 'lib/fs/ext3/superblock.rb', line 209

def isDynamic?
  @sb['ver_major'] == MV_DYNAMIC
end

#isNewDirEnt?Boolean

Returns:

  • (Boolean)


213
214
215
# File 'lib/fs/ext3/superblock.rb', line 213

def isNewDirEnt?
  gotBit?(@sb['incompat_flags'], ICF_FILE_TYPE)
end

#isValidBlock?(block) ⇒ Boolean

Returns:

  • (Boolean)


268
269
270
271
272
# File 'lib/fs/ext3/superblock.rb', line 268

def isValidBlock?(block)
  group, offset = blockNumToGroupNum(block)
  gde = gdt[group]
  gde.blockAllocBmp[offset]
end

#isValidInode?(inode) ⇒ Boolean

Returns:

  • (Boolean)


262
263
264
265
266
# File 'lib/fs/ext3/superblock.rb', line 262

def isValidInode?(inode)
  group, offset = inodeNumToGroupNum(inode)
  gde = gdt[group]
  gde.inodeAllocBmp[offset]
end