Merge "Change the internal representation in RangeSet." am: 7ffa8be41d
am: 2e12242b7a
am: 7f4e5d55f4
am: b126a2cd9a
Change-Id: Iabb1641aec17d535d740ae1275351e7befdf1432
This commit is contained in:
commit
c2b3d700b4
3 changed files with 328 additions and 249 deletions
|
@ -25,14 +25,15 @@
|
||||||
|
|
||||||
TEST(RangeSetTest, Parse_smoke) {
|
TEST(RangeSetTest, Parse_smoke) {
|
||||||
RangeSet rs = RangeSet::Parse("2,1,10");
|
RangeSet rs = RangeSet::Parse("2,1,10");
|
||||||
ASSERT_EQ(static_cast<size_t>(1), rs.count);
|
ASSERT_EQ(static_cast<size_t>(1), rs.size());
|
||||||
ASSERT_EQ((std::vector<size_t>{ 1, 10 }), rs.pos);
|
ASSERT_EQ((Range{ 1, 10 }), rs[0]);
|
||||||
ASSERT_EQ(static_cast<size_t>(9), rs.size);
|
ASSERT_EQ(static_cast<size_t>(9), rs.blocks());
|
||||||
|
|
||||||
RangeSet rs2 = RangeSet::Parse("4,15,20,1,10");
|
RangeSet rs2 = RangeSet::Parse("4,15,20,1,10");
|
||||||
ASSERT_EQ(static_cast<size_t>(2), rs2.count);
|
ASSERT_EQ(static_cast<size_t>(2), rs2.size());
|
||||||
ASSERT_EQ((std::vector<size_t>{ 15, 20, 1, 10 }), rs2.pos);
|
ASSERT_EQ((Range{ 15, 20 }), rs2[0]);
|
||||||
ASSERT_EQ(static_cast<size_t>(14), rs2.size);
|
ASSERT_EQ((Range{ 1, 10 }), rs2[1]);
|
||||||
|
ASSERT_EQ(static_cast<size_t>(14), rs2.blocks());
|
||||||
|
|
||||||
// Leading zeros are fine. But android::base::ParseUint() doesn't like trailing zeros like "10 ".
|
// Leading zeros are fine. But android::base::ParseUint() doesn't like trailing zeros like "10 ".
|
||||||
ASSERT_EQ(rs, RangeSet::Parse(" 2, 1, 10"));
|
ASSERT_EQ(rs, RangeSet::Parse(" 2, 1, 10"));
|
||||||
|
@ -82,3 +83,30 @@ TEST(RangeSetTest, GetBlockNumber) {
|
||||||
// Out of bound.
|
// Out of bound.
|
||||||
ASSERT_EXIT(rs.GetBlockNumber(9), ::testing::KilledBySignal(SIGABRT), "");
|
ASSERT_EXIT(rs.GetBlockNumber(9), ::testing::KilledBySignal(SIGABRT), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(RangeSetTest, equality) {
|
||||||
|
ASSERT_EQ(RangeSet::Parse("2,1,6"), RangeSet::Parse("2,1,6"));
|
||||||
|
|
||||||
|
ASSERT_NE(RangeSet::Parse("2,1,6"), RangeSet::Parse("2,1,7"));
|
||||||
|
ASSERT_NE(RangeSet::Parse("2,1,6"), RangeSet::Parse("2,2,7"));
|
||||||
|
|
||||||
|
// The orders of Range's matter. "4,1,5,8,10" != "4,8,10,1,5".
|
||||||
|
ASSERT_NE(RangeSet::Parse("4,1,5,8,10"), RangeSet::Parse("4,8,10,1,5"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(RangeSetTest, iterators) {
|
||||||
|
RangeSet rs = RangeSet::Parse("4,1,5,8,10");
|
||||||
|
std::vector<Range> ranges;
|
||||||
|
for (const auto& range : rs) {
|
||||||
|
ranges.push_back(range);
|
||||||
|
}
|
||||||
|
ASSERT_EQ((std::vector<Range>{ Range{ 1, 5 }, Range{ 8, 10 } }), ranges);
|
||||||
|
|
||||||
|
ranges.clear();
|
||||||
|
|
||||||
|
// Reverse iterators.
|
||||||
|
for (auto it = rs.crbegin(); it != rs.crend(); it++) {
|
||||||
|
ranges.push_back(*it);
|
||||||
|
}
|
||||||
|
ASSERT_EQ((std::vector<Range>{ Range{ 8, 10 }, Range{ 1, 5 } }), ranges);
|
||||||
|
}
|
||||||
|
|
|
@ -118,8 +118,7 @@ static bool discard_blocks(int fd, off64_t offset, uint64_t size) {
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t args[2] = { static_cast<uint64_t>(offset), size };
|
uint64_t args[2] = { static_cast<uint64_t>(offset), size };
|
||||||
int status = ioctl(fd, BLKDISCARD, &args);
|
if (ioctl(fd, BLKDISCARD, &args) == -1) {
|
||||||
if (status == -1) {
|
|
||||||
PLOG(ERROR) << "BLKDISCARD ioctl failed";
|
PLOG(ERROR) << "BLKDISCARD ioctl failed";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -151,11 +150,11 @@ class RangeSinkWriter {
|
||||||
public:
|
public:
|
||||||
RangeSinkWriter(int fd, const RangeSet& tgt)
|
RangeSinkWriter(int fd, const RangeSet& tgt)
|
||||||
: fd_(fd), tgt_(tgt), next_range_(0), current_range_left_(0) {
|
: fd_(fd), tgt_(tgt), next_range_(0), current_range_left_(0) {
|
||||||
CHECK_NE(tgt.count, static_cast<size_t>(0));
|
CHECK_NE(tgt.size(), static_cast<size_t>(0));
|
||||||
};
|
};
|
||||||
|
|
||||||
bool Finished() const {
|
bool Finished() const {
|
||||||
return next_range_ == tgt_.count && current_range_left_ == 0;
|
return next_range_ == tgt_.size() && current_range_left_ == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t Write(const uint8_t* data, size_t size) {
|
size_t Write(const uint8_t* data, size_t size) {
|
||||||
|
@ -168,10 +167,10 @@ class RangeSinkWriter {
|
||||||
while (size > 0) {
|
while (size > 0) {
|
||||||
// Move to the next range as needed.
|
// Move to the next range as needed.
|
||||||
if (current_range_left_ == 0) {
|
if (current_range_left_ == 0) {
|
||||||
if (next_range_ < tgt_.count) {
|
if (next_range_ < tgt_.size()) {
|
||||||
off64_t offset = static_cast<off64_t>(tgt_.pos[next_range_ * 2]) * BLOCKSIZE;
|
const Range& range = tgt_[next_range_];
|
||||||
current_range_left_ =
|
off64_t offset = static_cast<off64_t>(range.first) * BLOCKSIZE;
|
||||||
(tgt_.pos[next_range_ * 2 + 1] - tgt_.pos[next_range_ * 2]) * BLOCKSIZE;
|
current_range_left_ = (range.second - range.first) * BLOCKSIZE;
|
||||||
next_range_++;
|
next_range_++;
|
||||||
if (!discard_blocks(fd_, offset, current_range_left_)) {
|
if (!discard_blocks(fd_, offset, current_range_left_)) {
|
||||||
break;
|
break;
|
||||||
|
@ -282,16 +281,13 @@ static void* unzip_new_data(void* cookie) {
|
||||||
|
|
||||||
static int ReadBlocks(const RangeSet& src, std::vector<uint8_t>& buffer, int fd) {
|
static int ReadBlocks(const RangeSet& src, std::vector<uint8_t>& buffer, int fd) {
|
||||||
size_t p = 0;
|
size_t p = 0;
|
||||||
uint8_t* data = buffer.data();
|
for (const auto& range : src) {
|
||||||
|
if (!check_lseek(fd, static_cast<off64_t>(range.first) * BLOCKSIZE, SEEK_SET)) {
|
||||||
for (size_t i = 0; i < src.count; ++i) {
|
|
||||||
if (!check_lseek(fd, (off64_t) src.pos[i * 2] * BLOCKSIZE, SEEK_SET)) {
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t size = (src.pos[i * 2 + 1] - src.pos[i * 2]) * BLOCKSIZE;
|
size_t size = (range.second - range.first) * BLOCKSIZE;
|
||||||
|
if (read_all(fd, buffer.data() + p, size) == -1) {
|
||||||
if (read_all(fd, data + p, size) == -1) {
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -303,9 +299,9 @@ static int ReadBlocks(const RangeSet& src, std::vector<uint8_t>& buffer, int fd)
|
||||||
|
|
||||||
static int WriteBlocks(const RangeSet& tgt, const std::vector<uint8_t>& buffer, int fd) {
|
static int WriteBlocks(const RangeSet& tgt, const std::vector<uint8_t>& buffer, int fd) {
|
||||||
size_t written = 0;
|
size_t written = 0;
|
||||||
for (size_t i = 0; i < tgt.count; ++i) {
|
for (const auto& range : tgt) {
|
||||||
off64_t offset = static_cast<off64_t>(tgt.pos[i * 2]) * BLOCKSIZE;
|
off64_t offset = static_cast<off64_t>(range.first) * BLOCKSIZE;
|
||||||
size_t size = (tgt.pos[i * 2 + 1] - tgt.pos[i * 2]) * BLOCKSIZE;
|
size_t size = (range.second - range.first) * BLOCKSIZE;
|
||||||
if (!discard_blocks(fd, offset, size)) {
|
if (!discard_blocks(fd, offset, size)) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -386,21 +382,18 @@ static void PrintHashForCorruptedSourceBlocks(const CommandParameters& params,
|
||||||
// If there's no stashed blocks, content in the buffer is consecutive and has the same
|
// If there's no stashed blocks, content in the buffer is consecutive and has the same
|
||||||
// order as the source blocks.
|
// order as the source blocks.
|
||||||
if (pos == params.tokens.size()) {
|
if (pos == params.tokens.size()) {
|
||||||
locs.count = 1;
|
locs = RangeSet(std::vector<Range>{ Range{ 0, src.blocks() } });
|
||||||
locs.size = src.size;
|
|
||||||
locs.pos = { 0, src.size };
|
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, the next token is the offset of the source blocks in the target range.
|
// Otherwise, the next token is the offset of the source blocks in the target range.
|
||||||
// Example: for the tokens <4,63946,63947,63948,63979> <4,6,7,8,39> <stashed_blocks>;
|
// Example: for the tokens <4,63946,63947,63948,63979> <4,6,7,8,39> <stashed_blocks>;
|
||||||
// We want to print SHA-1 for the data in buffer[6], buffer[8], buffer[9] ... buffer[38];
|
// We want to print SHA-1 for the data in buffer[6], buffer[8], buffer[9] ... buffer[38];
|
||||||
// this corresponds to the 32 src blocks #63946, #63948, #63949 ... #63978.
|
// this corresponds to the 32 src blocks #63946, #63948, #63949 ... #63978.
|
||||||
locs = RangeSet::Parse(params.tokens[pos++]);
|
locs = RangeSet::Parse(params.tokens[pos++]);
|
||||||
CHECK_EQ(src.size, locs.size);
|
CHECK_EQ(src.blocks(), locs.blocks());
|
||||||
CHECK_EQ(locs.pos.size() % 2, static_cast<size_t>(0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG(INFO) << "printing hash in hex for " << src.size << " source blocks";
|
LOG(INFO) << "printing hash in hex for " << src.blocks() << " source blocks";
|
||||||
for (size_t i = 0; i < src.size; i++) {
|
for (size_t i = 0; i < src.blocks(); i++) {
|
||||||
size_t block_num = src.GetBlockNumber(i);
|
size_t block_num = src.GetBlockNumber(i);
|
||||||
size_t buffer_index = locs.GetBlockNumber(i);
|
size_t buffer_index = locs.GetBlockNumber(i);
|
||||||
CHECK_LE((buffer_index + 1) * BLOCKSIZE, buffer.size());
|
CHECK_LE((buffer_index + 1) * BLOCKSIZE, buffer.size());
|
||||||
|
@ -418,9 +411,9 @@ static void PrintHashForCorruptedStashedBlocks(const std::string& id,
|
||||||
const std::vector<uint8_t>& buffer,
|
const std::vector<uint8_t>& buffer,
|
||||||
const RangeSet& src) {
|
const RangeSet& src) {
|
||||||
LOG(INFO) << "printing hash in hex for stash_id: " << id;
|
LOG(INFO) << "printing hash in hex for stash_id: " << id;
|
||||||
CHECK_EQ(src.size * BLOCKSIZE, buffer.size());
|
CHECK_EQ(src.blocks() * BLOCKSIZE, buffer.size());
|
||||||
|
|
||||||
for (size_t i = 0; i < src.size; i++) {
|
for (size_t i = 0; i < src.blocks(); i++) {
|
||||||
size_t block_num = src.GetBlockNumber(i);
|
size_t block_num = src.GetBlockNumber(i);
|
||||||
|
|
||||||
uint8_t digest[SHA_DIGEST_LENGTH];
|
uint8_t digest[SHA_DIGEST_LENGTH];
|
||||||
|
@ -440,7 +433,7 @@ static void PrintHashForMissingStashedBlocks(const std::string& id, int fd) {
|
||||||
|
|
||||||
LOG(INFO) << "print hash in hex for source blocks in missing stash: " << id;
|
LOG(INFO) << "print hash in hex for source blocks in missing stash: " << id;
|
||||||
const RangeSet& src = stash_map[id];
|
const RangeSet& src = stash_map[id];
|
||||||
std::vector<uint8_t> buffer(src.size * BLOCKSIZE);
|
std::vector<uint8_t> buffer(src.blocks() * BLOCKSIZE);
|
||||||
if (ReadBlocks(src, buffer, fd) == -1) {
|
if (ReadBlocks(src, buffer, fd) == -1) {
|
||||||
LOG(ERROR) << "failed to read source blocks for stash: " << id;
|
LOG(ERROR) << "failed to read source blocks for stash: " << id;
|
||||||
return;
|
return;
|
||||||
|
@ -532,19 +525,18 @@ static void DeleteStash(const std::string& base) {
|
||||||
|
|
||||||
static int LoadStash(CommandParameters& params, const std::string& id, bool verify, size_t* blocks,
|
static int LoadStash(CommandParameters& params, const std::string& id, bool verify, size_t* blocks,
|
||||||
std::vector<uint8_t>& buffer, bool printnoent) {
|
std::vector<uint8_t>& buffer, bool printnoent) {
|
||||||
// In verify mode, if source range_set was saved for the given hash,
|
// In verify mode, if source range_set was saved for the given hash, check contents in the source
|
||||||
// check contents in the source blocks first. If the check fails,
|
// blocks first. If the check fails, search for the stashed files on /cache as usual.
|
||||||
// search for the stashed files on /cache as usual.
|
|
||||||
if (!params.canwrite) {
|
if (!params.canwrite) {
|
||||||
if (stash_map.find(id) != stash_map.end()) {
|
if (stash_map.find(id) != stash_map.end()) {
|
||||||
const RangeSet& src = stash_map[id];
|
const RangeSet& src = stash_map[id];
|
||||||
allocate(src.size * BLOCKSIZE, buffer);
|
allocate(src.blocks() * BLOCKSIZE, buffer);
|
||||||
|
|
||||||
if (ReadBlocks(src, buffer, params.fd) == -1) {
|
if (ReadBlocks(src, buffer, params.fd) == -1) {
|
||||||
LOG(ERROR) << "failed to read source blocks in stash map.";
|
LOG(ERROR) << "failed to read source blocks in stash map.";
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (VerifyBlocks(id, buffer, src.size, true) != 0) {
|
if (VerifyBlocks(id, buffer, src.blocks(), true) != 0) {
|
||||||
LOG(ERROR) << "failed to verify loaded source blocks in stash map.";
|
LOG(ERROR) << "failed to verify loaded source blocks in stash map.";
|
||||||
PrintHashForCorruptedStashedBlocks(id, buffer, src);
|
PrintHashForCorruptedStashedBlocks(id, buffer, src);
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -554,7 +546,6 @@ static int LoadStash(CommandParameters& params, const std::string& id, bool veri
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t blockcount = 0;
|
size_t blockcount = 0;
|
||||||
|
|
||||||
if (!blocks) {
|
if (!blocks) {
|
||||||
blocks = &blockcount;
|
blocks = &blockcount;
|
||||||
}
|
}
|
||||||
|
@ -562,9 +553,7 @@ static int LoadStash(CommandParameters& params, const std::string& id, bool veri
|
||||||
std::string fn = GetStashFileName(params.stashbase, id, "");
|
std::string fn = GetStashFileName(params.stashbase, id, "");
|
||||||
|
|
||||||
struct stat sb;
|
struct stat sb;
|
||||||
int res = stat(fn.c_str(), &sb);
|
if (stat(fn.c_str(), &sb) == -1) {
|
||||||
|
|
||||||
if (res == -1) {
|
|
||||||
if (errno != ENOENT || printnoent) {
|
if (errno != ENOENT || printnoent) {
|
||||||
PLOG(ERROR) << "stat \"" << fn << "\" failed";
|
PLOG(ERROR) << "stat \"" << fn << "\" failed";
|
||||||
PrintHashForMissingStashedBlocks(id, params.fd);
|
PrintHashForMissingStashedBlocks(id, params.fd);
|
||||||
|
@ -780,20 +769,18 @@ static int FreeStash(const std::string& base, const std::string& id) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Source contains packed data, which we want to move to the locations given in locs in the dest
|
||||||
|
// buffer. source and dest may be the same buffer.
|
||||||
static void MoveRange(std::vector<uint8_t>& dest, const RangeSet& locs,
|
static void MoveRange(std::vector<uint8_t>& dest, const RangeSet& locs,
|
||||||
const std::vector<uint8_t>& source) {
|
const std::vector<uint8_t>& source) {
|
||||||
// source contains packed data, which we want to move to the
|
|
||||||
// locations given in locs in the dest buffer. source and dest
|
|
||||||
// may be the same buffer.
|
|
||||||
|
|
||||||
const uint8_t* from = source.data();
|
const uint8_t* from = source.data();
|
||||||
uint8_t* to = dest.data();
|
uint8_t* to = dest.data();
|
||||||
size_t start = locs.size;
|
size_t start = locs.blocks();
|
||||||
for (int i = locs.count-1; i >= 0; --i) {
|
// Must do the movement backward.
|
||||||
size_t blocks = locs.pos[i*2+1] - locs.pos[i*2];
|
for (auto it = locs.crbegin(); it != locs.crend(); it++) {
|
||||||
|
size_t blocks = it->second - it->first;
|
||||||
start -= blocks;
|
start -= blocks;
|
||||||
memmove(to + (locs.pos[i*2] * BLOCKSIZE), from + (start * BLOCKSIZE),
|
memmove(to + (it->first * BLOCKSIZE), from + (start * BLOCKSIZE), blocks * BLOCKSIZE);
|
||||||
blocks * BLOCKSIZE);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -933,13 +920,13 @@ static int LoadSrcTgtVersion3(CommandParameters& params, RangeSet& tgt, size_t*
|
||||||
// <tgt_range>
|
// <tgt_range>
|
||||||
tgt = RangeSet::Parse(params.tokens[params.cpos++]);
|
tgt = RangeSet::Parse(params.tokens[params.cpos++]);
|
||||||
|
|
||||||
std::vector<uint8_t> tgtbuffer(tgt.size * BLOCKSIZE);
|
std::vector<uint8_t> tgtbuffer(tgt.blocks() * BLOCKSIZE);
|
||||||
if (ReadBlocks(tgt, tgtbuffer, params.fd) == -1) {
|
if (ReadBlocks(tgt, tgtbuffer, params.fd) == -1) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return now if target blocks already have expected content.
|
// Return now if target blocks already have expected content.
|
||||||
if (VerifyBlocks(tgthash, tgtbuffer, tgt.size, false) == 0) {
|
if (VerifyBlocks(tgthash, tgtbuffer, tgt.blocks(), false) == 0) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1023,7 +1010,7 @@ static int PerformCommandMove(CommandParameters& params) {
|
||||||
params.freestash.clear();
|
params.freestash.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
params.written += tgt.size;
|
params.written += tgt.blocks();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1045,11 +1032,11 @@ static int PerformCommandStash(CommandParameters& params) {
|
||||||
|
|
||||||
RangeSet src = RangeSet::Parse(params.tokens[params.cpos++]);
|
RangeSet src = RangeSet::Parse(params.tokens[params.cpos++]);
|
||||||
|
|
||||||
allocate(src.size * BLOCKSIZE, params.buffer);
|
allocate(src.blocks() * BLOCKSIZE, params.buffer);
|
||||||
if (ReadBlocks(src, params.buffer, params.fd) == -1) {
|
if (ReadBlocks(src, params.buffer, params.fd) == -1) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
blocks = src.size;
|
blocks = src.blocks();
|
||||||
stash_map[id] = src;
|
stash_map[id] = src;
|
||||||
|
|
||||||
if (VerifyBlocks(id, params.buffer, blocks, true) != 0) {
|
if (VerifyBlocks(id, params.buffer, blocks, true) != 0) {
|
||||||
|
@ -1088,7 +1075,6 @@ static int PerformCommandFree(CommandParameters& params) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static int PerformCommandZero(CommandParameters& params) {
|
static int PerformCommandZero(CommandParameters& params) {
|
||||||
|
|
||||||
if (params.cpos >= params.tokens.size()) {
|
if (params.cpos >= params.tokens.size()) {
|
||||||
LOG(ERROR) << "missing target blocks for zero";
|
LOG(ERROR) << "missing target blocks for zero";
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -1096,15 +1082,15 @@ static int PerformCommandZero(CommandParameters& params) {
|
||||||
|
|
||||||
RangeSet tgt = RangeSet::Parse(params.tokens[params.cpos++]);
|
RangeSet tgt = RangeSet::Parse(params.tokens[params.cpos++]);
|
||||||
|
|
||||||
LOG(INFO) << " zeroing " << tgt.size << " blocks";
|
LOG(INFO) << " zeroing " << tgt.blocks() << " blocks";
|
||||||
|
|
||||||
allocate(BLOCKSIZE, params.buffer);
|
allocate(BLOCKSIZE, params.buffer);
|
||||||
memset(params.buffer.data(), 0, BLOCKSIZE);
|
memset(params.buffer.data(), 0, BLOCKSIZE);
|
||||||
|
|
||||||
if (params.canwrite) {
|
if (params.canwrite) {
|
||||||
for (size_t i = 0; i < tgt.count; ++i) {
|
for (const auto& range : tgt) {
|
||||||
off64_t offset = static_cast<off64_t>(tgt.pos[i * 2]) * BLOCKSIZE;
|
off64_t offset = static_cast<off64_t>(range.first) * BLOCKSIZE;
|
||||||
size_t size = (tgt.pos[i * 2 + 1] - tgt.pos[i * 2]) * BLOCKSIZE;
|
size_t size = (range.second - range.first) * BLOCKSIZE;
|
||||||
if (!discard_blocks(params.fd, offset, size)) {
|
if (!discard_blocks(params.fd, offset, size)) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -1113,7 +1099,7 @@ static int PerformCommandZero(CommandParameters& params) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t j = tgt.pos[i * 2]; j < tgt.pos[i * 2 + 1]; ++j) {
|
for (size_t j = range.first; j < range.second; ++j) {
|
||||||
if (write_all(params.fd, params.buffer, BLOCKSIZE) == -1) {
|
if (write_all(params.fd, params.buffer, BLOCKSIZE) == -1) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -1124,7 +1110,7 @@ static int PerformCommandZero(CommandParameters& params) {
|
||||||
if (params.cmdname[0] == 'z') {
|
if (params.cmdname[0] == 'z') {
|
||||||
// Update only for the zero command, as the erase command will call
|
// Update only for the zero command, as the erase command will call
|
||||||
// this if DEBUG_ERASE is defined.
|
// this if DEBUG_ERASE is defined.
|
||||||
params.written += tgt.size;
|
params.written += tgt.blocks();
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1139,7 +1125,7 @@ static int PerformCommandNew(CommandParameters& params) {
|
||||||
RangeSet tgt = RangeSet::Parse(params.tokens[params.cpos++]);
|
RangeSet tgt = RangeSet::Parse(params.tokens[params.cpos++]);
|
||||||
|
|
||||||
if (params.canwrite) {
|
if (params.canwrite) {
|
||||||
LOG(INFO) << " writing " << tgt.size << " blocks of new data";
|
LOG(INFO) << " writing " << tgt.blocks() << " blocks of new data";
|
||||||
|
|
||||||
RangeSinkWriter writer(params.fd, tgt);
|
RangeSinkWriter writer(params.fd, tgt);
|
||||||
pthread_mutex_lock(¶ms.nti.mu);
|
pthread_mutex_lock(¶ms.nti.mu);
|
||||||
|
@ -1153,7 +1139,7 @@ static int PerformCommandNew(CommandParameters& params) {
|
||||||
pthread_mutex_unlock(¶ms.nti.mu);
|
pthread_mutex_unlock(¶ms.nti.mu);
|
||||||
}
|
}
|
||||||
|
|
||||||
params.written += tgt.size;
|
params.written += tgt.blocks();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1195,7 +1181,7 @@ static int PerformCommandDiff(CommandParameters& params) {
|
||||||
|
|
||||||
if (params.canwrite) {
|
if (params.canwrite) {
|
||||||
if (status == 0) {
|
if (status == 0) {
|
||||||
LOG(INFO) << "patching " << blocks << " blocks to " << tgt.size;
|
LOG(INFO) << "patching " << blocks << " blocks to " << tgt.blocks();
|
||||||
Value patch_value(
|
Value patch_value(
|
||||||
VAL_BLOB, std::string(reinterpret_cast<const char*>(params.patch_start + offset), len));
|
VAL_BLOB, std::string(reinterpret_cast<const char*>(params.patch_start + offset), len));
|
||||||
|
|
||||||
|
@ -1223,7 +1209,7 @@ static int PerformCommandDiff(CommandParameters& params) {
|
||||||
LOG(ERROR) << "range sink underrun?";
|
LOG(ERROR) << "range sink underrun?";
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LOG(INFO) << "skipping " << blocks << " blocks already patched to " << tgt.size << " ["
|
LOG(INFO) << "skipping " << blocks << " blocks already patched to " << tgt.blocks() << " ["
|
||||||
<< params.cmdline << "]";
|
<< params.cmdline << "]";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1233,7 +1219,7 @@ static int PerformCommandDiff(CommandParameters& params) {
|
||||||
params.freestash.clear();
|
params.freestash.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
params.written += tgt.size;
|
params.written += tgt.blocks();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1262,14 +1248,14 @@ static int PerformCommandErase(CommandParameters& params) {
|
||||||
RangeSet tgt = RangeSet::Parse(params.tokens[params.cpos++]);
|
RangeSet tgt = RangeSet::Parse(params.tokens[params.cpos++]);
|
||||||
|
|
||||||
if (params.canwrite) {
|
if (params.canwrite) {
|
||||||
LOG(INFO) << " erasing " << tgt.size << " blocks";
|
LOG(INFO) << " erasing " << tgt.blocks() << " blocks";
|
||||||
|
|
||||||
for (size_t i = 0; i < tgt.count; ++i) {
|
for (const auto& range : tgt) {
|
||||||
uint64_t blocks[2];
|
uint64_t blocks[2];
|
||||||
// offset in bytes
|
// offset in bytes
|
||||||
blocks[0] = tgt.pos[i * 2] * (uint64_t) BLOCKSIZE;
|
blocks[0] = range.first * static_cast<uint64_t>(BLOCKSIZE);
|
||||||
// length in bytes
|
// length in bytes
|
||||||
blocks[1] = (tgt.pos[i * 2 + 1] - tgt.pos[i * 2]) * (uint64_t) BLOCKSIZE;
|
blocks[1] = (range.second - range.first) * static_cast<uint64_t>(BLOCKSIZE);
|
||||||
|
|
||||||
if (ioctl(params.fd, BLKDISCARD, &blocks) == -1) {
|
if (ioctl(params.fd, BLKDISCARD, &blocks) == -1) {
|
||||||
PLOG(ERROR) << "BLKDISCARD ioctl failed";
|
PLOG(ERROR) << "BLKDISCARD ioctl failed";
|
||||||
|
@ -1645,14 +1631,14 @@ Value* RangeSha1Fn(const char* name, State* state, const std::vector<std::unique
|
||||||
SHA1_Init(&ctx);
|
SHA1_Init(&ctx);
|
||||||
|
|
||||||
std::vector<uint8_t> buffer(BLOCKSIZE);
|
std::vector<uint8_t> buffer(BLOCKSIZE);
|
||||||
for (size_t i = 0; i < rs.count; ++i) {
|
for (const auto& range : rs) {
|
||||||
if (!check_lseek(fd, (off64_t)rs.pos[i * 2] * BLOCKSIZE, SEEK_SET)) {
|
if (!check_lseek(fd, static_cast<off64_t>(range.first) * BLOCKSIZE, SEEK_SET)) {
|
||||||
ErrorAbort(state, kLseekFailure, "failed to seek %s: %s", blockdev_filename->data.c_str(),
|
ErrorAbort(state, kLseekFailure, "failed to seek %s: %s", blockdev_filename->data.c_str(),
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
return StringValue("");
|
return StringValue("");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t j = rs.pos[i * 2]; j < rs.pos[i * 2 + 1]; ++j) {
|
for (size_t j = range.first; j < range.second; ++j) {
|
||||||
if (read_all(fd, buffer, BLOCKSIZE) == -1) {
|
if (read_all(fd, buffer, BLOCKSIZE) == -1) {
|
||||||
ErrorAbort(state, kFreadFailure, "failed to read %s: %s", blockdev_filename->data.c_str(),
|
ErrorAbort(state, kFreadFailure, "failed to read %s: %s", blockdev_filename->data.c_str(),
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
|
@ -1700,7 +1686,7 @@ Value* CheckFirstBlockFn(const char* name, State* state,
|
||||||
return StringValue("");
|
return StringValue("");
|
||||||
}
|
}
|
||||||
|
|
||||||
RangeSet blk0{ 1 /*count*/, 1 /*size*/, std::vector<size_t>{ 0, 1 } /*position*/ };
|
RangeSet blk0(std::vector<Range>{ Range{ 0, 1 } });
|
||||||
std::vector<uint8_t> block0_buffer(BLOCKSIZE);
|
std::vector<uint8_t> block0_buffer(BLOCKSIZE);
|
||||||
|
|
||||||
if (ReadBlocks(blk0, block0_buffer, fd) == -1) {
|
if (ReadBlocks(blk0, block0_buffer, fd) == -1) {
|
||||||
|
@ -1770,24 +1756,20 @@ Value* BlockImageRecoverFn(const char* name, State* state,
|
||||||
}
|
}
|
||||||
|
|
||||||
fec_status status;
|
fec_status status;
|
||||||
|
|
||||||
if (!fh.get_status(status)) {
|
if (!fh.get_status(status)) {
|
||||||
ErrorAbort(state, kLibfecFailure, "failed to read FEC status");
|
ErrorAbort(state, kLibfecFailure, "failed to read FEC status");
|
||||||
return StringValue("");
|
return StringValue("");
|
||||||
}
|
}
|
||||||
|
|
||||||
RangeSet rs = RangeSet::Parse(ranges->data);
|
|
||||||
|
|
||||||
uint8_t buffer[BLOCKSIZE];
|
uint8_t buffer[BLOCKSIZE];
|
||||||
|
for (const auto& range : RangeSet::Parse(ranges->data)) {
|
||||||
for (size_t i = 0; i < rs.count; ++i) {
|
for (size_t j = range.first; j < range.second; ++j) {
|
||||||
for (size_t j = rs.pos[i * 2]; j < rs.pos[i * 2 + 1]; ++j) {
|
|
||||||
// Stay within the data area, libfec validates and corrects metadata
|
// Stay within the data area, libfec validates and corrects metadata
|
||||||
if (status.data_size <= (uint64_t)j * BLOCKSIZE) {
|
if (status.data_size <= static_cast<uint64_t>(j) * BLOCKSIZE) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fh.pread(buffer, BLOCKSIZE, (off64_t)j * BLOCKSIZE) != BLOCKSIZE) {
|
if (fh.pread(buffer, BLOCKSIZE, static_cast<off64_t>(j) * BLOCKSIZE) != BLOCKSIZE) {
|
||||||
ErrorAbort(state, kLibfecFailure, "failed to recover %s (block %zu): %s",
|
ErrorAbort(state, kLibfecFailure, "failed to recover %s (block %zu): %s",
|
||||||
filename->data.c_str(), j, strerror(errno));
|
filename->data.c_str(), j, strerror(errno));
|
||||||
return StringValue("");
|
return StringValue("");
|
||||||
|
|
|
@ -19,16 +19,35 @@
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <android-base/logging.h>
|
#include <android-base/logging.h>
|
||||||
#include <android-base/parseint.h>
|
#include <android-base/parseint.h>
|
||||||
#include <android-base/strings.h>
|
#include <android-base/strings.h>
|
||||||
|
|
||||||
struct RangeSet {
|
using Range = std::pair<size_t, size_t>;
|
||||||
size_t count; // Limit is INT_MAX.
|
|
||||||
size_t size; // The number of blocks in the RangeSet.
|
class RangeSet {
|
||||||
std::vector<size_t> pos; // Actual limit is INT_MAX.
|
public:
|
||||||
|
RangeSet() : blocks_(0) {}
|
||||||
|
|
||||||
|
explicit RangeSet(std::vector<Range>&& pairs) {
|
||||||
|
CHECK_NE(pairs.size(), static_cast<size_t>(0)) << "Invalid number of tokens";
|
||||||
|
|
||||||
|
// Sanity check the input.
|
||||||
|
size_t result = 0;
|
||||||
|
for (const auto& range : pairs) {
|
||||||
|
CHECK_LT(range.first, range.second)
|
||||||
|
<< "Empty or negative range: " << range.first << ", " << range.second;
|
||||||
|
size_t sz = range.second - range.first;
|
||||||
|
CHECK_LE(result, SIZE_MAX - sz) << "RangeSet size overflow";
|
||||||
|
result += sz;
|
||||||
|
}
|
||||||
|
|
||||||
|
ranges_ = pairs;
|
||||||
|
blocks_ = result;
|
||||||
|
}
|
||||||
|
|
||||||
static RangeSet Parse(const std::string& range_text) {
|
static RangeSet Parse(const std::string& range_text) {
|
||||||
std::vector<std::string> pieces = android::base::Split(range_text, ",");
|
std::vector<std::string> pieces = android::base::Split(range_text, ",");
|
||||||
|
@ -42,44 +61,43 @@ struct RangeSet {
|
||||||
CHECK_EQ(num % 2, static_cast<size_t>(0)) << "Number of tokens must be even: " << range_text;
|
CHECK_EQ(num % 2, static_cast<size_t>(0)) << "Number of tokens must be even: " << range_text;
|
||||||
CHECK_EQ(num, pieces.size() - 1) << "Mismatching number of tokens: " << range_text;
|
CHECK_EQ(num, pieces.size() - 1) << "Mismatching number of tokens: " << range_text;
|
||||||
|
|
||||||
std::vector<size_t> pairs(num);
|
std::vector<Range> pairs;
|
||||||
size_t size = 0;
|
|
||||||
for (size_t i = 0; i < num; i += 2) {
|
for (size_t i = 0; i < num; i += 2) {
|
||||||
CHECK(android::base::ParseUint(pieces[i + 1], &pairs[i], static_cast<size_t>(INT_MAX)));
|
size_t first;
|
||||||
CHECK(android::base::ParseUint(pieces[i + 2], &pairs[i + 1], static_cast<size_t>(INT_MAX)));
|
CHECK(android::base::ParseUint(pieces[i + 1], &first, static_cast<size_t>(INT_MAX)));
|
||||||
CHECK_LT(pairs[i], pairs[i + 1])
|
size_t second;
|
||||||
<< "Empty or negative range: " << pairs[i] << ", " << pairs[i + 1];
|
CHECK(android::base::ParseUint(pieces[i + 2], &second, static_cast<size_t>(INT_MAX)));
|
||||||
|
|
||||||
size_t sz = pairs[i + 1] - pairs[i];
|
pairs.emplace_back(first, second);
|
||||||
CHECK_LE(size, SIZE_MAX - sz) << "RangeSet size overflow";
|
|
||||||
size += sz;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return RangeSet{ num / 2, size, std::move(pairs) };
|
return RangeSet(std::move(pairs));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the block number for the i-th (starting from 0) block in the RangeSet.
|
// Get the block number for the i-th (starting from 0) block in the RangeSet.
|
||||||
size_t GetBlockNumber(size_t idx) const {
|
size_t GetBlockNumber(size_t idx) const {
|
||||||
CHECK_LT(idx, size) << "Index " << idx << " is greater than RangeSet size " << size;
|
CHECK_LT(idx, blocks_) << "Out of bound index " << idx << " (total blocks: " << blocks_ << ")";
|
||||||
for (size_t i = 0; i < pos.size(); i += 2) {
|
|
||||||
if (idx < pos[i + 1] - pos[i]) {
|
for (const auto& range : ranges_) {
|
||||||
return pos[i] + idx;
|
if (idx < range.second - range.first) {
|
||||||
|
return range.first + idx;
|
||||||
}
|
}
|
||||||
idx -= (pos[i + 1] - pos[i]);
|
idx -= (range.second - range.first);
|
||||||
}
|
}
|
||||||
CHECK(false);
|
|
||||||
|
CHECK(false) << "Failed to find block number for index " << idx;
|
||||||
return 0; // Unreachable, but to make compiler happy.
|
return 0; // Unreachable, but to make compiler happy.
|
||||||
}
|
}
|
||||||
|
|
||||||
// RangeSet has half-closed half-open bounds. For example, "3,5" contains blocks 3 and 4. So "3,5"
|
// RangeSet has half-closed half-open bounds. For example, "3,5" contains blocks 3 and 4. So "3,5"
|
||||||
// and "5,7" are not overlapped.
|
// and "5,7" are not overlapped.
|
||||||
bool Overlaps(const RangeSet& other) const {
|
bool Overlaps(const RangeSet& other) const {
|
||||||
for (size_t i = 0; i < count; ++i) {
|
for (const auto& range : ranges_) {
|
||||||
size_t start = pos[i * 2];
|
size_t start = range.first;
|
||||||
size_t end = pos[i * 2 + 1];
|
size_t end = range.second;
|
||||||
for (size_t j = 0; j < other.count; ++j) {
|
for (const auto& other_range : other.ranges_) {
|
||||||
size_t other_start = other.pos[j * 2];
|
size_t other_start = other_range.first;
|
||||||
size_t other_end = other.pos[j * 2 + 1];
|
size_t other_end = other_range.second;
|
||||||
// [start, end) vs [other_start, other_end)
|
// [start, end) vs [other_start, other_end)
|
||||||
if (!(other_start >= end || start >= other_end)) {
|
if (!(other_start >= end || start >= other_end)) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -89,7 +107,58 @@ struct RangeSet {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator==(const RangeSet& other) const {
|
// size() gives the number of Range's in this RangeSet.
|
||||||
return (count == other.count && size == other.size && pos == other.pos);
|
size_t size() const {
|
||||||
|
return ranges_.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// blocks() gives the number of all blocks in this RangeSet.
|
||||||
|
size_t blocks() const {
|
||||||
|
return blocks_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We provide const iterators only.
|
||||||
|
std::vector<Range>::const_iterator cbegin() const {
|
||||||
|
return ranges_.cbegin();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Range>::const_iterator cend() const {
|
||||||
|
return ranges_.cend();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Need to provide begin()/end() since range-based loop expects begin()/end().
|
||||||
|
std::vector<Range>::const_iterator begin() const {
|
||||||
|
return ranges_.cbegin();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Range>::const_iterator end() const {
|
||||||
|
return ranges_.cend();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reverse const iterators for MoveRange().
|
||||||
|
std::vector<Range>::const_reverse_iterator crbegin() const {
|
||||||
|
return ranges_.crbegin();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Range>::const_reverse_iterator crend() const {
|
||||||
|
return ranges_.crend();
|
||||||
|
}
|
||||||
|
|
||||||
|
const Range& operator[](size_t i) const {
|
||||||
|
return ranges_[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const RangeSet& other) const {
|
||||||
|
// The orders of Range's matter. "4,1,5,8,10" != "4,8,10,1,5".
|
||||||
|
return (ranges_ == other.ranges_);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const RangeSet& other) const {
|
||||||
|
return ranges_ != other.ranges_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Actual limit for each value and the total number are both INT_MAX.
|
||||||
|
std::vector<Range> ranges_;
|
||||||
|
size_t blocks_;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue