Module: QcowDisk

Defined in:
lib/disk/modules/QcowDisk.rb

Constant Summary collapse

QCOW_HEADER_PARTIAL =
BinaryStruct.new([
  'A4', 'magicNumber',
  'N', 'version',
])
SIZEOF_QCOW_HEADER_PARTIAL =
QCOW_HEADER_PARTIAL.size
QCOW_HEADER_V1 =
BinaryStruct.new([
  'A4', 'magicNumber',
  'N', 'version',
  'N', 'backing_filename_offset_hi',
  'N', 'backing_filename_offset_lo',
  'N', 'backing_filename_size',
  'N', 'mtime',
  'N', 'size_hi',
  'N', 'size_lo',
  'C', 'cluster_bits',
  'C', 'l2_bits',
  'N', 'crypt_method',
  'N', 'l1_table_offset_hi',
  'N', 'l1_table_offset_lo',
])
SIZEOF_QCOW_HEADER_V1 =
QCOW_HEADER_V1.size
QCOW_HEADER_V2 =
BinaryStruct.new([
  'A4', 'magicNumber',
  'N', 'version',
  'N', 'backing_filename_offset_hi',
  'N', 'backing_filename_offset_lo',
  'N', 'backing_filename_size',
  'N', 'cluster_bits',
  'N', 'size_hi',
  'N', 'size_lo',
  'N', 'crypt_method',
  'N', 'l1_size',
  'N', 'l1_table_offset_hi',
  'N', 'l1_table_offset_lo',
  'N', 'refcount_table_offset_hi',
  'N', 'refcount_table_offset_lo',
  'N', 'refcount_table_clusters',
  'N', 'number_of_snapshots',
  'N', 'snapshots_offset_hi',
  'N', 'snapshots_offset_lo',
])
SIZEOF_QCOW_HEADER_V2 =
QCOW_HEADER_V2.size
QCOW_HEADER_V3 =
BinaryStruct.new(QCOW_HEADER_V2.definition + [
  'Q', 'incompatible_features',
  'Q', 'compatible_features',
  'Q', 'autoclear_features',
  'N', 'refcount_order',
  'N', 'header_length'
])
SIZEOF_QCOW_HEADER_V3 =
QCOW_HEADER_V3.size
QCOW_OFLAG_COPIED =

indicate that the refcount of the referenced cluster is exactly one.

(1 << 63)
QCOW_OFLAG_COMPRESSED =

indicate that the cluster is compressed (they never have the copied flag)

(1 << 62)
LO63_MASK =
~QCOW_OFLAG_COPIED
LO62_MASK =
~(QCOW_OFLAG_COPIED | QCOW_OFLAG_COMPRESSED)
L1E_OFFSET_MASK =
0x00fffffffffffe00
L2E_OFFSET_MASK =
0x00fffffffffffe00
L2E_COMPRESSED_OFFSET_SIZE_MASK =
0x3fffffffffffffff
L2E_PREALLOCATED_MASK =
0x1
SECTOR_SIZE =
512
ZLIB_WINDOW_BITS =
-12
INCOMPATIBLE_FEATURES_MASK =
{
  :dirty   => 0x1,
  :corrupt => 0x2
}
KNOWN_INCOMPATIBLE_FEATURES_MASK =
0x3
COMPATIBLE_FEATURES_MASK =
{
  :lazy_refcounts => 0x1
}
AUTOCLEAR_FEATURES_MASK =
{
}
HEADER_EXTENSION_TYPE_SIZE =
4
HEADER_EXTENSION_LENGTH_SIZE =
4
HEADER_EXTENSION_TYPES =
{
  :end_of_header_extension_area => 0x00000000,
  :backing_file_format_name     => 0xE2792ACA,
  :feature_table_name           => 0x6803f857
}

Instance Method Summary collapse

Instance Method Details

#backing_file_nameObject



162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/disk/modules/QcowDisk.rb', line 162

def backing_file_name
  @backing_file_name ||= begin
    if backing_filename_offset > 0
      file_handle.seek(backing_filename_offset, IO::SEEK_SET)
      backing_fname = file_handle.read(backing_filename_size)
      bfn = File.expand_path File.join(File.dirname(@filename), backing_fname)

      #
      # Check if the backing file is a logical volume from a direct lun volume group.
      #
      bfn_test = File.expand_path File.join(File.dirname(@filename), File.basename(bfn))
      use_lv = false
      if (avm = @dInfo.applianceVolumeManager)
        use_lv = avm.lvHash.key?(bfn_test)
      end
      if (!File.symlink?(bfn) && !File.file?(bfn)) && use_lv
        bfn_test
      else
        bfn
      end
    else
      ""
    end
  end
end

#cluster_sectorsObject



188
189
190
# File 'lib/disk/modules/QcowDisk.rb', line 188

def cluster_sectors
  @cluster_sectors ||= 1 << (cluster_bits - 9)
end

#d_closeObject



145
146
147
148
149
150
151
# File 'lib/disk/modules/QcowDisk.rb', line 145

def d_close
  [@backing_file_handle, @file_handle].each do |h|
    next if h.nil?
    h.close
    h = nil
  end
end

#d_initObject



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/disk/modules/QcowDisk.rb', line 101

def d_init
  self.diskType = "QCOW"
  self.blockSize = SECTOR_SIZE

  if dInfo.mountMode.nil? || dInfo.mountMode == "r"
    dInfo.mountMode = "r"
    @fileMode = "r"
  elsif dInfo.mountMode == "rw"
    @fileMode = "r+"
  else
    raise "Unrecognized mountMode: #{dInfo.mountMode}"
  end

  @filename = dInfo.fileName
  @dOffset = dInfo.offset
  @downstreamDisk = dInfo.downstreamDisk
  self.diskType = "#{diskType}-#{@downstreamDisk.diskType}" if @downstreamDisk

  # Ensure all the disks in the chain are opened before we return (required to address RHEV SSA UID issues).
  backing_file_handle
end

#d_read(pos, len, _offset = 0) ⇒ Object



127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/disk/modules/QcowDisk.rb', line 127

def d_read(pos, len, _offset = 0)
  pos += @dOffset if @dOffset
  return nil if pos >= @endByteAddr
  len = @endByteAddr - pos if (pos + len) > @endByteAddr

  sector_num, sector_offset = pos.divmod(SECTOR_SIZE)
  sector_count = ((pos + len - 1) / SECTOR_SIZE) - sector_num + 1

  read_buf  = read_sectors(sector_num, sector_count)
  buf       = read_buf[sector_offset, len]

  buf
end

#d_sizeObject

Disk size in sectors.



154
155
156
# File 'lib/disk/modules/QcowDisk.rb', line 154

def d_size
  uint64(header, 'size') / @blockSize
end

#d_write(_pos, _buf, _len, _offset = 0) ⇒ Object



141
142
143
# File 'lib/disk/modules/QcowDisk.rb', line 141

def d_write(_pos, _buf, _len, _offset = 0)
  raise "QcowDisk#d_write not implemented"
end

#getBaseObject



123
124
125
# File 'lib/disk/modules/QcowDisk.rb', line 123

def getBase
  self
end

#total_sectorsObject



192
193
194
# File 'lib/disk/modules/QcowDisk.rb', line 192

def total_sectors
  @total_sectors ||= size / SECTOR_SIZE
end

#versionObject



158
159
160
# File 'lib/disk/modules/QcowDisk.rb', line 158

def version
  @version ||= header['version']
end