Refactor many file functions to use FileSpec over strings.

Summary:
This should solve the issue of sending denormalized paths over gdb-remote
if we stick to GetPath(false) in GDBRemoteCommunicationClient, and let the
server handle any denormalization.

Reviewers: ovyalov, zturner, vharron, clayborg

Reviewed By: clayborg

Subscribers: tberghammer, emaste, lldb-commits

Differential Revision: http://reviews.llvm.org/D9728

llvm-svn: 238604
diff --git a/lldb/source/Host/android/ProcessLauncherAndroid.cpp b/lldb/source/Host/android/ProcessLauncherAndroid.cpp
index 16c1d9b5..24eebc8 100644
--- a/lldb/source/Host/android/ProcessLauncherAndroid.cpp
+++ b/lldb/source/Host/android/ProcessLauncherAndroid.cpp
@@ -7,6 +7,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "lldb/Host/FileSpec.h"
 #include "lldb/Host/Host.h"
 #include "lldb/Host/HostProcess.h"
 #include "lldb/Host/android/ProcessLauncherAndroid.h"
@@ -19,9 +20,9 @@
 using namespace lldb_private;
 
 static bool
-DupDescriptor(const char *path, int fd, int flags)
+DupDescriptor(const FileSpec &file_spec, int fd, int flags)
 {
-    int target_fd = ::open(path, flags, 0666);
+    int target_fd = ::open(file_spec.GetCString(), flags, 0666);
 
     if (target_fd == -1)
         return false;
@@ -63,23 +64,23 @@
     else if (pid == 0)
     {
         if (const lldb_private::FileAction *file_action = launch_info.GetFileActionForFD(STDIN_FILENO)) {
-            const char* path = file_action->GetPath();
-            if (path && ::strlen(path))
-                if (!DupDescriptor(path, STDIN_FILENO, O_RDONLY))
+            FileSpec file_spec = file_action->GetFileSpec();
+            if (file_spec)
+                if (!DupDescriptor(file_spec, STDIN_FILENO, O_RDONLY))
                     exit(-1);
         }
 
         if (const lldb_private::FileAction *file_action = launch_info.GetFileActionForFD(STDOUT_FILENO)) {
-            const char* path = file_action->GetPath();
-            if (path && ::strlen(path))
-                if (!DupDescriptor(path, STDOUT_FILENO, O_WRONLY | O_CREAT | O_TRUNC))
+            FileSpec file_spec = file_action->GetFileSpec();
+            if (file_spec)
+                if (!DupDescriptor(file_spec, STDOUT_FILENO, O_WRONLY | O_CREAT | O_TRUNC))
                     exit(-1);
         }
 
         if (const lldb_private::FileAction *file_action = launch_info.GetFileActionForFD(STDERR_FILENO)) {
-            const char* path = file_action->GetPath();
-            if (path && ::strlen(path))
-                if (!DupDescriptor(path, STDERR_FILENO, O_WRONLY | O_CREAT | O_TRUNC))
+            FileSpec file_spec = file_action->GetFileSpec();
+            if (file_spec)
+                if (!DupDescriptor(file_spec, STDERR_FILENO, O_WRONLY | O_CREAT | O_TRUNC))
                     exit(-1);
         }
 
@@ -90,10 +91,10 @@
         FixupEnvironment(env);
         const char **envp = env.GetConstArgumentVector();
 
-        const char *working_dir = launch_info.GetWorkingDirectory();
-        if (working_dir != nullptr && working_dir[0])
+        FileSpec working_dir = launch_info.GetWorkingDirectory();
+        if (working_dir)
         {
-            if (::chdir(working_dir) != 0)
+            if (::chdir(working_dir.GetCString()) != 0)
                 exit(-1);
         }
 
diff --git a/lldb/source/Host/common/Editline.cpp b/lldb/source/Host/common/Editline.cpp
index 0264c4b..ed67d0c 100644
--- a/lldb/source/Host/common/Editline.cpp
+++ b/lldb/source/Host/common/Editline.cpp
@@ -193,9 +193,9 @@
             {
                 if (m_path.empty() && m_history && !m_prefix.empty())
                 {
-                    std::string parent_path = FileSpec ("~/.lldb", true).GetPath();
+                    FileSpec parent_path{"~/.lldb", true};
                     char history_path[PATH_MAX];
-                    if (FileSystem::MakeDirectory(parent_path.c_str(), lldb::eFilePermissionsDirectoryDefault).Success())
+                    if (FileSystem::MakeDirectory(parent_path, lldb::eFilePermissionsDirectoryDefault).Success())
                     {
                         snprintf (history_path, sizeof (history_path), "~/.lldb/%s-history", m_prefix.c_str());
                     }
diff --git a/lldb/source/Host/common/File.cpp b/lldb/source/Host/common/File.cpp
index 4361eae..a3420bf 100644
--- a/lldb/source/Host/common/File.cpp
+++ b/lldb/source/Host/common/File.cpp
@@ -325,12 +325,12 @@
 }
 
 uint32_t
-File::GetPermissions (const char *path, Error &error)
+File::GetPermissions(const FileSpec &file_spec, Error &error)
 {
-    if (path && path[0])
+    if (file_spec)
     {
         struct stat file_stats;
-        if (::stat (path, &file_stats) == -1)
+        if (::stat(file_spec.GetCString(), &file_stats) == -1)
             error.SetErrorToErrno();
         else
         {
@@ -339,12 +339,7 @@
         }
     }
     else
-    {
-        if (path)
-            error.SetErrorString ("invalid path");
-        else
-            error.SetErrorString ("empty path");        
-    }
+        error.SetErrorString ("empty file spec");
     return 0;
 }
 
diff --git a/lldb/source/Host/common/FileSpec.cpp b/lldb/source/Host/common/FileSpec.cpp
index c0efa71..65d6543 100644
--- a/lldb/source/Host/common/FileSpec.cpp
+++ b/lldb/source/Host/common/FileSpec.cpp
@@ -243,7 +243,17 @@
 }
 
 FileSpec::FileSpec(const char *pathname, bool resolve_path, ArchSpec arch) :
-    FileSpec(pathname, resolve_path, arch.GetTriple().isOSWindows() ? ePathSyntaxWindows : ePathSyntaxPosix)
+    FileSpec{pathname, resolve_path, arch.GetTriple().isOSWindows() ? ePathSyntaxWindows : ePathSyntaxPosix}
+{
+}
+
+FileSpec::FileSpec(const std::string &path, bool resolve_path, PathSyntax syntax) :
+    FileSpec{path.c_str(), resolve_path, syntax}
+{
+}
+
+FileSpec::FileSpec(const std::string &path, bool resolve_path, ArchSpec arch) :
+    FileSpec{path.c_str(), resolve_path, arch}
 {
 }
 
@@ -334,6 +344,12 @@
         m_directory.SetCString(normalized.c_str());
 }
 
+void
+FileSpec::SetFile(const std::string &pathname, bool resolve, PathSyntax syntax)
+{
+    return SetFile(pathname.c_str(), resolve, syntax);
+}
+
 //----------------------------------------------------------------------
 // Convert to pointer operator. This allows code to check any FileSpec
 // objects to see if they contain anything valid using code such as:
@@ -755,7 +771,7 @@
 {
     uint32_t file_permissions = 0;
     if (*this)
-        FileSystem::GetFilePermissions(GetPath().c_str(), file_permissions);
+        FileSystem::GetFilePermissions(*this, file_permissions);
     return file_permissions;
 }
 
@@ -829,6 +845,12 @@
     return std::string(result.begin(), result.end());
 }
 
