kernel32: Fix GetVolumeInformation for fat32 partitions.
[wine] / dlls / kernel32 / oldconfig.c
1 /*
2  * Support for converting from old configuration format
3  *
4  * Copyright 2005 Alexandre Julliard
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * NOTES
21  *   This file should be removed after a suitable transition period.
22  */
23
24 #include "config.h"
25 #include "wine/port.h"
26
27 #include <stdarg.h>
28 #include <sys/types.h>
29 #include <stdlib.h>
30 #include <stdio.h>
31 #ifdef HAVE_SYS_STAT_H
32 # include <sys/stat.h>
33 #endif
34 #include <fcntl.h>
35 #include <dirent.h>
36 #ifdef HAVE_SYS_IOCTL_H
37 #include <sys/ioctl.h>
38 #endif
39 #ifdef HAVE_LINUX_HDREG_H
40 # include <linux/hdreg.h>
41 #endif
42
43 #define NONAMELESSUNION
44 #define NONAMELESSSTRUCT
45 #include "windef.h"
46 #include "winbase.h"
47 #include "winternl.h"
48 #include "ntddscsi.h"
49 #include "wine/library.h"
50 #include "wine/unicode.h"
51 #include "wine/debug.h"
52 #include "kernel_private.h"
53
54 WINE_DEFAULT_DEBUG_CHANNEL(reg);
55
56
57 /* registry initialisation, allocates some default keys. */
58 static ULONG allocate_default_keys(void)
59 {
60     static const WCHAR StatDataW[] = {'D','y','n','D','a','t','a','\\',
61                                       'P','e','r','f','S','t','a','t','s','\\',
62                                       'S','t','a','t','D','a','t','a',0};
63     static const WCHAR ConfigManagerW[] = {'D','y','n','D','a','t','a','\\',
64                                            'C','o','n','f','i','g',' ','M','a','n','a','g','e','r','\\',
65                                             'E','n','u','m',0};
66     HANDLE hkey;
67     ULONG dispos;
68     OBJECT_ATTRIBUTES attr;
69     UNICODE_STRING nameW;
70
71     attr.Length = sizeof(attr);
72     attr.RootDirectory = 0;
73     attr.ObjectName = &nameW;
74     attr.Attributes = 0;
75     attr.SecurityDescriptor = NULL;
76     attr.SecurityQualityOfService = NULL;
77
78     RtlInitUnicodeString( &nameW, StatDataW );
79     if (!NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, &dispos )) NtClose( hkey );
80     if (dispos == REG_OPENED_EXISTING_KEY)
81         return dispos; /* someone else already loaded the registry */
82
83     RtlInitUnicodeString( &nameW, ConfigManagerW );
84     if (!NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL )) NtClose( hkey );
85
86     return dispos;
87 }
88
89 /******************************************************************
90  *              create_scsi_entry
91  *
92  * Initializes registry to contain scsi info about the cdrom in NT.
93  * All devices (even not real scsi ones) have this info in NT.
94  * NOTE: programs usually read these registry entries after sending the
95  *       IOCTL_SCSI_GET_ADDRESS ioctl to the cdrom
96  */
97 static void create_scsi_entry( PSCSI_ADDRESS scsi_addr, LPCSTR lpDriver, UINT uDriveType,
98     LPSTR lpDriveName, LPSTR lpUnixDeviceName )
99 {
100     static UCHAR uCdromNumber = 0;
101     static UCHAR uTapeNumber = 0;
102
103     OBJECT_ATTRIBUTES attr;
104     UNICODE_STRING nameW;
105     WCHAR dataW[50];
106     DWORD lenW;
107     char buffer[40];
108     DWORD value;
109     const char *data;
110     HANDLE scsiKey;
111     HANDLE portKey;
112     HANDLE busKey;
113     HANDLE targetKey;
114     HANDLE lunKey;
115     DWORD disp;
116
117     attr.Length = sizeof(attr);
118     attr.RootDirectory = 0;
119     attr.ObjectName = &nameW;
120     attr.Attributes = 0;
121     attr.SecurityDescriptor = NULL;
122     attr.SecurityQualityOfService = NULL;
123
124     /* Ensure there is Scsi key */
125     if (!RtlCreateUnicodeStringFromAsciiz( &nameW, "Machine\\HARDWARE\\DEVICEMAP\\Scsi" ) ||
126         NtCreateKey( &scsiKey, KEY_ALL_ACCESS, &attr, 0,
127                      NULL, REG_OPTION_VOLATILE, &disp ))
128     {
129         ERR("Cannot create DEVICEMAP\\Scsi registry key\n" );
130         return;
131     }
132     RtlFreeUnicodeString( &nameW );
133
134     snprintf(buffer,sizeof(buffer),"Scsi Port %d",scsi_addr->PortNumber);
135     attr.RootDirectory = scsiKey;
136     if (!RtlCreateUnicodeStringFromAsciiz( &nameW, buffer ) ||
137         NtCreateKey( &portKey, KEY_ALL_ACCESS, &attr, 0,
138                      NULL, REG_OPTION_VOLATILE, &disp ))
139     {
140         ERR("Cannot create DEVICEMAP\\Scsi Port registry key\n" );
141         return;
142     }
143     RtlFreeUnicodeString( &nameW );
144
145     RtlCreateUnicodeStringFromAsciiz( &nameW, "Driver" );
146     RtlMultiByteToUnicodeN( dataW, sizeof(dataW), &lenW, lpDriver, strlen(lpDriver));
147     NtSetValueKey( portKey, &nameW, 0, REG_SZ, (BYTE*)dataW, lenW );
148     RtlFreeUnicodeString( &nameW );
149     value = 10;
150     RtlCreateUnicodeStringFromAsciiz( &nameW, "FirstBusTimeScanInMs" );
151     NtSetValueKey( portKey,&nameW, 0, REG_DWORD, (BYTE *)&value, sizeof(DWORD));
152     RtlFreeUnicodeString( &nameW );
153
154     value = 0;
155     if (strcmp(lpDriver, "atapi") == 0)
156     {
157 #ifdef HDIO_GET_DMA
158         int fd, dma;
159         
160         fd = open(lpUnixDeviceName, O_RDONLY|O_NONBLOCK);
161         if (fd != -1)
162         {
163             if (ioctl(fd, HDIO_GET_DMA, &dma) != -1) value = dma;
164             close(fd);
165         }
166 #endif
167         RtlCreateUnicodeStringFromAsciiz( &nameW, "DMAEnabled" );
168         NtSetValueKey( portKey,&nameW, 0, REG_DWORD, (BYTE *)&value, sizeof(DWORD));
169         RtlFreeUnicodeString( &nameW );
170     }
171
172     snprintf(buffer, sizeof(buffer),"Scsi Bus %d", scsi_addr->PathId);
173     attr.RootDirectory = portKey;
174     if (!RtlCreateUnicodeStringFromAsciiz( &nameW, buffer ) ||
175         NtCreateKey( &busKey, KEY_ALL_ACCESS, &attr, 0,
176                      NULL, REG_OPTION_VOLATILE, &disp ))
177     {
178         ERR("Cannot create DEVICEMAP\\Scsi Port\\Scsi Bus registry key\n" );
179         return;
180     }
181     RtlFreeUnicodeString( &nameW );
182
183     attr.RootDirectory = busKey;
184     /* FIXME: get real controller Id for SCSI */
185     if (!RtlCreateUnicodeStringFromAsciiz( &nameW, "Initiator Id 255" ) ||
186         NtCreateKey( &targetKey, KEY_ALL_ACCESS, &attr, 0,
187                      NULL, REG_OPTION_VOLATILE, &disp ))
188     {
189         ERR("Cannot create DEVICEMAP\\Scsi Port\\Scsi Bus\\Initiator Id 255 registry key\n" );
190         return;
191     }
192     RtlFreeUnicodeString( &nameW );
193     NtClose( targetKey );
194
195     snprintf(buffer, sizeof(buffer),"Target Id %d", scsi_addr->TargetId);
196     attr.RootDirectory = busKey;
197     if (!RtlCreateUnicodeStringFromAsciiz( &nameW, buffer ) ||
198         NtCreateKey( &targetKey, KEY_ALL_ACCESS, &attr, 0,
199                      NULL, REG_OPTION_VOLATILE, &disp ))
200     {
201         ERR("Cannot create DEVICEMAP\\Scsi Port\\Scsi Bus 0\\Target Id registry key\n" );
202         return;
203     }
204     RtlFreeUnicodeString( &nameW );
205
206     snprintf(buffer, sizeof(buffer),"Logical Unit Id %d", scsi_addr->Lun);
207     attr.RootDirectory = targetKey;
208     if (!RtlCreateUnicodeStringFromAsciiz( &nameW, buffer ) ||
209         NtCreateKey( &lunKey, KEY_ALL_ACCESS, &attr, 0,
210                      NULL, REG_OPTION_VOLATILE, &disp ))
211     {
212         ERR("Cannot create DEVICEMAP\\Scsi Port\\Scsi Bus 0\\Target Id registry key\\Logical Unit Id\n" );
213         return;
214     }
215     RtlFreeUnicodeString( &nameW );
216
217     switch (uDriveType)
218     {
219         case DRIVE_NO_ROOT_DIR:
220         default:
221             data = "OtherPeripheral"; break;
222         case DRIVE_FIXED:
223             data = "DiskPeripheral"; break;
224         case DRIVE_REMOVABLE:
225             data = "TapePeripheral";
226             snprintf(buffer, sizeof(buffer), "Tape%d", uTapeNumber++);
227             break;
228         case DRIVE_CDROM:
229             data = "CdRomPeripheral";
230             snprintf(buffer, sizeof(buffer), "Cdrom%d", uCdromNumber++);
231             break;
232     }
233     RtlCreateUnicodeStringFromAsciiz( &nameW, "Type" );
234     RtlMultiByteToUnicodeN( dataW, sizeof(dataW), &lenW, data, strlen(data));
235     NtSetValueKey( lunKey, &nameW, 0, REG_SZ, (BYTE*)dataW, lenW );
236     RtlFreeUnicodeString( &nameW );
237
238     RtlCreateUnicodeStringFromAsciiz( &nameW, "Identifier" );
239     RtlMultiByteToUnicodeN( dataW, sizeof(dataW), &lenW, lpDriveName, strlen(lpDriveName));
240     NtSetValueKey( lunKey, &nameW, 0, REG_SZ, (BYTE*)dataW, lenW );
241     RtlFreeUnicodeString( &nameW );
242
243     if (uDriveType == DRIVE_CDROM || uDriveType == DRIVE_REMOVABLE)
244     {
245         RtlCreateUnicodeStringFromAsciiz( &nameW, "DeviceName" );
246         RtlMultiByteToUnicodeN( dataW, sizeof(dataW), &lenW, buffer, strlen(buffer));
247         NtSetValueKey( lunKey, &nameW, 0, REG_SZ, (BYTE*)dataW, lenW );
248         RtlFreeUnicodeString( &nameW );
249     }
250
251     RtlCreateUnicodeStringFromAsciiz( &nameW, "UnixDeviceName" );
252     RtlMultiByteToUnicodeN( dataW, sizeof(dataW), &lenW, lpUnixDeviceName, strlen(lpUnixDeviceName));
253     NtSetValueKey( lunKey, &nameW, 0, REG_SZ, (BYTE*)dataW, lenW );
254     RtlFreeUnicodeString( &nameW );
255
256     NtClose( lunKey );
257     NtClose( targetKey );
258     NtClose( busKey );
259     NtClose( portKey );
260     NtClose( scsiKey );
261 }
262
263 struct LinuxProcScsiDevice
264 {
265     int host;
266     int channel;
267     int target;
268     int lun;
269     char vendor[9];
270     char model[17];
271     char rev[5];
272     char type[33];
273     int ansirev;
274 };
275
276 /*
277  * we need to declare white spaces explicitly via %*1[ ],
278  * as there are very dainbread CD-ROM devices out there
279  * which have their manufacturer name blanked out via spaces,
280  * which confuses fscanf's parsing (skips all blank spaces)
281  */
282 static int SCSI_getprocentry( FILE * procfile, struct LinuxProcScsiDevice * dev )
283 {
284     int result;
285
286     result = fscanf( procfile,
287         "Host:%*1[ ]scsi%d%*1[ ]Channel:%*1[ ]%d%*1[ ]Id:%*1[ ]%d%*1[ ]Lun:%*1[ ]%d\n",
288         &dev->host,
289         &dev->channel,
290         &dev->target,
291         &dev->lun );
292     if( result == EOF )
293     {
294         /* "end of entries" return, so no TRACE() here */
295         return EOF;
296     }
297     if( result != 4 )
298     {
299         ERR("bus id line scan count error (fscanf returns %d, expected 4)\n", result);
300         return 0;
301     }
302     result = fscanf( procfile,
303         "  Vendor:%*1[ ]%8c%*1[ ]Model:%*1[ ]%16c%*1[ ]Rev:%*1[ ]%4c\n",
304         dev->vendor,
305         dev->model,
306         dev->rev );
307     if( result != 3 )
308     {
309         ERR("model line scan count error (fscanf returns %d, expected 3)\n", result);
310         return 0;
311     }
312
313     result = fscanf( procfile,
314         "  Type:%*3[ ]%32c%*1[ ]ANSI SCSI%*1[ ]revision:%*1[ ]%x\n",
315         dev->type,
316         &dev->ansirev );
317     if( result != 2 )
318     {
319         ERR("SCSI type line scan count error (fscanf returns %d, expected 2)\n", result);
320         return 0;
321     }
322     /* Since we fscanf with %XXc instead of %s.. put a NULL at end */
323     dev->vendor[8] = 0;
324     dev->model[16] = 0;
325     dev->rev[4] = 0;
326     dev->type[32] = 0;
327
328     return 1;
329 }
330
331
332 /* create the hardware registry branch */
333 static void create_hardware_branch(void)
334 {
335     /* The following mostly will work on Linux, but should not cause
336      * problems on other systems. */
337     static const char procname_ide_media[] = "/proc/ide/%s/media";
338     static const char procname_ide_model[] = "/proc/ide/%s/model";
339     static const char procname_scsi[] = "/proc/scsi/scsi";
340     DIR *idedir;
341     struct dirent *dent = NULL;
342     FILE *procfile = NULL;
343     char cStr[40], cDevModel[40], cUnixDeviceName[40], read1[10] = "\0", read2[10] = "\0";
344     SCSI_ADDRESS scsi_addr;
345     UINT nType;
346     struct LinuxProcScsiDevice dev;
347     int result = 0, nSgNumber = 0;
348     UCHAR uFirstSCSIPort = 0;
349
350     /* Enumerate all ide devices first */
351     idedir = opendir("/proc/ide");
352     if (idedir)
353     {
354         while ((dent = readdir(idedir)))
355         {
356             if (strncmp(dent->d_name, "hd", 2) == 0)
357             {
358                 sprintf(cStr, procname_ide_media, dent->d_name);
359                 procfile = fopen(cStr, "r");
360                 if (!procfile)
361                 {
362                     ERR("Could not open %s\n", cStr);
363                     continue;
364                 } else {
365                     fgets(cStr, sizeof(cStr), procfile);
366                     fclose(procfile);
367                     nType = DRIVE_UNKNOWN;
368                     if (strncasecmp(cStr, "disk", 4)  == 0) nType = DRIVE_FIXED;
369                     if (strncasecmp(cStr, "cdrom", 5) == 0) nType = DRIVE_CDROM;
370
371                     if (nType == DRIVE_UNKNOWN) continue;
372                 }
373
374                 sprintf(cStr, procname_ide_model, dent->d_name);
375                 procfile = fopen(cStr, "r");
376                 if (!procfile)
377                 {
378                     ERR("Could not open %s\n", cStr);
379                     switch (nType)
380                     {
381                     case DRIVE_FIXED: strcpy(cDevModel, "Wine harddisk"); break;
382                     case DRIVE_CDROM: strcpy(cDevModel, "Wine CDROM"); break;
383                     }
384                 } else {
385                     fgets(cDevModel, sizeof(cDevModel), procfile);
386                     fclose(procfile);
387                     cDevModel[strlen(cDevModel) - 1] = 0;
388                 }
389
390                 sprintf(cUnixDeviceName, "/dev/%s", dent->d_name);
391                 scsi_addr.PortNumber = (dent->d_name[2] - 'a') / 2;
392                 scsi_addr.PathId = 0;
393                 scsi_addr.TargetId = (dent->d_name[2] - 'a') % 2;
394                 scsi_addr.Lun = 0;
395                 if (scsi_addr.PortNumber + 1 > uFirstSCSIPort)
396                     uFirstSCSIPort = scsi_addr.PortNumber + 1;
397
398                 create_scsi_entry(&scsi_addr, "atapi", nType, cDevModel, cUnixDeviceName);
399             }
400         }
401         closedir(idedir);
402     }
403
404     /* Now goes SCSI */
405     procfile = fopen(procname_scsi, "r");
406     if (!procfile)
407     {
408         TRACE("Could not open %s\n", procname_scsi);
409         return;
410     }
411     fgets(cStr, 40, procfile);
412     sscanf(cStr, "Attached %9s %9s", read1, read2);
413
414     if (strcmp(read1, "devices:") != 0)
415     {
416         WARN("Incorrect %s format\n", procname_scsi);
417         fclose( procfile );
418         return;
419     }
420     if (strcmp(read2, "none") == 0)
421     {
422         TRACE("No SCSI devices found in %s.\n", procname_scsi);
423         fclose( procfile );
424         return;
425     }
426
427     /* Read info for one device */
428     while ((result = SCSI_getprocentry(procfile, &dev)) > 0)
429     {
430         scsi_addr.PortNumber = dev.host;
431         scsi_addr.PathId = dev.channel;
432         scsi_addr.TargetId = dev.target;
433         scsi_addr.Lun = dev.lun;
434
435         scsi_addr.PortNumber += uFirstSCSIPort;
436         if (strncmp(dev.type, "Direct-Access", 13) == 0) nType = DRIVE_FIXED;
437         else if (strncmp(dev.type, "Sequential-Access", 17) == 0) nType = DRIVE_REMOVABLE;
438         else if (strncmp(dev.type, "CD-ROM", 6) == 0) nType = DRIVE_CDROM;
439         else if (strncmp(dev.type, "Processor", 9) == 0) nType = DRIVE_NO_ROOT_DIR;
440         else continue;
441
442         strcpy(cDevModel, dev.vendor);
443         strcat(cDevModel, dev.model);
444         strcat(cDevModel, dev.rev);
445         sprintf(cUnixDeviceName, "/dev/sg%d", nSgNumber++);
446         /* FIXME: get real driver name */
447         create_scsi_entry(&scsi_addr, "WINE SCSI", nType, cDevModel, cUnixDeviceName);
448     }
449     if( result != EOF )
450         WARN("Incorrect %s format\n", procname_scsi);
451     fclose( procfile );
452 }
453
454
455 /***********************************************************************
456  *              convert_old_config
457  */
458 void convert_old_config(void)
459 {
460     if (allocate_default_keys() == REG_OPENED_EXISTING_KEY)
461         return; /* someone else already loaded the registry */
462
463     /* create some hardware keys (FIXME: should not be done here) */
464     create_hardware_branch();
465 }