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"
36 #include "wine/library.h"
37 #include "wine/list.h"
38 #include "wine/debug.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(mountmgr
);
49 static struct list drives_list
= LIST_INIT(drives_list
);
51 static char *get_dosdevices_path(void)
53 const char *config_dir
= wine_get_config_dir();
54 size_t len
= strlen(config_dir
) + sizeof("/dosdevices/a::");
55 char *path
= HeapAlloc( GetProcessHeap(), 0, len
);
58 strcpy( path
, config_dir
);
59 strcat( path
, "/dosdevices/a::" );
64 /* send notification about a change to a given drive */
65 static void send_notify( int drive
, int code
)
68 DEV_BROADCAST_VOLUME info
;
70 info
.dbcv_size
= sizeof(info
);
71 info
.dbcv_devicetype
= DBT_DEVTYP_VOLUME
;
72 info
.dbcv_reserved
= 0;
73 info
.dbcv_unitmask
= 1 << drive
;
74 info
.dbcv_flags
= DBTF_MEDIA
;
75 result
= BroadcastSystemMessageW( BSF_FORCEIFHUNG
|BSF_QUERY
, NULL
,
76 WM_DEVICECHANGE
, code
, (LPARAM
)&info
);
79 static inline int is_valid_device( struct stat
*st
)
81 #if defined(linux) || defined(__sun__)
82 return S_ISBLK( st
->st_mode
);
84 /* disks are char devices on *BSD */
85 return S_ISCHR( st
->st_mode
);
89 /* find or create a DOS drive for the corresponding device */
90 static int add_drive( const char *device
, const char *type
)
94 struct stat dev_st
, drive_st
;
95 int drive
, first
, last
, avail
= 0;
97 if (stat( device
, &dev_st
) == -1 || !is_valid_device( &dev_st
)) return -1;
99 if (!(path
= get_dosdevices_path())) return -1;
100 p
= path
+ strlen(path
) - 3;
102 memset( in_use
, 0, sizeof(in_use
) );
106 if (type
&& !strcmp( type
, "floppy" ))
115 for (drive
= first
; drive
< last
; drive
++)
117 if (in_use
[drive
]) continue; /* already checked */
119 if (stat( path
, &drive_st
) == -1)
121 if (lstat( path
, &drive_st
) == -1 && errno
== ENOENT
) /* this is a candidate */
126 /* if mount point symlink doesn't exist either, it's available */
127 if (lstat( path
, &drive_st
) == -1 && errno
== ENOENT
) avail
= drive
;
131 else in_use
[drive
] = 1;
136 if (!is_valid_device( &drive_st
)) continue;
137 if (dev_st
.st_rdev
== drive_st
.st_rdev
) goto done
;
142 /* try to use the one we found */
145 if (symlink( device
, path
) != -1) goto done
;
146 /* failed, retry the search */
152 HeapFree( GetProcessHeap(), 0, path
);
156 static BOOL
set_mount_point( struct dos_drive
*drive
, const char *mount_point
)
159 struct stat path_st
, mnt_st
;
160 BOOL modified
= FALSE
;
162 if (drive
->drive
== -1) return FALSE
;
163 if (!(path
= get_dosdevices_path())) return FALSE
;
164 p
= path
+ strlen(path
) - 3;
165 *p
= 'a' + drive
->drive
;
170 /* try to avoid unlinking if already set correctly */
171 if (stat( path
, &path_st
) == -1 || stat( mount_point
, &mnt_st
) == -1 ||
172 path_st
.st_dev
!= mnt_st
.st_dev
|| path_st
.st_ino
!= mnt_st
.st_ino
)
175 symlink( mount_point
, path
);
181 if (unlink( path
) != -1) modified
= TRUE
;
184 HeapFree( GetProcessHeap(), 0, path
);
188 BOOL
add_dos_device( const char *udi
, const char *device
,
189 const char *mount_point
, const char *type
)
191 struct dos_drive
*drive
;
193 /* first check if it already exists */
194 LIST_FOR_EACH_ENTRY( drive
, &drives_list
, struct dos_drive
, entry
)
196 if (!strcmp( udi
, drive
->udi
)) goto found
;
199 if (!(drive
= HeapAlloc( GetProcessHeap(), 0, sizeof(*drive
) ))) return FALSE
;
200 if (!(drive
->udi
= HeapAlloc( GetProcessHeap(), 0, strlen(udi
)+1 )))
202 HeapFree( GetProcessHeap(), 0, drive
);
205 strcpy( drive
->udi
, udi
);
206 list_add_tail( &drives_list
, &drive
->entry
);
209 drive
->drive
= add_drive( device
, type
);
210 if (drive
->drive
!= -1)
214 set_mount_point( drive
, mount_point
);
216 TRACE( "added device %c: udi %s for %s on %s type %s\n",
217 'a' + drive
->drive
, wine_dbgstr_a(udi
), wine_dbgstr_a(device
),
218 wine_dbgstr_a(mount_point
), wine_dbgstr_a(type
) );
220 /* hack: force the drive type in the registry */
221 if (!RegCreateKeyA( HKEY_LOCAL_MACHINE
, "Software\\Wine\\Drives", &hkey
))
224 name
[0] += drive
->drive
;
225 if (!type
|| strcmp( type
, "cdrom" )) type
= "floppy"; /* FIXME: default to floppy */
226 RegSetValueExA( hkey
, name
, 0, REG_SZ
, (const BYTE
*)type
, strlen(type
) + 1 );
230 send_notify( drive
->drive
, DBT_DEVICEARRIVAL
);
235 BOOL
remove_dos_device( const char *udi
)
238 struct dos_drive
*drive
;
240 LIST_FOR_EACH_ENTRY( drive
, &drives_list
, struct dos_drive
, entry
)
242 if (strcmp( udi
, drive
->udi
)) continue;
244 if (drive
->drive
!= -1)
246 BOOL modified
= set_mount_point( drive
, "" );
248 /* clear the registry key too */
249 if (!RegOpenKeyA( HKEY_LOCAL_MACHINE
, "Software\\Wine\\Drives", &hkey
))
252 name
[0] += drive
->drive
;
253 RegDeleteValueA( hkey
, name
);
257 if (modified
) send_notify( drive
->drive
, DBT_DEVICEREMOVECOMPLETE
);
260 list_remove( &drive
->entry
);
261 HeapFree( GetProcessHeap(), 0, drive
->udi
);
262 HeapFree( GetProcessHeap(), 0, drive
);