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>
47 #include <wine/library.h>
53 WINE_DEFAULT_DEBUG_CHANNEL(winecfg
);
55 struct drive drives
[26]; /* one for each drive letter */
57 static inline int letter_to_index(char letter
)
59 return (toupper(letter
) - 'A');
62 /* This function produces a mask for each drive letter that isn't
63 * currently used. Each bit of the long result represents a letter,
64 * with A being the least significant bit, and Z being the most
67 * To calculate this, we loop over each letter, and see if we can get
68 * a drive entry for it. If so, we set the appropriate bit. At the
69 * end, we flip each bit, to give the desired result.
71 * The letter parameter is always marked as being available. This is
72 * so the edit dialog can display the currently used drive letter
73 * alongside the available ones.
75 ULONG
drive_available_mask(char letter
)
83 for(i
= 0; i
< 26; i
++)
85 if (!drives
[i
].in_use
) continue;
86 result
|= (1 << (letter_to_index(drives
[i
].letter
)));
90 if (letter
) result
|= DRIVE_MASK_BIT(letter
);
92 WINE_TRACE("finished drive letter loop with %x\n", result
);
96 BOOL
add_drive(char letter
, const char *targetpath
, const char *device
, const WCHAR
*label
,
97 DWORD serial
, DWORD type
)
99 int driveIndex
= letter_to_index(letter
);
101 if(drives
[driveIndex
].in_use
)
104 WINE_TRACE("letter == '%c', unixpath == %s, device == %s, label == %s, serial == %08x, type == %d\n",
105 letter
, wine_dbgstr_a(targetpath
), wine_dbgstr_a(device
),
106 wine_dbgstr_w(label
), serial
, type
);
108 drives
[driveIndex
].letter
= toupper(letter
);
109 drives
[driveIndex
].unixpath
= strdupA(targetpath
);
110 drives
[driveIndex
].device
= device
? strdupA(device
) : NULL
;
111 drives
[driveIndex
].label
= label
? strdupW(label
) : NULL
;
112 drives
[driveIndex
].serial
= serial
;
113 drives
[driveIndex
].type
= type
;
114 drives
[driveIndex
].in_use
= TRUE
;
115 drives
[driveIndex
].modified
= TRUE
;
120 /* deallocates the contents of the drive. does not free the drive itself */
121 void delete_drive(struct drive
*d
)
123 HeapFree(GetProcessHeap(), 0, d
->unixpath
);
125 HeapFree(GetProcessHeap(), 0, d
->device
);
127 HeapFree(GetProcessHeap(), 0, d
->label
);
134 static DWORD
get_drive_type( char letter
)
138 DWORD ret
= DRIVE_UNKNOWN
;
140 sprintf(driveValue
, "%c:", letter
);
142 if (RegOpenKeyA(HKEY_LOCAL_MACHINE
, "Software\\Wine\\Drives", &hKey
) != ERROR_SUCCESS
)
143 WINE_TRACE(" Unable to open Software\\Wine\\Drives\n" );
147 DWORD size
= sizeof(buffer
);
149 if (!RegQueryValueExA( hKey
, driveValue
, NULL
, NULL
, (LPBYTE
)buffer
, &size
))
151 WINE_TRACE("Got type '%s' for %s\n", buffer
, driveValue
);
152 if (!lstrcmpiA( buffer
, "hd" )) ret
= DRIVE_FIXED
;
153 else if (!lstrcmpiA( buffer
, "network" )) ret
= DRIVE_REMOTE
;
154 else if (!lstrcmpiA( buffer
, "floppy" )) ret
= DRIVE_REMOVABLE
;
155 else if (!lstrcmpiA( buffer
, "cdrom" )) ret
= DRIVE_CDROM
;
163 static void set_drive_label( char letter
, const WCHAR
*label
)
165 static const WCHAR emptyW
[1];
166 WCHAR device
[] = {'a',':','\\',0}; /* SetVolumeLabel() requires a trailing slash */
169 if (!label
) label
= emptyW
;
170 if(!SetVolumeLabelW(device
, label
))
172 WINE_WARN("unable to set volume label for devicename of %s, label of %s\n",
173 wine_dbgstr_w(device
), wine_dbgstr_w(label
));
178 WINE_TRACE(" set volume label for devicename of %s, label of %s\n",
179 wine_dbgstr_w(device
), wine_dbgstr_w(label
));
183 /* set the drive serial number via a .windows-serial file */
184 static void set_drive_serial( WCHAR letter
, DWORD serial
)
186 WCHAR filename
[] = {'a',':','\\','.','w','i','n','d','o','w','s','-','s','e','r','i','a','l',0};
189 filename
[0] = letter
;
190 WINE_TRACE("Putting serial number of %08X into file %s\n", serial
, wine_dbgstr_w(filename
));
191 hFile
= CreateFileW(filename
, GENERIC_WRITE
, FILE_SHARE_READ
, NULL
,
192 CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
193 if (hFile
!= INVALID_HANDLE_VALUE
)
198 sprintf( buffer
, "%X\n", serial
);
199 WriteFile(hFile
, buffer
, strlen(buffer
), &w
, NULL
);
206 /* currently unused, but if users have this burning desire to be able to rename drives,
207 we can put it back in.
210 BOOL
copyDrive(struct drive
*pSrc
, struct drive
*pDst
)
214 WINE_TRACE("pDst already in use\n");
218 if(!pSrc
->unixpath
) WINE_TRACE("!pSrc->unixpath\n");
219 if(!pSrc
->label
) WINE_TRACE("!pSrc->label\n");
220 if(!pSrc
->serial
) WINE_TRACE("!pSrc->serial\n");
222 pDst
->unixpath
= strdupA(pSrc
->unixpath
);
223 pDst
->label
= strdupA(pSrc
->label
);
224 pDst
->serial
= strdupA(pSrc
->serial
);
225 pDst
->type
= pSrc
->type
;
231 BOOL
moveDrive(struct drive
*pSrc
, struct drive
*pDst
)
233 WINE_TRACE("pSrc->letter == %c, pDst->letter == %c\n", pSrc
->letter
, pDst
->letter
);
235 if(!copyDrive(pSrc
, pDst
))
237 WINE_TRACE("copyDrive failed\n");
247 static HANDLE
open_mountmgr(void)
251 if ((ret
= CreateFileW( MOUNTMGR_DOS_DEVICE_NAME
, GENERIC_READ
|GENERIC_WRITE
,
252 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, OPEN_EXISTING
,
253 0, 0 )) == INVALID_HANDLE_VALUE
)
254 WINE_ERR( "failed to open mount manager err %u\n", GetLastError() );
258 /* Load currently defined drives into the drives array */
259 BOOL
load_drives(void)
261 DWORD i
, size
= 1024;
263 WCHAR root
[] = {'A',':','\\',0};
265 if ((mgr
= open_mountmgr()) == INVALID_HANDLE_VALUE
) return FALSE
;
267 while (root
[0] <= 'Z')
269 struct mountmgr_unix_drive input
;
270 struct mountmgr_unix_drive
*data
;
272 if (!(data
= HeapAlloc( GetProcessHeap(), 0, size
))) break;
274 memset( &input
, 0, sizeof(input
) );
275 input
.letter
= root
[0];
277 if (DeviceIoControl( mgr
, IOCTL_MOUNTMGR_QUERY_UNIX_DRIVE
, &input
, sizeof(input
),
278 data
, size
, NULL
, NULL
))
280 char *unixpath
= NULL
, *device
= NULL
;
281 WCHAR volname
[MAX_PATH
];
284 if (data
->mount_point_offset
) unixpath
= (char *)data
+ data
->mount_point_offset
;
285 if (data
->device_offset
) device
= (char *)data
+ data
->device_offset
;
287 if (!GetVolumeInformationW( root
, volname
, sizeof(volname
)/sizeof(WCHAR
),
288 &serial
, NULL
, NULL
, NULL
, 0 ))
293 if (unixpath
) /* FIXME: handle unmounted drives too */
294 add_drive( root
[0], unixpath
, device
, volname
, serial
, get_drive_type(root
[0]) );
299 if (GetLastError() == ERROR_MORE_DATA
) size
= data
->size
;
300 else root
[0]++; /* skip this drive */
302 HeapFree( GetProcessHeap(), 0, data
);
305 /* reset modified flags */
306 for (i
= 0; i
< 26; i
++) drives
[i
].modified
= FALSE
;
312 /* some of this code appears to be broken by bugs in Wine: the label
313 * setting code has no effect, for instance */
314 void apply_drive_changes(void)
319 struct mountmgr_unix_drive
*ioctl
;
323 if ((mgr
= open_mountmgr()) == INVALID_HANDLE_VALUE
) return;
325 /* add each drive and remove as we go */
326 for(i
= 0; i
< 26; i
++)
328 if (!drives
[i
].modified
) continue;
329 drives
[i
].modified
= FALSE
;
331 len
= sizeof(*ioctl
);
332 if (drives
[i
].in_use
)
334 len
+= strlen(drives
[i
].unixpath
) + 1;
335 if (drives
[i
].device
) len
+= strlen(drives
[i
].device
) + 1;
337 if (!(ioctl
= HeapAlloc( GetProcessHeap(), 0, len
))) continue;
339 ioctl
->letter
= 'a' + i
;
340 ioctl
->device_offset
= 0;
341 if (drives
[i
].in_use
)
343 char *ptr
= (char *)(ioctl
+ 1);
345 ioctl
->type
= drives
[i
].type
;
346 strcpy( ptr
, drives
[i
].unixpath
);
347 ioctl
->mount_point_offset
= ptr
- (char *)ioctl
;
348 if (drives
[i
].device
)
350 ptr
+= strlen(ptr
) + 1;
351 strcpy( ptr
, drives
[i
].device
);
352 ioctl
->device_offset
= ptr
- (char *)ioctl
;
357 ioctl
->type
= DRIVE_NO_ROOT_DIR
;
358 ioctl
->mount_point_offset
= 0;
361 if (DeviceIoControl( mgr
, IOCTL_MOUNTMGR_DEFINE_UNIX_DRIVE
, ioctl
, len
, NULL
, 0, NULL
, NULL
))
363 set_drive_label( drives
[i
].letter
, drives
[i
].label
);
364 if (drives
[i
].in_use
) set_drive_serial( drives
[i
].letter
, drives
[i
].serial
);
365 WINE_TRACE( "set drive %c: to %s type %u\n", 'a' + i
,
366 wine_dbgstr_a(drives
[i
].unixpath
), drives
[i
].type
);
368 else WINE_WARN( "failed to set drive %c: to %s type %u err %u\n", 'a' + i
,
369 wine_dbgstr_a(drives
[i
].unixpath
), drives
[i
].type
, GetLastError() );
370 HeapFree( GetProcessHeap(), 0, ioctl
);