Reading block group descriptors

The block group descriptors are much simpler and smaller than the superblock. Recall that these group descriptors are 32 bytes long, unless an ext4 filesystem is using 64-bit mode, in which case they are 64 bytes long. The following code defines a GroupDescriptor class. As with the Superblock class, comments that would normally be at the end of a line have been placed above the line to make things more legible in this book.

class GroupDescriptor():

def init(self, data, wide=False): #/ Blocks bitmap block / self.blockBitmapLo=getU32(data)

/ Inodes bitmap block / self.inodeBitmapLo=getU32(data, 4)

/ Inodes table block / self.inodeTableLo=getU32(data, 8)

/ Free blocks count / self.freeBlocksCountLo=getU16(data, 0xc)

/ Free inodes count / self.freeInodesCountLo=getU16(data, 0xe)

/ Directories count / self.usedDirsCountLo=getU16(data, 0x10)

/ EXT4_BG_flags (INODE_UNINIT, etc) / self.flags=getU16(data, 0x12) self.flagsList = self.printFlagList()

/ Exclude bitmap for snapshots / self.excludeBitmapLo=getU32(data, 0x14)

/ crc32c(s_uuid+grp_num+bbitmap) LE / self.blockBitmapCsumLo=getU16(data, 0x18)

/ crc32c(s_uuid+grp_num+ibitmap) LE / self.inodeBitmapCsumLo=getU16(data, 0x1a)

/ Unused inodes count / self.itableUnusedLo=getU16(data, 0x1c)

/ crc16(sb_uuid+group+desc) / self.checksum=getU16(data, 0x1e) if wide==True:

/ Blocks bitmap block MSB / self.blockBitmapHi=getU32(data, 0x20)

/ Inodes bitmap block MSB / self.inodeBitmapHi=getU32(data, 0x24)

/ Inodes table block MSB / self.inodeTableHi=getU32(data, 0x28)

/ Free blocks count MSB / self.freeBlocksCountHi=getU16(data, 0x2c)

/ Free inodes count MSB / self.freeInodesCountHi=getU16(data, 0x2e)

/ Directories count MSB / self.usedDirsCountHi=getU16(data, 0x30)

/ Unused inodes count MSB / self.itableUnusedHi=getU16(data, 0x32)

/ Exclude bitmap block MSB / self.excludeBitmapHi=getU32(data, 0x34) #/ crc32c(s_uuid+grp_num+bbitmap) BE / self.blockBitmapCsumHi=getU16(data, 0x38)

/ crc32c(s_uuid+grp_num+ibitmap) BE / self.inodeBitmapCsumHi=getU16(data, 0x3a) self.reserved=getU32(data, 0x3c) def printFlagList(self):

flagList = [] #inode table and bitmap are not initialized (EXT4_BG_INODE_UNINIT).

if self.flags & 0x1:

flagList.append(‘Inode Uninitialized’)

block bitmap is not initialized (EXT4_BG_BLOCK_UNINIT).

if self.flags & 0x2:

flagList.append(‘Block Uninitialized’)

inode table is zeroed (EXT4_BG_INODE_ZEROED).

if self.flags & 0x4:

flagList.append(‘Inode Zeroed’)

return flagList def prettyPrint(self):

for k, v in sorted(self.dict.iteritems()) :

print k+”:”, v

This new class is straightforward. Note that the constructor takes an optional parameter which is used to indicate if 64-bit mode is being used. GroupDescriptor defines a prettyPrint function similar to the one found in Superblock.

On its own the GroupDescriptor class isn’t terribly useful. The main reason for this is that data is required from the superblock to locate, read, and interpret group descriptors. I have created a new class called ExtMetadata (for extended metadata) that combines information from the superblock and the block group descriptors in order to solve this problem. The code for this new class follows.

class ExtMetadata():

def init(self, filename, offset):

read first sector if not os.path.isfile(sys.argv[1]):

print(“File “ + str(filename) + “ cannot be openned for reading”) exit(1) with open(str(filename), ‘rb’) as f:

f.seek(1024 + int(offset) * 512) sbRaw = str(f.read(1024))

self.superblock = Superblock(sbRaw)

read block group descriptors

self.blockGroups = self.superblock.blockGroups()

if self.superblock.descriptorSize != 0: self.wideBlockGroups = True self.blockGroupDescriptorSize = 64 else:

self.wideBlockGroups = False self.blockGroupDescriptorSize = 32

read in group descriptors starting in block 1 with open(str(filename), ‘rb’) as f:

f.seek(int(offset) 512 + self.superblock.blockSize) bgdRaw = str(f.read(self.blockGroups \ self.blockGroupDescriptorSize)) self.bgdList = [] for i in range(0, self.blockGroups):

bgd = GroupDescriptor(bgdRaw[i * self.blockGroupDescriptorSize:], \ self.wideBlockGroups) self.bgdList.append(bgd) def prettyPrint(self):

self.superblock.prettyPrint() i = 0 for bgd in self.bgdList:

print “Block group:” + str(i) bgd.prettyPrint() print “”

i += 1

The constructor for this class now contains the code from main() in our previous script that is used to read the superblock from the disk image file. The reason for this is that we need the superblock information in order to know how much data to read when retrieving the group descriptors from the image file.

The constructor first reads the superblock, then creates a Superblock object, uses the information from the superblock to calculate the size of the group descriptor table, reads the group descriptor table and passes the data to the GroupDescriptor constructor in order to build a list of GroupDescriptor objects (inside the for loop).

The new main() function below has become very simple. It just checks for the existence of the image file, calls the ExtMetadata constructor, and then uses the

ExtMetadata.prettyPrint function to print the results.

def usage():

print(“usage “ + sys.argv[0] + \

\n” + \

“Reads superblock & group descriptors from an image file”) exit(1)

def main():

if len(sys.argv) < 3: usage()

read first sector if not os.path.isfile(sys.argv[1]):

print(“File “ + sys.argv[1] + “ cannot be openned for reading”) exit(1) emd = ExtMetadata(sys.argv[1], sys.argv[2]) emd.prettyPrint() if name == “main”:

main()

Partial output for this new script is shown in Figure 7.12. At this point our ExtMetadata class is very basic. We will expand this class in the next section.

FIGURE 7.12

Partial output from a script that parses the block group descriptor table.

results matching ""

    No results matching ""