partitionedfs: introduce add_disks() method
[tools/mic.git] / mic / utils / partitionedfs.py
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 MountError
26 from mic.utils.fs_related import *
27
28 # Lenght of MBR in sectors
29 MBR_SECTOR_LEN = 1
30
31 # Size of a sector in bytes
32 SECTOR_SIZE = 512
33
34 class PartitionedMount(Mount):
35     def __init__(self, mountdir, skipformat = False):
36         Mount.__init__(self, mountdir)
37         self.disks = {}
38         self.partitions = []
39         self.subvolumes = []
40         self.mapped = False
41         self.mountOrder = []
42         self.unmountOrder = []
43         self.parted=find_binary_path("parted")
44         self.kpartx=find_binary_path("kpartx")
45         self.mkswap=find_binary_path("mkswap")
46         self.btrfscmd=None
47         self.mountcmd=find_binary_path("mount")
48         self.umountcmd=find_binary_path("umount")
49         self.skipformat = skipformat
50         self.snapshot_created = self.skipformat
51         # Size of a sector used in calculations
52         self.sector_size = SECTOR_SIZE
53
54     def add_disks(self, disks):
55         """ Add the disks which have to be partitioned. """
56
57         for name in disks.keys():
58             self.disks[name] = { 'disk': disks[name],  # Disk object
59                                  'mapped': False, # True if kpartx mapping exists
60                                  'numpart': 0, # Number of allocate partitions
61                                  'partitions': [], # indexes to self.partitions
62                                  # Partitions with part num higher than 3 will
63                                  # be put inside extended partition.
64                                  'extended': 0, # Size of extended partition
65                                  # Offset of next partition (in sectors)
66                                  'offset': 0 }
67
68
69     def add_partition(self, size, disk, mountpoint, fstype = None, label=None, fsopts = None, boot = False, align = None):
70         # Converting MB to sectors for parted
71         size = size * 1024 * 1024 / self.sector_size
72
73         """ We need to handle subvolumes for btrfs """
74         if fstype == "btrfs" and fsopts and fsopts.find("subvol=") != -1:
75             self.btrfscmd=find_binary_path("btrfs")
76             subvol = None
77             opts = fsopts.split(",")
78             for opt in opts:
79                 if opt.find("subvol=") != -1:
80                     subvol = opt.replace("subvol=", "").strip()
81                     break
82             if not subvol:
83                 raise MountError("No subvolume: %s" % fsopts)
84             self.subvolumes.append({'size': size, # In sectors
85                                     'mountpoint': mountpoint, # Mount relative to chroot
86                                     'fstype': fstype, # Filesystem type
87                                     'fsopts': fsopts, # Filesystem mount options
88                                     'disk': disk, # physical disk name holding partition
89                                     'device': None, # kpartx device node for partition
90                                     'mount': None, # Mount object
91                                     'subvol': subvol, # Subvolume name
92                                     'boot': boot, # Bootable flag
93                                     'mounted': False # Mount flag
94                                    })
95
96         """ We still need partition for "/" or non-subvolume """
97         if mountpoint == "/" or not fsopts or fsopts.find("subvol=") == -1:
98             """ Don't need subvolume for "/" because it will be set as default subvolume """
99             if fsopts and fsopts.find("subvol=") != -1:
100                 opts = fsopts.split(",")
101                 for opt in opts:
102                     if opt.strip().startswith("subvol="):
103                         opts.remove(opt)
104                         break
105                 fsopts = ",".join(opts)
106             self.partitions.append({'size': size, # In sectors
107                                     'mountpoint': mountpoint, # Mount relative to chroot
108                                     'fstype': fstype, # Filesystem type
109                                     'fsopts': fsopts, # Filesystem mount options
110                                     'label': label, # Partition label
111                                     'disk': disk, # physical disk name holding partition
112                                     'device': None, # kpartx device node for partition
113                                     'mount': None, # Mount object
114                                     'num': None, # Partition number
115                                     'boot': boot, # Bootable flag
116                                     'align': align}) # Partition alignment
117
118     def __create_part_to_image(self, device, parttype, fstype, start, size):
119         # Start is included to the size so we need to substract one from the end.
120         end = start+size-1
121         msger.debug("Added '%s' part at Sector %d with size %d sectors" %
122                     (parttype, start, end))
123         part_cmd = [self.parted, "-s", device, "unit", "s", "mkpart", parttype]
124         if fstype:
125             part_cmd.extend([fstype])
126         part_cmd.extend(["%d" % start, "%d" % end])
127
128         msger.debug(part_cmd)
129         rc, out = runner.runtool(part_cmd, catch=3)
130         out = out.strip()
131         if out:
132             msger.debug('"parted" output: %s' % out)
133         return rc
134
135     def __format_disks(self):
136         msger.debug("Assigning partitions to disks")
137
138         mbr_sector_skipped = False
139
140         # Go through partitions in the order they are added in .ks file
141         for n in range(len(self.partitions)):
142             p = self.partitions[n]
143
144             if not self.disks.has_key(p['disk']):
145                 raise MountError("No disk %s for partition %s" % (p['disk'], p['mountpoint']))
146
147             if not mbr_sector_skipped:
148                 #  This hack is used to remove one sector from the first partition,
149                 #  that is the used to the MBR.
150                 p['size'] -= 1
151                 mbr_sector_skipped = True
152
153             # Get the disk where the partition is located
154             d = self.disks[p['disk']]
155             d['numpart'] += 1
156
157             # alignment in sectors
158             align_sectors = None
159             # if first partition then we need to skip the first sector
160             # where the MBR is located, if the alignment isn't set
161             # See: https://wiki.linaro.org/WorkingGroups/Kernel/Projects/FlashCardSurvey
162             if d['numpart'] == 1:
163                 if p['align'] and p['align'] > 0:
164                     align_sectors = p['align'] * 1024 / self.sector_size
165                 else:
166                     align_sectors = MBR_SECTOR_LEN
167             elif p['align']:
168                 # If not first partition and we do have alignment set we need
169                 # to align the partition.
170                 # FIXME: This leaves a empty spaces to the disk. To fill the
171                 # gaps we could enlargea the previous partition?
172
173                 # Calc how much the alignment is off.
174                 align_sectors = d['offset'] % (p['align'] * 1024 / self.sector_size)
175                 # We need to move forward to the next alignment point
176                 align_sectors = (p['align'] * 1024 / self.sector_size) - align_sectors
177
178             if align_sectors:
179                 if p['align'] and p['align'] > 0:
180                     msger.debug("Realignment for %s%s with %s sectors, original"
181                                 " offset %s, target alignment is %sK." %
182                                 (p['disk'], d['numpart'], align_sectors,
183                                  d['offset'], p['align']))
184                 # p['size'] already converted in secctors
185                 if p['size'] <= align_sectors:
186                     raise MountError("Partition for %s is too small to handle "
187                                      "the alignment change." % p['mountpoint'])
188
189                 # increase the offset so we actually start the partition on right alignment
190                 d['offset'] += align_sectors
191
192             if d['numpart'] > 3:
193                 # Increase allocation of extended partition to hold this partition
194                 d['extended'] += p['size']
195                 p['type'] = 'logical'
196                 p['num'] = d['numpart'] + 1
197             else:
198                 p['type'] = 'primary'
199                 p['num'] = d['numpart']
200
201             p['start'] = d['offset']
202             d['offset'] += p['size']
203             d['partitions'].append(n)
204             msger.debug("Assigned %s to %s%d at Sector %d with size %d sectors "
205                         "/ %d bytes." % (p['mountpoint'], p['disk'], p['num'],
206                                          p['start'], p['size'],
207                                          p['size'] * self.sector_size))
208
209         if self.skipformat:
210             msger.debug("Skipping disk format, because skipformat flag is set.")
211             return
212
213         for dev in self.disks.keys():
214             d = self.disks[dev]
215             msger.debug("Initializing partition table for %s" % (d['disk'].device))
216             rc, out = runner.runtool([self.parted, "-s", d['disk'].device, "mklabel", "msdos"], catch=3)
217             out = out.strip()
218             if out:
219                 msger.debug('"parted" output: %s' % out)
220
221             if rc != 0:
222                 # NOTE: We don't throw exception when return code is not 0, because
223                 # parted always fails to reload part table with loop devices.
224                 # This prevents us from distinguishing real errors based on return code.
225                 msger.debug("WARNING: parted returned '%s' instead of 0 when creating partition-table for disk '%s'." % (rc, d['disk'].device))
226
227         msger.debug("Creating partitions")
228
229         for p in self.partitions:
230             d = self.disks[p['disk']]
231             if p['num'] == 5:
232                 self.__create_part_to_image(d['disk'].device,"extended",None,p['start'],d['extended'])
233
234             if p['fstype'] == "swap":
235                 parted_fs_type = "linux-swap"
236             elif p['fstype'] == "vfat":
237                 parted_fs_type = "fat32"
238             elif p['fstype'] == "msdos":
239                 parted_fs_type = "fat16"
240             else:
241                 # Type for ext2/ext3/ext4/btrfs
242                 parted_fs_type = "ext2"
243
244             # Boot ROM of OMAP boards require vfat boot partition to have an
245             # even number of sectors.
246             if p['mountpoint'] == "/boot" and p['fstype'] in ["vfat","msdos"] and p['size'] % 2:
247                 msger.debug("Substracting one sector from '%s' partition to get even number of sectors for the partition." % (p['mountpoint']))
248                 p['size'] -= 1
249
250             ret = self.__create_part_to_image(d['disk'].device,p['type'],
251                                              parted_fs_type, p['start'],
252                                              p['size'])
253
254             if ret != 0:
255                 # NOTE: We don't throw exception when return code is not 0, because
256                 # parted always fails to reload part table with loop devices.
257                 # This prevents us from distinguishing real errors based on return code.
258                 msger.debug("WARNING: parted returned '%s' instead of 0 when creating partition '%s' for disk '%s'." % (ret, p['mountpoint'], d['disk'].device))
259
260             if p['boot']:
261                 msger.debug("Setting boot flag for partition '%s' on disk '%s'." % (p['num'],d['disk'].device))
262                 boot_cmd = [self.parted, "-s", d['disk'].device, "set", "%d" % p['num'], "boot", "on"]
263                 msger.debug(boot_cmd)
264                 rc = runner.show(boot_cmd)
265
266                 if rc != 0:
267                     # NOTE: We don't throw exception when return code is not 0, because
268                     # parted always fails to reload part table with loop devices.
269                     # This prevents us from distinguishing real errors based on return code.
270                     msger.warning("parted returned '%s' instead of 0 when adding boot flag for partition '%s' disk '%s'." % (rc,p['num'],d['disk'].device))
271
272     def __map_partitions(self):
273         """Load it if dm_snapshot isn't loaded"""
274         load_module("dm_snapshot")
275
276         for dev in self.disks.keys():
277             d = self.disks[dev]
278             if d['mapped']:
279                 continue
280
281             msger.debug("Running kpartx on %s" % d['disk'].device )
282             rc, kpartxOutput = runner.runtool([self.kpartx, "-l", "-v", d['disk'].device])
283             kpartxOutput = kpartxOutput.splitlines()
284
285             if rc != 0:
286                 raise MountError("Failed to query partition mapping for '%s'" %
287                                  d['disk'].device)
288
289             # Strip trailing blank and mask verbose output
290             i = 0
291             while i < len(kpartxOutput) and kpartxOutput[i][0:4] != "loop":
292                i = i + 1
293             kpartxOutput = kpartxOutput[i:]
294
295             # Quick sanity check that the number of partitions matches
296             # our expectation. If it doesn't, someone broke the code
297             # further up
298             if len(kpartxOutput) != d['numpart']:
299                 raise MountError("Unexpected number of partitions from kpartx: %d != %d" %
300                                  (len(kpartxOutput), d['numpart']))
301
302             for i in range(len(kpartxOutput)):
303                 line = kpartxOutput[i]
304                 newdev = line.split()[0]
305                 mapperdev = "/dev/mapper/" + newdev
306                 loopdev = d['disk'].device + newdev[-1]
307
308                 msger.debug("Dev %s: %s -> %s" % (newdev, loopdev, mapperdev))
309                 pnum = d['partitions'][i]
310                 self.partitions[pnum]['device'] = loopdev
311
312                 # grub's install wants partitions to be named
313                 # to match their parent device + partition num
314                 # kpartx doesn't work like this, so we add compat
315                 # symlinks to point to /dev/mapper
316                 if os.path.lexists(loopdev):
317                     os.unlink(loopdev)
318                 os.symlink(mapperdev, loopdev)
319
320             msger.debug("Adding partx mapping for %s" % d['disk'].device)
321             rc = runner.show([self.kpartx, "-v", "-a", d['disk'].device])
322
323             if rc != 0:
324                 # Make sure that the device maps are also removed on error case.
325                 # The d['mapped'] isn't set to True if the kpartx fails so
326                 # failed mapping will not be cleaned on cleanup either.
327                 runner.quiet([self.kpartx, "-d", d['disk'].device])
328                 raise MountError("Failed to map partitions for '%s'" %
329                                  d['disk'].device)
330
331             d['mapped'] = True
332
333     def __unmap_partitions(self):
334         for dev in self.disks.keys():
335             d = self.disks[dev]
336             if not d['mapped']:
337                 continue
338
339             msger.debug("Removing compat symlinks")
340             for pnum in d['partitions']:
341                 if self.partitions[pnum]['device'] != None:
342                     os.unlink(self.partitions[pnum]['device'])
343                     self.partitions[pnum]['device'] = None
344
345             msger.debug("Unmapping %s" % d['disk'].device)
346             rc = runner.quiet([self.kpartx, "-d", d['disk'].device])
347             if rc != 0:
348                 raise MountError("Failed to unmap partitions for '%s'" %
349                                  d['disk'].device)
350
351             d['mapped'] = False
352
353     def __calculate_mountorder(self):
354         msger.debug("Calculating mount order")
355         for p in self.partitions:
356             self.mountOrder.append(p['mountpoint'])
357             self.unmountOrder.append(p['mountpoint'])
358
359         self.mountOrder.sort()
360         self.unmountOrder.sort()
361         self.unmountOrder.reverse()
362
363     def cleanup(self):
364         Mount.cleanup(self)
365         if self.disks:
366             self.__unmap_partitions()
367             for dev in self.disks.keys():
368                 d = self.disks[dev]
369                 try:
370                     d['disk'].cleanup()
371                 except:
372                     pass
373
374     def unmount(self):
375         self.__unmount_subvolumes()
376         for mp in self.unmountOrder:
377             if mp == 'swap':
378                 continue
379             p = None
380             for p1 in self.partitions:
381                 if p1['mountpoint'] == mp:
382                     p = p1
383                     break
384
385             if p['mount'] != None:
386                 try:
387                     """ Create subvolume snapshot here """
388                     if p['fstype'] == "btrfs" and p['mountpoint'] == "/" and not self.snapshot_created:
389                         self.__create_subvolume_snapshots(p, p["mount"])
390                     p['mount'].cleanup()
391                 except:
392                     pass
393                 p['mount'] = None
394
395     """ Only for btrfs """
396     def __get_subvolume_id(self, rootpath, subvol):
397         if not self.btrfscmd:
398             self.btrfscmd=find_binary_path("btrfs")
399         argv = [ self.btrfscmd, "subvolume", "list", rootpath ]
400
401         rc, out = runner.runtool(argv)
402         msger.debug(out)
403
404         if rc != 0:
405             raise MountError("Failed to get subvolume id from %s', return code: %d." % (rootpath, rc))
406
407         subvolid = -1
408         for line in out.splitlines():
409             if line.endswith(" path %s" % subvol):
410                 subvolid = line.split()[1]
411                 if not subvolid.isdigit():
412                     raise MountError("Invalid subvolume id: %s" % subvolid)
413                 subvolid = int(subvolid)
414                 break
415         return subvolid
416
417     def __create_subvolume_metadata(self, p, pdisk):
418         if len(self.subvolumes) == 0:
419             return
420
421         argv = [ self.btrfscmd, "subvolume", "list", pdisk.mountdir ]
422         rc, out = runner.runtool(argv)
423         msger.debug(out)
424
425         if rc != 0:
426             raise MountError("Failed to get subvolume id from %s', return code: %d." % (pdisk.mountdir, rc))
427
428         subvolid_items = out.splitlines()
429         subvolume_metadata = ""
430         for subvol in self.subvolumes:
431             for line in subvolid_items:
432                 if line.endswith(" path %s" % subvol["subvol"]):
433                     subvolid = line.split()[1]
434                     if not subvolid.isdigit():
435                         raise MountError("Invalid subvolume id: %s" % subvolid)
436
437                     subvolid = int(subvolid)
438                     opts = subvol["fsopts"].split(",")
439                     for opt in opts:
440                         if opt.strip().startswith("subvol="):
441                             opts.remove(opt)
442                             break
443                     fsopts = ",".join(opts)
444                     subvolume_metadata += "%d\t%s\t%s\t%s\n" % (subvolid, subvol["subvol"], subvol['mountpoint'], fsopts)
445
446         if subvolume_metadata:
447             fd = open("%s/.subvolume_metadata" % pdisk.mountdir, "w")
448             fd.write(subvolume_metadata)
449             fd.close()
450
451     def __get_subvolume_metadata(self, p, pdisk):
452         subvolume_metadata_file = "%s/.subvolume_metadata" % pdisk.mountdir
453         if not os.path.exists(subvolume_metadata_file):
454             return
455
456         fd = open(subvolume_metadata_file, "r")
457         content = fd.read()
458         fd.close()
459
460         for line in content.splitlines():
461             items = line.split("\t")
462             if items and len(items) == 4:
463                 self.subvolumes.append({'size': 0, # In sectors
464                                         'mountpoint': items[2], # Mount relative to chroot
465                                         'fstype': "btrfs", # Filesystem type
466                                         'fsopts': items[3] + ",subvol=%s" %  items[1], # Filesystem mount options
467                                         'disk': p['disk'], # physical disk name holding partition
468                                         'device': None, # kpartx device node for partition
469                                         'mount': None, # Mount object
470                                         'subvol': items[1], # Subvolume name
471                                         'boot': False, # Bootable flag
472                                         'mounted': False # Mount flag
473                                    })
474
475     def __create_subvolumes(self, p, pdisk):
476         """ Create all the subvolumes """
477
478         for subvol in self.subvolumes:
479             argv = [ self.btrfscmd, "subvolume", "create", pdisk.mountdir + "/" + subvol["subvol"]]
480
481             rc = runner.show(argv)
482             if rc != 0:
483                 raise MountError("Failed to create subvolume '%s', return code: %d." % (subvol["subvol"], rc))
484
485         """ Set default subvolume, subvolume for "/" is default """
486         subvol = None
487         for subvolume in self.subvolumes:
488             if subvolume["mountpoint"] == "/" and p["disk"] == subvolume["disk"]:
489                 subvol = subvolume
490                 break
491
492         if subvol:
493             """ Get default subvolume id """
494             subvolid = self. __get_subvolume_id(pdisk.mountdir, subvol["subvol"])
495             """ Set default subvolume """
496             if subvolid != -1:
497                 rc = runner.show([ self.btrfscmd, "subvolume", "set-default", "%d" % subvolid, pdisk.mountdir])
498                 if rc != 0:
499                     raise MountError("Failed to set default subvolume id: %d', return code: %d." % (subvolid, rc))
500
501         self.__create_subvolume_metadata(p, pdisk)
502
503     def __mount_subvolumes(self, p, pdisk):
504         if self.skipformat:
505             """ Get subvolume info """
506             self.__get_subvolume_metadata(p, pdisk)
507             """ Set default mount options """
508             if len(self.subvolumes) != 0:
509                 for subvol in self.subvolumes:
510                     if subvol["mountpoint"] == p["mountpoint"] == "/":
511                         opts = subvol["fsopts"].split(",")
512                         for opt in opts:
513                             if opt.strip().startswith("subvol="):
514                                 opts.remove(opt)
515                                 break
516                         pdisk.fsopts = ",".join(opts)
517                         break
518
519         if len(self.subvolumes) == 0:
520             """ Return directly if no subvolumes """
521             return
522
523         """ Remount to make default subvolume mounted """
524         rc = runner.show([self.umountcmd, pdisk.mountdir])
525         if rc != 0:
526             raise MountError("Failed to umount %s" % pdisk.mountdir)
527
528         rc = runner.show([self.mountcmd, "-o", pdisk.fsopts, pdisk.disk.device, pdisk.mountdir])
529         if rc != 0:
530             raise MountError("Failed to umount %s" % pdisk.mountdir)
531
532         for subvol in self.subvolumes:
533             if subvol["mountpoint"] == "/":
534                 continue
535             subvolid = self. __get_subvolume_id(pdisk.mountdir, subvol["subvol"])
536             if subvolid == -1:
537                 msger.debug("WARNING: invalid subvolume %s" % subvol["subvol"])
538                 continue
539             """ Replace subvolume name with subvolume ID """
540             opts = subvol["fsopts"].split(",")
541             for opt in opts:
542                 if opt.strip().startswith("subvol="):
543                     opts.remove(opt)
544                     break
545
546             opts.extend(["subvolrootid=0", "subvol=%s" % subvol["subvol"]])
547             fsopts = ",".join(opts)
548             subvol['fsopts'] = fsopts
549             mountpoint = self.mountdir + subvol['mountpoint']
550             makedirs(mountpoint)
551             rc = runner.show([self.mountcmd, "-o", fsopts, pdisk.disk.device, mountpoint])
552             if rc != 0:
553                 raise MountError("Failed to mount subvolume %s to %s" % (subvol["subvol"], mountpoint))
554             subvol["mounted"] = True
555
556     def __unmount_subvolumes(self):
557         """ It may be called multiple times, so we need to chekc if it is still mounted. """
558         for subvol in self.subvolumes:
559             if subvol["mountpoint"] == "/":
560                 continue
561             if not subvol["mounted"]:
562                 continue
563             mountpoint = self.mountdir + subvol['mountpoint']
564             rc = runner.show([self.umountcmd, mountpoint])
565             if rc != 0:
566                 raise MountError("Failed to unmount subvolume %s from %s" % (subvol["subvol"], mountpoint))
567             subvol["mounted"] = False
568
569     def __create_subvolume_snapshots(self, p, pdisk):
570         import time
571
572         if self.snapshot_created:
573             return
574
575         """ Remount with subvolid=0 """
576         rc = runner.show([self.umountcmd, pdisk.mountdir])
577         if rc != 0:
578             raise MountError("Failed to umount %s" % pdisk.mountdir)
579         if pdisk.fsopts:
580             mountopts = pdisk.fsopts + ",subvolid=0"
581         else:
582             mountopts = "subvolid=0"
583         rc = runner.show([self.mountcmd, "-o", mountopts, pdisk.disk.device, pdisk.mountdir])
584         if rc != 0:
585             raise MountError("Failed to umount %s" % pdisk.mountdir)
586
587         """ Create all the subvolume snapshots """
588         snapshotts = time.strftime("%Y%m%d-%H%M")
589         for subvol in self.subvolumes:
590             subvolpath = pdisk.mountdir + "/" + subvol["subvol"]
591             snapshotpath = subvolpath + "_%s-1" % snapshotts
592             rc = runner.show([ self.btrfscmd, "subvolume", "snapshot", subvolpath, snapshotpath ])
593             if rc != 0:
594                 raise MountError("Failed to create subvolume snapshot '%s' for '%s', return code: %d." % (snapshotpath, subvolpath, rc))
595
596         self.snapshot_created = True
597
598     def mount(self):
599         for dev in self.disks.keys():
600             d = self.disks[dev]
601             d['disk'].create()
602
603         self.__format_disks()
604         self.__map_partitions()
605         self.__calculate_mountorder()
606
607         for mp in self.mountOrder:
608             p = None
609             for p1 in self.partitions:
610                 if p1['mountpoint'] == mp:
611                     p = p1
612                     break
613
614             if not p['label']:
615                 if p['mountpoint'] == "/":
616                     p['label'] = 'platform'
617                 else:
618                     p['label'] = mp.split('/')[-1]
619
620             if mp == 'swap':
621                 import uuid
622                 p['uuid'] = str(uuid.uuid1())
623                 runner.show([self.mkswap,
624                              '-L', p['label'],
625                              '-U', p['uuid'],
626                              p['device']])
627                 continue
628
629             rmmountdir = False
630             if p['mountpoint'] == "/":
631                 rmmountdir = True
632             if p['fstype'] == "vfat" or p['fstype'] == "msdos":
633                 myDiskMount = VfatDiskMount
634             elif p['fstype'] in ("ext2", "ext3", "ext4"):
635                 myDiskMount = ExtDiskMount
636             elif p['fstype'] == "btrfs":
637                 myDiskMount = BtrfsDiskMount
638             else:
639                 raise MountError("Fail to support file system " + p['fstype'])
640
641             if p['fstype'] == "btrfs" and not p['fsopts']:
642                 p['fsopts'] = "subvolid=0"
643
644             pdisk = myDiskMount(RawDisk(p['size'] * self.sector_size, p['device']),
645                                  self.mountdir + p['mountpoint'],
646                                  p['fstype'],
647                                  4096,
648                                  p['label'],
649                                  rmmountdir,
650                                  self.skipformat,
651                                  fsopts = p['fsopts'])
652             pdisk.mount(pdisk.fsopts)
653             if p['fstype'] == "btrfs" and p['mountpoint'] == "/":
654                 if not self.skipformat:
655                     self.__create_subvolumes(p, pdisk)
656                 self.__mount_subvolumes(p, pdisk)
657             p['mount'] = pdisk
658             p['uuid'] = pdisk.uuid
659
660     def resparse(self, size = None):
661         # Can't re-sparse a disk image - too hard
662         pass