blob: a7bb60cf649112362f342cf5da0d7191a020d390 [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[].
Neil Fullerf54cadb2017-02-09 11:53:07 +000041static const char SUPPORTED_BUNDLE_MAJOR_VERSION[] = "001";
42// The length of the bundle format major version excluding the \0
43static const size_t SUPPORTED_BUNDLE_MAJOR_VERSION_LEN = sizeof(SUPPORTED_BUNDLE_MAJOR_VERSION) - 1;
44// The minor version of the bundle format supported by this code as a null-terminated char[].
45static const char SUPPORTED_BUNDLE_MINOR_VERSION[] = "001";
46// The length of the bundle format minor version excluding the \0
47static const size_t SUPPORTED_BUNDLE_MINOR_VERSION_LEN = sizeof(SUPPORTED_BUNDLE_MINOR_VERSION) - 1;
48// The length of the bundle format version. e.g. 001.001
49static const size_t SUPPORTED_BUNDLE_VERSION_LEN =
50 SUPPORTED_BUNDLE_MAJOR_VERSION_LEN + SUPPORTED_BUNDLE_MINOR_VERSION_LEN + 1;
Neil Fullereec2bfb2016-12-13 16:26:02 +000051// The length of the IANA rules version bytes. e.g. 2016a
52static const size_t RULES_VERSION_LEN = 5;
53// Bundle version bytes are: AAA.BBB|CCCCC - the rules version is CCCCC
54static const size_t BUNDLE_VERSION_RULES_IDX = 8;
55
Neil Fuller08913222015-03-31 18:24:29 +010056static const char* TZDATA_FILENAME = "/tzdata";
57// tzdata file header (as much as we need for the version):
58// byte[11] tzdata_version -- e.g. "tzdata2012f"
59static const int TZ_HEADER_LENGTH = 11;
Neil Fullereec2bfb2016-12-13 16:26:02 +000060// The major version of the bundle format supported by this code as a null-terminated char[].
61static const char TZ_DATA_HEADER_PREFIX[] = "tzdata";
62static const size_t TZ_DATA_HEADER_PREFIX_LEN = sizeof(TZ_DATA_HEADER_PREFIX) - 1; // exclude \0
63
Neil Fuller08913222015-03-31 18:24:29 +010064
65static void usage() {
66 std::cerr << "Usage: tzdatacheck SYSTEM_TZ_DIR DATA_TZ_DIR\n"
67 "\n"
Neil Fullereec2bfb2016-12-13 16:26:02 +000068 "Checks whether any timezone update bundle in DATA_TZ_DIR is compatible with the\n"
69 "current Android release and better than or the same as base system timezone rules in\n"
70 "SYSTEM_TZ_DIR. If the timezone rules in SYSTEM_TZ_DIR are a higher version than the\n"
71 "one in DATA_TZ_DIR the DATA_TZ_DIR is renamed and then deleted.\n";
Neil Fuller08913222015-03-31 18:24:29 +010072 exit(1);
73}
74
75/*
Neil Fullereec2bfb2016-12-13 16:26:02 +000076 * Opens a file and fills buffer with the first byteCount bytes from the file.
77 * 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 +010078 * If the bytes were read successfully then true is returned.
79 */
Neil Fullereec2bfb2016-12-13 16:26:02 +000080static bool readBytes(const std::string& fileName, char* buffer, size_t byteCount) {
81 FILE* file = fopen(fileName.c_str(), "r");
82 if (file == nullptr) {
83 if (errno != ENOENT) {
84 PLOG(WARNING) << "Error opening file " << fileName;
Neil Fuller08913222015-03-31 18:24:29 +010085 }
Neil Fullereec2bfb2016-12-13 16:26:02 +000086 return false;
Neil Fuller08913222015-03-31 18:24:29 +010087 }
Neil Fullereec2bfb2016-12-13 16:26:02 +000088 size_t bytesRead = fread(buffer, 1, byteCount, file);
89 fclose(file);
Neil Fuller08913222015-03-31 18:24:29 +010090 if (bytesRead != byteCount) {
Neil Fullereec2bfb2016-12-13 16:26:02 +000091 LOG(WARNING) << fileName << " is too small. " << byteCount << " bytes required";
92 return false;
Neil Fuller08913222015-03-31 18:24:29 +010093 }
Neil Fuller08913222015-03-31 18:24:29 +010094 return true;
95}
96
Neil Fullereec2bfb2016-12-13 16:26:02 +000097/*
98 * Checks the contents of headerBytes. Returns true if it is valid (starts with "tzdata"), false
99 * otherwise.
100 */
101static bool checkValidTzDataHeader(const std::string& fileName, const char* headerBytes) {
Neil Fuller08913222015-03-31 18:24:29 +0100102 if (strncmp("tzdata", headerBytes, 6) != 0) {
Neil Fullereec2bfb2016-12-13 16:26:02 +0000103 LOG(WARNING) << fileName << " does not start with the expected bytes (tzdata)";
104 return false;
Neil Fuller08913222015-03-31 18:24:29 +0100105 }
Neil Fullereec2bfb2016-12-13 16:26:02 +0000106 return true;
107}
108
109static bool checkDigits(const char* buffer, const size_t count, size_t* i) {
110 for (size_t j = 0; j < count; j++) {
111 char toCheck = buffer[(*i)++];
112 if (!isdigit(toCheck)) {
113 return false;
114 }
115 }
116 return true;
117}
118
119static bool checkValidBundleVersion(const char* buffer) {
120 // See BUNDLE_VERSION_LENGTH comments above for a description of the format.
121 size_t i = 0;
122 if (!checkDigits(buffer, 3, &i)) {
123 return false;
124 }
125 if (buffer[i++] != '.') {
126 return false;
127 }
128 if (!checkDigits(buffer, 3, &i)) {
129 return false;
130 }
131 if (buffer[i++] != '|') {
132 return false;
133 }
134 if (!checkDigits(buffer, 4, &i)) {
135 return false;
136 }
137 // Ignore the last character. It is assumed to be a letter but we don't check because it's not
138 // obvious what would happen at 'z'.
139 return true;
Neil Fuller08913222015-03-31 18:24:29 +0100140}
141
142/* Return the parent directory of dirName. */
143static std::string getParentDir(const std::string& dirName) {
144 std::unique_ptr<char> mutable_dirname(strdup(dirName.c_str()));
145 return dirname(mutable_dirname.get());
146}
147
148/* Deletes a single file, symlink or directory. Called from nftw(). */
149static int deleteFn(const char* fpath, const struct stat*, int typeflag, struct FTW*) {
150 LOG(DEBUG) << "Inspecting " << fpath;
151 switch (typeflag) {
152 case FTW_F:
153 case FTW_SL:
154 LOG(DEBUG) << "Unlinking " << fpath;
155 if (unlink(fpath)) {
156 PLOG(WARNING) << "Failed to unlink file/symlink " << fpath;
157 }
158 break;
159 case FTW_D:
160 case FTW_DP:
161 LOG(DEBUG) << "Removing dir " << fpath;
162 if (rmdir(fpath)) {
163 PLOG(WARNING) << "Failed to remove dir " << fpath;
164 }
165 break;
166 default:
167 LOG(WARNING) << "Unsupported file type " << fpath << ": " << typeflag;
168 break;
169 }
170 return 0;
171}
172
Neil Fullereec2bfb2016-12-13 16:26:02 +0000173enum PathStatus { ERR, NONE, IS_DIR, NOT_DIR };
174
175static PathStatus checkPath(const std::string& path) {
176 struct stat buf;
177 if (stat(path.c_str(), &buf) != 0) {
178 if (errno != ENOENT) {
179 PLOG(WARNING) << "Unable to stat " << path;
180 return ERR;
181 }
182 return NONE;
183 }
184 return S_ISDIR(buf.st_mode) ? IS_DIR : NOT_DIR;
185}
186
Neil Fuller08913222015-03-31 18:24:29 +0100187/*
188 * Deletes dirToDelete and returns true if it is successful in removing or moving the directory out
Neil Fullereec2bfb2016-12-13 16:26:02 +0000189 * of the way. If dirToDelete does not exist this function does nothing and returns true. If
190 * dirToDelete is not a directory or cannot be accessed this method returns false.
Neil Fuller08913222015-03-31 18:24:29 +0100191 *
192 * During deletion, this function first renames the directory to a temporary name. If the temporary
193 * directory cannot be created, or the directory cannot be renamed, false is returned. After the
194 * rename, deletion of files and subdirs beneath the directory is performed on a "best effort"
195 * basis. Symlinks beneath the directory are not followed.
196 */
197static bool deleteDir(const std::string& dirToDelete) {
198 // Check whether the dir exists.
Neil Fullereec2bfb2016-12-13 16:26:02 +0000199 int pathStatus = checkPath(dirToDelete);
200 if (pathStatus == NONE) {
201 LOG(INFO) << "Path " << dirToDelete << " does not exist";
202 return true;
203 }
204 if (pathStatus != IS_DIR) {
205 LOG(WARNING) << "Path " << dirToDelete << " failed to stat() or is not a directory.";
Neil Fuller08913222015-03-31 18:24:29 +0100206 return false;
Neil Fuller08913222015-03-31 18:24:29 +0100207 }
208
209 // First, rename dirToDelete.
Neil Fullereec2bfb2016-12-13 16:26:02 +0000210
Neil Fuller08913222015-03-31 18:24:29 +0100211 std::string tempDirNameTemplate = getParentDir(dirToDelete);
212 tempDirNameTemplate += "/tempXXXXXX";
213
214 // Create an empty directory with the temporary name. For this we need a non-const char*.
215 std::vector<char> tempDirName(tempDirNameTemplate.length() + 1);
216 strcpy(&tempDirName[0], tempDirNameTemplate.c_str());
217 if (mkdtemp(&tempDirName[0]) == nullptr) {
218 PLOG(WARNING) << "Unable to create a temporary directory: " << tempDirNameTemplate;
219 return false;
220 }
221
Neil Fullereec2bfb2016-12-13 16:26:02 +0000222 // Rename dirToDelete to tempDirName (replacing the empty tempDirName directory created above).
Neil Fuller08913222015-03-31 18:24:29 +0100223 int rc = rename(dirToDelete.c_str(), &tempDirName[0]);
224 if (rc == -1) {
225 PLOG(WARNING) << "Unable to rename directory from " << dirToDelete << " to "
226 << &tempDirName[0];
227 return false;
228 }
229
230 // Recursively delete contents of tempDirName.
Neil Fullereec2bfb2016-12-13 16:26:02 +0000231
Neil Fuller08913222015-03-31 18:24:29 +0100232 rc = nftw(&tempDirName[0], deleteFn, 10 /* openFiles */,
233 FTW_DEPTH | FTW_MOUNT | FTW_PHYS);
234 if (rc == -1) {
235 LOG(INFO) << "Could not delete directory: " << &tempDirName[0];
236 }
237 return true;
238}
239
240/*
Neil Fullereec2bfb2016-12-13 16:26:02 +0000241 * Deletes the ConfigInstaller metadata directory.
242 * TODO(nfuller). http://b/31008728 Remove this when ConfigInstaller is no longer used.
243 */
244static void deleteConfigUpdaterMetadataDir(const char* dataZoneInfoDir) {
245 // Delete the update metadata
246 std::string dataUpdatesDirName(dataZoneInfoDir);
247 dataUpdatesDirName += "/updates";
248 LOG(INFO) << "Removing: " << dataUpdatesDirName;
249 bool deleted = deleteDir(dataUpdatesDirName);
250 if (!deleted) {
251 LOG(WARNING) << "Deletion of install metadata " << dataUpdatesDirName
252 << " was not successful";
253 }
254}
255
256/*
257 * Deletes the timezone update bundle directory.
258 */
259static void deleteUpdateBundleDir(std::string& bundleDirName) {
260 LOG(INFO) << "Removing: " << bundleDirName;
261 bool deleted = deleteDir(bundleDirName);
262 if (!deleted) {
263 LOG(WARNING) << "Deletion of bundle dir " << bundleDirName << " was not successful";
264 }
265}
266
267/*
Neil Fuller08913222015-03-31 18:24:29 +0100268 * After a platform update it is likely that timezone data found on the system partition will be
269 * newer than the version found in the data partition. This tool detects this case and removes the
Neil Fullereec2bfb2016-12-13 16:26:02 +0000270 * version in /data.
Neil Fuller08913222015-03-31 18:24:29 +0100271 *
272 * Note: This code is related to code in com.android.server.updates.TzDataInstallReceiver. The
273 * paths for the metadata and current timezone data must match.
274 *
275 * Typically on device the two args will be:
276 * /system/usr/share/zoneinfo /data/misc/zoneinfo
277 *
278 * See usage() for usage notes.
279 */
280int main(int argc, char* argv[]) {
281 if (argc != 3) {
282 usage();
Neil Fullereec2bfb2016-12-13 16:26:02 +0000283 return 1;
Neil Fuller08913222015-03-31 18:24:29 +0100284 }
285
286 const char* systemZoneInfoDir = argv[1];
287 const char* dataZoneInfoDir = argv[2];
288
Neil Fullereec2bfb2016-12-13 16:26:02 +0000289 // Check the bundle directory exists. If it does not, exit quickly: nothing to do.
Neil Fuller08913222015-03-31 18:24:29 +0100290 std::string dataCurrentDirName(dataZoneInfoDir);
291 dataCurrentDirName += "/current";
Neil Fullereec2bfb2016-12-13 16:26:02 +0000292 int dataCurrentDirStatus = checkPath(dataCurrentDirName);
293 if (dataCurrentDirStatus == NONE) {
294 LOG(INFO) << "timezone bundle dir " << dataCurrentDirName
295 << " does not exist. No action required.";
Neil Fuller08913222015-03-31 18:24:29 +0100296 return 0;
297 }
Neil Fullereec2bfb2016-12-13 16:26:02 +0000298 // If the bundle directory path is not a directory or we can't stat() the path, exit with a
299 // warning: either there's a problem accessing storage or the world is not as it should be;
300 // nothing to do.
301 if (dataCurrentDirStatus != IS_DIR) {
302 LOG(WARNING) << "Current bundle dir " << dataCurrentDirName
303 << " could not be accessed or is not a directory. result=" << dataCurrentDirStatus;
304 return 2;
305 }
Neil Fuller08913222015-03-31 18:24:29 +0100306
Neil Fullereec2bfb2016-12-13 16:26:02 +0000307 // Check the installed bundle version.
308 std::string bundleVersionFileName(dataCurrentDirName);
309 bundleVersionFileName += BUNDLE_VERSION_FILENAME;
310 std::vector<char> bundleVersion;
311 bundleVersion.reserve(BUNDLE_VERSION_LENGTH);
312 bool bundleVersionReadOk =
313 readBytes(bundleVersionFileName, bundleVersion.data(), BUNDLE_VERSION_LENGTH);
314 if (!bundleVersionReadOk) {
315 LOG(WARNING) << "bundle version file " << bundleVersionFileName
316 << " does not exist or is too short. Deleting bundle dir.";
317 // Implies the contents of the data partition is corrupt in some way. Try to clean up.
318 deleteConfigUpdaterMetadataDir(dataZoneInfoDir);
319 deleteUpdateBundleDir(dataCurrentDirName);
320 return 3;
321 }
322
323 if (!checkValidBundleVersion(bundleVersion.data())) {
324 LOG(WARNING) << "bundle version file " << bundleVersionFileName
325 << " is not valid. Deleting bundle dir.";
326 // Implies the contents of the data partition is corrupt in some way. Try to clean up.
327 deleteConfigUpdaterMetadataDir(dataZoneInfoDir);
328 deleteUpdateBundleDir(dataCurrentDirName);
329 return 4;
330 }
331
Neil Fullerf54cadb2017-02-09 11:53:07 +0000332 std::string actualBundleVersion =
333 std::string(bundleVersion.data(), SUPPORTED_BUNDLE_VERSION_LEN);
334 // Check the first 3 bytes of the bundle version: these are the major version (e.g. 001).
335 // It must match the one we support exactly to be ok.
336 if (strncmp(
337 &bundleVersion[0],
338 SUPPORTED_BUNDLE_MAJOR_VERSION,
339 SUPPORTED_BUNDLE_MAJOR_VERSION_LEN) != 0) {
340
Neil Fullereec2bfb2016-12-13 16:26:02 +0000341 LOG(INFO) << "bundle version file " << bundleVersionFileName
Neil Fullerf54cadb2017-02-09 11:53:07 +0000342 << " major version is not the required version " << SUPPORTED_BUNDLE_MAJOR_VERSION
343 << ", was \"" << actualBundleVersion << "\". Deleting bundle dir.";
344 // This implies there has been an OTA and the installed bundle is not compatible with the
345 // new version of Android. Remove the installed bundle.
346 deleteConfigUpdaterMetadataDir(dataZoneInfoDir);
347 deleteUpdateBundleDir(dataCurrentDirName);
348 return 5;
349 }
350
351 // Check the last 3 bytes of the bundle version: these are the minor version (e.g. 001).
352 // If the version in the bundle is < the minor version required by this device it cannot be
353 // used.
354 if (strncmp(
355 &bundleVersion[4],
356 SUPPORTED_BUNDLE_MINOR_VERSION,
357 SUPPORTED_BUNDLE_MINOR_VERSION_LEN) < 0) {
358
359 LOG(INFO) << "bundle version file " << bundleVersionFileName
360 << " minor version is not the required version " << SUPPORTED_BUNDLE_MINOR_VERSION
361 << ", was \"" << actualBundleVersion << "\". Deleting bundle dir.";
362 // This implies there has been an OTA and the installed bundle is not compatible with the
363 // new version of Android. Remove the installed bundle.
Neil Fullereec2bfb2016-12-13 16:26:02 +0000364 deleteConfigUpdaterMetadataDir(dataZoneInfoDir);
365 deleteUpdateBundleDir(dataCurrentDirName);
366 return 5;
367 }
368
369 // Read the system rules version out of the /system tzdata file.
Neil Fuller08913222015-03-31 18:24:29 +0100370 std::string systemTzDataFileName(systemZoneInfoDir);
371 systemTzDataFileName += TZDATA_FILENAME;
372 std::vector<char> systemTzDataHeader;
373 systemTzDataHeader.reserve(TZ_HEADER_LENGTH);
374 bool systemFileExists =
Neil Fullereec2bfb2016-12-13 16:26:02 +0000375 readBytes(systemTzDataFileName, systemTzDataHeader.data(), TZ_HEADER_LENGTH);
Neil Fuller08913222015-03-31 18:24:29 +0100376 if (!systemFileExists) {
Neil Fullereec2bfb2016-12-13 16:26:02 +0000377 // Implies the contents of the system partition is corrupt in some way. Nothing we can do.
378 LOG(WARNING) << systemTzDataFileName << " does not exist or could not be opened";
379 return 6;
Neil Fuller08913222015-03-31 18:24:29 +0100380 }
Neil Fullereec2bfb2016-12-13 16:26:02 +0000381 if (!checkValidTzDataHeader(systemTzDataFileName, systemTzDataHeader.data())) {
382 // Implies the contents of the system partition is corrupt in some way. Nothing we can do.
383 LOG(WARNING) << systemTzDataFileName << " does not have a valid header.";
384 return 7;
Neil Fuller08913222015-03-31 18:24:29 +0100385 }
386
Neil Fullereec2bfb2016-12-13 16:26:02 +0000387 // Compare the bundle rules version against the system rules version.
388 if (strncmp(
389 &systemTzDataHeader[TZ_DATA_HEADER_PREFIX_LEN],
390 &bundleVersion[BUNDLE_VERSION_RULES_IDX],
391 RULES_VERSION_LEN) <= 0) {
392 LOG(INFO) << "Found an installed bundle but it is valid. No action taken.";
393 // Implies there is an installed update, but it is good.
394 return 0;
395 }
396
397 // Implies there has been an OTA and the system version of the timezone rules is now newer
398 // than the version installed in /data. Remove the installed bundle.
399 LOG(INFO) << "timezone bundle in " << dataCurrentDirName << " is older than data in "
400 << systemTzDataFileName << "; fixing...";
401
402 deleteConfigUpdaterMetadataDir(dataZoneInfoDir);
403 deleteUpdateBundleDir(dataCurrentDirName);
Neil Fuller08913222015-03-31 18:24:29 +0100404 return 0;
405}