f262fad639
BUG=http://dartbug.com/26199 R=fschneider@google.com Review URL: https://codereview.chromium.org/1915853004 .
703 lines
19 KiB
C++
703 lines
19 KiB
C++
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
#include "platform/globals.h"
|
|
#if defined(TARGET_OS_WINDOWS)
|
|
|
|
#include "bin/file.h"
|
|
|
|
#include <fcntl.h> // NOLINT
|
|
#include <io.h> // NOLINT
|
|
#include <stdio.h> // NOLINT
|
|
#include <string.h> // NOLINT
|
|
#include <sys/stat.h> // NOLINT
|
|
#include <WinIoCtl.h> // NOLINT
|
|
|
|
#include "bin/builtin.h"
|
|
#include "bin/log.h"
|
|
#include "bin/utils.h"
|
|
#include "bin/utils_win.h"
|
|
#include "platform/utils.h"
|
|
|
|
namespace dart {
|
|
namespace bin {
|
|
|
|
class FileHandle {
|
|
public:
|
|
explicit FileHandle(int fd) : fd_(fd) { }
|
|
~FileHandle() { }
|
|
int fd() const { return fd_; }
|
|
void set_fd(int fd) { fd_ = fd; }
|
|
|
|
private:
|
|
int fd_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(FileHandle);
|
|
};
|
|
|
|
|
|
File::~File() {
|
|
if (!IsClosed()) {
|
|
Close();
|
|
}
|
|
delete handle_;
|
|
}
|
|
|
|
|
|
void File::Close() {
|
|
ASSERT(handle_->fd() >= 0);
|
|
if ((handle_->fd() == _fileno(stdout)) ||
|
|
(handle_->fd() == _fileno(stderr))) {
|
|
int fd = _open("NUL", _O_WRONLY);
|
|
ASSERT(fd >= 0);
|
|
_dup2(fd, handle_->fd());
|
|
close(fd);
|
|
} else {
|
|
int err = close(handle_->fd());
|
|
if (err != 0) {
|
|
Log::PrintErr("%s\n", strerror(errno));
|
|
}
|
|
}
|
|
handle_->set_fd(kClosedFd);
|
|
}
|
|
|
|
|
|
intptr_t File::GetFD() {
|
|
return handle_->fd();
|
|
}
|
|
|
|
|
|
bool File::IsClosed() {
|
|
return handle_->fd() == kClosedFd;
|
|
}
|
|
|
|
|
|
void* File::MapExecutable(intptr_t* len) {
|
|
UNIMPLEMENTED();
|
|
return NULL;
|
|
}
|
|
|
|
|
|
int64_t File::Read(void* buffer, int64_t num_bytes) {
|
|
ASSERT(handle_->fd() >= 0);
|
|
return read(handle_->fd(), buffer, num_bytes);
|
|
}
|
|
|
|
|
|
int64_t File::Write(const void* buffer, int64_t num_bytes) {
|
|
ASSERT(handle_->fd() >= 0);
|
|
return write(handle_->fd(), buffer, num_bytes);
|
|
}
|
|
|
|
|
|
int64_t File::Position() {
|
|
ASSERT(handle_->fd() >= 0);
|
|
return _lseeki64(handle_->fd(), 0, SEEK_CUR);
|
|
}
|
|
|
|
|
|
bool File::SetPosition(int64_t position) {
|
|
ASSERT(handle_->fd() >= 0);
|
|
return _lseeki64(handle_->fd(), position, SEEK_SET) >= 0;
|
|
}
|
|
|
|
|
|
bool File::Truncate(int64_t length) {
|
|
ASSERT(handle_->fd() >= 0);
|
|
return _chsize_s(handle_->fd(), length) == 0;
|
|
}
|
|
|
|
|
|
bool File::Flush() {
|
|
ASSERT(handle_->fd());
|
|
return _commit(handle_->fd()) != -1;
|
|
}
|
|
|
|
|
|
bool File::Lock(File::LockType lock, int64_t start, int64_t end) {
|
|
ASSERT(handle_->fd() >= 0);
|
|
ASSERT((end == -1) || (end > start));
|
|
HANDLE handle = reinterpret_cast<HANDLE>(_get_osfhandle(handle_->fd()));
|
|
OVERLAPPED overlapped;
|
|
ZeroMemory(&overlapped, sizeof(OVERLAPPED));
|
|
|
|
overlapped.Offset = Utils::Low32Bits(start);
|
|
overlapped.OffsetHigh = Utils::High32Bits(start);
|
|
|
|
int64_t length = end == -1 ? 0 : end - start;
|
|
if (length == 0) {
|
|
length = kMaxInt64;
|
|
}
|
|
int32_t length_low = Utils::Low32Bits(length);
|
|
int32_t length_high = Utils::High32Bits(length);
|
|
|
|
BOOL rc;
|
|
switch (lock) {
|
|
case File::kLockUnlock:
|
|
rc = UnlockFileEx(handle, 0, length_low, length_high, &overlapped);
|
|
break;
|
|
case File::kLockShared:
|
|
case File::kLockExclusive: {
|
|
DWORD flags = LOCKFILE_FAIL_IMMEDIATELY;
|
|
if (lock == File::kLockExclusive) {
|
|
flags |= LOCKFILE_EXCLUSIVE_LOCK;
|
|
}
|
|
rc = LockFileEx(handle, flags, 0,
|
|
length_low, length_high, &overlapped);
|
|
break;
|
|
}
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
|
|
int64_t File::Length() {
|
|
ASSERT(handle_->fd() >= 0);
|
|
struct __stat64 st;
|
|
if (_fstat64(handle_->fd(), &st) == 0) {
|
|
return st.st_size;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
File* File::FileOpenW(const wchar_t* system_name, FileOpenMode mode) {
|
|
int flags = O_RDONLY | O_BINARY | O_NOINHERIT;
|
|
if ((mode & kWrite) != 0) {
|
|
ASSERT((mode & kWriteOnly) == 0);
|
|
flags = (O_RDWR | O_CREAT | O_BINARY | O_NOINHERIT);
|
|
}
|
|
if ((mode & kWriteOnly) != 0) {
|
|
ASSERT((mode & kWrite) == 0);
|
|
flags = (O_WRONLY | O_CREAT | O_BINARY | O_NOINHERIT);
|
|
}
|
|
if ((mode & kTruncate) != 0) {
|
|
flags = flags | O_TRUNC;
|
|
}
|
|
int fd = _wopen(system_name, flags, 0666);
|
|
if (fd < 0) {
|
|
return NULL;
|
|
}
|
|
if ((((mode & kWrite) != 0) && ((mode & kTruncate) == 0)) ||
|
|
(((mode & kWriteOnly) != 0) && ((mode & kTruncate) == 0))) {
|
|
int64_t position = _lseeki64(fd, 0, SEEK_END);
|
|
if (position < 0) {
|
|
return NULL;
|
|
}
|
|
}
|
|
return new File(new FileHandle(fd));
|
|
}
|
|
|
|
|
|
File* File::ScopedOpen(const char* name, FileOpenMode mode) {
|
|
const wchar_t* system_name = StringUtilsWin::Utf8ToWide(name);
|
|
return FileOpenW(system_name, mode);
|
|
}
|
|
|
|
|
|
File* File::Open(const char* path, FileOpenMode mode) {
|
|
int path_len = MultiByteToWideChar(CP_UTF8, 0, path, -1, NULL, 0);
|
|
wchar_t* system_name = new wchar_t[path_len];
|
|
if (system_name == NULL) {
|
|
return NULL;
|
|
}
|
|
MultiByteToWideChar(CP_UTF8, 0, path, -1, system_name, path_len);
|
|
File* file = FileOpenW(system_name, mode);
|
|
delete[] system_name;
|
|
return file;
|
|
}
|
|
|
|
|
|
File* File::OpenStdio(int fd) {
|
|
switch (fd) {
|
|
case 1:
|
|
fd = _fileno(stdout);
|
|
break;
|
|
case 2:
|
|
fd = _fileno(stderr);
|
|
break;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
_setmode(fd, _O_BINARY);
|
|
return new File(new FileHandle(fd));
|
|
}
|
|
|
|
|
|
bool File::Exists(const char* name) {
|
|
struct __stat64 st;
|
|
const wchar_t* system_name = StringUtilsWin::Utf8ToWide(name);
|
|
bool stat_status = _wstat64(system_name, &st);
|
|
if (stat_status == 0) {
|
|
return ((st.st_mode & S_IFMT) == S_IFREG);
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
bool File::Create(const char* name) {
|
|
const wchar_t* system_name = StringUtilsWin::Utf8ToWide(name);
|
|
int fd = _wopen(system_name, O_RDONLY | O_CREAT, 0666);
|
|
if (fd < 0) {
|
|
return false;
|
|
}
|
|
return (close(fd) == 0);
|
|
}
|
|
|
|
|
|
// This structure is needed for creating and reading Junctions.
|
|
typedef struct _REPARSE_DATA_BUFFER {
|
|
ULONG ReparseTag;
|
|
USHORT ReparseDataLength;
|
|
USHORT Reserved;
|
|
|
|
union {
|
|
struct {
|
|
USHORT SubstituteNameOffset;
|
|
USHORT SubstituteNameLength;
|
|
USHORT PrintNameOffset;
|
|
USHORT PrintNameLength;
|
|
ULONG Flags;
|
|
WCHAR PathBuffer[1];
|
|
} SymbolicLinkReparseBuffer;
|
|
|
|
struct {
|
|
USHORT SubstituteNameOffset;
|
|
USHORT SubstituteNameLength;
|
|
USHORT PrintNameOffset;
|
|
USHORT PrintNameLength;
|
|
WCHAR PathBuffer[1];
|
|
} MountPointReparseBuffer;
|
|
|
|
struct {
|
|
UCHAR DataBuffer[1];
|
|
} GenericReparseBuffer;
|
|
};
|
|
} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
|
|
|
|
|
|
static const int kReparseDataHeaderSize = sizeof ULONG + 2 * sizeof USHORT;
|
|
static const int kMountPointHeaderSize = 4 * sizeof USHORT;
|
|
|
|
|
|
bool File::CreateLink(const char* utf8_name, const char* utf8_target) {
|
|
const wchar_t* name = StringUtilsWin::Utf8ToWide(utf8_name);
|
|
int create_status = CreateDirectoryW(name, NULL);
|
|
// If the directory already existed, treat it as a success.
|
|
if ((create_status == 0) &&
|
|
((GetLastError() != ERROR_ALREADY_EXISTS) ||
|
|
((GetFileAttributesW(name) & FILE_ATTRIBUTE_DIRECTORY) != 0))) {
|
|
return false;
|
|
}
|
|
|
|
HANDLE dir_handle = CreateFileW(
|
|
name,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
|
|
NULL);
|
|
if (dir_handle == INVALID_HANDLE_VALUE) {
|
|
return false;
|
|
}
|
|
|
|
const wchar_t* target = StringUtilsWin::Utf8ToWide(utf8_target);
|
|
int target_len = wcslen(target);
|
|
if (target_len > MAX_PATH - 1) {
|
|
CloseHandle(dir_handle);
|
|
return false;
|
|
}
|
|
|
|
int reparse_data_buffer_size =
|
|
sizeof REPARSE_DATA_BUFFER + 2 * MAX_PATH * sizeof WCHAR;
|
|
REPARSE_DATA_BUFFER* reparse_data_buffer =
|
|
reinterpret_cast<REPARSE_DATA_BUFFER*>(Dart_ScopeAllocate(
|
|
reparse_data_buffer_size));
|
|
reparse_data_buffer->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
|
|
wcscpy(reparse_data_buffer->MountPointReparseBuffer.PathBuffer, target);
|
|
wcscpy(
|
|
reparse_data_buffer->MountPointReparseBuffer.PathBuffer + target_len + 1,
|
|
target);
|
|
reparse_data_buffer->MountPointReparseBuffer.SubstituteNameOffset = 0;
|
|
reparse_data_buffer->MountPointReparseBuffer.SubstituteNameLength =
|
|
target_len * sizeof WCHAR;
|
|
reparse_data_buffer->MountPointReparseBuffer.PrintNameOffset =
|
|
(target_len + 1) * sizeof WCHAR;
|
|
reparse_data_buffer->MountPointReparseBuffer.PrintNameLength =
|
|
target_len * sizeof WCHAR;
|
|
reparse_data_buffer->ReparseDataLength =
|
|
(target_len + 1) * 2 * sizeof WCHAR + kMountPointHeaderSize;
|
|
DWORD dummy_received_bytes;
|
|
int result = DeviceIoControl(
|
|
dir_handle,
|
|
FSCTL_SET_REPARSE_POINT,
|
|
reparse_data_buffer,
|
|
reparse_data_buffer->ReparseDataLength + kReparseDataHeaderSize,
|
|
NULL,
|
|
0,
|
|
&dummy_received_bytes,
|
|
NULL);
|
|
if (CloseHandle(dir_handle) == 0) {
|
|
return false;
|
|
}
|
|
return (result != 0);
|
|
}
|
|
|
|
|
|
bool File::Delete(const char* name) {
|
|
const wchar_t* system_name = StringUtilsWin::Utf8ToWide(name);
|
|
int status = _wremove(system_name);
|
|
return status != -1;
|
|
}
|
|
|
|
|
|
bool File::DeleteLink(const char* name) {
|
|
const wchar_t* system_name = StringUtilsWin::Utf8ToWide(name);
|
|
bool result = false;
|
|
DWORD attributes = GetFileAttributesW(system_name);
|
|
if ((attributes != INVALID_FILE_ATTRIBUTES) &&
|
|
(attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
|
|
// It's a junction(link), delete it.
|
|
result = (RemoveDirectoryW(system_name) != 0);
|
|
} else {
|
|
SetLastError(ERROR_NOT_A_REPARSE_POINT);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
bool File::Rename(const char* old_path, const char* new_path) {
|
|
File::Type type = GetType(old_path, false);
|
|
if (type == kIsFile) {
|
|
const wchar_t* system_old_path = StringUtilsWin::Utf8ToWide(old_path);
|
|
const wchar_t* system_new_path = StringUtilsWin::Utf8ToWide(new_path);
|
|
DWORD flags = MOVEFILE_WRITE_THROUGH | MOVEFILE_REPLACE_EXISTING;
|
|
int move_status =
|
|
MoveFileExW(system_old_path, system_new_path, flags);
|
|
return (move_status != 0);
|
|
} else {
|
|
SetLastError(ERROR_FILE_NOT_FOUND);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
bool File::RenameLink(const char* old_path, const char* new_path) {
|
|
File::Type type = GetType(old_path, false);
|
|
if (type == kIsLink) {
|
|
const wchar_t* system_old_path = StringUtilsWin::Utf8ToWide(old_path);
|
|
const wchar_t* system_new_path = StringUtilsWin::Utf8ToWide(new_path);
|
|
DWORD flags = MOVEFILE_WRITE_THROUGH | MOVEFILE_REPLACE_EXISTING;
|
|
int move_status =
|
|
MoveFileExW(system_old_path, system_new_path, flags);
|
|
return (move_status != 0);
|
|
} else {
|
|
SetLastError(ERROR_FILE_NOT_FOUND);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
bool File::Copy(const char* old_path, const char* new_path) {
|
|
File::Type type = GetType(old_path, false);
|
|
if (type == kIsFile) {
|
|
const wchar_t* system_old_path = StringUtilsWin::Utf8ToWide(old_path);
|
|
const wchar_t* system_new_path = StringUtilsWin::Utf8ToWide(new_path);
|
|
bool success = CopyFileExW(system_old_path,
|
|
system_new_path,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
0) != 0;
|
|
return success;
|
|
} else {
|
|
SetLastError(ERROR_FILE_NOT_FOUND);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
int64_t File::LengthFromPath(const char* name) {
|
|
struct __stat64 st;
|
|
const wchar_t* system_name = StringUtilsWin::Utf8ToWide(name);
|
|
int stat_status = _wstat64(system_name, &st);
|
|
if (stat_status == 0) {
|
|
return st.st_size;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
const char* File::LinkTarget(const char* pathname) {
|
|
const wchar_t* name = StringUtilsWin::Utf8ToWide(pathname);
|
|
HANDLE dir_handle = CreateFileW(
|
|
name,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
|
|
NULL);
|
|
if (dir_handle == INVALID_HANDLE_VALUE) {
|
|
return NULL;
|
|
}
|
|
|
|
int buffer_size =
|
|
sizeof REPARSE_DATA_BUFFER + 2 * (MAX_PATH + 1) * sizeof WCHAR;
|
|
REPARSE_DATA_BUFFER* buffer = reinterpret_cast<REPARSE_DATA_BUFFER*>(
|
|
Dart_ScopeAllocate(buffer_size));
|
|
DWORD received_bytes; // Value is not used.
|
|
int result = DeviceIoControl(
|
|
dir_handle,
|
|
FSCTL_GET_REPARSE_POINT,
|
|
NULL,
|
|
0,
|
|
buffer,
|
|
buffer_size,
|
|
&received_bytes,
|
|
NULL);
|
|
if (result == 0) {
|
|
DWORD error = GetLastError();
|
|
CloseHandle(dir_handle);
|
|
SetLastError(error);
|
|
return NULL;
|
|
}
|
|
if (CloseHandle(dir_handle) == 0) {
|
|
return NULL;
|
|
}
|
|
|
|
wchar_t* target;
|
|
size_t target_offset;
|
|
size_t target_length;
|
|
if (buffer->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) {
|
|
target = buffer->MountPointReparseBuffer.PathBuffer;
|
|
target_offset = buffer->MountPointReparseBuffer.SubstituteNameOffset;
|
|
target_length = buffer->MountPointReparseBuffer.SubstituteNameLength;
|
|
} else if (buffer->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
|
|
target = buffer->SymbolicLinkReparseBuffer.PathBuffer;
|
|
target_offset = buffer->SymbolicLinkReparseBuffer.SubstituteNameOffset;
|
|
target_length = buffer->SymbolicLinkReparseBuffer.SubstituteNameLength;
|
|
} else { // Not a junction or a symbolic link.
|
|
SetLastError(ERROR_NOT_A_REPARSE_POINT);
|
|
return NULL;
|
|
}
|
|
|
|
target_offset /= sizeof(wchar_t); // Offset and length are in bytes.
|
|
target_length /= sizeof(wchar_t);
|
|
target += target_offset;
|
|
// Remove "\??\" from beginning of target.
|
|
if ((target_length > 4) && (wcsncmp(L"\\??\\", target, 4) == 0)) {
|
|
target += 4;
|
|
target_length -= 4;
|
|
}
|
|
int utf8_length = WideCharToMultiByte(CP_UTF8,
|
|
0,
|
|
target,
|
|
target_length,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
NULL);
|
|
char* utf8_target = DartUtils::ScopedCString(utf8_length + 1);
|
|
if (0 == WideCharToMultiByte(CP_UTF8,
|
|
0,
|
|
target,
|
|
target_length,
|
|
utf8_target,
|
|
utf8_length,
|
|
NULL,
|
|
NULL)) {
|
|
return NULL;
|
|
}
|
|
utf8_target[utf8_length] = '\0';
|
|
return utf8_target;
|
|
}
|
|
|
|
|
|
void File::Stat(const char* name, int64_t* data) {
|
|
File::Type type = GetType(name, false);
|
|
data[kType] = type;
|
|
if (type != kDoesNotExist) {
|
|
struct _stat64 st;
|
|
const wchar_t* system_name = StringUtilsWin::Utf8ToWide(name);
|
|
int stat_status = _wstat64(system_name, &st);
|
|
if (stat_status == 0) {
|
|
data[kCreatedTime] = st.st_ctime * 1000;
|
|
data[kModifiedTime] = st.st_mtime * 1000;
|
|
data[kAccessedTime] = st.st_atime * 1000;
|
|
data[kMode] = st.st_mode;
|
|
data[kSize] = st.st_size;
|
|
} else {
|
|
data[kType] = File::kDoesNotExist;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
time_t File::LastModified(const char* name) {
|
|
struct __stat64 st;
|
|
const wchar_t* system_name = StringUtilsWin::Utf8ToWide(name);
|
|
int stat_status = _wstat64(system_name, &st);
|
|
if (stat_status == 0) {
|
|
return st.st_mtime;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
bool File::IsAbsolutePath(const char* pathname) {
|
|
// Should we consider network paths?
|
|
if (pathname == NULL) {
|
|
return false;
|
|
}
|
|
return ((strlen(pathname) > 2) &&
|
|
(pathname[1] == ':') &&
|
|
((pathname[2] == '\\') || (pathname[2] == '/')));
|
|
}
|
|
|
|
|
|
const char* File::GetCanonicalPath(const char* pathname) {
|
|
const wchar_t* system_name = StringUtilsWin::Utf8ToWide(pathname);
|
|
HANDLE file_handle = CreateFileW(
|
|
system_name,
|
|
0,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_FLAG_BACKUP_SEMANTICS,
|
|
NULL);
|
|
if (file_handle == INVALID_HANDLE_VALUE) {
|
|
return NULL;
|
|
}
|
|
wchar_t dummy_buffer[1];
|
|
int required_size = GetFinalPathNameByHandle(file_handle,
|
|
dummy_buffer,
|
|
0,
|
|
VOLUME_NAME_DOS);
|
|
if (required_size == 0) {
|
|
DWORD error = GetLastError();
|
|
CloseHandle(file_handle);
|
|
SetLastError(error);
|
|
return NULL;
|
|
}
|
|
wchar_t* path;
|
|
path = reinterpret_cast<wchar_t*>(
|
|
Dart_ScopeAllocate(required_size * sizeof(*path)));
|
|
int result_size = GetFinalPathNameByHandle(file_handle,
|
|
path,
|
|
required_size,
|
|
VOLUME_NAME_DOS);
|
|
ASSERT(result_size <= required_size - 1);
|
|
// Remove leading \\?\ if possible, unless input used it.
|
|
char* result;
|
|
if ((result_size < MAX_PATH - 1 + 4) &&
|
|
(result_size > 4) &&
|
|
(wcsncmp(path, L"\\\\?\\", 4) == 0) &&
|
|
(wcsncmp(system_name, L"\\\\?\\", 4) != 0)) {
|
|
result = StringUtilsWin::WideToUtf8(path + 4);
|
|
} else {
|
|
result = StringUtilsWin::WideToUtf8(path);
|
|
}
|
|
CloseHandle(file_handle);
|
|
return result;
|
|
}
|
|
|
|
|
|
const char* File::PathSeparator() {
|
|
// This is already UTF-8 encoded.
|
|
return "\\";
|
|
}
|
|
|
|
|
|
const char* File::StringEscapedPathSeparator() {
|
|
// This is already UTF-8 encoded.
|
|
return "\\\\";
|
|
}
|
|
|
|
|
|
File::StdioHandleType File::GetStdioHandleType(int fd) {
|
|
// Treat all stdio handles as pipes. The Windows event handler and
|
|
// socket code will handle the different handle types.
|
|
return kPipe;
|
|
}
|
|
|
|
|
|
File::Type File::GetType(const char* pathname, bool follow_links) {
|
|
const wchar_t* name = StringUtilsWin::Utf8ToWide(pathname);
|
|
DWORD attributes = GetFileAttributesW(name);
|
|
File::Type result = kIsFile;
|
|
if (attributes == INVALID_FILE_ATTRIBUTES) {
|
|
result = kDoesNotExist;
|
|
} else if ((attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
|
|
if (follow_links) {
|
|
HANDLE dir_handle = CreateFileW(
|
|
name,
|
|
0,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_FLAG_BACKUP_SEMANTICS,
|
|
NULL);
|
|
if (dir_handle == INVALID_HANDLE_VALUE) {
|
|
result = File::kIsLink;
|
|
} else {
|
|
CloseHandle(dir_handle);
|
|
result = File::kIsDirectory;
|
|
}
|
|
} else {
|
|
result = kIsLink;
|
|
}
|
|
} else if ((attributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
|
|
result = kIsDirectory;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
File::Identical File::AreIdentical(const char* file_1, const char* file_2) {
|
|
BY_HANDLE_FILE_INFORMATION file_info[2];
|
|
const char* file_names[2] = { file_1, file_2 };
|
|
for (int i = 0; i < 2; ++i) {
|
|
const wchar_t* wide_name = StringUtilsWin::Utf8ToWide(file_names[i]);
|
|
HANDLE file_handle = CreateFileW(
|
|
wide_name,
|
|
0,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
|
|
NULL);
|
|
if (file_handle == INVALID_HANDLE_VALUE) {
|
|
return File::kError;
|
|
}
|
|
int result = GetFileInformationByHandle(file_handle, &file_info[i]);
|
|
if (result == 0) {
|
|
DWORD error = GetLastError();
|
|
CloseHandle(file_handle);
|
|
SetLastError(error);
|
|
return File::kError;
|
|
}
|
|
if (CloseHandle(file_handle) == 0) {
|
|
return File::kError;
|
|
}
|
|
}
|
|
if ((file_info[0].dwVolumeSerialNumber ==
|
|
file_info[1].dwVolumeSerialNumber) &&
|
|
(file_info[0].nFileIndexHigh == file_info[1].nFileIndexHigh) &&
|
|
(file_info[0].nFileIndexLow == file_info[1].nFileIndexLow)) {
|
|
return kIdentical;
|
|
} else {
|
|
return kDifferent;
|
|
}
|
|
}
|
|
|
|
} // namespace bin
|
|
} // namespace dart
|
|
|
|
#endif // defined(TARGET_OS_WINDOWS)
|