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
33 #include <wine/debug.h>
44 WINE_DEFAULT_DEBUG_CHANNEL(winecfg
);
46 struct drive drives
[26]; /* one for each drive letter */
48 static inline int letter_to_index(char letter
)
50 return (toupper(letter
) - 'A');
53 /* This function produces a mask for each drive letter that isn't
54 * currently used. Each bit of the long result represents a letter,
55 * with A being the least significant bit, and Z being the most
58 * To calculate this, we loop over each letter, and see if we can get
59 * a drive entry for it. If so, we set the appropriate bit. At the
60 * end, we flip each bit, to give the desired result.
62 * The letter parameter is always marked as being available. This is
63 * so the edit dialog can display the currently used drive letter
64 * alongside the available ones.
66 long drive_available_mask(char letter
)
74 for(i
= 0; i
< 26; i
++)
76 if (!drives
[i
].in_use
) continue;
77 result
|= (1 << (toupper(drives
[i
].letter
) - 'A'));
81 if (letter
) result
|= DRIVE_MASK_BIT(letter
);
83 WINE_TRACE("finished drive letter loop with %lx\n", result
);
87 BOOL
add_drive(char letter
, char *targetpath
, char *label
, char *serial
, uint type
)
89 int driveIndex
= letter_to_index(letter
);
91 if(drives
[driveIndex
].in_use
)
94 WINE_TRACE("letter == '%c', unixpath == '%s', label == '%s', serial == '%s', type == %d\n",
95 letter
, targetpath
, label
, serial
, type
);
97 drives
[driveIndex
].letter
= toupper(letter
);
98 drives
[driveIndex
].unixpath
= strdupA(targetpath
);
99 drives
[driveIndex
].label
= strdupA(label
);
100 drives
[driveIndex
].serial
= strdupA(serial
);
101 drives
[driveIndex
].type
= type
;
102 drives
[driveIndex
].in_use
= TRUE
;
107 /* deallocates the contents of the drive. does not free the drive itself */
108 void delete_drive(struct drive
*d
)
110 HeapFree(GetProcessHeap(), 0, d
->unixpath
);
111 HeapFree(GetProcessHeap(), 0, d
->label
);
112 HeapFree(GetProcessHeap(), 0, d
->serial
);
119 /* currently unused, but if users have this burning desire to be able to rename drives,
120 we can put it back in.
123 BOOL
copyDrive(struct drive
*pSrc
, struct drive
*pDst
)
127 WINE_TRACE("pDst already in use\n");
131 if(!pSrc
->unixpath
) WINE_TRACE("!pSrc->unixpath\n");
132 if(!pSrc
->label
) WINE_TRACE("!pSrc->label\n");
133 if(!pSrc
->serial
) WINE_TRACE("!pSrc->serial\n");
135 pDst
->unixpath
= strdupA(pSrc
->unixpath
);
136 pDst
->label
= strdupA(pSrc
->label
);
137 pDst
->serial
= strdupA(pSrc
->serial
);
138 pDst
->type
= pSrc
->type
;
144 BOOL
moveDrive(struct drive
*pSrc
, struct drive
*pDst
)
146 WINE_TRACE("pSrc->letter == %c, pDst->letter == %c\n", pSrc
->letter
, pDst
->letter
);
148 if(!copyDrive(pSrc
, pDst
))
150 WINE_TRACE("copyDrive failed\n");
160 /* Load currently defined drives into the drives array */
165 int drivecount
= 0, i
;
167 static const int arraysize
= 512;
171 /* FIXME: broken symlinks in $WINEPREFIX/dosdevices will not be
172 returned by this API, so we need to handle that */
174 /* setup the drives array */
175 dev
= devices
= HeapAlloc(GetProcessHeap(), 0, arraysize
);
176 len
= GetLogicalDriveStrings(arraysize
, devices
);
178 /* make all devices unused */
179 for (i
= 0; i
< 26; i
++)
181 drives
[i
].letter
= 'A' + i
;
182 drives
[i
].in_use
= FALSE
;
184 HeapFree(GetProcessHeap(), 0, drives
[i
].unixpath
);
185 drives
[i
].unixpath
= NULL
;
187 HeapFree(GetProcessHeap(), 0, drives
[i
].label
);
188 drives
[i
].label
= NULL
;
190 HeapFree(GetProcessHeap(), 0, drives
[i
].serial
);
191 drives
[i
].serial
= NULL
;
194 /* work backwards through the result of GetLogicalDriveStrings */
197 char volname
[512]; /* volume name */
203 char targetpath
[256];
206 *devices
= toupper(*devices
);
208 WINE_TRACE("devices == '%s'\n", devices
);
212 retval
= GetVolumeInformation(devices
,
222 WINE_ERR("GetVolumeInformation() for '%s' failed, setting serial to 0\n", devices
);
227 WINE_TRACE("serial: '0x%lX'\n", serial
);
229 /* build rootpath for GetDriveType() */
230 strncpy(rootpath
, devices
, sizeof(rootpath
));
231 pathlen
= strlen(rootpath
);
233 /* ensure that we have a backslash on the root path */
234 if ((rootpath
[pathlen
- 1] != '\\') && (pathlen
< sizeof(rootpath
)))
236 rootpath
[pathlen
] = '\\';
237 rootpath
[pathlen
+ 1] = 0;
240 strncpy(simplepath
, devices
, 2); /* QueryDosDevice() requires no trailing backslash */
242 QueryDosDevice(simplepath
, targetpath
, sizeof(targetpath
));
244 /* targetpath may have forward slashes rather than backslashes, so correct */
246 do if (*c
== '\\') *c
= '/'; while (*c
++);
248 snprintf(serialstr
, sizeof(serialstr
), "%lX", serial
);
249 WINE_TRACE("serialstr: '%s'\n", serialstr
);
250 add_drive(*devices
, targetpath
, volname
, serialstr
, GetDriveType(rootpath
));
252 len
-= strlen(devices
);
253 devices
+= strlen(devices
);
255 /* skip over any nulls */
256 while ((*devices
== 0) && (len
))
265 WINE_TRACE("found %d drives\n", drivecount
);
267 HeapFree(GetProcessHeap(), 0, dev
);
270 /* some of this code appears to be broken by bugs in Wine: the label
271 * setting code has no effect, for instance */
272 void apply_drive_changes()
276 CHAR targetpath
[256];
278 CHAR volumeNameBuffer
[512];
280 DWORD maxComponentLength
;
281 DWORD fileSystemFlags
;
282 CHAR fileSystemName
[128];
288 /* add each drive and remove as we go */
289 for(i
= 0; i
< 26; i
++)
291 defineDevice
= FALSE
;
293 snprintf(devicename
, sizeof(devicename
), "%c:", 'A' + i
);
296 if(QueryDosDevice(devicename
, targetpath
, sizeof(targetpath
)))
301 /* if we found a drive and have a drive then compare things */
302 if(foundDrive
&& drives
[i
].in_use
)
304 char newSerialNumberText
[256];
306 volumeNameBuffer
[0] = 0;
308 WINE_TRACE("drives[i].letter: '%c'\n", drives
[i
].letter
);
310 snprintf(devicename
, sizeof(devicename
), "%c:\\", 'A' + i
);
311 retval
= GetVolumeInformation(devicename
,
313 sizeof(volumeNameBuffer
),
318 sizeof(fileSystemName
));
321 WINE_TRACE(" GetVolumeInformation() for '%s' failed\n", devicename
);
322 WINE_TRACE(" Skipping this drive\n");
324 continue; /* skip this drive */
327 snprintf(newSerialNumberText
, sizeof(newSerialNumberText
), "%lX", serialNumber
);
329 WINE_TRACE(" current path: '%s', new path: '%s'\n",
330 targetpath
, drives
[i
].unixpath
);
331 WINE_TRACE(" current label: '%s', new label: '%s'\n",
332 volumeNameBuffer
, drives
[i
].label
);
333 WINE_TRACE(" current serial: '%s', new serial: '%s'\n",
334 newSerialNumberText
, drives
[i
].serial
);
336 /* compare to what we have */
337 /* do we have the same targetpath? */
338 if(strcmp(drives
[i
].unixpath
, targetpath
) ||
339 strcmp(drives
[i
].label
, volumeNameBuffer
) ||
340 strcmp(drives
[i
].serial
, newSerialNumberText
))
343 WINE_TRACE(" making changes to drive '%s'\n", devicename
);
347 WINE_TRACE(" no changes to drive '%s'\n", devicename
);
350 else if(foundDrive
&& !drives
[i
].in_use
)
352 /* remove this drive */
353 if(!DefineDosDevice(DDD_REMOVE_DEFINITION
, devicename
, drives
[i
].unixpath
))
355 WINE_ERR("unable to remove devicename of '%s', targetpath of '%s'\n",
356 devicename
, drives
[i
].unixpath
);
361 WINE_TRACE("removed devicename of '%s', targetpath of '%s'\n",
362 devicename
, drives
[i
].unixpath
);
365 else if(drives
[i
].in_use
) /* foundDrive must be false from the above check */
370 /* adding and modifying are the same steps */
378 char driveValue
[256];
380 /* define this drive */
381 /* DefineDosDevice() requires that NO trailing slash be present */
382 snprintf(devicename
, sizeof(devicename
), "%c:", 'A' + i
);
383 if(!DefineDosDevice(DDD_RAW_TARGET_PATH
, devicename
, drives
[i
].unixpath
))
385 WINE_ERR(" unable to define devicename of '%s', targetpath of '%s'\n",
386 devicename
, drives
[i
].unixpath
);
391 WINE_TRACE(" added devicename of '%s', targetpath of '%s'\n",
392 devicename
, drives
[i
].unixpath
);
394 /* SetVolumeLabel() requires a trailing slash */
395 snprintf(devicename
, sizeof(devicename
), "%c:\\", 'A' + i
);
396 if(!SetVolumeLabel(devicename
, drives
[i
].label
))
398 WINE_ERR("unable to set volume label for devicename of '%s', label of '%s'\n",
399 devicename
, drives
[i
].label
);
404 WINE_TRACE(" set volume label for devicename of '%s', label of '%s'\n",
405 devicename
, drives
[i
].label
);
409 /* Set the drive type in the registry */
410 if(drives
[i
].type
== DRIVE_FIXED
)
412 else if(drives
[i
].type
== DRIVE_REMOTE
)
413 typeText
= "network";
414 else if(drives
[i
].type
== DRIVE_REMOVABLE
)
416 else /* must be DRIVE_CDROM */
420 snprintf(driveValue
, sizeof(driveValue
), "%c:", toupper(drives
[i
].letter
));
422 retval
= RegOpenKey(HKEY_LOCAL_MACHINE
,
423 "Software\\Wine\\Drives",
426 if(retval
!= ERROR_SUCCESS
)
428 WINE_TRACE(" Unable to open '%s'\n", "Software\\Wine\\Drives");
432 retval
= RegSetValueEx(
438 strlen(typeText
) + 1);
439 if(retval
!= ERROR_SUCCESS
)
441 WINE_TRACE(" Unable to set value of '%s' to '%s'\n",
442 driveValue
, typeText
);
446 WINE_TRACE(" Finished setting value of '%s' to '%s'\n",
447 driveValue
, typeText
);
452 /* Set the drive serial number via a .windows-serial file in */
453 /* the targetpath directory */
454 snprintf(filename
, sizeof(filename
), "%c:\\.windows-serial", drives
[i
].letter
);
455 WINE_TRACE(" Putting serial number of '%ld' into file '%s'\n",
456 serialNumber
, filename
);
457 hFile
= CreateFile(filename
,
462 FILE_ATTRIBUTE_NORMAL
,
464 if (hFile
!= INVALID_HANDLE_VALUE
)
467 WINE_TRACE(" writing serial number of '%s'\n", drives
[i
].serial
);
470 strlen(drives
[i
].serial
),
482 WINE_TRACE(" CreateFile() error with file '%s'\n", filename
);