summaryrefslogtreecommitdiff
path: root/lib/Support/Windows/Path.inc
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Support/Windows/Path.inc')
-rw-r--r--lib/Support/Windows/Path.inc479
1 files changed, 310 insertions, 169 deletions
diff --git a/lib/Support/Windows/Path.inc b/lib/Support/Windows/Path.inc
index b00d3905f658..f81790b17df5 100644
--- a/lib/Support/Windows/Path.inc
+++ b/lib/Support/Windows/Path.inc
@@ -94,13 +94,16 @@ std::error_code widenPath(const Twine &Path8,
return EC;
FullPath.append(CurPath);
}
- // Traverse the requested path, canonicalizing . and .. as we go (because
- // the \\?\ prefix is documented to treat them as real components).
- // The iterators don't report separators and append() always attaches
- // preferred_separator so we don't need to call native() on the result.
+ // Traverse the requested path, canonicalizing . and .. (because the \\?\
+ // prefix is documented to treat them as real components). Ignore
+ // separators, which can be returned from the iterator if the path has a
+ // drive name. We don't need to call native() on the result since append()
+ // always attaches preferred_separator.
for (llvm::sys::path::const_iterator I = llvm::sys::path::begin(Path8Str),
E = llvm::sys::path::end(Path8Str);
I != E; ++I) {
+ if (I->size() == 1 && is_separator((*I)[0]))
+ continue;
if (I->size() == 1 && *I == ".")
continue;
if (I->size() == 2 && *I == "..")
@@ -165,14 +168,14 @@ ErrorOr<space_info> disk_space(const Twine &Path) {
return SpaceInfo;
}
-TimePoint<> file_status::getLastAccessedTime() const {
+TimePoint<> basic_file_status::getLastAccessedTime() const {
FILETIME Time;
Time.dwLowDateTime = LastAccessedTimeLow;
Time.dwHighDateTime = LastAccessedTimeHigh;
return toTimePoint(Time);
}
-TimePoint<> file_status::getLastModificationTime() const {
+TimePoint<> basic_file_status::getLastModificationTime() const {
FILETIME Time;
Time.dwLowDateTime = LastWriteTimeLow;
Time.dwHighDateTime = LastWriteTimeHigh;
@@ -250,35 +253,38 @@ std::error_code create_link(const Twine &to, const Twine &from) {
}
std::error_code create_hard_link(const Twine &to, const Twine &from) {
- return create_link(to, from);
+ return create_link(to, from);
}
std::error_code remove(const Twine &path, bool IgnoreNonExisting) {
SmallVector<wchar_t, 128> path_utf16;
- file_status ST;
- if (std::error_code EC = status(path, ST)) {
- if (EC != errc::no_such_file_or_directory || !IgnoreNonExisting)
- return EC;
- return std::error_code();
- }
-
if (std::error_code ec = widenPath(path, path_utf16))
return ec;
- if (ST.type() == file_type::directory_file) {
- if (!::RemoveDirectoryW(c_str(path_utf16))) {
- std::error_code EC = mapWindowsError(::GetLastError());
- if (EC != errc::no_such_file_or_directory || !IgnoreNonExisting)
- return EC;
- }
- return std::error_code();
- }
- if (!::DeleteFileW(c_str(path_utf16))) {
+ // We don't know whether this is a file or a directory, and remove() can
+ // accept both. The usual way to delete a file or directory is to use one of
+ // the DeleteFile or RemoveDirectory functions, but that requires you to know
+ // which one it is. We could stat() the file to determine that, but that would
+ // cost us additional system calls, which can be slow in a directory
+ // containing a large number of files. So instead we call CreateFile directly.
+ // The important part is the FILE_FLAG_DELETE_ON_CLOSE flag, which causes the
+ // file to be deleted once it is closed. We also use the flags
+ // FILE_FLAG_BACKUP_SEMANTICS (which allows us to open directories), and
+ // FILE_FLAG_OPEN_REPARSE_POINT (don't follow symlinks).
+ ScopedFileHandle h(::CreateFileW(
+ c_str(path_utf16), DELETE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS |
+ FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_DELETE_ON_CLOSE,
+ NULL));
+ if (!h) {
std::error_code EC = mapWindowsError(::GetLastError());
if (EC != errc::no_such_file_or_directory || !IgnoreNonExisting)
return EC;
}
+
return std::error_code();
}
@@ -338,83 +344,261 @@ std::error_code is_local(const Twine &path, bool &result) {
return is_local_internal(WidePath, result);
}
+static std::error_code realPathFromHandle(HANDLE H,
+ SmallVectorImpl<wchar_t> &Buffer) {
+ DWORD CountChars = ::GetFinalPathNameByHandleW(
+ H, Buffer.begin(), Buffer.capacity() - 1, FILE_NAME_NORMALIZED);
+ if (CountChars > Buffer.capacity()) {
+ // The buffer wasn't big enough, try again. In this case the return value
+ // *does* indicate the size of the null terminator.
+ Buffer.reserve(CountChars);
+ CountChars = ::GetFinalPathNameByHandleW(
+ H, Buffer.data(), Buffer.capacity() - 1, FILE_NAME_NORMALIZED);
+ }
+ if (CountChars == 0)
+ return mapWindowsError(GetLastError());
+ Buffer.set_size(CountChars);
+ return std::error_code();
+}
+
+static std::error_code realPathFromHandle(HANDLE H,
+ SmallVectorImpl<char> &RealPath) {
+ RealPath.clear();
+ SmallVector<wchar_t, MAX_PATH> Buffer;
+ if (std::error_code EC = realPathFromHandle(H, Buffer))
+ return EC;
+
+ const wchar_t *Data = Buffer.data();
+ DWORD CountChars = Buffer.size();
+ if (CountChars >= 4) {
+ if (0 == ::memcmp(Data, L"\\\\?\\", 8)) {
+ CountChars -= 4;
+ Data += 4;
+ }
+ }
+
+ // Convert the result from UTF-16 to UTF-8.
+ return UTF16ToUTF8(Data, CountChars, RealPath);
+}
+
std::error_code is_local(int FD, bool &Result) {
SmallVector<wchar_t, 128> FinalPath;
HANDLE Handle = reinterpret_cast<HANDLE>(_get_osfhandle(FD));
- size_t Len = 128;
- do {
- FinalPath.reserve(Len);
- Len = ::GetFinalPathNameByHandleW(Handle, FinalPath.data(),
- FinalPath.capacity() - 1, VOLUME_NAME_NT);
- if (Len == 0)
- return mapWindowsError(::GetLastError());
- } while (Len > FinalPath.capacity());
-
- FinalPath.set_size(Len);
+ if (std::error_code EC = realPathFromHandle(Handle, FinalPath))
+ return EC;
return is_local_internal(FinalPath, Result);
}
-std::error_code rename(const Twine &from, const Twine &to) {
- // Convert to utf-16.
- SmallVector<wchar_t, 128> wide_from;
- SmallVector<wchar_t, 128> wide_to;
- if (std::error_code ec = widenPath(from, wide_from))
- return ec;
- if (std::error_code ec = widenPath(to, wide_to))
- return ec;
+static std::error_code setDeleteDisposition(HANDLE Handle, bool Delete) {
+ FILE_DISPOSITION_INFO Disposition;
+ Disposition.DeleteFile = Delete;
+ if (!SetFileInformationByHandle(Handle, FileDispositionInfo, &Disposition,
+ sizeof(Disposition)))
+ return mapWindowsError(::GetLastError());
+ return std::error_code();
+}
- std::error_code ec = std::error_code();
+static std::error_code removeFD(int FD) {
+ HANDLE Handle = reinterpret_cast<HANDLE>(_get_osfhandle(FD));
+ return setDeleteDisposition(Handle, true);
+}
- // Retry while we see recoverable errors.
- // System scanners (eg. indexer) might open the source file when it is written
- // and closed.
+/// In order to handle temporary files we want the following properties
+///
+/// * The temporary file is deleted on crashes
+/// * We can use (read, rename, etc) the temporary file.
+/// * We can cancel the delete to keep the file.
+///
+/// Using FILE_DISPOSITION_INFO with DeleteFile=true will create a file that is
+/// deleted on close, but it has a few problems:
+///
+/// * The file cannot be used. An attempt to open or rename the file will fail.
+/// This makes the temporary file almost useless, as it cannot be part of
+/// any other CreateFileW call in the current or in another process.
+/// * It is not atomic. A crash just after CreateFileW or just after canceling
+/// the delete will leave the file on disk.
+///
+/// Using FILE_FLAG_DELETE_ON_CLOSE solves the first issues and the first part
+/// of the second one, but there is no way to cancel it in place. What works is
+/// to create a second handle to prevent the deletion, close the first one and
+/// then clear DeleteFile with SetFileInformationByHandle. This requires
+/// changing the handle and file descriptor the caller uses.
+static std::error_code cancelDeleteOnClose(int &FD) {
+ HANDLE Handle = reinterpret_cast<HANDLE>(_get_osfhandle(FD));
+ SmallVector<wchar_t, MAX_PATH> Name;
+ if (std::error_code EC = realPathFromHandle(Handle, Name))
+ return EC;
+ HANDLE NewHandle =
+ ::CreateFileW(Name.data(), GENERIC_READ | GENERIC_WRITE | DELETE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (NewHandle == INVALID_HANDLE_VALUE)
+ return mapWindowsError(::GetLastError());
+ if (close(FD))
+ return mapWindowsError(::GetLastError());
- bool TryReplace = true;
+ if (std::error_code EC = setDeleteDisposition(NewHandle, false))
+ return EC;
- for (int i = 0; i < 2000; i++) {
- if (i > 0)
- ::Sleep(1);
+ FD = ::_open_osfhandle(intptr_t(NewHandle), 0);
+ if (FD == -1) {
+ ::CloseHandle(NewHandle);
+ return mapWindowsError(ERROR_INVALID_HANDLE);
+ }
+ return std::error_code();
+}
+
+static std::error_code rename_internal(HANDLE FromHandle, const Twine &To,
+ bool ReplaceIfExists) {
+ SmallVector<wchar_t, 0> ToWide;
+ if (auto EC = widenPath(To, ToWide))
+ return EC;
+
+ std::vector<char> RenameInfoBuf(sizeof(FILE_RENAME_INFO) - sizeof(wchar_t) +
+ (ToWide.size() * sizeof(wchar_t)));
+ FILE_RENAME_INFO &RenameInfo =
+ *reinterpret_cast<FILE_RENAME_INFO *>(RenameInfoBuf.data());
+ RenameInfo.ReplaceIfExists = ReplaceIfExists;
+ RenameInfo.RootDirectory = 0;
+ RenameInfo.FileNameLength = ToWide.size();
+ std::copy(ToWide.begin(), ToWide.end(), &RenameInfo.FileName[0]);
+
+ SetLastError(ERROR_SUCCESS);
+ if (!SetFileInformationByHandle(FromHandle, FileRenameInfo, &RenameInfo,
+ RenameInfoBuf.size())) {
+ unsigned Error = GetLastError();
+ if (Error == ERROR_SUCCESS)
+ Error = ERROR_CALL_NOT_IMPLEMENTED; // Wine doesn't always set error code.
+ return mapWindowsError(Error);
+ }
- if (TryReplace) {
- // Try ReplaceFile first, as it is able to associate a new data stream
- // with the destination even if the destination file is currently open.
- if (::ReplaceFileW(wide_to.data(), wide_from.data(), NULL, 0, NULL, NULL))
+ return std::error_code();
+}
+
+static std::error_code rename_handle(HANDLE FromHandle, const Twine &To) {
+ SmallVector<wchar_t, 128> WideTo;
+ if (std::error_code EC = widenPath(To, WideTo))
+ return EC;
+
+ // We normally expect this loop to succeed after a few iterations. If it
+ // requires more than 200 tries, it's more likely that the failures are due to
+ // a true error, so stop trying.
+ for (unsigned Retry = 0; Retry != 200; ++Retry) {
+ auto EC = rename_internal(FromHandle, To, true);
+
+ if (EC ==
+ std::error_code(ERROR_CALL_NOT_IMPLEMENTED, std::system_category())) {
+ // Wine doesn't support SetFileInformationByHandle in rename_internal.
+ // Fall back to MoveFileEx.
+ SmallVector<wchar_t, MAX_PATH> WideFrom;
+ if (std::error_code EC2 = realPathFromHandle(FromHandle, WideFrom))
+ return EC2;
+ if (::MoveFileExW(WideFrom.begin(), WideTo.begin(),
+ MOVEFILE_REPLACE_EXISTING))
return std::error_code();
+ return mapWindowsError(GetLastError());
+ }
- DWORD ReplaceError = ::GetLastError();
- ec = mapWindowsError(ReplaceError);
+ if (!EC || EC != errc::permission_denied)
+ return EC;
- // If ReplaceFileW returned ERROR_UNABLE_TO_MOVE_REPLACEMENT or
- // ERROR_UNABLE_TO_MOVE_REPLACEMENT_2, retry but only use MoveFileExW().
- if (ReplaceError == ERROR_UNABLE_TO_MOVE_REPLACEMENT ||
- ReplaceError == ERROR_UNABLE_TO_MOVE_REPLACEMENT_2) {
- TryReplace = false;
+ // The destination file probably exists and is currently open in another
+ // process, either because the file was opened without FILE_SHARE_DELETE or
+ // it is mapped into memory (e.g. using MemoryBuffer). Rename it in order to
+ // move it out of the way of the source file. Use FILE_FLAG_DELETE_ON_CLOSE
+ // to arrange for the destination file to be deleted when the other process
+ // closes it.
+ ScopedFileHandle ToHandle(
+ ::CreateFileW(WideTo.begin(), GENERIC_READ | DELETE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, NULL));
+ if (!ToHandle) {
+ auto EC = mapWindowsError(GetLastError());
+ // Another process might have raced with us and moved the existing file
+ // out of the way before we had a chance to open it. If that happens, try
+ // to rename the source file again.
+ if (EC == errc::no_such_file_or_directory)
continue;
+ return EC;
+ }
+
+ BY_HANDLE_FILE_INFORMATION FI;
+ if (!GetFileInformationByHandle(ToHandle, &FI))
+ return mapWindowsError(GetLastError());
+
+ // Try to find a unique new name for the destination file.
+ for (unsigned UniqueId = 0; UniqueId != 200; ++UniqueId) {
+ std::string TmpFilename = (To + ".tmp" + utostr(UniqueId)).str();
+ if (auto EC = rename_internal(ToHandle, TmpFilename, false)) {
+ if (EC == errc::file_exists || EC == errc::permission_denied) {
+ // Again, another process might have raced with us and moved the file
+ // before we could move it. Check whether this is the case, as it
+ // might have caused the permission denied error. If that was the
+ // case, we don't need to move it ourselves.
+ ScopedFileHandle ToHandle2(::CreateFileW(
+ WideTo.begin(), 0,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));
+ if (!ToHandle2) {
+ auto EC = mapWindowsError(GetLastError());
+ if (EC == errc::no_such_file_or_directory)
+ break;
+ return EC;
+ }
+ BY_HANDLE_FILE_INFORMATION FI2;
+ if (!GetFileInformationByHandle(ToHandle2, &FI2))
+ return mapWindowsError(GetLastError());
+ if (FI.nFileIndexHigh != FI2.nFileIndexHigh ||
+ FI.nFileIndexLow != FI2.nFileIndexLow ||
+ FI.dwVolumeSerialNumber != FI2.dwVolumeSerialNumber)
+ break;
+ continue;
+ }
+ return EC;
}
- // If ReplaceFileW returned ERROR_UNABLE_TO_REMOVE_REPLACED, retry
- // using ReplaceFileW().
- if (ReplaceError == ERROR_UNABLE_TO_REMOVE_REPLACED)
- continue;
- // We get ERROR_FILE_NOT_FOUND if the destination file is missing.
- // MoveFileEx can handle this case.
- if (ReplaceError != ERROR_ACCESS_DENIED &&
- ReplaceError != ERROR_FILE_NOT_FOUND &&
- ReplaceError != ERROR_SHARING_VIOLATION)
- break;
+ break;
}
- if (::MoveFileExW(wide_from.begin(), wide_to.begin(),
- MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING))
- return std::error_code();
+ // Okay, the old destination file has probably been moved out of the way at
+ // this point, so try to rename the source file again. Still, another
+ // process might have raced with us to create and open the destination
+ // file, so we need to keep doing this until we succeed.
+ }
+
+ // The most likely root cause.
+ return errc::permission_denied;
+}
+
+static std::error_code rename_fd(int FromFD, const Twine &To) {
+ HANDLE FromHandle = reinterpret_cast<HANDLE>(_get_osfhandle(FromFD));
+ return rename_handle(FromHandle, To);
+}
+
+std::error_code rename(const Twine &From, const Twine &To) {
+ // Convert to utf-16.
+ SmallVector<wchar_t, 128> WideFrom;
+ if (std::error_code EC = widenPath(From, WideFrom))
+ return EC;
- DWORD MoveError = ::GetLastError();
- ec = mapWindowsError(MoveError);
- if (MoveError != ERROR_ACCESS_DENIED) break;
+ ScopedFileHandle FromHandle;
+ // Retry this a few times to defeat badly behaved file system scanners.
+ for (unsigned Retry = 0; Retry != 200; ++Retry) {
+ if (Retry != 0)
+ ::Sleep(10);
+ FromHandle =
+ ::CreateFileW(WideFrom.begin(), GENERIC_READ | DELETE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (FromHandle)
+ break;
}
+ if (!FromHandle)
+ return mapWindowsError(GetLastError());
- return ec;
+ return rename_handle(FromHandle, To);
}
std::error_code resize_file(int FD, uint64_t Size) {
@@ -502,6 +686,15 @@ static bool isReservedName(StringRef path) {
return false;
}
+static file_type file_type_from_attrs(DWORD Attrs) {
+ return (Attrs & FILE_ATTRIBUTE_DIRECTORY) ? file_type::directory_file
+ : file_type::regular_file;
+}
+
+static perms perms_from_attrs(DWORD Attrs) {
+ return (Attrs & FILE_ATTRIBUTE_READONLY) ? (all_read | all_exe) : all_all;
+}
+
static std::error_code getStatus(HANDLE FileHandle, file_status &Result) {
if (FileHandle == INVALID_HANDLE_VALUE)
goto handle_status_error;
@@ -530,22 +723,14 @@ static std::error_code getStatus(HANDLE FileHandle, file_status &Result) {
if (!::GetFileInformationByHandle(FileHandle, &Info))
goto handle_status_error;
- {
- file_type Type = (Info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
- ? file_type::directory_file
- : file_type::regular_file;
- perms Permissions = (Info.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
- ? (all_read | all_exe)
- : all_all;
- Result = file_status(
- Type, Permissions, Info.nNumberOfLinks,
- Info.ftLastAccessTime.dwHighDateTime,
- Info.ftLastAccessTime.dwLowDateTime,
- Info.ftLastWriteTime.dwHighDateTime, Info.ftLastWriteTime.dwLowDateTime,
- Info.dwVolumeSerialNumber, Info.nFileSizeHigh, Info.nFileSizeLow,
- Info.nFileIndexHigh, Info.nFileIndexLow);
- return std::error_code();
- }
+ Result = file_status(
+ file_type_from_attrs(Info.dwFileAttributes),
+ perms_from_attrs(Info.dwFileAttributes), Info.nNumberOfLinks,
+ Info.ftLastAccessTime.dwHighDateTime, Info.ftLastAccessTime.dwLowDateTime,
+ Info.ftLastWriteTime.dwHighDateTime, Info.ftLastWriteTime.dwLowDateTime,
+ Info.dwVolumeSerialNumber, Info.nFileSizeHigh, Info.nFileSizeLow,
+ Info.nFileIndexHigh, Info.nFileIndexLow);
+ return std::error_code();
handle_status_error:
DWORD LastError = ::GetLastError();
@@ -637,10 +822,6 @@ std::error_code setLastModificationAndAccessTime(int FD, TimePoint<> Time) {
std::error_code mapped_file_region::init(int FD, uint64_t Offset,
mapmode Mode) {
- // Make sure that the requested size fits within SIZE_T.
- if (Size > std::numeric_limits<SIZE_T>::max())
- return make_error_code(errc::invalid_argument);
-
HANDLE FileHandle = reinterpret_cast<HANDLE>(_get_osfhandle(FD));
if (FileHandle == INVALID_HANDLE_VALUE)
return make_error_code(errc::bad_file_descriptor);
@@ -654,8 +835,8 @@ std::error_code mapped_file_region::init(int FD, uint64_t Offset,
HANDLE FileMappingHandle =
::CreateFileMappingW(FileHandle, 0, flprotect,
- (Offset + Size) >> 32,
- (Offset + Size) & 0xffffffff,
+ Hi_32(Size),
+ Lo_32(Size),
0);
if (FileMappingHandle == NULL) {
std::error_code ec = mapWindowsError(GetLastError());
@@ -697,7 +878,7 @@ std::error_code mapped_file_region::init(int FD, uint64_t Offset,
return std::error_code();
}
-mapped_file_region::mapped_file_region(int fd, mapmode mode, uint64_t length,
+mapped_file_region::mapped_file_region(int fd, mapmode mode, size_t length,
uint64_t offset, std::error_code &ec)
: Size(length), Mapping() {
ec = init(fd, offset, mode);
@@ -710,7 +891,7 @@ mapped_file_region::~mapped_file_region() {
::UnmapViewOfFile(Mapping);
}
-uint64_t mapped_file_region::size() const {
+size_t mapped_file_region::size() const {
assert(Mapping && "Mapping failed but used anyway!");
return Size;
}
@@ -731,6 +912,16 @@ int mapped_file_region::alignment() {
return SysInfo.dwAllocationGranularity;
}
+static basic_file_status status_from_find_data(WIN32_FIND_DATAW *FindData) {
+ return basic_file_status(file_type_from_attrs(FindData->dwFileAttributes),
+ perms_from_attrs(FindData->dwFileAttributes),
+ FindData->ftLastAccessTime.dwHighDateTime,
+ FindData->ftLastAccessTime.dwLowDateTime,
+ FindData->ftLastWriteTime.dwHighDateTime,
+ FindData->ftLastWriteTime.dwLowDateTime,
+ FindData->nFileSizeHigh, FindData->nFileSizeLow);
+}
+
std::error_code detail::directory_iterator_construct(detail::DirIterState &it,
StringRef path,
bool follow_symlinks) {
@@ -751,7 +942,9 @@ std::error_code detail::directory_iterator_construct(detail::DirIterState &it,
// Get the first directory entry.
WIN32_FIND_DATAW FirstFind;
- ScopedFindHandle FindHandle(::FindFirstFileW(c_str(path_utf16), &FirstFind));
+ ScopedFindHandle FindHandle(::FindFirstFileExW(
+ c_str(path_utf16), FindExInfoBasic, &FirstFind, FindExSearchNameMatch,
+ NULL, FIND_FIRST_EX_LARGE_FETCH));
if (!FindHandle)
return mapWindowsError(::GetLastError());
@@ -778,7 +971,8 @@ std::error_code detail::directory_iterator_construct(detail::DirIterState &it,
it.IterationHandle = intptr_t(FindHandle.take());
SmallString<128> directory_entry_path(path);
path::append(directory_entry_path, directory_entry_name_utf8);
- it.CurrentEntry = directory_entry(directory_entry_path, follow_symlinks);
+ it.CurrentEntry = directory_entry(directory_entry_path, follow_symlinks,
+ status_from_find_data(&FirstFind));
return std::error_code();
}
@@ -814,36 +1008,13 @@ std::error_code detail::directory_iterator_increment(detail::DirIterState &it) {
directory_entry_path_utf8))
return ec;
- it.CurrentEntry.replace_filename(Twine(directory_entry_path_utf8));
+ it.CurrentEntry.replace_filename(Twine(directory_entry_path_utf8),
+ status_from_find_data(&FindData));
return std::error_code();
}
-static std::error_code realPathFromHandle(HANDLE H,
- SmallVectorImpl<char> &RealPath) {
- RealPath.clear();
- llvm::SmallVector<wchar_t, MAX_PATH> Buffer;
- DWORD CountChars = ::GetFinalPathNameByHandleW(
- H, Buffer.begin(), Buffer.capacity() - 1, FILE_NAME_NORMALIZED);
- if (CountChars > Buffer.capacity()) {
- // The buffer wasn't big enough, try again. In this case the return value
- // *does* indicate the size of the null terminator.
- Buffer.reserve(CountChars);
- CountChars = ::GetFinalPathNameByHandleW(
- H, Buffer.data(), Buffer.capacity() - 1, FILE_NAME_NORMALIZED);
- }
- if (CountChars == 0)
- return mapWindowsError(GetLastError());
-
- const wchar_t *Data = Buffer.data();
- if (CountChars >= 4) {
- if (0 == ::memcmp(Data, L"\\\\?\\", 8)) {
- CountChars -= 4;
- Data += 4;
- }
- }
-
- // Convert the result from UTF-16 to UTF-8.
- return UTF16ToUTF8(Data, CountChars, RealPath);
+ErrorOr<basic_file_status> directory_entry::status() const {
+ return Status;
}
static std::error_code directoryRealPath(const Twine &Name,
@@ -922,12 +1093,18 @@ std::error_code openFileForWrite(const Twine &Name, int &ResultFD,
CreationDisposition = CREATE_ALWAYS;
DWORD Access = GENERIC_WRITE;
+ DWORD Attributes = FILE_ATTRIBUTE_NORMAL;
if (Flags & F_RW)
Access |= GENERIC_READ;
+ if (Flags & F_Delete) {
+ Access |= DELETE;
+ Attributes |= FILE_FLAG_DELETE_ON_CLOSE;
+ }
- HANDLE H = ::CreateFileW(PathUTF16.begin(), Access,
- FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
- CreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL);
+ HANDLE H =
+ ::CreateFileW(PathUTF16.data(), Access,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL, CreationDisposition, Attributes, NULL);
if (H == INVALID_HANDLE_VALUE) {
DWORD LastError = ::GetLastError();
@@ -959,42 +1136,6 @@ std::error_code openFileForWrite(const Twine &Name, int &ResultFD,
return std::error_code();
}
-std::error_code getPathFromOpenFD(int FD, SmallVectorImpl<char> &ResultPath) {
- HANDLE FileHandle = reinterpret_cast<HANDLE>(::_get_osfhandle(FD));
- if (FileHandle == INVALID_HANDLE_VALUE)
- return make_error_code(errc::bad_file_descriptor);
-
- DWORD CharCount;
- SmallVector<wchar_t, 1024> TempPath;
- do {
- CharCount = ::GetFinalPathNameByHandleW(FileHandle, TempPath.begin(),
- TempPath.capacity(),
- FILE_NAME_NORMALIZED);
- if (CharCount < TempPath.capacity())
- break;
-
- // Reserve sufficient space for the path as well as the null character. Even
- // though the API does not document that it is required, if we reserve just
- // CharCount space, the function call will not store the resulting path and
- // still report success.
- TempPath.reserve(CharCount + 1);
- } while (true);
-
- if (CharCount == 0)
- return mapWindowsError(::GetLastError());
-
- TempPath.set_size(CharCount);
-
- // On earlier Windows releases, the character count includes the terminating
- // null.
- if (TempPath.back() == L'\0') {
- --CharCount;
- TempPath.pop_back();
- }
-
- return windows::UTF16ToUTF8(TempPath.data(), CharCount, ResultPath);
-}
-
std::error_code remove_directories(const Twine &path, bool IgnoreErrors) {
// Convert to utf-16.
SmallVector<wchar_t, 128> Path16;