https://bugs.webkit.org/show_bug.cgi?id=176143
Reviewed by Andreas Kling.
Source/WebCore:
getFileMetadata() does not work as expected for symbolic links:
On POSIX, getFileMetadata() always followed symlinks, which meant that FileMetadata.type could
never be TypeSymbolicLink. On Windows, the function properly did not follow symlinks but failed to set
FileMetadata.type to TypeSymbolicLink when the file was a symbolic link.
This patch adds a new ShouldFollowSymbolicLinks parameter to getFileMetadata() so that
the call site can decide the behavior it wants. If getFileMetadata() is called on a
symbolic link with ShouldFollowSymbolicLinks::No as parameter, FileMetadata.type is now
properly set to TypeSymbolicLink.
No new tests, covered by new API test.
* Modules/entriesapi/DOMFileSystem.cpp:
(WebCore::listDirectoryWithMetadata):
It is important we do not follow symlinks here since the code wants to discard them
and does so by checking FileMetadata.type.
* WebCore.xcodeproj/project.pbxproj:
* fileapi/File.cpp:
(WebCore::File::isDirectory const):
* html/FileListCreator.cpp:
(WebCore::appendDirectoryFiles):
(WebCore::FileListCreator::createFileList):
It is important we do not follow symlinks here since the code wants to discard them
and does so by checking FileMetadata.type.
* platform/FileSystem.cpp:
(WebCore::fileIsDirectory):
* platform/FileSystem.h:
* platform/glib/FileSystemGlib.cpp:
(WebCore::getFileLStat):
(WebCore::getFileMetadata):
* platform/posix/FileSystemPOSIX.cpp:
(WebCore::getFileMetadata):
(WebCore::createSymbolicLink):
* platform/win/FileSystemWin.cpp:
(WebCore::getFinalPathName):
(WebCore::getFileMetadata):
(WebCore::createSymbolicLink):
- Add new createSymbolicLink() function for testing purposes.
- On Posix, call lstat() instead of stat if ShouldFollowSymbolicLinks::No.
- On Windows, since FindFirstFileW() does not follow symlinks, resolve
final path using GetFinalPathNameByHandleW() if ShouldFollowSymbolicLinks::Yes.
- On Windows, properly set FileMetadata.type to TypeSymbolicLink if the file
is a symbolic link.
Tools:
Add API test coverage.
* TestWebKitAPI/Tests/WebCore/FileSystem.cpp:
(TestWebKitAPI::TEST_F):
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@221441
268f45cc-cd09-0410-ab3c-
d52691b4dbfc
+2017-08-31 Chris Dumez <cdumez@apple.com>
+
+ getFileMetadata() does not work as expected for symbolic links
+ https://bugs.webkit.org/show_bug.cgi?id=176143
+
+ Reviewed by Andreas Kling.
+
+ getFileMetadata() does not work as expected for symbolic links:
+ On POSIX, getFileMetadata() always followed symlinks, which meant that FileMetadata.type could
+ never be TypeSymbolicLink. On Windows, the function properly did not follow symlinks but failed to set
+ FileMetadata.type to TypeSymbolicLink when the file was a symbolic link.
+
+ This patch adds a new ShouldFollowSymbolicLinks parameter to getFileMetadata() so that
+ the call site can decide the behavior it wants. If getFileMetadata() is called on a
+ symbolic link with ShouldFollowSymbolicLinks::No as parameter, FileMetadata.type is now
+ properly set to TypeSymbolicLink.
+
+ No new tests, covered by new API test.
+
+ * Modules/entriesapi/DOMFileSystem.cpp:
+ (WebCore::listDirectoryWithMetadata):
+ It is important we do not follow symlinks here since the code wants to discard them
+ and does so by checking FileMetadata.type.
+
+ * WebCore.xcodeproj/project.pbxproj:
+ * fileapi/File.cpp:
+ (WebCore::File::isDirectory const):
+
+ * html/FileListCreator.cpp:
+ (WebCore::appendDirectoryFiles):
+ (WebCore::FileListCreator::createFileList):
+ It is important we do not follow symlinks here since the code wants to discard them
+ and does so by checking FileMetadata.type.
+
+ * platform/FileSystem.cpp:
+ (WebCore::fileIsDirectory):
+ * platform/FileSystem.h:
+ * platform/glib/FileSystemGlib.cpp:
+ (WebCore::getFileLStat):
+ (WebCore::getFileMetadata):
+ * platform/posix/FileSystemPOSIX.cpp:
+ (WebCore::getFileMetadata):
+ (WebCore::createSymbolicLink):
+ * platform/win/FileSystemWin.cpp:
+ (WebCore::getFinalPathName):
+ (WebCore::getFileMetadata):
+ (WebCore::createSymbolicLink):
+ - Add new createSymbolicLink() function for testing purposes.
+ - On Posix, call lstat() instead of stat if ShouldFollowSymbolicLinks::No.
+ - On Windows, since FindFirstFileW() does not follow symlinks, resolve
+ final path using GetFinalPathNameByHandleW() if ShouldFollowSymbolicLinks::Yes.
+ - On Windows, properly set FileMetadata.type to TypeSymbolicLink if the file
+ is a symbolic link.
+
2017-08-31 Michael Catanzaro <mcatanzaro@igalia.com>
REGRESSION(r221226): [SOUP] libsoup-CRITICAL **: soup_cookies_to_cookie_header: assertion 'cookies != NULL' failed
static ExceptionOr<Vector<ListedChild>> listDirectoryWithMetadata(const String& fullPath)
{
ASSERT(!isMainThread());
- if (!fileIsDirectory(fullPath))
+ if (!fileIsDirectory(fullPath, ShouldFollowSymbolicLinks::No))
return Exception { NotFoundError, "Path no longer exists or is no longer a directory" };
auto childPaths = listDirectory(fullPath, "*");
listedChildren.reserveInitialCapacity(childPaths.size());
for (auto& childPath : childPaths) {
FileMetadata metadata;
- if (!getFileMetadata(childPath, metadata))
+ if (!getFileMetadata(childPath, metadata, ShouldFollowSymbolicLinks::No))
continue;
listedChildren.uncheckedAppend(ListedChild { pathGetFileName(childPath), metadata.type });
}
467302021C4EFE7800BCB357 /* IgnoreOpensDuringUnloadCountIncrementer.h in Headers */ = {isa = PBXBuildFile; fileRef = 467302011C4EFE6600BCB357 /* IgnoreOpensDuringUnloadCountIncrementer.h */; };
468344DF1EDDFAAA00B7795B /* DOMRectList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 468344DD1EDDFA5F00B7795B /* DOMRectList.cpp */; };
468344E01EDDFAAA00B7795B /* DOMRectList.h in Headers */ = {isa = PBXBuildFile; fileRef = 468344DE1EDDFA5F00B7795B /* DOMRectList.h */; settings = {ATTRIBUTES = (Private, ); }; };
- 4689F1AF1267BAE100E8D380 /* FileMetadata.h in Headers */ = {isa = PBXBuildFile; fileRef = 4689F1AE1267BAE100E8D380 /* FileMetadata.h */; };
+ 4689F1AF1267BAE100E8D380 /* FileMetadata.h in Headers */ = {isa = PBXBuildFile; fileRef = 4689F1AE1267BAE100E8D380 /* FileMetadata.h */; settings = {ATTRIBUTES = (Private, ); }; };
46B63F6C1C6E8D19002E914B /* JSEventTargetCustom.h in Headers */ = {isa = PBXBuildFile; fileRef = 46B63F6B1C6E8CDF002E914B /* JSEventTargetCustom.h */; settings = {ATTRIBUTES = (Private, ); }; };
46C696CB1E7205F700597937 /* CPUMonitor.h in Headers */ = {isa = PBXBuildFile; fileRef = 46C696C91E7205E400597937 /* CPUMonitor.h */; settings = {ATTRIBUTES = (Private, ); }; };
46C696CC1E7205FC00597937 /* CPUMonitor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 46C696CA1E7205E400597937 /* CPUMonitor.cpp */; };
bool File::isDirectory() const
{
if (!m_isDirectory)
- m_isDirectory = fileIsDirectory(m_path);
+ m_isDirectory = fileIsDirectory(m_path, ShouldFollowSymbolicLinks::Yes);
return *m_isDirectory;
}
{
for (auto& childPath : listDirectory(directory, "*")) {
FileMetadata metadata;
- if (!getFileMetadata(childPath, metadata))
+ if (!getFileMetadata(childPath, metadata, ShouldFollowSymbolicLinks::No))
continue;
if (metadata.isHidden)
{
Vector<RefPtr<File>> fileObjects;
for (auto& info : paths) {
- if (shouldResolveDirectories == ShouldResolveDirectories::Yes && fileIsDirectory(info.path))
+ if (shouldResolveDirectories == ShouldResolveDirectories::Yes && fileIsDirectory(info.path, ShouldFollowSymbolicLinks::No))
appendDirectoryFiles(info.path, pathGetFileName(info.path), fileObjects);
else
fileObjects.append(File::createWithName(info.path, info.displayName));
closeFile(handle);
}
-bool fileIsDirectory(const String& path)
+bool fileIsDirectory(const String& path, ShouldFollowSymbolicLinks shouldFollowSymbolicLinks)
{
FileMetadata metadata;
- if (!getFileMetadata(path, metadata))
+ if (!getFileMetadata(path, metadata, shouldFollowSymbolicLinks))
return false;
return metadata.type == FileMetadata::TypeDirectory;
}
LockNonBlocking = 4
};
+enum class ShouldFollowSymbolicLinks { No, Yes };
+
struct FileMetadata;
WEBCORE_EXPORT bool fileExists(const String&);
WEBCORE_EXPORT bool getFileSize(PlatformFileHandle, long long& result);
WEBCORE_EXPORT bool getFileModificationTime(const String&, time_t& result);
WEBCORE_EXPORT bool getFileCreationTime(const String&, time_t& result); // Not all platforms store file creation time.
-bool getFileMetadata(const String&, FileMetadata&);
-bool fileIsDirectory(const String&);
+WEBCORE_EXPORT bool getFileMetadata(const String&, FileMetadata&, ShouldFollowSymbolicLinks);
+bool fileIsDirectory(const String&, ShouldFollowSymbolicLinks);
WEBCORE_EXPORT String pathByAppendingComponent(const String& path, const String& component);
String pathByAppendingComponents(const String& path, const Vector<String>& components);
String lastComponentOfPathIgnoringTrailingSlash(const String& path);
WEBCORE_EXPORT String directoryName(const String&);
WEBCORE_EXPORT bool getVolumeFreeSpace(const String&, uint64_t&);
WEBCORE_EXPORT std::optional<int32_t> getFileDeviceId(const CString&);
+WEBCORE_EXPORT bool createSymbolicLink(const String& targetPath, const String& symbolicLinkPath);
WEBCORE_EXPORT void setMetadataURL(const String& path, const String& urlString, const String& referrer = { });
return g_stat(filename.get(), statBuffer) != -1;
}
+static bool getFileLStat(const String& path, GStatBuf* statBuffer)
+{
+ GUniquePtr<gchar> filename = unescapedFilename(path);
+ if (!filename)
+ return false;
+
+ return g_lstat(filename.get(), statBuffer) != -1;
+}
+
bool getFileSize(const String& path, long long& resultSize)
{
GStatBuf statResult;
return true;
}
-bool getFileMetadata(const String& path, FileMetadata& metadata)
+bool getFileMetadata(const String& path, FileMetadata& metadata, ShouldFollowSymbolicLinks shouldFollowSymbolicLinks)
{
GStatBuf statResult;
- if (!getFileStat(path, &statResult))
- return false;
+
+ if (shouldFollowSymbolicLinks == ShouldFollowSymbolicLinks::Yes) {
+ if (!getFileStat(path, &statResult))
+ return false;
+ } else {
+ if (!getFileLStat(path, &statResult))
+ return false;
+ }
String filename = pathGetFileName(path);
metadata.isHidden = !filename.isEmpty() && filename[0] == '.';
return stringFromFileSystemRepresentation(g_get_home_dir());
}
+bool createSymbolicLink(const String& targetPath, const String& symbolicLinkPath)
+{
+ CString targetPathFSRep = fileSystemRepresentation(targetPath);
+ if (!targetPathFSRep.data() || targetPathFSRep.data()[0] == '\0')
+ return false;
+
+ CString symbolicLinkPathFSRep = fileSystemRepresentation(symbolicLinkPath);
+ if (!symbolicLinkPathFSRep.data() || symbolicLinkPathFSRep.data()[0] == '\0')
+ return false;
+
+ return !symlink(targetPathFSRep.data(), symbolicLinkPathFSRep.data());
+}
+
String pathGetFileName(const String& pathName)
{
GUniquePtr<gchar> tmpFilename = unescapedFilename(pathName);
// FIXME: Some platforms provide better ways to listen for file system object changes, consider using these.
FileMetadata metadata;
- if (!getFileMetadata(m_path, metadata))
+ if (!getFileMetadata(m_path, metadata, ShouldFollowSymbolicLinks::Yes))
return;
m_expectedModificationTime = metadata.modificationTime;
m_replacementShouldBeGenerated = false;
if (!m_replacementPath.isNull()) {
FileMetadata metadata;
- if (getFileMetadata(m_replacementPath, metadata))
+ if (getFileMetadata(m_replacementPath, metadata, ShouldFollowSymbolicLinks::Yes))
m_size = metadata.length;
}
return true;
}
-bool getFileMetadata(const String& path, FileMetadata& metadata)
+bool getFileMetadata(const String& path, FileMetadata& metadata, ShouldFollowSymbolicLinks shouldFollowSymbolicLinks)
{
CString fsRep = fileSystemRepresentation(path);
return false;
struct stat fileInfo;
- if (stat(fsRep.data(), &fileInfo))
- return false;
+
+ if (shouldFollowSymbolicLinks == ShouldFollowSymbolicLinks::Yes) {
+ if (stat(fsRep.data(), &fileInfo))
+ return false;
+ } else {
+ if (lstat(fsRep.data(), &fileInfo))
+ return false;
+ }
String filename = pathGetFileName(path);
return true;
}
+bool createSymbolicLink(const String& targetPath, const String& symbolicLinkPath)
+{
+ CString targetPathFSRep = fileSystemRepresentation(targetPath);
+ if (!targetPathFSRep.data() || targetPathFSRep.data()[0] == '\0')
+ return false;
+
+ CString symbolicLinkPathFSRep = fileSystemRepresentation(symbolicLinkPath);
+ if (!symbolicLinkPathFSRep.data() || symbolicLinkPathFSRep.data()[0] == '\0')
+ return false;
+
+ return !symlink(targetPathFSRep.data(), symbolicLinkPathFSRep.data());
+}
+
String pathByAppendingComponent(const String& path, const String& component)
{
if (path.endsWith('/'))
return true;
}
-bool getFileMetadata(const String& path, FileMetadata& metadata)
+static String getFinalPathName(const String& path)
+{
+ auto handle = openFile(path, OpenForRead);
+ if (!isHandleValid(handle))
+ return String();
+
+ StringVector<UChar> buffer(MAX_PATH);
+ if (::GetFinalPathNameByHandleW(handle, buffer.data(), buffer.size(), VOLUME_NAME_NT) >= MAX_PATH) {
+ closeFile(handle);
+ return String();
+ }
+ closeFile(handle);
+
+ buffer.shrink(wcslen(buffer.data()));
+ return String::adopt(WTFMove(buffer));
+}
+
+bool getFileMetadata(const String& path, FileMetadata& metadata, ShouldFollowSymbolicLinks shouldFollowSymbolicLinks)
{
WIN32_FIND_DATAW findData;
if (!getFindData(path, findData))
return false;
+ bool isSymbolicLink = findData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT && findData.dwReserved0 == IO_REPARSE_TAG_SYMLINK;
+ if (isSymbolicLink && shouldFollowSymbolicLinks == ShouldFollowSymbolicLinks::Yes) {
+ String targetPath = getFinalPathName(path);
+ if (targetPath.isNull())
+ return false;
+ if (!getFindData(targetPath, findData))
+ return false;
+ isSymbolicLink = false;
+ }
+
if (!getFileSizeFromFindData(findData, metadata.length))
return false;
getFileModificationTimeFromFindData(findData, modificationTime);
metadata.modificationTime = modificationTime;
metadata.isHidden = findData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN;
- metadata.type = (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? FileMetadata::TypeDirectory : FileMetadata::TypeFile;
+ if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ metadata.type = FileMetadata::TypeDirectory;
+ else if (isSymbolicLink)
+ metadata.type = FileMetadata::TypeSymbolicLink;
+ else
+ metadata.type = FileMetadata::TypeFile;
return true;
}
+bool createSymbolicLink(const String& targetPath, const String& symbolicLinkPath)
+{
+ return !::CreateSymbolicLinkW(symbolicLinkPath.charactersWithNullTermination().data(), targetPath.charactersWithNullTermination().data(), 0);
+}
+
bool fileExists(const String& path)
{
WIN32_FIND_DATAW findData;
+2017-08-31 Chris Dumez <cdumez@apple.com>
+
+ getFileMetadata() does not work as expected for symbolic links
+ https://bugs.webkit.org/show_bug.cgi?id=176143
+
+ Reviewed by Andreas Kling.
+
+ Add API test coverage.
+
+ * TestWebKitAPI/Tests/WebCore/FileSystem.cpp:
+ (TestWebKitAPI::TEST_F):
+
2017-08-31 Filip Pizlo <fpizlo@apple.com>
WSL parser should pass the token as the origin to the AST
#include "config.h"
#include "Test.h"
+#include <WebCore/FileMetadata.h>
#include <WebCore/FileSystem.h>
#include <wtf/MainThread.h>
#include <wtf/StringExtras.h>
PlatformFileHandle handle;
m_tempFilePath = openTemporaryFile("tempTestFile", handle);
writeToFile(handle, FileSystemTestData, strlen(FileSystemTestData));
- closeFile(handle);
+ closeFile(handle);
+
+ m_tempFileSymlinkPath = m_tempFilePath + "-symlink";
+ createSymbolicLink(m_tempFilePath, m_tempFileSymlinkPath);
m_tempEmptyFilePath = openTemporaryFile("tempEmptyTestFile", handle);
closeFile(handle);
void TearDown() override
{
deleteFile(m_tempFilePath);
+ deleteFile(m_tempFileSymlinkPath);
deleteFile(m_tempEmptyFilePath);
deleteFile(m_spaceContainingFilePath);
deleteFile(m_bangContainingFilePath);
}
const String& tempFilePath() { return m_tempFilePath; }
+ const String& tempFileSymlinkPath() { return m_tempFileSymlinkPath; }
const String& tempEmptyFilePath() { return m_tempEmptyFilePath; }
const String& spaceContainingFilePath() { return m_spaceContainingFilePath; }
const String& bangContainingFilePath() { return m_bangContainingFilePath; }
private:
String m_tempFilePath;
+ String m_tempFileSymlinkPath;
String m_tempEmptyFilePath;
String m_spaceContainingFilePath;
String m_bangContainingFilePath;
EXPECT_TRUE(filesHaveSameVolume(bangContainingFilePath(), quoteContainingFilePath()));
}
+TEST_F(FileSystemTest, GetFileMetadataSymlink)
+{
+ FileMetadata symlinkMetadata;
+ EXPECT_TRUE(getFileMetadata(tempFileSymlinkPath(), symlinkMetadata, ShouldFollowSymbolicLinks::No));
+ EXPECT_TRUE(symlinkMetadata.type == FileMetadata::TypeSymbolicLink);
+ EXPECT_FALSE(static_cast<size_t>(symlinkMetadata.length) == strlen(FileSystemTestData));
+
+ FileMetadata targetMetadata;
+ EXPECT_TRUE(getFileMetadata(tempFileSymlinkPath(), targetMetadata, ShouldFollowSymbolicLinks::Yes));
+ EXPECT_TRUE(targetMetadata.type == FileMetadata::TypeFile);
+ EXPECT_EQ(strlen(FileSystemTestData), static_cast<size_t>(targetMetadata.length));
+}
+
}