2 '''This script unpack a whole image into a directory
8 from subprocess import check_call
10 from imgdiff.info import get_partition_info, FSTab
14 '''Same as mkdir -p'''
17 except OSError as err:
18 if err.errno != errno.EEXIST:
22 class ResourceList(object):
24 Record all resource allocated into a file
26 def __init__(self, filename):
27 self.filename = filename
29 def umount(self, path):
30 '''record a mount point'''
31 line = 'mountpoint:%s%s' % (os.path.abspath(path), os.linesep)
32 with open(self.filename, 'a') as writer:
40 def __init__(self, limited_to_dir, resourcelist):
41 self.limited_to_dir = limited_to_dir
42 self.resourcelist = resourcelist
44 def _check_path(self, path):
45 '''Check whether path is ok to mount'''
46 if not path.startswith(self.limited_to_dir):
47 raise ValueError("Try to mount outside of jar: " + path)
48 if os.path.ismount(path):
49 raise Exception("Not allowed to override an exists "
50 "mountpoint: " + path)
52 self.resourcelist.umount(path)
55 def mount(self, image, offset, fstype, path):
56 '''Mount a partition starting from perticular
57 position of a image to a direcotry
59 self._check_path(path)
60 cmd = ['sudo', 'mount',
61 '-o', 'ro,offset=%d' % offset,
64 print 'Mounting', '%d@%s' % (offset, image), '->', path, '...'
67 def move(self, source, target):
68 '''Remove mount point to another path'''
69 self._check_path(target)
70 cmd = ['sudo', 'mount', '--make-runbindable', '/']
71 print 'Make runbindable ...', ' '.join(cmd)
73 cmd = ['sudo', 'mount', '-M', source, target]
74 print 'Moving mount point from', source, 'to', target, '...'
79 '''A raw type image'''
80 def __init__(self, image):
82 self.partab = get_partition_info(self.image)
85 def _is_fs_supported(fstype):
86 '''Only support ext? and *fat*.
87 Ignore others such as swap, tmpfs etc.
89 return fstype.startswith('ext') or 'fat' in fstype
91 def _mount_to_temp(self, basedir, mount):
92 '''Mount all partitions into temp dirs like partx/p?
94 num2temp, uuid2temp = {}, {}
95 for part in self.partab:
96 number = str(part['number'])
97 fstype = part['blkid']['type']
98 if not self._is_fs_supported(fstype):
99 print >> sys.stderr, \
100 "ignore partition %s of type %s" % (number, fstype)
103 path = os.path.join(basedir, 'partx', 'p'+number)
104 mount.mount(self.image, part['start'], fstype, path)
106 num2temp[number] = path
107 uuid2temp[part['blkid']['uuid']] = path
108 return num2temp, uuid2temp
111 def _move_to_root(fstab, num2temp, uuid2temp, basedir, mount):
112 '''Move partitions to their correct mount points according to fstab
115 for mountpoint in sorted(fstab.keys()):
116 item = fstab[mountpoint]
117 if 'number' in item and item['number'] in num2temp:
118 source = num2temp[item['number']]
119 elif 'uuid' in item and item['uuid'] in uuid2temp:
120 source = uuid2temp[item['uuid']]
122 print >> sys.stderr, "fstab mismatch with partition table:", \
126 # remove heading / otherwise the path will reduce to root
127 target = os.path.join(basedir, 'root',
128 mountpoint.lstrip(os.path.sep))
129 pairs.append((source, target))
131 for source, target in pairs:
132 mount.move(source, target)
135 def unpack(self, basedir, resourcelist):
136 '''Unpack self into the basedir and record all resource used
139 mount = Mount(basedir, resourcelist)
141 num2temp, uuid2temp = self._mount_to_temp(basedir, mount)
143 fstab = FSTab.guess(num2temp.values())
145 print >> sys.stderr, "Can't find fstab file from image"
147 return self._move_to_root(fstab,
154 parser = argparse.ArgumentParser()
155 parser.add_argument('image', type=os.path.abspath,
156 help='image file to unpack. Only raw format is '
158 parser.add_argument('basedir', type=os.path.abspath,
159 help='directory to unpack the image')
160 parser.add_argument('resourcelist_filename', type=os.path.abspath,
161 help='will record each mount point when unpacking '
162 'the image. Make sure call cleanup script with this '
163 'file name to release all allocated resources.')
164 return parser.parse_args()
170 img = Image(args.image)
171 resfile = ResourceList(args.resourcelist_filename)
172 return 0 if img.unpack(args.basedir, resfile) else 1
175 if __name__ == '__main__':