|
@@ -0,0 +1,224 @@
|
|
|
+/**
|
|
|
+ * Licensed to the Apache Software Foundation (ASF) under one or more
|
|
|
+ * contributor license agreements. See the NOTICE file distributed with this
|
|
|
+ * work for additional information regarding copyright ownership. The ASF
|
|
|
+ * licenses this file to you under the Apache License, Version 2.0 (the
|
|
|
+ * "License"); you may not use this file except in compliance with the License.
|
|
|
+ * You may obtain a copy of the License at
|
|
|
+ *
|
|
|
+ * http://www.apache.org/licenses/LICENSE-2.0
|
|
|
+ *
|
|
|
+ * Unless required by applicable law or agreed to in writing, software
|
|
|
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
|
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
|
+ * License for the specific language governing permissions and limitations under
|
|
|
+ * the License.
|
|
|
+ */
|
|
|
+
|
|
|
+#include "winutils.h"
|
|
|
+
|
|
|
+
|
|
|
+//----------------------------------------------------------------------------
|
|
|
+// The Windows SDK does not include the definition of REPARSE_DATA_BUFFER. To
|
|
|
+// avoid adding a dependency on the WDK we define the structure here.
|
|
|
+// Reference: http://msdn.microsoft.com/en-us/library/ff552012.aspx
|
|
|
+//
|
|
|
+#pragma warning(push)
|
|
|
+#pragma warning(disable: 4201) // nonstandard extension: nameless struct/union
|
|
|
+#pragma pack(push, 1)
|
|
|
+typedef struct _REPARSE_DATA_BUFFER {
|
|
|
+ ULONG ReparseTag;
|
|
|
+ USHORT ReparseDataLength;
|
|
|
+ USHORT Reserved;
|
|
|
+ union {
|
|
|
+ struct {
|
|
|
+ USHORT SubstituteNameOffset;
|
|
|
+ USHORT SubstituteNameLength;
|
|
|
+ USHORT PrintNameOffset;
|
|
|
+ USHORT PrintNameLength;
|
|
|
+ ULONG Flags;
|
|
|
+ WCHAR PathBuffer[1];
|
|
|
+ } SymbolicLinkReparseBuffer;
|
|
|
+ struct {
|
|
|
+ USHORT SubstituteNameOffset;
|
|
|
+ USHORT SubstituteNameLength;
|
|
|
+ USHORT PrintNameOffset;
|
|
|
+ USHORT PrintNameLength;
|
|
|
+ WCHAR PathBuffer[1];
|
|
|
+ } MountPointReparseBuffer;
|
|
|
+ struct {
|
|
|
+ UCHAR DataBuffer[1];
|
|
|
+ } GenericReparseBuffer;
|
|
|
+ };
|
|
|
+} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
|
|
|
+#pragma pack(pop)
|
|
|
+#pragma warning(pop)
|
|
|
+
|
|
|
+
|
|
|
+//----------------------------------------------------------------------------
|
|
|
+// Function: Readlink
|
|
|
+//
|
|
|
+// Description:
|
|
|
+// Prints the target of a symbolic link to stdout.
|
|
|
+//
|
|
|
+// The return codes and output are modeled after the UNIX readlink command.
|
|
|
+// Hence no error messages are printed. Unlike the UNIX readlink, no options
|
|
|
+// are accepted.
|
|
|
+//
|
|
|
+// Returns:
|
|
|
+// 0: on success
|
|
|
+// 1: on all errors
|
|
|
+//
|
|
|
+// Notes:
|
|
|
+//
|
|
|
+int Readlink(__in int argc, __in_ecount(argc) wchar_t *argv[])
|
|
|
+{
|
|
|
+ DWORD bytesReturned;
|
|
|
+ DWORD bufferSize = 1024; // Start off with a 1KB buffer.
|
|
|
+ HANDLE hFile = INVALID_HANDLE_VALUE;
|
|
|
+ PWSTR longLinkName = NULL;
|
|
|
+ PWCHAR printName = NULL;
|
|
|
+ PREPARSE_DATA_BUFFER pReparseData = NULL;
|
|
|
+ USHORT printNameLength;
|
|
|
+ USHORT printNameOffset;
|
|
|
+ DWORD result;
|
|
|
+ BOOLEAN succeeded = FALSE;
|
|
|
+
|
|
|
+ if (argc != 2)
|
|
|
+ {
|
|
|
+ ReadlinkUsage();
|
|
|
+ goto Cleanup;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (ConvertToLongPath(argv[1], &longLinkName) != ERROR_SUCCESS)
|
|
|
+ {
|
|
|
+ goto Cleanup;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Get a handle to the link to issue the FSCTL.
|
|
|
+ // FILE_FLAG_BACKUP_SEMANTICS is needed to open directories.
|
|
|
+ // FILE_FLAG_OPEN_REPARSE_POINT disables normal reparse point processing
|
|
|
+ // so we can query the symlink.
|
|
|
+ //
|
|
|
+ hFile = CreateFileW(longLinkName,
|
|
|
+ 0, // no rights needed to issue the FSCTL.
|
|
|
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
|
+ NULL,
|
|
|
+ OPEN_EXISTING,
|
|
|
+ FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
|
|
|
+ NULL);
|
|
|
+
|
|
|
+ if (hFile == INVALID_HANDLE_VALUE)
|
|
|
+ {
|
|
|
+ goto Cleanup;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (;;)
|
|
|
+ {
|
|
|
+ pReparseData = (PREPARSE_DATA_BUFFER) LocalAlloc(LMEM_FIXED, bufferSize);
|
|
|
+
|
|
|
+ if (pReparseData == NULL)
|
|
|
+ {
|
|
|
+ goto Cleanup;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Issue the FSCTL to query the link information.
|
|
|
+ //
|
|
|
+ result = DeviceIoControl(hFile,
|
|
|
+ FSCTL_GET_REPARSE_POINT,
|
|
|
+ NULL,
|
|
|
+ 0,
|
|
|
+ pReparseData,
|
|
|
+ bufferSize,
|
|
|
+ &bytesReturned,
|
|
|
+ NULL);
|
|
|
+
|
|
|
+ if (result != 0)
|
|
|
+ {
|
|
|
+ // Success!
|
|
|
+ //
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ else if ((GetLastError() == ERROR_INSUFFICIENT_BUFFER) ||
|
|
|
+ (GetLastError() == ERROR_MORE_DATA))
|
|
|
+ {
|
|
|
+ // Retry with a larger buffer.
|
|
|
+ //
|
|
|
+ LocalFree(pReparseData);
|
|
|
+ bufferSize *= 2;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // Unrecoverable error.
|
|
|
+ //
|
|
|
+ goto Cleanup;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (pReparseData->ReparseTag != IO_REPARSE_TAG_SYMLINK)
|
|
|
+ {
|
|
|
+ // Doesn't look like a symlink.
|
|
|
+ //
|
|
|
+ goto Cleanup;
|
|
|
+ }
|
|
|
+
|
|
|
+ // MSDN does not guarantee that the embedded paths in REPARSE_DATA_BUFFER
|
|
|
+ // will be NULL terminated. So we copy the string to a separate buffer and
|
|
|
+ // NULL terminate it before printing.
|
|
|
+ //
|
|
|
+ printNameLength = pReparseData->SymbolicLinkReparseBuffer.PrintNameLength;
|
|
|
+ printNameOffset = pReparseData->SymbolicLinkReparseBuffer.PrintNameOffset;
|
|
|
+ printName = (PWCHAR) LocalAlloc(LMEM_FIXED, printNameLength + 1);
|
|
|
+
|
|
|
+ if (printName == NULL)
|
|
|
+ {
|
|
|
+ goto Cleanup;
|
|
|
+ }
|
|
|
+
|
|
|
+ memcpy(
|
|
|
+ printName,
|
|
|
+ pReparseData->SymbolicLinkReparseBuffer.PathBuffer + printNameOffset,
|
|
|
+ printNameLength);
|
|
|
+
|
|
|
+ printName[printNameLength / sizeof(WCHAR)] = L'\0';
|
|
|
+
|
|
|
+ fwprintf(stdout, L"%ls", printName);
|
|
|
+ succeeded = TRUE;
|
|
|
+
|
|
|
+Cleanup:
|
|
|
+ if (hFile != INVALID_HANDLE_VALUE)
|
|
|
+ {
|
|
|
+ CloseHandle(hFile);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (printName != NULL)
|
|
|
+ {
|
|
|
+ LocalFree(printName);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (pReparseData != NULL)
|
|
|
+ {
|
|
|
+ LocalFree(pReparseData);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (longLinkName != NULL)
|
|
|
+ {
|
|
|
+ LocalFree(longLinkName);
|
|
|
+ }
|
|
|
+
|
|
|
+ return (succeeded ? EXIT_SUCCESS : EXIT_FAILURE);
|
|
|
+}
|
|
|
+
|
|
|
+void ReadlinkUsage()
|
|
|
+{
|
|
|
+ fwprintf(stdout, L"\
|
|
|
+Usage: readlink [LINKNAME]\n\
|
|
|
+Prints the target of a symbolic link\n\
|
|
|
+The output and returned error codes are similar to the UNIX\n\
|
|
|
+readlink command. However no options are accepted.\n\
|
|
|
+\n\
|
|
|
+0 is returned on success.\n\
|
|
|
+1 is returned for all errors.\n\
|
|
|
+\n");
|
|
|
+}
|
|
|
+
|