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
17 WORD ReparseDataLength
;
19 _ANONYMOUS_UNION
union
23 WORD SubstituteNameOffset
;
24 WORD SubstituteNameLength
;
29 } SymbolicLinkReparseBuffer
;
32 WORD SubstituteNameOffset
;
33 WORD SubstituteNameLength
;
37 } MountPointReparseBuffer
;
41 } GenericReparseBuffer
;
43 } REPARSE_DATA_BUFFER
, *PREPARSE_DATA_BUFFER
;
45 HMODULE hNTDLL
= GetModuleHandle(_T("NTDLL"));
46 BOOLEAN (WINAPI
*RtlDosPathNameToNtPathName_U
)(PCWSTR
, PUNICODE_STRING
, PCWSTR
*, CURDIR
*)
47 = (BOOLEAN (WINAPI
*)(PCWSTR
, PUNICODE_STRING
, PCWSTR
*, CURDIR
*))GetProcAddress(hNTDLL
, "RtlDosPathNameToNtPathName_U");
48 VOID (WINAPI
*RtlFreeUnicodeString
)(PUNICODE_STRING
)
49 = (VOID (WINAPI
*)(PUNICODE_STRING
))GetProcAddress(hNTDLL
, "RtlFreeUnicodeString");
51 TCHAR TargetFullPath
[MAX_PATH
];
53 #define TargetFullPathW TargetFullPath
55 WCHAR TargetFullPathW
[MAX_PATH
];
57 UNICODE_STRING TargetNTPath
;
60 /* The data for this kind of reparse point has two strings:
61 * The first ("SubstituteName") is the full target path in NT format,
62 * the second ("PrintName") is the full target path in Win32 format.
63 * Both of these must be wide-character strings. */
64 if (!RtlDosPathNameToNtPathName_U
||
65 !RtlFreeUnicodeString
||
66 !GetFullPathName(TargetName
, MAX_PATH
, TargetFullPath
, NULL
) ||
68 !MultiByteToWideChar(CP_ACP
, 0, TargetFullPath
, -1, TargetFullPathW
, -1) ||
70 !RtlDosPathNameToNtPathName_U(TargetFullPathW
, &TargetNTPath
, NULL
, NULL
))
75 /* We have both the names we need, so time to create the junction.
76 * Start with an empty directory */
77 if (!CreateDirectory(LinkName
, NULL
))
79 RtlFreeUnicodeString(&TargetNTPath
);
83 /* Open the directory we just created */
84 hJunction
= CreateFile(LinkName
, GENERIC_WRITE
, 0, NULL
,
85 OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
, NULL
);
86 if (hJunction
!= INVALID_HANDLE_VALUE
)
88 /* Allocate a buffer large enough to hold both strings, including trailing NULs */
89 SIZE_T TargetLen
= wcslen(TargetFullPathW
) * sizeof(WCHAR
);
90 DWORD DataSize
= (DWORD
)(FIELD_OFFSET(REPARSE_DATA_BUFFER
, MountPointReparseBuffer
.PathBuffer
)
91 + TargetNTPath
.Length
+ sizeof(WCHAR
)
92 + TargetLen
+ sizeof(WCHAR
));
93 PREPARSE_DATA_BUFFER Data
= _alloca(DataSize
);
95 /* Fill it out and use it to turn the directory into a reparse point */
96 Data
->ReparseTag
= IO_REPARSE_TAG_MOUNT_POINT
;
97 Data
->ReparseDataLength
= (WORD
)(DataSize
- FIELD_OFFSET(REPARSE_DATA_BUFFER
, MountPointReparseBuffer
));
99 Data
->MountPointReparseBuffer
.SubstituteNameOffset
= 0;
100 Data
->MountPointReparseBuffer
.SubstituteNameLength
= TargetNTPath
.Length
;
101 wcscpy(Data
->MountPointReparseBuffer
.PathBuffer
,
102 TargetNTPath
.Buffer
);
103 Data
->MountPointReparseBuffer
.PrintNameOffset
= TargetNTPath
.Length
+ sizeof(WCHAR
);
104 Data
->MountPointReparseBuffer
.PrintNameLength
= (USHORT
)TargetLen
;
105 wcscpy((WCHAR
*)((BYTE
*)Data
->MountPointReparseBuffer
.PathBuffer
106 + Data
->MountPointReparseBuffer
.PrintNameOffset
),
108 if (DeviceIoControl(hJunction
, FSCTL_SET_REPARSE_POINT
,
109 Data
, DataSize
, NULL
, 0, &DataSize
, NULL
))
112 CloseHandle(hJunction
);
113 RtlFreeUnicodeString(&TargetNTPath
);
116 CloseHandle(hJunction
);
118 RemoveDirectory(LinkName
);
119 RtlFreeUnicodeString(&TargetNTPath
);
124 cmd_mklink(LPTSTR param
)
126 HMODULE hKernel32
= GetModuleHandle(_T("KERNEL32"));
128 enum { SYMBOLIC
, HARD
, JUNCTION
} LinkType
= SYMBOLIC
;
134 if (!_tcsncmp(param
, _T("/?"), 2))
136 ConOutResPuts(STRING_MKLINK_HELP
);
140 arg
= split(param
, &argc
, FALSE
, FALSE
);
141 for (i
= 0; i
< argc
; i
++)
143 if (arg
[i
][0] == _T('/'))
145 if (!_tcsicmp(arg
[i
], _T("/D")))
146 Flags
|= 1; /* SYMBOLIC_LINK_FLAG_DIRECTORY */
147 else if (!_tcsicmp(arg
[i
], _T("/H")))
149 else if (!_tcsicmp(arg
[i
], _T("/J")))
153 error_invalid_switch(arg
[i
][1]);
162 error_too_many_parameters(arg
[i
]);
166 Name
[NumFiles
++] = arg
[i
];
173 error_req_param_missing();
179 if (LinkType
== SYMBOLIC
)
181 /* CreateSymbolicLink doesn't exist in old versions of Windows,
182 * so load dynamically */
183 BOOL (WINAPI
*CreateSymbolicLink
)(LPCTSTR
, LPCTSTR
, DWORD
)
185 = (BOOL (WINAPI
*)(LPCTSTR
, LPCTSTR
, DWORD
))GetProcAddress(hKernel32
, "CreateSymbolicLinkW");
187 = (BOOL (WINAPI
*)(LPCTSTR
, LPCTSTR
, DWORD
))GetProcAddress(hKernel32
, "CreateSymbolicLinkA");
189 if (CreateSymbolicLink
&& CreateSymbolicLink(Name
[0], Name
[1], Flags
))
191 ConOutResPrintf(STRING_MKLINK_CREATED_SYMBOLIC
, Name
[0], Name
[1]);
195 else if (LinkType
== HARD
)
197 /* CreateHardLink doesn't exist in old versions of Windows,
198 * so load dynamically */
199 BOOL (WINAPI
*CreateHardLink
)(LPCTSTR
, LPCTSTR
, LPSECURITY_ATTRIBUTES
)
201 = (BOOL (WINAPI
*)(LPCTSTR
, LPCTSTR
, LPSECURITY_ATTRIBUTES
))GetProcAddress(hKernel32
, "CreateHardLinkW");
203 = (BOOL (WINAPI
*)(LPCTSTR
, LPCTSTR
, LPSECURITY_ATTRIBUTES
))GetProcAddress(hKernel32
, "CreateHardLinkA");
205 if (CreateHardLink
&& CreateHardLink(Name
[0], Name
[1], NULL
))
207 ConOutResPrintf(STRING_MKLINK_CREATED_HARD
, Name
[0], Name
[1]);
213 if (CreateJunction(Name
[0], Name
[1]))
215 ConOutResPrintf(STRING_MKLINK_CREATED_JUNCTION
, Name
[0], Name
[1]);
220 ErrorMessage(GetLastError(), _T("MKLINK"));
224 #endif /* INCLUDE_CMD_MKLINK */