ee6d14e572e79f8c33c6464f072d08e64c4430ac
[WebKit.git] / Source / WebKit / NetworkProcess / cache / NetworkCacheFileSystem.cpp
1 /*
2  * Copyright (C) 2015 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. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "NetworkCacheFileSystem.h"
28
29 #include "Logging.h"
30 #include <WebCore/FileSystem.h>
31 #include <dirent.h>
32 #include <sys/stat.h>
33 #include <sys/time.h>
34 #include <wtf/Assertions.h>
35 #include <wtf/Function.h>
36 #include <wtf/text/CString.h>
37
38 #if PLATFORM(IOS) && !PLATFORM(IOS_SIMULATOR)
39 #include <sys/attr.h>
40 #include <unistd.h>
41 #endif
42
43 #if USE(SOUP)
44 #include <gio/gio.h>
45 #include <wtf/glib/GRefPtr.h>
46 #endif
47
48 namespace WebKit {
49 namespace NetworkCache {
50
51 static DirectoryEntryType directoryEntryType(uint8_t dtype)
52 {
53     switch (dtype) {
54     case DT_DIR:
55         return DirectoryEntryType::Directory;
56     case DT_REG:
57         return DirectoryEntryType::File;
58     default:
59         ASSERT_NOT_REACHED();
60         return DirectoryEntryType::File;
61     }
62 }
63
64 void traverseDirectory(const String& path, const Function<void (const String&, DirectoryEntryType)>& function)
65 {
66     DIR* dir = opendir(WebCore::FileSystem::fileSystemRepresentation(path).data());
67     if (!dir)
68         return;
69     dirent* dp;
70     while ((dp = readdir(dir))) {
71         if (dp->d_type != DT_DIR && dp->d_type != DT_REG)
72             continue;
73         const char* name = dp->d_name;
74         if (!strcmp(name, ".") || !strcmp(name, ".."))
75             continue;
76         auto nameString = String::fromUTF8(name);
77         if (nameString.isNull())
78             continue;
79         function(nameString, directoryEntryType(dp->d_type));
80     }
81     closedir(dir);
82 }
83
84 void deleteDirectoryRecursively(const String& path)
85 {
86     traverseDirectory(path, [&path](const String& name, DirectoryEntryType type) {
87         String entryPath = WebCore::FileSystem::pathByAppendingComponent(path, name);
88         switch (type) {
89         case DirectoryEntryType::File:
90             WebCore::FileSystem::deleteFile(entryPath);
91             break;
92         case DirectoryEntryType::Directory:
93             deleteDirectoryRecursively(entryPath);
94             break;
95         // This doesn't follow symlinks.
96         }
97     });
98     WebCore::FileSystem::deleteEmptyDirectory(path);
99 }
100
101 FileTimes fileTimes(const String& path)
102 {
103 #if HAVE(STAT_BIRTHTIME)
104     struct stat fileInfo;
105     if (stat(WebCore::FileSystem::fileSystemRepresentation(path).data(), &fileInfo))
106         return { };
107     return { std::chrono::system_clock::from_time_t(fileInfo.st_birthtime), std::chrono::system_clock::from_time_t(fileInfo.st_mtime) };
108 #elif USE(SOUP)
109     // There's no st_birthtime in some operating systems like Linux, so we use xattrs to set/get the creation time.
110     GRefPtr<GFile> file = adoptGRef(g_file_new_for_path(WebCore::FileSystem::fileSystemRepresentation(path).data()));
111     GRefPtr<GFileInfo> fileInfo = adoptGRef(g_file_query_info(file.get(), "xattr::birthtime,time::modified", G_FILE_QUERY_INFO_NONE, nullptr, nullptr));
112     if (!fileInfo)
113         return { };
114     const char* birthtimeString = g_file_info_get_attribute_string(fileInfo.get(), "xattr::birthtime");
115     if (!birthtimeString)
116         return { };
117     return { std::chrono::system_clock::from_time_t(g_ascii_strtoull(birthtimeString, nullptr, 10)),
118         std::chrono::system_clock::from_time_t(g_file_info_get_attribute_uint64(fileInfo.get(), "time::modified")) };
119 #endif
120 }
121
122 void updateFileModificationTimeIfNeeded(const String& path)
123 {
124     auto times = fileTimes(path);
125     if (times.creation != times.modification) {
126         // Don't update more than once per hour.
127         if (std::chrono::system_clock::now() - times.modification < std::chrono::hours(1))
128             return;
129     }
130     // This really updates both the access time and the modification time.
131     utimes(WebCore::FileSystem::fileSystemRepresentation(path).data(), nullptr);
132 }
133
134 bool canUseSharedMemoryForPath(const String& path)
135 {
136 #if PLATFORM(IOS) && !PLATFORM(IOS_SIMULATOR)
137     struct {
138         uint32_t length;
139         uint32_t protectionClass;
140     } attrBuffer;
141
142     attrlist attrList = { };
143     attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
144     attrList.commonattr = ATTR_CMN_DATA_PROTECT_FLAGS;
145     int32_t error = getattrlist(WebCore::FileSystem::fileSystemRepresentation(path).data(), &attrList, &attrBuffer, sizeof(attrBuffer), FSOPT_NOFOLLOW);
146     if (error) {
147         RELEASE_LOG_ERROR(Network, "Unable to get cache directory protection class, disabling use of shared mapped memory");
148         return false;
149     }
150
151     // For stricter protection classes shared maps could disappear when device is locked.
152     const uint32_t fileProtectionCompleteUntilFirstUserAuthentication = 3;
153     bool isSafe = attrBuffer.protectionClass >= fileProtectionCompleteUntilFirstUserAuthentication;
154
155     if (!isSafe)
156         RELEASE_LOG(Network, "Disallowing use of shared mapped memory due to container protection class %u", attrBuffer.protectionClass);
157
158     return isSafe;
159 #else
160     UNUSED_PARAM(path);
161     return true;
162 #endif
163 }
164
165 }
166 }