Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

delete Windows symbolic links in a perl script?

Tags:

symlink

perl

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.

like image 426
Peeter Joot Avatar asked Mar 01 '12 18:03

Peeter Joot


1 Answers

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.

like image 181
sirgeorge Avatar answered Sep 21 '22 08:09

sirgeorge