Index: src/platform/update_engine/delta_diff_generator.cc |
diff --git a/src/platform/update_engine/delta_diff_generator.cc b/src/platform/update_engine/delta_diff_generator.cc |
index 45872bc99a3c5c9551ed953d19a989a5a7962197..2dacfdf356ad7a60fa299ee11db7532c3119570b 100644 |
--- a/src/platform/update_engine/delta_diff_generator.cc |
+++ b/src/platform/update_engine/delta_diff_generator.cc |
@@ -9,6 +9,9 @@ |
#include <stdio.h> |
#include <unistd.h> |
#include <algorithm> |
+#include <map> |
+#include <set> |
+#include <string> |
#include <vector> |
#include <tr1/memory> |
#include <zlib.h> |
@@ -19,6 +22,9 @@ |
#include "update_engine/subprocess.h" |
#include "update_engine/utils.h" |
+using std::map; |
+using std::set; |
+using std::string; |
using std::vector; |
using std::tr1::shared_ptr; |
using chromeos_update_engine::DeltaArchiveManifest; |
@@ -26,6 +32,9 @@ using chromeos_update_engine::DeltaArchiveManifest; |
namespace chromeos_update_engine { |
namespace { |
+ |
+const char* kBsdiffPath = "/usr/bin/bsdiff"; |
+ |
// These structs and methods are helpers for EncodeDataToDeltaFile() |
// Before moving the data into a proto buffer, the data is stored in |
@@ -200,12 +209,13 @@ void NodeToDeltaArchiveManifest(Node* n, DeltaArchiveManifest* archive, |
bool DeltaDiffGenerator::WriteFileDiffsToDeltaFile( |
DeltaArchiveManifest* archive, |
DeltaArchiveManifest_File* file, |
- const std::string& file_name, |
- const std::string& old_path, |
- const std::string& new_path, |
+ const string& file_name, |
+ const string& old_path, |
+ const string& new_path, |
FileWriter* out_file_writer, |
int* out_file_length, |
- const std::string& force_compress_dev_path) { |
+ set<string> always_full_target_paths, |
+ const string& force_compress_dev_path) { |
TEST_AND_RETURN_FALSE(file->has_mode()); |
// Stat the actual file, too |
@@ -220,14 +230,21 @@ bool DeltaDiffGenerator::WriteFileDiffsToDeltaFile( |
DeltaArchiveManifest_File_Child* child = file->mutable_children(i); |
DeltaArchiveManifest_File* child_file = |
archive->mutable_files(child->index()); |
+ string recurse_old_path = old_path; |
+ string recurse_new_path = new_path; |
+ if (!file_name.empty()) { |
+ recurse_new_path += "/" + file_name; |
+ recurse_old_path += "/" + file_name; |
+ } |
TEST_AND_RETURN_FALSE(WriteFileDiffsToDeltaFile( |
archive, |
child_file, |
child->name(), |
- old_path + "/" + file_name, |
- new_path + "/" + file_name, |
+ recurse_old_path, |
+ recurse_new_path, |
out_file_writer, |
out_file_length, |
+ always_full_target_paths, |
force_compress_dev_path)); |
} |
return true; |
@@ -252,8 +269,15 @@ bool DeltaDiffGenerator::WriteFileDiffsToDeltaFile( |
format_set = true; |
} else if (S_ISREG(file->mode())) { |
// regular file. We may use a delta here. |
- TEST_AND_RETURN_FALSE(EncodeFile(old_path, new_path, file_name, &format, |
- &data)); |
+ const bool avoid_diff = utils::SetContainsKey(always_full_target_paths, |
+ new_path + "/" + file_name); |
+ bool no_change = false; |
+ TEST_AND_RETURN_FALSE(EncodeFile(old_path, new_path, file_name, |
+ avoid_diff, &format, &data, &no_change)); |
+ if (no_change) { |
+ // No data change. We're done! |
+ return true; |
+ } |
should_compress = false; |
format_set = true; |
if ((format == DeltaArchiveManifest_File_DataFormat_BSDIFF) || |
@@ -278,20 +302,17 @@ bool DeltaDiffGenerator::WriteFileDiffsToDeltaFile( |
format_set = true; |
} |
- if (!data.empty()) { |
- TEST_AND_RETURN_FALSE(format_set); |
- file->set_data_format(format); |
- file->set_data_offset(*out_file_length); |
- TEST_AND_RETURN_FALSE(static_cast<ssize_t>(data.size()) == |
- out_file_writer->Write(&data[0], data.size())); |
- file->set_data_length(data.size()); |
- *out_file_length += data.size(); |
- } |
+ TEST_AND_RETURN_FALSE(format_set); |
+ file->set_data_format(format); |
+ file->set_data_offset(*out_file_length); |
+ TEST_AND_RETURN_FALSE(static_cast<ssize_t>(data.size()) == |
+ out_file_writer->Write(&data[0], data.size())); |
+ file->set_data_length(data.size()); |
+ *out_file_length += data.size(); |
return true; |
} |
-bool DeltaDiffGenerator::EncodeLink(const std::string& path, |
- std::vector<char>* out) { |
+bool DeltaDiffGenerator::EncodeLink(const string& path, vector<char>* out) { |
// Store symlink path as file data |
vector<char> link_data(4096); |
int rc = readlink(path.c_str(), &link_data[0], link_data.size()); |
@@ -329,56 +350,85 @@ bool DeltaDiffGenerator::EncodeFile( |
const string& old_dir, |
const string& new_dir, |
const string& file_name, |
+ const bool avoid_diff, |
DeltaArchiveManifest_File_DataFormat* out_data_format, |
- vector<char>* out) { |
+ vector<char>* out, |
+ bool* no_change) { |
TEST_AND_RETURN_FALSE(out_data_format); |
- // First, see the full length: |
+ vector<char> ret; |
vector<char> full_data; |
- TEST_AND_RETURN_FALSE(utils::ReadFile(new_dir + "/" + file_name, &full_data)); |
- vector<char> gz_data; |
- if (!full_data.empty()) { |
- TEST_AND_RETURN_FALSE(GzipCompress(full_data, &gz_data)); |
+ { |
+ // First, see the full length: |
+ TEST_AND_RETURN_FALSE(utils::ReadFile(new_dir + "/" + file_name, |
+ &full_data)); |
+ vector<char> gz_data; |
+ if (!full_data.empty()) { |
+ TEST_AND_RETURN_FALSE(GzipCompress(full_data, &gz_data)); |
+ } |
+ |
+ if (gz_data.size() < full_data.size()) { |
+ *out_data_format = DeltaArchiveManifest_File_DataFormat_FULL_GZ; |
+ ret.swap(gz_data); |
+ } else { |
+ *out_data_format = DeltaArchiveManifest_File_DataFormat_FULL; |
+ ret = full_data; |
+ } |
} |
- vector<char> *ret = NULL; |
- if (gz_data.size() < full_data.size()) { |
- *out_data_format = DeltaArchiveManifest_File_DataFormat_FULL_GZ; |
- ret = &gz_data; |
- } else { |
- *out_data_format = DeltaArchiveManifest_File_DataFormat_FULL; |
- ret = &full_data; |
+ if (avoid_diff) { |
+ out->swap(ret); |
+ return true; |
} |
struct stat old_stbuf; |
if ((stat((old_dir + "/" + file_name).c_str(), &old_stbuf) < 0) || |
(!S_ISREG(old_stbuf.st_mode))) { |
- // stat() failed or old file is not a regular file. Just send back the full |
- // contents |
- *out = *ret; |
+ // stat() failed or old file is not a regular file. Just send back |
+ // the full contents |
+ out->swap(ret); |
return true; |
} |
- // We have an old file. Do a binary diff. For now use bsdiff. |
- const string kPatchFile = "/tmp/delta.patch"; |
+ // We have an old file. |
+ // First see if the data is _exactly_ the same |
+ { |
+ vector<char> original_data; |
+ TEST_AND_RETURN_FALSE(utils::ReadFile(old_dir + "/" + file_name, |
+ &original_data)); |
+ if (original_data == full_data) { |
+ // Original data unchanged in new file. |
+ *no_change = true; |
+ return true; |
+ } |
+ } |
+ |
+ // Do a binary diff. For now use bsdiff. |
+ const string kPatchFile = "/tmp/delta.patchXXXXXX"; |
+ vector<char> patch_file_path(kPatchFile.begin(), kPatchFile.end()); |
+ patch_file_path.push_back('\0'); |
+ |
+ int fd = mkstemp(&patch_file_path[0]); |
+ if (fd >= 0) |
+ close(fd); |
+ TEST_AND_RETURN_FALSE(fd != -1); |
vector<string> cmd; |
- cmd.push_back("/usr/bin/bsdiff"); |
+ cmd.push_back(kBsdiffPath); |
cmd.push_back(old_dir + "/" + file_name); |
cmd.push_back(new_dir + "/" + file_name); |
- cmd.push_back(kPatchFile); |
+ cmd.push_back(&patch_file_path[0]); |
int rc = 1; |
+ vector<char> patch_file; |
TEST_AND_RETURN_FALSE(Subprocess::SynchronousExec(cmd, &rc)); |
TEST_AND_RETURN_FALSE(rc == 0); |
- vector<char> patch_file; |
- TEST_AND_RETURN_FALSE(utils::ReadFile(kPatchFile, &patch_file)); |
- unlink(kPatchFile.c_str()); |
+ TEST_AND_RETURN_FALSE(utils::ReadFile(&patch_file_path[0], &patch_file)); |
+ unlink(&patch_file_path[0]); |
- if (patch_file.size() < ret->size()) { |
+ if (patch_file.size() < ret.size()) { |
*out_data_format = DeltaArchiveManifest_File_DataFormat_BSDIFF; |
- ret = &patch_file; |
+ ret.swap(patch_file); |
} |
- |
- *out = *ret; |
+ out->swap(ret); |
return true; |
} |
@@ -397,10 +447,11 @@ DeltaArchiveManifest* DeltaDiffGenerator::EncodeMetadataToProtoBuffer( |
bool DeltaDiffGenerator::EncodeDataToDeltaFile( |
DeltaArchiveManifest* archive, |
- const std::string& old_path, |
- const std::string& new_path, |
- const std::string& out_file, |
- const std::string& force_compress_dev_path) { |
+ const string& old_path, |
+ const string& new_path, |
+ const string& out_file, |
+ const set<string>& nondiff_paths, |
+ const string& force_compress_dev_path) { |
DirectFileWriter out_writer; |
int r = out_writer.Open(out_file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666); |
TEST_AND_RETURN_FALSE_ERRNO(r >= 0); |
@@ -419,6 +470,16 @@ bool DeltaDiffGenerator::EncodeDataToDeltaFile( |
TEST_AND_RETURN_FALSE(archive->files_size() > 0); |
DeltaArchiveManifest_File* file = archive->mutable_files(0); |
+ // nondiff_paths is passed in w/ paths relative to the installed |
+ // system (e.g. /etc/fstab), but WriteFileDiffsToDeltaFile requires them |
+ // to be the entire path of the new file. We create a new set |
+ // here with nondiff_paths expanded. |
+ set<string> always_full_target_paths; |
+ for (set<string>::const_iterator it = nondiff_paths.begin(); |
+ it != nondiff_paths.end(); ++it) { |
+ always_full_target_paths.insert(new_path + *it); |
+ } |
+ |
TEST_AND_RETURN_FALSE(WriteFileDiffsToDeltaFile(archive, |
file, |
"", |
@@ -426,6 +487,7 @@ bool DeltaDiffGenerator::EncodeDataToDeltaFile( |
new_path, |
&out_writer, |
&out_file_length, |
+ always_full_target_paths, |
force_compress_dev_path)); |
// Finally, write the protobuf to the end of the file |