drivedetect.c 10.2 KB
Newer Older
Mike Hearn's avatar
Mike Hearn committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
 * Drive autodetection code
 *
 * Copyright 2004 Mike Hearn
 *
 * 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
18
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
Mike Hearn's avatar
Mike Hearn committed
19 20 21
 *
 */

22
#include "config.h"
23
#include "wine/port.h"
24

25
#include <stdarg.h>
Mike Hearn's avatar
Mike Hearn committed
26
#include <stdio.h>
27
#ifdef HAVE_MNTENT_H
Mike Hearn's avatar
Mike Hearn committed
28
#include <mntent.h>
29
#endif
Mike Hearn's avatar
Mike Hearn committed
30 31 32 33
#include <stdlib.h>
#include <errno.h>
#include <sys/stat.h>

34
#include <windef.h>
Mike Hearn's avatar
Mike Hearn committed
35
#include <winbase.h>
36 37 38 39 40 41
#include <wine/debug.h>
#include <wine/library.h>

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

Mike Hearn's avatar
Mike Hearn committed
42 43 44 45

WINE_DEFAULT_DEBUG_CHANNEL(winecfg);

BOOL gui_mode = TRUE;
46
static ULONG working_mask = 0;
Mike Hearn's avatar
Mike Hearn committed
47

48 49 50 51 52 53
typedef struct
{
  const char *szNode;
  int nType;
} DEV_NODES;

54
#ifdef HAVE_MNTENT_H
55

56
static const DEV_NODES sDeviceNodes[] = {
57
  {"/dev/fd", DRIVE_REMOVABLE},
58
  {"/dev/pf", DRIVE_REMOVABLE},
59
  {"/dev/acd", DRIVE_CDROM},
60 61
  {"/dev/aztcd", DRIVE_CDROM},
  {"/dev/bpcd", DRIVE_CDROM},
62
  {"/dev/cd", DRIVE_CDROM},
63
  {"/dev/cdrom", DRIVE_CDROM},
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
  {"/dev/cdu535", DRIVE_CDROM},
  {"/dev/cdwriter", DRIVE_CDROM},
  {"/dev/cm205cd", DRIVE_CDROM},
  {"/dev/cm206cd", DRIVE_CDROM},
  {"/dev/gscd", DRIVE_CDROM},
  {"/dev/hitcd", DRIVE_CDROM},
  {"/dev/iseries/vcd", DRIVE_CDROM},
  {"/dev/lmscd", DRIVE_CDROM},
  {"/dev/mcd", DRIVE_CDROM},
  {"/dev/optcd", DRIVE_CDROM},
  {"/dev/pcd", DRIVE_CDROM},
  {"/dev/sbpcd", DRIVE_CDROM},
  {"/dev/scd", DRIVE_CDROM},
  {"/dev/sjcd", DRIVE_CDROM},
  {"/dev/sonycd", DRIVE_CDROM},
  {"/dev/sr", DRIVE_CDROM},
80 81 82
  {"",0}
};

83
static const char * const ignored_fstypes[] = {
Mike Hearn's avatar
Mike Hearn committed
84 85 86 87 88 89 90
    "devpts",
    "tmpfs",
    "proc",
    "sysfs",
    "swap",
    "usbdevfs",
    "rpc_pipefs",
91
    "binfmt_misc",
Mike Hearn's avatar
Mike Hearn committed
92 93 94
    NULL
};

95
static const char * const ignored_mnt_dirs[] = {
96 97 98 99
    "/boot",
    NULL
};

100 101 102 103 104 105 106 107 108 109 110 111 112 113
static int try_dev_node(char *dev)
{
    const DEV_NODES *pDevNodes = sDeviceNodes;
    
    while(pDevNodes->szNode[0])
    {
        if(!strncmp(dev,pDevNodes->szNode,strlen(pDevNodes->szNode)))
            return pDevNodes->nType;
        ++pDevNodes;
    }
    
    return DRIVE_FIXED;
}

