• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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