2 # loop.py : LoopImageCreator classes
4 # Copyright 2007, Red Hat Inc.
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; version 2 of the License.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU Library General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 import mic.utils.kickstart as kickstart
23 from baseimager import BaseImageCreator
24 from mic.utils.errors import *
25 from mic.utils.fs_related import *
26 from mic.utils.misc import *
29 """The maximum string length supported for LoopImageCreator.fslabel."""
31 class LoopImageCreator(BaseImageCreator):
32 """Installs a system into a loopback-mountable filesystem image.
34 LoopImageCreator is a straightforward ImageCreator subclass; the system
35 is installed into an ext3 filesystem on a sparse file which can be
36 subsequently loopback-mounted.
39 def __init__(self, creatoropts = None, pkgmgr = None):
40 """Initialize a LoopImageCreator instance.
42 This method takes the same arguments as ImageCreator.__init__() with
45 fslabel -- A string used as a label for any filesystems created.
47 BaseImageCreator.__init__(self, creatoropts, pkgmgr)
50 self.fslabel = self.name
53 self.__blocksize = 4096
55 self.__fstype = kickstart.get_image_fstype(self.ks, "ext3")
56 self.__fsopts = kickstart.get_image_fsopts(self.ks, "defaults,noatime")
61 self.__instloop = None
65 self.__image_size = kickstart.get_image_size(self.ks,
70 self._img_name = self.name + ".img"
72 def _set_fstype(self, fstype):
73 self.__fstype = fstype
75 def _set_image_size(self, imgsize):
76 self.__image_size = imgsize
81 def __get_fslabel(self):
82 if self.__fslabel is None:
86 def __set_fslabel(self, val):
90 self.__fslabel = val[:FSLABEL_MAXLEN]
91 #A string used to label any filesystems created.
93 #Some filesystems impose a constraint on the maximum allowed size of the
94 #filesystem label. In the case of ext3 it's 16 characters, but in the case
95 #of ISO9660 it's 32 characters.
97 #mke2fs silently truncates the label, but mkisofs aborts if the label is too
98 #long. So, for convenience sake, any string assigned to this attribute is
99 #silently truncated to FSLABEL_MAXLEN (32) characters.
101 fslabel = property(__get_fslabel, __set_fslabel)
104 def __get_image(self):
105 if self.__imgdir is None:
106 raise CreatorError("_image is not valid before calling mount()")
107 return self.__imgdir + "/meego.img"
108 #The location of the image file.
110 #This is the path to the filesystem image. Subclasses may use this path
111 #in order to package the image in _stage_final_image().
113 #Note, this directory does not exist before ImageCreator.mount() is called.
115 #Note also, this is a read-only attribute.
116 _image = property(__get_image)
119 def __get_blocksize(self):
120 return self.__blocksize
121 def __set_blocksize(self, val):
123 raise CreatorError("_blocksize must be set before calling mount()")
125 self.__blocksize = int(val)
127 raise CreatorError("'%s' is not a valid integer value "
128 "for _blocksize" % val)
129 #The block size used by the image's filesystem.
131 #This is the block size used when creating the filesystem image. Subclasses
132 #may change this if they wish to use something other than a 4k block size.
134 #Note, this attribute may only be set before calling mount().
135 _blocksize = property(__get_blocksize, __set_blocksize)
138 def __get_fstype(self):
140 def __set_fstype(self, val):
141 if val != "ext2" and val != "ext3":
142 raise CreatorError("Unknown _fstype '%s' supplied" % val)
144 #The type of filesystem used for the image.
146 #This is the filesystem type used when creating the filesystem image.
147 #Subclasses may change this if they wish to use something other ext3.
149 #Note, only ext2 and ext3 are currently supported.
151 #Note also, this attribute may only be set before calling mount().
152 _fstype = property(__get_fstype, __set_fstype)
155 def __get_fsopts(self):
157 def __set_fsopts(self, val):
159 #Mount options of filesystem used for the image.
161 #This can be specified by --fsoptions="xxx,yyy" in part command in
163 _fsopts = property(__get_fsopts, __set_fsopts)
167 # Helpers for subclasses
169 def _resparse(self, size = None):
170 """Rebuild the filesystem image to be as sparse as possible.
172 This method should be used by subclasses when staging the final image
173 in order to reduce the actual space taken up by the sparse image file
174 to be as little as possible.
176 This is done by resizing the filesystem to the minimal size (thereby
177 eliminating any space taken up by deleted files) and then resizing it
178 back to the supplied size.
180 size -- the size in, in bytes, which the filesystem image should be
181 resized to after it has been minimized; this defaults to None,
182 causing the original size specified by the kickstart file to
183 be used (or 4GiB if not specified in the kickstart).
185 return self.__instloop.resparse(size)
187 def _base_on(self, base_on):
188 shutil.copyfile(base_on, self._image)
191 # Actual implementation
193 def _mount_instroot(self, base_on = None):
194 if self.__imgdir is None:
195 self.__imgdir = self._mkdtemp()
197 if not base_on is None:
198 self._base_on(base_on)
200 if self.__fstype in ("ext2", "ext3", "ext4"):
201 MyDiskMount = ExtDiskMount
202 elif self.__fstype == "btrfs":
203 MyDiskMount = BtrfsDiskMount
205 self.__instloop = MyDiskMount(SparseLoopbackDisk(self._image, self.__image_size),
212 self.__instloop.mount()
213 except MountError, e:
214 raise CreatorError("Failed to loopback mount '%s' : %s" %
217 def _unmount_instroot(self):
218 if not self.__instloop is None:
219 self.__instloop.cleanup()
221 def _stage_final_image(self):
223 shutil.move(self._image, self._outdir + "/" + self._img_name)