ntdll: Implement retrieving DOS attributes in NtQueryInformationFile.
[wine.git] / programs / winecfg / drive.c
blob4d798ef7671e78355fba7e9fd6a5dd92c961b7d4
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 <assert.h>
25 #include <stdarg.h>
26 #include <stdio.h>
27 #include <string.h>
29 #include <ntstatus.h>
30 #define WIN32_NO_STATUS
31 #include <windef.h>
32 #include <winbase.h>
33 #include <winternl.h>
34 #include <winioctl.h>
35 #include <winreg.h>
36 #include <wine/debug.h>
37 #include <shellapi.h>
38 #include <objbase.h>
39 #include <shlguid.h>
40 #include <shlwapi.h>
41 #include <shlobj.h>
42 #define WINE_MOUNTMGR_EXTENSIONS
43 #include <ddk/mountmgr.h>
45 #include "winecfg.h"
46 #include "resource.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
61 * significant.
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)
73 ULONG result = 0;
74 int i;
76 WINE_TRACE("\n");
79 for(i = 0; i < 26; i++)
81 if (!drives[i].in_use) continue;
82 result |= (1 << (letter_to_index(drives[i].letter)));
85 result = ~result;
86 if (letter) result |= DRIVE_MASK_BIT(letter);
88 WINE_TRACE("finished drive letter loop with %lx\n", result);
89 return 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)
98 return FALSE;
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;
113 return 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);
120 d->unixpath = NULL;
121 HeapFree(GetProcessHeap(), 0, d->device);
122 d->device = NULL;
123 HeapFree(GetProcessHeap(), 0, d->label);
124 d->label = NULL;
125 d->serial = 0;
126 d->in_use = FALSE;
127 d->modified = TRUE;
130 static DWORD get_drive_type( char letter )
132 HKEY hKey;
133 WCHAR driveValue[4];
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" );
140 else
142 WCHAR buffer[80];
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;
153 RegCloseKey(hKey);
155 return ret;
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 */
163 device[0] = letter;
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));
170 PRINTERROR();
172 else
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";
183 HANDLE hFile;
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)
191 DWORD w;
192 char buffer[16];
194 sprintf( buffer, "%lX\n", serial );
195 WriteFile(hFile, buffer, strlen(buffer), &w, NULL);
196 CloseHandle(hFile);
200 static HANDLE open_mountmgr(void)
202 HANDLE ret;
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() );
208 return ret;
211 /* Load currently defined drives into the drives array */
212 BOOL load_drives(void)
214 DWORD i, size = 1024;
215 HANDLE mgr;
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];
235 DWORD serial;
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 ))
243 volname[0] = 0;
244 serial = 0;
246 if (unixpath) /* FIXME: handle unmounted drives too */
247 add_drive( root[0], unixpath, device, volname, serial, get_drive_type(root[0]) );
248 root[0]++;
250 else
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;
261 CloseHandle( mgr );
262 return TRUE;
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)
269 int i;
270 HANDLE mgr;
271 DWORD len;
272 struct mountmgr_unix_drive *ioctl;
274 WINE_TRACE("\n");
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;
291 ioctl->size = len;
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;
308 else
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 );
325 CloseHandle( mgr );
329 void query_shell_folder( const WCHAR *path, char *dest, unsigned int len )
331 UNICODE_STRING nt_name;
332 HANDLE mgr;
334 if ((mgr = open_mountmgr()) == INVALID_HANDLE_VALUE) return;
336 if (!RtlDosPathNameToNtPathName_U( path, &nt_name, NULL, NULL ))
338 CloseHandle( mgr );
339 return;
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;
350 HANDLE mgr;
351 DWORD len;
353 if ((mgr = open_mountmgr()) == INVALID_HANDLE_VALUE) return;
355 if (!RtlDosPathNameToNtPathName_U( path, &nt_name, NULL, NULL ))
357 CloseHandle( mgr );
358 return;
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 );
369 if (dest)
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 );