blob: 360eba206581d4156e5cf2f7505d8552b883c33f [file] [log] [blame]
/**
* 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");
}