CedarBackup2.writers.util

Package CedarBackup2 :: Package writers :: Module util |
[hide private]
[frames] | no frames]

# Source Code for Module CedarBackup2.writers.util

      1  # -*- coding: iso-8859-1 -*-
      2  # vim: set ft=python ts=3 sw=3 expandtab:
      3  # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
      4  #
      5  #              C E D A R
      6  #          S O L U T I O N S       "Software done right."
      7  #           S O F T W A R E
      8  #
      9  # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
     10  #
     11  # Copyright (c) 2004-2007,2010 Kenneth J. Pronovici.
     12  # All rights reserved.
     13  #
     14  # This program is free software; you can redistribute it and/or
     15  # modify it under the terms of the GNU General Public License,
     16  # Version 2, as published by the Free Software Foundation.
     17  #
     18  # This program is distributed in the hope that it will be useful,
     19  # but WITHOUT ANY WARRANTY; without even the implied warranty of
     20  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
     21  #
     22  # Copies of the GNU General Public License are available from
     23  # the Free Software Foundation website, http://www.gnu.org/.
     24  #
     25  # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
     26  #
     27  # Author   : Kenneth J. Pronovici <pronovic@ieee.org>
     28  # Language : Python 2 (>= 2.7)
     29  # Project  : Cedar Backup, release 2
     30  # Purpose  : Provides utilities related to image writers.
     31  #
     32  # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
     33
     34  ########################################################################
     35  # Module documentation
     36  ########################################################################
     37
     38  """
     39  Provides utilities related to image writers.
     40  @author: Kenneth J. Pronovici <pronovic@ieee.org>
     41  """
     42
     43
     44  ########################################################################
     45  # Imported modules
     46  ########################################################################
     47
     48  # System modules
     49  import os
     50  import re
     51  import logging
     52
     53  # Cedar Backup modules
     54  from CedarBackup2.util import resolveCommand, executeCommand
     55  from CedarBackup2.util import convertSize, UNIT_BYTES, UNIT_SECTORS, encodePath
     56
     57
     58  ########################################################################
     59  # Module-wide constants and variables
     60  ########################################################################
     61
     62  logger = logging.getLogger("CedarBackup2.log.writers.util")
     63
     64  MKISOFS_COMMAND      = [ "mkisofs", ]
     65  VOLNAME_COMMAND      = [ "volname", ]
     66
     67
     68  ########################################################################
     69  # Functions used to portably validate certain kinds of values
     70  ########################################################################
     71
     72  ############################
     73  # validateDevice() function
     74  ############################
     75
     76 -def validateDevice(device, unittest=False):
     77     """
     78     Validates a configured device.
     79     The device must be an absolute path, must exist, and must be writable.
     80     The unittest flag turns off validation of the device on disk.
     81     @param device: Filesystem device path.
     82     @param unittest: Indicates whether we're unit testing.
     83     @return: Device as a string, for instance C{"/dev/cdrw"}
     84     @raise ValueError: If the device value is invalid.
     85     @raise ValueError: If some path cannot be encoded properly.
     86     """
     87     if device is None:
     88        raise ValueError("Device must be filled in.")
     89     device = encodePath(device)
     90     if not os.path.isabs(device):
     91        raise ValueError("Backup device must be an absolute path.")
     92     if not unittest and not os.path.exists(device):
     93        raise ValueError("Backup device must exist on disk.")
     94     if not unittest and not os.access(device, os.W_OK):
     95        raise ValueError("Backup device is not writable by the current user.")
     96     return device
     97
     98
     99  ############################
    100  # validateScsiId() function
    101  ############################
    102
    103 -def validateScsiId(scsiId):
    104     """
    105     Validates a SCSI id string.
    106     SCSI id must be a string in the form C{[<method>:]scsibus,target,lun}.
    107     For Mac OS X (Darwin), we also accept the form C{IO.*Services[/N]}.
    108     @note: For consistency, if C{None} is passed in, C{None} will be returned.
    109     @param scsiId: SCSI id for the device.
    110     @return: SCSI id as a string, for instance C{"ATA:1,0,0"}
    111     @raise ValueError: If the SCSI id string is invalid.
    112     """
    113     if scsiId is not None:
    114        pattern = re.compile(r"^\s*(.*:)?\s*[0-9][0-9]*\s*,\s*[0-9][0-9]*\s*,\s*[0-9][0-9]*\s*$")
    115        if not pattern.search(scsiId):
    116           pattern = re.compile(r"^\s*IO.*Services(\/[0-9][0-9]*)?\s*$")
    117           if not pattern.search(scsiId):
    118              raise ValueError("SCSI id is not in a valid form.")
    119     return scsiId
    120
    121
    122  ################################
    123  # validateDriveSpeed() function
    124  ################################
    125
    126 -def validateDriveSpeed(driveSpeed):
    127     """
    128     Validates a drive speed value.
    129     Drive speed must be an integer which is >= 1.
    130     @note: For consistency, if C{None} is passed in, C{None} will be returned.
    131     @param driveSpeed: Speed at which the drive writes.
    132     @return: Drive speed as an integer
    133     @raise ValueError: If the drive speed value is invalid.
    134     """
    135     if driveSpeed is None:
    136        return None
    137     try:
    138        intSpeed = int(driveSpeed)
    139     except TypeError:
    140        raise ValueError("Drive speed must be an integer >= 1.")
    141     if intSpeed < 1:
    142        raise ValueError("Drive speed must an integer >= 1.")
    143     return intSpeed
    144
    145
    146  ########################################################################
    147  # General writer-related utility functions
    148  ########################################################################
    149
    150  ############################
    151  # readMediaLabel() function
    152  ############################
    153
    154 -def readMediaLabel(devicePath):
    155     """
    156     Reads the media label (volume name) from the indicated device.
    157     The volume name is read using the C{volname} command.
    158     @param devicePath: Device path to read from
    159     @return: Media label as a string, or None if there is no name or it could not be read.
    160     """
    161     args = [ devicePath, ]
    162     command = resolveCommand(VOLNAME_COMMAND)
    163     (result, output) = executeCommand(command, args, returnOutput=True, ignoreStderr=True)
    164     if result != 0:
    165        return None
    166     if output is None or len(output) < 1:
    167        return None
    168     return output[0].rstrip()
    169
    170
    171  ########################################################################
    172  # IsoImage class definition
    173  ########################################################################
    174
    175 -class IsoImage(object):
    176
    177     ######################
    178     # Class documentation
    179     ######################
    180
    181     """
    182     Represents an ISO filesystem image.
    183
    184     Summary
    185     =======
    186
    187        This object represents an ISO 9660 filesystem image.  It is implemented
    188        in terms of the C{mkisofs} program, which has been ported to many
    189        operating systems and platforms.  A "sensible subset" of the C{mkisofs}
    190        functionality is made available through the public interface, allowing
    191        callers to set a variety of basic options such as publisher id,
    192        application id, etc. as well as specify exactly which files and
    193        directories they want included in their image.
    194
    195        By default, the image is created using the Rock Ridge protocol (using the
    196        C{-r} option to C{mkisofs}) because Rock Ridge discs are generally more
    197        useful on UN*X filesystems than standard ISO 9660 images.  However,
    198        callers can fall back to the default C{mkisofs} functionality by setting
    199        the C{useRockRidge} instance variable to C{False}.  Note, however, that
    200        this option is not well-tested.
    201
    202     Where Files and Directories are Placed in the Image
    203     ===================================================
    204
    205        Although this class is implemented in terms of the C{mkisofs} program,
    206        its standard "image contents" semantics are slightly different than the original
    207        C{mkisofs} semantics.  The difference is that files and directories are
    208        added to the image with some additional information about their source
    209        directory kept intact.
    210
    211        As an example, suppose you add the file C{/etc/profile} to your image and
    212        you do not configure a graft point.  The file C{/profile} will be created
    213        in the image.  The behavior for directories is similar.  For instance,
    214        suppose that you add C{/etc/X11} to the image and do not configure a
    215        graft point.  In this case, the directory C{/X11} will be created in the
    216        image, even if the original C{/etc/X11} directory is empty.  I{This
    217        behavior differs from the standard C{mkisofs} behavior!}
    218
    219        If a graft point is configured, it will be used to modify the point at
    220        which a file or directory is added into an image.  Using the examples
    221        from above, let's assume you set a graft point of C{base} when adding
    222        C{/etc/profile} and C{/etc/X11} to your image.  In this case, the file
    223        C{/base/profile} and the directory C{/base/X11} would be added to the
    224        image.
    225
    226        I feel that this behavior is more consistent than the original C{mkisofs}
    227        behavior.  However, to be fair, it is not quite as flexible, and some
    228        users might not like it.  For this reason, the C{contentsOnly} parameter
    229        to the L{addEntry} method can be used to revert to the original behavior
    230        if desired.
    231
    232     @sort: __init__, addEntry, getEstimatedSize, _getEstimatedSize, writeImage,
    233            _buildDirEntries _buildGeneralArgs, _buildSizeArgs, _buildWriteArgs,
    234            device, boundaries, graftPoint, useRockRidge, applicationId,
    235            biblioFile, publisherId, preparerId, volumeId
    236     """
    237
    238     ##############
    239     # Constructor
    240     ##############
    241
    242 -   def __init__(self, device=None, boundaries=None, graftPoint=None):
    243        """
    244        Initializes an empty ISO image object.
    245
    246        Only the most commonly-used configuration items can be set using this
    247        constructor.  If you have a need to change the others, do so immediately
    248        after creating your object.
    249
    250        The device and boundaries values are both required in order to write
    251        multisession discs.  If either is missing or C{None}, a multisession disc
    252        will not be written.  The boundaries tuple is in terms of ISO sectors, as
    253        built by an image writer class and returned in a L{writer.MediaCapacity}
    254        object.
    255
    256        @param device: Name of the device that the image will be written to
    257        @type device: Either be a filesystem path or a SCSI address
    258
    259        @param boundaries: Session boundaries as required by C{mkisofs}
    260        @type boundaries: Tuple C{(last_sess_start,next_sess_start)} as returned from C{cdrecord -msinfo}, or C{None}
    261
    262        @param graftPoint: Default graft point for this page.
    263        @type graftPoint: String representing a graft point path (see L{addEntry}).
    264        """
    265        self._device = None
    266        self._boundaries = None
    267        self._graftPoint = None
    268        self._useRockRidge = True
    269        self._applicationId = None
    270        self._biblioFile = None
    271        self._publisherId = None
    272        self._preparerId = None
    273        self._volumeId = None
    274        self.entries = { }
    275        self.device = device
    276        self.boundaries = boundaries
    277        self.graftPoint = graftPoint
    278        self.useRockRidge = True
    279        self.applicationId = None
    280        self.biblioFile = None
    281        self.publisherId = None
    282        self.preparerId = None
    283        self.volumeId = None
    284        logger.debug("Created new ISO image object.")
    285
    286
    287     #############
    288     # Properties
    289     #############
    290
    291 -   def _setDevice(self, value):
    292        """
    293        Property target used to set the device value.
    294        If not C{None}, the value can be either an absolute path or a SCSI id.
    295        @raise ValueError: If the value is not valid
    296        """
    297        try:
    298           if value is None:
    299              self._device = None
    300           else:
    301              if os.path.isabs(value):
    302                 self._device = value
    303              else:
    304                 self._device = validateScsiId(value)
    305        except ValueError:
    306           raise ValueError("Device must either be an absolute path or a valid SCSI id.")
    307
    308 -   def _getDevice(self):
    309        """
    310        Property target used to get the device value.
    311        """
    312        return self._device
    313
    314 -   def _setBoundaries(self, value):
    315        """
    316        Property target used to set the boundaries tuple.
    317        If not C{None}, the value must be a tuple of two integers.
    318        @raise ValueError: If the tuple values are not integers.
    319        @raise IndexError: If the tuple does not contain enough elements.
    320        """
    321        if value is None:
    322           self._boundaries = None
    323        else:
    324           self._boundaries = (int(value[0]), int(value[1]))
    325
    326 -   def _getBoundaries(self):
    327        """
    328        Property target used to get the boundaries value.
    329        """
    330        return self._boundaries
    331
    332 -   def _setGraftPoint(self, value):
    333        """
    334        Property target used to set the graft point.
    335        The value must be a non-empty string if it is not C{None}.
    336        @raise ValueError: If the value is an empty string.
    337        """
    338        if value is not None:
    339           if len(value) < 1:
    340              raise ValueError("The graft point must be a non-empty string.")
    341        self._graftPoint = value
    342
    343 -   def _getGraftPoint(self):
    344        """
    345        Property target used to get the graft point.
    346        """
    347        return self._graftPoint
    348
    349 -   def _setUseRockRidge(self, value):
    350        """
    351        Property target used to set the use RockRidge flag.
    352        No validations, but we normalize the value to C{True} or C{False}.
    353        """
    354        if value:
    355           self._useRockRidge = True
    356        else:
    357           self._useRockRidge = False
    358
    359 -   def _getUseRockRidge(self):
    360        """
    361        Property target used to get the use RockRidge flag.
    362        """
    363        return self._useRockRidge
    364
    365 -   def _setApplicationId(self, value):
    366        """
    367        Property target used to set the application id.
    368        The value must be a non-empty string if it is not C{None}.
    369        @raise ValueError: If the value is an empty string.
    370        """
    371        if value is not None:
    372           if len(value) < 1:
    373              raise ValueError("The application id must be a non-empty string.")
    374        self._applicationId = value
    375
    376 -   def _getApplicationId(self):
    377        """
    378        Property target used to get the application id.
    379        """
    380        return self._applicationId
    381
    382 -   def _setBiblioFile(self, value):
    383        """
    384        Property target used to set the biblio file.
    385        The value must be a non-empty string if it is not C{None}.
    386        @raise ValueError: If the value is an empty string.
    387        """
    388        if value is not None:
    389           if len(value) < 1:
    390              raise ValueError("The biblio file must be a non-empty string.")
    391        self._biblioFile = value
    392
    393 -   def _getBiblioFile(self):
    394        """
    395        Property target used to get the biblio file.
    396        """
    397        return self._biblioFile
    398
    399 -   def _setPublisherId(self, value):
    400        """
    401        Property target used to set the publisher id.
    402        The value must be a non-empty string if it is not C{None}.
    403        @raise ValueError: If the value is an empty string.
    404        """
    405        if value is not None:
    406           if len(value) < 1:
    407              raise ValueError("The publisher id must be a non-empty string.")
    408        self._publisherId = value
    409
    410 -   def _getPublisherId(self):
    411        """
    412        Property target used to get the publisher id.
    413        """
    414        return self._publisherId
    415
    416 -   def _setPreparerId(self, value):
    417        """
    418        Property target used to set the preparer id.
    419        The value must be a non-empty string if it is not C{None}.
    420        @raise ValueError: If the value is an empty string.
    421        """
    422        if value is not None:
    423           if len(value) < 1:
    424              raise ValueError("The preparer id must be a non-empty string.")
    425        self._preparerId = value
    426
    427 -   def _getPreparerId(self):
    428        """
    429        Property target used to get the preparer id.
    430        """
    431        return self._preparerId
    432
    433 -   def _setVolumeId(self, value):
    434        """
    435        Property target used to set the volume id.
    436        The value must be a non-empty string if it is not C{None}.
    437        @raise ValueError: If the value is an empty string.
    438        """
    439        if value is not None:
    440           if len(value) < 1:
    441              raise ValueError("The volume id must be a non-empty string.")
    442        self._volumeId = value
    443
    444 -   def _getVolumeId(self):
    445        """
    446        Property target used to get the volume id.
    447        """
    448        return self._volumeId
    449
    450     device = property(_getDevice, _setDevice, None, "Device that image will be written to (device path or SCSI id).")
    451     boundaries = property(_getBoundaries, _setBoundaries, None, "Session boundaries as required by C{mkisofs}.")
    452     graftPoint = property(_getGraftPoint, _setGraftPoint, None, "Default image-wide graft point (see L{addEntry} for details).")
    453     useRockRidge = property(_getUseRockRidge, _setUseRockRidge, None, "Indicates whether to use RockRidge (default is C{True}).")
    454     applicationId = property(_getApplicationId, _setApplicationId, None, "Optionally specifies the ISO header application id value.")
    455     biblioFile = property(_getBiblioFile, _setBiblioFile, None, "Optionally specifies the ISO bibliographic file name.")
    456     publisherId = property(_getPublisherId, _setPublisherId, None, "Optionally specifies the ISO header publisher id value.")
    457     preparerId = property(_getPreparerId, _setPreparerId, None, "Optionally specifies the ISO header preparer id value.")
    458     volumeId = property(_getVolumeId, _setVolumeId, None, "Optionally specifies the ISO header volume id value.")
    459
    460
    461     #########################
    462     # General public methods
    463     #########################
    464
    465 -   def addEntry(self, path, graftPoint=None, override=False, contentsOnly=False):
    466        """
    467        Adds an individual file or directory into the ISO image.
    468
    469        The path must exist and must be a file or a directory.  By default, the
    470        entry will be placed into the image at the root directory, but this
    471        behavior can be overridden using the C{graftPoint} parameter or instance
    472        variable.
    473
    474        You can use the C{contentsOnly} behavior to revert to the "original"
    475        C{mkisofs} behavior for adding directories, which is to add only the
    476        items within the directory, and not the directory itself.
    477
    478        @note: Things get I{odd} if you try to add a directory to an image that
    479        will be written to a multisession disc, and the same directory already
    480        exists in an earlier session on that disc.  Not all of the data gets
    481        written.  You really wouldn't want to do this anyway, I guess.
    482
    483        @note: An exception will be thrown if the path has already been added to
    484        the image, unless the C{override} parameter is set to C{True}.
    485
    486        @note: The method C{graftPoints} parameter overrides the object-wide
    487        instance variable.  If neither the method parameter or object-wide value
    488        is set, the path will be written at the image root.  The graft point
    489        behavior is determined by the value which is in effect I{at the time this
    490        method is called}, so you I{must} set the object-wide value before
    491        calling this method for the first time, or your image may not be
    492        consistent.
    493
    494        @note: You I{cannot} use the local C{graftPoint} parameter to "turn off"
    495        an object-wide instance variable by setting it to C{None}.  Python's
    496        default argument functionality buys us a lot, but it can't make this
    497        method psychic. :)
    498
    499        @param path: File or directory to be added to the image
    500        @type path: String representing a path on disk
    501
    502        @param graftPoint: Graft point to be used when adding this entry
    503        @type graftPoint: String representing a graft point path, as described above
    504
    505        @param override: Override an existing entry with the same path.
    506        @type override: Boolean true/false
    507
    508        @param contentsOnly: Add directory contents only (standard C{mkisofs} behavior).
    509        @type contentsOnly: Boolean true/false
    510
    511        @raise ValueError: If path is not a file or directory, or does not exist.
    512        @raise ValueError: If the path has already been added, and override is not set.
    513        @raise ValueError: If a path cannot be encoded properly.
    514        """
    515        path = encodePath(path)
    516        if not override:
    517           if path in self.entries.keys():
    518              raise ValueError("Path has already been added to the image.")
    519        if os.path.islink(path):
    520           raise ValueError("Path must not be a link.")
    521        if os.path.isdir(path):
    522           if graftPoint is not None:
    523              if contentsOnly:
    524                 self.entries[path] = graftPoint
    525              else:
    526                 self.entries[path] = os.path.join(graftPoint, os.path.basename(path))
    527           elif self.graftPoint is not None:
    528              if contentsOnly:
    529                 self.entries[path] = self.graftPoint
    530              else:
    531                 self.entries[path] = os.path.join(self.graftPoint, os.path.basename(path))
    532           else:
    533              if contentsOnly:
    534                 self.entries[path] = None
    535              else:
    536                 self.entries[path] = os.path.basename(path)
    537        elif os.path.isfile(path):
    538           if graftPoint is not None:
    539              self.entries[path] = graftPoint
    540           elif self.graftPoint is not None:
    541              self.entries[path] = self.graftPoint
    542           else:
    543              self.entries[path] = None
    544        else:
    545           raise ValueError("Path must be a file or a directory.")
    546
    547 -   def getEstimatedSize(self):
    548        """
    549        Returns the estimated size (in bytes) of the ISO image.
    550
    551        This is implemented via the C{-print-size} option to C{mkisofs}, so it
    552        might take a bit of time to execute.  However, the result is as accurate
    553        as we can get, since it takes into account all of the ISO overhead, the
    554        true cost of directories in the structure, etc, etc.
    555
    556        @return: Estimated size of the image, in bytes.
    557
    558        @raise IOError: If there is a problem calling C{mkisofs}.
    559        @raise ValueError: If there are no filesystem entries in the image
    560        """
    561        if len(self.entries.keys()) == 0:
    562           raise ValueError("Image does not contain any entries.")
    563        return self._getEstimatedSize(self.entries)
    564
    565 -   def _getEstimatedSize(self, entries):
    566        """
    567        Returns the estimated size (in bytes) for the passed-in entries dictionary.
    568        @return: Estimated size of the image, in bytes.
    569        @raise IOError: If there is a problem calling C{mkisofs}.
    570        """
    571        args = self._buildSizeArgs(entries)
    572        command = resolveCommand(MKISOFS_COMMAND)
    573        (result, output) = executeCommand(command, args, returnOutput=True, ignoreStderr=True)
    574        if result != 0:
    575           raise IOError("Error (%d) executing mkisofs command to estimate size." % result)
    576        if len(output) != 1:
    577           raise IOError("Unable to parse mkisofs output.")
    578        try:
    579           sectors = float(output[0])
    580           size = convertSize(sectors, UNIT_SECTORS, UNIT_BYTES)
    581           return size
    582        except:
    583           raise IOError("Unable to parse mkisofs output.")
    584
    585 -   def writeImage(self, imagePath):
    586        """
    587        Writes this image to disk using the image path.
    588
    589        @param imagePath: Path to write image out as
    590        @type imagePath: String representing a path on disk
    591
    592        @raise IOError: If there is an error writing the image to disk.
    593        @raise ValueError: If there are no filesystem entries in the image
    594        @raise ValueError: If a path cannot be encoded properly.
    595        """
    596        imagePath = encodePath(imagePath)
    597        if len(self.entries.keys()) == 0:
    598           raise ValueError("Image does not contain any entries.")
    599        args = self._buildWriteArgs(self.entries, imagePath)
    600        command = resolveCommand(MKISOFS_COMMAND)
    601        (result, output) = executeCommand(command, args, returnOutput=False)
    602        if result != 0:
    603           raise IOError("Error (%d) executing mkisofs command to build image." % result)
    604
    605
    606     #########################################
    607     # Methods used to build mkisofs commands
    608     #########################################
    609
    610     @staticmethod
    611 -   def _buildDirEntries(entries):
    612        """
    613        Uses an entries dictionary to build a list of directory locations for use
    614        by C{mkisofs}.
    615
    616        We build a list of entries that can be passed to C{mkisofs}.  Each entry is
    617        either raw (if no graft point was configured) or in graft-point form as
    618        described above (if a graft point was configured).  The dictionary keys
    619        are the path names, and the values are the graft points, if any.
    620
    621        @param entries: Dictionary of image entries (i.e. self.entries)
    622
    623        @return: List of directory locations for use by C{mkisofs}
    624        """
    625        dirEntries = []
    626        for key in entries.keys():
    627           if entries[key] is None:
    628              dirEntries.append(key)
    629           else:
    630              dirEntries.append("%s/=%s" % (entries[key].strip("/"), key))
    631        return dirEntries
    632
    633 -   def _buildGeneralArgs(self):
    634        """
    635        Builds a list of general arguments to be passed to a C{mkisofs} command.
    636
    637        The various instance variables (C{applicationId}, etc.) are filled into
    638        the list of arguments if they are set.
    639        By default, we will build a RockRidge disc.  If you decide to change
    640        this, think hard about whether you know what you're doing.  This option
    641        is not well-tested.
    642
    643        @return: List suitable for passing to L{util.executeCommand} as C{args}.
    644        """
    645        args = []
    646        if self.applicationId is not None:
    647           args.append("-A")
    648           args.append(self.applicationId)
    649        if self.biblioFile is not None:
    650           args.append("-biblio")
    651           args.append(self.biblioFile)
    652        if self.publisherId is not None:
    653           args.append("-publisher")
    654           args.append(self.publisherId)
    655        if self.preparerId is not None:
    656           args.append("-p")
    657           args.append(self.preparerId)
    658        if self.volumeId is not None:
    659           args.append("-V")
    660           args.append(self.volumeId)
    661        return args
    662
    663 -   def _buildSizeArgs(self, entries):
    664        """
    665        Builds a list of arguments to be passed to a C{mkisofs} command.
    666
    667        The various instance variables (C{applicationId}, etc.) are filled into
    668        the list of arguments if they are set.  The command will be built to just
    669        return size output (a simple count of sectors via the C{-print-size} option),
    670        rather than an image file on disk.
    671
    672        By default, we will build a RockRidge disc.  If you decide to change
    673        this, think hard about whether you know what you're doing.  This option
    674        is not well-tested.
    675
    676        @param entries: Dictionary of image entries (i.e. self.entries)
    677
    678        @return: List suitable for passing to L{util.executeCommand} as C{args}.
    679        """
    680        args = self._buildGeneralArgs()
    681        args.append("-print-size")
    682        args.append("-graft-points")
    683        if self.useRockRidge:
    684           args.append("-r")
    685        if self.device is not None and self.boundaries is not None:
    686           args.append("-C")
    687           args.append("%d,%d" % (self.boundaries[0], self.boundaries[1]))
    688           args.append("-M")
    689           args.append(self.device)
    690        args.extend(self._buildDirEntries(entries))
    691        return args
    692
    693 -   def _buildWriteArgs(self, entries, imagePath):
    694        """
    695        Builds a list of arguments to be passed to a C{mkisofs} command.
    696
    697        The various instance variables (C{applicationId}, etc.) are filled into
    698        the list of arguments if they are set.  The command will be built to write
    699        an image to disk.
    700
    701        By default, we will build a RockRidge disc.  If you decide to change
    702        this, think hard about whether you know what you're doing.  This option
    703        is not well-tested.
    704
    705        @param entries: Dictionary of image entries (i.e. self.entries)
    706
    707        @param imagePath: Path to write image out as
    708        @type imagePath: String representing a path on disk
    709
    710        @return: List suitable for passing to L{util.executeCommand} as C{args}.
    711        """
    712        args = self._buildGeneralArgs()
    713        args.append("-graft-points")
    714        if self.useRockRidge:
    715           args.append("-r")
    716        args.append("-o")
    717        args.append(imagePath)
    718        if self.device is not None and self.boundaries is not None:
    719           args.append("-C")
    720           args.append("%d,%d" % (self.boundaries[0], self.boundaries[1]))
    721           args.append("-M")
    722           args.append(self.device)
    723        args.extend(self._buildDirEntries(entries))
    724        return args
    725