push e1d8a1293d44015bb0894687d02c5c53339996f7
[wine/hacks.git] / programs / winecfg / drive.c
blob1afeced033fc2fd8eebf16311a6d2ba31e8de271
1 /*
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
24 #include "config.h"
25 #include "wine/port.h"
27 #include <assert.h>
28 #include <stdarg.h>
29 #include <stdio.h>
30 #include <string.h>
32 #include <ntstatus.h>
33 #define WIN32_NO_STATUS
34 #include <windef.h>
35 #include <winbase.h>
36 #include <winternl.h>
37 #include <winioctl.h>
38 #include <winreg.h>
39 #include <wine/debug.h>
40 #include <shellapi.h>
41 #include <objbase.h>
42 #include <shlguid.h>
43 #include <shlwapi.h>
44 #include <shlobj.h>
45 #define WINE_MOUNTMGR_EXTENSIONS
46 #include <ddk/mountmgr.h>
47 #include <wine/library.h>
49 #include "winecfg.h"
50 #include "resource.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
65 * significant.
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)
77 ULONG result = 0;
78 int i;
80 WINE_TRACE("\n");
83 for(i = 0; i < 26; i++)
85 if (!drives[i].in_use) continue;
86 result |= (1 << (letter_to_index(drives[i].letter)));
89 result = ~result;
90 if (letter) result |= DRIVE_MASK_BIT(letter);
92 WINE_TRACE("finished drive letter loop with %x\n", result);
93 return 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)
102 return FALSE;
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;
117 return 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);
124 d->unixpath = NULL;
125 HeapFree(GetProcessHeap(), 0, d->device);
126 d->device = NULL;
127 HeapFree(GetProcessHeap(), 0, d->label);
128 d->label = NULL;
129 d->serial = 0;
130 d->in_use = FALSE;
131 d->modified = TRUE;
134 static DWORD get_drive_type( char letter )
136 HKEY hKey;
137 char driveValue[4];
138 DWORD ret = DRIVE_UNKNOWN;
140 sprintf(driveValue, "%c:", letter);
142 if (RegOpenKey(HKEY_LOCAL_MACHINE, "Software\\Wine\\Drives", &hKey) != ERROR_SUCCESS)
143 WINE_TRACE(" Unable to open Software\\Wine\\Drives\n" );
144 else
146 char buffer[80];
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 (!lstrcmpi( buffer, "hd" )) ret = DRIVE_FIXED;
153 else if (!lstrcmpi( buffer, "network" )) ret = DRIVE_REMOTE;
154 else if (!lstrcmpi( buffer, "floppy" )) ret = DRIVE_REMOVABLE;
155 else if (!lstrcmpi( buffer, "cdrom" )) ret = DRIVE_CDROM;
157 RegCloseKey(hKey);
159 return ret;
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 */
167 device[0] = letter;
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));
174 PRINTERROR();
176 else
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( char letter, DWORD serial )
186 char filename[] = "a:\\.windows-serial";
187 HANDLE hFile;
189 filename[0] = letter;
190 WINE_TRACE("Putting serial number of %08X into file '%s'\n", serial, filename);
191 hFile = CreateFile(filename, GENERIC_WRITE, FILE_SHARE_READ, NULL,
192 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
193 if (hFile != INVALID_HANDLE_VALUE)
195 DWORD w;
196 char buffer[16];
198 sprintf( buffer, "%X\n", serial );
199 WriteFile(hFile, buffer, strlen(buffer), &w, NULL);
200 CloseHandle(hFile);
204 #if 0
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)
212 if(pDst->in_use)
214 WINE_TRACE("pDst already in use\n");
215 return FALSE;
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;
226 pDst->in_use = TRUE;
228 return TRUE;
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");
238 return FALSE;
241 delete_drive(pSrc);
242 return TRUE;
245 #endif
247 static HANDLE open_mountmgr(void)
249 HANDLE ret;
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() );
255 return ret;
258 /* Load currently defined drives into the drives array */
259 BOOL load_drives(void)
261 DWORD i, size = 1024;
262 HANDLE mgr;
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];
282 DWORD serial;
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 ))
290 volname[0] = 0;
291 serial = 0;
293 if (unixpath) /* FIXME: handle unmounted drives too */
294 add_drive( root[0], unixpath, device, volname, serial, get_drive_type(root[0]) );
295 root[0]++;
297 else
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;
308 CloseHandle( mgr );
309 return TRUE;
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)
316 int i;
317 HANDLE mgr;
318 DWORD len;
319 struct mountmgr_unix_drive *ioctl;
321 WINE_TRACE("\n");
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;
338 ioctl->size = len;
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;
355 else
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() );
371 CloseHandle( mgr );