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