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