Mike Hearn's avatar
Mike Hearn committed
114 115
static BOOL should_ignore_fstype(char *type)
{
116
    const char * const *s;
Mike Hearn's avatar
Mike Hearn committed
117 118 119 120 121 122 123
    
    for (s = ignored_fstypes; *s; s++)
        if (!strcmp(*s, type)) return TRUE;

    return FALSE;
}

124 125
static BOOL should_ignore_mnt_dir(char *dir)
{
126
    const char * const *s;
127 128 129 130 131 132 133

    for (s = ignored_mnt_dirs; *s; s++)
        if (!strcmp(*s, dir)) return TRUE;

    return FALSE;
}

Mike Hearn's avatar
Mike Hearn committed
134 135 136 137 138 139 140 141 142 143 144
static BOOL is_drive_defined(char *path)
{
    int i;
    
    for (i = 0; i < 26; i++)
        if (drives[i].in_use && !strcmp(drives[i].unixpath, path)) return TRUE;

    return FALSE;
}

/* returns Z + 1 if there are no more available letters */
145
static char allocate_letter(int type)
Mike Hearn's avatar
Mike Hearn committed
146
{
147
    char letter, start;
Mike Hearn's avatar
Mike Hearn committed
148

149 150 151 152 153 154
    if (type == DRIVE_REMOVABLE)
	start = 'A';
    else
	start = 'C';

    for (letter = start; letter <= 'Z'; letter++)
Mike Hearn's avatar
Mike Hearn committed
155 156 157 158
        if ((DRIVE_MASK_BIT(letter) & working_mask) != 0) break;

    return letter;
}
159
#endif
Mike Hearn's avatar
Mike Hearn committed
160 161 162 163 164

#define FSTAB_OPEN 1
#define NO_MORE_LETTERS 2
#define NO_ROOT 3
#define NO_DRIVE_C 4
165
#define NO_HOME 5
Mike Hearn's avatar
Mike Hearn committed
166 167 168 169 170 171 172 173 174 175 176

static void report_error(int code)
{
    char *buffer;
    int len;
    
    switch (code)
    {
        case FSTAB_OPEN:
            if (gui_mode)
            {
177
                static const char s[] = "Could not open your mountpoint description table.\n\nOpening of /etc/fstab failed: %s";
Mike Hearn's avatar
Mike Hearn committed
178 179 180
                len = snprintf(NULL, 0, s, strerror(errno));
                buffer = HeapAlloc(GetProcessHeap(), 0, len + 1);
                snprintf(buffer, len, s, strerror(errno));
181
                MessageBoxA(NULL, s, "", MB_OK | MB_ICONEXCLAMATION);
Mike Hearn's avatar
Mike Hearn committed
182 183 184 185
                HeapFree(GetProcessHeap(), 0, buffer);
            }
            else
            {
186
                fprintf(stderr, "winecfg: could not open fstab: %s\n", strerror(errno));
Mike Hearn's avatar
Mike Hearn committed
187 188 189 190
            }
            break;

        case NO_MORE_LETTERS:
191
            if (gui_mode) MessageBoxA(NULL, "No more letters are available to auto-detect available drives with.", "", MB_OK | MB_ICONEXCLAMATION);
192
            fprintf(stderr, "winecfg: no more available letters while scanning /etc/fstab\n");
Mike Hearn's avatar
Mike Hearn committed
193 194 195
            break;

        case NO_ROOT:
196
            if (gui_mode) MessageBoxA(NULL, "Could not ensure that the root directory was mapped.\n\n"
Mike Hearn's avatar
Mike Hearn committed
197 198 199 200 201 202 203 204 205 206
                                     "This can happen if you run out of drive letters. "
                                     "It's important to have the root directory mapped, otherwise Wine"
                                     "will not be able to always find the programs you want to run. "
                                     "Try unmapping a drive letter then trying again.", "",
                                     MB_OK | MB_ICONEXCLAMATION);
            else fprintf(stderr, "winecfg: unable to map root drive\n");
            break;

        case NO_DRIVE_C:
            if (gui_mode)
207
                MessageBoxA(NULL, "No virtual drive C mapped!\n", "", MB_OK | MB_ICONEXCLAMATION);
Mike Hearn's avatar
Mike Hearn committed
208 209
            else
                fprintf(stderr, "winecfg: no drive_c directory\n");
210
            break;
211 212
        case NO_HOME:
            if (gui_mode)
213
                MessageBoxA(NULL, "Could not ensure that your home directory was mapped.\n\n"
214 215 216 217 218 219
                                 "This can happen if you run out of drive letters. "
                                 "Try unmapping a drive letter then try again.", "",
                                 MB_OK | MB_ICONEXCLAMATION);
            else 
                fprintf(stderr, "winecfg: unable to map home drive\n");
            break;
Mike Hearn's avatar
Mike Hearn committed
220 221 222
    }
}

