2 * Drive management code
4 * Copyright 2003 Mark Westcott
5 * Copyright 2003-2004 Mike Hearn
6 * Copyright 2004 Chris Morgan
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
30 #define WIN32_NO_STATUS
36 #include <wine/debug.h>
42 #define WINE_MOUNTMGR_EXTENSIONS
43 #include <ddk/mountmgr.h>
49 WINE_DEFAULT_DEBUG_CHANNEL(winecfg
);
51 struct drive drives
[26]; /* one for each drive letter */
53 static inline int letter_to_index(char letter
)
55 return (toupper(letter
) - 'A');
58 /* This function produces a mask for each drive letter that isn't
59 * currently used. Each bit of the long result represents a letter,
60 * with A being the least significant bit, and Z being the most
63 * To calculate this, we loop over each letter, and see if we can get
64 * a drive entry for it. If so, we set the appropriate bit. At the
65 * end, we flip each bit, to give the desired result.
67 * The letter parameter is always marked as being available. This is
68 * so the edit dialog can display the currently used drive letter
69 * alongside the available ones.
71 ULONG
drive_available_mask(char letter
)
79 for(i
= 0; i
< 26; i
++)
81 if (!drives
[i
].in_use
) continue;
82 result
|= (1 << (letter_to_index(drives
[i
].letter
)));
86 if (letter
) result
|= DRIVE_MASK_BIT(letter
);
88 WINE_TRACE("finished drive letter loop with %lx\n", result
);
92 BOOL
add_drive(char letter
, const char *targetpath
, const char *device
, const WCHAR
*label
,
93 DWORD serial
, DWORD type
)
95 int driveIndex
= letter_to_index(letter
);
97 if(drives
[driveIndex
].in_use
)
100 WINE_TRACE("letter == '%c', unixpath == %s, device == %s, label == %s, serial == %08lx, type == %ld\n",
101 letter
, wine_dbgstr_a(targetpath
), wine_dbgstr_a(device
),
102 wine_dbgstr_w(label
), serial
, type
);
104 drives
[driveIndex
].letter
= toupper(letter
);
105 drives
[driveIndex
].unixpath
= strdupA(targetpath
);
106 drives
[driveIndex
].device
= device
? strdupA(device
) : NULL
;
107 drives
[driveIndex
].label
= label
? strdupW(label
) : NULL
;
108 drives
[driveIndex
].serial
= serial
;
109 drives
[driveIndex
].type
= type
;
110 drives
[driveIndex
].in_use
= TRUE
;
111 drives
[driveIndex
].modified
= TRUE
;
116 /* deallocates the contents of the drive. does not free the drive itself */
117 void delete_drive(struct drive
*d
)
119 HeapFree(GetProcessHeap(), 0, d
->unixpath
);
121 HeapFree(GetProcessHeap(), 0, d
->device
);
123 HeapFree(GetProcessHeap(), 0, d
->label
);
130 static DWORD
get_drive_type( char letter
)
134 DWORD ret
= DRIVE_UNKNOWN
;
136 swprintf(driveValue
, 4, L
"%c:", letter
);
138 if (RegOpenKeyW(HKEY_LOCAL_MACHINE
, L
"Software\\Wine\\Drives", &hKey
) != ERROR_SUCCESS
)
139 WINE_TRACE(" Unable to open Software\\Wine\\Drives\n" );
143 DWORD size
= sizeof(buffer
);
145 if (!RegQueryValueExW( hKey
, driveValue
, NULL
, NULL
, (LPBYTE
)buffer
, &size
))
147 WINE_TRACE("Got type %s for %s\n", debugstr_w(buffer
), debugstr_w(driveValue
) );
148 if (!wcsicmp( buffer
, L
"hd" )) ret
= DRIVE_FIXED
;
149 else if (!wcsicmp( buffer
, L
"network" )) ret
= DRIVE_REMOTE
;
150 else if (!wcsicmp( buffer
, L
"floppy" )) ret
= DRIVE_REMOVABLE
;
151 else if (!wcsicmp( buffer
, L
"cdrom" )) ret
= DRIVE_CDROM
;
159 static void set_drive_label( char letter
, const WCHAR
*label
)
161 static const WCHAR emptyW
[1];
162 WCHAR device
[] = L
"a:\\"; /* SetVolumeLabel() requires a trailing slash */
165 if (!label
) label
= emptyW
;
166 if(!SetVolumeLabelW(device
, label
))
168 WINE_WARN("unable to set volume label for devicename of %s, label of %s\n",
169 wine_dbgstr_w(device
), wine_dbgstr_w(label
));
174 WINE_TRACE(" set volume label for devicename of %s, label of %s\n",
175 wine_dbgstr_w(device
), wine_dbgstr_w(label
));
179 /* set the drive serial number via a .windows-serial file */
180 static void set_drive_serial( WCHAR letter
, DWORD serial
)
182 WCHAR filename
[] = L
"a:\\.windows-serial";
185 filename
[0] = letter
;
186 WINE_TRACE("Putting serial number of %08lX into file %s\n", serial
, wine_dbgstr_w(filename
));
187 hFile
= CreateFileW(filename
, GENERIC_WRITE
, FILE_SHARE_READ
, NULL
,
188 CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
189 if (hFile
!= INVALID_HANDLE_VALUE
)
194 sprintf( buffer
, "%lX\n", serial
);
195 WriteFile(hFile
, buffer
, strlen(buffer
), &w
, NULL
);
200 static HANDLE
open_mountmgr(void)
204 if ((ret
= CreateFileW( MOUNTMGR_DOS_DEVICE_NAME
, GENERIC_READ
|GENERIC_WRITE
,
205 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, OPEN_EXISTING
,
206 0, 0 )) == INVALID_HANDLE_VALUE
)
207 WINE_ERR( "failed to open mount manager err %lu\n", GetLastError() );
211 /* Load currently defined drives into the drives array */
212 BOOL
load_drives(void)
214 DWORD i
, size
= 1024;
216 WCHAR root
[] = L
"A:\\";
218 if ((mgr
= open_mountmgr()) == INVALID_HANDLE_VALUE
) return FALSE
;
220 while (root
[0] <= 'Z')
222 struct mountmgr_unix_drive input
;
223 struct mountmgr_unix_drive
*data
;
225 if (!(data
= HeapAlloc( GetProcessHeap(), 0, size
))) break;
227 memset( &input
, 0, sizeof(input
) );
228 input
.letter
= root
[0];
230 if (DeviceIoControl( mgr
, IOCTL_MOUNTMGR_QUERY_UNIX_DRIVE
, &input
, sizeof(input
),
231 data
, size
, NULL
, NULL
))
233 char *unixpath
= NULL
, *device
= NULL
;
234 WCHAR volname
[MAX_PATH
];
237 if (data
->mount_point_offset
) unixpath
= (char *)data
+ data
->mount_point_offset
;
238 if (data
->device_offset
) device
= (char *)data
+ data
->device_offset
;
240 if (!GetVolumeInformationW( root
, volname
, ARRAY_SIZE(volname
),
241 &serial
, NULL
, NULL
, NULL
, 0 ))
246 if (unixpath
) /* FIXME: handle unmounted drives too */
247 add_drive( root
[0], unixpath
, device
, volname
, serial
, get_drive_type(root
[0]) );
252 if (GetLastError() == ERROR_MORE_DATA
) size
= data
->size
;
253 else root
[0]++; /* skip this drive */
255 HeapFree( GetProcessHeap(), 0, data
);
258 /* reset modified flags */
259 for (i
= 0; i
< 26; i
++) drives
[i
].modified
= FALSE
;
265 /* some of this code appears to be broken by bugs in Wine: the label
266 * setting code has no effect, for instance */
267 void apply_drive_changes(void)
272 struct mountmgr_unix_drive
*ioctl
;
276 if ((mgr
= open_mountmgr()) == INVALID_HANDLE_VALUE
) return;
278 /* add each drive and remove as we go */
279 for(i
= 0; i
< 26; i
++)
281 if (!drives
[i
].modified
) continue;
282 drives
[i
].modified
= FALSE
;
284 len
= sizeof(*ioctl
);
285 if (drives
[i
].in_use
)
287 len
+= strlen(drives
[i
].unixpath
) + 1;
288 if (drives
[i
].device
) len
+= strlen(drives
[i
].device
) + 1;
290 if (!(ioctl
= HeapAlloc( GetProcessHeap(), 0, len
))) continue;
292 ioctl
->letter
= 'a' + i
;
293 ioctl
->device_offset
= 0;
294 if (drives
[i
].in_use
)
296 char *ptr
= (char *)(ioctl
+ 1);
298 ioctl
->type
= drives
[i
].type
;
299 strcpy( ptr
, drives
[i
].unixpath
);
300 ioctl
->mount_point_offset
= ptr
- (char *)ioctl
;
301 if (drives
[i
].device
)
303 ptr
+= strlen(ptr
) + 1;
304 strcpy( ptr
, drives
[i
].device
);
305 ioctl
->device_offset
= ptr
- (char *)ioctl
;
310 ioctl
->type
= DRIVE_NO_ROOT_DIR
;
311 ioctl
->mount_point_offset
= 0;
314 if (DeviceIoControl( mgr
, IOCTL_MOUNTMGR_DEFINE_UNIX_DRIVE
, ioctl
, len
, NULL
, 0, NULL
, NULL
))
316 set_drive_label( drives
[i
].letter
, drives
[i
].label
);
317 if (drives
[i
].in_use
) set_drive_serial( drives
[i
].letter
, drives
[i
].serial
);
318 WINE_TRACE( "set drive %c: to %s type %lu\n", 'a' + i
,
319 wine_dbgstr_a(drives
[i
].unixpath
), drives
[i
].type
);
321 else WINE_WARN( "failed to set drive %c: to %s type %lu err %lu\n", 'a' + i
,
322 wine_dbgstr_a(drives
[i
].unixpath
), drives
[i
].type
, GetLastError() );
323 HeapFree( GetProcessHeap(), 0, ioctl
);
329 void query_shell_folder( const WCHAR
*path
, char *dest
, unsigned int len
)
331 UNICODE_STRING nt_name
;
334 if ((mgr
= open_mountmgr()) == INVALID_HANDLE_VALUE
) return;
336 if (!RtlDosPathNameToNtPathName_U( path
, &nt_name
, NULL
, NULL
))
341 DeviceIoControl( mgr
, IOCTL_MOUNTMGR_QUERY_SHELL_FOLDER
, nt_name
.Buffer
, nt_name
.Length
,
342 dest
, len
, NULL
, NULL
);
343 RtlFreeUnicodeString( &nt_name
);
346 void set_shell_folder( const WCHAR
*path
, const char *dest
)
348 struct mountmgr_shell_folder
*ioctl
;
349 UNICODE_STRING nt_name
;
353 if ((mgr
= open_mountmgr()) == INVALID_HANDLE_VALUE
) return;
355 if (!RtlDosPathNameToNtPathName_U( path
, &nt_name
, NULL
, NULL
))
361 len
= sizeof(*ioctl
) + nt_name
.Length
;
362 if (dest
) len
+= strlen(dest
) + 1;
364 if (!(ioctl
= HeapAlloc( GetProcessHeap(), 0, len
))) return;
365 ioctl
->create_backup
= TRUE
;
366 ioctl
->folder_offset
= sizeof(*ioctl
);
367 ioctl
->folder_size
= nt_name
.Length
;
368 memcpy( (char *)ioctl
+ ioctl
->folder_offset
, nt_name
.Buffer
, nt_name
.Length
);
371 ioctl
->symlink_offset
= ioctl
->folder_offset
+ ioctl
->folder_size
;
372 strcpy( (char *)ioctl
+ ioctl
->symlink_offset
, dest
);
374 else ioctl
->symlink_offset
= 0;
376 DeviceIoControl( mgr
, IOCTL_MOUNTMGR_DEFINE_SHELL_FOLDER
, ioctl
, len
, NULL
, 0, NULL
, NULL
);
377 HeapFree( GetProcessHeap(), 0, ioctl
);
378 RtlFreeUnicodeString( &nt_name
);