Upgrade libuv to 142a702
authorRyan Dahl <ry@tinyclouds.org>
Sun, 4 Sep 2011 23:13:03 +0000 (16:13 -0700)
committerRyan Dahl <ry@tinyclouds.org>
Sun, 4 Sep 2011 23:17:00 +0000 (16:17 -0700)
12 files changed:
deps/uv/common.gypi
deps/uv/include/uv-private/uv-win.h
deps/uv/include/uv.h
deps/uv/src/unix/core.c
deps/uv/src/unix/darwin.c
deps/uv/src/unix/fs.c
deps/uv/src/win/fs.c
deps/uv/src/win/winapi.c
deps/uv/src/win/winapi.h
deps/uv/test/test-fs.c
deps/uv/test/test-list.h
src/node_file.cc

index 840d3a7..f871a47 100644 (file)
@@ -1,5 +1,6 @@
 {
   'variables': {
+    'visibility%': 'hidden',         # V8's visibility setting
     'target_arch%': 'ia32',          # set v8's target architecture
     'host_arch%': 'ia32',            # set v8's host architecture
     'library%': 'static_library',    # allow override to 'shared_library' for DLL/.so builds
         'DataExecutionPrevention': 2, # enable DEP
         'AllowIsolation': 'true',
         'SuppressStartupBanner': 'true',
+        'target_conditions': [
+          ['_type=="executable"', {
+            'SubSystem': 1, # console executable
+          }],
+        ],
       },
     },
     'conditions': [
         ],
       }],
       [ 'OS=="linux" or OS=="freebsd" or OS=="openbsd" or OS=="solaris"', {
-       'target_defaults': {
-         'cflags': [ '-Wall', '-pthread', '-fno-rtti', '-fno-exceptions' ],
-         'ldflags': [ '-pthread', ],
-         'conditions': [
-           [ 'target_arch=="ia32"', {
-             'cflags': [ '-m32' ],
-             'ldflags': [ '-m32' ],
-           }],
-           [ 'OS=="linux"', {
-             'cflags': [ '-ansi' ],
-           }],
-           [ 'visibility=="hidden"', {
-             'cflags': [ '-fvisibility=hidden' ],
-           }],
-         ],
-       },
+        'cflags': [ '-Wall', '-pthread', ],
+        'cflags_cc': [ '-fno-rtti', '-fno-exceptions' ],
+        'ldflags': [ '-pthread', ],
+        'conditions': [
+          [ 'target_arch=="ia32"', {
+            'cflags': [ '-m32' ],
+            'ldflags': [ '-m32' ],
+          }],
+          [ 'OS=="linux"', {
+            'cflags': [ '-ansi' ],
+          }],
+          [ 'visibility=="hidden"', {
+            'cflags': [ '-fvisibility=hidden' ],
+          }],
+        ],
       }],
       ['OS=="mac"', {
-        'target_defaults': {
-          'xcode_settings': {
-            'ALWAYS_SEARCH_USER_PATHS': 'NO',
-            'GCC_C_LANGUAGE_STANDARD': 'ansi',        # -ansi
-            'GCC_CW_ASM_SYNTAX': 'NO',                # No -fasm-blocks
-            'GCC_DYNAMIC_NO_PIC': 'NO',               # No -mdynamic-no-pic
-                                                      # (Equivalent to -fPIC)
-            'GCC_ENABLE_CPP_EXCEPTIONS': 'NO',        # -fno-exceptions
-            'GCC_ENABLE_CPP_RTTI': 'NO',              # -fno-rtti
-            'GCC_ENABLE_PASCAL_STRINGS': 'NO',        # No -mpascal-strings
-            # GCC_INLINES_ARE_PRIVATE_EXTERN maps to -fvisibility-inlines-hidden
-            'GCC_INLINES_ARE_PRIVATE_EXTERN': 'YES',
-            'GCC_SYMBOLS_PRIVATE_EXTERN': 'YES',      # -fvisibility=hidden
-            'GCC_THREADSAFE_STATICS': 'NO',           # -fno-threadsafe-statics
-            'GCC_TREAT_WARNINGS_AS_ERRORS': 'YES',    # -Werror
-            'GCC_VERSION': '4.2',
-            'GCC_WARN_ABOUT_MISSING_NEWLINE': 'YES',  # -Wnewline-eof
-            'MACOSX_DEPLOYMENT_TARGET': '10.4',       # -mmacosx-version-min=10.4
-            'PREBINDING': 'NO',                       # No -Wl,-prebind
-            'USE_HEADERMAP': 'NO',
-            'OTHER_CFLAGS': [
-              '-fno-strict-aliasing',
-            ],
-            'WARNING_CFLAGS': [
-              '-Wall',
-              '-Wendif-labels',
-              '-W',
-              '-Wno-unused-parameter',
-              '-Wnon-virtual-dtor',
-            ],
-          },
-          'target_conditions': [
-            ['_type!="static_library"', {
-              'xcode_settings': {'OTHER_LDFLAGS': ['-Wl,-search_paths_first']},
-            }],
+        'xcode_settings': {
+          'ALWAYS_SEARCH_USER_PATHS': 'NO',
+          'GCC_CW_ASM_SYNTAX': 'NO',                # No -fasm-blocks
+          'GCC_DYNAMIC_NO_PIC': 'NO',               # No -mdynamic-no-pic
+                                                    # (Equivalent to -fPIC)
+          'GCC_ENABLE_CPP_EXCEPTIONS': 'NO',        # -fno-exceptions
+          'GCC_ENABLE_CPP_RTTI': 'NO',              # -fno-rtti
+          'GCC_ENABLE_PASCAL_STRINGS': 'NO',        # No -mpascal-strings
+          # GCC_INLINES_ARE_PRIVATE_EXTERN maps to -fvisibility-inlines-hidden
+          'GCC_INLINES_ARE_PRIVATE_EXTERN': 'YES',
+          'GCC_SYMBOLS_PRIVATE_EXTERN': 'YES',      # -fvisibility=hidden
+          'GCC_THREADSAFE_STATICS': 'NO',           # -fno-threadsafe-statics
+          'GCC_VERSION': '4.2',
+          'GCC_WARN_ABOUT_MISSING_NEWLINE': 'YES',  # -Wnewline-eof
+          'MACOSX_DEPLOYMENT_TARGET': '10.4',       # -mmacosx-version-min=10.4
+          'PREBINDING': 'NO',                       # No -Wl,-prebind
+          'USE_HEADERMAP': 'NO',
+          'OTHER_CFLAGS': [
+            '-fno-strict-aliasing',
+          ],
+          'WARNING_CFLAGS': [
+            '-Wall',
+            '-Wendif-labels',
+            '-W',
+            '-Wno-unused-parameter',
           ],
         },
+        'target_conditions': [
+          ['_type!="static_library"', {
+            'xcode_settings': {'OTHER_LDFLAGS': ['-Wl,-search_paths_first']},
+          }],
+        ],
       }],
     ],
   },
