wined3d: Implement shadow sampling for the ARB shader backend.
[wine.git] / dlls / mountmgr.sys / device.c
blob48ee8d9189a8bc5ca2446188ee1144a77f20d6e7
1 /*
2 * Dynamic devices support
4 * Copyright 2006 Alexandre Julliard
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.
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.
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
21 #include <assert.h>
22 #include <errno.h>
23 #include <stdarg.h>
24 #include <stdio.h>
25 #include <stdlib.h>
27 #include "mountmgr.h"
28 #include "winreg.h"
29 #include "winnls.h"
30 #include "winuser.h"
31 #include "dbt.h"
32 #include "unixlib.h"
34 #include "wine/list.h"
35 #include "wine/debug.h"
37 WINE_DEFAULT_DEBUG_CHANNEL(mountmgr);
39 #define MAX_DOS_DRIVES 26
40 #define MAX_PORTS 256
42 static const WCHAR drive_types[][8] =
44 L"", /* DEVICE_UNKNOWN */
45 L"", /* DEVICE_HARDDISK */
46 L"hd", /* DEVICE_HARDDISK_VOL */
47 L"floppy", /* DEVICE_FLOPPY */
48 L"cdrom", /* DEVICE_CDROM */
49 L"cdrom", /* DEVICE_DVD */
50 L"network", /* DEVICE_NETWORK */
51 L"ramdisk" /* DEVICE_RAMDISK */
54 enum fs_type
56 FS_ERROR, /* error accessing the device */
57 FS_UNKNOWN, /* unknown file system */
58 FS_FAT1216,
59 FS_FAT32,
60 FS_ISO9660,
61 FS_UDF /* For reference [E] = Ecma-167.pdf, [U] = udf260.pdf */
64 struct disk_device
66 enum device_type type; /* drive type */
67 DEVICE_OBJECT *dev_obj; /* disk device allocated for this volume */
68 UNICODE_STRING name; /* device name */
69 UNICODE_STRING symlink; /* device symlink if any */
70 STORAGE_DEVICE_NUMBER devnum; /* device number info */
71 char *unix_device; /* unix device path */
72 char *unix_mount; /* unix mount point path */
73 char *serial; /* disk serial number */
74 struct volume *volume; /* associated volume */
77 struct volume
79 struct list entry; /* entry in volumes list */
80 struct disk_device *device; /* disk device */
81 char *udi; /* unique identifier for dynamic volumes */
82 unsigned int ref; /* ref count */
83 GUID guid; /* volume uuid */
84 struct mount_point *mount; /* Volume{xxx} mount point */
85 WCHAR label[256]; /* volume label */
86 DWORD serial; /* volume serial number */
87 enum fs_type fs_type; /* file system type */
90 struct dos_drive
92 struct list entry; /* entry in drives list */
93 struct volume *volume; /* volume for this drive */
94 int drive; /* drive letter (0 = A: etc.) */
95 struct mount_point *mount; /* DosDevices mount point */
98 static struct list drives_list = LIST_INIT(drives_list);
99 static struct list volumes_list = LIST_INIT(volumes_list);
101 static DRIVER_OBJECT *harddisk_driver;
102 static DRIVER_OBJECT *serial_driver;
103 static DRIVER_OBJECT *parallel_driver;
105 static CRITICAL_SECTION device_section;
106 static CRITICAL_SECTION_DEBUG critsect_debug =
108 0, 0, &device_section,
109 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
110 0, 0, { (DWORD_PTR)(__FILE__ ": device_section") }
112 static CRITICAL_SECTION device_section = { &critsect_debug, -1, 0, 0, 0, 0 };
114 static const GUID *get_default_uuid( int letter )
116 static GUID guid;
118 guid.Data4[7] = 'A' + letter;
119 return &guid;
122 /* send notification about a change to a given drive */
123 static void send_notify( int drive, int code )
125 DEV_BROADCAST_VOLUME info;
127 info.dbcv_size = sizeof(info);
128 info.dbcv_devicetype = DBT_DEVTYP_VOLUME;
129 info.dbcv_reserved = 0;
130 info.dbcv_unitmask = 1 << drive;
131 info.dbcv_flags = DBTF_MEDIA;
132 BroadcastSystemMessageW( BSF_FORCEIFHUNG|BSF_QUERY, NULL,
133 WM_DEVICECHANGE, code, (LPARAM)&info );
136 #define BLOCK_SIZE 2048
137 #define SUPERBLOCK_SIZE BLOCK_SIZE
139 #define CDFRAMES_PERSEC 75
140 #define CDFRAMES_PERMIN (CDFRAMES_PERSEC * 60)
141 #define FRAME_OF_ADDR(a) ((a)[1] * CDFRAMES_PERMIN + (a)[2] * CDFRAMES_PERSEC + (a)[3])
142 #define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc)->TrackData[(idx) - (toc)->FirstTrack].Address)
144 #define GETWORD(buf,off) MAKEWORD(buf[(off)],buf[(off+1)])
145 #define GETLONG(buf,off) MAKELONG(GETWORD(buf,off),GETWORD(buf,off+2))
147 /* get the label by reading it from a file at the root of the filesystem */
148 static void get_filesystem_label( struct volume *volume )
150 char buffer[256], *p;
151 ULONG size = sizeof(buffer);
152 struct read_volume_file_params params = { volume->device->unix_mount, ".windows-label", buffer, &size };
154 volume->label[0] = 0;
155 if (!volume->device->unix_mount) return;
156 if (MOUNTMGR_CALL( read_volume_file, &params )) return;
158 p = buffer + size;
159 while (p > buffer && (p[-1] == ' ' || p[-1] == '\r' || p[-1] == '\n')) p--;
160 *p = 0;
161 if (!MultiByteToWideChar( CP_UNIXCP, 0, buffer, -1, volume->label, ARRAY_SIZE(volume->label) ))
162 volume->label[ARRAY_SIZE(volume->label) - 1] = 0;
165 /* get the serial number by reading it from a file at the root of the filesystem */
166 static void get_filesystem_serial( struct volume *volume )
168 char buffer[32];
169 ULONG size = sizeof(buffer);
170 struct read_volume_file_params params = { volume->device->unix_mount, ".windows-serial", buffer, &size };
172 volume->serial = 0;
173 if (!volume->device->unix_mount) return;
174 if (MOUNTMGR_CALL( read_volume_file, &params )) return;
176 buffer[size] = 0;
177 volume->serial = strtoul( buffer, NULL, 16 );
181 /******************************************************************
182 * VOLUME_FindCdRomDataBestVoldesc
184 static DWORD VOLUME_FindCdRomDataBestVoldesc( HANDLE handle )
186 BYTE cur_vd_type, max_vd_type = 0;
187 BYTE buffer[0x800];
188 DWORD size, offs, best_offs = 0, extra_offs = 0;
190 for (offs = 0x8000; offs <= 0x9800; offs += 0x800)
192 /* if 'CDROM' occurs at position 8, this is a pre-iso9660 cd, and
193 * the volume label is displaced forward by 8
195 if (SetFilePointer( handle, offs, NULL, FILE_BEGIN ) != offs) break;
196 if (!ReadFile( handle, buffer, sizeof(buffer), &size, NULL )) break;
197 if (size != sizeof(buffer)) break;
198 /* check for non-ISO9660 signature */
199 if (!memcmp( buffer + 11, "ROM", 3 )) extra_offs = 8;
200 cur_vd_type = buffer[extra_offs];
201 if (cur_vd_type == 0xff) /* voldesc set terminator */
202 break;
203 if (cur_vd_type > max_vd_type)
205 max_vd_type = cur_vd_type;
206 best_offs = offs + extra_offs;
209 return best_offs;
213 /***********************************************************************
214 * VOLUME_ReadFATSuperblock
216 static enum fs_type VOLUME_ReadFATSuperblock( HANDLE handle, BYTE *buff )
218 DWORD size;
220 /* try a fixed disk, with a FAT partition */
221 if (SetFilePointer( handle, 0, NULL, FILE_BEGIN ) != 0 ||
222 !ReadFile( handle, buff, SUPERBLOCK_SIZE, &size, NULL ))
224 if (GetLastError() == ERROR_BAD_DEV_TYPE) return FS_UNKNOWN; /* not a real device */
225 return FS_ERROR;
228 if (size < SUPERBLOCK_SIZE) return FS_UNKNOWN;
230 /* FIXME: do really all FAT have their name beginning with
231 * "FAT" ? (At least FAT12, FAT16 and FAT32 have :)
233 if (!memcmp(buff+0x36, "FAT", 3) || !memcmp(buff+0x52, "FAT", 3))
235 /* guess which type of FAT we have */
236 int reasonable;
237 unsigned int sectors,
238 sect_per_fat,
239 total_sectors,
240 num_boot_sectors,
241 num_fats,
242 num_root_dir_ents,
243 bytes_per_sector,
244 sectors_per_cluster,
245 nclust;
246 sect_per_fat = GETWORD(buff, 0x16);
247 if (!sect_per_fat) sect_per_fat = GETLONG(buff, 0x24);
248 total_sectors = GETWORD(buff, 0x13);
249 if (!total_sectors)
250 total_sectors = GETLONG(buff, 0x20);
251 num_boot_sectors = GETWORD(buff, 0x0e);
252 num_fats = buff[0x10];
253 num_root_dir_ents = GETWORD(buff, 0x11);
254 bytes_per_sector = GETWORD(buff, 0x0b);
255 sectors_per_cluster = buff[0x0d];
256 /* check if the parameters are reasonable and will not cause
257 * arithmetic errors in the calculation */
258 reasonable = num_boot_sectors < total_sectors &&
259 num_fats < 16 &&
260 bytes_per_sector >= 512 && bytes_per_sector % 512 == 0 &&
261 sectors_per_cluster >= 1;
262 if (!reasonable) return FS_UNKNOWN;
263 sectors = total_sectors - num_boot_sectors - num_fats * sect_per_fat -
264 (num_root_dir_ents * 32 + bytes_per_sector - 1) / bytes_per_sector;
265 nclust = sectors / sectors_per_cluster;
266 if ((buff[0x42] == 0x28 || buff[0x42] == 0x29) &&
267 !memcmp(buff+0x52, "FAT", 3)) return FS_FAT32;
268 if (nclust < 65525)
270 if ((buff[0x26] == 0x28 || buff[0x26] == 0x29) &&
271 !memcmp(buff+0x36, "FAT", 3))
272 return FS_FAT1216;
275 return FS_UNKNOWN;
279 /***********************************************************************
280 * VOLUME_ReadCDBlock
282 static BOOL VOLUME_ReadCDBlock( HANDLE handle, BYTE *buff, INT offs )
284 DWORD size, whence = offs >= 0 ? FILE_BEGIN : FILE_END;
286 if (SetFilePointer( handle, offs, NULL, whence ) != offs ||
287 !ReadFile( handle, buff, SUPERBLOCK_SIZE, &size, NULL ) ||
288 size != SUPERBLOCK_SIZE)
289 return FALSE;
291 return TRUE;
295 /***********************************************************************
296 * VOLUME_ReadCDSuperblock
298 static enum fs_type VOLUME_ReadCDSuperblock( HANDLE handle, BYTE *buff )
300 int i;
301 DWORD offs;
303 /* Check UDF first as UDF and ISO9660 structures can coexist on the same medium
304 * Starting from sector 16, we may find :
305 * - a CD-ROM Volume Descriptor Set (ISO9660) containing one or more Volume Descriptors
306 * - an Extended Area (UDF) -- [E] 2/8.3.1 and [U] 2.1.7
307 * There is no explicit end so read 16 sectors and then give up */
308 for( i=16; i<16+16; i++)
310 if (!VOLUME_ReadCDBlock(handle, buff, i*BLOCK_SIZE))
311 continue;
313 /* We are supposed to check "BEA01", "NSR0x" and "TEA01" IDs + verify tag checksum
314 * but we assume the volume is well-formatted */
315 if (!memcmp(&buff[1], "BEA01", 5)) return FS_UDF;
318 offs = VOLUME_FindCdRomDataBestVoldesc( handle );
319 if (!offs) return FS_UNKNOWN;
321 if (!VOLUME_ReadCDBlock(handle, buff, offs))
322 return FS_ERROR;
324 /* check for the iso9660 identifier */
325 if (!memcmp(&buff[1], "CD001", 5)) return FS_ISO9660;
326 return FS_UNKNOWN;
330 /**************************************************************************
331 * UDF_Find_PVD
332 * Find the Primary Volume Descriptor
334 static BOOL UDF_Find_PVD( HANDLE handle, BYTE pvd[] )
336 unsigned int i;
337 DWORD offset;
338 INT locations[] = { 256, -1, -257, 512 };
340 for(i=0; i<ARRAY_SIZE(locations); i++)
342 if (!VOLUME_ReadCDBlock(handle, pvd, locations[i]*BLOCK_SIZE))
343 return FALSE;
345 /* Tag Identifier of Anchor Volume Descriptor Pointer is 2 -- [E] 3/10.2.1 */
346 if (pvd[0]==2 && pvd[1]==0)
348 /* Tag location (Uint32) at offset 12, little-endian */
349 offset = pvd[20 + 0];
350 offset |= pvd[20 + 1] << 8;
351 offset |= pvd[20 + 2] << 16;
352 offset |= pvd[20 + 3] << 24;
353 offset *= BLOCK_SIZE;
355 if (!VOLUME_ReadCDBlock(handle, pvd, offset))
356 return FALSE;
358 /* Check for the Primary Volume Descriptor Tag Id -- [E] 3/10.1.1 */
359 if (pvd[0]!=1 || pvd[1]!=0)
360 return FALSE;
362 /* 8 or 16 bits per character -- [U] 2.1.1 */
363 if (!(pvd[24]==8 || pvd[24]==16))
364 return FALSE;
366 return TRUE;
370 return FALSE;
374 /**************************************************************************
375 * VOLUME_GetSuperblockLabel
377 static void VOLUME_GetSuperblockLabel( struct volume *volume, HANDLE handle, const BYTE *superblock )
379 const BYTE *label_ptr = NULL;
380 DWORD label_len;
381 BYTE pvd[BLOCK_SIZE];
383 switch (volume->fs_type)
385 case FS_ERROR:
386 label_len = 0;
387 break;
388 case FS_UNKNOWN:
389 get_filesystem_label( volume );
390 return;
391 case FS_FAT1216:
392 label_ptr = superblock + 0x2b;
393 label_len = 11;
394 break;
395 case FS_FAT32:
396 label_ptr = superblock + 0x47;
397 label_len = 11;
398 break;
399 case FS_ISO9660:
401 BYTE ver = superblock[0x5a];
403 if (superblock[0x58] == 0x25 && superblock[0x59] == 0x2f && /* Unicode ID */
404 ((ver == 0x40) || (ver == 0x43) || (ver == 0x45)))
405 { /* yippee, unicode */
406 unsigned int i;
408 for (i = 0; i < 16; i++)
409 volume->label[i] = (superblock[40+2*i] << 8) | superblock[41+2*i];
410 volume->label[i] = 0;
411 while (i && volume->label[i-1] == ' ') volume->label[--i] = 0;
412 return;
414 label_ptr = superblock + 40;
415 label_len = 32;
416 break;
418 case FS_UDF:
419 if(!UDF_Find_PVD(handle, pvd))
421 label_len = 0;
422 break;
425 /* [E] 3/10.1.4 and [U] 2.1.1 */
426 if(pvd[24]==8)
428 label_ptr = pvd + 24 + 1;
429 label_len = pvd[24+32-1];
430 break;
432 else
434 unsigned int i;
436 label_len = 1 + pvd[24+32-1];
437 for (i = 0; i < label_len; i += 2)
438 volume->label[i/2] = (pvd[24+1+i] << 8) | pvd[24+1+i+1];
439 volume->label[label_len] = 0;
440 return;
443 if (label_len) RtlMultiByteToUnicodeN( volume->label, sizeof(volume->label) - sizeof(WCHAR),
444 &label_len, (const char *)label_ptr, label_len );
445 label_len /= sizeof(WCHAR);
446 volume->label[label_len] = 0;
447 while (label_len && volume->label[label_len-1] == ' ') volume->label[--label_len] = 0;
451 /**************************************************************************
452 * UDF_Find_FSD_Sector
453 * Find the File Set Descriptor used to compute the serial of a UDF volume
455 static int UDF_Find_FSD_Sector( HANDLE handle, BYTE block[] )
457 int i, PVD_sector, PD_sector, PD_length;
459 if(!UDF_Find_PVD(handle,block))
460 goto default_sector;
462 /* Retrieve the tag location of the PVD -- [E] 3/7.2 */
463 PVD_sector = block[12 + 0];
464 PVD_sector |= block[12 + 1] << 8;
465 PVD_sector |= block[12 + 2] << 16;
466 PVD_sector |= block[12 + 3] << 24;
468 /* Find the Partition Descriptor */
469 for(i=PVD_sector+1; ; i++)
471 if(!VOLUME_ReadCDBlock(handle, block, i*BLOCK_SIZE))
472 goto default_sector;
474 /* Partition Descriptor Tag Id -- [E] 3/10.5.1 */
475 if(block[0]==5 && block[1]==0)
476 break;
478 /* Terminating Descriptor Tag Id -- [E] 3/10.9.1 */
479 if(block[0]==8 && block[1]==0)
480 goto default_sector;
483 /* Find the partition starting location -- [E] 3/10.5.8 */
484 PD_sector = block[188 + 0];
485 PD_sector |= block[188 + 1] << 8;
486 PD_sector |= block[188 + 2] << 16;
487 PD_sector |= block[188 + 3] << 24;
489 /* Find the partition length -- [E] 3/10.5.9 */
490 PD_length = block[192 + 0];
491 PD_length |= block[192 + 1] << 8;
492 PD_length |= block[192 + 2] << 16;
493 PD_length |= block[192 + 3] << 24;
495 for(i=PD_sector; i<PD_sector+PD_length; i++)
497 if(!VOLUME_ReadCDBlock(handle, block, i*BLOCK_SIZE))
498 goto default_sector;
500 /* File Set Descriptor Tag Id -- [E] 3/14.1.1 */
501 if(block[0]==0 && block[1]==1)
502 return i;
505 default_sector:
506 WARN("FSD sector not found, serial may be incorrect\n");
507 return 257;
511 /**************************************************************************
512 * VOLUME_GetSuperblockSerial
514 static void VOLUME_GetSuperblockSerial( struct volume *volume, HANDLE handle, const BYTE *superblock )
516 int FSD_sector;
517 BYTE block[BLOCK_SIZE];
519 switch (volume->fs_type)
521 case FS_ERROR:
522 break;
523 case FS_UNKNOWN:
524 get_filesystem_serial( volume );
525 break;
526 case FS_FAT1216:
527 volume->serial = GETLONG( superblock, 0x27 );
528 break;
529 case FS_FAT32:
530 volume->serial = GETLONG( superblock, 0x43 );
531 break;
532 case FS_UDF:
533 FSD_sector = UDF_Find_FSD_Sector(handle, block);
534 if (!VOLUME_ReadCDBlock(handle, block, FSD_sector*BLOCK_SIZE))
535 break;
536 superblock = block;
537 /* fallthrough */
538 case FS_ISO9660:
540 BYTE sum[4];
541 int i;
543 sum[0] = sum[1] = sum[2] = sum[3] = 0;
544 for (i = 0; i < 2048; i += 4)
546 /* DON'T optimize this into DWORD !! (breaks overflow) */
547 sum[0] += superblock[i+0];
548 sum[1] += superblock[i+1];
549 sum[2] += superblock[i+2];
550 sum[3] += superblock[i+3];
553 * OK, another braindead one... argh. Just believe it.
554 * Me$$ysoft chose to reverse the serial number in NT4/W2K.
555 * It's true and nobody will ever be able to change it.
557 if ((GetVersion() & 0x80000000) || volume->fs_type == FS_UDF)
558 volume->serial = (sum[3] << 24) | (sum[2] << 16) | (sum[1] << 8) | sum[0];
559 else
560 volume->serial = (sum[0] << 24) | (sum[1] << 16) | (sum[2] << 8) | sum[3];
566 /**************************************************************************
567 * VOLUME_GetAudioCDSerial
569 static DWORD VOLUME_GetAudioCDSerial( const CDROM_TOC *toc )
571 DWORD serial = 0;
572 int i;
574 for (i = 0; i <= toc->LastTrack - toc->FirstTrack; i++)
575 serial += ((toc->TrackData[i].Address[1] << 16) |
576 (toc->TrackData[i].Address[2] << 8) |
577 toc->TrackData[i].Address[3]);
580 * dwStart, dwEnd collect the beginning and end of the disc respectively, in
581 * frames.
582 * There it is collected for correcting the serial when there are less than
583 * 3 tracks.
585 if (toc->LastTrack - toc->FirstTrack + 1 < 3)
587 DWORD dwStart = FRAME_OF_TOC(toc, toc->FirstTrack);
588 DWORD dwEnd = FRAME_OF_TOC(toc, toc->LastTrack + 1);
589 serial += dwEnd - dwStart;
591 return serial;
595 /* create the disk device for a given volume */
596 static NTSTATUS create_disk_device( enum device_type type, struct disk_device **device_ret, struct volume *volume )
598 UINT i, first = 0;
599 NTSTATUS status = 0;
600 const WCHAR *format = NULL;
601 const WCHAR *link_format = NULL;
602 UNICODE_STRING name;
603 DEVICE_OBJECT *dev_obj;
604 struct disk_device *device;
606 switch(type)
608 case DEVICE_UNKNOWN:
609 case DEVICE_HARDDISK:
610 case DEVICE_NETWORK: /* FIXME */
611 format = L"\\Device\\Harddisk%u";
612 link_format = L"\\??\\PhysicalDrive%u";
613 break;
614 case DEVICE_HARDDISK_VOL:
615 format = L"\\Device\\HarddiskVolume%u";
616 first = 1; /* harddisk volumes start counting from 1 */
617 break;
618 case DEVICE_FLOPPY:
619 format = L"\\Device\\Floppy%u";
620 break;
621 case DEVICE_CDROM:
622 case DEVICE_DVD:
623 format = L"\\Device\\CdRom%u";
624 link_format = L"\\??\\CdRom%u";
625 break;
626 case DEVICE_RAMDISK:
627 format = L"\\Device\\Ramdisk%u";
628 break;
631 name.MaximumLength = (lstrlenW(format) + 10) * sizeof(WCHAR);
632 name.Buffer = RtlAllocateHeap( GetProcessHeap(), 0, name.MaximumLength );
633 for (i = first; i < 32; i++)
635 swprintf( name.Buffer, name.MaximumLength / sizeof(WCHAR), format, i );
636 name.Length = lstrlenW(name.Buffer) * sizeof(WCHAR);
637 status = IoCreateDevice( harddisk_driver, sizeof(*device), &name, 0, 0, FALSE, &dev_obj );
638 if (status != STATUS_OBJECT_NAME_COLLISION) break;
640 if (!status)
642 device = dev_obj->DeviceExtension;
643 device->dev_obj = dev_obj;
644 device->name = name;
645 device->type = type;
646 device->unix_device = NULL;
647 device->unix_mount = NULL;
648 device->symlink.Buffer = NULL;
649 device->volume = volume;
651 if (link_format)
653 UNICODE_STRING symlink;
655 symlink.MaximumLength = (lstrlenW(link_format) + 10) * sizeof(WCHAR);
656 if ((symlink.Buffer = RtlAllocateHeap( GetProcessHeap(), 0, symlink.MaximumLength )))
658 swprintf( symlink.Buffer, symlink.MaximumLength / sizeof(WCHAR), link_format, i );
659 symlink.Length = lstrlenW(symlink.Buffer) * sizeof(WCHAR);
660 if (!IoCreateSymbolicLink( &symlink, &name )) device->symlink = symlink;
664 switch (type)
666 case DEVICE_FLOPPY:
667 case DEVICE_RAMDISK:
668 device->devnum.DeviceType = FILE_DEVICE_DISK;
669 device->devnum.DeviceNumber = i;
670 device->devnum.PartitionNumber = ~0u;
671 break;
672 case DEVICE_CDROM:
673 device->devnum.DeviceType = FILE_DEVICE_CD_ROM;
674 device->devnum.DeviceNumber = i;
675 device->devnum.PartitionNumber = ~0u;
676 break;
677 case DEVICE_DVD:
678 device->devnum.DeviceType = FILE_DEVICE_DVD;
679 device->devnum.DeviceNumber = i;
680 device->devnum.PartitionNumber = ~0u;
681 break;
682 case DEVICE_UNKNOWN:
683 case DEVICE_HARDDISK:
684 case DEVICE_NETWORK: /* FIXME */
685 device->devnum.DeviceType = FILE_DEVICE_DISK;
686 device->devnum.DeviceNumber = i;
687 device->devnum.PartitionNumber = 0;
688 break;
689 case DEVICE_HARDDISK_VOL:
690 device->devnum.DeviceType = FILE_DEVICE_DISK;
691 device->devnum.DeviceNumber = 0;
692 device->devnum.PartitionNumber = i;
693 break;
695 *device_ret = device;
696 TRACE( "created device %s\n", debugstr_w(name.Buffer) );
698 else
700 FIXME( "IoCreateDevice %s got %lx\n", debugstr_w(name.Buffer), status );
701 RtlFreeUnicodeString( &name );
703 return status;
706 /* delete the disk device for a given drive */
707 static void delete_disk_device( struct disk_device *device )
709 TRACE( "deleting device %s\n", debugstr_w(device->name.Buffer) );
710 if (device->symlink.Buffer)
712 IoDeleteSymbolicLink( &device->symlink );
713 RtlFreeUnicodeString( &device->symlink );
715 free( device->unix_device );
716 free( device->unix_mount );
717 free( device->serial );
718 RtlFreeUnicodeString( &device->name );
719 IoDeleteDevice( device->dev_obj );
722 /* grab another reference to a volume */
723 static struct volume *grab_volume( struct volume *volume )
725 volume->ref++;
726 return volume;
729 /* release a volume and delete the corresponding disk device when refcount is 0 */
730 static unsigned int release_volume( struct volume *volume )
732 unsigned int ret = --volume->ref;
734 if (!ret)
736 TRACE( "%s udi %s\n", debugstr_guid(&volume->guid), debugstr_a(volume->udi) );
737 assert( !volume->udi );
738 list_remove( &volume->entry );
739 if (volume->mount) delete_mount_point( volume->mount );
740 delete_disk_device( volume->device );
741 free( volume );
743 return ret;
746 /* set the volume udi */
747 static void set_volume_udi( struct volume *volume, const char *udi )
749 if (udi)
751 assert( !volume->udi );
752 /* having a udi means the HAL side holds an extra reference */
753 if ((volume->udi = strdup( udi ))) grab_volume( volume );
755 else if (volume->udi)
757 free( volume->udi );
758 volume->udi = NULL;
759 release_volume( volume );
763 /* create a disk volume */
764 static NTSTATUS create_volume( const char *udi, enum device_type type, struct volume **volume_ret )
766 struct volume *volume;
767 NTSTATUS status;
769 if (!(volume = calloc( 1, sizeof(*volume) )))
770 return STATUS_NO_MEMORY;
772 if (!(status = create_disk_device( type, &volume->device, volume )))
774 if (udi) set_volume_udi( volume, udi );
775 list_add_tail( &volumes_list, &volume->entry );
776 *volume_ret = grab_volume( volume );
778 else free( volume );
780 return status;
783 /* create the disk device for a given volume */
784 static NTSTATUS create_dos_device( struct volume *volume, const char *udi, int letter,
785 enum device_type type, struct dos_drive **drive_ret )
787 struct dos_drive *drive;
788 NTSTATUS status;
790 if (!(drive = malloc( sizeof(*drive) ))) return STATUS_NO_MEMORY;
791 drive->drive = letter;
792 drive->mount = NULL;
794 if (volume)
796 if (udi) set_volume_udi( volume, udi );
797 drive->volume = grab_volume( volume );
798 status = STATUS_SUCCESS;
800 else status = create_volume( udi, type, &drive->volume );
802 if (status == STATUS_SUCCESS)
804 list_add_tail( &drives_list, &drive->entry );
805 *drive_ret = drive;
807 else free( drive );
809 return status;
812 /* delete the disk device for a given drive */
813 static void delete_dos_device( struct dos_drive *drive )
815 list_remove( &drive->entry );
816 if (drive->mount) delete_mount_point( drive->mount );
817 release_volume( drive->volume );
818 free( drive );
821 /* find a volume that matches the parameters */
822 static struct volume *find_matching_volume( const char *udi, const char *device,
823 const char *mount_point, enum device_type type )
825 struct volume *volume;
826 struct disk_device *disk_device;
828 LIST_FOR_EACH_ENTRY( volume, &volumes_list, struct volume, entry )
830 int match = 0;
832 /* when we have a udi we only match drives added manually */
833 if (udi && volume->udi) continue;
834 /* and when we don't have a udi we only match dynamic drives */
835 if (!udi && !volume->udi) continue;
837 disk_device = volume->device;
838 if (disk_device->type != type) continue;
839 if (device && disk_device->unix_device)
841 if (strcmp( device, disk_device->unix_device )) continue;
842 match++;
844 if (mount_point && disk_device->unix_mount)
846 if (strcmp( mount_point, disk_device->unix_mount )) continue;
847 match++;
849 if (!match) continue;
850 TRACE( "found matching volume %s for device %s mount %s type %u\n",
851 debugstr_guid(&volume->guid), debugstr_a(device), debugstr_a(mount_point), type );
852 return grab_volume( volume );
854 return NULL;
857 static BOOL get_volume_device_info( struct volume *volume )
859 const char *unix_device = volume->device->unix_device;
860 WCHAR *name;
861 HANDLE handle;
862 CDROM_TOC toc;
863 DWORD size;
864 BYTE superblock[SUPERBLOCK_SIZE];
866 if (!unix_device)
867 return FALSE;
869 if (MOUNTMGR_CALL( check_device_access, volume->device->unix_device )) return FALSE;
871 if (!(name = wine_get_dos_file_name( unix_device )))
873 ERR("Failed to convert %s to NT, err %lu\n", debugstr_a(unix_device), GetLastError());
874 return FALSE;
876 handle = CreateFileW( name, GENERIC_READ | SYNCHRONIZE, FILE_SHARE_READ | FILE_SHARE_WRITE,
877 NULL, OPEN_EXISTING, 0, 0 );
878 HeapFree( GetProcessHeap(), 0, name );
879 if (handle == INVALID_HANDLE_VALUE)
881 WARN("Failed to open %s, err %lu\n", debugstr_a(unix_device), GetLastError());
882 return FALSE;
885 if (DeviceIoControl( handle, IOCTL_CDROM_READ_TOC, NULL, 0, &toc, sizeof(toc), &size, 0 ))
887 if (!(toc.TrackData[0].Control & 0x04)) /* audio track */
889 TRACE( "%s: found audio CD\n", debugstr_a(unix_device) );
890 wcscpy( volume->label, L"Audio CD" );
891 volume->serial = VOLUME_GetAudioCDSerial( &toc );
892 volume->fs_type = FS_ISO9660;
893 CloseHandle( handle );
894 return TRUE;
896 volume->fs_type = VOLUME_ReadCDSuperblock( handle, superblock );
898 else
900 if(GetLastError() == ERROR_NOT_READY)
902 TRACE( "%s: removable drive with no inserted media\n", debugstr_a(unix_device) );
903 volume->fs_type = FS_UNKNOWN;
904 CloseHandle( handle );
905 return TRUE;
908 volume->fs_type = VOLUME_ReadFATSuperblock( handle, superblock );
909 if (volume->fs_type == FS_UNKNOWN) volume->fs_type = VOLUME_ReadCDSuperblock( handle, superblock );
912 TRACE( "%s: found fs type %d\n", debugstr_a(unix_device), volume->fs_type );
913 if (volume->fs_type == FS_ERROR)
915 CloseHandle( handle );
916 return FALSE;
919 VOLUME_GetSuperblockLabel( volume, handle, superblock );
920 VOLUME_GetSuperblockSerial( volume, handle, superblock );
922 CloseHandle( handle );
923 return TRUE;
926 /* set disk serial for dos devices that reside on a given Unix device */
927 static void set_dos_devices_disk_serial( struct disk_device *device )
929 unsigned int devices;
930 struct dos_drive *drive;
931 struct get_volume_dos_devices_params params = { device->unix_mount, &devices };
933 if (!device->serial || !device->unix_mount || MOUNTMGR_CALL( get_volume_dos_devices, &params ))
934 return;
936 LIST_FOR_EACH_ENTRY( drive, &drives_list, struct dos_drive, entry )
938 /* drives mapped to Unix devices already have serial set, if available */
939 if (drive->volume->device->unix_device) continue;
940 /* copy serial if drive resides on this Unix device */
941 if (devices & (1 << drive->drive))
943 free( drive->volume->device->serial );
944 drive->volume->device->serial = strdup( device->serial );
949 /* change the information for an existing volume */
950 static NTSTATUS set_volume_info( struct volume *volume, struct dos_drive *drive, const char *device,
951 const char *mount_point, enum device_type type, const GUID *guid,
952 const char *disk_serial )
954 void *id = NULL;
955 unsigned int id_len = 0;
956 struct disk_device *disk_device = volume->device;
957 NTSTATUS status;
959 if (type != disk_device->type)
961 if ((status = create_disk_device( type, &disk_device, volume ))) return status;
962 if (volume->mount)
964 delete_mount_point( volume->mount );
965 volume->mount = NULL;
967 if (drive && drive->mount)
969 delete_mount_point( drive->mount );
970 drive->mount = NULL;
972 delete_disk_device( volume->device );
973 volume->device = disk_device;
975 else
977 free( disk_device->unix_device );
978 free( disk_device->unix_mount );
979 free( disk_device->serial );
981 disk_device->unix_device = strdup( device );
982 disk_device->unix_mount = strdup( mount_point );
983 disk_device->serial = strdup( disk_serial );
984 set_dos_devices_disk_serial( disk_device );
986 if (!get_volume_device_info( volume ))
988 if (volume->device->type == DEVICE_CDROM)
989 volume->fs_type = FS_ISO9660;
990 else if (volume->device->type == DEVICE_DVD)
991 volume->fs_type = FS_UDF;
992 else
993 volume->fs_type = FS_UNKNOWN;
995 get_filesystem_label( volume );
996 get_filesystem_serial( volume );
999 TRACE("fs_type %#x, label %s, serial %08lx\n", volume->fs_type, debugstr_w(volume->label), volume->serial);
1001 if (guid && memcmp( &volume->guid, guid, sizeof(volume->guid) ))
1003 volume->guid = *guid;
1004 if (volume->mount)
1006 delete_mount_point( volume->mount );
1007 volume->mount = NULL;
1011 if (!volume->serial)
1012 memcpy(&volume->serial, &volume->guid.Data4[4], sizeof(DWORD));
1014 if (!volume->mount)
1015 volume->mount = add_volume_mount_point( disk_device->dev_obj, &disk_device->name, &volume->guid );
1016 if (drive && !drive->mount)
1017 drive->mount = add_dosdev_mount_point( disk_device->dev_obj, &disk_device->name, drive->drive );
1019 if (disk_device->unix_mount)
1021 id = disk_device->unix_mount;
1022 id_len = strlen( disk_device->unix_mount ) + 1;
1024 if (volume->mount) set_mount_point_id( volume->mount, id, id_len );
1025 if (drive && drive->mount) set_mount_point_id( drive->mount, id, id_len );
1027 return STATUS_SUCCESS;
1030 /* change the drive letter or volume for an existing drive */
1031 static void set_drive_info( struct dos_drive *drive, int letter, struct volume *volume )
1033 if (drive->drive != letter)
1035 if (drive->mount) delete_mount_point( drive->mount );
1036 drive->mount = NULL;
1037 drive->drive = letter;
1039 if (drive->volume != volume)
1041 if (drive->mount) delete_mount_point( drive->mount );
1042 drive->mount = NULL;
1043 grab_volume( volume );
1044 release_volume( drive->volume );
1045 drive->volume = volume;
1049 /* create devices for mapped drives */
1050 static void create_drive_devices(void)
1052 char dosdev[] = "a::";
1053 struct dos_drive *drive;
1054 struct volume *volume;
1055 unsigned int i;
1056 HKEY drives_key;
1057 enum device_type drive_type;
1058 WCHAR driveW[] = L"a:";
1060 if (RegOpenKeyW( HKEY_LOCAL_MACHINE, L"Software\\Wine\\Drives", &drives_key )) drives_key = 0;
1062 for (i = 0; i < MAX_DOS_DRIVES; i++)
1064 char link[4096], unix_dev[4096];
1065 char *device = NULL;
1066 struct get_dosdev_symlink_params params = { dosdev, link, sizeof(link) };
1068 dosdev[0] = 'a' + i;
1069 dosdev[2] = 0;
1070 if (MOUNTMGR_CALL( get_dosdev_symlink, &params )) continue;
1071 dosdev[2] = ':';
1072 params.dest = unix_dev;
1073 params.size = sizeof(unix_dev);
1074 if (!MOUNTMGR_CALL( get_dosdev_symlink, &params )) device = unix_dev;
1076 drive_type = i < 2 ? DEVICE_FLOPPY : DEVICE_HARDDISK_VOL;
1077 if (drives_key)
1079 WCHAR buffer[32];
1080 DWORD j, type, size = sizeof(buffer);
1082 driveW[0] = 'a' + i;
1083 if (!RegQueryValueExW( drives_key, driveW, NULL, &type, (BYTE *)buffer, &size ) &&
1084 type == REG_SZ)
1086 for (j = 0; j < ARRAY_SIZE(drive_types); j++)
1087 if (drive_types[j][0] && !wcsicmp( buffer, drive_types[j] ))
1089 drive_type = j;
1090 break;
1092 if (drive_type == DEVICE_FLOPPY && i >= 2) drive_type = DEVICE_HARDDISK;
1096 volume = find_matching_volume( NULL, device, link, drive_type );
1097 if (!create_dos_device( volume, NULL, i, drive_type, &drive ))
1099 /* don't reset uuid if we used an existing volume */
1100 const GUID *guid = volume ? NULL : get_default_uuid(i);
1101 set_volume_info( drive->volume, drive, device, link, drive_type, guid, NULL );
1103 if (volume) release_volume( volume );
1105 RegCloseKey( drives_key );
1108 /* fill in the "Logical Unit" key for a given SCSI address */
1109 static void create_scsi_entry( struct volume *volume, const struct scsi_info *info )
1111 static UCHAR tape_no = 0;
1113 WCHAR dataW[50];
1114 DWORD sizeW;
1115 DWORD value;
1116 const char *data;
1117 HKEY scsi_key;
1118 HKEY port_key;
1119 HKEY bus_key;
1120 HKEY target_key;
1121 HKEY lun_key;
1123 if (RegOpenKeyExW( HKEY_LOCAL_MACHINE, L"HARDWARE\\DEVICEMAP\\Scsi", 0, KEY_READ|KEY_WRITE, &scsi_key )) return;
1125 swprintf( dataW, ARRAY_SIZE( dataW ), L"Scsi Port %d", info->addr.PortNumber );
1126 if (RegCreateKeyExW( scsi_key, dataW, 0, NULL, REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &port_key, NULL )) return;
1127 RegCloseKey( scsi_key );
1129 RtlMultiByteToUnicodeN( dataW, sizeof(dataW), &sizeW, info->driver, strlen(info->driver)+1);
1130 RegSetValueExW( port_key, L"Driver", 0, REG_SZ, (const BYTE *)dataW, sizeW );
1131 value = 10;
1132 RegSetValueExW( port_key, L"FirstBusTimeScanInMs", 0, REG_DWORD, (const BYTE *)&value, sizeof(value));
1134 value = 0;
1136 swprintf( dataW, ARRAY_SIZE( dataW ), L"Scsi Bus %d", info->addr.PathId );
1137 if (RegCreateKeyExW( port_key, dataW, 0, NULL, REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &bus_key, NULL )) return;
1138 RegCloseKey( port_key );
1140 swprintf( dataW, ARRAY_SIZE( dataW ), L"Initiator Id %d", info->init_id );
1141 if (RegCreateKeyExW( bus_key, dataW, 0, NULL, REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &target_key, NULL )) return;
1142 RegCloseKey( target_key );
1144 swprintf( dataW, ARRAY_SIZE( dataW ), L"Target Id %d", info->addr.TargetId );
1145 if (RegCreateKeyExW( bus_key, dataW, 0, NULL, REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &target_key, NULL )) return;
1146 RegCloseKey( bus_key );
1148 swprintf( dataW, ARRAY_SIZE( dataW ), L"Logical Unit Id %d", info->addr.Lun );
1149 if (RegCreateKeyExW( target_key, dataW, 0, NULL, REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &lun_key, NULL )) return;
1150 RegCloseKey( target_key );
1152 switch (info->type)
1154 case SCSI_DISK_PERIPHERAL: data = "DiskPeripheral"; break;
1155 case SCSI_TAPE_PERIPHERAL: data = "TapePeripheral"; break;
1156 case SCSI_PRINTER_PERIPHERAL: data = "PrinterPeripheral"; break;
1157 case SCSI_WORM_PERIPHERAL: data = "WormPeripheral"; break;
1158 case SCSI_CDROM_PERIPHERAL: data = "CdRomPeripheral"; break;
1159 case SCSI_SCANNER_PERIPHERAL: data = "ScannerPeripheral"; break;
1160 case SCSI_OPTICAL_DISK_PERIPHERAL: data = "OpticalDiskPeripheral"; break;
1161 case SCSI_MEDIUM_CHANGER_PERIPHERAL: data = "MediumChangerPeripheral"; break;
1162 case SCSI_COMMS_PERIPHERAL: data = "CommunicationsPeripheral"; break;
1163 case SCSI_ASC_GRAPHICS_PERIPHERAL:
1164 case SCSI_ASC_GRAPHICS2_PERIPHERAL: data = "ASCPrePressGraphicsPeripheral"; break;
1165 case SCSI_ARRAY_PERIPHERAL: data = "ArrayPeripheral"; break;
1166 case SCSI_ENCLOSURE_PERIPHERAL: data = "EnclosurePeripheral"; break;
1167 case SCSI_REDUCED_DISK_PERIPHERAL: data = "RBCPeripheral"; break;
1168 case SCSI_CARD_READER_PERIPHERAL: data = "CardReaderPeripheral"; break;
1169 case SCSI_BRIDGE_PERIPHERAL: data = "BridgePeripheral"; break;
1170 case SCSI_OBJECT_STORAGE_PERIPHERAL: /* Object-based storage devices */
1171 case SCSI_DRIVE_CONTROLLER_PERIPHERAL: /* Automation/drive controllers */
1172 case SCSI_REDUCED_CDROM_PERIPHERAL: /* Reduced-commands MM devices */
1173 case SCSI_PROCESSOR_PERIPHERAL: /* Processor devices (considered to be "Other" by Windows) */
1174 default: data = "OtherPeripheral"; break;
1176 RtlMultiByteToUnicodeN( dataW, sizeof(dataW), &sizeW, data, strlen(data)+1);
1177 RegSetValueExW( lun_key, L"Type", 0, REG_SZ, (const BYTE *)dataW, sizeW );
1179 RtlMultiByteToUnicodeN( dataW, sizeof(dataW), &sizeW, info->model, strlen(info->model)+1);
1180 RegSetValueExW( lun_key, L"Identifier", 0, REG_SZ, (const BYTE *)dataW, sizeW );
1182 if (volume)
1184 UNICODE_STRING *dev = &volume->device->name;
1185 WCHAR *buffer = wcschr( dev->Buffer+1, '\\' ) + 1;
1186 ULONG length = dev->Length - (buffer - dev->Buffer)*sizeof(WCHAR);
1187 RegSetValueExW( lun_key, L"DeviceName", 0, REG_SZ, (const BYTE *)buffer, length );
1189 else if (info->type == SCSI_TAPE_PERIPHERAL)
1191 swprintf( dataW, ARRAY_SIZE( dataW ), L"Tape%d", tape_no++ );
1192 RegSetValueExW( lun_key, L"DeviceName", 0, REG_SZ, (const BYTE *)dataW, lstrlenW( dataW ) );
1195 RegCloseKey( lun_key );
1198 /* create a new disk volume */
1199 NTSTATUS add_volume( const char *udi, const char *device, const char *mount_point,
1200 enum device_type type, const GUID *guid, const char *disk_serial,
1201 const struct scsi_info *scsi_info )
1203 struct volume *volume;
1204 NTSTATUS status = STATUS_SUCCESS;
1206 TRACE( "adding %s device %s mount %s type %u uuid %s\n", debugstr_a(udi),
1207 debugstr_a(device), debugstr_a(mount_point), type, debugstr_guid(guid) );
1209 EnterCriticalSection( &device_section );
1210 LIST_FOR_EACH_ENTRY( volume, &volumes_list, struct volume, entry )
1211 if (volume->udi && !strcmp( udi, volume->udi ))
1213 grab_volume( volume );
1214 goto found;
1217 /* udi not found, search for a non-dynamic volume */
1218 if ((volume = find_matching_volume( udi, device, mount_point, type ))) set_volume_udi( volume, udi );
1219 else status = create_volume( udi, type, &volume );
1221 found:
1222 if (!status) status = set_volume_info( volume, NULL, device, mount_point, type, guid, disk_serial );
1223 if (!status && scsi_info) create_scsi_entry( volume, scsi_info );
1224 if (volume) release_volume( volume );
1225 LeaveCriticalSection( &device_section );
1226 return status;
1229 /* remove a disk volume */
1230 NTSTATUS remove_volume( const char *udi )
1232 NTSTATUS status = STATUS_NO_SUCH_DEVICE;
1233 struct volume *volume;
1235 EnterCriticalSection( &device_section );
1236 LIST_FOR_EACH_ENTRY( volume, &volumes_list, struct volume, entry )
1238 if (!volume->udi || strcmp( udi, volume->udi )) continue;
1239 set_volume_udi( volume, NULL );
1240 status = STATUS_SUCCESS;
1241 break;
1243 LeaveCriticalSection( &device_section );
1244 return status;
1248 /* create a new dos drive */
1249 NTSTATUS add_dos_device( int letter, const char *udi, const char *device,
1250 const char *mount_point, enum device_type type, const GUID *guid,
1251 const struct scsi_info *scsi_info )
1253 HKEY hkey;
1254 NTSTATUS status = STATUS_SUCCESS;
1255 struct dos_drive *drive, *next;
1256 struct volume *volume;
1257 int notify = -1;
1258 char dosdev[] = "a::";
1260 EnterCriticalSection( &device_section );
1261 volume = find_matching_volume( udi, device, mount_point, type );
1263 if (letter == -1) /* auto-assign a letter */
1265 struct add_drive_params params = { device, type, &letter };
1266 if ((status = MOUNTMGR_CALL( add_drive, &params ))) goto done;
1268 LIST_FOR_EACH_ENTRY_SAFE( drive, next, &drives_list, struct dos_drive, entry )
1270 if (drive->volume->udi && !strcmp( udi, drive->volume->udi )) goto found;
1271 if (drive->drive == letter) delete_dos_device( drive );
1274 else /* simply reset the device symlink */
1276 struct set_dosdev_symlink_params params = { dosdev, device };
1278 LIST_FOR_EACH_ENTRY( drive, &drives_list, struct dos_drive, entry )
1279 if (drive->drive == letter) break;
1281 dosdev[0] = 'a' + letter;
1282 if (&drive->entry == &drives_list)
1284 MOUNTMGR_CALL( set_dosdev_symlink, &params );
1286 else
1288 if (!device || !drive->volume->device->unix_device ||
1289 strcmp( device, drive->volume->device->unix_device ))
1290 MOUNTMGR_CALL( set_dosdev_symlink, &params );
1291 delete_dos_device( drive );
1295 if ((status = create_dos_device( volume, udi, letter, type, &drive ))) goto done;
1297 found:
1298 if (!guid && !volume) guid = get_default_uuid( letter );
1299 if (!volume) volume = grab_volume( drive->volume );
1300 set_drive_info( drive, letter, volume );
1301 dosdev[0] = 'a' + drive->drive;
1302 dosdev[2] = 0;
1303 if (!mount_point || !volume->device->unix_mount || strcmp( mount_point, volume->device->unix_mount ))
1305 struct set_dosdev_symlink_params params = { dosdev, mount_point };
1306 MOUNTMGR_CALL( set_dosdev_symlink, &params );
1308 set_volume_info( volume, drive, device, mount_point, type, guid, NULL );
1310 TRACE( "added device %c: udi %s for %s on %s type %u\n",
1311 'a' + drive->drive, wine_dbgstr_a(udi), wine_dbgstr_a(device),
1312 wine_dbgstr_a(mount_point), type );
1314 /* hack: force the drive type in the registry */
1315 if (!RegCreateKeyW( HKEY_LOCAL_MACHINE, L"Software\\Wine\\Drives", &hkey ))
1317 const WCHAR *type_name = drive_types[type];
1318 WCHAR name[] = L"a:";
1320 name[0] += drive->drive;
1321 if (!type_name[0] && type == DEVICE_HARDDISK) type_name = drive_types[DEVICE_FLOPPY];
1322 if (type_name[0])
1323 RegSetValueExW( hkey, name, 0, REG_SZ, (const BYTE *)type_name,
1324 (lstrlenW(type_name) + 1) * sizeof(WCHAR) );
1325 else
1326 RegDeleteValueW( hkey, name );
1327 RegCloseKey( hkey );
1330 if (udi) notify = drive->drive;
1331 if (scsi_info) create_scsi_entry( volume, scsi_info );
1333 done:
1334 if (volume) release_volume( volume );
1335 LeaveCriticalSection( &device_section );
1336 if (notify != -1) send_notify( notify, DBT_DEVICEARRIVAL );
1337 return status;
1340 /* remove an existing dos drive, by letter or udi */
1341 NTSTATUS remove_dos_device( int letter, const char *udi )
1343 NTSTATUS status = STATUS_NO_SUCH_DEVICE;
1344 HKEY hkey;
1345 struct dos_drive *drive;
1346 char dosdev[] = "a:";
1347 int notify = -1;
1349 EnterCriticalSection( &device_section );
1350 LIST_FOR_EACH_ENTRY( drive, &drives_list, struct dos_drive, entry )
1352 struct set_dosdev_symlink_params params = { dosdev, NULL };
1354 if (udi)
1356 if (!drive->volume->udi) continue;
1357 if (strcmp( udi, drive->volume->udi )) continue;
1358 set_volume_udi( drive->volume, NULL );
1360 else if (drive->drive != letter) continue;
1362 dosdev[0] = 'a' + drive->drive;
1363 MOUNTMGR_CALL( set_dosdev_symlink, &params );
1365 /* clear the registry key too */
1366 if (!RegOpenKeyW( HKEY_LOCAL_MACHINE, L"Software\\Wine\\Drives", &hkey ))
1368 WCHAR name[] = L"a:";
1369 name[0] += drive->drive;
1370 RegDeleteValueW( hkey, name );
1371 RegCloseKey( hkey );
1374 if (udi && drive->volume->device->unix_mount) notify = drive->drive;
1376 delete_dos_device( drive );
1377 status = STATUS_SUCCESS;
1378 break;
1380 LeaveCriticalSection( &device_section );
1381 if (notify != -1) send_notify( notify, DBT_DEVICEREMOVECOMPLETE );
1382 return status;
1385 static enum mountmgr_fs_type get_mountmgr_fs_type(enum fs_type fs_type)
1387 switch (fs_type)
1389 case FS_ISO9660: return MOUNTMGR_FS_TYPE_ISO9660;
1390 case FS_UDF: return MOUNTMGR_FS_TYPE_UDF;
1391 case FS_FAT1216: return MOUNTMGR_FS_TYPE_FAT;
1392 case FS_FAT32: return MOUNTMGR_FS_TYPE_FAT32;
1393 default: return MOUNTMGR_FS_TYPE_NTFS;
1397 /* query information about an existing dos drive, by letter or udi */
1398 static struct volume *find_volume_by_letter( int letter )
1400 struct volume *volume = NULL;
1401 struct dos_drive *drive;
1403 LIST_FOR_EACH_ENTRY( drive, &drives_list, struct dos_drive, entry )
1405 if (drive->drive != letter) continue;
1406 volume = grab_volume( drive->volume );
1407 TRACE( "found matching volume %s for drive letter %c:\n", debugstr_guid(&volume->guid),
1408 'a' + letter );
1409 break;
1411 return volume;
1414 /* query information about an existing unix device, by dev_t */
1415 static struct volume *find_volume_by_unixdev( ULONGLONG unix_dev )
1417 struct volume *volume;
1419 LIST_FOR_EACH_ENTRY( volume, &volumes_list, struct volume, entry )
1421 struct match_unixdev_params params = { volume->device->unix_device, unix_dev };
1422 if (!volume->device->unix_device || !MOUNTMGR_CALL( match_unixdev, &params ))
1423 continue;
1425 TRACE( "found matching volume %s\n", debugstr_guid(&volume->guid) );
1426 return grab_volume( volume );
1428 return NULL;
1431 /* implementation of IOCTL_MOUNTMGR_QUERY_UNIX_DRIVE */
1432 NTSTATUS query_unix_drive( void *buff, SIZE_T insize, SIZE_T outsize, IO_STATUS_BLOCK *iosb )
1434 const struct mountmgr_unix_drive *input = buff;
1435 struct mountmgr_unix_drive *output = NULL;
1436 char *device, *mount_point;
1437 int letter = towlower( input->letter );
1438 DWORD size, type = DEVICE_UNKNOWN, serial;
1439 NTSTATUS status = STATUS_SUCCESS;
1440 enum mountmgr_fs_type fs_type;
1441 enum device_type device_type;
1442 struct volume *volume;
1443 char *ptr;
1444 WCHAR *label;
1446 if (letter && (letter < 'a' || letter > 'z')) return STATUS_INVALID_PARAMETER;
1448 EnterCriticalSection( &device_section );
1449 if (letter)
1450 volume = find_volume_by_letter( letter - 'a' );
1451 else
1452 volume = find_volume_by_unixdev( input->unix_dev );
1453 if (volume)
1455 device_type = volume->device->type;
1456 fs_type = get_mountmgr_fs_type( volume->fs_type );
1457 serial = volume->serial;
1458 device = strdup( volume->device->unix_device );
1459 mount_point = strdup( volume->device->unix_mount );
1460 label = wcsdup( volume->label );
1461 release_volume( volume );
1463 LeaveCriticalSection( &device_section );
1465 if (!volume)
1466 return STATUS_NO_SUCH_DEVICE;
1468 switch (device_type)
1470 case DEVICE_UNKNOWN: type = DRIVE_UNKNOWN; break;
1471 case DEVICE_HARDDISK: type = DRIVE_REMOVABLE; break;
1472 case DEVICE_HARDDISK_VOL: type = DRIVE_FIXED; break;
1473 case DEVICE_FLOPPY: type = DRIVE_REMOVABLE; break;
1474 case DEVICE_CDROM: type = DRIVE_CDROM; break;
1475 case DEVICE_DVD: type = DRIVE_CDROM; break;
1476 case DEVICE_NETWORK: type = DRIVE_REMOTE; break;
1477 case DEVICE_RAMDISK: type = DRIVE_RAMDISK; break;
1480 size = sizeof(*output);
1481 if (label) size += (lstrlenW(label) + 1) * sizeof(WCHAR);
1482 if (device) size += strlen(device) + 1;
1483 if (mount_point) size += strlen(mount_point) + 1;
1485 input = NULL;
1486 output = buff;
1487 output->size = size;
1488 output->letter = letter;
1489 output->type = type;
1490 output->fs_type = fs_type;
1491 output->serial = serial;
1492 output->mount_point_offset = 0;
1493 output->device_offset = 0;
1494 output->label_offset = 0;
1496 ptr = (char *)(output + 1);
1498 if (label && ptr + (lstrlenW(label) + 1) * sizeof(WCHAR) - (char *)output <= outsize)
1500 output->label_offset = ptr - (char *)output;
1501 lstrcpyW( (WCHAR *)ptr, label );
1502 ptr += (lstrlenW(label) + 1) * sizeof(WCHAR);
1504 if (mount_point && ptr + strlen(mount_point) + 1 - (char *)output <= outsize)
1506 output->mount_point_offset = ptr - (char *)output;
1507 strcpy( ptr, mount_point );
1508 ptr += strlen(ptr) + 1;
1510 if (device && ptr + strlen(device) + 1 - (char *)output <= outsize)
1512 output->device_offset = ptr - (char *)output;
1513 strcpy( ptr, device );
1514 ptr += strlen(ptr) + 1;
1517 TRACE( "returning %c: dev %s mount %s type %lu\n",
1518 letter, debugstr_a(device), debugstr_a(mount_point), type );
1520 iosb->Information = ptr - (char *)output;
1521 if (size > outsize) status = STATUS_BUFFER_OVERFLOW;
1523 free( device );
1524 free( mount_point );
1525 free( label );
1526 return status;
1529 static NTSTATUS query_property( struct disk_device *device, IRP *irp )
1531 IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp );
1532 STORAGE_PROPERTY_QUERY *query = irp->AssociatedIrp.SystemBuffer;
1533 NTSTATUS status;
1535 if (!irp->AssociatedIrp.SystemBuffer
1536 || irpsp->Parameters.DeviceIoControl.InputBufferLength < sizeof(STORAGE_PROPERTY_QUERY))
1538 return STATUS_INVALID_PARAMETER;
1541 /* Try to persuade application not to check property */
1542 if (query->QueryType == PropertyExistsQuery)
1544 return STATUS_NOT_SUPPORTED;
1547 switch (query->PropertyId)
1549 case StorageDeviceProperty:
1551 STORAGE_DEVICE_DESCRIPTOR *descriptor;
1552 DWORD len = sizeof(*descriptor);
1554 if (device->serial) len += strlen( device->serial ) + 1;
1556 if (irpsp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(STORAGE_DESCRIPTOR_HEADER))
1557 status = STATUS_INVALID_PARAMETER;
1558 else if (irpsp->Parameters.DeviceIoControl.OutputBufferLength < len)
1560 descriptor = irp->AssociatedIrp.SystemBuffer;
1561 descriptor->Version = sizeof(STORAGE_DEVICE_DESCRIPTOR);
1562 descriptor->Size = len;
1563 irp->IoStatus.Information = sizeof(STORAGE_DESCRIPTOR_HEADER);
1564 status = STATUS_SUCCESS;
1566 else
1568 FIXME( "Faking StorageDeviceProperty data\n" );
1570 memset( irp->AssociatedIrp.SystemBuffer, 0, irpsp->Parameters.DeviceIoControl.OutputBufferLength );
1571 descriptor = irp->AssociatedIrp.SystemBuffer;
1572 descriptor->Version = sizeof(STORAGE_DEVICE_DESCRIPTOR);
1573 descriptor->Size = len;
1574 descriptor->DeviceType = FILE_DEVICE_DISK;
1575 descriptor->DeviceTypeModifier = 0;
1576 descriptor->RemovableMedia = FALSE;
1577 descriptor->CommandQueueing = FALSE;
1578 descriptor->VendorIdOffset = 0;
1579 descriptor->ProductIdOffset = 0;
1580 descriptor->ProductRevisionOffset = 0;
1581 descriptor->BusType = BusTypeScsi;
1582 descriptor->RawPropertiesLength = 0;
1583 if (!device->serial) descriptor->SerialNumberOffset = 0;
1584 else
1586 descriptor->SerialNumberOffset = sizeof(*descriptor);
1587 strcpy( (char *)descriptor + descriptor->SerialNumberOffset, device->serial );
1589 irp->IoStatus.Information = len;
1590 status = STATUS_SUCCESS;
1593 break;
1595 default:
1596 FIXME( "Unsupported property %#x\n", query->PropertyId );
1597 status = STATUS_NOT_SUPPORTED;
1598 break;
1600 return status;
1603 static NTSTATUS WINAPI harddisk_query_volume( DEVICE_OBJECT *device, IRP *irp )
1605 IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp );
1606 int info_class = irpsp->Parameters.QueryVolume.FsInformationClass;
1607 ULONG length = irpsp->Parameters.QueryVolume.Length;
1608 struct disk_device *dev = device->DeviceExtension;
1609 PIO_STATUS_BLOCK io = &irp->IoStatus;
1610 struct volume *volume;
1611 NTSTATUS status;
1613 TRACE( "volume query %x length %lu\n", info_class, length );
1615 EnterCriticalSection( &device_section );
1616 volume = dev->volume;
1617 if (!volume)
1619 status = STATUS_BAD_DEVICE_TYPE;
1620 goto done;
1623 switch(info_class)
1625 case FileFsVolumeInformation:
1628 FILE_FS_VOLUME_INFORMATION *info = irp->AssociatedIrp.SystemBuffer;
1630 if (length < sizeof(FILE_FS_VOLUME_INFORMATION))
1632 status = STATUS_INFO_LENGTH_MISMATCH;
1633 break;
1636 info->VolumeCreationTime.QuadPart = 0; /* FIXME */
1637 info->VolumeSerialNumber = volume->serial;
1638 info->VolumeLabelLength = min( lstrlenW(volume->label) * sizeof(WCHAR),
1639 length - offsetof( FILE_FS_VOLUME_INFORMATION, VolumeLabel ) );
1640 info->SupportsObjects = (get_mountmgr_fs_type(volume->fs_type) == MOUNTMGR_FS_TYPE_NTFS);
1641 memcpy( info->VolumeLabel, volume->label, info->VolumeLabelLength );
1643 io->Information = offsetof( FILE_FS_VOLUME_INFORMATION, VolumeLabel ) + info->VolumeLabelLength;
1644 status = STATUS_SUCCESS;
1645 break;
1647 case FileFsSizeInformation:
1649 FILE_FS_SIZE_INFORMATION *info = irp->AssociatedIrp.SystemBuffer;
1650 struct size_info size_info = { 0, 0, 0, 0, 0 };
1651 struct get_volume_size_info_params params = { dev->unix_mount, &size_info };
1653 if (length < sizeof(FILE_FS_SIZE_INFORMATION))
1655 status = STATUS_BUFFER_TOO_SMALL;
1656 break;
1659 if ((status = MOUNTMGR_CALL( get_volume_size_info, &params )) == STATUS_SUCCESS)
1661 info->TotalAllocationUnits.QuadPart = size_info.total_allocation_units;
1662 info->AvailableAllocationUnits.QuadPart = size_info.caller_available_allocation_units;
1663 info->SectorsPerAllocationUnit = size_info.sectors_per_allocation_unit;
1664 info->BytesPerSector = size_info.bytes_per_sector;
1665 io->Information = sizeof(*info);
1666 status = STATUS_SUCCESS;
1669 break;
1671 case FileFsAttributeInformation:
1673 FILE_FS_ATTRIBUTE_INFORMATION *info = irp->AssociatedIrp.SystemBuffer;
1674 enum mountmgr_fs_type fs_type = get_mountmgr_fs_type(volume->fs_type);
1675 const WCHAR *fsname;
1677 if (length < sizeof(FILE_FS_ATTRIBUTE_INFORMATION))
1679 status = STATUS_INFO_LENGTH_MISMATCH;
1680 break;
1683 switch (fs_type)
1685 case MOUNTMGR_FS_TYPE_ISO9660:
1686 fsname = L"CDFS";
1687 info->FileSystemAttributes = FILE_READ_ONLY_VOLUME;
1688 info->MaximumComponentNameLength = 221;
1689 break;
1690 case MOUNTMGR_FS_TYPE_UDF:
1691 fsname = L"UDF";
1692 info->FileSystemAttributes = FILE_READ_ONLY_VOLUME | FILE_UNICODE_ON_DISK | FILE_CASE_SENSITIVE_SEARCH;
1693 info->MaximumComponentNameLength = 255;
1694 break;
1695 case MOUNTMGR_FS_TYPE_FAT:
1696 fsname = L"FAT";
1697 info->FileSystemAttributes = FILE_CASE_PRESERVED_NAMES; /* FIXME */
1698 info->MaximumComponentNameLength = 255;
1699 break;
1700 case MOUNTMGR_FS_TYPE_FAT32:
1701 fsname = L"FAT32";
1702 info->FileSystemAttributes = FILE_CASE_PRESERVED_NAMES; /* FIXME */
1703 info->MaximumComponentNameLength = 255;
1704 break;
1705 default:
1706 fsname = L"NTFS";
1707 info->FileSystemAttributes = FILE_CASE_PRESERVED_NAMES | FILE_PERSISTENT_ACLS;
1708 info->MaximumComponentNameLength = 255;
1709 break;
1711 info->FileSystemNameLength = min( wcslen(fsname) * sizeof(WCHAR), length - offsetof( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName ) );
1712 memcpy(info->FileSystemName, fsname, info->FileSystemNameLength);
1713 io->Information = offsetof( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName ) + info->FileSystemNameLength;
1714 status = STATUS_SUCCESS;
1715 break;
1717 case FileFsFullSizeInformation:
1719 FILE_FS_FULL_SIZE_INFORMATION *info = irp->AssociatedIrp.SystemBuffer;
1720 struct size_info size_info = { 0, 0, 0, 0, 0 };
1721 struct get_volume_size_info_params params = { dev->unix_mount, &size_info };
1723 if (length < sizeof(FILE_FS_FULL_SIZE_INFORMATION))
1725 status = STATUS_BUFFER_TOO_SMALL;
1726 break;
1729 if ((status = MOUNTMGR_CALL( get_volume_size_info, &params )) == STATUS_SUCCESS)
1731 info->TotalAllocationUnits.QuadPart = size_info.total_allocation_units;
1732 info->CallerAvailableAllocationUnits.QuadPart = size_info.caller_available_allocation_units;
1733 info->ActualAvailableAllocationUnits.QuadPart = size_info.actual_available_allocation_units;
1734 info->SectorsPerAllocationUnit = size_info.sectors_per_allocation_unit;
1735 info->BytesPerSector = size_info.bytes_per_sector;
1736 io->Information = sizeof(*info);
1737 status = STATUS_SUCCESS;
1740 break;
1743 default:
1744 FIXME("Unsupported volume query %x\n", irpsp->Parameters.QueryVolume.FsInformationClass);
1745 status = STATUS_NOT_SUPPORTED;
1746 break;
1749 done:
1750 io->Status = status;
1751 LeaveCriticalSection( &device_section );
1752 IoCompleteRequest( irp, IO_NO_INCREMENT );
1753 return status;
1756 /* handler for ioctls on the harddisk device */
1757 static NTSTATUS WINAPI harddisk_ioctl( DEVICE_OBJECT *device, IRP *irp )
1759 IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp );
1760 struct disk_device *dev = device->DeviceExtension;
1761 NTSTATUS status;
1763 TRACE( "ioctl %lx insize %lu outsize %lu\n",
1764 irpsp->Parameters.DeviceIoControl.IoControlCode,
1765 irpsp->Parameters.DeviceIoControl.InputBufferLength,
1766 irpsp->Parameters.DeviceIoControl.OutputBufferLength );
1768 EnterCriticalSection( &device_section );
1770 switch(irpsp->Parameters.DeviceIoControl.IoControlCode)
1772 case IOCTL_DISK_GET_DRIVE_GEOMETRY:
1774 DISK_GEOMETRY info;
1775 DWORD len = min( sizeof(info), irpsp->Parameters.DeviceIoControl.OutputBufferLength );
1777 info.Cylinders.QuadPart = 10000;
1778 info.MediaType = (dev->devnum.DeviceType == FILE_DEVICE_DISK) ? FixedMedia : RemovableMedia;
1779 info.TracksPerCylinder = 255;
1780 info.SectorsPerTrack = 63;
1781 info.BytesPerSector = 512;
1782 memcpy( irp->AssociatedIrp.SystemBuffer, &info, len );
1783 irp->IoStatus.Information = len;
1784 status = STATUS_SUCCESS;
1785 break;
1787 case IOCTL_DISK_GET_DRIVE_GEOMETRY_EX:
1789 DISK_GEOMETRY_EX info;
1790 DWORD len = min( sizeof(info), irpsp->Parameters.DeviceIoControl.OutputBufferLength );
1792 FIXME("The DISK_PARTITION_INFO and DISK_DETECTION_INFO structures will not be filled\n");
1794 info.Geometry.Cylinders.QuadPart = 10000;
1795 info.Geometry.MediaType = (dev->devnum.DeviceType == FILE_DEVICE_DISK) ? FixedMedia : RemovableMedia;
1796 info.Geometry.TracksPerCylinder = 255;
1797 info.Geometry.SectorsPerTrack = 63;
1798 info.Geometry.BytesPerSector = 512;
1799 info.DiskSize.QuadPart = info.Geometry.Cylinders.QuadPart * info.Geometry.TracksPerCylinder *
1800 info.Geometry.SectorsPerTrack * info.Geometry.BytesPerSector;
1801 info.Data[0] = 0;
1802 memcpy( irp->AssociatedIrp.SystemBuffer, &info, len );
1803 irp->IoStatus.Information = len;
1804 status = STATUS_SUCCESS;
1805 break;
1807 case IOCTL_STORAGE_GET_DEVICE_NUMBER:
1809 DWORD len = min( sizeof(dev->devnum), irpsp->Parameters.DeviceIoControl.OutputBufferLength );
1811 memcpy( irp->AssociatedIrp.SystemBuffer, &dev->devnum, len );
1812 irp->IoStatus.Information = len;
1813 status = STATUS_SUCCESS;
1814 break;
1816 case IOCTL_CDROM_READ_TOC:
1817 status = STATUS_INVALID_DEVICE_REQUEST;
1818 break;
1819 case IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS:
1821 DWORD len = min( 32, irpsp->Parameters.DeviceIoControl.OutputBufferLength );
1823 FIXME( "returning zero-filled buffer for IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS\n" );
1824 memset( irp->AssociatedIrp.SystemBuffer, 0, len );
1825 irp->IoStatus.Information = len;
1826 status = STATUS_SUCCESS;
1827 break;
1829 case IOCTL_STORAGE_QUERY_PROPERTY:
1830 status = query_property( dev, irp );
1831 break;
1832 default:
1834 ULONG code = irpsp->Parameters.DeviceIoControl.IoControlCode;
1835 FIXME("Unsupported ioctl %lx (device=%lx access=%lx func=%lx method=%lx)\n",
1836 code, code >> 16, (code >> 14) & 3, (code >> 2) & 0xfff, code & 3);
1837 status = STATUS_NOT_SUPPORTED;
1838 break;
1842 irp->IoStatus.Status = status;
1843 LeaveCriticalSection( &device_section );
1844 IoCompleteRequest( irp, IO_NO_INCREMENT );
1845 return status;
1848 /* driver entry point for the harddisk driver */
1849 NTSTATUS WINAPI harddisk_driver_entry( DRIVER_OBJECT *driver, UNICODE_STRING *path )
1851 struct disk_device *device;
1853 harddisk_driver = driver;
1854 driver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = harddisk_ioctl;
1855 driver->MajorFunction[IRP_MJ_QUERY_VOLUME_INFORMATION] = harddisk_query_volume;
1857 /* create a harddisk0 device that isn't assigned to any drive */
1858 create_disk_device( DEVICE_HARDDISK, &device, NULL );
1860 create_drive_devices();
1862 return STATUS_SUCCESS;
1866 /* create a serial or parallel port */
1867 static BOOL create_port_device( DRIVER_OBJECT *driver, int n, const char *unix_path,
1868 const char *dosdevices_path, HKEY windows_ports_key )
1870 const WCHAR *dos_name_format, *nt_name_format, *reg_value_format, *symlink_format, *default_device;
1871 WCHAR dos_name[7], reg_value[256], nt_buffer[32], symlink_buffer[32];
1872 UNICODE_STRING nt_name, symlink_name, default_name;
1873 DEVICE_OBJECT *dev_obj;
1874 NTSTATUS status;
1875 struct set_dosdev_symlink_params params = { dosdevices_path, unix_path };
1877 /* create DOS device */
1878 if (MOUNTMGR_CALL( set_dosdev_symlink, &params )) return FALSE;
1880 if (driver == serial_driver)
1882 dos_name_format = L"COM%u";
1883 nt_name_format = L"\\Device\\Serial%u";
1884 reg_value_format = L"COM%u";
1885 symlink_format = L"\\DosDevices\\COM%u";
1886 default_device = L"\\DosDevices\\AUX";
1888 else
1890 dos_name_format = L"LPT%u";
1891 nt_name_format = L"\\Device\\Parallel%u";
1892 reg_value_format = L"\\DosDevices\\LPT%u";
1893 symlink_format = L"\\DosDevices\\LPT%u";
1894 default_device = L"\\DosDevices\\PRN";
1897 swprintf( dos_name, ARRAY_SIZE(dos_name), dos_name_format, n );
1899 /* create NT device */
1900 swprintf( nt_buffer, ARRAY_SIZE(nt_buffer), nt_name_format, n - 1 );
1901 RtlInitUnicodeString( &nt_name, nt_buffer );
1902 status = IoCreateDevice( driver, 0, &nt_name, 0, 0, FALSE, &dev_obj );
1903 if (status != STATUS_SUCCESS)
1905 FIXME( "IoCreateDevice %s got %lx\n", debugstr_w(nt_name.Buffer), status );
1906 return FALSE;
1908 swprintf( symlink_buffer, ARRAY_SIZE(symlink_buffer), symlink_format, n );
1909 RtlInitUnicodeString( &symlink_name, symlink_buffer );
1910 IoCreateSymbolicLink( &symlink_name, &nt_name );
1911 if (n == 1)
1913 RtlInitUnicodeString( &default_name, default_device );
1914 IoCreateSymbolicLink( &default_name, &symlink_name );
1917 /* TODO: store information about the Unix device in the NT device */
1919 /* create registry entry */
1920 swprintf( reg_value, ARRAY_SIZE(reg_value), reg_value_format, n );
1921 RegSetValueExW( windows_ports_key, nt_name.Buffer, 0, REG_SZ,
1922 (BYTE *)reg_value, (lstrlenW( reg_value ) + 1) * sizeof(WCHAR) );
1924 return TRUE;
1927 /* find and create serial or parallel ports */
1928 static void create_port_devices( DRIVER_OBJECT *driver, const char *devices )
1930 const WCHAR *windows_ports_key_name;
1931 const char *dosdev_fmt;
1932 char dosdev[8];
1933 HKEY wine_ports_key = NULL, windows_ports_key = NULL;
1934 char unix_path[256];
1935 const WCHAR *port_prefix;
1936 WCHAR reg_value[256];
1937 BOOL used[MAX_PORTS];
1938 WCHAR port[7];
1939 DWORD port_len, type, size;
1940 int i, n;
1942 if (driver == serial_driver)
1944 dosdev_fmt = "com%u";
1945 windows_ports_key_name = L"HARDWARE\\DEVICEMAP\\SERIALCOMM";
1946 port_prefix = L"COM";
1948 else
1950 dosdev_fmt = "lpt%u";
1951 windows_ports_key_name = L"HARDWARE\\DEVICEMAP\\PARALLEL PORTS";
1952 port_prefix = L"LPT";
1955 /* @@ Wine registry key: HKLM\Software\Wine\Ports */
1957 RegCreateKeyExW( HKEY_LOCAL_MACHINE, L"Software\\Wine\\Ports", 0, NULL, 0,
1958 KEY_QUERY_VALUE, NULL, &wine_ports_key, NULL );
1959 RegCreateKeyExW( HKEY_LOCAL_MACHINE, windows_ports_key_name, 0, NULL, REG_OPTION_VOLATILE,
1960 KEY_ALL_ACCESS, NULL, &windows_ports_key, NULL );
1962 /* add user-defined serial ports */
1963 memset(used, 0, sizeof(used));
1964 for (i = 0; ; i++)
1966 port_len = ARRAY_SIZE(port);
1967 size = sizeof(reg_value);
1968 if (RegEnumValueW( wine_ports_key, i, port, &port_len, NULL,
1969 &type, (BYTE*)reg_value, &size ) != ERROR_SUCCESS)
1970 break;
1971 if (type != REG_SZ || wcsnicmp( port, port_prefix, 3 ))
1972 continue;
1974 n = wcstol( port + 3, NULL, 10 );
1975 if (n < 1 || n >= MAX_PORTS)
1976 continue;
1978 if (!WideCharToMultiByte( CP_UNIXCP, WC_ERR_INVALID_CHARS, reg_value, size/sizeof(WCHAR),
1979 unix_path, sizeof(unix_path), NULL, NULL))
1980 continue;
1982 used[n - 1] = TRUE;
1983 sprintf( dosdev, dosdev_fmt, n );
1984 create_port_device( driver, n, unix_path, dosdev, windows_ports_key );
1987 /* look for ports in the usual places */
1989 for (n = 1; *devices; n++, devices += strlen(devices) + 1)
1991 while (n <= MAX_PORTS && used[n - 1]) n++;
1992 if (n > MAX_PORTS) break;
1993 sprintf( dosdev, dosdev_fmt, n );
1994 create_port_device( driver, n, devices, dosdev, windows_ports_key );
1997 RegCloseKey( wine_ports_key );
1998 RegCloseKey( windows_ports_key );
2001 /* driver entry point for the serial port driver */
2002 NTSTATUS WINAPI serial_driver_entry( DRIVER_OBJECT *driver, UNICODE_STRING *path )
2004 char devices[4096];
2005 struct detect_ports_params params = { devices, sizeof(devices) };
2007 serial_driver = driver;
2008 /* TODO: fill in driver->MajorFunction */
2010 MOUNTMGR_CALL( detect_serial_ports, &params );
2011 create_port_devices( driver, devices );
2013 return STATUS_SUCCESS;
2016 /* driver entry point for the parallel port driver */
2017 NTSTATUS WINAPI parallel_driver_entry( DRIVER_OBJECT *driver, UNICODE_STRING *path )
2019 char devices[4096];
2020 struct detect_ports_params params = { devices, sizeof(devices) };
2022 parallel_driver = driver;
2023 /* TODO: fill in driver->MajorFunction */
2025 MOUNTMGR_CALL( detect_parallel_ports, &params );
2026 create_port_devices( driver, devices );
2028 return STATUS_SUCCESS;