Added a few more Unicode digits from Unicode version 4.1.
[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_SetSuperblockLabel
386  */
387 static BOOL VOLUME_SetSuperblockLabel( enum fs_type type, HANDLE handle, const WCHAR *label )
388 {
389     CHAR label_data[11];
390     DWORD offset, len;
391
392     switch(type)
393     {
394     case FS_FAT1216:
395         offset = 0x2b;
396         break;
397     case FS_FAT32:
398         offset = 0x47;
399         break;
400     default:
401         SetLastError( ERROR_ACCESS_DENIED );
402         return FALSE;
403     }
404     RtlUnicodeToMultiByteN( label_data, sizeof(label_data), &len,
405                             label, strlenW(label) * sizeof(WCHAR) );
406     if (len < sizeof(label_data))
407         memset( label_data + len, ' ', sizeof(label_data) - len );
408
409     return (SetFilePointer( handle, offset, NULL, FILE_BEGIN ) == offset &&
410             WriteFile( handle, label_data, sizeof(label_data), &len, NULL ));
411 }
412
413
414 /**************************************************************************
415  *                              VOLUME_GetSuperblockSerial
416  */
417 static DWORD VOLUME_GetSuperblockSerial( enum fs_type type, const BYTE *superblock )
418 {
419     switch(type)
420     {
421     case FS_ERROR:
422     case FS_UNKNOWN:
423         break;
424     case FS_FAT1216:
425         return GETLONG( superblock, 0x27 );
426     case FS_FAT32:
427         return GETLONG( superblock, 0x33 );
428     case FS_ISO9660:
429         {
430             BYTE sum[4];
431             int i;
432
433             sum[0] = sum[1] = sum[2] = sum[3] = 0;
434             for (i = 0; i < 2048; i += 4)
435             {
436                 /* DON'T optimize this into DWORD !! (breaks overflow) */
437                 sum[0] += superblock[i+0];
438                 sum[1] += superblock[i+1];
439                 sum[2] += superblock[i+2];
440                 sum[3] += superblock[i+3];
441             }
442             /*
443              * OK, another braindead one... argh. Just believe it.
444              * Me$$ysoft chose to reverse the serial number in NT4/W2K.
445              * It's true and nobody will ever be able to change it.
446              */
447             if (GetVersion() & 0x80000000)
448                 return (sum[3] << 24) | (sum[2] << 16) | (sum[1] << 8) | sum[0];
449             else
450                 return (sum[0] << 24) | (sum[1] << 16) | (sum[2] << 8) | sum[3];
451         }
452     }
453     return 0;
454 }
455
456
457 /**************************************************************************
458  *                              VOLUME_GetAudioCDSerial
459  */
460 static DWORD VOLUME_GetAudioCDSerial( const CDROM_TOC *toc )
461 {
462     DWORD serial = 0;
463     int i;
464
465     for (i = 0; i <= toc->LastTrack - toc->FirstTrack; i++)
466         serial += ((toc->TrackData[i].Address[1] << 16) |
467                    (toc->TrackData[i].Address[2] << 8) |
468                    toc->TrackData[i].Address[3]);
469
470     /*
471      * dwStart, dwEnd collect the beginning and end of the disc respectively, in
472      * frames.
473      * There it is collected for correcting the serial when there are less than
474      * 3 tracks.
475      */
476     if (toc->LastTrack - toc->FirstTrack + 1 < 3)
477     {
478         DWORD dwStart = FRAME_OF_TOC(toc, toc->FirstTrack);
479         DWORD dwEnd = FRAME_OF_TOC(toc, toc->LastTrack + 1);
480         serial += dwEnd - dwStart;
481     }
482     return serial;
483 }
484
485
486 /***********************************************************************
487  *           GetVolumeInformationW   (KERNEL32.@)
488  */
489 BOOL WINAPI GetVolumeInformationW( LPCWSTR root, LPWSTR label, DWORD label_len,
490                                    DWORD *serial, DWORD *filename_len, DWORD *flags,
491                                    LPWSTR fsname, DWORD fsname_len )
492 {
493     static const WCHAR audiocdW[] = {'A','u','d','i','o',' ','C','D',0};
494     static const WCHAR fatW[] = {'F','A','T',0};
495     static const WCHAR ntfsW[] = {'N','T','F','S',0};
496     static const WCHAR cdfsW[] = {'C','D','F','S',0};
497
498     WCHAR device[] = {'\\','\\','.','\\','A',':',0};
499     HANDLE handle;
500     enum fs_type type = FS_UNKNOWN;
501
502     if (!root)
503     {
504         WCHAR path[MAX_PATH];
505         GetCurrentDirectoryW( MAX_PATH, path );
506         device[4] = path[0];
507     }
508     else
509     {
510         if (!root[0] || root[1] != ':')
511         {
512             SetLastError( ERROR_INVALID_NAME );
513             return FALSE;
514         }
515         device[4] = root[0];
516     }
517
518     /* try to open the device */
519
520     handle = CreateFileW( device, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE,
521                           NULL, OPEN_EXISTING, 0, 0 );
522     if (handle != INVALID_HANDLE_VALUE)
523     {
524         BYTE superblock[SUPERBLOCK_SIZE];
525         CDROM_TOC toc;
526         DWORD br;
527
528         /* check for audio CD */
529         /* FIXME: we only check the first track for now */
530         if (DeviceIoControl( handle, IOCTL_CDROM_READ_TOC, NULL, 0, &toc, sizeof(toc), &br, 0 ))
531         {
532             if (!(toc.TrackData[0].Control & 0x04))  /* audio track */
533             {
534                 TRACE( "%s: found audio CD\n", debugstr_w(device) );
535                 if (label) lstrcpynW( label, audiocdW, label_len );
536                 if (serial) *serial = VOLUME_GetAudioCDSerial( &toc );
537                 CloseHandle( handle );
538                 type = FS_ISO9660;
539                 goto fill_fs_info;
540             }
541             type = VOLUME_ReadCDSuperblock( handle, superblock );
542         }
543         else
544         {
545             type = VOLUME_ReadFATSuperblock( handle, superblock );
546             if (type == FS_UNKNOWN) type = VOLUME_ReadCDSuperblock( handle, superblock );
547         }
548         CloseHandle( handle );
549         TRACE( "%s: found fs type %d\n", debugstr_w(device), type );
550         if (type == FS_ERROR) return FALSE;
551
552         if (label && label_len) VOLUME_GetSuperblockLabel( type, superblock, label, label_len );
553         if (serial) *serial = VOLUME_GetSuperblockSerial( type, superblock );
554         goto fill_fs_info;
555     }
556     else TRACE( "cannot open device %s: err %ld\n", debugstr_w(device), GetLastError() );
557
558     /* we couldn't open the device, fallback to default strategy */
559
560     switch(GetDriveTypeW( root ))
561     {
562     case DRIVE_UNKNOWN:
563     case DRIVE_NO_ROOT_DIR:
564         SetLastError( ERROR_NOT_READY );
565         return FALSE;
566     case DRIVE_REMOVABLE:
567     case DRIVE_FIXED:
568     case DRIVE_REMOTE:
569     case DRIVE_RAMDISK:
570         type = FS_UNKNOWN;
571         break;
572     case DRIVE_CDROM:
573         type = FS_ISO9660;
574         break;
575     }
576
577     if (label && label_len)
578     {
579         WCHAR labelW[] = {'A',':','\\','.','w','i','n','d','o','w','s','-','l','a','b','e','l',0};
580
581         labelW[0] = device[4];
582         handle = CreateFileW( labelW, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
583                               OPEN_EXISTING, 0, 0 );
584         if (handle != INVALID_HANDLE_VALUE)
585         {
586             char buffer[256], *p;
587             DWORD size;
588
589             if (!ReadFile( handle, buffer, sizeof(buffer)-1, &size, NULL )) size = 0;
590             CloseHandle( handle );
591             p = buffer + size;
592             while (p > buffer && (p[-1] == ' ' || p[-1] == '\r' || p[-1] == '\n')) p--;
593             *p = 0;
594             if (!MultiByteToWideChar( CP_UNIXCP, 0, buffer, -1, label, label_len ))
595                 label[label_len-1] = 0;
596         }
597         else label[0] = 0;
598     }
599     if (serial)
600     {
601         WCHAR serialW[] = {'A',':','\\','.','w','i','n','d','o','w','s','-','s','e','r','i','a','l',0};
602
603         serialW[0] = device[4];
604         handle = CreateFileW( serialW, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
605                               OPEN_EXISTING, 0, 0 );
606         if (handle != INVALID_HANDLE_VALUE)
607         {
608             char buffer[32];
609             DWORD size;
610
611             if (!ReadFile( handle, buffer, sizeof(buffer)-1, &size, NULL )) size = 0;
612             CloseHandle( handle );
613             buffer[size] = 0;
614             *serial = strtoul( buffer, NULL, 16 );
615         }
616         else *serial = 0;
617     }
618
619 fill_fs_info:  /* now fill in the information that depends on the file system type */
620
621     switch(type)
622     {
623     case FS_ISO9660:
624         if (fsname) lstrcpynW( fsname, cdfsW, fsname_len );
625         if (filename_len) *filename_len = 221;
626         if (flags) *flags = FILE_READ_ONLY_VOLUME;
627         break;
628     case FS_FAT1216:
629     case FS_FAT32:
630         if (fsname) lstrcpynW( fsname, fatW, fsname_len );
631         if (filename_len) *filename_len = 255;
632         if (flags) *flags = FILE_CASE_PRESERVED_NAMES;  /* FIXME */
633         break;
634     default:
635         if (fsname) lstrcpynW( fsname, ntfsW, fsname_len );
636         if (filename_len) *filename_len = 255;
637         if (flags) *flags = FILE_CASE_PRESERVED_NAMES;
638         break;
639     }
640     return TRUE;
641 }
642
643
644 /***********************************************************************
645  *           GetVolumeInformationA   (KERNEL32.@)
646  */
647 BOOL WINAPI GetVolumeInformationA( LPCSTR root, LPSTR label,
648                                    DWORD label_len, DWORD *serial,
649                                    DWORD *filename_len, DWORD *flags,
650                                    LPSTR fsname, DWORD fsname_len )
651 {
652     WCHAR *rootW = NULL;
653     LPWSTR labelW, fsnameW;
654     BOOL ret;
655
656     if (root && !(rootW = FILE_name_AtoW( root, FALSE ))) return FALSE;
657
658     labelW = label ? HeapAlloc(GetProcessHeap(), 0, label_len * sizeof(WCHAR)) : NULL;
659     fsnameW = fsname ? HeapAlloc(GetProcessHeap(), 0, fsname_len * sizeof(WCHAR)) : NULL;
660
661     if ((ret = GetVolumeInformationW(rootW, labelW, label_len, serial,
662                                     filename_len, flags, fsnameW, fsname_len)))
663     {
664         if (label) FILE_name_WtoA( labelW, -1, label, label_len );
665         if (fsname) FILE_name_WtoA( fsnameW, -1, fsname, fsname_len );
666     }
667
668     HeapFree( GetProcessHeap(), 0, labelW );
669     HeapFree( GetProcessHeap(), 0, fsnameW );
670     return ret;
671 }
672
673
674
675 /***********************************************************************
676  *           SetVolumeLabelW   (KERNEL32.@)
677  */
678 BOOL WINAPI SetVolumeLabelW( LPCWSTR root, LPCWSTR label )
679 {
680     WCHAR device[] = {'\\','\\','.','\\','A',':',0};
681     HANDLE handle;
682     enum fs_type type = FS_UNKNOWN;
683
684     if (!root)
685     {
686         WCHAR path[MAX_PATH];
687         GetCurrentDirectoryW( MAX_PATH, path );
688         device[4] = path[0];
689     }
690     else
691     {
692         if (!root[0] || root[1] != ':')
693         {
694             SetLastError( ERROR_INVALID_NAME );
695             return FALSE;
696         }
697         device[4] = root[0];
698     }
699
700     /* try to open the device */
701
702     handle = CreateFileW( device, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
703                           NULL, OPEN_EXISTING, 0, 0 );
704     if (handle == INVALID_HANDLE_VALUE)
705     {
706         /* try read-only */
707         handle = CreateFileW( device, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE,
708                               NULL, OPEN_EXISTING, 0, 0 );
709         if (handle != INVALID_HANDLE_VALUE)
710         {
711             /* device can be read but not written, return error */
712             CloseHandle( handle );
713             SetLastError( ERROR_ACCESS_DENIED );
714             return FALSE;
715         }
716     }
717
718     if (handle != INVALID_HANDLE_VALUE)
719     {
720         BYTE superblock[SUPERBLOCK_SIZE];
721         BOOL ret;
722
723         type = VOLUME_ReadFATSuperblock( handle, superblock );
724         ret = VOLUME_SetSuperblockLabel( type, handle, label );
725         CloseHandle( handle );
726         return ret;
727     }
728     else
729     {
730         TRACE( "cannot open device %s: err %ld\n", debugstr_w(device), GetLastError() );
731         if (GetLastError() == ERROR_ACCESS_DENIED) return FALSE;
732     }
733
734     /* we couldn't open the device, fallback to default strategy */
735
736     switch(GetDriveTypeW( root ))
737     {
738     case DRIVE_UNKNOWN:
739     case DRIVE_NO_ROOT_DIR:
740         SetLastError( ERROR_NOT_READY );
741         break;
742     case DRIVE_REMOVABLE:
743     case DRIVE_FIXED:
744         {
745             WCHAR labelW[] = {'A',':','\\','.','w','i','n','d','o','w','s','-','l','a','b','e','l',0};
746
747             labelW[0] = device[4];
748             handle = CreateFileW( labelW, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
749                                   CREATE_ALWAYS, 0, 0 );
750             if (handle != INVALID_HANDLE_VALUE)
751             {
752                 char buffer[64];
753                 DWORD size;
754
755                 if (!WideCharToMultiByte( CP_UNIXCP, 0, label, -1, buffer, sizeof(buffer), NULL, NULL ))
756                     buffer[sizeof(buffer)-1] = 0;
757                 WriteFile( handle, buffer, strlen(buffer), &size, NULL );
758                 CloseHandle( handle );
759                 return TRUE;
760             }
761             break;
762         }
763     case DRIVE_REMOTE:
764     case DRIVE_RAMDISK:
765     case DRIVE_CDROM:
766         SetLastError( ERROR_ACCESS_DENIED );
767         break;
768     }
769     return FALSE;
770 }
771
772 /***********************************************************************
773  *           SetVolumeLabelA   (KERNEL32.@)
774  */
775 BOOL WINAPI SetVolumeLabelA(LPCSTR root, LPCSTR volname)
776 {
777     WCHAR *rootW = NULL, *volnameW = NULL;
778     BOOL ret;
779
780     if (root && !(rootW = FILE_name_AtoW( root, FALSE ))) return FALSE;
781     if (volname && !(volnameW = FILE_name_AtoW( volname, TRUE ))) return FALSE;
782     ret = SetVolumeLabelW( rootW, volnameW );
783     HeapFree( GetProcessHeap(), 0, volnameW );
784     return ret;
785 }
786
787
788 /***********************************************************************
789  *           GetVolumeNameForVolumeMountPointW   (KERNEL32.@)
790  */
791 BOOL WINAPI GetVolumeNameForVolumeMountPointW(LPCWSTR str, LPWSTR dst, DWORD size)
792 {
793     FIXME("(%s, %p, %lx): stub\n", debugstr_w(str), dst, size);
794     return 0;
795 }
796
797
798 /***********************************************************************
799  *           DefineDosDeviceW       (KERNEL32.@)
800  */
801 BOOL WINAPI DefineDosDeviceW( DWORD flags, LPCWSTR devname, LPCWSTR targetpath )
802 {
803     DWORD len, dosdev;
804     BOOL ret = FALSE;
805     char *path = NULL, *target, *p;
806
807     TRACE("%lx, %s, %s\n", flags, debugstr_w(devname), debugstr_w(targetpath));
808
809     if (!(flags & DDD_REMOVE_DEFINITION))
810     {
811         if (!(flags & DDD_RAW_TARGET_PATH))
812         {
813             FIXME( "(0x%08lx,%s,%s) DDD_RAW_TARGET_PATH flag not set, not supported yet\n",
814                    flags, debugstr_w(devname), debugstr_w(targetpath) );
815             SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
816             return FALSE;
817         }
818
819         len = WideCharToMultiByte( CP_UNIXCP, 0, targetpath, -1, NULL, 0, NULL, NULL );
820         if ((target = HeapAlloc( GetProcessHeap(), 0, len )))
821         {
822             WideCharToMultiByte( CP_UNIXCP, 0, targetpath, -1, target, len, NULL, NULL );
823             for (p = target; *p; p++) if (*p == '\\') *p = '/';
824         }
825         else
826         {
827             SetLastError( ERROR_NOT_ENOUGH_MEMORY );
828             return FALSE;
829         }
830     }
831     else target = NULL;
832
833     /* first check for a DOS device */
834
835     if ((dosdev = RtlIsDosDeviceName_U( devname )))
836     {
837         WCHAR name[5];
838
839         memcpy( name, devname + HIWORD(dosdev)/sizeof(WCHAR), LOWORD(dosdev) );
840         name[LOWORD(dosdev)/sizeof(WCHAR)] = 0;
841         path = get_dos_device_path( name );
842     }
843     else if (isalphaW(devname[0]) && devname[1] == ':' && !devname[2])  /* drive mapping */
844     {
845         path = get_dos_device_path( devname );
846     }
847     else SetLastError( ERROR_FILE_NOT_FOUND );
848
849     if (path)
850     {
851         if (target)
852         {
853             TRACE( "creating symlink %s -> %s\n", path, target );
854             unlink( path );
855             if (!symlink( target, path )) ret = TRUE;
856             else FILE_SetDosError();
857         }
858         else
859         {
860             TRACE( "removing symlink %s\n", path );
861             if (!unlink( path )) ret = TRUE;
862             else FILE_SetDosError();
863         }
864         HeapFree( GetProcessHeap(), 0, path );
865     }
866     HeapFree( GetProcessHeap(), 0, target );
867     return ret;
868 }
869
870
871 /***********************************************************************
872  *           DefineDosDeviceA       (KERNEL32.@)
873  */
874 BOOL WINAPI DefineDosDeviceA(DWORD flags, LPCSTR devname, LPCSTR targetpath)
875 {
876     WCHAR *devW, *targetW = NULL;
877     BOOL ret;
878
879     if (!(devW = FILE_name_AtoW( devname, FALSE ))) return FALSE;
880     if (targetpath && !(targetW = FILE_name_AtoW( targetpath, TRUE ))) return FALSE;
881     ret = DefineDosDeviceW(flags, devW, targetW);
882     HeapFree( GetProcessHeap(), 0, targetW );
883     return ret;
884 }
885
886
887 /***********************************************************************
888  *           QueryDosDeviceW   (KERNEL32.@)
889  *
890  * returns array of strings terminated by \0, terminated by \0
891  */
892 DWORD WINAPI QueryDosDeviceW( LPCWSTR devname, LPWSTR target, DWORD bufsize )
893 {
894     static const WCHAR auxW[] = {'A','U','X',0};
895     static const WCHAR nulW[] = {'N','U','L',0};
896     static const WCHAR prnW[] = {'P','R','N',0};
897     static const WCHAR comW[] = {'C','O','M',0};
898     static const WCHAR lptW[] = {'L','P','T',0};
899     static const WCHAR rootW[] = {'A',':','\\',0};
900     static const WCHAR com0W[] = {'\\','?','?','\\','C','O','M','0',0};
901     static const WCHAR com1W[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\','C','O','M','1',0,0};
902     static const WCHAR lpt1W[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\','L','P','T','1',0,0};
903
904     UNICODE_STRING nt_name;
905     ANSI_STRING unix_name;
906     WCHAR nt_buffer[10];
907     NTSTATUS status;
908
909     if (!bufsize)
910     {
911         SetLastError( ERROR_INSUFFICIENT_BUFFER );
912         return 0;
913     }
914
915     if (devname)
916     {
917         WCHAR *p, name[5];
918         char *path, *link;
919         DWORD dosdev, ret = 0;
920
921         if ((dosdev = RtlIsDosDeviceName_U( devname )))
922         {
923             memcpy( name, devname + HIWORD(dosdev)/sizeof(WCHAR), LOWORD(dosdev) );
924             name[LOWORD(dosdev)/sizeof(WCHAR)] = 0;
925         }
926         else if (devname[0] && devname[1] == ':' && !devname[2])
927         {
928             memcpy( name, devname, 3 * sizeof(WCHAR) );
929         }
930         else
931         {
932             SetLastError( ERROR_BAD_PATHNAME );
933             return 0;
934         }
935
936         if (!(path = get_dos_device_path( name ))) return 0;
937         link = read_symlink( path );
938         HeapFree( GetProcessHeap(), 0, path );
939
940         if (link)
941         {
942             ret = MultiByteToWideChar( CP_UNIXCP, 0, link, -1, target, bufsize );
943             HeapFree( GetProcessHeap(), 0, link );
944         }
945         else if (dosdev)  /* look for device defaults */
946         {
947             if (!strcmpiW( name, auxW ))
948             {
949                 if (bufsize >= sizeof(com1W)/sizeof(WCHAR))
950                 {
951                     memcpy( target, com1W, sizeof(com1W) );
952                     ret = sizeof(com1W)/sizeof(WCHAR);
953                 }
954                 else SetLastError( ERROR_INSUFFICIENT_BUFFER );
955                 return ret;
956             }
957             if (!strcmpiW( name, prnW ))
958             {
959                 if (bufsize >= sizeof(lpt1W)/sizeof(WCHAR))
960                 {
961                     memcpy( target, lpt1W, sizeof(lpt1W) );
962                     ret = sizeof(lpt1W)/sizeof(WCHAR);
963                 }
964                 else SetLastError( ERROR_INSUFFICIENT_BUFFER );
965                 return ret;
966             }
967
968             nt_buffer[0] = '\\';
969             nt_buffer[1] = '?';
970             nt_buffer[2] = '?';
971             nt_buffer[3] = '\\';
972             strcpyW( nt_buffer + 4, name );
973             RtlInitUnicodeString( &nt_name, nt_buffer );
974             status = wine_nt_to_unix_file_name( &nt_name, &unix_name, FILE_OPEN, TRUE );
975             if (status) SetLastError( RtlNtStatusToDosError(status) );
976             else
977             {
978                 ret = MultiByteToWideChar( CP_UNIXCP, 0, unix_name.Buffer, -1, target, bufsize );
979                 RtlFreeAnsiString( &unix_name );
980             }
981         }
982
983         if (ret)
984         {
985             if (ret < bufsize) target[ret++] = 0;  /* add an extra null */
986             for (p = target; *p; p++) if (*p == '/') *p = '\\';
987         }
988
989         return ret;
990     }
991     else  /* return a list of all devices */
992     {
993         WCHAR *p = target;
994         int i;
995
996         if (bufsize <= (sizeof(auxW)+sizeof(nulW)+sizeof(prnW))/sizeof(WCHAR))
997         {
998             SetLastError( ERROR_INSUFFICIENT_BUFFER );
999             return 0;
1000         }
1001
1002         memcpy( p, auxW, sizeof(auxW) );
1003         p += sizeof(auxW) / sizeof(WCHAR);
1004         memcpy( p, nulW, sizeof(nulW) );
1005         p += sizeof(nulW) / sizeof(WCHAR);
1006         memcpy( p, prnW, sizeof(prnW) );
1007         p += sizeof(prnW) / sizeof(WCHAR);
1008
1009         strcpyW( nt_buffer, com0W );
1010         RtlInitUnicodeString( &nt_name, nt_buffer );
1011
1012         for (i = 1; i <= 9; i++)
1013         {
1014             nt_buffer[7] = '0' + i;
1015             if (!wine_nt_to_unix_file_name( &nt_name, &unix_name, FILE_OPEN, TRUE ))
1016             {
1017                 RtlFreeAnsiString( &unix_name );
1018                 if (p + 5 >= target + bufsize)
1019                 {
1020                     SetLastError( ERROR_INSUFFICIENT_BUFFER );
1021                     return 0;
1022                 }
1023                 strcpyW( p, comW );
1024                 p[3] = '0' + i;
1025                 p[4] = 0;
1026                 p += 5;
1027             }
1028         }
1029         strcpyW( nt_buffer + 4, lptW );
1030         for (i = 1; i <= 9; i++)
1031         {
1032             nt_buffer[7] = '0' + i;
1033             if (!wine_nt_to_unix_file_name( &nt_name, &unix_name, FILE_OPEN, TRUE ))
1034             {
1035                 RtlFreeAnsiString( &unix_name );
1036                 if (p + 5 >= target + bufsize)
1037                 {
1038                     SetLastError( ERROR_INSUFFICIENT_BUFFER );
1039                     return 0;
1040                 }
1041                 strcpyW( p, lptW );
1042                 p[3] = '0' + i;
1043                 p[4] = 0;
1044                 p += 5;
1045             }
1046         }
1047
1048         strcpyW( nt_buffer + 4, rootW );
1049         RtlInitUnicodeString( &nt_name, nt_buffer );
1050
1051         for (i = 0; i < 26; i++)
1052         {
1053             nt_buffer[4] = 'a' + i;
1054             if (!wine_nt_to_unix_file_name( &nt_name, &unix_name, FILE_OPEN, TRUE ))
1055             {
1056                 RtlFreeAnsiString( &unix_name );
1057                 if (p + 3 >= target + bufsize)
1058                 {
1059                     SetLastError( ERROR_INSUFFICIENT_BUFFER );
1060                     return 0;
1061                 }
1062                 *p++ = 'A' + i;
1063                 *p++ = ':';
1064                 *p++ = 0;
1065             }
1066         }
1067         *p++ = 0;  /* terminating null */
1068         return p - target;
1069     }
1070 }
1071
1072
1073 /***********************************************************************
1074  *           QueryDosDeviceA   (KERNEL32.@)
1075  *
1076  * returns array of strings terminated by \0, terminated by \0
1077  */
1078 DWORD WINAPI QueryDosDeviceA( LPCSTR devname, LPSTR target, DWORD bufsize )
1079 {
1080     DWORD ret = 0, retW;
1081     WCHAR *devnameW = NULL;
1082     LPWSTR targetW;
1083
1084     if (devname && !(devnameW = FILE_name_AtoW( devname, FALSE ))) return 0;
1085
1086     targetW = HeapAlloc( GetProcessHeap(),0, bufsize * sizeof(WCHAR) );
1087     if (!targetW)
1088     {
1089         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1090         return 0;
1091     }
1092
1093     retW = QueryDosDeviceW(devnameW, targetW, bufsize);
1094
1095     ret = FILE_name_WtoA( targetW, retW, target, bufsize );
1096
1097     HeapFree(GetProcessHeap(), 0, targetW);
1098     return ret;
1099 }
1100
1101
1102 /***********************************************************************
1103  *           GetLogicalDrives   (KERNEL32.@)
1104  */
1105 DWORD WINAPI GetLogicalDrives(void)
1106 {
1107     const char *config_dir = wine_get_config_dir();
1108     struct stat st;
1109     char *buffer, *dev;
1110     DWORD ret = 0;
1111     int i;
1112
1113     if (!(buffer = HeapAlloc( GetProcessHeap(), 0, strlen(config_dir) + sizeof("/dosdevices/a:") )))
1114     {
1115         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1116         return 0;
1117     }
1118     strcpy( buffer, config_dir );
1119     strcat( buffer, "/dosdevices/a:" );
1120     dev = buffer + strlen(buffer) - 2;
1121
1122     for (i = 0; i < 26; i++)
1123     {
1124         *dev = 'a' + i;
1125         if (!stat( buffer, &st )) ret |= (1 << i);
1126     }
1127     HeapFree( GetProcessHeap(), 0, buffer );
1128     return ret;
1129 }
1130
1131
1132 /***********************************************************************
1133  *           GetLogicalDriveStringsA   (KERNEL32.@)
1134  */
1135 UINT WINAPI GetLogicalDriveStringsA( UINT len, LPSTR buffer )
1136 {
1137     DWORD drives = GetLogicalDrives();
1138     UINT drive, count;
1139
1140     for (drive = count = 0; drive < 26; drive++) if (drives & (1 << drive)) count++;
1141     if ((count * 4) + 1 > len) return count * 4 + 1;
1142
1143     for (drive = 0; drive < 26; drive++)
1144     {
1145         if (drives & (1 << drive))
1146         {
1147             *buffer++ = 'a' + drive;
1148             *buffer++ = ':';
1149             *buffer++ = '\\';
1150             *buffer++ = 0;
1151         }
1152     }
1153     *buffer = 0;
1154     return count * 4;
1155 }
1156
1157
1158 /***********************************************************************
1159  *           GetLogicalDriveStringsW   (KERNEL32.@)
1160  */
1161 UINT WINAPI GetLogicalDriveStringsW( UINT len, LPWSTR buffer )
1162 {
1163     DWORD drives = GetLogicalDrives();
1164     UINT drive, count;
1165
1166     for (drive = count = 0; drive < 26; drive++) if (drives & (1 << drive)) count++;
1167     if ((count * 4) + 1 > len) return count * 4 + 1;
1168
1169     for (drive = 0; drive < 26; drive++)
1170     {
1171         if (drives & (1 << drive))
1172         {
1173             *buffer++ = 'a' + drive;
1174             *buffer++ = ':';
1175             *buffer++ = '\\';
1176             *buffer++ = 0;
1177         }
1178     }
1179     *buffer = 0;
1180     return count * 4;
1181 }
1182
1183
1184 /***********************************************************************
1185  *           GetDriveTypeW   (KERNEL32.@)
1186  *
1187  * Returns the type of the disk drive specified. If root is NULL the
1188  * root of the current directory is used.
1189  *
1190  * RETURNS
1191  *
1192  *  Type of drive (from Win32 SDK):
1193  *
1194  *   DRIVE_UNKNOWN     unable to find out anything about the drive
1195  *   DRIVE_NO_ROOT_DIR nonexistent root dir
1196  *   DRIVE_REMOVABLE   the disk can be removed from the machine
1197  *   DRIVE_FIXED       the disk cannot be removed from the machine
1198  *   DRIVE_REMOTE      network disk
1199  *   DRIVE_CDROM       CDROM drive
1200  *   DRIVE_RAMDISK     virtual disk in RAM
1201  */
1202 UINT WINAPI GetDriveTypeW(LPCWSTR root) /* [in] String describing drive */
1203 {
1204     FILE_FS_DEVICE_INFORMATION info;
1205     IO_STATUS_BLOCK io;
1206     NTSTATUS status;
1207     HANDLE handle;
1208     UINT ret;
1209
1210     if (!open_device_root( root, &handle )) return DRIVE_NO_ROOT_DIR;
1211
1212     status = NtQueryVolumeInformationFile( handle, &io, &info, sizeof(info), FileFsDeviceInformation );
1213     NtClose( handle );
1214     if (status != STATUS_SUCCESS)
1215     {
1216         SetLastError( RtlNtStatusToDosError(status) );
1217         ret = DRIVE_UNKNOWN;
1218     }
1219     else if ((ret = get_registry_drive_type( root )) == DRIVE_UNKNOWN)
1220     {
1221         switch (info.DeviceType)
1222         {
1223         case FILE_DEVICE_CD_ROM_FILE_SYSTEM:  ret = DRIVE_CDROM; break;
1224         case FILE_DEVICE_VIRTUAL_DISK:        ret = DRIVE_RAMDISK; break;
1225         case FILE_DEVICE_NETWORK_FILE_SYSTEM: ret = DRIVE_REMOTE; break;
1226         case FILE_DEVICE_DISK_FILE_SYSTEM:
1227             if (info.Characteristics & FILE_REMOTE_DEVICE) ret = DRIVE_REMOTE;
1228             else if (info.Characteristics & FILE_REMOVABLE_MEDIA) ret = DRIVE_REMOVABLE;
1229             else ret = DRIVE_FIXED;
1230             break;
1231         default:
1232             ret = DRIVE_UNKNOWN;
1233             break;
1234         }
1235     }
1236     TRACE( "%s -> %d\n", debugstr_w(root), ret );
1237     return ret;
1238 }
1239
1240
1241 /***********************************************************************
1242  *           GetDriveTypeA   (KERNEL32.@)
1243  *
1244  * See GetDriveTypeW.
1245  */
1246 UINT WINAPI GetDriveTypeA( LPCSTR root )
1247 {
1248     WCHAR *rootW = NULL;
1249
1250     if (root && !(rootW = FILE_name_AtoW( root, FALSE ))) return DRIVE_NO_ROOT_DIR;
1251     return GetDriveTypeW( rootW );
1252 }
1253
1254
1255 /***********************************************************************
1256  *           GetDiskFreeSpaceExW   (KERNEL32.@)
1257  *
1258  *  This function is used to acquire the size of the available and
1259  *  total space on a logical volume.
1260  *
1261  * RETURNS
1262  *
1263  *  Zero on failure, nonzero upon success. Use GetLastError to obtain
1264  *  detailed error information.
1265  *
1266  */
1267 BOOL WINAPI GetDiskFreeSpaceExW( LPCWSTR root, PULARGE_INTEGER avail,
1268                                  PULARGE_INTEGER total, PULARGE_INTEGER totalfree )
1269 {
1270     FILE_FS_SIZE_INFORMATION info;
1271     IO_STATUS_BLOCK io;
1272     NTSTATUS status;
1273     HANDLE handle;
1274     UINT units;
1275
1276     TRACE( "%s,%p,%p,%p\n", debugstr_w(root), avail, total, totalfree );
1277
1278     if (!open_device_root( root, &handle )) return FALSE;
1279
1280     status = NtQueryVolumeInformationFile( handle, &io, &info, sizeof(info), FileFsSizeInformation );
1281     NtClose( handle );
1282     if (status != STATUS_SUCCESS)
1283     {
1284         SetLastError( RtlNtStatusToDosError(status) );
1285         return FALSE;
1286     }
1287
1288     units = info.SectorsPerAllocationUnit * info.BytesPerSector;
1289     if (total) total->QuadPart = info.TotalAllocationUnits.QuadPart * units;
1290     if (totalfree) totalfree->QuadPart = info.AvailableAllocationUnits.QuadPart * units;
1291     /* FIXME: this one should take quotas into account */
1292     if (avail) avail->QuadPart = info.AvailableAllocationUnits.QuadPart * units;
1293     return TRUE;
1294 }
1295
1296
1297 /***********************************************************************
1298  *           GetDiskFreeSpaceExA   (KERNEL32.@)
1299  *
1300  * See GetDiskFreeSpaceExW.
1301  */
1302 BOOL WINAPI GetDiskFreeSpaceExA( LPCSTR root, PULARGE_INTEGER avail,
1303                                  PULARGE_INTEGER total, PULARGE_INTEGER totalfree )
1304 {
1305     WCHAR *rootW = NULL;
1306
1307     if (root && !(rootW = FILE_name_AtoW( root, FALSE ))) return FALSE;
1308     return GetDiskFreeSpaceExW( rootW, avail, total, totalfree );
1309 }
1310
1311
1312 /***********************************************************************
1313  *           GetDiskFreeSpaceW   (KERNEL32.@)
1314  */
1315 BOOL WINAPI GetDiskFreeSpaceW( LPCWSTR root, LPDWORD cluster_sectors,
1316                                LPDWORD sector_bytes, LPDWORD free_clusters,
1317                                LPDWORD total_clusters )
1318 {
1319     FILE_FS_SIZE_INFORMATION info;
1320     IO_STATUS_BLOCK io;
1321     NTSTATUS status;
1322     HANDLE handle;
1323     UINT units;
1324
1325     TRACE( "%s,%p,%p,%p,%p\n", debugstr_w(root),
1326            cluster_sectors, sector_bytes, free_clusters, total_clusters );
1327
1328     if (!open_device_root( root, &handle )) return FALSE;
1329
1330     status = NtQueryVolumeInformationFile( handle, &io, &info, sizeof(info), FileFsSizeInformation );
1331     NtClose( handle );
1332     if (status != STATUS_SUCCESS)
1333     {
1334         SetLastError( RtlNtStatusToDosError(status) );
1335         return FALSE;
1336     }
1337
1338     units = info.SectorsPerAllocationUnit * info.BytesPerSector;
1339
1340     if( GetVersion() & 0x80000000) {    /* win3.x, 9x, ME */
1341         /* cap the size and available at 2GB as per specs */
1342         if (info.TotalAllocationUnits.QuadPart * units > 0x7fffffff) {
1343             info.TotalAllocationUnits.QuadPart = 0x7fffffff / units;
1344             if (info.AvailableAllocationUnits.QuadPart * units > 0x7fffffff)
1345                 info.AvailableAllocationUnits.QuadPart = 0x7fffffff / units;
1346         }
1347         /* nr. of clusters is always <= 65335 */
1348         while( info.TotalAllocationUnits.QuadPart > 65535 ) {
1349             info.TotalAllocationUnits.QuadPart /= 2;
1350             info.AvailableAllocationUnits.QuadPart /= 2;
1351             info.SectorsPerAllocationUnit *= 2;
1352         }
1353     }
1354
1355     if (cluster_sectors) *cluster_sectors = info.SectorsPerAllocationUnit;
1356     if (sector_bytes) *sector_bytes = info.BytesPerSector;
1357     if (free_clusters) *free_clusters = info.AvailableAllocationUnits.u.LowPart;
1358     if (total_clusters) *total_clusters = info.TotalAllocationUnits.u.LowPart;
1359     return TRUE;
1360 }
1361
1362
1363 /***********************************************************************
1364  *           GetDiskFreeSpaceA   (KERNEL32.@)
1365  */
1366 BOOL WINAPI GetDiskFreeSpaceA( LPCSTR root, LPDWORD cluster_sectors,
1367                                LPDWORD sector_bytes, LPDWORD free_clusters,
1368                                LPDWORD total_clusters )
1369 {
1370     WCHAR *rootW = NULL;
1371
1372     if (root && !(rootW = FILE_name_AtoW( root, FALSE ))) return FALSE;
1373     return GetDiskFreeSpaceW( rootW, cluster_sectors, sector_bytes, free_clusters, total_clusters );
1374 }
1375
1376 /***********************************************************************
1377  *           GetVolumePathNameA   (KERNEL32.@)
1378  */
1379 BOOL WINAPI GetVolumePathNameA(LPCSTR filename, LPSTR volumepathname, DWORD buflen)
1380 {
1381     FIXME("(%s, %p, %ld), stub!\n", debugstr_a(filename), volumepathname, buflen);
1382     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1383     return FALSE;
1384 }
1385
1386 /***********************************************************************
1387  *           GetVolumePathNameW   (KERNEL32.@)
1388  */
1389 BOOL WINAPI GetVolumePathNameW(LPCWSTR filename, LPWSTR volumepathname, DWORD buflen)
1390 {
1391     FIXME("(%s, %p, %ld), stub!\n", debugstr_w(filename), volumepathname, buflen);
1392     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1393     return FALSE;
1394 }