+const char *
+FileSpec::GetCString(bool denormalize) const
+{
+    return ConstString{GetPath(denormalize)}.AsCString(NULL);
+}
+
 void
 FileSpec::GetPath(llvm::SmallVectorImpl<char> &path, bool denormalize) const
 {
@@ -1336,6 +1358,12 @@
 }
 
 void
+FileSpec::AppendPathComponent(const std::string &new_path)
+{
+    return AppendPathComponent(new_path.c_str());
+}
+
+void
 FileSpec::RemoveLastPathComponent ()
 {
     const bool resolve = false;
diff --git a/lldb/source/Host/common/Host.cpp b/lldb/source/Host/common/Host.cpp
index e2471d2..20d6355 100644
--- a/lldb/source/Host/common/Host.cpp
+++ b/lldb/source/Host/common/Host.cpp
@@ -533,25 +533,25 @@
 }
 
 Error
-Host::RunShellCommand (const char *command,
-                       const char *working_dir,
-                       int *status_ptr,
-                       int *signo_ptr,
-                       std::string *command_output_ptr,
-                       uint32_t timeout_sec,
-                       bool run_in_default_shell)
+Host::RunShellCommand(const char *command,
+                      const FileSpec &working_dir,
+                      int *status_ptr,
+                      int *signo_ptr,
+                      std::string *command_output_ptr,
+                      uint32_t timeout_sec,
+                      bool run_in_default_shell)
 {
     return RunShellCommand(Args(command), working_dir, status_ptr, signo_ptr, command_output_ptr, timeout_sec, run_in_default_shell);
 }
 
 Error
