1 /*
2 * Copyright (C) 2016 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 <nvram/core/storage.h>
18
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <stdio.h>
22 #include <sys/stat.h>
23 #include <sys/types.h>
24 #include <unistd.h>
25
26 #include <android-base/file.h>
27 #include <android-base/logging.h>
28 #include <android-base/unique_fd.h>
29
30 #include <nvram/core/logger.h>
31
32 // An NVRAM storage layer implementation backed by the file system.
33 //
34 // NOTE: This does not meet the tamper evidence requirements for
35 // access-controlled NVRAM implementations, since the file system can't provide
36 // sufficient protection against tampering by attackers.
37
38 namespace {
39
40 // Name of the storage object holding the header.
41 const char kHeaderFileName[] = "header";
42
43 // Pattern for space data storage object names.
44 const char kSpaceDataFileNamePattern[] = "space_%08x";
45
46 // Temporary file name used in write-rename atomic write operations.
47 const char kTempFileName[] = "temp";
48
49 // Maximum size of objects we're willing to read and write.
50 const off_t kMaxFileSize = 2048;
51
52 // Buffer size for formatting names.
53 using NameBuffer = char[16];
54
55 // Global data directory descriptor.
56 int g_data_dir_fd = -1;
57
58 // Formats the storage object name for the given space index.
FormatSpaceFileName(NameBuffer name,uint32_t index)59 bool FormatSpaceFileName(NameBuffer name, uint32_t index) {
60 int ret =
61 snprintf(name, sizeof(NameBuffer), kSpaceDataFileNamePattern, index);
62 return ret >= 0 && ret < static_cast<int>(sizeof(NameBuffer));
63 };
64
DeleteFile(const char * name)65 nvram::storage::Status DeleteFile(const char* name) {
66 if (TEMP_FAILURE_RETRY(unlinkat(g_data_dir_fd, name, 0))) {
67 if (errno == ENOENT) {
68 return nvram::storage::Status::kNotFound;
69 }
70 PLOG(ERROR) << "Failed to remove " << name;
71 return nvram::storage::Status::kStorageError;
72 }
73
74 return nvram::storage::Status::kSuccess;
75 }
76
77 // Loads the storage object identified by |name|.
LoadFile(const char * name,nvram::Blob * blob)78 nvram::storage::Status LoadFile(const char* name, nvram::Blob* blob) {
79 android::base::unique_fd data_file_fd(
80 TEMP_FAILURE_RETRY(openat(g_data_dir_fd, name, O_RDONLY)));
81 if (data_file_fd.get() < 0) {
82 if (errno == ENOENT) {
83 return nvram::storage::Status::kNotFound;
84 }
85 PLOG(ERROR) << "Failed to open " << name;
86 return nvram::storage::Status::kStorageError;
87 }
88
89 struct stat data_file_stat;
90 if (TEMP_FAILURE_RETRY(fstat(data_file_fd.get(), &data_file_stat))) {
91 PLOG(ERROR) << "Failed to stat " << name;
92 return nvram::storage::Status::kStorageError;
93 }
94
95 if (data_file_stat.st_size > kMaxFileSize) {
96 LOG(ERROR) << "Bad size for " << name << ":" << data_file_stat.st_size;
97 return nvram::storage::Status::kStorageError;
98 }
99
100 if (!blob->Resize(data_file_stat.st_size)) {
101 LOG(ERROR) << "Failed to allocate read buffer for " << name;
102 return nvram::storage::Status::kStorageError;
103 }
104
105 if (!android::base::ReadFully(data_file_fd.get(), blob->data(),
106 blob->size())) {
107 PLOG(ERROR) << "Failed to read " << name;
108 return nvram::storage::Status::kStorageError;
109 }
110
111 return nvram::storage::Status::kSuccess;
112 }
113
114 // Writes blob to the storage object indicated by |name|.
StoreFile(const char * name,const nvram::Blob & blob)115 nvram::storage::Status StoreFile(const char* name, const nvram::Blob& blob) {
116 android::base::unique_fd data_file_fd(TEMP_FAILURE_RETRY(
117 openat(g_data_dir_fd, kTempFileName, O_WRONLY | O_CREAT | O_TRUNC,
118 S_IRUSR | S_IWUSR)));
119 if (data_file_fd.get() < 0) {
120 if (errno == ENOENT) {
121 return nvram::storage::Status::kNotFound;
122 }
123 PLOG(ERROR) << "Failed to open " << kTempFileName;
124 return nvram::storage::Status::kStorageError;
125 }
126
127 if (!android::base::WriteFully(data_file_fd.get(), blob.data(),
128 blob.size())) {
129 PLOG(ERROR) << "Failed to write " << kTempFileName;
130 DeleteFile(kTempFileName);
131 return nvram::storage::Status::kStorageError;
132 }
133
134 // Force the file contents to be written to disk.
135 if (TEMP_FAILURE_RETRY(fdatasync(data_file_fd.get()))) {
136 PLOG(ERROR) << "Failed to sync " << kTempFileName;
137 DeleteFile(kTempFileName);
138 return nvram::storage::Status::kStorageError;
139 }
140
141 data_file_fd.reset();
142
143 // Move the file into place.
144 if (TEMP_FAILURE_RETRY(
145 renameat(g_data_dir_fd, kTempFileName, g_data_dir_fd, name))) {
146 PLOG(ERROR) << "Failed to move " << kTempFileName << " to " << name;
147 DeleteFile(kTempFileName);
148 return nvram::storage::Status::kStorageError;
149 }
150
151 // Force the directory meta data to be written to disk.
152 if (TEMP_FAILURE_RETRY(fsync(g_data_dir_fd))) {
153 PLOG(ERROR) << "Failed to sync data directory";
154 return nvram::storage::Status::kStorageError;
155 }
156
157 return nvram::storage::Status::kSuccess;
158 }
159
160 } // namespace
161
162 // Initializes the storage layer with the provided data directory descriptor.
InitStorage(int data_dir_fd)163 void InitStorage(int data_dir_fd) {
164 g_data_dir_fd = data_dir_fd;
165 }
166
167 namespace nvram {
168 namespace storage {
169
LoadHeader(Blob * blob)170 Status LoadHeader(Blob* blob) {
171 return LoadFile(kHeaderFileName, blob);
172 }
173
StoreHeader(const Blob & blob)174 Status StoreHeader(const Blob& blob) {
175 return StoreFile(kHeaderFileName, blob);
176 }
177
LoadSpace(uint32_t index,Blob * blob)178 Status LoadSpace(uint32_t index, Blob* blob) {
179 NameBuffer name;
180 if (!FormatSpaceFileName(name, index)) {
181 return Status::kStorageError;
182 }
183 return LoadFile(name, blob);
184 }
185
StoreSpace(uint32_t index,const Blob & blob)186 Status StoreSpace(uint32_t index, const Blob& blob) {
187 NameBuffer name;
188 if (!FormatSpaceFileName(name, index)) {
189 return Status::kStorageError;
190 }
191 return StoreFile(name, blob);
192 }
193
DeleteSpace(uint32_t index)194 Status DeleteSpace(uint32_t index) {
195 NameBuffer name;
196 if (!FormatSpaceFileName(name, index)) {
197 return Status::kStorageError;
198 }
199
200 return DeleteFile(name);
201 }
202
203 } // namespace storage
204 } // namespace nvram
205