xref: /third_party/libuv/src/win/fs.c (revision e66f31c5)
1/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
2 *
3 * Permission is hereby granted, free of charge, to any person obtaining a copy
4 * of this software and associated documentation files (the "Software"), to
5 * deal in the Software without restriction, including without limitation the
6 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7 * sell copies of the Software, and to permit persons to whom the Software is
8 * furnished to do so, subject to the following conditions:
9 *
10 * The above copyright notice and this permission notice shall be included in
11 * all copies or substantial portions of the Software.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19 * IN THE SOFTWARE.
20 */
21
22#include <assert.h>
23#include <stdlib.h>
24#include <direct.h>
25#include <errno.h>
26#include <fcntl.h>
27#include <io.h>
28#include <limits.h>
29#include <sys/stat.h>
30#include <sys/utime.h>
31#include <stdio.h>
32
33#include "uv.h"
34
35/* <winioctl.h> requires <windows.h>, included via "uv.h" above, but needs to
36   be included before our "winapi.h", included via "internal.h" below. */
37#include <winioctl.h>
38
39#include "internal.h"
40#include "req-inl.h"
41#include "handle-inl.h"
42#include "fs-fd-hash-inl.h"
43
44
45#define UV_FS_FREE_PATHS         0x0002
46#define UV_FS_FREE_PTR           0x0008
47#define UV_FS_CLEANEDUP          0x0010
48
49
50#define INIT(subtype)                                                         \
51  do {                                                                        \
52    if (req == NULL)                                                          \
53      return UV_EINVAL;                                                       \
54    uv__fs_req_init(loop, req, subtype, cb);                                  \
55  }                                                                           \
56  while (0)
57
58#define POST                                                                  \
59  do {                                                                        \
60    if (cb != NULL) {                                                         \
61      uv__req_register(loop, req);                                            \
62      uv__work_submit(loop,                                                   \
63                      &req->work_req,                                         \
64                      UV__WORK_FAST_IO,                                       \
65                      uv__fs_work,                                            \
66                      uv__fs_done);                                           \
67      return 0;                                                               \
68    } else {                                                                  \
69      uv__fs_work(&req->work_req);                                            \
70      return req->result;                                                     \
71    }                                                                         \
72  }                                                                           \
73  while (0)
74
75#define SET_REQ_RESULT(req, result_value)                                   \
76  do {                                                                      \
77    req->result = (result_value);                                           \
78    assert(req->result != -1);                                              \
79  } while (0)
80
81#define SET_REQ_WIN32_ERROR(req, sys_errno)                                 \
82  do {                                                                      \
83    req->sys_errno_ = (sys_errno);                                          \
84    req->result = uv_translate_sys_error(req->sys_errno_);                  \
85  } while (0)
86
87#define SET_REQ_UV_ERROR(req, uv_errno, sys_errno)                          \
88  do {                                                                      \
89    req->result = (uv_errno);                                               \
90    req->sys_errno_ = (sys_errno);                                          \
91  } while (0)
92
93#define VERIFY_FD(fd, req)                                                  \
94  if (fd == -1) {                                                           \
95    req->result = UV_EBADF;                                                 \
96    req->sys_errno_ = ERROR_INVALID_HANDLE;                                 \
97    return;                                                                 \
98  }
99
100#define MILLION ((int64_t) 1000 * 1000)
101#define BILLION ((int64_t) 1000 * 1000 * 1000)
102
103static void uv__filetime_to_timespec(uv_timespec_t *ts, int64_t filetime) {
104  filetime -= 116444736 * BILLION;
105  ts->tv_sec = (long) (filetime / (10 * MILLION));
106  ts->tv_nsec = (long) ((filetime - ts->tv_sec * 10 * MILLION) * 100U);
107  if (ts->tv_nsec < 0) {
108    ts->tv_sec -= 1;
109    ts->tv_nsec += 1e9;
110  }
111}
112
113#define TIME_T_TO_FILETIME(time, filetime_ptr)                              \
114  do {                                                                      \
115    int64_t bigtime = ((time) * 10 * MILLION + 116444736 * BILLION);        \
116    (filetime_ptr)->dwLowDateTime = (uint64_t) bigtime & 0xFFFFFFFF;        \
117    (filetime_ptr)->dwHighDateTime = (uint64_t) bigtime >> 32;              \
118  } while(0)
119
120#define IS_SLASH(c) ((c) == L'\\' || (c) == L'/')
121#define IS_LETTER(c) (((c) >= L'a' && (c) <= L'z') || \
122  ((c) >= L'A' && (c) <= L'Z'))
123
124#define MIN(a,b) (((a) < (b)) ? (a) : (b))
125
126const WCHAR JUNCTION_PREFIX[] = L"\\??\\";
127const WCHAR JUNCTION_PREFIX_LEN = 4;
128
129const WCHAR LONG_PATH_PREFIX[] = L"\\\\?\\";
130const WCHAR LONG_PATH_PREFIX_LEN = 4;
131
132const WCHAR UNC_PATH_PREFIX[] = L"\\\\?\\UNC\\";
133const WCHAR UNC_PATH_PREFIX_LEN = 8;
134
135static int uv__file_symlink_usermode_flag = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
136
137static DWORD uv__allocation_granularity;
138
139
140void uv__fs_init(void) {
141  SYSTEM_INFO system_info;
142
143  GetSystemInfo(&system_info);
144  uv__allocation_granularity = system_info.dwAllocationGranularity;
145
146  uv__fd_hash_init();
147}
148
149
150INLINE static int fs__readlink_handle(HANDLE handle,
151                                      char** target_ptr,
152                                      size_t* target_len_ptr) {
153  char buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
154  REPARSE_DATA_BUFFER* reparse_data = (REPARSE_DATA_BUFFER*) buffer;
155  WCHAR* w_target;
156  DWORD w_target_len;
157  DWORD bytes;
158  size_t i;
159  size_t len;
160
161  if (!DeviceIoControl(handle,
162                       FSCTL_GET_REPARSE_POINT,
163                       NULL,
164                       0,
165                       buffer,
166                       sizeof buffer,
167                       &bytes,
168                       NULL)) {
169    return -1;
170  }
171
172  if (reparse_data->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
173    /* Real symlink */
174    w_target = reparse_data->SymbolicLinkReparseBuffer.PathBuffer +
175        (reparse_data->SymbolicLinkReparseBuffer.SubstituteNameOffset /
176        sizeof(WCHAR));
177    w_target_len =
178        reparse_data->SymbolicLinkReparseBuffer.SubstituteNameLength /
179        sizeof(WCHAR);
180
181    /* Real symlinks can contain pretty much everything, but the only thing we
182     * really care about is undoing the implicit conversion to an NT namespaced
183     * path that CreateSymbolicLink will perform on absolute paths. If the path
184     * is win32-namespaced then the user must have explicitly made it so, and
185     * we better just return the unmodified reparse data. */
186    if (w_target_len >= 4 &&
187        w_target[0] == L'\\' &&
188        w_target[1] == L'?' &&
189        w_target[2] == L'?' &&
190        w_target[3] == L'\\') {
191      /* Starts with \??\ */
192      if (w_target_len >= 6 &&
193          ((w_target[4] >= L'A' && w_target[4] <= L'Z') ||
194           (w_target[4] >= L'a' && w_target[4] <= L'z')) &&
195          w_target[5] == L':' &&
196          (w_target_len == 6 || w_target[6] == L'\\')) {
197        /* \??\<drive>:\ */
198        w_target += 4;
199        w_target_len -= 4;
200
201      } else if (w_target_len >= 8 &&
202                 (w_target[4] == L'U' || w_target[4] == L'u') &&
203                 (w_target[5] == L'N' || w_target[5] == L'n') &&
204                 (w_target[6] == L'C' || w_target[6] == L'c') &&
205                 w_target[7] == L'\\') {
206        /* \??\UNC\<server>\<share>\ - make sure the final path looks like
207         * \\<server>\<share>\ */
208        w_target += 6;
209        w_target[0] = L'\\';
210        w_target_len -= 6;
211      }
212    }
213
214  } else if (reparse_data->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) {
215    /* Junction. */
216    w_target = reparse_data->MountPointReparseBuffer.PathBuffer +
217        (reparse_data->MountPointReparseBuffer.SubstituteNameOffset /
218        sizeof(WCHAR));
219    w_target_len = reparse_data->MountPointReparseBuffer.SubstituteNameLength /
220        sizeof(WCHAR);
221
222    /* Only treat junctions that look like \??\<drive>:\ as symlink. Junctions
223     * can also be used as mount points, like \??\Volume{<guid>}, but that's
224     * confusing for programs since they wouldn't be able to actually
225     * understand such a path when returned by uv_readlink(). UNC paths are
226     * never valid for junctions so we don't care about them. */
227    if (!(w_target_len >= 6 &&
228          w_target[0] == L'\\' &&
229          w_target[1] == L'?' &&
230          w_target[2] == L'?' &&
231          w_target[3] == L'\\' &&
232          ((w_target[4] >= L'A' && w_target[4] <= L'Z') ||
233           (w_target[4] >= L'a' && w_target[4] <= L'z')) &&
234          w_target[5] == L':' &&
235          (w_target_len == 6 || w_target[6] == L'\\'))) {
236      SetLastError(ERROR_SYMLINK_NOT_SUPPORTED);
237      return -1;
238    }
239
240    /* Remove leading \??\ */
241    w_target += 4;
242    w_target_len -= 4;
243
244  } else if (reparse_data->ReparseTag == IO_REPARSE_TAG_APPEXECLINK) {
245    /* String #3 in the list has the target filename. */
246    if (reparse_data->AppExecLinkReparseBuffer.StringCount < 3) {
247      SetLastError(ERROR_SYMLINK_NOT_SUPPORTED);
248      return -1;
249    }
250    w_target = reparse_data->AppExecLinkReparseBuffer.StringList;
251    /* The StringList buffer contains a list of strings separated by "\0",   */
252    /* with "\0\0" terminating the list. Move to the 3rd string in the list: */
253    for (i = 0; i < 2; ++i) {
254      len = wcslen(w_target);
255      if (len == 0) {
256        SetLastError(ERROR_SYMLINK_NOT_SUPPORTED);
257        return -1;
258      }
259      w_target += len + 1;
260    }
261    w_target_len = wcslen(w_target);
262    if (w_target_len == 0) {
263      SetLastError(ERROR_SYMLINK_NOT_SUPPORTED);
264      return -1;
265    }
266    /* Make sure it is an absolute path. */
267    if (!(w_target_len >= 3 &&
268         ((w_target[0] >= L'a' && w_target[0] <= L'z') ||
269          (w_target[0] >= L'A' && w_target[0] <= L'Z')) &&
270         w_target[1] == L':' &&
271         w_target[2] == L'\\')) {
272      SetLastError(ERROR_SYMLINK_NOT_SUPPORTED);
273      return -1;
274    }
275
276  } else {
277    /* Reparse tag does not indicate a symlink. */
278    SetLastError(ERROR_SYMLINK_NOT_SUPPORTED);
279    return -1;
280  }
281
282  assert(target_ptr == NULL || *target_ptr == NULL);
283  return uv_utf16_to_wtf8(w_target, w_target_len, target_ptr, target_len_ptr);
284}
285
286
287INLINE static int fs__capture_path(uv_fs_t* req, const char* path,
288    const char* new_path, const int copy_path) {
289  WCHAR* buf;
290  WCHAR* pos;
291  size_t buf_sz = 0;
292  size_t path_len = 0;
293  ssize_t pathw_len = 0;
294  ssize_t new_pathw_len = 0;
295
296  /* new_path can only be set if path is also set. */
297  assert(new_path == NULL || path != NULL);
298
299  if (path != NULL) {
300    pathw_len = uv_wtf8_length_as_utf16(path);
301    if (pathw_len < 0)
302      return ERROR_INVALID_NAME;
303    buf_sz += pathw_len * sizeof(WCHAR);
304  }
305
306  if (path != NULL && copy_path) {
307    path_len = 1 + strlen(path);
308    buf_sz += path_len;
309  }
310
311  if (new_path != NULL) {
312    new_pathw_len = uv_wtf8_length_as_utf16(new_path);
313    if (new_pathw_len < 0)
314      return ERROR_INVALID_NAME;
315    buf_sz += new_pathw_len * sizeof(WCHAR);
316  }
317
318
319  if (buf_sz == 0) {
320    req->file.pathw = NULL;
321    req->fs.info.new_pathw = NULL;
322    req->path = NULL;
323    return 0;
324  }
325
326  buf = uv__malloc(buf_sz);
327  if (buf == NULL) {
328    return ERROR_OUTOFMEMORY;
329  }
330
331  pos = buf;
332
333  if (path != NULL) {
334    uv_wtf8_to_utf16(path, pos, pathw_len);
335    req->file.pathw = pos;
336    pos += pathw_len;
337  } else {
338    req->file.pathw = NULL;
339  }
340
341  if (new_path != NULL) {
342    uv_wtf8_to_utf16(new_path, pos, new_pathw_len);
343    req->fs.info.new_pathw = pos;
344    pos += new_pathw_len;
345  } else {
346    req->fs.info.new_pathw = NULL;
347  }
348
349  req->path = path;
350  if (path != NULL && copy_path) {
351    memcpy(pos, path, path_len);
352    assert(path_len == buf_sz - (pos - buf) * sizeof(WCHAR));
353    req->path = (char*) pos;
354  }
355
356  req->flags |= UV_FS_FREE_PATHS;
357
358  return 0;
359}
360
361
362INLINE static void uv__fs_req_init(uv_loop_t* loop, uv_fs_t* req,
363    uv_fs_type fs_type, const uv_fs_cb cb) {
364  uv__once_init();
365  UV_REQ_INIT(req, UV_FS);
366  req->loop = loop;
367  req->flags = 0;
368  req->fs_type = fs_type;
369  req->sys_errno_ = 0;
370  req->result = 0;
371  req->ptr = NULL;
372  req->path = NULL;
373  req->cb = cb;
374  memset(&req->fs, 0, sizeof(req->fs));
375}
376
377
378void fs__open(uv_fs_t* req) {
379  DWORD access;
380  DWORD share;
381  DWORD disposition;
382  DWORD attributes = 0;
383  HANDLE file;
384  int fd, current_umask;
385  int flags = req->fs.info.file_flags;
386  struct uv__fd_info_s fd_info;
387
388  /* Adjust flags to be compatible with the memory file mapping. Save the
389   * original flags to emulate the correct behavior. */
390  if (flags & UV_FS_O_FILEMAP) {
391    fd_info.flags = flags;
392    fd_info.current_pos.QuadPart = 0;
393
394    if ((flags & (UV_FS_O_RDONLY | UV_FS_O_WRONLY | UV_FS_O_RDWR)) ==
395        UV_FS_O_WRONLY) {
396      /* CreateFileMapping always needs read access */
397      flags = (flags & ~UV_FS_O_WRONLY) | UV_FS_O_RDWR;
398    }
399
400    if (flags & UV_FS_O_APPEND) {
401      /* Clear the append flag and ensure RDRW mode */
402      flags &= ~UV_FS_O_APPEND;
403      flags &= ~(UV_FS_O_RDONLY | UV_FS_O_WRONLY | UV_FS_O_RDWR);
404      flags |= UV_FS_O_RDWR;
405    }
406  }
407
408  /* Obtain the active umask. umask() never fails and returns the previous
409   * umask. */
410  current_umask = _umask(0);
411  _umask(current_umask);
412
413  /* convert flags and mode to CreateFile parameters */
414  switch (flags & (UV_FS_O_RDONLY | UV_FS_O_WRONLY | UV_FS_O_RDWR)) {
415  case UV_FS_O_RDONLY:
416    access = FILE_GENERIC_READ;
417    break;
418  case UV_FS_O_WRONLY:
419    access = FILE_GENERIC_WRITE;
420    break;
421  case UV_FS_O_RDWR:
422    access = FILE_GENERIC_READ | FILE_GENERIC_WRITE;
423    break;
424  default:
425    goto einval;
426  }
427
428  if (flags & UV_FS_O_APPEND) {
429    access &= ~FILE_WRITE_DATA;
430    access |= FILE_APPEND_DATA;
431  }
432
433  /*
434   * Here is where we deviate significantly from what CRT's _open()
435   * does. We indiscriminately use all the sharing modes, to match
436   * UNIX semantics. In particular, this ensures that the file can
437   * be deleted even whilst it's open, fixing issue
438   * https://github.com/nodejs/node-v0.x-archive/issues/1449.
439   * We still support exclusive sharing mode, since it is necessary
440   * for opening raw block devices, otherwise Windows will prevent
441   * any attempt to write past the master boot record.
442   */
443  if (flags & UV_FS_O_EXLOCK) {
444    share = 0;
445  } else {
446    share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
447  }
448
449  switch (flags & (UV_FS_O_CREAT | UV_FS_O_EXCL | UV_FS_O_TRUNC)) {
450  case 0:
451  case UV_FS_O_EXCL:
452    disposition = OPEN_EXISTING;
453    break;
454  case UV_FS_O_CREAT:
455    disposition = OPEN_ALWAYS;
456    break;
457  case UV_FS_O_CREAT | UV_FS_O_EXCL:
458  case UV_FS_O_CREAT | UV_FS_O_TRUNC | UV_FS_O_EXCL:
459    disposition = CREATE_NEW;
460    break;
461  case UV_FS_O_TRUNC:
462  case UV_FS_O_TRUNC | UV_FS_O_EXCL:
463    disposition = TRUNCATE_EXISTING;
464    break;
465  case UV_FS_O_CREAT | UV_FS_O_TRUNC:
466    disposition = CREATE_ALWAYS;
467    break;
468  default:
469    goto einval;
470  }
471
472  attributes |= FILE_ATTRIBUTE_NORMAL;
473  if (flags & UV_FS_O_CREAT) {
474    if (!((req->fs.info.mode & ~current_umask) & _S_IWRITE)) {
475      attributes |= FILE_ATTRIBUTE_READONLY;
476    }
477  }
478
479  if (flags & UV_FS_O_TEMPORARY ) {
480    attributes |= FILE_FLAG_DELETE_ON_CLOSE | FILE_ATTRIBUTE_TEMPORARY;
481    access |= DELETE;
482  }
483
484  if (flags & UV_FS_O_SHORT_LIVED) {
485    attributes |= FILE_ATTRIBUTE_TEMPORARY;
486  }
487
488  switch (flags & (UV_FS_O_SEQUENTIAL | UV_FS_O_RANDOM)) {
489  case 0:
490    break;
491  case UV_FS_O_SEQUENTIAL:
492    attributes |= FILE_FLAG_SEQUENTIAL_SCAN;
493    break;
494  case UV_FS_O_RANDOM:
495    attributes |= FILE_FLAG_RANDOM_ACCESS;
496    break;
497  default:
498    goto einval;
499  }
500
501  if (flags & UV_FS_O_DIRECT) {
502    /*
503     * FILE_APPEND_DATA and FILE_FLAG_NO_BUFFERING are mutually exclusive.
504     * Windows returns 87, ERROR_INVALID_PARAMETER if these are combined.
505     *
506     * FILE_APPEND_DATA is included in FILE_GENERIC_WRITE:
507     *
508     * FILE_GENERIC_WRITE = STANDARD_RIGHTS_WRITE |
509     *                      FILE_WRITE_DATA |
510     *                      FILE_WRITE_ATTRIBUTES |
511     *                      FILE_WRITE_EA |
512     *                      FILE_APPEND_DATA |
513     *                      SYNCHRONIZE
514     *
515     * Note: Appends are also permitted by FILE_WRITE_DATA.
516     *
517     * In order for direct writes and direct appends to succeed, we therefore
518     * exclude FILE_APPEND_DATA if FILE_WRITE_DATA is specified, and otherwise
519     * fail if the user's sole permission is a direct append, since this
520     * particular combination is invalid.
521     */
522    if (access & FILE_APPEND_DATA) {
523      if (access & FILE_WRITE_DATA) {
524        access &= ~FILE_APPEND_DATA;
525      } else {
526        goto einval;
527      }
528    }
529    attributes |= FILE_FLAG_NO_BUFFERING;
530  }
531
532  switch (flags & (UV_FS_O_DSYNC | UV_FS_O_SYNC)) {
533  case 0:
534    break;
535  case UV_FS_O_DSYNC:
536  case UV_FS_O_SYNC:
537    attributes |= FILE_FLAG_WRITE_THROUGH;
538    break;
539  default:
540    goto einval;
541  }
542
543  /* Setting this flag makes it possible to open a directory. */
544  attributes |= FILE_FLAG_BACKUP_SEMANTICS;
545
546  file = CreateFileW(req->file.pathw,
547                     access,
548                     share,
549                     NULL,
550                     disposition,
551                     attributes,
552                     NULL);
553  if (file == INVALID_HANDLE_VALUE) {
554    DWORD error = GetLastError();
555    if (error == ERROR_FILE_EXISTS && (flags & UV_FS_O_CREAT) &&
556        !(flags & UV_FS_O_EXCL)) {
557      /* Special case: when ERROR_FILE_EXISTS happens and UV_FS_O_CREAT was
558       * specified, it means the path referred to a directory. */
559      SET_REQ_UV_ERROR(req, UV_EISDIR, error);
560    } else {
561      SET_REQ_WIN32_ERROR(req, GetLastError());
562    }
563    return;
564  }
565
566  fd = _open_osfhandle((intptr_t) file, flags);
567  if (fd < 0) {
568    /* The only known failure mode for _open_osfhandle() is EMFILE, in which
569     * case GetLastError() will return zero. However we'll try to handle other
570     * errors as well, should they ever occur.
571     */
572    if (errno == EMFILE)
573      SET_REQ_UV_ERROR(req, UV_EMFILE, ERROR_TOO_MANY_OPEN_FILES);
574    else if (GetLastError() != ERROR_SUCCESS)
575      SET_REQ_WIN32_ERROR(req, GetLastError());
576    else
577      SET_REQ_WIN32_ERROR(req, (DWORD) UV_UNKNOWN);
578    CloseHandle(file);
579    return;
580  }
581
582  if (flags & UV_FS_O_FILEMAP) {
583    FILE_STANDARD_INFO file_info;
584    if (!GetFileInformationByHandleEx(file,
585                                      FileStandardInfo,
586                                      &file_info,
587                                      sizeof file_info)) {
588      SET_REQ_WIN32_ERROR(req, GetLastError());
589      CloseHandle(file);
590      return;
591    }
592    fd_info.is_directory = file_info.Directory;
593
594    if (fd_info.is_directory) {
595      fd_info.size.QuadPart = 0;
596      fd_info.mapping = INVALID_HANDLE_VALUE;
597    } else {
598      if (!GetFileSizeEx(file, &fd_info.size)) {
599        SET_REQ_WIN32_ERROR(req, GetLastError());
600        CloseHandle(file);
601        return;
602      }
603
604      if (fd_info.size.QuadPart == 0) {
605        fd_info.mapping = INVALID_HANDLE_VALUE;
606      } else {
607        DWORD flProtect = (fd_info.flags & (UV_FS_O_RDONLY | UV_FS_O_WRONLY |
608          UV_FS_O_RDWR)) == UV_FS_O_RDONLY ? PAGE_READONLY : PAGE_READWRITE;
609        fd_info.mapping = CreateFileMapping(file,
610                                            NULL,
611                                            flProtect,
612                                            fd_info.size.HighPart,
613                                            fd_info.size.LowPart,
614                                            NULL);
615        if (fd_info.mapping == NULL) {
616          SET_REQ_WIN32_ERROR(req, GetLastError());
617          CloseHandle(file);
618          return;
619        }
620      }
621    }
622
623    uv__fd_hash_add(fd, &fd_info);
624  }
625
626  SET_REQ_RESULT(req, fd);
627  return;
628
629 einval:
630  SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER);
631}
632
633void fs__close(uv_fs_t* req) {
634  int fd = req->file.fd;
635  int result;
636  struct uv__fd_info_s fd_info;
637
638  VERIFY_FD(fd, req);
639
640  if (uv__fd_hash_remove(fd, &fd_info)) {
641    if (fd_info.mapping != INVALID_HANDLE_VALUE) {
642      CloseHandle(fd_info.mapping);
643    }
644  }
645
646  if (fd > 2)
647    result = _close(fd);
648  else
649    result = 0;
650
651  /* _close doesn't set _doserrno on failure, but it does always set errno
652   * to EBADF on failure.
653   */
654  if (result == -1) {
655    assert(errno == EBADF);
656    SET_REQ_UV_ERROR(req, UV_EBADF, ERROR_INVALID_HANDLE);
657  } else {
658    SET_REQ_RESULT(req, 0);
659  }
660}
661
662
663LONG fs__filemap_ex_filter(LONG excode, PEXCEPTION_POINTERS pep,
664                           int* perror) {
665  if (excode != (LONG)EXCEPTION_IN_PAGE_ERROR) {
666    return EXCEPTION_CONTINUE_SEARCH;
667  }
668
669  assert(perror != NULL);
670  if (pep != NULL && pep->ExceptionRecord != NULL &&
671      pep->ExceptionRecord->NumberParameters >= 3) {
672    NTSTATUS status = (NTSTATUS)pep->ExceptionRecord->ExceptionInformation[3];
673    *perror = pRtlNtStatusToDosError(status);
674    if (*perror != ERROR_SUCCESS) {
675      return EXCEPTION_EXECUTE_HANDLER;
676    }
677  }
678  *perror = UV_UNKNOWN;
679  return EXCEPTION_EXECUTE_HANDLER;
680}
681
682
683void fs__read_filemap(uv_fs_t* req, struct uv__fd_info_s* fd_info) {
684  int fd = req->file.fd; /* VERIFY_FD done in fs__read */
685  int rw_flags = fd_info->flags &
686    (UV_FS_O_RDONLY | UV_FS_O_WRONLY | UV_FS_O_RDWR);
687  size_t read_size, done_read;
688  unsigned int index;
689  LARGE_INTEGER pos, end_pos;
690  size_t view_offset;
691  LARGE_INTEGER view_base;
692  void* view;
693
694  if (rw_flags == UV_FS_O_WRONLY) {
695    SET_REQ_WIN32_ERROR(req, ERROR_INVALID_FLAGS);
696    return;
697  }
698  if (fd_info->is_directory) {
699    SET_REQ_WIN32_ERROR(req, ERROR_INVALID_FUNCTION);
700    return;
701  }
702
703  if (req->fs.info.offset == -1) {
704    pos = fd_info->current_pos;
705  } else {
706    pos.QuadPart = req->fs.info.offset;
707  }
708
709  /* Make sure we wont read past EOF. */
710  if (pos.QuadPart >= fd_info->size.QuadPart) {
711    SET_REQ_RESULT(req, 0);
712    return;
713  }
714
715  read_size = 0;
716  for (index = 0; index < req->fs.info.nbufs; ++index) {
717    read_size += req->fs.info.bufs[index].len;
718  }
719  read_size = (size_t) MIN((LONGLONG) read_size,
720                           fd_info->size.QuadPart - pos.QuadPart);
721  if (read_size == 0) {
722    SET_REQ_RESULT(req, 0);
723    return;
724  }
725
726  end_pos.QuadPart = pos.QuadPart + read_size;
727
728  view_offset = pos.QuadPart % uv__allocation_granularity;
729  view_base.QuadPart = pos.QuadPart - view_offset;
730  view = MapViewOfFile(fd_info->mapping,
731                       FILE_MAP_READ,
732                       view_base.HighPart,
733                       view_base.LowPart,
734                       view_offset + read_size);
735  if (view == NULL) {
736    SET_REQ_WIN32_ERROR(req, GetLastError());
737    return;
738  }
739
740  done_read = 0;
741  for (index = 0;
742       index < req->fs.info.nbufs && done_read < read_size;
743       ++index) {
744    size_t this_read_size = MIN(req->fs.info.bufs[index].len,
745                                read_size - done_read);
746#ifdef _MSC_VER
747    int err = 0;
748    __try {
749#endif
750      memcpy(req->fs.info.bufs[index].base,
751             (char*)view + view_offset + done_read,
752             this_read_size);
753#ifdef _MSC_VER
754    }
755    __except (fs__filemap_ex_filter(GetExceptionCode(),
756                                    GetExceptionInformation(), &err)) {
757      SET_REQ_WIN32_ERROR(req, err);
758      UnmapViewOfFile(view);
759      return;
760    }
761#endif
762    done_read += this_read_size;
763  }
764  assert(done_read == read_size);
765
766  if (!UnmapViewOfFile(view)) {
767    SET_REQ_WIN32_ERROR(req, GetLastError());
768    return;
769  }
770
771  if (req->fs.info.offset == -1) {
772    fd_info->current_pos = end_pos;
773    uv__fd_hash_add(fd, fd_info);
774  }
775
776  SET_REQ_RESULT(req, read_size);
777  return;
778}
779
780void fs__read(uv_fs_t* req) {
781  int fd = req->file.fd;
782  int64_t offset = req->fs.info.offset;
783  HANDLE handle;
784  OVERLAPPED overlapped, *overlapped_ptr;
785  LARGE_INTEGER offset_;
786  DWORD bytes;
787  DWORD error;
788  int result;
789  unsigned int index;
790  LARGE_INTEGER original_position;
791  LARGE_INTEGER zero_offset;
792  int restore_position;
793  struct uv__fd_info_s fd_info;
794
795  VERIFY_FD(fd, req);
796
797  if (uv__fd_hash_get(fd, &fd_info)) {
798    fs__read_filemap(req, &fd_info);
799    return;
800  }
801
802  zero_offset.QuadPart = 0;
803  restore_position = 0;
804  handle = uv__get_osfhandle(fd);
805
806  if (handle == INVALID_HANDLE_VALUE) {
807    SET_REQ_WIN32_ERROR(req, ERROR_INVALID_HANDLE);
808    return;
809  }
810
811  if (offset != -1) {
812    memset(&overlapped, 0, sizeof overlapped);
813    overlapped_ptr = &overlapped;
814    if (SetFilePointerEx(handle, zero_offset, &original_position,
815                         FILE_CURRENT)) {
816      restore_position = 1;
817    }
818  } else {
819    overlapped_ptr = NULL;
820  }
821
822  index = 0;
823  bytes = 0;
824  do {
825    DWORD incremental_bytes;
826
827    if (offset != -1) {
828      offset_.QuadPart = offset + bytes;
829      overlapped.Offset = offset_.LowPart;
830      overlapped.OffsetHigh = offset_.HighPart;
831    }
832
833    result = ReadFile(handle,
834                      req->fs.info.bufs[index].base,
835                      req->fs.info.bufs[index].len,
836                      &incremental_bytes,
837                      overlapped_ptr);
838    bytes += incremental_bytes;
839    ++index;
840  } while (result && index < req->fs.info.nbufs);
841
842  if (restore_position)
843    SetFilePointerEx(handle, original_position, NULL, FILE_BEGIN);
844
845  if (result || bytes > 0) {
846    SET_REQ_RESULT(req, bytes);
847  } else {
848    error = GetLastError();
849    if (error == ERROR_ACCESS_DENIED) {
850      error = ERROR_INVALID_FLAGS;
851    }
852
853    if (error == ERROR_HANDLE_EOF || error == ERROR_BROKEN_PIPE) {
854      SET_REQ_RESULT(req, bytes);
855    } else {
856      SET_REQ_WIN32_ERROR(req, error);
857    }
858  }
859}
860
861
862void fs__write_filemap(uv_fs_t* req, HANDLE file,
863                       struct uv__fd_info_s* fd_info) {
864  int fd = req->file.fd; /* VERIFY_FD done in fs__write */
865  int force_append = fd_info->flags & UV_FS_O_APPEND;
866  int rw_flags = fd_info->flags &
867    (UV_FS_O_RDONLY | UV_FS_O_WRONLY | UV_FS_O_RDWR);
868  size_t write_size, done_write;
869  unsigned int index;
870  LARGE_INTEGER pos, end_pos;
871  size_t view_offset;
872  LARGE_INTEGER view_base;
873  void* view;
874  FILETIME ft;
875
876  if (rw_flags == UV_FS_O_RDONLY) {
877    SET_REQ_WIN32_ERROR(req, ERROR_INVALID_FLAGS);
878    return;
879  }
880  if (fd_info->is_directory) {
881    SET_REQ_WIN32_ERROR(req, ERROR_INVALID_FUNCTION);
882    return;
883  }
884
885  write_size = 0;
886  for (index = 0; index < req->fs.info.nbufs; ++index) {
887    write_size += req->fs.info.bufs[index].len;
888  }
889
890  if (write_size == 0) {
891    SET_REQ_RESULT(req, 0);
892    return;
893  }
894
895  if (force_append) {
896    pos = fd_info->size;
897  } else if (req->fs.info.offset == -1) {
898    pos = fd_info->current_pos;
899  } else {
900    pos.QuadPart = req->fs.info.offset;
901  }
902
903  end_pos.QuadPart = pos.QuadPart + write_size;
904
905  /* Recreate the mapping to enlarge the file if needed */
906  if (end_pos.QuadPart > fd_info->size.QuadPart) {
907    if (fd_info->mapping != INVALID_HANDLE_VALUE) {
908      CloseHandle(fd_info->mapping);
909    }
910
911    fd_info->mapping = CreateFileMapping(file,
912                                         NULL,
913                                         PAGE_READWRITE,
914                                         end_pos.HighPart,
915                                         end_pos.LowPart,
916                                         NULL);
917    if (fd_info->mapping == NULL) {
918      SET_REQ_WIN32_ERROR(req, GetLastError());
919      CloseHandle(file);
920      fd_info->mapping = INVALID_HANDLE_VALUE;
921      fd_info->size.QuadPart = 0;
922      fd_info->current_pos.QuadPart = 0;
923      uv__fd_hash_add(fd, fd_info);
924      return;
925    }
926
927    fd_info->size = end_pos;
928    uv__fd_hash_add(fd, fd_info);
929  }
930
931  view_offset = pos.QuadPart % uv__allocation_granularity;
932  view_base.QuadPart = pos.QuadPart - view_offset;
933  view = MapViewOfFile(fd_info->mapping,
934                       FILE_MAP_WRITE,
935                       view_base.HighPart,
936                       view_base.LowPart,
937                       view_offset + write_size);
938  if (view == NULL) {
939    SET_REQ_WIN32_ERROR(req, GetLastError());
940    return;
941  }
942
943  done_write = 0;
944  for (index = 0; index < req->fs.info.nbufs; ++index) {
945#ifdef _MSC_VER
946    int err = 0;
947    __try {
948#endif
949      memcpy((char*)view + view_offset + done_write,
950             req->fs.info.bufs[index].base,
951             req->fs.info.bufs[index].len);
952#ifdef _MSC_VER
953    }
954    __except (fs__filemap_ex_filter(GetExceptionCode(),
955                                    GetExceptionInformation(), &err)) {
956      SET_REQ_WIN32_ERROR(req, err);
957      UnmapViewOfFile(view);
958      return;
959    }
960#endif
961    done_write += req->fs.info.bufs[index].len;
962  }
963  assert(done_write == write_size);
964
965  if (!FlushViewOfFile(view, 0)) {
966    SET_REQ_WIN32_ERROR(req, GetLastError());
967    UnmapViewOfFile(view);
968    return;
969  }
970  if (!UnmapViewOfFile(view)) {
971    SET_REQ_WIN32_ERROR(req, GetLastError());
972    return;
973  }
974
975  if (req->fs.info.offset == -1) {
976    fd_info->current_pos = end_pos;
977    uv__fd_hash_add(fd, fd_info);
978  }
979
980  GetSystemTimeAsFileTime(&ft);
981  SetFileTime(file, NULL, NULL, &ft);
982
983  SET_REQ_RESULT(req, done_write);
984}
985
986void fs__write(uv_fs_t* req) {
987  int fd = req->file.fd;
988  int64_t offset = req->fs.info.offset;
989  HANDLE handle;
990  OVERLAPPED overlapped, *overlapped_ptr;
991  LARGE_INTEGER offset_;
992  DWORD bytes;
993  DWORD error;
994  int result;
995  unsigned int index;
996  LARGE_INTEGER original_position;
997  LARGE_INTEGER zero_offset;
998  int restore_position;
999  struct uv__fd_info_s fd_info;
1000
1001  VERIFY_FD(fd, req);
1002
1003  zero_offset.QuadPart = 0;
1004  restore_position = 0;
1005  handle = uv__get_osfhandle(fd);
1006  if (handle == INVALID_HANDLE_VALUE) {
1007    SET_REQ_WIN32_ERROR(req, ERROR_INVALID_HANDLE);
1008    return;
1009  }
1010
1011  if (uv__fd_hash_get(fd, &fd_info)) {
1012    fs__write_filemap(req, handle, &fd_info);
1013    return;
1014  }
1015
1016  if (offset != -1) {
1017    memset(&overlapped, 0, sizeof overlapped);
1018    overlapped_ptr = &overlapped;
1019    if (SetFilePointerEx(handle, zero_offset, &original_position,
1020                         FILE_CURRENT)) {
1021      restore_position = 1;
1022    }
1023  } else {
1024    overlapped_ptr = NULL;
1025  }
1026
1027  index = 0;
1028  bytes = 0;
1029  do {
1030    DWORD incremental_bytes;
1031
1032    if (offset != -1) {
1033      offset_.QuadPart = offset + bytes;
1034      overlapped.Offset = offset_.LowPart;
1035      overlapped.OffsetHigh = offset_.HighPart;
1036    }
1037
1038    result = WriteFile(handle,
1039                       req->fs.info.bufs[index].base,
1040                       req->fs.info.bufs[index].len,
1041                       &incremental_bytes,
1042                       overlapped_ptr);
1043    bytes += incremental_bytes;
1044    ++index;
1045  } while (result && index < req->fs.info.nbufs);
1046
1047  if (restore_position)
1048    SetFilePointerEx(handle, original_position, NULL, FILE_BEGIN);
1049
1050  if (result || bytes > 0) {
1051    SET_REQ_RESULT(req, bytes);
1052  } else {
1053    error = GetLastError();
1054
1055    if (error == ERROR_ACCESS_DENIED) {
1056      error = ERROR_INVALID_FLAGS;
1057    }
1058
1059    SET_REQ_WIN32_ERROR(req, error);
1060  }
1061}
1062
1063
1064void fs__rmdir(uv_fs_t* req) {
1065  int result = _wrmdir(req->file.pathw);
1066  if (result == -1)
1067    SET_REQ_WIN32_ERROR(req, _doserrno);
1068  else
1069    SET_REQ_RESULT(req, 0);
1070}
1071
1072
1073void fs__unlink(uv_fs_t* req) {
1074  const WCHAR* pathw = req->file.pathw;
1075  HANDLE handle;
1076  BY_HANDLE_FILE_INFORMATION info;
1077  FILE_DISPOSITION_INFORMATION disposition;
1078  IO_STATUS_BLOCK iosb;
1079  NTSTATUS status;
1080
1081  handle = CreateFileW(pathw,
1082                       FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | DELETE,
1083                       FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1084                       NULL,
1085                       OPEN_EXISTING,
1086                       FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
1087                       NULL);
1088
1089  if (handle == INVALID_HANDLE_VALUE) {
1090    SET_REQ_WIN32_ERROR(req, GetLastError());
1091    return;
1092  }
1093
1094  if (!GetFileInformationByHandle(handle, &info)) {
1095    SET_REQ_WIN32_ERROR(req, GetLastError());
1096    CloseHandle(handle);
1097    return;
1098  }
1099
1100  if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1101    /* Do not allow deletion of directories, unless it is a symlink. When the
1102     * path refers to a non-symlink directory, report EPERM as mandated by
1103     * POSIX.1. */
1104
1105    /* Check if it is a reparse point. If it's not, it's a normal directory. */
1106    if (!(info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
1107      SET_REQ_WIN32_ERROR(req, ERROR_ACCESS_DENIED);
1108      CloseHandle(handle);
1109      return;
1110    }
1111
1112    /* Read the reparse point and check if it is a valid symlink. If not, don't
1113     * unlink. */
1114    if (fs__readlink_handle(handle, NULL, NULL) < 0) {
1115      DWORD error = GetLastError();
1116      if (error == ERROR_SYMLINK_NOT_SUPPORTED)
1117        error = ERROR_ACCESS_DENIED;
1118      SET_REQ_WIN32_ERROR(req, error);
1119      CloseHandle(handle);
1120      return;
1121    }
1122  }
1123
1124  if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
1125    /* Remove read-only attribute */
1126    FILE_BASIC_INFORMATION basic = { 0 };
1127
1128    basic.FileAttributes = (info.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY) |
1129                           FILE_ATTRIBUTE_ARCHIVE;
1130
1131    status = pNtSetInformationFile(handle,
1132                                   &iosb,
1133                                   &basic,
1134                                   sizeof basic,
1135                                   FileBasicInformation);
1136    if (!NT_SUCCESS(status)) {
1137      SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(status));
1138      CloseHandle(handle);
1139      return;
1140    }
1141  }
1142
1143  /* Try to set the delete flag. */
1144  disposition.DeleteFile = TRUE;
1145  status = pNtSetInformationFile(handle,
1146                                 &iosb,
1147                                 &disposition,
1148                                 sizeof disposition,
1149                                 FileDispositionInformation);
1150  if (NT_SUCCESS(status)) {
1151    SET_REQ_SUCCESS(req);
1152  } else {
1153    SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(status));
1154  }
1155
1156  CloseHandle(handle);
1157}
1158
1159
1160void fs__mkdir(uv_fs_t* req) {
1161  /* TODO: use req->mode. */
1162  if (CreateDirectoryW(req->file.pathw, NULL)) {
1163    SET_REQ_RESULT(req, 0);
1164  } else {
1165    SET_REQ_WIN32_ERROR(req, GetLastError());
1166    if (req->sys_errno_ == ERROR_INVALID_NAME ||
1167        req->sys_errno_ == ERROR_DIRECTORY)
1168      req->result = UV_EINVAL;
1169  }
1170}
1171
1172typedef int (*uv__fs_mktemp_func)(uv_fs_t* req);
1173
1174/* OpenBSD original: lib/libc/stdio/mktemp.c */
1175void fs__mktemp(uv_fs_t* req, uv__fs_mktemp_func func) {
1176  static const WCHAR *tempchars =
1177    L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
1178  static const size_t num_chars = 62;
1179  static const size_t num_x = 6;
1180  WCHAR *cp, *ep;
1181  unsigned int tries, i;
1182  size_t len;
1183  uint64_t v;
1184  char* path;
1185
1186  path = (char*)req->path;
1187  len = wcslen(req->file.pathw);
1188  ep = req->file.pathw + len;
1189  if (len < num_x || wcsncmp(ep - num_x, L"XXXXXX", num_x)) {
1190    SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER);
1191    goto clobber;
1192  }
1193
1194  tries = TMP_MAX;
1195  do {
1196    if (uv__random_rtlgenrandom((void *)&v, sizeof(v)) < 0) {
1197      SET_REQ_UV_ERROR(req, UV_EIO, ERROR_IO_DEVICE);
1198      goto clobber;
1199    }
1200
1201    cp = ep - num_x;
1202    for (i = 0; i < num_x; i++) {
1203      *cp++ = tempchars[v % num_chars];
1204      v /= num_chars;
1205    }
1206
1207    if (func(req)) {
1208      if (req->result >= 0) {
1209        len = strlen(path);
1210        wcstombs(path + len - num_x, ep - num_x, num_x);
1211      }
1212      return;
1213    }
1214  } while (--tries);
1215
1216  SET_REQ_WIN32_ERROR(req, GetLastError());
1217
1218clobber:
1219  path[0] = '\0';
1220}
1221
1222
1223static int fs__mkdtemp_func(uv_fs_t* req) {
1224  DWORD error;
1225  if (CreateDirectoryW(req->file.pathw, NULL)) {
1226    SET_REQ_RESULT(req, 0);
1227    return 1;
1228  }
1229  error = GetLastError();
1230  if (error != ERROR_ALREADY_EXISTS) {
1231    SET_REQ_WIN32_ERROR(req, error);
1232    return 1;
1233  }
1234
1235  return 0;
1236}
1237
1238
1239void fs__mkdtemp(uv_fs_t* req) {
1240  fs__mktemp(req, fs__mkdtemp_func);
1241}
1242
1243
1244static int fs__mkstemp_func(uv_fs_t* req) {
1245  HANDLE file;
1246  int fd;
1247
1248  file = CreateFileW(req->file.pathw,
1249                     GENERIC_READ | GENERIC_WRITE,
1250                     FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1251                     NULL,
1252                     CREATE_NEW,
1253                     FILE_ATTRIBUTE_NORMAL,
1254                     NULL);
1255
1256  if (file == INVALID_HANDLE_VALUE) {
1257    DWORD error;
1258    error = GetLastError();
1259
1260    /* If the file exists, the main fs__mktemp() function
1261       will retry. If it's another error, we want to stop. */
1262    if (error != ERROR_FILE_EXISTS) {
1263      SET_REQ_WIN32_ERROR(req, error);
1264      return 1;
1265    }
1266
1267    return 0;
1268  }
1269
1270  fd = _open_osfhandle((intptr_t) file, 0);
1271  if (fd < 0) {
1272    /* The only known failure mode for _open_osfhandle() is EMFILE, in which
1273     * case GetLastError() will return zero. However we'll try to handle other
1274     * errors as well, should they ever occur.
1275     */
1276    if (errno == EMFILE)
1277      SET_REQ_UV_ERROR(req, UV_EMFILE, ERROR_TOO_MANY_OPEN_FILES);
1278    else if (GetLastError() != ERROR_SUCCESS)
1279      SET_REQ_WIN32_ERROR(req, GetLastError());
1280    else
1281      SET_REQ_WIN32_ERROR(req, UV_UNKNOWN);
1282    CloseHandle(file);
1283    return 1;
1284  }
1285
1286  SET_REQ_RESULT(req, fd);
1287
1288  return 1;
1289}
1290
1291
1292void fs__mkstemp(uv_fs_t* req) {
1293  fs__mktemp(req, fs__mkstemp_func);
1294}
1295
1296
1297void fs__scandir(uv_fs_t* req) {
1298  static const size_t dirents_initial_size = 32;
1299
1300  HANDLE dir_handle = INVALID_HANDLE_VALUE;
1301
1302  uv__dirent_t** dirents = NULL;
1303  size_t dirents_size = 0;
1304  size_t dirents_used = 0;
1305
1306  IO_STATUS_BLOCK iosb;
1307  NTSTATUS status;
1308
1309  /* Buffer to hold directory entries returned by NtQueryDirectoryFile.
1310   * It's important that this buffer can hold at least one entry, regardless
1311   * of the length of the file names present in the enumerated directory.
1312   * A file name is at most 256 WCHARs long.
1313   * According to MSDN, the buffer must be aligned at an 8-byte boundary.
1314   */
1315#if _MSC_VER
1316  __declspec(align(8)) char buffer[8192];
1317#else
1318  __attribute__ ((aligned (8))) char buffer[8192];
1319#endif
1320
1321  STATIC_ASSERT(sizeof buffer >=
1322                sizeof(FILE_DIRECTORY_INFORMATION) + 256 * sizeof(WCHAR));
1323
1324  /* Open the directory. */
1325  dir_handle =
1326      CreateFileW(req->file.pathw,
1327                  FILE_LIST_DIRECTORY | SYNCHRONIZE,
1328                  FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1329                  NULL,
1330                  OPEN_EXISTING,
1331                  FILE_FLAG_BACKUP_SEMANTICS,
1332                  NULL);
1333  if (dir_handle == INVALID_HANDLE_VALUE)
1334    goto win32_error;
1335
1336  /* Read the first chunk. */
1337  status = pNtQueryDirectoryFile(dir_handle,
1338                                 NULL,
1339                                 NULL,
1340                                 NULL,
1341                                 &iosb,
1342                                 &buffer,
1343                                 sizeof buffer,
1344                                 FileDirectoryInformation,
1345                                 FALSE,
1346                                 NULL,
1347                                 TRUE);
1348
1349  /* If the handle is not a directory, we'll get STATUS_INVALID_PARAMETER.
1350   * This should be reported back as UV_ENOTDIR.
1351   */
1352  if (status == (NTSTATUS)STATUS_INVALID_PARAMETER)
1353    goto not_a_directory_error;
1354
1355  while (NT_SUCCESS(status)) {
1356    char* position = buffer;
1357    size_t next_entry_offset = 0;
1358
1359    do {
1360      FILE_DIRECTORY_INFORMATION* info;
1361      uv__dirent_t* dirent;
1362
1363      size_t wchar_len;
1364      size_t wtf8_len;
1365      char* wtf8;
1366
1367      /* Obtain a pointer to the current directory entry. */
1368      position += next_entry_offset;
1369      info = (FILE_DIRECTORY_INFORMATION*) position;
1370
1371      /* Fetch the offset to the next directory entry. */
1372      next_entry_offset = info->NextEntryOffset;
1373
1374      /* Compute the length of the filename in WCHARs. */
1375      wchar_len = info->FileNameLength / sizeof info->FileName[0];
1376
1377      /* Skip over '.' and '..' entries.  It has been reported that
1378       * the SharePoint driver includes the terminating zero byte in
1379       * the filename length.  Strip those first.
1380       */
1381      while (wchar_len > 0 && info->FileName[wchar_len - 1] == L'\0')
1382        wchar_len -= 1;
1383
1384      if (wchar_len == 0)
1385        continue;
1386      if (wchar_len == 1 && info->FileName[0] == L'.')
1387        continue;
1388      if (wchar_len == 2 && info->FileName[0] == L'.' &&
1389          info->FileName[1] == L'.')
1390        continue;
1391
1392      /* Compute the space required to store the filename as WTF-8. */
1393      wtf8_len = uv_utf16_length_as_wtf8(&info->FileName[0], wchar_len);
1394
1395      /* Resize the dirent array if needed. */
1396      if (dirents_used >= dirents_size) {
1397        size_t new_dirents_size =
1398            dirents_size == 0 ? dirents_initial_size : dirents_size << 1;
1399        uv__dirent_t** new_dirents =
1400            uv__realloc(dirents, new_dirents_size * sizeof *dirents);
1401
1402        if (new_dirents == NULL)
1403          goto out_of_memory_error;
1404
1405        dirents_size = new_dirents_size;
1406        dirents = new_dirents;
1407      }
1408
1409      /* Allocate space for the uv dirent structure. The dirent structure
1410       * includes room for the first character of the filename, but `utf8_len`
1411       * doesn't count the NULL terminator at this point.
1412       */
1413      dirent = uv__malloc(sizeof *dirent + wtf8_len);
1414      if (dirent == NULL)
1415        goto out_of_memory_error;
1416
1417      dirents[dirents_used++] = dirent;
1418
1419      /* Convert file name to UTF-8. */
1420      wtf8 = &dirent->d_name[0];
1421      if (uv_utf16_to_wtf8(&info->FileName[0], wchar_len, &wtf8, &wtf8_len) != 0)
1422        goto out_of_memory_error;
1423
1424      /* Fill out the type field. */
1425      if (info->FileAttributes & FILE_ATTRIBUTE_DEVICE)
1426        dirent->d_type = UV__DT_CHAR;
1427      else if (info->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
1428        dirent->d_type = UV__DT_LINK;
1429      else if (info->FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1430        dirent->d_type = UV__DT_DIR;
1431      else
1432        dirent->d_type = UV__DT_FILE;
1433    } while (next_entry_offset != 0);
1434
1435    /* Read the next chunk. */
1436    status = pNtQueryDirectoryFile(dir_handle,
1437                                   NULL,
1438                                   NULL,
1439                                   NULL,
1440                                   &iosb,
1441                                   &buffer,
1442                                   sizeof buffer,
1443                                   FileDirectoryInformation,
1444                                   FALSE,
1445                                   NULL,
1446                                   FALSE);
1447
1448    /* After the first pNtQueryDirectoryFile call, the function may return
1449     * STATUS_SUCCESS even if the buffer was too small to hold at least one
1450     * directory entry.
1451     */
1452    if (status == STATUS_SUCCESS && iosb.Information == 0)
1453      status = STATUS_BUFFER_OVERFLOW;
1454  }
1455
1456  if (status != STATUS_NO_MORE_FILES)
1457    goto nt_error;
1458
1459  CloseHandle(dir_handle);
1460
1461  /* Store the result in the request object. */
1462  req->ptr = dirents;
1463  if (dirents != NULL)
1464    req->flags |= UV_FS_FREE_PTR;
1465
1466  SET_REQ_RESULT(req, dirents_used);
1467
1468  /* `nbufs` will be used as index by uv_fs_scandir_next. */
1469  req->fs.info.nbufs = 0;
1470
1471  return;
1472
1473nt_error:
1474  SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(status));
1475  goto cleanup;
1476
1477win32_error:
1478  SET_REQ_WIN32_ERROR(req, GetLastError());
1479  goto cleanup;
1480
1481not_a_directory_error:
1482  SET_REQ_UV_ERROR(req, UV_ENOTDIR, ERROR_DIRECTORY);
1483  goto cleanup;
1484
1485out_of_memory_error:
1486  SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
1487  goto cleanup;
1488
1489cleanup:
1490  if (dir_handle != INVALID_HANDLE_VALUE)
1491    CloseHandle(dir_handle);
1492  while (dirents_used > 0)
1493    uv__free(dirents[--dirents_used]);
1494  if (dirents != NULL)
1495    uv__free(dirents);
1496}
1497
1498void fs__opendir(uv_fs_t* req) {
1499  WCHAR* pathw;
1500  size_t len;
1501  const WCHAR* fmt;
1502  WCHAR* find_path;
1503  uv_dir_t* dir;
1504
1505  pathw = req->file.pathw;
1506  dir = NULL;
1507  find_path = NULL;
1508
1509  /* Figure out whether path is a file or a directory. */
1510  if (!(GetFileAttributesW(pathw) & FILE_ATTRIBUTE_DIRECTORY)) {
1511    SET_REQ_UV_ERROR(req, UV_ENOTDIR, ERROR_DIRECTORY);
1512    goto error;
1513  }
1514
1515  dir = uv__malloc(sizeof(*dir));
1516  if (dir == NULL) {
1517    SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
1518    goto error;
1519  }
1520
1521  len = wcslen(pathw);
1522
1523  if (len == 0)
1524    fmt = L"./*";
1525  else if (IS_SLASH(pathw[len - 1]))
1526    fmt = L"%s*";
1527  else
1528    fmt = L"%s\\*";
1529
1530  find_path = uv__malloc(sizeof(WCHAR) * (len + 4));
1531  if (find_path == NULL) {
1532    SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
1533    goto error;
1534  }
1535
1536  _snwprintf(find_path, len + 3, fmt, pathw);
1537  dir->dir_handle = FindFirstFileW(find_path, &dir->find_data);
1538  uv__free(find_path);
1539  find_path = NULL;
1540  if (dir->dir_handle == INVALID_HANDLE_VALUE &&
1541      GetLastError() != ERROR_FILE_NOT_FOUND) {
1542    SET_REQ_WIN32_ERROR(req, GetLastError());
1543    goto error;
1544  }
1545
1546  dir->need_find_call = FALSE;
1547  req->ptr = dir;
1548  SET_REQ_RESULT(req, 0);
1549  return;
1550
1551error:
1552  uv__free(dir);
1553  uv__free(find_path);
1554  req->ptr = NULL;
1555}
1556
1557void fs__readdir(uv_fs_t* req) {
1558  uv_dir_t* dir;
1559  uv_dirent_t* dirents;
1560  uv__dirent_t dent;
1561  unsigned int dirent_idx;
1562  PWIN32_FIND_DATAW find_data;
1563  unsigned int i;
1564  int r;
1565
1566  req->flags |= UV_FS_FREE_PTR;
1567  dir = req->ptr;
1568  dirents = dir->dirents;
1569  memset(dirents, 0, dir->nentries * sizeof(*dir->dirents));
1570  find_data = &dir->find_data;
1571  dirent_idx = 0;
1572
1573  while (dirent_idx < dir->nentries) {
1574    if (dir->need_find_call && FindNextFileW(dir->dir_handle, find_data) == 0) {
1575      if (GetLastError() == ERROR_NO_MORE_FILES)
1576        break;
1577      goto error;
1578    }
1579
1580    /* Skip "." and ".." entries. */
1581    if (find_data->cFileName[0] == L'.' &&
1582        (find_data->cFileName[1] == L'\0' ||
1583        (find_data->cFileName[1] == L'.' &&
1584        find_data->cFileName[2] == L'\0'))) {
1585      dir->need_find_call = TRUE;
1586      continue;
1587    }
1588
1589    r = uv__convert_utf16_to_utf8((const WCHAR*) &find_data->cFileName,
1590                                  -1,
1591                                  (char**) &dirents[dirent_idx].name);
1592    if (r != 0)
1593      goto error;
1594
1595    /* Copy file type. */
1596    if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
1597      dent.d_type = UV__DT_DIR;
1598    else if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0)
1599      dent.d_type = UV__DT_LINK;
1600    else if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_DEVICE) != 0)
1601      dent.d_type = UV__DT_CHAR;
1602    else
1603      dent.d_type = UV__DT_FILE;
1604
1605    dirents[dirent_idx].type = uv__fs_get_dirent_type(&dent);
1606    dir->need_find_call = TRUE;
1607    ++dirent_idx;
1608  }
1609
1610  SET_REQ_RESULT(req, dirent_idx);
1611  return;
1612
1613error:
1614  SET_REQ_WIN32_ERROR(req, GetLastError());
1615  for (i = 0; i < dirent_idx; ++i) {
1616    uv__free((char*) dirents[i].name);
1617    dirents[i].name = NULL;
1618  }
1619}
1620
1621void fs__closedir(uv_fs_t* req) {
1622  uv_dir_t* dir;
1623
1624  dir = req->ptr;
1625  FindClose(dir->dir_handle);
1626  uv__free(req->ptr);
1627  SET_REQ_RESULT(req, 0);
1628}
1629
1630INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf,
1631    int do_lstat) {
1632  size_t target_length = 0;
1633  FILE_FS_DEVICE_INFORMATION device_info;
1634  FILE_ALL_INFORMATION file_info;
1635  FILE_FS_VOLUME_INFORMATION volume_info;
1636  NTSTATUS nt_status;
1637  IO_STATUS_BLOCK io_status;
1638
1639  nt_status = pNtQueryVolumeInformationFile(handle,
1640                                            &io_status,
1641                                            &device_info,
1642                                            sizeof device_info,
1643                                            FileFsDeviceInformation);
1644
1645  /* Buffer overflow (a warning status code) is expected here. */
1646  if (NT_ERROR(nt_status)) {
1647    SetLastError(pRtlNtStatusToDosError(nt_status));
1648    return -1;
1649  }
1650
1651  /* If it's NUL device set fields as reasonable as possible and return. */
1652  if (device_info.DeviceType == FILE_DEVICE_NULL) {
1653    memset(statbuf, 0, sizeof(uv_stat_t));
1654    statbuf->st_mode = _S_IFCHR;
1655    statbuf->st_mode |= (_S_IREAD | _S_IWRITE) | ((_S_IREAD | _S_IWRITE) >> 3) |
1656                        ((_S_IREAD | _S_IWRITE) >> 6);
1657    statbuf->st_nlink = 1;
1658    statbuf->st_blksize = 4096;
1659    statbuf->st_rdev = FILE_DEVICE_NULL << 16;
1660    return 0;
1661  }
1662
1663  nt_status = pNtQueryInformationFile(handle,
1664                                      &io_status,
1665                                      &file_info,
1666                                      sizeof file_info,
1667                                      FileAllInformation);
1668
1669  /* Buffer overflow (a warning status code) is expected here. */
1670  if (NT_ERROR(nt_status)) {
1671    SetLastError(pRtlNtStatusToDosError(nt_status));
1672    return -1;
1673  }
1674
1675  nt_status = pNtQueryVolumeInformationFile(handle,
1676                                            &io_status,
1677                                            &volume_info,
1678                                            sizeof volume_info,
1679                                            FileFsVolumeInformation);
1680
1681  /* Buffer overflow (a warning status code) is expected here. */
1682  if (io_status.Status == STATUS_NOT_IMPLEMENTED) {
1683    statbuf->st_dev = 0;
1684  } else if (NT_ERROR(nt_status)) {
1685    SetLastError(pRtlNtStatusToDosError(nt_status));
1686    return -1;
1687  } else {
1688    statbuf->st_dev = volume_info.VolumeSerialNumber;
1689  }
1690
1691  /* Todo: st_mode should probably always be 0666 for everyone. We might also
1692   * want to report 0777 if the file is a .exe or a directory.
1693   *
1694   * Currently it's based on whether the 'readonly' attribute is set, which
1695   * makes little sense because the semantics are so different: the 'read-only'
1696   * flag is just a way for a user to protect against accidental deletion, and
1697   * serves no security purpose. Windows uses ACLs for that.
1698   *
1699   * Also people now use uv_fs_chmod() to take away the writable bit for good
1700   * reasons. Windows however just makes the file read-only, which makes it
1701   * impossible to delete the file afterwards, since read-only files can't be
1702   * deleted.
1703   *
1704   * IOW it's all just a clusterfuck and we should think of something that
1705   * makes slightly more sense.
1706   *
1707   * And uv_fs_chmod should probably just fail on windows or be a total no-op.
1708   * There's nothing sensible it can do anyway.
1709   */
1710  statbuf->st_mode = 0;
1711
1712  /*
1713  * On Windows, FILE_ATTRIBUTE_REPARSE_POINT is a general purpose mechanism
1714  * by which filesystem drivers can intercept and alter file system requests.
1715  *
1716  * The only reparse points we care about are symlinks and mount points, both
1717  * of which are treated as POSIX symlinks. Further, we only care when
1718  * invoked via lstat, which seeks information about the link instead of its
1719  * target. Otherwise, reparse points must be treated as regular files.
1720  */
1721  if (do_lstat &&
1722      (file_info.BasicInformation.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
1723    /*
1724     * If reading the link fails, the reparse point is not a symlink and needs
1725     * to be treated as a regular file. The higher level lstat function will
1726     * detect this failure and retry without do_lstat if appropriate.
1727     */
1728    if (fs__readlink_handle(handle, NULL, &target_length) != 0)
1729      return -1;
1730    statbuf->st_mode |= S_IFLNK;
1731    statbuf->st_size = target_length;
1732  }
1733
1734  if (statbuf->st_mode == 0) {
1735    if (file_info.BasicInformation.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1736      statbuf->st_mode |= _S_IFDIR;
1737      statbuf->st_size = 0;
1738    } else {
1739      statbuf->st_mode |= _S_IFREG;
1740      statbuf->st_size = file_info.StandardInformation.EndOfFile.QuadPart;
1741    }
1742  }
1743
1744  if (file_info.BasicInformation.FileAttributes & FILE_ATTRIBUTE_READONLY)
1745    statbuf->st_mode |= _S_IREAD | (_S_IREAD >> 3) | (_S_IREAD >> 6);
1746  else
1747    statbuf->st_mode |= (_S_IREAD | _S_IWRITE) | ((_S_IREAD | _S_IWRITE) >> 3) |
1748                        ((_S_IREAD | _S_IWRITE) >> 6);
1749
1750  uv__filetime_to_timespec(&statbuf->st_atim,
1751                           file_info.BasicInformation.LastAccessTime.QuadPart);
1752  uv__filetime_to_timespec(&statbuf->st_ctim,
1753                           file_info.BasicInformation.ChangeTime.QuadPart);
1754  uv__filetime_to_timespec(&statbuf->st_mtim,
1755                           file_info.BasicInformation.LastWriteTime.QuadPart);
1756  uv__filetime_to_timespec(&statbuf->st_birthtim,
1757                           file_info.BasicInformation.CreationTime.QuadPart);
1758
1759  statbuf->st_ino = file_info.InternalInformation.IndexNumber.QuadPart;
1760
1761  /* st_blocks contains the on-disk allocation size in 512-byte units. */
1762  statbuf->st_blocks =
1763      (uint64_t) file_info.StandardInformation.AllocationSize.QuadPart >> 9;
1764
1765  statbuf->st_nlink = file_info.StandardInformation.NumberOfLinks;
1766
1767  /* The st_blksize is supposed to be the 'optimal' number of bytes for reading
1768   * and writing to the disk. That is, for any definition of 'optimal' - it's
1769   * supposed to at least avoid read-update-write behavior when writing to the
1770   * disk.
1771   *
1772   * However nobody knows this and even fewer people actually use this value,
1773   * and in order to fill it out we'd have to make another syscall to query the
1774   * volume for FILE_FS_SECTOR_SIZE_INFORMATION.
1775   *
1776   * Therefore we'll just report a sensible value that's quite commonly okay
1777   * on modern hardware.
1778   *
1779   * 4096 is the minimum required to be compatible with newer Advanced Format
1780   * drives (which have 4096 bytes per physical sector), and to be backwards
1781   * compatible with older drives (which have 512 bytes per physical sector).
1782   */
1783  statbuf->st_blksize = 4096;
1784
1785  /* Todo: set st_flags to something meaningful. Also provide a wrapper for
1786   * chattr(2).
1787   */
1788  statbuf->st_flags = 0;
1789
1790  /* Windows has nothing sensible to say about these values, so they'll just
1791   * remain empty.
1792   */
1793  statbuf->st_gid = 0;
1794  statbuf->st_uid = 0;
1795  statbuf->st_rdev = 0;
1796  statbuf->st_gen = 0;
1797
1798  return 0;
1799}
1800
1801
1802INLINE static void fs__stat_prepare_path(WCHAR* pathw) {
1803  size_t len = wcslen(pathw);
1804
1805  /* TODO: ignore namespaced paths. */
1806  if (len > 1 && pathw[len - 2] != L':' &&
1807      (pathw[len - 1] == L'\\' || pathw[len - 1] == L'/')) {
1808    pathw[len - 1] = '\0';
1809  }
1810}
1811
1812
1813INLINE static DWORD fs__stat_impl_from_path(WCHAR* path,
1814                                            int do_lstat,
1815                                            uv_stat_t* statbuf) {
1816  HANDLE handle;
1817  DWORD flags;
1818  DWORD ret;
1819
1820  flags = FILE_FLAG_BACKUP_SEMANTICS;
1821  if (do_lstat)
1822    flags |= FILE_FLAG_OPEN_REPARSE_POINT;
1823
1824  handle = CreateFileW(path,
1825                       FILE_READ_ATTRIBUTES,
1826                       FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1827                       NULL,
1828                       OPEN_EXISTING,
1829                       flags,
1830                       NULL);
1831
1832  if (handle == INVALID_HANDLE_VALUE)
1833    return GetLastError();
1834
1835  if (fs__stat_handle(handle, statbuf, do_lstat) != 0)
1836    ret = GetLastError();
1837  else
1838    ret = 0;
1839
1840  CloseHandle(handle);
1841  return ret;
1842}
1843
1844
1845INLINE static void fs__stat_impl(uv_fs_t* req, int do_lstat) {
1846  DWORD error;
1847
1848  error = fs__stat_impl_from_path(req->file.pathw, do_lstat, &req->statbuf);
1849  if (error != 0) {
1850    if (do_lstat &&
1851        (error == ERROR_SYMLINK_NOT_SUPPORTED ||
1852         error == ERROR_NOT_A_REPARSE_POINT)) {
1853      /* We opened a reparse point but it was not a symlink. Try again. */
1854      fs__stat_impl(req, 0);
1855    } else {
1856      /* Stat failed. */
1857      SET_REQ_WIN32_ERROR(req, error);
1858    }
1859
1860    return;
1861  }
1862
1863  req->ptr = &req->statbuf;
1864  SET_REQ_RESULT(req, 0);
1865}
1866
1867
1868INLINE static int fs__fstat_handle(int fd, HANDLE handle, uv_stat_t* statbuf) {
1869  DWORD file_type;
1870
1871  /* Each file type is processed differently. */
1872  file_type = uv_guess_handle(fd);
1873  switch (file_type) {
1874  /* Disk files use the existing logic from fs__stat_handle. */
1875  case UV_FILE:
1876    return fs__stat_handle(handle, statbuf, 0);
1877
1878  /* Devices and pipes are processed identically. There is no more information
1879   * for them from any API. Fields are set as reasonably as possible and the
1880   * function returns. */
1881  case UV_TTY:
1882  case UV_NAMED_PIPE:
1883    memset(statbuf, 0, sizeof(uv_stat_t));
1884    statbuf->st_mode = file_type == UV_TTY ? _S_IFCHR : _S_IFIFO;
1885    statbuf->st_nlink = 1;
1886    statbuf->st_rdev = (file_type == UV_TTY ? FILE_DEVICE_CONSOLE : FILE_DEVICE_NAMED_PIPE) << 16;
1887    statbuf->st_ino = (uintptr_t) handle;
1888    return 0;
1889
1890  /* If file type is unknown it is an error. */
1891  case UV_UNKNOWN_HANDLE:
1892  default:
1893    SetLastError(ERROR_INVALID_HANDLE);
1894    return -1;
1895  }
1896}
1897
1898
1899static void fs__stat(uv_fs_t* req) {
1900  fs__stat_prepare_path(req->file.pathw);
1901  fs__stat_impl(req, 0);
1902}
1903
1904
1905static void fs__lstat(uv_fs_t* req) {
1906  fs__stat_prepare_path(req->file.pathw);
1907  fs__stat_impl(req, 1);
1908}
1909
1910
1911static void fs__fstat(uv_fs_t* req) {
1912  int fd = req->file.fd;
1913  HANDLE handle;
1914
1915  VERIFY_FD(fd, req);
1916
1917  handle = uv__get_osfhandle(fd);
1918
1919  if (handle == INVALID_HANDLE_VALUE) {
1920    SET_REQ_WIN32_ERROR(req, ERROR_INVALID_HANDLE);
1921    return;
1922  }
1923
1924  if (fs__fstat_handle(fd, handle, &req->statbuf) != 0) {
1925    SET_REQ_WIN32_ERROR(req, GetLastError());
1926    return;
1927  }
1928
1929  req->ptr = &req->statbuf;
1930  SET_REQ_RESULT(req, 0);
1931}
1932
1933
1934static void fs__rename(uv_fs_t* req) {
1935  if (!MoveFileExW(req->file.pathw, req->fs.info.new_pathw, MOVEFILE_REPLACE_EXISTING)) {
1936    SET_REQ_WIN32_ERROR(req, GetLastError());
1937    return;
1938  }
1939
1940  SET_REQ_RESULT(req, 0);
1941}
1942
1943
1944INLINE static void fs__sync_impl(uv_fs_t* req) {
1945  int fd = req->file.fd;
1946  int result;
1947
1948  VERIFY_FD(fd, req);
1949
1950  result = FlushFileBuffers(uv__get_osfhandle(fd)) ? 0 : -1;
1951  if (result == -1) {
1952    SET_REQ_WIN32_ERROR(req, GetLastError());
1953  } else {
1954    SET_REQ_RESULT(req, result);
1955  }
1956}
1957
1958
1959static void fs__fsync(uv_fs_t* req) {
1960  fs__sync_impl(req);
1961}
1962
1963
1964static void fs__fdatasync(uv_fs_t* req) {
1965  fs__sync_impl(req);
1966}
1967
1968
1969static void fs__ftruncate(uv_fs_t* req) {
1970  int fd = req->file.fd;
1971  HANDLE handle;
1972  struct uv__fd_info_s fd_info = { 0 };
1973  NTSTATUS status;
1974  IO_STATUS_BLOCK io_status;
1975  FILE_END_OF_FILE_INFORMATION eof_info;
1976
1977  VERIFY_FD(fd, req);
1978
1979  handle = uv__get_osfhandle(fd);
1980
1981  if (uv__fd_hash_get(fd, &fd_info)) {
1982    if (fd_info.is_directory) {
1983      SET_REQ_WIN32_ERROR(req, ERROR_ACCESS_DENIED);
1984      return;
1985    }
1986
1987    if (fd_info.mapping != INVALID_HANDLE_VALUE) {
1988      CloseHandle(fd_info.mapping);
1989    }
1990  }
1991
1992  eof_info.EndOfFile.QuadPart = req->fs.info.offset;
1993
1994  status = pNtSetInformationFile(handle,
1995                                 &io_status,
1996                                 &eof_info,
1997                                 sizeof eof_info,
1998                                 FileEndOfFileInformation);
1999
2000  if (NT_SUCCESS(status)) {
2001    SET_REQ_RESULT(req, 0);
2002  } else {
2003    SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(status));
2004
2005    if (fd_info.flags) {
2006      CloseHandle(handle);
2007      fd_info.mapping = INVALID_HANDLE_VALUE;
2008      fd_info.size.QuadPart = 0;
2009      fd_info.current_pos.QuadPart = 0;
2010      uv__fd_hash_add(fd, &fd_info);
2011      return;
2012    }
2013  }
2014
2015  if (fd_info.flags) {
2016    fd_info.size = eof_info.EndOfFile;
2017
2018    if (fd_info.size.QuadPart == 0) {
2019      fd_info.mapping = INVALID_HANDLE_VALUE;
2020    } else {
2021      DWORD flProtect = (fd_info.flags & (UV_FS_O_RDONLY | UV_FS_O_WRONLY |
2022        UV_FS_O_RDWR)) == UV_FS_O_RDONLY ? PAGE_READONLY : PAGE_READWRITE;
2023      fd_info.mapping = CreateFileMapping(handle,
2024                                          NULL,
2025                                          flProtect,
2026                                          fd_info.size.HighPart,
2027                                          fd_info.size.LowPart,
2028                                          NULL);
2029      if (fd_info.mapping == NULL) {
2030        SET_REQ_WIN32_ERROR(req, GetLastError());
2031        CloseHandle(handle);
2032        fd_info.mapping = INVALID_HANDLE_VALUE;
2033        fd_info.size.QuadPart = 0;
2034        fd_info.current_pos.QuadPart = 0;
2035        uv__fd_hash_add(fd, &fd_info);
2036        return;
2037      }
2038    }
2039
2040    uv__fd_hash_add(fd, &fd_info);
2041  }
2042}
2043
2044
2045static void fs__copyfile(uv_fs_t* req) {
2046  int flags;
2047  int overwrite;
2048  uv_stat_t statbuf;
2049  uv_stat_t new_statbuf;
2050
2051  flags = req->fs.info.file_flags;
2052
2053  if (flags & UV_FS_COPYFILE_FICLONE_FORCE) {
2054    SET_REQ_UV_ERROR(req, UV_ENOSYS, ERROR_NOT_SUPPORTED);
2055    return;
2056  }
2057
2058  overwrite = flags & UV_FS_COPYFILE_EXCL;
2059
2060  if (CopyFileW(req->file.pathw, req->fs.info.new_pathw, overwrite) != 0) {
2061    SET_REQ_RESULT(req, 0);
2062    return;
2063  }
2064
2065  SET_REQ_WIN32_ERROR(req, GetLastError());
2066  if (req->result != UV_EBUSY)
2067    return;
2068
2069  /* if error UV_EBUSY check if src and dst file are the same */
2070  if (fs__stat_impl_from_path(req->file.pathw, 0, &statbuf) != 0 ||
2071      fs__stat_impl_from_path(req->fs.info.new_pathw, 0, &new_statbuf) != 0) {
2072    return;
2073  }
2074
2075  if (statbuf.st_dev == new_statbuf.st_dev &&
2076      statbuf.st_ino == new_statbuf.st_ino) {
2077    SET_REQ_RESULT(req, 0);
2078  }
2079}
2080
2081
2082static void fs__sendfile(uv_fs_t* req) {
2083  int fd_in = req->file.fd, fd_out = req->fs.info.fd_out;
2084  size_t length = req->fs.info.bufsml[0].len;
2085  int64_t offset = req->fs.info.offset;
2086  const size_t max_buf_size = 65536;
2087  size_t buf_size = length < max_buf_size ? length : max_buf_size;
2088  int n, result = 0;
2089  int64_t result_offset = 0;
2090  char* buf = (char*) uv__malloc(buf_size);
2091  if (!buf) {
2092    uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
2093  }
2094
2095  if (offset != -1) {
2096    result_offset = _lseeki64(fd_in, offset, SEEK_SET);
2097  }
2098
2099  if (result_offset == -1) {
2100    result = -1;
2101  } else {
2102    while (length > 0) {
2103      n = _read(fd_in, buf, length < buf_size ? length : buf_size);
2104      if (n == 0) {
2105        break;
2106      } else if (n == -1) {
2107        result = -1;
2108        break;
2109      }
2110
2111      length -= n;
2112
2113      n = _write(fd_out, buf, n);
2114      if (n == -1) {
2115        result = -1;
2116        break;
2117      }
2118
2119      result += n;
2120    }
2121  }
2122
2123  uv__free(buf);
2124
2125  SET_REQ_RESULT(req, result);
2126}
2127
2128
2129static void fs__access(uv_fs_t* req) {
2130  DWORD attr = GetFileAttributesW(req->file.pathw);
2131
2132  if (attr == INVALID_FILE_ATTRIBUTES) {
2133    SET_REQ_WIN32_ERROR(req, GetLastError());
2134    return;
2135  }
2136
2137  /*
2138   * Access is possible if
2139   * - write access wasn't requested,
2140   * - or the file isn't read-only,
2141   * - or it's a directory.
2142   * (Directories cannot be read-only on Windows.)
2143   */
2144  if (!(req->fs.info.mode & W_OK) ||
2145      !(attr & FILE_ATTRIBUTE_READONLY) ||
2146      (attr & FILE_ATTRIBUTE_DIRECTORY)) {
2147    SET_REQ_RESULT(req, 0);
2148  } else {
2149    SET_REQ_WIN32_ERROR(req, UV_EPERM);
2150  }
2151
2152}
2153
2154
2155static void fs__chmod(uv_fs_t* req) {
2156  int result = _wchmod(req->file.pathw, req->fs.info.mode);
2157  if (result == -1)
2158    SET_REQ_WIN32_ERROR(req, _doserrno);
2159  else
2160    SET_REQ_RESULT(req, 0);
2161}
2162
2163
2164static void fs__fchmod(uv_fs_t* req) {
2165  int fd = req->file.fd;
2166  int clear_archive_flag;
2167  HANDLE handle;
2168  NTSTATUS nt_status;
2169  IO_STATUS_BLOCK io_status;
2170  FILE_BASIC_INFORMATION file_info;
2171
2172  VERIFY_FD(fd, req);
2173
2174  handle = ReOpenFile(uv__get_osfhandle(fd), FILE_WRITE_ATTRIBUTES, 0, 0);
2175  if (handle == INVALID_HANDLE_VALUE) {
2176    SET_REQ_WIN32_ERROR(req, GetLastError());
2177    return;
2178  }
2179
2180  nt_status = pNtQueryInformationFile(handle,
2181                                      &io_status,
2182                                      &file_info,
2183                                      sizeof file_info,
2184                                      FileBasicInformation);
2185
2186  if (!NT_SUCCESS(nt_status)) {
2187    SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(nt_status));
2188    goto fchmod_cleanup;
2189  }
2190
2191  /* Test if the Archive attribute is cleared */
2192  if ((file_info.FileAttributes & FILE_ATTRIBUTE_ARCHIVE) == 0) {
2193      /* Set Archive flag, otherwise setting or clearing the read-only
2194         flag will not work */
2195      file_info.FileAttributes |= FILE_ATTRIBUTE_ARCHIVE;
2196      nt_status = pNtSetInformationFile(handle,
2197                                        &io_status,
2198                                        &file_info,
2199                                        sizeof file_info,
2200                                        FileBasicInformation);
2201      if (!NT_SUCCESS(nt_status)) {
2202        SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(nt_status));
2203        goto fchmod_cleanup;
2204      }
2205      /* Remember to clear the flag later on */
2206      clear_archive_flag = 1;
2207  } else {
2208      clear_archive_flag = 0;
2209  }
2210
2211  if (req->fs.info.mode & _S_IWRITE) {
2212    file_info.FileAttributes &= ~FILE_ATTRIBUTE_READONLY;
2213  } else {
2214    file_info.FileAttributes |= FILE_ATTRIBUTE_READONLY;
2215  }
2216
2217  nt_status = pNtSetInformationFile(handle,
2218                                    &io_status,
2219                                    &file_info,
2220                                    sizeof file_info,
2221                                    FileBasicInformation);
2222
2223  if (!NT_SUCCESS(nt_status)) {
2224    SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(nt_status));
2225    goto fchmod_cleanup;
2226  }
2227
2228  if (clear_archive_flag) {
2229      file_info.FileAttributes &= ~FILE_ATTRIBUTE_ARCHIVE;
2230      if (file_info.FileAttributes == 0) {
2231          file_info.FileAttributes = FILE_ATTRIBUTE_NORMAL;
2232      }
2233      nt_status = pNtSetInformationFile(handle,
2234                                        &io_status,
2235                                        &file_info,
2236                                        sizeof file_info,
2237                                        FileBasicInformation);
2238      if (!NT_SUCCESS(nt_status)) {
2239        SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(nt_status));
2240        goto fchmod_cleanup;
2241      }
2242  }
2243
2244  SET_REQ_SUCCESS(req);
2245fchmod_cleanup:
2246  CloseHandle(handle);
2247}
2248
2249
2250INLINE static int fs__utime_handle(HANDLE handle, double atime, double mtime) {
2251  FILETIME filetime_a, filetime_m;
2252
2253  TIME_T_TO_FILETIME(atime, &filetime_a);
2254  TIME_T_TO_FILETIME(mtime, &filetime_m);
2255
2256  if (!SetFileTime(handle, NULL, &filetime_a, &filetime_m)) {
2257    return -1;
2258  }
2259
2260  return 0;
2261}
2262
2263INLINE static DWORD fs__utime_impl_from_path(WCHAR* path,
2264                                             double atime,
2265                                             double mtime,
2266                                             int do_lutime) {
2267  HANDLE handle;
2268  DWORD flags;
2269  DWORD ret;
2270
2271  flags = FILE_FLAG_BACKUP_SEMANTICS;
2272  if (do_lutime) {
2273    flags |= FILE_FLAG_OPEN_REPARSE_POINT;
2274  }
2275
2276  handle = CreateFileW(path,
2277                       FILE_WRITE_ATTRIBUTES,
2278                       FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
2279                       NULL,
2280                       OPEN_EXISTING,
2281                       flags,
2282                       NULL);
2283
2284  if (handle == INVALID_HANDLE_VALUE)
2285    return GetLastError();
2286
2287  if (fs__utime_handle(handle, atime, mtime) != 0)
2288    ret = GetLastError();
2289  else
2290    ret = 0;
2291
2292  CloseHandle(handle);
2293  return ret;
2294}
2295
2296INLINE static void fs__utime_impl(uv_fs_t* req, int do_lutime) {
2297  DWORD error;
2298
2299  error = fs__utime_impl_from_path(req->file.pathw,
2300                                   req->fs.time.atime,
2301                                   req->fs.time.mtime,
2302                                   do_lutime);
2303
2304  if (error != 0) {
2305    if (do_lutime &&
2306        (error == ERROR_SYMLINK_NOT_SUPPORTED ||
2307         error == ERROR_NOT_A_REPARSE_POINT)) {
2308      /* Opened file is a reparse point but not a symlink. Try again. */
2309      fs__utime_impl(req, 0);
2310    } else {
2311      /* utime failed. */
2312      SET_REQ_WIN32_ERROR(req, error);
2313    }
2314
2315    return;
2316  }
2317
2318  SET_REQ_RESULT(req, 0);
2319}
2320
2321static void fs__utime(uv_fs_t* req) {
2322  fs__utime_impl(req, /* do_lutime */ 0);
2323}
2324
2325
2326static void fs__futime(uv_fs_t* req) {
2327  int fd = req->file.fd;
2328  HANDLE handle;
2329  VERIFY_FD(fd, req);
2330
2331  handle = uv__get_osfhandle(fd);
2332
2333  if (handle == INVALID_HANDLE_VALUE) {
2334    SET_REQ_WIN32_ERROR(req, ERROR_INVALID_HANDLE);
2335    return;
2336  }
2337
2338  if (fs__utime_handle(handle, req->fs.time.atime, req->fs.time.mtime) != 0) {
2339    SET_REQ_WIN32_ERROR(req, GetLastError());
2340    return;
2341  }
2342
2343  SET_REQ_RESULT(req, 0);
2344}
2345
2346static void fs__lutime(uv_fs_t* req) {
2347  fs__utime_impl(req, /* do_lutime */ 1);
2348}
2349
2350
2351static void fs__link(uv_fs_t* req) {
2352  DWORD r = CreateHardLinkW(req->fs.info.new_pathw, req->file.pathw, NULL);
2353  if (r == 0)
2354    SET_REQ_WIN32_ERROR(req, GetLastError());
2355  else
2356    SET_REQ_RESULT(req, 0);
2357}
2358
2359
2360static void fs__create_junction(uv_fs_t* req, const WCHAR* path,
2361    const WCHAR* new_path) {
2362  HANDLE handle = INVALID_HANDLE_VALUE;
2363  REPARSE_DATA_BUFFER *buffer = NULL;
2364  int created = 0;
2365  int target_len;
2366  int is_absolute, is_long_path;
2367  int needed_buf_size, used_buf_size, used_data_size, path_buf_len;
2368  int start, len, i;
2369  int add_slash;
2370  DWORD bytes;
2371  WCHAR* path_buf;
2372
2373  target_len = wcslen(path);
2374  is_long_path = wcsncmp(path, LONG_PATH_PREFIX, LONG_PATH_PREFIX_LEN) == 0;
2375
2376  if (is_long_path) {
2377    is_absolute = 1;
2378  } else {
2379    is_absolute = target_len >= 3 && IS_LETTER(path[0]) &&
2380      path[1] == L':' && IS_SLASH(path[2]);
2381  }
2382
2383  if (!is_absolute) {
2384    /* Not supporting relative paths */
2385    SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_NOT_SUPPORTED);
2386    return;
2387  }
2388
2389  /* Do a pessimistic calculation of the required buffer size */
2390  needed_buf_size =
2391      FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer) +
2392      JUNCTION_PREFIX_LEN * sizeof(WCHAR) +
2393      2 * (target_len + 2) * sizeof(WCHAR);
2394
2395  /* Allocate the buffer */
2396  buffer = (REPARSE_DATA_BUFFER*)uv__malloc(needed_buf_size);
2397  if (!buffer) {
2398    uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
2399  }
2400
2401  /* Grab a pointer to the part of the buffer where filenames go */
2402  path_buf = (WCHAR*)&(buffer->MountPointReparseBuffer.PathBuffer);
2403  path_buf_len = 0;
2404
2405  /* Copy the substitute (internal) target path */
2406  start = path_buf_len;
2407
2408  wcsncpy((WCHAR*)&path_buf[path_buf_len], JUNCTION_PREFIX,
2409    JUNCTION_PREFIX_LEN);
2410  path_buf_len += JUNCTION_PREFIX_LEN;
2411
2412  add_slash = 0;
2413  for (i = is_long_path ? LONG_PATH_PREFIX_LEN : 0; path[i] != L'\0'; i++) {
2414    if (IS_SLASH(path[i])) {
2415      add_slash = 1;
2416      continue;
2417    }
2418
2419    if (add_slash) {
2420      path_buf[path_buf_len++] = L'\\';
2421      add_slash = 0;
2422    }
2423
2424    path_buf[path_buf_len++] = path[i];
2425  }
2426  path_buf[path_buf_len++] = L'\\';
2427  len = path_buf_len - start;
2428
2429  /* Set the info about the substitute name */
2430  buffer->MountPointReparseBuffer.SubstituteNameOffset = start * sizeof(WCHAR);
2431  buffer->MountPointReparseBuffer.SubstituteNameLength = len * sizeof(WCHAR);
2432
2433  /* Insert null terminator */
2434  path_buf[path_buf_len++] = L'\0';
2435
2436  /* Copy the print name of the target path */
2437  start = path_buf_len;
2438  add_slash = 0;
2439  for (i = is_long_path ? LONG_PATH_PREFIX_LEN : 0; path[i] != L'\0'; i++) {
2440    if (IS_SLASH(path[i])) {
2441      add_slash = 1;
2442      continue;
2443    }
2444
2445    if (add_slash) {
2446      path_buf[path_buf_len++] = L'\\';
2447      add_slash = 0;
2448    }
2449
2450    path_buf[path_buf_len++] = path[i];
2451  }
2452  len = path_buf_len - start;
2453  if (len == 2) {
2454    path_buf[path_buf_len++] = L'\\';
2455    len++;
2456  }
2457
2458  /* Set the info about the print name */
2459  buffer->MountPointReparseBuffer.PrintNameOffset = start * sizeof(WCHAR);
2460  buffer->MountPointReparseBuffer.PrintNameLength = len * sizeof(WCHAR);
2461
2462  /* Insert another null terminator */
2463  path_buf[path_buf_len++] = L'\0';
2464
2465  /* Calculate how much buffer space was actually used */
2466  used_buf_size = FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer) +
2467    path_buf_len * sizeof(WCHAR);
2468  used_data_size = used_buf_size -
2469    FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer);
2470
2471  /* Put general info in the data buffer */
2472  buffer->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
2473  buffer->ReparseDataLength = used_data_size;
2474  buffer->Reserved = 0;
2475
2476  /* Create a new directory */
2477  if (!CreateDirectoryW(new_path, NULL)) {
2478    SET_REQ_WIN32_ERROR(req, GetLastError());
2479    goto error;
2480  }
2481  created = 1;
2482
2483  /* Open the directory */
2484  handle = CreateFileW(new_path,
2485                       GENERIC_WRITE,
2486                       0,
2487                       NULL,
2488                       OPEN_EXISTING,
2489                       FILE_FLAG_BACKUP_SEMANTICS |
2490                         FILE_FLAG_OPEN_REPARSE_POINT,
2491                       NULL);
2492  if (handle == INVALID_HANDLE_VALUE) {
2493    SET_REQ_WIN32_ERROR(req, GetLastError());
2494    goto error;
2495  }
2496
2497  /* Create the actual reparse point */
2498  if (!DeviceIoControl(handle,
2499                       FSCTL_SET_REPARSE_POINT,
2500                       buffer,
2501                       used_buf_size,
2502                       NULL,
2503                       0,
2504                       &bytes,
2505                       NULL)) {
2506    SET_REQ_WIN32_ERROR(req, GetLastError());
2507    goto error;
2508  }
2509
2510  /* Clean up */
2511  CloseHandle(handle);
2512  uv__free(buffer);
2513
2514  SET_REQ_RESULT(req, 0);
2515  return;
2516
2517error:
2518  uv__free(buffer);
2519
2520  if (handle != INVALID_HANDLE_VALUE) {
2521    CloseHandle(handle);
2522  }
2523
2524  if (created) {
2525    RemoveDirectoryW(new_path);
2526  }
2527}
2528
2529
2530static void fs__symlink(uv_fs_t* req) {
2531  WCHAR* pathw;
2532  WCHAR* new_pathw;
2533  int flags;
2534  int err;
2535
2536  pathw = req->file.pathw;
2537  new_pathw = req->fs.info.new_pathw;
2538
2539  if (req->fs.info.file_flags & UV_FS_SYMLINK_JUNCTION) {
2540    fs__create_junction(req, pathw, new_pathw);
2541    return;
2542  }
2543
2544  if (req->fs.info.file_flags & UV_FS_SYMLINK_DIR)
2545    flags = SYMBOLIC_LINK_FLAG_DIRECTORY | uv__file_symlink_usermode_flag;
2546  else
2547    flags = uv__file_symlink_usermode_flag;
2548
2549  if (CreateSymbolicLinkW(new_pathw, pathw, flags)) {
2550    SET_REQ_RESULT(req, 0);
2551    return;
2552  }
2553
2554  /* Something went wrong. We will test if it is because of user-mode
2555   * symlinks.
2556   */
2557  err = GetLastError();
2558  if (err == ERROR_INVALID_PARAMETER &&
2559      flags & SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE) {
2560    /* This system does not support user-mode symlinks. We will clear the
2561     * unsupported flag and retry.
2562     */
2563    uv__file_symlink_usermode_flag = 0;
2564    fs__symlink(req);
2565  } else {
2566    SET_REQ_WIN32_ERROR(req, err);
2567  }
2568}
2569
2570
2571static void fs__readlink(uv_fs_t* req) {
2572  HANDLE handle;
2573
2574  handle = CreateFileW(req->file.pathw,
2575                       0,
2576                       0,
2577                       NULL,
2578                       OPEN_EXISTING,
2579                       FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
2580                       NULL);
2581
2582  if (handle == INVALID_HANDLE_VALUE) {
2583    SET_REQ_WIN32_ERROR(req, GetLastError());
2584    return;
2585  }
2586
2587  assert(req->ptr == NULL);
2588  if (fs__readlink_handle(handle, (char**) &req->ptr, NULL) != 0) {
2589    DWORD error = GetLastError();
2590    SET_REQ_WIN32_ERROR(req, error);
2591    if (error == ERROR_NOT_A_REPARSE_POINT)
2592      req->result = UV_EINVAL;
2593    CloseHandle(handle);
2594    return;
2595  }
2596
2597  req->flags |= UV_FS_FREE_PTR;
2598  SET_REQ_RESULT(req, 0);
2599
2600  CloseHandle(handle);
2601}
2602
2603
2604static ssize_t fs__realpath_handle(HANDLE handle, char** realpath_ptr) {
2605  int r;
2606  DWORD w_realpath_len;
2607  WCHAR* w_realpath_ptr = NULL;
2608  WCHAR* w_realpath_buf;
2609
2610  w_realpath_len = GetFinalPathNameByHandleW(handle, NULL, 0, VOLUME_NAME_DOS);
2611  if (w_realpath_len == 0) {
2612    return -1;
2613  }
2614
2615  w_realpath_buf = uv__malloc((w_realpath_len + 1) * sizeof(WCHAR));
2616  if (w_realpath_buf == NULL) {
2617    SetLastError(ERROR_OUTOFMEMORY);
2618    return -1;
2619  }
2620  w_realpath_ptr = w_realpath_buf;
2621
2622  if (GetFinalPathNameByHandleW(
2623          handle, w_realpath_ptr, w_realpath_len, VOLUME_NAME_DOS) == 0) {
2624    uv__free(w_realpath_buf);
2625    SetLastError(ERROR_INVALID_HANDLE);
2626    return -1;
2627  }
2628
2629  /* convert UNC path to long path */
2630  if (wcsncmp(w_realpath_ptr,
2631              UNC_PATH_PREFIX,
2632              UNC_PATH_PREFIX_LEN) == 0) {
2633    w_realpath_ptr += 6;
2634    *w_realpath_ptr = L'\\';
2635    w_realpath_len -= 6;
2636  } else if (wcsncmp(w_realpath_ptr,
2637                      LONG_PATH_PREFIX,
2638                      LONG_PATH_PREFIX_LEN) == 0) {
2639    w_realpath_ptr += 4;
2640    w_realpath_len -= 4;
2641  } else {
2642    uv__free(w_realpath_buf);
2643    SetLastError(ERROR_INVALID_HANDLE);
2644    return -1;
2645  }
2646
2647  assert(*realpath_ptr == NULL);
2648  r = uv_utf16_to_wtf8(w_realpath_ptr, w_realpath_len, realpath_ptr, NULL);
2649  uv__free(w_realpath_buf);
2650  return r;
2651}
2652
2653static void fs__realpath(uv_fs_t* req) {
2654  HANDLE handle;
2655
2656  handle = CreateFileW(req->file.pathw,
2657                       0,
2658                       0,
2659                       NULL,
2660                       OPEN_EXISTING,
2661                       FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS,
2662                       NULL);
2663  if (handle == INVALID_HANDLE_VALUE) {
2664    SET_REQ_WIN32_ERROR(req, GetLastError());
2665    return;
2666  }
2667
2668  assert(req->ptr == NULL);
2669  if (fs__realpath_handle(handle, (char**) &req->ptr) == -1) {
2670    CloseHandle(handle);
2671    SET_REQ_WIN32_ERROR(req, GetLastError());
2672    return;
2673  }
2674
2675  CloseHandle(handle);
2676  req->flags |= UV_FS_FREE_PTR;
2677  SET_REQ_RESULT(req, 0);
2678}
2679
2680
2681static void fs__chown(uv_fs_t* req) {
2682  SET_REQ_RESULT(req, 0);
2683}
2684
2685
2686static void fs__fchown(uv_fs_t* req) {
2687  SET_REQ_RESULT(req, 0);
2688}
2689
2690
2691static void fs__lchown(uv_fs_t* req) {
2692  SET_REQ_RESULT(req, 0);
2693}
2694
2695
2696static void fs__statfs(uv_fs_t* req) {
2697  uv_statfs_t* stat_fs;
2698  DWORD sectors_per_cluster;
2699  DWORD bytes_per_sector;
2700  DWORD free_clusters;
2701  DWORD total_clusters;
2702  WCHAR* pathw;
2703
2704  pathw = req->file.pathw;
2705retry_get_disk_free_space:
2706  if (0 == GetDiskFreeSpaceW(pathw,
2707                             &sectors_per_cluster,
2708                             &bytes_per_sector,
2709                             &free_clusters,
2710                             &total_clusters)) {
2711    DWORD err;
2712    WCHAR* fpart;
2713    size_t len;
2714    DWORD ret;
2715    BOOL is_second;
2716
2717    err = GetLastError();
2718    is_second = pathw != req->file.pathw;
2719    if (err != ERROR_DIRECTORY || is_second) {
2720      if (is_second)
2721        uv__free(pathw);
2722
2723      SET_REQ_WIN32_ERROR(req, err);
2724      return;
2725    }
2726
2727    len = MAX_PATH + 1;
2728    pathw = uv__malloc(len * sizeof(*pathw));
2729    if (pathw == NULL) {
2730      SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
2731      return;
2732    }
2733retry_get_full_path_name:
2734    ret = GetFullPathNameW(req->file.pathw,
2735                           len,
2736                           pathw,
2737                           &fpart);
2738    if (ret == 0) {
2739      uv__free(pathw);
2740      SET_REQ_WIN32_ERROR(req, err);
2741      return;
2742    } else if (ret > len) {
2743      len = ret;
2744      pathw = uv__reallocf(pathw, len * sizeof(*pathw));
2745      if (pathw == NULL) {
2746        SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
2747        return;
2748      }
2749      goto retry_get_full_path_name;
2750    }
2751    if (fpart != 0)
2752      *fpart = L'\0';
2753
2754    goto retry_get_disk_free_space;
2755  }
2756  if (pathw != req->file.pathw) {
2757    uv__free(pathw);
2758  }
2759
2760  stat_fs = uv__malloc(sizeof(*stat_fs));
2761  if (stat_fs == NULL) {
2762    SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
2763    return;
2764  }
2765
2766  stat_fs->f_type = 0;
2767  stat_fs->f_bsize = bytes_per_sector * sectors_per_cluster;
2768  stat_fs->f_blocks = total_clusters;
2769  stat_fs->f_bfree = free_clusters;
2770  stat_fs->f_bavail = free_clusters;
2771  stat_fs->f_files = 0;
2772  stat_fs->f_ffree = 0;
2773  req->ptr = stat_fs;
2774  req->flags |= UV_FS_FREE_PTR;
2775  SET_REQ_RESULT(req, 0);
2776}
2777
2778
2779static void uv__fs_work(struct uv__work* w) {
2780  uv_fs_t* req;
2781
2782  req = container_of(w, uv_fs_t, work_req);
2783  assert(req->type == UV_FS);
2784
2785#define XX(uc, lc)  case UV_FS_##uc: fs__##lc(req); break;
2786  switch (req->fs_type) {
2787    XX(OPEN, open)
2788    XX(CLOSE, close)
2789    XX(READ, read)
2790    XX(WRITE, write)
2791    XX(COPYFILE, copyfile)
2792    XX(SENDFILE, sendfile)
2793    XX(STAT, stat)
2794    XX(LSTAT, lstat)
2795    XX(FSTAT, fstat)
2796    XX(FTRUNCATE, ftruncate)
2797    XX(UTIME, utime)
2798    XX(FUTIME, futime)
2799    XX(LUTIME, lutime)
2800    XX(ACCESS, access)
2801    XX(CHMOD, chmod)
2802    XX(FCHMOD, fchmod)
2803    XX(FSYNC, fsync)
2804    XX(FDATASYNC, fdatasync)
2805    XX(UNLINK, unlink)
2806    XX(RMDIR, rmdir)
2807    XX(MKDIR, mkdir)
2808    XX(MKDTEMP, mkdtemp)
2809    XX(MKSTEMP, mkstemp)
2810    XX(RENAME, rename)
2811    XX(SCANDIR, scandir)
2812    XX(READDIR, readdir)
2813    XX(OPENDIR, opendir)
2814    XX(CLOSEDIR, closedir)
2815    XX(LINK, link)
2816    XX(SYMLINK, symlink)
2817    XX(READLINK, readlink)
2818    XX(REALPATH, realpath)
2819    XX(CHOWN, chown)
2820    XX(FCHOWN, fchown)
2821    XX(LCHOWN, lchown)
2822    XX(STATFS, statfs)
2823    default:
2824      assert(!"bad uv_fs_type");
2825  }
2826}
2827
2828
2829static void uv__fs_done(struct uv__work* w, int status) {
2830  uv_fs_t* req;
2831
2832  req = container_of(w, uv_fs_t, work_req);
2833  uv__req_unregister(req->loop, req);
2834
2835  if (status == UV_ECANCELED) {
2836    assert(req->result == 0);
2837    SET_REQ_UV_ERROR(req, UV_ECANCELED, 0);
2838  }
2839
2840  req->cb(req);
2841}
2842
2843
2844void uv_fs_req_cleanup(uv_fs_t* req) {
2845  if (req == NULL)
2846    return;
2847
2848  if (req->flags & UV_FS_CLEANEDUP)
2849    return;
2850
2851  if (req->flags & UV_FS_FREE_PATHS)
2852    uv__free(req->file.pathw);
2853
2854  if (req->flags & UV_FS_FREE_PTR) {
2855    if (req->fs_type == UV_FS_SCANDIR && req->ptr != NULL)
2856      uv__fs_scandir_cleanup(req);
2857    else if (req->fs_type == UV_FS_READDIR)
2858      uv__fs_readdir_cleanup(req);
2859    else
2860      uv__free(req->ptr);
2861  }
2862
2863  if (req->fs.info.bufs != req->fs.info.bufsml)
2864    uv__free(req->fs.info.bufs);
2865
2866  req->path = NULL;
2867  req->file.pathw = NULL;
2868  req->fs.info.new_pathw = NULL;
2869  req->fs.info.bufs = NULL;
2870  req->ptr = NULL;
2871
2872  req->flags |= UV_FS_CLEANEDUP;
2873}
2874
2875
2876int uv_fs_open(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags,
2877    int mode, uv_fs_cb cb) {
2878  int err;
2879
2880  INIT(UV_FS_OPEN);
2881  err = fs__capture_path(req, path, NULL, cb != NULL);
2882  if (err) {
2883    SET_REQ_WIN32_ERROR(req, err);
2884    return req->result;
2885  }
2886
2887  req->fs.info.file_flags = flags;
2888  req->fs.info.mode = mode;
2889  POST;
2890}
2891
2892
2893int uv_fs_close(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) {
2894  INIT(UV_FS_CLOSE);
2895  req->file.fd = fd;
2896  POST;
2897}
2898
2899
2900int uv_fs_read(uv_loop_t* loop,
2901               uv_fs_t* req,
2902               uv_file fd,
2903               const uv_buf_t bufs[],
2904               unsigned int nbufs,
2905               int64_t offset,
2906               uv_fs_cb cb) {
2907  INIT(UV_FS_READ);
2908
2909  if (bufs == NULL || nbufs == 0) {
2910    SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER);
2911    return UV_EINVAL;
2912  }
2913
2914  req->file.fd = fd;
2915
2916  req->fs.info.nbufs = nbufs;
2917  req->fs.info.bufs = req->fs.info.bufsml;
2918  if (nbufs > ARRAY_SIZE(req->fs.info.bufsml))
2919    req->fs.info.bufs = uv__malloc(nbufs * sizeof(*bufs));
2920
2921  if (req->fs.info.bufs == NULL) {
2922    SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
2923    return UV_ENOMEM;
2924  }
2925
2926  memcpy(req->fs.info.bufs, bufs, nbufs * sizeof(*bufs));
2927
2928  req->fs.info.offset = offset;
2929  POST;
2930}
2931
2932
2933int uv_fs_write(uv_loop_t* loop,
2934                uv_fs_t* req,
2935                uv_file fd,
2936                const uv_buf_t bufs[],
2937                unsigned int nbufs,
2938                int64_t offset,
2939                uv_fs_cb cb) {
2940  INIT(UV_FS_WRITE);
2941
2942  if (bufs == NULL || nbufs == 0) {
2943    SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER);
2944    return UV_EINVAL;
2945  }
2946
2947  req->file.fd = fd;
2948
2949  req->fs.info.nbufs = nbufs;
2950  req->fs.info.bufs = req->fs.info.bufsml;
2951  if (nbufs > ARRAY_SIZE(req->fs.info.bufsml))
2952    req->fs.info.bufs = uv__malloc(nbufs * sizeof(*bufs));
2953
2954  if (req->fs.info.bufs == NULL) {
2955    SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
2956    return UV_ENOMEM;
2957  }
2958
2959  memcpy(req->fs.info.bufs, bufs, nbufs * sizeof(*bufs));
2960
2961  req->fs.info.offset = offset;
2962  POST;
2963}
2964
2965
2966int uv_fs_unlink(uv_loop_t* loop, uv_fs_t* req, const char* path,
2967    uv_fs_cb cb) {
2968  int err;
2969
2970  INIT(UV_FS_UNLINK);
2971  err = fs__capture_path(req, path, NULL, cb != NULL);
2972  if (err) {
2973    SET_REQ_WIN32_ERROR(req, err);
2974    return req->result;
2975  }
2976
2977  POST;
2978}
2979
2980
2981int uv_fs_mkdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode,
2982    uv_fs_cb cb) {
2983  int err;
2984
2985  INIT(UV_FS_MKDIR);
2986  err = fs__capture_path(req, path, NULL, cb != NULL);
2987  if (err) {
2988    SET_REQ_WIN32_ERROR(req, err);
2989    return req->result;
2990  }
2991
2992  req->fs.info.mode = mode;
2993  POST;
2994}
2995
2996
2997int uv_fs_mkdtemp(uv_loop_t* loop,
2998                  uv_fs_t* req,
2999                  const char* tpl,
3000                  uv_fs_cb cb) {
3001  int err;
3002
3003  INIT(UV_FS_MKDTEMP);
3004  err = fs__capture_path(req, tpl, NULL, TRUE);
3005  if (err) {
3006    SET_REQ_WIN32_ERROR(req, err);
3007    return req->result;
3008  }
3009
3010  POST;
3011}
3012
3013
3014int uv_fs_mkstemp(uv_loop_t* loop,
3015                  uv_fs_t* req,
3016                  const char* tpl,
3017                  uv_fs_cb cb) {
3018  int err;
3019
3020  INIT(UV_FS_MKSTEMP);
3021  err = fs__capture_path(req, tpl, NULL, TRUE);
3022  if (err) {
3023    SET_REQ_WIN32_ERROR(req, err);
3024    return req->result;
3025  }
3026
3027  POST;
3028}
3029
3030
3031int uv_fs_rmdir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) {
3032  int err;
3033
3034  INIT(UV_FS_RMDIR);
3035  err = fs__capture_path(req, path, NULL, cb != NULL);
3036  if (err) {
3037    SET_REQ_WIN32_ERROR(req, err);
3038    return req->result;
3039  }
3040
3041  POST;
3042}
3043
3044
3045int uv_fs_scandir(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags,
3046    uv_fs_cb cb) {
3047  int err;
3048
3049  INIT(UV_FS_SCANDIR);
3050  err = fs__capture_path(req, path, NULL, cb != NULL);
3051  if (err) {
3052    SET_REQ_WIN32_ERROR(req, err);
3053    return req->result;
3054  }
3055
3056  req->fs.info.file_flags = flags;
3057  POST;
3058}
3059
3060int uv_fs_opendir(uv_loop_t* loop,
3061                  uv_fs_t* req,
3062                  const char* path,
3063                  uv_fs_cb cb) {
3064  int err;
3065
3066  INIT(UV_FS_OPENDIR);
3067  err = fs__capture_path(req, path, NULL, cb != NULL);
3068  if (err) {
3069    SET_REQ_WIN32_ERROR(req, err);
3070    return req->result;
3071  }
3072  POST;
3073}
3074
3075int uv_fs_readdir(uv_loop_t* loop,
3076                  uv_fs_t* req,
3077                  uv_dir_t* dir,
3078                  uv_fs_cb cb) {
3079  INIT(UV_FS_READDIR);
3080
3081  if (dir == NULL ||
3082      dir->dirents == NULL ||
3083      dir->dir_handle == INVALID_HANDLE_VALUE) {
3084    SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER);
3085    return UV_EINVAL;
3086  }
3087
3088  req->ptr = dir;
3089  POST;
3090}
3091
3092int uv_fs_closedir(uv_loop_t* loop,
3093                   uv_fs_t* req,
3094                   uv_dir_t* dir,
3095                   uv_fs_cb cb) {
3096  INIT(UV_FS_CLOSEDIR);
3097  if (dir == NULL) {
3098    SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER);
3099    return UV_EINVAL;
3100  }
3101  req->ptr = dir;
3102  POST;
3103}
3104
3105int uv_fs_link(uv_loop_t* loop, uv_fs_t* req, const char* path,
3106    const char* new_path, uv_fs_cb cb) {
3107  int err;
3108
3109  INIT(UV_FS_LINK);
3110  err = fs__capture_path(req, path, new_path, cb != NULL);
3111  if (err) {
3112    SET_REQ_WIN32_ERROR(req, err);
3113    return req->result;
3114  }
3115
3116  POST;
3117}
3118
3119
3120int uv_fs_symlink(uv_loop_t* loop, uv_fs_t* req, const char* path,
3121    const char* new_path, int flags, uv_fs_cb cb) {
3122  int err;
3123
3124  INIT(UV_FS_SYMLINK);
3125  err = fs__capture_path(req, path, new_path, cb != NULL);
3126  if (err) {
3127    SET_REQ_WIN32_ERROR(req, err);
3128    return req->result;
3129  }
3130
3131  req->fs.info.file_flags = flags;
3132  POST;
3133}
3134
3135
3136int uv_fs_readlink(uv_loop_t* loop, uv_fs_t* req, const char* path,
3137    uv_fs_cb cb) {
3138  int err;
3139
3140  INIT(UV_FS_READLINK);
3141  err = fs__capture_path(req, path, NULL, cb != NULL);
3142  if (err) {
3143    SET_REQ_WIN32_ERROR(req, err);
3144    return req->result;
3145  }
3146
3147  POST;
3148}
3149
3150
3151int uv_fs_realpath(uv_loop_t* loop, uv_fs_t* req, const char* path,
3152    uv_fs_cb cb) {
3153  int err;
3154
3155  INIT(UV_FS_REALPATH);
3156
3157  if (!path) {
3158    SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER);
3159    return UV_EINVAL;
3160  }
3161
3162  err = fs__capture_path(req, path, NULL, cb != NULL);
3163  if (err) {
3164    SET_REQ_WIN32_ERROR(req, err);
3165    return req->result;
3166  }
3167
3168  POST;
3169}
3170
3171
3172int uv_fs_chown(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid,
3173    uv_gid_t gid, uv_fs_cb cb) {
3174  int err;
3175
3176  INIT(UV_FS_CHOWN);
3177  err = fs__capture_path(req, path, NULL, cb != NULL);
3178  if (err) {
3179    SET_REQ_WIN32_ERROR(req, err);
3180    return req->result;
3181  }
3182
3183  POST;
3184}
3185
3186
3187int uv_fs_fchown(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_uid_t uid,
3188    uv_gid_t gid, uv_fs_cb cb) {
3189  INIT(UV_FS_FCHOWN);
3190  POST;
3191}
3192
3193
3194int uv_fs_lchown(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid,
3195    uv_gid_t gid, uv_fs_cb cb) {
3196  int err;
3197
3198  INIT(UV_FS_LCHOWN);
3199  err = fs__capture_path(req, path, NULL, cb != NULL);
3200  if (err) {
3201    SET_REQ_WIN32_ERROR(req, err);
3202    return req->result;
3203  }
3204
3205  POST;
3206}
3207
3208
3209int uv_fs_stat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) {
3210  int err;
3211
3212  INIT(UV_FS_STAT);
3213  err = fs__capture_path(req, path, NULL, cb != NULL);
3214  if (err) {
3215    SET_REQ_WIN32_ERROR(req, err);
3216    return req->result;
3217  }
3218
3219  POST;
3220}
3221
3222
3223int uv_fs_lstat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) {
3224  int err;
3225
3226  INIT(UV_FS_LSTAT);
3227  err = fs__capture_path(req, path, NULL, cb != NULL);
3228  if (err) {
3229    SET_REQ_WIN32_ERROR(req, err);
3230    return req->result;
3231  }
3232
3233  POST;
3234}
3235
3236
3237int uv_fs_fstat(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) {
3238  INIT(UV_FS_FSTAT);
3239  req->file.fd = fd;
3240  POST;
3241}
3242
3243
3244int uv_fs_rename(uv_loop_t* loop, uv_fs_t* req, const char* path,
3245    const char* new_path, uv_fs_cb cb) {
3246  int err;
3247
3248  INIT(UV_FS_RENAME);
3249  err = fs__capture_path(req, path, new_path, cb != NULL);
3250  if (err) {
3251    SET_REQ_WIN32_ERROR(req, err);
3252    return req->result;
3253  }
3254
3255  POST;
3256}
3257
3258
3259int uv_fs_fsync(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) {
3260  INIT(UV_FS_FSYNC);
3261  req->file.fd = fd;
3262  POST;
3263}
3264
3265
3266int uv_fs_fdatasync(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) {
3267  INIT(UV_FS_FDATASYNC);
3268  req->file.fd = fd;
3269  POST;
3270}
3271
3272
3273int uv_fs_ftruncate(uv_loop_t* loop, uv_fs_t* req, uv_file fd,
3274    int64_t offset, uv_fs_cb cb) {
3275  INIT(UV_FS_FTRUNCATE);
3276  req->file.fd = fd;
3277  req->fs.info.offset = offset;
3278  POST;
3279}
3280
3281
3282int uv_fs_copyfile(uv_loop_t* loop,
3283                   uv_fs_t* req,
3284                   const char* path,
3285                   const char* new_path,
3286                   int flags,
3287                   uv_fs_cb cb) {
3288  int err;
3289
3290  INIT(UV_FS_COPYFILE);
3291
3292  if (flags & ~(UV_FS_COPYFILE_EXCL |
3293                UV_FS_COPYFILE_FICLONE |
3294                UV_FS_COPYFILE_FICLONE_FORCE)) {
3295    SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER);
3296    return UV_EINVAL;
3297  }
3298
3299  err = fs__capture_path(req, path, new_path, cb != NULL);
3300  if (err) {
3301    SET_REQ_WIN32_ERROR(req, err);
3302    return req->result;
3303  }
3304
3305  req->fs.info.file_flags = flags;
3306  POST;
3307}
3308
3309
3310int uv_fs_sendfile(uv_loop_t* loop, uv_fs_t* req, uv_file fd_out,
3311    uv_file fd_in, int64_t in_offset, size_t length, uv_fs_cb cb) {
3312  INIT(UV_FS_SENDFILE);
3313  req->file.fd = fd_in;
3314  req->fs.info.fd_out = fd_out;
3315  req->fs.info.offset = in_offset;
3316  req->fs.info.bufsml[0].len = length;
3317  POST;
3318}
3319
3320
3321int uv_fs_access(uv_loop_t* loop,
3322                 uv_fs_t* req,
3323                 const char* path,
3324                 int flags,
3325                 uv_fs_cb cb) {
3326  int err;
3327
3328  INIT(UV_FS_ACCESS);
3329  err = fs__capture_path(req, path, NULL, cb != NULL);
3330  if (err) {
3331    SET_REQ_WIN32_ERROR(req, err);
3332    return req->result;
3333  }
3334
3335  req->fs.info.mode = flags;
3336  POST;
3337}
3338
3339
3340int uv_fs_chmod(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode,
3341    uv_fs_cb cb) {
3342  int err;
3343
3344  INIT(UV_FS_CHMOD);
3345  err = fs__capture_path(req, path, NULL, cb != NULL);
3346  if (err) {
3347    SET_REQ_WIN32_ERROR(req, err);
3348    return req->result;
3349  }
3350
3351  req->fs.info.mode = mode;
3352  POST;
3353}
3354
3355
3356int uv_fs_fchmod(uv_loop_t* loop, uv_fs_t* req, uv_file fd, int mode,
3357    uv_fs_cb cb) {
3358  INIT(UV_FS_FCHMOD);
3359  req->file.fd = fd;
3360  req->fs.info.mode = mode;
3361  POST;
3362}
3363
3364
3365int uv_fs_utime(uv_loop_t* loop, uv_fs_t* req, const char* path, double atime,
3366    double mtime, uv_fs_cb cb) {
3367  int err;
3368
3369  INIT(UV_FS_UTIME);
3370  err = fs__capture_path(req, path, NULL, cb != NULL);
3371  if (err) {
3372    SET_REQ_WIN32_ERROR(req, err);
3373    return req->result;
3374  }
3375
3376  req->fs.time.atime = atime;
3377  req->fs.time.mtime = mtime;
3378  POST;
3379}
3380
3381
3382int uv_fs_futime(uv_loop_t* loop, uv_fs_t* req, uv_file fd, double atime,
3383    double mtime, uv_fs_cb cb) {
3384  INIT(UV_FS_FUTIME);
3385  req->file.fd = fd;
3386  req->fs.time.atime = atime;
3387  req->fs.time.mtime = mtime;
3388  POST;
3389}
3390
3391int uv_fs_lutime(uv_loop_t* loop, uv_fs_t* req, const char* path, double atime,
3392    double mtime, uv_fs_cb cb) {
3393  int err;
3394
3395  INIT(UV_FS_LUTIME);
3396  err = fs__capture_path(req, path, NULL, cb != NULL);
3397  if (err) {
3398    SET_REQ_WIN32_ERROR(req, err);
3399    return req->result;
3400  }
3401
3402  req->fs.time.atime = atime;
3403  req->fs.time.mtime = mtime;
3404  POST;
3405}
3406
3407
3408int uv_fs_statfs(uv_loop_t* loop,
3409                 uv_fs_t* req,
3410                 const char* path,
3411                 uv_fs_cb cb) {
3412  int err;
3413
3414  INIT(UV_FS_STATFS);
3415  err = fs__capture_path(req, path, NULL, cb != NULL);
3416  if (err) {
3417    SET_REQ_WIN32_ERROR(req, err);
3418    return req->result;
3419  }
3420
3421  POST;
3422}
3423
3424int uv_fs_get_system_error(const uv_fs_t* req) {
3425  return req->sys_errno_;
3426}
3427