-Host::RunShellCommand (const Args &args,
-                       const char *working_dir,
-                       int *status_ptr,
-                       int *signo_ptr,
-                       std::string *command_output_ptr,
-                       uint32_t timeout_sec,
-                       bool run_in_default_shell)
+Host::RunShellCommand(const Args &args,
+                      const FileSpec &working_dir,
+                      int *status_ptr,
+                      int *signo_ptr,
+                      std::string *command_output_ptr,
+                      uint32_t timeout_sec,
+                      bool run_in_default_shell)
 {
     Error error;
     ProcessLaunchInfo launch_info;
@@ -597,11 +597,13 @@
             llvm::sys::fs::createTemporaryFile("lldb-shell-output.%%%%%%", "", output_file_path);
         }
     }
-    
+
+    FileSpec output_file_spec{output_file_path.c_str(), false};
+
     launch_info.AppendSuppressFileAction (STDIN_FILENO, true, false);
-    if (!output_file_path.empty())
+    if (output_file_spec)
     {
-        launch_info.AppendOpenFileAction(STDOUT_FILENO, output_file_path.c_str(), false, true);
+        launch_info.AppendOpenFileAction(STDOUT_FILENO, output_file_spec, false, true);
         launch_info.AppendDuplicateFileAction(STDOUT_FILENO, STDERR_FILENO);
     }
     else
@@ -660,8 +662,7 @@
             if (command_output_ptr)
             {
                 command_output_ptr->clear();
-                FileSpec file_spec(output_file_path.c_str(), File::eOpenOptionRead);
-                uint64_t file_size = file_spec.GetByteSize();
+                uint64_t file_size = output_file_spec.GetByteSize();
                 if (file_size > 0)
                 {
                     if (file_size > command_output_ptr->max_size())
@@ -670,18 +671,19 @@
                     }
                     else
                     {
-                        command_output_ptr->resize(file_size);
-                        file_spec.ReadFileContents(0, &((*command_output_ptr)[0]), command_output_ptr->size(), &error);
+                        std::vector<char> command_output(file_size);
+                        output_file_spec.ReadFileContents(0, command_output.data(), file_size, &error);
+                        if (error.Success())
+                            command_output_ptr->assign(command_output.data(), file_size);
                     }
                 }
             }
         }
         shell_info->can_delete.SetValue(true, eBroadcastAlways);
     }
-    
-    FileSpec output_file_spec(output_file_path.c_str(), false);
+
     if (FileSystem::GetFileExists(output_file_spec))
-        FileSystem::Unlink(output_file_path.c_str());
+        FileSystem::Unlink(output_file_spec);
     // Handshake with the monitor thread, or just let it know in advance that
     // it can delete "shell_info" in case we timed out and were not able to kill
     // the process...
@@ -832,16 +834,18 @@
     current_dir[0] = '\0';
 #endif
 
