drive.c 10.9 KB
Newer Older
1
/*
Mike Hearn's avatar
Mike Hearn committed
2
 * Drive management code
3 4
 *
 * Copyright 2003 Mark Westcott
Mike Hearn's avatar
Mike Hearn committed
5
 * Copyright 2003-2004 Mike Hearn
6
 * Copyright 2004 Chris Morgan
7 8 9 10 11 12 13 14 15 16 17 18 19
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * 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
 *
 */

24 25 26
#include "config.h"
#include "wine/port.h"

27 28
#include <assert.h>
#include <stdarg.h>
29
#include <stdio.h>
30 31
#include <string.h>

32 33
#include <ntstatus.h>
#define WIN32_NO_STATUS
34 35
#include <windef.h>
#include <winbase.h>
36 37
#include <winternl.h>
#include <winioctl.h>
38 39
#include <winreg.h>
#include <wine/debug.h>
40
#include <shellapi.h>
41
#include <objbase.h>
42 43 44
#include <shlguid.h>
#include <shlwapi.h>
#include <shlobj.h>
45 46
#define WINE_MOUNTMGR_EXTENSIONS
#include <ddk/mountmgr.h>
47
#include <wine/library.h>
48 49 50 51

#include "winecfg.h"
#include "resource.h"

52

53 54
WINE_DEFAULT_DEBUG_CHANNEL(winecfg);

Mike Hearn's avatar
Mike Hearn committed
55 56 57
struct drive drives[26]; /* one for each drive letter */

static inline int letter_to_index(char letter)
58
{
Mike Hearn's avatar
Mike Hearn committed
59 60
    return (toupper(letter) - 'A');
}
61

Mike Hearn's avatar
Mike Hearn committed
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
/* This function produces a mask for each drive letter that isn't
 * currently used. Each bit of the long result represents a letter,
 * with A being the least significant bit, and Z being the most
 * significant.
 *
 * To calculate this, we loop over each letter, and see if we can get
 * a drive entry for it. If so, we set the appropriate bit. At the
 * end, we flip each bit, to give the desired result.
 *
 * The letter parameter is always marked as being available. This is
 * so the edit dialog can display the currently used drive letter
 * alongside the available ones.
 */
long drive_available_mask(char letter)
{
  long result = 0;
  int i;
79

Mike Hearn's avatar
Mike Hearn committed
80
  WINE_TRACE("\n");
81

Mike Hearn's avatar
Mike Hearn committed
82 83 84 85

  for(i = 0; i < 26; i++)
  {
      if (!drives[i].in_use) continue;
86
      result |= (1 << (letter_to_index(drives[i].letter)));
Mike Hearn's avatar
Mike Hearn committed
87 88 89 90 91 92 93
  }

  result = ~result;
  if (letter) result |= DRIVE_MASK_BIT(letter);

  WINE_TRACE("finished drive letter loop with %lx\n", result);
  return result;
94
}
95

96 97
BOOL add_drive(char letter, const char *targetpath, const char *device, const WCHAR *label,
               DWORD serial, DWORD type)
98
{
Mike Hearn's avatar
Mike Hearn committed
99
    int driveIndex = letter_to_index(letter);
100 101

    if(drives[driveIndex].in_use)
102
        return FALSE;
103

104 105 106
    WINE_TRACE("letter == '%c', unixpath == %s, device == %s, label == %s, serial == %08x, type == %d\n",
               letter, wine_dbgstr_a(targetpath), wine_dbgstr_a(device),
               wine_dbgstr_w(label), serial, type);
107

108
    drives[driveIndex].letter   = toupper(letter);
Mike Hearn's avatar
Mike Hearn committed
109
    drives[driveIndex].unixpath = strdupA(targetpath);
110 111
    drives[driveIndex].device   = device ? strdupA(device) : NULL;
    drives[driveIndex].label    = label ? strdupW(label) : NULL;
112
    drives[driveIndex].serial   = serial;
113 114
    drives[driveIndex].type     = type;
    drives[driveIndex].in_use   = TRUE;
115
    drives[driveIndex].modified = TRUE;
116

117 118
    return TRUE;
}
119

Mike Hearn's avatar
Mike Hearn committed
120 121
/* deallocates the contents of the drive. does not free the drive itself  */
void delete_drive(struct drive *d)
122
{
Mike Hearn's avatar
Mike Hearn committed
123
    HeapFree(GetProcessHeap(), 0, d->unixpath);
124
    d->unixpath = NULL;
125 126
    HeapFree(GetProcessHeap(), 0, d->device);
    d->device = NULL;
Mike Hearn's avatar
Mike Hearn committed
127
    HeapFree(GetProcessHeap(), 0, d->label);
128
    d->label = NULL;
129
    d->serial = 0;
Mike Hearn's avatar
Mike Hearn committed
130
    d->in_use = FALSE;
131
    d->modified = TRUE;
132 133
}

