Home | History | Annotate | Download | only in hal
      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