-    const char *working_dir = launch_info.GetWorkingDirectory();
+    FileSpec working_dir{launch_info.GetWorkingDirectory()};
     if (working_dir)
     {
 #if defined (__APPLE__)
         // Set the working directory on this thread only
-        if (__pthread_chdir (working_dir) < 0) {
+        if (__pthread_chdir(working_dir.GetCString()) < 0) {
             if (errno == ENOENT) {
-                error.SetErrorStringWithFormat("No such file or directory: %s", working_dir);
+                error.SetErrorStringWithFormat("No such file or directory: %s",
+                        working_dir.GetCString());
             } else if (errno == ENOTDIR) {
-                error.SetErrorStringWithFormat("Path doesn't name a directory: %s", working_dir);
+                error.SetErrorStringWithFormat("Path doesn't name a directory: %s",
+                        working_dir.GetCString());
             } else {
                 error.SetErrorStringWithFormat("An unknown error occurred when changing directory for process execution.");
             }
@@ -855,10 +859,11 @@
             return error;
         }
 
-        if (::chdir(working_dir) == -1)
+        if (::chdir(working_dir.GetCString()) == -1)
         {
             error.SetError(errno, eErrorTypePOSIX);
-            error.LogIfError(log, "unable to change working directory to %s", working_dir);
+            error.LogIfError(log, "unable to change working directory to %s",
+                    working_dir.GetCString());
             return error;
         }
 #endif
diff --git a/lldb/source/Host/common/HostInfoBase.cpp b/lldb/source/Host/common/HostInfoBase.cpp
index f44ad96..cd4815a 100644
--- a/lldb/source/Host/common/HostInfoBase.cpp
+++ b/lldb/source/Host/common/HostInfoBase.cpp
@@ -40,7 +40,7 @@
 
         // Remove the LLDB temporary directory if we have one. Set "recurse" to
         // true to all files that were created for the LLDB process can be cleaned up.
-        FileSystem::DeleteDirectory(tmpdir_file_spec.GetDirectory().GetCString(), true);
+        FileSystem::DeleteDirectory(tmpdir_file_spec, true);
     }
 
     //----------------------------------------------------------------------
@@ -326,18 +326,15 @@
     if (!HostInfo::ComputeGlobalTempFileDirectory(temp_file_spec))
         return false;
 
-    std::string pid_str;
-    llvm::raw_string_ostream pid_stream(pid_str);
-    pid_stream << Host::GetCurrentProcessID();
-    temp_file_spec.AppendPathComponent(pid_stream.str().c_str());
-    std::string final_path = temp_file_spec.GetPath();
-    if (!FileSystem::MakeDirectory(final_path.c_str(), eFilePermissionsDirectoryDefault).Success())
+    std::string pid_str{std::to_string(Host::GetCurrentProcessID())};
+    temp_file_spec.AppendPathComponent(pid_str);
+    if (!FileSystem::MakeDirectory(temp_file_spec, eFilePermissionsDirectoryDefault).Success())
         return false;
 
     // Make an atexit handler to clean up the process specify LLDB temp dir
     // and all of its contents.
     ::atexit(CleanupProcessSpecificLLDBTempDir);
-    file_spec.GetDirectory().SetCStringWithLength(final_path.c_str(), final_path.size());
+    file_spec = temp_file_spec;
     return true;
 }
 
@@ -370,7 +367,7 @@
         return false;
 
     temp_file_spec.AppendPathComponent("lldb");
-    if (!FileSystem::MakeDirectory(temp_file_spec.GetPath().c_str(), eFilePermissionsDirectoryDefault).Success())
+    if (!FileSystem::MakeDirectory(temp_file_spec, eFilePermissionsDirectoryDefault).Success())
         return false;
 
     file_spec = temp_file_spec;
diff --git a/lldb/source/Host/common/Socket.cpp b/lldb/source/Host/common/Socket.cpp
index 2059fec..f7e93c6 100644
--- a/lldb/source/Host/common/Socket.cpp
+++ b/lldb/source/Host/common/Socket.cpp
@@ -504,7 +504,7 @@
     saddr_un.sun_len = SUN_LEN (&saddr_un);
 #endif
 
