summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.mk11
-rw-r--r--README.md13
-rw-r--r--applypatch/Android.mk30
-rw-r--r--applypatch/applypatch.cpp16
-rw-r--r--applypatch/applypatch_main.cpp28
-rw-r--r--applypatch/applypatch_modes.cpp (renamed from applypatch/main.cpp)67
-rw-r--r--applypatch/applypatch_modes.h22
-rw-r--r--applypatch/include/applypatch/applypatch.h15
-rw-r--r--common.h11
-rw-r--r--edify/expr.cpp123
-rw-r--r--edify/expr.h25
-rw-r--r--install.cpp2
-rw-r--r--install.h3
-rw-r--r--otautil/SysUtil.cpp351
-rw-r--r--otautil/SysUtil.h47
-rw-r--r--print_sha1.h4
-rw-r--r--recovery-persist.cpp42
-rw-r--r--recovery-refresh.cpp58
-rw-r--r--recovery.cpp200
-rw-r--r--roots.h2
-rw-r--r--rotate_logs.cpp115
-rw-r--r--rotate_logs.h44
-rw-r--r--tests/Android.mk48
-rw-r--r--tests/common/test_constants.h15
-rw-r--r--tests/component/applypatch_test.cpp595
-rw-r--r--tests/component/updater_test.cpp134
-rw-r--r--tests/component/verifier_test.cpp11
-rw-r--r--tests/manual/recovery_test.cpp87
-rw-r--r--tests/testdata/bonus.filebin0 -> 557334 bytes
-rw-r--r--tests/testdata/boot.imgbin0 -> 783655 bytes
-rw-r--r--tests/testdata/recovery-from-boot-with-bonus.pbin0 -> 381615 bytes
-rw-r--r--tests/testdata/recovery-from-boot.pbin0 -> 5404 bytes
-rw-r--r--tests/testdata/recovery.imgbin0 -> 529707 bytes
-rw-r--r--tests/unit/recovery_test.cpp91
-rw-r--r--tests/unit/sysutil_test.cpp140
-rw-r--r--tests/unit/zip_test.cpp109
-rw-r--r--updater/blockimg.cpp54
-rw-r--r--updater/install.cpp924
-rw-r--r--verifier.cpp7
39 files changed, 1842 insertions, 1602 deletions
diff --git a/Android.mk b/Android.mk
index dbc5603d8..2943f016b 100644
--- a/Android.mk
+++ b/Android.mk
@@ -46,6 +46,7 @@ LOCAL_SRC_FILES := \
install.cpp \
recovery.cpp \
roots.cpp \
+ rotate_logs.cpp \
screen_ui.cpp \
ui.cpp \
verifier.cpp \
@@ -119,7 +120,9 @@ include $(BUILD_EXECUTABLE)
# recovery-persist (system partition dynamic executable run after /data mounts)
# ===============================
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := recovery-persist.cpp
+LOCAL_SRC_FILES := \
+ recovery-persist.cpp \
+ rotate_logs.cpp
LOCAL_MODULE := recovery-persist
LOCAL_SHARED_LIBRARIES := liblog libbase
LOCAL_CFLAGS := -Werror
@@ -129,9 +132,11 @@ include $(BUILD_EXECUTABLE)
# recovery-refresh (system partition dynamic executable run at init)
# ===============================
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := recovery-refresh.cpp
+LOCAL_SRC_FILES := \
+ recovery-refresh.cpp \
+ rotate_logs.cpp
LOCAL_MODULE := recovery-refresh
-LOCAL_SHARED_LIBRARIES := liblog
+LOCAL_SHARED_LIBRARIES := liblog libbase
LOCAL_CFLAGS := -Werror
LOCAL_INIT_RC := recovery-refresh.rc
include $(BUILD_EXECUTABLE)
diff --git a/README.md b/README.md
index 01fab9465..dc3d44e6e 100644
--- a/README.md
+++ b/README.md
@@ -27,3 +27,16 @@ Running the tests
# Or 64-bit device
adb shell /data/nativetest64/recovery_unit_test/recovery_unit_test
adb shell /data/nativetest64/recovery_component_test/recovery_component_test
+
+Running the manual tests
+------------------------
+
+`recovery-refresh` and `recovery-persist` executables exist only on systems without
+/cache partition. And we need to follow special steps to run tests for them.
+
+- Execute the test on an A/B device first. The test should fail but it will log
+ some contents to pmsg.
+
+- Reboot the device immediately and run the test again. The test should save the
+ contents of pmsg buffer into /data/misc/recovery/inject.txt. Test will pass if
+ this file has expected contents.
diff --git a/applypatch/Android.mk b/applypatch/Android.mk
index 9bbac4410..fa0fe8a37 100644
--- a/applypatch/Android.mk
+++ b/applypatch/Android.mk
@@ -68,21 +68,41 @@ LOCAL_STATIC_LIBRARIES += libcrypto libbz libz
LOCAL_CFLAGS := -Werror
include $(BUILD_HOST_STATIC_LIBRARY)
-# applypatch (executable)
+# libapplypatch_modes (static library)
# ===============================
include $(CLEAR_VARS)
LOCAL_CLANG := true
-LOCAL_SRC_FILES := main.cpp
+LOCAL_SRC_FILES := \
+ applypatch_modes.cpp
+LOCAL_MODULE := libapplypatch_modes
+LOCAL_C_INCLUDES := bootable/recovery
+LOCAL_STATIC_LIBRARIES := \
+ libapplypatch \
+ libbase \
+ libedify \
+ libcrypto
+LOCAL_CFLAGS := -Werror
+include $(BUILD_STATIC_LIBRARY)
+
+# applypatch (target executable)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_CLANG := true
+LOCAL_SRC_FILES := applypatch_main.cpp
LOCAL_MODULE := applypatch
-LOCAL_C_INCLUDES += bootable/recovery
-LOCAL_STATIC_LIBRARIES += \
+LOCAL_C_INCLUDES := bootable/recovery
+LOCAL_STATIC_LIBRARIES := \
+ libapplypatch_modes \
libapplypatch \
libbase \
libedify \
libotafault \
libcrypto \
libbz
-LOCAL_SHARED_LIBRARIES += libbase libz libcutils libc
+LOCAL_SHARED_LIBRARIES := \
+ libbase \
+ libz \
+ libcutils
LOCAL_CFLAGS := -Werror
include $(BUILD_EXECUTABLE)
diff --git a/applypatch/applypatch.cpp b/applypatch/applypatch.cpp
index 48e4c8386..9d505f9f6 100644
--- a/applypatch/applypatch.cpp
+++ b/applypatch/applypatch.cpp
@@ -530,8 +530,8 @@ int applypatch(const char* source_filename,
const char* target_sha1_str,
size_t target_size,
const std::vector<std::string>& patch_sha1_str,
- Value** patch_data,
- Value* bonus_data) {
+ const std::vector<std::unique_ptr<Value>>& patch_data,
+ const Value* bonus_data) {
printf("patch %s: ", source_filename);
if (target_filename[0] == '-' && target_filename[1] == '\0') {
@@ -546,8 +546,8 @@ int applypatch(const char* source_filename,
FileContents copy_file;
FileContents source_file;
- const Value* source_patch_value = NULL;
- const Value* copy_patch_value = NULL;
+ 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) {
@@ -571,11 +571,11 @@ int applypatch(const char* source_filename,
if (!source_file.data.empty()) {
int to_use = FindMatchingPatch(source_file.sha1, patch_sha1_str);
if (to_use >= 0) {
- source_patch_value = patch_data[to_use];
+ source_patch_value = patch_data[to_use].get();
}
}
- if (source_patch_value == NULL) {
+ if (source_patch_value == nullptr) {
source_file.data.clear();
printf("source file is bad; trying copy\n");
@@ -587,10 +587,10 @@ int applypatch(const char* source_filename,
int to_use = FindMatchingPatch(copy_file.sha1, patch_sha1_str);
if (to_use >= 0) {
- copy_patch_value = patch_data[to_use];
+ copy_patch_value = patch_data[to_use].get();
}
- if (copy_patch_value == NULL) {
+ if (copy_patch_value == nullptr) {
// fail.
printf("copy file doesn't match source SHA-1s either\n");
return 1;
diff --git a/applypatch/applypatch_main.cpp b/applypatch/applypatch_main.cpp
new file mode 100644
index 000000000..197077c93
--- /dev/null
+++ b/applypatch/applypatch_main.cpp
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "applypatch_modes.h"
+
+// This program (applypatch) applies binary patches to files in a way that
+// is safe (the original file is not touched until we have the desired
+// replacement for it) and idempotent (it's okay to run this program
+// multiple times).
+//
+// See the comments to applypatch_modes() function.
+
+int main(int argc, char** argv) {
+ return applypatch_modes(argc, const_cast<const char**>(argv));
+}
diff --git a/applypatch/main.cpp b/applypatch/applypatch_modes.cpp
index 294f7ee21..7b191a801 100644
--- a/applypatch/main.cpp
+++ b/applypatch/applypatch_modes.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "applypatch_modes.h"
+
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -23,11 +25,14 @@
#include <string>
#include <vector>
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+#include <openssl/sha.h>
+
#include "applypatch/applypatch.h"
#include "edify/expr.h"
-#include "openssl/sha.h"
-static int CheckMode(int argc, char** argv) {
+static int CheckMode(int argc, const char** argv) {
if (argc < 3) {
return 2;
}
@@ -39,44 +44,42 @@ static int CheckMode(int argc, char** argv) {
return applypatch_check(argv[2], sha1);
}
-static int SpaceMode(int argc, char** argv) {
+static int SpaceMode(int argc, const char** argv) {
if (argc != 3) {
return 2;
}
- char* endptr;
- size_t bytes = strtol(argv[2], &endptr, 10);
- if (bytes == 0 && endptr == argv[2]) {
+
+ size_t bytes;
+ if (!android::base::ParseUint(argv[2], &bytes) || bytes == 0) {
printf("can't parse \"%s\" as byte count\n\n", argv[2]);
return 1;
}
return CacheSizeCheck(bytes);
}
-// Parse arguments (which should be of the form "<sha1>:<filename>"
-// into the new parallel arrays *sha1s and *files.Returns true on
-// success.
-static bool ParsePatchArgs(int argc, char** argv, std::vector<std::string>* sha1s,
+// Parse arguments (which should be of the form "<sha1>:<filename>" into the
+// new parallel arrays *sha1s and *files. Returns true on success.
+static bool ParsePatchArgs(int argc, const char** argv, std::vector<std::string>* sha1s,
std::vector<FileContents>* files) {
if (sha1s == nullptr) {
return false;
}
for (int i = 0; i < argc; ++i) {
- uint8_t digest[SHA_DIGEST_LENGTH];
- char* colon = strchr(argv[i], ':');
- if (colon == nullptr) {
- printf("no ':' in patch argument \"%s\"\n", argv[i]);
+ std::vector<std::string> pieces = android::base::Split(argv[i], ":");
+ if (pieces.size() != 2) {
+ printf("failed to parse patch argument \"%s\"\n", argv[i]);
return false;
}
- *colon = '\0';
- ++colon;
- if (ParseSha1(argv[i], digest) != 0) {
+
+ uint8_t digest[SHA_DIGEST_LENGTH];
+ if (ParseSha1(pieces[0].c_str(), digest) != 0) {
printf("failed to parse sha1 \"%s\"\n", argv[i]);
return false;
}
- sha1s->push_back(argv[i]);
+ sha1s->push_back(pieces[0]);
FileContents fc;
- if (LoadFileContents(colon, &fc) != 0) {
+ if (LoadFileContents(pieces[1].c_str(), &fc) != 0) {
return false;
}
files->push_back(std::move(fc));
@@ -89,7 +92,7 @@ static int FlashMode(const char* src_filename, const char* tgt_filename,
return applypatch_flash(src_filename, tgt_filename, tgt_sha1, tgt_size);
}
-static int PatchMode(int argc, char** argv) {
+static int PatchMode(int argc, const char** argv) {
FileContents bonusFc;
Value bonus(VAL_INVALID, "");
@@ -108,9 +111,8 @@ static int PatchMode(int argc, char** argv) {
return 2;
}
- char* endptr;
- size_t target_size = strtol(argv[4], &endptr, 10);
- if (target_size == 0 && endptr == argv[4]) {
+ size_t target_size;
+ if (!android::base::ParseUint(argv[4], &target_size) || target_size == 0) {
printf("can't parse \"%s\" as byte count\n\n", argv[4]);
return 1;
}
@@ -123,25 +125,24 @@ static int PatchMode(int argc, char** argv) {
}
return FlashMode(argv[1], argv[2], argv[3], target_size);
}
+
std::vector<std::string> sha1s;
std::vector<FileContents> files;
if (!ParsePatchArgs(argc-5, argv+5, &sha1s, &files)) {
printf("failed to parse patch args\n");
return 1;
}
- std::vector<Value> patches;
- std::vector<Value*> patch_ptrs;
+
+ std::vector<std::unique_ptr<Value>> patches;
for (size_t i = 0; i < files.size(); ++i) {
- patches.push_back(Value(VAL_BLOB,
- std::string(files[i].data.cbegin(), files[i].data.cend())));
- patch_ptrs.push_back(&patches[i]);
+ patches.push_back(std::make_unique<Value>(
+ VAL_BLOB, std::string(files[i].data.cbegin(), files[i].data.cend())));
}
- return applypatch(argv[1], argv[2], argv[3], target_size,
- sha1s, patch_ptrs.data(), &bonus);
+ return applypatch(argv[1], argv[2], argv[3], target_size, sha1s, patches, &bonus);
}
-// This program applies binary patches to files in a way that is safe
-// (the original file is not touched until we have the desired
+// This program (applypatch) applies binary patches to files in a way that
+// is safe (the original file is not touched until we have the desired
// replacement for it) and idempotent (it's okay to run this program
// multiple times).
//
@@ -167,7 +168,7 @@ static int PatchMode(int argc, char** argv) {
// to read the source data. See the comments for the
// LoadPartitionContents() function for the format of such a filename.
-int main(int argc, char** argv) {
+int applypatch_modes(int argc, const char** argv) {
if (argc < 2) {
usage:
printf(
diff --git a/applypatch/applypatch_modes.h b/applypatch/applypatch_modes.h
new file mode 100644
index 000000000..3d9d08df5
--- /dev/null
+++ b/applypatch/applypatch_modes.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _APPLYPATCH_MODES_H
+#define _APPLYPATCH_MODES_H
+
+int applypatch_modes(int argc, const char** argv);
+
+#endif // _APPLYPATCH_MODES_H
diff --git a/applypatch/include/applypatch/applypatch.h b/applypatch/include/applypatch/applypatch.h
index 80d681638..ca3dafbc9 100644
--- a/applypatch/include/applypatch/applypatch.h
+++ b/applypatch/include/applypatch/applypatch.h
@@ -20,10 +20,12 @@
#include <stdint.h>
#include <sys/stat.h>
+#include <memory>
#include <string>
#include <vector>
-#include "openssl/sha.h"
+#include <openssl/sha.h>
+
#include "edify/expr.h"
struct FileContents {
@@ -41,27 +43,26 @@ struct FileContents {
typedef ssize_t (*SinkFn)(const unsigned char*, ssize_t, void*);
-// applypatch.c
+// applypatch.cpp
int ShowLicenses();
size_t FreeSpaceForFile(const char* filename);
int CacheSizeCheck(size_t bytes);
int ParseSha1(const char* str, uint8_t* digest);
-int applypatch_flash(const char* source_filename, const char* target_filename,
- const char* target_sha1_str, size_t target_size);
int applypatch(const char* source_filename,
const char* target_filename,
const char* target_sha1_str,
size_t target_size,
const std::vector<std::string>& patch_sha1_str,
- Value** patch_data,
- Value* bonus_data);
+ const std::vector<std::unique_ptr<Value>>& patch_data,
+ const Value* bonus_data);
int applypatch_check(const char* filename,
const std::vector<std::string>& patch_sha1_str);
+int applypatch_flash(const char* source_filename, const char* target_filename,
+ const char* target_sha1_str, size_t target_size);
int LoadFileContents(const char* filename, FileContents* file);
int SaveFileContents(const char* filename, const FileContents* file);
-void FreeFileContents(FileContents* file);
int FindMatchingPatch(uint8_t* sha1, const std::vector<std::string>& patch_sha1_str);
// bsdiff.cpp
diff --git a/common.h b/common.h
index a948fb1a0..319af3d72 100644
--- a/common.h
+++ b/common.h
@@ -17,15 +17,22 @@
#ifndef RECOVERY_COMMON_H
#define RECOVERY_COMMON_H
-#include <stdbool.h>
#include <stdio.h>
#include <stdarg.h>
#define STRINGIFY(x) #x
#define EXPAND(x) STRINGIFY(x)
+class RecoveryUI;
+
+extern RecoveryUI* ui;
extern bool modified_flash;
-typedef struct fstab_rec Volume;
+
+// The current stage, e.g. "1/2".
+extern const char* stage;
+
+// The reason argument provided in "--reason=".
+extern const char* reason;
// fopen a file, mounting volumes and making parent dirs as necessary.
FILE* fopen_path(const char *path, const char *mode);
diff --git a/edify/expr.cpp b/edify/expr.cpp
index ec2409752..329cf3acd 100644
--- a/edify/expr.cpp
+++ b/edify/expr.cpp
@@ -254,31 +254,25 @@ Value* LessThanIntFn(const char* name, State* state, int argc, Expr* argv[]) {
return nullptr;
}
- char* left;
- char* right;
- if (ReadArgs(state, argv, 2, &left, &right) < 0) return nullptr;
-
- bool result = false;
- char* end;
+ std::vector<std::string> args;
+ if (!ReadArgs(state, 2, argv, &args)) {
+ return nullptr;
+ }
// Parse up to at least long long or 64-bit integers.
- int64_t l_int = static_cast<int64_t>(strtoll(left, &end, 10));
- if (left[0] == '\0' || *end != '\0') {
- goto done;
+ int64_t l_int;
+ if (!android::base::ParseInt(args[0].c_str(), &l_int)) {
+ state->errmsg = "failed to parse int in " + args[0];
+ return nullptr;
}
int64_t r_int;
- r_int = static_cast<int64_t>(strtoll(right, &end, 10));
- if (right[0] == '\0' || *end != '\0') {
- goto done;
+ if (!android::base::ParseInt(args[1].c_str(), &r_int)) {
+ state->errmsg = "failed to parse int in " + args[1];
+ return nullptr;
}
- result = l_int < r_int;
-
- done:
- free(left);
- free(right);
- return StringValue(result ? "t" : "");
+ return StringValue(l_int < r_int ? "t" : "");
}
Value* GreaterThanIntFn(const char* name, State* state,
@@ -372,99 +366,6 @@ bool ReadValueArgs(State* state, int argc, Expr* argv[],
return true;
}
-// Evaluate the expressions in argv, giving 'count' char* (the ... is
-// zero or more char** to put them in). If any expression evaluates
-// to NULL, free the rest and return -1. Return 0 on success.
-int ReadArgs(State* state, Expr* argv[], int count, ...) {
- char** args = reinterpret_cast<char**>(malloc(count * sizeof(char*)));
- va_list v;
- va_start(v, count);
- int i;
- for (i = 0; i < count; ++i) {
- std::string str;
- if (!Evaluate(state, argv[i], &str) ||
- (args[i] = strdup(str.c_str())) == nullptr) {
- va_end(v);
- int j;
- for (j = 0; j < i; ++j) {
- free(args[j]);
- }
- free(args);
- return -1;
- }
- *(va_arg(v, char**)) = args[i];
- }
- va_end(v);
- free(args);
- return 0;
-}
-
-// Evaluate the expressions in argv, giving 'count' Value* (the ... is
-// zero or more Value** to put them in). If any expression evaluates
-// to NULL, free the rest and return -1. Return 0 on success.
-int ReadValueArgs(State* state, Expr* argv[], int count, ...) {
- Value** args = new Value*[count];
- va_list v;
- va_start(v, count);
- for (int i = 0; i < count; ++i) {
- args[i] = EvaluateValue(state, argv[i]);
- if (args[i] == NULL) {
- va_end(v);
- int j;
- for (j = 0; j < i; ++j) {
- delete args[j];
- }
- delete[] args;
- return -1;
- }
- *(va_arg(v, Value**)) = args[i];
- }
- va_end(v);
- delete[] args;
- return 0;
-}
-
-// Evaluate the expressions in argv, returning an array of char*
-// results. If any evaluate to NULL, free the rest and return NULL.
-// The caller is responsible for freeing the returned array and the
-// strings it contains.
-char** ReadVarArgs(State* state, int argc, Expr* argv[]) {
- char** args = (char**)malloc(argc * sizeof(char*));
- for (int i = 0; i < argc; ++i) {
- std::string str;
- if (!Evaluate(state, argv[i], &str) ||
- (args[i] = strdup(str.c_str())) == nullptr) {
- for (int j = 0; j < i; ++j) {
- free(args[j]);
- }
- free(args);
- return NULL;
- }
- }
- return args;
-}
-
-// Evaluate the expressions in argv, returning an array of Value*
-// results. If any evaluate to NULL, free the rest and return NULL.
-// The caller is responsible for freeing the returned array and the
-// Values it contains.
-Value** ReadValueVarArgs(State* state, int argc, Expr* argv[]) {
- Value** args = new Value*[argc];
- int i = 0;
- for (i = 0; i < argc; ++i) {
- args[i] = EvaluateValue(state, argv[i]);
- if (args[i] == NULL) {
- int j;
- for (j = 0; j < i; ++j) {
- delete args[j];
- }
- delete[] args;
- return NULL;
- }
- }
- return args;
-}
-
// Use printf-style arguments to compose an error message to put into
// *state. Returns nullptr.
Value* ErrorAbort(State* state, const char* format, ...) {
diff --git a/edify/expr.h b/edify/expr.h
index 85306542d..911adbc82 100644
--- a/edify/expr.h
+++ b/edify/expr.h
@@ -128,30 +128,7 @@ bool ReadArgs(State* state, int argc, Expr* argv[], std::vector<std::string>* ar
// Evaluate the expressions in argv, and put the results of Value* in
// args. If any expression evaluate to nullptr, free the rest and return
// false. Return true on success.
-bool ReadValueArgs(State* state, int argc, Expr* argv[],
- std::vector<std::unique_ptr<Value>>* args);
-
-// Evaluate the expressions in argv, giving 'count' char* (the ... is
-// zero or more char** to put them in). If any expression evaluates
-// to NULL, free the rest and return -1. Return 0 on success.
-int ReadArgs(State* state, Expr* argv[], int count, ...);
-
-// Evaluate the expressions in argv, giving 'count' Value* (the ... is
-// zero or more Value** to put them in). If any expression evaluates
-// to NULL, free the rest and return -1. Return 0 on success.
-int ReadValueArgs(State* state, Expr* argv[], int count, ...);
-
-// Evaluate the expressions in argv, returning an array of char*
-// results. If any evaluate to NULL, free the rest and return NULL.
-// The caller is responsible for freeing the returned array and the
-// strings it contains.
-char** ReadVarArgs(State* state, int argc, Expr* argv[]);
-
-// Evaluate the expressions in argv, returning an array of Value*
-// results. If any evaluate to NULL, free the rest and return NULL.
-// The caller is responsible for freeing the returned array and the
-// Values it contains.
-Value** ReadValueVarArgs(State* state, int argc, Expr* argv[]);
+bool ReadValueArgs(State* state, int argc, Expr* argv[], std::vector<std::unique_ptr<Value>>* args);
// Use printf-style arguments to compose an error message to put into
// *state. Returns NULL.
diff --git a/install.cpp b/install.cpp
index 919f241a2..f124a2688 100644
--- a/install.cpp
+++ b/install.cpp
@@ -47,8 +47,6 @@
#include "ui.h"
#include "verifier.h"
-extern RecoveryUI* ui;
-
#define ASSUMED_UPDATE_BINARY_NAME "META-INF/com/google/android/update-binary"
static constexpr const char* AB_OTA_PAYLOAD_PROPERTIES = "payload_properties.txt";
static constexpr const char* AB_OTA_PAYLOAD = "payload.bin";
diff --git a/install.h b/install.h
index 7f66a51ca..1ec01e817 100644
--- a/install.h
+++ b/install.h
@@ -20,10 +20,9 @@
#include <string>
#include <ziparchive/zip_archive.h>
-#include "common.h"
-
enum { INSTALL_SUCCESS, INSTALL_ERROR, INSTALL_CORRUPT, INSTALL_NONE, INSTALL_SKIPPED,
INSTALL_RETRY };
+
// Install the package specified by root_path. If INSTALL_SUCCESS is
// returned and *wipe_cache is true on exit, caller should wipe the
// cache partition.
diff --git a/otautil/SysUtil.cpp b/otautil/SysUtil.cpp
index efeb91c88..5156514f0 100644
--- a/otautil/SysUtil.cpp
+++ b/otautil/SysUtil.cpp
@@ -1,13 +1,25 @@
/*
* Copyright 2006 The Android Open Source Project
*
- * System utilities.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
+
+#include "SysUtil.h"
+
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
-#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
@@ -17,197 +29,190 @@
#include <sys/types.h>
#include <unistd.h>
-#include <android-base/logging.h>
+#include <algorithm>
+#include <string>
+#include <vector>
-#include "SysUtil.h"
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
static bool sysMapFD(int fd, MemMapping* pMap) {
- assert(pMap != NULL);
+ CHECK(pMap != nullptr);
- struct stat sb;
- if (fstat(fd, &sb) == -1) {
- PLOG(ERROR) << "fstat(" << fd << ") failed";
- return false;
- }
+ struct stat sb;
+ if (fstat(fd, &sb) == -1) {
+ PLOG(ERROR) << "fstat(" << fd << ") failed";
+ return false;
+ }
- void* memPtr = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
- if (memPtr == MAP_FAILED) {
- PLOG(ERROR) << "mmap(" << sb.st_size << ", R, PRIVATE, " << fd << ", 0) failed";
- return false;
- }
+ void* memPtr = mmap(nullptr, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (memPtr == MAP_FAILED) {
+ PLOG(ERROR) << "mmap(" << sb.st_size << ", R, PRIVATE, " << fd << ", 0) failed";
+ return false;
+ }
- pMap->addr = reinterpret_cast<unsigned char*>(memPtr);
- pMap->length = sb.st_size;
- pMap->range_count = 1;
- pMap->ranges = reinterpret_cast<MappedRange*>(malloc(sizeof(MappedRange)));
- if (pMap->ranges == NULL) {
- PLOG(ERROR) << "malloc failed";
- munmap(memPtr, sb.st_size);
- return false;
- }
- pMap->ranges[0].addr = memPtr;
- pMap->ranges[0].length = sb.st_size;
+ pMap->addr = static_cast<unsigned char*>(memPtr);
+ pMap->length = sb.st_size;
+ pMap->ranges.push_back({ memPtr, static_cast<size_t>(sb.st_size) });
- return true;
+ return true;
}
-static int sysMapBlockFile(FILE* mapf, MemMapping* pMap)
-{
- char block_dev[PATH_MAX+1];
- size_t size;
- unsigned int blksize;
- size_t blocks;
- unsigned int range_count;
- unsigned int i;
-
- if (fgets(block_dev, sizeof(block_dev), mapf) == NULL) {
- PLOG(ERROR) << "failed to read block device from header";
- return -1;
- }
- for (i = 0; i < sizeof(block_dev); ++i) {
- if (block_dev[i] == '\n') {
- block_dev[i] = 0;
- break;
- }
- }
-
- if (fscanf(mapf, "%zu %u\n%u\n", &size, &blksize, &range_count) != 3) {
- LOG(ERROR) << "failed to parse block map header";
- return -1;
- }
- if (blksize != 0) {
- blocks = ((size-1) / blksize) + 1;
- }
- if (size == 0 || blksize == 0 || blocks > SIZE_MAX / blksize || range_count == 0) {
- LOG(ERROR) << "invalid data in block map file: size " << size << ", blksize " << blksize
- << ", range_count " << range_count;
- return -1;
- }
+// A "block map" which looks like this (from uncrypt/uncrypt.cpp):
+//
+// /dev/block/platform/msm_sdcc.1/by-name/userdata # block device
+// 49652 4096 # file size in bytes, block size
+// 3 # count of block ranges
+// 1000 1008 # block range 0
+// 2100 2102 # ... block range 1
+// 30 33 # ... block range 2
+//
+// Each block range represents a half-open interval; the line "30 33"
+// reprents the blocks [30, 31, 32].
+static int sysMapBlockFile(const char* filename, MemMapping* pMap) {
+ CHECK(pMap != nullptr);
+
+ std::string content;
+ if (!android::base::ReadFileToString(filename, &content)) {
+ PLOG(ERROR) << "Failed to read " << filename;
+ return -1;
+ }
+
+ std::vector<std::string> lines = android::base::Split(android::base::Trim(content), "\n");
+ if (lines.size() < 4) {
+ LOG(ERROR) << "Block map file is too short: " << lines.size();
+ return -1;
+ }
+
+ size_t size;
+ unsigned int blksize;
+ if (sscanf(lines[1].c_str(), "%zu %u", &size, &blksize) != 2) {
+ LOG(ERROR) << "Failed to parse file size and block size: " << lines[1];
+ return -1;
+ }
+
+ size_t range_count;
+ if (sscanf(lines[2].c_str(), "%zu", &range_count) != 1) {
+ LOG(ERROR) << "Failed to parse block map header: " << lines[2];
+ return -1;
+ }
+
+ size_t blocks;
+ if (blksize != 0) {
+ blocks = ((size - 1) / blksize) + 1;
+ }
+ if (size == 0 || blksize == 0 || blocks > SIZE_MAX / blksize || range_count == 0 ||
+ lines.size() != 3 + range_count) {
+ LOG(ERROR) << "Invalid data in block map file: size " << size << ", blksize " << blksize
+ << ", range_count " << range_count << ", lines " << lines.size();
+ return -1;
+ }
+
+ // Reserve enough contiguous address space for the whole file.
+ void* reserve = mmap64(nullptr, blocks * blksize, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0);
+ if (reserve == MAP_FAILED) {
+ PLOG(ERROR) << "failed to reserve address space";
+ return -1;
+ }
+
+ const std::string& block_dev = lines[0];
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(block_dev.c_str(), O_RDONLY)));
+ if (fd == -1) {
+ PLOG(ERROR) << "failed to open block device " << block_dev;
+ munmap(reserve, blocks * blksize);
+ return -1;
+ }
+
+ pMap->ranges.resize(range_count);
+
+ unsigned char* next = static_cast<unsigned char*>(reserve);
+ size_t remaining_size = blocks * blksize;
+ bool success = true;
+ for (size_t i = 0; i < range_count; ++i) {
+ const std::string& line = lines[i + 3];
+
+ size_t start, end;
+ if (sscanf(line.c_str(), "%zu %zu\n", &start, &end) != 2) {
+ LOG(ERROR) << "failed to parse range " << i << " in block map: " << line;
+ success = false;
+ break;
+ }
+ size_t length = (end - start) * blksize;
+ if (end <= start || (end - start) > SIZE_MAX / blksize || length > remaining_size) {
+ LOG(ERROR) << "unexpected range in block map: " << start << " " << end;
+ success = false;
+ break;
+ }
+
+ void* addr = mmap64(next, length, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd,
+ static_cast<off64_t>(start) * blksize);
+ if (addr == MAP_FAILED) {
+ PLOG(ERROR) << "failed to map block " << i;
+ success = false;
+ break;
+ }
+ pMap->ranges[i].addr = addr;
+ pMap->ranges[i].length = length;
+
+ next += length;
+ remaining_size -= length;
+ }
+ if (success && remaining_size != 0) {
+ LOG(ERROR) << "ranges in block map are invalid: remaining_size = " << remaining_size;
+ success = false;
+ }
+ if (!success) {
+ munmap(reserve, blocks * blksize);
+ return -1;
+ }
+
+ pMap->addr = static_cast<unsigned char*>(reserve);
+ pMap->length = size;
+
+ LOG(INFO) << "mmapped " << range_count << " ranges";
+
+ return 0;
+}
- pMap->range_count = range_count;
- pMap->ranges = reinterpret_cast<MappedRange*>(calloc(range_count, sizeof(MappedRange)));
- if (pMap->ranges == NULL) {
- PLOG(ERROR) << "calloc(" << range_count << ", " << sizeof(MappedRange) << ") failed";
- return -1;
- }
+int sysMapFile(const char* fn, MemMapping* pMap) {
+ if (fn == nullptr || pMap == nullptr) {
+ LOG(ERROR) << "Invalid argument(s)";
+ return -1;
+ }
- // Reserve enough contiguous address space for the whole file.
- unsigned char* reserve = reinterpret_cast<unsigned char*>(mmap64(NULL, blocks * blksize,
- PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0));
- if (reserve == MAP_FAILED) {
- PLOG(ERROR) << "failed to reserve address space";
- free(pMap->ranges);
- return -1;
- }
+ *pMap = {};
- int fd = open(block_dev, O_RDONLY);
- if (fd < 0) {
- PLOG(ERROR) << "failed to open block device " << block_dev;
- munmap(reserve, blocks * blksize);
- free(pMap->ranges);
- return -1;
+ if (fn[0] == '@') {
+ if (sysMapBlockFile(fn + 1, pMap) != 0) {
+ LOG(ERROR) << "Map of '" << fn << "' failed";
+ return -1;
}
-
- unsigned char* next = reserve;
- size_t remaining_size = blocks * blksize;
- bool success = true;
- for (i = 0; i < range_count; ++i) {
- size_t start, end;
- if (fscanf(mapf, "%zu %zu\n", &start, &end) != 2) {
- LOG(ERROR) << "failed to parse range " << i << " in block map";
- success = false;
- break;
- }
- size_t length = (end - start) * blksize;
- if (end <= start || ((end - start) > SIZE_MAX / blksize) || length > remaining_size) {
- LOG(ERROR) << "unexpected range in block map: " << start << " " << end;
- success = false;
- break;
- }
-
- void* addr = mmap64(next, length, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, ((off64_t)(start*blksize)));
- if (addr == MAP_FAILED) {
- PLOG(ERROR) << "failed to map block " << i;
- success = false;
- break;
- }
- pMap->ranges[i].addr = addr;
- pMap->ranges[i].length = length;
-
- next += length;
- remaining_size -= length;
+ } else {
+ // This is a regular file.
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(fn, O_RDONLY)));
+ if (fd == -1) {
+ PLOG(ERROR) << "Unable to open '" << fn << "'";
+ return -1;
}
- if (success && remaining_size != 0) {
- LOG(ERROR) << "ranges in block map are invalid: remaining_size = " << remaining_size;
- success = false;
- }
- if (!success) {
- close(fd);
- munmap(reserve, blocks * blksize);
- free(pMap->ranges);
- return -1;
- }
-
- close(fd);
- pMap->addr = reserve;
- pMap->length = size;
-
- LOG(INFO) << "mmapped " << range_count << " ranges";
-
- return 0;
-}
-int sysMapFile(const char* fn, MemMapping* pMap)
-{
- memset(pMap, 0, sizeof(*pMap));
-
- if (fn && fn[0] == '@') {
- // A map of blocks
- FILE* mapf = fopen(fn+1, "r");
- if (mapf == NULL) {
- PLOG(ERROR) << "Unable to open '" << (fn+1) << "'";
- return -1;
- }
-
- if (sysMapBlockFile(mapf, pMap) != 0) {
- LOG(ERROR) << "Map of '" << fn << "' failed";
- fclose(mapf);
- return -1;
- }
-
- fclose(mapf);
- } else {
- // This is a regular file.
- int fd = open(fn, O_RDONLY);
- if (fd == -1) {
- PLOG(ERROR) << "Unable to open '" << fn << "'";
- return -1;
- }
-
- if (!sysMapFD(fd, pMap)) {
- LOG(ERROR) << "Map of '" << fn << "' failed";
- close(fd);
- return -1;
- }
-
- close(fd);
+ if (!sysMapFD(fd, pMap)) {
+ LOG(ERROR) << "Map of '" << fn << "' failed";
+ return -1;
}
- return 0;
+ }
+ return 0;
}
/*
* Release a memory mapping.
*/
-void sysReleaseMap(MemMapping* pMap)
-{
- int i;
- for (i = 0; i < pMap->range_count; ++i) {
- if (munmap(pMap->ranges[i].addr, pMap->ranges[i].length) < 0) {
- PLOG(ERROR) << "munmap(" << pMap->ranges[i].addr << ", " << pMap->ranges[i].length
- << ") failed";
- }
+void sysReleaseMap(MemMapping* pMap) {
+ std::for_each(pMap->ranges.cbegin(), pMap->ranges.cend(), [](const MappedRange& range) {
+ if (munmap(range.addr, range.length) == -1) {
+ PLOG(ERROR) << "munmap(" << range.addr << ", " << range.length << ") failed";
}
- free(pMap->ranges);
- pMap->ranges = NULL;
- pMap->range_count = 0;
+ });
+ pMap->ranges.clear();
}
diff --git a/otautil/SysUtil.h b/otautil/SysUtil.h
index 7adff1e54..6a79bf31f 100644
--- a/otautil/SysUtil.h
+++ b/otautil/SysUtil.h
@@ -1,33 +1,40 @@
/*
* Copyright 2006 The Android Open Source Project
*
- * System utilities.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
-#ifndef _MINZIP_SYSUTIL
-#define _MINZIP_SYSUTIL
-#include <stdio.h>
+#ifndef _OTAUTIL_SYSUTIL
+#define _OTAUTIL_SYSUTIL
+
#include <sys/types.h>
-#ifdef __cplusplus
-extern "C" {
-#endif
+#include <vector>
-typedef struct MappedRange {
- void* addr;
- size_t length;
-} MappedRange;
+struct MappedRange {
+ void* addr;
+ size_t length;
+};
/*
* Use this to keep track of mapped segments.
*/
-typedef struct MemMapping {
- unsigned char* addr; /* start of data */
- size_t length; /* length of data */
+struct MemMapping {
+ unsigned char* addr; /* start of data */
+ size_t length; /* length of data */
- int range_count;
- MappedRange* ranges;
-} MemMapping;
+ std::vector<MappedRange> ranges;
+};
/*
* Map a file into a private, read-only memory segment. If 'fn'
@@ -45,8 +52,4 @@ int sysMapFile(const char* fn, MemMapping* pMap);
*/
void sysReleaseMap(MemMapping* pMap);
-#ifdef __cplusplus
-}
-#endif
-
-#endif /*_MINZIP_SYSUTIL*/
+#endif // _OTAUTIL_SYSUTIL
diff --git a/print_sha1.h b/print_sha1.h
index c7c1f3651..1f8589519 100644
--- a/print_sha1.h
+++ b/print_sha1.h
@@ -20,7 +20,7 @@
#include <stdint.h>
#include <string>
-#include "openssl/sha.h"
+#include <openssl/sha.h>
static std::string print_sha1(const uint8_t* sha1, size_t len) {
const char* hex = "0123456789abcdef";
@@ -41,7 +41,7 @@ static std::string short_sha1(const uint8_t sha1[SHA_DIGEST_LENGTH]) {
}
static std::string print_hex(const uint8_t* bytes, size_t len) {
- return print_sha1(bytes, len);
+ return print_sha1(bytes, len);
}
#endif // RECOVERY_PRINT_SHA1_H
diff --git a/recovery-persist.cpp b/recovery-persist.cpp
index b0ec141cb..d706ccac8 100644
--- a/recovery-persist.cpp
+++ b/recovery-persist.cpp
@@ -30,7 +30,6 @@
// --force-persist ignore /cache mount, always rotate in the contents.
//
-#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -40,17 +39,16 @@
#include <android-base/file.h>
#include <android-base/logging.h>
-
#include <private/android_logger.h> /* private pmsg functions */
+#include "rotate_logs.h"
+
static const char *LAST_LOG_FILE = "/data/misc/recovery/last_log";
static const char *LAST_PMSG_FILE = "/sys/fs/pstore/pmsg-ramoops-0";
static const char *LAST_KMSG_FILE = "/data/misc/recovery/last_kmsg";
static const char *LAST_CONSOLE_FILE = "/sys/fs/pstore/console-ramoops-0";
static const char *ALT_LAST_CONSOLE_FILE = "/sys/fs/pstore/console-ramoops";
-static const int KEEP_LOG_COUNT = 10;
-
// close a file, log an error if the error indicator is set
static void check_and_fclose(FILE *fp, const char *name) {
fflush(fp);
@@ -80,39 +78,6 @@ static void copy_file(const char* source, const char* destination) {
static bool rotated = false;
-// Rename last_log -> last_log.1 -> last_log.2 -> ... -> last_log.$max.
-// Similarly rename last_kmsg -> last_kmsg.1 -> ... -> last_kmsg.$max.
-// Overwrite any existing last_log.$max and last_kmsg.$max.
-static void rotate_logs(int max) {
- // Logs should only be rotated once.
-
- if (rotated) {
- return;
- }
- rotated = true;
-
- for (int i = max-1; i >= 0; --i) {
- std::string old_log(LAST_LOG_FILE);
- if (i > 0) {
- old_log += "." + std::to_string(i);
- }
- std::string new_log(LAST_LOG_FILE);
- new_log += "." + std::to_string(i+1);
-
- // Ignore errors if old_log doesn't exist.
- rename(old_log.c_str(), new_log.c_str());
-
- std::string old_kmsg(LAST_KMSG_FILE);
- if (i > 0) {
- old_kmsg += "." + std::to_string(i);
- }
- std::string new_kmsg(LAST_KMSG_FILE);
- new_kmsg += "." + std::to_string(i+1);
-
- rename(old_kmsg.c_str(), new_kmsg.c_str());
- }
-}
-
ssize_t logsave(
log_id_t /* logId */,
char /* prio */,
@@ -138,7 +103,8 @@ ssize_t logsave(
// already-rotated files? Algorithm thus far is KISS: one file,
// one rotation allowed.
- rotate_logs(KEEP_LOG_COUNT);
+ rotate_logs(LAST_LOG_FILE, LAST_KMSG_FILE);
+ rotated = true;
return android::base::WriteStringToFile(buffer, destination.c_str());
}
diff --git a/recovery-refresh.cpp b/recovery-refresh.cpp
index b2ab52f7e..14565d3f4 100644
--- a/recovery-refresh.cpp
+++ b/recovery-refresh.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "recovery-refresh"
-
//
// Strictly to deal with reboot into system after OTA, then
// reboot while in system before boot complete landing us back
@@ -40,64 +38,11 @@
//
#include <string.h>
-
#include <string>
-#include <android/log.h> /* Android Log Priority Tags */
#include <private/android_logger.h> /* private pmsg functions */
-static const char LAST_KMSG_FILE[] = "recovery/last_kmsg";
-static const char LAST_LOG_FILE[] = "recovery/last_log";
-
-static ssize_t logbasename(
- log_id_t /* logId */,
- char /* prio */,
- const char *filename,
- const char * /* buf */, size_t len,
- void *arg) {
- if (strstr(LAST_KMSG_FILE, filename) ||
- strstr(LAST_LOG_FILE, filename)) {
- bool *doRotate = reinterpret_cast<bool *>(arg);
- *doRotate = true;
- }
- return len;
-}
-
-static ssize_t logrotate(
- log_id_t logId,
- char prio,
- const char *filename,
- const char *buf, size_t len,
- void *arg) {
- bool *doRotate = reinterpret_cast<bool *>(arg);
- if (!*doRotate) {
- return __android_log_pmsg_file_write(logId, prio, filename, buf, len);
- }
-
- std::string name(filename);
- size_t dot = name.find_last_of('.');
- std::string sub = name.substr(0, dot);
-
- if (!strstr(LAST_KMSG_FILE, sub.c_str()) &&
- !strstr(LAST_LOG_FILE, sub.c_str())) {
- return __android_log_pmsg_file_write(logId, prio, filename, buf, len);
- }
-
- // filename rotation
- if (dot == std::string::npos) {
- name += ".1";
- } else {
- std::string number = name.substr(dot + 1);
- if (!isdigit(number.data()[0])) {
- name += ".1";
- } else {
- auto i = std::stoull(number);
- name = sub + "." + std::to_string(i + 1);
- }
- }
-
- return __android_log_pmsg_file_write(logId, prio, name.c_str(), buf, len);
-}
+#include "rotate_logs.h"
int main(int argc, char **argv) {
static const char filter[] = "recovery/";
@@ -105,7 +50,6 @@ int main(int argc, char **argv) {
static const char rotate_flag[] = "--rotate";
ssize_t ret;
bool doRotate = false;
-
// Take last pmsg contents and rewrite it to the current pmsg session.
if ((argc <= 1) || !argv[1] ||
(((doRotate = strcmp(argv[1], rotate_flag))) &&
diff --git a/recovery.cpp b/recovery.cpp
index 4d1ad1df2..02d460eb9 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -66,10 +66,9 @@
#include "minui/minui.h"
#include "otautil/DirUtil.h"
#include "roots.h"
-#include "ui.h"
+#include "rotate_logs.h"
#include "screen_ui.h"
-
-struct selabel_handle *sehandle;
+#include "ui.h"
static const struct option OPTIONS[] = {
{ "update_package", required_argument, NULL, 'u' },
@@ -110,7 +109,6 @@ static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log";
static const char *TEMPORARY_INSTALL_FILE = "/tmp/last_install";
static const char *LAST_KMSG_FILE = "/cache/recovery/last_kmsg";
static const char *LAST_LOG_FILE = "/cache/recovery/last_log";
-static const int KEEP_LOG_COUNT = 10;
// We will try to apply the update package 5 times at most in case of an I/O error.
static const int EIO_RETRY_COUNT = 4;
static const int BATTERY_READ_TIMEOUT_IN_SEC = 10;
@@ -119,15 +117,18 @@ static const int BATTERY_READ_TIMEOUT_IN_SEC = 10;
// So we should check battery with a slightly lower limitation.
static const int BATTERY_OK_PERCENTAGE = 20;
static const int BATTERY_WITH_CHARGER_OK_PERCENTAGE = 15;
-constexpr const char* RECOVERY_WIPE = "/etc/recovery.wipe";
+static constexpr const char* RECOVERY_WIPE = "/etc/recovery.wipe";
+static constexpr const char* DEFAULT_LOCALE = "en_US";
-RecoveryUI* ui = NULL;
-static const char* locale = "en_US";
-char* stage = NULL;
-char* reason = NULL;
-bool modified_flash = false;
+static std::string locale;
static bool has_cache = false;
+RecoveryUI* ui = nullptr;
+bool modified_flash = false;
+const char* stage = nullptr;
+const char* reason = nullptr;
+struct selabel_handle* sehandle;
+
/*
* The recovery tool communicates with the main system through /cache files.
* /cache/recovery/command - INPUT - command line for tool, one arg per line
@@ -206,6 +207,9 @@ FILE* fopen_path(const char *path, const char *mode) {
// close a file, log an error if the error indicator is set
static void check_and_fclose(FILE *fp, const char *name) {
fflush(fp);
+ if (fsync(fileno(fp)) == -1) {
+ PLOG(ERROR) << "Failed to fsync " << name;
+ }
if (ferror(fp)) {
PLOG(ERROR) << "Error in " << name;
}
@@ -447,37 +451,6 @@ static void copy_log_file(const char* source, const char* destination, bool appe
}
}
-// Rename last_log -> last_log.1 -> last_log.2 -> ... -> last_log.$max.
-// Similarly rename last_kmsg -> last_kmsg.1 -> ... -> last_kmsg.$max.
-// Overwrite any existing last_log.$max and last_kmsg.$max.
-static void rotate_logs(int max) {
- // Logs should only be rotated once.
- static bool rotated = false;
- if (rotated) {
- return;
- }
- rotated = true;
- ensure_path_mounted(LAST_LOG_FILE);
- ensure_path_mounted(LAST_KMSG_FILE);
-
- for (int i = max-1; i >= 0; --i) {
- std::string old_log = android::base::StringPrintf("%s", LAST_LOG_FILE);
- if (i > 0) {
- old_log += "." + std::to_string(i);
- }
- std::string new_log = android::base::StringPrintf("%s.%d", LAST_LOG_FILE, i+1);
- // Ignore errors if old_log doesn't exist.
- rename(old_log.c_str(), new_log.c_str());
-
- std::string old_kmsg = android::base::StringPrintf("%s", LAST_KMSG_FILE);
- if (i > 0) {
- old_kmsg += "." + std::to_string(i);
- }
- std::string new_kmsg = android::base::StringPrintf("%s.%d", LAST_KMSG_FILE, i+1);
- rename(old_kmsg.c_str(), new_kmsg.c_str());
- }
-}
-
static void copy_logs() {
// We only rotate and record the log of the current session if there are
// actual attempts to modify the flash, such as wipes, installs from BCB
@@ -496,7 +469,9 @@ static void copy_logs() {
return;
}
- rotate_logs(KEEP_LOG_COUNT);
+ ensure_path_mounted(LAST_LOG_FILE);
+ ensure_path_mounted(LAST_KMSG_FILE);
+ rotate_logs(LAST_LOG_FILE, LAST_KMSG_FILE);
// Copy logs to cache so the system can find out what happened.
copy_log_file(TEMPORARY_LOG_FILE, LOG_FILE, true);
@@ -515,24 +490,18 @@ static void copy_logs() {
// clear the recovery command and prepare to boot a (hopefully working) system,
// copy our log file to cache as well (for the system to read). This function is
// idempotent: call it as many times as you like.
-static void
-finish_recovery() {
+static void finish_recovery() {
// Save the locale to cache, so if recovery is next started up
// without a --locale argument (eg, directly from the bootloader)
// it will use the last-known locale.
- if (locale != NULL) {
- size_t len = strlen(locale);
- __pmsg_write(LOCALE_FILE, locale, len);
- if (has_cache) {
- LOG(INFO) << "Saving locale \"" << locale << "\"";
- FILE* fp = fopen_path(LOCALE_FILE, "w");
- if (fp != NULL) {
- fwrite(locale, 1, len, fp);
- fflush(fp);
- fsync(fileno(fp));
- check_and_fclose(fp, LOCALE_FILE);
- }
+ if (!locale.empty() && has_cache) {
+ LOG(INFO) << "Saving locale \"" << locale << "\"";
+
+ FILE* fp = fopen_path(LOCALE_FILE, "w");
+ if (!android::base::WriteStringToFd(locale, fileno(fp))) {
+ PLOG(ERROR) << "Failed to save locale to " << LOCALE_FILE;
}
+ check_and_fclose(fp, LOCALE_FILE);
}
copy_logs();
@@ -1282,40 +1251,32 @@ print_property(const char *key, const char *name, void *cookie) {
printf("%s=%s\n", key, name);
}
-static void
-load_locale_from_cache() {
- FILE* fp = fopen_path(LOCALE_FILE, "r");
- char buffer[80];
- if (fp != NULL) {
- fgets(buffer, sizeof(buffer), fp);
- int j = 0;
- unsigned int i;
- for (i = 0; i < sizeof(buffer) && buffer[i]; ++i) {
- if (!isspace(buffer[i])) {
- buffer[j++] = buffer[i];
- }
- }
- buffer[j] = 0;
- locale = strdup(buffer);
- check_and_fclose(fp, LOCALE_FILE);
+static std::string load_locale_from_cache() {
+ if (ensure_path_mounted(LOCALE_FILE) != 0) {
+ LOG(ERROR) << "Can't mount " << LOCALE_FILE;
+ return "";
}
-}
-static RecoveryUI* gCurrentUI = NULL;
+ std::string content;
+ if (!android::base::ReadFileToString(LOCALE_FILE, &content)) {
+ PLOG(ERROR) << "Can't read " << LOCALE_FILE;
+ return "";
+ }
-void
-ui_print(const char* format, ...) {
- char buffer[256];
+ return android::base::Trim(content);
+}
+void ui_print(const char* format, ...) {
+ std::string buffer;
va_list ap;
va_start(ap, format);
- vsnprintf(buffer, sizeof(buffer), format, ap);
+ android::base::StringAppendV(&buffer, format, ap);
va_end(ap);
- if (gCurrentUI != NULL) {
- gCurrentUI->Print("%s", buffer);
+ if (ui != nullptr) {
+ ui->Print("%s", buffer.c_str());
} else {
- fputs(buffer, stdout);
+ fputs(buffer.c_str(), stdout);
}
}
@@ -1324,8 +1285,8 @@ static constexpr char log_characters[] = "VDIWEF";
void UiLogger(android::base::LogId id, android::base::LogSeverity severity,
const char* tag, const char* file, unsigned int line,
const char* message) {
- if (severity >= android::base::ERROR && gCurrentUI != NULL) {
- gCurrentUI->Print("E:%s\n", message);
+ if (severity >= android::base::ERROR && ui != nullptr) {
+ ui->Print("E:%s\n", message);
} else {
fprintf(stdout, "%c:%s\n", log_characters[severity], message);
}
@@ -1421,63 +1382,13 @@ static void log_failure_code(ErrorCode code, const char *update_package) {
};
std::string log_content = android::base::Join(log_buffer, "\n");
if (!android::base::WriteStringToFile(log_content, TEMPORARY_INSTALL_FILE)) {
- PLOG(ERROR) << "failed to write " << TEMPORARY_INSTALL_FILE;
+ PLOG(ERROR) << "failed to write " << TEMPORARY_INSTALL_FILE;
}
// Also write the info into last_log.
LOG(INFO) << log_content;
}
-static ssize_t logbasename(
- log_id_t /* logId */,
- char /* prio */,
- const char *filename,
- const char * /* buf */, size_t len,
- void *arg) {
- if (strstr(LAST_KMSG_FILE, filename) ||
- strstr(LAST_LOG_FILE, filename)) {
- bool *doRotate = reinterpret_cast<bool *>(arg);
- *doRotate = true;
- }
- return len;
-}
-
-static ssize_t logrotate(
- log_id_t logId,
- char prio,
- const char *filename,
- const char *buf, size_t len,
- void *arg) {
- bool *doRotate = reinterpret_cast<bool *>(arg);
- if (!*doRotate) {
- return __android_log_pmsg_file_write(logId, prio, filename, buf, len);
- }
-
- std::string name(filename);
- size_t dot = name.find_last_of('.');
- std::string sub = name.substr(0, dot);
-
- if (!strstr(LAST_KMSG_FILE, sub.c_str()) &&
- !strstr(LAST_LOG_FILE, sub.c_str())) {
- return __android_log_pmsg_file_write(logId, prio, filename, buf, len);
- }
-
- // filename rotation
- if (dot == std::string::npos) {
- name += ".1";
- } else {
- std::string number = name.substr(dot + 1);
- if (!isdigit(number.data()[0])) {
- name += ".1";
- } else {
- auto i = std::stoull(number);
- name = sub + "." + std::to_string(i + 1);
- }
- }
-
- return __android_log_pmsg_file_write(logId, prio, name.c_str(), buf, len);
-}
-
int main(int argc, char **argv) {
// We don't have logcat yet under recovery; so we'll print error on screen and
// log to stdout (which is redirected to recovery.log) as we used to do.
@@ -1487,6 +1398,7 @@ int main(int argc, char **argv) {
static const char filter[] = "recovery/";
// Do we need to rotate?
bool doRotate = false;
+
__android_log_pmsg_file_read(
LOG_ID_SYSTEM, ANDROID_LOG_INFO, filter,
logbasename, &doRotate);
@@ -1573,18 +1485,24 @@ int main(int argc, char **argv) {
}
}
- if (locale == nullptr && has_cache) {
- load_locale_from_cache();
+ if (locale.empty()) {
+ if (has_cache) {
+ locale = load_locale_from_cache();
+ }
+
+ if (locale.empty()) {
+ locale = DEFAULT_LOCALE;
+ }
}
- printf("locale is [%s]\n", locale);
+
+ printf("locale is [%s]\n", locale.c_str());
printf("stage is [%s]\n", stage);
printf("reason is [%s]\n", reason);
Device* device = make_device();
ui = device->GetUI();
- gCurrentUI = ui;
- ui->SetLocale(locale);
+ ui->SetLocale(locale.c_str());
ui->Init();
// Set background string to "installing security update" for security update,
// otherwise set it to "installing system update".
@@ -1599,7 +1517,7 @@ int main(int argc, char **argv) {
if (show_text) ui->ShowText(true);
struct selinux_opt seopts[] = {
- { SELABEL_OPT_PATH, "/file_contexts" }
+ { SELABEL_OPT_PATH, "/file_contexts" }
};
sehandle = selabel_open(SELABEL_CTX_FILE, seopts, 1);
@@ -1771,7 +1689,7 @@ int main(int argc, char **argv) {
break;
}
while (true) {
- pause();
+ pause();
}
// Should be unreachable.
return EXIT_SUCCESS;
diff --git a/roots.h b/roots.h
index a14b7d971..542f03b9c 100644
--- a/roots.h
+++ b/roots.h
@@ -17,7 +17,7 @@
#ifndef RECOVERY_ROOTS_H_
#define RECOVERY_ROOTS_H_
-#include "common.h"
+typedef struct fstab_rec Volume;
// Load and parse volume data from /etc/recovery.fstab.
void load_volume_table();
diff --git a/rotate_logs.cpp b/rotate_logs.cpp
new file mode 100644
index 000000000..fc220215e
--- /dev/null
+++ b/rotate_logs.cpp
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "rotate_logs.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/stringprintf.h>
+#include <private/android_logger.h> /* private pmsg functions */
+
+static const std::string LAST_KMSG_FILTER = "recovery/last_kmsg";
+static const std::string LAST_LOG_FILTER = "recovery/last_log";
+
+ssize_t logbasename(
+ log_id_t /* logId */,
+ char /* prio */,
+ const char *filename,
+ const char * /* buf */, size_t len,
+ void *arg) {
+ bool* doRotate = static_cast<bool*>(arg);
+ if (LAST_KMSG_FILTER.find(filename) != std::string::npos ||
+ LAST_LOG_FILTER.find(filename) != std::string::npos) {
+ *doRotate = true;
+ }
+ return len;
+}
+
+ssize_t logrotate(
+ log_id_t logId,
+ char prio,
+ const char *filename,
+ const char *buf, size_t len,
+ void *arg) {
+ bool* doRotate = static_cast<bool*>(arg);
+ if (!*doRotate) {
+ return __android_log_pmsg_file_write(logId, prio, filename, buf, len);
+ }
+
+ std::string name(filename);
+ size_t dot = name.find_last_of('.');
+ std::string sub = name.substr(0, dot);
+
+ if (LAST_KMSG_FILTER.find(sub) == std::string::npos &&
+ LAST_LOG_FILTER.find(sub) == std::string::npos) {
+ return __android_log_pmsg_file_write(logId, prio, filename, buf, len);
+ }
+
+ // filename rotation
+ if (dot == std::string::npos) {
+ name += ".1";
+ } else {
+ std::string number = name.substr(dot + 1);
+ if (!isdigit(number[0])) {
+ name += ".1";
+ } else {
+ size_t i;
+ if (!android::base::ParseUint(number, &i)) {
+ LOG(ERROR) << "failed to parse uint in " << number;
+ return -1;
+ }
+ name = sub + "." + std::to_string(i + 1);
+ }
+ }
+
+ return __android_log_pmsg_file_write(logId, prio, name.c_str(), buf, len);
+}
+
+// Rename last_log -> last_log.1 -> last_log.2 -> ... -> last_log.$max.
+// Similarly rename last_kmsg -> last_kmsg.1 -> ... -> last_kmsg.$max.
+// Overwrite any existing last_log.$max and last_kmsg.$max.
+void rotate_logs(const char* last_log_file, const char* last_kmsg_file) {
+ // Logs should only be rotated once.
+ static bool rotated = false;
+ if (rotated) {
+ return;
+ }
+ rotated = true;
+
+ for (int i = KEEP_LOG_COUNT - 1; i >= 0; --i) {
+ std::string old_log = android::base::StringPrintf("%s", last_log_file);
+ if (i > 0) {
+ old_log += "." + std::to_string(i);
+ }
+ std::string new_log = android::base::StringPrintf("%s.%d", last_log_file, i+1);
+ // Ignore errors if old_log doesn't exist.
+ rename(old_log.c_str(), new_log.c_str());
+
+ std::string old_kmsg = android::base::StringPrintf("%s", last_kmsg_file);
+ if (i > 0) {
+ old_kmsg += "." + std::to_string(i);
+ }
+ std::string new_kmsg = android::base::StringPrintf("%s.%d", last_kmsg_file, i+1);
+ rename(old_kmsg.c_str(), new_kmsg.c_str());
+ }
+}
diff --git a/rotate_logs.h b/rotate_logs.h
new file mode 100644
index 000000000..809c213b6
--- /dev/null
+++ b/rotate_logs.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ROTATE_LOGS_H
+#define _ROTATE_LOGS_H
+
+#include <string>
+
+#include <private/android_logger.h> /* private pmsg functions */
+
+constexpr int KEEP_LOG_COUNT = 10;
+
+ssize_t logbasename(log_id_t /* logId */,
+ char /* prio */,
+ const char *filename,
+ const char * /* buf */, size_t len,
+ void *arg);
+
+ssize_t logrotate(
+ log_id_t logId,
+ char prio,
+ const char *filename,
+ const char *buf, size_t len,
+ void *arg);
+
+// Rename last_log -> last_log.1 -> last_log.2 -> ... -> last_log.$max.
+// Similarly rename last_kmsg -> last_kmsg.1 -> ... -> last_kmsg.$max.
+// Overwrite any existing last_log.$max and last_kmsg.$max.
+void rotate_logs(const char* last_log_file, const char* last_kmsg_file);
+
+#endif //_ROTATE_LOG_H
diff --git a/tests/Android.mk b/tests/Android.mk
index 3d05386b0..e87a22964 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -18,7 +18,6 @@ LOCAL_PATH := $(call my-dir)
# Unit tests
include $(CLEAR_VARS)
-LOCAL_CLANG := true
LOCAL_CFLAGS := -Werror
LOCAL_MODULE := recovery_unit_test
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
@@ -32,18 +31,31 @@ LOCAL_STATIC_LIBRARIES := \
libselinux \
libbase
-LOCAL_SRC_FILES := unit/asn1_decoder_test.cpp
-LOCAL_SRC_FILES += unit/recovery_test.cpp
-LOCAL_SRC_FILES += unit/locale_test.cpp
-LOCAL_SRC_FILES += unit/zip_test.cpp
+LOCAL_SRC_FILES := \
+ unit/asn1_decoder_test.cpp \
+ unit/locale_test.cpp \
+ unit/sysutil_test.cpp \
+ unit/zip_test.cpp
+
LOCAL_C_INCLUDES := bootable/recovery
LOCAL_SHARED_LIBRARIES := liblog
include $(BUILD_NATIVE_TEST)
-# Component tests
+# Manual tests
include $(CLEAR_VARS)
LOCAL_CLANG := true
-LOCAL_CFLAGS += -Wno-unused-parameter -Werror
+LOCAL_CFLAGS := -Werror
+LOCAL_MODULE := recovery_manual_test
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+LOCAL_STATIC_LIBRARIES := libbase
+
+LOCAL_SRC_FILES := manual/recovery_test.cpp
+LOCAL_SHARED_LIBRARIES := liblog
+include $(BUILD_NATIVE_TEST)
+
+# Component tests
+include $(CLEAR_VARS)
+LOCAL_CFLAGS := -Werror
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
LOCAL_MODULE := recovery_component_test
LOCAL_C_INCLUDES := bootable/recovery
@@ -63,6 +75,7 @@ tune2fs_static_libraries := \
libext2fs
LOCAL_STATIC_LIBRARIES := \
+ libapplypatch_modes \
libapplypatch \
libedify \
libotafault \
@@ -86,7 +99,8 @@ LOCAL_STATIC_LIBRARIES := \
testdata_files := $(call find-subdir-files, testdata/*)
-testdata_out_path := $(TARGET_OUT_DATA_NATIVE_TESTS)/recovery
+# The testdata files that will go to $OUT/data/nativetest/recovery.
+testdata_out_path := $(TARGET_OUT_DATA)/nativetest/recovery
GEN := $(addprefix $(testdata_out_path)/, $(testdata_files))
$(GEN): PRIVATE_PATH := $(LOCAL_PATH)
$(GEN): PRIVATE_CUSTOM_TOOL = cp $< $@
@@ -94,14 +108,16 @@ $(GEN): $(testdata_out_path)/% : $(LOCAL_PATH)/%
$(transform-generated-source)
LOCAL_GENERATED_SOURCES += $(GEN)
-ifdef TARGET_2ND_ARCH
-testdata_out_path_2nd_arch := $($(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_DATA_NATIVE_TESTS)/recovery
-GEN_2ND_ARCH := $(addprefix $(testdata_out_path_2nd_arch)/, $(testdata_files))
-$(GEN_2ND_ARCH): PRIVATE_PATH := $(LOCAL_PATH)
-$(GEN_2ND_ARCH): PRIVATE_CUSTOM_TOOL = cp $< $@
-$(GEN_2ND_ARCH): $(testdata_out_path_2nd_arch)/% : $(LOCAL_PATH)/%
+# A copy of the testdata to be packed into continuous_native_tests.zip.
+testdata_continuous_zip_prefix := \
+ $(call intermediates-dir-for,PACKAGING,recovery_component_test)/DATA
+testdata_continuous_zip_path := $(testdata_continuous_zip_prefix)/nativetest/recovery
+GEN := $(addprefix $(testdata_continuous_zip_path)/, $(testdata_files))
+$(GEN): PRIVATE_PATH := $(LOCAL_PATH)
+$(GEN): PRIVATE_CUSTOM_TOOL = cp $< $@
+$(GEN): $(testdata_continuous_zip_path)/% : $(LOCAL_PATH)/%
$(transform-generated-source)
-LOCAL_GENERATED_SOURCES += $(GEN_2ND_ARCH)
-endif # TARGET_2ND_ARCH
+LOCAL_GENERATED_SOURCES += $(GEN)
+LOCAL_PICKUP_FILES := $(testdata_continuous_zip_prefix)
include $(BUILD_NATIVE_TEST)
diff --git a/tests/common/test_constants.h b/tests/common/test_constants.h
index 3490f6805..97e74a3c2 100644
--- a/tests/common/test_constants.h
+++ b/tests/common/test_constants.h
@@ -13,13 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
#ifndef _OTA_TEST_CONSTANTS_H
#define _OTA_TEST_CONSTANTS_H
-#if defined(__LP64__)
-#define NATIVE_TEST_PATH "/nativetest64"
-#else
-#define NATIVE_TEST_PATH "/nativetest"
-#endif
+#include <stdlib.h>
+
+static const char* data_root = getenv("ANDROID_DATA");
+
+static std::string from_testdata_base(const std::string& fname) {
+ return std::string(data_root) + "/nativetest/recovery/testdata/" + fname;
+}
-#endif
+#endif // _OTA_TEST_CONSTANTS_H
diff --git a/tests/component/applypatch_test.cpp b/tests/component/applypatch_test.cpp
index 908a9f5f5..1a0b19113 100644
--- a/tests/component/applypatch_test.cpp
+++ b/tests/component/applypatch_test.cpp
@@ -23,167 +23,158 @@
#include <sys/types.h>
#include <time.h>
+#include <memory>
#include <string>
+#include <vector>
#include <android-base/file.h>
#include <android-base/stringprintf.h>
#include <android-base/test_utils.h>
+#include <openssl/sha.h>
#include "applypatch/applypatch.h"
+#include "applypatch/applypatch_modes.h"
#include "common/test_constants.h"
-#include "openssl/sha.h"
#include "print_sha1.h"
-static const std::string DATA_PATH = getenv("ANDROID_DATA");
-static const std::string TESTDATA_PATH = "/recovery/testdata";
-static const std::string WORK_FS = "/data";
+static void sha1sum(const std::string& fname, std::string* sha1, size_t* fsize = nullptr) {
+ ASSERT_NE(nullptr, sha1);
-static std::string sha1sum(const std::string& fname) {
- uint8_t digest[SHA_DIGEST_LENGTH];
- std::string data;
- android::base::ReadFileToString(fname, &data);
+ std::string data;
+ ASSERT_TRUE(android::base::ReadFileToString(fname, &data));
- SHA1((const uint8_t*)data.c_str(), data.size(), digest);
- return print_sha1(digest);
-}
+ if (fsize != nullptr) {
+ *fsize = data.size();
+ }
-static void mangle_file(const std::string& fname) {
- FILE* fh = fopen(&fname[0], "w");
- int r;
- for (int i=0; i < 1024; i++) {
- r = rand();
- fwrite(&r, sizeof(short), 1, fh);
- }
- fclose(fh);
+ uint8_t digest[SHA_DIGEST_LENGTH];
+ SHA1(reinterpret_cast<const uint8_t*>(data.c_str()), data.size(), digest);
+ *sha1 = print_sha1(digest);
}
-static bool file_cmp(std::string& f1, std::string& f2) {
- std::string c1;
- std::string c2;
- android::base::ReadFileToString(f1, &c1);
- android::base::ReadFileToString(f2, &c2);
- return c1 == c2;
+static void mangle_file(const std::string& fname) {
+ std::string content;
+ content.reserve(1024);
+ for (size_t i = 0; i < 1024; i++) {
+ content[i] = rand() % 256;
+ }
+ ASSERT_TRUE(android::base::WriteStringToFile(content, fname));
}
-static std::string from_testdata_base(const std::string& fname) {
- return android::base::StringPrintf("%s%s%s/%s",
- &DATA_PATH[0],
- &NATIVE_TEST_PATH[0],
- &TESTDATA_PATH[0],
- &fname[0]);
+static bool file_cmp(const std::string& f1, const std::string& f2) {
+ std::string c1;
+ android::base::ReadFileToString(f1, &c1);
+ std::string c2;
+ android::base::ReadFileToString(f2, &c2);
+ return c1 == c2;
}
class ApplyPatchTest : public ::testing::Test {
- public:
- static void SetUpTestCase() {
- // set up files
- old_file = from_testdata_base("old.file");
- new_file = from_testdata_base("new.file");
- patch_file = from_testdata_base("patch.bsdiff");
- rand_file = "/cache/applypatch_test_rand.file";
- cache_file = "/cache/saved.file";
-
- // write stuff to rand_file
- android::base::WriteStringToFile("hello", rand_file);
-
- // set up SHA constants
- old_sha1 = sha1sum(old_file);
- new_sha1 = sha1sum(new_file);
- srand(time(NULL));
- bad_sha1_a = android::base::StringPrintf("%040x", rand());
- bad_sha1_b = android::base::StringPrintf("%040x", rand());
-
- struct stat st;
- stat(&new_file[0], &st);
- new_size = st.st_size;
- }
-
- static std::string old_file;
- static std::string new_file;
- static std::string rand_file;
- static std::string cache_file;
- static std::string patch_file;
-
- static std::string old_sha1;
- static std::string new_sha1;
- static std::string bad_sha1_a;
- static std::string bad_sha1_b;
-
- static size_t new_size;
+ public:
+ static void SetUpTestCase() {
+ // set up files
+ old_file = from_testdata_base("old.file");
+ new_file = from_testdata_base("new.file");
+ patch_file = from_testdata_base("patch.bsdiff");
+ rand_file = "/cache/applypatch_test_rand.file";
+ cache_file = "/cache/saved.file";
+
+ // write stuff to rand_file
+ ASSERT_TRUE(android::base::WriteStringToFile("hello", rand_file));
+
+ // set up SHA constants
+ sha1sum(old_file, &old_sha1);
+ sha1sum(new_file, &new_sha1);
+ srand(time(nullptr));
+ bad_sha1_a = android::base::StringPrintf("%040x", rand());
+ bad_sha1_b = android::base::StringPrintf("%040x", rand());
+
+ struct stat st;
+ stat(&new_file[0], &st);
+ new_size = st.st_size;
+ }
+
+ static std::string old_file;
+ static std::string new_file;
+ static std::string rand_file;
+ static std::string cache_file;
+ static std::string patch_file;
+
+ static std::string old_sha1;
+ static std::string new_sha1;
+ static std::string bad_sha1_a;
+ static std::string bad_sha1_b;
+
+ static size_t new_size;
};
std::string ApplyPatchTest::old_file;
std::string ApplyPatchTest::new_file;
-static void cp(std::string src, std::string tgt) {
- std::string cmd = android::base::StringPrintf("cp %s %s",
- &src[0],
- &tgt[0]);
- system(&cmd[0]);
+static void cp(const std::string& src, const std::string& tgt) {
+ std::string cmd = "cp " + src + " " + tgt;
+ system(cmd.c_str());
}
static void backup_old() {
- cp(ApplyPatchTest::old_file, ApplyPatchTest::cache_file);
+ cp(ApplyPatchTest::old_file, ApplyPatchTest::cache_file);
}
static void restore_old() {
- cp(ApplyPatchTest::cache_file, ApplyPatchTest::old_file);
+ cp(ApplyPatchTest::cache_file, ApplyPatchTest::old_file);
}
class ApplyPatchCacheTest : public ApplyPatchTest {
- public:
- virtual void SetUp() {
- backup_old();
- }
-
- virtual void TearDown() {
- restore_old();
- }
+ public:
+ virtual void SetUp() {
+ backup_old();
+ }
+
+ virtual void TearDown() {
+ restore_old();
+ }
};
class ApplyPatchFullTest : public ApplyPatchCacheTest {
- public:
- static void SetUpTestCase() {
- ApplyPatchTest::SetUpTestCase();
- unsigned long free_kb = FreeSpaceForFile(&WORK_FS[0]);
- ASSERT_GE(free_kb * 1024, new_size * 3 / 2);
- output_f = new TemporaryFile();
- output_loc = std::string(output_f->path);
-
- struct FileContents fc;
-
- ASSERT_EQ(0, LoadFileContents(&rand_file[0], &fc));
- Value* patch1 = new Value(VAL_BLOB, std::string(fc.data.begin(), fc.data.end()));
- patches.push_back(patch1);
-
- ASSERT_EQ(0, LoadFileContents(&patch_file[0], &fc));
- Value* patch2 = new Value(VAL_BLOB, std::string(fc.data.begin(), fc.data.end()));
- patches.push_back(patch2);
- }
- static void TearDownTestCase() {
- delete output_f;
- for (auto it = patches.begin(); it != patches.end(); ++it) {
- delete *it;
- }
- patches.clear();
- }
-
- static std::vector<Value*> patches;
- static TemporaryFile* output_f;
- static std::string output_loc;
+ public:
+ static void SetUpTestCase() {
+ ApplyPatchTest::SetUpTestCase();
+
+ output_f = new TemporaryFile();
+ output_loc = std::string(output_f->path);
+
+ struct FileContents fc;
+
+ ASSERT_EQ(0, LoadFileContents(&rand_file[0], &fc));
+ patches.push_back(
+ std::make_unique<Value>(VAL_BLOB, std::string(fc.data.begin(), fc.data.end())));
+
+ ASSERT_EQ(0, LoadFileContents(&patch_file[0], &fc));
+ patches.push_back(
+ std::make_unique<Value>(VAL_BLOB, std::string(fc.data.begin(), fc.data.end())));
+ }
+
+ static void TearDownTestCase() {
+ delete output_f;
+ patches.clear();
+ }
+
+ static std::vector<std::unique_ptr<Value>> patches;
+ static TemporaryFile* output_f;
+ static std::string output_loc;
};
class ApplyPatchDoubleCacheTest : public ApplyPatchFullTest {
- public:
- virtual void SetUp() {
- ApplyPatchCacheTest::SetUp();
- cp(cache_file, "/cache/reallysaved.file");
- }
-
- virtual void TearDown() {
- cp("/cache/reallysaved.file", cache_file);
- ApplyPatchCacheTest::TearDown();
- }
+ public:
+ virtual void SetUp() {
+ ApplyPatchCacheTest::SetUp();
+ cp(cache_file, "/cache/reallysaved.file");
+ }
+
+ virtual void TearDown() {
+ cp("/cache/reallysaved.file", cache_file);
+ ApplyPatchCacheTest::TearDown();
+ }
};
std::string ApplyPatchTest::rand_file;
@@ -196,188 +187,268 @@ std::string ApplyPatchTest::bad_sha1_b;
size_t ApplyPatchTest::new_size;
-std::vector<Value*> ApplyPatchFullTest::patches;
+std::vector<std::unique_ptr<Value>> ApplyPatchFullTest::patches;
TemporaryFile* ApplyPatchFullTest::output_f;
std::string ApplyPatchFullTest::output_loc;
TEST_F(ApplyPatchTest, CheckModeSkip) {
- std::vector<std::string> sha1s;
- ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s));
+ std::vector<std::string> sha1s;
+ ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s));
}
TEST_F(ApplyPatchTest, CheckModeSingle) {
- std::vector<std::string> sha1s = { old_sha1 };
- ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s));
+ std::vector<std::string> sha1s = { old_sha1 };
+ ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s));
}
TEST_F(ApplyPatchTest, CheckModeMultiple) {
- std::vector<std::string> sha1s = {
- bad_sha1_a,
- old_sha1,
- bad_sha1_b
- };
- ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s));
+ std::vector<std::string> sha1s = { bad_sha1_a, old_sha1, bad_sha1_b };
+ ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s));
}
TEST_F(ApplyPatchTest, CheckModeFailure) {
- std::vector<std::string> sha1s = {
- bad_sha1_a,
- bad_sha1_b
- };
- ASSERT_NE(0, applypatch_check(&old_file[0], sha1s));
+ std::vector<std::string> sha1s = { bad_sha1_a, bad_sha1_b };
+ ASSERT_NE(0, applypatch_check(&old_file[0], sha1s));
}
TEST_F(ApplyPatchCacheTest, CheckCacheCorruptedSingle) {
- mangle_file(old_file);
- std::vector<std::string> sha1s = { old_sha1 };
- ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s));
+ mangle_file(old_file);
+ std::vector<std::string> sha1s = { old_sha1 };
+ ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s));
}
TEST_F(ApplyPatchCacheTest, CheckCacheCorruptedMultiple) {
- mangle_file(old_file);
- std::vector<std::string> sha1s = {
- bad_sha1_a,
- old_sha1,
- bad_sha1_b
- };
- ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s));
+ mangle_file(old_file);
+ std::vector<std::string> sha1s = { bad_sha1_a, old_sha1, bad_sha1_b };
+ ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s));
}
TEST_F(ApplyPatchCacheTest, CheckCacheCorruptedFailure) {
- mangle_file(old_file);
- std::vector<std::string> sha1s = {
- bad_sha1_a,
- bad_sha1_b
- };
- ASSERT_NE(0, applypatch_check(&old_file[0], sha1s));
+ mangle_file(old_file);
+ std::vector<std::string> sha1s = { bad_sha1_a, bad_sha1_b };
+ ASSERT_NE(0, applypatch_check(&old_file[0], sha1s));
}
TEST_F(ApplyPatchCacheTest, CheckCacheMissingSingle) {
- unlink(&old_file[0]);
- std::vector<std::string> sha1s = { old_sha1 };
- ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s));
+ unlink(&old_file[0]);
+ std::vector<std::string> sha1s = { old_sha1 };
+ ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s));
}
TEST_F(ApplyPatchCacheTest, CheckCacheMissingMultiple) {
- unlink(&old_file[0]);
- std::vector<std::string> sha1s = {
- bad_sha1_a,
- old_sha1,
- bad_sha1_b
- };
- ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s));
+ unlink(&old_file[0]);
+ std::vector<std::string> sha1s = { bad_sha1_a, old_sha1, bad_sha1_b };
+ ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s));
}
TEST_F(ApplyPatchCacheTest, CheckCacheMissingFailure) {
- unlink(&old_file[0]);
- std::vector<std::string> sha1s = {
- bad_sha1_a,
- bad_sha1_b
- };
- ASSERT_NE(0, applypatch_check(&old_file[0], sha1s));
+ unlink(&old_file[0]);
+ std::vector<std::string> sha1s = { bad_sha1_a, bad_sha1_b };
+ ASSERT_NE(0, applypatch_check(&old_file[0], sha1s));
}
TEST_F(ApplyPatchFullTest, ApplyInPlace) {
- std::vector<std::string> sha1s = {
- bad_sha1_a,
- old_sha1
- };
- int ap_result = applypatch(&old_file[0],
- "-",
- &new_sha1[0],
- new_size,
- sha1s,
- patches.data(),
- nullptr);
- ASSERT_EQ(0, ap_result);
- ASSERT_TRUE(file_cmp(old_file, new_file));
- // reapply, applypatch is idempotent so it should succeed
- ap_result = applypatch(&old_file[0],
- "-",
- &new_sha1[0],
- new_size,
- sha1s,
- patches.data(),
- nullptr);
- ASSERT_EQ(0, ap_result);
- ASSERT_TRUE(file_cmp(old_file, new_file));
+ std::vector<std::string> sha1s = { bad_sha1_a, old_sha1 };
+ ASSERT_EQ(0, applypatch(&old_file[0], "-", &new_sha1[0], new_size, sha1s, patches, nullptr));
+ ASSERT_TRUE(file_cmp(old_file, new_file));
+
+ // reapply, applypatch is idempotent so it should succeed
+ ASSERT_EQ(0, applypatch(&old_file[0], "-", &new_sha1[0], new_size, sha1s, patches, nullptr));
+ ASSERT_TRUE(file_cmp(old_file, new_file));
}
TEST_F(ApplyPatchFullTest, ApplyInNewLocation) {
- std::vector<std::string> sha1s = {
- bad_sha1_a,
- old_sha1
- };
- int ap_result = applypatch(&old_file[0],
- &output_loc[0],
- &new_sha1[0],
- new_size,
- sha1s,
- patches.data(),
- nullptr);
- ASSERT_EQ(0, ap_result);
- ASSERT_TRUE(file_cmp(output_loc, new_file));
- ap_result = applypatch(&old_file[0],
- &output_loc[0],
- &new_sha1[0],
- new_size,
- sha1s,
- patches.data(),
- nullptr);
- ASSERT_EQ(0, ap_result);
- ASSERT_TRUE(file_cmp(output_loc, new_file));
+ std::vector<std::string> sha1s = { bad_sha1_a, old_sha1 };
+ // Apply bsdiff patch to new location.
+ ASSERT_EQ(
+ 0, applypatch(&old_file[0], &output_loc[0], &new_sha1[0], new_size, sha1s, patches, nullptr));
+ ASSERT_TRUE(file_cmp(output_loc, new_file));
+
+ // Reapply to the same location.
+ ASSERT_EQ(
+ 0, applypatch(&old_file[0], &output_loc[0], &new_sha1[0], new_size, sha1s, patches, nullptr));
+ ASSERT_TRUE(file_cmp(output_loc, new_file));
}
TEST_F(ApplyPatchFullTest, ApplyCorruptedInNewLocation) {
- mangle_file(old_file);
- std::vector<std::string> sha1s = {
- bad_sha1_a,
- old_sha1
- };
- int ap_result = applypatch(&old_file[0],
- &output_loc[0],
- &new_sha1[0],
- new_size,
- sha1s,
- patches.data(),
- nullptr);
- ASSERT_EQ(0, ap_result);
- ASSERT_TRUE(file_cmp(output_loc, new_file));
- ap_result = applypatch(&old_file[0],
- &output_loc[0],
- &new_sha1[0],
- new_size,
- sha1s,
- patches.data(),
- nullptr);
- ASSERT_EQ(0, ap_result);
- ASSERT_TRUE(file_cmp(output_loc, new_file));
+ std::vector<std::string> sha1s = { bad_sha1_a, old_sha1 };
+ // Apply bsdiff patch to new location with corrupted source.
+ mangle_file(old_file);
+ ASSERT_EQ(
+ 0, applypatch(&old_file[0], &output_loc[0], &new_sha1[0], new_size, sha1s, patches, nullptr));
+ ASSERT_TRUE(file_cmp(output_loc, new_file));
+
+ // Reapply bsdiff patch to new location with corrupted source.
+ ASSERT_EQ(
+ 0, applypatch(&old_file[0], &output_loc[0], &new_sha1[0], new_size, sha1s, patches, nullptr));
+ ASSERT_TRUE(file_cmp(output_loc, new_file));
}
TEST_F(ApplyPatchDoubleCacheTest, ApplyDoubleCorruptedInNewLocation) {
- mangle_file(old_file);
- mangle_file(cache_file);
-
- std::vector<std::string> sha1s = {
- bad_sha1_a,
- old_sha1
- };
- int ap_result = applypatch(&old_file[0],
- &output_loc[0],
- &new_sha1[0],
- new_size,
- sha1s,
- patches.data(),
- nullptr);
- ASSERT_NE(0, ap_result);
- ASSERT_FALSE(file_cmp(output_loc, new_file));
- ap_result = applypatch(&old_file[0],
- &output_loc[0],
- &new_sha1[0],
- new_size,
- sha1s,
- patches.data(),
- nullptr);
- ASSERT_NE(0, ap_result);
- ASSERT_FALSE(file_cmp(output_loc, new_file));
+ std::vector<std::string> sha1s = { bad_sha1_a, old_sha1 };
+
+ // Apply bsdiff patch to new location with corrupted source and copy (no new file).
+ // Expected to fail.
+ mangle_file(old_file);
+ mangle_file(cache_file);
+ ASSERT_NE(
+ 0, applypatch(&old_file[0], &output_loc[0], &new_sha1[0], new_size, sha1s, patches, nullptr));
+ ASSERT_FALSE(file_cmp(output_loc, new_file));
+
+ // Expected to fail again on retry.
+ ASSERT_NE(
+ 0, applypatch(&old_file[0], &output_loc[0], &new_sha1[0], new_size, sha1s, patches, nullptr));
+ ASSERT_FALSE(file_cmp(output_loc, new_file));
+
+ // Expected to fail with incorrect new file.
+ mangle_file(output_loc);
+ ASSERT_NE(
+ 0, applypatch(&old_file[0], &output_loc[0], &new_sha1[0], new_size, sha1s, patches, nullptr));
+ ASSERT_FALSE(file_cmp(output_loc, new_file));
+}
+
+TEST(ApplyPatchModes, InvalidArgs) {
+ // At least two args (including the filename).
+ ASSERT_EQ(2, applypatch_modes(1, (const char* []){ "applypatch" }));
+
+ // Unrecognized args.
+ ASSERT_EQ(2, applypatch_modes(2, (const char* []){ "applypatch", "-x" }));
+}
+
+TEST(ApplyPatchModes, PatchMode) {
+ std::string boot_img = from_testdata_base("boot.img");
+ size_t boot_img_size;
+ std::string boot_img_sha1;
+ sha1sum(boot_img, &boot_img_sha1, &boot_img_size);
+
+ std::string recovery_img = from_testdata_base("recovery.img");
+ size_t recovery_img_size;
+ std::string recovery_img_sha1;
+ sha1sum(recovery_img, &recovery_img_sha1, &recovery_img_size);
+
+ std::string bonus_file = from_testdata_base("bonus.file");
+
+ // applypatch -b <bonus-file> <src-file> <tgt-file> <tgt-sha1> <tgt-size> <src-sha1>:<patch>
+ TemporaryFile tmp1;
+ std::vector<const char*> args = {
+ "applypatch",
+ "-b",
+ bonus_file.c_str(),
+ boot_img.c_str(),
+ tmp1.path,
+ recovery_img_sha1.c_str(),
+ std::to_string(recovery_img_size).c_str(),
+ (boot_img_sha1 + ":" + from_testdata_base("recovery-from-boot.p")).c_str()
+ };
+ ASSERT_EQ(0, applypatch_modes(args.size(), args.data()));
+
+ // applypatch <src-file> <tgt-file> <tgt-sha1> <tgt-size> <src-sha1>:<patch>
+ TemporaryFile tmp2;
+ std::vector<const char*> args2 = {
+ "applypatch",
+ boot_img.c_str(),
+ tmp2.path,
+ recovery_img_sha1.c_str(),
+ std::to_string(recovery_img_size).c_str(),
+ (boot_img_sha1 + ":" + from_testdata_base("recovery-from-boot-with-bonus.p")).c_str()
+ };
+ ASSERT_EQ(0, applypatch_modes(args2.size(), args2.data()));
+
+ // applypatch -b <bonus-file> <src-file> <tgt-file> <tgt-sha1> <tgt-size> \
+ // <src-sha1-fake>:<patch1> <src-sha1>:<patch2>
+ TemporaryFile tmp3;
+ std::string bad_sha1_a = android::base::StringPrintf("%040x", rand());
+ std::string bad_sha1_b = android::base::StringPrintf("%040x", rand());
+ std::vector<const char*> args3 = {
+ "applypatch",
+ "-b",
+ bonus_file.c_str(),
+ boot_img.c_str(),
+ tmp3.path,
+ recovery_img_sha1.c_str(),
+ std::to_string(recovery_img_size).c_str(),
+ (bad_sha1_a + ":" + from_testdata_base("recovery-from-boot.p")).c_str(),
+ (boot_img_sha1 + ":" + from_testdata_base("recovery-from-boot.p")).c_str(),
+ (bad_sha1_b + ":" + from_testdata_base("recovery-from-boot.p")).c_str(),
+ };
+ ASSERT_EQ(0, applypatch_modes(args3.size(), args3.data()));
+}
+
+TEST(ApplyPatchModes, PatchModeInvalidArgs) {
+ // Invalid bonus file.
+ ASSERT_NE(0, applypatch_modes(3, (const char* []){ "applypatch", "-b", "/doesntexist" }));
+
+ std::string bonus_file = from_testdata_base("bonus.file");
+ // With bonus file, but missing args.
+ ASSERT_EQ(2, applypatch_modes(3, (const char* []){ "applypatch", "-b", bonus_file.c_str() }));
+
+ std::string boot_img = from_testdata_base("boot.img");
+ size_t boot_img_size;
+ std::string boot_img_sha1;
+ sha1sum(boot_img, &boot_img_sha1, &boot_img_size);
+
+ std::string recovery_img = from_testdata_base("recovery.img");
+ size_t recovery_img_size;
+ std::string recovery_img_sha1;
+ sha1sum(recovery_img, &recovery_img_sha1, &recovery_img_size);
+
+ // Bonus file is not supported in flash mode.
+ // applypatch -b <bonus-file> <src-file> <tgt-file> <tgt-sha1> <tgt-size>
+ TemporaryFile tmp4;
+ std::vector<const char*> args4 = {
+ "applypatch",
+ "-b",
+ bonus_file.c_str(),
+ boot_img.c_str(),
+ tmp4.path,
+ recovery_img_sha1.c_str(),
+ std::to_string(recovery_img_size).c_str() };
+ ASSERT_NE(0, applypatch_modes(args4.size(), args4.data()));
+
+ // Failed to parse patch args.
+ TemporaryFile tmp5;
+ std::vector<const char*> args5 = {
+ "applypatch",
+ boot_img.c_str(),
+ tmp5.path,
+ recovery_img_sha1.c_str(),
+ std::to_string(recovery_img_size).c_str(),
+ ("invalid-sha1:filename" + from_testdata_base("recovery-from-boot-with-bonus.p")).c_str(),
+ };
+ ASSERT_NE(0, applypatch_modes(args5.size(), args5.data()));
+
+ // Target size cannot be zero.
+ TemporaryFile tmp6;
+ std::vector<const char*> args6 = {
+ "applypatch",
+ boot_img.c_str(),
+ tmp6.path,
+ recovery_img_sha1.c_str(),
+ "0", // target size
+ (boot_img_sha1 + ":" + from_testdata_base("recovery-from-boot-with-bonus.p")).c_str()
+ };
+ ASSERT_NE(0, applypatch_modes(args6.size(), args6.data()));
+}
+
+TEST(ApplyPatchModes, CheckModeInvalidArgs) {
+ // Insufficient args.
+ ASSERT_EQ(2, applypatch_modes(2, (const char* []){ "applypatch", "-c" }));
+}
+
+TEST(ApplyPatchModes, SpaceModeInvalidArgs) {
+ // Insufficient args.
+ ASSERT_EQ(2, applypatch_modes(2, (const char* []){ "applypatch", "-s" }));
+
+ // Invalid bytes arg.
+ ASSERT_EQ(1, applypatch_modes(3, (const char* []){ "applypatch", "-s", "x" }));
+
+ // 0 is invalid.
+ ASSERT_EQ(1, applypatch_modes(3, (const char* []){ "applypatch", "-s", "0" }));
+
+ // 0x10 is fine.
+ ASSERT_EQ(0, applypatch_modes(3, (const char* []){ "applypatch", "-s", "0x10" }));
+}
+
+TEST(ApplyPatchModes, ShowLicenses) {
+ ASSERT_EQ(0, applypatch_modes(2, (const char* []){ "applypatch", "-l" }));
}
diff --git a/tests/component/updater_test.cpp b/tests/component/updater_test.cpp
index a859f11c1..acc2b4040 100644
--- a/tests/component/updater_test.cpp
+++ b/tests/component/updater_test.cpp
@@ -16,7 +16,9 @@
#include <string>
+#include <android-base/file.h>
#include <android-base/properties.h>
+#include <android-base/test_utils.h>
#include <gtest/gtest.h>
#include "edify/expr.h"
@@ -97,3 +99,135 @@ TEST_F(UpdaterTest, sha1_check) {
// sha1_check() expects at least one argument.
expect(nullptr, "sha1_check()", kArgsParsingFailure);
}
+
+TEST_F(UpdaterTest, file_getprop) {
+ // file_getprop() expects two arguments.
+ expect(nullptr, "file_getprop()", kArgsParsingFailure);
+ expect(nullptr, "file_getprop(\"arg1\")", kArgsParsingFailure);
+ expect(nullptr, "file_getprop(\"arg1\", \"arg2\", \"arg3\")", kArgsParsingFailure);
+
+ // File doesn't exist.
+ expect(nullptr, "file_getprop(\"/doesntexist\", \"key1\")", kFileGetPropFailure);
+
+ // Reject too large files (current limit = 65536).
+ TemporaryFile temp_file1;
+ std::string buffer(65540, '\0');
+ ASSERT_TRUE(android::base::WriteStringToFile(buffer, temp_file1.path));
+
+ // Read some keys.
+ TemporaryFile temp_file2;
+ std::string content("ro.product.name=tardis\n"
+ "# comment\n\n\n"
+ "ro.product.model\n"
+ "ro.product.board = magic \n");
+ ASSERT_TRUE(android::base::WriteStringToFile(content, temp_file2.path));
+
+ std::string script1("file_getprop(\"" + std::string(temp_file2.path) +
+ "\", \"ro.product.name\")");
+ expect("tardis", script1.c_str(), kNoCause);
+
+ std::string script2("file_getprop(\"" + std::string(temp_file2.path) +
+ "\", \"ro.product.board\")");
+ expect("magic", script2.c_str(), kNoCause);
+
+ // No match.
+ std::string script3("file_getprop(\"" + std::string(temp_file2.path) +
+ "\", \"ro.product.wrong\")");
+ expect("", script3.c_str(), kNoCause);
+
+ std::string script4("file_getprop(\"" + std::string(temp_file2.path) +
+ "\", \"ro.product.name=\")");
+ expect("", script4.c_str(), kNoCause);
+
+ std::string script5("file_getprop(\"" + std::string(temp_file2.path) +
+ "\", \"ro.product.nam\")");
+ expect("", script5.c_str(), kNoCause);
+
+ std::string script6("file_getprop(\"" + std::string(temp_file2.path) +
+ "\", \"ro.product.model\")");
+ expect("", script6.c_str(), kNoCause);
+}
+
+TEST_F(UpdaterTest, delete) {
+ // Delete none.
+ expect("0", "delete()", kNoCause);
+ expect("0", "delete(\"/doesntexist\")", kNoCause);
+ expect("0", "delete(\"/doesntexist1\", \"/doesntexist2\")", kNoCause);
+ expect("0", "delete(\"/doesntexist1\", \"/doesntexist2\", \"/doesntexist3\")", kNoCause);
+
+ // Delete one file.
+ TemporaryFile temp_file1;
+ ASSERT_TRUE(android::base::WriteStringToFile("abc", temp_file1.path));
+ std::string script1("delete(\"" + std::string(temp_file1.path) + "\")");
+ expect("1", script1.c_str(), kNoCause);
+
+ // Delete two files.
+ TemporaryFile temp_file2;
+ ASSERT_TRUE(android::base::WriteStringToFile("abc", temp_file2.path));
+ TemporaryFile temp_file3;
+ ASSERT_TRUE(android::base::WriteStringToFile("abc", temp_file3.path));
+ std::string script2("delete(\"" + std::string(temp_file2.path) + "\", \"" +
+ std::string(temp_file3.path) + "\")");
+ expect("2", script2.c_str(), kNoCause);
+
+ // Delete already deleted files.
+ expect("0", script2.c_str(), kNoCause);
+
+ // Delete one out of three.
+ TemporaryFile temp_file4;
+ ASSERT_TRUE(android::base::WriteStringToFile("abc", temp_file4.path));
+ std::string script3("delete(\"/doesntexist1\", \"" + std::string(temp_file4.path) +
+ "\", \"/doesntexist2\")");
+ expect("1", script3.c_str(), kNoCause);
+}
+
+TEST_F(UpdaterTest, rename) {
+ // rename() expects two arguments.
+ expect(nullptr, "rename()", kArgsParsingFailure);
+ expect(nullptr, "rename(\"arg1\")", kArgsParsingFailure);
+ expect(nullptr, "rename(\"arg1\", \"arg2\", \"arg3\")", kArgsParsingFailure);
+
+ // src_name or dst_name cannot be empty.
+ expect(nullptr, "rename(\"\", \"arg2\")", kArgsParsingFailure);
+ expect(nullptr, "rename(\"arg1\", \"\")", kArgsParsingFailure);
+
+ // File doesn't exist (both of src and dst).
+ expect(nullptr, "rename(\"/doesntexist\", \"/doesntexisteither\")" , kFileRenameFailure);
+
+ // Can't create parent directory.
+ TemporaryFile temp_file1;
+ ASSERT_TRUE(android::base::WriteStringToFile("abc", temp_file1.path));
+ std::string script1("rename(\"" + std::string(temp_file1.path) + "\", \"/proc/0/file1\")");
+ expect(nullptr, script1.c_str(), kFileRenameFailure);
+
+ // Rename.
+ TemporaryFile temp_file2;
+ std::string script2("rename(\"" + std::string(temp_file1.path) + "\", \"" +
+ std::string(temp_file2.path) + "\")");
+ expect(temp_file2.path, script2.c_str(), kNoCause);
+
+ // Already renamed.
+ expect(temp_file2.path, script2.c_str(), kNoCause);
+
+ // Parents create successfully.
+ TemporaryFile temp_file3;
+ TemporaryDir td;
+ std::string temp_dir = std::string(td.path) + "/aaa/bbb/a.txt";
+ std::string script3("rename(\"" + std::string(temp_file3.path) + "\", \"" +
+ temp_dir + "\")");
+ expect(temp_dir.c_str(), script3.c_str(), kNoCause);
+}
+
+TEST_F(UpdaterTest, symlink) {
+ // symlink expects 1+ argument.
+ expect(nullptr, "symlink()", kArgsParsingFailure);
+
+ // symlink should fail if src is an empty string.
+ TemporaryFile temp_file1;
+ std::string script1("symlink(\"" + std::string(temp_file1.path) + "\", \"\")");
+ expect(nullptr, script1.c_str(), kSymlinkFailure);
+
+ // symlink failed to remove old src.
+ std::string script2("symlink(\"" + std::string(temp_file1.path) + "\", \"/proc\")");
+ expect(nullptr, script2.c_str(), kSymlinkFailure);
+}
diff --git a/tests/component/verifier_test.cpp b/tests/component/verifier_test.cpp
index 7f9a71408..60a78f5c3 100644
--- a/tests/component/verifier_test.cpp
+++ b/tests/component/verifier_test.cpp
@@ -37,9 +37,6 @@
#include "ui.h"
#include "verifier.h"
-static const char* DATA_PATH = getenv("ANDROID_DATA");
-static const char* TESTDATA_PATH = "/recovery/testdata/";
-
RecoveryUI* ui = NULL;
class MockUI : public RecoveryUI {
@@ -92,17 +89,13 @@ class VerifierTest : public testing::TestWithParam<std::vector<std::string>> {
virtual void SetUp() {
std::vector<std::string> args = GetParam();
- std::string package =
- android::base::StringPrintf("%s%s%s%s", DATA_PATH, NATIVE_TEST_PATH,
- TESTDATA_PATH, args[0].c_str());
+ std::string package = from_testdata_base(args[0]);
if (sysMapFile(package.c_str(), &memmap) != 0) {
FAIL() << "Failed to mmap " << package << ": " << strerror(errno) << "\n";
}
for (auto it = ++(args.cbegin()); it != args.cend(); ++it) {
- std::string public_key_file = android::base::StringPrintf(
- "%s%s%stestkey_%s.txt", DATA_PATH, NATIVE_TEST_PATH,
- TESTDATA_PATH, it->c_str());
+ std::string public_key_file = from_testdata_base("testkey_" + *it + ".txt");
ASSERT_TRUE(load_keys(public_key_file.c_str(), certs));
}
}
diff --git a/tests/manual/recovery_test.cpp b/tests/manual/recovery_test.cpp
new file mode 100644
index 000000000..e83849546
--- /dev/null
+++ b/tests/manual/recovery_test.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android/log.h>
+#include <gtest/gtest.h>
+#include <private/android_logger.h>
+
+static const std::string myFilename = "/data/misc/recovery/inject.txt";
+static const std::string myContent = "Hello World\nWelcome to my recovery\n";
+
+// Failure is expected on systems that do not deliver either the
+// recovery-persist or recovery-refresh executables. Tests also require
+// a reboot sequence of test to truly verify.
+
+static ssize_t __pmsg_fn(log_id_t logId, char prio, const char *filename,
+ const char *buf, size_t len, void *arg) {
+ EXPECT_EQ(LOG_ID_SYSTEM, logId);
+ EXPECT_EQ(ANDROID_LOG_INFO, prio);
+ EXPECT_NE(std::string::npos, myFilename.find(filename));
+ EXPECT_EQ(myContent, buf);
+ EXPECT_EQ(myContent.size(), len);
+ EXPECT_EQ(nullptr, arg);
+ return len;
+}
+
+// recovery.refresh - May fail. Requires recovery.inject, two reboots,
+// then expect success after second reboot.
+TEST(recovery, refresh) {
+ EXPECT_EQ(0, access("/system/bin/recovery-refresh", F_OK));
+
+ ssize_t ret = __android_log_pmsg_file_read(
+ LOG_ID_SYSTEM, ANDROID_LOG_INFO, "recovery/", __pmsg_fn, nullptr);
+ if (ret == -ENOENT) {
+ EXPECT_LT(0, __android_log_pmsg_file_write(LOG_ID_SYSTEM, ANDROID_LOG_INFO,
+ myFilename.c_str(), myContent.c_str(), myContent.size()));
+
+ fprintf(stderr, "injected test data, requires two intervening reboots "
+ "to check for replication\n");
+ }
+ EXPECT_EQ(static_cast<ssize_t>(myContent.size()), ret);
+}
+
+// recovery.persist - Requires recovery.inject, then a reboot, then
+// expect success after for this test on that boot.
+TEST(recovery, persist) {
+ EXPECT_EQ(0, access("/system/bin/recovery-persist", F_OK));
+
+ ssize_t ret = __android_log_pmsg_file_read(
+ LOG_ID_SYSTEM, ANDROID_LOG_INFO, "recovery/", __pmsg_fn, nullptr);
+ if (ret == -ENOENT) {
+ EXPECT_LT(0, __android_log_pmsg_file_write(LOG_ID_SYSTEM, ANDROID_LOG_INFO,
+ myFilename.c_str(), myContent.c_str(), myContent.size()));
+
+ fprintf(stderr, "injected test data, requires intervening reboot "
+ "to check for storage\n");
+ }
+
+ std::string buf;
+ EXPECT_TRUE(android::base::ReadFileToString(myFilename, &buf));
+ EXPECT_EQ(myContent, buf);
+ if (access(myFilename.c_str(), O_RDONLY) == 0) {
+ fprintf(stderr, "Removing persistent test data, "
+ "check if reconstructed on reboot\n");
+ }
+ EXPECT_EQ(0, unlink(myFilename.c_str()));
+}
diff --git a/tests/testdata/bonus.file b/tests/testdata/bonus.file
new file mode 100644
index 000000000..918ef8ac5
--- /dev/null
+++ b/tests/testdata/bonus.file
Binary files differ
diff --git a/tests/testdata/boot.img b/tests/testdata/boot.img
new file mode 100644
index 000000000..dd4897510
--- /dev/null
+++ b/tests/testdata/boot.img
Binary files differ
diff --git a/tests/testdata/recovery-from-boot-with-bonus.p b/tests/testdata/recovery-from-boot-with-bonus.p
new file mode 100644
index 000000000..08b6f55e4
--- /dev/null
+++ b/tests/testdata/recovery-from-boot-with-bonus.p
Binary files differ
diff --git a/tests/testdata/recovery-from-boot.p b/tests/testdata/recovery-from-boot.p
new file mode 100644
index 000000000..06f6c299f
--- /dev/null
+++ b/tests/testdata/recovery-from-boot.p
Binary files differ
diff --git a/tests/testdata/recovery.img b/tests/testdata/recovery.img
new file mode 100644
index 000000000..b862e6f0c
--- /dev/null
+++ b/tests/testdata/recovery.img
Binary files differ
diff --git a/tests/unit/recovery_test.cpp b/tests/unit/recovery_test.cpp
deleted file mode 100644
index 28b845fb9..000000000
--- a/tests/unit/recovery_test.cpp
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <fcntl.h>
-#include <string.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <android/log.h>
-#include <gtest/gtest.h>
-#include <private/android_logger.h>
-
-static const char myFilename[] = "/data/misc/recovery/inject.txt";
-static const char myContent[] = "Hello World\nWelcome to my recovery\n";
-
-// Failure is expected on systems that do not deliver either the
-// recovery-persist or recovery-refresh executables. Tests also require
-// a reboot sequence of test to truly verify.
-
-static ssize_t __pmsg_fn(log_id_t logId, char prio, const char *filename,
- const char *buf, size_t len, void *arg) {
- EXPECT_EQ(LOG_ID_SYSTEM, logId);
- EXPECT_EQ(ANDROID_LOG_INFO, prio);
- EXPECT_EQ(0, NULL == strstr(myFilename,filename));
- EXPECT_EQ(0, strcmp(myContent, buf));
- EXPECT_EQ(sizeof(myContent), len);
- EXPECT_EQ(0, NULL != arg);
- return len;
-}
-
-// recovery.refresh - May fail. Requires recovery.inject, two reboots,
-// then expect success after second reboot.
-TEST(recovery, refresh) {
- EXPECT_EQ(0, access("/system/bin/recovery-refresh", F_OK));
-
- ssize_t ret = __android_log_pmsg_file_read(
- LOG_ID_SYSTEM, ANDROID_LOG_INFO, "recovery/", __pmsg_fn, NULL);
- if (ret == -ENOENT) {
- EXPECT_LT(0, __android_log_pmsg_file_write(
- LOG_ID_SYSTEM, ANDROID_LOG_INFO,
- myFilename, myContent, sizeof(myContent)));
- fprintf(stderr, "injected test data, "
- "requires two intervening reboots "
- "to check for replication\n");
- }
- EXPECT_EQ((ssize_t)sizeof(myContent), ret);
-}
-
-// recovery.persist - Requires recovery.inject, then a reboot, then
-// expect success after for this test on that boot.
-TEST(recovery, persist) {
- EXPECT_EQ(0, access("/system/bin/recovery-persist", F_OK));
-
- ssize_t ret = __android_log_pmsg_file_read(
- LOG_ID_SYSTEM, ANDROID_LOG_INFO, "recovery/", __pmsg_fn, NULL);
- if (ret == -ENOENT) {
- EXPECT_LT(0, __android_log_pmsg_file_write(
- LOG_ID_SYSTEM, ANDROID_LOG_INFO,
- myFilename, myContent, sizeof(myContent)));
- fprintf(stderr, "injected test data, "
- "requires intervening reboot "
- "to check for storage\n");
- }
-
- int fd = open(myFilename, O_RDONLY);
- EXPECT_LE(0, fd);
-
- char buf[sizeof(myContent) + 32];
- ret = read(fd, buf, sizeof(buf));
- close(fd);
- EXPECT_EQ(ret, (ssize_t)sizeof(myContent));
- EXPECT_EQ(0, strcmp(myContent, buf));
- if (fd >= 0) {
- fprintf(stderr, "Removing persistent test data, "
- "check if reconstructed on reboot\n");
- }
- EXPECT_EQ(0, unlink(myFilename));
-}
diff --git a/tests/unit/sysutil_test.cpp b/tests/unit/sysutil_test.cpp
new file mode 100644
index 000000000..f4699664b
--- /dev/null
+++ b/tests/unit/sysutil_test.cpp
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+
+#include "otautil/SysUtil.h"
+
+TEST(SysUtilTest, InvalidArgs) {
+ MemMapping mapping;
+
+ // Invalid argument.
+ ASSERT_EQ(-1, sysMapFile(nullptr, &mapping));
+ ASSERT_EQ(-1, sysMapFile("/somefile", nullptr));
+}
+
+TEST(SysUtilTest, sysMapFileRegularFile) {
+ TemporaryFile temp_file1;
+ std::string content = "abc";
+ ASSERT_TRUE(android::base::WriteStringToFile(content, temp_file1.path));
+
+ // sysMapFile() should map the file to one range.
+ MemMapping mapping;
+ ASSERT_EQ(0, sysMapFile(temp_file1.path, &mapping));
+ ASSERT_NE(nullptr, mapping.addr);
+ ASSERT_EQ(content.size(), mapping.length);
+ ASSERT_EQ(1U, mapping.ranges.size());
+
+ sysReleaseMap(&mapping);
+ ASSERT_EQ(0U, mapping.ranges.size());
+}
+
+TEST(SysUtilTest, sysMapFileBlockMap) {
+ // Create a file that has 10 blocks.
+ TemporaryFile package;
+ std::string content;
+ constexpr size_t file_size = 4096 * 10;
+ content.reserve(file_size);
+ ASSERT_TRUE(android::base::WriteStringToFile(content, package.path));
+
+ TemporaryFile block_map_file;
+ std::string filename = std::string("@") + block_map_file.path;
+ MemMapping mapping;
+
+ // One range.
+ std::string block_map_content = std::string(package.path) + "\n40960 4096\n1\n0 10\n";
+ ASSERT_TRUE(android::base::WriteStringToFile(block_map_content, block_map_file.path));
+
+ ASSERT_EQ(0, sysMapFile(filename.c_str(), &mapping));
+ ASSERT_EQ(file_size, mapping.length);
+ ASSERT_EQ(1U, mapping.ranges.size());
+
+ // It's okay to not have the trailing '\n'.
+ block_map_content = std::string(package.path) + "\n40960 4096\n1\n0 10";
+ ASSERT_TRUE(android::base::WriteStringToFile(block_map_content, block_map_file.path));
+
+ ASSERT_EQ(0, sysMapFile(filename.c_str(), &mapping));
+ ASSERT_EQ(file_size, mapping.length);
+ ASSERT_EQ(1U, mapping.ranges.size());
+
+ // Or having multiple trailing '\n's.
+ block_map_content = std::string(package.path) + "\n40960 4096\n1\n0 10\n\n\n";
+ ASSERT_TRUE(android::base::WriteStringToFile(block_map_content, block_map_file.path));
+
+ ASSERT_EQ(0, sysMapFile(filename.c_str(), &mapping));
+ ASSERT_EQ(file_size, mapping.length);
+ ASSERT_EQ(1U, mapping.ranges.size());
+
+ // Multiple ranges.
+ block_map_content = std::string(package.path) + "\n40960 4096\n3\n0 3\n3 5\n5 10\n";
+ ASSERT_TRUE(android::base::WriteStringToFile(block_map_content, block_map_file.path));
+
+ ASSERT_EQ(0, sysMapFile(filename.c_str(), &mapping));
+ ASSERT_EQ(file_size, mapping.length);
+ ASSERT_EQ(3U, mapping.ranges.size());
+
+ sysReleaseMap(&mapping);
+ ASSERT_EQ(0U, mapping.ranges.size());
+}
+
+TEST(SysUtilTest, sysMapFileBlockMapInvalidBlockMap) {
+ MemMapping mapping;
+ TemporaryFile temp_file;
+ std::string filename = std::string("@") + temp_file.path;
+
+ // Block map file is too short.
+ ASSERT_TRUE(android::base::WriteStringToFile("/somefile\n", temp_file.path));
+ ASSERT_EQ(-1, sysMapFile(filename.c_str(), &mapping));
+
+ ASSERT_TRUE(android::base::WriteStringToFile("/somefile\n4096 4096\n0\n", temp_file.path));
+ ASSERT_EQ(-1, sysMapFile(filename.c_str(), &mapping));
+
+ // Block map file has unexpected number of lines.
+ ASSERT_TRUE(android::base::WriteStringToFile("/somefile\n4096 4096\n1\n", temp_file.path));
+ ASSERT_EQ(-1, sysMapFile(filename.c_str(), &mapping));
+
+ ASSERT_TRUE(android::base::WriteStringToFile("/somefile\n4096 4096\n2\n0 1\n", temp_file.path));
+ ASSERT_EQ(-1, sysMapFile(filename.c_str(), &mapping));
+
+ // Invalid size/blksize/range_count.
+ ASSERT_TRUE(android::base::WriteStringToFile("/somefile\nabc 4096\n1\n0 1\n", temp_file.path));
+ ASSERT_EQ(-1, sysMapFile(filename.c_str(), &mapping));
+
+ ASSERT_TRUE(android::base::WriteStringToFile("/somefile\n4096 4096\n\n0 1\n", temp_file.path));
+ ASSERT_EQ(-1, sysMapFile(filename.c_str(), &mapping));
+
+ // size/blksize/range_count don't match.
+ ASSERT_TRUE(android::base::WriteStringToFile("/somefile\n0 4096\n1\n0 1\n", temp_file.path));
+ ASSERT_EQ(-1, sysMapFile(filename.c_str(), &mapping));
+
+ ASSERT_TRUE(android::base::WriteStringToFile("/somefile\n4096 0\n1\n0 1\n", temp_file.path));
+ ASSERT_EQ(-1, sysMapFile(filename.c_str(), &mapping));
+
+ ASSERT_TRUE(android::base::WriteStringToFile("/somefile\n4096 4096\n0\n0 1\n", temp_file.path));
+ ASSERT_EQ(-1, sysMapFile(filename.c_str(), &mapping));
+
+ // Invalid block dev path.
+ ASSERT_TRUE(android::base::WriteStringToFile("/doesntexist\n4096 4096\n1\n0 1\n", temp_file.path));
+ ASSERT_EQ(-1, sysMapFile(filename.c_str(), &mapping));
+
+ sysReleaseMap(&mapping);
+ ASSERT_EQ(0U, mapping.ranges.size());
+}
diff --git a/tests/unit/zip_test.cpp b/tests/unit/zip_test.cpp
index b617446b8..49729467d 100644
--- a/tests/unit/zip_test.cpp
+++ b/tests/unit/zip_test.cpp
@@ -16,78 +16,69 @@
#include <errno.h>
#include <fcntl.h>
-#include <pthread.h>
#include <unistd.h>
#include <memory>
#include <vector>
#include <android-base/file.h>
-#include <android-base/stringprintf.h>
-#include <android-base/unique_fd.h>
#include <android-base/test_utils.h>
#include <gtest/gtest.h>
#include <otautil/SysUtil.h>
#include <otautil/ZipUtil.h>
#include <ziparchive/zip_archive.h>
-static const std::string DATA_PATH(getenv("ANDROID_DATA"));
-static const std::string TESTDATA_PATH("/recovery/testdata/");
-
-static const std::vector<uint8_t> kATxtContents {
- 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
- 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
- '\n'
-};
-
-static const std::vector<uint8_t> kBTxtContents {
- 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
- '\n'
-};
-
-TEST(otazip, ExtractPackageRecursive) {
- TemporaryDir td;
- ASSERT_NE(td.path, nullptr);
- ZipArchiveHandle handle;
- std::string zip_path = DATA_PATH + TESTDATA_PATH + "/ziptest_valid.zip";
- ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle));
- // Extract the whole package into a temp directory.
- ExtractPackageRecursive(handle, "", td.path, nullptr, nullptr);
- // Make sure all the files are extracted correctly.
- std::string path(td.path);
- android::base::unique_fd fd(open((path + "/a.txt").c_str(), O_RDONLY));
- ASSERT_NE(fd, -1);
- std::vector<uint8_t> read_data;
- read_data.resize(kATxtContents.size());
- // The content of the file is the same as expected.
- ASSERT_TRUE(android::base::ReadFully(fd.get(), read_data.data(), read_data.size()));
- ASSERT_EQ(0, memcmp(read_data.data(), kATxtContents.data(), kATxtContents.size()));
-
- fd.reset(open((path + "/b.txt").c_str(), O_RDONLY));
- ASSERT_NE(fd, -1);
- fd.reset(open((path + "/b/c.txt").c_str(), O_RDONLY));
- ASSERT_NE(fd, -1);
- fd.reset(open((path + "/b/d.txt").c_str(), O_RDONLY));
- ASSERT_NE(fd, -1);
- read_data.resize(kBTxtContents.size());
- ASSERT_TRUE(android::base::ReadFully(fd.get(), read_data.data(), read_data.size()));
- ASSERT_EQ(0, memcmp(read_data.data(), kBTxtContents.data(), kBTxtContents.size()));
+#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;
+ ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle));
+
+ // Extract the whole package into a temp directory.
+ TemporaryDir td;
+ ASSERT_NE(nullptr, td.path);
+ ExtractPackageRecursive(handle, "", td.path, nullptr, nullptr);
+
+ // Make sure all the files are extracted correctly.
+ std::string path(td.path);
+ ASSERT_EQ(0, access((path + "/a.txt").c_str(), O_RDONLY));
+ ASSERT_EQ(0, access((path + "/b.txt").c_str(), O_RDONLY));
+ ASSERT_EQ(0, access((path + "/b/c.txt").c_str(), O_RDONLY));
+ ASSERT_EQ(0, access((path + "/b/d.txt").c_str(), O_RDONLY));
+
+ // The content of the file is the same as expected.
+ std::string content1;
+ ASSERT_TRUE(android::base::ReadFileToString(path + "/a.txt", &content1));
+ ASSERT_EQ(kATxtContents, content1);
+
+ std::string content2;
+ ASSERT_TRUE(android::base::ReadFileToString(path + "/b/d.txt", &content2));
+ ASSERT_EQ(kBTxtContents, content2);
}
-TEST(otazip, OpenFromMemory) {
- MemMapping map;
- std::string zip_path = DATA_PATH + TESTDATA_PATH + "/ziptest_dummy-update.zip";
- ASSERT_EQ(0, sysMapFile(zip_path.c_str(), &map));
- // Map an update package into memory and open the archive from there.
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchiveFromMemory(map.addr, map.length, zip_path.c_str(), &handle));
- static constexpr const char* BINARY_PATH = "META-INF/com/google/android/update-binary";
- ZipString binary_path(BINARY_PATH);
- ZipEntry binary_entry;
- // Make sure the package opens correctly and its entry can be read.
- ASSERT_EQ(0, FindEntry(handle, binary_path, &binary_entry));
- TemporaryFile tmp_binary;
- ASSERT_NE(-1, tmp_binary.fd);
- ASSERT_EQ(0, ExtractEntryToFile(handle, &binary_entry, tmp_binary.fd));
+TEST(ZipTest, OpenFromMemory) {
+ MemMapping map;
+ std::string zip_path = from_testdata_base("ziptest_dummy-update.zip");
+ ASSERT_EQ(0, sysMapFile(zip_path.c_str(), &map));
+
+ // Map an update package into memory and open the archive from there.
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveFromMemory(map.addr, map.length, zip_path.c_str(), &handle));
+
+ static constexpr const char* BINARY_PATH = "META-INF/com/google/android/update-binary";
+ ZipString binary_path(BINARY_PATH);
+ ZipEntry binary_entry;
+ // Make sure the package opens correctly and its entry can be read.
+ ASSERT_EQ(0, FindEntry(handle, binary_path, &binary_entry));
+
+ TemporaryFile tmp_binary;
+ ASSERT_NE(-1, tmp_binary.fd);
+ ASSERT_EQ(0, ExtractEntryToFile(handle, &binary_entry, tmp_binary.fd));
+
+ sysReleaseMap(&map);
}
diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp
index f08ca5b0c..c939cf89d 100644
--- a/updater/blockimg.cpp
+++ b/updater/blockimg.cpp
@@ -1368,18 +1368,15 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, int /* arg
fprintf(stderr, "This update is a retry.\n");
}
- Value* blockdev_filename = nullptr;
- Value* transfer_list_value = nullptr;
- Value* new_data_fn = nullptr;
- Value* patch_data_fn = nullptr;
- if (ReadValueArgs(state, argv, 4, &blockdev_filename, &transfer_list_value,
- &new_data_fn, &patch_data_fn) < 0) {
- return StringValue("");
+ std::vector<std::unique_ptr<Value>> args;
+ if (!ReadValueArgs(state, 4, argv, &args)) {
+ return nullptr;
}
- std::unique_ptr<Value> blockdev_filename_holder(blockdev_filename);
- std::unique_ptr<Value> transfer_list_value_holder(transfer_list_value);
- std::unique_ptr<Value> new_data_fn_holder(new_data_fn);
- std::unique_ptr<Value> patch_data_fn_holder(patch_data_fn);
+
+ const Value* blockdev_filename = args[0].get();
+ const Value* transfer_list_value = args[1].get();
+ const Value* new_data_fn = args[2].get();
+ const Value* patch_data_fn = args[3].get();
if (blockdev_filename->type != VAL_STRING) {
ErrorAbort(state, kArgsParsingFailure, "blockdev_filename argument to %s must be string",
@@ -1689,14 +1686,13 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[]
}
Value* RangeSha1Fn(const char* name, State* state, int /* argc */, Expr* argv[]) {
- Value* blockdev_filename;
- Value* ranges;
-
- if (ReadValueArgs(state, argv, 2, &blockdev_filename, &ranges) < 0) {
- return StringValue("");
+ std::vector<std::unique_ptr<Value>> args;
+ if (!ReadValueArgs(state, 2, argv, &args)) {
+ return nullptr;
}
- std::unique_ptr<Value> ranges_holder(ranges);
- std::unique_ptr<Value> blockdev_filename_holder(blockdev_filename);
+
+ const Value* blockdev_filename = args[0].get();
+ const Value* ranges = args[1].get();
if (blockdev_filename->type != VAL_STRING) {
ErrorAbort(state, kArgsParsingFailure, "blockdev_filename argument to %s must be string",
@@ -1751,14 +1747,14 @@ Value* RangeSha1Fn(const char* name, State* state, int /* argc */, Expr* argv[])
// if executes successfully and an empty string otherwise.
Value* CheckFirstBlockFn(const char* name, State* state, int argc, Expr* argv[]) {
- Value* arg_filename;
-
- if (ReadValueArgs(state, argv, 1, &arg_filename) < 0) {
+ std::vector<std::unique_ptr<Value>> args;
+ if (!ReadValueArgs(state, 1, argv, &args)) {
return nullptr;
}
- std::unique_ptr<Value> filename(arg_filename);
- if (filename->type != VAL_STRING) {
+ const Value* arg_filename = args[0].get();
+
+ if (arg_filename->type != VAL_STRING) {
ErrorAbort(state, kArgsParsingFailure, "filename argument to %s must be string", name);
return StringValue("");
}
@@ -1799,15 +1795,13 @@ Value* CheckFirstBlockFn(const char* name, State* state, int argc, Expr* argv[])
Value* BlockImageRecoverFn(const char* name, State* state, int argc, Expr* argv[]) {
- Value* arg_filename;
- Value* arg_ranges;
-
- if (ReadValueArgs(state, argv, 2, &arg_filename, &arg_ranges) < 0) {
- return NULL;
+ std::vector<std::unique_ptr<Value>> args;
+ if (!ReadValueArgs(state, 2, argv, &args)) {
+ return nullptr;
}
- std::unique_ptr<Value> filename(arg_filename);
- std::unique_ptr<Value> ranges(arg_ranges);
+ const Value* filename = args[0].get();
+ const Value* ranges = args[1].get();
if (filename->type != VAL_STRING) {
ErrorAbort(state, kArgsParsingFailure, "filename argument to %s must be string", name);
diff --git a/updater/install.cpp b/updater/install.cpp
index a41c5db3c..cba95f5b0 100644
--- a/updater/install.cpp
+++ b/updater/install.cpp
@@ -40,6 +40,7 @@
#include <vector>
#include <android-base/parseint.h>
+#include <android-base/parsedouble.h>
#include <android-base/properties.h>
#include <android-base/strings.h>
#include <android-base/stringprintf.h>
@@ -65,7 +66,7 @@
// Send over the buffer to recovery though the command pipe.
static void uiPrint(State* state, const std::string& buffer) {
- UpdaterInfo* ui = reinterpret_cast<UpdaterInfo*>(state->cookie);
+ UpdaterInfo* ui = static_cast<UpdaterInfo*>(state->cookie);
// "line1\nline2\n" will be split into 3 tokens: "line1", "line2" and "".
// So skip sending empty strings to UI.
@@ -94,83 +95,84 @@ void uiPrintf(State* _Nonnull state, const char* _Nonnull format, ...) {
uiPrint(state, error_msg);
}
+static bool is_dir(const std::string& dirpath) {
+ struct stat st;
+ return stat(dirpath.c_str(), &st) == 0 && S_ISDIR(st.st_mode);
+}
+
// Create all parent directories of name, if necessary.
-static int make_parents(char* name) {
- char* p;
- for (p = name + (strlen(name)-1); p > name; --p) {
- if (*p != '/') continue;
- *p = '\0';
- if (make_parents(name) < 0) return -1;
- int result = mkdir(name, 0700);
- if (result == 0) printf("created [%s]\n", name);
- *p = '/';
- if (result == 0 || errno == EEXIST) {
- // successfully created or already existed; we're done
- return 0;
- } else {
- printf("failed to mkdir %s: %s\n", name, strerror(errno));
- return -1;
+static bool make_parents(const std::string& name) {
+ size_t prev_end = 0;
+ while (prev_end < name.size()) {
+ size_t next_end = name.find('/', prev_end + 1);
+ if (next_end == std::string::npos) {
+ break;
+ }
+ std::string dir_path = name.substr(0, next_end);
+ if (!is_dir(dir_path)) {
+ int result = mkdir(dir_path.c_str(), 0700);
+ if (result != 0) {
+ printf("failed to mkdir %s when make parents for %s: %s\n", dir_path.c_str(),
+ name.c_str(), strerror(errno));
+ return false;
+ }
+
+ printf("created [%s]\n", dir_path.c_str());
}
+ prev_end = next_end;
}
- return 0;
+ return true;
}
// mount(fs_type, partition_type, location, mount_point)
+// mount(fs_type, partition_type, location, mount_point, mount_options)
//
// fs_type="ext4" partition_type="EMMC" location=device
Value* MountFn(const char* name, State* state, int argc, Expr* argv[]) {
- char* result = nullptr;
if (argc != 4 && argc != 5) {
return ErrorAbort(state, kArgsParsingFailure, "%s() expects 4-5 args, got %d", name, argc);
}
- char* fs_type;
- char* partition_type;
- char* location;
- char* mount_point;
- char* mount_options;
- bool has_mount_options;
+
+ std::vector<std::string> args;
+ if (!ReadArgs(state, argc, argv, &args)) {
+ return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
+ }
+ const std::string& fs_type = args[0];
+ const std::string& partition_type = args[1];
+ const std::string& location = args[2];
+ const std::string& mount_point = args[3];
+ std::string mount_options;
+
if (argc == 5) {
- has_mount_options = true;
- if (ReadArgs(state, argv, 5, &fs_type, &partition_type,
- &location, &mount_point, &mount_options) < 0) {
- return NULL;
- }
- } else {
- has_mount_options = false;
- if (ReadArgs(state, argv, 4, &fs_type, &partition_type,
- &location, &mount_point) < 0) {
- return NULL;
- }
+ mount_options = args[4];
}
- if (strlen(fs_type) == 0) {
- ErrorAbort(state, kArgsParsingFailure, "fs_type argument to %s() can't be empty", name);
- goto done;
+ if (fs_type.empty()) {
+ return ErrorAbort(state, kArgsParsingFailure, "fs_type argument to %s() can't be empty",
+ name);
}
- if (strlen(partition_type) == 0) {
- ErrorAbort(state, kArgsParsingFailure, "partition_type argument to %s() can't be empty",
- name);
- goto done;
+ if (partition_type.empty()) {
+ return ErrorAbort(state, kArgsParsingFailure, "partition_type argument to %s() can't be empty",
+ name);
}
- if (strlen(location) == 0) {
- ErrorAbort(state, kArgsParsingFailure, "location argument to %s() can't be empty", name);
- goto done;
+ if (location.empty()) {
+ return ErrorAbort(state, kArgsParsingFailure, "location argument to %s() can't be empty",
+ name);
}
- if (strlen(mount_point) == 0) {
- ErrorAbort(state, kArgsParsingFailure, "mount_point argument to %s() can't be empty",
- name);
- goto done;
+ if (mount_point.empty()) {
+ return ErrorAbort(state, kArgsParsingFailure, "mount_point argument to %s() can't be empty",
+ name);
}
{
char *secontext = NULL;
if (sehandle) {
- selabel_lookup(sehandle, &secontext, mount_point, 0755);
+ selabel_lookup(sehandle, &secontext, mount_point.c_str(), 0755);
setfscreatecon(secontext);
}
- mkdir(mount_point, 0755);
+ mkdir(mount_point.c_str(), 0755);
if (secontext) {
freecon(secontext);
@@ -178,90 +180,71 @@ Value* MountFn(const char* name, State* state, int argc, Expr* argv[]) {
}
}
- if (mount(location, mount_point, fs_type,
- MS_NOATIME | MS_NODEV | MS_NODIRATIME,
- has_mount_options ? mount_options : "") < 0) {
+ if (mount(location.c_str(), mount_point.c_str(), fs_type.c_str(),
+ MS_NOATIME | MS_NODEV | MS_NODIRATIME, mount_options.c_str()) < 0) {
uiPrintf(state, "%s: failed to mount %s at %s: %s\n",
- name, location, mount_point, strerror(errno));
- result = strdup("");
- } else {
- result = mount_point;
+ name, location.c_str(), mount_point.c_str(), strerror(errno));
+ return StringValue("");
}
-done:
- free(fs_type);
- free(partition_type);
- free(location);
- if (result != mount_point) free(mount_point);
- if (has_mount_options) free(mount_options);
- return StringValue(result);
+ return StringValue(mount_point);
}
// is_mounted(mount_point)
Value* IsMountedFn(const char* name, State* state, int argc, Expr* argv[]) {
- char* result = nullptr;
if (argc != 1) {
return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %d", name, argc);
}
- char* mount_point;
- if (ReadArgs(state, argv, 1, &mount_point) < 0) {
- return NULL;
+
+ std::vector<std::string> args;
+ if (!ReadArgs(state, argc, argv, &args)) {
+ return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
}
- if (strlen(mount_point) == 0) {
- ErrorAbort(state, kArgsParsingFailure, "mount_point argument to unmount() can't be empty");
- goto done;
+ const std::string& mount_point = args[0];
+ if (mount_point.empty()) {
+ return ErrorAbort(state, kArgsParsingFailure,
+ "mount_point argument to unmount() can't be empty");
}
scan_mounted_volumes();
- {
- MountedVolume* vol = find_mounted_volume_by_mount_point(mount_point);
- if (vol == NULL) {
- result = strdup("");
- } else {
- result = mount_point;
- }
+ MountedVolume* vol = find_mounted_volume_by_mount_point(mount_point.c_str());
+ if (vol == nullptr) {
+ return StringValue("");
}
-done:
- if (result != mount_point) free(mount_point);
- return StringValue(result);
+ return StringValue(mount_point);
}
Value* UnmountFn(const char* name, State* state, int argc, Expr* argv[]) {
- char* result = nullptr;
if (argc != 1) {
return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %d", name, argc);
}
- char* mount_point;
- if (ReadArgs(state, argv, 1, &mount_point) < 0) {
- return NULL;
+ std::vector<std::string> args;
+ if (!ReadArgs(state, argc, argv, &args)) {
+ return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
}
- if (strlen(mount_point) == 0) {
- ErrorAbort(state, kArgsParsingFailure, "mount_point argument to unmount() can't be empty");
- goto done;
+ const std::string& mount_point = args[0];
+ if (mount_point.empty()) {
+ return ErrorAbort(state, kArgsParsingFailure,
+ "mount_point argument to unmount() can't be empty");
}
scan_mounted_volumes();
- {
- MountedVolume* vol = find_mounted_volume_by_mount_point(mount_point);
- if (vol == NULL) {
- uiPrintf(state, "unmount of %s failed; no such volume\n", mount_point);
- result = strdup("");
- } else {
- int ret = unmount_mounted_volume(vol);
- if (ret != 0) {
- uiPrintf(state, "unmount of %s failed (%d): %s\n",
- mount_point, ret, strerror(errno));
- }
- result = mount_point;
+ MountedVolume* vol = find_mounted_volume_by_mount_point(mount_point.c_str());
+ if (vol == nullptr) {
+ uiPrintf(state, "unmount of %s failed; no such volume\n", mount_point.c_str());
+ return nullptr;
+ } else {
+ int ret = unmount_mounted_volume(vol);
+ if (ret != 0) {
+ uiPrintf(state, "unmount of %s failed (%d): %s\n",
+ mount_point.c_str(), ret, strerror(errno));
}
}
-done:
- if (result != mount_point) free(mount_point);
- return StringValue(result);
+ return StringValue(mount_point);
}
static int exec_cmd(const char* path, char* const argv[]) {
@@ -278,7 +261,6 @@ static int exec_cmd(const char* path, char* const argv[]) {
return WEXITSTATUS(status);
}
-
// format(fs_type, partition_type, location, fs_size, mount_point)
//
// fs_type="ext4" partition_type="EMMC" location=device fs_size=<bytes> mount_point=<location>
@@ -287,120 +269,119 @@ static int exec_cmd(const char* path, char* const argv[]) {
// if fs_size > 0, that is the size to use
// if fs_size < 0, then reserve that many bytes at the end of the partition (not for "f2fs")
Value* FormatFn(const char* name, State* state, int argc, Expr* argv[]) {
- char* result = nullptr;
if (argc != 5) {
return ErrorAbort(state, kArgsParsingFailure, "%s() expects 5 args, got %d", name, argc);
}
- char* fs_type;
- char* partition_type;
- char* location;
- char* fs_size;
- char* mount_point;
- if (ReadArgs(state, argv, 5, &fs_type, &partition_type, &location, &fs_size, &mount_point) < 0) {
- return NULL;
+ std::vector<std::string> args;
+ if (!ReadArgs(state, argc, argv, &args)) {
+ return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
}
+ const std::string& fs_type = args[0];
+ const std::string& partition_type = args[1];
+ const std::string& location = args[2];
+ const std::string& fs_size = args[3];
+ const std::string& mount_point = args[4];
- if (strlen(fs_type) == 0) {
- ErrorAbort(state, kArgsParsingFailure, "fs_type argument to %s() can't be empty", name);
- goto done;
+ if (fs_type.empty()) {
+ return ErrorAbort(state, kArgsParsingFailure, "fs_type argument to %s() can't be empty",
+ name);
}
- if (strlen(partition_type) == 0) {
- ErrorAbort(state, kArgsParsingFailure, "partition_type argument to %s() can't be empty",
- name);
- goto done;
+ if (partition_type.empty()) {
+ return ErrorAbort(state, kArgsParsingFailure,
+ "partition_type argument to %s() can't be empty", name);
}
- if (strlen(location) == 0) {
- ErrorAbort(state, kArgsParsingFailure, "location argument to %s() can't be empty", name);
- goto done;
+ if (location.empty()) {
+ return ErrorAbort(state, kArgsParsingFailure, "location argument to %s() can't be empty",
+ name);
+ }
+ if (mount_point.empty()) {
+ return ErrorAbort(state, kArgsParsingFailure,
+ "mount_point argument to %s() can't be empty", name);
}
- if (strlen(mount_point) == 0) {
- ErrorAbort(state, kArgsParsingFailure, "mount_point argument to %s() can't be empty",
- name);
- goto done;
+ int64_t size;
+ if (!android::base::ParseInt(fs_size, &size)) {
+ return ErrorAbort(state, kArgsParsingFailure,
+ "%s: failed to parse int in %s\n", name, fs_size.c_str());
}
- if (strcmp(fs_type, "ext4") == 0) {
- int status = make_ext4fs(location, atoll(fs_size), mount_point, sehandle);
+ if (fs_type == "ext4") {
+ int status = make_ext4fs(location.c_str(), size, mount_point.c_str(), sehandle);
if (status != 0) {
- printf("%s: make_ext4fs failed (%d) on %s",
- name, status, location);
- result = strdup("");
- goto done;
+ printf("%s: make_ext4fs failed (%d) on %s", name, status, location.c_str());
+ return StringValue("");
}
- result = location;
- } else if (strcmp(fs_type, "f2fs") == 0) {
- char *num_sectors;
- if (asprintf(&num_sectors, "%lld", atoll(fs_size) / 512) <= 0) {
- printf("format_volume: failed to create %s command for %s\n", fs_type, location);
- result = strdup("");
- goto done;
+ return StringValue(location);
+ } else if (fs_type == "f2fs") {
+ if (size < 0) {
+ printf("%s: fs_size can't be negative for f2fs: %s", name, fs_size.c_str());
+ return StringValue("");
}
+ std::string num_sectors = std::to_string(size / 512);
+
const char *f2fs_path = "/sbin/mkfs.f2fs";
- const char* const f2fs_argv[] = {"mkfs.f2fs", "-t", "-d1", location, num_sectors, NULL};
+ const char* const f2fs_argv[] = {"mkfs.f2fs", "-t", "-d1", location.c_str(),
+ num_sectors.c_str(), nullptr};
int status = exec_cmd(f2fs_path, (char* const*)f2fs_argv);
- free(num_sectors);
if (status != 0) {
- printf("%s: mkfs.f2fs failed (%d) on %s",
- name, status, location);
- result = strdup("");
- goto done;
+ printf("%s: mkfs.f2fs failed (%d) on %s", name, status, location.c_str());
+ return StringValue("");
}
- result = location;
+ return StringValue(location);
} else {
printf("%s: unsupported fs_type \"%s\" partition_type \"%s\"",
- name, fs_type, partition_type);
+ name, fs_type.c_str(), partition_type.c_str());
}
-done:
- free(fs_type);
- free(partition_type);
- if (result != location) free(location);
- return StringValue(result);
+ return nullptr;
}
+// rename(src_name, dst_name)
+// Renames src_name to dst_name. It automatically creates the necessary directories for dst_name.
+// Example: rename("system/app/Hangouts/Hangouts.apk", "system/priv-app/Hangouts/Hangouts.apk")
Value* RenameFn(const char* name, State* state, int argc, Expr* argv[]) {
- char* result = nullptr;
if (argc != 2) {
return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc);
}
- char* src_name;
- char* dst_name;
-
- if (ReadArgs(state, argv, 2, &src_name, &dst_name) < 0) {
- return NULL;
+ std::vector<std::string> args;
+ if (!ReadArgs(state, argc, argv, &args)) {
+ return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
}
- if (strlen(src_name) == 0) {
- ErrorAbort(state, kArgsParsingFailure, "src_name argument to %s() can't be empty", name);
- goto done;
+ const std::string& src_name = args[0];
+ const std::string& dst_name = args[1];
+
+ if (src_name.empty()) {
+ return ErrorAbort(state, kArgsParsingFailure, "src_name argument to %s() can't be empty",
+ name);
}
- if (strlen(dst_name) == 0) {
- ErrorAbort(state, kArgsParsingFailure, "dst_name argument to %s() can't be empty", name);
- goto done;
+ if (dst_name.empty()) {
+ return ErrorAbort(state, kArgsParsingFailure, "dst_name argument to %s() can't be empty",
+ name);
}
- if (make_parents(dst_name) != 0) {
- ErrorAbort(state, kFileRenameFailure, "Creating parent of %s failed, error %s",
- dst_name, strerror(errno));
- } else if (access(dst_name, F_OK) == 0 && access(src_name, F_OK) != 0) {
+ if (!make_parents(dst_name)) {
+ return ErrorAbort(state, kFileRenameFailure, "Creating parent of %s failed, error %s",
+ dst_name.c_str(), strerror(errno));
+ } else if (access(dst_name.c_str(), F_OK) == 0 && access(src_name.c_str(), F_OK) != 0) {
// File was already moved
- result = dst_name;
- } else if (rename(src_name, dst_name) != 0) {
- ErrorAbort(state, kFileRenameFailure, "Rename of %s to %s failed, error %s",
- src_name, dst_name, strerror(errno));
- } else {
- result = dst_name;
+ return StringValue(dst_name);
+ } else if (rename(src_name.c_str(), dst_name.c_str()) != 0) {
+ return ErrorAbort(state, kFileRenameFailure, "Rename of %s to %s failed, error %s",
+ src_name.c_str(), dst_name.c_str(), strerror(errno));
}
-done:
- free(src_name);
- if (result != dst_name) free(dst_name);
- return StringValue(result);
+ return StringValue(dst_name);
}
+// delete([filename, ...])
+// Deletes all the filenames listed. Returns the number of files successfully deleted.
+//
+// delete_recursive([dirname, ...])
+// Recursively deletes dirnames and all their contents. Returns the number of directories
+// successfully deleted.
Value* DeleteFn(const char* name, State* state, int argc, Expr* argv[]) {
- std::vector<std::string> paths;
+ std::vector<std::string> paths(argc);
for (int i = 0; i < argc; ++i) {
if (!Evaluate(state, argv[i], &paths[i])) {
return nullptr;
@@ -416,7 +397,7 @@ Value* DeleteFn(const char* name, State* state, int argc, Expr* argv[]) {
}
}
- return StringValue(android::base::StringPrintf("%d", success));
+ return StringValue(std::to_string(success));
}
@@ -424,20 +405,28 @@ Value* ShowProgressFn(const char* name, State* state, int argc, Expr* argv[]) {
if (argc != 2) {
return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc);
}
- char* frac_str;
- char* sec_str;
- if (ReadArgs(state, argv, 2, &frac_str, &sec_str) < 0) {
- return NULL;
+
+ std::vector<std::string> args;
+ if (!ReadArgs(state, argc, argv, &args)) {
+ return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
}
+ const std::string& frac_str = args[0];
+ const std::string& sec_str = args[1];
- double frac = strtod(frac_str, NULL);
+ double frac;
+ if (!android::base::ParseDouble(frac_str.c_str(), &frac)) {
+ return ErrorAbort(state, kArgsParsingFailure,
+ "%s: failed to parse double in %s\n", name, frac_str.c_str());
+ }
int sec;
- android::base::ParseInt(sec_str, &sec);
+ if (!android::base::ParseInt(sec_str.c_str(), &sec)) {
+ return ErrorAbort(state, kArgsParsingFailure,
+ "%s: failed to parse int in %s\n", name, sec_str.c_str());
+ }
UpdaterInfo* ui = (UpdaterInfo*)(state->cookie);
fprintf(ui->cmd_pipe, "progress %f %d\n", frac, sec);
- free(sec_str);
return StringValue(frac_str);
}
@@ -445,12 +434,18 @@ Value* SetProgressFn(const char* name, State* state, int argc, Expr* argv[]) {
if (argc != 1) {
return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %d", name, argc);
}
- char* frac_str;
- if (ReadArgs(state, argv, 1, &frac_str) < 0) {
- return NULL;
+
+ std::vector<std::string> args;
+ if (!ReadArgs(state, 1, argv, &args)) {
+ return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
}
+ const std::string& frac_str = args[0];
- double frac = strtod(frac_str, NULL);
+ double frac;
+ if (!android::base::ParseDouble(frac_str.c_str(), &frac)) {
+ return ErrorAbort(state, kArgsParsingFailure,
+ "%s: failed to parse double in %s\n", name, frac_str.c_str());
+ }
UpdaterInfo* ui = (UpdaterInfo*)(state->cookie);
fprintf(ui->cmd_pipe, "set_progress %f\n", frac);
@@ -464,9 +459,13 @@ Value* PackageExtractDirFn(const char* name, State* state,
if (argc != 2) {
return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc);
}
- char* zip_path;
- char* dest_path;
- if (ReadArgs(state, argv, 2, &zip_path, &dest_path) < 0) return NULL;
+
+ 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;
@@ -475,8 +474,6 @@ Value* PackageExtractDirFn(const char* name, State* state,
bool success = ExtractPackageRecursive(za, zip_path, dest_path, &timestamp, sehandle);
- free(zip_path);
- free(dest_path);
return StringValue(success ? "t" : "");
}
@@ -499,54 +496,57 @@ Value* PackageExtractFileFn(const char* name, State* state,
ZipArchiveHandle za = ((UpdaterInfo*)(state->cookie))->package_zip;
- char* zip_path;
- char* dest_path;
- if (ReadArgs(state, argv, 2, &zip_path, &dest_path) < 0) return NULL;
+ 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);
+ 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);
- goto done2;
+ printf("%s: no %s in package\n", name, zip_path.c_str());
+ return StringValue("");
}
- {
- int fd = TEMP_FAILURE_RETRY(ota_open(dest_path, 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, strerror(errno));
- goto done2;
- }
- success = ExtractEntryToFile(za, &entry, fd);
- if (ota_fsync(fd) == -1) {
- printf("fsync of \"%s\" failed: %s\n", dest_path, strerror(errno));
- success = false;
- }
- if (ota_close(fd) == -1) {
- printf("close of \"%s\" failed: %s\n", dest_path, 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("");
+ }
+ 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;
}
- done2:
- free(zip_path);
- free(dest_path);
return StringValue(success ? "t" : "");
} else {
// The one-argument version returns the contents of the file
// as the result.
- char* zip_path;
- if (ReadArgs(state, argv, 1, &zip_path) < 0) return NULL;
+ 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];
Value* v = new Value(VAL_INVALID, "");
ZipArchiveHandle za = ((UpdaterInfo*)(state->cookie))->package_zip;
- ZipString zip_string_path(zip_path);
+ 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);
- goto done1;
+ printf("%s: no %s in package\n", name, zip_path.c_str());
+ return v;
}
v->data.resize(entry.uncompressed_length);
@@ -557,8 +557,6 @@ Value* PackageExtractFileFn(const char* name, State* state,
success = true;
}
- done1:
- free(zip_path);
if (!success) {
v->data.clear();
} else {
@@ -579,34 +577,31 @@ Value* SymlinkFn(const char* name, State* state, int argc, Expr* argv[]) {
return nullptr;
}
- char** srcs = ReadVarArgs(state, argc-1, argv+1);
- if (srcs == NULL) {
- return NULL;
+ std::vector<std::string> srcs;
+ if (!ReadArgs(state, argc-1, argv+1, &srcs)) {
+ return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
}
int bad = 0;
- int i;
- for (i = 0; i < argc-1; ++i) {
- if (unlink(srcs[i]) < 0) {
+ for (int i = 0; i < argc-1; ++i) {
+ if (unlink(srcs[i].c_str()) < 0) {
if (errno != ENOENT) {
printf("%s: failed to remove %s: %s\n",
- name, srcs[i], strerror(errno));
+ name, srcs[i].c_str(), strerror(errno));
++bad;
}
}
- if (make_parents(srcs[i])) {
+ if (!make_parents(srcs[i])) {
printf("%s: failed to symlink %s to %s: making parents failed\n",
- name, srcs[i], target.c_str());
+ name, srcs[i].c_str(), target.c_str());
++bad;
}
- if (symlink(target.c_str(), srcs[i]) < 0) {
+ if (symlink(target.c_str(), srcs[i].c_str()) < 0) {
printf("%s: failed to symlink %s to %s: %s\n",
- name, srcs[i], target.c_str(), strerror(errno));
+ name, srcs[i].c_str(), target.c_str(), strerror(errno));
++bad;
}
- free(srcs[i]);
}
- free(srcs);
if (bad) {
return ErrorAbort(state, kSymlinkFailure, "%s: some symlinks failed", name);
}
@@ -625,12 +620,13 @@ struct perm_parsed_args {
bool has_dmode;
mode_t dmode;
bool has_selabel;
- char* selabel;
+ const char* selabel;
bool has_capabilities;
uint64_t capabilities;
};
-static struct perm_parsed_args ParsePermArgs(State * state, int argc, char** args) {
+static struct perm_parsed_args ParsePermArgs(State * state, int argc,
+ const std::vector<std::string>& args) {
int i;
struct perm_parsed_args parsed;
int bad = 0;
@@ -639,84 +635,84 @@ static struct perm_parsed_args ParsePermArgs(State * state, int argc, char** arg
memset(&parsed, 0, sizeof(parsed));
for (i = 1; i < argc; i += 2) {
- if (strcmp("uid", args[i]) == 0) {
+ if (args[i] == "uid") {
int64_t uid;
- if (sscanf(args[i+1], "%" SCNd64, &uid) == 1) {
+ if (sscanf(args[i+1].c_str(), "%" SCNd64, &uid) == 1) {
parsed.uid = uid;
parsed.has_uid = true;
} else {
- uiPrintf(state, "ParsePermArgs: invalid UID \"%s\"\n", args[i + 1]);
+ uiPrintf(state, "ParsePermArgs: invalid UID \"%s\"\n", args[i + 1].c_str());
bad++;
}
continue;
}
- if (strcmp("gid", args[i]) == 0) {
+ if (args[i] == "gid") {
int64_t gid;
- if (sscanf(args[i+1], "%" SCNd64, &gid) == 1) {
+ if (sscanf(args[i+1].c_str(), "%" SCNd64, &gid) == 1) {
parsed.gid = gid;
parsed.has_gid = true;
} else {
- uiPrintf(state, "ParsePermArgs: invalid GID \"%s\"\n", args[i + 1]);
+ uiPrintf(state, "ParsePermArgs: invalid GID \"%s\"\n", args[i + 1].c_str());
bad++;
}
continue;
}
- if (strcmp("mode", args[i]) == 0) {
+ if (args[i] == "mode") {
int32_t mode;
- if (sscanf(args[i+1], "%" SCNi32, &mode) == 1) {
+ if (sscanf(args[i+1].c_str(), "%" SCNi32, &mode) == 1) {
parsed.mode = mode;
parsed.has_mode = true;
} else {
- uiPrintf(state, "ParsePermArgs: invalid mode \"%s\"\n", args[i + 1]);
+ uiPrintf(state, "ParsePermArgs: invalid mode \"%s\"\n", args[i + 1].c_str());
bad++;
}
continue;
}
- if (strcmp("dmode", args[i]) == 0) {
+ if (args[i] == "dmode") {
int32_t mode;
- if (sscanf(args[i+1], "%" SCNi32, &mode) == 1) {
+ if (sscanf(args[i+1].c_str(), "%" SCNi32, &mode) == 1) {
parsed.dmode = mode;
parsed.has_dmode = true;
} else {
- uiPrintf(state, "ParsePermArgs: invalid dmode \"%s\"\n", args[i + 1]);
+ uiPrintf(state, "ParsePermArgs: invalid dmode \"%s\"\n", args[i + 1].c_str());
bad++;
}
continue;
}
- if (strcmp("fmode", args[i]) == 0) {
+ if (args[i] == "fmode") {
int32_t mode;
- if (sscanf(args[i+1], "%" SCNi32, &mode) == 1) {
+ if (sscanf(args[i+1].c_str(), "%" SCNi32, &mode) == 1) {
parsed.fmode = mode;
parsed.has_fmode = true;
} else {
- uiPrintf(state, "ParsePermArgs: invalid fmode \"%s\"\n", args[i + 1]);
+ uiPrintf(state, "ParsePermArgs: invalid fmode \"%s\"\n", args[i + 1].c_str());
bad++;
}
continue;
}
- if (strcmp("capabilities", args[i]) == 0) {
+ if (args[i] == "capabilities") {
int64_t capabilities;
- if (sscanf(args[i+1], "%" SCNi64, &capabilities) == 1) {
+ if (sscanf(args[i+1].c_str(), "%" SCNi64, &capabilities) == 1) {
parsed.capabilities = capabilities;
parsed.has_capabilities = true;
} else {
- uiPrintf(state, "ParsePermArgs: invalid capabilities \"%s\"\n", args[i + 1]);
+ uiPrintf(state, "ParsePermArgs: invalid capabilities \"%s\"\n", args[i + 1].c_str());
bad++;
}
continue;
}
- if (strcmp("selabel", args[i]) == 0) {
- if (args[i+1][0] != '\0') {
- parsed.selabel = args[i+1];
+ if (args[i] == "selabel") {
+ if (!args[i+1].empty()) {
+ parsed.selabel = args[i+1].c_str();
parsed.has_selabel = true;
} else {
- uiPrintf(state, "ParsePermArgs: invalid selabel \"%s\"\n", args[i + 1]);
+ uiPrintf(state, "ParsePermArgs: invalid selabel \"%s\"\n", args[i + 1].c_str());
bad++;
}
continue;
}
if (max_warnings != 0) {
- printf("ParsedPermArgs: unknown key \"%s\", ignoring\n", args[i]);
+ printf("ParsedPermArgs: unknown key \"%s\", ignoring\n", args[i].c_str());
max_warnings--;
if (max_warnings == 0) {
printf("ParsedPermArgs: suppressing further warnings\n");
@@ -825,48 +821,34 @@ static int do_SetMetadataRecursive(const char* filename, const struct stat *stat
}
static Value* SetMetadataFn(const char* name, State* state, int argc, Expr* argv[]) {
- int bad = 0;
- struct stat sb;
- Value* result = NULL;
-
- bool recursive = (strcmp(name, "set_metadata_recursive") == 0);
-
if ((argc % 2) != 1) {
return ErrorAbort(state, kArgsParsingFailure,
"%s() expects an odd number of arguments, got %d", name, argc);
}
- char** args = ReadVarArgs(state, argc, argv);
- if (args == NULL) return NULL;
-
- if (lstat(args[0], &sb) == -1) {
- result = ErrorAbort(state, kSetMetadataFailure, "%s: Error on lstat of \"%s\": %s",
- name, args[0], strerror(errno));
- goto done;
+ std::vector<std::string> args;
+ if (!ReadArgs(state, argc, argv, &args)) {
+ return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
}
- {
- struct perm_parsed_args parsed = ParsePermArgs(state, argc, args);
-
- if (recursive) {
- recursive_parsed_args = parsed;
- recursive_state = state;
- bad += nftw(args[0], do_SetMetadataRecursive, 30, FTW_CHDIR | FTW_DEPTH | FTW_PHYS);
- memset(&recursive_parsed_args, 0, sizeof(recursive_parsed_args));
- recursive_state = NULL;
- } else {
- bad += ApplyParsedPerms(state, args[0], &sb, parsed);
- }
+ struct stat sb;
+ if (lstat(args[0].c_str(), &sb) == -1) {
+ return ErrorAbort(state, kSetMetadataFailure, "%s: Error on lstat of \"%s\": %s",
+ name, args[0].c_str(), strerror(errno));
}
-done:
- for (int i = 0; i < argc; ++i) {
- free(args[i]);
- }
- free(args);
+ struct perm_parsed_args parsed = ParsePermArgs(state, argc, args);
+ int bad = 0;
+ bool recursive = (strcmp(name, "set_metadata_recursive") == 0);
- if (result != NULL) {
- return result;
+ if (recursive) {
+ recursive_parsed_args = parsed;
+ recursive_state = state;
+ bad += nftw(args[0].c_str(), do_SetMetadataRecursive, 30, FTW_CHDIR | FTW_DEPTH | FTW_PHYS);
+ memset(&recursive_parsed_args, 0, sizeof(recursive_parsed_args));
+ recursive_state = NULL;
+ } else {
+ bad += ApplyParsedPerms(state, args[0].c_str(), &sb, parsed);
}
if (bad > 0) {
@@ -893,116 +875,86 @@ Value* GetPropFn(const char* name, State* state, int argc, Expr* argv[]) {
// file_getprop(file, key)
//
// interprets 'file' as a getprop-style file (key=value pairs, one
-// per line. # comment lines,blank lines, lines without '=' ignored),
+// per line. # comment lines, blank lines, lines without '=' ignored),
// and returns the value for 'key' (or "" if it isn't defined).
Value* FileGetPropFn(const char* name, State* state, int argc, Expr* argv[]) {
- char* result = NULL;
- char* buffer = NULL;
- char* filename;
- char* key;
- if (ReadArgs(state, argv, 2, &filename, &key) < 0) {
- return NULL;
+ if (argc != 2) {
+ return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc);
}
- struct stat st;
- if (stat(filename, &st) < 0) {
- ErrorAbort(state, kFileGetPropFailure, "%s: failed to stat \"%s\": %s", name, filename,
- strerror(errno));
- goto done;
+ 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[0];
+ const std::string& key = args[1];
-#define MAX_FILE_GETPROP_SIZE 65536
-
- if (st.st_size > MAX_FILE_GETPROP_SIZE) {
- ErrorAbort(state, kFileGetPropFailure, "%s too large for %s (max %d)", filename, name,
- MAX_FILE_GETPROP_SIZE);
- goto done;
+ struct stat st;
+ if (stat(filename.c_str(), &st) < 0) {
+ return ErrorAbort(state, kFileGetPropFailure, "%s: failed to stat \"%s\": %s", name,
+ filename.c_str(), strerror(errno));
}
- buffer = reinterpret_cast<char*>(malloc(st.st_size+1));
- if (buffer == NULL) {
- ErrorAbort(state, kFileGetPropFailure, "%s: failed to alloc %zu bytes", name,
- static_cast<size_t>(st.st_size+1));
- goto done;
+ constexpr off_t MAX_FILE_GETPROP_SIZE = 65536;
+ if (st.st_size > MAX_FILE_GETPROP_SIZE) {
+ return ErrorAbort(state, kFileGetPropFailure, "%s too large for %s (max %lld)",
+ filename.c_str(), name, static_cast<long long>(MAX_FILE_GETPROP_SIZE));
}
- FILE* f;
- f = ota_fopen(filename, "rb");
- if (f == NULL) {
- ErrorAbort(state, kFileOpenFailure, "%s: failed to open %s: %s", name, filename,
- strerror(errno));
- goto done;
+ std::string buffer(st.st_size, '\0');
+ FILE* f = ota_fopen(filename.c_str(), "rb");
+ if (f == nullptr) {
+ return ErrorAbort(state, kFileOpenFailure, "%s: failed to open %s: %s", name,
+ filename.c_str(), strerror(errno));
}
- if (ota_fread(buffer, 1, st.st_size, f) != static_cast<size_t>(st.st_size)) {
+ if (ota_fread(&buffer[0], 1, st.st_size, f) != static_cast<size_t>(st.st_size)) {
ErrorAbort(state, kFreadFailure, "%s: failed to read %zu bytes from %s",
- name, static_cast<size_t>(st.st_size), filename);
+ name, static_cast<size_t>(st.st_size), filename.c_str());
ota_fclose(f);
- goto done;
+ return nullptr;
}
- buffer[st.st_size] = '\0';
ota_fclose(f);
- char* line;
- line = strtok(buffer, "\n");
- do {
- // skip whitespace at start of line
- while (*line && isspace(*line)) ++line;
+ std::vector<std::string> lines = android::base::Split(buffer, "\n");
+ for (size_t i = 0; i < lines.size(); i++) {
+ std::string line = android::base::Trim(lines[i]);
// comment or blank line: skip to next line
- if (*line == '\0' || *line == '#') continue;
-
- char* equal = strchr(line, '=');
- if (equal == NULL) {
+ if (line.empty() || line[0] == '#') {
+ continue;
+ }
+ size_t equal_pos = line.find('=');
+ if (equal_pos == std::string::npos) {
continue;
}
// trim whitespace between key and '='
- char* key_end = equal-1;
- while (key_end > line && isspace(*key_end)) --key_end;
- key_end[1] = '\0';
+ std::string str = android::base::Trim(line.substr(0, equal_pos));
// not the key we're looking for
- if (strcmp(key, line) != 0) continue;
-
- // skip whitespace after the '=' to the start of the value
- char* val_start = equal+1;
- while(*val_start && isspace(*val_start)) ++val_start;
+ if (key != str) continue;
- // trim trailing whitespace
- char* val_end = val_start + strlen(val_start)-1;
- while (val_end > val_start && isspace(*val_end)) --val_end;
- val_end[1] = '\0';
-
- result = strdup(val_start);
- break;
-
- } while ((line = strtok(NULL, "\n")));
-
- if (result == NULL) result = strdup("");
+ return StringValue(android::base::Trim(line.substr(equal_pos + 1)));
+ }
- done:
- free(filename);
- free(key);
- free(buffer);
- return StringValue(result);
+ return StringValue("");
}
// apply_patch_space(bytes)
Value* ApplyPatchSpaceFn(const char* name, State* state,
int argc, Expr* argv[]) {
- char* bytes_str;
- if (ReadArgs(state, argv, 1, &bytes_str) < 0) {
- return NULL;
+ std::vector<std::string> args;
+ if (!ReadArgs(state, 1, argv, &args)) {
+ return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
}
+ const std::string& bytes_str = args[0];
size_t bytes;
- if (!android::base::ParseUint(bytes_str, &bytes)) {
- ErrorAbort(state, kArgsParsingFailure, "%s(): can't parse \"%s\" as byte count\n\n",
- name, bytes_str);
- free(bytes_str);
- return nullptr;
+ if (!android::base::ParseUint(bytes_str.c_str(), &bytes)) {
+ return ErrorAbort(state, kArgsParsingFailure, "%s(): can't parse \"%s\" as byte count\n\n",
+ name, bytes_str.c_str());
}
return StringValue(CacheSizeCheck(bytes) ? "" : "t");
@@ -1013,63 +965,51 @@ Value* ApplyPatchSpaceFn(const char* name, State* state,
Value* ApplyPatchFn(const char* name, State* state, int argc, Expr* argv[]) {
if (argc < 6 || (argc % 2) == 1) {
return ErrorAbort(state, kArgsParsingFailure, "%s(): expected at least 6 args and an "
- "even number, got %d", name, argc);
+ "even number, got %d", name, argc);
}
- char* source_filename;
- char* target_filename;
- char* target_sha1;
- char* target_size_str;
- if (ReadArgs(state, argv, 4, &source_filename, &target_filename,
- &target_sha1, &target_size_str) < 0) {
- return NULL;
+ std::vector<std::string> args;
+ if (!ReadArgs(state, 4, argv, &args)) {
+ return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
}
+ const std::string& source_filename = args[0];
+ const std::string& target_filename = args[1];
+ const std::string& target_sha1 = args[2];
+ const std::string& target_size_str = args[3];
size_t target_size;
- if (!android::base::ParseUint(target_size_str, &target_size)) {
- ErrorAbort(state, kArgsParsingFailure, "%s(): can't parse \"%s\" as byte count",
- name, target_size_str);
- free(source_filename);
- free(target_filename);
- free(target_sha1);
- free(target_size_str);
- return nullptr;
+ if (!android::base::ParseUint(target_size_str.c_str(), &target_size)) {
+ return ErrorAbort(state, kArgsParsingFailure, "%s(): can't parse \"%s\" as byte count",
+ name, target_size_str.c_str());
}
int patchcount = (argc-4) / 2;
- std::unique_ptr<Value*> arg_values(ReadValueVarArgs(state, argc-4, argv+4));
- if (!arg_values) {
+ std::vector<std::unique_ptr<Value>> arg_values;
+ if (!ReadValueArgs(state, argc-4, argv+4, &arg_values)) {
return nullptr;
}
- std::vector<std::unique_ptr<Value>> patch_shas;
- std::vector<std::unique_ptr<Value>> patches;
- // Protect values by unique_ptrs first to get rid of memory leak.
- for (int i = 0; i < patchcount * 2; i += 2) {
- patch_shas.emplace_back(arg_values.get()[i]);
- patches.emplace_back(arg_values.get()[i+1]);
- }
for (int i = 0; i < patchcount; ++i) {
- if (patch_shas[i]->type != VAL_STRING) {
- ErrorAbort(state, kArgsParsingFailure, "%s(): sha-1 #%d is not string", name, i);
- return nullptr;
+ if (arg_values[i * 2]->type != VAL_STRING) {
+ return ErrorAbort(state, kArgsParsingFailure, "%s(): sha-1 #%d is not string", name,
+ i * 2);
}
- if (patches[i]->type != VAL_BLOB) {
- ErrorAbort(state, kArgsParsingFailure, "%s(): patch #%d is not blob", name, i);
- return nullptr;
+ if (arg_values[i * 2 + 1]->type != VAL_BLOB) {
+ return ErrorAbort(state, kArgsParsingFailure, "%s(): patch #%d is not blob", name,
+ i * 2 + 1);
}
}
std::vector<std::string> patch_sha_str;
- std::vector<Value*> patch_ptrs;
+ std::vector<std::unique_ptr<Value>> patches;
for (int i = 0; i < patchcount; ++i) {
- patch_sha_str.push_back(patch_shas[i]->data);
- patch_ptrs.push_back(patches[i].get());
+ patch_sha_str.push_back(arg_values[i * 2]->data);
+ patches.push_back(std::move(arg_values[i * 2 + 1]));
}
- int result = applypatch(source_filename, target_filename,
- target_sha1, target_size,
- patch_sha_str, patch_ptrs.data(), NULL);
+ int result = applypatch(source_filename.c_str(), target_filename.c_str(),
+ target_sha1.c_str(), target_size,
+ patch_sha_str, patches, nullptr);
return StringValue(result == 0 ? "t" : "");
}
@@ -1082,17 +1022,17 @@ Value* ApplyPatchCheckFn(const char* name, State* state,
name, argc);
}
- char* filename;
- if (ReadArgs(state, argv, 1, &filename) < 0) {
- return nullptr;
+ std::vector<std::string> args;
+ if (!ReadArgs(state, 1, argv, &args)) {
+ return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
}
+ const std::string& filename = args[0];
std::vector<std::string> sha1s;
- if (!ReadArgs(state, argc-1, argv+1, &sha1s)) {
- return nullptr;
+ if (!ReadArgs(state, argc - 1, argv + 1, &sha1s)) {
+ return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
}
-
- int result = applypatch_check(filename, sha1s);
+ int result = applypatch_check(filename.c_str(), sha1s);
return StringValue(result == 0 ? "t" : "");
}
@@ -1100,19 +1040,12 @@ Value* ApplyPatchCheckFn(const char* name, State* state,
// This is the updater side handler for ui_print() in edify script. Contents
// will be sent over to the recovery side for on-screen display.
Value* UIPrintFn(const char* name, State* state, int argc, Expr* argv[]) {
- char** args = ReadVarArgs(state, argc, argv);
- if (args == NULL) {
- return NULL;
+ std::vector<std::string> args;
+ if (!ReadArgs(state, argc, argv, &args)) {
+ return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
}
- std::string buffer;
- for (int i = 0; i < argc; ++i) {
- buffer += args[i];
- free(args[i]);
- }
- free(args);
-
- buffer += "\n";
+ std::string buffer = android::base::Join(args, "") + "\n";
uiPrint(state, buffer);
return StringValue(buffer);
}
@@ -1129,14 +1062,17 @@ Value* RunProgramFn(const char* name, State* state, int argc, Expr* argv[]) {
if (argc < 1) {
return ErrorAbort(state, kArgsParsingFailure, "%s() expects at least 1 arg", name);
}
- char** args = ReadVarArgs(state, argc, argv);
- if (args == NULL) {
- return NULL;
+
+ std::vector<std::string> args;
+ if (!ReadArgs(state, argc, argv, &args)) {
+ return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
}
- char** args2 = reinterpret_cast<char**>(malloc(sizeof(char*) * (argc+1)));
- memcpy(args2, args, sizeof(char*) * argc);
- args2[argc] = NULL;
+ char* args2[argc+1];
+ for (int i = 0; i < argc; i++) {
+ args2[i] = &args[i][0];
+ }
+ args2[argc] = nullptr;
printf("about to run program [%s] with %d args\n", args2[0], argc);
@@ -1158,13 +1094,6 @@ Value* RunProgramFn(const char* name, State* state, int argc, Expr* argv[]) {
WTERMSIG(status));
}
- int i;
- for (i = 0; i < argc; ++i) {
- free(args[i]);
- }
- free(args);
- free(args2);
-
return StringValue(android::base::StringPrintf("%d", status));
}
@@ -1181,13 +1110,9 @@ Value* Sha1CheckFn(const char* name, State* state, int argc, Expr* argv[]) {
return ErrorAbort(state, kArgsParsingFailure, "%s() expects at least 1 arg", name);
}
- std::unique_ptr<Value*> arg_values(ReadValueVarArgs(state, argc, argv));
- if (arg_values == nullptr) {
- return nullptr;
- }
std::vector<std::unique_ptr<Value>> args;
- for (int i = 0; i < argc; ++i) {
- args.emplace_back(arg_values.get()[i]);
+ if (!ReadValueArgs(state, argc, argv, &args)) {
+ return nullptr;
}
if (args[0]->type == VAL_INVALID) {
@@ -1223,17 +1148,20 @@ Value* ReadFileFn(const char* name, State* state, int argc, Expr* argv[]) {
if (argc != 1) {
return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %d", name, argc);
}
- char* filename;
- if (ReadArgs(state, argv, 1, &filename) < 0) return NULL;
+
+ std::vector<std::string> args;
+ if (!ReadArgs(state, 1, argv, &args)) {
+ return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
+ }
+ const std::string& filename = args[0];
Value* v = new Value(VAL_INVALID, "");
FileContents fc;
- if (LoadFileContents(filename, &fc) == 0) {
+ if (LoadFileContents(filename.c_str(), &fc) == 0) {
v->type = VAL_BLOB;
v->data = std::string(fc.data.begin(), fc.data.end());
}
- free(filename);
return v;
}
@@ -1251,27 +1179,27 @@ Value* RebootNowFn(const char* name, State* state, int argc, Expr* argv[]) {
return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc);
}
- char* filename;
- char* property;
- if (ReadArgs(state, argv, 2, &filename, &property) < 0) return NULL;
+ 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[0];
+ const std::string& property = args[1];
// zero out the 'command' field of the bootloader message.
char buffer[80];
memset(buffer, 0, sizeof(((struct bootloader_message*)0)->command));
- FILE* f = ota_fopen(filename, "r+b");
+ FILE* f = ota_fopen(filename.c_str(), "r+b");
fseek(f, offsetof(struct bootloader_message, command), SEEK_SET);
ota_fwrite(buffer, sizeof(((struct bootloader_message*)0)->command), 1, f);
ota_fclose(f);
- free(filename);
std::string reboot_cmd = "reboot,";
- if (property != nullptr) reboot_cmd += property;
+ reboot_cmd += property;
android::base::SetProperty(ANDROID_RB_PROPERTY, reboot_cmd);
sleep(5);
- free(property);
- ErrorAbort(state, kRebootFailure, "%s() failed to reboot", name);
- return NULL;
+ return ErrorAbort(state, kRebootFailure, "%s() failed to reboot", name);
}
// Store a string value somewhere that future invocations of recovery
@@ -1289,26 +1217,28 @@ Value* SetStageFn(const char* name, State* state, int argc, Expr* argv[]) {
return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc);
}
- char* filename;
- char* stagestr;
- if (ReadArgs(state, argv, 2, &filename, &stagestr) < 0) return NULL;
+ 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[0];
+ std::string& stagestr = args[1];
// Store this value in the misc partition, immediately after the
// bootloader message that the main recovery uses to save its
// arguments in case of the device restarting midway through
// package installation.
- FILE* f = ota_fopen(filename, "r+b");
+ FILE* f = ota_fopen(filename.c_str(), "r+b");
fseek(f, offsetof(struct bootloader_message, stage), SEEK_SET);
- size_t to_write = strlen(stagestr) + 1;
+ size_t to_write = stagestr.size();
size_t max_size = sizeof(((struct bootloader_message*)0)->stage);
if (to_write > max_size) {
to_write = max_size;
- stagestr[max_size - 1] = 0;
+ stagestr = stagestr.substr(0, max_size-1);
}
- size_t status = ota_fwrite(stagestr, to_write, 1, f);
+ size_t status = ota_fwrite(stagestr.c_str(), to_write, 1, f);
ota_fclose(f);
- free(stagestr);
if (status != to_write) {
return StringValue("");
}
@@ -1322,11 +1252,14 @@ Value* GetStageFn(const char* name, State* state, int argc, Expr* argv[]) {
return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %d", name, argc);
}
- char* filename;
- if (ReadArgs(state, argv, 1, &filename) < 0) return NULL;
+ std::vector<std::string> args;
+ if (!ReadArgs(state, 1, argv, &args)) {
+ return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
+ }
+ const std::string& filename = args[0];
char buffer[sizeof(((struct bootloader_message*)0)->stage)];
- FILE* f = ota_fopen(filename, "rb");
+ FILE* f = ota_fopen(filename.c_str(), "rb");
fseek(f, offsetof(struct bootloader_message, stage), SEEK_SET);
size_t status = ota_fread(buffer, sizeof(buffer), 1, f);
ota_fclose(f);
@@ -1343,21 +1276,25 @@ Value* WipeBlockDeviceFn(const char* name, State* state, int argc, Expr* argv[])
return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc);
}
- char* filename;
- char* len_str;
- if (ReadArgs(state, argv, 2, &filename, &len_str) < 0) return NULL;
+ 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[0];
+ const std::string& len_str = args[1];
size_t len;
- android::base::ParseUint(len_str, &len);
- int fd = ota_open(filename, O_WRONLY, 0644);
- int success = wipe_block_device(fd, len);
-
- free(filename);
- free(len_str);
+ if (!android::base::ParseUint(len_str.c_str(), &len)) {
+ return nullptr;
+ }
+ int fd = ota_open(filename.c_str(), O_WRONLY, 0644);
+ // The wipe_block_device function in ext4_utils returns 0 on success and 1
+ // for failure.
+ int status = wipe_block_device(fd, len);
ota_close(fd);
- return StringValue(success ? "t" : "");
+ return StringValue((status == 0) ? "t" : "");
}
Value* EnableRebootFn(const char* name, State* state, int argc, Expr* argv[]) {
@@ -1374,28 +1311,27 @@ Value* Tune2FsFn(const char* name, State* state, int argc, Expr* argv[]) {
return ErrorAbort(state, kArgsParsingFailure, "%s() expects args, got %d", name, argc);
}
- char** args = ReadVarArgs(state, argc, argv);
- if (args == NULL) {
+ std::vector<std::string> args;
+ if (!ReadArgs(state, argc, argv, &args)) {
return ErrorAbort(state, kArgsParsingFailure, "%s() could not read args", name);
}
- char** args2 = reinterpret_cast<char**>(malloc(sizeof(char*) * (argc+1)));
+ char* args2[argc+1];
// Tune2fs expects the program name as its args[0]
- args2[0] = strdup(name);
- for (int i = 0; i < argc; ++i) {
- args2[i + 1] = args[i];
+ args2[0] = const_cast<char*>(name);
+ if (args2[0] == nullptr) {
+ return nullptr;
}
- int result = tune2fs_main(argc + 1, args2);
for (int i = 0; i < argc; ++i) {
- free(args[i]);
+ args2[i + 1] = &args[i][0];
}
- free(args);
- free(args2[0]);
- free(args2);
+ // tune2fs changes the file system parameters on an ext2 file system; it
+ // returns 0 on success.
+ int result = tune2fs_main(argc + 1, args2);
+
if (result != 0) {
- return ErrorAbort(state, kTune2FsFailure, "%s() returned error code %d",
- name, result);
+ return ErrorAbort(state, kTune2FsFailure, "%s() returned error code %d", name, result);
}
return StringValue("t");
}
diff --git a/verifier.cpp b/verifier.cpp
index 82cdd3bc7..44098f70e 100644
--- a/verifier.cpp
+++ b/verifier.cpp
@@ -14,9 +14,11 @@
* limitations under the License.
*/
+#include "verifier.h"
+
#include <errno.h>
-#include <malloc.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include <algorithm>
@@ -31,9 +33,6 @@
#include "common.h"
#include "print_sha1.h"
#include "ui.h"
-#include "verifier.h"
-
-extern RecoveryUI* ui;
static constexpr size_t MiB = 1024 * 1024;