xref: /libuv/src/win/fs.c (revision 058c49b7)
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_UV_ERROR(req, uv_translate_write_sys_error(error), 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      * TODO: change it to UV_NOTDIR in v2. */
1118     SET_REQ_UV_ERROR(req, UV_ENOENT, ERROR_DIRECTORY);
1119     CloseHandle(handle);
1120     return;
1121   }
1122 
1123   if (!isrmdir && (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
1124     /* If not explicitly allowed, do not allow deletion of directories, unless
1125      * it is a symlink. When the path refers to a non-symlink directory, report
1126      * EPERM as mandated by POSIX.1. */
1127 
1128     /* Check if it is a reparse point. If it's not, it's a normal directory. */
1129     if (!(info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
1130       SET_REQ_WIN32_ERROR(req, ERROR_ACCESS_DENIED);
1131       CloseHandle(handle);
1132       return;
1133     }
1134 
1135     /* Read the reparse point and check if it is a valid symlink. If not, don't
1136      * unlink. */
1137     if (fs__readlink_handle(handle, NULL, NULL) < 0) {
1138       error = GetLastError();
1139       if (error == ERROR_SYMLINK_NOT_SUPPORTED)
1140         error = ERROR_ACCESS_DENIED;
1141       SET_REQ_WIN32_ERROR(req, error);
1142       CloseHandle(handle);
1143       return;
1144     }
1145   }
1146 
1147   /* Try posix delete first */
1148   disposition_ex.Flags = FILE_DISPOSITION_DELETE | FILE_DISPOSITION_POSIX_SEMANTICS |
1149                           FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE;
1150 
1151   status = pNtSetInformationFile(handle,
1152                                  &iosb,
1153                                  &disposition_ex,
1154                                  sizeof disposition_ex,
1155                                  FileDispositionInformationEx);
1156   if (NT_SUCCESS(status)) {
1157     SET_REQ_SUCCESS(req);
1158   } else {
1159     /* If status == STATUS_CANNOT_DELETE here, given we set
1160      * FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE, STATUS_CANNOT_DELETE can only mean
1161      * that there is an existing mapped view to the file, preventing delete.
1162      * STATUS_CANNOT_DELETE maps to UV_EACCES so it's not specifically worth handling  */
1163     error = pRtlNtStatusToDosError(status);
1164     if (error == ERROR_NOT_SUPPORTED /* filesystem does not support posix deletion */ ||
1165         error == ERROR_INVALID_PARAMETER /* pre Windows 10 error */ ||
1166         error == ERROR_INVALID_FUNCTION /* pre Windows 10 1607 error */) {
1167       /* posix delete not supported so try fallback */
1168       if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
1169         /* Remove read-only attribute */
1170         FILE_BASIC_INFORMATION basic = { 0 };
1171 
1172         basic.FileAttributes = (info.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY) |
1173                               FILE_ATTRIBUTE_ARCHIVE;
1174 
1175         status = pNtSetInformationFile(handle,
1176                                       &iosb,
1177                                       &basic,
1178                                       sizeof basic,
1179                                       FileBasicInformation);
1180         if (!NT_SUCCESS(status)) {
1181           SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(status));
1182           CloseHandle(handle);
1183           return;
1184         }
1185       }
1186 
1187       /* Try to set the delete flag. */
1188       disposition.DeleteFile = TRUE;
1189       status = pNtSetInformationFile(handle,
1190                                     &iosb,
1191                                     &disposition,
1192                                     sizeof disposition,
1193                                     FileDispositionInformation);
1194       if (NT_SUCCESS(status)) {
1195         SET_REQ_SUCCESS(req);
1196       } else {
1197         SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(status));
1198       }
1199     } else {
1200       SET_REQ_WIN32_ERROR(req, error);
1201     }
1202   }
1203 
1204   CloseHandle(handle);
1205 }
1206 
1207 
fs__rmdir(uv_fs_t * req)1208 static void fs__rmdir(uv_fs_t* req) {
1209   fs__unlink_rmdir(req, /*isrmdir*/1);
1210 }
1211 
1212 
fs__unlink(uv_fs_t * req)1213 static void fs__unlink(uv_fs_t* req) {
1214   fs__unlink_rmdir(req, /*isrmdir*/0);
1215 }
1216 
1217 
fs__mkdir(uv_fs_t * req)1218 void fs__mkdir(uv_fs_t* req) {
1219   /* TODO: use req->mode. */
1220   if (CreateDirectoryW(req->file.pathw, NULL)) {
1221     SET_REQ_RESULT(req, 0);
1222   } else {
1223     SET_REQ_WIN32_ERROR(req, GetLastError());
1224     if (req->sys_errno_ == ERROR_INVALID_NAME ||
1225         req->sys_errno_ == ERROR_DIRECTORY)
1226       req->result = UV_EINVAL;
1227   }
1228 }
1229 
1230 typedef int (*uv__fs_mktemp_func)(uv_fs_t* req);
1231 
1232 /* OpenBSD original: lib/libc/stdio/mktemp.c */
fs__mktemp(uv_fs_t * req,uv__fs_mktemp_func func)1233 void fs__mktemp(uv_fs_t* req, uv__fs_mktemp_func func) {
1234   static const WCHAR *tempchars =
1235     L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
1236   static const size_t num_chars = 62;
1237   static const size_t num_x = 6;
1238   WCHAR *cp, *ep;
1239   unsigned int tries, i;
1240   size_t len;
1241   uint64_t v;
1242   char* path;
1243 
1244   path = (char*)req->path;
1245   len = wcslen(req->file.pathw);
1246   ep = req->file.pathw + len;
1247   if (len < num_x || wcsncmp(ep - num_x, L"XXXXXX", num_x)) {
1248     SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER);
1249     goto clobber;
1250   }
1251 
1252   tries = TMP_MAX;
1253   do {
1254     if (uv__random_rtlgenrandom((void *)&v, sizeof(v)) < 0) {
1255       SET_REQ_UV_ERROR(req, UV_EIO, ERROR_IO_DEVICE);
1256       goto clobber;
1257     }
1258 
1259     cp = ep - num_x;
1260     for (i = 0; i < num_x; i++) {
1261       *cp++ = tempchars[v % num_chars];
1262       v /= num_chars;
1263     }
1264 
1265     if (func(req)) {
1266       if (req->result >= 0) {
1267         len = strlen(path);
1268         wcstombs(path + len - num_x, ep - num_x, num_x);
1269       }
1270       return;
1271     }
1272   } while (--tries);
1273 
1274   SET_REQ_WIN32_ERROR(req, GetLastError());
1275 
1276 clobber:
1277   path[0] = '\0';
1278 }
1279 
1280 
fs__mkdtemp_func(uv_fs_t * req)1281 static int fs__mkdtemp_func(uv_fs_t* req) {
1282   DWORD error;
1283   if (CreateDirectoryW(req->file.pathw, NULL)) {
1284     SET_REQ_RESULT(req, 0);
1285     return 1;
1286   }
1287   error = GetLastError();
1288   if (error != ERROR_ALREADY_EXISTS) {
1289     SET_REQ_WIN32_ERROR(req, error);
1290     return 1;
1291   }
1292 
1293   return 0;
1294 }
1295 
1296 
fs__mkdtemp(uv_fs_t * req)1297 void fs__mkdtemp(uv_fs_t* req) {
1298   fs__mktemp(req, fs__mkdtemp_func);
1299 }
1300 
1301 
fs__mkstemp_func(uv_fs_t * req)1302 static int fs__mkstemp_func(uv_fs_t* req) {
1303   HANDLE file;
1304   int fd;
1305 
1306   file = CreateFileW(req->file.pathw,
1307                      GENERIC_READ | GENERIC_WRITE,
1308                      FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1309                      NULL,
1310                      CREATE_NEW,
1311                      FILE_ATTRIBUTE_NORMAL,
1312                      NULL);
1313 
1314   if (file == INVALID_HANDLE_VALUE) {
1315     DWORD error;
1316     error = GetLastError();
1317 
1318     /* If the file exists, the main fs__mktemp() function
1319        will retry. If it's another error, we want to stop. */
1320     if (error != ERROR_FILE_EXISTS) {
1321       SET_REQ_WIN32_ERROR(req, error);
1322       return 1;
1323     }
1324 
1325     return 0;
1326   }
1327 
1328   fd = _open_osfhandle((intptr_t) file, 0);
1329   if (fd < 0) {
1330     /* The only known failure mode for _open_osfhandle() is EMFILE, in which
1331      * case GetLastError() will return zero. However we'll try to handle other
1332      * errors as well, should they ever occur.
1333      */
1334     if (errno == EMFILE)
1335       SET_REQ_UV_ERROR(req, UV_EMFILE, ERROR_TOO_MANY_OPEN_FILES);
1336     else if (GetLastError() != ERROR_SUCCESS)
1337       SET_REQ_WIN32_ERROR(req, GetLastError());
1338     else
1339       SET_REQ_WIN32_ERROR(req, UV_UNKNOWN);
1340     CloseHandle(file);
1341     return 1;
1342   }
1343 
1344   SET_REQ_RESULT(req, fd);
1345 
1346   return 1;
1347 }
1348 
1349 
fs__mkstemp(uv_fs_t * req)1350 void fs__mkstemp(uv_fs_t* req) {
1351   fs__mktemp(req, fs__mkstemp_func);
1352 }
1353 
1354 
fs__scandir(uv_fs_t * req)1355 void fs__scandir(uv_fs_t* req) {
1356   static const size_t dirents_initial_size = 32;
1357 
1358   HANDLE dir_handle = INVALID_HANDLE_VALUE;
1359 
1360   uv__dirent_t** dirents = NULL;
1361   size_t dirents_size = 0;
1362   size_t dirents_used = 0;
1363 
1364   IO_STATUS_BLOCK iosb;
1365   NTSTATUS status;
1366 
1367   /* Buffer to hold directory entries returned by NtQueryDirectoryFile.
1368    * It's important that this buffer can hold at least one entry, regardless
1369    * of the length of the file names present in the enumerated directory.
1370    * A file name is at most 256 WCHARs long.
1371    * According to MSDN, the buffer must be aligned at an 8-byte boundary.
1372    */
1373 #if _MSC_VER
1374   __declspec(align(8)) char buffer[8192];
1375 #else
1376   __attribute__ ((aligned (8))) char buffer[8192];
1377 #endif
1378 
1379   STATIC_ASSERT(sizeof buffer >=
1380                 sizeof(FILE_DIRECTORY_INFORMATION) + 256 * sizeof(WCHAR));
1381 
1382   /* Open the directory. */
1383   dir_handle =
1384       CreateFileW(req->file.pathw,
1385                   FILE_LIST_DIRECTORY | SYNCHRONIZE,
1386                   FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1387                   NULL,
1388                   OPEN_EXISTING,
1389                   FILE_FLAG_BACKUP_SEMANTICS,
1390                   NULL);
1391   if (dir_handle == INVALID_HANDLE_VALUE)
1392     goto win32_error;
1393 
1394   /* Read the first chunk. */
1395   status = pNtQueryDirectoryFile(dir_handle,
1396                                  NULL,
1397                                  NULL,
1398                                  NULL,
1399                                  &iosb,
1400                                  &buffer,
1401                                  sizeof buffer,
1402                                  FileDirectoryInformation,
1403                                  FALSE,
1404                                  NULL,
1405                                  TRUE);
1406 
1407   /* If the handle is not a directory, we'll get STATUS_INVALID_PARAMETER.
1408    * This should be reported back as UV_ENOTDIR.
1409    */
1410   if (status == (NTSTATUS)STATUS_INVALID_PARAMETER)
1411     goto not_a_directory_error;
1412 
1413   while (NT_SUCCESS(status)) {
1414     char* position = buffer;
1415     size_t next_entry_offset = 0;
1416 
1417     do {
1418       FILE_DIRECTORY_INFORMATION* info;
1419       uv__dirent_t* dirent;
1420 
1421       size_t wchar_len;
1422       size_t wtf8_len;
1423       char* wtf8;
1424 
1425       /* Obtain a pointer to the current directory entry. */
1426       position += next_entry_offset;
1427       info = (FILE_DIRECTORY_INFORMATION*) position;
1428 
1429       /* Fetch the offset to the next directory entry. */
1430       next_entry_offset = info->NextEntryOffset;
1431 
1432       /* Compute the length of the filename in WCHARs. */
1433       wchar_len = info->FileNameLength / sizeof info->FileName[0];
1434 
1435       /* Skip over '.' and '..' entries.  It has been reported that
1436        * the SharePoint driver includes the terminating zero byte in
1437        * the filename length.  Strip those first.
1438        */
1439       while (wchar_len > 0 && info->FileName[wchar_len - 1] == L'\0')
1440         wchar_len -= 1;
1441 
1442       if (wchar_len == 0)
1443         continue;
1444       if (wchar_len == 1 && info->FileName[0] == L'.')
1445         continue;
1446       if (wchar_len == 2 && info->FileName[0] == L'.' &&
1447           info->FileName[1] == L'.')
1448         continue;
1449 
1450       /* Compute the space required to store the filename as WTF-8. */
1451       wtf8_len = uv_utf16_length_as_wtf8(&info->FileName[0], wchar_len);
1452 
1453       /* Resize the dirent array if needed. */
1454       if (dirents_used >= dirents_size) {
1455         size_t new_dirents_size =
1456             dirents_size == 0 ? dirents_initial_size : dirents_size << 1;
1457         uv__dirent_t** new_dirents =
1458             uv__realloc(dirents, new_dirents_size * sizeof *dirents);
1459 
1460         if (new_dirents == NULL)
1461           goto out_of_memory_error;
1462 
1463         dirents_size = new_dirents_size;
1464         dirents = new_dirents;
1465       }
1466 
1467       /* Allocate space for the uv dirent structure. The dirent structure
1468        * includes room for the first character of the filename, but `utf8_len`
1469        * doesn't count the NULL terminator at this point.
1470        */
1471       dirent = uv__malloc(sizeof *dirent + wtf8_len);
1472       if (dirent == NULL)
1473         goto out_of_memory_error;
1474 
1475       dirents[dirents_used++] = dirent;
1476 
1477       /* Convert file name to UTF-8. */
1478       wtf8 = &dirent->d_name[0];
1479       if (uv_utf16_to_wtf8(&info->FileName[0], wchar_len, &wtf8, &wtf8_len) != 0)
1480         goto out_of_memory_error;
1481 
1482       /* Fill out the type field. */
1483       if (info->FileAttributes & FILE_ATTRIBUTE_DEVICE)
1484         dirent->d_type = UV__DT_CHAR;
1485       else if (info->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
1486         dirent->d_type = UV__DT_LINK;
1487       else if (info->FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1488         dirent->d_type = UV__DT_DIR;
1489       else
1490         dirent->d_type = UV__DT_FILE;
1491     } while (next_entry_offset != 0);
1492 
1493     /* Read the next chunk. */
1494     status = pNtQueryDirectoryFile(dir_handle,
1495                                    NULL,
1496                                    NULL,
1497                                    NULL,
1498                                    &iosb,
1499                                    &buffer,
1500                                    sizeof buffer,
1501                                    FileDirectoryInformation,
1502                                    FALSE,
1503                                    NULL,
1504                                    FALSE);
1505 
1506     /* After the first pNtQueryDirectoryFile call, the function may return
1507      * STATUS_SUCCESS even if the buffer was too small to hold at least one
1508      * directory entry.
1509      */
1510     if (status == STATUS_SUCCESS && iosb.Information == 0)
1511       status = STATUS_BUFFER_OVERFLOW;
1512   }
1513 
1514   if (status != STATUS_NO_MORE_FILES)
1515     goto nt_error;
1516 
1517   CloseHandle(dir_handle);
1518 
1519   /* Store the result in the request object. */
1520   req->ptr = dirents;
1521   if (dirents != NULL)
1522     req->flags |= UV_FS_FREE_PTR;
1523 
1524   SET_REQ_RESULT(req, dirents_used);
1525 
1526   /* `nbufs` will be used as index by uv_fs_scandir_next. */
1527   req->fs.info.nbufs = 0;
1528 
1529   return;
1530 
1531 nt_error:
1532   SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(status));
1533   goto cleanup;
1534 
1535 win32_error:
1536   SET_REQ_WIN32_ERROR(req, GetLastError());
1537   goto cleanup;
1538 
1539 not_a_directory_error:
1540   SET_REQ_UV_ERROR(req, UV_ENOTDIR, ERROR_DIRECTORY);
1541   goto cleanup;
1542 
1543 out_of_memory_error:
1544   SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
1545   goto cleanup;
1546 
1547 cleanup:
1548   if (dir_handle != INVALID_HANDLE_VALUE)
1549     CloseHandle(dir_handle);
1550   while (dirents_used > 0)
1551     uv__free(dirents[--dirents_used]);
1552   if (dirents != NULL)
1553     uv__free(dirents);
1554 }
1555 
fs__opendir(uv_fs_t * req)1556 void fs__opendir(uv_fs_t* req) {
1557   WCHAR* pathw;
1558   size_t len;
1559   const WCHAR* fmt;
1560   WCHAR* find_path;
1561   uv_dir_t* dir;
1562 
1563   pathw = req->file.pathw;
1564   dir = NULL;
1565   find_path = NULL;
1566 
1567   /* Figure out whether path is a file or a directory. */
1568   if (!(GetFileAttributesW(pathw) & FILE_ATTRIBUTE_DIRECTORY)) {
1569     SET_REQ_UV_ERROR(req, UV_ENOTDIR, ERROR_DIRECTORY);
1570     goto error;
1571   }
1572 
1573   dir = uv__malloc(sizeof(*dir));
1574   if (dir == NULL) {
1575     SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
1576     goto error;
1577   }
1578 
1579   len = wcslen(pathw);
1580 
1581   if (len == 0)
1582     fmt = L"./*";
1583   else if (IS_SLASH(pathw[len - 1]))
1584     fmt = L"%s*";
1585   else
1586     fmt = L"%s\\*";
1587 
1588   find_path = uv__malloc(sizeof(WCHAR) * (len + 4));
1589   if (find_path == NULL) {
1590     SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
1591     goto error;
1592   }
1593 
1594   _snwprintf(find_path, len + 3, fmt, pathw);
1595   dir->dir_handle = FindFirstFileW(find_path, &dir->find_data);
1596   uv__free(find_path);
1597   find_path = NULL;
1598   if (dir->dir_handle == INVALID_HANDLE_VALUE &&
1599       GetLastError() != ERROR_FILE_NOT_FOUND) {
1600     SET_REQ_WIN32_ERROR(req, GetLastError());
1601     goto error;
1602   }
1603 
1604   dir->need_find_call = FALSE;
1605   req->ptr = dir;
1606   SET_REQ_RESULT(req, 0);
1607   return;
1608 
1609 error:
1610   uv__free(dir);
1611   uv__free(find_path);
1612   req->ptr = NULL;
1613 }
1614 
fs__readdir(uv_fs_t * req)1615 void fs__readdir(uv_fs_t* req) {
1616   uv_dir_t* dir;
1617   uv_dirent_t* dirents;
1618   uv__dirent_t dent;
1619   unsigned int dirent_idx;
1620   PWIN32_FIND_DATAW find_data;
1621   unsigned int i;
1622   int r;
1623 
1624   req->flags |= UV_FS_FREE_PTR;
1625   dir = req->ptr;
1626   dirents = dir->dirents;
1627   memset(dirents, 0, dir->nentries * sizeof(*dir->dirents));
1628   find_data = &dir->find_data;
1629   dirent_idx = 0;
1630 
1631   while (dirent_idx < dir->nentries) {
1632     if (dir->need_find_call && FindNextFileW(dir->dir_handle, find_data) == 0) {
1633       if (GetLastError() == ERROR_NO_MORE_FILES)
1634         break;
1635       goto error;
1636     }
1637 
1638     /* Skip "." and ".." entries. */
1639     if (find_data->cFileName[0] == L'.' &&
1640         (find_data->cFileName[1] == L'\0' ||
1641         (find_data->cFileName[1] == L'.' &&
1642         find_data->cFileName[2] == L'\0'))) {
1643       dir->need_find_call = TRUE;
1644       continue;
1645     }
1646 
1647     r = uv__convert_utf16_to_utf8((const WCHAR*) &find_data->cFileName,
1648                                   -1,
1649                                   (char**) &dirents[dirent_idx].name);
1650     if (r != 0)
1651       goto error;
1652 
1653     /* Copy file type. */
1654     if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_DEVICE) != 0)
1655       dent.d_type = UV__DT_CHAR;
1656     else if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0)
1657       dent.d_type = UV__DT_LINK;
1658     else if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
1659       dent.d_type = UV__DT_DIR;
1660     else
1661       dent.d_type = UV__DT_FILE;
1662 
1663     dirents[dirent_idx].type = uv__fs_get_dirent_type(&dent);
1664     dir->need_find_call = TRUE;
1665     ++dirent_idx;
1666   }
1667 
1668   SET_REQ_RESULT(req, dirent_idx);
1669   return;
1670 
1671 error:
1672   SET_REQ_WIN32_ERROR(req, GetLastError());
1673   for (i = 0; i < dirent_idx; ++i) {
1674     uv__free((char*) dirents[i].name);
1675     dirents[i].name = NULL;
1676   }
1677 }
1678 
fs__closedir(uv_fs_t * req)1679 void fs__closedir(uv_fs_t* req) {
1680   uv_dir_t* dir;
1681 
1682   dir = req->ptr;
1683   FindClose(dir->dir_handle);
1684   uv__free(req->ptr);
1685   SET_REQ_RESULT(req, 0);
1686 }
1687 
fs__stat_path(WCHAR * path,uv_stat_t * statbuf,int do_lstat)1688 INLINE static fs__stat_path_return_t fs__stat_path(WCHAR* path,
1689     uv_stat_t* statbuf, int do_lstat) {
1690   FILE_STAT_BASIC_INFORMATION stat_info;
1691 
1692   // Check if the new fast API is available.
1693   if (!pGetFileInformationByName) {
1694     return FS__STAT_PATH_TRY_SLOW;
1695   }
1696 
1697   // Check if the API call fails.
1698   if (!pGetFileInformationByName(path, FileStatBasicByNameInfo, &stat_info,
1699       sizeof(stat_info))) {
1700     switch(GetLastError()) {
1701       case ERROR_FILE_NOT_FOUND:
1702       case ERROR_PATH_NOT_FOUND:
1703       case ERROR_NOT_READY:
1704       case ERROR_BAD_NET_NAME:
1705         /* These errors aren't worth retrying with the slow path. */
1706         return FS__STAT_PATH_ERROR;
1707     }
1708     return FS__STAT_PATH_TRY_SLOW;
1709   }
1710 
1711   // A file handle is needed to get st_size for links.
1712   if ((stat_info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
1713     return FS__STAT_PATH_TRY_SLOW;
1714   }
1715 
1716   if (stat_info.DeviceType == FILE_DEVICE_NULL) {
1717     fs__stat_assign_statbuf_null(statbuf);
1718     return FS__STAT_PATH_SUCCESS;
1719   }
1720 
1721   fs__stat_assign_statbuf(statbuf, stat_info, do_lstat);
1722   return FS__STAT_PATH_SUCCESS;
1723 }
1724 
fs__stat_handle(HANDLE handle,uv_stat_t * statbuf,int do_lstat)1725 INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf,
1726     int do_lstat) {
1727   size_t target_length = 0;
1728   FILE_FS_DEVICE_INFORMATION device_info;
1729   FILE_ALL_INFORMATION file_info;
1730   FILE_FS_VOLUME_INFORMATION volume_info;
1731   NTSTATUS nt_status;
1732   IO_STATUS_BLOCK io_status;
1733   FILE_STAT_BASIC_INFORMATION stat_info;
1734 
1735   nt_status = pNtQueryVolumeInformationFile(handle,
1736                                             &io_status,
1737                                             &device_info,
1738                                             sizeof device_info,
1739                                             FileFsDeviceInformation);
1740 
1741   /* Buffer overflow (a warning status code) is expected here. */
1742   if (NT_ERROR(nt_status)) {
1743     SetLastError(pRtlNtStatusToDosError(nt_status));
1744     return -1;
1745   }
1746 
1747   /* If it's NUL device set fields as reasonable as possible and return. */
1748   if (device_info.DeviceType == FILE_DEVICE_NULL) {
1749     fs__stat_assign_statbuf_null(statbuf);
1750     return 0;
1751   }
1752 
1753   nt_status = pNtQueryInformationFile(handle,
1754                                       &io_status,
1755                                       &file_info,
1756                                       sizeof file_info,
1757                                       FileAllInformation);
1758 
1759   /* Buffer overflow (a warning status code) is expected here. */
1760   if (NT_ERROR(nt_status)) {
1761     SetLastError(pRtlNtStatusToDosError(nt_status));
1762     return -1;
1763   }
1764 
1765   nt_status = pNtQueryVolumeInformationFile(handle,
1766                                             &io_status,
1767                                             &volume_info,
1768                                             sizeof volume_info,
1769                                             FileFsVolumeInformation);
1770 
1771   /* Buffer overflow (a warning status code) is expected here. */
1772   if (io_status.Status == STATUS_NOT_IMPLEMENTED) {
1773     stat_info.VolumeSerialNumber.QuadPart = 0;
1774   } else if (NT_ERROR(nt_status)) {
1775     SetLastError(pRtlNtStatusToDosError(nt_status));
1776     return -1;
1777   } else {
1778     stat_info.VolumeSerialNumber.QuadPart = volume_info.VolumeSerialNumber;
1779   }
1780 
1781   stat_info.DeviceType = device_info.DeviceType;
1782   stat_info.FileAttributes = file_info.BasicInformation.FileAttributes;
1783   stat_info.NumberOfLinks = file_info.StandardInformation.NumberOfLinks;
1784   stat_info.FileId.QuadPart =
1785       file_info.InternalInformation.IndexNumber.QuadPart;
1786   stat_info.ChangeTime.QuadPart =
1787       file_info.BasicInformation.ChangeTime.QuadPart;
1788   stat_info.CreationTime.QuadPart =
1789       file_info.BasicInformation.CreationTime.QuadPart;
1790   stat_info.LastAccessTime.QuadPart =
1791       file_info.BasicInformation.LastAccessTime.QuadPart;
1792   stat_info.LastWriteTime.QuadPart =
1793       file_info.BasicInformation.LastWriteTime.QuadPart;
1794   stat_info.AllocationSize.QuadPart =
1795       file_info.StandardInformation.AllocationSize.QuadPart;
1796 
1797   if (do_lstat &&
1798       (file_info.BasicInformation.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
1799     /*
1800      * If reading the link fails, the reparse point is not a symlink and needs
1801      * to be treated as a regular file. The higher level lstat function will
1802      * detect this failure and retry without do_lstat if appropriate.
1803      */
1804     if (fs__readlink_handle(handle, NULL, &target_length) != 0) {
1805       fs__stat_assign_statbuf(statbuf, stat_info, do_lstat);
1806       return -1;
1807     }
1808     stat_info.EndOfFile.QuadPart = target_length;
1809   } else {
1810     stat_info.EndOfFile.QuadPart =
1811       file_info.StandardInformation.EndOfFile.QuadPart;
1812   }
1813 
1814   fs__stat_assign_statbuf(statbuf, stat_info, do_lstat);
1815   return 0;
1816 }
1817 
fs__stat_assign_statbuf_null(uv_stat_t * statbuf)1818 INLINE static void fs__stat_assign_statbuf_null(uv_stat_t* statbuf) {
1819   memset(statbuf, 0, sizeof(uv_stat_t));
1820   statbuf->st_mode = _S_IFCHR;
1821   statbuf->st_mode |= (_S_IREAD | _S_IWRITE) | ((_S_IREAD | _S_IWRITE) >> 3) |
1822                       ((_S_IREAD | _S_IWRITE) >> 6);
1823   statbuf->st_nlink = 1;
1824   statbuf->st_blksize = 4096;
1825   statbuf->st_rdev = FILE_DEVICE_NULL << 16;
1826 }
1827 
fs__stat_assign_statbuf(uv_stat_t * statbuf,FILE_STAT_BASIC_INFORMATION stat_info,int do_lstat)1828 INLINE static void fs__stat_assign_statbuf(uv_stat_t* statbuf,
1829     FILE_STAT_BASIC_INFORMATION stat_info, int do_lstat) {
1830   statbuf->st_dev = stat_info.VolumeSerialNumber.QuadPart;
1831 
1832   /* Todo: st_mode should probably always be 0666 for everyone. We might also
1833    * want to report 0777 if the file is a .exe or a directory.
1834    *
1835    * Currently it's based on whether the 'readonly' attribute is set, which
1836    * makes little sense because the semantics are so different: the 'read-only'
1837    * flag is just a way for a user to protect against accidental deletion, and
1838    * serves no security purpose. Windows uses ACLs for that.
1839    *
1840    * Also people now use uv_fs_chmod() to take away the writable bit for good
1841    * reasons. Windows however just makes the file read-only, which makes it
1842    * impossible to delete the file afterwards, since read-only files can't be
1843    * deleted.
1844    *
1845    * IOW it's all just a clusterfuck and we should think of something that
1846    * makes slightly more sense.
1847    *
1848    * And uv_fs_chmod should probably just fail on windows or be a total no-op.
1849    * There's nothing sensible it can do anyway.
1850    */
1851   statbuf->st_mode = 0;
1852 
1853   /*
1854   * On Windows, FILE_ATTRIBUTE_REPARSE_POINT is a general purpose mechanism
1855   * by which filesystem drivers can intercept and alter file system requests.
1856   *
1857   * The only reparse points we care about are symlinks and mount points, both
1858   * of which are treated as POSIX symlinks. Further, we only care when
1859   * invoked via lstat, which seeks information about the link instead of its
1860   * target. Otherwise, reparse points must be treated as regular files.
1861   */
1862   if (do_lstat &&
1863       (stat_info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
1864     statbuf->st_mode |= S_IFLNK;
1865     statbuf->st_size = stat_info.EndOfFile.QuadPart;
1866   }
1867 
1868   if (statbuf->st_mode == 0) {
1869     if (stat_info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1870       statbuf->st_mode |= _S_IFDIR;
1871       statbuf->st_size = 0;
1872     } else {
1873       statbuf->st_mode |= _S_IFREG;
1874       statbuf->st_size = stat_info.EndOfFile.QuadPart;
1875     }
1876   }
1877 
1878   if (stat_info.FileAttributes & FILE_ATTRIBUTE_READONLY)
1879     statbuf->st_mode |= _S_IREAD | (_S_IREAD >> 3) | (_S_IREAD >> 6);
1880   else
1881     statbuf->st_mode |= (_S_IREAD | _S_IWRITE) | ((_S_IREAD | _S_IWRITE) >> 3) |
1882                         ((_S_IREAD | _S_IWRITE) >> 6);
1883 
1884   uv__filetime_to_timespec(&statbuf->st_atim,
1885                            stat_info.LastAccessTime.QuadPart);
1886   uv__filetime_to_timespec(&statbuf->st_ctim,
1887                            stat_info.ChangeTime.QuadPart);
1888   uv__filetime_to_timespec(&statbuf->st_mtim,
1889                            stat_info.LastWriteTime.QuadPart);
1890   uv__filetime_to_timespec(&statbuf->st_birthtim,
1891                            stat_info.CreationTime.QuadPart);
1892 
1893   statbuf->st_ino = stat_info.FileId.QuadPart;
1894 
1895   /* st_blocks contains the on-disk allocation size in 512-byte units. */
1896   statbuf->st_blocks =
1897       (uint64_t) stat_info.AllocationSize.QuadPart >> 9;
1898 
1899   statbuf->st_nlink = stat_info.NumberOfLinks;
1900 
1901   /* The st_blksize is supposed to be the 'optimal' number of bytes for reading
1902    * and writing to the disk. That is, for any definition of 'optimal' - it's
1903    * supposed to at least avoid read-update-write behavior when writing to the
1904    * disk.
1905    *
1906    * However nobody knows this and even fewer people actually use this value,
1907    * and in order to fill it out we'd have to make another syscall to query the
1908    * volume for FILE_FS_SECTOR_SIZE_INFORMATION.
1909    *
1910    * Therefore we'll just report a sensible value that's quite commonly okay
1911    * on modern hardware.
1912    *
1913    * 4096 is the minimum required to be compatible with newer Advanced Format
1914    * drives (which have 4096 bytes per physical sector), and to be backwards
1915    * compatible with older drives (which have 512 bytes per physical sector).
1916    */
1917   statbuf->st_blksize = 4096;
1918 
1919   /* Todo: set st_flags to something meaningful. Also provide a wrapper for
1920    * chattr(2).
1921    */
1922   statbuf->st_flags = 0;
1923 
1924   /* Windows has nothing sensible to say about these values, so they'll just
1925    * remain empty.
1926    */
1927   statbuf->st_gid = 0;
1928   statbuf->st_uid = 0;
1929   statbuf->st_rdev = 0;
1930   statbuf->st_gen = 0;
1931 }
1932 
1933 
fs__stat_prepare_path(WCHAR * pathw)1934 INLINE static void fs__stat_prepare_path(WCHAR* pathw) {
1935   size_t len = wcslen(pathw);
1936 
1937   /* TODO: ignore namespaced paths. */
1938   if (len > 1 && pathw[len - 2] != L':' &&
1939       (pathw[len - 1] == L'\\' || pathw[len - 1] == L'/')) {
1940     pathw[len - 1] = '\0';
1941   }
1942 }
1943 
1944 
fs__stat_impl_from_path(WCHAR * path,int do_lstat,uv_stat_t * statbuf)1945 INLINE static DWORD fs__stat_impl_from_path(WCHAR* path,
1946                                             int do_lstat,
1947                                             uv_stat_t* statbuf) {
1948   HANDLE handle;
1949   DWORD flags;
1950   DWORD ret;
1951 
1952   // If new API exists, try to use it.
1953   switch (fs__stat_path(path, statbuf, do_lstat)) {
1954     case FS__STAT_PATH_SUCCESS:
1955       return 0;
1956     case FS__STAT_PATH_ERROR:
1957       return GetLastError();
1958     case FS__STAT_PATH_TRY_SLOW:
1959       break;
1960   }
1961 
1962   // If the new API does not exist, use the old API.
1963   flags = FILE_FLAG_BACKUP_SEMANTICS;
1964   if (do_lstat)
1965     flags |= FILE_FLAG_OPEN_REPARSE_POINT;
1966 
1967   handle = CreateFileW(path,
1968                        FILE_READ_ATTRIBUTES,
1969                        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1970                        NULL,
1971                        OPEN_EXISTING,
1972                        flags,
1973                        NULL);
1974 
1975   if (handle == INVALID_HANDLE_VALUE)
1976     return GetLastError();
1977 
1978   if (fs__stat_handle(handle, statbuf, do_lstat) != 0)
1979     ret = GetLastError();
1980   else
1981     ret = 0;
1982 
1983   CloseHandle(handle);
1984   return ret;
1985 }
1986 
1987 
fs__stat_impl(uv_fs_t * req,int do_lstat)1988 INLINE static void fs__stat_impl(uv_fs_t* req, int do_lstat) {
1989   DWORD error;
1990 
1991   error = fs__stat_impl_from_path(req->file.pathw, do_lstat, &req->statbuf);
1992   if (error != 0) {
1993     if (do_lstat &&
1994         (error == ERROR_SYMLINK_NOT_SUPPORTED ||
1995          error == ERROR_NOT_A_REPARSE_POINT)) {
1996       /* We opened a reparse point but it was not a symlink. Try again. */
1997       fs__stat_impl(req, 0);
1998     } else {
1999       /* Stat failed. */
2000       SET_REQ_WIN32_ERROR(req, error);
2001     }
2002 
2003     return;
2004   }
2005 
2006   req->ptr = &req->statbuf;
2007   SET_REQ_RESULT(req, 0);
2008 }
2009 
2010 
fs__fstat_handle(int fd,HANDLE handle,uv_stat_t * statbuf)2011 INLINE static int fs__fstat_handle(int fd, HANDLE handle, uv_stat_t* statbuf) {
2012   DWORD file_type;
2013 
2014   /* Each file type is processed differently. */
2015   file_type = uv_guess_handle(fd);
2016   switch (file_type) {
2017   /* Disk files use the existing logic from fs__stat_handle. */
2018   case UV_FILE:
2019     return fs__stat_handle(handle, statbuf, 0);
2020 
2021   /* Devices and pipes are processed identically. There is no more information
2022    * for them from any API. Fields are set as reasonably as possible and the
2023    * function returns. */
2024   case UV_TTY:
2025   case UV_NAMED_PIPE:
2026     memset(statbuf, 0, sizeof(uv_stat_t));
2027     statbuf->st_mode = file_type == UV_TTY ? _S_IFCHR : _S_IFIFO;
2028     statbuf->st_nlink = 1;
2029     statbuf->st_rdev = (file_type == UV_TTY ? FILE_DEVICE_CONSOLE : FILE_DEVICE_NAMED_PIPE) << 16;
2030     statbuf->st_ino = (uintptr_t) handle;
2031     return 0;
2032 
2033   /* If file type is unknown it is an error. */
2034   case UV_UNKNOWN_HANDLE:
2035   default:
2036     SetLastError(ERROR_INVALID_HANDLE);
2037     return -1;
2038   }
2039 }
2040 
2041 
fs__stat(uv_fs_t * req)2042 static void fs__stat(uv_fs_t* req) {
2043   fs__stat_prepare_path(req->file.pathw);
2044   fs__stat_impl(req, 0);
2045 }
2046 
2047 
fs__lstat(uv_fs_t * req)2048 static void fs__lstat(uv_fs_t* req) {
2049   fs__stat_prepare_path(req->file.pathw);
2050   fs__stat_impl(req, 1);
2051 }
2052 
2053 
fs__fstat(uv_fs_t * req)2054 static void fs__fstat(uv_fs_t* req) {
2055   int fd = req->file.fd;
2056   HANDLE handle;
2057 
2058   VERIFY_FD(fd, req);
2059 
2060   handle = uv__get_osfhandle(fd);
2061 
2062   if (handle == INVALID_HANDLE_VALUE) {
2063     SET_REQ_WIN32_ERROR(req, ERROR_INVALID_HANDLE);
2064     return;
2065   }
2066 
2067   if (fs__fstat_handle(fd, handle, &req->statbuf) != 0) {
2068     SET_REQ_WIN32_ERROR(req, GetLastError());
2069     return;
2070   }
2071 
2072   req->ptr = &req->statbuf;
2073   SET_REQ_RESULT(req, 0);
2074 }
2075 
2076 
fs__rename(uv_fs_t * req)2077 static void fs__rename(uv_fs_t* req) {
2078   if (!MoveFileExW(req->file.pathw, req->fs.info.new_pathw, MOVEFILE_REPLACE_EXISTING)) {
2079     SET_REQ_WIN32_ERROR(req, GetLastError());
2080     return;
2081   }
2082 
2083   SET_REQ_RESULT(req, 0);
2084 }
2085 
2086 
fs__sync_impl(uv_fs_t * req)2087 INLINE static void fs__sync_impl(uv_fs_t* req) {
2088   int fd = req->file.fd;
2089   int result;
2090 
2091   VERIFY_FD(fd, req);
2092 
2093   result = FlushFileBuffers(uv__get_osfhandle(fd)) ? 0 : -1;
2094   if (result == -1) {
2095     SET_REQ_WIN32_ERROR(req, GetLastError());
2096   } else {
2097     SET_REQ_RESULT(req, result);
2098   }
2099 }
2100 
2101 
fs__fsync(uv_fs_t * req)2102 static void fs__fsync(uv_fs_t* req) {
2103   fs__sync_impl(req);
2104 }
2105 
2106 
fs__fdatasync(uv_fs_t * req)2107 static void fs__fdatasync(uv_fs_t* req) {
2108   fs__sync_impl(req);
2109 }
2110 
2111 
fs__ftruncate(uv_fs_t * req)2112 static void fs__ftruncate(uv_fs_t* req) {
2113   int fd = req->file.fd;
2114   HANDLE handle;
2115   struct uv__fd_info_s fd_info = { 0 };
2116   NTSTATUS status;
2117   IO_STATUS_BLOCK io_status;
2118   FILE_END_OF_FILE_INFORMATION eof_info;
2119 
2120   VERIFY_FD(fd, req);
2121 
2122   handle = uv__get_osfhandle(fd);
2123 
2124   if (uv__fd_hash_get(fd, &fd_info)) {
2125     if (fd_info.is_directory) {
2126       SET_REQ_WIN32_ERROR(req, ERROR_ACCESS_DENIED);
2127       return;
2128     }
2129 
2130     if (fd_info.mapping != INVALID_HANDLE_VALUE) {
2131       CloseHandle(fd_info.mapping);
2132     }
2133   }
2134 
2135   eof_info.EndOfFile.QuadPart = req->fs.info.offset;
2136 
2137   status = pNtSetInformationFile(handle,
2138                                  &io_status,
2139                                  &eof_info,
2140                                  sizeof eof_info,
2141                                  FileEndOfFileInformation);
2142 
2143   if (NT_SUCCESS(status)) {
2144     SET_REQ_RESULT(req, 0);
2145   } else {
2146     SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(status));
2147 
2148     if (fd_info.flags) {
2149       CloseHandle(handle);
2150       fd_info.mapping = INVALID_HANDLE_VALUE;
2151       fd_info.size.QuadPart = 0;
2152       fd_info.current_pos.QuadPart = 0;
2153       uv__fd_hash_add(fd, &fd_info);
2154       return;
2155     }
2156   }
2157 
2158   if (fd_info.flags) {
2159     fd_info.size = eof_info.EndOfFile;
2160 
2161     if (fd_info.size.QuadPart == 0) {
2162       fd_info.mapping = INVALID_HANDLE_VALUE;
2163     } else {
2164       DWORD flProtect = (fd_info.flags & (UV_FS_O_RDONLY | UV_FS_O_WRONLY |
2165         UV_FS_O_RDWR)) == UV_FS_O_RDONLY ? PAGE_READONLY : PAGE_READWRITE;
2166       fd_info.mapping = CreateFileMapping(handle,
2167                                           NULL,
2168                                           flProtect,
2169                                           fd_info.size.HighPart,
2170                                           fd_info.size.LowPart,
2171                                           NULL);
2172       if (fd_info.mapping == NULL) {
2173         SET_REQ_WIN32_ERROR(req, GetLastError());
2174         CloseHandle(handle);
2175         fd_info.mapping = INVALID_HANDLE_VALUE;
2176         fd_info.size.QuadPart = 0;
2177         fd_info.current_pos.QuadPart = 0;
2178         uv__fd_hash_add(fd, &fd_info);
2179         return;
2180       }
2181     }
2182 
2183     uv__fd_hash_add(fd, &fd_info);
2184   }
2185 }
2186 
2187 
fs__copyfile(uv_fs_t * req)2188 static void fs__copyfile(uv_fs_t* req) {
2189   int flags;
2190   int overwrite;
2191   uv_stat_t statbuf;
2192   uv_stat_t new_statbuf;
2193 
2194   flags = req->fs.info.file_flags;
2195 
2196   if (flags & UV_FS_COPYFILE_FICLONE_FORCE) {
2197     SET_REQ_UV_ERROR(req, UV_ENOSYS, ERROR_NOT_SUPPORTED);
2198     return;
2199   }
2200 
2201   overwrite = flags & UV_FS_COPYFILE_EXCL;
2202 
2203   if (CopyFileW(req->file.pathw, req->fs.info.new_pathw, overwrite) != 0) {
2204     SET_REQ_RESULT(req, 0);
2205     return;
2206   }
2207 
2208   SET_REQ_WIN32_ERROR(req, GetLastError());
2209   if (req->result != UV_EBUSY)
2210     return;
2211 
2212   /* if error UV_EBUSY check if src and dst file are the same */
2213   if (fs__stat_impl_from_path(req->file.pathw, 0, &statbuf) != 0 ||
2214       fs__stat_impl_from_path(req->fs.info.new_pathw, 0, &new_statbuf) != 0) {
2215     return;
2216   }
2217 
2218   if (statbuf.st_dev == new_statbuf.st_dev &&
2219       statbuf.st_ino == new_statbuf.st_ino) {
2220     SET_REQ_RESULT(req, 0);
2221   }
2222 }
2223 
2224 
fs__sendfile(uv_fs_t * req)2225 static void fs__sendfile(uv_fs_t* req) {
2226   int fd_in = req->file.fd, fd_out = req->fs.info.fd_out;
2227   size_t length = req->fs.info.bufsml[0].len;
2228   int64_t offset = req->fs.info.offset;
2229   const size_t max_buf_size = 65536;
2230   size_t buf_size = length < max_buf_size ? length : max_buf_size;
2231   int n, result = 0;
2232   int64_t result_offset = 0;
2233   char* buf = (char*) uv__malloc(buf_size);
2234   if (!buf) {
2235     uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
2236   }
2237 
2238   if (offset != -1) {
2239     result_offset = _lseeki64(fd_in, offset, SEEK_SET);
2240   }
2241 
2242   if (result_offset == -1) {
2243     result = -1;
2244   } else {
2245     while (length > 0) {
2246       n = _read(fd_in, buf, length < buf_size ? length : buf_size);
2247       if (n == 0) {
2248         break;
2249       } else if (n == -1) {
2250         result = -1;
2251         break;
2252       }
2253 
2254       length -= n;
2255 
2256       n = _write(fd_out, buf, n);
2257       if (n == -1) {
2258         result = -1;
2259         break;
2260       }
2261 
2262       result += n;
2263     }
2264   }
2265 
2266   uv__free(buf);
2267 
2268   SET_REQ_RESULT(req, result);
2269 }
2270 
2271 
fs__access(uv_fs_t * req)2272 static void fs__access(uv_fs_t* req) {
2273   DWORD attr = GetFileAttributesW(req->file.pathw);
2274 
2275   if (attr == INVALID_FILE_ATTRIBUTES) {
2276     SET_REQ_WIN32_ERROR(req, GetLastError());
2277     return;
2278   }
2279 
2280   /*
2281    * Access is possible if
2282    * - write access wasn't requested,
2283    * - or the file isn't read-only,
2284    * - or it's a directory.
2285    * (Directories cannot be read-only on Windows.)
2286    */
2287   if (!(req->fs.info.mode & W_OK) ||
2288       !(attr & FILE_ATTRIBUTE_READONLY) ||
2289       (attr & FILE_ATTRIBUTE_DIRECTORY)) {
2290     SET_REQ_RESULT(req, 0);
2291   } else {
2292     SET_REQ_WIN32_ERROR(req, UV_EPERM);
2293   }
2294 
2295 }
2296 
2297 
fs__chmod(uv_fs_t * req)2298 static void fs__chmod(uv_fs_t* req) {
2299   int result = _wchmod(req->file.pathw, req->fs.info.mode);
2300   if (result == -1)
2301     SET_REQ_WIN32_ERROR(req, _doserrno);
2302   else
2303     SET_REQ_RESULT(req, 0);
2304 }
2305 
2306 
fs__fchmod(uv_fs_t * req)2307 static void fs__fchmod(uv_fs_t* req) {
2308   int fd = req->file.fd;
2309   int clear_archive_flag;
2310   HANDLE handle;
2311   NTSTATUS nt_status;
2312   IO_STATUS_BLOCK io_status;
2313   FILE_BASIC_INFORMATION file_info;
2314 
2315   VERIFY_FD(fd, req);
2316 
2317   handle = ReOpenFile(uv__get_osfhandle(fd), FILE_WRITE_ATTRIBUTES, 0, 0);
2318   if (handle == INVALID_HANDLE_VALUE) {
2319     SET_REQ_WIN32_ERROR(req, GetLastError());
2320     return;
2321   }
2322 
2323   nt_status = pNtQueryInformationFile(handle,
2324                                       &io_status,
2325                                       &file_info,
2326                                       sizeof file_info,
2327                                       FileBasicInformation);
2328 
2329   if (!NT_SUCCESS(nt_status)) {
2330     SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(nt_status));
2331     goto fchmod_cleanup;
2332   }
2333 
2334   /* Test if the Archive attribute is cleared */
2335   if ((file_info.FileAttributes & FILE_ATTRIBUTE_ARCHIVE) == 0) {
2336       /* Set Archive flag, otherwise setting or clearing the read-only
2337          flag will not work */
2338       file_info.FileAttributes |= FILE_ATTRIBUTE_ARCHIVE;
2339       nt_status = pNtSetInformationFile(handle,
2340                                         &io_status,
2341                                         &file_info,
2342                                         sizeof file_info,
2343                                         FileBasicInformation);
2344       if (!NT_SUCCESS(nt_status)) {
2345         SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(nt_status));
2346         goto fchmod_cleanup;
2347       }
2348       /* Remember to clear the flag later on */
2349       clear_archive_flag = 1;
2350   } else {
2351       clear_archive_flag = 0;
2352   }
2353 
2354   if (req->fs.info.mode & _S_IWRITE) {
2355     file_info.FileAttributes &= ~FILE_ATTRIBUTE_READONLY;
2356   } else {
2357     file_info.FileAttributes |= FILE_ATTRIBUTE_READONLY;
2358   }
2359 
2360   nt_status = pNtSetInformationFile(handle,
2361                                     &io_status,
2362                                     &file_info,
2363                                     sizeof file_info,
2364                                     FileBasicInformation);
2365 
2366   if (!NT_SUCCESS(nt_status)) {
2367     SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(nt_status));
2368     goto fchmod_cleanup;
2369   }
2370 
2371   if (clear_archive_flag) {
2372       file_info.FileAttributes &= ~FILE_ATTRIBUTE_ARCHIVE;
2373       if (file_info.FileAttributes == 0) {
2374           file_info.FileAttributes = FILE_ATTRIBUTE_NORMAL;
2375       }
2376       nt_status = pNtSetInformationFile(handle,
2377                                         &io_status,
2378                                         &file_info,
2379                                         sizeof file_info,
2380                                         FileBasicInformation);
2381       if (!NT_SUCCESS(nt_status)) {
2382         SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(nt_status));
2383         goto fchmod_cleanup;
2384       }
2385   }
2386 
2387   SET_REQ_SUCCESS(req);
2388 fchmod_cleanup:
2389   CloseHandle(handle);
2390 }
2391 
2392 
fs__utime_handle(HANDLE handle,double atime,double mtime)2393 INLINE static int fs__utime_handle(HANDLE handle, double atime, double mtime) {
2394   FILETIME filetime_a, filetime_m;
2395 
2396   TIME_T_TO_FILETIME(atime, &filetime_a);
2397   TIME_T_TO_FILETIME(mtime, &filetime_m);
2398 
2399   if (!SetFileTime(handle, NULL, &filetime_a, &filetime_m)) {
2400     return -1;
2401   }
2402 
2403   return 0;
2404 }
2405 
fs__utime_impl_from_path(WCHAR * path,double atime,double mtime,int do_lutime)2406 INLINE static DWORD fs__utime_impl_from_path(WCHAR* path,
2407                                              double atime,
2408                                              double mtime,
2409                                              int do_lutime) {
2410   HANDLE handle;
2411   DWORD flags;
2412   DWORD ret;
2413 
2414   flags = FILE_FLAG_BACKUP_SEMANTICS;
2415   if (do_lutime) {
2416     flags |= FILE_FLAG_OPEN_REPARSE_POINT;
2417   }
2418 
2419   handle = CreateFileW(path,
2420                        FILE_WRITE_ATTRIBUTES,
2421                        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
2422                        NULL,
2423                        OPEN_EXISTING,
2424                        flags,
2425                        NULL);
2426 
2427   if (handle == INVALID_HANDLE_VALUE)
2428     return GetLastError();
2429 
2430   if (fs__utime_handle(handle, atime, mtime) != 0)
2431     ret = GetLastError();
2432   else
2433     ret = 0;
2434 
2435   CloseHandle(handle);
2436   return ret;
2437 }
2438 
fs__utime_impl(uv_fs_t * req,int do_lutime)2439 INLINE static void fs__utime_impl(uv_fs_t* req, int do_lutime) {
2440   DWORD error;
2441 
2442   error = fs__utime_impl_from_path(req->file.pathw,
2443                                    req->fs.time.atime,
2444                                    req->fs.time.mtime,
2445                                    do_lutime);
2446 
2447   if (error != 0) {
2448     if (do_lutime &&
2449         (error == ERROR_SYMLINK_NOT_SUPPORTED ||
2450          error == ERROR_NOT_A_REPARSE_POINT)) {
2451       /* Opened file is a reparse point but not a symlink. Try again. */
2452       fs__utime_impl(req, 0);
2453     } else {
2454       /* utime failed. */
2455       SET_REQ_WIN32_ERROR(req, error);
2456     }
2457 
2458     return;
2459   }
2460 
2461   SET_REQ_RESULT(req, 0);
2462 }
2463 
fs__utime(uv_fs_t * req)2464 static void fs__utime(uv_fs_t* req) {
2465   fs__utime_impl(req, /* do_lutime */ 0);
2466 }
2467 
2468 
fs__futime(uv_fs_t * req)2469 static void fs__futime(uv_fs_t* req) {
2470   int fd = req->file.fd;
2471   HANDLE handle;
2472   VERIFY_FD(fd, req);
2473 
2474   handle = uv__get_osfhandle(fd);
2475 
2476   if (handle == INVALID_HANDLE_VALUE) {
2477     SET_REQ_WIN32_ERROR(req, ERROR_INVALID_HANDLE);
2478     return;
2479   }
2480 
2481   if (fs__utime_handle(handle, req->fs.time.atime, req->fs.time.mtime) != 0) {
2482     SET_REQ_WIN32_ERROR(req, GetLastError());
2483     return;
2484   }
2485 
2486   SET_REQ_RESULT(req, 0);
2487 }
2488 
fs__lutime(uv_fs_t * req)2489 static void fs__lutime(uv_fs_t* req) {
2490   fs__utime_impl(req, /* do_lutime */ 1);
2491 }
2492 
2493 
fs__link(uv_fs_t * req)2494 static void fs__link(uv_fs_t* req) {
2495   DWORD r = CreateHardLinkW(req->fs.info.new_pathw, req->file.pathw, NULL);
2496   if (r == 0)
2497     SET_REQ_WIN32_ERROR(req, GetLastError());
2498   else
2499     SET_REQ_RESULT(req, 0);
2500 }
2501 
2502 
fs__create_junction(uv_fs_t * req,const WCHAR * path,const WCHAR * new_path)2503 static void fs__create_junction(uv_fs_t* req, const WCHAR* path,
2504     const WCHAR* new_path) {
2505   HANDLE handle = INVALID_HANDLE_VALUE;
2506   REPARSE_DATA_BUFFER *buffer = NULL;
2507   int created = 0;
2508   int target_len;
2509   int is_absolute, is_long_path;
2510   int needed_buf_size, used_buf_size, used_data_size, path_buf_len;
2511   int start, len, i;
2512   int add_slash;
2513   DWORD bytes;
2514   WCHAR* path_buf;
2515 
2516   target_len = wcslen(path);
2517   is_long_path = wcsncmp(path, LONG_PATH_PREFIX, LONG_PATH_PREFIX_LEN) == 0;
2518 
2519   if (is_long_path) {
2520     is_absolute = 1;
2521   } else {
2522     is_absolute = target_len >= 3 && IS_LETTER(path[0]) &&
2523       path[1] == L':' && IS_SLASH(path[2]);
2524   }
2525 
2526   if (!is_absolute) {
2527     /* Not supporting relative paths */
2528     SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_NOT_SUPPORTED);
2529     return;
2530   }
2531 
2532   /* Do a pessimistic calculation of the required buffer size */
2533   needed_buf_size =
2534       FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer) +
2535       JUNCTION_PREFIX_LEN * sizeof(WCHAR) +
2536       2 * (target_len + 2) * sizeof(WCHAR);
2537 
2538   /* Allocate the buffer */
2539   buffer = (REPARSE_DATA_BUFFER*)uv__malloc(needed_buf_size);
2540   if (!buffer) {
2541     uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
2542   }
2543 
2544   /* Grab a pointer to the part of the buffer where filenames go */
2545   path_buf = (WCHAR*)&(buffer->MountPointReparseBuffer.PathBuffer);
2546   path_buf_len = 0;
2547 
2548   /* Copy the substitute (internal) target path */
2549   start = path_buf_len;
2550 
2551   wcsncpy((WCHAR*)&path_buf[path_buf_len], JUNCTION_PREFIX,
2552     JUNCTION_PREFIX_LEN);
2553   path_buf_len += JUNCTION_PREFIX_LEN;
2554 
2555   add_slash = 0;
2556   for (i = is_long_path ? LONG_PATH_PREFIX_LEN : 0; path[i] != L'\0'; i++) {
2557     if (IS_SLASH(path[i])) {
2558       add_slash = 1;
2559       continue;
2560     }
2561 
2562     if (add_slash) {
2563       path_buf[path_buf_len++] = L'\\';
2564       add_slash = 0;
2565     }
2566 
2567     path_buf[path_buf_len++] = path[i];
2568   }
2569   if (add_slash)
2570     path_buf[path_buf_len++] = L'\\';
2571   len = path_buf_len - start;
2572 
2573   /* Insert null terminator */
2574   path_buf[path_buf_len++] = L'\0';
2575 
2576   /* Set the info about the substitute name */
2577   buffer->MountPointReparseBuffer.SubstituteNameOffset = start * sizeof(WCHAR);
2578   buffer->MountPointReparseBuffer.SubstituteNameLength = len * sizeof(WCHAR);
2579 
2580   /* Copy the print name of the target path */
2581   start = path_buf_len;
2582   add_slash = 0;
2583   for (i = is_long_path ? LONG_PATH_PREFIX_LEN : 0; path[i] != L'\0'; i++) {
2584     if (IS_SLASH(path[i])) {
2585       add_slash = 1;
2586       continue;
2587     }
2588 
2589     if (add_slash) {
2590       path_buf[path_buf_len++] = L'\\';
2591       add_slash = 0;
2592     }
2593 
2594     path_buf[path_buf_len++] = path[i];
2595   }
2596   len = path_buf_len - start;
2597   if (len == 2 || add_slash) {
2598     path_buf[path_buf_len++] = L'\\';
2599     len++;
2600   }
2601 
2602   /* Insert another null terminator */
2603   path_buf[path_buf_len++] = L'\0';
2604 
2605   /* Set the info about the print name */
2606   buffer->MountPointReparseBuffer.PrintNameOffset = start * sizeof(WCHAR);
2607   buffer->MountPointReparseBuffer.PrintNameLength = len * sizeof(WCHAR);
2608 
2609   /* Calculate how much buffer space was actually used */
2610   used_buf_size = FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer) +
2611     path_buf_len * sizeof(WCHAR);
2612   used_data_size = used_buf_size -
2613     FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer);
2614 
2615   /* Put general info in the data buffer */
2616   buffer->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
2617   buffer->ReparseDataLength = used_data_size;
2618   buffer->Reserved = 0;
2619 
2620   /* Create a new directory */
2621   if (!CreateDirectoryW(new_path, NULL)) {
2622     SET_REQ_WIN32_ERROR(req, GetLastError());
2623     goto error;
2624   }
2625   created = 1;
2626 
2627   /* Open the directory */
2628   handle = CreateFileW(new_path,
2629                        GENERIC_WRITE,
2630                        0,
2631                        NULL,
2632                        OPEN_EXISTING,
2633                        FILE_FLAG_BACKUP_SEMANTICS |
2634                          FILE_FLAG_OPEN_REPARSE_POINT,
2635                        NULL);
2636   if (handle == INVALID_HANDLE_VALUE) {
2637     SET_REQ_WIN32_ERROR(req, GetLastError());
2638     goto error;
2639   }
2640 
2641   /* Create the actual reparse point */
2642   if (!DeviceIoControl(handle,
2643                        FSCTL_SET_REPARSE_POINT,
2644                        buffer,
2645                        used_buf_size,
2646                        NULL,
2647                        0,
2648                        &bytes,
2649                        NULL)) {
2650     SET_REQ_WIN32_ERROR(req, GetLastError());
2651     goto error;
2652   }
2653 
2654   /* Clean up */
2655   CloseHandle(handle);
2656   uv__free(buffer);
2657 
2658   SET_REQ_RESULT(req, 0);
2659   return;
2660 
2661 error:
2662   uv__free(buffer);
2663 
2664   if (handle != INVALID_HANDLE_VALUE) {
2665     CloseHandle(handle);
2666   }
2667 
2668   if (created) {
2669     RemoveDirectoryW(new_path);
2670   }
2671 }
2672 
2673 
fs__symlink(uv_fs_t * req)2674 static void fs__symlink(uv_fs_t* req) {
2675   WCHAR* pathw;
2676   WCHAR* new_pathw;
2677   int flags;
2678   int err;
2679 
2680   pathw = req->file.pathw;
2681   new_pathw = req->fs.info.new_pathw;
2682 
2683   if (req->fs.info.file_flags & UV_FS_SYMLINK_JUNCTION) {
2684     fs__create_junction(req, pathw, new_pathw);
2685     return;
2686   }
2687 
2688   if (req->fs.info.file_flags & UV_FS_SYMLINK_DIR)
2689     flags = SYMBOLIC_LINK_FLAG_DIRECTORY | uv__file_symlink_usermode_flag;
2690   else
2691     flags = uv__file_symlink_usermode_flag;
2692 
2693   if (CreateSymbolicLinkW(new_pathw, pathw, flags)) {
2694     SET_REQ_RESULT(req, 0);
2695     return;
2696   }
2697 
2698   /* Something went wrong. We will test if it is because of user-mode
2699    * symlinks.
2700    */
2701   err = GetLastError();
2702   if (err == ERROR_INVALID_PARAMETER &&
2703       flags & SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE) {
2704     /* This system does not support user-mode symlinks. We will clear the
2705      * unsupported flag and retry.
2706      */
2707     uv__file_symlink_usermode_flag = 0;
2708     fs__symlink(req);
2709   } else {
2710     SET_REQ_WIN32_ERROR(req, err);
2711   }
2712 }
2713 
2714 
fs__readlink(uv_fs_t * req)2715 static void fs__readlink(uv_fs_t* req) {
2716   HANDLE handle;
2717 
2718   handle = CreateFileW(req->file.pathw,
2719                        0,
2720                        0,
2721                        NULL,
2722                        OPEN_EXISTING,
2723                        FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
2724                        NULL);
2725 
2726   if (handle == INVALID_HANDLE_VALUE) {
2727     SET_REQ_WIN32_ERROR(req, GetLastError());
2728     return;
2729   }
2730 
2731   assert(req->ptr == NULL);
2732   if (fs__readlink_handle(handle, (char**) &req->ptr, NULL) != 0) {
2733     DWORD error = GetLastError();
2734     SET_REQ_WIN32_ERROR(req, error);
2735     if (error == ERROR_NOT_A_REPARSE_POINT)
2736       req->result = UV_EINVAL;
2737     CloseHandle(handle);
2738     return;
2739   }
2740 
2741   req->flags |= UV_FS_FREE_PTR;
2742   SET_REQ_RESULT(req, 0);
2743 
2744   CloseHandle(handle);
2745 }
2746 
2747 
fs__realpath_handle(HANDLE handle,char ** realpath_ptr)2748 static ssize_t fs__realpath_handle(HANDLE handle, char** realpath_ptr) {
2749   int r;
2750   DWORD w_realpath_len;
2751   WCHAR* w_realpath_ptr = NULL;
2752   WCHAR* w_realpath_buf;
2753 
2754   w_realpath_len = GetFinalPathNameByHandleW(handle, NULL, 0, VOLUME_NAME_DOS);
2755   if (w_realpath_len == 0) {
2756     return -1;
2757   }
2758 
2759   w_realpath_buf = uv__malloc((w_realpath_len + 1) * sizeof(WCHAR));
2760   if (w_realpath_buf == NULL) {
2761     SetLastError(ERROR_OUTOFMEMORY);
2762     return -1;
2763   }
2764   w_realpath_ptr = w_realpath_buf;
2765 
2766   if (GetFinalPathNameByHandleW(
2767           handle, w_realpath_ptr, w_realpath_len, VOLUME_NAME_DOS) == 0) {
2768     uv__free(w_realpath_buf);
2769     SetLastError(ERROR_INVALID_HANDLE);
2770     return -1;
2771   }
2772 
2773   /* convert UNC path to long path */
2774   if (wcsncmp(w_realpath_ptr,
2775               UNC_PATH_PREFIX,
2776               UNC_PATH_PREFIX_LEN) == 0) {
2777     w_realpath_ptr += 6;
2778     *w_realpath_ptr = L'\\';
2779     w_realpath_len -= 6;
2780   } else if (wcsncmp(w_realpath_ptr,
2781                       LONG_PATH_PREFIX,
2782                       LONG_PATH_PREFIX_LEN) == 0) {
2783     w_realpath_ptr += 4;
2784     w_realpath_len -= 4;
2785   } else {
2786     uv__free(w_realpath_buf);
2787     SetLastError(ERROR_INVALID_HANDLE);
2788     return -1;
2789   }
2790 
2791   assert(*realpath_ptr == NULL);
2792   r = uv_utf16_to_wtf8(w_realpath_ptr, w_realpath_len, realpath_ptr, NULL);
2793   uv__free(w_realpath_buf);
2794   return r;
2795 }
2796 
fs__realpath(uv_fs_t * req)2797 static void fs__realpath(uv_fs_t* req) {
2798   HANDLE handle;
2799 
2800   handle = CreateFileW(req->file.pathw,
2801                        0,
2802                        0,
2803                        NULL,
2804                        OPEN_EXISTING,
2805                        FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS,
2806                        NULL);
2807   if (handle == INVALID_HANDLE_VALUE) {
2808     SET_REQ_WIN32_ERROR(req, GetLastError());
2809     return;
2810   }
2811 
2812   assert(req->ptr == NULL);
2813   if (fs__realpath_handle(handle, (char**) &req->ptr) == -1) {
2814     CloseHandle(handle);
2815     SET_REQ_WIN32_ERROR(req, GetLastError());
2816     return;
2817   }
2818 
2819   CloseHandle(handle);
2820   req->flags |= UV_FS_FREE_PTR;
2821   SET_REQ_RESULT(req, 0);
2822 }
2823 
2824 
fs__chown(uv_fs_t * req)2825 static void fs__chown(uv_fs_t* req) {
2826   SET_REQ_RESULT(req, 0);
2827 }
2828 
2829 
fs__fchown(uv_fs_t * req)2830 static void fs__fchown(uv_fs_t* req) {
2831   SET_REQ_RESULT(req, 0);
2832 }
2833 
2834 
fs__lchown(uv_fs_t * req)2835 static void fs__lchown(uv_fs_t* req) {
2836   SET_REQ_RESULT(req, 0);
2837 }
2838 
2839 
fs__statfs(uv_fs_t * req)2840 static void fs__statfs(uv_fs_t* req) {
2841   uv_statfs_t* stat_fs;
2842   DWORD sectors_per_cluster;
2843   DWORD bytes_per_sector;
2844   DWORD free_clusters;
2845   DWORD total_clusters;
2846   WCHAR* pathw;
2847 
2848   pathw = req->file.pathw;
2849 retry_get_disk_free_space:
2850   if (0 == GetDiskFreeSpaceW(pathw,
2851                              &sectors_per_cluster,
2852                              &bytes_per_sector,
2853                              &free_clusters,
2854                              &total_clusters)) {
2855     DWORD err;
2856     WCHAR* fpart;
2857     size_t len;
2858     DWORD ret;
2859     BOOL is_second;
2860 
2861     err = GetLastError();
2862     is_second = pathw != req->file.pathw;
2863     if (err != ERROR_DIRECTORY || is_second) {
2864       if (is_second)
2865         uv__free(pathw);
2866 
2867       SET_REQ_WIN32_ERROR(req, err);
2868       return;
2869     }
2870 
2871     len = MAX_PATH + 1;
2872     pathw = uv__malloc(len * sizeof(*pathw));
2873     if (pathw == NULL) {
2874       SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
2875       return;
2876     }
2877 retry_get_full_path_name:
2878     ret = GetFullPathNameW(req->file.pathw,
2879                            len,
2880                            pathw,
2881                            &fpart);
2882     if (ret == 0) {
2883       uv__free(pathw);
2884       SET_REQ_WIN32_ERROR(req, err);
2885       return;
2886     } else if (ret > len) {
2887       len = ret;
2888       pathw = uv__reallocf(pathw, len * sizeof(*pathw));
2889       if (pathw == NULL) {
2890         SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
2891         return;
2892       }
2893       goto retry_get_full_path_name;
2894     }
2895     if (fpart != 0)
2896       *fpart = L'\0';
2897 
2898     goto retry_get_disk_free_space;
2899   }
2900   if (pathw != req->file.pathw) {
2901     uv__free(pathw);
2902   }
2903 
2904   stat_fs = uv__malloc(sizeof(*stat_fs));
2905   if (stat_fs == NULL) {
2906     SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
2907     return;
2908   }
2909 
2910   stat_fs->f_type = 0;
2911   stat_fs->f_bsize = bytes_per_sector * sectors_per_cluster;
2912   stat_fs->f_blocks = total_clusters;
2913   stat_fs->f_bfree = free_clusters;
2914   stat_fs->f_bavail = free_clusters;
2915   stat_fs->f_files = 0;
2916   stat_fs->f_ffree = 0;
2917   req->ptr = stat_fs;
2918   req->flags |= UV_FS_FREE_PTR;
2919   SET_REQ_RESULT(req, 0);
2920 }
2921 
2922 
uv__fs_work(struct uv__work * w)2923 static void uv__fs_work(struct uv__work* w) {
2924   uv_fs_t* req;
2925 
2926   req = container_of(w, uv_fs_t, work_req);
2927   assert(req->type == UV_FS);
2928 
2929 #define XX(uc, lc)  case UV_FS_##uc: fs__##lc(req); break;
2930   switch (req->fs_type) {
2931     XX(OPEN, open)
2932     XX(CLOSE, close)
2933     XX(READ, read)
2934     XX(WRITE, write)
2935     XX(COPYFILE, copyfile)
2936     XX(SENDFILE, sendfile)
2937     XX(STAT, stat)
2938     XX(LSTAT, lstat)
2939     XX(FSTAT, fstat)
2940     XX(FTRUNCATE, ftruncate)
2941     XX(UTIME, utime)
2942     XX(FUTIME, futime)
2943     XX(LUTIME, lutime)
2944     XX(ACCESS, access)
2945     XX(CHMOD, chmod)
2946     XX(FCHMOD, fchmod)
2947     XX(FSYNC, fsync)
2948     XX(FDATASYNC, fdatasync)
2949     XX(UNLINK, unlink)
2950     XX(RMDIR, rmdir)
2951     XX(MKDIR, mkdir)
2952     XX(MKDTEMP, mkdtemp)
2953     XX(MKSTEMP, mkstemp)
2954     XX(RENAME, rename)
2955     XX(SCANDIR, scandir)
2956     XX(READDIR, readdir)
2957     XX(OPENDIR, opendir)
2958     XX(CLOSEDIR, closedir)
2959     XX(LINK, link)
2960     XX(SYMLINK, symlink)
2961     XX(READLINK, readlink)
2962     XX(REALPATH, realpath)
2963     XX(CHOWN, chown)
2964     XX(FCHOWN, fchown)
2965     XX(LCHOWN, lchown)
2966     XX(STATFS, statfs)
2967     default:
2968       assert(!"bad uv_fs_type");
2969   }
2970 }
2971 
2972 
uv__fs_done(struct uv__work * w,int status)2973 static void uv__fs_done(struct uv__work* w, int status) {
2974   uv_fs_t* req;
2975 
2976   req = container_of(w, uv_fs_t, work_req);
2977   uv__req_unregister(req->loop);
2978 
2979   if (status == UV_ECANCELED) {
2980     assert(req->result == 0);
2981     SET_REQ_UV_ERROR(req, UV_ECANCELED, 0);
2982   }
2983 
2984   req->cb(req);
2985 }
2986 
2987 
uv_fs_req_cleanup(uv_fs_t * req)2988 void uv_fs_req_cleanup(uv_fs_t* req) {
2989   if (req == NULL)
2990     return;
2991 
2992   if (req->flags & UV_FS_CLEANEDUP)
2993     return;
2994 
2995   if (req->flags & UV_FS_FREE_PATHS)
2996     uv__free(req->file.pathw);
2997 
2998   if (req->flags & UV_FS_FREE_PTR) {
2999     if (req->fs_type == UV_FS_SCANDIR && req->ptr != NULL)
3000       uv__fs_scandir_cleanup(req);
3001     else if (req->fs_type == UV_FS_READDIR)
3002       uv__fs_readdir_cleanup(req);
3003     else
3004       uv__free(req->ptr);
3005   }
3006 
3007   if (req->fs.info.bufs != req->fs.info.bufsml)
3008     uv__free(req->fs.info.bufs);
3009 
3010   req->path = NULL;
3011   req->file.pathw = NULL;
3012   req->fs.info.new_pathw = NULL;
3013   req->fs.info.bufs = NULL;
3014   req->ptr = NULL;
3015 
3016   req->flags |= UV_FS_CLEANEDUP;
3017 }
3018 
3019 
uv_fs_open(uv_loop_t * loop,uv_fs_t * req,const char * path,int flags,int mode,uv_fs_cb cb)3020 int uv_fs_open(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags,
3021     int mode, uv_fs_cb cb) {
3022   int err;
3023 
3024   INIT(UV_FS_OPEN);
3025   err = fs__capture_path(req, path, NULL, cb != NULL);
3026   if (err) {
3027     SET_REQ_WIN32_ERROR(req, err);
3028     return req->result;
3029   }
3030 
3031   req->fs.info.file_flags = flags;
3032   req->fs.info.mode = mode;
3033   POST;
3034 }
3035 
3036 
uv_fs_close(uv_loop_t * loop,uv_fs_t * req,uv_file fd,uv_fs_cb cb)3037 int uv_fs_close(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) {
3038   INIT(UV_FS_CLOSE);
3039   req->file.fd = fd;
3040   POST;
3041 }
3042 
3043 
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)3044 int uv_fs_read(uv_loop_t* loop,
3045                uv_fs_t* req,
3046                uv_file fd,
3047                const uv_buf_t bufs[],
3048                unsigned int nbufs,
3049                int64_t offset,
3050                uv_fs_cb cb) {
3051   INIT(UV_FS_READ);
3052 
3053   if (bufs == NULL || nbufs == 0) {
3054     SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER);
3055     return UV_EINVAL;
3056   }
3057 
3058   req->file.fd = fd;
3059 
3060   req->fs.info.nbufs = nbufs;
3061   req->fs.info.bufs = req->fs.info.bufsml;
3062   if (nbufs > ARRAY_SIZE(req->fs.info.bufsml))
3063     req->fs.info.bufs = uv__malloc(nbufs * sizeof(*bufs));
3064 
3065   if (req->fs.info.bufs == NULL) {
3066     SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
3067     return UV_ENOMEM;
3068   }
3069 
3070   memcpy(req->fs.info.bufs, bufs, nbufs * sizeof(*bufs));
3071 
3072   req->fs.info.offset = offset;
3073   POST;
3074 }
3075 
3076 
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)3077 int uv_fs_write(uv_loop_t* loop,
3078                 uv_fs_t* req,
3079                 uv_file fd,
3080                 const uv_buf_t bufs[],
3081                 unsigned int nbufs,
3082                 int64_t offset,
3083                 uv_fs_cb cb) {
3084   INIT(UV_FS_WRITE);
3085 
3086   if (bufs == NULL || nbufs == 0) {
3087     SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER);
3088     return UV_EINVAL;
3089   }
3090 
3091   req->file.fd = fd;
3092 
3093   req->fs.info.nbufs = nbufs;
3094   req->fs.info.bufs = req->fs.info.bufsml;
3095   if (nbufs > ARRAY_SIZE(req->fs.info.bufsml))
3096     req->fs.info.bufs = uv__malloc(nbufs * sizeof(*bufs));
3097 
3098   if (req->fs.info.bufs == NULL) {
3099     SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
3100     return UV_ENOMEM;
3101   }
3102 
3103   memcpy(req->fs.info.bufs, bufs, nbufs * sizeof(*bufs));
3104 
3105   req->fs.info.offset = offset;
3106   POST;
3107 }
3108 
3109 
uv_fs_unlink(uv_loop_t * loop,uv_fs_t * req,const char * path,uv_fs_cb cb)3110 int uv_fs_unlink(uv_loop_t* loop, uv_fs_t* req, const char* path,
3111     uv_fs_cb cb) {
3112   int err;
3113 
3114   INIT(UV_FS_UNLINK);
3115   err = fs__capture_path(req, path, NULL, cb != NULL);
3116   if (err) {
3117     SET_REQ_WIN32_ERROR(req, err);
3118     return req->result;
3119   }
3120 
3121   POST;
3122 }
3123 
3124 
uv_fs_mkdir(uv_loop_t * loop,uv_fs_t * req,const char * path,int mode,uv_fs_cb cb)3125 int uv_fs_mkdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode,
3126     uv_fs_cb cb) {
3127   int err;
3128 
3129   INIT(UV_FS_MKDIR);
3130   err = fs__capture_path(req, path, NULL, cb != NULL);
3131   if (err) {
3132     SET_REQ_WIN32_ERROR(req, err);
3133     return req->result;
3134   }
3135 
3136   req->fs.info.mode = mode;
3137   POST;
3138 }
3139 
3140 
uv_fs_mkdtemp(uv_loop_t * loop,uv_fs_t * req,const char * tpl,uv_fs_cb cb)3141 int uv_fs_mkdtemp(uv_loop_t* loop,
3142                   uv_fs_t* req,
3143                   const char* tpl,
3144                   uv_fs_cb cb) {
3145   int err;
3146 
3147   INIT(UV_FS_MKDTEMP);
3148   err = fs__capture_path(req, tpl, NULL, TRUE);
3149   if (err) {
3150     SET_REQ_WIN32_ERROR(req, err);
3151     return req->result;
3152   }
3153 
3154   POST;
3155 }
3156 
3157 
uv_fs_mkstemp(uv_loop_t * loop,uv_fs_t * req,const char * tpl,uv_fs_cb cb)3158 int uv_fs_mkstemp(uv_loop_t* loop,
3159                   uv_fs_t* req,
3160                   const char* tpl,
3161                   uv_fs_cb cb) {
3162   int err;
3163 
3164   INIT(UV_FS_MKSTEMP);
3165   err = fs__capture_path(req, tpl, NULL, TRUE);
3166   if (err) {
3167     SET_REQ_WIN32_ERROR(req, err);
3168     return req->result;
3169   }
3170 
3171   POST;
3172 }
3173 
3174 
uv_fs_rmdir(uv_loop_t * loop,uv_fs_t * req,const char * path,uv_fs_cb cb)3175 int uv_fs_rmdir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) {
3176   int err;
3177 
3178   INIT(UV_FS_RMDIR);
3179   err = fs__capture_path(req, path, NULL, cb != NULL);
3180   if (err) {
3181     SET_REQ_WIN32_ERROR(req, err);
3182     return req->result;
3183   }
3184 
3185   POST;
3186 }
3187 
3188 
uv_fs_scandir(uv_loop_t * loop,uv_fs_t * req,const char * path,int flags,uv_fs_cb cb)3189 int uv_fs_scandir(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags,
3190     uv_fs_cb cb) {
3191   int err;
3192 
3193   INIT(UV_FS_SCANDIR);
3194   err = fs__capture_path(req, path, NULL, cb != NULL);
3195   if (err) {
3196     SET_REQ_WIN32_ERROR(req, err);
3197     return req->result;
3198   }
3199 
3200   req->fs.info.file_flags = flags;
3201   POST;
3202 }
3203 
uv_fs_opendir(uv_loop_t * loop,uv_fs_t * req,const char * path,uv_fs_cb cb)3204 int uv_fs_opendir(uv_loop_t* loop,
3205                   uv_fs_t* req,
3206                   const char* path,
3207                   uv_fs_cb cb) {
3208   int err;
3209 
3210   INIT(UV_FS_OPENDIR);
3211   err = fs__capture_path(req, path, NULL, cb != NULL);
3212   if (err) {
3213     SET_REQ_WIN32_ERROR(req, err);
3214     return req->result;
3215   }
3216   POST;
3217 }
3218 
uv_fs_readdir(uv_loop_t * loop,uv_fs_t * req,uv_dir_t * dir,uv_fs_cb cb)3219 int uv_fs_readdir(uv_loop_t* loop,
3220                   uv_fs_t* req,
3221                   uv_dir_t* dir,
3222                   uv_fs_cb cb) {
3223   INIT(UV_FS_READDIR);
3224 
3225   if (dir == NULL ||
3226       dir->dirents == NULL ||
3227       dir->dir_handle == INVALID_HANDLE_VALUE) {
3228     SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER);
3229     return UV_EINVAL;
3230   }
3231 
3232   req->ptr = dir;
3233   POST;
3234 }
3235 
uv_fs_closedir(uv_loop_t * loop,uv_fs_t * req,uv_dir_t * dir,uv_fs_cb cb)3236 int uv_fs_closedir(uv_loop_t* loop,
3237                    uv_fs_t* req,
3238                    uv_dir_t* dir,
3239                    uv_fs_cb cb) {
3240   INIT(UV_FS_CLOSEDIR);
3241   if (dir == NULL) {
3242     SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER);
3243     return UV_EINVAL;
3244   }
3245   req->ptr = dir;
3246   POST;
3247 }
3248 
uv_fs_link(uv_loop_t * loop,uv_fs_t * req,const char * path,const char * new_path,uv_fs_cb cb)3249 int uv_fs_link(uv_loop_t* loop, uv_fs_t* req, const char* path,
3250     const char* new_path, uv_fs_cb cb) {
3251   int err;
3252 
3253   INIT(UV_FS_LINK);
3254   err = fs__capture_path(req, path, new_path, cb != NULL);
3255   if (err) {
3256     SET_REQ_WIN32_ERROR(req, err);
3257     return req->result;
3258   }
3259 
3260   POST;
3261 }
3262 
3263 
uv_fs_symlink(uv_loop_t * loop,uv_fs_t * req,const char * path,const char * new_path,int flags,uv_fs_cb cb)3264 int uv_fs_symlink(uv_loop_t* loop, uv_fs_t* req, const char* path,
3265     const char* new_path, int flags, uv_fs_cb cb) {
3266   int err;
3267 
3268   INIT(UV_FS_SYMLINK);
3269   err = fs__capture_path(req, path, new_path, cb != NULL);
3270   if (err) {
3271     SET_REQ_WIN32_ERROR(req, err);
3272     return req->result;
3273   }
3274 
3275   req->fs.info.file_flags = flags;
3276   POST;
3277 }
3278 
3279 
uv_fs_readlink(uv_loop_t * loop,uv_fs_t * req,const char * path,uv_fs_cb cb)3280 int uv_fs_readlink(uv_loop_t* loop, uv_fs_t* req, const char* path,
3281     uv_fs_cb cb) {
3282   int err;
3283 
3284   INIT(UV_FS_READLINK);
3285   err = fs__capture_path(req, path, NULL, cb != NULL);
3286   if (err) {
3287     SET_REQ_WIN32_ERROR(req, err);
3288     return req->result;
3289   }
3290 
3291   POST;
3292 }
3293 
3294 
uv_fs_realpath(uv_loop_t * loop,uv_fs_t * req,const char * path,uv_fs_cb cb)3295 int uv_fs_realpath(uv_loop_t* loop, uv_fs_t* req, const char* path,
3296     uv_fs_cb cb) {
3297   int err;
3298 
3299   INIT(UV_FS_REALPATH);
3300 
3301   if (!path) {
3302     SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER);
3303     return UV_EINVAL;
3304   }
3305 
3306   err = fs__capture_path(req, path, NULL, cb != NULL);
3307   if (err) {
3308     SET_REQ_WIN32_ERROR(req, err);
3309     return req->result;
3310   }
3311 
3312   POST;
3313 }
3314 
3315 
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)3316 int uv_fs_chown(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid,
3317     uv_gid_t gid, uv_fs_cb cb) {
3318   int err;
3319 
3320   INIT(UV_FS_CHOWN);
3321   err = fs__capture_path(req, path, NULL, cb != NULL);
3322   if (err) {
3323     SET_REQ_WIN32_ERROR(req, err);
3324     return req->result;
3325   }
3326 
3327   POST;
3328 }
3329 
3330 
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)3331 int uv_fs_fchown(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_uid_t uid,
3332     uv_gid_t gid, uv_fs_cb cb) {
3333   INIT(UV_FS_FCHOWN);
3334   POST;
3335 }
3336 
3337 
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)3338 int uv_fs_lchown(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid,
3339     uv_gid_t gid, uv_fs_cb cb) {
3340   int err;
3341 
3342   INIT(UV_FS_LCHOWN);
3343   err = fs__capture_path(req, path, NULL, cb != NULL);
3344   if (err) {
3345     SET_REQ_WIN32_ERROR(req, err);
3346     return req->result;
3347   }
3348 
3349   POST;
3350 }
3351 
3352 
uv_fs_stat(uv_loop_t * loop,uv_fs_t * req,const char * path,uv_fs_cb cb)3353 int uv_fs_stat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) {
3354   int err;
3355 
3356   INIT(UV_FS_STAT);
3357   err = fs__capture_path(req, path, NULL, cb != NULL);
3358   if (err) {
3359     SET_REQ_WIN32_ERROR(req, err);
3360     return req->result;
3361   }
3362 
3363   POST;
3364 }
3365 
3366 
uv_fs_lstat(uv_loop_t * loop,uv_fs_t * req,const char * path,uv_fs_cb cb)3367 int uv_fs_lstat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) {
3368   int err;
3369 
3370   INIT(UV_FS_LSTAT);
3371   err = fs__capture_path(req, path, NULL, cb != NULL);
3372   if (err) {
3373     SET_REQ_WIN32_ERROR(req, err);
3374     return req->result;
3375   }
3376 
3377   POST;
3378 }
3379 
3380 
uv_fs_fstat(uv_loop_t * loop,uv_fs_t * req,uv_file fd,uv_fs_cb cb)3381 int uv_fs_fstat(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) {
3382   INIT(UV_FS_FSTAT);
3383   req->file.fd = fd;
3384   POST;
3385 }
3386 
3387 
uv_fs_rename(uv_loop_t * loop,uv_fs_t * req,const char * path,const char * new_path,uv_fs_cb cb)3388 int uv_fs_rename(uv_loop_t* loop, uv_fs_t* req, const char* path,
3389     const char* new_path, uv_fs_cb cb) {
3390   int err;
3391 
3392   INIT(UV_FS_RENAME);
3393   err = fs__capture_path(req, path, new_path, cb != NULL);
3394   if (err) {
3395     SET_REQ_WIN32_ERROR(req, err);
3396     return req->result;
3397   }
3398 
3399   POST;
3400 }
3401 
3402 
uv_fs_fsync(uv_loop_t * loop,uv_fs_t * req,uv_file fd,uv_fs_cb cb)3403 int uv_fs_fsync(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) {
3404   INIT(UV_FS_FSYNC);
3405   req->file.fd = fd;
3406   POST;
3407 }
3408 
3409 
uv_fs_fdatasync(uv_loop_t * loop,uv_fs_t * req,uv_file fd,uv_fs_cb cb)3410 int uv_fs_fdatasync(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) {
3411   INIT(UV_FS_FDATASYNC);
3412   req->file.fd = fd;
3413   POST;
3414 }
3415 
3416 
uv_fs_ftruncate(uv_loop_t * loop,uv_fs_t * req,uv_file fd,int64_t offset,uv_fs_cb cb)3417 int uv_fs_ftruncate(uv_loop_t* loop, uv_fs_t* req, uv_file fd,
3418     int64_t offset, uv_fs_cb cb) {
3419   INIT(UV_FS_FTRUNCATE);
3420   req->file.fd = fd;
3421   req->fs.info.offset = offset;
3422   POST;
3423 }
3424 
3425 
uv_fs_copyfile(uv_loop_t * loop,uv_fs_t * req,const char * path,const char * new_path,int flags,uv_fs_cb cb)3426 int uv_fs_copyfile(uv_loop_t* loop,
3427                    uv_fs_t* req,
3428                    const char* path,
3429                    const char* new_path,
3430                    int flags,
3431                    uv_fs_cb cb) {
3432   int err;
3433 
3434   INIT(UV_FS_COPYFILE);
3435 
3436   if (flags & ~(UV_FS_COPYFILE_EXCL |
3437                 UV_FS_COPYFILE_FICLONE |
3438                 UV_FS_COPYFILE_FICLONE_FORCE)) {
3439     SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER);
3440     return UV_EINVAL;
3441   }
3442 
3443   err = fs__capture_path(req, path, new_path, cb != NULL);
3444   if (err) {
3445     SET_REQ_WIN32_ERROR(req, err);
3446     return req->result;
3447   }
3448 
3449   req->fs.info.file_flags = flags;
3450   POST;
3451 }
3452 
3453 
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)3454 int uv_fs_sendfile(uv_loop_t* loop, uv_fs_t* req, uv_file fd_out,
3455     uv_file fd_in, int64_t in_offset, size_t length, uv_fs_cb cb) {
3456   INIT(UV_FS_SENDFILE);
3457   req->file.fd = fd_in;
3458   req->fs.info.fd_out = fd_out;
3459   req->fs.info.offset = in_offset;
3460   req->fs.info.bufsml[0].len = length;
3461   POST;
3462 }
3463 
3464 
uv_fs_access(uv_loop_t * loop,uv_fs_t * req,const char * path,int flags,uv_fs_cb cb)3465 int uv_fs_access(uv_loop_t* loop,
3466                  uv_fs_t* req,
3467                  const char* path,
3468                  int flags,
3469                  uv_fs_cb cb) {
3470   int err;
3471 
3472   INIT(UV_FS_ACCESS);
3473   err = fs__capture_path(req, path, NULL, cb != NULL);
3474   if (err) {
3475     SET_REQ_WIN32_ERROR(req, err);
3476     return req->result;
3477   }
3478 
3479   req->fs.info.mode = flags;
3480   POST;
3481 }
3482 
3483 
uv_fs_chmod(uv_loop_t * loop,uv_fs_t * req,const char * path,int mode,uv_fs_cb cb)3484 int uv_fs_chmod(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode,
3485     uv_fs_cb cb) {
3486   int err;
3487 
3488   INIT(UV_FS_CHMOD);
3489   err = fs__capture_path(req, path, NULL, cb != NULL);
3490   if (err) {
3491     SET_REQ_WIN32_ERROR(req, err);
3492     return req->result;
3493   }
3494 
3495   req->fs.info.mode = mode;
3496   POST;
3497 }
3498 
3499 
uv_fs_fchmod(uv_loop_t * loop,uv_fs_t * req,uv_file fd,int mode,uv_fs_cb cb)3500 int uv_fs_fchmod(uv_loop_t* loop, uv_fs_t* req, uv_file fd, int mode,
3501     uv_fs_cb cb) {
3502   INIT(UV_FS_FCHMOD);
3503   req->file.fd = fd;
3504   req->fs.info.mode = mode;
3505   POST;
3506 }
3507 
3508 
uv_fs_utime(uv_loop_t * loop,uv_fs_t * req,const char * path,double atime,double mtime,uv_fs_cb cb)3509 int uv_fs_utime(uv_loop_t* loop, uv_fs_t* req, const char* path, double atime,
3510     double mtime, uv_fs_cb cb) {
3511   int err;
3512 
3513   INIT(UV_FS_UTIME);
3514   err = fs__capture_path(req, path, NULL, cb != NULL);
3515   if (err) {
3516     SET_REQ_WIN32_ERROR(req, err);
3517     return req->result;
3518   }
3519 
3520   req->fs.time.atime = atime;
3521   req->fs.time.mtime = mtime;
3522   POST;
3523 }
3524 
3525 
uv_fs_futime(uv_loop_t * loop,uv_fs_t * req,uv_file fd,double atime,double mtime,uv_fs_cb cb)3526 int uv_fs_futime(uv_loop_t* loop, uv_fs_t* req, uv_file fd, double atime,
3527     double mtime, uv_fs_cb cb) {
3528   INIT(UV_FS_FUTIME);
3529   req->file.fd = fd;
3530   req->fs.time.atime = atime;
3531   req->fs.time.mtime = mtime;
3532   POST;
3533 }
3534 
uv_fs_lutime(uv_loop_t * loop,uv_fs_t * req,const char * path,double atime,double mtime,uv_fs_cb cb)3535 int uv_fs_lutime(uv_loop_t* loop, uv_fs_t* req, const char* path, double atime,
3536     double mtime, uv_fs_cb cb) {
3537   int err;
3538 
3539   INIT(UV_FS_LUTIME);
3540   err = fs__capture_path(req, path, NULL, cb != NULL);
3541   if (err) {
3542     SET_REQ_WIN32_ERROR(req, err);
3543     return req->result;
3544   }
3545 
3546   req->fs.time.atime = atime;
3547   req->fs.time.mtime = mtime;
3548   POST;
3549 }
3550 
3551 
uv_fs_statfs(uv_loop_t * loop,uv_fs_t * req,const char * path,uv_fs_cb cb)3552 int uv_fs_statfs(uv_loop_t* loop,
3553                  uv_fs_t* req,
3554                  const char* path,
3555                  uv_fs_cb cb) {
3556   int err;
3557 
3558   INIT(UV_FS_STATFS);
3559   err = fs__capture_path(req, path, NULL, cb != NULL);
3560   if (err) {
3561     SET_REQ_WIN32_ERROR(req, err);
3562     return req->result;
3563   }
3564 
3565   POST;
3566 }
3567 
uv_fs_get_system_error(const uv_fs_t * req)3568 int uv_fs_get_system_error(const uv_fs_t* req) {
3569   return req->sys_errno_;
3570 }
3571