1 /* Copyright 2014-present Facebook, Inc.
2 * Licensed under the Apache License, Version 2.0 */
3
4 #include "watchman.h"
5 #include "make_unique.h"
6
7 using watchman::FileDescriptor;
8
9 // Things are more complicated here than on unix.
10 // We maintain an overlapped context for reads and
11 // another for writes. Actual write data is queued
12 // and dispatched to the underlying handle as prior
13 // writes complete.
14
15 struct win_handle;
16
17 namespace {
18 class WindowsEvent : public watchman_event {
19 public:
20 HANDLE hEvent;
21
WindowsEvent(bool initialState=false)22 explicit WindowsEvent(bool initialState = false)
23 : hEvent(CreateEvent(nullptr, TRUE, initialState, nullptr)) {}
24
~WindowsEvent()25 ~WindowsEvent() {
26 CloseHandle(hEvent);
27 }
28
notify()29 void notify() override {
30 SetEvent(hEvent);
31 }
32
testAndClear()33 bool testAndClear() override {
34 bool was_set = WaitForSingleObject(hEvent, 0) == WAIT_OBJECT_0;
35 ResetEvent(hEvent);
36 return was_set;
37 }
38
reset()39 void reset() {
40 ResetEvent(hEvent);
41 }
42 };
43 }
44
45 struct overlapped_op {
46 OVERLAPPED olap;
47 struct win_handle *h;
48 struct write_buf *wbuf;
49 };
50
51 struct write_buf {
52 struct write_buf *next;
53 int len;
54 char *cursor;
55 char data[1];
56 };
57
58 class win_handle : public watchman_stream {
59 public:
60 struct overlapped_op *read_pending{nullptr}, *write_pending{nullptr};
61 FileDescriptor h;
62 WindowsEvent waitable;
63 CRITICAL_SECTION mtx;
64 bool error_pending{false};
65 DWORD errcode{0};
66 DWORD file_type;
67 struct write_buf *write_head{nullptr}, *write_tail{nullptr};
68 char read_buf[8192];
69 char* read_cursor{read_buf};
70 int read_avail{0};
71 bool blocking{true};
72
73 explicit win_handle(FileDescriptor&& handle);
74 ~win_handle();
75 int read(void* buf, int size) override;
76 int write(const void* buf, int size) override;
77 w_evt_t getEvents() override;
78 void setNonBlock(bool nonb) override;
79 bool rewind() override;
80 bool shutdown() override;
81 bool peerIsOwner() override;
getFileDescriptor() const82 const FileDescriptor& getFileDescriptor() const override {
83 return h;
84 }
85
86 // Helper to avoid sprinkling casts all over this file
handle() const87 inline HANDLE handle() const {
88 return (HANDLE)h.handle();
89 }
90
getPeerProcessID() const91 pid_t getPeerProcessID() const override {
92 return 0;
93 }
94 };
95
96 #if 1
97 #define stream_debug(x, ...) 0
98 #else
99 #define stream_debug(x, ...) \
100 do { \
101 time_t now; \
102 char timebuf[64]; \
103 struct tm tm; \
104 time(&now); \
105 localtime_s(&tm, &now); \
106 strftime(timebuf, sizeof(timebuf), "%Y-%m-%dT%H:%M:%S", &tm); \
107 fprintf(stderr, "%s : ", timebuf); \
108 fprintf(stderr, x, __VA_ARGS__); \
109 fflush(stderr); \
110 } while (0)
111 #endif
112
113 typedef BOOL (WINAPI *get_overlapped_result_ex_func)(
114 HANDLE file,
115 LPOVERLAPPED olap,
116 LPDWORD bytes,
117 DWORD millis,
118 BOOL alertable);
119 static BOOL WINAPI probe_get_overlapped_result_ex(
120 HANDLE file,
121 LPOVERLAPPED olap,
122 LPDWORD bytes,
123 DWORD millis,
124 BOOL alertable);
125 static get_overlapped_result_ex_func get_overlapped_result_ex =
126 probe_get_overlapped_result_ex;
127
get_overlapped_result_ex_impl(HANDLE file,LPOVERLAPPED olap,LPDWORD bytes,DWORD millis,BOOL alertable)128 static BOOL WINAPI get_overlapped_result_ex_impl(
129 HANDLE file,
130 LPOVERLAPPED olap,
131 LPDWORD bytes,
132 DWORD millis,
133 BOOL alertable) {
134
135 DWORD waitReturnCode, err;
136
137 stream_debug( "Preparing to wait for maximum %ums\n", millis );
138 if ( millis != 0 ) {
139
140 waitReturnCode = WaitForSingleObjectEx(olap->hEvent, millis, alertable);
141 switch (waitReturnCode)
142 {
143 case WAIT_OBJECT_0:
144 // Event is signaled, overlapped IO operation result should be available.
145 break;
146 case WAIT_IO_COMPLETION:
147 // WaitForSingleObjectEx returnes because the system added an I/O
148 // completion routine or an asynchronous procedure call (APC) to the
149 // thread queue.
150 SetLastError(WAIT_IO_COMPLETION);
151 break;
152 case WAIT_TIMEOUT:
153 // We reached the maximum allowed wait time, the IO operation failed
154 // to complete in timely fashion.
155 SetLastError(WAIT_TIMEOUT);
156 return FALSE;
157
158 case WAIT_FAILED:
159 // something went wrong calling WaitForSingleObjectEx
160 err = GetLastError();
161 stream_debug("WaitForSingleObjectEx failed: %s\n", win32_strerror(err));
162 return FALSE;
163
164 default:
165 // unexpected situation deserving investigation.
166 err = GetLastError();
167 stream_debug("Unexpected error: %s\n", win32_strerror(err));
168 return FALSE;
169 }
170 }
171
172 return GetOverlappedResult(file, olap, bytes, FALSE);
173 }
174
175
probe_get_overlapped_result_ex(HANDLE file,LPOVERLAPPED olap,LPDWORD bytes,DWORD millis,BOOL alertable)176 static BOOL WINAPI probe_get_overlapped_result_ex(
177 HANDLE file,
178 LPOVERLAPPED olap,
179 LPDWORD bytes,
180 DWORD millis,
181 BOOL alertable ) {
182 get_overlapped_result_ex_func func;
183
184 func = (get_overlapped_result_ex_func)GetProcAddress(
185 GetModuleHandle("kernel32.dll"),
186 "GetOverlappedResultEx");
187
188 if ((getenv("WATCHMAN_WIN7_COMPAT") &&
189 getenv("WATCHMAN_WIN7_COMPAT")[0] == '1') || !func) {
190 func = get_overlapped_result_ex_impl;
191 }
192
193 get_overlapped_result_ex = func;
194
195 return func(file, olap, bytes, millis, alertable);
196 }
197
~win_handle()198 win_handle::~win_handle() {
199 EnterCriticalSection(&mtx);
200
201 if (read_pending) {
202 if (CancelIoEx(handle(), &read_pending->olap)) {
203 free(read_pending);
204 read_pending = nullptr;
205 }
206 }
207 if (write_pending) {
208 if (CancelIoEx(handle(), &write_pending->olap)) {
209 free(write_pending);
210 write_pending = nullptr;
211 }
212
213 while (write_head) {
214 struct write_buf* b = write_head;
215 write_head = b->next;
216
217 free(b);
218 }
219 }
220
221 DeleteCriticalSection(&mtx);
222 }
223
move_from_read_buffer(struct win_handle * h,int * total_read_ptr,char ** target_buf_ptr,int * size_ptr)224 static void move_from_read_buffer(struct win_handle *h,
225 int *total_read_ptr,
226 char **target_buf_ptr,
227 int *size_ptr) {
228 int nread = std::min(*size_ptr, h->read_avail);
229 size_t wasted;
230
231 if (!nread) {
232 return;
233 }
234
235 memcpy(*target_buf_ptr, h->read_cursor, nread);
236 *total_read_ptr += nread;
237 *target_buf_ptr += nread;
238 *size_ptr -= nread;
239 h->read_cursor += nread;
240 h->read_avail -= nread;
241
242 stream_debug("moved %d bytes from buffer\n", nread);
243
244 // Pack the buffer to free up space at the rear for reads
245 wasted = h->read_cursor - h->read_buf;
246 if (wasted) {
247 memmove(h->read_buf, h->read_cursor, h->read_avail);
248 h->read_cursor = h->read_buf;
249 }
250 }
251
win_read_handle_completion(struct win_handle * h)252 static bool win_read_handle_completion(struct win_handle *h) {
253 BOOL olap_res;
254 DWORD bytes, err;
255
256 again:
257
258 EnterCriticalSection(&h->mtx);
259 if (!h->read_pending) {
260 LeaveCriticalSection(&h->mtx);
261 return false;
262 }
263
264 stream_debug("have read_pending, checking status\n");
265 h->waitable.reset();
266
267 // Don't hold the mutex while we're blocked
268 LeaveCriticalSection(&h->mtx);
269 olap_res = get_overlapped_result_ex(
270 h->handle(),
271 &h->read_pending->olap,
272 &bytes,
273 h->blocking ? INFINITE : 0,
274 true);
275 err = GetLastError();
276 EnterCriticalSection(&h->mtx);
277
278 if (olap_res) {
279 stream_debug("pending read completed, read %d bytes, %s\n",
280 (int)bytes, win32_strerror(err));
281 h->read_avail += bytes;
282 free(h->read_pending);
283 h->read_pending = nullptr;
284 } else {
285 if (err == WAIT_IO_COMPLETION) {
286 // Some other async thing completed and our wait was interrupted.
287 // This is similar to EINTR
288 LeaveCriticalSection(&h->mtx);
289 goto again;
290 }
291 stream_debug("pending read failed: %s\n", win32_strerror(err));
292 if (err != ERROR_IO_INCOMPLETE) {
293 // Failed
294 free(h->read_pending);
295 h->read_pending = nullptr;
296
297 h->errcode = err;
298 h->error_pending = true;
299 stream_debug("marking read as failed\n");
300 h->waitable.notify();
301 }
302 }
303 LeaveCriticalSection(&h->mtx);
304
305 return h->read_pending != nullptr;
306 }
307
win_read_blocking(struct win_handle * h,void * buf,int size)308 static int win_read_blocking(struct win_handle* h, void* buf, int size) {
309 int total_read = 0;
310 DWORD bytes, err;
311
312 move_from_read_buffer(h, &total_read, (char**)&buf, &size);
313
314 if (size == 0) {
315 return total_read;
316 }
317
318 stream_debug("blocking read of %d bytes\n", (int)size);
319 if (ReadFile(h->handle(), buf, size, &bytes, nullptr)) {
320 total_read += bytes;
321 stream_debug("blocking read provided %d bytes, total=%d\n",
322 (int)bytes, total_read);
323 return total_read;
324 }
325
326 err = GetLastError();
327
328 stream_debug("blocking read failed: %s\n", win32_strerror(err));
329
330 if (total_read) {
331 stream_debug("but already got %d bytes from buffer\n", total_read);
332 return total_read;
333 }
334
335 errno = map_win32_err(err);
336 return -1;
337 }
338
win_read_non_blocking(struct win_handle * h,void * buf,int size)339 static int win_read_non_blocking(struct win_handle* h, void* buf, int size) {
340 int total_read = 0;
341 char *target;
342 DWORD target_space;
343 DWORD bytes;
344
345 stream_debug("non_blocking read for %d bytes\n", size);
346
347 move_from_read_buffer(h, &total_read, (char**)&buf, &size);
348
349 target = h->read_cursor + h->read_avail;
350 target_space = (DWORD)((h->read_buf + sizeof(h->read_buf)) - target);
351
352 stream_debug("initiate read for %d\n", target_space);
353
354 // Create a unique olap for each request
355 h->read_pending = (overlapped_op*)calloc(1, sizeof(*h->read_pending));
356 if (h->read_avail == 0) {
357 stream_debug("ResetEvent because there is no read_avail right now\n");
358 h->waitable.reset();
359 }
360 h->read_pending->olap.hEvent = h->waitable.hEvent;
361 h->read_pending->h = h;
362
363 if (!ReadFile(
364 h->handle(), target, target_space, nullptr, &h->read_pending->olap)) {
365 DWORD err = GetLastError();
366
367 if (err != ERROR_IO_PENDING) {
368 free(h->read_pending);
369 h->read_pending = nullptr;
370
371 stream_debug("olap read failed immediately: %s\n",
372 win32_strerror(err));
373 h->waitable.notify();
374 } else {
375 stream_debug("olap read queued ok\n");
376 }
377
378 errno = map_win32_err(err);
379 return total_read == 0 ? -1 : total_read;
380 }
381
382 // Note: we obtain the bytes via GetOverlappedResult because the docs for
383 // ReadFile warn against passing the pointer to the ReadFile parameter for
384 // asynchronouse reads
385 GetOverlappedResult(h->handle(), &h->read_pending->olap, &bytes, FALSE);
386 stream_debug("olap read succeeded immediately bytes=%d\n", (int)bytes);
387
388 h->read_avail += bytes;
389 free(h->read_pending);
390 h->read_pending = nullptr;
391
392 move_from_read_buffer(h, &total_read, (char**)&buf, &size);
393
394 stream_debug("read returning %d\n", total_read);
395 h->waitable.notify();
396 return total_read;
397 }
398
read(void * buf,int size)399 int win_handle::read(void* buf, int size) {
400 if (win_read_handle_completion(this)) {
401 errno = EAGAIN;
402 return -1;
403 }
404
405 // Report a prior failure
406 if (error_pending) {
407 stream_debug(
408 "win_read: reporting prior failure err=%d errno=%d %s\n",
409 errcode,
410 map_win32_err(errcode),
411 win32_strerror(errcode));
412 errno = map_win32_err(errcode);
413 error_pending = false;
414 return -1;
415 }
416
417 if (blocking) {
418 return win_read_blocking(this, buf, size);
419 }
420
421 return win_read_non_blocking(this, buf, size);
422 }
423
424 static void initiate_write(struct win_handle *h);
425
write_completed(DWORD err,DWORD bytes,LPOVERLAPPED olap)426 static void CALLBACK write_completed(DWORD err, DWORD bytes,
427 LPOVERLAPPED olap) {
428 // Reverse engineer our handle from the olap pointer
429 struct overlapped_op *op = (overlapped_op*)olap;
430 struct win_handle *h = op->h;
431 struct write_buf *wbuf = op->wbuf;
432
433 stream_debug("WriteFileEx: completion callback invoked: bytes=%d %s\n",
434 (int)bytes, win32_strerror(err));
435
436 EnterCriticalSection(&h->mtx);
437 if (h->write_pending == op) {
438 h->write_pending = nullptr;
439 }
440
441 if (err == 0) {
442 wbuf->cursor += bytes;
443 wbuf->len -= bytes;
444
445 if (wbuf->len == 0) {
446 // Consumed this buffer
447 free(wbuf);
448 } else {
449 stream_debug("WriteFileEx: short write: %d written, %d remain\n",
450 bytes, wbuf->len);
451 // the initiate_write call will send the remainder
452 // but we need to re-insert this wbuf in the write queue
453 wbuf->next = h->write_head;
454 h->write_head = wbuf;
455 if (!h->write_tail) {
456 h->write_tail = wbuf;
457 }
458 }
459 } else {
460 stream_debug("WriteFilex: completion: failed: %s\n",
461 win32_strerror(err));
462 h->errcode = err;
463 h->error_pending = true;
464 }
465
466 stream_debug("SetEvent because WriteFileEx completed\n");
467 h->waitable.notify();
468
469 // Send whatever else we have waiting to go
470 initiate_write(h);
471
472 LeaveCriticalSection(&h->mtx);
473
474 // Free the prior struct after possibly initiating another write
475 // to minimize the chance of the same address being reused and
476 // confusing the completion status
477 free(op);
478 }
479
480 // Must be called with the mutex held
initiate_write(struct win_handle * h)481 static void initiate_write(struct win_handle *h) {
482 struct write_buf *wbuf = h->write_head;
483 if (h->write_pending || !wbuf) {
484 return;
485 }
486
487 h->write_head = wbuf->next;
488 if (!h->write_head) {
489 h->write_tail = nullptr;
490 }
491
492 h->write_pending = (overlapped_op*)calloc(1, sizeof(*h->write_pending));
493 h->write_pending->h = h;
494 h->write_pending->wbuf = wbuf;
495
496 stream_debug(
497 "Calling WriteFileEx with wbuf=%p wbuf->cursor=%p len=%d olap=%p\n", wbuf,
498 wbuf->cursor, wbuf->len, &h->write_pending->olap);
499 if (!WriteFileEx(
500 h->handle(),
501 wbuf->cursor,
502 wbuf->len,
503 &h->write_pending->olap,
504 write_completed)) {
505 stream_debug("WriteFileEx: failed %s\n",
506 win32_strerror(GetLastError()));
507 free(h->write_pending);
508 h->write_pending = nullptr;
509 } else {
510 stream_debug("WriteFileEx: queued %d bytes for later\n", wbuf->len);
511 }
512 }
513
write(const void * buf,int size)514 int win_handle::write(const void* buf, int size) {
515 struct write_buf *wbuf;
516
517 EnterCriticalSection(&mtx);
518 if (file_type != FILE_TYPE_PIPE && blocking && !write_head) {
519 DWORD bytes;
520 stream_debug("blocking write of %d\n", size);
521 if (WriteFile(handle(), buf, size, &bytes, nullptr)) {
522 LeaveCriticalSection(&mtx);
523 stream_debug("blocking write wrote %d bytes of %d\n", bytes, size);
524 return bytes;
525 }
526 errcode = GetLastError();
527 error_pending = true;
528 errno = map_win32_err(errcode);
529 stream_debug("SetEvent because blocking write completed (failed)\n");
530 waitable.notify();
531 stream_debug("write failed: %s\n", win32_strerror(errcode));
532 LeaveCriticalSection(&mtx);
533 return -1;
534 }
535
536 wbuf = (write_buf*)malloc(sizeof(*wbuf) + size - 1);
537 if (!wbuf) {
538 return -1;
539 }
540 wbuf->next = nullptr;
541 wbuf->cursor = wbuf->data;
542 wbuf->len = size;
543 memcpy(wbuf->data, buf, size);
544
545 if (write_tail) {
546 write_tail->next = wbuf;
547 } else {
548 write_head = wbuf;
549 }
550 write_tail = wbuf;
551
552 stream_debug("queue write of %d bytes to write_tail\n", size);
553
554 if (!write_pending) {
555 initiate_write(this);
556 }
557
558 LeaveCriticalSection(&mtx);
559
560 return size;
561 }
562
getEvents()563 w_evt_t win_handle::getEvents() {
564 return &waitable;
565 }
566
setNonBlock(bool nonb)567 void win_handle::setNonBlock(bool nonb) {
568 blocking = !nonb;
569 }
570
rewind()571 bool win_handle::rewind() {
572 bool res;
573 LARGE_INTEGER new_pos;
574
575 new_pos.QuadPart = 0;
576 res = SetFilePointerEx(handle(), new_pos, &new_pos, FILE_BEGIN);
577 errno = map_win32_err(GetLastError());
578 return res;
579 }
580
581 // Ensure that any data buffered for write are sent prior to setting
582 // ourselves up to close
shutdown()583 bool win_handle::shutdown() {
584 BOOL olap_res;
585 DWORD bytes;
586
587 blocking = true;
588 while (write_pending) {
589 olap_res = get_overlapped_result_ex(
590 handle(), &write_pending->olap, &bytes, INFINITE, true);
591 }
592
593 return true;
594 }
595
peerIsOwner()596 bool win_handle::peerIsOwner() {
597 // TODO: implement this for Windows
598 return true;
599 }
600
w_event_make(void)601 std::unique_ptr<watchman_event> w_event_make(void) {
602 return watchman::make_unique<WindowsEvent>();
603 }
604
win_handle(FileDescriptor && handle)605 win_handle::win_handle(FileDescriptor&& handle)
606 : h(std::move(handle)),
607 // Initially signalled, meaning that they can try reading
608 waitable(true),
609 file_type(GetFileType((HANDLE)h.handle())) {
610 InitializeCriticalSection(&mtx);
611 }
612
w_stm_fdopen(FileDescriptor && handle)613 std::unique_ptr<watchman_stream> w_stm_fdopen(FileDescriptor&& handle) {
614 if (!handle) {
615 return nullptr;
616 }
617
618 return watchman::make_unique<win_handle>(std::move(handle));
619 }
620
w_stm_connect_named_pipe(const char * path,int timeoutms)621 std::unique_ptr<watchman_stream> w_stm_connect_named_pipe(
622 const char* path,
623 int timeoutms) {
624 DWORD err;
625 DWORD64 deadline = GetTickCount64() + timeoutms;
626
627 if (strlen(path) > 255) {
628 w_log(W_LOG_ERR, "w_stm_connect_named_pipe(%s) path is too long\n", path);
629 errno = E2BIG;
630 return nullptr;
631 }
632
633 while (true) {
634 FileDescriptor handle(intptr_t(CreateFile(
635 path,
636 GENERIC_READ | GENERIC_WRITE,
637 0,
638 nullptr,
639 OPEN_EXISTING,
640 FILE_FLAG_OVERLAPPED,
641 nullptr)));
642
643 if (handle) {
644 return w_stm_fdopen(std::move(handle));
645 }
646
647 err = GetLastError();
648 if (timeoutms > 0) {
649 timeoutms -= (DWORD)(GetTickCount64() - deadline);
650 }
651 if (timeoutms <= 0 ||
652 (err != ERROR_PIPE_BUSY && err != ERROR_FILE_NOT_FOUND)) {
653 // either we're out of time, or retrying won't help with this error
654 errno = map_win32_err(err);
655 return nullptr;
656 }
657
658 // We can retry
659 if (!WaitNamedPipe(path, timeoutms)) {
660 err = GetLastError();
661 if (err == ERROR_SEM_TIMEOUT) {
662 errno = map_win32_err(err);
663 return nullptr;
664 }
665 if (err == ERROR_FILE_NOT_FOUND) {
666 // Grace to allow it to be created
667 SleepEx(10, true);
668 }
669 }
670 }
671 }
672
w_poll_events(struct watchman_event_poll * p,int n,int timeoutms)673 int w_poll_events(struct watchman_event_poll *p, int n, int timeoutms) {
674 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
675 int i;
676 DWORD res;
677
678 if (n > MAXIMUM_WAIT_OBJECTS - 1) {
679 // Programmer error :-/
680 w_log(W_LOG_FATAL, "%d > MAXIMUM_WAIT_OBJECTS-1 (%d)\n", n,
681 MAXIMUM_WAIT_OBJECTS - 1);
682 }
683
684 for (i = 0; i < n; i++) {
685 auto evt = dynamic_cast<WindowsEvent*>(p[i].evt);
686 w_check(evt != nullptr, "!WindowsEvent");
687 handles[i] = evt->hEvent;
688 p[i].ready = false;
689 }
690
691 res = WaitForMultipleObjectsEx(n, handles, false,
692 timeoutms == -1 ? INFINITE : timeoutms, true);
693
694 if (res == WAIT_FAILED) {
695 errno = map_win32_err(GetLastError());
696 return -1;
697 }
698 if (res == WAIT_IO_COMPLETION) {
699 errno = EINTR;
700 return -1;
701 }
702 // Note: WAIT_OBJECT_0 == 0
703 if (/* res >= WAIT_OBJECT_0 && */ res < WAIT_OBJECT_0 + n) {
704 p[res - WAIT_OBJECT_0].ready = true;
705 return 1;
706 }
707 if (res >= WAIT_ABANDONED_0 && res < WAIT_ABANDONED_0 + n) {
708 p[res - WAIT_ABANDONED_0].ready = true;
709 return 1;
710 }
711 return 0;
712 }
713
714 // similar to open(2), but returns a handle
w_handle_open(const char * path,int flags)715 FileDescriptor w_handle_open(const char* path, int flags) {
716 DWORD access = 0, share = 0, create = 0, attrs = 0;
717 DWORD err;
718 SECURITY_ATTRIBUTES sec;
719
720 if (!strcmp(path, "/dev/null")) {
721 path = "NUL:";
722 }
723
724 auto wpath = w_string_piece(path).asWideUNC();
725
726 if (flags & (O_WRONLY|O_RDWR)) {
727 access |= GENERIC_WRITE;
728 }
729 if ((flags & O_WRONLY) == 0) {
730 access |= GENERIC_READ;
731 }
732
733 // We want more posix-y behavior by default
734 share = FILE_SHARE_DELETE|FILE_SHARE_READ|FILE_SHARE_WRITE;
735
736 memset(&sec, 0, sizeof(sec));
737 sec.nLength = sizeof(sec);
738 sec.bInheritHandle = TRUE;
739 if (flags & O_CLOEXEC) {
740 sec.bInheritHandle = FALSE;
741 }
742
743 if ((flags & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL)) {
744 create = CREATE_NEW;
745 } else if ((flags & (O_CREAT|O_TRUNC)) == (O_CREAT|O_TRUNC)) {
746 create = CREATE_ALWAYS;
747 } else if (flags & O_CREAT) {
748 create = OPEN_ALWAYS;
749 } else if (flags & O_TRUNC) {
750 create = TRUNCATE_EXISTING;
751 } else {
752 create = OPEN_EXISTING;
753 }
754
755 attrs = FILE_ATTRIBUTE_NORMAL;
756 if (flags & O_DIRECTORY) {
757 attrs |= FILE_FLAG_BACKUP_SEMANTICS;
758 }
759
760 FileDescriptor h(intptr_t(
761 CreateFileW(wpath.c_str(), access, share, &sec, create, attrs, nullptr)));
762 err = GetLastError();
763
764 errno = map_win32_err(err);
765 return h;
766 }
767
w_stm_open(const char * path,int flags,...)768 std::unique_ptr<watchman_stream> w_stm_open(const char* path, int flags, ...) {
769 return w_stm_fdopen(w_handle_open(path, flags));
770 }
771