index 3a80f55..35c9c19 100644 (file)
@@ -247,6 +247,7 @@ RB_HEAD(uv_timer_tree_s, uv_timer_s);
 
 #define UV_FS_PRIVATE_FIELDS              \
   int flags;                              \
+  int last_error;                         \
   struct _stat stat;                      \
   void* arg0;                             \
   union {                                 \
index 4f4c7ce..894c98f 100644 (file)
@@ -948,8 +948,14 @@ int uv_fs_lstat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb);
 int uv_fs_link(uv_loop_t* loop, uv_fs_t* req, const char* path,
     const char* new_path, uv_fs_cb cb);
 
+/* 
+ * This flag can be used with uv_fs_symlink on Windows
+ * to specify whether path argument points to a directory.
+ */
+#define UV_FS_SYMLINK_DIR          0x0001
+
 int uv_fs_symlink(uv_loop_t* loop, uv_fs_t* req, const char* path,
-    const char* new_path, uv_fs_cb cb);
+    const char* new_path, int flags, uv_fs_cb cb);
 
 int uv_fs_readlink(uv_loop_t* loop, uv_fs_t* req, const char* path,
     uv_fs_cb cb);
index a352977..a016d1b 100644 (file)
@@ -323,7 +323,6 @@ int64_t uv_now(uv_loop_t* loop) {
 void uv__req_init(uv_req_t* req) {
   /* loop->counters.req_init++; */
   req->type = UV_UNKNOWN_REQ;
-  req->data = NULL;
 }
 
 
index fa2948c..bfa06ee 100644 (file)
@@ -24,6 +24,7 @@
 #include <CoreServices/CoreServices.h>
 #include <mach/mach.h>
 #include <mach/mach_time.h>
+#include <mach-o/dyld.h> /* _NSGetExecutablePath */
 
 
 uint64_t uv_hrtime() {
index fc4edbb..5bc1ca9 100644 (file)
@@ -112,7 +112,7 @@ static int uv__fs_after(eio_req* eio) {
     case UV_FS_READDIR:
       /*
        * XXX This is pretty bad.
-       * We alloc and copy the large null termiated string list from libeio.
+       * We alloc and copy the large null terminated string list from libeio.
        * This is done because libeio is going to free eio->ptr2 after this
        * callback. We must keep it until uv_fs_req_cleanup. If we get rid of
        * libeio this can be avoided.
@@ -130,12 +130,31 @@ static int uv__fs_after(eio_req* eio) {
       req->ptr = malloc(buflen);
       memcpy(req->ptr, req->eio->ptr2, buflen);
       break;
+
     case UV_FS_STAT:
     case UV_FS_LSTAT:
     case UV_FS_FSTAT:
       req->ptr = req->eio->ptr2;
       break;
 
+    case UV_FS_READLINK:
+      if (req->result == -1) {
+        req->ptr = NULL;
+      } else {
+        assert(req->result > 0);
+
+        if ((name = realloc(req->eio->ptr2, req->result + 1)) == NULL) {
+          /* Not enough memory. Reuse buffer, chop off last byte. */
+          name = req->eio->ptr2;
+          req->result--;
+        }
+
+        name[req->result] = '\0';
+        req->ptr = name;
+        req->result = 0;
+      }
+      break;
+
     default:
       break;
   }
