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
27 #define NONAMELESSUNION
36 #include "wine/list.h"
37 #include "wine/debug.h"
39 WINE_DEFAULT_DEBUG_CHANNEL(mountmgr
);
41 #define MAX_DOS_DRIVES 26
44 static const WCHAR drive_types
[][8] =
46 L
"", /* DEVICE_UNKNOWN */
47 L
"", /* DEVICE_HARDDISK */
48 L
"hd", /* DEVICE_HARDDISK_VOL */
49 L
"floppy", /* DEVICE_FLOPPY */
50 L
"cdrom", /* DEVICE_CDROM */
51 L
"cdrom", /* DEVICE_DVD */
52 L
"network", /* DEVICE_NETWORK */
53 L
"ramdisk" /* DEVICE_RAMDISK */
58 FS_ERROR
, /* error accessing the device */
59 FS_UNKNOWN
, /* unknown file system */
63 FS_UDF
/* For reference [E] = Ecma-167.pdf, [U] = udf260.pdf */
68 enum device_type type
; /* drive type */
69 DEVICE_OBJECT
*dev_obj
; /* disk device allocated for this volume */
70 UNICODE_STRING name
; /* device name */
71 UNICODE_STRING symlink
; /* device symlink if any */
72 STORAGE_DEVICE_NUMBER devnum
; /* device number info */
73 char *unix_device
; /* unix device path */
74 char *unix_mount
; /* unix mount point path */
75 char *serial
; /* disk serial number */
76 struct volume
*volume
; /* associated volume */
81 struct list entry
; /* entry in volumes list */
82 struct disk_device
*device
; /* disk device */
83 char *udi
; /* unique identifier for dynamic volumes */
84 unsigned int ref
; /* ref count */
85 GUID guid
; /* volume uuid */
86 struct mount_point
*mount
; /* Volume{xxx} mount point */
87 WCHAR label
[256]; /* volume label */
88 DWORD serial
; /* volume serial number */
89 enum fs_type fs_type
; /* file system type */
94 struct list entry
; /* entry in drives list */
95 struct volume
*volume
; /* volume for this drive */
96 int drive
; /* drive letter (0 = A: etc.) */
97 struct mount_point
*mount
; /* DosDevices mount point */
100 static struct list drives_list
= LIST_INIT(drives_list
);
101 static struct list volumes_list
= LIST_INIT(volumes_list
);
103 static DRIVER_OBJECT
*harddisk_driver
;
104 static DRIVER_OBJECT
*serial_driver
;
105 static DRIVER_OBJECT
*parallel_driver
;
107 static CRITICAL_SECTION device_section
;
108 static CRITICAL_SECTION_DEBUG critsect_debug
=
110 0, 0, &device_section
,
111 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
112 0, 0, { (DWORD_PTR
)(__FILE__
": device_section") }
114 static CRITICAL_SECTION device_section
= { &critsect_debug
, -1, 0, 0, 0, 0 };
116 static char *strdupA( const char *str
)
120 if (!str
) return NULL
;
121 if ((ret
= RtlAllocateHeap( GetProcessHeap(), 0, strlen(str
) + 1 ))) strcpy( ret
, str
);
125 static WCHAR
*strdupW( const WCHAR
*str
)
129 if (!str
) return NULL
;
130 if ((ret
= RtlAllocateHeap( GetProcessHeap(), 0, (lstrlenW(str
) + 1) * sizeof(WCHAR
) ))) lstrcpyW( ret
, str
);
134 static const GUID
*get_default_uuid( int letter
)
138 guid
.Data4
[7] = 'A' + letter
;
142 /* send notification about a change to a given drive */
143 static void send_notify( int drive
, int code
)
145 DEV_BROADCAST_VOLUME info
;
147 info
.dbcv_size
= sizeof(info
);
148 info
.dbcv_devicetype
= DBT_DEVTYP_VOLUME
;
149 info
.dbcv_reserved
= 0;
150 info
.dbcv_unitmask
= 1 << drive
;
151 info
.dbcv_flags
= DBTF_MEDIA
;
152 BroadcastSystemMessageW( BSF_FORCEIFHUNG
|BSF_QUERY
, NULL
,
153 WM_DEVICECHANGE
, code
, (LPARAM
)&info
);
156 #define BLOCK_SIZE 2048
157 #define SUPERBLOCK_SIZE BLOCK_SIZE
159 #define CDFRAMES_PERSEC 75
160 #define CDFRAMES_PERMIN (CDFRAMES_PERSEC * 60)
161 #define FRAME_OF_ADDR(a) ((a)[1] * CDFRAMES_PERMIN + (a)[2] * CDFRAMES_PERSEC + (a)[3])
162 #define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc)->TrackData[(idx) - (toc)->FirstTrack].Address)
164 #define GETWORD(buf,off) MAKEWORD(buf[(off)],buf[(off+1)])
165 #define GETLONG(buf,off) MAKELONG(GETWORD(buf,off),GETWORD(buf,off+2))
167 /* get the label by reading it from a file at the root of the filesystem */
168 static void get_filesystem_label( struct volume
*volume
)
170 char buffer
[256], *p
;
171 ULONG size
= sizeof(buffer
);
172 struct read_volume_file_params params
= { volume
->device
->unix_mount
, ".windows-label", buffer
, &size
};
174 volume
->label
[0] = 0;
175 if (!volume
->device
->unix_mount
) return;
176 if (MOUNTMGR_CALL( read_volume_file
, ¶ms
)) return;
179 while (p
> buffer
&& (p
[-1] == ' ' || p
[-1] == '\r' || p
[-1] == '\n')) p
--;
181 if (!MultiByteToWideChar( CP_UNIXCP
, 0, buffer
, -1, volume
->label
, ARRAY_SIZE(volume
->label
) ))
182 volume
->label
[ARRAY_SIZE(volume
->label
) - 1] = 0;
185 /* get the serial number by reading it from a file at the root of the filesystem */
186 static void get_filesystem_serial( struct volume
*volume
)
189 ULONG size
= sizeof(buffer
);
190 struct read_volume_file_params params
= { volume
->device
->unix_mount
, ".windows-serial", buffer
, &size
};
193 if (!volume
->device
->unix_mount
) return;
194 if (MOUNTMGR_CALL( read_volume_file
, ¶ms
)) return;
197 volume
->serial
= strtoul( buffer
, NULL
, 16 );
201 /******************************************************************
202 * VOLUME_FindCdRomDataBestVoldesc
204 static DWORD
VOLUME_FindCdRomDataBestVoldesc( HANDLE handle
)
206 BYTE cur_vd_type
, max_vd_type
= 0;
208 DWORD size
, offs
, best_offs
= 0, extra_offs
= 0;
210 for (offs
= 0x8000; offs
<= 0x9800; offs
+= 0x800)
212 /* if 'CDROM' occurs at position 8, this is a pre-iso9660 cd, and
213 * the volume label is displaced forward by 8
215 if (SetFilePointer( handle
, offs
, NULL
, FILE_BEGIN
) != offs
) break;
216 if (!ReadFile( handle
, buffer
, sizeof(buffer
), &size
, NULL
)) break;
217 if (size
!= sizeof(buffer
)) break;
218 /* check for non-ISO9660 signature */
219 if (!memcmp( buffer
+ 11, "ROM", 3 )) extra_offs
= 8;
220 cur_vd_type
= buffer
[extra_offs
];
221 if (cur_vd_type
== 0xff) /* voldesc set terminator */
223 if (cur_vd_type
> max_vd_type
)
225 max_vd_type
= cur_vd_type
;
226 best_offs
= offs
+ extra_offs
;
233 /***********************************************************************
234 * VOLUME_ReadFATSuperblock
236 static enum fs_type
VOLUME_ReadFATSuperblock( HANDLE handle
, BYTE
*buff
)
240 /* try a fixed disk, with a FAT partition */
241 if (SetFilePointer( handle
, 0, NULL
, FILE_BEGIN
) != 0 ||
242 !ReadFile( handle
, buff
, SUPERBLOCK_SIZE
, &size
, NULL
))
244 if (GetLastError() == ERROR_BAD_DEV_TYPE
) return FS_UNKNOWN
; /* not a real device */
248 if (size
< SUPERBLOCK_SIZE
) return FS_UNKNOWN
;
250 /* FIXME: do really all FAT have their name beginning with
251 * "FAT" ? (At least FAT12, FAT16 and FAT32 have :)
253 if (!memcmp(buff
+0x36, "FAT", 3) || !memcmp(buff
+0x52, "FAT", 3))
255 /* guess which type of FAT we have */
257 unsigned int sectors
,
266 sect_per_fat
= GETWORD(buff
, 0x16);
267 if (!sect_per_fat
) sect_per_fat
= GETLONG(buff
, 0x24);
268 total_sectors
= GETWORD(buff
, 0x13);
270 total_sectors
= GETLONG(buff
, 0x20);
271 num_boot_sectors
= GETWORD(buff
, 0x0e);
272 num_fats
= buff
[0x10];
273 num_root_dir_ents
= GETWORD(buff
, 0x11);
274 bytes_per_sector
= GETWORD(buff
, 0x0b);
275 sectors_per_cluster
= buff
[0x0d];
276 /* check if the parameters are reasonable and will not cause
277 * arithmetic errors in the calculation */
278 reasonable
= num_boot_sectors
< total_sectors
&&
280 bytes_per_sector
>= 512 && bytes_per_sector
% 512 == 0 &&
281 sectors_per_cluster
>= 1;
282 if (!reasonable
) return FS_UNKNOWN
;
283 sectors
= total_sectors
- num_boot_sectors
- num_fats
* sect_per_fat
-
284 (num_root_dir_ents
* 32 + bytes_per_sector
- 1) / bytes_per_sector
;
285 nclust
= sectors
/ sectors_per_cluster
;
286 if ((buff
[0x42] == 0x28 || buff
[0x42] == 0x29) &&
287 !memcmp(buff
+0x52, "FAT", 3)) return FS_FAT32
;
290 if ((buff
[0x26] == 0x28 || buff
[0x26] == 0x29) &&
291 !memcmp(buff
+0x36, "FAT", 3))
299 /***********************************************************************
302 static BOOL
VOLUME_ReadCDBlock( HANDLE handle
, BYTE
*buff
, INT offs
)
304 DWORD size
, whence
= offs
>= 0 ? FILE_BEGIN
: FILE_END
;
306 if (SetFilePointer( handle
, offs
, NULL
, whence
) != offs
||
307 !ReadFile( handle
, buff
, SUPERBLOCK_SIZE
, &size
, NULL
) ||
308 size
!= SUPERBLOCK_SIZE
)
315 /***********************************************************************
316 * VOLUME_ReadCDSuperblock
318 static enum fs_type
VOLUME_ReadCDSuperblock( HANDLE handle
, BYTE
*buff
)
323 /* Check UDF first as UDF and ISO9660 structures can coexist on the same medium
324 * Starting from sector 16, we may find :
325 * - a CD-ROM Volume Descriptor Set (ISO9660) containing one or more Volume Descriptors
326 * - an Extended Area (UDF) -- [E] 2/8.3.1 and [U] 2.1.7
327 * There is no explicit end so read 16 sectors and then give up */
328 for( i
=16; i
<16+16; i
++)
330 if (!VOLUME_ReadCDBlock(handle
, buff
, i
*BLOCK_SIZE
))
333 /* We are supposed to check "BEA01", "NSR0x" and "TEA01" IDs + verify tag checksum
334 * but we assume the volume is well-formatted */
335 if (!memcmp(&buff
[1], "BEA01", 5)) return FS_UDF
;
338 offs
= VOLUME_FindCdRomDataBestVoldesc( handle
);
339 if (!offs
) return FS_UNKNOWN
;
341 if (!VOLUME_ReadCDBlock(handle
, buff
, offs
))
344 /* check for the iso9660 identifier */
345 if (!memcmp(&buff
[1], "CD001", 5)) return FS_ISO9660
;
350 /**************************************************************************
352 * Find the Primary Volume Descriptor
354 static BOOL
UDF_Find_PVD( HANDLE handle
, BYTE pvd
[] )
358 INT locations
[] = { 256, -1, -257, 512 };
360 for(i
=0; i
<ARRAY_SIZE(locations
); i
++)
362 if (!VOLUME_ReadCDBlock(handle
, pvd
, locations
[i
]*BLOCK_SIZE
))
365 /* Tag Identifier of Anchor Volume Descriptor Pointer is 2 -- [E] 3/10.2.1 */
366 if (pvd
[0]==2 && pvd
[1]==0)
368 /* Tag location (Uint32) at offset 12, little-endian */
369 offset
= pvd
[20 + 0];
370 offset
|= pvd
[20 + 1] << 8;
371 offset
|= pvd
[20 + 2] << 16;
372 offset
|= pvd
[20 + 3] << 24;
373 offset
*= BLOCK_SIZE
;
375 if (!VOLUME_ReadCDBlock(handle
, pvd
, offset
))
378 /* Check for the Primary Volume Descriptor Tag Id -- [E] 3/10.1.1 */
379 if (pvd
[0]!=1 || pvd
[1]!=0)
382 /* 8 or 16 bits per character -- [U] 2.1.1 */
383 if (!(pvd
[24]==8 || pvd
[24]==16))
394 /**************************************************************************
395 * VOLUME_GetSuperblockLabel
397 static void VOLUME_GetSuperblockLabel( struct volume
*volume
, HANDLE handle
, const BYTE
*superblock
)
399 const BYTE
*label_ptr
= NULL
;
401 BYTE pvd
[BLOCK_SIZE
];
403 switch (volume
->fs_type
)
409 get_filesystem_label( volume
);
412 label_ptr
= superblock
+ 0x2b;
416 label_ptr
= superblock
+ 0x47;
421 BYTE ver
= superblock
[0x5a];
423 if (superblock
[0x58] == 0x25 && superblock
[0x59] == 0x2f && /* Unicode ID */
424 ((ver
== 0x40) || (ver
== 0x43) || (ver
== 0x45)))
425 { /* yippee, unicode */
428 for (i
= 0; i
< 16; i
++)
429 volume
->label
[i
] = (superblock
[40+2*i
] << 8) | superblock
[41+2*i
];
430 volume
->label
[i
] = 0;
431 while (i
&& volume
->label
[i
-1] == ' ') volume
->label
[--i
] = 0;
434 label_ptr
= superblock
+ 40;
439 if(!UDF_Find_PVD(handle
, pvd
))
445 /* [E] 3/10.1.4 and [U] 2.1.1 */
448 label_ptr
= pvd
+ 24 + 1;
449 label_len
= pvd
[24+32-1];
456 label_len
= 1 + pvd
[24+32-1];
457 for (i
= 0; i
< label_len
; i
+= 2)
458 volume
->label
[i
/2] = (pvd
[24+1+i
] << 8) | pvd
[24+1+i
+1];
459 volume
->label
[label_len
] = 0;
463 if (label_len
) RtlMultiByteToUnicodeN( volume
->label
, sizeof(volume
->label
) - sizeof(WCHAR
),
464 &label_len
, (const char *)label_ptr
, label_len
);
465 label_len
/= sizeof(WCHAR
);
466 volume
->label
[label_len
] = 0;
467 while (label_len
&& volume
->label
[label_len
-1] == ' ') volume
->label
[--label_len
] = 0;
471 /**************************************************************************
472 * UDF_Find_FSD_Sector
473 * Find the File Set Descriptor used to compute the serial of a UDF volume
475 static int UDF_Find_FSD_Sector( HANDLE handle
, BYTE block
[] )
477 int i
, PVD_sector
, PD_sector
, PD_length
;
479 if(!UDF_Find_PVD(handle
,block
))
482 /* Retrieve the tag location of the PVD -- [E] 3/7.2 */
483 PVD_sector
= block
[12 + 0];
484 PVD_sector
|= block
[12 + 1] << 8;
485 PVD_sector
|= block
[12 + 2] << 16;
486 PVD_sector
|= block
[12 + 3] << 24;
488 /* Find the Partition Descriptor */
489 for(i
=PVD_sector
+1; ; i
++)
491 if(!VOLUME_ReadCDBlock(handle
, block
, i
*BLOCK_SIZE
))
494 /* Partition Descriptor Tag Id -- [E] 3/10.5.1 */
495 if(block
[0]==5 && block
[1]==0)
498 /* Terminating Descriptor Tag Id -- [E] 3/10.9.1 */
499 if(block
[0]==8 && block
[1]==0)
503 /* Find the partition starting location -- [E] 3/10.5.8 */
504 PD_sector
= block
[188 + 0];
505 PD_sector
|= block
[188 + 1] << 8;
506 PD_sector
|= block
[188 + 2] << 16;
507 PD_sector
|= block
[188 + 3] << 24;
509 /* Find the partition length -- [E] 3/10.5.9 */
510 PD_length
= block
[192 + 0];
511 PD_length
|= block
[192 + 1] << 8;
512 PD_length
|= block
[192 + 2] << 16;
513 PD_length
|= block
[192 + 3] << 24;
515 for(i
=PD_sector
; i
<PD_sector
+PD_length
; i
++)
517 if(!VOLUME_ReadCDBlock(handle
, block
, i
*BLOCK_SIZE
))
520 /* File Set Descriptor Tag Id -- [E] 3/14.1.1 */
521 if(block
[0]==0 && block
[1]==1)
526 WARN("FSD sector not found, serial may be incorrect\n");
531 /**************************************************************************
532 * VOLUME_GetSuperblockSerial
534 static void VOLUME_GetSuperblockSerial( struct volume
*volume
, HANDLE handle
, const BYTE
*superblock
)
537 BYTE block
[BLOCK_SIZE
];
539 switch (volume
->fs_type
)
544 get_filesystem_serial( volume
);
547 volume
->serial
= GETLONG( superblock
, 0x27 );
550 volume
->serial
= GETLONG( superblock
, 0x43 );
553 FSD_sector
= UDF_Find_FSD_Sector(handle
, block
);
554 if (!VOLUME_ReadCDBlock(handle
, block
, FSD_sector
*BLOCK_SIZE
))
563 sum
[0] = sum
[1] = sum
[2] = sum
[3] = 0;
564 for (i
= 0; i
< 2048; i
+= 4)
566 /* DON'T optimize this into DWORD !! (breaks overflow) */
567 sum
[0] += superblock
[i
+0];
568 sum
[1] += superblock
[i
+1];
569 sum
[2] += superblock
[i
+2];
570 sum
[3] += superblock
[i
+3];
573 * OK, another braindead one... argh. Just believe it.
574 * Me$$ysoft chose to reverse the serial number in NT4/W2K.
575 * It's true and nobody will ever be able to change it.
577 if ((GetVersion() & 0x80000000) || volume
->fs_type
== FS_UDF
)
578 volume
->serial
= (sum
[3] << 24) | (sum
[2] << 16) | (sum
[1] << 8) | sum
[0];
580 volume
->serial
= (sum
[0] << 24) | (sum
[1] << 16) | (sum
[2] << 8) | sum
[3];
586 /**************************************************************************
587 * VOLUME_GetAudioCDSerial
589 static DWORD
VOLUME_GetAudioCDSerial( const CDROM_TOC
*toc
)
594 for (i
= 0; i
<= toc
->LastTrack
- toc
->FirstTrack
; i
++)
595 serial
+= ((toc
->TrackData
[i
].Address
[1] << 16) |
596 (toc
->TrackData
[i
].Address
[2] << 8) |
597 toc
->TrackData
[i
].Address
[3]);
600 * dwStart, dwEnd collect the beginning and end of the disc respectively, in
602 * There it is collected for correcting the serial when there are less than
605 if (toc
->LastTrack
- toc
->FirstTrack
+ 1 < 3)
607 DWORD dwStart
= FRAME_OF_TOC(toc
, toc
->FirstTrack
);
608 DWORD dwEnd
= FRAME_OF_TOC(toc
, toc
->LastTrack
+ 1);
609 serial
+= dwEnd
- dwStart
;
615 /* create the disk device for a given volume */
616 static NTSTATUS
create_disk_device( enum device_type type
, struct disk_device
**device_ret
, struct volume
*volume
)
620 const WCHAR
*format
= NULL
;
621 const WCHAR
*link_format
= NULL
;
623 DEVICE_OBJECT
*dev_obj
;
624 struct disk_device
*device
;
629 case DEVICE_HARDDISK
:
630 case DEVICE_NETWORK
: /* FIXME */
631 format
= L
"\\Device\\Harddisk%u";
632 link_format
= L
"\\??\\PhysicalDrive%u";
634 case DEVICE_HARDDISK_VOL
:
635 format
= L
"\\Device\\HarddiskVolume%u";
636 first
= 1; /* harddisk volumes start counting from 1 */
639 format
= L
"\\Device\\Floppy%u";
643 format
= L
"\\Device\\CdRom%u";
644 link_format
= L
"\\??\\CdRom%u";
647 format
= L
"\\Device\\Ramdisk%u";
651 name
.MaximumLength
= (lstrlenW(format
) + 10) * sizeof(WCHAR
);
652 name
.Buffer
= RtlAllocateHeap( GetProcessHeap(), 0, name
.MaximumLength
);
653 for (i
= first
; i
< 32; i
++)
655 swprintf( name
.Buffer
, name
.MaximumLength
/ sizeof(WCHAR
), format
, i
);
656 name
.Length
= lstrlenW(name
.Buffer
) * sizeof(WCHAR
);
657 status
= IoCreateDevice( harddisk_driver
, sizeof(*device
), &name
, 0, 0, FALSE
, &dev_obj
);
658 if (status
!= STATUS_OBJECT_NAME_COLLISION
) break;
662 device
= dev_obj
->DeviceExtension
;
663 device
->dev_obj
= dev_obj
;
666 device
->unix_device
= NULL
;
667 device
->unix_mount
= NULL
;
668 device
->symlink
.Buffer
= NULL
;
669 device
->volume
= volume
;
673 UNICODE_STRING symlink
;
675 symlink
.MaximumLength
= (lstrlenW(link_format
) + 10) * sizeof(WCHAR
);
676 if ((symlink
.Buffer
= RtlAllocateHeap( GetProcessHeap(), 0, symlink
.MaximumLength
)))
678 swprintf( symlink
.Buffer
, symlink
.MaximumLength
/ sizeof(WCHAR
), link_format
, i
);
679 symlink
.Length
= lstrlenW(symlink
.Buffer
) * sizeof(WCHAR
);
680 if (!IoCreateSymbolicLink( &symlink
, &name
)) device
->symlink
= symlink
;
688 device
->devnum
.DeviceType
= FILE_DEVICE_DISK
;
689 device
->devnum
.DeviceNumber
= i
;
690 device
->devnum
.PartitionNumber
= ~0u;
693 device
->devnum
.DeviceType
= FILE_DEVICE_CD_ROM
;
694 device
->devnum
.DeviceNumber
= i
;
695 device
->devnum
.PartitionNumber
= ~0u;
698 device
->devnum
.DeviceType
= FILE_DEVICE_DVD
;
699 device
->devnum
.DeviceNumber
= i
;
700 device
->devnum
.PartitionNumber
= ~0u;
703 case DEVICE_HARDDISK
:
704 case DEVICE_NETWORK
: /* FIXME */
705 device
->devnum
.DeviceType
= FILE_DEVICE_DISK
;
706 device
->devnum
.DeviceNumber
= i
;
707 device
->devnum
.PartitionNumber
= 0;
709 case DEVICE_HARDDISK_VOL
:
710 device
->devnum
.DeviceType
= FILE_DEVICE_DISK
;
711 device
->devnum
.DeviceNumber
= 0;
712 device
->devnum
.PartitionNumber
= i
;
715 *device_ret
= device
;
716 TRACE( "created device %s\n", debugstr_w(name
.Buffer
) );
720 FIXME( "IoCreateDevice %s got %x\n", debugstr_w(name
.Buffer
), status
);
721 RtlFreeUnicodeString( &name
);
726 /* delete the disk device for a given drive */
727 static void delete_disk_device( struct disk_device
*device
)
729 TRACE( "deleting device %s\n", debugstr_w(device
->name
.Buffer
) );
730 if (device
->symlink
.Buffer
)
732 IoDeleteSymbolicLink( &device
->symlink
);
733 RtlFreeUnicodeString( &device
->symlink
);
735 RtlFreeHeap( GetProcessHeap(), 0, device
->unix_device
);
736 RtlFreeHeap( GetProcessHeap(), 0, device
->unix_mount
);
737 RtlFreeHeap( GetProcessHeap(), 0, device
->serial
);
738 RtlFreeUnicodeString( &device
->name
);
739 IoDeleteDevice( device
->dev_obj
);
742 /* grab another reference to a volume */
743 static struct volume
*grab_volume( struct volume
*volume
)
749 /* release a volume and delete the corresponding disk device when refcount is 0 */
750 static unsigned int release_volume( struct volume
*volume
)
752 unsigned int ret
= --volume
->ref
;
756 TRACE( "%s udi %s\n", debugstr_guid(&volume
->guid
), debugstr_a(volume
->udi
) );
757 assert( !volume
->udi
);
758 list_remove( &volume
->entry
);
759 if (volume
->mount
) delete_mount_point( volume
->mount
);
760 delete_disk_device( volume
->device
);
761 RtlFreeHeap( GetProcessHeap(), 0, volume
);
766 /* set the volume udi */
767 static void set_volume_udi( struct volume
*volume
, const char *udi
)
771 assert( !volume
->udi
);
772 /* having a udi means the HAL side holds an extra reference */
773 if ((volume
->udi
= strdupA( udi
))) grab_volume( volume
);
775 else if (volume
->udi
)
777 RtlFreeHeap( GetProcessHeap(), 0, volume
->udi
);
779 release_volume( volume
);
783 /* create a disk volume */
784 static NTSTATUS
create_volume( const char *udi
, enum device_type type
, struct volume
**volume_ret
)
786 struct volume
*volume
;
789 if (!(volume
= RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*volume
) )))
790 return STATUS_NO_MEMORY
;
792 if (!(status
= create_disk_device( type
, &volume
->device
, volume
)))
794 if (udi
) set_volume_udi( volume
, udi
);
795 list_add_tail( &volumes_list
, &volume
->entry
);
796 *volume_ret
= grab_volume( volume
);
798 else RtlFreeHeap( GetProcessHeap(), 0, volume
);
803 /* create the disk device for a given volume */
804 static NTSTATUS
create_dos_device( struct volume
*volume
, const char *udi
, int letter
,
805 enum device_type type
, struct dos_drive
**drive_ret
)
807 struct dos_drive
*drive
;
810 if (!(drive
= RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*drive
) ))) return STATUS_NO_MEMORY
;
811 drive
->drive
= letter
;
816 if (udi
) set_volume_udi( volume
, udi
);
817 drive
->volume
= grab_volume( volume
);
818 status
= STATUS_SUCCESS
;
820 else status
= create_volume( udi
, type
, &drive
->volume
);
822 if (status
== STATUS_SUCCESS
)
824 list_add_tail( &drives_list
, &drive
->entry
);
827 else RtlFreeHeap( GetProcessHeap(), 0, drive
);
832 /* delete the disk device for a given drive */
833 static void delete_dos_device( struct dos_drive
*drive
)
835 list_remove( &drive
->entry
);
836 if (drive
->mount
) delete_mount_point( drive
->mount
);
837 release_volume( drive
->volume
);
838 RtlFreeHeap( GetProcessHeap(), 0, drive
);
841 /* find a volume that matches the parameters */
842 static struct volume
*find_matching_volume( const char *udi
, const char *device
,
843 const char *mount_point
, enum device_type type
)
845 struct volume
*volume
;
846 struct disk_device
*disk_device
;
848 LIST_FOR_EACH_ENTRY( volume
, &volumes_list
, struct volume
, entry
)
852 /* when we have a udi we only match drives added manually */
853 if (udi
&& volume
->udi
) continue;
854 /* and when we don't have a udi we only match dynamic drives */
855 if (!udi
&& !volume
->udi
) continue;
857 disk_device
= volume
->device
;
858 if (disk_device
->type
!= type
) continue;
859 if (device
&& disk_device
->unix_device
)
861 if (strcmp( device
, disk_device
->unix_device
)) continue;
864 if (mount_point
&& disk_device
->unix_mount
)
866 if (strcmp( mount_point
, disk_device
->unix_mount
)) continue;
869 if (!match
) continue;
870 TRACE( "found matching volume %s for device %s mount %s type %u\n",
871 debugstr_guid(&volume
->guid
), debugstr_a(device
), debugstr_a(mount_point
), type
);
872 return grab_volume( volume
);
877 static BOOL
get_volume_device_info( struct volume
*volume
)
879 const char *unix_device
= volume
->device
->unix_device
;
884 BYTE superblock
[SUPERBLOCK_SIZE
];
889 if (MOUNTMGR_CALL( check_device_access
, volume
->device
->unix_device
)) return FALSE
;
891 if (!(name
= wine_get_dos_file_name( unix_device
)))
893 ERR("Failed to convert %s to NT, err %u\n", debugstr_a(unix_device
), GetLastError());
896 handle
= CreateFileW( name
, GENERIC_READ
| SYNCHRONIZE
, FILE_SHARE_READ
| FILE_SHARE_WRITE
,
897 NULL
, OPEN_EXISTING
, 0, 0 );
898 RtlFreeHeap( GetProcessHeap(), 0, name
);
899 if (handle
== INVALID_HANDLE_VALUE
)
901 WARN("Failed to open %s, err %u\n", debugstr_a(unix_device
), GetLastError());
905 if (DeviceIoControl( handle
, IOCTL_CDROM_READ_TOC
, NULL
, 0, &toc
, sizeof(toc
), &size
, 0 ))
907 if (!(toc
.TrackData
[0].Control
& 0x04)) /* audio track */
909 TRACE( "%s: found audio CD\n", debugstr_a(unix_device
) );
910 wcscpy( volume
->label
, L
"Audio CD" );
911 volume
->serial
= VOLUME_GetAudioCDSerial( &toc
);
912 volume
->fs_type
= FS_ISO9660
;
913 CloseHandle( handle
);
916 volume
->fs_type
= VOLUME_ReadCDSuperblock( handle
, superblock
);
920 volume
->fs_type
= VOLUME_ReadFATSuperblock( handle
, superblock
);
921 if (volume
->fs_type
== FS_UNKNOWN
) volume
->fs_type
= VOLUME_ReadCDSuperblock( handle
, superblock
);
924 TRACE( "%s: found fs type %d\n", debugstr_a(unix_device
), volume
->fs_type
);
925 if (volume
->fs_type
== FS_ERROR
)
927 CloseHandle( handle
);
931 VOLUME_GetSuperblockLabel( volume
, handle
, superblock
);
932 VOLUME_GetSuperblockSerial( volume
, handle
, superblock
);
934 CloseHandle( handle
);
938 /* set disk serial for dos devices that reside on a given Unix device */
939 static void set_dos_devices_disk_serial( struct disk_device
*device
)
941 unsigned int devices
;
942 struct dos_drive
*drive
;
943 struct get_volume_dos_devices_params params
= { device
->unix_mount
, &devices
};
945 if (!device
->serial
|| !device
->unix_mount
|| MOUNTMGR_CALL( get_volume_dos_devices
, ¶ms
))
948 LIST_FOR_EACH_ENTRY( drive
, &drives_list
, struct dos_drive
, entry
)
950 /* drives mapped to Unix devices already have serial set, if available */
951 if (drive
->volume
->device
->unix_device
) continue;
952 /* copy serial if drive resides on this Unix device */
953 if (devices
& (1 << drive
->drive
))
955 HeapFree( GetProcessHeap(), 0, drive
->volume
->device
->serial
);
956 drive
->volume
->device
->serial
= strdupA( device
->serial
);
961 /* change the information for an existing volume */
962 static NTSTATUS
set_volume_info( struct volume
*volume
, struct dos_drive
*drive
, const char *device
,
963 const char *mount_point
, enum device_type type
, const GUID
*guid
,
964 const char *disk_serial
)
967 unsigned int id_len
= 0;
968 struct disk_device
*disk_device
= volume
->device
;
971 if (type
!= disk_device
->type
)
973 if ((status
= create_disk_device( type
, &disk_device
, volume
))) return status
;
976 delete_mount_point( volume
->mount
);
977 volume
->mount
= NULL
;
979 if (drive
&& drive
->mount
)
981 delete_mount_point( drive
->mount
);
984 delete_disk_device( volume
->device
);
985 volume
->device
= disk_device
;
989 RtlFreeHeap( GetProcessHeap(), 0, disk_device
->unix_device
);
990 RtlFreeHeap( GetProcessHeap(), 0, disk_device
->unix_mount
);
991 RtlFreeHeap( GetProcessHeap(), 0, disk_device
->serial
);
993 disk_device
->unix_device
= strdupA( device
);
994 disk_device
->unix_mount
= strdupA( mount_point
);
995 disk_device
->serial
= strdupA( disk_serial
);
996 set_dos_devices_disk_serial( disk_device
);
998 if (!get_volume_device_info( volume
))
1000 if (volume
->device
->type
== DEVICE_CDROM
)
1001 volume
->fs_type
= FS_ISO9660
;
1002 else if (volume
->device
->type
== DEVICE_DVD
)
1003 volume
->fs_type
= FS_UDF
;
1005 volume
->fs_type
= FS_UNKNOWN
;
1007 get_filesystem_label( volume
);
1008 get_filesystem_serial( volume
);
1011 TRACE("fs_type %#x, label %s, serial %08x\n", volume
->fs_type
, debugstr_w(volume
->label
), volume
->serial
);
1013 if (guid
&& memcmp( &volume
->guid
, guid
, sizeof(volume
->guid
) ))
1015 volume
->guid
= *guid
;
1018 delete_mount_point( volume
->mount
);
1019 volume
->mount
= NULL
;
1023 if (!volume
->serial
)
1024 memcpy(&volume
->serial
, &volume
->guid
.Data4
[4], sizeof(DWORD
));
1027 volume
->mount
= add_volume_mount_point( disk_device
->dev_obj
, &disk_device
->name
, &volume
->guid
);
1028 if (drive
&& !drive
->mount
)
1029 drive
->mount
= add_dosdev_mount_point( disk_device
->dev_obj
, &disk_device
->name
, drive
->drive
);
1031 if (disk_device
->unix_mount
)
1033 id
= disk_device
->unix_mount
;
1034 id_len
= strlen( disk_device
->unix_mount
) + 1;
1036 if (volume
->mount
) set_mount_point_id( volume
->mount
, id
, id_len
);
1037 if (drive
&& drive
->mount
) set_mount_point_id( drive
->mount
, id
, id_len
);
1039 return STATUS_SUCCESS
;
1042 /* change the drive letter or volume for an existing drive */
1043 static void set_drive_info( struct dos_drive
*drive
, int letter
, struct volume
*volume
)
1045 if (drive
->drive
!= letter
)
1047 if (drive
->mount
) delete_mount_point( drive
->mount
);
1048 drive
->mount
= NULL
;
1049 drive
->drive
= letter
;
1051 if (drive
->volume
!= volume
)
1053 if (drive
->mount
) delete_mount_point( drive
->mount
);
1054 drive
->mount
= NULL
;
1055 grab_volume( volume
);
1056 release_volume( drive
->volume
);
1057 drive
->volume
= volume
;
1061 /* create devices for mapped drives */
1062 static void create_drive_devices(void)
1064 char dosdev
[] = "a::";
1065 struct dos_drive
*drive
;
1066 struct volume
*volume
;
1069 enum device_type drive_type
;
1070 WCHAR driveW
[] = L
"a:";
1072 if (RegOpenKeyW( HKEY_LOCAL_MACHINE
, L
"Software\\Wine\\Drives", &drives_key
)) drives_key
= 0;
1074 for (i
= 0; i
< MAX_DOS_DRIVES
; i
++)
1076 char link
[4096], unix_dev
[4096];
1077 char *device
= NULL
;
1078 struct get_dosdev_symlink_params params
= { dosdev
, link
, sizeof(link
) };
1080 dosdev
[0] = 'a' + i
;
1082 if (MOUNTMGR_CALL( get_dosdev_symlink
, ¶ms
)) continue;
1084 params
.dest
= unix_dev
;
1085 params
.size
= sizeof(unix_dev
);
1086 if (!MOUNTMGR_CALL( get_dosdev_symlink
, ¶ms
)) device
= unix_dev
;
1088 drive_type
= i
< 2 ? DEVICE_FLOPPY
: DEVICE_HARDDISK_VOL
;
1092 DWORD j
, type
, size
= sizeof(buffer
);
1094 driveW
[0] = 'a' + i
;
1095 if (!RegQueryValueExW( drives_key
, driveW
, NULL
, &type
, (BYTE
*)buffer
, &size
) &&
1098 for (j
= 0; j
< ARRAY_SIZE(drive_types
); j
++)
1099 if (drive_types
[j
][0] && !wcsicmp( buffer
, drive_types
[j
] ))
1104 if (drive_type
== DEVICE_FLOPPY
&& i
>= 2) drive_type
= DEVICE_HARDDISK
;
1108 volume
= find_matching_volume( NULL
, device
, link
, drive_type
);
1109 if (!create_dos_device( volume
, NULL
, i
, drive_type
, &drive
))
1111 /* don't reset uuid if we used an existing volume */
1112 const GUID
*guid
= volume
? NULL
: get_default_uuid(i
);
1113 set_volume_info( drive
->volume
, drive
, device
, link
, drive_type
, guid
, NULL
);
1115 if (volume
) release_volume( volume
);
1117 RegCloseKey( drives_key
);
1120 /* fill in the "Logical Unit" key for a given SCSI address */
1121 static void create_scsi_entry( struct volume
*volume
, const struct scsi_info
*info
)
1123 static UCHAR tape_no
= 0;
1135 if (RegOpenKeyExW( HKEY_LOCAL_MACHINE
, L
"HARDWARE\\DEVICEMAP\\Scsi", 0, KEY_READ
|KEY_WRITE
, &scsi_key
)) return;
1137 swprintf( dataW
, ARRAY_SIZE( dataW
), L
"Scsi Port %d", info
->addr
.PortNumber
);
1138 if (RegCreateKeyExW( scsi_key
, dataW
, 0, NULL
, REG_OPTION_VOLATILE
, KEY_ALL_ACCESS
, NULL
, &port_key
, NULL
)) return;
1139 RegCloseKey( scsi_key
);
1141 RtlMultiByteToUnicodeN( dataW
, sizeof(dataW
), &sizeW
, info
->driver
, strlen(info
->driver
)+1);
1142 RegSetValueExW( port_key
, L
"Driver", 0, REG_SZ
, (const BYTE
*)dataW
, sizeW
);
1144 RegSetValueExW( port_key
, L
"FirstBusTimeScanInMs", 0, REG_DWORD
, (const BYTE
*)&value
, sizeof(value
));
1148 swprintf( dataW
, ARRAY_SIZE( dataW
), L
"Scsi Bus %d", info
->addr
.PathId
);
1149 if (RegCreateKeyExW( port_key
, dataW
, 0, NULL
, REG_OPTION_VOLATILE
, KEY_ALL_ACCESS
, NULL
, &bus_key
, NULL
)) return;
1150 RegCloseKey( port_key
);
1152 swprintf( dataW
, ARRAY_SIZE( dataW
), L
"Initiator Id %d", info
->init_id
);
1153 if (RegCreateKeyExW( bus_key
, dataW
, 0, NULL
, REG_OPTION_VOLATILE
, KEY_ALL_ACCESS
, NULL
, &target_key
, NULL
)) return;
1154 RegCloseKey( target_key
);
1156 swprintf( dataW
, ARRAY_SIZE( dataW
), L
"Target Id %d", info
->addr
.TargetId
);
1157 if (RegCreateKeyExW( bus_key
, dataW
, 0, NULL
, REG_OPTION_VOLATILE
, KEY_ALL_ACCESS
, NULL
, &target_key
, NULL
)) return;
1158 RegCloseKey( bus_key
);
1160 swprintf( dataW
, ARRAY_SIZE( dataW
), L
"Logical Unit Id %d", info
->addr
.Lun
);
1161 if (RegCreateKeyExW( target_key
, dataW
, 0, NULL
, REG_OPTION_VOLATILE
, KEY_ALL_ACCESS
, NULL
, &lun_key
, NULL
)) return;
1162 RegCloseKey( target_key
);
1166 case SCSI_DISK_PERIPHERAL
: data
= "DiskPeripheral"; break;
1167 case SCSI_TAPE_PERIPHERAL
: data
= "TapePeripheral"; break;
1168 case SCSI_PRINTER_PERIPHERAL
: data
= "PrinterPeripheral"; break;
1169 case SCSI_WORM_PERIPHERAL
: data
= "WormPeripheral"; break;
1170 case SCSI_CDROM_PERIPHERAL
: data
= "CdRomPeripheral"; break;
1171 case SCSI_SCANNER_PERIPHERAL
: data
= "ScannerPeripheral"; break;
1172 case SCSI_OPTICAL_DISK_PERIPHERAL
: data
= "OpticalDiskPeripheral"; break;
1173 case SCSI_MEDIUM_CHANGER_PERIPHERAL
: data
= "MediumChangerPeripheral"; break;
1174 case SCSI_COMMS_PERIPHERAL
: data
= "CommunicationsPeripheral"; break;
1175 case SCSI_ASC_GRAPHICS_PERIPHERAL
:
1176 case SCSI_ASC_GRAPHICS2_PERIPHERAL
: data
= "ASCPrePressGraphicsPeripheral"; break;
1177 case SCSI_ARRAY_PERIPHERAL
: data
= "ArrayPeripheral"; break;
1178 case SCSI_ENCLOSURE_PERIPHERAL
: data
= "EnclosurePeripheral"; break;
1179 case SCSI_REDUCED_DISK_PERIPHERAL
: data
= "RBCPeripheral"; break;
1180 case SCSI_CARD_READER_PERIPHERAL
: data
= "CardReaderPeripheral"; break;
1181 case SCSI_BRIDGE_PERIPHERAL
: data
= "BridgePeripheral"; break;
1182 case SCSI_OBJECT_STORAGE_PERIPHERAL
: /* Object-based storage devices */
1183 case SCSI_DRIVE_CONTROLLER_PERIPHERAL
: /* Automation/drive controllers */
1184 case SCSI_REDUCED_CDROM_PERIPHERAL
: /* Reduced-commands MM devices */
1185 case SCSI_PROCESSOR_PERIPHERAL
: /* Processor devices (considered to be "Other" by Windows) */
1186 default: data
= "OtherPeripheral"; break;
1188 RtlMultiByteToUnicodeN( dataW
, sizeof(dataW
), &sizeW
, data
, strlen(data
)+1);
1189 RegSetValueExW( lun_key
, L
"Type", 0, REG_SZ
, (const BYTE
*)dataW
, sizeW
);
1191 RtlMultiByteToUnicodeN( dataW
, sizeof(dataW
), &sizeW
, info
->model
, strlen(info
->model
)+1);
1192 RegSetValueExW( lun_key
, L
"Identifier", 0, REG_SZ
, (const BYTE
*)dataW
, sizeW
);
1196 UNICODE_STRING
*dev
= &volume
->device
->name
;
1197 WCHAR
*buffer
= wcschr( dev
->Buffer
+1, '\\' ) + 1;
1198 ULONG length
= dev
->Length
- (buffer
- dev
->Buffer
)*sizeof(WCHAR
);
1199 RegSetValueExW( lun_key
, L
"DeviceName", 0, REG_SZ
, (const BYTE
*)buffer
, length
);
1201 else if (info
->type
== SCSI_TAPE_PERIPHERAL
)
1203 swprintf( dataW
, ARRAY_SIZE( dataW
), L
"Tape%d", tape_no
++ );
1204 RegSetValueExW( lun_key
, L
"DeviceName", 0, REG_SZ
, (const BYTE
*)dataW
, lstrlenW( dataW
) );
1207 RegCloseKey( lun_key
);
1210 /* create a new disk volume */
1211 NTSTATUS
add_volume( const char *udi
, const char *device
, const char *mount_point
,
1212 enum device_type type
, const GUID
*guid
, const char *disk_serial
,
1213 const struct scsi_info
*scsi_info
)
1215 struct volume
*volume
;
1216 NTSTATUS status
= STATUS_SUCCESS
;
1218 TRACE( "adding %s device %s mount %s type %u uuid %s\n", debugstr_a(udi
),
1219 debugstr_a(device
), debugstr_a(mount_point
), type
, debugstr_guid(guid
) );
1221 EnterCriticalSection( &device_section
);
1222 LIST_FOR_EACH_ENTRY( volume
, &volumes_list
, struct volume
, entry
)
1223 if (volume
->udi
&& !strcmp( udi
, volume
->udi
))
1225 grab_volume( volume
);
1229 /* udi not found, search for a non-dynamic volume */
1230 if ((volume
= find_matching_volume( udi
, device
, mount_point
, type
))) set_volume_udi( volume
, udi
);
1231 else status
= create_volume( udi
, type
, &volume
);
1234 if (!status
) status
= set_volume_info( volume
, NULL
, device
, mount_point
, type
, guid
, disk_serial
);
1235 if (!status
&& scsi_info
) create_scsi_entry( volume
, scsi_info
);
1236 if (volume
) release_volume( volume
);
1237 LeaveCriticalSection( &device_section
);
1241 /* remove a disk volume */
1242 NTSTATUS
remove_volume( const char *udi
)
1244 NTSTATUS status
= STATUS_NO_SUCH_DEVICE
;
1245 struct volume
*volume
;
1247 EnterCriticalSection( &device_section
);
1248 LIST_FOR_EACH_ENTRY( volume
, &volumes_list
, struct volume
, entry
)
1250 if (!volume
->udi
|| strcmp( udi
, volume
->udi
)) continue;
1251 set_volume_udi( volume
, NULL
);
1252 status
= STATUS_SUCCESS
;
1255 LeaveCriticalSection( &device_section
);
1260 /* create a new dos drive */
1261 NTSTATUS
add_dos_device( int letter
, const char *udi
, const char *device
,
1262 const char *mount_point
, enum device_type type
, const GUID
*guid
,
1263 const struct scsi_info
*scsi_info
)
1266 NTSTATUS status
= STATUS_SUCCESS
;
1267 struct dos_drive
*drive
, *next
;
1268 struct volume
*volume
;
1270 char dosdev
[] = "a::";
1272 EnterCriticalSection( &device_section
);
1273 volume
= find_matching_volume( udi
, device
, mount_point
, type
);
1275 if (letter
== -1) /* auto-assign a letter */
1277 struct add_drive_params params
= { device
, type
, &letter
};
1278 if ((status
= MOUNTMGR_CALL( add_drive
, ¶ms
))) goto done
;
1280 LIST_FOR_EACH_ENTRY_SAFE( drive
, next
, &drives_list
, struct dos_drive
, entry
)
1282 if (drive
->volume
->udi
&& !strcmp( udi
, drive
->volume
->udi
)) goto found
;
1283 if (drive
->drive
== letter
) delete_dos_device( drive
);
1286 else /* simply reset the device symlink */
1288 struct set_dosdev_symlink_params params
= { dosdev
, device
};
1290 LIST_FOR_EACH_ENTRY( drive
, &drives_list
, struct dos_drive
, entry
)
1291 if (drive
->drive
== letter
) break;
1293 dosdev
[0] = 'a' + letter
;
1294 if (&drive
->entry
== &drives_list
)
1296 MOUNTMGR_CALL( set_dosdev_symlink
, ¶ms
);
1300 if (!device
|| !drive
->volume
->device
->unix_device
||
1301 strcmp( device
, drive
->volume
->device
->unix_device
))
1302 MOUNTMGR_CALL( set_dosdev_symlink
, ¶ms
);
1303 delete_dos_device( drive
);
1307 if ((status
= create_dos_device( volume
, udi
, letter
, type
, &drive
))) goto done
;
1310 if (!guid
&& !volume
) guid
= get_default_uuid( letter
);
1311 if (!volume
) volume
= grab_volume( drive
->volume
);
1312 set_drive_info( drive
, letter
, volume
);
1313 dosdev
[0] = 'a' + drive
->drive
;
1315 if (!mount_point
|| !volume
->device
->unix_mount
|| strcmp( mount_point
, volume
->device
->unix_mount
))
1317 struct set_dosdev_symlink_params params
= { dosdev
, mount_point
};
1318 MOUNTMGR_CALL( set_dosdev_symlink
, ¶ms
);
1320 set_volume_info( volume
, drive
, device
, mount_point
, type
, guid
, NULL
);
1322 TRACE( "added device %c: udi %s for %s on %s type %u\n",
1323 'a' + drive
->drive
, wine_dbgstr_a(udi
), wine_dbgstr_a(device
),
1324 wine_dbgstr_a(mount_point
), type
);
1326 /* hack: force the drive type in the registry */
1327 if (!RegCreateKeyW( HKEY_LOCAL_MACHINE
, L
"Software\\Wine\\Drives", &hkey
))
1329 const WCHAR
*type_name
= drive_types
[type
];
1330 WCHAR name
[] = L
"a:";
1332 name
[0] += drive
->drive
;
1333 if (!type_name
[0] && type
== DEVICE_HARDDISK
) type_name
= drive_types
[DEVICE_FLOPPY
];
1335 RegSetValueExW( hkey
, name
, 0, REG_SZ
, (const BYTE
*)type_name
,
1336 (lstrlenW(type_name
) + 1) * sizeof(WCHAR
) );
1338 RegDeleteValueW( hkey
, name
);
1339 RegCloseKey( hkey
);
1342 if (udi
) notify
= drive
->drive
;
1343 if (scsi_info
) create_scsi_entry( volume
, scsi_info
);
1346 if (volume
) release_volume( volume
);
1347 LeaveCriticalSection( &device_section
);
1348 if (notify
!= -1) send_notify( notify
, DBT_DEVICEARRIVAL
);
1352 /* remove an existing dos drive, by letter or udi */
1353 NTSTATUS
remove_dos_device( int letter
, const char *udi
)
1355 NTSTATUS status
= STATUS_NO_SUCH_DEVICE
;
1357 struct dos_drive
*drive
;
1358 char dosdev
[] = "a:";
1361 EnterCriticalSection( &device_section
);
1362 LIST_FOR_EACH_ENTRY( drive
, &drives_list
, struct dos_drive
, entry
)
1364 struct set_dosdev_symlink_params params
= { dosdev
, NULL
};
1368 if (!drive
->volume
->udi
) continue;
1369 if (strcmp( udi
, drive
->volume
->udi
)) continue;
1370 set_volume_udi( drive
->volume
, NULL
);
1372 else if (drive
->drive
!= letter
) continue;
1374 dosdev
[0] = 'a' + drive
->drive
;
1375 MOUNTMGR_CALL( set_dosdev_symlink
, ¶ms
);
1377 /* clear the registry key too */
1378 if (!RegOpenKeyW( HKEY_LOCAL_MACHINE
, L
"Software\\Wine\\Drives", &hkey
))
1380 WCHAR name
[] = L
"a:";
1381 name
[0] += drive
->drive
;
1382 RegDeleteValueW( hkey
, name
);
1383 RegCloseKey( hkey
);
1386 if (udi
&& drive
->volume
->device
->unix_mount
) notify
= drive
->drive
;
1388 delete_dos_device( drive
);
1389 status
= STATUS_SUCCESS
;
1392 LeaveCriticalSection( &device_section
);
1393 if (notify
!= -1) send_notify( notify
, DBT_DEVICEREMOVECOMPLETE
);
1397 static enum mountmgr_fs_type
get_mountmgr_fs_type(enum fs_type fs_type
)
1401 case FS_ISO9660
: return MOUNTMGR_FS_TYPE_ISO9660
;
1402 case FS_UDF
: return MOUNTMGR_FS_TYPE_UDF
;
1403 case FS_FAT1216
: return MOUNTMGR_FS_TYPE_FAT
;
1404 case FS_FAT32
: return MOUNTMGR_FS_TYPE_FAT32
;
1405 default: return MOUNTMGR_FS_TYPE_NTFS
;
1409 /* query information about an existing dos drive, by letter or udi */
1410 static struct volume
*find_volume_by_letter( int letter
)
1412 struct volume
*volume
= NULL
;
1413 struct dos_drive
*drive
;
1415 LIST_FOR_EACH_ENTRY( drive
, &drives_list
, struct dos_drive
, entry
)
1417 if (drive
->drive
!= letter
) continue;
1418 volume
= grab_volume( drive
->volume
);
1419 TRACE( "found matching volume %s for drive letter %c:\n", debugstr_guid(&volume
->guid
),
1426 /* query information about an existing unix device, by dev_t */
1427 static struct volume
*find_volume_by_unixdev( ULONGLONG unix_dev
)
1429 struct volume
*volume
;
1431 LIST_FOR_EACH_ENTRY( volume
, &volumes_list
, struct volume
, entry
)
1433 struct match_unixdev_params params
= { volume
->device
->unix_device
, unix_dev
};
1434 if (!volume
->device
->unix_device
|| !MOUNTMGR_CALL( match_unixdev
, ¶ms
))
1437 TRACE( "found matching volume %s\n", debugstr_guid(&volume
->guid
) );
1438 return grab_volume( volume
);
1443 /* implementation of IOCTL_MOUNTMGR_QUERY_UNIX_DRIVE */
1444 NTSTATUS
query_unix_drive( void *buff
, SIZE_T insize
, SIZE_T outsize
, IO_STATUS_BLOCK
*iosb
)
1446 const struct mountmgr_unix_drive
*input
= buff
;
1447 struct mountmgr_unix_drive
*output
= NULL
;
1448 char *device
, *mount_point
;
1449 int letter
= towlower( input
->letter
);
1450 DWORD size
, type
= DEVICE_UNKNOWN
, serial
;
1451 NTSTATUS status
= STATUS_SUCCESS
;
1452 enum mountmgr_fs_type fs_type
;
1453 enum device_type device_type
;
1454 struct volume
*volume
;
1458 if (letter
&& (letter
< 'a' || letter
> 'z')) return STATUS_INVALID_PARAMETER
;
1460 EnterCriticalSection( &device_section
);
1462 volume
= find_volume_by_letter( letter
- 'a' );
1464 volume
= find_volume_by_unixdev( input
->unix_dev
);
1467 device_type
= volume
->device
->type
;
1468 fs_type
= get_mountmgr_fs_type( volume
->fs_type
);
1469 serial
= volume
->serial
;
1470 device
= strdupA( volume
->device
->unix_device
);
1471 mount_point
= strdupA( volume
->device
->unix_mount
);
1472 label
= strdupW( volume
->label
);
1473 release_volume( volume
);
1475 LeaveCriticalSection( &device_section
);
1478 return STATUS_NO_SUCH_DEVICE
;
1480 switch (device_type
)
1482 case DEVICE_UNKNOWN
: type
= DRIVE_UNKNOWN
; break;
1483 case DEVICE_HARDDISK
: type
= DRIVE_REMOVABLE
; break;
1484 case DEVICE_HARDDISK_VOL
: type
= DRIVE_FIXED
; break;
1485 case DEVICE_FLOPPY
: type
= DRIVE_REMOVABLE
; break;
1486 case DEVICE_CDROM
: type
= DRIVE_CDROM
; break;
1487 case DEVICE_DVD
: type
= DRIVE_CDROM
; break;
1488 case DEVICE_NETWORK
: type
= DRIVE_REMOTE
; break;
1489 case DEVICE_RAMDISK
: type
= DRIVE_RAMDISK
; break;
1492 size
= sizeof(*output
);
1493 if (label
) size
+= (lstrlenW(label
) + 1) * sizeof(WCHAR
);
1494 if (device
) size
+= strlen(device
) + 1;
1495 if (mount_point
) size
+= strlen(mount_point
) + 1;
1499 output
->size
= size
;
1500 output
->letter
= letter
;
1501 output
->type
= type
;
1502 output
->fs_type
= fs_type
;
1503 output
->serial
= serial
;
1504 output
->mount_point_offset
= 0;
1505 output
->device_offset
= 0;
1506 output
->label_offset
= 0;
1508 ptr
= (char *)(output
+ 1);
1510 if (label
&& ptr
+ (lstrlenW(label
) + 1) * sizeof(WCHAR
) - (char *)output
<= outsize
)
1512 output
->label_offset
= ptr
- (char *)output
;
1513 lstrcpyW( (WCHAR
*)ptr
, label
);
1514 ptr
+= (lstrlenW(label
) + 1) * sizeof(WCHAR
);
1516 if (mount_point
&& ptr
+ strlen(mount_point
) + 1 - (char *)output
<= outsize
)
1518 output
->mount_point_offset
= ptr
- (char *)output
;
1519 strcpy( ptr
, mount_point
);
1520 ptr
+= strlen(ptr
) + 1;
1522 if (device
&& ptr
+ strlen(device
) + 1 - (char *)output
<= outsize
)
1524 output
->device_offset
= ptr
- (char *)output
;
1525 strcpy( ptr
, device
);
1526 ptr
+= strlen(ptr
) + 1;
1529 TRACE( "returning %c: dev %s mount %s type %u\n",
1530 letter
, debugstr_a(device
), debugstr_a(mount_point
), type
);
1532 iosb
->Information
= ptr
- (char *)output
;
1533 if (size
> outsize
) status
= STATUS_BUFFER_OVERFLOW
;
1535 RtlFreeHeap( GetProcessHeap(), 0, device
);
1536 RtlFreeHeap( GetProcessHeap(), 0, mount_point
);
1537 RtlFreeHeap( GetProcessHeap(), 0, label
);
1541 static NTSTATUS
query_property( struct disk_device
*device
, IRP
*irp
)
1543 IO_STACK_LOCATION
*irpsp
= IoGetCurrentIrpStackLocation( irp
);
1544 STORAGE_PROPERTY_QUERY
*query
= irp
->AssociatedIrp
.SystemBuffer
;
1547 if (!irp
->AssociatedIrp
.SystemBuffer
1548 || irpsp
->Parameters
.DeviceIoControl
.InputBufferLength
< sizeof(STORAGE_PROPERTY_QUERY
))
1550 return STATUS_INVALID_PARAMETER
;
1553 /* Try to persuade application not to check property */
1554 if (query
->QueryType
== PropertyExistsQuery
)
1556 return STATUS_NOT_SUPPORTED
;
1559 switch (query
->PropertyId
)
1561 case StorageDeviceProperty
:
1563 STORAGE_DEVICE_DESCRIPTOR
*descriptor
;
1564 DWORD len
= sizeof(*descriptor
);
1566 if (device
->serial
) len
+= strlen( device
->serial
) + 1;
1568 if (irpsp
->Parameters
.DeviceIoControl
.OutputBufferLength
< sizeof(STORAGE_DESCRIPTOR_HEADER
))
1569 status
= STATUS_INVALID_PARAMETER
;
1570 else if (irpsp
->Parameters
.DeviceIoControl
.OutputBufferLength
< len
)
1572 descriptor
= irp
->AssociatedIrp
.SystemBuffer
;
1573 descriptor
->Version
= sizeof(STORAGE_DEVICE_DESCRIPTOR
);
1574 descriptor
->Size
= len
;
1575 irp
->IoStatus
.Information
= sizeof(STORAGE_DESCRIPTOR_HEADER
);
1576 status
= STATUS_SUCCESS
;
1580 FIXME( "Faking StorageDeviceProperty data\n" );
1582 memset( irp
->AssociatedIrp
.SystemBuffer
, 0, irpsp
->Parameters
.DeviceIoControl
.OutputBufferLength
);
1583 descriptor
= irp
->AssociatedIrp
.SystemBuffer
;
1584 descriptor
->Version
= sizeof(STORAGE_DEVICE_DESCRIPTOR
);
1585 descriptor
->Size
= len
;
1586 descriptor
->DeviceType
= FILE_DEVICE_DISK
;
1587 descriptor
->DeviceTypeModifier
= 0;
1588 descriptor
->RemovableMedia
= FALSE
;
1589 descriptor
->CommandQueueing
= FALSE
;
1590 descriptor
->VendorIdOffset
= 0;
1591 descriptor
->ProductIdOffset
= 0;
1592 descriptor
->ProductRevisionOffset
= 0;
1593 descriptor
->BusType
= BusTypeScsi
;
1594 descriptor
->RawPropertiesLength
= 0;
1595 if (!device
->serial
) descriptor
->SerialNumberOffset
= 0;
1598 descriptor
->SerialNumberOffset
= sizeof(*descriptor
);
1599 strcpy( (char *)descriptor
+ descriptor
->SerialNumberOffset
, device
->serial
);
1601 irp
->IoStatus
.Information
= len
;
1602 status
= STATUS_SUCCESS
;
1608 FIXME( "Unsupported property %#x\n", query
->PropertyId
);
1609 status
= STATUS_NOT_SUPPORTED
;
1615 static NTSTATUS WINAPI
harddisk_query_volume( DEVICE_OBJECT
*device
, IRP
*irp
)
1617 IO_STACK_LOCATION
*irpsp
= IoGetCurrentIrpStackLocation( irp
);
1618 int info_class
= irpsp
->Parameters
.QueryVolume
.FsInformationClass
;
1619 ULONG length
= irpsp
->Parameters
.QueryVolume
.Length
;
1620 struct disk_device
*dev
= device
->DeviceExtension
;
1621 PIO_STATUS_BLOCK io
= &irp
->IoStatus
;
1622 struct volume
*volume
;
1625 TRACE( "volume query %x length %u\n", info_class
, length
);
1627 EnterCriticalSection( &device_section
);
1628 volume
= dev
->volume
;
1631 status
= STATUS_BAD_DEVICE_TYPE
;
1637 case FileFsVolumeInformation
:
1640 FILE_FS_VOLUME_INFORMATION
*info
= irp
->AssociatedIrp
.SystemBuffer
;
1642 if (length
< sizeof(FILE_FS_VOLUME_INFORMATION
))
1644 status
= STATUS_INFO_LENGTH_MISMATCH
;
1648 info
->VolumeCreationTime
.QuadPart
= 0; /* FIXME */
1649 info
->VolumeSerialNumber
= volume
->serial
;
1650 info
->VolumeLabelLength
= min( lstrlenW(volume
->label
) * sizeof(WCHAR
),
1651 length
- offsetof( FILE_FS_VOLUME_INFORMATION
, VolumeLabel
) );
1652 info
->SupportsObjects
= (get_mountmgr_fs_type(volume
->fs_type
) == MOUNTMGR_FS_TYPE_NTFS
);
1653 memcpy( info
->VolumeLabel
, volume
->label
, info
->VolumeLabelLength
);
1655 io
->Information
= offsetof( FILE_FS_VOLUME_INFORMATION
, VolumeLabel
) + info
->VolumeLabelLength
;
1656 status
= STATUS_SUCCESS
;
1659 case FileFsAttributeInformation
:
1661 FILE_FS_ATTRIBUTE_INFORMATION
*info
= irp
->AssociatedIrp
.SystemBuffer
;
1662 enum mountmgr_fs_type fs_type
= get_mountmgr_fs_type(volume
->fs_type
);
1663 const WCHAR
*fsname
;
1665 if (length
< sizeof(FILE_FS_ATTRIBUTE_INFORMATION
))
1667 status
= STATUS_INFO_LENGTH_MISMATCH
;
1673 case MOUNTMGR_FS_TYPE_ISO9660
:
1675 info
->FileSystemAttributes
= FILE_READ_ONLY_VOLUME
;
1676 info
->MaximumComponentNameLength
= 221;
1678 case MOUNTMGR_FS_TYPE_UDF
:
1680 info
->FileSystemAttributes
= FILE_READ_ONLY_VOLUME
| FILE_UNICODE_ON_DISK
| FILE_CASE_SENSITIVE_SEARCH
;
1681 info
->MaximumComponentNameLength
= 255;
1683 case MOUNTMGR_FS_TYPE_FAT
:
1685 info
->FileSystemAttributes
= FILE_CASE_PRESERVED_NAMES
; /* FIXME */
1686 info
->MaximumComponentNameLength
= 255;
1688 case MOUNTMGR_FS_TYPE_FAT32
:
1690 info
->FileSystemAttributes
= FILE_CASE_PRESERVED_NAMES
; /* FIXME */
1691 info
->MaximumComponentNameLength
= 255;
1695 info
->FileSystemAttributes
= FILE_CASE_PRESERVED_NAMES
| FILE_PERSISTENT_ACLS
;
1696 info
->MaximumComponentNameLength
= 255;
1699 info
->FileSystemNameLength
= min( wcslen(fsname
) * sizeof(WCHAR
), length
- offsetof( FILE_FS_ATTRIBUTE_INFORMATION
, FileSystemName
) );
1700 memcpy(info
->FileSystemName
, fsname
, info
->FileSystemNameLength
);
1701 io
->Information
= offsetof( FILE_FS_ATTRIBUTE_INFORMATION
, FileSystemName
) + info
->FileSystemNameLength
;
1702 status
= STATUS_SUCCESS
;
1706 FIXME("Unsupported volume query %x\n", irpsp
->Parameters
.QueryVolume
.FsInformationClass
);
1707 status
= STATUS_NOT_SUPPORTED
;
1712 io
->u
.Status
= status
;
1713 LeaveCriticalSection( &device_section
);
1714 IoCompleteRequest( irp
, IO_NO_INCREMENT
);
1718 /* handler for ioctls on the harddisk device */
1719 static NTSTATUS WINAPI
harddisk_ioctl( DEVICE_OBJECT
*device
, IRP
*irp
)
1721 IO_STACK_LOCATION
*irpsp
= IoGetCurrentIrpStackLocation( irp
);
1722 struct disk_device
*dev
= device
->DeviceExtension
;
1725 TRACE( "ioctl %x insize %u outsize %u\n",
1726 irpsp
->Parameters
.DeviceIoControl
.IoControlCode
,
1727 irpsp
->Parameters
.DeviceIoControl
.InputBufferLength
,
1728 irpsp
->Parameters
.DeviceIoControl
.OutputBufferLength
);
1730 EnterCriticalSection( &device_section
);
1732 switch(irpsp
->Parameters
.DeviceIoControl
.IoControlCode
)
1734 case IOCTL_DISK_GET_DRIVE_GEOMETRY
:
1737 DWORD len
= min( sizeof(info
), irpsp
->Parameters
.DeviceIoControl
.OutputBufferLength
);
1739 info
.Cylinders
.QuadPart
= 10000;
1740 info
.MediaType
= (dev
->devnum
.DeviceType
== FILE_DEVICE_DISK
) ? FixedMedia
: RemovableMedia
;
1741 info
.TracksPerCylinder
= 255;
1742 info
.SectorsPerTrack
= 63;
1743 info
.BytesPerSector
= 512;
1744 memcpy( irp
->AssociatedIrp
.SystemBuffer
, &info
, len
);
1745 irp
->IoStatus
.Information
= len
;
1746 status
= STATUS_SUCCESS
;
1749 case IOCTL_DISK_GET_DRIVE_GEOMETRY_EX
:
1751 DISK_GEOMETRY_EX info
;
1752 DWORD len
= min( sizeof(info
), irpsp
->Parameters
.DeviceIoControl
.OutputBufferLength
);
1754 FIXME("The DISK_PARTITION_INFO and DISK_DETECTION_INFO structures will not be filled\n");
1756 info
.Geometry
.Cylinders
.QuadPart
= 10000;
1757 info
.Geometry
.MediaType
= (dev
->devnum
.DeviceType
== FILE_DEVICE_DISK
) ? FixedMedia
: RemovableMedia
;
1758 info
.Geometry
.TracksPerCylinder
= 255;
1759 info
.Geometry
.SectorsPerTrack
= 63;
1760 info
.Geometry
.BytesPerSector
= 512;
1761 info
.DiskSize
.QuadPart
= info
.Geometry
.Cylinders
.QuadPart
* info
.Geometry
.TracksPerCylinder
*
1762 info
.Geometry
.SectorsPerTrack
* info
.Geometry
.BytesPerSector
;
1764 memcpy( irp
->AssociatedIrp
.SystemBuffer
, &info
, len
);
1765 irp
->IoStatus
.Information
= len
;
1766 status
= STATUS_SUCCESS
;
1769 case IOCTL_STORAGE_GET_DEVICE_NUMBER
:
1771 DWORD len
= min( sizeof(dev
->devnum
), irpsp
->Parameters
.DeviceIoControl
.OutputBufferLength
);
1773 memcpy( irp
->AssociatedIrp
.SystemBuffer
, &dev
->devnum
, len
);
1774 irp
->IoStatus
.Information
= len
;
1775 status
= STATUS_SUCCESS
;
1778 case IOCTL_CDROM_READ_TOC
:
1779 status
= STATUS_INVALID_DEVICE_REQUEST
;
1781 case IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS
:
1783 DWORD len
= min( 32, irpsp
->Parameters
.DeviceIoControl
.OutputBufferLength
);
1785 FIXME( "returning zero-filled buffer for IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS\n" );
1786 memset( irp
->AssociatedIrp
.SystemBuffer
, 0, len
);
1787 irp
->IoStatus
.Information
= len
;
1788 status
= STATUS_SUCCESS
;
1791 case IOCTL_STORAGE_QUERY_PROPERTY
:
1792 status
= query_property( dev
, irp
);
1796 ULONG code
= irpsp
->Parameters
.DeviceIoControl
.IoControlCode
;
1797 FIXME("Unsupported ioctl %x (device=%x access=%x func=%x method=%x)\n",
1798 code
, code
>> 16, (code
>> 14) & 3, (code
>> 2) & 0xfff, code
& 3);
1799 status
= STATUS_NOT_SUPPORTED
;
1804 irp
->IoStatus
.u
.Status
= status
;
1805 LeaveCriticalSection( &device_section
);
1806 IoCompleteRequest( irp
, IO_NO_INCREMENT
);
1810 /* driver entry point for the harddisk driver */
1811 NTSTATUS WINAPI
harddisk_driver_entry( DRIVER_OBJECT
*driver
, UNICODE_STRING
*path
)
1813 struct disk_device
*device
;
1815 harddisk_driver
= driver
;
1816 driver
->MajorFunction
[IRP_MJ_DEVICE_CONTROL
] = harddisk_ioctl
;
1817 driver
->MajorFunction
[IRP_MJ_QUERY_VOLUME_INFORMATION
] = harddisk_query_volume
;
1819 /* create a harddisk0 device that isn't assigned to any drive */
1820 create_disk_device( DEVICE_HARDDISK
, &device
, NULL
);
1822 create_drive_devices();
1824 return STATUS_SUCCESS
;
1828 /* create a serial or parallel port */
1829 static BOOL
create_port_device( DRIVER_OBJECT
*driver
, int n
, const char *unix_path
,
1830 const char *dosdevices_path
, HKEY windows_ports_key
)
1832 const WCHAR
*dos_name_format
, *nt_name_format
, *reg_value_format
, *symlink_format
, *default_device
;
1833 WCHAR dos_name
[7], reg_value
[256], nt_buffer
[32], symlink_buffer
[32];
1834 UNICODE_STRING nt_name
, symlink_name
, default_name
;
1835 DEVICE_OBJECT
*dev_obj
;
1837 struct set_dosdev_symlink_params params
= { dosdevices_path
, unix_path
};
1839 /* create DOS device */
1840 if (MOUNTMGR_CALL( set_dosdev_symlink
, ¶ms
)) return FALSE
;
1842 if (driver
== serial_driver
)
1844 dos_name_format
= L
"COM%u";
1845 nt_name_format
= L
"\\Device\\Serial%u";
1846 reg_value_format
= L
"COM%u";
1847 symlink_format
= L
"\\DosDevices\\COM%u";
1848 default_device
= L
"\\DosDevices\\AUX";
1852 dos_name_format
= L
"LPT%u";
1853 nt_name_format
= L
"\\Device\\Parallel%u";
1854 reg_value_format
= L
"\\DosDevices\\LPT%u";
1855 symlink_format
= L
"\\DosDevices\\LPT%u";
1856 default_device
= L
"\\DosDevices\\PRN";
1859 swprintf( dos_name
, ARRAY_SIZE(dos_name
), dos_name_format
, n
);
1861 /* create NT device */
1862 swprintf( nt_buffer
, ARRAY_SIZE(nt_buffer
), nt_name_format
, n
- 1 );
1863 RtlInitUnicodeString( &nt_name
, nt_buffer
);
1864 status
= IoCreateDevice( driver
, 0, &nt_name
, 0, 0, FALSE
, &dev_obj
);
1865 if (status
!= STATUS_SUCCESS
)
1867 FIXME( "IoCreateDevice %s got %x\n", debugstr_w(nt_name
.Buffer
), status
);
1870 swprintf( symlink_buffer
, ARRAY_SIZE(symlink_buffer
), symlink_format
, n
);
1871 RtlInitUnicodeString( &symlink_name
, symlink_buffer
);
1872 IoCreateSymbolicLink( &symlink_name
, &nt_name
);
1875 RtlInitUnicodeString( &default_name
, default_device
);
1876 IoCreateSymbolicLink( &default_name
, &symlink_name
);
1879 /* TODO: store information about the Unix device in the NT device */
1881 /* create registry entry */
1882 swprintf( reg_value
, ARRAY_SIZE(reg_value
), reg_value_format
, n
);
1883 RegSetValueExW( windows_ports_key
, nt_name
.Buffer
, 0, REG_SZ
,
1884 (BYTE
*)reg_value
, (lstrlenW( reg_value
) + 1) * sizeof(WCHAR
) );
1889 /* find and create serial or parallel ports */
1890 static void create_port_devices( DRIVER_OBJECT
*driver
, const char *devices
)
1892 const WCHAR
*windows_ports_key_name
;
1893 const char *dosdev_fmt
;
1895 HKEY wine_ports_key
= NULL
, windows_ports_key
= NULL
;
1896 char unix_path
[256];
1897 const WCHAR
*port_prefix
;
1898 WCHAR reg_value
[256];
1899 BOOL used
[MAX_PORTS
];
1901 DWORD port_len
, type
, size
;
1904 if (driver
== serial_driver
)
1906 dosdev_fmt
= "com%u";
1907 windows_ports_key_name
= L
"HARDWARE\\DEVICEMAP\\SERIALCOMM";
1908 port_prefix
= L
"COM";
1912 dosdev_fmt
= "lpt%u";
1913 windows_ports_key_name
= L
"HARDWARE\\DEVICEMAP\\PARALLEL PORTS";
1914 port_prefix
= L
"LPT";
1917 /* @@ Wine registry key: HKLM\Software\Wine\Ports */
1919 RegCreateKeyExW( HKEY_LOCAL_MACHINE
, L
"Software\\Wine\\Ports", 0, NULL
, 0,
1920 KEY_QUERY_VALUE
, NULL
, &wine_ports_key
, NULL
);
1921 RegCreateKeyExW( HKEY_LOCAL_MACHINE
, windows_ports_key_name
, 0, NULL
, REG_OPTION_VOLATILE
,
1922 KEY_ALL_ACCESS
, NULL
, &windows_ports_key
, NULL
);
1924 /* add user-defined serial ports */
1925 memset(used
, 0, sizeof(used
));
1928 port_len
= ARRAY_SIZE(port
);
1929 size
= sizeof(reg_value
);
1930 if (RegEnumValueW( wine_ports_key
, i
, port
, &port_len
, NULL
,
1931 &type
, (BYTE
*)reg_value
, &size
) != ERROR_SUCCESS
)
1933 if (type
!= REG_SZ
|| wcsnicmp( port
, port_prefix
, 3 ))
1936 n
= wcstol( port
+ 3, NULL
, 10 );
1937 if (n
< 1 || n
>= MAX_PORTS
)
1940 if (!WideCharToMultiByte( CP_UNIXCP
, WC_ERR_INVALID_CHARS
, reg_value
, size
/sizeof(WCHAR
),
1941 unix_path
, sizeof(unix_path
), NULL
, NULL
))
1945 sprintf( dosdev
, dosdev_fmt
, n
);
1946 create_port_device( driver
, n
, unix_path
, dosdev
, windows_ports_key
);
1949 /* look for ports in the usual places */
1951 for (n
= 1; *devices
; n
++, devices
+= strlen(devices
) + 1)
1953 while (n
<= MAX_PORTS
&& used
[n
- 1]) n
++;
1954 if (n
> MAX_PORTS
) break;
1955 sprintf( dosdev
, dosdev_fmt
, n
);
1956 create_port_device( driver
, n
, devices
, dosdev
, windows_ports_key
);
1959 RegCloseKey( wine_ports_key
);
1960 RegCloseKey( windows_ports_key
);
1963 /* driver entry point for the serial port driver */
1964 NTSTATUS WINAPI
serial_driver_entry( DRIVER_OBJECT
*driver
, UNICODE_STRING
*path
)
1967 struct detect_ports_params params
= { devices
, sizeof(devices
) };
1969 serial_driver
= driver
;
1970 /* TODO: fill in driver->MajorFunction */
1972 MOUNTMGR_CALL( detect_serial_ports
, ¶ms
);
1973 create_port_devices( driver
, devices
);
1975 return STATUS_SUCCESS
;
1978 /* driver entry point for the parallel port driver */
1979 NTSTATUS WINAPI
parallel_driver_entry( DRIVER_OBJECT
*driver
, UNICODE_STRING
*path
)
1982 struct detect_ports_params params
= { devices
, sizeof(devices
) };
1984 parallel_driver
= driver
;
1985 /* TODO: fill in driver->MajorFunction */
1987 MOUNTMGR_CALL( detect_parallel_ports
, ¶ms
);
1988 create_port_devices( driver
, devices
);
1990 return STATUS_SUCCESS
;