| Alex Deymo | aea4c1c | 2015-08-19 20:24:43 -0700 | [diff] [blame] | 1 | // | 
 | 2 | // Copyright (C) 2010 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 | // | 
| Andrew de los Reyes | 5fdae4a | 2010-10-05 10:47:42 -0700 | [diff] [blame] | 16 |  | 
| Alex Deymo | 1beda78 | 2015-06-07 23:01:25 +0200 | [diff] [blame] | 17 | #include "update_engine/payload_generator/extent_ranges.h" | 
| Andrew de los Reyes | 5fdae4a | 2010-10-05 10:47:42 -0700 | [diff] [blame] | 18 |  | 
| Alex Vakulenko | d2779df | 2014-06-16 13:19:00 -0700 | [diff] [blame] | 19 | #include <algorithm> | 
| Andrew de los Reyes | 5fdae4a | 2010-10-05 10:47:42 -0700 | [diff] [blame] | 20 | #include <set> | 
 | 21 | #include <utility> | 
 | 22 | #include <vector> | 
 | 23 |  | 
 | 24 | #include <base/logging.h> | 
 | 25 |  | 
| Amin Hassani | d8b67f4 | 2017-12-06 13:47:52 -0800 | [diff] [blame] | 26 | #include "update_engine/common/utils.h" | 
| Alex Deymo | 39910dc | 2015-11-09 17:04:30 -0800 | [diff] [blame] | 27 | #include "update_engine/payload_consumer/payload_constants.h" | 
| Rahul Chaudhry | efbeecf | 2016-11-15 15:46:55 -0800 | [diff] [blame] | 28 | #include "update_engine/payload_generator/extent_utils.h" | 
| Alex Deymo | 161c4a1 | 2014-05-16 15:56:21 -0700 | [diff] [blame] | 29 |  | 
| Andrew de los Reyes | 5fdae4a | 2010-10-05 10:47:42 -0700 | [diff] [blame] | 30 | using std::set; | 
 | 31 | using std::vector; | 
 | 32 |  | 
 | 33 | namespace chromeos_update_engine { | 
 | 34 |  | 
 | 35 | bool ExtentRanges::ExtentsOverlapOrTouch(const Extent& a, const Extent& b) { | 
 | 36 |   if (a.start_block() == b.start_block()) | 
 | 37 |     return true; | 
| Darin Petkov | 94817cb | 2013-05-08 14:33:24 +0200 | [diff] [blame] | 38 |   if (a.start_block() == kSparseHole || b.start_block() == kSparseHole) | 
 | 39 |     return false; | 
| Andrew de los Reyes | 5fdae4a | 2010-10-05 10:47:42 -0700 | [diff] [blame] | 40 |   if (a.start_block() < b.start_block()) { | 
 | 41 |     return a.start_block() + a.num_blocks() >= b.start_block(); | 
 | 42 |   } else { | 
 | 43 |     return b.start_block() + b.num_blocks() >= a.start_block(); | 
 | 44 |   } | 
 | 45 | } | 
 | 46 |  | 
 | 47 | bool ExtentRanges::ExtentsOverlap(const Extent& a, const Extent& b) { | 
 | 48 |   if (a.start_block() == b.start_block()) | 
 | 49 |     return true; | 
| Darin Petkov | 94817cb | 2013-05-08 14:33:24 +0200 | [diff] [blame] | 50 |   if (a.start_block() == kSparseHole || b.start_block() == kSparseHole) | 
 | 51 |     return false; | 
| Andrew de los Reyes | 5fdae4a | 2010-10-05 10:47:42 -0700 | [diff] [blame] | 52 |   if (a.start_block() < b.start_block()) { | 
 | 53 |     return a.start_block() + a.num_blocks() > b.start_block(); | 
 | 54 |   } else { | 
 | 55 |     return b.start_block() + b.num_blocks() > a.start_block(); | 
 | 56 |   } | 
 | 57 | } | 
 | 58 |  | 
 | 59 | void ExtentRanges::AddBlock(uint64_t block) { | 
 | 60 |   AddExtent(ExtentForRange(block, 1)); | 
 | 61 | } | 
 | 62 |  | 
 | 63 | void ExtentRanges::SubtractBlock(uint64_t block) { | 
 | 64 |   SubtractExtent(ExtentForRange(block, 1)); | 
 | 65 | } | 
 | 66 |  | 
 | 67 | namespace { | 
 | 68 |  | 
 | 69 | Extent UnionOverlappingExtents(const Extent& first, const Extent& second) { | 
| Darin Petkov | 94817cb | 2013-05-08 14:33:24 +0200 | [diff] [blame] | 70 |   CHECK_NE(kSparseHole, first.start_block()); | 
 | 71 |   CHECK_NE(kSparseHole, second.start_block()); | 
| Alex Deymo | f329b93 | 2014-10-30 01:37:48 -0700 | [diff] [blame] | 72 |   uint64_t start = std::min(first.start_block(), second.start_block()); | 
 | 73 |   uint64_t end = std::max(first.start_block() + first.num_blocks(), | 
 | 74 |                           second.start_block() + second.num_blocks()); | 
| Andrew de los Reyes | 5fdae4a | 2010-10-05 10:47:42 -0700 | [diff] [blame] | 75 |   return ExtentForRange(start, end - start); | 
 | 76 | } | 
| Darin Petkov | 94817cb | 2013-05-08 14:33:24 +0200 | [diff] [blame] | 77 |  | 
| Alex Vakulenko | d2779df | 2014-06-16 13:19:00 -0700 | [diff] [blame] | 78 | }  // namespace | 
| Andrew de los Reyes | 5fdae4a | 2010-10-05 10:47:42 -0700 | [diff] [blame] | 79 |  | 
 | 80 | void ExtentRanges::AddExtent(Extent extent) { | 
| Darin Petkov | 94817cb | 2013-05-08 14:33:24 +0200 | [diff] [blame] | 81 |   if (extent.start_block() == kSparseHole || extent.num_blocks() == 0) | 
| Andrew de los Reyes | 5fdae4a | 2010-10-05 10:47:42 -0700 | [diff] [blame] | 82 |     return; | 
 | 83 |  | 
 | 84 |   ExtentSet::iterator begin_del = extent_set_.end(); | 
 | 85 |   ExtentSet::iterator end_del = extent_set_.end(); | 
 | 86 |   uint64_t del_blocks = 0; | 
 | 87 |   for (ExtentSet::iterator it = extent_set_.begin(), e = extent_set_.end(); | 
 | 88 |        it != e; ++it) { | 
 | 89 |     if (ExtentsOverlapOrTouch(*it, extent)) { | 
 | 90 |       end_del = it; | 
 | 91 |       ++end_del; | 
 | 92 |       del_blocks += it->num_blocks(); | 
 | 93 |       if (begin_del == extent_set_.end()) | 
 | 94 |         begin_del = it; | 
 | 95 |  | 
 | 96 |       extent = UnionOverlappingExtents(extent, *it); | 
 | 97 |     } | 
 | 98 |   } | 
 | 99 |   extent_set_.erase(begin_del, end_del); | 
 | 100 |   extent_set_.insert(extent); | 
 | 101 |   blocks_ -= del_blocks; | 
 | 102 |   blocks_ += extent.num_blocks(); | 
 | 103 | } | 
 | 104 |  | 
 | 105 | namespace { | 
 | 106 | // Returns base - subtractee (set subtraction). | 
 | 107 | ExtentRanges::ExtentSet SubtractOverlappingExtents(const Extent& base, | 
 | 108 |                                                    const Extent& subtractee) { | 
 | 109 |   ExtentRanges::ExtentSet ret; | 
 | 110 |   if (subtractee.start_block() > base.start_block()) { | 
 | 111 |     ret.insert(ExtentForRange(base.start_block(), | 
 | 112 |                               subtractee.start_block() - base.start_block())); | 
 | 113 |   } | 
 | 114 |   uint64_t base_end = base.start_block() + base.num_blocks(); | 
 | 115 |   uint64_t subtractee_end = subtractee.start_block() + subtractee.num_blocks(); | 
 | 116 |   if (base_end > subtractee_end) { | 
 | 117 |     ret.insert(ExtentForRange(subtractee_end, base_end - subtractee_end)); | 
 | 118 |   } | 
 | 119 |   return ret; | 
 | 120 | } | 
| Alex Vakulenko | d2779df | 2014-06-16 13:19:00 -0700 | [diff] [blame] | 121 | }  // namespace | 
| Andrew de los Reyes | 5fdae4a | 2010-10-05 10:47:42 -0700 | [diff] [blame] | 122 |  | 
 | 123 | void ExtentRanges::SubtractExtent(const Extent& extent) { | 
| Darin Petkov | 94817cb | 2013-05-08 14:33:24 +0200 | [diff] [blame] | 124 |   if (extent.start_block() == kSparseHole || extent.num_blocks() == 0) | 
| Andrew de los Reyes | 5fdae4a | 2010-10-05 10:47:42 -0700 | [diff] [blame] | 125 |     return; | 
 | 126 |  | 
 | 127 |   ExtentSet::iterator begin_del = extent_set_.end(); | 
 | 128 |   ExtentSet::iterator end_del = extent_set_.end(); | 
 | 129 |   uint64_t del_blocks = 0; | 
 | 130 |   ExtentSet new_extents; | 
 | 131 |   for (ExtentSet::iterator it = extent_set_.begin(), e = extent_set_.end(); | 
 | 132 |        it != e; ++it) { | 
 | 133 |     if (!ExtentsOverlap(*it, extent)) | 
 | 134 |       continue; | 
| Darin Petkov | 94817cb | 2013-05-08 14:33:24 +0200 | [diff] [blame] | 135 |  | 
| Andrew de los Reyes | 5fdae4a | 2010-10-05 10:47:42 -0700 | [diff] [blame] | 136 |     if (begin_del == extent_set_.end()) | 
 | 137 |       begin_del = it; | 
 | 138 |     end_del = it; | 
 | 139 |     ++end_del; | 
| Darin Petkov | 94817cb | 2013-05-08 14:33:24 +0200 | [diff] [blame] | 140 |  | 
| Andrew de los Reyes | 5fdae4a | 2010-10-05 10:47:42 -0700 | [diff] [blame] | 141 |     del_blocks += it->num_blocks(); | 
 | 142 |  | 
 | 143 |     ExtentSet subtraction = SubtractOverlappingExtents(*it, extent); | 
 | 144 |     for (ExtentSet::iterator jt = subtraction.begin(), je = subtraction.end(); | 
 | 145 |          jt != je; ++jt) { | 
 | 146 |       new_extents.insert(*jt); | 
 | 147 |       del_blocks -= jt->num_blocks(); | 
 | 148 |     } | 
 | 149 |   } | 
 | 150 |   extent_set_.erase(begin_del, end_del); | 
 | 151 |   extent_set_.insert(new_extents.begin(), new_extents.end()); | 
 | 152 |   blocks_ -= del_blocks; | 
 | 153 | } | 
 | 154 |  | 
 | 155 | void ExtentRanges::AddRanges(const ExtentRanges& ranges) { | 
 | 156 |   for (ExtentSet::const_iterator it = ranges.extent_set_.begin(), | 
 | 157 |            e = ranges.extent_set_.end(); it != e; ++it) { | 
 | 158 |     AddExtent(*it); | 
 | 159 |   } | 
 | 160 | } | 
 | 161 |  | 
 | 162 | void ExtentRanges::SubtractRanges(const ExtentRanges& ranges) { | 
 | 163 |   for (ExtentSet::const_iterator it = ranges.extent_set_.begin(), | 
 | 164 |            e = ranges.extent_set_.end(); it != e; ++it) { | 
 | 165 |     SubtractExtent(*it); | 
 | 166 |   } | 
 | 167 | } | 
 | 168 |  | 
 | 169 | void ExtentRanges::AddExtents(const vector<Extent>& extents) { | 
 | 170 |   for (vector<Extent>::const_iterator it = extents.begin(), e = extents.end(); | 
 | 171 |        it != e; ++it) { | 
 | 172 |     AddExtent(*it); | 
 | 173 |   } | 
 | 174 | } | 
 | 175 |  | 
 | 176 | void ExtentRanges::SubtractExtents(const vector<Extent>& extents) { | 
 | 177 |   for (vector<Extent>::const_iterator it = extents.begin(), e = extents.end(); | 
 | 178 |        it != e; ++it) { | 
 | 179 |     SubtractExtent(*it); | 
 | 180 |   } | 
 | 181 | } | 
 | 182 |  | 
 | 183 | void ExtentRanges::AddRepeatedExtents( | 
 | 184 |     const ::google::protobuf::RepeatedPtrField<Extent> &exts) { | 
 | 185 |   for (int i = 0, e = exts.size(); i != e; ++i) { | 
 | 186 |     AddExtent(exts.Get(i)); | 
 | 187 |   } | 
 | 188 | } | 
 | 189 |  | 
 | 190 | void ExtentRanges::SubtractRepeatedExtents( | 
 | 191 |     const ::google::protobuf::RepeatedPtrField<Extent> &exts) { | 
 | 192 |   for (int i = 0, e = exts.size(); i != e; ++i) { | 
 | 193 |     SubtractExtent(exts.Get(i)); | 
 | 194 |   } | 
 | 195 | } | 
 | 196 |  | 
| Alex Deymo | f006135 | 2015-07-01 14:59:15 -0700 | [diff] [blame] | 197 | bool ExtentRanges::ContainsBlock(uint64_t block) const { | 
 | 198 |   auto lower = extent_set_.lower_bound(ExtentForRange(block, 1)); | 
 | 199 |   // The block could be on the extent before the one in |lower|. | 
 | 200 |   if (lower != extent_set_.begin()) | 
 | 201 |     lower--; | 
 | 202 |   // Any extent starting at block+1 or later is not interesting, so this is the | 
 | 203 |   // upper limit. | 
 | 204 |   auto upper = extent_set_.lower_bound(ExtentForRange(block + 1, 0)); | 
 | 205 |   for (auto iter = lower; iter != upper; ++iter) { | 
 | 206 |     if (iter->start_block() <= block && | 
 | 207 |         block < iter->start_block() + iter->num_blocks()) { | 
 | 208 |       return true; | 
 | 209 |     } | 
 | 210 |   } | 
 | 211 |   return false; | 
 | 212 | } | 
 | 213 |  | 
| Andrew de los Reyes | 5fdae4a | 2010-10-05 10:47:42 -0700 | [diff] [blame] | 214 | void ExtentRanges::Dump() const { | 
 | 215 |   LOG(INFO) << "ExtentRanges Dump. blocks: " << blocks_; | 
 | 216 |   for (ExtentSet::const_iterator it = extent_set_.begin(), | 
 | 217 |            e = extent_set_.end(); | 
 | 218 |        it != e; ++it) { | 
 | 219 |     LOG(INFO) << "{" << it->start_block() << ", " << it->num_blocks() << "}"; | 
 | 220 |   } | 
 | 221 | } | 
 | 222 |  | 
 | 223 | Extent ExtentForRange(uint64_t start_block, uint64_t num_blocks) { | 
 | 224 |   Extent ret; | 
 | 225 |   ret.set_start_block(start_block); | 
 | 226 |   ret.set_num_blocks(num_blocks); | 
 | 227 |   return ret; | 
 | 228 | } | 
 | 229 |  | 
| Sen Jiang | 0a582fb | 2018-06-26 19:27:21 -0700 | [diff] [blame] | 230 | Extent ExtentForBytes(uint64_t block_size, | 
 | 231 |                       uint64_t start_bytes, | 
 | 232 |                       uint64_t size_bytes) { | 
 | 233 |   uint64_t start_block = start_bytes / block_size; | 
 | 234 |   uint64_t end_block = utils::DivRoundUp(start_bytes + size_bytes, block_size); | 
 | 235 |   return ExtentForRange(start_block, end_block - start_block); | 
 | 236 | } | 
 | 237 |  | 
| Alex Deymo | f329b93 | 2014-10-30 01:37:48 -0700 | [diff] [blame] | 238 | vector<Extent> ExtentRanges::GetExtentsForBlockCount( | 
| Andrew de los Reyes | 5fdae4a | 2010-10-05 10:47:42 -0700 | [diff] [blame] | 239 |     uint64_t count) const { | 
 | 240 |   vector<Extent> out; | 
 | 241 |   if (count == 0) | 
 | 242 |     return out; | 
 | 243 |   uint64_t out_blocks = 0; | 
 | 244 |   CHECK(count <= blocks_); | 
 | 245 |   for (ExtentSet::const_iterator it = extent_set_.begin(), | 
 | 246 |            e = extent_set_.end(); | 
 | 247 |        it != e; ++it) { | 
 | 248 |     const uint64_t blocks_needed = count - out_blocks; | 
 | 249 |     const Extent& extent = *it; | 
 | 250 |     out.push_back(extent); | 
 | 251 |     out_blocks += extent.num_blocks(); | 
 | 252 |     if (extent.num_blocks() < blocks_needed) | 
 | 253 |       continue; | 
 | 254 |     if (extent.num_blocks() == blocks_needed) | 
 | 255 |       break; | 
 | 256 |     // If we get here, we just added the last extent needed, but it's too big | 
 | 257 |     out_blocks -= extent.num_blocks(); | 
 | 258 |     out_blocks += blocks_needed; | 
 | 259 |     out.back().set_num_blocks(blocks_needed); | 
 | 260 |     break; | 
 | 261 |   } | 
| Amin Hassani | d8b67f4 | 2017-12-06 13:47:52 -0800 | [diff] [blame] | 262 |   CHECK(out_blocks == utils::BlocksInExtents(out)); | 
| Andrew de los Reyes | 5fdae4a | 2010-10-05 10:47:42 -0700 | [diff] [blame] | 263 |   return out; | 
 | 264 | } | 
 | 265 |  | 
| Alex Deymo | f006135 | 2015-07-01 14:59:15 -0700 | [diff] [blame] | 266 | vector<Extent> FilterExtentRanges(const vector<Extent>& extents, | 
| Alex Deymo | a376a6e | 2015-06-05 00:31:34 +0200 | [diff] [blame] | 267 |                                   const ExtentRanges& ranges) { | 
 | 268 |   vector<Extent> result; | 
 | 269 |   const ExtentRanges::ExtentSet& extent_set = ranges.extent_set(); | 
 | 270 |   for (Extent extent : extents) { | 
 | 271 |     // The extents are sorted by the start_block. We want to iterate all the | 
 | 272 |     // Extents in the ExtentSet possibly overlapping the current |extent|. This | 
 | 273 |     // is achieved by looking from the extent whose start_block is *lower* than | 
 | 274 |     // the extent.start_block() up to the greatest extent whose start_block is | 
 | 275 |     // lower than extent.start_block() + extent.num_blocks(). | 
 | 276 |     auto lower = extent_set.lower_bound(extent); | 
 | 277 |     // We need to decrement the lower_bound to look at the extent that could | 
 | 278 |     // overlap the beginning of the current |extent|. | 
 | 279 |     if (lower != extent_set.begin()) | 
 | 280 |       lower--; | 
 | 281 |     auto upper = extent_set.lower_bound( | 
 | 282 |         ExtentForRange(extent.start_block() + extent.num_blocks(), 0)); | 
 | 283 |     for (auto iter = lower; iter != upper; ++iter) { | 
 | 284 |       if (!ExtentRanges::ExtentsOverlap(extent, *iter)) | 
 | 285 |         continue; | 
 | 286 |       if (iter->start_block() <= extent.start_block()) { | 
 | 287 |         // We need to cut blocks from the beginning of the |extent|. | 
 | 288 |         uint64_t cut_blocks = iter->start_block() + iter->num_blocks() - | 
 | 289 |             extent.start_block(); | 
 | 290 |         if (cut_blocks >= extent.num_blocks()) { | 
 | 291 |           extent.set_num_blocks(0); | 
 | 292 |           break; | 
 | 293 |         } | 
 | 294 |         extent = ExtentForRange(extent.start_block() + cut_blocks, | 
 | 295 |                                 extent.num_blocks() - cut_blocks); | 
 | 296 |       } else { | 
 | 297 |         // We need to cut blocks on the middle of the extent, possible up to the | 
 | 298 |         // end of it. | 
 | 299 |         result.push_back( | 
 | 300 |             ExtentForRange(extent.start_block(), | 
 | 301 |                            iter->start_block() - extent.start_block())); | 
 | 302 |         uint64_t new_start = iter->start_block() + iter->num_blocks(); | 
 | 303 |         uint64_t old_end = extent.start_block() + extent.num_blocks(); | 
 | 304 |         if (new_start >= old_end) { | 
 | 305 |           extent.set_num_blocks(0); | 
 | 306 |           break; | 
 | 307 |         } | 
 | 308 |         extent = ExtentForRange(new_start, old_end - new_start); | 
 | 309 |       } | 
 | 310 |     } | 
 | 311 |     if (extent.num_blocks() > 0) | 
 | 312 |       result.push_back(extent); | 
 | 313 |   } | 
 | 314 |   return result; | 
 | 315 | } | 
 | 316 |  | 
| Andrew de los Reyes | 5fdae4a | 2010-10-05 10:47:42 -0700 | [diff] [blame] | 317 | }  // namespace chromeos_update_engine |