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