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
25 #include "wine/port.h"
33 #define WIN32_NO_STATUS
39 #include <wine/debug.h>
45 #define WINE_MOUNTMGR_EXTENSIONS
46 #include <ddk/mountmgr.h>
52 WINE_DEFAULT_DEBUG_CHANNEL(winecfg
);
54 struct drive drives
[26]; /* one for each drive letter */
56 static inline int letter_to_index(char letter
)
58 return (toupper(letter
) - 'A');
61 /* This function produces a mask for each drive letter that isn't
62 * currently used. Each bit of the long result represents a letter,
63 * with A being the least significant bit, and Z being the most
66 * To calculate this, we loop over each letter, and see if we can get
67 * a drive entry for it. If so, we set the appropriate bit. At the
68 * end, we flip each bit, to give the desired result.
70 * The letter parameter is always marked as being available. This is
71 * so the edit dialog can display the currently used drive letter
72 * alongside the available ones.
74 ULONG
drive_available_mask(char letter
)
82 for(i
= 0; i
< 26; i
++)
84 if (!drives
[i
].in_use
) continue;
85 result
|= (1 << (letter_to_index(drives
[i
].letter
)));
89 if (letter
) result
|= DRIVE_MASK_BIT(letter
);
91 WINE_TRACE("finished drive letter loop with %x\n", result
);
95 BOOL
add_drive(char letter
, const char *targetpath
, const char *device
, const WCHAR
*label
,
96 DWORD serial
, DWORD type
)
98 int driveIndex
= letter_to_index(letter
);
100 if(drives
[driveIndex
].in_use
)
103 WINE_TRACE("letter == '%c', unixpath == %s, device == %s, label == %s, serial == %08x, type == %d\n",
104 letter
, wine_dbgstr_a(targetpath
), wine_dbgstr_a(device
),
105 wine_dbgstr_w(label
), serial
, type
);
107 drives
[driveIndex
].letter
= toupper(letter
);
108 drives
[driveIndex
].unixpath
= strdupA(targetpath
);
109 drives
[driveIndex
].device
= device
? strdupA(device
) : NULL
;
110 drives
[driveIndex
].label
= label
? strdupW(label
) : NULL
;
111 drives
[driveIndex
].serial
= serial
;
112 drives
[driveIndex
].type
= type
;
113 drives
[driveIndex
].in_use
= TRUE
;
114 drives
[driveIndex
].modified
= TRUE
;
119 /* deallocates the contents of the drive. does not free the drive itself */
120 void delete_drive(struct drive
*d
)
122 HeapFree(GetProcessHeap(), 0, d
->unixpath
);
124 HeapFree(GetProcessHeap(), 0, d
->device
);
126 HeapFree(GetProcessHeap(), 0, d
->label
);
133 static DWORD
get_drive_type( char letter
)
137 DWORD ret
= DRIVE_UNKNOWN
;
139 sprintf(driveValue
, "%c:", letter
);
141 if (RegOpenKeyA(HKEY_LOCAL_MACHINE
, "Software\\Wine\\Drives", &hKey
) != ERROR_SUCCESS
)
142 WINE_TRACE(" Unable to open Software\\Wine\\Drives\n" );
146 DWORD size
= sizeof(buffer
);
148 if (!RegQueryValueExA( hKey
, driveValue
, NULL
, NULL
, (LPBYTE
)buffer
, &size
))
150 WINE_TRACE("Got type '%s' for %s\n", buffer
, driveValue
);
151 if (!lstrcmpiA( buffer
, "hd" )) ret
= DRIVE_FIXED
;
152 else if (!lstrcmpiA( buffer
, "network" )) ret
= DRIVE_REMOTE
;
153 else if (!lstrcmpiA( buffer
, "floppy" )) ret
= DRIVE_REMOVABLE
;
154 else if (!lstrcmpiA( buffer
, "cdrom" )) ret
= DRIVE_CDROM
;
162 static void set_drive_label( char letter
, const WCHAR
*label
)
164 static const WCHAR emptyW
[1];
165 WCHAR device
[] = {'a',':','\\',0}; /* SetVolumeLabel() requires a trailing slash */
168 if (!label
) label
= emptyW
;
169 if(!SetVolumeLabelW(device
, label
))
171 WINE_WARN("unable to set volume label for devicename of %s, label of %s\n",
172 wine_dbgstr_w(device
), wine_dbgstr_w(label
));
177 WINE_TRACE(" set volume label for devicename of %s, label of %s\n",
178 wine_dbgstr_w(device
), wine_dbgstr_w(label
));
182 /* set the drive serial number via a .windows-serial file */
183 static void set_drive_serial( WCHAR letter
, DWORD serial
)
185 WCHAR filename
[] = {'a',':','\\','.','w','i','n','d','o','w','s','-','s','e','r','i','a','l',0};
188 filename
[0] = letter
;
189 WINE_TRACE("Putting serial number of %08X into file %s\n", serial
, wine_dbgstr_w(filename
));
190 hFile
= CreateFileW(filename
, GENERIC_WRITE
, FILE_SHARE_READ
, NULL
,
191 CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
192 if (hFile
!= INVALID_HANDLE_VALUE
)
197 sprintf( buffer
, "%X\n", serial
);
198 WriteFile(hFile
, buffer
, strlen(buffer
), &w
, NULL
);
205 /* currently unused, but if users have this burning desire to be able to rename drives,
206 we can put it back in.
209 BOOL
copyDrive(struct drive
*pSrc
, struct drive
*pDst
)
213 WINE_TRACE("pDst already in use\n");
217 if(!pSrc
->unixpath
) WINE_TRACE("!pSrc->unixpath\n");
218 if(!pSrc
->label
) WINE_TRACE("!pSrc->label\n");
219 if(!pSrc
->serial
) WINE_TRACE("!pSrc->serial\n");
221 pDst
->unixpath
= strdupA(pSrc
->unixpath
);
222 pDst
->label
= strdupA(pSrc
->label
);
223 pDst
->serial
= strdupA(pSrc
->serial
);
224 pDst
->type
= pSrc
->type
;
230 BOOL
moveDrive(struct drive
*pSrc
, struct drive
*pDst
)
232 WINE_TRACE("pSrc->letter == %c, pDst->letter == %c\n", pSrc
->letter
, pDst
->letter
);
234 if(!copyDrive(pSrc
, pDst
))
236 WINE_TRACE("copyDrive failed\n");
246 static HANDLE
open_mountmgr(void)
250 if ((ret
= CreateFileW( MOUNTMGR_DOS_DEVICE_NAME
, GENERIC_READ
|GENERIC_WRITE
,
251 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, OPEN_EXISTING
,
252 0, 0 )) == INVALID_HANDLE_VALUE
)
253 WINE_ERR( "failed to open mount manager err %u\n", GetLastError() );
257 /* Load currently defined drives into the drives array */
258 BOOL
load_drives(void)
260 DWORD i
, size
= 1024;
262 WCHAR root
[] = {'A',':','\\',0};
264 if ((mgr
= open_mountmgr()) == INVALID_HANDLE_VALUE
) return FALSE
;
266 while (root
[0] <= 'Z')
268 struct mountmgr_unix_drive input
;
269 struct mountmgr_unix_drive
*data
;
271 if (!(data
= HeapAlloc( GetProcessHeap(), 0, size
))) break;
273 memset( &input
, 0, sizeof(input
) );
274 input
.letter
= root
[0];
276 if (DeviceIoControl( mgr
, IOCTL_MOUNTMGR_QUERY_UNIX_DRIVE
, &input
, sizeof(input
),
277 data
, size
, NULL
, NULL
))
279 char *unixpath
= NULL
, *device
= NULL
;
280 WCHAR volname
[MAX_PATH
];
283 if (data
->mount_point_offset
) unixpath
= (char *)data
+ data
->mount_point_offset
;
284 if (data
->device_offset
) device
= (char *)data
+ data
->device_offset
;
286 if (!GetVolumeInformationW( root
, volname
, ARRAY_SIZE(volname
),
287 &serial
, NULL
, NULL
, NULL
, 0 ))
292 if (unixpath
) /* FIXME: handle unmounted drives too */
293 add_drive( root
[0], unixpath
, device
, volname
, serial
, get_drive_type(root
[0]) );
298 if (GetLastError() == ERROR_MORE_DATA
) size
= data
->size
;
299 else root
[0]++; /* skip this drive */
301 HeapFree( GetProcessHeap(), 0, data
);
304 /* reset modified flags */
305 for (i
= 0; i
< 26; i
++) drives
[i
].modified
= FALSE
;
311 /* some of this code appears to be broken by bugs in Wine: the label
312 * setting code has no effect, for instance */
313 void apply_drive_changes(void)
318 struct mountmgr_unix_drive
*ioctl
;
322 if ((mgr
= open_mountmgr()) == INVALID_HANDLE_VALUE
) return;
324 /* add each drive and remove as we go */
325 for(i
= 0; i
< 26; i
++)
327 if (!drives
[i
].modified
) continue;
328 drives
[i
].modified
= FALSE
;
330 len
= sizeof(*ioctl
);
331 if (drives
[i
].in_use
)
333 len
+= strlen(drives
[i
].unixpath
) + 1;
334 if (drives
[i
].device
) len
+= strlen(drives
[i
].device
) + 1;
336 if (!(ioctl
= HeapAlloc( GetProcessHeap(), 0, len
))) continue;
338 ioctl
->letter
= 'a' + i
;
339 ioctl
->device_offset
= 0;
340 if (drives
[i
].in_use
)
342 char *ptr
= (char *)(ioctl
+ 1);
344 ioctl
->type
= drives
[i
].type
;
345 strcpy( ptr
, drives
[i
].unixpath
);
346 ioctl
->mount_point_offset
= ptr
- (char *)ioctl
;
347 if (drives
[i
].device
)
349 ptr
+= strlen(ptr
) + 1;
350 strcpy( ptr
, drives
[i
].device
);
351 ioctl
->device_offset
= ptr
- (char *)ioctl
;
356 ioctl
->type
= DRIVE_NO_ROOT_DIR
;
357 ioctl
->mount_point_offset
= 0;
360 if (DeviceIoControl( mgr
, IOCTL_MOUNTMGR_DEFINE_UNIX_DRIVE
, ioctl
, len
, NULL
, 0, NULL
, NULL
))
362 set_drive_label( drives
[i
].letter
, drives
[i
].label
);
363 if (drives
[i
].in_use
) set_drive_serial( drives
[i
].letter
, drives
[i
].serial
);
364 WINE_TRACE( "set drive %c: to %s type %u\n", 'a' + i
,
365 wine_dbgstr_a(drives
[i
].unixpath
), drives
[i
].type
);
367 else WINE_WARN( "failed to set drive %c: to %s type %u err %u\n", 'a' + i
,
368 wine_dbgstr_a(drives
[i
].unixpath
), drives
[i
].type
, GetLastError() );
369 HeapFree( GetProcessHeap(), 0, ioctl
);