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/unicode.h"
39 #include "wine/debug.h"
41 WINE_DEFAULT_DEBUG_CHANNEL(mountmgr
);
43 static const WCHAR drive_types
[][8] =
45 { 0 }, /* DRIVE_UNKNOWN */
46 { 0 }, /* DRIVE_NO_ROOT_DIR */
47 {'f','l','o','p','p','y',0}, /* DRIVE_REMOVABLE */
48 {'h','d',0}, /* DRIVE_FIXED */
49 {'n','e','t','w','o','r','k',0}, /* DRIVE_REMOTE */
50 {'c','d','r','o','m',0}, /* DRIVE_CDROM */
51 {'r','a','m','d','i','s','k',0} /* DRIVE_RAMDISK */
61 static struct list drives_list
= LIST_INIT(drives_list
);
63 static char *get_dosdevices_path(void)
65 const char *config_dir
= wine_get_config_dir();
66 size_t len
= strlen(config_dir
) + sizeof("/dosdevices/a::");
67 char *path
= HeapAlloc( GetProcessHeap(), 0, len
);
70 strcpy( path
, config_dir
);
71 strcat( path
, "/dosdevices/a::" );
76 /* send notification about a change to a given drive */
77 static void send_notify( int drive
, int code
)
80 DEV_BROADCAST_VOLUME info
;
82 info
.dbcv_size
= sizeof(info
);
83 info
.dbcv_devicetype
= DBT_DEVTYP_VOLUME
;
84 info
.dbcv_reserved
= 0;
85 info
.dbcv_unitmask
= 1 << drive
;
86 info
.dbcv_flags
= DBTF_MEDIA
;
87 result
= BroadcastSystemMessageW( BSF_FORCEIFHUNG
|BSF_QUERY
, NULL
,
88 WM_DEVICECHANGE
, code
, (LPARAM
)&info
);
91 static inline int is_valid_device( struct stat
*st
)
93 #if defined(linux) || defined(__sun__)
94 return S_ISBLK( st
->st_mode
);
96 /* disks are char devices on *BSD */
97 return S_ISCHR( st
->st_mode
);
101 /* find or create a DOS drive for the corresponding device */
102 static int add_drive( const char *device
, DWORD type
)
106 struct stat dev_st
, drive_st
;
107 int drive
, first
, last
, avail
= 0;
109 if (stat( device
, &dev_st
) == -1 || !is_valid_device( &dev_st
)) return -1;
111 if (!(path
= get_dosdevices_path())) return -1;
112 p
= path
+ strlen(path
) - 3;
114 memset( in_use
, 0, sizeof(in_use
) );
118 if (type
== DRIVE_REMOVABLE
)
127 for (drive
= first
; drive
< last
; drive
++)
129 if (in_use
[drive
]) continue; /* already checked */
131 if (stat( path
, &drive_st
) == -1)
133 if (lstat( path
, &drive_st
) == -1 && errno
== ENOENT
) /* this is a candidate */
138 /* if mount point symlink doesn't exist either, it's available */
139 if (lstat( path
, &drive_st
) == -1 && errno
== ENOENT
) avail
= drive
;
143 else in_use
[drive
] = 1;
148 if (!is_valid_device( &drive_st
)) continue;
149 if (dev_st
.st_rdev
== drive_st
.st_rdev
) goto done
;
154 /* try to use the one we found */
157 if (symlink( device
, path
) != -1) goto done
;
158 /* failed, retry the search */
164 HeapFree( GetProcessHeap(), 0, path
);
168 static BOOL
set_mount_point( struct dos_drive
*drive
, const char *mount_point
)
171 struct stat path_st
, mnt_st
;
172 BOOL modified
= FALSE
;
174 if (drive
->drive
== -1) return FALSE
;
175 if (!(path
= get_dosdevices_path())) return FALSE
;
176 p
= path
+ strlen(path
) - 3;
177 *p
= 'a' + drive
->drive
;
182 /* try to avoid unlinking if already set correctly */
183 if (stat( path
, &path_st
) == -1 || stat( mount_point
, &mnt_st
) == -1 ||
184 path_st
.st_dev
!= mnt_st
.st_dev
|| path_st
.st_ino
!= mnt_st
.st_ino
)
187 symlink( mount_point
, path
);
193 if (unlink( path
) != -1) modified
= TRUE
;
196 HeapFree( GetProcessHeap(), 0, path
);
200 BOOL
add_dos_device( const char *udi
, const char *device
,
201 const char *mount_point
, DWORD type
)
203 struct dos_drive
*drive
;
205 /* first check if it already exists */
206 LIST_FOR_EACH_ENTRY( drive
, &drives_list
, struct dos_drive
, entry
)
208 if (!strcmp( udi
, drive
->udi
)) goto found
;
211 if (!(drive
= HeapAlloc( GetProcessHeap(), 0, sizeof(*drive
) ))) return FALSE
;
212 if (!(drive
->udi
= HeapAlloc( GetProcessHeap(), 0, strlen(udi
)+1 )))
214 HeapFree( GetProcessHeap(), 0, drive
);
217 strcpy( drive
->udi
, udi
);
218 list_add_tail( &drives_list
, &drive
->entry
);
221 drive
->drive
= add_drive( device
, type
);
222 if (drive
->drive
!= -1)
226 set_mount_point( drive
, mount_point
);
228 TRACE( "added device %c: udi %s for %s on %s type %u\n",
229 'a' + drive
->drive
, wine_dbgstr_a(udi
), wine_dbgstr_a(device
),
230 wine_dbgstr_a(mount_point
), type
);
232 /* hack: force the drive type in the registry */
233 if (!RegCreateKeyA( HKEY_LOCAL_MACHINE
, "Software\\Wine\\Drives", &hkey
))
235 const WCHAR
*type_name
= drive_types
[type
];
236 WCHAR name
[3] = {'a',':',0};
238 name
[0] += drive
->drive
;
240 RegSetValueExW( hkey
, name
, 0, REG_SZ
, (const BYTE
*)type_name
,
241 (strlenW(type_name
) + 1) * sizeof(WCHAR
) );
243 RegDeleteValueW( hkey
, name
);
247 send_notify( drive
->drive
, DBT_DEVICEARRIVAL
);
252 BOOL
remove_dos_device( const char *udi
)
255 struct dos_drive
*drive
;
257 LIST_FOR_EACH_ENTRY( drive
, &drives_list
, struct dos_drive
, entry
)
259 if (strcmp( udi
, drive
->udi
)) continue;
261 if (drive
->drive
!= -1)
263 BOOL modified
= set_mount_point( drive
, "" );
265 /* clear the registry key too */
266 if (!RegOpenKeyA( HKEY_LOCAL_MACHINE
, "Software\\Wine\\Drives", &hkey
))
268 WCHAR name
[3] = {'a',':',0};
269 name
[0] += drive
->drive
;
270 RegDeleteValueW( hkey
, name
);
274 if (modified
) send_notify( drive
->drive
, DBT_DEVICEREMOVECOMPLETE
);
277 list_remove( &drive
->entry
);
278 HeapFree( GetProcessHeap(), 0, drive
->udi
);
279 HeapFree( GetProcessHeap(), 0, drive
);