/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS system libraries * FILE: dll/win32/kernel32/client/file/mntpoint.c * PURPOSE: File volume mount point functions * PROGRAMMER: Pierre Schweitzer (pierre@reactos.org) */ #include #define NDEBUG #include /* * @implemented */ static BOOL GetVolumeNameForRoot(IN LPCWSTR lpszRootPath, OUT LPWSTR lpszVolumeName, IN DWORD cchBufferLength) { BOOL Ret; NTSTATUS Status; PWSTR FoundVolume; DWORD BytesReturned; UNICODE_STRING NtPathName; IO_STATUS_BLOCK IoStatusBlock; PMOUNTMGR_MOUNT_POINT MountPoint; ULONG CurrentMntPt, FoundVolumeLen; PMOUNTMGR_MOUNT_POINTS MountPoints; OBJECT_ATTRIBUTES ObjectAttributes; HANDLE VolumeHandle, MountMgrHandle; struct { MOUNTDEV_NAME; WCHAR Buffer[MAX_PATH]; } MountDevName; /* It makes no sense on a non-local drive */ if (GetDriveTypeW(lpszRootPath) == DRIVE_REMOTE) { SetLastError(ERROR_PATH_NOT_FOUND); return FALSE; } /* Get the NT path */ if (!RtlDosPathNameToNtPathName_U(lpszRootPath, &NtPathName, NULL, NULL)) { SetLastError(ERROR_PATH_NOT_FOUND); return FALSE; } /* If it's a root path - likely - drop backslash to open volume */ if (NtPathName.Buffer[(NtPathName.Length / sizeof(WCHAR)) - 1] == L'\\') { NtPathName.Buffer[(NtPathName.Length / sizeof(WCHAR)) - 1] = UNICODE_NULL; NtPathName.Length -= sizeof(WCHAR); } /* If that's a DOS volume, upper case the letter */ if (NtPathName.Length >= 2 * sizeof(WCHAR)) { if (NtPathName.Buffer[(NtPathName.Length / sizeof(WCHAR)) - 1] == L':') { NtPathName.Buffer[(NtPathName.Length / sizeof(WCHAR)) - 2] = _toupper(NtPathName.Buffer[(NtPathName.Length / sizeof(WCHAR)) - 2]); } } /* Attempt to open the volume */ InitializeObjectAttributes(&ObjectAttributes, &NtPathName, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = NtOpenFile(&VolumeHandle, SYNCHRONIZE | FILE_READ_ATTRIBUTES, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_ALERT); if (!NT_SUCCESS(Status)) { RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer); BaseSetLastNTError(Status); return FALSE; } /* Query the device name - that's what we'll translate */ if (!DeviceIoControl(VolumeHandle, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0, &MountDevName, sizeof(MountDevName), &BytesReturned, NULL)) { NtClose(VolumeHandle); RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer); return FALSE; } /* No longer need the volume */ NtClose(VolumeHandle); RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer); /* We'll keep the device name for later usage */ NtPathName.Length = MountDevName.NameLength; NtPathName.MaximumLength = MountDevName.NameLength + sizeof(UNICODE_NULL); NtPathName.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, NtPathName.MaximumLength); if (NtPathName.Buffer == NULL) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } RtlCopyMemory(NtPathName.Buffer, MountDevName.Name, NtPathName.Length); NtPathName.Buffer[NtPathName.Length / sizeof(WCHAR)] = UNICODE_NULL; /* Allocate the structure for querying the mount mgr */ MountPoint = RtlAllocateHeap(RtlGetProcessHeap(), 0, NtPathName.Length + sizeof(MOUNTMGR_MOUNT_POINT)); if (MountPoint == NULL) { RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer); SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } /* 0 everything, we provide a device name */ RtlZeroMemory(MountPoint, sizeof(MOUNTMGR_MOUNT_POINT)); MountPoint->DeviceNameOffset = sizeof(MOUNTMGR_MOUNT_POINT); MountPoint->DeviceNameLength = NtPathName.Length; RtlCopyMemory((PVOID)((ULONG_PTR)MountPoint + sizeof(MOUNTMGR_MOUNT_POINT)), NtPathName.Buffer, NtPathName.Length); /* Allocate a dummy output buffer to probe for size */ MountPoints = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(MOUNTMGR_MOUNT_POINTS)); if (MountPoints == NULL) { RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer); RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoint); SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } /* Open a handle to 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, NtPathName.Buffer); RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoints); RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoint); return FALSE; } /* Query the names associated to our device name */ Ret = DeviceIoControl(MountMgrHandle, IOCTL_MOUNTMGR_QUERY_POINTS, MountPoint, NtPathName.Length + sizeof(MOUNTMGR_MOUNT_POINT), MountPoints, sizeof(MOUNTMGR_MOUNT_POINTS), &BytesReturned, NULL); /* As long as the buffer is too small, keep looping */ while (!Ret && GetLastError() == ERROR_MORE_DATA) { ULONG BufferSize; /* Get the size we've to allocate */ BufferSize = MountPoints->Size; /* Reallocate the buffer with enough room */ RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoints); MountPoints = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferSize); if (MountPoints == NULL) { CloseHandle(MountMgrHandle); RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer); RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoint); SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } /* Reissue the request, it should work now! */ Ret = DeviceIoControl(MountMgrHandle, IOCTL_MOUNTMGR_QUERY_POINTS, MountPoint, NtPathName.Length + sizeof(MOUNTMGR_MOUNT_POINT), MountPoints, BufferSize, &BytesReturned, NULL); } /* We're done, no longer need the mount manager */ CloseHandle(MountMgrHandle); /* Nor our input buffer */ RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoint); /* If the mount manager failed, just quit */ if (!Ret) { RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer); RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoints); SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } CurrentMntPt = 0; /* If there were no associated mount points, we'll return the device name */ if (MountPoints->NumberOfMountPoints == 0) { FoundVolume = NtPathName.Buffer; FoundVolumeLen = NtPathName.Length; } /* Otherwise, find one which is matching */ else { for (; CurrentMntPt < MountPoints->NumberOfMountPoints; ++CurrentMntPt) { UNICODE_STRING SymbolicLink; /* Make a string of it, to easy the checks */ SymbolicLink.Length = MountPoints->MountPoints[CurrentMntPt].SymbolicLinkNameLength; SymbolicLink.MaximumLength = SymbolicLink.Length; SymbolicLink.Buffer = (PVOID)((ULONG_PTR)&MountPoints->MountPoints[CurrentMntPt] + MountPoints->MountPoints[CurrentMntPt].SymbolicLinkNameOffset); /* If that's a NT volume name (GUID form), keep it! */ if (MOUNTMGR_IS_NT_VOLUME_NAME(&SymbolicLink)) { FoundVolume = SymbolicLink.Buffer; FoundVolumeLen = SymbolicLink.Length; break; } } } /* We couldn't find anything matching, return an error */ if (CurrentMntPt == MountPoints->NumberOfMountPoints) { RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer); RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoints); SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } /* We found a matching volume, have we enough memory to return it? */ if (cchBufferLength * sizeof(WCHAR) < FoundVolumeLen + 2 * sizeof(WCHAR)) { RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer); RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoints); SetLastError(ERROR_FILENAME_EXCED_RANGE); return FALSE; } /* Copy it back! */ RtlCopyMemory(lpszVolumeName, FoundVolume, FoundVolumeLen); /* Make it compliant */ lpszVolumeName[1] = L'\\'; /* And transform it as root path */ lpszVolumeName[FoundVolumeLen / sizeof(WCHAR)] = L'\\'; lpszVolumeName[FoundVolumeLen / sizeof(WCHAR) + 1] = UNICODE_NULL; /* We're done! */ RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer); RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoints); return TRUE; } /* * @implemented */ BOOL BasepGetVolumeNameFromReparsePoint(IN LPCWSTR lpszMountPoint, OUT LPWSTR lpszVolumeName, IN DWORD cchBufferLength, OUT LPBOOL IsAMountPoint) { WCHAR Old; DWORD BytesReturned; HANDLE ReparseHandle; UNICODE_STRING SubstituteName; PREPARSE_DATA_BUFFER ReparseBuffer; /* Try to open the reparse point */ ReparseHandle = CreateFileW(lpszMountPoint, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS | FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE); /* It failed! */ if (ReparseHandle == INVALID_HANDLE_VALUE) { /* Report it's not a mount point (it's not a reparse point) */ if (IsAMountPoint != NULL) { *IsAMountPoint = FALSE; } /* And zero output */ if (lpszVolumeName != NULL && cchBufferLength >= 1) { lpszVolumeName[0] = UNICODE_NULL; } return FALSE; } /* This is a mount point! */ if (IsAMountPoint != NULL) { *IsAMountPoint = TRUE; } /* Prepare a buffer big enough to read its data */ ReparseBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, MAXIMUM_REPARSE_DATA_BUFFER_SIZE); if (ReparseBuffer == NULL) { CloseHandle(ReparseHandle); SetLastError(ERROR_NOT_ENOUGH_MEMORY); /* Zero output */ if (lpszVolumeName != NULL && cchBufferLength >= 1) { lpszVolumeName[0] = UNICODE_NULL; } return FALSE; } /* Dump the reparse point data */ if (!DeviceIoControl(ReparseHandle, FSCTL_GET_REPARSE_POINT, NULL, 0, ReparseBuffer, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &BytesReturned, NULL)) { RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseBuffer); CloseHandle(ReparseHandle); /* Zero output */ if (lpszVolumeName != NULL && cchBufferLength >= 1) { lpszVolumeName[0] = UNICODE_NULL; } return FALSE; } /* We no longer need the reparse point */ CloseHandle(ReparseHandle); /* We only handle mount points */ if (ReparseBuffer->ReparseTag != IO_REPARSE_TAG_MOUNT_POINT) { RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseBuffer); /* Zero output */ if (lpszVolumeName != NULL && cchBufferLength >= 1) { lpszVolumeName[0] = UNICODE_NULL; } return FALSE; } /* Do we have enough room for copying substitue name? */ if ((ReparseBuffer->MountPointReparseBuffer.SubstituteNameLength + sizeof(UNICODE_NULL)) > cchBufferLength * sizeof(WCHAR)) { RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseBuffer); SetLastError(ERROR_FILENAME_EXCED_RANGE); /* Zero output */ if (lpszVolumeName != NULL && cchBufferLength >= 1) { lpszVolumeName[0] = UNICODE_NULL; } return FALSE; } /* Copy the link target */ RtlCopyMemory(lpszVolumeName, &ReparseBuffer->MountPointReparseBuffer.PathBuffer[ReparseBuffer->MountPointReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)], ReparseBuffer->MountPointReparseBuffer.SubstituteNameLength); /* Make it DOS valid */ Old = lpszVolumeName[1]; /* We want a root path */ lpszVolumeName[1] = L'\\'; /* And null terminate obviously */ lpszVolumeName[ReparseBuffer->MountPointReparseBuffer.SubstituteNameLength / sizeof(WCHAR)] = UNICODE_NULL; /* Make it a string to easily check it */ SubstituteName.Length = ReparseBuffer->MountPointReparseBuffer.SubstituteNameLength; SubstituteName.MaximumLength = SubstituteName.Length; SubstituteName.Buffer = lpszVolumeName; /* No longer need the data? */ RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseBuffer); /* Is that a dos volume name with backslash? */ if (MOUNTMGR_IS_DOS_VOLUME_NAME_WB(&SubstituteName)) { return TRUE; } /* No, so restore previous name and return to the caller */ lpszVolumeName[1] = Old; SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } /* * @implemented */ BOOL BasepGetVolumeNameForVolumeMountPoint(IN LPCWSTR lpszMountPoint, OUT LPWSTR lpszVolumeName, IN DWORD cchBufferLength, OUT LPBOOL IsAMountPoint) { BOOL Ret; UNICODE_STRING MountPoint; /* Assume it's a mount point (likely for non reparse points) */ if (IsAMountPoint != NULL) { *IsAMountPoint = 1; } /* Make a string with the mount point name */ RtlInitUnicodeString(&MountPoint, lpszMountPoint); /* Not a root path? */ if (MountPoint.Buffer[(MountPoint.Length / sizeof(WCHAR)) - 1] != L'\\') { BaseSetLastNTError(STATUS_OBJECT_NAME_INVALID); /* Zero output */ if (lpszVolumeName != NULL && cchBufferLength >= 1) { lpszVolumeName[0] = UNICODE_NULL; } return FALSE; } /* Does it look like :\? */ if (MountPoint.Length == 3 * sizeof(WCHAR)) { /* Try to get volume name for root path */ Ret = GetVolumeNameForRoot(lpszMountPoint, lpszVolumeName, cchBufferLength); /* It failed? */ if (!Ret) { /* If wasn't a drive letter, so maybe a reparse point? */ if (MountPoint.Buffer[1] != ':') { Ret = BasepGetVolumeNameFromReparsePoint(lpszMountPoint, lpszVolumeName, cchBufferLength, IsAMountPoint); } /* It was, so zero output */ else if (lpszVolumeName != NULL && cchBufferLength >= 1) { lpszVolumeName[0] = UNICODE_NULL; } } } else { /* Try to get volume name for root path */ Ret = GetVolumeNameForRoot(lpszMountPoint, lpszVolumeName, cchBufferLength); /* It failed? */ if (!Ret) { /* It was a DOS volume as UNC name, so fail and zero output */ if (MountPoint.Length == 14 && MountPoint.Buffer[0] == '\\' && MountPoint.Buffer[1] == '\\' && (MountPoint.Buffer[2] == '.' || MountPoint.Buffer[2] == '?') && MountPoint.Buffer[3] == L'\\' && MountPoint.Buffer[5] == ':') { if (lpszVolumeName != NULL && cchBufferLength >= 1) { lpszVolumeName[0] = UNICODE_NULL; } } /* Maybe it's a reparse point? */ else { Ret = BasepGetVolumeNameFromReparsePoint(lpszMountPoint, lpszVolumeName, cchBufferLength, IsAMountPoint); } } } return Ret; } /** * @name GetVolumeNameForVolumeMountPointW * @implemented * * Return an unique volume name for a drive root or mount point. * * @param VolumeMountPoint * Pointer to string that contains either root drive name or * mount point name. * @param VolumeName * Pointer to buffer that is filled with resulting unique * volume name on success. * @param VolumeNameLength * Size of VolumeName buffer in TCHARs. * * @return * TRUE when the function succeeds and the VolumeName buffer is filled, * FALSE otherwise. */ BOOL WINAPI GetVolumeNameForVolumeMountPointW(IN LPCWSTR VolumeMountPoint, OUT LPWSTR VolumeName, IN DWORD VolumeNameLength) { BOOL Ret; /* Just query our internal function */ Ret = BasepGetVolumeNameForVolumeMountPoint(VolumeMountPoint, VolumeName, VolumeNameLength, NULL); if (!Ret && VolumeName != NULL && VolumeNameLength >= 1) { VolumeName[0] = UNICODE_NULL; } return Ret; } /* * @implemented */ BOOL WINAPI GetVolumeNameForVolumeMountPointA(IN LPCSTR lpszVolumeMountPoint, IN LPSTR lpszVolumeName, IN DWORD cchBufferLength) { BOOL Ret; ANSI_STRING VolumeName; UNICODE_STRING VolumeNameU; PUNICODE_STRING VolumeMountPointU; /* Convert mount point to unicode */ VolumeMountPointU = Basep8BitStringToStaticUnicodeString(lpszVolumeMountPoint); if (VolumeMountPointU == NULL) { return FALSE; } /* Initialize the strings we'll use for convention */ VolumeName.Buffer = lpszVolumeName; VolumeName.Length = 0; VolumeName.MaximumLength = cchBufferLength - 1; VolumeNameU.Length = 0; VolumeNameU.MaximumLength = (cchBufferLength - 1) * sizeof(WCHAR) + sizeof(UNICODE_NULL); /* Allocate a buffer big enough to contain the returned name */ VolumeNameU.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, VolumeNameU.MaximumLength); if (VolumeNameU.Buffer == NULL) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } /* Query -W */ Ret = GetVolumeNameForVolumeMountPointW(VolumeMountPointU->Buffer, VolumeNameU.Buffer, cchBufferLength); /* If it succeed, perform -A conversion */ if (Ret) { NTSTATUS Status; /* Reinit our string for length */ RtlInitUnicodeString(&VolumeNameU, VolumeNameU.Buffer); /* Convert to ANSI */ Status = RtlUnicodeStringToAnsiString(&VolumeName, &VolumeNameU, FALSE); /* If conversion failed, force failure, otherwise, just null terminate */ if (!NT_SUCCESS(Status)) { Ret = FALSE; BaseSetLastNTError(Status); } else { VolumeName.Buffer[VolumeName.Length] = ANSI_NULL; } } /* Internal buffer no longer needed */ RtlFreeHeap(RtlGetProcessHeap(), 0, VolumeNameU.Buffer); return Ret; } /* * @unimplemented */ BOOL WINAPI SetVolumeMountPointW(IN LPCWSTR lpszVolumeMountPoint, IN LPCWSTR lpszVolumeName) { STUB; return 0; } /* * @unimplemented */ BOOL WINAPI SetVolumeMountPointA(IN LPCSTR lpszVolumeMountPoint, IN LPCSTR lpszVolumeName) { STUB; return 0; } /* * @unimplemented */ BOOL WINAPI DeleteVolumeMountPointA(IN LPCSTR lpszVolumeMountPoint) { STUB; return 0; } /* * @unimplemented */ BOOL WINAPI DeleteVolumeMountPointW(IN LPCWSTR lpszVolumeMountPoint) { STUB; return 0; } /* * @unimplemented */ HANDLE WINAPI FindFirstVolumeMountPointW(IN LPCWSTR lpszRootPathName, IN LPWSTR lpszVolumeMountPoint, IN DWORD cchBufferLength) { STUB; return 0; } /* * @unimplemented */ HANDLE WINAPI FindFirstVolumeMountPointA(IN LPCSTR lpszRootPathName, IN LPSTR lpszVolumeMountPoint, IN DWORD cchBufferLength) { STUB; return 0; } /* * @unimplemented */ BOOL WINAPI FindNextVolumeMountPointA(IN HANDLE hFindVolumeMountPoint, IN LPSTR lpszVolumeMountPoint, DWORD cchBufferLength) { STUB; return 0; } /* * @unimplemented */ BOOL WINAPI FindNextVolumeMountPointW(IN HANDLE hFindVolumeMountPoint, IN LPWSTR lpszVolumeMountPoint, DWORD cchBufferLength) { STUB; return 0; } /* * @unimplemented */ BOOL WINAPI FindVolumeMountPointClose(IN HANDLE hFindVolumeMountPoint) { STUB; return 0; } /* EOF */