@@ -490,15 +509,62 @@ int uv_fs_link(uv_loop_t* loop, uv_fs_t* req, const char* path,
 
 
 int uv_fs_symlink(uv_loop_t* loop, uv_fs_t* req, const char* path,
-    const char* new_path, uv_fs_cb cb) {
+    const char* new_path, int flags, uv_fs_cb cb) {
   WRAP_EIO(UV_FS_SYMLINK, eio_symlink, symlink, ARGS2(path, new_path))
 }
 
 
 int uv_fs_readlink(uv_loop_t* loop, uv_fs_t* req, const char* path,
     uv_fs_cb cb) {
-  assert(0 && "implement me");
-  return -1;
+  size_t size;
+  int status;
+  char* buf;
+
+  status = -1;
+
+  uv_fs_req_init(loop, req, UV_FS_READLINK, cb);
+
+  if (cb) {
+    if ((req->eio = eio_readlink(path, EIO_PRI_DEFAULT, uv__fs_after, req))) {
+      uv_ref(loop);
+      return 0;
+    } else {
+      uv_err_new(loop, ENOMEM);
+      return -1;
+    }
+  } else {
+    /* pathconf(_PC_PATH_MAX) may return -1 to signify that path
+     * lengths have no upper limit or aren't suitable for malloc'ing.
+     */
+    if ((size = pathconf(path, _PC_PATH_MAX)) == -1) {
+#if defined(PATH_MAX)
+      size = PATH_MAX;
+#else
+      size = 4096;
+#endif
+    }
+
+    if ((buf = malloc(size + 1)) == NULL) {
+      uv_err_new(loop, ENOMEM);
+      return -1;
+    }
+
+    if ((size = readlink(path, buf, size)) == -1) {
+      req->errorno = errno;
+      req->result = -1;
+      free(buf);
+    } else {
+      /* Cannot conceivably fail since it shrinks the buffer. */
+      buf = realloc(buf, size + 1);
+      buf[size] = '\0';
+      req->result = 0;
+      req->ptr = buf;
+    }
+
+    return 0;
+  }
+
+  assert(0 && "unreachable");
 }
 
 
index 63d569b..770b4df 100644 (file)
@@ -22,6 +22,7 @@
 #include <assert.h>
 #include <malloc.h>
 #include <direct.h>
+#include <errno.h>
 #include <fcntl.h>
 #include <io.h>
 #include <sys/stat.h>
@@ -36,6 +37,7 @@
 #define UV_FS_FREE_ARG1          0x0004
 #define UV_FS_FREE_PTR           0x0008
 #define UV_FS_CLEANEDUP          0x0010
+#define UV_FS_LAST_ERROR_SET     0x0020
 
 #define STRDUP_ARG(req, i)                                                  \
   req->arg##i = (void*)strdup((const char*)req->arg##i);                    \
   uv_ref((loop));
 
 
+#define SET_UV_LAST_ERROR_FROM_REQ(req)                                     \
+  if (req->flags & UV_FS_LAST_ERROR_SET) {                                  \
+    uv_set_sys_error(req->loop, req->last_error);                           \
+  }
+
+#define SET_REQ_LAST_ERROR(req, error)                                      \
+  req->last_error = error;                                                  \
+  req->flags |= UV_FS_LAST_ERROR_SET;
+
+
 void uv_fs_init() {
   _fmode = _O_BINARY;
 }
@@ -86,6 +98,7 @@ static void uv_fs_req_init_async(uv_loop_t* loop, uv_fs_t* req,
   req->result = 0;
   req->ptr = NULL;
   req->errorno = 0;
+  req->last_error = 0;
   memset(&req->overlapped, 0, sizeof(req->overlapped));
 }
 
@@ -187,6 +200,7 @@ void fs__readdir(uv_fs_t* req, const char* path, int flags) {
 
   if(dir == INVALID_HANDLE_VALUE) {
     result = -1;
+    SET_REQ_LAST_ERROR(req, GetLastError());
     goto done;
   }
 
@@ -267,6 +281,9 @@ void fs__rename(uv_fs_t* req, const char* path, const char* new_path) {
 
 void fs__fsync(uv_fs_t* req, uv_file file) {
   int result = FlushFileBuffers((HANDLE)_get_osfhandle(file)) ? 0 : -1;
+  if (result == -1) {
+    SET_REQ_LAST_ERROR(req, GetLastError());
+  }
   SET_REQ_RESULT(req, result);
 }
 
@@ -383,6 +400,128 @@ void fs__futime(uv_fs_t* req, uv_file file, double atime, double mtime) {
 }
 
 
+void fs__link(uv_fs_t* req, const char* path, const char* new_path) {
+  int result = CreateHardLinkA(new_path, path, NULL) ? 0 : -1;
+  if (result == -1) {
+    SET_REQ_LAST_ERROR(req, GetLastError());
+  }
+  SET_REQ_RESULT(req, result);
+}
+
+
+void fs__symlink(uv_fs_t* req, const char* path, const char* new_path,
+                 int flags) {
+  int result;
+  if (pCreateSymbolicLinkA) {
+    result = pCreateSymbolicLinkA(new_path,
+                                  path,
+                                  flags & UV_FS_SYMLINK_DIR ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0) ? 0 : -1;
+    if (result == -1) {
+      SET_REQ_LAST_ERROR(req, GetLastError());
+    }
+  } else {
+    result = -1;
+    errno = ENOTSUP;
+  }
+  
+  SET_REQ_RESULT(req, result);
+}
+
+
+void fs__readlink(uv_fs_t* req, const char* path) {
+  int result = -1;
+  BOOL rv;
+  HANDLE symlink;
+  void* buffer;
+  DWORD bytes_returned;
+  REPARSE_DATA_BUFFER* reparse_data;
+  int utf8size;
+
+  symlink = CreateFileA(path,
+                        0,
+                        0,
+                        NULL,
+                        OPEN_EXISTING,
+                        FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
+                        NULL);
+
+  if (INVALID_HANDLE_VALUE == symlink) {
+    result = -1;
+    SET_REQ_LAST_ERROR(req, GetLastError());
+    goto done;
+  }
+
+  buffer = malloc(MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
+  if (!buffer) {
+    uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
+  }
+
+  rv = DeviceIoControl(symlink,
+                       FSCTL_GET_REPARSE_POINT,
+                       NULL,
+                       0,
+                       buffer, 
+                       MAXIMUM_REPARSE_DATA_BUFFER_SIZE,
+                       &bytes_returned,
+                       NULL);
+
+  if (!rv) {
+    result = -1;
+    SET_REQ_LAST_ERROR(req, GetLastError());
+    goto done;
+  }
+
+  reparse_data = buffer;
+  if (reparse_data->ReparseTag != IO_REPARSE_TAG_SYMLINK) {
+    result = -1;
+    /* something is seriously wrong */
+    SET_REQ_LAST_ERROR(req, GetLastError());
+    goto done;
+  }
+
+  utf8size = uv_utf16_to_utf8(reparse_data->SymbolicLinkReparseBuffer.PathBuffer + (reparse_data->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(wchar_t)),
+                              reparse_data->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(wchar_t),
+                              NULL,
+                              0);
+  if (!utf8size) {
+    result = -1;
+    SET_REQ_LAST_ERROR(req, GetLastError());
+    goto done;
+  }
+
+  req->ptr = malloc(utf8size + 1);
+  if (!req->ptr) {
+    uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
+  }
+
+  req->flags |= UV_FS_FREE_PTR;
+
+  utf8size = uv_utf16_to_utf8(reparse_data->SymbolicLinkReparseBuffer.PathBuffer + (reparse_data->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(wchar_t)),
+                              reparse_data->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(wchar_t),
+                              req->ptr,
+                              utf8size);
+  if (!utf8size) {
+    result = -1;
+    SET_REQ_LAST_ERROR(req, GetLastError());
+    goto done;
+  }
+
+  ((char*)req->ptr)[utf8size] = '\0';
+  result = 0;
+
+done:
+  if (buffer) {
+    free(buffer);
+  }
+
+  if (symlink != INVALID_HANDLE_VALUE) {
+    CloseHandle(symlink);
+  }
+
+  SET_REQ_RESULT(req, result);
+}
+
+
 void fs__nop(uv_fs_t* req) {
   req->result = 0;
 }
@@ -464,6 +603,15 @@ static DWORD WINAPI uv_fs_thread_proc(void* parameter) {
     case UV_FS_FUTIME:
       fs__futime(req, (uv_file)req->arg0, req->arg4, req->arg5);
       break;
+    case UV_FS_LINK:
+      fs__link(req, (const char*)req->arg0, (const char*)req->arg1);
+      break;
+    case UV_FS_SYMLINK:
+      fs__symlink(req, (const char*)req->arg0, (const char*)req->arg1, (int)req->arg2);
+      break;
+    case UV_FS_READLINK:
+      fs__readlink(req, (const char*)req->arg0);
+      break;
     case UV_FS_CHOWN:
     case UV_FS_FCHOWN:
       fs__nop(req);
@@ -488,6 +636,7 @@ int uv_fs_open(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags,
   } else {
     uv_fs_req_init_sync(loop, req, UV_FS_OPEN);
     fs__open(req, path, flags, mode);
+    SET_UV_LAST_ERROR_FROM_REQ(req);
   }
 
   return 0;
@@ -502,6 +651,7 @@ int uv_fs_close(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) {
   } else {
     uv_fs_req_init_sync(loop, req, UV_FS_CLOSE);
     fs__close(req, file);
+    SET_UV_LAST_ERROR_FROM_REQ(req);
   }
 
   return 0;
@@ -517,6 +667,7 @@ int uv_fs_read(uv_loop_t* loop, uv_fs_t* req, uv_file file, void* buf,
   } else {
     uv_fs_req_init_sync(loop, req, UV_FS_READ);
     fs__read(req, file, buf, length, offset);
+    SET_UV_LAST_ERROR_FROM_REQ(req);
   }
 
   return 0;
@@ -532,6 +683,7 @@ int uv_fs_write(uv_loop_t* loop, uv_fs_t* req, uv_file file, void* buf,
   } else {
     uv_fs_req_init_sync(loop, req, UV_FS_WRITE);
     fs__write(req, file, buf, length, offset);
+    SET_UV_LAST_ERROR_FROM_REQ(req);
   }
 
   return 0;
@@ -548,6 +700,7 @@ int uv_fs_unlink(uv_loop_t* loop, uv_fs_t* req, const char* path,
   } else {
     uv_fs_req_init_sync(loop, req, UV_FS_UNLINK);
     fs__unlink(req, path);
+    SET_UV_LAST_ERROR_FROM_REQ(req);
   }
 
   return 0;
@@ -564,6 +717,7 @@ int uv_fs_mkdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode,
   } else {
     uv_fs_req_init_sync(loop, req, UV_FS_MKDIR);
     fs__mkdir(req, path, mode);
+    SET_UV_LAST_ERROR_FROM_REQ(req);
   }
 
   return 0;
@@ -579,6 +733,7 @@ int uv_fs_rmdir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) {
   } else {
     uv_fs_req_init_sync(loop, req, UV_FS_RMDIR);
     fs__rmdir(req, path);
+    SET_UV_LAST_ERROR_FROM_REQ(req);
   }
 
   return 0;
@@ -595,6 +750,7 @@ int uv_fs_readdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags,
   } else {
     uv_fs_req_init_sync(loop, req, UV_FS_READDIR);
     fs__readdir(req, path, flags);
+    SET_UV_LAST_ERROR_FROM_REQ(req);
   }
 
   return 0;
@@ -603,22 +759,54 @@ int uv_fs_readdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags,
 
 int uv_fs_link(uv_loop_t* loop, uv_fs_t* req, const char* path,
     const char* new_path, uv_fs_cb cb) {
-  assert(0 && "implement me");
-  return -1;
+  if (cb) {
+    uv_fs_req_init_async(loop, req, UV_FS_LINK, cb);
+    WRAP_REQ_ARGS2(req, path, new_path);
+    STRDUP_ARG(req, 0);
+    STRDUP_ARG(req, 1);
+    QUEUE_FS_TP_JOB(loop, req);
+  } else {
+    uv_fs_req_init_sync(loop, req, UV_FS_LINK);
+    fs__link(req, path, new_path);
+    SET_UV_LAST_ERROR_FROM_REQ(req);
+  }
+
+  return 0;
 }
 
 
 int uv_fs_symlink(uv_loop_t* loop, uv_fs_t* req, const char* path,
-    const char* new_path, uv_fs_cb cb) {
-  assert(0 && "implement me");
-  return -1;
+    const char* new_path, int flags, uv_fs_cb cb) {
+  if (cb) {
+    uv_fs_req_init_async(loop, req, UV_FS_SYMLINK, cb);
+    WRAP_REQ_ARGS3(req, path, new_path, flags);
+    STRDUP_ARG(req, 0);
+    STRDUP_ARG(req, 1);
+    QUEUE_FS_TP_JOB(loop, req);
+  } else {
+    uv_fs_req_init_sync(loop, req, UV_FS_SYMLINK);
+    fs__symlink(req, path, new_path, flags);
+    SET_UV_LAST_ERROR_FROM_REQ(req);
+  }
+
+  return 0;
 }
 
 
 int uv_fs_readlink(uv_loop_t* loop, uv_fs_t* req, const char* path,
     uv_fs_cb cb) {
-  assert(0 && "implement me");
-  return -1;
+  if (cb) {
+    uv_fs_req_init_async(loop, req, UV_FS_READLINK, cb);
+    WRAP_REQ_ARGS1(req, path);
+    STRDUP_ARG(req, 0);
+    QUEUE_FS_TP_JOB(loop, req);
+  } else {
+    uv_fs_req_init_sync(loop, req, UV_FS_READLINK);
+    fs__readlink(req, path);
+    SET_UV_LAST_ERROR_FROM_REQ(req);
+  }
+
+  return 0;
 }
 
 
@@ -632,6 +820,7 @@ int uv_fs_chown(uv_loop_t* loop, uv_fs_t* req, const char* path, int uid,
   } else {
     uv_fs_req_init_sync(loop, req, UV_FS_CHOWN);
     fs__nop(req);
+    SET_UV_LAST_ERROR_FROM_REQ(req);
   }
 
   return 0;
@@ -647,6 +836,7 @@ int uv_fs_fchown(uv_loop_t* loop, uv_fs_t* req, uv_file file, int uid,
   } else {
     uv_fs_req_init_sync(loop, req, UV_FS_FCHOWN);
     fs__nop(req);
+    SET_UV_LAST_ERROR_FROM_REQ(req);
   }
 
   return 0;
@@ -684,6 +874,7 @@ int uv_fs_stat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) {
     if (path2) {
       free(path2);
     }
+    SET_UV_LAST_ERROR_FROM_REQ(req);
   }
 
   return 0;
@@ -722,6 +913,7 @@ int uv_fs_lstat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) {
     if (path2) {
       free(path2);
     }
+    SET_UV_LAST_ERROR_FROM_REQ(req);
   }
 
   return 0;
