class EC2::Platform::Linux::Image

This class encapsulate functionality to create an file loopback image from a volume. The image is created using dd. Sub-directories of the volume, including mounts of local filesystems, are copied to the image. Symbolic links are preserved.

This class encapsulate functionality to create an file loopback image from a volume. The image is created using dd. Sub-directories of the volume, including mounts of local filesystems, are copied to the image. Symbolic links are preserved.

Constants

BASE_UTILS
CHROOT_UTILS
DEFAULT_FSTAB
EXCLUDES
IMG_MNT
LEGACY_FSTAB
PART_UTILS
ROOT_DEVICE_REGEX

Public Class Methods

new( volume, image_filename, mb_image_size, exclude, includes, filter = true, fstab = nil, part_type = nil, arch = nil, script = nil, debug = false, grub_config = nil ) click to toggle source

Initialize the instance with the required parameters.

  • volume The path to the volume to create the image file from.

  • image_filename The name of the image file to create.

  • mb_image_size The image file size in MB.

  • exclude List of directories to exclude.

  • debug Make extra noise.

# File lib/ec2/platform/linux/image.rb, line 46
def initialize( volume,
                image_filename,
                mb_image_size,
                exclude,
                includes,
                filter = true,
                fstab = nil,
                part_type = nil,
                arch = nil,
                script = nil,
                debug = false,
                grub_config = nil )
  @volume = volume
  @image_filename = image_filename
  @mb_image_size = mb_image_size
  @exclude = exclude
  @includes = includes
  @filter = filter
  @arch = arch || EC2::Platform::Linux::Uname.platform
  @script = script
  @fstab = nil
  @conf = grub_config
  @warnings = Array.new

  self.verify_runtime(BASE_UTILS)
  self.set_partition_type(part_type)

  # Cunning plan or horrible hack?
  # If :legacy is passed in as the fstab, we use the old v3 manifest's
  # device naming and fstab.
  if [:legacy, :default].include? fstab
    @fstab = fstab
  elsif not fstab.nil?
    @fstab = File.open(fstab).read()
  end
  @debug = debug

  # Exclude the temporary image mount point if it is under the volume
  # being bundled.
  if IMG_MNT.index( volume ) == 0
    @exclude << IMG_MNT
  end
end

Public Instance Methods

check_deps(part_type) click to toggle source
# File lib/ec2/platform/linux/image.rb, line 201
def check_deps(part_type)
    if part_type == EC2::Platform::PartitionType::MBR
      self.verify_runtime([ 'parted' ])
      self.verify_runtime(CHROOT_UTILS, @volume)
    elsif part_type == EC2::Platform::PartitionType::GPT
      self.verify_runtime([ 'sgdisk' ])
      self.verify_runtime(CHROOT_UTILS, @volume)
    end
end
check_kernel_parameters(conf) click to toggle source
# File lib/ec2/platform/linux/image.rb, line 126
def check_kernel_parameters(conf)
  cmdline = File.read('/proc/cmdline')
  cmdline_hash = make_hash(tokenize(cmdline))

  default = nil
  File.readlines(conf).each do |line|
    match = line.match(/^default.*([\d+])/)
    if match
      default = match.captures[0]
    end
  end
  if not default
    STDERR.puts "Couldn't find default kernel designation in grub config. The resulting image may not boot."
    return
  end
  default = default.to_i

  kernels = File.readlines(conf).grep(/^\s*kernel/)
  kernel_line = kernels[default]

  kernel_line = remove_kernel(tokenize(kernel_line))
  kernel_hash = make_hash(kernel_line)

  compare_hashes(cmdline_hash, kernel_hash)

  if not @warnings.empty?
    $stdout.puts "Found the following differences between your kernel " +
                 "commandline and the grub configuration on the volume:"

    @warnings.each do |warning|
      $stdout.puts warning
    end

    $stdout.puts "Please verify that the kernel command line in " +
                 "#{File.expand_path(conf)} is correct for your new AMI."
  end
end
compare_hashes(a, b) click to toggle source
# File lib/ec2/platform/linux/image.rb, line 102
def compare_hashes(a, b)
  a.each do |key,value|
    if not b.has_key?(key)
      @warnings.push "\t* Missing key '#{key}' with value '#{value}'"
    elsif b[key] != value
      @warnings.push "\t* Key '#{key}' value '#{b[key]}' differs from /proc/cmdline value '#{value}'"
    end
  end
