readlink.c 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  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. #include "winutils.h"
  18. //----------------------------------------------------------------------------
  19. // The Windows SDK does not include the definition of REPARSE_DATA_BUFFER. To
  20. // avoid adding a dependency on the WDK we define the structure here.
  21. // Reference: http://msdn.microsoft.com/en-us/library/ff552012.aspx
  22. //
  23. #pragma warning(push)
  24. #pragma warning(disable: 4201) // nonstandard extension: nameless struct/union
  25. #pragma pack(push, 1)
  26. typedef struct _REPARSE_DATA_BUFFER {
  27. ULONG ReparseTag;
  28. USHORT ReparseDataLength;
  29. USHORT Reserved;
  30. union {
  31. struct {
  32. USHORT SubstituteNameOffset;
  33. USHORT SubstituteNameLength;
  34. USHORT PrintNameOffset;
  35. USHORT PrintNameLength;
  36. ULONG Flags;
  37. WCHAR PathBuffer[1];
  38. } SymbolicLinkReparseBuffer;
  39. struct {
  40. USHORT SubstituteNameOffset;
  41. USHORT SubstituteNameLength;
  42. USHORT PrintNameOffset;
  43. USHORT PrintNameLength;
  44. WCHAR PathBuffer[1];
  45. } MountPointReparseBuffer;
  46. struct {
  47. UCHAR DataBuffer[1];
  48. } GenericReparseBuffer;
  49. };
  50. } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
  51. #pragma pack(pop)
  52. #pragma warning(pop)
  53. //----------------------------------------------------------------------------
  54. // Function: Readlink
  55. //
  56. // Description:
  57. // Prints the target of a symbolic link to stdout.
  58. //
  59. // The return codes and output are modeled after the UNIX readlink command.
  60. // Hence no error messages are printed. Unlike the UNIX readlink, no options
  61. // are accepted.
  62. //
  63. // Returns:
  64. // 0: on success
  65. // 1: on all errors
  66. //
  67. // Notes:
  68. //
  69. int Readlink(__in int argc, __in_ecount(argc) wchar_t *argv[])
  70. {
  71. DWORD bytesReturned;
  72. DWORD bufferSize = 1024; // Start off with a 1KB buffer.
  73. HANDLE hFile = INVALID_HANDLE_VALUE;
  74. PWSTR longLinkName = NULL;
  75. PWCHAR printName = NULL;
  76. PREPARSE_DATA_BUFFER pReparseData = NULL;
  77. USHORT printNameLength;
  78. USHORT printNameOffset;
  79. DWORD result;
  80. BOOLEAN succeeded = FALSE;
  81. if (argc != 2)
  82. {
  83. ReadlinkUsage();
  84. goto Cleanup;
  85. }
  86. if (ConvertToLongPath(argv[1], &longLinkName) != ERROR_SUCCESS)
  87. {
  88. goto Cleanup;
  89. }
  90. // Get a handle to the link to issue the FSCTL.
  91. // FILE_FLAG_BACKUP_SEMANTICS is needed to open directories.
  92. // FILE_FLAG_OPEN_REPARSE_POINT disables normal reparse point processing
  93. // so we can query the symlink.
  94. //
  95. hFile = CreateFileW(longLinkName,
  96. 0, // no rights needed to issue the FSCTL.
  97. FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
  98. NULL,
  99. OPEN_EXISTING,
  100. FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
  101. NULL);
  102. if (hFile == INVALID_HANDLE_VALUE)
  103. {
  104. goto Cleanup;
  105. }
  106. for (;;)
  107. {
  108. pReparseData = (PREPARSE_DATA_BUFFER) LocalAlloc(LMEM_FIXED, bufferSize);
  109. if (pReparseData == NULL)
  110. {
  111. goto Cleanup;
  112. }
  113. // Issue the FSCTL to query the link information.
  114. //
  115. result = DeviceIoControl(hFile,
  116. FSCTL_GET_REPARSE_POINT,
  117. NULL,
  118. 0,
  119. pReparseData,
  120. bufferSize,
  121. &bytesReturned,
  122. NULL);
  123. if (result != 0)
  124. {
  125. // Success!
  126. //
  127. break;
  128. }
  129. else if ((GetLastError() == ERROR_INSUFFICIENT_BUFFER) ||
  130. (GetLastError() == ERROR_MORE_DATA))
  131. {
  132. // Retry with a larger buffer.
  133. //
  134. LocalFree(pReparseData);
  135. bufferSize *= 2;
  136. }
  137. else
  138. {
  139. // Unrecoverable error.
  140. //
  141. goto Cleanup;
  142. }
  143. }
  144. if (pReparseData->ReparseTag != IO_REPARSE_TAG_SYMLINK)
  145. {
  146. // Doesn't look like a symlink.
  147. //
  148. goto Cleanup;
  149. }
  150. // MSDN does not guarantee that the embedded paths in REPARSE_DATA_BUFFER
  151. // will be NULL terminated. So we copy the string to a separate buffer and
  152. // NULL terminate it before printing.
  153. //
  154. printNameLength = pReparseData->SymbolicLinkReparseBuffer.PrintNameLength;
  155. printNameOffset = pReparseData->SymbolicLinkReparseBuffer.PrintNameOffset;
  156. printName = (PWCHAR) LocalAlloc(LMEM_FIXED, printNameLength + 1);
  157. if (printName == NULL)
  158. {
  159. goto Cleanup;
  160. }
  161. memcpy(
  162. printName,
  163. pReparseData->SymbolicLinkReparseBuffer.PathBuffer + printNameOffset,
  164. printNameLength);
  165. printName[printNameLength / sizeof(WCHAR)] = L'\0';
  166. fwprintf(stdout, L"%ls", printName);
  167. succeeded = TRUE;
  168. Cleanup:
  169. if (hFile != INVALID_HANDLE_VALUE)
  170. {
  171. CloseHandle(hFile);
  172. }
  173. if (printName != NULL)
  174. {
  175. LocalFree(printName);
  176. }
  177. if (pReparseData != NULL)
  178. {
  179. LocalFree(pReparseData);
  180. }
  181. if (longLinkName != NULL)
  182. {
  183. LocalFree(longLinkName);
  184. }
  185. return (succeeded ? EXIT_SUCCESS : EXIT_FAILURE);
  186. }
  187. void ReadlinkUsage()
  188. {
  189. fwprintf(stdout, L"\
  190. Usage: readlink [LINKNAME]\n\
  191. Prints the target of a symbolic link\n\
  192. The output and returned error codes are similar to the UNIX\n\
  193. readlink command. However no options are accepted.\n\
  194. \n\
  195. 0 is returned on success.\n\
  196. 1 is returned for all errors.\n\
  197. \n");
  198. }