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