end
is_disk_image?() click to toggle source
# File lib/ec2/platform/linux/image.rb, line 324
def is_disk_image?
  EC2::Platform::PartitionType.valid?(@part_type)
end
make() click to toggle source

Create the loopback image file and copy volume to it.

# File lib/ec2/platform/linux/image.rb, line 165
def make
  begin
    puts( "Copying #{@volume} into the image file #{@image_filename}...")
    puts( 'Excluding: ' )
    @exclude.each { |x| puts( "\t #{x}" ) }

    create_image_file
    format_image
    execute( 'sync' )  # Flush so newly formatted filesystem is ready to mount.
    mount_image
    copy_rec( @volume, IMG_MNT)
    update_fstab
    customize_image
    finalize_image
  ensure
    cleanup
  end
end
make_hash(array) click to toggle source
# File lib/ec2/platform/linux/image.rb, line 92
def make_hash(array)
  hash = Hash.new
  array.each do |entry|
    # Split on the first '='
    key, value = entry.split('=', 2)
    hash[key] = value || ''
  end
  hash
end
remove_kernel(tokens) click to toggle source
# File lib/ec2/platform/linux/image.rb, line 112
def remove_kernel(tokens)
  if tokens.include?('kernel')
    tokens.delete('kernel')
    # The kernel can receive optional arguments, drop those along with the kernel
    kernel_index = tokens.index{ |token| !token.include?('--') }
    tokens = tokens.drop(kernel_index + 1)
  end
  tokens
end
set_partition_type(input) click to toggle source
# File lib/ec2/platform/linux/image.rb, line 218
def set_partition_type(input)
  input ||= EC2::Platform::PartitionType::NONE
  if input == EC2::Platform::PartitionType::NONE
    # We are not doing anything interesting. Return early.
    puts('Not partitioning boot device.')
    @part_type = EC2::Platform::PartitionType::NONE
    return
  end

  # Verify that general partitioning utilities are present
  self.verify_runtime(PART_UTILS)

  value = input
  puts('Setting partition type to bundle "%s" with...' % [@volume])
  if File.directory?(@volume)
    mtab = EC2::Platform::Linux::Mtab.load
    entry = mtab.entries[@volume]
    if entry
      # Volume is a mounted file system:
      # * Determine the mounted device
      # * Ensure device partition scheme is one that we understand
      # * Ensure device and it's container(if applicable) are block devices
      # * Determine the current partition type using parted if appropriate.
      device = entry.device
      root = nil
      if (match = ROOT_DEVICE_REGEX.match(device))
        root = match[1]
        root = device unless File.exists?(root) # classic AMI with no partition table
        # Be paranoid. Bail unless the device and parent are block devices
        [device, root].each do |dev|
          unless File.blockdev?(dev)
            raise FatalError.new('Not a block device: %s' % [dev])
          end
        end
      else
        raise FatalError.new('Non-standard volume device "%s"' % [device])
      end
      if input == :auto
        self.verify_runtime([ 'parted' ])
        puts('Auto-detecting partition type for "%s"' % [@volume])
        cmd = "parted -s %s print|awk -F: '/^Partition Table/{print $2}'" % [root]
        value = evaluate(cmd).strip
        raise FatalError.new('Cannot determine partition type') if value.empty?
        puts('Partition label detected using parted: "%s"' % value)
      end
    else
      # Volume specified is possibly a file system root:
      #   * Proceed cautiously using sane defaults if no partition type
      #     has been provided.
      puts('Volume "%s" is not a mount point.'  % [@volume])
      value = EC2::Platform::PartitionType::GPT if input == :auto
      puts('Treating it as a file system root and using "%s"...' % [value])
    end
  elsif File.blockdev?(@volume)
    # Volume specified is a block device:
    #   * Not sure how we got here.
    #   * We only support bundling of file system roots and not block
    #     devices, so throw an exception.
    raise FatalError('Volume cannot be a block device "%s".' % [@volume])
  else
    # Volume specified is not a file system (mounted or otherwise):
    #   * Bail!
    raise FatalError.new('Cannot determine partition type of "%s"' % [@volume])
  end

  if EC2::Platform::PartitionType.valid?(value)
    @part_type = value
  elsif value == 'msdos'
    # This is the parted label value reported for MBR partition tables.
    @part_type = EC2::Platform::PartitionType::MBR
  elsif value == 'loop'
    # This typically indicates that we have a bare partition that is not
    # part of a partition table. This is typically the case for pv amis.
    @part_type = EC2::Platform::PartitionType::NONE
  elsif value == 'gpt'
    @part_type = EC2::Platform::PartitionType::GPT
  elsif value
    if input == :auto
      # Somehow we failed to determine a partition type that we support
      raise FatalError.new('Could not determine a suitable partition type')
    else
      # User specified a format we currently do not support. Bail.
      raise FatalError.new('Unsupported partition table type %s' % input)
    end
  else
    raise FatalError.ne('Cannot determine partition type for %s' % [@volume])
  end
  puts('Using partition type "%s"' % @part_type)

  self.check_deps(@part_type)
