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 );