Split openFileForWrite into windows and unix versions.
[oota-llvm.git] / lib / Support / Windows / Path.inc
index 1ecd80329ccb50867b64ddd753dd152205799972..1be743387119cb4e3806b77d1be6fd9b8ca2ceb5 100644 (file)
@@ -324,29 +324,6 @@ retry_cur_dir:
   return error_code::success();
 }
 
-error_code copy_file(const Twine &from, const Twine &to, copy_option copt) {
-  // Get arguments.
-  SmallString<128> from_storage;
-  SmallString<128> to_storage;
-  StringRef f = from.toStringRef(from_storage);
-  StringRef t = to.toStringRef(to_storage);
-
-  // Convert to utf-16.
-  SmallVector<wchar_t, 128> wide_from;
-  SmallVector<wchar_t, 128> wide_to;
-  if (error_code ec = UTF8ToUTF16(f, wide_from)) return ec;
-  if (error_code ec = UTF8ToUTF16(t, wide_to)) return ec;
-
-  // Copy the file.
-  BOOL res = ::CopyFileW(wide_from.begin(), wide_to.begin(),
-                         copt != copy_option::overwrite_if_exists);
-
-  if (res == 0)
-    return windows_error(::GetLastError());
-
-  return error_code::success();
-}
-
 error_code create_directory(const Twine &path, bool &existed) {
   SmallString<128> path_storage;
   SmallVector<wchar_t, 128> path_utf16;
@@ -598,6 +575,55 @@ static bool isReservedName(StringRef path) {
   return false;
 }
 
+static error_code getStatus(HANDLE FileHandle, file_status &Result) {
+  if (FileHandle == INVALID_HANDLE_VALUE)
+    goto handle_status_error;
+
+  switch (::GetFileType(FileHandle)) {
+  default:
+    llvm_unreachable("Don't know anything about this file type");
+  case FILE_TYPE_UNKNOWN: {
+    DWORD Err = ::GetLastError();
+    if (Err != NO_ERROR)
+      return windows_error(Err);
+    Result = file_status(file_type::type_unknown);
+    return error_code::success();
+  }
+  case FILE_TYPE_DISK:
+    break;
+  case FILE_TYPE_CHAR:
+    Result = file_status(file_type::character_file);
+    return error_code::success();
+  case FILE_TYPE_PIPE:
+    Result = file_status(file_type::fifo_file);
+    return error_code::success();
+  }
+
+  BY_HANDLE_FILE_INFORMATION Info;
+  if (!::GetFileInformationByHandle(FileHandle, &Info))
+    goto handle_status_error;
+
+  Result = file_status(
+        file_type::regular_file, Info.ftLastWriteTime.dwHighDateTime,
+        Info.ftLastWriteTime.dwLowDateTime, Info.dwVolumeSerialNumber,
+        Info.nFileSizeHigh, Info.nFileSizeLow, Info.nFileIndexHigh,
+        Info.nFileIndexLow);
+  return error_code::success();
+
+handle_status_error:
+  error_code EC = windows_error(::GetLastError());
+  if (EC == windows_error::file_not_found ||
+      EC == windows_error::path_not_found)
+    Result = file_status(file_type::file_not_found);
+  else if (EC == windows_error::sharing_violation)
+    Result = file_status(file_type::type_unknown);
+  else {
+    Result = file_status(file_type::status_error);
+    return EC;
+  }
+  return error_code::success();
+}
+
 error_code status(const Twine &path, file_status &result) {
   SmallString<128> path_storage;
   SmallVector<wchar_t, 128> path_utf16;
@@ -613,7 +639,7 @@ error_code status(const Twine &path, file_status &result) {
 
   DWORD attr = ::GetFileAttributesW(path_utf16.begin());
   if (attr == INVALID_FILE_ATTRIBUTES)
-    goto handle_status_error;
+    return getStatus(INVALID_HANDLE_VALUE, result);
 
   // Handle reparse points.
   if (attr & FILE_ATTRIBUTE_REPARSE_POINT) {
@@ -626,7 +652,7 @@ error_code status(const Twine &path, file_status &result) {
                     FILE_FLAG_BACKUP_SEMANTICS,
                     0));
     if (!h)
-      goto handle_status_error;
+      return getStatus(INVALID_HANDLE_VALUE, result);
   }
 
   if (attr & FILE_ATTRIBUTE_DIRECTORY)
@@ -641,32 +667,19 @@ error_code status(const Twine &path, file_status &result) {
                     FILE_FLAG_BACKUP_SEMANTICS,
                     0));
     if (!h)
-      goto handle_status_error;
+      return getStatus(INVALID_HANDLE_VALUE, result);
     BY_HANDLE_FILE_INFORMATION Info;
     if (!::GetFileInformationByHandle(h, &Info))
-      goto handle_status_error;
+      return getStatus(INVALID_HANDLE_VALUE, result);
 
-    result = file_status(
-        file_type::regular_file, Info.ftLastWriteTime.dwHighDateTime,
-        Info.ftLastWriteTime.dwLowDateTime, Info.dwVolumeSerialNumber,
-        Info.nFileSizeHigh, Info.nFileSizeLow, Info.nFileIndexHigh,
-        Info.nFileIndexLow);
+    return getStatus(h, result);
   }
   return error_code::success();
