# ubelt._win32_links module

For dealing with symlinks, junctions, and hard-links on windows.

Note

The terminology used here was written before I really understood the difference between symlinks, hardlinks, and junctions. As such it may be inconsistent or incorrect in some places. This might be fixed in the future.

References

[SO18883892]

https://stackoverflow.com/questions/18883892/batch-file-windows-cmd-exe-test-if-a-directory-is-a-link-symlink

[SO21561850]

https://stackoverflow.com/questions/21561850/python-test-for-junction-point-target

[WinTwoFilesSame]

http://timgolden.me.uk/python/win32_how_do_i/see_if_two_files_are_the_same_file.html

[SO6260149]

https://stackoverflow.com/questions/6260149/os-symlink-support-in-windows

[WinDesktopAA365006]

https://msdn.microsoft.com/en-us/library/windows/desktop/aa365006(v=vs.85).aspx

[SU902082]

https://superuser.com/a/902082/215232

Weird Behavior:
  - [ ] In many cases using the win32 API seems to result in privilege errors

      but using shell commands does not have this problem.

ubelt._win32_links._win32_can_symlink(verbose=0, force=False, testing=False)
  Parameters:

    - verbose (int) – verbosity level

    - force (bool) – flag

    - testing (bool) – flag

  Example

      >>> # xdoctest: +REQUIRES(WIN32)
      >>> import ubelt as ub
      >>> _win32_can_symlink(verbose=3, force=True, testing=True)

ubelt._win32_links._symlink(path, link, overwrite=0, verbose=0)

  Windows helper for ub.symlink

ubelt._win32_links._win32_symlink2(path, link, allow_fallback=True, verbose=0)

  Perform a real symbolic link if possible. However, on most versions of windows you need special privileges to create a real symlink. Therefore, we try to create a symlink, but if that fails we fallback to using a junction.

  AFAIK, the main difference between symlinks and junctions are that symlinks can reference relative or absolute paths, where as junctions always reference absolute paths. Not 100% on this though. Windows is weird.

  Note that junctions will not register as links via islink, but I believe real symlinks will.

ubelt._win32_links._win32_symlink(path, link, verbose=0)

  Creates real symlink. This will only work in versions greater than Windows Vista. Creating real symlinks requires admin permissions or at least specially enabled symlink permissions. On Windows 10 enabling developer mode should give you these permissions.

ubelt._win32_links._win32_junction(path, link, verbose=0)

  On older (pre 10) versions of windows we need admin privileges to make symlinks, however junctions seem to work.

  For paths we do a junction (softlink) and for files we use a hard link

  Example

      >>> # xdoc: +REQUIRES(WIN32)
      >>> import ubelt as ub
      >>> root = ub.Path.appdir('ubelt', 'win32_junction').ensuredir()
      >>> ub.delete(root)
      >>> ub.ensuredir(root)
      >>> fpath = join(root, 'fpath.txt')
      >>> dpath = join(root, 'dpath')
      >>> fjunc = join(root, 'fjunc.txt')
      >>> djunc = join(root, 'djunc')
      >>> ub.touch(fpath)
      >>> ub.ensuredir(dpath)
      >>> ub.ensuredir(join(root, 'djunc_fake'))
      >>> ub.ensuredir(join(root, 'djunc_fake with space'))
      >>> ub.touch(join(root, 'djunc_fake with space file'))
      >>> _win32_junction(fpath, fjunc)
      >>> _win32_junction(dpath, djunc)
      >>> # thank god colons are not allowed
      >>> djunc2 = join(root, 'djunc2 [with pathological attrs]')
      >>> _win32_junction(dpath, djunc2)
      >>> _win32_is_junction(djunc)
      >>> ub.writeto(join(djunc, 'afile.txt'), 'foo')
      >>> assert ub.readfrom(join(dpath, 'afile.txt')) == 'foo'
      >>> ub.writeto(fjunc, 'foo')

ubelt._win32_links._win32_is_junction(path)

  Determines if a path is a win32 junction

  Note

  on PyPy this is bugged and will currently return True for a symlinked directory.

  Return type:

    bool

  Example

      >>> # xdoctest: +REQUIRES(WIN32)
      >>> from ubelt._win32_links import _win32_junction, _win32_is_junction
      >>> import ubelt as ub
      >>> root = ub.Path.appdir('ubelt', 'win32_junction').ensuredir()
      >>> ub.delete(root)
      >>> ub.ensuredir(root)
      >>> dpath = root / 'dpath'
      >>> djunc = root / 'djunc'
      >>> dpath.ensuredir()
      >>> _win32_junction(dpath, djunc)
      >>> assert _win32_is_junction(djunc) is True
      >>> assert _win32_is_junction(dpath) is False
      >>> assert _win32_is_junction('notafile') is False

ubelt._win32_links._is_reparse_point(path)

  Check if a directory is a reparse point in windows.

  Note: a reparse point seems like it could be a junction or symlink.

  [SO54678399]

  https://stackoverflow.com/a/54678399/887074

ubelt._win32_links._win32_read_junction(path)

  Returns the location that the junction points, raises ValueError if path is not a junction.

  Example

      >>> # xdoc: +REQUIRES(WIN32)
      >>> import ubelt as ub
      >>> root = ub.Path.appdir('ubelt', 'win32_junction').ensuredir()
      >>> ub.delete(root)
      >>> ub.ensuredir(root)
      >>> dpath = join(root, 'dpath')
      >>> djunc = join(root, 'djunc')
      >>> ub.ensuredir(dpath)
      >>> _win32_junction(dpath, djunc)
      >>> path = djunc
      >>> pointed = _win32_read_junction(path)
      >>> print('pointed = {!r}'.format(pointed))

ubelt._win32_links._win32_rmtree(path, verbose=0)

  rmtree for win32 that treats junctions like directory symlinks. The junction removal portion may not be safe on race conditions.

  There is a known issue [CPythonBug31226] that prevents `shutil.rmtree()` from deleting directories with junctions.

  References

  [CPythonBug31226]

  https://bugs.python.org/issue31226

ubelt._win32_links._win32_is_hardlinked(fpath1, fpath2)

  Test if two hard links point to the same location

  Example

      >>> # xdoc: +REQUIRES(WIN32)
      >>> import ubelt as ub
      >>> root = ub.Path.appdir('ubelt', 'win32_hardlink').ensuredir()
      >>> ub.delete(root)
      >>> ub.ensuredir(root)
      >>> fpath1 = join(root, 'fpath1')
      >>> fpath2 = join(root, 'fpath2')
      >>> ub.touch(fpath1)
      >>> ub.touch(fpath2)
      >>> fjunc1 = _win32_junction(fpath1, join(root, 'fjunc1'))
      >>> fjunc2 = _win32_junction(fpath2, join(root, 'fjunc2'))
      >>> assert _win32_is_hardlinked(fjunc1, fpath1)
      >>> assert _win32_is_hardlinked(fjunc2, fpath2)
      >>> assert not _win32_is_hardlinked(fjunc2, fpath1)
      >>> assert not _win32_is_hardlinked(fjunc1, fpath2)

ubelt._win32_links._win32_dir(path, star='')

  Using the windows cmd shell to get information about a directory