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
34 #include "wine/list.h"
35 #include "wine/debug.h"
37 WINE_DEFAULT_DEBUG_CHANNEL(mountmgr
);
39 #define MAX_DOS_DRIVES 26
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 */
56 FS_ERROR
, /* error accessing the device */
57 FS_UNKNOWN
, /* unknown file system */
61 FS_UDF
/* For reference [E] = Ecma-167.pdf, [U] = udf260.pdf */
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 */
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 */
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
)
118 guid
.Data4
[7] = 'A' + letter
;
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
, ¶ms
)) return;
159 while (p
> buffer
&& (p
[-1] == ' ' || p
[-1] == '\r' || p
[-1] == '\n')) p
--;
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
)
169 ULONG size
= sizeof(buffer
);
170 struct read_volume_file_params params
= { volume
->device
->unix_mount
, ".windows-serial", buffer
, &size
};
173 if (!volume
->device
->unix_mount
) return;
174 if (MOUNTMGR_CALL( read_volume_file
, ¶ms
)) return;
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;
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 */
203 if (cur_vd_type
> max_vd_type
)
205 max_vd_type
= cur_vd_type
;
206 best_offs
= offs
+ extra_offs
;
213 /***********************************************************************
214 * VOLUME_ReadFATSuperblock
216 static enum fs_type
VOLUME_ReadFATSuperblock( HANDLE handle
, BYTE
*buff
)
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 */
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 */
237 unsigned int sectors
,
246 sect_per_fat
= GETWORD(buff
, 0x16);
247 if (!sect_per_fat
) sect_per_fat
= GETLONG(buff
, 0x24);
248 total_sectors
= GETWORD(buff
, 0x13);
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
&&
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
;
270 if ((buff
[0x26] == 0x28 || buff
[0x26] == 0x29) &&
271 !memcmp(buff
+0x36, "FAT", 3))
279 /***********************************************************************
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
)
295 /***********************************************************************
296 * VOLUME_ReadCDSuperblock
298 static enum fs_type
VOLUME_ReadCDSuperblock( HANDLE handle
, BYTE
*buff
)
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
))
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
))
324 /* check for the iso9660 identifier */
325 if (!memcmp(&buff
[1], "CD001", 5)) return FS_ISO9660
;
330 /**************************************************************************
332 * Find the Primary Volume Descriptor
334 static BOOL
UDF_Find_PVD( HANDLE handle
, BYTE pvd
[] )
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
))
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
))
358 /* Check for the Primary Volume Descriptor Tag Id -- [E] 3/10.1.1 */
359 if (pvd
[0]!=1 || pvd
[1]!=0)
362 /* 8 or 16 bits per character -- [U] 2.1.1 */
363 if (!(pvd
[24]==8 || pvd
[24]==16))
374 /**************************************************************************
375 * VOLUME_GetSuperblockLabel
377 static void VOLUME_GetSuperblockLabel( struct volume
*volume
, HANDLE handle
, const BYTE
*superblock
)
379 const BYTE
*label_ptr
= NULL
;
381 BYTE pvd
[BLOCK_SIZE
];
383 switch (volume
->fs_type
)
389 get_filesystem_label( volume
);
392 label_ptr
= superblock
+ 0x2b;
396 label_ptr
= superblock
+ 0x47;
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 */
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;
414 label_ptr
= superblock
+ 40;
419 if(!UDF_Find_PVD(handle
, pvd
))
425 /* [E] 3/10.1.4 and [U] 2.1.1 */
428 label_ptr
= pvd
+ 24 + 1;
429 label_len
= pvd
[24+32-1];
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;
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
))
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
))
474 /* Partition Descriptor Tag Id -- [E] 3/10.5.1 */
475 if(block
[0]==5 && block
[1]==0)
478 /* Terminating Descriptor Tag Id -- [E] 3/10.9.1 */
479 if(block
[0]==8 && block
[1]==0)
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
))
500 /* File Set Descriptor Tag Id -- [E] 3/14.1.1 */
501 if(block
[0]==0 && block
[1]==1)
506 WARN("FSD sector not found, serial may be incorrect\n");
511 /**************************************************************************
512 * VOLUME_GetSuperblockSerial
514 static void VOLUME_GetSuperblockSerial( struct volume
*volume
, HANDLE handle
, const BYTE
*superblock
)
517 BYTE block
[BLOCK_SIZE
];
519 switch (volume
->fs_type
)
524 get_filesystem_serial( volume
);
527 volume
->serial
= GETLONG( superblock
, 0x27 );
530 volume
->serial
= GETLONG( superblock
, 0x43 );
533 FSD_sector
= UDF_Find_FSD_Sector(handle
, block
);
534 if (!VOLUME_ReadCDBlock(handle
, block
, FSD_sector
*BLOCK_SIZE
))
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];
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
)
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
582 * There it is collected for correcting the serial when there are less than
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
;
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
)
600 const WCHAR
*format
= NULL
;
601 const WCHAR
*link_format
= NULL
;
603 DEVICE_OBJECT
*dev_obj
;
604 struct disk_device
*device
;
609 case DEVICE_HARDDISK
:
610 case DEVICE_NETWORK
: /* FIXME */
611 format
= L
"\\Device\\Harddisk%u";
612 link_format
= L
"\\??\\PhysicalDrive%u";
614 case DEVICE_HARDDISK_VOL
:
615 format
= L
"\\Device\\HarddiskVolume%u";
616 first
= 1; /* harddisk volumes start counting from 1 */
619 format
= L
"\\Device\\Floppy%u";
623 format
= L
"\\Device\\CdRom%u";
624 link_format
= L
"\\??\\CdRom%u";
627 format
= L
"\\Device\\Ramdisk%u";
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;
642 device
= dev_obj
->DeviceExtension
;
643 device
->dev_obj
= dev_obj
;
646 device
->unix_device
= NULL
;
647 device
->unix_mount
= NULL
;
648 device
->symlink
.Buffer
= NULL
;
649 device
->volume
= volume
;
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
;
668 device
->devnum
.DeviceType
= FILE_DEVICE_DISK
;
669 device
->devnum
.DeviceNumber
= i
;
670 device
->devnum
.PartitionNumber
= ~0u;
673 device
->devnum
.DeviceType
= FILE_DEVICE_CD_ROM
;
674 device
->devnum
.DeviceNumber
= i
;
675 device
->devnum
.PartitionNumber
= ~0u;
678 device
->devnum
.DeviceType
= FILE_DEVICE_DVD
;
679 device
->devnum
.DeviceNumber
= i
;
680 device
->devnum
.PartitionNumber
= ~0u;
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;
689 case DEVICE_HARDDISK_VOL
:
690 device
->devnum
.DeviceType
= FILE_DEVICE_DISK
;
691 device
->devnum
.DeviceNumber
= 0;
692 device
->devnum
.PartitionNumber
= i
;
695 *device_ret
= device
;
696 TRACE( "created device %s\n", debugstr_w(name
.Buffer
) );
700 FIXME( "IoCreateDevice %s got %lx\n", debugstr_w(name
.Buffer
), status
);
701 RtlFreeUnicodeString( &name
);
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
)
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
;
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
);
746 /* set the volume udi */
747 static void set_volume_udi( struct volume
*volume
, const char *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
)
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
;
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
);
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
;
790 if (!(drive
= malloc( sizeof(*drive
) ))) return STATUS_NO_MEMORY
;
791 drive
->drive
= letter
;
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
);
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
);
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
)
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;
844 if (mount_point
&& disk_device
->unix_mount
)
846 if (strcmp( mount_point
, disk_device
->unix_mount
)) continue;
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
);
857 static BOOL
get_volume_device_info( struct volume
*volume
)
859 const char *unix_device
= volume
->device
->unix_device
;
864 BYTE superblock
[SUPERBLOCK_SIZE
];
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());
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());
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
);
896 volume
->fs_type
= VOLUME_ReadCDSuperblock( handle
, superblock
);
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
);
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
);
919 VOLUME_GetSuperblockLabel( volume
, handle
, superblock
);
920 VOLUME_GetSuperblockSerial( volume
, handle
, superblock
);
922 CloseHandle( handle
);
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
, ¶ms
))
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
)
955 unsigned int id_len
= 0;
956 struct disk_device
*disk_device
= volume
->device
;
959 if (type
!= disk_device
->type
)
961 if ((status
= create_disk_device( type
, &disk_device
, volume
))) return status
;
964 delete_mount_point( volume
->mount
);
965 volume
->mount
= NULL
;
967 if (drive
&& drive
->mount
)
969 delete_mount_point( drive
->mount
);
972 delete_disk_device( volume
->device
);
973 volume
->device
= disk_device
;
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
;
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
;
1006 delete_mount_point( volume
->mount
);
1007 volume
->mount
= NULL
;
1011 if (!volume
->serial
)
1012 memcpy(&volume
->serial
, &volume
->guid
.Data4
[4], sizeof(DWORD
));
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
;
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
;
1070 if (MOUNTMGR_CALL( get_dosdev_symlink
, ¶ms
)) continue;
1072 params
.dest
= unix_dev
;
1073 params
.size
= sizeof(unix_dev
);
1074 if (!MOUNTMGR_CALL( get_dosdev_symlink
, ¶ms
)) device
= unix_dev
;
1076 drive_type
= i
< 2 ? DEVICE_FLOPPY
: DEVICE_HARDDISK_VOL
;
1080 DWORD j
, type
, size
= sizeof(buffer
);
1082 driveW
[0] = 'a' + i
;
1083 if (!RegQueryValueExW( drives_key
, driveW
, NULL
, &type
, (BYTE
*)buffer
, &size
) &&
1086 for (j
= 0; j
< ARRAY_SIZE(drive_types
); j
++)
1087 if (drive_types
[j
][0] && !wcsicmp( buffer
, drive_types
[j
] ))
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;
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
);
1132 RegSetValueExW( port_key
, L
"FirstBusTimeScanInMs", 0, REG_DWORD
, (const BYTE
*)&value
, sizeof(value
));
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
);
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
);
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
);
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
);
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
);
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
;
1243 LeaveCriticalSection( &device_section
);
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
)
1254 NTSTATUS status
= STATUS_SUCCESS
;
1255 struct dos_drive
*drive
, *next
;
1256 struct volume
*volume
;
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
, ¶ms
))) 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
, ¶ms
);
1288 if (!device
|| !drive
->volume
->device
->unix_device
||
1289 strcmp( device
, drive
->volume
->device
->unix_device
))
1290 MOUNTMGR_CALL( set_dosdev_symlink
, ¶ms
);
1291 delete_dos_device( drive
);
1295 if ((status
= create_dos_device( volume
, udi
, letter
, type
, &drive
))) goto done
;
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
;
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
, ¶ms
);
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
];
1323 RegSetValueExW( hkey
, name
, 0, REG_SZ
, (const BYTE
*)type_name
,
1324 (lstrlenW(type_name
) + 1) * sizeof(WCHAR
) );
1326 RegDeleteValueW( hkey
, name
);
1327 RegCloseKey( hkey
);
1330 if (udi
) notify
= drive
->drive
;
1331 if (scsi_info
) create_scsi_entry( volume
, scsi_info
);
1334 if (volume
) release_volume( volume
);
1335 LeaveCriticalSection( &device_section
);
1336 if (notify
!= -1) send_notify( notify
, DBT_DEVICEARRIVAL
);
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
;
1345 struct dos_drive
*drive
;
1346 char dosdev
[] = "a:";
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
};
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
, ¶ms
);
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
;
1380 LeaveCriticalSection( &device_section
);
1381 if (notify
!= -1) send_notify( notify
, DBT_DEVICEREMOVECOMPLETE
);
1385 static enum mountmgr_fs_type
get_mountmgr_fs_type(enum fs_type 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
),
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
, ¶ms
))
1425 TRACE( "found matching volume %s\n", debugstr_guid(&volume
->guid
) );
1426 return grab_volume( volume
);
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
;
1446 if (letter
&& (letter
< 'a' || letter
> 'z')) return STATUS_INVALID_PARAMETER
;
1448 EnterCriticalSection( &device_section
);
1450 volume
= find_volume_by_letter( letter
- 'a' );
1452 volume
= find_volume_by_unixdev( input
->unix_dev
);
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
);
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;
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
;
1524 free( mount_point
);
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
;
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
;
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;
1586 descriptor
->SerialNumberOffset
= sizeof(*descriptor
);
1587 strcpy( (char *)descriptor
+ descriptor
->SerialNumberOffset
, device
->serial
);
1589 irp
->IoStatus
.Information
= len
;
1590 status
= STATUS_SUCCESS
;
1596 FIXME( "Unsupported property %#x\n", query
->PropertyId
);
1597 status
= STATUS_NOT_SUPPORTED
;
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
;
1613 TRACE( "volume query %x length %lu\n", info_class
, length
);
1615 EnterCriticalSection( &device_section
);
1616 volume
= dev
->volume
;
1619 status
= STATUS_BAD_DEVICE_TYPE
;
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
;
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
;
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
;
1659 if ((status
= MOUNTMGR_CALL( get_volume_size_info
, ¶ms
)) == 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
;
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
;
1685 case MOUNTMGR_FS_TYPE_ISO9660
:
1687 info
->FileSystemAttributes
= FILE_READ_ONLY_VOLUME
;
1688 info
->MaximumComponentNameLength
= 221;
1690 case MOUNTMGR_FS_TYPE_UDF
:
1692 info
->FileSystemAttributes
= FILE_READ_ONLY_VOLUME
| FILE_UNICODE_ON_DISK
| FILE_CASE_SENSITIVE_SEARCH
;
1693 info
->MaximumComponentNameLength
= 255;
1695 case MOUNTMGR_FS_TYPE_FAT
:
1697 info
->FileSystemAttributes
= FILE_CASE_PRESERVED_NAMES
; /* FIXME */
1698 info
->MaximumComponentNameLength
= 255;
1700 case MOUNTMGR_FS_TYPE_FAT32
:
1702 info
->FileSystemAttributes
= FILE_CASE_PRESERVED_NAMES
; /* FIXME */
1703 info
->MaximumComponentNameLength
= 255;
1707 info
->FileSystemAttributes
= FILE_CASE_PRESERVED_NAMES
| FILE_PERSISTENT_ACLS
;
1708 info
->MaximumComponentNameLength
= 255;
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
;
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
;
1729 if ((status
= MOUNTMGR_CALL( get_volume_size_info
, ¶ms
)) == 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
;
1744 FIXME("Unsupported volume query %x\n", irpsp
->Parameters
.QueryVolume
.FsInformationClass
);
1745 status
= STATUS_NOT_SUPPORTED
;
1750 io
->Status
= status
;
1751 LeaveCriticalSection( &device_section
);
1752 IoCompleteRequest( irp
, IO_NO_INCREMENT
);
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
;
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
:
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
;
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
;
1802 memcpy( irp
->AssociatedIrp
.SystemBuffer
, &info
, len
);
1803 irp
->IoStatus
.Information
= len
;
1804 status
= STATUS_SUCCESS
;
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
;
1816 case IOCTL_CDROM_READ_TOC
:
1817 status
= STATUS_INVALID_DEVICE_REQUEST
;
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
;
1829 case IOCTL_STORAGE_QUERY_PROPERTY
:
1830 status
= query_property( dev
, irp
);
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
;
1842 irp
->IoStatus
.Status
= status
;
1843 LeaveCriticalSection( &device_section
);
1844 IoCompleteRequest( irp
, IO_NO_INCREMENT
);
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
;
1875 struct set_dosdev_symlink_params params
= { dosdevices_path
, unix_path
};
1877 /* create DOS device */
1878 if (MOUNTMGR_CALL( set_dosdev_symlink
, ¶ms
)) 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";
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
);
1908 swprintf( symlink_buffer
, ARRAY_SIZE(symlink_buffer
), symlink_format
, n
);
1909 RtlInitUnicodeString( &symlink_name
, symlink_buffer
);
1910 IoCreateSymbolicLink( &symlink_name
, &nt_name
);
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
) );
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
;
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
];
1939 DWORD port_len
, type
, size
;
1942 if (driver
== serial_driver
)
1944 dosdev_fmt
= "com%u";
1945 windows_ports_key_name
= L
"HARDWARE\\DEVICEMAP\\SERIALCOMM";
1946 port_prefix
= L
"COM";
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
));
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
)
1971 if (type
!= REG_SZ
|| wcsnicmp( port
, port_prefix
, 3 ))
1974 n
= wcstol( port
+ 3, NULL
, 10 );
1975 if (n
< 1 || n
>= MAX_PORTS
)
1978 if (!WideCharToMultiByte( CP_UNIXCP
, WC_ERR_INVALID_CHARS
, reg_value
, size
/sizeof(WCHAR
),
1979 unix_path
, sizeof(unix_path
), NULL
, NULL
))
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
)
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
, ¶ms
);
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
)
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
, ¶ms
);
2026 create_port_devices( driver
, devices
);
2028 return STATUS_SUCCESS
;