2 * MKLINK.C - mklink internal command.
7 #ifdef INCLUDE_CMD_MKLINK
9 /* There is no API for creating junctions, so we must do it the hard way */
10 static BOOL
CreateJunction(LPCTSTR LinkName
, LPCTSTR TargetName
)
12 /* Structure for reparse point daya when ReparseTag is one of
13 * the tags defined by Microsoft. Copied from MinGW winnt.h */
14 typedef struct _REPARSE_DATA_BUFFER
{
16 WORD ReparseDataLength
;
18 _ANONYMOUS_UNION
union {
20 WORD SubstituteNameOffset
;
21 WORD SubstituteNameLength
;
26 } SymbolicLinkReparseBuffer
;
28 WORD SubstituteNameOffset
;
29 WORD SubstituteNameLength
;
33 } MountPointReparseBuffer
;
36 } GenericReparseBuffer
;
38 } REPARSE_DATA_BUFFER
, *PREPARSE_DATA_BUFFER
;
40 HMODULE hNTDLL
= GetModuleHandle(_T("NTDLL"));
41 BOOLEAN (WINAPI
*RtlDosPathNameToNtPathName_U
)(PCWSTR
, PUNICODE_STRING
, PCWSTR
*, CURDIR
*)
42 = (BOOLEAN (WINAPI
*)(PCWSTR
, PUNICODE_STRING
, PCWSTR
*, CURDIR
*))GetProcAddress(hNTDLL
, "RtlDosPathNameToNtPathName_U");
43 VOID (WINAPI
*RtlFreeUnicodeString
)(PUNICODE_STRING
)
44 = (VOID (WINAPI
*)(PUNICODE_STRING
))GetProcAddress(hNTDLL
, "RtlFreeUnicodeString");
46 TCHAR TargetFullPath
[MAX_PATH
];
48 #define TargetFullPathW TargetFullPath
50 WCHAR TargetFullPathW
[MAX_PATH
];
52 UNICODE_STRING TargetNTPath
;
55 /* The data for this kind of reparse point has two strings:
56 * The first ("SubstituteName") is the full target path in NT format,
57 * the second ("PrintName") is the full target path in Win32 format.
58 * Both of these must be wide-character strings. */
59 if (!RtlDosPathNameToNtPathName_U
||
60 !RtlFreeUnicodeString
||
61 !GetFullPathName(TargetName
, MAX_PATH
, TargetFullPath
, NULL
) ||
63 !MultiByteToWideChar(CP_ACP
, 0, TargetFullPath
, -1, TargetFullPathW
, -1) ||
65 !RtlDosPathNameToNtPathName_U(TargetFullPathW
, &TargetNTPath
, NULL
, NULL
))
70 /* We have both the names we need, so time to create the junction.
71 * Start with an empty directory */
72 if (!CreateDirectory(LinkName
, NULL
))
74 RtlFreeUnicodeString(&TargetNTPath
);
78 /* Open the directory we just created */
79 hJunction
= CreateFile(LinkName
, GENERIC_WRITE
, 0, NULL
,
80 OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
, NULL
);
81 if (hJunction
!= INVALID_HANDLE_VALUE
)
83 /* Allocate a buffer large enough to hold both strings, including trailing NULs */
84 SIZE_T TargetLen
= wcslen(TargetFullPathW
) * sizeof(WCHAR
);
85 DWORD DataSize
= (DWORD
)(FIELD_OFFSET(REPARSE_DATA_BUFFER
, MountPointReparseBuffer
.PathBuffer
)
86 + TargetNTPath
.Length
+ sizeof(WCHAR
)
87 + TargetLen
+ sizeof(WCHAR
));
88 PREPARSE_DATA_BUFFER Data
= _alloca(DataSize
);
90 /* Fill it out and use it to turn the directory into a reparse point */
91 Data
->ReparseTag
= IO_REPARSE_TAG_MOUNT_POINT
;
92 Data
->ReparseDataLength
= (WORD
)(DataSize
- FIELD_OFFSET(REPARSE_DATA_BUFFER
, MountPointReparseBuffer
));
94 Data
->MountPointReparseBuffer
.SubstituteNameOffset
= 0;
95 Data
->MountPointReparseBuffer
.SubstituteNameLength
= TargetNTPath
.Length
;
96 wcscpy(Data
->MountPointReparseBuffer
.PathBuffer
,
98 Data
->MountPointReparseBuffer
.PrintNameOffset
= TargetNTPath
.Length
+ sizeof(WCHAR
);
99 Data
->MountPointReparseBuffer
.PrintNameLength
= (USHORT
)TargetLen
;
100 wcscpy((WCHAR
*)((BYTE
*)Data
->MountPointReparseBuffer
.PathBuffer
101 + Data
->MountPointReparseBuffer
.PrintNameOffset
),
103 if (DeviceIoControl(hJunction
, FSCTL_SET_REPARSE_POINT
,
104 Data
, DataSize
, NULL
, 0, &DataSize
, NULL
))
107 CloseHandle(hJunction
);
108 RtlFreeUnicodeString(&TargetNTPath
);
111 CloseHandle(hJunction
);
113 RemoveDirectory(LinkName
);
114 RtlFreeUnicodeString(&TargetNTPath
);
119 cmd_mklink(LPTSTR param
)
121 HMODULE hKernel32
= GetModuleHandle(_T("KERNEL32"));
123 enum { SYMBOLIC
, HARD
, JUNCTION
} LinkType
= SYMBOLIC
;
129 if (!_tcsncmp(param
, _T("/?"), 2))
131 ConOutResPuts(STRING_MKLINK_HELP
);
135 arg
= split(param
, &argc
, FALSE
, FALSE
);
136 for (i
= 0; i
< argc
; i
++)
138 if (arg
[i
][0] == _T('/'))
140 if (!_tcsicmp(arg
[i
], _T("/D")))
141 Flags
|= 1; /* SYMBOLIC_LINK_FLAG_DIRECTORY */
142 else if (!_tcsicmp(arg
[i
], _T("/H")))
144 else if (!_tcsicmp(arg
[i
], _T("/J")))
148 error_invalid_switch(arg
[i
][1]);
157 error_too_many_parameters(arg
[i
]);
161 Name
[NumFiles
++] = arg
[i
];
168 error_req_param_missing();
174 if (LinkType
== SYMBOLIC
)
176 /* CreateSymbolicLink doesn't exist in old versions of Windows,
177 * so load dynamically */
178 BOOL (WINAPI
*CreateSymbolicLink
)(LPCTSTR
, LPCTSTR
, DWORD
)
180 = (BOOL (WINAPI
*)(LPCTSTR
, LPCTSTR
, DWORD
))GetProcAddress(hKernel32
, "CreateSymbolicLinkW");
182 = (BOOL (WINAPI
*)(LPCTSTR
, LPCTSTR
, DWORD
))GetProcAddress(hKernel32
, "CreateSymbolicLinkA");
184 if (CreateSymbolicLink
&& CreateSymbolicLink(Name
[0], Name
[1], Flags
))
186 ConOutResPrintf(STRING_MKLINK_CREATED_SYMBOLIC
, Name
[0], Name
[1]);
190 else if (LinkType
== HARD
)
192 /* CreateHardLink doesn't exist in old versions of Windows,
193 * so load dynamically */
194 BOOL (WINAPI
*CreateHardLink
)(LPCTSTR
, LPCTSTR
, LPSECURITY_ATTRIBUTES
)
196 = (BOOL (WINAPI
*)(LPCTSTR
, LPCTSTR
, LPSECURITY_ATTRIBUTES
))GetProcAddress(hKernel32
, "CreateHardLinkW");
198 = (BOOL (WINAPI
*)(LPCTSTR
, LPCTSTR
, LPSECURITY_ATTRIBUTES
))GetProcAddress(hKernel32
, "CreateHardLinkA");
200 if (CreateHardLink
&& CreateHardLink(Name
[0], Name
[1], NULL
))
202 ConOutResPrintf(STRING_MKLINK_CREATED_HARD
, Name
[0], Name
[1]);
208 if (CreateJunction(Name
[0], Name
[1]))
210 ConOutResPrintf(STRING_MKLINK_CREATED_JUNCTION
, Name
[0], Name
[1]);
215 ErrorMessage(GetLastError(), _T("MKLINK"));
219 #endif /* INCLUDE_CMD_MKLINK */