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