func zonesReadDir(dirName string, zones Zones) error { dir, err := ioutil.ReadDir(dirName) if err != nil { log.Println("Could not read", dirName, ":", err) return err } seenZones := map[string]bool{} var parseErr error for _, file := range dir { fileName := file.Name() if !strings.HasSuffix(strings.ToLower(fileName), ".json") { continue } zoneName := zoneNameFromFile(fileName) seenZones[zoneName] = true if zone, ok := zones[zoneName]; !ok || file.ModTime().After(zone.LastRead) { if ok { logPrintf("Reloading %s\n", fileName) } else { logPrintf("Reading new file %s\n", fileName) } //log.Println("FILE:", i, file, zoneName) config, err := readZoneFile(zoneName, path.Join(dirName, fileName)) if config == nil || err != nil { parseErr = fmt.Errorf("Error reading zone '%s': %s", zoneName, err) log.Println(parseErr.Error()) continue } config.LastRead = file.ModTime() addHandler(zones, zoneName, config) } } for zoneName, zone := range zones { if zoneName == "pgeodns" { continue } if ok, _ := seenZones[zoneName]; ok { continue } log.Println("Removing zone", zone.Origin) zone.Close() dns.HandleRemove(zoneName) delete(zones, zoneName) } return parseErr }
func zonesReadDir(dirName string, zones Zones) error { dir, err := ioutil.ReadDir(dirName) if err != nil { log.Println("Could not read", dirName, ":", err) return err } seenZones := map[string]bool{} var parseErr error for _, file := range dir { fileName := file.Name() if !strings.HasSuffix(strings.ToLower(fileName), ".json") || strings.HasPrefix(path.Base(fileName), ".") || file.IsDir() { continue } zoneName := zoneNameFromFile(fileName) seenZones[zoneName] = true if _, ok := lastRead[zoneName]; !ok || file.ModTime().After(lastRead[zoneName].time) { modTime := file.ModTime() if ok { logPrintf("Reloading %s\n", fileName) lastRead[zoneName].time = modTime } else { logPrintf("Reading new file %s\n", fileName) lastRead[zoneName] = &ZoneReadRecord{time: modTime} } filename := path.Join(dirName, fileName) // Check the sha256 of the file has not changed. It's worth an explanation of // why there isn't a TOCTOU race here. Conceivably after checking whether the // SHA has changed, the contents then change again before we actually load // the JSON. This can occur in two situations: // // 1. The SHA has not changed when we read the file for the SHA, but then // changes before we process the JSON // // 2. The SHA has changed when we read the file for the SHA, but then changes // again before we process the JSON // // In circumstance (1) we won't reread the file the first time, but the subsequent // change should alter the mtime again, causing us to reread it. This reflects // the fact there were actually two changes. // // In circumstance (2) we have already reread the file once, and then when the // contents are changed the mtime changes again // // Provided files are replaced atomically, this should be OK. If files are not // replaced atomically we have other problems (e.g. partial reads). sha256 := sha256File(filename) if lastRead[zoneName].hash == sha256 { logPrintf("Skipping new file %s as hash is unchanged\n", filename) continue } config, err := readZoneFile(zoneName, filename) if config == nil || err != nil { parseErr = fmt.Errorf("Error reading zone '%s': %s", zoneName, err) log.Println(parseErr.Error()) continue } (lastRead[zoneName]).hash = sha256 addHandler(zones, zoneName, config) } } for zoneName, zone := range zones { if zoneName == "pgeodns" { continue } if ok, _ := seenZones[zoneName]; ok { continue } log.Println("Removing zone", zone.Origin) delete(lastRead, zoneName) zone.Close() dns.HandleRemove(zoneName) delete(zones, zoneName) } return parseErr }