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