makefiles: Add rules for installing fake modules.
[wine] / programs / winecfg / drive.c
1 /*
2  * Drive management code
3  *
4  * Copyright 2003 Mark Westcott
5  * Copyright 2003-2004 Mike Hearn
6  * Copyright 2004 Chris Morgan
7  *
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.
12  *
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.
17  *
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
21  *
22  */
23
24 #include "config.h"
25 #include "wine/port.h"
26
27 #include <assert.h>
28 #include <stdarg.h>
29 #include <stdio.h>
30 #include <string.h>
31
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>
48
49 #include "winecfg.h"
50 #include "resource.h"
51
52
53 WINE_DEFAULT_DEBUG_CHANNEL(winecfg);
54
55 struct drive drives[26]; /* one for each drive letter */
56
57 static inline int letter_to_index(char letter)
58 {
59     return (toupper(letter) - 'A');
60 }
61
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.
66  *
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.
70  *
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.
74  */
75 ULONG drive_available_mask(char letter)
76 {
77   ULONG result = 0;
78   int i;
79
80   WINE_TRACE("\n");
81
82
83   for(i = 0; i < 26; i++)
84   {
85       if (!drives[i].in_use) continue;
86       result |= (1 << (letter_to_index(drives[i].letter)));
87   }
88
89   result = ~result;
90   if (letter) result |= DRIVE_MASK_BIT(letter);
91
92   WINE_TRACE("finished drive letter loop with %x\n", result);
93   return result;
94 }
95
96 BOOL add_drive(char letter, const char *targetpath, const char *device, const WCHAR *label,
97                DWORD serial, DWORD type)
98 {
99     int driveIndex = letter_to_index(letter);
100
101     if(drives[driveIndex].in_use)
102         return FALSE;
103
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);
107
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;
116
117     return TRUE;
118 }
119
120 /* deallocates the contents of the drive. does not free the drive itself  */
121 void delete_drive(struct drive *d)
122 {
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;
132 }
133
134 static DWORD get_drive_type( char letter )
135 {
136     HKEY hKey;
137     char driveValue[4];
138     DWORD ret = DRIVE_UNKNOWN;
139
140     sprintf(driveValue, "%c:", letter);
141
142     if (RegOpenKey(HKEY_LOCAL_MACHINE, "Software\\Wine\\Drives", &hKey) != ERROR_SUCCESS)
143         WINE_TRACE("  Unable to open Software\\Wine\\Drives\n" );
144     else
145     {
146         char buffer[80];
147         DWORD size = sizeof(buffer);
148
149         if (!RegQueryValueExA( hKey, driveValue, NULL, NULL, (LPBYTE)buffer, &size ))
150         {
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;
156         }
157         RegCloseKey(hKey);
158     }
159     return ret;
160 }
161
162
163 static void set_drive_label( char letter, const WCHAR *label )
164 {
165     static const WCHAR emptyW[1];
166     WCHAR device[] = {'a',':','\\',0};  /* SetVolumeLabel() requires a trailing slash */
167     device[0] = letter;
168
169     if (!label) label = emptyW;
170     if(!SetVolumeLabelW(device, label))
171     {
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();
175     }
176     else
177     {
178         WINE_TRACE("  set volume label for devicename of %s, label of %s\n",
179                   wine_dbgstr_w(device), wine_dbgstr_w(label));
180     }
181 }
182
183 /* set the drive serial number via a .windows-serial file */
184 static void set_drive_serial( char letter, DWORD serial )
185 {
186     char filename[] = "a:\\.windows-serial";
187     HANDLE hFile;
188
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)
194     {
195         DWORD w;
196         char buffer[16];
197
198         sprintf( buffer, "%X\n", serial );
199         WriteFile(hFile, buffer, strlen(buffer), &w, NULL);
200         CloseHandle(hFile);
201     }
202 }
203
204 #if 0
205
206 /* currently unused, but if users have this burning desire to be able to rename drives,
207    we can put it back in.
208  */
209
210 BOOL copyDrive(struct drive *pSrc, struct drive *pDst)
211 {
212     if(pDst->in_use)
213     {
214         WINE_TRACE("pDst already in use\n");
215         return FALSE;
216     }
217
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");
221
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;
227
228     return TRUE;
229 }
230
231 BOOL moveDrive(struct drive *pSrc, struct drive *pDst)
232 {
233     WINE_TRACE("pSrc->letter == %c, pDst->letter == %c\n", pSrc->letter, pDst->letter);
234
235     if(!copyDrive(pSrc, pDst))
236     {
237         WINE_TRACE("copyDrive failed\n");
238         return FALSE;
239     }
240
241     delete_drive(pSrc);
242     return TRUE;
243 }
244
245 #endif
246
247 static HANDLE open_mountmgr(void)
248 {
249     HANDLE ret;
250
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;
256 }
257
258 /* Load currently defined drives into the drives array  */
259 BOOL load_drives(void)
260 {
261     DWORD i, size = 1024;
262     HANDLE mgr;
263     WCHAR root[] = {'A',':','\\',0};
264
265     if ((mgr = open_mountmgr()) == INVALID_HANDLE_VALUE) return FALSE;
266
267     while (root[0] <= 'Z')
268     {
269         struct mountmgr_unix_drive input;
270         struct mountmgr_unix_drive *data;
271
272         if (!(data = HeapAlloc( GetProcessHeap(), 0, size ))) break;
273
274         memset( &input, 0, sizeof(input) );
275         input.letter = root[0];
276
277         if (DeviceIoControl( mgr, IOCTL_MOUNTMGR_QUERY_UNIX_DRIVE, &input, sizeof(input),
278                              data, size, NULL, NULL ))
279         {
280             char *unixpath = NULL, *device = NULL;
281             WCHAR volname[MAX_PATH];
282             DWORD serial;
283
284             if (data->mount_point_offset) unixpath = (char *)data + data->mount_point_offset;
285             if (data->device_offset) device = (char *)data + data->device_offset;
286
287             if (!GetVolumeInformationW( root, volname, sizeof(volname)/sizeof(WCHAR),
288                                         &serial, NULL, NULL, NULL, 0 ))
289             {
290                 volname[0] = 0;
291                 serial = 0;
292             }
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]++;
296         }
297         else
298         {
299             if (GetLastError() == ERROR_MORE_DATA) size = data->size;
300             else root[0]++;  /* skip this drive */
301         }
302         HeapFree( GetProcessHeap(), 0, data );
303     }
304
305     /* reset modified flags */
306     for (i = 0; i < 26; i++) drives[i].modified = FALSE;
307
308     CloseHandle( mgr );
309     return TRUE;
310 }
311
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)
315 {
316     int i;
317     HANDLE mgr;
318     DWORD len;
319     struct mountmgr_unix_drive *ioctl;
320
321     WINE_TRACE("\n");
322
323     if ((mgr = open_mountmgr()) == INVALID_HANDLE_VALUE) return;
324
325     /* add each drive and remove as we go */
326     for(i = 0; i < 26; i++)
327     {
328         if (!drives[i].modified) continue;
329         drives[i].modified = FALSE;
330
331         len = sizeof(*ioctl);
332         if (drives[i].in_use)
333         {
334             len += strlen(drives[i].unixpath) + 1;
335             if (drives[i].device) len += strlen(drives[i].device) + 1;
336         }
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)
342         {
343             char *ptr = (char *)(ioctl + 1);
344
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)
349             {
350                 ptr += strlen(ptr) + 1;
351                 strcpy( ptr, drives[i].device );
352                 ioctl->device_offset = ptr - (char *)ioctl;
353             }
354         }
355         else
356         {
357             ioctl->type = DRIVE_NO_ROOT_DIR;
358             ioctl->mount_point_offset = 0;
359         }
360
361         if (DeviceIoControl( mgr, IOCTL_MOUNTMGR_DEFINE_UNIX_DRIVE, ioctl, len, NULL, 0, NULL, NULL ))
362         {
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 );
367         }
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     }
371     CloseHandle( mgr );
372 }