Pass characters to get_file_name() helper, not bytes.
[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     if (!(flags & DDD_REMOVE_DEFINITION))
808     {
809         if (!(flags & DDD_RAW_TARGET_PATH))
810         {
811             FIXME( "(0x%08lx,%s,%s) DDD_RAW_TARGET_PATH flag not set, not supported yet\n",
812                    flags, debugstr_w(devname), debugstr_w(targetpath) );
813             SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
814             return FALSE;
815         }
816
817         len = WideCharToMultiByte( CP_UNIXCP, 0, targetpath, -1, NULL, 0, NULL, NULL );
818         if ((target = HeapAlloc( GetProcessHeap(), 0, len )))
819         {
820             WideCharToMultiByte( CP_UNIXCP, 0, targetpath, -1, target, len, NULL, NULL );
821             for (p = target; *p; p++) if (*p == '\\') *p = '/';
822         }
823         else
824         {
825             SetLastError( ERROR_NOT_ENOUGH_MEMORY );
826             return FALSE;
827         }
828     }
829     else target = NULL;
830
831     /* first check for a DOS device */
832
833     if ((dosdev = RtlIsDosDeviceName_U( devname )))
834     {
835         WCHAR name[5];
836
837         memcpy( name, devname + HIWORD(dosdev)/sizeof(WCHAR), LOWORD(dosdev) );
838         name[LOWORD(dosdev)/sizeof(WCHAR)] = 0;
839         path = get_dos_device_path( name );
840     }
841     else if (isalphaW(devname[0]) && devname[1] == ':' && !devname[2])  /* drive mapping */
842     {
843         path = get_dos_device_path( devname );
844     }
845     else SetLastError( ERROR_FILE_NOT_FOUND );
846
847     if (path)
848     {
849         if (target)
850         {
851             TRACE( "creating symlink %s -> %s\n", path, target );
852             unlink( path );
853             if (!symlink( target, path )) ret = TRUE;
854             else FILE_SetDosError();
855         }
856         else
857         {
858             TRACE( "removing symlink %s\n", path );
859             if (!unlink( path )) ret = TRUE;
860             else FILE_SetDosError();
861         }
862         HeapFree( GetProcessHeap(), 0, path );
863     }
864     HeapFree( GetProcessHeap(), 0, target );
865     return ret;
866 }
867
868
869 /***********************************************************************
870  *           DefineDosDeviceA       (KERNEL32.@)
871  */
872 BOOL WINAPI DefineDosDeviceA(DWORD flags, LPCSTR devname, LPCSTR targetpath)
873 {
874     WCHAR *devW, *targetW = NULL;
875     BOOL ret;
876
877     if (!(devW = FILE_name_AtoW( devname, FALSE ))) return FALSE;
878     if (targetpath && !(targetW = FILE_name_AtoW( targetpath, TRUE ))) return FALSE;
879     ret = DefineDosDeviceW(flags, devW, targetW);
880     HeapFree( GetProcessHeap(), 0, targetW );
881     return ret;
882 }
883
884
885 /***********************************************************************
886  *           QueryDosDeviceW   (KERNEL32.@)
887  *
888  * returns array of strings terminated by \0, terminated by \0
889  */
890 DWORD WINAPI QueryDosDeviceW( LPCWSTR devname, LPWSTR target, DWORD bufsize )
891 {
892     static const WCHAR auxW[] = {'A','U','X',0};
893     static const WCHAR nulW[] = {'N','U','L',0};
894     static const WCHAR prnW[] = {'P','R','N',0};
895     static const WCHAR comW[] = {'C','O','M',0};
896     static const WCHAR lptW[] = {'L','P','T',0};
897     static const WCHAR rootW[] = {'A',':','\\',0};
898     static const WCHAR com0W[] = {'\\','?','?','\\','C','O','M','0',0};
899     static const WCHAR com1W[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\','C','O','M','1',0,0};
900     static const WCHAR lpt1W[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\','L','P','T','1',0,0};
901
902     UNICODE_STRING nt_name;
903     ANSI_STRING unix_name;
904     WCHAR nt_buffer[10];
905     NTSTATUS status;
906
907     if (!bufsize)
908     {
909         SetLastError( ERROR_INSUFFICIENT_BUFFER );
910         return 0;
911     }
912
913     if (devname)
914     {
915         WCHAR *p, name[5];
916         char *path, *link;
917         DWORD dosdev, ret = 0;
918
919         if ((dosdev = RtlIsDosDeviceName_U( devname )))
920         {
921             memcpy( name, devname + HIWORD(dosdev)/sizeof(WCHAR), LOWORD(dosdev) );
922             name[LOWORD(dosdev)/sizeof(WCHAR)] = 0;
923         }
924         else if (devname[0] && devname[1] == ':' && !devname[2])
925         {
926             memcpy( name, devname, 3 * sizeof(WCHAR) );
927         }
928         else
929         {
930             SetLastError( ERROR_BAD_PATHNAME );
931             return 0;
932         }
933
934         if (!(path = get_dos_device_path( name ))) return 0;
935         link = read_symlink( path );
936         HeapFree( GetProcessHeap(), 0, path );
937
938         if (link)
939         {
940             ret = MultiByteToWideChar( CP_UNIXCP, 0, link, -1, target, bufsize );
941             HeapFree( GetProcessHeap(), 0, link );
942         }
943         else if (dosdev)  /* look for device defaults */
944         {
945             if (!strcmpiW( name, auxW ))
946             {
947                 if (bufsize >= sizeof(com1W)/sizeof(WCHAR))
948                 {
949                     memcpy( target, com1W, sizeof(com1W) );
950                     ret = sizeof(com1W)/sizeof(WCHAR);
951                 }
952                 else SetLastError( ERROR_INSUFFICIENT_BUFFER );
953                 return ret;
954             }
955             if (!strcmpiW( name, prnW ))
956             {
957                 if (bufsize >= sizeof(lpt1W)/sizeof(WCHAR))
958                 {
959                     memcpy( target, lpt1W, sizeof(lpt1W) );
960                     ret = sizeof(lpt1W)/sizeof(WCHAR);
961                 }
962                 else SetLastError( ERROR_INSUFFICIENT_BUFFER );
963                 return ret;
964             }
965
966             nt_buffer[0] = '\\';
967             nt_buffer[1] = '?';
968             nt_buffer[2] = '?';
969             nt_buffer[3] = '\\';
970             strcpyW( nt_buffer + 4, name );
971             RtlInitUnicodeString( &nt_name, nt_buffer );
972             status = wine_nt_to_unix_file_name( &nt_name, &unix_name, FILE_OPEN, TRUE );
973             if (status) SetLastError( RtlNtStatusToDosError(status) );
974             else
975             {
976                 ret = MultiByteToWideChar( CP_UNIXCP, 0, unix_name.Buffer, -1, target, bufsize );
977                 RtlFreeAnsiString( &unix_name );
978             }
979         }
980
981         if (ret)
982         {
983             if (ret < bufsize) target[ret++] = 0;  /* add an extra null */
984             for (p = target; *p; p++) if (*p == '/') *p = '\\';
985         }
986
987         return ret;
988     }
989     else  /* return a list of all devices */
990     {
991         WCHAR *p = target;
992         int i;
993
994         if (bufsize <= (sizeof(auxW)+sizeof(nulW)+sizeof(prnW))/sizeof(WCHAR))
995         {
996             SetLastError( ERROR_INSUFFICIENT_BUFFER );
997             return 0;
998         }
999
1000         memcpy( p, auxW, sizeof(auxW) );
1001         p += sizeof(auxW) / sizeof(WCHAR);
1002         memcpy( p, nulW, sizeof(nulW) );
1003         p += sizeof(nulW) / sizeof(WCHAR);
1004         memcpy( p, prnW, sizeof(prnW) );
1005         p += sizeof(prnW) / sizeof(WCHAR);
1006
1007         strcpyW( nt_buffer, com0W );
1008         RtlInitUnicodeString( &nt_name, nt_buffer );
1009
1010         for (i = 1; i <= 9; i++)
1011         {
1012             nt_buffer[7] = '0' + i;
1013             if (!wine_nt_to_unix_file_name( &nt_name, &unix_name, FILE_OPEN, TRUE ))
1014             {
1015                 RtlFreeAnsiString( &unix_name );
1016                 if (p + 5 >= target + bufsize)
1017                 {
1018                     SetLastError( ERROR_INSUFFICIENT_BUFFER );
1019                     return 0;
1020                 }
1021                 strcpyW( p, comW );
1022                 p[3] = '0' + i;
1023                 p[4] = 0;
1024                 p += 5;
1025             }
1026         }
1027         strcpyW( nt_buffer + 4, lptW );
1028         for (i = 1; i <= 9; i++)
1029         {
1030             nt_buffer[7] = '0' + i;
1031             if (!wine_nt_to_unix_file_name( &nt_name, &unix_name, FILE_OPEN, TRUE ))
1032             {
1033                 RtlFreeAnsiString( &unix_name );
1034                 if (p + 5 >= target + bufsize)
1035                 {
1036                     SetLastError( ERROR_INSUFFICIENT_BUFFER );
1037                     return 0;
1038                 }
1039                 strcpyW( p, lptW );
1040                 p[3] = '0' + i;
1041                 p[4] = 0;
1042                 p += 5;
1043             }
1044         }
1045
1046         strcpyW( nt_buffer + 4, rootW );
1047         RtlInitUnicodeString( &nt_name, nt_buffer );
1048
1049         for (i = 0; i < 26; i++)
1050         {
1051             nt_buffer[4] = 'a' + i;
1052             if (!wine_nt_to_unix_file_name( &nt_name, &unix_name, FILE_OPEN, TRUE ))
1053             {
1054                 RtlFreeAnsiString( &unix_name );
1055                 if (p + 3 >= target + bufsize)
1056                 {
1057                     SetLastError( ERROR_INSUFFICIENT_BUFFER );
1058                     return 0;
1059                 }
1060                 *p++ = 'A' + i;
1061                 *p++ = ':';
1062                 *p++ = 0;
1063             }
1064         }
1065         *p++ = 0;  /* terminating null */
1066         return p - target;
1067     }
1068 }
1069
1070
1071 /***********************************************************************
1072  *           QueryDosDeviceA   (KERNEL32.@)
1073  *
1074  * returns array of strings terminated by \0, terminated by \0
1075  */
1076 DWORD WINAPI QueryDosDeviceA( LPCSTR devname, LPSTR target, DWORD bufsize )
1077 {
1078     DWORD ret = 0, retW;
1079     WCHAR *devnameW = NULL;
1080     LPWSTR targetW;
1081
1082     if (devname && !(devnameW = FILE_name_AtoW( devname, FALSE ))) return 0;
1083
1084     targetW = HeapAlloc( GetProcessHeap(),0, bufsize * sizeof(WCHAR) );
1085     if (!targetW)
1086     {
1087         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1088         return 0;
1089     }
1090
1091     retW = QueryDosDeviceW(devnameW, targetW, bufsize);
1092
1093     ret = FILE_name_WtoA( targetW, retW, target, bufsize );
1094
1095     HeapFree(GetProcessHeap(), 0, targetW);
1096     return ret;
1097 }
1098
1099
1100 /***********************************************************************
1101  *           GetLogicalDrives   (KERNEL32.@)
1102  */
1103 DWORD WINAPI GetLogicalDrives(void)
1104 {
1105     const char *config_dir = wine_get_config_dir();
1106     struct stat st;
1107     char *buffer, *dev;
1108     DWORD ret = 0;
1109     int i;
1110
1111     if (!(buffer = HeapAlloc( GetProcessHeap(), 0, strlen(config_dir) + sizeof("/dosdevices/a:") )))
1112     {
1113         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1114         return 0;
1115     }
1116     strcpy( buffer, config_dir );
1117     strcat( buffer, "/dosdevices/a:" );
1118     dev = buffer + strlen(buffer) - 2;
1119
1120     for (i = 0; i < 26; i++)
1121     {
1122         *dev = 'a' + i;
1123         if (!stat( buffer, &st )) ret |= (1 << i);
1124     }
1125     HeapFree( GetProcessHeap(), 0, buffer );
1126     return ret;
1127 }
1128
1129
1130 /***********************************************************************
1131  *           GetLogicalDriveStringsA   (KERNEL32.@)
1132  */
1133 UINT WINAPI GetLogicalDriveStringsA( UINT len, LPSTR buffer )
1134 {
1135     DWORD drives = GetLogicalDrives();
1136     UINT drive, count;
1137
1138     for (drive = count = 0; drive < 26; drive++) if (drives & (1 << drive)) count++;
1139     if ((count * 4) + 1 > len) return count * 4 + 1;
1140
1141     for (drive = 0; drive < 26; drive++)
1142     {
1143         if (drives & (1 << drive))
1144         {
1145             *buffer++ = 'a' + drive;
1146             *buffer++ = ':';
1147             *buffer++ = '\\';
1148             *buffer++ = 0;
1149         }
1150     }
1151     *buffer = 0;
1152     return count * 4;
1153 }
1154
1155
1156 /***********************************************************************
1157  *           GetLogicalDriveStringsW   (KERNEL32.@)
1158  */
1159 UINT WINAPI GetLogicalDriveStringsW( UINT len, LPWSTR buffer )
1160 {
1161     DWORD drives = GetLogicalDrives();
1162     UINT drive, count;
1163
1164     for (drive = count = 0; drive < 26; drive++) if (drives & (1 << drive)) count++;
1165     if ((count * 4) + 1 > len) return count * 4 + 1;
1166
1167     for (drive = 0; drive < 26; drive++)
1168     {
1169         if (drives & (1 << drive))
1170         {
1171             *buffer++ = 'a' + drive;
1172             *buffer++ = ':';
1173             *buffer++ = '\\';
1174             *buffer++ = 0;
1175         }
1176     }
1177     *buffer = 0;
1178     return count * 4;
1179 }
1180
1181
1182 /***********************************************************************
1183  *           GetDriveTypeW   (KERNEL32.@)
1184  *
1185  * Returns the type of the disk drive specified. If root is NULL the
1186  * root of the current directory is used.
1187  *
1188  * RETURNS
1189  *
1190  *  Type of drive (from Win32 SDK):
1191  *
1192  *   DRIVE_UNKNOWN     unable to find out anything about the drive
1193  *   DRIVE_NO_ROOT_DIR nonexistent root dir
1194  *   DRIVE_REMOVABLE   the disk can be removed from the machine
1195  *   DRIVE_FIXED       the disk cannot be removed from the machine
1196  *   DRIVE_REMOTE      network disk
1197  *   DRIVE_CDROM       CDROM drive
1198  *   DRIVE_RAMDISK     virtual disk in RAM
1199  */
1200 UINT WINAPI GetDriveTypeW(LPCWSTR root) /* [in] String describing drive */
1201 {
1202     FILE_FS_DEVICE_INFORMATION info;
1203     IO_STATUS_BLOCK io;
1204     NTSTATUS status;
1205     HANDLE handle;
1206     UINT ret;
1207
1208     if (!open_device_root( root, &handle )) return DRIVE_NO_ROOT_DIR;
1209
1210     status = NtQueryVolumeInformationFile( handle, &io, &info, sizeof(info), FileFsDeviceInformation );
1211     NtClose( handle );
1212     if (status != STATUS_SUCCESS)
1213     {
1214         SetLastError( RtlNtStatusToDosError(status) );
1215         ret = DRIVE_UNKNOWN;
1216     }
1217     else if ((ret = get_registry_drive_type( root )) == DRIVE_UNKNOWN)
1218     {
1219         switch (info.DeviceType)
1220         {
1221         case FILE_DEVICE_CD_ROM_FILE_SYSTEM:  ret = DRIVE_CDROM; break;
1222         case FILE_DEVICE_VIRTUAL_DISK:        ret = DRIVE_RAMDISK; break;
1223         case FILE_DEVICE_NETWORK_FILE_SYSTEM: ret = DRIVE_REMOTE; break;
1224         case FILE_DEVICE_DISK_FILE_SYSTEM:
1225             if (info.Characteristics & FILE_REMOTE_DEVICE) ret = DRIVE_REMOTE;
1226             else if (info.Characteristics & FILE_REMOVABLE_MEDIA) ret = DRIVE_REMOVABLE;
1227             else ret = DRIVE_FIXED;
1228             break;
1229         default:
1230             ret = DRIVE_UNKNOWN;
1231             break;
1232         }
1233     }
1234     TRACE( "%s -> %d\n", debugstr_w(root), ret );
1235     return ret;
1236 }
1237
1238
1239 /***********************************************************************
1240  *           GetDriveTypeA   (KERNEL32.@)
1241  */
1242 UINT WINAPI GetDriveTypeA( LPCSTR root )
1243 {
1244     WCHAR *rootW = NULL;
1245
1246     if (root && !(rootW = FILE_name_AtoW( root, FALSE ))) return DRIVE_NO_ROOT_DIR;
1247     return GetDriveTypeW( rootW );
1248 }
1249
1250
1251 /***********************************************************************
1252  *           GetDiskFreeSpaceExW   (KERNEL32.@)
1253  *
1254  *  This function is used to acquire the size of the available and
1255  *  total space on a logical volume.
1256  *
1257  * RETURNS
1258  *
1259  *  Zero on failure, nonzero upon success. Use GetLastError to obtain
1260  *  detailed error information.
1261  *
1262  */
1263 BOOL WINAPI GetDiskFreeSpaceExW( LPCWSTR root, PULARGE_INTEGER avail,
1264                                  PULARGE_INTEGER total, PULARGE_INTEGER totalfree )
1265 {
1266     FILE_FS_SIZE_INFORMATION info;
1267     IO_STATUS_BLOCK io;
1268     NTSTATUS status;
1269     HANDLE handle;
1270     UINT units;
1271
1272     TRACE( "%s,%p,%p,%p\n", debugstr_w(root), avail, total, totalfree );
1273
1274     if (!open_device_root( root, &handle )) return FALSE;
1275
1276     status = NtQueryVolumeInformationFile( handle, &io, &info, sizeof(info), FileFsSizeInformation );
1277     NtClose( handle );
1278     if (status != STATUS_SUCCESS)
1279     {
1280         SetLastError( RtlNtStatusToDosError(status) );
1281         return FALSE;
1282     }
1283
1284     units = info.SectorsPerAllocationUnit * info.BytesPerSector;
1285     if (total) total->QuadPart = info.TotalAllocationUnits.QuadPart * units;
1286     if (totalfree) totalfree->QuadPart = info.AvailableAllocationUnits.QuadPart * units;
1287     /* FIXME: this one should take quotas into account */
1288     if (avail) avail->QuadPart = info.AvailableAllocationUnits.QuadPart * units;
1289     return TRUE;
1290 }
1291
1292
1293 /***********************************************************************
1294  *           GetDiskFreeSpaceExA   (KERNEL32.@)
1295  */
1296 BOOL WINAPI GetDiskFreeSpaceExA( LPCSTR root, PULARGE_INTEGER avail,
1297                                  PULARGE_INTEGER total, PULARGE_INTEGER totalfree )
1298 {
1299     WCHAR *rootW = NULL;
1300
1301     if (root && !(rootW = FILE_name_AtoW( root, FALSE ))) return FALSE;
1302     return GetDiskFreeSpaceExW( rootW, avail, total, totalfree );
1303 }
1304
1305
1306 /***********************************************************************
1307  *           GetDiskFreeSpaceW   (KERNEL32.@)
1308  */
1309 BOOL WINAPI GetDiskFreeSpaceW( LPCWSTR root, LPDWORD cluster_sectors,
1310                                LPDWORD sector_bytes, LPDWORD free_clusters,
1311                                LPDWORD total_clusters )
1312 {
1313     FILE_FS_SIZE_INFORMATION info;
1314     IO_STATUS_BLOCK io;
1315     NTSTATUS status;
1316     HANDLE handle;
1317     UINT units;
1318
1319     TRACE( "%s,%p,%p,%p,%p\n", debugstr_w(root),
1320            cluster_sectors, sector_bytes, free_clusters, total_clusters );
1321
1322     if (!open_device_root( root, &handle )) return FALSE;
1323
1324     status = NtQueryVolumeInformationFile( handle, &io, &info, sizeof(info), FileFsSizeInformation );
1325     NtClose( handle );
1326     if (status != STATUS_SUCCESS)
1327     {
1328         SetLastError( RtlNtStatusToDosError(status) );
1329         return FALSE;
1330     }
1331
1332     units = info.SectorsPerAllocationUnit * info.BytesPerSector;
1333
1334     /* cap the size and available at 2GB as per specs */
1335     if (info.AvailableAllocationUnits.QuadPart * units > 0x7fffffff)
1336         info.AvailableAllocationUnits.QuadPart = 0x7fffffff / units;
1337     if (info.TotalAllocationUnits.QuadPart * units > 0x7fffffff)
1338         info.TotalAllocationUnits.QuadPart = 0x7fffffff / units;
1339
1340     if (cluster_sectors) *cluster_sectors = info.SectorsPerAllocationUnit;
1341     if (sector_bytes) *sector_bytes = info.BytesPerSector;
1342     if (free_clusters) *free_clusters = info.AvailableAllocationUnits.u.LowPart;
1343     if (total_clusters) *total_clusters = info.TotalAllocationUnits.u.LowPart;
1344     return TRUE;
1345 }
1346
1347
1348 /***********************************************************************
1349  *           GetDiskFreeSpaceA   (KERNEL32.@)
1350  */
1351 BOOL WINAPI GetDiskFreeSpaceA( LPCSTR root, LPDWORD cluster_sectors,
1352                                LPDWORD sector_bytes, LPDWORD free_clusters,
1353                                LPDWORD total_clusters )
1354 {
1355     WCHAR *rootW = NULL;
1356
1357     if (root && !(rootW = FILE_name_AtoW( root, FALSE ))) return FALSE;
1358     return GetDiskFreeSpaceW( rootW, cluster_sectors, sector_bytes, free_clusters, total_clusters );
1359 }
1360
1361 /***********************************************************************
1362  *           GetVolumePathNameA   (KERNEL32.@)
1363  */
1364 BOOL WINAPI GetVolumePathNameA(LPCSTR filename, LPSTR volumepathname, DWORD buflen)
1365 {
1366     FIXME("(%s, %p, %ld), stub!\n", debugstr_a(filename), volumepathname, buflen);
1367     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1368     return FALSE;
1369 }
1370
1371 /***********************************************************************
1372  *           GetVolumePathNameW   (KERNEL32.@)
1373  */
1374 BOOL WINAPI GetVolumePathNameW(LPCWSTR filename, LPWSTR volumepathname, DWORD buflen)
1375 {
1376     FIXME("(%s, %p, %ld), stub!\n", debugstr_w(filename), volumepathname, buflen);
1377     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1378     return FALSE;
1379 }