FileSystemPOSIX.cpp [plain text]
#include "config.h"
#include "FileSystem.h"
#include "FileMetadata.h"
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <fnmatch.h>
#include <libgen.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <wtf/text/CString.h>
#include <wtf/text/WTFString.h>
namespace WebCore {
bool fileExists(const String& path)
{
if (path.isNull())
return false;
CString fsRep = fileSystemRepresentation(path);
if (!fsRep.data() || fsRep.data()[0] == '\0')
return false;
struct stat fileInfo;
return !stat(fsRep.data(), &fileInfo);
}
bool deleteFile(const String& path)
{
CString fsRep = fileSystemRepresentation(path);
if (!fsRep.data() || fsRep.data()[0] == '\0')
return false;
return !unlink(fsRep.data());
}
PlatformFileHandle openFile(const String& path, FileOpenMode mode)
{
CString fsRep = fileSystemRepresentation(path);
if (fsRep.isNull())
return invalidPlatformFileHandle;
int platformFlag = 0;
if (mode == OpenForRead)
platformFlag |= O_RDONLY;
else if (mode == OpenForWrite)
platformFlag |= (O_WRONLY | O_CREAT | O_TRUNC);
#if OS(DARWIN)
else if (mode == OpenForEventsOnly)
platformFlag |= O_EVTONLY;
#endif
return open(fsRep.data(), platformFlag, 0666);
}
void closeFile(PlatformFileHandle& handle)
{
if (isHandleValid(handle)) {
close(handle);
handle = invalidPlatformFileHandle;
}
}
long long seekFile(PlatformFileHandle handle, long long offset, FileSeekOrigin origin)
{
int whence = SEEK_SET;
switch (origin) {
case SeekFromBeginning:
whence = SEEK_SET;
break;
case SeekFromCurrent:
whence = SEEK_CUR;
break;
case SeekFromEnd:
whence = SEEK_END;
break;
default:
ASSERT_NOT_REACHED();
}
return static_cast<long long>(lseek(handle, offset, whence));
}
bool truncateFile(PlatformFileHandle handle, long long offset)
{
return !ftruncate(handle, offset);
}
int writeToFile(PlatformFileHandle handle, const char* data, int length)
{
do {
int bytesWritten = write(handle, data, static_cast<size_t>(length));
if (bytesWritten >= 0)
return bytesWritten;
} while (errno == EINTR);
return -1;
}
int readFromFile(PlatformFileHandle handle, char* data, int length)
{
do {
int bytesRead = read(handle, data, static_cast<size_t>(length));
if (bytesRead >= 0)
return bytesRead;
} while (errno == EINTR);
return -1;
}
#if USE(FILE_LOCK)
bool lockFile(PlatformFileHandle handle, FileLockMode lockMode)
{
COMPILE_ASSERT(LOCK_SH == LockShared, LockSharedEncodingIsAsExpected);
COMPILE_ASSERT(LOCK_EX == LockExclusive, LockExclusiveEncodingIsAsExpected);
COMPILE_ASSERT(LOCK_NB == LockNonBlocking, LockNonBlockingEncodingIsAsExpected);
int result = flock(handle, lockMode);
return (result != -1);
}
bool unlockFile(PlatformFileHandle handle)
{
int result = flock(handle, LOCK_UN);
return (result != -1);
}
#endif
#if !PLATFORM(MAC)
bool deleteEmptyDirectory(const String& path)
{
CString fsRep = fileSystemRepresentation(path);
if (!fsRep.data() || fsRep.data()[0] == '\0')
return false;
return !rmdir(fsRep.data());
}
#endif
bool getFileSize(const String& path, long long& result)
{
CString fsRep = fileSystemRepresentation(path);
if (!fsRep.data() || fsRep.data()[0] == '\0')
return false;
struct stat fileInfo;
if (stat(fsRep.data(), &fileInfo))
return false;
result = fileInfo.st_size;
return true;
}
bool getFileSize(PlatformFileHandle handle, long long& result)
{
struct stat fileInfo;
if (fstat(handle, &fileInfo))
return false;
result = fileInfo.st_size;
return true;
}
bool getFileCreationTime(const String& path, time_t& result)
{
#if OS(DARWIN) || OS(OPENBSD) || OS(NETBSD) || OS(FREEBSD)
CString fsRep = fileSystemRepresentation(path);
if (!fsRep.data() || fsRep.data()[0] == '\0')
return false;
struct stat fileInfo;
if (stat(fsRep.data(), &fileInfo))
return false;
result = fileInfo.st_birthtime;
return true;
#else
UNUSED_PARAM(path);
UNUSED_PARAM(result);
return false;
#endif
}
bool getFileModificationTime(const String& path, time_t& result)
{
CString fsRep = fileSystemRepresentation(path);
if (!fsRep.data() || fsRep.data()[0] == '\0')
return false;
struct stat fileInfo;
if (stat(fsRep.data(), &fileInfo))
return false;
result = fileInfo.st_mtime;
return true;
}
bool getFileMetadata(const String& path, FileMetadata& metadata)
{
CString fsRep = fileSystemRepresentation(path);
if (!fsRep.data() || fsRep.data()[0] == '\0')
return false;
struct stat fileInfo;
if (stat(fsRep.data(), &fileInfo))
return false;
metadata.modificationTime = fileInfo.st_mtime;
metadata.length = fileInfo.st_size;
metadata.type = S_ISDIR(fileInfo.st_mode) ? FileMetadata::TypeDirectory : FileMetadata::TypeFile;
return true;
}
String pathByAppendingComponent(const String& path, const String& component)
{
if (path.endsWith('/'))
return path + component;
return path + "/" + component;
}
bool makeAllDirectories(const String& path)
{
CString fullPath = fileSystemRepresentation(path);
if (!access(fullPath.data(), F_OK))
return true;
char* p = fullPath.mutableData() + 1;
int length = fullPath.length();
if(p[length - 1] == '/')
p[length - 1] = '\0';
for (; *p; ++p)
if (*p == '/') {
*p = '\0';
if (access(fullPath.data(), F_OK))
if (mkdir(fullPath.data(), S_IRWXU))
return false;
*p = '/';
}
if (access(fullPath.data(), F_OK))
if (mkdir(fullPath.data(), S_IRWXU))
return false;
return true;
}
String pathGetFileName(const String& path)
{
return path.substring(path.reverseFind('/') + 1);
}
String directoryName(const String& path)
{
CString fsRep = fileSystemRepresentation(path);
if (!fsRep.data() || fsRep.data()[0] == '\0')
return String();
return dirname(fsRep.mutableData());
}
Vector<String> listDirectory(const String& path, const String& filter)
{
Vector<String> entries;
CString cpath = fileSystemRepresentation(path);
CString cfilter = fileSystemRepresentation(filter);
DIR* dir = opendir(cpath.data());
if (dir) {
struct dirent* dp;
while ((dp = readdir(dir))) {
const char* name = dp->d_name;
if (!strcmp(name, ".") || !strcmp(name, ".."))
continue;
if (fnmatch(cfilter.data(), name, 0))
continue;
char filePath[PATH_MAX];
if (static_cast<int>(sizeof(filePath) - 1) < snprintf(filePath, sizeof(filePath), "%s/%s", cpath.data(), name))
continue;
auto string = stringFromFileSystemRepresentation(filePath);
if (!string.isNull())
entries.append(WTFMove(string));
}
closedir(dir);
}
return entries;
}
#if !OS(DARWIN) || PLATFORM(GTK)
String openTemporaryFile(const String& prefix, PlatformFileHandle& handle)
{
char buffer[PATH_MAX];
const char* tmpDir = getenv("TMPDIR");
if (!tmpDir)
tmpDir = "/tmp";
if (snprintf(buffer, PATH_MAX, "%s/%sXXXXXX", tmpDir, prefix.utf8().data()) >= PATH_MAX)
goto end;
handle = mkstemp(buffer);
if (handle < 0)
goto end;
return String::fromUTF8(buffer);
end:
handle = invalidPlatformFileHandle;
return String();
}
#endif
bool hardLinkOrCopyFile(const String& source, const String& destination)
{
if (source.isEmpty() || destination.isEmpty())
return false;
CString fsSource = fileSystemRepresentation(source);
if (!fsSource.data())
return false;
CString fsDestination = fileSystemRepresentation(destination);
if (!fsDestination.data())
return false;
if (!link(fsSource.data(), fsDestination.data()))
return true;
auto handle = open(fsDestination.data(), O_WRONLY | O_CREAT | O_EXCL, 0666);
if (handle == -1)
return false;
bool appendResult = appendFileContentsToFileHandle(source, handle);
close(handle);
if (!appendResult)
unlink(fsDestination.data());
return appendResult;
}
std::optional<int32_t> getFileDeviceId(const CString& fsFile)
{
struct stat fileStat;
if (stat(fsFile.data(), &fileStat) == -1)
return std::nullopt;
return fileStat.st_dev;
}
}