end
settle() click to toggle source
# File lib/ec2/platform/linux/image.rb, line 312
def settle
  # Run sync and udevadm settle to quiet device.
  execute('sync||:')
  if File.executable?('/usr/sbin/udevsettle')
    execute('/usr/sbin/udevsettle||:')
  elsif File.executable?('/sbin/udevadm')
    execute('/sbin/udevadm settle||:')
  end
end
tokenize(string) click to toggle source
# File lib/ec2/platform/linux/image.rb, line 122
def tokenize(string)
  string.strip.split(/\s+/)
end
verify_runtime(utils, chroot = nil) click to toggle source
# File lib/ec2/platform/linux/image.rb, line 186
def verify_runtime(utils, chroot = nil)
  unless ENV['PATH']
    raise FatalError.new('PATH not set, cannot find needed utilities')
  end

  paths = ENV['PATH'].split(File::PATH_SEPARATOR)
  paths.map! { |path| File.join(chroot, path) } if chroot

  utils.each do |util|
    unless paths.any? { |dir| File.executable?(File.join(dir, util)) }
      raise FatalError.new("Required utility '%s' not found in PATH - is it installed?" % util)
    end
  end
end

Private Instance Methods

adjustconf(conf) click to toggle source
# File lib/ec2/platform/linux/image.rb, line 630
def adjustconf(conf)
  if conf
    puts("Adjusting #{File.expand_path(conf)}")
    conf = File.expand_path(conf)
    data = IO.read(conf).split(/\n/).map do |line|
      line.gsub(/root\s+\(hd0\)/, 'root (hd0,0)')
    end
    File.open(conf, 'w'){|io| io << data.join("\n")}
    puts(evaluate('cat %s' % conf))
  end
end
cleanup() click to toggle source

Unmount devices. Delete temporary files.

# File lib/ec2/platform/linux/image.rb, line 347
def cleanup
  # Unmount image file.
  if self.is_disk_image?
    unmount('%s/sys' % IMG_MNT)
    unmount('%s/proc' % IMG_MNT)
    unmount('%s/dev' % IMG_MNT)
  end

  unmount(IMG_MNT)
  if self.is_disk_image? and @diskdev
    diskname = File.basename(@diskdev)
    execute('kpartx -d %s' % @diskdev)
    execute('dmsetup remove %s' % diskname)
    execute('losetup -d %s' % @diskloop)
  end
end
copy_rec( src, dst, options={:xattributes => true} ) click to toggle source
# File lib/ec2/platform/linux/image.rb, line 691
def copy_rec( src, dst, options={:xattributes => true} )
  begin
    rsync = EC2::Platform::Linux::Rsync::Command.new
    rsync.archive.times.recursive.sparse.links.quietly.include(@includes).exclude(@exclude)
    if @filter
      rsync.exclude(EC2::Platform::Linux::Constants::Security::FILE_FILTER)
    end
    rsync.xattributes if options[ :xattributes ]
    rsync.src(File::join( src, '*' )).dst(dst)
    execute(rsync.expand)
    return true
  rescue Exception => e
    rc = $?.exitstatus
    return true if rc == 0
    if rc == 23 and SysChecks::rsync_usable?
      STDERR.puts [
       'NOTE: rsync seemed successful but exited with error code 23. This probably means',
       'that your version of rsync was built against a kernel with HAVE_LUTIMES defined,',
       'although the current kernel was not built with this option enabled. The bundling',
       'process will thus ignore the error and continue bundling.  If bundling completes',
       'successfully, your image should be perfectly usable. We, however, recommend that',
       'you install a version of rsync that handles this situation more elegantly.'
      ].join("\n")
      return true
    elsif rc == 1 and options[ :xattributes ]
      STDERR.puts [
       'NOTE: rsync with preservation of extended file attributes failed. Retrying rsync',
       'without attempting to preserve extended file attributes...'
      ].join("\n")
      o = options.clone
      o[ :xattributes ] = false
      return copy_rec( src, dst, o)
    end
    raise e
  end