@@ -736,6 +928,7 @@ int uv_fs_fstat(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) {
   } else {
     uv_fs_req_init_sync(loop, req, UV_FS_FSTAT);
     fs__fstat(req, file);
+    SET_UV_LAST_ERROR_FROM_REQ(req);
   }
 
   return 0;
@@ -753,6 +946,7 @@ int uv_fs_rename(uv_loop_t* loop, uv_fs_t* req, const char* path,
   } else {
     uv_fs_req_init_sync(loop, req, UV_FS_RENAME);
     fs__rename(req, path, new_path);
+    SET_UV_LAST_ERROR_FROM_REQ(req);
   }
 
   return 0;
@@ -767,6 +961,7 @@ int uv_fs_fdatasync(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) {
   } else {
     uv_fs_req_init_sync(loop, req, UV_FS_FDATASYNC);
     fs__fsync(req, file);
+    SET_UV_LAST_ERROR_FROM_REQ(req);
   }
 
   return 0;
@@ -781,6 +976,7 @@ int uv_fs_fsync(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) {
   } else {
     uv_fs_req_init_sync(loop, req, UV_FS_FSYNC);
     fs__fsync(req, file);
+    SET_UV_LAST_ERROR_FROM_REQ(req);
   }
 
   return 0;
@@ -796,6 +992,7 @@ int uv_fs_ftruncate(uv_loop_t* loop, uv_fs_t* req, uv_file file,
   } else {
     uv_fs_req_init_sync(loop, req, UV_FS_FTRUNCATE);
     fs__ftruncate(req, file, offset);
+    SET_UV_LAST_ERROR_FROM_REQ(req);
   }
 
   return 0;
