Class: Ext4::Superblock

Inherits:
Object
  • Object
show all
Defined in:
lib/fs/ext4/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_HUGE_FILE =

Directories use B-Trees (not implemented?).

0x0008
ROF_GDT_CSUM =
0x0010
0x0020
ROF_EXTRA_ISIZE =
0x0040
ROF_FLAGS =
(ROF_SPARSE | ROF_LARGE_FILES | ROF_BTREES | ROF_HUGE_FILE | ROF_GDT_CSUM | ROF_DIR_NLINK | ROF_EXTRA_ISIZE)
@@track_inodes =
false

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(stream) ⇒ Superblock

Returns a new instance of Superblock.



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

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

  # 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 "Ext4::Superblock.initialize: Invalid signature=[#{@sb['signature']}]" if @sb['signature'] != SUPERBLOCK_SIG
  raise "Ext4::Superblock.initialize: Invalid file system state" if @sb['fs_state'] > FSS_END
  if (state = @sb['fs_state']) != FSS_CLEAN
    $log.warn("Ext4 file system has errors")        if $log && gotBit?(state, FSS_ERR)
    $log.warn("Ext4 orphan inodes being recovered") if $log && gotBit?(state, FSS_ORPHAN_REC)
  end
  raise "Ext4::Superblock.initialize: Invalid error handling method=[#{@sb['err_method']}]" if @sb['err_method'] > EHM_PANIC

  @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.



146
147
148
# File 'lib/fs/ext4/superblock.rb', line 146

def blockSize
  @blockSize
end

#fsIdObject (readonly)

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



145
146
147
# File 'lib/fs/ext4/superblock.rb', line 145

def fsId
  @fsId
end

#numBlocksObject (readonly)

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



145
146
147
# File 'lib/fs/ext4/superblock.rb', line 145

def numBlocks
  @numBlocks
end

#numGroupsObject (readonly)

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



145
146
147
# File 'lib/fs/ext4/superblock.rb', line 145

def numGroups
  @numGroups
end

#numInodesObject (readonly)

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



145
146
147
# File 'lib/fs/ext4/superblock.rb', line 145

def numInodes
  @numInodes
end

#sectorSizeObject (readonly)

Returns the value of attribute sectorSize.



146
147
148
# File 'lib/fs/ext4/superblock.rb', line 146

def sectorSize
  @sectorSize
end

#streamObject (readonly)

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



145
146
147
# File 'lib/fs/ext4/superblock.rb', line 145

def stream
  @stream
end

#volNameObject (readonly)

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



145
146
147
# File 'lib/fs/ext4/superblock.rb', line 145

def volName
  @volName
end

Instance Method Details

#blockNumToGroupNum(block) ⇒ Object



235
236
237
238
239
240
241
242
243
# File 'lib/fs/ext4/superblock.rb', line 235

def blockNumToGroupNum(block)
  unless block.kind_of?(Numeric)
    $log.error("Ext4::Superblock.blockNumToGroupNum called from: #{caller.join('\n')}")
    raise "Ext4::Superblock.blockNumToGroupNum: block is expected to be numeric, but is <#{block.class.name}>"
  end
  group = (block - @sb['block_group_zero']) / @sb['blocks_in_group']
  offset = block.modulo(@sb['blocks_in_group'])
  return group, offset
end

#blocksPerGroupObject



207
208
209
# File 'lib/fs/ext4/superblock.rb', line 207

def blocksPerGroup
  @sb['blocks_in_group']
end

#blockToAddress(block) ⇒ Object



253
254
255
256
257
# File 'lib/fs/ext4/superblock.rb', line 253

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

#firstGroupBlockNum(group) ⇒ Object



245
246
247
# File 'lib/fs/ext4/superblock.rb', line 245

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

#fragmentSizeObject



203
204
205
# File 'lib/fs/ext4/superblock.rb', line 203

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

#fragmentsPerGroupObject



211
212
213
# File 'lib/fs/ext4/superblock.rb', line 211

def fragmentsPerGroup
  @sb['fragments_in_group']
end

#freeBytesObject



231
232
233
# File 'lib/fs/ext4/superblock.rb', line 231

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

#gdtObject

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



191
192
193
# File 'lib/fs/ext4/superblock.rb', line 191

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

#getBlock(block, _ignore_alloc = false) ⇒ Object

Ignore allocation is for testing only.



286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
# File 'lib/fs/ext4/superblock.rb', line 286

def getBlock(block, _ignore_alloc = false)
  raise "Ext4::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.



272
273
274
275
276
277
278
279
280
281
282
283
# File 'lib/fs/ext4/superblock.rb', line 272

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), self, inode)
    $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)


307
308
309
# File 'lib/fs/ext4/superblock.rb', line 307

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

#groupDescriptorSizeObject



227
228
229
# File 'lib/fs/ext4/superblock.rb', line 227

def groupDescriptorSize
  @groupDescriptorSize ||= is_enabled_64_bit? ? @sb['group_desc_size'] : GDE_SIZE
end

#inodeNumToGroupNum(inode) ⇒ Object



249
250
251
# File 'lib/fs/ext4/superblock.rb', line 249

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

#inodeSizeObject



219
220
221
# File 'lib/fs/ext4/superblock.rb', line 219

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

#inodesPerGroupObject



215
216
217
# File 'lib/fs/ext4/superblock.rb', line 215

def inodesPerGroup
  @sb['inodes_in_group']
end

#is_enabled_64_bit?Boolean

Returns:

  • (Boolean)


223
224
225
# File 'lib/fs/ext4/superblock.rb', line 223

def is_enabled_64_bit?
  @is_enabled_64_bit ||= gotBit?(@sb['incompat_flags'], ICF_64BIT)
end

#isDynamic?Boolean

Returns:

  • (Boolean)


195
196
197
# File 'lib/fs/ext4/superblock.rb', line 195

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

#isNewDirEnt?Boolean

Returns:

  • (Boolean)


199
200
201
# File 'lib/fs/ext4/superblock.rb', line 199

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

#isValidBlock?(block) ⇒ Boolean

Returns:

  • (Boolean)


265
266
267
268
269
# File 'lib/fs/ext4/superblock.rb', line 265

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

#isValidInode?(inode) ⇒ Boolean

Returns:

  • (Boolean)


259
260
261
262
263
# File 'lib/fs/ext4/superblock.rb', line 259

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