delta_diff_generator.h revision fdf005161523d9873a281f2a96d51dd4a3261f4f
1// Copyright (c) 2012 The Chromium OS Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#ifndef UPDATE_ENGINE_PAYLOAD_GENERATOR_DELTA_DIFF_GENERATOR_H_ 6#define UPDATE_ENGINE_PAYLOAD_GENERATOR_DELTA_DIFF_GENERATOR_H_ 7 8#include <set> 9#include <string> 10#include <vector> 11 12#include <base/macros.h> 13#include <chromeos/secure_blob.h> 14 15#include "update_engine/payload_constants.h" 16#include "update_engine/payload_generator/graph_types.h" 17#include "update_engine/payload_generator/graph_utils.h" 18#include "update_engine/payload_generator/payload_generation_config.h" 19#include "update_engine/update_metadata.pb.h" 20 21// There is one function in DeltaDiffGenerator of importance to users 22// of the class: GenerateDeltaUpdateFile(). Before calling it, 23// the old and new images must be mounted. Call GenerateDeltaUpdateFile() 24// with both the mount-points of the images in addition to the paths of 25// the images (both old and new). A delta from old to new will be 26// generated and stored in output_path. 27 28namespace chromeos_update_engine { 29 30extern const char* const kEmptyPath; 31extern const size_t kBlockSize; 32extern const size_t kRootFSPartitionSize; 33 34// The payload generation strategy prototype. This is the function that does 35// all the work to generate the operations for the rootfs and the kernel. 36// Given the |config|, generates the payload by populating the |graph| with 37// the operations required to update the rootfs when applied in the order 38// specified by |final_order|, and populating |kernel_ops| with the list of 39// kernel operations required to update the kernel. 40// The operations returned will refer to offsets in the file |data_file_fd|, 41// where this function stores the output, but not necessarily in the same 42// order as they appear in the |final_order| and |kernel_ops|. 43// This function stores the amount of data written to |data_file_fd| in 44// |data_file_size|. 45// TODO(deymo): Replace |graph|, |kernel_ops| and |final_order| by two vectors 46// of annotated InstallOperation (InstallOperation plus a name for logging 47// purposes). At this level, there's no need to have a graph. 48// TODO(deymo): Convert this type alias into a base class, so other parameter 49// can be passed to the particular generators while keeping the same interface 50// for operation generation. 51using OperationsGenerator = bool( 52 const PayloadGenerationConfig& /* config */, 53 int /* data_file_fd */, 54 off_t* /* data_file_size */, 55 Graph* /* graph */, 56 std::vector<DeltaArchiveManifest_InstallOperation>* /* kernel_ops */, 57 std::vector<Vertex::Index>* /* final_order */); 58 59 60class DeltaDiffGenerator { 61 public: 62 // Represents a disk block on the install partition. 63 struct Block { 64 // During install, each block on the install partition will be written 65 // and some may be read (in all likelihood, many will be read). 66 // The reading and writing will be performed by InstallOperations, 67 // each of which has a corresponding vertex in a graph. 68 // A Block object tells which vertex will read or write this block 69 // at install time. 70 // Generally, there will be a vector of Block objects whose length 71 // is the number of blocks on the install partition. 72 Block() : reader(Vertex::kInvalidIndex), writer(Vertex::kInvalidIndex) {} 73 Vertex::Index reader; 74 Vertex::Index writer; 75 }; 76 77 // This is the only function that external users of the class should call. 78 // The |config| describes the payload generation request, describing both 79 // old and new images for delta payloads and only the new image for full 80 // payloads. 81 // For delta payloads, the images should be already mounted read-only at 82 // the respective rootfs_mountpt. 83 // |private_key_path| points to a private key used to sign the update. 84 // Pass empty string to not sign the update. 85 // |output_path| is the filename where the delta update should be written. 86 // Returns true on success. Also writes the size of the metadata into 87 // |metadata_size|. 88 static bool GenerateDeltaUpdateFile(const PayloadGenerationConfig& config, 89 const std::string& output_path, 90 const std::string& private_key_path, 91 uint64_t* metadata_size); 92 93 // These functions are public so that the unit tests can access them: 94 95 // Generate the update payload operations for the kernel and rootfs using 96 // SOURCE_* operations, used to generate deltas for the minor version 97 // kSourceMinorPayloadVersion. This function will generate operations in the 98 // rootfs that will read blocks from the source partition in random order and 99 // write the new image on the target partition, also possibly in random order. 100 // The rootfs operations are stored in |graph| and should be executed in the 101 // |final_order| order. The kernel operations are stored in |kernel_ops|. All 102 // the offsets in the operations reference the data written to |data_file_fd|. 103 // The total amount of data written to that file is stored in 104 // |data_file_size|. 105 static bool GenerateDeltaWithSourceOperations( 106 const PayloadGenerationConfig& config, 107 int data_file_fd, 108 off_t* data_file_size, 109 Graph* graph, 110 std::vector<DeltaArchiveManifest_InstallOperation>* kernel_ops, 111 std::vector<Vertex::Index>* final_order); 112 113 // For each regular file within new_root, creates a node in the graph, 114 // determines the best way to compress it (REPLACE, REPLACE_BZ, COPY, BSDIFF), 115 // and writes any necessary data to the end of data_fd. 116 static bool DeltaReadFiles(Graph* graph, 117 std::vector<Block>* blocks, 118 const std::string& old_root, 119 const std::string& new_root, 120 off_t chunk_size, 121 int data_fd, 122 off_t* data_file_size, 123 bool src_ops_allowed); 124 125 // For a given regular file which must exist at new_root + path, and 126 // may exist at old_root + path, creates a new InstallOperation and 127 // adds it to the graph. Also, populates the |blocks| array as 128 // necessary, if |blocks| is non-null. Also, writes the data 129 // necessary to send the file down to the client into data_fd, which 130 // has length *data_file_size. *data_file_size is updated 131 // appropriately. If |existing_vertex| is no kInvalidIndex, use that 132 // rather than allocating a new vertex. Returns true on success. 133 static bool DeltaReadFile(Graph* graph, 134 Vertex::Index existing_vertex, 135 std::vector<Block>* blocks, 136 const std::string& old_root, 137 const std::string& new_root, 138 const std::string& path, 139 off_t chunk_offset, 140 off_t chunk_size, 141 int data_fd, 142 off_t* data_file_size, 143 bool src_ops_allowed); 144 145 // Reads old_filename (if it exists) and a new_filename and determines 146 // the smallest way to encode this file for the diff. It stores 147 // necessary data in out_data and fills in out_op. 148 // If there's no change in old and new files, it creates a MOVE 149 // operation. If there is a change, or the old file doesn't exist, 150 // the smallest of REPLACE, REPLACE_BZ, or BSDIFF wins. 151 // new_filename must contain at least one byte. 152 // |new_filename| is read starting at |chunk_offset|. 153 // If |chunk_size| is not -1, only up to |chunk_size| bytes are diffed. 154 // If |src_ops_allowed| is true, it will emit SOURCE_COPY and SOURCE_BSDIFF 155 // operations instead of MOVE and BSDIFF, respectively. 156 // Returns true on success. 157 static bool ReadFileToDiff(const std::string& old_filename, 158 const std::string& new_filename, 159 off_t chunk_offset, 160 off_t chunk_size, 161 bool bsdiff_allowed, 162 chromeos::Blob* out_data, 163 DeltaArchiveManifest_InstallOperation* out_op, 164 bool gather_extents, 165 bool src_ops_allowed); 166 167 // Delta compresses a kernel partition |new_kernel_part| with knowledge of the 168 // old kernel partition |old_kernel_part|. If |old_kernel_part| is an empty 169 // string, generates a full update of the partition. 170 static bool DeltaCompressKernelPartition( 171 const std::string& old_kernel_part, 172 const std::string& new_kernel_part, 173 std::vector<DeltaArchiveManifest_InstallOperation>* ops, 174 int blobs_fd, 175 off_t* blobs_length, 176 bool src_ops_allowed); 177 178 // Reads blocks from image_path that are not yet marked as being written in 179 // the blocks array. These blocks that remain are either unchanged files or 180 // non-file-data blocks. We compare each of them to the old image, and 181 // compress the ones that changed into a single REPLACE_BZ operation. This 182 // updates a newly created node in the graph to write these blocks and writes 183 // the appropriate blob to blobs_fd. Reads and updates blobs_length. 184 static bool ReadUnwrittenBlocks( 185 const std::vector<Block>& blocks, 186 int blobs_fd, 187 off_t* blobs_length, 188 const std::string& old_image_path, 189 const std::string& new_image_path, 190 Vertex* vertex); 191 192 // Stores all Extents in 'extents' into 'out'. 193 static void StoreExtents(const std::vector<Extent>& extents, 194 google::protobuf::RepeatedPtrField<Extent>* out); 195 196 // Install operations in the manifest may reference data blobs, which 197 // are in data_blobs_path. This function creates a new data blobs file 198 // with the data blobs in the same order as the referencing install 199 // operations in the manifest. E.g. if manifest[0] has a data blob 200 // "X" at offset 1, manifest[1] has a data blob "Y" at offset 0, 201 // and data_blobs_path's file contains "YX", new_data_blobs_path 202 // will set to be a file that contains "XY". 203 static bool ReorderDataBlobs(DeltaArchiveManifest* manifest, 204 const std::string& data_blobs_path, 205 const std::string& new_data_blobs_path); 206 207 // Computes a SHA256 hash of the given buf and sets the hash value in the 208 // operation so that update_engine could verify. This hash should be set 209 // for all operations that have a non-zero data blob. One exception is the 210 // dummy operation for signature blob because the contents of the signature 211 // blob will not be available at payload creation time. So, update_engine will 212 // gracefully ignore the dummy signature operation. 213 static bool AddOperationHash(DeltaArchiveManifest_InstallOperation* op, 214 const chromeos::Blob& buf); 215 216 217 // Returns true if |op| is a no-op operation that doesn't do any useful work 218 // (e.g., a move operation that copies blocks onto themselves). 219 static bool IsNoopOperation(const DeltaArchiveManifest_InstallOperation& op); 220 221 static bool InitializePartitionInfo(bool is_kernel, 222 const std::string& partition, 223 PartitionInfo* info); 224 225 // Runs the bsdiff tool on two files and returns the resulting delta in 226 // |out|. Returns true on success. 227 static bool BsdiffFiles(const std::string& old_file, 228 const std::string& new_file, 229 chromeos::Blob* out); 230 231 // Adds to |manifest| a dummy operation that points to a signature blob 232 // located at the specified offset/length. 233 static void AddSignatureOp(uint64_t signature_blob_offset, 234 uint64_t signature_blob_length, 235 DeltaArchiveManifest* manifest); 236 237 // Takes |graph| and returns a vertex order with the vertex indices of 238 // |graph|, in the order they appear. Returns |true| on success. 239 static std::vector<Vertex::Index> OrderIndices(const Graph& graph); 240 241 // Takes a collection (vector or RepeatedPtrField) of Extent and 242 // returns a vector of the blocks referenced, in order. 243 template<typename T> 244 static std::vector<uint64_t> ExpandExtents(const T& extents) { 245 std::vector<uint64_t> ret; 246 for (size_t i = 0, e = static_cast<size_t>(extents.size()); i != e; ++i) { 247 const Extent extent = graph_utils::GetElement(extents, i); 248 if (extent.start_block() == kSparseHole) { 249 ret.resize(ret.size() + extent.num_blocks(), kSparseHole); 250 } else { 251 for (uint64_t block = extent.start_block(); 252 block < (extent.start_block() + extent.num_blocks()); block++) { 253 ret.push_back(block); 254 } 255 } 256 } 257 return ret; 258 } 259 260 static void CheckGraph(const Graph& graph); 261 262 private: 263 // This should never be constructed. 264 DISALLOW_IMPLICIT_CONSTRUCTORS(DeltaDiffGenerator); 265}; 266 267}; // namespace chromeos_update_engine 268 269#endif // UPDATE_ENGINE_PAYLOAD_GENERATOR_DELTA_DIFF_GENERATOR_H_ 270