xref: /aosp_15_r20/frameworks/native/opengl/libs/EGL/FileBlobCache.cpp (revision 38e8c45f13ce32b0dcecb25141ffecaf386fa17f)
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 #include <zlib.h>
31 
32 // Cache file header
33 static const char* cacheFileMagic = "EGL$";
34 static const size_t cacheFileHeaderSize = 8;
35 
36 namespace android {
37 
GenerateCRC32(const uint8_t * data,size_t size)38 uint32_t GenerateCRC32(const uint8_t *data, size_t size)
39 {
40     const unsigned long initialValue = crc32_z(0u, nullptr, 0u);
41     return static_cast<uint32_t>(crc32_z(initialValue, data, size));
42 }
43 
FileBlobCache(size_t maxKeySize,size_t maxValueSize,size_t maxTotalSize,const std::string & filename)44 FileBlobCache::FileBlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize,
45         const std::string& filename)
46         : BlobCache(maxKeySize, maxValueSize, maxTotalSize)
47         , mFilename(filename) {
48     ATRACE_CALL();
49 
50     if (mFilename.length() > 0) {
51         size_t headerSize = cacheFileHeaderSize;
52 
53         int fd = open(mFilename.c_str(), O_RDONLY, 0);
54         if (fd == -1) {
55             if (errno != ENOENT) {
56                 ALOGE("error opening cache file %s: %s (%d)", mFilename.c_str(),
57                         strerror(errno), errno);
58             }
59             return;
60         }
61 
62         struct stat statBuf;
63         if (fstat(fd, &statBuf) == -1) {
64             ALOGE("error stat'ing cache file: %s (%d)", strerror(errno), errno);
65             close(fd);
66             return;
67         }
68 
69         // Check the size before trying to mmap it.
70         size_t fileSize = statBuf.st_size;
71         if (fileSize > mMaxTotalSize * 2) {
72             ALOGE("cache file is too large: %#" PRIx64,
73                   static_cast<off64_t>(statBuf.st_size));
74             close(fd);
75             return;
76         }
77 
78         uint8_t* buf = reinterpret_cast<uint8_t*>(mmap(nullptr, fileSize,
79                 PROT_READ, MAP_PRIVATE, fd, 0));
80         if (buf == MAP_FAILED) {
81             ALOGE("error mmaping cache file: %s (%d)", strerror(errno),
82                     errno);
83             close(fd);
84             return;
85         }
86 
87         // Check the file magic and CRC
88         size_t cacheSize = fileSize - headerSize;
89         if (memcmp(buf, cacheFileMagic, 4) != 0) {
90             ALOGE("cache file has bad mojo");
91             close(fd);
92             return;
93         }
94         uint32_t* crc = reinterpret_cast<uint32_t*>(buf + 4);
95         if (GenerateCRC32(buf + headerSize, cacheSize) != *crc) {
96             ALOGE("cache file failed CRC check");
97             close(fd);
98             return;
99         }
100 
101         int err = unflatten(buf + headerSize, cacheSize);
102         if (err < 0) {
103             ALOGE("error reading cache contents: %s (%d)", strerror(-err),
104                     -err);
105             munmap(buf, fileSize);
106             close(fd);
107             return;
108         }
109 
110         munmap(buf, fileSize);
111         close(fd);
112     }
113 }
114 
writeToFile()115 void FileBlobCache::writeToFile() {
116     ATRACE_CALL();
117 
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 = GenerateCRC32(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 
getSize()186 size_t FileBlobCache::getSize() {
187     if (mFilename.length() > 0) {
188         return getFlattenedSize() + cacheFileHeaderSize;
189     }
190     return 0;
191 }
192 }
193