223
static void ensure_root_is_mapped(void)
Mike Hearn's avatar
Mike Hearn committed
224 225 226 227 228 229 230 231 232 233 234 235 236 237
{
    int i;
    BOOL mapped = FALSE;

    for (i = 0; i < 26; i++)
        if (drives[i].in_use && !strcmp(drives[i].unixpath, "/")) mapped = TRUE;

    if (!mapped)
    {
        /* work backwards from Z, trying to map it */
        char letter;

        for (letter = 'Z'; letter >= 'A'; letter--)
        {
238 239
            if (!drives[letter - 'A'].in_use) 
            {
240
                add_drive(letter, "/", NULL, NULL, 0, DRIVE_FIXED);
241 242 243
                WINE_TRACE("allocated drive %c as the root drive\n", letter);
                break;
            }
Mike Hearn's avatar
Mike Hearn committed
244 245 246 247 248 249
        }

        if (letter == ('A' - 1)) report_error(NO_ROOT);
    }
}

250
static void ensure_home_is_mapped(void)
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268
{
    int i;
    BOOL mapped = FALSE;
    char *home = getenv("HOME");

    if (!home) return;

    for (i = 0; i < 26; i++)
        if (drives[i].in_use && !strcmp(drives[i].unixpath, home)) mapped = TRUE;

    if (!mapped)
    {
        char letter;

        for (letter = 'H'; letter <= 'Z'; letter++)
        {
            if (!drives[letter - 'A'].in_use)
            {
269
                add_drive(letter, home, NULL, NULL, 0, DRIVE_FIXED);
270 271 272 273 274 275 276 277
                WINE_TRACE("allocated drive %c as the user's home directory\n", letter);
                break;
            }
        }
        if (letter == ('Z' + 1)) report_error(NO_HOME);
    }        
}

278
static void ensure_drive_c_is_mapped(void)
Mike Hearn's avatar
Mike Hearn committed
279 280 281 282 283 284 285 286 287 288 289 290 291 292
{
    struct stat buf;
    const char *configdir = wine_get_config_dir();
    int len;
    char *drive_c_dir;
    
    if (drives[2].in_use) return;

    len = snprintf(NULL, 0, "%s/../drive_c", configdir);
    drive_c_dir = HeapAlloc(GetProcessHeap(), 0, len);
    snprintf(drive_c_dir, len, "%s/../drive_c", configdir);

    if (stat(drive_c_dir, &buf) == 0)
    {
293
        WCHAR label[64];
294
        LoadStringW(GetModuleHandleW(NULL), IDS_SYSTEM_DRIVE_LABEL, label, ARRAY_SIZE(label));
295
        add_drive('C', "../drive_c", NULL, label, 0, DRIVE_FIXED);
Mike Hearn's avatar
Mike Hearn committed
296 297 298 299 300
    }
    else
    {
        report_error(NO_DRIVE_C);
    }
301
    HeapFree(GetProcessHeap(), 0, drive_c_dir);
Mike Hearn's avatar
Mike Hearn committed
302 303
}