-    FileSystem::Unlink(name.data());
+    FileSystem::Unlink(FileSpec{name, true});
     bool success = false;
     if (::bind (listen_fd, (struct sockaddr *)&saddr_un, SUN_LEN (&saddr_un)) == 0) 
     {
diff --git a/lldb/source/Host/macosx/Host.mm b/lldb/source/Host/macosx/Host.mm
index 79efb9f..6912d59 100644
--- a/lldb/source/Host/macosx/Host.mm
+++ b/lldb/source/Host/macosx/Host.mm
@@ -411,9 +411,9 @@
     if (arch_spec.IsValid())
         command.Printf(" --arch=%s", arch_spec.GetArchitectureName());
 
-    const char *working_dir = launch_info.GetWorkingDirectory();
+    FileSpec working_dir{launch_info.GetWorkingDirectory()};
     if (working_dir)
-        command.Printf(" --working-dir '%s'", working_dir);
+        command.Printf(" --working-dir '%s'", working_dir.GetCString());
     else
     {
         char cwd[PATH_MAX];
@@ -527,7 +527,7 @@
         WaitForProcessToSIGSTOP(pid, 5);
     }
 
-    FileSystem::Unlink(unix_socket_name);
+    FileSystem::Unlink(FileSpec{unix_socket_name, false});
     [applescript release];
     if (pid != LLDB_INVALID_PROCESS_ID)
         launch_info.SetProcessID (pid);
diff --git a/lldb/source/Host/posix/FileSystem.cpp b/lldb/source/Host/posix/FileSystem.cpp
index 15ded77..626c37d 100644
--- a/lldb/source/Host/posix/FileSystem.cpp
+++ b/lldb/source/Host/posix/FileSystem.cpp
@@ -35,70 +35,61 @@
 }
 
 Error
-FileSystem::MakeDirectory(const char *path, uint32_t file_permissions)
+FileSystem::MakeDirectory(const FileSpec &file_spec, uint32_t file_permissions)
 {
-    Error error;
-    if (path && path[0])
+    if (file_spec)
     {
-        if (::mkdir(path, file_permissions) != 0)
+        Error error;
+        if (::mkdir(file_spec.GetCString(), file_permissions) == -1)
         {
             error.SetErrorToErrno();
+            errno = 0;
             switch (error.GetError())
             {
                 case ENOENT:
                 {
                     // Parent directory doesn't exist, so lets make it if we can
-                    FileSpec spec(path, false);
-                    if (spec.GetDirectory() && spec.GetFilename())
+                    // Make the parent directory and try again
+                    FileSpec parent_file_spec{file_spec.GetDirectory().GetCString(), false};
+                    error = MakeDirectory(parent_file_spec, file_permissions);
+                    if (error.Fail())
+                        return error;
+                    // Try and make the directory again now that the parent directory was made successfully
+                    if (::mkdir(file_spec.GetCString(), file_permissions) == -1)
                     {
-                        // Make the parent directory and try again
-                        Error error2 = MakeDirectory(spec.GetDirectory().GetCString(), file_permissions);
-                        if (error2.Success())
-                        {
-                            // Try and make the directory again now that the parent directory was made successfully
-                            if (::mkdir(path, file_permissions) == 0)
-                                error.Clear();
-                            else
-                                error.SetErrorToErrno();
-                        }
+                        error.SetErrorToErrno();
+                        return error;
                     }
                 }
-                break;
-
                 case EEXIST:
                 {
-                    FileSpec path_spec(path, false);
-                    if (path_spec.IsDirectory())
-                        error.Clear(); // It is a directory and it already exists
+                    if (file_spec.IsDirectory())
+                        return Error{}; // It is a directory and it already exists
                 }
-                break;
             }
         }
+        return error;
     }
-    else
-    {
-        error.SetErrorString("empty path");
-    }
-    return error;
+    return Error{"empty path"};
 }
 
 Error
