Revision c0240ac1
b/image_creator/gpt.py | ||
---|---|---|
40 | 40 |
|
41 | 41 |
|
42 | 42 |
class MBR(object): |
43 |
"""Represents a Master Boot Record.""" |
|
43 | 44 |
class Partition(object): |
44 | 45 |
format = "<B3sB3sLL" |
45 | 46 |
|
46 | 47 |
def __init__(self, raw_part): |
47 |
|
|
48 |
( self.status,
|
|
48 |
( |
|
49 |
self.status,
|
|
49 | 50 |
self.start, |
50 | 51 |
self.type, |
51 | 52 |
self.end, |
... | ... | |
63 | 64 |
self.sector_count |
64 | 65 |
) |
65 | 66 |
|
66 |
def show(self): |
|
67 |
@staticmethod |
|
68 |
def size(): |
|
69 |
"""Returns the size of an MBR partition entry""" |
|
70 |
return struct.calcsize(MBR.Partition.format) |
|
71 |
|
|
72 |
def __str__(self): |
|
67 | 73 |
start = self.unpack_chs(self.start) |
68 | 74 |
end = self.unpack_chs(self.end) |
69 |
print "%d %s %d %s %d %d" % (self.status, start, self.type, end,
|
|
75 |
return "%d %s %d %s %d %d" % (self.status, start, self.type, end,
|
|
70 | 76 |
self.first_sector, self.sector_count) |
71 | 77 |
|
72 | 78 |
def unpack_chs(self, chs): |
79 |
"""Unpacks a CHS address string to a tuple.""" |
|
73 | 80 |
|
74 | 81 |
assert len(chs) == 3 |
75 | 82 |
|
... | ... | |
81 | 88 |
return (cylinder, head, sector) |
82 | 89 |
|
83 | 90 |
def pack_chs(self, cylinder, head, sector): |
91 |
"""Packs a CHS tuple to an address string.""" |
|
84 | 92 |
|
85 | 93 |
assert 1 <= sector <= 63 |
86 | 94 |
assert 0 <= cylinder <= 1023 |
... | ... | |
117 | 125 |
for i in range(4): |
118 | 126 |
self.part[i] = self.Partition(raw_part[i]) |
119 | 127 |
|
128 |
@staticmethod |
|
129 |
def size(): |
|
130 |
"""Returns the size of a Master Boot Record.""" |
|
131 |
return struct.calcsize(MBR.format) |
|
132 |
|
|
120 | 133 |
def pack(self): |
134 |
"""Packs an MBR to a binary string.""" |
|
121 | 135 |
return struct.pack(self.format, |
122 | 136 |
self.code_area, |
123 | 137 |
self.part[0].pack(), |
... | ... | |
127 | 141 |
self.signature |
128 | 142 |
) |
129 | 143 |
|
130 |
def show(self): |
|
144 |
def __str__(self): |
|
145 |
ret = "" |
|
131 | 146 |
for i in range(4): |
132 |
print "Part %d: " % i, |
|
133 |
self.part[i].show() |
|
147 |
ret += "Partition %d: %s\n" % (i, self.part[i]) |
|
148 |
ret += "Signature: %s %s\n" % ( |
|
149 |
hex(ord(self.signature[0])), hex(ord(self.signature[1]))) |
|
150 |
return ret |
|
134 | 151 |
|
135 | 152 |
|
136 | 153 |
class GPTPartitionTable(object): |
154 |
"""Represents a GUID Partition Table.""" |
|
137 | 155 |
class GPTHeader(object): |
156 |
"""Represents a GPT Header of a GUID Partition Table.""" |
|
138 | 157 |
format = "<8s4sII4xQQQQ16sQIII" |
139 | 158 |
""" |
140 | 159 |
Offset Length Contents |
... | ... | |
159 | 178 |
def __init__(self, block): |
160 | 179 |
self.signature, \ |
161 | 180 |
self.revision, \ |
162 |
self.size, \ |
|
181 |
self.hdr_size, \
|
|
163 | 182 |
self.header_crc32, \ |
164 | 183 |
self.current_lba, \ |
165 | 184 |
self.backup_lba, \ |
... | ... | |
172 | 191 |
self.part_crc32 = struct.unpack(self.format, block) |
173 | 192 |
|
174 | 193 |
def pack(self): |
194 |
"""Packs a GPT Header to a binary string.""" |
|
175 | 195 |
return struct.pack(self.format, |
176 | 196 |
self.signature, \ |
177 | 197 |
self.revision, \ |
178 |
self.size, \ |
|
198 |
self.hdr_size, \
|
|
179 | 199 |
self.header_crc32, \ |
180 | 200 |
self.current_lba, \ |
181 | 201 |
self.backup_lba, \ |
... | ... | |
188 | 208 |
self.part_crc32 |
189 | 209 |
) |
190 | 210 |
|
191 |
def show(self): |
|
192 |
print "Signature: %s" % self.signature |
|
193 |
print "Revision: %r" % self.revision |
|
194 |
print "Header Size: %d" % self.size |
|
195 |
print "CRC32: %d" % self.header_crc32 |
|
196 |
print "Current LBA: %d" % self.current_lba |
|
197 |
print "Backup LBA: %d" % self.backup_lba |
|
198 |
print "First Usable LBA: %d" % self.first_usable_lba |
|
199 |
print "Last Usable LBA: %d" % self.last_usable_lba |
|
200 |
print "Disk GUID: %s" % uuid.UUID(bytes=self.uuid) |
|
201 |
print "Partition entries starting LBA: %d" % self.part_entry_start |
|
202 |
print "Number of Partition entries: %d" % self.part_count |
|
203 |
print "Size of a partition entry: %d" % self.part_entry_size |
|
204 |
print "CRC32 of partition array: %s" % self.part_crc32 |
|
211 |
@staticmethod |
|
212 |
def size(): |
|
213 |
"""Returns the size of a GPT Header.""" |
|
214 |
return struct.calcsize(GPTPartitionTable.GPTHeader.format) |
|
215 |
|
|
216 |
def __str__(self): |
|
217 |
return \ |
|
218 |
"Signature: %s\n" % self.signature + \ |
|
219 |
"Revision: %r\n" % self.revision + \ |
|
220 |
"Header Size: %d\n" % self.hdr_size + \ |
|
221 |
"CRC32: %d\n" % self.header_crc32 + \ |
|
222 |
"Current LBA: %d\n" % self.current_lba + \ |
|
223 |
"Backup LBA: %d\n" % self.backup_lba + \ |
|
224 |
"First Usable LBA: %d\n" % self.first_usable_lba + \ |
|
225 |
"Last Usable LBA: %d\n" % self.last_usable_lba + \ |
|
226 |
"Disk GUID: %s\n" % uuid.UUID(bytes=self.uuid) + \ |
|
227 |
"Partition entries starting LBA: %d\n" % self.part_entry_start + \ |
|
228 |
"Number of Partition entries: %d\n" % self.part_count + \ |
|
229 |
"Size of a partition entry: %d\n" % self.part_entry_size + \ |
|
230 |
"CRC32 of partition array: %s\n" % self.part_crc32 |
|
205 | 231 |
|
206 | 232 |
def __init__(self, disk): |
207 | 233 |
self.disk = disk |
... | ... | |
209 | 235 |
#MBR (Logical block address 0) |
210 | 236 |
lba0 = d.read(BLOCKSIZE) |
211 | 237 |
self.mbr = MBR(lba0) |
238 |
|
|
212 | 239 |
# Primary GPT Header (LBA 1) |
213 |
lba1 = d.read(BLOCKSIZE) |
|
214 |
self.primary = self.GPTHeader(lba1[:92]) |
|
240 |
raw_header = d.read(self.GPTHeader.size()) |
|
241 |
self.primary = self.GPTHeader(raw_header) |
|
242 |
|
|
215 | 243 |
# Partition entries (LBA 2...34) |
216 | 244 |
d.seek(self.primary.part_entry_start * BLOCKSIZE) |
217 | 245 |
entries_size = self.primary.part_count * \ |
218 | 246 |
self.primary.part_entry_size |
219 | 247 |
self.part_entries = d.read(entries_size) |
248 |
|
|
220 | 249 |
# Secondary GPT Header (LBA -1) |
221 | 250 |
d.seek(self.primary.backup_lba * BLOCKSIZE) |
222 |
lba_1 = d.read(BLOCKSIZE)
|
|
223 |
self.secondary = self.GPTHeader(lba_1[:92])
|
|
251 |
raw_header = d.read(self.GPTHeader.size())
|
|
252 |
self.secondary = self.GPTHeader(raw_header)
|
|
224 | 253 |
|
225 | 254 |
def size(self): |
226 |
return (self.primary.backup_lba + 1) * BLOCKSIZE |
|
255 |
"""Returns the payload size of GPT partitioned device.""" |
|
256 |
return (self.primary.backup_lba + 1) * BLOCKSIZE |
|
227 | 257 |
|
228 | 258 |
def shrink(self, size): |
229 |
|
|
259 |
"""Move the secondary GPT Header entries to the address specified by |
|
260 |
size parameter. |
|
261 |
""" |
|
230 | 262 |
if size == self.size(): |
231 | 263 |
return size |
232 | 264 |
|
... | ... | |
238 | 270 |
lba_count = new_size // BLOCKSIZE |
239 | 271 |
|
240 | 272 |
# Correct MBR |
241 |
#TODO: Check for hybrid partition tables
|
|
273 |
#TODO: Check if the partition tables is hybrid
|
|
242 | 274 |
self.mbr.part[0].sector_count = (new_size // BLOCKSIZE) - 1 |
243 | 275 |
|
244 |
# Correct Primary header
|
|
276 |
# Fix Primary header
|
|
245 | 277 |
self.primary.header_crc32 = 0 |
246 | 278 |
self.primary.backup_lba = lba_count - 1 # LBA-1 |
247 | 279 |
self.primary.last_usable_lba = lba_count - 34 # LBA-34 |
248 | 280 |
self.primary.header_crc32 = \ |
249 | 281 |
binascii.crc32(self.primary.pack()) & 0xffffffff |
250 | 282 |
|
251 |
# Correct Secondary header entries
|
|
283 |
# Fix Secondary header
|
|
252 | 284 |
self.secondary.header_crc32 = 0 |
253 | 285 |
self.secondary.current_lba = self.primary.backup_lba |
254 | 286 |
self.secondary.last_usable_lba = lba_count - 34 # LBA-34 |
... | ... | |
259 | 291 |
# Copy the new partition table back to the device |
260 | 292 |
with open(self.disk, "wb") as d: |
261 | 293 |
d.write(self.mbr.pack()) |
262 |
d.write(struct.pack("%ss" % BLOCKSIZE, '\x00' * BLOCKSIZE)) |
|
263 |
d.seek(BLOCKSIZE) |
|
264 | 294 |
d.write(self.primary.pack()) |
295 |
d.write('\x00' * (BLOCKSIZE - self.primary.size())) |
|
265 | 296 |
d.seek(self.secondary.part_entry_start * BLOCKSIZE) |
266 | 297 |
d.write(self.part_entries) |
267 | 298 |
d.seek(self.primary.backup_lba * BLOCKSIZE) |
268 |
d.write(struct.pack("%ss" % BLOCKSIZE, '\x00' * BLOCKSIZE)) |
|
269 |
d.seek(self.primary.backup_lba * BLOCKSIZE) |
|
270 | 299 |
d.write(self.secondary.pack()) |
300 |
d.write('\x00' * (BLOCKSIZE - self.secondary.size())) |
|
271 | 301 |
|
272 | 302 |
return new_size |
273 | 303 |
|
274 | 304 |
if __name__ == '__main__': |
275 | 305 |
ptable = GPTPartitionTable(sys.argv[1]) |
276 | 306 |
|
277 |
print "MBR:" |
|
278 |
ptable.mbr.show() |
|
279 |
|
|
280 |
print "Primary partition table:" |
|
281 |
ptable.primary.show() |
|
282 |
|
|
283 |
print "Secondary partition table:" |
|
284 |
ptable.secondary.show() |
|
307 |
print "MBR:\n%s" % ptable.mbr |
|
308 |
print "Primary partition table:\n%s" % ptable.primary |
|
309 |
print "Secondary partition table:\n%s" % ptable.secondary |
|
285 | 310 |
|
286 | 311 |
# vim: set sta sts=4 shiftwidth=4 sw=4 et ai : |
Also available in: Unified diff