end
create_image_file() click to toggle source

Call dd to create the image file.

# File lib/ec2/platform/linux/image.rb, line 367
def create_image_file
  cmd = "dd if=/dev/zero status=noxfer of=" + @image_filename +
        " bs=1M count=1 seek=" + (@mb_image_size-1).to_s
  execute( cmd )
end
customize_image() click to toggle source
# File lib/ec2/platform/linux/image.rb, line 514
def customize_image
  return unless @script and File.executable?(@script)
  puts('Customizing cloned volume mounted at %s with script %s' % [IMG_MNT, @script])
  output = evaluate('%s "%s"' % [@script, IMG_MNT])
  STDERR.puts output if @debug
end
evaluate( cmd, success = 0, verbattim = nil ) click to toggle source
# File lib/ec2/platform/linux/image.rb, line 798
def evaluate( cmd, success = 0, verbattim = nil )
  verbattim = @debug if verbattim.nil?
  STDERR.puts( "Evaluating: %s" % cmd ) if verbattim
  pid, stdin, stdout, stderr = Open4::popen4( cmd )
  pid, status = Process::waitpid2 pid
  unless status.exitstatus == success
    raise FatalError.new( "Failed to evaluate '#{cmd }'. Reason: #{stderr.read}." )
  end
  stdout.read
end
execute( cmd, chroot = nil, nullenv = true ) click to toggle source

Execute the command line cmd.

# File lib/ec2/platform/linux/image.rb, line 772
def execute( cmd, chroot = nil, nullenv = true )
  command = cmd
  if chroot and not File.directory?(chroot)
    raise FatalError.new('Cannot chroot into %s. Not a directory' % [chroot])
  end
  if chroot
    env = nullenv ? 'env -i' : ''
    command = 'setarch %s chroot %s %s %s' % [@arch, chroot, env, cmd]
  else
    command = cmd
  end

  if @debug
    if chroot
      STDERR.puts( 'Executing(chroot=%s): %s' % [chroot, command ] )
    else
      STDERR.puts( 'Executing: %s' % command )
    end
  else
    command += ' >/dev/null 2>&1'
  end
  raise FatalError.new("Failed to execute: '#{cmd}'") unless system( command )
