diff options
Diffstat (limited to 'scripts/lib/mic/utils/partitionedfs.py')
-rw-r--r-- | scripts/lib/mic/utils/partitionedfs.py | 360 |
1 files changed, 0 insertions, 360 deletions
diff --git a/scripts/lib/mic/utils/partitionedfs.py b/scripts/lib/mic/utils/partitionedfs.py deleted file mode 100644 index 68e4cabbe0..0000000000 --- a/scripts/lib/mic/utils/partitionedfs.py +++ /dev/null | |||
@@ -1,360 +0,0 @@ | |||
1 | #!/usr/bin/python -tt | ||
2 | # | ||
3 | # Copyright (c) 2009, 2010, 2011 Intel, Inc. | ||
4 | # Copyright (c) 2007, 2008 Red Hat, Inc. | ||
5 | # Copyright (c) 2008 Daniel P. Berrange | ||
6 | # Copyright (c) 2008 David P. Huff | ||
7 | # | ||
8 | # This program is free software; you can redistribute it and/or modify it | ||
9 | # under the terms of the GNU General Public License as published by the Free | ||
10 | # Software Foundation; version 2 of the License | ||
11 | # | ||
12 | # This program is distributed in the hope that it will be useful, but | ||
13 | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
14 | # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
15 | # for more details. | ||
16 | # | ||
17 | # You should have received a copy of the GNU General Public License along | ||
18 | # with this program; if not, write to the Free Software Foundation, Inc., 59 | ||
19 | # Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
20 | |||
21 | import os | ||
22 | |||
23 | from mic import msger | ||
24 | from mic.utils import runner | ||
25 | from mic.utils.errors import ImageError | ||
26 | from mic.utils.fs_related import * | ||
27 | from mic.utils.oe.misc import * | ||
28 | |||
29 | # Overhead of the MBR partitioning scheme (just one sector) | ||
30 | MBR_OVERHEAD = 1 | ||
31 | |||
32 | # Size of a sector in bytes | ||
33 | SECTOR_SIZE = 512 | ||
34 | |||
35 | class Image: | ||
36 | """ | ||
37 | Generic base object for an image. | ||
38 | |||
39 | An Image is a container for a set of DiskImages and associated | ||
40 | partitions. | ||
41 | """ | ||
42 | def __init__(self): | ||
43 | self.disks = {} | ||
44 | self.partitions = [] | ||
45 | self.parted = find_binary_path("parted") | ||
46 | # Size of a sector used in calculations | ||
47 | self.sector_size = SECTOR_SIZE | ||
48 | self._partitions_layed_out = False | ||
49 | |||
50 | def __add_disk(self, disk_name): | ||
51 | """ Add a disk 'disk_name' to the internal list of disks. Note, | ||
52 | 'disk_name' is the name of the disk in the target system | ||
53 | (e.g., sdb). """ | ||
54 | |||
55 | if disk_name in self.disks: | ||
56 | # We already have this disk | ||
57 | return | ||
58 | |||
59 | assert not self._partitions_layed_out | ||
60 | |||
61 | self.disks[disk_name] = \ | ||
62 | { 'disk': None, # Disk object | ||
63 | 'numpart': 0, # Number of allocate partitions | ||
64 | 'partitions': [], # Indexes to self.partitions | ||
65 | 'offset': 0, # Offset of next partition (in sectors) | ||
66 | # Minimum required disk size to fit all partitions (in bytes) | ||
67 | 'min_size': 0, | ||
68 | 'ptable_format': "msdos" } # Partition table format | ||
69 | |||
70 | def add_disk(self, disk_name, disk_obj): | ||
71 | """ Add a disk object which have to be partitioned. More than one disk | ||
72 | can be added. In case of multiple disks, disk partitions have to be | ||
73 | added for each disk separately with 'add_partition()". """ | ||
74 | |||
75 | self.__add_disk(disk_name) | ||
76 | self.disks[disk_name]['disk'] = disk_obj | ||
77 | |||
78 | def __add_partition(self, part): | ||
79 | """ This is a helper function for 'add_partition()' which adds a | ||
80 | partition to the internal list of partitions. """ | ||
81 | |||
82 | assert not self._partitions_layed_out | ||
83 | |||
84 | self.partitions.append(part) | ||
85 | self.__add_disk(part['disk_name']) | ||
86 | |||
87 | def add_partition(self, size, disk_name, mountpoint, source_file = None, fstype = None, | ||
88 | label=None, fsopts = None, boot = False, align = None, | ||
89 | part_type = None): | ||
90 | """ Add the next partition. Prtitions have to be added in the | ||
91 | first-to-last order. """ | ||
92 | |||
93 | ks_pnum = len(self.partitions) | ||
94 | |||
95 | # Converting MB to sectors for parted | ||
96 | size = size * 1024 * 1024 / self.sector_size | ||
97 | |||
98 | # We still need partition for "/" or non-subvolume | ||
99 | if mountpoint == "/" or not fsopts: | ||
100 | part = { 'ks_pnum' : ks_pnum, # Partition number in the KS file | ||
101 | 'size': size, # In sectors | ||
102 | 'mountpoint': mountpoint, # Mount relative to chroot | ||
103 | 'source_file': source_file, # partition contents | ||
104 | 'fstype': fstype, # Filesystem type | ||
105 | 'fsopts': fsopts, # Filesystem mount options | ||
106 | 'label': label, # Partition label | ||
107 | 'disk_name': disk_name, # physical disk name holding partition | ||
108 | 'device': None, # kpartx device node for partition | ||
109 | 'num': None, # Partition number | ||
110 | 'boot': boot, # Bootable flag | ||
111 | 'align': align, # Partition alignment | ||
112 | 'part_type' : part_type } # Partition type | ||
113 | |||
114 | self.__add_partition(part) | ||
115 | |||
116 | def layout_partitions(self, ptable_format = "msdos"): | ||
117 | """ Layout the partitions, meaning calculate the position of every | ||
118 | partition on the disk. The 'ptable_format' parameter defines the | ||
119 | partition table format and may be "msdos". """ | ||
120 | |||
121 | msger.debug("Assigning %s partitions to disks" % ptable_format) | ||
122 | |||
123 | if ptable_format not in ('msdos'): | ||
124 | raise ImageError("Unknown partition table format '%s', supported " \ | ||
125 | "formats are: 'msdos'" % ptable_format) | ||
126 | |||
127 | if self._partitions_layed_out: | ||
128 | return | ||
129 | |||
130 | self._partitions_layed_out = True | ||
131 | |||
132 | # Go through partitions in the order they are added in .ks file | ||
133 | for n in range(len(self.partitions)): | ||
134 | p = self.partitions[n] | ||
135 | |||
136 | if not self.disks.has_key(p['disk_name']): | ||
137 | raise ImageError("No disk %s for partition %s" \ | ||
138 | % (p['disk_name'], p['mountpoint'])) | ||
139 | |||
140 | if p['part_type']: | ||
141 | # The --part-type can also be implemented for MBR partitions, | ||
142 | # in which case it would map to the 1-byte "partition type" | ||
143 | # filed at offset 3 of the partition entry. | ||
144 | raise ImageError("setting custom partition type is not " \ | ||
145 | "implemented for msdos partitions") | ||
146 | |||
147 | # Get the disk where the partition is located | ||
148 | d = self.disks[p['disk_name']] | ||
149 | d['numpart'] += 1 | ||
150 | d['ptable_format'] = ptable_format | ||
151 | |||
152 | if d['numpart'] == 1: | ||
153 | if ptable_format == "msdos": | ||
154 | overhead = MBR_OVERHEAD | ||
155 | |||
156 | # Skip one sector required for the partitioning scheme overhead | ||
157 | d['offset'] += overhead | ||
158 | # Steal few sectors from the first partition to offset for the | ||
159 | # partitioning overhead | ||
160 | p['size'] -= overhead | ||
161 | |||
162 | if p['align']: | ||
163 | # If not first partition and we do have alignment set we need | ||
164 | # to align the partition. | ||
165 | # FIXME: This leaves a empty spaces to the disk. To fill the | ||
166 | # gaps we could enlargea the previous partition? | ||
167 | |||
168 | # Calc how much the alignment is off. | ||
169 | align_sectors = d['offset'] % (p['align'] * 1024 / self.sector_size) | ||
170 | # We need to move forward to the next alignment point | ||
171 | align_sectors = (p['align'] * 1024 / self.sector_size) - align_sectors | ||
172 | |||
173 | msger.debug("Realignment for %s%s with %s sectors, original" | ||
174 | " offset %s, target alignment is %sK." % | ||
175 | (p['disk_name'], d['numpart'], align_sectors, | ||
176 | d['offset'], p['align'])) | ||
177 | |||
178 | # increase the offset so we actually start the partition on right alignment | ||
179 | d['offset'] += align_sectors | ||
180 | |||
181 | p['start'] = d['offset'] | ||
182 | d['offset'] += p['size'] | ||
183 | |||
184 | p['type'] = 'primary' | ||
185 | p['num'] = d['numpart'] | ||
186 | |||
187 | if d['ptable_format'] == "msdos": | ||
188 | if d['numpart'] > 2: | ||
189 | # Every logical partition requires an additional sector for | ||
190 | # the EBR, so steal the last sector from the end of each | ||
191 | # partition starting from the 3rd one for the EBR. This | ||
192 | # will make sure the logical partitions are aligned | ||
193 | # correctly. | ||
194 | p['size'] -= 1 | ||
195 | |||
196 | if d['numpart'] > 3: | ||
197 | p['type'] = 'logical' | ||
198 | p['num'] = d['numpart'] + 1 | ||
199 | |||
200 | d['partitions'].append(n) | ||
201 | msger.debug("Assigned %s to %s%d, sectors range %d-%d size %d " | ||
202 | "sectors (%d bytes)." \ | ||
203 | % (p['mountpoint'], p['disk_name'], p['num'], | ||
204 | p['start'], p['start'] + p['size'] - 1, | ||
205 | p['size'], p['size'] * self.sector_size)) | ||
206 | |||
207 | # Once all the partitions have been layed out, we can calculate the | ||
208 | # minumim disk sizes. | ||
209 | for disk_name, d in self.disks.items(): | ||
210 | d['min_size'] = d['offset'] | ||
211 | |||
212 | d['min_size'] *= self.sector_size | ||
213 | |||
214 | def __run_parted(self, args): | ||
215 | """ Run parted with arguments specified in the 'args' list. """ | ||
216 | |||
217 | args.insert(0, self.parted) | ||
218 | msger.debug(args) | ||
219 | |||
220 | rc, out = runner.runtool(args, catch = 3) | ||
221 | out = out.strip() | ||
222 | if out: | ||
223 | msger.debug('"parted" output: %s' % out) | ||
224 | |||
225 | if rc != 0: | ||
226 | # We don't throw exception when return code is not 0, because | ||
227 | # parted always fails to reload part table with loop devices. This | ||
228 | # prevents us from distinguishing real errors based on return | ||
229 | # code. | ||
230 | msger.error("WARNING: parted returned '%s' instead of 0 (use --debug for details)" % rc) | ||
231 | |||
232 | def __create_partition(self, device, parttype, fstype, start, size): | ||
233 | """ Create a partition on an image described by the 'device' object. """ | ||
234 | |||
235 | # Start is included to the size so we need to substract one from the end. | ||
236 | end = start + size - 1 | ||
237 | msger.debug("Added '%s' partition, sectors %d-%d, size %d sectors" % | ||
238 | (parttype, start, end, size)) | ||
239 | |||
240 | args = ["-s", device, "unit", "s", "mkpart", parttype] | ||
241 | if fstype: | ||
242 | args.extend([fstype]) | ||
243 | args.extend(["%d" % start, "%d" % end]) | ||
244 | |||
245 | return self.__run_parted(args) | ||
246 | |||
247 | def __format_disks(self): | ||
248 | self.layout_partitions() | ||
249 | |||
250 | for dev in self.disks.keys(): | ||
251 | d = self.disks[dev] | ||
252 | msger.debug("Initializing partition table for %s" % \ | ||
253 | (d['disk'].device)) | ||
254 | self.__run_parted(["-s", d['disk'].device, "mklabel", | ||
255 | d['ptable_format']]) | ||
256 | |||
257 | msger.debug("Creating partitions") | ||
258 | |||
259 | for p in self.partitions: | ||
260 | d = self.disks[p['disk_name']] | ||
261 | if d['ptable_format'] == "msdos" and p['num'] == 5: | ||
262 | # The last sector of the 3rd partition was reserved for the EBR | ||
263 | # of the first _logical_ partition. This is why the extended | ||
264 | # partition should start one sector before the first logical | ||
265 | # partition. | ||
266 | self.__create_partition(d['disk'].device, "extended", | ||
267 | None, p['start'] - 1, | ||
268 | d['offset'] - p['start']) | ||
269 | |||
270 | if p['fstype'] == "swap": | ||
271 | parted_fs_type = "linux-swap" | ||
272 | elif p['fstype'] == "vfat": | ||
273 | parted_fs_type = "fat32" | ||
274 | elif p['fstype'] == "msdos": | ||
275 | parted_fs_type = "fat16" | ||
276 | else: | ||
277 | # Type for ext2/ext3/ext4/btrfs | ||
278 | parted_fs_type = "ext2" | ||
279 | |||
280 | # Boot ROM of OMAP boards require vfat boot partition to have an | ||
281 | # even number of sectors. | ||
282 | if p['mountpoint'] == "/boot" and p['fstype'] in ["vfat", "msdos"] \ | ||
283 | and p['size'] % 2: | ||
284 | msger.debug("Substracting one sector from '%s' partition to " \ | ||
285 | "get even number of sectors for the partition" % \ | ||
286 | p['mountpoint']) | ||
287 | p['size'] -= 1 | ||
288 | |||
289 | self.__create_partition(d['disk'].device, p['type'], | ||
290 | parted_fs_type, p['start'], p['size']) | ||
291 | |||
292 | if p['boot']: | ||
293 | flag_name = "boot" | ||
294 | msger.debug("Set '%s' flag for partition '%s' on disk '%s'" % \ | ||
295 | (flag_name, p['num'], d['disk'].device)) | ||
296 | self.__run_parted(["-s", d['disk'].device, "set", | ||
297 | "%d" % p['num'], flag_name, "on"]) | ||
298 | |||
299 | # Parted defaults to enabling the lba flag for fat16 partitions, | ||
300 | # which causes compatibility issues with some firmware (and really | ||
301 | # isn't necessary). | ||
302 | if parted_fs_type == "fat16": | ||
303 | if d['ptable_format'] == 'msdos': | ||
304 | msger.debug("Disable 'lba' flag for partition '%s' on disk '%s'" % \ | ||
305 | (p['num'], d['disk'].device)) | ||
306 | self.__run_parted(["-s", d['disk'].device, "set", | ||
307 | "%d" % p['num'], "lba", "off"]) | ||
308 | |||
309 | def cleanup(self): | ||
310 | if self.disks: | ||
311 | for dev in self.disks.keys(): | ||
312 | d = self.disks[dev] | ||
313 | try: | ||
314 | d['disk'].cleanup() | ||
315 | except: | ||
316 | pass | ||
317 | |||
318 | def __write_partition(self, num, source_file, start, size): | ||
319 | """ | ||
320 | Install source_file contents into a partition. | ||
321 | """ | ||
322 | if not source_file: # nothing to write | ||
323 | return | ||
324 | |||
325 | # Start is included in the size so need to substract one from the end. | ||
326 | end = start + size - 1 | ||
327 | msger.debug("Installed %s in partition %d, sectors %d-%d, size %d sectors" % (source_file, num, start, end, size)) | ||
328 | |||
329 | dd_cmd = "dd if=%s of=%s bs=%d seek=%d count=%d conv=notrunc" % \ | ||
330 | (source_file, self.image_file, self.sector_size, start, size) | ||
331 | exec_cmd(dd_cmd) | ||
332 | |||
333 | |||
334 | def assemble(self, image_file): | ||
335 | msger.debug("Installing partitions") | ||
336 | |||
337 | self.image_file = image_file | ||
338 | |||
339 | for p in self.partitions: | ||
340 | d = self.disks[p['disk_name']] | ||
341 | if d['ptable_format'] == "msdos" and p['num'] == 5: | ||
342 | # The last sector of the 3rd partition was reserved for the EBR | ||
343 | # of the first _logical_ partition. This is why the extended | ||
344 | # partition should start one sector before the first logical | ||
345 | # partition. | ||
346 | self.__write_partition(p['num'], p['source_file'], | ||
347 | p['start'] - 1, | ||
348 | d['offset'] - p['start']) | ||
349 | |||
350 | self.__write_partition(p['num'], p['source_file'], | ||
351 | p['start'], p['size']) | ||
352 | |||
353 | def create(self): | ||
354 | for dev in self.disks.keys(): | ||
355 | d = self.disks[dev] | ||
356 | d['disk'].create() | ||
357 | |||
358 | self.__format_disks() | ||
359 | |||
360 | return | ||