Explicitly use CreateFileA in readlink
[folly.git] / folly / portability / Unistd.cpp
1 /*
2  * Copyright 2017 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 // We need to prevent winnt.h from defining the core STATUS codes,
18 // otherwise they will conflict with what we're getting from ntstatus.h
19 #define UMDF_USING_NTSTATUS
20
21 #include <folly/portability/Unistd.h>
22
23 #ifdef _WIN32
24 #include <cstdio>
25 #include <fcntl.h>
26 #include <folly/portability/Sockets.h>
27 #include <folly/portability/Windows.h>
28
29 // Including ntdef.h requires building as a driver, but all we want
30 // is a status code, but we need NTSTATUS defined for that. Luckily
31 // bcrypt.h also defines NTSTATUS, so we'll use that one instead.
32 #include <bcrypt.h>
33 #include <ntstatus.h>
34
35 // Generic wrapper for the p* family of functions.
36 template <class F, class... Args>
37 static int wrapPositional(F f, int fd, off_t offset, Args... args) {
38   off_t origLoc = lseek(fd, 0, SEEK_CUR);
39   if (origLoc == (off_t)-1) {
40     return -1;
41   }
42   if (lseek(fd, offset, SEEK_SET) == (off_t)-1) {
43     return -1;
44   }
45
46   int res = (int)f(fd, args...);
47
48   int curErrNo = errno;
49   if (lseek(fd, origLoc, SEEK_SET) == (off_t)-1) {
50     if (res == -1) {
51       errno = curErrNo;
52     }
53     return -1;
54   }
55   errno = curErrNo;
56
57   return res;
58 }
59
60 namespace folly {
61 namespace portability {
62 namespace unistd {
63 int access(char const* fn, int am) { return _access(fn, am); }
64
65 int chdir(const char* path) { return _chdir(path); }
66
67 int close(int fh) {
68   if (folly::portability::sockets::is_fh_socket(fh)) {
69     SOCKET h = (SOCKET)_get_osfhandle(fh);
70
71     // If we were to just call _close on the descriptor, it would
72     // close the HANDLE, but it wouldn't free any of the resources
73     // associated to the SOCKET, and we can't call _close after
74     // calling closesocket, because closesocket has already closed
75     // the HANDLE, and _close would attempt to close the HANDLE
76     // again, resulting in a double free.
77     // We can however protect the HANDLE from actually being closed
78     // long enough to close the file descriptor, then close the
79     // socket itself.
80     constexpr DWORD protectFlag = HANDLE_FLAG_PROTECT_FROM_CLOSE;
81     DWORD handleFlags = 0;
82     if (!GetHandleInformation((HANDLE)h, &handleFlags)) {
83       return -1;
84     }
85     if (!SetHandleInformation((HANDLE)h, protectFlag, protectFlag)) {
86       return -1;
87     }
88     int c = 0;
89     __try {
90       // We expect this to fail. It still closes the file descriptor though.
91       c = _close(fh);
92       // We just have to catch the SEH exception that gets thrown when we do
93       // this with a debugger attached -_-....
94     } __except (
95         GetExceptionCode() == STATUS_HANDLE_NOT_CLOSABLE
96             ? EXCEPTION_CONTINUE_EXECUTION
97             : EXCEPTION_CONTINUE_SEARCH) {
98       // We told it to continue execution, so there's nothing here would
99       // be run anyways.
100     }
101     // We're at the core, we don't get the luxery of SCOPE_EXIT because
102     // of circular dependencies.
103     if (!SetHandleInformation((HANDLE)h, protectFlag, handleFlags)) {
104       return -1;
105     }
106     if (c != -1) {
107       return -1;
108     }
109     return closesocket(h);
110   }
111   return _close(fh);
112 }
113
114 int dup(int fh) { return _dup(fh); }
115
116 int dup2(int fhs, int fhd) { return _dup2(fhs, fhd); }
117
118 int fsync(int fd) {
119   HANDLE h = (HANDLE)_get_osfhandle(fd);
120   if (h == INVALID_HANDLE_VALUE) {
121     return -1;
122   }
123   if (!FlushFileBuffers(h)) {
124     return -1;
125   }
126   return 0;
127 }
128
129 int ftruncate(int fd, off_t len) {
130   if (_lseek(fd, len, SEEK_SET) == -1) {
131     return -1;
132   }
133
134   HANDLE h = (HANDLE)_get_osfhandle(fd);
135   if (h == INVALID_HANDLE_VALUE) {
136     return -1;
137   }
138   if (!SetEndOfFile(h)) {
139     return -1;
140   }
141   return 0;
142 }
143
144 char* getcwd(char* buf, int sz) { return _getcwd(buf, sz); }
145
146 int getdtablesize() { return _getmaxstdio(); }
147
148 int getgid() { return 1; }
149
150 pid_t getpid() { return (pid_t)uint64_t(GetCurrentProcessId()); }
151
152 // No major need to implement this, and getting a non-potentially
153 // stale ID on windows is a bit involved.
154 pid_t getppid() { return (pid_t)1; }
155
156 int getuid() { return 1; }
157
158 int isatty(int fh) { return _isatty(fh); }
159
160 int lockf(int fd, int cmd, off_t len) { return _locking(fd, cmd, len); }
161
162 off_t lseek(int fh, off_t off, int orig) {
163   return _lseek(fh, off, orig);
164 }
165
166 int rmdir(const char* path) { return _rmdir(path); }
167
168 int pipe(int pth[2]) {
169   // We need to be able to listen to pipes with
170   // libevent, so they need to be actual sockets.
171   return socketpair(PF_UNIX, SOCK_STREAM, 0, pth);
172 }
173
174 ssize_t pread(int fd, void* buf, size_t count, off_t offset) {
175   return wrapPositional(_read, fd, offset, buf, (unsigned int)count);
176 }
177
178 ssize_t pwrite(int fd, const void* buf, size_t count, off_t offset) {
179   return wrapPositional(_write, fd, offset, buf, (unsigned int)count);
180 }
181
182 ssize_t read(int fh, void* buf, size_t count) {
183   if (folly::portability::sockets::is_fh_socket(fh)) {
184     SOCKET s = (SOCKET)_get_osfhandle(fh);
185     if (s != INVALID_SOCKET) {
186       auto r = folly::portability::sockets::recv(fh, buf, count, 0);
187       if (r == -1 && WSAGetLastError() == WSAEWOULDBLOCK) {
188         errno = EAGAIN;
189       }
190       return r;
191     }
192   }
193   auto r = _read(fh, buf, unsigned int(count));
194   if (r == -1 && GetLastError() == ERROR_NO_DATA) {
195     // This only happens if the file was non-blocking and
196     // no data was present. We have to translate the error
197     // to a form that the rest of the world is expecting.
198     errno = EAGAIN;
199   }
200   return r;
201 }
202
203 ssize_t readlink(const char* path, char* buf, size_t buflen) {
204   if (!buflen) {
205     return -1;
206   }
207
208   HANDLE h = CreateFileA(
209       path,
210       GENERIC_READ,
211       FILE_SHARE_READ,
212       nullptr,
213       OPEN_EXISTING,
214       FILE_FLAG_BACKUP_SEMANTICS,
215       nullptr);
216   if (h == INVALID_HANDLE_VALUE) {
217     return -1;
218   }
219
220   DWORD ret =
221       GetFinalPathNameByHandleA(h, buf, DWORD(buflen - 1), VOLUME_NAME_DOS);
222   if (ret >= buflen || ret >= MAX_PATH || !ret) {
223     CloseHandle(h);
224     return -1;
225   }
226
227   CloseHandle(h);
228   buf[ret] = '\0';
229   return ret;
230 }
231
232 void* sbrk(intptr_t /* i */) {
233   return (void*)-1;
234 }
235
236 unsigned int sleep(unsigned int seconds) {
237   Sleep((DWORD)(seconds * 1000));
238   return 0;
239 }
240
241 long sysconf(int tp) {
242   switch (tp) {
243     case _SC_PAGESIZE: {
244       SYSTEM_INFO inf;
245       GetSystemInfo(&inf);
246       return (long)inf.dwPageSize;
247     }
248     case _SC_NPROCESSORS_ONLN: {
249       SYSTEM_INFO inf;
250       GetSystemInfo(&inf);
251       return (long)inf.dwNumberOfProcessors;
252     }
253     default:
254       return -1L;
255   }
256 }
257
258 int truncate(const char* path, off_t len) {
259   int fd = _open(path, O_WRONLY);
260   if (!fd) {
261     return -1;
262   }
263   if (ftruncate(fd, len)) {
264     _close(fd);
265     return -1;
266   }
267   return _close(fd) ? -1 : 0;
268 }
269
270 int usleep(unsigned int ms) {
271   Sleep((DWORD)(ms / 1000));
272   return 0;
273 }
274
275 ssize_t write(int fh, void const* buf, size_t count) {
276   if (folly::portability::sockets::is_fh_socket(fh)) {
277     SOCKET s = (SOCKET)_get_osfhandle(fh);
278     if (s != INVALID_SOCKET) {
279       auto r = folly::portability::sockets::send(fh, buf, (size_t)count, 0);
280       if (r == -1 && WSAGetLastError() == WSAEWOULDBLOCK) {
281         errno = EAGAIN;
282       }
283       return r;
284     }
285   }
286   auto r = _write(fh, buf, unsigned int(count));
287   if ((r > 0 && size_t(r) != count) || (r == -1 && errno == ENOSPC)) {
288     // Writing to a pipe with a full buffer doesn't generate
289     // any error type, unless it caused us to write exactly 0
290     // bytes, so we have to see if we have a pipe first. We
291     // don't touch the errno for anything else.
292     HANDLE h = (HANDLE)_get_osfhandle(fh);
293     if (GetFileType(h) == FILE_TYPE_PIPE) {
294       DWORD state = 0;
295       if (GetNamedPipeHandleState(
296               h, &state, nullptr, nullptr, nullptr, nullptr, 0)) {
297         if ((state & PIPE_NOWAIT) == PIPE_NOWAIT) {
298           errno = EAGAIN;
299           return -1;
300         }
301       }
302     }
303   }
304   return r;
305 }
306 }
307 }
308 }
309 #endif