[SHELL32] SHChangeNotify: Use tree for CDirectoryList (#6784)
[reactos.git] / dll / win32 / kernel32 / client / file / mntpoint.c
1 /*
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)
7 */
8
9 #include <k32.h>
10 #define NDEBUG
11 #include <debug.h>
12
13 /*
14 * @implemented
15 */
16 static BOOL
17 GetVolumeNameForRoot(IN LPCWSTR lpszRootPath,
18 OUT LPWSTR lpszVolumeName,
19 IN DWORD cchBufferLength)
20 {
21 BOOL Ret;
22 NTSTATUS Status;
23 PWSTR FoundVolume;
24 DWORD BytesReturned;
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;
32 struct
33 {
34 MOUNTDEV_NAME;
35 WCHAR Buffer[MAX_PATH];
36 } MountDevName;
37
38 /* It makes no sense on a non-local drive */
39 if (GetDriveTypeW(lpszRootPath) == DRIVE_REMOTE)
40 {
41 SetLastError(ERROR_PATH_NOT_FOUND);
42 return FALSE;
43 }
44
45 /* Get the NT path */
46 if (!RtlDosPathNameToNtPathName_U(lpszRootPath, &NtPathName, NULL, NULL))
47 {
48 SetLastError(ERROR_PATH_NOT_FOUND);
49 return FALSE;
50 }
51
52 /* If it's a root path - likely - drop backslash to open volume */
53 if (NtPathName.Buffer[(NtPathName.Length / sizeof(WCHAR)) - 1] == L'\\')
54 {
55 NtPathName.Buffer[(NtPathName.Length / sizeof(WCHAR)) - 1] = UNICODE_NULL;
56 NtPathName.Length -= sizeof(WCHAR);
57 }
58
59 /* If that's a DOS volume, upper case the letter */
60 if (NtPathName.Length >= 2 * sizeof(WCHAR))
61 {
62 if (NtPathName.Buffer[(NtPathName.Length / sizeof(WCHAR)) - 1] == L':')
63 {
64 NtPathName.Buffer[(NtPathName.Length / sizeof(WCHAR)) - 2] = _toupper(NtPathName.Buffer[(NtPathName.Length / sizeof(WCHAR)) - 2]);
65 }
66 }
67
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))
76 {
77 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer);
78 BaseSetLastNTError(Status);
79 return FALSE;
80 }
81
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,
85 NULL))
86 {
87 NtClose(VolumeHandle);
88 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer);
89 return FALSE;
90 }
91
92 /* No longer need the volume */
93 NtClose(VolumeHandle);
94 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer);
95
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)
101 {
102 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
103 return FALSE;
104 }
105
106 RtlCopyMemory(NtPathName.Buffer, MountDevName.Name, NtPathName.Length);
107 NtPathName.Buffer[NtPathName.Length / sizeof(WCHAR)] = UNICODE_NULL;
108
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)
113 {
114 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer);
115 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
116 return FALSE;
117 }
118
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);
124
125 /* Allocate a dummy output buffer to probe for size */
126 MountPoints = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(MOUNTMGR_MOUNT_POINTS));
127 if (MountPoints == NULL)
128 {
129 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer);
130 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoint);
131 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
132 return FALSE;
133 }
134
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)
141 {
142 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer);
143 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoints);
144 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoint);
145 return FALSE;
146 }
147
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,
152 NULL);
153 /* As long as the buffer is too small, keep looping */
154 while (!Ret && GetLastError() == ERROR_MORE_DATA)
155 {
156 ULONG BufferSize;
157
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)
164 {
165 CloseHandle(MountMgrHandle);
166 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer);
167 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoint);
168 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
169 return FALSE;
170 }
171
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);
176 }
177
178 /* We're done, no longer need the mount manager */
179 CloseHandle(MountMgrHandle);
180 /* Nor our input buffer */
181 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoint);
182
183 /* If the mount manager failed, just quit */
184 if (!Ret)
185 {
186 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer);
187 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoints);
188 SetLastError(ERROR_INVALID_PARAMETER);
189 return FALSE;
190 }
191
192 CurrentMntPt = 0;
193 /* If there were no associated mount points, we'll return the device name */
194 if (MountPoints->NumberOfMountPoints == 0)
195 {
196 FoundVolume = NtPathName.Buffer;
197 FoundVolumeLen = NtPathName.Length;
198 }
199 /* Otherwise, find one which is matching */
200 else
201 {
202 for (; CurrentMntPt < MountPoints->NumberOfMountPoints; ++CurrentMntPt)
203 {
204 UNICODE_STRING SymbolicLink;
205
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))
212 {
213 FoundVolume = SymbolicLink.Buffer;
214 FoundVolumeLen = SymbolicLink.Length;
215
216 break;
217 }
218 }
219 }
220
221 /* We couldn't find anything matching, return an error */
222 if (CurrentMntPt == MountPoints->NumberOfMountPoints)
223 {
224 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer);
225 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoints);
226 SetLastError(ERROR_INVALID_PARAMETER);
227 return FALSE;
228 }
229
230 /* We found a matching volume, have we enough memory to return it? */
231 if (cchBufferLength * sizeof(WCHAR) < FoundVolumeLen + 2 * sizeof(WCHAR))
232 {
233 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer);
234 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoints);
235 SetLastError(ERROR_FILENAME_EXCED_RANGE);
236 return FALSE;
237 }
238
239 /* Copy it back! */
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;
246
247 /* We're done! */
248 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer);
249 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoints);
250 return TRUE;
251 }
252
253 /*
254 * @implemented
255 */
256 BOOL
257 BasepGetVolumeNameFromReparsePoint(IN LPCWSTR lpszMountPoint,
258 OUT LPWSTR lpszVolumeName,
259 IN DWORD cchBufferLength,
260 OUT LPBOOL IsAMountPoint)
261 {
262 WCHAR Old;
263 DWORD BytesReturned;
264 HANDLE ReparseHandle;
265 UNICODE_STRING SubstituteName;
266 PREPARSE_DATA_BUFFER ReparseBuffer;
267
268 /* Try to open the reparse point */
269 ReparseHandle = CreateFileW(lpszMountPoint, 0,
270 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
271 OPEN_EXISTING,
272 FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS | FILE_ATTRIBUTE_NORMAL,
273 INVALID_HANDLE_VALUE);
274 /* It failed! */
275 if (ReparseHandle == INVALID_HANDLE_VALUE)
276 {
277 /* Report it's not a mount point (it's not a reparse point) */
278 if (IsAMountPoint != NULL)
279 {
280 *IsAMountPoint = FALSE;
281 }
282
283 /* And zero output */
284 if (lpszVolumeName != NULL && cchBufferLength >= 1)
285 {
286 lpszVolumeName[0] = UNICODE_NULL;
287 }
288
289 return FALSE;
290 }
291
292 /* This is a mount point! */
293 if (IsAMountPoint != NULL)
294 {
295 *IsAMountPoint = TRUE;
296 }
297
298 /* Prepare a buffer big enough to read its data */
299 ReparseBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
300 if (ReparseBuffer == NULL)
301 {
302 CloseHandle(ReparseHandle);
303 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
304
305 /* Zero output */
306 if (lpszVolumeName != NULL && cchBufferLength >= 1)
307 {
308 lpszVolumeName[0] = UNICODE_NULL;
309 }
310
311 return FALSE;
312 }
313
314 /* Dump the reparse point data */
315 if (!DeviceIoControl(ReparseHandle, FSCTL_GET_REPARSE_POINT, NULL, 0,
316 ReparseBuffer, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &BytesReturned,
317 NULL))
318 {
319 RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseBuffer);
320 CloseHandle(ReparseHandle);
321
322 /* Zero output */
323 if (lpszVolumeName != NULL && cchBufferLength >= 1)
324 {
325 lpszVolumeName[0] = UNICODE_NULL;
326 }
327
328 return FALSE;
329 }
330
331 /* We no longer need the reparse point */
332 CloseHandle(ReparseHandle);
333
334 /* We only handle mount points */
335 if (ReparseBuffer->ReparseTag != IO_REPARSE_TAG_MOUNT_POINT)
336 {
337 RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseBuffer);
338
339 /* Zero output */
340 if (lpszVolumeName != NULL && cchBufferLength >= 1)
341 {
342 lpszVolumeName[0] = UNICODE_NULL;
343 }
344
345 return FALSE;
346 }
347
348 /* Do we have enough room for copying substitue name? */
349 if ((ReparseBuffer->MountPointReparseBuffer.SubstituteNameLength + sizeof(UNICODE_NULL)) > cchBufferLength * sizeof(WCHAR))
350 {
351 RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseBuffer);
352 SetLastError(ERROR_FILENAME_EXCED_RANGE);
353
354 /* Zero output */
355 if (lpszVolumeName != NULL && cchBufferLength >= 1)
356 {
357 lpszVolumeName[0] = UNICODE_NULL;
358 }
359
360 return FALSE;
361 }
362
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;
373
374 /* Make it a string to easily check it */
375 SubstituteName.Length = ReparseBuffer->MountPointReparseBuffer.SubstituteNameLength;
376 SubstituteName.MaximumLength = SubstituteName.Length;
377 SubstituteName.Buffer = lpszVolumeName;
378
379 /* No longer need the data? */
380 RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseBuffer);
381
382 /* Is that a dos volume name with backslash? */
383 if (MOUNTMGR_IS_DOS_VOLUME_NAME_WB(&SubstituteName))
384 {
385 return TRUE;
386 }
387
388 /* No, so restore previous name and return to the caller */
389 lpszVolumeName[1] = Old;
390 SetLastError(ERROR_INVALID_PARAMETER);
391 return FALSE;
392 }
393
394 /*
395 * @implemented
396 */
397 BOOL
398 BasepGetVolumeNameForVolumeMountPoint(IN LPCWSTR lpszMountPoint,
399 OUT LPWSTR lpszVolumeName,
400 IN DWORD cchBufferLength,
401 OUT LPBOOL IsAMountPoint)
402 {
403 BOOL Ret;
404 UNICODE_STRING MountPoint;
405
406 /* Assume it's a mount point (likely for non reparse points) */
407 if (IsAMountPoint != NULL)
408 {
409 *IsAMountPoint = 1;
410 }
411
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'\\')
416 {
417 BaseSetLastNTError(STATUS_OBJECT_NAME_INVALID);
418 /* Zero output */
419 if (lpszVolumeName != NULL && cchBufferLength >= 1)
420 {
421 lpszVolumeName[0] = UNICODE_NULL;
422 }
423
424 return FALSE;
425 }
426
427 /* Does it look like <letter>:\? */
428 if (MountPoint.Length == 3 * sizeof(WCHAR))
429 {
430 /* Try to get volume name for root path */
431 Ret = GetVolumeNameForRoot(lpszMountPoint, lpszVolumeName, cchBufferLength);
432 /* It failed? */
433 if (!Ret)
434 {
435 /* If wasn't a drive letter, so maybe a reparse point? */
436 if (MountPoint.Buffer[1] != ':')
437 {
438 Ret = BasepGetVolumeNameFromReparsePoint(lpszMountPoint, lpszVolumeName, cchBufferLength, IsAMountPoint);
439 }
440 /* It was, so zero output */
441 else if (lpszVolumeName != NULL && cchBufferLength >= 1)
442 {
443 lpszVolumeName[0] = UNICODE_NULL;
444 }
445 }
446 }
447 else
448 {
449 /* Try to get volume name for root path */
450 Ret = GetVolumeNameForRoot(lpszMountPoint, lpszVolumeName, cchBufferLength);
451 /* It failed? */
452 if (!Ret)
453 {
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] == ':')
458 {
459 if (lpszVolumeName != NULL && cchBufferLength >= 1)
460 {
461 lpszVolumeName[0] = UNICODE_NULL;
462 }
463 }
464 /* Maybe it's a reparse point? */
465 else
466 {
467 Ret = BasepGetVolumeNameFromReparsePoint(lpszMountPoint, lpszVolumeName, cchBufferLength, IsAMountPoint);
468 }
469 }
470 }
471
472 return Ret;
473 }
474
475 /**
476 * @name GetVolumeNameForVolumeMountPointW
477 * @implemented
478 *
479 * Return an unique volume name for a drive root or mount point.
480 *
481 * @param VolumeMountPoint
482 * Pointer to string that contains either root drive name or
483 * mount point name.
484 * @param VolumeName
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.
489 *
490 * @return
491 * TRUE when the function succeeds and the VolumeName buffer is filled,
492 * FALSE otherwise.
493 */
494 BOOL
495 WINAPI
496 GetVolumeNameForVolumeMountPointW(IN LPCWSTR VolumeMountPoint,
497 OUT LPWSTR VolumeName,
498 IN DWORD VolumeNameLength)
499 {
500 BOOL Ret;
501
502 /* Just query our internal function */
503 Ret = BasepGetVolumeNameForVolumeMountPoint(VolumeMountPoint, VolumeName,
504 VolumeNameLength, NULL);
505 if (!Ret && VolumeName != NULL && VolumeNameLength >= 1)
506 {
507 VolumeName[0] = UNICODE_NULL;
508 }
509
510 return Ret;
511 }
512
513 /*
514 * @implemented
515 */
516 BOOL
517 WINAPI
518 GetVolumeNameForVolumeMountPointA(IN LPCSTR lpszVolumeMountPoint,
519 IN LPSTR lpszVolumeName,
520 IN DWORD cchBufferLength)
521 {
522 BOOL Ret;
523 ANSI_STRING VolumeName;
524 UNICODE_STRING VolumeNameU;
525 PUNICODE_STRING VolumeMountPointU;
526
527 /* Convert mount point to unicode */
528 VolumeMountPointU = Basep8BitStringToStaticUnicodeString(lpszVolumeMountPoint);
529 if (VolumeMountPointU == NULL)
530 {
531 return FALSE;
532 }
533
534 /* Initialize the strings we'll use for convention */
535 VolumeName.Buffer = lpszVolumeName;
536 VolumeName.Length = 0;
537 VolumeName.MaximumLength = cchBufferLength - 1;
538
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)
544 {
545 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
546 return FALSE;
547 }
548
549 /* Query -W */
550 Ret = GetVolumeNameForVolumeMountPointW(VolumeMountPointU->Buffer, VolumeNameU.Buffer, cchBufferLength);
551 /* If it succeed, perform -A conversion */
552 if (Ret)
553 {
554 NTSTATUS Status;
555
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))
562 {
563 Ret = FALSE;
564 BaseSetLastNTError(Status);
565 }
566 else
567 {
568 VolumeName.Buffer[VolumeName.Length] = ANSI_NULL;
569 }
570 }
571
572 /* Internal buffer no longer needed */
573 RtlFreeHeap(RtlGetProcessHeap(), 0, VolumeNameU.Buffer);
574
575 return Ret;
576 }
577
578 /*
579 * @unimplemented
580 */
581 BOOL
582 WINAPI
583 SetVolumeMountPointW(IN LPCWSTR lpszVolumeMountPoint,
584 IN LPCWSTR lpszVolumeName)
585 {
586 STUB;
587 return 0;
588 }
589
590 /*
591 * @unimplemented
592 */
593 BOOL
594 WINAPI
595 SetVolumeMountPointA(IN LPCSTR lpszVolumeMountPoint,
596 IN LPCSTR lpszVolumeName)
597 {
598 STUB;
599 return 0;
600 }
601
602 /*
603 * @unimplemented
604 */
605 BOOL
606 WINAPI
607 DeleteVolumeMountPointA(IN LPCSTR lpszVolumeMountPoint)
608 {
609 STUB;
610 return 0;
611 }
612
613 /*
614 * @unimplemented
615 */
616 BOOL
617 WINAPI
618 DeleteVolumeMountPointW(IN LPCWSTR lpszVolumeMountPoint)
619 {
620 STUB;
621 return 0;
622 }
623
624 /*
625 * @unimplemented
626 */
627 HANDLE
628 WINAPI
629 FindFirstVolumeMountPointW(IN LPCWSTR lpszRootPathName,
630 IN LPWSTR lpszVolumeMountPoint,
631 IN DWORD cchBufferLength)
632 {
633 STUB;
634 return 0;
635 }
636
637 /*
638 * @unimplemented
639 */
640 HANDLE
641 WINAPI
642 FindFirstVolumeMountPointA(IN LPCSTR lpszRootPathName,
643 IN LPSTR lpszVolumeMountPoint,
644 IN DWORD cchBufferLength)
645 {
646 STUB;
647 return 0;
648 }
649
650 /*
651 * @unimplemented
652 */
653 BOOL
654 WINAPI
655 FindNextVolumeMountPointA(IN HANDLE hFindVolumeMountPoint,
656 IN LPSTR lpszVolumeMountPoint,
657 DWORD cchBufferLength)
658 {
659 STUB;
660 return 0;
661 }
662
663 /*
664 * @unimplemented
665 */
666 BOOL
667 WINAPI
668 FindNextVolumeMountPointW(IN HANDLE hFindVolumeMountPoint,
669 IN LPWSTR lpszVolumeMountPoint,
670 DWORD cchBufferLength)
671 {
672 STUB;
673 return 0;
674 }
675
676 /*
677 * @unimplemented
678 */
679 BOOL
680 WINAPI
681 FindVolumeMountPointClose(IN HANDLE hFindVolumeMountPoint)
682 {
683 STUB;
684 return 0;
685 }
686
687 /* EOF */