end
finalize_image() click to toggle source
# File lib/ec2/platform/linux/image.rb, line 521
def finalize_image
  return unless self.is_disk_image?
  begin
    # GRUB needs to reference the device.map file to know about our
    # disk layout. So let's write out a simple one.
    devmapfile = '%s/boot/grub/device.map' % IMG_MNT
    FileUtils.mkdir_p(File.dirname(devmapfile))
    File.open(devmapfile, 'w') do|io|
      io << "(hd0) %s\n" % @diskdev
    end

    # Provide a suitable /etc/mtab if it doesn't already exist
    fixmtab = false
    mtabfile = '%s/etc/mtab' % IMG_MNT
    execute('ln -s /proc/mounts %s' % mtabfile) unless File.exists?(mtabfile)

    puts('Installing GRUB on root device with %s boot scheme' % @part_type)
    # Newish versions of old GRUB expect the first partition
    # of /dev/mapper/hda to be /dev/mapper/hda1. Lie to GRUB
    # by adding a symlink to /dev/mapper/hda1.
    hdap1 = '%s/dev/mapper/hdap1' % IMG_MNT
    execute('ln -s ./hda1 %s' % hdap1) unless File.exists?(hdap1)

    # Try to find the grub stages. There isn't a good way to know where
    # exactly these will be on a given system, so this glob is a little
    # excessive, but it should usually result in finding the path to
    # the grub stages. It's also possible that it will exist outside
    # /usr, but unlikely enough to not merit another glob there.
    stage1 = Dir.glob("#{IMG_MNT}/usr/**/grub*/**/stage1")
    if stage1.empty?
      raise RuntimeError, "Couldn't find grub stages under #{IMG_MNT}"
    end
    stagesdir = File.dirname(stage1[0])

    # Copy the stages into the grub dir on /boot
    # Normally you'd let grub-install do this, but it can be very picky
    # and doing the setup manually seems to be more reliable.
    Dir.glob(["#{stagesdir}/stage{1,2}", "#{stagesdir}/*_stage1_5"]).each do |stage|
      dest = "#{IMG_MNT}/boot/grub/%s" % File.basename(stage)
      FileUtils.rm_f(dest)
      FileUtils.cp(stage, dest)
    end

    # We're now ready to install GRUB.
    case @part_type
    when EC2::Platform::PartitionType::MBR
      cmd = 'device (hd0) %s\nroot (hd0,0)\nsetup (hd0)' % @diskdev
      execute('echo -e "%s" | grub --device-map=/dev/null --batch' % cmd, IMG_MNT)
    when EC2::Platform::PartitionType::GPT
      case @fstype
      when /ext[234]/
        file = '/boot/grub/e2fs_stage1_5'
      when 'xfs'
        file = '/boot/grub/xfs_stage1_5'
      else
        raise RuntimeError, 'File system type %s unsupported' % [@fstype]
      end

      file = File.join(IMG_MNT, file)
      size = (File.size(file) + 511)/512
      head = 2048 # start of the BIOS Boot Partition
      cmd = 'dd if=%s of=%s seek=%s conv=fsync status=noxfer' % [file, @diskdev, head]
      execute(cmd)
      cmd =  'device (hd0) %s\n' % @diskdev
      cmd += 'root (hd0,0)\n'
      cmd += 'install /boot/grub/stage1 (hd0) (hd0)%s+%s ' % [head, size]
      cmd += 'p (hd0,0)/boot/grub/stage2 /boot/grub/grub.conf'
      execute('echo -e "%s" | grub --device-map=/dev/null --batch' % cmd, IMG_MNT)
    else
      raise RuntimeError, 'Unknown partition table type %s' % @part_type
    end

    # Check for reasonable kernel parameters
    if @conf # user-supplied
      src = Pathname(@conf).realpath.to_s
      puts "Using user supplied grub config #{src}"
      check_kernel_parameters(@conf)

      dst_dir = File.join(IMG_MNT, '/boot/grub')
      FileUtils.mkdir_p(dst_dir) unless File.exists?(dst_dir)

      menulst = File.join(dst_dir, '/menu.lst')
      # Copy the user supplied grub-config over any default ones
      FileUtils.copy_entry(src, menulst, remove_destination=true)

      grubconf = File.join(dst_dir, '/grub.conf')
      File.delete(grubconf) unless not File.exists?(grubconf)
      File.symlink('menu.lst', grubconf)
      @conf = menulst
    else
      default_confs = [File.join(IMG_MNT, '/boot/grub/grub.conf'),
                       File.join(IMG_MNT, '/boot/grub/menu.lst')]
      @conf = default_confs.find { |file| File.file?(file) }
      if @conf
        puts "Using default grub config"
        check_kernel_parameters(@conf)
      else
        STDERR.puts('WARNING: No GRUB config found. The resulting image may not boot')
      end
    end

    # Finally, tweak grub.conf to ensure we can boot.
    adjustconf(@conf)
  ensure
    FileUtils.rm_f(hdap1)
    FileUtils.rm_f(mtabfile) if fixmtab
  end
end
format_image() click to toggle source

Format the image file, tune filesystem not to fsck based on interval. Where available and possible, retain the original root volume label uuid and file-system type falling back to using ext3 if not sure of what to do.

