delta_diff_utils.cc 45 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109
  1. //
  2. // Copyright (C) 2015 The Android Open Source Project
  3. //
  4. // Licensed under the Apache License, Version 2.0 (the "License");
  5. // you may not use this file except in compliance with the License.
  6. // You may obtain a copy of the License at
  7. //
  8. // http://www.apache.org/licenses/LICENSE-2.0
  9. //
  10. // Unless required by applicable law or agreed to in writing, software
  11. // distributed under the License is distributed on an "AS IS" BASIS,
  12. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. // See the License for the specific language governing permissions and
  14. // limitations under the License.
  15. //
  16. #include "update_engine/payload_generator/delta_diff_utils.h"
  17. #include <endian.h>
  18. #if defined(__clang__)
  19. // TODO(*): Remove these pragmas when b/35721782 is fixed.
  20. #pragma clang diagnostic push
  21. #pragma clang diagnostic ignored "-Wmacro-redefined"
  22. #endif
  23. #include <ext2fs/ext2fs.h>
  24. #if defined(__clang__)
  25. #pragma clang diagnostic pop
  26. #endif
  27. #include <unistd.h>
  28. #include <algorithm>
  29. #include <functional>
  30. #include <list>
  31. #include <map>
  32. #include <memory>
  33. #include <numeric>
  34. #include <utility>
  35. #include <base/files/file_util.h>
  36. #include <base/format_macros.h>
  37. #include <base/strings/string_util.h>
  38. #include <base/strings/stringprintf.h>
  39. #include <base/threading/simple_thread.h>
  40. #include <base/time/time.h>
  41. #include <brillo/data_encoding.h>
  42. #include <bsdiff/bsdiff.h>
  43. #include <bsdiff/patch_writer_factory.h>
  44. #include <puffin/utils.h>
  45. #include "update_engine/common/hash_calculator.h"
  46. #include "update_engine/common/subprocess.h"
  47. #include "update_engine/common/utils.h"
  48. #include "update_engine/payload_consumer/payload_constants.h"
  49. #include "update_engine/payload_generator/ab_generator.h"
  50. #include "update_engine/payload_generator/block_mapping.h"
  51. #include "update_engine/payload_generator/bzip.h"
  52. #include "update_engine/payload_generator/deflate_utils.h"
  53. #include "update_engine/payload_generator/delta_diff_generator.h"
  54. #include "update_engine/payload_generator/extent_ranges.h"
  55. #include "update_engine/payload_generator/extent_utils.h"
  56. #include "update_engine/payload_generator/squashfs_filesystem.h"
  57. #include "update_engine/payload_generator/xz.h"
  58. using std::list;
  59. using std::map;
  60. using std::string;
  61. using std::vector;
  62. namespace chromeos_update_engine {
  63. namespace {
  64. // The maximum destination size allowed for bsdiff. In general, bsdiff should
  65. // work for arbitrary big files, but the payload generation and payload
  66. // application requires a significant amount of RAM. We put a hard-limit of
  67. // 200 MiB that should not affect any released board, but will limit the
  68. // Chrome binary in ASan builders.
  69. const uint64_t kMaxBsdiffDestinationSize = 200 * 1024 * 1024; // bytes
  70. // The maximum destination size allowed for puffdiff. In general, puffdiff
  71. // should work for arbitrary big files, but the payload application is quite
  72. // memory intensive, so we limit these operations to 150 MiB.
  73. const uint64_t kMaxPuffdiffDestinationSize = 150 * 1024 * 1024; // bytes
  74. const int kBrotliCompressionQuality = 11;
  75. // Process a range of blocks from |range_start| to |range_end| in the extent at
  76. // position |*idx_p| of |extents|. If |do_remove| is true, this range will be
  77. // removed, which may cause the extent to be trimmed, split or removed entirely.
  78. // The value of |*idx_p| is updated to point to the next extent to be processed.
  79. // Returns true iff the next extent to process is a new or updated one.
  80. bool ProcessExtentBlockRange(vector<Extent>* extents,
  81. size_t* idx_p,
  82. const bool do_remove,
  83. uint64_t range_start,
  84. uint64_t range_end) {
  85. size_t idx = *idx_p;
  86. uint64_t start_block = (*extents)[idx].start_block();
  87. uint64_t num_blocks = (*extents)[idx].num_blocks();
  88. uint64_t range_size = range_end - range_start;
  89. if (do_remove) {
  90. if (range_size == num_blocks) {
  91. // Remove the entire extent.
  92. extents->erase(extents->begin() + idx);
  93. } else if (range_end == num_blocks) {
  94. // Trim the end of the extent.
  95. (*extents)[idx].set_num_blocks(num_blocks - range_size);
  96. idx++;
  97. } else if (range_start == 0) {
  98. // Trim the head of the extent.
  99. (*extents)[idx].set_start_block(start_block + range_size);
  100. (*extents)[idx].set_num_blocks(num_blocks - range_size);
  101. } else {
  102. // Trim the middle, splitting the remainder into two parts.
  103. (*extents)[idx].set_num_blocks(range_start);
  104. Extent e;
  105. e.set_start_block(start_block + range_end);
  106. e.set_num_blocks(num_blocks - range_end);
  107. idx++;
  108. extents->insert(extents->begin() + idx, e);
  109. }
  110. } else if (range_end == num_blocks) {
  111. // Done with this extent.
  112. idx++;
  113. } else {
  114. return false;
  115. }
  116. *idx_p = idx;
  117. return true;
  118. }
  119. // Remove identical corresponding block ranges in |src_extents| and
  120. // |dst_extents|. Used for preventing moving of blocks onto themselves during
  121. // MOVE operations. The value of |total_bytes| indicates the actual length of
  122. // content; this may be slightly less than the total size of blocks, in which
  123. // case the last block is only partly occupied with data. Returns the total
  124. // number of bytes removed.
  125. size_t RemoveIdenticalBlockRanges(vector<Extent>* src_extents,
  126. vector<Extent>* dst_extents,
  127. const size_t total_bytes) {
  128. size_t src_idx = 0;
  129. size_t dst_idx = 0;
  130. uint64_t src_offset = 0, dst_offset = 0;
  131. size_t removed_bytes = 0, nonfull_block_bytes;
  132. bool do_remove = false;
  133. while (src_idx < src_extents->size() && dst_idx < dst_extents->size()) {
  134. do_remove = ((*src_extents)[src_idx].start_block() + src_offset ==
  135. (*dst_extents)[dst_idx].start_block() + dst_offset);
  136. uint64_t src_num_blocks = (*src_extents)[src_idx].num_blocks();
  137. uint64_t dst_num_blocks = (*dst_extents)[dst_idx].num_blocks();
  138. uint64_t min_num_blocks =
  139. std::min(src_num_blocks - src_offset, dst_num_blocks - dst_offset);
  140. uint64_t prev_src_offset = src_offset;
  141. uint64_t prev_dst_offset = dst_offset;
  142. src_offset += min_num_blocks;
  143. dst_offset += min_num_blocks;
  144. bool new_src = ProcessExtentBlockRange(
  145. src_extents, &src_idx, do_remove, prev_src_offset, src_offset);
  146. bool new_dst = ProcessExtentBlockRange(
  147. dst_extents, &dst_idx, do_remove, prev_dst_offset, dst_offset);
  148. if (new_src) {
  149. src_offset = 0;
  150. }
  151. if (new_dst) {
  152. dst_offset = 0;
  153. }
  154. if (do_remove)
  155. removed_bytes += min_num_blocks * kBlockSize;
  156. }
  157. // If we removed the last block and this block is only partly used by file
  158. // content, deduct the unused portion from the total removed byte count.
  159. if (do_remove && (nonfull_block_bytes = total_bytes % kBlockSize))
  160. removed_bytes -= kBlockSize - nonfull_block_bytes;
  161. return removed_bytes;
  162. }
  163. // Storing a diff operation has more overhead over replace operation in the
  164. // manifest, we need to store an additional src_sha256_hash which is 32 bytes
  165. // and not compressible, and also src_extents which could use anywhere from a
  166. // few bytes to hundreds of bytes depending on the number of extents.
  167. // This function evaluates the overhead tradeoff and determines if it's worth to
  168. // use a diff operation with data blob of |diff_size| and |num_src_extents|
  169. // extents over an existing |op| with data blob of |old_blob_size|.
  170. bool IsDiffOperationBetter(const InstallOperation& op,
  171. size_t old_blob_size,
  172. size_t diff_size,
  173. size_t num_src_extents) {
  174. if (!diff_utils::IsAReplaceOperation(op.type()))
  175. return diff_size < old_blob_size;
  176. // Reference: https://developers.google.com/protocol-buffers/docs/encoding
  177. // For |src_sha256_hash| we need 1 byte field number/type, 1 byte size and 32
  178. // bytes data, for |src_extents| we need 1 byte field number/type and 1 byte
  179. // size.
  180. constexpr size_t kDiffOverhead = 1 + 1 + 32 + 1 + 1;
  181. // Each extent has two variable length encoded uint64, here we use a rough
  182. // estimate of 6 bytes overhead per extent, since |num_blocks| is usually
  183. // very small.
  184. constexpr size_t kDiffOverheadPerExtent = 6;
  185. return diff_size + kDiffOverhead + num_src_extents * kDiffOverheadPerExtent <
  186. old_blob_size;
  187. }
  188. // Returns the levenshtein distance between string |a| and |b|.
  189. // https://en.wikipedia.org/wiki/Levenshtein_distance
  190. int LevenshteinDistance(const string& a, const string& b) {
  191. vector<int> distances(a.size() + 1);
  192. std::iota(distances.begin(), distances.end(), 0);
  193. for (size_t i = 1; i <= b.size(); i++) {
  194. distances[0] = i;
  195. int previous_distance = i - 1;
  196. for (size_t j = 1; j <= a.size(); j++) {
  197. int new_distance =
  198. std::min({distances[j] + 1,
  199. distances[j - 1] + 1,
  200. previous_distance + (a[j - 1] == b[i - 1] ? 0 : 1)});
  201. previous_distance = distances[j];
  202. distances[j] = new_distance;
  203. }
  204. }
  205. return distances.back();
  206. }
  207. } // namespace
  208. namespace diff_utils {
  209. // This class encapsulates a file delta processing thread work. The
  210. // processor computes the delta between the source and target files;
  211. // and write the compressed delta to the blob.
  212. class FileDeltaProcessor : public base::DelegateSimpleThread::Delegate {
  213. public:
  214. FileDeltaProcessor(const string& old_part,
  215. const string& new_part,
  216. const PayloadVersion& version,
  217. const vector<Extent>& old_extents,
  218. const vector<Extent>& new_extents,
  219. const vector<puffin::BitExtent>& old_deflates,
  220. const vector<puffin::BitExtent>& new_deflates,
  221. const string& name,
  222. ssize_t chunk_blocks,
  223. BlobFileWriter* blob_file)
  224. : old_part_(old_part),
  225. new_part_(new_part),
  226. version_(version),
  227. old_extents_(old_extents),
  228. new_extents_(new_extents),
  229. new_extents_blocks_(utils::BlocksInExtents(new_extents)),
  230. old_deflates_(old_deflates),
  231. new_deflates_(new_deflates),
  232. name_(name),
  233. chunk_blocks_(chunk_blocks),
  234. blob_file_(blob_file) {}
  235. bool operator>(const FileDeltaProcessor& other) const {
  236. return new_extents_blocks_ > other.new_extents_blocks_;
  237. }
  238. ~FileDeltaProcessor() override = default;
  239. // Overrides DelegateSimpleThread::Delegate.
  240. // Calculate the list of operations and write their corresponding deltas to
  241. // the blob_file.
  242. void Run() override;
  243. // Merge each file processor's ops list to aops.
  244. bool MergeOperation(vector<AnnotatedOperation>* aops);
  245. private:
  246. const string& old_part_; // NOLINT(runtime/member_string_references)
  247. const string& new_part_; // NOLINT(runtime/member_string_references)
  248. const PayloadVersion& version_;
  249. // The block ranges of the old/new file within the src/tgt image
  250. const vector<Extent> old_extents_;
  251. const vector<Extent> new_extents_;
  252. const size_t new_extents_blocks_;
  253. const vector<puffin::BitExtent> old_deflates_;
  254. const vector<puffin::BitExtent> new_deflates_;
  255. const string name_;
  256. // Block limit of one aop.
  257. const ssize_t chunk_blocks_;
  258. BlobFileWriter* blob_file_;
  259. // The list of ops to reach the new file from the old file.
  260. vector<AnnotatedOperation> file_aops_;
  261. bool failed_ = false;
  262. DISALLOW_COPY_AND_ASSIGN(FileDeltaProcessor);
  263. };
  264. void FileDeltaProcessor::Run() {
  265. TEST_AND_RETURN(blob_file_ != nullptr);
  266. base::TimeTicks start = base::TimeTicks::Now();
  267. if (!DeltaReadFile(&file_aops_,
  268. old_part_,
  269. new_part_,
  270. old_extents_,
  271. new_extents_,
  272. old_deflates_,
  273. new_deflates_,
  274. name_,
  275. chunk_blocks_,
  276. version_,
  277. blob_file_)) {
  278. LOG(ERROR) << "Failed to generate delta for " << name_ << " ("
  279. << new_extents_blocks_ << " blocks)";
  280. failed_ = true;
  281. return;
  282. }
  283. if (!version_.InplaceUpdate()) {
  284. if (!ABGenerator::FragmentOperations(
  285. version_, &file_aops_, new_part_, blob_file_)) {
  286. LOG(ERROR) << "Failed to fragment operations for " << name_;
  287. failed_ = true;
  288. return;
  289. }
  290. }
  291. LOG(INFO) << "Encoded file " << name_ << " (" << new_extents_blocks_
  292. << " blocks) in " << (base::TimeTicks::Now() - start);
  293. }
  294. bool FileDeltaProcessor::MergeOperation(vector<AnnotatedOperation>* aops) {
  295. if (failed_)
  296. return false;
  297. aops->reserve(aops->size() + file_aops_.size());
  298. std::move(file_aops_.begin(), file_aops_.end(), std::back_inserter(*aops));
  299. return true;
  300. }
  301. FilesystemInterface::File GetOldFile(
  302. const map<string, FilesystemInterface::File>& old_files_map,
  303. const string& new_file_name) {
  304. if (old_files_map.empty())
  305. return {};
  306. auto old_file_iter = old_files_map.find(new_file_name);
  307. if (old_file_iter != old_files_map.end())
  308. return old_file_iter->second;
  309. // No old file match for the new file name, use a similar file with the
  310. // shortest levenshtein distance.
  311. // This works great if the file has version number in it, but even for
  312. // a completely new file, using a similar file can still help.
  313. int min_distance = new_file_name.size();
  314. const FilesystemInterface::File* old_file;
  315. for (const auto& pair : old_files_map) {
  316. int distance = LevenshteinDistance(new_file_name, pair.first);
  317. if (distance < min_distance) {
  318. min_distance = distance;
  319. old_file = &pair.second;
  320. }
  321. }
  322. LOG(INFO) << "Using " << old_file->name << " as source for " << new_file_name;
  323. return *old_file;
  324. }
  325. bool DeltaReadPartition(vector<AnnotatedOperation>* aops,
  326. const PartitionConfig& old_part,
  327. const PartitionConfig& new_part,
  328. ssize_t hard_chunk_blocks,
  329. size_t soft_chunk_blocks,
  330. const PayloadVersion& version,
  331. BlobFileWriter* blob_file) {
  332. ExtentRanges old_visited_blocks;
  333. ExtentRanges new_visited_blocks;
  334. // If verity is enabled, mark those blocks as visited to skip generating
  335. // operations for them.
  336. if (version.minor >= kVerityMinorPayloadVersion &&
  337. !new_part.verity.IsEmpty()) {
  338. LOG(INFO) << "Skipping verity hash tree blocks: "
  339. << ExtentsToString({new_part.verity.hash_tree_extent});
  340. new_visited_blocks.AddExtent(new_part.verity.hash_tree_extent);
  341. LOG(INFO) << "Skipping verity FEC blocks: "
  342. << ExtentsToString({new_part.verity.fec_extent});
  343. new_visited_blocks.AddExtent(new_part.verity.fec_extent);
  344. }
  345. ExtentRanges old_zero_blocks;
  346. TEST_AND_RETURN_FALSE(DeltaMovedAndZeroBlocks(aops,
  347. old_part.path,
  348. new_part.path,
  349. old_part.size / kBlockSize,
  350. new_part.size / kBlockSize,
  351. soft_chunk_blocks,
  352. version,
  353. blob_file,
  354. &old_visited_blocks,
  355. &new_visited_blocks,
  356. &old_zero_blocks));
  357. bool puffdiff_allowed = version.OperationAllowed(InstallOperation::PUFFDIFF);
  358. map<string, FilesystemInterface::File> old_files_map;
  359. if (old_part.fs_interface) {
  360. vector<FilesystemInterface::File> old_files;
  361. TEST_AND_RETURN_FALSE(deflate_utils::PreprocessPartitionFiles(
  362. old_part, &old_files, puffdiff_allowed));
  363. for (const FilesystemInterface::File& file : old_files)
  364. old_files_map[file.name] = file;
  365. }
  366. TEST_AND_RETURN_FALSE(new_part.fs_interface);
  367. vector<FilesystemInterface::File> new_files;
  368. TEST_AND_RETURN_FALSE(deflate_utils::PreprocessPartitionFiles(
  369. new_part, &new_files, puffdiff_allowed));
  370. list<FileDeltaProcessor> file_delta_processors;
  371. // The processing is very straightforward here, we generate operations for
  372. // every file (and pseudo-file such as the metadata) in the new filesystem
  373. // based on the file with the same name in the old filesystem, if any.
  374. // Files with overlapping data blocks (like hardlinks or filesystems with tail
  375. // packing or compression where the blocks store more than one file) are only
  376. // generated once in the new image, but are also used only once from the old
  377. // image due to some simplifications (see below).
  378. for (const FilesystemInterface::File& new_file : new_files) {
  379. // Ignore the files in the new filesystem without blocks. Symlinks with
  380. // data blocks (for example, symlinks bigger than 60 bytes in ext2) are
  381. // handled as normal files. We also ignore blocks that were already
  382. // processed by a previous file.
  383. vector<Extent> new_file_extents =
  384. FilterExtentRanges(new_file.extents, new_visited_blocks);
  385. new_visited_blocks.AddExtents(new_file_extents);
  386. if (new_file_extents.empty())
  387. continue;
  388. // We can't visit each dst image inode more than once, as that would
  389. // duplicate work. Here, we avoid visiting each source image inode
  390. // more than once. Technically, we could have multiple operations
  391. // that read the same blocks from the source image for diffing, but
  392. // we choose not to avoid complexity. Eventually we will move away
  393. // from using a graph/cycle detection/etc to generate diffs, and at that
  394. // time, it will be easy (non-complex) to have many operations read
  395. // from the same source blocks. At that time, this code can die. -adlr
  396. FilesystemInterface::File old_file =
  397. GetOldFile(old_files_map, new_file.name);
  398. vector<Extent> old_file_extents;
  399. if (version.InplaceUpdate())
  400. old_file_extents =
  401. FilterExtentRanges(old_file.extents, old_visited_blocks);
  402. else
  403. old_file_extents = FilterExtentRanges(old_file.extents, old_zero_blocks);
  404. old_visited_blocks.AddExtents(old_file_extents);
  405. file_delta_processors.emplace_back(old_part.path,
  406. new_part.path,
  407. version,
  408. std::move(old_file_extents),
  409. std::move(new_file_extents),
  410. old_file.deflates,
  411. new_file.deflates,
  412. new_file.name, // operation name
  413. hard_chunk_blocks,
  414. blob_file);
  415. }
  416. // Process all the blocks not included in any file. We provided all the unused
  417. // blocks in the old partition as available data.
  418. vector<Extent> new_unvisited = {
  419. ExtentForRange(0, new_part.size / kBlockSize)};
  420. new_unvisited = FilterExtentRanges(new_unvisited, new_visited_blocks);
  421. if (!new_unvisited.empty()) {
  422. vector<Extent> old_unvisited;
  423. if (old_part.fs_interface) {
  424. old_unvisited.push_back(ExtentForRange(0, old_part.size / kBlockSize));
  425. old_unvisited = FilterExtentRanges(old_unvisited, old_visited_blocks);
  426. }
  427. LOG(INFO) << "Scanning " << utils::BlocksInExtents(new_unvisited)
  428. << " unwritten blocks using chunk size of " << soft_chunk_blocks
  429. << " blocks.";
  430. // We use the soft_chunk_blocks limit for the <non-file-data> as we don't
  431. // really know the structure of this data and we should not expect it to
  432. // have redundancy between partitions.
  433. file_delta_processors.emplace_back(
  434. old_part.path,
  435. new_part.path,
  436. version,
  437. std::move(old_unvisited),
  438. std::move(new_unvisited),
  439. vector<puffin::BitExtent>{}, // old_deflates,
  440. vector<puffin::BitExtent>{}, // new_deflates
  441. "<non-file-data>", // operation name
  442. soft_chunk_blocks,
  443. blob_file);
  444. }
  445. size_t max_threads = GetMaxThreads();
  446. // Sort the files in descending order based on number of new blocks to make
  447. // sure we start the largest ones first.
  448. if (file_delta_processors.size() > max_threads) {
  449. file_delta_processors.sort(std::greater<FileDeltaProcessor>());
  450. }
  451. base::DelegateSimpleThreadPool thread_pool("incremental-update-generator",
  452. max_threads);
  453. thread_pool.Start();
  454. for (auto& processor : file_delta_processors) {
  455. thread_pool.AddWork(&processor);
  456. }
  457. thread_pool.JoinAll();
  458. for (auto& processor : file_delta_processors) {
  459. TEST_AND_RETURN_FALSE(processor.MergeOperation(aops));
  460. }
  461. return true;
  462. }
  463. bool DeltaMovedAndZeroBlocks(vector<AnnotatedOperation>* aops,
  464. const string& old_part,
  465. const string& new_part,
  466. size_t old_num_blocks,
  467. size_t new_num_blocks,
  468. ssize_t chunk_blocks,
  469. const PayloadVersion& version,
  470. BlobFileWriter* blob_file,
  471. ExtentRanges* old_visited_blocks,
  472. ExtentRanges* new_visited_blocks,
  473. ExtentRanges* old_zero_blocks) {
  474. vector<BlockMapping::BlockId> old_block_ids;
  475. vector<BlockMapping::BlockId> new_block_ids;
  476. TEST_AND_RETURN_FALSE(MapPartitionBlocks(old_part,
  477. new_part,
  478. old_num_blocks * kBlockSize,
  479. new_num_blocks * kBlockSize,
  480. kBlockSize,
  481. &old_block_ids,
  482. &new_block_ids));
  483. // If the update is inplace, we map all the blocks that didn't move,
  484. // regardless of the contents since they are already copied and no operation
  485. // is required.
  486. if (version.InplaceUpdate()) {
  487. uint64_t num_blocks = std::min(old_num_blocks, new_num_blocks);
  488. for (uint64_t block = 0; block < num_blocks; block++) {
  489. if (old_block_ids[block] == new_block_ids[block] &&
  490. !old_visited_blocks->ContainsBlock(block) &&
  491. !new_visited_blocks->ContainsBlock(block)) {
  492. old_visited_blocks->AddBlock(block);
  493. new_visited_blocks->AddBlock(block);
  494. }
  495. }
  496. }
  497. // A mapping from the block_id to the list of block numbers with that block id
  498. // in the old partition. This is used to lookup where in the old partition
  499. // is a block from the new partition.
  500. map<BlockMapping::BlockId, vector<uint64_t>> old_blocks_map;
  501. for (uint64_t block = old_num_blocks; block-- > 0;) {
  502. if (old_block_ids[block] != 0 && !old_visited_blocks->ContainsBlock(block))
  503. old_blocks_map[old_block_ids[block]].push_back(block);
  504. // Mark all zeroed blocks in the old image as "used" since it doesn't make
  505. // any sense to spend I/O to read zeros from the source partition and more
  506. // importantly, these could sometimes be blocks discarded in the SSD which
  507. // would read non-zero values.
  508. if (old_block_ids[block] == 0)
  509. old_zero_blocks->AddBlock(block);
  510. }
  511. old_visited_blocks->AddRanges(*old_zero_blocks);
  512. // The collection of blocks in the new partition with just zeros. This is a
  513. // common case for free-space that's also problematic for bsdiff, so we want
  514. // to optimize it using REPLACE_BZ operations. The blob for a REPLACE_BZ of
  515. // just zeros is so small that it doesn't make sense to spend the I/O reading
  516. // zeros from the old partition.
  517. vector<Extent> new_zeros;
  518. vector<Extent> old_identical_blocks;
  519. vector<Extent> new_identical_blocks;
  520. for (uint64_t block = 0; block < new_num_blocks; block++) {
  521. // Only produce operations for blocks that were not yet visited.
  522. if (new_visited_blocks->ContainsBlock(block))
  523. continue;
  524. if (new_block_ids[block] == 0) {
  525. AppendBlockToExtents(&new_zeros, block);
  526. continue;
  527. }
  528. auto old_blocks_map_it = old_blocks_map.find(new_block_ids[block]);
  529. // Check if the block exists in the old partition at all.
  530. if (old_blocks_map_it == old_blocks_map.end() ||
  531. old_blocks_map_it->second.empty())
  532. continue;
  533. AppendBlockToExtents(&old_identical_blocks,
  534. old_blocks_map_it->second.back());
  535. AppendBlockToExtents(&new_identical_blocks, block);
  536. // We can't reuse source blocks in minor version 1 because the cycle
  537. // breaking algorithm used in the in-place update doesn't support that.
  538. if (version.InplaceUpdate())
  539. old_blocks_map_it->second.pop_back();
  540. }
  541. if (chunk_blocks == -1)
  542. chunk_blocks = new_num_blocks;
  543. // Produce operations for the zero blocks split per output extent.
  544. size_t num_ops = aops->size();
  545. new_visited_blocks->AddExtents(new_zeros);
  546. for (const Extent& extent : new_zeros) {
  547. if (version.OperationAllowed(InstallOperation::ZERO)) {
  548. for (uint64_t offset = 0; offset < extent.num_blocks();
  549. offset += chunk_blocks) {
  550. uint64_t num_blocks =
  551. std::min(static_cast<uint64_t>(extent.num_blocks()) - offset,
  552. static_cast<uint64_t>(chunk_blocks));
  553. InstallOperation operation;
  554. operation.set_type(InstallOperation::ZERO);
  555. *(operation.add_dst_extents()) =
  556. ExtentForRange(extent.start_block() + offset, num_blocks);
  557. aops->push_back({.name = "<zeros>", .op = operation});
  558. }
  559. } else {
  560. TEST_AND_RETURN_FALSE(DeltaReadFile(aops,
  561. "",
  562. new_part,
  563. {}, // old_extents
  564. {extent}, // new_extents
  565. {}, // old_deflates
  566. {}, // new_deflates
  567. "<zeros>",
  568. chunk_blocks,
  569. version,
  570. blob_file));
  571. }
  572. }
  573. LOG(INFO) << "Produced " << (aops->size() - num_ops) << " operations for "
  574. << utils::BlocksInExtents(new_zeros) << " zeroed blocks";
  575. // Produce MOVE/SOURCE_COPY operations for the moved blocks.
  576. num_ops = aops->size();
  577. uint64_t used_blocks = 0;
  578. old_visited_blocks->AddExtents(old_identical_blocks);
  579. new_visited_blocks->AddExtents(new_identical_blocks);
  580. for (const Extent& extent : new_identical_blocks) {
  581. // We split the operation at the extent boundary or when bigger than
  582. // chunk_blocks.
  583. for (uint64_t op_block_offset = 0; op_block_offset < extent.num_blocks();
  584. op_block_offset += chunk_blocks) {
  585. aops->emplace_back();
  586. AnnotatedOperation* aop = &aops->back();
  587. aop->name = "<identical-blocks>";
  588. aop->op.set_type(version.OperationAllowed(InstallOperation::SOURCE_COPY)
  589. ? InstallOperation::SOURCE_COPY
  590. : InstallOperation::MOVE);
  591. uint64_t chunk_num_blocks =
  592. std::min(static_cast<uint64_t>(extent.num_blocks()) - op_block_offset,
  593. static_cast<uint64_t>(chunk_blocks));
  594. // The current operation represents the move/copy operation for the
  595. // sublist starting at |used_blocks| of length |chunk_num_blocks| where
  596. // the src and dst are from |old_identical_blocks| and
  597. // |new_identical_blocks| respectively.
  598. StoreExtents(
  599. ExtentsSublist(old_identical_blocks, used_blocks, chunk_num_blocks),
  600. aop->op.mutable_src_extents());
  601. Extent* op_dst_extent = aop->op.add_dst_extents();
  602. op_dst_extent->set_start_block(extent.start_block() + op_block_offset);
  603. op_dst_extent->set_num_blocks(chunk_num_blocks);
  604. CHECK(
  605. vector<Extent>{*op_dst_extent} == // NOLINT(whitespace/braces)
  606. ExtentsSublist(new_identical_blocks, used_blocks, chunk_num_blocks));
  607. used_blocks += chunk_num_blocks;
  608. }
  609. }
  610. LOG(INFO) << "Produced " << (aops->size() - num_ops) << " operations for "
  611. << used_blocks << " identical blocks moved";
  612. return true;
  613. }
  614. bool DeltaReadFile(vector<AnnotatedOperation>* aops,
  615. const string& old_part,
  616. const string& new_part,
  617. const vector<Extent>& old_extents,
  618. const vector<Extent>& new_extents,
  619. const vector<puffin::BitExtent>& old_deflates,
  620. const vector<puffin::BitExtent>& new_deflates,
  621. const string& name,
  622. ssize_t chunk_blocks,
  623. const PayloadVersion& version,
  624. BlobFileWriter* blob_file) {
  625. brillo::Blob data;
  626. InstallOperation operation;
  627. uint64_t total_blocks = utils::BlocksInExtents(new_extents);
  628. if (chunk_blocks == -1)
  629. chunk_blocks = total_blocks;
  630. for (uint64_t block_offset = 0; block_offset < total_blocks;
  631. block_offset += chunk_blocks) {
  632. // Split the old/new file in the same chunks. Note that this could drop
  633. // some information from the old file used for the new chunk. If the old
  634. // file is smaller (or even empty when there's no old file) the chunk will
  635. // also be empty.
  636. vector<Extent> old_extents_chunk =
  637. ExtentsSublist(old_extents, block_offset, chunk_blocks);
  638. vector<Extent> new_extents_chunk =
  639. ExtentsSublist(new_extents, block_offset, chunk_blocks);
  640. NormalizeExtents(&old_extents_chunk);
  641. NormalizeExtents(&new_extents_chunk);
  642. TEST_AND_RETURN_FALSE(ReadExtentsToDiff(old_part,
  643. new_part,
  644. old_extents_chunk,
  645. new_extents_chunk,
  646. old_deflates,
  647. new_deflates,
  648. version,
  649. &data,
  650. &operation));
  651. // Check if the operation writes nothing.
  652. if (operation.dst_extents_size() == 0) {
  653. if (operation.type() == InstallOperation::MOVE) {
  654. LOG(INFO) << "Empty MOVE operation (" << name << "), skipping";
  655. continue;
  656. } else {
  657. LOG(ERROR) << "Empty non-MOVE operation";
  658. return false;
  659. }
  660. }
  661. // Now, insert into the list of operations.
  662. AnnotatedOperation aop;
  663. aop.name = name;
  664. if (static_cast<uint64_t>(chunk_blocks) < total_blocks) {
  665. aop.name = base::StringPrintf(
  666. "%s:%" PRIu64, name.c_str(), block_offset / chunk_blocks);
  667. }
  668. aop.op = operation;
  669. // Write the data
  670. TEST_AND_RETURN_FALSE(aop.SetOperationBlob(data, blob_file));
  671. aops->emplace_back(aop);
  672. }
  673. return true;
  674. }
  675. bool GenerateBestFullOperation(const brillo::Blob& new_data,
  676. const PayloadVersion& version,
  677. brillo::Blob* out_blob,
  678. InstallOperation::Type* out_type) {
  679. if (new_data.empty())
  680. return false;
  681. if (version.OperationAllowed(InstallOperation::ZERO) &&
  682. std::all_of(
  683. new_data.begin(), new_data.end(), [](uint8_t x) { return x == 0; })) {
  684. // The read buffer is all zeros, so produce a ZERO operation. No need to
  685. // check other types of operations in this case.
  686. *out_blob = brillo::Blob();
  687. *out_type = InstallOperation::ZERO;
  688. return true;
  689. }
  690. bool out_blob_set = false;
  691. // Try compressing |new_data| with xz first.
  692. if (version.OperationAllowed(InstallOperation::REPLACE_XZ)) {
  693. brillo::Blob new_data_xz;
  694. if (XzCompress(new_data, &new_data_xz) && !new_data_xz.empty()) {
  695. *out_type = InstallOperation::REPLACE_XZ;
  696. *out_blob = std::move(new_data_xz);
  697. out_blob_set = true;
  698. }
  699. }
  700. // Try compressing it with bzip2.
  701. if (version.OperationAllowed(InstallOperation::REPLACE_BZ)) {
  702. brillo::Blob new_data_bz;
  703. // TODO(deymo): Implement some heuristic to determine if it is worth trying
  704. // to compress the blob with bzip2 if we already have a good REPLACE_XZ.
  705. if (BzipCompress(new_data, &new_data_bz) && !new_data_bz.empty() &&
  706. (!out_blob_set || out_blob->size() > new_data_bz.size())) {
  707. // A REPLACE_BZ is better or nothing else was set.
  708. *out_type = InstallOperation::REPLACE_BZ;
  709. *out_blob = std::move(new_data_bz);
  710. out_blob_set = true;
  711. }
  712. }
  713. // If nothing else worked or it was badly compressed we try a REPLACE.
  714. if (!out_blob_set || out_blob->size() >= new_data.size()) {
  715. *out_type = InstallOperation::REPLACE;
  716. // This needs to make a copy of the data in the case bzip or xz didn't
  717. // compress well, which is not the common case so the performance hit is
  718. // low.
  719. *out_blob = new_data;
  720. }
  721. return true;
  722. }
  723. bool ReadExtentsToDiff(const string& old_part,
  724. const string& new_part,
  725. const vector<Extent>& old_extents,
  726. const vector<Extent>& new_extents,
  727. const vector<puffin::BitExtent>& old_deflates,
  728. const vector<puffin::BitExtent>& new_deflates,
  729. const PayloadVersion& version,
  730. brillo::Blob* out_data,
  731. InstallOperation* out_op) {
  732. InstallOperation operation;
  733. // We read blocks from old_extents and write blocks to new_extents.
  734. uint64_t blocks_to_read = utils::BlocksInExtents(old_extents);
  735. uint64_t blocks_to_write = utils::BlocksInExtents(new_extents);
  736. // Disable bsdiff, and puffdiff when the data is too big.
  737. bool bsdiff_allowed =
  738. version.OperationAllowed(InstallOperation::SOURCE_BSDIFF) ||
  739. version.OperationAllowed(InstallOperation::BSDIFF);
  740. if (bsdiff_allowed &&
  741. blocks_to_read * kBlockSize > kMaxBsdiffDestinationSize) {
  742. LOG(INFO) << "bsdiff blacklisted, data too big: "
  743. << blocks_to_read * kBlockSize << " bytes";
  744. bsdiff_allowed = false;
  745. }
  746. bool puffdiff_allowed = version.OperationAllowed(InstallOperation::PUFFDIFF);
  747. if (puffdiff_allowed &&
  748. blocks_to_read * kBlockSize > kMaxPuffdiffDestinationSize) {
  749. LOG(INFO) << "puffdiff blacklisted, data too big: "
  750. << blocks_to_read * kBlockSize << " bytes";
  751. puffdiff_allowed = false;
  752. }
  753. // Make copies of the extents so we can modify them.
  754. vector<Extent> src_extents = old_extents;
  755. vector<Extent> dst_extents = new_extents;
  756. // Read in bytes from new data.
  757. brillo::Blob new_data;
  758. TEST_AND_RETURN_FALSE(utils::ReadExtents(new_part,
  759. new_extents,
  760. &new_data,
  761. kBlockSize * blocks_to_write,
  762. kBlockSize));
  763. TEST_AND_RETURN_FALSE(!new_data.empty());
  764. // Data blob that will be written to delta file.
  765. brillo::Blob data_blob;
  766. // Try generating a full operation for the given new data, regardless of the
  767. // old_data.
  768. InstallOperation::Type op_type;
  769. TEST_AND_RETURN_FALSE(
  770. GenerateBestFullOperation(new_data, version, &data_blob, &op_type));
  771. operation.set_type(op_type);
  772. brillo::Blob old_data;
  773. if (blocks_to_read > 0) {
  774. // Read old data.
  775. TEST_AND_RETURN_FALSE(utils::ReadExtents(old_part,
  776. src_extents,
  777. &old_data,
  778. kBlockSize * blocks_to_read,
  779. kBlockSize));
  780. if (old_data == new_data) {
  781. // No change in data.
  782. operation.set_type(version.OperationAllowed(InstallOperation::SOURCE_COPY)
  783. ? InstallOperation::SOURCE_COPY
  784. : InstallOperation::MOVE);
  785. data_blob = brillo::Blob();
  786. } else if (IsDiffOperationBetter(
  787. operation, data_blob.size(), 0, src_extents.size())) {
  788. // No point in trying diff if zero blob size diff operation is
  789. // still worse than replace.
  790. if (bsdiff_allowed) {
  791. base::FilePath patch;
  792. TEST_AND_RETURN_FALSE(base::CreateTemporaryFile(&patch));
  793. ScopedPathUnlinker unlinker(patch.value());
  794. std::unique_ptr<bsdiff::PatchWriterInterface> bsdiff_patch_writer;
  795. InstallOperation::Type operation_type = InstallOperation::BSDIFF;
  796. if (version.OperationAllowed(InstallOperation::BROTLI_BSDIFF)) {
  797. bsdiff_patch_writer =
  798. bsdiff::CreateBSDF2PatchWriter(patch.value(),
  799. bsdiff::CompressorType::kBrotli,
  800. kBrotliCompressionQuality);
  801. operation_type = InstallOperation::BROTLI_BSDIFF;
  802. } else {
  803. bsdiff_patch_writer = bsdiff::CreateBsdiffPatchWriter(patch.value());
  804. if (version.OperationAllowed(InstallOperation::SOURCE_BSDIFF)) {
  805. operation_type = InstallOperation::SOURCE_BSDIFF;
  806. }
  807. }
  808. brillo::Blob bsdiff_delta;
  809. TEST_AND_RETURN_FALSE(0 == bsdiff::bsdiff(old_data.data(),
  810. old_data.size(),
  811. new_data.data(),
  812. new_data.size(),
  813. bsdiff_patch_writer.get(),
  814. nullptr));
  815. TEST_AND_RETURN_FALSE(utils::ReadFile(patch.value(), &bsdiff_delta));
  816. CHECK_GT(bsdiff_delta.size(), static_cast<brillo::Blob::size_type>(0));
  817. if (IsDiffOperationBetter(operation,
  818. data_blob.size(),
  819. bsdiff_delta.size(),
  820. src_extents.size())) {
  821. operation.set_type(operation_type);
  822. data_blob = std::move(bsdiff_delta);
  823. }
  824. }
  825. if (puffdiff_allowed) {
  826. // Find all deflate positions inside the given extents and then put all
  827. // deflates together because we have already read all the extents into
  828. // one buffer.
  829. vector<puffin::BitExtent> src_deflates;
  830. TEST_AND_RETURN_FALSE(deflate_utils::FindAndCompactDeflates(
  831. src_extents, old_deflates, &src_deflates));
  832. vector<puffin::BitExtent> dst_deflates;
  833. TEST_AND_RETURN_FALSE(deflate_utils::FindAndCompactDeflates(
  834. dst_extents, new_deflates, &dst_deflates));
  835. puffin::RemoveEqualBitExtents(
  836. old_data, new_data, &src_deflates, &dst_deflates);
  837. // See crbug.com/915559.
  838. if (version.minor <= kPuffdiffMinorPayloadVersion) {
  839. TEST_AND_RETURN_FALSE(puffin::RemoveDeflatesWithBadDistanceCaches(
  840. old_data, &src_deflates));
  841. TEST_AND_RETURN_FALSE(puffin::RemoveDeflatesWithBadDistanceCaches(
  842. new_data, &dst_deflates));
  843. }
  844. // Only Puffdiff if both files have at least one deflate left.
  845. if (!src_deflates.empty() && !dst_deflates.empty()) {
  846. brillo::Blob puffdiff_delta;
  847. string temp_file_path;
  848. TEST_AND_RETURN_FALSE(utils::MakeTempFile(
  849. "puffdiff-delta.XXXXXX", &temp_file_path, nullptr));
  850. ScopedPathUnlinker temp_file_unlinker(temp_file_path);
  851. // Perform PuffDiff operation.
  852. TEST_AND_RETURN_FALSE(puffin::PuffDiff(old_data,
  853. new_data,
  854. src_deflates,
  855. dst_deflates,
  856. temp_file_path,
  857. &puffdiff_delta));
  858. TEST_AND_RETURN_FALSE(puffdiff_delta.size() > 0);
  859. if (IsDiffOperationBetter(operation,
  860. data_blob.size(),
  861. puffdiff_delta.size(),
  862. src_extents.size())) {
  863. operation.set_type(InstallOperation::PUFFDIFF);
  864. data_blob = std::move(puffdiff_delta);
  865. }
  866. }
  867. }
  868. }
  869. }
  870. // Remove identical src/dst block ranges in MOVE operations.
  871. if (operation.type() == InstallOperation::MOVE) {
  872. auto removed_bytes =
  873. RemoveIdenticalBlockRanges(&src_extents, &dst_extents, new_data.size());
  874. operation.set_src_length(old_data.size() - removed_bytes);
  875. operation.set_dst_length(new_data.size() - removed_bytes);
  876. }
  877. // WARNING: We always set legacy |src_length| and |dst_length| fields for
  878. // BSDIFF. For SOURCE_BSDIFF we only set them for minor version 3 and
  879. // lower. This is needed because we used to use these two parameters in the
  880. // SOURCE_BSDIFF for minor version 3 and lower, but we do not need them
  881. // anymore in higher minor versions. This means if we stop adding these
  882. // parameters for those minor versions, the delta payloads will be invalid.
  883. if (operation.type() == InstallOperation::BSDIFF ||
  884. (operation.type() == InstallOperation::SOURCE_BSDIFF &&
  885. version.minor <= kOpSrcHashMinorPayloadVersion)) {
  886. operation.set_src_length(old_data.size());
  887. operation.set_dst_length(new_data.size());
  888. }
  889. // Embed extents in the operation. Replace (all variants), zero and discard
  890. // operations should not have source extents.
  891. if (!IsNoSourceOperation(operation.type())) {
  892. StoreExtents(src_extents, operation.mutable_src_extents());
  893. }
  894. // All operations have dst_extents.
  895. StoreExtents(dst_extents, operation.mutable_dst_extents());
  896. *out_data = std::move(data_blob);
  897. *out_op = operation;
  898. return true;
  899. }
  900. bool IsAReplaceOperation(InstallOperation::Type op_type) {
  901. return (op_type == InstallOperation::REPLACE ||
  902. op_type == InstallOperation::REPLACE_BZ ||
  903. op_type == InstallOperation::REPLACE_XZ);
  904. }
  905. bool IsNoSourceOperation(InstallOperation::Type op_type) {
  906. return (IsAReplaceOperation(op_type) || op_type == InstallOperation::ZERO ||
  907. op_type == InstallOperation::DISCARD);
  908. }
  909. // Returns true if |op| is a no-op operation that doesn't do any useful work
  910. // (e.g., a move operation that copies blocks onto themselves).
  911. bool IsNoopOperation(const InstallOperation& op) {
  912. return (op.type() == InstallOperation::MOVE &&
  913. ExpandExtents(op.src_extents()) == ExpandExtents(op.dst_extents()));
  914. }
  915. void FilterNoopOperations(vector<AnnotatedOperation>* ops) {
  916. ops->erase(std::remove_if(ops->begin(),
  917. ops->end(),
  918. [](const AnnotatedOperation& aop) {
  919. return IsNoopOperation(aop.op);
  920. }),
  921. ops->end());
  922. }
  923. bool InitializePartitionInfo(const PartitionConfig& part, PartitionInfo* info) {
  924. info->set_size(part.size);
  925. HashCalculator hasher;
  926. TEST_AND_RETURN_FALSE(hasher.UpdateFile(part.path, part.size) ==
  927. static_cast<off_t>(part.size));
  928. TEST_AND_RETURN_FALSE(hasher.Finalize());
  929. const brillo::Blob& hash = hasher.raw_hash();
  930. info->set_hash(hash.data(), hash.size());
  931. LOG(INFO) << part.path << ": size=" << part.size
  932. << " hash=" << brillo::data_encoding::Base64Encode(hash);
  933. return true;
  934. }
  935. bool CompareAopsByDestination(AnnotatedOperation first_aop,
  936. AnnotatedOperation second_aop) {
  937. // We want empty operations to be at the end of the payload.
  938. if (!first_aop.op.dst_extents().size() || !second_aop.op.dst_extents().size())
  939. return ((!first_aop.op.dst_extents().size()) <
  940. (!second_aop.op.dst_extents().size()));
  941. uint32_t first_dst_start = first_aop.op.dst_extents(0).start_block();
  942. uint32_t second_dst_start = second_aop.op.dst_extents(0).start_block();
  943. return first_dst_start < second_dst_start;
  944. }
  945. bool IsExtFilesystem(const string& device) {
  946. brillo::Blob header;
  947. // See include/linux/ext2_fs.h for more details on the structure. We obtain
  948. // ext2 constants from ext2fs/ext2fs.h header but we don't link with the
  949. // library.
  950. if (!utils::ReadFileChunk(
  951. device, 0, SUPERBLOCK_OFFSET + SUPERBLOCK_SIZE, &header) ||
  952. header.size() < SUPERBLOCK_OFFSET + SUPERBLOCK_SIZE)
  953. return false;
  954. const uint8_t* superblock = header.data() + SUPERBLOCK_OFFSET;
  955. // ext3_fs.h: ext3_super_block.s_blocks_count
  956. uint32_t block_count =
  957. *reinterpret_cast<const uint32_t*>(superblock + 1 * sizeof(int32_t));
  958. // ext3_fs.h: ext3_super_block.s_log_block_size
  959. uint32_t log_block_size =
  960. *reinterpret_cast<const uint32_t*>(superblock + 6 * sizeof(int32_t));
  961. // ext3_fs.h: ext3_super_block.s_magic
  962. uint16_t magic =
  963. *reinterpret_cast<const uint16_t*>(superblock + 14 * sizeof(int32_t));
  964. block_count = le32toh(block_count);
  965. log_block_size = le32toh(log_block_size) + EXT2_MIN_BLOCK_LOG_SIZE;
  966. magic = le16toh(magic);
  967. if (magic != EXT2_SUPER_MAGIC)
  968. return false;
  969. // Sanity check the parameters.
  970. TEST_AND_RETURN_FALSE(log_block_size >= EXT2_MIN_BLOCK_LOG_SIZE &&
  971. log_block_size <= EXT2_MAX_BLOCK_LOG_SIZE);
  972. TEST_AND_RETURN_FALSE(block_count > 0);
  973. return true;
  974. }
  975. // Return the number of CPUs on the machine, and 4 threads in minimum.
  976. size_t GetMaxThreads() {
  977. return std::max(sysconf(_SC_NPROCESSORS_ONLN), 4L);
  978. }
  979. } // namespace diff_utils
  980. } // namespace chromeos_update_engine