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