+}
 
-handle_status_error:
-  error_code ec = windows_error(::GetLastError());
-  if (ec == windows_error::file_not_found ||
-      ec == windows_error::path_not_found)
-    result = file_status(file_type::file_not_found);
-  else if (ec == windows_error::sharing_violation)
-    result = file_status(file_type::type_unknown);
-  else {
-    result = file_status(file_type::status_error);
-    return ec;
-  }
-
-  return error_code::success();
+error_code status(int FD, file_status &Result) {
+  HANDLE FileHandle = reinterpret_cast<HANDLE>(_get_osfhandle(FD));
+  return getStatus(FileHandle, Result);
 }
 
 error_code setLastModificationAndAccessTime(int FD, TimeValue Time) {
@@ -740,7 +753,6 @@ error_code mapped_file_region::init(int FD, bool CloseFD, uint64_t Offset) {
   case readonly:  flprotect = PAGE_READONLY; break;
   case readwrite: flprotect = PAGE_READWRITE; break;
   case priv:      flprotect = PAGE_WRITECOPY; break;
-  default: llvm_unreachable("invalid mapping mode");
   }
 
   FileMappingHandle = ::CreateFileMapping(FileHandle,
@@ -764,7 +776,6 @@ error_code mapped_file_region::init(int FD, bool CloseFD, uint64_t Offset) {
   case readonly:  dwDesiredAccess = FILE_MAP_READ; break;
   case readwrite: dwDesiredAccess = FILE_MAP_WRITE; break;
   case priv:      dwDesiredAccess = FILE_MAP_COPY; break;
-  default: llvm_unreachable("invalid mapping mode");
   }
   Mapping = ::MapViewOfFile(FileMappingHandle,
                             dwDesiredAccess,
@@ -1027,7 +1038,92 @@ error_code unmap_file_pages(void *base, size_t size) {
   return windows_error::invalid_function;
 }
 
+error_code openFileForRead(const Twine &Name, int &ResultFD) {
+  SmallString<128> PathStorage;
+  SmallVector<wchar_t, 128> PathUTF16;
+
+  if (error_code EC = UTF8ToUTF16(Name.toStringRef(PathStorage),
+                                  PathUTF16))
+    return EC;
+
+  HANDLE H = ::CreateFileW(PathUTF16.begin(), GENERIC_READ,
+                           FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+                           OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+  if (H == INVALID_HANDLE_VALUE) {
+    error_code EC = windows_error(::GetLastError());
+    // Provide a better error message when trying to open directories.
+    // This only runs if we failed to open the file, so there is probably
+    // no performances issues.
+    if (EC != windows_error::access_denied)
+      return EC;
+    if (is_directory(Name))
+      return error_code(errc::is_a_directory, posix_category());
+    return EC;
+  }
+
+  int FD = ::_open_osfhandle(intptr_t(H), 0);
+  if (FD == -1) {
+    ::CloseHandle(H);
+    return windows_error::invalid_handle;
+  }
+
+  ResultFD = FD;
+  return error_code::success();
+}
+
+error_code openFileForWrite(const Twine &Name, int &ResultFD,
+                            sys::fs::OpenFlags Flags, unsigned Mode) {
+  // Verify that we don't have both "append" and "excl".
+  assert((!(Flags & sys::fs::F_Excl) || !(Flags & sys::fs::F_Append)) &&
+         "Cannot specify both 'excl' and 'append' file creation flags!");
+
+  SmallString<128> PathStorage;
+  SmallVector<wchar_t, 128> PathUTF16;
+
+  if (error_code EC = UTF8ToUTF16(Name.toStringRef(PathStorage),
+                                  PathUTF16))
+    return EC;
+
+  DWORD CreationDisposition;
+  if (Flags & F_Excl)
+    CreationDisposition = CREATE_NEW;
+  else if (Flags & F_Append) 
+    CreationDisposition = OPEN_ALWAYS;
+  else
+    CreationDisposition = CREATE_ALWAYS;
+
+  HANDLE H = ::CreateFileW(PathUTF16.begin(), GENERIC_WRITE,
+                           FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+                           CreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL);
+
+  if (H == INVALID_HANDLE_VALUE) {
+    error_code EC = windows_error(::GetLastError());
+    // Provide a better error message when trying to open directories.
+    // This only runs if we failed to open the file, so there is probably
+    // no performances issues.
+    if (EC != windows_error::access_denied)
+      return EC;
+    if (is_directory(Name))
+      return error_code(errc::is_a_directory, posix_category());
+    return EC;
+  }
+
+  int OpenFlags = 0;
+  if (Flags & F_Append)
+    OpenFlags |= _O_APPEND;
+
+  if (!(Flags & F_Binary))
+    OpenFlags |= _O_TEXT;
 
+  int FD = ::_open_osfhandle(intptr_t(H), OpenFlags);
+  if (FD == -1) {
+    ::CloseHandle(H);
+    return windows_error::invalid_handle;
+  }
+
+  ResultFD = FD;
+  return error_code::success();
+}
 
 } // end namespace fs
 } // end namespace sys