Skip to content

Commit

Permalink
Fix #37
Browse files Browse the repository at this point in the history
  • Loading branch information
Andrew Sampson committed Dec 29, 2016
1 parent 5e942d6 commit ce49f4e
Show file tree
Hide file tree
Showing 4 changed files with 193 additions and 3 deletions.
14 changes: 11 additions & 3 deletions SteamCleaner/Analyzer/FileFinders/RedisFileFinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using SteamCleaner.Utilities.Files;

#endregion

Expand Down Expand Up @@ -38,16 +39,23 @@ public void Search(List<string> files, IEnumerable<string> paths)
{
continue;

}
}
if (!dirRegex.IsMatch(path))
{
continue;
}
AddFiles(files, path);
Search(files, Directory.GetDirectories(path));
var targetPath = path;
if (SymbolicLink.IsSymbolic(path) && SymbolicLink.Exists(path))
{
targetPath = SymbolicLink.GetTarget(path);
}
AddFiles(files, targetPath);
Search(files, Directory.GetDirectories(targetPath));
}
}



private void AddFiles(List<string> files, string path)
{
files.AddRange(from f in Directory.GetFiles(path)
Expand Down
2 changes: 2 additions & 0 deletions SteamCleaner/SteamCleaner.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@
<Compile Include="Model\CleanResult.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Utilities\ExceptionHandler.cs" />
<Compile Include="Utilities\Files\SymbolicLink.cs" />
<Compile Include="Utilities\Files\SymbolicLinkReparseData.cs" />
<Compile Include="Utilities\StringUtilities.cs" />
<Compile Include="Utilities\Tools.cs" />
<EmbeddedResource Include="Properties\Resources.resx">
Expand Down
150 changes: 150 additions & 0 deletions SteamCleaner/Utilities/Files/SymbolicLink.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Win32.SafeHandles;

namespace SteamCleaner.Utilities.Files
{
public static class SymbolicLink
{
private const uint genericReadAccess = 0x80000000;

private const uint fileFlagsForOpenReparsePointAndBackupSemantics = 0x02200000;

private const int ioctlCommandGetReparsePoint = 0x000900A8;

private const uint openExisting = 0x3;

private const uint pathNotAReparsePointError = 0x80071126;

private const uint shareModeAll = 0x7; // Read, Write, Delete

private const uint symLinkTag = 0xA000000C;

private const int targetIsAFile = 0;

private const int targetIsADirectory = 1;

[DllImport("kernel32.dll", SetLastError = true)]
private static extern SafeFileHandle CreateFile(
string lpFileName,
uint dwDesiredAccess,
uint dwShareMode,
IntPtr lpSecurityAttributes,
uint dwCreationDisposition,
uint dwFlagsAndAttributes,
IntPtr hTemplateFile);

[DllImport("kernel32.dll", SetLastError = true)]
static extern bool CreateSymbolicLink(string lpSymlinkFileName, string lpTargetFileName, int dwFlags);

[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool DeviceIoControl(
IntPtr hDevice,
uint dwIoControlCode,
IntPtr lpInBuffer,
int nInBufferSize,
IntPtr lpOutBuffer,
int nOutBufferSize,
out int lpBytesReturned,
IntPtr lpOverlapped);
public static bool IsSymbolic(string path)
{
FileInfo pathInfo = new FileInfo(path);
return pathInfo.Attributes.HasFlag(FileAttributes.ReparsePoint);
}
public static void CreateDirectoryLink(string linkPath, string targetPath)
{
if (!CreateSymbolicLink(linkPath, targetPath, targetIsADirectory) || Marshal.GetLastWin32Error() != 0)
{
try
{
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
}
catch (COMException exception)
{
throw new IOException(exception.Message, exception);
}
}
}

public static void CreateFileLink(string linkPath, string targetPath)
{
if (!CreateSymbolicLink(linkPath, targetPath, targetIsAFile))
{
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
}
}

public static bool Exists(string path)
{
if (!Directory.Exists(path) && !File.Exists(path))
{
return false;
}
string target = GetTarget(path);
return target != null;
}

private static SafeFileHandle getFileHandle(string path)
{
return CreateFile(path, genericReadAccess, shareModeAll, IntPtr.Zero, openExisting,
fileFlagsForOpenReparsePointAndBackupSemantics, IntPtr.Zero);
}

public static string GetTarget(string path)
{
SymbolicLinkReparseData reparseDataBuffer;

using (SafeFileHandle fileHandle = getFileHandle(path))
{
if (fileHandle.IsInvalid)
{
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
}

int outBufferSize = Marshal.SizeOf(typeof(SymbolicLinkReparseData));
IntPtr outBuffer = IntPtr.Zero;
try
{
outBuffer = Marshal.AllocHGlobal(outBufferSize);
int bytesReturned;
bool success = DeviceIoControl(
fileHandle.DangerousGetHandle(), ioctlCommandGetReparsePoint, IntPtr.Zero, 0,
outBuffer, outBufferSize, out bytesReturned, IntPtr.Zero);

fileHandle.Close();

if (!success)
{
if (((uint)Marshal.GetHRForLastWin32Error()) == pathNotAReparsePointError)
{
return null;
}
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
}

reparseDataBuffer = (SymbolicLinkReparseData)Marshal.PtrToStructure(
outBuffer, typeof(SymbolicLinkReparseData));
}
finally
{
Marshal.FreeHGlobal(outBuffer);
}
}
if (reparseDataBuffer.ReparseTag != symLinkTag)
{
return null;
}

string target = Encoding.Unicode.GetString(reparseDataBuffer.PathBuffer,
reparseDataBuffer.PrintNameOffset, reparseDataBuffer.PrintNameLength);

return target;
}
}
}
30 changes: 30 additions & 0 deletions SteamCleaner/Utilities/Files/SymbolicLinkReparseData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace SteamCleaner.Utilities.Files
{
/// <remarks>
/// Refer to http://msdn.microsoft.com/en-us/library/windows/hardware/ff552012%28v=vs.85%29.aspx
/// </remarks>
[StructLayout(LayoutKind.Sequential)]
public struct SymbolicLinkReparseData
{
// Not certain about this!
private const int maxUnicodePathLength = 260 * 2;

public uint ReparseTag;
public ushort ReparseDataLength;
public ushort Reserved;
public ushort SubstituteNameOffset;
public ushort SubstituteNameLength;
public ushort PrintNameOffset;
public ushort PrintNameLength;
public uint Flags;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = maxUnicodePathLength)]
public byte[] PathBuffer;
}
}

0 comments on commit ce49f4e

Please sign in to comment.