# File lib/ec2/platform/linux/image.rb, line 379
def format_image
  mtab = EC2::Platform::Linux::Mtab.load
  root = mtab.entries[Pathname(@volume).realpath.to_s].device rescue nil
  info = fsinfo( root )
  label= info[:label]
  uuid = info[:uuid]
  type = info[:type] || 'ext3'
  execute('modprobe loop') unless File.blockdev?('/dev/loop0')

  target = nil
  if self.is_disk_image?
    cmd = []
    img = @image_filename
    size = (@mb_image_size * 1024 * 1024 / 512)
    case @part_type
    when EC2::Platform::PartitionType::MBR
      # Add a partition table and leave space to install a boot-loader.
      # The boot partition fills up the disk. Note that '-1s' indicates
      # the end of disk (and not 1 sector in from the end.
      head = 63
      cmd << ['unit s']
      cmd << ['mklabel msdos']
      cmd << ['mkpart primary %s -1s' % head]
      cmd << ['set 1 boot on print quit']
      cmd = "parted --script %s -- '%s'" % [img, cmd.join(' ')]
      execute(cmd)
      self.settle
    when EC2::Platform::PartitionType::GPT
      # Add a 1M (2048 sector) BIOS Boot Partition (BPP) to hold the
      # GRUB bootloader and then fill the rest of the disk with the
      # boot partition.
      #
      # * GRUB2 is BBP-aware and will automatically use this partition for
      #   stage2.
      #
      # * Legacy GRUB is not smart enough to use the BBP for stage 1.5.
      #   We deal with that during GRUB setup in #finalize_image.
      #
      # * Legacy GRUB knows enough about GPT partitions to reference
      #   them by number (avoiding the need for the hybrid MBR hack), so
      #   we set this partition to the maximum partition available to
      #   avoid incrementing the root partition number.
      last = evaluate('sgdisk --print %s |grep "^Partition table holds up to"|cut -d" " -f6 ||:' % img).strip
      last = 4 if last.empty? # fallback to 4.
      execute('sgdisk --new %s:1M:+1M --change-name %s:"BIOS Boot Partition" --typecode %s:ef02 %s' % [last,last,last, img])
      self.settle
      execute('sgdisk --largest-new=1 --change-name 1:"Linux" --typecode 1:8300 %s' % img)
      self.settle
      execute('sgdisk --print  %s' % img)
      self.settle
    else
      raise NotImplementedError, "Partition table type %s not supported" % @part_type
    end
    self.settle

    # The series of activities below are to ensure we can workaround
    # the vagaries of various versions of GRUB. After we have created
    # partition table above, we fake an hda-named device by leveraging
    # device mapper to create a linear device named "/dev/mapper/hda".
    # We then set @target to its first partition. This makes @target
    # easy to manipulate pretty much in the same way that we handle
    # non-partitioned images. All cleanup happens during #cleanup.
    @diskloop = evaluate('losetup -f').strip
    execute('losetup %s %s' % [@diskloop, @image_filename])
    @diskdev = '/dev/mapper/hda'
    @partdev = '%s1' % [ @diskdev]
    diskname = File.basename(@diskdev)
    loopname = File.basename(@diskloop)
    majmin = IO.read('/sys/block/%s/dev' % loopname).strip
    execute( 'echo 0 %s linear %s 0|dmsetup create %s' % [size, majmin, diskname] )
    execute( 'kpartx -a %s' % [ @diskdev ] )
    self.settle
    @target = @partdev
    @fstype = type
  else
    # Not creating a disk image.
    @target = @image_filename
  end

  tune = nil
  mkfs = [ '/sbin/mkfs.' + type ]
  case type
  when 'btrfs'
    mkfs << [ '-L', label] unless label.to_s.empty?
    mkfs << [ @target ]
  when 'xfs'
    mkfs << [ '-L', label] unless label.to_s.empty?
    mkfs << [ @target ]
    tune =  [ '/usr/sbin/xfs_admin' ]
    tune << [ '-U', uuid ] unless uuid.to_s.empty?
    tune << [ @target ]
  else
    # Type unknown or ext2 or ext3 or ext4
    # New e2fsprogs changed the default inode size to 256 which is
    # incompatible with some older kernels, and older versions of
    # grub. The options below change the behavior back to the
    # expected RHEL5 behavior if we are bundling disk images. This
    # is not ideal, but oh well.
    if ['ext2', 'ext3', 'ext4'].include?(type)
      # Clear all the defaults specified in /etc/mke2fs.conf
      features = ['none']

      # Get Filesytem Features as reported by dumpe2fs
      output = evaluate("dumpe2fs -h %s | grep 'Filesystem features'" % root)
      parts = output.split(':')[1].lstrip.split(' ')
      features.concat(parts)
      features.delete('needs_recovery')
      if features.include?('64bit')
        puts "WARNING: 64bit filesystem flag detected on root device (#{root}), resulting image may not boot"
      end

      if self.is_disk_image?
        mkfs = [ '/sbin/mke2fs -t %s -v -m 1' % type ]
        mkfs << ['-O', features.join(',')]
        if ['ext2', 'ext3',].include?(type)
          mkfs << [ '-I 128 -i 8192' ]
        end
      else
        mkfs << ['-F']
        mkfs << ['-O', features.join(',')]
      end
    else
      # Unknown case
      mkfs << ['-F']
    end
    mkfs << [ '-L', label] unless label.to_s.empty?
    mkfs << [ @target ]
    tune =  [ '/sbin/tune2fs -i 0' ]
    tune << [ '-U', uuid ] unless uuid.to_s.empty?
    tune << [ @target ]
  end
  execute( mkfs.join( ' ' ) )
  execute( tune.join( ' ' ) ) if tune
