a909928a4ce3b5d33a4a055adb89928275310994
[tools/mic.git] / mic / imager / liveusb.py
1 #!/usr/bin/python -tt
2 #
3 # Copyright (c) 2011 Intel, Inc.
4 #
5 # This program is free software; you can redistribute it and/or modify it
6 # under the terms of the GNU General Public License as published by the Free
7 # Software Foundation; version 2 of the License
8 #
9 # This program is distributed in the hope that it will be useful, but
10 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 # or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 # for more details.
13 #
14 # You should have received a copy of the GNU General Public License along
15 # with this program; if not, write to the Free Software Foundation, Inc., 59
16 # Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18 import os
19 import shutil
20 import re
21
22 from mic import msger
23 from mic.utils import misc, fs_related, runner
24 from mic.utils.errors import CreatorError, MountError
25 from mic.utils.partitionedfs import PartitionedMount
26 from mic.imager.livecd import LiveCDImageCreator
27
28
29 class LiveUSBImageCreator(LiveCDImageCreator):
30     def __init__(self, *args):
31         LiveCDImageCreator.__init__(self, *args)
32
33         self._dep_checks.extend(["kpartx", "parted"])
34
35         # remove dependency of genisoimage in parent class
36         if "genisoimage" in self._dep_checks:
37             self._dep_checks.remove("genisoimage")
38
39     def _create_usbimg(self, isodir):
40         overlaysizemb = 64 #default
41         #skipcompress = self.skip_compression?
42         fstype = "vfat"
43         homesizemb=0
44         swapsizemb=0
45         homefile="home.img"
46         plussize=128
47         kernelargs=None
48
49         if fstype == 'vfat':
50             if overlaysizemb > 2047:
51                 raise CreatorError("Can't have an overlay of 2048MB or "
52                                    "greater on VFAT")
53
54             if homesizemb > 2047:
55                 raise CreatorError("Can't have an home overlay of 2048MB or "
56                                    "greater on VFAT")
57
58             if swapsizemb > 2047:
59                 raise CreatorError("Can't have an swap overlay of 2048MB or "
60                                    "greater on VFAT")
61
62         livesize = misc.get_file_size(isodir + "/LiveOS")
63
64         usbimgsize = (overlaysizemb + \
65                       homesizemb + \
66                       swapsizemb + \
67                       livesize + \
68                       plussize) * 1024L * 1024L
69
70         disk = fs_related.SparseLoopbackDisk("%s/%s.usbimg" \
71                                                  % (self._outdir, self.name),
72                                              usbimgsize)
73         usbmnt = self._mkdtemp("usb-mnt")
74         usbloop = PartitionedMount(usbmnt)
75         usbloop.add_disk('/dev/sdb', disk)
76
77         usbloop.add_partition(usbimgsize/1024/1024,
78                               "/dev/sdb",
79                               "/",
80                               fstype,
81                               boot=True)
82
83         usbloop.mount()
84
85         try:
86             fs_related.makedirs(usbmnt + "/LiveOS")
87
88             if os.path.exists(isodir + "/LiveOS/squashfs.img"):
89                 shutil.copyfile(isodir + "/LiveOS/squashfs.img",
90                                 usbmnt + "/LiveOS/squashfs.img")
91             else:
92                 fs_related.mksquashfs(os.path.dirname(self._image),
93                                       usbmnt + "/LiveOS/squashfs.img")
94
95             if os.path.exists(isodir + "/LiveOS/osmin.img"):
96                 shutil.copyfile(isodir + "/LiveOS/osmin.img",
97                                 usbmnt + "/LiveOS/osmin.img")
98
99             if fstype == "vfat" or fstype == "msdos":
100                 uuid = usbloop.partitions[0]['mount'].uuid
101                 label = usbloop.partitions[0]['mount'].fslabel
102                 usblabel = "UUID=%s-%s" % (uuid[0:4], uuid[4:8])
103                 overlaysuffix = "-%s-%s-%s" % (label, uuid[0:4], uuid[4:8])
104             else:
105                 diskmount = usbloop.partitions[0]['mount']
106                 usblabel = "UUID=%s" % diskmount.uuid
107                 overlaysuffix = "-%s-%s" % (diskmount.fslabel, diskmount.uuid)
108
109             args = ['cp', "-Rf", isodir + "/isolinux", usbmnt + "/syslinux"]
110             rc = runner.show(args)
111             if rc:
112                 raise CreatorError("Can't copy isolinux directory %s" \
113                                    % (isodir + "/isolinux/*"))
114
115             if os.path.isfile("/usr/share/syslinux/isolinux.bin"):
116                 syslinux_path = "/usr/share/syslinux"
117             elif  os.path.isfile("/usr/lib/syslinux/isolinux.bin"):
118                 syslinux_path = "/usr/lib/syslinux"
119             else:
120                 raise CreatorError("syslinux not installed : "
121                                    "cannot find syslinux installation path")
122
123             for f in ("isolinux.bin", "vesamenu.c32"):
124                 path = os.path.join(syslinux_path, f)
125                 if os.path.isfile(path):
126                     args = ['cp', path, usbmnt + "/syslinux/"]
127                     rc = runner.show(args)
128                     if rc:
129                         raise CreatorError("Can't copy syslinux file " + path)
130                 else:
131                     raise CreatorError("syslinux not installed: "
132                                        "syslinux file %s not found" % path)
133
134             fd = open(isodir + "/isolinux/isolinux.cfg", "r")
135             text = fd.read()
136             fd.close()
137             pattern = re.compile('CDLABEL=[^ ]*')
138             text = pattern.sub(usblabel, text)
139             pattern = re.compile('rootfstype=[^ ]*')
140             text = pattern.sub("rootfstype=" + fstype, text)
141             if kernelargs:
142                 text = text.replace("rd.live.image", "rd.live.image " + kernelargs)
143
144             if overlaysizemb > 0:
145                 msger.info("Initializing persistent overlay file")
146                 overfile = "overlay" + overlaysuffix
147                 if fstype == "vfat":
148                     args = ['dd',
149                             "if=/dev/zero",
150                             "of=" + usbmnt + "/LiveOS/" + overfile,
151                             "count=%d" % overlaysizemb,
152                             "bs=1M"]
153                 else:
154                     args = ['dd',
155                             "if=/dev/null",
156                             "of=" + usbmnt + "/LiveOS/" + overfile,
157                             "count=1",
158                             "bs=1M",
159                             "seek=%d" % overlaysizemb]
160                 rc = runner.show(args)
161                 if rc:
162                     raise CreatorError("Can't create overlay file")
163                 text = text.replace("rd.live.image", "rd.live.image rd.live.overlay=" + usblabel)
164                 text = text.replace(" ro ", " rw ")
165
166             if swapsizemb > 0:
167                 msger.info("Initializing swap file")
168                 swapfile = usbmnt + "/LiveOS/" + "swap.img"
169                 args = ['dd',
170                         "if=/dev/zero",
171                         "of=" + swapfile,
172                         "count=%d" % swapsizemb,
173                         "bs=1M"]
174                 rc = runner.show(args)
175                 if rc:
176                     raise CreatorError("Can't create swap file")
177                 args = ["mkswap", "-f", swapfile]
178                 rc = runner.show(args)
179                 if rc:
180                     raise CreatorError("Can't mkswap on swap file")
181
182             if homesizemb > 0:
183                 msger.info("Initializing persistent /home")
184                 homefile = usbmnt + "/LiveOS/" + homefile
185                 if fstype == "vfat":
186                     args = ['dd',
187                             "if=/dev/zero",
188                             "of=" + homefile,
189                             "count=%d" % homesizemb,
190                             "bs=1M"]
191                 else:
192                     args = ['dd',
193                             "if=/dev/null",
194                             "of=" + homefile,
195                             "count=1",
196                             "bs=1M",
197                             "seek=%d" % homesizemb]
198                 rc = runner.show(args)
199                 if rc:
200                     raise CreatorError("Can't create home file")
201
202                 mkfscmd = fs_related.find_binary_path("/sbin/mkfs." + fstype)
203                 if fstype == "ext2" or fstype == "ext3":
204                     args = [mkfscmd, "-F", "-j", homefile]
205                 else:
206                     args = [mkfscmd, homefile]
207                 rc = runner.show(args)
208                 if rc:
209                     raise CreatorError("Can't mke2fs home file")
210                 if fstype == "ext2" or fstype == "ext3":
211                     tune2fs = fs_related.find_binary_path("tune2fs")
212                     args = [tune2fs,
213                             "-c0",
214                             "-i0",
215                             "-ouser_xattr,acl",
216                             homefile]
217                     rc = runner.show(args)
218                     if rc:
219                         raise CreatorError("Can't tune2fs home file")
220
221             if fstype == "vfat" or fstype == "msdos":
222                 syslinuxcmd = fs_related.find_binary_path("syslinux")
223                 syslinuxcfg = usbmnt + "/syslinux/syslinux.cfg"
224                 args = [syslinuxcmd,
225                         "-d",
226                         "syslinux",
227                         usbloop.partitions[0]["device"]]
228
229             elif fstype == "ext2" or fstype == "ext3":
230                 extlinuxcmd = fs_related.find_binary_path("extlinux")
231                 syslinuxcfg = usbmnt + "/syslinux/extlinux.conf"
232                 args = [extlinuxcmd,
233                         "-i",
234                         usbmnt + "/syslinux"]
235
236             else:
237                 raise CreatorError("Invalid file system type: %s" % (fstype))
238
239             os.unlink(usbmnt + "/syslinux/isolinux.cfg")
240             fd = open(syslinuxcfg, "w")
241             fd.write(text)
242             fd.close()
243             rc = runner.show(args)
244             if rc:
245                 raise CreatorError("Can't install boot loader.")
246
247         finally:
248             usbloop.unmount()
249             usbloop.cleanup()
250
251         # Need to do this after image is unmounted and device mapper is closed
252         msger.info("set MBR")
253         mbrfile = "/usr/lib/syslinux/mbr.bin"
254         if not os.path.exists(mbrfile):
255             mbrfile = "/usr/share/syslinux/mbr.bin"
256             if not os.path.exists(mbrfile):
257                 raise CreatorError("mbr.bin file didn't exist.")
258         mbrsize = os.path.getsize(mbrfile)
259         outimg = "%s/%s.usbimg" % (self._outdir, self.name)
260
261         args = ['dd',
262                 "if=" + mbrfile,
263                 "of=" + outimg,
264                 "seek=0",
265                 "conv=notrunc",
266                 "bs=1",
267                 "count=%d" % (mbrsize)]
268         rc = runner.show(args)
269         if rc:
270             raise CreatorError("Can't set MBR.")
271
272     def _stage_final_image(self):
273         try:
274             isodir = self._get_isodir()
275             fs_related.makedirs(isodir + "/LiveOS")
276
277             minimal_size = self._resparse()
278
279             if not self.skip_minimize:
280                 fs_related.create_image_minimizer(isodir + "/LiveOS/osmin.img",
281                                                   self._image,
282                                                   minimal_size)
283
284             if self.skip_compression:
285                 shutil.move(self._image,
286                             isodir + "/LiveOS/ext3fs.img")
287             else:
288                 fs_related.makedirs(os.path.join(
289                                         os.path.dirname(self._image),
290                                         "LiveOS"))
291                 shutil.move(self._image,
292                             os.path.join(os.path.dirname(self._image),
293                                          "LiveOS", "ext3fs.img"))
294                 fs_related.mksquashfs(os.path.dirname(self._image),
295                            isodir + "/LiveOS/squashfs.img")
296
297                 self._create_usbimg(isodir)
298
299                 if self.pack_to:
300                     usbimg = os.path.join(self._outdir, self.name + ".usbimg")
301                     packimg = os.path.join(self._outdir, self.pack_to)
302                     misc.packing(packimg, usbimg)
303                     os.unlink(usbimg)
304
305         finally:
306             shutil.rmtree(isodir, ignore_errors = True)
307             self._set_isodir(None)
308