drive.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  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. #include <assert.h>
  24. #include <stdarg.h>
  25. #include <stdio.h>
  26. #include <string.h>
  27. #include <ntstatus.h>
  28. #define WIN32_NO_STATUS
  29. #include <windef.h>
  30. #include <winbase.h>
  31. #include <winternl.h>
  32. #include <winioctl.h>
  33. #include <winreg.h>
  34. #include <wine/debug.h>
  35. #include <shellapi.h>
  36. #include <objbase.h>
  37. #include <shlguid.h>
  38. #include <shlwapi.h>
  39. #include <shlobj.h>
  40. #define WINE_MOUNTMGR_EXTENSIONS
  41. #include <ddk/mountmgr.h>
  42. #include "winecfg.h"
  43. #include "resource.h"
  44. WINE_DEFAULT_DEBUG_CHANNEL(winecfg);
  45. struct drive drives[26]; /* one for each drive letter */
  46. static inline int letter_to_index(char letter)
  47. {
  48. return (toupper(letter) - 'A');
  49. }
  50. /* This function produces a mask for each drive letter that isn't
  51. * currently used. Each bit of the long result represents a letter,
  52. * with A being the least significant bit, and Z being the most
  53. * significant.
  54. *
  55. * To calculate this, we loop over each letter, and see if we can get
  56. * a drive entry for it. If so, we set the appropriate bit. At the
  57. * end, we flip each bit, to give the desired result.
  58. *
  59. * The letter parameter is always marked as being available. This is
  60. * so the edit dialog can display the currently used drive letter
  61. * alongside the available ones.
  62. */
  63. ULONG drive_available_mask(char letter)
  64. {
  65. ULONG result = 0;
  66. int i;
  67. WINE_TRACE("\n");
  68. for(i = 0; i < 26; i++)
  69. {
  70. if (!drives[i].in_use) continue;
  71. result |= (1 << (letter_to_index(drives[i].letter)));
  72. }
  73. result = ~result;
  74. if (letter) result |= DRIVE_MASK_BIT(letter);
  75. WINE_TRACE("finished drive letter loop with %x\n", result);
  76. return result;
  77. }
  78. BOOL add_drive(char letter, const char *targetpath, const char *device, const WCHAR *label,
  79. DWORD serial, DWORD type)
  80. {
  81. int driveIndex = letter_to_index(letter);
  82. if(drives[driveIndex].in_use)
  83. return FALSE;
  84. WINE_TRACE("letter == '%c', unixpath == %s, device == %s, label == %s, serial == %08x, type == %d\n",
  85. letter, wine_dbgstr_a(targetpath), wine_dbgstr_a(device),
  86. wine_dbgstr_w(label), serial, type);
  87. drives[driveIndex].letter = toupper(letter);
  88. drives[driveIndex].unixpath = strdupA(targetpath);
  89. drives[driveIndex].device = device ? strdupA(device) : NULL;
  90. drives[driveIndex].label = label ? strdupW(label) : NULL;
  91. drives[driveIndex].serial = serial;
  92. drives[driveIndex].type = type;
  93. drives[driveIndex].in_use = TRUE;
  94. drives[driveIndex].modified = TRUE;
  95. return TRUE;
  96. }
  97. /* deallocates the contents of the drive. does not free the drive itself */
  98. void delete_drive(struct drive *d)
  99. {
  100. HeapFree(GetProcessHeap(), 0, d->unixpath);
  101. d->unixpath = NULL;
  102. HeapFree(GetProcessHeap(), 0, d->device);
  103. d->device = NULL;
  104. HeapFree(GetProcessHeap(), 0, d->label);
  105. d->label = NULL;
  106. d->serial = 0;
  107. d->in_use = FALSE;
  108. d->modified = TRUE;
  109. }
  110. static DWORD get_drive_type( char letter )
  111. {
  112. HKEY hKey;
  113. WCHAR driveValue[4];
  114. DWORD ret = DRIVE_UNKNOWN;
  115. swprintf(driveValue, 4, L"%c:", letter);
  116. if (RegOpenKeyW(HKEY_LOCAL_MACHINE, L"Software\\Wine\\Drives", &hKey) != ERROR_SUCCESS)
  117. WINE_TRACE(" Unable to open Software\\Wine\\Drives\n" );
  118. else
  119. {
  120. WCHAR buffer[80];
  121. DWORD size = sizeof(buffer);
  122. if (!RegQueryValueExW( hKey, driveValue, NULL, NULL, (LPBYTE)buffer, &size ))
  123. {
  124. WINE_TRACE("Got type %s for %s\n", debugstr_w(buffer), debugstr_w(driveValue) );
  125. if (!wcsicmp( buffer, L"hd" )) ret = DRIVE_FIXED;
  126. else if (!wcsicmp( buffer, L"network" )) ret = DRIVE_REMOTE;
  127. else if (!wcsicmp( buffer, L"floppy" )) ret = DRIVE_REMOVABLE;
  128. else if (!wcsicmp( buffer, L"cdrom" )) ret = DRIVE_CDROM;
  129. }
  130. RegCloseKey(hKey);
  131. }
  132. return ret;
  133. }
  134. static void set_drive_label( char letter, const WCHAR *label )
  135. {
  136. static const WCHAR emptyW[1];
  137. WCHAR device[] = L"a:\\"; /* SetVolumeLabel() requires a trailing slash */
  138. device[0] = letter;
  139. if (!label) label = emptyW;
  140. if(!SetVolumeLabelW(device, label))
  141. {
  142. WINE_WARN("unable to set volume label for devicename of %s, label of %s\n",
  143. wine_dbgstr_w(device), wine_dbgstr_w(label));
  144. PRINTERROR();
  145. }
  146. else
  147. {
  148. WINE_TRACE(" set volume label for devicename of %s, label of %s\n",
  149. wine_dbgstr_w(device), wine_dbgstr_w(label));
  150. }
  151. }
  152. /* set the drive serial number via a .windows-serial file */
  153. static void set_drive_serial( WCHAR letter, DWORD serial )
  154. {
  155. WCHAR filename[] = L"a:\\.windows-serial";
  156. HANDLE hFile;
  157. filename[0] = letter;
  158. WINE_TRACE("Putting serial number of %08X into file %s\n", serial, wine_dbgstr_w(filename));
  159. hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ, NULL,
  160. CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  161. if (hFile != INVALID_HANDLE_VALUE)
  162. {
  163. DWORD w;
  164. char buffer[16];
  165. sprintf( buffer, "%X\n", serial );
  166. WriteFile(hFile, buffer, strlen(buffer), &w, NULL);
  167. CloseHandle(hFile);
  168. }
  169. }
  170. static HANDLE open_mountmgr(void)
  171. {
  172. HANDLE ret;
  173. if ((ret = CreateFileW( MOUNTMGR_DOS_DEVICE_NAME, GENERIC_READ|GENERIC_WRITE,
  174. FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
  175. 0, 0 )) == INVALID_HANDLE_VALUE)
  176. WINE_ERR( "failed to open mount manager err %u\n", GetLastError() );
  177. return ret;
  178. }
  179. /* Load currently defined drives into the drives array */
  180. BOOL load_drives(void)
  181. {
  182. DWORD i, size = 1024;
  183. HANDLE mgr;
  184. WCHAR root[] = L"A:\\";
  185. if ((mgr = open_mountmgr()) == INVALID_HANDLE_VALUE) return FALSE;
  186. while (root[0] <= 'Z')
  187. {
  188. struct mountmgr_unix_drive input;
  189. struct mountmgr_unix_drive *data;
  190. if (!(data = HeapAlloc( GetProcessHeap(), 0, size ))) break;
  191. memset( &input, 0, sizeof(input) );
  192. input.letter = root[0];
  193. if (DeviceIoControl( mgr, IOCTL_MOUNTMGR_QUERY_UNIX_DRIVE, &input, sizeof(input),
  194. data, size, NULL, NULL ))
  195. {
  196. char *unixpath = NULL, *device = NULL;
  197. WCHAR volname[MAX_PATH];
  198. DWORD serial;
  199. if (data->mount_point_offset) unixpath = (char *)data + data->mount_point_offset;
  200. if (data->device_offset) device = (char *)data + data->device_offset;
  201. if (!GetVolumeInformationW( root, volname, ARRAY_SIZE(volname),
  202. &serial, NULL, NULL, NULL, 0 ))
  203. {
  204. volname[0] = 0;
  205. serial = 0;
  206. }
  207. if (unixpath) /* FIXME: handle unmounted drives too */
  208. add_drive( root[0], unixpath, device, volname, serial, get_drive_type(root[0]) );
  209. root[0]++;
  210. }
  211. else
  212. {
  213. if (GetLastError() == ERROR_MORE_DATA) size = data->size;
  214. else root[0]++; /* skip this drive */
  215. }
  216. HeapFree( GetProcessHeap(), 0, data );
  217. }
  218. /* reset modified flags */
  219. for (i = 0; i < 26; i++) drives[i].modified = FALSE;
  220. CloseHandle( mgr );
  221. return TRUE;
  222. }
  223. /* some of this code appears to be broken by bugs in Wine: the label
  224. * setting code has no effect, for instance */
  225. void apply_drive_changes(void)
  226. {
  227. int i;
  228. HANDLE mgr;
  229. DWORD len;
  230. struct mountmgr_unix_drive *ioctl;
  231. WINE_TRACE("\n");
  232. if ((mgr = open_mountmgr()) == INVALID_HANDLE_VALUE) return;
  233. /* add each drive and remove as we go */
  234. for(i = 0; i < 26; i++)
  235. {
  236. if (!drives[i].modified) continue;
  237. drives[i].modified = FALSE;
  238. len = sizeof(*ioctl);
  239. if (drives[i].in_use)
  240. {
  241. len += strlen(drives[i].unixpath) + 1;
  242. if (drives[i].device) len += strlen(drives[i].device) + 1;
  243. }
  244. if (!(ioctl = HeapAlloc( GetProcessHeap(), 0, len ))) continue;
  245. ioctl->size = len;
  246. ioctl->letter = 'a' + i;
  247. ioctl->device_offset = 0;
  248. if (drives[i].in_use)
  249. {
  250. char *ptr = (char *)(ioctl + 1);
  251. ioctl->type = drives[i].type;
  252. strcpy( ptr, drives[i].unixpath );
  253. ioctl->mount_point_offset = ptr - (char *)ioctl;
  254. if (drives[i].device)
  255. {
  256. ptr += strlen(ptr) + 1;
  257. strcpy( ptr, drives[i].device );
  258. ioctl->device_offset = ptr - (char *)ioctl;
  259. }
  260. }
  261. else
  262. {
  263. ioctl->type = DRIVE_NO_ROOT_DIR;
  264. ioctl->mount_point_offset = 0;
  265. }
  266. if (DeviceIoControl( mgr, IOCTL_MOUNTMGR_DEFINE_UNIX_DRIVE, ioctl, len, NULL, 0, NULL, NULL ))
  267. {
  268. set_drive_label( drives[i].letter, drives[i].label );
  269. if (drives[i].in_use) set_drive_serial( drives[i].letter, drives[i].serial );
  270. WINE_TRACE( "set drive %c: to %s type %u\n", 'a' + i,
  271. wine_dbgstr_a(drives[i].unixpath), drives[i].type );
  272. }
  273. else WINE_WARN( "failed to set drive %c: to %s type %u err %u\n", 'a' + i,
  274. wine_dbgstr_a(drives[i].unixpath), drives[i].type, GetLastError() );
  275. HeapFree( GetProcessHeap(), 0, ioctl );
  276. }
  277. CloseHandle( mgr );
  278. }
  279. void query_shell_folder( const WCHAR *path, char *dest, unsigned int len )
  280. {
  281. UNICODE_STRING nt_name;
  282. HANDLE mgr;
  283. if ((mgr = open_mountmgr()) == INVALID_HANDLE_VALUE) return;
  284. if (!RtlDosPathNameToNtPathName_U( path, &nt_name, NULL, NULL ))
  285. {
  286. CloseHandle( mgr );
  287. return;
  288. }
  289. DeviceIoControl( mgr, IOCTL_MOUNTMGR_QUERY_SHELL_FOLDER, nt_name.Buffer, nt_name.Length,
  290. dest, len, NULL, NULL );
  291. RtlFreeUnicodeString( &nt_name );
  292. }
  293. void set_shell_folder( const WCHAR *path, const char *dest )
  294. {
  295. struct mountmgr_shell_folder *ioctl;
  296. UNICODE_STRING nt_name;
  297. HANDLE mgr;
  298. DWORD len;
  299. if ((mgr = open_mountmgr()) == INVALID_HANDLE_VALUE) return;
  300. if (!RtlDosPathNameToNtPathName_U( path, &nt_name, NULL, NULL ))
  301. {
  302. CloseHandle( mgr );
  303. return;
  304. }
  305. len = sizeof(*ioctl) + nt_name.Length;
  306. if (dest) len += strlen(dest) + 1;
  307. if (!(ioctl = HeapAlloc( GetProcessHeap(), 0, len ))) return;
  308. ioctl->create_backup = TRUE;
  309. ioctl->folder_offset = sizeof(*ioctl);
  310. ioctl->folder_size = nt_name.Length;
  311. memcpy( (char *)ioctl + ioctl->folder_offset, nt_name.Buffer, nt_name.Length );
  312. if (dest)
  313. {
  314. ioctl->symlink_offset = ioctl->folder_offset + ioctl->folder_size;
  315. strcpy( (char *)ioctl + ioctl->symlink_offset, dest );
  316. }
  317. else ioctl->symlink_offset = 0;
  318. DeviceIoControl( mgr, IOCTL_MOUNTMGR_DEFINE_SHELL_FOLDER, ioctl, len, NULL, 0, NULL, NULL );
  319. HeapFree( GetProcessHeap(), 0, ioctl );
  320. RtlFreeUnicodeString( &nt_name );
  321. }