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