Commit | Line | Data |
---|---|---|
7d8711e1 MH |
1 | /* |
2 | * Drive autodetection code | |
3 | * | |
4 | * Copyright 2004 Mike Hearn | |
5 | * | |
6 | * This library is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU Lesser General Public | |
8 | * License as published by the Free Software Foundation; either | |
9 | * version 2.1 of the License, or (at your option) any later version. | |
10 | * | |
11 | * This library is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * Lesser General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU Lesser General Public | |
17 | * License along with this library; if not, write to the Free Software | |
360a3f91 | 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
7d8711e1 MH |
19 | * |
20 | */ | |
21 | ||
6c152520 | 22 | #include "config.h" |
49b7fdcf | 23 | #include "wine/port.h" |
6c152520 | 24 | |
f65cd2e2 | 25 | #include <stdarg.h> |
7d8711e1 | 26 | #include <stdio.h> |
1635947f | 27 | #ifdef HAVE_MNTENT_H |
7d8711e1 | 28 | #include <mntent.h> |
1635947f | 29 | #endif |
7d8711e1 MH |
30 | #include <stdlib.h> |
31 | #include <errno.h> | |
7d8711e1 MH |
32 | #include <sys/stat.h> |
33 | ||
f150ddc3 | 34 | #include <windef.h> |
7d8711e1 | 35 | #include <winbase.h> |
f150ddc3 AJ |
36 | #include <wine/debug.h> |
37 | #include <wine/library.h> | |
38 | ||
39 | #include "winecfg.h" | |
40 | #include "resource.h" | |
41 | ||
7d8711e1 MH |
42 | |
43 | WINE_DEFAULT_DEBUG_CHANNEL(winecfg); | |
44 | ||
45 | BOOL gui_mode = TRUE; | |
e09fc215 | 46 | static ULONG working_mask = 0; |
7d8711e1 | 47 | |
82a7eb76 VM |
48 | typedef struct |
49 | { | |
50 | const char *szNode; | |
51 | int nType; | |
52 | } DEV_NODES; | |
53 | ||
1635947f | 54 | #ifdef HAVE_MNTENT_H |
6cf1b31d | 55 | |
e452c246 | 56 | static const DEV_NODES sDeviceNodes[] = { |
6cf1b31d | 57 | {"/dev/fd", DRIVE_REMOVABLE}, |
6c5eff90 | 58 | {"/dev/pf", DRIVE_REMOVABLE}, |
6b80c804 | 59 | {"/dev/acd", DRIVE_CDROM}, |
6c5eff90 PR |
60 | {"/dev/aztcd", DRIVE_CDROM}, |
61 | {"/dev/bpcd", DRIVE_CDROM}, | |
6b80c804 | 62 | {"/dev/cd", DRIVE_CDROM}, |
6cf1b31d | 63 | {"/dev/cdrom", DRIVE_CDROM}, |
6c5eff90 PR |
64 | {"/dev/cdu535", DRIVE_CDROM}, |
65 | {"/dev/cdwriter", DRIVE_CDROM}, | |
66 | {"/dev/cm205cd", DRIVE_CDROM}, | |
67 | {"/dev/cm206cd", DRIVE_CDROM}, | |
68 | {"/dev/gscd", DRIVE_CDROM}, | |
69 | {"/dev/hitcd", DRIVE_CDROM}, | |
70 | {"/dev/iseries/vcd", DRIVE_CDROM}, | |
71 | {"/dev/lmscd", DRIVE_CDROM}, | |
72 | {"/dev/mcd", DRIVE_CDROM}, | |
73 | {"/dev/optcd", DRIVE_CDROM}, | |
74 | {"/dev/pcd", DRIVE_CDROM}, | |
75 | {"/dev/sbpcd", DRIVE_CDROM}, | |
76 | {"/dev/scd", DRIVE_CDROM}, | |
77 | {"/dev/sjcd", DRIVE_CDROM}, | |
78 | {"/dev/sonycd", DRIVE_CDROM}, | |
79 | {"/dev/sr", DRIVE_CDROM}, | |
6cf1b31d PS |
80 | {"",0} |
81 | }; | |
82 | ||
e452c246 | 83 | static const char * const ignored_fstypes[] = { |
7d8711e1 MH |
84 | "devpts", |
85 | "tmpfs", | |
86 | "proc", | |
87 | "sysfs", | |
88 | "swap", | |
89 | "usbdevfs", | |
90 | "rpc_pipefs", | |
12c08baf | 91 | "binfmt_misc", |
7d8711e1 MH |
92 | NULL |
93 | }; | |
94 | ||
e452c246 | 95 | static const char * const ignored_mnt_dirs[] = { |
6c152520 MJ |
96 | "/boot", |
97 | NULL | |
98 | }; | |
99 | ||
6cf1b31d PS |
100 | static int try_dev_node(char *dev) |
101 | { | |
102 | const DEV_NODES *pDevNodes = sDeviceNodes; | |
103 | ||
104 | while(pDevNodes->szNode[0]) | |
105 | { | |
106 | if(!strncmp(dev,pDevNodes->szNode,strlen(pDevNodes->szNode))) | |
107 | return pDevNodes->nType; | |
108 | ++pDevNodes; | |
109 | } | |
110 | ||
111 | return DRIVE_FIXED; | |
112 | } | |
113 | ||
7d8711e1 MH |
114 | static BOOL should_ignore_fstype(char *type) |
115 | { | |
e452c246 | 116 | const char * const *s; |
7d8711e1 MH |
117 | |
118 | for (s = ignored_fstypes; *s; s++) | |
119 | if (!strcmp(*s, type)) return TRUE; | |
120 | ||
121 | return FALSE; | |
122 | } | |
123 | ||
6c152520 MJ |
124 | static BOOL should_ignore_mnt_dir(char *dir) |
125 | { | |
e452c246 | 126 | const char * const *s; |
6c152520 MJ |
127 | |
128 | for (s = ignored_mnt_dirs; *s; s++) | |
129 | if (!strcmp(*s, dir)) return TRUE; | |
130 | ||
131 | return FALSE; | |
132 | } | |
133 | ||
7d8711e1 MH |
134 | static BOOL is_drive_defined(char *path) |
135 | { | |
136 | int i; | |
137 | ||
138 | for (i = 0; i < 26; i++) | |
139 | if (drives[i].in_use && !strcmp(drives[i].unixpath, path)) return TRUE; | |
140 | ||
141 | return FALSE; | |
142 | } | |
143 | ||
144 | /* returns Z + 1 if there are no more available letters */ | |
34d9d5a3 | 145 | static char allocate_letter(int type) |
7d8711e1 | 146 | { |
34d9d5a3 | 147 | char letter, start; |
7d8711e1 | 148 | |
34d9d5a3 PR |
149 | if (type == DRIVE_REMOVABLE) |
150 | start = 'A'; | |
151 | else | |
152 | start = 'C'; | |
153 | ||
154 | for (letter = start; letter <= 'Z'; letter++) | |
7d8711e1 MH |
155 | if ((DRIVE_MASK_BIT(letter) & working_mask) != 0) break; |
156 | ||
157 | return letter; | |
158 | } | |
1635947f | 159 | #endif |
7d8711e1 MH |
160 | |
161 | #define FSTAB_OPEN 1 | |
162 | #define NO_MORE_LETTERS 2 | |
163 | #define NO_ROOT 3 | |
164 | #define NO_DRIVE_C 4 | |
6c152520 | 165 | #define NO_HOME 5 |
7d8711e1 MH |
166 | |
167 | static void report_error(int code) | |
168 | { | |
169 | char *buffer; | |
170 | int len; | |
171 | ||
172 | switch (code) | |
173 | { | |
174 | case FSTAB_OPEN: | |
175 | if (gui_mode) | |
176 | { | |
e452c246 | 177 | static const char s[] = "Could not open your mountpoint description table.\n\nOpening of /etc/fstab failed: %s"; |
7d8711e1 MH |
178 | len = snprintf(NULL, 0, s, strerror(errno)); |
179 | buffer = HeapAlloc(GetProcessHeap(), 0, len + 1); | |
180 | snprintf(buffer, len, s, strerror(errno)); | |
181 | MessageBox(NULL, s, "", MB_OK | MB_ICONEXCLAMATION); | |
182 | HeapFree(GetProcessHeap(), 0, buffer); | |
183 | } | |
184 | else | |
185 | { | |
6c152520 | 186 | fprintf(stderr, "winecfg: could not open fstab: %s\n", strerror(errno)); |
7d8711e1 MH |
187 | } |
188 | break; | |
189 | ||
190 | case NO_MORE_LETTERS: | |
191 | if (gui_mode) MessageBox(NULL, "No more letters are available to auto-detect available drives with.", "", MB_OK | MB_ICONEXCLAMATION); | |
6c152520 | 192 | fprintf(stderr, "winecfg: no more available letters while scanning /etc/fstab\n"); |
7d8711e1 MH |
193 | break; |
194 | ||
195 | case NO_ROOT: | |
196 | if (gui_mode) MessageBox(NULL, "Could not ensure that the root directory was mapped.\n\n" | |
197 | "This can happen if you run out of drive letters. " | |
198 | "It's important to have the root directory mapped, otherwise Wine" | |
199 | "will not be able to always find the programs you want to run. " | |
200 | "Try unmapping a drive letter then trying again.", "", | |
201 | MB_OK | MB_ICONEXCLAMATION); | |
202 | else fprintf(stderr, "winecfg: unable to map root drive\n"); | |
203 | break; | |
204 | ||
205 | case NO_DRIVE_C: | |
206 | if (gui_mode) | |
82868174 | 207 | MessageBox(NULL, "No virtual drive C mapped!\n", "", MB_OK | MB_ICONEXCLAMATION); |
7d8711e1 MH |
208 | else |
209 | fprintf(stderr, "winecfg: no drive_c directory\n"); | |
fd2fe2be | 210 | break; |
6c152520 MJ |
211 | case NO_HOME: |
212 | if (gui_mode) | |
213 | MessageBox(NULL, "Could not ensure that your home directory was mapped.\n\n" | |
214 | "This can happen if you run out of drive letters. " | |
215 | "Try unmapping a drive letter then try again.", "", | |
216 | MB_OK | MB_ICONEXCLAMATION); | |
217 | else | |
218 | fprintf(stderr, "winecfg: unable to map home drive\n"); | |
219 | break; | |
7d8711e1 | 220 | } |
7d8711e1 MH |
221 | } |
222 | ||
71208770 | 223 | static void ensure_root_is_mapped(void) |
7d8711e1 MH |
224 | { |
225 | int i; | |
226 | BOOL mapped = FALSE; | |
227 | ||
228 | for (i = 0; i < 26; i++) | |
229 | if (drives[i].in_use && !strcmp(drives[i].unixpath, "/")) mapped = TRUE; | |
230 | ||
231 | if (!mapped) | |
232 | { | |
233 | /* work backwards from Z, trying to map it */ | |
234 | char letter; | |
235 | ||
236 | for (letter = 'Z'; letter >= 'A'; letter--) | |
237 | { | |
5e161ac7 MJ |
238 | if (!drives[letter - 'A'].in_use) |
239 | { | |
18b66912 | 240 | add_drive(letter, "/", NULL, NULL, 0, DRIVE_FIXED); |
5e161ac7 MJ |
241 | WINE_TRACE("allocated drive %c as the root drive\n", letter); |
242 | break; | |
243 | } | |
7d8711e1 MH |
244 | } |
245 | ||
246 | if (letter == ('A' - 1)) report_error(NO_ROOT); | |
247 | } | |
248 | } | |
249 | ||
71208770 | 250 | static void ensure_home_is_mapped(void) |
6c152520 MJ |
251 | { |
252 | int i; | |
253 | BOOL mapped = FALSE; | |
254 | char *home = getenv("HOME"); | |
255 | ||
256 | if (!home) return; | |
257 | ||
258 | for (i = 0; i < 26; i++) | |
259 | if (drives[i].in_use && !strcmp(drives[i].unixpath, home)) mapped = TRUE; | |
260 | ||
261 | if (!mapped) | |
262 | { | |
263 | char letter; | |
264 | ||
265 | for (letter = 'H'; letter <= 'Z'; letter++) | |
266 | { | |
267 | if (!drives[letter - 'A'].in_use) | |
268 | { | |
18b66912 | 269 | add_drive(letter, home, NULL, NULL, 0, DRIVE_FIXED); |
6c152520 MJ |
270 | WINE_TRACE("allocated drive %c as the user's home directory\n", letter); |
271 | break; | |
272 | } | |
273 | } | |
274 | if (letter == ('Z' + 1)) report_error(NO_HOME); | |
275 | } | |
276 | } | |
277 | ||
71208770 | 278 | static void ensure_drive_c_is_mapped(void) |
7d8711e1 MH |
279 | { |
280 | struct stat buf; | |
281 | const char *configdir = wine_get_config_dir(); | |
282 | int len; | |
283 | char *drive_c_dir; | |
284 | ||
285 | if (drives[2].in_use) return; | |
286 | ||
287 | len = snprintf(NULL, 0, "%s/../drive_c", configdir); | |
288 | drive_c_dir = HeapAlloc(GetProcessHeap(), 0, len); | |
289 | snprintf(drive_c_dir, len, "%s/../drive_c", configdir); | |
290 | HeapFree(GetProcessHeap(), 0, drive_c_dir); | |
291 | ||
292 | if (stat(drive_c_dir, &buf) == 0) | |
293 | { | |
f150ddc3 AJ |
294 | WCHAR label[64]; |
295 | LoadStringW (GetModuleHandle (NULL), IDS_SYSTEM_DRIVE_LABEL, label, | |
296 | sizeof(label)/sizeof(label[0])); | |
18b66912 | 297 | add_drive('C', "../drive_c", NULL, label, 0, DRIVE_FIXED); |
7d8711e1 MH |
298 | } |
299 | else | |
300 | { | |
301 | report_error(NO_DRIVE_C); | |
302 | } | |
303 | } | |
304 | ||
51a7ca34 | 305 | int autodetect_drives(void) |
7d8711e1 | 306 | { |
1635947f | 307 | #ifdef HAVE_MNTENT_H |
7d8711e1 MH |
308 | struct mntent *ent; |
309 | FILE *fstab; | |
1635947f | 310 | #endif |
7d8711e1 MH |
311 | |
312 | /* we want to build a list of autodetected drives, then ensure each entry | |
313 | exists in the users setup. so, we superimpose the autodetected drives | |
314 | onto whatever is pre-existing. | |
315 | ||
316 | for now let's just rummage around inside the fstab. | |
317 | */ | |
318 | ||
319 | load_drives(); | |
320 | ||
321 | working_mask = drive_available_mask('\0'); | |
322 | ||
1635947f | 323 | #ifdef HAVE_MNTENT_H |
7d8711e1 MH |
324 | fstab = fopen("/etc/fstab", "r"); |
325 | if (!fstab) | |
326 | { | |
327 | report_error(FSTAB_OPEN); | |
328 | return FALSE; | |
329 | } | |
330 | ||
331 | while ((ent = getmntent(fstab))) | |
332 | { | |
333 | char letter; | |
7d8711e1 | 334 | int type; |
18b66912 AJ |
335 | char *device = NULL; |
336 | ||
7d8711e1 MH |
337 | WINE_TRACE("ent->mnt_dir=%s\n", ent->mnt_dir); |
338 | ||
339 | if (should_ignore_fstype(ent->mnt_type)) continue; | |
6c152520 | 340 | if (should_ignore_mnt_dir(ent->mnt_dir)) continue; |
7d8711e1 MH |
341 | if (is_drive_defined(ent->mnt_dir)) continue; |
342 | ||
34d9d5a3 PR |
343 | if (!strcmp(ent->mnt_type, "nfs")) type = DRIVE_REMOTE; |
344 | else if (!strcmp(ent->mnt_type, "nfs4")) type = DRIVE_REMOTE; | |
345 | else if (!strcmp(ent->mnt_type, "smbfs")) type = DRIVE_REMOTE; | |
346 | else if (!strcmp(ent->mnt_type, "cifs")) type = DRIVE_REMOTE; | |
347 | else if (!strcmp(ent->mnt_type, "coda")) type = DRIVE_REMOTE; | |
348 | else if (!strcmp(ent->mnt_type, "iso9660")) type = DRIVE_CDROM; | |
349 | else if (!strcmp(ent->mnt_type, "ramfs")) type = DRIVE_RAMDISK; | |
350 | else type = try_dev_node(ent->mnt_fsname); | |
351 | ||
7d8711e1 | 352 | /* allocate a drive for it */ |
34d9d5a3 | 353 | letter = allocate_letter(type); |
8df74a0e | 354 | if (letter == 'Z' + 1) |
7d8711e1 MH |
355 | { |
356 | report_error(NO_MORE_LETTERS); | |
357 | fclose(fstab); | |
358 | return FALSE; | |
359 | } | |
7d8711e1 | 360 | |
18b66912 AJ |
361 | if (type == DRIVE_CDROM) device = ent->mnt_fsname; |
362 | ||
363 | WINE_TRACE("adding drive %c for %s, device %s, type %s\n", | |
364 | letter, ent->mnt_dir, device, ent->mnt_type); | |
365 | add_drive(letter, ent->mnt_dir, device, NULL, 0, type); | |
f150ddc3 | 366 | |
6c152520 MJ |
367 | /* working_mask is a map of the drive letters still available. */ |
368 | working_mask &= ~DRIVE_MASK_BIT(letter); | |
7d8711e1 MH |
369 | } |
370 | ||
371 | fclose(fstab); | |
1635947f | 372 | #endif |
7d8711e1 MH |
373 | |
374 | ensure_root_is_mapped(); | |
375 | ||
376 | ensure_drive_c_is_mapped(); | |
377 | ||
6c152520 MJ |
378 | ensure_home_is_mapped(); |
379 | ||
7d8711e1 MH |
380 | return TRUE; |
381 | } |