Add a FileSystem namespace to FileSystem.cpp
[WebKit-https.git] / Source / WebKit / NetworkProcess / cache / CacheStorageEngine.cpp
1 /*
2  * Copyright (C) 2017 Apple Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "CacheStorageEngine.h"
28
29 #include "NetworkCacheFileSystem.h"
30 #include "NetworkCacheIOChannel.h"
31 #include "NetworkProcess.h"
32 #include "WebsiteDataType.h"
33 #include <WebCore/CacheQueryOptions.h>
34 #include <WebCore/SecurityOrigin.h>
35 #include <pal/SessionID.h>
36 #include <wtf/CallbackAggregator.h>
37 #include <wtf/NeverDestroyed.h>
38 #include <wtf/text/StringBuilder.h>
39 #include <wtf/text/StringHash.h>
40
41 using namespace WebCore::DOMCacheEngine;
42 using namespace WebKit::NetworkCache;
43
44 namespace WebKit {
45
46 namespace CacheStorage {
47
48 static HashMap<PAL::SessionID, RefPtr<Engine>>& globalEngineMap()
49 {
50     static NeverDestroyed<HashMap<PAL::SessionID, RefPtr<Engine>>> map;
51
52     return map;
53 }
54
55 String Engine::cachesRootPath(const String& origin)
56 {
57     if (!shouldPersist())
58         return { };
59
60     Key key(origin, { }, { }, { }, salt());
61     return WebCore::FileSystem::pathByAppendingComponent(rootPath(), key.partitionHashAsString());
62 }
63
64 Engine::~Engine()
65 {
66     for (auto& caches : m_caches.values())
67         caches->detach();
68 }
69
70 Engine& Engine::from(PAL::SessionID sessionID)
71 {
72     auto addResult = globalEngineMap().add(sessionID, nullptr);
73     if (addResult.isNewEntry)
74         addResult.iterator->value = Engine::create(NetworkProcess::singleton().cacheStorageDirectory(sessionID));
75     return *addResult.iterator->value;
76 }
77
78 void Engine::destroyEngine(PAL::SessionID sessionID)
79 {
80     ASSERT(sessionID != PAL::SessionID::defaultSessionID());
81     globalEngineMap().remove(sessionID);
82 }
83
84 void Engine::fetchEntries(PAL::SessionID sessionID, bool shouldComputeSize, WTF::CompletionHandler<void(Vector<WebsiteData::Entry>)>&& completionHandler)
85 {
86     from(sessionID).fetchEntries(shouldComputeSize, WTFMove(completionHandler));
87 }
88
89 void Engine::clearAllEngines(WTF::Function<void()>&& completionHandler)
90 {
91     auto clearTaskHandler = CallbackAggregator::create(WTFMove(completionHandler));
92     for (auto& engine : globalEngineMap().values())
93         engine->clearAllCaches(clearTaskHandler.get());
94 }
95
96 void Engine::clearEnginesForOrigins(const Vector<String>& origins, WTF::Function<void()>&& completionHandler)
97 {
98     auto clearTaskHandler = CallbackAggregator::create(WTFMove(completionHandler));
99     for (auto& engine : globalEngineMap().values()) {
100         for (auto& origin : origins)
101             engine->clearCachesForOrigin(origin, clearTaskHandler.get());
102     }
103 }
104
105 Engine& Engine::defaultEngine()
106 {
107     auto sessionID = PAL::SessionID::defaultSessionID();
108     static NeverDestroyed<Ref<Engine>> defaultEngine = { Engine::create(NetworkProcess::singleton().cacheStorageDirectory(sessionID)) };
109     return defaultEngine.get();
110 }
111
112 Engine::Engine(String&& rootPath)
113     : m_rootPath(WTFMove(rootPath))
114 {
115     if (!m_rootPath.isNull())
116         m_ioQueue = WorkQueue::create("com.apple.WebKit.CacheStorageEngine.serialBackground", WorkQueue::Type::Serial, WorkQueue::QOS::Background);
117 }
118
119 void Engine::open(const String& origin, const String& cacheName, CacheIdentifierCallback&& callback)
120 {
121     readCachesFromDisk(origin, [cacheName, callback = WTFMove(callback)](CachesOrError&& cachesOrError) mutable {
122         if (!cachesOrError.hasValue()) {
123             callback(makeUnexpected(cachesOrError.error()));
124             return;
125         }
126
127         cachesOrError.value().get().open(cacheName, WTFMove(callback));
128     });
129 }
130
131 void Engine::remove(uint64_t cacheIdentifier, CacheIdentifierCallback&& callback)
132 {
133     Caches* cachesToModify = nullptr;
134     for (auto& caches : m_caches.values()) {
135         auto* cacheToRemove = caches->find(cacheIdentifier);
136         if (cacheToRemove) {
137             cachesToModify = caches.ptr();
138             break;
139         }
140     }
141     if (!cachesToModify) {
142         callback(makeUnexpected(Error::Internal));
143         return;
144     }
145
146     cachesToModify->remove(cacheIdentifier, WTFMove(callback));
147 }
148
149 void Engine::retrieveCaches(const String& origin, uint64_t updateCounter, CacheInfosCallback&& callback)
150 {
151     readCachesFromDisk(origin, [updateCounter, callback = WTFMove(callback)](CachesOrError&& cachesOrError) mutable {
152         if (!cachesOrError.hasValue()) {
153             callback(makeUnexpected(cachesOrError.error()));
154             return;
155         }
156
157         callback(cachesOrError.value().get().cacheInfos(updateCounter));
158     });
159 }
160
161 void Engine::retrieveRecords(uint64_t cacheIdentifier, WebCore::URL&& url, RecordsCallback&& callback)
162 {
163     readCache(cacheIdentifier, [url = WTFMove(url), callback = WTFMove(callback)](CacheOrError&& result) mutable {
164         if (!result.hasValue()) {
165             callback(makeUnexpected(result.error()));
166             return;
167         }
168         result.value().get().retrieveRecords(url, WTFMove(callback));
169     });
170 }
171
172 void Engine::putRecords(uint64_t cacheIdentifier, Vector<Record>&& records, RecordIdentifiersCallback&& callback)
173 {
174     readCache(cacheIdentifier, [records = WTFMove(records), callback = WTFMove(callback)](CacheOrError&& result) mutable {
175         if (!result.hasValue()) {
176             callback(makeUnexpected(result.error()));
177             return;
178         }
179
180         result.value().get().put(WTFMove(records), WTFMove(callback));
181     });
182 }
183
184 void Engine::deleteMatchingRecords(uint64_t cacheIdentifier, WebCore::ResourceRequest&& request, WebCore::CacheQueryOptions&& options, RecordIdentifiersCallback&& callback)
185 {
186     readCache(cacheIdentifier, [request = WTFMove(request), options = WTFMove(options), callback = WTFMove(callback)](CacheOrError&& result) mutable {
187         if (!result.hasValue()) {
188             callback(makeUnexpected(result.error()));
189             return;
190         }
191
192         result.value().get().remove(WTFMove(request), WTFMove(options), WTFMove(callback));
193     });
194 }
195
196 void Engine::initialize(Function<void(std::optional<Error>&&)>&& callback)
197 {
198     if (m_salt) {
199         callback(std::nullopt);
200         return;
201     }
202
203     if (!shouldPersist()) {
204         callback(std::nullopt);
205         return;
206     }
207
208     String saltPath = WebCore::FileSystem::pathByAppendingComponent(m_rootPath, ASCIILiteral("salt"));
209     m_ioQueue->dispatch([protectedThis = makeRef(*this), this, callback = WTFMove(callback), saltPath = WTFMove(saltPath)] () mutable {
210         WebCore::FileSystem::makeAllDirectories(m_rootPath);
211         RunLoop::main().dispatch([protectedThis = WTFMove(protectedThis), this, salt = readOrMakeSalt(saltPath), callback = WTFMove(callback)]() mutable {
212             if (!salt) {
213                 callback(Error::WriteDisk);
214                 return;
215             }
216             m_salt = WTFMove(salt);
217             callback(std::nullopt);
218         });
219     });
220 }
221
222 void Engine::readCachesFromDisk(const String& origin, CachesCallback&& callback)
223 {
224     initialize([this, origin, callback = WTFMove(callback)](std::optional<Error>&& error) mutable {
225         auto& caches = m_caches.ensure(origin, [&origin, this] {
226             return Caches::create(*this, String { origin }, cachesRootPath(origin), NetworkProcess::singleton().cacheStoragePerOriginQuota());
227         }).iterator->value;
228
229         if (caches->isInitialized()) {
230             callback(std::reference_wrapper<Caches> { caches.get() });
231             return;
232         }
233
234         if (error) {
235             callback(makeUnexpected(error.value()));
236             return;
237         }
238
239         caches->initialize([callback = WTFMove(callback), caches = caches.copyRef()](std::optional<Error>&& error) mutable {
240             if (error) {
241                 callback(makeUnexpected(error.value()));
242                 return;
243             }
244
245             callback(std::reference_wrapper<Caches> { caches.get() });
246         });
247     });
248 }
249
250 void Engine::readCache(uint64_t cacheIdentifier, CacheCallback&& callback)
251 {
252     auto* cache = this->cache(cacheIdentifier);
253     if (!cache) {
254         callback(makeUnexpected(Error::Internal));
255         return;
256     }
257     if (!cache->isOpened()) {
258         cache->open([this, protectedThis = makeRef(*this), cacheIdentifier, callback = WTFMove(callback)](std::optional<Error>&& error) mutable {
259             if (error) {
260                 callback(makeUnexpected(error.value()));
261                 return;
262             }
263
264             auto* cache = this->cache(cacheIdentifier);
265             if (!cache) {
266                 callback(makeUnexpected(Error::Internal));
267                 return;
268             }
269             ASSERT(cache->isOpened());
270             callback(std::reference_wrapper<Cache> { *cache });
271         });
272         return;
273     }
274     callback(std::reference_wrapper<Cache> { *cache });
275 }
276
277 Cache* Engine::cache(uint64_t cacheIdentifier)
278 {
279     Cache* result = nullptr;
280     for (auto& caches : m_caches.values()) {
281         if ((result = caches->find(cacheIdentifier)))
282             break;
283     }
284     return result;
285 }
286
287 void Engine::writeFile(const String& filename, NetworkCache::Data&& data, WebCore::DOMCacheEngine::CompletionCallback&& callback)
288 {
289     if (!shouldPersist()) {
290         callback(std::nullopt);
291         return;
292     }
293
294     m_ioQueue->dispatch([callback = WTFMove(callback), data = WTFMove(data), filename = filename.isolatedCopy()] () mutable {
295         auto channel = IOChannel::open(filename, IOChannel::Type::Create);
296         channel->write(0, data, nullptr, [callback = WTFMove(callback)](int error) mutable {
297             ASSERT(RunLoop::isMain());
298             if (error) {
299                 callback(Error::WriteDisk);
300                 return;
301             }
302             callback(std::nullopt);
303         });
304     });
305 }
306
307 void Engine::readFile(const String& filename, WTF::Function<void(const NetworkCache::Data&, int error)>&& callback)
308 {
309     if (!shouldPersist()) {
310         callback(Data { }, 0);
311         return;
312     }
313
314     m_ioQueue->dispatch([callback = WTFMove(callback), filename = filename.isolatedCopy()]() mutable {
315         auto channel = IOChannel::open(filename, IOChannel::Type::Read);
316         if (channel->fileDescriptor() < 0) {
317             RunLoop::main().dispatch([callback = WTFMove(callback)]() mutable {
318                 callback(Data { }, 0);
319             });
320             return;
321         }
322
323         channel->read(0, std::numeric_limits<size_t>::max(), nullptr, [callback = WTFMove(callback)](const Data& data, int error) mutable {
324             // FIXME: We should do the decoding in the background thread.
325             ASSERT(RunLoop::isMain());
326             callback(data, error);
327         });
328     });
329 }
330
331 void Engine::removeFile(const String& filename)
332 {
333     if (!shouldPersist())
334         return;
335
336     m_ioQueue->dispatch([filename = filename.isolatedCopy()]() mutable {
337         WebCore::FileSystem::deleteFile(filename);
338     });
339 }
340
341 void Engine::removeCaches(const String& origin)
342 {
343     ASSERT(m_caches.contains(origin));
344     m_caches.remove(origin);
345 }
346
347 class ReadOriginsTaskCounter : public RefCounted<ReadOriginsTaskCounter> {
348 public:
349     static Ref<ReadOriginsTaskCounter> create(WTF::CompletionHandler<void(Vector<WebsiteData::Entry>)>&& callback)
350     {
351         return adoptRef(*new ReadOriginsTaskCounter(WTFMove(callback)));
352     }
353
354     ~ReadOriginsTaskCounter()
355     {
356         m_callback(WTFMove(m_entries));
357     }
358
359     void addOrigin(WebCore::SecurityOriginData&& origin, uint64_t size)
360     {
361         m_entries.append(WebsiteData::Entry { WTFMove(origin), WebsiteDataType::DOMCache, size });
362     }
363
364 private:
365     explicit ReadOriginsTaskCounter(WTF::CompletionHandler<void(Vector<WebsiteData::Entry>)>&& callback)
366         : m_callback(WTFMove(callback))
367     {
368     }
369
370     WTF::CompletionHandler<void(Vector<WebsiteData::Entry>)> m_callback;
371     Vector<WebsiteData::Entry> m_entries;
372 };
373
374 void Engine::fetchEntries(bool shouldComputeSize, WTF::CompletionHandler<void(Vector<WebsiteData::Entry>)>&& completionHandler)
375 {
376     if (!shouldPersist()) {
377         auto entries = WTF::map(m_caches, [] (auto& pair) {
378             return WebsiteData::Entry { pair.value->origin(), WebsiteDataType::DOMCache, 0 };
379         });
380         completionHandler(WTFMove(entries));
381         return;
382     }
383
384     auto taskCounter = ReadOriginsTaskCounter::create(WTFMove(completionHandler));
385     for (auto& folderPath : WebCore::FileSystem::listDirectory(m_rootPath, "*")) {
386         if (!WebCore::FileSystem::fileIsDirectory(folderPath, WebCore::FileSystem::ShouldFollowSymbolicLinks::No))
387             continue;
388         Caches::retrieveOriginFromDirectory(folderPath, *m_ioQueue, [protectedThis = makeRef(*this), shouldComputeSize, taskCounter = taskCounter.copyRef()] (std::optional<WebCore::SecurityOriginData>&& origin) mutable {
389             ASSERT(RunLoop::isMain());
390             if (!origin)
391                 return;
392
393             if (!shouldComputeSize) {
394                 taskCounter->addOrigin(WTFMove(origin.value()), 0);
395                 return;
396             }
397
398             auto cacheOrigin = origin->securityOrigin()->toString();
399             protectedThis->readCachesFromDisk(cacheOrigin, [origin = WTFMove(origin.value()), taskCounter = WTFMove(taskCounter)] (CachesOrError&& result) mutable {
400                 if (!result.hasValue())
401                     return;
402                 taskCounter->addOrigin(WTFMove(origin), result.value().get().storageSize());
403
404             });
405         });
406     }
407 }
408
409 void Engine::clearAllCaches(CallbackAggregator& taskHandler)
410 {
411     for (auto& caches : m_caches.values())
412         caches->clear([taskHandler = makeRef(taskHandler)] { });
413
414     if (!shouldPersist())
415         return;
416
417     m_ioQueue->dispatch([path = m_rootPath.isolatedCopy(), taskHandler = makeRef(taskHandler)] {
418         for (auto& filename : WebCore::FileSystem::listDirectory(path, "*")) {
419             if (WebCore::FileSystem::fileIsDirectory(filename, WebCore::FileSystem::ShouldFollowSymbolicLinks::No))
420                 deleteDirectoryRecursively(filename);
421         }
422     });
423 }
424
425 void Engine::clearCachesForOrigin(const String& origin, CallbackAggregator& taskHandler)
426 {
427     if (auto caches = m_caches.get(origin)) {
428         caches->clear([taskHandler = makeRef(taskHandler)] { });
429         return;
430     }
431
432     if (!shouldPersist())
433         return;
434
435     m_ioQueue->dispatch([filename = cachesRootPath(origin), taskHandler = makeRef(taskHandler)] {
436         deleteDirectoryRecursively(filename);
437     });
438 }
439
440 void Engine::clearMemoryRepresentation(const String& origin, WebCore::DOMCacheEngine::CompletionCallback&& callback)
441 {
442     readCachesFromDisk(origin, [callback = WTFMove(callback)](CachesOrError&& result) {
443         if (!result.hasValue()) {
444             callback(result.error());
445             return;
446         }
447         result.value().get().clearMemoryRepresentation();
448         callback(std::nullopt);
449     });
450 }
451
452 void Engine::lock(uint64_t cacheIdentifier)
453 {
454     auto& counter = m_cacheLocks.ensure(cacheIdentifier, []() {
455         return 0;
456     }).iterator->value;
457
458     ++counter;
459 }
460
461 void Engine::unlock(uint64_t cacheIdentifier)
462 {
463     auto lockCount = m_cacheLocks.find(cacheIdentifier);
464     if (lockCount == m_cacheLocks.end())
465         return;
466
467     ASSERT(lockCount->value);
468     if (--lockCount->value)
469         return;
470
471     auto* cache = this->cache(cacheIdentifier);
472     if (!cache)
473         return;
474
475     cache->dispose();
476 }
477
478 String Engine::representation()
479 {
480     bool isFirst = true;
481     StringBuilder builder;
482     builder.append("[");
483     for (auto& keyValue : m_caches) {
484         if (!isFirst)
485             builder.append(",");
486         isFirst = false;
487
488         builder.append("\n{ \"origin\" : \"");
489         builder.append(keyValue.key);
490         builder.append("\", \"caches\" : ");
491         keyValue.value->appendRepresentation(builder);
492         builder.append("}");
493     }
494     builder.append("\n]");
495     return builder.toString();
496 }
497
498 } // namespace CacheStorage
499
500 } // namespace WebKit