-FileSystem::DeleteDirectory(const char *path, bool recurse)
+FileSystem::DeleteDirectory(const FileSpec &file_spec, bool recurse)
 {
     Error error;
-    if (path && path[0])
+    if (file_spec)
     {
         if (recurse)
         {
             StreamString command;
-            command.Printf("rm -rf \"%s\"", path);
+            command.Printf("rm -rf \"%s\"", file_spec.GetCString());
             int status = ::system(command.GetString().c_str());
             if (status != 0)
                 error.SetError(status, eErrorTypeGeneric);
         }
         else
         {
-            if (::rmdir(path) != 0)
+            if (::rmdir(file_spec.GetCString()) != 0)
                 error.SetErrorToErrno();
         }
     }
@@ -110,11 +101,11 @@
 }
 
 Error
-FileSystem::GetFilePermissions(const char *path, uint32_t &file_permissions)
+FileSystem::GetFilePermissions(const FileSpec &file_spec, uint32_t &file_permissions)
 {
     Error error;
     struct stat file_stats;
-    if (::stat(path, &file_stats) == 0)
+    if (::stat(file_spec.GetCString(), &file_stats) == 0)
     {
         // The bits in "st_mode" currently match the definitions
         // for the file mode bits in unix.
@@ -128,10 +119,10 @@
 }
 
 Error
-FileSystem::SetFilePermissions(const char *path, uint32_t file_permissions)
+FileSystem::SetFilePermissions(const FileSpec &file_spec, uint32_t file_permissions)
 {
     Error error;
-    if (::chmod(path, file_permissions) != 0)
+    if (::chmod(file_spec.GetCString(), file_permissions) != 0)
         error.SetErrorToErrno();
     return error;
 }
@@ -149,43 +140,45 @@
 }
 
 Error
-FileSystem::Hardlink(const char *src, const char *dst)
+FileSystem::Hardlink(const FileSpec &src, const FileSpec &dst)
 {
     Error error;
-    if (::link(dst, src) == -1)
+    if (::link(dst.GetCString(), src.GetCString()) == -1)
         error.SetErrorToErrno();
     return error;
 }
 
 Error
-FileSystem::Symlink(const char *src, const char *dst)
+FileSystem::Symlink(const FileSpec &src, const FileSpec &dst)
 {
     Error error;
-    if (::symlink(dst, src) == -1)
+    if (::symlink(dst.GetCString(), src.GetCString()) == -1)
         error.SetErrorToErrno();
     return error;
 }
 
 Error
-FileSystem::Unlink(const char *path)
+FileSystem::Unlink(const FileSpec &file_spec)
 {
     Error error;
-    if (::unlink(path) == -1)
+    if (::unlink(file_spec.GetCString()) == -1)
         error.SetErrorToErrno();
     return error;
 }
 
 Error
-FileSystem::Readlink(const char *path, char *buf, size_t buf_len)
+FileSystem::Readlink(const FileSpec &src, FileSpec &dst)
 {
     Error error;
-    ssize_t count = ::readlink(path, buf, buf_len);
+    char buf[PATH_MAX];
+    ssize_t count = ::readlink(src.GetCString(), buf, sizeof(buf) - 1);
     if (count < 0)
         error.SetErrorToErrno();
-    else if (static_cast<size_t>(count) < (buf_len - 1))
-        buf[count] = '\0'; // Success
     else
-        error.SetErrorString("'buf' buffer is too small to contain link contents");
+    {
+        buf[count] = '\0'; // Success
+        dst.SetFile(buf, false);
+    }
     return error;
 }
 
diff --git a/lldb/source/Host/posix/HostProcessPosix.cpp b/lldb/source/Host/posix/HostProcessPosix.cpp
index 8e19add..5761a79 100644
--- a/lldb/source/Host/posix/HostProcessPosix.cpp
+++ b/lldb/source/Host/posix/HostProcessPosix.cpp
@@ -69,28 +69,25 @@
 
     // Use special code here because proc/[pid]/exe is a symbolic link.
     char link_path[PATH_MAX];
