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