diff options
author | Neil Fuller <nfuller@google.com> | 2015-04-13 11:58:51 +0000 |
---|---|---|
committer | Android Git Automerger <android-git-automerger@android.com> | 2015-04-13 11:58:51 +0000 |
commit | 90014d61c83ef5105881c171481a50f21fd4dd57 (patch) | |
tree | d9b7c13846649375efa41ee6f509637ce1c2a9da | |
parent | ed8ce08600c8b166aa53ac2197c5b3e3692ade41 (diff) | |
parent | 532d0a11c439b101ac2d26f6fdc44f9791f6d0ad (diff) | |
download | system_core-90014d61c83ef5105881c171481a50f21fd4dd57.zip system_core-90014d61c83ef5105881c171481a50f21fd4dd57.tar.gz system_core-90014d61c83ef5105881c171481a50f21fd4dd57.tar.bz2 |
am 532d0a11: am 9b83b623: Merge "Executable to run on boot that removes old tzdata if needed"
* commit '532d0a11c439b101ac2d26f6fdc44f9791f6d0ad':
Executable to run on boot that removes old tzdata if needed
-rw-r--r-- | rootdir/init.rc | 3 | ||||
-rw-r--r-- | tzdatacheck/Android.mk | 21 | ||||
-rw-r--r-- | tzdatacheck/tzdatacheck.cpp | 236 |
3 files changed, 260 insertions, 0 deletions
diff --git a/rootdir/init.rc b/rootdir/init.rc index e6f4f4a..a2b8f59 100644 --- a/rootdir/init.rc +++ b/rootdir/init.rc @@ -321,6 +321,9 @@ on post-fs-data # Set SELinux security contexts on upgrade or policy update. restorecon_recursive /data + # Check any timezone data in /data is newer than the copy in /system, delete if not. + exec u:r:tzdatacheck:s0 system system -- /system/bin/tzdatacheck /system/usr/share/zoneinfo /data/misc/zoneinfo + # If there is no fs-post-data action in the init.<device>.rc file, you # must uncomment this line, otherwise encrypted filesystems # won't work. diff --git a/tzdatacheck/Android.mk b/tzdatacheck/Android.mk new file mode 100644 index 0000000..0e25f7d --- /dev/null +++ b/tzdatacheck/Android.mk @@ -0,0 +1,21 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +# ======================================================== +# Executable +# ======================================================== +include $(CLEAR_VARS) +LOCAL_SRC_FILES:= tzdatacheck.cpp +LOCAL_MODULE := tzdatacheck +LOCAL_SHARED_LIBRARIES := libbase libcutils liblog +LOCAL_CFLAGS := -Werror +include $(BUILD_EXECUTABLE) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES:= tzdatacheck.cpp +LOCAL_MODULE := tzdatacheck +LOCAL_SHARED_LIBRARIES := libbase libcutils liblog +LOCAL_CFLAGS := -Werror +include $(BUILD_HOST_EXECUTABLE) + diff --git a/tzdatacheck/tzdatacheck.cpp b/tzdatacheck/tzdatacheck.cpp new file mode 100644 index 0000000..31f7b55 --- /dev/null +++ b/tzdatacheck/tzdatacheck.cpp @@ -0,0 +1,236 @@ +/* + * 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 <errno.h> +#include <ftw.h> +#include <libgen.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <iostream> +#include <memory> +#include <string> +#include <vector> + +#include "base/logging.h" + +static const char* TZDATA_FILENAME = "/tzdata"; +// tzdata file header (as much as we need for the version): +// byte[11] tzdata_version -- e.g. "tzdata2012f" +static const int TZ_HEADER_LENGTH = 11; + +static void usage() { + std::cerr << "Usage: tzdatacheck SYSTEM_TZ_DIR DATA_TZ_DIR\n" + "\n" + "Compares the headers of two tzdata files. If the one in SYSTEM_TZ_DIR is the\n" + "same or a higher version than the one in DATA_TZ_DIR the DATA_TZ_DIR is renamed\n" + "and then deleted.\n"; + exit(1); +} + +/* + * Opens a file and fills headerBytes with the first byteCount bytes from the file. It is a fatal + * error if the file is too small or cannot be opened. If the file does not exist false is returned. + * If the bytes were read successfully then true is returned. + */ +static bool readHeader(const std::string& tzDataFileName, char* headerBytes, size_t byteCount) { + FILE* tzDataFile = fopen(tzDataFileName.c_str(), "r"); + if (tzDataFile == nullptr) { + if (errno == ENOENT) { + return false; + } else { + PLOG(FATAL) << "Error opening tzdata file " << tzDataFileName; + } + } + size_t bytesRead = fread(headerBytes, 1, byteCount, tzDataFile); + if (bytesRead != byteCount) { + LOG(FATAL) << tzDataFileName << " is too small. " << byteCount << " bytes required"; + } + fclose(tzDataFile); + return true; +} + +/* Checks the contents of headerBytes. It is a fatal error if it not a tzdata header. */ +static void checkValidHeader(const std::string& fileName, char* headerBytes) { + if (strncmp("tzdata", headerBytes, 6) != 0) { + LOG(FATAL) << fileName << " does not start with the expected bytes (tzdata)"; + } +} + +/* Return the parent directory of dirName. */ +static std::string getParentDir(const std::string& dirName) { + std::unique_ptr<char> mutable_dirname(strdup(dirName.c_str())); + return dirname(mutable_dirname.get()); +} + +/* Deletes a single file, symlink or directory. Called from nftw(). */ +static int deleteFn(const char* fpath, const struct stat*, int typeflag, struct FTW*) { + LOG(DEBUG) << "Inspecting " << fpath; + switch (typeflag) { + case FTW_F: + case FTW_SL: + LOG(DEBUG) << "Unlinking " << fpath; + if (unlink(fpath)) { + PLOG(WARNING) << "Failed to unlink file/symlink " << fpath; + } + break; + case FTW_D: + case FTW_DP: + LOG(DEBUG) << "Removing dir " << fpath; + if (rmdir(fpath)) { + PLOG(WARNING) << "Failed to remove dir " << fpath; + } + break; + default: + LOG(WARNING) << "Unsupported file type " << fpath << ": " << typeflag; + break; + } + return 0; +} + +/* + * Deletes dirToDelete and returns true if it is successful in removing or moving the directory out + * of the way. If dirToDelete does not exist this function does nothing and returns true. + * + * During deletion, this function first renames the directory to a temporary name. If the temporary + * directory cannot be created, or the directory cannot be renamed, false is returned. After the + * rename, deletion of files and subdirs beneath the directory is performed on a "best effort" + * basis. Symlinks beneath the directory are not followed. + */ +static bool deleteDir(const std::string& dirToDelete) { + // Check whether the dir exists. + struct stat buf; + if (stat(dirToDelete.c_str(), &buf) == 0) { + if (!S_ISDIR(buf.st_mode)) { + LOG(WARNING) << dirToDelete << " is not a directory"; + return false; + } + } else { + if (errno == ENOENT) { + PLOG(INFO) << "Directory does not exist: " << dirToDelete; + return true; + } else { + PLOG(WARNING) << "Unable to stat " << dirToDelete; + return false; + } + } + + // First, rename dirToDelete. + std::string tempDirNameTemplate = getParentDir(dirToDelete); + tempDirNameTemplate += "/tempXXXXXX"; + + // Create an empty directory with the temporary name. For this we need a non-const char*. + std::vector<char> tempDirName(tempDirNameTemplate.length() + 1); + strcpy(&tempDirName[0], tempDirNameTemplate.c_str()); + if (mkdtemp(&tempDirName[0]) == nullptr) { + PLOG(WARNING) << "Unable to create a temporary directory: " << tempDirNameTemplate; + return false; + } + + // Rename dirToDelete to tempDirName. + int rc = rename(dirToDelete.c_str(), &tempDirName[0]); + if (rc == -1) { + PLOG(WARNING) << "Unable to rename directory from " << dirToDelete << " to " + << &tempDirName[0]; + return false; + } + + // Recursively delete contents of tempDirName. + rc = nftw(&tempDirName[0], deleteFn, 10 /* openFiles */, + FTW_DEPTH | FTW_MOUNT | FTW_PHYS); + if (rc == -1) { + LOG(INFO) << "Could not delete directory: " << &tempDirName[0]; + } + return true; +} + +/* + * After a platform update it is likely that timezone data found on the system partition will be + * newer than the version found in the data partition. This tool detects this case and removes the + * version in /data along with any update metadata. + * + * Note: This code is related to code in com.android.server.updates.TzDataInstallReceiver. The + * paths for the metadata and current timezone data must match. + * + * Typically on device the two args will be: + * /system/usr/share/zoneinfo /data/misc/zoneinfo + * + * See usage() for usage notes. + */ +int main(int argc, char* argv[]) { + if (argc != 3) { + usage(); + } + + const char* systemZoneInfoDir = argv[1]; + const char* dataZoneInfoDir = argv[2]; + + std::string dataCurrentDirName(dataZoneInfoDir); + dataCurrentDirName += "/current"; + std::string dataTzDataFileName(dataCurrentDirName); + dataTzDataFileName += TZDATA_FILENAME; + + std::vector<char> dataTzDataHeader; + dataTzDataHeader.reserve(TZ_HEADER_LENGTH); + + bool dataFileExists = readHeader(dataTzDataFileName, dataTzDataHeader.data(), TZ_HEADER_LENGTH); + if (!dataFileExists) { + LOG(INFO) << "tzdata file " << dataTzDataFileName << " does not exist. No action required."; + return 0; + } + checkValidHeader(dataTzDataFileName, dataTzDataHeader.data()); + + std::string systemTzDataFileName(systemZoneInfoDir); + systemTzDataFileName += TZDATA_FILENAME; + std::vector<char> systemTzDataHeader; + systemTzDataHeader.reserve(TZ_HEADER_LENGTH); + bool systemFileExists = + readHeader(systemTzDataFileName, systemTzDataHeader.data(), TZ_HEADER_LENGTH); + if (!systemFileExists) { + LOG(FATAL) << systemTzDataFileName << " does not exist or could not be opened"; + } + checkValidHeader(systemTzDataFileName, systemTzDataHeader.data()); + + if (strncmp(&systemTzDataHeader[0], &dataTzDataHeader[0], TZ_HEADER_LENGTH) < 0) { + LOG(INFO) << "tzdata file " << dataTzDataFileName << " is the newer than " + << systemTzDataFileName << ". No action required."; + } else { + // We have detected the case this tool is intended to prevent. Go fix it. + LOG(INFO) << "tzdata file " << dataTzDataFileName << " is the same as or older than " + << systemTzDataFileName << "; fixing..."; + + // Delete the update metadata + std::string dataUpdatesDirName(dataZoneInfoDir); + dataUpdatesDirName += "/updates"; + LOG(INFO) << "Removing: " << dataUpdatesDirName; + bool deleted = deleteDir(dataUpdatesDirName); + if (!deleted) { + LOG(WARNING) << "Deletion of install metadata " << dataUpdatesDirName + << " was not successful"; + } + + // Delete the TZ data + LOG(INFO) << "Removing: " << dataCurrentDirName; + deleted = deleteDir(dataCurrentDirName); + if (!deleted) { + LOG(WARNING) << "Deletion of tzdata " << dataCurrentDirName << " was not successful"; + } + } + + return 0; +} |