makefiles: Don't use standard libs for programs that specify -nodefaultlibs.
[wine/zf.git] / dlls / kernel32 / volume.c
blob4f0f4a0a0fc198fcabd3e7f9204b93379f582f3d
1 /*
2 * Volume management functions
4 * Copyright 1993 Erik Bos
5 * Copyright 1996, 2004 Alexandre Julliard
6 * Copyright 1999 Petr Tomasek
7 * Copyright 2000 Andreas Mohr
8 * Copyright 2003 Eric Pouech
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25 #include "config.h"
26 #include "wine/port.h"
28 #include <stdarg.h>
29 #include <stdlib.h>
30 #include <stdio.h>
32 #include "ntstatus.h"
33 #define WIN32_NO_STATUS
34 #include "windef.h"
35 #include "winbase.h"
36 #include "winnls.h"
37 #include "winternl.h"
38 #include "winioctl.h"
39 #include "ntddcdrm.h"
40 #define WINE_MOUNTMGR_EXTENSIONS
41 #include "ddk/mountmgr.h"
42 #include "ddk/wdm.h"
43 #include "kernel_private.h"
44 #include "wine/library.h"
45 #include "wine/unicode.h"
46 #include "wine/debug.h"
48 WINE_DEFAULT_DEBUG_CHANNEL(volume);
50 #define BLOCK_SIZE 2048
51 #define SUPERBLOCK_SIZE BLOCK_SIZE
52 #define SYMBOLIC_LINK_QUERY 0x0001
54 #define CDFRAMES_PERSEC 75
55 #define CDFRAMES_PERMIN (CDFRAMES_PERSEC * 60)
56 #define FRAME_OF_ADDR(a) ((a)[1] * CDFRAMES_PERMIN + (a)[2] * CDFRAMES_PERSEC + (a)[3])
57 #define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc)->TrackData[(idx) - (toc)->FirstTrack].Address)
59 #define GETWORD(buf,off) MAKEWORD(buf[(off)],buf[(off+1)])
60 #define GETLONG(buf,off) MAKELONG(GETWORD(buf,off),GETWORD(buf,off+2))
62 enum fs_type
64 FS_ERROR, /* error accessing the device */
65 FS_UNKNOWN, /* unknown file system */
66 FS_FAT1216,
67 FS_FAT32,
68 FS_ISO9660,
69 FS_UDF /* For reference [E] = Ecma-167.pdf, [U] = udf260.pdf */
72 /* read the contents of an NT symlink object */
73 static NTSTATUS read_nt_symlink( const WCHAR *name, WCHAR *target, DWORD size )
75 NTSTATUS status;
76 OBJECT_ATTRIBUTES attr;
77 UNICODE_STRING nameW;
78 HANDLE handle;
80 attr.Length = sizeof(attr);
81 attr.RootDirectory = 0;
82 attr.Attributes = OBJ_CASE_INSENSITIVE;
83 attr.ObjectName = &nameW;
84 attr.SecurityDescriptor = NULL;
85 attr.SecurityQualityOfService = NULL;
86 RtlInitUnicodeString( &nameW, name );
88 if (!(status = NtOpenSymbolicLinkObject( &handle, SYMBOLIC_LINK_QUERY, &attr )))
90 UNICODE_STRING targetW;
91 targetW.Buffer = target;
92 targetW.MaximumLength = (size - 1) * sizeof(WCHAR);
93 status = NtQuerySymbolicLinkObject( handle, &targetW, NULL );
94 if (!status) target[targetW.Length / sizeof(WCHAR)] = 0;
95 NtClose( handle );
97 return status;
100 /* open a handle to a device root */
101 static BOOL open_device_root( LPCWSTR root, HANDLE *handle )
103 static const WCHAR default_rootW[] = {'\\',0};
104 UNICODE_STRING nt_name;
105 OBJECT_ATTRIBUTES attr;
106 IO_STATUS_BLOCK io;
107 NTSTATUS status;
109 if (!root) root = default_rootW;
110 if (!RtlDosPathNameToNtPathName_U( root, &nt_name, NULL, NULL ))
112 SetLastError( ERROR_PATH_NOT_FOUND );
113 return FALSE;
115 attr.Length = sizeof(attr);
116 attr.RootDirectory = 0;
117 attr.Attributes = OBJ_CASE_INSENSITIVE;
118 attr.ObjectName = &nt_name;
119 attr.SecurityDescriptor = NULL;
120 attr.SecurityQualityOfService = NULL;
122 status = NtOpenFile( handle, SYNCHRONIZE, &attr, &io, 0,
123 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT );
124 RtlFreeUnicodeString( &nt_name );
125 return set_ntstatus( status );
128 /* query the type of a drive from the mount manager */
129 static DWORD get_mountmgr_drive_type( LPCWSTR root )
131 HANDLE mgr;
132 struct mountmgr_unix_drive data;
133 DWORD br;
135 memset( &data, 0, sizeof(data) );
136 if (root) data.letter = root[0];
137 else
139 WCHAR curdir[MAX_PATH];
140 GetCurrentDirectoryW( MAX_PATH, curdir );
141 if (curdir[1] != ':' || curdir[2] != '\\') return DRIVE_UNKNOWN;
142 data.letter = curdir[0];
145 mgr = CreateFileW( MOUNTMGR_DOS_DEVICE_NAME, GENERIC_READ,
146 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0 );
147 if (mgr == INVALID_HANDLE_VALUE) return DRIVE_UNKNOWN;
149 if (!DeviceIoControl( mgr, IOCTL_MOUNTMGR_QUERY_UNIX_DRIVE, &data, sizeof(data), &data,
150 sizeof(data), &br, NULL ) && GetLastError() != ERROR_MORE_DATA)
151 data.type = DRIVE_UNKNOWN;
153 CloseHandle( mgr );
154 return data.type;
157 /* get the label by reading it from a file at the root of the filesystem */
158 static void get_filesystem_label( const UNICODE_STRING *device, WCHAR *label, DWORD len )
160 static const WCHAR labelW[] = {'.','w','i','n','d','o','w','s','-','l','a','b','e','l',0};
161 HANDLE handle;
162 UNICODE_STRING name;
163 IO_STATUS_BLOCK io;
164 OBJECT_ATTRIBUTES attr;
166 label[0] = 0;
168 attr.Length = sizeof(attr);
169 attr.RootDirectory = 0;
170 attr.Attributes = OBJ_CASE_INSENSITIVE;
171 attr.ObjectName = &name;
172 attr.SecurityDescriptor = NULL;
173 attr.SecurityQualityOfService = NULL;
175 name.MaximumLength = device->Length + sizeof(labelW);
176 name.Length = name.MaximumLength - sizeof(WCHAR);
177 if (!(name.Buffer = HeapAlloc( GetProcessHeap(), 0, name.MaximumLength ))) return;
179 memcpy( name.Buffer, device->Buffer, device->Length );
180 memcpy( name.Buffer + device->Length / sizeof(WCHAR), labelW, sizeof(labelW) );
181 if (!NtOpenFile( &handle, GENERIC_READ | SYNCHRONIZE, &attr, &io, FILE_SHARE_READ|FILE_SHARE_WRITE,
182 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT ))
184 char buffer[256], *p;
185 DWORD size;
187 if (!ReadFile( handle, buffer, sizeof(buffer)-1, &size, NULL )) size = 0;
188 CloseHandle( handle );
189 p = buffer + size;
190 while (p > buffer && (p[-1] == ' ' || p[-1] == '\r' || p[-1] == '\n')) p--;
191 *p = 0;
192 if (!MultiByteToWideChar( CP_UNIXCP, 0, buffer, -1, label, len ))
193 label[len-1] = 0;
195 RtlFreeUnicodeString( &name );
198 /* get the serial number by reading it from a file at the root of the filesystem */
199 static DWORD get_filesystem_serial( const UNICODE_STRING *device )
201 static const WCHAR serialW[] = {'.','w','i','n','d','o','w','s','-','s','e','r','i','a','l',0};
202 HANDLE handle;
203 UNICODE_STRING name;
204 IO_STATUS_BLOCK io;
205 OBJECT_ATTRIBUTES attr;
206 DWORD ret = 0;
208 attr.Length = sizeof(attr);
209 attr.RootDirectory = 0;
210 attr.Attributes = OBJ_CASE_INSENSITIVE;
211 attr.ObjectName = &name;
212 attr.SecurityDescriptor = NULL;
213 attr.SecurityQualityOfService = NULL;
215 name.MaximumLength = device->Length + sizeof(serialW);
216 name.Length = name.MaximumLength - sizeof(WCHAR);
217 if (!(name.Buffer = HeapAlloc( GetProcessHeap(), 0, name.MaximumLength ))) return 0;
219 memcpy( name.Buffer, device->Buffer, device->Length );
220 memcpy( name.Buffer + device->Length / sizeof(WCHAR), serialW, sizeof(serialW) );
221 if (!NtOpenFile( &handle, GENERIC_READ | SYNCHRONIZE, &attr, &io, FILE_SHARE_READ|FILE_SHARE_WRITE,
222 FILE_SYNCHRONOUS_IO_NONALERT ))
224 char buffer[32];
225 DWORD size;
227 if (!ReadFile( handle, buffer, sizeof(buffer)-1, &size, NULL )) size = 0;
228 CloseHandle( handle );
229 buffer[size] = 0;
230 ret = strtoul( buffer, NULL, 16 );
232 RtlFreeUnicodeString( &name );
233 return ret;
237 /******************************************************************
238 * VOLUME_FindCdRomDataBestVoldesc
240 static DWORD VOLUME_FindCdRomDataBestVoldesc( HANDLE handle )
242 BYTE cur_vd_type, max_vd_type = 0;
243 BYTE buffer[0x800];
244 DWORD size, offs, best_offs = 0, extra_offs = 0;
246 for (offs = 0x8000; offs <= 0x9800; offs += 0x800)
248 /* if 'CDROM' occurs at position 8, this is a pre-iso9660 cd, and
249 * the volume label is displaced forward by 8
251 if (SetFilePointer( handle, offs, NULL, FILE_BEGIN ) != offs) break;
252 if (!ReadFile( handle, buffer, sizeof(buffer), &size, NULL )) break;
253 if (size != sizeof(buffer)) break;
254 /* check for non-ISO9660 signature */
255 if (!memcmp( buffer + 11, "ROM", 3 )) extra_offs = 8;
256 cur_vd_type = buffer[extra_offs];
257 if (cur_vd_type == 0xff) /* voldesc set terminator */
258 break;
259 if (cur_vd_type > max_vd_type)
261 max_vd_type = cur_vd_type;
262 best_offs = offs + extra_offs;
265 return best_offs;
269 /***********************************************************************
270 * VOLUME_ReadFATSuperblock
272 static enum fs_type VOLUME_ReadFATSuperblock( HANDLE handle, BYTE *buff )
274 DWORD size;
276 /* try a fixed disk, with a FAT partition */
277 if (SetFilePointer( handle, 0, NULL, FILE_BEGIN ) != 0 ||
278 !ReadFile( handle, buff, SUPERBLOCK_SIZE, &size, NULL ))
280 if (GetLastError() == ERROR_BAD_DEV_TYPE) return FS_UNKNOWN; /* not a real device */
281 return FS_ERROR;
284 if (size < SUPERBLOCK_SIZE) return FS_UNKNOWN;
286 /* FIXME: do really all FAT have their name beginning with
287 * "FAT" ? (At least FAT12, FAT16 and FAT32 have :)
289 if (!memcmp(buff+0x36, "FAT", 3) || !memcmp(buff+0x52, "FAT", 3))
291 /* guess which type of FAT we have */
292 int reasonable;
293 unsigned int sectors,
294 sect_per_fat,
295 total_sectors,
296 num_boot_sectors,
297 num_fats,
298 num_root_dir_ents,
299 bytes_per_sector,
300 sectors_per_cluster,
301 nclust;
302 sect_per_fat = GETWORD(buff, 0x16);
303 if (!sect_per_fat) sect_per_fat = GETLONG(buff, 0x24);
304 total_sectors = GETWORD(buff, 0x13);
305 if (!total_sectors)
306 total_sectors = GETLONG(buff, 0x20);
307 num_boot_sectors = GETWORD(buff, 0x0e);
308 num_fats = buff[0x10];
309 num_root_dir_ents = GETWORD(buff, 0x11);
310 bytes_per_sector = GETWORD(buff, 0x0b);
311 sectors_per_cluster = buff[0x0d];
312 /* check if the parameters are reasonable and will not cause
313 * arithmetic errors in the calculation */
314 reasonable = num_boot_sectors < total_sectors &&
315 num_fats < 16 &&
316 bytes_per_sector >= 512 && bytes_per_sector % 512 == 0 &&
317 sectors_per_cluster >= 1;
318 if (!reasonable) return FS_UNKNOWN;
319 sectors = total_sectors - num_boot_sectors - num_fats * sect_per_fat -
320 (num_root_dir_ents * 32 + bytes_per_sector - 1) / bytes_per_sector;
321 nclust = sectors / sectors_per_cluster;
322 if ((buff[0x42] == 0x28 || buff[0x42] == 0x29) &&
323 !memcmp(buff+0x52, "FAT", 3)) return FS_FAT32;
324 if (nclust < 65525)
326 if ((buff[0x26] == 0x28 || buff[0x26] == 0x29) &&
327 !memcmp(buff+0x36, "FAT", 3))
328 return FS_FAT1216;
331 return FS_UNKNOWN;
335 /***********************************************************************
336 * VOLUME_ReadCDBlock
338 static BOOL VOLUME_ReadCDBlock( HANDLE handle, BYTE *buff, INT offs )
340 DWORD size, whence = offs >= 0 ? FILE_BEGIN : FILE_END;
342 if (SetFilePointer( handle, offs, NULL, whence ) != offs ||
343 !ReadFile( handle, buff, SUPERBLOCK_SIZE, &size, NULL ) ||
344 size != SUPERBLOCK_SIZE)
345 return FALSE;
347 return TRUE;
351 /***********************************************************************
352 * VOLUME_ReadCDSuperblock
354 static enum fs_type VOLUME_ReadCDSuperblock( HANDLE handle, BYTE *buff )
356 int i;
357 DWORD offs;
359 /* Check UDF first as UDF and ISO9660 structures can coexist on the same medium
360 * Starting from sector 16, we may find :
361 * - a CD-ROM Volume Descriptor Set (ISO9660) containing one or more Volume Descriptors
362 * - an Extended Area (UDF) -- [E] 2/8.3.1 and [U] 2.1.7
363 * There is no explicit end so read 16 sectors and then give up */
364 for( i=16; i<16+16; i++)
366 if (!VOLUME_ReadCDBlock(handle, buff, i*BLOCK_SIZE))
367 continue;
369 /* We are supposed to check "BEA01", "NSR0x" and "TEA01" IDs + verify tag checksum
370 * but we assume the volume is well-formatted */
371 if (!memcmp(&buff[1], "BEA01", 5)) return FS_UDF;
374 offs = VOLUME_FindCdRomDataBestVoldesc( handle );
375 if (!offs) return FS_UNKNOWN;
377 if (!VOLUME_ReadCDBlock(handle, buff, offs))
378 return FS_ERROR;
380 /* check for the iso9660 identifier */
381 if (!memcmp(&buff[1], "CD001", 5)) return FS_ISO9660;
382 return FS_UNKNOWN;
386 /**************************************************************************
387 * UDF_Find_PVD
388 * Find the Primary Volume Descriptor
390 static BOOL UDF_Find_PVD( HANDLE handle, BYTE pvd[] )
392 unsigned int i;
393 DWORD offset;
394 INT locations[] = { 256, -1, -257, 512 };
396 for(i=0; i<ARRAY_SIZE(locations); i++)
398 if (!VOLUME_ReadCDBlock(handle, pvd, locations[i]*BLOCK_SIZE))
399 return FALSE;
401 /* Tag Identifier of Anchor Volume Descriptor Pointer is 2 -- [E] 3/10.2.1 */
402 if (pvd[0]==2 && pvd[1]==0)
404 /* Tag location (Uint32) at offset 12, little-endian */
405 offset = pvd[20 + 0];
406 offset |= pvd[20 + 1] << 8;
407 offset |= pvd[20 + 2] << 16;
408 offset |= pvd[20 + 3] << 24;
409 offset *= BLOCK_SIZE;
411 if (!VOLUME_ReadCDBlock(handle, pvd, offset))
412 return FALSE;
414 /* Check for the Primary Volume Descriptor Tag Id -- [E] 3/10.1.1 */
415 if (pvd[0]!=1 || pvd[1]!=0)
416 return FALSE;
418 /* 8 or 16 bits per character -- [U] 2.1.1 */
419 if (!(pvd[24]==8 || pvd[24]==16))
420 return FALSE;
422 return TRUE;
426 return FALSE;
430 /**************************************************************************
431 * VOLUME_GetSuperblockLabel
433 static void VOLUME_GetSuperblockLabel( const UNICODE_STRING *device, HANDLE handle,
434 enum fs_type type, const BYTE *superblock,
435 WCHAR *label, DWORD len )
437 const BYTE *label_ptr = NULL;
438 DWORD label_len;
440 switch(type)
442 case FS_ERROR:
443 label_len = 0;
444 break;
445 case FS_UNKNOWN:
446 get_filesystem_label( device, label, len );
447 return;
448 case FS_FAT1216:
449 label_ptr = superblock + 0x2b;
450 label_len = 11;
451 break;
452 case FS_FAT32:
453 label_ptr = superblock + 0x47;
454 label_len = 11;
455 break;
456 case FS_ISO9660:
458 BYTE ver = superblock[0x5a];
460 if (superblock[0x58] == 0x25 && superblock[0x59] == 0x2f && /* Unicode ID */
461 ((ver == 0x40) || (ver == 0x43) || (ver == 0x45)))
462 { /* yippee, unicode */
463 unsigned int i;
465 if (len > 17) len = 17;
466 for (i = 0; i < len-1; i++)
467 label[i] = (superblock[40+2*i] << 8) | superblock[41+2*i];
468 label[i] = 0;
469 while (i && label[i-1] == ' ') label[--i] = 0;
470 return;
472 label_ptr = superblock + 40;
473 label_len = 32;
474 break;
476 case FS_UDF:
478 BYTE pvd[BLOCK_SIZE];
480 if(!UDF_Find_PVD(handle, pvd))
482 label_len = 0;
483 break;
486 /* [E] 3/10.1.4 and [U] 2.1.1 */
487 if(pvd[24]==8)
489 label_ptr = pvd + 24 + 1;
490 label_len = pvd[24+32-1];
491 break;
493 else
495 unsigned int i;
497 label_len = 1 + pvd[24+32-1];
498 for(i=0; i<label_len && i<len; i+=2)
499 label[i/2] = (pvd[24+1 +i] << 8) | pvd[24+1 +i+1];
500 label[label_len] = 0;
501 return;
505 if (label_len) RtlMultiByteToUnicodeN( label, (len-1) * sizeof(WCHAR),
506 &label_len, (LPCSTR)label_ptr, label_len );
507 label_len /= sizeof(WCHAR);
508 label[label_len] = 0;
509 while (label_len && label[label_len-1] == ' ') label[--label_len] = 0;
513 /**************************************************************************
514 * UDF_Find_FSD_Sector
515 * Find the File Set Descriptor used to compute the serial of a UDF volume
517 static int UDF_Find_FSD_Sector( HANDLE handle, BYTE block[] )
519 int i, PVD_sector, PD_sector, PD_length;
521 if(!UDF_Find_PVD(handle,block))
522 goto default_sector;
524 /* Retrieve the tag location of the PVD -- [E] 3/7.2 */
525 PVD_sector = block[12 + 0];
526 PVD_sector |= block[12 + 1] << 8;
527 PVD_sector |= block[12 + 2] << 16;
528 PVD_sector |= block[12 + 3] << 24;
530 /* Find the Partition Descriptor */
531 for(i=PVD_sector+1; ; i++)
533 if(!VOLUME_ReadCDBlock(handle, block, i*BLOCK_SIZE))
534 goto default_sector;
536 /* Partition Descriptor Tag Id -- [E] 3/10.5.1 */
537 if(block[0]==5 && block[1]==0)
538 break;
540 /* Terminating Descriptor Tag Id -- [E] 3/10.9.1 */
541 if(block[0]==8 && block[1]==0)
542 goto default_sector;
545 /* Find the partition starting location -- [E] 3/10.5.8 */
546 PD_sector = block[188 + 0];
547 PD_sector |= block[188 + 1] << 8;
548 PD_sector |= block[188 + 2] << 16;
549 PD_sector |= block[188 + 3] << 24;
551 /* Find the partition length -- [E] 3/10.5.9 */
552 PD_length = block[192 + 0];
553 PD_length |= block[192 + 1] << 8;
554 PD_length |= block[192 + 2] << 16;
555 PD_length |= block[192 + 3] << 24;
557 for(i=PD_sector; i<PD_sector+PD_length; i++)
559 if(!VOLUME_ReadCDBlock(handle, block, i*BLOCK_SIZE))
560 goto default_sector;
562 /* File Set Descriptor Tag Id -- [E] 3/14.1.1 */
563 if(block[0]==0 && block[1]==1)
564 return i;
567 default_sector:
568 WARN("FSD sector not found, serial may be incorrect\n");
569 return 257;
573 /**************************************************************************
574 * VOLUME_GetSuperblockSerial
576 static DWORD VOLUME_GetSuperblockSerial( const UNICODE_STRING *device, HANDLE handle,
577 enum fs_type type, const BYTE *superblock )
579 int FSD_sector;
580 BYTE block[BLOCK_SIZE];
582 switch(type)
584 case FS_ERROR:
585 break;
586 case FS_UNKNOWN:
587 return get_filesystem_serial( device );
588 case FS_FAT1216:
589 return GETLONG( superblock, 0x27 );
590 case FS_FAT32:
591 return GETLONG( superblock, 0x33 );
592 case FS_UDF:
593 FSD_sector = UDF_Find_FSD_Sector(handle, block);
594 if (!VOLUME_ReadCDBlock(handle, block, FSD_sector*BLOCK_SIZE))
595 break;
596 superblock = block;
597 /* fallthrough */
598 case FS_ISO9660:
600 BYTE sum[4];
601 int i;
603 sum[0] = sum[1] = sum[2] = sum[3] = 0;
604 for (i = 0; i < 2048; i += 4)
606 /* DON'T optimize this into DWORD !! (breaks overflow) */
607 sum[0] += superblock[i+0];
608 sum[1] += superblock[i+1];
609 sum[2] += superblock[i+2];
610 sum[3] += superblock[i+3];
613 * OK, another braindead one... argh. Just believe it.
614 * Me$$ysoft chose to reverse the serial number in NT4/W2K.
615 * It's true and nobody will ever be able to change it.
617 if ((GetVersion() & 0x80000000) || type == FS_UDF)
618 return (sum[3] << 24) | (sum[2] << 16) | (sum[1] << 8) | sum[0];
619 else
620 return (sum[0] << 24) | (sum[1] << 16) | (sum[2] << 8) | sum[3];
623 return 0;
627 /**************************************************************************
628 * VOLUME_GetAudioCDSerial
630 static DWORD VOLUME_GetAudioCDSerial( const CDROM_TOC *toc )
632 DWORD serial = 0;
633 int i;
635 for (i = 0; i <= toc->LastTrack - toc->FirstTrack; i++)
636 serial += ((toc->TrackData[i].Address[1] << 16) |
637 (toc->TrackData[i].Address[2] << 8) |
638 toc->TrackData[i].Address[3]);
641 * dwStart, dwEnd collect the beginning and end of the disc respectively, in
642 * frames.
643 * There it is collected for correcting the serial when there are less than
644 * 3 tracks.
646 if (toc->LastTrack - toc->FirstTrack + 1 < 3)
648 DWORD dwStart = FRAME_OF_TOC(toc, toc->FirstTrack);
649 DWORD dwEnd = FRAME_OF_TOC(toc, toc->LastTrack + 1);
650 serial += dwEnd - dwStart;
652 return serial;
656 /***********************************************************************
657 * GetVolumeInformationW (KERNEL32.@)
659 BOOL WINAPI GetVolumeInformationW( LPCWSTR root, LPWSTR label, DWORD label_len,
660 DWORD *serial, DWORD *filename_len, DWORD *flags,
661 LPWSTR fsname, DWORD fsname_len )
663 static const WCHAR audiocdW[] = {'A','u','d','i','o',' ','C','D',0};
664 static const WCHAR fatW[] = {'F','A','T',0};
665 static const WCHAR fat32W[] = {'F','A','T','3','2',0};
666 static const WCHAR ntfsW[] = {'N','T','F','S',0};
667 static const WCHAR cdfsW[] = {'C','D','F','S',0};
668 static const WCHAR udfW[] = {'U','D','F',0};
669 static const WCHAR default_rootW[] = {'\\',0};
671 HANDLE handle;
672 NTSTATUS status;
673 UNICODE_STRING nt_name;
674 IO_STATUS_BLOCK io;
675 OBJECT_ATTRIBUTES attr;
676 FILE_FS_DEVICE_INFORMATION info;
677 unsigned int i;
678 enum fs_type type = FS_UNKNOWN;
679 BOOL ret = FALSE;
681 if (!root) root = default_rootW;
682 if (!RtlDosPathNameToNtPathName_U( root, &nt_name, NULL, NULL ))
684 SetLastError( ERROR_PATH_NOT_FOUND );
685 return FALSE;
687 /* there must be exactly one backslash in the name, at the end */
688 for (i = 4; i < nt_name.Length / sizeof(WCHAR); i++) if (nt_name.Buffer[i] == '\\') break;
689 if (i != nt_name.Length / sizeof(WCHAR) - 1)
691 /* check if root contains an explicit subdir */
692 if (root[0] && root[1] == ':') root += 2;
693 while (*root == '\\') root++;
694 if (strchrW( root, '\\' ))
695 SetLastError( ERROR_DIR_NOT_ROOT );
696 else
697 SetLastError( ERROR_INVALID_NAME );
698 goto done;
701 /* try to open the device */
703 attr.Length = sizeof(attr);
704 attr.RootDirectory = 0;
705 attr.Attributes = OBJ_CASE_INSENSITIVE;
706 attr.ObjectName = &nt_name;
707 attr.SecurityDescriptor = NULL;
708 attr.SecurityQualityOfService = NULL;
710 nt_name.Length -= sizeof(WCHAR); /* without trailing slash */
711 status = NtOpenFile( &handle, GENERIC_READ | SYNCHRONIZE, &attr, &io, FILE_SHARE_READ | FILE_SHARE_WRITE,
712 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT );
713 nt_name.Length += sizeof(WCHAR);
715 if (status == STATUS_SUCCESS)
717 BYTE superblock[SUPERBLOCK_SIZE];
718 CDROM_TOC toc;
719 DWORD br;
721 /* check for audio CD */
722 /* FIXME: we only check the first track for now */
723 if (DeviceIoControl( handle, IOCTL_CDROM_READ_TOC, NULL, 0, &toc, sizeof(toc), &br, 0 ))
725 if (!(toc.TrackData[0].Control & 0x04)) /* audio track */
727 TRACE( "%s: found audio CD\n", debugstr_w(nt_name.Buffer) );
728 if (label) lstrcpynW( label, audiocdW, label_len );
729 if (serial) *serial = VOLUME_GetAudioCDSerial( &toc );
730 CloseHandle( handle );
731 type = FS_ISO9660;
732 goto fill_fs_info;
734 type = VOLUME_ReadCDSuperblock( handle, superblock );
736 else
738 type = VOLUME_ReadFATSuperblock( handle, superblock );
739 if (type == FS_UNKNOWN) type = VOLUME_ReadCDSuperblock( handle, superblock );
741 TRACE( "%s: found fs type %d\n", debugstr_w(nt_name.Buffer), type );
742 if (type == FS_ERROR)
744 CloseHandle( handle );
745 goto done;
748 if (label && label_len) VOLUME_GetSuperblockLabel( &nt_name, handle, type, superblock, label, label_len );
749 if (serial) *serial = VOLUME_GetSuperblockSerial( &nt_name, handle, type, superblock );
750 CloseHandle( handle );
751 goto fill_fs_info;
753 else
755 TRACE( "cannot open device %s: %x\n", debugstr_w(nt_name.Buffer), status );
756 if (status == STATUS_ACCESS_DENIED)
757 MESSAGE( "wine: Read access denied for device %s, FS volume label and serial are not available.\n", debugstr_w(nt_name.Buffer) );
759 /* we couldn't open the device, fallback to default strategy */
761 if (!set_ntstatus( NtOpenFile( &handle, SYNCHRONIZE, &attr, &io, 0,
762 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT )))
763 goto done;
765 status = NtQueryVolumeInformationFile( handle, &io, &info, sizeof(info), FileFsDeviceInformation );
766 NtClose( handle );
767 if (!set_ntstatus( status )) goto done;
769 if (info.DeviceType == FILE_DEVICE_CD_ROM_FILE_SYSTEM) type = FS_ISO9660;
771 if (label && label_len) get_filesystem_label( &nt_name, label, label_len );
772 if (serial) *serial = get_filesystem_serial( &nt_name );
774 fill_fs_info: /* now fill in the information that depends on the file system type */
776 switch(type)
778 case FS_ISO9660:
779 if (fsname) lstrcpynW( fsname, cdfsW, fsname_len );
780 if (filename_len) *filename_len = 221;
781 if (flags) *flags = FILE_READ_ONLY_VOLUME;
782 break;
783 case FS_UDF:
784 if (fsname) lstrcpynW( fsname, udfW, fsname_len );
785 if (filename_len) *filename_len = 255;
786 if (flags)
787 *flags = FILE_READ_ONLY_VOLUME | FILE_UNICODE_ON_DISK | FILE_CASE_SENSITIVE_SEARCH;
788 break;
789 case FS_FAT1216:
790 if (fsname) lstrcpynW( fsname, fatW, fsname_len );
791 case FS_FAT32:
792 if (type == FS_FAT32 && fsname) lstrcpynW( fsname, fat32W, fsname_len );
793 if (filename_len) *filename_len = 255;
794 if (flags) *flags = FILE_CASE_PRESERVED_NAMES; /* FIXME */
795 break;
796 default:
797 if (fsname) lstrcpynW( fsname, ntfsW, fsname_len );
798 if (filename_len) *filename_len = 255;
799 if (flags) *flags = FILE_CASE_PRESERVED_NAMES | FILE_PERSISTENT_ACLS;
800 break;
802 ret = TRUE;
804 done:
805 RtlFreeUnicodeString( &nt_name );
806 return ret;
810 /***********************************************************************
811 * GetVolumeInformationA (KERNEL32.@)
813 BOOL WINAPI GetVolumeInformationA( LPCSTR root, LPSTR label,
814 DWORD label_len, DWORD *serial,
815 DWORD *filename_len, DWORD *flags,
816 LPSTR fsname, DWORD fsname_len )
818 WCHAR *rootW = NULL;
819 LPWSTR labelW, fsnameW;
820 BOOL ret;
822 if (root && !(rootW = FILE_name_AtoW( root, FALSE ))) return FALSE;
824 labelW = label ? HeapAlloc(GetProcessHeap(), 0, label_len * sizeof(WCHAR)) : NULL;
825 fsnameW = fsname ? HeapAlloc(GetProcessHeap(), 0, fsname_len * sizeof(WCHAR)) : NULL;
827 if ((ret = GetVolumeInformationW(rootW, labelW, label_len, serial,
828 filename_len, flags, fsnameW, fsname_len)))
830 if (label) FILE_name_WtoA( labelW, -1, label, label_len );
831 if (fsname) FILE_name_WtoA( fsnameW, -1, fsname, fsname_len );
834 HeapFree( GetProcessHeap(), 0, labelW );
835 HeapFree( GetProcessHeap(), 0, fsnameW );
836 return ret;
841 /***********************************************************************
842 * SetVolumeLabelW (KERNEL32.@)
844 BOOL WINAPI SetVolumeLabelW( LPCWSTR root, LPCWSTR label )
846 WCHAR device[] = {'\\','\\','.','\\','A',':',0};
847 HANDLE handle;
848 enum fs_type type = FS_UNKNOWN;
850 if (!root)
852 WCHAR path[MAX_PATH];
853 GetCurrentDirectoryW( MAX_PATH, path );
854 device[4] = path[0];
856 else
858 if (!root[0] || root[1] != ':')
860 SetLastError( ERROR_INVALID_NAME );
861 return FALSE;
863 device[4] = root[0];
866 /* try to open the device */
868 handle = CreateFileW( device, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE,
869 NULL, OPEN_EXISTING, 0, 0 );
870 if (handle != INVALID_HANDLE_VALUE)
872 BYTE superblock[SUPERBLOCK_SIZE];
874 type = VOLUME_ReadFATSuperblock( handle, superblock );
875 if (type == FS_UNKNOWN) type = VOLUME_ReadCDSuperblock( handle, superblock );
876 CloseHandle( handle );
877 if (type != FS_UNKNOWN)
879 /* we can't set the label on FAT or CDROM file systems */
880 TRACE( "cannot set label on device %s type %d\n", debugstr_w(device), type );
881 SetLastError( ERROR_ACCESS_DENIED );
882 return FALSE;
885 else
887 TRACE( "cannot open device %s: err %d\n", debugstr_w(device), GetLastError() );
888 if (GetLastError() == ERROR_ACCESS_DENIED) return FALSE;
891 /* we couldn't open the device, fallback to default strategy */
893 switch(GetDriveTypeW( root ))
895 case DRIVE_UNKNOWN:
896 case DRIVE_NO_ROOT_DIR:
897 SetLastError( ERROR_NOT_READY );
898 break;
899 case DRIVE_REMOVABLE:
900 case DRIVE_FIXED:
902 WCHAR labelW[] = {'A',':','\\','.','w','i','n','d','o','w','s','-','l','a','b','e','l',0};
904 labelW[0] = device[4];
906 if (!label[0]) /* delete label file when setting an empty label */
907 return DeleteFileW( labelW ) || GetLastError() == ERROR_FILE_NOT_FOUND;
909 handle = CreateFileW( labelW, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
910 CREATE_ALWAYS, 0, 0 );
911 if (handle != INVALID_HANDLE_VALUE)
913 char buffer[64];
914 DWORD size;
916 if (!WideCharToMultiByte( CP_UNIXCP, 0, label, -1, buffer, sizeof(buffer)-1, NULL, NULL ))
917 buffer[sizeof(buffer)-2] = 0;
918 strcat( buffer, "\n" );
919 WriteFile( handle, buffer, strlen(buffer), &size, NULL );
920 CloseHandle( handle );
921 return TRUE;
923 break;
925 case DRIVE_REMOTE:
926 case DRIVE_RAMDISK:
927 case DRIVE_CDROM:
928 SetLastError( ERROR_ACCESS_DENIED );
929 break;
931 return FALSE;
934 /***********************************************************************
935 * SetVolumeLabelA (KERNEL32.@)
937 BOOL WINAPI SetVolumeLabelA(LPCSTR root, LPCSTR volname)
939 WCHAR *rootW = NULL, *volnameW = NULL;
940 BOOL ret;
942 if (root && !(rootW = FILE_name_AtoW( root, FALSE ))) return FALSE;
943 if (volname && !(volnameW = FILE_name_AtoW( volname, TRUE ))) return FALSE;
944 ret = SetVolumeLabelW( rootW, volnameW );
945 HeapFree( GetProcessHeap(), 0, volnameW );
946 return ret;
950 /***********************************************************************
951 * GetVolumeNameForVolumeMountPointA (KERNEL32.@)
953 BOOL WINAPI GetVolumeNameForVolumeMountPointA( LPCSTR path, LPSTR volume, DWORD size )
955 BOOL ret;
956 WCHAR volumeW[50], *pathW = NULL;
957 DWORD len = min(ARRAY_SIZE(volumeW), size );
959 TRACE("(%s, %p, %x)\n", debugstr_a(path), volume, size);
961 if (!path || !(pathW = FILE_name_AtoW( path, TRUE )))
962 return FALSE;
964 if ((ret = GetVolumeNameForVolumeMountPointW( pathW, volumeW, len )))
965 FILE_name_WtoA( volumeW, -1, volume, len );
967 HeapFree( GetProcessHeap(), 0, pathW );
968 return ret;
971 /***********************************************************************
972 * GetVolumeNameForVolumeMountPointW (KERNEL32.@)
974 BOOL WINAPI GetVolumeNameForVolumeMountPointW( LPCWSTR path, LPWSTR volume, DWORD size )
976 static const WCHAR prefixW[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\',0};
977 static const WCHAR volumeW[] = {'\\','?','?','\\','V','o','l','u','m','e','{'};
978 static const WCHAR trailingW[] = {'\\',0};
980 MOUNTMGR_MOUNT_POINT *input = NULL, *o1;
981 MOUNTMGR_MOUNT_POINTS *output = NULL;
982 WCHAR *p;
983 char *r;
984 DWORD i, i_size = 1024, o_size = 1024;
985 WCHAR *nonpersist_name;
986 WCHAR symlink_name[MAX_PATH];
987 NTSTATUS status;
988 HANDLE mgr = INVALID_HANDLE_VALUE;
989 BOOL ret = FALSE;
990 DWORD br;
992 TRACE("(%s, %p, %x)\n", debugstr_w(path), volume, size);
993 if (path[lstrlenW(path)-1] != '\\')
995 SetLastError( ERROR_INVALID_NAME );
996 return FALSE;
999 if (size < 50)
1001 SetLastError( ERROR_FILENAME_EXCED_RANGE );
1002 return FALSE;
1004 /* if length of input is > 3 then it must be a mounted folder */
1005 if (lstrlenW(path) > 3)
1007 FIXME("Mounted Folders are not yet supported\n");
1008 SetLastError( ERROR_NOT_A_REPARSE_POINT );
1009 return FALSE;
1012 mgr = CreateFileW( MOUNTMGR_DOS_DEVICE_NAME, 0, FILE_SHARE_READ,
1013 NULL, OPEN_EXISTING, 0, 0 );
1014 if (mgr == INVALID_HANDLE_VALUE) return FALSE;
1016 if (!(input = HeapAlloc( GetProcessHeap(), 0, i_size )))
1018 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1019 goto err_ret;
1022 if (!(output = HeapAlloc( GetProcessHeap(), 0, o_size )))
1024 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1025 goto err_ret;
1028 /* construct the symlink name as "\DosDevices\C:" */
1029 lstrcpyW( symlink_name, prefixW );
1030 lstrcatW( symlink_name, path );
1031 symlink_name[lstrlenW(symlink_name)-1] = 0;
1033 /* Take the mount point and get the "nonpersistent name" */
1034 /* We will then take that and get the volume name */
1035 nonpersist_name = (WCHAR *)(input + 1);
1036 status = read_nt_symlink( symlink_name, nonpersist_name, i_size - sizeof(*input) );
1037 TRACE("read_nt_symlink got stat=%x, for %s, got <%s>\n", status,
1038 debugstr_w(symlink_name), debugstr_w(nonpersist_name));
1039 if (status != STATUS_SUCCESS)
1041 SetLastError( ERROR_FILE_NOT_FOUND );
1042 goto err_ret;
1045 /* Now take the "nonpersistent name" and ask the mountmgr */
1046 /* to give us all the mount points. One of them will be */
1047 /* the volume name (format of \??\Volume{). */
1048 memset( input, 0, sizeof(*input) ); /* clear all input parameters */
1049 input->DeviceNameOffset = sizeof(*input);
1050 input->DeviceNameLength = lstrlenW( nonpersist_name) * sizeof(WCHAR);
1051 i_size = input->DeviceNameOffset + input->DeviceNameLength;
1053 output->Size = o_size;
1055 /* now get the true volume name from the mountmgr */
1056 if (!DeviceIoControl( mgr, IOCTL_MOUNTMGR_QUERY_POINTS, input, i_size,
1057 output, o_size, &br, NULL ))
1058 goto err_ret;
1060 /* Verify and return the data, note string is not null terminated */
1061 TRACE("found %d matching mount points\n", output->NumberOfMountPoints);
1062 if (output->NumberOfMountPoints < 1)
1064 SetLastError( ERROR_NO_VOLUME_ID );
1065 goto err_ret;
1067 o1 = &output->MountPoints[0];
1069 /* look for the volume name in returned values */
1070 for(i=0;i<output->NumberOfMountPoints;i++)
1072 p = (WCHAR*)((char *)output + o1->SymbolicLinkNameOffset);
1073 r = (char *)output + o1->UniqueIdOffset;
1074 TRACE("found symlink=%s, unique=%s, devname=%s\n",
1075 debugstr_wn(p, o1->SymbolicLinkNameLength/sizeof(WCHAR)),
1076 debugstr_an(r, o1->UniqueIdLength),
1077 debugstr_wn((WCHAR*)((char *)output + o1->DeviceNameOffset),
1078 o1->DeviceNameLength/sizeof(WCHAR)));
1080 if (!strncmpW( p, volumeW, ARRAY_SIZE( volumeW )))
1082 /* is there space in the return variable ?? */
1083 if ((o1->SymbolicLinkNameLength/sizeof(WCHAR))+2 > size)
1085 SetLastError( ERROR_FILENAME_EXCED_RANGE );
1086 goto err_ret;
1088 memcpy( volume, p, o1->SymbolicLinkNameLength );
1089 volume[o1->SymbolicLinkNameLength / sizeof(WCHAR)] = 0;
1090 lstrcatW( volume, trailingW );
1091 /* change second char from '?' to '\' */
1092 volume[1] = '\\';
1093 ret = TRUE;
1094 break;
1096 o1++;
1099 err_ret:
1100 HeapFree( GetProcessHeap(), 0, input );
1101 HeapFree( GetProcessHeap(), 0, output );
1102 CloseHandle( mgr );
1103 return ret;
1106 /***********************************************************************
1107 * DefineDosDeviceW (KERNEL32.@)
1109 BOOL WINAPI DefineDosDeviceW( DWORD flags, const WCHAR *device, const WCHAR *target )
1111 WCHAR link_name[15] = {'\\','D','o','s','D','e','v','i','c','e','s','\\',0};
1112 UNICODE_STRING nt_name, nt_target;
1113 OBJECT_ATTRIBUTES attr;
1114 NTSTATUS status;
1115 HANDLE handle;
1117 TRACE("%#x, %s, %s\n", flags, debugstr_w(device), debugstr_w(target));
1119 if (flags & ~(DDD_RAW_TARGET_PATH | DDD_REMOVE_DEFINITION))
1120 FIXME("Ignoring flags %#x.\n", flags & ~(DDD_RAW_TARGET_PATH | DDD_REMOVE_DEFINITION));
1122 strcatW( link_name, device );
1123 RtlInitUnicodeString( &nt_name, link_name );
1124 InitializeObjectAttributes( &attr, &nt_name, OBJ_CASE_INSENSITIVE, 0, NULL );
1125 if (flags & DDD_REMOVE_DEFINITION)
1127 if (!set_ntstatus( NtOpenSymbolicLinkObject( &handle, 0, &attr ) ))
1128 return FALSE;
1130 SERVER_START_REQ( unlink_object )
1132 req->handle = wine_server_obj_handle( handle );
1133 status = wine_server_call( req );
1135 SERVER_END_REQ;
1136 NtClose( handle );
1138 return set_ntstatus( status );
1141 if (!(flags & DDD_RAW_TARGET_PATH))
1143 if (!RtlDosPathNameToNtPathName_U( target, &nt_target, NULL, NULL))
1145 SetLastError( ERROR_PATH_NOT_FOUND );
1146 return FALSE;
1149 else
1150 RtlInitUnicodeString( &nt_target, target );
1152 return set_ntstatus( NtCreateSymbolicLinkObject( &handle, SYMBOLIC_LINK_ALL_ACCESS, &attr, &nt_target ) );
1156 /***********************************************************************
1157 * DefineDosDeviceA (KERNEL32.@)
1159 BOOL WINAPI DefineDosDeviceA(DWORD flags, LPCSTR devname, LPCSTR targetpath)
1161 WCHAR *devW, *targetW = NULL;
1162 BOOL ret;
1164 if (!(devW = FILE_name_AtoW( devname, FALSE ))) return FALSE;
1165 if (targetpath && !(targetW = FILE_name_AtoW( targetpath, TRUE ))) return FALSE;
1166 ret = DefineDosDeviceW(flags, devW, targetW);
1167 HeapFree( GetProcessHeap(), 0, targetW );
1168 return ret;
1172 /***********************************************************************
1173 * QueryDosDeviceW (KERNEL32.@)
1175 * returns array of strings terminated by \0, terminated by \0
1177 DWORD WINAPI QueryDosDeviceW( LPCWSTR devname, LPWSTR target, DWORD bufsize )
1179 static const WCHAR dosdevW[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\',0};
1181 UNICODE_STRING nt_name;
1182 NTSTATUS status;
1184 if (!bufsize)
1186 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1187 return 0;
1190 if (devname)
1192 WCHAR name[8];
1193 WCHAR *buffer;
1194 DWORD dosdev, ret = 0;
1196 if ((dosdev = RtlIsDosDeviceName_U( devname )))
1198 memcpy( name, devname + HIWORD(dosdev)/sizeof(WCHAR), LOWORD(dosdev) );
1199 name[LOWORD(dosdev)/sizeof(WCHAR)] = 0;
1200 devname = name;
1203 if (!(buffer = HeapAlloc( GetProcessHeap(), 0, sizeof(dosdevW) + strlenW(devname)*sizeof(WCHAR) )))
1205 SetLastError( ERROR_OUTOFMEMORY );
1206 return 0;
1208 memcpy( buffer, dosdevW, sizeof(dosdevW) );
1209 strcatW( buffer, devname );
1210 status = read_nt_symlink( buffer, target, bufsize );
1211 HeapFree( GetProcessHeap(), 0, buffer );
1212 if (!set_ntstatus( status )) return 0;
1213 ret = strlenW( target ) + 1;
1214 if (ret < bufsize) target[ret++] = 0; /* add an extra null */
1215 return ret;
1217 else /* return a list of all devices */
1219 OBJECT_ATTRIBUTES attr;
1220 HANDLE handle;
1221 WCHAR *p = target;
1223 RtlInitUnicodeString( &nt_name, dosdevW );
1224 nt_name.Length -= sizeof(WCHAR); /* without trailing slash */
1225 attr.Length = sizeof(attr);
1226 attr.RootDirectory = 0;
1227 attr.ObjectName = &nt_name;
1228 attr.Attributes = OBJ_CASE_INSENSITIVE;
1229 attr.SecurityDescriptor = NULL;
1230 attr.SecurityQualityOfService = NULL;
1231 status = NtOpenDirectoryObject( &handle, FILE_LIST_DIRECTORY, &attr );
1232 if (!status)
1234 char data[1024];
1235 DIRECTORY_BASIC_INFORMATION *info = (DIRECTORY_BASIC_INFORMATION *)data;
1236 ULONG ctx = 0, len;
1238 while (!NtQueryDirectoryObject( handle, info, sizeof(data), 1, 0, &ctx, &len ))
1240 if (p + info->ObjectName.Length/sizeof(WCHAR) + 1 >= target + bufsize)
1242 SetLastError( ERROR_INSUFFICIENT_BUFFER );
1243 NtClose( handle );
1244 return 0;
1246 memcpy( p, info->ObjectName.Buffer, info->ObjectName.Length );
1247 p += info->ObjectName.Length/sizeof(WCHAR);
1248 *p++ = 0;
1250 NtClose( handle );
1253 *p++ = 0; /* terminating null */
1254 return p - target;
1259 /***********************************************************************
1260 * QueryDosDeviceA (KERNEL32.@)
1262 * returns array of strings terminated by \0, terminated by \0
1264 DWORD WINAPI QueryDosDeviceA( LPCSTR devname, LPSTR target, DWORD bufsize )
1266 DWORD ret = 0, retW;
1267 WCHAR *devnameW = NULL;
1268 LPWSTR targetW;
1270 if (devname && !(devnameW = FILE_name_AtoW( devname, FALSE ))) return 0;
1272 targetW = HeapAlloc( GetProcessHeap(),0, bufsize * sizeof(WCHAR) );
1273 if (!targetW)
1275 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1276 return 0;
1279 retW = QueryDosDeviceW(devnameW, targetW, bufsize);
1281 ret = FILE_name_WtoA( targetW, retW, target, bufsize );
1283 HeapFree(GetProcessHeap(), 0, targetW);
1284 return ret;
1288 /***********************************************************************
1289 * GetLogicalDrives (KERNEL32.@)
1291 DWORD WINAPI GetLogicalDrives(void)
1293 static const WCHAR dosdevW[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\',0};
1294 OBJECT_ATTRIBUTES attr;
1295 UNICODE_STRING nt_name;
1296 DWORD bitmask = 0;
1297 NTSTATUS status;
1298 HANDLE handle;
1300 RtlInitUnicodeString( &nt_name, dosdevW );
1301 nt_name.Length -= sizeof(WCHAR); /* without trailing slash */
1302 attr.Length = sizeof(attr);
1303 attr.RootDirectory = 0;
1304 attr.ObjectName = &nt_name;
1305 attr.Attributes = OBJ_CASE_INSENSITIVE;
1306 attr.SecurityDescriptor = NULL;
1307 attr.SecurityQualityOfService = NULL;
1308 status = NtOpenDirectoryObject( &handle, FILE_LIST_DIRECTORY, &attr );
1309 if (!status)
1311 char data[1024];
1312 DIRECTORY_BASIC_INFORMATION *info = (DIRECTORY_BASIC_INFORMATION *)data;
1313 ULONG ctx = 0, len;
1315 while (!NtQueryDirectoryObject( handle, info, sizeof(data), 1, 0, &ctx, &len ))
1316 if(info->ObjectName.Length == 2*sizeof(WCHAR) && info->ObjectName.Buffer[1] == ':')
1317 bitmask |= 1 << (info->ObjectName.Buffer[0] - 'A');
1319 NtClose( handle );
1322 return bitmask;
1326 /***********************************************************************
1327 * GetLogicalDriveStringsA (KERNEL32.@)
1329 UINT WINAPI GetLogicalDriveStringsA( UINT len, LPSTR buffer )
1331 DWORD drives = GetLogicalDrives();
1332 UINT drive, count;
1334 for (drive = count = 0; drive < 26; drive++) if (drives & (1 << drive)) count++;
1335 if ((count * 4) + 1 > len) return count * 4 + 1;
1337 for (drive = 0; drive < 26; drive++)
1339 if (drives & (1 << drive))
1341 *buffer++ = 'A' + drive;
1342 *buffer++ = ':';
1343 *buffer++ = '\\';
1344 *buffer++ = 0;
1347 *buffer = 0;
1348 return count * 4;
1352 /***********************************************************************
1353 * GetLogicalDriveStringsW (KERNEL32.@)
1355 UINT WINAPI GetLogicalDriveStringsW( UINT len, LPWSTR buffer )
1357 DWORD drives = GetLogicalDrives();
1358 UINT drive, count;
1360 for (drive = count = 0; drive < 26; drive++) if (drives & (1 << drive)) count++;
1361 if ((count * 4) + 1 > len) return count * 4 + 1;
1363 for (drive = 0; drive < 26; drive++)
1365 if (drives & (1 << drive))
1367 *buffer++ = 'A' + drive;
1368 *buffer++ = ':';
1369 *buffer++ = '\\';
1370 *buffer++ = 0;
1373 *buffer = 0;
1374 return count * 4;
1378 /***********************************************************************
1379 * GetDriveTypeW (KERNEL32.@)
1381 * Returns the type of the disk drive specified. If root is NULL the
1382 * root of the current directory is used.
1384 * RETURNS
1386 * Type of drive (from Win32 SDK):
1388 * DRIVE_UNKNOWN unable to find out anything about the drive
1389 * DRIVE_NO_ROOT_DIR nonexistent root dir
1390 * DRIVE_REMOVABLE the disk can be removed from the machine
1391 * DRIVE_FIXED the disk cannot be removed from the machine
1392 * DRIVE_REMOTE network disk
1393 * DRIVE_CDROM CDROM drive
1394 * DRIVE_RAMDISK virtual disk in RAM
1396 UINT WINAPI GetDriveTypeW(LPCWSTR root) /* [in] String describing drive */
1398 FILE_FS_DEVICE_INFORMATION info;
1399 IO_STATUS_BLOCK io;
1400 NTSTATUS status;
1401 HANDLE handle;
1402 UINT ret;
1404 if (!open_device_root( root, &handle ))
1406 /* CD ROM devices do not necessarily have a volume, but a drive type */
1407 ret = get_mountmgr_drive_type( root );
1408 if (ret == DRIVE_CDROM || ret == DRIVE_REMOVABLE)
1409 return ret;
1411 return DRIVE_NO_ROOT_DIR;
1414 status = NtQueryVolumeInformationFile( handle, &io, &info, sizeof(info), FileFsDeviceInformation );
1415 NtClose( handle );
1416 if (status != STATUS_SUCCESS)
1418 SetLastError( RtlNtStatusToDosError(status) );
1419 ret = DRIVE_UNKNOWN;
1421 else
1423 switch (info.DeviceType)
1425 case FILE_DEVICE_CD_ROM_FILE_SYSTEM: ret = DRIVE_CDROM; break;
1426 case FILE_DEVICE_VIRTUAL_DISK: ret = DRIVE_RAMDISK; break;
1427 case FILE_DEVICE_NETWORK_FILE_SYSTEM: ret = DRIVE_REMOTE; break;
1428 case FILE_DEVICE_DISK_FILE_SYSTEM:
1429 if (info.Characteristics & FILE_REMOTE_DEVICE) ret = DRIVE_REMOTE;
1430 else if (info.Characteristics & FILE_REMOVABLE_MEDIA) ret = DRIVE_REMOVABLE;
1431 else if ((ret = get_mountmgr_drive_type( root )) == DRIVE_UNKNOWN) ret = DRIVE_FIXED;
1432 break;
1433 default:
1434 ret = DRIVE_UNKNOWN;
1435 break;
1438 TRACE( "%s -> %d\n", debugstr_w(root), ret );
1439 return ret;
1443 /***********************************************************************
1444 * GetDriveTypeA (KERNEL32.@)
1446 * See GetDriveTypeW.
1448 UINT WINAPI GetDriveTypeA( LPCSTR root )
1450 WCHAR *rootW = NULL;
1452 if (root && !(rootW = FILE_name_AtoW( root, FALSE ))) return DRIVE_NO_ROOT_DIR;
1453 return GetDriveTypeW( rootW );
1457 /***********************************************************************
1458 * GetDiskFreeSpaceExW (KERNEL32.@)
1460 * This function is used to acquire the size of the available and
1461 * total space on a logical volume.
1463 * RETURNS
1465 * Zero on failure, nonzero upon success. Use GetLastError to obtain
1466 * detailed error information.
1469 BOOL WINAPI GetDiskFreeSpaceExW( LPCWSTR root, PULARGE_INTEGER avail,
1470 PULARGE_INTEGER total, PULARGE_INTEGER totalfree )
1472 FILE_FS_SIZE_INFORMATION info;
1473 IO_STATUS_BLOCK io;
1474 NTSTATUS status;
1475 HANDLE handle;
1476 UINT units;
1478 TRACE( "%s,%p,%p,%p\n", debugstr_w(root), avail, total, totalfree );
1480 if (!open_device_root( root, &handle )) return FALSE;
1482 status = NtQueryVolumeInformationFile( handle, &io, &info, sizeof(info), FileFsSizeInformation );
1483 NtClose( handle );
1484 if (!set_ntstatus( status )) return FALSE;
1486 units = info.SectorsPerAllocationUnit * info.BytesPerSector;
1487 if (total) total->QuadPart = info.TotalAllocationUnits.QuadPart * units;
1488 if (totalfree) totalfree->QuadPart = info.AvailableAllocationUnits.QuadPart * units;
1489 /* FIXME: this one should take quotas into account */
1490 if (avail) avail->QuadPart = info.AvailableAllocationUnits.QuadPart * units;
1491 return TRUE;
1495 /***********************************************************************
1496 * GetDiskFreeSpaceExA (KERNEL32.@)
1498 * See GetDiskFreeSpaceExW.
1500 BOOL WINAPI GetDiskFreeSpaceExA( LPCSTR root, PULARGE_INTEGER avail,
1501 PULARGE_INTEGER total, PULARGE_INTEGER totalfree )
1503 WCHAR *rootW = NULL;
1505 if (root && !(rootW = FILE_name_AtoW( root, FALSE ))) return FALSE;
1506 return GetDiskFreeSpaceExW( rootW, avail, total, totalfree );
1510 /***********************************************************************
1511 * GetDiskFreeSpaceW (KERNEL32.@)
1513 BOOL WINAPI GetDiskFreeSpaceW( LPCWSTR root, LPDWORD cluster_sectors,
1514 LPDWORD sector_bytes, LPDWORD free_clusters,
1515 LPDWORD total_clusters )
1517 FILE_FS_SIZE_INFORMATION info;
1518 IO_STATUS_BLOCK io;
1519 NTSTATUS status;
1520 HANDLE handle;
1521 UINT units;
1523 TRACE( "%s,%p,%p,%p,%p\n", debugstr_w(root),
1524 cluster_sectors, sector_bytes, free_clusters, total_clusters );
1526 if (!open_device_root( root, &handle )) return FALSE;
1528 status = NtQueryVolumeInformationFile( handle, &io, &info, sizeof(info), FileFsSizeInformation );
1529 NtClose( handle );
1530 if (!set_ntstatus( status )) return FALSE;
1532 units = info.SectorsPerAllocationUnit * info.BytesPerSector;
1534 if( GetVersion() & 0x80000000) { /* win3.x, 9x, ME */
1535 /* cap the size and available at 2GB as per specs */
1536 if (info.TotalAllocationUnits.QuadPart * units > 0x7fffffff) {
1537 info.TotalAllocationUnits.QuadPart = 0x7fffffff / units;
1538 if (info.AvailableAllocationUnits.QuadPart * units > 0x7fffffff)
1539 info.AvailableAllocationUnits.QuadPart = 0x7fffffff / units;
1541 /* nr. of clusters is always <= 65335 */
1542 while( info.TotalAllocationUnits.QuadPart > 65535 ) {
1543 info.TotalAllocationUnits.QuadPart /= 2;
1544 info.AvailableAllocationUnits.QuadPart /= 2;
1545 info.SectorsPerAllocationUnit *= 2;
1549 if (cluster_sectors) *cluster_sectors = info.SectorsPerAllocationUnit;
1550 if (sector_bytes) *sector_bytes = info.BytesPerSector;
1551 if (free_clusters) *free_clusters = info.AvailableAllocationUnits.u.LowPart;
1552 if (total_clusters) *total_clusters = info.TotalAllocationUnits.u.LowPart;
1553 TRACE("%#08x, %#08x, %#08x, %#08x\n", info.SectorsPerAllocationUnit, info.BytesPerSector,
1554 info.AvailableAllocationUnits.u.LowPart, info.TotalAllocationUnits.u.LowPart);
1555 return TRUE;
1559 /***********************************************************************
1560 * GetDiskFreeSpaceA (KERNEL32.@)
1562 BOOL WINAPI GetDiskFreeSpaceA( LPCSTR root, LPDWORD cluster_sectors,
1563 LPDWORD sector_bytes, LPDWORD free_clusters,
1564 LPDWORD total_clusters )
1566 WCHAR *rootW = NULL;
1568 if (root && !(rootW = FILE_name_AtoW( root, FALSE ))) return FALSE;
1569 return GetDiskFreeSpaceW( rootW, cluster_sectors, sector_bytes, free_clusters, total_clusters );
1572 /***********************************************************************
1573 * GetVolumePathNameA (KERNEL32.@)
1575 BOOL WINAPI GetVolumePathNameA(LPCSTR filename, LPSTR volumepathname, DWORD buflen)
1577 BOOL ret;
1578 WCHAR *filenameW = NULL, *volumeW = NULL;
1580 TRACE("(%s, %p, %d)\n", debugstr_a(filename), volumepathname, buflen);
1582 if (filename && !(filenameW = FILE_name_AtoW( filename, FALSE )))
1583 return FALSE;
1584 if (volumepathname && !(volumeW = HeapAlloc( GetProcessHeap(), 0, buflen * sizeof(WCHAR) )))
1585 return FALSE;
1587 if ((ret = GetVolumePathNameW( filenameW, volumeW, buflen )))
1588 FILE_name_WtoA( volumeW, -1, volumepathname, buflen );
1590 HeapFree( GetProcessHeap(), 0, volumeW );
1591 return ret;
1594 static BOOL is_dos_path( const UNICODE_STRING *path )
1596 static const WCHAR global_prefix[4] = {'\\','?','?','\\'};
1597 return path->Length >= 7 * sizeof(WCHAR)
1598 && !memcmp(path->Buffer, global_prefix, sizeof(global_prefix))
1599 && path->Buffer[5] == ':' && path->Buffer[6] == '\\';
1602 /* resolve all symlinks in a path in-place; return FALSE if allocation failed */
1603 static BOOL resolve_symlink( UNICODE_STRING *path )
1605 OBJECT_NAME_INFORMATION *info;
1606 OBJECT_ATTRIBUTES attr;
1607 IO_STATUS_BLOCK io;
1608 NTSTATUS status;
1609 HANDLE file;
1610 ULONG size;
1612 InitializeObjectAttributes( &attr, path, OBJ_CASE_INSENSITIVE, 0, NULL );
1613 if (NtOpenFile( &file, SYNCHRONIZE, &attr, &io, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1614 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT ))
1615 return TRUE;
1617 if (NtQueryObject( file, ObjectNameInformation, NULL, 0, &size ) != STATUS_INFO_LENGTH_MISMATCH)
1619 NtClose( file );
1620 return TRUE;
1623 if (!(info = HeapAlloc( GetProcessHeap(), 0, size )))
1625 NtClose( file );
1626 return FALSE;
1629 status = NtQueryObject( file, ObjectNameInformation, info, size, NULL );
1630 NtClose( file );
1631 if (status)
1632 return TRUE;
1634 RtlFreeUnicodeString( path );
1635 status = RtlDuplicateUnicodeString( 0, &info->Name, path );
1636 HeapFree( GetProcessHeap(), 0, info );
1637 return !status;
1640 /***********************************************************************
1641 * GetVolumePathNameW (KERNEL32.@)
1643 BOOL WINAPI GetVolumePathNameW(const WCHAR *path, WCHAR *volume_path, DWORD length)
1645 static const WCHAR device_prefix[4] = {'\\','\\','.','\\'};
1646 static const WCHAR device_prefix2[4] = {'\\','\\','?','\\'};
1647 static const WCHAR global_prefix[4] = {'\\','?','?','\\'};
1648 FILE_ATTRIBUTE_TAG_INFORMATION attr_info;
1649 FILE_BASIC_INFORMATION basic_info;
1650 OBJECT_ATTRIBUTES attr;
1651 UNICODE_STRING nt_name;
1652 NTSTATUS status;
1654 if (path && !memcmp(path, global_prefix, sizeof(global_prefix)))
1656 WCHAR current_drive[MAX_PATH];
1658 GetCurrentDirectoryW( ARRAY_SIZE(current_drive), current_drive );
1659 if (length >= 3)
1661 WCHAR ret_path[4] = {current_drive[0], ':', '\\', 0};
1662 lstrcpynW( volume_path, ret_path, length );
1663 return TRUE;
1666 SetLastError( ERROR_FILENAME_EXCED_RANGE );
1667 return FALSE;
1670 if (!volume_path || !length || !RtlDosPathNameToNtPathName_U( path, &nt_name, NULL, NULL ))
1672 SetLastError( ERROR_INVALID_PARAMETER );
1673 return FALSE;
1676 if (!is_dos_path( &nt_name ))
1678 RtlFreeUnicodeString( &nt_name );
1679 WARN("invalid path %s\n", debugstr_w(path));
1680 SetLastError( ERROR_INVALID_NAME );
1681 return FALSE;
1684 InitializeObjectAttributes( &attr, &nt_name, OBJ_CASE_INSENSITIVE, 0, NULL );
1686 while (nt_name.Length > 7 * sizeof(WCHAR))
1688 IO_STATUS_BLOCK io;
1689 HANDLE file;
1691 if (!NtQueryAttributesFile( &attr, &basic_info )
1692 && (basic_info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
1693 && (basic_info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1694 && !NtOpenFile( &file, SYNCHRONIZE | FILE_READ_ATTRIBUTES, &attr, &io,
1695 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1696 FILE_OPEN_REPARSE_POINT | FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT ))
1698 status = NtQueryInformationFile( file, &io, &attr_info,
1699 sizeof(attr_info), FileAttributeTagInformation );
1700 NtClose( file );
1701 if (!status)
1704 if (attr_info.ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
1705 break;
1707 if (!resolve_symlink( &nt_name ))
1709 SetLastError( ERROR_OUTOFMEMORY );
1710 return FALSE;
1715 if (nt_name.Buffer[(nt_name.Length / sizeof(WCHAR)) - 1] == '\\')
1716 nt_name.Length -= sizeof(WCHAR);
1717 while (nt_name.Length && nt_name.Buffer[(nt_name.Length / sizeof(WCHAR)) - 1] != '\\')
1718 nt_name.Length -= sizeof(WCHAR);
1721 nt_name.Buffer[nt_name.Length / sizeof(WCHAR)] = 0;
1723 if (NtQueryAttributesFile( &attr, &basic_info ))
1725 RtlFreeUnicodeString( &nt_name );
1726 WARN("nonexistent path %s -> %s\n", debugstr_w(path), debugstr_w( nt_name.Buffer ));
1727 SetLastError( ERROR_FILE_NOT_FOUND );
1728 return FALSE;
1731 if (!memcmp(path, device_prefix, sizeof(device_prefix))
1732 || !memcmp(path, device_prefix2, sizeof(device_prefix2)))
1734 if (length >= nt_name.Length / sizeof(WCHAR))
1736 memcpy(volume_path, path, 4 * sizeof(WCHAR));
1737 lstrcpynW( volume_path + 4, nt_name.Buffer + 4, length - 4 );
1739 TRACE("%s -> %s\n", debugstr_w(path), debugstr_w(volume_path));
1741 RtlFreeUnicodeString( &nt_name );
1742 return TRUE;
1745 else if (length >= (nt_name.Length / sizeof(WCHAR)) - 4)
1747 lstrcpynW( volume_path, nt_name.Buffer + 4, length );
1748 volume_path[0] = toupperW(volume_path[0]);
1750 TRACE("%s -> %s\n", debugstr_w(path), debugstr_w(volume_path));
1752 RtlFreeUnicodeString( &nt_name );
1753 return TRUE;
1756 RtlFreeUnicodeString( &nt_name );
1757 SetLastError( ERROR_FILENAME_EXCED_RANGE );
1758 return FALSE;
1762 /***********************************************************************
1763 * GetVolumePathNamesForVolumeNameA (KERNEL32.@)
1765 BOOL WINAPI GetVolumePathNamesForVolumeNameA(LPCSTR volumename, LPSTR volumepathname, DWORD buflen, PDWORD returnlen)
1767 BOOL ret;
1768 WCHAR *volumenameW = NULL, *volumepathnameW;
1770 if (volumename && !(volumenameW = FILE_name_AtoW( volumename, TRUE ))) return FALSE;
1771 if (!(volumepathnameW = HeapAlloc( GetProcessHeap(), 0, buflen * sizeof(WCHAR) )))
1773 HeapFree( GetProcessHeap(), 0, volumenameW );
1774 return FALSE;
1776 if ((ret = GetVolumePathNamesForVolumeNameW( volumenameW, volumepathnameW, buflen, returnlen )))
1778 char *path = volumepathname;
1779 const WCHAR *pathW = volumepathnameW;
1781 while (*pathW)
1783 int len = strlenW( pathW ) + 1;
1784 FILE_name_WtoA( pathW, len, path, buflen );
1785 buflen -= len;
1786 pathW += len;
1787 path += len;
1789 path[0] = 0;
1791 HeapFree( GetProcessHeap(), 0, volumenameW );
1792 HeapFree( GetProcessHeap(), 0, volumepathnameW );
1793 return ret;
1796 static MOUNTMGR_MOUNT_POINTS *query_mount_points( HANDLE mgr, MOUNTMGR_MOUNT_POINT *input, DWORD insize )
1798 MOUNTMGR_MOUNT_POINTS *output;
1799 DWORD outsize = 1024;
1800 DWORD br;
1802 for (;;)
1804 if (!(output = HeapAlloc( GetProcessHeap(), 0, outsize )))
1806 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1807 return NULL;
1809 if (DeviceIoControl( mgr, IOCTL_MOUNTMGR_QUERY_POINTS, input, insize, output, outsize, &br, NULL )) break;
1810 outsize = output->Size;
1811 HeapFree( GetProcessHeap(), 0, output );
1812 if (GetLastError() != ERROR_MORE_DATA) return NULL;
1814 return output;
1816 /***********************************************************************
1817 * GetVolumePathNamesForVolumeNameW (KERNEL32.@)
1819 BOOL WINAPI GetVolumePathNamesForVolumeNameW(LPCWSTR volumename, LPWSTR volumepathname, DWORD buflen, PDWORD returnlen)
1821 static const WCHAR dosdevicesW[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\'};
1822 HANDLE mgr;
1823 DWORD len, size;
1824 MOUNTMGR_MOUNT_POINT *spec;
1825 MOUNTMGR_MOUNT_POINTS *link, *target = NULL;
1826 WCHAR *name, *path;
1827 BOOL ret = FALSE;
1828 UINT i, j;
1830 TRACE("%s, %p, %u, %p\n", debugstr_w(volumename), volumepathname, buflen, returnlen);
1832 if (!volumename || (len = strlenW( volumename )) != 49)
1834 SetLastError( ERROR_INVALID_NAME );
1835 return FALSE;
1837 mgr = CreateFileW( MOUNTMGR_DOS_DEVICE_NAME, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0 );
1838 if (mgr == INVALID_HANDLE_VALUE) return FALSE;
1840 size = sizeof(*spec) + sizeof(WCHAR) * (len - 1); /* remove trailing backslash */
1841 if (!(spec = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, size ))) goto done;
1842 spec->SymbolicLinkNameOffset = sizeof(*spec);
1843 spec->SymbolicLinkNameLength = size - sizeof(*spec);
1844 name = (WCHAR *)((char *)spec + spec->SymbolicLinkNameOffset);
1845 memcpy( name, volumename, size - sizeof(*spec) );
1846 name[1] = '?'; /* map \\?\ to \??\ */
1848 target = query_mount_points( mgr, spec, size );
1849 HeapFree( GetProcessHeap(), 0, spec );
1850 if (!target)
1852 goto done;
1854 if (!target->NumberOfMountPoints)
1856 SetLastError( ERROR_FILE_NOT_FOUND );
1857 goto done;
1859 len = 0;
1860 path = volumepathname;
1861 for (i = 0; i < target->NumberOfMountPoints; i++)
1863 link = NULL;
1864 if (target->MountPoints[i].DeviceNameOffset)
1866 const WCHAR *device = (const WCHAR *)((const char *)target + target->MountPoints[i].DeviceNameOffset);
1867 USHORT device_len = target->MountPoints[i].DeviceNameLength;
1869 size = sizeof(*spec) + device_len;
1870 if (!(spec = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, size ))) goto done;
1871 spec->DeviceNameOffset = sizeof(*spec);
1872 spec->DeviceNameLength = device_len;
1873 memcpy( (char *)spec + spec->DeviceNameOffset, device, device_len );
1875 link = query_mount_points( mgr, spec, size );
1876 HeapFree( GetProcessHeap(), 0, spec );
1878 else if (target->MountPoints[i].UniqueIdOffset)
1880 const WCHAR *id = (const WCHAR *)((const char *)target + target->MountPoints[i].UniqueIdOffset);
1881 USHORT id_len = target->MountPoints[i].UniqueIdLength;
1883 size = sizeof(*spec) + id_len;
1884 if (!(spec = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, size ))) goto done;
1885 spec->UniqueIdOffset = sizeof(*spec);
1886 spec->UniqueIdLength = id_len;
1887 memcpy( (char *)spec + spec->UniqueIdOffset, id, id_len );
1889 link = query_mount_points( mgr, spec, size );
1890 HeapFree( GetProcessHeap(), 0, spec );
1892 if (!link) continue;
1893 for (j = 0; j < link->NumberOfMountPoints; j++)
1895 const WCHAR *linkname;
1897 if (!link->MountPoints[j].SymbolicLinkNameOffset) continue;
1898 linkname = (const WCHAR *)((const char *)link + link->MountPoints[j].SymbolicLinkNameOffset);
1900 if (link->MountPoints[j].SymbolicLinkNameLength == sizeof(dosdevicesW) + 2 * sizeof(WCHAR) &&
1901 !strncmpiW( linkname, dosdevicesW, ARRAY_SIZE( dosdevicesW )))
1903 len += 4;
1904 if (volumepathname && len < buflen)
1906 path[0] = linkname[ARRAY_SIZE( dosdevicesW )];
1907 path[1] = ':';
1908 path[2] = '\\';
1909 path[3] = 0;
1910 path += 4;
1914 HeapFree( GetProcessHeap(), 0, link );
1916 if (buflen <= len) SetLastError( ERROR_MORE_DATA );
1917 else if (volumepathname)
1919 volumepathname[len] = 0;
1920 ret = TRUE;
1922 if (returnlen) *returnlen = len + 1;
1924 done:
1925 HeapFree( GetProcessHeap(), 0, target );
1926 CloseHandle( mgr );
1927 return ret;
1930 /***********************************************************************
1931 * FindFirstVolumeA (KERNEL32.@)
1933 HANDLE WINAPI FindFirstVolumeA(LPSTR volume, DWORD len)
1935 WCHAR *buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
1936 HANDLE handle = FindFirstVolumeW( buffer, len );
1938 if (handle != INVALID_HANDLE_VALUE)
1940 if (!WideCharToMultiByte( CP_ACP, 0, buffer, -1, volume, len, NULL, NULL ))
1942 FindVolumeClose( handle );
1943 handle = INVALID_HANDLE_VALUE;
1946 HeapFree( GetProcessHeap(), 0, buffer );
1947 return handle;
1950 /***********************************************************************
1951 * FindFirstVolumeW (KERNEL32.@)
1953 HANDLE WINAPI FindFirstVolumeW( LPWSTR volume, DWORD len )
1955 DWORD size = 1024;
1956 DWORD br;
1957 HANDLE mgr = CreateFileW( MOUNTMGR_DOS_DEVICE_NAME, 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
1958 NULL, OPEN_EXISTING, 0, 0 );
1959 if (mgr == INVALID_HANDLE_VALUE) return INVALID_HANDLE_VALUE;
1961 for (;;)
1963 MOUNTMGR_MOUNT_POINT input;
1964 MOUNTMGR_MOUNT_POINTS *output;
1966 if (!(output = HeapAlloc( GetProcessHeap(), 0, size )))
1968 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1969 break;
1971 memset( &input, 0, sizeof(input) );
1973 if (!DeviceIoControl( mgr, IOCTL_MOUNTMGR_QUERY_POINTS, &input, sizeof(input),
1974 output, size, &br, NULL ))
1976 if (GetLastError() != ERROR_MORE_DATA) break;
1977 size = output->Size;
1978 HeapFree( GetProcessHeap(), 0, output );
1979 continue;
1981 CloseHandle( mgr );
1982 /* abuse the Size field to store the current index */
1983 output->Size = 0;
1984 if (!FindNextVolumeW( output, volume, len ))
1986 HeapFree( GetProcessHeap(), 0, output );
1987 return INVALID_HANDLE_VALUE;
1989 return output;
1991 CloseHandle( mgr );
1992 return INVALID_HANDLE_VALUE;
1995 /***********************************************************************
1996 * FindNextVolumeA (KERNEL32.@)
1998 BOOL WINAPI FindNextVolumeA( HANDLE handle, LPSTR volume, DWORD len )
2000 WCHAR *buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
2001 BOOL ret;
2003 if ((ret = FindNextVolumeW( handle, buffer, len )))
2005 if (!WideCharToMultiByte( CP_ACP, 0, buffer, -1, volume, len, NULL, NULL )) ret = FALSE;
2007 HeapFree( GetProcessHeap(), 0, buffer );
2008 return ret;
2011 /***********************************************************************
2012 * FindNextVolumeW (KERNEL32.@)
2014 BOOL WINAPI FindNextVolumeW( HANDLE handle, LPWSTR volume, DWORD len )
2016 MOUNTMGR_MOUNT_POINTS *data = handle;
2018 while (data->Size < data->NumberOfMountPoints)
2020 static const WCHAR volumeW[] = {'\\','?','?','\\','V','o','l','u','m','e','{',};
2021 WCHAR *link = (WCHAR *)((char *)data + data->MountPoints[data->Size].SymbolicLinkNameOffset);
2022 DWORD size = data->MountPoints[data->Size].SymbolicLinkNameLength;
2023 data->Size++;
2024 /* skip non-volumes */
2025 if (size < sizeof(volumeW) || memcmp( link, volumeW, sizeof(volumeW) )) continue;
2026 if (size + sizeof(WCHAR) >= len * sizeof(WCHAR))
2028 SetLastError( ERROR_FILENAME_EXCED_RANGE );
2029 return FALSE;
2031 memcpy( volume, link, size );
2032 volume[1] = '\\'; /* map \??\ to \\?\ */
2033 volume[size / sizeof(WCHAR)] = '\\'; /* Windows appends a backslash */
2034 volume[size / sizeof(WCHAR) + 1] = 0;
2035 TRACE( "returning entry %u %s\n", data->Size - 1, debugstr_w(volume) );
2036 return TRUE;
2038 SetLastError( ERROR_NO_MORE_FILES );
2039 return FALSE;
2042 /***********************************************************************
2043 * FindVolumeClose (KERNEL32.@)
2045 BOOL WINAPI FindVolumeClose(HANDLE handle)
2047 return HeapFree( GetProcessHeap(), 0, handle );
2050 /***********************************************************************
2051 * FindFirstVolumeMountPointA (KERNEL32.@)
2053 HANDLE WINAPI FindFirstVolumeMountPointA(LPCSTR root, LPSTR mount_point, DWORD len)
2055 FIXME("(%s, %p, %d), stub!\n", debugstr_a(root), mount_point, len);
2056 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2057 return INVALID_HANDLE_VALUE;
2060 /***********************************************************************
2061 * FindFirstVolumeMountPointW (KERNEL32.@)
2063 HANDLE WINAPI FindFirstVolumeMountPointW(LPCWSTR root, LPWSTR mount_point, DWORD len)
2065 FIXME("(%s, %p, %d), stub!\n", debugstr_w(root), mount_point, len);
2066 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2067 return INVALID_HANDLE_VALUE;
2070 /***********************************************************************
2071 * FindVolumeMountPointClose (KERNEL32.@)
2073 BOOL WINAPI FindVolumeMountPointClose(HANDLE h)
2075 FIXME("(%p), stub!\n", h);
2076 return FALSE;
2079 /***********************************************************************
2080 * DeleteVolumeMountPointA (KERNEL32.@)
2082 BOOL WINAPI DeleteVolumeMountPointA(LPCSTR mountpoint)
2084 FIXME("(%s), stub!\n", debugstr_a(mountpoint));
2085 return FALSE;
2088 /***********************************************************************
2089 * DeleteVolumeMountPointW (KERNEL32.@)
2091 BOOL WINAPI DeleteVolumeMountPointW(LPCWSTR mountpoint)
2093 FIXME("(%s), stub!\n", debugstr_w(mountpoint));
2094 return FALSE;
2097 /***********************************************************************
2098 * SetVolumeMountPointA (KERNEL32.@)
2100 BOOL WINAPI SetVolumeMountPointA(LPCSTR path, LPCSTR volume)
2102 FIXME("(%s, %s), stub!\n", debugstr_a(path), debugstr_a(volume));
2103 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2104 return FALSE;
2107 /***********************************************************************
2108 * SetVolumeMountPointW (KERNEL32.@)
2110 BOOL WINAPI SetVolumeMountPointW(LPCWSTR path, LPCWSTR volume)
2112 FIXME("(%s, %s), stub!\n", debugstr_w(path), debugstr_w(volume));
2113 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2114 return FALSE;
2117 /***********************************************************************
2118 * GetVolumeInformationByHandleW (KERNEL32.@)
2120 BOOL WINAPI GetVolumeInformationByHandleW(HANDLE handle, WCHAR *volnamebuf, DWORD volnamesize, DWORD *volserial, DWORD *maxlength, DWORD *flags, WCHAR *fsnamebuf, DWORD fsnamesize)
2122 FIXME("%p %p %d %p %p %p %p %d\n", handle, volnamebuf, volnamesize, volserial, maxlength, flags, fsnamebuf, fsnamesize);
2124 if(volnamebuf && volnamesize)
2125 *volnamebuf = 0;
2126 if(volserial)
2127 *volserial = 0;
2128 if(maxlength)
2129 *maxlength = 0;
2130 if(flags)
2131 *flags = 0;
2132 if(fsnamebuf && fsnamesize)
2133 *fsnamebuf = 0;
2135 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2136 return FALSE;