Merge changes I91cf0def,I47570e8e am: 5f01cf3cac am: 84ceaedb59
am: 738694469d
Change-Id: I3df9cd0523d2bfcdc716c855c8dca86aba09be9d
diff --git a/Checkpoint.cpp b/Checkpoint.cpp
index 1020ad7..75a22ec 100644
--- a/Checkpoint.cpp
+++ b/Checkpoint.cpp
@@ -260,6 +260,8 @@
// MAGIC is BOW in ascii
const int kMagic = 0x00574f42;
+// Partially restored MAGIC is WOB in ascii
+const int kPartialRestoreMagic = 0x00424f57;
void crc32(const void* data, size_t n_bytes, uint32_t* crc) {
static uint32_t table[0x100] = {
@@ -347,6 +349,79 @@
relocations.insert(slice.begin(), slice.end());
}
+// A map of sectors that have been written to.
+// The final entry must always be False.
+// When we restart the restore after an interruption, we must take care that
+// when we copy from dest to source, that the block we copy to was not
+// previously copied from.
+// i e. A->B C->A; If we replay this sequence, we end up copying C->B
+// We must save our partial result whenever we finish a page, or when we copy
+// to a location that was copied from earlier (our source is an earlier dest)
+typedef std::map<sector_t, bool> Used_Sectors;
+
+bool checkCollision(Used_Sectors& used_sectors, sector_t start, sector_t end) {
+ auto second_overlap = used_sectors.upper_bound(start);
+ auto first_overlap = --second_overlap;
+
+ if (first_overlap->second) {
+ return true;
+ } else if (second_overlap != used_sectors.end() && second_overlap->first < end) {
+ return true;
+ }
+ return false;
+}
+
+void markUsed(Used_Sectors& used_sectors, sector_t start, sector_t end) {
+ auto start_pos = used_sectors.insert_or_assign(start, true).first;
+ auto end_pos = used_sectors.insert_or_assign(end, false).first;
+
+ if (start_pos == used_sectors.begin() || !std::prev(start_pos)->second) {
+ start_pos++;
+ }
+ if (std::next(end_pos) != used_sectors.end() && !std::next(end_pos)->second) {
+ end_pos++;
+ }
+ if (start_pos->first < end_pos->first) {
+ used_sectors.erase(start_pos, end_pos);
+ }
+}
+
+// Restores the given log_entry's data from dest -> source
+// If that entry is a log sector, set the magic to kPartialRestoreMagic and flush.
+void restoreSector(int device_fd, Used_Sectors& used_sectors, std::vector<char>& ls_buffer,
+ log_entry* le, std::vector<char>& buffer) {
+ log_sector_v1_0& ls = *reinterpret_cast<log_sector_v1_0*>(&ls_buffer[0]);
+ uint32_t index = le - ((log_entry*)&ls_buffer[ls.header_size]);
+ int count = (le->size - 1) / kSectorSize + 1;
+
+ if (checkCollision(used_sectors, le->source, le->source + count)) {
+ fsync(device_fd);
+ lseek64(device_fd, 0, SEEK_SET);
+ ls.count = index + 1;
+ ls.magic = kPartialRestoreMagic;
+ write(device_fd, &ls_buffer[0], ls.block_size);
+ fsync(device_fd);
+ used_sectors.clear();
+ used_sectors[0] = false;
+ }
+
+ markUsed(used_sectors, le->dest, le->dest + count);
+
+ if (index == 0 && ls.sequence != 0) {
+ log_sector_v1_0* next = reinterpret_cast<log_sector_v1_0*>(&buffer[0]);
+ if (next->magic == kMagic) {
+ next->magic = kPartialRestoreMagic;
+ }
+ }
+
+ lseek64(device_fd, le->source * kSectorSize, SEEK_SET);
+ write(device_fd, &buffer[0], le->size);
+
+ if (index == 0) {
+ fsync(device_fd);
+ }
+}
+
// Read from the device
// If we are validating, the read occurs as though the relocations had happened
std::vector<char> relocatedRead(int device_fd, Relocations const& relocations, bool validating,
@@ -371,9 +446,10 @@
} // namespace
-Status cp_restoreCheckpoint(const std::string& blockDevice) {
+Status cp_restoreCheckpoint(const std::string& blockDevice, int restore_limit) {
bool validating = true;
std::string action = "Validating";
+ int restore_count = 0;
for (;;) {
Relocations relocations;
@@ -389,7 +465,10 @@
log_sector_v1_0 original_ls;
read(device_fd, reinterpret_cast<char*>(&original_ls), sizeof(original_ls));
- if (original_ls.magic != kMagic) {
+ if (original_ls.magic == kPartialRestoreMagic) {
+ validating = false;
+ action = "Restoring";
+ } else if (original_ls.magic != kMagic) {
LOG(ERROR) << "No magic";
return Status::fromExceptionCode(EINVAL, "No magic");
}
@@ -397,10 +476,14 @@
LOG(INFO) << action << " " << original_ls.sequence << " log sectors";
for (int sequence = original_ls.sequence; sequence >= 0 && status.isOk(); sequence--) {
- auto buffer = relocatedRead(device_fd, relocations, validating, 0,
- original_ls.block_size, original_ls.block_size);
- log_sector_v1_0 const& ls = *reinterpret_cast<log_sector_v1_0*>(&buffer[0]);
- if (ls.magic != kMagic) {
+ auto ls_buffer = relocatedRead(device_fd, relocations, validating, 0,
+ original_ls.block_size, original_ls.block_size);
+ log_sector_v1_0& ls = *reinterpret_cast<log_sector_v1_0*>(&ls_buffer[0]);
+
+ Used_Sectors used_sectors;
+ used_sectors[0] = false;
+
+ if (ls.magic != kMagic && (ls.magic != kPartialRestoreMagic || validating)) {
LOG(ERROR) << "No magic!";
status = Status::fromExceptionCode(EINVAL, "No magic");
break;
@@ -422,10 +505,9 @@
}
LOG(INFO) << action << " from log sector " << ls.sequence;
-
for (log_entry* le =
- reinterpret_cast<log_entry*>(&buffer[ls.header_size]) + ls.count - 1;
- le >= reinterpret_cast<log_entry*>(&buffer[ls.header_size]); --le) {
+ reinterpret_cast<log_entry*>(&ls_buffer[ls.header_size]) + ls.count - 1;
+ le >= reinterpret_cast<log_entry*>(&ls_buffer[ls.header_size]); --le) {
// This is very noisy - limit to DEBUG only
LOG(VERBOSE) << action << " " << le->size << " bytes from sector " << le->dest
<< " to " << le->source << " with checksum " << std::hex
@@ -445,10 +527,15 @@
}
if (validating) {
- relocate(relocations, le->source, le->dest, le->size / kSectorSize);
+ relocate(relocations, le->source, le->dest, (le->size - 1) / kSectorSize + 1);
} else {
- lseek64(device_fd, le->source * kSectorSize, SEEK_SET);
- write(device_fd, &buffer[0], le->size);
+ restoreSector(device_fd, used_sectors, ls_buffer, le, buffer);
+ restore_count++;
+ if (restore_limit && restore_count >= restore_limit) {
+ LOG(WARNING) << "Hit the test limit";
+ status = Status::fromExceptionCode(EAGAIN, "Hit the test limit");
+ break;
+ }
}
}
}
diff --git a/Checkpoint.h b/Checkpoint.h
index 64ceed3..70dad8a 100644
--- a/Checkpoint.h
+++ b/Checkpoint.h
@@ -37,7 +37,7 @@
android::binder::Status cp_prepareCheckpoint();
-android::binder::Status cp_restoreCheckpoint(const std::string& mountPoint);
+android::binder::Status cp_restoreCheckpoint(const std::string& mountPoint, int count = 0);
android::binder::Status cp_markBootAttempt();
diff --git a/VoldNativeService.cpp b/VoldNativeService.cpp
index 9fc8c9c..121f903 100644
--- a/VoldNativeService.cpp
+++ b/VoldNativeService.cpp
@@ -982,6 +982,14 @@
return cp_restoreCheckpoint(mountPoint);
}
+binder::Status VoldNativeService::restoreCheckpointPart(const std::string& mountPoint, int count) {
+ ENFORCE_UID(AID_SYSTEM);
+ CHECK_ARGUMENT_PATH(mountPoint);
+ ACQUIRE_LOCK;
+
+ return cp_restoreCheckpoint(mountPoint, count);
+}
+
binder::Status VoldNativeService::markBootAttempt() {
ENFORCE_UID(AID_SYSTEM);
ACQUIRE_LOCK;
diff --git a/VoldNativeService.h b/VoldNativeService.h
index 8d75a16..5783f8b 100644
--- a/VoldNativeService.h
+++ b/VoldNativeService.h
@@ -136,6 +136,7 @@
binder::Status commitChanges();
binder::Status prepareCheckpoint();
binder::Status restoreCheckpoint(const std::string& mountPoint);
+ binder::Status restoreCheckpointPart(const std::string& mountPoint, int count);
binder::Status markBootAttempt();
binder::Status abortChanges();
binder::Status supportsCheckpoint(bool* _aidl_return);
diff --git a/binder/android/os/IVold.aidl b/binder/android/os/IVold.aidl
index 50abc26..fa6cc2d 100644
--- a/binder/android/os/IVold.aidl
+++ b/binder/android/os/IVold.aidl
@@ -112,6 +112,7 @@
void commitChanges();
void prepareCheckpoint();
void restoreCheckpoint(@utf8InCpp String device);
+ void restoreCheckpointPart(@utf8InCpp String device, int count);
void markBootAttempt();
boolean supportsCheckpoint();
diff --git a/vdc.cpp b/vdc.cpp
index 35775a7..d01fb49 100644
--- a/vdc.cpp
+++ b/vdc.cpp
@@ -127,6 +127,10 @@
checkStatus(vold->prepareCheckpoint());
} else if (args[0] == "checkpoint" && args[1] == "restoreCheckpoint" && args.size() == 3) {
checkStatus(vold->restoreCheckpoint(args[2]));
+ } else if (args[0] == "checkpoint" && args[1] == "restoreCheckpointPart" && args.size() == 4) {
+ int count;
+ if (!android::base::ParseInt(args[3], &count)) exit(EINVAL);
+ checkStatus(vold->restoreCheckpointPart(args[2], count));
} else if (args[0] == "checkpoint" && args[1] == "markBootAttempt" && args.size() == 2) {
checkStatus(vold->markBootAttempt());
} else if (args[0] == "checkpoint" && args[1] == "abortChanges" && args.size() == 2) {