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