summaryrefslogtreecommitdiffstats
path: root/scripts/lib/wic/utils
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 /scripts/lib/wic/utils
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>
Diffstat (limited to 'scripts/lib/wic/utils')
-rw-r--r--scripts/lib/wic/utils/partitionedfs.py194
1 files changed, 75 insertions, 119 deletions
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)