1// Copyright 2016 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// Parse the "tzdata" packed timezone file used on Android.
6// The format is lifted from ZoneInfoDB.java and ZoneInfo.java in
7// java/libcore/util in the AOSP.
8
9package time
10
11import (
12	"errors"
13	"syscall"
14)
15
16var platformZoneSources = []string{
17	"/system/usr/share/zoneinfo/tzdata",
18	"/data/misc/zoneinfo/current/tzdata",
19}
20
21func initLocal() {
22	// TODO(elias.naur): getprop persist.sys.timezone
23	localLoc = *UTC
24}
25
26func init() {
27	loadTzinfoFromTzdata = androidLoadTzinfoFromTzdata
28}
29
30var allowGorootSource = true
31
32func gorootZoneSource(goroot string) (string, bool) {
33	if goroot == "" || !allowGorootSource {
34		return "", false
35	}
36	return goroot + "/lib/time/zoneinfo.zip", true
37}
38
39func androidLoadTzinfoFromTzdata(file, name string) ([]byte, error) {
40	const (
41		headersize = 12 + 3*4
42		namesize   = 40
43		entrysize  = namesize + 3*4
44	)
45	if len(name) > namesize {
46		return nil, errors.New(name + " is longer than the maximum zone name length (40 bytes)")
47	}
48	fd, err := open(file)
49	if err != nil {
50		return nil, err
51	}
52	defer closefd(fd)
53
54	buf := make([]byte, headersize)
55	if err := preadn(fd, buf, 0); err != nil {
56		return nil, errors.New("corrupt tzdata file " + file)
57	}
58	d := dataIO{buf, false}
59	if magic := d.read(6); string(magic) != "tzdata" {
60		return nil, errors.New("corrupt tzdata file " + file)
61	}
62	d = dataIO{buf[12:], false}
63	indexOff, _ := d.big4()
64	dataOff, _ := d.big4()
65	indexSize := dataOff - indexOff
66	entrycount := indexSize / entrysize
67	buf = make([]byte, indexSize)
68	if err := preadn(fd, buf, int(indexOff)); err != nil {
69		return nil, errors.New("corrupt tzdata file " + file)
70	}
71	for i := 0; i < int(entrycount); i++ {
72		entry := buf[i*entrysize : (i+1)*entrysize]
73		// len(name) <= namesize is checked at function entry
74		if string(entry[:len(name)]) != name {
75			continue
76		}
77		d := dataIO{entry[namesize:], false}
78		off, _ := d.big4()
79		size, _ := d.big4()
80		buf := make([]byte, size)
81		if err := preadn(fd, buf, int(off+dataOff)); err != nil {
82			return nil, errors.New("corrupt tzdata file " + file)
83		}
84		return buf, nil
85	}
86	return nil, syscall.ENOENT
87}
88