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.5) 
 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