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