@@ -811,6 +1008,7 @@ int uv_fs_sendfile(uv_loop_t* loop, uv_fs_t* req, uv_file out_fd,
   } else {
     uv_fs_req_init_sync(loop, req, UV_FS_SENDFILE);
     fs__sendfile(req, out_fd, in_fd, in_offset, length);
+    SET_UV_LAST_ERROR_FROM_REQ(req);
   }
 
   return 0;
@@ -827,6 +1025,7 @@ int uv_fs_chmod(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode,
   } else {
     uv_fs_req_init_sync(loop, req, UV_FS_CHMOD);
     fs__chmod(req, path, mode);
+    SET_UV_LAST_ERROR_FROM_REQ(req);
   }
 
   return 0;
@@ -842,6 +1041,7 @@ int uv_fs_fchmod(uv_loop_t* loop, uv_fs_t* req, uv_file file, int mode,
   } else {
     uv_fs_req_init_sync(loop, req, UV_FS_FCHMOD);
     fs__fchmod(req, file, mode);
+    SET_UV_LAST_ERROR_FROM_REQ(req);
   }
 
   return 0;
@@ -860,6 +1060,7 @@ int uv_fs_utime(uv_loop_t* loop, uv_fs_t* req, const char* path, double atime,
   } else {
     uv_fs_req_init_sync(loop, req, UV_FS_UTIME);
     fs__utime(req, path, atime, mtime);
+    SET_UV_LAST_ERROR_FROM_REQ(req);
   }
 
   return 0;
