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