diff --git a/hadoop-common-project/hadoop-common/src/main/winutils/readlink.c b/hadoop-common-project/hadoop-common/src/main/winutils/readlink.c new file mode 100644 index 00000000000..360eba20658 --- /dev/null +++ b/hadoop-common-project/hadoop-common/src/main/winutils/readlink.c @@ -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"); +} +