134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
static DWORD get_drive_type( char letter )
{
    HKEY hKey;
    char driveValue[4];
    DWORD ret = DRIVE_UNKNOWN;

    sprintf(driveValue, "%c:", letter);

    if (RegOpenKey(HKEY_LOCAL_MACHINE, "Software\\Wine\\Drives", &hKey) != ERROR_SUCCESS)
        WINE_TRACE("  Unable to open Software\\Wine\\Drives\n" );
    else
    {
        char buffer[80];
        DWORD size = sizeof(buffer);

        if (!RegQueryValueExA( hKey, driveValue, NULL, NULL, (LPBYTE)buffer, &size ))
        {
            WINE_TRACE("Got type '%s' for %s\n", buffer, driveValue );
152 153 154 155
            if (!lstrcmpi( buffer, "hd" )) ret = DRIVE_FIXED;
            else if (!lstrcmpi( buffer, "network" )) ret = DRIVE_REMOTE;
            else if (!lstrcmpi( buffer, "floppy" )) ret = DRIVE_REMOVABLE;
            else if (!lstrcmpi( buffer, "cdrom" )) ret = DRIVE_CDROM;
156 157 158 159 160 161
        }
        RegCloseKey(hKey);
    }
    return ret;
}

162

163
static void set_drive_label( char letter, const WCHAR *label )
164
{
165 166
    static const WCHAR emptyW[1];
    WCHAR device[] = {'a',':','\\',0};  /* SetVolumeLabel() requires a trailing slash */
167 168
    device[0] = letter;

169 170
    if (!label) label = emptyW;
    if(!SetVolumeLabelW(device, label))
171
    {
172 173
        WINE_WARN("unable to set volume label for devicename of %s, label of %s\n",
                  wine_dbgstr_w(device), wine_dbgstr_w(label));
174 175 176 177
        PRINTERROR();
    }
    else
    {
178 179
        WINE_TRACE("  set volume label for devicename of %s, label of %s\n",
                  wine_dbgstr_w(device), wine_dbgstr_w(label));
180 181 182 183
    }
}

/* set the drive serial number via a .windows-serial file */
184
static void set_drive_serial( char letter, DWORD serial )
185 186 187 188 189
{
    char filename[] = "a:\\.windows-serial";
    HANDLE hFile;

    filename[0] = letter;
190
    WINE_TRACE("Putting serial number of %08x into file '%s'\n", serial, filename);
191 192 193 194 195
    hFile = CreateFile(filename, GENERIC_WRITE, FILE_SHARE_READ, NULL,
                       CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile != INVALID_HANDLE_VALUE)
    {
        DWORD w;
196 197 198 199
        char buffer[16];

        sprintf( buffer, "%X\n", serial );
        WriteFile(hFile, buffer, strlen(buffer), &w, NULL);
200 201 202 203
        CloseHandle(hFile);
    }
}

Mike Hearn's avatar
Mike Hearn committed
204
#if 0
205

Mike Hearn's avatar
Mike Hearn committed
206 207 208
/* currently unused, but if users have this burning desire to be able to rename drives,
   we can put it back in.
 */
209

Mike Hearn's avatar
Mike Hearn committed
210
BOOL copyDrive(struct drive *pSrc, struct drive *pDst)
211 212 213 214 215 216 217 218 219 220 221
{
    if(pDst->in_use)
    {
        WINE_TRACE("pDst already in use\n");
        return FALSE;
    }

    if(!pSrc->unixpath) WINE_TRACE("!pSrc->unixpath\n");
    if(!pSrc->label) WINE_TRACE("!pSrc->label\n");
    if(!pSrc->serial) WINE_TRACE("!pSrc->serial\n");

Mike Hearn's avatar
Mike Hearn committed
222 223 224
    pDst->unixpath = strdupA(pSrc->unixpath);
    pDst->label = strdupA(pSrc->label);
    pDst->serial = strdupA(pSrc->serial);
225 226 227 228 229 230
    pDst->type = pSrc->type;
    pDst->in_use = TRUE;

    return TRUE;
}

Mike Hearn's avatar
Mike Hearn committed
231
BOOL moveDrive(struct drive *pSrc, struct drive *pDst)
232 233 234 235 236 237 238 239 240
{
    WINE_TRACE("pSrc->letter == %c, pDst->letter == %c\n", pSrc->letter, pDst->letter);

    if(!copyDrive(pSrc, pDst))
    {
        WINE_TRACE("copyDrive failed\n");
        return FALSE;
    }

Mike Hearn's avatar
Mike Hearn committed
241
    delete_drive(pSrc);
242
    return TRUE;
243 244
}

Mike Hearn's avatar
Mike Hearn committed
245
#endif
246

247 248 249 250 251 252 253 254 255 256 257
static HANDLE open_mountmgr(void)
{
    HANDLE ret;

    if ((ret = CreateFileW( MOUNTMGR_DOS_DEVICE_NAME, GENERIC_READ|GENERIC_WRITE,
                            FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
                            0, 0 )) == INVALID_HANDLE_VALUE)
        WINE_ERR( "failed to open mount manager err %u\n", GetLastError() );
    return ret;
}

