summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEd Bartosh <ed.bartosh@linux.intel.com>2017-02-09 15:52:55 +0200
committerRichard Purdie <richard.purdie@linuxfoundation.org>2017-02-15 20:06:44 -0800
commitadf5e2956ca4e3136be0ee785115a7e1941a135b (patch)
treef15fb103786bbf8437fee80c89956bbe5be60e02
parent47ee6c2153db204a9fda718b92616d4bf851057e (diff)
downloadpoky-adf5e2956ca4e3136be0ee785115a7e1941a135b.tar.gz
wic: move disk operations to PartitionImage class
Disk operations were spread over DirectPlugin, DiskImage and Image code making the code hard to understand. Renamed Image class to PartitionedImage. Removed DiskImage class. Moved disk operations to PartitionedImage. There was an implicit support for multiple disks: if different devices were specified in .wks file (e.g. --ondisk sda and --ondisk sdb), wic would theoretically generate multiple images. This is quite confusing option and the code supporting it was broken for a long time. The same effect (multiple output images) can be achieved in obvious and clear way - by using multiple .wks files. This functionality was removed. PartitionedImage works only with one image. This makes the code less complex and easier to maintain. (From OE-Core rev: 4dc9dbfc7fbc16d349a019e8973d50905cd28244) Signed-off-by: Ed Bartosh <ed.bartosh@linux.intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
-rw-r--r--scripts/lib/wic/plugins/imager/direct.py97
-rw-r--r--scripts/lib/wic/plugins/source/bootimg-pcbios.py2
-rw-r--r--scripts/lib/wic/plugins/source/isoimage-isohybrid.py10
-rw-r--r--scripts/lib/wic/plugins/source/rootfs_pcbios_ext.py4
-rw-r--r--scripts/lib/wic/utils/partitionedfs.py194
5 files changed, 111 insertions, 196 deletions
diff --git a/scripts/lib/wic/plugins/imager/direct.py b/scripts/lib/wic/plugins/imager/direct.py
index a9144e2f4b..fefe88e0df 100644
--- a/scripts/lib/wic/plugins/imager/direct.py
+++ b/scripts/lib/wic/plugins/imager/direct.py
@@ -36,25 +36,8 @@ from wic.plugin import pluginmgr
36from wic.pluginbase import ImagerPlugin 36from wic.pluginbase import ImagerPlugin
37from wic.utils.errors import CreatorError, ImageError 37from wic.utils.errors import CreatorError, ImageError
38from wic.utils.misc import get_bitbake_var, exec_cmd, exec_native_cmd 38from wic.utils.misc import get_bitbake_var, exec_cmd, exec_native_cmd
39from wic.utils.partitionedfs import Image 39from wic.utils.partitionedfs import PartitionedImage
40 40
41class DiskImage():
42 """
43 A Disk backed by a file.
44 """
45 def __init__(self, device, size):
46 self.size = size
47 self.device = device
48 self.created = False
49
50 def create(self):
51 if self.created:
52 return
53 # create sparse disk image
54 with open(self.device, 'w') as sparse:
55 os.ftruncate(sparse.fileno(), self.size)
56
57 self.created = True
58 41
59class DirectPlugin(ImagerPlugin): 42class DirectPlugin(ImagerPlugin):
60 """ 43 """
@@ -189,9 +172,10 @@ class DirectPlugin(ImagerPlugin):
189 filesystems from the artifacts directly and combine them into 172 filesystems from the artifacts directly and combine them into
190 a partitioned image. 173 a partitioned image.
191 """ 174 """
192 self._image = Image(self.native_sysroot) 175 image_path = self._full_path(self.workdir, self.parts[0].disk, "direct")
176 self._image = PartitionedImage(image_path, self.ptable_format,
177 self.native_sysroot)
193 178
194 disk_ids = {}
195 for num, part in enumerate(self.parts, 1): 179 for num, part in enumerate(self.parts, 1):
196 # as a convenience, set source to the boot partition source 180 # as a convenience, set source to the boot partition source
197 # instead of forcing it to be set via bootloader --source 181 # instead of forcing it to be set via bootloader --source
@@ -203,10 +187,8 @@ class DirectPlugin(ImagerPlugin):
203 if self.ptable_format == 'gpt': 187 if self.ptable_format == 'gpt':
204 part.uuid = str(uuid.uuid4()) 188 part.uuid = str(uuid.uuid4())
205 else: # msdos partition table 189 else: # msdos partition table
206 if part.disk not in disk_ids: 190 part.uuid = '%0x-%02d' % (self._image.identifier,
207 disk_ids[part.disk] = int.from_bytes(os.urandom(4), 'little') 191 self._get_part_num(num, self.parts))
208 disk_id = disk_ids[part.disk]
209 part.uuid = '%0x-%02d' % (disk_id, self._get_part_num(num, self.parts))
210 192
211 fstab_path = self._write_fstab(self.rootfs_dir.get("ROOTFS_DIR")) 193 fstab_path = self._write_fstab(self.rootfs_dir.get("ROOTFS_DIR"))
212 194
@@ -225,11 +207,6 @@ class DirectPlugin(ImagerPlugin):
225 part.size = int(round(float(rsize_bb))) 207 part.size = int(round(float(rsize_bb)))
226 # need to create the filesystems in order to get their 208 # need to create the filesystems in order to get their
227 # sizes before we can add them and do the layout. 209 # sizes before we can add them and do the layout.
228 # Image.create() actually calls __format_disks() to create
229 # the disk images and carve out the partitions, then
230 # self.assemble() calls Image.assemble() which calls
231 # __write_partitition() for each partition to dd the fs
232 # into the partitions.
233 part.prepare(self, self.workdir, self.oe_builddir, self.rootfs_dir, 210 part.prepare(self, self.workdir, self.oe_builddir, self.rootfs_dir,
234 self.bootimg_dir, self.kernel_dir, self.native_sysroot) 211 self.bootimg_dir, self.kernel_dir, self.native_sysroot)
235 212
@@ -238,26 +215,14 @@ class DirectPlugin(ImagerPlugin):
238 if fstab_path: 215 if fstab_path:
239 shutil.move(fstab_path + ".orig", fstab_path) 216 shutil.move(fstab_path + ".orig", fstab_path)
240 217
241 self._image.layout_partitions(self.ptable_format) 218 self._image.layout_partitions()
242
243 for disk_name, disk in self._image.disks.items():
244 full_path = self._full_path(self.workdir, disk_name, "direct")
245 msger.debug("Adding disk %s as %s with size %s bytes" \
246 % (disk_name, full_path, disk['min_size']))
247 disk_obj = DiskImage(full_path, disk['min_size'])
248 self._image.add_disk(disk_name, disk_obj, disk_ids.get(disk_name))
249
250 self._image.create() 219 self._image.create()
251 220
252 def assemble(self): 221 def assemble(self):
253 """ 222 """
254 Assemble partitions into disk image(s) 223 Assemble partitions into disk image
255 """ 224 """
256 for disk_name, disk in self._image.disks.items(): 225 self._image.assemble()
257 full_path = self._full_path(self.workdir, disk_name, "direct")
258 msger.debug("Assembling disk %s as %s with size %s bytes" \
259 % (disk_name, full_path, disk['min_size']))
260 self._image.assemble(full_path)
261 226
262 def finalize(self): 227 def finalize(self):
263 """ 228 """
@@ -267,26 +232,25 @@ class DirectPlugin(ImagerPlugin):
267 creating and installing a bootloader configuration. 232 creating and installing a bootloader configuration.
268 """ 233 """
269 source_plugin = self.ks.bootloader.source 234 source_plugin = self.ks.bootloader.source
235 disk_name = self.parts[0].disk
270 if source_plugin: 236 if source_plugin:
271 name = "do_install_disk" 237 name = "do_install_disk"
272 methods = pluginmgr.get_source_plugin_methods(source_plugin, 238 methods = pluginmgr.get_source_plugin_methods(source_plugin,
273 {name: None}) 239 {name: None})
274 for disk_name, disk in self._image.disks.items(): 240 methods["do_install_disk"](self._image, disk_name, self, self.workdir,
275 methods["do_install_disk"](disk, disk_name, self, self.workdir, 241 self.oe_builddir, self.bootimg_dir,
276 self.oe_builddir, self.bootimg_dir, 242 self.kernel_dir, self.native_sysroot)
277 self.kernel_dir, self.native_sysroot) 243
278 244 full_path = self._image.path
279 for disk_name, disk in self._image.disks.items(): 245 # Generate .bmap
280 full_path = self._full_path(self.workdir, disk_name, "direct") 246 if self.bmap:
281 # Generate .bmap 247 msger.debug("Generating bmap file for %s" % disk_name)
282 if self.bmap: 248 exec_native_cmd("bmaptool create %s -o %s.bmap" % (full_path, full_path),
283 msger.debug("Generating bmap file for %s" % disk_name) 249 self.native_sysroot)
284 exec_native_cmd("bmaptool create %s -o %s.bmap" % (full_path, full_path), 250 # Compress the image
285 self.native_sysroot) 251 if self.compressor:
286 # Compress the image 252 msger.debug("Compressing disk %s with %s" % (disk_name, self.compressor))
287 if self.compressor: 253 exec_cmd("%s %s" % (self.compressor, full_path))
288 msger.debug("Compressing disk %s with %s" % (disk_name, self.compressor))
289 exec_cmd("%s %s" % (self.compressor, full_path))
290 254
291 def print_info(self): 255 def print_info(self):
292 """ 256 """
@@ -294,13 +258,12 @@ class DirectPlugin(ImagerPlugin):
294 """ 258 """
295 msg = "The new image(s) can be found here:\n" 259 msg = "The new image(s) can be found here:\n"
296 260
297 for disk_name in self._image.disks: 261 extension = "direct" + {"gzip": ".gz",
298 extension = "direct" + {"gzip": ".gz", 262 "bzip2": ".bz2",
299 "bzip2": ".bz2", 263 "xz": ".xz",
300 "xz": ".xz", 264 None: ""}.get(self.compressor)
301 None: ""}.get(self.compressor) 265 full_path = self._full_path(self.outdir, self.parts[0].disk, extension)
302 full_path = self._full_path(self.outdir, disk_name, extension) 266 msg += ' %s\n\n' % full_path
303 msg += ' %s\n\n' % full_path
304 267
305 msg += 'The following build artifacts were used to create the image(s):\n' 268 msg += 'The following build artifacts were used to create the image(s):\n'
306 for part in self.parts: 269 for part in self.parts:
diff --git a/scripts/lib/wic/plugins/source/bootimg-pcbios.py b/scripts/lib/wic/plugins/source/bootimg-pcbios.py
index 4b9b26552a..0be2b78e51 100644
--- a/scripts/lib/wic/plugins/source/bootimg-pcbios.py
+++ b/scripts/lib/wic/plugins/source/bootimg-pcbios.py
@@ -63,7 +63,7 @@ class BootimgPcbiosPlugin(SourcePlugin):
63 63
64 full_path = creator._full_path(workdir, disk_name, "direct") 64 full_path = creator._full_path(workdir, disk_name, "direct")
65 msger.debug("Installing MBR on disk %s as %s with size %s bytes" \ 65 msger.debug("Installing MBR on disk %s as %s with size %s bytes" \
66 % (disk_name, full_path, disk['min_size'])) 66 % (disk_name, full_path, disk.min_size))
67 67
68 rcode = runner.show(['dd', 'if=%s' % mbrfile, 68 rcode = runner.show(['dd', 'if=%s' % mbrfile,
69 'of=%s' % full_path, 'conv=notrunc']) 69 'of=%s' % full_path, 'conv=notrunc'])
diff --git a/scripts/lib/wic/plugins/source/isoimage-isohybrid.py b/scripts/lib/wic/plugins/source/isoimage-isohybrid.py
index ca28bc0fa3..fb34235631 100644
--- a/scripts/lib/wic/plugins/source/isoimage-isohybrid.py
+++ b/scripts/lib/wic/plugins/source/isoimage-isohybrid.py
@@ -473,13 +473,12 @@ class IsoImagePlugin(SourcePlugin):
473 utility for booting via BIOS from disk storage devices. 473 utility for booting via BIOS from disk storage devices.
474 """ 474 """
475 475
476 iso_img = "%s.p1" % disk.path
476 full_path = creator._full_path(workdir, disk_name, "direct") 477 full_path = creator._full_path(workdir, disk_name, "direct")
477 iso_img = "%s.p1" % full_path
478 full_path_iso = creator._full_path(workdir, disk_name, "iso") 478 full_path_iso = creator._full_path(workdir, disk_name, "iso")
479 479
480 isohybrid_cmd = "isohybrid -u %s" % iso_img 480 isohybrid_cmd = "isohybrid -u %s" % iso_img
481 msger.debug("running command: %s" % \ 481 msger.debug("running command: %s" % isohybrid_cmd)
482 isohybrid_cmd)
483 exec_native_cmd(isohybrid_cmd, native_sysroot) 482 exec_native_cmd(isohybrid_cmd, native_sysroot)
484 483
485 # Replace the image created by direct plugin with the one created by 484 # Replace the image created by direct plugin with the one created by
@@ -487,9 +486,6 @@ class IsoImagePlugin(SourcePlugin):
487 # mkisofs has a very specific MBR is system area of the ISO image, and 486 # mkisofs has a very specific MBR is system area of the ISO image, and
488 # direct plugin adds and configures an another MBR. 487 # direct plugin adds and configures an another MBR.
489 msger.debug("Replaceing the image created by direct plugin\n") 488 msger.debug("Replaceing the image created by direct plugin\n")
490 os.remove(full_path) 489 os.remove(disk.path)
491 shutil.copy2(iso_img, full_path_iso) 490 shutil.copy2(iso_img, full_path_iso)
492 shutil.copy2(full_path_iso, full_path) 491 shutil.copy2(full_path_iso, full_path)
493
494 # Remove temporary ISO file
495 os.remove(iso_img)
diff --git a/scripts/lib/wic/plugins/source/rootfs_pcbios_ext.py b/scripts/lib/wic/plugins/source/rootfs_pcbios_ext.py
index cb1da93d30..9e79a139da 100644
--- a/scripts/lib/wic/plugins/source/rootfs_pcbios_ext.py
+++ b/scripts/lib/wic/plugins/source/rootfs_pcbios_ext.py
@@ -204,9 +204,9 @@ class RootfsPlugin(SourcePlugin):
204 if not os.path.exists(mbrfile): 204 if not os.path.exists(mbrfile):
205 msger.error("Couldn't find %s. Has syslinux-native been baked?" % mbrfile) 205 msger.error("Couldn't find %s. Has syslinux-native been baked?" % mbrfile)
206 206
207 full_path = disk['disk'].device 207 full_path = disk.path
208 msger.debug("Installing MBR on disk %s as %s with size %s bytes" \ 208 msger.debug("Installing MBR on disk %s as %s with size %s bytes" \
209 % (disk_name, full_path, disk['min_size'])) 209 % (disk_name, full_path, disk.min_size))
210 210
211 ret_code = runner.show(['dd', 'if=%s' % mbrfile, 'of=%s' % full_path, 'conv=notrunc']) 211 ret_code = runner.show(['dd', 'if=%s' % mbrfile, 'of=%s' % full_path, 'conv=notrunc'])
212 if ret_code != 0: 212 if ret_code != 0:
diff --git a/scripts/lib/wic/utils/partitionedfs.py b/scripts/lib/wic/utils/partitionedfs.py
index 5397bb704c..cdf8f08015 100644
--- a/scripts/lib/wic/utils/partitionedfs.py
+++ b/scripts/lib/wic/utils/partitionedfs.py
@@ -19,6 +19,7 @@
19# Temple Place - Suite 330, Boston, MA 02111-1307, USA. 19# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 20
21import os 21import os
22
22from wic import msger 23from wic import msger
23from wic.utils.errors import ImageError 24from wic.utils.errors import ImageError
24from wic.utils.misc import exec_native_cmd 25from wic.utils.misc import exec_native_cmd
@@ -33,50 +34,28 @@ GPT_OVERHEAD = 34
33# Size of a sector in bytes 34# Size of a sector in bytes
34SECTOR_SIZE = 512 35SECTOR_SIZE = 512
35 36
36class Image(): 37class PartitionedImage():
37 """ 38 """
38 Generic base object for an image. 39 Partitioned image in a file.
39
40 An Image is a container for a set of DiskImages and associated
41 partitions.
42 """ 40 """
43 def __init__(self, native_sysroot=None): 41
44 self.disks = {} 42 def __init__(self, path, ptable_format, native_sysroot=None):
43 self.path = path # Path to the image file
44 self.numpart = 0 # Number of allocated partitions
45 self.realpart = 0 # Number of partitions in the partition table
46 self.offset = 0 # Offset of next partition (in sectors)
47 self.min_size = 0 # Minimum required disk size to fit
48 # all partitions (in bytes)
49 self.ptable_format = ptable_format # Partition table format
50 # Disk system identifier
51 self.identifier = int.from_bytes(os.urandom(4), 'little')
52
45 self.partitions = [] 53 self.partitions = []
46 self.partimages = [] 54 self.partimages = []
47 # Size of a sector used in calculations 55 # Size of a sector used in calculations
48 self.sector_size = SECTOR_SIZE 56 self.sector_size = SECTOR_SIZE
49 self.native_sysroot = native_sysroot 57 self.native_sysroot = native_sysroot
50 58
51 def _add_disk(self, disk_name):
52 """ Add a disk 'disk_name' to the internal list of disks. Note,
53 'disk_name' is the name of the disk in the target system
54 (e.g., sdb). """
55
56 if disk_name in self.disks:
57 # We already have this disk
58 return
59
60 self.disks[disk_name] = \
61 {'disk': None, # Disk object
62 'numpart': 0, # Number of allocate partitions
63 'realpart': 0, # Number of partitions in the partition table
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 'identifier': None} # Disk system identifier
70
71 def add_disk(self, name, disk_obj, identifier):
72 """ Add a disk object which have to be partitioned. More than one disk
73 can be added. In case of multiple disks, disk partitions have to be
74 added for each disk separately with 'add_partition()". """
75
76 self._add_disk(name)
77 self.disks[name]['disk'] = disk_obj
78 self.disks[name]['identifier'] = identifier
79
80 def add_partition(self, part): 59 def add_partition(self, part):
81 """ 60 """
82 Add the next partition. Partitions have to be added in the 61 Add the next partition. Partitions have to be added in the
@@ -88,24 +67,19 @@ class Image():
88 part.size_sec = part.disk_size * 1024 // self.sector_size 67 part.size_sec = part.disk_size * 1024 // self.sector_size
89 68
90 self.partitions.append(part) 69 self.partitions.append(part)
91 self._add_disk(part.disk)
92 70
93 def layout_partitions(self, ptable_format="msdos"): 71 def layout_partitions(self):
94 """ Layout the partitions, meaning calculate the position of every 72 """ Layout the partitions, meaning calculate the position of every
95 partition on the disk. The 'ptable_format' parameter defines the 73 partition on the disk. The 'ptable_format' parameter defines the
96 partition table format and may be "msdos". """ 74 partition table format and may be "msdos". """
97 75
98 msger.debug("Assigning %s partitions to disks" % ptable_format) 76 msger.debug("Assigning %s partitions to disks" % self.ptable_format)
99 77
100 # Go through partitions in the order they are added in .ks file 78 # Go through partitions in the order they are added in .ks file
101 for num in range(len(self.partitions)): 79 for num in range(len(self.partitions)):
102 part = self.partitions[num] 80 part = self.partitions[num]
103 81
104 if part.disk not in self.disks: 82 if self.ptable_format == 'msdos' and part.part_type:
105 raise ImageError("No disk %s for partition %s" \
106 % (part.disk, part.mountpoint))
107
108 if ptable_format == 'msdos' and part.part_type:
109 # The --part-type can also be implemented for MBR partitions, 83 # The --part-type can also be implemented for MBR partitions,
110 # in which case it would map to the 1-byte "partition type" 84 # in which case it would map to the 1-byte "partition type"
111 # filed at offset 3 of the partition entry. 85 # filed at offset 3 of the partition entry.
@@ -113,27 +87,24 @@ class Image():
113 "implemented for msdos partitions") 87 "implemented for msdos partitions")
114 88
115 # Get the disk where the partition is located 89 # Get the disk where the partition is located
116 disk = self.disks[part.disk] 90 self.numpart += 1
117 disk['numpart'] += 1
118 if not part.no_table: 91 if not part.no_table:
119 disk['realpart'] += 1 92 self.realpart += 1
120 disk['ptable_format'] = ptable_format
121 93
122 if disk['numpart'] == 1: 94 if self.numpart == 1:
123 if ptable_format == "msdos": 95 if self.ptable_format == "msdos":
124 overhead = MBR_OVERHEAD 96 overhead = MBR_OVERHEAD
125 elif ptable_format == "gpt": 97 elif self.ptable_format == "gpt":
126 overhead = GPT_OVERHEAD 98 overhead = GPT_OVERHEAD
127 99
128 # Skip one sector required for the partitioning scheme overhead 100 # Skip one sector required for the partitioning scheme overhead
129 disk['offset'] += overhead 101 self.offset += overhead
130 102
131 if disk['realpart'] > 3: 103 if self.realpart > 3:
132 # Reserve a sector for EBR for every logical partition 104 # Reserve a sector for EBR for every logical partition
133 # before alignment is performed. 105 # before alignment is performed.
134 if ptable_format == "msdos": 106 if self.ptable_format == "msdos":
135 disk['offset'] += 1 107 self.offset += 1
136
137 108
138 if part.align: 109 if part.align:
139 # If not first partition and we do have alignment set we need 110 # If not first partition and we do have alignment set we need
@@ -142,7 +113,7 @@ class Image():
142 # gaps we could enlargea the previous partition? 113 # gaps we could enlargea the previous partition?
143 114
144 # Calc how much the alignment is off. 115 # Calc how much the alignment is off.
145 align_sectors = disk['offset'] % (part.align * 1024 // self.sector_size) 116 align_sectors = self.offset % (part.align * 1024 // self.sector_size)
146 117
147 if align_sectors: 118 if align_sectors:
148 # If partition is not aligned as required, we need 119 # If partition is not aligned as required, we need
@@ -151,43 +122,41 @@ class Image():
151 122
152 msger.debug("Realignment for %s%s with %s sectors, original" 123 msger.debug("Realignment for %s%s with %s sectors, original"
153 " offset %s, target alignment is %sK." % 124 " offset %s, target alignment is %sK." %
154 (part.disk, disk['numpart'], align_sectors, 125 (part.disk, self.numpart, align_sectors,
155 disk['offset'], part.align)) 126 self.offset, part.align))
156 127
157 # increase the offset so we actually start the partition on right alignment 128 # increase the offset so we actually start the partition on right alignment
158 disk['offset'] += align_sectors 129 self.offset += align_sectors
159 130
160 part.start = disk['offset'] 131 part.start = self.offset
161 disk['offset'] += part.size_sec 132 self.offset += part.size_sec
162 133
163 part.type = 'primary' 134 part.type = 'primary'
164 if not part.no_table: 135 if not part.no_table:
165 part.num = disk['realpart'] 136 part.num = self.realpart
166 else: 137 else:
167 part.num = 0 138 part.num = 0
168 139
169 if disk['ptable_format'] == "msdos": 140 if self.ptable_format == "msdos":
170 # only count the partitions that are in partition table 141 # only count the partitions that are in partition table
171 if len([p for p in self.partitions if not p.no_table]) > 4: 142 if len([p for p in self.partitions if not p.no_table]) > 4:
172 if disk['realpart'] > 3: 143 if self.realpart > 3:
173 part.type = 'logical' 144 part.type = 'logical'
174 part.num = disk['realpart'] + 1 145 part.num = self.realpart + 1
175 146
176 disk['partitions'].append(num)
177 msger.debug("Assigned %s to %s%d, sectors range %d-%d size %d " 147 msger.debug("Assigned %s to %s%d, sectors range %d-%d size %d "
178 "sectors (%d bytes)." \ 148 "sectors (%d bytes)." \
179 % (part.mountpoint, part.disk, part.num, 149 % (part.mountpoint, part.disk, part.num,
180 part.start, disk['offset'] - 1, 150 part.start, self.offset - 1,
181 part.size_sec, part.size_sec * self.sector_size)) 151 part.size_sec, part.size_sec * self.sector_size))
182 152
183 # Once all the partitions have been layed out, we can calculate the 153 # Once all the partitions have been layed out, we can calculate the
184 # minumim disk sizes. 154 # minumim disk size
185 for disk in self.disks.values(): 155 self.min_size = self.offset
186 disk['min_size'] = disk['offset'] 156 if self.ptable_format == "gpt":
187 if disk['ptable_format'] == "gpt": 157 self.min_size += GPT_OVERHEAD
188 disk['min_size'] += GPT_OVERHEAD
189 158
190 disk['min_size'] *= self.sector_size 159 self.min_size *= self.sector_size
191 160
192 def _create_partition(self, device, parttype, fstype, start, size): 161 def _create_partition(self, device, parttype, fstype, start, size):
193 """ Create a partition on an image described by the 'device' object. """ 162 """ Create a partition on an image described by the 'device' object. """
@@ -205,23 +174,18 @@ class Image():
205 return exec_native_cmd(cmd, self.native_sysroot) 174 return exec_native_cmd(cmd, self.native_sysroot)
206 175
207 def create(self): 176 def create(self):
208 for dev in self.disks: 177 msger.debug("Creating sparse file %s" % self.path)
209 disk = self.disks[dev] 178 with open(self.path, 'w') as sparse:
210 disk['disk'].create() 179 os.ftruncate(sparse.fileno(), self.min_size)
211 180
212 for dev in self.disks: 181 msger.debug("Initializing partition table for %s" % self.path)
213 disk = self.disks[dev] 182 exec_native_cmd("parted -s %s mklabel %s" %
214 msger.debug("Initializing partition table for %s" % \ 183 (self.path, self.ptable_format), self.native_sysroot)
215 (disk['disk'].device)) 184
216 exec_native_cmd("parted -s %s mklabel %s" % \ 185 msger.debug("Set disk identifier %x" % self.identifier)
217 (disk['disk'].device, disk['ptable_format']), 186 with open(self.path, 'r+b') as img:
218 self.native_sysroot) 187 img.seek(0x1B8)
219 188 img.write(self.identifier.to_bytes(4, 'little'))
220 if disk['identifier']:
221 msger.debug("Set disk identifier %x" % disk['identifier'])
222 with open(disk['disk'].device, 'r+b') as img:
223 img.seek(0x1B8)
224 img.write(disk['identifier'].to_bytes(4, 'little'))
225 189
226 msger.debug("Creating partitions") 190 msger.debug("Creating partitions")
227 191
@@ -229,8 +193,7 @@ class Image():
229 if part.num == 0: 193 if part.num == 0:
230 continue 194 continue
231 195
232 disk = self.disks[part.disk] 196 if self.ptable_format == "msdos" and part.num == 5:
233 if disk['ptable_format'] == "msdos" and part.num == 5:
234 # Create an extended partition (note: extended 197 # Create an extended partition (note: extended
235 # partition is described in MBR and contains all 198 # partition is described in MBR and contains all
236 # logical partitions). The logical partitions save a 199 # logical partitions). The logical partitions save a
@@ -242,9 +205,9 @@ class Image():
242 # starts a sector before the first logical partition, 205 # starts a sector before the first logical partition,
243 # add a sector at the back, so that there is enough 206 # add a sector at the back, so that there is enough
244 # room for all logical partitions. 207 # room for all logical partitions.
245 self._create_partition(disk['disk'].device, "extended", 208 self._create_partition(self.path, "extended",
246 None, part.start - 1, 209 None, part.start - 1,
247 disk['offset'] - part.start + 1) 210 self.offset - part.start + 1)
248 211
249 if part.fstype == "swap": 212 if part.fstype == "swap":
250 parted_fs_type = "linux-swap" 213 parted_fs_type = "linux-swap"
@@ -267,7 +230,7 @@ class Image():
267 part.mountpoint) 230 part.mountpoint)
268 part.size_sec -= 1 231 part.size_sec -= 1
269 232
270 self._create_partition(disk['disk'].device, part.type, 233 self._create_partition(self.path, part.type,
271 parted_fs_type, part.start, part.size_sec) 234 parted_fs_type, part.start, part.size_sec)
272 235
273 if part.part_type: 236 if part.part_type:
@@ -275,71 +238,64 @@ class Image():
275 (part.num, part.part_type)) 238 (part.num, part.part_type))
276 exec_native_cmd("sgdisk --typecode=%d:%s %s" % \ 239 exec_native_cmd("sgdisk --typecode=%d:%s %s" % \
277 (part.num, part.part_type, 240 (part.num, part.part_type,
278 disk['disk'].device), self.native_sysroot) 241 self.path), self.native_sysroot)
279 242
280 if part.uuid and disk['ptable_format'] == "gpt": 243 if part.uuid and self.ptable_format == "gpt":
281 msger.debug("partition %d: set UUID to %s" % \ 244 msger.debug("partition %d: set UUID to %s" % \
282 (part.num, part.uuid)) 245 (part.num, part.uuid))
283 exec_native_cmd("sgdisk --partition-guid=%d:%s %s" % \ 246 exec_native_cmd("sgdisk --partition-guid=%d:%s %s" % \
284 (part.num, part.uuid, disk['disk'].device), 247 (part.num, part.uuid, self.path),
285 self.native_sysroot) 248 self.native_sysroot)
286 249
287 if part.label and disk['ptable_format'] == "gpt": 250 if part.label and self.ptable_format == "gpt":
288 msger.debug("partition %d: set name to %s" % \ 251 msger.debug("partition %d: set name to %s" % \
289 (part.num, part.label)) 252 (part.num, part.label))
290 exec_native_cmd("parted -s %s name %d %s" % \ 253 exec_native_cmd("parted -s %s name %d %s" % \
291 (disk['disk'].device, part.num, part.label), 254 (self.path, part.num, part.label),
292 self.native_sysroot) 255 self.native_sysroot)
293 256
294 if part.active: 257 if part.active:
295 flag_name = "legacy_boot" if disk['ptable_format'] == 'gpt' else "boot" 258 flag_name = "legacy_boot" if self.ptable_format == 'gpt' else "boot"
296 msger.debug("Set '%s' flag for partition '%s' on disk '%s'" % \ 259 msger.debug("Set '%s' flag for partition '%s' on disk '%s'" % \
297 (flag_name, part.num, disk['disk'].device)) 260 (flag_name, part.num, self.path))
298 exec_native_cmd("parted -s %s set %d %s on" % \ 261 exec_native_cmd("parted -s %s set %d %s on" % \
299 (disk['disk'].device, part.num, flag_name), 262 (self.path, part.num, flag_name),
300 self.native_sysroot) 263 self.native_sysroot)
301 if part.system_id: 264 if part.system_id:
302 exec_native_cmd("sfdisk --part-type %s %s %s" % \ 265 exec_native_cmd("sfdisk --part-type %s %s %s" % \
303 (disk['disk'].device, part.num, part.system_id), 266 (self.path, part.num, part.system_id),
304 self.native_sysroot) 267 self.native_sysroot)
305 268
306 # Parted defaults to enabling the lba flag for fat16 partitions, 269 # Parted defaults to enabling the lba flag for fat16 partitions,
307 # which causes compatibility issues with some firmware (and really 270 # which causes compatibility issues with some firmware (and really
308 # isn't necessary). 271 # isn't necessary).
309 if parted_fs_type == "fat16": 272 if parted_fs_type == "fat16":
310 if disk['ptable_format'] == 'msdos': 273 if self.ptable_format == 'msdos':
311 msger.debug("Disable 'lba' flag for partition '%s' on disk '%s'" % \ 274 msger.debug("Disable 'lba' flag for partition '%s' on disk '%s'" % \
312 (part.num, disk['disk'].device)) 275 (part.num, self.path))
313 exec_native_cmd("parted -s %s set %d lba off" % \ 276 exec_native_cmd("parted -s %s set %d lba off" % \
314 (disk['disk'].device, part.num), 277 (self.path, part.num),
315 self.native_sysroot) 278 self.native_sysroot)
316 279
317 def cleanup(self): 280 def cleanup(self):
318 if self.disks:
319 for dev in self.disks:
320 disk = self.disks[dev]
321 if hasattr(disk['disk'], 'cleanup'):
322 disk['disk'].cleanup()
323
324 # remove partition images 281 # remove partition images
325 for image in self.partimages: 282 for image in self.partimages:
326 if os.path.isfile(image): 283 os.remove(image)
327 os.remove(image)
328 284
329 def assemble(self, image_file): 285 def assemble(self):
330 msger.debug("Installing partitions") 286 msger.debug("Installing partitions")
331 287
332 for part in self.partitions: 288 for part in self.partitions:
333 source = part.source_file 289 source = part.source_file
334 if source: 290 if source:
335 # install source_file contents into a partition 291 # install source_file contents into a partition
336 sparse_copy(source, image_file, part.start * self.sector_size) 292 sparse_copy(source, self.path, part.start * self.sector_size)
337 293
338 msger.debug("Installed %s in partition %d, sectors %d-%d, " 294 msger.debug("Installed %s in partition %d, sectors %d-%d, "
339 "size %d sectors" % \ 295 "size %d sectors" % \
340 (source, part.num, part.start, 296 (source, part.num, part.start,
341 part.start + part.size_sec - 1, part.size_sec)) 297 part.start + part.size_sec - 1, part.size_sec))
342 298
343 partimage = image_file + '.p%d' % part.num 299 partimage = self.path + '.p%d' % part.num
344 os.rename(source, partimage) 300 os.rename(source, partimage)
345 self.partimages.append(partimage) 301 self.partimages.append(partimage)