@@ -877,6 +1078,7 @@ int uv_fs_futime(uv_loop_t* loop, uv_fs_t* req, uv_file file, double atime,
   } else {
     uv_fs_req_init_sync(loop, req, UV_FS_FUTIME);
     fs__futime(req, file, atime, mtime);
+    SET_UV_LAST_ERROR_FROM_REQ(req);
   }
 
   return 0;
@@ -885,6 +1087,7 @@ int uv_fs_futime(uv_loop_t* loop, uv_fs_t* req, uv_file file, double atime,
 
 void uv_process_fs_req(uv_loop_t* loop, uv_fs_t* req) {
   assert(req->cb);
+  SET_UV_LAST_ERROR_FROM_REQ(req);
   req->cb(req);
 }
 
index bf4d5e3..4a58c14 100644 (file)
@@ -31,6 +31,7 @@ sNtQueryInformationFile pNtQueryInformationFile;
 sNtSetInformationFile pNtSetInformationFile;
 sGetQueuedCompletionStatusEx pGetQueuedCompletionStatusEx;
 sSetFileCompletionNotificationModes pSetFileCompletionNotificationModes;
+sCreateSymbolicLinkA pCreateSymbolicLinkA;
 
 
 void uv_winapi_init() {
@@ -74,4 +75,7 @@ void uv_winapi_init() {
 
   pSetFileCompletionNotificationModes = (sSetFileCompletionNotificationModes)
     GetProcAddress(kernel32_module, "SetFileCompletionNotificationModes");
+
+  pCreateSymbolicLinkA = (sCreateSymbolicLinkA)
+    GetProcAddress(kernel32_module, "CreateSymbolicLinkA");
 }
index 9feee8b..9dc0acb 100644 (file)
           (FACILITY_NTWIN32 << 16) | ERROR_SEVERITY_ERROR)))
 #endif
 
