1*1a96fba6SXin Li // Copyright 2018 The Chromium OS Authors. All rights reserved.
2*1a96fba6SXin Li // Use of this source code is governed by a BSD-style license that can be
3*1a96fba6SXin Li // found in the LICENSE file.
4*1a96fba6SXin Li
5*1a96fba6SXin Li #include "brillo/timezone/tzif_parser.h"
6*1a96fba6SXin Li
7*1a96fba6SXin Li #include <arpa/inet.h>
8*1a96fba6SXin Li #include <stdint.h>
9*1a96fba6SXin Li #include <string.h>
10*1a96fba6SXin Li #include <utility>
11*1a96fba6SXin Li #include <vector>
12*1a96fba6SXin Li
13*1a96fba6SXin Li #include <base/files/file.h>
14*1a96fba6SXin Li #include <base/files/file_path.h>
15*1a96fba6SXin Li #include <base/logging.h>
16*1a96fba6SXin Li #include <base/stl_util.h>
17*1a96fba6SXin Li #include <base/strings/string_util.h>
18*1a96fba6SXin Li
19*1a96fba6SXin Li namespace {
20*1a96fba6SXin Li
21*1a96fba6SXin Li struct tzif_header {
22*1a96fba6SXin Li char magic[4];
23*1a96fba6SXin Li char version;
24*1a96fba6SXin Li char reserved[15];
25*1a96fba6SXin Li int32_t ttisgmtcnt;
26*1a96fba6SXin Li int32_t ttisstdcnt;
27*1a96fba6SXin Li int32_t leapcnt;
28*1a96fba6SXin Li int32_t timecnt;
29*1a96fba6SXin Li int32_t typecnt;
30*1a96fba6SXin Li int32_t charcnt;
31*1a96fba6SXin Li };
32*1a96fba6SXin Li
ReadInt(base::File * file,int32_t * out_int)33*1a96fba6SXin Li bool ReadInt(base::File* file, int32_t* out_int) {
34*1a96fba6SXin Li DCHECK(out_int);
35*1a96fba6SXin Li int32_t buf;
36*1a96fba6SXin Li int read = file->ReadAtCurrentPos(reinterpret_cast<char*>(&buf), sizeof(buf));
37*1a96fba6SXin Li if (read != sizeof(buf)) {
38*1a96fba6SXin Li return false;
39*1a96fba6SXin Li }
40*1a96fba6SXin Li // Values are stored in network byte order (highest-order byte first).
41*1a96fba6SXin Li // We probably need to convert them to match the endianness of our system.
42*1a96fba6SXin Li *out_int = ntohl(buf);
43*1a96fba6SXin Li return true;
44*1a96fba6SXin Li }
45*1a96fba6SXin Li
ParseTzifHeader(base::File * tzfile,struct tzif_header * header)46*1a96fba6SXin Li bool ParseTzifHeader(base::File* tzfile, struct tzif_header* header) {
47*1a96fba6SXin Li DCHECK(header);
48*1a96fba6SXin Li int read = tzfile->ReadAtCurrentPos(header->magic, sizeof(header->magic));
49*1a96fba6SXin Li if (read != sizeof(header->magic)) {
50*1a96fba6SXin Li return false;
51*1a96fba6SXin Li }
52*1a96fba6SXin Li if (memcmp(header->magic, "TZif", 4) != 0) {
53*1a96fba6SXin Li return false;
54*1a96fba6SXin Li }
55*1a96fba6SXin Li
56*1a96fba6SXin Li read = tzfile->ReadAtCurrentPos(&header->version, sizeof(header->version));
57*1a96fba6SXin Li if (read != sizeof(header->version)) {
58*1a96fba6SXin Li return false;
59*1a96fba6SXin Li }
60*1a96fba6SXin Li if (header->version != '\0' && header->version != '2' &&
61*1a96fba6SXin Li header->version != '3') {
62*1a96fba6SXin Li return false;
63*1a96fba6SXin Li }
64*1a96fba6SXin Li
65*1a96fba6SXin Li read = tzfile->ReadAtCurrentPos(header->reserved, sizeof(header->reserved));
66*1a96fba6SXin Li if (read != sizeof(header->reserved)) {
67*1a96fba6SXin Li return false;
68*1a96fba6SXin Li }
69*1a96fba6SXin Li for (size_t i = 0; i < sizeof(header->reserved); i++) {
70*1a96fba6SXin Li if (header->reserved[i] != 0) {
71*1a96fba6SXin Li return false;
72*1a96fba6SXin Li }
73*1a96fba6SXin Li }
74*1a96fba6SXin Li
75*1a96fba6SXin Li if (!ReadInt(tzfile, &header->ttisgmtcnt) || header->ttisgmtcnt < 0) {
76*1a96fba6SXin Li return false;
77*1a96fba6SXin Li }
78*1a96fba6SXin Li if (!ReadInt(tzfile, &header->ttisstdcnt) || header->ttisstdcnt < 0) {
79*1a96fba6SXin Li return false;
80*1a96fba6SXin Li }
81*1a96fba6SXin Li if (!ReadInt(tzfile, &header->leapcnt) || header->leapcnt < 0) {
82*1a96fba6SXin Li return false;
83*1a96fba6SXin Li }
84*1a96fba6SXin Li if (!ReadInt(tzfile, &header->timecnt) || header->timecnt < 0) {
85*1a96fba6SXin Li return false;
86*1a96fba6SXin Li }
87*1a96fba6SXin Li if (!ReadInt(tzfile, &header->typecnt) || header->typecnt < 0) {
88*1a96fba6SXin Li return false;
89*1a96fba6SXin Li }
90*1a96fba6SXin Li if (!ReadInt(tzfile, &header->charcnt) || header->charcnt < 0) {
91*1a96fba6SXin Li return false;
92*1a96fba6SXin Li }
93*1a96fba6SXin Li return true;
94*1a96fba6SXin Li }
95*1a96fba6SXin Li
96*1a96fba6SXin Li } // namespace
97*1a96fba6SXin Li
98*1a96fba6SXin Li namespace brillo {
99*1a96fba6SXin Li
100*1a96fba6SXin Li namespace timezone {
101*1a96fba6SXin Li
GetPosixTimezone(const base::FilePath & tzif_path)102*1a96fba6SXin Li base::Optional<std::string> GetPosixTimezone(const base::FilePath& tzif_path) {
103*1a96fba6SXin Li base::FilePath to_parse;
104*1a96fba6SXin Li if (tzif_path.IsAbsolute()) {
105*1a96fba6SXin Li to_parse = tzif_path;
106*1a96fba6SXin Li } else {
107*1a96fba6SXin Li to_parse = base::FilePath("/usr/share/zoneinfo").Append(tzif_path);
108*1a96fba6SXin Li }
109*1a96fba6SXin Li base::File tzfile(to_parse, base::File::FLAG_OPEN | base::File::FLAG_READ);
110*1a96fba6SXin Li struct tzif_header first_header;
111*1a96fba6SXin Li if (!tzfile.IsValid() || !ParseTzifHeader(&tzfile, &first_header)) {
112*1a96fba6SXin Li return base::nullopt;
113*1a96fba6SXin Li }
114*1a96fba6SXin Li
115*1a96fba6SXin Li if (first_header.version == '\0') {
116*1a96fba6SXin Li // Generating a POSIX-style TZ string from a TZif version 1 file is hard;
117*1a96fba6SXin Li // TZ strings need relative dates (1st Sunday in March, 1st Sunday in Nov,
118*1a96fba6SXin Li // etc.), but the version 1 files only give absolute dates for each
119*1a96fba6SXin Li // year up to 2037. Fortunately version 1 files are no longer created by
120*1a96fba6SXin Li // the newer versions of the timezone-data package.
121*1a96fba6SXin Li //
122*1a96fba6SXin Li // Because of this, we're not going to try and handle this, and instead just
123*1a96fba6SXin Li // return an error.
124*1a96fba6SXin Li return base::nullopt;
125*1a96fba6SXin Li }
126*1a96fba6SXin Li
127*1a96fba6SXin Li // TZif versions 2 and 3 embed a POSIX-style TZ string after their
128*1a96fba6SXin Li // contents. We read that out and return it.
129*1a96fba6SXin Li
130*1a96fba6SXin Li // Skip past the first body section and all of the second section. See
131*1a96fba6SXin Li // 'man tzfile' for an explanation of these offset values.
132*1a96fba6SXin Li int64_t first_body_size =
133*1a96fba6SXin Li 4 * first_header.timecnt + 1 * first_header.timecnt +
134*1a96fba6SXin Li (4 + 1 + 1) * first_header.typecnt + 1 * first_header.charcnt +
135*1a96fba6SXin Li (4 + 4) * first_header.leapcnt + 1 * first_header.ttisstdcnt +
136*1a96fba6SXin Li 1 * first_header.ttisgmtcnt;
137*1a96fba6SXin Li tzfile.Seek(base::File::FROM_CURRENT, first_body_size);
138*1a96fba6SXin Li
139*1a96fba6SXin Li struct tzif_header second_header;
140*1a96fba6SXin Li if (!ParseTzifHeader(&tzfile, &second_header)) {
141*1a96fba6SXin Li return base::nullopt;
142*1a96fba6SXin Li }
143*1a96fba6SXin Li
144*1a96fba6SXin Li int64_t second_body_size =
145*1a96fba6SXin Li 8 * second_header.timecnt + 1 * second_header.timecnt +
146*1a96fba6SXin Li (4 + 1 + 1) * second_header.typecnt + 1 * second_header.charcnt +
147*1a96fba6SXin Li (8 + 4) * second_header.leapcnt + 1 * second_header.ttisstdcnt +
148*1a96fba6SXin Li 1 * second_header.ttisgmtcnt;
149*1a96fba6SXin Li int64_t offset = tzfile.Seek(base::File::FROM_CURRENT, second_body_size);
150*1a96fba6SXin Li
151*1a96fba6SXin Li std::string time_string(tzfile.GetLength() - offset, '\0');
152*1a96fba6SXin Li if (tzfile.ReadAtCurrentPos(base::data(time_string), time_string.size()) !=
153*1a96fba6SXin Li time_string.size()) {
154*1a96fba6SXin Li return base::nullopt;
155*1a96fba6SXin Li }
156*1a96fba6SXin Li
157*1a96fba6SXin Li // According to the spec, the embedded string is enclosed by '\n' characters.
158*1a96fba6SXin Li base::TrimWhitespaceASCII(time_string, base::TRIM_ALL, &time_string);
159*1a96fba6SXin Li return std::move(time_string);
160*1a96fba6SXin Li }
161*1a96fba6SXin Li
162*1a96fba6SXin Li } // namespace timezone
163*1a96fba6SXin Li
164*1a96fba6SXin Li } // namespace brillo
165