1/*
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
17#include "persistent_integer.h"
18
19#include <fcntl.h>
20
21#include <base/logging.h>
22#include <base/posix/eintr_wrapper.h>
23
24#include "constants.h"
25
26namespace chromeos_metrics {
27
28PersistentInteger::PersistentInteger(const std::string& name,
29                                     const base::FilePath& directory)
30    : value_(0),
31      version_(kVersion),
32      name_(name),
33      backing_file_path_(directory.Append(name_)),
34      synced_(false) {}
35
36PersistentInteger::~PersistentInteger() {}
37
38void PersistentInteger::Set(int64_t value) {
39  value_ = value;
40  Write();
41}
42
43int64_t PersistentInteger::Get() {
44  // If not synced, then read.  If the read fails, it's a good idea to write.
45  if (!synced_ && !Read())
46    Write();
47  return value_;
48}
49
50int64_t PersistentInteger::GetAndClear() {
51  int64_t v = Get();
52  Set(0);
53  return v;
54}
55
56void PersistentInteger::Add(int64_t x) {
57  Set(Get() + x);
58}
59
60void PersistentInteger::Write() {
61  int fd = HANDLE_EINTR(open(backing_file_path_.value().c_str(),
62                             O_WRONLY | O_CREAT | O_TRUNC,
63                             S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH));
64  PCHECK(fd >= 0) << "cannot open " << backing_file_path_.value()
65                  << " for writing";
66  PCHECK((HANDLE_EINTR(write(fd, &version_, sizeof(version_))) ==
67          sizeof(version_)) &&
68         (HANDLE_EINTR(write(fd, &value_, sizeof(value_))) ==
69          sizeof(value_)))
70      << "cannot write to " << backing_file_path_.value();
71  close(fd);
72  synced_ = true;
73}
74
75bool PersistentInteger::Read() {
76  int fd = HANDLE_EINTR(open(backing_file_path_.value().c_str(), O_RDONLY));
77  if (fd < 0) {
78    PLOG(WARNING) << "cannot open " << backing_file_path_.value()
79                  << " for reading";
80    return false;
81  }
82  int32_t version;
83  int64_t value;
84  bool read_succeeded = false;
85  if (HANDLE_EINTR(read(fd, &version, sizeof(version))) == sizeof(version) &&
86      version == version_ &&
87      HANDLE_EINTR(read(fd, &value, sizeof(value))) == sizeof(value)) {
88    value_ = value;
89    read_succeeded = true;
90    synced_ = true;
91  }
92  close(fd);
93  return read_succeeded;
94}
95
96}  // namespace chromeos_metrics
97