Last active
June 13, 2016 22:47
-
-
Save l0ki000/a78eda595abd5dd9ff0153989242753f to your computer and use it in GitHub Desktop.
Script is going to find broken cygwin soft links, and repair it. Fixing issue which is described here: https://www.cygwin.com/faq.html#faq.using.symlinkstoppedworking
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/perl | |
# Trying to fix cygwin symlink issue, which is described here: | |
# https://www.cygwin.com/faq.html#faq.using.symlinkstoppedworking | |
use warnings; | |
use strict; | |
use feature ":5.12"; | |
use English; | |
use File::Basename; | |
use File::Copy; | |
use Getopt::Long; | |
use Readonly; | |
use Encode qw(encode decode); | |
Readonly::Scalar my $symlink => '!<symlink>'; | |
sub getUTF8SymlinkAsUTF16 | |
{ | |
my $file = shift @_; | |
my $result; | |
# read as binary | |
open (my $fh, "<", $file) or die "Can't open < $file: $ERRNO"; | |
my $line = readline($fh); | |
close($fh); | |
undef $fh; | |
# proper symlinks always contain $symlink prefix | |
$line =~ m/$symlink(.*)/; | |
if (not defined $1) | |
{ | |
return undef; | |
} | |
# try to decode the rest of the string | |
eval | |
{ | |
$result = encode("UTF-16LE", $1); | |
1; | |
}; | |
if ($EVAL_ERROR) | |
{ | |
warn "$file failed to encode UTF-16LE: " . $EVAL_ERROR; | |
return undef; | |
} | |
# forcing UTF-16LE BOM, as perl for uknown reason skipping it | |
return $symlink . "\xFF\xFE" . $result; | |
} | |
sub getUTF16Symlink | |
{ | |
my $file = shift @_; | |
my $result; | |
# read as binary and try to convert to UTF-16 | |
open (my $fh, "<", $file) or die "Can't open < $file: $ERRNO"; | |
my $line = readline($fh); | |
close($fh); | |
undef $fh; | |
# proper symlinks always contain $symlink prefix | |
$line =~ m/$symlink(.*)/; | |
if (not defined $1) | |
{ | |
return undef; | |
} | |
# try to decode the rest of the string | |
eval | |
{ | |
$result = decode("UTF-16", $1); | |
1; | |
}; | |
if ($EVAL_ERROR) | |
{ | |
warn "$file failed to decode UTF-16: " . $EVAL_ERROR; | |
return undef; | |
} | |
return $result; | |
} | |
sub setAttrib | |
{ | |
my $file = shift @_; | |
# making file path applicable for windows attrib.exe | |
my $tmp_file = `cygpath --windows $file`; | |
die "Failed to get windows path: $ERRNO" if $CHILD_ERROR; | |
# escaping all "\" as we are executing it in bash | |
$tmp_file =~ s/\\/\\\\/g; | |
`attrib.exe +A +S $tmp_file`; | |
warn "Failed to change attributes: $ERRNO" if $CHILD_ERROR; | |
} | |
# options and help | |
my $make_backup; | |
my $dry_run; | |
GetOptions('b|backup' => \$make_backup, | |
'd|dry-run' => \$dry_run); | |
my $working_dir = shift @ARGV; | |
if (!$working_dir) | |
{ | |
my $script_name = fileparse($PROGRAM_NAME); | |
say "Usage: $script_name [OPTIONS] <working_dir>"; | |
say "Script is going to grep recursively from <working_dir> for \"$symlink\" and if found broken symlink file, restore it."; | |
say "\nOptions:"; | |
say "\t-b, --backup\tRename old symlinks to bak files, instead of removing"; | |
say "\t-d, --dry-run\tDry run"; | |
exit 1; | |
} | |
# grep all files for magic <!symlink> | |
my $cmd_line = "grep -rl " . "\'$symlink\' " . $working_dir; | |
my @tmp_files = `$cmd_line`; | |
die "Failed to grep: $ERRNO" if ($CHILD_ERROR == 2); | |
# NB: cygwin grep doesn't recognize symbolic links as a files, ie they shoudn't be listed by grep, but double check here | |
# check each file is not recognized by cygwin as a symlink or executable/lib | |
my @files; | |
foreach my $file (@tmp_files) | |
{ | |
chomp($file); | |
my $file_type = `file $file`; | |
if ($file_type =~ m/executable/) | |
{ | |
say "$file -- is an executable, skipping"; | |
next; | |
} | |
my $stat = `stat --format="%n -- %F" $file`; | |
if ($stat =~ m/-- symbolic link/) | |
{ | |
say "$file -- is a symbolic link, skipping"; | |
next; | |
} | |
push @files, $file; | |
} | |
# reading the file, correcting attr && fixing | |
foreach my $file (@files) | |
{ | |
my $link = getUTF16Symlink($file); | |
# setting "archive" and "system" attributes | |
if ($link) | |
{ | |
say "$file -- is an UTF-16 link to: $link"; | |
if (not $dry_run) | |
{ | |
setAttrib($file); | |
} | |
# this is good UTF-16 symlink, and after setting proper attr everything should work | |
next; | |
} | |
# if we got here, file has <!symlink> and failed to be UTF-16 decoded | |
if ($make_backup && not $dry_run) | |
{ | |
say "$file -- making a backup copy as $file.bak"; | |
copy($file, $file . ".bak") | |
or die "Backup copy failed: $CHILD_ERROR"; | |
} | |
$link = getUTF8SymlinkAsUTF16($file); | |
if ($link) | |
{ | |
$link =~ m/$symlink(.*)/; | |
my $tmp_link = decode("UTF-16", $1); | |
say "$file -- is an UTF8 link to: $tmp_link"; | |
if (not $dry_run) | |
{ | |
open(my $fh, ">", $file) or die "Can't open > $file: $ERRNO"; | |
print $fh $link; | |
close($fh); | |
setAttrib($file); | |
} | |
next; | |
} | |
# out of ideas | |
if (not $link) | |
{ | |
warn "$file -- is a miracle, out of ideas, sorry"; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment