1 /**
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with this
4 * work for additional information regarding copyright ownership. The ASF
5 * licenses this file to you under the Apache License, Version 2.0 (the
6 * "License"); you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 * License for the specific language governing permissions and limitations under
15 * the License.
16 */
17
18 #include "winutils.h"
19
20
21 //----------------------------------------------------------------------------
22 // The Windows SDK does not include the definition of REPARSE_DATA_BUFFER. To
23 // avoid adding a dependency on the WDK we define the structure here.
24 // Reference: http://msdn.microsoft.com/en-us/library/ff552012.aspx
25 //
26 #pragma warning(push)
27 #pragma warning(disable: 4201) // nonstandard extension: nameless struct/union
28 #pragma pack(push, 1)
29 typedef struct _REPARSE_DATA_BUFFER {
30 ULONG ReparseTag;
31 USHORT ReparseDataLength;
32 USHORT Reserved;
33 union {
34 struct {
35 USHORT SubstituteNameOffset;
36 USHORT SubstituteNameLength;
37 USHORT PrintNameOffset;
38 USHORT PrintNameLength;
39 ULONG Flags;
40 WCHAR PathBuffer[1];
41 } SymbolicLinkReparseBuffer;
42 struct {
43 USHORT SubstituteNameOffset;
44 USHORT SubstituteNameLength;
45 USHORT PrintNameOffset;
46 USHORT PrintNameLength;
47 WCHAR PathBuffer[1];
48 } MountPointReparseBuffer;
49 struct {
50 UCHAR DataBuffer[1];
51 } GenericReparseBuffer;
52 };
53 } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
54 #pragma pack(pop)
55 #pragma warning(pop)
56
57
58 //----------------------------------------------------------------------------
59 // Function: Readlink
60 //
61 // Description:
62 // Prints the target of a symbolic link to stdout.
63 //
64 // The return codes and output are modeled after the UNIX readlink command.
65 // Hence no error messages are printed. Unlike the UNIX readlink, no options
66 // are accepted.
67 //
68 // Returns:
69 // 0: on success
70 // 1: on all errors
71 //
72 // Notes:
73 //
Readlink(__in int argc,__in_ecount (argc)wchar_t * argv[])74 int Readlink(__in int argc, __in_ecount(argc) wchar_t *argv[])
75 {
76 DWORD bytesReturned;
77 DWORD bufferSize = 1024; // Start off with a 1KB buffer.
78 HANDLE hFile = INVALID_HANDLE_VALUE;
79 PWSTR longLinkName = NULL;
80 PWCHAR printName = NULL;
81 PREPARSE_DATA_BUFFER pReparseData = NULL;
82 USHORT printNameLength;
83 USHORT printNameOffset;
84 DWORD result;
85 BOOLEAN succeeded = FALSE;
86
87 if (argc != 2)
88 {
89 ReadlinkUsage();
90 goto Cleanup;
91 }
92
93 if (ConvertToLongPath(argv[1], &longLinkName) != ERROR_SUCCESS)
94 {
95 goto Cleanup;
96 }
97
98 // Get a handle to the link to issue the FSCTL.
99 // FILE_FLAG_BACKUP_SEMANTICS is needed to open directories.
100 // FILE_FLAG_OPEN_REPARSE_POINT disables normal reparse point processing
101 // so we can query the symlink.
102 //
103 hFile = CreateFileW(longLinkName,
104 0, // no rights needed to issue the FSCTL.
105 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
106 NULL,
107 OPEN_EXISTING,
108 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
109 NULL);
110
111 if (hFile == INVALID_HANDLE_VALUE)
112 {
113 goto Cleanup;
114 }
115
116 for (;;)
117 {
118 pReparseData = (PREPARSE_DATA_BUFFER) LocalAlloc(LMEM_FIXED, bufferSize);
119
120 if (pReparseData == NULL)
121 {
122 goto Cleanup;
123 }
124
125 // Issue the FSCTL to query the link information.
126 //
127 result = DeviceIoControl(hFile,
128 FSCTL_GET_REPARSE_POINT,
129 NULL,
130 0,
131 pReparseData,
132 bufferSize,
133 &bytesReturned,
134 NULL);
135
136 if (result != 0)
137 {
138 // Success!
139 //
140 break;
141 }
142 else if ((GetLastError() == ERROR_INSUFFICIENT_BUFFER) ||
143 (GetLastError() == ERROR_MORE_DATA))
144 {
145 // Retry with a larger buffer.
146 //
147 LocalFree(pReparseData);
148 bufferSize *= 2;
149 }
150 else
151 {
152 // Unrecoverable error.
153 //
154 goto Cleanup;
155 }
156 }
157
158 if (pReparseData->ReparseTag != IO_REPARSE_TAG_SYMLINK)
159 {
160 // Doesn't look like a symlink.
161 //
162 goto Cleanup;
163 }
164
165 // MSDN does not guarantee that the embedded paths in REPARSE_DATA_BUFFER
166 // will be NULL terminated. So we copy the string to a separate buffer and
167 // NULL terminate it before printing.
168 //
169 printNameLength = pReparseData->SymbolicLinkReparseBuffer.PrintNameLength;
170 printNameOffset = pReparseData->SymbolicLinkReparseBuffer.PrintNameOffset;
171 printName = (PWCHAR) LocalAlloc(LMEM_FIXED, printNameLength + 1);
172
173 if (printName == NULL)
174 {
175 goto Cleanup;
176 }
177
178 memcpy(
179 printName,
180 pReparseData->SymbolicLinkReparseBuffer.PathBuffer + printNameOffset,
181 printNameLength);
182
183 printName[printNameLength / sizeof(WCHAR)] = L'\0';
184
185 fwprintf(stdout, L"%ls", printName);
186 succeeded = TRUE;
187
188 Cleanup:
189 if (hFile != INVALID_HANDLE_VALUE)
190 {
191 CloseHandle(hFile);
192 }
193
194 if (printName != NULL)
195 {
196 LocalFree(printName);
197 }
198
199 if (pReparseData != NULL)
200 {
201 LocalFree(pReparseData);
202 }
203
204 if (longLinkName != NULL)
205 {
206 LocalFree(longLinkName);
207 }
208
209 return (succeeded ? EXIT_SUCCESS : EXIT_FAILURE);
210 }
211
ReadlinkUsage()212 void ReadlinkUsage()
213 {
214 fwprintf(stdout, L"\
215 Usage: readlink [LINKNAME]\n\
216 Prints the target of a symbolic link\n\
217 The output and returned error codes are similar to the UNIX\n\
218 readlink command. However no options are accepted.\n\
219 \n\
220 0 is returned on success.\n\
221 1 is returned for all errors.\n\
222 \n");
223 }
224
225