Avoid crash in X11DRV_IsSolidColor for TrueColor displays.
[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
69 /* return default device to use for serial ports */
70 /* result should not be more than 16 characters long */
71 static BOOL get_default_com_device( char *buffer, int num )
72 {
73     if (!num || num > 9) return FALSE;
74 #ifdef linux
75     sprintf( buffer, "/dev/ttyS%d", num - 1 );
76     return TRUE;
77 #else
78     FIXME( "no known default for device com%d\n", num );
79     return FALSE;
80 #endif
81 }
82
83 /* return default device to use for parallel ports */
84 /* result should not be more than 16 characters long */
85 static BOOL get_default_lpt_device( char *buffer, int num )
86 {
87     if (!num || num > 9) return FALSE;
88 #ifdef linux
89     sprintf( buffer, "/dev/lp%d", num - 1 );
90     return TRUE;
91 #else
92     FIXME( "no known default for device lpt%d\n", num );
93     return FALSE;
94 #endif
95 }
96
97 /* read a Unix symlink; returned buffer must be freed by caller */
98 static char *read_symlink( const char *path )
99 {
100     char *buffer;
101     int ret, size = 128;
102
103     for (;;)
104     {
105         if (!(buffer = HeapAlloc( GetProcessHeap(), 0, size )))
106         {
107             SetLastError( ERROR_NOT_ENOUGH_MEMORY );
108             return 0;
109         }
110         ret = readlink( path, buffer, size );
111         if (ret == -1)
112         {
113             FILE_SetDosError();
114             HeapFree( GetProcessHeap(), 0, buffer );
115             return 0;
116         }
117         if (ret != sizeof(buffer))
118         {
119             buffer[ret] = 0;
120             return buffer;
121         }
122         HeapFree( GetProcessHeap(), 0, buffer );
123         size *= 2;
124     }
125 }
126
127 /* get the path of a dos device symlink in the $WINEPREFIX/dosdevices directory */
128 static char *get_dos_device_path( LPCWSTR name )
129 {
130     const char *config_dir = wine_get_config_dir();
131     char *buffer, *dev;
132     int i;
133
134     if (!(buffer = HeapAlloc( GetProcessHeap(), 0,
135                               strlen(config_dir) + sizeof("/dosdevices/") + 5 )))
136     {
137         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
138         return NULL;
139     }
140     strcpy( buffer, config_dir );
141     strcat( buffer, "/dosdevices/" );
142     dev = buffer + strlen(buffer);
143     /* no codepage conversion, DOS device names are ASCII anyway */
144     for (i = 0; i < 5; i++)
145         if (!(dev[i] = (char)tolowerW(name[i]))) break;
146     dev[5] = 0;
147     return buffer;
148 }
149
150
151 /***********************************************************************
152  *              VOLUME_CreateDevices
153  *
154  * Create the device files for the new device naming scheme.
155  * Should go away after a transition period.
156  */
157 void VOLUME_CreateDevices(void)
158 {
159     const char *config_dir = wine_get_config_dir();
160     char *buffer;
161     int i, count = 0;
162
163     if (!(buffer = HeapAlloc( GetProcessHeap(), 0,
164                               strlen(config_dir) + sizeof("/dosdevices") )))
165         return;
166
167     strcpy( buffer, config_dir );
168     strcat( buffer, "/dosdevices" );
169
170     if (!mkdir( buffer, 0777 ))  /* we created it, so now create the devices */
171     {
172         HKEY hkey;
173         DWORD dummy;
174         OBJECT_ATTRIBUTES attr;
175         UNICODE_STRING nameW;
176         WCHAR *p, *devnameW;
177         char tmp[128];
178         WCHAR com[5] = {'C','O','M','1',0};
179         WCHAR lpt[5] = {'L','P','T','1',0};
180
181         static const WCHAR serialportsW[] = {'M','a','c','h','i','n','e','\\',
182                                              'S','o','f','t','w','a','r','e','\\',
183                                              'W','i','n','e','\\','W','i','n','e','\\',
184                                              'C','o','n','f','i','g','\\',
185                                              'S','e','r','i','a','l','P','o','r','t','s',0};
186         static const WCHAR parallelportsW[] = {'M','a','c','h','i','n','e','\\',
187                                                'S','o','f','t','w','a','r','e','\\',
188                                                'W','i','n','e','\\','W','i','n','e','\\',
189                                                'C','o','n','f','i','g','\\',
190                                                'P','a','r','a','l','l','e','l','P','o','r','t','s',0};
191
192         attr.Length = sizeof(attr);
193         attr.RootDirectory = 0;
194         attr.ObjectName = &nameW;
195         attr.Attributes = 0;
196         attr.SecurityDescriptor = NULL;
197         attr.SecurityQualityOfService = NULL;
198         RtlInitUnicodeString( &nameW, serialportsW );
199
200         if (!NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ))
201         {
202             RtlInitUnicodeString( &nameW, com );
203             for (i = 1; i <= 9; i++)
204             {
205                 com[3] = '0' + i;
206                 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation,
207                                       tmp, sizeof(tmp), &dummy ))
208                 {
209                     devnameW = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
210                     if ((p = strchrW( devnameW, ',' ))) *p = 0;
211                     if (DefineDosDeviceW( DDD_RAW_TARGET_PATH, com, devnameW ))
212                     {
213                         char devname[32];
214                         WideCharToMultiByte(CP_UNIXCP, 0, devnameW, -1,
215                                             devname, sizeof(devname), NULL, NULL);
216                         MESSAGE( "Created symlink %s/dosdevices/com%d -> %s\n", config_dir, i, devname );
217                         count++;
218                     }
219                 }
220             }
221             NtClose( hkey );
222         }
223
224         RtlInitUnicodeString( &nameW, parallelportsW );
225         if (!NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ))
226         {
227             RtlInitUnicodeString( &nameW, lpt );
228             for (i = 1; i <= 9; i++)
229             {
230                 lpt[3] = '0' + i;
231                 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation,
232                                       tmp, sizeof(tmp), &dummy ))
233                 {
234                     devnameW = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
235                     if ((p = strchrW( devnameW, ',' ))) *p = 0;
236                     if (DefineDosDeviceW( DDD_RAW_TARGET_PATH, lpt, devnameW ))
237                     {
238                         char devname[32];
239                         WideCharToMultiByte(CP_UNIXCP, 0, devnameW, -1,
240                                             devname, sizeof(devname), NULL, NULL);
241                         MESSAGE( "Created symlink %s/dosdevices/lpt%d -> %s\n", config_dir, i, devname );
242                         count++;
243                     }
244                 }
245             }
246             NtClose( hkey );
247         }
248         if (count)
249             MESSAGE( "\nYou can now remove the [SerialPorts] and [ParallelPorts] sections\n"
250                      "in your configuration file, they are replaced by the above symlinks.\n\n" );
251     }
252     HeapFree( GetProcessHeap(), 0, buffer );
253 }
254
255
256 /******************************************************************
257  *              VOLUME_OpenDevice
258  */
259 HANDLE VOLUME_OpenDevice( LPCWSTR name, DWORD access, DWORD sharing,
260                           LPSECURITY_ATTRIBUTES sa, DWORD attributes )
261 {
262     char *buffer, *dev;
263     HANDLE ret;
264
265     if (!(buffer = get_dos_device_path( name ))) return 0;
266     dev = strrchr( buffer, '/' ) + 1;
267
268     for (;;)
269     {
270         TRACE("trying %s\n", buffer );
271
272         ret = FILE_CreateFile( buffer, access, sharing, sa, OPEN_EXISTING, 0, 0, TRUE, DRIVE_FIXED );
273         if (ret || GetLastError() != ERROR_FILE_NOT_FOUND) break;
274         if (!dev) break;
275
276         /* now try some defaults for it */
277         if (!strcmp( dev, "aux" ))
278         {
279             strcpy( dev, "com1" );
280             continue;
281         }
282         if (!strcmp( dev, "prn" ))
283         {
284             strcpy( dev, "lpt1" );
285             continue;
286         }
287         if (!strcmp( dev, "nul" ))
288         {
289             strcpy( buffer, "/dev/null" );
290             dev = NULL; /* last try */
291             continue;
292         }
293         if (!strncmp( dev, "com", 3 ) && get_default_com_device( buffer, dev[3] - '0' ))
294         {
295             dev = NULL; /* last try */
296             continue;
297         }
298         if (!strncmp( dev, "lpt", 3 ) && get_default_lpt_device( buffer, dev[3] - '0' ))
299         {
300             dev = NULL; /* last try */
301             continue;
302         }
303         break;
304     }
305
306     if (!ret) ERR( "could not open device %s err %ld\n", debugstr_w(name), GetLastError() );
307     HeapFree( GetProcessHeap(), 0, buffer );
308     return ret;
309 }
310
311
312 /******************************************************************
313  *              VOLUME_FindCdRomDataBestVoldesc
314  */
315 static DWORD VOLUME_FindCdRomDataBestVoldesc( HANDLE handle )
316 {
317     BYTE cur_vd_type, max_vd_type = 0;
318     BYTE buffer[16];
319     DWORD size, offs, best_offs = 0, extra_offs = 0;
320
321     for (offs = 0x8000; offs <= 0x9800; offs += 0x800)
322     {
323         /* if 'CDROM' occurs at position 8, this is a pre-iso9660 cd, and
324          * the volume label is displaced forward by 8
325          */
326         if (SetFilePointer( handle, offs, NULL, FILE_BEGIN ) != offs) break;
327         if (!ReadFile( handle, buffer, sizeof(buffer), &size, NULL )) break;
328         if (size != sizeof(buffer)) break;
329         /* check for non-ISO9660 signature */
330         if (!memcmp( buffer + 11, "ROM", 3 )) extra_offs = 8;
331         cur_vd_type = buffer[extra_offs];
332         if (cur_vd_type == 0xff) /* voldesc set terminator */
333             break;
334         if (cur_vd_type > max_vd_type)
335         {
336             max_vd_type = cur_vd_type;
337             best_offs = offs + extra_offs;
338         }
339     }
340     return best_offs;
341 }
342
343
344 /***********************************************************************
345  *           VOLUME_ReadFATSuperblock
346  */
347 static enum fs_type VOLUME_ReadFATSuperblock( HANDLE handle, BYTE *buff )
348 {
349     DWORD size;
350
351     /* try a fixed disk, with a FAT partition */
352     if (SetFilePointer( handle, 0, NULL, FILE_BEGIN ) != 0 ||
353         !ReadFile( handle, buff, SUPERBLOCK_SIZE, &size, NULL ) ||
354         size != SUPERBLOCK_SIZE)
355         return FS_ERROR;
356
357     if (buff[0] == 0xE9 || (buff[0] == 0xEB && buff[2] == 0x90))
358     {
359         /* guess which type of FAT we have */
360         unsigned int sz, nsect, nclust;
361         sz = GETWORD(buff, 0x16);
362         if (!sz) sz = GETLONG(buff, 0x24);
363         nsect = GETWORD(buff, 0x13);
364         if (!nsect) nsect = GETLONG(buff, 0x20);
365         nsect -= GETWORD(buff, 0x0e) + buff[0x10] * sz +
366             (GETWORD(buff, 0x11) * 32 + (GETWORD(buff, 0x0b) - 1)) / GETWORD(buff, 0x0b);
367         nclust = nsect / buff[0x0d];
368
369         if (nclust < 65525)
370         {
371             if (buff[0x26] == 0x29 && !memcmp(buff+0x36, "FAT", 3))
372             {
373                 /* FIXME: do really all FAT have their name beginning with
374                  * "FAT" ? (At least FAT12, FAT16 and FAT32 have :)
375                  */
376                 return FS_FAT1216;
377             }
378         }
379         else if (!memcmp(buff+0x52, "FAT", 3)) return FS_FAT32;
380     }
381     return FS_UNKNOWN;
382 }
383
384
385 /***********************************************************************
386  *           VOLUME_ReadCDSuperblock
387  */
388 static enum fs_type VOLUME_ReadCDSuperblock( HANDLE handle, BYTE *buff )
389 {
390     DWORD size, offs = VOLUME_FindCdRomDataBestVoldesc( handle );
391
392     if (!offs) return FS_UNKNOWN;
393
394     if (SetFilePointer( handle, offs, NULL, FILE_BEGIN ) != offs ||
395         !ReadFile( handle, buff, SUPERBLOCK_SIZE, &size, NULL ) ||
396         size != SUPERBLOCK_SIZE)
397         return FS_ERROR;
398
399     /* check for iso9660 present */
400     if (!memcmp(&buff[1], "CD001", 5)) return FS_ISO9660;
401     return FS_UNKNOWN;
402 }
403
404
405 /**************************************************************************
406  *                              VOLUME_GetSuperblockLabel
407  */
408 static void VOLUME_GetSuperblockLabel( enum fs_type type, const BYTE *superblock,
409                                        WCHAR *label, DWORD len )
410 {
411     const BYTE *label_ptr = NULL;
412     DWORD label_len;
413
414     switch(type)
415     {
416     case FS_ERROR:
417     case FS_UNKNOWN:
418         label_len = 0;
419         break;
420     case FS_FAT1216:
421         label_ptr = superblock + 0x2b;
422         label_len = 11;
423         break;
424     case FS_FAT32:
425         label_ptr = superblock + 0x47;
426         label_len = 11;
427         break;
428     case FS_ISO9660:
429         {
430             BYTE ver = superblock[0x5a];
431
432             if (superblock[0x58] == 0x25 && superblock[0x59] == 0x2f &&  /* Unicode ID */
433                 ((ver == 0x40) || (ver == 0x43) || (ver == 0x45)))
434             { /* yippee, unicode */
435                 int i;
436
437                 if (len > 17) len = 17;
438                 for (i = 0; i < len-1; i++)
439                     label[i] = (superblock[40+2*i] << 8) | superblock[41+2*i];
440                 label[i] = 0;
441                 while (i && label[i-1] == ' ') label[--i] = 0;
442                 return;
443             }
444             label_ptr = superblock + 40;
445             label_len = 32;
446             break;
447         }
448     }
449     if (label_len) RtlMultiByteToUnicodeN( label, (len-1) * sizeof(WCHAR),
450                                            &label_len, label_ptr, label_len );
451     label_len /= sizeof(WCHAR);
452     label[label_len] = 0;
453     while (label_len && label[label_len-1] == ' ') label[--label_len] = 0;
454 }
455
456
457 /**************************************************************************
458  *                              VOLUME_SetSuperblockLabel
459  */
460 static BOOL VOLUME_SetSuperblockLabel( enum fs_type type, HANDLE handle, const WCHAR *label )
461 {
462     BYTE label_data[11];
463     DWORD offset, len;
464
465     switch(type)
466     {
467     case FS_FAT1216:
468         offset = 0x2b;
469         break;
470     case FS_FAT32:
471         offset = 0x47;
472         break;
473     default:
474         SetLastError( ERROR_ACCESS_DENIED );
475         return FALSE;
476     }
477     RtlUnicodeToMultiByteN( label_data, sizeof(label_data), &len,
478                             label, strlenW(label) * sizeof(WCHAR) );
479     if (len < sizeof(label_data))
480         memset( label_data + len, ' ', sizeof(label_data) - len );
481
482     return (SetFilePointer( handle, offset, NULL, FILE_BEGIN ) == offset &&
483             WriteFile( handle, label_data, sizeof(label_data), &len, NULL ));
484 }
485
486
487 /**************************************************************************
488  *                              VOLUME_GetSuperblockSerial
489  */
490 static DWORD VOLUME_GetSuperblockSerial( enum fs_type type, const BYTE *superblock )
491 {
492     switch(type)
493     {
494     case FS_ERROR:
495     case FS_UNKNOWN:
496         break;
497     case FS_FAT1216:
498         return GETLONG( superblock, 0x27 );
499     case FS_FAT32:
500         return GETLONG( superblock, 0x33 );
501     case FS_ISO9660:
502         {
503             BYTE sum[4];
504             int i;
505
506             sum[0] = sum[1] = sum[2] = sum[3] = 0;
507             for (i = 0; i < 2048; i += 4)
508             {
509                 /* DON'T optimize this into DWORD !! (breaks overflow) */
510                 sum[0] += superblock[i+0];
511                 sum[1] += superblock[i+1];
512                 sum[2] += superblock[i+2];
513                 sum[3] += superblock[i+3];
514             }
515             /*
516              * OK, another braindead one... argh. Just believe it.
517              * Me$$ysoft chose to reverse the serial number in NT4/W2K.
518              * It's true and nobody will ever be able to change it.
519              */
520             if (GetVersion() & 0x80000000)
521                 return (sum[3] << 24) | (sum[2] << 16) | (sum[1] << 8) | sum[0];
522             else
523                 return (sum[0] << 24) | (sum[1] << 16) | (sum[2] << 8) | sum[3];
524         }
525     }
526     return 0;
527 }
528
529
530 /**************************************************************************
531  *                              VOLUME_GetAudioCDSerial
532  */
533 static DWORD VOLUME_GetAudioCDSerial( const CDROM_TOC *toc )
534 {
535     DWORD serial = 0;
536     int i;
537
538     for (i = 0; i <= toc->LastTrack - toc->FirstTrack; i++)
539         serial += ((toc->TrackData[i].Address[1] << 16) |
540                    (toc->TrackData[i].Address[2] << 8) |
541                    toc->TrackData[i].Address[3]);
542
543     /*
544      * dwStart, dwEnd collect the beginning and end of the disc respectively, in
545      * frames.
546      * There it is collected for correcting the serial when there are less than
547      * 3 tracks.
548      */
549     if (toc->LastTrack - toc->FirstTrack + 1 < 3)
550     {
551         DWORD dwStart = FRAME_OF_TOC(toc, toc->FirstTrack);
552         DWORD dwEnd = FRAME_OF_TOC(toc, toc->LastTrack + 1);
553         serial += dwEnd - dwStart;
554     }
555     return serial;
556 }
557
558
559 /***********************************************************************
560  *           GetVolumeInformationW   (KERNEL32.@)
561  */
562 BOOL WINAPI GetVolumeInformationW( LPCWSTR root, LPWSTR label, DWORD label_len,
563                                    DWORD *serial, DWORD *filename_len, DWORD *flags,
564                                    LPWSTR fsname, DWORD fsname_len )
565 {
566     static const WCHAR audiocdW[] = {'A','u','d','i','o',' ','C','D',0};
567     static const WCHAR fatW[] = {'F','A','T',0};
568     static const WCHAR cdfsW[] = {'C','D','F','S',0};
569
570     WCHAR device[] = {'\\','\\','.','\\','A',':',0};
571     HANDLE handle;
572     enum fs_type type = FS_UNKNOWN;
573
574     if (!root)
575     {
576         WCHAR path[MAX_PATH];
577         GetCurrentDirectoryW( MAX_PATH, path );
578         device[4] = path[0];
579     }
580     else
581     {
582         if (!root[0] || root[1] != ':')
583         {
584             SetLastError( ERROR_INVALID_NAME );
585             return FALSE;
586         }
587         device[4] = root[0];
588     }
589
590     /* try to open the device */
591
592     handle = CreateFileW( device, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE,
593                           NULL, OPEN_EXISTING, 0, 0 );
594     if (handle != INVALID_HANDLE_VALUE)
595     {
596         BYTE superblock[SUPERBLOCK_SIZE];
597         CDROM_TOC toc;
598         DWORD br;
599
600         /* check for audio CD */
601         /* FIXME: we only check the first track for now */
602         if (DeviceIoControl( handle, IOCTL_CDROM_READ_TOC, NULL, 0, &toc, sizeof(toc), &br, 0 ))
603         {
604             if (!(toc.TrackData[0].Control & 0x04))  /* audio track */
605             {
606                 TRACE( "%s: found audio CD\n", debugstr_w(device) );
607                 if (label) lstrcpynW( label, audiocdW, label_len );
608                 if (serial) *serial = VOLUME_GetAudioCDSerial( &toc );
609                 CloseHandle( handle );
610                 type = FS_ISO9660;
611                 goto fill_fs_info;
612             }
613             type = VOLUME_ReadCDSuperblock( handle, superblock );
614         }
615         else
616         {
617             type = VOLUME_ReadFATSuperblock( handle, superblock );
618             if (type == FS_UNKNOWN) type = VOLUME_ReadCDSuperblock( handle, superblock );
619         }
620         CloseHandle( handle );
621         TRACE( "%s: found fs type %d\n", debugstr_w(device), type );
622         if (type == FS_ERROR) return FALSE;
623
624         if (label && label_len) VOLUME_GetSuperblockLabel( type, superblock, label, label_len );
625         if (serial) *serial = VOLUME_GetSuperblockSerial( type, superblock );
626         goto fill_fs_info;
627     }
628     else
629     {
630         TRACE( "cannot open device %s: err %ld\n", debugstr_w(device), GetLastError() );
631         if (GetLastError() != ERROR_ACCESS_DENIED) return FALSE;
632     }
633
634     /* we couldn't open the device, fallback to default strategy */
635
636     switch(GetDriveTypeW( root ))
637     {
638     case DRIVE_UNKNOWN:
639     case DRIVE_NO_ROOT_DIR:
640         SetLastError( ERROR_NOT_READY );
641         return FALSE;
642     case DRIVE_REMOVABLE:
643     case DRIVE_FIXED:
644     case DRIVE_REMOTE:
645     case DRIVE_RAMDISK:
646         type = FS_UNKNOWN;
647         break;
648     case DRIVE_CDROM:
649         type = FS_ISO9660;
650         break;
651     }
652
653     if (label && label_len)
654     {
655         WCHAR labelW[] = {'A',':','\\','.','w','i','n','d','o','w','s','-','l','a','b','e','l',0};
656
657         labelW[0] = device[4];
658         handle = CreateFileW( labelW, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
659                               OPEN_EXISTING, 0, 0 );
660         if (handle != INVALID_HANDLE_VALUE)
661         {
662             char buffer[256], *p;
663             DWORD size;
664
665             if (!ReadFile( handle, buffer, sizeof(buffer)-1, &size, NULL )) size = 0;
666             CloseHandle( handle );
667             p = buffer + size;
668             while (p > buffer && (p[-1] == ' ' || p[-1] == '\r' || p[-1] == '\n')) p--;
669             *p = 0;
670             if (!MultiByteToWideChar( CP_UNIXCP, 0, buffer, -1, label, label_len ))
671                 label[label_len-1] = 0;
672         }
673         else label[0] = 0;
674     }
675     if (serial)
676     {
677         WCHAR serialW[] = {'A',':','\\','.','w','i','n','d','o','w','s','-','s','e','r','i','a','l',0};
678
679         serialW[0] = device[4];
680         handle = CreateFileW( serialW, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
681                               OPEN_EXISTING, 0, 0 );
682         if (handle != INVALID_HANDLE_VALUE)
683         {
684             char buffer[32];
685             DWORD size;
686
687             if (!ReadFile( handle, buffer, sizeof(buffer)-1, &size, NULL )) size = 0;
688             CloseHandle( handle );
689             buffer[size] = 0;
690             *serial = strtoul( buffer, NULL, 16 );
691         }
692         else *serial = 0;
693     }
694
695 fill_fs_info:  /* now fill in the information that depends on the file system type */
696
697     switch(type)
698     {
699     case FS_ISO9660:
700         if (fsname) lstrcpynW( fsname, cdfsW, fsname_len );
701         if (filename_len) *filename_len = 221;
702         if (flags) *flags = FILE_READ_ONLY_VOLUME;
703         break;
704     case FS_FAT1216:
705     case FS_FAT32:
706     default:  /* default to FAT file system (FIXME) */
707         if (fsname) lstrcpynW( fsname, fatW, fsname_len );
708         if (filename_len) *filename_len = 255;
709         if (flags) *flags = FILE_CASE_PRESERVED_NAMES;  /* FIXME */
710         break;
711     }
712     return TRUE;
713 }
714
715
716 /***********************************************************************
717  *           GetVolumeInformationA   (KERNEL32.@)
718  */
719 BOOL WINAPI GetVolumeInformationA( LPCSTR root, LPSTR label,
720                                        DWORD label_len, DWORD *serial,
721                                        DWORD *filename_len, DWORD *flags,
722                                        LPSTR fsname, DWORD fsname_len )
723 {
724     UNICODE_STRING rootW;
725     LPWSTR labelW, fsnameW;
726     BOOL ret;
727
728     if (root) RtlCreateUnicodeStringFromAsciiz(&rootW, root);
729     else rootW.Buffer = NULL;
730     labelW = label ? HeapAlloc(GetProcessHeap(), 0, label_len * sizeof(WCHAR)) : NULL;
731     fsnameW = fsname ? HeapAlloc(GetProcessHeap(), 0, fsname_len * sizeof(WCHAR)) : NULL;
732
733     if ((ret = GetVolumeInformationW(rootW.Buffer, labelW, label_len, serial,
734                                     filename_len, flags, fsnameW, fsname_len)))
735     {
736         if (label) WideCharToMultiByte(CP_ACP, 0, labelW, -1, label, label_len, NULL, NULL);
737         if (fsname) WideCharToMultiByte(CP_ACP, 0, fsnameW, -1, fsname, fsname_len, NULL, NULL);
738     }
739
740     RtlFreeUnicodeString(&rootW);
741     if (labelW) HeapFree( GetProcessHeap(), 0, labelW );
742     if (fsnameW) HeapFree( GetProcessHeap(), 0, fsnameW );
743     return ret;
744 }
745
746
747
748 /***********************************************************************
749  *           SetVolumeLabelW   (KERNEL32.@)
750  */
751 BOOL WINAPI SetVolumeLabelW( LPCWSTR root, LPCWSTR label )
752 {
753     WCHAR device[] = {'\\','\\','.','\\','A',':',0};
754     HANDLE handle;
755     enum fs_type type = FS_UNKNOWN;
756
757     if (!root)
758     {
759         WCHAR path[MAX_PATH];
760         GetCurrentDirectoryW( MAX_PATH, path );
761         device[4] = path[0];
762     }
763     else
764     {
765         if (!root[0] || root[1] != ':')
766         {
767             SetLastError( ERROR_INVALID_NAME );
768             return FALSE;
769         }
770         device[4] = root[0];
771     }
772
773     /* try to open the device */
774
775     handle = CreateFileW( device, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
776                           NULL, OPEN_EXISTING, 0, 0 );
777     if (handle == INVALID_HANDLE_VALUE)
778     {
779         /* try read-only */
780         handle = CreateFileW( device, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE,
781                               NULL, OPEN_EXISTING, 0, 0 );
782         if (handle != INVALID_HANDLE_VALUE)
783         {
784             /* device can be read but not written, return error */
785             CloseHandle( handle );
786             SetLastError( ERROR_ACCESS_DENIED );
787             return FALSE;
788         }
789     }
790
791     if (handle != INVALID_HANDLE_VALUE)
792     {
793         BYTE superblock[SUPERBLOCK_SIZE];
794         BOOL ret;
795
796         type = VOLUME_ReadFATSuperblock( handle, superblock );
797         ret = VOLUME_SetSuperblockLabel( type, handle, label );
798         CloseHandle( handle );
799         return ret;
800     }
801     else
802     {
803         TRACE( "cannot open device %s: err %ld\n", debugstr_w(device), GetLastError() );
804         if (GetLastError() != ERROR_ACCESS_DENIED) return FALSE;
805     }
806
807     /* we couldn't open the device, fallback to default strategy */
808
809     switch(GetDriveTypeW( root ))
810     {
811     case DRIVE_UNKNOWN:
812     case DRIVE_NO_ROOT_DIR:
813         SetLastError( ERROR_NOT_READY );
814         break;
815     case DRIVE_REMOVABLE:
816     case DRIVE_FIXED:
817         {
818             WCHAR labelW[] = {'A',':','\\','.','w','i','n','d','o','w','s','-','l','a','b','e','l',0};
819
820             labelW[0] = device[4];
821             handle = CreateFileW( labelW, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
822                                   CREATE_ALWAYS, 0, 0 );
823             if (handle != INVALID_HANDLE_VALUE)
824             {
825                 char buffer[64];
826                 DWORD size;
827
828                 if (!WideCharToMultiByte( CP_UNIXCP, 0, label, -1, buffer, sizeof(buffer), NULL, NULL ))
829                     buffer[sizeof(buffer)-1] = 0;
830                 WriteFile( handle, buffer, strlen(buffer), &size, NULL );
831                 CloseHandle( handle );
832                 return TRUE;
833             }
834             break;
835         }
836     case DRIVE_REMOTE:
837     case DRIVE_RAMDISK:
838     case DRIVE_CDROM:
839         SetLastError( ERROR_ACCESS_DENIED );
840         break;
841     }
842     return FALSE;
843 }
844
845 /***********************************************************************
846  *           SetVolumeLabelA   (KERNEL32.@)
847  */
848 BOOL WINAPI SetVolumeLabelA(LPCSTR root, LPCSTR volname)
849 {
850     UNICODE_STRING rootW, volnameW;
851     BOOL ret;
852
853     if (root) RtlCreateUnicodeStringFromAsciiz(&rootW, root);
854     else rootW.Buffer = NULL;
855     if (volname) RtlCreateUnicodeStringFromAsciiz(&volnameW, volname);
856     else volnameW.Buffer = NULL;
857
858     ret = SetVolumeLabelW( rootW.Buffer, volnameW.Buffer );
859
860     RtlFreeUnicodeString(&rootW);
861     RtlFreeUnicodeString(&volnameW);
862     return ret;
863 }
864
865
866 /***********************************************************************
867  *           GetVolumeNameForVolumeMountPointW   (KERNEL32.@)
868  */
869 BOOL WINAPI GetVolumeNameForVolumeMountPointW(LPCWSTR str, LPWSTR dst, DWORD size)
870 {
871     FIXME("(%s, %p, %lx): stub\n", debugstr_w(str), dst, size);
872     return 0;
873 }
874
875
876 /***********************************************************************
877  *           DefineDosDeviceW       (KERNEL32.@)
878  */
879 BOOL WINAPI DefineDosDeviceW( DWORD flags, LPCWSTR devname, LPCWSTR targetpath )
880 {
881     DWORD dosdev;
882
883     if (!(flags & DDD_RAW_TARGET_PATH))
884     {
885         FIXME( "(0x%08lx,%s,%s) DDD_RAW_TARGET_PATH flag not set, not supported yet\n",
886                flags, debugstr_w(devname), debugstr_w(targetpath) );
887         SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
888         return FALSE;
889     }
890
891     /* first check for a DOS device */
892
893     if ((dosdev = RtlIsDosDeviceName_U( devname )))
894     {
895         WCHAR name[5];
896         DWORD len;
897         char *path, *target, *p;
898         BOOL ret = FALSE;
899
900         memcpy( name, devname + HIWORD(dosdev)/sizeof(WCHAR), LOWORD(dosdev) );
901         name[LOWORD(dosdev)/sizeof(WCHAR)] = 0;
902         if (!(path = get_dos_device_path( name ))) return FALSE;
903
904         len = WideCharToMultiByte( CP_UNIXCP, 0, targetpath, -1, NULL, 0, NULL, NULL );
905         if ((target = HeapAlloc( GetProcessHeap(), 0, len )))
906         {
907             WideCharToMultiByte( CP_UNIXCP, 0, targetpath, -1, target, len, NULL, NULL );
908             for (p = target; *p; p++) if (*p == '\\') *p = '/';
909             TRACE( "creating symlink %s -> %s\n", path, target );
910             unlink( path );
911             if (!symlink( target, path )) ret = TRUE;
912             else FILE_SetDosError();
913             HeapFree( GetProcessHeap(), 0, target );
914         }
915         else SetLastError( ERROR_NOT_ENOUGH_MEMORY );
916         HeapFree( GetProcessHeap(), 0, path );
917         return ret;
918     }
919
920     /* now it must be a drive mapping */
921
922     FIXME("(0x%08lx,%s,%s) drive mappings not supported yet\n",
923           flags, debugstr_w(devname), debugstr_w(targetpath) );
924     SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
925     return FALSE;
926 }
927
928
929 /***********************************************************************
930  *           DefineDosDeviceA       (KERNEL32.@)
931  */
932 BOOL WINAPI DefineDosDeviceA(DWORD flags, LPCSTR devname, LPCSTR targetpath)
933 {
934     UNICODE_STRING d, t;
935     BOOL ret;
936
937     if (!RtlCreateUnicodeStringFromAsciiz(&d, devname))
938     {
939         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
940         return FALSE;
941     }
942     if (!RtlCreateUnicodeStringFromAsciiz(&t, targetpath))
943     {
944         RtlFreeUnicodeString(&d);
945         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
946         return FALSE;
947     }
948     ret = DefineDosDeviceW(flags, d.Buffer, t.Buffer);
949     RtlFreeUnicodeString(&d);
950     RtlFreeUnicodeString(&t);
951     return ret;
952 }
953
954
955 /***********************************************************************
956  *           QueryDosDeviceW   (KERNEL32.@)
957  *
958  * returns array of strings terminated by \0, terminated by \0
959  */
960 DWORD WINAPI QueryDosDeviceW( LPCWSTR devname, LPWSTR target, DWORD bufsize )
961 {
962     static const WCHAR auxW[] = {'A','U','X',0};
963     static const WCHAR nulW[] = {'N','U','L',0};
964     static const WCHAR prnW[] = {'P','R','N',0};
965     static const WCHAR comW[] = {'C','O','M',0};
966     static const WCHAR lptW[] = {'L','P','T',0};
967     static const WCHAR com0W[] = {'C','O','M','0',0};
968     static const WCHAR com1W[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\','C','O','M','1',0,0};
969     static const WCHAR lpt1W[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\','L','P','T','1',0,0};
970
971     char buffer[16];
972     struct stat st;
973
974     if (!bufsize)
975     {
976         SetLastError( ERROR_INSUFFICIENT_BUFFER );
977         return 0;
978     }
979
980     if (devname)
981     {
982         WCHAR *p, name[5];
983         char *path, *link;
984         DWORD dosdev, ret = 0;
985
986         if (!(dosdev = RtlIsDosDeviceName_U( devname )))
987         {
988             SetLastError( ERROR_BAD_PATHNAME );
989             return 0;
990         }
991         memcpy( name, devname + HIWORD(dosdev)/sizeof(WCHAR), LOWORD(dosdev) );
992         name[LOWORD(dosdev)/sizeof(WCHAR)] = 0;
993         if (!(path = get_dos_device_path( name ))) return 0;
994         link = read_symlink( path );
995         HeapFree( GetProcessHeap(), 0, path );
996
997         if (link)
998         {
999             ret = MultiByteToWideChar( CP_UNIXCP, 0, link, -1, target, bufsize );
1000             HeapFree( GetProcessHeap(), 0, link );
1001         }
1002         else  /* look for defaults */
1003         {
1004             if (!strcmpiW( name, auxW ))
1005             {
1006                 if (bufsize >= sizeof(com1W)/sizeof(WCHAR))
1007                 {
1008                     memcpy( target, com1W, sizeof(com1W) );
1009                     ret = sizeof(com1W)/sizeof(WCHAR);
1010                 }
1011                 else SetLastError( ERROR_INSUFFICIENT_BUFFER );
1012                 return ret;
1013             }
1014             if (!strcmpiW( name, prnW ))
1015             {
1016                 if (bufsize >= sizeof(lpt1W)/sizeof(WCHAR))
1017                 {
1018                     memcpy( target, lpt1W, sizeof(lpt1W) );
1019                     ret = sizeof(lpt1W)/sizeof(WCHAR);
1020                 }
1021                 else SetLastError( ERROR_INSUFFICIENT_BUFFER );
1022                 return ret;
1023             }
1024
1025             buffer[0] = 0;
1026
1027             if (!strcmpiW( name, nulW ))
1028                 strcpy( buffer, "/dev/null" );
1029             else if (!strncmpiW( name, comW, 3 ))
1030                 get_default_com_device( buffer, name[3] - '0' );
1031             else if (!strncmpiW( name, lptW, 3 ))
1032                 get_default_lpt_device( buffer, name[3] - '0' );
1033
1034             if (buffer[0] && !stat( buffer, &st ))
1035                 ret = MultiByteToWideChar( CP_UNIXCP, 0, buffer, -1, target, bufsize );
1036             else
1037                 SetLastError( ERROR_FILE_NOT_FOUND );
1038         }
1039
1040         if (ret)
1041         {
1042             if (ret < bufsize) target[ret++] = 0;  /* add an extra null */
1043             for (p = target; *p; p++) if (*p == '/') *p = '\\';
1044         }
1045
1046         return ret;
1047     }
1048     else  /* return a list of all devices */
1049     {
1050         WCHAR *p = target;
1051         char *path, *dev, buffer[16];
1052         int i;
1053
1054         if (bufsize <= (sizeof(auxW)+sizeof(nulW)+sizeof(prnW))/sizeof(WCHAR))
1055         {
1056             SetLastError( ERROR_INSUFFICIENT_BUFFER );
1057             return 0;
1058         }
1059
1060         memcpy( p, auxW, sizeof(auxW) );
1061         p += sizeof(auxW) / sizeof(WCHAR);
1062         memcpy( p, nulW, sizeof(nulW) );
1063         p += sizeof(nulW) / sizeof(WCHAR);
1064         memcpy( p, prnW, sizeof(prnW) );
1065         p += sizeof(prnW) / sizeof(WCHAR);
1066
1067         if (!(path = get_dos_device_path( com0W ))) return 0;
1068         dev = strrchr( path, '/' ) + 1;
1069
1070         for (i = 1; i <= 9; i++)
1071         {
1072             sprintf( dev, "com%d", i );
1073             if (!stat( path, &st ) ||
1074                 (get_default_com_device( buffer, i ) && !stat( buffer, &st )))
1075             {
1076                 if (p + 5 >= target + bufsize)
1077                 {
1078                     SetLastError( ERROR_INSUFFICIENT_BUFFER );
1079                     return 0;
1080                 }
1081                 strcpyW( p, comW );
1082                 p[3] = '0' + i;
1083                 p[4] = 0;
1084                 p += 5;
1085             }
1086         }
1087         for (i = 1; i <= 9; i++)
1088         {
1089             sprintf( dev, "lpt%d", i );
1090             if (!stat( path, &st ) ||
1091                 (get_default_lpt_device( buffer, i ) && !stat( buffer, &st )))
1092             {
1093                 if (p + 5 >= target + bufsize)
1094                 {
1095                     SetLastError( ERROR_INSUFFICIENT_BUFFER );
1096                     return 0;
1097                 }
1098                 strcpyW( p, lptW );
1099                 p[3] = '0' + i;
1100                 p[4] = 0;
1101                 p += 5;
1102             }
1103         }
1104         *p++ = 0;  /* terminating null */
1105         return p - target;
1106     }
1107 }
1108
1109
1110 /***********************************************************************
1111  *           QueryDosDeviceA   (KERNEL32.@)
1112  *
1113  * returns array of strings terminated by \0, terminated by \0
1114  */
1115 DWORD WINAPI QueryDosDeviceA( LPCSTR devname, LPSTR target, DWORD bufsize )
1116 {
1117     DWORD ret = 0, retW;
1118     UNICODE_STRING devnameW;
1119     LPWSTR targetW = HeapAlloc( GetProcessHeap(),0, bufsize * sizeof(WCHAR) );
1120
1121     if (!targetW)
1122     {
1123         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1124         return 0;
1125     }
1126
1127     if (devname) RtlCreateUnicodeStringFromAsciiz(&devnameW, devname);
1128     else devnameW.Buffer = NULL;
1129
1130     retW = QueryDosDeviceW(devnameW.Buffer, targetW, bufsize);
1131
1132     ret = WideCharToMultiByte(CP_ACP, 0, targetW, retW, target, bufsize, NULL, NULL);
1133
1134     RtlFreeUnicodeString(&devnameW);
1135     HeapFree(GetProcessHeap(), 0, targetW);
1136     return ret;
1137 }