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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
32 #include <wine/debug.h>
43 WINE_DEFAULT_DEBUG_CHANNEL(winecfg
);
45 struct drive drives
[26]; /* one for each drive letter */
47 static inline int letter_to_index(char letter
)
49 return (toupper(letter
) - 'A');
52 /* This function produces a mask for each drive letter that isn't
53 * currently used. Each bit of the long result represents a letter,
54 * with A being the least significant bit, and Z being the most
57 * To calculate this, we loop over each letter, and see if we can get
58 * a drive entry for it. If so, we set the appropriate bit. At the
59 * end, we flip each bit, to give the desired result.
61 * The letter parameter is always marked as being available. This is
62 * so the edit dialog can display the currently used drive letter
63 * alongside the available ones.
65 long drive_available_mask(char letter
)
73 for(i
= 0; i
< 26; i
++)
75 if (!drives
[i
].in_use
) continue;
76 result
|= (1 << (toupper(drives
[i
].letter
) - 'A'));
80 if (letter
) result
|= DRIVE_MASK_BIT(letter
);
82 WINE_TRACE("finished drive letter loop with %lx\n", result
);
86 BOOL
add_drive(char letter
, char *targetpath
, char *label
, char *serial
, unsigned int type
)
88 int driveIndex
= letter_to_index(letter
);
90 if(drives
[driveIndex
].in_use
)
93 WINE_TRACE("letter == '%c', unixpath == '%s', label == '%s', serial == '%s', type == %d\n",
94 letter
, targetpath
, label
, serial
, type
);
96 drives
[driveIndex
].letter
= toupper(letter
);
97 drives
[driveIndex
].unixpath
= strdupA(targetpath
);
98 drives
[driveIndex
].label
= strdupA(label
);
99 drives
[driveIndex
].serial
= strdupA(serial
);
100 drives
[driveIndex
].type
= type
;
101 drives
[driveIndex
].in_use
= TRUE
;
106 /* deallocates the contents of the drive. does not free the drive itself */
107 void delete_drive(struct drive
*d
)
109 HeapFree(GetProcessHeap(), 0, d
->unixpath
);
111 HeapFree(GetProcessHeap(), 0, d
->label
);
113 HeapFree(GetProcessHeap(), 0, d
->serial
);
121 /* currently unused, but if users have this burning desire to be able to rename drives,
122 we can put it back in.
125 BOOL
copyDrive(struct drive
*pSrc
, struct drive
*pDst
)
129 WINE_TRACE("pDst already in use\n");
133 if(!pSrc
->unixpath
) WINE_TRACE("!pSrc->unixpath\n");
134 if(!pSrc
->label
) WINE_TRACE("!pSrc->label\n");
135 if(!pSrc
->serial
) WINE_TRACE("!pSrc->serial\n");
137 pDst
->unixpath
= strdupA(pSrc
->unixpath
);
138 pDst
->label
= strdupA(pSrc
->label
);
139 pDst
->serial
= strdupA(pSrc
->serial
);
140 pDst
->type
= pSrc
->type
;
146 BOOL
moveDrive(struct drive
*pSrc
, struct drive
*pDst
)
148 WINE_TRACE("pSrc->letter == %c, pDst->letter == %c\n", pSrc
->letter
, pDst
->letter
);
150 if(!copyDrive(pSrc
, pDst
))
152 WINE_TRACE("copyDrive failed\n");
162 /* Load currently defined drives into the drives array */
167 int drivecount
= 0, i
;
169 static const int arraysize
= 512;
173 /* FIXME: broken symlinks in $WINEPREFIX/dosdevices will not be
174 returned by this API, so we need to handle that */
176 /* setup the drives array */
177 dev
= devices
= HeapAlloc(GetProcessHeap(), 0, arraysize
);
178 len
= GetLogicalDriveStrings(arraysize
, devices
);
180 /* make all devices unused */
181 for (i
= 0; i
< 26; i
++)
183 drives
[i
].letter
= 'A' + i
;
184 drives
[i
].in_use
= FALSE
;
186 HeapFree(GetProcessHeap(), 0, drives
[i
].unixpath
);
187 drives
[i
].unixpath
= NULL
;
189 HeapFree(GetProcessHeap(), 0, drives
[i
].label
);
190 drives
[i
].label
= NULL
;
192 HeapFree(GetProcessHeap(), 0, drives
[i
].serial
);
193 drives
[i
].serial
= NULL
;
196 /* work backwards through the result of GetLogicalDriveStrings */
199 char volname
[512]; /* volume name */
205 char targetpath
[256];
208 *devices
= toupper(*devices
);
210 WINE_TRACE("devices == '%s'\n", devices
);
214 retval
= GetVolumeInformation(devices
,
224 WINE_ERR("GetVolumeInformation() for '%s' failed, setting serial to 0\n", devices
);
229 WINE_TRACE("serial: '0x%lX'\n", serial
);
231 /* build rootpath for GetDriveType() */
232 lstrcpynA(rootpath
, devices
, sizeof(rootpath
));
233 pathlen
= strlen(rootpath
);
235 /* ensure that we have a backslash on the root path */
236 if ((rootpath
[pathlen
- 1] != '\\') && (pathlen
< sizeof(rootpath
)))
238 rootpath
[pathlen
] = '\\';
239 rootpath
[pathlen
+ 1] = 0;
242 /* QueryDosDevice() requires no trailing backslash */
243 lstrcpynA(simplepath
, devices
, 3);
244 QueryDosDevice(simplepath
, targetpath
, sizeof(targetpath
));
246 /* targetpath may have forward slashes rather than backslashes, so correct */
248 do if (*c
== '\\') *c
= '/'; while (*c
++);
250 snprintf(serialstr
, sizeof(serialstr
), "%lX", serial
);
251 WINE_TRACE("serialstr: '%s'\n", serialstr
);
252 add_drive(*devices
, targetpath
, volname
, serialstr
, GetDriveType(rootpath
));
254 len
-= strlen(devices
);
255 devices
+= strlen(devices
);
257 /* skip over any nulls */
258 while ((*devices
== 0) && (len
))
267 WINE_TRACE("found %d drives\n", drivecount
);
269 HeapFree(GetProcessHeap(), 0, dev
);
272 /* some of this code appears to be broken by bugs in Wine: the label
273 * setting code has no effect, for instance */
274 void apply_drive_changes()
278 CHAR targetpath
[256];
280 CHAR volumeNameBuffer
[512];
282 DWORD maxComponentLength
;
283 DWORD fileSystemFlags
;
284 CHAR fileSystemName
[128];
290 /* add each drive and remove as we go */
291 for(i
= 0; i
< 26; i
++)
293 defineDevice
= FALSE
;
295 snprintf(devicename
, sizeof(devicename
), "%c:", 'A' + i
);
298 if(QueryDosDevice(devicename
, targetpath
, sizeof(targetpath
)))
302 /* correct the slashes in the path to be UNIX style */
303 while ((cursor
= strchr(targetpath
, '\\'))) *cursor
= '/';
308 /* if we found a drive and have a drive then compare things */
309 if(foundDrive
&& drives
[i
].in_use
)
311 char newSerialNumberText
[256];
313 volumeNameBuffer
[0] = 0;
315 WINE_TRACE("drives[i].letter: '%c'\n", drives
[i
].letter
);
317 snprintf(devicename
, sizeof(devicename
), "%c:\\", 'A' + i
);
318 retval
= GetVolumeInformation(devicename
,
320 sizeof(volumeNameBuffer
),
325 sizeof(fileSystemName
));
328 WINE_TRACE(" GetVolumeInformation() for '%s' failed\n", devicename
);
329 WINE_TRACE(" Skipping this drive\n");
331 continue; /* skip this drive */
334 snprintf(newSerialNumberText
, sizeof(newSerialNumberText
), "%lX", serialNumber
);
336 WINE_TRACE(" current path: '%s', new path: '%s'\n",
337 targetpath
, drives
[i
].unixpath
);
338 WINE_TRACE(" current label: '%s', new label: '%s'\n",
339 volumeNameBuffer
, drives
[i
].label
);
340 WINE_TRACE(" current serial: '%s', new serial: '%s'\n",
341 newSerialNumberText
, drives
[i
].serial
);
343 /* compare to what we have */
344 /* do we have the same targetpath? */
345 if(strcmp(drives
[i
].unixpath
, targetpath
) ||
346 strcmp(drives
[i
].label
, volumeNameBuffer
) ||
347 strcmp(drives
[i
].serial
, newSerialNumberText
))
350 WINE_TRACE(" making changes to drive '%s'\n", devicename
);
354 WINE_TRACE(" no changes to drive '%s'\n", devicename
);
357 else if(foundDrive
&& !drives
[i
].in_use
)
360 char driveValue
[256];
362 /* remove this drive */
363 if(!DefineDosDevice(DDD_REMOVE_DEFINITION
, devicename
, drives
[i
].unixpath
))
365 WINE_ERR("unable to remove devicename of '%s', targetpath of '%s'\n",
366 devicename
, drives
[i
].unixpath
);
371 WINE_TRACE("removed devicename of '%s', targetpath of '%s'\n",
372 devicename
, drives
[i
].unixpath
);
375 retval
= RegOpenKey(HKEY_LOCAL_MACHINE
, "Software\\Wine\\Drives", &hKey
);
376 if (retval
!= ERROR_SUCCESS
)
378 WINE_TRACE("Unable to open '%s'\n", "Software\\Wine\\Drives");
382 snprintf(driveValue
, sizeof(driveValue
), "%c:", toupper(drives
[i
].letter
));
384 retval
= RegDeleteValue(hKey
, driveValue
);
387 else if(drives
[i
].in_use
) /* foundDrive must be false from the above check */
392 /* adding and modifying are the same steps */
400 char driveValue
[256];
402 /* define this drive */
403 /* DefineDosDevice() requires that NO trailing slash be present */
404 snprintf(devicename
, sizeof(devicename
), "%c:", 'A' + i
);
405 if(!DefineDosDevice(DDD_RAW_TARGET_PATH
, devicename
, drives
[i
].unixpath
))
407 WINE_ERR(" unable to define devicename of '%s', targetpath of '%s'\n",
408 devicename
, drives
[i
].unixpath
);
413 WINE_TRACE(" added devicename of '%s', targetpath of '%s'\n",
414 devicename
, drives
[i
].unixpath
);
416 /* SetVolumeLabel() requires a trailing slash */
417 snprintf(devicename
, sizeof(devicename
), "%c:\\", 'A' + i
);
418 if(!SetVolumeLabel(devicename
, drives
[i
].label
))
420 WINE_WARN("unable to set volume label for devicename of '%s', label of '%s'\n",
421 devicename
, drives
[i
].label
);
426 WINE_TRACE(" set volume label for devicename of '%s', label of '%s'\n",
427 devicename
, drives
[i
].label
);
431 /* Set the drive type in the registry */
432 if(drives
[i
].type
== DRIVE_FIXED
)
434 else if(drives
[i
].type
== DRIVE_REMOTE
)
435 typeText
= "network";
436 else if(drives
[i
].type
== DRIVE_REMOVABLE
)
438 else /* must be DRIVE_CDROM */
442 snprintf(driveValue
, sizeof(driveValue
), "%c:", toupper(drives
[i
].letter
));
444 retval
= RegOpenKey(HKEY_LOCAL_MACHINE
,
445 "Software\\Wine\\Drives",
448 if(retval
!= ERROR_SUCCESS
)
450 WINE_TRACE(" Unable to open '%s'\n", "Software\\Wine\\Drives");
454 retval
= RegSetValueEx(
460 strlen(typeText
) + 1);
461 if(retval
!= ERROR_SUCCESS
)
463 WINE_TRACE(" Unable to set value of '%s' to '%s'\n",
464 driveValue
, typeText
);
468 WINE_TRACE(" Finished setting value of '%s' to '%s'\n",
469 driveValue
, typeText
);
474 /* Set the drive serial number via a .windows-serial file in */
475 /* the targetpath directory */
476 snprintf(filename
, sizeof(filename
), "%c:\\.windows-serial", drives
[i
].letter
);
477 WINE_TRACE(" Putting serial number of '%ld' into file '%s'\n",
478 serialNumber
, filename
);
479 hFile
= CreateFile(filename
,
484 FILE_ATTRIBUTE_NORMAL
,
486 if (hFile
!= INVALID_HANDLE_VALUE
)
489 WINE_TRACE(" writing serial number of '%s'\n", drives
[i
].serial
);
492 strlen(drives
[i
].serial
),
504 WINE_TRACE(" CreateFile() error with file '%s'\n", filename
);