Revision 822ba661

b/snf-image-helper/Makefile.am
7 7

  
8 8
dist_doc_DATA = COPYING AUTHORS ChangeLog
9 9
dist_bin_SCRIPTS = snf-image-helper
10
dist_scripts_SCRIPTS= snf-passtohash.py inject-files.py decode-properties.py
10
dist_scripts_SCRIPTS= snf-passtohash.py inject-files.py decode-properties.py \
11
								fix_gpt.py
11 12
dist_common_DATA = common.sh unattend.xml
12 13

  
13 14
edit = sed \
b/snf-image-helper/fix_gpt.py
1
#!/usr/bin/env python
2

  
3
# Copyright (C) 2012 GRNET S.A.
4
#
5
# This program is free software; you can redistribute it and/or modify
6
# it under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 2 of the License, or
8
# (at your option) any later version.
9
#
10
# This program is distributed in the hope that it will be useful, but
11
# WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
# General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License
16
# along with this program; if not, write to the Free Software
17
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18
# 02110-1301, USA.
19

  
20
import struct
21
import sys
22
import binascii
23
import stat
24
import os
25

  
26
BLOCKSIZE = 512
27

  
28

  
29
class MBR(object):
30
    """Represents a Master Boot Record."""
31
    class Partition(object):
32
        format = "<B3sB3sLL"
33

  
34
        def __init__(self, raw_part):
35
            (
36
                self.status,
37
                self.start,
38
                self.type,
39
                self.end,
40
                self.first_sector,
41
                self.sector_count
42
            ) = struct.unpack(self.format, raw_part)
43

  
44
        def pack(self):
45
            return struct.pack(self.format,
46
                self.status,
47
                self.start,
48
                self.type,
49
                self.end,
50
                self.first_sector,
51
                self.sector_count
52
            )
53

  
54
        @staticmethod
55
        def size():
56
            """Returns the size of an MBR partition entry"""
57
            return struct.calcsize(MBR.Partition.format)
58

  
59
        def unpack_chs(self, chs):
60
            """Unpacks a CHS address string to a tuple."""
61

  
62
            assert len(chs) == 3
63

  
64
            head = struct.unpack('<B', chs[0])[0]
65
            sector = struct.unpack('<B', chs[1])[0] & 0x3f
66
            cylinder = (struct.unpack('<B', chs[1])[0] & 0xC0) << 2 | \
67
                struct.unpack('<B', chs[2])[0]
68

  
69
            return (cylinder, head, sector)
70

  
71
        def pack_chs(self, cylinder, head, sector):
72
            """Packs a CHS tuple to an address string."""
73

  
74
            assert 1 <= sector <= 63
75
            assert 0 <= cylinder <= 1023
76
            assert 0 <= head <= 255
77

  
78
            byte0 = head
79
            byte1 = (cylinder >> 2) & 0xC0 | sector
80
            byte2 = cylinder & 0xff
81

  
82
            return struct.pack('<BBB', byte0, byte1, byte2)
83

  
84
    format = "<444s2x16s16s16s16s2s"
85
    """
86
    Offset  Length          Contents
87
    0       440(max. 446)   code area
88
    440     2(optional)     disk signature
89
    444     2               Usually nulls
90
    446     16              Partition 0
91
    462     16              Partition 1
92
    478     16              Partition 2
93
    494     16              Partition 3
94
    510     2               MBR signature
95
    """
96
    def __init__(self, block):
97
        raw_part = {}
98
        self.code_area, \
99
        raw_part[0], \
100
        raw_part[1], \
101
        raw_part[2], \
102
        raw_part[3], \
103
        self.signature = struct.unpack(self.format, block)
104

  
105
        self.part = {}
106
        for i in range(4):
107
            self.part[i] = self.Partition(raw_part[i])
108

  
109
    @staticmethod
110
    def size():
111
        """Returns the size of a Master Boot Record."""
112
        return struct.calcsize(MBR.format)
113

  
114
    def pack(self):
115
        """Packs an MBR to a binary string."""
116
        return struct.pack(self.format,
117
            self.code_area,
118
            self.part[0].pack(),
119
            self.part[1].pack(),
120
            self.part[2].pack(),
121
            self.part[3].pack(),
122
            self.signature
123
        )
