comparison third_party/libuv/src/win/fs.c @ 160:948de3f54cea

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