summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--applypatch/applypatch.cpp1044
-rw-r--r--applypatch/imgdiff.cpp32
-rw-r--r--error_code.h83
-rw-r--r--minadbd/minadbd_services.cpp2
-rw-r--r--minui/font_10x18.h8
-rw-r--r--minui/graphics.cpp114
-rw-r--r--minui/graphics_adf.cpp8
-rw-r--r--minui/graphics_fbdev.cpp2
-rw-r--r--minui/minui.h16
-rw-r--r--minui/resources.cpp12
-rw-r--r--otafault/ota_io.h10
-rw-r--r--screen_ui.cpp10
-rw-r--r--tests/Android.mk2
-rw-r--r--tests/common/test_constants.h11
-rw-r--r--tests/component/updater_test.cpp221
-rw-r--r--tests/unit/zip_test.cpp3
-rw-r--r--updater/install.cpp217
-rw-r--r--wear_ui.cpp12
18 files changed, 1014 insertions, 793 deletions
diff --git a/applypatch/applypatch.cpp b/applypatch/applypatch.cpp
index 9d505f9f6..9b84fa104 100644
--- a/applypatch/applypatch.cpp
+++ b/applypatch/applypatch.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "applypatch/applypatch.h"
+
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
@@ -27,16 +29,18 @@
#include <memory>
#include <string>
+#include <utility>
+#include <vector>
+#include <android-base/parseint.h>
#include <android-base/strings.h>
+#include <openssl/sha.h>
-#include "openssl/sha.h"
-#include "applypatch/applypatch.h"
#include "edify/expr.h"
#include "ota_io.h"
#include "print_sha1.h"
-static int LoadPartitionContents(const char* filename, FileContents* file);
+static int LoadPartitionContents(const std::string& filename, FileContents* file);
static ssize_t FileSink(const unsigned char* data, ssize_t len, void* token);
static int GenerateTarget(FileContents* source_file,
const Value* source_patch_value,
@@ -48,39 +52,34 @@ static int GenerateTarget(FileContents* source_file,
size_t target_size,
const Value* bonus_data);
-// Read a file into memory; store the file contents and associated
-// metadata in *file.
-//
+// Read a file into memory; store the file contents and associated metadata in *file.
// Return 0 on success.
int LoadFileContents(const char* filename, FileContents* file) {
- // A special 'filename' beginning with "EMMC:" means to
- // load the contents of a partition.
- if (strncmp(filename, "EMMC:", 5) == 0) {
- return LoadPartitionContents(filename, file);
- }
+ // A special 'filename' beginning with "EMMC:" means to load the contents of a partition.
+ if (strncmp(filename, "EMMC:", 5) == 0) {
+ return LoadPartitionContents(filename, file);
+ }
- if (stat(filename, &file->st) != 0) {
- printf("failed to stat \"%s\": %s\n", filename, strerror(errno));
- return -1;
- }
+ if (stat(filename, &file->st) == -1) {
+ printf("failed to stat \"%s\": %s\n", filename, strerror(errno));
+ return -1;
+ }
- std::vector<unsigned char> data(file->st.st_size);
- FILE* f = ota_fopen(filename, "rb");
- if (f == NULL) {
- printf("failed to open \"%s\": %s\n", filename, strerror(errno));
- return -1;
- }
+ std::vector<unsigned char> data(file->st.st_size);
+ std::unique_ptr<FILE, decltype(&ota_fclose)> f(ota_fopen(filename, "rb"), ota_fclose);
+ if (!f) {
+ printf("failed to open \"%s\": %s\n", filename, strerror(errno));
+ return -1;
+ }
- size_t bytes_read = ota_fread(data.data(), 1, data.size(), f);
- if (bytes_read != data.size()) {
- printf("short read of \"%s\" (%zu bytes of %zu)\n", filename, bytes_read, data.size());
- ota_fclose(f);
- return -1;
- }
- ota_fclose(f);
- file->data = std::move(data);
- SHA1(file->data.data(), file->data.size(), file->sha1);
- return 0;
+ size_t bytes_read = ota_fread(data.data(), 1, data.size(), f.get());
+ if (bytes_read != data.size()) {
+ printf("short read of \"%s\" (%zu bytes of %zu)\n", filename, bytes_read, data.size());
+ return -1;
+ }
+ file->data = std::move(data);
+ SHA1(file->data.data(), file->data.size(), file->sha1);
+ return 0;
}
// Load the contents of an EMMC partition into the provided
@@ -97,284 +96,257 @@ int LoadFileContents(const char* filename, FileContents* file) {
// "end-of-file" marker), so the caller must specify the possible
// lengths and the hash of the data, and we'll do the load expecting
// to find one of those hashes.
-static int LoadPartitionContents(const char* filename, FileContents* file) {
- std::string copy(filename);
- std::vector<std::string> pieces = android::base::Split(copy, ":");
- if (pieces.size() < 4 || pieces.size() % 2 != 0) {
- printf("LoadPartitionContents called with bad filename (%s)\n", filename);
- return -1;
- }
-
- if (pieces[0] != "EMMC") {
- printf("LoadPartitionContents called with bad filename (%s)\n", filename);
+static int LoadPartitionContents(const std::string& filename, FileContents* file) {
+ std::vector<std::string> pieces = android::base::Split(filename, ":");
+ if (pieces.size() < 4 || pieces.size() % 2 != 0 || pieces[0] != "EMMC") {
+ printf("LoadPartitionContents called with bad filename \"%s\"\n", filename.c_str());
+ return -1;
+ }
+
+ size_t pair_count = (pieces.size() - 2) / 2; // # of (size, sha1) pairs in filename
+ std::vector<std::pair<size_t, std::string>> pairs;
+ for (size_t i = 0; i < pair_count; ++i) {
+ size_t size;
+ if (!android::base::ParseUint(pieces[i * 2 + 2], &size) || size == 0) {
+ printf("LoadPartitionContents called with bad size \"%s\"\n", pieces[i * 2 + 2].c_str());
+ return -1;
+ }
+ pairs.push_back({ size, pieces[i * 2 + 3] });
+ }
+
+ // Sort the pairs array so that they are in order of increasing size.
+ std::sort(pairs.begin(), pairs.end());
+
+ const char* partition = pieces[1].c_str();
+ std::unique_ptr<FILE, decltype(&ota_fclose)> dev(ota_fopen(partition, "rb"), ota_fclose);
+ if (!dev) {
+ printf("failed to open emmc partition \"%s\": %s\n", partition, strerror(errno));
+ return -1;
+ }
+
+ SHA_CTX sha_ctx;
+ SHA1_Init(&sha_ctx);
+
+ // Allocate enough memory to hold the largest size.
+ std::vector<unsigned char> buffer(pairs[pair_count - 1].first);
+ unsigned char* buffer_ptr = buffer.data();
+ size_t buffer_size = 0; // # bytes read so far
+ bool found = false;
+
+ for (const auto& pair : pairs) {
+ size_t current_size = pair.first;
+ const std::string& current_sha1 = pair.second;
+
+ // Read enough additional bytes to get us up to the next size. (Again,
+ // we're trying the possibilities in order of increasing size).
+ size_t next = current_size - buffer_size;
+ if (next > 0) {
+ size_t read = ota_fread(buffer_ptr, 1, next, dev.get());
+ if (next != read) {
+ printf("short read (%zu bytes of %zu) for partition \"%s\"\n", read, next, partition);
return -1;
- }
- const char* partition = pieces[1].c_str();
-
- size_t pairs = (pieces.size() - 2) / 2; // # of (size, sha1) pairs in filename
- std::vector<size_t> index(pairs);
- std::vector<size_t> size(pairs);
- std::vector<std::string> sha1sum(pairs);
-
- for (size_t i = 0; i < pairs; ++i) {
- size[i] = strtol(pieces[i*2+2].c_str(), NULL, 10);
- if (size[i] == 0) {
- printf("LoadPartitionContents called with bad size (%s)\n", filename);
- return -1;
- }
- sha1sum[i] = pieces[i*2+3].c_str();
- index[i] = i;
+ }
+ SHA1_Update(&sha_ctx, buffer_ptr, read);
+ buffer_size += read;
+ buffer_ptr += read;
}
- // Sort the index[] array so it indexes the pairs in order of increasing size.
- sort(index.begin(), index.end(),
- [&](const size_t& i, const size_t& j) {
- return (size[i] < size[j]);
- }
- );
+ // Duplicate the SHA context and finalize the duplicate so we can
+ // check it against this pair's expected hash.
+ SHA_CTX temp_ctx;
+ memcpy(&temp_ctx, &sha_ctx, sizeof(SHA_CTX));
+ uint8_t sha_so_far[SHA_DIGEST_LENGTH];
+ SHA1_Final(sha_so_far, &temp_ctx);
- FILE* dev = ota_fopen(partition, "rb");
- if (dev == NULL) {
- printf("failed to open emmc partition \"%s\": %s\n", partition, strerror(errno));
- return -1;
- }
-
- SHA_CTX sha_ctx;
- SHA1_Init(&sha_ctx);
uint8_t parsed_sha[SHA_DIGEST_LENGTH];
-
- // Allocate enough memory to hold the largest size.
- std::vector<unsigned char> data(size[index[pairs-1]]);
- char* p = reinterpret_cast<char*>(data.data());
- size_t data_size = 0; // # bytes read so far
- bool found = false;
-
- for (size_t i = 0; i < pairs; ++i) {
- // Read enough additional bytes to get us up to the next size. (Again,
- // we're trying the possibilities in order of increasing size).
- size_t next = size[index[i]] - data_size;
- if (next > 0) {
- size_t read = ota_fread(p, 1, next, dev);
- if (next != read) {
- printf("short read (%zu bytes of %zu) for partition \"%s\"\n",
- read, next, partition);
- return -1;
- }
- SHA1_Update(&sha_ctx, p, read);
- data_size += read;
- p += read;
- }
-
- // Duplicate the SHA context and finalize the duplicate so we can
- // check it against this pair's expected hash.
- SHA_CTX temp_ctx;
- memcpy(&temp_ctx, &sha_ctx, sizeof(SHA_CTX));
- uint8_t sha_so_far[SHA_DIGEST_LENGTH];
- SHA1_Final(sha_so_far, &temp_ctx);
-
- if (ParseSha1(sha1sum[index[i]].c_str(), parsed_sha) != 0) {
- printf("failed to parse sha1 %s in %s\n", sha1sum[index[i]].c_str(), filename);
- return -1;
- }
-
- if (memcmp(sha_so_far, parsed_sha, SHA_DIGEST_LENGTH) == 0) {
- // we have a match. stop reading the partition; we'll return
- // the data we've read so far.
- printf("partition read matched size %zu sha %s\n",
- size[index[i]], sha1sum[index[i]].c_str());
- found = true;
- break;
- }
+ if (ParseSha1(current_sha1.c_str(), parsed_sha) != 0) {
+ printf("failed to parse SHA-1 %s in %s\n", current_sha1.c_str(), filename.c_str());
+ return -1;
}
- ota_fclose(dev);
-
- if (!found) {
- // Ran off the end of the list of (size,sha1) pairs without finding a match.
- printf("contents of partition \"%s\" didn't match %s\n", partition, filename);
- return -1;
+ if (memcmp(sha_so_far, parsed_sha, SHA_DIGEST_LENGTH) == 0) {
+ // We have a match. Stop reading the partition; we'll return the data we've read so far.
+ printf("partition read matched size %zu SHA-1 %s\n", current_size, current_sha1.c_str());
+ found = true;
+ break;
}
+ }
- SHA1_Final(file->sha1, &sha_ctx);
+ if (!found) {
+ // Ran off the end of the list of (size, sha1) pairs without finding a match.
+ printf("contents of partition \"%s\" didn't match %s\n", partition, filename.c_str());
+ return -1;
+ }
- data.resize(data_size);
- file->data = std::move(data);
- // Fake some stat() info.
- file->st.st_mode = 0644;
- file->st.st_uid = 0;
- file->st.st_gid = 0;
+ SHA1_Final(file->sha1, &sha_ctx);
- return 0;
+ buffer.resize(buffer_size);
+ file->data = std::move(buffer);
+ // Fake some stat() info.
+ file->st.st_mode = 0644;
+ file->st.st_uid = 0;
+ file->st.st_gid = 0;
+
+ return 0;
}
// Save the contents of the given FileContents object under the given
// filename. Return 0 on success.
int SaveFileContents(const char* filename, const FileContents* file) {
- int fd = ota_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, S_IRUSR | S_IWUSR);
- if (fd < 0) {
- printf("failed to open \"%s\" for write: %s\n", filename, strerror(errno));
- return -1;
- }
+ unique_fd fd(ota_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, S_IRUSR | S_IWUSR));
+ if (fd == -1) {
+ printf("failed to open \"%s\" for write: %s\n", filename, strerror(errno));
+ return -1;
+ }
- ssize_t bytes_written = FileSink(file->data.data(), file->data.size(), &fd);
- if (bytes_written != static_cast<ssize_t>(file->data.size())) {
- printf("short write of \"%s\" (%zd bytes of %zu) (%s)\n",
- filename, bytes_written, file->data.size(), strerror(errno));
- ota_close(fd);
- return -1;
- }
- if (ota_fsync(fd) != 0) {
- printf("fsync of \"%s\" failed: %s\n", filename, strerror(errno));
- return -1;
- }
- if (ota_close(fd) != 0) {
- printf("close of \"%s\" failed: %s\n", filename, strerror(errno));
- return -1;
- }
+ ssize_t bytes_written = FileSink(file->data.data(), file->data.size(), &fd);
+ if (bytes_written != static_cast<ssize_t>(file->data.size())) {
+ printf("short write of \"%s\" (%zd bytes of %zu): %s\n", filename, bytes_written,
+ file->data.size(), strerror(errno));
+ return -1;
+ }
+ if (ota_fsync(fd) != 0) {
+ printf("fsync of \"%s\" failed: %s\n", filename, strerror(errno));
+ return -1;
+ }
+ if (ota_close(fd) != 0) {
+ printf("close of \"%s\" failed: %s\n", filename, strerror(errno));
+ return -1;
+ }
- if (chmod(filename, file->st.st_mode) != 0) {
- printf("chmod of \"%s\" failed: %s\n", filename, strerror(errno));
- return -1;
- }
- if (chown(filename, file->st.st_uid, file->st.st_gid) != 0) {
- printf("chown of \"%s\" failed: %s\n", filename, strerror(errno));
- return -1;
- }
+ if (chmod(filename, file->st.st_mode) != 0) {
+ printf("chmod of \"%s\" failed: %s\n", filename, strerror(errno));
+ return -1;
+ }
+ if (chown(filename, file->st.st_uid, file->st.st_gid) != 0) {
+ printf("chown of \"%s\" failed: %s\n", filename, strerror(errno));
+ return -1;
+ }
- return 0;
+ return 0;
}
// Write a memory buffer to 'target' partition, a string of the form
// "EMMC:<partition_device>[:...]". The target name
// might contain multiple colons, but WriteToPartition() only uses the first
// two and ignores the rest. Return 0 on success.
-int WriteToPartition(const unsigned char* data, size_t len, const char* target) {
- std::string copy(target);
- std::vector<std::string> pieces = android::base::Split(copy, ":");
+int WriteToPartition(const unsigned char* data, size_t len, const std::string& target) {
+ std::vector<std::string> pieces = android::base::Split(target, ":");
+ if (pieces.size() < 2 || pieces[0] != "EMMC") {
+ printf("WriteToPartition called with bad target (%s)\n", target.c_str());
+ return -1;
+ }
- if (pieces.size() < 2) {
- printf("WriteToPartition called with bad target (%s)\n", target);
+ const char* partition = pieces[1].c_str();
+ unique_fd fd(ota_open(partition, O_RDWR));
+ if (fd == -1) {
+ printf("failed to open %s: %s\n", partition, strerror(errno));
+ return -1;
+ }
+
+ size_t start = 0;
+ bool success = false;
+ for (size_t attempt = 0; attempt < 2; ++attempt) {
+ if (TEMP_FAILURE_RETRY(lseek(fd, start, SEEK_SET)) == -1) {
+ printf("failed seek on %s: %s\n", partition, strerror(errno));
+ return -1;
+ }
+ while (start < len) {
+ size_t to_write = len - start;
+ if (to_write > 1 << 20) to_write = 1 << 20;
+
+ ssize_t written = TEMP_FAILURE_RETRY(ota_write(fd, data + start, to_write));
+ if (written == -1) {
+ printf("failed write writing to %s: %s\n", partition, strerror(errno));
return -1;
+ }
+ start += written;
}
- if (pieces[0] != "EMMC") {
- printf("WriteToPartition called with bad target (%s)\n", target);
- return -1;
+ if (ota_fsync(fd) != 0) {
+ printf("failed to sync to %s: %s\n", partition, strerror(errno));
+ return -1;
+ }
+ if (ota_close(fd) != 0) {
+ printf("failed to close %s: %s\n", partition, strerror(errno));
+ return -1;
}
- const char* partition = pieces[1].c_str();
- size_t start = 0;
- bool success = false;
- int fd = ota_open(partition, O_RDWR | O_SYNC);
- if (fd < 0) {
- printf("failed to open %s: %s\n", partition, strerror(errno));
- return -1;
+ fd.reset(ota_open(partition, O_RDONLY));
+ if (fd == -1) {
+ printf("failed to reopen %s for verify: %s\n", partition, strerror(errno));
+ return -1;
}
- for (size_t attempt = 0; attempt < 2; ++attempt) {
- if (TEMP_FAILURE_RETRY(lseek(fd, start, SEEK_SET)) == -1) {
- printf("failed seek on %s: %s\n", partition, strerror(errno));
- return -1;
- }
- while (start < len) {
- size_t to_write = len - start;
- if (to_write > 1<<20) to_write = 1<<20;
-
- ssize_t written = TEMP_FAILURE_RETRY(ota_write(fd, data+start, to_write));
- if (written == -1) {
- printf("failed write writing to %s: %s\n", partition, strerror(errno));
- return -1;
- }
- start += written;
- }
- if (ota_fsync(fd) != 0) {
- printf("failed to sync to %s (%s)\n", partition, strerror(errno));
- return -1;
- }
- if (ota_close(fd) != 0) {
- printf("failed to close %s (%s)\n", partition, strerror(errno));
- return -1;
- }
- fd = ota_open(partition, O_RDONLY);
- if (fd < 0) {
- printf("failed to reopen %s for verify (%s)\n", partition, strerror(errno));
- return -1;
- }
+ // Drop caches so our subsequent verification read won't just be reading the cache.
+ sync();
+ unique_fd dc(ota_open("/proc/sys/vm/drop_caches", O_WRONLY));
+ if (TEMP_FAILURE_RETRY(ota_write(dc, "3\n", 2)) == -1) {
+ printf("write to /proc/sys/vm/drop_caches failed: %s\n", strerror(errno));
+ } else {
+ printf(" caches dropped\n");
+ }
+ ota_close(dc);
+ sleep(1);
- // Drop caches so our subsequent verification read
- // won't just be reading the cache.
- sync();
- int dc = ota_open("/proc/sys/vm/drop_caches", O_WRONLY);
- if (TEMP_FAILURE_RETRY(ota_write(dc, "3\n", 2)) == -1) {
- printf("write to /proc/sys/vm/drop_caches failed: %s\n", strerror(errno));
- } else {
- printf(" caches dropped\n");
- }
- ota_close(dc);
- sleep(1);
+ // Verify.
+ if (TEMP_FAILURE_RETRY(lseek(fd, 0, SEEK_SET)) == -1) {
+ printf("failed to seek back to beginning of %s: %s\n", partition, strerror(errno));
+ return -1;
+ }
- // verify
- if (TEMP_FAILURE_RETRY(lseek(fd, 0, SEEK_SET)) == -1) {
- printf("failed to seek back to beginning of %s: %s\n",
- partition, strerror(errno));
- return -1;
+ unsigned char buffer[4096];
+ start = len;
+ for (size_t p = 0; p < len; p += sizeof(buffer)) {
+ size_t to_read = len - p;
+ if (to_read > sizeof(buffer)) {
+ to_read = sizeof(buffer);
+ }
+
+ size_t so_far = 0;
+ while (so_far < to_read) {
+ ssize_t read_count = TEMP_FAILURE_RETRY(ota_read(fd, buffer + so_far, to_read - so_far));
+ if (read_count == -1) {
+ printf("verify read error %s at %zu: %s\n", partition, p, strerror(errno));
+ return -1;
+ } else if (read_count == 0) {
+ printf("verify read reached unexpected EOF, %s at %zu\n", partition, p);
+ return -1;
}
- unsigned char buffer[4096];
- start = len;
- for (size_t p = 0; p < len; p += sizeof(buffer)) {
- size_t to_read = len - p;
- if (to_read > sizeof(buffer)) {
- to_read = sizeof(buffer);
- }
-
- size_t so_far = 0;
- while (so_far < to_read) {
- ssize_t read_count =
- TEMP_FAILURE_RETRY(ota_read(fd, buffer+so_far, to_read-so_far));
- if (read_count == -1) {
- printf("verify read error %s at %zu: %s\n",
- partition, p, strerror(errno));
- return -1;
- } else if (read_count == 0) {
- printf("verify read reached unexpected EOF, %s at %zu\n", partition, p);
- return -1;
- }
- if (static_cast<size_t>(read_count) < to_read) {
- printf("short verify read %s at %zu: %zd %zu %s\n",
- partition, p, read_count, to_read, strerror(errno));
- }
- so_far += read_count;
- }
-
- if (memcmp(buffer, data+p, to_read) != 0) {
- printf("verification failed starting at %zu\n", p);
- start = p;
- break;
- }
+ if (static_cast<size_t>(read_count) < to_read) {
+ printf("short verify read %s at %zu: %zd %zu\n", partition, p, read_count, to_read);
}
+ so_far += read_count;
+ }
- if (start == len) {
- printf("verification read succeeded (attempt %zu)\n", attempt+1);
- success = true;
- break;
- }
+ if (memcmp(buffer, data + p, to_read) != 0) {
+ printf("verification failed starting at %zu\n", p);
+ start = p;
+ break;
+ }
}
- if (!success) {
- printf("failed to verify after all attempts\n");
- return -1;
+ if (start == len) {
+ printf("verification read succeeded (attempt %zu)\n", attempt + 1);
+ success = true;
+ break;
}
+ }
- if (ota_close(fd) != 0) {
- printf("error closing %s (%s)\n", partition, strerror(errno));
- return -1;
- }
- sync();
+ if (!success) {
+ printf("failed to verify after all attempts\n");
+ return -1;
+ }
- return 0;
-}
+ if (ota_close(fd) == -1) {
+ printf("error closing %s: %s\n", partition, strerror(errno));
+ return -1;
+ }
+ sync();
+ return 0;
+}
// Take a string 'str' of 40 hex digits and parse it into the 20
// byte array 'digest'. 'str' may contain only the digest or be of
@@ -409,48 +381,46 @@ int ParseSha1(const char* str, uint8_t* digest) {
// Return the index of the match on success, or -1 if no match is
// found.
int FindMatchingPatch(uint8_t* sha1, const std::vector<std::string>& patch_sha1_str) {
- for (size_t i = 0; i < patch_sha1_str.size(); ++i) {
- uint8_t patch_sha1[SHA_DIGEST_LENGTH];
- if (ParseSha1(patch_sha1_str[i].c_str(), patch_sha1) == 0 &&
- memcmp(patch_sha1, sha1, SHA_DIGEST_LENGTH) == 0) {
- return i;
- }
- }
- return -1;
+ for (size_t i = 0; i < patch_sha1_str.size(); ++i) {
+ uint8_t patch_sha1[SHA_DIGEST_LENGTH];
+ if (ParseSha1(patch_sha1_str[i].c_str(), patch_sha1) == 0 &&
+ memcmp(patch_sha1, sha1, SHA_DIGEST_LENGTH) == 0) {
+ return i;
+ }
+ }
+ return -1;
}
// Returns 0 if the contents of the file (argv[2]) or the cached file
// match any of the sha1's on the command line (argv[3:]). Returns
// nonzero otherwise.
int applypatch_check(const char* filename, const std::vector<std::string>& patch_sha1_str) {
- FileContents file;
-
- // It's okay to specify no sha1s; the check will pass if the
- // LoadFileContents is successful. (Useful for reading
- // partitions, where the filename encodes the sha1s; no need to
- // check them twice.)
- if (LoadFileContents(filename, &file) != 0 ||
- (patch_sha1_str.size() > 0 && FindMatchingPatch(file.sha1, patch_sha1_str) < 0)) {
- printf("file \"%s\" doesn't have any of expected "
- "sha1 sums; checking cache\n", filename);
-
- // If the source file is missing or corrupted, it might be because
- // we were killed in the middle of patching it. A copy of it
- // should have been made in CACHE_TEMP_SOURCE. If that file
- // exists and matches the sha1 we're looking for, the check still
- // passes.
-
- if (LoadFileContents(CACHE_TEMP_SOURCE, &file) != 0) {
- printf("failed to load cache file\n");
- return 1;
- }
-
- if (FindMatchingPatch(file.sha1, patch_sha1_str) < 0) {
- printf("cache bits don't match any sha1 for \"%s\"\n", filename);
- return 1;
- }
- }
- return 0;
+ FileContents file;
+
+ // It's okay to specify no sha1s; the check will pass if the
+ // LoadFileContents is successful. (Useful for reading
+ // partitions, where the filename encodes the sha1s; no need to
+ // check them twice.)
+ if (LoadFileContents(filename, &file) != 0 ||
+ (!patch_sha1_str.empty() && FindMatchingPatch(file.sha1, patch_sha1_str) < 0)) {
+ printf("file \"%s\" doesn't have any of expected sha1 sums; checking cache\n", filename);
+
+ // If the source file is missing or corrupted, it might be because
+ // we were killed in the middle of patching it. A copy of it
+ // should have been made in CACHE_TEMP_SOURCE. If that file
+ // exists and matches the sha1 we're looking for, the check still
+ // passes.
+ if (LoadFileContents(CACHE_TEMP_SOURCE, &file) != 0) {
+ printf("failed to load cache file\n");
+ return 1;
+ }
+
+ if (FindMatchingPatch(file.sha1, patch_sha1_str) < 0) {
+ printf("cache bits don't match any sha1 for \"%s\"\n", filename);
+ return 1;
+ }
+ }
+ return 0;
}
int ShowLicenses() {
@@ -544,10 +514,8 @@ int applypatch(const char* source_filename,
return 1;
}
- FileContents copy_file;
FileContents source_file;
const Value* source_patch_value = nullptr;
- const Value* copy_patch_value = nullptr;
// We try to load the target file into the source_file object.
if (LoadFileContents(target_filename, &source_file) == 0) {
@@ -575,6 +543,8 @@ int applypatch(const char* source_filename,
}
}
+ FileContents copy_file;
+ const Value* copy_patch_value = nullptr;
if (source_patch_value == nullptr) {
source_file.data.clear();
printf("source file is bad; trying copy\n");
@@ -612,50 +582,49 @@ int applypatch(const char* source_filename,
*/
int applypatch_flash(const char* source_filename, const char* target_filename,
const char* target_sha1_str, size_t target_size) {
- printf("flash %s: ", target_filename);
-
- uint8_t target_sha1[SHA_DIGEST_LENGTH];
- if (ParseSha1(target_sha1_str, target_sha1) != 0) {
- printf("failed to parse tgt-sha1 \"%s\"\n", target_sha1_str);
- return 1;
- }
-
- FileContents source_file;
- std::string target_str(target_filename);
-
- std::vector<std::string> pieces = android::base::Split(target_str, ":");
- if (pieces.size() != 2 || pieces[0] != "EMMC") {
- printf("invalid target name \"%s\"", target_filename);
- return 1;
- }
-
- // Load the target into the source_file object to see if already applied.
- pieces.push_back(std::to_string(target_size));
- pieces.push_back(target_sha1_str);
- std::string fullname = android::base::Join(pieces, ':');
- if (LoadPartitionContents(fullname.c_str(), &source_file) == 0 &&
- memcmp(source_file.sha1, target_sha1, SHA_DIGEST_LENGTH) == 0) {
- // The early-exit case: the image was already applied, this partition
- // has the desired hash, nothing for us to do.
- printf("already %s\n", short_sha1(target_sha1).c_str());
- return 0;
- }
-
- if (LoadFileContents(source_filename, &source_file) == 0) {
- if (memcmp(source_file.sha1, target_sha1, SHA_DIGEST_LENGTH) != 0) {
- // The source doesn't have desired checksum.
- printf("source \"%s\" doesn't have expected sha1 sum\n", source_filename);
- printf("expected: %s, found: %s\n", short_sha1(target_sha1).c_str(),
- short_sha1(source_file.sha1).c_str());
- return 1;
- }
- }
-
- if (WriteToPartition(source_file.data.data(), target_size, target_filename) != 0) {
- printf("write of copied data to %s failed\n", target_filename);
- return 1;
- }
+ printf("flash %s: ", target_filename);
+
+ uint8_t target_sha1[SHA_DIGEST_LENGTH];
+ if (ParseSha1(target_sha1_str, target_sha1) != 0) {
+ printf("failed to parse tgt-sha1 \"%s\"\n", target_sha1_str);
+ return 1;
+ }
+
+ std::string target_str(target_filename);
+ std::vector<std::string> pieces = android::base::Split(target_str, ":");
+ if (pieces.size() != 2 || pieces[0] != "EMMC") {
+ printf("invalid target name \"%s\"", target_filename);
+ return 1;
+ }
+
+ // Load the target into the source_file object to see if already applied.
+ pieces.push_back(std::to_string(target_size));
+ pieces.push_back(target_sha1_str);
+ std::string fullname = android::base::Join(pieces, ':');
+ FileContents source_file;
+ if (LoadPartitionContents(fullname, &source_file) == 0 &&
+ memcmp(source_file.sha1, target_sha1, SHA_DIGEST_LENGTH) == 0) {
+ // The early-exit case: the image was already applied, this partition
+ // has the desired hash, nothing for us to do.
+ printf("already %s\n", short_sha1(target_sha1).c_str());
return 0;
+ }
+
+ if (LoadFileContents(source_filename, &source_file) == 0) {
+ if (memcmp(source_file.sha1, target_sha1, SHA_DIGEST_LENGTH) != 0) {
+ // The source doesn't have desired checksum.
+ printf("source \"%s\" doesn't have expected sha1 sum\n", source_filename);
+ printf("expected: %s, found: %s\n", short_sha1(target_sha1).c_str(),
+ short_sha1(source_file.sha1).c_str());
+ return 1;
+ }
+ }
+
+ if (WriteToPartition(source_file.data.data(), target_size, target_filename) != 0) {
+ printf("write of copied data to %s failed\n", target_filename);
+ return 1;
+ }
+ return 0;
}
static int GenerateTarget(FileContents* source_file,
@@ -667,221 +636,214 @@ static int GenerateTarget(FileContents* source_file,
const uint8_t target_sha1[SHA_DIGEST_LENGTH],
size_t target_size,
const Value* bonus_data) {
- int retry = 1;
- SHA_CTX ctx;
- std::string memory_sink_str;
- FileContents* source_to_use;
- int made_copy = 0;
-
- bool target_is_partition = (strncmp(target_filename, "EMMC:", 5) == 0);
- const std::string tmp_target_filename = std::string(target_filename) + ".patch";
-
- // assume that target_filename (eg "/system/app/Foo.apk") is located
- // on the same filesystem as its top-level directory ("/system").
- // We need something that exists for calling statfs().
- std::string target_fs = target_filename;
- auto slash_pos = target_fs.find('/', 1);
- if (slash_pos != std::string::npos) {
- target_fs.resize(slash_pos);
- }
-
- const Value* patch;
- if (source_patch_value != NULL) {
- source_to_use = source_file;
- patch = source_patch_value;
- } else {
- source_to_use = copy_file;
- patch = copy_patch_value;
- }
- if (patch->type != VAL_BLOB) {
- printf("patch is not a blob\n");
+ // assume that target_filename (eg "/system/app/Foo.apk") is located
+ // on the same filesystem as its top-level directory ("/system").
+ // We need something that exists for calling statfs().
+ std::string target_fs = target_filename;
+ auto slash_pos = target_fs.find('/', 1);
+ if (slash_pos != std::string::npos) {
+ target_fs.resize(slash_pos);
+ }
+
+ FileContents* source_to_use;
+ const Value* patch;
+ if (source_patch_value != nullptr) {
+ source_to_use = source_file;
+ patch = source_patch_value;
+ } else {
+ source_to_use = copy_file;
+ patch = copy_patch_value;
+ }
+
+ if (patch->type != VAL_BLOB) {
+ printf("patch is not a blob\n");
+ return 1;
+ }
+
+ const char* header = &patch->data[0];
+ size_t header_bytes_read = patch->data.size();
+ bool use_bsdiff = false;
+ if (header_bytes_read >= 8 && memcmp(header, "BSDIFF40", 8) == 0) {
+ use_bsdiff = true;
+ } else if (header_bytes_read >= 8 && memcmp(header, "IMGDIFF2", 8) == 0) {
+ use_bsdiff = false;
+ } else {
+ printf("Unknown patch file format\n");
+ return 1;
+ }
+
+ bool target_is_partition = (strncmp(target_filename, "EMMC:", 5) == 0);
+ const std::string tmp_target_filename = std::string(target_filename) + ".patch";
+
+ int retry = 1;
+ bool made_copy = false;
+ SHA_CTX ctx;
+ std::string memory_sink_str; // Don't need to reserve space.
+ do {
+ // Is there enough room in the target filesystem to hold the patched file?
+
+ if (target_is_partition) {
+ // If the target is a partition, we're actually going to
+ // write the output to /tmp and then copy it to the
+ // partition. statfs() always returns 0 blocks free for
+ // /tmp, so instead we'll just assume that /tmp has enough
+ // space to hold the file.
+
+ // We still write the original source to cache, in case
+ // the partition write is interrupted.
+ if (MakeFreeSpaceOnCache(source_file->data.size()) < 0) {
+ printf("not enough free space on /cache\n");
return 1;
- }
- const char* header = &patch->data[0];
- size_t header_bytes_read = patch->data.size();
- bool use_bsdiff = false;
- if (header_bytes_read >= 8 && memcmp(header, "BSDIFF40", 8) == 0) {
- use_bsdiff = true;
- } else if (header_bytes_read >= 8 && memcmp(header, "IMGDIFF2", 8) == 0) {
- use_bsdiff = false;
- } else {
- printf("Unknown patch file format\n");
+ }
+ if (SaveFileContents(CACHE_TEMP_SOURCE, source_file) < 0) {
+ printf("failed to back up source file\n");
return 1;
- }
-
- do {
- // Is there enough room in the target filesystem to hold the patched
- // file?
-
- if (target_is_partition) {
- // If the target is a partition, we're actually going to
- // write the output to /tmp and then copy it to the
- // partition. statfs() always returns 0 blocks free for
- // /tmp, so instead we'll just assume that /tmp has enough
- // space to hold the file.
-
- // We still write the original source to cache, in case
- // the partition write is interrupted.
- if (MakeFreeSpaceOnCache(source_file->data.size()) < 0) {
- printf("not enough free space on /cache\n");
- return 1;
- }
- if (SaveFileContents(CACHE_TEMP_SOURCE, source_file) < 0) {
- printf("failed to back up source file\n");
- return 1;
- }
- made_copy = 1;
- retry = 0;
- } else {
- int enough_space = 0;
- if (retry > 0) {
- size_t free_space = FreeSpaceForFile(target_fs.c_str());
- enough_space =
- (free_space > (256 << 10)) && // 256k (two-block) minimum
- (free_space > (target_size * 3 / 2)); // 50% margin of error
- if (!enough_space) {
- printf("target %zu bytes; free space %zu bytes; retry %d; enough %d\n",
- target_size, free_space, retry, enough_space);
- }
- }
-
- if (!enough_space) {
- retry = 0;
- }
-
- if (!enough_space && source_patch_value != NULL) {
- // Using the original source, but not enough free space. First
- // copy the source file to cache, then delete it from the original
- // location.
-
- if (strncmp(source_filename, "EMMC:", 5) == 0) {
- // It's impossible to free space on the target filesystem by
- // deleting the source if the source is a partition. If
- // we're ever in a state where we need to do this, fail.
- printf("not enough free space for target but source is partition\n");
- return 1;
- }
-
- if (MakeFreeSpaceOnCache(source_file->data.size()) < 0) {
- printf("not enough free space on /cache\n");
- return 1;
- }
-
- if (SaveFileContents(CACHE_TEMP_SOURCE, source_file) < 0) {
- printf("failed to back up source file\n");
- return 1;
- }
- made_copy = 1;
- unlink(source_filename);
-
- size_t free_space = FreeSpaceForFile(target_fs.c_str());
- printf("(now %zu bytes free for target) ", free_space);
- }
- }
-
-
- SinkFn sink = NULL;
- void* token = NULL;
- int output_fd = -1;
- if (target_is_partition) {
- // We store the decoded output in memory.
- sink = MemorySink;
- token = &memory_sink_str;
- } else {
- // We write the decoded output to "<tgt-file>.patch".
- output_fd = ota_open(tmp_target_filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_SYNC,
- S_IRUSR | S_IWUSR);
- if (output_fd < 0) {
- printf("failed to open output file %s: %s\n", tmp_target_filename.c_str(),
- strerror(errno));
- return 1;
- }
- sink = FileSink;
- token = &output_fd;
+ }
+ made_copy = true;
+ retry = 0;
+ } else {
+ bool enough_space = false;
+ if (retry > 0) {
+ size_t free_space = FreeSpaceForFile(target_fs.c_str());
+ enough_space = (free_space > (256 << 10)) && // 256k (two-block) minimum
+ (free_space > (target_size * 3 / 2)); // 50% margin of error
+ if (!enough_space) {
+ printf("target %zu bytes; free space %zu bytes; retry %d; enough %d\n", target_size,
+ free_space, retry, enough_space);
}
+ }
+ if (!enough_space) {
+ retry = 0;
+ }
- SHA1_Init(&ctx);
+ if (!enough_space && source_patch_value != nullptr) {
+ // Using the original source, but not enough free space. First
+ // copy the source file to cache, then delete it from the original
+ // location.
- int result;
- if (use_bsdiff) {
- result = ApplyBSDiffPatch(source_to_use->data.data(), source_to_use->data.size(),
- patch, 0, sink, token, &ctx);
- } else {
- result = ApplyImagePatch(source_to_use->data.data(), source_to_use->data.size(),
- patch, sink, token, &ctx, bonus_data);
+ if (strncmp(source_filename, "EMMC:", 5) == 0) {
+ // It's impossible to free space on the target filesystem by
+ // deleting the source if the source is a partition. If
+ // we're ever in a state where we need to do this, fail.
+ printf("not enough free space for target but source is partition\n");
+ return 1;
}
- if (!target_is_partition) {
- if (ota_fsync(output_fd) != 0) {
- printf("failed to fsync file \"%s\" (%s)\n", tmp_target_filename.c_str(),
- strerror(errno));
- result = 1;
- }
- if (ota_close(output_fd) != 0) {
- printf("failed to close file \"%s\" (%s)\n", tmp_target_filename.c_str(),
- strerror(errno));
- result = 1;
- }
+ if (MakeFreeSpaceOnCache(source_file->data.size()) < 0) {
+ printf("not enough free space on /cache\n");
+ return 1;
}
- if (result != 0) {
- if (retry == 0) {
- printf("applying patch failed\n");
- return result != 0;
- } else {
- printf("applying patch failed; retrying\n");
- }
- if (!target_is_partition) {
- unlink(tmp_target_filename.c_str());
- }
- } else {
- // succeeded; no need to retry
- break;
+ if (SaveFileContents(CACHE_TEMP_SOURCE, source_file) < 0) {
+ printf("failed to back up source file\n");
+ return 1;
}
- } while (retry-- > 0);
+ made_copy = true;
+ unlink(source_filename);
- uint8_t current_target_sha1[SHA_DIGEST_LENGTH];
- SHA1_Final(current_target_sha1, &ctx);
- if (memcmp(current_target_sha1, target_sha1, SHA_DIGEST_LENGTH) != 0) {
- printf("patch did not produce expected sha1\n");
- return 1;
- } else {
- printf("now %s\n", short_sha1(target_sha1).c_str());
+ size_t free_space = FreeSpaceForFile(target_fs.c_str());
+ printf("(now %zu bytes free for target) ", free_space);
+ }
}
+ SinkFn sink = nullptr;
+ void* token = nullptr;
+ unique_fd output_fd;
if (target_is_partition) {
- // Copy the temp file to the partition.
- if (WriteToPartition(reinterpret_cast<const unsigned char*>(memory_sink_str.c_str()),
- memory_sink_str.size(), target_filename) != 0) {
- printf("write of patched data to %s failed\n", target_filename);
- return 1;
- }
+ // We store the decoded output in memory.
+ sink = MemorySink;
+ token = &memory_sink_str;
} else {
- // Give the .patch file the same owner, group, and mode of the
- // original source file.
- if (chmod(tmp_target_filename.c_str(), source_to_use->st.st_mode) != 0) {
- printf("chmod of \"%s\" failed: %s\n", tmp_target_filename.c_str(), strerror(errno));
- return 1;
- }
- if (chown(tmp_target_filename.c_str(), source_to_use->st.st_uid, source_to_use->st.st_gid) != 0) {
- printf("chown of \"%s\" failed: %s\n", tmp_target_filename.c_str(), strerror(errno));
- return 1;
- }
+ // We write the decoded output to "<tgt-file>.patch".
+ output_fd.reset(ota_open(tmp_target_filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_SYNC,
+ S_IRUSR | S_IWUSR));
+ if (output_fd == -1) {
+ printf("failed to open output file %s: %s\n", tmp_target_filename.c_str(), strerror(errno));
+ return 1;
+ }
+ sink = FileSink;
+ token = &output_fd;
+ }
- // Finally, rename the .patch file to replace the target file.
- if (rename(tmp_target_filename.c_str(), target_filename) != 0) {
- printf("rename of .patch to \"%s\" failed: %s\n", target_filename, strerror(errno));
- return 1;
- }
+ SHA1_Init(&ctx);
+
+ int result;
+ if (use_bsdiff) {
+ result = ApplyBSDiffPatch(source_to_use->data.data(), source_to_use->data.size(), patch, 0,
+ sink, token, &ctx);
+ } else {
+ result = ApplyImagePatch(source_to_use->data.data(), source_to_use->data.size(), patch, sink,
+ token, &ctx, bonus_data);
}
- // If this run of applypatch created the copy, and we're here, we
- // can delete it.
- if (made_copy) {
- unlink(CACHE_TEMP_SOURCE);
+ if (!target_is_partition) {
+ if (ota_fsync(output_fd) != 0) {
+ printf("failed to fsync file \"%s\": %s\n", tmp_target_filename.c_str(), strerror(errno));
+ result = 1;
+ }
+ if (ota_close(output_fd) != 0) {
+ printf("failed to close file \"%s\": %s\n", tmp_target_filename.c_str(), strerror(errno));
+ result = 1;
+ }
}
- // Success!
- return 0;
+ if (result != 0) {
+ if (retry == 0) {
+ printf("applying patch failed\n");
+ return 1;
+ } else {
+ printf("applying patch failed; retrying\n");
+ }
+ if (!target_is_partition) {
+ unlink(tmp_target_filename.c_str());
+ }
+ } else {
+ // succeeded; no need to retry
+ break;
+ }
+ } while (retry-- > 0);
+
+ uint8_t current_target_sha1[SHA_DIGEST_LENGTH];
+ SHA1_Final(current_target_sha1, &ctx);
+ if (memcmp(current_target_sha1, target_sha1, SHA_DIGEST_LENGTH) != 0) {
+ printf("patch did not produce expected sha1\n");
+ return 1;
+ } else {
+ printf("now %s\n", short_sha1(target_sha1).c_str());
+ }
+
+ if (target_is_partition) {
+ // Copy the temp file to the partition.
+ if (WriteToPartition(reinterpret_cast<const unsigned char*>(memory_sink_str.c_str()),
+ memory_sink_str.size(), target_filename) != 0) {
+ printf("write of patched data to %s failed\n", target_filename);
+ return 1;
+ }
+ } else {
+ // Give the .patch file the same owner, group, and mode of the original source file.
+ if (chmod(tmp_target_filename.c_str(), source_to_use->st.st_mode) != 0) {
+ printf("chmod of \"%s\" failed: %s\n", tmp_target_filename.c_str(), strerror(errno));
+ return 1;
+ }
+ if (chown(tmp_target_filename.c_str(), source_to_use->st.st_uid,
+ source_to_use->st.st_gid) != 0) {
+ printf("chown of \"%s\" failed: %s\n", tmp_target_filename.c_str(), strerror(errno));
+ return 1;
+ }
+
+ // Finally, rename the .patch file to replace the target file.
+ if (rename(tmp_target_filename.c_str(), target_filename) != 0) {
+ printf("rename of .patch to \"%s\" failed: %s\n", target_filename, strerror(errno));
+ return 1;
+ }
+ }
+
+ // If this run of applypatch created the copy, and we're here, we can delete it.
+ if (made_copy) {
+ unlink(CACHE_TEMP_SOURCE);
+ }
+
+ // Success!
+ return 0;
}
diff --git a/applypatch/imgdiff.cpp b/applypatch/imgdiff.cpp
index 7c5bb866d..f6087de01 100644
--- a/applypatch/imgdiff.cpp
+++ b/applypatch/imgdiff.cpp
@@ -189,7 +189,7 @@ unsigned char* ReadZip(const char* filename,
}
size_t sz = static_cast<size_t>(st.st_size);
- unsigned char* img = reinterpret_cast<unsigned char*>(malloc(sz));
+ unsigned char* img = static_cast<unsigned char*>(malloc(sz));
FILE* f = fopen(filename, "rb");
if (fread(img, 1, sz, f) != sz) {
printf("failed to read \"%s\" %s\n", filename, strerror(errno));
@@ -216,7 +216,7 @@ unsigned char* ReadZip(const char* filename,
int cdcount = Read2(img+i+8);
int cdoffset = Read4(img+i+16);
- ZipFileEntry* temp_entries = reinterpret_cast<ZipFileEntry*>(malloc(
+ ZipFileEntry* temp_entries = static_cast<ZipFileEntry*>(malloc(
cdcount * sizeof(ZipFileEntry)));
int entrycount = 0;
@@ -234,7 +234,7 @@ unsigned char* ReadZip(const char* filename,
int mlen = Read2(cd+32); // file comment len
int hoffset = Read4(cd+42); // local header offset
- char* filename = reinterpret_cast<char*>(malloc(nlen+1));
+ char* filename = static_cast<char*>(malloc(nlen+1));
memcpy(filename, cd+46, nlen);
filename[nlen] = '\0';
@@ -283,7 +283,7 @@ unsigned char* ReadZip(const char* filename,
#endif
*num_chunks = 0;
- *chunks = reinterpret_cast<ImageChunk*>(malloc((entrycount*2+2) * sizeof(ImageChunk)));
+ *chunks = static_cast<ImageChunk*>(malloc((entrycount*2+2) * sizeof(ImageChunk)));
ImageChunk* curr = *chunks;
if (include_pseudo_chunk) {
@@ -308,7 +308,7 @@ unsigned char* ReadZip(const char* filename,
curr->filename = temp_entries[nextentry].filename;
curr->len = temp_entries[nextentry].uncomp_len;
- curr->data = reinterpret_cast<unsigned char*>(malloc(curr->len));
+ curr->data = static_cast<unsigned char*>(malloc(curr->len));
z_stream strm;
strm.zalloc = Z_NULL;
@@ -378,7 +378,7 @@ unsigned char* ReadImage(const char* filename,
}
size_t sz = static_cast<size_t>(st.st_size);
- unsigned char* img = reinterpret_cast<unsigned char*>(malloc(sz + 4));
+ unsigned char* img = static_cast<unsigned char*>(malloc(sz + 4));
FILE* f = fopen(filename, "rb");
if (fread(img, 1, sz, f) != sz) {
printf("failed to read \"%s\" %s\n", filename, strerror(errno));
@@ -408,7 +408,7 @@ unsigned char* ReadImage(const char* filename,
size_t chunk_offset = pos;
*num_chunks += 3;
- *chunks = reinterpret_cast<ImageChunk*>(realloc(*chunks,
+ *chunks = static_cast<ImageChunk*>(realloc(*chunks,
*num_chunks * sizeof(ImageChunk)));
ImageChunk* curr = *chunks + (*num_chunks-3);
@@ -431,7 +431,7 @@ unsigned char* ReadImage(const char* filename,
size_t allocated = 32768;
curr->len = 0;
- curr->data = reinterpret_cast<unsigned char*>(malloc(allocated));
+ curr->data = static_cast<unsigned char*>(malloc(allocated));
curr->start = pos;
curr->deflate_data = p;
@@ -459,7 +459,7 @@ unsigned char* ReadImage(const char* filename,
curr->len = allocated - strm.avail_out;
if (strm.avail_out == 0) {
allocated *= 2;
- curr->data = reinterpret_cast<unsigned char*>(realloc(curr->data, allocated));
+ curr->data = static_cast<unsigned char*>(realloc(curr->data, allocated));
}
} while (ret != Z_STREAM_END);
@@ -502,7 +502,7 @@ unsigned char* ReadImage(const char* filename,
// Reallocate the list for every chunk; we expect the number of
// chunks to be small (5 for typical boot and recovery images).
++*num_chunks;
- *chunks = reinterpret_cast<ImageChunk*>(realloc(*chunks, *num_chunks * sizeof(ImageChunk)));
+ *chunks = static_cast<ImageChunk*>(realloc(*chunks, *num_chunks * sizeof(ImageChunk)));
ImageChunk* curr = *chunks + (*num_chunks-1);
curr->start = pos;
@@ -585,7 +585,7 @@ int ReconstructDeflateChunk(ImageChunk* chunk) {
return -1;
}
- unsigned char* out = reinterpret_cast<unsigned char*>(malloc(BUFFER_SIZE));
+ unsigned char* out = static_cast<unsigned char*>(malloc(BUFFER_SIZE));
// We only check two combinations of encoder parameters: level 6
// (the default) and level 9 (the maximum).
@@ -646,7 +646,7 @@ unsigned char* MakePatch(ImageChunk* src, ImageChunk* tgt, size_t* size) {
size_t sz = static_cast<size_t>(st.st_size);
// TODO: Memory leak on error return.
- unsigned char* data = reinterpret_cast<unsigned char*>(malloc(sz));
+ unsigned char* data = static_cast<unsigned char*>(malloc(sz));
if (tgt->type == CHUNK_NORMAL && tgt->len <= sz) {
unlink(ptemp);
@@ -813,7 +813,7 @@ int main(int argc, char** argv) {
return 1;
}
bonus_size = st.st_size;
- bonus_data = reinterpret_cast<unsigned char*>(malloc(bonus_size));
+ bonus_data = static_cast<unsigned char*>(malloc(bonus_size));
FILE* f = fopen(argv[2], "rb");
if (f == NULL) {
printf("failed to open bonus file %s: %s\n", argv[2], strerror(errno));
@@ -959,9 +959,9 @@ int main(int argc, char** argv) {
DumpChunks(src_chunks, num_src_chunks);
printf("Construct patches for %d chunks...\n", num_tgt_chunks);
- unsigned char** patch_data = reinterpret_cast<unsigned char**>(malloc(
+ unsigned char** patch_data = static_cast<unsigned char**>(malloc(
num_tgt_chunks * sizeof(unsigned char*)));
- size_t* patch_size = reinterpret_cast<size_t*>(malloc(num_tgt_chunks * sizeof(size_t)));
+ size_t* patch_size = static_cast<size_t*>(malloc(num_tgt_chunks * sizeof(size_t)));
for (i = 0; i < num_tgt_chunks; ++i) {
if (zip_mode) {
ImageChunk* src;
@@ -975,7 +975,7 @@ int main(int argc, char** argv) {
} else {
if (i == 1 && bonus_data) {
printf(" using %zu bytes of bonus data for chunk %d\n", bonus_size, i);
- src_chunks[i].data = reinterpret_cast<unsigned char*>(realloc(src_chunks[i].data,
+ src_chunks[i].data = static_cast<unsigned char*>(realloc(src_chunks[i].data,
src_chunks[i].len + bonus_size));
memcpy(src_chunks[i].data+src_chunks[i].len, bonus_data, bonus_size);
src_chunks[i].len += bonus_size;
diff --git a/error_code.h b/error_code.h
index 92e93bdea..92b1574d4 100644
--- a/error_code.h
+++ b/error_code.h
@@ -18,52 +18,53 @@
#define _ERROR_CODE_H_
enum ErrorCode {
- kNoError = -1,
- kLowBattery = 20,
- kZipVerificationFailure,
- kZipOpenFailure,
- kBootreasonInBlacklist
+ kNoError = -1,
+ kLowBattery = 20,
+ kZipVerificationFailure,
+ kZipOpenFailure,
+ kBootreasonInBlacklist
};
enum CauseCode {
- kNoCause = -1,
- kArgsParsingFailure = 100,
- kStashCreationFailure,
- kFileOpenFailure,
- kLseekFailure,
- kFreadFailure,
- kFwriteFailure,
- kFsyncFailure,
- kLibfecFailure,
- kFileGetPropFailure,
- kFileRenameFailure,
- kSymlinkFailure,
- kSetMetadataFailure,
- kTune2FsFailure,
- kRebootFailure,
- kVendorFailure = 200
+ kNoCause = -1,
+ kArgsParsingFailure = 100,
+ kStashCreationFailure,
+ kFileOpenFailure,
+ kLseekFailure,
+ kFreadFailure,
+ kFwriteFailure,
+ kFsyncFailure,
+ kLibfecFailure,
+ kFileGetPropFailure,
+ kFileRenameFailure,
+ kSymlinkFailure,
+ kSetMetadataFailure,
+ kTune2FsFailure,
+ kRebootFailure,
+ kPackageExtractFileFailure,
+ kVendorFailure = 200
};
enum UncryptErrorCode {
- kUncryptNoError = -1,
- kUncryptErrorHolder = 50,
- kUncryptTimeoutError = 100,
- kUncryptFileRemoveError,
- kUncryptFileOpenError,
- kUncryptSocketOpenError,
- kUncryptSocketWriteError,
- kUncryptSocketListenError,
- kUncryptSocketAcceptError,
- kUncryptFstabReadError,
- kUncryptFileStatError,
- kUncryptBlockOpenError,
- kUncryptIoctlError,
- kUncryptReadError,
- kUncryptWriteError,
- kUncryptFileSyncError,
- kUncryptFileCloseError,
- kUncryptFileRenameError,
- kUncryptPackageMissingError,
+ kUncryptNoError = -1,
+ kUncryptErrorHolder = 50,
+ kUncryptTimeoutError = 100,
+ kUncryptFileRemoveError,
+ kUncryptFileOpenError,
+ kUncryptSocketOpenError,
+ kUncryptSocketWriteError,
+ kUncryptSocketListenError,
+ kUncryptSocketAcceptError,
+ kUncryptFstabReadError,
+ kUncryptFileStatError,
+ kUncryptBlockOpenError,
+ kUncryptIoctlError,
+ kUncryptReadError,
+ kUncryptWriteError,
+ kUncryptFileSyncError,
+ kUncryptFileCloseError,
+ kUncryptFileRenameError,
+ kUncryptPackageMissingError,
};
-#endif
+#endif // _ERROR_CODE_H_
diff --git a/minadbd/minadbd_services.cpp b/minadbd/minadbd_services.cpp
index 003b51913..426d982eb 100644
--- a/minadbd/minadbd_services.cpp
+++ b/minadbd/minadbd_services.cpp
@@ -66,7 +66,7 @@ static int create_service_thread(void (*func)(int, void *), void *cookie) {
return -1;
}
- stinfo* sti = reinterpret_cast<stinfo*>(malloc(sizeof(stinfo)));
+ stinfo* sti = static_cast<stinfo*>(malloc(sizeof(stinfo)));
if(sti == 0) fatal("cannot allocate stinfo");
sti->func = func;
sti->cookie = cookie;
diff --git a/minui/font_10x18.h b/minui/font_10x18.h
index 29d705344..30dfb9c56 100644
--- a/minui/font_10x18.h
+++ b/minui/font_10x18.h
@@ -1,14 +1,14 @@
struct {
unsigned width;
unsigned height;
- unsigned cwidth;
- unsigned cheight;
+ unsigned char_width;
+ unsigned char_height;
unsigned char rundata[2973];
} font = {
.width = 960,
.height = 18,
- .cwidth = 10,
- .cheight = 18,
+ .char_width = 10,
+ .char_height = 18,
.rundata = {
0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x55,0x82,0x06,0x82,0x02,0x82,0x10,0x82,
0x11,0x83,0x08,0x82,0x0a,0x82,0x04,0x82,0x46,0x82,0x08,0x82,0x07,0x84,0x06,
diff --git a/minui/graphics.cpp b/minui/graphics.cpp
index c0eea9e38..dcca3ec41 100644
--- a/minui/graphics.cpp
+++ b/minui/graphics.cpp
@@ -35,12 +35,6 @@
#include "minui.h"
#include "graphics.h"
-struct GRFont {
- GRSurface* texture;
- int cwidth;
- int cheight;
-};
-
static GRFont* gr_font = NULL;
static minui_backend* gr_backend = NULL;
@@ -60,15 +54,20 @@ static bool outside(int x, int y)
return x < 0 || x >= gr_draw->width || y < 0 || y >= gr_draw->height;
}
-int gr_measure(const char *s)
+const GRFont* gr_sys_font()
+{
+ return gr_font;
+}
+
+int gr_measure(const GRFont* font, const char *s)
{
- return gr_font->cwidth * strlen(s);
+ return font->char_width * strlen(s);
}
-void gr_font_size(int *x, int *y)
+void gr_font_size(const GRFont* font, int *x, int *y)
{
- *x = gr_font->cwidth;
- *y = gr_font->cheight;
+ *x = font->char_width;
+ *y = font->char_height;
}
static void text_blend(unsigned char* src_p, int src_row_bytes,
@@ -103,34 +102,32 @@ static void text_blend(unsigned char* src_p, int src_row_bytes,
}
}
-void gr_text(int x, int y, const char *s, bool bold)
+void gr_text(const GRFont* font, int x, int y, const char *s, bool bold)
{
- GRFont* font = gr_font;
-
if (!font->texture || gr_current_a == 0) return;
- bold = bold && (font->texture->height != font->cheight);
+ bold = bold && (font->texture->height != font->char_height);
x += overscan_offset_x;
y += overscan_offset_y;
unsigned char ch;
while ((ch = *s++)) {
- if (outside(x, y) || outside(x+font->cwidth-1, y+font->cheight-1)) break;
+ if (outside(x, y) || outside(x+font->char_width-1, y+font->char_height-1)) break;
if (ch < ' ' || ch > '~') {
ch = '?';
}
- unsigned char* src_p = font->texture->data + ((ch - ' ') * font->cwidth) +
- (bold ? font->cheight * font->texture->row_bytes : 0);
+ unsigned char* src_p = font->texture->data + ((ch - ' ') * font->char_width) +
+ (bold ? font->char_height * font->texture->row_bytes : 0);
unsigned char* dst_p = gr_draw->data + y*gr_draw->row_bytes + x*gr_draw->pixel_bytes;
text_blend(src_p, font->texture->row_bytes,
dst_p, gr_draw->row_bytes,
- font->cwidth, font->cheight);
+ font->char_width, font->char_height);
- x += font->cwidth;
+ x += font->char_width;
}
}
@@ -267,40 +264,59 @@ unsigned int gr_get_height(GRSurface* surface) {
return surface->height;
}
+int gr_init_font(const char* name, GRFont** dest) {
+ GRFont* font = reinterpret_cast<GRFont*>(calloc(1, sizeof(*gr_font)));
+ if (font == nullptr) {
+ return -1;
+ }
+
+ int res = res_create_alpha_surface(name, &(font->texture));
+ if (res < 0) {
+ free(font);
+ return res;
+ }
+
+ // The font image should be a 96x2 array of character images. The
+ // columns are the printable ASCII characters 0x20 - 0x7f. The
+ // top row is regular text; the bottom row is bold.
+ font->char_width = font->texture->width / 96;
+ font->char_height = font->texture->height / 2;
+
+ *dest = font;
+
+ return 0;
+}
+
static void gr_init_font(void)
{
- gr_font = reinterpret_cast<GRFont*>(calloc(sizeof(*gr_font), 1));
-
- int res = res_create_alpha_surface("font", &(gr_font->texture));
+ int res = gr_init_font("font", &gr_font);
if (res == 0) {
- // The font image should be a 96x2 array of character images. The
- // columns are the printable ASCII characters 0x20 - 0x7f. The
- // top row is regular text; the bottom row is bold.
- gr_font->cwidth = gr_font->texture->width / 96;
- gr_font->cheight = gr_font->texture->height / 2;
- } else {
- printf("failed to read font: res=%d\n", res);
-
- // fall back to the compiled-in font.
- gr_font->texture = reinterpret_cast<GRSurface*>(malloc(sizeof(*gr_font->texture)));
- gr_font->texture->width = font.width;
- gr_font->texture->height = font.height;
- gr_font->texture->row_bytes = font.width;
- gr_font->texture->pixel_bytes = 1;
-
- unsigned char* bits = reinterpret_cast<unsigned char*>(malloc(font.width * font.height));
- gr_font->texture->data = reinterpret_cast<unsigned char*>(bits);
-
- unsigned char data;
- unsigned char* in = font.rundata;
- while((data = *in++)) {
- memset(bits, (data & 0x80) ? 255 : 0, data & 0x7f);
- bits += (data & 0x7f);
- }
+ return;
+ }
+
+ printf("failed to read font: res=%d\n", res);
+
- gr_font->cwidth = font.cwidth;
- gr_font->cheight = font.cheight;
+ // fall back to the compiled-in font.
+ gr_font = static_cast<GRFont*>(calloc(sizeof(*gr_font), 1));
+ gr_font->texture = static_cast<GRSurface*>(malloc(sizeof(*gr_font->texture)));
+ gr_font->texture->width = font.width;
+ gr_font->texture->height = font.height;
+ gr_font->texture->row_bytes = font.width;
+ gr_font->texture->pixel_bytes = 1;
+
+ unsigned char* bits = static_cast<unsigned char*>(malloc(font.width * font.height));
+ gr_font->texture->data = bits;
+
+ unsigned char data;
+ unsigned char* in = font.rundata;
+ while((data = *in++)) {
+ memset(bits, (data & 0x80) ? 255 : 0, data & 0x7f);
+ bits += (data & 0x7f);
}
+
+ gr_font->char_width = font.char_width;
+ gr_font->char_height = font.char_height;
}
#if 0
diff --git a/minui/graphics_adf.cpp b/minui/graphics_adf.cpp
index 3c3541094..9e262b044 100644
--- a/minui/graphics_adf.cpp
+++ b/minui/graphics_adf.cpp
@@ -68,9 +68,9 @@ static int adf_surface_init(adf_pdata *pdata, drm_mode_modeinfo *mode, adf_surfa
surf->base.row_bytes = surf->pitch;
surf->base.pixel_bytes = (pdata->format == DRM_FORMAT_RGB565) ? 2 : 4;
- surf->base.data = reinterpret_cast<uint8_t*>(mmap(NULL,
- surf->pitch * surf->base.height, PROT_WRITE,
- MAP_SHARED, surf->fd, surf->offset));
+ surf->base.data = static_cast<uint8_t*>(mmap(NULL,
+ surf->pitch * surf->base.height, PROT_WRITE,
+ MAP_SHARED, surf->fd, surf->offset));
if (surf->base.data == MAP_FAILED) {
close(surf->fd);
return -errno;
@@ -259,7 +259,7 @@ static void adf_exit(minui_backend *backend)
minui_backend *open_adf()
{
- adf_pdata* pdata = reinterpret_cast<adf_pdata*>(calloc(1, sizeof(*pdata)));
+ adf_pdata* pdata = static_cast<adf_pdata*>(calloc(1, sizeof(*pdata)));
if (!pdata) {
perror("allocating adf backend failed");
return NULL;
diff --git a/minui/graphics_fbdev.cpp b/minui/graphics_fbdev.cpp
index 0788f7552..631ef4e13 100644
--- a/minui/graphics_fbdev.cpp
+++ b/minui/graphics_fbdev.cpp
@@ -133,7 +133,7 @@ static GRSurface* fbdev_init(minui_backend* backend) {
gr_framebuffer[0].height = vi.yres;
gr_framebuffer[0].row_bytes = fi.line_length;
gr_framebuffer[0].pixel_bytes = vi.bits_per_pixel / 8;
- gr_framebuffer[0].data = reinterpret_cast<uint8_t*>(bits);
+ gr_framebuffer[0].data = static_cast<uint8_t*>(bits);
memset(gr_framebuffer[0].data, 0, gr_framebuffer[0].height * gr_framebuffer[0].row_bytes);
/* check if we can use double buffering */
diff --git a/minui/minui.h b/minui/minui.h
index 5362d3fe3..78890b84b 100644
--- a/minui/minui.h
+++ b/minui/minui.h
@@ -33,6 +33,12 @@ struct GRSurface {
unsigned char* data;
};
+struct GRFont {
+ GRSurface* texture;
+ int char_width;
+ int char_height;
+};
+
int gr_init();
void gr_exit();
@@ -45,10 +51,14 @@ void gr_fb_blank(bool blank);
void gr_clear(); // clear entire surface to current color
void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a);
void gr_fill(int x1, int y1, int x2, int y2);
-void gr_text(int x, int y, const char *s, bool bold);
+
void gr_texticon(int x, int y, GRSurface* icon);
-int gr_measure(const char *s);
-void gr_font_size(int *x, int *y);
+
+const GRFont* gr_sys_font();
+int gr_init_font(const char* name, GRFont** dest);
+void gr_text(const GRFont* font, int x, int y, const char *s, bool bold);
+int gr_measure(const GRFont* font, const char *s);
+void gr_font_size(const GRFont* font, int *x, int *y);
void gr_blit(GRSurface* source, int sx, int sy, int w, int h, int dx, int dy);
unsigned int gr_get_width(GRSurface* surface);
diff --git a/minui/resources.cpp b/minui/resources.cpp
index 455ef2784..9ccbf4b1b 100644
--- a/minui/resources.cpp
+++ b/minui/resources.cpp
@@ -37,7 +37,7 @@
static GRSurface* malloc_surface(size_t data_size) {
size_t size = sizeof(GRSurface) + data_size + SURFACE_DATA_ALIGNMENT;
- unsigned char* temp = reinterpret_cast<unsigned char*>(malloc(size));
+ unsigned char* temp = static_cast<unsigned char*>(malloc(size));
if (temp == NULL) return NULL;
GRSurface* surface = reinterpret_cast<GRSurface*>(temp);
surface->data = temp + sizeof(GRSurface) +
@@ -221,7 +221,7 @@ int res_create_display_surface(const char* name, GRSurface** pSurface) {
png_set_bgr(png_ptr);
#endif
- p_row = reinterpret_cast<unsigned char*>(malloc(width * 4));
+ p_row = static_cast<unsigned char*>(malloc(width * 4));
for (y = 0; y < height; ++y) {
png_read_row(png_ptr, p_row, NULL);
transform_rgb_to_draw(p_row, surface->data + y * surface->row_bytes, channels, width);
@@ -281,7 +281,7 @@ int res_create_multi_display_surface(const char* name, int* frames, int* fps,
goto exit;
}
- surface = reinterpret_cast<GRSurface**>(calloc(*frames, sizeof(GRSurface*)));
+ surface = static_cast<GRSurface**>(calloc(*frames, sizeof(GRSurface*)));
if (surface == NULL) {
result = -8;
goto exit;
@@ -298,7 +298,7 @@ int res_create_multi_display_surface(const char* name, int* frames, int* fps,
png_set_bgr(png_ptr);
#endif
- p_row = reinterpret_cast<unsigned char*>(malloc(width * 4));
+ p_row = static_cast<unsigned char*>(malloc(width * 4));
for (y = 0; y < height; ++y) {
png_read_row(png_ptr, p_row, NULL);
int frame = y % *frames;
@@ -308,7 +308,7 @@ int res_create_multi_display_surface(const char* name, int* frames, int* fps,
}
free(p_row);
- *pSurface = reinterpret_cast<GRSurface**>(surface);
+ *pSurface = surface;
exit:
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
@@ -436,7 +436,7 @@ int res_create_localized_alpha_surface(const char* name,
memcpy(surface->data + i*w, row.data(), w);
}
- *pSurface = reinterpret_cast<GRSurface*>(surface);
+ *pSurface = surface;
break;
} else {
int i;
diff --git a/otafault/ota_io.h b/otafault/ota_io.h
index 84187a76e..e119eef08 100644
--- a/otafault/ota_io.h
+++ b/otafault/ota_io.h
@@ -26,6 +26,8 @@
#include <stdio.h>
#include <sys/stat.h>
+#include <android-base/unique_fd.h>
+
#define OTAIO_CACHE_FNAME "/cache/saved.file"
void ota_set_fault_files();
@@ -50,4 +52,12 @@ ssize_t ota_write(int fd, const void* buf, size_t nbyte);
int ota_fsync(int fd);
+struct OtaCloser {
+ static void Close(int fd) {
+ ota_close(fd);
+ }
+};
+
+using unique_fd = android::base::unique_fd_impl<OtaCloser>;
+
#endif
diff --git a/screen_ui.cpp b/screen_ui.cpp
index c61727258..fab348964 100644
--- a/screen_ui.cpp
+++ b/screen_ui.cpp
@@ -248,7 +248,7 @@ void ScreenRecoveryUI::DrawHorizontalRule(int* y) {
}
void ScreenRecoveryUI::DrawTextLine(int x, int* y, const char* line, bool bold) {
- gr_text(x, *y, line, bold);
+ gr_text(gr_sys_font(), x, *y, line, bold);
*y += char_height_ + 4;
}
@@ -304,10 +304,10 @@ void ScreenRecoveryUI::draw_screen_locked() {
gr_fill(0, y - 2, gr_fb_width(), y + char_height_ + 2);
// Bold white text for the selected item.
SetColor(MENU_SEL_FG);
- gr_text(4, y, menu_[i], true);
+ gr_text(gr_sys_font(), 4, y, menu_[i], true);
SetColor(MENU);
} else {
- gr_text(4, y, menu_[i], false);
+ gr_text(gr_sys_font(), 4, y, menu_[i], false);
}
y += char_height_ + 4;
}
@@ -323,7 +323,7 @@ void ScreenRecoveryUI::draw_screen_locked() {
for (int ty = gr_fb_height() - char_height_;
ty >= y && count < text_rows_;
ty -= char_height_, ++count) {
- gr_text(0, ty, text_[row], false);
+ gr_text(gr_sys_font(), 0, ty, text_[row], false);
--row;
if (row < 0) row = text_rows_ - 1;
}
@@ -442,7 +442,7 @@ void ScreenRecoveryUI::Init() {
density_ = static_cast<float>(android::base::GetIntProperty("ro.sf.lcd_density", 160)) / 160.f;
is_large_ = gr_fb_height() > PixelsFromDp(800);
- gr_font_size(&char_width_, &char_height_);
+ gr_font_size(gr_sys_font(), &char_width_, &char_height_);
text_rows_ = gr_fb_height() / char_height_;
text_cols_ = gr_fb_width() / char_width_;
diff --git a/tests/Android.mk b/tests/Android.mk
index e87a22964..fdc947028 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -92,6 +92,8 @@ LOCAL_STATIC_LIBRARIES := \
libcrypto \
libcutils \
libbz \
+ libziparchive \
+ libutils \
libz \
libbase \
libtune2fs \
diff --git a/tests/common/test_constants.h b/tests/common/test_constants.h
index 97e74a3c2..f6b6922a4 100644
--- a/tests/common/test_constants.h
+++ b/tests/common/test_constants.h
@@ -19,6 +19,17 @@
#include <stdlib.h>
+// Zip entries in ziptest_valid.zip.
+static const std::string kATxtContents("abcdefghabcdefgh\n");
+static const std::string kBTxtContents("abcdefgh\n");
+static const std::string kCTxtContents("abcdefghabcdefgh\n");
+static const std::string kDTxtContents("abcdefgh\n");
+
+// echo -n -e "abcdefghabcdefgh\n" | sha1sum
+static const std::string kATxtSha1Sum("32c96a03dc8cd20097940f351bca6261ee5a1643");
+// echo -n -e "abcdefgh\n" | sha1sum
+static const std::string kBTxtSha1Sum("e414af7161c9554089f4106d6f1797ef14a73666");
+
static const char* data_root = getenv("ANDROID_DATA");
static std::string from_testdata_base(const std::string& fname) {
diff --git a/tests/component/updater_test.cpp b/tests/component/updater_test.cpp
index 973c19de3..cd285729d 100644
--- a/tests/component/updater_test.cpp
+++ b/tests/component/updater_test.cpp
@@ -24,35 +24,40 @@
#include <android-base/properties.h>
#include <android-base/test_utils.h>
#include <gtest/gtest.h>
+#include <ziparchive/zip_archive.h>
+#include "common/test_constants.h"
#include "edify/expr.h"
#include "error_code.h"
#include "updater/install.h"
+#include "updater/updater.h"
struct selabel_handle *sehandle = nullptr;
-static void expect(const char* expected, const char* expr_str, CauseCode cause_code) {
- Expr* e;
- int error_count;
- EXPECT_EQ(parse_string(expr_str, &e, &error_count), 0);
+static void expect(const char* expected, const char* expr_str, CauseCode cause_code,
+ UpdaterInfo* info = nullptr) {
+ Expr* e;
+ int error_count = 0;
+ ASSERT_EQ(0, parse_string(expr_str, &e, &error_count));
+ ASSERT_EQ(0, error_count);
- State state(expr_str, nullptr);
+ State state(expr_str, info);
- std::string result;
- bool status = Evaluate(&state, e, &result);
+ std::string result;
+ bool status = Evaluate(&state, e, &result);
- if (expected == nullptr) {
- EXPECT_FALSE(status);
- } else {
- EXPECT_STREQ(expected, result.c_str());
- }
-
- // Error code is set in updater/updater.cpp only, by parsing State.errmsg.
- EXPECT_EQ(kNoError, state.error_code);
+ if (expected == nullptr) {
+ ASSERT_FALSE(status);
+ } else {
+ ASSERT_TRUE(status);
+ ASSERT_STREQ(expected, result.c_str());
+ }
- // Cause code should always be available.
- EXPECT_EQ(cause_code, state.cause_code);
+ // Error code is set in updater/updater.cpp only, by parsing State.errmsg.
+ ASSERT_EQ(kNoError, state.error_code);
+ // Cause code should always be available.
+ ASSERT_EQ(cause_code, state.cause_code);
}
class UpdaterTest : public ::testing::Test {
@@ -264,3 +269,185 @@ TEST_F(UpdaterTest, symlink) {
ASSERT_EQ(0, unlink(src1.c_str()));
ASSERT_EQ(0, unlink(src2.c_str()));
}
+
+TEST_F(UpdaterTest, package_extract_dir) {
+ // package_extract_dir expects 2 arguments.
+ expect(nullptr, "package_extract_dir()", kArgsParsingFailure);
+ expect(nullptr, "package_extract_dir(\"arg1\")", kArgsParsingFailure);
+ expect(nullptr, "package_extract_dir(\"arg1\", \"arg2\", \"arg3\")", kArgsParsingFailure);
+
+ std::string zip_path = from_testdata_base("ziptest_valid.zip");
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle));
+
+ // Need to set up the ziphandle.
+ UpdaterInfo updater_info;
+ updater_info.package_zip = handle;
+
+ // Extract "b/c.txt" and "b/d.txt" with package_extract_dir("b", "<dir>").
+ TemporaryDir td;
+ std::string temp_dir(td.path);
+ std::string script("package_extract_dir(\"b\", \"" + temp_dir + "\")");
+ expect("t", script.c_str(), kNoCause, &updater_info);
+
+ // Verify.
+ std::string data;
+ std::string file_c = temp_dir + "/c.txt";
+ ASSERT_TRUE(android::base::ReadFileToString(file_c, &data));
+ ASSERT_EQ(kCTxtContents, data);
+
+ std::string file_d = temp_dir + "/d.txt";
+ ASSERT_TRUE(android::base::ReadFileToString(file_d, &data));
+ ASSERT_EQ(kDTxtContents, data);
+
+ // Modify the contents in order to retry. It's expected to be overwritten.
+ ASSERT_TRUE(android::base::WriteStringToFile("random", file_c));
+ ASSERT_TRUE(android::base::WriteStringToFile("random", file_d));
+
+ // Extract again and verify.
+ expect("t", script.c_str(), kNoCause, &updater_info);
+
+ ASSERT_TRUE(android::base::ReadFileToString(file_c, &data));
+ ASSERT_EQ(kCTxtContents, data);
+ ASSERT_TRUE(android::base::ReadFileToString(file_d, &data));
+ ASSERT_EQ(kDTxtContents, data);
+
+ // Clean up the temp files under td.
+ ASSERT_EQ(0, unlink(file_c.c_str()));
+ ASSERT_EQ(0, unlink(file_d.c_str()));
+
+ // Extracting "b/" (with slash) should give the same result.
+ script = "package_extract_dir(\"b/\", \"" + temp_dir + "\")";
+ expect("t", script.c_str(), kNoCause, &updater_info);
+
+ ASSERT_TRUE(android::base::ReadFileToString(file_c, &data));
+ ASSERT_EQ(kCTxtContents, data);
+ ASSERT_TRUE(android::base::ReadFileToString(file_d, &data));
+ ASSERT_EQ(kDTxtContents, data);
+
+ ASSERT_EQ(0, unlink(file_c.c_str()));
+ ASSERT_EQ(0, unlink(file_d.c_str()));
+
+ // Extracting "" is allowed. The entries will carry the path name.
+ script = "package_extract_dir(\"\", \"" + temp_dir + "\")";
+ expect("t", script.c_str(), kNoCause, &updater_info);
+
+ std::string file_a = temp_dir + "/a.txt";
+ ASSERT_TRUE(android::base::ReadFileToString(file_a, &data));
+ ASSERT_EQ(kATxtContents, data);
+ std::string file_b = temp_dir + "/b.txt";
+ ASSERT_TRUE(android::base::ReadFileToString(file_b, &data));
+ ASSERT_EQ(kBTxtContents, data);
+ std::string file_b_c = temp_dir + "/b/c.txt";
+ ASSERT_TRUE(android::base::ReadFileToString(file_b_c, &data));
+ ASSERT_EQ(kCTxtContents, data);
+ std::string file_b_d = temp_dir + "/b/d.txt";
+ ASSERT_TRUE(android::base::ReadFileToString(file_b_d, &data));
+ ASSERT_EQ(kDTxtContents, data);
+
+ ASSERT_EQ(0, unlink(file_a.c_str()));
+ ASSERT_EQ(0, unlink(file_b.c_str()));
+ ASSERT_EQ(0, unlink(file_b_c.c_str()));
+ ASSERT_EQ(0, unlink(file_b_d.c_str()));
+ ASSERT_EQ(0, rmdir((temp_dir + "/b").c_str()));
+
+ // Extracting non-existent entry should still give "t".
+ script = "package_extract_dir(\"doesntexist\", \"" + temp_dir + "\")";
+ expect("t", script.c_str(), kNoCause, &updater_info);
+
+ // Only relative zip_path is allowed.
+ script = "package_extract_dir(\"/b\", \"" + temp_dir + "\")";
+ expect("", script.c_str(), kNoCause, &updater_info);
+
+ // Only absolute dest_path is allowed.
+ script = "package_extract_dir(\"b\", \"path\")";
+ expect("", script.c_str(), kNoCause, &updater_info);
+
+ CloseArchive(handle);
+}
+
+// TODO: Test extracting to block device.
+TEST_F(UpdaterTest, package_extract_file) {
+ // package_extract_file expects 1 or 2 arguments.
+ expect(nullptr, "package_extract_file()", kArgsParsingFailure);
+ expect(nullptr, "package_extract_file(\"arg1\", \"arg2\", \"arg3\")", kArgsParsingFailure);
+
+ std::string zip_path = from_testdata_base("ziptest_valid.zip");
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle));
+
+ // Need to set up the ziphandle.
+ UpdaterInfo updater_info;
+ updater_info.package_zip = handle;
+
+ // Two-argument version.
+ TemporaryFile temp_file1;
+ std::string script("package_extract_file(\"a.txt\", \"" + std::string(temp_file1.path) + "\")");
+ expect("t", script.c_str(), kNoCause, &updater_info);
+
+ // Verify the extracted entry.
+ std::string data;
+ ASSERT_TRUE(android::base::ReadFileToString(temp_file1.path, &data));
+ ASSERT_EQ(kATxtContents, data);
+
+ // Now extract another entry to the same location, which should overwrite.
+ script = "package_extract_file(\"b.txt\", \"" + std::string(temp_file1.path) + "\")";
+ expect("t", script.c_str(), kNoCause, &updater_info);
+
+ ASSERT_TRUE(android::base::ReadFileToString(temp_file1.path, &data));
+ ASSERT_EQ(kBTxtContents, data);
+
+ // Missing zip entry. The two-argument version doesn't abort.
+ script = "package_extract_file(\"doesntexist\", \"" + std::string(temp_file1.path) + "\")";
+ expect("", script.c_str(), kNoCause, &updater_info);
+
+ // Extract to /dev/full should fail.
+ script = "package_extract_file(\"a.txt\", \"/dev/full\")";
+ expect("", script.c_str(), kNoCause, &updater_info);
+
+ // One-argument version.
+ script = "sha1_check(package_extract_file(\"a.txt\"))";
+ expect(kATxtSha1Sum.c_str(), script.c_str(), kNoCause, &updater_info);
+
+ script = "sha1_check(package_extract_file(\"b.txt\"))";
+ expect(kBTxtSha1Sum.c_str(), script.c_str(), kNoCause, &updater_info);
+
+ // Missing entry. The one-argument version aborts the evaluation.
+ script = "package_extract_file(\"doesntexist\")";
+ expect(nullptr, script.c_str(), kPackageExtractFileFailure, &updater_info);
+
+ CloseArchive(handle);
+}
+
+TEST_F(UpdaterTest, write_value) {
+ // write_value() expects two arguments.
+ expect(nullptr, "write_value()", kArgsParsingFailure);
+ expect(nullptr, "write_value(\"arg1\")", kArgsParsingFailure);
+ expect(nullptr, "write_value(\"arg1\", \"arg2\", \"arg3\")", kArgsParsingFailure);
+
+ // filename cannot be empty.
+ expect(nullptr, "write_value(\"value\", \"\")", kArgsParsingFailure);
+
+ // Write some value to file.
+ TemporaryFile temp_file;
+ std::string value = "magicvalue";
+ std::string script("write_value(\"" + value + "\", \"" + std::string(temp_file.path) + "\")");
+ expect("t", script.c_str(), kNoCause);
+
+ // Verify the content.
+ std::string content;
+ ASSERT_TRUE(android::base::ReadFileToString(temp_file.path, &content));
+ ASSERT_EQ(value, content);
+
+ // Allow writing empty string.
+ script = "write_value(\"\", \"" + std::string(temp_file.path) + "\")";
+ expect("t", script.c_str(), kNoCause);
+
+ // Verify the content.
+ ASSERT_TRUE(android::base::ReadFileToString(temp_file.path, &content));
+ ASSERT_EQ("", content);
+
+ // It should fail gracefully when write fails.
+ script = "write_value(\"value\", \"/proc/0/file1\")";
+ expect("", script.c_str(), kNoCause);
+}
diff --git a/tests/unit/zip_test.cpp b/tests/unit/zip_test.cpp
index 49729467d..ef0ee4c1d 100644
--- a/tests/unit/zip_test.cpp
+++ b/tests/unit/zip_test.cpp
@@ -30,9 +30,6 @@
#include "common/test_constants.h"
-static const std::string kATxtContents("abcdefghabcdefgh\n");
-static const std::string kBTxtContents("abcdefgh\n");
-
TEST(ZipTest, ExtractPackageRecursive) {
std::string zip_path = from_testdata_base("ziptest_valid.zip");
ZipArchiveHandle handle;
diff --git a/updater/install.cpp b/updater/install.cpp
index 59c54dd4c..6c110732a 100644
--- a/updater/install.cpp
+++ b/updater/install.cpp
@@ -39,11 +39,12 @@
#include <string>
#include <vector>
-#include <android-base/parseint.h>
+#include <android-base/file.h>
#include <android-base/parsedouble.h>
+#include <android-base/parseint.h>
#include <android-base/properties.h>
-#include <android-base/strings.h>
#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <cutils/android_reboot.h>
#include <ext4_utils/make_ext4fs.h>
#include <ext4_utils/wipe.h>
@@ -453,117 +454,114 @@ Value* SetProgressFn(const char* name, State* state, int argc, Expr* argv[]) {
return StringValue(frac_str);
}
-// package_extract_dir(package_path, destination_path)
-Value* PackageExtractDirFn(const char* name, State* state,
- int argc, Expr* argv[]) {
- if (argc != 2) {
- return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc);
- }
+// package_extract_dir(package_dir, dest_dir)
+// Extracts all files from the package underneath package_dir and writes them to the
+// corresponding tree beneath dest_dir. Any existing files are overwritten.
+// Example: package_extract_dir("system", "/system")
+//
+// Note: package_dir needs to be a relative path; dest_dir needs to be an absolute path.
+Value* PackageExtractDirFn(const char* name, State* state, int argc, Expr* argv[]) {
+ if (argc != 2) {
+ return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc);
+ }
- std::vector<std::string> args;
- if (!ReadArgs(state, 2, argv, &args)) {
- return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
- }
- const std::string& zip_path = args[0];
- const std::string& dest_path = args[1];
+ std::vector<std::string> args;
+ if (!ReadArgs(state, 2, argv, &args)) {
+ return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
+ }
+ const std::string& zip_path = args[0];
+ const std::string& dest_path = args[1];
- ZipArchiveHandle za = ((UpdaterInfo*)(state->cookie))->package_zip;
+ ZipArchiveHandle za = static_cast<UpdaterInfo*>(state->cookie)->package_zip;
- // To create a consistent system image, never use the clock for timestamps.
- struct utimbuf timestamp = { 1217592000, 1217592000 }; // 8/1/2008 default
+ // To create a consistent system image, never use the clock for timestamps.
+ constexpr struct utimbuf timestamp = { 1217592000, 1217592000 }; // 8/1/2008 default
- bool success = ExtractPackageRecursive(za, zip_path, dest_path, &timestamp, sehandle);
+ bool success = ExtractPackageRecursive(za, zip_path, dest_path, &timestamp, sehandle);
- return StringValue(success ? "t" : "");
+ return StringValue(success ? "t" : "");
}
+// package_extract_file(package_file[, dest_file])
+// Extracts a single package_file from the update package and writes it to dest_file,
+// overwriting existing files if necessary. Without the dest_file argument, returns the
+// contents of the package file as a binary blob.
+Value* PackageExtractFileFn(const char* name, State* state, int argc, Expr* argv[]) {
+ if (argc < 1 || argc > 2) {
+ return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 or 2 args, got %d", name, argc);
+ }
-// package_extract_file(package_path, destination_path)
-// or
-// package_extract_file(package_path)
-// to return the entire contents of the file as the result of this
-// function (the char* returned is actually a FileContents*).
-Value* PackageExtractFileFn(const char* name, State* state,
- int argc, Expr* argv[]) {
- if (argc < 1 || argc > 2) {
- return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 or 2 args, got %d",
- name, argc);
- }
- bool success = false;
-
- if (argc == 2) {
- // The two-argument version extracts to a file.
+ if (argc == 2) {
+ // The two-argument version extracts to a file.
- ZipArchiveHandle za = ((UpdaterInfo*)(state->cookie))->package_zip;
-
- std::vector<std::string> args;
- if (!ReadArgs(state, 2, argv, &args)) {
- return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse %d args", name,
- argc);
- }
- const std::string& zip_path = args[0];
- const std::string& dest_path = args[1];
+ std::vector<std::string> args;
+ if (!ReadArgs(state, 2, argv, &args)) {
+ return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse %d args", name, argc);
+ }
+ const std::string& zip_path = args[0];
+ const std::string& dest_path = args[1];
- ZipString zip_string_path(zip_path.c_str());
- ZipEntry entry;
- if (FindEntry(za, zip_string_path, &entry) != 0) {
- printf("%s: no %s in package\n", name, zip_path.c_str());
- return StringValue("");
- }
+ ZipArchiveHandle za = static_cast<UpdaterInfo*>(state->cookie)->package_zip;
+ ZipString zip_string_path(zip_path.c_str());
+ ZipEntry entry;
+ if (FindEntry(za, zip_string_path, &entry) != 0) {
+ printf("%s: no %s in package\n", name, zip_path.c_str());
+ return StringValue("");
+ }
- int fd = TEMP_FAILURE_RETRY(ota_open(dest_path.c_str(), O_WRONLY | O_CREAT | O_TRUNC,
- S_IRUSR | S_IWUSR));
- if (fd == -1) {
- printf("%s: can't open %s for write: %s\n", name, dest_path.c_str(), strerror(errno));
- return StringValue("");
- }
- success = ExtractEntryToFile(za, &entry, fd);
- if (ota_fsync(fd) == -1) {
- printf("fsync of \"%s\" failed: %s\n", dest_path.c_str(), strerror(errno));
- success = false;
- }
- if (ota_close(fd) == -1) {
- printf("close of \"%s\" failed: %s\n", dest_path.c_str(), strerror(errno));
- success = false;
- }
+ int fd = TEMP_FAILURE_RETRY(
+ ota_open(dest_path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR));
+ if (fd == -1) {
+ printf("%s: can't open %s for write: %s\n", name, dest_path.c_str(), strerror(errno));
+ return StringValue("");
+ }
- return StringValue(success ? "t" : "");
- } else {
- // The one-argument version returns the contents of the file
- // as the result.
+ bool success = true;
+ int32_t ret = ExtractEntryToFile(za, &entry, fd);
+ if (ret != 0) {
+ printf("%s: Failed to extract entry \"%s\" (%u bytes) to \"%s\": %s\n", name,
+ zip_path.c_str(), entry.uncompressed_length, dest_path.c_str(), ErrorCodeString(ret));
+ success = false;
+ }
+ if (ota_fsync(fd) == -1) {
+ printf("fsync of \"%s\" failed: %s\n", dest_path.c_str(), strerror(errno));
+ success = false;
+ }
+ if (ota_close(fd) == -1) {
+ printf("close of \"%s\" failed: %s\n", dest_path.c_str(), strerror(errno));
+ success = false;
+ }
- std::vector<std::string> args;
- if (!ReadArgs(state, 1, argv, &args)) {
- return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse %d args", name,
- argc);
- }
- const std::string& zip_path = args[0];
+ return StringValue(success ? "t" : "");
+ } else {
+ // The one-argument version returns the contents of the file as the result.
- Value* v = new Value(VAL_INVALID, "");
+ std::vector<std::string> args;
+ if (!ReadArgs(state, 1, argv, &args)) {
+ return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse %d args", name, argc);
+ }
+ const std::string& zip_path = args[0];
- ZipArchiveHandle za = ((UpdaterInfo*)(state->cookie))->package_zip;
- ZipString zip_string_path(zip_path.c_str());
- ZipEntry entry;
- if (FindEntry(za, zip_string_path, &entry) != 0) {
- printf("%s: no %s in package\n", name, zip_path.c_str());
- return v;
- }
+ ZipArchiveHandle za = static_cast<UpdaterInfo*>(state->cookie)->package_zip;
+ ZipString zip_string_path(zip_path.c_str());
+ ZipEntry entry;
+ if (FindEntry(za, zip_string_path, &entry) != 0) {
+ return ErrorAbort(state, kPackageExtractFileFailure, "%s(): no %s in package", name,
+ zip_path.c_str());
+ }
- v->data.resize(entry.uncompressed_length);
- if (ExtractToMemory(za, &entry, reinterpret_cast<uint8_t*>(&v->data[0]),
- v->data.size()) != 0) {
- printf("%s: faled to extract %zu bytes to memory\n", name, v->data.size());
- } else {
- success = true;
- }
+ std::string buffer;
+ buffer.resize(entry.uncompressed_length);
- if (!success) {
- v->data.clear();
- } else {
- v->type = VAL_BLOB;
- }
- return v;
+ int32_t ret = ExtractToMemory(za, &entry, reinterpret_cast<uint8_t*>(&buffer[0]), buffer.size());
+ if (ret != 0) {
+ return ErrorAbort(state, kPackageExtractFileFailure,
+ "%s: Failed to extract entry \"%s\" (%zu bytes) to memory: %s", name,
+ zip_path.c_str(), buffer.size(), ErrorCodeString(ret));
}
+
+ return new Value(VAL_BLOB, buffer);
+ }
}
// symlink(target, [src1, src2, ...])
@@ -868,7 +866,6 @@ Value* GetPropFn(const char* name, State* state, int argc, Expr* argv[]) {
return StringValue(value);
}
-
// file_getprop(file, key)
//
// interprets 'file' as a getprop-style file (key=value pairs, one
@@ -1162,6 +1159,33 @@ Value* ReadFileFn(const char* name, State* state, int argc, Expr* argv[]) {
return v;
}
+// write_value(value, filename)
+// Writes 'value' to 'filename'.
+// Example: write_value("960000", "/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq")
+Value* WriteValueFn(const char* name, State* state, int argc, Expr* argv[]) {
+ if (argc != 2) {
+ return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc);
+ }
+
+ std::vector<std::string> args;
+ if (!ReadArgs(state, 2, argv, &args)) {
+ return ErrorAbort(state, kArgsParsingFailure, "%s(): Failed to parse the argument(s)", name);
+ }
+
+ const std::string& filename = args[1];
+ if (filename.empty()) {
+ return ErrorAbort(state, kArgsParsingFailure, "%s(): Filename cannot be empty", name);
+ }
+
+ const std::string& value = args[0];
+ if (!android::base::WriteStringToFile(value, filename)) {
+ printf("%s: Failed to write to \"%s\": %s\n", name, filename.c_str(), strerror(errno));
+ return StringValue("");
+ } else {
+ return StringValue("t");
+ }
+}
+
// Immediately reboot the device. Recovery is not finished normally,
// so if you reboot into recovery it will re-start applying the
// current package (because nothing has cleared the copy of the
@@ -1370,6 +1394,7 @@ void RegisterInstallFunctions() {
RegisterFunction("read_file", ReadFileFn);
RegisterFunction("sha1_check", Sha1CheckFn);
RegisterFunction("rename", RenameFn);
+ RegisterFunction("write_value", WriteValueFn);
RegisterFunction("wipe_cache", WipeCacheFn);
diff --git a/wear_ui.cpp b/wear_ui.cpp
index 17889076a..5433d110b 100644
--- a/wear_ui.cpp
+++ b/wear_ui.cpp
@@ -177,7 +177,7 @@ void WearRecoveryUI::draw_screen_locked()
// items don't fit on the screen.
if (menu_items > menu_end - menu_start) {
sprintf(cur_selection_str, "Current item: %d/%d", menu_sel + 1, menu_items);
- gr_text(x+4, y, cur_selection_str, 1);
+ gr_text(gr_sys_font(), x+4, y, cur_selection_str, 1);
y += char_height_+4;
}
@@ -192,10 +192,10 @@ void WearRecoveryUI::draw_screen_locked()
gr_fill(x, y-2, gr_fb_width()-x, y+char_height_+2);
// white text of selected item
SetColor(MENU_SEL_FG);
- if (menu[i][0]) gr_text(x+4, y, menu[i], 1);
+ if (menu[i][0]) gr_text(gr_sys_font(), x+4, y, menu[i], 1);
SetColor(MENU);
- } else {
- if (menu[i][0]) gr_text(x+4, y, menu[i], 0);
+ } else if (menu[i][0]) {
+ gr_text(gr_sys_font(), x+4, y, menu[i], 0);
}
y += char_height_+4;
}
@@ -216,7 +216,7 @@ void WearRecoveryUI::draw_screen_locked()
for (int ty = gr_fb_height() - char_height_ - outer_height;
ty > y+2 && count < text_rows;
ty -= char_height_, ++count) {
- gr_text(x+4, ty, text[row], 0);
+ gr_text(gr_sys_font(), x+4, ty, text[row], 0);
--row;
if (row < 0) row = text_rows-1;
}
@@ -285,7 +285,7 @@ void WearRecoveryUI::Init()
{
gr_init();
- gr_font_size(&char_width_, &char_height_);
+ gr_font_size(gr_sys_font(), &char_width_, &char_height_);
text_col = text_row = 0;
text_rows = (gr_fb_height()) / char_height_;