-    char exe_path[PATH_MAX] = "";
-    if (snprintf (link_path, PATH_MAX, "/proc/%" PRIu64 "/exe", m_process) <= 0)
+    if (snprintf(link_path, PATH_MAX, "/proc/%" PRIu64 "/exe", m_process) != 1)
     {
         error.SetErrorString("Unable to build /proc/<pid>/exe string");
         return error;
     }
 
-    error = FileSystem::Readlink(link_path, exe_path, llvm::array_lengthof(exe_path));
+    error = FileSystem::Readlink(FileSpec{link_path, false}, file_spec);
     if (!error.Success())
         return error;
 
-    const ssize_t len = strlen(exe_path);
     // If the binary has been deleted, the link name has " (deleted)" appended.
     // Remove if there.
-    static const ssize_t deleted_len = strlen(" (deleted)");
-    if (len > deleted_len &&
-        !strcmp(exe_path + len - deleted_len, " (deleted)"))
+    if (file_spec.GetFilename().GetStringRef().endswith(" (deleted)"))
     {
-        exe_path[len - deleted_len] = 0;
+        const char *filename = file_spec.GetFilename().GetCString();
+        static const size_t deleted_len = strlen(" (deleted)");
+        const size_t len = file_spec.GetFilename().GetLength();
+        file_spec.GetFilename().SetCStringWithLength(filename, len - deleted_len);
     }
-
-    file_spec.SetFile(exe_path, false);
     return error;
 }
 
diff --git a/lldb/source/Host/posix/PipePosix.cpp b/lldb/source/Host/posix/PipePosix.cpp
index 7f73f9b..0ed319f 100644
--- a/lldb/source/Host/posix/PipePosix.cpp
+++ b/lldb/source/Host/posix/PipePosix.cpp
@@ -335,7 +335,7 @@
 Error
 PipePosix::Delete(llvm::StringRef name)
 {
-    return FileSystem::Unlink(name.data());
+    return FileSystem::Unlink(FileSpec{name.data(), true});
 }
 
 bool
diff --git a/lldb/source/Host/windows/FileSystem.cpp b/lldb/source/Host/windows/FileSystem.cpp
index 679412c..7e452bc 100644
--- a/lldb/source/Host/windows/FileSystem.cpp
+++ b/lldb/source/Host/windows/FileSystem.cpp
@@ -23,12 +23,12 @@
 }
 
 Error
