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 <wtf/Assertions.h>
31#include <wtf/FileSystem.h>
32#include <wtf/Function.h>
33#include <wtf/text/CString.h>
34
35#if !OS(WINDOWS)
36#include <dirent.h>
37#include <sys/stat.h>
38#include <sys/time.h>
39#else
40#include <windows.h>
41#endif
42
43#if PLATFORM(IOS_FAMILY) && !PLATFORM(IOS_FAMILY_SIMULATOR)
44#include <sys/attr.h>
45#include <unistd.h>
46#endif
47
48#if USE(SOUP)
49#include <gio/gio.h>
50#include <wtf/glib/GRefPtr.h>
51#endif
52
53namespace WebKit {
54namespace NetworkCache {
55
56#if !OS(WINDOWS)
57static DirectoryEntryType directoryEntryType(uint8_t dtype)
58{
59 switch (dtype) {
60 case DT_DIR:
61 return DirectoryEntryType::Directory;
62 case DT_REG:
63 return DirectoryEntryType::File;
64 default:
65 ASSERT_NOT_REACHED();
66 return DirectoryEntryType::File;
67 }
68 return DirectoryEntryType::File;
69}
70#endif
71
72void traverseDirectory(const String& path, const Function<void (const String&, DirectoryEntryType)>& function)
73{
74#if !OS(WINDOWS)
75 DIR* dir = opendir(FileSystem::fileSystemRepresentation(path).data());
76 if (!dir)
77 return;
78 dirent* dp;
79 while ((dp = readdir(dir))) {
80 if (dp->d_type != DT_DIR && dp->d_type != DT_REG)
81 continue;
82 const char* name = dp->d_name;
83 if (!strcmp(name, ".") || !strcmp(name, ".."))
84 continue;
85 auto nameString = String::fromUTF8(name);
86 if (nameString.isNull())
87 continue;
88 function(nameString, directoryEntryType(dp->d_type));
89 }
90 closedir(dir);
91#else
92 auto entries = FileSystem::listDirectory(path);
93 for (auto& entry : entries) {
94 auto type = FileSystem::fileIsDirectory(entry, FileSystem::ShouldFollowSymbolicLinks::No) ? DirectoryEntryType::Directory : DirectoryEntryType::File;
95 function(entry, type);
96 }
97#endif
98}
99
100void deleteDirectoryRecursively(const String& path)
101{
102 traverseDirectory(path, [&path](const String& name, DirectoryEntryType type) {
103 String entryPath = FileSystem::pathByAppendingComponent(path, name);
104 switch (type) {
105 case DirectoryEntryType::File:
106 FileSystem::deleteFile(entryPath);
107 break;
108 case DirectoryEntryType::Directory:
109 deleteDirectoryRecursively(entryPath);
110 break;
111 // This doesn't follow symlinks.
112 }
113 });
114 FileSystem::deleteEmptyDirectory(path);
115}
116
117FileTimes fileTimes(const String& path)
118{
119#if HAVE(STAT_BIRTHTIME)
120 struct stat fileInfo;
121 if (stat(FileSystem::fileSystemRepresentation(path).data(), &fileInfo))
122 return { };
123 return { WallTime::fromRawSeconds(fileInfo.st_birthtime), WallTime::fromRawSeconds(fileInfo.st_mtime) };
124#elif USE(SOUP)
125 // There's no st_birthtime in some operating systems like Linux, so we use xattrs to set/get the creation time.
126 GRefPtr<GFile> file = adoptGRef(g_file_new_for_path(FileSystem::fileSystemRepresentation(path).data()));
127 GRefPtr<GFileInfo> fileInfo = adoptGRef(g_file_query_info(file.get(), "xattr::birthtime,time::modified", G_FILE_QUERY_INFO_NONE, nullptr, nullptr));
128 if (!fileInfo)
129 return { };
130 const char* birthtimeString = g_file_info_get_attribute_string(fileInfo.get(), "xattr::birthtime");
131 if (!birthtimeString)
132 return { };
133 return { WallTime::fromRawSeconds(g_ascii_strtoull(birthtimeString, nullptr, 10)),
134 WallTime::fromRawSeconds(g_file_info_get_attribute_uint64(fileInfo.get(), "time::modified")) };
135#elif OS(WINDOWS)
136 auto createTime = FileSystem::getFileCreationTime(path);
137 auto modifyTime = FileSystem::getFileModificationTime(path);
138 return { createTime.valueOr(WallTime()), modifyTime.valueOr(WallTime()) };
139#endif
140}
141
142void updateFileModificationTimeIfNeeded(const String& path)
143{
144 auto times = fileTimes(path);
145 if (times.creation != times.modification) {
146 // Don't update more than once per hour.
147 if (WallTime::now() - times.modification < 1_h)
148 return;
149 }
150#if !OS(WINDOWS)
151 // This really updates both the access time and the modification time.
152 utimes(FileSystem::fileSystemRepresentation(path).data(), nullptr);
153#else
154 FILETIME time;
155 GetSystemTimeAsFileTime(&time);
156 auto file = CreateFile(path.wideCharacters().data(), GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
157 if (file == INVALID_HANDLE_VALUE)
158 return;
159 SetFileTime(file, &time, &time, &time);
160 CloseHandle(file);
161#endif
162}
163
164}
165}
166