Home | History | Annotate | Download | only in EGL
      1 /*
      2  ** Copyright 2017, 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 "FileBlobCache.h"
     18 
     19 #include <errno.h>
     20 #include <inttypes.h>
     21 #include <log/log.h>
     22 #include <sys/mman.h>
     23 #include <sys/stat.h>
     24 
     25 
     26 // Cache file header
     27 static const char* cacheFileMagic = "EGL$";
     28 static const size_t cacheFileHeaderSize = 8;
     29 
     30 namespace android {
     31 
     32 static uint32_t crc32c(const uint8_t* buf, size_t len) {
     33     const uint32_t polyBits = 0x82F63B78;
     34     uint32_t r = 0;
     35     for (size_t i = 0; i < len; i++) {
     36         r ^= buf[i];
     37         for (int j = 0; j < 8; j++) {
     38             if (r & 1) {
     39                 r = (r >> 1) ^ polyBits;
     40             } else {
     41                 r >>= 1;
     42             }
     43         }
     44     }
     45     return r;
     46 }
     47 
     48 FileBlobCache::FileBlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize,
     49         const std::string& filename)
     50         : BlobCache(maxKeySize, maxValueSize, maxTotalSize)
     51         , mFilename(filename) {
     52     if (mFilename.length() > 0) {
     53         size_t headerSize = cacheFileHeaderSize;
     54 
     55         int fd = open(mFilename.c_str(), O_RDONLY, 0);
     56         if (fd == -1) {
     57             if (errno != ENOENT) {
     58                 ALOGE("error opening cache file %s: %s (%d)", mFilename.c_str(),
     59                         strerror(errno), errno);
     60             }
     61             return;
     62         }
     63 
     64         struct stat statBuf;
     65         if (fstat(fd, &statBuf) == -1) {
     66             ALOGE("error stat'ing cache file: %s (%d)", strerror(errno), errno);
     67             close(fd);
     68             return;
     69         }
     70 
     71         // Sanity check the size before trying to mmap it.
     72         size_t fileSize = statBuf.st_size;
     73         if (fileSize > mMaxTotalSize * 2) {
     74             ALOGE("cache file is too large: %#" PRIx64,
     75                   static_cast<off64_t>(statBuf.st_size));
     76             close(fd);
     77             return;
     78         }
     79 
     80         uint8_t* buf = reinterpret_cast<uint8_t*>(mmap(NULL, fileSize,
     81                 PROT_READ, MAP_PRIVATE, fd, 0));
     82         if (buf == MAP_FAILED) {
     83             ALOGE("error mmaping cache file: %s (%d)", strerror(errno),
     84                     errno);
     85             close(fd);
     86             return;
     87         }
     88 
     89         // Check the file magic and CRC
     90         size_t cacheSize = fileSize - headerSize;
     91         if (memcmp(buf, cacheFileMagic, 4) != 0) {
     92             ALOGE("cache file has bad mojo");
     93             close(fd);
     94             return;
     95         }
     96         uint32_t* crc = reinterpret_cast<uint32_t*>(buf + 4);
     97         if (crc32c(buf + headerSize, cacheSize) != *crc) {
     98             ALOGE("cache file failed CRC check");
     99             close(fd);
    100             return;
    101         }
    102 
    103         int err = unflatten(buf + headerSize, cacheSize);
    104         if (err < 0) {
    105             ALOGE("error reading cache contents: %s (%d)", strerror(-err),
    106                     -err);
    107             munmap(buf, fileSize);
    108             close(fd);
    109             return;
    110         }
    111 
    112         munmap(buf, fileSize);
    113         close(fd);
    114     }
    115 }
    116 
    117 void FileBlobCache::writeToFile() {
    118     if (mFilename.length() > 0) {
    119         size_t cacheSize = getFlattenedSize();
    120         size_t headerSize = cacheFileHeaderSize;
    121         const char* fname = mFilename.c_str();
    122 
    123         // Try to create the file with no permissions so we can write it
    124         // without anyone trying to read it.
    125         int fd = open(fname, O_CREAT | O_EXCL | O_RDWR, 0);
    126         if (fd == -1) {
    127             if (errno == EEXIST) {
    128                 // The file exists, delete it and try again.
    129                 if (unlink(fname) == -1) {
    130                     // No point in retrying if the unlink failed.
    131                     ALOGE("error unlinking cache file %s: %s (%d)", fname,
    132                             strerror(errno), errno);
    133                     return;
    134                 }
    135                 // Retry now that we've unlinked the file.
    136                 fd = open(fname, O_CREAT | O_EXCL | O_RDWR, 0);
    137             }
    138             if (fd == -1) {
    139                 ALOGE("error creating cache file %s: %s (%d)", fname,
    140                         strerror(errno), errno);
    141                 return;
    142             }
    143         }
    144 
    145         size_t fileSize = headerSize + cacheSize;
    146 
    147         uint8_t* buf = new uint8_t [fileSize];
    148         if (!buf) {
    149             ALOGE("error allocating buffer for cache contents: %s (%d)",
    150                     strerror(errno), errno);
    151             close(fd);
    152             unlink(fname);
    153             return;
    154         }
    155 
    156         int err = flatten(buf + headerSize, cacheSize);
    157         if (err < 0) {
    158             ALOGE("error writing cache contents: %s (%d)", strerror(-err),
    159                     -err);
    160             delete [] buf;
    161             close(fd);
    162             unlink(fname);
    163             return;
    164         }
    165 
    166         // Write the file magic and CRC
    167         memcpy(buf, cacheFileMagic, 4);
    168         uint32_t* crc = reinterpret_cast<uint32_t*>(buf + 4);
    169         *crc = crc32c(buf + headerSize, cacheSize);
    170 
    171         if (write(fd, buf, fileSize) == -1) {
    172             ALOGE("error writing cache file: %s (%d)", strerror(errno),
    173                     errno);
    174             delete [] buf;
    175             close(fd);
    176             unlink(fname);
    177             return;
    178         }
    179 
    180         delete [] buf;
    181         fchmod(fd, S_IRUSR);
    182         close(fd);
    183     }
    184 }
    185 
    186 }
    187