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. 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 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|. 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|. 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. 163 void InitStorage(int data_dir_fd) { 164 g_data_dir_fd = data_dir_fd; 165 } 166 167 namespace nvram { 168 namespace storage { 169 170 Status LoadHeader(Blob* blob) { 171 return LoadFile(kHeaderFileName, blob); 172 } 173 174 Status StoreHeader(const Blob& blob) { 175 return StoreFile(kHeaderFileName, blob); 176 } 177 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 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 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