-FileSystem::MakeDirectory(const char *path, uint32_t file_permissions)
+FileSystem::MakeDirectory(const FileSpec &file_spec, uint32_t file_permissions)
 {
     // On Win32, the mode parameter is ignored, as Windows files and directories support a
     // different permission model than POSIX.
     Error error;
-    const auto err_code = llvm::sys::fs::create_directories(path, true);
+    const auto err_code = llvm::sys::fs::create_directories(file_spec.GetPath(), true);
     if (err_code)
     {
         error.SetErrorString(err_code.message().c_str());
@@ -38,12 +38,12 @@
 }
 
 Error
-FileSystem::DeleteDirectory(const char *path, bool recurse)
+FileSystem::DeleteDirectory(const FileSpec &file_spec, bool recurse)
 {
     Error error;
     if (!recurse)
     {
-        BOOL result = ::RemoveDirectory(path);
+        BOOL result = ::RemoveDirectory(file_spec.GetCString());
         if (!result)
             error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
     }
@@ -51,7 +51,7 @@
     {
         // SHFileOperation() accepts a list of paths, and so must be double-null-terminated to
         // indicate the end of the list.
-        std::string path_buffer(path);
+        std::string path_buffer{file_spec.GetPath()};
         path_buffer.push_back(0);
 
         SHFILEOPSTRUCT shfos = {0};
@@ -68,7 +68,7 @@
 }
 
 Error
-FileSystem::GetFilePermissions(const char *path, uint32_t &file_permissions)
+FileSystem::GetFilePermissions(const FileSpec &file_spec, uint32_t &file_permissions)
 {
     Error error;
     error.SetErrorStringWithFormat("%s is not supported on this host", __PRETTY_FUNCTION__);
@@ -76,7 +76,7 @@
 }
 
 Error
-FileSystem::SetFilePermissions(const char *path, uint32_t file_permissions)
+FileSystem::SetFilePermissions(const FileSpec &file_spec, uint32_t file_permissions)
 {
     Error error;
     error.SetErrorStringWithFormat("%s is not supported on this host", __PRETTY_FUNCTION__);
@@ -96,19 +96,19 @@
 }
 
 Error
-FileSystem::Hardlink(const char *linkname, const char *target)
+FileSystem::Hardlink(const FileSpec &src, const FileSpec &dst)
 {
     Error error;
-    if (!::CreateHardLink(linkname, target, nullptr))
+    if (!::CreateHardLink(src.GetCString(), dst.GetCString(), nullptr))
         error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
     return error;
 }
 
 Error
-FileSystem::Symlink(const char *linkname, const char *target)
+FileSystem::Symlink(const FileSpec &src, const FileSpec &dst)
 {
     Error error;
-    DWORD attrib = ::GetFileAttributes(target);
+    DWORD attrib = ::GetFileAttributes(dst.GetCString());
     if (attrib == INVALID_FILE_ATTRIBUTES)
     {
         error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
@@ -116,38 +116,43 @@
     }
     bool is_directory = !!(attrib & FILE_ATTRIBUTE_DIRECTORY);
     DWORD flag = is_directory ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0;
-    BOOL result = ::CreateSymbolicLink(linkname, target, flag);
+    BOOL result = ::CreateSymbolicLink(src.GetCString(), dst.GetCString(), flag);
     if (!result)
         error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
     return error;
 }
 
 Error
-FileSystem::Unlink(const char *path)
+FileSystem::Unlink(const FileSpec &file_spec)
 {
     Error error;
-    BOOL result = ::DeleteFile(path);
+    BOOL result = ::DeleteFile(file_spec.GetCString());
     if (!result)
         error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
     return error;
 }
 
 Error
-FileSystem::Readlink(const char *path, char *buf, size_t buf_len)
+FileSystem::Readlink(const FileSpec &src, FileSpec &dst)
 {
     Error error;
-    HANDLE h = ::CreateFile(path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
-                            FILE_FLAG_OPEN_REPARSE_POINT, NULL);
+    HANDLE h = ::CreateFile(src.GetCString(), GENERIC_READ,
+            FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
+            FILE_FLAG_OPEN_REPARSE_POINT, NULL);
     if (h == INVALID_HANDLE_VALUE)
     {
         error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
         return error;
     }
 
+    char buf[PATH_MAX];
     // Subtract 1 from the path length since this function does not add a null terminator.
-    DWORD result = ::GetFinalPathNameByHandle(h, buf, buf_len - 1, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
+    DWORD result = ::GetFinalPathNameByHandle(h, buf, sizeof(buf) - 1,
+            FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
     if (result == 0)
         error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
+    else
+        dst.SetFile(buf, false);
 
     ::CloseHandle(h);
     return error;
diff --git a/lldb/source/Host/windows/ProcessLauncherWindows.cpp b/lldb/source/Host/windows/ProcessLauncherWindows.cpp
index 12e03e1..d498eea 100644
--- a/lldb/source/Host/windows/ProcessLauncherWindows.cpp
+++ b/lldb/source/Host/windows/ProcessLauncherWindows.cpp
@@ -52,7 +52,7 @@
     executable = launch_info.GetExecutableFile().GetPath();
     launch_info.GetArguments().GetQuotedCommandString(commandLine);
     BOOL result = ::CreateProcessA(executable.c_str(), const_cast<char *>(commandLine.c_str()), NULL, NULL, TRUE, flags, NULL,
-                                   launch_info.GetWorkingDirectory(), &startupinfo, &pi);
+                                   launch_info.GetWorkingDirectory().GetCString(), &startupinfo, &pi);
     if (result)
     {
         // Do not call CloseHandle on pi.hProcess, since we want to pass that back through the HostProcess.