blob: fb5c84bb688c8c2740cf5b19bc060f7d7dcb65c5 [file] [log] [blame]
Neil Fuller08913222015-03-31 18:24:29 +01001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Neil Fullereec2bfb2016-12-13 16:26:02 +000017#include <ctype.h>
Neil Fuller08913222015-03-31 18:24:29 +010018#include <errno.h>
19#include <ftw.h>
20#include <libgen.h>
21#include <stdarg.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <unistd.h>
26#include <iostream>
27#include <memory>
28#include <string>
29#include <vector>
30
Elliott Hughes4f713192015-12-04 22:00:26 -080031#include "android-base/logging.h"
Neil Fuller08913222015-03-31 18:24:29 +010032
Neil Fullereec2bfb2016-12-13 16:26:02 +000033static 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
Neil Fuller08913222015-03-31 18:24:29 +010048static const char* TZDATA_FILENAME = "/tzdata";
49// tzdata file header (as much as we need for the version):
50// byte[11] tzdata_version -- e.g. "tzdata2012f"
51static const int TZ_HEADER_LENGTH = 11;
Neil Fullereec2bfb2016-12-13 16:26:02 +000052// 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
Neil Fuller08913222015-03-31 18:24:29 +010056
57static void usage() {
58 std::cerr << "Usage: tzdatacheck SYSTEM_TZ_DIR DATA_TZ_DIR\n"
59 "\n"
Neil Fullereec2bfb2016-12-13 16:26:02 +000060 "Checks whether any timezone update bundle in DATA_TZ_DIR is compatible with the\n"
61 "current Android release and better than or the same as base system timezone rules in\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";
Neil Fuller08913222015-03-31 18:24:29 +010064 exit(1);
65}
66
67/*
Neil Fullereec2bfb2016-12-13 16:26:02 +000068 * Opens a file and fills buffer with the first byteCount bytes from the file.
69 * If the file does not exist or cannot be opened or is too short then false is returned.
Neil Fuller08913222015-03-31 18:24:29 +010070 * If the bytes were read successfully then true is returned.
71 */
Neil Fullereec2bfb2016-12-13 16:26:02 +000072static bool readBytes(const std::string& fileName, char* buffer, size_t byteCount) {
73 FILE* file = fopen(fileName.c_str(), "r");
74 if (file == nullptr) {
75 if (errno != ENOENT) {
76 PLOG(WARNING) << "Error opening file " << fileName;
Neil Fuller08913222015-03-31 18:24:29 +010077 }
Neil Fullereec2bfb2016-12-13 16:26:02 +000078 return false;
Neil Fuller08913222015-03-31 18:24:29 +010079 }
Neil Fullereec2bfb2016-12-13 16:26:02 +000080 size_t bytesRead = fread(buffer, 1, byteCount, file);
81 fclose(file);
Neil Fuller08913222015-03-31 18:24:29 +010082 if (bytesRead != byteCount) {
Neil Fullereec2bfb2016-12-13 16:26:02 +000083 LOG(WARNING) << fileName << " is too small. " << byteCount << " bytes required";
84 return false;
Neil Fuller08913222015-03-31 18:24:29 +010085 }
Neil Fuller08913222015-03-31 18:24:29 +010086 return true;
87}
88
Neil Fullereec2bfb2016-12-13 16:26:02 +000089/*
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) {
Neil Fuller08913222015-03-31 18:24:29 +010094 if (strncmp("tzdata", headerBytes, 6) != 0) {
Neil Fullereec2bfb2016-12-13 16:26:02 +000095 LOG(WARNING) << fileName << " does not start with the expected bytes (tzdata)";
96 return false;
Neil Fuller08913222015-03-31 18:24:29 +010097 }
Neil Fullereec2bfb2016-12-13 16:26:02 +000098 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;
Neil Fuller08913222015-03-31 18:24:29 +0100132}
133
134/* Return the parent directory of dirName. */
135static std::string getParentDir(const std::string& dirName) {
136 std::unique_ptr<char> mutable_dirname(strdup(dirName.c_str()));
137 return dirname(mutable_dirname.get());
138}
139
140/* Deletes a single file, symlink or directory. Called from nftw(). */
141static int deleteFn(const char* fpath, const struct stat*, int typeflag, struct FTW*) {
142 LOG(DEBUG) << "Inspecting " << fpath;
143 switch (typeflag) {
144 case FTW_F:
145 case FTW_SL:
146 LOG(DEBUG) << "Unlinking " << fpath;
147 if (unlink(fpath)) {
148 PLOG(WARNING) << "Failed to unlink file/symlink " << fpath;
149 }
150 break;
151 case FTW_D:
152 case FTW_DP:
153 LOG(DEBUG) << "Removing dir " << fpath;
154 if (rmdir(fpath)) {
155 PLOG(WARNING) << "Failed to remove dir " << fpath;
156 }
157 break;
158 default:
159 LOG(WARNING) << "Unsupported file type " << fpath << ": " << typeflag;
160 break;
161 }
162 return 0;
163}
164
Neil Fullereec2bfb2016-12-13 16:26:02 +0000165enum 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
Neil Fuller08913222015-03-31 18:24:29 +0100179/*
180 * Deletes dirToDelete and returns true if it is successful in removing or moving the directory out
Neil Fullereec2bfb2016-12-13 16:26:02 +0000181 * 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.
Neil Fuller08913222015-03-31 18:24:29 +0100183 *
184 * During deletion, this function first renames the directory to a temporary name. If the temporary
185 * directory cannot be created, or the directory cannot be renamed, false is returned. After the
186 * rename, deletion of files and subdirs beneath the directory is performed on a "best effort"
187 * basis. Symlinks beneath the directory are not followed.
188 */
189static bool deleteDir(const std::string& dirToDelete) {
190 // Check whether the dir exists.
Neil Fullereec2bfb2016-12-13 16:26:02 +0000191 int pathStatus = checkPath(dirToDelete);
192 if (pathStatus == NONE) {
193 LOG(INFO) << "Path " << dirToDelete << " does not exist";
194 return true;
195 }
196 if (pathStatus != IS_DIR) {
197 LOG(WARNING) << "Path " << dirToDelete << " failed to stat() or is not a directory.";
Neil Fuller08913222015-03-31 18:24:29 +0100198 return false;
Neil Fuller08913222015-03-31 18:24:29 +0100199 }
200
201 // First, rename dirToDelete.
Neil Fullereec2bfb2016-12-13 16:26:02 +0000202
Neil Fuller08913222015-03-31 18:24:29 +0100203 std::string tempDirNameTemplate = getParentDir(dirToDelete);
204 tempDirNameTemplate += "/tempXXXXXX";
205
206 // Create an empty directory with the temporary name. For this we need a non-const char*.
207 std::vector<char> tempDirName(tempDirNameTemplate.length() + 1);
208 strcpy(&tempDirName[0], tempDirNameTemplate.c_str());
209 if (mkdtemp(&tempDirName[0]) == nullptr) {
210 PLOG(WARNING) << "Unable to create a temporary directory: " << tempDirNameTemplate;
211 return false;
212 }
213
Neil Fullereec2bfb2016-12-13 16:26:02 +0000214 // Rename dirToDelete to tempDirName (replacing the empty tempDirName directory created above).
Neil Fuller08913222015-03-31 18:24:29 +0100215 int rc = rename(dirToDelete.c_str(), &tempDirName[0]);
216 if (rc == -1) {
217 PLOG(WARNING) << "Unable to rename directory from " << dirToDelete << " to "
218 << &tempDirName[0];
219 return false;
220 }
221
222 // Recursively delete contents of tempDirName.
Neil Fullereec2bfb2016-12-13 16:26:02 +0000223
Neil Fuller08913222015-03-31 18:24:29 +0100224 rc = nftw(&tempDirName[0], deleteFn, 10 /* openFiles */,
225 FTW_DEPTH | FTW_MOUNT | FTW_PHYS);
226 if (rc == -1) {
227 LOG(INFO) << "Could not delete directory: " << &tempDirName[0];
228 }
229 return true;
230}
231
232/*
Neil Fullereec2bfb2016-12-13 16:26:02 +0000233 * 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/*
Neil Fuller08913222015-03-31 18:24:29 +0100260 * After a platform update it is likely that timezone data found on the system partition will be
261 * newer than the version found in the data partition. This tool detects this case and removes the
Neil Fullereec2bfb2016-12-13 16:26:02 +0000262 * version in /data.
Neil Fuller08913222015-03-31 18:24:29 +0100263 *
264 * Note: This code is related to code in com.android.server.updates.TzDataInstallReceiver. The
265 * paths for the metadata and current timezone data must match.
266 *
267 * Typically on device the two args will be:
268 * /system/usr/share/zoneinfo /data/misc/zoneinfo
269 *
270 * See usage() for usage notes.
271 */
272int main(int argc, char* argv[]) {
273 if (argc != 3) {
274 usage();
Neil Fullereec2bfb2016-12-13 16:26:02 +0000275 return 1;
Neil Fuller08913222015-03-31 18:24:29 +0100276 }
277
278 const char* systemZoneInfoDir = argv[1];
279 const char* dataZoneInfoDir = argv[2];
280
Neil Fullereec2bfb2016-12-13 16:26:02 +0000281 // Check the bundle directory exists. If it does not, exit quickly: nothing to do.
Neil Fuller08913222015-03-31 18:24:29 +0100282 std::string dataCurrentDirName(dataZoneInfoDir);
283 dataCurrentDirName += "/current";
Neil Fullereec2bfb2016-12-13 16:26:02 +0000284 int dataCurrentDirStatus = checkPath(dataCurrentDirName);
285 if (dataCurrentDirStatus == NONE) {
286 LOG(INFO) << "timezone bundle dir " << dataCurrentDirName
287 << " does not exist. No action required.";
Neil Fuller08913222015-03-31 18:24:29 +0100288 return 0;
289 }
Neil Fullereec2bfb2016-12-13 16:26:02 +0000290 // 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 }
Neil Fuller08913222015-03-31 18:24:29 +0100298
Neil Fullereec2bfb2016-12-13 16:26:02 +0000299 // Check the installed bundle version.
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 }
314
315 if (!checkValidBundleVersion(bundleVersion.data())) {
316 LOG(WARNING) << "bundle version file " << bundleVersionFileName
317 << " is not valid. Deleting bundle dir.";
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;
336 }
337
338 // Read the system rules version out of the /system tzdata file.
Neil Fuller08913222015-03-31 18:24:29 +0100339 std::string systemTzDataFileName(systemZoneInfoDir);
340 systemTzDataFileName += TZDATA_FILENAME;
341 std::vector<char> systemTzDataHeader;
342 systemTzDataHeader.reserve(TZ_HEADER_LENGTH);
343 bool systemFileExists =
Neil Fullereec2bfb2016-12-13 16:26:02 +0000344 readBytes(systemTzDataFileName, systemTzDataHeader.data(), TZ_HEADER_LENGTH);
Neil Fuller08913222015-03-31 18:24:29 +0100345 if (!systemFileExists) {
Neil Fullereec2bfb2016-12-13 16:26:02 +0000346 // Implies the contents of the system partition is corrupt in some way. Nothing we can do.
347 LOG(WARNING) << systemTzDataFileName << " does not exist or could not be opened";
348 return 6;
Neil Fuller08913222015-03-31 18:24:29 +0100349 }
Neil Fullereec2bfb2016-12-13 16:26:02 +0000350 if (!checkValidTzDataHeader(systemTzDataFileName, systemTzDataHeader.data())) {
351 // Implies the contents of the system partition is corrupt in some way. Nothing we can do.
352 LOG(WARNING) << systemTzDataFileName << " does not have a valid header.";
353 return 7;
Neil Fuller08913222015-03-31 18:24:29 +0100354 }
355
Neil Fullereec2bfb2016-12-13 16:26:02 +0000356 // Compare the bundle rules version against the system rules version.
357 if (strncmp(
358 &systemTzDataHeader[TZ_DATA_HEADER_PREFIX_LEN],
359 &bundleVersion[BUNDLE_VERSION_RULES_IDX],
360 RULES_VERSION_LEN) <= 0) {
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;
364 }
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);
Neil Fuller08913222015-03-31 18:24:29 +0100373 return 0;
374}