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 38namespace { 39 40// Name of the storage object holding the header. 41const char kHeaderFileName[] = "header"; 42 43// Pattern for space data storage object names. 44const char kSpaceDataFileNamePattern[] = "space_%08x"; 45 46// Temporary file name used in write-rename atomic write operations. 47const char kTempFileName[] = "temp"; 48 49// Maximum size of objects we're willing to read and write. 50const off_t kMaxFileSize = 2048; 51 52// Buffer size for formatting names. 53using NameBuffer = char[16]; 54 55// Global data directory descriptor. 56int g_data_dir_fd = -1; 57 58// Formats the storage object name for the given space index. 59bool 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 65nvram::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|. 78nvram::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|. 115nvram::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. 163void InitStorage(int data_dir_fd) { 164 g_data_dir_fd = data_dir_fd; 165} 166 167namespace nvram { 168namespace storage { 169 170Status LoadHeader(Blob* blob) { 171 return LoadFile(kHeaderFileName, blob); 172} 173 174Status StoreHeader(const Blob& blob) { 175 return StoreFile(kHeaderFileName, blob); 176} 177 178Status 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 186Status 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 194Status 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