Mike Hearn's avatar
Mike Hearn committed
258
/* Load currently defined drives into the drives array  */
259
BOOL load_drives(void)
260
{
261 262 263
    DWORD i, size = 1024;
    HANDLE mgr;
    WCHAR root[] = {'A',':',0};
264

265
    if ((mgr = open_mountmgr()) == INVALID_HANDLE_VALUE) return FALSE;
266

267
    while (root[0] <= 'Z')
268
    {
269 270
        struct mountmgr_unix_drive input;
        struct mountmgr_unix_drive *data;
Mike Hearn's avatar
Mike Hearn committed
271

272
        if (!(data = HeapAlloc( GetProcessHeap(), 0, size ))) break;
Mike Hearn's avatar
Mike Hearn committed
273

274 275
        memset( &input, 0, sizeof(input) );
        input.letter = root[0];
Mike Hearn's avatar
Mike Hearn committed
276

277 278
        if (DeviceIoControl( mgr, IOCTL_MOUNTMGR_QUERY_UNIX_DRIVE, &input, sizeof(input),
                             data, size, NULL, NULL ))
Mike Hearn's avatar
Mike Hearn committed
279
        {
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
            char *unixpath = NULL, *device = NULL;
            WCHAR volname[MAX_PATH];
            DWORD serial;

            if (data->mount_point_offset) unixpath = (char *)data + data->mount_point_offset;
            if (data->device_offset) device = (char *)data + data->device_offset;

            if (!GetVolumeInformationW( root, volname, sizeof(volname)/sizeof(WCHAR),
                                        &serial, NULL, NULL, NULL, 0 ))
            {
                volname[0] = 0;
                serial = 0;
            }
            if (unixpath)  /* FIXME: handle unmounted drives too */
                add_drive( root[0], unixpath, device, volname, serial, get_drive_type(root[0]) );
            root[0]++;
Mike Hearn's avatar
Mike Hearn committed
296
        }
297
        else
Mike Hearn's avatar
Mike Hearn committed
298
        {
299 300
            if (GetLastError() == ERROR_MORE_DATA) size = data->size;
            else root[0]++;  /* skip this drive */
Mike Hearn's avatar
Mike Hearn committed
301
        }
302
        HeapFree( GetProcessHeap(), 0, data );
303 304
    }

305 306 307
    /* reset modified flags */
    for (i = 0; i < 26; i++) drives[i].modified = FALSE;

308 309
    CloseHandle( mgr );
    return TRUE;
310 311
}

Mike Hearn's avatar
Mike Hearn committed
312 313
/* some of this code appears to be broken by bugs in Wine: the label
 * setting code has no effect, for instance  */
314
void apply_drive_changes(void)
315 316
{
    int i;
317 318 319
    HANDLE mgr;
    DWORD len;
    struct mountmgr_unix_drive *ioctl;
320 321 322

    WINE_TRACE("\n");

323 324
    if ((mgr = open_mountmgr()) == INVALID_HANDLE_VALUE) return;

325 326 327
    /* add each drive and remove as we go */
    for(i = 0; i < 26; i++)
    {
328 329
        if (!drives[i].modified) continue;
        drives[i].modified = FALSE;
330

331
        len = sizeof(*ioctl);
332 333 334 335 336
        if (drives[i].in_use)
        {
            len += strlen(drives[i].unixpath) + 1;
            if (drives[i].device) len += strlen(drives[i].device) + 1;
        }
337 338 339 340
        if (!(ioctl = HeapAlloc( GetProcessHeap(), 0, len ))) continue;
        ioctl->size = len;
        ioctl->letter = 'a' + i;
        ioctl->device_offset = 0;
341
        if (drives[i].in_use)
342
        {
343 344
            char *ptr = (char *)(ioctl + 1);

345
            ioctl->type = drives[i].type;
346 347 348 349 350 351 352 353
            strcpy( ptr, drives[i].unixpath );
            ioctl->mount_point_offset = ptr - (char *)ioctl;
            if (drives[i].device)
            {
                ptr += strlen(ptr) + 1;
                strcpy( ptr, drives[i].device );
                ioctl->device_offset = ptr - (char *)ioctl;
            }
354 355 356 357 358
        }
        else
        {
            ioctl->type = DRIVE_NO_ROOT_DIR;
            ioctl->mount_point_offset = 0;
Mike Hearn's avatar
Mike Hearn committed
359
        }
360 361

        if (DeviceIoControl( mgr, IOCTL_MOUNTMGR_DEFINE_UNIX_DRIVE, ioctl, len, NULL, 0, NULL, NULL ))
362
        {
363 364 365 366
            set_drive_label( drives[i].letter, drives[i].label );
            if (drives[i].in_use) set_drive_serial( drives[i].letter, drives[i].serial );
            WINE_TRACE( "set drive %c: to %s type %u\n", 'a' + i,
                        wine_dbgstr_a(drives[i].unixpath), drives[i].type );
Mike Hearn's avatar
Mike Hearn committed
367
        }
368 369
        else WINE_WARN( "failed to set drive %c: to %s type %u err %u\n", 'a' + i,
                       wine_dbgstr_a(drives[i].unixpath), drives[i].type, GetLastError() );
370
    }
371
    CloseHandle( mgr );
372
}