libuv icon indicating copy to clipboard operation
libuv copied to clipboard

win,fs: handle AppExecLink reparse points in fstat

Open 11soda11 opened this issue 1 month ago • 4 comments

Currently, libuv throws EACCES when trying to fstat AppExecLink reparse points, because Win32's CreateFileW can't resolve them on its own.

Use fs__readlink_handle as a fallback for reparse points that CreateFileW can't handle.

Fixes this nodejs issue: https://github.com/nodejs/node/issues/36790

This was already attempted (https://github.com/libuv/libuv/pull/4663) but the author of that pr stopped working on it. I believe this is what was intended in https://github.com/libuv/libuv/pull/4663#pullrequestreview-2524405802 and https://github.com/libuv/libuv/pull/4663#pullrequestreview-2573143548 but correct me if im wrong.

11soda11 avatar Nov 19 '25 13:11 11soda11

There are still fixes to be made, will add soon

11soda11 avatar Nov 21 '25 20:11 11soda11

Should be fine now, I changed readlink to accept a preallocated buffer (to reduce unnecessary allocations), but i can put that in another pr if that is preferred.

11soda11 avatar Nov 22 '25 13:11 11soda11

Claude also wrote a test for you (apply with caution since it is AI generated):

--- a/test/test-fs.c
+++ b/test/test-fs.c
@@ -2738,7 +2738,23 @@ TEST_IMPL(fs_lstat_windows_store_apps) {
                  dirent.name) < 0) {
       continue;
     }
-    ASSERT_OK(uv_fs_lstat(loop, &stat_req, file_path, NULL));
+    /* lstat should work on AppExecLink reparse points */
+    ASSERT_OK(uv_fs_lstat(loop, &stat_req, file_path, NULL));
+    uv_fs_req_cleanup(&stat_req);
+
+    /* stat should also work - this is what PR #4936 fixes */
+    r = uv_fs_stat(loop, &stat_req, file_path, NULL);
+    ASSERT_OK(r);
+    uv_fs_req_cleanup(&stat_req);
+
+    /* readlink should return the target path */
+    r = uv_fs_readlink(loop, &stat_req, file_path, NULL);
+    ASSERT_OK(r);
+    ASSERT_NOT_NULL(stat_req.ptr);
+    /* Target should be an absolute path starting with a drive letter */
+    ASSERT_GE(strlen(stat_req.ptr), 3);
+    uv_fs_req_cleanup(&stat_req);
+    break;  /* One successful test is enough */
   }
   MAKE_VALGRIND_HAPPY(loop);
   return 0;

vtjnash avatar Dec 03 '25 21:12 vtjnash

I added the suggested changes + made a small change in fs__create_file (line 439 + 460) to skip an unreachable if statement. Also added the test, which turned out to be a oneliner.

11soda11 avatar Dec 04 '25 20:12 11soda11