Skip to content

Commit 65ae8d8

Browse files
committed
Initial code import.
0 parents  commit 65ae8d8

10 files changed

+620
-0
lines changed

ln-win.sln

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
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

ln-win/Internal.h

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* NeoSmart JunctionPoint Library
3+
* Author: Mahmoud Al-Qudsi <[email protected]>
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+
}

ln-win/JunctionPoint.cpp

+209
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
/*
2+
* NeoSmart JunctionPoint Library
3+
* Author: Mahmoud Al-Qudsi <[email protected]>
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 <Shlobj.h>
13+
#include <atlstr.h>
14+
#include <memory>
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<REPARSE_DATA_BUFFER> 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+
}

ln-win/JunctionPoint.h

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* NeoSmart JunctionPoint Library
3+
* Author: Mahmoud Al-Qudsi <[email protected]>
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 <Windows.h>
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+
}

ln-win/ln-win.cpp

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* NeoSmart JunctionPoint Library
3+
* Author: Mahmoud Al-Qudsi <[email protected]>
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 <atlstr.h>
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+
}

0 commit comments

Comments
 (0)