summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeil Fuller2017-01-11 08:46:46 -0600
committerGerrit Code Review2017-01-11 08:46:47 -0600
commit6540dfefca55d4daffe69548291ae4e4e72336bf (patch)
treea712b58ed25995bf1192620d8dbc93469b1bdaa1
parent6ad690dfd492ea23517ca908ed75a86fecfd31b5 (diff)
parenteec2bfb53b9967cd40d137cbc013100ddb888640 (diff)
downloadplatform-system-core-6540dfefca55d4daffe69548291ae4e4e72336bf.tar.gz
platform-system-core-6540dfefca55d4daffe69548291ae4e4e72336bf.tar.xz
platform-system-core-6540dfefca55d4daffe69548291ae4e4e72336bf.zip
Merge "Change tzdatacheck to account for bundle format changes"
-rw-r--r--tzdatacheck/tzdatacheck.cpp280
1 files changed, 209 insertions, 71 deletions
diff --git a/tzdatacheck/tzdatacheck.cpp b/tzdatacheck/tzdatacheck.cpp
index c1ab2ac47..fb5c84bb6 100644
--- a/tzdatacheck/tzdatacheck.cpp
+++ b/tzdatacheck/tzdatacheck.cpp
@@ -14,6 +14,7 @@
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 16
17#include <ctype.h>
17#include <errno.h> 18#include <errno.h>
18#include <ftw.h> 19#include <ftw.h>
19#include <libgen.h> 20#include <libgen.h>
@@ -29,47 +30,105 @@
29 30
30#include "android-base/logging.h" 31#include "android-base/logging.h"
31 32
33static const char* BUNDLE_VERSION_FILENAME = "/bundle_version";
34// bundle_version is an ASCII file consisting of 17 bytes in the form: AAA.BBB|CCCCC|DDD
35// AAA.BBB is the major/minor version of the bundle format (e.g. 001.001),
36// CCCCC is the rules version (e.g. 2016g)
37// DDD is the android revision for this rules version to allow for bundle corrections (e.g. 001)
38// We only need the first 13 to determine if it is suitable for the device.
39static const int BUNDLE_VERSION_LENGTH = 13;
40// The major version of the bundle format supported by this code as a null-terminated char[].
41static const char REQUIRED_BUNDLE_VERSION[] = "001";
42static const size_t REQUIRED_BUNDLE_VERSION_LEN = sizeof(REQUIRED_BUNDLE_VERSION) - 1; // exclude \0
43// The length of the IANA rules version bytes. e.g. 2016a
44static const size_t RULES_VERSION_LEN = 5;
45// Bundle version bytes are: AAA.BBB|CCCCC - the rules version is CCCCC
46static const size_t BUNDLE_VERSION_RULES_IDX = 8;
47
32static const char* TZDATA_FILENAME = "/tzdata"; 48static const char* TZDATA_FILENAME = "/tzdata";
33// tzdata file header (as much as we need for the version): 49// tzdata file header (as much as we need for the version):
34// byte[11] tzdata_version -- e.g. "tzdata2012f" 50// byte[11] tzdata_version -- e.g. "tzdata2012f"
35static const int TZ_HEADER_LENGTH = 11; 51static const int TZ_HEADER_LENGTH = 11;
52// The major version of the bundle format supported by this code as a null-terminated char[].
53static const char TZ_DATA_HEADER_PREFIX[] = "tzdata";
54static const size_t TZ_DATA_HEADER_PREFIX_LEN = sizeof(TZ_DATA_HEADER_PREFIX) - 1; // exclude \0
55
36 56
37static void usage() { 57static void usage() {
38 std::cerr << "Usage: tzdatacheck SYSTEM_TZ_DIR DATA_TZ_DIR\n" 58 std::cerr << "Usage: tzdatacheck SYSTEM_TZ_DIR DATA_TZ_DIR\n"
39 "\n" 59 "\n"
40 "Compares the headers of two tzdata files. If the one in SYSTEM_TZ_DIR is the\n" 60 "Checks whether any timezone update bundle in DATA_TZ_DIR is compatible with the\n"
41 "same or a higher version than the one in DATA_TZ_DIR the DATA_TZ_DIR is renamed\n" 61 "current Android release and better than or the same as base system timezone rules in\n"
42 "and then deleted.\n"; 62 "SYSTEM_TZ_DIR. If the timezone rules in SYSTEM_TZ_DIR are a higher version than the\n"
63 "one in DATA_TZ_DIR the DATA_TZ_DIR is renamed and then deleted.\n";
43 exit(1); 64 exit(1);
44} 65}
45 66
46/* 67/*
47 * Opens a file and fills headerBytes with the first byteCount bytes from the file. It is a fatal 68 * Opens a file and fills buffer with the first byteCount bytes from the file.
48 * error if the file is too small or cannot be opened. If the file does not exist false is returned. 69 * If the file does not exist or cannot be opened or is too short then false is returned.
49 * If the bytes were read successfully then true is returned. 70 * If the bytes were read successfully then true is returned.
50 */ 71 */
51static bool readHeader(const std::string& tzDataFileName, char* headerBytes, size_t byteCount) { 72static bool readBytes(const std::string& fileName, char* buffer, size_t byteCount) {
52 FILE* tzDataFile = fopen(tzDataFileName.c_str(), "r"); 73 FILE* file = fopen(fileName.c_str(), "r");
53 if (tzDataFile == nullptr) { 74 if (file == nullptr) {
54 if (errno == ENOENT) { 75 if (errno != ENOENT) {
55 return false; 76 PLOG(WARNING) << "Error opening file " << fileName;
56 } else {
57 PLOG(FATAL) << "Error opening tzdata file " << tzDataFileName;
58 } 77 }
78 return false;
59 } 79 }
60 size_t bytesRead = fread(headerBytes, 1, byteCount, tzDataFile); 80 size_t bytesRead = fread(buffer, 1, byteCount, file);
81 fclose(file);
61 if (bytesRead != byteCount) { 82 if (bytesRead != byteCount) {
62 LOG(FATAL) << tzDataFileName << " is too small. " << byteCount << " bytes required"; 83 LOG(WARNING) << fileName << " is too small. " << byteCount << " bytes required";
84 return false;
63 } 85 }
64 fclose(tzDataFile);
65 return true; 86 return true;
66} 87}
67 88
68/* Checks the contents of headerBytes. It is a fatal error if it not a tzdata header. */ 89/*
69static void checkValidHeader(const std::string& fileName, char* headerBytes) { 90 * Checks the contents of headerBytes. Returns true if it is valid (starts with "tzdata"), false
91 * otherwise.
92 */
93static bool checkValidTzDataHeader(const std::string& fileName, const char* headerBytes) {
70 if (strncmp("tzdata", headerBytes, 6) != 0) { 94 if (strncmp("tzdata", headerBytes, 6) != 0) {
71 LOG(FATAL) << fileName << " does not start with the expected bytes (tzdata)"; 95 LOG(WARNING) << fileName << " does not start with the expected bytes (tzdata)";
96 return false;
72 } 97 }
98 return true;
99}
100
101static bool checkDigits(const char* buffer, const size_t count, size_t* i) {
102 for (size_t j = 0; j < count; j++) {
103 char toCheck = buffer[(*i)++];
104 if (!isdigit(toCheck)) {
105 return false;
106 }
107 }
108 return true;
109}
110
111static bool checkValidBundleVersion(const char* buffer) {
112 // See BUNDLE_VERSION_LENGTH comments above for a description of the format.
113 size_t i = 0;
114 if (!checkDigits(buffer, 3, &i)) {
115 return false;
116 }
117 if (buffer[i++] != '.') {
118 return false;
119 }
120 if (!checkDigits(buffer, 3, &i)) {
121 return false;
122 }
123 if (buffer[i++] != '|') {
124 return false;
125 }
126 if (!checkDigits(buffer, 4, &i)) {
127 return false;
128 }
129 // Ignore the last character. It is assumed to be a letter but we don't check because it's not
130 // obvious what would happen at 'z'.
131 return true;
73} 132}
74 133
75/* Return the parent directory of dirName. */ 134/* Return the parent directory of dirName. */
@@ -103,9 +162,24 @@ static int deleteFn(const char* fpath, const struct stat*, int typeflag, struct
103 return 0; 162 return 0;
104} 163}
105 164
165enum PathStatus { ERR, NONE, IS_DIR, NOT_DIR };
166
167static PathStatus checkPath(const std::string& path) {
168 struct stat buf;
169 if (stat(path.c_str(), &buf) != 0) {
170 if (errno != ENOENT) {
171 PLOG(WARNING) << "Unable to stat " << path;
172 return ERR;
173 }
174 return NONE;
175 }
176 return S_ISDIR(buf.st_mode) ? IS_DIR : NOT_DIR;
177}
178
106/* 179/*
107 * Deletes dirToDelete and returns true if it is successful in removing or moving the directory out 180 * Deletes dirToDelete and returns true if it is successful in removing or moving the directory out
108 * of the way. If dirToDelete does not exist this function does nothing and returns true. 181 * of the way. If dirToDelete does not exist this function does nothing and returns true. If
182 * dirToDelete is not a directory or cannot be accessed this method returns false.
109 * 183 *
110 * During deletion, this function first renames the directory to a temporary name. If the temporary 184 * During deletion, this function first renames the directory to a temporary name. If the temporary
111 * directory cannot be created, or the directory cannot be renamed, false is returned. After the 185 * directory cannot be created, or the directory cannot be renamed, false is returned. After the
@@ -114,23 +188,18 @@ static int deleteFn(const char* fpath, const struct stat*, int typeflag, struct
114 */ 188 */
115static bool deleteDir(const std::string& dirToDelete) { 189static bool deleteDir(const std::string& dirToDelete) {
116 // Check whether the dir exists. 190 // Check whether the dir exists.
117 struct stat buf; 191 int pathStatus = checkPath(dirToDelete);
118 if (stat(dirToDelete.c_str(), &buf) == 0) { 192 if (pathStatus == NONE) {
119 if (!S_ISDIR(buf.st_mode)) { 193 LOG(INFO) << "Path " << dirToDelete << " does not exist";
120 LOG(WARNING) << dirToDelete << " is not a directory"; 194 return true;
195 }
196 if (pathStatus != IS_DIR) {
197 LOG(WARNING) << "Path " << dirToDelete << " failed to stat() or is not a directory.";
121 return false; 198 return false;
122 }
123 } else {
124 if (errno == ENOENT) {
125 PLOG(INFO) << "Directory does not exist: " << dirToDelete;
126 return true;
127 } else {
128 PLOG(WARNING) << "Unable to stat " << dirToDelete;
129 return false;
130 }
131 } 199 }
132 200
133 // First, rename dirToDelete. 201 // First, rename dirToDelete.
202
134 std::string tempDirNameTemplate = getParentDir(dirToDelete); 203 std::string tempDirNameTemplate = getParentDir(dirToDelete);
135 tempDirNameTemplate += "/tempXXXXXX"; 204 tempDirNameTemplate += "/tempXXXXXX";
136 205
@@ -142,7 +211,7 @@ static bool deleteDir(const std::string& dirToDelete) {
142 return false; 211 return false;
143 } 212 }
144 213
145 // Rename dirToDelete to tempDirName. 214 // Rename dirToDelete to tempDirName (replacing the empty tempDirName directory created above).
146 int rc = rename(dirToDelete.c_str(), &tempDirName[0]); 215 int rc = rename(dirToDelete.c_str(), &tempDirName[0]);
147 if (rc == -1) { 216 if (rc == -1) {
148 PLOG(WARNING) << "Unable to rename directory from " << dirToDelete << " to " 217 PLOG(WARNING) << "Unable to rename directory from " << dirToDelete << " to "
@@ -151,6 +220,7 @@ static bool deleteDir(const std::string& dirToDelete) {
151 } 220 }
152 221
153 // Recursively delete contents of tempDirName. 222 // Recursively delete contents of tempDirName.
223
154 rc = nftw(&tempDirName[0], deleteFn, 10 /* openFiles */, 224 rc = nftw(&tempDirName[0], deleteFn, 10 /* openFiles */,
155 FTW_DEPTH | FTW_MOUNT | FTW_PHYS); 225 FTW_DEPTH | FTW_MOUNT | FTW_PHYS);
156 if (rc == -1) { 226 if (rc == -1) {
@@ -160,9 +230,36 @@ static bool deleteDir(const std::string& dirToDelete) {
160} 230}
161 231
162/* 232/*
233 * Deletes the ConfigInstaller metadata directory.
234 * TODO(nfuller). http://b/31008728 Remove this when ConfigInstaller is no longer used.
235 */
236static void deleteConfigUpdaterMetadataDir(const char* dataZoneInfoDir) {
237 // Delete the update metadata
238 std::string dataUpdatesDirName(dataZoneInfoDir);
239 dataUpdatesDirName += "/updates";
240 LOG(INFO) << "Removing: " << dataUpdatesDirName;
241 bool deleted = deleteDir(dataUpdatesDirName);
242 if (!deleted) {
243 LOG(WARNING) << "Deletion of install metadata " << dataUpdatesDirName
244 << " was not successful";
245 }
246}
247
248/*
249 * Deletes the timezone update bundle directory.
250 */
251static void deleteUpdateBundleDir(std::string& bundleDirName) {
252 LOG(INFO) << "Removing: " << bundleDirName;
253 bool deleted = deleteDir(bundleDirName);
254 if (!deleted) {
255 LOG(WARNING) << "Deletion of bundle dir " << bundleDirName << " was not successful";
256 }
257}
258
259/*
163 * After a platform update it is likely that timezone data found on the system partition will be 260 * After a platform update it is likely that timezone data found on the system partition will be
164 * newer than the version found in the data partition. This tool detects this case and removes the 261 * newer than the version found in the data partition. This tool detects this case and removes the
165 * version in /data along with any update metadata. 262 * version in /data.
166 * 263 *
167 * Note: This code is related to code in com.android.server.updates.TzDataInstallReceiver. The 264 * Note: This code is related to code in com.android.server.updates.TzDataInstallReceiver. The
168 * paths for the metadata and current timezone data must match. 265 * paths for the metadata and current timezone data must match.
@@ -175,62 +272,103 @@ static bool deleteDir(const std::string& dirToDelete) {
175int main(int argc, char* argv[]) { 272int main(int argc, char* argv[]) {
176 if (argc != 3) { 273 if (argc != 3) {
177 usage(); 274 usage();
275 return 1;
178 } 276 }
179 277
180 const char* systemZoneInfoDir = argv[1]; 278 const char* systemZoneInfoDir = argv[1];
181 const char* dataZoneInfoDir = argv[2]; 279 const char* dataZoneInfoDir = argv[2];
182 280
281 // Check the bundle directory exists. If it does not, exit quickly: nothing to do.
183 std::string dataCurrentDirName(dataZoneInfoDir); 282 std::string dataCurrentDirName(dataZoneInfoDir);
184 dataCurrentDirName += "/current"; 283 dataCurrentDirName += "/current";
185 std::string dataTzDataFileName(dataCurrentDirName); 284 int dataCurrentDirStatus = checkPath(dataCurrentDirName);
186 dataTzDataFileName += TZDATA_FILENAME; 285 if (dataCurrentDirStatus == NONE) {
286 LOG(INFO) << "timezone bundle dir " << dataCurrentDirName
287 << " does not exist. No action required.";
288 return 0;
289 }
290 // If the bundle directory path is not a directory or we can't stat() the path, exit with a
291 // warning: either there's a problem accessing storage or the world is not as it should be;
292 // nothing to do.
293 if (dataCurrentDirStatus != IS_DIR) {
294 LOG(WARNING) << "Current bundle dir " << dataCurrentDirName
295 << " could not be accessed or is not a directory. result=" << dataCurrentDirStatus;
296 return 2;
297 }
187 298
188 std::vector<char> dataTzDataHeader; 299 // Check the installed bundle version.
189 dataTzDataHeader.reserve(TZ_HEADER_LENGTH); 300 std::string bundleVersionFileName(dataCurrentDirName);
301 bundleVersionFileName += BUNDLE_VERSION_FILENAME;
302 std::vector<char> bundleVersion;
303 bundleVersion.reserve(BUNDLE_VERSION_LENGTH);
304 bool bundleVersionReadOk =
305 readBytes(bundleVersionFileName, bundleVersion.data(), BUNDLE_VERSION_LENGTH);
306 if (!bundleVersionReadOk) {
307 LOG(WARNING) << "bundle version file " << bundleVersionFileName
308 << " does not exist or is too short. Deleting bundle dir.";
309 // Implies the contents of the data partition is corrupt in some way. Try to clean up.
310 deleteConfigUpdaterMetadataDir(dataZoneInfoDir);
311 deleteUpdateBundleDir(dataCurrentDirName);
312 return 3;
313 }
190 314
191 bool dataFileExists = readHeader(dataTzDataFileName, dataTzDataHeader.data(), TZ_HEADER_LENGTH); 315 if (!checkValidBundleVersion(bundleVersion.data())) {
192 if (!dataFileExists) { 316 LOG(WARNING) << "bundle version file " << bundleVersionFileName
193 LOG(INFO) << "tzdata file " << dataTzDataFileName << " does not exist. No action required."; 317 << " is not valid. Deleting bundle dir.";
194 return 0; 318 // Implies the contents of the data partition is corrupt in some way. Try to clean up.
319 deleteConfigUpdaterMetadataDir(dataZoneInfoDir);
320 deleteUpdateBundleDir(dataCurrentDirName);
321 return 4;
322 }
323
324 // Check the first 3 bytes of the bundleVersionHeader: these are the major version (e.g. 001).
325 // It must match exactly to be ok. The minor version is currently ignored.
326 if (strncmp(&bundleVersion[0], REQUIRED_BUNDLE_VERSION, REQUIRED_BUNDLE_VERSION_LEN) != 0) {
327 LOG(INFO) << "bundle version file " << bundleVersionFileName
328 << " is not the required version " << REQUIRED_BUNDLE_VERSION
329 << ". Deleting bundle dir.";
330 // This shouldn't happen with 001, but it in future, this will imply there has been an OTA
331 // and the installed bundle is not compatible with the new version of Android. Remove the
332 // installed bundle.
333 deleteConfigUpdaterMetadataDir(dataZoneInfoDir);
334 deleteUpdateBundleDir(dataCurrentDirName);
335 return 5;
195 } 336 }
196 checkValidHeader(dataTzDataFileName, dataTzDataHeader.data());
197 337
338 // Read the system rules version out of the /system tzdata file.
198 std::string systemTzDataFileName(systemZoneInfoDir); 339 std::string systemTzDataFileName(systemZoneInfoDir);
199 systemTzDataFileName += TZDATA_FILENAME; 340 systemTzDataFileName += TZDATA_FILENAME;
200 std::vector<char> systemTzDataHeader; 341 std::vector<char> systemTzDataHeader;
201 systemTzDataHeader.reserve(TZ_HEADER_LENGTH); 342 systemTzDataHeader.reserve(TZ_HEADER_LENGTH);
202 bool systemFileExists = 343 bool systemFileExists =
203 readHeader(systemTzDataFileName, systemTzDataHeader.data(), TZ_HEADER_LENGTH); 344 readBytes(systemTzDataFileName, systemTzDataHeader.data(), TZ_HEADER_LENGTH);
204 if (!systemFileExists) { 345 if (!systemFileExists) {
205 LOG(FATAL) << systemTzDataFileName << " does not exist or could not be opened"; 346 // Implies the contents of the system partition is corrupt in some way. Nothing we can do.
206 } 347 LOG(WARNING) << systemTzDataFileName << " does not exist or could not be opened";
207 checkValidHeader(systemTzDataFileName, systemTzDataHeader.data()); 348 return 6;
208 349 }
209 if (strncmp(&systemTzDataHeader[0], &dataTzDataHeader[0], TZ_HEADER_LENGTH) < 0) { 350 if (!checkValidTzDataHeader(systemTzDataFileName, systemTzDataHeader.data())) {
210 LOG(INFO) << "tzdata file " << dataTzDataFileName << " is the newer than " 351 // Implies the contents of the system partition is corrupt in some way. Nothing we can do.
211 << systemTzDataFileName << ". No action required."; 352 LOG(WARNING) << systemTzDataFileName << " does not have a valid header.";
212 } else { 353 return 7;
213 // We have detected the case this tool is intended to prevent. Go fix it. 354 }
214 LOG(INFO) << "tzdata file " << dataTzDataFileName << " is the same as or older than "
215 << systemTzDataFileName << "; fixing...";
216
217 // Delete the update metadata
218 std::string dataUpdatesDirName(dataZoneInfoDir);
219 dataUpdatesDirName += "/updates";
220 LOG(INFO) << "Removing: " << dataUpdatesDirName;
221 bool deleted = deleteDir(dataUpdatesDirName);
222 if (!deleted) {
223 LOG(WARNING) << "Deletion of install metadata " << dataUpdatesDirName
224 << " was not successful";
225 }
226 355
227 // Delete the TZ data 356 // Compare the bundle rules version against the system rules version.
228 LOG(INFO) << "Removing: " << dataCurrentDirName; 357 if (strncmp(
229 deleted = deleteDir(dataCurrentDirName); 358 &systemTzDataHeader[TZ_DATA_HEADER_PREFIX_LEN],
230 if (!deleted) { 359 &bundleVersion[BUNDLE_VERSION_RULES_IDX],
231 LOG(WARNING) << "Deletion of tzdata " << dataCurrentDirName << " was not successful"; 360 RULES_VERSION_LEN) <= 0) {
232 } 361 LOG(INFO) << "Found an installed bundle but it is valid. No action taken.";
362 // Implies there is an installed update, but it is good.
363 return 0;
233 } 364 }
234 365
366 // Implies there has been an OTA and the system version of the timezone rules is now newer
367 // than the version installed in /data. Remove the installed bundle.
368 LOG(INFO) << "timezone bundle in " << dataCurrentDirName << " is older than data in "
369 << systemTzDataFileName << "; fixing...";
370
371 deleteConfigUpdaterMetadataDir(dataZoneInfoDir);
372 deleteUpdateBundleDir(dataCurrentDirName);
235 return 0; 373 return 0;
236} 374}