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