124

  
125

  
126
class GPTPartitionTable(object):
127
    """Represents a GUID Partition Table."""
128
    class GPTHeader(object):
129
        """Represents a GPT Header of a GUID Partition Table."""
130
        format = "<8s4sII4xQQQQ16sQIII"
131
        """
132
        Offset	Length 	        Contents
133
        0       8 bytes         Signature
134
        8       4 bytes 	Revision
135
        12      4 bytes 	Header size in little endian
136
        16 	4 bytes 	CRC32 of header
137
        20 	4 bytes 	Reserved; must be zero
138
        24 	8 bytes 	Current LBA
139
        32 	8 bytes 	Backup LBA
140
        40 	8 bytes 	First usable LBA for partitions
141
        48 	8 bytes 	Last usable LBA
142
        56 	16 bytes 	Disk GUID
143
        72 	8 bytes 	Partition entries starting LBA
144
        80 	4 bytes 	Number of partition entries
145
        84 	4 bytes 	Size of a partition entry
146
        88 	4 bytes 	CRC32 of partition array
147
        92 	* 	        Reserved; must be zeroes
148
        LBA    size            Total
149
        """
150

  
151
        def __init__(self, block):
152
            self.signature, \
153
            self.revision, \
154
            self.hdr_size, \
155
            self.header_crc32, \
156
            self.current_lba, \
157
            self.backup_lba, \
158
            self.first_usable_lba, \
159
            self.last_usable_lba, \
160
            self.uuid, \
161
            self.part_entry_start, \
162
            self.part_count, \
163
            self.part_entry_size, \
164
            self.part_crc32 = struct.unpack(self.format, block)
165

  
166
        def pack(self):
167
            """Packs a GPT Header to a binary string."""
168
            return struct.pack(self.format,
169
                self.signature, \
170
                self.revision, \
171
                self.hdr_size, \
172
                self.header_crc32, \
173
                self.current_lba, \
174
                self.backup_lba, \
175
                self.first_usable_lba, \
176
                self.last_usable_lba, \
177
                self.uuid, \
178
                self.part_entry_start, \
179
                self.part_count, \
180
                self.part_entry_size, \
181
                self.part_crc32
182
            )
183

  
184
        @staticmethod
185
        def size():
186
            """Returns the size of a GPT Header."""
187
            return struct.calcsize(GPTPartitionTable.GPTHeader.format)
188

  
189
    def __init__(self, disk):
190
        self.disk = disk
191
        with open(disk, "rb") as d:
192
            # MBR (Logical block address 0)
193
            lba0 = d.read(BLOCKSIZE)
194
            self.mbr = MBR(lba0)
195

  
196
            # Primary GPT Header (LBA 1)
197
            raw_header = d.read(self.GPTHeader.size())
198
            self.primary = self.GPTHeader(raw_header)
199

  
200
            # Partition entries (LBA 2...34)
201
            d.seek(self.primary.part_entry_start * BLOCKSIZE)
202
            entries_size = self.primary.part_count * \
203
                                                self.primary.part_entry_size
204
            self.part_entries = d.read(entries_size)
205

  
206
            # Secondary GPT Header (LBA -1)
207
            d.seek(self.primary.backup_lba * BLOCKSIZE)
208
            raw_header = d.read(self.GPTHeader.size())
209
            self.secondary = self.GPTHeader(raw_header)
210

  
211
    def size(self):
212
        """Returns the payload size of GPT partitioned device."""
213
        return (self.primary.backup_lba + 1) * BLOCKSIZE
214

  
215
    def fix(self, lba_count):
216
        """Move the secondary GPT Header entries to the LBA specified by
217
        lba_count parameter.
218
        """
219

  
220
        assert lba_count * BLOCKSIZE > self.size()
221

  
222
        # Correct MBR
223
        #TODO: Check if the partition tables is hybrid
224
        self.mbr.part[0].sector_count = lba_count - 1
225

  
226
        # Fix Primary header
227
        self.primary.header_crc32 = 0
228
        self.primary.backup_lba = lba_count - 1  # LBA-1
229
        self.primary.last_usable_lba = lba_count - 34  # LBA-34
230
        self.primary.header_crc32 = \
231
                            binascii.crc32(self.primary.pack()) & 0xffffffff
232

  
233
        # Fix Secondary header
