vbscript: Handle index read access to array properties.
[wine.git] / dlls / mountmgr.sys / device.c
blob328b0b2f344194dcdcd6c4046cc881032711e1ea
1 /*
2 * Dynamic devices support
4 * Copyright 2006 Alexandre Julliard
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <assert.h>
22 #include <errno.h>
23 #include <stdarg.h>
24 #include <stdio.h>
25 #include <stdlib.h>
27 #define NONAMELESSUNION
29 #include "mountmgr.h"
30 #include "winreg.h"
31 #include "winnls.h"
32 #include "winuser.h"
33 #include "dbt.h"
34 #include "unixlib.h"
36 #include "wine/list.h"
37 #include "wine/debug.h"
39 WINE_DEFAULT_DEBUG_CHANNEL(mountmgr);
41 #define MAX_DOS_DRIVES 26
42 #define MAX_PORTS 256
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 */
56 enum fs_type
58 FS_ERROR, /* error accessing the device */
59 FS_UNKNOWN, /* unknown file system */
60 FS_FAT1216,
61 FS_FAT32,
62 FS_ISO9660,
63 FS_UDF /* For reference [E] = Ecma-167.pdf, [U] = udf260.pdf */
66 struct disk_device
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 */
79 struct 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 */
92 struct dos_drive
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 )
118 char *ret;
120 if (!str) return NULL;
121 if ((ret = RtlAllocateHeap( GetProcessHeap(), 0, strlen(str) + 1 ))) strcpy( ret, str );
122 return ret;
125 static WCHAR *strdupW( const WCHAR *str )
127 WCHAR *ret;
129 if (!str) return NULL;
130 if ((ret = RtlAllocateHeap( GetProcessHeap(), 0, (lstrlenW(str) + 1) * sizeof(WCHAR) ))) lstrcpyW( ret, str );
131 return ret;
134 static const GUID *get_default_uuid( int letter )
136 static GUID guid;
138 guid.Data4[7] = 'A' + letter;
139 return &guid;
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, &params )) return;
178 p = buffer + size;
179 while (p > buffer && (p[-1] == ' ' || p[-1] == '\r' || p[-1] == '\n')) p--;
180 *p = 0;
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 )
188 char buffer[32];
189 ULONG size = sizeof(buffer);
190 struct read_volume_file_params params = { volume->device->unix_mount, ".windows-serial", buffer, &size };
192 volume->serial = 0;
193 if (!volume->device->unix_mount) return;
194 if (MOUNTMGR_CALL( read_volume_file, &params )) return;
196 buffer[size] = 0;
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;
207 BYTE buffer[0x800];
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 */
222 break;
223 if (cur_vd_type > max_vd_type)
225 max_vd_type = cur_vd_type;
226 best_offs = offs + extra_offs;
229 return best_offs;
233 /***********************************************************************
234 * VOLUME_ReadFATSuperblock
236 static enum fs_type VOLUME_ReadFATSuperblock( HANDLE handle, BYTE *buff )
238 DWORD size;
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 */
245 return FS_ERROR;
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 */
256 int reasonable;
257 unsigned int sectors,
258 sect_per_fat,
259 total_sectors,
260 num_boot_sectors,
261 num_fats,
262 num_root_dir_ents,
263 bytes_per_sector,
264 sectors_per_cluster,
265 nclust;
266 sect_per_fat = GETWORD(buff, 0x16);
267 if (!sect_per_fat) sect_per_fat = GETLONG(buff, 0x24);
268 total_sectors = GETWORD(buff, 0x13);
269 if (!total_sectors)
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 &&
279 num_fats < 16 &&
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;
288 if (nclust < 65525)
290 if ((buff[0x26] == 0x28 || buff[0x26] == 0x29) &&
291 !memcmp(buff+0x36, "FAT", 3))
292 return FS_FAT1216;
295 return FS_UNKNOWN;
299 /***********************************************************************
300 * VOLUME_ReadCDBlock
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)
309 return FALSE;
311 return TRUE;
315 /***********************************************************************
316 * VOLUME_ReadCDSuperblock
318 static enum fs_type VOLUME_ReadCDSuperblock( HANDLE handle, BYTE *buff )
320 int i;
321 DWORD offs;
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))
331 continue;
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))
342 return FS_ERROR;
344 /* check for the iso9660 identifier */
345 if (!memcmp(&buff[1], "CD001", 5)) return FS_ISO9660;
346 return FS_UNKNOWN;
350 /**************************************************************************
351 * UDF_Find_PVD
352 * Find the Primary Volume Descriptor
354 static BOOL UDF_Find_PVD( HANDLE handle, BYTE pvd[] )
356 unsigned int i;
357 DWORD offset;
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))
363 return FALSE;
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))
376 return FALSE;
378 /* Check for the Primary Volume Descriptor Tag Id -- [E] 3/10.1.1 */
379 if (pvd[0]!=1 || pvd[1]!=0)
380 return FALSE;
382 /* 8 or 16 bits per character -- [U] 2.1.1 */
383 if (!(pvd[24]==8 || pvd[24]==16))
384 return FALSE;
386 return TRUE;
390 return FALSE;
394 /**************************************************************************
395 * VOLUME_GetSuperblockLabel
397 static void VOLUME_GetSuperblockLabel( struct volume *volume, HANDLE handle, const BYTE *superblock )
399 const BYTE *label_ptr = NULL;
400 DWORD label_len;
401 BYTE pvd[BLOCK_SIZE];
403 switch (volume->fs_type)
405 case FS_ERROR:
406 label_len = 0;
407 break;
408 case FS_UNKNOWN:
409 get_filesystem_label( volume );
410 return;
411 case FS_FAT1216:
412 label_ptr = superblock + 0x2b;
413 label_len = 11;
414 break;
415 case FS_FAT32:
416 label_ptr = superblock + 0x47;
417 label_len = 11;
418 break;
419 case FS_ISO9660:
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 */
426 unsigned int i;
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;
432 return;
434 label_ptr = superblock + 40;
435 label_len = 32;
436 break;
438 case FS_UDF:
439 if(!UDF_Find_PVD(handle, pvd))
441 label_len = 0;
442 break;
445 /* [E] 3/10.1.4 and [U] 2.1.1 */
446 if(pvd[24]==8)
448 label_ptr = pvd + 24 + 1;
449 label_len = pvd[24+32-1];
450 break;
452 else
454 unsigned int i;
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;
460 return;
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))
480 goto default_sector;
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))
492 goto default_sector;
494 /* Partition Descriptor Tag Id -- [E] 3/10.5.1 */
495 if(block[0]==5 && block[1]==0)
496 break;
498 /* Terminating Descriptor Tag Id -- [E] 3/10.9.1 */
499 if(block[0]==8 && block[1]==0)
500 goto default_sector;
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))
518 goto default_sector;
520 /* File Set Descriptor Tag Id -- [E] 3/14.1.1 */
521 if(block[0]==0 && block[1]==1)
522 return i;
525 default_sector:
526 WARN("FSD sector not found, serial may be incorrect\n");
527 return 257;
531 /**************************************************************************
532 * VOLUME_GetSuperblockSerial
534 static void VOLUME_GetSuperblockSerial( struct volume *volume, HANDLE handle, const BYTE *superblock )
536 int FSD_sector;
537 BYTE block[BLOCK_SIZE];
539 switch (volume->fs_type)
541 case FS_ERROR:
542 break;
543 case FS_UNKNOWN:
544 get_filesystem_serial( volume );
545 break;
546 case FS_FAT1216:
547 volume->serial = GETLONG( superblock, 0x27 );
548 break;
549 case FS_FAT32:
550 volume->serial = GETLONG( superblock, 0x43 );
551 break;
552 case FS_UDF:
553 FSD_sector = UDF_Find_FSD_Sector(handle, block);
554 if (!VOLUME_ReadCDBlock(handle, block, FSD_sector*BLOCK_SIZE))
555 break;
556 superblock = block;
557 /* fallthrough */
558 case FS_ISO9660:
560 BYTE sum[4];
561 int i;
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];
579 else
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 )
591 DWORD serial = 0;
592 int i;
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
601 * frames.
602 * There it is collected for correcting the serial when there are less than
603 * 3 tracks.
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;
611 return serial;
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 )
618 UINT i, first = 0;
619 NTSTATUS status = 0;
620 const WCHAR *format = NULL;
621 const WCHAR *link_format = NULL;
622 UNICODE_STRING name;
623 DEVICE_OBJECT *dev_obj;
624 struct disk_device *device;
626 switch(type)
628 case DEVICE_UNKNOWN:
629 case DEVICE_HARDDISK:
630 case DEVICE_NETWORK: /* FIXME */
631 format = L"\\Device\\Harddisk%u";
632 link_format = L"\\??\\PhysicalDrive%u";
633 break;
634 case DEVICE_HARDDISK_VOL:
635 format = L"\\Device\\HarddiskVolume%u";
636 first = 1; /* harddisk volumes start counting from 1 */
637 break;
638 case DEVICE_FLOPPY:
639 format = L"\\Device\\Floppy%u";
640 break;
641 case DEVICE_CDROM:
642 case DEVICE_DVD:
643 format = L"\\Device\\CdRom%u";
644 link_format = L"\\??\\CdRom%u";
645 break;
646 case DEVICE_RAMDISK:
647 format = L"\\Device\\Ramdisk%u";
648 break;
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;
660 if (!status)
662 device = dev_obj->DeviceExtension;
663 device->dev_obj = dev_obj;
664 device->name = name;
665 device->type = type;
666 device->unix_device = NULL;
667 device->unix_mount = NULL;
668 device->symlink.Buffer = NULL;
669 device->volume = volume;
671 if (link_format)
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;
684 switch (type)
686 case DEVICE_FLOPPY:
687 case DEVICE_RAMDISK:
688 device->devnum.DeviceType = FILE_DEVICE_DISK;
689 device->devnum.DeviceNumber = i;
690 device->devnum.PartitionNumber = ~0u;
691 break;
692 case DEVICE_CDROM:
693 device->devnum.DeviceType = FILE_DEVICE_CD_ROM;
694 device->devnum.DeviceNumber = i;
695 device->devnum.PartitionNumber = ~0u;
696 break;
697 case DEVICE_DVD:
698 device->devnum.DeviceType = FILE_DEVICE_DVD;
699 device->devnum.DeviceNumber = i;
700 device->devnum.PartitionNumber = ~0u;
701 break;
702 case DEVICE_UNKNOWN:
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;
708 break;
709 case DEVICE_HARDDISK_VOL:
710 device->devnum.DeviceType = FILE_DEVICE_DISK;
711 device->devnum.DeviceNumber = 0;
712 device->devnum.PartitionNumber = i;
713 break;
715 *device_ret = device;
716 TRACE( "created device %s\n", debugstr_w(name.Buffer) );
718 else
720 FIXME( "IoCreateDevice %s got %lx\n", debugstr_w(name.Buffer), status );
721 RtlFreeUnicodeString( &name );
723 return status;
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 )
745 volume->ref++;
746 return 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;
754 if (!ret)
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 );
763 return ret;
766 /* set the volume udi */
767 static void set_volume_udi( struct volume *volume, const char *udi )
769 if (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 );
778 volume->udi = NULL;
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;
787 NTSTATUS status;
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 );
800 return status;
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;
808 NTSTATUS status;
810 if (!(drive = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*drive) ))) return STATUS_NO_MEMORY;
811 drive->drive = letter;
812 drive->mount = NULL;
814 if (volume)
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 );
825 *drive_ret = drive;
827 else RtlFreeHeap( GetProcessHeap(), 0, drive );
829 return status;
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 )
850 int match = 0;
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;
862 match++;
864 if (mount_point && disk_device->unix_mount)
866 if (strcmp( mount_point, disk_device->unix_mount )) continue;
867 match++;
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 );
874 return NULL;
877 static BOOL get_volume_device_info( struct volume *volume )
879 const char *unix_device = volume->device->unix_device;
880 WCHAR *name;
881 HANDLE handle;
882 CDROM_TOC toc;
883 DWORD size;
884 BYTE superblock[SUPERBLOCK_SIZE];
886 if (!unix_device)
887 return FALSE;
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 %lu\n", debugstr_a(unix_device), GetLastError());
894 return FALSE;
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 %lu\n", debugstr_a(unix_device), GetLastError());
902 return FALSE;
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 );
914 return TRUE;
916 volume->fs_type = VOLUME_ReadCDSuperblock( handle, superblock );
918 else
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 );
928 return FALSE;
931 VOLUME_GetSuperblockLabel( volume, handle, superblock );
932 VOLUME_GetSuperblockSerial( volume, handle, superblock );
934 CloseHandle( handle );
935 return TRUE;
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, &params ))
946 return;
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 )
966 void *id = NULL;
967 unsigned int id_len = 0;
968 struct disk_device *disk_device = volume->device;
969 NTSTATUS status;
971 if (type != disk_device->type)
973 if ((status = create_disk_device( type, &disk_device, volume ))) return status;
974 if (volume->mount)
976 delete_mount_point( volume->mount );
977 volume->mount = NULL;
979 if (drive && drive->mount)
981 delete_mount_point( drive->mount );
982 drive->mount = NULL;
984 delete_disk_device( volume->device );
985 volume->device = disk_device;
987 else
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;
1004 else
1005 volume->fs_type = FS_UNKNOWN;
1007 get_filesystem_label( volume );
1008 get_filesystem_serial( volume );
1011 TRACE("fs_type %#x, label %s, serial %08lx\n", volume->fs_type, debugstr_w(volume->label), volume->serial);
1013 if (guid && memcmp( &volume->guid, guid, sizeof(volume->guid) ))
1015 volume->guid = *guid;
1016 if (volume->mount)
1018 delete_mount_point( volume->mount );
1019 volume->mount = NULL;
1023 if (!volume->serial)
1024 memcpy(&volume->serial, &volume->guid.Data4[4], sizeof(DWORD));
1026 if (!volume->mount)
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;
1067 unsigned int i;
1068 HKEY drives_key;
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;
1081 dosdev[2] = 0;
1082 if (MOUNTMGR_CALL( get_dosdev_symlink, &params )) continue;
1083 dosdev[2] = ':';
1084 params.dest = unix_dev;
1085 params.size = sizeof(unix_dev);
1086 if (!MOUNTMGR_CALL( get_dosdev_symlink, &params )) device = unix_dev;
1088 drive_type = i < 2 ? DEVICE_FLOPPY : DEVICE_HARDDISK_VOL;
1089 if (drives_key)
1091 WCHAR buffer[32];
1092 DWORD j, type, size = sizeof(buffer);
1094 driveW[0] = 'a' + i;
1095 if (!RegQueryValueExW( drives_key, driveW, NULL, &type, (BYTE *)buffer, &size ) &&
1096 type == REG_SZ)
1098 for (j = 0; j < ARRAY_SIZE(drive_types); j++)
1099 if (drive_types[j][0] && !wcsicmp( buffer, drive_types[j] ))
1101 drive_type = j;
1102 break;
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;
1125 WCHAR dataW[50];
1126 DWORD sizeW;
1127 DWORD value;
1128 const char *data;
1129 HKEY scsi_key;
1130 HKEY port_key;
1131 HKEY bus_key;
1132 HKEY target_key;
1133 HKEY lun_key;
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 );
1143 value = 10;
1144 RegSetValueExW( port_key, L"FirstBusTimeScanInMs", 0, REG_DWORD, (const BYTE *)&value, sizeof(value));
1146 value = 0;
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 );
1164 switch (info->type)
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 );
1194 if (volume)
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 );
1226 goto found;
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 );
1233 found:
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 );
1238 return status;
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;
1253 break;
1255 LeaveCriticalSection( &device_section );
1256 return status;
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 )
1265 HKEY hkey;
1266 NTSTATUS status = STATUS_SUCCESS;
1267 struct dos_drive *drive, *next;
1268 struct volume *volume;
1269 int notify = -1;
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, &params ))) 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, &params );
1298 else
1300 if (!device || !drive->volume->device->unix_device ||
1301 strcmp( device, drive->volume->device->unix_device ))
1302 MOUNTMGR_CALL( set_dosdev_symlink, &params );
1303 delete_dos_device( drive );
1307 if ((status = create_dos_device( volume, udi, letter, type, &drive ))) goto done;
1309 found:
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;
1314 dosdev[2] = 0;
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, &params );
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];
1334 if (type_name[0])
1335 RegSetValueExW( hkey, name, 0, REG_SZ, (const BYTE *)type_name,
1336 (lstrlenW(type_name) + 1) * sizeof(WCHAR) );
1337 else
1338 RegDeleteValueW( hkey, name );
1339 RegCloseKey( hkey );
1342 if (udi) notify = drive->drive;
1343 if (scsi_info) create_scsi_entry( volume, scsi_info );
1345 done:
1346 if (volume) release_volume( volume );
1347 LeaveCriticalSection( &device_section );
1348 if (notify != -1) send_notify( notify, DBT_DEVICEARRIVAL );
1349 return status;
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;
1356 HKEY hkey;
1357 struct dos_drive *drive;
1358 char dosdev[] = "a:";
1359 int notify = -1;
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 };
1366 if (udi)
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, &params );
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;
1390 break;
1392 LeaveCriticalSection( &device_section );
1393 if (notify != -1) send_notify( notify, DBT_DEVICEREMOVECOMPLETE );
1394 return status;
1397 static enum mountmgr_fs_type get_mountmgr_fs_type(enum fs_type fs_type)
1399 switch (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),
1420 'a' + letter );
1421 break;
1423 return volume;
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, &params ))
1435 continue;
1437 TRACE( "found matching volume %s\n", debugstr_guid(&volume->guid) );
1438 return grab_volume( volume );
1440 return NULL;
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;
1455 char *ptr;
1456 WCHAR *label;
1458 if (letter && (letter < 'a' || letter > 'z')) return STATUS_INVALID_PARAMETER;
1460 EnterCriticalSection( &device_section );
1461 if (letter)
1462 volume = find_volume_by_letter( letter - 'a' );
1463 else
1464 volume = find_volume_by_unixdev( input->unix_dev );
1465 if (volume)
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 );
1477 if (!volume)
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;
1497 input = NULL;
1498 output = buff;
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 %lu\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 );
1538 return status;
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;
1545 NTSTATUS status;
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;
1578 else
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;
1596 else
1598 descriptor->SerialNumberOffset = sizeof(*descriptor);
1599 strcpy( (char *)descriptor + descriptor->SerialNumberOffset, device->serial );
1601 irp->IoStatus.Information = len;
1602 status = STATUS_SUCCESS;
1605 break;
1607 default:
1608 FIXME( "Unsupported property %#x\n", query->PropertyId );
1609 status = STATUS_NOT_SUPPORTED;
1610 break;
1612 return status;
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;
1623 NTSTATUS status;
1625 TRACE( "volume query %x length %lu\n", info_class, length );
1627 EnterCriticalSection( &device_section );
1628 volume = dev->volume;
1629 if (!volume)
1631 status = STATUS_BAD_DEVICE_TYPE;
1632 goto done;
1635 switch(info_class)
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;
1645 break;
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;
1657 break;
1659 case FileFsSizeInformation:
1661 FILE_FS_SIZE_INFORMATION *info = irp->AssociatedIrp.SystemBuffer;
1662 struct size_info size_info = { 0, 0, 0, 0, 0 };
1663 struct get_volume_size_info_params params = { dev->unix_mount, &size_info };
1665 if (length < sizeof(FILE_FS_SIZE_INFORMATION))
1667 status = STATUS_BUFFER_TOO_SMALL;
1668 break;
1671 if ((status = MOUNTMGR_CALL( get_volume_size_info, &params )) == STATUS_SUCCESS)
1673 info->TotalAllocationUnits.QuadPart = size_info.total_allocation_units;
1674 info->AvailableAllocationUnits.QuadPart = size_info.caller_available_allocation_units;
1675 info->SectorsPerAllocationUnit = size_info.sectors_per_allocation_unit;
1676 info->BytesPerSector = size_info.bytes_per_sector;
1677 io->Information = sizeof(*info);
1678 status = STATUS_SUCCESS;
1681 break;
1683 case FileFsAttributeInformation:
1685 FILE_FS_ATTRIBUTE_INFORMATION *info = irp->AssociatedIrp.SystemBuffer;
1686 enum mountmgr_fs_type fs_type = get_mountmgr_fs_type(volume->fs_type);
1687 const WCHAR *fsname;
1689 if (length < sizeof(FILE_FS_ATTRIBUTE_INFORMATION))
1691 status = STATUS_INFO_LENGTH_MISMATCH;
1692 break;
1695 switch (fs_type)
1697 case MOUNTMGR_FS_TYPE_ISO9660:
1698 fsname = L"CDFS";
1699 info->FileSystemAttributes = FILE_READ_ONLY_VOLUME;
1700 info->MaximumComponentNameLength = 221;
1701 break;
1702 case MOUNTMGR_FS_TYPE_UDF:
1703 fsname = L"UDF";
1704 info->FileSystemAttributes = FILE_READ_ONLY_VOLUME | FILE_UNICODE_ON_DISK | FILE_CASE_SENSITIVE_SEARCH;
1705 info->MaximumComponentNameLength = 255;
1706 break;
1707 case MOUNTMGR_FS_TYPE_FAT:
1708 fsname = L"FAT";
1709 info->FileSystemAttributes = FILE_CASE_PRESERVED_NAMES; /* FIXME */
1710 info->MaximumComponentNameLength = 255;
1711 break;
1712 case MOUNTMGR_FS_TYPE_FAT32:
1713 fsname = L"FAT32";
1714 info->FileSystemAttributes = FILE_CASE_PRESERVED_NAMES; /* FIXME */
1715 info->MaximumComponentNameLength = 255;
1716 break;
1717 default:
1718 fsname = L"NTFS";
1719 info->FileSystemAttributes = FILE_CASE_PRESERVED_NAMES | FILE_PERSISTENT_ACLS;
1720 info->MaximumComponentNameLength = 255;
1721 break;
1723 info->FileSystemNameLength = min( wcslen(fsname) * sizeof(WCHAR), length - offsetof( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName ) );
1724 memcpy(info->FileSystemName, fsname, info->FileSystemNameLength);
1725 io->Information = offsetof( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName ) + info->FileSystemNameLength;
1726 status = STATUS_SUCCESS;
1727 break;
1729 case FileFsFullSizeInformation:
1731 FILE_FS_FULL_SIZE_INFORMATION *info = irp->AssociatedIrp.SystemBuffer;
1732 struct size_info size_info = { 0, 0, 0, 0, 0 };
1733 struct get_volume_size_info_params params = { dev->unix_mount, &size_info };
1735 if (length < sizeof(FILE_FS_FULL_SIZE_INFORMATION))
1737 status = STATUS_BUFFER_TOO_SMALL;
1738 break;
1741 if ((status = MOUNTMGR_CALL( get_volume_size_info, &params )) == STATUS_SUCCESS)
1743 info->TotalAllocationUnits.QuadPart = size_info.total_allocation_units;
1744 info->CallerAvailableAllocationUnits.QuadPart = size_info.caller_available_allocation_units;
1745 info->ActualAvailableAllocationUnits.QuadPart = size_info.actual_available_allocation_units;
1746 info->SectorsPerAllocationUnit = size_info.sectors_per_allocation_unit;
1747 info->BytesPerSector = size_info.bytes_per_sector;
1748 io->Information = sizeof(*info);
1749 status = STATUS_SUCCESS;
1752 break;
1755 default:
1756 FIXME("Unsupported volume query %x\n", irpsp->Parameters.QueryVolume.FsInformationClass);
1757 status = STATUS_NOT_SUPPORTED;
1758 break;
1761 done:
1762 io->u.Status = status;
1763 LeaveCriticalSection( &device_section );
1764 IoCompleteRequest( irp, IO_NO_INCREMENT );
1765 return status;
1768 /* handler for ioctls on the harddisk device */
1769 static NTSTATUS WINAPI harddisk_ioctl( DEVICE_OBJECT *device, IRP *irp )
1771 IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp );
1772 struct disk_device *dev = device->DeviceExtension;
1773 NTSTATUS status;
1775 TRACE( "ioctl %lx insize %lu outsize %lu\n",
1776 irpsp->Parameters.DeviceIoControl.IoControlCode,
1777 irpsp->Parameters.DeviceIoControl.InputBufferLength,
1778 irpsp->Parameters.DeviceIoControl.OutputBufferLength );
1780 EnterCriticalSection( &device_section );
1782 switch(irpsp->Parameters.DeviceIoControl.IoControlCode)
1784 case IOCTL_DISK_GET_DRIVE_GEOMETRY:
1786 DISK_GEOMETRY info;
1787 DWORD len = min( sizeof(info), irpsp->Parameters.DeviceIoControl.OutputBufferLength );
1789 info.Cylinders.QuadPart = 10000;
1790 info.MediaType = (dev->devnum.DeviceType == FILE_DEVICE_DISK) ? FixedMedia : RemovableMedia;
1791 info.TracksPerCylinder = 255;
1792 info.SectorsPerTrack = 63;
1793 info.BytesPerSector = 512;
1794 memcpy( irp->AssociatedIrp.SystemBuffer, &info, len );
1795 irp->IoStatus.Information = len;
1796 status = STATUS_SUCCESS;
1797 break;
1799 case IOCTL_DISK_GET_DRIVE_GEOMETRY_EX:
1801 DISK_GEOMETRY_EX info;
1802 DWORD len = min( sizeof(info), irpsp->Parameters.DeviceIoControl.OutputBufferLength );
1804 FIXME("The DISK_PARTITION_INFO and DISK_DETECTION_INFO structures will not be filled\n");
1806 info.Geometry.Cylinders.QuadPart = 10000;
1807 info.Geometry.MediaType = (dev->devnum.DeviceType == FILE_DEVICE_DISK) ? FixedMedia : RemovableMedia;
1808 info.Geometry.TracksPerCylinder = 255;
1809 info.Geometry.SectorsPerTrack = 63;
1810 info.Geometry.BytesPerSector = 512;
1811 info.DiskSize.QuadPart = info.Geometry.Cylinders.QuadPart * info.Geometry.TracksPerCylinder *
1812 info.Geometry.SectorsPerTrack * info.Geometry.BytesPerSector;
1813 info.Data[0] = 0;
1814 memcpy( irp->AssociatedIrp.SystemBuffer, &info, len );
1815 irp->IoStatus.Information = len;
1816 status = STATUS_SUCCESS;
1817 break;
1819 case IOCTL_STORAGE_GET_DEVICE_NUMBER:
1821 DWORD len = min( sizeof(dev->devnum), irpsp->Parameters.DeviceIoControl.OutputBufferLength );
1823 memcpy( irp->AssociatedIrp.SystemBuffer, &dev->devnum, len );
1824 irp->IoStatus.Information = len;
1825 status = STATUS_SUCCESS;
1826 break;
1828 case IOCTL_CDROM_READ_TOC:
1829 status = STATUS_INVALID_DEVICE_REQUEST;
1830 break;
1831 case IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS:
1833 DWORD len = min( 32, irpsp->Parameters.DeviceIoControl.OutputBufferLength );
1835 FIXME( "returning zero-filled buffer for IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS\n" );
1836 memset( irp->AssociatedIrp.SystemBuffer, 0, len );
1837 irp->IoStatus.Information = len;
1838 status = STATUS_SUCCESS;
1839 break;
1841 case IOCTL_STORAGE_QUERY_PROPERTY:
1842 status = query_property( dev, irp );
1843 break;
1844 default:
1846 ULONG code = irpsp->Parameters.DeviceIoControl.IoControlCode;
1847 FIXME("Unsupported ioctl %lx (device=%lx access=%lx func=%lx method=%lx)\n",
1848 code, code >> 16, (code >> 14) & 3, (code >> 2) & 0xfff, code & 3);
1849 status = STATUS_NOT_SUPPORTED;
1850 break;
1854 irp->IoStatus.u.Status = status;
1855 LeaveCriticalSection( &device_section );
1856 IoCompleteRequest( irp, IO_NO_INCREMENT );
1857 return status;
1860 /* driver entry point for the harddisk driver */
1861 NTSTATUS WINAPI harddisk_driver_entry( DRIVER_OBJECT *driver, UNICODE_STRING *path )
1863 struct disk_device *device;
1865 harddisk_driver = driver;
1866 driver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = harddisk_ioctl;
1867 driver->MajorFunction[IRP_MJ_QUERY_VOLUME_INFORMATION] = harddisk_query_volume;
1869 /* create a harddisk0 device that isn't assigned to any drive */
1870 create_disk_device( DEVICE_HARDDISK, &device, NULL );
1872 create_drive_devices();
1874 return STATUS_SUCCESS;
1878 /* create a serial or parallel port */
1879 static BOOL create_port_device( DRIVER_OBJECT *driver, int n, const char *unix_path,
1880 const char *dosdevices_path, HKEY windows_ports_key )
1882 const WCHAR *dos_name_format, *nt_name_format, *reg_value_format, *symlink_format, *default_device;
1883 WCHAR dos_name[7], reg_value[256], nt_buffer[32], symlink_buffer[32];
1884 UNICODE_STRING nt_name, symlink_name, default_name;
1885 DEVICE_OBJECT *dev_obj;
1886 NTSTATUS status;
1887 struct set_dosdev_symlink_params params = { dosdevices_path, unix_path };
1889 /* create DOS device */
1890 if (MOUNTMGR_CALL( set_dosdev_symlink, &params )) return FALSE;
1892 if (driver == serial_driver)
1894 dos_name_format = L"COM%u";
1895 nt_name_format = L"\\Device\\Serial%u";
1896 reg_value_format = L"COM%u";
1897 symlink_format = L"\\DosDevices\\COM%u";
1898 default_device = L"\\DosDevices\\AUX";
1900 else
1902 dos_name_format = L"LPT%u";
1903 nt_name_format = L"\\Device\\Parallel%u";
1904 reg_value_format = L"\\DosDevices\\LPT%u";
1905 symlink_format = L"\\DosDevices\\LPT%u";
1906 default_device = L"\\DosDevices\\PRN";
1909 swprintf( dos_name, ARRAY_SIZE(dos_name), dos_name_format, n );
1911 /* create NT device */
1912 swprintf( nt_buffer, ARRAY_SIZE(nt_buffer), nt_name_format, n - 1 );
1913 RtlInitUnicodeString( &nt_name, nt_buffer );
1914 status = IoCreateDevice( driver, 0, &nt_name, 0, 0, FALSE, &dev_obj );
1915 if (status != STATUS_SUCCESS)
1917 FIXME( "IoCreateDevice %s got %lx\n", debugstr_w(nt_name.Buffer), status );
1918 return FALSE;
1920 swprintf( symlink_buffer, ARRAY_SIZE(symlink_buffer), symlink_format, n );
1921 RtlInitUnicodeString( &symlink_name, symlink_buffer );
1922 IoCreateSymbolicLink( &symlink_name, &nt_name );
1923 if (n == 1)
1925 RtlInitUnicodeString( &default_name, default_device );
1926 IoCreateSymbolicLink( &default_name, &symlink_name );
1929 /* TODO: store information about the Unix device in the NT device */
1931 /* create registry entry */
1932 swprintf( reg_value, ARRAY_SIZE(reg_value), reg_value_format, n );
1933 RegSetValueExW( windows_ports_key, nt_name.Buffer, 0, REG_SZ,
1934 (BYTE *)reg_value, (lstrlenW( reg_value ) + 1) * sizeof(WCHAR) );
1936 return TRUE;
1939 /* find and create serial or parallel ports */
1940 static void create_port_devices( DRIVER_OBJECT *driver, const char *devices )
1942 const WCHAR *windows_ports_key_name;
1943 const char *dosdev_fmt;
1944 char dosdev[8];
1945 HKEY wine_ports_key = NULL, windows_ports_key = NULL;
1946 char unix_path[256];
1947 const WCHAR *port_prefix;
1948 WCHAR reg_value[256];
1949 BOOL used[MAX_PORTS];
1950 WCHAR port[7];
1951 DWORD port_len, type, size;
1952 int i, n;
1954 if (driver == serial_driver)
1956 dosdev_fmt = "com%u";
1957 windows_ports_key_name = L"HARDWARE\\DEVICEMAP\\SERIALCOMM";
1958 port_prefix = L"COM";
1960 else
1962 dosdev_fmt = "lpt%u";
1963 windows_ports_key_name = L"HARDWARE\\DEVICEMAP\\PARALLEL PORTS";
1964 port_prefix = L"LPT";
1967 /* @@ Wine registry key: HKLM\Software\Wine\Ports */
1969 RegCreateKeyExW( HKEY_LOCAL_MACHINE, L"Software\\Wine\\Ports", 0, NULL, 0,
1970 KEY_QUERY_VALUE, NULL, &wine_ports_key, NULL );
1971 RegCreateKeyExW( HKEY_LOCAL_MACHINE, windows_ports_key_name, 0, NULL, REG_OPTION_VOLATILE,
1972 KEY_ALL_ACCESS, NULL, &windows_ports_key, NULL );
1974 /* add user-defined serial ports */
1975 memset(used, 0, sizeof(used));
1976 for (i = 0; ; i++)
1978 port_len = ARRAY_SIZE(port);
1979 size = sizeof(reg_value);
1980 if (RegEnumValueW( wine_ports_key, i, port, &port_len, NULL,
1981 &type, (BYTE*)reg_value, &size ) != ERROR_SUCCESS)
1982 break;
1983 if (type != REG_SZ || wcsnicmp( port, port_prefix, 3 ))
1984 continue;
1986 n = wcstol( port + 3, NULL, 10 );
1987 if (n < 1 || n >= MAX_PORTS)
1988 continue;
1990 if (!WideCharToMultiByte( CP_UNIXCP, WC_ERR_INVALID_CHARS, reg_value, size/sizeof(WCHAR),
1991 unix_path, sizeof(unix_path), NULL, NULL))
1992 continue;
1994 used[n - 1] = TRUE;
1995 sprintf( dosdev, dosdev_fmt, n );
1996 create_port_device( driver, n, unix_path, dosdev, windows_ports_key );
1999 /* look for ports in the usual places */
2001 for (n = 1; *devices; n++, devices += strlen(devices) + 1)
2003 while (n <= MAX_PORTS && used[n - 1]) n++;
2004 if (n > MAX_PORTS) break;
2005 sprintf( dosdev, dosdev_fmt, n );
2006 create_port_device( driver, n, devices, dosdev, windows_ports_key );
2009 RegCloseKey( wine_ports_key );
2010 RegCloseKey( windows_ports_key );
2013 /* driver entry point for the serial port driver */
2014 NTSTATUS WINAPI serial_driver_entry( DRIVER_OBJECT *driver, UNICODE_STRING *path )
2016 char devices[4096];
2017 struct detect_ports_params params = { devices, sizeof(devices) };
2019 serial_driver = driver;
2020 /* TODO: fill in driver->MajorFunction */
2022 MOUNTMGR_CALL( detect_serial_ports, &params );
2023 create_port_devices( driver, devices );
2025 return STATUS_SUCCESS;
2028 /* driver entry point for the parallel port driver */
2029 NTSTATUS WINAPI parallel_driver_entry( DRIVER_OBJECT *driver, UNICODE_STRING *path )
2031 char devices[4096];
2032 struct detect_ports_params params = { devices, sizeof(devices) };
2034 parallel_driver = driver;
2035 /* TODO: fill in driver->MajorFunction */
2037 MOUNTMGR_CALL( detect_parallel_ports, &params );
2038 create_port_devices( driver, devices );
2040 return STATUS_SUCCESS;