PageRenderTime 138ms CodeModel.GetById 13ms app.highlight 113ms RepoModel.GetById 1ms app.codeStats 0ms

/src/win/fs.c

http://github.com/joyent/libuv
C | 1951 lines | 1484 code | 382 blank | 85 comment | 358 complexity | 3490ff24b3beb8ba60b467147e8ed109 MD5 | raw file
   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 <malloc.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
  37
  38#define UV_FS_FREE_PATHS         0x0002
  39#define UV_FS_FREE_PTR           0x0008
  40#define UV_FS_CLEANEDUP          0x0010
  41
  42
  43#define QUEUE_FS_TP_JOB(loop, req)                                          \
  44  if (!QueueUserWorkItem(&uv_fs_thread_proc,                                \
  45                         req,                                               \
  46                         WT_EXECUTEDEFAULT)) {                              \
  47    uv__set_sys_error((loop), GetLastError());                              \
  48    return -1;                                                              \
  49  }                                                                         \
  50  uv__req_register(loop, req);
  51
  52#define SET_UV_LAST_ERROR_FROM_REQ(req)                                     \
  53  uv__set_error(req->loop, req->errorno, req->sys_errno_);
  54
  55#define SET_REQ_RESULT(req, result_value)                                   \
  56  req->result = (result_value);                                             \
  57  if (req->result == -1) {                                                  \
  58    req->sys_errno_ = _doserrno;                                            \
  59    req->errorno = uv_translate_sys_error(req->sys_errno_);                 \
  60  }
  61
  62#define SET_REQ_WIN32_ERROR(req, sys_errno)                                 \
  63  req->result = -1;                                                         \
  64  req->sys_errno_ = (sys_errno);                                            \
  65  req->errorno = uv_translate_sys_error(req->sys_errno_);
  66
  67#define SET_REQ_UV_ERROR(req, uv_errno, sys_errno)                          \
  68  req->result = -1;                                                         \
  69  req->sys_errno_ = (sys_errno);                                            \
  70  req->errorno = (uv_errno);
  71
  72#define VERIFY_FD(fd, req)                                                  \
  73  if (fd == -1) {                                                           \
  74    req->result = -1;                                                       \
  75    req->errorno = UV_EBADF;                                                \
  76    req->sys_errno_ = ERROR_INVALID_HANDLE;                                 \
  77    return;                                                                 \
  78  }
  79
  80#define FILETIME_TO_TIME_T(filetime)                                        \
  81   ((*((uint64_t*) &(filetime)) - 116444736000000000ULL) / 10000000ULL);
  82
  83#define TIME_T_TO_FILETIME(time, filetime_ptr)                              \
  84  do {                                                                      \
  85    *(uint64_t*) (filetime_ptr) = ((int64_t) (time) * 10000000LL) +         \
  86                                  116444736000000000ULL;                    \
  87  } while(0)
  88
  89
  90#define IS_SLASH(c) ((c) == L'\\' || (c) == L'/')
  91#define IS_LETTER(c) (((c) >= L'a' && (c) <= L'z') || \
  92  ((c) >= L'A' && (c) <= L'Z'))
  93
  94const WCHAR JUNCTION_PREFIX[] = L"\\??\\";
  95const WCHAR JUNCTION_PREFIX_LEN = 4;
  96
  97const WCHAR LONG_PATH_PREFIX[] = L"\\\\?\\";
  98const WCHAR LONG_PATH_PREFIX_LEN = 4;
  99
 100
 101void uv_fs_init() {
 102  _fmode = _O_BINARY;
 103}
 104
 105
 106INLINE static int fs__capture_path(uv_loop_t* loop, uv_fs_t* req,
 107    const char* path, const char* new_path, const int copy_path) {
 108  char* buf;
 109  char* pos;
 110  ssize_t buf_sz = 0, path_len, pathw_len, new_pathw_len;
 111
 112  /* new_path can only be set if path is also set. */
 113  assert(new_path == NULL || path != NULL);
 114
 115  if (path != NULL) {
 116    pathw_len = MultiByteToWideChar(CP_UTF8,
 117                                    0,
 118                                    path,
 119                                    -1,
 120                                    NULL,
 121                                    0);
 122    if (pathw_len == 0) {
 123      uv__set_sys_error(loop, GetLastError());
 124      return -1;
 125    }
 126
 127    buf_sz += pathw_len * sizeof(WCHAR);
 128  }
 129
 130  if (path != NULL && copy_path) {
 131    path_len = 1 + strlen(path);
 132    buf_sz += path_len;
 133  }
 134
 135  if (new_path != NULL) {
 136    new_pathw_len = MultiByteToWideChar(CP_UTF8,
 137                                        0,
 138                                        new_path,
 139                                        -1,
 140                                        NULL,
 141                                        0);
 142    if (new_pathw_len == 0) {
 143      uv__set_sys_error(loop, GetLastError());
 144      return -1;
 145    }
 146
 147    buf_sz += new_pathw_len * sizeof(WCHAR);
 148  }
 149
 150
 151  if (buf_sz == 0) {
 152    req->pathw = NULL;
 153    req->new_pathw = NULL;
 154    req->path = NULL;
 155    return 0;
 156  }
 157
 158  buf = (char*) malloc(buf_sz);
 159  if (buf == NULL) {
 160    uv__set_artificial_error(loop, UV_ENOMEM);
 161    return -1;
 162  }
 163
 164  pos = buf;
 165
 166  if (path != NULL) {
 167    DWORD r = MultiByteToWideChar(CP_UTF8,
 168                                  0,
 169                                  path,
 170                                  -1,
 171                                  (WCHAR*) pos,
 172                                  pathw_len);
 173    assert(r == pathw_len);
 174    req->pathw = (WCHAR*) pos;
 175    pos += r * sizeof(WCHAR);
 176  } else {
 177    req->pathw = NULL;
 178  }
 179
 180  if (new_path != NULL) {
 181    DWORD r = MultiByteToWideChar(CP_UTF8,
 182                                  0,
 183                                  new_path,
 184                                  -1,
 185                                  (WCHAR*) pos,
 186                                  new_pathw_len);
 187    assert(r == new_pathw_len);
 188    req->new_pathw = (WCHAR*) pos;
 189    pos += r * sizeof(WCHAR);
 190  } else {
 191    req->new_pathw = NULL;
 192  }
 193
 194  if (!copy_path) {
 195    req->path = path;
 196  } else if (path) {
 197    memcpy(pos, path, path_len);
 198    assert(path_len == buf_sz - (pos - buf));
 199    req->path = pos;
 200  } else {
 201    req->path = NULL;
 202  }
 203
 204  req->flags |= UV_FS_FREE_PATHS;
 205
 206  return 0;
 207}
 208
 209
 210
 211INLINE static void uv_fs_req_init(uv_loop_t* loop, uv_fs_t* req,
 212    uv_fs_type fs_type, const uv_fs_cb cb) {
 213  uv_req_init(loop, (uv_req_t*) req);
 214
 215  req->type = UV_FS;
 216  req->loop = loop;
 217  req->flags = 0;
 218  req->fs_type = fs_type;
 219  req->result = 0;
 220  req->ptr = NULL;
 221  req->errorno = UV_OK;
 222  req->path = NULL;
 223
 224  if (cb != NULL) {
 225    req->cb = cb;
 226    memset(&req->overlapped, 0, sizeof(req->overlapped));
 227  }
 228}
 229
 230
 231static int is_path_dir(const WCHAR* path) {
 232  DWORD attr = GetFileAttributesW(path);
 233
 234  if (attr != INVALID_FILE_ATTRIBUTES) {
 235    return attr & FILE_ATTRIBUTE_DIRECTORY ? 1 : 0;
 236  } else {
 237    return 0;
 238  }
 239}
 240
 241
 242INLINE static int fs__readlink_handle(HANDLE handle, char** target_ptr,
 243    int64_t* target_len_ptr) {
 244  char buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
 245  REPARSE_DATA_BUFFER* reparse_data = (REPARSE_DATA_BUFFER*) buffer;
 246  WCHAR *w_target;
 247  DWORD w_target_len;
 248  char* target;
 249  int target_len;
 250  DWORD bytes;
 251
 252  if (!DeviceIoControl(handle,
 253                       FSCTL_GET_REPARSE_POINT,
 254                       NULL,
 255                       0,
 256                       buffer,
 257                       sizeof buffer,
 258                       &bytes,
 259                       NULL)) {
 260    return -1;
 261  }
 262
 263  if (reparse_data->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
 264    /* Real symlink */
 265    w_target = reparse_data->SymbolicLinkReparseBuffer.PathBuffer +
 266        (reparse_data->SymbolicLinkReparseBuffer.SubstituteNameOffset /
 267        sizeof(WCHAR));
 268    w_target_len =
 269        reparse_data->SymbolicLinkReparseBuffer.SubstituteNameLength /
 270        sizeof(WCHAR);
 271
 272    /* Real symlinks can contain pretty much everything, but the only thing */
 273    /* we really care about is undoing the implicit conversion to an NT */
 274    /* namespaced path that CreateSymbolicLink will perform on absolute */
 275    /* paths. If the path is win32-namespaced then the user must have */
 276    /* explicitly made it so, and we better just return the unmodified */
 277    /* reparse data. */
 278    if (w_target_len >= 4 &&
 279        w_target[0] == L'\\' &&
 280        w_target[1] == L'?' &&
 281        w_target[2] == L'?' &&
 282        w_target[3] == L'\\') {
 283      /* Starts with \??\ */
 284      if (w_target_len >= 6 &&
 285          ((w_target[4] >= L'A' && w_target[4] <= L'Z') ||
 286           (w_target[4] >= L'a' && w_target[4] <= L'z')) &&
 287          w_target[5] == L':' &&
 288          (w_target_len == 6 || w_target[6] == L'\\')) {
 289        /* \??\?drive?:\ */
 290        w_target += 4;
 291        w_target_len -= 4;
 292
 293      } else if (w_target_len >= 8 &&
 294                 (w_target[4] == L'U' || w_target[4] == L'u') &&
 295                 (w_target[5] == L'N' || w_target[5] == L'n') &&
 296                 (w_target[6] == L'C' || w_target[6] == L'c') &&
 297                 w_target[7] == L'\\') {
 298        /* \??\UNC\?server?\?share?\ - make sure the final path looks like */
 299        /* \\?server?\?share?\ */
 300        w_target += 6;
 301        w_target[0] = L'\\';
 302        w_target_len -= 6;
 303      }
 304    }
 305
 306  } else if (reparse_data->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) {
 307    /* Junction. */
 308    w_target = reparse_data->MountPointReparseBuffer.PathBuffer +
 309        (reparse_data->MountPointReparseBuffer.SubstituteNameOffset /
 310        sizeof(WCHAR));
 311    w_target_len = reparse_data->MountPointReparseBuffer.SubstituteNameLength /
 312        sizeof(WCHAR);
 313
 314    /* Only treat junctions that look like \??\?drive?:\ as symlink. */
 315    /* Junctions can also be used as mount points, like \??\Volume{?guid?}, */
 316    /* but that's confusing for programs since they wouldn't be able to */
 317    /* actually understand such a path when returned by uv_readlink(). */
 318    /* UNC paths are never valid for junctions so we don't care about them. */
 319    if (!(w_target_len >= 6 &&
 320          w_target[0] == L'\\' &&
 321          w_target[1] == L'?' &&
 322          w_target[2] == L'?' &&
 323          w_target[3] == L'\\' &&
 324          ((w_target[4] >= L'A' && w_target[4] <= L'Z') ||
 325           (w_target[4] >= L'a' && w_target[4] <= L'z')) &&
 326          w_target[5] == L':' &&
 327          (w_target_len == 6 || w_target[6] == L'\\'))) {
 328      SetLastError(ERROR_SYMLINK_NOT_SUPPORTED);
 329      return -1;
 330    }
 331
 332    /* Remove leading \??\ */
 333    w_target += 4;
 334    w_target_len -= 4;
 335
 336  } else {
 337    /* Reparse tag does not indicate a symlink. */
 338    SetLastError(ERROR_SYMLINK_NOT_SUPPORTED);
 339    return -1;
 340  }
 341
 342  /* If needed, compute the length of the target. */
 343  if (target_ptr != NULL || target_len_ptr != NULL) {
 344    /* Compute the length of the target. */
 345    target_len = WideCharToMultiByte(CP_UTF8,
 346                                     0,
 347                                     w_target,
 348                                     w_target_len,
 349                                     NULL,
 350                                     0,
 351                                     NULL,
 352                                     NULL);
 353    if (target_len == 0) {
 354      return -1;
 355    }
 356  }
 357
 358  /* If requested, allocate memory and convert to UTF8. */
 359  if (target_ptr != NULL) {
 360    int r;
 361    target = (char*) malloc(target_len + 1);
 362    if (target == NULL) {
 363      SetLastError(ERROR_OUTOFMEMORY);
 364      return -1;
 365    }
 366
 367    r = WideCharToMultiByte(CP_UTF8,
 368                            0,
 369                            w_target,
 370                            w_target_len,
 371                            target,
 372                            target_len,
 373                            NULL,
 374                            NULL);
 375    assert(r == target_len);
 376    target[target_len] = '\0';
 377
 378    *target_ptr = target;
 379  }
 380
 381  if (target_len_ptr != NULL) {
 382    *target_len_ptr = target_len;
 383  }
 384
 385  return 0;
 386}
 387
 388
 389void fs__open(uv_fs_t* req) {
 390  DWORD access;
 391  DWORD share;
 392  DWORD disposition;
 393  DWORD attributes = 0;
 394  HANDLE file;
 395  int result, current_umask;
 396  int flags = req->file_flags;
 397
 398  /* Obtain the active umask. umask() never fails and returns the previous */
 399  /* umask. */
 400  current_umask = umask(0);
 401  umask(current_umask);
 402
 403  /* convert flags and mode to CreateFile parameters */
 404  switch (flags & (_O_RDONLY | _O_WRONLY | _O_RDWR)) {
 405  case _O_RDONLY:
 406    access = FILE_GENERIC_READ;
 407    attributes |= FILE_FLAG_BACKUP_SEMANTICS;
 408    break;
 409  case _O_WRONLY:
 410    access = FILE_GENERIC_WRITE;
 411    break;
 412  case _O_RDWR:
 413    access = FILE_GENERIC_READ | FILE_GENERIC_WRITE;
 414    break;
 415  default:
 416    result  = -1;
 417    goto end;
 418  }
 419
 420  if (flags & _O_APPEND) {
 421    access &= ~FILE_WRITE_DATA;
 422    access |= FILE_APPEND_DATA;
 423    attributes &= ~FILE_FLAG_BACKUP_SEMANTICS;
 424  }
 425
 426  /*
 427   * Here is where we deviate significantly from what CRT's _open()
 428   * does. We indiscriminately use all the sharing modes, to match
 429   * UNIX semantics. In particular, this ensures that the file can
 430   * be deleted even whilst it's open, fixing issue #1449.
 431   */
 432  share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
 433
 434  switch (flags & (_O_CREAT | _O_EXCL | _O_TRUNC)) {
 435  case 0:
 436  case _O_EXCL:
 437    disposition = OPEN_EXISTING;
 438    break;
 439  case _O_CREAT:
 440    disposition = OPEN_ALWAYS;
 441    break;
 442  case _O_CREAT | _O_EXCL:
 443  case _O_CREAT | _O_TRUNC | _O_EXCL:
 444    disposition = CREATE_NEW;
 445    break;
 446  case _O_TRUNC:
 447  case _O_TRUNC | _O_EXCL:
 448    disposition = TRUNCATE_EXISTING;
 449    break;
 450  case _O_CREAT | _O_TRUNC:
 451    disposition = CREATE_ALWAYS;
 452    break;
 453  default:
 454    result = -1;
 455    goto end;
 456  }
 457
 458  attributes |= FILE_ATTRIBUTE_NORMAL;
 459  if (flags & _O_CREAT) {
 460    if (!((req->mode & ~current_umask) & _S_IWRITE)) {
 461      attributes |= FILE_ATTRIBUTE_READONLY;
 462    }
 463  }
 464
 465  if (flags & _O_TEMPORARY ) {
 466    attributes |= FILE_FLAG_DELETE_ON_CLOSE | FILE_ATTRIBUTE_TEMPORARY;
 467    access |= DELETE;
 468  }
 469
 470  if (flags & _O_SHORT_LIVED) {
 471    attributes |= FILE_ATTRIBUTE_TEMPORARY;
 472  }
 473
 474  switch (flags & (_O_SEQUENTIAL | _O_RANDOM)) {
 475  case 0:
 476    break;
 477  case _O_SEQUENTIAL:
 478    attributes |= FILE_FLAG_SEQUENTIAL_SCAN;
 479    break;
 480  case _O_RANDOM:
 481    attributes |= FILE_FLAG_RANDOM_ACCESS;
 482    break;
 483  default:
 484    result = -1;
 485    goto end;
 486  }
 487
 488  /* Setting this flag makes it possible to open a directory. */
 489  attributes |= FILE_FLAG_BACKUP_SEMANTICS;
 490
 491  file = CreateFileW(req->pathw,
 492                     access,
 493                     share,
 494                     NULL,
 495                     disposition,
 496                     attributes,
 497                     NULL);
 498  if (file == INVALID_HANDLE_VALUE) {
 499    DWORD error = GetLastError();
 500    if (error == ERROR_FILE_EXISTS && (flags & _O_CREAT) &&
 501        !(flags & _O_EXCL)) {
 502      /* Special case: when ERROR_FILE_EXISTS happens and O_CREAT was */
 503      /* specified, it means the path referred to a directory. */
 504      SET_REQ_UV_ERROR(req, UV_EISDIR, error);
 505    } else {
 506      SET_REQ_WIN32_ERROR(req, GetLastError());
 507    }
 508    return;
 509  }
 510  result = _open_osfhandle((intptr_t) file, flags);
 511end:
 512  SET_REQ_RESULT(req, result);
 513}
 514
 515void fs__close(uv_fs_t* req) {
 516  int fd = req->fd;
 517  int result;
 518
 519  VERIFY_FD(fd, req);
 520
 521  result = _close(fd);
 522  SET_REQ_RESULT(req, result);
 523}
 524
 525
 526void fs__read(uv_fs_t* req) {
 527  int fd = req->fd;
 528  size_t length = req->length;
 529  int64_t offset = req->offset;
 530  HANDLE handle;
 531  OVERLAPPED overlapped, *overlapped_ptr;
 532  LARGE_INTEGER offset_;
 533  DWORD bytes;
 534  DWORD error;
 535
 536  VERIFY_FD(fd, req);
 537
 538  handle = (HANDLE) _get_osfhandle(fd);
 539  if (handle == INVALID_HANDLE_VALUE) {
 540    SET_REQ_RESULT(req, -1);
 541    return;
 542  }
 543
 544  if (length > INT_MAX) {
 545    SET_REQ_WIN32_ERROR(req, ERROR_INSUFFICIENT_BUFFER);
 546    return;
 547  }
 548
 549  if (offset != -1) {
 550    memset(&overlapped, 0, sizeof overlapped);
 551
 552    offset_.QuadPart = offset;
 553    overlapped.Offset = offset_.LowPart;
 554    overlapped.OffsetHigh = offset_.HighPart;
 555
 556    overlapped_ptr = &overlapped;
 557  } else {
 558    overlapped_ptr = NULL;
 559  }
 560
 561  if (ReadFile(handle, req->buf, req->length, &bytes, overlapped_ptr)) {
 562    SET_REQ_RESULT(req, bytes);
 563  } else {
 564    error = GetLastError();
 565    if (error == ERROR_HANDLE_EOF) {
 566      SET_REQ_RESULT(req, bytes);
 567    } else {
 568      SET_REQ_WIN32_ERROR(req, error);
 569    }
 570  }
 571}
 572
 573
 574void fs__write(uv_fs_t* req) {
 575  int fd = req->fd;
 576  size_t length = req->length;
 577  int64_t offset = req->offset;
 578  HANDLE handle;
 579  OVERLAPPED overlapped, *overlapped_ptr;
 580  LARGE_INTEGER offset_;
 581  DWORD bytes;
 582
 583  VERIFY_FD(fd, req);
 584
 585  handle = (HANDLE) _get_osfhandle(fd);
 586  if (handle == INVALID_HANDLE_VALUE) {
 587    SET_REQ_RESULT(req, -1);
 588    return;
 589  }
 590
 591  if (length > INT_MAX) {
 592    SET_REQ_WIN32_ERROR(req, ERROR_INSUFFICIENT_BUFFER);
 593    return;
 594  }
 595
 596  if (offset != -1) {
 597    memset(&overlapped, 0, sizeof overlapped);
 598
 599    offset_.QuadPart = offset;
 600    overlapped.Offset = offset_.LowPart;
 601    overlapped.OffsetHigh = offset_.HighPart;
 602
 603    overlapped_ptr = &overlapped;
 604  } else {
 605    overlapped_ptr = NULL;
 606  }
 607
 608  if (WriteFile(handle, req->buf, length, &bytes, overlapped_ptr)) {
 609    SET_REQ_RESULT(req, bytes);
 610  } else {
 611    SET_REQ_WIN32_ERROR(req, GetLastError());
 612  }
 613}
 614
 615
 616void fs__rmdir(uv_fs_t* req) {
 617  int result = _wrmdir(req->pathw);
 618  SET_REQ_RESULT(req, result);
 619}
 620
 621
 622void fs__unlink(uv_fs_t* req) {
 623  const WCHAR* pathw = req->pathw;
 624  HANDLE handle;
 625  BY_HANDLE_FILE_INFORMATION info;
 626  FILE_DISPOSITION_INFORMATION disposition;
 627  IO_STATUS_BLOCK iosb;
 628  NTSTATUS status;
 629
 630  handle = CreateFileW(pathw,
 631                       FILE_READ_ATTRIBUTES | DELETE,
 632                       FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
 633                       NULL,
 634                       OPEN_EXISTING,
 635                       FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
 636                       NULL);
 637
 638  if (handle == INVALID_HANDLE_VALUE) {
 639    SET_REQ_WIN32_ERROR(req, GetLastError());
 640    return;
 641  }
 642
 643  if (!GetFileInformationByHandle(handle, &info)) {
 644    SET_REQ_WIN32_ERROR(req, GetLastError());
 645    CloseHandle(handle);
 646    return;
 647  }
 648
 649  if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
 650    /* Do not allow deletion of directories, unless it is a symlink. When */
 651    /* the path refers to a non-symlink directory, report EPERM as mandated */
 652    /* by POSIX.1. */
 653
 654    /* Check if it is a reparse point. If it's not, it's a normal directory. */
 655    if (!(info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
 656      SET_REQ_WIN32_ERROR(req, ERROR_ACCESS_DENIED);
 657      CloseHandle(handle);
 658      return;
 659    }
 660
 661    /* Read the reparse point and check if it is a valid symlink. */
 662    /* If not, don't unlink. */
 663    if (fs__readlink_handle(handle, NULL, NULL) < 0) {
 664      DWORD error = GetLastError();
 665      if (error == ERROR_SYMLINK_NOT_SUPPORTED)
 666        error = ERROR_ACCESS_DENIED;
 667      SET_REQ_WIN32_ERROR(req, error);
 668      CloseHandle(handle);
 669      return;
 670    }
 671  }
 672
 673  /* Try to set the delete flag. */
 674  disposition.DeleteFile = TRUE;
 675  status = pNtSetInformationFile(handle,
 676                                 &iosb,
 677                                 &disposition,
 678                                 sizeof disposition,
 679                                 FileDispositionInformation);
 680  if (NT_SUCCESS(status)) {
 681    SET_REQ_SUCCESS(req);
 682  } else {
 683    SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(status));
 684  }
 685
 686  CloseHandle(handle);
 687}
 688
 689
 690void fs__mkdir(uv_fs_t* req) {
 691  /* TODO: use req->mode. */
 692  int result = _wmkdir(req->pathw);
 693  SET_REQ_RESULT(req, result);
 694}
 695
 696
 697void fs__readdir(uv_fs_t* req) {
 698  WCHAR* pathw = req->pathw;
 699  size_t len = wcslen(pathw);
 700  int result, size;
 701  WCHAR* buf = NULL, *ptr, *name;
 702  HANDLE dir;
 703  WIN32_FIND_DATAW ent = { 0 };
 704  size_t buf_char_len = 4096;
 705  WCHAR* path2;
 706  const WCHAR* fmt;
 707
 708  if (len == 0) {
 709    fmt = L"./*";
 710  } else if (pathw[len - 1] == L'/' || pathw[len - 1] == L'\\') {
 711    fmt = L"%s*";
 712  } else {
 713    fmt = L"%s\\*";
 714  }
 715
 716  /* Figure out whether path is a file or a directory. */
 717  if (!(GetFileAttributesW(pathw) & FILE_ATTRIBUTE_DIRECTORY)) {
 718    req->result = -1;
 719    req->errorno = UV_ENOTDIR;
 720    req->sys_errno_ = ERROR_SUCCESS;
 721    return;
 722  }
 723
 724  path2 = (WCHAR*)malloc(sizeof(WCHAR) * (len + 4));
 725  if (!path2) {
 726    uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
 727  }
 728
 729#ifdef _MSC_VER
 730  swprintf(path2, len + 3, fmt, pathw);
 731#else
 732  swprintf(path2, fmt, pathw);
 733#endif
 734  dir = FindFirstFileW(path2, &ent);
 735  free(path2);
 736
 737  if(dir == INVALID_HANDLE_VALUE) {
 738    SET_REQ_WIN32_ERROR(req, GetLastError());
 739    return;
 740  }
 741
 742  result = 0;
 743
 744  do {
 745    name = ent.cFileName;
 746
 747    if (name[0] != L'.' || (name[1] && (name[1] != L'.' || name[2]))) {
 748      len = wcslen(name);
 749
 750      if (!buf) {
 751        buf = (WCHAR*)malloc(buf_char_len * sizeof(WCHAR));
 752        if (!buf) {
 753          uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
 754        }
 755
 756        ptr = buf;
 757      }
 758
 759      while ((ptr - buf) + len + 1 > buf_char_len) {
 760        buf_char_len *= 2;
 761        path2 = buf;
 762        buf = (WCHAR*)realloc(buf, buf_char_len * sizeof(WCHAR));
 763        if (!buf) {
 764          uv_fatal_error(ERROR_OUTOFMEMORY, "realloc");
 765        }
 766
 767        ptr = buf + (ptr - path2);
 768      }
 769
 770      wcscpy(ptr, name);
 771      ptr += len + 1;
 772      result++;
 773    }
 774  } while(FindNextFileW(dir, &ent));
 775
 776  FindClose(dir);
 777
 778  if (buf) {
 779    /* Convert result to UTF8. */
 780    size = uv_utf16_to_utf8(buf, buf_char_len, NULL, 0);
 781    if (!size) {
 782      SET_REQ_WIN32_ERROR(req, GetLastError());
 783      return;
 784    }
 785
 786    req->ptr = (char*)malloc(size + 1);
 787    if (!req->ptr) {
 788      uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
 789    }
 790
 791    size = uv_utf16_to_utf8(buf, buf_char_len, (char*)req->ptr, size);
 792    if (!size) {
 793      free(buf);
 794      free(req->ptr);
 795      req->ptr = NULL;
 796      SET_REQ_WIN32_ERROR(req, GetLastError());
 797      return;
 798    }
 799    free(buf);
 800
 801    ((char*)req->ptr)[size] = '\0';
 802    req->flags |= UV_FS_FREE_PTR;
 803  } else {
 804    req->ptr = NULL;
 805  }
 806
 807  SET_REQ_RESULT(req, result);
 808}
 809
 810
 811INLINE static int fs__stat_handle(HANDLE handle, uv_statbuf_t* statbuf) {
 812  BY_HANDLE_FILE_INFORMATION info;
 813
 814  if (!GetFileInformationByHandle(handle, &info)) {
 815    return -1;
 816  }
 817
 818  /* TODO: set st_dev, st_rdev and st_ino to something meaningful. */
 819  statbuf->st_ino = 0;
 820  statbuf->st_dev = 0;
 821  statbuf->st_rdev = 0;
 822
 823  statbuf->st_gid = 0;
 824  statbuf->st_uid = 0;
 825
 826  statbuf->st_mode = 0;
 827
 828  if (info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
 829    if (fs__readlink_handle(handle, NULL, &statbuf->st_size) != 0) {
 830      return -1;
 831    }
 832    statbuf->st_mode |= S_IFLNK;
 833  } else if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
 834    statbuf->st_mode |= _S_IFDIR;
 835    statbuf->st_size = 0;
 836  } else {
 837    statbuf->st_mode |= _S_IFREG;
 838    statbuf->st_size = ((int64_t) info.nFileSizeHigh << 32) +
 839                        (int64_t) info.nFileSizeLow;
 840  }
 841
 842  if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
 843    statbuf->st_mode |= (_S_IREAD + (_S_IREAD >> 3) + (_S_IREAD >> 6));
 844  } else {
 845    statbuf->st_mode |= ((_S_IREAD|_S_IWRITE) + ((_S_IREAD|_S_IWRITE) >> 3) +
 846      ((_S_IREAD|_S_IWRITE) >> 6));
 847  }
 848
 849  statbuf->st_mtime = FILETIME_TO_TIME_T(info.ftLastWriteTime);
 850  statbuf->st_atime = FILETIME_TO_TIME_T(info.ftLastAccessTime);
 851  statbuf->st_ctime = FILETIME_TO_TIME_T(info.ftCreationTime);
 852
 853  statbuf->st_nlink = (info.nNumberOfLinks <= SHRT_MAX) ?
 854                      (short) info.nNumberOfLinks : SHRT_MAX;
 855
 856  return 0;
 857}
 858
 859
 860INLINE static void fs__stat_prepare_path(WCHAR* pathw) {
 861  size_t len = wcslen(pathw);
 862
 863  /* TODO: ignore namespaced paths. */
 864  if (len > 1 && pathw[len - 2] != L':' &&
 865      (pathw[len - 1] == L'\\' || pathw[len - 1] == L'/')) {
 866    pathw[len - 1] = '\0';
 867  }
 868}
 869
 870
 871INLINE static void fs__stat_impl(uv_fs_t* req, int do_lstat) {
 872  HANDLE handle;
 873  DWORD flags;
 874
 875  flags = FILE_FLAG_BACKUP_SEMANTICS;
 876  if (do_lstat) {
 877    flags |= FILE_FLAG_OPEN_REPARSE_POINT;
 878  }
 879
 880  handle = CreateFileW(req->pathw,
 881                       FILE_READ_ATTRIBUTES,
 882                       FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
 883                       NULL,
 884                       OPEN_EXISTING,
 885                       flags,
 886                       NULL);
 887  if (handle == INVALID_HANDLE_VALUE) {
 888    SET_REQ_WIN32_ERROR(req, GetLastError());
 889    return;
 890  }
 891
 892  if (fs__stat_handle(handle, &req->stat) != 0) {
 893    DWORD error = GetLastError();
 894    if (do_lstat && error == ERROR_SYMLINK_NOT_SUPPORTED) {
 895      /* We opened a reparse point but it was not a symlink. Try again. */
 896      fs__stat_impl(req, 0);
 897
 898    } else {
 899      /* Stat failed. */
 900      SET_REQ_WIN32_ERROR(req, GetLastError());
 901    }
 902
 903    CloseHandle(handle);
 904    return;
 905  }
 906
 907  req->ptr = &req->stat;
 908  req->result = 0;
 909  CloseHandle(handle);
 910}
 911
 912
 913static void fs__stat(uv_fs_t* req) {
 914  fs__stat_prepare_path(req->pathw);
 915  fs__stat_impl(req, 0);
 916}
 917
 918
 919static void fs__lstat(uv_fs_t* req) {
 920  fs__stat_prepare_path(req->pathw);
 921  fs__stat_impl(req, 1);
 922}
 923
 924
 925static void fs__fstat(uv_fs_t* req) {
 926  int fd = req->fd;
 927  HANDLE handle;
 928
 929  VERIFY_FD(fd, req);
 930
 931  handle = (HANDLE) _get_osfhandle(fd);
 932
 933  if (handle == INVALID_HANDLE_VALUE) {
 934    SET_REQ_WIN32_ERROR(req, ERROR_INVALID_HANDLE);
 935    return;
 936  }
 937
 938  if (fs__stat_handle(handle, &req->stat) != 0) {
 939    SET_REQ_WIN32_ERROR(req, GetLastError());
 940    return;
 941  }
 942
 943  req->ptr = &req->stat;
 944  req->result = 0;
 945}
 946
 947
 948static void fs__rename(uv_fs_t* req) {
 949  if (!MoveFileExW(req->pathw, req->new_pathw, MOVEFILE_REPLACE_EXISTING)) {
 950    SET_REQ_WIN32_ERROR(req, GetLastError());
 951    return;
 952  }
 953
 954  SET_REQ_RESULT(req, 0);
 955}
 956
 957
 958INLINE static void fs__sync_impl(uv_fs_t* req) {
 959  int fd = req->fd;
 960  int result;
 961
 962  VERIFY_FD(fd, req);
 963
 964  result = FlushFileBuffers((HANDLE) _get_osfhandle(fd)) ? 0 : -1;
 965  if (result == -1) {
 966    SET_REQ_WIN32_ERROR(req, GetLastError());
 967  } else {
 968    SET_REQ_RESULT(req, result);
 969  }
 970}
 971
 972
 973static void fs__fsync(uv_fs_t* req) {
 974  fs__sync_impl(req);
 975}
 976
 977
 978static void fs__fdatasync(uv_fs_t* req) {
 979  fs__sync_impl(req);
 980}
 981
 982
 983static void fs__ftruncate(uv_fs_t* req) {
 984  int fd = req->fd;
 985  HANDLE handle;
 986  NTSTATUS status;
 987  IO_STATUS_BLOCK io_status;
 988  FILE_END_OF_FILE_INFORMATION eof_info;
 989
 990  VERIFY_FD(fd, req);
 991
 992  handle = (HANDLE)_get_osfhandle(fd);
 993
 994  eof_info.EndOfFile.QuadPart = req->offset;
 995
 996  status = pNtSetInformationFile(handle,
 997                                 &io_status,
 998                                 &eof_info,
 999                                 sizeof eof_info,
1000                                 FileEndOfFileInformation);
1001
1002  if (NT_SUCCESS(status)) {
1003    SET_REQ_RESULT(req, 0);
1004  } else {
1005    SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(status));
1006  }
1007}
1008
1009
1010static void fs__sendfile(uv_fs_t* req) {
1011  int fd_in = req->fd, fd_out = req->fd_out;
1012  size_t length = req->length;
1013  int64_t offset = req->offset;
1014  const size_t max_buf_size = 65536;
1015  size_t buf_size = length < max_buf_size ? length : max_buf_size;
1016  int n, result = 0;
1017  int64_t result_offset = 0;
1018  char* buf = (char*) malloc(buf_size);
1019  if (!buf) {
1020    uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
1021  }
1022
1023  if (offset != -1) {
1024    result_offset = _lseeki64(fd_in, offset, SEEK_SET);
1025  }
1026
1027  if (result_offset == -1) {
1028    result = -1;
1029  } else {
1030    while (length > 0) {
1031      n = _read(fd_in, buf, length < buf_size ? length : buf_size);
1032      if (n == 0) {
1033        break;
1034      } else if (n == -1) {
1035        result = -1;
1036        break;
1037      }
1038
1039      length -= n;
1040
1041      n = _write(fd_out, buf, n);
1042      if (n == -1) {
1043        result = -1;
1044        break;
1045      }
1046
1047      result += n;
1048    }
1049  }
1050
1051  SET_REQ_RESULT(req, result);
1052}
1053
1054
1055static void fs__chmod(uv_fs_t* req) {
1056  int result = _wchmod(req->pathw, req->mode);
1057  SET_REQ_RESULT(req, result);
1058}
1059
1060
1061static void fs__fchmod(uv_fs_t* req) {
1062  int fd = req->fd;
1063  int result;
1064  HANDLE handle;
1065  NTSTATUS nt_status;
1066  IO_STATUS_BLOCK io_status;
1067  FILE_BASIC_INFORMATION file_info;
1068
1069  VERIFY_FD(fd, req);
1070
1071  handle = (HANDLE)_get_osfhandle(fd);
1072
1073  nt_status = pNtQueryInformationFile(handle,
1074                                      &io_status,
1075                                      &file_info,
1076                                      sizeof file_info,
1077                                      FileBasicInformation);
1078
1079  if (nt_status != STATUS_SUCCESS) {
1080    result = -1;
1081    goto done;
1082  }
1083
1084  if (req->mode & _S_IWRITE) {
1085    file_info.FileAttributes &= ~FILE_ATTRIBUTE_READONLY;
1086  } else {
1087    file_info.FileAttributes |= FILE_ATTRIBUTE_READONLY;
1088  }
1089
1090  nt_status = pNtSetInformationFile(handle,
1091                                    &io_status,
1092                                    &file_info,
1093                                    sizeof file_info,
1094                                    FileBasicInformation);
1095
1096  if (nt_status != STATUS_SUCCESS) {
1097    result = -1;
1098    goto done;
1099  }
1100
1101  result = 0;
1102
1103done:
1104  SET_REQ_RESULT(req, result);
1105}
1106
1107
1108INLINE static int fs__utime_handle(HANDLE handle, double atime, double mtime) {
1109  FILETIME filetime_a, filetime_m;
1110
1111  TIME_T_TO_FILETIME((time_t) atime, &filetime_a);
1112  TIME_T_TO_FILETIME((time_t) mtime, &filetime_m);
1113
1114  if (!SetFileTime(handle, NULL, &filetime_a, &filetime_m)) {
1115    return -1;
1116  }
1117
1118  return 0;
1119}
1120
1121
1122static void fs__utime(uv_fs_t* req) {
1123  HANDLE handle;
1124
1125  handle = CreateFileW(req->pathw,
1126                       FILE_WRITE_ATTRIBUTES,
1127                       FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1128                       NULL,
1129                       OPEN_EXISTING,
1130                       FILE_FLAG_BACKUP_SEMANTICS,
1131                       NULL);
1132
1133  if (handle == INVALID_HANDLE_VALUE) {
1134    SET_REQ_WIN32_ERROR(req, GetLastError());
1135    return;
1136  }
1137
1138  if (fs__utime_handle(handle, req->atime, req->mtime) != 0) {
1139    SET_REQ_WIN32_ERROR(req, GetLastError());
1140    CloseHandle(handle);
1141    return;
1142  }
1143
1144  CloseHandle(handle);
1145
1146  req->result = 0;
1147}
1148
1149
1150static void fs__futime(uv_fs_t* req) {
1151  int fd = req->fd;
1152  HANDLE handle;
1153  VERIFY_FD(fd, req);
1154
1155  handle = (HANDLE) _get_osfhandle(fd);
1156
1157  if (handle == INVALID_HANDLE_VALUE) {
1158    SET_REQ_WIN32_ERROR(req, ERROR_INVALID_HANDLE);
1159    return;
1160  }
1161
1162  if (fs__utime_handle(handle, req->atime, req->mtime) != 0) {
1163    SET_REQ_WIN32_ERROR(req, GetLastError());
1164    return;
1165  }
1166
1167  req->result = 0;
1168}
1169
1170
1171static void fs__link(uv_fs_t* req) {
1172  DWORD r = CreateHardLinkW(req->new_pathw, req->pathw, NULL);
1173  if (r == 0) {
1174    SET_REQ_WIN32_ERROR(req, GetLastError());
1175  } else {
1176    req->result = 0;
1177  }
1178}
1179
1180
1181static void fs__create_junction(uv_fs_t* req, const WCHAR* path,
1182    const WCHAR* new_path) {
1183  HANDLE handle = INVALID_HANDLE_VALUE;
1184  REPARSE_DATA_BUFFER *buffer = NULL;
1185  int created = 0;
1186  int target_len;
1187  int is_absolute, is_long_path;
1188  int needed_buf_size, used_buf_size, used_data_size, path_buf_len;
1189  int start, len, i;
1190  int add_slash;
1191  DWORD bytes;
1192  WCHAR* path_buf;
1193
1194  target_len = wcslen(path);
1195  is_long_path = wcsncmp(path, LONG_PATH_PREFIX, LONG_PATH_PREFIX_LEN) == 0;
1196
1197  if (is_long_path) {
1198    is_absolute = 1;
1199  } else {
1200    is_absolute = target_len >= 3 && IS_LETTER(path[0]) &&
1201      path[1] == L':' && IS_SLASH(path[2]);
1202  }
1203
1204  if (!is_absolute) {
1205    /* Not supporting relative paths */
1206    SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_NOT_SUPPORTED);
1207    return;
1208  }
1209
1210  // Do a pessimistic calculation of the required buffer size
1211  needed_buf_size =
1212      FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer) +
1213      JUNCTION_PREFIX_LEN * sizeof(WCHAR) +
1214      2 * (target_len + 2) * sizeof(WCHAR);
1215
1216  // Allocate the buffer
1217  buffer = (REPARSE_DATA_BUFFER*)malloc(needed_buf_size);
1218  if (!buffer) {
1219    uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
1220  }
1221
1222  // Grab a pointer to the part of the buffer where filenames go
1223  path_buf = (WCHAR*)&(buffer->MountPointReparseBuffer.PathBuffer);
1224  path_buf_len = 0;
1225
1226  // Copy the substitute (internal) target path
1227  start = path_buf_len;
1228
1229  wcsncpy((WCHAR*)&path_buf[path_buf_len], JUNCTION_PREFIX,
1230    JUNCTION_PREFIX_LEN);
1231  path_buf_len += JUNCTION_PREFIX_LEN;
1232
1233  add_slash = 0;
1234  for (i = is_long_path ? LONG_PATH_PREFIX_LEN : 0; path[i] != L'\0'; i++) {
1235    if (IS_SLASH(path[i])) {
1236      add_slash = 1;
1237      continue;
1238    }
1239
1240    if (add_slash) {
1241      path_buf[path_buf_len++] = L'\\';
1242      add_slash = 0;
1243    }
1244
1245    path_buf[path_buf_len++] = path[i];
1246  }
1247  path_buf[path_buf_len++] = L'\\';
1248  len = path_buf_len - start;
1249
1250  // Set the info about the substitute name
1251  buffer->MountPointReparseBuffer.SubstituteNameOffset = start * sizeof(WCHAR);
1252  buffer->MountPointReparseBuffer.SubstituteNameLength = len * sizeof(WCHAR);
1253
1254  // Insert null terminator
1255  path_buf[path_buf_len++] = L'\0';
1256
1257  // Copy the print name of the target path
1258  start = path_buf_len;
1259  add_slash = 0;
1260  for (i = is_long_path ? LONG_PATH_PREFIX_LEN : 0; path[i] != L'\0'; i++) {
1261    if (IS_SLASH(path[i])) {
1262      add_slash = 1;
1263      continue;
1264    }
1265
1266    if (add_slash) {
1267      path_buf[path_buf_len++] = L'\\';
1268      add_slash = 0;
1269    }
1270
1271    path_buf[path_buf_len++] = path[i];
1272  }
1273  len = path_buf_len - start;
1274  if (len == 2) {
1275    path_buf[path_buf_len++] = L'\\';
1276    len++;
1277  }
1278
1279  // Set the info about the print name
1280  buffer->MountPointReparseBuffer.PrintNameOffset = start * sizeof(WCHAR);
1281  buffer->MountPointReparseBuffer.PrintNameLength = len * sizeof(WCHAR);
1282
1283  // Insert another null terminator
1284  path_buf[path_buf_len++] = L'\0';
1285
1286  // Calculate how much buffer space was actually used
1287  used_buf_size = FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer) +
1288    path_buf_len * sizeof(WCHAR);
1289  used_data_size = used_buf_size -
1290    FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer);
1291
1292  // Put general info in the data buffer
1293  buffer->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
1294  buffer->ReparseDataLength = used_data_size;
1295  buffer->Reserved = 0;
1296
1297  // Create a new directory
1298  if (!CreateDirectoryW(new_path, NULL)) {
1299    SET_REQ_WIN32_ERROR(req, GetLastError());
1300    goto error;
1301  }
1302  created = 1;
1303
1304  // Open the directory
1305  handle = CreateFileW(new_path,
1306                       GENERIC_ALL,
1307                       0,
1308                       NULL,
1309                       OPEN_EXISTING,
1310                       FILE_FLAG_BACKUP_SEMANTICS |
1311                         FILE_FLAG_OPEN_REPARSE_POINT,
1312                       NULL);
1313  if (handle == INVALID_HANDLE_VALUE) {
1314    SET_REQ_WIN32_ERROR(req, GetLastError());
1315    goto error;
1316  }
1317
1318  // Create the actual reparse point
1319  if (!DeviceIoControl(handle,
1320                       FSCTL_SET_REPARSE_POINT,
1321                       buffer,
1322                       used_buf_size,
1323                       NULL,
1324                       0,
1325                       &bytes,
1326                       NULL)) {
1327    SET_REQ_WIN32_ERROR(req, GetLastError());
1328    goto error;
1329  }
1330
1331  // Clean up
1332  CloseHandle(handle);
1333  free(buffer);
1334
1335  SET_REQ_RESULT(req, 0);
1336  return;
1337
1338error:
1339  free(buffer);
1340
1341  if (handle != INVALID_HANDLE_VALUE) {
1342    CloseHandle(handle);
1343  }
1344
1345  if (created) {
1346    RemoveDirectoryW(new_path);
1347  }
1348}
1349
1350
1351static void fs__symlink(uv_fs_t* req) {
1352  WCHAR* pathw = req->pathw;
1353  WCHAR* new_pathw = req->new_pathw;
1354  int flags = req->file_flags;
1355  int result;
1356
1357
1358  if (flags & UV_FS_SYMLINK_JUNCTION) {
1359    fs__create_junction(req, pathw, new_pathw);
1360  } else if (pCreateSymbolicLinkW) {
1361    result = pCreateSymbolicLinkW(new_pathw,
1362                                  pathw,
1363                                  flags & UV_FS_SYMLINK_DIR ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0) ? 0 : -1;
1364    if (result == -1) {
1365      SET_REQ_WIN32_ERROR(req, GetLastError());
1366    } else {
1367      SET_REQ_RESULT(req, result);
1368    }
1369  } else {
1370    SET_REQ_UV_ERROR(req, UV_ENOSYS, ERROR_NOT_SUPPORTED);
1371  }
1372}
1373
1374
1375static void fs__readlink(uv_fs_t* req) {
1376  HANDLE handle;
1377
1378  handle = CreateFileW(req->pathw,
1379                       0,
1380                       0,
1381                       NULL,
1382                       OPEN_EXISTING,
1383                       FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
1384                       NULL);
1385
1386  if (handle == INVALID_HANDLE_VALUE) {
1387    SET_REQ_WIN32_ERROR(req, GetLastError());
1388    return;
1389  }
1390
1391  if (fs__readlink_handle(handle, (char**) &req->ptr, NULL) != 0) {
1392    SET_REQ_WIN32_ERROR(req, GetLastError());
1393    CloseHandle(handle);
1394    return;
1395  }
1396
1397  req->flags |= UV_FS_FREE_PTR;
1398  SET_REQ_RESULT(req, 0);
1399
1400  CloseHandle(handle);
1401}
1402
1403
1404
1405static void fs__chown(uv_fs_t* req) {
1406  req->result = 0;
1407}
1408
1409
1410static void fs__fchown(uv_fs_t* req) {
1411  req->result = 0;
1412}
1413
1414
1415static DWORD WINAPI uv_fs_thread_proc(void* parameter) {
1416  uv_fs_t* req = (uv_fs_t*) parameter;
1417  uv_loop_t* loop = req->loop;
1418
1419  assert(req != NULL);
1420  assert(req->type == UV_FS);
1421
1422#define XX(uc, lc)  case UV_FS_##uc: fs__##lc(req); break;
1423  switch (req->fs_type) {
1424    XX(OPEN, open)
1425    XX(CLOSE, close)
1426    XX(READ, read)
1427    XX(WRITE, write)
1428    XX(SENDFILE, sendfile)
1429    XX(STAT, stat)
1430    XX(LSTAT, lstat)
1431    XX(FSTAT, fstat)
1432    XX(FTRUNCATE, ftruncate)
1433    XX(UTIME, utime)
1434    XX(FUTIME, futime)
1435    XX(CHMOD, chmod)
1436    XX(FCHMOD, fchmod)
1437    XX(FSYNC, fsync)
1438    XX(FDATASYNC, fdatasync)
1439    XX(UNLINK, unlink)
1440    XX(RMDIR, rmdir)
1441    XX(MKDIR, mkdir)
1442    XX(RENAME, rename)
1443    XX(READDIR, readdir)
1444    XX(LINK, link)
1445    XX(SYMLINK, symlink)
1446    XX(READLINK, readlink)
1447    XX(CHOWN, chown)
1448    XX(FCHOWN, fchown);
1449    default:
1450      assert(!"bad uv_fs_type");
1451  }
1452
1453  POST_COMPLETION_FOR_REQ(loop, req);
1454  return 0;
1455}
1456
1457
1458int uv_fs_open(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags,
1459    int mode, uv_fs_cb cb) {
1460  uv_fs_req_init(loop, req, UV_FS_OPEN, cb);
1461
1462  if (fs__capture_path(loop, req, path, NULL, cb != NULL) < 0) {
1463    return -1;
1464  }
1465
1466  req->file_flags = flags;
1467  req->mode = mode;
1468
1469  if (cb) {
1470    QUEUE_FS_TP_JOB(loop, req);
1471    return 0;
1472  } else {
1473    fs__open(req);
1474    SET_UV_LAST_ERROR_FROM_REQ(req);
1475    return req->result;
1476  }
1477}
1478
1479
1480int uv_fs_close(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) {
1481  uv_fs_req_init(loop, req, UV_FS_CLOSE, cb);
1482  req->fd = fd;
1483
1484  if (cb) {
1485    QUEUE_FS_TP_JOB(loop, req);
1486    return 0;
1487  } else {
1488    fs__close(req);
1489    SET_UV_LAST_ERROR_FROM_REQ(req);
1490    return req->result;
1491  }
1492}
1493
1494
1495int uv_fs_read(uv_loop_t* loop, uv_fs_t* req, uv_file fd, void* buf,
1496    size_t length, int64_t offset, uv_fs_cb cb) {
1497  uv_fs_req_init(loop, req, UV_FS_READ, cb);
1498
1499  req->fd = fd;
1500  req->buf = buf;
1501  req->length = length;
1502  req->offset = offset;
1503
1504  if (cb) {
1505    QUEUE_FS_TP_JOB(loop, req);
1506    return 0;
1507  } else {
1508    fs__read(req);
1509    SET_UV_LAST_ERROR_FROM_REQ(req);
1510    return req->result;
1511  }
1512}
1513
1514
1515int uv_fs_write(uv_loop_t* loop, uv_fs_t* req, uv_file fd, void* buf,
1516    size_t length, int64_t offset, uv_fs_cb cb) {
1517  uv_fs_req_init(loop, req, UV_FS_WRITE, cb);
1518
1519  req->fd = fd;
1520  req->buf = buf;
1521  req->length = length;
1522  req->offset = offset;
1523
1524  if (cb) {
1525    QUEUE_FS_TP_JOB(loop, req);
1526    return 0;
1527  } else {
1528    fs__write(req);
1529    SET_UV_LAST_ERROR_FROM_REQ(req);
1530    return req->result;
1531  }
1532}
1533
1534
1535int uv_fs_unlink(uv_loop_t* loop, uv_fs_t* req, const char* path,
1536    uv_fs_cb cb) {
1537  uv_fs_req_init(loop, req, UV_FS_UNLINK, cb);
1538
1539  if (fs__capture_path(loop, req, path, NULL, cb != NULL) < 0) {
1540    return -1;
1541  }
1542
1543  if (cb) {
1544    QUEUE_FS_TP_JOB(loop, req);
1545    return 0;
1546  } else {
1547    fs__unlink(req);
1548    SET_UV_LAST_ERROR_FROM_REQ(req);
1549    return req->result;
1550  }
1551}
1552
1553
1554int uv_fs_mkdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode,
1555    uv_fs_cb cb) {
1556  uv_fs_req_init(loop, req, UV_FS_MKDIR, cb);
1557
1558  if (fs__capture_path(loop, req, path, NULL, cb != NULL) < 0) {
1559    return -1;
1560  }
1561
1562  req->mode = mode;
1563
1564  if (cb) {
1565    QUEUE_FS_TP_JOB(loop, req);
1566    return 0;
1567  } else {
1568    fs__mkdir(req);
1569    SET_UV_LAST_ERROR_FROM_REQ(req);
1570    return req->result;
1571  }
1572}
1573
1574
1575int uv_fs_rmdir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) {
1576  uv_fs_req_init(loop, req, UV_FS_RMDIR, cb);
1577
1578  if (fs__capture_path(loop, req, path, NULL, cb != NULL) < 0) {
1579    return -1;
1580  }
1581
1582  if (cb) {
1583    QUEUE_FS_TP_JOB(loop, req);
1584    return 0;
1585  } else {
1586    fs__rmdir(req);
1587    SET_UV_LAST_ERROR_FROM_REQ(req);
1588    return req->result;
1589  }
1590}
1591
1592
1593int uv_fs_readdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags,
1594    uv_fs_cb cb) {
1595  uv_fs_req_init(loop, req, UV_FS_READDIR, cb);
1596
1597  if (fs__capture_path(loop, req, path, NULL, cb != NULL) < 0) {
1598    return -1;
1599  }
1600
1601  req->file_flags;
1602
1603  if (cb) {
1604    QUEUE_FS_TP_JOB(loop, req);
1605    return 0;
1606  } else {
1607    fs__readdir(req);
1608    SET_UV_LAST_ERROR_FROM_REQ(req);
1609    return req->result;
1610  }
1611}
1612
1613
1614int uv_fs_link(uv_loop_t* loop, uv_fs_t* req, const char* path,
1615    const char* new_path, uv_fs_cb cb) {
1616  uv_fs_req_init(loop, req, UV_FS_LINK, cb);
1617
1618  if (fs__capture_path(loop, req, path, new_path, cb != NULL) < 0) {
1619    return -1;
1620  }
1621
1622  if (cb) {
1623    QUEUE_FS_TP_JOB(loop, req);
1624    return 0;
1625  } else {
1626    fs__link(req);
1627    SET_UV_LAST_ERROR_FROM_REQ(req);
1628    return req->result;
1629  }
1630}
1631
1632
1633int uv_fs_symlink(uv_loop_t* loop, uv_fs_t* req, const char* path,
1634    const char* new_path, int flags, uv_fs_cb cb) {
1635  uv_fs_req_init(loop, req, UV_FS_SYMLINK, cb);
1636
1637  if (fs__capture_path(loop, req, path, new_path, cb != NULL) < 0) {
1638    return -1;
1639  }
1640
1641  req->file_flags = flags;
1642
1643  if (cb) {
1644    QUEUE_FS_TP_JOB(loop, req);
1645    return 0;
1646  } else {
1647    fs__symlink(req);
1648    SET_UV_LAST_ERROR_FROM_REQ(req);
1649    return req->result;
1650  }
1651}
1652
1653
1654int uv_fs_readlink(uv_loop_t* loop, uv_fs_t* req, const char* path,
1655    uv_fs_cb cb) {
1656  uv_fs_req_init(loop, req, UV_FS_READLINK, cb);
1657
1658  if (fs__capture_path(loop, req, path, NULL, cb != NULL) < 0) {
1659    return -1;
1660  }
1661
1662  if (cb) {
1663    QUEUE_FS_TP_JOB(loop, req);
1664    return 0;
1665  } else {
1666    fs__readlink(req);
1667    SET_UV_LAST_ERROR_FROM_REQ(req);
1668    return req->result;
1669  }
1670}
1671
1672
1673int uv_fs_chown(uv_loop_t* loop, uv_fs_t* req, const char* path, int uid,
1674    int gid, uv_fs_cb cb) {
1675  uv_fs_req_init(loop, req, UV_FS_CHOWN, cb);
1676
1677  if (fs__capture_path(loop, req, path, NULL, cb != NULL) < 0) {
1678    return -1;
1679  }
1680
1681  if (cb) {
1682    QUEUE_FS_TP_JOB(loop, req);
1683    return 0;
1684  } else {
1685    fs__chown(req);
1686    SET_UV_LAST_ERROR_FROM_REQ(req);
1687    return req->result;
1688  }
1689}
1690
1691
1692int uv_fs_fchown(uv_loop_t* loop, uv_fs_t* req, uv_file fd, int uid,
1693    int gid, uv_fs_cb cb) {
1694  uv_fs_req_init(loop, req, UV_FS_FCHOWN, cb);
1695
1696  if (cb) {
1697    QUEUE_FS_TP_JOB(loop, req);
1698    return 0;
1699  } else {
1700    fs__fchown(req);
1701    SET_UV_LAST_ERROR_FROM_REQ(req);
1702    return req->result;
1703  }
1704}
1705
1706
1707int uv_fs_stat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) {
1708  uv_fs_req_init(loop, req, UV_FS_STAT, cb);
1709
1710  if (fs__capture_path(loop, req, path, NULL, cb != NULL) < 0) {
1711    return -1;
1712  }
1713
1714  if (cb) {
1715    QUEUE_FS_TP_JOB(loop, req);
1716    return 0;
1717  } else {
1718    fs__stat(req);
1719    SET_UV_LAST_ERROR_FROM_REQ(req);
1720    return req->result;
1721  }
1722}
1723
1724
1725int uv_fs_lstat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) {
1726  uv_fs_req_init(loop, req, UV_FS_LSTAT, cb);
1727
1728  if (fs__capture_path(loop, req, path, NULL, cb != NULL) < 0) {
1729    return -1;
1730  }
1731
1732  if (cb) {
1733    QUEUE_FS_TP_JOB(loop, req);
1734    return 0;
1735  } else {
1736    fs__lstat(req);
1737    SET_UV_LAST_ERROR_FROM_REQ(req);
1738    return req->result;
1739  }
1740}
1741
1742
1743int uv_fs_fstat(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) {
1744  uv_fs_req_init(loop, req, UV_FS_FSTAT, cb);
1745  req->fd = fd;
1746
1747  if (cb) {
1748    QUEUE_FS_TP_JOB(loop, req);
1749    return 0;
1750  } else {
1751    fs__fstat(req);
1752    SET_UV_LAST_ERROR_FROM_REQ(req);
1753    return req->result;
1754  }
1755}
1756
1757
1758int uv_fs_rename(uv_loop_t* loop, uv_fs_t* req, const char* path,
1759    const char* new_path, uv_fs_cb cb) {
1760  uv_fs_req_init(loop, req, UV_FS_RENAME, cb);
1761
1762  if (fs__capture_path(loop, req, path, new_path, cb != NULL) < 0) {
1763    return -1;
1764  }
1765
1766  if (cb) {
1767    QUEUE_FS_TP_JOB(loop, req);
1768    return 0;
1769  } else {
1770    fs__rename(req);
1771    SET_UV_LAST_ERROR_FROM_REQ(req);
1772    return req->result;
1773  }
1774}
1775
1776
1777int uv_fs_fsync(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) {
1778  uv_fs_req_init(loop, req, UV_FS_FSYNC, cb);
1779  req->fd = fd;
1780
1781  if (cb) {
1782    QUEUE_FS_TP_JOB(loop, req);
1783    return 0;
1784  } else {
1785    fs__fsync(req);
1786    SET_UV_LAST_ERROR_FROM_REQ(req);
1787    return req->result;
1788  }
1789}
1790
1791
1792int uv_fs_fdatasync(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) {
1793  uv_fs_req_init(loop, req, UV_FS_FDATASYNC, cb);
1794  req->fd = fd;
1795
1796  if (cb) {
1797    QUEUE_FS_TP_JOB(loop, req);
1798    return 0;
1799  } else {
1800    fs__fdatasync(req);
1801    SET_UV_LAST_ERROR_FROM_REQ(req);
1802    return req->result;
1803  }
1804}
1805
1806
1807int uv_fs_ftruncate(uv_loop_t* loop, uv_fs_t* req, uv_file fd,
1808    int64_t offset, uv_fs_cb cb) {
1809  uv_fs_req_init(loop, req, UV_FS_FTRUNCATE, cb);
1810
1811  req->fd = fd;
1812  req->offset = offset;
1813
1814  if (cb) {
1815    QUEUE_FS_TP_JOB(loop, req);
1816    return 0;
1817  } else {
1818    fs__ftruncate(req);
1819    SET_UV_LAST_ERROR_FROM_REQ(req);
1820    return req->result;
1821  }
1822}
1823
1824
1825
1826int uv_fs_sendfile(uv_loop_t* loop, uv_fs_t* req, uv_file fd_out,
1827    uv_file fd_in, int64_t in_offset, size_t length, uv_fs_cb cb) {
1828  uv_fs_req_init(loop, req, UV_FS_SENDFILE, cb);
1829
1830  req->fd = fd_in;
1831  req->fd_out = fd_out;
1832  req->offset = in_offset;
1833  req->length = length;
1834
1835  if (cb) {
1836    QUEUE_FS_TP_JOB(loop, req);
1837    return 0;
1838  } else {
1839    fs__sendfile(req);
1840    SET_UV_LAST_ERROR_FROM_REQ(req);
1841    return req->result;
1842  }
1843}
1844
1845
1846int uv_fs_chmod(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode,
1847    uv_fs_cb cb) {
1848  uv_fs_req_init(loop, req, UV_FS_CHMOD, cb);
1849
1850  if (fs__capture_path(loop, req, path, NULL, cb != NULL) < 0) {
1851    return -1;
1852  }
1853
1854  req->mode = mode;
1855
1856  if (cb) {
1857    QUEUE_FS_TP_JOB(loop, req);
1858    return 0;
1859  } else {
1860    fs__chmod(req);
1861    SET_UV_LAST_ERROR_FROM_REQ(req);
1862    return req->result;
1863  }
1864}
1865
1866
1867int uv_fs_fchmod(uv_loop_t* loop, uv_fs_t* req, uv_file fd, int mode,
1868    uv_fs_cb cb) {
1869  uv_fs_req_init(loop, req, UV_FS_FCHMOD, cb);
1870
1871  req->fd = fd;
1872  req->mode = mode;
1873
1874  if (cb) {
1875    QUEUE_FS_TP_JOB(loop, req);
1876    return 0;
1877  } else {
1878    fs__fchmod(req);
1879    SET_UV_LAST_ERROR_FROM_REQ(req);
1880    return req->result;
1881  }
1882}
1883
1884
1885int uv_fs_utime(uv_loop_t* loop, uv_fs_t* req, const char* path, double atime,
1886    double mtime, uv_fs_cb cb) {
1887  uv_fs_req_init(loop, req, UV_FS_UTIME, cb);
1888
1889  if (fs__capture_path(loop, req, path, NULL, cb != NULL) < 0) {
1890    return -1;
1891  }
1892
1893  req->atime = atime;
1894  req->mtime = mtime;
1895
1896  if (cb) {
1897    QUEUE_FS_TP_JOB(loop, req);
1898    return 0;
1899  } else {
1900    fs__utime(req);
1901    SET_UV_LAST_ERROR_FROM_REQ(req);
1902    return req->result;
1903  }
1904}
1905
1906
1907int uv_fs_futime(uv_loop_t* loop, uv_fs_t* req, uv_file fd, double atime,
1908    double mtime, uv_fs_cb cb) {
1909  uv_fs_req_init(loop, req, UV_FS_FUTIME, cb);
1910
1911  req->fd = fd;
1912  req->atime = atime;
1913  req->mtime = mtime;
1914
1915  if (cb) {
1916    QUEUE_FS_TP_JOB(loop, req);
1917    return 0;
1918  } else {
1919    fs__futime(req);
1920    SET_UV_LAST_ERROR_FROM_REQ(req);
1921    return req->result;
1922  }
1923}
1924
1925
1926void uv_process_fs_req(uv_loop_t* loop, uv_fs_t* req) {
1927  assert(req->cb);
1928  uv__req_unregister(loop, req);
1929  SET_UV_LAST_ERROR_FROM_REQ(req);
1930  req->cb(req);
1931}
1932
1933
1934void uv_fs_req_cleanup(uv_fs_t* req) {
1935  if (req->flags & UV_FS_CLEANEDUP)
1936    return;
1937
1938  if (req->flags & UV_FS_FREE_PATHS)
1939    free(req->pathw);
1940
1941  if (req->flags & UV_FS_FREE_PTR)
1942    free(req->ptr);
1943
1944  req->path = NULL;
1945  req->pathw = NULL;
1946  req->new_pathw = NULL;
1947  req->ptr = NULL;
1948
1949  req->flags |= UV_FS_CLEANEDUP;
1950}
1951