234
        self.secondary.header_crc32 = 0
235
        self.secondary.current_lba = self.primary.backup_lba
236
        self.secondary.last_usable_lba = lba_count - 34  # LBA-34
237
        self.secondary.part_entry_start = lba_count - 33  # LBA-33
238
        self.secondary.header_crc32 = \
239
                            binascii.crc32(self.secondary.pack()) & 0xffffffff
240

  
241
        # Copy the new partition table back to the device
242
        with open(self.disk, "wb") as d:
243
            d.write(self.mbr.pack())
244
            d.write(self.primary.pack())
245
            d.write('\x00' * (BLOCKSIZE - self.primary.size()))
246
            d.seek(self.secondary.part_entry_start * BLOCKSIZE)
247
            d.write(self.part_entries)
248
            d.seek(self.primary.backup_lba * BLOCKSIZE)
249
            d.write(self.secondary.pack())
250
            d.write('\x00' * (BLOCKSIZE - self.secondary.size()))
251

  
252

  
253
if __name__ == '__main__':
254
    usage = "Usage: %s <disk> <sectors>\n" % (sys.argv[0])
255

  
256
    if len(sys.argv) != 3:
257
        sys.stderr.write(usage)
258
        sys.exit(1)
259

  
260
    disk = sys.argv[1]
261
    mode = os.stat(disk).st_mode
262
    if not stat.S_ISBLK(mode):
263
        sys.stderr.write("Parameter disk must be a block device\n")
264
        sys.stderr.write(usage)
265
        sys.exit(1)
266

  
267
    try:
268
        size = int(sys.argv[2])
269
    except ValueError:
270
        sys.stderr.write("Parameter new_size must be a number\n")
271
        sys.stderr.write(usage)
272
        sys.exit(1)
273

  
274
    ptable = GPTPartitionTable(disk)
275
    if size * BLOCKSIZE == ptable.size():
276
        sys.stderr.write("Nothing to do...\n")
277
    elif size * BLOCKSIZE > ptable.size():
278
        ptable.fix(size)
279
        sys.stderr.write("GPT table was fixed\n")
280
    else:
281
        sys.stderr.write("Disk is langer than size")
282
        exit(1)
283

  
284
    sys.exit(0)
285

  
286
# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :
b/snf-image-helper/tasks/10FixPartitionTable.in
41 41

  
42 42
table_type=$(get_partition_table_type "$table")
43 43

  
44
if [ "$table_type" != "msdos" ]; then # We are planning to add gpt support
44
if [ "$table_type" == "gpt" ]; then
45
    "@scriptsdir@/fix_gpt.py" "$SNF_IMAGE_DEV" $($BLOCKDEV --getsz "$SNF_IMAGE_DEV")
46
elif [ "$table_type" != "msdos" ]; then
45 47
    log_error "Device: \'${SNF_IMAGE_DEV}' contains unsupported partition "
46
    "table type: \`$table_type'. For now only msdos partitions are supported."
48
    "table type: \`$table_type'. Only msdos & gpt partitions are supported."
47 49
fi
48 50

  
49 51
last_part=$(get_last_partition "$table")
......
87 89
if [ "$table_type" != "msdos" ]; then
88 90
    # Primary, extended and logical partitions is a concept for msdos partition
89 91
    # tables. Parted's mkpart will use part-type as partition name if applied
90
    # on a gpt table.
91
    last_part_type=""
92
    # on a gpt table and leaving this empty is fragile: For a strange reason
93
    # for swap partitions the command fails (???)
94
    last_part_type="primary"
92 95
elif [ $last_part_id -gt 4 ]; then
93 96
    last_part_type="logical"
94 97
    extended=$(get_extended_partition "$table")
......
132 135
if [ $swap_num -gt 0 ]; then
133 136
    swap_part="$swap_num:${swap_start}s:${swap_end}s:0:linux-swap(v1)::;"
134 137
    if [ "$table_type" != "msdos" ]; then
135
        swap_ptype=""
138
        swap_ptype="swap" # in gpt this is used as a partition name
136 139
    elif [ $swap_num -ge 5 ]; then
137 140
        if [ -z "$extended" ]; then
138 141
            extended="0:$((swap_start - 2))s:${swap_end}s:0:::;"

Also available in: Unified diff