├── ln-win ├── stdafx.cpp ├── targetver.h ├── stdafx.h ├── JunctionPoint.h ├── ln-win.cpp ├── ln-win.vcxproj.filters ├── Internal.h ├── ln-win.vcxproj └── JunctionPoint.cpp ├── LICENSE ├── README └── ln-win.sln /ln-win/stdafx.cpp: -------------------------------------------------------------------------------- 1 | // stdafx.cpp : source file that includes just the standard includes 2 | // ln-win.pch will be the pre-compiled header 3 | // stdafx.obj will contain the pre-compiled type information 4 | 5 | #include "stdafx.h" 6 | 7 | // TODO: reference any additional headers you need in STDAFX.H 8 | // and not in this file 9 | -------------------------------------------------------------------------------- /ln-win/targetver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Including SDKDDKVer.h defines the highest available Windows platform. 4 | 5 | // If you wish to build your application for a previous Windows platform, include WinSDKVer.h and 6 | // set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. 7 | 8 | #include 9 | -------------------------------------------------------------------------------- /ln-win/stdafx.h: -------------------------------------------------------------------------------- 1 | // stdafx.h : include file for standard system include files, 2 | // or project specific include files that are used frequently, but 3 | // are changed infrequently 4 | // 5 | 6 | #pragma once 7 | 8 | #include "targetver.h" 9 | 10 | #include 11 | #include 12 | 13 | 14 | 15 | // TODO: reference additional headers your program requires here 16 | -------------------------------------------------------------------------------- /ln-win/JunctionPoint.h: -------------------------------------------------------------------------------- 1 | /* 2 | * NeoSmart JunctionPoint Library 3 | * Author: Mahmoud Al-Qudsi 4 | * Copyright (C) 2011 by NeoSmart Technologies 5 | * This code is released under the terms of the MIT License 6 | */ 7 | 8 | #pragma once 9 | 10 | #include 11 | 12 | #define MAX_RECURSE_DEPTH 10 13 | 14 | namespace neosmart 15 | { 16 | bool CreateJunctionPoint(LPCTSTR origin, LPCTSTR junction); 17 | bool IsDirectoryJunction(LPCTSTR path, DWORD attributes = 0); 18 | bool GetJunctionDestination(LPCTSTR path, OUT LPTSTR destination, DWORD attributes = 0); 19 | bool DeleteJunctionPoint(LPCTSTR path); 20 | } 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2011 by NeoSmart Technologies 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | ln-win consists of a JunctionPoint library and a port of the unix `ln` 2 | tool for Windows, developed and maintained by Mahmoud Al-Qudsi 3 | of NeoSmart Technologies 4 | 5 | 6 | ln.exe 7 | 8 | * ln-win implements both hardlinks and softlinks for both files and 9 | folders 10 | 11 | * Create softlinks by specifying /s in the commandline 12 | 13 | * Hardlinks are created by default (per the posix standard) 14 | 15 | 16 | JunctionPoints library 17 | 18 | * CreateJunctionPoint will create a reparse point (hardlink) for an NTFS 19 | directory 20 | 21 | * GetJunctionDestination will get the final target directory for a 22 | symlink'd or hardlink'd directory. Files are not supported by this 23 | function. 24 | 25 | * DeleteJunctionDestination will remove the reparse point from a 26 | directory 27 | 28 | * IsDirectoryJunction will test whether a directory is a symlink or 29 | junction point to another directory 30 | 31 | 32 | Install: 33 | 34 | * Compile and copy the x64 build to C:\Windows\System32 35 | 36 | * Compile and copy the x86 build to C:\Windows\SysWow64 37 | 38 | * Use from the commandline as usual: ln.exe [/s] source destination 39 | -------------------------------------------------------------------------------- /ln-win.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 11.00 3 | # Visual Studio 2010 4 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ln-win", "ln-win\ln-win.vcxproj", "{85DD1A06-CFDB-45C0-9BBA-42D8A67FFBDE}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Win32 = Debug|Win32 9 | Debug|x64 = Debug|x64 10 | Release|Win32 = Release|Win32 11 | Release|x64 = Release|x64 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {85DD1A06-CFDB-45C0-9BBA-42D8A67FFBDE}.Debug|Win32.ActiveCfg = Debug|Win32 15 | {85DD1A06-CFDB-45C0-9BBA-42D8A67FFBDE}.Debug|Win32.Build.0 = Debug|Win32 16 | {85DD1A06-CFDB-45C0-9BBA-42D8A67FFBDE}.Debug|x64.ActiveCfg = Debug|x64 17 | {85DD1A06-CFDB-45C0-9BBA-42D8A67FFBDE}.Debug|x64.Build.0 = Debug|x64 18 | {85DD1A06-CFDB-45C0-9BBA-42D8A67FFBDE}.Release|Win32.ActiveCfg = Release|Win32 19 | {85DD1A06-CFDB-45C0-9BBA-42D8A67FFBDE}.Release|Win32.Build.0 = Release|Win32 20 | {85DD1A06-CFDB-45C0-9BBA-42D8A67FFBDE}.Release|x64.ActiveCfg = Release|x64 21 | {85DD1A06-CFDB-45C0-9BBA-42D8A67FFBDE}.Release|x64.Build.0 = Release|x64 22 | EndGlobalSection 23 | GlobalSection(SolutionProperties) = preSolution 24 | HideSolutionNode = FALSE 25 | EndGlobalSection 26 | EndGlobal 27 | -------------------------------------------------------------------------------- /ln-win/ln-win.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * NeoSmart JunctionPoint Library 3 | * Author: Mahmoud Al-Qudsi 4 | * Copyright (C) 2011 by NeoSmart Technologies 5 | * This code is released under the terms of the MIT License 6 | */ 7 | 8 | #include "stdafx.h" 9 | #include 10 | 11 | #include "JunctionPoint.h" 12 | 13 | using namespace std; 14 | using namespace neosmart; 15 | 16 | int PrintSyntax() 17 | { 18 | _tprintf(_T("ln [/s] source destination")); 19 | return -1; 20 | } 21 | 22 | int _tmain(int argc, _TCHAR* argv[]) 23 | { 24 | if(argc < 3 || argc > 4 || (argc == 4 && _tcsicmp(argv[1], _T("-s")) != 0 && _tcsicmp(argv[1], _T("/s")))) 25 | { 26 | return PrintSyntax(); 27 | } 28 | 29 | bool softlink = argc == 4; 30 | 31 | CString source, destination; 32 | int index = 1; 33 | 34 | if(softlink) 35 | ++index; 36 | 37 | source = argv[index++]; 38 | destination = argv[index]; 39 | 40 | if(GetFileAttributes(destination) != INVALID_FILE_ATTRIBUTES) 41 | { 42 | _tprintf(_T("Destination already exists!\r\n")); 43 | return -1; 44 | } 45 | 46 | bool isDirectory = (GetFileAttributes(source) & FILE_ATTRIBUTE_DIRECTORY) != 0; 47 | 48 | if(softlink) 49 | CreateSymbolicLink(destination, source, isDirectory ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0); 50 | else if(isDirectory) 51 | CreateJunctionPoint(source, destination); 52 | else 53 | CreateHardLink(destination, source, NULL); 54 | 55 | return 0; 56 | } 57 | -------------------------------------------------------------------------------- /ln-win/ln-win.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Header Files 20 | 21 | 22 | Header Files 23 | 24 | 25 | Header Files 26 | 27 | 28 | Header Files 29 | 30 | 31 | 32 | 33 | Source Files 34 | 35 | 36 | Source Files 37 | 38 | 39 | Source Files 40 | 41 | 42 | -------------------------------------------------------------------------------- /ln-win/Internal.h: -------------------------------------------------------------------------------- 1 | /* 2 | * NeoSmart JunctionPoint Library 3 | * Author: Mahmoud Al-Qudsi 4 | * Copyright (C) 2011 by NeoSmart Technologies 5 | * This code is released under the terms of the MIT License 6 | */ 7 | 8 | #pragma once 9 | 10 | #define SYMLINK_FLAG_RELATIVE 1 11 | #define REPARSE_DATA_BUFFER_HEADER_SIZE offsetof(REPARSE_DATA_BUFFER, GenericReparseBuffer) 12 | 13 | namespace neosmart 14 | { 15 | struct SafeHandle 16 | { 17 | HANDLE Handle; 18 | 19 | SafeHandle() 20 | { 21 | Handle = NULL; 22 | } 23 | 24 | ~SafeHandle() 25 | { 26 | ForceClose(); 27 | } 28 | 29 | bool IsInvalid() 30 | { 31 | return Handle == INVALID_HANDLE_VALUE || Handle == NULL; 32 | } 33 | 34 | void ForceClose() 35 | { 36 | if(!IsInvalid()) 37 | CloseHandle(Handle); 38 | Handle = NULL; 39 | } 40 | }; 41 | 42 | #pragma pack(push, 1) 43 | typedef struct _REPARSE_DATA_BUFFER 44 | { 45 | DWORD ReparseTag; 46 | WORD ReparseDataLength; 47 | WORD Reserved; 48 | union 49 | { 50 | struct 51 | { 52 | WORD SubstituteNameOffset; 53 | WORD SubstituteNameLength; 54 | WORD PrintNameOffset; 55 | WORD PrintNameLength; 56 | ULONG Flags; 57 | WCHAR PathBuffer[1]; 58 | } SymbolicLinkReparseBuffer; 59 | 60 | struct 61 | { 62 | WORD SubstituteNameOffset; 63 | WORD SubstituteNameLength; 64 | WORD PrintNameOffset; 65 | WORD PrintNameLength; 66 | WCHAR PathBuffer[1]; 67 | } MountPointReparseBuffer; 68 | 69 | struct 70 | { 71 | BYTE DataBuffer[1]; 72 | } GenericReparseBuffer; 73 | }; 74 | } REPARSE_DATA_BUFFER; 75 | #pragma pack(pop) 76 | } 77 | -------------------------------------------------------------------------------- /ln-win/ln-win.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {85DD1A06-CFDB-45C0-9BBA-42D8A67FFBDE} 23 | Win32Proj 24 | lnwin 25 | 26 | 27 | 28 | Application 29 | true 30 | Unicode 31 | 32 | 33 | Application 34 | true 35 | Unicode 36 | 37 | 38 | Application 39 | false 40 | true 41 | Unicode 42 | 43 | 44 | Application 45 | false 46 | true 47 | Unicode 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | true 67 | ln 68 | 69 | 70 | true 71 | ln 72 | 73 | 74 | false 75 | ln 76 | 77 | 78 | false 79 | ln 80 | 81 | 82 | 83 | Use 84 | Level3 85 | Disabled 86 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 87 | MultiThreadedDebug 88 | 89 | 90 | Console 91 | true 92 | 93 | 94 | 95 | 96 | Use 97 | Level3 98 | Disabled 99 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 100 | MultiThreadedDebug 101 | 102 | 103 | Console 104 | true 105 | 106 | 107 | 108 | 109 | Level3 110 | Use 111 | MaxSpeed 112 | true 113 | true 114 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 115 | MultiThreaded 116 | 117 | 118 | Console 119 | true 120 | true 121 | true 122 | 123 | 124 | 125 | 126 | Level3 127 | Use 128 | MaxSpeed 129 | true 130 | true 131 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 132 | MultiThreaded 133 | 134 | 135 | Console 136 | true 137 | true 138 | true 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | Create 152 | Create 153 | Create 154 | Create 155 | 156 | 157 | 158 | 159 | 160 | -------------------------------------------------------------------------------- /ln-win/JunctionPoint.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * NeoSmart JunctionPoint Library 3 | * Author: Mahmoud Al-Qudsi 4 | * Copyright (C) 2011 by NeoSmart Technologies 5 | * This code is released under the terms of the MIT License 6 | */ 7 | 8 | #include "stdafx.h" 9 | #include "JunctionPoint.h" 10 | #include "Internal.h" 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | using namespace std; 17 | 18 | namespace neosmart 19 | { 20 | __declspec(thread) int recurseDepth = 0; 21 | 22 | bool CreateJunctionPoint(LPCTSTR origin, LPCTSTR junction) 23 | { 24 | CString nativeTarget; 25 | 26 | //Prepend \??\ to path to mark it as not-for-parsing 27 | nativeTarget.Format(_T("\\??\\%s"), origin); 28 | 29 | //This API only supports Windows-style slashes. 30 | nativeTarget.Replace(_T('/'), _T('\\')); 31 | 32 | //Make sure there's a trailing slash 33 | if(nativeTarget.Right(1) != _T("\\")) 34 | nativeTarget += _T("\\"); 35 | 36 | size_t size = sizeof(REPARSE_DATA_BUFFER) - sizeof(TCHAR) + nativeTarget.GetLength() * sizeof(TCHAR); 37 | auto_ptr reparseBuffer((REPARSE_DATA_BUFFER*) new unsigned char[size]); 38 | 39 | //Fill the reparse buffer 40 | reparseBuffer->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; 41 | reparseBuffer->Reserved = NULL; 42 | 43 | reparseBuffer->MountPointReparseBuffer.SubstituteNameOffset = 0; 44 | reparseBuffer->MountPointReparseBuffer.SubstituteNameLength = nativeTarget.GetLength() * (int) sizeof(TCHAR); 45 | 46 | //No substitute name, point it outside the bounds of the string 47 | reparseBuffer->MountPointReparseBuffer.PrintNameOffset = reparseBuffer->MountPointReparseBuffer.SubstituteNameLength + (int) sizeof(TCHAR); 48 | reparseBuffer->MountPointReparseBuffer.PrintNameLength = 0; 49 | 50 | //Copy the actual string 51 | //_tcscpy(reparseBuffer->MountPointReparseBuffer.PathBuffer, nativeTarget); 52 | memcpy(reparseBuffer->MountPointReparseBuffer.PathBuffer, (LPCTSTR) nativeTarget, reparseBuffer->MountPointReparseBuffer.SubstituteNameLength); 53 | 54 | //Set ReparseDataLength to the size of the MountPointReparseBuffer 55 | //Kind in mind that with the padding for the union (given that SymbolicLinkReparseBuffer is larger), 56 | //this is NOT equal to sizeof(MountPointReparseBuffer) 57 | reparseBuffer->ReparseDataLength = sizeof(REPARSE_DATA_BUFFER) - REPARSE_DATA_BUFFER_HEADER_SIZE - sizeof(TCHAR) + reparseBuffer->MountPointReparseBuffer.SubstituteNameLength; 58 | 59 | //Create the junction directory 60 | CreateDirectory(junction, NULL); 61 | 62 | //Set the reparse point 63 | SafeHandle hDir; 64 | hDir.Handle = CreateFile(junction, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL); 65 | 66 | if(hDir.IsInvalid()) 67 | { 68 | _tprintf(_T("Failed to open directory!\r\n")); 69 | return false; 70 | } 71 | 72 | DWORD bytesReturned = 0; 73 | if(!DeviceIoControl(hDir.Handle, FSCTL_SET_REPARSE_POINT, reparseBuffer.get(), (unsigned int) size, NULL, 0, &bytesReturned, NULL)) 74 | { 75 | RemoveDirectory(junction); 76 | _tprintf(_T("Error issuing DeviceIoControl FSCTL_SET_REPARSE_POINT: 0x%x.\r\n"), GetLastError()); 77 | return false; 78 | } 79 | 80 | return true; 81 | } 82 | 83 | bool GetJunctionDestination(LPCTSTR path, OUT LPTSTR destination, DWORD attributes) 84 | { 85 | if(attributes == 0) 86 | attributes = GetFileAttributes(path); 87 | 88 | if(!IsDirectoryJunction(path, attributes)) 89 | return false; 90 | 91 | SafeHandle hDir; 92 | hDir.Handle = CreateFile(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL); 93 | if (hDir.IsInvalid()) 94 | return false; // Failed to open directory 95 | 96 | BYTE buf[MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; 97 | REPARSE_DATA_BUFFER &ReparseBuffer = (REPARSE_DATA_BUFFER&)buf; 98 | 99 | DWORD dwRet; 100 | if (!DeviceIoControl(hDir.Handle, FSCTL_GET_REPARSE_POINT, NULL, 0, &ReparseBuffer, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &dwRet, NULL)) 101 | { 102 | _tprintf(_T("Error getting reparse destination: 0x%x"), GetLastError()); 103 | return false; 104 | } 105 | 106 | if(ReparseBuffer.ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) 107 | { 108 | //Junction point 109 | ReparseBuffer.MountPointReparseBuffer.SubstituteNameLength /= sizeof(TCHAR); 110 | ReparseBuffer.MountPointReparseBuffer.SubstituteNameOffset /= sizeof(TCHAR); 111 | 112 | LPWSTR pPath = ReparseBuffer.MountPointReparseBuffer.PathBuffer + ReparseBuffer.MountPointReparseBuffer.SubstituteNameOffset; 113 | pPath[ReparseBuffer.MountPointReparseBuffer.SubstituteNameLength] = _T('\0'); 114 | 115 | if (wcsncmp(pPath, L"\\??\\", 4) == 0) 116 | pPath += 4; // Skip 'non-parsed' prefix 117 | 118 | _tcscpy_s(destination, MAX_PATH, pPath); 119 | } 120 | else if(ReparseBuffer.ReparseTag == IO_REPARSE_TAG_SYMLINK) 121 | { 122 | //Symlink 123 | ReparseBuffer.SymbolicLinkReparseBuffer.SubstituteNameLength /= sizeof(TCHAR); 124 | ReparseBuffer.SymbolicLinkReparseBuffer.SubstituteNameOffset /= sizeof(TCHAR); 125 | 126 | LPWSTR pPath = ReparseBuffer.SymbolicLinkReparseBuffer.PathBuffer + ReparseBuffer.SymbolicLinkReparseBuffer.SubstituteNameOffset; 127 | pPath[ReparseBuffer.SymbolicLinkReparseBuffer.SubstituteNameLength] = _T('\0'); 128 | 129 | if (wcsncmp(pPath, L"\\??\\", 4) == 0) 130 | pPath += 4; // Skip 'non-parsed' prefix 131 | 132 | if(ReparseBuffer.SymbolicLinkReparseBuffer.Flags & SYMLINK_FLAG_RELATIVE) 133 | { 134 | //It's a relative path. Construct the relative path and resolve destination 135 | _tcscpy_s(destination, MAX_PATH, path); 136 | PathAddBackslash(destination); 137 | _tcscat_s(destination, MAX_PATH, pPath); 138 | PathResolve(destination, NULL, PRF_REQUIREABSOLUTE); 139 | } 140 | else 141 | { 142 | _tcscpy_s(destination, MAX_PATH, pPath); 143 | } 144 | } 145 | else 146 | { 147 | //Unsupported mount point 148 | return false; 149 | } 150 | 151 | //We may have to do this recursively 152 | ++recurseDepth; 153 | bool result; 154 | if(recurseDepth < MAX_RECURSE_DEPTH) 155 | { 156 | DWORD newAttributes = GetFileAttributes(destination); 157 | if(IsDirectoryJunction(destination, newAttributes)) 158 | result = GetJunctionDestination(CString(destination), destination, newAttributes); 159 | } 160 | else 161 | { 162 | result = false; 163 | } 164 | 165 | --recurseDepth; 166 | return result; 167 | } 168 | 169 | //Doesn't support symlink'd files :( 170 | bool IsDirectoryJunction(LPCTSTR path, DWORD attributes) 171 | { 172 | if(attributes == 0) 173 | attributes = ::GetFileAttributes(path); 174 | 175 | if (attributes == INVALID_FILE_ATTRIBUTES) 176 | return false; //Doesn't exist 177 | 178 | if ((attributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT)) != (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT)) 179 | return false; //Not a directory or not a reparse point 180 | 181 | return true; 182 | } 183 | 184 | //This removes the reparse point but does not delete the directory! 185 | bool DeleteJunctionPoint(LPCTSTR path) 186 | { 187 | REPARSE_GUID_DATA_BUFFER reparsePoint = {0}; 188 | reparsePoint.ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; 189 | 190 | SafeHandle hDir; 191 | hDir.Handle = CreateFile(path, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL); 192 | 193 | //Does the path exist? 194 | if(hDir.IsInvalid()) 195 | return false; 196 | 197 | DWORD returnedBytes; 198 | DWORD result = DeviceIoControl(hDir.Handle, FSCTL_DELETE_REPARSE_POINT, &reparsePoint, REPARSE_GUID_DATA_BUFFER_HEADER_SIZE, NULL, 0, &returnedBytes, NULL) != 0; 199 | if(result == 0) 200 | { 201 | _tprintf_s(_T("Failed to issue FSCTL_DELETE_REPARSE_POINT. Last error: 0x%x"), GetLastError()); 202 | return false; 203 | } 204 | else 205 | { 206 | return true; 207 | } 208 | } 209 | } 210 | --------------------------------------------------------------------------------