end
fsinfo( fs ) click to toggle source
# File lib/ec2/platform/linux/image.rb, line 642
def fsinfo( fs )
  result = {}
  if fs and File.exists?( fs )
    ['LABEL', 'UUID', 'TYPE' ].each do |tag|
      begin
        property = tag.downcase.to_sym
        value = evaluate( '/sbin/blkid -o value -s %s %s' % [tag, fs] ).strip
        result[property] = value if value and not value.empty?
      rescue FatalError => e
        if @debug
          STDERR.puts e.message
          STDERR.puts "Could not replicate file system #{property}. Proceeding..."
        end
      end
    end
  end
  result
end
make_fstab() click to toggle source
# File lib/ec2/platform/linux/image.rb, line 740
def make_fstab
  case @fstab
  when :legacy
    return LEGACY_FSTAB
  when :default
    return DEFAULT_FSTAB
  else
    return @fstab
  end
end
make_special_devices() click to toggle source
# File lib/ec2/platform/linux/image.rb, line 730
def make_special_devices
  execute("mknod %s/dev/null    c 1 3" % IMG_MNT)
  execute("mknod %s/dev/zero    c 1 5" % IMG_MNT)
  execute("mknod %s/dev/tty     c 5 0" % IMG_MNT)
  execute("mknod %s/dev/console c 5 1" % IMG_MNT)
  execute("ln -s null %s/dev/X0R" % IMG_MNT)
end
mount_image() click to toggle source

Mount the image file as a loopback device. The mount point is created if necessary.

# File lib/ec2/platform/linux/image.rb, line 665
def mount_image
  Dir.mkdir(IMG_MNT) if not FileUtil::exists?(IMG_MNT)
  raise FatalError.new("image already mounted") if mounted?(IMG_MNT)
  dirs = ['mnt', 'proc', 'sys', 'dev']
  if self.is_disk_image?
    execute( 'mount -o nouuid -t %s %s %s' % [@fstype, @target, IMG_MNT] )
    dirs.each{|dir| FileUtils.mkdir_p( '%s/%s' % [IMG_MNT, dir])}
    make_special_devices
    execute( 'mount -o bind /proc %s/proc' % IMG_MNT )
    execute( 'mount -o bind /sys %s/sys' % IMG_MNT )
    execute( 'mount -o bind /dev %s/dev' % IMG_MNT )
  else
    execute( 'mount -o loop ' + @target + ' ' + IMG_MNT )
    dirs.each{ |dir| FileUtils.mkdir_p( '%s/%s' % [IMG_MNT, dir]) }
    make_special_devices
  end
end
mounted?(mpoint) click to toggle source
# File lib/ec2/platform/linux/image.rb, line 340
def mounted?(mpoint)
  EC2::Platform::Linux::Mtab.load.entries.keys.include? mpoint
end
unmount(mpoint) click to toggle source
# File lib/ec2/platform/linux/image.rb, line 332
def unmount(mpoint)
  if mounted?(mpoint) then
      execute('umount -d ' + mpoint)
  end
end
update_fstab() click to toggle source
# File lib/ec2/platform/linux/image.rb, line 753
def update_fstab
  if @fstab
    etc = File::join( IMG_MNT, 'etc')
    fstab = File::join( etc, 'fstab' )

    FileUtils::mkdir_p( etc ) unless File::exist?( etc)
    execute( "cp #{fstab} #{fstab}.old" ) if File.exist?( fstab )
    fstab_content = make_fstab
    File.open( fstab, 'w' ) { |f| f.write( fstab_content ) }
    puts "/etc/fstab:"
    fstab_content.each_line do |s|
      puts "\t #{s}"
    end
  end
end