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 | ||