304
BOOL autodetect_drives(void)
Mike Hearn's avatar
Mike Hearn committed
305
{
306
#ifdef HAVE_MNTENT_H
Mike Hearn's avatar
Mike Hearn committed
307 308
    struct mntent *ent;
    FILE *fstab;
309
#endif
Mike Hearn's avatar
Mike Hearn committed
310 311 312 313 314 315 316 317 318 319 320 321

    /* we want to build a list of autodetected drives, then ensure each entry
       exists in the users setup. so, we superimpose the autodetected drives
       onto whatever is pre-existing.

       for now let's just rummage around inside the fstab.
     */

    load_drives();

    working_mask = drive_available_mask('\0');

322
#ifdef HAVE_MNTENT_H
Mike Hearn's avatar
Mike Hearn committed
323 324 325 326 327 328 329 330 331 332 333
    fstab = fopen("/etc/fstab", "r");
    if (!fstab)
    {
        report_error(FSTAB_OPEN);
        return FALSE;
    }

    while ((ent = getmntent(fstab)))
    {
        char letter;
        int type;
334 335
        char *device = NULL;

Mike Hearn's avatar
Mike Hearn committed
336 337 338
        WINE_TRACE("ent->mnt_dir=%s\n", ent->mnt_dir);

        if (should_ignore_fstype(ent->mnt_type)) continue;
339
        if (should_ignore_mnt_dir(ent->mnt_dir)) continue;
Mike Hearn's avatar
Mike Hearn committed
340 341
        if (is_drive_defined(ent->mnt_dir)) continue;

342 343 344 345 346 347 348 349 350
        if (!strcmp(ent->mnt_type, "nfs")) type = DRIVE_REMOTE;
        else if (!strcmp(ent->mnt_type, "nfs4")) type = DRIVE_REMOTE;
        else if (!strcmp(ent->mnt_type, "smbfs")) type = DRIVE_REMOTE;
        else if (!strcmp(ent->mnt_type, "cifs")) type = DRIVE_REMOTE;
        else if (!strcmp(ent->mnt_type, "coda")) type = DRIVE_REMOTE;
        else if (!strcmp(ent->mnt_type, "iso9660")) type = DRIVE_CDROM;
        else if (!strcmp(ent->mnt_type, "ramfs")) type = DRIVE_RAMDISK;
        else type = try_dev_node(ent->mnt_fsname);
        
Mike Hearn's avatar
Mike Hearn committed
351
        /* allocate a drive for it */
352
        letter = allocate_letter(type);
353
        if (letter == 'Z' + 1)
Mike Hearn's avatar
Mike Hearn committed
354 355 356 357 358 359
        {
            report_error(NO_MORE_LETTERS);
            fclose(fstab);
            return FALSE;
        }

360 361 362 363 364
        if (type == DRIVE_CDROM) device = ent->mnt_fsname;

        WINE_TRACE("adding drive %c for %s, device %s, type %s\n",
                   letter, ent->mnt_dir, device, ent->mnt_type);
        add_drive(letter, ent->mnt_dir, device, NULL, 0, type);
365

366 367
        /* working_mask is a map of the drive letters still available. */
        working_mask &= ~DRIVE_MASK_BIT(letter);
Mike Hearn's avatar
Mike Hearn committed
368 369 370
    }

    fclose(fstab);
371
#endif
Mike Hearn's avatar
Mike Hearn committed
372 373 374 375 376

    ensure_root_is_mapped();

    ensure_drive_c_is_mapped();

377 378
    ensure_home_is_mapped();

Mike Hearn's avatar
Mike Hearn committed
379 380
    return TRUE;
}