+/* from ntifs.h */
+typedef struct _REPARSE_DATA_BUFFER {
+  ULONG  ReparseTag;
+  USHORT ReparseDataLength;
+  USHORT Reserved;
+  union {
+    struct {
+      USHORT SubstituteNameOffset;
+      USHORT SubstituteNameLength;
+      USHORT PrintNameOffset;
+      USHORT PrintNameLength;
+      ULONG Flags;
+      WCHAR PathBuffer[1];
+    } SymbolicLinkReparseBuffer;
+    struct {
+      USHORT SubstituteNameOffset;
+      USHORT SubstituteNameLength;
+      USHORT PrintNameOffset;
+      USHORT PrintNameLength;
+      WCHAR PathBuffer[1];
+    } MountPointReparseBuffer;
+    struct {
+      UCHAR  DataBuffer[1];
+    } GenericReparseBuffer;
+  } DUMMYUNIONNAME;
+} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
+
 typedef struct _IO_STATUS_BLOCK {
   union {
     NTSTATUS Status;
@@ -4186,6 +4213,8 @@ typedef NTSTATUS (NTAPI *sNtSetInformationFile)
 #define FILE_SKIP_COMPLETION_PORT_ON_SUCCESS    0x1
 #define FILE_SKIP_SET_EVENT_ON_HANDLE           0x2
 
+#define SYMBOLIC_LINK_FLAG_DIRECTORY            0x1
+
 #ifdef __MINGW32__
   typedef struct _OVERLAPPED_ENTRY {
       ULONG_PTR lpCompletionKey;
@@ -4207,6 +4236,11 @@ typedef BOOL (WINAPI* sSetFileCompletionNotificationModes)
              (HANDLE FileHandle,
               UCHAR Flags);
 
+typedef BOOLEAN (WINAPI* sCreateSymbolicLinkA)
+                (LPCSTR lpSymlinkFileName,
+                 LPCSTR lpTargetFileName,
+                 DWORD dwFlags);
+
 
 /* Ntapi function pointers */
 extern sRtlNtStatusToDosError pRtlNtStatusToDosError;
@@ -4217,5 +4251,6 @@ extern sNtSetInformationFile pNtSetInformationFile;
 /* Kernel32 function pointers */
 extern sGetQueuedCompletionStatusEx pGetQueuedCompletionStatusEx;
 extern sSetFileCompletionNotificationModes pSetFileCompletionNotificationModes;
+extern sCreateSymbolicLinkA pCreateSymbolicLinkA;
 
 #endif /* UV_WIN_WINAPI_H_ */
index 27f9173..1f14abf 100644 (file)
@@ -25,6 +25,7 @@
 #include "uv.h"
 #include "task.h"
 
+#include <errno.h>
 #include <string.h> /* memset */
 #include <fcntl.h>
 #include <sys/stat.h>
@@ -62,6 +63,9 @@ static int chmod_cb_count;
 static int fchmod_cb_count;
 static int chown_cb_count;
 static int fchown_cb_count;
+static int link_cb_count;
+static int symlink_cb_count;
+static int readlink_cb_count;
 
 static uv_loop_t* loop;
 
@@ -109,6 +113,29 @@ void check_permission(const char* filename, int mode) {
 }
 
 
+static void link_cb(uv_fs_t* req) {
+  ASSERT(req->fs_type == UV_FS_LINK);
+  ASSERT(req->result == 0);
+  link_cb_count++;
+  uv_fs_req_cleanup(req);
+}
+
+
+static void symlink_cb(uv_fs_t* req) {
+  ASSERT(req->fs_type == UV_FS_SYMLINK);
+  ASSERT(req->result == 0);
+  symlink_cb_count++;
+  uv_fs_req_cleanup(req);
+}
+
+static void readlink_cb(uv_fs_t* req) {
+  ASSERT(req->fs_type == UV_FS_READLINK);
+  ASSERT(req->result == 0);
+  ASSERT(strcmp(req->ptr, "test_file_symlink2") == 0);
+  readlink_cb_count++;
+  uv_fs_req_cleanup(req);
+}
+
 static void fchmod_cb(uv_fs_t* req) {
   ASSERT(req->fs_type == UV_FS_FCHMOD);
   ASSERT(req->result == 0);
@@ -297,9 +324,8 @@ static void readdir_cb(uv_fs_t* req) {
   ASSERT(req->fs_type == UV_FS_READDIR);
   ASSERT(req->result == 2);
   ASSERT(req->ptr);
-  ASSERT(strcmp((const char*)req->ptr, "file1") == 0);
-  ASSERT(strcmp((char*)req->ptr + strlen((const char*)req->ptr) + 1,
-                "file2") == 0);
+  ASSERT(memcmp(req->ptr, "file1\0file2\0", 12) == 0
+      || memcmp(req->ptr, "file2\0file1\0", 12) == 0);
   readdir_cb_count++;
   uv_fs_req_cleanup(req);
   ASSERT(!req->ptr);
@@ -734,6 +760,7 @@ TEST_IMPL(fs_chmod) {
   ASSERT(r == 0);
   uv_run(loop);
   ASSERT(chmod_cb_count == 1);
+  chmod_cb_count = 0; /* reset for the next test */
 #endif
 
   /* async chmod */
@@ -820,4 +847,207 @@ TEST_IMPL(fs_chown) {
   unlink("test_file");
 
   return 0;
+}
+
+
+TEST_IMPL(fs_link) {
+  int r;
+  uv_fs_t req;
+  uv_file file;
+  uv_file link;
+
+  /* Setup. */
+  unlink("test_file");
+  unlink("test_file_link");
+  unlink("test_file_link2");
+
+  uv_init();
+
+  loop = uv_default_loop();
+
+  r = uv_fs_open(loop, &req, "test_file", O_RDWR | O_CREAT,
+      S_IWRITE | S_IREAD, NULL);
+  ASSERT(r == 0);
+  ASSERT(req.result != -1);
+  file = req.result;
+  uv_fs_req_cleanup(&req);
+
+  r = uv_fs_write(loop, &req, file, test_buf, sizeof(test_buf), -1, NULL);
+  ASSERT(r == 0);
+  ASSERT(req.result == sizeof(test_buf));
+  uv_fs_req_cleanup(&req);
+
+  close(file);
+
+  /* sync link */
+  r = uv_fs_link(loop, &req, "test_file", "test_file_link", NULL);
+  ASSERT(r == 0);
+  ASSERT(req.result == 0);
+  uv_fs_req_cleanup(&req);
+
+  r = uv_fs_open(loop, &req, "test_file_link", O_RDWR, 0, NULL);
+  ASSERT(r == 0);
+  ASSERT(req.result != -1);
+  link = req.result;
+  uv_fs_req_cleanup(&req);
+
+  memset(buf, 0, sizeof(buf));
+  r = uv_fs_read(loop, &req, link, buf, sizeof(buf), 0, NULL);
+  ASSERT(r == 0);
+  ASSERT(req.result != -1);
+  ASSERT(strcmp(buf, test_buf) == 0);
+
+  close(link);
+
+  /* async link */
+  r = uv_fs_link(loop, &req, "test_file", "test_file_link2", link_cb);
+  ASSERT(r == 0);
+  uv_run(loop);
+  ASSERT(link_cb_count == 1);
+
+  r = uv_fs_open(loop, &req, "test_file_link2", O_RDWR, 0, NULL);
+  ASSERT(r == 0);
+  ASSERT(req.result != -1);
+  link = req.result;
+  uv_fs_req_cleanup(&req);
+
+  memset(buf, 0, sizeof(buf));
+  r = uv_fs_read(loop, &req, link, buf, sizeof(buf), 0, NULL);
+  ASSERT(r == 0);
+  ASSERT(req.result != -1);
+  ASSERT(strcmp(buf, test_buf) == 0);
+
+  close(link);
+
+  /*
+   * Run the loop just to check we don't have make any extraneous uv_ref()
+   * calls. This should drop out immediately.
+   */
+  uv_run(loop);
+
+  /* Cleanup. */
+  unlink("test_file");
+  unlink("test_file_link");
+  unlink("test_file_link2");
+
+  return 0;
+}
+
+
+TEST_IMPL(fs_symlink) {
+  int r;
+  uv_fs_t req;
+  uv_file file;
+  uv_file link;
+
+  /* Setup. */
+  unlink("test_file");
+  unlink("test_file_symlink");
+  unlink("test_file_symlink2");
+  unlink("test_file_symlink_symlink");
+  unlink("test_file_symlink2_symlink");
+
+  uv_init();
+
+  loop = uv_default_loop();
+
+  r = uv_fs_open(loop, &req, "test_file", O_RDWR | O_CREAT,
+      S_IWRITE | S_IREAD, NULL);
+  ASSERT(r == 0);
+  ASSERT(req.result != -1);
+  file = req.result;
+  uv_fs_req_cleanup(&req);
+
+  r = uv_fs_write(loop, &req, file, test_buf, sizeof(test_buf), -1, NULL);
+  ASSERT(r == 0);
+  ASSERT(req.result == sizeof(test_buf));
+  uv_fs_req_cleanup(&req);
+
+  close(file);
+
+  /* sync symlink */
+  r = uv_fs_symlink(loop, &req, "test_file", "test_file_symlink", 0, NULL);
+  ASSERT(r == 0);
+#ifdef _WIN32
+  if (req.result == -1) {
+    if (req.errorno == ENOTSUP) {
+      /*
+       * Windows doesn't support symlinks on older versions.
+       * We just pass the test and bail out early if we get ENOTSUP.
+       */
+      return 0;
+    } else if (uv_last_error(loop).sys_errno_ == ERROR_PRIVILEGE_NOT_HELD) {
+      /* 
+       * Creating a symlink is only allowed when running elevated.
+       * We pass the test and bail out early if we get ERROR_PRIVILEGE_NOT_HELD.
+       */
+      return 0;
+    }
+  }
+#endif
+  ASSERT(req.result == 0);
+  uv_fs_req_cleanup(&req);
+
+  r = uv_fs_open(loop, &req, "test_file_symlink", O_RDWR, 0, NULL);
+  ASSERT(r == 0);
+  ASSERT(req.result != -1);
+  link = req.result;
+  uv_fs_req_cleanup(&req);
+
+  memset(buf, 0, sizeof(buf));
+  r = uv_fs_read(loop, &req, link, buf, sizeof(buf), 0, NULL);
+  ASSERT(r == 0);
+  ASSERT(req.result != -1);
+  ASSERT(strcmp(buf, test_buf) == 0);
+
+  close(link);
+
+  r = uv_fs_symlink(loop, &req, "test_file_symlink", "test_file_symlink_symlink", 0, NULL);
+  ASSERT(r == 0);
+  r = uv_fs_readlink(loop, &req, "test_file_symlink_symlink", NULL);
+  ASSERT(r == 0);
+  ASSERT(strcmp(req.ptr, "test_file_symlink") == 0);
+  uv_fs_req_cleanup(&req);
+
+  /* async link */
+  r = uv_fs_symlink(loop, &req, "test_file", "test_file_symlink2", 0, symlink_cb);
+  ASSERT(r == 0);
+  uv_run(loop);
+  ASSERT(symlink_cb_count == 1);
+
+  r = uv_fs_open(loop, &req, "test_file_symlink2", O_RDWR, 0, NULL);
+  ASSERT(r == 0);
+  ASSERT(req.result != -1);
+  link = req.result;
+  uv_fs_req_cleanup(&req);
+
+  memset(buf, 0, sizeof(buf));
+  r = uv_fs_read(loop, &req, link, buf, sizeof(buf), 0, NULL);
+  ASSERT(r == 0);
+  ASSERT(req.result != -1);
+  ASSERT(strcmp(buf, test_buf) == 0);
+
+  close(link);
+
+  r = uv_fs_symlink(loop, &req, "test_file_symlink2", "test_file_symlink2_symlink", 0, NULL);
+  ASSERT(r == 0);
+  r = uv_fs_readlink(loop, &req, "test_file_symlink2_symlink", readlink_cb);
+  ASSERT(r == 0);
+  uv_run(loop);
+  ASSERT(readlink_cb_count == 1);
+
+  /*
+   * Run the loop just to check we don't have make any extraneous uv_ref()
+   * calls. This should drop out immediately.
+   */
+  uv_run(loop);
+
+  /* Cleanup. */
+  unlink("test_file");
+  unlink("test_file_symlink");
+  unlink("test_file_symlink_symlink");
+  unlink("test_file_symlink2");
+  unlink("test_file_symlink2_symlink");
+
+  return 0;
 }
\ No newline at end of file
index 94cf03c..fe8f1ed 100644 (file)
@@ -79,6 +79,8 @@ TEST_DECLARE   (fs_async_sendfile)
 TEST_DECLARE   (fs_fstat)
 TEST_DECLARE   (fs_chmod)
 TEST_DECLARE   (fs_chown)
+TEST_DECLARE   (fs_link)
+TEST_DECLARE   (fs_symlink)
 TEST_DECLARE   (threadpool_queue_work_simple)
 #ifdef _WIN32
 TEST_DECLARE   (spawn_detect_pipe_name_collisions_on_windows)
@@ -185,6 +187,8 @@ TASK_LIST_START
   TEST_ENTRY  (fs_fstat)
   TEST_ENTRY  (fs_chmod)
   TEST_ENTRY  (fs_chown)
+  TEST_ENTRY  (fs_link)
+  TEST_ENTRY  (fs_symlink)
 
   TEST_ENTRY  (threadpool_queue_work_simple)
 
index fc422fe..f2d163c 100644 (file)
@@ -384,10 +384,13 @@ static Handle<Value> Symlink(const Arguments& args) {
   String::Utf8Value dest(args[0]->ToString());
   String::Utf8Value path(args[1]->ToString());
 
+  // Just set to zero for now. Support UV_FS_SYMLINK_DIR in the future.
+  int flags = 0;
+
   if (args[2]->IsFunction()) {
-    ASYNC_CALL(symlink, args[2], *dest, *path)
+    ASYNC_CALL(symlink, args[2], *dest, *path, flags)
   } else {
-    SYNC_CALL(symlink, *path, *dest, *path)
+    SYNC_CALL(symlink, *path, *dest, *path, flags)
     return Undefined();
   }
 }