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