NetworkCacheFileSystem.cpp [plain text]
#include "config.h"
#include "NetworkCacheFileSystem.h"
#include "Logging.h"
#include <WebCore/FileSystem.h>
#include <wtf/Assertions.h>
#include <wtf/Function.h>
#include <wtf/text/CString.h>
#if !OS(WINDOWS)
#include <dirent.h>
#include <sys/stat.h>
#include <sys/time.h>
#endif
#if PLATFORM(IOS_FAMILY) && !PLATFORM(IOS_FAMILY_SIMULATOR)
#include <sys/attr.h>
#include <unistd.h>
#endif
#if USE(SOUP)
#include <gio/gio.h>
#include <wtf/glib/GRefPtr.h>
#endif
namespace WebKit {
namespace NetworkCache {
#if !OS(WINDOWS)
static DirectoryEntryType directoryEntryType(uint8_t dtype)
{
switch (dtype) {
case DT_DIR:
return DirectoryEntryType::Directory;
case DT_REG:
return DirectoryEntryType::File;
default:
ASSERT_NOT_REACHED();
return DirectoryEntryType::File;
}
return DirectoryEntryType::File;
}
#endif
void traverseDirectory(const String& path, const Function<void (const String&, DirectoryEntryType)>& function)
{
#if !OS(WINDOWS)
DIR* dir = opendir(WebCore::FileSystem::fileSystemRepresentation(path).data());
if (!dir)
return;
dirent* dp;
while ((dp = readdir(dir))) {
if (dp->d_type != DT_DIR && dp->d_type != DT_REG)
continue;
const char* name = dp->d_name;
if (!strcmp(name, ".") || !strcmp(name, ".."))
continue;
auto nameString = String::fromUTF8(name);
if (nameString.isNull())
continue;
function(nameString, directoryEntryType(dp->d_type));
}
closedir(dir);
#else
function(String(), DirectoryEntryType::File);
#endif
}
void deleteDirectoryRecursively(const String& path)
{
traverseDirectory(path, [&path](const String& name, DirectoryEntryType type) {
String entryPath = WebCore::FileSystem::pathByAppendingComponent(path, name);
switch (type) {
case DirectoryEntryType::File:
WebCore::FileSystem::deleteFile(entryPath);
break;
case DirectoryEntryType::Directory:
deleteDirectoryRecursively(entryPath);
break;
}
});
WebCore::FileSystem::deleteEmptyDirectory(path);
}
FileTimes fileTimes(const String& path)
{
#if HAVE(STAT_BIRTHTIME)
struct stat fileInfo;
if (stat(WebCore::FileSystem::fileSystemRepresentation(path).data(), &fileInfo))
return { };
return { WallTime::fromRawSeconds(fileInfo.st_birthtime), WallTime::fromRawSeconds(fileInfo.st_mtime) };
#elif USE(SOUP)
GRefPtr<GFile> file = adoptGRef(g_file_new_for_path(WebCore::FileSystem::fileSystemRepresentation(path).data()));
GRefPtr<GFileInfo> fileInfo = adoptGRef(g_file_query_info(file.get(), "xattr::birthtime,time::modified", G_FILE_QUERY_INFO_NONE, nullptr, nullptr));
if (!fileInfo)
return { };
const char* birthtimeString = g_file_info_get_attribute_string(fileInfo.get(), "xattr::birthtime");
if (!birthtimeString)
return { };
return { WallTime::fromRawSeconds(g_ascii_strtoull(birthtimeString, nullptr, 10)),
WallTime::fromRawSeconds(g_file_info_get_attribute_uint64(fileInfo.get(), "time::modified")) };
#elif OS(WINDOWS)
return FileTimes();
#endif
}
void updateFileModificationTimeIfNeeded(const String& path)
{
auto times = fileTimes(path);
if (times.creation != times.modification) {
if (WallTime::now() - times.modification < 1_h)
return;
}
#if !OS(WINDOWS)
utimes(WebCore::FileSystem::fileSystemRepresentation(path).data(), nullptr);
#endif
}
static String& pathRegisteredAsUnsafeToMemoryMapForTesting()
{
static NeverDestroyed<String> path;
return path.get();
}
void registerPathAsUnsafeToMemoryMapForTesting(const String& path)
{
pathRegisteredAsUnsafeToMemoryMapForTesting() = path;
}
bool isSafeToUseMemoryMapForPath(const String& path)
{
if (path == pathRegisteredAsUnsafeToMemoryMapForTesting())
return false;
#if PLATFORM(IOS_FAMILY) && !PLATFORM(IOS_FAMILY_SIMULATOR)
struct {
uint32_t length;
uint32_t protectionClass;
} attrBuffer;
attrlist attrList = { };
attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
attrList.commonattr = ATTR_CMN_DATA_PROTECT_FLAGS;
int32_t error = getattrlist(WebCore::FileSystem::fileSystemRepresentation(path).data(), &attrList, &attrBuffer, sizeof(attrBuffer), FSOPT_NOFOLLOW);
if (error) {
RELEASE_LOG_ERROR(Network, "Unable to get cache directory protection class, disabling use of shared mapped memory");
return false;
}
const uint32_t fileProtectionCompleteUntilFirstUserAuthentication = 3;
bool isSafe = attrBuffer.protectionClass >= fileProtectionCompleteUntilFirstUserAuthentication;
if (!isSafe)
RELEASE_LOG(Network, "Disallowing use of shared mapped memory due to container protection class %u", attrBuffer.protectionClass);
return isSafe;
#else
UNUSED_PARAM(path);
return true;
#endif
}
}
}