delete Windows symbolic links in a perl script?
Asked Answered
P

1

7

Suppose I create some Windows symbolic links, as in:

rd /s /q source withlink linkdir
mkdir source
mkdir withlink
echo blah > source/myfile
cd withlink
touch blah
mklink mylink ..\source\myfile
@REM mklink /d linkdir ..\source
cd ..

I can delete the directory containing the symlinks in the shell with

rd /s /q withlink

I have the same task to do in a perl script where we currently use cygwin 'rm -rf'. Unfortunately we are using cygwin 1.5 and rm and rm -rf don't work properly in that version on the symbolic links I'd like to use (they delete symbolic link contents instead of the symlinks).

If I try:

use File::Path qw( rmtree ) ;
rmtree( ['withlink'] ) ;

This works nicely, provided I don't have any directory symbolic links (like the one REM'ed out in the create-the-links sequence above), then perl's rmtree ends up behaving like cygwin, and I end up with the directory contents of my original directory deleted.

Does anybody have a suggestion of an alternate perl recursive directory deletion method that I could use. I thought of just a shell callout:

system("rd /s /q withlink") ;

but this requires I test the platform and have different perl code for Windows and Unix.

EDIT: Note that, unlike Unix, unlink() does not work to remove a directory symlink, at least with perl v5.6.0, which is what our build system is currently using. However, rmdir() does work to remove a windows directory symlink.

Pensioner answered 1/3, 2012 at 18:26 Comment(1)
sorry, I edited my title slightly while I was still asking my question, and prematurely and accidentally submitted the question in an incomplete state.Pensioner
M
4

You must use rmdir to remove directory symlinks and junction points in Windows and you must use simply unlink to remove symlinks to files. The reason is that a directory symlink and junction point is really an empty directory with additional file system metadata (called reparse point data). Similarily, symlinks to files are empty files with reparse point data. Such a directory or file is called reparse point when you read Microsoft NTFS documentation. Reparse point type is determined by so called reparse point TAG. There are two reparse points' tags visible to users as "links":

  1. IO_REPARSE_TAG_SYMLINK - if set on a directory - it is a symbolic link to a directory, if set on file - it is a symlink to a file
  2. IO_REPARSE_TAG_MOUNT_POINT - can only be set on a directory - it is so called "junction point". It could be "link" to another directory, but it could be "mount point" for entire device (volume).

To summarise:

  1. You remove symlinks to directories and junction points as if they were empty directories (in fact if you check attributes of such a thing it has two: FILE_ATTRIBUTE_DIRECTORY and FILE_ATTRIBUTE_REPARSE_POINT).
  2. You remove symlinks to files as if they were files.
  3. In both cases mentioned above - only link/junction is removed (not the target).
  4. To find out whether file/folder is a link (all kinds): if you call GetFileAttributes or GetFileInformationByHandle or FindNextFile then attributtes contain FILE_ATTRIBUTE_REPARSE_POINT flag (e.g.: WIN32_FIND_DATA::dwFileAttributes). If it is a link to a directory it also contains flag FILE_ATTRIBUTE_DIRECTORY (see 1.).

Hope that helps.

Maragretmarala answered 15/3, 2012 at 5:44 Comment(2)
That's consistent with what I found, but thanks for the info. Do you know of a way to query these file system tags within perl, or would you have to write C code that calls FindFirstFile (and friends)?Pensioner
Yes, I do have C++ code that can query/set/delete these reparse tags which works from Windows XP and up. The code is too long and complicated to post it here, it would require writing entire article about it. It is mostly based on my experiments and reading MSDN. Beginning from Vista you can use CreateSymbolicLink function to create symlinks or CreateHardLink (XP and up) to create hardlinks. I updated my answer - how to detect links/junctions - please see above.Maragretmarala

© 2022 - 2024 — McMap. All rights reserved.