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
22 #include "wine/port.h"
31 #ifdef HAVE_SYS_IOCTL_H
32 # include <sys/ioctl.h>
35 #define NONAMELESSUNION
42 #include "wine/list.h"
43 #include "wine/unicode.h"
44 #include "wine/debug.h"
46 WINE_DEFAULT_DEBUG_CHANNEL(mountmgr
);
48 #define MAX_DOS_DRIVES 26
51 static const WCHAR drive_types
[][8] =
53 { 0 }, /* DEVICE_UNKNOWN */
54 { 0 }, /* DEVICE_HARDDISK */
55 {'h','d',0}, /* DEVICE_HARDDISK_VOL */
56 {'f','l','o','p','p','y',0}, /* DEVICE_FLOPPY */
57 {'c','d','r','o','m',0}, /* DEVICE_CDROM */
58 {'c','d','r','o','m',0}, /* DEVICE_DVD */
59 {'n','e','t','w','o','r','k',0}, /* DEVICE_NETWORK */
60 {'r','a','m','d','i','s','k',0} /* DEVICE_RAMDISK */
63 static const WCHAR drives_keyW
[] = {'S','o','f','t','w','a','r','e','\\',
64 'W','i','n','e','\\','D','r','i','v','e','s',0};
65 static const WCHAR ports_keyW
[] = {'S','o','f','t','w','a','r','e','\\',
66 'W','i','n','e','\\','P','o','r','t','s',0};
67 static const WCHAR scsi_keyW
[] = {'H','A','R','D','W','A','R','E','\\','D','E','V','I','C','E','M','A','P','\\','S','c','s','i',0};
68 static const WCHAR scsi_port_keyW
[] = {'S','c','s','i',' ','P','o','r','t',' ','%','d',0};
69 static const WCHAR scsi_bus_keyW
[] = {'S','c','s','i',' ','B','u','s',' ','%','d',0};
70 static const WCHAR target_id_keyW
[] = {'T','a','r','g','e','t',' ','I','d',' ','%','d',0};
71 static const WCHAR lun_keyW
[] = {'L','o','g','i','c','a','l',' ','U','n','i','t',' ','I','d',' ','%','d',0};
72 static const WCHAR devnameW
[] = {'D','e','v','i','c','e','N','a','m','e',0};
76 FS_ERROR
, /* error accessing the device */
77 FS_UNKNOWN
, /* unknown file system */
81 FS_UDF
/* For reference [E] = Ecma-167.pdf, [U] = udf260.pdf */
86 enum device_type type
; /* drive type */
87 DEVICE_OBJECT
*dev_obj
; /* disk device allocated for this volume */
88 UNICODE_STRING name
; /* device name */
89 UNICODE_STRING symlink
; /* device symlink if any */
90 STORAGE_DEVICE_NUMBER devnum
; /* device number info */
91 char *unix_device
; /* unix device path */
92 char *unix_mount
; /* unix mount point path */
93 char *serial
; /* disk serial number */
94 struct volume
*volume
; /* associated volume */
99 struct list entry
; /* entry in volumes list */
100 struct disk_device
*device
; /* disk device */
101 char *udi
; /* unique identifier for dynamic volumes */
102 unsigned int ref
; /* ref count */
103 GUID guid
; /* volume uuid */
104 struct mount_point
*mount
; /* Volume{xxx} mount point */
105 WCHAR label
[256]; /* volume label */
106 DWORD serial
; /* volume serial number */
107 enum fs_type fs_type
; /* file system type */
112 struct list entry
; /* entry in drives list */
113 struct volume
*volume
; /* volume for this drive */
114 int drive
; /* drive letter (0 = A: etc.) */
115 struct mount_point
*mount
; /* DosDevices mount point */
118 static struct list drives_list
= LIST_INIT(drives_list
);
119 static struct list volumes_list
= LIST_INIT(volumes_list
);
121 static DRIVER_OBJECT
*harddisk_driver
;
122 static DRIVER_OBJECT
*serial_driver
;
123 static DRIVER_OBJECT
*parallel_driver
;
125 static CRITICAL_SECTION device_section
;
126 static CRITICAL_SECTION_DEBUG critsect_debug
=
128 0, 0, &device_section
,
129 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
130 0, 0, { (DWORD_PTR
)(__FILE__
": device_section") }
132 static CRITICAL_SECTION device_section
= { &critsect_debug
, -1, 0, 0, 0, 0 };
134 static char *get_dosdevices_path( char **device
)
136 const char *home
= getenv( "HOME" );
137 const char *prefix
= getenv( "WINEPREFIX" );
138 size_t len
= (prefix
? strlen(prefix
) : strlen(home
) + strlen("/.wine")) + sizeof("/dosdevices/com256");
139 char *path
= HeapAlloc( GetProcessHeap(), 0, len
);
143 if (prefix
) strcpy( path
, prefix
);
146 strcpy( path
, home
);
147 strcat( path
, "/.wine" );
149 strcat( path
, "/dosdevices/a::" );
150 *device
= path
+ len
- sizeof("com256");
155 static char *strdupA( const char *str
)
159 if (!str
) return NULL
;
160 if ((ret
= RtlAllocateHeap( GetProcessHeap(), 0, strlen(str
) + 1 ))) strcpy( ret
, str
);
164 WCHAR
*strdupW( const WCHAR
*str
)
168 if (!str
) return NULL
;
169 if ((ret
= RtlAllocateHeap( GetProcessHeap(), 0, (strlenW(str
) + 1) * sizeof(WCHAR
) ))) strcpyW( ret
, str
);
173 static const GUID
*get_default_uuid( int letter
)
177 guid
.Data4
[7] = 'A' + letter
;
181 /* read a Unix symlink; returned buffer must be freed by caller */
182 static char *read_symlink( const char *path
)
189 if (!(buffer
= RtlAllocateHeap( GetProcessHeap(), 0, size
)))
191 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
194 ret
= readlink( path
, buffer
, size
);
197 RtlFreeHeap( GetProcessHeap(), 0, buffer
);
205 RtlFreeHeap( GetProcessHeap(), 0, buffer
);
210 /* update a symlink if it changed; return TRUE if updated */
211 static void update_symlink( const char *path
, const char *dest
, const char *orig_dest
)
215 if (!orig_dest
|| strcmp( orig_dest
, dest
))
218 symlink( dest
, path
);
224 /* send notification about a change to a given drive */
225 static void send_notify( int drive
, int code
)
227 DEV_BROADCAST_VOLUME info
;
229 info
.dbcv_size
= sizeof(info
);
230 info
.dbcv_devicetype
= DBT_DEVTYP_VOLUME
;
231 info
.dbcv_reserved
= 0;
232 info
.dbcv_unitmask
= 1 << drive
;
233 info
.dbcv_flags
= DBTF_MEDIA
;
234 BroadcastSystemMessageW( BSF_FORCEIFHUNG
|BSF_QUERY
, NULL
,
235 WM_DEVICECHANGE
, code
, (LPARAM
)&info
);
238 #define BLOCK_SIZE 2048
239 #define SUPERBLOCK_SIZE BLOCK_SIZE
241 #define CDFRAMES_PERSEC 75
242 #define CDFRAMES_PERMIN (CDFRAMES_PERSEC * 60)
243 #define FRAME_OF_ADDR(a) ((a)[1] * CDFRAMES_PERMIN + (a)[2] * CDFRAMES_PERSEC + (a)[3])
244 #define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc)->TrackData[(idx) - (toc)->FirstTrack].Address)
246 #define GETWORD(buf,off) MAKEWORD(buf[(off)],buf[(off+1)])
247 #define GETLONG(buf,off) MAKELONG(GETWORD(buf,off),GETWORD(buf,off+2))
249 static int open_volume_file( const struct volume
*volume
, const char *file
)
251 const char *unix_mount
= volume
->device
->unix_mount
;
255 if (!unix_mount
) return -1;
257 if (unix_mount
[0] == '/')
259 if (!(path
= HeapAlloc( GetProcessHeap(), 0, strlen( unix_mount
) + 1 + strlen( file
) + 1 )))
262 strcpy( path
, unix_mount
);
266 const char *home
= getenv( "HOME" );
267 const char *prefix
= getenv( "WINEPREFIX" );
268 size_t len
= prefix
? strlen(prefix
) : strlen(home
) + strlen("/.wine");
270 if (!(path
= HeapAlloc( GetProcessHeap(), 0, len
+ strlen("/dosdevices/") +
271 strlen(unix_mount
) + 1 + strlen( file
) + 1 )))
274 if (prefix
) strcpy( path
, prefix
);
277 strcpy( path
, home
);
278 strcat( path
, "/.wine" );
280 strcat( path
, "/dosdevices/" );
281 strcat( path
, unix_mount
);
284 strcat( path
, file
);
286 fd
= open( path
, O_RDONLY
);
287 HeapFree( GetProcessHeap(), 0, path
);
291 /* get the label by reading it from a file at the root of the filesystem */
292 static void get_filesystem_label( struct volume
*volume
)
296 char buffer
[256], *p
;
298 volume
->label
[0] = 0;
300 if ((fd
= open_volume_file( volume
, ".windows-label" )) == -1)
302 size
= read( fd
, buffer
, sizeof(buffer
) );
306 while (p
> buffer
&& (p
[-1] == ' ' || p
[-1] == '\r' || p
[-1] == '\n')) p
--;
308 if (!MultiByteToWideChar( CP_UNIXCP
, 0, buffer
, -1, volume
->label
, ARRAY_SIZE(volume
->label
) ))
309 volume
->label
[ARRAY_SIZE(volume
->label
) - 1] = 0;
312 /* get the serial number by reading it from a file at the root of the filesystem */
313 static void get_filesystem_serial( struct volume
*volume
)
321 if ((fd
= open_volume_file( volume
, ".windows-serial" )) == -1)
323 size
= read( fd
, buffer
, sizeof(buffer
) );
326 if (size
< 0) return;
328 volume
->serial
= strtoul( buffer
, NULL
, 16 );
332 /******************************************************************
333 * VOLUME_FindCdRomDataBestVoldesc
335 static DWORD
VOLUME_FindCdRomDataBestVoldesc( HANDLE handle
)
337 BYTE cur_vd_type
, max_vd_type
= 0;
339 DWORD size
, offs
, best_offs
= 0, extra_offs
= 0;
341 for (offs
= 0x8000; offs
<= 0x9800; offs
+= 0x800)
343 /* if 'CDROM' occurs at position 8, this is a pre-iso9660 cd, and
344 * the volume label is displaced forward by 8
346 if (SetFilePointer( handle
, offs
, NULL
, FILE_BEGIN
) != offs
) break;
347 if (!ReadFile( handle
, buffer
, sizeof(buffer
), &size
, NULL
)) break;
348 if (size
!= sizeof(buffer
)) break;
349 /* check for non-ISO9660 signature */
350 if (!memcmp( buffer
+ 11, "ROM", 3 )) extra_offs
= 8;
351 cur_vd_type
= buffer
[extra_offs
];
352 if (cur_vd_type
== 0xff) /* voldesc set terminator */
354 if (cur_vd_type
> max_vd_type
)
356 max_vd_type
= cur_vd_type
;
357 best_offs
= offs
+ extra_offs
;
364 /***********************************************************************
365 * VOLUME_ReadFATSuperblock
367 static enum fs_type
VOLUME_ReadFATSuperblock( HANDLE handle
, BYTE
*buff
)
371 /* try a fixed disk, with a FAT partition */
372 if (SetFilePointer( handle
, 0, NULL
, FILE_BEGIN
) != 0 ||
373 !ReadFile( handle
, buff
, SUPERBLOCK_SIZE
, &size
, NULL
))
375 if (GetLastError() == ERROR_BAD_DEV_TYPE
) return FS_UNKNOWN
; /* not a real device */
379 if (size
< SUPERBLOCK_SIZE
) return FS_UNKNOWN
;
381 /* FIXME: do really all FAT have their name beginning with
382 * "FAT" ? (At least FAT12, FAT16 and FAT32 have :)
384 if (!memcmp(buff
+0x36, "FAT", 3) || !memcmp(buff
+0x52, "FAT", 3))
386 /* guess which type of FAT we have */
388 unsigned int sectors
,
397 sect_per_fat
= GETWORD(buff
, 0x16);
398 if (!sect_per_fat
) sect_per_fat
= GETLONG(buff
, 0x24);
399 total_sectors
= GETWORD(buff
, 0x13);
401 total_sectors
= GETLONG(buff
, 0x20);
402 num_boot_sectors
= GETWORD(buff
, 0x0e);
403 num_fats
= buff
[0x10];
404 num_root_dir_ents
= GETWORD(buff
, 0x11);
405 bytes_per_sector
= GETWORD(buff
, 0x0b);
406 sectors_per_cluster
= buff
[0x0d];
407 /* check if the parameters are reasonable and will not cause
408 * arithmetic errors in the calculation */
409 reasonable
= num_boot_sectors
< total_sectors
&&
411 bytes_per_sector
>= 512 && bytes_per_sector
% 512 == 0 &&
412 sectors_per_cluster
>= 1;
413 if (!reasonable
) return FS_UNKNOWN
;
414 sectors
= total_sectors
- num_boot_sectors
- num_fats
* sect_per_fat
-
415 (num_root_dir_ents
* 32 + bytes_per_sector
- 1) / bytes_per_sector
;
416 nclust
= sectors
/ sectors_per_cluster
;
417 if ((buff
[0x42] == 0x28 || buff
[0x42] == 0x29) &&
418 !memcmp(buff
+0x52, "FAT", 3)) return FS_FAT32
;
421 if ((buff
[0x26] == 0x28 || buff
[0x26] == 0x29) &&
422 !memcmp(buff
+0x36, "FAT", 3))
430 /***********************************************************************
433 static BOOL
VOLUME_ReadCDBlock( HANDLE handle
, BYTE
*buff
, INT offs
)
435 DWORD size
, whence
= offs
>= 0 ? FILE_BEGIN
: FILE_END
;
437 if (SetFilePointer( handle
, offs
, NULL
, whence
) != offs
||
438 !ReadFile( handle
, buff
, SUPERBLOCK_SIZE
, &size
, NULL
) ||
439 size
!= SUPERBLOCK_SIZE
)
446 /***********************************************************************
447 * VOLUME_ReadCDSuperblock
449 static enum fs_type
VOLUME_ReadCDSuperblock( HANDLE handle
, BYTE
*buff
)
454 /* Check UDF first as UDF and ISO9660 structures can coexist on the same medium
455 * Starting from sector 16, we may find :
456 * - a CD-ROM Volume Descriptor Set (ISO9660) containing one or more Volume Descriptors
457 * - an Extended Area (UDF) -- [E] 2/8.3.1 and [U] 2.1.7
458 * There is no explicit end so read 16 sectors and then give up */
459 for( i
=16; i
<16+16; i
++)
461 if (!VOLUME_ReadCDBlock(handle
, buff
, i
*BLOCK_SIZE
))
464 /* We are supposed to check "BEA01", "NSR0x" and "TEA01" IDs + verify tag checksum
465 * but we assume the volume is well-formatted */
466 if (!memcmp(&buff
[1], "BEA01", 5)) return FS_UDF
;
469 offs
= VOLUME_FindCdRomDataBestVoldesc( handle
);
470 if (!offs
) return FS_UNKNOWN
;
472 if (!VOLUME_ReadCDBlock(handle
, buff
, offs
))
475 /* check for the iso9660 identifier */
476 if (!memcmp(&buff
[1], "CD001", 5)) return FS_ISO9660
;
481 /**************************************************************************
483 * Find the Primary Volume Descriptor
485 static BOOL
UDF_Find_PVD( HANDLE handle
, BYTE pvd
[] )
489 INT locations
[] = { 256, -1, -257, 512 };
491 for(i
=0; i
<ARRAY_SIZE(locations
); i
++)
493 if (!VOLUME_ReadCDBlock(handle
, pvd
, locations
[i
]*BLOCK_SIZE
))
496 /* Tag Identifier of Anchor Volume Descriptor Pointer is 2 -- [E] 3/10.2.1 */
497 if (pvd
[0]==2 && pvd
[1]==0)
499 /* Tag location (Uint32) at offset 12, little-endian */
500 offset
= pvd
[20 + 0];
501 offset
|= pvd
[20 + 1] << 8;
502 offset
|= pvd
[20 + 2] << 16;
503 offset
|= pvd
[20 + 3] << 24;
504 offset
*= BLOCK_SIZE
;
506 if (!VOLUME_ReadCDBlock(handle
, pvd
, offset
))
509 /* Check for the Primary Volume Descriptor Tag Id -- [E] 3/10.1.1 */
510 if (pvd
[0]!=1 || pvd
[1]!=0)
513 /* 8 or 16 bits per character -- [U] 2.1.1 */
514 if (!(pvd
[24]==8 || pvd
[24]==16))
525 /**************************************************************************
526 * VOLUME_GetSuperblockLabel
528 static void VOLUME_GetSuperblockLabel( struct volume
*volume
, HANDLE handle
, const BYTE
*superblock
)
530 const BYTE
*label_ptr
= NULL
;
533 switch (volume
->fs_type
)
539 get_filesystem_label( volume
);
542 label_ptr
= superblock
+ 0x2b;
546 label_ptr
= superblock
+ 0x47;
551 BYTE ver
= superblock
[0x5a];
553 if (superblock
[0x58] == 0x25 && superblock
[0x59] == 0x2f && /* Unicode ID */
554 ((ver
== 0x40) || (ver
== 0x43) || (ver
== 0x45)))
555 { /* yippee, unicode */
558 for (i
= 0; i
< 16; i
++)
559 volume
->label
[i
] = (superblock
[40+2*i
] << 8) | superblock
[41+2*i
];
560 volume
->label
[i
] = 0;
561 while (i
&& volume
->label
[i
-1] == ' ') volume
->label
[--i
] = 0;
564 label_ptr
= superblock
+ 40;
570 BYTE pvd
[BLOCK_SIZE
];
572 if(!UDF_Find_PVD(handle
, pvd
))
578 /* [E] 3/10.1.4 and [U] 2.1.1 */
581 label_ptr
= pvd
+ 24 + 1;
582 label_len
= pvd
[24+32-1];
589 label_len
= 1 + pvd
[24+32-1];
590 for (i
= 0; i
< label_len
; i
+= 2)
591 volume
->label
[i
/2] = (pvd
[24+1+i
] << 8) | pvd
[24+1+i
+1];
592 volume
->label
[label_len
] = 0;
597 if (label_len
) RtlMultiByteToUnicodeN( volume
->label
, sizeof(volume
->label
) - sizeof(WCHAR
),
598 &label_len
, (const char *)label_ptr
, label_len
);
599 label_len
/= sizeof(WCHAR
);
600 volume
->label
[label_len
] = 0;
601 while (label_len
&& volume
->label
[label_len
-1] == ' ') volume
->label
[--label_len
] = 0;
605 /**************************************************************************
606 * UDF_Find_FSD_Sector
607 * Find the File Set Descriptor used to compute the serial of a UDF volume
609 static int UDF_Find_FSD_Sector( HANDLE handle
, BYTE block
[] )
611 int i
, PVD_sector
, PD_sector
, PD_length
;
613 if(!UDF_Find_PVD(handle
,block
))
616 /* Retrieve the tag location of the PVD -- [E] 3/7.2 */
617 PVD_sector
= block
[12 + 0];
618 PVD_sector
|= block
[12 + 1] << 8;
619 PVD_sector
|= block
[12 + 2] << 16;
620 PVD_sector
|= block
[12 + 3] << 24;
622 /* Find the Partition Descriptor */
623 for(i
=PVD_sector
+1; ; i
++)
625 if(!VOLUME_ReadCDBlock(handle
, block
, i
*BLOCK_SIZE
))
628 /* Partition Descriptor Tag Id -- [E] 3/10.5.1 */
629 if(block
[0]==5 && block
[1]==0)
632 /* Terminating Descriptor Tag Id -- [E] 3/10.9.1 */
633 if(block
[0]==8 && block
[1]==0)
637 /* Find the partition starting location -- [E] 3/10.5.8 */
638 PD_sector
= block
[188 + 0];
639 PD_sector
|= block
[188 + 1] << 8;
640 PD_sector
|= block
[188 + 2] << 16;
641 PD_sector
|= block
[188 + 3] << 24;
643 /* Find the partition length -- [E] 3/10.5.9 */
644 PD_length
= block
[192 + 0];
645 PD_length
|= block
[192 + 1] << 8;
646 PD_length
|= block
[192 + 2] << 16;
647 PD_length
|= block
[192 + 3] << 24;
649 for(i
=PD_sector
; i
<PD_sector
+PD_length
; i
++)
651 if(!VOLUME_ReadCDBlock(handle
, block
, i
*BLOCK_SIZE
))
654 /* File Set Descriptor Tag Id -- [E] 3/14.1.1 */
655 if(block
[0]==0 && block
[1]==1)
660 WARN("FSD sector not found, serial may be incorrect\n");
665 /**************************************************************************
666 * VOLUME_GetSuperblockSerial
668 static void VOLUME_GetSuperblockSerial( struct volume
*volume
, HANDLE handle
, const BYTE
*superblock
)
671 BYTE block
[BLOCK_SIZE
];
673 switch (volume
->fs_type
)
678 get_filesystem_serial( volume
);
681 volume
->serial
= GETLONG( superblock
, 0x27 );
684 volume
->serial
= GETLONG( superblock
, 0x43 );
687 FSD_sector
= UDF_Find_FSD_Sector(handle
, block
);
688 if (!VOLUME_ReadCDBlock(handle
, block
, FSD_sector
*BLOCK_SIZE
))
697 sum
[0] = sum
[1] = sum
[2] = sum
[3] = 0;
698 for (i
= 0; i
< 2048; i
+= 4)
700 /* DON'T optimize this into DWORD !! (breaks overflow) */
701 sum
[0] += superblock
[i
+0];
702 sum
[1] += superblock
[i
+1];
703 sum
[2] += superblock
[i
+2];
704 sum
[3] += superblock
[i
+3];
707 * OK, another braindead one... argh. Just believe it.
708 * Me$$ysoft chose to reverse the serial number in NT4/W2K.
709 * It's true and nobody will ever be able to change it.
711 if ((GetVersion() & 0x80000000) || volume
->fs_type
== FS_UDF
)
712 volume
->serial
= (sum
[3] << 24) | (sum
[2] << 16) | (sum
[1] << 8) | sum
[0];
714 volume
->serial
= (sum
[0] << 24) | (sum
[1] << 16) | (sum
[2] << 8) | sum
[3];
720 /**************************************************************************
721 * VOLUME_GetAudioCDSerial
723 static DWORD
VOLUME_GetAudioCDSerial( const CDROM_TOC
*toc
)
728 for (i
= 0; i
<= toc
->LastTrack
- toc
->FirstTrack
; i
++)
729 serial
+= ((toc
->TrackData
[i
].Address
[1] << 16) |
730 (toc
->TrackData
[i
].Address
[2] << 8) |
731 toc
->TrackData
[i
].Address
[3]);
734 * dwStart, dwEnd collect the beginning and end of the disc respectively, in
736 * There it is collected for correcting the serial when there are less than
739 if (toc
->LastTrack
- toc
->FirstTrack
+ 1 < 3)
741 DWORD dwStart
= FRAME_OF_TOC(toc
, toc
->FirstTrack
);
742 DWORD dwEnd
= FRAME_OF_TOC(toc
, toc
->LastTrack
+ 1);
743 serial
+= dwEnd
- dwStart
;
749 /* create the disk device for a given volume */
750 static NTSTATUS
create_disk_device( enum device_type type
, struct disk_device
**device_ret
, struct volume
*volume
)
752 static const WCHAR harddiskvolW
[] = {'\\','D','e','v','i','c','e',
753 '\\','H','a','r','d','d','i','s','k','V','o','l','u','m','e','%','u',0};
754 static const WCHAR harddiskW
[] = {'\\','D','e','v','i','c','e','\\','H','a','r','d','d','i','s','k','%','u',0};
755 static const WCHAR cdromW
[] = {'\\','D','e','v','i','c','e','\\','C','d','R','o','m','%','u',0};
756 static const WCHAR floppyW
[] = {'\\','D','e','v','i','c','e','\\','F','l','o','p','p','y','%','u',0};
757 static const WCHAR ramdiskW
[] = {'\\','D','e','v','i','c','e','\\','R','a','m','d','i','s','k','%','u',0};
758 static const WCHAR cdromlinkW
[] = {'\\','?','?','\\','C','d','R','o','m','%','u',0};
759 static const WCHAR physdriveW
[] = {'\\','?','?','\\','P','h','y','s','i','c','a','l','D','r','i','v','e','%','u',0};
763 const WCHAR
*format
= NULL
;
764 const WCHAR
*link_format
= NULL
;
766 DEVICE_OBJECT
*dev_obj
;
767 struct disk_device
*device
;
772 case DEVICE_HARDDISK
:
773 case DEVICE_NETWORK
: /* FIXME */
775 link_format
= physdriveW
;
777 case DEVICE_HARDDISK_VOL
:
778 format
= harddiskvolW
;
779 first
= 1; /* harddisk volumes start counting from 1 */
787 link_format
= cdromlinkW
;
794 name
.MaximumLength
= (strlenW(format
) + 10) * sizeof(WCHAR
);
795 name
.Buffer
= RtlAllocateHeap( GetProcessHeap(), 0, name
.MaximumLength
);
796 for (i
= first
; i
< 32; i
++)
798 sprintfW( name
.Buffer
, format
, i
);
799 name
.Length
= strlenW(name
.Buffer
) * sizeof(WCHAR
);
800 status
= IoCreateDevice( harddisk_driver
, sizeof(*device
), &name
, 0, 0, FALSE
, &dev_obj
);
801 if (status
!= STATUS_OBJECT_NAME_COLLISION
) break;
805 device
= dev_obj
->DeviceExtension
;
806 device
->dev_obj
= dev_obj
;
809 device
->unix_device
= NULL
;
810 device
->unix_mount
= NULL
;
811 device
->symlink
.Buffer
= NULL
;
812 device
->volume
= volume
;
816 UNICODE_STRING symlink
;
818 symlink
.MaximumLength
= (strlenW(link_format
) + 10) * sizeof(WCHAR
);
819 if ((symlink
.Buffer
= RtlAllocateHeap( GetProcessHeap(), 0, symlink
.MaximumLength
)))
821 sprintfW( symlink
.Buffer
, link_format
, i
);
822 symlink
.Length
= strlenW(symlink
.Buffer
) * sizeof(WCHAR
);
823 if (!IoCreateSymbolicLink( &symlink
, &name
)) device
->symlink
= symlink
;
831 device
->devnum
.DeviceType
= FILE_DEVICE_DISK
;
832 device
->devnum
.DeviceNumber
= i
;
833 device
->devnum
.PartitionNumber
= ~0u;
836 device
->devnum
.DeviceType
= FILE_DEVICE_CD_ROM
;
837 device
->devnum
.DeviceNumber
= i
;
838 device
->devnum
.PartitionNumber
= ~0u;
841 device
->devnum
.DeviceType
= FILE_DEVICE_DVD
;
842 device
->devnum
.DeviceNumber
= i
;
843 device
->devnum
.PartitionNumber
= ~0u;
846 case DEVICE_HARDDISK
:
847 case DEVICE_NETWORK
: /* FIXME */
848 device
->devnum
.DeviceType
= FILE_DEVICE_DISK
;
849 device
->devnum
.DeviceNumber
= i
;
850 device
->devnum
.PartitionNumber
= 0;
852 case DEVICE_HARDDISK_VOL
:
853 device
->devnum
.DeviceType
= FILE_DEVICE_DISK
;
854 device
->devnum
.DeviceNumber
= 0;
855 device
->devnum
.PartitionNumber
= i
;
858 *device_ret
= device
;
859 TRACE( "created device %s\n", debugstr_w(name
.Buffer
) );
863 FIXME( "IoCreateDevice %s got %x\n", debugstr_w(name
.Buffer
), status
);
864 RtlFreeUnicodeString( &name
);
869 /* delete the disk device for a given drive */
870 static void delete_disk_device( struct disk_device
*device
)
872 TRACE( "deleting device %s\n", debugstr_w(device
->name
.Buffer
) );
873 if (device
->symlink
.Buffer
)
875 IoDeleteSymbolicLink( &device
->symlink
);
876 RtlFreeUnicodeString( &device
->symlink
);
878 RtlFreeHeap( GetProcessHeap(), 0, device
->unix_device
);
879 RtlFreeHeap( GetProcessHeap(), 0, device
->unix_mount
);
880 RtlFreeHeap( GetProcessHeap(), 0, device
->serial
);
881 RtlFreeUnicodeString( &device
->name
);
882 IoDeleteDevice( device
->dev_obj
);
885 /* grab another reference to a volume */
886 static struct volume
*grab_volume( struct volume
*volume
)
892 /* release a volume and delete the corresponding disk device when refcount is 0 */
893 static unsigned int release_volume( struct volume
*volume
)
895 unsigned int ret
= --volume
->ref
;
899 TRACE( "%s udi %s\n", debugstr_guid(&volume
->guid
), debugstr_a(volume
->udi
) );
900 assert( !volume
->udi
);
901 list_remove( &volume
->entry
);
902 if (volume
->mount
) delete_mount_point( volume
->mount
);
903 delete_disk_device( volume
->device
);
904 RtlFreeHeap( GetProcessHeap(), 0, volume
);
909 /* set the volume udi */
910 static void set_volume_udi( struct volume
*volume
, const char *udi
)
914 assert( !volume
->udi
);
915 /* having a udi means the HAL side holds an extra reference */
916 if ((volume
->udi
= strdupA( udi
))) grab_volume( volume
);
918 else if (volume
->udi
)
920 RtlFreeHeap( GetProcessHeap(), 0, volume
->udi
);
922 release_volume( volume
);
926 /* create a disk volume */
927 static NTSTATUS
create_volume( const char *udi
, enum device_type type
, struct volume
**volume_ret
)
929 struct volume
*volume
;
932 if (!(volume
= RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*volume
) )))
933 return STATUS_NO_MEMORY
;
935 if (!(status
= create_disk_device( type
, &volume
->device
, volume
)))
937 if (udi
) set_volume_udi( volume
, udi
);
938 list_add_tail( &volumes_list
, &volume
->entry
);
939 *volume_ret
= grab_volume( volume
);
941 else RtlFreeHeap( GetProcessHeap(), 0, volume
);
946 /* create the disk device for a given volume */
947 static NTSTATUS
create_dos_device( struct volume
*volume
, const char *udi
, int letter
,
948 enum device_type type
, struct dos_drive
**drive_ret
)
950 struct dos_drive
*drive
;
953 if (!(drive
= RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*drive
) ))) return STATUS_NO_MEMORY
;
954 drive
->drive
= letter
;
959 if (udi
) set_volume_udi( volume
, udi
);
960 drive
->volume
= grab_volume( volume
);
961 status
= STATUS_SUCCESS
;
963 else status
= create_volume( udi
, type
, &drive
->volume
);
965 if (status
== STATUS_SUCCESS
)
967 list_add_tail( &drives_list
, &drive
->entry
);
970 else RtlFreeHeap( GetProcessHeap(), 0, drive
);
975 /* delete the disk device for a given drive */
976 static void delete_dos_device( struct dos_drive
*drive
)
978 list_remove( &drive
->entry
);
979 if (drive
->mount
) delete_mount_point( drive
->mount
);
980 release_volume( drive
->volume
);
981 RtlFreeHeap( GetProcessHeap(), 0, drive
);
984 /* find a volume that matches the parameters */
985 static struct volume
*find_matching_volume( const char *udi
, const char *device
,
986 const char *mount_point
, enum device_type type
)
988 struct volume
*volume
;
989 struct disk_device
*disk_device
;
991 LIST_FOR_EACH_ENTRY( volume
, &volumes_list
, struct volume
, entry
)
995 /* when we have a udi we only match drives added manually */
996 if (udi
&& volume
->udi
) continue;
997 /* and when we don't have a udi we only match dynamic drives */
998 if (!udi
&& !volume
->udi
) continue;
1000 disk_device
= volume
->device
;
1001 if (disk_device
->type
!= type
) continue;
1002 if (device
&& disk_device
->unix_device
)
1004 if (strcmp( device
, disk_device
->unix_device
)) continue;
1007 if (mount_point
&& disk_device
->unix_mount
)
1009 if (strcmp( mount_point
, disk_device
->unix_mount
)) continue;
1012 if (!match
) continue;
1013 TRACE( "found matching volume %s for device %s mount %s type %u\n",
1014 debugstr_guid(&volume
->guid
), debugstr_a(device
), debugstr_a(mount_point
), type
);
1015 return grab_volume( volume
);
1020 static BOOL
get_volume_device_info( struct volume
*volume
)
1022 const char *unix_device
= volume
->device
->unix_device
;
1027 BYTE superblock
[SUPERBLOCK_SIZE
];
1033 if (access( unix_device
, R_OK
))
1035 WARN("Unable to open %s, not accessible\n", debugstr_a(unix_device
));
1040 if (!(name
= wine_get_dos_file_name( unix_device
)))
1042 ERR("Failed to convert %s to NT, err %u\n", debugstr_a(unix_device
), GetLastError());
1045 handle
= CreateFileW( name
, GENERIC_READ
| SYNCHRONIZE
, FILE_SHARE_READ
| FILE_SHARE_WRITE
,
1046 NULL
, OPEN_EXISTING
, 0, 0 );
1047 RtlFreeHeap( GetProcessHeap(), 0, name
);
1048 if (handle
== INVALID_HANDLE_VALUE
)
1050 WARN("Failed to open %s, err %u\n", debugstr_a(unix_device
), GetLastError());
1054 if (DeviceIoControl( handle
, IOCTL_CDROM_READ_TOC
, NULL
, 0, &toc
, sizeof(toc
), &size
, 0 ))
1056 if (!(toc
.TrackData
[0].Control
& 0x04)) /* audio track */
1058 static const WCHAR audiocdW
[] = {'A','u','d','i','o',' ','C','D',0};
1059 TRACE( "%s: found audio CD\n", debugstr_a(unix_device
) );
1060 lstrcpynW( volume
->label
, audiocdW
, ARRAY_SIZE(volume
->label
) );
1061 volume
->serial
= VOLUME_GetAudioCDSerial( &toc
);
1062 volume
->fs_type
= FS_ISO9660
;
1063 CloseHandle( handle
);
1066 volume
->fs_type
= VOLUME_ReadCDSuperblock( handle
, superblock
);
1070 volume
->fs_type
= VOLUME_ReadFATSuperblock( handle
, superblock
);
1071 if (volume
->fs_type
== FS_UNKNOWN
) volume
->fs_type
= VOLUME_ReadCDSuperblock( handle
, superblock
);
1074 TRACE( "%s: found fs type %d\n", debugstr_a(unix_device
), volume
->fs_type
);
1075 if (volume
->fs_type
== FS_ERROR
)
1077 CloseHandle( handle
);
1081 VOLUME_GetSuperblockLabel( volume
, handle
, superblock
);
1082 VOLUME_GetSuperblockSerial( volume
, handle
, superblock
);
1084 CloseHandle( handle
);
1088 /* set disk serial for dos devices that reside on a given Unix device */
1089 static void set_dos_devices_disk_serial( struct disk_device
*device
)
1091 struct dos_drive
*drive
;
1092 struct stat dev_st
, drive_st
;
1095 if (!device
->serial
|| !device
->unix_mount
|| stat( device
->unix_mount
, &dev_st
) == -1) return;
1097 if (!(path
= get_dosdevices_path( &p
))) return;
1100 LIST_FOR_EACH_ENTRY( drive
, &drives_list
, struct dos_drive
, entry
)
1102 /* drives mapped to Unix devices already have serial set, if available */
1103 if (drive
->volume
->device
->unix_device
) continue;
1105 p
[0] = 'a' + drive
->drive
;
1107 /* copy serial if drive resides on this Unix device */
1108 if (stat( path
, &drive_st
) != -1 && drive_st
.st_rdev
== dev_st
.st_rdev
)
1110 HeapFree( GetProcessHeap(), 0, drive
->volume
->device
->serial
);
1111 drive
->volume
->device
->serial
= strdupA( device
->serial
);
1115 HeapFree( GetProcessHeap(), 0, path
);
1118 /* change the information for an existing volume */
1119 static NTSTATUS
set_volume_info( struct volume
*volume
, struct dos_drive
*drive
, const char *device
,
1120 const char *mount_point
, enum device_type type
, const GUID
*guid
,
1121 const char *disk_serial
)
1124 unsigned int id_len
= 0;
1125 struct disk_device
*disk_device
= volume
->device
;
1128 if (type
!= disk_device
->type
)
1130 if ((status
= create_disk_device( type
, &disk_device
, volume
))) return status
;
1133 delete_mount_point( volume
->mount
);
1134 volume
->mount
= NULL
;
1136 if (drive
&& drive
->mount
)
1138 delete_mount_point( drive
->mount
);
1139 drive
->mount
= NULL
;
1141 delete_disk_device( volume
->device
);
1142 volume
->device
= disk_device
;
1146 RtlFreeHeap( GetProcessHeap(), 0, disk_device
->unix_device
);
1147 RtlFreeHeap( GetProcessHeap(), 0, disk_device
->unix_mount
);
1148 RtlFreeHeap( GetProcessHeap(), 0, disk_device
->serial
);
1150 disk_device
->unix_device
= strdupA( device
);
1151 disk_device
->unix_mount
= strdupA( mount_point
);
1152 disk_device
->serial
= strdupA( disk_serial
);
1153 set_dos_devices_disk_serial( disk_device
);
1155 if (!get_volume_device_info( volume
))
1157 if (volume
->device
->type
== DEVICE_CDROM
)
1158 volume
->fs_type
= FS_ISO9660
;
1159 else if (volume
->device
->type
== DEVICE_DVD
)
1160 volume
->fs_type
= FS_UDF
;
1162 volume
->fs_type
= FS_UNKNOWN
;
1164 get_filesystem_label( volume
);
1165 get_filesystem_serial( volume
);
1168 TRACE("fs_type %#x, label %s, serial %08x\n", volume
->fs_type
, debugstr_w(volume
->label
), volume
->serial
);
1170 if (guid
&& memcmp( &volume
->guid
, guid
, sizeof(volume
->guid
) ))
1172 volume
->guid
= *guid
;
1175 delete_mount_point( volume
->mount
);
1176 volume
->mount
= NULL
;
1180 if (!volume
->serial
)
1181 memcpy(&volume
->serial
, &volume
->guid
.Data4
[4], sizeof(DWORD
));
1184 volume
->mount
= add_volume_mount_point( disk_device
->dev_obj
, &disk_device
->name
, &volume
->guid
);
1185 if (drive
&& !drive
->mount
)
1186 drive
->mount
= add_dosdev_mount_point( disk_device
->dev_obj
, &disk_device
->name
, drive
->drive
);
1188 if (disk_device
->unix_mount
)
1190 id
= disk_device
->unix_mount
;
1191 id_len
= strlen( disk_device
->unix_mount
) + 1;
1193 if (volume
->mount
) set_mount_point_id( volume
->mount
, id
, id_len
);
1194 if (drive
&& drive
->mount
) set_mount_point_id( drive
->mount
, id
, id_len
);
1196 return STATUS_SUCCESS
;
1199 /* change the drive letter or volume for an existing drive */
1200 static void set_drive_info( struct dos_drive
*drive
, int letter
, struct volume
*volume
)
1202 if (drive
->drive
!= letter
)
1204 if (drive
->mount
) delete_mount_point( drive
->mount
);
1205 drive
->mount
= NULL
;
1206 drive
->drive
= letter
;
1208 if (drive
->volume
!= volume
)
1210 if (drive
->mount
) delete_mount_point( drive
->mount
);
1211 drive
->mount
= NULL
;
1212 grab_volume( volume
);
1213 release_volume( drive
->volume
);
1214 drive
->volume
= volume
;
1218 static inline BOOL
is_valid_device( struct stat
*st
)
1220 #if defined(linux) || defined(__sun__)
1221 return S_ISBLK( st
->st_mode
);
1223 /* disks are char devices on *BSD */
1224 return S_ISCHR( st
->st_mode
);
1228 /* find or create a DOS drive for the corresponding device */
1229 static int add_drive( const char *device
, enum device_type type
)
1233 struct stat dev_st
, drive_st
;
1234 int drive
, first
, last
, avail
= 0;
1236 if (stat( device
, &dev_st
) == -1 || !is_valid_device( &dev_st
)) return -1;
1238 if (!(path
= get_dosdevices_path( &p
))) return -1;
1240 memset( in_use
, 0, sizeof(in_use
) );
1262 for (drive
= first
; drive
< last
; drive
++)
1264 if (in_use
[drive
]) continue; /* already checked */
1266 if (stat( path
, &drive_st
) == -1)
1268 if (lstat( path
, &drive_st
) == -1 && errno
== ENOENT
) /* this is a candidate */
1273 /* if mount point symlink doesn't exist either, it's available */
1274 if (lstat( path
, &drive_st
) == -1 && errno
== ENOENT
) avail
= drive
;
1278 else in_use
[drive
] = 1;
1283 if (!is_valid_device( &drive_st
)) continue;
1284 if (dev_st
.st_rdev
== drive_st
.st_rdev
) goto done
;
1289 /* try to use the one we found */
1292 if (symlink( device
, path
) != -1) goto done
;
1293 /* failed, retry the search */
1299 HeapFree( GetProcessHeap(), 0, path
);
1303 /* create devices for mapped drives */
1304 static void create_drive_devices(void)
1306 char *path
, *p
, *link
, *device
;
1307 struct dos_drive
*drive
;
1308 struct volume
*volume
;
1311 enum device_type drive_type
;
1312 WCHAR driveW
[] = {'a',':',0};
1314 if (!(path
= get_dosdevices_path( &p
))) return;
1315 if (RegOpenKeyW( HKEY_LOCAL_MACHINE
, drives_keyW
, &drives_key
)) drives_key
= 0;
1317 for (i
= 0; i
< MAX_DOS_DRIVES
; i
++)
1321 if (!(link
= read_symlink( path
))) continue;
1323 device
= read_symlink( path
);
1325 drive_type
= i
< 2 ? DEVICE_FLOPPY
: DEVICE_HARDDISK_VOL
;
1329 DWORD j
, type
, size
= sizeof(buffer
);
1331 driveW
[0] = 'a' + i
;
1332 if (!RegQueryValueExW( drives_key
, driveW
, NULL
, &type
, (BYTE
*)buffer
, &size
) &&
1335 for (j
= 0; j
< ARRAY_SIZE(drive_types
); j
++)
1336 if (drive_types
[j
][0] && !strcmpiW( buffer
, drive_types
[j
] ))
1341 if (drive_type
== DEVICE_FLOPPY
&& i
>= 2) drive_type
= DEVICE_HARDDISK
;
1345 volume
= find_matching_volume( NULL
, device
, link
, drive_type
);
1346 if (!create_dos_device( volume
, NULL
, i
, drive_type
, &drive
))
1348 /* don't reset uuid if we used an existing volume */
1349 const GUID
*guid
= volume
? NULL
: get_default_uuid(i
);
1350 set_volume_info( drive
->volume
, drive
, device
, link
, drive_type
, guid
, NULL
);
1354 RtlFreeHeap( GetProcessHeap(), 0, link
);
1355 RtlFreeHeap( GetProcessHeap(), 0, device
);
1357 if (volume
) release_volume( volume
);
1359 RegCloseKey( drives_key
);
1360 RtlFreeHeap( GetProcessHeap(), 0, path
);
1363 /* open the "Logical Unit" key for a given SCSI address */
1364 static HKEY
get_scsi_device_lun_key( SCSI_ADDRESS
*scsi_addr
)
1367 HKEY scsi_key
, port_key
, bus_key
, target_key
, lun_key
;
1369 if (RegOpenKeyExW( HKEY_LOCAL_MACHINE
, scsi_keyW
, 0, KEY_READ
|KEY_WRITE
, &scsi_key
)) return NULL
;
1371 snprintfW( dataW
, ARRAY_SIZE( dataW
), scsi_port_keyW
, scsi_addr
->PortNumber
);
1372 if (RegCreateKeyExW( scsi_key
, dataW
, 0, NULL
, REG_OPTION_VOLATILE
, KEY_ALL_ACCESS
, NULL
, &port_key
, NULL
)) return NULL
;
1373 RegCloseKey( scsi_key
);
1375 snprintfW( dataW
, ARRAY_SIZE( dataW
), scsi_bus_keyW
, scsi_addr
->PathId
);
1376 if (RegCreateKeyExW( port_key
, dataW
, 0, NULL
, REG_OPTION_VOLATILE
, KEY_ALL_ACCESS
, NULL
, &bus_key
, NULL
)) return NULL
;
1377 RegCloseKey( port_key
);
1379 snprintfW( dataW
, ARRAY_SIZE( dataW
), target_id_keyW
, scsi_addr
->TargetId
);
1380 if (RegCreateKeyExW( bus_key
, dataW
, 0, NULL
, REG_OPTION_VOLATILE
, KEY_ALL_ACCESS
, NULL
, &target_key
, NULL
)) return NULL
;
1381 RegCloseKey( bus_key
);
1383 snprintfW( dataW
, ARRAY_SIZE( dataW
), lun_keyW
, scsi_addr
->Lun
);
1384 if (RegCreateKeyExW( target_key
, dataW
, 0, NULL
, REG_OPTION_VOLATILE
, KEY_ALL_ACCESS
, NULL
, &lun_key
, NULL
)) return NULL
;
1385 RegCloseKey( target_key
);
1390 /* fill in the "Logical Unit" key for a given SCSI address */
1391 void create_scsi_entry( SCSI_ADDRESS
*scsi_addr
, UINT init_id
, const char *driver
, UINT type
, const char *model
, const UNICODE_STRING
*dev
)
1393 static UCHAR tape_no
= 0;
1394 static const WCHAR tapeW
[] = {'T','a','p','e','%','d',0};
1395 static const WCHAR init_id_keyW
[] = {'I','n','i','t','i','a','t','o','r',' ','I','d',' ','%','d',0};
1396 static const WCHAR driverW
[] = {'D','r','i','v','e','r',0};
1397 static const WCHAR bus_time_scanW
[] = {'F','i','r','s','t','B','u','s','T','i','m','e','S','c','a','n','I','n','M','s',0};
1398 static const WCHAR typeW
[] = {'T','y','p','e',0};
1399 static const WCHAR identW
[] = {'I','d','e','n','t','i','f','i','e','r',0};
1411 if (RegOpenKeyExW( HKEY_LOCAL_MACHINE
, scsi_keyW
, 0, KEY_READ
|KEY_WRITE
, &scsi_key
)) return;
1413 snprintfW( dataW
, ARRAY_SIZE( dataW
), scsi_port_keyW
, scsi_addr
->PortNumber
);
1414 if (RegCreateKeyExW( scsi_key
, dataW
, 0, NULL
, REG_OPTION_VOLATILE
, KEY_ALL_ACCESS
, NULL
, &port_key
, NULL
)) return;
1415 RegCloseKey( scsi_key
);
1417 RtlMultiByteToUnicodeN( dataW
, sizeof(dataW
), &sizeW
, driver
, strlen(driver
)+1);
1418 RegSetValueExW( port_key
, driverW
, 0, REG_SZ
, (const BYTE
*)dataW
, sizeW
);
1420 RegSetValueExW( port_key
, bus_time_scanW
, 0, REG_DWORD
, (const BYTE
*)&value
, sizeof(value
));
1424 snprintfW( dataW
, ARRAY_SIZE( dataW
), scsi_bus_keyW
, scsi_addr
->PathId
);
1425 if (RegCreateKeyExW( port_key
, dataW
, 0, NULL
, REG_OPTION_VOLATILE
, KEY_ALL_ACCESS
, NULL
, &bus_key
, NULL
)) return;
1426 RegCloseKey( port_key
);
1428 snprintfW( dataW
, ARRAY_SIZE( dataW
), init_id_keyW
, init_id
);
1429 if (RegCreateKeyExW( bus_key
, dataW
, 0, NULL
, REG_OPTION_VOLATILE
, KEY_ALL_ACCESS
, NULL
, &target_key
, NULL
)) return;
1430 RegCloseKey( target_key
);
1432 snprintfW( dataW
, ARRAY_SIZE( dataW
), target_id_keyW
, scsi_addr
->TargetId
);
1433 if (RegCreateKeyExW( bus_key
, dataW
, 0, NULL
, REG_OPTION_VOLATILE
, KEY_ALL_ACCESS
, NULL
, &target_key
, NULL
)) return;
1434 RegCloseKey( bus_key
);
1436 snprintfW( dataW
, ARRAY_SIZE( dataW
), lun_keyW
, scsi_addr
->Lun
);
1437 if (RegCreateKeyExW( target_key
, dataW
, 0, NULL
, REG_OPTION_VOLATILE
, KEY_ALL_ACCESS
, NULL
, &lun_key
, NULL
)) return;
1438 RegCloseKey( target_key
);
1442 case SCSI_DISK_PERIPHERAL
: data
= "DiskPeripheral"; break;
1443 case SCSI_TAPE_PERIPHERAL
: data
= "TapePeripheral"; break;
1444 case SCSI_PRINTER_PERIPHERAL
: data
= "PrinterPeripheral"; break;
1445 case SCSI_WORM_PERIPHERAL
: data
= "WormPeripheral"; break;
1446 case SCSI_CDROM_PERIPHERAL
: data
= "CdRomPeripheral"; break;
1447 case SCSI_SCANNER_PERIPHERAL
: data
= "ScannerPeripheral"; break;
1448 case SCSI_OPTICAL_DISK_PERIPHERAL
: data
= "OpticalDiskPeripheral"; break;
1449 case SCSI_MEDIUM_CHANGER_PERIPHERAL
: data
= "MediumChangerPeripheral"; break;
1450 case SCSI_COMMS_PERIPHERAL
: data
= "CommunicationsPeripheral"; break;
1451 case SCSI_ASC_GRAPHICS_PERIPHERAL
:
1452 case SCSI_ASC_GRAPHICS2_PERIPHERAL
: data
= "ASCPrePressGraphicsPeripheral"; break;
1453 case SCSI_ARRAY_PERIPHERAL
: data
= "ArrayPeripheral"; break;
1454 case SCSI_ENCLOSURE_PERIPHERAL
: data
= "EnclosurePeripheral"; break;
1455 case SCSI_REDUCED_DISK_PERIPHERAL
: data
= "RBCPeripheral"; break;
1456 case SCSI_CARD_READER_PERIPHERAL
: data
= "CardReaderPeripheral"; break;
1457 case SCSI_BRIDGE_PERIPHERAL
: data
= "BridgePeripheral"; break;
1458 case SCSI_OBJECT_STORAGE_PERIPHERAL
: /* Object-based storage devices */
1459 case SCSI_DRIVE_CONTROLLER_PERIPHERAL
: /* Automation/drive controllers */
1460 case SCSI_REDUCED_CDROM_PERIPHERAL
: /* Reduced-commands MM devices */
1461 case SCSI_PROCESSOR_PERIPHERAL
: /* Processor devices (considered to be "Other" by Windows) */
1462 default: data
= "OtherPeripheral"; break;
1464 RtlMultiByteToUnicodeN( dataW
, sizeof(dataW
), &sizeW
, data
, strlen(data
)+1);
1465 RegSetValueExW( lun_key
, typeW
, 0, REG_SZ
, (const BYTE
*)dataW
, sizeW
);
1467 RtlMultiByteToUnicodeN( dataW
, sizeof(dataW
), &sizeW
, model
, strlen(model
)+1);
1468 RegSetValueExW( lun_key
, identW
, 0, REG_SZ
, (const BYTE
*)dataW
, sizeW
);
1472 WCHAR
*buffer
= memchrW( dev
->Buffer
+1, '\\', dev
->Length
)+1;
1473 ULONG length
= dev
->Length
- (buffer
- dev
->Buffer
)*sizeof(WCHAR
);
1474 RegSetValueExW( lun_key
, devnameW
, 0, REG_SZ
, (const BYTE
*)buffer
, length
);
1476 else if (type
== SCSI_TAPE_PERIPHERAL
)
1478 snprintfW( dataW
, ARRAY_SIZE( dataW
), tapeW
, tape_no
++ );
1479 RegSetValueExW( lun_key
, devnameW
, 0, REG_SZ
, (const BYTE
*)dataW
, strlenW( dataW
) );
1482 RegCloseKey( lun_key
);
1485 /* set the "DeviceName" for a given SCSI address */
1486 void set_scsi_device_name( SCSI_ADDRESS
*scsi_addr
, const UNICODE_STRING
*dev
)
1492 lun_key
= get_scsi_device_lun_key( scsi_addr
);
1493 buffer
= memchrW( dev
->Buffer
+1, '\\', dev
->Length
)+1;
1494 length
= dev
->Length
- (buffer
- dev
->Buffer
)*sizeof(WCHAR
);
1495 RegSetValueExW( lun_key
, devnameW
, 0, REG_SZ
, (const BYTE
*)buffer
, length
);
1497 RegCloseKey( lun_key
);
1501 /* create a new disk volume */
1502 NTSTATUS
add_volume( const char *udi
, const char *device
, const char *mount_point
,
1503 enum device_type type
, const GUID
*guid
, const char *disk_serial
)
1505 struct volume
*volume
;
1506 NTSTATUS status
= STATUS_SUCCESS
;
1508 TRACE( "adding %s device %s mount %s type %u uuid %s\n", debugstr_a(udi
),
1509 debugstr_a(device
), debugstr_a(mount_point
), type
, debugstr_guid(guid
) );
1511 EnterCriticalSection( &device_section
);
1512 LIST_FOR_EACH_ENTRY( volume
, &volumes_list
, struct volume
, entry
)
1513 if (volume
->udi
&& !strcmp( udi
, volume
->udi
))
1515 grab_volume( volume
);
1519 /* udi not found, search for a non-dynamic volume */
1520 if ((volume
= find_matching_volume( udi
, device
, mount_point
, type
))) set_volume_udi( volume
, udi
);
1521 else status
= create_volume( udi
, type
, &volume
);
1524 if (!status
) status
= set_volume_info( volume
, NULL
, device
, mount_point
, type
, guid
, disk_serial
);
1525 if (volume
) release_volume( volume
);
1526 LeaveCriticalSection( &device_section
);
1530 /* remove a disk volume */
1531 NTSTATUS
remove_volume( const char *udi
)
1533 NTSTATUS status
= STATUS_NO_SUCH_DEVICE
;
1534 struct volume
*volume
;
1536 EnterCriticalSection( &device_section
);
1537 LIST_FOR_EACH_ENTRY( volume
, &volumes_list
, struct volume
, entry
)
1539 if (!volume
->udi
|| strcmp( udi
, volume
->udi
)) continue;
1540 set_volume_udi( volume
, NULL
);
1541 status
= STATUS_SUCCESS
;
1544 LeaveCriticalSection( &device_section
);
1549 /* create a new dos drive */
1550 NTSTATUS
add_dos_device( int letter
, const char *udi
, const char *device
,
1551 const char *mount_point
, enum device_type type
, const GUID
*guid
,
1552 UNICODE_STRING
*devname
)
1556 NTSTATUS status
= STATUS_SUCCESS
;
1557 struct dos_drive
*drive
, *next
;
1558 struct volume
*volume
;
1561 if (!(path
= get_dosdevices_path( &p
))) return STATUS_NO_MEMORY
;
1563 EnterCriticalSection( &device_section
);
1564 volume
= find_matching_volume( udi
, device
, mount_point
, type
);
1566 if (letter
== -1) /* auto-assign a letter */
1568 letter
= add_drive( device
, type
);
1571 status
= STATUS_OBJECT_NAME_COLLISION
;
1575 LIST_FOR_EACH_ENTRY_SAFE( drive
, next
, &drives_list
, struct dos_drive
, entry
)
1577 if (drive
->volume
->udi
&& !strcmp( udi
, drive
->volume
->udi
)) goto found
;
1578 if (drive
->drive
== letter
) delete_dos_device( drive
);
1581 else /* simply reset the device symlink */
1583 LIST_FOR_EACH_ENTRY( drive
, &drives_list
, struct dos_drive
, entry
)
1584 if (drive
->drive
== letter
) break;
1587 if (&drive
->entry
== &drives_list
) update_symlink( path
, device
, NULL
);
1590 update_symlink( path
, device
, drive
->volume
->device
->unix_device
);
1591 delete_dos_device( drive
);
1595 if ((status
= create_dos_device( volume
, udi
, letter
, type
, &drive
))) goto done
;
1598 if (!guid
&& !volume
) guid
= get_default_uuid( letter
);
1599 if (!volume
) volume
= grab_volume( drive
->volume
);
1600 set_drive_info( drive
, letter
, volume
);
1601 p
[0] = 'a' + drive
->drive
;
1603 update_symlink( path
, mount_point
, volume
->device
->unix_mount
);
1604 set_volume_info( volume
, drive
, device
, mount_point
, type
, guid
, NULL
);
1606 TRACE( "added device %c: udi %s for %s on %s type %u\n",
1607 'a' + drive
->drive
, wine_dbgstr_a(udi
), wine_dbgstr_a(device
),
1608 wine_dbgstr_a(mount_point
), type
);
1610 /* hack: force the drive type in the registry */
1611 if (!RegCreateKeyW( HKEY_LOCAL_MACHINE
, drives_keyW
, &hkey
))
1613 const WCHAR
*type_name
= drive_types
[type
];
1614 WCHAR name
[] = {'a',':',0};
1616 name
[0] += drive
->drive
;
1617 if (!type_name
[0] && type
== DEVICE_HARDDISK
) type_name
= drive_types
[DEVICE_FLOPPY
];
1619 RegSetValueExW( hkey
, name
, 0, REG_SZ
, (const BYTE
*)type_name
,
1620 (strlenW(type_name
) + 1) * sizeof(WCHAR
) );
1622 RegDeleteValueW( hkey
, name
);
1623 RegCloseKey( hkey
);
1626 if (udi
) notify
= drive
->drive
;
1628 if (devname
) *devname
= volume
->device
->name
;
1631 if (volume
) release_volume( volume
);
1632 LeaveCriticalSection( &device_section
);
1633 RtlFreeHeap( GetProcessHeap(), 0, path
);
1634 if (notify
!= -1) send_notify( notify
, DBT_DEVICEARRIVAL
);
1638 /* remove an existing dos drive, by letter or udi */
1639 NTSTATUS
remove_dos_device( int letter
, const char *udi
)
1641 NTSTATUS status
= STATUS_NO_SUCH_DEVICE
;
1643 struct dos_drive
*drive
;
1647 EnterCriticalSection( &device_section
);
1648 LIST_FOR_EACH_ENTRY( drive
, &drives_list
, struct dos_drive
, entry
)
1652 if (!drive
->volume
->udi
) continue;
1653 if (strcmp( udi
, drive
->volume
->udi
)) continue;
1654 set_volume_udi( drive
->volume
, NULL
);
1656 else if (drive
->drive
!= letter
) continue;
1658 if ((path
= get_dosdevices_path( &p
)))
1660 p
[0] = 'a' + drive
->drive
;
1663 RtlFreeHeap( GetProcessHeap(), 0, path
);
1666 /* clear the registry key too */
1667 if (!RegOpenKeyW( HKEY_LOCAL_MACHINE
, drives_keyW
, &hkey
))
1669 WCHAR name
[] = {'a',':',0};
1670 name
[0] += drive
->drive
;
1671 RegDeleteValueW( hkey
, name
);
1672 RegCloseKey( hkey
);
1675 if (udi
&& drive
->volume
->device
->unix_mount
) notify
= drive
->drive
;
1677 delete_dos_device( drive
);
1678 status
= STATUS_SUCCESS
;
1681 LeaveCriticalSection( &device_section
);
1682 if (notify
!= -1) send_notify( notify
, DBT_DEVICEREMOVECOMPLETE
);
1686 static enum mountmgr_fs_type
get_mountmgr_fs_type(enum fs_type fs_type
)
1690 case FS_ISO9660
: return MOUNTMGR_FS_TYPE_ISO9660
;
1691 case FS_UDF
: return MOUNTMGR_FS_TYPE_UDF
;
1692 case FS_FAT1216
: return MOUNTMGR_FS_TYPE_FAT
;
1693 case FS_FAT32
: return MOUNTMGR_FS_TYPE_FAT32
;
1694 default: return MOUNTMGR_FS_TYPE_NTFS
;
1698 /* query information about an existing dos drive, by letter or udi */
1699 static struct volume
*find_volume_by_letter( int letter
)
1701 struct volume
*volume
= NULL
;
1702 struct dos_drive
*drive
;
1704 LIST_FOR_EACH_ENTRY( drive
, &drives_list
, struct dos_drive
, entry
)
1706 if (drive
->drive
!= letter
) continue;
1707 volume
= grab_volume( drive
->volume
);
1708 TRACE( "found matching volume %s for drive letter %c:\n", debugstr_guid(&volume
->guid
),
1715 /* query information about an existing unix device, by dev_t */
1716 static struct volume
*find_volume_by_unixdev( ULONGLONG unix_dev
)
1718 struct volume
*volume
;
1721 LIST_FOR_EACH_ENTRY( volume
, &volumes_list
, struct volume
, entry
)
1723 if (!volume
->device
->unix_device
|| stat( volume
->device
->unix_device
, &st
) < 0
1724 || st
.st_rdev
!= unix_dev
)
1727 TRACE( "found matching volume %s\n", debugstr_guid(&volume
->guid
) );
1728 return grab_volume( volume
);
1733 /* implementation of IOCTL_MOUNTMGR_QUERY_UNIX_DRIVE */
1734 NTSTATUS
query_unix_drive( void *buff
, SIZE_T insize
, SIZE_T outsize
, IO_STATUS_BLOCK
*iosb
)
1736 const struct mountmgr_unix_drive
*input
= buff
;
1737 struct mountmgr_unix_drive
*output
= NULL
;
1738 char *device
, *mount_point
;
1739 int letter
= tolowerW( input
->letter
);
1740 DWORD size
, type
= DEVICE_UNKNOWN
, serial
;
1741 NTSTATUS status
= STATUS_SUCCESS
;
1742 enum mountmgr_fs_type fs_type
;
1743 enum device_type device_type
;
1744 struct volume
*volume
;
1748 if (letter
&& (letter
< 'a' || letter
> 'z')) return STATUS_INVALID_PARAMETER
;
1750 EnterCriticalSection( &device_section
);
1752 volume
= find_volume_by_letter( letter
- 'a' );
1754 volume
= find_volume_by_unixdev( input
->unix_dev
);
1757 device_type
= volume
->device
->type
;
1758 fs_type
= get_mountmgr_fs_type( volume
->fs_type
);
1759 serial
= volume
->serial
;
1760 device
= strdupA( volume
->device
->unix_device
);
1761 mount_point
= strdupA( volume
->device
->unix_mount
);
1762 label
= strdupW( volume
->label
);
1763 release_volume( volume
);
1765 LeaveCriticalSection( &device_section
);
1768 return STATUS_NO_SUCH_DEVICE
;
1770 switch (device_type
)
1772 case DEVICE_UNKNOWN
: type
= DRIVE_UNKNOWN
; break;
1773 case DEVICE_HARDDISK
: type
= DRIVE_REMOVABLE
; break;
1774 case DEVICE_HARDDISK_VOL
: type
= DRIVE_FIXED
; break;
1775 case DEVICE_FLOPPY
: type
= DRIVE_REMOVABLE
; break;
1776 case DEVICE_CDROM
: type
= DRIVE_CDROM
; break;
1777 case DEVICE_DVD
: type
= DRIVE_CDROM
; break;
1778 case DEVICE_NETWORK
: type
= DRIVE_REMOTE
; break;
1779 case DEVICE_RAMDISK
: type
= DRIVE_RAMDISK
; break;
1782 size
= sizeof(*output
);
1783 if (label
) size
+= (strlenW(label
) + 1) * sizeof(WCHAR
);
1784 if (device
) size
+= strlen(device
) + 1;
1785 if (mount_point
) size
+= strlen(mount_point
) + 1;
1789 output
->size
= size
;
1790 output
->letter
= letter
;
1791 output
->type
= type
;
1792 output
->fs_type
= fs_type
;
1793 output
->serial
= serial
;
1794 output
->mount_point_offset
= 0;
1795 output
->device_offset
= 0;
1796 output
->label_offset
= 0;
1798 ptr
= (char *)(output
+ 1);
1800 if (label
&& ptr
+ (strlenW(label
) + 1) * sizeof(WCHAR
) - (char *)output
<= outsize
)
1802 output
->label_offset
= ptr
- (char *)output
;
1803 strcpyW( (WCHAR
*)ptr
, label
);
1804 ptr
+= (strlenW(label
) + 1) * sizeof(WCHAR
);
1806 if (mount_point
&& ptr
+ strlen(mount_point
) + 1 - (char *)output
<= outsize
)
1808 output
->mount_point_offset
= ptr
- (char *)output
;
1809 strcpy( ptr
, mount_point
);
1810 ptr
+= strlen(ptr
) + 1;
1812 if (device
&& ptr
+ strlen(device
) + 1 - (char *)output
<= outsize
)
1814 output
->device_offset
= ptr
- (char *)output
;
1815 strcpy( ptr
, device
);
1816 ptr
+= strlen(ptr
) + 1;
1819 TRACE( "returning %c: dev %s mount %s type %u\n",
1820 letter
, debugstr_a(device
), debugstr_a(mount_point
), type
);
1822 iosb
->Information
= ptr
- (char *)output
;
1823 if (size
> outsize
) status
= STATUS_BUFFER_OVERFLOW
;
1825 RtlFreeHeap( GetProcessHeap(), 0, device
);
1826 RtlFreeHeap( GetProcessHeap(), 0, mount_point
);
1827 RtlFreeHeap( GetProcessHeap(), 0, label
);
1831 static NTSTATUS
query_property( struct disk_device
*device
, IRP
*irp
)
1833 IO_STACK_LOCATION
*irpsp
= IoGetCurrentIrpStackLocation( irp
);
1834 STORAGE_PROPERTY_QUERY
*query
= irp
->AssociatedIrp
.SystemBuffer
;
1837 if (!irp
->AssociatedIrp
.SystemBuffer
1838 || irpsp
->Parameters
.DeviceIoControl
.InputBufferLength
< sizeof(STORAGE_PROPERTY_QUERY
))
1840 return STATUS_INVALID_PARAMETER
;
1843 /* Try to persuade application not to check property */
1844 if (query
->QueryType
== PropertyExistsQuery
)
1846 return STATUS_NOT_SUPPORTED
;
1849 switch (query
->PropertyId
)
1851 case StorageDeviceProperty
:
1853 STORAGE_DEVICE_DESCRIPTOR
*descriptor
;
1854 DWORD len
= sizeof(*descriptor
);
1856 if (device
->serial
) len
+= strlen( device
->serial
) + 1;
1858 if (irpsp
->Parameters
.DeviceIoControl
.OutputBufferLength
< sizeof(STORAGE_DESCRIPTOR_HEADER
))
1859 status
= STATUS_INVALID_PARAMETER
;
1860 else if (irpsp
->Parameters
.DeviceIoControl
.OutputBufferLength
< len
)
1862 descriptor
= irp
->AssociatedIrp
.SystemBuffer
;
1863 descriptor
->Version
= sizeof(STORAGE_DEVICE_DESCRIPTOR
);
1864 descriptor
->Size
= len
;
1865 irp
->IoStatus
.Information
= sizeof(STORAGE_DESCRIPTOR_HEADER
);
1866 status
= STATUS_SUCCESS
;
1870 FIXME( "Faking StorageDeviceProperty data\n" );
1872 memset( irp
->AssociatedIrp
.SystemBuffer
, 0, irpsp
->Parameters
.DeviceIoControl
.OutputBufferLength
);
1873 descriptor
= irp
->AssociatedIrp
.SystemBuffer
;
1874 descriptor
->Version
= sizeof(STORAGE_DEVICE_DESCRIPTOR
);
1875 descriptor
->Size
= len
;
1876 descriptor
->DeviceType
= FILE_DEVICE_DISK
;
1877 descriptor
->DeviceTypeModifier
= 0;
1878 descriptor
->RemovableMedia
= FALSE
;
1879 descriptor
->CommandQueueing
= FALSE
;
1880 descriptor
->VendorIdOffset
= 0;
1881 descriptor
->ProductIdOffset
= 0;
1882 descriptor
->ProductRevisionOffset
= 0;
1883 descriptor
->BusType
= BusTypeScsi
;
1884 descriptor
->RawPropertiesLength
= 0;
1885 if (!device
->serial
) descriptor
->SerialNumberOffset
= 0;
1888 descriptor
->SerialNumberOffset
= sizeof(*descriptor
);
1889 strcpy( (char *)descriptor
+ descriptor
->SerialNumberOffset
, device
->serial
);
1891 irp
->IoStatus
.Information
= len
;
1892 status
= STATUS_SUCCESS
;
1898 FIXME( "Unsupported property %#x\n", query
->PropertyId
);
1899 status
= STATUS_NOT_SUPPORTED
;
1905 static NTSTATUS WINAPI
harddisk_query_volume( DEVICE_OBJECT
*device
, IRP
*irp
)
1907 IO_STACK_LOCATION
*irpsp
= IoGetCurrentIrpStackLocation( irp
);
1908 int info_class
= irpsp
->Parameters
.QueryVolume
.FsInformationClass
;
1909 ULONG length
= irpsp
->Parameters
.QueryVolume
.Length
;
1910 struct disk_device
*dev
= device
->DeviceExtension
;
1911 PIO_STATUS_BLOCK io
= &irp
->IoStatus
;
1912 struct volume
*volume
;
1915 TRACE( "volume query %x length %u\n", info_class
, length
);
1917 EnterCriticalSection( &device_section
);
1918 volume
= dev
->volume
;
1921 status
= STATUS_BAD_DEVICE_TYPE
;
1927 case FileFsVolumeInformation
:
1930 FILE_FS_VOLUME_INFORMATION
*info
= irp
->AssociatedIrp
.SystemBuffer
;
1932 if (length
< sizeof(FILE_FS_VOLUME_INFORMATION
))
1934 status
= STATUS_INFO_LENGTH_MISMATCH
;
1938 info
->VolumeCreationTime
.QuadPart
= 0; /* FIXME */
1939 info
->VolumeSerialNumber
= volume
->serial
;
1940 info
->VolumeLabelLength
= min( strlenW(volume
->label
) * sizeof(WCHAR
),
1941 length
- offsetof( FILE_FS_VOLUME_INFORMATION
, VolumeLabel
) );
1942 info
->SupportsObjects
= (get_mountmgr_fs_type(volume
->fs_type
) == MOUNTMGR_FS_TYPE_NTFS
);
1943 memcpy( info
->VolumeLabel
, volume
->label
, info
->VolumeLabelLength
);
1945 io
->Information
= offsetof( FILE_FS_VOLUME_INFORMATION
, VolumeLabel
) + info
->VolumeLabelLength
;
1946 status
= STATUS_SUCCESS
;
1949 case FileFsAttributeInformation
:
1951 static const WCHAR fatW
[] = {'F','A','T'};
1952 static const WCHAR fat32W
[] = {'F','A','T','3','2'};
1953 static const WCHAR ntfsW
[] = {'N','T','F','S'};
1954 static const WCHAR cdfsW
[] = {'C','D','F','S'};
1955 static const WCHAR udfW
[] = {'U','D','F'};
1957 FILE_FS_ATTRIBUTE_INFORMATION
*info
= irp
->AssociatedIrp
.SystemBuffer
;
1958 enum mountmgr_fs_type fs_type
= get_mountmgr_fs_type(volume
->fs_type
);
1960 if (length
< sizeof(FILE_FS_ATTRIBUTE_INFORMATION
))
1962 status
= STATUS_INFO_LENGTH_MISMATCH
;
1968 case MOUNTMGR_FS_TYPE_ISO9660
:
1969 info
->FileSystemAttributes
= FILE_READ_ONLY_VOLUME
;
1970 info
->MaximumComponentNameLength
= 221;
1971 info
->FileSystemNameLength
= min( sizeof(cdfsW
), length
- offsetof( FILE_FS_ATTRIBUTE_INFORMATION
, FileSystemName
) );
1972 memcpy(info
->FileSystemName
, cdfsW
, info
->FileSystemNameLength
);
1974 case MOUNTMGR_FS_TYPE_UDF
:
1975 info
->FileSystemAttributes
= FILE_READ_ONLY_VOLUME
| FILE_UNICODE_ON_DISK
| FILE_CASE_SENSITIVE_SEARCH
;
1976 info
->MaximumComponentNameLength
= 255;
1977 info
->FileSystemNameLength
= min( sizeof(udfW
), length
- offsetof( FILE_FS_ATTRIBUTE_INFORMATION
, FileSystemName
) );
1978 memcpy(info
->FileSystemName
, udfW
, info
->FileSystemNameLength
);
1980 case MOUNTMGR_FS_TYPE_FAT
:
1981 info
->FileSystemAttributes
= FILE_CASE_PRESERVED_NAMES
; /* FIXME */
1982 info
->MaximumComponentNameLength
= 255;
1983 info
->FileSystemNameLength
= min( sizeof(fatW
), length
- offsetof( FILE_FS_ATTRIBUTE_INFORMATION
, FileSystemName
) );
1984 memcpy(info
->FileSystemName
, fatW
, info
->FileSystemNameLength
);
1986 case MOUNTMGR_FS_TYPE_FAT32
:
1987 info
->FileSystemAttributes
= FILE_CASE_PRESERVED_NAMES
; /* FIXME */
1988 info
->MaximumComponentNameLength
= 255;
1989 info
->FileSystemNameLength
= min( sizeof(fat32W
), length
- offsetof( FILE_FS_ATTRIBUTE_INFORMATION
, FileSystemName
) );
1990 memcpy(info
->FileSystemName
, fat32W
, info
->FileSystemNameLength
);
1993 info
->FileSystemAttributes
= FILE_CASE_PRESERVED_NAMES
| FILE_PERSISTENT_ACLS
;
1994 info
->MaximumComponentNameLength
= 255;
1995 info
->FileSystemNameLength
= min( sizeof(ntfsW
), length
- offsetof( FILE_FS_ATTRIBUTE_INFORMATION
, FileSystemName
) );
1996 memcpy(info
->FileSystemName
, ntfsW
, info
->FileSystemNameLength
);
2000 io
->Information
= offsetof( FILE_FS_ATTRIBUTE_INFORMATION
, FileSystemName
) + info
->FileSystemNameLength
;
2001 status
= STATUS_SUCCESS
;
2005 FIXME("Unsupported volume query %x\n", irpsp
->Parameters
.QueryVolume
.FsInformationClass
);
2006 status
= STATUS_NOT_SUPPORTED
;
2011 io
->u
.Status
= status
;
2012 LeaveCriticalSection( &device_section
);
2013 IoCompleteRequest( irp
, IO_NO_INCREMENT
);
2017 /* handler for ioctls on the harddisk device */
2018 static NTSTATUS WINAPI
harddisk_ioctl( DEVICE_OBJECT
*device
, IRP
*irp
)
2020 IO_STACK_LOCATION
*irpsp
= IoGetCurrentIrpStackLocation( irp
);
2021 struct disk_device
*dev
= device
->DeviceExtension
;
2024 TRACE( "ioctl %x insize %u outsize %u\n",
2025 irpsp
->Parameters
.DeviceIoControl
.IoControlCode
,
2026 irpsp
->Parameters
.DeviceIoControl
.InputBufferLength
,
2027 irpsp
->Parameters
.DeviceIoControl
.OutputBufferLength
);
2029 EnterCriticalSection( &device_section
);
2031 switch(irpsp
->Parameters
.DeviceIoControl
.IoControlCode
)
2033 case IOCTL_DISK_GET_DRIVE_GEOMETRY
:
2036 DWORD len
= min( sizeof(info
), irpsp
->Parameters
.DeviceIoControl
.OutputBufferLength
);
2038 info
.Cylinders
.QuadPart
= 10000;
2039 info
.MediaType
= (dev
->devnum
.DeviceType
== FILE_DEVICE_DISK
) ? FixedMedia
: RemovableMedia
;
2040 info
.TracksPerCylinder
= 255;
2041 info
.SectorsPerTrack
= 63;
2042 info
.BytesPerSector
= 512;
2043 memcpy( irp
->AssociatedIrp
.SystemBuffer
, &info
, len
);
2044 irp
->IoStatus
.Information
= len
;
2045 status
= STATUS_SUCCESS
;
2048 case IOCTL_DISK_GET_DRIVE_GEOMETRY_EX
:
2050 DISK_GEOMETRY_EX info
;
2051 DWORD len
= min( sizeof(info
), irpsp
->Parameters
.DeviceIoControl
.OutputBufferLength
);
2053 FIXME("The DISK_PARTITION_INFO and DISK_DETECTION_INFO structures will not be filled\n");
2055 info
.Geometry
.Cylinders
.QuadPart
= 10000;
2056 info
.Geometry
.MediaType
= (dev
->devnum
.DeviceType
== FILE_DEVICE_DISK
) ? FixedMedia
: RemovableMedia
;
2057 info
.Geometry
.TracksPerCylinder
= 255;
2058 info
.Geometry
.SectorsPerTrack
= 63;
2059 info
.Geometry
.BytesPerSector
= 512;
2060 info
.DiskSize
.QuadPart
= info
.Geometry
.Cylinders
.QuadPart
* info
.Geometry
.TracksPerCylinder
*
2061 info
.Geometry
.SectorsPerTrack
* info
.Geometry
.BytesPerSector
;
2063 memcpy( irp
->AssociatedIrp
.SystemBuffer
, &info
, len
);
2064 irp
->IoStatus
.Information
= len
;
2065 status
= STATUS_SUCCESS
;
2068 case IOCTL_STORAGE_GET_DEVICE_NUMBER
:
2070 DWORD len
= min( sizeof(dev
->devnum
), irpsp
->Parameters
.DeviceIoControl
.OutputBufferLength
);
2072 memcpy( irp
->AssociatedIrp
.SystemBuffer
, &dev
->devnum
, len
);
2073 irp
->IoStatus
.Information
= len
;
2074 status
= STATUS_SUCCESS
;
2077 case IOCTL_CDROM_READ_TOC
:
2078 status
= STATUS_INVALID_DEVICE_REQUEST
;
2080 case IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS
:
2082 DWORD len
= min( 32, irpsp
->Parameters
.DeviceIoControl
.OutputBufferLength
);
2084 FIXME( "returning zero-filled buffer for IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS\n" );
2085 memset( irp
->AssociatedIrp
.SystemBuffer
, 0, len
);
2086 irp
->IoStatus
.Information
= len
;
2087 status
= STATUS_SUCCESS
;
2090 case IOCTL_STORAGE_QUERY_PROPERTY
:
2091 status
= query_property( dev
, irp
);
2095 ULONG code
= irpsp
->Parameters
.DeviceIoControl
.IoControlCode
;
2096 FIXME("Unsupported ioctl %x (device=%x access=%x func=%x method=%x)\n",
2097 code
, code
>> 16, (code
>> 14) & 3, (code
>> 2) & 0xfff, code
& 3);
2098 status
= STATUS_NOT_SUPPORTED
;
2103 irp
->IoStatus
.u
.Status
= status
;
2104 LeaveCriticalSection( &device_section
);
2105 IoCompleteRequest( irp
, IO_NO_INCREMENT
);
2109 /* driver entry point for the harddisk driver */
2110 NTSTATUS WINAPI
harddisk_driver_entry( DRIVER_OBJECT
*driver
, UNICODE_STRING
*path
)
2112 struct disk_device
*device
;
2114 harddisk_driver
= driver
;
2115 driver
->MajorFunction
[IRP_MJ_DEVICE_CONTROL
] = harddisk_ioctl
;
2116 driver
->MajorFunction
[IRP_MJ_QUERY_VOLUME_INFORMATION
] = harddisk_query_volume
;
2118 /* create a harddisk0 device that isn't assigned to any drive */
2119 create_disk_device( DEVICE_HARDDISK
, &device
, NULL
);
2121 create_drive_devices();
2123 return STATUS_SUCCESS
;
2127 /* create a serial or parallel port */
2128 static BOOL
create_port_device( DRIVER_OBJECT
*driver
, int n
, const char *unix_path
,
2129 const char *dosdevices_path
, HKEY windows_ports_key
)
2131 static const WCHAR comW
[] = {'C','O','M','%','u',0};
2132 static const WCHAR lptW
[] = {'L','P','T','%','u',0};
2133 static const WCHAR device_serialW
[] = {'\\','D','e','v','i','c','e','\\','S','e','r','i','a','l','%','u',0};
2134 static const WCHAR device_parallelW
[] = {'\\','D','e','v','i','c','e','\\','P','a','r','a','l','l','e','l','%','u',0};
2135 static const WCHAR dosdevices_comW
[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\','C','O','M','%','u',0};
2136 static const WCHAR dosdevices_auxW
[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\','A','U','X',0};
2137 static const WCHAR dosdevices_lptW
[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\','L','P','T','%','u',0};
2138 static const WCHAR dosdevices_prnW
[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\','P','R','N',0};
2139 const WCHAR
*dos_name_format
, *nt_name_format
, *reg_value_format
, *symlink_format
, *default_device
;
2140 WCHAR dos_name
[7], reg_value
[256], nt_buffer
[32], symlink_buffer
[32];
2141 UNICODE_STRING nt_name
, symlink_name
, default_name
;
2142 DEVICE_OBJECT
*dev_obj
;
2145 if (driver
== serial_driver
)
2147 dos_name_format
= comW
;
2148 nt_name_format
= device_serialW
;
2149 reg_value_format
= comW
;
2150 symlink_format
= dosdevices_comW
;
2151 default_device
= dosdevices_auxW
;
2155 dos_name_format
= lptW
;
2156 nt_name_format
= device_parallelW
;
2157 reg_value_format
= dosdevices_lptW
;
2158 symlink_format
= dosdevices_lptW
;
2159 default_device
= dosdevices_prnW
;
2162 sprintfW( dos_name
, dos_name_format
, n
);
2164 /* create DOS device */
2165 unlink( dosdevices_path
);
2166 if (symlink( unix_path
, dosdevices_path
) != 0)
2169 /* create NT device */
2170 sprintfW( nt_buffer
, nt_name_format
, n
- 1 );
2171 RtlInitUnicodeString( &nt_name
, nt_buffer
);
2172 status
= IoCreateDevice( driver
, 0, &nt_name
, 0, 0, FALSE
, &dev_obj
);
2173 if (status
!= STATUS_SUCCESS
)
2175 FIXME( "IoCreateDevice %s got %x\n", debugstr_w(nt_name
.Buffer
), status
);
2178 sprintfW( symlink_buffer
, symlink_format
, n
);
2179 RtlInitUnicodeString( &symlink_name
, symlink_buffer
);
2180 IoCreateSymbolicLink( &symlink_name
, &nt_name
);
2183 RtlInitUnicodeString( &default_name
, default_device
);
2184 IoCreateSymbolicLink( &default_name
, &symlink_name
);
2187 /* TODO: store information about the Unix device in the NT device */
2189 /* create registry entry */
2190 sprintfW( reg_value
, reg_value_format
, n
);
2191 RegSetValueExW( windows_ports_key
, nt_name
.Buffer
, 0, REG_SZ
,
2192 (BYTE
*)reg_value
, (strlenW( reg_value
) + 1) * sizeof(WCHAR
) );
2197 /* find and create serial or parallel ports */
2198 static void create_port_devices( DRIVER_OBJECT
*driver
)
2200 static const char *serial_search_paths
[] = {
2205 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
2207 #elif defined(__DragonFly__)
2212 static const char *parallel_search_paths
[] = {
2218 static const WCHAR serialcomm_keyW
[] = {'H','A','R','D','W','A','R','E','\\',
2219 'D','E','V','I','C','E','M','A','P','\\',
2220 'S','E','R','I','A','L','C','O','M','M',0};
2221 static const WCHAR parallel_ports_keyW
[] = {'H','A','R','D','W','A','R','E','\\',
2222 'D','E','V','I','C','E','M','A','P','\\',
2223 'P','A','R','A','L','L','E','L',' ','P','O','R','T','S',0};
2224 static const WCHAR comW
[] = {'C','O','M'};
2225 static const WCHAR lptW
[] = {'L','P','T'};
2226 const char **search_paths
;
2227 const WCHAR
*windows_ports_key_name
;
2228 char *dosdevices_path
, *p
;
2229 HKEY wine_ports_key
= NULL
, windows_ports_key
= NULL
;
2230 char unix_path
[256];
2231 const WCHAR
*port_prefix
;
2232 WCHAR reg_value
[256];
2233 BOOL used
[MAX_PORTS
];
2235 DWORD port_len
, type
, size
;
2238 if (!(dosdevices_path
= get_dosdevices_path( &p
)))
2241 if (driver
== serial_driver
)
2246 search_paths
= serial_search_paths
;
2247 windows_ports_key_name
= serialcomm_keyW
;
2255 search_paths
= parallel_search_paths
;
2256 windows_ports_key_name
= parallel_ports_keyW
;
2261 /* @@ Wine registry key: HKLM\Software\Wine\Ports */
2263 RegCreateKeyExW( HKEY_LOCAL_MACHINE
, ports_keyW
, 0, NULL
, 0,
2264 KEY_QUERY_VALUE
, NULL
, &wine_ports_key
, NULL
);
2265 RegCreateKeyExW( HKEY_LOCAL_MACHINE
, windows_ports_key_name
, 0, NULL
, REG_OPTION_VOLATILE
,
2266 KEY_ALL_ACCESS
, NULL
, &windows_ports_key
, NULL
);
2268 /* add user-defined serial ports */
2269 memset(used
, 0, sizeof(used
));
2272 port_len
= ARRAY_SIZE(port
);
2273 size
= sizeof(reg_value
);
2274 if (RegEnumValueW( wine_ports_key
, i
, port
, &port_len
, NULL
,
2275 &type
, (BYTE
*)reg_value
, &size
) != ERROR_SUCCESS
)
2277 if (type
!= REG_SZ
|| strncmpiW( port
, port_prefix
, 3 ))
2280 n
= atoiW( port
+ 3 );
2281 if (n
< 1 || n
>= MAX_PORTS
)
2284 if (!WideCharToMultiByte( CP_UNIXCP
, WC_ERR_INVALID_CHARS
, reg_value
, size
/sizeof(WCHAR
),
2285 unix_path
, sizeof(unix_path
), NULL
, NULL
))
2289 sprintf( p
, "%u", n
);
2290 create_port_device( driver
, n
, unix_path
, dosdevices_path
, windows_ports_key
);
2293 /* look for ports in the usual places */
2295 while (n
<= MAX_PORTS
&& used
[n
- 1]) n
++;
2296 for (i
= 0; search_paths
[i
]; i
++)
2298 for (j
= 0; n
<= MAX_PORTS
; j
++)
2300 sprintf( unix_path
, search_paths
[i
], j
);
2301 if (access( unix_path
, F_OK
) != 0)
2304 sprintf( p
, "%u", n
);
2305 create_port_device( driver
, n
, unix_path
, dosdevices_path
, windows_ports_key
);
2307 while (n
<= MAX_PORTS
&& used
[n
- 1]) n
++;
2311 RegCloseKey( wine_ports_key
);
2312 RegCloseKey( windows_ports_key
);
2313 HeapFree( GetProcessHeap(), 0, dosdevices_path
);
2316 /* driver entry point for the serial port driver */
2317 NTSTATUS WINAPI
serial_driver_entry( DRIVER_OBJECT
*driver
, UNICODE_STRING
*path
)
2319 serial_driver
= driver
;
2320 /* TODO: fill in driver->MajorFunction */
2322 create_port_devices( driver
);
2324 return STATUS_SUCCESS
;
2327 /* driver entry point for the parallel port driver */
2328 NTSTATUS WINAPI
parallel_driver_entry( DRIVER_OBJECT
*driver
, UNICODE_STRING
*path
)
2330 parallel_driver
= driver
;
2331 /* TODO: fill in driver->MajorFunction */
2333 create_port_devices( driver
);
2335 return STATUS_SUCCESS
;