1 # Copyright 2012 GRNET S.A. All rights reserved.
3 # Redistribution and use in source and binary forms, with or
4 # without modification, are permitted provided that the following
7 # 1. Redistributions of source code must retain the above
8 # copyright notice, this list of conditions and the following
11 # 2. Redistributions in binary form must reproduce the above
12 # copyright notice, this list of conditions and the following
13 # disclaimer in the documentation and/or other materials
14 # provided with the distribution.
16 # THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 # USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 # POSSIBILITY OF SUCH DAMAGE.
29 # The views and conclusions contained in the software and
30 # documentation are those of the authors and should not be
31 # interpreted as representing official policies, either expressed
32 # or implied, of GRNET S.A.
43 class Partition(object):
46 def __init__(self, raw_part):
54 ) = struct.unpack(self.format, raw_part)
57 return struct.pack(self.format,
67 start = self.unpack_chs(self.start)
68 end = self.unpack_chs(self.end)
69 print "%d %s %d %s %d %d" % (self.status, start, self.type, end,
70 self.first_sector, self.sector_count)
72 def unpack_chs(self, chs):
76 head = struct.unpack('<B', chs[0])[0]
77 sector = struct.unpack('<B', chs[1])[0] & 0x3f
78 cylinder = (struct.unpack('<B', chs[1])[0] & 0xC0) << 2 | \
79 struct.unpack('<B', chs[2])[0]
81 return (cylinder, head, sector)
83 def pack_chs(self, cylinder, head, sector):
85 assert 1 <= sector <= 63
86 assert 0 <= cylinder <= 1023
87 assert 0 <= head <= 255
90 byte1 = (cylinder >> 2) & 0xC0 | sector
91 byte2 = cylinder & 0xff
93 return struct.pack('<BBB', byte0, byte1, byte2)
95 format = "<444s2x16s16s16s16s2s"
97 Offset Length Contents
98 0 440(max. 446) code area
99 440 2(optional) disk signature
107 def __init__(self, block):
114 self.signature = struct.unpack(self.format, block)
118 self.part[i] = self.Partition(raw_part[i])
121 return struct.pack(self.format,
132 print "Part %d: " % i,
136 class GPTPartitionTable(object):
137 class GPTHeader(object):
138 format = "<8s4sII4xQQQQ16sQIII"
140 Offset Length Contents
143 12 4 bytes Header size in little endian
144 16 4 bytes CRC32 of header
145 20 4 bytes Reserved; must be zero
146 24 8 bytes Current LBA
147 32 8 bytes Backup LBA
148 40 8 bytes First usable LBA for partitions
149 48 8 bytes Last usable LBA
150 56 16 bytes Disk GUID
151 72 8 bytes Partition entries starting LBA
152 80 4 bytes Number of partition entries
153 84 4 bytes Size of a partition entry
154 88 4 bytes CRC32 of partition array
155 92 * Reserved; must be zeroes
159 def __init__(self, block):
166 self.first_usable_lba, \
167 self.last_usable_lba, \
169 self.part_entry_start, \
171 self.part_entry_size, \
172 self.part_crc32 = struct.unpack(self.format, block)
175 return struct.pack(self.format,
182 self.first_usable_lba, \
183 self.last_usable_lba, \
185 self.part_entry_start, \
187 self.part_entry_size, \
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
206 def __init__(self, disk):
208 with open(disk, "rb") as d:
209 #MBR (Logical block address 0)
210 lba0 = d.read(BLOCKSIZE)
212 # Primary GPT Header (LBA 1)
213 lba1 = d.read(BLOCKSIZE)
214 self.primary = self.GPTHeader(lba1[:92])
215 # Partition entries (LBA 2...34)
216 d.seek(self.primary.part_entry_start * BLOCKSIZE)
217 entries_size = self.primary.part_count * \
218 self.primary.part_entry_size
219 self.part_entries = d.read(entries_size)
220 # Secondary GPT Header (LBA -1)
221 d.seek(self.primary.backup_lba * BLOCKSIZE)
222 lba_1 = d.read(BLOCKSIZE)
223 self.secondary = self.GPTHeader(lba_1[:92])
226 return (self.primary.backup_lba + 1) * BLOCKSIZE
228 def shrink(self, size):
230 if size == self.size():
233 assert size < self.size()
235 # new_size = size + Partition Entries + Secondary GPT Header
236 new_size = size + len(self.part_entries) + BLOCKSIZE
237 new_size = ((new_size + 4095) // 4096) * 4096 # align to 4K
238 lba_count = new_size // BLOCKSIZE
241 #TODO: Check for hybrid partition tables
242 self.mbr.part[0].sector_count = (new_size // BLOCKSIZE) - 1
244 # Correct Primary header
245 self.primary.header_crc32 = 0
246 self.primary.backup_lba = lba_count - 1 # LBA-1
247 self.primary.last_usable_lba = lba_count - 34 # LBA-34
248 self.primary.header_crc32 = \
249 binascii.crc32(self.primary.pack()) & 0xffffffff
251 # Correct Secondary header entries
252 self.secondary.header_crc32 = 0
253 self.secondary.current_lba = self.primary.backup_lba
254 self.secondary.last_usable_lba = lba_count - 34 # LBA-34
255 self.secondary.part_entry_start = lba_count - 33 # LBA-33
256 self.secondary.header_crc32 = \
257 binascii.crc32(self.secondary.pack()) & 0xffffffff
259 # Copy the new partition table back to the device
260 with open(self.disk, "wb") as d:
261 d.write(self.mbr.pack())
262 d.write(struct.pack("%ss" % BLOCKSIZE, '\x00' * BLOCKSIZE))
264 d.write(self.primary.pack())
265 d.seek(self.secondary.part_entry_start * BLOCKSIZE)
266 d.write(self.part_entries)
267 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 d.write(self.secondary.pack())
274 if __name__ == '__main__':
275 ptable = GPTPartitionTable(sys.argv[1])
280 print "Primary partition table:"
281 ptable.primary.show()
283 print "Secondary partition table:"
284 ptable.secondary.show()
286 # vim: set sta sts=4 shiftwidth=4 sw=4 et ai :