diff options
Diffstat (limited to 'updater/install.cpp')
-rw-r--r-- | updater/install.cpp | 1524 |
1 files changed, 632 insertions, 892 deletions
diff --git a/updater/install.cpp b/updater/install.cpp index 74feb56be..b9bc19e2c 100644 --- a/updater/install.cpp +++ b/updater/install.cpp @@ -14,59 +14,60 @@ * limitations under the License. */ +#include "updater/install.h" + #include <ctype.h> #include <errno.h> +#include <fcntl.h> +#include <ftw.h> +#include <inttypes.h> #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sys/capability.h> #include <sys/mount.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/wait.h> -#include <unistd.h> -#include <fcntl.h> -#include <time.h> -#include <selinux/selinux.h> -#include <ftw.h> -#include <sys/capability.h> #include <sys/xattr.h> -#include <linux/xattr.h> -#include <inttypes.h> +#include <time.h> +#include <unistd.h> +#include <utime.h> #include <memory> #include <string> #include <vector> #include <android-base/file.h> +#include <android-base/parsedouble.h> #include <android-base/parseint.h> -#include <android-base/strings.h> +#include <android-base/properties.h> #include <android-base/stringprintf.h> +#include <android-base/strings.h> +#include <cutils/android_reboot.h> +#include <ext4_utils/make_ext4fs.h> +#include <ext4_utils/wipe.h> +#include <openssl/sha.h> +#include <selinux/label.h> +#include <selinux/selinux.h> +#include <ziparchive/zip_archive.h> -#include "bootloader.h" #include "applypatch/applypatch.h" -#include "cutils/android_reboot.h" -#include "cutils/misc.h" -#include "cutils/properties.h" +#include "bootloader.h" #include "edify/expr.h" #include "error_code.h" -#include "minzip/DirUtil.h" -#include "mtdutils/mounts.h" -#include "mtdutils/mtdutils.h" -#include "openssl/sha.h" +#include "mounts.h" #include "ota_io.h" -#include "updater.h" -#include "install.h" +#include "otautil/DirUtil.h" +#include "otautil/ZipUtil.h" +#include "print_sha1.h" #include "tune2fs.h" - -#ifdef USE_EXT4 -#include "make_ext4fs.h" -#include "wipe.h" -#endif +#include "updater/updater.h" // 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. @@ -84,8 +85,7 @@ static void uiPrint(State* state, const std::string& buffer) { fprintf(stderr, "%s", buffer.c_str()); } -__attribute__((__format__(printf, 2, 3))) __nonnull((2)) -void uiPrintf(State* state, const char* format, ...) { +void uiPrintf(State* _Nonnull state, const char* _Nonnull format, ...) { std::string error_msg; va_list ap; @@ -96,76 +96,84 @@ void uiPrintf(State* state, const char* format, ...) { uiPrint(state, error_msg); } -// Take a sha-1 digest and return it as a newly-allocated hex string. -char* PrintSha1(const uint8_t* digest) { - char* buffer = reinterpret_cast<char*>(malloc(SHA_DIGEST_LENGTH*2 + 1)); - const char* alphabet = "0123456789abcdef"; - size_t i; - for (i = 0; i < SHA_DIGEST_LENGTH; ++i) { - buffer[i*2] = alphabet[(digest[i] >> 4) & 0xf]; - buffer[i*2+1] = alphabet[digest[i] & 0xf]; - } - buffer[i*2] = '\0'; - return buffer; +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 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 true; } // mount(fs_type, partition_type, location, mount_point) +// mount(fs_type, partition_type, location, mount_point, mount_options) // -// fs_type="yaffs2" partition_type="MTD" location=partition // fs_type="ext4" partition_type="EMMC" location=device Value* MountFn(const char* name, State* state, int argc, Expr* argv[]) { - char* result = NULL; 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); @@ -173,109 +181,71 @@ Value* MountFn(const char* name, State* state, int argc, Expr* argv[]) { } } - if (strcmp(partition_type, "MTD") == 0) { - mtd_scan_partitions(); - const MtdPartition* mtd; - mtd = mtd_find_partition_by_name(location); - if (mtd == NULL) { - uiPrintf(state, "%s: no mtd partition named \"%s\"\n", - name, location); - result = strdup(""); - goto done; - } - if (mtd_mount_partition(mtd, mount_point, fs_type, 0 /* rw */) != 0) { - uiPrintf(state, "mtd mount of %s failed: %s\n", - location, strerror(errno)); - result = strdup(""); - goto done; - } - result = mount_point; - } else { - if (mount(location, mount_point, fs_type, - MS_NOATIME | MS_NODEV | MS_NODIRATIME, - has_mount_options ? mount_options : "") < 0) { - uiPrintf(state, "%s: failed to mount %s at %s: %s\n", - name, location, mount_point, strerror(errno)); - result = strdup(""); - } else { - result = mount_point; - } + 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.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 = NULL; 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(); - { - const 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 = NULL; 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(); - { - const 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[]) { @@ -292,167 +262,130 @@ 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="yaffs2" partition_type="MTD" location=partition fs_size=<bytes> mount_point=<location> // fs_type="ext4" partition_type="EMMC" location=device fs_size=<bytes> mount_point=<location> // fs_type="f2fs" partition_type="EMMC" location=device fs_size=<bytes> mount_point=<location> // if fs_size == 0, then make fs uses the entire partition. // 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 = NULL; 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 (location.empty()) { + return ErrorAbort(state, kArgsParsingFailure, "location 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 (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(partition_type, "MTD") == 0) { - mtd_scan_partitions(); - const MtdPartition* mtd = mtd_find_partition_by_name(location); - if (mtd == NULL) { - printf("%s: no mtd partition named \"%s\"", - name, location); - result = strdup(""); - goto done; - } - MtdWriteContext* ctx = mtd_write_partition(mtd); - if (ctx == NULL) { - printf("%s: can't write \"%s\"", name, location); - result = strdup(""); - goto done; - } - if (mtd_erase_blocks(ctx, -1) == -1) { - mtd_write_close(ctx); - printf("%s: failed to erase \"%s\"", name, location); - result = strdup(""); - goto done; - } - if (mtd_write_close(ctx) != 0) { - printf("%s: failed to close \"%s\"", name, location); - result = strdup(""); - goto done; - } - result = location; -#ifdef USE_EXT4 - } else 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; -#endif + 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 = NULL; 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[]) { - char** paths = reinterpret_cast<char**>(malloc(argc * sizeof(char*))); + std::vector<std::string> paths(argc); for (int i = 0; i < argc; ++i) { - paths[i] = Evaluate(state, argv[i]); - if (paths[i] == NULL) { - for (int j = 0; j < i; ++j) { - free(paths[j]); - } - free(paths); - return NULL; + if (!Evaluate(state, argv[i], &paths[i])) { + return nullptr; } } @@ -460,15 +393,12 @@ Value* DeleteFn(const char* name, State* state, int argc, Expr* argv[]) { int success = 0; for (int i = 0; i < argc; ++i) { - if ((recursive ? dirUnlinkHierarchy(paths[i]) : unlink(paths[i])) == 0) + if ((recursive ? dirUnlinkHierarchy(paths[i].c_str()) : unlink(paths[i].c_str())) == 0) { ++success; - free(paths[i]); + } } - free(paths); - char buffer[10]; - sprintf(buffer, "%d", success); - return StringValue(strdup(buffer)); + return StringValue(std::to_string(success)); } @@ -476,20 +406,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); } @@ -497,12 +435,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); @@ -510,185 +454,153 @@ Value* SetProgressFn(const char* name, State* state, int argc, Expr* argv[]) { return StringValue(frac_str); } -// package_extract_dir(package_path, destination_path) -Value* PackageExtractDirFn(const char* name, State* state, - int argc, Expr* argv[]) { - if (argc != 2) { - return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc); - } - char* zip_path; - char* dest_path; - if (ReadArgs(state, argv, 2, &zip_path, &dest_path) < 0) return NULL; +// package_extract_dir(package_dir, dest_dir) +// Extracts all files from the package underneath package_dir and writes them to the +// corresponding tree beneath dest_dir. Any existing files are overwritten. +// Example: package_extract_dir("system", "/system") +// +// Note: package_dir needs to be a relative path; dest_dir needs to be an absolute path. +Value* PackageExtractDirFn(const char* name, State* state, int argc, Expr* argv[]) { + if (argc != 2) { + return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc); + } - ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip; + std::vector<std::string> args; + if (!ReadArgs(state, 2, argv, &args)) { + return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name); + } + const std::string& zip_path = args[0]; + const std::string& dest_path = args[1]; - // To create a consistent system image, never use the clock for timestamps. - struct utimbuf timestamp = { 1217592000, 1217592000 }; // 8/1/2008 default + ZipArchiveHandle za = static_cast<UpdaterInfo*>(state->cookie)->package_zip; - bool success = mzExtractRecursive(za, zip_path, dest_path, - ×tamp, - NULL, NULL, sehandle); - free(zip_path); - free(dest_path); - return StringValue(strdup(success ? "t" : "")); -} + // To create a consistent system image, never use the clock for timestamps. + constexpr struct utimbuf timestamp = { 1217592000, 1217592000 }; // 8/1/2008 default + bool success = ExtractPackageRecursive(za, zip_path, dest_path, ×tamp, sehandle); -// package_extract_file(package_path, destination_path) -// or -// package_extract_file(package_path) -// to return the entire contents of the file as the result of this -// function (the char* returned is actually a FileContents*). -Value* PackageExtractFileFn(const char* name, State* state, - int argc, Expr* argv[]) { - if (argc < 1 || argc > 2) { - return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 or 2 args, got %d", - name, argc); - } - bool success = false; + return StringValue(success ? "t" : ""); +} - if (argc == 2) { - // The two-argument version extracts to a file. +// package_extract_file(package_file[, dest_file]) +// Extracts a single package_file from the update package and writes it to dest_file, +// overwriting existing files if necessary. Without the dest_file argument, returns the +// contents of the package file as a binary blob. +Value* PackageExtractFileFn(const char* name, State* state, int argc, Expr* argv[]) { + if (argc < 1 || argc > 2) { + return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 or 2 args, got %d", name, argc); + } - ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip; + if (argc == 2) { + // The two-argument version extracts to a file. - 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]; - const ZipEntry* entry = mzFindZipEntry(za, zip_path); - if (entry == NULL) { - printf("%s: no %s in package\n", name, zip_path); - goto done2; - } + ZipArchiveHandle za = static_cast<UpdaterInfo*>(state->cookie)->package_zip; + ZipString zip_string_path(zip_path.c_str()); + ZipEntry entry; + if (FindEntry(za, zip_string_path, &entry) != 0) { + printf("%s: no %s in package\n", name, zip_path.c_str()); + return StringValue(""); + } - { - int fd = TEMP_FAILURE_RETRY(ota_open(dest_path, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, - 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 = mzExtractZipEntryToFile(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(""); + } - done2: - free(zip_path); - free(dest_path); - return StringValue(strdup(success ? "t" : "")); - } else { - // The one-argument version returns the contents of the file - // as the result. + bool success = true; + int32_t ret = ExtractEntryToFile(za, &entry, fd); + if (ret != 0) { + printf("%s: Failed to extract entry \"%s\" (%u bytes) to \"%s\": %s\n", name, + zip_path.c_str(), entry.uncompressed_length, dest_path.c_str(), ErrorCodeString(ret)); + success = false; + } + if (ota_fsync(fd) == -1) { + printf("fsync of \"%s\" failed: %s\n", dest_path.c_str(), strerror(errno)); + success = false; + } + if (ota_close(fd) == -1) { + printf("close of \"%s\" failed: %s\n", dest_path.c_str(), strerror(errno)); + success = false; + } - char* zip_path; - if (ReadArgs(state, argv, 1, &zip_path) < 0) return NULL; + return StringValue(success ? "t" : ""); + } else { + // The one-argument version returns the contents of the file as the result. - Value* v = reinterpret_cast<Value*>(malloc(sizeof(Value))); - v->type = VAL_BLOB; - v->size = -1; - v->data = NULL; - - ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip; - const ZipEntry* entry = mzFindZipEntry(za, zip_path); - if (entry == NULL) { - printf("%s: no %s in package\n", name, zip_path); - goto done1; - } + 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]; - v->size = mzGetZipEntryUncompLen(entry); - v->data = reinterpret_cast<char*>(malloc(v->size)); - if (v->data == NULL) { - printf("%s: failed to allocate %ld bytes for %s\n", - name, (long)v->size, zip_path); - goto done1; - } + ZipArchiveHandle za = static_cast<UpdaterInfo*>(state->cookie)->package_zip; + ZipString zip_string_path(zip_path.c_str()); + ZipEntry entry; + if (FindEntry(za, zip_string_path, &entry) != 0) { + return ErrorAbort(state, kPackageExtractFileFailure, "%s(): no %s in package", name, + zip_path.c_str()); + } - success = mzExtractZipEntryToBuffer(za, entry, - (unsigned char *)v->data); + std::string buffer; + buffer.resize(entry.uncompressed_length); - done1: - free(zip_path); - if (!success) { - free(v->data); - v->data = NULL; - v->size = -1; - } - return v; + int32_t ret = ExtractToMemory(za, &entry, reinterpret_cast<uint8_t*>(&buffer[0]), buffer.size()); + if (ret != 0) { + return ErrorAbort(state, kPackageExtractFileFailure, + "%s: Failed to extract entry \"%s\" (%zu bytes) to memory: %s", name, + zip_path.c_str(), buffer.size(), ErrorCodeString(ret)); } -} -// 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; - } - } - return 0; + return new Value(VAL_BLOB, buffer); + } } -// symlink target src1 src2 ... -// unlinks any previously existing src1, src2, etc before creating symlinks. +// symlink(target, [src1, src2, ...]) +// Creates all sources as symlinks to target. It unlinks any previously existing src1, src2, etc +// before creating symlinks. Value* SymlinkFn(const char* name, State* state, int argc, Expr* argv[]) { if (argc == 0) { return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1+ args, got %d", name, argc); } - char* target; - target = Evaluate(state, argv[0]); - if (target == NULL) return NULL; + std::string target; + if (!Evaluate(state, argv[0], &target)) { + return nullptr; + } - char** srcs = ReadVarArgs(state, argc-1, argv+1); - if (srcs == NULL) { - free(target); - 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) { - if (errno != ENOENT) { - printf("%s: failed to remove %s: %s\n", - name, srcs[i], strerror(errno)); - ++bad; - } - } - if (make_parents(srcs[i])) { + size_t bad = 0; + for (const auto& src : srcs) { + if (unlink(src.c_str()) == -1 && errno != ENOENT) { + printf("%s: failed to remove %s: %s\n", name, src.c_str(), strerror(errno)); + ++bad; + } else if (!make_parents(src)) { printf("%s: failed to symlink %s to %s: making parents failed\n", - name, srcs[i], target); + name, src.c_str(), target.c_str()); ++bad; - } - if (symlink(target, srcs[i]) < 0) { + } else if (symlink(target.c_str(), src.c_str()) == -1) { printf("%s: failed to symlink %s to %s: %s\n", - name, srcs[i], target, strerror(errno)); + name, src.c_str(), target.c_str(), strerror(errno)); ++bad; } - free(srcs[i]); } - free(srcs); - if (bad) { - return ErrorAbort(state, kSymlinkFailure, "%s: some symlinks failed", name); + if (bad != 0) { + return ErrorAbort(state, kSymlinkFailure, "%s: Failed to create %zu symlink(s)", name, bad); } - return StringValue(strdup("")); + return StringValue("t"); } struct perm_parsed_args { @@ -703,12 +615,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; @@ -717,84 +630,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"); @@ -903,279 +816,142 @@ 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) { return ErrorAbort(state, kSetMetadataFailure, "%s: some changes failed", name); } - return StringValue(strdup("")); + return StringValue(""); } Value* GetPropFn(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* key = Evaluate(state, argv[0]); - if (key == NULL) return NULL; - - char value[PROPERTY_VALUE_MAX]; - property_get(key, value, ""); - free(key); + std::string key; + if (!Evaluate(state, argv[0], &key)) { + return nullptr; + } + std::string value = android::base::GetProperty(key, ""); - return StringValue(strdup(value)); + return StringValue(value); } // 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 %lld bytes", name, - (long long)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 = 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)) { - ErrorAbort(state, kFreadFailure, "%s: failed to read %lld bytes from %s", - name, (long long)st.st_size+1, filename); - fclose(f); - goto done; + 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.c_str()); + ota_fclose(f); + return nullptr; } - buffer[st.st_size] = '\0'; - fclose(f); + 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; - - // 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 (key != str) continue; - if (result == NULL) result = strdup(""); - - done: - free(filename); - free(key); - free(buffer); - return StringValue(result); -} - -// write_raw_image(filename_or_blob, partition) -Value* WriteRawImageFn(const char* name, State* state, int argc, Expr* argv[]) { - char* result = NULL; - - Value* partition_value; - Value* contents; - if (ReadValueArgs(state, argv, 2, &contents, &partition_value) < 0) { - return NULL; - } - - char* partition = NULL; - if (partition_value->type != VAL_STRING) { - ErrorAbort(state, kArgsParsingFailure, "partition argument to %s must be string", name); - goto done; - } - partition = partition_value->data; - if (strlen(partition) == 0) { - ErrorAbort(state, kArgsParsingFailure, "partition argument to %s can't be empty", name); - goto done; - } - if (contents->type == VAL_STRING && strlen((char*) contents->data) == 0) { - ErrorAbort(state, kArgsParsingFailure, "file argument to %s can't be empty", name); - goto done; - } - - mtd_scan_partitions(); - const MtdPartition* mtd; - mtd = mtd_find_partition_by_name(partition); - if (mtd == NULL) { - printf("%s: no mtd partition named \"%s\"\n", name, partition); - result = strdup(""); - goto done; - } - - MtdWriteContext* ctx; - ctx = mtd_write_partition(mtd); - if (ctx == NULL) { - printf("%s: can't write mtd partition \"%s\"\n", - name, partition); - result = strdup(""); - goto done; - } - - bool success; - - if (contents->type == VAL_STRING) { - // we're given a filename as the contents - char* filename = contents->data; - FILE* f = ota_fopen(filename, "rb"); - if (f == NULL) { - printf("%s: can't open %s: %s\n", name, filename, strerror(errno)); - result = strdup(""); - goto done; - } - - success = true; - char* buffer = reinterpret_cast<char*>(malloc(BUFSIZ)); - int read; - while (success && (read = ota_fread(buffer, 1, BUFSIZ, f)) > 0) { - int wrote = mtd_write_data(ctx, buffer, read); - success = success && (wrote == read); - } - free(buffer); - ota_fclose(f); - } else { - // we're given a blob as the contents - ssize_t wrote = mtd_write_data(ctx, contents->data, contents->size); - success = (wrote == contents->size); - } - if (!success) { - printf("mtd_write_data to %s failed: %s\n", - partition, strerror(errno)); - } - - if (mtd_erase_blocks(ctx, -1) == -1) { - printf("%s: error erasing blocks of %s\n", name, partition); - } - if (mtd_write_close(ctx) != 0) { - printf("%s: error closing write of %s\n", name, partition); + return StringValue(android::base::Trim(line.substr(equal_pos + 1))); } - printf("%s %s partition\n", - success ? "wrote" : "failed to write", partition); - - result = success ? partition : strdup(""); - -done: - if (result != partition) FreeValue(partition_value); - FreeValue(contents); - 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(strdup(CacheSizeCheck(bytes) ? "" : "t")); + return StringValue(CacheSizeCheck(bytes) ? "" : "t"); } // apply_patch(file, size, init_sha1, tgt_sha1, patch) @@ -1183,66 +959,53 @@ 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*, decltype(&free)> arg_values(ReadValueVarArgs(state, argc-4, argv+4), - free); - 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, decltype(&FreeValue)>> patch_shas; - std::vector<std::unique_ptr<Value, decltype(&FreeValue)>> 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], FreeValue); - patches.emplace_back(arg_values.get()[i+1], FreeValue); - } 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<char*> patch_sha_str; - std::vector<Value*> patch_ptrs; + std::vector<std::string> patch_sha_str; + 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, - patchcount, patch_sha_str.data(), 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(strdup(result == 0 ? "t" : "")); + return StringValue(result == 0 ? "t" : ""); } // apply_patch_check(file, [sha1_1, ...]) @@ -1253,43 +1016,32 @@ Value* ApplyPatchCheckFn(const char* name, State* state, 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]; - int patchcount = argc-1; - char** sha1s = ReadVarArgs(state, argc-1, argv+1); - - int result = applypatch_check(filename, patchcount, sha1s); - - int i; - for (i = 0; i < patchcount; ++i) { - free(sha1s[i]); + std::vector<std::string> sha1s; + if (!ReadArgs(state, argc - 1, argv + 1, &sha1s)) { + return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name); } - free(sha1s); + int result = applypatch_check(filename.c_str(), sha1s); - return StringValue(strdup(result == 0 ? "t" : "")); + return StringValue(result == 0 ? "t" : ""); } // 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::string buffer; - for (int i = 0; i < argc; ++i) { - buffer += args[i]; - free(args[i]); + std::vector<std::string> args; + if (!ReadArgs(state, argc, argv, &args)) { + return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name); } - free(args); - buffer += "\n"; + std::string buffer = android::base::Join(args, "") + "\n"; uiPrint(state, buffer); - return StringValue(strdup(buffer.c_str())); + return StringValue(buffer); } Value* WipeCacheFn(const char* name, State* state, int argc, Expr* argv[]) { @@ -1297,21 +1049,24 @@ Value* WipeCacheFn(const char* name, State* state, int argc, Expr* argv[]) { return ErrorAbort(state, kArgsParsingFailure, "%s() expects no args, got %d", name, argc); } fprintf(((UpdaterInfo*)(state->cookie))->cmd_pipe, "wipe_cache\n"); - return StringValue(strdup("t")); + return StringValue("t"); } 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); @@ -1333,17 +1088,7 @@ 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); - - char buffer[20]; - sprintf(buffer, "%d", status); - - return StringValue(strdup(buffer)); + return StringValue(android::base::StringPrintf("%d", status)); } // sha1_check(data) @@ -1359,45 +1104,36 @@ 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*, decltype(&free)> arg_values(ReadValueVarArgs(state, argc, argv), free); - if (arg_values == nullptr) { + std::vector<std::unique_ptr<Value>> args; + if (!ReadValueArgs(state, argc, argv, &args)) { return nullptr; } - std::vector<std::unique_ptr<Value, decltype(&FreeValue)>> args; - for (int i = 0; i < argc; ++i) { - args.emplace_back(arg_values.get()[i], FreeValue); - } - if (args[0]->size < 0) { - return StringValue(strdup("")); + if (args[0]->type == VAL_INVALID) { + return StringValue(""); } uint8_t digest[SHA_DIGEST_LENGTH]; - SHA1(reinterpret_cast<uint8_t*>(args[0]->data), args[0]->size, digest); + SHA1(reinterpret_cast<const uint8_t*>(args[0]->data.c_str()), args[0]->data.size(), digest); if (argc == 1) { - return StringValue(PrintSha1(digest)); + return StringValue(print_sha1(digest)); } - int i; - uint8_t arg_digest[SHA_DIGEST_LENGTH]; - for (i = 1; i < argc; ++i) { + for (int i = 1; i < argc; ++i) { + uint8_t arg_digest[SHA_DIGEST_LENGTH]; if (args[i]->type != VAL_STRING) { - printf("%s(): arg %d is not a string; skipping", - name, i); - } else if (ParseSha1(args[i]->data, arg_digest) != 0) { + printf("%s(): arg %d is not a string; skipping", name, i); + } else if (ParseSha1(args[i]->data.c_str(), arg_digest) != 0) { // Warn about bad args and skip them. - printf("%s(): error parsing \"%s\" as sha-1; skipping", - name, args[i]->data); + printf("%s(): error parsing \"%s\" as sha-1; skipping", name, args[i]->data.c_str()); } else if (memcmp(digest, arg_digest, SHA_DIGEST_LENGTH) == 0) { - break; + // Found a match. + return args[i].release(); } } - if (i >= argc) { - // Didn't match any of the hex strings; return false. - return StringValue(strdup("")); - } - // Found a match. - return args[i].release(); + + // Didn't match any of the hex strings; return false. + return StringValue(""); } // Read a local file and return its contents (the Value* returned @@ -1406,26 +1142,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; - Value* v = static_cast<Value*>(malloc(sizeof(Value))); - if (v == nullptr) { - 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); } - v->type = VAL_BLOB; - v->size = -1; - v->data = nullptr; + const std::string& filename = args[0]; + + Value* v = new Value(VAL_INVALID, ""); FileContents fc; - if (LoadFileContents(filename, &fc) == 0) { - v->data = static_cast<char*>(malloc(fc.data.size())); - if (v->data != nullptr) { - memcpy(v->data, fc.data.data(), fc.data.size()); - v->size = fc.data.size(); - } + 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; } @@ -1433,25 +1163,27 @@ Value* ReadFileFn(const char* name, State* state, int argc, Expr* argv[]) { // Writes 'value' to 'filename'. // Example: write_value("960000", "/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq") Value* WriteValueFn(const char* name, State* state, int argc, Expr* argv[]) { - if (argc != 2) { - return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc); - } - - char* value; - char* filename; - if (ReadArgs(state, argv, 2, &value, &filename) < 0) { - return ErrorAbort(state, kArgsParsingFailure, "%s(): Failed to parse the argument(s)", - name); - } - - bool ret = android::base::WriteStringToFile(value, filename); - if (!ret) { - printf("%s: Failed to write to \"%s\": %s\n", name, filename, strerror(errno)); - } - - free(value); - free(filename); - return StringValue(strdup(ret ? "t" : "")); + if (argc != 2) { + return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc); + } + + std::vector<std::string> args; + if (!ReadArgs(state, 2, argv, &args)) { + return ErrorAbort(state, kArgsParsingFailure, "%s(): Failed to parse the argument(s)", name); + } + + const std::string& filename = args[1]; + if (filename.empty()) { + return ErrorAbort(state, kArgsParsingFailure, "%s(): Filename cannot be empty", name); + } + + const std::string& value = args[0]; + if (!android::base::WriteStringToFile(value, filename)) { + printf("%s: Failed to write to \"%s\": %s\n", name, filename.c_str(), strerror(errno)); + return StringValue(""); + } else { + return StringValue("t"); + } } // Immediately reboot the device. Recovery is not finished normally, @@ -1464,35 +1196,35 @@ Value* WriteValueFn(const char* name, State* state, int argc, Expr* argv[]) { // partition, or "" (empty string) to boot from the regular boot // partition. Value* RebootNowFn(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* filename; - char* property; - if (ReadArgs(state, argv, 2, &filename, &property) < 0) return NULL; - - char buffer[80]; - - // zero out the 'command' field of the bootloader message. - memset(buffer, 0, sizeof(((struct bootloader_message*)0)->command)); - FILE* f = fopen(filename, "r+b"); - fseek(f, offsetof(struct bootloader_message, command), SEEK_SET); - ota_fwrite(buffer, sizeof(((struct bootloader_message*)0)->command), 1, f); - fclose(f); - free(filename); - - strcpy(buffer, "reboot,"); - if (property != NULL) { - strncat(buffer, property, sizeof(buffer)-10); - } - - property_set(ANDROID_RB_PROPERTY, buffer); - - sleep(5); - free(property); - ErrorAbort(state, kRebootFailure, "%s() failed to reboot", name); - return NULL; + if (argc != 2) { + return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc); + } + + std::vector<std::string> args; + if (!ReadArgs(state, 2, argv, &args)) { + return ErrorAbort(state, kArgsParsingFailure, "%s(): Failed to parse the argument(s)", name); + } + const std::string& filename = args[0]; + const std::string& property = args[1]; + + // Zero out the 'command' field of the bootloader message. Leave the rest intact. + bootloader_message boot; + std::string err; + if (!read_bootloader_message_from(&boot, filename, &err)) { + printf("%s(): Failed to read from \"%s\": %s", name, filename.c_str(), err.c_str()); + return StringValue(""); + } + memset(boot.command, 0, sizeof(boot.command)); + if (!write_bootloader_message_to(boot, filename, &err)) { + printf("%s(): Failed to write to \"%s\": %s", name, filename.c_str(), err.c_str()); + return StringValue(""); + } + + const std::string reboot_cmd = "reboot," + property; + android::base::SetProperty(ANDROID_RB_PROPERTY, reboot_cmd); + + sleep(5); + return ErrorAbort(state, kRebootFailure, "%s() failed to reboot", name); } // Store a string value somewhere that future invocations of recovery @@ -1506,51 +1238,57 @@ Value* RebootNowFn(const char* name, State* state, int argc, Expr* argv[]) { // second argument is the string to store; it should not exceed 31 // bytes. Value* SetStageFn(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* filename; - char* stagestr; - if (ReadArgs(state, argv, 2, &filename, &stagestr) < 0) return NULL; - - // 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 = fopen(filename, "r+b"); - fseek(f, offsetof(struct bootloader_message, stage), SEEK_SET); - int to_write = strlen(stagestr)+1; - int max_size = sizeof(((struct bootloader_message*)0)->stage); - if (to_write > max_size) { - to_write = max_size; - stagestr[max_size-1] = 0; - } - ota_fwrite(stagestr, to_write, 1, f); - fclose(f); - - free(stagestr); - return StringValue(filename); + if (argc != 2) { + return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc); + } + + std::vector<std::string> args; + if (!ReadArgs(state, 2, argv, &args)) { + return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name); + } + const std::string& filename = args[0]; + const 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. + bootloader_message boot; + std::string err; + if (!read_bootloader_message_from(&boot, filename, &err)) { + printf("%s(): Failed to read from \"%s\": %s", name, filename.c_str(), err.c_str()); + return StringValue(""); + } + strlcpy(boot.stage, stagestr.c_str(), sizeof(boot.stage)); + if (!write_bootloader_message_to(boot, filename, &err)) { + printf("%s(): Failed to write to \"%s\": %s", name, filename.c_str(), err.c_str()); + return StringValue(""); + } + + return StringValue(filename); } // Return the value most recently saved with SetStageFn. The argument // is the block device for the misc partition. Value* GetStageFn(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; - - char buffer[sizeof(((struct bootloader_message*)0)->stage)]; - FILE* f = fopen(filename, "rb"); - fseek(f, offsetof(struct bootloader_message, stage), SEEK_SET); - ota_fread(buffer, sizeof(buffer), 1, f); - fclose(f); - buffer[sizeof(buffer)-1] = '\0'; - - return StringValue(strdup(buffer)); + if (argc != 1) { + return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %d", name, argc); + } + + 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]; + + bootloader_message boot; + std::string err; + if (!read_bootloader_message_from(&boot, filename, &err)) { + printf("%s(): Failed to read from \"%s\": %s", name, filename.c_str(), err.c_str()); + return StringValue(""); + } + + return StringValue(boot.stage); } Value* WipeBlockDeviceFn(const char* name, State* state, int argc, Expr* argv[]) { @@ -1558,21 +1296,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(strdup(success ? "t" : "")); + return StringValue((status == 0) ? "t" : ""); } Value* EnableRebootFn(const char* name, State* state, int argc, Expr* argv[]) { @@ -1581,7 +1323,7 @@ Value* EnableRebootFn(const char* name, State* state, int argc, Expr* argv[]) { } UpdaterInfo* ui = (UpdaterInfo*)(state->cookie); fprintf(ui->cmd_pipe, "enable_reboot\n"); - return StringValue(strdup("t")); + return StringValue("t"); } Value* Tune2FsFn(const char* name, State* state, int argc, Expr* argv[]) { @@ -1589,30 +1331,29 @@ 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(strdup("t")); + return StringValue("t"); } void RegisterInstallFunctions() { @@ -1642,7 +1383,6 @@ void RegisterInstallFunctions() { RegisterFunction("getprop", GetPropFn); RegisterFunction("file_getprop", FileGetPropFn); - RegisterFunction("write_raw_image", WriteRawImageFn); RegisterFunction("apply_patch", ApplyPatchFn); RegisterFunction("apply_patch_check", ApplyPatchCheckFn); |