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