blob: 2dacfdf356ad7a60fa299ee11db7532c3119570b [file] [log] [blame]
adlr@google.com3defe6a2009-12-04 20:57:17 +00001// Copyright (c) 2009 The Chromium 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#include "update_engine/delta_diff_generator.h"
6#include <dirent.h>
7#include <endian.h>
8#include <errno.h>
9#include <stdio.h>
10#include <unistd.h>
11#include <algorithm>
Andrew de los Reyes4fe15d02009-12-10 19:01:36 -080012#include <map>
13#include <set>
14#include <string>
adlr@google.com3defe6a2009-12-04 20:57:17 +000015#include <vector>
16#include <tr1/memory>
17#include <zlib.h>
18#include "chromeos/obsolete_logging.h"
19#include "base/scoped_ptr.h"
20#include "update_engine/delta_diff_parser.h"
21#include "update_engine/gzip.h"
22#include "update_engine/subprocess.h"
23#include "update_engine/utils.h"
24
Andrew de los Reyes4fe15d02009-12-10 19:01:36 -080025using std::map;
26using std::set;
27using std::string;
adlr@google.com3defe6a2009-12-04 20:57:17 +000028using std::vector;
29using std::tr1::shared_ptr;
30using chromeos_update_engine::DeltaArchiveManifest;
31
32namespace chromeos_update_engine {
33
34namespace {
Andrew de los Reyes4fe15d02009-12-10 19:01:36 -080035
36const char* kBsdiffPath = "/usr/bin/bsdiff";
37
adlr@google.com3defe6a2009-12-04 20:57:17 +000038// These structs and methods are helpers for EncodeDataToDeltaFile()
39
40// Before moving the data into a proto buffer, the data is stored in
41// memory in these Node and Child structures.
42
43// Each Node struct represents a file on disk (which can be regular file,
44// directory, fifo, socket, symlink, etc). Nodes that contain children
45// (just directories) will have a vector of Child objects. Each child
46// object has a filename and a pointer to the associated Node. Thus,
47// filenames for files are stored with their parents, not as part of
48// the file itself.
49
50// These structures are easier to work with than the protobuf format
51// when adding files. When generating a delta file, we add an entry
52// for each file to a root Node object. Then, we sort each Node's
53// children vector so the children are stored alphabetically. Then,
54// we assign an index value to the idx field of each Node by a preorder
55// tree traversal. The index value assigned to a Node is the index it
56// will have in the DeltaArchiveManifest protobuf.
57// Finally, we add each Node to a DeltaArchiveManifest protobuf.
58
59struct Node;
60
61struct Child {
62 Child(const string& the_name,
63 Node* the_node)
64 : name(the_name),
65 node(the_node) {}
66 string name;
67 // Use shared_ptr here rather than scoped_ptr b/c this struct will be copied
68 // in stl containers
69 scoped_ptr<Node> node;
70};
71
72// For the C++ sort() function.
73struct ChildLessThan {
74 bool operator()(const shared_ptr<Child>& a, const shared_ptr<Child>& b) {
75 return a->name < b->name;
76 }
77};
78
79struct Node {
80 Node()
81 : mode(0),
82 uid(0),
83 gid(0),
Andrew de los Reyese5733992009-12-08 13:34:00 -080084 nlink(0),
85 inode(0),
adlr@google.com3defe6a2009-12-04 20:57:17 +000086 compressed(false),
87 offset(-1),
88 length(0),
89 idx(0) {}
90
91 mode_t mode;
92 uid_t uid;
93 gid_t gid;
94
Andrew de los Reyese5733992009-12-08 13:34:00 -080095 // a file may be a potential hardlink if it's not a directory
96 // and it has a link count > 1.
97 bool IsPotentialHardlink() const {
98 return !S_ISDIR(mode) && nlink > 1;
99 }
100 nlink_t nlink; // number of hard links
101 ino_t inode;
102
adlr@google.com3defe6a2009-12-04 20:57:17 +0000103 // data
104 bool compressed;
105 int offset; // -1 means no data
106 int length;
107
108 vector<shared_ptr<Child> > children;
109 int idx;
110};
111
112// This function sets *node's variables to match what's at path.
113// This includes calling this function recursively on all children. Children
114// not on the same device as the original node will not be considered.
115// Returns true on success.
116bool UpdateNodeFromPath(const string& path, Node* node) {
117 // Set metadata
118 struct stat stbuf;
119 TEST_AND_RETURN_FALSE_ERRNO(lstat(path.c_str(), &stbuf) == 0);
120 const dev_t dev = stbuf.st_dev;
121 node->mode = stbuf.st_mode;
122 node->uid = stbuf.st_uid;
123 node->gid = stbuf.st_gid;
Andrew de los Reyese5733992009-12-08 13:34:00 -0800124 node->nlink = stbuf.st_nlink;
125 node->inode = stbuf.st_ino;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000126 if (!S_ISDIR(node->mode)) {
127 return true;
128 }
129
130 DIR* dir = opendir(path.c_str());
131 TEST_AND_RETURN_FALSE(dir);
132
133 struct dirent entry;
134 struct dirent* dir_entry;
135
136 for (;;) {
137 TEST_AND_RETURN_FALSE_ERRNO(readdir_r(dir, &entry, &dir_entry) == 0);
138 if (!dir_entry) {
139 // done
140 break;
141 }
142 if (!strcmp(".", dir_entry->d_name))
143 continue;
144 if (!strcmp("..", dir_entry->d_name))
145 continue;
146
147 string child_path = path + "/" + dir_entry->d_name;
148 struct stat child_stbuf;
149 TEST_AND_RETURN_FALSE_ERRNO(lstat(child_path.c_str(), &child_stbuf) == 0);
150 // make sure it's on the same dev
151 if (child_stbuf.st_dev != dev)
152 continue;
153 shared_ptr<Child> child(new Child(dir_entry->d_name, new Node));
154 node->children.push_back(child);
155 TEST_AND_RETURN_FALSE(UpdateNodeFromPath(path + "/" + child->name,
156 child->node.get()));
157 }
158 TEST_AND_RETURN_FALSE_ERRNO(closedir(dir) == 0);
159 // Done with all subdirs. sort children.
160 sort(node->children.begin(), node->children.end(), ChildLessThan());
161 return true;
162}
163
164// We go through n setting the index value of each Node to
165// *next_index_value, then increment next_index_value.
166// We then recursively assign index values to children.
167// The first caller should call this with *next_index_value == 0 and
168// the root Node, thus setting the root Node's index to 0.
169void PopulateChildIndexes(Node* n, int* next_index_value) {
170 n->idx = (*next_index_value)++;
171 for (unsigned int i = 0; i < n->children.size(); i++) {
172 PopulateChildIndexes(n->children[i]->node.get(), next_index_value);
173 }
174}
175
176// This converts a Node tree rooted at n into a DeltaArchiveManifest.
Andrew de los Reyese5733992009-12-08 13:34:00 -0800177void NodeToDeltaArchiveManifest(Node* n, DeltaArchiveManifest* archive,
178 map<ino_t, string>* hard_links,
179 const string& path) {
adlr@google.com3defe6a2009-12-04 20:57:17 +0000180 DeltaArchiveManifest_File *f = archive->add_files();
181 f->set_mode(n->mode);
182 f->set_uid(n->uid);
183 f->set_gid(n->gid);
Andrew de los Reyese5733992009-12-08 13:34:00 -0800184 if (utils::MapContainsKey(*hard_links, n->inode)) {
185 // We have a hard link
186 CHECK(!S_ISDIR(n->mode));
187 f->set_hardlink_path((*hard_links)[n->inode]);
188 } else if (n->IsPotentialHardlink()) {
189 (*hard_links)[n->inode] = path;
190 }
adlr@google.com3defe6a2009-12-04 20:57:17 +0000191 if (!S_ISDIR(n->mode))
192 return;
193 for (unsigned int i = 0; i < n->children.size(); i++) {
194 DeltaArchiveManifest_File_Child* child = f->add_children();
195 child->set_name(n->children[i]->name);
196 child->set_index(n->children[i]->node->idx);
197 }
198 for (unsigned int i = 0; i < n->children.size(); i++) {
Andrew de los Reyese5733992009-12-08 13:34:00 -0800199 NodeToDeltaArchiveManifest(n->children[i]->node.get(), archive, hard_links,
200 path + "/" + n->children[i]->name);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000201 }
202}
203
204} // namespace {}
205
206// For each file in archive, write a delta for it into out_file
207// and update 'file' to refer to the delta.
208// This is a recursive function. Returns true on success.
209bool DeltaDiffGenerator::WriteFileDiffsToDeltaFile(
210 DeltaArchiveManifest* archive,
211 DeltaArchiveManifest_File* file,
Andrew de los Reyes4fe15d02009-12-10 19:01:36 -0800212 const string& file_name,
213 const string& old_path,
214 const string& new_path,
adlr@google.com3defe6a2009-12-04 20:57:17 +0000215 FileWriter* out_file_writer,
Andrew de los Reyese5733992009-12-08 13:34:00 -0800216 int* out_file_length,
Andrew de los Reyes4fe15d02009-12-10 19:01:36 -0800217 set<string> always_full_target_paths,
218 const string& force_compress_dev_path) {
adlr@google.com3defe6a2009-12-04 20:57:17 +0000219 TEST_AND_RETURN_FALSE(file->has_mode());
220
221 // Stat the actual file, too
222 struct stat stbuf;
223 TEST_AND_RETURN_FALSE_ERRNO(lstat((new_path + "/" + file_name).c_str(),
224 &stbuf) == 0);
225 TEST_AND_RETURN_FALSE(stbuf.st_mode == file->mode());
226
227 // See if we're a directory or not
228 if (S_ISDIR(file->mode())) {
229 for (int i = 0; i < file->children_size(); i++) {
230 DeltaArchiveManifest_File_Child* child = file->mutable_children(i);
231 DeltaArchiveManifest_File* child_file =
232 archive->mutable_files(child->index());
Andrew de los Reyes4fe15d02009-12-10 19:01:36 -0800233 string recurse_old_path = old_path;
234 string recurse_new_path = new_path;
235 if (!file_name.empty()) {
236 recurse_new_path += "/" + file_name;
237 recurse_old_path += "/" + file_name;
238 }
adlr@google.com3defe6a2009-12-04 20:57:17 +0000239 TEST_AND_RETURN_FALSE(WriteFileDiffsToDeltaFile(
240 archive,
241 child_file,
242 child->name(),
Andrew de los Reyes4fe15d02009-12-10 19:01:36 -0800243 recurse_old_path,
244 recurse_new_path,
adlr@google.com3defe6a2009-12-04 20:57:17 +0000245 out_file_writer,
Andrew de los Reyese5733992009-12-08 13:34:00 -0800246 out_file_length,
Andrew de los Reyes4fe15d02009-12-10 19:01:36 -0800247 always_full_target_paths,
Andrew de los Reyese5733992009-12-08 13:34:00 -0800248 force_compress_dev_path));
adlr@google.com3defe6a2009-12-04 20:57:17 +0000249 }
250 return true;
251 }
252
Andrew de los Reyese5733992009-12-08 13:34:00 -0800253 if (S_ISFIFO(file->mode()) || S_ISSOCK(file->mode()) ||
254 file->has_hardlink_path()) {
adlr@google.com3defe6a2009-12-04 20:57:17 +0000255 // These don't store any additional data
256 return true;
257 }
258
259 vector<char> data;
260 bool should_compress = true;
261 bool format_set = false;
262 DeltaArchiveManifest_File_DataFormat format;
263 if (S_ISLNK(file->mode())) {
adlr@google.com3defe6a2009-12-04 20:57:17 +0000264 TEST_AND_RETURN_FALSE(EncodeLink(new_path + "/" + file_name, &data));
265 } else if (S_ISCHR(file->mode()) || S_ISBLK(file->mode())) {
Andrew de los Reyese5733992009-12-08 13:34:00 -0800266 TEST_AND_RETURN_FALSE(EncodeDev(stbuf, &data, &format,
267 new_path + "/" + file_name ==
268 force_compress_dev_path));
269 format_set = true;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000270 } else if (S_ISREG(file->mode())) {
adlr@google.com3defe6a2009-12-04 20:57:17 +0000271 // regular file. We may use a delta here.
Andrew de los Reyes4fe15d02009-12-10 19:01:36 -0800272 const bool avoid_diff = utils::SetContainsKey(always_full_target_paths,
273 new_path + "/" + file_name);
274 bool no_change = false;
275 TEST_AND_RETURN_FALSE(EncodeFile(old_path, new_path, file_name,
276 avoid_diff, &format, &data, &no_change));
277 if (no_change) {
278 // No data change. We're done!
279 return true;
280 }
adlr@google.com3defe6a2009-12-04 20:57:17 +0000281 should_compress = false;
282 format_set = true;
283 if ((format == DeltaArchiveManifest_File_DataFormat_BSDIFF) ||
284 (format == DeltaArchiveManifest_File_DataFormat_FULL_GZ))
285 TEST_AND_RETURN_FALSE(!data.empty());
286 } else {
287 // Should never get here; unhandled mode type.
288 LOG(ERROR) << "Unhandled mode type: " << file->mode();
289 return false;
290 }
Andrew de los Reyese5733992009-12-08 13:34:00 -0800291
adlr@google.com3defe6a2009-12-04 20:57:17 +0000292 if (!format_set) {
293 // Pick a format now
294 vector<char> compressed_data;
295 TEST_AND_RETURN_FALSE(GzipCompress(data, &compressed_data));
296 if (compressed_data.size() < data.size()) {
297 format = DeltaArchiveManifest_File_DataFormat_FULL_GZ;
298 data.swap(compressed_data);
299 } else {
300 format = DeltaArchiveManifest_File_DataFormat_FULL;
301 }
302 format_set = true;
303 }
304
Andrew de los Reyes4fe15d02009-12-10 19:01:36 -0800305 TEST_AND_RETURN_FALSE(format_set);
306 file->set_data_format(format);
307 file->set_data_offset(*out_file_length);
308 TEST_AND_RETURN_FALSE(static_cast<ssize_t>(data.size()) ==
309 out_file_writer->Write(&data[0], data.size()));
310 file->set_data_length(data.size());
311 *out_file_length += data.size();
adlr@google.com3defe6a2009-12-04 20:57:17 +0000312 return true;
313}
314
Andrew de los Reyes4fe15d02009-12-10 19:01:36 -0800315bool DeltaDiffGenerator::EncodeLink(const string& path, vector<char>* out) {
adlr@google.com3defe6a2009-12-04 20:57:17 +0000316 // Store symlink path as file data
317 vector<char> link_data(4096);
318 int rc = readlink(path.c_str(), &link_data[0], link_data.size());
319 TEST_AND_RETURN_FALSE_ERRNO(rc >= 0);
320 link_data.resize(rc);
321 out->swap(link_data);
322 return true;
323}
324
Andrew de los Reyese5733992009-12-08 13:34:00 -0800325bool DeltaDiffGenerator::EncodeDev(
326 const struct stat& stbuf,
327 vector<char>* out,
328 DeltaArchiveManifest_File_DataFormat* format,
329 bool force_compression) {
adlr@google.com3defe6a2009-12-04 20:57:17 +0000330 LinuxDevice dev;
331 dev.set_major(major(stbuf.st_rdev));
332 dev.set_minor(minor(stbuf.st_rdev));
333 out->resize(dev.ByteSize());
334 TEST_AND_RETURN_FALSE(dev.SerializeToArray(&(*out)[0], out->size()));
Andrew de los Reyese5733992009-12-08 13:34:00 -0800335 if (force_compression) {
336 vector<char> compressed;
337 TEST_AND_RETURN_FALSE(GzipCompress(*out, &compressed));
338 out->swap(compressed);
339 *format = DeltaArchiveManifest_File_DataFormat_FULL_GZ;
340 } else {
341 *format = DeltaArchiveManifest_File_DataFormat_FULL;
342 }
adlr@google.com3defe6a2009-12-04 20:57:17 +0000343 return true;
344}
345
346// Encode the file at new_path + "/" + file_name. It may be a binary diff
347// based on old_path + "/" + file_name. out_data_format will be set to
348// the format used. out_data_format may not be NULL.
349bool DeltaDiffGenerator::EncodeFile(
350 const string& old_dir,
351 const string& new_dir,
352 const string& file_name,
Andrew de los Reyes4fe15d02009-12-10 19:01:36 -0800353 const bool avoid_diff,
adlr@google.com3defe6a2009-12-04 20:57:17 +0000354 DeltaArchiveManifest_File_DataFormat* out_data_format,
Andrew de los Reyes4fe15d02009-12-10 19:01:36 -0800355 vector<char>* out,
356 bool* no_change) {
adlr@google.com3defe6a2009-12-04 20:57:17 +0000357 TEST_AND_RETURN_FALSE(out_data_format);
Andrew de los Reyes4fe15d02009-12-10 19:01:36 -0800358 vector<char> ret;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000359 vector<char> full_data;
Andrew de los Reyes4fe15d02009-12-10 19:01:36 -0800360 {
361 // First, see the full length:
362 TEST_AND_RETURN_FALSE(utils::ReadFile(new_dir + "/" + file_name,
363 &full_data));
364 vector<char> gz_data;
365 if (!full_data.empty()) {
366 TEST_AND_RETURN_FALSE(GzipCompress(full_data, &gz_data));
367 }
adlr@google.com3defe6a2009-12-04 20:57:17 +0000368
Andrew de los Reyes4fe15d02009-12-10 19:01:36 -0800369 if (gz_data.size() < full_data.size()) {
370 *out_data_format = DeltaArchiveManifest_File_DataFormat_FULL_GZ;
371 ret.swap(gz_data);
372 } else {
373 *out_data_format = DeltaArchiveManifest_File_DataFormat_FULL;
374 ret = full_data;
375 }
376 }
377
378 if (avoid_diff) {
379 out->swap(ret);
380 return true;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000381 }
382
383 struct stat old_stbuf;
384 if ((stat((old_dir + "/" + file_name).c_str(), &old_stbuf) < 0) ||
385 (!S_ISREG(old_stbuf.st_mode))) {
Andrew de los Reyes4fe15d02009-12-10 19:01:36 -0800386 // stat() failed or old file is not a regular file. Just send back
387 // the full contents
388 out->swap(ret);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000389 return true;
390 }
Andrew de los Reyes4fe15d02009-12-10 19:01:36 -0800391 // We have an old file.
392 // First see if the data is _exactly_ the same
393 {
394 vector<char> original_data;
395 TEST_AND_RETURN_FALSE(utils::ReadFile(old_dir + "/" + file_name,
396 &original_data));
397 if (original_data == full_data) {
398 // Original data unchanged in new file.
399 *no_change = true;
400 return true;
401 }
402 }
403
404 // Do a binary diff. For now use bsdiff.
405 const string kPatchFile = "/tmp/delta.patchXXXXXX";
406 vector<char> patch_file_path(kPatchFile.begin(), kPatchFile.end());
407 patch_file_path.push_back('\0');
408
409 int fd = mkstemp(&patch_file_path[0]);
410 if (fd >= 0)
411 close(fd);
412 TEST_AND_RETURN_FALSE(fd != -1);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000413
414 vector<string> cmd;
Andrew de los Reyes4fe15d02009-12-10 19:01:36 -0800415 cmd.push_back(kBsdiffPath);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000416 cmd.push_back(old_dir + "/" + file_name);
417 cmd.push_back(new_dir + "/" + file_name);
Andrew de los Reyes4fe15d02009-12-10 19:01:36 -0800418 cmd.push_back(&patch_file_path[0]);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000419
420 int rc = 1;
Andrew de los Reyes4fe15d02009-12-10 19:01:36 -0800421 vector<char> patch_file;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000422 TEST_AND_RETURN_FALSE(Subprocess::SynchronousExec(cmd, &rc));
423 TEST_AND_RETURN_FALSE(rc == 0);
Andrew de los Reyes4fe15d02009-12-10 19:01:36 -0800424 TEST_AND_RETURN_FALSE(utils::ReadFile(&patch_file_path[0], &patch_file));
425 unlink(&patch_file_path[0]);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000426
Andrew de los Reyes4fe15d02009-12-10 19:01:36 -0800427 if (patch_file.size() < ret.size()) {
adlr@google.com3defe6a2009-12-04 20:57:17 +0000428 *out_data_format = DeltaArchiveManifest_File_DataFormat_BSDIFF;
Andrew de los Reyes4fe15d02009-12-10 19:01:36 -0800429 ret.swap(patch_file);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000430 }
Andrew de los Reyes4fe15d02009-12-10 19:01:36 -0800431 out->swap(ret);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000432 return true;
433}
434
435DeltaArchiveManifest* DeltaDiffGenerator::EncodeMetadataToProtoBuffer(
436 const char* new_path) {
437 Node node;
438 if (!UpdateNodeFromPath(new_path, &node))
439 return NULL;
440 int index = 0;
441 PopulateChildIndexes(&node, &index);
442 DeltaArchiveManifest *ret = new DeltaArchiveManifest;
Andrew de los Reyese5733992009-12-08 13:34:00 -0800443 map<ino_t, string> hard_links; // inode -> first found path for inode
444 NodeToDeltaArchiveManifest(&node, ret, &hard_links, "");
adlr@google.com3defe6a2009-12-04 20:57:17 +0000445 return ret;
446}
447
Andrew de los Reyese5733992009-12-08 13:34:00 -0800448bool DeltaDiffGenerator::EncodeDataToDeltaFile(
449 DeltaArchiveManifest* archive,
Andrew de los Reyes4fe15d02009-12-10 19:01:36 -0800450 const string& old_path,
451 const string& new_path,
452 const string& out_file,
453 const set<string>& nondiff_paths,
454 const string& force_compress_dev_path) {
adlr@google.com3defe6a2009-12-04 20:57:17 +0000455 DirectFileWriter out_writer;
456 int r = out_writer.Open(out_file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666);
457 TEST_AND_RETURN_FALSE_ERRNO(r >= 0);
458 ScopedFileWriterCloser closer(&out_writer);
459 TEST_AND_RETURN_FALSE(out_writer.Write(DeltaDiffParser::kFileMagic,
460 strlen(DeltaDiffParser::kFileMagic))
461 == static_cast<ssize_t>(
462 strlen(DeltaDiffParser::kFileMagic)));
463 // Write 8 null bytes. This will be filled in w/ the offset of
464 // the protobuf.
465 TEST_AND_RETURN_FALSE(out_writer.Write("\0\0\0\0\0\0\0\0", 8) == 8);
466 // 8 more bytes will be filled w/ the protobuf length.
467 TEST_AND_RETURN_FALSE(out_writer.Write("\0\0\0\0\0\0\0\0", 8) == 8);
468 int out_file_length = strlen(DeltaDiffParser::kFileMagic) + 16;
469
470 TEST_AND_RETURN_FALSE(archive->files_size() > 0);
471 DeltaArchiveManifest_File* file = archive->mutable_files(0);
472
Andrew de los Reyes4fe15d02009-12-10 19:01:36 -0800473 // nondiff_paths is passed in w/ paths relative to the installed
474 // system (e.g. /etc/fstab), but WriteFileDiffsToDeltaFile requires them
475 // to be the entire path of the new file. We create a new set
476 // here with nondiff_paths expanded.
477 set<string> always_full_target_paths;
478 for (set<string>::const_iterator it = nondiff_paths.begin();
479 it != nondiff_paths.end(); ++it) {
480 always_full_target_paths.insert(new_path + *it);
481 }
482
adlr@google.com3defe6a2009-12-04 20:57:17 +0000483 TEST_AND_RETURN_FALSE(WriteFileDiffsToDeltaFile(archive,
484 file,
485 "",
486 old_path,
487 new_path,
488 &out_writer,
Andrew de los Reyese5733992009-12-08 13:34:00 -0800489 &out_file_length,
Andrew de los Reyes4fe15d02009-12-10 19:01:36 -0800490 always_full_target_paths,
Andrew de los Reyese5733992009-12-08 13:34:00 -0800491 force_compress_dev_path));
adlr@google.com3defe6a2009-12-04 20:57:17 +0000492
493 // Finally, write the protobuf to the end of the file
494 string encoded_archive;
495 TEST_AND_RETURN_FALSE(archive->SerializeToString(&encoded_archive));
496
497 // Compress the protobuf (which contains filenames)
498 vector<char> compressed_encoded_archive;
499 TEST_AND_RETURN_FALSE(GzipCompressString(encoded_archive,
500 &compressed_encoded_archive));
501
502 TEST_AND_RETURN_FALSE(out_writer.Write(compressed_encoded_archive.data(),
503 compressed_encoded_archive.size()) ==
504 static_cast<ssize_t>(
505 compressed_encoded_archive.size()));
506
507 // write offset of protobut to just after the file magic
508 int64 big_endian_protobuf_offset = htobe64(out_file_length);
509 TEST_AND_RETURN_FALSE(pwrite(out_writer.fd(),
510 &big_endian_protobuf_offset,
511 sizeof(big_endian_protobuf_offset),
512 strlen(DeltaDiffParser::kFileMagic)) ==
513 sizeof(big_endian_protobuf_offset));
514 // Write the size just after the offset
515 int64 pb_length = htobe64(compressed_encoded_archive.size());
516 TEST_AND_RETURN_FALSE(pwrite(out_writer.fd(),
517 &pb_length,
518 sizeof(pb_length),
519 strlen(DeltaDiffParser::kFileMagic) +
520 sizeof(big_endian_protobuf_offset)) ==
521 sizeof(pb_length));
522 return true;
523}
524
525} // namespace chromeos_update_engine