#!/usr/bin/env python
# -*- coding: utf-8 -*- 

####################################################################
# licensed under GPL V2
# Daniel Rocher - 2011-03-10
####################################################################

# changelog
#
# * 2011-03-10 - Version 0.1.0
#	- initial release

"""Analyse le superblock à partir d'un fichier

Usage:
	python read_superblock.py file

executez ce programme avec l'option --help pour connaitre toutes les options.
"""

__author__ = "Daniel Rocher <daniel.rocher@adella.org>"
__version__ = "0.1.0"
__date__ = "$Date: 2011-03-10 20:30:00 $"
__copyright__ = "Copyright (c) 2011 Daniel Rocher"
__license__ = "GPL Version 2"

import struct, time, uuid
import sys, getopt, platform, os

# justification
just=35
error_parse_value="ERREUR - Impossible de récupérer la valeur"

def usage():
	"""usage() -> affiche les options du script
	
	affichage des options du script
	"""
	print "\n\tSyntaxe: %s [OPTION]... file\n" % sys.argv[0]
	print "\t\t-v, --version                 : Affiche la version"
	print "\t\t-h, --help                    : Print Help (this message) and exit\n"
	print "\t\tExemple: %s sdb1.img\n\t\t\t %s /dev/sdb1" % (sys.argv[0], sys.argv[0])


# false si le nombre magique est incorrect (0xef53)
def isMagic(magic):
	try:
		if struct.unpack('H',magic)[0]==0xef53:
			return True
		else:
			return False
	except:
		return False


# affichage d'une ligne de données
def printLine(label, msg):
	print  "%s : %s" % (label.ljust(just,' '), msg )


# affiche en hexa à partir d'une donnée binaire
def printHexa(label, hexString):
	try:
		if len(hexString)==2:
			printLine(label, hex(struct.unpack('H',hexString)[0]))
		elif len(hexString)==4:
			printLine(label, hex(struct.unpack('I',hexString)[0]))
		else:
			raise
	except:
		printLine(label, error_parse_value)


# affiche un entier à partir d'une donnée binaire
def printInt(label, hexString):
	try:
		if len(hexString)==2:
			printLine(label, struct.unpack('h',hexString)[0])
		elif len(hexString)==4:
			printLine(label, struct.unpack('i',hexString)[0])
		else:
			raise
	except:
		printLine(label, error_parse_value)


# affiche une date à partir d'une donnée binaire
def printDate(label, hexString):
	try:
		printLine(label, time.ctime((struct.unpack('i',hexString)[0])))
	except:
		printLine(label, error_parse_value)

# affiche un texte à partir d'une donnée binaire
def printTxt(label, hexString):
	try:
		resu=hexString.split('\0',1)[0]
		printLine(label, resu)
	except:
		printLine(label, error_parse_value)

# affiche un uuid à partir d'une donnée binaire
def printUUID(label, hexString):
	try:
		printLine(label, uuid.UUID(bytes=hexString))
	except:
		printLine(label, error_parse_value)


if __name__ == "__main__":
	# recupere arguments
	args=sys.argv[1:]
	try:
		opts, args = getopt.getopt(args, "hv", ["help", "version"])
	except getopt.GetoptError,err:
		print "\n",err
		usage()
		sys.exit(2)

	# traitement des options
	for opt, arg in opts:
		if opt in ("-h", "--help"):
			usage()
			sys.exit()
		elif opt in ("-v","--version"):
			print "\nVersion du script : %s" % __version__
			print "Version de Python : %s\n\n" % platform.python_version()
			sys.exit()

	if len(args)==0:
		print "\nVous devez saisir un nom de fichier"
		usage()
		sys.exit(2)
	else:
		myFile=args[0]


	if not os.path.exists(myFile):
		print "\nErreur :\n\tLe fichier %s n'existe pas !\n" % myFile
		sys.exit(2)
		

	try:
		fichier = open(myFile,"rb")

		# find magic number
		fichier.seek(1080)
		offset=0
		if not isMagic(fichier.read(2)):
			print u"\nAVERTISSEMENT: Le nombre magique n'est pas à la bonne position !!!\nRecherche du nombre magique...\n"
			for i in range(800,2000,1):
				fichier.seek(i)
				if isMagic(fichier.read(2)):
					offset=i-(1080)
					print u"nombre magique trouvé avec un offset de %d octet(s).\nUtilisation de cet offset pour afficher les données du superbloc.\n" % (offset)
					break
			if offset==0:
				print u"\nLe nombre magique n'a pas été trouvé, le système est endommagé (ou bien il ne s'agit pas d'un système ext2/ext3/ext4)\n"
				sys.exit(1)

		fichier.seek((1024)+offset)


		printInt("Inodes count", fichier.read(4))
		printInt("Blocks count", fichier.read(4))
		printInt("Reserved blocks count", fichier.read(4))
		printInt("Free blocks count", fichier.read(4))
		printInt("Free inodes count", fichier.read(4))
		printInt("First Data Block", fichier.read(4))
		printInt("Block size", fichier.read(4))
		printInt("Fragment size", fichier.read(4))
		printInt("Blocks per group", fichier.read(4))
		printInt("Fragments per group", fichier.read(4))
		printInt("Inodes per group", fichier.read(4))
		printDate("Mount time", fichier.read(4))
		printDate("Write time", fichier.read(4))
		printInt("Mount count", fichier.read(2))
		printInt("Maximal mount count", fichier.read(2))
		printHexa("Magic number", fichier.read(2))
		printInt("File system state", fichier.read(2))
		printInt("Errors", fichier.read(2))
		printInt("minor revision level", fichier.read(2))
		printDate("time of last check", fichier.read(4))
		printInt("max. time between checks", fichier.read(4))
		printInt("OS", fichier.read(4))
		printInt("Revision level", fichier.read(4))
		printInt("Default uid for reserved blocks", fichier.read(2))
		printInt("Default gid for reserved blocks", fichier.read(2))
		printInt("First non-reserved inode", fichier.read(4))
		printInt("size of inode structure", fichier.read(2))
		printInt("block group # of this superblock", fichier.read(2))
		printInt("compatible feature set", fichier.read(4))
		printInt("incompatible feature set", fichier.read(4))
		printInt("readonly-compatible feature set", fichier.read(4))
		printUUID("128-bit uuid for volume", fichier.read(16))
		printTxt("volume name", fichier.read(16))
		printTxt("directory where last mounted", fichier.read(64))
		printInt("For compression", fichier.read(4))
	except IOError, err:
		print "\nErreur de lecture de %s:\n\t %s\n" % (myFile, err)
		sys.exit(2)

	fichier.close
