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