From ff6df890a2a01bf3bf56d3f430b17a5ef69055cf Mon Sep 17 00:00:00 2001 From: Jed Estep Date: Tue, 15 Dec 2015 16:04:53 -0800 Subject: Control fault injection with config files instead of build flags Bug: 27724259 Change-Id: I65bdefed10b3fb85fcb9e1147eaf0687d7d438f4 --- otafault/Android.mk | 43 +++++-------- otafault/config.cpp | 70 +++++++++++++++++++++ otafault/config.h | 74 ++++++++++++++++++++++ otafault/ota_io.cpp | 174 +++++++++++++++++++++------------------------------- otafault/ota_io.h | 4 ++ otafault/test.cpp | 6 +- 6 files changed, 237 insertions(+), 134 deletions(-) create mode 100644 otafault/config.cpp create mode 100644 otafault/config.h (limited to 'otafault') diff --git a/otafault/Android.mk b/otafault/Android.mk index 75617a146..7468de6c4 100644 --- a/otafault/Android.mk +++ b/otafault/Android.mk @@ -1,10 +1,10 @@ -# Copyright 2015 The ANdroid Open Source Project +# Copyright 2015 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 +# 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, @@ -14,45 +14,30 @@ LOCAL_PATH := $(call my-dir) -empty := -space := $(empty) $(empty) -comma := , - -ifneq ($(TARGET_INJECT_FAULTS),) -TARGET_INJECT_FAULTS := $(subst $(comma),$(space),$(strip $(TARGET_INJECT_FAULTS))) -endif - include $(CLEAR_VARS) -LOCAL_SRC_FILES := ota_io.cpp +otafault_static_libs := \ + libminzip \ + libz \ + libselinux \ + +LOCAL_SRC_FILES := config.cpp ota_io.cpp LOCAL_MODULE_TAGS := eng LOCAL_MODULE := libotafault LOCAL_CLANG := true - -ifneq ($(TARGET_INJECT_FAULTS),) -$(foreach ft,$(TARGET_INJECT_FAULTS),\ - $(eval LOCAL_CFLAGS += -DTARGET_$(ft)_FAULT=$(TARGET_$(ft)_FAULT_FILE))) -LOCAL_CFLAGS += -Wno-unused-parameter -LOCAL_CFLAGS += -DTARGET_INJECT_FAULTS -endif - -LOCAL_STATIC_LIBRARIES := libc +LOCAL_C_INCLUDES := bootable/recovery +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH) +LOCAL_WHOLE_STATIC_LIBRARIES := $(otafault_static_libs) include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) -LOCAL_SRC_FILES := ota_io.cpp test.cpp +LOCAL_SRC_FILES := config.cpp ota_io.cpp test.cpp LOCAL_MODULE_TAGS := tests LOCAL_MODULE := otafault_test -LOCAL_STATIC_LIBRARIES := libc +LOCAL_STATIC_LIBRARIES := $(otafault_static_libs) +LOCAL_C_INCLUDES := bootable/recovery LOCAL_FORCE_STATIC_EXECUTABLE := true -LOCAL_CFLAGS += -Wno-unused-parameter -Wno-writable-strings - -ifneq ($(TARGET_INJECT_FAULTS),) -$(foreach ft,$(TARGET_INJECT_FAULTS),\ - $(eval LOCAL_CFLAGS += -DTARGET_$(ft)_FAULT=$(TARGET_$(ft)_FAULT_FILE))) -LOCAL_CFLAGS += -DTARGET_INJECT_FAULTS -endif include $(BUILD_EXECUTABLE) diff --git a/otafault/config.cpp b/otafault/config.cpp new file mode 100644 index 000000000..aa4b4d5ce --- /dev/null +++ b/otafault/config.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2015 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 +#include + +#include +#include + +#include "minzip/Zip.h" +#include "config.h" +#include "ota_io.h" + +#define OTAIO_MAX_FNAME_SIZE 128 + +static ZipArchive* archive; +static std::map should_inject_cache; + +static const char* get_type_path(const char* io_type) { + char* path = (char*)calloc(strlen(io_type) + strlen(OTAIO_BASE_DIR) + 2, sizeof(char)); + sprintf(path, "%s/%s", OTAIO_BASE_DIR, io_type); + return path; +} + +void ota_io_init(ZipArchive* za) { + archive = za; + ota_set_fault_files(); +} + +bool should_fault_inject(const char* io_type) { + // archive will be NULL if we used an entry point other + // than updater/updater.cpp:main + if (archive == NULL) { + return false; + } + if (should_inject_cache.find(io_type) != should_inject_cache.end()) { + return should_inject_cache[io_type]; + } + const char* type_path = get_type_path(io_type); + const ZipEntry* entry = mzFindZipEntry(archive, type_path); + should_inject_cache[type_path] = entry != nullptr; + free((void*)type_path); + return entry != NULL; +} + +bool should_hit_cache() { + return should_fault_inject(OTAIO_CACHE); +} + +std::string fault_fname(const char* io_type) { + const char* type_path = get_type_path(io_type); + char* fname = (char*) calloc(OTAIO_MAX_FNAME_SIZE, sizeof(char)); + const ZipEntry* entry = mzFindZipEntry(archive, type_path); + mzReadZipEntry(archive, entry, fname, OTAIO_MAX_FNAME_SIZE); + free((void*)type_path); + return std::string(fname); +} diff --git a/otafault/config.h b/otafault/config.h new file mode 100644 index 000000000..4430be3fb --- /dev/null +++ b/otafault/config.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2015 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. + */ + +/* + * Read configuration files in the OTA package to determine which files, if any, will trigger errors. + * + * OTA packages can be modified to trigger errors by adding a top-level + * directory called .libotafault, which may optionally contain up to three + * files called READ, WRITE, and FSYNC. Each one of these optional files + * contains the name of a single file on the device disk which will cause + * an IO error on the first call of the appropriate I/O action to that file. + * + * Example: + * ota.zip + * + * .libotafault + * WRITE + * + * If the contents of the file WRITE were /system/build.prop, the first write + * action to /system/build.prop would fail with EIO. Note that READ and + * FSYNC files are absent, so these actions will not cause an error. + */ + +#ifndef _UPDATER_OTA_IO_CFG_H_ +#define _UPDATER_OTA_IO_CFG_H_ + +#include + +#include + +#include "minzip/Zip.h" + +#define OTAIO_BASE_DIR ".libotafault" +#define OTAIO_READ "READ" +#define OTAIO_WRITE "WRITE" +#define OTAIO_FSYNC "FSYNC" +#define OTAIO_CACHE "CACHE" + +/* + * Initialize libotafault by providing a reference to the OTA package. + */ +void ota_io_init(ZipArchive* za); + +/* + * Return true if a config file is present for the given IO type. + */ +bool should_fault_inject(const char* io_type); + +/* + * Return true if an EIO should occur on the next hit to /cache/saved.file + * instead of the next hit to the specified file. + */ +bool should_hit_cache(); + +/* + * Return the name of the file that should cause an error for the + * given IO type. + */ +std::string fault_fname(const char* io_type); + +#endif diff --git a/otafault/ota_io.cpp b/otafault/ota_io.cpp index 9434ebea3..04458537b 100644 --- a/otafault/ota_io.cpp +++ b/otafault/ota_io.cpp @@ -14,9 +14,7 @@ * limitations under the License. */ -#if defined (TARGET_INJECT_FAULTS) #include -#endif #include #include @@ -24,185 +22,155 @@ #include #include +#include "config.h" #include "ota_io.h" -#if defined (TARGET_INJECT_FAULTS) -static std::map FilenameCache; -static std::string FaultFileName = -#if defined (TARGET_READ_FAULT) - TARGET_READ_FAULT; -#elif defined (TARGET_WRITE_FAULT) - TARGET_WRITE_FAULT; -#elif defined (TARGET_FSYNC_FAULT) - TARGET_FSYNC_FAULT; -#endif // defined (TARGET_READ_FAULT) -#endif // defined (TARGET_INJECT_FAULTS) +static std::map filename_cache; +static std::string read_fault_file_name = ""; +static std::string write_fault_file_name = ""; +static std::string fsync_fault_file_name = ""; + +static bool get_hit_file(const char* cached_path, std::string ffn) { + return should_hit_cache() + ? !strncmp(cached_path, OTAIO_CACHE_FNAME, strlen(cached_path)) + : !strncmp(cached_path, ffn.c_str(), strlen(cached_path)); +} + +void ota_set_fault_files() { + if (should_fault_inject(OTAIO_READ)) { + read_fault_file_name = fault_fname(OTAIO_READ); + } + if (should_fault_inject(OTAIO_WRITE)) { + write_fault_file_name = fault_fname(OTAIO_WRITE); + } + if (should_fault_inject(OTAIO_FSYNC)) { + fsync_fault_file_name = fault_fname(OTAIO_FSYNC); + } +} bool have_eio_error = false; int ota_open(const char* path, int oflags) { -#if defined (TARGET_INJECT_FAULTS) // Let the caller handle errors; we do not care if open succeeds or fails int fd = open(path, oflags); - FilenameCache[fd] = path; + filename_cache[fd] = path; return fd; -#else - return open(path, oflags); -#endif } int ota_open(const char* path, int oflags, mode_t mode) { -#if defined (TARGET_INJECT_FAULTS) int fd = open(path, oflags, mode); - FilenameCache[fd] = path; - return fd; -#else - return open(path, oflags, mode); -#endif -} + filename_cache[fd] = path; + return fd; } FILE* ota_fopen(const char* path, const char* mode) { -#if defined (TARGET_INJECT_FAULTS) FILE* fh = fopen(path, mode); - FilenameCache[(intptr_t)fh] = path; + filename_cache[(intptr_t)fh] = path; return fh; -#else - return fopen(path, mode); -#endif } int ota_close(int fd) { -#if defined (TARGET_INJECT_FAULTS) - // descriptors can be reused, so make sure not to leave them in the cahce - FilenameCache.erase(fd); -#endif + // descriptors can be reused, so make sure not to leave them in the cache + filename_cache.erase(fd); return close(fd); } int ota_fclose(FILE* fh) { -#if defined (TARGET_INJECT_FAULTS) - FilenameCache.erase((intptr_t)fh); -#endif + filename_cache.erase((intptr_t)fh); return fclose(fh); } size_t ota_fread(void* ptr, size_t size, size_t nitems, FILE* stream) { -#if defined (TARGET_READ_FAULT) - if (FilenameCache.find((intptr_t)stream) != FilenameCache.end() - && FilenameCache[(intptr_t)stream] == FaultFileName) { - FaultFileName = ""; - errno = EIO; - have_eio_error = true; - return 0; - } else { - size_t status = fread(ptr, size, nitems, stream); - // If I/O error occurs, set the retry-update flag. - if (status != nitems && errno == EIO) { + if (should_fault_inject(OTAIO_READ)) { + auto cached = filename_cache.find((intptr_t)stream); + const char* cached_path = cached->second; + if (cached != filename_cache.end() && + get_hit_file(cached_path, read_fault_file_name)) { + read_fault_file_name = ""; + errno = EIO; have_eio_error = true; + return 0; } - return status; } -#else size_t status = fread(ptr, size, nitems, stream); if (status != nitems && errno == EIO) { have_eio_error = true; } return status; -#endif } ssize_t ota_read(int fd, void* buf, size_t nbyte) { -#if defined (TARGET_READ_FAULT) - if (FilenameCache.find(fd) != FilenameCache.end() - && FilenameCache[fd] == FaultFileName) { - FaultFileName = ""; - errno = EIO; - have_eio_error = true; - return -1; - } else { - ssize_t status = read(fd, buf, nbyte); - if (status == -1 && errno == EIO) { + if (should_fault_inject(OTAIO_READ)) { + auto cached = filename_cache.find(fd); + const char* cached_path = cached->second; + if (cached != filename_cache.end() + && get_hit_file(cached_path, read_fault_file_name)) { + read_fault_file_name = ""; + errno = EIO; have_eio_error = true; + return -1; } - return status; } -#else ssize_t status = read(fd, buf, nbyte); if (status == -1 && errno == EIO) { have_eio_error = true; } return status; -#endif } size_t ota_fwrite(const void* ptr, size_t size, size_t count, FILE* stream) { -#if defined (TARGET_WRITE_FAULT) - if (FilenameCache.find((intptr_t)stream) != FilenameCache.end() - && FilenameCache[(intptr_t)stream] == FaultFileName) { - FaultFileName = ""; - errno = EIO; - have_eio_error = true; - return 0; - } else { - size_t status = fwrite(ptr, size, count, stream); - if (status != count && errno == EIO) { + if (should_fault_inject(OTAIO_WRITE)) { + auto cached = filename_cache.find((intptr_t)stream); + const char* cached_path = cached->second; + if (cached != filename_cache.end() && + get_hit_file(cached_path, write_fault_file_name)) { + write_fault_file_name = ""; + errno = EIO; have_eio_error = true; + return 0; } - return status; } -#else size_t status = fwrite(ptr, size, count, stream); if (status != count && errno == EIO) { have_eio_error = true; } return status; -#endif } ssize_t ota_write(int fd, const void* buf, size_t nbyte) { -#if defined (TARGET_WRITE_FAULT) - if (FilenameCache.find(fd) != FilenameCache.end() - && FilenameCache[fd] == FaultFileName) { - FaultFileName = ""; - errno = EIO; - have_eio_error = true; - return -1; - } else { - ssize_t status = write(fd, buf, nbyte); - if (status == -1 && errno == EIO) { + if (should_fault_inject(OTAIO_WRITE)) { + auto cached = filename_cache.find(fd); + const char* cached_path = cached->second; + if (cached != filename_cache.end() && + get_hit_file(cached_path, write_fault_file_name)) { + write_fault_file_name = ""; + errno = EIO; have_eio_error = true; + return -1; } - return status; } -#else ssize_t status = write(fd, buf, nbyte); if (status == -1 && errno == EIO) { have_eio_error = true; } return status; -#endif } int ota_fsync(int fd) { -#if defined (TARGET_FSYNC_FAULT) - if (FilenameCache.find(fd) != FilenameCache.end() - && FilenameCache[fd] == FaultFileName) { - FaultFileName = ""; - errno = EIO; - have_eio_error = true; - return -1; - } else { - int status = fsync(fd); - if (status == -1 && errno == EIO) { + if (should_fault_inject(OTAIO_FSYNC)) { + auto cached = filename_cache.find(fd); + const char* cached_path = cached->second; + if (cached != filename_cache.end() && + get_hit_file(cached_path, fsync_fault_file_name)) { + fsync_fault_file_name = ""; + errno = EIO; have_eio_error = true; + return -1; } - return status; } -#else int status = fsync(fd); if (status == -1 && errno == EIO) { have_eio_error = true; } return status; -#endif } + diff --git a/otafault/ota_io.h b/otafault/ota_io.h index 641a5ae0a..84187a76e 100644 --- a/otafault/ota_io.h +++ b/otafault/ota_io.h @@ -26,6 +26,10 @@ #include #include +#define OTAIO_CACHE_FNAME "/cache/saved.file" + +void ota_set_fault_files(); + int ota_open(const char* path, int oflags); int ota_open(const char* path, int oflags, mode_t mode); diff --git a/otafault/test.cpp b/otafault/test.cpp index a0f731517..6514782bf 100644 --- a/otafault/test.cpp +++ b/otafault/test.cpp @@ -17,16 +17,18 @@ #include #include #include +#include #include "ota_io.h" -int main(int argc, char **argv) { +int main(int /* argc */, char** /* argv */) { int fd = open("testdata/test.file", O_RDWR); char buf[8]; - char *out = "321"; + const char* out = "321"; int readv = ota_read(fd, buf, 4); printf("Read returned %d\n", readv); int writev = ota_write(fd, out, 4); printf("Write returned %d\n", writev); + close(fd); return 0; } -- cgit v1.2.3