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
;
185 /* work backwards through the result of GetLogicalDriveStrings */
188 char volname
[512]; /* volume name */
194 char targetpath
[256];
197 *devices
= toupper(*devices
);
199 WINE_TRACE("devices == '%s'\n", devices
);
203 retval
= GetVolumeInformation(devices
,
213 WINE_ERR("GetVolumeInformation() for '%s' failed, setting serial to 0\n", devices
);
218 WINE_TRACE("serial: '0x%lX'\n", serial
);
220 /* build rootpath for GetDriveType() */
221 strncpy(rootpath
, devices
, sizeof(rootpath
));
222 pathlen
= strlen(rootpath
);
224 /* ensure that we have a backslash on the root path */
225 if ((rootpath
[pathlen
- 1] != '\\') && (pathlen
< sizeof(rootpath
)))
227 rootpath
[pathlen
] = '\\';
228 rootpath
[pathlen
+ 1] = 0;
231 strncpy(simplepath
, devices
, 2); /* QueryDosDevice() requires no trailing backslash */
233 QueryDosDevice(simplepath
, targetpath
, sizeof(targetpath
));
235 /* targetpath may have forward slashes rather than backslashes, so correct */
237 do if (*c
== '\\') *c
= '/'; while (*c
++);
239 snprintf(serialstr
, sizeof(serialstr
), "%lX", serial
);
240 WINE_TRACE("serialstr: '%s'\n", serialstr
);
241 add_drive(*devices
, targetpath
, volname
, serialstr
, GetDriveType(rootpath
));
243 len
-= strlen(devices
);
244 devices
+= strlen(devices
);
246 /* skip over any nulls */
247 while ((*devices
== 0) && (len
))
256 WINE_TRACE("found %d drives\n", drivecount
);
258 HeapFree(GetProcessHeap(), 0, dev
);
261 /* some of this code appears to be broken by bugs in Wine: the label
262 * setting code has no effect, for instance */
263 void apply_drive_changes()
267 CHAR targetpath
[256];
269 CHAR volumeNameBuffer
[512];
271 DWORD maxComponentLength
;
272 DWORD fileSystemFlags
;
273 CHAR fileSystemName
[128];
279 /* add each drive and remove as we go */
280 for(i
= 0; i
< 26; i
++)
282 defineDevice
= FALSE
;
284 snprintf(devicename
, sizeof(devicename
), "%c:", 'A' + i
);
287 if(QueryDosDevice(devicename
, targetpath
, sizeof(targetpath
)))
292 /* if we found a drive and have a drive then compare things */
293 if(foundDrive
&& drives
[i
].in_use
)
295 char newSerialNumberText
[256];
297 volumeNameBuffer
[0] = 0;
299 WINE_TRACE("drives[i].letter: '%c'\n", drives
[i
].letter
);
301 snprintf(devicename
, sizeof(devicename
), "%c:\\", 'A' + i
);
302 retval
= GetVolumeInformation(devicename
,
304 sizeof(volumeNameBuffer
),
309 sizeof(fileSystemName
));
312 WINE_TRACE(" GetVolumeInformation() for '%s' failed\n", devicename
);
313 WINE_TRACE(" Skipping this drive\n");
315 continue; /* skip this drive */
318 snprintf(newSerialNumberText
, sizeof(newSerialNumberText
), "%lX", serialNumber
);
320 WINE_TRACE(" current path: '%s', new path: '%s'\n",
321 targetpath
, drives
[i
].unixpath
);
322 WINE_TRACE(" current label: '%s', new label: '%s'\n",
323 volumeNameBuffer
, drives
[i
].label
);
324 WINE_TRACE(" current serial: '%s', new serial: '%s'\n",
325 newSerialNumberText
, drives
[i
].serial
);
327 /* compare to what we have */
328 /* do we have the same targetpath? */
329 if(strcmp(drives
[i
].unixpath
, targetpath
) ||
330 strcmp(drives
[i
].label
, volumeNameBuffer
) ||
331 strcmp(drives
[i
].serial
, newSerialNumberText
))
334 WINE_TRACE(" making changes to drive '%s'\n", devicename
);
338 WINE_TRACE(" no changes to drive '%s'\n", devicename
);
341 else if(foundDrive
&& !drives
[i
].in_use
)
343 /* remove this drive */
344 if(!DefineDosDevice(DDD_REMOVE_DEFINITION
, devicename
, drives
[i
].unixpath
))
346 WINE_ERR("unable to remove devicename of '%s', targetpath of '%s'\n",
347 devicename
, drives
[i
].unixpath
);
352 WINE_TRACE("removed devicename of '%s', targetpath of '%s'\n",
353 devicename
, drives
[i
].unixpath
);
356 else if(drives
[i
].in_use
) /* foundDrive must be false from the above check */
361 /* adding and modifying are the same steps */
369 char driveValue
[256];
371 /* define this drive */
372 /* DefineDosDevice() requires that NO trailing slash be present */
373 snprintf(devicename
, sizeof(devicename
), "%c:", 'A' + i
);
374 if(!DefineDosDevice(DDD_RAW_TARGET_PATH
, devicename
, drives
[i
].unixpath
))
376 WINE_ERR(" unable to define devicename of '%s', targetpath of '%s'\n",
377 devicename
, drives
[i
].unixpath
);
382 WINE_TRACE(" added devicename of '%s', targetpath of '%s'\n",
383 devicename
, drives
[i
].unixpath
);
385 /* SetVolumeLabel() requires a trailing slash */
386 snprintf(devicename
, sizeof(devicename
), "%c:\\", 'A' + i
);
387 if(!SetVolumeLabel(devicename
, drives
[i
].label
))
389 WINE_ERR("unable to set volume label for devicename of '%s', label of '%s'\n",
390 devicename
, drives
[i
].label
);
395 WINE_TRACE(" set volume label for devicename of '%s', label of '%s'\n",
396 devicename
, drives
[i
].label
);
400 /* Set the drive type in the registry */
401 if(drives
[i
].type
== DRIVE_FIXED
)
403 else if(drives
[i
].type
== DRIVE_REMOTE
)
404 typeText
= "network";
405 else if(drives
[i
].type
== DRIVE_REMOVABLE
)
407 else /* must be DRIVE_CDROM */
411 snprintf(driveValue
, sizeof(driveValue
), "%c:", toupper(drives
[i
].letter
));
413 retval
= RegOpenKey(HKEY_LOCAL_MACHINE
,
414 "Software\\Wine\\Drives",
417 if(retval
!= ERROR_SUCCESS
)
419 WINE_TRACE(" Unable to open '%s'\n", "Software\\Wine\\Drives");
423 retval
= RegSetValueEx(
429 strlen(typeText
) + 1);
430 if(retval
!= ERROR_SUCCESS
)
432 WINE_TRACE(" Unable to set value of '%s' to '%s'\n",
433 driveValue
, typeText
);
437 WINE_TRACE(" Finished setting value of '%s' to '%s'\n",
438 driveValue
, typeText
);
443 /* Set the drive serial number via a .windows-serial file in */
444 /* the targetpath directory */
445 snprintf(filename
, sizeof(filename
), "%c:\\.windows-serial", drives
[i
].letter
);
446 WINE_TRACE(" Putting serial number of '%ld' into file '%s'\n",
447 serialNumber
, filename
);
448 hFile
= CreateFile(filename
,
453 FILE_ATTRIBUTE_NORMAL
,
455 if (hFile
!= INVALID_HANDLE_VALUE
)
457 WINE_TRACE(" writing serial number of '%s'\n", drives
[i
].serial
);
460 strlen(drives
[i
].serial
),
472 WINE_TRACE(" CreateFile() error with file '%s'\n", filename
);
476 /* if this drive is in use we should free it up */
479 delete_drive(&drives
[i
]); /* free up the string memory */