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 
crc32c(const uint8_t * buf,size_t len)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 
FileBlobCache(size_t maxKeySize,size_t maxValueSize,size_t maxTotalSize,const std::string & filename)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 
writeToFile()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