summaryrefslogtreecommitdiffstats
path: root/scripts/lib/wic
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/lib/wic')
-rw-r--r--scripts/lib/wic/3rdparty/pykickstart/__init__.py0
-rw-r--r--scripts/lib/wic/3rdparty/pykickstart/base.py466
-rw-r--r--scripts/lib/wic/3rdparty/pykickstart/commands/__init__.py20
-rw-r--r--scripts/lib/wic/3rdparty/pykickstart/commands/bootloader.py216
-rw-r--r--scripts/lib/wic/3rdparty/pykickstart/commands/partition.py314
-rw-r--r--scripts/lib/wic/3rdparty/pykickstart/constants.py57
-rw-r--r--scripts/lib/wic/3rdparty/pykickstart/errors.py103
-rw-r--r--scripts/lib/wic/3rdparty/pykickstart/handlers/__init__.py0
-rw-r--r--scripts/lib/wic/3rdparty/pykickstart/handlers/control.py46
-rw-r--r--scripts/lib/wic/3rdparty/pykickstart/handlers/f16.py24
-rw-r--r--scripts/lib/wic/3rdparty/pykickstart/ko.py37
-rw-r--r--scripts/lib/wic/3rdparty/pykickstart/options.py223
-rw-r--r--scripts/lib/wic/3rdparty/pykickstart/parser.py619
-rw-r--r--scripts/lib/wic/3rdparty/pykickstart/sections.py244
-rw-r--r--scripts/lib/wic/3rdparty/pykickstart/version.py168
-rw-r--r--scripts/lib/wic/__init__.py12
-rw-r--r--scripts/lib/wic/__version__.py1
-rw-r--r--scripts/lib/wic/canned-wks/common.wks.inc3
-rw-r--r--scripts/lib/wic/canned-wks/directdisk-bootloader-config.cfg27
-rw-r--r--scripts/lib/wic/canned-wks/directdisk-bootloader-config.wks8
-rw-r--r--scripts/lib/wic/canned-wks/directdisk-gpt.wks2
-rw-r--r--scripts/lib/wic/canned-wks/directdisk-multi-rootfs.wks2
-rw-r--r--scripts/lib/wic/canned-wks/directdisk.wks6
-rw-r--r--scripts/lib/wic/canned-wks/efi-bootdisk.wks.in3
-rw-r--r--scripts/lib/wic/canned-wks/mkefidisk.wks4
-rw-r--r--scripts/lib/wic/canned-wks/mkgummidisk.wks11
-rw-r--r--scripts/lib/wic/canned-wks/mkhybridiso.wks2
-rw-r--r--scripts/lib/wic/canned-wks/qemuriscv.wks3
-rw-r--r--scripts/lib/wic/canned-wks/qemux86-directdisk.wks6
-rw-r--r--scripts/lib/wic/canned-wks/sdimage-bootpart.wks4
-rw-r--r--scripts/lib/wic/canned-wks/systemd-bootdisk.wks11
-rw-r--r--scripts/lib/wic/conf.py100
-rw-r--r--scripts/lib/wic/config/wic.conf6
-rw-r--r--scripts/lib/wic/creator.py124
-rw-r--r--scripts/lib/wic/engine.py522
-rw-r--r--scripts/lib/wic/filemap.py576
-rw-r--r--scripts/lib/wic/help.py481
-rw-r--r--scripts/lib/wic/imager/__init__.py0
-rw-r--r--scripts/lib/wic/imager/baseimager.py192
-rw-r--r--scripts/lib/wic/imager/direct.py378
-rw-r--r--scripts/lib/wic/kickstart/__init__.py122
-rw-r--r--scripts/lib/wic/kickstart/custom_commands/__init__.py7
-rw-r--r--scripts/lib/wic/kickstart/custom_commands/partition.py526
-rw-r--r--scripts/lib/wic/kickstart/custom_commands/wicboot.py60
-rw-r--r--scripts/lib/wic/ksparser.py295
-rw-r--r--scripts/lib/wic/misc.py264
-rw-r--r--scripts/lib/wic/msger.py309
-rw-r--r--scripts/lib/wic/partition.py496
-rw-r--r--scripts/lib/wic/plugin.py150
-rw-r--r--scripts/lib/wic/pluginbase.py116
-rw-r--r--scripts/lib/wic/plugins/imager/direct.py623
-rw-r--r--scripts/lib/wic/plugins/imager/direct_plugin.py102
-rw-r--r--scripts/lib/wic/plugins/source/bootimg-biosplusefi.py213
-rw-r--r--scripts/lib/wic/plugins/source/bootimg-efi.py384
-rw-r--r--scripts/lib/wic/plugins/source/bootimg-partition.py207
-rw-r--r--scripts/lib/wic/plugins/source/bootimg-pcbios.py207
-rw-r--r--scripts/lib/wic/plugins/source/empty.py32
-rw-r--r--scripts/lib/wic/plugins/source/fsimage.py73
-rw-r--r--scripts/lib/wic/plugins/source/isoimage-isohybrid.py443
-rw-r--r--scripts/lib/wic/plugins/source/rawcopy.py92
-rw-r--r--scripts/lib/wic/plugins/source/rootfs.py222
-rw-r--r--scripts/lib/wic/plugins/source/rootfs_pcbios_ext.py181
-rw-r--r--scripts/lib/wic/test1
-rw-r--r--scripts/lib/wic/utils/__init__.py0
-rw-r--r--scripts/lib/wic/utils/errors.py29
-rw-r--r--scripts/lib/wic/utils/fs_related.py84
-rw-r--r--scripts/lib/wic/utils/misc.py58
-rw-r--r--scripts/lib/wic/utils/oe/__init__.py22
-rw-r--r--scripts/lib/wic/utils/oe/misc.py234
-rw-r--r--scripts/lib/wic/utils/partitionedfs.py362
-rw-r--r--scripts/lib/wic/utils/runner.py110
-rw-r--r--scripts/lib/wic/utils/syslinux.py58
72 files changed, 4492 insertions, 6611 deletions
diff --git a/scripts/lib/wic/3rdparty/pykickstart/__init__.py b/scripts/lib/wic/3rdparty/pykickstart/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
--- a/scripts/lib/wic/3rdparty/pykickstart/__init__.py
+++ /dev/null
diff --git a/scripts/lib/wic/3rdparty/pykickstart/base.py b/scripts/lib/wic/3rdparty/pykickstart/base.py
deleted file mode 100644
index e6c8f56f9d..0000000000
--- a/scripts/lib/wic/3rdparty/pykickstart/base.py
+++ /dev/null
@@ -1,466 +0,0 @@
-#
-# Chris Lumens <clumens@redhat.com>
-#
-# Copyright 2006, 2007, 2008 Red Hat, Inc.
-#
-# This copyrighted material is made available to anyone wishing to use, modify,
-# copy, or redistribute it subject to the terms and conditions of the GNU
-# General Public License v.2. This program is distributed in the hope that it
-# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
-# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-# See the GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along with
-# this program; if not, write to the Free Software Foundation, Inc., 51
-# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
-# trademarks that are incorporated in the source code or documentation are not
-# subject to the GNU General Public License and may only be used or replicated
-# with the express permission of Red Hat, Inc.
-#
-"""
-Base classes for creating commands and syntax version object.
-
-This module exports several important base classes:
-
- BaseData - The base abstract class for all data objects. Data objects
- are contained within a BaseHandler object.
-
- BaseHandler - The base abstract class from which versioned kickstart
- handler are derived. Subclasses of BaseHandler hold
- BaseData and KickstartCommand objects.
-
- DeprecatedCommand - An abstract subclass of KickstartCommand that should
- be further subclassed by users of this module. When
- a subclass is used, a warning message will be
- printed.
-
- KickstartCommand - The base abstract class for all kickstart commands.
- Command objects are contained within a BaseHandler
- object.
-"""
-import gettext
-gettext.textdomain("pykickstart")
-_ = lambda x: gettext.ldgettext("pykickstart", x)
-
-import types
-import warnings
-from pykickstart.errors import *
-from pykickstart.ko import *
-from pykickstart.parser import Packages
-from pykickstart.version import versionToString
-
-###
-### COMMANDS
-###
-class KickstartCommand(KickstartObject):
- """The base class for all kickstart commands. This is an abstract class."""
- removedKeywords = []
- removedAttrs = []
-
- def __init__(self, writePriority=0, *args, **kwargs):
- """Create a new KickstartCommand instance. This method must be
- provided by all subclasses, but subclasses must call
- KickstartCommand.__init__ first. Instance attributes:
-
- currentCmd -- The name of the command in the input file that
- caused this handler to be run.
- currentLine -- The current unprocessed line from the input file
- that caused this handler to be run.
- handler -- A reference to the BaseHandler subclass this
- command is contained withing. This is needed to
- allow referencing of Data objects.
- lineno -- The current line number in the input file.
- writePriority -- An integer specifying when this command should be
- printed when iterating over all commands' __str__
- methods. The higher the number, the later this
- command will be written. All commands with the
- same priority will be written alphabetically.
- """
-
- # We don't want people using this class by itself.
- if self.__class__ is KickstartCommand:
- raise TypeError, "KickstartCommand is an abstract class."
-
- KickstartObject.__init__(self, *args, **kwargs)
-
- self.writePriority = writePriority
-
- # These will be set by the dispatcher.
- self.currentCmd = ""
- self.currentLine = ""
- self.handler = None
- self.lineno = 0
-
- # If a subclass provides a removedKeywords list, remove all the
- # members from the kwargs list before we start processing it. This
- # ensures that subclasses don't continue to recognize arguments that
- # were removed.
- for arg in filter(kwargs.has_key, self.removedKeywords):
- kwargs.pop(arg)
-
- def __call__(self, *args, **kwargs):
- """Set multiple attributes on a subclass of KickstartCommand at once
- via keyword arguments. Valid attributes are anything specified in
- a subclass, but unknown attributes will be ignored.
- """
- for (key, val) in kwargs.items():
- # Ignore setting attributes that were removed in a subclass, as
- # if they were unknown attributes.
- if key in self.removedAttrs:
- continue
-
- if hasattr(self, key):
- setattr(self, key, val)
-
- def __str__(self):
- """Return a string formatted for output to a kickstart file. This
- method must be provided by all subclasses.
- """
- return KickstartObject.__str__(self)
-
- def parse(self, args):
- """Parse the list of args and set data on the KickstartCommand object.
- This method must be provided by all subclasses.
- """
- raise TypeError, "parse() not implemented for KickstartCommand"
-
- def apply(self, instroot="/"):
- """Write out the configuration related to the KickstartCommand object.
- Subclasses which do not provide this method will not have their
- configuration written out.
- """
- return
-
- def dataList(self):
- """For commands that can occur multiple times in a single kickstart
- file (like network, part, etc.), return the list that we should
- append more data objects to.
- """
- return None
-
- def deleteRemovedAttrs(self):
- """Remove all attributes from self that are given in the removedAttrs
- list. This method should be called from __init__ in a subclass,
- but only after the superclass's __init__ method has been called.
- """
- for attr in filter(lambda k: hasattr(self, k), self.removedAttrs):
- delattr(self, attr)
-
- # Set the contents of the opts object (an instance of optparse.Values
- # returned by parse_args) as attributes on the KickstartCommand object.
- # It's useful to call this from KickstartCommand subclasses after parsing
- # the arguments.
- def _setToSelf(self, optParser, opts):
- self._setToObj(optParser, opts, self)
-
- # Sets the contents of the opts object (an instance of optparse.Values
- # returned by parse_args) as attributes on the provided object obj. It's
- # useful to call this from KickstartCommand subclasses that handle lists
- # of objects (like partitions, network devices, etc.) and need to populate
- # a Data object.
- def _setToObj(self, optParser, opts, obj):
- for key in filter (lambda k: getattr(opts, k) != None, optParser.keys()):
- setattr(obj, key, getattr(opts, key))
-
-class DeprecatedCommand(KickstartCommand):
- """Specify that a command is deprecated and no longer has any function.
- Any command that is deprecated should be subclassed from this class,
- only specifying an __init__ method that calls the superclass's __init__.
- This is an abstract class.
- """
- def __init__(self, writePriority=None, *args, **kwargs):
- # We don't want people using this class by itself.
- if self.__class__ is KickstartCommand:
- raise TypeError, "DeprecatedCommand is an abstract class."
-
- # Create a new DeprecatedCommand instance.
- KickstartCommand.__init__(self, writePriority, *args, **kwargs)
-
- def __str__(self):
- """Placeholder since DeprecatedCommands don't work anymore."""
- return ""
-
- def parse(self, args):
- """Print a warning message if the command is seen in the input file."""
- mapping = {"lineno": self.lineno, "cmd": self.currentCmd}
- warnings.warn(_("Ignoring deprecated command on line %(lineno)s: The %(cmd)s command has been deprecated and no longer has any effect. It may be removed from future releases, which will result in a fatal error from kickstart. Please modify your kickstart file to remove this command.") % mapping, DeprecationWarning)
-
-
-###
-### HANDLERS
-###
-class BaseHandler(KickstartObject):
- """Each version of kickstart syntax is provided by a subclass of this
- class. These subclasses are what users will interact with for parsing,
- extracting data, and writing out kickstart files. This is an abstract
- class.
-
- version -- The version this syntax handler supports. This is set by
- a class attribute of a BaseHandler subclass and is used to
- set up the command dict. It is for read-only use.
- """
- version = None
-
- def __init__(self, mapping=None, dataMapping=None, commandUpdates=None,
- dataUpdates=None, *args, **kwargs):
- """Create a new BaseHandler instance. This method must be provided by
- all subclasses, but subclasses must call BaseHandler.__init__ first.
-
- mapping -- A custom map from command strings to classes,
- useful when creating your own handler with
- special command objects. It is otherwise unused
- and rarely needed. If you give this argument,
- the mapping takes the place of the default one
- and so must include all commands you want
- recognized.
- dataMapping -- This is the same as mapping, but for data
- objects. All the same comments apply.
- commandUpdates -- This is similar to mapping, but does not take
- the place of the defaults entirely. Instead,
- this mapping is applied after the defaults and
- updates it with just the commands you want to
- modify.
- dataUpdates -- This is the same as commandUpdates, but for
- data objects.
-
-
- Instance attributes:
-
- commands -- A mapping from a string command to a KickstartCommand
- subclass object that handles it. Multiple strings can
- map to the same object, but only one instance of the
- command object should ever exist. Most users should
- never have to deal with this directly, as it is
- manipulated internally and called through dispatcher.
- currentLine -- The current unprocessed line from the input file
- that caused this handler to be run.
- packages -- An instance of pykickstart.parser.Packages which
- describes the packages section of the input file.
- platform -- A string describing the hardware platform, which is
- needed only by system-config-kickstart.
- scripts -- A list of pykickstart.parser.Script instances, which is
- populated by KickstartParser.addScript and describes the
- %pre/%post/%traceback script section of the input file.
- """
-
- # We don't want people using this class by itself.
- if self.__class__ is BaseHandler:
- raise TypeError, "BaseHandler is an abstract class."
-
- KickstartObject.__init__(self, *args, **kwargs)
-
- # This isn't really a good place for these, but it's better than
- # everything else I can think of.
- self.scripts = []
- self.packages = Packages()
- self.platform = ""
-
- # These will be set by the dispatcher.
- self.commands = {}
- self.currentLine = 0
-
- # A dict keyed by an integer priority number, with each value being a
- # list of KickstartCommand subclasses. This dict is maintained by
- # registerCommand and used in __str__. No one else should be touching
- # it.
- self._writeOrder = {}
-
- self._registerCommands(mapping, dataMapping, commandUpdates, dataUpdates)
-
- def __str__(self):
- """Return a string formatted for output to a kickstart file."""
- retval = ""
-
- if self.platform != "":
- retval += "#platform=%s\n" % self.platform
-
- retval += "#version=%s\n" % versionToString(self.version)
-
- lst = self._writeOrder.keys()
- lst.sort()
-
- for prio in lst:
- for obj in self._writeOrder[prio]:
- retval += obj.__str__()
-
- for script in self.scripts:
- retval += script.__str__()
-
- retval += self.packages.__str__()
-
- return retval
-
- def _insertSorted(self, lst, obj):
- length = len(lst)
- i = 0
-
- while i < length:
- # If the two classes have the same name, it's because we are
- # overriding an existing class with one from a later kickstart
- # version, so remove the old one in favor of the new one.
- if obj.__class__.__name__ > lst[i].__class__.__name__:
- i += 1
- elif obj.__class__.__name__ == lst[i].__class__.__name__:
- lst[i] = obj
- return
- elif obj.__class__.__name__ < lst[i].__class__.__name__:
- break
-
- if i >= length:
- lst.append(obj)
- else:
- lst.insert(i, obj)
-
- def _setCommand(self, cmdObj):
- # Add an attribute on this version object. We need this to provide a
- # way for clients to access the command objects. We also need to strip
- # off the version part from the front of the name.
- if cmdObj.__class__.__name__.find("_") != -1:
- name = unicode(cmdObj.__class__.__name__.split("_", 1)[1])
- else:
- name = unicode(cmdObj.__class__.__name__).lower()
-
- setattr(self, name.lower(), cmdObj)
-
- # Also, add the object into the _writeOrder dict in the right place.
- if cmdObj.writePriority is not None:
- if self._writeOrder.has_key(cmdObj.writePriority):
- self._insertSorted(self._writeOrder[cmdObj.writePriority], cmdObj)
- else:
- self._writeOrder[cmdObj.writePriority] = [cmdObj]
-
- def _registerCommands(self, mapping=None, dataMapping=None, commandUpdates=None,
- dataUpdates=None):
- if mapping == {} or mapping == None:
- from pykickstart.handlers.control import commandMap
- cMap = commandMap[self.version]
- else:
- cMap = mapping
-
- if dataMapping == {} or dataMapping == None:
- from pykickstart.handlers.control import dataMap
- dMap = dataMap[self.version]
- else:
- dMap = dataMapping
-
- if type(commandUpdates) == types.DictType:
- cMap.update(commandUpdates)
-
- if type(dataUpdates) == types.DictType:
- dMap.update(dataUpdates)
-
- for (cmdName, cmdClass) in cMap.iteritems():
- # First make sure we haven't instantiated this command handler
- # already. If we have, we just need to make another mapping to
- # it in self.commands.
- cmdObj = None
-
- for (key, val) in self.commands.iteritems():
- if val.__class__.__name__ == cmdClass.__name__:
- cmdObj = val
- break
-
- # If we didn't find an instance in self.commands, create one now.
- if cmdObj == None:
- cmdObj = cmdClass()
- self._setCommand(cmdObj)
-
- # Finally, add the mapping to the commands dict.
- self.commands[cmdName] = cmdObj
- self.commands[cmdName].handler = self
-
- # We also need to create attributes for the various data objects.
- # No checks here because dMap is a bijection. At least, that's what
- # the comment says. Hope no one screws that up.
- for (dataName, dataClass) in dMap.iteritems():
- setattr(self, dataName, dataClass)
-
- def dispatcher(self, args, lineno):
- """Call the appropriate KickstartCommand handler for the current line
- in the kickstart file. A handler for the current command should
- be registered, though a handler of None is not an error. Returns
- the data object returned by KickstartCommand.parse.
-
- args -- A list of arguments to the current command
- lineno -- The line number in the file, for error reporting
- """
- cmd = args[0]
-
- if not self.commands.has_key(cmd):
- raise KickstartParseError, formatErrorMsg(lineno, msg=_("Unknown command: %s" % cmd))
- elif self.commands[cmd] != None:
- self.commands[cmd].currentCmd = cmd
- self.commands[cmd].currentLine = self.currentLine
- self.commands[cmd].lineno = lineno
-
- # The parser returns the data object that was modified. This could
- # be a BaseData subclass that should be put into a list, or it
- # could be the command handler object itself.
- obj = self.commands[cmd].parse(args[1:])
- lst = self.commands[cmd].dataList()
- if lst is not None:
- lst.append(obj)
-
- return obj
-
- def maskAllExcept(self, lst):
- """Set all entries in the commands dict to None, except the ones in
- the lst. All other commands will not be processed.
- """
- self._writeOrder = {}
-
- for (key, val) in self.commands.iteritems():
- if not key in lst:
- self.commands[key] = None
-
- def hasCommand(self, cmd):
- """Return true if there is a handler for the string cmd."""
- return hasattr(self, cmd)
-
-
-###
-### DATA
-###
-class BaseData(KickstartObject):
- """The base class for all data objects. This is an abstract class."""
- removedKeywords = []
- removedAttrs = []
-
- def __init__(self, *args, **kwargs):
- """Create a new BaseData instance.
-
- lineno -- Line number in the ks-file where this object was defined
- """
-
- # We don't want people using this class by itself.
- if self.__class__ is BaseData:
- raise TypeError, "BaseData is an abstract class."
-
- KickstartObject.__init__(self, *args, **kwargs)
- self.lineno = 0
-
- def __str__(self):
- """Return a string formatted for output to a kickstart file."""
- return ""
-
- def __call__(self, *args, **kwargs):
- """Set multiple attributes on a subclass of BaseData at once via
- keyword arguments. Valid attributes are anything specified in a
- subclass, but unknown attributes will be ignored.
- """
- for (key, val) in kwargs.items():
- # Ignore setting attributes that were removed in a subclass, as
- # if they were unknown attributes.
- if key in self.removedAttrs:
- continue
-
- if hasattr(self, key):
- setattr(self, key, val)
-
- def deleteRemovedAttrs(self):
- """Remove all attributes from self that are given in the removedAttrs
- list. This method should be called from __init__ in a subclass,
- but only after the superclass's __init__ method has been called.
- """
- for attr in filter(lambda k: hasattr(self, k), self.removedAttrs):
- delattr(self, attr)
diff --git a/scripts/lib/wic/3rdparty/pykickstart/commands/__init__.py b/scripts/lib/wic/3rdparty/pykickstart/commands/__init__.py
deleted file mode 100644
index 2d94550935..0000000000
--- a/scripts/lib/wic/3rdparty/pykickstart/commands/__init__.py
+++ /dev/null
@@ -1,20 +0,0 @@
-#
-# Chris Lumens <clumens@redhat.com>
-#
-# Copyright 2009 Red Hat, Inc.
-#
-# This copyrighted material is made available to anyone wishing to use, modify,
-# copy, or redistribute it subject to the terms and conditions of the GNU
-# General Public License v.2. This program is distributed in the hope that it
-# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
-# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-# See the GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along with
-# this program; if not, write to the Free Software Foundation, Inc., 51
-# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
-# trademarks that are incorporated in the source code or documentation are not
-# subject to the GNU General Public License and may only be used or replicated
-# with the express permission of Red Hat, Inc.
-#
-import bootloader, partition
diff --git a/scripts/lib/wic/3rdparty/pykickstart/commands/bootloader.py b/scripts/lib/wic/3rdparty/pykickstart/commands/bootloader.py
deleted file mode 100644
index c2b552f689..0000000000
--- a/scripts/lib/wic/3rdparty/pykickstart/commands/bootloader.py
+++ /dev/null
@@ -1,216 +0,0 @@
-#
-# Chris Lumens <clumens@redhat.com>
-#
-# Copyright 2007 Red Hat, Inc.
-#
-# This copyrighted material is made available to anyone wishing to use, modify,
-# copy, or redistribute it subject to the terms and conditions of the GNU
-# General Public License v.2. This program is distributed in the hope that it
-# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
-# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-# See the GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along with
-# this program; if not, write to the Free Software Foundation, Inc., 51
-# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
-# trademarks that are incorporated in the source code or documentation are not
-# subject to the GNU General Public License and may only be used or replicated
-# with the express permission of Red Hat, Inc.
-#
-from pykickstart.base import *
-from pykickstart.options import *
-
-class FC3_Bootloader(KickstartCommand):
- removedKeywords = KickstartCommand.removedKeywords
- removedAttrs = KickstartCommand.removedAttrs
-
- def __init__(self, writePriority=10, *args, **kwargs):
- KickstartCommand.__init__(self, writePriority, *args, **kwargs)
- self.op = self._getParser()
-
- self.driveorder = kwargs.get("driveorder", [])
- self.appendLine = kwargs.get("appendLine", "")
- self.forceLBA = kwargs.get("forceLBA", False)
- self.linear = kwargs.get("linear", True)
- self.location = kwargs.get("location", "")
- self.md5pass = kwargs.get("md5pass", "")
- self.password = kwargs.get("password", "")
- self.upgrade = kwargs.get("upgrade", False)
- self.useLilo = kwargs.get("useLilo", False)
-
- self.deleteRemovedAttrs()
-
- def _getArgsAsStr(self):
- retval = ""
-
- if self.appendLine != "":
- retval += " --append=\"%s\"" % self.appendLine
- if self.linear:
- retval += " --linear"
- if self.location:
- retval += " --location=%s" % self.location
- if hasattr(self, "forceLBA") and self.forceLBA:
- retval += " --lba32"
- if self.password != "":
- retval += " --password=\"%s\"" % self.password
- if self.md5pass != "":
- retval += " --md5pass=\"%s\"" % self.md5pass
- if self.upgrade:
- retval += " --upgrade"
- if self.useLilo:
- retval += " --useLilo"
- if len(self.driveorder) > 0:
- retval += " --driveorder=\"%s\"" % ",".join(self.driveorder)
-
- return retval
-
- def __str__(self):
- retval = KickstartCommand.__str__(self)
-
- if self.location != "":
- retval += "# System bootloader configuration\nbootloader"
- retval += self._getArgsAsStr() + "\n"
-
- return retval
-
- def _getParser(self):
- def driveorder_cb (option, opt_str, value, parser):
- for d in value.split(','):
- parser.values.ensure_value(option.dest, []).append(d)
-
- op = KSOptionParser()
- op.add_option("--append", dest="appendLine")
- op.add_option("--linear", dest="linear", action="store_true",
- default=True)
- op.add_option("--nolinear", dest="linear", action="store_false")
- op.add_option("--location", dest="location", type="choice",
- default="mbr",
- choices=["mbr", "partition", "none", "boot"])
- op.add_option("--lba32", dest="forceLBA", action="store_true",
- default=False)
- op.add_option("--password", dest="password", default="")
- op.add_option("--md5pass", dest="md5pass", default="")
- op.add_option("--upgrade", dest="upgrade", action="store_true",
- default=False)
- op.add_option("--useLilo", dest="useLilo", action="store_true",
- default=False)
- op.add_option("--driveorder", dest="driveorder", action="callback",
- callback=driveorder_cb, nargs=1, type="string")
- return op
-
- def parse(self, args):
- (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
- self._setToSelf(self.op, opts)
-
- if self.currentCmd == "lilo":
- self.useLilo = True
-
- return self
-
-class FC4_Bootloader(FC3_Bootloader):
- removedKeywords = FC3_Bootloader.removedKeywords + ["linear", "useLilo"]
- removedAttrs = FC3_Bootloader.removedAttrs + ["linear", "useLilo"]
-
- def __init__(self, writePriority=10, *args, **kwargs):
- FC3_Bootloader.__init__(self, writePriority, *args, **kwargs)
-
- def _getArgsAsStr(self):
- retval = ""
- if self.appendLine != "":
- retval += " --append=\"%s\"" % self.appendLine
- if self.location:
- retval += " --location=%s" % self.location
- if hasattr(self, "forceLBA") and self.forceLBA:
- retval += " --lba32"
- if self.password != "":
- retval += " --password=\"%s\"" % self.password
- if self.md5pass != "":
- retval += " --md5pass=\"%s\"" % self.md5pass
- if self.upgrade:
- retval += " --upgrade"
- if len(self.driveorder) > 0:
- retval += " --driveorder=\"%s\"" % ",".join(self.driveorder)
- return retval
-
- def _getParser(self):
- op = FC3_Bootloader._getParser(self)
- op.remove_option("--linear")
- op.remove_option("--nolinear")
- op.remove_option("--useLilo")
- return op
-
- def parse(self, args):
- (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
- self._setToSelf(self.op, opts)
- return self
-
-class F8_Bootloader(FC4_Bootloader):
- removedKeywords = FC4_Bootloader.removedKeywords
- removedAttrs = FC4_Bootloader.removedAttrs
-
- def __init__(self, writePriority=10, *args, **kwargs):
- FC4_Bootloader.__init__(self, writePriority, *args, **kwargs)
-
- self.timeout = kwargs.get("timeout", None)
- self.default = kwargs.get("default", "")
-
- def _getArgsAsStr(self):
- ret = FC4_Bootloader._getArgsAsStr(self)
-
- if self.timeout is not None:
- ret += " --timeout=%d" %(self.timeout,)
- if self.default:
- ret += " --default=%s" %(self.default,)
-
- return ret
-
- def _getParser(self):
- op = FC4_Bootloader._getParser(self)
- op.add_option("--timeout", dest="timeout", type="int")
- op.add_option("--default", dest="default")
- return op
-
-class F12_Bootloader(F8_Bootloader):
- removedKeywords = F8_Bootloader.removedKeywords
- removedAttrs = F8_Bootloader.removedAttrs
-
- def _getParser(self):
- op = F8_Bootloader._getParser(self)
- op.add_option("--lba32", dest="forceLBA", deprecated=1, action="store_true")
- return op
-
-class F14_Bootloader(F12_Bootloader):
- removedKeywords = F12_Bootloader.removedKeywords + ["forceLBA"]
- removedAttrs = F12_Bootloader.removedKeywords + ["forceLBA"]
-
- def _getParser(self):
- op = F12_Bootloader._getParser(self)
- op.remove_option("--lba32")
- return op
-
-class F15_Bootloader(F14_Bootloader):
- removedKeywords = F14_Bootloader.removedKeywords
- removedAttrs = F14_Bootloader.removedAttrs
-
- def __init__(self, writePriority=10, *args, **kwargs):
- F14_Bootloader.__init__(self, writePriority, *args, **kwargs)
-
- self.isCrypted = kwargs.get("isCrypted", False)
-
- def _getArgsAsStr(self):
- ret = F14_Bootloader._getArgsAsStr(self)
-
- if self.isCrypted:
- ret += " --iscrypted"
-
- return ret
-
- def _getParser(self):
- def password_cb(option, opt_str, value, parser):
- parser.values.isCrypted = True
- parser.values.password = value
-
- op = F14_Bootloader._getParser(self)
- op.add_option("--iscrypted", dest="isCrypted", action="store_true", default=False)
- op.add_option("--md5pass", action="callback", callback=password_cb, nargs=1, type="string")
- return op
diff --git a/scripts/lib/wic/3rdparty/pykickstart/commands/partition.py b/scripts/lib/wic/3rdparty/pykickstart/commands/partition.py
deleted file mode 100644
index b564b1a7ab..0000000000
--- a/scripts/lib/wic/3rdparty/pykickstart/commands/partition.py
+++ /dev/null
@@ -1,314 +0,0 @@
-#
-# Chris Lumens <clumens@redhat.com>
-#
-# Copyright 2005, 2006, 2007, 2008 Red Hat, Inc.
-#
-# This copyrighted material is made available to anyone wishing to use, modify,
-# copy, or redistribute it subject to the terms and conditions of the GNU
-# General Public License v.2. This program is distributed in the hope that it
-# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
-# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-# See the GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along with
-# this program; if not, write to the Free Software Foundation, Inc., 51
-# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
-# trademarks that are incorporated in the source code or documentation are not
-# subject to the GNU General Public License and may only be used or replicated
-# with the express permission of Red Hat, Inc.
-#
-from pykickstart.base import *
-from pykickstart.errors import *
-from pykickstart.options import *
-
-import gettext
-import warnings
-_ = lambda x: gettext.ldgettext("pykickstart", x)
-
-class FC3_PartData(BaseData):
- removedKeywords = BaseData.removedKeywords
- removedAttrs = BaseData.removedAttrs
-
- def __init__(self, *args, **kwargs):
- BaseData.__init__(self, *args, **kwargs)
- self.active = kwargs.get("active", False)
- self.primOnly = kwargs.get("primOnly", False)
- self.end = kwargs.get("end", 0)
- self.fstype = kwargs.get("fstype", "")
- self.grow = kwargs.get("grow", False)
- self.maxSizeMB = kwargs.get("maxSizeMB", 0)
- self.format = kwargs.get("format", True)
- self.onbiosdisk = kwargs.get("onbiosdisk", "")
- self.disk = kwargs.get("disk", "")
- self.onPart = kwargs.get("onPart", "")
- self.recommended = kwargs.get("recommended", False)
- self.size = kwargs.get("size", None)
- self.start = kwargs.get("start", 0)
- self.mountpoint = kwargs.get("mountpoint", "")
-
- def __eq__(self, y):
- if self.mountpoint:
- return self.mountpoint == y.mountpoint
- else:
- return False
-
- def _getArgsAsStr(self):
- retval = ""
-
- if self.active:
- retval += " --active"
- if self.primOnly:
- retval += " --asprimary"
- if hasattr(self, "end") and self.end != 0:
- retval += " --end=%s" % self.end
- if self.fstype != "":
- retval += " --fstype=\"%s\"" % self.fstype
- if self.grow:
- retval += " --grow"
- if self.maxSizeMB > 0:
- retval += " --maxsize=%d" % self.maxSizeMB
- if not self.format:
- retval += " --noformat"
- if self.onbiosdisk != "":
- retval += " --onbiosdisk=%s" % self.onbiosdisk
- if self.disk != "":
- retval += " --ondisk=%s" % self.disk
- if self.onPart != "":
- retval += " --onpart=%s" % self.onPart
- if self.recommended:
- retval += " --recommended"
- if self.size and self.size != 0:
- retval += " --size=%sk" % self.size
- if hasattr(self, "start") and self.start != 0:
- retval += " --start=%s" % self.start
-
- return retval
-
- def __str__(self):
- retval = BaseData.__str__(self)
- if self.mountpoint:
- mountpoint_str = "%s" % self.mountpoint
- else:
- mountpoint_str = "(No mount point)"
- retval += "part %s%s\n" % (mountpoint_str, self._getArgsAsStr())
- return retval
-
-class FC4_PartData(FC3_PartData):
- removedKeywords = FC3_PartData.removedKeywords
- removedAttrs = FC3_PartData.removedAttrs
-
- def __init__(self, *args, **kwargs):
- FC3_PartData.__init__(self, *args, **kwargs)
- self.bytesPerInode = kwargs.get("bytesPerInode", 4096)
- self.fsopts = kwargs.get("fsopts", "")
- self.label = kwargs.get("label", "")
-
- def _getArgsAsStr(self):
- retval = FC3_PartData._getArgsAsStr(self)
-
- if hasattr(self, "bytesPerInode") and self.bytesPerInode != 0:
- retval += " --bytes-per-inode=%d" % self.bytesPerInode
- if self.fsopts != "":
- retval += " --fsoptions=\"%s\"" % self.fsopts
- if self.label != "":
- retval += " --label=%s" % self.label
-
- return retval
-
-class F9_PartData(FC4_PartData):
- removedKeywords = FC4_PartData.removedKeywords + ["bytesPerInode"]
- removedAttrs = FC4_PartData.removedAttrs + ["bytesPerInode"]
-
- def __init__(self, *args, **kwargs):
- FC4_PartData.__init__(self, *args, **kwargs)
- self.deleteRemovedAttrs()
-
- self.fsopts = kwargs.get("fsopts", "")
- self.label = kwargs.get("label", "")
- self.fsprofile = kwargs.get("fsprofile", "")
- self.encrypted = kwargs.get("encrypted", False)
- self.passphrase = kwargs.get("passphrase", "")
-
- def _getArgsAsStr(self):
- retval = FC4_PartData._getArgsAsStr(self)
-
- if self.fsprofile != "":
- retval += " --fsprofile=\"%s\"" % self.fsprofile
- if self.encrypted:
- retval += " --encrypted"
-
- if self.passphrase != "":
- retval += " --passphrase=\"%s\"" % self.passphrase
-
- return retval
-
-class F11_PartData(F9_PartData):
- removedKeywords = F9_PartData.removedKeywords + ["start", "end"]
- removedAttrs = F9_PartData.removedAttrs + ["start", "end"]
-
-class F12_PartData(F11_PartData):
- removedKeywords = F11_PartData.removedKeywords
- removedAttrs = F11_PartData.removedAttrs
-
- def __init__(self, *args, **kwargs):
- F11_PartData.__init__(self, *args, **kwargs)
-
- self.escrowcert = kwargs.get("escrowcert", "")
- self.backuppassphrase = kwargs.get("backuppassphrase", False)
-
- def _getArgsAsStr(self):
- retval = F11_PartData._getArgsAsStr(self)
-
- if self.encrypted and self.escrowcert != "":
- retval += " --escrowcert=\"%s\"" % self.escrowcert
-
- if self.backuppassphrase:
- retval += " --backuppassphrase"
-
- return retval
-
-F14_PartData = F12_PartData
-
-class FC3_Partition(KickstartCommand):
- removedKeywords = KickstartCommand.removedKeywords
- removedAttrs = KickstartCommand.removedAttrs
-
- def __init__(self, writePriority=130, *args, **kwargs):
- KickstartCommand.__init__(self, writePriority, *args, **kwargs)
- self.op = self._getParser()
-
- self.partitions = kwargs.get("partitions", [])
-
- def __str__(self):
- retval = ""
-
- for part in self.partitions:
- retval += part.__str__()
-
- if retval != "":
- return "# Disk partitioning information\n" + retval
- else:
- return ""
-
- def _getParser(self):
- def part_cb (option, opt_str, value, parser):
- if value.startswith("/dev/"):
- parser.values.ensure_value(option.dest, value[5:])
- else:
- parser.values.ensure_value(option.dest, value)
-
- op = KSOptionParser()
- op.add_option("--active", dest="active", action="store_true",
- default=False)
- op.add_option("--asprimary", dest="primOnly", action="store_true",
- default=False)
- op.add_option("--end", dest="end", action="store", type="int",
- nargs=1)
- op.add_option("--fstype", "--type", dest="fstype")
- op.add_option("--grow", dest="grow", action="store_true", default=False)
- op.add_option("--maxsize", dest="maxSizeMB", action="store", type="int",
- nargs=1)
- op.add_option("--noformat", dest="format", action="store_false",
- default=True)
- op.add_option("--onbiosdisk", dest="onbiosdisk")
- op.add_option("--ondisk", "--ondrive", dest="disk")
- op.add_option("--onpart", "--usepart", dest="onPart", action="callback",
- callback=part_cb, nargs=1, type="string")
- op.add_option("--recommended", dest="recommended", action="store_true",
- default=False)
- op.add_option("--size", dest="size", action="store", type="size",
- nargs=1)
- op.add_option("--start", dest="start", action="store", type="int",
- nargs=1)
- return op
-
- def parse(self, args):
- (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)
-
- pd = self.handler.PartData()
- self._setToObj(self.op, opts, pd)
- pd.lineno = self.lineno
- if extra:
- pd.mountpoint = extra[0]
- if pd in self.dataList():
- warnings.warn(_("A partition with the mountpoint %s has already been defined.") % pd.mountpoint)
- else:
- pd.mountpoint = None
-
- return pd
-
- def dataList(self):
- return self.partitions
-
-class FC4_Partition(FC3_Partition):
- removedKeywords = FC3_Partition.removedKeywords
- removedAttrs = FC3_Partition.removedAttrs
-
- def __init__(self, writePriority=130, *args, **kwargs):
- FC3_Partition.__init__(self, writePriority, *args, **kwargs)
-
- def part_cb (option, opt_str, value, parser):
- if value.startswith("/dev/"):
- parser.values.ensure_value(option.dest, value[5:])
- else:
- parser.values.ensure_value(option.dest, value)
-
- def _getParser(self):
- op = FC3_Partition._getParser(self)
- op.add_option("--bytes-per-inode", dest="bytesPerInode", action="store",
- type="int", nargs=1)
- op.add_option("--fsoptions", dest="fsopts")
- op.add_option("--label", dest="label")
- return op
-
-class F9_Partition(FC4_Partition):
- removedKeywords = FC4_Partition.removedKeywords
- removedAttrs = FC4_Partition.removedAttrs
-
- def __init__(self, writePriority=130, *args, **kwargs):
- FC4_Partition.__init__(self, writePriority, *args, **kwargs)
-
- def part_cb (option, opt_str, value, parser):
- if value.startswith("/dev/"):
- parser.values.ensure_value(option.dest, value[5:])
- else:
- parser.values.ensure_value(option.dest, value)
-
- def _getParser(self):
- op = FC4_Partition._getParser(self)
- op.add_option("--bytes-per-inode", deprecated=1)
- op.add_option("--fsprofile")
- op.add_option("--encrypted", action="store_true", default=False)
- op.add_option("--passphrase")
- return op
-
-class F11_Partition(F9_Partition):
- removedKeywords = F9_Partition.removedKeywords
- removedAttrs = F9_Partition.removedAttrs
-
- def _getParser(self):
- op = F9_Partition._getParser(self)
- op.add_option("--start", deprecated=1)
- op.add_option("--end", deprecated=1)
- return op
-
-class F12_Partition(F11_Partition):
- removedKeywords = F11_Partition.removedKeywords
- removedAttrs = F11_Partition.removedAttrs
-
- def _getParser(self):
- op = F11_Partition._getParser(self)
- op.add_option("--escrowcert")
- op.add_option("--backuppassphrase", action="store_true", default=False)
- return op
-
-class F14_Partition(F12_Partition):
- removedKeywords = F12_Partition.removedKeywords
- removedAttrs = F12_Partition.removedAttrs
-
- def _getParser(self):
- op = F12_Partition._getParser(self)
- op.remove_option("--bytes-per-inode")
- op.remove_option("--start")
- op.remove_option("--end")
- return op
diff --git a/scripts/lib/wic/3rdparty/pykickstart/constants.py b/scripts/lib/wic/3rdparty/pykickstart/constants.py
deleted file mode 100644
index 5e12fc80ec..0000000000
--- a/scripts/lib/wic/3rdparty/pykickstart/constants.py
+++ /dev/null
@@ -1,57 +0,0 @@
-#
-# Chris Lumens <clumens@redhat.com>
-#
-# Copyright 2005-2007 Red Hat, Inc.
-#
-# This copyrighted material is made available to anyone wishing to use, modify,
-# copy, or redistribute it subject to the terms and conditions of the GNU
-# General Public License v.2. This program is distributed in the hope that it
-# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
-# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-# See the GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along with
-# this program; if not, write to the Free Software Foundation, Inc., 51
-# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
-# trademarks that are incorporated in the source code or documentation are not
-# subject to the GNU General Public License and may only be used or replicated
-# with the express permission of Red Hat, Inc.
-#
-CLEARPART_TYPE_LINUX = 0
-CLEARPART_TYPE_ALL = 1
-CLEARPART_TYPE_NONE = 2
-
-DISPLAY_MODE_CMDLINE = 0
-DISPLAY_MODE_GRAPHICAL = 1
-DISPLAY_MODE_TEXT = 2
-
-FIRSTBOOT_DEFAULT = 0
-FIRSTBOOT_SKIP = 1
-FIRSTBOOT_RECONFIG = 2
-
-KS_MISSING_PROMPT = 0
-KS_MISSING_IGNORE = 1
-
-SELINUX_DISABLED = 0
-SELINUX_ENFORCING = 1
-SELINUX_PERMISSIVE = 2
-
-KS_SCRIPT_PRE = 0
-KS_SCRIPT_POST = 1
-KS_SCRIPT_TRACEBACK = 2
-
-KS_WAIT = 0
-KS_REBOOT = 1
-KS_SHUTDOWN = 2
-
-KS_INSTKEY_SKIP = -99
-
-BOOTPROTO_DHCP = "dhcp"
-BOOTPROTO_BOOTP = "bootp"
-BOOTPROTO_STATIC = "static"
-BOOTPROTO_QUERY = "query"
-BOOTPROTO_IBFT = "ibft"
-
-GROUP_REQUIRED = 0
-GROUP_DEFAULT = 1
-GROUP_ALL = 2
diff --git a/scripts/lib/wic/3rdparty/pykickstart/errors.py b/scripts/lib/wic/3rdparty/pykickstart/errors.py
deleted file mode 100644
index a234d99d43..0000000000
--- a/scripts/lib/wic/3rdparty/pykickstart/errors.py
+++ /dev/null
@@ -1,103 +0,0 @@
-#
-# errors.py: Kickstart error handling.
-#
-# Chris Lumens <clumens@redhat.com>
-#
-# This copyrighted material is made available to anyone wishing to use, modify,
-# copy, or redistribute it subject to the terms and conditions of the GNU
-# General Public License v.2. This program is distributed in the hope that it
-# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
-# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-# See the GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along with
-# this program; if not, write to the Free Software Foundation, Inc., 51
-# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
-# trademarks that are incorporated in the source code or documentation are not
-# subject to the GNU General Public License and may only be used or replicated
-# with the express permission of Red Hat, Inc.
-#
-"""
-Error handling classes and functions.
-
-This module exports a single function:
-
- formatErrorMsg - Properly formats an error message.
-
-It also exports several exception classes:
-
- KickstartError - A generic exception class.
-
- KickstartParseError - An exception for errors relating to parsing.
-
- KickstartValueError - An exception for errors relating to option
- processing.
-
- KickstartVersionError - An exception for errors relating to unsupported
- syntax versions.
-"""
-import gettext
-_ = lambda x: gettext.ldgettext("pykickstart", x)
-
-def formatErrorMsg(lineno, msg=""):
- """Properly format the error message msg for inclusion in an exception."""
- if msg != "":
- mapping = {"lineno": lineno, "msg": msg}
- return _("The following problem occurred on line %(lineno)s of the kickstart file:\n\n%(msg)s\n") % mapping
- else:
- return _("There was a problem reading from line %s of the kickstart file") % lineno
-
-class KickstartError(Exception):
- """A generic exception class for unspecific error conditions."""
- def __init__(self, val = ""):
- """Create a new KickstartError exception instance with the descriptive
- message val. val should be the return value of formatErrorMsg.
- """
- Exception.__init__(self)
- self.value = val
-
- def __str__ (self):
- return self.value
-
-class KickstartParseError(KickstartError):
- """An exception class for errors when processing the input file, such as
- unknown options, commands, or sections.
- """
- def __init__(self, msg):
- """Create a new KickstartParseError exception instance with the
- descriptive message val. val should be the return value of
- formatErrorMsg.
- """
- KickstartError.__init__(self, msg)
-
- def __str__(self):
- return self.value
-
-class KickstartValueError(KickstartError):
- """An exception class for errors when processing arguments to commands,
- such as too many arguments, too few arguments, or missing required
- arguments.
- """
- def __init__(self, msg):
- """Create a new KickstartValueError exception instance with the
- descriptive message val. val should be the return value of
- formatErrorMsg.
- """
- KickstartError.__init__(self, msg)
-
- def __str__ (self):
- return self.value
-
-class KickstartVersionError(KickstartError):
- """An exception class for errors related to using an incorrect version of
- kickstart syntax.
- """
- def __init__(self, msg):
- """Create a new KickstartVersionError exception instance with the
- descriptive message val. val should be the return value of
- formatErrorMsg.
- """
- KickstartError.__init__(self, msg)
-
- def __str__ (self):
- return self.value
diff --git a/scripts/lib/wic/3rdparty/pykickstart/handlers/__init__.py b/scripts/lib/wic/3rdparty/pykickstart/handlers/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
--- a/scripts/lib/wic/3rdparty/pykickstart/handlers/__init__.py
+++ /dev/null
diff --git a/scripts/lib/wic/3rdparty/pykickstart/handlers/control.py b/scripts/lib/wic/3rdparty/pykickstart/handlers/control.py
deleted file mode 100644
index 8dc80d1ebe..0000000000
--- a/scripts/lib/wic/3rdparty/pykickstart/handlers/control.py
+++ /dev/null
@@ -1,46 +0,0 @@
-#
-# Chris Lumens <clumens@redhat.com>
-#
-# Copyright 2007, 2008, 2009, 2010 Red Hat, Inc.
-#
-# This copyrighted material is made available to anyone wishing to use, modify,
-# copy, or redistribute it subject to the terms and conditions of the GNU
-# General Public License v.2. This program is distributed in the hope that it
-# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
-# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-# See the GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along with
-# this program; if not, write to the Free Software Foundation, Inc., 51
-# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
-# trademarks that are incorporated in the source code or documentation are not
-# subject to the GNU General Public License and may only be used or replicated
-# with the express permission of Red Hat, Inc.
-#
-from pykickstart.version import *
-from pykickstart.commands import *
-
-# This map is keyed on kickstart syntax version as provided by
-# pykickstart.version. Within each sub-dict is a mapping from command name
-# to the class that handles it. This is an onto mapping - that is, multiple
-# command names can map to the same class. However, the Handler will ensure
-# that only one instance of each class ever exists.
-commandMap = {
- # based on f15
- F16: {
- "bootloader": bootloader.F15_Bootloader,
- "part": partition.F14_Partition,
- "partition": partition.F14_Partition,
- },
-}
-
-# This map is keyed on kickstart syntax version as provided by
-# pykickstart.version. Within each sub-dict is a mapping from a data object
-# name to the class that provides it. This is a bijective mapping - that is,
-# each name maps to exactly one data class and all data classes have a name.
-# More than one instance of each class is allowed to exist, however.
-dataMap = {
- F16: {
- "PartData": partition.F14_PartData,
- },
-}
diff --git a/scripts/lib/wic/3rdparty/pykickstart/handlers/f16.py b/scripts/lib/wic/3rdparty/pykickstart/handlers/f16.py
deleted file mode 100644
index 3c52f8d754..0000000000
--- a/scripts/lib/wic/3rdparty/pykickstart/handlers/f16.py
+++ /dev/null
@@ -1,24 +0,0 @@
-#
-# Chris Lumens <clumens@redhat.com>
-#
-# Copyright 2011 Red Hat, Inc.
-#
-# This copyrighted material is made available to anyone wishing to use, modify,
-# copy, or redistribute it subject to the terms and conditions of the GNU
-# General Public License v.2. This program is distributed in the hope that it
-# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
-# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-# See the GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along with
-# this program; if not, write to the Free Software Foundation, Inc., 51
-# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
-# trademarks that are incorporated in the source code or documentation are not
-# subject to the GNU General Public License and may only be used or replicated
-# with the express permission of Red Hat, Inc.
-#
-from pykickstart.base import *
-from pykickstart.version import *
-
-class F16Handler(BaseHandler):
- version = F16
diff --git a/scripts/lib/wic/3rdparty/pykickstart/ko.py b/scripts/lib/wic/3rdparty/pykickstart/ko.py
deleted file mode 100644
index 1350d19c70..0000000000
--- a/scripts/lib/wic/3rdparty/pykickstart/ko.py
+++ /dev/null
@@ -1,37 +0,0 @@
-#
-# Chris Lumens <clumens@redhat.com>
-#
-# Copyright 2009 Red Hat, Inc.
-#
-# This copyrighted material is made available to anyone wishing to use, modify,
-# copy, or redistribute it subject to the terms and conditions of the GNU
-# General Public License v.2. This program is distributed in the hope that it
-# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
-# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-# See the GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along with
-# this program; if not, write to the Free Software Foundation, Inc., 51
-# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
-# trademarks that are incorporated in the source code or documentation are not
-# subject to the GNU General Public License and may only be used or replicated
-# with the express permission of Red Hat, Inc.
-#
-"""
-Base classes for internal pykickstart use.
-
-The module exports the following important classes:
-
- KickstartObject - The base class for all classes in pykickstart
-"""
-
-class KickstartObject(object):
- """The base class for all other classes in pykickstart."""
- def __init__(self, *args, **kwargs):
- """Create a new KickstartObject instance. All other classes in
- pykickstart should be derived from this one. Instance attributes:
- """
- pass
-
- def __str__(self):
- return ""
diff --git a/scripts/lib/wic/3rdparty/pykickstart/options.py b/scripts/lib/wic/3rdparty/pykickstart/options.py
deleted file mode 100644
index ebc23eda63..0000000000
--- a/scripts/lib/wic/3rdparty/pykickstart/options.py
+++ /dev/null
@@ -1,223 +0,0 @@
-#
-# Chris Lumens <clumens@redhat.com>
-#
-# Copyright 2005, 2006, 2007 Red Hat, Inc.
-#
-# This copyrighted material is made available to anyone wishing to use, modify,
-# copy, or redistribute it subject to the terms and conditions of the GNU
-# General Public License v.2. This program is distributed in the hope that it
-# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
-# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-# See the GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along with
-# this program; if not, write to the Free Software Foundation, Inc., 51
-# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
-# trademarks that are incorporated in the source code or documentation are not
-# subject to the GNU General Public License and may only be used or replicated
-# with the express permission of Red Hat, Inc.
-#
-"""
-Specialized option handling.
-
-This module exports two classes:
-
- KSOptionParser - A specialized subclass of OptionParser to be used
- in BaseHandler subclasses.
-
- KSOption - A specialized subclass of Option.
-"""
-import warnings
-from copy import copy
-from optparse import *
-
-from constants import *
-from errors import *
-from version import *
-
-import gettext
-_ = lambda x: gettext.ldgettext("pykickstart", x)
-
-class KSOptionParser(OptionParser):
- """A specialized subclass of optparse.OptionParser to handle extra option
- attribute checking, work error reporting into the KickstartParseError
- framework, and to turn off the default help.
- """
- def exit(self, status=0, msg=None):
- pass
-
- def error(self, msg):
- if self.lineno != None:
- raise KickstartParseError, formatErrorMsg(self.lineno, msg=msg)
- else:
- raise KickstartParseError, msg
-
- def keys(self):
- retval = []
-
- for opt in self.option_list:
- if opt not in retval:
- retval.append(opt.dest)
-
- return retval
-
- def _init_parsing_state (self):
- OptionParser._init_parsing_state(self)
- self.option_seen = {}
-
- def check_values (self, values, args):
- def seen(self, option):
- return self.option_seen.has_key(option)
-
- def usedTooNew(self, option):
- return option.introduced and option.introduced > self.version
-
- def usedDeprecated(self, option):
- return option.deprecated
-
- def usedRemoved(self, option):
- return option.removed and option.removed <= self.version
-
- for option in filter(lambda o: isinstance(o, Option), self.option_list):
- if option.required and not seen(self, option):
- raise KickstartValueError, formatErrorMsg(self.lineno, _("Option %s is required") % option)
- elif seen(self, option) and usedTooNew(self, option):
- mapping = {"option": option, "intro": versionToString(option.introduced),
- "version": versionToString(self.version)}
- self.error(_("The %(option)s option was introduced in version %(intro)s, but you are using kickstart syntax version %(version)s.") % mapping)
- elif seen(self, option) and usedRemoved(self, option):
- mapping = {"option": option, "removed": versionToString(option.removed),
- "version": versionToString(self.version)}
-
- if option.removed == self.version:
- self.error(_("The %(option)s option is no longer supported.") % mapping)
- else:
- self.error(_("The %(option)s option was removed in version %(removed)s, but you are using kickstart syntax version %(version)s.") % mapping)
- elif seen(self, option) and usedDeprecated(self, option):
- mapping = {"lineno": self.lineno, "option": option}
- warnings.warn(_("Ignoring deprecated option on line %(lineno)s: The %(option)s option has been deprecated and no longer has any effect. It may be removed from future releases, which will result in a fatal error from kickstart. Please modify your kickstart file to remove this option.") % mapping, DeprecationWarning)
-
- return (values, args)
-
- def parse_args(self, *args, **kwargs):
- if kwargs.has_key("lineno"):
- self.lineno = kwargs.pop("lineno")
-
- return OptionParser.parse_args(self, **kwargs)
-
- def __init__(self, mapping=None, version=None):
- """Create a new KSOptionParser instance. Each KickstartCommand
- subclass should create one instance of KSOptionParser, providing
- at least the lineno attribute. mapping and version are not required.
- Instance attributes:
-
- mapping -- A mapping from option strings to different values.
- version -- The version of the kickstart syntax we are checking
- against.
- """
- OptionParser.__init__(self, option_class=KSOption,
- add_help_option=False,
- conflict_handler="resolve")
- if mapping is None:
- self.map = {}
- else:
- self.map = mapping
-
- self.lineno = None
- self.option_seen = {}
- self.version = version
-
-def _check_ksboolean(option, opt, value):
- if value.lower() in ("on", "yes", "true", "1"):
- return True
- elif value.lower() in ("off", "no", "false", "0"):
- return False
- else:
- mapping = {"opt": opt, "value": value}
- raise OptionValueError(_("Option %(opt)s: invalid boolean value: %(value)r") % mapping)
-
-def _check_string(option, opt, value):
- if len(value) > 2 and value.startswith("--"):
- mapping = {"opt": opt, "value": value}
- raise OptionValueError(_("Option %(opt)s: invalid string value: %(value)r") % mapping)
- else:
- return value
-
-def _check_size(option, opt, value):
- # Former default was MB
- if value.isdigit():
- return int(value) * 1024L
-
- mapping = {"opt": opt, "value": value}
- if not value[:-1].isdigit():
- raise OptionValueError(_("Option %(opt)s: invalid size value: %(value)r") % mapping)
-
- size = int(value[:-1])
- if value.endswith("k") or value.endswith("K"):
- return size
- if value.endswith("M"):
- return size * 1024L
- if value.endswith("G"):
- return size * 1024L * 1024L
- raise OptionValueError(_("Option %(opt)s: invalid size value: %(value)r") % mapping)
-
-# Creates a new Option class that supports several new attributes:
-# - required: any option with this attribute must be supplied or an exception
-# is thrown
-# - introduced: the kickstart syntax version that this option first appeared
-# in - an exception will be raised if the option is used and
-# the specified syntax version is less than the value of this
-# attribute
-# - deprecated: the kickstart syntax version that this option was deprecated
-# in - a DeprecationWarning will be thrown if the option is
-# used and the specified syntax version is greater than the
-# value of this attribute
-# - removed: the kickstart syntax version that this option was removed in - an
-# exception will be raised if the option is used and the specified
-# syntax version is greated than the value of this attribute
-# Also creates a new type:
-# - ksboolean: support various kinds of boolean values on an option
-# And two new actions:
-# - map : allows you to define an opt -> val mapping such that dest gets val
-# when opt is seen
-# - map_extend: allows you to define an opt -> [val1, ... valn] mapping such
-# that dest gets a list of vals built up when opt is seen
-class KSOption (Option):
- ATTRS = Option.ATTRS + ['introduced', 'deprecated', 'removed', 'required']
- ACTIONS = Option.ACTIONS + ("map", "map_extend",)
- STORE_ACTIONS = Option.STORE_ACTIONS + ("map", "map_extend",)
-
- TYPES = Option.TYPES + ("ksboolean", "string", "size")
- TYPE_CHECKER = copy(Option.TYPE_CHECKER)
- TYPE_CHECKER["ksboolean"] = _check_ksboolean
- TYPE_CHECKER["string"] = _check_string
- TYPE_CHECKER["size"] = _check_size
-
- def _check_required(self):
- if self.required and not self.takes_value():
- raise OptionError(_("Required flag set for option that doesn't take a value"), self)
-
- # Make sure _check_required() is called from the constructor!
- CHECK_METHODS = Option.CHECK_METHODS + [_check_required]
-
- def process (self, opt, value, values, parser):
- Option.process(self, opt, value, values, parser)
- parser.option_seen[self] = 1
-
- # Override default take_action method to handle our custom actions.
- def take_action(self, action, dest, opt, value, values, parser):
- if action == "map":
- values.ensure_value(dest, parser.map[opt.lstrip('-')])
- elif action == "map_extend":
- values.ensure_value(dest, []).extend(parser.map[opt.lstrip('-')])
- else:
- Option.take_action(self, action, dest, opt, value, values, parser)
-
- def takes_value(self):
- # Deprecated options don't take a value.
- return Option.takes_value(self) and not self.deprecated
-
- def __init__(self, *args, **kwargs):
- self.deprecated = False
- self.required = False
- Option.__init__(self, *args, **kwargs)
diff --git a/scripts/lib/wic/3rdparty/pykickstart/parser.py b/scripts/lib/wic/3rdparty/pykickstart/parser.py
deleted file mode 100644
index 9c9674bf73..0000000000
--- a/scripts/lib/wic/3rdparty/pykickstart/parser.py
+++ /dev/null
@@ -1,619 +0,0 @@
-#
-# parser.py: Kickstart file parser.
-#
-# Chris Lumens <clumens@redhat.com>
-#
-# Copyright 2005, 2006, 2007, 2008, 2011 Red Hat, Inc.
-#
-# This copyrighted material is made available to anyone wishing to use, modify,
-# copy, or redistribute it subject to the terms and conditions of the GNU
-# General Public License v.2. This program is distributed in the hope that it
-# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
-# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-# See the GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along with
-# this program; if not, write to the Free Software Foundation, Inc., 51
-# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
-# trademarks that are incorporated in the source code or documentation are not
-# subject to the GNU General Public License and may only be used or replicated
-# with the express permission of Red Hat, Inc.
-#
-"""
-Main kickstart file processing module.
-
-This module exports several important classes:
-
- Script - Representation of a single %pre, %post, or %traceback script.
-
- Packages - Representation of the %packages section.
-
- KickstartParser - The kickstart file parser state machine.
-"""
-
-from collections import Iterator
-import os
-import shlex
-import sys
-import tempfile
-from copy import copy
-from optparse import *
-
-import constants
-from errors import KickstartError, KickstartParseError, KickstartValueError, formatErrorMsg
-from ko import KickstartObject
-from sections import *
-import version
-
-import gettext
-_ = lambda x: gettext.ldgettext("pykickstart", x)
-
-STATE_END = "end"
-STATE_COMMANDS = "commands"
-
-ver = version.DEVEL
-
-
-class PutBackIterator(Iterator):
- def __init__(self, iterable):
- self._iterable = iter(iterable)
- self._buf = None
-
- def __iter__(self):
- return self
-
- def put(self, s):
- self._buf = s
-
- def next(self):
- if self._buf:
- retval = self._buf
- self._buf = None
- return retval
- else:
- return self._iterable.next()
-
-###
-### SCRIPT HANDLING
-###
-class Script(KickstartObject):
- """A class representing a single kickstart script. If functionality beyond
- just a data representation is needed (for example, a run method in
- anaconda), Script may be subclassed. Although a run method is not
- provided, most of the attributes of Script have to do with running the
- script. Instances of Script are held in a list by the Version object.
- """
- def __init__(self, script, *args , **kwargs):
- """Create a new Script instance. Instance attributes:
-
- errorOnFail -- If execution of the script fails, should anaconda
- stop, display an error, and then reboot without
- running any other scripts?
- inChroot -- Does the script execute in anaconda's chroot
- environment or not?
- interp -- The program that should be used to interpret this
- script.
- lineno -- The line number this script starts on.
- logfile -- Where all messages from the script should be logged.
- script -- A string containing all the lines of the script.
- type -- The type of the script, which can be KS_SCRIPT_* from
- pykickstart.constants.
- """
- KickstartObject.__init__(self, *args, **kwargs)
- self.script = "".join(script)
-
- self.interp = kwargs.get("interp", "/bin/sh")
- self.inChroot = kwargs.get("inChroot", False)
- self.lineno = kwargs.get("lineno", None)
- self.logfile = kwargs.get("logfile", None)
- self.errorOnFail = kwargs.get("errorOnFail", False)
- self.type = kwargs.get("type", constants.KS_SCRIPT_PRE)
-
- def __str__(self):
- """Return a string formatted for output to a kickstart file."""
- retval = ""
-
- if self.type == constants.KS_SCRIPT_PRE:
- retval += '\n%pre'
- elif self.type == constants.KS_SCRIPT_POST:
- retval += '\n%post'
- elif self.type == constants.KS_SCRIPT_TRACEBACK:
- retval += '\n%traceback'
-
- if self.interp != "/bin/sh" and self.interp != "":
- retval += " --interpreter=%s" % self.interp
- if self.type == constants.KS_SCRIPT_POST and not self.inChroot:
- retval += " --nochroot"
- if self.logfile != None:
- retval += " --logfile %s" % self.logfile
- if self.errorOnFail:
- retval += " --erroronfail"
-
- if self.script.endswith("\n"):
- if ver >= version.F8:
- return retval + "\n%s%%end\n" % self.script
- else:
- return retval + "\n%s\n" % self.script
- else:
- if ver >= version.F8:
- return retval + "\n%s\n%%end\n" % self.script
- else:
- return retval + "\n%s\n" % self.script
-
-
-##
-## PACKAGE HANDLING
-##
-class Group:
- """A class representing a single group in the %packages section."""
- def __init__(self, name="", include=constants.GROUP_DEFAULT):
- """Create a new Group instance. Instance attributes:
-
- name -- The group's identifier
- include -- The level of how much of the group should be included.
- Values can be GROUP_* from pykickstart.constants.
- """
- self.name = name
- self.include = include
-
- def __str__(self):
- """Return a string formatted for output to a kickstart file."""
- if self.include == constants.GROUP_REQUIRED:
- return "@%s --nodefaults" % self.name
- elif self.include == constants.GROUP_ALL:
- return "@%s --optional" % self.name
- else:
- return "@%s" % self.name
-
- def __cmp__(self, other):
- if self.name < other.name:
- return -1
- elif self.name > other.name:
- return 1
- return 0
-
-class Packages(KickstartObject):
- """A class representing the %packages section of the kickstart file."""
- def __init__(self, *args, **kwargs):
- """Create a new Packages instance. Instance attributes:
-
- addBase -- Should the Base group be installed even if it is
- not specified?
- default -- Should the default package set be selected?
- excludedList -- A list of all the packages marked for exclusion in
- the %packages section, without the leading minus
- symbol.
- excludeDocs -- Should documentation in each package be excluded?
- groupList -- A list of Group objects representing all the groups
- specified in the %packages section. Names will be
- stripped of the leading @ symbol.
- excludedGroupList -- A list of Group objects representing all the
- groups specified for removal in the %packages
- section. Names will be stripped of the leading
- -@ symbols.
- handleMissing -- If unknown packages are specified in the %packages
- section, should it be ignored or not? Values can
- be KS_MISSING_* from pykickstart.constants.
- packageList -- A list of all the packages specified in the
- %packages section.
- instLangs -- A list of languages to install.
- """
- KickstartObject.__init__(self, *args, **kwargs)
-
- self.addBase = True
- self.default = False
- self.excludedList = []
- self.excludedGroupList = []
- self.excludeDocs = False
- self.groupList = []
- self.handleMissing = constants.KS_MISSING_PROMPT
- self.packageList = []
- self.instLangs = None
-
- def __str__(self):
- """Return a string formatted for output to a kickstart file."""
- pkgs = ""
-
- if not self.default:
- grps = self.groupList
- grps.sort()
- for grp in grps:
- pkgs += "%s\n" % grp.__str__()
-
- p = self.packageList
- p.sort()
- for pkg in p:
- pkgs += "%s\n" % pkg
-
- grps = self.excludedGroupList
- grps.sort()
- for grp in grps:
- pkgs += "-%s\n" % grp.__str__()
-
- p = self.excludedList
- p.sort()
- for pkg in p:
- pkgs += "-%s\n" % pkg
-
- if pkgs == "":
- return ""
-
- retval = "\n%packages"
-
- if self.default:
- retval += " --default"
- if self.excludeDocs:
- retval += " --excludedocs"
- if not self.addBase:
- retval += " --nobase"
- if self.handleMissing == constants.KS_MISSING_IGNORE:
- retval += " --ignoremissing"
- if self.instLangs:
- retval += " --instLangs=%s" % self.instLangs
-
- if ver >= version.F8:
- return retval + "\n" + pkgs + "\n%end\n"
- else:
- return retval + "\n" + pkgs + "\n"
-
- def _processGroup (self, line):
- op = OptionParser()
- op.add_option("--nodefaults", action="store_true", default=False)
- op.add_option("--optional", action="store_true", default=False)
-
- (opts, extra) = op.parse_args(args=line.split())
-
- if opts.nodefaults and opts.optional:
- raise KickstartValueError, _("Group cannot specify both --nodefaults and --optional")
-
- # If the group name has spaces in it, we have to put it back together
- # now.
- grp = " ".join(extra)
-
- if opts.nodefaults:
- self.groupList.append(Group(name=grp, include=constants.GROUP_REQUIRED))
- elif opts.optional:
- self.groupList.append(Group(name=grp, include=constants.GROUP_ALL))
- else:
- self.groupList.append(Group(name=grp, include=constants.GROUP_DEFAULT))
-
- def add (self, pkgList):
- """Given a list of lines from the input file, strip off any leading
- symbols and add the result to the appropriate list.
- """
- existingExcludedSet = set(self.excludedList)
- existingPackageSet = set(self.packageList)
- newExcludedSet = set()
- newPackageSet = set()
-
- excludedGroupList = []
-
- for pkg in pkgList:
- stripped = pkg.strip()
-
- if stripped[0] == "@":
- self._processGroup(stripped[1:])
- elif stripped[0] == "-":
- if stripped[1] == "@":
- excludedGroupList.append(Group(name=stripped[2:]))
- else:
- newExcludedSet.add(stripped[1:])
- else:
- newPackageSet.add(stripped)
-
- # Groups have to be excluded in two different ways (note: can't use
- # sets here because we have to store objects):
- excludedGroupNames = map(lambda g: g.name, excludedGroupList)
-
- # First, an excluded group may be cancelling out a previously given
- # one. This is often the case when using %include. So there we should
- # just remove the group from the list.
- self.groupList = filter(lambda g: g.name not in excludedGroupNames, self.groupList)
-
- # Second, the package list could have included globs which are not
- # processed by pykickstart. In that case we need to preserve a list of
- # excluded groups so whatever tool doing package/group installation can
- # take appropriate action.
- self.excludedGroupList.extend(excludedGroupList)
-
- existingPackageSet = (existingPackageSet - newExcludedSet) | newPackageSet
- existingExcludedSet = (existingExcludedSet - existingPackageSet) | newExcludedSet
-
- self.packageList = list(existingPackageSet)
- self.excludedList = list(existingExcludedSet)
-
-
-###
-### PARSER
-###
-class KickstartParser:
- """The kickstart file parser class as represented by a basic state
- machine. To create a specialized parser, make a subclass and override
- any of the methods you care about. Methods that don't need to do
- anything may just pass. However, _stateMachine should never be
- overridden.
- """
- def __init__ (self, handler, followIncludes=True, errorsAreFatal=True,
- missingIncludeIsFatal=True):
- """Create a new KickstartParser instance. Instance attributes:
-
- errorsAreFatal -- Should errors cause processing to halt, or
- just print a message to the screen? This
- is most useful for writing syntax checkers
- that may want to continue after an error is
- encountered.
- followIncludes -- If %include is seen, should the included
- file be checked as well or skipped?
- handler -- An instance of a BaseHandler subclass. If
- None, the input file will still be parsed
- but no data will be saved and no commands
- will be executed.
- missingIncludeIsFatal -- Should missing include files be fatal, even
- if errorsAreFatal is False?
- """
- self.errorsAreFatal = errorsAreFatal
- self.followIncludes = followIncludes
- self.handler = handler
- self.currentdir = {}
- self.missingIncludeIsFatal = missingIncludeIsFatal
-
- self._state = STATE_COMMANDS
- self._includeDepth = 0
- self._line = ""
-
- self.version = self.handler.version
-
- global ver
- ver = self.version
-
- self._sections = {}
- self.setupSections()
-
- def _reset(self):
- """Reset the internal variables of the state machine for a new kickstart file."""
- self._state = STATE_COMMANDS
- self._includeDepth = 0
-
- def getSection(self, s):
- """Return a reference to the requested section (s must start with '%'s),
- or raise KeyError if not found.
- """
- return self._sections[s]
-
- def handleCommand (self, lineno, args):
- """Given the list of command and arguments, call the Version's
- dispatcher method to handle the command. Returns the command or
- data object returned by the dispatcher. This method may be
- overridden in a subclass if necessary.
- """
- if self.handler:
- self.handler.currentCmd = args[0]
- self.handler.currentLine = self._line
- retval = self.handler.dispatcher(args, lineno)
-
- return retval
-
- def registerSection(self, obj):
- """Given an instance of a Section subclass, register the new section
- with the parser. Calling this method means the parser will
- recognize your new section and dispatch into the given object to
- handle it.
- """
- if not obj.sectionOpen:
- raise TypeError, "no sectionOpen given for section %s" % obj
-
- if not obj.sectionOpen.startswith("%"):
- raise TypeError, "section %s tag does not start with a %%" % obj.sectionOpen
-
- self._sections[obj.sectionOpen] = obj
-
- def _finalize(self, obj):
- """Called at the close of a kickstart section to take any required
- actions. Internally, this is used to add scripts once we have the
- whole body read.
- """
- obj.finalize()
- self._state = STATE_COMMANDS
-
- def _handleSpecialComments(self, line):
- """Kickstart recognizes a couple special comments."""
- if self._state != STATE_COMMANDS:
- return
-
- # Save the platform for s-c-kickstart.
- if line[:10] == "#platform=":
- self.handler.platform = self._line[11:]
-
- def _readSection(self, lineIter, lineno):
- obj = self._sections[self._state]
-
- while True:
- try:
- line = lineIter.next()
- if line == "":
- # This section ends at the end of the file.
- if self.version >= version.F8:
- raise KickstartParseError, formatErrorMsg(lineno, msg=_("Section does not end with %%end."))
-
- self._finalize(obj)
- except StopIteration:
- break
-
- lineno += 1
-
- # Throw away blank lines and comments, unless the section wants all
- # lines.
- if self._isBlankOrComment(line) and not obj.allLines:
- continue
-
- if line.startswith("%"):
- args = shlex.split(line)
-
- if args and args[0] == "%end":
- # This is a properly terminated section.
- self._finalize(obj)
- break
- elif args and args[0] == "%ksappend":
- continue
- elif args and (self._validState(args[0]) or args[0] in ["%include", "%ksappend"]):
- # This is an unterminated section.
- if self.version >= version.F8:
- raise KickstartParseError, formatErrorMsg(lineno, msg=_("Section does not end with %%end."))
-
- # Finish up. We do not process the header here because
- # kicking back out to STATE_COMMANDS will ensure that happens.
- lineIter.put(line)
- lineno -= 1
- self._finalize(obj)
- break
- else:
- # This is just a line within a section. Pass it off to whatever
- # section handles it.
- obj.handleLine(line)
-
- return lineno
-
- def _validState(self, st):
- """Is the given section tag one that has been registered with the parser?"""
- return st in self._sections.keys()
-
- def _tryFunc(self, fn):
- """Call the provided function (which doesn't take any arguments) and
- do the appropriate error handling. If errorsAreFatal is False, this
- function will just print the exception and keep going.
- """
- try:
- fn()
- except Exception, msg:
- if self.errorsAreFatal:
- raise
- else:
- print msg
-
- def _isBlankOrComment(self, line):
- return line.isspace() or line == "" or line.lstrip()[0] == '#'
-
- def _stateMachine(self, lineIter):
- # For error reporting.
- lineno = 0
-
- while True:
- # Get the next line out of the file, quitting if this is the last line.
- try:
- self._line = lineIter.next()
- if self._line == "":
- break
- except StopIteration:
- break
-
- lineno += 1
-
- # Eliminate blank lines, whitespace-only lines, and comments.
- if self._isBlankOrComment(self._line):
- self._handleSpecialComments(self._line)
- continue
-
- # Remove any end-of-line comments.
- sanitized = self._line.split("#")[0]
-
- # Then split the line.
- args = shlex.split(sanitized.rstrip())
-
- if args[0] == "%include":
- # This case comes up primarily in ksvalidator.
- if not self.followIncludes:
- continue
-
- if len(args) == 1 or not args[1]:
- raise KickstartParseError, formatErrorMsg(lineno)
-
- self._includeDepth += 1
-
- try:
- self.readKickstart(args[1], reset=False)
- except KickstartError:
- # Handle the include file being provided over the
- # network in a %pre script. This case comes up in the
- # early parsing in anaconda.
- if self.missingIncludeIsFatal:
- raise
-
- self._includeDepth -= 1
- continue
-
- # Now on to the main event.
- if self._state == STATE_COMMANDS:
- if args[0] == "%ksappend":
- # This is handled by the preprocess* functions, so continue.
- continue
- elif args[0][0] == '%':
- # This is the beginning of a new section. Handle its header
- # here.
- newSection = args[0]
- if not self._validState(newSection):
- raise KickstartParseError, formatErrorMsg(lineno, msg=_("Unknown kickstart section: %s" % newSection))
-
- self._state = newSection
- obj = self._sections[self._state]
- self._tryFunc(lambda: obj.handleHeader(lineno, args))
-
- # This will handle all section processing, kicking us back
- # out to STATE_COMMANDS at the end with the current line
- # being the next section header, etc.
- lineno = self._readSection(lineIter, lineno)
- else:
- # This is a command in the command section. Dispatch to it.
- self._tryFunc(lambda: self.handleCommand(lineno, args))
- elif self._state == STATE_END:
- break
-
- def readKickstartFromString (self, s, reset=True):
- """Process a kickstart file, provided as the string str."""
- if reset:
- self._reset()
-
- # Add a "" to the end of the list so the string reader acts like the
- # file reader and we only get StopIteration when we're after the final
- # line of input.
- i = PutBackIterator(s.splitlines(True) + [""])
- self._stateMachine (i)
-
- def readKickstart(self, f, reset=True):
- """Process a kickstart file, given by the filename f."""
- if reset:
- self._reset()
-
- # an %include might not specify a full path. if we don't try to figure
- # out what the path should have been, then we're unable to find it
- # requiring full path specification, though, sucks. so let's make
- # the reading "smart" by keeping track of what the path is at each
- # include depth.
- if not os.path.exists(f):
- if self.currentdir.has_key(self._includeDepth - 1):
- if os.path.exists(os.path.join(self.currentdir[self._includeDepth - 1], f)):
- f = os.path.join(self.currentdir[self._includeDepth - 1], f)
-
- cd = os.path.dirname(f)
- if not cd.startswith("/"):
- cd = os.path.abspath(cd)
- self.currentdir[self._includeDepth] = cd
-
- try:
- s = file(f).read()
- except IOError, e:
- raise KickstartError, formatErrorMsg(0, msg=_("Unable to open input kickstart file: %s") % e.strerror)
-
- self.readKickstartFromString(s, reset=False)
-
- def setupSections(self):
- """Install the sections all kickstart files support. You may override
- this method in a subclass, but should avoid doing so unless you know
- what you're doing.
- """
- self._sections = {}
-
- # Install the sections all kickstart files support.
- self.registerSection(PreScriptSection(self.handler, dataObj=Script))
- self.registerSection(PostScriptSection(self.handler, dataObj=Script))
- self.registerSection(TracebackScriptSection(self.handler, dataObj=Script))
- self.registerSection(PackageSection(self.handler))
diff --git a/scripts/lib/wic/3rdparty/pykickstart/sections.py b/scripts/lib/wic/3rdparty/pykickstart/sections.py
deleted file mode 100644
index 44df856b8d..0000000000
--- a/scripts/lib/wic/3rdparty/pykickstart/sections.py
+++ /dev/null
@@ -1,244 +0,0 @@
-#
-# sections.py: Kickstart file sections.
-#
-# Chris Lumens <clumens@redhat.com>
-#
-# Copyright 2011 Red Hat, Inc.
-#
-# This copyrighted material is made available to anyone wishing to use, modify,
-# copy, or redistribute it subject to the terms and conditions of the GNU
-# General Public License v.2. This program is distributed in the hope that it
-# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
-# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-# See the GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along with
-# this program; if not, write to the Free Software Foundation, Inc., 51
-# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
-# trademarks that are incorporated in the source code or documentation are not
-# subject to the GNU General Public License and may only be used or replicated
-# with the express permission of Red Hat, Inc.
-#
-"""
-This module exports the classes that define a section of a kickstart file. A
-section is a chunk of the file starting with a %tag and ending with a %end.
-Examples of sections include %packages, %pre, and %post.
-
-You may use this module to define your own custom sections which will be
-treated just the same as a predefined one by the kickstart parser. All that
-is necessary is to create a new subclass of Section and call
-parser.registerSection with an instance of your new class.
-"""
-from constants import *
-from options import KSOptionParser
-from version import *
-
-class Section(object):
- """The base class for defining kickstart sections. You are free to
- subclass this as appropriate.
-
- Class attributes:
-
- allLines -- Does this section require the parser to call handleLine
- for every line in the section, even blanks and comments?
- sectionOpen -- The string that denotes the start of this section. You
- must start your tag with a percent sign.
- timesSeen -- This attribute is for informational purposes only. It is
- incremented every time handleHeader is called to keep
- track of the number of times a section of this type is
- seen.
- """
- allLines = False
- sectionOpen = ""
- timesSeen = 0
-
- def __init__(self, handler, **kwargs):
- """Create a new Script instance. At the least, you must pass in an
- instance of a baseHandler subclass.
-
- Valid kwargs:
-
- dataObj --
- """
- self.handler = handler
-
- self.version = self.handler.version
-
- self.dataObj = kwargs.get("dataObj", None)
-
- def finalize(self):
- """This method is called when the %end tag for a section is seen. It
- is not required to be provided.
- """
- pass
-
- def handleLine(self, line):
- """This method is called for every line of a section. Take whatever
- action is appropriate. While this method is not required to be
- provided, not providing it does not make a whole lot of sense.
-
- Arguments:
-
- line -- The complete line, with any trailing newline.
- """
- pass
-
- def handleHeader(self, lineno, args):
- """This method is called when the opening tag for a section is seen.
- Not all sections will need this method, though all provided with
- kickstart include one.
-
- Arguments:
-
- args -- A list of all strings passed as arguments to the section
- opening tag.
- """
- self.timesSeen += 1
-
-class NullSection(Section):
- """This defines a section that pykickstart will recognize but do nothing
- with. If the parser runs across a %section that has no object registered,
- it will raise an error. Sometimes, you may want to simply ignore those
- sections instead. This class is useful for that purpose.
- """
- def __init__(self, *args, **kwargs):
- """Create a new NullSection instance. You must pass a sectionOpen
- parameter (including a leading '%') for the section you wish to
- ignore.
- """
- Section.__init__(self, *args, **kwargs)
- self.sectionOpen = kwargs.get("sectionOpen")
-
-class ScriptSection(Section):
- allLines = True
-
- def __init__(self, *args, **kwargs):
- Section.__init__(self, *args, **kwargs)
- self._script = {}
- self._resetScript()
-
- def _getParser(self):
- op = KSOptionParser(self.version)
- op.add_option("--erroronfail", dest="errorOnFail", action="store_true",
- default=False)
- op.add_option("--interpreter", dest="interpreter", default="/bin/sh")
- op.add_option("--log", "--logfile", dest="log")
- return op
-
- def _resetScript(self):
- self._script = {"interp": "/bin/sh", "log": None, "errorOnFail": False,
- "lineno": None, "chroot": False, "body": []}
-
- def handleLine(self, line):
- self._script["body"].append(line)
-
- def finalize(self):
- if " ".join(self._script["body"]).strip() == "":
- return
-
- kwargs = {"interp": self._script["interp"],
- "inChroot": self._script["chroot"],
- "lineno": self._script["lineno"],
- "logfile": self._script["log"],
- "errorOnFail": self._script["errorOnFail"],
- "type": self._script["type"]}
-
- s = self.dataObj (self._script["body"], **kwargs)
- self._resetScript()
-
- if self.handler:
- self.handler.scripts.append(s)
-
- def handleHeader(self, lineno, args):
- """Process the arguments to a %pre/%post/%traceback header for later
- setting on a Script instance once the end of the script is found.
- This method may be overridden in a subclass if necessary.
- """
- Section.handleHeader(self, lineno, args)
- op = self._getParser()
-
- (opts, extra) = op.parse_args(args=args[1:], lineno=lineno)
-
- self._script["interp"] = opts.interpreter
- self._script["lineno"] = lineno
- self._script["log"] = opts.log
- self._script["errorOnFail"] = opts.errorOnFail
- if hasattr(opts, "nochroot"):
- self._script["chroot"] = not opts.nochroot
-
-class PreScriptSection(ScriptSection):
- sectionOpen = "%pre"
-
- def _resetScript(self):
- ScriptSection._resetScript(self)
- self._script["type"] = KS_SCRIPT_PRE
-
-class PostScriptSection(ScriptSection):
- sectionOpen = "%post"
-
- def _getParser(self):
- op = ScriptSection._getParser(self)
- op.add_option("--nochroot", dest="nochroot", action="store_true",
- default=False)
- return op
-
- def _resetScript(self):
- ScriptSection._resetScript(self)
- self._script["chroot"] = True
- self._script["type"] = KS_SCRIPT_POST
-
-class TracebackScriptSection(ScriptSection):
- sectionOpen = "%traceback"
-
- def _resetScript(self):
- ScriptSection._resetScript(self)
- self._script["type"] = KS_SCRIPT_TRACEBACK
-
-class PackageSection(Section):
- sectionOpen = "%packages"
-
- def handleLine(self, line):
- if not self.handler:
- return
-
- (h, s, t) = line.partition('#')
- line = h.rstrip()
-
- self.handler.packages.add([line])
-
- def handleHeader(self, lineno, args):
- """Process the arguments to the %packages header and set attributes
- on the Version's Packages instance appropriate. This method may be
- overridden in a subclass if necessary.
- """
- Section.handleHeader(self, lineno, args)
- op = KSOptionParser(version=self.version)
- op.add_option("--excludedocs", dest="excludedocs", action="store_true",
- default=False)
- op.add_option("--ignoremissing", dest="ignoremissing",
- action="store_true", default=False)
- op.add_option("--nobase", dest="nobase", action="store_true",
- default=False)
- op.add_option("--ignoredeps", dest="resolveDeps", action="store_false",
- deprecated=FC4, removed=F9)
- op.add_option("--resolvedeps", dest="resolveDeps", action="store_true",
- deprecated=FC4, removed=F9)
- op.add_option("--default", dest="defaultPackages", action="store_true",
- default=False, introduced=F7)
- op.add_option("--instLangs", dest="instLangs", type="string",
- default="", introduced=F9)
-
- (opts, extra) = op.parse_args(args=args[1:], lineno=lineno)
-
- self.handler.packages.excludeDocs = opts.excludedocs
- self.handler.packages.addBase = not opts.nobase
- if opts.ignoremissing:
- self.handler.packages.handleMissing = KS_MISSING_IGNORE
- else:
- self.handler.packages.handleMissing = KS_MISSING_PROMPT
-
- if opts.defaultPackages:
- self.handler.packages.default = True
-
- if opts.instLangs:
- self.handler.packages.instLangs = opts.instLangs
diff --git a/scripts/lib/wic/3rdparty/pykickstart/version.py b/scripts/lib/wic/3rdparty/pykickstart/version.py
deleted file mode 100644
index 8a8e6aad22..0000000000
--- a/scripts/lib/wic/3rdparty/pykickstart/version.py
+++ /dev/null
@@ -1,168 +0,0 @@
-#
-# Chris Lumens <clumens@redhat.com>
-#
-# Copyright 2006, 2007, 2008, 2009, 2010 Red Hat, Inc.
-#
-# This copyrighted material is made available to anyone wishing to use, modify,
-# copy, or redistribute it subject to the terms and conditions of the GNU
-# General Public License v.2. This program is distributed in the hope that it
-# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
-# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-# See the GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along with
-# this program; if not, write to the Free Software Foundation, Inc., 51
-# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat
-# trademarks that are incorporated in the source code or documentation are not
-# subject to the GNU General Public License and may only be used or replicated
-# with the express permission of Red Hat, Inc.
-#
-"""
-Methods for working with kickstart versions.
-
-This module defines several symbolic constants that specify kickstart syntax
-versions. Each version corresponds roughly to one release of Red Hat Linux,
-Red Hat Enterprise Linux, or Fedora Core as these are where most syntax
-changes take place.
-
-This module also exports several functions:
-
- makeVersion - Given a version number, return an instance of the
- matching handler class.
-
- returnClassForVersion - Given a version number, return the matching
- handler class. This does not return an
- instance of that class, however.
-
- stringToVersion - Convert a string representation of a version number
- into the symbolic constant.
-
- versionToString - Perform the reverse mapping.
-
- versionFromFile - Read a kickstart file and determine the version of
- syntax it uses. This requires the kickstart file to
- have a version= comment in it.
-"""
-import imputil, re, sys
-
-import gettext
-_ = lambda x: gettext.ldgettext("pykickstart", x)
-
-from pykickstart.errors import KickstartVersionError
-
-# Symbolic names for internal version numbers.
-RHEL3 = 900
-FC3 = 1000
-RHEL4 = 1100
-FC4 = 2000
-FC5 = 3000
-FC6 = 4000
-RHEL5 = 4100
-F7 = 5000
-F8 = 6000
-F9 = 7000
-F10 = 8000
-F11 = 9000
-F12 = 10000
-F13 = 11000
-RHEL6 = 11100
-F14 = 12000
-F15 = 13000
-F16 = 14000
-
-# This always points at the latest version and is the default.
-DEVEL = F16
-
-# A one-to-one mapping from string representations to version numbers.
-versionMap = {
- "DEVEL": DEVEL,
- "FC3": FC3, "FC4": FC4, "FC5": FC5, "FC6": FC6, "F7": F7, "F8": F8,
- "F9": F9, "F10": F10, "F11": F11, "F12": F12, "F13": F13,
- "F14": F14, "F15": F15, "F16": F16,
- "RHEL3": RHEL3, "RHEL4": RHEL4, "RHEL5": RHEL5, "RHEL6": RHEL6
-}
-
-def stringToVersion(s):
- """Convert string into one of the provided version constants. Raises
- KickstartVersionError if string does not match anything.
- """
- # First try these short forms.
- try:
- return versionMap[s.upper()]
- except KeyError:
- pass
-
- # Now try the Fedora versions.
- m = re.match("^fedora.* (\d+)$", s, re.I)
-
- if m and m.group(1):
- if versionMap.has_key("FC" + m.group(1)):
- return versionMap["FC" + m.group(1)]
- elif versionMap.has_key("F" + m.group(1)):
- return versionMap["F" + m.group(1)]
- else:
- raise KickstartVersionError(_("Unsupported version specified: %s") % s)
-
- # Now try the RHEL versions.
- m = re.match("^red hat enterprise linux.* (\d+)([\.\d]*)$", s, re.I)
-
- if m and m.group(1):
- if versionMap.has_key("RHEL" + m.group(1)):
- return versionMap["RHEL" + m.group(1)]
- else:
- raise KickstartVersionError(_("Unsupported version specified: %s") % s)
-
- # If nothing else worked, we're out of options.
- raise KickstartVersionError(_("Unsupported version specified: %s") % s)
-
-def versionToString(version, skipDevel=False):
- """Convert version into a string representation of the version number.
- This is the reverse operation of stringToVersion. Raises
- KickstartVersionError if version does not match anything.
- """
- if not skipDevel and version == versionMap["DEVEL"]:
- return "DEVEL"
-
- for (key, val) in versionMap.iteritems():
- if key == "DEVEL":
- continue
- elif val == version:
- return key
-
- raise KickstartVersionError(_("Unsupported version specified: %s") % version)
-
-def returnClassForVersion(version=DEVEL):
- """Return the class of the syntax handler for version. version can be
- either a string or the matching constant. Raises KickstartValueError
- if version does not match anything.
- """
- try:
- version = int(version)
- module = "%s" % versionToString(version, skipDevel=True)
- except ValueError:
- module = "%s" % version
- version = stringToVersion(version)
-
- module = module.lower()
-
- try:
- import pykickstart.handlers
- sys.path.extend(pykickstart.handlers.__path__)
- found = imputil.imp.find_module(module)
- loaded = imputil.imp.load_module(module, found[0], found[1], found[2])
-
- for (k, v) in loaded.__dict__.iteritems():
- if k.lower().endswith("%shandler" % module):
- return v
- except:
- raise KickstartVersionError(_("Unsupported version specified: %s") % version)
-
-def makeVersion(version=DEVEL):
- """Return a new instance of the syntax handler for version. version can be
- either a string or the matching constant. This function is useful for
- standalone programs which just need to handle a specific version of
- kickstart syntax (as provided by a command line argument, for example)
- and need to instantiate the correct object.
- """
- cl = returnClassForVersion(version)
- return cl()
diff --git a/scripts/lib/wic/__init__.py b/scripts/lib/wic/__init__.py
index 63c1d9c846..85567934ae 100644
--- a/scripts/lib/wic/__init__.py
+++ b/scripts/lib/wic/__init__.py
@@ -1,4 +1,10 @@
-import os, sys
+#!/usr/bin/env python3
+#
+# Copyright (c) 2007 Red Hat, Inc.
+# Copyright (c) 2011 Intel, Inc.
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
-cur_path = os.path.dirname(__file__) or '.'
-sys.path.insert(0, cur_path + '/3rdparty')
+class WicError(Exception):
+ pass
diff --git a/scripts/lib/wic/__version__.py b/scripts/lib/wic/__version__.py
deleted file mode 100644
index 5452a46712..0000000000
--- a/scripts/lib/wic/__version__.py
+++ /dev/null
@@ -1 +0,0 @@
-VERSION = "2.00"
diff --git a/scripts/lib/wic/canned-wks/common.wks.inc b/scripts/lib/wic/canned-wks/common.wks.inc
new file mode 100644
index 0000000000..89880b417b
--- /dev/null
+++ b/scripts/lib/wic/canned-wks/common.wks.inc
@@ -0,0 +1,3 @@
+# This file is included into 3 canned wks files from this directory
+part /boot --source bootimg-pcbios --ondisk sda --label boot --active --align 1024
+part / --source rootfs --use-uuid --fstype=ext4 --label platform --align 1024
diff --git a/scripts/lib/wic/canned-wks/directdisk-bootloader-config.cfg b/scripts/lib/wic/canned-wks/directdisk-bootloader-config.cfg
new file mode 100644
index 0000000000..c58e74a853
--- /dev/null
+++ b/scripts/lib/wic/canned-wks/directdisk-bootloader-config.cfg
@@ -0,0 +1,27 @@
+# This is an example configuration file for syslinux.
+TIMEOUT 50
+ALLOWOPTIONS 1
+SERIAL 0 115200
+PROMPT 0
+
+UI vesamenu.c32
+menu title Select boot options
+menu tabmsg Press [Tab] to edit, [Return] to select
+
+DEFAULT Graphics console boot
+
+LABEL Graphics console boot
+KERNEL /vmlinuz
+APPEND label=boot rootwait
+
+LABEL Serial console boot
+KERNEL /vmlinuz
+APPEND label=boot rootwait console=ttyS0,115200
+
+LABEL Graphics console install
+KERNEL /vmlinuz
+APPEND label=install rootwait
+
+LABEL Serial console install
+KERNEL /vmlinuz
+APPEND label=install rootwait console=ttyS0,115200
diff --git a/scripts/lib/wic/canned-wks/directdisk-bootloader-config.wks b/scripts/lib/wic/canned-wks/directdisk-bootloader-config.wks
new file mode 100644
index 0000000000..3529e05c87
--- /dev/null
+++ b/scripts/lib/wic/canned-wks/directdisk-bootloader-config.wks
@@ -0,0 +1,8 @@
+# short-description: Create a 'pcbios' direct disk image with custom bootloader config
+# long-description: Creates a partitioned legacy BIOS disk image that the user
+# can directly dd to boot media. The bootloader configuration source is a user file.
+
+include common.wks.inc
+
+bootloader --configfile="directdisk-bootloader-config.cfg"
+
diff --git a/scripts/lib/wic/canned-wks/directdisk-gpt.wks b/scripts/lib/wic/canned-wks/directdisk-gpt.wks
index ea01cf3752..8d7d8de6ea 100644
--- a/scripts/lib/wic/canned-wks/directdisk-gpt.wks
+++ b/scripts/lib/wic/canned-wks/directdisk-gpt.wks
@@ -6,5 +6,5 @@
part /boot --source bootimg-pcbios --ondisk sda --label boot --active --align 1024
part / --source rootfs --ondisk sda --fstype=ext4 --label platform --align 1024 --use-uuid
-bootloader --ptable gpt --timeout=0 --append="rootwait rootfstype=ext4 video=vesafb vga=0x318 console=tty0"
+bootloader --ptable gpt --timeout=0 --append="rootwait rootfstype=ext4 video=vesafb vga=0x318 console=tty0 console=ttyS0,115200n8"
diff --git a/scripts/lib/wic/canned-wks/directdisk-multi-rootfs.wks b/scripts/lib/wic/canned-wks/directdisk-multi-rootfs.wks
index 8a81f8f519..f61d941d6d 100644
--- a/scripts/lib/wic/canned-wks/directdisk-multi-rootfs.wks
+++ b/scripts/lib/wic/canned-wks/directdisk-multi-rootfs.wks
@@ -19,5 +19,5 @@ part /boot --source bootimg-pcbios --ondisk sda --label boot --active --align 10
part / --source rootfs --rootfs-dir=rootfs1 --ondisk sda --fstype=ext4 --label platform --align 1024
part /rescue --source rootfs --rootfs-dir=rootfs2 --ondisk sda --fstype=ext4 --label secondary --align 1024
-bootloader --timeout=0 --append="rootwait rootfstype=ext4 video=vesafb vga=0x318 console=tty0"
+bootloader --timeout=0 --append="rootwait rootfstype=ext4 video=vesafb vga=0x318 console=tty0 console=ttyS0,115200n8"
diff --git a/scripts/lib/wic/canned-wks/directdisk.wks b/scripts/lib/wic/canned-wks/directdisk.wks
index af4c9eadaf..8c8e06b02c 100644
--- a/scripts/lib/wic/canned-wks/directdisk.wks
+++ b/scripts/lib/wic/canned-wks/directdisk.wks
@@ -2,9 +2,7 @@
# long-description: Creates a partitioned legacy BIOS disk image that the user
# can directly dd to boot media.
+include common.wks.inc
-part /boot --source bootimg-pcbios --ondisk sda --label boot --active --align 1024
-part / --source rootfs --ondisk sda --fstype=ext4 --label platform --align 1024
-
-bootloader --timeout=0 --append="rootwait rootfstype=ext4 video=vesafb vga=0x318 console=tty0"
+bootloader --timeout=0 --append="rootwait rootfstype=ext4 video=vesafb vga=0x318 console=tty0 console=ttyS0,115200n8"
diff --git a/scripts/lib/wic/canned-wks/efi-bootdisk.wks.in b/scripts/lib/wic/canned-wks/efi-bootdisk.wks.in
new file mode 100644
index 0000000000..7300e65e32
--- /dev/null
+++ b/scripts/lib/wic/canned-wks/efi-bootdisk.wks.in
@@ -0,0 +1,3 @@
+bootloader --ptable gpt
+part /boot --source rootfs --rootfs-dir=${IMAGE_ROOTFS}/boot --fstype=vfat --label boot --active --align 1024 --use-uuid --overhead-factor 1.0
+part / --source rootfs --fstype=ext4 --label root --align 1024 --exclude-path boot/
diff --git a/scripts/lib/wic/canned-wks/mkefidisk.wks b/scripts/lib/wic/canned-wks/mkefidisk.wks
index 696e94e3d7..9f534fe184 100644
--- a/scripts/lib/wic/canned-wks/mkefidisk.wks
+++ b/scripts/lib/wic/canned-wks/mkefidisk.wks
@@ -4,8 +4,8 @@
part /boot --source bootimg-efi --sourceparams="loader=grub-efi" --ondisk sda --label msdos --active --align 1024
-part / --source rootfs --ondisk sda --fstype=ext4 --label platform --align 1024
+part / --source rootfs --ondisk sda --fstype=ext4 --label platform --align 1024 --use-uuid
part swap --ondisk sda --size 44 --label swap1 --fstype=swap
-bootloader --timeout=10 --append="rootwait rootfstype=ext4 console=ttyPCH0,115200 console=tty0 vmalloc=256MB snd-hda-intel.enable_msi=0"
+bootloader --ptable gpt --timeout=5 --append="rootfstype=ext4 console=ttyS0,115200 console=tty0"
diff --git a/scripts/lib/wic/canned-wks/mkgummidisk.wks b/scripts/lib/wic/canned-wks/mkgummidisk.wks
deleted file mode 100644
index 66a22f60bd..0000000000
--- a/scripts/lib/wic/canned-wks/mkgummidisk.wks
+++ /dev/null
@@ -1,11 +0,0 @@
-# short-description: Create an EFI disk image
-# long-description: Creates a partitioned EFI disk image that the user
-# can directly dd to boot media.
-
-part /boot --source bootimg-efi --sourceparams="loader=gummiboot" --ondisk sda --label msdos --active --align 1024
-
-part / --source rootfs --ondisk sda --fstype=ext4 --label platform --align 1024
-
-part swap --ondisk sda --size 44 --label swap1 --fstype=swap
-
-bootloader --timeout=10 --append="rootwait rootfstype=ext4 console=ttyPCH0,115200 console=tty0 vmalloc=256MB snd-hda-intel.enable_msi=0"
diff --git a/scripts/lib/wic/canned-wks/mkhybridiso.wks b/scripts/lib/wic/canned-wks/mkhybridiso.wks
index 9d34e9b477..48c5ac4791 100644
--- a/scripts/lib/wic/canned-wks/mkhybridiso.wks
+++ b/scripts/lib/wic/canned-wks/mkhybridiso.wks
@@ -2,6 +2,6 @@
# long-description: Creates an EFI and legacy bootable hybrid ISO image
# which can be used on optical media as well as USB media.
-part /boot --source isoimage-isohybrid --sourceparams="loader=grub-efi,image_name=HYBRID_ISO_IMG" --ondisk cd --label HYBRIDISO --fstype=ext4
+part /boot --source isoimage-isohybrid --sourceparams="loader=grub-efi,image_name=HYBRID_ISO_IMG" --ondisk cd --label HYBRIDISO
bootloader --timeout=15 --append=""
diff --git a/scripts/lib/wic/canned-wks/qemuriscv.wks b/scripts/lib/wic/canned-wks/qemuriscv.wks
new file mode 100644
index 0000000000..12c68b7069
--- /dev/null
+++ b/scripts/lib/wic/canned-wks/qemuriscv.wks
@@ -0,0 +1,3 @@
+# short-description: Create qcow2 image for RISC-V QEMU machines
+
+part / --source rootfs --fstype=ext4 --label root --align 4096 --size 5G
diff --git a/scripts/lib/wic/canned-wks/qemux86-directdisk.wks b/scripts/lib/wic/canned-wks/qemux86-directdisk.wks
index 8fc38b54d0..22b45217f1 100644
--- a/scripts/lib/wic/canned-wks/qemux86-directdisk.wks
+++ b/scripts/lib/wic/canned-wks/qemux86-directdisk.wks
@@ -2,9 +2,7 @@
# long-description: Creates a partitioned legacy BIOS disk image that the user
# can directly use to boot a qemu machine.
+include common.wks.inc
-part /boot --source bootimg-pcbios --ondisk sda --label boot --active --align 1024
-part / --source rootfs --ondisk sda --fstype=ext4 --label platform --align 1024
-
-bootloader --timeout=0 --append="vga=0 uvesafb.mode_option=640x480-32 root=/dev/vda2 rw mem=256M ip=192.168.7.2::192.168.7.1:255.255.255.0 oprofile.timer=1 rootfstype=ext4 "
+bootloader --timeout=0 --append="rw oprofile.timer=1 rootfstype=ext4 "
diff --git a/scripts/lib/wic/canned-wks/sdimage-bootpart.wks b/scripts/lib/wic/canned-wks/sdimage-bootpart.wks
index 7ffd632f4a..63bc4dab6a 100644
--- a/scripts/lib/wic/canned-wks/sdimage-bootpart.wks
+++ b/scripts/lib/wic/canned-wks/sdimage-bootpart.wks
@@ -2,5 +2,5 @@
# long-description: Creates a partitioned SD card image. Boot files
# are located in the first vfat partition.
-part /boot --source bootimg-partition --ondisk mmcblk --fstype=vfat --label boot --active --align 4 --size 16
-part / --source rootfs --ondisk mmcblk --fstype=ext4 --label root --align 4
+part /boot --source bootimg-partition --ondisk mmcblk0 --fstype=vfat --label boot --active --align 4 --size 16
+part / --source rootfs --ondisk mmcblk0 --fstype=ext4 --label root --align 4
diff --git a/scripts/lib/wic/canned-wks/systemd-bootdisk.wks b/scripts/lib/wic/canned-wks/systemd-bootdisk.wks
new file mode 100644
index 0000000000..95d7b97a60
--- /dev/null
+++ b/scripts/lib/wic/canned-wks/systemd-bootdisk.wks
@@ -0,0 +1,11 @@
+# short-description: Create an EFI disk image with systemd-boot
+# long-description: Creates a partitioned EFI disk image that the user
+# can directly dd to boot media. The selected bootloader is systemd-boot.
+
+part /boot --source bootimg-efi --sourceparams="loader=systemd-boot" --ondisk sda --label msdos --active --align 1024 --use-uuid
+
+part / --source rootfs --ondisk sda --fstype=ext4 --label platform --align 1024 --use-uuid
+
+part swap --ondisk sda --size 44 --label swap1 --fstype=swap --use-uuid
+
+bootloader --ptable gpt --timeout=5 --append="rootwait rootfstype=ext4 console=ttyS0,115200 console=tty0"
diff --git a/scripts/lib/wic/conf.py b/scripts/lib/wic/conf.py
deleted file mode 100644
index 1d4363a526..0000000000
--- a/scripts/lib/wic/conf.py
+++ /dev/null
@@ -1,100 +0,0 @@
-#!/usr/bin/env python -tt
-#
-# Copyright (c) 2011 Intel, Inc.
-#
-# This program is free software; you can redistribute it and/or modify it
-# under the terms of the GNU General Public License as published by the Free
-# Software Foundation; version 2 of the License
-#
-# This program is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
-# for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc., 59
-# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-import os
-
-from wic import msger
-from wic import kickstart
-from wic.utils import misc
-
-
-def get_siteconf():
- wic_path = os.path.dirname(__file__)
- eos = wic_path.find('scripts') + len('scripts')
- scripts_path = wic_path[:eos]
-
- return scripts_path + "/lib/image/config/wic.conf"
-
-class ConfigMgr(object):
- DEFAULTS = {
- 'common': {
- "distro_name": "Default Distribution",
- "plugin_dir": "/usr/lib/wic/plugins"}, # TODO use prefix also?
- 'create': {
- "tmpdir": '/var/tmp/wic',
- "outdir": './wic-output',
- "release": None,
- "logfile": None,
- "name_prefix": None,
- "name_suffix": None}
- }
-
- # make the manager class as singleton
- _instance = None
- def __new__(cls, *args, **kwargs):
- if not cls._instance:
- cls._instance = super(ConfigMgr, cls).__new__(cls, *args, **kwargs)
-
- return cls._instance
-
- def __init__(self, ksconf=None, siteconf=None):
- # reset config options
- self.reset()
-
- if not siteconf:
- siteconf = get_siteconf()
-
- # initial options from siteconf
- self._siteconf = siteconf
-
- if ksconf:
- self._ksconf = ksconf
-
- def reset(self):
- self.__ksconf = None
- self.__siteconf = None
- self.create = {}
-
- # initialize the values with defaults
- for sec, vals in self.DEFAULTS.iteritems():
- setattr(self, sec, vals)
-
- def __set_ksconf(self, ksconf):
- if not os.path.isfile(ksconf):
- msger.error('Cannot find ks file: %s' % ksconf)
-
- self.__ksconf = ksconf
- self._parse_kickstart(ksconf)
- def __get_ksconf(self):
- return self.__ksconf
- _ksconf = property(__get_ksconf, __set_ksconf)
-
- def _parse_kickstart(self, ksconf=None):
- if not ksconf:
- return
-
- ksobj = kickstart.read_kickstart(ksconf)
-
- self.create['ks'] = ksobj
- self.create['name'] = os.path.splitext(os.path.basename(ksconf))[0]
-
- self.create['name'] = misc.build_name(ksconf,
- self.create['release'],
- self.create['name_prefix'],
- self.create['name_suffix'])
-
-configmgr = ConfigMgr()
diff --git a/scripts/lib/wic/config/wic.conf b/scripts/lib/wic/config/wic.conf
deleted file mode 100644
index a51bcb55eb..0000000000
--- a/scripts/lib/wic/config/wic.conf
+++ /dev/null
@@ -1,6 +0,0 @@
-[common]
-; general settings
-distro_name = OpenEmbedded
-
-[create]
-; settings for create subcommand
diff --git a/scripts/lib/wic/creator.py b/scripts/lib/wic/creator.py
deleted file mode 100644
index 5231297282..0000000000
--- a/scripts/lib/wic/creator.py
+++ /dev/null
@@ -1,124 +0,0 @@
-#!/usr/bin/env python -tt
-#
-# Copyright (c) 2011 Intel, Inc.
-#
-# This program is free software; you can redistribute it and/or modify it
-# under the terms of the GNU General Public License as published by the Free
-# Software Foundation; version 2 of the License
-#
-# This program is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
-# for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc., 59
-# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-import os, sys
-from optparse import OptionParser, SUPPRESS_HELP
-
-from wic import msger
-from wic.utils import errors
-from wic.conf import configmgr
-from wic.plugin import pluginmgr
-
-
-class Creator(object):
- """${name}: create an image
-
- Usage:
- ${name} SUBCOMMAND <ksfile> [OPTS]
-
- ${command_list}
- ${option_list}
- """
-
- name = 'wic create(cr)'
-
- def __init__(self, *args, **kwargs):
- self._subcmds = {}
-
- # get cmds from pluginmgr
- # mix-in do_subcmd interface
- for subcmd, klass in pluginmgr.get_plugins('imager').iteritems():
- if not hasattr(klass, 'do_create'):
- msger.warning("Unsupported subcmd: %s" % subcmd)
- continue
-
- func = getattr(klass, 'do_create')
- self._subcmds[subcmd] = func
-
- def get_optparser(self):
- optparser = OptionParser()
- optparser.add_option('-d', '--debug', action='store_true',
- dest='debug',
- help=SUPPRESS_HELP)
- optparser.add_option('-v', '--verbose', action='store_true',
- dest='verbose',
- help=SUPPRESS_HELP)
- optparser.add_option('', '--logfile', type='string', dest='logfile',
- default=None,
- help='Path of logfile')
- optparser.add_option('-c', '--config', type='string', dest='config',
- default=None,
- help='Specify config file for wic')
- optparser.add_option('-o', '--outdir', type='string', action='store',
- dest='outdir', default=None,
- help='Output directory')
- optparser.add_option('', '--tmpfs', action='store_true', dest='enabletmpfs',
- help='Setup tmpdir as tmpfs to accelerate, experimental'
- ' feature, use it if you have more than 4G memory')
- return optparser
-
- def postoptparse(self, options):
- abspath = lambda pth: os.path.abspath(os.path.expanduser(pth))
-
- if options.verbose:
- msger.set_loglevel('verbose')
- if options.debug:
- msger.set_loglevel('debug')
-
- if options.logfile:
- logfile_abs_path = abspath(options.logfile)
- if os.path.isdir(logfile_abs_path):
- raise errors.Usage("logfile's path %s should be file"
- % options.logfile)
- if not os.path.exists(os.path.dirname(logfile_abs_path)):
- os.makedirs(os.path.dirname(logfile_abs_path))
- msger.set_interactive(False)
- msger.set_logfile(logfile_abs_path)
- configmgr.create['logfile'] = options.logfile
-
- if options.config:
- configmgr.reset()
- configmgr._siteconf = options.config
-
- if options.outdir is not None:
- configmgr.create['outdir'] = abspath(options.outdir)
-
- cdir = 'outdir'
- if os.path.exists(configmgr.create[cdir]) \
- and not os.path.isdir(configmgr.create[cdir]):
- msger.error('Invalid directory specified: %s' \
- % configmgr.create[cdir])
-
- if options.enabletmpfs:
- configmgr.create['enabletmpfs'] = options.enabletmpfs
-
- def main(self, argv=None):
- if argv is None:
- argv = sys.argv
- else:
- argv = argv[:] # don't modify caller's list
-
- pname = argv[0]
- if pname not in self._subcmds:
- msger.error('Unknown plugin: %s' % pname)
-
- optparser = self.get_optparser()
- options, args = optparser.parse_args(argv)
-
- self.postoptparse(options)
-
- return self._subcmds[pname](options, *args[1:])
diff --git a/scripts/lib/wic/engine.py b/scripts/lib/wic/engine.py
index 76b93e82f2..018815b966 100644
--- a/scripts/lib/wic/engine.py
+++ b/scripts/lib/wic/engine.py
@@ -1,21 +1,7 @@
-# ex:ts=4:sw=4:sts=4:et
-# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
#
# Copyright (c) 2013, Intel Corporation.
-# All rights reserved.
#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 as
-# published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+# SPDX-License-Identifier: GPL-2.0-only
#
# DESCRIPTION
@@ -28,14 +14,22 @@
# Tom Zanussi <tom.zanussi (at] linux.intel.com>
#
+import logging
import os
-import sys
+import tempfile
+import json
+import subprocess
+import re
+
+from collections import namedtuple, OrderedDict
+from distutils.spawn import find_executable
-from wic import msger, creator
-from wic.utils import misc
-from wic.plugin import pluginmgr
-from wic.utils.oe import misc
+from wic import WicError
+from wic.filemap import sparse_copy
+from wic.pluginbase import PluginMgr
+from wic.misc import get_bitbake_var, exec_cmd
+logger = logging.getLogger('wic')
def verify_build_env():
"""
@@ -44,23 +38,25 @@ def verify_build_env():
Returns True if it is, false otherwise
"""
if not os.environ.get("BUILDDIR"):
- print "BUILDDIR not found, exiting. (Did you forget to source oe-init-build-env?)"
- sys.exit(1)
+ raise WicError("BUILDDIR not found, exiting. (Did you forget to source oe-init-build-env?)")
return True
CANNED_IMAGE_DIR = "lib/wic/canned-wks" # relative to scripts
SCRIPTS_CANNED_IMAGE_DIR = "scripts/" + CANNED_IMAGE_DIR
+WIC_DIR = "wic"
def build_canned_image_list(path):
- layers_path = misc.get_bitbake_var("BBLAYERS")
+ layers_path = get_bitbake_var("BBLAYERS")
canned_wks_layer_dirs = []
if layers_path is not None:
for layer_path in layers_path.split():
- cpath = os.path.join(layer_path, SCRIPTS_CANNED_IMAGE_DIR)
- canned_wks_layer_dirs.append(cpath)
+ for wks_path in (WIC_DIR, SCRIPTS_CANNED_IMAGE_DIR):
+ cpath = os.path.join(layer_path, wks_path)
+ if os.path.isdir(cpath):
+ canned_wks_layer_dirs.append(cpath)
cpath = os.path.join(path, CANNED_IMAGE_DIR)
canned_wks_layer_dirs.append(cpath)
@@ -80,7 +76,8 @@ def find_canned_image(scripts_path, wks_file):
for fname in files:
if fname.endswith("~") or fname.endswith("#"):
continue
- if fname.endswith(".wks") and wks_file + ".wks" == fname:
+ if ((fname.endswith(".wks") and wks_file + ".wks" == fname) or \
+ (fname.endswith(".wks.in") and wks_file + ".wks.in" == fname)):
fullpath = os.path.join(canned_wks_dir, fname)
return fullpath
return None
@@ -97,7 +94,7 @@ def list_canned_images(scripts_path):
for fname in files:
if fname.endswith("~") or fname.endswith("#"):
continue
- if fname.endswith(".wks"):
+ if fname.endswith(".wks") or fname.endswith(".wks.in"):
fullpath = os.path.join(canned_wks_dir, fname)
with open(fullpath) as wks:
for line in wks:
@@ -106,8 +103,8 @@ def list_canned_images(scripts_path):
if idx != -1:
desc = line[idx + len("short-description:"):].strip()
break
- basename = os.path.splitext(fname)[0]
- print " %s\t\t%s" % (basename.ljust(30), desc)
+ basename = fname.split('.')[0]
+ print(" %s\t\t%s" % (basename.ljust(30), desc))
def list_canned_image_help(scripts_path, fullpath):
@@ -120,15 +117,15 @@ def list_canned_image_help(scripts_path, fullpath):
if not found:
idx = line.find("long-description:")
if idx != -1:
- print
- print line[idx + len("long-description:"):].strip()
+ print()
+ print(line[idx + len("long-description:"):].strip())
found = True
continue
if not line.strip():
break
idx = line.find("#")
if idx != -1:
- print line[idx + len("#:"):].rstrip()
+ print(line[idx + len("#:"):].rstrip())
else:
break
@@ -137,25 +134,24 @@ def list_source_plugins():
"""
List the available source plugins i.e. plugins available for --source.
"""
- plugins = pluginmgr.get_source_plugins()
+ plugins = PluginMgr.get_plugins('source')
for plugin in plugins:
- print " %s" % plugin
+ print(" %s" % plugin)
def wic_create(wks_file, rootfs_dir, bootimg_dir, kernel_dir,
- native_sysroot, scripts_path, image_output_dir,
- compressor, debug):
- """Create image
+ native_sysroot, options):
+ """
+ Create image
wks_file - user-defined OE kickstart file
rootfs_dir - absolute path to the build's /rootfs dir
bootimg_dir - absolute path to the build's boot artifacts directory
kernel_dir - absolute path to the build's kernel directory
native_sysroot - absolute path to the build's native sysroots dir
- scripts_path - absolute path to /scripts dir
image_output_dir - dirname to create for image
- compressor - compressor utility to compress the image
+ options - wic command line options (debug, bmap, etc)
Normally, the values for the build artifacts values are determined
by 'wic -e' from the output of the 'bitbake -e' command given an
@@ -178,43 +174,455 @@ def wic_create(wks_file, rootfs_dir, bootimg_dir, kernel_dir,
try:
oe_builddir = os.environ["BUILDDIR"]
except KeyError:
- print "BUILDDIR not found, exiting. (Did you forget to source oe-init-build-env?)"
- sys.exit(1)
+ raise WicError("BUILDDIR not found, exiting. (Did you forget to source oe-init-build-env?)")
+
+ if not os.path.exists(options.outdir):
+ os.makedirs(options.outdir)
- if debug:
- msger.set_loglevel('debug')
+ pname = options.imager
+ plugin_class = PluginMgr.get_plugins('imager').get(pname)
+ if not plugin_class:
+ raise WicError('Unknown plugin: %s' % pname)
- crobj = creator.Creator()
+ plugin = plugin_class(wks_file, rootfs_dir, bootimg_dir, kernel_dir,
+ native_sysroot, oe_builddir, options)
- crobj.main(["direct", native_sysroot, kernel_dir, bootimg_dir, rootfs_dir,
- wks_file, image_output_dir, oe_builddir, compressor or ""])
+ plugin.do_create()
- print "\nThe image(s) were created using OE kickstart file:\n %s" % wks_file
+ logger.info("The image(s) were created using OE kickstart file:\n %s", wks_file)
def wic_list(args, scripts_path):
"""
Print the list of images or source plugins.
"""
- if len(args) < 1:
+ if args.list_type is None:
return False
- if args == ["images"]:
+ if args.list_type == "images":
+
list_canned_images(scripts_path)
return True
- elif args == ["source-plugins"]:
+ elif args.list_type == "source-plugins":
list_source_plugins()
return True
- elif len(args) == 2 and args[1] == "help":
- wks_file = args[0]
+ elif len(args.help_for) == 1 and args.help_for[0] == 'help':
+ wks_file = args.list_type
fullpath = find_canned_image(scripts_path, wks_file)
if not fullpath:
- print "No image named %s found, exiting. "\
- "(Use 'wic list images' to list available images, or "\
- "specify a fully-qualified OE kickstart (.wks) "\
- "filename)\n" % wks_file
- sys.exit(1)
+ raise WicError("No image named %s found, exiting. "
+ "(Use 'wic list images' to list available images, "
+ "or specify a fully-qualified OE kickstart (.wks) "
+ "filename)" % wks_file)
+
list_canned_image_help(scripts_path, fullpath)
return True
return False
+
+
+class Disk:
+ def __init__(self, imagepath, native_sysroot, fstypes=('fat', 'ext')):
+ self.imagepath = imagepath
+ self.native_sysroot = native_sysroot
+ self.fstypes = fstypes
+ self._partitions = None
+ self._partimages = {}
+ self._lsector_size = None
+ self._psector_size = None
+ self._ptable_format = None
+
+ # find parted
+ # read paths from $PATH environment variable
+ # if it fails, use hardcoded paths
+ pathlist = "/bin:/usr/bin:/usr/sbin:/sbin/"
+ try:
+ self.paths = os.environ['PATH'] + ":" + pathlist
+ except KeyError:
+ self.paths = pathlist
+
+ if native_sysroot:
+ for path in pathlist.split(':'):
+ self.paths = "%s%s:%s" % (native_sysroot, path, self.paths)
+
+ self.parted = find_executable("parted", self.paths)
+ if not self.parted:
+ raise WicError("Can't find executable parted")
+
+ self.partitions = self.get_partitions()
+
+ def __del__(self):
+ for path in self._partimages.values():
+ os.unlink(path)
+
+ def get_partitions(self):
+ if self._partitions is None:
+ self._partitions = OrderedDict()
+ out = exec_cmd("%s -sm %s unit B print" % (self.parted, self.imagepath))
+ parttype = namedtuple("Part", "pnum start end size fstype")
+ splitted = out.splitlines()
+ # skip over possible errors in exec_cmd output
+ try:
+ idx =splitted.index("BYT;")
+ except ValueError:
+ raise WicError("Error getting partition information from %s" % (self.parted))
+ lsector_size, psector_size, self._ptable_format = splitted[idx + 1].split(":")[3:6]
+ self._lsector_size = int(lsector_size)
+ self._psector_size = int(psector_size)
+ for line in splitted[idx + 2:]:
+ pnum, start, end, size, fstype = line.split(':')[:5]
+ partition = parttype(int(pnum), int(start[:-1]), int(end[:-1]),
+ int(size[:-1]), fstype)
+ self._partitions[pnum] = partition
+
+ return self._partitions
+
+ def __getattr__(self, name):
+ """Get path to the executable in a lazy way."""
+ if name in ("mdir", "mcopy", "mdel", "mdeltree", "sfdisk", "e2fsck",
+ "resize2fs", "mkswap", "mkdosfs", "debugfs","blkid"):
+ aname = "_%s" % name
+ if aname not in self.__dict__:
+ setattr(self, aname, find_executable(name, self.paths))
+ if aname not in self.__dict__ or self.__dict__[aname] is None:
+ raise WicError("Can't find executable '{}'".format(name))
+ return self.__dict__[aname]
+ return self.__dict__[name]
+
+ def _get_part_image(self, pnum):
+ if pnum not in self.partitions:
+ raise WicError("Partition %s is not in the image" % pnum)
+ part = self.partitions[pnum]
+ # check if fstype is supported
+ for fstype in self.fstypes:
+ if part.fstype.startswith(fstype):
+ break
+ else:
+ raise WicError("Not supported fstype: {}".format(part.fstype))
+ if pnum not in self._partimages:
+ tmpf = tempfile.NamedTemporaryFile(prefix="wic-part")
+ dst_fname = tmpf.name
+ tmpf.close()
+ sparse_copy(self.imagepath, dst_fname, skip=part.start, length=part.size)
+ self._partimages[pnum] = dst_fname
+
+ return self._partimages[pnum]
+
+ def _put_part_image(self, pnum):
+ """Put partition image into partitioned image."""
+ sparse_copy(self._partimages[pnum], self.imagepath,
+ seek=self.partitions[pnum].start)
+
+ def dir(self, pnum, path):
+ if pnum not in self.partitions:
+ raise WicError("Partition %s is not in the image" % pnum)
+
+ if self.partitions[pnum].fstype.startswith('ext'):
+ return exec_cmd("{} {} -R 'ls -l {}'".format(self.debugfs,
+ self._get_part_image(pnum),
+ path), as_shell=True)
+ else: # fat
+ return exec_cmd("{} -i {} ::{}".format(self.mdir,
+ self._get_part_image(pnum),
+ path))
+
+ def copy(self, src, dest):
+ """Copy partition image into wic image."""
+ pnum = dest.part if isinstance(src, str) else src.part
+
+ if self.partitions[pnum].fstype.startswith('ext'):
+ if isinstance(src, str):
+ cmd = "printf 'cd {}\nwrite {} {}\n' | {} -w {}".\
+ format(os.path.dirname(dest.path), src, os.path.basename(src),
+ self.debugfs, self._get_part_image(pnum))
+ else: # copy from wic
+ # run both dump and rdump to support both files and directory
+ cmd = "printf 'cd {}\ndump /{} {}\nrdump /{} {}\n' | {} {}".\
+ format(os.path.dirname(src.path), src.path,
+ dest, src.path, dest, self.debugfs,
+ self._get_part_image(pnum))
+ else: # fat
+ if isinstance(src, str):
+ cmd = "{} -i {} -snop {} ::{}".format(self.mcopy,
+ self._get_part_image(pnum),
+ src, dest.path)
+ else:
+ cmd = "{} -i {} -snop ::{} {}".format(self.mcopy,
+ self._get_part_image(pnum),
+ src.path, dest)
+
+ exec_cmd(cmd, as_shell=True)
+ self._put_part_image(pnum)
+
+ def remove_ext(self, pnum, path, recursive):
+ """
+ Remove files/dirs and their contents from the partition.
+ This only applies to ext* partition.
+ """
+ abs_path = re.sub('\/\/+', '/', path)
+ cmd = "{} {} -wR 'rm \"{}\"'".format(self.debugfs,
+ self._get_part_image(pnum),
+ abs_path)
+ out = exec_cmd(cmd , as_shell=True)
+ for line in out.splitlines():
+ if line.startswith("rm:"):
+ if "file is a directory" in line:
+ if recursive:
+ # loop through content and delete them one by one if
+ # flaged with -r
+ subdirs = iter(self.dir(pnum, abs_path).splitlines())
+ next(subdirs)
+ for subdir in subdirs:
+ dir = subdir.split(':')[1].split(" ", 1)[1]
+ if not dir == "." and not dir == "..":
+ self.remove_ext(pnum, "%s/%s" % (abs_path, dir), recursive)
+
+ rmdir_out = exec_cmd("{} {} -wR 'rmdir \"{}\"'".format(self.debugfs,
+ self._get_part_image(pnum),
+ abs_path.rstrip('/'))
+ , as_shell=True)
+
+ for rmdir_line in rmdir_out.splitlines():
+ if "directory not empty" in rmdir_line:
+ raise WicError("Could not complete operation: \n%s \n"
+ "use -r to remove non-empty directory" % rmdir_line)
+ if rmdir_line.startswith("rmdir:"):
+ raise WicError("Could not complete operation: \n%s "
+ "\n%s" % (str(line), rmdir_line))
+
+ else:
+ raise WicError("Could not complete operation: \n%s "
+ "\nUnable to remove %s" % (str(line), abs_path))
+
+ def remove(self, pnum, path, recursive):
+ """Remove files/dirs from the partition."""
+ partimg = self._get_part_image(pnum)
+ if self.partitions[pnum].fstype.startswith('ext'):
+ self.remove_ext(pnum, path, recursive)
+
+ else: # fat
+ cmd = "{} -i {} ::{}".format(self.mdel, partimg, path)
+ try:
+ exec_cmd(cmd)
+ except WicError as err:
+ if "not found" in str(err) or "non empty" in str(err):
+ # mdel outputs 'File ... not found' or 'directory .. non empty"
+ # try to use mdeltree as path could be a directory
+ cmd = "{} -i {} ::{}".format(self.mdeltree,
+ partimg, path)
+ exec_cmd(cmd)
+ else:
+ raise err
+ self._put_part_image(pnum)
+
+ def write(self, target, expand):
+ """Write disk image to the media or file."""
+ def write_sfdisk_script(outf, parts):
+ for key, val in parts['partitiontable'].items():
+ if key in ("partitions", "device", "firstlba", "lastlba"):
+ continue
+ if key == "id":
+ key = "label-id"
+ outf.write("{}: {}\n".format(key, val))
+ outf.write("\n")
+ for part in parts['partitiontable']['partitions']:
+ line = ''
+ for name in ('attrs', 'name', 'size', 'type', 'uuid'):
+ if name == 'size' and part['type'] == 'f':
+ # don't write size for extended partition
+ continue
+ val = part.get(name)
+ if val:
+ line += '{}={}, '.format(name, val)
+ if line:
+ line = line[:-2] # strip ', '
+ if part.get('bootable'):
+ line += ' ,bootable'
+ outf.write("{}\n".format(line))
+ outf.flush()
+
+ def read_ptable(path):
+ out = exec_cmd("{} -J {}".format(self.sfdisk, path))
+ return json.loads(out)
+
+ def write_ptable(parts, target):
+ with tempfile.NamedTemporaryFile(prefix="wic-sfdisk-", mode='w') as outf:
+ write_sfdisk_script(outf, parts)
+ cmd = "{} --no-reread {} < {} ".format(self.sfdisk, target, outf.name)
+ exec_cmd(cmd, as_shell=True)
+
+ if expand is None:
+ sparse_copy(self.imagepath, target)
+ else:
+ # copy first sectors that may contain bootloader
+ sparse_copy(self.imagepath, target, length=2048 * self._lsector_size)
+
+ # copy source partition table to the target
+ parts = read_ptable(self.imagepath)
+ write_ptable(parts, target)
+
+ # get size of unpartitioned space
+ free = None
+ for line in exec_cmd("{} -F {}".format(self.sfdisk, target)).splitlines():
+ if line.startswith("Unpartitioned space ") and line.endswith("sectors"):
+ free = int(line.split()[-2])
+ # Align free space to a 2048 sector boundary. YOCTO #12840.
+ free = free - (free % 2048)
+ if free is None:
+ raise WicError("Can't get size of unpartitioned space")
+
+ # calculate expanded partitions sizes
+ sizes = {}
+ num_auto_resize = 0
+ for num, part in enumerate(parts['partitiontable']['partitions'], 1):
+ if num in expand:
+ if expand[num] != 0: # don't resize partition if size is set to 0
+ sectors = expand[num] // self._lsector_size
+ free -= sectors - part['size']
+ part['size'] = sectors
+ sizes[num] = sectors
+ elif part['type'] != 'f':
+ sizes[num] = -1
+ num_auto_resize += 1
+
+ for num, part in enumerate(parts['partitiontable']['partitions'], 1):
+ if sizes.get(num) == -1:
+ part['size'] += free // num_auto_resize
+
+ # write resized partition table to the target
+ write_ptable(parts, target)
+
+ # read resized partition table
+ parts = read_ptable(target)
+
+ # copy partitions content
+ for num, part in enumerate(parts['partitiontable']['partitions'], 1):
+ pnum = str(num)
+ fstype = self.partitions[pnum].fstype
+
+ # copy unchanged partition
+ if part['size'] == self.partitions[pnum].size // self._lsector_size:
+ logger.info("copying unchanged partition {}".format(pnum))
+ sparse_copy(self._get_part_image(pnum), target, seek=part['start'] * self._lsector_size)
+ continue
+
+ # resize or re-create partitions
+ if fstype.startswith('ext') or fstype.startswith('fat') or \
+ fstype.startswith('linux-swap'):
+
+ partfname = None
+ with tempfile.NamedTemporaryFile(prefix="wic-part{}-".format(pnum)) as partf:
+ partfname = partf.name
+
+ if fstype.startswith('ext'):
+ logger.info("resizing ext partition {}".format(pnum))
+ partimg = self._get_part_image(pnum)
+ sparse_copy(partimg, partfname)
+ exec_cmd("{} -pf {}".format(self.e2fsck, partfname))
+ exec_cmd("{} {} {}s".format(\
+ self.resize2fs, partfname, part['size']))
+ elif fstype.startswith('fat'):
+ logger.info("copying content of the fat partition {}".format(pnum))
+ with tempfile.TemporaryDirectory(prefix='wic-fatdir-') as tmpdir:
+ # copy content to the temporary directory
+ cmd = "{} -snompi {} :: {}".format(self.mcopy,
+ self._get_part_image(pnum),
+ tmpdir)
+ exec_cmd(cmd)
+ # create new msdos partition
+ label = part.get("name")
+ label_str = "-n {}".format(label) if label else ''
+
+ cmd = "{} {} -C {} {}".format(self.mkdosfs, label_str, partfname,
+ part['size'])
+ exec_cmd(cmd)
+ # copy content from the temporary directory to the new partition
+ cmd = "{} -snompi {} {}/* ::".format(self.mcopy, partfname, tmpdir)
+ exec_cmd(cmd, as_shell=True)
+ elif fstype.startswith('linux-swap'):
+ logger.info("creating swap partition {}".format(pnum))
+ label = part.get("name")
+ label_str = "-L {}".format(label) if label else ''
+ out = exec_cmd("{} --probe {}".format(self.blkid, self._get_part_image(pnum)))
+ uuid = out[out.index("UUID=\"")+6:out.index("UUID=\"")+42]
+ uuid_str = "-U {}".format(uuid) if uuid else ''
+ with open(partfname, 'w') as sparse:
+ os.ftruncate(sparse.fileno(), part['size'] * self._lsector_size)
+ exec_cmd("{} {} {} {}".format(self.mkswap, label_str, uuid_str, partfname))
+ sparse_copy(partfname, target, seek=part['start'] * self._lsector_size)
+ os.unlink(partfname)
+ elif part['type'] != 'f':
+ logger.warning("skipping partition {}: unsupported fstype {}".format(pnum, fstype))
+
+def wic_ls(args, native_sysroot):
+ """List contents of partitioned image or vfat partition."""
+ disk = Disk(args.path.image, native_sysroot)
+ if not args.path.part:
+ if disk.partitions:
+ print('Num Start End Size Fstype')
+ for part in disk.partitions.values():
+ print("{:2d} {:12d} {:12d} {:12d} {}".format(\
+ part.pnum, part.start, part.end,
+ part.size, part.fstype))
+ else:
+ path = args.path.path or '/'
+ print(disk.dir(args.path.part, path))
+
+def wic_cp(args, native_sysroot):
+ """
+ Copy file or directory to/from the vfat/ext partition of
+ partitioned image.
+ """
+ if isinstance(args.dest, str):
+ disk = Disk(args.src.image, native_sysroot)
+ else:
+ disk = Disk(args.dest.image, native_sysroot)
+ disk.copy(args.src, args.dest)
+
+
+def wic_rm(args, native_sysroot):
+ """
+ Remove files or directories from the vfat partition of
+ partitioned image.
+ """
+ disk = Disk(args.path.image, native_sysroot)
+ disk.remove(args.path.part, args.path.path, args.recursive_delete)
+
+def wic_write(args, native_sysroot):
+ """
+ Write image to a target device.
+ """
+ disk = Disk(args.image, native_sysroot, ('fat', 'ext', 'linux-swap'))
+ disk.write(args.target, args.expand)
+
+def find_canned(scripts_path, file_name):
+ """
+ Find a file either by its path or by name in the canned files dir.
+
+ Return None if not found
+ """
+ if os.path.exists(file_name):
+ return file_name
+
+ layers_canned_wks_dir = build_canned_image_list(scripts_path)
+ for canned_wks_dir in layers_canned_wks_dir:
+ for root, dirs, files in os.walk(canned_wks_dir):
+ for fname in files:
+ if fname == file_name:
+ fullpath = os.path.join(canned_wks_dir, fname)
+ return fullpath
+
+def get_custom_config(boot_file):
+ """
+ Get the custom configuration to be used for the bootloader.
+
+ Return None if the file can't be found.
+ """
+ # Get the scripts path of poky
+ scripts_path = os.path.abspath("%s/../.." % os.path.dirname(__file__))
+
+ cfg_file = find_canned(scripts_path, boot_file)
+ if cfg_file:
+ with open(cfg_file, "r") as f:
+ config = f.read()
+ return config
diff --git a/scripts/lib/wic/filemap.py b/scripts/lib/wic/filemap.py
new file mode 100644
index 0000000000..4d9da28172
--- /dev/null
+++ b/scripts/lib/wic/filemap.py
@@ -0,0 +1,576 @@
+#
+# Copyright (c) 2012 Intel, Inc.
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+
+"""
+This module implements python implements a way to get file block. Two methods
+are supported - the FIEMAP ioctl and the 'SEEK_HOLE / SEEK_DATA' features of
+the file seek syscall. The former is implemented by the 'FilemapFiemap' class,
+the latter is implemented by the 'FilemapSeek' class. Both classes provide the
+same API. The 'filemap' function automatically selects which class can be used
+and returns an instance of the class.
+"""
+
+# Disable the following pylint recommendations:
+# * Too many instance attributes (R0902)
+# pylint: disable=R0902
+
+import errno
+import os
+import struct
+import array
+import fcntl
+import tempfile
+import logging
+
+def get_block_size(file_obj):
+ """
+ Returns block size for file object 'file_obj'. Errors are indicated by the
+ 'IOError' exception.
+ """
+ # Get the block size of the host file-system for the image file by calling
+ # the FIGETBSZ ioctl (number 2).
+ try:
+ binary_data = fcntl.ioctl(file_obj, 2, struct.pack('I', 0))
+ bsize = struct.unpack('I', binary_data)[0]
+ except OSError:
+ bsize = None
+
+ # If ioctl causes OSError or give bsize to zero failback to os.fstat
+ if not bsize:
+ import os
+ stat = os.fstat(file_obj.fileno())
+ if hasattr(stat, 'st_blksize'):
+ bsize = stat.st_blksize
+ else:
+ raise IOError("Unable to determine block size")
+ return bsize
+
+class ErrorNotSupp(Exception):
+ """
+ An exception of this type is raised when the 'FIEMAP' or 'SEEK_HOLE' feature
+ is not supported either by the kernel or the file-system.
+ """
+ pass
+
+class Error(Exception):
+ """A class for all the other exceptions raised by this module."""
+ pass
+
+
+class _FilemapBase(object):
+ """
+ This is a base class for a couple of other classes in this module. This
+ class simply performs the common parts of the initialization process: opens
+ the image file, gets its size, etc. The 'log' parameter is the logger object
+ to use for printing messages.
+ """
+
+ def __init__(self, image, log=None):
+ """
+ Initialize a class instance. The 'image' argument is full path to the
+ file or file object to operate on.
+ """
+
+ self._log = log
+ if self._log is None:
+ self._log = logging.getLogger(__name__)
+
+ self._f_image_needs_close = False
+
+ if hasattr(image, "fileno"):
+ self._f_image = image
+ self._image_path = image.name
+ else:
+ self._image_path = image
+ self._open_image_file()
+
+ try:
+ self.image_size = os.fstat(self._f_image.fileno()).st_size
+ except IOError as err:
+ raise Error("cannot get information about file '%s': %s"
+ % (self._f_image.name, err))
+
+ try:
+ self.block_size = get_block_size(self._f_image)
+ except IOError as err:
+ raise Error("cannot get block size for '%s': %s"
+ % (self._image_path, err))
+
+ self.blocks_cnt = self.image_size + self.block_size - 1
+ self.blocks_cnt //= self.block_size
+
+ try:
+ self._f_image.flush()
+ except IOError as err:
+ raise Error("cannot flush image file '%s': %s"
+ % (self._image_path, err))
+
+ try:
+ os.fsync(self._f_image.fileno()),
+ except OSError as err:
+ raise Error("cannot synchronize image file '%s': %s "
+ % (self._image_path, err.strerror))
+
+ self._log.debug("opened image \"%s\"" % self._image_path)
+ self._log.debug("block size %d, blocks count %d, image size %d"
+ % (self.block_size, self.blocks_cnt, self.image_size))
+
+ def __del__(self):
+ """The class destructor which just closes the image file."""
+ if self._f_image_needs_close:
+ self._f_image.close()
+
+ def _open_image_file(self):
+ """Open the image file."""
+ try:
+ self._f_image = open(self._image_path, 'rb')
+ except IOError as err:
+ raise Error("cannot open image file '%s': %s"
+ % (self._image_path, err))
+
+ self._f_image_needs_close = True
+
+ def block_is_mapped(self, block): # pylint: disable=W0613,R0201
+ """
+ This method has has to be implemented by child classes. It returns
+ 'True' if block number 'block' of the image file is mapped and 'False'
+ otherwise.
+ """
+
+ raise Error("the method is not implemented")
+
+ def get_mapped_ranges(self, start, count): # pylint: disable=W0613,R0201
+ """
+ This method has has to be implemented by child classes. This is a
+ generator which yields ranges of mapped blocks in the file. The ranges
+ are tuples of 2 elements: [first, last], where 'first' is the first
+ mapped block and 'last' is the last mapped block.
+
+ The ranges are yielded for the area of the file of size 'count' blocks,
+ starting from block 'start'.
+ """
+
+ raise Error("the method is not implemented")
+
+
+# The 'SEEK_HOLE' and 'SEEK_DATA' options of the file seek system call
+_SEEK_DATA = 3
+_SEEK_HOLE = 4
+
+def _lseek(file_obj, offset, whence):
+ """This is a helper function which invokes 'os.lseek' for file object
+ 'file_obj' and with specified 'offset' and 'whence'. The 'whence'
+ argument is supposed to be either '_SEEK_DATA' or '_SEEK_HOLE'. When
+ there is no more data or hole starting from 'offset', this function
+ returns '-1'. Otherwise the data or hole position is returned."""
+
+ try:
+ return os.lseek(file_obj.fileno(), offset, whence)
+ except OSError as err:
+ # The 'lseek' system call returns the ENXIO if there is no data or
+ # hole starting from the specified offset.
+ if err.errno == errno.ENXIO:
+ return -1
+ elif err.errno == errno.EINVAL:
+ raise ErrorNotSupp("the kernel or file-system does not support "
+ "\"SEEK_HOLE\" and \"SEEK_DATA\"")
+ else:
+ raise
+
+class FilemapSeek(_FilemapBase):
+ """
+ This class uses the 'SEEK_HOLE' and 'SEEK_DATA' to find file block mapping.
+ Unfortunately, the current implementation requires the caller to have write
+ access to the image file.
+ """
+
+ def __init__(self, image, log=None):
+ """Refer the '_FilemapBase' class for the documentation."""
+
+ # Call the base class constructor first
+ _FilemapBase.__init__(self, image, log)
+ self._log.debug("FilemapSeek: initializing")
+
+ self._probe_seek_hole()
+
+ def _probe_seek_hole(self):
+ """
+ Check whether the system implements 'SEEK_HOLE' and 'SEEK_DATA'.
+ Unfortunately, there seems to be no clean way for detecting this,
+ because often the system just fakes them by just assuming that all
+ files are fully mapped, so 'SEEK_HOLE' always returns EOF and
+ 'SEEK_DATA' always returns the requested offset.
+
+ I could not invent a better way of detecting the fake 'SEEK_HOLE'
+ implementation than just to create a temporary file in the same
+ directory where the image file resides. It would be nice to change this
+ to something better.
+ """
+
+ directory = os.path.dirname(self._image_path)
+
+ try:
+ tmp_obj = tempfile.TemporaryFile("w+", dir=directory)
+ except IOError as err:
+ raise ErrorNotSupp("cannot create a temporary in \"%s\": %s" \
+ % (directory, err))
+
+ try:
+ os.ftruncate(tmp_obj.fileno(), self.block_size)
+ except OSError as err:
+ raise ErrorNotSupp("cannot truncate temporary file in \"%s\": %s"
+ % (directory, err))
+
+ offs = _lseek(tmp_obj, 0, _SEEK_HOLE)
+ if offs != 0:
+ # We are dealing with the stub 'SEEK_HOLE' implementation which
+ # always returns EOF.
+ self._log.debug("lseek(0, SEEK_HOLE) returned %d" % offs)
+ raise ErrorNotSupp("the file-system does not support "
+ "\"SEEK_HOLE\" and \"SEEK_DATA\" but only "
+ "provides a stub implementation")
+
+ tmp_obj.close()
+
+ def block_is_mapped(self, block):
+ """Refer the '_FilemapBase' class for the documentation."""
+ offs = _lseek(self._f_image, block * self.block_size, _SEEK_DATA)
+ if offs == -1:
+ result = False
+ else:
+ result = (offs // self.block_size == block)
+
+ self._log.debug("FilemapSeek: block_is_mapped(%d) returns %s"
+ % (block, result))
+ return result
+
+ def _get_ranges(self, start, count, whence1, whence2):
+ """
+ This function implements 'get_mapped_ranges()' depending
+ on what is passed in the 'whence1' and 'whence2' arguments.
+ """
+
+ assert whence1 != whence2
+ end = start * self.block_size
+ limit = end + count * self.block_size
+
+ while True:
+ start = _lseek(self._f_image, end, whence1)
+ if start == -1 or start >= limit or start == self.image_size:
+ break
+
+ end = _lseek(self._f_image, start, whence2)
+ if end == -1 or end == self.image_size:
+ end = self.blocks_cnt * self.block_size
+ if end > limit:
+ end = limit
+
+ start_blk = start // self.block_size
+ end_blk = end // self.block_size - 1
+ self._log.debug("FilemapSeek: yielding range (%d, %d)"
+ % (start_blk, end_blk))
+ yield (start_blk, end_blk)
+
+ def get_mapped_ranges(self, start, count):
+ """Refer the '_FilemapBase' class for the documentation."""
+ self._log.debug("FilemapSeek: get_mapped_ranges(%d, %d(%d))"
+ % (start, count, start + count - 1))
+ return self._get_ranges(start, count, _SEEK_DATA, _SEEK_HOLE)
+
+
+# Below goes the FIEMAP ioctl implementation, which is not very readable
+# because it deals with the rather complex FIEMAP ioctl. To understand the
+# code, you need to know the FIEMAP interface, which is documented in the
+# "Documentation/filesystems/fiemap.txt" file in the Linux kernel sources.
+
+# Format string for 'struct fiemap'
+_FIEMAP_FORMAT = "=QQLLLL"
+# sizeof(struct fiemap)
+_FIEMAP_SIZE = struct.calcsize(_FIEMAP_FORMAT)
+# Format string for 'struct fiemap_extent'
+_FIEMAP_EXTENT_FORMAT = "=QQQQQLLLL"
+# sizeof(struct fiemap_extent)
+_FIEMAP_EXTENT_SIZE = struct.calcsize(_FIEMAP_EXTENT_FORMAT)
+# The FIEMAP ioctl number
+_FIEMAP_IOCTL = 0xC020660B
+# This FIEMAP ioctl flag which instructs the kernel to sync the file before
+# reading the block map
+_FIEMAP_FLAG_SYNC = 0x00000001
+# Size of the buffer for 'struct fiemap_extent' elements which will be used
+# when invoking the FIEMAP ioctl. The larger is the buffer, the less times the
+# FIEMAP ioctl will be invoked.
+_FIEMAP_BUFFER_SIZE = 256 * 1024
+
+class FilemapFiemap(_FilemapBase):
+ """
+ This class provides API to the FIEMAP ioctl. Namely, it allows to iterate
+ over all mapped blocks and over all holes.
+
+ This class synchronizes the image file every time it invokes the FIEMAP
+ ioctl in order to work-around early FIEMAP implementation kernel bugs.
+ """
+
+ def __init__(self, image, log=None):
+ """
+ Initialize a class instance. The 'image' argument is full the file
+ object to operate on.
+ """
+
+ # Call the base class constructor first
+ _FilemapBase.__init__(self, image, log)
+ self._log.debug("FilemapFiemap: initializing")
+
+ self._buf_size = _FIEMAP_BUFFER_SIZE
+
+ # Calculate how many 'struct fiemap_extent' elements fit the buffer
+ self._buf_size -= _FIEMAP_SIZE
+ self._fiemap_extent_cnt = self._buf_size // _FIEMAP_EXTENT_SIZE
+ assert self._fiemap_extent_cnt > 0
+ self._buf_size = self._fiemap_extent_cnt * _FIEMAP_EXTENT_SIZE
+ self._buf_size += _FIEMAP_SIZE
+
+ # Allocate a mutable buffer for the FIEMAP ioctl
+ self._buf = array.array('B', [0] * self._buf_size)
+
+ # Check if the FIEMAP ioctl is supported
+ self.block_is_mapped(0)
+
+ def _invoke_fiemap(self, block, count):
+ """
+ Invoke the FIEMAP ioctl for 'count' blocks of the file starting from
+ block number 'block'.
+
+ The full result of the operation is stored in 'self._buf' on exit.
+ Returns the unpacked 'struct fiemap' data structure in form of a python
+ list (just like 'struct.upack()').
+ """
+
+ if self.blocks_cnt != 0 and (block < 0 or block >= self.blocks_cnt):
+ raise Error("bad block number %d, should be within [0, %d]"
+ % (block, self.blocks_cnt))
+
+ # Initialize the 'struct fiemap' part of the buffer. We use the
+ # '_FIEMAP_FLAG_SYNC' flag in order to make sure the file is
+ # synchronized. The reason for this is that early FIEMAP
+ # implementations had many bugs related to cached dirty data, and
+ # synchronizing the file is a necessary work-around.
+ struct.pack_into(_FIEMAP_FORMAT, self._buf, 0, block * self.block_size,
+ count * self.block_size, _FIEMAP_FLAG_SYNC, 0,
+ self._fiemap_extent_cnt, 0)
+
+ try:
+ fcntl.ioctl(self._f_image, _FIEMAP_IOCTL, self._buf, 1)
+ except IOError as err:
+ # Note, the FIEMAP ioctl is supported by the Linux kernel starting
+ # from version 2.6.28 (year 2008).
+ if err.errno == errno.EOPNOTSUPP:
+ errstr = "FilemapFiemap: the FIEMAP ioctl is not supported " \
+ "by the file-system"
+ self._log.debug(errstr)
+ raise ErrorNotSupp(errstr)
+ if err.errno == errno.ENOTTY:
+ errstr = "FilemapFiemap: the FIEMAP ioctl is not supported " \
+ "by the kernel"
+ self._log.debug(errstr)
+ raise ErrorNotSupp(errstr)
+ raise Error("the FIEMAP ioctl failed for '%s': %s"
+ % (self._image_path, err))
+
+ return struct.unpack(_FIEMAP_FORMAT, self._buf[:_FIEMAP_SIZE])
+
+ def block_is_mapped(self, block):
+ """Refer the '_FilemapBase' class for the documentation."""
+ struct_fiemap = self._invoke_fiemap(block, 1)
+
+ # The 3rd element of 'struct_fiemap' is the 'fm_mapped_extents' field.
+ # If it contains zero, the block is not mapped, otherwise it is
+ # mapped.
+ result = bool(struct_fiemap[3])
+ self._log.debug("FilemapFiemap: block_is_mapped(%d) returns %s"
+ % (block, result))
+ return result
+
+ def _unpack_fiemap_extent(self, index):
+ """
+ Unpack a 'struct fiemap_extent' structure object number 'index' from
+ the internal 'self._buf' buffer.
+ """
+
+ offset = _FIEMAP_SIZE + _FIEMAP_EXTENT_SIZE * index
+ return struct.unpack(_FIEMAP_EXTENT_FORMAT,
+ self._buf[offset : offset + _FIEMAP_EXTENT_SIZE])
+
+ def _do_get_mapped_ranges(self, start, count):
+ """
+ Implements most the functionality for the 'get_mapped_ranges()'
+ generator: invokes the FIEMAP ioctl, walks through the mapped extents
+ and yields mapped block ranges. However, the ranges may be consecutive
+ (e.g., (1, 100), (100, 200)) and 'get_mapped_ranges()' simply merges
+ them.
+ """
+
+ block = start
+ while block < start + count:
+ struct_fiemap = self._invoke_fiemap(block, count)
+
+ mapped_extents = struct_fiemap[3]
+ if mapped_extents == 0:
+ # No more mapped blocks
+ return
+
+ extent = 0
+ while extent < mapped_extents:
+ fiemap_extent = self._unpack_fiemap_extent(extent)
+
+ # Start of the extent
+ extent_start = fiemap_extent[0]
+ # Starting block number of the extent
+ extent_block = extent_start // self.block_size
+ # Length of the extent
+ extent_len = fiemap_extent[2]
+ # Count of blocks in the extent
+ extent_count = extent_len // self.block_size
+
+ # Extent length and offset have to be block-aligned
+ assert extent_start % self.block_size == 0
+ assert extent_len % self.block_size == 0
+
+ if extent_block > start + count - 1:
+ return
+
+ first = max(extent_block, block)
+ last = min(extent_block + extent_count, start + count) - 1
+ yield (first, last)
+
+ extent += 1
+
+ block = extent_block + extent_count
+
+ def get_mapped_ranges(self, start, count):
+ """Refer the '_FilemapBase' class for the documentation."""
+ self._log.debug("FilemapFiemap: get_mapped_ranges(%d, %d(%d))"
+ % (start, count, start + count - 1))
+ iterator = self._do_get_mapped_ranges(start, count)
+ first_prev, last_prev = next(iterator)
+
+ for first, last in iterator:
+ if last_prev == first - 1:
+ last_prev = last
+ else:
+ self._log.debug("FilemapFiemap: yielding range (%d, %d)"
+ % (first_prev, last_prev))
+ yield (first_prev, last_prev)
+ first_prev, last_prev = first, last
+
+ self._log.debug("FilemapFiemap: yielding range (%d, %d)"
+ % (first_prev, last_prev))
+ yield (first_prev, last_prev)
+
+class FilemapNobmap(_FilemapBase):
+ """
+ This class is used when both the 'SEEK_DATA/HOLE' and FIEMAP are not
+ supported by the filesystem or kernel.
+ """
+
+ def __init__(self, image, log=None):
+ """Refer the '_FilemapBase' class for the documentation."""
+
+ # Call the base class constructor first
+ _FilemapBase.__init__(self, image, log)
+ self._log.debug("FilemapNobmap: initializing")
+
+ def block_is_mapped(self, block):
+ """Refer the '_FilemapBase' class for the documentation."""
+ return True
+
+ def get_mapped_ranges(self, start, count):
+ """Refer the '_FilemapBase' class for the documentation."""
+ self._log.debug("FilemapNobmap: get_mapped_ranges(%d, %d(%d))"
+ % (start, count, start + count - 1))
+ yield (start, start + count -1)
+
+def filemap(image, log=None):
+ """
+ Create and return an instance of a Filemap class - 'FilemapFiemap' or
+ 'FilemapSeek', depending on what the system we run on supports. If the
+ FIEMAP ioctl is supported, an instance of the 'FilemapFiemap' class is
+ returned. Otherwise, if 'SEEK_HOLE' is supported an instance of the
+ 'FilemapSeek' class is returned. If none of these are supported, the
+ function generates an 'Error' type exception.
+ """
+
+ try:
+ return FilemapFiemap(image, log)
+ except ErrorNotSupp:
+ try:
+ return FilemapSeek(image, log)
+ except ErrorNotSupp:
+ return FilemapNobmap(image, log)
+
+def sparse_copy(src_fname, dst_fname, skip=0, seek=0,
+ length=0, api=None):
+ """
+ Efficiently copy sparse file to or into another file.
+
+ src_fname: path to source file
+ dst_fname: path to destination file
+ skip: skip N bytes at thestart of src
+ seek: seek N bytes from the start of dst
+ length: read N bytes from src and write them to dst
+ api: FilemapFiemap or FilemapSeek object
+ """
+ if not api:
+ api = filemap
+ fmap = api(src_fname)
+ try:
+ dst_file = open(dst_fname, 'r+b')
+ except IOError:
+ dst_file = open(dst_fname, 'wb')
+ if length:
+ dst_size = length + seek
+ else:
+ dst_size = os.path.getsize(src_fname) + seek - skip
+ dst_file.truncate(dst_size)
+
+ written = 0
+ for first, last in fmap.get_mapped_ranges(0, fmap.blocks_cnt):
+ start = first * fmap.block_size
+ end = (last + 1) * fmap.block_size
+
+ if skip >= end:
+ continue
+
+ if start < skip < end:
+ start = skip
+
+ fmap._f_image.seek(start, os.SEEK_SET)
+
+ written += start - skip - written
+ if length and written >= length:
+ dst_file.seek(seek + length, os.SEEK_SET)
+ dst_file.close()
+ return
+
+ dst_file.seek(seek + start - skip, os.SEEK_SET)
+
+ chunk_size = 1024 * 1024
+ to_read = end - start
+ read = 0
+
+ while read < to_read:
+ if read + chunk_size > to_read:
+ chunk_size = to_read - read
+ size = chunk_size
+ if length and written + size > length:
+ size = length - written
+ chunk = fmap._f_image.read(size)
+ dst_file.write(chunk)
+ read += size
+ written += size
+ if written == length:
+ dst_file.close()
+ return
+ dst_file.close()
diff --git a/scripts/lib/wic/help.py b/scripts/lib/wic/help.py
index 9a778b69da..991907d3f5 100644
--- a/scripts/lib/wic/help.py
+++ b/scripts/lib/wic/help.py
@@ -1,21 +1,6 @@
-# ex:ts=4:sw=4:sts=4:et
-# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
-#
# Copyright (c) 2013, Intel Corporation.
-# All rights reserved.
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 as
-# published by the Free Software Foundation.
#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+# SPDX-License-Identifier: GPL-2.0-only
#
# DESCRIPTION
# This module implements some basic help invocation functions along
@@ -28,10 +13,12 @@
import subprocess
import logging
-from wic.plugin import pluginmgr, PLUGIN_TYPES
+from wic.pluginbase import PluginMgr, PLUGIN_TYPES
+
+logger = logging.getLogger('wic')
def subcommand_error(args):
- logging.info("invalid subcommand %s" % args[0])
+ logger.info("invalid subcommand %s", args[0])
def display_help(subcommand, subcommands):
@@ -45,7 +32,7 @@ def display_help(subcommand, subcommands):
if callable(hlp):
hlp = hlp()
pager = subprocess.Popen('less', stdin=subprocess.PIPE)
- pager.communicate(hlp)
+ pager.communicate(hlp.encode('utf-8'))
return True
@@ -54,8 +41,8 @@ def wic_help(args, usage_str, subcommands):
"""
Subcommand help dispatcher.
"""
- if len(args) == 1 or not display_help(args[1], subcommands):
- print usage_str
+ if args.help_topic == None or not display_help(args.help_topic, subcommands):
+ print(usage_str)
def get_wic_plugins_help():
@@ -66,7 +53,7 @@ def get_wic_plugins_help():
result = wic_plugins_help
for plugin_type in PLUGIN_TYPES:
result += '\n\n%s PLUGINS\n\n' % plugin_type.upper()
- for name, plugin in pluginmgr.get_plugins(plugin_type).iteritems():
+ for name, plugin in PluginMgr.get_plugins(plugin_type).items():
result += "\n %s plugin:\n" % name
if plugin.__doc__:
result += plugin.__doc__
@@ -80,19 +67,20 @@ def invoke_subcommand(args, parser, main_command_usage, subcommands):
Dispatch to subcommand handler borrowed from combo-layer.
Should use argparse, but has to work in 2.6.
"""
- if not args:
- logging.error("No subcommand specified, exiting")
+ if not args.command:
+ logger.error("No subcommand specified, exiting")
parser.print_help()
return 1
- elif args[0] == "help":
+ elif args.command == "help":
wic_help(args, main_command_usage, subcommands)
- elif args[0] not in subcommands:
- logging.error("Unsupported subcommand %s, exiting\n" % (args[0]))
+ elif args.command not in subcommands:
+ logger.error("Unsupported subcommand %s, exiting\n", args.command)
parser.print_help()
return 1
else:
- usage = subcommands.get(args[0], subcommand_error)[1]
- subcommands.get(args[0], subcommand_error)[0](args[1:], usage)
+ subcmd = subcommands.get(args.command, subcommand_error)
+ usage = subcmd[1]
+ subcmd[0](args, usage)
##
@@ -128,10 +116,10 @@ wic_create_usage = """
Create a new OpenEmbedded image
usage: wic create <wks file or image name> [-o <DIRNAME> | --outdir <DIRNAME>]
- [-i <JSON PROPERTY FILE> | --infile <JSON PROPERTY_FILE>]
[-e | --image-name] [-s, --skip-build-check] [-D, --debug]
[-r, --rootfs-dir] [-b, --bootimg-dir]
[-k, --kernel-dir] [-n, --native-sysroot] [-f, --build-rootfs]
+ [-c, --compress-with] [-m, --bmap]
This command creates an OpenEmbedded image based on the 'OE kickstart
commands' found in the <wks file>.
@@ -152,7 +140,7 @@ SYNOPSIS
[-e | --image-name] [-s, --skip-build-check] [-D, --debug]
[-r, --rootfs-dir] [-b, --bootimg-dir]
[-k, --kernel-dir] [-n, --native-sysroot] [-f, --build-rootfs]
- [-c, --compress-with]
+ [-c, --compress-with] [-m, --bmap] [--no-fstab-update]
DESCRIPTION
This command creates an OpenEmbedded image based on the 'OE
@@ -221,6 +209,14 @@ DESCRIPTION
The -c option is used to specify compressor utility to compress
an image. gzip, bzip2 and xz compressors are supported.
+
+ The -m option is used to produce .bmap file for the image. This file
+ can be used to flash image using bmaptool utility.
+
+ The --no-fstab-update option is used to doesn't change fstab file. When
+ using this option the final fstab file will be same that in rootfs and
+ wic doesn't update file, e.g adding a new mount point. User can control
+ the fstab file content in base-files recipe.
"""
wic_list_usage = """
@@ -278,6 +274,243 @@ DESCRIPTION
details.
"""
+wic_ls_usage = """
+
+ List content of a partitioned image
+
+ usage: wic ls <image>[:<partition>[<path>]] [--native-sysroot <path>]
+
+ This command outputs either list of image partitions or directory contents
+ of vfat and ext* partitions.
+
+ See 'wic help ls' for more detailed instructions.
+
+"""
+
+wic_ls_help = """
+
+NAME
+ wic ls - List contents of partitioned image or partition
+
+SYNOPSIS
+ wic ls <image>
+ wic ls <image>:<vfat or ext* partition>
+ wic ls <image>:<vfat or ext* partition><path>
+ wic ls <image>:<vfat or ext* partition><path> --native-sysroot <path>
+
+DESCRIPTION
+ This command lists either partitions of the image or directory contents
+ of vfat or ext* partitions.
+
+ The first form it lists partitions of the image.
+ For example:
+ $ wic ls tmp/deploy/images/qemux86-64/core-image-minimal-qemux86-64.wic
+ Num Start End Size Fstype
+ 1 1048576 24438783 23390208 fat16
+ 2 25165824 50315263 25149440 ext4
+
+ Second and third form list directory content of the partition:
+ $ wic ls tmp/deploy/images/qemux86-64/core-image-minimal-qemux86-64.wic:1
+ Volume in drive : is boot
+ Volume Serial Number is 2DF2-5F02
+ Directory for ::/
+
+ efi <DIR> 2017-05-11 10:54
+ startup nsh 26 2017-05-11 10:54
+ vmlinuz 6922288 2017-05-11 10:54
+ 3 files 6 922 314 bytes
+ 15 818 752 bytes free
+
+
+ $ wic ls tmp/deploy/images/qemux86-64/core-image-minimal-qemux86-64.wic:1/EFI/boot/
+ Volume in drive : is boot
+ Volume Serial Number is 2DF2-5F02
+ Directory for ::/EFI/boot
+
+ . <DIR> 2017-05-11 10:54
+ .. <DIR> 2017-05-11 10:54
+ grub cfg 679 2017-05-11 10:54
+ bootx64 efi 571392 2017-05-11 10:54
+ 4 files 572 071 bytes
+ 15 818 752 bytes free
+
+ The -n option is used to specify the path to the native sysroot
+ containing the tools(parted and mtools) to use.
+
+"""
+
+wic_cp_usage = """
+
+ Copy files and directories to/from the vfat or ext* partition
+
+ usage: wic cp <src> <dest> [--native-sysroot <path>]
+
+ source/destination image in format <image>:<partition>[<path>]
+
+ This command copies files or directories either
+ - from local to vfat or ext* partitions of partitioned image
+ - from vfat or ext* partitions of partitioned image to local
+
+ See 'wic help cp' for more detailed instructions.
+
+"""
+
+wic_cp_help = """
+
+NAME
+ wic cp - copy files and directories to/from the vfat or ext* partitions
+
+SYNOPSIS
+ wic cp <src> <dest>:<partition>
+ wic cp <src>:<partition> <dest>
+ wic cp <src> <dest-image>:<partition><path>
+ wic cp <src> <dest-image>:<partition><path> --native-sysroot <path>
+
+DESCRIPTION
+ This command copies files or directories either
+ - from local to vfat or ext* partitions of partitioned image
+ - from vfat or ext* partitions of partitioned image to local
+
+ The first form of it copies file or directory to the root directory of
+ the partition:
+ $ wic cp test.wks tmp/deploy/images/qemux86-64/core-image-minimal-qemux86-64.wic:1
+ $ wic ls tmp/deploy/images/qemux86-64/core-image-minimal-qemux86-64.wic:1
+ Volume in drive : is boot
+ Volume Serial Number is DB4C-FD4C
+ Directory for ::/
+
+ efi <DIR> 2017-05-24 18:15
+ loader <DIR> 2017-05-24 18:15
+ startup nsh 26 2017-05-24 18:15
+ vmlinuz 6926384 2017-05-24 18:15
+ test wks 628 2017-05-24 21:22
+ 5 files 6 927 038 bytes
+ 15 677 440 bytes free
+
+ The second form of the command copies file or directory to the specified directory
+ on the partition:
+ $ wic cp test tmp/deploy/images/qemux86-64/core-image-minimal-qemux86-64.wic:1/efi/
+ $ wic ls tmp/deploy/images/qemux86-64/core-image-minimal-qemux86-64.wic:1/efi/
+ Volume in drive : is boot
+ Volume Serial Number is DB4C-FD4C
+ Directory for ::/efi
+
+ . <DIR> 2017-05-24 18:15
+ .. <DIR> 2017-05-24 18:15
+ boot <DIR> 2017-05-24 18:15
+ test <DIR> 2017-05-24 21:27
+ 4 files 0 bytes
+ 15 675 392 bytes free
+
+ The third form of the command copies file or directory from the specified directory
+ on the partition to local:
+ $ wic cp tmp/deploy/images/qemux86-64/core-image-minimal-qemux86-64.wic:1/vmlinuz test
+
+ The -n option is used to specify the path to the native sysroot
+ containing the tools(parted and mtools) to use.
+"""
+
+wic_rm_usage = """
+
+ Remove files or directories from the vfat or ext* partitions
+
+ usage: wic rm <image>:<partition><path> [--native-sysroot <path>]
+
+ This command removes files or directories from the vfat or ext* partitions of
+ the partitioned image.
+
+ See 'wic help rm' for more detailed instructions.
+
+"""
+
+wic_rm_help = """
+
+NAME
+ wic rm - remove files or directories from the vfat or ext* partitions
+
+SYNOPSIS
+ wic rm <src> <image>:<partition><path>
+ wic rm <src> <image>:<partition><path> --native-sysroot <path>
+ wic rm -r <image>:<partition><path>
+
+DESCRIPTION
+ This command removes files or directories from the vfat or ext* partition of the
+ partitioned image:
+
+ $ wic ls ./tmp/deploy/images/qemux86-64/core-image-minimal-qemux86-64.wic:1
+ Volume in drive : is boot
+ Volume Serial Number is 11D0-DE21
+ Directory for ::/
+
+ libcom32 c32 186500 2017-06-02 15:15
+ libutil c32 24148 2017-06-02 15:15
+ syslinux cfg 209 2017-06-02 15:15
+ vesamenu c32 27104 2017-06-02 15:15
+ vmlinuz 6926384 2017-06-02 15:15
+ 5 files 7 164 345 bytes
+ 16 582 656 bytes free
+
+ $ wic rm ./tmp/deploy/images/qemux86-64/core-image-minimal-qemux86-64.wic:1/libutil.c32
+
+ $ wic ls ./tmp/deploy/images/qemux86-64/core-image-minimal-qemux86-64.wic:1
+ Volume in drive : is boot
+ Volume Serial Number is 11D0-DE21
+ Directory for ::/
+
+ libcom32 c32 186500 2017-06-02 15:15
+ syslinux cfg 209 2017-06-02 15:15
+ vesamenu c32 27104 2017-06-02 15:15
+ vmlinuz 6926384 2017-06-02 15:15
+ 4 files 7 140 197 bytes
+ 16 607 232 bytes free
+
+ The -n option is used to specify the path to the native sysroot
+ containing the tools(parted and mtools) to use.
+
+ The -r option is used to remove directories and their contents
+ recursively,this only applies to ext* partition.
+"""
+
+wic_write_usage = """
+
+ Write image to a device
+
+ usage: wic write <image> <target device> [--expand [rules]] [--native-sysroot <path>]
+
+ This command writes partitioned image to a target device (USB stick, SD card etc).
+
+ See 'wic help write' for more detailed instructions.
+
+"""
+
+wic_write_help = """
+
+NAME
+ wic write - write an image to a device
+
+SYNOPSIS
+ wic write <image> <target>
+ wic write <image> <target> --expand auto
+ wic write <image> <target> --expand 1:100M,2:300M
+ wic write <image> <target> --native-sysroot <path>
+
+DESCRIPTION
+ This command writes an image to a target device (USB stick, SD card etc)
+
+ $ wic write ./tmp/deploy/images/qemux86-64/core-image-minimal-qemux86-64.wic /dev/sdb
+
+ The --expand option is used to resize image partitions.
+ --expand auto expands partitions to occupy all free space available on the target device.
+ It's also possible to specify expansion rules in a format
+ <partition>:<size>[,<partition>:<size>...] for one or more partitions.
+ Specifying size 0 will keep partition unmodified.
+ Note: Resizing boot partition can result in non-bootable image for non-EFI images. It is
+ recommended to use size 0 for boot partition to keep image bootable.
+
+ The --native-sysroot option is used to specify the path to the native sysroot
+ containing the tools(parted, resize2fs) to use.
+"""
+
wic_plugins_help = """
NAME
@@ -303,7 +536,8 @@ DESCRIPTION
Source plugins can also be implemented and added by external
layers - any plugins found in a scripts/lib/wic/plugins/source/
- directory in an external layer will also be made available.
+ or lib/wic/plugins/source/ directory in an external layer will
+ also be made available.
When the wic implementation needs to invoke a partition-specific
implementation, it looks for the plugin that has the same name as
@@ -341,6 +575,10 @@ DESCRIPTION
partition. In other words, it 'prepares' the final partition
image which will be incorporated into the disk image.
+ do_post_partition()
+ Called after the partition is created. It is useful to add post
+ operations e.g. signing the partition.
+
do_configure_partition()
Called before do_prepare_partition(), typically used to
create custom configuration files for a partition, for
@@ -368,12 +606,7 @@ DESCRIPTION
This scheme is extensible - adding more hooks is a simple matter
of adding more plugin methods to SourcePlugin and derived classes.
- The code that then needs to call the plugin methods uses
- plugin.get_source_plugin_methods() to find the method(s) needed by
- the call; this is done by filling up a dict with keys containing
- the method names of interest - on success, these will be filled in
- with the actual methods. Please see the implementation for
- examples and details.
+ Please see the implementation for details.
"""
wic_overview_help = """
@@ -404,7 +637,7 @@ DESCRIPTION
oe-core: directdisk.bbclass and mkefidisk.sh. The difference
between wic and those examples is that with wic the functionality
of those scripts is implemented by a general-purpose partitioning
- 'language' based on Redhat kickstart syntax).
+ 'language' based on Red Hat kickstart syntax).
The initial motivation and design considerations that lead to the
current tool are described exhaustively in Yocto Bug #3847
@@ -617,7 +850,7 @@ DESCRIPTION
This command creates a partition on the system and uses the
following syntax:
- part <mountpoint>
+ part [<mountpoint>]
The <mountpoint> is where the partition will be mounted and
must take of one of the following forms:
@@ -626,6 +859,19 @@ DESCRIPTION
swap: The partition will be used as swap space.
+ If a <mountpoint> is not specified the partition will be created
+ but will not be mounted.
+
+ Partitions with a <mountpoint> specified will be automatically mounted.
+ This is achieved by wic adding entries to the fstab during image
+ generation. In order for a valid fstab to be generated one of the
+ --ondrive, --ondisk, --use-uuid or --use-label partition options must
+ be used for each partition that specifies a mountpoint. Note that with
+ --use-{uuid,label} and non-root <mountpoint>, including swap, the mount
+ program must understand the PARTUUID or LABEL syntax. This currently
+ excludes the busybox versions of these applications.
+
+
The following are supported 'part' options:
--size: The minimum partition size. Specify an integer value
@@ -633,6 +879,12 @@ DESCRIPTION
not specified, the size is in MB.
You do not need this option if you use --source.
+ --fixed-size: Exact partition size. Value format is the same
+ as for --size option. This option cannot be
+ specified along with --size. If partition data
+ is larger than --fixed-size and error will be
+ raised when assembling disk image.
+
--source: This option is a wic-specific option that names the
source of the data that will populate the
partition. The most common value for this option
@@ -671,11 +923,14 @@ DESCRIPTION
apply to partitions created using '--source rootfs' (see
--source above). Valid values are:
+ vfat
+ msdos
ext2
ext3
ext4
btrfs
squashfs
+ erofs
swap
--fsoptions: Specifies a free-form string of options to be
@@ -690,6 +945,14 @@ DESCRIPTION
label is already in use by another filesystem,
a new label is created for the partition.
+ --use-label: This option is specific to wic. It makes wic to use the
+ label in /etc/fstab to specify a partition. If the
+ --use-label and --use-uuid are used at the same time,
+ we prefer the uuid because it is less likely to cause
+ name confliction. We don't support using this parameter
+ on the root partition since it requires an initramfs to
+ parse this value and we do not currently support that.
+
--active: Marks the partition as active.
--align (in KBytes): This option is specific to wic and says
@@ -702,17 +965,50 @@ DESCRIPTION
partition table. It may be useful for
bootloaders.
+ --exclude-path: This option is specific to wic. It excludes the given
+ relative path from the resulting image. If the path
+ ends with a slash, only the content of the directory
+ is omitted, not the directory itself. This option only
+ has an effect with the rootfs source plugin.
+
+ --include-path: This option is specific to wic. It adds the contents
+ of the given path or a rootfs to the resulting image.
+ The option contains two fields, the origin and the
+ destination. When the origin is a rootfs, it follows
+ the same logic as the rootfs-dir argument and the
+ permissions and owners are kept. When the origin is a
+ path, it is relative to the directory in which wic is
+ running not the rootfs itself so use of an absolute
+ path is recommended, and the owner and group is set to
+ root:root. If no destination is given it is
+ automatically set to the root of the rootfs. This
+ option only has an effect with the rootfs source
+ plugin.
+
+ --change-directory: This option is specific to wic. It changes to the
+ given directory before copying the files. This
+ option is useful when we want to split a rootfs in
+ multiple partitions and we want to keep the right
+ permissions and usernames in all the partitions.
+
+ --no-fstab-update: This option is specific to wic. It does not update the
+ '/etc/fstab' stock file for the given partition.
+
--extra-space: This option is specific to wic. It adds extra
space after the space filled by the content
of the partition. The final size can go
beyond the size specified by --size.
- By default, 10MB.
+ By default, 10MB. This option cannot be used
+ with --fixed-size option.
--overhead-factor: This option is specific to wic. The
size of the partition is multiplied by
this factor. It has to be greater than or
- equal to 1.
- The default value is 1.3.
+ equal to 1. The default value is 1.3.
+ This option cannot be used with --fixed-size
+ option.
+
+ --part-name: This option is specific to wic. It specifies name for GPT partitions.
--part-type: This option is specific to wic. It specifies partition
type GUID for GPT partitions.
@@ -728,6 +1024,21 @@ DESCRIPTION
in bootloader configuration before running wic. In this case .wks file can
be generated or modified to set preconfigured parition UUID using this option.
+ --fsuuid: This option is specific to wic. It specifies filesystem UUID.
+ It's useful if preconfigured filesystem UUID is added to kernel command line
+ in bootloader configuration before running wic. In this case .wks file can
+ be generated or modified to set preconfigured filesystem UUID using this option.
+
+ --system-id: This option is specific to wic. It specifies partition system id. It's useful
+ for the harware that requires non-default partition system ids. The parameter
+ in one byte long hex number either with 0x prefix or without it.
+
+ --mkfs-extraopts: This option specifies extra options to pass to mkfs utility.
+ NOTE, that wic uses default options for some filesystems, for example
+ '-S 512' for mkfs.fat or '-F -i 8192' for mkfs.ext. Those options will
+ not take effect when --mkfs-extraopts is used. This should be taken into
+ account when using --mkfs-extraopts.
+
* bootloader
This command allows the user to specify various bootloader
@@ -740,8 +1051,92 @@ DESCRIPTION
bootloader command-line - for example, the syslinux
APPEND or grub kernel command line.
+ --configfile: Specifies a user defined configuration file for
+ the bootloader. This file must be located in the
+ canned-wks folder or could be the full path to the
+ file. Using this option will override any other
+ bootloader option.
+
Note that bootloader functionality and boot partitions are
implemented by the various --source plugins that implement
bootloader functionality; the bootloader command essentially
provides a means of modifying bootloader configuration.
+
+ * include
+
+ This command allows the user to include the content of .wks file
+ into original .wks file.
+
+ Command uses the following syntax:
+
+ include <file>
+
+ The <file> is either path to the file or its name. If name is
+ specified wic will try to find file in the directories with canned
+ .wks files.
+
+"""
+
+wic_help_help = """
+NAME
+ wic help - display a help topic
+
+DESCRIPTION
+ Specify a help topic to display it. Topics are shown above.
+"""
+
+
+wic_help = """
+Creates a customized OpenEmbedded image.
+
+Usage: wic [--version]
+ wic help [COMMAND or TOPIC]
+ wic COMMAND [ARGS]
+
+ usage 1: Returns the current version of Wic
+ usage 2: Returns detailed help for a COMMAND or TOPIC
+ usage 3: Executes COMMAND
+
+
+COMMAND:
+
+ list - List available canned images and source plugins
+ ls - List contents of partitioned image or partition
+ rm - Remove files or directories from the vfat or ext* partitions
+ help - Show help for a wic COMMAND or TOPIC
+ write - Write an image to a device
+ cp - Copy files and directories to the vfat or ext* partitions
+ create - Create a new OpenEmbedded image
+
+
+TOPIC:
+ overview - Presents an overall overview of Wic
+ plugins - Presents an overview and API for Wic plugins
+ kickstart - Presents a Wic kicstart file reference
+
+
+Examples:
+
+ $ wic --version
+
+ Returns the current version of Wic
+
+
+ $ wic help cp
+
+ Returns the SYNOPSIS and DESCRIPTION for the Wic "cp" command.
+
+
+ $ wic list images
+
+ Returns the list of canned images (i.e. *.wks files located in
+ the /scripts/lib/wic/canned-wks directory.
+
+
+ $ wic create mkefidisk -e core-image-minimal
+
+ Creates an EFI disk image from artifacts used in a previous
+ core-image-minimal build in standard BitBake locations
+ (e.g. Cooked Mode).
+
"""
diff --git a/scripts/lib/wic/imager/__init__.py b/scripts/lib/wic/imager/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
--- a/scripts/lib/wic/imager/__init__.py
+++ /dev/null
diff --git a/scripts/lib/wic/imager/baseimager.py b/scripts/lib/wic/imager/baseimager.py
deleted file mode 100644
index acbe948584..0000000000
--- a/scripts/lib/wic/imager/baseimager.py
+++ /dev/null
@@ -1,192 +0,0 @@
-#!/usr/bin/env python -tt
-#
-# Copyright (c) 2007 Red Hat Inc.
-# Copyright (c) 2009, 2010, 2011 Intel, Inc.
-#
-# This program is free software; you can redistribute it and/or modify it
-# under the terms of the GNU General Public License as published by the Free
-# Software Foundation; version 2 of the License
-#
-# This program is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
-# for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc., 59
-# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-from __future__ import with_statement
-import os
-import tempfile
-import shutil
-
-from wic import msger
-from wic.utils.errors import CreatorError
-from wic.utils import runner
-
-class BaseImageCreator(object):
- """Base class for image creation.
-
- BaseImageCreator is the simplest creator class available; it will
- create a system image according to the supplied kickstart file.
-
- e.g.
-
- import wic.imgcreate as imgcreate
- ks = imgcreate.read_kickstart("foo.ks")
- imgcreate.ImageCreator(ks, "foo").create()
- """
-
- def __del__(self):
- self.cleanup()
-
- def __init__(self, createopts=None):
- """Initialize an ImageCreator instance.
-
- ks -- a pykickstart.KickstartParser instance; this instance will be
- used to drive the install by e.g. providing the list of packages
- to be installed, the system configuration and %post scripts
-
- name -- a name for the image; used for e.g. image filenames or
- filesystem labels
- """
-
- self.__builddir = None
-
- self.ks = None
- self.name = "target"
- self.tmpdir = "/var/tmp/wic"
- self.workdir = "/var/tmp/wic/build"
-
- # setup tmpfs tmpdir when enabletmpfs is True
- self.enabletmpfs = False
-
- if createopts:
- # Mapping table for variables that have different names.
- optmap = {"outdir" : "destdir",
- }
-
- # update setting from createopts
- for key in createopts.keys():
- if key in optmap:
- option = optmap[key]
- else:
- option = key
- setattr(self, option, createopts[key])
-
- self.destdir = os.path.abspath(os.path.expanduser(self.destdir))
-
- self._dep_checks = ["ls", "bash", "cp", "echo"]
-
- # Output image file names
- self.outimage = []
-
- # No ks provided when called by convertor, so skip the dependency check
- if self.ks:
- # If we have btrfs partition we need to check necessary tools
- for part in self.ks.handler.partition.partitions:
- if part.fstype and part.fstype == "btrfs":
- self._dep_checks.append("mkfs.btrfs")
- break
-
- # make sure the specified tmpdir and cachedir exist
- if not os.path.exists(self.tmpdir):
- os.makedirs(self.tmpdir)
-
-
- #
- # Hooks for subclasses
- #
- def _create(self):
- """Create partitions for the disk image(s)
-
- This is the hook where subclasses may create the partitions
- that will be assembled into disk image(s).
-
- There is no default implementation.
- """
- pass
-
- def _cleanup(self):
- """Undo anything performed in _create().
-
- This is the hook where subclasses must undo anything which was
- done in _create().
-
- There is no default implementation.
-
- """
- pass
-
- #
- # Actual implementation
- #
- def __ensure_builddir(self):
- if not self.__builddir is None:
- return
-
- try:
- self.workdir = os.path.join(self.tmpdir, "build")
- if not os.path.exists(self.workdir):
- os.makedirs(self.workdir)
- self.__builddir = tempfile.mkdtemp(dir=self.workdir,
- prefix="imgcreate-")
- except OSError as err:
- raise CreatorError("Failed create build directory in %s: %s" %
- (self.tmpdir, err))
-
- def __setup_tmpdir(self):
- if not self.enabletmpfs:
- return
-
- runner.show('mount -t tmpfs -o size=4G tmpfs %s' % self.workdir)
-
- def __clean_tmpdir(self):
- if not self.enabletmpfs:
- return
-
- runner.show('umount -l %s' % self.workdir)
-
- def create(self):
- """Create partitions for the disk image(s)
-
- Create the partitions that will be assembled into disk
- image(s).
- """
- self.__setup_tmpdir()
- self.__ensure_builddir()
-
- self._create()
-
- def cleanup(self):
- """Undo anything performed in create().
-
- Note, make sure to call this method once finished with the creator
- instance in order to ensure no stale files are left on the host e.g.:
-
- creator = ImageCreator(ks, name)
- try:
- creator.create()
- finally:
- creator.cleanup()
-
- """
- if not self.__builddir:
- return
-
- self._cleanup()
-
- shutil.rmtree(self.__builddir, ignore_errors=True)
- self.__builddir = None
-
- self.__clean_tmpdir()
-
-
- def print_outimage_info(self):
- msg = "The new image can be found here:\n"
- self.outimage.sort()
- for path in self.outimage:
- msg += ' %s\n' % os.path.abspath(path)
-
- msger.info(msg)
diff --git a/scripts/lib/wic/imager/direct.py b/scripts/lib/wic/imager/direct.py
deleted file mode 100644
index 31c0edc7d3..0000000000
--- a/scripts/lib/wic/imager/direct.py
+++ /dev/null
@@ -1,378 +0,0 @@
-# ex:ts=4:sw=4:sts=4:et
-# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
-#
-# Copyright (c) 2013, Intel Corporation.
-# All rights reserved.
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 as
-# published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-#
-# DESCRIPTION
-# This implements the 'direct' image creator class for 'wic'
-#
-# AUTHORS
-# Tom Zanussi <tom.zanussi (at] linux.intel.com>
-#
-
-import os
-import shutil
-
-from wic import kickstart, msger
-from wic.utils import fs_related
-from wic.utils.oe.misc import get_bitbake_var
-from wic.utils.partitionedfs import Image
-from wic.utils.errors import CreatorError, ImageError
-from wic.imager.baseimager import BaseImageCreator
-from wic.plugin import pluginmgr
-from wic.utils.oe.misc import exec_cmd
-
-disk_methods = {
- "do_install_disk":None,
-}
-
-class DirectImageCreator(BaseImageCreator):
- """
- Installs a system into a file containing a partitioned disk image.
-
- DirectImageCreator is an advanced ImageCreator subclass; an image
- file is formatted with a partition table, each partition created
- from a rootfs or other OpenEmbedded build artifact and dd'ed into
- the virtual disk. The disk image can subsequently be dd'ed onto
- media and used on actual hardware.
- """
-
- def __init__(self, oe_builddir, image_output_dir, rootfs_dir, bootimg_dir,
- kernel_dir, native_sysroot, compressor, creatoropts=None):
- """
- Initialize a DirectImageCreator instance.
-
- This method takes the same arguments as ImageCreator.__init__()
- """
- BaseImageCreator.__init__(self, creatoropts)
-
- self.__image = None
- self.__disks = {}
- self.__disk_format = "direct"
- self._disk_names = []
- self.ptable_format = self.ks.handler.bootloader.ptable
-
- self.oe_builddir = oe_builddir
- if image_output_dir:
- self.tmpdir = image_output_dir
- self.rootfs_dir = rootfs_dir
- self.bootimg_dir = bootimg_dir
- self.kernel_dir = kernel_dir
- self.native_sysroot = native_sysroot
- self.compressor = compressor
-
- def __get_part_num(self, num, parts):
- """calculate the real partition number, accounting for partitions not
- in the partition table and logical partitions
- """
- realnum = 0
- for pnum, part in enumerate(parts, 1):
- if not part.no_table:
- realnum += 1
- if pnum == num:
- if part.no_table:
- return 0
- if self.ptable_format == 'msdos' and realnum > 3:
- # account for logical partition numbering, ex. sda5..
- return realnum + 1
- return realnum
-
- def _write_fstab(self, image_rootfs):
- """overriden to generate fstab (temporarily) in rootfs. This is called
- from _create, make sure it doesn't get called from
- BaseImage.create()
- """
- if not image_rootfs:
- return
-
- fstab_path = image_rootfs + "/etc/fstab"
- if not os.path.isfile(fstab_path):
- return
-
- with open(fstab_path) as fstab:
- fstab_lines = fstab.readlines()
-
- if self._update_fstab(fstab_lines, self._get_parts()):
- shutil.copyfile(fstab_path, fstab_path + ".orig")
-
- with open(fstab_path, "w") as fstab:
- fstab.writelines(fstab_lines)
-
- return fstab_path
-
- def _update_fstab(self, fstab_lines, parts):
- """Assume partition order same as in wks"""
- updated = False
- for num, part in enumerate(parts, 1):
- pnum = self.__get_part_num(num, parts)
- if not pnum or not part.mountpoint \
- or part.mountpoint in ("/", "/boot"):
- continue
-
- # mmc device partitions are named mmcblk0p1, mmcblk0p2..
- prefix = 'p' if part.disk.startswith('mmcblk') else ''
- device_name = "/dev/%s%s%d" % (part.disk, prefix, pnum)
-
- opts = part.fsopts if part.fsopts else "defaults"
- line = "\t".join([device_name, part.mountpoint, part.fstype,
- opts, "0", "0"]) + "\n"
-
- fstab_lines.append(line)
- updated = True
-
- return updated
-
- def set_bootimg_dir(self, bootimg_dir):
- """
- Accessor for bootimg_dir, the actual location used for the source
- of the bootimg. Should be set by source plugins (only if they
- change the default bootimg source) so the correct info gets
- displayed for print_outimage_info().
- """
- self.bootimg_dir = bootimg_dir
-
- def _get_parts(self):
- if not self.ks:
- raise CreatorError("Failed to get partition info, "
- "please check your kickstart setting.")
-
- # Set a default partition if no partition is given out
- if not self.ks.handler.partition.partitions:
- partstr = "part / --size 1900 --ondisk sda --fstype=ext3"
- args = partstr.split()
- part = self.ks.handler.partition.parse(args[1:])
- if part not in self.ks.handler.partition.partitions:
- self.ks.handler.partition.partitions.append(part)
-
- # partitions list from kickstart file
- return kickstart.get_partitions(self.ks)
-
- def get_disk_names(self):
- """ Returns a list of physical target disk names (e.g., 'sdb') which
- will be created. """
-
- if self._disk_names:
- return self._disk_names
-
- #get partition info from ks handler
- parts = self._get_parts()
-
- for i in range(len(parts)):
- if parts[i].disk:
- disk_name = parts[i].disk
- else:
- raise CreatorError("Failed to create disks, no --ondisk "
- "specified in partition line of ks file")
-
- if parts[i].mountpoint and not parts[i].fstype:
- raise CreatorError("Failed to create disks, no --fstype "
- "specified for partition with mountpoint "
- "'%s' in the ks file")
-
- self._disk_names.append(disk_name)
-
- return self._disk_names
-
- def _full_name(self, name, extention):
- """ Construct full file name for a file we generate. """
- return "%s-%s.%s" % (self.name, name, extention)
-
- def _full_path(self, path, name, extention):
- """ Construct full file path to a file we generate. """
- return os.path.join(path, self._full_name(name, extention))
-
- def get_default_source_plugin(self):
- """
- The default source plugin i.e. the plugin that's consulted for
- overall image generation tasks outside of any particular
- partition. For convenience, we just hang it off the
- bootloader handler since it's the one non-partition object in
- any setup. By default the default plugin is set to the same
- plugin as the /boot partition; since we hang it off the
- bootloader object, the default can be explicitly set using the
- --source bootloader param.
- """
- return self.ks.handler.bootloader.source
-
- #
- # Actual implemention
- #
- def _create(self):
- """
- For 'wic', we already have our build artifacts - we just create
- filesystems from the artifacts directly and combine them into
- a partitioned image.
- """
- parts = self._get_parts()
-
- self.__image = Image(self.native_sysroot)
-
- for part in parts:
- # as a convenience, set source to the boot partition source
- # instead of forcing it to be set via bootloader --source
- if not self.ks.handler.bootloader.source and part.mountpoint == "/boot":
- self.ks.handler.bootloader.source = part.source
-
- fstab_path = self._write_fstab(self.rootfs_dir.get("ROOTFS_DIR"))
-
- for part in parts:
- # get rootfs size from bitbake variable if it's not set in .ks file
- if not part.size:
- # and if rootfs name is specified for the partition
- image_name = part.get_rootfs()
- if image_name:
- # Bitbake variable ROOTFS_SIZE is calculated in
- # Image._get_rootfs_size method from meta/lib/oe/image.py
- # using IMAGE_ROOTFS_SIZE, IMAGE_ROOTFS_ALIGNMENT,
- # IMAGE_OVERHEAD_FACTOR and IMAGE_ROOTFS_EXTRA_SPACE
- rsize_bb = get_bitbake_var('ROOTFS_SIZE', image_name)
- if rsize_bb:
- # convert from Kb to Mb
- part.size = int(rsize_bb) / 1024
- # need to create the filesystems in order to get their
- # sizes before we can add them and do the layout.
- # Image.create() actually calls __format_disks() to create
- # the disk images and carve out the partitions, then
- # self.assemble() calls Image.assemble() which calls
- # __write_partitition() for each partition to dd the fs
- # into the partitions.
- part.prepare(self, self.workdir, self.oe_builddir, self.rootfs_dir,
- self.bootimg_dir, self.kernel_dir, self.native_sysroot)
-
-
- self.__image.add_partition(int(part.size),
- part.disk,
- part.mountpoint,
- part.source_file,
- part.fstype,
- part.label,
- fsopts=part.fsopts,
- boot=part.active,
- align=part.align,
- no_table=part.no_table,
- part_type=part.part_type,
- uuid=part.uuid)
-
- if fstab_path:
- shutil.move(fstab_path + ".orig", fstab_path)
-
- self.__image.layout_partitions(self.ptable_format)
-
- self.__imgdir = self.workdir
- for disk_name, disk in self.__image.disks.items():
- full_path = self._full_path(self.__imgdir, disk_name, "direct")
- msger.debug("Adding disk %s as %s with size %s bytes" \
- % (disk_name, full_path, disk['min_size']))
- disk_obj = fs_related.DiskImage(full_path, disk['min_size'])
- self.__disks[disk_name] = disk_obj
- self.__image.add_disk(disk_name, disk_obj)
-
- self.__image.create()
-
- def assemble(self):
- """
- Assemble partitions into disk image(s)
- """
- for disk_name, disk in self.__image.disks.items():
- full_path = self._full_path(self.__imgdir, disk_name, "direct")
- msger.debug("Assembling disk %s as %s with size %s bytes" \
- % (disk_name, full_path, disk['min_size']))
- self.__image.assemble(full_path)
-
- def finalize(self):
- """
- Finalize the disk image.
-
- For example, prepare the image to be bootable by e.g.
- creating and installing a bootloader configuration.
-
- """
- source_plugin = self.get_default_source_plugin()
- if source_plugin:
- self._source_methods = pluginmgr.get_source_plugin_methods(source_plugin, disk_methods)
- for disk_name, disk in self.__image.disks.items():
- self._source_methods["do_install_disk"](disk, disk_name, self,
- self.workdir,
- self.oe_builddir,
- self.bootimg_dir,
- self.kernel_dir,
- self.native_sysroot)
- # Compress the image
- if self.compressor:
- for disk_name, disk in self.__image.disks.items():
- full_path = self._full_path(self.__imgdir, disk_name, "direct")
- msger.debug("Compressing disk %s with %s" % \
- (disk_name, self.compressor))
- exec_cmd("%s %s" % (self.compressor, full_path))
-
- def print_outimage_info(self):
- """
- Print the image(s) and artifacts used, for the user.
- """
- msg = "The new image(s) can be found here:\n"
-
- parts = self._get_parts()
-
- for disk_name in self.__image.disks:
- extension = "direct" + {"gzip": ".gz",
- "bzip2": ".bz2",
- "xz": ".xz",
- "": ""}.get(self.compressor)
- full_path = self._full_path(self.__imgdir, disk_name, extension)
- msg += ' %s\n\n' % full_path
-
- msg += 'The following build artifacts were used to create the image(s):\n'
- for part in parts:
- if part.get_rootfs() is None:
- continue
- if part.mountpoint == '/':
- suffix = ':'
- else:
- suffix = '["%s"]:' % (part.mountpoint or part.label)
- msg += ' ROOTFS_DIR%s%s\n' % (suffix.ljust(20), part.get_rootfs())
-
- msg += ' BOOTIMG_DIR: %s\n' % self.bootimg_dir
- msg += ' KERNEL_DIR: %s\n' % self.kernel_dir
- msg += ' NATIVE_SYSROOT: %s\n' % self.native_sysroot
-
- msger.info(msg)
-
- @property
- def rootdev(self):
- """
- Get root device name to use as a 'root' parameter
- in kernel command line.
-
- Assume partition order same as in wks
- """
- parts = self._get_parts()
- for num, part in enumerate(parts, 1):
- if part.mountpoint == "/":
- if part.uuid:
- return "PARTUUID=%s" % part.uuid
- else:
- suffix = 'p' if part.disk.startswith('mmcblk') else ''
- pnum = self.__get_part_num(num, parts)
- return "/dev/%s%s%-d" % (part.disk, suffix, pnum)
-
- def _cleanup(self):
- if not self.__image is None:
- try:
- self.__image.cleanup()
- except ImageError, err:
- msger.warning("%s" % err)
-
diff --git a/scripts/lib/wic/kickstart/__init__.py b/scripts/lib/wic/kickstart/__init__.py
deleted file mode 100644
index c9b0e51f3c..0000000000
--- a/scripts/lib/wic/kickstart/__init__.py
+++ /dev/null
@@ -1,122 +0,0 @@
-#!/usr/bin/env python -tt
-#
-# Copyright (c) 2007 Red Hat, Inc.
-# Copyright (c) 2009, 2010, 2011 Intel, Inc.
-#
-# This program is free software; you can redistribute it and/or modify it
-# under the terms of the GNU General Public License as published by the Free
-# Software Foundation; version 2 of the License
-#
-# This program is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
-# for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc., 59
-# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-import os, sys, re
-import shutil
-import subprocess
-import string
-
-import pykickstart.sections as kssections
-import pykickstart.commands as kscommands
-import pykickstart.constants as ksconstants
-import pykickstart.errors as kserrors
-import pykickstart.parser as ksparser
-import pykickstart.version as ksversion
-from pykickstart.handlers.control import commandMap
-from pykickstart.handlers.control import dataMap
-
-from wic import msger
-from wic.utils import errors, misc, runner, fs_related as fs
-from custom_commands import wicboot, partition
-
-def read_kickstart(path):
- """Parse a kickstart file and return a KickstartParser instance.
-
- This is a simple utility function which takes a path to a kickstart file,
- parses it and returns a pykickstart KickstartParser instance which can
- be then passed to an ImageCreator constructor.
-
- If an error occurs, a CreatorError exception is thrown.
- """
-
- #version = ksversion.makeVersion()
- #ks = ksparser.KickstartParser(version)
-
- using_version = ksversion.DEVEL
- commandMap[using_version]["bootloader"] = wicboot.Wic_Bootloader
- commandMap[using_version]["part"] = partition.Wic_Partition
- commandMap[using_version]["partition"] = partition.Wic_Partition
- dataMap[using_version]["PartData"] = partition.Wic_PartData
- superclass = ksversion.returnClassForVersion(version=using_version)
-
- class KSHandlers(superclass):
- def __init__(self):
- superclass.__init__(self, mapping=commandMap[using_version])
-
- kickstart = ksparser.KickstartParser(KSHandlers(), errorsAreFatal=True)
-
- try:
- kickstart.readKickstart(path)
- except (kserrors.KickstartParseError, kserrors.KickstartError), err:
- msger.warning("Errors occurred when parsing kickstart file: %s\n" % path)
- msger.error("%s" % err)
-
- return kickstart
-
-def get_image_size(kickstart, default=None):
- __size = 0
- for part in kickstart.handler.partition.partitions:
- if part.mountpoint == "/" and part.size:
- __size = part.size
- if __size > 0:
- return int(__size) * 1024L
- else:
- return default
-
-def get_image_fstype(kickstart, default=None):
- for part in kickstart.handler.partition.partitions:
- if part.mountpoint == "/" and part.fstype:
- return part.fstype
- return default
-
-def get_image_fsopts(kickstart, default=None):
- for part in kickstart.handler.partition.partitions:
- if part.mountpoint == "/" and part.fsopts:
- return part.fsopts
- return default
-
-def get_timeout(kickstart, default=None):
- if not hasattr(kickstart.handler.bootloader, "timeout"):
- return default
- if kickstart.handler.bootloader.timeout is None:
- return default
- return int(kickstart.handler.bootloader.timeout)
-
-def get_kernel_args(kickstart, default="ro rd.live.image"):
- if not hasattr(kickstart.handler.bootloader, "appendLine"):
- return default
- if kickstart.handler.bootloader.appendLine is None:
- return default
- return "%s %s" %(default, kickstart.handler.bootloader.appendLine)
-
-def get_menu_args(kickstart, default=""):
- if not hasattr(kickstart.handler.bootloader, "menus"):
- return default
- if kickstart.handler.bootloader.menus in (None, ""):
- return default
- return "%s" % kickstart.handler.bootloader.menus
-
-def get_default_kernel(kickstart, default=None):
- if not hasattr(kickstart.handler.bootloader, "default"):
- return default
- if not kickstart.handler.bootloader.default:
- return default
- return kickstart.handler.bootloader.default
-
-def get_partitions(kickstart):
- return kickstart.handler.partition.partitions
diff --git a/scripts/lib/wic/kickstart/custom_commands/__init__.py b/scripts/lib/wic/kickstart/custom_commands/__init__.py
deleted file mode 100644
index e4ae40622c..0000000000
--- a/scripts/lib/wic/kickstart/custom_commands/__init__.py
+++ /dev/null
@@ -1,7 +0,0 @@
-from partition import Wic_Partition
-from partition import Wic_PartData
-
-__all__ = (
- "Wic_Partition",
- "Wic_PartData",
-)
diff --git a/scripts/lib/wic/kickstart/custom_commands/partition.py b/scripts/lib/wic/kickstart/custom_commands/partition.py
deleted file mode 100644
index eee25a493d..0000000000
--- a/scripts/lib/wic/kickstart/custom_commands/partition.py
+++ /dev/null
@@ -1,526 +0,0 @@
-# ex:ts=4:sw=4:sts=4:et
-# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
-#
-# Copyright (c) 2013, Intel Corporation.
-# All rights reserved.
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 as
-# published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-#
-# DESCRIPTION
-# This module provides the OpenEmbedded partition object definitions.
-#
-# AUTHORS
-# Tom Zanussi <tom.zanussi (at] linux.intel.com>
-#
-
-import os
-import tempfile
-import uuid
-from optparse import OptionValueError
-
-from pykickstart.commands.partition import FC4_PartData, FC4_Partition
-from wic.utils.oe.misc import msger, parse_sourceparams
-from wic.utils.oe.misc import exec_cmd, exec_native_cmd
-from wic.plugin import pluginmgr
-
-partition_methods = {
- "do_stage_partition":None,
- "do_prepare_partition":None,
- "do_configure_partition":None,
-}
-
-class Wic_PartData(FC4_PartData):
- removedKeywords = FC4_PartData.removedKeywords
- removedAttrs = FC4_PartData.removedAttrs
-
- def __init__(self, *args, **kwargs):
- FC4_PartData.__init__(self, *args, **kwargs)
- self.deleteRemovedAttrs()
- self.align = kwargs.get("align", None)
- self.extopts = kwargs.get("extopts", None)
- self.part_type = kwargs.get("part_type", None)
- self.source = kwargs.get("source", None)
- self.sourceparams = kwargs.get("sourceparams", None)
- self.rootfs = kwargs.get("rootfs-dir", None)
- self.no_table = kwargs.get("no-table", False)
- self.extra_space = kwargs.get("extra-space", "10M")
- self.overhead_factor = kwargs.get("overhead-factor", 1.3)
- self._use_uuid = False
- self.uuid = kwargs.get("uuid", None)
- self.use_uuid = kwargs.get("use-uuid", False)
- self.source_file = ""
- self.size = 0
-
- def _getArgsAsStr(self):
- retval = FC4_PartData._getArgsAsStr(self)
-
- if self.align:
- retval += " --align=%d" % self.align
- if self.extopts:
- retval += " --extoptions=%s" % self.extopts
- if self.part_type:
- retval += " --part-type=%s" % self.part_type
- if self.source:
- retval += " --source=%s" % self.source
- if self.sourceparams:
- retval += " --sourceparams=%s" % self.sourceparams
- if self.rootfs:
- retval += " --rootfs-dir=%s" % self.rootfs
- if self.no_table:
- retval += " --no-table"
- if self.use_uuid:
- retval += " --use-uuid"
- if self.uuid:
- retval += " --uuid=%s" % self.uuid
- retval += " --extra-space=%s" % self.extra_space
- retval += " --overhead-factor=%f" % self.overhead_factor
-
- return retval
-
- @property
- def use_uuid(self):
- return self._use_uuid
-
- @use_uuid.setter
- def use_uuid(self, value):
- self._use_uuid = value
- if value and not self.uuid:
- self.uuid = str(uuid.uuid4())
-
- def get_rootfs(self):
- """
- Acessor for rootfs dir
- """
- return self.rootfs
-
- def set_rootfs(self, rootfs):
- """
- Acessor for actual rootfs dir, which must be set by source
- plugins.
- """
- self.rootfs = rootfs
-
- def get_size(self):
- """
- Accessor for partition size, 0 or --size before set_size().
- """
- return self.size
-
- def set_size(self, size):
- """
- Accessor for actual partition size, which must be set by source
- plugins.
- """
- self.size = size
-
- def set_source_file(self, source_file):
- """
- Accessor for source_file, the location of the generated partition
- image, which must be set by source plugins.
- """
- self.source_file = source_file
-
- def get_extra_block_count(self, current_blocks):
- """
- The --size param is reflected in self.size (in kB), and we already
- have current_blocks (1k) blocks, calculate and return the
- number of (1k) blocks we need to add to get to --size, 0 if
- we're already there or beyond.
- """
- msger.debug("Requested partition size for %s: %d" % \
- (self.mountpoint, self.size))
-
- if not self.size:
- return 0
-
- requested_blocks = self.size
-
- msger.debug("Requested blocks %d, current_blocks %d" % \
- (requested_blocks, current_blocks))
-
- if requested_blocks > current_blocks:
- return requested_blocks - current_blocks
- else:
- return 0
-
- def prepare(self, creator, cr_workdir, oe_builddir, rootfs_dir, bootimg_dir,
- kernel_dir, native_sysroot):
- """
- Prepare content for individual partitions, depending on
- partition command parameters.
- """
- self.sourceparams_dict = {}
-
- if self.sourceparams:
- self.sourceparams_dict = parse_sourceparams(self.sourceparams)
-
- if not self.source:
- if not self.size:
- msger.error("The %s partition has a size of zero. Please "
- "specify a non-zero --size for that partition." % \
- self.mountpoint)
- if self.fstype and self.fstype == "swap":
- self.prepare_swap_partition(cr_workdir, oe_builddir,
- native_sysroot)
- elif self.fstype:
- rootfs = "%s/fs_%s.%s.%s" % (cr_workdir, self.label,
- self.lineno, self.fstype)
- if os.path.isfile(rootfs):
- os.remove(rootfs)
- for prefix in ("ext", "btrfs", "vfat", "squashfs"):
- if self.fstype.startswith(prefix):
- method = getattr(self,
- "prepare_empty_partition_" + prefix)
- method(rootfs, oe_builddir, native_sysroot)
- self.source_file = rootfs
- break
- return
-
- plugins = pluginmgr.get_source_plugins()
-
- if self.source not in plugins:
- msger.error("The '%s' --source specified for %s doesn't exist.\n\t"
- "See 'wic list source-plugins' for a list of available"
- " --sources.\n\tSee 'wic help source-plugins' for "
- "details on adding a new source plugin." % \
- (self.source, self.mountpoint))
-
- self._source_methods = pluginmgr.get_source_plugin_methods(\
- self.source, partition_methods)
- self._source_methods["do_configure_partition"](self, self.sourceparams_dict,
- creator, cr_workdir,
- oe_builddir,
- bootimg_dir,
- kernel_dir,
- native_sysroot)
- self._source_methods["do_stage_partition"](self, self.sourceparams_dict,
- creator, cr_workdir,
- oe_builddir,
- bootimg_dir, kernel_dir,
- native_sysroot)
- self._source_methods["do_prepare_partition"](self, self.sourceparams_dict,
- creator, cr_workdir,
- oe_builddir,
- bootimg_dir, kernel_dir, rootfs_dir,
- native_sysroot)
-
- def prepare_rootfs_from_fs_image(self, cr_workdir, oe_builddir,
- rootfs_dir):
- """
- Handle an already-created partition e.g. xxx.ext3
- """
- rootfs = oe_builddir
- du_cmd = "du -Lbks %s" % rootfs
- out = exec_cmd(du_cmd)
- rootfs_size = out.split()[0]
-
- self.size = rootfs_size
- self.source_file = rootfs
-
- def prepare_rootfs(self, cr_workdir, oe_builddir, rootfs_dir,
- native_sysroot):
- """
- Prepare content for a rootfs partition i.e. create a partition
- and fill it from a /rootfs dir.
-
- Currently handles ext2/3/4, btrfs and vfat.
- """
- p_prefix = os.environ.get("PSEUDO_PREFIX", "%s/usr" % native_sysroot)
- p_localstatedir = os.environ.get("PSEUDO_LOCALSTATEDIR",
- "%s/../pseudo" % rootfs_dir)
- p_passwd = os.environ.get("PSEUDO_PASSWD", rootfs_dir)
- p_nosymlinkexp = os.environ.get("PSEUDO_NOSYMLINKEXP", "1")
- pseudo = "export PSEUDO_PREFIX=%s;" % p_prefix
- pseudo += "export PSEUDO_LOCALSTATEDIR=%s;" % p_localstatedir
- pseudo += "export PSEUDO_PASSWD=%s;" % p_passwd
- pseudo += "export PSEUDO_NOSYMLINKEXP=%s;" % p_nosymlinkexp
- pseudo += "%s/usr/bin/pseudo " % native_sysroot
-
- rootfs = "%s/rootfs_%s.%s.%s" % (cr_workdir, self.label,
- self.lineno, self.fstype)
- if os.path.isfile(rootfs):
- os.remove(rootfs)
-
- for prefix in ("ext", "btrfs", "vfat", "squashfs"):
- if self.fstype.startswith(prefix):
- method = getattr(self, "prepare_rootfs_" + prefix)
- method(rootfs, oe_builddir, rootfs_dir, native_sysroot, pseudo)
-
- self.source_file = rootfs
-
- # get the rootfs size in the right units for kickstart (kB)
- du_cmd = "du -Lbks %s" % rootfs
- out = exec_cmd(du_cmd)
- self.size = out.split()[0]
-
- break
-
- def prepare_rootfs_ext(self, rootfs, oe_builddir, rootfs_dir,
- native_sysroot, pseudo):
- """
- Prepare content for an ext2/3/4 rootfs partition.
- """
- du_cmd = "du -ks %s" % rootfs_dir
- out = exec_cmd(du_cmd)
- actual_rootfs_size = int(out.split()[0])
-
- extra_blocks = self.get_extra_block_count(actual_rootfs_size)
- if extra_blocks < self.extra_space:
- extra_blocks = self.extra_space
-
- rootfs_size = actual_rootfs_size + extra_blocks
- rootfs_size *= self.overhead_factor
-
- msger.debug("Added %d extra blocks to %s to get to %d total blocks" % \
- (extra_blocks, self.mountpoint, rootfs_size))
-
- dd_cmd = "dd if=/dev/zero of=%s bs=1024 seek=%d count=0 bs=1k" % \
- (rootfs, rootfs_size)
- exec_cmd(dd_cmd)
-
- extra_imagecmd = "-i 8192"
-
- label_str = ""
- if self.label:
- label_str = "-L %s" % self.label
-
- mkfs_cmd = "mkfs.%s -F %s %s %s -d %s" % \
- (self.fstype, extra_imagecmd, rootfs, label_str, rootfs_dir)
- exec_native_cmd(pseudo + mkfs_cmd, native_sysroot)
-
- def prepare_rootfs_btrfs(self, rootfs, oe_builddir, rootfs_dir,
- native_sysroot, pseudo):
- """
- Prepare content for a btrfs rootfs partition.
-
- Currently handles ext2/3/4 and btrfs.
- """
- du_cmd = "du -ks %s" % rootfs_dir
- out = exec_cmd(du_cmd)
- actual_rootfs_size = int(out.split()[0])
-
- extra_blocks = self.get_extra_block_count(actual_rootfs_size)
- if extra_blocks < self.extra_space:
- extra_blocks = self.extra_space
-
- rootfs_size = actual_rootfs_size + extra_blocks
- rootfs_size *= self.overhead_factor
-
- msger.debug("Added %d extra blocks to %s to get to %d total blocks" % \
- (extra_blocks, self.mountpoint, rootfs_size))
-
- dd_cmd = "dd if=/dev/zero of=%s bs=1024 seek=%d count=0 bs=1k" % \
- (rootfs, rootfs_size)
- exec_cmd(dd_cmd)
-
- label_str = ""
- if self.label:
- label_str = "-L %s" % self.label
-
- mkfs_cmd = "mkfs.%s -b %d -r %s %s %s" % \
- (self.fstype, rootfs_size * 1024, rootfs_dir, label_str, rootfs)
- exec_native_cmd(pseudo + mkfs_cmd, native_sysroot)
-
- def prepare_rootfs_vfat(self, rootfs, oe_builddir, rootfs_dir,
- native_sysroot, pseudo):
- """
- Prepare content for a vfat rootfs partition.
- """
- du_cmd = "du -bks %s" % rootfs_dir
- out = exec_cmd(du_cmd)
- blocks = int(out.split()[0])
-
- extra_blocks = self.get_extra_block_count(blocks)
- if extra_blocks < self.extra_space:
- extra_blocks = self.extra_space
-
- blocks += extra_blocks
-
- msger.debug("Added %d extra blocks to %s to get to %d total blocks" % \
- (extra_blocks, self.mountpoint, blocks))
-
- # Ensure total sectors is an integral number of sectors per
- # track or mcopy will complain. Sectors are 512 bytes, and we
- # generate images with 32 sectors per track. This calculation
- # is done in blocks, thus the mod by 16 instead of 32. Apply
- # sector count fix only when needed.
- if blocks % 16 != 0:
- blocks += (16 - (blocks % 16))
-
- label_str = "-n boot"
- if self.label:
- label_str = "-n %s" % self.label
-
- dosfs_cmd = "mkdosfs %s -S 512 -C %s %d" % (label_str, rootfs, blocks)
- exec_native_cmd(dosfs_cmd, native_sysroot)
-
- mcopy_cmd = "mcopy -i %s -s %s/* ::/" % (rootfs, rootfs_dir)
- exec_native_cmd(mcopy_cmd, native_sysroot)
-
- chmod_cmd = "chmod 644 %s" % rootfs
- exec_cmd(chmod_cmd)
-
- def prepare_rootfs_squashfs(self, rootfs, oe_builddir, rootfs_dir,
- native_sysroot, pseudo):
- """
- Prepare content for a squashfs rootfs partition.
- """
- squashfs_cmd = "mksquashfs %s %s -noappend" % \
- (rootfs_dir, rootfs)
- exec_native_cmd(pseudo + squashfs_cmd, native_sysroot)
-
- def prepare_empty_partition_ext(self, rootfs, oe_builddir,
- native_sysroot):
- """
- Prepare an empty ext2/3/4 partition.
- """
- dd_cmd = "dd if=/dev/zero of=%s bs=1k seek=%d count=0" % \
- (rootfs, self.size)
- exec_cmd(dd_cmd)
-
- extra_imagecmd = "-i 8192"
-
- label_str = ""
- if self.label:
- label_str = "-L %s" % self.label
-
- mkfs_cmd = "mkfs.%s -F %s %s %s" % \
- (self.fstype, extra_imagecmd, label_str, rootfs)
- exec_native_cmd(mkfs_cmd, native_sysroot)
-
- def prepare_empty_partition_btrfs(self, rootfs, oe_builddir,
- native_sysroot):
- """
- Prepare an empty btrfs partition.
- """
- dd_cmd = "dd if=/dev/zero of=%s bs=1k seek=%d count=0" % \
- (rootfs, self.size)
- exec_cmd(dd_cmd)
-
- label_str = ""
- if self.label:
- label_str = "-L %s" % self.label
-
- mkfs_cmd = "mkfs.%s -b %d %s %s" % \
- (self.fstype, self.size * 1024, label_str, rootfs)
- exec_native_cmd(mkfs_cmd, native_sysroot)
-
- def prepare_empty_partition_vfat(self, rootfs, oe_builddir,
- native_sysroot):
- """
- Prepare an empty vfat partition.
- """
- blocks = self.size
-
- label_str = "-n boot"
- if self.label:
- label_str = "-n %s" % self.label
-
- dosfs_cmd = "mkdosfs %s -S 512 -C %s %d" % (label_str, rootfs, blocks)
- exec_native_cmd(dosfs_cmd, native_sysroot)
-
- chmod_cmd = "chmod 644 %s" % rootfs
- exec_cmd(chmod_cmd)
-
- def prepare_empty_partition_squashfs(self, cr_workdir, oe_builddir,
- native_sysroot):
- """
- Prepare an empty squashfs partition.
- """
- msger.warning("Creating of an empty squashfs %s partition was attempted. " \
- "Proceeding as requested." % self.mountpoint)
-
- path = "%s/fs_%s.%s" % (cr_workdir, self.label, self.fstype)
- os.path.isfile(path) and os.remove(path)
-
- # it is not possible to create a squashfs without source data,
- # thus prepare an empty temp dir that is used as source
- tmpdir = tempfile.mkdtemp()
-
- squashfs_cmd = "mksquashfs %s %s -noappend" % \
- (tmpdir, path)
- exec_native_cmd(squashfs_cmd, native_sysroot)
-
- os.rmdir(tmpdir)
-
- # get the rootfs size in the right units for kickstart (kB)
- du_cmd = "du -Lbks %s" % path
- out = exec_cmd(du_cmd)
- fs_size = out.split()[0]
-
- self.size = fs_size
-
- def prepare_swap_partition(self, cr_workdir, oe_builddir, native_sysroot):
- """
- Prepare a swap partition.
- """
- path = "%s/fs.%s" % (cr_workdir, self.fstype)
-
- dd_cmd = "dd if=/dev/zero of=%s bs=1k seek=%d count=0" % \
- (path, self.size)
- exec_cmd(dd_cmd)
-
- import uuid
- label_str = ""
- if self.label:
- label_str = "-L %s" % self.label
- mkswap_cmd = "mkswap %s -U %s %s" % (label_str, str(uuid.uuid1()), path)
- exec_native_cmd(mkswap_cmd, native_sysroot)
-
-
-class Wic_Partition(FC4_Partition):
- removedKeywords = FC4_Partition.removedKeywords
- removedAttrs = FC4_Partition.removedAttrs
-
- def _getParser(self):
- def overhead_cb(option, opt_str, value, parser):
- if value < 1:
- raise OptionValueError("Option %s: invalid value: %r" % \
- (option, value))
- setattr(parser.values, option.dest, value)
-
- parser = FC4_Partition._getParser(self)
-
- # The alignment value is given in kBytes. e.g., value 8 means that
- # the partition is aligned to start from 8096 byte boundary.
- parser.add_option("--align", type="int", action="store", dest="align",
- default=None)
- parser.add_option("--extoptions", type="string", action="store", dest="extopts",
- default=None)
- parser.add_option("--part-type", type="string", action="store", dest="part_type",
- default=None)
- # use specified source file to fill the partition
- # and calculate partition size
- parser.add_option("--source", type="string", action="store",
- dest="source", default=None)
- # comma-separated list of param=value pairs
- parser.add_option("--sourceparams", type="string", action="store",
- dest="sourceparams", default=None)
- # use specified rootfs path to fill the partition
- parser.add_option("--rootfs-dir", type="string", action="store",
- dest="rootfs", default=None)
- # wether to add the partition in the partition table
- parser.add_option("--no-table", dest="no_table", action="store_true",
- default=False)
- # extra space beyond the partition size
- parser.add_option("--extra-space", dest="extra_space", action="store",
- type="size", nargs=1, default="10M")
- parser.add_option("--overhead-factor", dest="overhead_factor",
- action="callback", callback=overhead_cb, type="float",
- nargs=1, default=1.3)
- parser.add_option("--use-uuid", dest="use_uuid", action="store_true",
- default=False)
- parser.add_option("--uuid")
-
- return parser
diff --git a/scripts/lib/wic/kickstart/custom_commands/wicboot.py b/scripts/lib/wic/kickstart/custom_commands/wicboot.py
deleted file mode 100644
index a3e1852be2..0000000000
--- a/scripts/lib/wic/kickstart/custom_commands/wicboot.py
+++ /dev/null
@@ -1,60 +0,0 @@
-# ex:ts=4:sw=4:sts=4:et
-# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
-#
-# Copyright (c) 2014, Intel Corporation.
-# All rights reserved.
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 as
-# published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-#
-# DESCRIPTION
-# This module provides the OpenEmbedded bootloader object definitions.
-#
-# AUTHORS
-# Tom Zanussi <tom.zanussi (at] linux.intel.com>
-#
-from pykickstart.commands.bootloader import F8_Bootloader
-
-class Wic_Bootloader(F8_Bootloader):
- def __init__(self, writePriority=10, appendLine="", driveorder=None,
- forceLBA=False, location="", md5pass="", password="",
- upgrade=False, menus=""):
- F8_Bootloader.__init__(self, writePriority, appendLine, driveorder,
- forceLBA, location, md5pass, password, upgrade)
-
- self.menus = ""
- self.ptable = "msdos"
- self.source = ""
-
- def _getArgsAsStr(self):
- retval = F8_Bootloader._getArgsAsStr(self)
-
- if self.menus == "":
- retval += " --menus=%s" %(self.menus,)
- if self.ptable:
- retval += " --ptable=\"%s\"" %(self.ptable,)
- if self.source:
- retval += " --source=%s" % self.source
-
- return retval
-
- def _getParser(self):
- parser = F8_Bootloader._getParser(self)
- parser.add_option("--menus", dest="menus")
- parser.add_option("--ptable", dest="ptable", choices=("msdos", "gpt"),
- default="msdos")
- # use specified source plugin to implement bootloader-specific methods
- parser.add_option("--source", type="string", action="store",
- dest="source", default=None)
- return parser
-
diff --git a/scripts/lib/wic/ksparser.py b/scripts/lib/wic/ksparser.py
new file mode 100644
index 0000000000..0df9eb0d05
--- /dev/null
+++ b/scripts/lib/wic/ksparser.py
@@ -0,0 +1,295 @@
+#!/usr/bin/env python3
+#
+# Copyright (c) 2016 Intel, Inc.
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# DESCRIPTION
+# This module provides parser for kickstart format
+#
+# AUTHORS
+# Ed Bartosh <ed.bartosh> (at] linux.intel.com>
+
+"""Kickstart parser module."""
+
+import os
+import shlex
+import logging
+import re
+
+from argparse import ArgumentParser, ArgumentError, ArgumentTypeError
+
+from wic.engine import find_canned
+from wic.partition import Partition
+from wic.misc import get_bitbake_var
+
+logger = logging.getLogger('wic')
+
+__expand_var_regexp__ = re.compile(r"\${[^{}@\n\t :]+}")
+
+def expand_line(line):
+ while True:
+ m = __expand_var_regexp__.search(line)
+ if not m:
+ return line
+ key = m.group()[2:-1]
+ val = get_bitbake_var(key)
+ if val is None:
+ logger.warning("cannot expand variable %s" % key)
+ return line
+ line = line[:m.start()] + val + line[m.end():]
+
+class KickStartError(Exception):
+ """Custom exception."""
+ pass
+
+class KickStartParser(ArgumentParser):
+ """
+ This class overwrites error method to throw exception
+ instead of producing usage message(default argparse behavior).
+ """
+ def error(self, message):
+ raise ArgumentError(None, message)
+
+def sizetype(default, size_in_bytes=False):
+ def f(arg):
+ """
+ Custom type for ArgumentParser
+ Converts size string in <num>[S|s|K|k|M|G] format into the integer value
+ """
+ try:
+ suffix = default
+ size = int(arg)
+ except ValueError:
+ try:
+ suffix = arg[-1:]
+ size = int(arg[:-1])
+ except ValueError:
+ raise ArgumentTypeError("Invalid size: %r" % arg)
+
+
+ if size_in_bytes:
+ if suffix == 's' or suffix == 'S':
+ return size * 512
+ mult = 1024
+ else:
+ mult = 1
+
+ if suffix == "k" or suffix == "K":
+ return size * mult
+ if suffix == "M":
+ return size * mult * 1024
+ if suffix == "G":
+ return size * mult * 1024 * 1024
+
+ raise ArgumentTypeError("Invalid size: %r" % arg)
+ return f
+
+def overheadtype(arg):
+ """
+ Custom type for ArgumentParser
+ Converts overhead string to float and checks if it's bigger than 1.0
+ """
+ try:
+ result = float(arg)
+ except ValueError:
+ raise ArgumentTypeError("Invalid value: %r" % arg)
+
+ if result < 1.0:
+ raise ArgumentTypeError("Overhead factor should be > 1.0" % arg)
+
+ return result
+
+def cannedpathtype(arg):
+ """
+ Custom type for ArgumentParser
+ Tries to find file in the list of canned wks paths
+ """
+ scripts_path = os.path.abspath(os.path.dirname(__file__) + '../../..')
+ result = find_canned(scripts_path, arg)
+ if not result:
+ raise ArgumentTypeError("file not found: %s" % arg)
+ return result
+
+def systemidtype(arg):
+ """
+ Custom type for ArgumentParser
+ Checks if the argument sutisfies system id requirements,
+ i.e. if it's one byte long integer > 0
+ """
+ error = "Invalid system type: %s. must be hex "\
+ "between 0x1 and 0xFF" % arg
+ try:
+ result = int(arg, 16)
+ except ValueError:
+ raise ArgumentTypeError(error)
+
+ if result <= 0 or result > 0xff:
+ raise ArgumentTypeError(error)
+
+ return arg
+
+class KickStart():
+ """Kickstart parser implementation."""
+
+ DEFAULT_EXTRA_SPACE = 10*1024
+ DEFAULT_OVERHEAD_FACTOR = 1.3
+
+ def __init__(self, confpath):
+
+ self.partitions = []
+ self.bootloader = None
+ self.lineno = 0
+ self.partnum = 0
+
+ parser = KickStartParser()
+ subparsers = parser.add_subparsers()
+
+ part = subparsers.add_parser('part')
+ part.add_argument('mountpoint', nargs='?')
+ part.add_argument('--active', action='store_true')
+ part.add_argument('--align', type=int)
+ part.add_argument('--offset', type=sizetype("K", True))
+ part.add_argument('--exclude-path', nargs='+')
+ part.add_argument('--include-path', nargs='+', action='append')
+ part.add_argument('--change-directory')
+ part.add_argument("--extra-space", type=sizetype("M"))
+ part.add_argument('--fsoptions', dest='fsopts')
+ part.add_argument('--fstype', default='vfat',
+ choices=('ext2', 'ext3', 'ext4', 'btrfs',
+ 'squashfs', 'vfat', 'msdos', 'erofs',
+ 'swap'))
+ part.add_argument('--mkfs-extraopts', default='')
+ part.add_argument('--label')
+ part.add_argument('--use-label', action='store_true')
+ part.add_argument('--no-table', action='store_true')
+ part.add_argument('--ondisk', '--ondrive', dest='disk', default='sda')
+ part.add_argument("--overhead-factor", type=overheadtype)
+ part.add_argument('--part-name')
+ part.add_argument('--part-type')
+ part.add_argument('--rootfs-dir')
+ part.add_argument('--type', default='primary',
+ choices = ('primary', 'logical'))
+
+ # --size and --fixed-size cannot be specified together; options
+ # ----extra-space and --overhead-factor should also raise a parser
+ # --error, but since nesting mutually exclusive groups does not work,
+ # ----extra-space/--overhead-factor are handled later
+ sizeexcl = part.add_mutually_exclusive_group()
+ sizeexcl.add_argument('--size', type=sizetype("M"), default=0)
+ sizeexcl.add_argument('--fixed-size', type=sizetype("M"), default=0)
+
+ part.add_argument('--source')
+ part.add_argument('--sourceparams')
+ part.add_argument('--system-id', type=systemidtype)
+ part.add_argument('--use-uuid', action='store_true')
+ part.add_argument('--uuid')
+ part.add_argument('--fsuuid')
+ part.add_argument('--no-fstab-update', action='store_true')
+
+ bootloader = subparsers.add_parser('bootloader')
+ bootloader.add_argument('--append')
+ bootloader.add_argument('--configfile')
+ bootloader.add_argument('--ptable', choices=('msdos', 'gpt'),
+ default='msdos')
+ bootloader.add_argument('--timeout', type=int)
+ bootloader.add_argument('--source')
+
+ include = subparsers.add_parser('include')
+ include.add_argument('path', type=cannedpathtype)
+
+ self._parse(parser, confpath)
+ if not self.bootloader:
+ logger.warning('bootloader config not specified, using defaults\n')
+ self.bootloader = bootloader.parse_args([])
+
+ def _parse(self, parser, confpath):
+ """
+ Parse file in .wks format using provided parser.
+ """
+ with open(confpath) as conf:
+ lineno = 0
+ for line in conf:
+ line = line.strip()
+ lineno += 1
+ if line and line[0] != '#':
+ line = expand_line(line)
+ try:
+ line_args = shlex.split(line)
+ parsed = parser.parse_args(line_args)
+ except ArgumentError as err:
+ raise KickStartError('%s:%d: %s' % \
+ (confpath, lineno, err))
+ if line.startswith('part'):
+ # SquashFS does not support filesystem UUID
+ if parsed.fstype == 'squashfs':
+ if parsed.fsuuid:
+ err = "%s:%d: SquashFS does not support UUID" \
+ % (confpath, lineno)
+ raise KickStartError(err)
+ if parsed.label:
+ err = "%s:%d: SquashFS does not support LABEL" \
+ % (confpath, lineno)
+ raise KickStartError(err)
+ # erofs does not support filesystem labels
+ if parsed.fstype == 'erofs' and parsed.label:
+ err = "%s:%d: erofs does not support LABEL" % (confpath, lineno)
+ raise KickStartError(err)
+ if parsed.fstype == 'msdos' or parsed.fstype == 'vfat':
+ if parsed.fsuuid:
+ if parsed.fsuuid.upper().startswith('0X'):
+ if len(parsed.fsuuid) > 10:
+ err = "%s:%d: fsuuid %s given in wks kickstart file " \
+ "exceeds the length limit for %s filesystem. " \
+ "It should be in the form of a 32 bit hexadecimal" \
+ "number (for example, 0xABCD1234)." \
+ % (confpath, lineno, parsed.fsuuid, parsed.fstype)
+ raise KickStartError(err)
+ elif len(parsed.fsuuid) > 8:
+ err = "%s:%d: fsuuid %s given in wks kickstart file " \
+ "exceeds the length limit for %s filesystem. " \
+ "It should be in the form of a 32 bit hexadecimal" \
+ "number (for example, 0xABCD1234)." \
+ % (confpath, lineno, parsed.fsuuid, parsed.fstype)
+ raise KickStartError(err)
+ if parsed.use_label and not parsed.label:
+ err = "%s:%d: Must set the label with --label" \
+ % (confpath, lineno)
+ raise KickStartError(err)
+ # using ArgumentParser one cannot easily tell if option
+ # was passed as argument, if said option has a default
+ # value; --overhead-factor/--extra-space cannot be used
+ # with --fixed-size, so at least detect when these were
+ # passed with non-0 values ...
+ if parsed.fixed_size:
+ if parsed.overhead_factor or parsed.extra_space:
+ err = "%s:%d: arguments --overhead-factor and --extra-space not "\
+ "allowed with argument --fixed-size" \
+ % (confpath, lineno)
+ raise KickStartError(err)
+ else:
+ # ... and provide defaults if not using
+ # --fixed-size iff given option was not used
+ # (again, one cannot tell if option was passed but
+ # with value equal to 0)
+ if '--overhead-factor' not in line_args:
+ parsed.overhead_factor = self.DEFAULT_OVERHEAD_FACTOR
+ if '--extra-space' not in line_args:
+ parsed.extra_space = self.DEFAULT_EXTRA_SPACE
+
+ self.partnum += 1
+ self.partitions.append(Partition(parsed, self.partnum))
+ elif line.startswith('include'):
+ self._parse(parser, parsed.path)
+ elif line.startswith('bootloader'):
+ if not self.bootloader:
+ self.bootloader = parsed
+ # Concatenate the strings set in APPEND
+ append_var = get_bitbake_var("APPEND")
+ if append_var:
+ self.bootloader.append = ' '.join(filter(None, \
+ (self.bootloader.append, append_var)))
+ else:
+ err = "%s:%d: more than one bootloader specified" \
+ % (confpath, lineno)
+ raise KickStartError(err)
diff --git a/scripts/lib/wic/misc.py b/scripts/lib/wic/misc.py
new file mode 100644
index 0000000000..57c042c503
--- /dev/null
+++ b/scripts/lib/wic/misc.py
@@ -0,0 +1,264 @@
+#
+# Copyright (c) 2013, Intel Corporation.
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# DESCRIPTION
+# This module provides a place to collect various wic-related utils
+# for the OpenEmbedded Image Tools.
+#
+# AUTHORS
+# Tom Zanussi <tom.zanussi (at] linux.intel.com>
+#
+"""Miscellaneous functions."""
+
+import logging
+import os
+import re
+import subprocess
+
+from collections import defaultdict
+from distutils import spawn
+
+from wic import WicError
+
+logger = logging.getLogger('wic')
+
+# executable -> recipe pairs for exec_native_cmd
+NATIVE_RECIPES = {"bmaptool": "bmap-tools",
+ "dumpe2fs": "e2fsprogs",
+ "grub-mkimage": "grub-efi",
+ "isohybrid": "syslinux",
+ "mcopy": "mtools",
+ "mdel" : "mtools",
+ "mdeltree" : "mtools",
+ "mdir" : "mtools",
+ "mkdosfs": "dosfstools",
+ "mkisofs": "cdrtools",
+ "mkfs.btrfs": "btrfs-tools",
+ "mkfs.ext2": "e2fsprogs",
+ "mkfs.ext3": "e2fsprogs",
+ "mkfs.ext4": "e2fsprogs",
+ "mkfs.vfat": "dosfstools",
+ "mksquashfs": "squashfs-tools",
+ "mkswap": "util-linux",
+ "mmd": "mtools",
+ "parted": "parted",
+ "sfdisk": "util-linux",
+ "sgdisk": "gptfdisk",
+ "syslinux": "syslinux",
+ "tar": "tar"
+ }
+
+def runtool(cmdln_or_args):
+ """ wrapper for most of the subprocess calls
+ input:
+ cmdln_or_args: can be both args and cmdln str (shell=True)
+ return:
+ rc, output
+ """
+ if isinstance(cmdln_or_args, list):
+ cmd = cmdln_or_args[0]
+ shell = False
+ else:
+ import shlex
+ cmd = shlex.split(cmdln_or_args)[0]
+ shell = True
+
+ sout = subprocess.PIPE
+ serr = subprocess.STDOUT
+
+ try:
+ process = subprocess.Popen(cmdln_or_args, stdout=sout,
+ stderr=serr, shell=shell)
+ sout, serr = process.communicate()
+ # combine stdout and stderr, filter None out and decode
+ out = ''.join([out.decode('utf-8') for out in [sout, serr] if out])
+ except OSError as err:
+ if err.errno == 2:
+ # [Errno 2] No such file or directory
+ raise WicError('Cannot run command: %s, lost dependency?' % cmd)
+ else:
+ raise # relay
+
+ return process.returncode, out
+
+def _exec_cmd(cmd_and_args, as_shell=False):
+ """
+ Execute command, catching stderr, stdout
+
+ Need to execute as_shell if the command uses wildcards
+ """
+ logger.debug("_exec_cmd: %s", cmd_and_args)
+ args = cmd_and_args.split()
+ logger.debug(args)
+
+ if as_shell:
+ ret, out = runtool(cmd_and_args)
+ else:
+ ret, out = runtool(args)
+ out = out.strip()
+ if ret != 0:
+ raise WicError("_exec_cmd: %s returned '%s' instead of 0\noutput: %s" % \
+ (cmd_and_args, ret, out))
+
+ logger.debug("_exec_cmd: output for %s (rc = %d): %s",
+ cmd_and_args, ret, out)
+
+ return ret, out
+
+
+def exec_cmd(cmd_and_args, as_shell=False):
+ """
+ Execute command, return output
+ """
+ return _exec_cmd(cmd_and_args, as_shell)[1]
+
+def find_executable(cmd, paths):
+ recipe = cmd
+ if recipe in NATIVE_RECIPES:
+ recipe = NATIVE_RECIPES[recipe]
+ provided = get_bitbake_var("ASSUME_PROVIDED")
+ if provided and "%s-native" % recipe in provided:
+ return True
+
+ return spawn.find_executable(cmd, paths)
+
+def exec_native_cmd(cmd_and_args, native_sysroot, pseudo=""):
+ """
+ Execute native command, catching stderr, stdout
+
+ Need to execute as_shell if the command uses wildcards
+
+ Always need to execute native commands as_shell
+ """
+ # The reason -1 is used is because there may be "export" commands.
+ args = cmd_and_args.split(';')[-1].split()
+ logger.debug(args)
+
+ if pseudo:
+ cmd_and_args = pseudo + cmd_and_args
+
+ hosttools_dir = get_bitbake_var("HOSTTOOLS_DIR")
+
+ native_paths = "%s/sbin:%s/usr/sbin:%s/usr/bin:%s/bin:%s" % \
+ (native_sysroot, native_sysroot,
+ native_sysroot, native_sysroot,
+ hosttools_dir)
+
+ native_cmd_and_args = "export PATH=%s:$PATH;%s" % \
+ (native_paths, cmd_and_args)
+ logger.debug("exec_native_cmd: %s", native_cmd_and_args)
+
+ # If the command isn't in the native sysroot say we failed.
+ if find_executable(args[0], native_paths):
+ ret, out = _exec_cmd(native_cmd_and_args, True)
+ else:
+ ret = 127
+ out = "can't find native executable %s in %s" % (args[0], native_paths)
+
+ prog = args[0]
+ # shell command-not-found
+ if ret == 127 \
+ or (pseudo and ret == 1 and out == "Can't find '%s' in $PATH." % prog):
+ msg = "A native program %s required to build the image "\
+ "was not found (see details above).\n\n" % prog
+ recipe = NATIVE_RECIPES.get(prog)
+ if recipe:
+ msg += "Please make sure wic-tools have %s-native in its DEPENDS, "\
+ "build it with 'bitbake wic-tools' and try again.\n" % recipe
+ else:
+ msg += "Wic failed to find a recipe to build native %s. Please "\
+ "file a bug against wic.\n" % prog
+ raise WicError(msg)
+
+ return ret, out
+
+BOOTDD_EXTRA_SPACE = 16384
+
+class BitbakeVars(defaultdict):
+ """
+ Container for Bitbake variables.
+ """
+ def __init__(self):
+ defaultdict.__init__(self, dict)
+
+ # default_image and vars_dir attributes should be set from outside
+ self.default_image = None
+ self.vars_dir = None
+
+ def _parse_line(self, line, image, matcher=re.compile(r"^([a-zA-Z0-9\-_+./~]+)=(.*)")):
+ """
+ Parse one line from bitbake -e output or from .env file.
+ Put result key-value pair into the storage.
+ """
+ if "=" not in line:
+ return
+ match = matcher.match(line)
+ if not match:
+ return
+ key, val = match.groups()
+ self[image][key] = val.strip('"')
+
+ def get_var(self, var, image=None, cache=True):
+ """
+ Get bitbake variable from 'bitbake -e' output or from .env file.
+ This is a lazy method, i.e. it runs bitbake or parses file only when
+ only when variable is requested. It also caches results.
+ """
+ if not image:
+ image = self.default_image
+
+ if image not in self:
+ if image and self.vars_dir:
+ fname = os.path.join(self.vars_dir, image + '.env')
+ if os.path.isfile(fname):
+ # parse .env file
+ with open(fname) as varsfile:
+ for line in varsfile:
+ self._parse_line(line, image)
+ else:
+ print("Couldn't get bitbake variable from %s." % fname)
+ print("File %s doesn't exist." % fname)
+ return
+ else:
+ # Get bitbake -e output
+ cmd = "bitbake -e"
+ if image:
+ cmd += " %s" % image
+
+ log_level = logger.getEffectiveLevel()
+ logger.setLevel(logging.INFO)
+ ret, lines = _exec_cmd(cmd)
+ logger.setLevel(log_level)
+
+ if ret:
+ logger.error("Couldn't get '%s' output.", cmd)
+ logger.error("Bitbake failed with error:\n%s\n", lines)
+ return
+
+ # Parse bitbake -e output
+ for line in lines.split('\n'):
+ self._parse_line(line, image)
+
+ # Make first image a default set of variables
+ if cache:
+ images = [key for key in self if key]
+ if len(images) == 1:
+ self[None] = self[image]
+
+ result = self[image].get(var)
+ if not cache:
+ self.pop(image, None)
+
+ return result
+
+# Create BB_VARS singleton
+BB_VARS = BitbakeVars()
+
+def get_bitbake_var(var, image=None, cache=True):
+ """
+ Provide old get_bitbake_var API by wrapping
+ get_var method of BB_VARS singleton.
+ """
+ return BB_VARS.get_var(var, image, cache)
diff --git a/scripts/lib/wic/msger.py b/scripts/lib/wic/msger.py
deleted file mode 100644
index b737554228..0000000000
--- a/scripts/lib/wic/msger.py
+++ /dev/null
@@ -1,309 +0,0 @@
-#!/usr/bin/env python -tt
-# vim: ai ts=4 sts=4 et sw=4
-#
-# Copyright (c) 2009, 2010, 2011 Intel, Inc.
-#
-# This program is free software; you can redistribute it and/or modify it
-# under the terms of the GNU General Public License as published by the Free
-# Software Foundation; version 2 of the License
-#
-# This program is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
-# for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc., 59
-# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-import os
-import sys
-import re
-import time
-
-__ALL__ = ['set_mode',
- 'get_loglevel',
- 'set_loglevel',
- 'set_logfile',
- 'raw',
- 'debug',
- 'verbose',
- 'info',
- 'warning',
- 'error',
- 'ask',
- 'pause',
- ]
-
-# COLORs in ANSI
-INFO_COLOR = 32 # green
-WARN_COLOR = 33 # yellow
-ERR_COLOR = 31 # red
-ASK_COLOR = 34 # blue
-NO_COLOR = 0
-
-PREFIX_RE = re.compile('^<(.*?)>\s*(.*)', re.S)
-
-INTERACTIVE = True
-
-LOG_LEVEL = 1
-LOG_LEVELS = {
- 'quiet': 0,
- 'normal': 1,
- 'verbose': 2,
- 'debug': 3,
- 'never': 4,
-}
-
-LOG_FILE_FP = None
-LOG_CONTENT = ''
-CATCHERR_BUFFILE_FD = -1
-CATCHERR_BUFFILE_PATH = None
-CATCHERR_SAVED_2 = -1
-
-def _general_print(head, color, msg=None, stream=None, level='normal'):
- global LOG_CONTENT
- if not stream:
- stream = sys.stdout
-
- if LOG_LEVELS[level] > LOG_LEVEL:
- # skip
- return
-
- # encode raw 'unicode' str to utf8 encoded str
- if msg and isinstance(msg, unicode):
- msg = msg.encode('utf-8', 'ignore')
-
- errormsg = ''
- if CATCHERR_BUFFILE_FD > 0:
- size = os.lseek(CATCHERR_BUFFILE_FD, 0, os.SEEK_END)
- os.lseek(CATCHERR_BUFFILE_FD, 0, os.SEEK_SET)
- errormsg = os.read(CATCHERR_BUFFILE_FD, size)
- os.ftruncate(CATCHERR_BUFFILE_FD, 0)
-
- # append error msg to LOG
- if errormsg:
- LOG_CONTENT += errormsg
-
- # append normal msg to LOG
- save_msg = msg.strip() if msg else None
- if save_msg:
- timestr = time.strftime("[%m/%d %H:%M:%S %Z] ", time.localtime())
- LOG_CONTENT += timestr + save_msg + '\n'
-
- if errormsg:
- _color_print('', NO_COLOR, errormsg, stream, level)
-
- _color_print(head, color, msg, stream, level)
-
-def _color_print(head, color, msg, stream, level):
- colored = True
- if color == NO_COLOR or \
- not stream.isatty() or \
- os.getenv('ANSI_COLORS_DISABLED') is not None:
- colored = False
-
- if head.startswith('\r'):
- # need not \n at last
- newline = False
- else:
- newline = True
-
- if colored:
- head = '\033[%dm%s:\033[0m ' %(color, head)
- if not newline:
- # ESC cmd to clear line
- head = '\033[2K' + head
- else:
- if head:
- head += ': '
- if head.startswith('\r'):
- head = head.lstrip()
- newline = True
-
- if msg is not None:
- if isinstance(msg, unicode):
- msg = msg.encode('utf8', 'ignore')
-
- stream.write('%s%s' % (head, msg))
- if newline:
- stream.write('\n')
-
- stream.flush()
-
-def _color_perror(head, color, msg, level='normal'):
- if CATCHERR_BUFFILE_FD > 0:
- _general_print(head, color, msg, sys.stdout, level)
- else:
- _general_print(head, color, msg, sys.stderr, level)
-
-def _split_msg(head, msg):
- if isinstance(msg, list):
- msg = '\n'.join(map(str, msg))
-
- if msg.startswith('\n'):
- # means print \n at first
- msg = msg.lstrip()
- head = '\n' + head
-
- elif msg.startswith('\r'):
- # means print \r at first
- msg = msg.lstrip()
- head = '\r' + head
-
- match = PREFIX_RE.match(msg)
- if match:
- head += ' <%s>' % match.group(1)
- msg = match.group(2)
-
- return head, msg
-
-def get_loglevel():
- return (k for k, v in LOG_LEVELS.items() if v == LOG_LEVEL).next()
-
-def set_loglevel(level):
- global LOG_LEVEL
- if level not in LOG_LEVELS:
- # no effect
- return
-
- LOG_LEVEL = LOG_LEVELS[level]
-
-def set_interactive(mode=True):
- global INTERACTIVE
- if mode:
- INTERACTIVE = True
- else:
- INTERACTIVE = False
-
-def log(msg=''):
- # log msg to LOG_CONTENT then save to logfile
- global LOG_CONTENT
- if msg:
- LOG_CONTENT += msg
-
-def raw(msg=''):
- _general_print('', NO_COLOR, msg)
-
-def info(msg):
- head, msg = _split_msg('Info', msg)
- _general_print(head, INFO_COLOR, msg)
-
-def verbose(msg):
- head, msg = _split_msg('Verbose', msg)
- _general_print(head, INFO_COLOR, msg, level='verbose')
-
-def warning(msg):
- head, msg = _split_msg('Warning', msg)
- _color_perror(head, WARN_COLOR, msg)
-
-def debug(msg):
- head, msg = _split_msg('Debug', msg)
- _color_perror(head, ERR_COLOR, msg, level='debug')
-
-def error(msg):
- head, msg = _split_msg('Error', msg)
- _color_perror(head, ERR_COLOR, msg)
- sys.exit(1)
-
-def ask(msg, default=True):
- _general_print('\rQ', ASK_COLOR, '')
- try:
- if default:
- msg += '(Y/n) '
- else:
- msg += '(y/N) '
- if INTERACTIVE:
- while True:
- repl = raw_input(msg)
- if repl.lower() == 'y':
- return True
- elif repl.lower() == 'n':
- return False
- elif not repl.strip():
- # <Enter>
- return default
-
- # else loop
- else:
- if default:
- msg += ' Y'
- else:
- msg += ' N'
- _general_print('', NO_COLOR, msg)
-
- return default
- except KeyboardInterrupt:
- sys.stdout.write('\n')
- sys.exit(2)
-
-def choice(msg, choices, default=0):
- if default >= len(choices):
- return None
- _general_print('\rQ', ASK_COLOR, '')
- try:
- msg += " [%s] " % '/'.join(choices)
- if INTERACTIVE:
- while True:
- repl = raw_input(msg)
- if repl in choices:
- return repl
- elif not repl.strip():
- return choices[default]
- else:
- msg += choices[default]
- _general_print('', NO_COLOR, msg)
-
- return choices[default]
- except KeyboardInterrupt:
- sys.stdout.write('\n')
- sys.exit(2)
-
-def pause(msg=None):
- if INTERACTIVE:
- _general_print('\rQ', ASK_COLOR, '')
- if msg is None:
- msg = 'press <ENTER> to continue ...'
- raw_input(msg)
-
-def set_logfile(fpath):
- global LOG_FILE_FP
-
- def _savelogf():
- if LOG_FILE_FP:
- with open(LOG_FILE_FP, 'w') as log:
- log.write(LOG_CONTENT)
-
- if LOG_FILE_FP is not None:
- warning('duplicate log file configuration')
-
- LOG_FILE_FP = fpath
-
- import atexit
- atexit.register(_savelogf)
-
-def enable_logstderr(fpath):
- global CATCHERR_BUFFILE_FD
- global CATCHERR_BUFFILE_PATH
- global CATCHERR_SAVED_2
-
- if os.path.exists(fpath):
- os.remove(fpath)
- CATCHERR_BUFFILE_PATH = fpath
- CATCHERR_BUFFILE_FD = os.open(CATCHERR_BUFFILE_PATH, os.O_RDWR|os.O_CREAT)
- CATCHERR_SAVED_2 = os.dup(2)
- os.dup2(CATCHERR_BUFFILE_FD, 2)
-
-def disable_logstderr():
- global CATCHERR_BUFFILE_FD
- global CATCHERR_BUFFILE_PATH
- global CATCHERR_SAVED_2
-
- raw(msg=None) # flush message buffer and print it.
- os.dup2(CATCHERR_SAVED_2, 2)
- os.close(CATCHERR_SAVED_2)
- os.close(CATCHERR_BUFFILE_FD)
- os.unlink(CATCHERR_BUFFILE_PATH)
- CATCHERR_BUFFILE_FD = -1
- CATCHERR_BUFFILE_PATH = None
- CATCHERR_SAVED_2 = -1
diff --git a/scripts/lib/wic/partition.py b/scripts/lib/wic/partition.py
new file mode 100644
index 0000000000..a25834048e
--- /dev/null
+++ b/scripts/lib/wic/partition.py
@@ -0,0 +1,496 @@
+#
+# Copyright (c) 2013-2016 Intel Corporation.
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# DESCRIPTION
+# This module provides the OpenEmbedded partition object definitions.
+#
+# AUTHORS
+# Tom Zanussi <tom.zanussi (at] linux.intel.com>
+# Ed Bartosh <ed.bartosh> (at] linux.intel.com>
+
+import logging
+import os
+import uuid
+
+from wic import WicError
+from wic.misc import exec_cmd, exec_native_cmd, get_bitbake_var
+from wic.pluginbase import PluginMgr
+
+logger = logging.getLogger('wic')
+
+class Partition():
+
+ def __init__(self, args, lineno):
+ self.args = args
+ self.active = args.active
+ self.align = args.align
+ self.disk = args.disk
+ self.device = None
+ self.extra_space = args.extra_space
+ self.exclude_path = args.exclude_path
+ self.include_path = args.include_path
+ self.change_directory = args.change_directory
+ self.fsopts = args.fsopts
+ self.fstype = args.fstype
+ self.label = args.label
+ self.use_label = args.use_label
+ self.mkfs_extraopts = args.mkfs_extraopts
+ self.mountpoint = args.mountpoint
+ self.no_table = args.no_table
+ self.num = None
+ self.offset = args.offset
+ self.overhead_factor = args.overhead_factor
+ self.part_name = args.part_name
+ self.part_type = args.part_type
+ self.rootfs_dir = args.rootfs_dir
+ self.size = args.size
+ self.fixed_size = args.fixed_size
+ self.source = args.source
+ self.sourceparams = args.sourceparams
+ self.system_id = args.system_id
+ self.use_uuid = args.use_uuid
+ self.uuid = args.uuid
+ self.fsuuid = args.fsuuid
+ self.type = args.type
+ self.no_fstab_update = args.no_fstab_update
+ self.updated_fstab_path = None
+ self.has_fstab = False
+ self.update_fstab_in_rootfs = False
+
+ self.lineno = lineno
+ self.source_file = ""
+
+ def get_extra_block_count(self, current_blocks):
+ """
+ The --size param is reflected in self.size (in kB), and we already
+ have current_blocks (1k) blocks, calculate and return the
+ number of (1k) blocks we need to add to get to --size, 0 if
+ we're already there or beyond.
+ """
+ logger.debug("Requested partition size for %s: %d",
+ self.mountpoint, self.size)
+
+ if not self.size:
+ return 0
+
+ requested_blocks = self.size
+
+ logger.debug("Requested blocks %d, current_blocks %d",
+ requested_blocks, current_blocks)
+
+ if requested_blocks > current_blocks:
+ return requested_blocks - current_blocks
+ else:
+ return 0
+
+ def get_rootfs_size(self, actual_rootfs_size=0):
+ """
+ Calculate the required size of rootfs taking into consideration
+ --size/--fixed-size flags as well as overhead and extra space, as
+ specified in kickstart file. Raises an error if the
+ `actual_rootfs_size` is larger than fixed-size rootfs.
+
+ """
+ if self.fixed_size:
+ rootfs_size = self.fixed_size
+ if actual_rootfs_size > rootfs_size:
+ raise WicError("Actual rootfs size (%d kB) is larger than "
+ "allowed size %d kB" %
+ (actual_rootfs_size, rootfs_size))
+ else:
+ extra_blocks = self.get_extra_block_count(actual_rootfs_size)
+ if extra_blocks < self.extra_space:
+ extra_blocks = self.extra_space
+
+ rootfs_size = actual_rootfs_size + extra_blocks
+ rootfs_size = int(rootfs_size * self.overhead_factor)
+
+ logger.debug("Added %d extra blocks to %s to get to %d total blocks",
+ extra_blocks, self.mountpoint, rootfs_size)
+
+ return rootfs_size
+
+ @property
+ def disk_size(self):
+ """
+ Obtain on-disk size of partition taking into consideration
+ --size/--fixed-size options.
+
+ """
+ return self.fixed_size if self.fixed_size else self.size
+
+ def prepare(self, creator, cr_workdir, oe_builddir, rootfs_dir,
+ bootimg_dir, kernel_dir, native_sysroot, updated_fstab_path):
+ """
+ Prepare content for individual partitions, depending on
+ partition command parameters.
+ """
+ self.updated_fstab_path = updated_fstab_path
+ if self.updated_fstab_path and not (self.fstype.startswith("ext") or self.fstype == "msdos"):
+ self.update_fstab_in_rootfs = True
+
+ if not self.source:
+ if not self.size and not self.fixed_size:
+ raise WicError("The %s partition has a size of zero. Please "
+ "specify a non-zero --size/--fixed-size for that "
+ "partition." % self.mountpoint)
+
+ if self.fstype == "swap":
+ self.prepare_swap_partition(cr_workdir, oe_builddir,
+ native_sysroot)
+ self.source_file = "%s/fs.%s" % (cr_workdir, self.fstype)
+ else:
+ if self.fstype in ('squashfs', 'erofs'):
+ raise WicError("It's not possible to create empty %s "
+ "partition '%s'" % (self.fstype, self.mountpoint))
+
+ rootfs = "%s/fs_%s.%s.%s" % (cr_workdir, self.label,
+ self.lineno, self.fstype)
+ if os.path.isfile(rootfs):
+ os.remove(rootfs)
+
+ prefix = "ext" if self.fstype.startswith("ext") else self.fstype
+ method = getattr(self, "prepare_empty_partition_" + prefix)
+ method(rootfs, oe_builddir, native_sysroot)
+ self.source_file = rootfs
+ return
+
+ plugins = PluginMgr.get_plugins('source')
+
+ if self.source not in plugins:
+ raise WicError("The '%s' --source specified for %s doesn't exist.\n\t"
+ "See 'wic list source-plugins' for a list of available"
+ " --sources.\n\tSee 'wic help source-plugins' for "
+ "details on adding a new source plugin." %
+ (self.source, self.mountpoint))
+
+ srcparams_dict = {}
+ if self.sourceparams:
+ # Split sourceparams string of the form key1=val1[,key2=val2,...]
+ # into a dict. Also accepts valueless keys i.e. without =
+ splitted = self.sourceparams.split(',')
+ srcparams_dict = dict(par.split('=', 1) for par in splitted if par)
+
+ plugin = PluginMgr.get_plugins('source')[self.source]
+ plugin.do_configure_partition(self, srcparams_dict, creator,
+ cr_workdir, oe_builddir, bootimg_dir,
+ kernel_dir, native_sysroot)
+ plugin.do_stage_partition(self, srcparams_dict, creator,
+ cr_workdir, oe_builddir, bootimg_dir,
+ kernel_dir, native_sysroot)
+ plugin.do_prepare_partition(self, srcparams_dict, creator,
+ cr_workdir, oe_builddir, bootimg_dir,
+ kernel_dir, rootfs_dir, native_sysroot)
+ plugin.do_post_partition(self, srcparams_dict, creator,
+ cr_workdir, oe_builddir, bootimg_dir,
+ kernel_dir, rootfs_dir, native_sysroot)
+
+ # further processing required Partition.size to be an integer, make
+ # sure that it is one
+ if not isinstance(self.size, int):
+ raise WicError("Partition %s internal size is not an integer. "
+ "This a bug in source plugin %s and needs to be fixed." %
+ (self.mountpoint, self.source))
+
+ if self.fixed_size and self.size > self.fixed_size:
+ raise WicError("File system image of partition %s is "
+ "larger (%d kB) than its allowed size %d kB" %
+ (self.mountpoint, self.size, self.fixed_size))
+
+ def prepare_rootfs(self, cr_workdir, oe_builddir, rootfs_dir,
+ native_sysroot, real_rootfs = True, pseudo_dir = None):
+ """
+ Prepare content for a rootfs partition i.e. create a partition
+ and fill it from a /rootfs dir.
+
+ Currently handles ext2/3/4, btrfs, vfat and squashfs.
+ """
+
+ rootfs = "%s/rootfs_%s.%s.%s" % (cr_workdir, self.label,
+ self.lineno, self.fstype)
+ if os.path.isfile(rootfs):
+ os.remove(rootfs)
+
+ p_prefix = os.environ.get("PSEUDO_PREFIX", "%s/usr" % native_sysroot)
+ if (pseudo_dir):
+ # Canonicalize the ignore paths. This corresponds to
+ # calling oe.path.canonicalize(), which is used in bitbake.conf.
+ ignore_paths = [rootfs] + (get_bitbake_var("PSEUDO_IGNORE_PATHS") or "").split(",")
+ canonical_paths = []
+ for path in ignore_paths:
+ if "$" not in path:
+ trailing_slash = path.endswith("/") and "/" or ""
+ canonical_paths.append(os.path.realpath(path) + trailing_slash)
+ ignore_paths = ",".join(canonical_paths)
+
+ pseudo = "export PSEUDO_PREFIX=%s;" % p_prefix
+ pseudo += "export PSEUDO_LOCALSTATEDIR=%s;" % pseudo_dir
+ pseudo += "export PSEUDO_PASSWD=%s;" % rootfs_dir
+ pseudo += "export PSEUDO_NOSYMLINKEXP=1;"
+ pseudo += "export PSEUDO_IGNORE_PATHS=%s;" % ignore_paths
+ pseudo += "%s " % get_bitbake_var("FAKEROOTCMD")
+ else:
+ pseudo = None
+
+ if not self.size and real_rootfs:
+ # The rootfs size is not set in .ks file so try to get it
+ # from bitbake variable
+ rsize_bb = get_bitbake_var('ROOTFS_SIZE')
+ rdir = get_bitbake_var('IMAGE_ROOTFS')
+ if rsize_bb and rdir == rootfs_dir:
+ # Bitbake variable ROOTFS_SIZE is calculated in
+ # Image._get_rootfs_size method from meta/lib/oe/image.py
+ # using IMAGE_ROOTFS_SIZE, IMAGE_ROOTFS_ALIGNMENT,
+ # IMAGE_OVERHEAD_FACTOR and IMAGE_ROOTFS_EXTRA_SPACE
+ self.size = int(round(float(rsize_bb)))
+ else:
+ # Bitbake variable ROOTFS_SIZE is not defined so compute it
+ # from the rootfs_dir size using the same logic found in
+ # get_rootfs_size() from meta/classes/image.bbclass
+ du_cmd = "du -ks %s" % rootfs_dir
+ out = exec_cmd(du_cmd)
+ self.size = int(out.split()[0])
+
+ prefix = "ext" if self.fstype.startswith("ext") else self.fstype
+ method = getattr(self, "prepare_rootfs_" + prefix)
+ method(rootfs, cr_workdir, oe_builddir, rootfs_dir, native_sysroot, pseudo)
+ self.source_file = rootfs
+
+ # get the rootfs size in the right units for kickstart (kB)
+ du_cmd = "du -Lbks %s" % rootfs
+ out = exec_cmd(du_cmd)
+ self.size = int(out.split()[0])
+
+ def prepare_rootfs_ext(self, rootfs, cr_workdir, oe_builddir, rootfs_dir,
+ native_sysroot, pseudo):
+ """
+ Prepare content for an ext2/3/4 rootfs partition.
+ """
+ du_cmd = "du -ks %s" % rootfs_dir
+ out = exec_cmd(du_cmd)
+ actual_rootfs_size = int(out.split()[0])
+
+ rootfs_size = self.get_rootfs_size(actual_rootfs_size)
+
+ with open(rootfs, 'w') as sparse:
+ os.ftruncate(sparse.fileno(), rootfs_size * 1024)
+
+ extraopts = self.mkfs_extraopts or "-F -i 8192"
+
+ label_str = ""
+ if self.label:
+ label_str = "-L %s" % self.label
+
+ mkfs_cmd = "mkfs.%s %s %s %s -U %s -d %s" % \
+ (self.fstype, extraopts, rootfs, label_str, self.fsuuid, rootfs_dir)
+ exec_native_cmd(mkfs_cmd, native_sysroot, pseudo=pseudo)
+
+ if self.updated_fstab_path and self.has_fstab and not self.no_fstab_update:
+ debugfs_script_path = os.path.join(cr_workdir, "debugfs_script")
+ with open(debugfs_script_path, "w") as f:
+ f.write("cd etc\n")
+ f.write("rm fstab\n")
+ f.write("write %s fstab\n" % (self.updated_fstab_path))
+ debugfs_cmd = "debugfs -w -f %s %s" % (debugfs_script_path, rootfs)
+ exec_native_cmd(debugfs_cmd, native_sysroot)
+
+ mkfs_cmd = "fsck.%s -pvfD %s" % (self.fstype, rootfs)
+ exec_native_cmd(mkfs_cmd, native_sysroot, pseudo=pseudo)
+
+ self.check_for_Y2038_problem(rootfs, native_sysroot)
+
+ def prepare_rootfs_btrfs(self, rootfs, cr_workdir, oe_builddir, rootfs_dir,
+ native_sysroot, pseudo):
+ """
+ Prepare content for a btrfs rootfs partition.
+ """
+ du_cmd = "du -ks %s" % rootfs_dir
+ out = exec_cmd(du_cmd)
+ actual_rootfs_size = int(out.split()[0])
+
+ rootfs_size = self.get_rootfs_size(actual_rootfs_size)
+
+ with open(rootfs, 'w') as sparse:
+ os.ftruncate(sparse.fileno(), rootfs_size * 1024)
+
+ label_str = ""
+ if self.label:
+ label_str = "-L %s" % self.label
+
+ mkfs_cmd = "mkfs.%s -b %d -r %s %s %s -U %s %s" % \
+ (self.fstype, rootfs_size * 1024, rootfs_dir, label_str,
+ self.mkfs_extraopts, self.fsuuid, rootfs)
+ exec_native_cmd(mkfs_cmd, native_sysroot, pseudo=pseudo)
+
+ def prepare_rootfs_msdos(self, rootfs, cr_workdir, oe_builddir, rootfs_dir,
+ native_sysroot, pseudo):
+ """
+ Prepare content for a msdos/vfat rootfs partition.
+ """
+ du_cmd = "du -bks %s" % rootfs_dir
+ out = exec_cmd(du_cmd)
+ blocks = int(out.split()[0])
+
+ rootfs_size = self.get_rootfs_size(blocks)
+
+ label_str = "-n boot"
+ if self.label:
+ label_str = "-n %s" % self.label
+
+ size_str = ""
+
+ extraopts = self.mkfs_extraopts or '-S 512'
+
+ dosfs_cmd = "mkdosfs %s -i %s %s %s -C %s %d" % \
+ (label_str, self.fsuuid, size_str, extraopts, rootfs,
+ rootfs_size)
+ exec_native_cmd(dosfs_cmd, native_sysroot)
+
+ mcopy_cmd = "mcopy -i %s -s %s/* ::/" % (rootfs, rootfs_dir)
+ exec_native_cmd(mcopy_cmd, native_sysroot)
+
+ if self.updated_fstab_path and self.has_fstab and not self.no_fstab_update:
+ mcopy_cmd = "mcopy -i %s %s ::/etc/fstab" % (rootfs, self.updated_fstab_path)
+ exec_native_cmd(mcopy_cmd, native_sysroot)
+
+ chmod_cmd = "chmod 644 %s" % rootfs
+ exec_cmd(chmod_cmd)
+
+ prepare_rootfs_vfat = prepare_rootfs_msdos
+
+ def prepare_rootfs_squashfs(self, rootfs, cr_workdir, oe_builddir, rootfs_dir,
+ native_sysroot, pseudo):
+ """
+ Prepare content for a squashfs rootfs partition.
+ """
+ extraopts = self.mkfs_extraopts or '-noappend'
+ squashfs_cmd = "mksquashfs %s %s %s" % \
+ (rootfs_dir, rootfs, extraopts)
+ exec_native_cmd(squashfs_cmd, native_sysroot, pseudo=pseudo)
+
+ def prepare_rootfs_erofs(self, rootfs, cr_workdir, oe_builddir, rootfs_dir,
+ native_sysroot, pseudo):
+ """
+ Prepare content for a erofs rootfs partition.
+ """
+ extraopts = self.mkfs_extraopts or ''
+ erofs_cmd = "mkfs.erofs %s -U %s %s %s" % \
+ (extraopts, self.fsuuid, rootfs, rootfs_dir)
+ exec_native_cmd(erofs_cmd, native_sysroot, pseudo=pseudo)
+
+ def prepare_empty_partition_ext(self, rootfs, oe_builddir,
+ native_sysroot):
+ """
+ Prepare an empty ext2/3/4 partition.
+ """
+ size = self.disk_size
+ with open(rootfs, 'w') as sparse:
+ os.ftruncate(sparse.fileno(), size * 1024)
+
+ extraopts = self.mkfs_extraopts or "-i 8192"
+
+ label_str = ""
+ if self.label:
+ label_str = "-L %s" % self.label
+
+ mkfs_cmd = "mkfs.%s -F %s %s -U %s %s" % \
+ (self.fstype, extraopts, label_str, self.fsuuid, rootfs)
+ exec_native_cmd(mkfs_cmd, native_sysroot)
+
+ self.check_for_Y2038_problem(rootfs, native_sysroot)
+
+ def prepare_empty_partition_btrfs(self, rootfs, oe_builddir,
+ native_sysroot):
+ """
+ Prepare an empty btrfs partition.
+ """
+ size = self.disk_size
+ with open(rootfs, 'w') as sparse:
+ os.ftruncate(sparse.fileno(), size * 1024)
+
+ label_str = ""
+ if self.label:
+ label_str = "-L %s" % self.label
+
+ mkfs_cmd = "mkfs.%s -b %d %s -U %s %s %s" % \
+ (self.fstype, self.size * 1024, label_str, self.fsuuid,
+ self.mkfs_extraopts, rootfs)
+ exec_native_cmd(mkfs_cmd, native_sysroot)
+
+ def prepare_empty_partition_msdos(self, rootfs, oe_builddir,
+ native_sysroot):
+ """
+ Prepare an empty vfat partition.
+ """
+ blocks = self.disk_size
+
+ label_str = "-n boot"
+ if self.label:
+ label_str = "-n %s" % self.label
+
+ size_str = ""
+
+ extraopts = self.mkfs_extraopts or '-S 512'
+
+ dosfs_cmd = "mkdosfs %s -i %s %s %s -C %s %d" % \
+ (label_str, self.fsuuid, extraopts, size_str, rootfs,
+ blocks)
+
+ exec_native_cmd(dosfs_cmd, native_sysroot)
+
+ chmod_cmd = "chmod 644 %s" % rootfs
+ exec_cmd(chmod_cmd)
+
+ prepare_empty_partition_vfat = prepare_empty_partition_msdos
+
+ def prepare_swap_partition(self, cr_workdir, oe_builddir, native_sysroot):
+ """
+ Prepare a swap partition.
+ """
+ path = "%s/fs.%s" % (cr_workdir, self.fstype)
+
+ with open(path, 'w') as sparse:
+ os.ftruncate(sparse.fileno(), self.size * 1024)
+
+ label_str = ""
+ if self.label:
+ label_str = "-L %s" % self.label
+
+ mkswap_cmd = "mkswap %s -U %s %s" % (label_str, self.fsuuid, path)
+ exec_native_cmd(mkswap_cmd, native_sysroot)
+
+ def check_for_Y2038_problem(self, rootfs, native_sysroot):
+ """
+ Check if the filesystem is affected by the Y2038 problem
+ (Y2038 problem = 32 bit time_t overflow in January 2038)
+ """
+ def get_err_str(part):
+ err = "The {} filesystem {} has no Y2038 support."
+ if part.mountpoint:
+ args = [part.fstype, "mounted at %s" % part.mountpoint]
+ elif part.label:
+ args = [part.fstype, "labeled '%s'" % part.label]
+ elif part.part_name:
+ args = [part.fstype, "in partition '%s'" % part.part_name]
+ else:
+ args = [part.fstype, "in partition %s" % part.num]
+ return err.format(*args)
+
+ # ext2 and ext3 are always affected by the Y2038 problem
+ if self.fstype in ["ext2", "ext3"]:
+ logger.warn(get_err_str(self))
+ return
+
+ ret, out = exec_native_cmd("dumpe2fs %s" % rootfs, native_sysroot)
+
+ # if ext4 is affected by the Y2038 problem depends on the inode size
+ for line in out.splitlines():
+ if line.startswith("Inode size:"):
+ size = int(line.split(":")[1].strip())
+ if size < 256:
+ logger.warn("%s Inodes (of size %d) are too small." %
+ (get_err_str(self), size))
+ break
+
diff --git a/scripts/lib/wic/plugin.py b/scripts/lib/wic/plugin.py
deleted file mode 100644
index ccfdfcb934..0000000000
--- a/scripts/lib/wic/plugin.py
+++ /dev/null
@@ -1,150 +0,0 @@
-#!/usr/bin/env python -tt
-#
-# Copyright (c) 2011 Intel, Inc.
-#
-# This program is free software; you can redistribute it and/or modify it
-# under the terms of the GNU General Public License as published by the Free
-# Software Foundation; version 2 of the License
-#
-# This program is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
-# for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc., 59
-# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-import os, sys
-
-from wic import msger
-from wic import pluginbase
-from wic.utils import errors
-from wic.utils.oe.misc import get_bitbake_var
-
-__ALL__ = ['PluginMgr', 'pluginmgr']
-
-PLUGIN_TYPES = ["imager", "source"]
-
-PLUGIN_DIR = "/lib/wic/plugins" # relative to scripts
-SCRIPTS_PLUGIN_DIR = "scripts" + PLUGIN_DIR
-
-class PluginMgr(object):
- plugin_dirs = {}
-
- # make the manager class as singleton
- _instance = None
- def __new__(cls, *args, **kwargs):
- if not cls._instance:
- cls._instance = super(PluginMgr, cls).__new__(cls, *args, **kwargs)
-
- return cls._instance
-
- def __init__(self):
- wic_path = os.path.dirname(__file__)
- eos = wic_path.find('scripts') + len('scripts')
- scripts_path = wic_path[:eos]
- self.scripts_path = scripts_path
- self.plugin_dir = scripts_path + PLUGIN_DIR
- self.layers_path = None
-
- def _build_plugin_dir_list(self, plugin_dir, ptype):
- if self.layers_path is None:
- self.layers_path = get_bitbake_var("BBLAYERS")
- layer_dirs = []
-
- if self.layers_path is not None:
- for layer_path in self.layers_path.split():
- path = os.path.join(layer_path, SCRIPTS_PLUGIN_DIR, ptype)
- layer_dirs.append(path)
-
- path = os.path.join(plugin_dir, ptype)
- layer_dirs.append(path)
-
- return layer_dirs
-
- def append_dirs(self, dirs):
- for path in dirs:
- self._add_plugindir(path)
-
- # load all the plugins AGAIN
- self._load_all()
-
- def _add_plugindir(self, path):
- path = os.path.abspath(os.path.expanduser(path))
-
- if not os.path.isdir(path):
- return
-
- if path not in self.plugin_dirs:
- self.plugin_dirs[path] = False
- # the value True/False means "loaded"
-
- def _load_all(self):
- for (pdir, loaded) in self.plugin_dirs.iteritems():
- if loaded:
- continue
-
- sys.path.insert(0, pdir)
- for mod in [x[:-3] for x in os.listdir(pdir) if x.endswith(".py")]:
- if mod and mod != '__init__':
- if mod in sys.modules:
- #self.plugin_dirs[pdir] = True
- msger.warning("Module %s already exists, skip" % mod)
- else:
- try:
- pymod = __import__(mod)
- self.plugin_dirs[pdir] = True
- msger.debug("Plugin module %s:%s imported"\
- % (mod, pymod.__file__))
- except ImportError, err:
- msg = 'Failed to load plugin %s/%s: %s' \
- % (os.path.basename(pdir), mod, err)
- msger.warning(msg)
-
- del sys.path[0]
-
- def get_plugins(self, ptype):
- """ the return value is dict of name:class pairs """
-
- if ptype not in PLUGIN_TYPES:
- raise errors.CreatorError('%s is not valid plugin type' % ptype)
-
- plugins_dir = self._build_plugin_dir_list(self.plugin_dir, ptype)
-
- self.append_dirs(plugins_dir)
-
- return pluginbase.get_plugins(ptype)
-
- def get_source_plugins(self):
- """
- Return list of available source plugins.
- """
- plugins_dir = self._build_plugin_dir_list(self.plugin_dir, 'source')
-
- self.append_dirs(plugins_dir)
-
- return self.get_plugins('source')
-
-
- def get_source_plugin_methods(self, source_name, methods):
- """
- The methods param is a dict with the method names to find. On
- return, the dict values will be filled in with pointers to the
- corresponding methods. If one or more methods are not found,
- None is returned.
- """
- return_methods = None
- for _source_name, klass in self.get_plugins('source').iteritems():
- if _source_name == source_name:
- for _method_name in methods.keys():
- if not hasattr(klass, _method_name):
- msger.warning("Unimplemented %s source interface for: %s"\
- % (_method_name, _source_name))
- return None
- func = getattr(klass, _method_name)
- methods[_method_name] = func
- return_methods = methods
- return return_methods
-
-pluginmgr = PluginMgr()
diff --git a/scripts/lib/wic/pluginbase.py b/scripts/lib/wic/pluginbase.py
index ee8fe95c6f..d9b4e57747 100644
--- a/scripts/lib/wic/pluginbase.py
+++ b/scripts/lib/wic/pluginbase.py
@@ -1,48 +1,79 @@
-#!/usr/bin/env python -tt
+#!/usr/bin/env python3
#
# Copyright (c) 2011 Intel, Inc.
#
-# This program is free software; you can redistribute it and/or modify it
-# under the terms of the GNU General Public License as published by the Free
-# Software Foundation; version 2 of the License
+# SPDX-License-Identifier: GPL-2.0-only
#
-# This program is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
-# for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc., 59
-# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-from wic import msger
+__all__ = ['ImagerPlugin', 'SourcePlugin']
+
+import os
+import logging
+
+from collections import defaultdict
+from importlib.machinery import SourceFileLoader
-class _Plugin(object):
- class __metaclass__(type):
- def __init__(cls, name, bases, attrs):
- if not hasattr(cls, 'plugins'):
- cls.plugins = {}
+from wic import WicError
+from wic.misc import get_bitbake_var
- elif 'wic_plugin_type' in attrs:
- if attrs['wic_plugin_type'] not in cls.plugins:
- cls.plugins[attrs['wic_plugin_type']] = {}
+PLUGIN_TYPES = ["imager", "source"]
- elif hasattr(cls, 'wic_plugin_type') and 'name' in attrs:
- cls.plugins[cls.wic_plugin_type][attrs['name']] = cls
+SCRIPTS_PLUGIN_DIR = ["scripts/lib/wic/plugins", "lib/wic/plugins"]
- def show_plugins(cls):
- for cls in cls.plugins[cls.wic_plugin_type]:
- print cls
+logger = logging.getLogger('wic')
- def get_plugins(cls):
- return cls.plugins
+PLUGINS = defaultdict(dict)
+class PluginMgr:
+ _plugin_dirs = []
-class ImagerPlugin(_Plugin):
+ @classmethod
+ def get_plugins(cls, ptype):
+ """Get dictionary of <plugin_name>:<class> pairs."""
+ if ptype not in PLUGIN_TYPES:
+ raise WicError('%s is not valid plugin type' % ptype)
+
+ # collect plugin directories
+ if not cls._plugin_dirs:
+ cls._plugin_dirs = [os.path.join(os.path.dirname(__file__), 'plugins')]
+ layers = get_bitbake_var("BBLAYERS") or ''
+ for layer_path in layers.split():
+ for script_plugin_dir in SCRIPTS_PLUGIN_DIR:
+ path = os.path.join(layer_path, script_plugin_dir)
+ path = os.path.abspath(os.path.expanduser(path))
+ if path not in cls._plugin_dirs and os.path.isdir(path):
+ cls._plugin_dirs.insert(0, path)
+
+ if ptype not in PLUGINS:
+ # load all ptype plugins
+ for pdir in cls._plugin_dirs:
+ ppath = os.path.join(pdir, ptype)
+ if os.path.isdir(ppath):
+ for fname in os.listdir(ppath):
+ if fname.endswith('.py'):
+ mname = fname[:-3]
+ mpath = os.path.join(ppath, fname)
+ logger.debug("loading plugin module %s", mpath)
+ SourceFileLoader(mname, mpath).load_module()
+
+ return PLUGINS.get(ptype)
+
+class PluginMeta(type):
+ def __new__(cls, name, bases, attrs):
+ class_type = type.__new__(cls, name, bases, attrs)
+ if 'name' in attrs:
+ PLUGINS[class_type.wic_plugin_type][attrs['name']] = class_type
+
+ return class_type
+
+class ImagerPlugin(metaclass=PluginMeta):
wic_plugin_type = "imager"
+ def do_create(self):
+ raise WicError("Method %s.do_create is not implemented" %
+ self.__class__.__name__)
-class SourcePlugin(_Plugin):
+class SourcePlugin(metaclass=PluginMeta):
wic_plugin_type = "source"
"""
The methods that can be implemented by --source plugins.
@@ -58,7 +89,7 @@ class SourcePlugin(_Plugin):
disk image. This provides a hook to allow finalization of a
disk image e.g. to write an MBR to it.
"""
- msger.debug("SourcePlugin: do_install_disk: disk: %s" % disk_name)
+ logger.debug("SourcePlugin: do_install_disk: disk: %s", disk_name)
@classmethod
def do_stage_partition(cls, part, source_params, creator, cr_workdir,
@@ -75,7 +106,7 @@ class SourcePlugin(_Plugin):
Not that get_bitbake_var() allows you to acces non-standard
variables that you might want to use for this.
"""
- msger.debug("SourcePlugin: do_stage_partition: part: %s" % part)
+ logger.debug("SourcePlugin: do_stage_partition: part: %s", part)
@classmethod
def do_configure_partition(cls, part, source_params, creator, cr_workdir,
@@ -86,7 +117,7 @@ class SourcePlugin(_Plugin):
custom configuration files for a partition, for example
syslinux or grub config files.
"""
- msger.debug("SourcePlugin: do_configure_partition: part: %s" % part)
+ logger.debug("SourcePlugin: do_configure_partition: part: %s", part)
@classmethod
def do_prepare_partition(cls, part, source_params, creator, cr_workdir,
@@ -96,13 +127,14 @@ class SourcePlugin(_Plugin):
Called to do the actual content population for a partition i.e. it
'prepares' the partition to be incorporated into the image.
"""
- msger.debug("SourcePlugin: do_prepare_partition: part: %s" % part)
-
-def get_plugins(typen):
- plugins = ImagerPlugin.get_plugins()
- if typen in plugins:
- return plugins[typen]
- else:
- return None
+ logger.debug("SourcePlugin: do_prepare_partition: part: %s", part)
-__all__ = ['ImagerPlugin', 'SourcePlugin', 'get_plugins']
+ @classmethod
+ def do_post_partition(cls, part, source_params, creator, cr_workdir,
+ oe_builddir, bootimg_dir, kernel_dir, rootfs_dir,
+ native_sysroot):
+ """
+ Called after the partition is created. It is useful to add post
+ operations e.g. security signing the partition.
+ """
+ logger.debug("SourcePlugin: do_post_partition: part: %s", part)
diff --git a/scripts/lib/wic/plugins/imager/direct.py b/scripts/lib/wic/plugins/imager/direct.py
new file mode 100644
index 0000000000..495518fac8
--- /dev/null
+++ b/scripts/lib/wic/plugins/imager/direct.py
@@ -0,0 +1,623 @@
+#
+# Copyright (c) 2013, Intel Corporation.
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# DESCRIPTION
+# This implements the 'direct' imager plugin class for 'wic'
+#
+# AUTHORS
+# Tom Zanussi <tom.zanussi (at] linux.intel.com>
+#
+
+import logging
+import os
+import random
+import shutil
+import tempfile
+import uuid
+
+from time import strftime
+
+from oe.path import copyhardlinktree
+
+from wic import WicError
+from wic.filemap import sparse_copy
+from wic.ksparser import KickStart, KickStartError
+from wic.pluginbase import PluginMgr, ImagerPlugin
+from wic.misc import get_bitbake_var, exec_cmd, exec_native_cmd
+
+logger = logging.getLogger('wic')
+
+class DirectPlugin(ImagerPlugin):
+ """
+ Install a system into a file containing a partitioned disk image.
+
+ An image file is formatted with a partition table, each partition
+ created from a rootfs or other OpenEmbedded build artifact and dd'ed
+ into the virtual disk. The disk image can subsequently be dd'ed onto
+ media and used on actual hardware.
+ """
+ name = 'direct'
+
+ def __init__(self, wks_file, rootfs_dir, bootimg_dir, kernel_dir,
+ native_sysroot, oe_builddir, options):
+ try:
+ self.ks = KickStart(wks_file)
+ except KickStartError as err:
+ raise WicError(str(err))
+
+ # parse possible 'rootfs=name' items
+ self.rootfs_dir = dict(rdir.split('=') for rdir in rootfs_dir.split(' '))
+ self.bootimg_dir = bootimg_dir
+ self.kernel_dir = kernel_dir
+ self.native_sysroot = native_sysroot
+ self.oe_builddir = oe_builddir
+
+ self.debug = options.debug
+ self.outdir = options.outdir
+ self.compressor = options.compressor
+ self.bmap = options.bmap
+ self.no_fstab_update = options.no_fstab_update
+ self.updated_fstab_path = None
+
+ self.name = "%s-%s" % (os.path.splitext(os.path.basename(wks_file))[0],
+ strftime("%Y%m%d%H%M"))
+ self.workdir = self.setup_workdir(options.workdir)
+ self._image = None
+ self.ptable_format = self.ks.bootloader.ptable
+ self.parts = self.ks.partitions
+
+ # as a convenience, set source to the boot partition source
+ # instead of forcing it to be set via bootloader --source
+ for part in self.parts:
+ if not self.ks.bootloader.source and part.mountpoint == "/boot":
+ self.ks.bootloader.source = part.source
+ break
+
+ image_path = self._full_path(self.workdir, self.parts[0].disk, "direct")
+ self._image = PartitionedImage(image_path, self.ptable_format,
+ self.parts, self.native_sysroot,
+ options.extra_space)
+
+ def setup_workdir(self, workdir):
+ if workdir:
+ if os.path.exists(workdir):
+ raise WicError("Internal workdir '%s' specified in wic arguments already exists!" % (workdir))
+
+ os.makedirs(workdir)
+ return workdir
+ else:
+ return tempfile.mkdtemp(dir=self.outdir, prefix='tmp.wic.')
+
+ def do_create(self):
+ """
+ Plugin entry point.
+ """
+ try:
+ self.create()
+ self.assemble()
+ self.finalize()
+ self.print_info()
+ finally:
+ self.cleanup()
+
+ def update_fstab(self, image_rootfs):
+ """Assume partition order same as in wks"""
+ if not image_rootfs:
+ return
+
+ fstab_path = image_rootfs + "/etc/fstab"
+ if not os.path.isfile(fstab_path):
+ return
+
+ with open(fstab_path) as fstab:
+ fstab_lines = fstab.readlines()
+
+ updated = False
+ for part in self.parts:
+ if not part.realnum or not part.mountpoint \
+ or part.mountpoint == "/" or not part.mountpoint.startswith('/'):
+ continue
+
+ if part.use_uuid:
+ if part.fsuuid:
+ # FAT UUID is different from others
+ if len(part.fsuuid) == 10:
+ device_name = "UUID=%s-%s" % \
+ (part.fsuuid[2:6], part.fsuuid[6:])
+ else:
+ device_name = "UUID=%s" % part.fsuuid
+ else:
+ device_name = "PARTUUID=%s" % part.uuid
+ elif part.use_label:
+ device_name = "LABEL=%s" % part.label
+ else:
+ # mmc device partitions are named mmcblk0p1, mmcblk0p2..
+ prefix = 'p' if part.disk.startswith('mmcblk') else ''
+ device_name = "/dev/%s%s%d" % (part.disk, prefix, part.realnum)
+
+ opts = part.fsopts if part.fsopts else "defaults"
+ line = "\t".join([device_name, part.mountpoint, part.fstype,
+ opts, "0", "0"]) + "\n"
+
+ fstab_lines.append(line)
+ updated = True
+
+ if updated:
+ self.updated_fstab_path = os.path.join(self.workdir, "fstab")
+ with open(self.updated_fstab_path, "w") as f:
+ f.writelines(fstab_lines)
+
+ def _full_path(self, path, name, extention):
+ """ Construct full file path to a file we generate. """
+ return os.path.join(path, "%s-%s.%s" % (self.name, name, extention))
+
+ #
+ # Actual implemention
+ #
+ def create(self):
+ """
+ For 'wic', we already have our build artifacts - we just create
+ filesystems from the artifacts directly and combine them into
+ a partitioned image.
+ """
+ if not self.no_fstab_update:
+ self.update_fstab(self.rootfs_dir.get("ROOTFS_DIR"))
+
+ for part in self.parts:
+ # get rootfs size from bitbake variable if it's not set in .ks file
+ if not part.size:
+ # and if rootfs name is specified for the partition
+ image_name = self.rootfs_dir.get(part.rootfs_dir)
+ if image_name and os.path.sep not in image_name:
+ # Bitbake variable ROOTFS_SIZE is calculated in
+ # Image._get_rootfs_size method from meta/lib/oe/image.py
+ # using IMAGE_ROOTFS_SIZE, IMAGE_ROOTFS_ALIGNMENT,
+ # IMAGE_OVERHEAD_FACTOR and IMAGE_ROOTFS_EXTRA_SPACE
+ rsize_bb = get_bitbake_var('ROOTFS_SIZE', image_name)
+ if rsize_bb:
+ part.size = int(round(float(rsize_bb)))
+
+ self._image.prepare(self)
+ self._image.layout_partitions()
+ self._image.create()
+
+ def assemble(self):
+ """
+ Assemble partitions into disk image
+ """
+ self._image.assemble()
+
+ def finalize(self):
+ """
+ Finalize the disk image.
+
+ For example, prepare the image to be bootable by e.g.
+ creating and installing a bootloader configuration.
+ """
+ source_plugin = self.ks.bootloader.source
+ disk_name = self.parts[0].disk
+ if source_plugin:
+ plugin = PluginMgr.get_plugins('source')[source_plugin]
+ plugin.do_install_disk(self._image, disk_name, self, self.workdir,
+ self.oe_builddir, self.bootimg_dir,
+ self.kernel_dir, self.native_sysroot)
+
+ full_path = self._image.path
+ # Generate .bmap
+ if self.bmap:
+ logger.debug("Generating bmap file for %s", disk_name)
+ python = os.path.join(self.native_sysroot, 'usr/bin/python3-native/python3')
+ bmaptool = os.path.join(self.native_sysroot, 'usr/bin/bmaptool')
+ exec_native_cmd("%s %s create %s -o %s.bmap" % \
+ (python, bmaptool, full_path, full_path), self.native_sysroot)
+ # Compress the image
+ if self.compressor:
+ logger.debug("Compressing disk %s with %s", disk_name, self.compressor)
+ exec_cmd("%s %s" % (self.compressor, full_path))
+
+ def print_info(self):
+ """
+ Print the image(s) and artifacts used, for the user.
+ """
+ msg = "The new image(s) can be found here:\n"
+
+ extension = "direct" + {"gzip": ".gz",
+ "bzip2": ".bz2",
+ "xz": ".xz",
+ None: ""}.get(self.compressor)
+ full_path = self._full_path(self.outdir, self.parts[0].disk, extension)
+ msg += ' %s\n\n' % full_path
+
+ msg += 'The following build artifacts were used to create the image(s):\n'
+ for part in self.parts:
+ if part.rootfs_dir is None:
+ continue
+ if part.mountpoint == '/':
+ suffix = ':'
+ else:
+ suffix = '["%s"]:' % (part.mountpoint or part.label)
+ rootdir = part.rootfs_dir
+ msg += ' ROOTFS_DIR%s%s\n' % (suffix.ljust(20), rootdir)
+
+ msg += ' BOOTIMG_DIR: %s\n' % self.bootimg_dir
+ msg += ' KERNEL_DIR: %s\n' % self.kernel_dir
+ msg += ' NATIVE_SYSROOT: %s\n' % self.native_sysroot
+
+ logger.info(msg)
+
+ @property
+ def rootdev(self):
+ """
+ Get root device name to use as a 'root' parameter
+ in kernel command line.
+
+ Assume partition order same as in wks
+ """
+ for part in self.parts:
+ if part.mountpoint == "/":
+ if part.uuid:
+ return "PARTUUID=%s" % part.uuid
+ else:
+ suffix = 'p' if part.disk.startswith('mmcblk') else ''
+ return "/dev/%s%s%-d" % (part.disk, suffix, part.realnum)
+
+ def cleanup(self):
+ if self._image:
+ self._image.cleanup()
+
+ # Move results to the output dir
+ if not os.path.exists(self.outdir):
+ os.makedirs(self.outdir)
+
+ for fname in os.listdir(self.workdir):
+ path = os.path.join(self.workdir, fname)
+ if os.path.isfile(path):
+ shutil.move(path, os.path.join(self.outdir, fname))
+
+ # remove work directory when it is not in debugging mode
+ if not self.debug:
+ shutil.rmtree(self.workdir, ignore_errors=True)
+
+# Overhead of the MBR partitioning scheme (just one sector)
+MBR_OVERHEAD = 1
+
+# Overhead of the GPT partitioning scheme
+GPT_OVERHEAD = 34
+
+# Size of a sector in bytes
+SECTOR_SIZE = 512
+
+class PartitionedImage():
+ """
+ Partitioned image in a file.
+ """
+
+ def __init__(self, path, ptable_format, partitions, native_sysroot=None, extra_space=0):
+ self.path = path # Path to the image file
+ self.numpart = 0 # Number of allocated partitions
+ self.realpart = 0 # Number of partitions in the partition table
+ self.primary_part_num = 0 # Number of primary partitions (msdos)
+ self.extendedpart = 0 # Create extended partition before this logical partition (msdos)
+ self.extended_size_sec = 0 # Size of exteded partition (msdos)
+ self.logical_part_cnt = 0 # Number of total logical paritions (msdos)
+ self.offset = 0 # Offset of next partition (in sectors)
+ self.min_size = 0 # Minimum required disk size to fit
+ # all partitions (in bytes)
+ self.ptable_format = ptable_format # Partition table format
+ # Disk system identifier
+ self.identifier = random.SystemRandom().randint(1, 0xffffffff)
+
+ self.partitions = partitions
+ self.partimages = []
+ # Size of a sector used in calculations
+ self.sector_size = SECTOR_SIZE
+ self.native_sysroot = native_sysroot
+ num_real_partitions = len([p for p in self.partitions if not p.no_table])
+ self.extra_space = extra_space
+
+ # calculate the real partition number, accounting for partitions not
+ # in the partition table and logical partitions
+ realnum = 0
+ for part in self.partitions:
+ if part.no_table:
+ part.realnum = 0
+ else:
+ realnum += 1
+ if self.ptable_format == 'msdos' and realnum > 3 and num_real_partitions > 4:
+ part.realnum = realnum + 1
+ continue
+ part.realnum = realnum
+
+ # generate parition and filesystem UUIDs
+ for part in self.partitions:
+ if not part.uuid and part.use_uuid:
+ if self.ptable_format == 'gpt':
+ part.uuid = str(uuid.uuid4())
+ else: # msdos partition table
+ part.uuid = '%08x-%02d' % (self.identifier, part.realnum)
+ if not part.fsuuid:
+ if part.fstype == 'vfat' or part.fstype == 'msdos':
+ part.fsuuid = '0x' + str(uuid.uuid4())[:8].upper()
+ else:
+ part.fsuuid = str(uuid.uuid4())
+ else:
+ #make sure the fsuuid for vfat/msdos align with format 0xYYYYYYYY
+ if part.fstype == 'vfat' or part.fstype == 'msdos':
+ if part.fsuuid.upper().startswith("0X"):
+ part.fsuuid = '0x' + part.fsuuid.upper()[2:].rjust(8,"0")
+ else:
+ part.fsuuid = '0x' + part.fsuuid.upper().rjust(8,"0")
+
+ def prepare(self, imager):
+ """Prepare an image. Call prepare method of all image partitions."""
+ for part in self.partitions:
+ # need to create the filesystems in order to get their
+ # sizes before we can add them and do the layout.
+ part.prepare(imager, imager.workdir, imager.oe_builddir,
+ imager.rootfs_dir, imager.bootimg_dir,
+ imager.kernel_dir, imager.native_sysroot,
+ imager.updated_fstab_path)
+
+ # Converting kB to sectors for parted
+ part.size_sec = part.disk_size * 1024 // self.sector_size
+
+ def layout_partitions(self):
+ """ Layout the partitions, meaning calculate the position of every
+ partition on the disk. The 'ptable_format' parameter defines the
+ partition table format and may be "msdos". """
+
+ logger.debug("Assigning %s partitions to disks", self.ptable_format)
+
+ # The number of primary and logical partitions. Extended partition and
+ # partitions not listed in the table are not included.
+ num_real_partitions = len([p for p in self.partitions if not p.no_table])
+
+ # Go through partitions in the order they are added in .ks file
+ for num in range(len(self.partitions)):
+ part = self.partitions[num]
+
+ if self.ptable_format == 'msdos' and part.part_name:
+ raise WicError("setting custom partition name is not " \
+ "implemented for msdos partitions")
+
+ if self.ptable_format == 'msdos' and part.part_type:
+ # The --part-type can also be implemented for MBR partitions,
+ # in which case it would map to the 1-byte "partition type"
+ # filed at offset 3 of the partition entry.
+ raise WicError("setting custom partition type is not " \
+ "implemented for msdos partitions")
+
+ # Get the disk where the partition is located
+ self.numpart += 1
+ if not part.no_table:
+ self.realpart += 1
+
+ if self.numpart == 1:
+ if self.ptable_format == "msdos":
+ overhead = MBR_OVERHEAD
+ elif self.ptable_format == "gpt":
+ overhead = GPT_OVERHEAD
+
+ # Skip one sector required for the partitioning scheme overhead
+ self.offset += overhead
+
+ if self.ptable_format == "msdos":
+ if self.primary_part_num > 3 or \
+ (self.extendedpart == 0 and self.primary_part_num >= 3 and num_real_partitions > 4):
+ part.type = 'logical'
+ # Reserve a sector for EBR for every logical partition
+ # before alignment is performed.
+ if part.type == 'logical':
+ self.offset += 2
+
+ align_sectors = 0
+ if part.align:
+ # If not first partition and we do have alignment set we need
+ # to align the partition.
+ # FIXME: This leaves a empty spaces to the disk. To fill the
+ # gaps we could enlargea the previous partition?
+
+ # Calc how much the alignment is off.
+ align_sectors = self.offset % (part.align * 1024 // self.sector_size)
+
+ if align_sectors:
+ # If partition is not aligned as required, we need
+ # to move forward to the next alignment point
+ align_sectors = (part.align * 1024 // self.sector_size) - align_sectors
+
+ logger.debug("Realignment for %s%s with %s sectors, original"
+ " offset %s, target alignment is %sK.",
+ part.disk, self.numpart, align_sectors,
+ self.offset, part.align)
+
+ # increase the offset so we actually start the partition on right alignment
+ self.offset += align_sectors
+
+ if part.offset is not None:
+ offset = part.offset // self.sector_size
+
+ if offset * self.sector_size != part.offset:
+ raise WicError("Could not place %s%s at offset %d with sector size %d" % (part.disk, self.numpart, part.offset, self.sector_size))
+
+ delta = offset - self.offset
+ if delta < 0:
+ raise WicError("Could not place %s%s at offset %d: next free sector is %d (delta: %d)" % (part.disk, self.numpart, part.offset, self.offset, delta))
+
+ logger.debug("Skipping %d sectors to place %s%s at offset %dK",
+ delta, part.disk, self.numpart, part.offset)
+
+ self.offset = offset
+
+ part.start = self.offset
+ self.offset += part.size_sec
+
+ if not part.no_table:
+ part.num = self.realpart
+ else:
+ part.num = 0
+
+ if self.ptable_format == "msdos" and not part.no_table:
+ if part.type == 'logical':
+ self.logical_part_cnt += 1
+ part.num = self.logical_part_cnt + 4
+ if self.extendedpart == 0:
+ # Create extended partition as a primary partition
+ self.primary_part_num += 1
+ self.extendedpart = part.num
+ else:
+ self.extended_size_sec += align_sectors
+ self.extended_size_sec += part.size_sec + 2
+ else:
+ self.primary_part_num += 1
+ part.num = self.primary_part_num
+
+ logger.debug("Assigned %s to %s%d, sectors range %d-%d size %d "
+ "sectors (%d bytes).", part.mountpoint, part.disk,
+ part.num, part.start, self.offset - 1, part.size_sec,
+ part.size_sec * self.sector_size)
+
+ # Once all the partitions have been layed out, we can calculate the
+ # minumim disk size
+ self.min_size = self.offset
+ if self.ptable_format == "gpt":
+ self.min_size += GPT_OVERHEAD
+
+ self.min_size *= self.sector_size
+ self.min_size += self.extra_space
+
+ def _create_partition(self, device, parttype, fstype, start, size):
+ """ Create a partition on an image described by the 'device' object. """
+
+ # Start is included to the size so we need to substract one from the end.
+ end = start + size - 1
+ logger.debug("Added '%s' partition, sectors %d-%d, size %d sectors",
+ parttype, start, end, size)
+
+ cmd = "parted -s %s unit s mkpart %s" % (device, parttype)
+ if fstype:
+ cmd += " %s" % fstype
+ cmd += " %d %d" % (start, end)
+
+ return exec_native_cmd(cmd, self.native_sysroot)
+
+ def create(self):
+ logger.debug("Creating sparse file %s", self.path)
+ with open(self.path, 'w') as sparse:
+ os.ftruncate(sparse.fileno(), self.min_size)
+
+ logger.debug("Initializing partition table for %s", self.path)
+ exec_native_cmd("parted -s %s mklabel %s" %
+ (self.path, self.ptable_format), self.native_sysroot)
+
+ logger.debug("Set disk identifier %x", self.identifier)
+ with open(self.path, 'r+b') as img:
+ img.seek(0x1B8)
+ img.write(self.identifier.to_bytes(4, 'little'))
+
+ logger.debug("Creating partitions")
+
+ for part in self.partitions:
+ if part.num == 0:
+ continue
+
+ if self.ptable_format == "msdos" and part.num == self.extendedpart:
+ # Create an extended partition (note: extended
+ # partition is described in MBR and contains all
+ # logical partitions). The logical partitions save a
+ # sector for an EBR just before the start of a
+ # partition. The extended partition must start one
+ # sector before the start of the first logical
+ # partition. This way the first EBR is inside of the
+ # extended partition. Since the extended partitions
+ # starts a sector before the first logical partition,
+ # add a sector at the back, so that there is enough
+ # room for all logical partitions.
+ self._create_partition(self.path, "extended",
+ None, part.start - 2,
+ self.extended_size_sec)
+
+ if part.fstype == "swap":
+ parted_fs_type = "linux-swap"
+ elif part.fstype == "vfat":
+ parted_fs_type = "fat32"
+ elif part.fstype == "msdos":
+ parted_fs_type = "fat16"
+ if not part.system_id:
+ part.system_id = '0x6' # FAT16
+ else:
+ # Type for ext2/ext3/ext4/btrfs
+ parted_fs_type = "ext2"
+
+ # Boot ROM of OMAP boards require vfat boot partition to have an
+ # even number of sectors.
+ if part.mountpoint == "/boot" and part.fstype in ["vfat", "msdos"] \
+ and part.size_sec % 2:
+ logger.debug("Subtracting one sector from '%s' partition to "
+ "get even number of sectors for the partition",
+ part.mountpoint)
+ part.size_sec -= 1
+
+ self._create_partition(self.path, part.type,
+ parted_fs_type, part.start, part.size_sec)
+
+ if part.part_name:
+ logger.debug("partition %d: set name to %s",
+ part.num, part.part_name)
+ exec_native_cmd("sgdisk --change-name=%d:%s %s" % \
+ (part.num, part.part_name,
+ self.path), self.native_sysroot)
+
+ if part.part_type:
+ logger.debug("partition %d: set type UID to %s",
+ part.num, part.part_type)
+ exec_native_cmd("sgdisk --typecode=%d:%s %s" % \
+ (part.num, part.part_type,
+ self.path), self.native_sysroot)
+
+ if part.uuid and self.ptable_format == "gpt":
+ logger.debug("partition %d: set UUID to %s",
+ part.num, part.uuid)
+ exec_native_cmd("sgdisk --partition-guid=%d:%s %s" % \
+ (part.num, part.uuid, self.path),
+ self.native_sysroot)
+
+ if part.label and self.ptable_format == "gpt":
+ logger.debug("partition %d: set name to %s",
+ part.num, part.label)
+ exec_native_cmd("parted -s %s name %d %s" % \
+ (self.path, part.num, part.label),
+ self.native_sysroot)
+
+ if part.active:
+ flag_name = "legacy_boot" if self.ptable_format == 'gpt' else "boot"
+ logger.debug("Set '%s' flag for partition '%s' on disk '%s'",
+ flag_name, part.num, self.path)
+ exec_native_cmd("parted -s %s set %d %s on" % \
+ (self.path, part.num, flag_name),
+ self.native_sysroot)
+ if part.system_id:
+ exec_native_cmd("sfdisk --part-type %s %s %s" % \
+ (self.path, part.num, part.system_id),
+ self.native_sysroot)
+
+ def cleanup(self):
+ pass
+
+ def assemble(self):
+ logger.debug("Installing partitions")
+
+ for part in self.partitions:
+ source = part.source_file
+ if source:
+ # install source_file contents into a partition
+ sparse_copy(source, self.path, seek=part.start * self.sector_size)
+
+ logger.debug("Installed %s in partition %d, sectors %d-%d, "
+ "size %d sectors", source, part.num, part.start,
+ part.start + part.size_sec - 1, part.size_sec)
+
+ partimage = self.path + '.p%d' % part.num
+ bb.utils.rename(source, partimage)
+ self.partimages.append(partimage)
diff --git a/scripts/lib/wic/plugins/imager/direct_plugin.py b/scripts/lib/wic/plugins/imager/direct_plugin.py
deleted file mode 100644
index 6d3f46cc61..0000000000
--- a/scripts/lib/wic/plugins/imager/direct_plugin.py
+++ /dev/null
@@ -1,102 +0,0 @@
-# ex:ts=4:sw=4:sts=4:et
-# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
-#
-# Copyright (c) 2013, Intel Corporation.
-# All rights reserved.
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 as
-# published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-#
-# DESCRIPTION
-# This implements the 'direct' imager plugin class for 'wic'
-#
-# AUTHORS
-# Tom Zanussi <tom.zanussi (at] linux.intel.com>
-#
-
-from wic.utils import errors
-from wic.conf import configmgr
-
-import wic.imager.direct as direct
-from wic.pluginbase import ImagerPlugin
-
-class DirectPlugin(ImagerPlugin):
- """
- Install a system into a file containing a partitioned disk image.
-
- An image file is formatted with a partition table, each partition
- created from a rootfs or other OpenEmbedded build artifact and dd'ed
- into the virtual disk. The disk image can subsequently be dd'ed onto
- media and used on actual hardware.
- """
-
- name = 'direct'
-
- @classmethod
- def __rootfs_dir_to_dict(cls, rootfs_dirs):
- """
- Gets a string that contain 'connection=dir' splitted by
- space and return a dict
- """
- krootfs_dir = {}
- for rootfs_dir in rootfs_dirs.split(' '):
- key, val = rootfs_dir.split('=')
- krootfs_dir[key] = val
-
- return krootfs_dir
-
- @classmethod
- def do_create(cls, opts, *args):
- """
- Create direct image, called from creator as 'direct' cmd
- """
- if len(args) != 8:
- raise errors.Usage("Extra arguments given")
-
- native_sysroot = args[0]
- kernel_dir = args[1]
- bootimg_dir = args[2]
- rootfs_dir = args[3]
-
- creatoropts = configmgr.create
- ksconf = args[4]
-
- image_output_dir = args[5]
- oe_builddir = args[6]
- compressor = args[7]
-
- krootfs_dir = cls.__rootfs_dir_to_dict(rootfs_dir)
-
- configmgr._ksconf = ksconf
-
- creator = direct.DirectImageCreator(oe_builddir,
- image_output_dir,
- krootfs_dir,
- bootimg_dir,
- kernel_dir,
- native_sysroot,
- compressor,
- creatoropts)
-
- try:
- creator.create()
- creator.assemble()
- creator.finalize()
- creator.print_outimage_info()
-
- except errors.CreatorError:
- raise
- finally:
- creator.cleanup()
-
- return 0
diff --git a/scripts/lib/wic/plugins/source/bootimg-biosplusefi.py b/scripts/lib/wic/plugins/source/bootimg-biosplusefi.py
new file mode 100644
index 0000000000..5bd7390680
--- /dev/null
+++ b/scripts/lib/wic/plugins/source/bootimg-biosplusefi.py
@@ -0,0 +1,213 @@
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# DESCRIPTION
+# This implements the 'bootimg-biosplusefi' source plugin class for 'wic'
+#
+# AUTHORS
+# William Bourque <wbourque [at) gmail.com>
+
+import types
+
+from wic.pluginbase import SourcePlugin
+from importlib.machinery import SourceFileLoader
+
+class BootimgBiosPlusEFIPlugin(SourcePlugin):
+ """
+ Create MBR + EFI boot partition
+
+ This plugin creates a boot partition that contains both
+ legacy BIOS and EFI content. It will be able to boot from both.
+ This is useful when managing PC fleet with some older machines
+ without EFI support.
+
+ Note it is possible to create an image that can boot from both
+ legacy BIOS and EFI by defining two partitions : one with arg
+ --source bootimg-efi and another one with --source bootimg-pcbios.
+ However, this method has the obvious downside that it requires TWO
+ partitions to be created on the storage device.
+ Both partitions will also be marked as "bootable" which does not work on
+ most BIOS, has BIOS often uses the "bootable" flag to determine
+ what to boot. If you have such a BIOS, you need to manually remove the
+ "bootable" flag from the EFI partition for the drive to be bootable.
+ Having two partitions also seems to confuse wic : the content of
+ the first partition will be duplicated into the second, even though it
+ will not be used at all.
+
+ Also, unlike "isoimage-isohybrid" that also does BIOS and EFI, this plugin
+ allows you to have more than only a single rootfs partitions and does
+ not turn the rootfs into an initramfs RAM image.
+
+ This plugin is made to put everything into a single /boot partition so it
+ does not have the limitations listed above.
+
+ The plugin is made so it does tries not to reimplement what's already
+ been done in other plugins; as such it imports "bootimg-pcbios"
+ and "bootimg-efi".
+ Plugin "bootimg-pcbios" is used to generate legacy BIOS boot.
+ Plugin "bootimg-efi" is used to generate the UEFI boot. Note that it
+ requires a --sourceparams argument to know which loader to use; refer
+ to "bootimg-efi" code/documentation for the list of loader.
+
+ Imports are handled with "SourceFileLoader" from importlib as it is
+ otherwise very difficult to import module that has hyphen "-" in their
+ filename.
+ The SourcePlugin() methods used in the plugins (do_install_disk,
+ do_configure_partition, do_prepare_partition) are then called on both,
+ beginning by "bootimg-efi".
+
+ Plugin options, such as "--sourceparams" can still be passed to a
+ plugin, as long they does not cause issue in the other plugin.
+
+ Example wic configuration:
+ part /boot --source bootimg-biosplusefi --sourceparams="loader=grub-efi"\\
+ --ondisk sda --label os_boot --active --align 1024 --use-uuid
+ """
+
+ name = 'bootimg-biosplusefi'
+
+ __PCBIOS_MODULE_NAME = "bootimg-pcbios"
+ __EFI_MODULE_NAME = "bootimg-efi"
+
+ __imgEFIObj = None
+ __imgBiosObj = None
+
+ @classmethod
+ def __init__(cls):
+ """
+ Constructor (init)
+ """
+
+ # XXX
+ # For some reasons, __init__ constructor is never called.
+ # Something to do with how pluginbase works?
+ cls.__instanciateSubClasses()
+
+ @classmethod
+ def __instanciateSubClasses(cls):
+ """
+
+ """
+
+ # Import bootimg-pcbios (class name "BootimgPcbiosPlugin")
+ modulePath = os.path.join(os.path.dirname(os.path.realpath(__file__)),
+ cls.__PCBIOS_MODULE_NAME + ".py")
+ loader = SourceFileLoader(cls.__PCBIOS_MODULE_NAME, modulePath)
+ mod = types.ModuleType(loader.name)
+ loader.exec_module(mod)
+ cls.__imgBiosObj = mod.BootimgPcbiosPlugin()
+
+ # Import bootimg-efi (class name "BootimgEFIPlugin")
+ modulePath = os.path.join(os.path.dirname(os.path.realpath(__file__)),
+ cls.__EFI_MODULE_NAME + ".py")
+ loader = SourceFileLoader(cls.__EFI_MODULE_NAME, modulePath)
+ mod = types.ModuleType(loader.name)
+ loader.exec_module(mod)
+ cls.__imgEFIObj = mod.BootimgEFIPlugin()
+
+ @classmethod
+ def do_install_disk(cls, disk, disk_name, creator, workdir, oe_builddir,
+ bootimg_dir, kernel_dir, native_sysroot):
+ """
+ Called after all partitions have been prepared and assembled into a
+ disk image.
+ """
+
+ if ( (not cls.__imgEFIObj) or (not cls.__imgBiosObj) ):
+ cls.__instanciateSubClasses()
+
+ cls.__imgEFIObj.do_install_disk(
+ disk,
+ disk_name,
+ creator,
+ workdir,
+ oe_builddir,
+ bootimg_dir,
+ kernel_dir,
+ native_sysroot)
+
+ cls.__imgBiosObj.do_install_disk(
+ disk,
+ disk_name,
+ creator,
+ workdir,
+ oe_builddir,
+ bootimg_dir,
+ kernel_dir,
+ native_sysroot)
+
+ @classmethod
+ def do_configure_partition(cls, part, source_params, creator, cr_workdir,
+ oe_builddir, bootimg_dir, kernel_dir,
+ native_sysroot):
+ """
+ Called before do_prepare_partition()
+ """
+
+ if ( (not cls.__imgEFIObj) or (not cls.__imgBiosObj) ):
+ cls.__instanciateSubClasses()
+
+ cls.__imgEFIObj.do_configure_partition(
+ part,
+ source_params,
+ creator,
+ cr_workdir,
+ oe_builddir,
+ bootimg_dir,
+ kernel_dir,
+ native_sysroot)
+
+ cls.__imgBiosObj.do_configure_partition(
+ part,
+ source_params,
+ creator,
+ cr_workdir,
+ oe_builddir,
+ bootimg_dir,
+ kernel_dir,
+ native_sysroot)
+
+ @classmethod
+ def do_prepare_partition(cls, part, source_params, creator, cr_workdir,
+ oe_builddir, bootimg_dir, kernel_dir,
+ rootfs_dir, native_sysroot):
+ """
+ Called to do the actual content population for a partition i.e. it
+ 'prepares' the partition to be incorporated into the image.
+ """
+
+ if ( (not cls.__imgEFIObj) or (not cls.__imgBiosObj) ):
+ cls.__instanciateSubClasses()
+
+ cls.__imgEFIObj.do_prepare_partition(
+ part,
+ source_params,
+ creator,
+ cr_workdir,
+ oe_builddir,
+ bootimg_dir,
+ kernel_dir,
+ rootfs_dir,
+ native_sysroot)
+
+ cls.__imgBiosObj.do_prepare_partition(
+ part,
+ source_params,
+ creator,
+ cr_workdir,
+ oe_builddir,
+ bootimg_dir,
+ kernel_dir,
+ rootfs_dir,
+ native_sysroot)
diff --git a/scripts/lib/wic/plugins/source/bootimg-efi.py b/scripts/lib/wic/plugins/source/bootimg-efi.py
index fa63c6abda..0391aebdc8 100644
--- a/scripts/lib/wic/plugins/source/bootimg-efi.py
+++ b/scripts/lib/wic/plugins/source/bootimg-efi.py
@@ -1,21 +1,7 @@
-# ex:ts=4:sw=4:sts=4:et
-# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
#
# Copyright (c) 2014, Intel Corporation.
-# All rights reserved.
#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 as
-# published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+# SPDX-License-Identifier: GPL-2.0-only
#
# DESCRIPTION
# This implements the 'bootimg-efi' source plugin class for 'wic'
@@ -24,54 +10,106 @@
# Tom Zanussi <tom.zanussi (at] linux.intel.com>
#
+import logging
import os
+import tempfile
import shutil
+import re
-from wic import kickstart, msger
+from glob import glob
+
+from wic import WicError
+from wic.engine import get_custom_config
from wic.pluginbase import SourcePlugin
-from wic.utils.oe.misc import exec_cmd, exec_native_cmd, get_bitbake_var, \
- BOOTDD_EXTRA_SPACE
+from wic.misc import (exec_cmd, exec_native_cmd,
+ get_bitbake_var, BOOTDD_EXTRA_SPACE)
+
+logger = logging.getLogger('wic')
class BootimgEFIPlugin(SourcePlugin):
"""
Create EFI boot partition.
- This plugin supports GRUB 2 and gummiboot bootloaders.
+ This plugin supports GRUB 2 and systemd-boot bootloaders.
"""
name = 'bootimg-efi'
@classmethod
- def do_configure_grubefi(cls, hdddir, creator, cr_workdir):
+ def do_configure_grubefi(cls, hdddir, creator, cr_workdir, source_params):
"""
Create loader-specific (grub-efi) config
"""
- options = creator.ks.handler.bootloader.appendLine
-
- grubefi_conf = ""
- grubefi_conf += "serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1\n"
- grubefi_conf += "default=boot\n"
- timeout = kickstart.get_timeout(creator.ks)
- if not timeout:
- timeout = 0
- grubefi_conf += "timeout=%s\n" % timeout
- grubefi_conf += "menuentry 'boot'{\n"
+ configfile = creator.ks.bootloader.configfile
+ custom_cfg = None
+ if configfile:
+ custom_cfg = get_custom_config(configfile)
+ if custom_cfg:
+ # Use a custom configuration for grub
+ grubefi_conf = custom_cfg
+ logger.debug("Using custom configuration file "
+ "%s for grub.cfg", configfile)
+ else:
+ raise WicError("configfile is specified but failed to "
+ "get it from %s." % configfile)
- kernel = "/bzImage"
+ initrd = source_params.get('initrd')
- grubefi_conf += "linux %s root=%s rootwait %s\n" \
- % (kernel, creator.rootdev, options)
- grubefi_conf += "}\n"
+ if initrd:
+ bootimg_dir = get_bitbake_var("DEPLOY_DIR_IMAGE")
+ if not bootimg_dir:
+ raise WicError("Couldn't find DEPLOY_DIR_IMAGE, exiting")
- msger.debug("Writing grubefi config %s/hdd/boot/EFI/BOOT/grub.cfg" \
- % cr_workdir)
+ initrds = initrd.split(';')
+ for rd in initrds:
+ cp_cmd = "cp %s/%s %s" % (bootimg_dir, rd, hdddir)
+ exec_cmd(cp_cmd, True)
+ else:
+ logger.debug("Ignoring missing initrd")
+
+ if not custom_cfg:
+ # Create grub configuration using parameters from wks file
+ bootloader = creator.ks.bootloader
+ title = source_params.get('title')
+
+ grubefi_conf = ""
+ grubefi_conf += "serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1\n"
+ grubefi_conf += "default=boot\n"
+ grubefi_conf += "timeout=%s\n" % bootloader.timeout
+ grubefi_conf += "menuentry '%s'{\n" % (title if title else "boot")
+
+ kernel = get_bitbake_var("KERNEL_IMAGETYPE")
+ if get_bitbake_var("INITRAMFS_IMAGE_BUNDLE") == "1":
+ if get_bitbake_var("INITRAMFS_IMAGE"):
+ kernel = "%s-%s.bin" % \
+ (get_bitbake_var("KERNEL_IMAGETYPE"), get_bitbake_var("INITRAMFS_LINK_NAME"))
+
+ label = source_params.get('label')
+ label_conf = "root=%s" % creator.rootdev
+ if label:
+ label_conf = "LABEL=%s" % label
+
+ grubefi_conf += "linux /%s %s rootwait %s\n" \
+ % (kernel, label_conf, bootloader.append)
+
+ if initrd:
+ initrds = initrd.split(';')
+ grubefi_conf += "initrd"
+ for rd in initrds:
+ grubefi_conf += " /%s" % rd
+ grubefi_conf += "\n"
+
+ grubefi_conf += "}\n"
+
+ logger.debug("Writing grubefi config %s/hdd/boot/EFI/BOOT/grub.cfg",
+ cr_workdir)
cfg = open("%s/hdd/boot/EFI/BOOT/grub.cfg" % cr_workdir, "w")
cfg.write(grubefi_conf)
cfg.close()
@classmethod
- def do_configure_gummiboot(cls, hdddir, creator, cr_workdir):
+ def do_configure_systemdboot(cls, hdddir, creator, cr_workdir, source_params):
"""
- Create loader-specific (gummiboot) config
+ Create loader-specific systemd-boot/gummiboot config
"""
install_cmd = "install -d %s/loader" % hdddir
exec_cmd(install_cmd)
@@ -79,34 +117,80 @@ class BootimgEFIPlugin(SourcePlugin):
install_cmd = "install -d %s/loader/entries" % hdddir
exec_cmd(install_cmd)
- options = creator.ks.handler.bootloader.appendLine
-
- timeout = kickstart.get_timeout(creator.ks)
- if not timeout:
- timeout = 0
+ bootloader = creator.ks.bootloader
loader_conf = ""
- loader_conf += "default boot\n"
- loader_conf += "timeout %d\n" % timeout
+ if source_params.get('create-unified-kernel-image') != "true":
+ loader_conf += "default boot\n"
+ loader_conf += "timeout %d\n" % bootloader.timeout
+
+ initrd = source_params.get('initrd')
+
+ if initrd and source_params.get('create-unified-kernel-image') != "true":
+ # obviously we need to have a common common deploy var
+ bootimg_dir = get_bitbake_var("DEPLOY_DIR_IMAGE")
+ if not bootimg_dir:
+ raise WicError("Couldn't find DEPLOY_DIR_IMAGE, exiting")
- msger.debug("Writing gummiboot config %s/hdd/boot/loader/loader.conf" \
- % cr_workdir)
+ initrds = initrd.split(';')
+ for rd in initrds:
+ cp_cmd = "cp %s/%s %s" % (bootimg_dir, rd, hdddir)
+ exec_cmd(cp_cmd, True)
+ else:
+ logger.debug("Ignoring missing initrd")
+
+ logger.debug("Writing systemd-boot config "
+ "%s/hdd/boot/loader/loader.conf", cr_workdir)
cfg = open("%s/hdd/boot/loader/loader.conf" % cr_workdir, "w")
cfg.write(loader_conf)
cfg.close()
- kernel = "/bzImage"
+ configfile = creator.ks.bootloader.configfile
+ custom_cfg = None
+ if configfile:
+ custom_cfg = get_custom_config(configfile)
+ if custom_cfg:
+ # Use a custom configuration for systemd-boot
+ boot_conf = custom_cfg
+ logger.debug("Using custom configuration file "
+ "%s for systemd-boots's boot.conf", configfile)
+ else:
+ raise WicError("configfile is specified but failed to "
+ "get it from %s.", configfile)
- boot_conf = ""
- boot_conf += "title boot\n"
- boot_conf += "linux %s\n" % kernel
- boot_conf += "options LABEL=Boot root=%s %s\n" % (creator.rootdev, options)
+ if not custom_cfg:
+ # Create systemd-boot configuration using parameters from wks file
+ kernel = get_bitbake_var("KERNEL_IMAGETYPE")
+ if get_bitbake_var("INITRAMFS_IMAGE_BUNDLE") == "1":
+ if get_bitbake_var("INITRAMFS_IMAGE"):
+ kernel = "%s-%s.bin" % \
+ (get_bitbake_var("KERNEL_IMAGETYPE"), get_bitbake_var("INITRAMFS_LINK_NAME"))
- msger.debug("Writing gummiboot config %s/hdd/boot/loader/entries/boot.conf" \
- % cr_workdir)
- cfg = open("%s/hdd/boot/loader/entries/boot.conf" % cr_workdir, "w")
- cfg.write(boot_conf)
- cfg.close()
+ title = source_params.get('title')
+
+ boot_conf = ""
+ boot_conf += "title %s\n" % (title if title else "boot")
+ boot_conf += "linux /%s\n" % kernel
+
+ label = source_params.get('label')
+ label_conf = "LABEL=Boot root=%s" % creator.rootdev
+ if label:
+ label_conf = "LABEL=%s" % label
+
+ boot_conf += "options %s %s\n" % \
+ (label_conf, bootloader.append)
+
+ if initrd:
+ initrds = initrd.split(';')
+ for rd in initrds:
+ boot_conf += "initrd /%s\n" % rd
+
+ if source_params.get('create-unified-kernel-image') != "true":
+ logger.debug("Writing systemd-boot config "
+ "%s/hdd/boot/loader/entries/boot.conf", cr_workdir)
+ cfg = open("%s/hdd/boot/loader/entries/boot.conf" % cr_workdir, "w")
+ cfg.write(boot_conf)
+ cfg.close()
@classmethod
@@ -117,22 +201,71 @@ class BootimgEFIPlugin(SourcePlugin):
Called before do_prepare_partition(), creates loader-specific config
"""
hdddir = "%s/hdd/boot" % cr_workdir
- rm_cmd = "rm -rf %s" % cr_workdir
- exec_cmd(rm_cmd)
install_cmd = "install -d %s/EFI/BOOT" % hdddir
exec_cmd(install_cmd)
try:
if source_params['loader'] == 'grub-efi':
- cls.do_configure_grubefi(hdddir, creator, cr_workdir)
- elif source_params['loader'] == 'gummiboot':
- cls.do_configure_gummiboot(hdddir, creator, cr_workdir)
+ cls.do_configure_grubefi(hdddir, creator, cr_workdir, source_params)
+ elif source_params['loader'] == 'systemd-boot':
+ cls.do_configure_systemdboot(hdddir, creator, cr_workdir, source_params)
else:
- msger.error("unrecognized bootimg-efi loader: %s" % source_params['loader'])
+ raise WicError("unrecognized bootimg-efi loader: %s" % source_params['loader'])
except KeyError:
- msger.error("bootimg-efi requires a loader, none specified")
-
+ raise WicError("bootimg-efi requires a loader, none specified")
+
+ if get_bitbake_var("IMAGE_EFI_BOOT_FILES") is None:
+ logger.debug('No boot files defined in IMAGE_EFI_BOOT_FILES')
+ else:
+ boot_files = None
+ for (fmt, id) in (("_uuid-%s", part.uuid), ("_label-%s", part.label), (None, None)):
+ if fmt:
+ var = fmt % id
+ else:
+ var = ""
+
+ boot_files = get_bitbake_var("IMAGE_EFI_BOOT_FILES" + var)
+ if boot_files:
+ break
+
+ logger.debug('Boot files: %s', boot_files)
+
+ # list of tuples (src_name, dst_name)
+ deploy_files = []
+ for src_entry in re.findall(r'[\w;\-\./\*]+', boot_files):
+ if ';' in src_entry:
+ dst_entry = tuple(src_entry.split(';'))
+ if not dst_entry[0] or not dst_entry[1]:
+ raise WicError('Malformed boot file entry: %s' % src_entry)
+ else:
+ dst_entry = (src_entry, src_entry)
+
+ logger.debug('Destination entry: %r', dst_entry)
+ deploy_files.append(dst_entry)
+
+ cls.install_task = [];
+ for deploy_entry in deploy_files:
+ src, dst = deploy_entry
+ if '*' in src:
+ # by default install files under their basename
+ entry_name_fn = os.path.basename
+ if dst != src:
+ # unless a target name was given, then treat name
+ # as a directory and append a basename
+ entry_name_fn = lambda name: \
+ os.path.join(dst,
+ os.path.basename(name))
+
+ srcs = glob(os.path.join(kernel_dir, src))
+
+ logger.debug('Globbed sources: %s', ', '.join(srcs))
+ for entry in srcs:
+ src = os.path.relpath(entry, kernel_dir)
+ entry_dst_name = entry_name_fn(entry)
+ cls.install_task.append((src, entry_dst_name))
+ else:
+ cls.install_task.append((src, dst))
@classmethod
def do_prepare_partition(cls, part, source_params, creator, cr_workdir,
@@ -143,36 +276,106 @@ class BootimgEFIPlugin(SourcePlugin):
'prepares' the partition to be incorporated into the image.
In this case, prepare content for an EFI (grub) boot partition.
"""
- if not bootimg_dir:
- bootimg_dir = get_bitbake_var("HDDDIR")
- if not bootimg_dir:
- msger.error("Couldn't find HDDDIR, exiting\n")
- # just so the result notes display it
- creator.set_bootimg_dir(bootimg_dir)
+ if not kernel_dir:
+ kernel_dir = get_bitbake_var("DEPLOY_DIR_IMAGE")
+ if not kernel_dir:
+ raise WicError("Couldn't find DEPLOY_DIR_IMAGE, exiting")
staging_kernel_dir = kernel_dir
hdddir = "%s/hdd/boot" % cr_workdir
- install_cmd = "install -m 0644 %s/bzImage %s/bzImage" % \
- (staging_kernel_dir, hdddir)
- exec_cmd(install_cmd)
+ kernel = get_bitbake_var("KERNEL_IMAGETYPE")
+ if get_bitbake_var("INITRAMFS_IMAGE_BUNDLE") == "1":
+ if get_bitbake_var("INITRAMFS_IMAGE"):
+ kernel = "%s-%s.bin" % \
+ (get_bitbake_var("KERNEL_IMAGETYPE"), get_bitbake_var("INITRAMFS_LINK_NAME"))
+
+ if source_params.get('create-unified-kernel-image') == "true":
+ initrd = source_params.get('initrd')
+ if not initrd:
+ raise WicError("initrd= must be specified when create-unified-kernel-image=true, exiting")
+
+ deploy_dir = get_bitbake_var("DEPLOY_DIR_IMAGE")
+ efi_stub = glob("%s/%s" % (deploy_dir, "linux*.efi.stub"))
+ if len(efi_stub) == 0:
+ raise WicError("Unified Kernel Image EFI stub not found, exiting")
+ efi_stub = efi_stub[0]
+
+ with tempfile.TemporaryDirectory() as tmp_dir:
+ label = source_params.get('label')
+ label_conf = "root=%s" % creator.rootdev
+ if label:
+ label_conf = "LABEL=%s" % label
+
+ bootloader = creator.ks.bootloader
+ cmdline = open("%s/cmdline" % tmp_dir, "w")
+ cmdline.write("%s %s" % (label_conf, bootloader.append))
+ cmdline.close()
+
+ initrds = initrd.split(';')
+ initrd = open("%s/initrd" % tmp_dir, "wb")
+ for f in initrds:
+ with open("%s/%s" % (deploy_dir, f), 'rb') as in_file:
+ shutil.copyfileobj(in_file, initrd)
+ initrd.close()
+
+ # Searched by systemd-boot:
+ # https://systemd.io/BOOT_LOADER_SPECIFICATION/#type-2-efi-unified-kernel-images
+ install_cmd = "install -d %s/EFI/Linux" % hdddir
+ exec_cmd(install_cmd)
+
+ staging_dir_host = get_bitbake_var("STAGING_DIR_HOST")
+
+ # https://www.freedesktop.org/software/systemd/man/systemd-stub.html
+ objcopy_cmd = "objcopy \
+ --add-section .osrel=%s --change-section-vma .osrel=0x20000 \
+ --add-section .cmdline=%s --change-section-vma .cmdline=0x30000 \
+ --add-section .linux=%s --change-section-vma .linux=0x2000000 \
+ --add-section .initrd=%s --change-section-vma .initrd=0x3000000 \
+ %s %s" % \
+ ("%s/usr/lib/os-release" % staging_dir_host,
+ cmdline.name,
+ "%s/%s" % (staging_kernel_dir, kernel),
+ initrd.name,
+ efi_stub,
+ "%s/EFI/Linux/linux.efi" % hdddir)
+ exec_cmd(objcopy_cmd)
+ else:
+ install_cmd = "install -m 0644 %s/%s %s/%s" % \
+ (staging_kernel_dir, kernel, hdddir, kernel)
+ exec_cmd(install_cmd)
+
+ if get_bitbake_var("IMAGE_EFI_BOOT_FILES"):
+ for src_path, dst_path in cls.install_task:
+ install_cmd = "install -m 0644 -D %s %s" \
+ % (os.path.join(kernel_dir, src_path),
+ os.path.join(hdddir, dst_path))
+ exec_cmd(install_cmd)
try:
if source_params['loader'] == 'grub-efi':
shutil.copyfile("%s/hdd/boot/EFI/BOOT/grub.cfg" % cr_workdir,
"%s/grub.cfg" % cr_workdir)
- cp_cmd = "cp %s/EFI/BOOT/* %s/EFI/BOOT" % (bootimg_dir, hdddir)
- exec_cmd(cp_cmd, True)
+ for mod in [x for x in os.listdir(kernel_dir) if x.startswith("grub-efi-")]:
+ cp_cmd = "cp %s/%s %s/EFI/BOOT/%s" % (kernel_dir, mod, hdddir, mod[9:])
+ exec_cmd(cp_cmd, True)
shutil.move("%s/grub.cfg" % cr_workdir,
"%s/hdd/boot/EFI/BOOT/grub.cfg" % cr_workdir)
- elif source_params['loader'] == 'gummiboot':
- cp_cmd = "cp %s/EFI/BOOT/* %s/EFI/BOOT" % (bootimg_dir, hdddir)
- exec_cmd(cp_cmd, True)
+ elif source_params['loader'] == 'systemd-boot':
+ for mod in [x for x in os.listdir(kernel_dir) if x.startswith("systemd-")]:
+ cp_cmd = "cp %s/%s %s/EFI/BOOT/%s" % (kernel_dir, mod, hdddir, mod[8:])
+ exec_cmd(cp_cmd, True)
else:
- msger.error("unrecognized bootimg-efi loader: %s" % source_params['loader'])
+ raise WicError("unrecognized bootimg-efi loader: %s" %
+ source_params['loader'])
except KeyError:
- msger.error("bootimg-efi requires a loader, none specified")
+ raise WicError("bootimg-efi requires a loader, none specified")
+
+ startup = os.path.join(kernel_dir, "startup.nsh")
+ if os.path.exists(startup):
+ cp_cmd = "cp %s %s/" % (startup, hdddir)
+ exec_cmd(cp_cmd, True)
du_cmd = "du -bks %s" % hdddir
out = exec_cmd(du_cmd)
@@ -185,19 +388,16 @@ class BootimgEFIPlugin(SourcePlugin):
blocks += extra_blocks
- msger.debug("Added %d extra blocks to %s to get to %d total blocks" % \
- (extra_blocks, part.mountpoint, blocks))
-
- # Ensure total sectors is an integral number of sectors per
- # track or mcopy will complain. Sectors are 512 bytes, and we
- # generate images with 32 sectors per track. This calculation is
- # done in blocks, thus the mod by 16 instead of 32.
- blocks += (16 - (blocks % 16))
+ logger.debug("Added %d extra blocks to %s to get to %d total blocks",
+ extra_blocks, part.mountpoint, blocks)
# dosfs image, created by mkdosfs
bootimg = "%s/boot.img" % cr_workdir
- dosfs_cmd = "mkdosfs -n efi -C %s %d" % (bootimg, blocks)
+ label = part.label if part.label else "ESP"
+
+ dosfs_cmd = "mkdosfs -n %s -i %s -C %s %d" % \
+ (label, part.fsuuid, bootimg, blocks)
exec_native_cmd(dosfs_cmd, native_sysroot)
mcopy_cmd = "mcopy -i %s -s %s/* ::/" % (bootimg, hdddir)
@@ -210,5 +410,5 @@ class BootimgEFIPlugin(SourcePlugin):
out = exec_cmd(du_cmd)
bootimg_size = out.split()[0]
- part.set_size(bootimg_size)
- part.set_source_file(bootimg)
+ part.size = int(bootimg_size)
+ part.source_file = bootimg
diff --git a/scripts/lib/wic/plugins/source/bootimg-partition.py b/scripts/lib/wic/plugins/source/bootimg-partition.py
index bc2ca0f6fa..5dbe2558d2 100644
--- a/scripts/lib/wic/plugins/source/bootimg-partition.py
+++ b/scripts/lib/wic/plugins/source/bootimg-partition.py
@@ -1,18 +1,5 @@
-# ex:ts=4:sw=4:sts=4:et
-# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 as
-# published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+# SPDX-License-Identifier: GPL-2.0-only
#
# DESCRIPTION
# This implements the 'bootimg-partition' source plugin class for
@@ -23,14 +10,19 @@
# Maciej Borzecki <maciej.borzecki (at] open-rnd.pl>
#
+import logging
import os
import re
-from wic import msger
-from wic.pluginbase import SourcePlugin
-from wic.utils.oe.misc import exec_cmd, get_bitbake_var
from glob import glob
+from wic import WicError
+from wic.engine import get_custom_config
+from wic.pluginbase import SourcePlugin
+from wic.misc import exec_cmd, get_bitbake_var
+
+logger = logging.getLogger('wic')
+
class BootimgPartitionPlugin(SourcePlugin):
"""
Create an image of boot partition, copying over files
@@ -40,56 +32,36 @@ class BootimgPartitionPlugin(SourcePlugin):
name = 'bootimg-partition'
@classmethod
- def do_install_disk(cls, disk, disk_name, cr, workdir, oe_builddir,
- bootimg_dir, kernel_dir, native_sysroot):
- """
- Called after all partitions have been prepared and assembled into a
- disk image. Do nothing.
- """
- pass
-
- @classmethod
def do_configure_partition(cls, part, source_params, cr, cr_workdir,
- oe_builddir, bootimg_dir, kernel_dir,
- native_sysroot):
- """
- Called before do_prepare_partition(). Possibly prepare
- configuration files of some sort.
-
- """
- pass
-
- @classmethod
- def do_prepare_partition(cls, part, source_params, cr, cr_workdir,
oe_builddir, bootimg_dir, kernel_dir,
- rootfs_dir, native_sysroot):
+ native_sysroot):
"""
- Called to do the actual content population for a partition i.e. it
- 'prepares' the partition to be incorporated into the image.
- In this case, does the following:
- - sets up a vfat partition
- - copies all files listed in IMAGE_BOOT_FILES variable
+ Called before do_prepare_partition(), create u-boot specific boot config
"""
- hdddir = "%s/boot" % cr_workdir
- rm_cmd = "rm -rf %s/boot" % cr_workdir
- exec_cmd(rm_cmd)
-
+ hdddir = "%s/boot.%d" % (cr_workdir, part.lineno)
install_cmd = "install -d %s" % hdddir
exec_cmd(install_cmd)
- if not bootimg_dir:
- bootimg_dir = get_bitbake_var("DEPLOY_DIR_IMAGE")
- if not bootimg_dir:
- msger.error("Couldn't find DEPLOY_DIR_IMAGE, exiting\n")
+ if not kernel_dir:
+ kernel_dir = get_bitbake_var("DEPLOY_DIR_IMAGE")
+ if not kernel_dir:
+ raise WicError("Couldn't find DEPLOY_DIR_IMAGE, exiting")
- msger.debug('Bootimg dir: %s' % bootimg_dir)
+ boot_files = None
+ for (fmt, id) in (("_uuid-%s", part.uuid), ("_label-%s", part.label), (None, None)):
+ if fmt:
+ var = fmt % id
+ else:
+ var = ""
- boot_files = get_bitbake_var("IMAGE_BOOT_FILES")
+ boot_files = get_bitbake_var("IMAGE_BOOT_FILES" + var)
+ if boot_files is not None:
+ break
- if not boot_files:
- msger.error('No boot files defined, IMAGE_BOOT_FILES unset')
+ if boot_files is None:
+ raise WicError('No boot files defined, IMAGE_BOOT_FILES unset for entry #%d' % part.lineno)
- msger.debug('Boot files: %s' % boot_files)
+ logger.debug('Boot files: %s', boot_files)
# list of tuples (src_name, dst_name)
deploy_files = []
@@ -97,16 +69,16 @@ class BootimgPartitionPlugin(SourcePlugin):
if ';' in src_entry:
dst_entry = tuple(src_entry.split(';'))
if not dst_entry[0] or not dst_entry[1]:
- msger.error('Malformed boot file entry: %s' % (src_entry))
+ raise WicError('Malformed boot file entry: %s' % src_entry)
else:
dst_entry = (src_entry, src_entry)
- msger.debug('Destination entry: %r' % (dst_entry,))
+ logger.debug('Destination entry: %r', dst_entry)
deploy_files.append(dst_entry)
+ cls.install_task = [];
for deploy_entry in deploy_files:
src, dst = deploy_entry
- install_task = []
if '*' in src:
# by default install files under their basename
entry_name_fn = os.path.basename
@@ -117,27 +89,106 @@ class BootimgPartitionPlugin(SourcePlugin):
os.path.join(dst,
os.path.basename(name))
- srcs = glob(os.path.join(bootimg_dir, src))
+ srcs = glob(os.path.join(kernel_dir, src))
- msger.debug('Globbed sources: %s' % (', '.join(srcs)))
+ logger.debug('Globbed sources: %s', ', '.join(srcs))
for entry in srcs:
+ src = os.path.relpath(entry, kernel_dir)
entry_dst_name = entry_name_fn(entry)
- install_task.append((entry,
- os.path.join(hdddir,
- entry_dst_name)))
+ cls.install_task.append((src, entry_dst_name))
else:
- install_task = [(os.path.join(bootimg_dir, src),
- os.path.join(hdddir, dst))]
-
- for task in install_task:
- src_path, dst_path = task
- msger.debug('Install %s as %s' % (os.path.basename(src_path),
- dst_path))
- install_cmd = "install -m 0644 -D %s %s" \
- % (src_path, dst_path)
- exec_cmd(install_cmd)
-
- msger.debug('Prepare boot partition using rootfs in %s' % (hdddir))
- part.prepare_rootfs(cr_workdir, oe_builddir, hdddir,
- native_sysroot)
+ cls.install_task.append((src, dst))
+
+ if source_params.get('loader') != "u-boot":
+ return
+
+ configfile = cr.ks.bootloader.configfile
+ custom_cfg = None
+ if configfile:
+ custom_cfg = get_custom_config(configfile)
+ if custom_cfg:
+ # Use a custom configuration for extlinux.conf
+ extlinux_conf = custom_cfg
+ logger.debug("Using custom configuration file "
+ "%s for extlinux.cfg", configfile)
+ else:
+ raise WicError("configfile is specified but failed to "
+ "get it from %s." % configfile)
+
+ if not custom_cfg:
+ # The kernel types supported by the sysboot of u-boot
+ kernel_types = ["zImage", "Image", "fitImage", "uImage", "vmlinux"]
+ has_dtb = False
+ fdt_dir = '/'
+ kernel_name = None
+
+ # Find the kernel image name, from the highest precedence to lowest
+ for image in kernel_types:
+ for task in cls.install_task:
+ src, dst = task
+ if re.match(image, src):
+ kernel_name = os.path.join('/', dst)
+ break
+ if kernel_name:
+ break
+
+ for task in cls.install_task:
+ src, dst = task
+ # We suppose that all the dtb are in the same directory
+ if re.search(r'\.dtb', src) and fdt_dir == '/':
+ has_dtb = True
+ fdt_dir = os.path.join(fdt_dir, os.path.dirname(dst))
+ break
+
+ if not kernel_name:
+ raise WicError('No kernel file found')
+
+ # Compose the extlinux.conf
+ extlinux_conf = "default Yocto\n"
+ extlinux_conf += "label Yocto\n"
+ extlinux_conf += " kernel %s\n" % kernel_name
+ if has_dtb:
+ extlinux_conf += " fdtdir %s\n" % fdt_dir
+ bootloader = cr.ks.bootloader
+ extlinux_conf += "append root=%s rootwait %s\n" \
+ % (cr.rootdev, bootloader.append if bootloader.append else '')
+
+ install_cmd = "install -d %s/extlinux/" % hdddir
+ exec_cmd(install_cmd)
+ cfg = open("%s/extlinux/extlinux.conf" % hdddir, "w")
+ cfg.write(extlinux_conf)
+ cfg.close()
+
+
+ @classmethod
+ def do_prepare_partition(cls, part, source_params, cr, cr_workdir,
+ oe_builddir, bootimg_dir, kernel_dir,
+ rootfs_dir, native_sysroot):
+ """
+ Called to do the actual content population for a partition i.e. it
+ 'prepares' the partition to be incorporated into the image.
+ In this case, does the following:
+ - sets up a vfat partition
+ - copies all files listed in IMAGE_BOOT_FILES variable
+ """
+ hdddir = "%s/boot.%d" % (cr_workdir, part.lineno)
+ if not kernel_dir:
+ kernel_dir = get_bitbake_var("DEPLOY_DIR_IMAGE")
+ if not kernel_dir:
+ raise WicError("Couldn't find DEPLOY_DIR_IMAGE, exiting")
+
+ logger.debug('Kernel dir: %s', bootimg_dir)
+
+
+ for task in cls.install_task:
+ src_path, dst_path = task
+ logger.debug('Install %s as %s', src_path, dst_path)
+ install_cmd = "install -m 0644 -D %s %s" \
+ % (os.path.join(kernel_dir, src_path),
+ os.path.join(hdddir, dst_path))
+ exec_cmd(install_cmd)
+
+ logger.debug('Prepare boot partition using rootfs in %s', hdddir)
+ part.prepare_rootfs(cr_workdir, oe_builddir, hdddir,
+ native_sysroot, False)
diff --git a/scripts/lib/wic/plugins/source/bootimg-pcbios.py b/scripts/lib/wic/plugins/source/bootimg-pcbios.py
index 96ed54dbad..32e47f1831 100644
--- a/scripts/lib/wic/plugins/source/bootimg-pcbios.py
+++ b/scripts/lib/wic/plugins/source/bootimg-pcbios.py
@@ -1,21 +1,7 @@
-# ex:ts=4:sw=4:sts=4:et
-# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
#
# Copyright (c) 2014, Intel Corporation.
-# All rights reserved.
#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 as
-# published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+# SPDX-License-Identifier: GPL-2.0-only
#
# DESCRIPTION
# This implements the 'bootimg-pcbios' source plugin class for 'wic'
@@ -24,14 +10,17 @@
# Tom Zanussi <tom.zanussi (at] linux.intel.com>
#
+import logging
import os
+import re
-from wic.utils.errors import ImageError
-from wic import kickstart, msger
-from wic.utils import runner
+from wic import WicError
+from wic.engine import get_custom_config
from wic.pluginbase import SourcePlugin
-from wic.utils.oe.misc import exec_cmd, exec_native_cmd, \
- get_bitbake_var, BOOTDD_EXTRA_SPACE
+from wic.misc import (exec_cmd, exec_native_cmd,
+ get_bitbake_var, BOOTDD_EXTRA_SPACE)
+
+logger = logging.getLogger('wic')
class BootimgPcbiosPlugin(SourcePlugin):
"""
@@ -41,33 +30,51 @@ class BootimgPcbiosPlugin(SourcePlugin):
name = 'bootimg-pcbios'
@classmethod
+ def _get_bootimg_dir(cls, bootimg_dir, dirname):
+ """
+ Check if dirname exists in default bootimg_dir or in STAGING_DIR.
+ """
+ staging_datadir = get_bitbake_var("STAGING_DATADIR")
+ for result in (bootimg_dir, staging_datadir):
+ if os.path.exists("%s/%s" % (result, dirname)):
+ return result
+
+ # STAGING_DATADIR is expanded with MLPREFIX if multilib is enabled
+ # but dependency syslinux is still populated to original STAGING_DATADIR
+ nonarch_datadir = re.sub('/[^/]*recipe-sysroot', '/recipe-sysroot', staging_datadir)
+ if os.path.exists(os.path.join(nonarch_datadir, dirname)):
+ return nonarch_datadir
+
+ raise WicError("Couldn't find correct bootimg_dir, exiting")
+
+ @classmethod
def do_install_disk(cls, disk, disk_name, creator, workdir, oe_builddir,
bootimg_dir, kernel_dir, native_sysroot):
"""
Called after all partitions have been prepared and assembled into a
disk image. In this case, we install the MBR.
"""
+ bootimg_dir = cls._get_bootimg_dir(bootimg_dir, 'syslinux')
mbrfile = "%s/syslinux/" % bootimg_dir
if creator.ptable_format == 'msdos':
mbrfile += "mbr.bin"
elif creator.ptable_format == 'gpt':
mbrfile += "gptmbr.bin"
else:
- msger.error("Unsupported partition table: %s" % creator.ptable_format)
+ raise WicError("Unsupported partition table: %s" %
+ creator.ptable_format)
if not os.path.exists(mbrfile):
- msger.error("Couldn't find %s. If using the -e option, do you "
- "have the right MACHINE set in local.conf? If not, "
- "is the bootimg_dir path correct?" % mbrfile)
+ raise WicError("Couldn't find %s. If using the -e option, do you "
+ "have the right MACHINE set in local.conf? If not, "
+ "is the bootimg_dir path correct?" % mbrfile)
full_path = creator._full_path(workdir, disk_name, "direct")
- msger.debug("Installing MBR on disk %s as %s with size %s bytes" \
- % (disk_name, full_path, disk['min_size']))
+ logger.debug("Installing MBR on disk %s as %s with size %s bytes",
+ disk_name, full_path, disk.min_size)
- rcode = runner.show(['dd', 'if=%s' % mbrfile,
- 'of=%s' % full_path, 'conv=notrunc'])
- if rcode != 0:
- raise ImageError("Unable to set MBR to %s" % full_path)
+ dd_cmd = "dd if=%s of=%s conv=notrunc" % (mbrfile, full_path)
+ exec_cmd(dd_cmd, native_sysroot)
@classmethod
def do_configure_partition(cls, part, source_params, creator, cr_workdir,
@@ -77,43 +84,52 @@ class BootimgPcbiosPlugin(SourcePlugin):
Called before do_prepare_partition(), creates syslinux config
"""
hdddir = "%s/hdd/boot" % cr_workdir
- rm_cmd = "rm -rf " + cr_workdir
- exec_cmd(rm_cmd)
install_cmd = "install -d %s" % hdddir
exec_cmd(install_cmd)
- splash = os.path.join(cr_workdir, "/hdd/boot/splash.jpg")
- if os.path.exists(splash):
- splashline = "menu background splash.jpg"
- else:
- splashline = ""
-
- options = creator.ks.handler.bootloader.appendLine
-
- syslinux_conf = ""
- syslinux_conf += "PROMPT 0\n"
- timeout = kickstart.get_timeout(creator.ks)
- if not timeout:
- timeout = 0
- syslinux_conf += "TIMEOUT " + str(timeout) + "\n"
- syslinux_conf += "\n"
- syslinux_conf += "ALLOWOPTIONS 1\n"
- syslinux_conf += "SERIAL 0 115200\n"
- syslinux_conf += "\n"
- if splashline:
- syslinux_conf += "%s\n" % splashline
- syslinux_conf += "DEFAULT boot\n"
- syslinux_conf += "LABEL boot\n"
-
- kernel = "/vmlinuz"
- syslinux_conf += "KERNEL " + kernel + "\n"
-
- syslinux_conf += "APPEND label=boot root=%s %s\n" % \
- (creator.rootdev, options)
-
- msger.debug("Writing syslinux config %s/hdd/boot/syslinux.cfg" \
- % cr_workdir)
+ bootloader = creator.ks.bootloader
+
+ custom_cfg = None
+ if bootloader.configfile:
+ custom_cfg = get_custom_config(bootloader.configfile)
+ if custom_cfg:
+ # Use a custom configuration for grub
+ syslinux_conf = custom_cfg
+ logger.debug("Using custom configuration file %s "
+ "for syslinux.cfg", bootloader.configfile)
+ else:
+ raise WicError("configfile is specified but failed to "
+ "get it from %s." % bootloader.configfile)
+
+ if not custom_cfg:
+ # Create syslinux configuration using parameters from wks file
+ splash = os.path.join(cr_workdir, "/hdd/boot/splash.jpg")
+ if os.path.exists(splash):
+ splashline = "menu background splash.jpg"
+ else:
+ splashline = ""
+
+ syslinux_conf = ""
+ syslinux_conf += "PROMPT 0\n"
+ syslinux_conf += "TIMEOUT " + str(bootloader.timeout) + "\n"
+ syslinux_conf += "\n"
+ syslinux_conf += "ALLOWOPTIONS 1\n"
+ syslinux_conf += "SERIAL 0 115200\n"
+ syslinux_conf += "\n"
+ if splashline:
+ syslinux_conf += "%s\n" % splashline
+ syslinux_conf += "DEFAULT boot\n"
+ syslinux_conf += "LABEL boot\n"
+
+ kernel = "/vmlinuz"
+ syslinux_conf += "KERNEL " + kernel + "\n"
+
+ syslinux_conf += "APPEND label=boot root=%s %s\n" % \
+ (creator.rootdev, bootloader.append)
+
+ logger.debug("Writing syslinux config %s/hdd/boot/syslinux.cfg",
+ cr_workdir)
cfg = open("%s/hdd/boot/syslinux.cfg" % cr_workdir, "w")
cfg.write(syslinux_conf)
cfg.close()
@@ -127,33 +143,31 @@ class BootimgPcbiosPlugin(SourcePlugin):
'prepares' the partition to be incorporated into the image.
In this case, prepare content for legacy bios boot partition.
"""
- def _has_syslinux(dirname):
- if dirname:
- syslinux = "%s/syslinux" % dirname
- if os.path.exists(syslinux):
- return True
- return False
-
- if not _has_syslinux(bootimg_dir):
- bootimg_dir = get_bitbake_var("STAGING_DATADIR")
- if not bootimg_dir:
- msger.error("Couldn't find STAGING_DATADIR, exiting\n")
- if not _has_syslinux(bootimg_dir):
- msger.error("Please build syslinux first\n")
- # just so the result notes display it
- creator.set_bootimg_dir(bootimg_dir)
+ bootimg_dir = cls._get_bootimg_dir(bootimg_dir, 'syslinux')
staging_kernel_dir = kernel_dir
hdddir = "%s/hdd/boot" % cr_workdir
- install_cmd = "install -m 0644 %s/bzImage %s/vmlinuz" \
- % (staging_kernel_dir, hdddir)
- exec_cmd(install_cmd)
-
- install_cmd = "install -m 444 %s/syslinux/ldlinux.sys %s/ldlinux.sys" \
- % (bootimg_dir, hdddir)
- exec_cmd(install_cmd)
+ kernel = get_bitbake_var("KERNEL_IMAGETYPE")
+ if get_bitbake_var("INITRAMFS_IMAGE_BUNDLE") == "1":
+ if get_bitbake_var("INITRAMFS_IMAGE"):
+ kernel = "%s-%s.bin" % \
+ (get_bitbake_var("KERNEL_IMAGETYPE"), get_bitbake_var("INITRAMFS_LINK_NAME"))
+
+ cmds = ("install -m 0644 %s/%s %s/vmlinuz" %
+ (staging_kernel_dir, kernel, hdddir),
+ "install -m 444 %s/syslinux/ldlinux.sys %s/ldlinux.sys" %
+ (bootimg_dir, hdddir),
+ "install -m 0644 %s/syslinux/vesamenu.c32 %s/vesamenu.c32" %
+ (bootimg_dir, hdddir),
+ "install -m 444 %s/syslinux/libcom32.c32 %s/libcom32.c32" %
+ (bootimg_dir, hdddir),
+ "install -m 444 %s/syslinux/libutil.c32 %s/libutil.c32" %
+ (bootimg_dir, hdddir))
+
+ for install_cmd in cmds:
+ exec_cmd(install_cmd)
du_cmd = "du -bks %s" % hdddir
out = exec_cmd(du_cmd)
@@ -166,19 +180,16 @@ class BootimgPcbiosPlugin(SourcePlugin):
blocks += extra_blocks
- msger.debug("Added %d extra blocks to %s to get to %d total blocks" % \
- (extra_blocks, part.mountpoint, blocks))
-
- # Ensure total sectors is an integral number of sectors per
- # track or mcopy will complain. Sectors are 512 bytes, and we
- # generate images with 32 sectors per track. This calculation is
- # done in blocks, thus the mod by 16 instead of 32.
- blocks += (16 - (blocks % 16))
+ logger.debug("Added %d extra blocks to %s to get to %d total blocks",
+ extra_blocks, part.mountpoint, blocks)
# dosfs image, created by mkdosfs
- bootimg = "%s/boot.img" % cr_workdir
+ bootimg = "%s/boot%s.img" % (cr_workdir, part.lineno)
+
+ label = part.label if part.label else "boot"
- dosfs_cmd = "mkdosfs -n boot -S 512 -C %s %d" % (bootimg, blocks)
+ dosfs_cmd = "mkdosfs -n %s -i %s -S 512 -C %s %d" % \
+ (label, part.fsuuid, bootimg, blocks)
exec_native_cmd(dosfs_cmd, native_sysroot)
mcopy_cmd = "mcopy -i %s -s %s/* ::/" % (bootimg, hdddir)
@@ -194,7 +205,5 @@ class BootimgPcbiosPlugin(SourcePlugin):
out = exec_cmd(du_cmd)
bootimg_size = out.split()[0]
- part.set_size(bootimg_size)
- part.set_source_file(bootimg)
-
-
+ part.size = int(bootimg_size)
+ part.source_file = bootimg
diff --git a/scripts/lib/wic/plugins/source/empty.py b/scripts/lib/wic/plugins/source/empty.py
new file mode 100644
index 0000000000..041617d648
--- /dev/null
+++ b/scripts/lib/wic/plugins/source/empty.py
@@ -0,0 +1,32 @@
+#
+# SPDX-License-Identifier: MIT
+#
+
+# The empty wic plugin is used to create unformatted empty partitions for wic
+# images.
+# To use it you must pass "empty" as argument for the "--source" parameter in
+# the wks file. For example:
+# part foo --source empty --ondisk sda --size="1024" --align 1024
+
+import logging
+
+from wic.pluginbase import SourcePlugin
+
+logger = logging.getLogger('wic')
+
+class EmptyPartitionPlugin(SourcePlugin):
+ """
+ Populate unformatted empty partition.
+ """
+
+ name = 'empty'
+
+ @classmethod
+ def do_prepare_partition(cls, part, source_params, cr, cr_workdir,
+ oe_builddir, bootimg_dir, kernel_dir,
+ rootfs_dir, native_sysroot):
+ """
+ Called to do the actual content population for a partition i.e. it
+ 'prepares' the partition to be incorporated into the image.
+ """
+ return
diff --git a/scripts/lib/wic/plugins/source/fsimage.py b/scripts/lib/wic/plugins/source/fsimage.py
deleted file mode 100644
index f894e89367..0000000000
--- a/scripts/lib/wic/plugins/source/fsimage.py
+++ /dev/null
@@ -1,73 +0,0 @@
-# ex:ts=4:sw=4:sts=4:et
-# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 as
-# published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-#
-
-import os
-
-from wic import msger
-from wic.pluginbase import SourcePlugin
-from wic.utils.oe.misc import get_bitbake_var
-
-class FSImagePlugin(SourcePlugin):
- """
- Add an already existing filesystem image to the partition layout.
- """
-
- name = 'fsimage'
-
- @classmethod
- def do_install_disk(cls, disk, disk_name, cr, workdir, oe_builddir,
- bootimg_dir, kernel_dir, native_sysroot):
- """
- Called after all partitions have been prepared and assembled into a
- disk image. Do nothing.
- """
- pass
-
- @classmethod
- def do_configure_partition(cls, part, source_params, cr, cr_workdir,
- oe_builddir, bootimg_dir, kernel_dir,
- native_sysroot):
- """
- Called before do_prepare_partition(). Possibly prepare
- configuration files of some sort.
- """
- pass
-
- @classmethod
- def do_prepare_partition(cls, part, source_params, cr, cr_workdir,
- oe_builddir, bootimg_dir, kernel_dir,
- rootfs_dir, native_sysroot):
- """
- Called to do the actual content population for a partition i.e. it
- 'prepares' the partition to be incorporated into the image.
- """
- if not bootimg_dir:
- bootimg_dir = get_bitbake_var("DEPLOY_DIR_IMAGE")
- if not bootimg_dir:
- msger.error("Couldn't find DEPLOY_DIR_IMAGE, exiting\n")
-
- msger.debug('Bootimg dir: %s' % bootimg_dir)
-
- if 'file' not in source_params:
- msger.error("No file specified\n")
- return
-
- src = os.path.join(bootimg_dir, source_params['file'])
-
-
- msger.debug('Preparing partition using image %s' % (src))
- part.prepare_rootfs_from_fs_image(cr_workdir, src, "")
diff --git a/scripts/lib/wic/plugins/source/isoimage-isohybrid.py b/scripts/lib/wic/plugins/source/isoimage-isohybrid.py
index 9472d8abb9..afc9ea0f8f 100644
--- a/scripts/lib/wic/plugins/source/isoimage-isohybrid.py
+++ b/scripts/lib/wic/plugins/source/isoimage-isohybrid.py
@@ -1,18 +1,5 @@
-# ex:ts=4:sw=4:sts=4:et
-# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
-
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 as
-# published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+# SPDX-License-Identifier: GPL-2.0-only
#
# DESCRIPTION
# This implements the 'isoimage-isohybrid' source plugin class for 'wic'
@@ -20,13 +7,18 @@
# AUTHORS
# Mihaly Varga <mihaly.varga (at] ni.com>
+import glob
+import logging
import os
import re
import shutil
-from wic import kickstart, msger
+from wic import WicError
+from wic.engine import get_custom_config
from wic.pluginbase import SourcePlugin
-from wic.utils.oe.misc import exec_cmd, exec_native_cmd, get_bitbake_var
+from wic.misc import exec_cmd, exec_native_cmd, get_bitbake_var
+
+logger = logging.getLogger('wic')
class IsoImagePlugin(SourcePlugin):
"""
@@ -42,7 +34,7 @@ class IsoImagePlugin(SourcePlugin):
Example kickstart file:
part /boot --source isoimage-isohybrid --sourceparams="loader=grub-efi, \\
- image_name= IsoImage" --ondisk cd --label LIVECD --fstype=ext2
+ image_name= IsoImage" --ondisk cd --label LIVECD
bootloader --timeout=10 --append=" "
In --sourceparams "loader" specifies the bootloader used for booting in EFI
@@ -58,19 +50,17 @@ class IsoImagePlugin(SourcePlugin):
"""
Create loader-specific (syslinux) config
"""
- splash = os.path.join(cr_workdir, "/ISO/boot/splash.jpg")
+ splash = os.path.join(cr_workdir, "ISO/boot/splash.jpg")
if os.path.exists(splash):
splashline = "menu background splash.jpg"
else:
splashline = ""
- options = creator.ks.handler.bootloader.appendLine
-
- timeout = kickstart.get_timeout(creator.ks, 10)
+ bootloader = creator.ks.bootloader
syslinux_conf = ""
syslinux_conf += "PROMPT 0\n"
- syslinux_conf += "TIMEOUT %s \n" % timeout
+ syslinux_conf += "TIMEOUT %s \n" % (bootloader.timeout or 10)
syslinux_conf += "\n"
syslinux_conf += "ALLOWOPTIONS 1\n"
syslinux_conf += "SERIAL 0 115200\n"
@@ -80,52 +70,73 @@ class IsoImagePlugin(SourcePlugin):
syslinux_conf += "DEFAULT boot\n"
syslinux_conf += "LABEL boot\n"
- kernel = "/bzImage"
- syslinux_conf += "KERNEL " + kernel + "\n"
- syslinux_conf += "APPEND initrd=/initrd LABEL=boot %s\n" % options
+ kernel = get_bitbake_var("KERNEL_IMAGETYPE")
+ if get_bitbake_var("INITRAMFS_IMAGE_BUNDLE") == "1":
+ if get_bitbake_var("INITRAMFS_IMAGE"):
+ kernel = "%s-%s.bin" % \
+ (get_bitbake_var("KERNEL_IMAGETYPE"), get_bitbake_var("INITRAMFS_LINK_NAME"))
+
+ syslinux_conf += "KERNEL /" + kernel + "\n"
+ syslinux_conf += "APPEND initrd=/initrd LABEL=boot %s\n" \
+ % bootloader.append
+
+ logger.debug("Writing syslinux config %s/ISO/isolinux/isolinux.cfg",
+ cr_workdir)
- msger.debug("Writing syslinux config %s/ISO/isolinux/isolinux.cfg" \
- % cr_workdir)
with open("%s/ISO/isolinux/isolinux.cfg" % cr_workdir, "w") as cfg:
cfg.write(syslinux_conf)
@classmethod
- def do_configure_grubefi(cls, part, creator, cr_workdir):
+ def do_configure_grubefi(cls, part, creator, target_dir):
"""
Create loader-specific (grub-efi) config
"""
- splash = os.path.join(cr_workdir, "/EFI/boot/splash.jpg")
- if os.path.exists(splash):
- splashline = "menu background splash.jpg"
+ configfile = creator.ks.bootloader.configfile
+ if configfile:
+ grubefi_conf = get_custom_config(configfile)
+ if grubefi_conf:
+ logger.debug("Using custom configuration file %s for grub.cfg",
+ configfile)
+ else:
+ raise WicError("configfile is specified "
+ "but failed to get it from %s", configfile)
else:
- splashline = ""
-
- options = creator.ks.handler.bootloader.appendLine
-
- grubefi_conf = ""
- grubefi_conf += "serial --unit=0 --speed=115200 --word=8 "
- grubefi_conf += "--parity=no --stop=1\n"
- grubefi_conf += "default=boot\n"
- timeout = kickstart.get_timeout(creator.ks, 10)
- grubefi_conf += "timeout=%s\n" % timeout
- grubefi_conf += "\n"
- grubefi_conf += "search --set=root --label %s " % part.label
- grubefi_conf += "\n"
- grubefi_conf += "menuentry 'boot'{\n"
-
- kernel = "/bzImage"
-
- grubefi_conf += "linux %s rootwait %s\n" \
- % (kernel, options)
- grubefi_conf += "initrd /initrd \n"
- grubefi_conf += "}\n"
-
- if splashline:
- grubefi_conf += "%s\n" % splashline
-
- msger.debug("Writing grubefi config %s/EFI/BOOT/grub.cfg" \
- % cr_workdir)
- with open("%s/EFI/BOOT/grub.cfg" % cr_workdir, "w") as cfg:
+ splash = os.path.join(target_dir, "splash.jpg")
+ if os.path.exists(splash):
+ splashline = "menu background splash.jpg"
+ else:
+ splashline = ""
+
+ bootloader = creator.ks.bootloader
+
+ grubefi_conf = ""
+ grubefi_conf += "serial --unit=0 --speed=115200 --word=8 "
+ grubefi_conf += "--parity=no --stop=1\n"
+ grubefi_conf += "default=boot\n"
+ grubefi_conf += "timeout=%s\n" % (bootloader.timeout or 10)
+ grubefi_conf += "\n"
+ grubefi_conf += "search --set=root --label %s " % part.label
+ grubefi_conf += "\n"
+ grubefi_conf += "menuentry 'boot'{\n"
+
+ kernel = get_bitbake_var("KERNEL_IMAGETYPE")
+ if get_bitbake_var("INITRAMFS_IMAGE_BUNDLE") == "1":
+ if get_bitbake_var("INITRAMFS_IMAGE"):
+ kernel = "%s-%s.bin" % \
+ (get_bitbake_var("KERNEL_IMAGETYPE"), get_bitbake_var("INITRAMFS_LINK_NAME"))
+
+ grubefi_conf += "linux /%s rootwait %s\n" \
+ % (kernel, bootloader.append)
+ grubefi_conf += "initrd /initrd \n"
+ grubefi_conf += "}\n"
+
+ if splashline:
+ grubefi_conf += "%s\n" % splashline
+
+ cfg_path = os.path.join(target_dir, "grub.cfg")
+ logger.debug("Writing grubefi config %s", cfg_path)
+
+ with open(cfg_path, "w") as cfg:
cfg.write(grubefi_conf)
@staticmethod
@@ -134,28 +145,28 @@ class IsoImagePlugin(SourcePlugin):
Create path for initramfs image
"""
- initrd = get_bitbake_var("INITRD")
+ initrd = get_bitbake_var("INITRD_LIVE") or get_bitbake_var("INITRD")
if not initrd:
initrd_dir = get_bitbake_var("DEPLOY_DIR_IMAGE")
if not initrd_dir:
- msger.error("Couldn't find DEPLOY_DIR_IMAGE, exiting.\n")
+ raise WicError("Couldn't find DEPLOY_DIR_IMAGE, exiting.")
image_name = get_bitbake_var("IMAGE_BASENAME")
if not image_name:
- msger.error("Couldn't find IMAGE_BASENAME, exiting.\n")
+ raise WicError("Couldn't find IMAGE_BASENAME, exiting.")
image_type = get_bitbake_var("INITRAMFS_FSTYPES")
if not image_type:
- msger.error("Couldn't find INITRAMFS_FSTYPES, exiting.\n")
+ raise WicError("Couldn't find INITRAMFS_FSTYPES, exiting.")
- machine_arch = get_bitbake_var("MACHINE_ARCH")
- if not machine_arch:
- msger.error("Couldn't find MACHINE_ARCH, exiting.\n")
+ machine = os.path.basename(initrd_dir)
- initrd = "%s/%s-initramfs-%s.%s" \
- % (initrd_dir, image_name, machine_arch, image_type)
+ pattern = '%s/%s*%s.%s' % (initrd_dir, image_name, machine, image_type)
+ files = glob.glob(pattern)
+ if files:
+ initrd = files[0]
- if not os.path.exists(initrd):
+ if not initrd or not os.path.exists(initrd):
# Create initrd from rootfs directory
initrd = "%s/initrd.cpio.gz" % cr_workdir
initrd_dir = "%s/INITRD" % cr_workdir
@@ -174,63 +185,16 @@ class IsoImagePlugin(SourcePlugin):
os.symlink(os.readlink("%s/sbin/init" % rootfs_dir), \
"%s/init" % initrd_dir)
else:
- msger.error("Couldn't find or build initrd, exiting.\n")
+ raise WicError("Couldn't find or build initrd, exiting.")
- exec_cmd("cd %s && find . | cpio -o -H newc >%s/initrd.cpio " \
- % (initrd_dir, cr_workdir), as_shell=True)
- exec_cmd("gzip -f -9 -c %s/initrd.cpio > %s" \
- % (cr_workdir, initrd), as_shell=True)
+ exec_cmd("cd %s && find . | cpio -o -H newc -R root:root >%s/initrd.cpio " \
+ % (initrd_dir, cr_workdir), as_shell=True)
+ exec_cmd("gzip -f -9 %s/initrd.cpio" % cr_workdir, as_shell=True)
shutil.rmtree(initrd_dir)
return initrd
@classmethod
- def do_stage_partition(cls, part, source_params, creator, cr_workdir,
- oe_builddir, bootimg_dir, kernel_dir,
- native_sysroot):
- """
- Special content staging called before do_prepare_partition().
- It cheks if all necessary tools are available, if not
- tries to instal them.
- """
- # Make sure parted is available in native sysroot
- if not os.path.isfile("%s/usr/sbin/parted" % native_sysroot):
- msger.info("Building parted-native...\n")
- exec_cmd("bitbake parted-native")
-
- # Make sure mkfs.ext2/3/4 is available in native sysroot
- if not os.path.isfile("%s/sbin/mkfs.ext2" % native_sysroot):
- msger.info("Building e2fsprogs-native...\n")
- exec_cmd("bitbake e2fsprogs-native")
-
- # Make sure syslinux is available in sysroot and in native sysroot
- syslinux_dir = get_bitbake_var("STAGING_DATADIR")
- if not syslinux_dir:
- msger.error("Couldn't find STAGING_DATADIR, exiting.\n")
- if not os.path.exists("%s/syslinux" % syslinux_dir):
- msger.info("Building syslinux...\n")
- exec_cmd("bitbake syslinux")
- msger.info("Building syslinux-native...\n")
- exec_cmd("bitbake syslinux-native")
- if not os.path.exists("%s/syslinux" % syslinux_dir):
- msger.error("Please build syslinux first\n")
-
- #Make sure mkisofs is available in native sysroot
- if not os.path.isfile("%s/usr/bin/mkisofs" % native_sysroot):
- msger.info("Building cdrtools-native...\n")
- exec_cmd("bitbake cdrtools-native")
-
- # Make sure mkfs.vfat is available in native sysroot
- if not os.path.isfile("%s/sbin/mkfs.vfat" % native_sysroot):
- msger.info("Building dosfstools-native...\n")
- exec_cmd("bitbake dosfstools-native")
-
- # Make sure mtools is available in native sysroot
- if not os.path.isfile("%s/usr/bin/mcopy" % native_sysroot):
- msger.info("Building mtools-native...\n")
- exec_cmd("bitbake mtools-native")
-
- @classmethod
def do_configure_partition(cls, part, source_params, creator, cr_workdir,
oe_builddir, bootimg_dir, kernel_dir,
native_sysroot):
@@ -239,18 +203,30 @@ class IsoImagePlugin(SourcePlugin):
"""
isodir = "%s/ISO/" % cr_workdir
- if os.path.exists(cr_workdir):
- shutil.rmtree(cr_workdir)
+ if os.path.exists(isodir):
+ shutil.rmtree(isodir)
install_cmd = "install -d %s " % isodir
exec_cmd(install_cmd)
# Overwrite the name of the created image
- msger.debug("%s" % source_params)
+ logger.debug(source_params)
if 'image_name' in source_params and \
source_params['image_name'].strip():
creator.name = source_params['image_name'].strip()
- msger.debug("The name of the image is: %s" % creator.name)
+ logger.debug("The name of the image is: %s", creator.name)
+
+ @staticmethod
+ def _install_payload(source_params, iso_dir):
+ """
+ Copies contents of payload directory (as specified in 'payload_dir' param) into iso_dir
+ """
+
+ if source_params.get('payload_dir'):
+ payload_dir = source_params['payload_dir']
+
+ logger.debug("Payload directory: %s", payload_dir)
+ shutil.copytree(payload_dir, iso_dir, symlinks=True, dirs_exist_ok=True)
@classmethod
def do_prepare_partition(cls, part, source_params, creator, cr_workdir,
@@ -264,154 +240,108 @@ class IsoImagePlugin(SourcePlugin):
isodir = "%s/ISO" % cr_workdir
- if part.rootfs is None:
+ cls._install_payload(source_params, isodir)
+
+ if part.rootfs_dir is None:
if not 'ROOTFS_DIR' in rootfs_dir:
- msger.error("Couldn't find --rootfs-dir, exiting.\n")
+ raise WicError("Couldn't find --rootfs-dir, exiting.")
rootfs_dir = rootfs_dir['ROOTFS_DIR']
else:
- if part.rootfs in rootfs_dir:
- rootfs_dir = rootfs_dir[part.rootfs]
- elif part.rootfs:
- rootfs_dir = part.rootfs
+ if part.rootfs_dir in rootfs_dir:
+ rootfs_dir = rootfs_dir[part.rootfs_dir]
+ elif part.rootfs_dir:
+ rootfs_dir = part.rootfs_dir
else:
- msg = "Couldn't find --rootfs-dir=%s connection "
- msg += "or it is not a valid path, exiting.\n"
- msger.error(msg % part.rootfs)
+ raise WicError("Couldn't find --rootfs-dir=%s connection "
+ "or it is not a valid path, exiting." %
+ part.rootfs_dir)
if not os.path.isdir(rootfs_dir):
rootfs_dir = get_bitbake_var("IMAGE_ROOTFS")
if not os.path.isdir(rootfs_dir):
- msger.error("Couldn't find IMAGE_ROOTFS, exiting.\n")
+ raise WicError("Couldn't find IMAGE_ROOTFS, exiting.")
- part.set_rootfs(rootfs_dir)
-
- # Prepare rootfs.img
- hdd_dir = get_bitbake_var("HDDDIR")
+ part.rootfs_dir = rootfs_dir
+ deploy_dir = get_bitbake_var("DEPLOY_DIR_IMAGE")
img_iso_dir = get_bitbake_var("ISODIR")
- rootfs_img = "%s/rootfs.img" % hdd_dir
- if not os.path.isfile(rootfs_img):
- rootfs_img = "%s/rootfs.img" % img_iso_dir
- if not os.path.isfile(rootfs_img):
- # check if rootfs.img is in deploydir
- deploy_dir = get_bitbake_var("DEPLOY_DIR_IMAGE")
- image_name = get_bitbake_var("IMAGE_LINK_NAME")
- rootfs_img = "%s/%s.%s" \
- % (deploy_dir, image_name, part.fstype)
-
- if not os.path.isfile(rootfs_img):
- # create image file with type specified by --fstype
- # which contains rootfs
- du_cmd = "du -bks %s" % rootfs_dir
- out = exec_cmd(du_cmd)
- part.set_size(int(out.split()[0]))
- part.extra_space = 0
- part.overhead_factor = 1.2
- part.prepare_rootfs(cr_workdir, oe_builddir, rootfs_dir, \
- native_sysroot)
- rootfs_img = part.source_file
-
- install_cmd = "install -m 0644 %s %s/rootfs.img" \
- % (rootfs_img, isodir)
- exec_cmd(install_cmd)
-
# Remove the temporary file created by part.prepare_rootfs()
if os.path.isfile(part.source_file):
os.remove(part.source_file)
- # Prepare initial ramdisk
- initrd = "%s/initrd" % hdd_dir
- if not os.path.isfile(initrd):
- initrd = "%s/initrd" % img_iso_dir
- if not os.path.isfile(initrd):
- initrd = cls._build_initramfs_path(rootfs_dir, cr_workdir)
-
- install_cmd = "install -m 0644 %s %s/initrd" \
- % (initrd, isodir)
+ # Support using a different initrd other than default
+ if source_params.get('initrd'):
+ initrd = source_params['initrd']
+ if not deploy_dir:
+ raise WicError("Couldn't find DEPLOY_DIR_IMAGE, exiting")
+ cp_cmd = "cp %s/%s %s" % (deploy_dir, initrd, cr_workdir)
+ exec_cmd(cp_cmd)
+ else:
+ # Prepare initial ramdisk
+ initrd = "%s/initrd" % deploy_dir
+ if not os.path.isfile(initrd):
+ initrd = "%s/initrd" % img_iso_dir
+ if not os.path.isfile(initrd):
+ initrd = cls._build_initramfs_path(rootfs_dir, cr_workdir)
+
+ install_cmd = "install -m 0644 %s %s/initrd" % (initrd, isodir)
exec_cmd(install_cmd)
# Remove the temporary file created by _build_initramfs_path function
if os.path.isfile("%s/initrd.cpio.gz" % cr_workdir):
os.remove("%s/initrd.cpio.gz" % cr_workdir)
- # Install bzImage
- install_cmd = "install -m 0644 %s/bzImage %s/bzImage" % \
- (kernel_dir, isodir)
+ kernel = get_bitbake_var("KERNEL_IMAGETYPE")
+ if get_bitbake_var("INITRAMFS_IMAGE_BUNDLE") == "1":
+ if get_bitbake_var("INITRAMFS_IMAGE"):
+ kernel = "%s-%s.bin" % \
+ (get_bitbake_var("KERNEL_IMAGETYPE"), get_bitbake_var("INITRAMFS_LINK_NAME"))
+
+ install_cmd = "install -m 0644 %s/%s %s/%s" % \
+ (kernel_dir, kernel, isodir, kernel)
exec_cmd(install_cmd)
#Create bootloader for efi boot
try:
- if source_params['loader'] == 'grub-efi':
- # Builds grub.cfg if ISODIR didn't exist or
- # didn't contains grub.cfg
- bootimg_dir = img_iso_dir
- if not os.path.exists("%s/EFI/BOOT" % bootimg_dir):
- bootimg_dir = "%s/bootimg" % cr_workdir
- if os.path.exists(bootimg_dir):
- shutil.rmtree(bootimg_dir)
- install_cmd = "install -d %s/EFI/BOOT" % bootimg_dir
- exec_cmd(install_cmd)
-
- if not os.path.isfile("%s/EFI/BOOT/boot.cfg" % bootimg_dir):
- cls.do_configure_grubefi(part, creator, bootimg_dir)
+ target_dir = "%s/EFI/BOOT" % isodir
+ if os.path.exists(target_dir):
+ shutil.rmtree(target_dir)
+
+ os.makedirs(target_dir)
+ if source_params['loader'] == 'grub-efi':
# Builds bootx64.efi/bootia32.efi if ISODIR didn't exist or
# didn't contains it
target_arch = get_bitbake_var("TARGET_SYS")
if not target_arch:
- msger.error("Coludn't find target architecture\n")
+ raise WicError("Coludn't find target architecture")
if re.match("x86_64", target_arch):
- grub_target = 'x86_64-efi'
- grub_image = "bootx64.efi"
+ grub_src_image = "grub-efi-bootx64.efi"
+ grub_dest_image = "bootx64.efi"
elif re.match('i.86', target_arch):
- grub_target = 'i386-efi'
- grub_image = "bootia32.efi"
+ grub_src_image = "grub-efi-bootia32.efi"
+ grub_dest_image = "bootia32.efi"
else:
- msger.error("grub-efi is incompatible with target %s\n" \
- % target_arch)
-
- if not os.path.isfile("%s/EFI/BOOT/%s" \
- % (bootimg_dir, grub_image)):
- grub_path = get_bitbake_var("STAGING_LIBDIR")
- if not grub_path:
- msger.error("Couldn't find STAGING_LIBDIR, exiting.\n")
-
- grub_core = "%s/grub/%s" % (grub_path, grub_target)
- if not os.path.exists(grub_core):
- msger.info("Building grub-efi...\n")
- exec_cmd("bitbake grub-efi")
- if not os.path.exists(grub_core):
- msger.error("Please build grub-efi first\n")
-
- grub_cmd = "grub-mkimage -p '/EFI/BOOT' "
- grub_cmd += "-d %s " % grub_core
- grub_cmd += "-O %s -o %s/EFI/BOOT/%s " \
- % (grub_target, bootimg_dir, grub_image)
- grub_cmd += "part_gpt part_msdos ntfs ntfscomp fat ext2 "
- grub_cmd += "normal chain boot configfile linux multiboot "
- grub_cmd += "search efi_gop efi_uga font gfxterm gfxmenu "
- grub_cmd += "terminal minicmd test iorw loadenv echo help "
- grub_cmd += "reboot serial terminfo iso9660 loopback tar "
- grub_cmd += "memdisk ls search_fs_uuid udf btrfs xfs lvm "
- grub_cmd += "reiserfs ata "
- exec_native_cmd(grub_cmd, native_sysroot)
-
- else:
- # TODO: insert gummiboot stuff
- msger.error("unrecognized bootimg-efi loader: %s" \
- % source_params['loader'])
- except KeyError:
- msger.error("bootimg-efi requires a loader, none specified")
+ raise WicError("grub-efi is incompatible with target %s" %
+ target_arch)
- if os.path.exists("%s/EFI/BOOT" % isodir):
- shutil.rmtree("%s/EFI/BOOT" % isodir)
+ grub_target = os.path.join(target_dir, grub_dest_image)
+ if not os.path.isfile(grub_target):
+ grub_src = os.path.join(deploy_dir, grub_src_image)
+ if not os.path.exists(grub_src):
+ raise WicError("Grub loader %s is not found in %s. "
+ "Please build grub-efi first" % (grub_src_image, deploy_dir))
+ shutil.copy(grub_src, grub_target)
- shutil.copytree(bootimg_dir+"/EFI/BOOT", isodir+"/EFI/BOOT")
+ if not os.path.isfile(os.path.join(target_dir, "boot.cfg")):
+ cls.do_configure_grubefi(part, creator, target_dir)
- # If exists, remove cr_workdir/bootimg temporary folder
- if os.path.exists("%s/bootimg" % cr_workdir):
- shutil.rmtree("%s/bootimg" % cr_workdir)
+ else:
+ raise WicError("unrecognized bootimg-efi loader: %s" %
+ source_params['loader'])
+ except KeyError:
+ raise WicError("bootimg-efi requires a loader, none specified")
# Create efi.img that contains bootloader files for EFI booting
# if ISODIR didn't exist or didn't contains it
@@ -420,26 +350,23 @@ class IsoImagePlugin(SourcePlugin):
(img_iso_dir, isodir)
exec_cmd(install_cmd)
else:
+ # Default to 100 blocks of extra space for file system overhead
+ esp_extra_blocks = int(source_params.get('esp_extra_blocks', '100'))
+
du_cmd = "du -bks %s/EFI" % isodir
out = exec_cmd(du_cmd)
blocks = int(out.split()[0])
- # Add some extra space for file system overhead
- blocks += 100
- msg = "Added 100 extra blocks to %s to get to %d total blocks" \
- % (part.mountpoint, blocks)
- msger.debug(msg)
-
- # Ensure total sectors is an integral number of sectors per
- # track or mcopy will complain. Sectors are 512 bytes, and we
- # generate images with 32 sectors per track. This calculation is
- # done in blocks, thus the mod by 16 instead of 32.
- blocks += (16 - (blocks % 16))
+ blocks += esp_extra_blocks
+ logger.debug("Added 100 extra blocks to %s to get to %d "
+ "total blocks", part.mountpoint, blocks)
# dosfs image for EFI boot
bootimg = "%s/efi.img" % isodir
- dosfs_cmd = 'mkfs.vfat -n "EFIimg" -S 512 -C %s %d' \
- % (bootimg, blocks)
+ esp_label = source_params.get('esp_label', 'EFIimg')
+
+ dosfs_cmd = 'mkfs.vfat -n \'%s\' -S 512 -C %s %d' \
+ % (esp_label, bootimg, blocks)
exec_native_cmd(dosfs_cmd, native_sysroot)
mmd_cmd = "mmd -i %s ::/EFI" % bootimg
@@ -455,7 +382,7 @@ class IsoImagePlugin(SourcePlugin):
# Prepare files for legacy boot
syslinux_dir = get_bitbake_var("STAGING_DATADIR")
if not syslinux_dir:
- msger.error("Couldn't find STAGING_DATADIR, exiting.\n")
+ raise WicError("Couldn't find STAGING_DATADIR, exiting.")
if os.path.exists("%s/isolinux" % isodir):
shutil.rmtree("%s/isolinux" % isodir)
@@ -495,7 +422,7 @@ class IsoImagePlugin(SourcePlugin):
mkisofs_cmd += "-eltorito-platform 0xEF -eltorito-boot %s " % efi_img
mkisofs_cmd += "-no-emul-boot %s " % isodir
- msger.debug("running command: %s" % mkisofs_cmd)
+ logger.debug("running command: %s", mkisofs_cmd)
exec_native_cmd(mkisofs_cmd, native_sysroot)
shutil.rmtree(isodir)
@@ -504,8 +431,8 @@ class IsoImagePlugin(SourcePlugin):
out = exec_cmd(du_cmd)
isoimg_size = int(out.split()[0])
- part.set_size(isoimg_size)
- part.set_source_file(iso_img)
+ part.size = isoimg_size
+ part.source_file = iso_img
@classmethod
def do_install_disk(cls, disk, disk_name, creator, workdir, oe_builddir,
@@ -516,23 +443,19 @@ class IsoImagePlugin(SourcePlugin):
utility for booting via BIOS from disk storage devices.
"""
+ iso_img = "%s.p1" % disk.path
full_path = creator._full_path(workdir, disk_name, "direct")
- iso_img = "%s.p1" % full_path
full_path_iso = creator._full_path(workdir, disk_name, "iso")
isohybrid_cmd = "isohybrid -u %s" % iso_img
- msger.debug("running command: %s" % \
- isohybrid_cmd)
+ logger.debug("running command: %s", isohybrid_cmd)
exec_native_cmd(isohybrid_cmd, native_sysroot)
# Replace the image created by direct plugin with the one created by
# mkisofs command. This is necessary because the iso image created by
# mkisofs has a very specific MBR is system area of the ISO image, and
# direct plugin adds and configures an another MBR.
- msger.debug("Replaceing the image created by direct plugin\n")
- os.remove(full_path)
+ logger.debug("Replaceing the image created by direct plugin\n")
+ os.remove(disk.path)
shutil.copy2(iso_img, full_path_iso)
shutil.copy2(full_path_iso, full_path)
-
- # Remove temporary ISO file
- os.remove(iso_img)
diff --git a/scripts/lib/wic/plugins/source/rawcopy.py b/scripts/lib/wic/plugins/source/rawcopy.py
index f0691baa91..fa7b1eb8ac 100644
--- a/scripts/lib/wic/plugins/source/rawcopy.py
+++ b/scripts/lib/wic/plugins/source/rawcopy.py
@@ -1,25 +1,16 @@
-# ex:ts=4:sw=4:sts=4:et
-# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 as
-# published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+# SPDX-License-Identifier: GPL-2.0-only
#
+import logging
import os
-from wic import msger
+from wic import WicError
from wic.pluginbase import SourcePlugin
-from wic.utils.oe.misc import exec_cmd, get_bitbake_var
+from wic.misc import exec_cmd, get_bitbake_var
+from wic.filemap import sparse_copy
+
+logger = logging.getLogger('wic')
class RawCopyPlugin(SourcePlugin):
"""
@@ -28,24 +19,24 @@ class RawCopyPlugin(SourcePlugin):
name = 'rawcopy'
- @classmethod
- def do_install_disk(cls, disk, disk_name, cr, workdir, oe_builddir,
- bootimg_dir, kernel_dir, native_sysroot):
- """
- Called after all partitions have been prepared and assembled into a
- disk image. Do nothing.
- """
- pass
+ @staticmethod
+ def do_image_label(fstype, dst, label):
+ if fstype.startswith('ext'):
+ cmd = 'tune2fs -L %s %s' % (label, dst)
+ elif fstype in ('msdos', 'vfat'):
+ cmd = 'dosfslabel %s %s' % (dst, label)
+ elif fstype == 'btrfs':
+ cmd = 'btrfs filesystem label %s %s' % (dst, label)
+ elif fstype == 'swap':
+ cmd = 'mkswap -L %s %s' % (label, dst)
+ elif fstype in ('squashfs', 'erofs'):
+ raise WicError("It's not possible to update a %s "
+ "filesystem label '%s'" % (fstype, label))
+ else:
+ raise WicError("Cannot update filesystem label: "
+ "Unknown fstype: '%s'" % (fstype))
- @classmethod
- def do_configure_partition(cls, part, source_params, cr, cr_workdir,
- oe_builddir, bootimg_dir, kernel_dir,
- native_sysroot):
- """
- Called before do_prepare_partition(). Possibly prepare
- configuration files of some sort.
- """
- pass
+ exec_cmd(cmd)
@classmethod
def do_prepare_partition(cls, part, source_params, cr, cr_workdir,
@@ -55,33 +46,36 @@ class RawCopyPlugin(SourcePlugin):
Called to do the actual content population for a partition i.e. it
'prepares' the partition to be incorporated into the image.
"""
- if not bootimg_dir:
- bootimg_dir = get_bitbake_var("DEPLOY_DIR_IMAGE")
- if not bootimg_dir:
- msger.error("Couldn't find DEPLOY_DIR_IMAGE, exiting\n")
+ if not kernel_dir:
+ kernel_dir = get_bitbake_var("DEPLOY_DIR_IMAGE")
+ if not kernel_dir:
+ raise WicError("Couldn't find DEPLOY_DIR_IMAGE, exiting")
- msger.debug('Bootimg dir: %s' % bootimg_dir)
+ logger.debug('Kernel dir: %s', kernel_dir)
if 'file' not in source_params:
- msger.error("No file specified\n")
- return
+ raise WicError("No file specified")
- src = os.path.join(bootimg_dir, source_params['file'])
- dst = src
+ src = os.path.join(kernel_dir, source_params['file'])
+ dst = os.path.join(cr_workdir, "%s.%s" % (os.path.basename(source_params['file']), part.lineno))
+
+ if not os.path.exists(os.path.dirname(dst)):
+ os.makedirs(os.path.dirname(dst))
if 'skip' in source_params:
- dst = os.path.join(cr_workdir, source_params['file'])
- dd_cmd = "dd if=%s of=%s ibs=%s skip=1 conv=notrunc" % \
- (src, dst, source_params['skip'])
- exec_cmd(dd_cmd)
+ sparse_copy(src, dst, skip=int(source_params['skip']))
+ else:
+ sparse_copy(src, dst)
# get the size in the right units for kickstart (kB)
du_cmd = "du -Lbks %s" % dst
out = exec_cmd(du_cmd)
- filesize = out.split()[0]
+ filesize = int(out.split()[0])
- if int(filesize) > int(part.size):
+ if filesize > part.size:
part.size = filesize
- part.source_file = dst
+ if part.label:
+ RawCopyPlugin.do_image_label(part.fstype, dst, part.label)
+ part.source_file = dst
diff --git a/scripts/lib/wic/plugins/source/rootfs.py b/scripts/lib/wic/plugins/source/rootfs.py
index a90712b247..2e34e715ca 100644
--- a/scripts/lib/wic/plugins/source/rootfs.py
+++ b/scripts/lib/wic/plugins/source/rootfs.py
@@ -1,21 +1,7 @@
-# ex:ts=4:sw=4:sts=4:et
-# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
#
# Copyright (c) 2014, Intel Corporation.
-# All rights reserved.
#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 as
-# published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+# SPDX-License-Identifier: GPL-2.0-only
#
# DESCRIPTION
# This implements the 'rootfs' source plugin class for 'wic'
@@ -25,11 +11,19 @@
# Joao Henrique Ferreira de Freitas <joaohf (at] gmail.com>
#
+import logging
import os
+import shutil
+import sys
-from wic import msger
+from oe.path import copyhardlinktree
+from pathlib import Path
+
+from wic import WicError
from wic.pluginbase import SourcePlugin
-from wic.utils.oe.misc import get_bitbake_var
+from wic.misc import get_bitbake_var, exec_native_cmd
+
+logger = logging.getLogger('wic')
class RootfsPlugin(SourcePlugin):
"""
@@ -39,18 +33,42 @@ class RootfsPlugin(SourcePlugin):
name = 'rootfs'
@staticmethod
+ def __validate_path(cmd, rootfs_dir, path):
+ if os.path.isabs(path):
+ logger.error("%s: Must be relative: %s" % (cmd, orig_path))
+ sys.exit(1)
+
+ # Disallow climbing outside of parent directory using '..',
+ # because doing so could be quite disastrous (we will delete the
+ # directory, or modify a directory outside OpenEmbedded).
+ full_path = os.path.realpath(os.path.join(rootfs_dir, path))
+ if not full_path.startswith(os.path.realpath(rootfs_dir)):
+ logger.error("%s: Must point inside the rootfs:" % (cmd, path))
+ sys.exit(1)
+
+ return full_path
+
+ @staticmethod
def __get_rootfs_dir(rootfs_dir):
if os.path.isdir(rootfs_dir):
- return rootfs_dir
+ return os.path.realpath(rootfs_dir)
image_rootfs_dir = get_bitbake_var("IMAGE_ROOTFS", rootfs_dir)
if not os.path.isdir(image_rootfs_dir):
- msg = "No valid artifact IMAGE_ROOTFS from image named"
- msg += " %s has been found at %s, exiting.\n" % \
- (rootfs_dir, image_rootfs_dir)
- msger.error(msg)
+ raise WicError("No valid artifact IMAGE_ROOTFS from image "
+ "named %s has been found at %s, exiting." %
+ (rootfs_dir, image_rootfs_dir))
+
+ return os.path.realpath(image_rootfs_dir)
- return image_rootfs_dir
+ @staticmethod
+ def __get_pseudo(native_sysroot, rootfs, pseudo_dir):
+ pseudo = "export PSEUDO_PREFIX=%s/usr;" % native_sysroot
+ pseudo += "export PSEUDO_LOCALSTATEDIR=%s;" % pseudo_dir
+ pseudo += "export PSEUDO_PASSWD=%s;" % rootfs
+ pseudo += "export PSEUDO_NOSYMLINKEXP=1;"
+ pseudo += "%s " % get_bitbake_var("FAKEROOTCMD")
+ return pseudo
@classmethod
def do_prepare_partition(cls, part, source_params, cr, cr_workdir,
@@ -61,23 +79,155 @@ class RootfsPlugin(SourcePlugin):
'prepares' the partition to be incorporated into the image.
In this case, prepare content for legacy bios boot partition.
"""
- if part.rootfs is None:
+ if part.rootfs_dir is None:
if not 'ROOTFS_DIR' in krootfs_dir:
- msg = "Couldn't find --rootfs-dir, exiting"
- msger.error(msg)
+ raise WicError("Couldn't find --rootfs-dir, exiting")
+
rootfs_dir = krootfs_dir['ROOTFS_DIR']
else:
- if part.rootfs in krootfs_dir:
- rootfs_dir = krootfs_dir[part.rootfs]
- elif part.rootfs:
- rootfs_dir = part.rootfs
+ if part.rootfs_dir in krootfs_dir:
+ rootfs_dir = krootfs_dir[part.rootfs_dir]
+ elif part.rootfs_dir:
+ rootfs_dir = part.rootfs_dir
else:
- msg = "Couldn't find --rootfs-dir=%s connection"
- msg += " or it is not a valid path, exiting"
- msger.error(msg % part.rootfs)
+ raise WicError("Couldn't find --rootfs-dir=%s connection or "
+ "it is not a valid path, exiting" % part.rootfs_dir)
+
+ part.rootfs_dir = cls.__get_rootfs_dir(rootfs_dir)
+ part.has_fstab = os.path.exists(os.path.join(part.rootfs_dir, "etc/fstab"))
+ pseudo_dir = os.path.join(part.rootfs_dir, "../pseudo")
+ if not os.path.lexists(pseudo_dir):
+ logger.warn("%s folder does not exist. "
+ "Usernames and permissions will be invalid " % pseudo_dir)
+ pseudo_dir = None
+
+ new_rootfs = None
+ new_pseudo = None
+ # Handle excluded paths.
+ if part.exclude_path or part.include_path or part.change_directory or part.update_fstab_in_rootfs:
+ # We need a new rootfs directory we can safely modify without
+ # interfering with other tasks. Copy to workdir.
+ new_rootfs = os.path.realpath(os.path.join(cr_workdir, "rootfs%d" % part.lineno))
+
+ if os.path.lexists(new_rootfs):
+ shutil.rmtree(os.path.join(new_rootfs))
+
+ if part.change_directory:
+ cd = part.change_directory
+ if cd[-1] == '/':
+ cd = cd[:-1]
+ orig_dir = cls.__validate_path("--change-directory", part.rootfs_dir, cd)
+ else:
+ orig_dir = part.rootfs_dir
+ copyhardlinktree(orig_dir, new_rootfs)
+
+ # Convert the pseudo directory to its new location
+ if (pseudo_dir):
+ new_pseudo = os.path.realpath(
+ os.path.join(cr_workdir, "pseudo%d" % part.lineno))
+ if os.path.lexists(new_pseudo):
+ shutil.rmtree(new_pseudo)
+ os.mkdir(new_pseudo)
+ shutil.copy(os.path.join(pseudo_dir, "files.db"),
+ os.path.join(new_pseudo, "files.db"))
+
+ pseudo_cmd = "%s -B -m %s -M %s" % (cls.__get_pseudo(native_sysroot,
+ new_rootfs,
+ new_pseudo),
+ orig_dir, new_rootfs)
+ exec_native_cmd(pseudo_cmd, native_sysroot)
+
+ for in_path in part.include_path or []:
+ #parse arguments
+ include_path = in_path[0]
+ if len(in_path) > 2:
+ logger.error("'Invalid number of arguments for include-path")
+ sys.exit(1)
+ if len(in_path) == 2:
+ path = in_path[1]
+ else:
+ path = None
+
+ # Pack files to be included into a tar file.
+ # We need to create a tar file, because that way we can keep the
+ # permissions from the files even when they belong to different
+ # pseudo enviroments.
+ # If we simply copy files using copyhardlinktree/copytree... the
+ # copied files will belong to the user running wic.
+ tar_file = os.path.realpath(
+ os.path.join(cr_workdir, "include-path%d.tar" % part.lineno))
+ if os.path.isfile(include_path):
+ parent = os.path.dirname(os.path.realpath(include_path))
+ tar_cmd = "tar c --owner=root --group=root -f %s -C %s %s" % (
+ tar_file, parent, os.path.relpath(include_path, parent))
+ exec_native_cmd(tar_cmd, native_sysroot)
+ else:
+ if include_path in krootfs_dir:
+ include_path = krootfs_dir[include_path]
+ include_path = cls.__get_rootfs_dir(include_path)
+ include_pseudo = os.path.join(include_path, "../pseudo")
+ if os.path.lexists(include_pseudo):
+ pseudo = cls.__get_pseudo(native_sysroot, include_path,
+ include_pseudo)
+ tar_cmd = "tar cf %s -C %s ." % (tar_file, include_path)
+ else:
+ pseudo = None
+ tar_cmd = "tar c --owner=root --group=root -f %s -C %s ." % (
+ tar_file, include_path)
+ exec_native_cmd(tar_cmd, native_sysroot, pseudo)
+
+ #create destination
+ if path:
+ destination = cls.__validate_path("--include-path", new_rootfs, path)
+ Path(destination).mkdir(parents=True, exist_ok=True)
+ else:
+ destination = new_rootfs
+
+ #extract destination
+ untar_cmd = "tar xf %s -C %s" % (tar_file, destination)
+ if new_pseudo:
+ pseudo = cls.__get_pseudo(native_sysroot, new_rootfs, new_pseudo)
+ else:
+ pseudo = None
+ exec_native_cmd(untar_cmd, native_sysroot, pseudo)
+ os.remove(tar_file)
+
+ for orig_path in part.exclude_path or []:
+ path = orig_path
+
+ full_path = cls.__validate_path("--exclude-path", new_rootfs, path)
+
+ if not os.path.lexists(full_path):
+ continue
- real_rootfs_dir = cls.__get_rootfs_dir(rootfs_dir)
+ if new_pseudo:
+ pseudo = cls.__get_pseudo(native_sysroot, new_rootfs, new_pseudo)
+ else:
+ pseudo = None
+ if path.endswith(os.sep):
+ # Delete content only.
+ for entry in os.listdir(full_path):
+ full_entry = os.path.join(full_path, entry)
+ rm_cmd = "rm -rf %s" % (full_entry)
+ exec_native_cmd(rm_cmd, native_sysroot, pseudo)
+ else:
+ # Delete whole directory.
+ rm_cmd = "rm -rf %s" % (full_path)
+ exec_native_cmd(rm_cmd, native_sysroot, pseudo)
- part.set_rootfs(real_rootfs_dir)
- part.prepare_rootfs(cr_workdir, oe_builddir, real_rootfs_dir, native_sysroot)
+ # Update part.has_fstab here as fstab may have been added or
+ # removed by the above modifications.
+ part.has_fstab = os.path.exists(os.path.join(new_rootfs, "etc/fstab"))
+ if part.update_fstab_in_rootfs and part.has_fstab and not part.no_fstab_update:
+ fstab_path = os.path.join(new_rootfs, "etc/fstab")
+ # Assume that fstab should always be owned by root with fixed permissions
+ install_cmd = "install -m 0644 %s %s" % (part.updated_fstab_path, fstab_path)
+ if new_pseudo:
+ pseudo = cls.__get_pseudo(native_sysroot, new_rootfs, new_pseudo)
+ else:
+ pseudo = None
+ exec_native_cmd(install_cmd, native_sysroot, pseudo)
+ part.prepare_rootfs(cr_workdir, oe_builddir,
+ new_rootfs or part.rootfs_dir, native_sysroot,
+ pseudo_dir = new_pseudo or pseudo_dir)
diff --git a/scripts/lib/wic/plugins/source/rootfs_pcbios_ext.py b/scripts/lib/wic/plugins/source/rootfs_pcbios_ext.py
deleted file mode 100644
index 76e7b033fb..0000000000
--- a/scripts/lib/wic/plugins/source/rootfs_pcbios_ext.py
+++ /dev/null
@@ -1,181 +0,0 @@
-# ex:ts=4:sw=4:sts=4:et
-# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
-#
-# This program is free software; you can distribute it and/or modify
-# it under the terms of the GNU General Public License version 2 as
-# published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for mo details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-#
-# AUTHOR
-# Adrian Freihofer <adrian.freihofer (at] neratec.com>
-#
-
-import os
-from wic import kickstart
-from wic import msger
-from wic.utils import syslinux
-from wic.utils import runner
-from wic.utils.oe import misc
-from wic.utils.errors import ImageError
-from wic.pluginbase import SourcePlugin
-
-
-# pylint: disable=no-init
-class RootfsPlugin(SourcePlugin):
- """
- Create root partition and install syslinux bootloader
-
- This plugin creates a disk image containing a bootable root partition with
- syslinux installed. The filesystem is ext2/3/4, no extra boot partition is
- required.
-
- Example kickstart file:
- part / --source rootfs-pcbios-ext --ondisk sda --fstype=ext4 --label rootfs --align 1024
- bootloader --source rootfs-pcbios-ext --timeout=0 --append="rootwait rootfstype=ext4"
-
- The first line generates a root file system including a syslinux.cfg file
- The "--source rootfs-pcbios-ext" in the second line triggers the installation
- of ldlinux.sys into the image.
- """
-
- name = 'rootfs-pcbios-ext'
-
- @staticmethod
- def _get_rootfs_dir(rootfs_dir):
- """
- Find rootfs pseudo dir
-
- If rootfs_dir is a directory consider it as rootfs directory.
- Otherwise ask bitbake about the IMAGE_ROOTFS directory.
- """
- if os.path.isdir(rootfs_dir):
- return rootfs_dir
-
- image_rootfs_dir = misc.get_bitbake_var("IMAGE_ROOTFS", rootfs_dir)
- if not os.path.isdir(image_rootfs_dir):
- msg = "No valid artifact IMAGE_ROOTFS from image named"
- msg += " %s has been found at %s, exiting.\n" % \
- (rootfs_dir, image_rootfs_dir)
- msger.error(msg)
-
- return image_rootfs_dir
-
- # pylint: disable=unused-argument
- @classmethod
- def do_configure_partition(cls, part, source_params, image_creator,
- image_creator_workdir, oe_builddir, bootimg_dir,
- kernel_dir, native_sysroot):
- """
- Creates syslinux config in rootfs directory
-
- Called before do_prepare_partition()
- """
- options = image_creator.ks.handler.bootloader.appendLine
-
- syslinux_conf = ""
- syslinux_conf += "PROMPT 0\n"
-
- timeout = kickstart.get_timeout(image_creator.ks)
- if not timeout:
- timeout = 0
- syslinux_conf += "TIMEOUT " + str(timeout) + "\n"
- syslinux_conf += "ALLOWOPTIONS 1\n"
-
- # Derive SERIAL... line from from kernel boot parameters
- syslinux_conf += syslinux.serial_console_form_kargs(options) + "\n"
-
- syslinux_conf += "DEFAULT linux\n"
- syslinux_conf += "LABEL linux\n"
- syslinux_conf += " KERNEL /boot/bzImage\n"
-
- syslinux_conf += " APPEND label=boot root=%s %s\n" % \
- (image_creator.rootdev, options)
-
- syslinux_cfg = os.path.join(image_creator.rootfs_dir['ROOTFS_DIR'], "boot", "syslinux.cfg")
- msger.debug("Writing syslinux config %s" % syslinux_cfg)
- with open(syslinux_cfg, "w") as cfg:
- cfg.write(syslinux_conf)
-
- @classmethod
- def do_prepare_partition(cls, part, source_params, image_creator,
- image_creator_workdir, oe_builddir, bootimg_dir,
- kernel_dir, krootfs_dir, native_sysroot):
- """
- Creates partition out of rootfs directory
-
- Prepare content for a rootfs partition i.e. create a partition
- and fill it from a /rootfs dir.
- Install syslinux bootloader into root partition image file
- """
- def is_exe(exepath):
- """Verify exepath is an executable file"""
- return os.path.isfile(exepath) and os.access(exepath, os.X_OK)
-
- # Make sure syslinux-nomtools is available in native sysroot or fail
- native_syslinux_nomtools = os.path.join(native_sysroot, "usr/bin/syslinux-nomtools")
- if not is_exe(native_syslinux_nomtools):
- msger.info("building syslinux-native...")
- misc.exec_cmd("bitbake syslinux-native")
- if not is_exe(native_syslinux_nomtools):
- msger.error("Couldn't find syslinux-nomtools (%s), exiting\n" %
- native_syslinux_nomtools)
-
- if part.rootfs is None:
- if 'ROOTFS_DIR' not in krootfs_dir:
- msger.error("Couldn't find --rootfs-dir, exiting")
- rootfs_dir = krootfs_dir['ROOTFS_DIR']
- else:
- if part.rootfs in krootfs_dir:
- rootfs_dir = krootfs_dir[part.rootfs]
- elif part.rootfs:
- rootfs_dir = part.rootfs
- else:
- msg = "Couldn't find --rootfs-dir=%s connection"
- msg += " or it is not a valid path, exiting"
- msger.error(msg % part.rootfs)
-
- real_rootfs_dir = cls._get_rootfs_dir(rootfs_dir)
-
- part.set_rootfs(real_rootfs_dir)
- part.prepare_rootfs(image_creator_workdir, oe_builddir, real_rootfs_dir, native_sysroot)
-
- # install syslinux into rootfs partition
- syslinux_cmd = "syslinux-nomtools -d /boot -i %s" % part.source_file
- misc.exec_native_cmd(syslinux_cmd, native_sysroot)
-
- @classmethod
- def do_install_disk(cls, disk, disk_name, image_creator, workdir, oe_builddir,
- bootimg_dir, kernel_dir, native_sysroot):
- """
- Assemble partitions to disk image
-
- Called after all partitions have been prepared and assembled into a
- disk image. In this case, we install the MBR.
- """
- mbrfile = os.path.join(native_sysroot, "usr/share/syslinux/")
- if image_creator.ptable_format == 'msdos':
- mbrfile += "mbr.bin"
- elif image_creator.ptable_format == 'gpt':
- mbrfile += "gptmbr.bin"
- else:
- msger.error("Unsupported partition table: %s" % \
- image_creator.ptable_format)
-
- if not os.path.exists(mbrfile):
- msger.error("Couldn't find %s. Has syslinux-native been baked?" % mbrfile)
-
- full_path = disk['disk'].device
- msger.debug("Installing MBR on disk %s as %s with size %s bytes" \
- % (disk_name, full_path, disk['min_size']))
-
- ret_code = runner.show(['dd', 'if=%s' % mbrfile, 'of=%s' % full_path, 'conv=notrunc'])
- if ret_code != 0:
- raise ImageError("Unable to set MBR to %s" % full_path)
diff --git a/scripts/lib/wic/test b/scripts/lib/wic/test
deleted file mode 100644
index 9daeafb986..0000000000
--- a/scripts/lib/wic/test
+++ /dev/null
@@ -1 +0,0 @@
-test
diff --git a/scripts/lib/wic/utils/__init__.py b/scripts/lib/wic/utils/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
--- a/scripts/lib/wic/utils/__init__.py
+++ /dev/null
diff --git a/scripts/lib/wic/utils/errors.py b/scripts/lib/wic/utils/errors.py
deleted file mode 100644
index d1b514dd9d..0000000000
--- a/scripts/lib/wic/utils/errors.py
+++ /dev/null
@@ -1,29 +0,0 @@
-#!/usr/bin/env python -tt
-#
-# Copyright (c) 2007 Red Hat, Inc.
-# Copyright (c) 2011 Intel, Inc.
-#
-# This program is free software; you can redistribute it and/or modify it
-# under the terms of the GNU General Public License as published by the Free
-# Software Foundation; version 2 of the License
-#
-# This program is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
-# for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc., 59
-# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-class WicError(Exception):
- pass
-
-class CreatorError(WicError):
- pass
-
-class Usage(WicError):
- pass
-
-class ImageError(WicError):
- pass
diff --git a/scripts/lib/wic/utils/fs_related.py b/scripts/lib/wic/utils/fs_related.py
deleted file mode 100644
index 2e74461a40..0000000000
--- a/scripts/lib/wic/utils/fs_related.py
+++ /dev/null
@@ -1,84 +0,0 @@
-#!/usr/bin/env python -tt
-#
-# Copyright (c) 2007, Red Hat, Inc.
-# Copyright (c) 2009, 2010, 2011 Intel, Inc.
-#
-# This program is free software; you can redistribute it and/or modify it
-# under the terms of the GNU General Public License as published by the Free
-# Software Foundation; version 2 of the License
-#
-# This program is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
-# for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc., 59
-# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-from __future__ import with_statement
-import os
-import errno
-
-from wic.utils.oe.misc import exec_cmd
-
-def makedirs(dirname):
- """A version of os.makedirs() that doesn't throw an
- exception if the leaf directory already exists.
- """
- try:
- os.makedirs(dirname)
- except OSError, err:
- if err.errno != errno.EEXIST:
- raise
-
-class Disk:
- """
- Generic base object for a disk.
- """
- def __init__(self, size, device=None):
- self._device = device
- self._size = size
-
- def create(self):
- pass
-
- def cleanup(self):
- pass
-
- def get_device(self):
- return self._device
- def set_device(self, path):
- self._device = path
- device = property(get_device, set_device)
-
- def get_size(self):
- return self._size
- size = property(get_size)
-
-
-class DiskImage(Disk):
- """
- A Disk backed by a file.
- """
- def __init__(self, image_file, size):
- Disk.__init__(self, size)
- self.image_file = image_file
-
- def exists(self):
- return os.path.exists(self.image_file)
-
- def create(self):
- if self.device is not None:
- return
-
- blocks = self.size / 1024
- if self.size - blocks * 1024:
- blocks += 1
-
- # create disk image
- dd_cmd = "dd if=/dev/zero of=%s bs=1024 seek=%d count=1" % \
- (self.image_file, blocks)
- exec_cmd(dd_cmd)
-
- self.device = self.image_file
diff --git a/scripts/lib/wic/utils/misc.py b/scripts/lib/wic/utils/misc.py
deleted file mode 100644
index 9d750694df..0000000000
--- a/scripts/lib/wic/utils/misc.py
+++ /dev/null
@@ -1,58 +0,0 @@
-#!/usr/bin/env python -tt
-#
-# Copyright (c) 2010, 2011 Intel Inc.
-#
-# This program is free software; you can redistribute it and/or modify it
-# under the terms of the GNU General Public License as published by the Free
-# Software Foundation; version 2 of the License
-#
-# This program is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
-# for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc., 59
-# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-import os
-import time
-
-def build_name(kscfg, release=None, prefix=None, suffix=None):
- """Construct and return an image name string.
-
- This is a utility function to help create sensible name and fslabel
- strings. The name is constructed using the sans-prefix-and-extension
- kickstart filename and the supplied prefix and suffix.
-
- kscfg -- a path to a kickstart file
- release -- a replacement to suffix for image release
- prefix -- a prefix to prepend to the name; defaults to None, which causes
- no prefix to be used
- suffix -- a suffix to append to the name; defaults to None, which causes
- a YYYYMMDDHHMM suffix to be used
-
- Note, if maxlen is less then the len(suffix), you get to keep both pieces.
-
- """
- name = os.path.basename(kscfg)
- idx = name.rfind('.')
- if idx >= 0:
- name = name[:idx]
-
- if release is not None:
- suffix = ""
- if prefix is None:
- prefix = ""
- if suffix is None:
- suffix = time.strftime("%Y%m%d%H%M")
-
- if name.startswith(prefix):
- name = name[len(prefix):]
-
- prefix = "%s-" % prefix if prefix else ""
- suffix = "-%s" % suffix if suffix else ""
-
- ret = prefix + name + suffix
-
- return ret
diff --git a/scripts/lib/wic/utils/oe/__init__.py b/scripts/lib/wic/utils/oe/__init__.py
deleted file mode 100644
index 0a81575a74..0000000000
--- a/scripts/lib/wic/utils/oe/__init__.py
+++ /dev/null
@@ -1,22 +0,0 @@
-#
-# OpenEmbedded wic utils library
-#
-# Copyright (c) 2013, Intel Corporation.
-# All rights reserved.
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 as
-# published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-#
-# AUTHORS
-# Tom Zanussi <tom.zanussi (at] linux.intel.com>
-#
diff --git a/scripts/lib/wic/utils/oe/misc.py b/scripts/lib/wic/utils/oe/misc.py
deleted file mode 100644
index 7370d93136..0000000000
--- a/scripts/lib/wic/utils/oe/misc.py
+++ /dev/null
@@ -1,234 +0,0 @@
-# ex:ts=4:sw=4:sts=4:et
-# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
-#
-# Copyright (c) 2013, Intel Corporation.
-# All rights reserved.
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 as
-# published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-#
-# DESCRIPTION
-# This module provides a place to collect various wic-related utils
-# for the OpenEmbedded Image Tools.
-#
-# AUTHORS
-# Tom Zanussi <tom.zanussi (at] linux.intel.com>
-#
-"""Miscellaneous functions."""
-
-import os
-from collections import defaultdict
-
-from wic import msger
-from wic.utils import runner
-
-# executable -> recipe pairs for exec_native_cmd
-NATIVE_RECIPES = {"mcopy": "mtools",
- "mkdosfs": "dosfstools",
- "mkfs.btrfs": "btrfs-tools",
- "mkfs.ext2": "e2fsprogs",
- "mkfs.ext3": "e2fsprogs",
- "mkfs.ext4": "e2fsprogs",
- "mkfs.vfat": "dosfstools",
- "mksquashfs": "squashfs-tools",
- "mkswap": "util-linux",
- "parted": "parted",
- "sgdisk": "gptfdisk",
- "syslinux": "syslinux"
- }
-
-def _exec_cmd(cmd_and_args, as_shell=False, catch=3):
- """
- Execute command, catching stderr, stdout
-
- Need to execute as_shell if the command uses wildcards
- """
- msger.debug("_exec_cmd: %s" % cmd_and_args)
- args = cmd_and_args.split()
- msger.debug(args)
-
- if as_shell:
- ret, out = runner.runtool(cmd_and_args, catch)
- else:
- ret, out = runner.runtool(args, catch)
- out = out.strip()
- msger.debug("_exec_cmd: output for %s (rc = %d): %s" % \
- (cmd_and_args, ret, out))
-
- return (ret, out)
-
-
-def exec_cmd(cmd_and_args, as_shell=False, catch=3):
- """
- Execute command, catching stderr, stdout
-
- Exits if rc non-zero
- """
- ret, out = _exec_cmd(cmd_and_args, as_shell, catch)
-
- if ret != 0:
- msger.error("exec_cmd: %s returned '%s' instead of 0" % \
- (cmd_and_args, ret))
-
- return out
-
-
-def exec_native_cmd(cmd_and_args, native_sysroot, catch=3):
- """
- Execute native command, catching stderr, stdout
-
- Need to execute as_shell if the command uses wildcards
-
- Always need to execute native commands as_shell
- """
- native_paths = \
- "export PATH=%s/sbin:%s/usr/sbin:%s/usr/bin" % \
- (native_sysroot, native_sysroot, native_sysroot)
- native_cmd_and_args = "%s;%s" % (native_paths, cmd_and_args)
- msger.debug("exec_native_cmd: %s" % cmd_and_args)
-
- args = cmd_and_args.split()
- msger.debug(args)
-
- ret, out = _exec_cmd(native_cmd_and_args, True, catch)
-
- if ret == 127: # shell command-not-found
- prog = args[0]
- msg = "A native program %s required to build the image "\
- "was not found (see details above).\n\n" % prog
- recipe = NATIVE_RECIPES.get(prog)
- if recipe:
- msg += "Please bake it with 'bitbake %s-native' "\
- "and try again.\n" % recipe
- else:
- msg += "Wic failed to find a recipe to build native %s. Please "\
- "file a bug against wic.\n" % prog
- msger.error(msg)
- if out:
- msger.debug('"%s" output: %s' % (args[0], out))
-
- if ret != 0:
- msger.error("exec_cmd: '%s' returned '%s' instead of 0" % \
- (cmd_and_args, ret))
-
- return ret, out
-
-BOOTDD_EXTRA_SPACE = 16384
-
-class BitbakeVars(defaultdict):
- """
- Container for Bitbake variables.
- """
- def __init__(self):
- defaultdict.__init__(self, dict)
-
- # default_image and vars_dir attributes should be set from outside
- self.default_image = None
- self.vars_dir = None
-
- def _parse_line(self, line, image):
- """
- Parse one line from bitbake -e output or from .env file.
- Put result key-value pair into the storage.
- """
- if "=" not in line:
- return
- try:
- key, val = line.split("=")
- except ValueError:
- return
- key = key.strip()
- val = val.strip()
- if key.replace('_', '').isalnum():
- self[image][key] = val.strip('"')
-
- def get_var(self, var, image=None):
- """
- Get bitbake variable from 'bitbake -e' output or from .env file.
- This is a lazy method, i.e. it runs bitbake or parses file only when
- only when variable is requested. It also caches results.
- """
- if not image:
- image = self.default_image
-
- if image not in self:
- if image and self.vars_dir:
- fname = os.path.join(self.vars_dir, image + '.env')
- if os.path.isfile(fname):
- # parse .env file
- with open(fname) as varsfile:
- for line in varsfile:
- self._parse_line(line, image)
- else:
- print "Couldn't get bitbake variable from %s." % fname
- print "File %s doesn't exist." % fname
- return
- else:
- # Get bitbake -e output
- cmd = "bitbake -e"
- if image:
- cmd += " %s" % image
-
- log_level = msger.get_loglevel()
- msger.set_loglevel('normal')
- ret, lines = _exec_cmd(cmd)
- msger.set_loglevel(log_level)
-
- if ret:
- print "Couldn't get '%s' output." % cmd
- print "Bitbake failed with error:\n%s\n" % lines
- return
-
- # Parse bitbake -e output
- for line in lines.split('\n'):
- self._parse_line(line, image)
-
- # Make first image a default set of variables
- images = [key for key in self if key]
- if len(images) == 1:
- self[None] = self[image]
-
- return self[image].get(var)
-
-# Create BB_VARS singleton
-BB_VARS = BitbakeVars()
-
-def get_bitbake_var(var, image=None):
- """
- Provide old get_bitbake_var API by wrapping
- get_var method of BB_VARS singleton.
- """
- return BB_VARS.get_var(var, image)
-
-def parse_sourceparams(sourceparams):
- """
- Split sourceparams string of the form key1=val1[,key2=val2,...]
- into a dict. Also accepts valueless keys i.e. without =.
-
- Returns dict of param key/val pairs (note that val may be None).
- """
- params_dict = {}
-
- params = sourceparams.split(',')
- if params:
- for par in params:
- if not par:
- continue
- if not '=' in par:
- key = par
- val = None
- else:
- key, val = par.split('=')
- params_dict[key] = val
-
- return params_dict
diff --git a/scripts/lib/wic/utils/partitionedfs.py b/scripts/lib/wic/utils/partitionedfs.py
deleted file mode 100644
index 5a103bbc7e..0000000000
--- a/scripts/lib/wic/utils/partitionedfs.py
+++ /dev/null
@@ -1,362 +0,0 @@
-#!/usr/bin/env python -tt
-#
-# Copyright (c) 2009, 2010, 2011 Intel, Inc.
-# Copyright (c) 2007, 2008 Red Hat, Inc.
-# Copyright (c) 2008 Daniel P. Berrange
-# Copyright (c) 2008 David P. Huff
-#
-# This program is free software; you can redistribute it and/or modify it
-# under the terms of the GNU General Public License as published by the Free
-# Software Foundation; version 2 of the License
-#
-# This program is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
-# for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc., 59
-# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-import os
-from wic import msger
-from wic.utils.errors import ImageError
-from wic.utils.oe.misc import exec_cmd, exec_native_cmd
-
-# Overhead of the MBR partitioning scheme (just one sector)
-MBR_OVERHEAD = 1
-
-# Overhead of the GPT partitioning scheme
-GPT_OVERHEAD = 34
-
-# Size of a sector in bytes
-SECTOR_SIZE = 512
-
-class Image(object):
- """
- Generic base object for an image.
-
- An Image is a container for a set of DiskImages and associated
- partitions.
- """
- def __init__(self, native_sysroot=None):
- self.disks = {}
- self.partitions = []
- # Size of a sector used in calculations
- self.sector_size = SECTOR_SIZE
- self._partitions_layed_out = False
- self.native_sysroot = native_sysroot
-
- def __add_disk(self, disk_name):
- """ Add a disk 'disk_name' to the internal list of disks. Note,
- 'disk_name' is the name of the disk in the target system
- (e.g., sdb). """
-
- if disk_name in self.disks:
- # We already have this disk
- return
-
- assert not self._partitions_layed_out
-
- self.disks[disk_name] = \
- {'disk': None, # Disk object
- 'numpart': 0, # Number of allocate partitions
- 'realpart': 0, # Number of partitions in the partition table
- 'partitions': [], # Indexes to self.partitions
- 'offset': 0, # Offset of next partition (in sectors)
- # Minimum required disk size to fit all partitions (in bytes)
- 'min_size': 0,
- 'ptable_format': "msdos"} # Partition table format
-
- def add_disk(self, disk_name, disk_obj):
- """ Add a disk object which have to be partitioned. More than one disk
- can be added. In case of multiple disks, disk partitions have to be
- added for each disk separately with 'add_partition()". """
-
- self.__add_disk(disk_name)
- self.disks[disk_name]['disk'] = disk_obj
-
- def __add_partition(self, part):
- """ This is a helper function for 'add_partition()' which adds a
- partition to the internal list of partitions. """
-
- assert not self._partitions_layed_out
-
- self.partitions.append(part)
- self.__add_disk(part['disk_name'])
-
- def add_partition(self, size, disk_name, mountpoint, source_file=None, fstype=None,
- label=None, fsopts=None, boot=False, align=None, no_table=False,
- part_type=None, uuid=None):
- """ Add the next partition. Prtitions have to be added in the
- first-to-last order. """
-
- ks_pnum = len(self.partitions)
-
- # Converting kB to sectors for parted
- size = size * 1024 / self.sector_size
-
- # We still need partition for "/" or non-subvolume
- if mountpoint == "/" or not fsopts:
- part = {'ks_pnum': ks_pnum, # Partition number in the KS file
- 'size': size, # In sectors
- 'mountpoint': mountpoint, # Mount relative to chroot
- 'source_file': source_file, # partition contents
- 'fstype': fstype, # Filesystem type
- 'fsopts': fsopts, # Filesystem mount options
- 'label': label, # Partition label
- 'disk_name': disk_name, # physical disk name holding partition
- 'device': None, # kpartx device node for partition
- 'num': None, # Partition number
- 'boot': boot, # Bootable flag
- 'align': align, # Partition alignment
- 'no_table' : no_table, # Partition does not appear in partition table
- 'part_type' : part_type, # Partition type
- 'uuid': uuid} # Partition UUID
-
- self.__add_partition(part)
-
- def layout_partitions(self, ptable_format="msdos"):
- """ Layout the partitions, meaning calculate the position of every
- partition on the disk. The 'ptable_format' parameter defines the
- partition table format and may be "msdos". """
-
- msger.debug("Assigning %s partitions to disks" % ptable_format)
-
- if self._partitions_layed_out:
- return
-
- self._partitions_layed_out = True
-
- # Go through partitions in the order they are added in .ks file
- for num in range(len(self.partitions)):
- part = self.partitions[num]
-
- if not self.disks.has_key(part['disk_name']):
- raise ImageError("No disk %s for partition %s" \
- % (part['disk_name'], part['mountpoint']))
-
- if ptable_format == 'msdos' and part['part_type']:
- # The --part-type can also be implemented for MBR partitions,
- # in which case it would map to the 1-byte "partition type"
- # filed at offset 3 of the partition entry.
- raise ImageError("setting custom partition type is not " \
- "implemented for msdos partitions")
-
- # Get the disk where the partition is located
- disk = self.disks[part['disk_name']]
- disk['numpart'] += 1
- if not part['no_table']:
- disk['realpart'] += 1
- disk['ptable_format'] = ptable_format
-
- if disk['numpart'] == 1:
- if ptable_format == "msdos":
- overhead = MBR_OVERHEAD
- elif ptable_format == "gpt":
- overhead = GPT_OVERHEAD
-
- # Skip one sector required for the partitioning scheme overhead
- disk['offset'] += overhead
-
- if disk['realpart'] > 3:
- # Reserve a sector for EBR for every logical partition
- # before alignment is performed.
- if ptable_format == "msdos":
- disk['offset'] += 1
-
-
- if part['align']:
- # If not first partition and we do have alignment set we need
- # to align the partition.
- # FIXME: This leaves a empty spaces to the disk. To fill the
- # gaps we could enlargea the previous partition?
-
- # Calc how much the alignment is off.
- align_sectors = disk['offset'] % (part['align'] * 1024 / self.sector_size)
-
- if align_sectors:
- # If partition is not aligned as required, we need
- # to move forward to the next alignment point
- align_sectors = (part['align'] * 1024 / self.sector_size) - align_sectors
-
- msger.debug("Realignment for %s%s with %s sectors, original"
- " offset %s, target alignment is %sK." %
- (part['disk_name'], disk['numpart'], align_sectors,
- disk['offset'], part['align']))
-
- # increase the offset so we actually start the partition on right alignment
- disk['offset'] += align_sectors
-
- part['start'] = disk['offset']
- disk['offset'] += part['size']
-
- part['type'] = 'primary'
- if not part['no_table']:
- part['num'] = disk['realpart']
- else:
- part['num'] = 0
-
- if disk['ptable_format'] == "msdos":
- if disk['realpart'] > 3:
- part['type'] = 'logical'
- part['num'] = disk['realpart'] + 1
-
- disk['partitions'].append(num)
- msger.debug("Assigned %s to %s%d, sectors range %d-%d size %d "
- "sectors (%d bytes)." \
- % (part['mountpoint'], part['disk_name'], part['num'],
- part['start'], part['start'] + part['size'] - 1,
- part['size'], part['size'] * self.sector_size))
-
- # Once all the partitions have been layed out, we can calculate the
- # minumim disk sizes.
- for disk in self.disks.values():
- disk['min_size'] = disk['offset']
- if disk['ptable_format'] == "gpt":
- disk['min_size'] += GPT_OVERHEAD
-
- disk['min_size'] *= self.sector_size
-
- def __create_partition(self, device, parttype, fstype, start, size):
- """ Create a partition on an image described by the 'device' object. """
-
- # Start is included to the size so we need to substract one from the end.
- end = start + size - 1
- msger.debug("Added '%s' partition, sectors %d-%d, size %d sectors" %
- (parttype, start, end, size))
-
- cmd = "parted -s %s unit s mkpart %s" % (device, parttype)
- if fstype:
- cmd += " %s" % fstype
- cmd += " %d %d" % (start, end)
-
- return exec_native_cmd(cmd, self.native_sysroot)
-
- def __format_disks(self):
- self.layout_partitions()
-
- for dev in self.disks.keys():
- disk = self.disks[dev]
- msger.debug("Initializing partition table for %s" % \
- (disk['disk'].device))
- exec_native_cmd("parted -s %s mklabel %s" % \
- (disk['disk'].device, disk['ptable_format']),
- self.native_sysroot)
-
- msger.debug("Creating partitions")
-
- for part in self.partitions:
- if part['num'] == 0:
- continue
-
- disk = self.disks[part['disk_name']]
- if disk['ptable_format'] == "msdos" and part['num'] == 5:
- # Create an extended partition (note: extended
- # partition is described in MBR and contains all
- # logical partitions). The logical partitions save a
- # sector for an EBR just before the start of a
- # partition. The extended partition must start one
- # sector before the start of the first logical
- # partition. This way the first EBR is inside of the
- # extended partition. Since the extended partitions
- # starts a sector before the first logical partition,
- # add a sector at the back, so that there is enough
- # room for all logical partitions.
- self.__create_partition(disk['disk'].device, "extended",
- None, part['start'] - 1,
- disk['offset'] - part['start'] + 1)
-
- if part['fstype'] == "swap":
- parted_fs_type = "linux-swap"
- elif part['fstype'] == "vfat":
- parted_fs_type = "fat32"
- elif part['fstype'] == "msdos":
- parted_fs_type = "fat16"
- elif part['fstype'] == "ontrackdm6aux3":
- parted_fs_type = "ontrackdm6aux3"
- else:
- # Type for ext2/ext3/ext4/btrfs
- parted_fs_type = "ext2"
-
- # Boot ROM of OMAP boards require vfat boot partition to have an
- # even number of sectors.
- if part['mountpoint'] == "/boot" and part['fstype'] in ["vfat", "msdos"] \
- and part['size'] % 2:
- msger.debug("Substracting one sector from '%s' partition to " \
- "get even number of sectors for the partition" % \
- part['mountpoint'])
- part['size'] -= 1
-
- self.__create_partition(disk['disk'].device, part['type'],
- parted_fs_type, part['start'], part['size'])
-
- if part['part_type']:
- msger.debug("partition %d: set type UID to %s" % \
- (part['num'], part['part_type']))
- exec_native_cmd("sgdisk --typecode=%d:%s %s" % \
- (part['num'], part['part_type'],
- disk['disk'].device), self.native_sysroot)
-
- if part['uuid']:
- msger.debug("partition %d: set UUID to %s" % \
- (part['num'], part['uuid']))
- exec_native_cmd("sgdisk --partition-guid=%d:%s %s" % \
- (part['num'], part['uuid'], disk['disk'].device),
- self.native_sysroot)
-
- if part['boot']:
- flag_name = "legacy_boot" if disk['ptable_format'] == 'gpt' else "boot"
- msger.debug("Set '%s' flag for partition '%s' on disk '%s'" % \
- (flag_name, part['num'], disk['disk'].device))
- exec_native_cmd("parted -s %s set %d %s on" % \
- (disk['disk'].device, part['num'], flag_name),
- self.native_sysroot)
-
- # Parted defaults to enabling the lba flag for fat16 partitions,
- # which causes compatibility issues with some firmware (and really
- # isn't necessary).
- if parted_fs_type == "fat16":
- if disk['ptable_format'] == 'msdos':
- msger.debug("Disable 'lba' flag for partition '%s' on disk '%s'" % \
- (part['num'], disk['disk'].device))
- exec_native_cmd("parted -s %s set %d lba off" % \
- (disk['disk'].device, part['num']),
- self.native_sysroot)
-
- def cleanup(self):
- if self.disks:
- for dev in self.disks:
- disk = self.disks[dev]
- try:
- disk['disk'].cleanup()
- except:
- pass
-
- def assemble(self, image_file):
- msger.debug("Installing partitions")
-
- for part in self.partitions:
- source = part['source_file']
- if source:
- # install source_file contents into a partition
- cmd = "dd if=%s of=%s bs=%d seek=%d count=%d conv=notrunc" % \
- (source, image_file, self.sector_size,
- part['start'], part['size'])
- exec_cmd(cmd)
-
- msger.debug("Installed %s in partition %d, sectors %d-%d, "
- "size %d sectors" % \
- (source, part['num'], part['start'],
- part['start'] + part['size'] - 1, part['size']))
-
- os.rename(source, image_file + '.p%d' % part['num'])
-
- def create(self):
- for dev in self.disks.keys():
- disk = self.disks[dev]
- disk['disk'].create()
-
- self.__format_disks()
-
- return
diff --git a/scripts/lib/wic/utils/runner.py b/scripts/lib/wic/utils/runner.py
deleted file mode 100644
index 7431917ff0..0000000000
--- a/scripts/lib/wic/utils/runner.py
+++ /dev/null
@@ -1,110 +0,0 @@
-#!/usr/bin/env python -tt
-#
-# Copyright (c) 2011 Intel, Inc.
-#
-# This program is free software; you can redistribute it and/or modify it
-# under the terms of the GNU General Public License as published by the Free
-# Software Foundation; version 2 of the License
-#
-# This program is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
-# for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc., 59
-# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-import os
-import subprocess
-
-from wic import msger
-
-def runtool(cmdln_or_args, catch=1):
- """ wrapper for most of the subprocess calls
- input:
- cmdln_or_args: can be both args and cmdln str (shell=True)
- catch: 0, quitely run
- 1, only STDOUT
- 2, only STDERR
- 3, both STDOUT and STDERR
- return:
- (rc, output)
- if catch==0: the output will always None
- """
-
- if catch not in (0, 1, 2, 3):
- # invalid catch selection, will cause exception, that's good
- return None
-
- if isinstance(cmdln_or_args, list):
- cmd = cmdln_or_args[0]
- shell = False
- else:
- import shlex
- cmd = shlex.split(cmdln_or_args)[0]
- shell = True
-
- if catch != 3:
- dev_null = os.open("/dev/null", os.O_WRONLY)
-
- if catch == 0:
- sout = dev_null
- serr = dev_null
- elif catch == 1:
- sout = subprocess.PIPE
- serr = dev_null
- elif catch == 2:
- sout = dev_null
- serr = subprocess.PIPE
- elif catch == 3:
- sout = subprocess.PIPE
- serr = subprocess.STDOUT
-
- try:
- process = subprocess.Popen(cmdln_or_args, stdout=sout,
- stderr=serr, shell=shell)
- (sout, serr) = process.communicate()
- # combine stdout and stderr, filter None out
- out = ''.join(filter(None, [sout, serr]))
- except OSError, err:
- if err.errno == 2:
- # [Errno 2] No such file or directory
- msger.error('Cannot run command: %s, lost dependency?' % cmd)
- else:
- raise # relay
- finally:
- if catch != 3:
- os.close(dev_null)
-
- return (process.returncode, out)
-
-def show(cmdln_or_args):
- # show all the message using msger.verbose
-
- rcode, out = runtool(cmdln_or_args, catch=3)
-
- if isinstance(cmdln_or_args, list):
- cmd = ' '.join(cmdln_or_args)
- else:
- cmd = cmdln_or_args
-
- msg = 'running command: "%s"' % cmd
- if out:
- out = out.strip()
- if out:
- msg += ', with output::'
- msg += '\n +----------------'
- for line in out.splitlines():
- msg += '\n | %s' % line
- msg += '\n +----------------'
-
- msger.verbose(msg)
- return rcode
-
-def outs(cmdln_or_args, catch=1):
- # get the outputs of tools
- return runtool(cmdln_or_args, catch)[1].strip()
-
-def quiet(cmdln_or_args):
- return runtool(cmdln_or_args, catch=0)[0]
diff --git a/scripts/lib/wic/utils/syslinux.py b/scripts/lib/wic/utils/syslinux.py
deleted file mode 100644
index aace2863c1..0000000000
--- a/scripts/lib/wic/utils/syslinux.py
+++ /dev/null
@@ -1,58 +0,0 @@
-# ex:ts=4:sw=4:sts=4:et
-# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
-#
-# This program is free software; you can redistribute it and/or modify it
-# under the terms of the GNU General Public License as published by the Free
-# Software Foundation; version 2 of the License
-#
-# This program is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
-# for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc., 59
-# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-#
-# AUTHOR
-# Adrian Freihofer <adrian.freihofer (at] neratec.com>
-
-
-import re
-from wic import msger
-
-
-def serial_console_form_kargs(kernel_args):
- """
- Create SERIAL... line from kernel parameters
-
- syslinux needs a line SERIAL port [baudrate [flowcontrol]]
- in the syslinux.cfg file. The config line is generated based
- on kernel boot parameters. The the parameters of the first
- ttyS console are considered for syslinux config.
- @param kernel_args kernel command line
- @return line for syslinux config file e.g. "SERIAL 0 115200"
- """
- syslinux_conf = ""
- for param in kernel_args.split():
- param_match = re.match("console=ttyS([0-9]+),?([0-9]*)([noe]?)([0-9]?)(r?)", param)
- if param_match:
- syslinux_conf += "SERIAL " + param_match.group(1)
- # baudrate
- if param_match.group(2):
- syslinux_conf += " " + param_match.group(2)
- # parity
- if param_match.group(3) and param_match.group(3) != 'n':
- msger.warning("syslinux does not support parity for console. {} is ignored."
- .format(param_match.group(3)))
- # number of bits
- if param_match.group(4) and param_match.group(4) != '8':
- msger.warning("syslinux supports 8 bit console configuration only. {} is ignored."
- .format(param_match.group(4)))
- # flow control
- if param_match.group(5) and param_match.group(5) != '':
- msger.warning("syslinux console flowcontrol configuration. {} is ignored."
- .format(param_match.group(5)))
- break
-
- return syslinux_conf