Author: pschweitzer Date: Sun Oct 1 08:23:06 2017 New Revision: 76012
URL: http://svn.reactos.org/svn/reactos?rev=76012&view=rev Log: [KERNEL32] Implement GetVolumePathNamesForVolumeNameW()
Modified: trunk/reactos/dll/win32/kernel32/client/file/volume.c
Modified: trunk/reactos/dll/win32/kernel32/client/file/volume.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/kernel32/client/f... ============================================================================== --- trunk/reactos/dll/win32/kernel32/client/file/volume.c [iso-8859-1] (original) +++ trunk/reactos/dll/win32/kernel32/client/file/volume.c [iso-8859-1] Sun Oct 1 08:23:06 2017 @@ -1056,7 +1056,7 @@
/* - * @unimplemented + * @implemented */ BOOL WINAPI @@ -1065,8 +1065,198 @@ IN DWORD cchBufferLength, OUT PDWORD lpcchReturnLength) { - STUB; - return 0; + BOOL Ret; + PWSTR MultiSz; + DWORD BytesReturned; + HANDLE MountMgrHandle; + UNICODE_STRING VolumeName; + PMOUNTMGR_TARGET_NAME TargetName; + PMOUNTMGR_VOLUME_PATHS VolumePaths; + ULONG BufferSize, CharsInMgr, CharsInOutput, Paths; + + /* First look that our volume name looks somehow correct */ + RtlInitUnicodeString(&VolumeName, lpszVolumeName); + if (VolumeName.Buffer[(VolumeName.Length / sizeof(WCHAR)) - 1] != L'\') + { + BaseSetLastNTError(STATUS_OBJECT_NAME_INVALID); + return FALSE; + } + + /* Validate it's a DOS volume name finishing with a backslash */ + if (!MOUNTMGR_IS_DOS_VOLUME_NAME_WB(&VolumeName)) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + /* Allocate an input MOUNTMGR_TARGET_NAME */ + TargetName = RtlAllocateHeap(RtlGetProcessHeap(), 0, MAX_PATH * sizeof(WCHAR) + sizeof(USHORT)); + if (TargetName == NULL) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return FALSE; + } + + /* And fill it */ + RtlZeroMemory(TargetName, MAX_PATH * sizeof(WCHAR) + sizeof(USHORT)); + TargetName->DeviceNameLength = VolumeName.Length - sizeof(WCHAR); + RtlCopyMemory(TargetName->DeviceName, VolumeName.Buffer, TargetName->DeviceNameLength); + TargetName->DeviceName[1] = L'?'; + + /* Open the mount manager */ + MountMgrHandle = CreateFileW(MOUNTMGR_DOS_DEVICE_NAME, 0, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, + INVALID_HANDLE_VALUE); + if (MountMgrHandle == INVALID_HANDLE_VALUE) + { + RtlFreeHeap(RtlGetProcessHeap(), 0, TargetName); + return FALSE; + } + + /* Allocate an initial output buffer, just to get length */ + VolumePaths = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(MOUNTMGR_VOLUME_PATHS)); + if (VolumePaths == NULL) + { + CloseHandle(MountMgrHandle); + RtlFreeHeap(RtlGetProcessHeap(), 0, TargetName); + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return FALSE; + } + + /* Query the paths */ + Ret = DeviceIoControl(MountMgrHandle, IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATHS, + TargetName, MAX_PATH * sizeof(WCHAR) + sizeof(USHORT), + VolumePaths, sizeof(MOUNTMGR_VOLUME_PATHS), &BytesReturned, + NULL); + /* Loop until we can query everything */ + while (!Ret) + { + /* If failed for another reason than too small buffer, fail */ + if (GetLastError() != ERROR_MORE_DATA) + { + CloseHandle(MountMgrHandle); + RtlFreeHeap(RtlGetProcessHeap(), 0, TargetName); + RtlFreeHeap(RtlGetProcessHeap(), 0, VolumePaths); + return FALSE; + } + + /* Get the required length */ + BufferSize = VolumePaths->MultiSzLength + sizeof(MOUNTMGR_VOLUME_PATHS); + + /* And reallocate our output buffer (big enough this time) */ + RtlFreeHeap(RtlGetProcessHeap(), 0, VolumePaths); + VolumePaths = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferSize); + if (VolumePaths == NULL) + { + CloseHandle(MountMgrHandle); + RtlFreeHeap(RtlGetProcessHeap(), 0, TargetName); + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return FALSE; + } + + /* Query again the mount mgr */ + Ret = DeviceIoControl(MountMgrHandle, IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATHS, + TargetName, MAX_PATH * sizeof(WCHAR) + sizeof(USHORT), + VolumePaths, BufferSize, &BytesReturned, NULL); + } + + /* We're done, no need for input nor mount mgr any longer */ + CloseHandle(MountMgrHandle); + RtlFreeHeap(RtlGetProcessHeap(), 0, TargetName); + + /* Initialize: + - Number of paths we saw (useful to count extra ) + - Progress in mount mgr output + - Progress in output buffer + - Direct buffer to returned MultiSz + */ + Paths = 0; + CharsInMgr = 0; + CharsInOutput = 0; + MultiSz = VolumePaths->MultiSz; + + /* If we have an output buffer */ + if (cchBufferLength != 0) + { + /* Loop on the output to recopy it back to the caller + * Note that we loop until -1 not to handle last 0 (will be done later on) + */ + for (; (CharsInMgr < VolumePaths->MultiSzLength / sizeof(WCHAR) - 1) && (CharsInOutput < cchBufferLength); + ++CharsInMgr, ++CharsInOutput) + { + /* When we reach the end of a path */ + if (MultiSz[CharsInMgr] == UNICODE_NULL) + { + /* On path done (count), add an extra \ at the end */ + ++Paths; + lpszVolumePathNames[CharsInOutput] = L'\'; + ++CharsInOutput; + /* Make sure we don't overflow */ + if (CharsInOutput == cchBufferLength) + { + break; + } + } + + /* Copy the char to the caller + * So, in case we're in the end of a path, we wrote two chars to + * the output buffer: \ and \0 + */ + lpszVolumePathNames[CharsInOutput] = MultiSz[CharsInMgr]; + } + } + + /* If output buffer was too small (ie, we couldn't parse all the input buffer) */ + if (CharsInMgr < VolumePaths->MultiSzLength / sizeof(WCHAR) - 1) + { + /* Keep looping on it, to count the number of extra \ that will be required + * So that on the next call, caller can allocate enough space + */ + for (; CharsInMgr < VolumePaths->MultiSzLength / sizeof(WCHAR) - 1; ++CharsInMgr) + { + if (MultiSz[CharsInMgr] == UNICODE_NULL) + { + ++Paths; + } + } + } + + /* If we couldn't write as much as we wanted to the output buffer + * This handles the case where we could write everything excepted the + * terminating \0 for multi SZ + */ + if (CharsInOutput >= cchBufferLength) + { + /* Fail and set appropriate error code */ + Ret = FALSE; + SetLastError(ERROR_MORE_DATA); + /* If caller wants to know how many chars to allocate, return it */ + if (lpcchReturnLength != NULL) + { + /* It's amount of extra \ + number of chars in MultiSz (including double \0) */ + *lpcchReturnLength = Paths + (VolumePaths->MultiSzLength / sizeof(WCHAR)); + } + } + else + { + /* It succeed so terminate the multi SZ (second \0) */ + lpszVolumePathNames[CharsInOutput] = UNICODE_NULL; + Ret = TRUE; + + /* If caller wants the amount of chars written, return it */ + if (lpcchReturnLength != NULL) + { + /* Including the terminating \0 we just added */ + *lpcchReturnLength = CharsInOutput + 1; + } + } + + /* Free last bits */ + RtlFreeHeap(RtlGetProcessHeap(), 0, VolumePaths); + + /* And return */ + return Ret; }
/* EOF */