9e31a466582971c3fbf7ff6c917f6c732bbb5775
[reactos.git] / reactos / 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 RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer);
125
126 /* Allocate a dummy output buffer to probe for size */
127 MountPoints = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(MOUNTMGR_MOUNT_POINTS));
128 if (MountPoints == NULL)
129 {
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, MountPoints);
143 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoint);
144 return FALSE;
145 }
146
147 /* Query the names associated to our device name */
148 Ret = DeviceIoControl(MountMgrHandle, IOCTL_MOUNTMGR_QUERY_POINTS,
149 MountPoint, NtPathName.Length + sizeof(MOUNTMGR_MOUNT_POINT),
150 MountPoints, sizeof(MOUNTMGR_MOUNT_POINTS), &BytesReturned,
151 NULL);
152 /* As long as the buffer is too small, keep looping */
153 while (!Ret && GetLastError() == ERROR_MORE_DATA)
154 {
155 ULONG BufferSize;
156
157 /* Get the size we've to allocate */
158 BufferSize = MountPoints->Size;
159 /* Reallocate the buffer with enough room */
160 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoints);
161 MountPoints = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferSize);
162 if (MountPoints == NULL)
163 {
164 CloseHandle(MountMgrHandle);
165 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoint);
166 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
167 return FALSE;
168 }
169
170 /* Reissue the request, it should work now! */
171 Ret = DeviceIoControl(MountMgrHandle, IOCTL_MOUNTMGR_QUERY_POINTS,
172 MountPoint, NtPathName.Length + sizeof(MOUNTMGR_MOUNT_POINT),
173 MountPoints, BufferSize, &BytesReturned, NULL);
174 }
175
176 /* We're done, no longer need the mount manager */
177 CloseHandle(MountMgrHandle);
178 /* Nor our input buffer */
179 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoint);
180
181 /* If the mount manager failed, just quit */
182 if (!Ret)
183 {
184 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoints);
185 SetLastError(ERROR_INVALID_PARAMETER);
186 return FALSE;
187 }
188
189 CurrentMntPt = 0;
190 /* If there were no associated mount points, we'll return the device name */
191 if (MountPoints->NumberOfMountPoints == 0)
192 {
193 FoundVolume = NtPathName.Buffer;
194 FoundVolumeLen = NtPathName.Length;
195 }
196 /* Otherwise, find one which is matching */
197 else
198 {
199 for (; CurrentMntPt < MountPoints->NumberOfMountPoints; ++CurrentMntPt)
200 {
201 UNICODE_STRING SymbolicLink;
202
203 /* Make a string of it, to easy the checks */
204 SymbolicLink.Length = MountPoints->MountPoints[CurrentMntPt].SymbolicLinkNameLength;
205 SymbolicLink.MaximumLength = SymbolicLink.Length;
206 SymbolicLink.Buffer = (PVOID)((ULONG_PTR)&MountPoints->MountPoints[CurrentMntPt] + MountPoints->MountPoints[CurrentMntPt].SymbolicLinkNameOffset);
207 /* If that's a NT volume name (GUID form), keep it! */
208 if (MOUNTMGR_IS_NT_VOLUME_NAME(&SymbolicLink))
209 {
210 FoundVolume = SymbolicLink.Buffer;
211 FoundVolumeLen = SymbolicLink.Length;
212
213 break;
214 }
215 }
216 }
217
218 /* We couldn't find anything matching, return an error */
219 if (CurrentMntPt == MountPoints->NumberOfMountPoints)
220 {
221 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoints);
222 SetLastError(ERROR_INVALID_PARAMETER);
223 return FALSE;
224 }
225
226 /* We found a matching volume, have we enough memory to return it? */
227 if (cchBufferLength * sizeof(WCHAR) < FoundVolumeLen + 2 * sizeof(WCHAR))
228 {
229 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoints);
230 SetLastError(ERROR_FILENAME_EXCED_RANGE);
231 return FALSE;
232 }
233
234 /* Copy it back! */
235 RtlCopyMemory(lpszVolumeName, FoundVolume, FoundVolumeLen);
236 /* Make it compliant */
237 lpszVolumeName[1] = L'\\';
238 /* And transform it as root path */
239 lpszVolumeName[FoundVolumeLen / sizeof(WCHAR)] = L'\\';
240 lpszVolumeName[FoundVolumeLen / sizeof(WCHAR) + 1] = UNICODE_NULL;
241
242 /* We're done! */
243 RtlFreeHeap(RtlGetProcessHeap(), 0, MountPoints);
244 return TRUE;
245 }
246
247 /*
248 * @implemented
249 */
250 static BOOL
251 BasepGetVolumeNameFromReparsePoint(IN LPCWSTR lpszMountPoint,
252 OUT LPWSTR lpszVolumeName,
253 IN DWORD cchBufferLength,
254 OUT LPBOOL IsAMountPoint)
255 {
256 WCHAR Old;
257 DWORD BytesReturned;
258 HANDLE ReparseHandle;
259 UNICODE_STRING SubstituteName;
260 PREPARSE_DATA_BUFFER ReparseBuffer;
261
262 /* Try to open the reparse point */
263 ReparseHandle = CreateFileW(lpszMountPoint, 0,
264 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
265 OPEN_EXISTING,
266 FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS | FILE_ATTRIBUTE_NORMAL,
267 INVALID_HANDLE_VALUE);
268 /* It failed! */
269 if (ReparseHandle == INVALID_HANDLE_VALUE)
270 {
271 /* Report it's not a mount point (it's not a reparse point) */
272 if (IsAMountPoint != NULL)
273 {
274 *IsAMountPoint = FALSE;
275 }
276
277 /* And zero output */
278 if (lpszVolumeName != NULL && cchBufferLength >= 1)
279 {
280 lpszVolumeName[0] = UNICODE_NULL;
281 }
282
283 return FALSE;
284 }
285
286 /* This is a mount point! */
287 if (IsAMountPoint != NULL)
288 {
289 *IsAMountPoint = TRUE;
290 }
291
292 /* Prepare a buffer big enough to read its data */
293 ReparseBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
294 if (ReparseBuffer == NULL)
295 {
296 CloseHandle(ReparseHandle);
297 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
298
299 /* Zero output */
300 if (lpszVolumeName != NULL && cchBufferLength >= 1)
301 {
302 lpszVolumeName[0] = UNICODE_NULL;
303 }
304
305 return FALSE;
306 }
307
308 /* Dump the reparse point data */
309 if (!DeviceIoControl(ReparseHandle, FSCTL_GET_REPARSE_POINT, NULL, 0,
310 ReparseBuffer, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &BytesReturned,
311 NULL))
312 {
313 RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseBuffer);
314 CloseHandle(ReparseHandle);
315
316 /* Zero output */
317 if (lpszVolumeName != NULL && cchBufferLength >= 1)
318 {
319 lpszVolumeName[0] = UNICODE_NULL;
320 }
321
322 return FALSE;
323 }
324
325 /* We no longer need the reparse point */
326 CloseHandle(ReparseHandle);
327
328 /* We only handle mount points */
329 if (ReparseBuffer->ReparseTag != IO_REPARSE_TAG_MOUNT_POINT)
330 {
331 RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseBuffer);
332
333 /* Zero output */
334 if (lpszVolumeName != NULL && cchBufferLength >= 1)
335 {
336 lpszVolumeName[0] = UNICODE_NULL;
337 }
338
339 return FALSE;
340 }
341
342 /* Do we have enough room for copying substitue name? */
343 if ((ReparseBuffer->MountPointReparseBuffer.SubstituteNameLength + sizeof(UNICODE_NULL)) > cchBufferLength * sizeof(WCHAR))
344 {
345 RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseBuffer);
346 SetLastError(ERROR_FILENAME_EXCED_RANGE);
347
348 /* Zero output */
349 if (lpszVolumeName != NULL && cchBufferLength >= 1)
350 {
351 lpszVolumeName[0] = UNICODE_NULL;
352 }
353
354 return FALSE;
355 }
356
357 /* Copy the link target */
358 RtlCopyMemory(lpszVolumeName,
359 &ReparseBuffer->MountPointReparseBuffer.PathBuffer[ReparseBuffer->MountPointReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)],
360 ReparseBuffer->MountPointReparseBuffer.SubstituteNameLength);
361 /* Make it DOS valid */
362 Old = lpszVolumeName[1];
363 /* We want a root path */
364 lpszVolumeName[1] = L'\\';
365 /* And null terminate obviously */
366 lpszVolumeName[ReparseBuffer->MountPointReparseBuffer.SubstituteNameLength / sizeof(WCHAR)] = UNICODE_NULL;
367
368 /* Make it a string to easily check it */
369 SubstituteName.Length = ReparseBuffer->MountPointReparseBuffer.SubstituteNameLength;
370 SubstituteName.MaximumLength = SubstituteName.Length;
371 SubstituteName.Buffer = lpszVolumeName;
372
373 /* No longer need the data? */
374 RtlFreeHeap(RtlGetProcessHeap(), 0, ReparseBuffer);
375
376 /* Is that a dos volume name with backslash? */
377 if (MOUNTMGR_IS_DOS_VOLUME_NAME_WB(&SubstituteName))
378 {
379 return TRUE;
380 }
381
382 /* No, so restore previous name and return to the caller */
383 lpszVolumeName[1] = Old;
384 SetLastError(ERROR_INVALID_PARAMETER);
385 return FALSE;
386 }
387
388 /*
389 * @implemented
390 */
391 BOOL
392 BasepGetVolumeNameForVolumeMountPoint(IN LPCWSTR lpszMountPoint,
393 OUT LPWSTR lpszVolumeName,
394 IN DWORD cchBufferLength,
395 OUT LPBOOL IsAMountPoint)
396 {
397 BOOL Ret;
398 UNICODE_STRING MountPoint;
399
400 /* Assume it's a mount point (likely for non reparse points) */
401 if (IsAMountPoint != NULL)
402 {
403 *IsAMountPoint = 1;
404 }
405
406 /* Make a string with the mount point name */
407 RtlInitUnicodeString(&MountPoint, lpszMountPoint);
408 /* Not a root path? */
409 if (MountPoint.Buffer[(MountPoint.Length / sizeof(WCHAR)) - 1] != L'\\')
410 {
411 BaseSetLastNTError(STATUS_OBJECT_NAME_INVALID);
412 /* Zero output */
413 if (lpszVolumeName != NULL && cchBufferLength >= 1)
414 {
415 lpszVolumeName[0] = UNICODE_NULL;
416 }
417
418 return FALSE;
419 }
420
421 /* Does it look like <letter>:\? */
422 if (MountPoint.Length == 3 * sizeof(WCHAR))
423 {
424 /* Try to get volume name for root path */
425 Ret = GetVolumeNameForRoot(lpszMountPoint, lpszVolumeName, cchBufferLength);
426 /* It failed? */
427 if (!Ret)
428 {
429 /* If wasn't a drive letter, so maybe a reparse point? */
430 if (MountPoint.Buffer[1] != ':')
431 {
432 Ret = BasepGetVolumeNameFromReparsePoint(lpszMountPoint, lpszVolumeName, cchBufferLength, IsAMountPoint);
433 }
434 /* It was, so zero output */
435 else if (lpszVolumeName != NULL && cchBufferLength >= 1)
436 {
437 lpszVolumeName[0] = UNICODE_NULL;
438 }
439 }
440 }
441 else
442 {
443 /* Try to get volume name for root path */
444 Ret = GetVolumeNameForRoot(lpszMountPoint, lpszVolumeName, cchBufferLength);
445 /* It failed? */
446 if (!Ret)
447 {
448 /* It was a DOS volume as UNC name, so fail and zero output */
449 if (MountPoint.Length == 14 && MountPoint.Buffer[0] == '\\' && MountPoint.Buffer[1] == '\\' &&
450 (MountPoint.Buffer[2] == '.' || MountPoint.Buffer[2] == '?') && MountPoint.Buffer[3] == L'\\' &&
451 MountPoint.Buffer[5] == ':')
452 {
453 if (lpszVolumeName != NULL && cchBufferLength >= 1)
454 {
455 lpszVolumeName[0] = UNICODE_NULL;
456 }
457 }
458 /* Maybe it's a reparse point? */
459 else
460 {
461 Ret = BasepGetVolumeNameFromReparsePoint(lpszMountPoint, lpszVolumeName, cchBufferLength, IsAMountPoint);
462 }
463 }
464 }
465
466 return Ret;
467 }
468
469 /**
470 * @name GetVolumeNameForVolumeMountPointW
471 * @implemented
472 *
473 * Return an unique volume name for a drive root or mount point.
474 *
475 * @param VolumeMountPoint
476 * Pointer to string that contains either root drive name or
477 * mount point name.
478 * @param VolumeName
479 * Pointer to buffer that is filled with resulting unique
480 * volume name on success.
481 * @param VolumeNameLength
482 * Size of VolumeName buffer in TCHARs.
483 *
484 * @return
485 * TRUE when the function succeeds and the VolumeName buffer is filled,
486 * FALSE otherwise.
487 */
488 BOOL
489 WINAPI
490 GetVolumeNameForVolumeMountPointW(IN LPCWSTR VolumeMountPoint,
491 OUT LPWSTR VolumeName,
492 IN DWORD VolumeNameLength)
493 {
494 BOOL Ret;
495
496 /* Just query our internal function */
497 Ret = BasepGetVolumeNameForVolumeMountPoint(VolumeMountPoint, VolumeName,
498 VolumeNameLength, NULL);
499 if (!Ret && VolumeName != NULL && VolumeNameLength >= 1)
500 {
501 VolumeName[0] = UNICODE_NULL;
502 }
503
504 return Ret;
505 }
506
507 /*
508 * @implemented
509 */
510 BOOL
511 WINAPI
512 GetVolumeNameForVolumeMountPointA(IN LPCSTR lpszVolumeMountPoint,
513 IN LPSTR lpszVolumeName,
514 IN DWORD cchBufferLength)
515 {
516 BOOL Ret;
517 ANSI_STRING VolumeName;
518 UNICODE_STRING VolumeNameU;
519 PUNICODE_STRING VolumeMountPointU;
520
521 /* Convert mount point to unicode */
522 VolumeMountPointU = Basep8BitStringToStaticUnicodeString(lpszVolumeMountPoint);
523 if (VolumeMountPointU == NULL)
524 {
525 return FALSE;
526 }
527
528 /* Initialize the strings we'll use for convention */
529 VolumeName.Buffer = lpszVolumeName;
530 VolumeName.Length = 0;
531 VolumeName.MaximumLength = cchBufferLength - 1;
532
533 VolumeNameU.Length = 0;
534 VolumeNameU.MaximumLength = (cchBufferLength - 1) * sizeof(WCHAR) + sizeof(UNICODE_NULL);
535 /* Allocate a buffer big enough to contain the returned name */
536 VolumeNameU.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, VolumeNameU.MaximumLength);
537 if (VolumeNameU.Buffer == NULL)
538 {
539 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
540 return FALSE;
541 }
542
543 /* Query -W */
544 Ret = GetVolumeNameForVolumeMountPointW(VolumeMountPointU->Buffer, VolumeNameU.Buffer, cchBufferLength);
545 /* If it succeed, perform -A conversion */
546 if (Ret)
547 {
548 NTSTATUS Status;
549
550 /* Reinit our string for length */
551 RtlInitUnicodeString(&VolumeNameU, VolumeNameU.Buffer);
552 /* Convert to ANSI */
553 Status = RtlUnicodeStringToAnsiString(&VolumeName, &VolumeNameU, FALSE);
554 /* If conversion failed, force failure, otherwise, just null terminate */
555 if (!NT_SUCCESS(Status))
556 {
557 Ret = FALSE;
558 BaseSetLastNTError(Status);
559 }
560 else
561 {
562 VolumeName.Buffer[VolumeName.Length] = ANSI_NULL;
563 }
564 }
565
566 /* Internal buffer no longer needed */
567 RtlFreeHeap(RtlGetProcessHeap(), 0, VolumeNameU.Buffer);
568
569 return Ret;
570 }
571
572 /*
573 * @unimplemented
574 */
575 BOOL
576 WINAPI
577 SetVolumeMountPointW(IN LPCWSTR lpszVolumeMountPoint,
578 IN LPCWSTR lpszVolumeName)
579 {
580 STUB;
581 return 0;
582 }
583
584 /*
585 * @unimplemented
586 */
587 BOOL
588 WINAPI
589 SetVolumeMountPointA(IN LPCSTR lpszVolumeMountPoint,
590 IN LPCSTR lpszVolumeName)
591 {
592 STUB;
593 return 0;
594 }
595
596 /*
597 * @unimplemented
598 */
599 BOOL
600 WINAPI
601 DeleteVolumeMountPointA(IN LPCSTR lpszVolumeMountPoint)
602 {
603 STUB;
604 return 0;
605 }
606
607 /*
608 * @unimplemented
609 */
610 BOOL
611 WINAPI
612 DeleteVolumeMountPointW(IN LPCWSTR lpszVolumeMountPoint)
613 {
614 STUB;
615 return 0;
616 }
617
618 /*
619 * @unimplemented
620 */
621 HANDLE
622 WINAPI
623 FindFirstVolumeMountPointW(IN LPCWSTR lpszRootPathName,
624 IN LPWSTR lpszVolumeMountPoint,
625 IN DWORD cchBufferLength)
626 {
627 STUB;
628 return 0;
629 }
630
631 /*
632 * @unimplemented
633 */
634 HANDLE
635 WINAPI
636 FindFirstVolumeMountPointA(IN LPCSTR lpszRootPathName,
637 IN LPSTR lpszVolumeMountPoint,
638 IN DWORD cchBufferLength)
639 {
640 STUB;
641 return 0;
642 }
643
644 /*
645 * @unimplemented
646 */
647 BOOL
648 WINAPI
649 FindNextVolumeMountPointA(IN HANDLE hFindVolumeMountPoint,
650 IN LPSTR lpszVolumeMountPoint,
651 DWORD cchBufferLength)
652 {
653 STUB;
654 return 0;
655 }
656
657 /*
658 * @unimplemented
659 */
660 BOOL
661 WINAPI
662 FindNextVolumeMountPointW(IN HANDLE hFindVolumeMountPoint,
663 IN LPWSTR lpszVolumeMountPoint,
664 DWORD cchBufferLength)
665 {
666 STUB;
667 return 0;
668 }
669
670 /*
671 * @unimplemented
672 */
673 BOOL
674 WINAPI
675 FindVolumeMountPointClose(IN HANDLE hFindVolumeMountPoint)
676 {
677 STUB;
678 return 0;
679 }
680
681 /* EOF */