blob: ed6829792c8e46da545d81f8e3b24a6dbad1f78a [file] [log] [blame]
Austin Schuh812d0d12021-11-04 20:16:48 -07001// Copyright (c) FIRST and other WPILib contributors.
2// Open Source Software; you can modify and/or share it under the terms of
3// the WPILib BSD license file in the root directory of this project.
4
5//===----------------------------------------------------------------------===//
6//
7// The LLVM Compiler Infrastructure
8//
9// This file is distributed under the University of Illinois Open Source
10// License. See LICENSE.TXT for details.
11//
12//===----------------------------------------------------------------------===//
13
14#include <cassert>
15
16#ifdef _WIN32
17#include <fcntl.h>
18#include <io.h>
19#include <sys/types.h>
20// Require at least Windows 7 API.
21#define _WIN32_WINNT 0x0601
22#define _WIN32_IE 0x0800 // MinGW at it again. FIXME: verify if still needed.
23#define WIN32_LEAN_AND_MEAN
24#ifndef NOMINMAX
25#define NOMINMAX
26#endif
27
28#define WIN32_NO_STATUS
29#include <windows.h>
30#undef WIN32_NO_STATUS
31#include <winternl.h>
32#include <ntstatus.h>
33
34#include <shellapi.h>
35#include <shlobj.h>
36
37#include "wpi/WindowsError.h"
38
39#else // _WIN32
40
41#include <fcntl.h>
42#include <unistd.h>
43
44#endif // _WIN32
45
Austin Schuh812d0d12021-11-04 20:16:48 -070046#include "wpi/Errno.h"
47#include "wpi/ErrorHandling.h"
48#include "wpi/WindowsError.h"
49#include "wpi/fs.h"
50
51namespace fs {
52
53#ifdef _WIN32
54
55#ifdef _MSC_VER
56#pragma comment(lib, "shell32.lib")
57#pragma comment(lib, "ole32.lib")
58#pragma warning(push)
59#pragma warning(disable : 4244 4267 4146)
60#endif
61
62const file_t kInvalidFile = INVALID_HANDLE_VALUE;
63
64static DWORD nativeDisposition(CreationDisposition Disp, OpenFlags Flags) {
Austin Schuh812d0d12021-11-04 20:16:48 -070065 switch (Disp) {
66 case CD_CreateAlways:
67 return CREATE_ALWAYS;
68 case CD_CreateNew:
69 return CREATE_NEW;
70 case CD_OpenAlways:
71 return OPEN_ALWAYS;
72 case CD_OpenExisting:
73 return OPEN_EXISTING;
74 }
75 wpi_unreachable("unreachable!");
76}
77
78static DWORD nativeAccess(FileAccess Access, OpenFlags Flags) {
79 DWORD Result = 0;
80 if (Access & FA_Read)
81 Result |= GENERIC_READ;
82 if (Access & FA_Write)
83 Result |= GENERIC_WRITE;
84 if (Flags & OF_Delete)
85 Result |= DELETE;
86 if (Flags & OF_UpdateAtime)
87 Result |= FILE_WRITE_ATTRIBUTES;
88 return Result;
89}
90
91static file_t openFileInternal(const path& Path, std::error_code& EC,
92 DWORD Disp, DWORD Access, DWORD Flags,
93 bool Inherit = false) {
94 SECURITY_ATTRIBUTES SA;
95 SA.nLength = sizeof(SA);
96 SA.lpSecurityDescriptor = nullptr;
97 SA.bInheritHandle = Inherit;
98
99 HANDLE H =
100 ::CreateFileW(Path.c_str(), Access,
101 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, &SA,
102 Disp, Flags, NULL);
103 if (H == INVALID_HANDLE_VALUE) {
104 DWORD LastError = ::GetLastError();
105 EC = wpi::mapWindowsError(LastError);
106 // Provide a better error message when trying to open directories.
107 // This only runs if we failed to open the file, so there is probably
108 // no performances issues.
109 if (LastError != ERROR_ACCESS_DENIED) {
110 return kInvalidFile;
111 }
112 if (is_directory(Path)) {
113 EC = std::make_error_code(std::errc::is_a_directory);
114 }
115 return kInvalidFile;
116 }
117 EC = std::error_code();
118 return H;
119}
120
121static std::error_code setDeleteDisposition(HANDLE Handle, bool Delete) {
122 FILE_DISPOSITION_INFO Disposition;
123 Disposition.DeleteFile = Delete;
124 if (!::SetFileInformationByHandle(Handle, FileDispositionInfo, &Disposition,
125 sizeof(Disposition)))
126 return wpi::mapWindowsError(::GetLastError());
127 return std::error_code();
128}
129
130file_t OpenFile(const path& Path, std::error_code& EC, CreationDisposition Disp,
131 FileAccess Access, OpenFlags Flags, unsigned Mode) {
132 // Verify that we don't have both "append" and "excl".
133 assert((!(Disp == CD_CreateNew) || !(Flags & OF_Append)) &&
134 "Cannot specify both 'CreateNew' and 'Append' file creation flags!");
135
136 DWORD NativeDisp = nativeDisposition(Disp, Flags);
137 DWORD NativeAccess = nativeAccess(Access, Flags);
138
139 bool Inherit = false;
140 if (Flags & OF_ChildInherit) {
141 Inherit = true;
142 }
143
144 file_t Result = openFileInternal(Path, EC, NativeDisp, NativeAccess,
145 FILE_ATTRIBUTE_NORMAL, Inherit);
146 if (EC) {
147 return Result;
148 }
149
150 if (Flags & OF_UpdateAtime) {
151 FILETIME FileTime;
152 SYSTEMTIME SystemTime;
153 ::GetSystemTime(&SystemTime);
154 if (::SystemTimeToFileTime(&SystemTime, &FileTime) == 0 ||
155 ::SetFileTime(Result, NULL, &FileTime, NULL) == 0) {
156 DWORD LastError = ::GetLastError();
157 ::CloseHandle(Result);
158 EC = wpi::mapWindowsError(LastError);
159 return kInvalidFile;
160 }
161 }
162
163 if (Flags & OF_Delete) {
164 if ((EC = setDeleteDisposition(Result, true))) {
165 ::CloseHandle(Result);
166 return kInvalidFile;
167 }
168 }
169 return Result;
170}
171
172file_t OpenFileForRead(const path& Path, std::error_code& EC, OpenFlags Flags) {
173 return OpenFile(Path, EC, CD_OpenExisting, FA_Read, Flags);
174}
175
176int FileToFd(file_t& F, std::error_code& EC, OpenFlags Flags) {
177 if (F == kInvalidFile) {
178 EC = wpi::mapWindowsError(ERROR_INVALID_HANDLE);
179 return -1;
180 }
181
182 int CrtOpenFlags = 0;
183 if (Flags & OF_Append) {
184 CrtOpenFlags |= _O_APPEND;
185 }
186
187 if (Flags & OF_Text) {
188 CrtOpenFlags |= _O_TEXT;
189 }
190
191 int ResultFD = ::_open_osfhandle(intptr_t(F), CrtOpenFlags);
192 if (ResultFD == -1) {
193 ::CloseHandle(F);
194 EC = wpi::mapWindowsError(ERROR_INVALID_HANDLE);
195 return -1;
196 }
197
198 EC = std::error_code();
199 F = kInvalidFile;
200 return ResultFD;
201}
202
203void CloseFile(file_t& F) {
204 ::CloseHandle(F);
205 F = kInvalidFile;
206}
207
208#else // _WIN32
209
210const file_t kInvalidFile = -1;
211
212static int nativeOpenFlags(CreationDisposition Disp, OpenFlags Flags,
213 FileAccess Access) {
214 int Result = 0;
215 if (Access == FA_Read) {
216 Result |= O_RDONLY;
217 } else if (Access == FA_Write) {
218 Result |= O_WRONLY;
219 } else if (Access == (FA_Read | FA_Write)) {
220 Result |= O_RDWR;
221 }
222
Austin Schuh812d0d12021-11-04 20:16:48 -0700223 if (Disp == CD_CreateNew) {
224 Result |= O_CREAT; // Create if it doesn't exist.
225 Result |= O_EXCL; // Fail if it does.
226 } else if (Disp == CD_CreateAlways) {
227 Result |= O_CREAT; // Create if it doesn't exist.
228 Result |= O_TRUNC; // Truncate if it does.
229 } else if (Disp == CD_OpenAlways) {
230 Result |= O_CREAT; // Create if it doesn't exist.
231 } else if (Disp == CD_OpenExisting) {
232 // Nothing special, just don't add O_CREAT and we get these semantics.
233 }
234
235 if (Flags & F_Append) {
236 Result |= O_APPEND;
237 }
238
239#ifdef O_CLOEXEC
240 if (!(Flags & OF_ChildInherit)) {
241 Result |= O_CLOEXEC;
242 }
243#endif
244
245 return Result;
246}
247
248file_t OpenFile(const path& Path, std::error_code& EC, CreationDisposition Disp,
249 FileAccess Access, OpenFlags Flags, unsigned Mode) {
250 int OpenFlags = nativeOpenFlags(Disp, Flags, Access);
251 file_t ResultFD = kInvalidFile;
252
253 // Call ::open in a lambda to avoid overload resolution in RetryAfterSignal
254 // when open is overloaded, such as in Bionic.
255 auto Open = [&]() { return ::open(Path.c_str(), OpenFlags, Mode); };
256 if ((ResultFD = wpi::sys::RetryAfterSignal(-1, Open)) < 0) {
257 EC = std::error_code(errno, std::generic_category());
258 return kInvalidFile;
259 }
260#ifndef O_CLOEXEC
261 if (!(Flags & OF_ChildInherit)) {
262 int r = fcntl(ResultFD, F_SETFD, FD_CLOEXEC);
263 (void)r;
264 assert(r == 0 && "fcntl(F_SETFD, FD_CLOEXEC) failed");
265 }
266#endif
267 EC = std::error_code();
268 return ResultFD;
269}
270
271file_t OpenFileForRead(const path& Path, std::error_code& EC, OpenFlags Flags) {
272 return OpenFile(Path, EC, CD_OpenExisting, FA_Read, Flags, 0666);
273}
274
275int FileToFd(file_t& F, std::error_code& EC, OpenFlags Flags) {
276 int fd = F;
277 F = kInvalidFile;
278 EC = std::error_code();
279 return fd;
280}
281
282void CloseFile(file_t& F) {
283 ::close(F);
284 F = kInvalidFile;
285}
286
287#endif // _WIN32
288
289} // namespace fs