/** * 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"); }