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 #include <sys/stat.h>
14 #include <sys/types.h>
15 
16 #include "lldb/Host/FileSystem.h"
17 #include "llvm/Support/FileSystem.h"
18 
19 using namespace lldb_private;
20 
21 FileSpec::PathSyntax
22 FileSystem::GetNativePathSyntax()
23 {
24     return FileSpec::ePathSyntaxWindows;
25 }
26 
27 Error
28 FileSystem::MakeDirectory(const FileSpec &file_spec, uint32_t file_permissions)
29 {
30     // On Win32, the mode parameter is ignored, as Windows files and directories support a
31     // different permission model than POSIX.
32     Error error;
33     const auto err_code = llvm::sys::fs::create_directories(file_spec.GetPath(), true);
34     if (err_code)
35     {
36         error.SetErrorString(err_code.message().c_str());
37     }
38 
39     return error;
40 }
41 
42 Error
43 FileSystem::DeleteDirectory(const FileSpec &file_spec, bool recurse)
44 {
45     Error error;
46     if (!recurse)
47     {
48         BOOL result = ::RemoveDirectory(file_spec.GetCString());
49         if (!result)
50             error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
51     }
52     else
53     {
54         // SHFileOperation() accepts a list of paths, and so must be double-null-terminated to
55         // indicate the end of the list.
56         std::string path_buffer{file_spec.GetPath()};
57         path_buffer.push_back(0);
58 
59         SHFILEOPSTRUCT shfos = {0};
60         shfos.wFunc = FO_DELETE;
61         shfos.pFrom = path_buffer.c_str();
62         shfos.fFlags = FOF_NO_UI;
63 
64         int result = ::SHFileOperation(&shfos);
65         // TODO(zturner): Correctly handle the intricacies of SHFileOperation return values.
66         if (result != 0)
67             error.SetErrorStringWithFormat("SHFileOperation failed");
68     }
69     return error;
70 }
71 
72 Error
73 FileSystem::GetFilePermissions(const FileSpec &file_spec, uint32_t &file_permissions)
74 {
75     Error error;
76     // Beware that Windows's permission model is different from Unix's, and it's
77     // not clear if this API is supposed to check ACLs.  To match the caller's
78     // expectations as closely as possible, we'll use Microsoft's _stat, which
79     // attempts to emulate POSIX stat.  This should be good enough for basic
80     // checks like FileSpec::Readable.
81     struct _stat file_stats;
82     if (::_stat(file_spec.GetCString(), &file_stats) == 0)
83     {
84         // The owner permission bits in "st_mode" currently match the definitions
85         // for the owner file mode bits.
86         file_permissions = file_stats.st_mode & (_S_IREAD | _S_IWRITE | _S_IEXEC);
87     }
88     else
89     {
90         error.SetErrorToErrno();
91     }
92 
93     return error;
94 }
95 
96 Error
97 FileSystem::SetFilePermissions(const FileSpec &file_spec, uint32_t file_permissions)
98 {
99     Error error;
100     error.SetErrorStringWithFormat("%s is not supported on this host", __PRETTY_FUNCTION__);
101     return error;
102 }
103 
104 lldb::user_id_t
105 FileSystem::GetFileSize(const FileSpec &file_spec)
106 {
107     return file_spec.GetByteSize();
108 }
109 
110 bool
111 FileSystem::GetFileExists(const FileSpec &file_spec)
112 {
113     return file_spec.Exists();
114 }
115 
116 Error
117 FileSystem::Hardlink(const FileSpec &src, const FileSpec &dst)
118 {
119     Error error;
120     if (!::CreateHardLink(src.GetCString(), dst.GetCString(), nullptr))
121         error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
122     return error;
123 }
124 
125 Error
126 FileSystem::Symlink(const FileSpec &src, const FileSpec &dst)
127 {
128     Error error;
129     DWORD attrib = ::GetFileAttributes(dst.GetCString());
130     if (attrib == INVALID_FILE_ATTRIBUTES)
131     {
132         error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
133         return error;
134     }
135     bool is_directory = !!(attrib & FILE_ATTRIBUTE_DIRECTORY);
136     DWORD flag = is_directory ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0;
137     BOOL result = ::CreateSymbolicLink(src.GetCString(), dst.GetCString(), flag);
138     if (!result)
139         error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
140     return error;
141 }
142 
143 Error
144 FileSystem::Unlink(const FileSpec &file_spec)
145 {
146     Error error;
147     BOOL result = ::DeleteFile(file_spec.GetCString());
148     if (!result)
149         error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
150     return error;
151 }
152 
153 Error
154 FileSystem::Readlink(const FileSpec &src, FileSpec &dst)
155 {
156     Error error;
157     HANDLE h = ::CreateFile(src.GetCString(), GENERIC_READ,
158             FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
159             FILE_FLAG_OPEN_REPARSE_POINT, NULL);
160     if (h == INVALID_HANDLE_VALUE)
161     {
162         error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
163         return error;
164     }
165 
166     char buf[PATH_MAX];
167     // Subtract 1 from the path length since this function does not add a null terminator.
168     DWORD result = ::GetFinalPathNameByHandle(h, buf, sizeof(buf) - 1,
169             FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
170     if (result == 0)
171         error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
172     else
173         dst.SetFile(buf, false);
174 
175     ::CloseHandle(h);
176     return error;
177 }
178 
179 bool
180 FileSystem::IsLocal(const FileSpec &spec)
181 {
182     if (spec)
183     {
184         // TODO: return true if the file is on a locally mounted file system
185         return true;
186     }
187 
188     return false;
189 }
190