1b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross/*
2b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross ** Copyright 2011, The Android Open Source Project
3b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross **
4b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross ** Licensed under the Apache License, Version 2.0 (the "License");
5b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross ** you may not use this file except in compliance with the License.
6b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross ** You may obtain a copy of the License at
7b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross **
8b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross **     http://www.apache.org/licenses/LICENSE-2.0
9b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross **
10b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross ** Unless required by applicable law or agreed to in writing, software
11b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross ** distributed under the License is distributed on an "AS IS" BASIS,
12b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross ** See the License for the specific language governing permissions and
14b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross ** limitations under the License.
15b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross */
16b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross
1759572bdc906880f9b03aa8abd4e2d2095a012538David Gross#include "nnCache.h"
18b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross
19b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross#include <inttypes.h>
20b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross#include <sys/mman.h>
21b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross#include <sys/stat.h>
22b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross#include <unistd.h>
23b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross
24b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross#include <thread>
25b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross
26b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross#include <log/log.h>
27b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross
28b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross// Cache file header
2944ece9b77fd043eb2ce35eca8eb17bfa08565300David Grossstatic const char* cacheFileMagic = "nn$$";
30b63b00ddd1eee59a296c5c5eaac5510c75a49444David Grossstatic const size_t cacheFileHeaderSize = 8;
31b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross
32b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross// The time in seconds to wait before saving newly inserted cache entries.
33b63b00ddd1eee59a296c5c5eaac5510c75a49444David Grossstatic const unsigned int deferredSaveDelay = 4;
34b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross
35b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross// ----------------------------------------------------------------------------
36b63b00ddd1eee59a296c5c5eaac5510c75a49444David Grossnamespace android {
37b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross// ----------------------------------------------------------------------------
38b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross
39b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross//
4044ece9b77fd043eb2ce35eca8eb17bfa08565300David Gross// NNCache definition
41b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross//
4244ece9b77fd043eb2ce35eca8eb17bfa08565300David GrossNNCache::NNCache() :
434ad7f07e2e14601613f8293a7983833c4158f5aeDavid Gross    mInitialized(false),
444ad7f07e2e14601613f8293a7983833c4158f5aeDavid Gross    mMaxKeySize(0), mMaxValueSize(0), mMaxTotalSize(0),
45341dfdb8e78fdbaca6e2f9aa11d31107c635fc80David Gross    mPolicy(defaultPolicy()),
464ad7f07e2e14601613f8293a7983833c4158f5aeDavid Gross    mSavePending(false) {
47b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross}
48b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross
4944ece9b77fd043eb2ce35eca8eb17bfa08565300David GrossNNCache::~NNCache() {
50b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross}
51b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross
5244ece9b77fd043eb2ce35eca8eb17bfa08565300David GrossNNCache NNCache::sCache;
53b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross
5444ece9b77fd043eb2ce35eca8eb17bfa08565300David GrossNNCache* NNCache::get() {
55b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross    return &sCache;
56b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross}
57b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross
58341dfdb8e78fdbaca6e2f9aa11d31107c635fc80David Grossvoid NNCache::initialize(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize,
59341dfdb8e78fdbaca6e2f9aa11d31107c635fc80David Gross                         Policy policy) {
60b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross    std::lock_guard<std::mutex> lock(mMutex);
61b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross    mInitialized = true;
624ad7f07e2e14601613f8293a7983833c4158f5aeDavid Gross    mMaxKeySize = maxKeySize;
634ad7f07e2e14601613f8293a7983833c4158f5aeDavid Gross    mMaxValueSize = maxValueSize;
644ad7f07e2e14601613f8293a7983833c4158f5aeDavid Gross    mMaxTotalSize = maxTotalSize;
65341dfdb8e78fdbaca6e2f9aa11d31107c635fc80David Gross    mPolicy = policy;
66b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross}
67b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross
6844ece9b77fd043eb2ce35eca8eb17bfa08565300David Grossvoid NNCache::terminate() {
69b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross    std::lock_guard<std::mutex> lock(mMutex);
70b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross    saveBlobCacheLocked();
71b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross    mBlobCache = NULL;
72fbb46bfb1a3d3a26d730eda80aa45e24371153cbDavid Gross    mInitialized = false;
73b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross}
74b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross
7544ece9b77fd043eb2ce35eca8eb17bfa08565300David Grossvoid NNCache::setBlob(const void* key, ssize_t keySize,
7644ece9b77fd043eb2ce35eca8eb17bfa08565300David Gross        const void* value, ssize_t valueSize) {
77b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross    std::lock_guard<std::mutex> lock(mMutex);
78b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross
79b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross    if (keySize < 0 || valueSize < 0) {
8044ece9b77fd043eb2ce35eca8eb17bfa08565300David Gross        ALOGW("nnCache::setBlob: negative sizes are not allowed");
81b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        return;
82b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross    }
83b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross
84b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross    if (mInitialized) {
85b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        BlobCache* bc = getBlobCacheLocked();
86b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        bc->set(key, keySize, value, valueSize);
87b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross
88b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        if (!mSavePending) {
89b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross            mSavePending = true;
90b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross            std::thread deferredSaveThread([this]() {
91b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross                sleep(deferredSaveDelay);
92b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross                std::lock_guard<std::mutex> lock(mMutex);
93b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross                if (mInitialized) {
94b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross                    saveBlobCacheLocked();
95b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross                }
96b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross                mSavePending = false;
97b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross            });
98b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross            deferredSaveThread.detach();
99b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        }
100b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross    }
101b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross}
102b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross
10344ece9b77fd043eb2ce35eca8eb17bfa08565300David Grossssize_t NNCache::getBlob(const void* key, ssize_t keySize,
10444ece9b77fd043eb2ce35eca8eb17bfa08565300David Gross        void* value, ssize_t valueSize) {
105b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross    std::lock_guard<std::mutex> lock(mMutex);
106b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross
107b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross    if (keySize < 0 || valueSize < 0) {
10844ece9b77fd043eb2ce35eca8eb17bfa08565300David Gross        ALOGW("nnCache::getBlob: negative sizes are not allowed");
109b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        return 0;
110b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross    }
111b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross
112b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross    if (mInitialized) {
113b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        BlobCache* bc = getBlobCacheLocked();
114b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        return bc->get(key, keySize, value, valueSize);
115b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross    }
116b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross    return 0;
117b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross}
118b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross
11979c0b79406117a800ed4dd965b3d329cdc8903ffDavid Grossssize_t NNCache::getBlob(const void* key, ssize_t keySize,
12079c0b79406117a800ed4dd965b3d329cdc8903ffDavid Gross        void** value, std::function<void*(size_t)> alloc) {
12179c0b79406117a800ed4dd965b3d329cdc8903ffDavid Gross    std::lock_guard<std::mutex> lock(mMutex);
12279c0b79406117a800ed4dd965b3d329cdc8903ffDavid Gross
12379c0b79406117a800ed4dd965b3d329cdc8903ffDavid Gross    if (keySize < 0) {
12479c0b79406117a800ed4dd965b3d329cdc8903ffDavid Gross        ALOGW("nnCache::getBlob: negative sizes are not allowed");
12579c0b79406117a800ed4dd965b3d329cdc8903ffDavid Gross        return 0;
12679c0b79406117a800ed4dd965b3d329cdc8903ffDavid Gross    }
12779c0b79406117a800ed4dd965b3d329cdc8903ffDavid Gross
12879c0b79406117a800ed4dd965b3d329cdc8903ffDavid Gross    if (mInitialized) {
12979c0b79406117a800ed4dd965b3d329cdc8903ffDavid Gross        BlobCache* bc = getBlobCacheLocked();
13079c0b79406117a800ed4dd965b3d329cdc8903ffDavid Gross        return bc->get(key, keySize, value, alloc);
13179c0b79406117a800ed4dd965b3d329cdc8903ffDavid Gross    }
13279c0b79406117a800ed4dd965b3d329cdc8903ffDavid Gross    return 0;
13379c0b79406117a800ed4dd965b3d329cdc8903ffDavid Gross}
13479c0b79406117a800ed4dd965b3d329cdc8903ffDavid Gross
13544ece9b77fd043eb2ce35eca8eb17bfa08565300David Grossvoid NNCache::setCacheFilename(const char* filename) {
136b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross    std::lock_guard<std::mutex> lock(mMutex);
137b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross    mFilename = filename;
138b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross}
139b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross
14044ece9b77fd043eb2ce35eca8eb17bfa08565300David GrossBlobCache* NNCache::getBlobCacheLocked() {
141b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross    if (mBlobCache == nullptr) {
142341dfdb8e78fdbaca6e2f9aa11d31107c635fc80David Gross        mBlobCache.reset(new BlobCache(mMaxKeySize, mMaxValueSize, mMaxTotalSize, mPolicy));
143b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        loadBlobCacheLocked();
144b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross    }
145b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross    return mBlobCache.get();
146b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross}
147b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross
148b63b00ddd1eee59a296c5c5eaac5510c75a49444David Grossstatic uint32_t crc32c(const uint8_t* buf, size_t len) {
149b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross    const uint32_t polyBits = 0x82F63B78;
150b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross    uint32_t r = 0;
151b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross    for (size_t i = 0; i < len; i++) {
152b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        r ^= buf[i];
153b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        for (int j = 0; j < 8; j++) {
154b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross            if (r & 1) {
155b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross                r = (r >> 1) ^ polyBits;
156b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross            } else {
157b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross                r >>= 1;
158b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross            }
159b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        }
160b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross    }
161b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross    return r;
162b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross}
163b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross
16444ece9b77fd043eb2ce35eca8eb17bfa08565300David Grossvoid NNCache::saveBlobCacheLocked() {
165b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross    if (mFilename.length() > 0 && mBlobCache != NULL) {
166b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        size_t cacheSize = mBlobCache->getFlattenedSize();
167b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        size_t headerSize = cacheFileHeaderSize;
168b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        const char* fname = mFilename.c_str();
169b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross
170b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        // Try to create the file with no permissions so we can write it
171b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        // without anyone trying to read it.
172b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        int fd = open(fname, O_CREAT | O_EXCL | O_RDWR, 0);
173b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        if (fd == -1) {
174b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross            if (errno == EEXIST) {
175b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross                // The file exists, delete it and try again.
176b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross                if (unlink(fname) == -1) {
177b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross                    // No point in retrying if the unlink failed.
178b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross                    ALOGE("error unlinking cache file %s: %s (%d)", fname,
179b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross                            strerror(errno), errno);
180b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross                    return;
181b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross                }
182b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross                // Retry now that we've unlinked the file.
183b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross                fd = open(fname, O_CREAT | O_EXCL | O_RDWR, 0);
184b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross            }
185b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross            if (fd == -1) {
186b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross                ALOGE("error creating cache file %s: %s (%d)", fname,
187b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross                        strerror(errno), errno);
188b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross                return;
189b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross            }
190b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        }
191b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross
192b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        size_t fileSize = headerSize + cacheSize;
193b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross
194b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        uint8_t* buf = new uint8_t [fileSize];
195b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        if (!buf) {
196b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross            ALOGE("error allocating buffer for cache contents: %s (%d)",
197b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross                    strerror(errno), errno);
198b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross            close(fd);
199b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross            unlink(fname);
200b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross            return;
201b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        }
202b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross
203b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        int err = mBlobCache->flatten(buf + headerSize, cacheSize);
204b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        if (err < 0) {
205b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross            ALOGE("error writing cache contents: %s (%d)", strerror(-err),
206b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross                    -err);
207b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross            delete [] buf;
208b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross            close(fd);
209b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross            unlink(fname);
210b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross            return;
211b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        }
212b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross
213b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        // Write the file magic and CRC
214b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        memcpy(buf, cacheFileMagic, 4);
215b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        uint32_t* crc = reinterpret_cast<uint32_t*>(buf + 4);
216b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        *crc = crc32c(buf + headerSize, cacheSize);
217b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross
218b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        if (write(fd, buf, fileSize) == -1) {
219b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross            ALOGE("error writing cache file: %s (%d)", strerror(errno),
220b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross                    errno);
221b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross            delete [] buf;
222b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross            close(fd);
223b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross            unlink(fname);
224b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross            return;
225b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        }
226b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross
227b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        delete [] buf;
228b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        fchmod(fd, S_IRUSR);
229b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        close(fd);
230b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross    }
231b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross}
232b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross
23344ece9b77fd043eb2ce35eca8eb17bfa08565300David Grossvoid NNCache::loadBlobCacheLocked() {
234b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross    if (mFilename.length() > 0) {
235b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        size_t headerSize = cacheFileHeaderSize;
236b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross
237b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        int fd = open(mFilename.c_str(), O_RDONLY, 0);
238b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        if (fd == -1) {
239b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross            if (errno != ENOENT) {
240b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross                ALOGE("error opening cache file %s: %s (%d)", mFilename.c_str(),
241b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross                        strerror(errno), errno);
242b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross            }
243b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross            return;
244b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        }
245b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross
246b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        struct stat statBuf;
247b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        if (fstat(fd, &statBuf) == -1) {
248b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross            ALOGE("error stat'ing cache file: %s (%d)", strerror(errno), errno);
249b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross            close(fd);
250b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross            return;
251b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        }
252b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross
253b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        // Sanity check the size before trying to mmap it.
254b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        size_t fileSize = statBuf.st_size;
2554ad7f07e2e14601613f8293a7983833c4158f5aeDavid Gross        if (fileSize > mMaxTotalSize * 2) {
256b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross            ALOGE("cache file is too large: %#" PRIx64,
257b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross                  static_cast<off64_t>(statBuf.st_size));
258b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross            close(fd);
259b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross            return;
260b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        }
261b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross
262b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        uint8_t* buf = reinterpret_cast<uint8_t*>(mmap(NULL, fileSize,
263b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross                PROT_READ, MAP_PRIVATE, fd, 0));
264b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        if (buf == MAP_FAILED) {
265b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross            ALOGE("error mmaping cache file: %s (%d)", strerror(errno),
266b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross                    errno);
267b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross            close(fd);
268b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross            return;
269b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        }
270b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross
271b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        // Check the file magic and CRC
272b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        size_t cacheSize = fileSize - headerSize;
273b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        if (memcmp(buf, cacheFileMagic, 4) != 0) {
274b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross            ALOGE("cache file has bad mojo");
275b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross            close(fd);
276b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross            return;
277b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        }
278b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        uint32_t* crc = reinterpret_cast<uint32_t*>(buf + 4);
279b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        if (crc32c(buf + headerSize, cacheSize) != *crc) {
280b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross            ALOGE("cache file failed CRC check");
281b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross            close(fd);
282b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross            return;
283b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        }
284b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross
285b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        int err = mBlobCache->unflatten(buf + headerSize, cacheSize);
286b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        if (err < 0) {
287b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross            ALOGE("error reading cache contents: %s (%d)", strerror(-err),
288b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross                    -err);
289b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross            munmap(buf, fileSize);
290b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross            close(fd);
291b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross            return;
292b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        }
293b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross
294b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        munmap(buf, fileSize);
295b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross        close(fd);
296b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross    }
297b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross}
298b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross
299b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross// ----------------------------------------------------------------------------
300b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross}; // namespace android
301b63b00ddd1eee59a296c5c5eaac5510c75a49444David Gross// ----------------------------------------------------------------------------
302