1 //===-- FileSystem.cpp ------------------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "lldb/Host/windows/windows.h"
11 
12 #include <shellapi.h>
13 
14 #include "lldb/Host/FileSystem.h"
15 #include "llvm/Support/FileSystem.h"
16 
17 using namespace lldb_private;
18 
19 FileSpec::PathSyntax
20 FileSystem::GetNativePathSyntax()
21 {
22     return FileSpec::ePathSyntaxWindows;
23 }
24 
25 Error
26 FileSystem::MakeDirectory(const FileSpec &file_spec, uint32_t file_permissions)
27 {
28     // On Win32, the mode parameter is ignored, as Windows files and directories support a
29     // different permission model than POSIX.
30     Error error;
31     const auto err_code = llvm::sys::fs::create_directories(file_spec.GetPath(), true);
32     if (err_code)
33     {
34         error.SetErrorString(err_code.message().c_str());
35     }
36 
37     return error;
38 }
39 
40 Error
41 FileSystem::DeleteDirectory(const FileSpec &file_spec, bool recurse)
42 {
43     Error error;
44     if (!recurse)
45     {
46         BOOL result = ::RemoveDirectory(file_spec.GetCString());
47         if (!result)
48             error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
49     }
50     else
51     {
52         // SHFileOperation() accepts a list of paths, and so must be double-null-terminated to
53         // indicate the end of the list.
54         std::string path_buffer{file_spec.GetPath()};
55         path_buffer.push_back(0);
56 
57         SHFILEOPSTRUCT shfos = {0};
58         shfos.wFunc = FO_DELETE;
59         shfos.pFrom = path_buffer.c_str();
60         shfos.fFlags = FOF_NO_UI;
61 
62         int result = ::SHFileOperation(&shfos);
63         // TODO(zturner): Correctly handle the intricacies of SHFileOperation return values.
64         if (result != 0)
65             error.SetErrorStringWithFormat("SHFileOperation failed");
66     }
67     return error;
68 }
69 
70 Error
71 FileSystem::GetFilePermissions(const FileSpec &file_spec, uint32_t &file_permissions)
72 {
73     Error error;
74     error.SetErrorStringWithFormat("%s is not supported on this host", __PRETTY_FUNCTION__);
75     return error;
76 }
77 
78 Error
79 FileSystem::SetFilePermissions(const FileSpec &file_spec, uint32_t file_permissions)
80 {
81     Error error;
82     error.SetErrorStringWithFormat("%s is not supported on this host", __PRETTY_FUNCTION__);
83     return error;
84 }
85 
86 lldb::user_id_t
87 FileSystem::GetFileSize(const FileSpec &file_spec)
88 {
89     return file_spec.GetByteSize();
90 }
91 
92 bool
93 FileSystem::GetFileExists(const FileSpec &file_spec)
94 {
95     return file_spec.Exists();
96 }
97 
98 Error
99 FileSystem::Hardlink(const FileSpec &src, const FileSpec &dst)
100 {
101     Error error;
102     if (!::CreateHardLink(src.GetCString(), dst.GetCString(), nullptr))
103         error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
104     return error;
105 }
106 
107 Error
108 FileSystem::Symlink(const FileSpec &src, const FileSpec &dst)
109 {
110     Error error;
111     DWORD attrib = ::GetFileAttributes(dst.GetCString());
112     if (attrib == INVALID_FILE_ATTRIBUTES)
113     {
114         error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
115         return error;
116     }
117     bool is_directory = !!(attrib & FILE_ATTRIBUTE_DIRECTORY);
118     DWORD flag = is_directory ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0;
119     BOOL result = ::CreateSymbolicLink(src.GetCString(), dst.GetCString(), flag);
120     if (!result)
121         error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
122     return error;
123 }
124 
125 Error
126 FileSystem::Unlink(const FileSpec &file_spec)
127 {
128     Error error;
129     BOOL result = ::DeleteFile(file_spec.GetCString());
130     if (!result)
131         error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
132     return error;
133 }
134 
135 Error
136 FileSystem::Readlink(const FileSpec &src, FileSpec &dst)
137 {
138     Error error;
139     HANDLE h = ::CreateFile(src.GetCString(), GENERIC_READ,
140             FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
141             FILE_FLAG_OPEN_REPARSE_POINT, NULL);
142     if (h == INVALID_HANDLE_VALUE)
143     {
144         error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
145         return error;
146     }
147 
148     char buf[PATH_MAX];
149     // Subtract 1 from the path length since this function does not add a null terminator.
150     DWORD result = ::GetFinalPathNameByHandle(h, buf, sizeof(buf) - 1,
151             FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
152     if (result == 0)
153         error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
154     else
155         dst.SetFile(buf, false);
156 
157     ::CloseHandle(h);
158     return error;
159 }
160 
161 bool
162 FileSystem::IsLocal(const FileSpec &spec)
163 {
164     if (spec)
165     {
166         // TODO: return true if the file is on a locally mounted file system
167         return true;
168     }
169 
170     return false;
171 }
172