2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS system libraries
4 * FILE: dll/win32/kernel32/client/file/mntpoint.c
5 * PURPOSE: File volume mount point functions
6 * PROGRAMMER: Pierre Schweitzer (pierre@reactos.org)
17 GetVolumeNameForRoot(IN LPCWSTR lpszRootPath
,
18 OUT LPWSTR lpszVolumeName
,
19 IN DWORD cchBufferLength
)
25 UNICODE_STRING NtPathName
;
26 IO_STATUS_BLOCK IoStatusBlock
;
27 PMOUNTMGR_MOUNT_POINT MountPoint
;
28 ULONG CurrentMntPt
, FoundVolumeLen
;
29 PMOUNTMGR_MOUNT_POINTS MountPoints
;
30 OBJECT_ATTRIBUTES ObjectAttributes
;
31 HANDLE VolumeHandle
, MountMgrHandle
;
35 WCHAR Buffer
[MAX_PATH
];
38 /* It makes no sense on a non-local drive */
39 if (GetDriveTypeW(lpszRootPath
) == DRIVE_REMOTE
)
41 SetLastError(ERROR_PATH_NOT_FOUND
);
46 if (!RtlDosPathNameToNtPathName_U(lpszRootPath
, &NtPathName
, NULL
, NULL
))
48 SetLastError(ERROR_PATH_NOT_FOUND
);
52 /* If it's a root path - likely - drop backslash to open volume */
53 if (NtPathName
.Buffer
[(NtPathName
.Length
/ sizeof(WCHAR
)) - 1] == L
'\\')
55 NtPathName
.Buffer
[(NtPathName
.Length
/ sizeof(WCHAR
)) - 1] = UNICODE_NULL
;
56 NtPathName
.Length
-= sizeof(WCHAR
);
59 /* If that's a DOS volume, upper case the letter */
60 if (NtPathName
.Length
>= 2 * sizeof(WCHAR
))
62 if (NtPathName
.Buffer
[(NtPathName
.Length
/ sizeof(WCHAR
)) - 1] == L
':')
64 NtPathName
.Buffer
[(NtPathName
.Length
/ sizeof(WCHAR
)) - 2] = _toupper(NtPathName
.Buffer
[(NtPathName
.Length
/ sizeof(WCHAR
)) - 2]);
68 /* Attempt to open the volume */
69 InitializeObjectAttributes(&ObjectAttributes
, &NtPathName
,
70 OBJ_CASE_INSENSITIVE
, NULL
, NULL
);
71 Status
= NtOpenFile(&VolumeHandle
, SYNCHRONIZE
| FILE_READ_ATTRIBUTES
,
72 &ObjectAttributes
, &IoStatusBlock
,
73 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
74 FILE_SYNCHRONOUS_IO_ALERT
);
75 if (!NT_SUCCESS(Status
))
77 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName
.Buffer
);
78 BaseSetLastNTError(Status
);
82 /* Query the device name - that's what we'll translate */
83 if (!DeviceIoControl(VolumeHandle
, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME
, NULL
,
84 0, &MountDevName
, sizeof(MountDevName
), &BytesReturned
,
87 NtClose(VolumeHandle
);
88 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName
.Buffer
);
92 /* No longer need the volume */
93 NtClose(VolumeHandle
);
94 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName
.Buffer
);
96 /* We'll keep the device name for later usage */
97 NtPathName
.Length
= MountDevName
.NameLength
;
98 NtPathName
.MaximumLength
= MountDevName
.NameLength
+ sizeof(UNICODE_NULL
);
99 NtPathName
.Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, NtPathName
.MaximumLength
);
100 if (NtPathName
.Buffer
== NULL
)
102 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
106 RtlCopyMemory(NtPathName
.Buffer
, MountDevName
.Name
, NtPathName
.Length
);
107 NtPathName
.Buffer
[NtPathName
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
109 /* Allocate the structure for querying the mount mgr */
110 MountPoint
= RtlAllocateHeap(RtlGetProcessHeap(), 0,
111 NtPathName
.Length
+ sizeof(MOUNTMGR_MOUNT_POINT
));
112 if (MountPoint
== NULL
)
114 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName
.Buffer
);
115 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
119 /* 0 everything, we provide a device name */
120 RtlZeroMemory(MountPoint
, sizeof(MOUNTMGR_MOUNT_POINT
));
121 MountPoint
->DeviceNameOffset
= sizeof(MOUNTMGR_MOUNT_POINT
);
122 MountPoint
->DeviceNameLength
= NtPathName
.Length
;
123 RtlCopyMemory((PVOID
)((ULONG_PTR
)MountPoint
+ sizeof(MOUNTMGR_MOUNT_POINT
)), NtPathName
.Buffer
, NtPathName
.Length
);
125 /* Allocate a dummy output buffer to probe for size */
126 MountPoints
= RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(MOUNTMGR_MOUNT_POINTS
));
127 if (MountPoints
== NULL
)
129 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName
.Buffer
);
130 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoint
);
131 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
135 /* Open a handle to the mount manager */
136 MountMgrHandle
= CreateFileW(MOUNTMGR_DOS_DEVICE_NAME
, 0,
137 FILE_SHARE_READ
| FILE_SHARE_WRITE
, NULL
,
138 OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
,
139 INVALID_HANDLE_VALUE
);
140 if (MountMgrHandle
== INVALID_HANDLE_VALUE
)
142 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName
.Buffer
);
143 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoints
);
144 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoint
);
148 /* Query the names associated to our device name */
149 Ret
= DeviceIoControl(MountMgrHandle
, IOCTL_MOUNTMGR_QUERY_POINTS
,
150 MountPoint
, NtPathName
.Length
+ sizeof(MOUNTMGR_MOUNT_POINT
),
151 MountPoints
, sizeof(MOUNTMGR_MOUNT_POINTS
), &BytesReturned
,
153 /* As long as the buffer is too small, keep looping */
154 while (!Ret
&& GetLastError() == ERROR_MORE_DATA
)
158 /* Get the size we've to allocate */
159 BufferSize
= MountPoints
->Size
;
160 /* Reallocate the buffer with enough room */
161 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoints
);
162 MountPoints
= RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferSize
);
163 if (MountPoints
== NULL
)
165 CloseHandle(MountMgrHandle
);
166 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName
.Buffer
);
167 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoint
);
168 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
172 /* Reissue the request, it should work now! */
173 Ret
= DeviceIoControl(MountMgrHandle
, IOCTL_MOUNTMGR_QUERY_POINTS
,
174 MountPoint
, NtPathName
.Length
+ sizeof(MOUNTMGR_MOUNT_POINT
),
175 MountPoints
, BufferSize
, &BytesReturned
, NULL
);
178 /* We're done, no longer need the mount manager */
179 CloseHandle(MountMgrHandle
);
180 /* Nor our input buffer */
181 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoint
);
183 /* If the mount manager failed, just quit */
186 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName
.Buffer
);
187 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoints
);
188 SetLastError(ERROR_INVALID_PARAMETER
);
193 /* If there were no associated mount points, we'll return the device name */
194 if (MountPoints
->NumberOfMountPoints
== 0)
196 FoundVolume
= NtPathName
.Buffer
;
197 FoundVolumeLen
= NtPathName
.Length
;
199 /* Otherwise, find one which is matching */
202 for (; CurrentMntPt
< MountPoints
->NumberOfMountPoints
; ++CurrentMntPt
)
204 UNICODE_STRING SymbolicLink
;
206 /* Make a string of it, to easy the checks */
207 SymbolicLink
.Length
= MountPoints
->MountPoints
[CurrentMntPt
].SymbolicLinkNameLength
;
208 SymbolicLink
.MaximumLength
= SymbolicLink
.Length
;
209 SymbolicLink
.Buffer
= (PVOID
)((ULONG_PTR
)&MountPoints
->MountPoints
[CurrentMntPt
] + MountPoints
->MountPoints
[CurrentMntPt
].SymbolicLinkNameOffset
);
210 /* If that's a NT volume name (GUID form), keep it! */
211 if (MOUNTMGR_IS_NT_VOLUME_NAME(&SymbolicLink
))
213 FoundVolume
= SymbolicLink
.Buffer
;
214 FoundVolumeLen
= SymbolicLink
.Length
;
221 /* We couldn't find anything matching, return an error */
222 if (CurrentMntPt
== MountPoints
->NumberOfMountPoints
)
224 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName
.Buffer
);
225 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoints
);
226 SetLastError(ERROR_INVALID_PARAMETER
);
230 /* We found a matching volume, have we enough memory to return it? */
231 if (cchBufferLength
* sizeof(WCHAR
) < FoundVolumeLen
+ 2 * sizeof(WCHAR
))
233 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName
.Buffer
);
234 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoints
);
235 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
240 RtlCopyMemory(lpszVolumeName
, FoundVolume
, FoundVolumeLen
);
241 /* Make it compliant */
242 lpszVolumeName
[1] = L
'\\';
243 /* And transform it as root path */
244 lpszVolumeName
[FoundVolumeLen
/ sizeof(WCHAR
)] = L
'\\';
245 lpszVolumeName
[FoundVolumeLen
/ sizeof(WCHAR
) + 1] = UNICODE_NULL
;
248 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName
.Buffer
);
249 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoints
);
257 BasepGetVolumeNameFromReparsePoint(IN LPCWSTR lpszMountPoint
,
258 OUT LPWSTR lpszVolumeName
,
259 IN DWORD cchBufferLength
,
260 OUT LPBOOL IsAMountPoint
)
264 HANDLE ReparseHandle
;
265 UNICODE_STRING SubstituteName
;
266 PREPARSE_DATA_BUFFER ReparseBuffer
;
268 /* Try to open the reparse point */
269 ReparseHandle
= CreateFileW(lpszMountPoint
, 0,
270 FILE_SHARE_READ
| FILE_SHARE_WRITE
, NULL
,
272 FILE_FLAG_OPEN_REPARSE_POINT
| FILE_FLAG_BACKUP_SEMANTICS
| FILE_ATTRIBUTE_NORMAL
,
273 INVALID_HANDLE_VALUE
);
275 if (ReparseHandle
== INVALID_HANDLE_VALUE
)
277 /* Report it's not a mount point (it's not a reparse point) */
278 if (IsAMountPoint
!= NULL
)
280 *IsAMountPoint
= FALSE
;
283 /* And zero output */
284 if (lpszVolumeName
!= NULL
&& cchBufferLength
>= 1)
286 lpszVolumeName
[0] = UNICODE_NULL
;
292 /* This is a mount point! */
293 if (IsAMountPoint
!= NULL
)
295 *IsAMountPoint
= TRUE
;
298 /* Prepare a buffer big enough to read its data */
299 ReparseBuffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, MAXIMUM_REPARSE_DATA_BUFFER_SIZE
);
300 if (ReparseBuffer
== NULL
)
302 CloseHandle(ReparseHandle
);
303 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
306 if (lpszVolumeName
!= NULL
&& cchBufferLength
>= 1)
308 lpszVolumeName
[0] = UNICODE_NULL
;
314 /* Dump the reparse point data */
315 if (!DeviceIoControl(ReparseHandle
, FSCTL_GET_REPARSE_POINT
, NULL
, 0,
316 ReparseBuffer
, MAXIMUM_REPARSE_DATA_BUFFER_SIZE
, &BytesReturned
,
319 RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseBuffer
);
320 CloseHandle(ReparseHandle
);
323 if (lpszVolumeName
!= NULL
&& cchBufferLength
>= 1)
325 lpszVolumeName
[0] = UNICODE_NULL
;
331 /* We no longer need the reparse point */
332 CloseHandle(ReparseHandle
);
334 /* We only handle mount points */
335 if (ReparseBuffer
->ReparseTag
!= IO_REPARSE_TAG_MOUNT_POINT
)
337 RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseBuffer
);
340 if (lpszVolumeName
!= NULL
&& cchBufferLength
>= 1)
342 lpszVolumeName
[0] = UNICODE_NULL
;
348 /* Do we have enough room for copying substitue name? */
349 if ((ReparseBuffer
->MountPointReparseBuffer
.SubstituteNameLength
+ sizeof(UNICODE_NULL
)) > cchBufferLength
* sizeof(WCHAR
))
351 RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseBuffer
);
352 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
355 if (lpszVolumeName
!= NULL
&& cchBufferLength
>= 1)
357 lpszVolumeName
[0] = UNICODE_NULL
;
363 /* Copy the link target */
364 RtlCopyMemory(lpszVolumeName
,
365 &ReparseBuffer
->MountPointReparseBuffer
.PathBuffer
[ReparseBuffer
->MountPointReparseBuffer
.SubstituteNameOffset
/ sizeof(WCHAR
)],
366 ReparseBuffer
->MountPointReparseBuffer
.SubstituteNameLength
);
367 /* Make it DOS valid */
368 Old
= lpszVolumeName
[1];
369 /* We want a root path */
370 lpszVolumeName
[1] = L
'\\';
371 /* And null terminate obviously */
372 lpszVolumeName
[ReparseBuffer
->MountPointReparseBuffer
.SubstituteNameLength
/ sizeof(WCHAR
)] = UNICODE_NULL
;
374 /* Make it a string to easily check it */
375 SubstituteName
.Length
= ReparseBuffer
->MountPointReparseBuffer
.SubstituteNameLength
;
376 SubstituteName
.MaximumLength
= SubstituteName
.Length
;
377 SubstituteName
.Buffer
= lpszVolumeName
;
379 /* No longer need the data? */
380 RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseBuffer
);
382 /* Is that a dos volume name with backslash? */
383 if (MOUNTMGR_IS_DOS_VOLUME_NAME_WB(&SubstituteName
))
388 /* No, so restore previous name and return to the caller */
389 lpszVolumeName
[1] = Old
;
390 SetLastError(ERROR_INVALID_PARAMETER
);
398 BasepGetVolumeNameForVolumeMountPoint(IN LPCWSTR lpszMountPoint
,
399 OUT LPWSTR lpszVolumeName
,
400 IN DWORD cchBufferLength
,
401 OUT LPBOOL IsAMountPoint
)
404 UNICODE_STRING MountPoint
;
406 /* Assume it's a mount point (likely for non reparse points) */
407 if (IsAMountPoint
!= NULL
)
412 /* Make a string with the mount point name */
413 RtlInitUnicodeString(&MountPoint
, lpszMountPoint
);
414 /* Not a root path? */
415 if (MountPoint
.Buffer
[(MountPoint
.Length
/ sizeof(WCHAR
)) - 1] != L
'\\')
417 BaseSetLastNTError(STATUS_OBJECT_NAME_INVALID
);
419 if (lpszVolumeName
!= NULL
&& cchBufferLength
>= 1)
421 lpszVolumeName
[0] = UNICODE_NULL
;
427 /* Does it look like <letter>:\? */
428 if (MountPoint
.Length
== 3 * sizeof(WCHAR
))
430 /* Try to get volume name for root path */
431 Ret
= GetVolumeNameForRoot(lpszMountPoint
, lpszVolumeName
, cchBufferLength
);
435 /* If wasn't a drive letter, so maybe a reparse point? */
436 if (MountPoint
.Buffer
[1] != ':')
438 Ret
= BasepGetVolumeNameFromReparsePoint(lpszMountPoint
, lpszVolumeName
, cchBufferLength
, IsAMountPoint
);
440 /* It was, so zero output */
441 else if (lpszVolumeName
!= NULL
&& cchBufferLength
>= 1)
443 lpszVolumeName
[0] = UNICODE_NULL
;
449 /* Try to get volume name for root path */
450 Ret
= GetVolumeNameForRoot(lpszMountPoint
, lpszVolumeName
, cchBufferLength
);
454 /* It was a DOS volume as UNC name, so fail and zero output */
455 if (MountPoint
.Length
== 14 && MountPoint
.Buffer
[0] == '\\' && MountPoint
.Buffer
[1] == '\\' &&
456 (MountPoint
.Buffer
[2] == '.' || MountPoint
.Buffer
[2] == '?') && MountPoint
.Buffer
[3] == L
'\\' &&
457 MountPoint
.Buffer
[5] == ':')
459 if (lpszVolumeName
!= NULL
&& cchBufferLength
>= 1)
461 lpszVolumeName
[0] = UNICODE_NULL
;
464 /* Maybe it's a reparse point? */
467 Ret
= BasepGetVolumeNameFromReparsePoint(lpszMountPoint
, lpszVolumeName
, cchBufferLength
, IsAMountPoint
);
476 * @name GetVolumeNameForVolumeMountPointW
479 * Return an unique volume name for a drive root or mount point.
481 * @param VolumeMountPoint
482 * Pointer to string that contains either root drive name or
485 * Pointer to buffer that is filled with resulting unique
486 * volume name on success.
487 * @param VolumeNameLength
488 * Size of VolumeName buffer in TCHARs.
491 * TRUE when the function succeeds and the VolumeName buffer is filled,
496 GetVolumeNameForVolumeMountPointW(IN LPCWSTR VolumeMountPoint
,
497 OUT LPWSTR VolumeName
,
498 IN DWORD VolumeNameLength
)
502 /* Just query our internal function */
503 Ret
= BasepGetVolumeNameForVolumeMountPoint(VolumeMountPoint
, VolumeName
,
504 VolumeNameLength
, NULL
);
505 if (!Ret
&& VolumeName
!= NULL
&& VolumeNameLength
>= 1)
507 VolumeName
[0] = UNICODE_NULL
;
518 GetVolumeNameForVolumeMountPointA(IN LPCSTR lpszVolumeMountPoint
,
519 IN LPSTR lpszVolumeName
,
520 IN DWORD cchBufferLength
)
523 ANSI_STRING VolumeName
;
524 UNICODE_STRING VolumeNameU
;
525 PUNICODE_STRING VolumeMountPointU
;
527 /* Convert mount point to unicode */
528 VolumeMountPointU
= Basep8BitStringToStaticUnicodeString(lpszVolumeMountPoint
);
529 if (VolumeMountPointU
== NULL
)
534 /* Initialize the strings we'll use for convention */
535 VolumeName
.Buffer
= lpszVolumeName
;
536 VolumeName
.Length
= 0;
537 VolumeName
.MaximumLength
= cchBufferLength
- 1;
539 VolumeNameU
.Length
= 0;
540 VolumeNameU
.MaximumLength
= (cchBufferLength
- 1) * sizeof(WCHAR
) + sizeof(UNICODE_NULL
);
541 /* Allocate a buffer big enough to contain the returned name */
542 VolumeNameU
.Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, VolumeNameU
.MaximumLength
);
543 if (VolumeNameU
.Buffer
== NULL
)
545 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
550 Ret
= GetVolumeNameForVolumeMountPointW(VolumeMountPointU
->Buffer
, VolumeNameU
.Buffer
, cchBufferLength
);
551 /* If it succeed, perform -A conversion */
556 /* Reinit our string for length */
557 RtlInitUnicodeString(&VolumeNameU
, VolumeNameU
.Buffer
);
558 /* Convert to ANSI */
559 Status
= RtlUnicodeStringToAnsiString(&VolumeName
, &VolumeNameU
, FALSE
);
560 /* If conversion failed, force failure, otherwise, just null terminate */
561 if (!NT_SUCCESS(Status
))
564 BaseSetLastNTError(Status
);
568 VolumeName
.Buffer
[VolumeName
.Length
] = ANSI_NULL
;
572 /* Internal buffer no longer needed */
573 RtlFreeHeap(RtlGetProcessHeap(), 0, VolumeNameU
.Buffer
);
583 SetVolumeMountPointW(IN LPCWSTR lpszVolumeMountPoint
,
584 IN LPCWSTR lpszVolumeName
)
595 SetVolumeMountPointA(IN LPCSTR lpszVolumeMountPoint
,
596 IN LPCSTR lpszVolumeName
)
607 DeleteVolumeMountPointA(IN LPCSTR lpszVolumeMountPoint
)
618 DeleteVolumeMountPointW(IN LPCWSTR lpszVolumeMountPoint
)
629 FindFirstVolumeMountPointW(IN LPCWSTR lpszRootPathName
,
630 IN LPWSTR lpszVolumeMountPoint
,
631 IN DWORD cchBufferLength
)
642 FindFirstVolumeMountPointA(IN LPCSTR lpszRootPathName
,
643 IN LPSTR lpszVolumeMountPoint
,
644 IN DWORD cchBufferLength
)
655 FindNextVolumeMountPointA(IN HANDLE hFindVolumeMountPoint
,
656 IN LPSTR lpszVolumeMountPoint
,
657 DWORD cchBufferLength
)
668 FindNextVolumeMountPointW(IN HANDLE hFindVolumeMountPoint
,
669 IN LPWSTR lpszVolumeMountPoint
,
670 DWORD cchBufferLength
)
681 FindVolumeMountPointClose(IN HANDLE hFindVolumeMountPoint
)