Search code examples
javascriptnode.jswindowsappveyor

`fs.stat` function for symlink pointing to directory in Windows


In my windows environment (x64), fs.stat function for symlink pointing to directory brings an error.

On the other hand, in appveyor (ia32), fs.stat function for symlink pointing to directory works well.

This is why? and which behavior is correct?

A part of my test code is

it('Test 001 : fs.stat for symlink', function () {
  var target = path.resolve(workDir, 'sample1');
  console.log('target :' + target);
  fs.mkdirSync(target);                 // create directory
  var link = path.resolve(workDir, 'link1');
  console.log('link :' + link);
  fs.symlinkSync(target, link);         // create symlink to directory

  expect(function() {
    var stat = fs.statSync(link);       // checking symlink stat
    console.log(stat);
  }).not.toThrow();

});

Please see https://github.com/knight9999/WindowsSymbolicLinkCheck for all codes.

The result in my local environment is

Failures:
1) win symlink tests Test 001 : fs.stat for symlink
  Message:
    Expected function not to throw, but it threw Error: EPERM: operation not permitted, stat 'C:\Users\knaito\AppData\Local\Temp\appveyor-tests-winSymlinkTests-VcfaRo\link1'.
  Stack:
    Error: Expected function not to throw, but it threw Error: EPERM: operation not permitted, stat 'C:\Users\knaito\AppData\Local\Temp\appveyor-tests-winSymlinkTests-VcfaRo\link1'.
        at <Jasmine>
        at UserContext.<anonymous> (C:\Users\knaito\Documents\work\cordova-develop\WindowsSymbolicLinkCheck\spec\winSymlink.spec.js:24:12)
        at <Jasmine>

1 spec, 1 failure

The result in appveyor is

Started
target :C:\Users\appveyor\AppData\Local\Temp\1\appveyor-tests-winSymlinkTests-NsaPyJ\sample1
link :C:\Users\appveyor\AppData\Local\Temp\1\appveyor-tests-winSymlinkTests-NsaPyJ\link1
Stats {
  dev: 3567992900,
  mode: 16822,
  nlink: 1,
  uid: 0,
  gid: 0,
  rdev: 0,
  blksize: undefined,
  ino: 844424934502125,
  size: 0,
  blocks: undefined,
  atimeMs: 1537164694047.7703,
  mtimeMs: 1537164694047.7703,
  ctimeMs: 1537164694047.7703,
  birthtimeMs: 1537164694047.7703,
  atime: 2018-09-17T06:11:34.048Z,
  mtime: 2018-09-17T06:11:34.048Z,
  ctime: 2018-09-17T06:11:34.048Z,
  birthtime: 2018-09-17T06:11:34.048Z }
.

Please see https://ci.appveyor.com/project/knight9999/windowssymboliclinkcheck .

Note: I know that the symlinkd or junction is appropriate for directory in windows. However fs.copy for a junction pointing to directory creates new symlink (neither symlinkd nor junction) automatically.


Solution

  • I found this issue in detail by myself.

    For Windows 10, fs.statSync calls nodejs internal function

    INLINE static void fs__stat_impl(uv_fs_t* req, int do_lstat) {
      HANDLE handle;
      DWORD flags;
    
      flags = FILE_FLAG_BACKUP_SEMANTICS;
      if (do_lstat) {
        flags |= FILE_FLAG_OPEN_REPARSE_POINT;
      }
    
      handle = CreateFileW(req->file.pathw,
                           FILE_READ_ATTRIBUTES,
                           FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
                           NULL,
                           OPEN_EXISTING,
                           flags,
                           NULL);
      if (handle == INVALID_HANDLE_VALUE) {
        SET_REQ_WIN32_ERROR(req, GetLastError());
        return;
      }
      ...
    

    in deps/uv/src/win/fs.c. See https://github.com/nodejs/node/blob/9b292272ff3d71a0ebabe46d040346dbd34585db/deps/uv/src/win/fs.c#L1252 .

    CreateFileW returns INVALID_HANDLE_VALUE for a symlink pointing to other directory.

    But for Windows Server 2012 R2 (same as in appveyor), this function works well.

    Therefore

    node -e "console.log(fs.statSync('/path/to/symlink/pointing/other/directory`));"
    

    brings error for Windows 10, works well for Windows Server 2012 R2.

    I conclude we should avoid symlink which points to other directory in Windows 10. A junction is better.