kernel32: Use the NT name to open the device in GetVolumeInformationW.
[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 #define WINE_MOUNTMGR_EXTENSIONS
41 #include "ddk/mountmgr.h"
42 #include "kernel_private.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 #define SYMBOLIC_LINK_QUERY 0x0001
51
52 #define CDFRAMES_PERSEC         75
53 #define CDFRAMES_PERMIN         (CDFRAMES_PERSEC * 60)
54 #define FRAME_OF_ADDR(a)        ((a)[1] * CDFRAMES_PERMIN + (a)[2] * CDFRAMES_PERSEC + (a)[3])
55 #define FRAME_OF_TOC(toc, idx)  FRAME_OF_ADDR((toc)->TrackData[(idx) - (toc)->FirstTrack].Address)
56
57 #define GETWORD(buf,off)  MAKEWORD(buf[(off)],buf[(off+1)])
58 #define GETLONG(buf,off)  MAKELONG(GETWORD(buf,off),GETWORD(buf,off+2))
59
60 enum fs_type
61 {
62     FS_ERROR,    /* error accessing the device */
63     FS_UNKNOWN,  /* unknown file system */
64     FS_FAT1216,
65     FS_FAT32,
66     FS_ISO9660
67 };
68
69 /* read a Unix symlink; returned buffer must be freed by caller */
70 static char *read_symlink( const char *path )
71 {
72     char *buffer;
73     int ret, size = 128;
74
75     for (;;)
76     {
77         if (!(buffer = HeapAlloc( GetProcessHeap(), 0, size )))
78         {
79             SetLastError( ERROR_NOT_ENOUGH_MEMORY );
80             return 0;
81         }
82         ret = readlink( path, buffer, size );
83         if (ret == -1)
84         {
85             FILE_SetDosError();
86             HeapFree( GetProcessHeap(), 0, buffer );
87             return 0;
88         }
89         if (ret != size)
90         {
91             buffer[ret] = 0;
92             return buffer;
93         }
94         HeapFree( GetProcessHeap(), 0, buffer );
95         size *= 2;
96     }
97 }
98
99 /* get the path of a dos device symlink in the $WINEPREFIX/dosdevices directory */
100 static char *get_dos_device_path( LPCWSTR name )
101 {
102     const char *config_dir = wine_get_config_dir();
103     char *buffer, *dev;
104     int i;
105
106     if (!(buffer = HeapAlloc( GetProcessHeap(), 0,
107                               strlen(config_dir) + sizeof("/dosdevices/") + 5 )))
108     {
109         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
110         return NULL;
111     }
112     strcpy( buffer, config_dir );
113     strcat( buffer, "/dosdevices/" );
114     dev = buffer + strlen(buffer);
115     /* no codepage conversion, DOS device names are ASCII anyway */
116     for (i = 0; i < 5; i++)
117         if (!(dev[i] = (char)tolowerW(name[i]))) break;
118     dev[5] = 0;
119     return buffer;
120 }
121
122 /* read the contents of an NT symlink object */
123 static NTSTATUS read_nt_symlink( const WCHAR *name, WCHAR *target, DWORD size )
124 {
125     NTSTATUS status;
126     OBJECT_ATTRIBUTES attr;
127     UNICODE_STRING nameW;
128     HANDLE handle;
129
130     attr.Length = sizeof(attr);
131     attr.RootDirectory = 0;
132     attr.Attributes = OBJ_CASE_INSENSITIVE;
133     attr.ObjectName = &nameW;
134     attr.SecurityDescriptor = NULL;
135     attr.SecurityQualityOfService = NULL;
136     RtlInitUnicodeString( &nameW, name );
137
138     if (!(status = NtOpenSymbolicLinkObject( &handle, SYMBOLIC_LINK_QUERY, &attr )))
139     {
140         UNICODE_STRING targetW;
141         targetW.Buffer = target;
142         targetW.MaximumLength = (size - 1) * sizeof(WCHAR);
143         status = NtQuerySymbolicLinkObject( handle, &targetW, NULL );
144         if (!status) target[targetW.Length / sizeof(WCHAR)] = 0;
145         NtClose( handle );
146     }
147     return status;
148 }
149
150 /* open a handle to a device root */
151 static BOOL open_device_root( LPCWSTR root, HANDLE *handle )
152 {
153     static const WCHAR default_rootW[] = {'\\',0};
154     UNICODE_STRING nt_name;
155     OBJECT_ATTRIBUTES attr;
156     IO_STATUS_BLOCK io;
157     NTSTATUS status;
158
159     if (!root) root = default_rootW;
160     if (!RtlDosPathNameToNtPathName_U( root, &nt_name, NULL, NULL ))
161     {
162         SetLastError( ERROR_PATH_NOT_FOUND );
163         return FALSE;
164     }
165     attr.Length = sizeof(attr);
166     attr.RootDirectory = 0;
167     attr.Attributes = OBJ_CASE_INSENSITIVE;
168     attr.ObjectName = &nt_name;
169     attr.SecurityDescriptor = NULL;
170     attr.SecurityQualityOfService = NULL;
171
172     status = NtOpenFile( handle, 0, &attr, &io, 0,
173                          FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT );
174     RtlFreeUnicodeString( &nt_name );
175     if (status != STATUS_SUCCESS)
176     {
177         SetLastError( RtlNtStatusToDosError(status) );
178         return FALSE;
179     }
180     return TRUE;
181 }
182
183 /* query the type of a drive from the mount manager */
184 static DWORD get_mountmgr_drive_type( LPCWSTR root )
185 {
186     HANDLE mgr;
187     struct mountmgr_unix_drive data;
188
189     memset( &data, 0, sizeof(data) );
190     if (root) data.letter = root[0];
191     else
192     {
193         WCHAR curdir[MAX_PATH];
194         GetCurrentDirectoryW( MAX_PATH, curdir );
195         if (curdir[1] != ':' || curdir[2] != '\\') return DRIVE_UNKNOWN;
196         data.letter = curdir[0];
197     }
198
199     mgr = CreateFileW( MOUNTMGR_DOS_DEVICE_NAME, GENERIC_READ,
200                        FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0 );
201     if (mgr == INVALID_HANDLE_VALUE) return DRIVE_UNKNOWN;
202
203     if (!DeviceIoControl( mgr, IOCTL_MOUNTMGR_QUERY_UNIX_DRIVE, &data, sizeof(data), &data,
204                           sizeof(data), NULL, NULL ) && GetLastError() != ERROR_MORE_DATA)
205         data.type = DRIVE_UNKNOWN;
206
207     CloseHandle( mgr );
208     return data.type;
209 }
210
211 /* get the label by reading it from a file at the root of the filesystem */
212 static void get_filesystem_label( const WCHAR *device, WCHAR *label, DWORD len )
213 {
214     HANDLE handle;
215     WCHAR labelW[] = {'A',':','\\','.','w','i','n','d','o','w','s','-','l','a','b','e','l',0};
216
217     labelW[0] = device[4];
218     handle = CreateFileW( labelW, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
219                           OPEN_EXISTING, 0, 0 );
220     if (handle != INVALID_HANDLE_VALUE)
221     {
222         char buffer[256], *p;
223         DWORD size;
224
225         if (!ReadFile( handle, buffer, sizeof(buffer)-1, &size, NULL )) size = 0;
226         CloseHandle( handle );
227         p = buffer + size;
228         while (p > buffer && (p[-1] == ' ' || p[-1] == '\r' || p[-1] == '\n')) p--;
229         *p = 0;
230         if (!MultiByteToWideChar( CP_UNIXCP, 0, buffer, -1, label, len ))
231             label[len-1] = 0;
232     }
233     else label[0] = 0;
234 }
235
236 /* get the serial number by reading it from a file at the root of the filesystem */
237 static DWORD get_filesystem_serial( const WCHAR *device )
238 {
239     HANDLE handle;
240     WCHAR serialW[] = {'A',':','\\','.','w','i','n','d','o','w','s','-','s','e','r','i','a','l',0};
241
242     serialW[0] = device[4];
243     handle = CreateFileW( serialW, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
244                           OPEN_EXISTING, 0, 0 );
245     if (handle != INVALID_HANDLE_VALUE)
246     {
247         char buffer[32];
248         DWORD size;
249
250         if (!ReadFile( handle, buffer, sizeof(buffer)-1, &size, NULL )) size = 0;
251         CloseHandle( handle );
252         buffer[size] = 0;
253         return strtoul( buffer, NULL, 16 );
254     }
255     else return 0;
256 }
257
258
259 /******************************************************************
260  *              VOLUME_FindCdRomDataBestVoldesc
261  */
262 static DWORD VOLUME_FindCdRomDataBestVoldesc( HANDLE handle )
263 {
264     BYTE cur_vd_type, max_vd_type = 0;
265     BYTE buffer[0x800];
266     DWORD size, offs, best_offs = 0, extra_offs = 0;
267
268     for (offs = 0x8000; offs <= 0x9800; offs += 0x800)
269     {
270         /* if 'CDROM' occurs at position 8, this is a pre-iso9660 cd, and
271          * the volume label is displaced forward by 8
272          */
273         if (SetFilePointer( handle, offs, NULL, FILE_BEGIN ) != offs) break;
274         if (!ReadFile( handle, buffer, sizeof(buffer), &size, NULL )) break;
275         if (size != sizeof(buffer)) break;
276         /* check for non-ISO9660 signature */
277         if (!memcmp( buffer + 11, "ROM", 3 )) extra_offs = 8;
278         cur_vd_type = buffer[extra_offs];
279         if (cur_vd_type == 0xff) /* voldesc set terminator */
280             break;
281         if (cur_vd_type > max_vd_type)
282         {
283             max_vd_type = cur_vd_type;
284             best_offs = offs + extra_offs;
285         }
286     }
287     return best_offs;
288 }
289
290
291 /***********************************************************************
292  *           VOLUME_ReadFATSuperblock
293  */
294 static enum fs_type VOLUME_ReadFATSuperblock( HANDLE handle, BYTE *buff )
295 {
296     DWORD size;
297
298     /* try a fixed disk, with a FAT partition */
299     if (SetFilePointer( handle, 0, NULL, FILE_BEGIN ) != 0 ||
300         !ReadFile( handle, buff, SUPERBLOCK_SIZE, &size, NULL ))
301     {
302         if (GetLastError() == ERROR_BAD_DEV_TYPE) return FS_UNKNOWN;  /* not a real device */
303         return FS_ERROR;
304     }
305
306     if (size < SUPERBLOCK_SIZE) return FS_UNKNOWN;
307
308     /* FIXME: do really all FAT have their name beginning with
309      * "FAT" ? (At least FAT12, FAT16 and FAT32 have :)
310      */
311     if (!memcmp(buff+0x36, "FAT", 3) || !memcmp(buff+0x52, "FAT", 3))
312     {
313         /* guess which type of FAT we have */
314         int reasonable;
315         unsigned int sectors,
316                      sect_per_fat,
317                      total_sectors,
318                      num_boot_sectors,
319                      num_fats,
320                      num_root_dir_ents,
321                      bytes_per_sector,
322                      sectors_per_cluster,
323                      nclust;
324         sect_per_fat = GETWORD(buff, 0x16);
325         if (!sect_per_fat) sect_per_fat = GETLONG(buff, 0x24);
326         total_sectors = GETWORD(buff, 0x13);
327         if (!total_sectors)
328             total_sectors = GETLONG(buff, 0x20);
329         num_boot_sectors = GETWORD(buff, 0x0e);
330         num_fats =  buff[0x10];
331         num_root_dir_ents = GETWORD(buff, 0x11);
332         bytes_per_sector = GETWORD(buff, 0x0b);
333         sectors_per_cluster = buff[0x0d];
334         /* check if the parameters are reasonable and will not cause
335          * arithmetic errors in the calculation */
336         reasonable = num_boot_sectors < total_sectors &&
337                      num_fats < 16 &&
338                      bytes_per_sector >= 512 && bytes_per_sector % 512 == 0 &&
339                      sectors_per_cluster > 1;
340         if (!reasonable) return FS_UNKNOWN;
341         sectors =  total_sectors - num_boot_sectors - num_fats * sect_per_fat -
342             (num_root_dir_ents * 32 + bytes_per_sector - 1) / bytes_per_sector;
343         nclust = sectors / sectors_per_cluster;
344         if ((buff[0x42] == 0x28 || buff[0x42] == 0x29) &&
345                 !memcmp(buff+0x52, "FAT", 3)) return FS_FAT32;
346         if (nclust < 65525)
347         {
348             if ((buff[0x26] == 0x28 || buff[0x26] == 0x29) &&
349                     !memcmp(buff+0x36, "FAT", 3))
350                 return FS_FAT1216;
351         }
352     }
353     return FS_UNKNOWN;
354 }
355
356
357 /***********************************************************************
358  *           VOLUME_ReadCDSuperblock
359  */
360 static enum fs_type VOLUME_ReadCDSuperblock( HANDLE handle, BYTE *buff )
361 {
362     DWORD size, offs = VOLUME_FindCdRomDataBestVoldesc( handle );
363
364     if (!offs) return FS_UNKNOWN;
365
366     if (SetFilePointer( handle, offs, NULL, FILE_BEGIN ) != offs ||
367         !ReadFile( handle, buff, SUPERBLOCK_SIZE, &size, NULL ) ||
368         size != SUPERBLOCK_SIZE)
369         return FS_ERROR;
370
371     /* check for iso9660 present */
372     if (!memcmp(&buff[1], "CD001", 5)) return FS_ISO9660;
373     return FS_UNKNOWN;
374 }
375
376
377 /**************************************************************************
378  *                              VOLUME_GetSuperblockLabel
379  */
380 static void VOLUME_GetSuperblockLabel( const WCHAR *device, enum fs_type type, const BYTE *superblock,
381                                        WCHAR *label, DWORD len )
382 {
383     const BYTE *label_ptr = NULL;
384     DWORD label_len;
385
386     switch(type)
387     {
388     case FS_ERROR:
389         label_len = 0;
390         break;
391     case FS_UNKNOWN:
392         get_filesystem_label( device, label, len );
393         return;
394     case FS_FAT1216:
395         label_ptr = superblock + 0x2b;
396         label_len = 11;
397         break;
398     case FS_FAT32:
399         label_ptr = superblock + 0x47;
400         label_len = 11;
401         break;
402     case FS_ISO9660:
403         {
404             BYTE ver = superblock[0x5a];
405
406             if (superblock[0x58] == 0x25 && superblock[0x59] == 0x2f &&  /* Unicode ID */
407                 ((ver == 0x40) || (ver == 0x43) || (ver == 0x45)))
408             { /* yippee, unicode */
409                 unsigned int i;
410
411                 if (len > 17) len = 17;
412                 for (i = 0; i < len-1; i++)
413                     label[i] = (superblock[40+2*i] << 8) | superblock[41+2*i];
414                 label[i] = 0;
415                 while (i && label[i-1] == ' ') label[--i] = 0;
416                 return;
417             }
418             label_ptr = superblock + 40;
419             label_len = 32;
420             break;
421         }
422     }
423     if (label_len) RtlMultiByteToUnicodeN( label, (len-1) * sizeof(WCHAR),
424                                            &label_len, (LPCSTR)label_ptr, label_len );
425     label_len /= sizeof(WCHAR);
426     label[label_len] = 0;
427     while (label_len && label[label_len-1] == ' ') label[--label_len] = 0;
428 }
429
430
431 /**************************************************************************
432  *                              VOLUME_GetSuperblockSerial
433  */
434 static DWORD VOLUME_GetSuperblockSerial( const WCHAR *device, enum fs_type type, const BYTE *superblock )
435 {
436     switch(type)
437     {
438     case FS_ERROR:
439         break;
440     case FS_UNKNOWN:
441         return get_filesystem_serial( device );
442     case FS_FAT1216:
443         return GETLONG( superblock, 0x27 );
444     case FS_FAT32:
445         return GETLONG( superblock, 0x33 );
446     case FS_ISO9660:
447         {
448             BYTE sum[4];
449             int i;
450
451             sum[0] = sum[1] = sum[2] = sum[3] = 0;
452             for (i = 0; i < 2048; i += 4)
453             {
454                 /* DON'T optimize this into DWORD !! (breaks overflow) */
455                 sum[0] += superblock[i+0];
456                 sum[1] += superblock[i+1];
457                 sum[2] += superblock[i+2];
458                 sum[3] += superblock[i+3];
459             }
460             /*
461              * OK, another braindead one... argh. Just believe it.
462              * Me$$ysoft chose to reverse the serial number in NT4/W2K.
463              * It's true and nobody will ever be able to change it.
464              */
465             if (GetVersion() & 0x80000000)
466                 return (sum[3] << 24) | (sum[2] << 16) | (sum[1] << 8) | sum[0];
467             else
468                 return (sum[0] << 24) | (sum[1] << 16) | (sum[2] << 8) | sum[3];
469         }
470     }
471     return 0;
472 }
473
474
475 /**************************************************************************
476  *                              VOLUME_GetAudioCDSerial
477  */
478 static DWORD VOLUME_GetAudioCDSerial( const CDROM_TOC *toc )
479 {
480     DWORD serial = 0;
481     int i;
482
483     for (i = 0; i <= toc->LastTrack - toc->FirstTrack; i++)
484         serial += ((toc->TrackData[i].Address[1] << 16) |
485                    (toc->TrackData[i].Address[2] << 8) |
486                    toc->TrackData[i].Address[3]);
487
488     /*
489      * dwStart, dwEnd collect the beginning and end of the disc respectively, in
490      * frames.
491      * There it is collected for correcting the serial when there are less than
492      * 3 tracks.
493      */
494     if (toc->LastTrack - toc->FirstTrack + 1 < 3)
495     {
496         DWORD dwStart = FRAME_OF_TOC(toc, toc->FirstTrack);
497         DWORD dwEnd = FRAME_OF_TOC(toc, toc->LastTrack + 1);
498         serial += dwEnd - dwStart;
499     }
500     return serial;
501 }
502
503
504 /***********************************************************************
505  *           GetVolumeInformationW   (KERNEL32.@)
506  */
507 BOOL WINAPI GetVolumeInformationW( LPCWSTR root, LPWSTR label, DWORD label_len,
508                                    DWORD *serial, DWORD *filename_len, DWORD *flags,
509                                    LPWSTR fsname, DWORD fsname_len )
510 {
511     static const WCHAR audiocdW[] = {'A','u','d','i','o',' ','C','D',0};
512     static const WCHAR fatW[] = {'F','A','T',0};
513     static const WCHAR fat32W[] = {'F','A','T','3','2',0};
514     static const WCHAR ntfsW[] = {'N','T','F','S',0};
515     static const WCHAR cdfsW[] = {'C','D','F','S',0};
516     static const WCHAR default_rootW[] = {'\\',0};
517
518     WCHAR device[] = {'\\','\\','.','\\','A',':',0};
519     HANDLE handle;
520     NTSTATUS status;
521     UNICODE_STRING nt_name;
522     IO_STATUS_BLOCK io;
523     OBJECT_ATTRIBUTES attr;
524     WCHAR *p;
525     enum fs_type type = FS_UNKNOWN;
526     BOOL ret = FALSE;
527
528     if (!root) root = default_rootW;
529     if (!RtlDosPathNameToNtPathName_U( root, &nt_name, NULL, NULL ))
530     {
531         SetLastError( ERROR_PATH_NOT_FOUND );
532         return FALSE;
533     }
534     /* there must be exactly one backslash in the name, at the end */
535     p = memchrW( nt_name.Buffer + 4, '\\', (nt_name.Length - 4) / sizeof(WCHAR) );
536     if (p != nt_name.Buffer + nt_name.Length / sizeof(WCHAR) - 1)
537     {
538         SetLastError( ERROR_INVALID_NAME );
539         goto done;
540     }
541     device[4] = nt_name.Buffer[4];
542
543     /* try to open the device */
544
545     attr.Length = sizeof(attr);
546     attr.RootDirectory = 0;
547     attr.Attributes = OBJ_CASE_INSENSITIVE;
548     attr.ObjectName = &nt_name;
549     attr.SecurityDescriptor = NULL;
550     attr.SecurityQualityOfService = NULL;
551
552     nt_name.Length -= sizeof(WCHAR);  /* without trailing slash */
553     status = NtOpenFile( &handle, GENERIC_READ, &attr, &io, FILE_SHARE_READ | FILE_SHARE_WRITE,
554                          FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT );
555     nt_name.Length += sizeof(WCHAR);
556
557     if (status == STATUS_SUCCESS)
558     {
559         BYTE superblock[SUPERBLOCK_SIZE];
560         CDROM_TOC toc;
561         DWORD br;
562
563         /* check for audio CD */
564         /* FIXME: we only check the first track for now */
565         if (DeviceIoControl( handle, IOCTL_CDROM_READ_TOC, NULL, 0, &toc, sizeof(toc), &br, 0 ))
566         {
567             if (!(toc.TrackData[0].Control & 0x04))  /* audio track */
568             {
569                 TRACE( "%s: found audio CD\n", debugstr_w(nt_name.Buffer) );
570                 if (label) lstrcpynW( label, audiocdW, label_len );
571                 if (serial) *serial = VOLUME_GetAudioCDSerial( &toc );
572                 CloseHandle( handle );
573                 type = FS_ISO9660;
574                 goto fill_fs_info;
575             }
576             type = VOLUME_ReadCDSuperblock( handle, superblock );
577         }
578         else
579         {
580             type = VOLUME_ReadFATSuperblock( handle, superblock );
581             if (type == FS_UNKNOWN) type = VOLUME_ReadCDSuperblock( handle, superblock );
582         }
583         CloseHandle( handle );
584         TRACE( "%s: found fs type %d\n", debugstr_w(nt_name.Buffer), type );
585         if (type == FS_ERROR) goto done;
586
587         if (label && label_len) VOLUME_GetSuperblockLabel( device, type, superblock, label, label_len );
588         if (serial) *serial = VOLUME_GetSuperblockSerial( device, type, superblock );
589         goto fill_fs_info;
590     }
591     else TRACE( "cannot open device %s: err %d\n", debugstr_w(nt_name.Buffer), GetLastError() );
592
593     /* we couldn't open the device, fallback to default strategy */
594
595     switch(GetDriveTypeW( root ))
596     {
597     case DRIVE_UNKNOWN:
598     case DRIVE_NO_ROOT_DIR:
599         SetLastError( ERROR_NOT_READY );
600         goto done;
601     case DRIVE_REMOVABLE:
602     case DRIVE_FIXED:
603     case DRIVE_REMOTE:
604     case DRIVE_RAMDISK:
605         type = FS_UNKNOWN;
606         break;
607     case DRIVE_CDROM:
608         type = FS_ISO9660;
609         break;
610     }
611
612     if (label && label_len) get_filesystem_label( device, label, label_len );
613     if (serial) *serial = get_filesystem_serial( device );
614
615 fill_fs_info:  /* now fill in the information that depends on the file system type */
616
617     switch(type)
618     {
619     case FS_ISO9660:
620         if (fsname) lstrcpynW( fsname, cdfsW, fsname_len );
621         if (filename_len) *filename_len = 221;
622         if (flags) *flags = FILE_READ_ONLY_VOLUME;
623         break;
624     case FS_FAT1216:
625         if (fsname) lstrcpynW( fsname, fatW, fsname_len );
626     case FS_FAT32:
627         if (type == FS_FAT32 && fsname) lstrcpynW( fsname, fat32W, fsname_len );
628         if (filename_len) *filename_len = 255;
629         if (flags) *flags = FILE_CASE_PRESERVED_NAMES;  /* FIXME */
630         break;
631     default:
632         if (fsname) lstrcpynW( fsname, ntfsW, fsname_len );
633         if (filename_len) *filename_len = 255;
634         if (flags) *flags = FILE_CASE_PRESERVED_NAMES;
635         break;
636     }
637     ret = TRUE;
638
639 done:
640     RtlFreeUnicodeString( &nt_name );
641     return ret;
642 }
643
644
645 /***********************************************************************
646  *           GetVolumeInformationA   (KERNEL32.@)
647  */
648 BOOL WINAPI GetVolumeInformationA( LPCSTR root, LPSTR label,
649                                    DWORD label_len, DWORD *serial,
650                                    DWORD *filename_len, DWORD *flags,
651                                    LPSTR fsname, DWORD fsname_len )
652 {
653     WCHAR *rootW = NULL;
654     LPWSTR labelW, fsnameW;
655     BOOL ret;
656
657     if (root && !(rootW = FILE_name_AtoW( root, FALSE ))) return FALSE;
658
659     labelW = label ? HeapAlloc(GetProcessHeap(), 0, label_len * sizeof(WCHAR)) : NULL;
660     fsnameW = fsname ? HeapAlloc(GetProcessHeap(), 0, fsname_len * sizeof(WCHAR)) : NULL;
661
662     if ((ret = GetVolumeInformationW(rootW, labelW, label_len, serial,
663                                     filename_len, flags, fsnameW, fsname_len)))
664     {
665         if (label) FILE_name_WtoA( labelW, -1, label, label_len );
666         if (fsname) FILE_name_WtoA( fsnameW, -1, fsname, fsname_len );
667     }
668
669     HeapFree( GetProcessHeap(), 0, labelW );
670     HeapFree( GetProcessHeap(), 0, fsnameW );
671     return ret;
672 }
673
674
675
676 /***********************************************************************
677  *           SetVolumeLabelW   (KERNEL32.@)
678  */
679 BOOL WINAPI SetVolumeLabelW( LPCWSTR root, LPCWSTR label )
680 {
681     WCHAR device[] = {'\\','\\','.','\\','A',':',0};
682     HANDLE handle;
683     enum fs_type type = FS_UNKNOWN;
684
685     if (!root)
686     {
687         WCHAR path[MAX_PATH];
688         GetCurrentDirectoryW( MAX_PATH, path );
689         device[4] = path[0];
690     }
691     else
692     {
693         if (!root[0] || root[1] != ':')
694         {
695             SetLastError( ERROR_INVALID_NAME );
696             return FALSE;
697         }
698         device[4] = root[0];
699     }
700
701     /* try to open the device */
702
703     handle = CreateFileW( device, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE,
704                           NULL, OPEN_EXISTING, 0, 0 );
705     if (handle != INVALID_HANDLE_VALUE)
706     {
707         BYTE superblock[SUPERBLOCK_SIZE];
708
709         type = VOLUME_ReadFATSuperblock( handle, superblock );
710         if (type == FS_UNKNOWN) type = VOLUME_ReadCDSuperblock( handle, superblock );
711         CloseHandle( handle );
712         if (type != FS_UNKNOWN)
713         {
714             /* we can't set the label on FAT or CDROM file systems */
715             TRACE( "cannot set label on device %s type %d\n", debugstr_w(device), type );
716             SetLastError( ERROR_ACCESS_DENIED );
717             return FALSE;
718         }
719     }
720     else
721     {
722         TRACE( "cannot open device %s: err %d\n", debugstr_w(device), GetLastError() );
723         if (GetLastError() == ERROR_ACCESS_DENIED) return FALSE;
724     }
725
726     /* we couldn't open the device, fallback to default strategy */
727
728     switch(GetDriveTypeW( root ))
729     {
730     case DRIVE_UNKNOWN:
731     case DRIVE_NO_ROOT_DIR:
732         SetLastError( ERROR_NOT_READY );
733         break;
734     case DRIVE_REMOVABLE:
735     case DRIVE_FIXED:
736         {
737             WCHAR labelW[] = {'A',':','\\','.','w','i','n','d','o','w','s','-','l','a','b','e','l',0};
738
739             labelW[0] = device[4];
740
741             if (!label[0])  /* delete label file when setting an empty label */
742                 return DeleteFileW( labelW ) || GetLastError() == ERROR_FILE_NOT_FOUND;
743
744             handle = CreateFileW( labelW, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
745                                   CREATE_ALWAYS, 0, 0 );
746             if (handle != INVALID_HANDLE_VALUE)
747             {
748                 char buffer[64];
749                 DWORD size;
750
751                 if (!WideCharToMultiByte( CP_UNIXCP, 0, label, -1, buffer, sizeof(buffer)-1, NULL, NULL ))
752                     buffer[sizeof(buffer)-2] = 0;
753                 strcat( buffer, "\n" );
754                 WriteFile( handle, buffer, strlen(buffer), &size, NULL );
755                 CloseHandle( handle );
756                 return TRUE;
757             }
758             break;
759         }
760     case DRIVE_REMOTE:
761     case DRIVE_RAMDISK:
762     case DRIVE_CDROM:
763         SetLastError( ERROR_ACCESS_DENIED );
764         break;
765     }
766     return FALSE;
767 }
768
769 /***********************************************************************
770  *           SetVolumeLabelA   (KERNEL32.@)
771  */
772 BOOL WINAPI SetVolumeLabelA(LPCSTR root, LPCSTR volname)
773 {
774     WCHAR *rootW = NULL, *volnameW = NULL;
775     BOOL ret;
776
777     if (root && !(rootW = FILE_name_AtoW( root, FALSE ))) return FALSE;
778     if (volname && !(volnameW = FILE_name_AtoW( volname, TRUE ))) return FALSE;
779     ret = SetVolumeLabelW( rootW, volnameW );
780     HeapFree( GetProcessHeap(), 0, volnameW );
781     return ret;
782 }
783
784
785 /***********************************************************************
786  *           GetVolumeNameForVolumeMountPointA   (KERNEL32.@)
787  */
788 BOOL WINAPI GetVolumeNameForVolumeMountPointA( LPCSTR path, LPSTR volume, DWORD size )
789 {
790     BOOL ret;
791     WCHAR volumeW[50], *pathW = NULL;
792     DWORD len = min( sizeof(volumeW) / sizeof(WCHAR), size );
793
794     TRACE("(%s, %p, %x)\n", debugstr_a(path), volume, size);
795
796     if (!path || !(pathW = FILE_name_AtoW( path, TRUE )))
797         return FALSE;
798
799     if ((ret = GetVolumeNameForVolumeMountPointW( pathW, volumeW, len )))
800         FILE_name_WtoA( volumeW, -1, volume, len );
801
802     HeapFree( GetProcessHeap(), 0, pathW );
803     return ret;
804 }
805
806 /***********************************************************************
807  *           GetVolumeNameForVolumeMountPointW   (KERNEL32.@)
808  */
809 BOOL WINAPI GetVolumeNameForVolumeMountPointW( LPCWSTR path, LPWSTR volume, DWORD size )
810 {
811     static const WCHAR prefixW[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\',0};
812     static const WCHAR volumeW[] = {'\\','?','?','\\','V','o','l','u','m','e','{',0};
813     static const WCHAR trailingW[] = {'\\',0};
814
815     MOUNTMGR_MOUNT_POINT *input = NULL, *o1;
816     MOUNTMGR_MOUNT_POINTS *output = NULL;
817     WCHAR *p;
818     char *r;
819     DWORD i, i_size = 1024, o_size = 1024;
820     WCHAR *nonpersist_name;
821     WCHAR symlink_name[MAX_PATH];
822     NTSTATUS status;
823     HANDLE mgr = INVALID_HANDLE_VALUE;
824     BOOL ret = FALSE;
825
826     TRACE("(%s, %p, %x)\n", debugstr_w(path), volume, size);
827     if (path[lstrlenW(path)-1] != '\\')
828     {
829         SetLastError( ERROR_INVALID_NAME );
830         return FALSE;
831     }
832
833     if (size < 50)
834     {
835         SetLastError( ERROR_FILENAME_EXCED_RANGE );
836         return FALSE;
837     }
838     /* if length of input is > 3 then it must be a mounted folder */
839     if (lstrlenW(path) > 3)
840     {
841         FIXME("Mounted Folders are not yet supported\n");
842         SetLastError( ERROR_NOT_A_REPARSE_POINT );
843         return FALSE;
844     }
845
846     mgr = CreateFileW( MOUNTMGR_DOS_DEVICE_NAME, 0, FILE_SHARE_READ,
847                         NULL, OPEN_EXISTING, 0, 0 );
848     if (mgr == INVALID_HANDLE_VALUE) return FALSE;
849
850     if (!(input = HeapAlloc( GetProcessHeap(), 0, i_size )))
851     {
852         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
853         goto err_ret;
854     }
855
856     if (!(output = HeapAlloc( GetProcessHeap(), 0, o_size )))
857     {
858         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
859         goto err_ret;
860     }
861
862     /* construct the symlink name as "\DosDevices\C:" */
863     lstrcpyW( symlink_name, prefixW );
864     lstrcatW( symlink_name, path );
865     symlink_name[lstrlenW(symlink_name)-1] = 0;
866
867     /* Take the mount point and get the "nonpersistent name" */
868     /* We will then take that and get the volume name        */
869     nonpersist_name = (WCHAR *)(input + 1);
870     status = read_nt_symlink( symlink_name, nonpersist_name, i_size - sizeof(*input) );
871     TRACE("read_nt_symlink got stat=%x, for %s, got <%s>\n", status,
872             debugstr_w(symlink_name), debugstr_w(nonpersist_name));
873     if (status != STATUS_SUCCESS)
874     {
875         SetLastError( ERROR_FILE_NOT_FOUND );
876         goto err_ret;
877     }
878
879     /* Now take the "nonpersistent name" and ask the mountmgr  */
880     /* to give us all the mount points.  One of them will be   */
881     /* the volume name  (format of \??\Volume{).               */
882     memset( input, 0, sizeof(*input) );  /* clear all input parameters */
883     input->DeviceNameOffset = sizeof(*input);
884     input->DeviceNameLength = lstrlenW( nonpersist_name) * sizeof(WCHAR);
885     i_size = input->DeviceNameOffset + input->DeviceNameLength;
886
887     output->Size = o_size;
888
889     /* now get the true volume name from the mountmgr   */
890     if (!DeviceIoControl( mgr, IOCTL_MOUNTMGR_QUERY_POINTS, input, i_size,
891                         output, o_size, NULL, NULL ))
892         goto err_ret;
893
894     /* Verify and return the data, note string is not null terminated  */
895     TRACE("found %d matching mount points\n", output->NumberOfMountPoints);
896     if (output->NumberOfMountPoints < 1)
897     {
898         SetLastError( ERROR_NO_VOLUME_ID );
899         goto err_ret;
900     }
901     o1 = &output->MountPoints[0];
902
903     /* look for the volume name in returned values  */
904     for(i=0;i<output->NumberOfMountPoints;i++)
905     {
906         p = (WCHAR*)((char *)output + o1->SymbolicLinkNameOffset);
907         r = (char *)output + o1->UniqueIdOffset;
908         TRACE("found symlink=%s, unique=%s, devname=%s\n",
909             debugstr_wn(p, o1->SymbolicLinkNameLength/sizeof(WCHAR)),
910             debugstr_an(r, o1->UniqueIdLength),
911             debugstr_wn((WCHAR*)((char *)output + o1->DeviceNameOffset),
912                             o1->DeviceNameLength/sizeof(WCHAR)));
913
914         if (!strncmpW( p, volumeW, (sizeof(volumeW)-1)/sizeof(WCHAR) ))
915         {
916             /* is there space in the return variable ?? */
917             if ((o1->SymbolicLinkNameLength/sizeof(WCHAR))+2 > size)
918             {
919                 SetLastError( ERROR_FILENAME_EXCED_RANGE );
920                 goto err_ret;
921             }
922             memcpy( volume, p, o1->SymbolicLinkNameLength );
923             volume[o1->SymbolicLinkNameLength / sizeof(WCHAR)] = 0;
924             lstrcatW( volume, trailingW );
925             /* change second char from '?' to '\'  */
926             volume[1] = '\\';
927             ret = TRUE;
928             break;
929         }
930         o1++;
931     }
932
933 err_ret:
934     HeapFree( GetProcessHeap(), 0, input );
935     HeapFree( GetProcessHeap(), 0, output );
936     CloseHandle( mgr );
937     return ret;
938 }
939
940 /***********************************************************************
941  *           DefineDosDeviceW       (KERNEL32.@)
942  */
943 BOOL WINAPI DefineDosDeviceW( DWORD flags, LPCWSTR devname, LPCWSTR targetpath )
944 {
945     DWORD len, dosdev;
946     BOOL ret = FALSE;
947     char *path = NULL, *target, *p;
948
949     TRACE("%x, %s, %s\n", flags, debugstr_w(devname), debugstr_w(targetpath));
950
951     if (!(flags & DDD_REMOVE_DEFINITION))
952     {
953         if (!(flags & DDD_RAW_TARGET_PATH))
954         {
955             FIXME( "(0x%08x,%s,%s) DDD_RAW_TARGET_PATH flag not set, not supported yet\n",
956                    flags, debugstr_w(devname), debugstr_w(targetpath) );
957             SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
958             return FALSE;
959         }
960
961         len = WideCharToMultiByte( CP_UNIXCP, 0, targetpath, -1, NULL, 0, NULL, NULL );
962         if ((target = HeapAlloc( GetProcessHeap(), 0, len )))
963         {
964             WideCharToMultiByte( CP_UNIXCP, 0, targetpath, -1, target, len, NULL, NULL );
965             for (p = target; *p; p++) if (*p == '\\') *p = '/';
966         }
967         else
968         {
969             SetLastError( ERROR_NOT_ENOUGH_MEMORY );
970             return FALSE;
971         }
972     }
973     else target = NULL;
974
975     /* first check for a DOS device */
976
977     if ((dosdev = RtlIsDosDeviceName_U( devname )))
978     {
979         WCHAR name[5];
980
981         memcpy( name, devname + HIWORD(dosdev)/sizeof(WCHAR), LOWORD(dosdev) );
982         name[LOWORD(dosdev)/sizeof(WCHAR)] = 0;
983         path = get_dos_device_path( name );
984     }
985     else if (isalphaW(devname[0]) && devname[1] == ':' && !devname[2])  /* drive mapping */
986     {
987         path = get_dos_device_path( devname );
988     }
989     else SetLastError( ERROR_FILE_NOT_FOUND );
990
991     if (path)
992     {
993         if (target)
994         {
995             TRACE( "creating symlink %s -> %s\n", path, target );
996             unlink( path );
997             if (!symlink( target, path )) ret = TRUE;
998             else FILE_SetDosError();
999         }
1000         else
1001         {
1002             TRACE( "removing symlink %s\n", path );
1003             if (!unlink( path )) ret = TRUE;
1004             else FILE_SetDosError();
1005         }
1006         HeapFree( GetProcessHeap(), 0, path );
1007     }
1008     HeapFree( GetProcessHeap(), 0, target );
1009     return ret;
1010 }
1011
1012
1013 /***********************************************************************
1014  *           DefineDosDeviceA       (KERNEL32.@)
1015  */
1016 BOOL WINAPI DefineDosDeviceA(DWORD flags, LPCSTR devname, LPCSTR targetpath)
1017 {
1018     WCHAR *devW, *targetW = NULL;
1019     BOOL ret;
1020
1021     if (!(devW = FILE_name_AtoW( devname, FALSE ))) return FALSE;
1022     if (targetpath && !(targetW = FILE_name_AtoW( targetpath, TRUE ))) return FALSE;
1023     ret = DefineDosDeviceW(flags, devW, targetW);
1024     HeapFree( GetProcessHeap(), 0, targetW );
1025     return ret;
1026 }
1027
1028
1029 /***********************************************************************
1030  *           QueryDosDeviceW   (KERNEL32.@)
1031  *
1032  * returns array of strings terminated by \0, terminated by \0
1033  */
1034 DWORD WINAPI QueryDosDeviceW( LPCWSTR devname, LPWSTR target, DWORD bufsize )
1035 {
1036     static const WCHAR auxW[] = {'A','U','X',0};
1037     static const WCHAR nulW[] = {'N','U','L',0};
1038     static const WCHAR prnW[] = {'P','R','N',0};
1039     static const WCHAR comW[] = {'C','O','M',0};
1040     static const WCHAR lptW[] = {'L','P','T',0};
1041     static const WCHAR com0W[] = {'\\','?','?','\\','C','O','M','0',0};
1042     static const WCHAR com1W[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\','C','O','M','1',0,0};
1043     static const WCHAR lpt1W[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\','L','P','T','1',0,0};
1044     static const WCHAR dosdevW[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\',0};
1045
1046     UNICODE_STRING nt_name;
1047     ANSI_STRING unix_name;
1048     WCHAR nt_buffer[10];
1049     NTSTATUS status;
1050
1051     if (!bufsize)
1052     {
1053         SetLastError( ERROR_INSUFFICIENT_BUFFER );
1054         return 0;
1055     }
1056
1057     if (devname)
1058     {
1059         WCHAR *p, name[5];
1060         char *path, *link;
1061         DWORD dosdev, ret = 0;
1062
1063         if ((dosdev = RtlIsDosDeviceName_U( devname )))
1064         {
1065             memcpy( name, devname + HIWORD(dosdev)/sizeof(WCHAR), LOWORD(dosdev) );
1066             name[LOWORD(dosdev)/sizeof(WCHAR)] = 0;
1067         }
1068         else
1069         {
1070             NTSTATUS status;
1071             WCHAR *buffer;
1072
1073             if (!(buffer = HeapAlloc( GetProcessHeap(), 0, sizeof(dosdevW) + strlenW(devname)*sizeof(WCHAR) )))
1074             {
1075                 SetLastError( ERROR_OUTOFMEMORY );
1076                 return 0;
1077             }
1078             memcpy( buffer, dosdevW, sizeof(dosdevW) );
1079             strcatW( buffer, devname );
1080             status = read_nt_symlink( buffer, target, bufsize );
1081             HeapFree( GetProcessHeap(), 0, buffer );
1082             if (status)
1083             {
1084                 SetLastError( RtlNtStatusToDosError(status) );
1085                 return 0;
1086             }
1087             ret = strlenW( target ) + 1;
1088             goto done;
1089         }
1090
1091         /* FIXME: should read NT symlink for all devices */
1092
1093         if (!(path = get_dos_device_path( name ))) return 0;
1094         link = read_symlink( path );
1095         HeapFree( GetProcessHeap(), 0, path );
1096
1097         if (link)
1098         {
1099             ret = MultiByteToWideChar( CP_UNIXCP, 0, link, -1, target, bufsize );
1100             HeapFree( GetProcessHeap(), 0, link );
1101         }
1102         else if (dosdev)  /* look for device defaults */
1103         {
1104             if (!strcmpiW( name, auxW ))
1105             {
1106                 if (bufsize >= sizeof(com1W)/sizeof(WCHAR))
1107                 {
1108                     memcpy( target, com1W, sizeof(com1W) );
1109                     ret = sizeof(com1W)/sizeof(WCHAR);
1110                 }
1111                 else SetLastError( ERROR_INSUFFICIENT_BUFFER );
1112                 return ret;
1113             }
1114             if (!strcmpiW( name, prnW ))
1115             {
1116                 if (bufsize >= sizeof(lpt1W)/sizeof(WCHAR))
1117                 {
1118                     memcpy( target, lpt1W, sizeof(lpt1W) );
1119                     ret = sizeof(lpt1W)/sizeof(WCHAR);
1120                 }
1121                 else SetLastError( ERROR_INSUFFICIENT_BUFFER );
1122                 return ret;
1123             }
1124
1125             nt_buffer[0] = '\\';
1126             nt_buffer[1] = '?';
1127             nt_buffer[2] = '?';
1128             nt_buffer[3] = '\\';
1129             strcpyW( nt_buffer + 4, name );
1130             RtlInitUnicodeString( &nt_name, nt_buffer );
1131             status = wine_nt_to_unix_file_name( &nt_name, &unix_name, FILE_OPEN, TRUE );
1132             if (status) SetLastError( RtlNtStatusToDosError(status) );
1133             else
1134             {
1135                 ret = MultiByteToWideChar( CP_UNIXCP, 0, unix_name.Buffer, -1, target, bufsize );
1136                 RtlFreeAnsiString( &unix_name );
1137             }
1138         }
1139     done:
1140         if (ret)
1141         {
1142             if (ret < bufsize) target[ret++] = 0;  /* add an extra null */
1143             for (p = target; *p; p++) if (*p == '/') *p = '\\';
1144         }
1145
1146         return ret;
1147     }
1148     else  /* return a list of all devices */
1149     {
1150         OBJECT_ATTRIBUTES attr;
1151         HANDLE handle;
1152         WCHAR *p = target;
1153         int i;
1154
1155         if (bufsize <= (sizeof(auxW)+sizeof(nulW)+sizeof(prnW))/sizeof(WCHAR))
1156         {
1157             SetLastError( ERROR_INSUFFICIENT_BUFFER );
1158             return 0;
1159         }
1160
1161         /* FIXME: these should be NT symlinks too */
1162
1163         memcpy( p, auxW, sizeof(auxW) );
1164         p += sizeof(auxW) / sizeof(WCHAR);
1165         memcpy( p, nulW, sizeof(nulW) );
1166         p += sizeof(nulW) / sizeof(WCHAR);
1167         memcpy( p, prnW, sizeof(prnW) );
1168         p += sizeof(prnW) / sizeof(WCHAR);
1169
1170         strcpyW( nt_buffer, com0W );
1171         RtlInitUnicodeString( &nt_name, nt_buffer );
1172
1173         for (i = 1; i <= 9; i++)
1174         {
1175             nt_buffer[7] = '0' + i;
1176             if (!wine_nt_to_unix_file_name( &nt_name, &unix_name, FILE_OPEN, TRUE ))
1177             {
1178                 RtlFreeAnsiString( &unix_name );
1179                 if (p + 5 >= target + bufsize)
1180                 {
1181                     SetLastError( ERROR_INSUFFICIENT_BUFFER );
1182                     return 0;
1183                 }
1184                 strcpyW( p, comW );
1185                 p[3] = '0' + i;
1186                 p[4] = 0;
1187                 p += 5;
1188             }
1189         }
1190         strcpyW( nt_buffer + 4, lptW );
1191         for (i = 1; i <= 9; i++)
1192         {
1193             nt_buffer[7] = '0' + i;
1194             if (!wine_nt_to_unix_file_name( &nt_name, &unix_name, FILE_OPEN, TRUE ))
1195             {
1196                 RtlFreeAnsiString( &unix_name );
1197                 if (p + 5 >= target + bufsize)
1198                 {
1199                     SetLastError( ERROR_INSUFFICIENT_BUFFER );
1200                     return 0;
1201                 }
1202                 strcpyW( p, lptW );
1203                 p[3] = '0' + i;
1204                 p[4] = 0;
1205                 p += 5;
1206             }
1207         }
1208
1209         RtlInitUnicodeString( &nt_name, dosdevW );
1210         nt_name.Length -= sizeof(WCHAR);  /* without trailing slash */
1211         attr.Length = sizeof(attr);
1212         attr.RootDirectory = 0;
1213         attr.ObjectName = &nt_name;
1214         attr.Attributes = OBJ_CASE_INSENSITIVE;
1215         attr.SecurityDescriptor = NULL;
1216         attr.SecurityQualityOfService = NULL;
1217         status = NtOpenDirectoryObject( &handle, FILE_LIST_DIRECTORY, &attr );
1218         if (!status)
1219         {
1220             char data[1024];
1221             DIRECTORY_BASIC_INFORMATION *info = (DIRECTORY_BASIC_INFORMATION *)data;
1222             ULONG ctx = 0, len;
1223
1224             while (!NtQueryDirectoryObject( handle, info, sizeof(data), 1, 0, &ctx, &len ))
1225             {
1226                 if (p + info->ObjectName.Length/sizeof(WCHAR) + 1 >= target + bufsize)
1227                 {
1228                     SetLastError( ERROR_INSUFFICIENT_BUFFER );
1229                     NtClose( handle );
1230                     return 0;
1231                 }
1232                 memcpy( p, info->ObjectName.Buffer, info->ObjectName.Length );
1233                 p += info->ObjectName.Length/sizeof(WCHAR);
1234                 *p++ = 0;
1235             }
1236             NtClose( handle );
1237         }
1238
1239         *p++ = 0;  /* terminating null */
1240         return p - target;
1241     }
1242 }
1243
1244
1245 /***********************************************************************
1246  *           QueryDosDeviceA   (KERNEL32.@)
1247  *
1248  * returns array of strings terminated by \0, terminated by \0
1249  */
1250 DWORD WINAPI QueryDosDeviceA( LPCSTR devname, LPSTR target, DWORD bufsize )
1251 {
1252     DWORD ret = 0, retW;
1253     WCHAR *devnameW = NULL;
1254     LPWSTR targetW;
1255
1256     if (devname && !(devnameW = FILE_name_AtoW( devname, FALSE ))) return 0;
1257
1258     targetW = HeapAlloc( GetProcessHeap(),0, bufsize * sizeof(WCHAR) );
1259     if (!targetW)
1260     {
1261         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1262         return 0;
1263     }
1264
1265     retW = QueryDosDeviceW(devnameW, targetW, bufsize);
1266
1267     ret = FILE_name_WtoA( targetW, retW, target, bufsize );
1268
1269     HeapFree(GetProcessHeap(), 0, targetW);
1270     return ret;
1271 }
1272
1273
1274 /***********************************************************************
1275  *           GetLogicalDrives   (KERNEL32.@)
1276  */
1277 DWORD WINAPI GetLogicalDrives(void)
1278 {
1279     const char *config_dir = wine_get_config_dir();
1280     struct stat st;
1281     char *buffer, *dev;
1282     DWORD ret = 0;
1283     int i;
1284
1285     if (!(buffer = HeapAlloc( GetProcessHeap(), 0, strlen(config_dir) + sizeof("/dosdevices/a:") )))
1286     {
1287         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1288         return 0;
1289     }
1290     strcpy( buffer, config_dir );
1291     strcat( buffer, "/dosdevices/a:" );
1292     dev = buffer + strlen(buffer) - 2;
1293
1294     for (i = 0; i < 26; i++)
1295     {
1296         *dev = 'a' + i;
1297         if (!stat( buffer, &st )) ret |= (1 << i);
1298     }
1299     HeapFree( GetProcessHeap(), 0, buffer );
1300     return ret;
1301 }
1302
1303
1304 /***********************************************************************
1305  *           GetLogicalDriveStringsA   (KERNEL32.@)
1306  */
1307 UINT WINAPI GetLogicalDriveStringsA( UINT len, LPSTR buffer )
1308 {
1309     DWORD drives = GetLogicalDrives();
1310     UINT drive, count;
1311
1312     for (drive = count = 0; drive < 26; drive++) if (drives & (1 << drive)) count++;
1313     if ((count * 4) + 1 > len) return count * 4 + 1;
1314
1315     for (drive = 0; drive < 26; drive++)
1316     {
1317         if (drives & (1 << drive))
1318         {
1319             *buffer++ = 'A' + drive;
1320             *buffer++ = ':';
1321             *buffer++ = '\\';
1322             *buffer++ = 0;
1323         }
1324     }
1325     *buffer = 0;
1326     return count * 4;
1327 }
1328
1329
1330 /***********************************************************************
1331  *           GetLogicalDriveStringsW   (KERNEL32.@)
1332  */
1333 UINT WINAPI GetLogicalDriveStringsW( UINT len, LPWSTR buffer )
1334 {
1335     DWORD drives = GetLogicalDrives();
1336     UINT drive, count;
1337
1338     for (drive = count = 0; drive < 26; drive++) if (drives & (1 << drive)) count++;
1339     if ((count * 4) + 1 > len) return count * 4 + 1;
1340
1341     for (drive = 0; drive < 26; drive++)
1342     {
1343         if (drives & (1 << drive))
1344         {
1345             *buffer++ = 'A' + drive;
1346             *buffer++ = ':';
1347             *buffer++ = '\\';
1348             *buffer++ = 0;
1349         }
1350     }
1351     *buffer = 0;
1352     return count * 4;
1353 }
1354
1355
1356 /***********************************************************************
1357  *           GetDriveTypeW   (KERNEL32.@)
1358  *
1359  * Returns the type of the disk drive specified. If root is NULL the
1360  * root of the current directory is used.
1361  *
1362  * RETURNS
1363  *
1364  *  Type of drive (from Win32 SDK):
1365  *
1366  *   DRIVE_UNKNOWN     unable to find out anything about the drive
1367  *   DRIVE_NO_ROOT_DIR nonexistent root dir
1368  *   DRIVE_REMOVABLE   the disk can be removed from the machine
1369  *   DRIVE_FIXED       the disk cannot be removed from the machine
1370  *   DRIVE_REMOTE      network disk
1371  *   DRIVE_CDROM       CDROM drive
1372  *   DRIVE_RAMDISK     virtual disk in RAM
1373  */
1374 UINT WINAPI GetDriveTypeW(LPCWSTR root) /* [in] String describing drive */
1375 {
1376     FILE_FS_DEVICE_INFORMATION info;
1377     IO_STATUS_BLOCK io;
1378     NTSTATUS status;
1379     HANDLE handle;
1380     UINT ret;
1381
1382     if (!open_device_root( root, &handle )) return DRIVE_NO_ROOT_DIR;
1383
1384     status = NtQueryVolumeInformationFile( handle, &io, &info, sizeof(info), FileFsDeviceInformation );
1385     NtClose( handle );
1386     if (status != STATUS_SUCCESS)
1387     {
1388         SetLastError( RtlNtStatusToDosError(status) );
1389         ret = DRIVE_UNKNOWN;
1390     }
1391     else
1392     {
1393         switch (info.DeviceType)
1394         {
1395         case FILE_DEVICE_CD_ROM_FILE_SYSTEM:  ret = DRIVE_CDROM; break;
1396         case FILE_DEVICE_VIRTUAL_DISK:        ret = DRIVE_RAMDISK; break;
1397         case FILE_DEVICE_NETWORK_FILE_SYSTEM: ret = DRIVE_REMOTE; break;
1398         case FILE_DEVICE_DISK_FILE_SYSTEM:
1399             if (info.Characteristics & FILE_REMOTE_DEVICE) ret = DRIVE_REMOTE;
1400             else if (info.Characteristics & FILE_REMOVABLE_MEDIA) ret = DRIVE_REMOVABLE;
1401             else if ((ret = get_mountmgr_drive_type( root )) == DRIVE_UNKNOWN) ret = DRIVE_FIXED;
1402             break;
1403         default:
1404             ret = DRIVE_UNKNOWN;
1405             break;
1406         }
1407     }
1408     TRACE( "%s -> %d\n", debugstr_w(root), ret );
1409     return ret;
1410 }
1411
1412
1413 /***********************************************************************
1414  *           GetDriveTypeA   (KERNEL32.@)
1415  *
1416  * See GetDriveTypeW.
1417  */
1418 UINT WINAPI GetDriveTypeA( LPCSTR root )
1419 {
1420     WCHAR *rootW = NULL;
1421
1422     if (root && !(rootW = FILE_name_AtoW( root, FALSE ))) return DRIVE_NO_ROOT_DIR;
1423     return GetDriveTypeW( rootW );
1424 }
1425
1426
1427 /***********************************************************************
1428  *           GetDiskFreeSpaceExW   (KERNEL32.@)
1429  *
1430  *  This function is used to acquire the size of the available and
1431  *  total space on a logical volume.
1432  *
1433  * RETURNS
1434  *
1435  *  Zero on failure, nonzero upon success. Use GetLastError to obtain
1436  *  detailed error information.
1437  *
1438  */
1439 BOOL WINAPI GetDiskFreeSpaceExW( LPCWSTR root, PULARGE_INTEGER avail,
1440                                  PULARGE_INTEGER total, PULARGE_INTEGER totalfree )
1441 {
1442     FILE_FS_SIZE_INFORMATION info;
1443     IO_STATUS_BLOCK io;
1444     NTSTATUS status;
1445     HANDLE handle;
1446     UINT units;
1447
1448     TRACE( "%s,%p,%p,%p\n", debugstr_w(root), avail, total, totalfree );
1449
1450     if (!open_device_root( root, &handle )) return FALSE;
1451
1452     status = NtQueryVolumeInformationFile( handle, &io, &info, sizeof(info), FileFsSizeInformation );
1453     NtClose( handle );
1454     if (status != STATUS_SUCCESS)
1455     {
1456         SetLastError( RtlNtStatusToDosError(status) );
1457         return FALSE;
1458     }
1459
1460     units = info.SectorsPerAllocationUnit * info.BytesPerSector;
1461     if (total) total->QuadPart = info.TotalAllocationUnits.QuadPart * units;
1462     if (totalfree) totalfree->QuadPart = info.AvailableAllocationUnits.QuadPart * units;
1463     /* FIXME: this one should take quotas into account */
1464     if (avail) avail->QuadPart = info.AvailableAllocationUnits.QuadPart * units;
1465     return TRUE;
1466 }
1467
1468
1469 /***********************************************************************
1470  *           GetDiskFreeSpaceExA   (KERNEL32.@)
1471  *
1472  * See GetDiskFreeSpaceExW.
1473  */
1474 BOOL WINAPI GetDiskFreeSpaceExA( LPCSTR root, PULARGE_INTEGER avail,
1475                                  PULARGE_INTEGER total, PULARGE_INTEGER totalfree )
1476 {
1477     WCHAR *rootW = NULL;
1478
1479     if (root && !(rootW = FILE_name_AtoW( root, FALSE ))) return FALSE;
1480     return GetDiskFreeSpaceExW( rootW, avail, total, totalfree );
1481 }
1482
1483
1484 /***********************************************************************
1485  *           GetDiskFreeSpaceW   (KERNEL32.@)
1486  */
1487 BOOL WINAPI GetDiskFreeSpaceW( LPCWSTR root, LPDWORD cluster_sectors,
1488                                LPDWORD sector_bytes, LPDWORD free_clusters,
1489                                LPDWORD total_clusters )
1490 {
1491     FILE_FS_SIZE_INFORMATION info;
1492     IO_STATUS_BLOCK io;
1493     NTSTATUS status;
1494     HANDLE handle;
1495     UINT units;
1496
1497     TRACE( "%s,%p,%p,%p,%p\n", debugstr_w(root),
1498            cluster_sectors, sector_bytes, free_clusters, total_clusters );
1499
1500     if (!open_device_root( root, &handle )) return FALSE;
1501
1502     status = NtQueryVolumeInformationFile( handle, &io, &info, sizeof(info), FileFsSizeInformation );
1503     NtClose( handle );
1504     if (status != STATUS_SUCCESS)
1505     {
1506         SetLastError( RtlNtStatusToDosError(status) );
1507         return FALSE;
1508     }
1509
1510     units = info.SectorsPerAllocationUnit * info.BytesPerSector;
1511
1512     if( GetVersion() & 0x80000000) {    /* win3.x, 9x, ME */
1513         /* cap the size and available at 2GB as per specs */
1514         if (info.TotalAllocationUnits.QuadPart * units > 0x7fffffff) {
1515             info.TotalAllocationUnits.QuadPart = 0x7fffffff / units;
1516             if (info.AvailableAllocationUnits.QuadPart * units > 0x7fffffff)
1517                 info.AvailableAllocationUnits.QuadPart = 0x7fffffff / units;
1518         }
1519         /* nr. of clusters is always <= 65335 */
1520         while( info.TotalAllocationUnits.QuadPart > 65535 ) {
1521             info.TotalAllocationUnits.QuadPart /= 2;
1522             info.AvailableAllocationUnits.QuadPart /= 2;
1523             info.SectorsPerAllocationUnit *= 2;
1524         }
1525     }
1526
1527     if (cluster_sectors) *cluster_sectors = info.SectorsPerAllocationUnit;
1528     if (sector_bytes) *sector_bytes = info.BytesPerSector;
1529     if (free_clusters) *free_clusters = info.AvailableAllocationUnits.u.LowPart;
1530     if (total_clusters) *total_clusters = info.TotalAllocationUnits.u.LowPart;
1531     return TRUE;
1532 }
1533
1534
1535 /***********************************************************************
1536  *           GetDiskFreeSpaceA   (KERNEL32.@)
1537  */
1538 BOOL WINAPI GetDiskFreeSpaceA( LPCSTR root, LPDWORD cluster_sectors,
1539                                LPDWORD sector_bytes, LPDWORD free_clusters,
1540                                LPDWORD total_clusters )
1541 {
1542     WCHAR *rootW = NULL;
1543
1544     if (root && !(rootW = FILE_name_AtoW( root, FALSE ))) return FALSE;
1545     return GetDiskFreeSpaceW( rootW, cluster_sectors, sector_bytes, free_clusters, total_clusters );
1546 }
1547
1548 /***********************************************************************
1549  *           GetVolumePathNameA   (KERNEL32.@)
1550  */
1551 BOOL WINAPI GetVolumePathNameA(LPCSTR filename, LPSTR volumepathname, DWORD buflen)
1552 {
1553     BOOL ret;
1554     WCHAR *filenameW = NULL, *volumeW;
1555
1556     FIXME("(%s, %p, %d), stub!\n", debugstr_a(filename), volumepathname, buflen);
1557
1558     if (filename && !(filenameW = FILE_name_AtoW( filename, FALSE ))) return FALSE;
1559     if (!(volumeW = HeapAlloc( GetProcessHeap(), 0, buflen * sizeof(WCHAR) ))) return FALSE;
1560
1561     if ((ret = GetVolumePathNameW( filenameW, volumeW, buflen )))
1562         FILE_name_WtoA( volumeW, -1, volumepathname, buflen );
1563
1564     HeapFree( GetProcessHeap(), 0, volumeW );
1565     return ret;
1566 }
1567
1568 /***********************************************************************
1569  *           GetVolumePathNameW   (KERNEL32.@)
1570  */
1571 BOOL WINAPI GetVolumePathNameW(LPCWSTR filename, LPWSTR volumepathname, DWORD buflen)
1572 {
1573     const WCHAR *p = filename;
1574
1575     FIXME("(%s, %p, %d), stub!\n", debugstr_w(filename), volumepathname, buflen);
1576
1577     if (p && tolowerW(p[0]) >= 'a' && tolowerW(p[0]) <= 'z' && p[1] ==':' && p[2] == '\\' && buflen >= 4)
1578     {
1579         volumepathname[0] = p[0];
1580         volumepathname[1] = ':';
1581         volumepathname[2] = '\\';
1582         volumepathname[3] = 0;
1583         return TRUE;
1584     }
1585     return FALSE;
1586 }
1587
1588 /***********************************************************************
1589  *           GetVolumePathNamesForVolumeNameW   (KERNEL32.@)
1590  */
1591 BOOL WINAPI GetVolumePathNamesForVolumeNameW(LPCWSTR volumename, LPWSTR volumepathname, DWORD buflen, PDWORD returnlen)
1592 {
1593     FIXME("(%s, %p, %d, %p), stub!\n", debugstr_w(volumename), volumepathname, buflen, returnlen);
1594     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1595     return FALSE;
1596 }
1597
1598 /***********************************************************************
1599  *           FindFirstVolumeA   (KERNEL32.@)
1600  */
1601 HANDLE WINAPI FindFirstVolumeA(LPSTR volume, DWORD len)
1602 {
1603     WCHAR *buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
1604     HANDLE handle = FindFirstVolumeW( buffer, len );
1605
1606     if (handle != INVALID_HANDLE_VALUE)
1607     {
1608         if (!WideCharToMultiByte( CP_ACP, 0, buffer, -1, volume, len, NULL, NULL ))
1609         {
1610             FindVolumeClose( handle );
1611             handle = INVALID_HANDLE_VALUE;
1612         }
1613     }
1614     HeapFree( GetProcessHeap(), 0, buffer );
1615     return handle;
1616 }
1617
1618 /***********************************************************************
1619  *           FindFirstVolumeW   (KERNEL32.@)
1620  */
1621 HANDLE WINAPI FindFirstVolumeW( LPWSTR volume, DWORD len )
1622 {
1623     DWORD size = 1024;
1624     HANDLE mgr = CreateFileW( MOUNTMGR_DOS_DEVICE_NAME, 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
1625                               NULL, OPEN_EXISTING, 0, 0 );
1626     if (mgr == INVALID_HANDLE_VALUE) return INVALID_HANDLE_VALUE;
1627
1628     for (;;)
1629     {
1630         MOUNTMGR_MOUNT_POINT input;
1631         MOUNTMGR_MOUNT_POINTS *output;
1632
1633         if (!(output = HeapAlloc( GetProcessHeap(), 0, size )))
1634         {
1635             SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1636             break;
1637         }
1638         memset( &input, 0, sizeof(input) );
1639
1640         if (!DeviceIoControl( mgr, IOCTL_MOUNTMGR_QUERY_POINTS, &input, sizeof(input),
1641                               output, size, NULL, NULL ))
1642         {
1643             if (GetLastError() != ERROR_MORE_DATA) break;
1644             size = output->Size;
1645             HeapFree( GetProcessHeap(), 0, output );
1646             continue;
1647         }
1648         CloseHandle( mgr );
1649         /* abuse the Size field to store the current index */
1650         output->Size = 0;
1651         if (!FindNextVolumeW( output, volume, len ))
1652         {
1653             HeapFree( GetProcessHeap(), 0, output );
1654             return INVALID_HANDLE_VALUE;
1655         }
1656         return output;
1657     }
1658     CloseHandle( mgr );
1659     return INVALID_HANDLE_VALUE;
1660 }
1661
1662 /***********************************************************************
1663  *           FindNextVolumeA   (KERNEL32.@)
1664  */
1665 BOOL WINAPI FindNextVolumeA( HANDLE handle, LPSTR volume, DWORD len )
1666 {
1667     WCHAR *buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
1668     BOOL ret;
1669
1670     if ((ret = FindNextVolumeW( handle, buffer, len )))
1671     {
1672         if (!WideCharToMultiByte( CP_ACP, 0, buffer, -1, volume, len, NULL, NULL )) ret = FALSE;
1673     }
1674     HeapFree( GetProcessHeap(), 0, buffer );
1675     return ret;
1676 }
1677
1678 /***********************************************************************
1679  *           FindNextVolumeW   (KERNEL32.@)
1680  */
1681 BOOL WINAPI FindNextVolumeW( HANDLE handle, LPWSTR volume, DWORD len )
1682 {
1683     MOUNTMGR_MOUNT_POINTS *data = handle;
1684
1685     while (data->Size < data->NumberOfMountPoints)
1686     {
1687         static const WCHAR volumeW[] = {'\\','?','?','\\','V','o','l','u','m','e','{',};
1688         WCHAR *link = (WCHAR *)((char *)data + data->MountPoints[data->Size].SymbolicLinkNameOffset);
1689         DWORD size = data->MountPoints[data->Size].SymbolicLinkNameLength;
1690         data->Size++;
1691         /* skip non-volumes */
1692         if (size < sizeof(volumeW) || memcmp( link, volumeW, sizeof(volumeW) )) continue;
1693         if (size + sizeof(WCHAR) >= len * sizeof(WCHAR))
1694         {
1695             SetLastError( ERROR_FILENAME_EXCED_RANGE );
1696             return FALSE;
1697         }
1698         memcpy( volume, link, size );
1699         volume[1] = '\\';  /* map \??\ to \\?\ */
1700         volume[size / sizeof(WCHAR)] = '\\';  /* Windows appends a backslash */
1701         volume[size / sizeof(WCHAR) + 1] = 0;
1702         TRACE( "returning entry %u %s\n", data->Size - 1, debugstr_w(volume) );
1703         return TRUE;
1704     }
1705     SetLastError( ERROR_NO_MORE_FILES );
1706     return FALSE;
1707 }
1708
1709 /***********************************************************************
1710  *           FindVolumeClose   (KERNEL32.@)
1711  */
1712 BOOL WINAPI FindVolumeClose(HANDLE handle)
1713 {
1714     return HeapFree( GetProcessHeap(), 0, handle );
1715 }
1716
1717 /***********************************************************************
1718  *           FindFirstVolumeMountPointA   (KERNEL32.@)
1719  */
1720 HANDLE WINAPI FindFirstVolumeMountPointA(LPCSTR root, LPSTR mount_point, DWORD len)
1721 {
1722     FIXME("(%s, %p, %d), stub!\n", debugstr_a(root), mount_point, len);
1723     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1724     return INVALID_HANDLE_VALUE;
1725 }
1726
1727 /***********************************************************************
1728  *           FindFirstVolumeMountPointW   (KERNEL32.@)
1729  */
1730 HANDLE WINAPI FindFirstVolumeMountPointW(LPCWSTR root, LPWSTR mount_point, DWORD len)
1731 {
1732     FIXME("(%s, %p, %d), stub!\n", debugstr_w(root), mount_point, len);
1733     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1734     return INVALID_HANDLE_VALUE;
1735 }
1736
1737 /***********************************************************************
1738  *           FindVolumeMountPointClose   (KERNEL32.@)
1739  */
1740 BOOL WINAPI FindVolumeMountPointClose(HANDLE h)
1741 {
1742     FIXME("(%p), stub!\n", h);
1743     return FALSE;
1744 }