kernel32: Return the Windows symlink in QueryDosDevice instead of the Unix one.
[wine] / dlls / kernel32 / volume.c
1 /*
2  * Volume management functions
3  *
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
9  *
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.
14  *
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.
19  *
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
23  */
24
25 #include "config.h"
26 #include "wine/port.h"
27
28 #include <stdarg.h>
29 #include <stdlib.h>
30 #include <stdio.h>
31
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 #include "ddk/mountmgr.h"
41 #include "kernel_private.h"
42 #include "wine/library.h"
43 #include "wine/unicode.h"
44 #include "wine/debug.h"
45
46 WINE_DEFAULT_DEBUG_CHANNEL(volume);
47
48 #define SUPERBLOCK_SIZE 2048
49 #define SYMBOLIC_LINK_QUERY 0x0001
50
51 #define CDFRAMES_PERSEC         75
52 #define CDFRAMES_PERMIN         (CDFRAMES_PERSEC * 60)
53 #define FRAME_OF_ADDR(a)        ((a)[1] * CDFRAMES_PERMIN + (a)[2] * CDFRAMES_PERSEC + (a)[3])
54 #define FRAME_OF_TOC(toc, idx)  FRAME_OF_ADDR((toc)->TrackData[(idx) - (toc)->FirstTrack].Address)
55
56 #define GETWORD(buf,off)  MAKEWORD(buf[(off)],buf[(off+1)])
57 #define GETLONG(buf,off)  MAKELONG(GETWORD(buf,off),GETWORD(buf,off+2))
58
59 enum fs_type
60 {
61     FS_ERROR,    /* error accessing the device */
62     FS_UNKNOWN,  /* unknown file system */
63     FS_FAT1216,
64     FS_FAT32,
65     FS_ISO9660
66 };
67
68 static const WCHAR drive_types[][8] =
69 {
70     { 0 }, /* DRIVE_UNKNOWN */
71     { 0 }, /* DRIVE_NO_ROOT_DIR */
72     {'f','l','o','p','p','y',0}, /* DRIVE_REMOVABLE */
73     {'h','d',0}, /* DRIVE_FIXED */
74     {'n','e','t','w','o','r','k',0}, /* DRIVE_REMOTE */
75     {'c','d','r','o','m',0}, /* DRIVE_CDROM */
76     {'r','a','m','d','i','s','k',0} /* DRIVE_RAMDISK */
77 };
78
79 /* read a Unix symlink; returned buffer must be freed by caller */
80 static char *read_symlink( const char *path )
81 {
82     char *buffer;
83     int ret, size = 128;
84
85     for (;;)
86     {
87         if (!(buffer = HeapAlloc( GetProcessHeap(), 0, size )))
88         {
89             SetLastError( ERROR_NOT_ENOUGH_MEMORY );
90             return 0;
91         }
92         ret = readlink( path, buffer, size );
93         if (ret == -1)
94         {
95             FILE_SetDosError();
96             HeapFree( GetProcessHeap(), 0, buffer );
97             return 0;
98         }
99         if (ret != size)
100         {
101             buffer[ret] = 0;
102             return buffer;
103         }
104         HeapFree( GetProcessHeap(), 0, buffer );
105         size *= 2;
106     }
107 }
108
109 /* get the path of a dos device symlink in the $WINEPREFIX/dosdevices directory */
110 static char *get_dos_device_path( LPCWSTR name )
111 {
112     const char *config_dir = wine_get_config_dir();
113     char *buffer, *dev;
114     int i;
115
116     if (!(buffer = HeapAlloc( GetProcessHeap(), 0,
117                               strlen(config_dir) + sizeof("/dosdevices/") + 5 )))
118     {
119         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
120         return NULL;
121     }
122     strcpy( buffer, config_dir );
123     strcat( buffer, "/dosdevices/" );
124     dev = buffer + strlen(buffer);
125     /* no codepage conversion, DOS device names are ASCII anyway */
126     for (i = 0; i < 5; i++)
127         if (!(dev[i] = (char)tolowerW(name[i]))) break;
128     dev[5] = 0;
129     return buffer;
130 }
131
132 /* read the contents of an NT symlink object */
133 static NTSTATUS read_nt_symlink( const WCHAR *name, WCHAR *target, DWORD size )
134 {
135     NTSTATUS status;
136     OBJECT_ATTRIBUTES attr;
137     UNICODE_STRING nameW;
138     HANDLE handle;
139
140     attr.Length = sizeof(attr);
141     attr.RootDirectory = 0;
142     attr.Attributes = OBJ_CASE_INSENSITIVE;
143     attr.ObjectName = &nameW;
144     attr.SecurityDescriptor = NULL;
145     attr.SecurityQualityOfService = NULL;
146     RtlInitUnicodeString( &nameW, name );
147
148     if (!(status = NtOpenSymbolicLinkObject( &handle, SYMBOLIC_LINK_QUERY, &attr )))
149     {
150         UNICODE_STRING targetW;
151         targetW.Buffer = target;
152         targetW.MaximumLength = (size - 1) * sizeof(WCHAR);
153         status = NtQuerySymbolicLinkObject( handle, &targetW, NULL );
154         if (!status) target[targetW.Length / sizeof(WCHAR)] = 0;
155         NtClose( handle );
156     }
157     return status;
158 }
159
160 /* open a handle to a device root */
161 static BOOL open_device_root( LPCWSTR root, HANDLE *handle )
162 {
163     static const WCHAR default_rootW[] = {'\\',0};
164     UNICODE_STRING nt_name;
165     OBJECT_ATTRIBUTES attr;
166     IO_STATUS_BLOCK io;
167     NTSTATUS status;
168
169     if (!root) root = default_rootW;
170     if (!RtlDosPathNameToNtPathName_U( root, &nt_name, NULL, NULL ))
171     {
172         SetLastError( ERROR_PATH_NOT_FOUND );
173         return FALSE;
174     }
175     attr.Length = sizeof(attr);
176     attr.RootDirectory = 0;
177     attr.Attributes = OBJ_CASE_INSENSITIVE;
178     attr.ObjectName = &nt_name;
179     attr.SecurityDescriptor = NULL;
180     attr.SecurityQualityOfService = NULL;
181
182     status = NtOpenFile( handle, 0, &attr, &io, 0,
183                          FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT );
184     RtlFreeUnicodeString( &nt_name );
185     if (status != STATUS_SUCCESS)
186     {
187         SetLastError( RtlNtStatusToDosError(status) );
188         return FALSE;
189     }
190     return TRUE;
191 }
192
193 /* get the label by reading it from a file at the root of the filesystem */
194 static void get_filesystem_label( const WCHAR *device, WCHAR *label, DWORD len )
195 {
196     HANDLE handle;
197     WCHAR labelW[] = {'A',':','\\','.','w','i','n','d','o','w','s','-','l','a','b','e','l',0};
198
199     labelW[0] = device[4];
200     handle = CreateFileW( labelW, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
201                           OPEN_EXISTING, 0, 0 );
202     if (handle != INVALID_HANDLE_VALUE)
203     {
204         char buffer[256], *p;
205         DWORD size;
206
207         if (!ReadFile( handle, buffer, sizeof(buffer)-1, &size, NULL )) size = 0;
208         CloseHandle( handle );
209         p = buffer + size;
210         while (p > buffer && (p[-1] == ' ' || p[-1] == '\r' || p[-1] == '\n')) p--;
211         *p = 0;
212         if (!MultiByteToWideChar( CP_UNIXCP, 0, buffer, -1, label, len ))
213             label[len-1] = 0;
214     }
215     else label[0] = 0;
216 }
217
218 /* get the serial number by reading it from a file at the root of the filesystem */
219 static DWORD get_filesystem_serial( const WCHAR *device )
220 {
221     HANDLE handle;
222     WCHAR serialW[] = {'A',':','\\','.','w','i','n','d','o','w','s','-','s','e','r','i','a','l',0};
223
224     serialW[0] = device[4];
225     handle = CreateFileW( serialW, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
226                           OPEN_EXISTING, 0, 0 );
227     if (handle != INVALID_HANDLE_VALUE)
228     {
229         char buffer[32];
230         DWORD size;
231
232         if (!ReadFile( handle, buffer, sizeof(buffer)-1, &size, NULL )) size = 0;
233         CloseHandle( handle );
234         buffer[size] = 0;
235         return strtoul( buffer, NULL, 16 );
236     }
237     else return 0;
238 }
239
240 /* fetch the type of a drive from the registry */
241 static UINT get_registry_drive_type( const WCHAR *root )
242 {
243     static const WCHAR drive_types_keyW[] = {'M','a','c','h','i','n','e','\\',
244                                              'S','o','f','t','w','a','r','e','\\',
245                                              'W','i','n','e','\\',
246                                              'D','r','i','v','e','s',0 };
247     OBJECT_ATTRIBUTES attr;
248     UNICODE_STRING nameW;
249     HANDLE hkey;
250     DWORD dummy;
251     UINT ret = DRIVE_UNKNOWN;
252     char tmp[32 + sizeof(KEY_VALUE_PARTIAL_INFORMATION)];
253     WCHAR driveW[] = {'A',':',0};
254
255     attr.Length = sizeof(attr);
256     attr.RootDirectory = 0;
257     attr.ObjectName = &nameW;
258     attr.Attributes = 0;
259     attr.SecurityDescriptor = NULL;
260     attr.SecurityQualityOfService = NULL;
261     RtlInitUnicodeString( &nameW, drive_types_keyW );
262     /* @@ Wine registry key: HKLM\Software\Wine\Drives */
263     if (NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ) != STATUS_SUCCESS) return DRIVE_UNKNOWN;
264
265     if (root) driveW[0] = root[0];
266     else
267     {
268         WCHAR path[MAX_PATH];
269         GetCurrentDirectoryW( MAX_PATH, path );
270         driveW[0] = path[0];
271     }
272
273     RtlInitUnicodeString( &nameW, driveW );
274     if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
275     {
276         unsigned int i;
277         WCHAR *data = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
278
279         for (i = 0; i < sizeof(drive_types)/sizeof(drive_types[0]); i++)
280         {
281             if (!strcmpiW( data, drive_types[i] ))
282             {
283                 ret = i;
284                 break;
285             }
286         }
287     }
288     NtClose( hkey );
289     return ret;
290 }
291
292
293 /******************************************************************
294  *              VOLUME_FindCdRomDataBestVoldesc
295  */
296 static DWORD VOLUME_FindCdRomDataBestVoldesc( HANDLE handle )
297 {
298     BYTE cur_vd_type, max_vd_type = 0;
299     BYTE buffer[0x800];
300     DWORD size, offs, best_offs = 0, extra_offs = 0;
301
302     for (offs = 0x8000; offs <= 0x9800; offs += 0x800)
303     {
304         /* if 'CDROM' occurs at position 8, this is a pre-iso9660 cd, and
305          * the volume label is displaced forward by 8
306          */
307         if (SetFilePointer( handle, offs, NULL, FILE_BEGIN ) != offs) break;
308         if (!ReadFile( handle, buffer, sizeof(buffer), &size, NULL )) break;
309         if (size != sizeof(buffer)) break;
310         /* check for non-ISO9660 signature */
311         if (!memcmp( buffer + 11, "ROM", 3 )) extra_offs = 8;
312         cur_vd_type = buffer[extra_offs];
313         if (cur_vd_type == 0xff) /* voldesc set terminator */
314             break;
315         if (cur_vd_type > max_vd_type)
316         {
317             max_vd_type = cur_vd_type;
318             best_offs = offs + extra_offs;
319         }
320     }
321     return best_offs;
322 }
323
324
325 /***********************************************************************
326  *           VOLUME_ReadFATSuperblock
327  */
328 static enum fs_type VOLUME_ReadFATSuperblock( HANDLE handle, BYTE *buff )
329 {
330     DWORD size;
331
332     /* try a fixed disk, with a FAT partition */
333     if (SetFilePointer( handle, 0, NULL, FILE_BEGIN ) != 0 ||
334         !ReadFile( handle, buff, SUPERBLOCK_SIZE, &size, NULL ))
335     {
336         if (GetLastError() == ERROR_BAD_DEV_TYPE) return FS_UNKNOWN;  /* not a real device */
337         return FS_ERROR;
338     }
339
340     if (size < SUPERBLOCK_SIZE) return FS_UNKNOWN;
341
342     /* FIXME: do really all FAT have their name beginning with
343      * "FAT" ? (At least FAT12, FAT16 and FAT32 have :)
344      */
345     if (!memcmp(buff+0x36, "FAT", 3) || !memcmp(buff+0x52, "FAT", 3))
346     {
347         /* guess which type of FAT we have */
348         int reasonable;
349         unsigned int sectors,
350                      sect_per_fat,
351                      total_sectors,
352                      num_boot_sectors,
353                      num_fats,
354                      num_root_dir_ents,
355                      bytes_per_sector,
356                      sectors_per_cluster,
357                      nclust;
358         sect_per_fat = GETWORD(buff, 0x16);
359         if (!sect_per_fat) sect_per_fat = GETLONG(buff, 0x24);
360         total_sectors = GETWORD(buff, 0x13);
361         if (!total_sectors)
362             total_sectors = GETLONG(buff, 0x20);
363         num_boot_sectors = GETWORD(buff, 0x0e);
364         num_fats =  buff[0x10];
365         num_root_dir_ents = GETWORD(buff, 0x11);
366         bytes_per_sector = GETWORD(buff, 0x0b);
367         sectors_per_cluster = buff[0x0d];
368         /* check if the parameters are reasonable and will not cause
369          * arithmetic errors in the calculation */
370         reasonable = num_boot_sectors < total_sectors &&
371                      num_fats < 16 &&
372                      bytes_per_sector >= 512 && bytes_per_sector % 512 == 0 &&
373                      sectors_per_cluster > 1;
374         if (!reasonable) return FS_UNKNOWN;
375         sectors =  total_sectors - num_boot_sectors - num_fats * sect_per_fat -
376             (num_root_dir_ents * 32 + bytes_per_sector - 1) / bytes_per_sector;
377         nclust = sectors / sectors_per_cluster;
378         if ((buff[0x42] == 0x28 || buff[0x42] == 0x29) &&
379                 !memcmp(buff+0x52, "FAT", 3)) return FS_FAT32;
380         if (nclust < 65525)
381         {
382             if ((buff[0x26] == 0x28 || buff[0x26] == 0x29) &&
383                     !memcmp(buff+0x36, "FAT", 3))
384                 return FS_FAT1216;
385         }
386     }
387     return FS_UNKNOWN;
388 }
389
390
391 /***********************************************************************
392  *           VOLUME_ReadCDSuperblock
393  */
394 static enum fs_type VOLUME_ReadCDSuperblock( HANDLE handle, BYTE *buff )
395 {
396     DWORD size, offs = VOLUME_FindCdRomDataBestVoldesc( handle );
397
398     if (!offs) return FS_UNKNOWN;
399
400     if (SetFilePointer( handle, offs, NULL, FILE_BEGIN ) != offs ||
401         !ReadFile( handle, buff, SUPERBLOCK_SIZE, &size, NULL ) ||
402         size != SUPERBLOCK_SIZE)
403         return FS_ERROR;
404
405     /* check for iso9660 present */
406     if (!memcmp(&buff[1], "CD001", 5)) return FS_ISO9660;
407     return FS_UNKNOWN;
408 }
409
410
411 /**************************************************************************
412  *                              VOLUME_GetSuperblockLabel
413  */
414 static void VOLUME_GetSuperblockLabel( const WCHAR *device, enum fs_type type, const BYTE *superblock,
415                                        WCHAR *label, DWORD len )
416 {
417     const BYTE *label_ptr = NULL;
418     DWORD label_len;
419
420     switch(type)
421     {
422     case FS_ERROR:
423         label_len = 0;
424         break;
425     case FS_UNKNOWN:
426         get_filesystem_label( device, label, len );
427         return;
428     case FS_FAT1216:
429         label_ptr = superblock + 0x2b;
430         label_len = 11;
431         break;
432     case FS_FAT32:
433         label_ptr = superblock + 0x47;
434         label_len = 11;
435         break;
436     case FS_ISO9660:
437         {
438             BYTE ver = superblock[0x5a];
439
440             if (superblock[0x58] == 0x25 && superblock[0x59] == 0x2f &&  /* Unicode ID */
441                 ((ver == 0x40) || (ver == 0x43) || (ver == 0x45)))
442             { /* yippee, unicode */
443                 unsigned int i;
444
445                 if (len > 17) len = 17;
446                 for (i = 0; i < len-1; i++)
447                     label[i] = (superblock[40+2*i] << 8) | superblock[41+2*i];
448                 label[i] = 0;
449                 while (i && label[i-1] == ' ') label[--i] = 0;
450                 return;
451             }
452             label_ptr = superblock + 40;
453             label_len = 32;
454             break;
455         }
456     }
457     if (label_len) RtlMultiByteToUnicodeN( label, (len-1) * sizeof(WCHAR),
458                                            &label_len, (LPCSTR)label_ptr, label_len );
459     label_len /= sizeof(WCHAR);
460     label[label_len] = 0;
461     while (label_len && label[label_len-1] == ' ') label[--label_len] = 0;
462 }
463
464
465 /**************************************************************************
466  *                              VOLUME_GetSuperblockSerial
467  */
468 static DWORD VOLUME_GetSuperblockSerial( const WCHAR *device, enum fs_type type, const BYTE *superblock )
469 {
470     switch(type)
471     {
472     case FS_ERROR:
473         break;
474     case FS_UNKNOWN:
475         return get_filesystem_serial( device );
476     case FS_FAT1216:
477         return GETLONG( superblock, 0x27 );
478     case FS_FAT32:
479         return GETLONG( superblock, 0x33 );
480     case FS_ISO9660:
481         {
482             BYTE sum[4];
483             int i;
484
485             sum[0] = sum[1] = sum[2] = sum[3] = 0;
486             for (i = 0; i < 2048; i += 4)
487             {
488                 /* DON'T optimize this into DWORD !! (breaks overflow) */
489                 sum[0] += superblock[i+0];
490                 sum[1] += superblock[i+1];
491                 sum[2] += superblock[i+2];
492                 sum[3] += superblock[i+3];
493             }
494             /*
495              * OK, another braindead one... argh. Just believe it.
496              * Me$$ysoft chose to reverse the serial number in NT4/W2K.
497              * It's true and nobody will ever be able to change it.
498              */
499             if (GetVersion() & 0x80000000)
500                 return (sum[3] << 24) | (sum[2] << 16) | (sum[1] << 8) | sum[0];
501             else
502                 return (sum[0] << 24) | (sum[1] << 16) | (sum[2] << 8) | sum[3];
503         }
504     }
505     return 0;
506 }
507
508
509 /**************************************************************************
510  *                              VOLUME_GetAudioCDSerial
511  */
512 static DWORD VOLUME_GetAudioCDSerial( const CDROM_TOC *toc )
513 {
514     DWORD serial = 0;
515     int i;
516
517     for (i = 0; i <= toc->LastTrack - toc->FirstTrack; i++)
518         serial += ((toc->TrackData[i].Address[1] << 16) |
519                    (toc->TrackData[i].Address[2] << 8) |
520                    toc->TrackData[i].Address[3]);
521
522     /*
523      * dwStart, dwEnd collect the beginning and end of the disc respectively, in
524      * frames.
525      * There it is collected for correcting the serial when there are less than
526      * 3 tracks.
527      */
528     if (toc->LastTrack - toc->FirstTrack + 1 < 3)
529     {
530         DWORD dwStart = FRAME_OF_TOC(toc, toc->FirstTrack);
531         DWORD dwEnd = FRAME_OF_TOC(toc, toc->LastTrack + 1);
532         serial += dwEnd - dwStart;
533     }
534     return serial;
535 }
536
537
538 /***********************************************************************
539  *           GetVolumeInformationW   (KERNEL32.@)
540  */
541 BOOL WINAPI GetVolumeInformationW( LPCWSTR root, LPWSTR label, DWORD label_len,
542                                    DWORD *serial, DWORD *filename_len, DWORD *flags,
543                                    LPWSTR fsname, DWORD fsname_len )
544 {
545     static const WCHAR audiocdW[] = {'A','u','d','i','o',' ','C','D',0};
546     static const WCHAR fatW[] = {'F','A','T',0};
547     static const WCHAR fat32W[] = {'F','A','T','3','2',0};
548     static const WCHAR ntfsW[] = {'N','T','F','S',0};
549     static const WCHAR cdfsW[] = {'C','D','F','S',0};
550
551     WCHAR device[] = {'\\','\\','.','\\','A',':',0};
552     HANDLE handle;
553     enum fs_type type = FS_UNKNOWN;
554
555     if (!root)
556     {
557         WCHAR path[MAX_PATH];
558         GetCurrentDirectoryW( MAX_PATH, path );
559         device[4] = path[0];
560     }
561     else
562     {
563         if (!root[0] || root[1] != ':')
564         {
565             SetLastError( ERROR_INVALID_NAME );
566             return FALSE;
567         }
568         device[4] = root[0];
569     }
570
571     /* try to open the device */
572
573     handle = CreateFileW( device, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE,
574                           NULL, OPEN_EXISTING, 0, 0 );
575     if (handle != INVALID_HANDLE_VALUE)
576     {
577         BYTE superblock[SUPERBLOCK_SIZE];
578         CDROM_TOC toc;
579         DWORD br;
580
581         /* check for audio CD */
582         /* FIXME: we only check the first track for now */
583         if (DeviceIoControl( handle, IOCTL_CDROM_READ_TOC, NULL, 0, &toc, sizeof(toc), &br, 0 ))
584         {
585             if (!(toc.TrackData[0].Control & 0x04))  /* audio track */
586             {
587                 TRACE( "%s: found audio CD\n", debugstr_w(device) );
588                 if (label) lstrcpynW( label, audiocdW, label_len );
589                 if (serial) *serial = VOLUME_GetAudioCDSerial( &toc );
590                 CloseHandle( handle );
591                 type = FS_ISO9660;
592                 goto fill_fs_info;
593             }
594             type = VOLUME_ReadCDSuperblock( handle, superblock );
595         }
596         else
597         {
598             type = VOLUME_ReadFATSuperblock( handle, superblock );
599             if (type == FS_UNKNOWN) type = VOLUME_ReadCDSuperblock( handle, superblock );
600         }
601         CloseHandle( handle );
602         TRACE( "%s: found fs type %d\n", debugstr_w(device), type );
603         if (type == FS_ERROR) return FALSE;
604
605         if (label && label_len) VOLUME_GetSuperblockLabel( device, type, superblock, label, label_len );
606         if (serial) *serial = VOLUME_GetSuperblockSerial( device, type, superblock );
607         goto fill_fs_info;
608     }
609     else TRACE( "cannot open device %s: err %d\n", debugstr_w(device), GetLastError() );
610
611     /* we couldn't open the device, fallback to default strategy */
612
613     switch(GetDriveTypeW( root ))
614     {
615     case DRIVE_UNKNOWN:
616     case DRIVE_NO_ROOT_DIR:
617         SetLastError( ERROR_NOT_READY );
618         return FALSE;
619     case DRIVE_REMOVABLE:
620     case DRIVE_FIXED:
621     case DRIVE_REMOTE:
622     case DRIVE_RAMDISK:
623         type = FS_UNKNOWN;
624         break;
625     case DRIVE_CDROM:
626         type = FS_ISO9660;
627         break;
628     }
629
630     if (label && label_len) get_filesystem_label( device, label, label_len );
631     if (serial) *serial = get_filesystem_serial( device );
632
633 fill_fs_info:  /* now fill in the information that depends on the file system type */
634
635     switch(type)
636     {
637     case FS_ISO9660:
638         if (fsname) lstrcpynW( fsname, cdfsW, fsname_len );
639         if (filename_len) *filename_len = 221;
640         if (flags) *flags = FILE_READ_ONLY_VOLUME;
641         break;
642     case FS_FAT1216:
643         if (fsname) lstrcpynW( fsname, fatW, fsname_len );
644     case FS_FAT32:
645         if (type == FS_FAT32 && fsname) lstrcpynW( fsname, fat32W, fsname_len );
646         if (filename_len) *filename_len = 255;
647         if (flags) *flags = FILE_CASE_PRESERVED_NAMES;  /* FIXME */
648         break;
649     default:
650         if (fsname) lstrcpynW( fsname, ntfsW, fsname_len );
651         if (filename_len) *filename_len = 255;
652         if (flags) *flags = FILE_CASE_PRESERVED_NAMES;
653         break;
654     }
655     return TRUE;
656 }
657
658
659 /***********************************************************************
660  *           GetVolumeInformationA   (KERNEL32.@)
661  */
662 BOOL WINAPI GetVolumeInformationA( LPCSTR root, LPSTR label,
663                                    DWORD label_len, DWORD *serial,
664                                    DWORD *filename_len, DWORD *flags,
665                                    LPSTR fsname, DWORD fsname_len )
666 {
667     WCHAR *rootW = NULL;
668     LPWSTR labelW, fsnameW;
669     BOOL ret;
670
671     if (root && !(rootW = FILE_name_AtoW( root, FALSE ))) return FALSE;
672
673     labelW = label ? HeapAlloc(GetProcessHeap(), 0, label_len * sizeof(WCHAR)) : NULL;
674     fsnameW = fsname ? HeapAlloc(GetProcessHeap(), 0, fsname_len * sizeof(WCHAR)) : NULL;
675
676     if ((ret = GetVolumeInformationW(rootW, labelW, label_len, serial,
677                                     filename_len, flags, fsnameW, fsname_len)))
678     {
679         if (label) FILE_name_WtoA( labelW, -1, label, label_len );
680         if (fsname) FILE_name_WtoA( fsnameW, -1, fsname, fsname_len );
681     }
682
683     HeapFree( GetProcessHeap(), 0, labelW );
684     HeapFree( GetProcessHeap(), 0, fsnameW );
685     return ret;
686 }
687
688
689
690 /***********************************************************************
691  *           SetVolumeLabelW   (KERNEL32.@)
692  */
693 BOOL WINAPI SetVolumeLabelW( LPCWSTR root, LPCWSTR label )
694 {
695     WCHAR device[] = {'\\','\\','.','\\','A',':',0};
696     HANDLE handle;
697     enum fs_type type = FS_UNKNOWN;
698
699     if (!root)
700     {
701         WCHAR path[MAX_PATH];
702         GetCurrentDirectoryW( MAX_PATH, path );
703         device[4] = path[0];
704     }
705     else
706     {
707         if (!root[0] || root[1] != ':')
708         {
709             SetLastError( ERROR_INVALID_NAME );
710             return FALSE;
711         }
712         device[4] = root[0];
713     }
714
715     /* try to open the device */
716
717     handle = CreateFileW( device, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE,
718                           NULL, OPEN_EXISTING, 0, 0 );
719     if (handle != INVALID_HANDLE_VALUE)
720     {
721         BYTE superblock[SUPERBLOCK_SIZE];
722
723         type = VOLUME_ReadFATSuperblock( handle, superblock );
724         if (type == FS_UNKNOWN) type = VOLUME_ReadCDSuperblock( handle, superblock );
725         CloseHandle( handle );
726         if (type != FS_UNKNOWN)
727         {
728             /* we can't set the label on FAT or CDROM file systems */
729             TRACE( "cannot set label on device %s type %d\n", debugstr_w(device), type );
730             SetLastError( ERROR_ACCESS_DENIED );
731             return FALSE;
732         }
733     }
734     else
735     {
736         TRACE( "cannot open device %s: err %d\n", debugstr_w(device), GetLastError() );
737         if (GetLastError() == ERROR_ACCESS_DENIED) return FALSE;
738     }
739
740     /* we couldn't open the device, fallback to default strategy */
741
742     switch(GetDriveTypeW( root ))
743     {
744     case DRIVE_UNKNOWN:
745     case DRIVE_NO_ROOT_DIR:
746         SetLastError( ERROR_NOT_READY );
747         break;
748     case DRIVE_REMOVABLE:
749     case DRIVE_FIXED:
750         {
751             WCHAR labelW[] = {'A',':','\\','.','w','i','n','d','o','w','s','-','l','a','b','e','l',0};
752
753             labelW[0] = device[4];
754
755             if (!label[0])  /* delete label file when setting an empty label */
756                 return DeleteFileW( labelW ) || GetLastError() == ERROR_FILE_NOT_FOUND;
757
758             handle = CreateFileW( labelW, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
759                                   CREATE_ALWAYS, 0, 0 );
760             if (handle != INVALID_HANDLE_VALUE)
761             {
762                 char buffer[64];
763                 DWORD size;
764
765                 if (!WideCharToMultiByte( CP_UNIXCP, 0, label, -1, buffer, sizeof(buffer)-1, NULL, NULL ))
766                     buffer[sizeof(buffer)-2] = 0;
767                 strcat( buffer, "\n" );
768                 WriteFile( handle, buffer, strlen(buffer), &size, NULL );
769                 CloseHandle( handle );
770                 return TRUE;
771             }
772             break;
773         }
774     case DRIVE_REMOTE:
775     case DRIVE_RAMDISK:
776     case DRIVE_CDROM:
777         SetLastError( ERROR_ACCESS_DENIED );
778         break;
779     }
780     return FALSE;
781 }
782
783 /***********************************************************************
784  *           SetVolumeLabelA   (KERNEL32.@)
785  */
786 BOOL WINAPI SetVolumeLabelA(LPCSTR root, LPCSTR volname)
787 {
788     WCHAR *rootW = NULL, *volnameW = NULL;
789     BOOL ret;
790
791     if (root && !(rootW = FILE_name_AtoW( root, FALSE ))) return FALSE;
792     if (volname && !(volnameW = FILE_name_AtoW( volname, TRUE ))) return FALSE;
793     ret = SetVolumeLabelW( rootW, volnameW );
794     HeapFree( GetProcessHeap(), 0, volnameW );
795     return ret;
796 }
797
798
799 /***********************************************************************
800  *           GetVolumeNameForVolumeMountPointA   (KERNEL32.@)
801  */
802 BOOL WINAPI GetVolumeNameForVolumeMountPointA( LPCSTR path, LPSTR volume, DWORD size )
803 {
804     BOOL ret;
805     WCHAR volumeW[50], *pathW = NULL;
806     DWORD len = min( sizeof(volumeW) / sizeof(WCHAR), size );
807
808     TRACE("(%s, %p, %x)\n", debugstr_a(path), volume, size);
809
810     if (!path || !(pathW = FILE_name_AtoW( path, TRUE )))
811         return FALSE;
812
813     if ((ret = GetVolumeNameForVolumeMountPointW( pathW, volumeW, len )))
814         FILE_name_WtoA( volumeW, -1, volume, len );
815
816     HeapFree( GetProcessHeap(), 0, pathW );
817     return ret;
818 }
819
820 /***********************************************************************
821  *           GetVolumeNameForVolumeMountPointW   (KERNEL32.@)
822  */
823 BOOL WINAPI GetVolumeNameForVolumeMountPointW( LPCWSTR path, LPWSTR volume, DWORD size )
824 {
825     BOOL ret = FALSE;
826     static const WCHAR fmt[] =
827         { '\\','\\','?','\\','V','o','l','u','m','e','{','%','0','2','x','}','\\',0 };
828
829     TRACE("(%s, %p, %x)\n", debugstr_w(path), volume, size);
830
831     if (!path || !path[0]) return FALSE;
832
833     if (size >= sizeof(fmt) / sizeof(WCHAR))
834     {
835         /* FIXME: will break when we support volume mounts */
836         sprintfW( volume, fmt, tolowerW( path[0] ) - 'a' );
837         ret = TRUE;
838     }
839     return ret;
840 }
841
842 /***********************************************************************
843  *           DefineDosDeviceW       (KERNEL32.@)
844  */
845 BOOL WINAPI DefineDosDeviceW( DWORD flags, LPCWSTR devname, LPCWSTR targetpath )
846 {
847     DWORD len, dosdev;
848     BOOL ret = FALSE;
849     char *path = NULL, *target, *p;
850
851     TRACE("%x, %s, %s\n", flags, debugstr_w(devname), debugstr_w(targetpath));
852
853     if (!(flags & DDD_REMOVE_DEFINITION))
854     {
855         if (!(flags & DDD_RAW_TARGET_PATH))
856         {
857             FIXME( "(0x%08x,%s,%s) DDD_RAW_TARGET_PATH flag not set, not supported yet\n",
858                    flags, debugstr_w(devname), debugstr_w(targetpath) );
859             SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
860             return FALSE;
861         }
862
863         len = WideCharToMultiByte( CP_UNIXCP, 0, targetpath, -1, NULL, 0, NULL, NULL );
864         if ((target = HeapAlloc( GetProcessHeap(), 0, len )))
865         {
866             WideCharToMultiByte( CP_UNIXCP, 0, targetpath, -1, target, len, NULL, NULL );
867             for (p = target; *p; p++) if (*p == '\\') *p = '/';
868         }
869         else
870         {
871             SetLastError( ERROR_NOT_ENOUGH_MEMORY );
872             return FALSE;
873         }
874     }
875     else target = NULL;
876
877     /* first check for a DOS device */
878
879     if ((dosdev = RtlIsDosDeviceName_U( devname )))
880     {
881         WCHAR name[5];
882
883         memcpy( name, devname + HIWORD(dosdev)/sizeof(WCHAR), LOWORD(dosdev) );
884         name[LOWORD(dosdev)/sizeof(WCHAR)] = 0;
885         path = get_dos_device_path( name );
886     }
887     else if (isalphaW(devname[0]) && devname[1] == ':' && !devname[2])  /* drive mapping */
888     {
889         path = get_dos_device_path( devname );
890     }
891     else SetLastError( ERROR_FILE_NOT_FOUND );
892
893     if (path)
894     {
895         if (target)
896         {
897             TRACE( "creating symlink %s -> %s\n", path, target );
898             unlink( path );
899             if (!symlink( target, path )) ret = TRUE;
900             else FILE_SetDosError();
901         }
902         else
903         {
904             TRACE( "removing symlink %s\n", path );
905             if (!unlink( path )) ret = TRUE;
906             else FILE_SetDosError();
907         }
908         HeapFree( GetProcessHeap(), 0, path );
909     }
910     HeapFree( GetProcessHeap(), 0, target );
911     return ret;
912 }
913
914
915 /***********************************************************************
916  *           DefineDosDeviceA       (KERNEL32.@)
917  */
918 BOOL WINAPI DefineDosDeviceA(DWORD flags, LPCSTR devname, LPCSTR targetpath)
919 {
920     WCHAR *devW, *targetW = NULL;
921     BOOL ret;
922
923     if (!(devW = FILE_name_AtoW( devname, FALSE ))) return FALSE;
924     if (targetpath && !(targetW = FILE_name_AtoW( targetpath, TRUE ))) return FALSE;
925     ret = DefineDosDeviceW(flags, devW, targetW);
926     HeapFree( GetProcessHeap(), 0, targetW );
927     return ret;
928 }
929
930
931 /***********************************************************************
932  *           QueryDosDeviceW   (KERNEL32.@)
933  *
934  * returns array of strings terminated by \0, terminated by \0
935  */
936 DWORD WINAPI QueryDosDeviceW( LPCWSTR devname, LPWSTR target, DWORD bufsize )
937 {
938     static const WCHAR auxW[] = {'A','U','X',0};
939     static const WCHAR nulW[] = {'N','U','L',0};
940     static const WCHAR prnW[] = {'P','R','N',0};
941     static const WCHAR comW[] = {'C','O','M',0};
942     static const WCHAR lptW[] = {'L','P','T',0};
943     static const WCHAR rootW[] = {'A',':','\\',0};
944     static const WCHAR com0W[] = {'\\','?','?','\\','C','O','M','0',0};
945     static const WCHAR com1W[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\','C','O','M','1',0,0};
946     static const WCHAR lpt1W[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\','L','P','T','1',0,0};
947     static const WCHAR driveW[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\','A',':',0};
948
949     UNICODE_STRING nt_name;
950     ANSI_STRING unix_name;
951     WCHAR nt_buffer[10];
952     NTSTATUS status;
953
954     if (!bufsize)
955     {
956         SetLastError( ERROR_INSUFFICIENT_BUFFER );
957         return 0;
958     }
959
960     if (devname)
961     {
962         WCHAR *p, name[5];
963         char *path, *link;
964         DWORD dosdev, ret = 0;
965
966         if ((dosdev = RtlIsDosDeviceName_U( devname )))
967         {
968             memcpy( name, devname + HIWORD(dosdev)/sizeof(WCHAR), LOWORD(dosdev) );
969             name[LOWORD(dosdev)/sizeof(WCHAR)] = 0;
970         }
971         else if (devname[0] && devname[1] == ':' && !devname[2])
972         {
973             /* FIXME: should do this for all devices, not just drives */
974             NTSTATUS status;
975             WCHAR buffer[sizeof(driveW)/sizeof(WCHAR)];
976
977             memcpy( buffer, driveW, sizeof(driveW) );
978             buffer[12] = devname[0];
979             if ((status = read_nt_symlink( buffer, target, bufsize )))
980             {
981                 SetLastError( RtlNtStatusToDosError(status) );
982                 return 0;
983             }
984             ret = strlenW( target ) + 1;
985             goto done;
986         }
987         else
988         {
989             SetLastError( ERROR_BAD_PATHNAME );
990             return 0;
991         }
992
993         if (!(path = get_dos_device_path( name ))) return 0;
994         link = read_symlink( path );
995         HeapFree( GetProcessHeap(), 0, path );
996
997         if (link)
998         {
999             ret = MultiByteToWideChar( CP_UNIXCP, 0, link, -1, target, bufsize );
1000             HeapFree( GetProcessHeap(), 0, link );
1001         }
1002         else if (dosdev)  /* look for device defaults */
1003         {
1004             if (!strcmpiW( name, auxW ))
1005             {
1006                 if (bufsize >= sizeof(com1W)/sizeof(WCHAR))
1007                 {
1008                     memcpy( target, com1W, sizeof(com1W) );
1009                     ret = sizeof(com1W)/sizeof(WCHAR);
1010                 }
1011                 else SetLastError( ERROR_INSUFFICIENT_BUFFER );
1012                 return ret;
1013             }
1014             if (!strcmpiW( name, prnW ))
1015             {
1016                 if (bufsize >= sizeof(lpt1W)/sizeof(WCHAR))
1017                 {
1018                     memcpy( target, lpt1W, sizeof(lpt1W) );
1019                     ret = sizeof(lpt1W)/sizeof(WCHAR);
1020                 }
1021                 else SetLastError( ERROR_INSUFFICIENT_BUFFER );
1022                 return ret;
1023             }
1024
1025             nt_buffer[0] = '\\';
1026             nt_buffer[1] = '?';
1027             nt_buffer[2] = '?';
1028             nt_buffer[3] = '\\';
1029             strcpyW( nt_buffer + 4, name );
1030             RtlInitUnicodeString( &nt_name, nt_buffer );
1031             status = wine_nt_to_unix_file_name( &nt_name, &unix_name, FILE_OPEN, TRUE );
1032             if (status) SetLastError( RtlNtStatusToDosError(status) );
1033             else
1034             {
1035                 ret = MultiByteToWideChar( CP_UNIXCP, 0, unix_name.Buffer, -1, target, bufsize );
1036                 RtlFreeAnsiString( &unix_name );
1037             }
1038         }
1039     done:
1040         if (ret)
1041         {
1042             if (ret < bufsize) target[ret++] = 0;  /* add an extra null */
1043             for (p = target; *p; p++) if (*p == '/') *p = '\\';
1044         }
1045
1046         return ret;
1047     }
1048     else  /* return a list of all devices */
1049     {
1050         WCHAR *p = target;
1051         int i;
1052
1053         if (bufsize <= (sizeof(auxW)+sizeof(nulW)+sizeof(prnW))/sizeof(WCHAR))
1054         {
1055             SetLastError( ERROR_INSUFFICIENT_BUFFER );
1056             return 0;
1057         }
1058
1059         memcpy( p, auxW, sizeof(auxW) );
1060         p += sizeof(auxW) / sizeof(WCHAR);
1061         memcpy( p, nulW, sizeof(nulW) );
1062         p += sizeof(nulW) / sizeof(WCHAR);
1063         memcpy( p, prnW, sizeof(prnW) );
1064         p += sizeof(prnW) / sizeof(WCHAR);
1065
1066         strcpyW( nt_buffer, com0W );
1067         RtlInitUnicodeString( &nt_name, nt_buffer );
1068
1069         for (i = 1; i <= 9; i++)
1070         {
1071             nt_buffer[7] = '0' + i;
1072             if (!wine_nt_to_unix_file_name( &nt_name, &unix_name, FILE_OPEN, TRUE ))
1073             {
1074                 RtlFreeAnsiString( &unix_name );
1075                 if (p + 5 >= target + bufsize)
1076                 {
1077                     SetLastError( ERROR_INSUFFICIENT_BUFFER );
1078                     return 0;
1079                 }
1080                 strcpyW( p, comW );
1081                 p[3] = '0' + i;
1082                 p[4] = 0;
1083                 p += 5;
1084             }
1085         }
1086         strcpyW( nt_buffer + 4, lptW );
1087         for (i = 1; i <= 9; i++)
1088         {
1089             nt_buffer[7] = '0' + i;
1090             if (!wine_nt_to_unix_file_name( &nt_name, &unix_name, FILE_OPEN, TRUE ))
1091             {
1092                 RtlFreeAnsiString( &unix_name );
1093                 if (p + 5 >= target + bufsize)
1094                 {
1095                     SetLastError( ERROR_INSUFFICIENT_BUFFER );
1096                     return 0;
1097                 }
1098                 strcpyW( p, lptW );
1099                 p[3] = '0' + i;
1100                 p[4] = 0;
1101                 p += 5;
1102             }
1103         }
1104
1105         strcpyW( nt_buffer + 4, rootW );
1106         RtlInitUnicodeString( &nt_name, nt_buffer );
1107
1108         /* FIXME: should simply enumerate the DosDevices directory instead */
1109         for (i = 0; i < 26; i++)
1110         {
1111             WCHAR buffer[sizeof(driveW)/sizeof(WCHAR)], dummy[8];
1112             NTSTATUS status;
1113
1114             memcpy( buffer, driveW, sizeof(driveW) );
1115             buffer[12] = 'A' + i;
1116             status = read_nt_symlink( buffer, dummy, sizeof(dummy)/sizeof(WCHAR) );
1117             if (status == STATUS_SUCCESS || status == STATUS_BUFFER_TOO_SMALL)
1118             {
1119                 if (p + 3 >= target + bufsize)
1120                 {
1121                     SetLastError( ERROR_INSUFFICIENT_BUFFER );
1122                     return 0;
1123                 }
1124                 *p++ = 'A' + i;
1125                 *p++ = ':';
1126                 *p++ = 0;
1127             }
1128         }
1129         *p++ = 0;  /* terminating null */
1130         return p - target;
1131     }
1132 }
1133
1134
1135 /***********************************************************************
1136  *           QueryDosDeviceA   (KERNEL32.@)
1137  *
1138  * returns array of strings terminated by \0, terminated by \0
1139  */
1140 DWORD WINAPI QueryDosDeviceA( LPCSTR devname, LPSTR target, DWORD bufsize )
1141 {
1142     DWORD ret = 0, retW;
1143     WCHAR *devnameW = NULL;
1144     LPWSTR targetW;
1145
1146     if (devname && !(devnameW = FILE_name_AtoW( devname, FALSE ))) return 0;
1147
1148     targetW = HeapAlloc( GetProcessHeap(),0, bufsize * sizeof(WCHAR) );
1149     if (!targetW)
1150     {
1151         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1152         return 0;
1153     }
1154
1155     retW = QueryDosDeviceW(devnameW, targetW, bufsize);
1156
1157     ret = FILE_name_WtoA( targetW, retW, target, bufsize );
1158
1159     HeapFree(GetProcessHeap(), 0, targetW);
1160     return ret;
1161 }
1162
1163
1164 /***********************************************************************
1165  *           GetLogicalDrives   (KERNEL32.@)
1166  */
1167 DWORD WINAPI GetLogicalDrives(void)
1168 {
1169     const char *config_dir = wine_get_config_dir();
1170     struct stat st;
1171     char *buffer, *dev;
1172     DWORD ret = 0;
1173     int i;
1174
1175     if (!(buffer = HeapAlloc( GetProcessHeap(), 0, strlen(config_dir) + sizeof("/dosdevices/a:") )))
1176     {
1177         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1178         return 0;
1179     }
1180     strcpy( buffer, config_dir );
1181     strcat( buffer, "/dosdevices/a:" );
1182     dev = buffer + strlen(buffer) - 2;
1183
1184     for (i = 0; i < 26; i++)
1185     {
1186         *dev = 'a' + i;
1187         if (!stat( buffer, &st )) ret |= (1 << i);
1188     }
1189     HeapFree( GetProcessHeap(), 0, buffer );
1190     return ret;
1191 }
1192
1193
1194 /***********************************************************************
1195  *           GetLogicalDriveStringsA   (KERNEL32.@)
1196  */
1197 UINT WINAPI GetLogicalDriveStringsA( UINT len, LPSTR buffer )
1198 {
1199     DWORD drives = GetLogicalDrives();
1200     UINT drive, count;
1201
1202     for (drive = count = 0; drive < 26; drive++) if (drives & (1 << drive)) count++;
1203     if ((count * 4) + 1 > len) return count * 4 + 1;
1204
1205     for (drive = 0; drive < 26; drive++)
1206     {
1207         if (drives & (1 << drive))
1208         {
1209             *buffer++ = 'A' + drive;
1210             *buffer++ = ':';
1211             *buffer++ = '\\';
1212             *buffer++ = 0;
1213         }
1214     }
1215     *buffer = 0;
1216     return count * 4;
1217 }
1218
1219
1220 /***********************************************************************
1221  *           GetLogicalDriveStringsW   (KERNEL32.@)
1222  */
1223 UINT WINAPI GetLogicalDriveStringsW( UINT len, LPWSTR buffer )
1224 {
1225     DWORD drives = GetLogicalDrives();
1226     UINT drive, count;
1227
1228     for (drive = count = 0; drive < 26; drive++) if (drives & (1 << drive)) count++;
1229     if ((count * 4) + 1 > len) return count * 4 + 1;
1230
1231     for (drive = 0; drive < 26; drive++)
1232     {
1233         if (drives & (1 << drive))
1234         {
1235             *buffer++ = 'A' + drive;
1236             *buffer++ = ':';
1237             *buffer++ = '\\';
1238             *buffer++ = 0;
1239         }
1240     }
1241     *buffer = 0;
1242     return count * 4;
1243 }
1244
1245
1246 /***********************************************************************
1247  *           GetDriveTypeW   (KERNEL32.@)
1248  *
1249  * Returns the type of the disk drive specified. If root is NULL the
1250  * root of the current directory is used.
1251  *
1252  * RETURNS
1253  *
1254  *  Type of drive (from Win32 SDK):
1255  *
1256  *   DRIVE_UNKNOWN     unable to find out anything about the drive
1257  *   DRIVE_NO_ROOT_DIR nonexistent root dir
1258  *   DRIVE_REMOVABLE   the disk can be removed from the machine
1259  *   DRIVE_FIXED       the disk cannot be removed from the machine
1260  *   DRIVE_REMOTE      network disk
1261  *   DRIVE_CDROM       CDROM drive
1262  *   DRIVE_RAMDISK     virtual disk in RAM
1263  */
1264 UINT WINAPI GetDriveTypeW(LPCWSTR root) /* [in] String describing drive */
1265 {
1266     FILE_FS_DEVICE_INFORMATION info;
1267     IO_STATUS_BLOCK io;
1268     NTSTATUS status;
1269     HANDLE handle;
1270     UINT ret;
1271
1272     if (!open_device_root( root, &handle )) return DRIVE_NO_ROOT_DIR;
1273
1274     status = NtQueryVolumeInformationFile( handle, &io, &info, sizeof(info), FileFsDeviceInformation );
1275     NtClose( handle );
1276     if (status != STATUS_SUCCESS)
1277     {
1278         SetLastError( RtlNtStatusToDosError(status) );
1279         ret = DRIVE_UNKNOWN;
1280     }
1281     else if ((ret = get_registry_drive_type( root )) == DRIVE_UNKNOWN)
1282     {
1283         switch (info.DeviceType)
1284         {
1285         case FILE_DEVICE_CD_ROM_FILE_SYSTEM:  ret = DRIVE_CDROM; break;
1286         case FILE_DEVICE_VIRTUAL_DISK:        ret = DRIVE_RAMDISK; break;
1287         case FILE_DEVICE_NETWORK_FILE_SYSTEM: ret = DRIVE_REMOTE; break;
1288         case FILE_DEVICE_DISK_FILE_SYSTEM:
1289             if (info.Characteristics & FILE_REMOTE_DEVICE) ret = DRIVE_REMOTE;
1290             else if (info.Characteristics & FILE_REMOVABLE_MEDIA) ret = DRIVE_REMOVABLE;
1291             else ret = DRIVE_FIXED;
1292             break;
1293         default:
1294             ret = DRIVE_UNKNOWN;
1295             break;
1296         }
1297     }
1298     TRACE( "%s -> %d\n", debugstr_w(root), ret );
1299     return ret;
1300 }
1301
1302
1303 /***********************************************************************
1304  *           GetDriveTypeA   (KERNEL32.@)
1305  *
1306  * See GetDriveTypeW.
1307  */
1308 UINT WINAPI GetDriveTypeA( LPCSTR root )
1309 {
1310     WCHAR *rootW = NULL;
1311
1312     if (root && !(rootW = FILE_name_AtoW( root, FALSE ))) return DRIVE_NO_ROOT_DIR;
1313     return GetDriveTypeW( rootW );
1314 }
1315
1316
1317 /***********************************************************************
1318  *           GetDiskFreeSpaceExW   (KERNEL32.@)
1319  *
1320  *  This function is used to acquire the size of the available and
1321  *  total space on a logical volume.
1322  *
1323  * RETURNS
1324  *
1325  *  Zero on failure, nonzero upon success. Use GetLastError to obtain
1326  *  detailed error information.
1327  *
1328  */
1329 BOOL WINAPI GetDiskFreeSpaceExW( LPCWSTR root, PULARGE_INTEGER avail,
1330                                  PULARGE_INTEGER total, PULARGE_INTEGER totalfree )
1331 {
1332     FILE_FS_SIZE_INFORMATION info;
1333     IO_STATUS_BLOCK io;
1334     NTSTATUS status;
1335     HANDLE handle;
1336     UINT units;
1337
1338     TRACE( "%s,%p,%p,%p\n", debugstr_w(root), avail, total, totalfree );
1339
1340     if (!open_device_root( root, &handle )) return FALSE;
1341
1342     status = NtQueryVolumeInformationFile( handle, &io, &info, sizeof(info), FileFsSizeInformation );
1343     NtClose( handle );
1344     if (status != STATUS_SUCCESS)
1345     {
1346         SetLastError( RtlNtStatusToDosError(status) );
1347         return FALSE;
1348     }
1349
1350     units = info.SectorsPerAllocationUnit * info.BytesPerSector;
1351     if (total) total->QuadPart = info.TotalAllocationUnits.QuadPart * units;
1352     if (totalfree) totalfree->QuadPart = info.AvailableAllocationUnits.QuadPart * units;
1353     /* FIXME: this one should take quotas into account */
1354     if (avail) avail->QuadPart = info.AvailableAllocationUnits.QuadPart * units;
1355     return TRUE;
1356 }
1357
1358
1359 /***********************************************************************
1360  *           GetDiskFreeSpaceExA   (KERNEL32.@)
1361  *
1362  * See GetDiskFreeSpaceExW.
1363  */
1364 BOOL WINAPI GetDiskFreeSpaceExA( LPCSTR root, PULARGE_INTEGER avail,
1365                                  PULARGE_INTEGER total, PULARGE_INTEGER totalfree )
1366 {
1367     WCHAR *rootW = NULL;
1368
1369     if (root && !(rootW = FILE_name_AtoW( root, FALSE ))) return FALSE;
1370     return GetDiskFreeSpaceExW( rootW, avail, total, totalfree );
1371 }
1372
1373
1374 /***********************************************************************
1375  *           GetDiskFreeSpaceW   (KERNEL32.@)
1376  */
1377 BOOL WINAPI GetDiskFreeSpaceW( LPCWSTR root, LPDWORD cluster_sectors,
1378                                LPDWORD sector_bytes, LPDWORD free_clusters,
1379                                LPDWORD total_clusters )
1380 {
1381     FILE_FS_SIZE_INFORMATION info;
1382     IO_STATUS_BLOCK io;
1383     NTSTATUS status;
1384     HANDLE handle;
1385     UINT units;
1386
1387     TRACE( "%s,%p,%p,%p,%p\n", debugstr_w(root),
1388            cluster_sectors, sector_bytes, free_clusters, total_clusters );
1389
1390     if (!open_device_root( root, &handle )) return FALSE;
1391
1392     status = NtQueryVolumeInformationFile( handle, &io, &info, sizeof(info), FileFsSizeInformation );
1393     NtClose( handle );
1394     if (status != STATUS_SUCCESS)
1395     {
1396         SetLastError( RtlNtStatusToDosError(status) );
1397         return FALSE;
1398     }
1399
1400     units = info.SectorsPerAllocationUnit * info.BytesPerSector;
1401
1402     if( GetVersion() & 0x80000000) {    /* win3.x, 9x, ME */
1403         /* cap the size and available at 2GB as per specs */
1404         if (info.TotalAllocationUnits.QuadPart * units > 0x7fffffff) {
1405             info.TotalAllocationUnits.QuadPart = 0x7fffffff / units;
1406             if (info.AvailableAllocationUnits.QuadPart * units > 0x7fffffff)
1407                 info.AvailableAllocationUnits.QuadPart = 0x7fffffff / units;
1408         }
1409         /* nr. of clusters is always <= 65335 */
1410         while( info.TotalAllocationUnits.QuadPart > 65535 ) {
1411             info.TotalAllocationUnits.QuadPart /= 2;
1412             info.AvailableAllocationUnits.QuadPart /= 2;
1413             info.SectorsPerAllocationUnit *= 2;
1414         }
1415     }
1416
1417     if (cluster_sectors) *cluster_sectors = info.SectorsPerAllocationUnit;
1418     if (sector_bytes) *sector_bytes = info.BytesPerSector;
1419     if (free_clusters) *free_clusters = info.AvailableAllocationUnits.u.LowPart;
1420     if (total_clusters) *total_clusters = info.TotalAllocationUnits.u.LowPart;
1421     return TRUE;
1422 }
1423
1424
1425 /***********************************************************************
1426  *           GetDiskFreeSpaceA   (KERNEL32.@)
1427  */
1428 BOOL WINAPI GetDiskFreeSpaceA( LPCSTR root, LPDWORD cluster_sectors,
1429                                LPDWORD sector_bytes, LPDWORD free_clusters,
1430                                LPDWORD total_clusters )
1431 {
1432     WCHAR *rootW = NULL;
1433
1434     if (root && !(rootW = FILE_name_AtoW( root, FALSE ))) return FALSE;
1435     return GetDiskFreeSpaceW( rootW, cluster_sectors, sector_bytes, free_clusters, total_clusters );
1436 }
1437
1438 /***********************************************************************
1439  *           GetVolumePathNameA   (KERNEL32.@)
1440  */
1441 BOOL WINAPI GetVolumePathNameA(LPCSTR filename, LPSTR volumepathname, DWORD buflen)
1442 {
1443     FIXME("(%s, %p, %d), stub!\n", debugstr_a(filename), volumepathname, buflen);
1444     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1445     return FALSE;
1446 }
1447
1448 /***********************************************************************
1449  *           GetVolumePathNameW   (KERNEL32.@)
1450  */
1451 BOOL WINAPI GetVolumePathNameW(LPCWSTR filename, LPWSTR volumepathname, DWORD buflen)
1452 {
1453     FIXME("(%s, %p, %d), stub!\n", debugstr_w(filename), volumepathname, buflen);
1454     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1455     return FALSE;
1456 }
1457
1458 /***********************************************************************
1459  *           FindFirstVolumeA   (KERNEL32.@)
1460  */
1461 HANDLE WINAPI FindFirstVolumeA(LPSTR volume, DWORD len)
1462 {
1463     WCHAR *buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
1464     HANDLE handle = FindFirstVolumeW( buffer, len );
1465
1466     if (handle != INVALID_HANDLE_VALUE)
1467     {
1468         if (!WideCharToMultiByte( CP_ACP, 0, buffer, -1, volume, len, NULL, NULL ))
1469         {
1470             FindVolumeClose( handle );
1471             handle = INVALID_HANDLE_VALUE;
1472         }
1473     }
1474     HeapFree( GetProcessHeap(), 0, buffer );
1475     return handle;
1476 }
1477
1478 /***********************************************************************
1479  *           FindFirstVolumeW   (KERNEL32.@)
1480  */
1481 HANDLE WINAPI FindFirstVolumeW( LPWSTR volume, DWORD len )
1482 {
1483     DWORD size = 1024;
1484     HANDLE mgr = CreateFileW( MOUNTMGR_DOS_DEVICE_NAME, 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
1485                               NULL, OPEN_EXISTING, 0, 0 );
1486     if (mgr == INVALID_HANDLE_VALUE) return INVALID_HANDLE_VALUE;
1487
1488     for (;;)
1489     {
1490         MOUNTMGR_MOUNT_POINT input;
1491         MOUNTMGR_MOUNT_POINTS *output;
1492
1493         if (!(output = HeapAlloc( GetProcessHeap(), 0, size )))
1494         {
1495             SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1496             break;
1497         }
1498         memset( &input, 0, sizeof(input) );
1499
1500         if (!DeviceIoControl( mgr, IOCTL_MOUNTMGR_QUERY_POINTS, &input, sizeof(input),
1501                               output, size, NULL, NULL ))
1502         {
1503             if (GetLastError() != ERROR_MORE_DATA) break;
1504             size = output->Size;
1505             HeapFree( GetProcessHeap(), 0, output );
1506             continue;
1507         }
1508         CloseHandle( mgr );
1509         /* abuse the Size field to store the current index */
1510         output->Size = 0;
1511         if (!FindNextVolumeW( output, volume, len ))
1512         {
1513             HeapFree( GetProcessHeap(), 0, output );
1514             return INVALID_HANDLE_VALUE;
1515         }
1516         return (HANDLE)output;
1517     }
1518     CloseHandle( mgr );
1519     return INVALID_HANDLE_VALUE;
1520 }
1521
1522 /***********************************************************************
1523  *           FindNextVolumeA   (KERNEL32.@)
1524  */
1525 BOOL WINAPI FindNextVolumeA( HANDLE handle, LPSTR volume, DWORD len )
1526 {
1527     WCHAR *buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
1528     BOOL ret;
1529
1530     if ((ret = FindNextVolumeW( handle, buffer, len )))
1531     {
1532         if (!WideCharToMultiByte( CP_ACP, 0, buffer, -1, volume, len, NULL, NULL )) ret = FALSE;
1533     }
1534     HeapFree( GetProcessHeap(), 0, buffer );
1535     return ret;
1536 }
1537
1538 /***********************************************************************
1539  *           FindNextVolumeW   (KERNEL32.@)
1540  */
1541 BOOL WINAPI FindNextVolumeW( HANDLE handle, LPWSTR volume, DWORD len )
1542 {
1543     MOUNTMGR_MOUNT_POINTS *data = handle;
1544
1545     while (data->Size < data->NumberOfMountPoints)
1546     {
1547         static const WCHAR volumeW[] = {'\\','?','?','\\','V','o','l','u','m','e','{',};
1548         WCHAR *link = (WCHAR *)((char *)data + data->MountPoints[data->Size].SymbolicLinkNameOffset);
1549         DWORD size = data->MountPoints[data->Size].SymbolicLinkNameLength;
1550         data->Size++;
1551         /* skip non-volumes */
1552         if (size < sizeof(volumeW) || memcmp( link, volumeW, sizeof(volumeW) )) continue;
1553         if (size + sizeof(WCHAR) >= len * sizeof(WCHAR))
1554         {
1555             SetLastError( ERROR_FILENAME_EXCED_RANGE );
1556             return FALSE;
1557         }
1558         memcpy( volume, link, size );
1559         volume[1] = '\\';  /* map \??\ to \\?\ */
1560         volume[size / sizeof(WCHAR)] = '\\';  /* Windows appends a backslash */
1561         volume[size / sizeof(WCHAR) + 1] = 0;
1562         TRACE( "returning entry %u %s\n", data->Size - 1, debugstr_w(volume) );
1563         return TRUE;
1564     }
1565     SetLastError( ERROR_NO_MORE_FILES );
1566     return FALSE;
1567 }
1568
1569 /***********************************************************************
1570  *           FindVolumeClose   (KERNEL32.@)
1571  */
1572 BOOL WINAPI FindVolumeClose(HANDLE handle)
1573 {
1574     return HeapFree( GetProcessHeap(), 0, handle );
1575 }
1576
1577 /***********************************************************************
1578  *           FindFirstVolumeMountPointA   (KERNEL32.@)
1579  */
1580 HANDLE WINAPI FindFirstVolumeMountPointA(LPCSTR root, LPSTR mount_point, DWORD len)
1581 {
1582     FIXME("(%s, %p, %d), stub!\n", debugstr_a(root), mount_point, len);
1583     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1584     return INVALID_HANDLE_VALUE;
1585 }
1586
1587 /***********************************************************************
1588  *           FindFirstVolumeMountPointW   (KERNEL32.@)
1589  */
1590 HANDLE WINAPI FindFirstVolumeMountPointW(LPCWSTR root, LPWSTR mount_point, DWORD len)
1591 {
1592     FIXME("(%s, %p, %d), stub!\n", debugstr_w(root), mount_point, len);
1593     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1594     return INVALID_HANDLE_VALUE;
1595 }
1596
1597 /***********************************************************************
1598  *           FindVolumeMountPointClose   (KERNEL32.@)
1599  */
1600 BOOL WINAPI FindVolumeMountPointClose(HANDLE h)
1601 {
1602     FIXME("(%p), stub!\n", h);
1603     return FALSE;
1604 }