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