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