]>
Commit | Line | Data |
---|---|---|
23c9e63c | 1 | /* |
c6263b96 CV |
2 | * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, |
3 | * 2010, 2013 Red Hat, Inc. | |
23c9e63c DD |
4 | * |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License as published by | |
7 | * the Free Software Foundation; either version 2 of the License, or | |
8 | * (at your option) any later version. | |
9 | * | |
10 | * A copy of the GNU General Public License can be found at | |
11 | * http://www.gnu.org/ | |
12 | * | |
13 | * Written by DJ Delorie <dj@cygnus.com> | |
14 | * | |
15 | */ | |
16 | ||
17 | /* The purpose of this file is to hide all the details about accessing | |
18 | Cygwin's mount table. If the format or location of the mount table | |
19 | changes, this is the file to change to match it. */ | |
20 | ||
558484cc | 21 | #include "ini.h" |
23c9e63c | 22 | #include "win32.h" |
65288dc7 | 23 | #include "filemanip.h" |
507e5b52 | 24 | #include "LogSingleton.h" |
23c9e63c DD |
25 | |
26 | #include <stdio.h> | |
a351e48c | 27 | #include <stdlib.h> |
2cae901b | 28 | #include <malloc.h> |
db04fc41 RC |
29 | |
30 | // These headers aren't available outside the winsup tree | |
31 | // #include "../cygwin/include/cygwin/version.h" | |
32 | // KEEP SYNCHRONISED WITH /src/winsup/cygwin/include/cygwin/version.h | |
33 | ||
db04fc41 | 34 | #define CYGWIN_INFO_CYGWIN_REGISTRY_NAME "Cygwin" |
2f0315ad | 35 | #define CYGWIN_INFO_CYGWIN_SETUP_REGISTRY_NAME "setup" |
db04fc41 RC |
36 | |
37 | // #include "../cygwin/include/sys/mount.h" | |
38 | ||
39 | // KEEP SYNCHRONISED WITH /src/winsup/cygwin/include/sys/mount.h | |
40 | #ifdef __cplusplus | |
41 | extern "C" { | |
42 | #endif | |
43 | ||
44 | enum | |
45 | { | |
46 | MOUNT_SYMLINK = 0x001, /* "mount point" is a symlink */ | |
47 | MOUNT_BINARY = 0x002, /* "binary" format read/writes */ | |
48 | MOUNT_SYSTEM = 0x008, /* mount point came from system table */ | |
49 | MOUNT_EXEC = 0x010, /* Any file in the mounted directory gets 'x' bit */ | |
50 | MOUNT_AUTO = 0x020, /* mount point refers to auto device mount */ | |
51 | MOUNT_CYGWIN_EXEC = 0x040, /* file or directory is or contains a cygwin executable */ | |
52 | MOUNT_MIXED = 0x080, /* reads are text, writes are binary */ | |
53 | }; | |
54 | ||
55 | // int mount (const char *, const char *, unsigned __flags); | |
56 | // int umount (const char *); | |
57 | // int cygwin_umount (const char *__path, unsigned __flags); | |
58 | ||
59 | #ifdef __cplusplus | |
60 | }; | |
61 | #endif | |
62 | ||
63 | ||
23c9e63c DD |
64 | |
65 | #include "mount.h" | |
66 | #include "msg.h" | |
67 | #include "resource.h" | |
68 | #include "dialog.h" | |
a351e48c | 69 | #include "state.h" |
23c9e63c | 70 | |
4f618d88 MB |
71 | #ifdef MAINTAINER_FEATURES |
72 | #include "getopt++/GetOption.h" | |
73 | #include "getopt++/StringOption.h" | |
9d53f045 | 74 | static StringOption CygwinRegistryNameOption (CYGWIN_INFO_CYGWIN_REGISTRY_NAME, '#', "override-registry-name", "Override registry name to allow parallel installs for testing purposes", false); |
4f618d88 MB |
75 | #undef CYGWIN_INFO_CYGWIN_REGISTRY_NAME |
76 | #define CYGWIN_INFO_CYGWIN_REGISTRY_NAME (((std::string)CygwinRegistryNameOption).c_str()) | |
77 | #endif | |
78 | ||
1ac649ed RC |
79 | /* Used when treating / and \ as equivalent. */ |
80 | #define SLASH_P(ch) \ | |
81 | ({ \ | |
82 | char __c = (ch); \ | |
83 | ((__c) == '/' || (__c) == '\\'); \ | |
84 | }) | |
85 | ||
a351e48c | 86 | static struct mnt |
b24c88b3 | 87 | { |
2bba98e8 MB |
88 | std::string native; |
89 | std::string posix; | |
b24c88b3 RC |
90 | int istext; |
91 | } | |
92 | mount_table[255]; | |
23c9e63c | 93 | |
85b43844 CF |
94 | struct mnt *root_here = NULL; |
95 | ||
a351e48c | 96 | void |
2f0315ad CV |
97 | create_install_root () |
98 | { | |
99 | char buf[1000]; | |
100 | HKEY key; | |
101 | DWORD disposition; | |
102 | DWORD rv; | |
103 | ||
104 | snprintf (buf, sizeof(buf), "Software\\%s\\%s", | |
105 | CYGWIN_INFO_CYGWIN_REGISTRY_NAME, | |
106 | CYGWIN_INFO_CYGWIN_SETUP_REGISTRY_NAME); | |
107 | HKEY kr = (root_scope == IDC_ROOT_USER) ? HKEY_CURRENT_USER | |
108 | : HKEY_LOCAL_MACHINE; | |
507e5b52 CV |
109 | do |
110 | { | |
111 | rv = RegCreateKeyEx (kr, buf, 0, (char *)"Cygwin", 0, | |
112 | KEY_ALL_ACCESS | SETUP_KEY_WOW64, | |
113 | 0, &key, &disposition); | |
114 | if (rv != ERROR_ACCESS_DENIED || kr != HKEY_LOCAL_MACHINE) | |
115 | break; | |
157dc2b8 | 116 | Log (LOG_PLAIN) << "Access denied trying to create rootdir registry key" |
507e5b52 CV |
117 | << endLog; |
118 | kr = HKEY_CURRENT_USER; | |
119 | } | |
120 | while (rv == ERROR_ACCESS_DENIED); | |
121 | if (rv == ERROR_SUCCESS) | |
122 | do | |
123 | { | |
124 | rv = RegSetValueEx (key, "rootdir", 0, REG_SZ, | |
125 | (BYTE *) get_root_dir ().c_str (), | |
126 | get_root_dir ().size () + 1); | |
127 | if (rv != ERROR_ACCESS_DENIED || kr != HKEY_LOCAL_MACHINE) | |
128 | break; | |
157dc2b8 | 129 | Log (LOG_PLAIN) << "Access denied trying to create rootdir registry value" |
507e5b52 CV |
130 | << endLog; |
131 | kr = HKEY_CURRENT_USER; | |
132 | } | |
133 | while (rv == ERROR_ACCESS_DENIED); | |
2f0315ad | 134 | if (rv != ERROR_SUCCESS) |
e0373ab5 | 135 | mbox (NULL, "Couldn't create registry key\n" |
507e5b52 CV |
136 | "to store installation path", |
137 | "Cygwin Setup", MB_OK | MB_ICONWARNING); | |
2f0315ad CV |
138 | RegCloseKey (key); |
139 | ||
b0bb51b4 CV |
140 | // The mount table is already in the right shape at this point. |
141 | // Reading it again is not necessary. | |
142 | //read_mounts (std::string ()); | |
2f0315ad CV |
143 | } |
144 | ||
65288dc7 CV |
145 | inline char * |
146 | unconvert_slashes (char *in_name) | |
147 | { | |
148 | char *name = in_name; | |
149 | while ((name = strchr (name, '/')) != NULL) | |
150 | *name++ = '\\'; | |
151 | return in_name; | |
152 | } | |
153 | ||
154 | inline char * | |
155 | skip_ws (char *in) | |
156 | { | |
157 | while (*in == ' ' || *in == '\t') | |
158 | ++in; | |
159 | return in; | |
160 | } | |
161 | ||
162 | inline char * | |
163 | find_ws (char *in) | |
164 | { | |
165 | while (*in && *in != ' ' && *in != '\t') | |
166 | ++in; | |
167 | return in; | |
168 | } | |
169 | ||
170 | inline char * | |
171 | conv_fstab_spaces (char *field) | |
172 | { | |
173 | register char *sp = field; | |
ef3be327 | 174 | while ((sp = strstr (sp, "\\040"))) |
65288dc7 CV |
175 | { |
176 | *sp++ = ' '; | |
177 | memmove (sp, sp + 3, strlen (sp + 3) + 1); | |
178 | } | |
179 | return field; | |
180 | } | |
181 | ||
d2e8d256 CV |
182 | static bool got_usr_bin; |
183 | static bool got_usr_lib; | |
184 | ||
65288dc7 CV |
185 | static bool |
186 | from_fstab_line (mnt *m, char *line) | |
187 | { | |
188 | char *native_path, *posix_path, *fs_type; | |
189 | ||
190 | /* First field: Native path. */ | |
191 | char *c = skip_ws (line); | |
192 | if (!*c || *c == '#') | |
193 | return false; | |
194 | char *cend = find_ws (c); | |
195 | *cend = '\0'; | |
196 | native_path = conv_fstab_spaces (c); | |
197 | /* Second field: POSIX path. */ | |
198 | c = skip_ws (cend + 1); | |
199 | if (!*c) | |
200 | return false; | |
201 | cend = find_ws (c); | |
202 | *cend = '\0'; | |
203 | posix_path = conv_fstab_spaces (c); | |
204 | /* Third field: FS type. */ | |
205 | c = skip_ws (cend + 1); | |
206 | if (!*c) | |
207 | return false; | |
208 | cend = find_ws (c); | |
209 | *cend = '\0'; | |
210 | fs_type = c; | |
211 | ||
212 | if (strcmp (fs_type, "cygdrive")) | |
213 | { | |
214 | for (mnt *sm = mount_table; sm < m; ++sm) | |
215 | if (sm->posix == std::string (posix_path)) | |
216 | { | |
217 | sm->native = std::string (unconvert_slashes (native_path)); | |
218 | return false; | |
219 | } | |
220 | m->posix = std::string (posix_path); | |
221 | m->native = std::string (unconvert_slashes (native_path)); | |
d2e8d256 CV |
222 | if (!strcmp (posix_path, "/usr/bin")) |
223 | got_usr_bin = true; | |
224 | else if (!strcmp (posix_path, "/usr/lib")) | |
225 | got_usr_lib = true; | |
65288dc7 CV |
226 | } |
227 | return true; | |
228 | } | |
229 | ||
230 | #define BUFSIZE 65536 | |
8669c59a | 231 | #define LFSTAB L"\\etc\\fstab" |
65288dc7 CV |
232 | |
233 | static bool | |
5524f268 | 234 | from_fstab (mnt *m, const std::string& in_path) |
65288dc7 CV |
235 | { |
236 | char buf[BUFSIZE]; | |
8669c59a | 237 | WCHAR path[in_path.size () + sizeof (LFSTAB)]; |
65288dc7 | 238 | |
8669c59a CF |
239 | mklongpath (path, in_path.c_str (), sizeof (path) / sizeof (WCHAR)); |
240 | wcscat (path, LFSTAB); | |
65288dc7 | 241 | HANDLE h = CreateFileW (path, GENERIC_READ, FILE_SHARE_READ, NULL, |
b41c2908 CV |
242 | OPEN_EXISTING, |
243 | FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, | |
244 | NULL); | |
65288dc7 CV |
245 | if (h == INVALID_HANDLE_VALUE) |
246 | return false; | |
247 | char *got = buf; | |
248 | DWORD len = 0; | |
249 | /* Using BUFSIZE-2 leaves space to append two \0. */ | |
250 | while (ReadFile (h, got, BUFSIZE - 2 - (got - buf), &len, NULL)) | |
251 | { | |
252 | char *end; | |
253 | ||
254 | /* Set end marker. */ | |
255 | got[len] = got[len + 1] = '\0'; | |
256 | /* Set len to the absolute len of bytes in buf. */ | |
257 | len += got - buf; | |
258 | /* Reset got to start reading at the start of the buffer again. */ | |
259 | got = buf; | |
260 | while (got < buf + len && (end = strchr (got, '\n'))) | |
261 | { | |
262 | end[end[-1] == '\r' ? -1 : 0] = '\0'; | |
263 | if (from_fstab_line (m, got)) | |
264 | ++m; | |
265 | got = end + 1; | |
266 | } | |
267 | if (len < BUFSIZE - 1) | |
268 | break; | |
269 | /* We have to read once more. Move remaining bytes to the start of | |
270 | the buffer and reposition got so that it points to the end of | |
271 | the remaining bytes. */ | |
272 | len = buf + len - got; | |
273 | memmove (buf, got, len); | |
274 | got = buf + len; | |
275 | buf[len] = buf[len + 1] = '\0'; | |
276 | } | |
277 | if (got > buf && from_fstab_line (m, got)) | |
278 | ++m; | |
279 | CloseHandle (h); | |
280 | return true; | |
281 | } | |
282 | ||
2f0315ad CV |
283 | static void |
284 | add_usr_mnts (struct mnt *m) | |
285 | { | |
286 | /* Set default /usr/bin and /usr/lib */ | |
d2e8d256 CV |
287 | if (!got_usr_bin) |
288 | { | |
289 | m->posix = "/usr/bin"; | |
290 | m->native = root_here->native + "\\bin"; | |
291 | ++m; | |
292 | } | |
293 | if (!got_usr_lib) | |
294 | { | |
295 | m->posix = "/usr/lib"; | |
296 | m->native = root_here->native + "\\lib"; | |
297 | } | |
2f0315ad CV |
298 | } |
299 | ||
1e029da2 YS |
300 | void |
301 | read_mounts (const std::string val) | |
2f0315ad CV |
302 | { |
303 | DWORD posix_path_size; | |
304 | struct mnt *m = mount_table; | |
2f0315ad CV |
305 | char buf[10000]; |
306 | ||
307 | root_here = NULL; | |
308 | for (mnt * m1 = mount_table; m1->posix.size (); m1++) | |
309 | { | |
310 | m1->posix.clear(); | |
311 | m1->native.clear(); | |
312 | } | |
d2e8d256 | 313 | got_usr_bin = got_usr_lib = false; |
2f0315ad | 314 | |
507e5b52 | 315 | root_scope = (nt_sec.isRunAsAdmin ())? IDC_ROOT_SYSTEM : IDC_ROOT_USER; |
2f0315ad CV |
316 | |
317 | if (val.size ()) | |
318 | { | |
a9a8e93a CV |
319 | /* Cygwin rootdir always < MAX_PATH. */ |
320 | char rootdir[MAX_PATH + 1]; | |
321 | ||
322 | if (GetFullPathName (val.c_str (), MAX_PATH + 1, rootdir, NULL)) | |
323 | { | |
324 | m->native = rootdir; | |
325 | m->posix = "/"; | |
326 | root_here = m; | |
327 | add_usr_mnts (++m); | |
328 | } | |
2f0315ad CV |
329 | } |
330 | else | |
331 | { | |
332 | /* Always check HKEY_LOCAL_MACHINE first. */ | |
333 | for (int isuser = 0; isuser <= 1; isuser++) | |
334 | { | |
335 | snprintf (buf, sizeof(buf), "Software\\%s\\%s", | |
336 | CYGWIN_INFO_CYGWIN_REGISTRY_NAME, | |
337 | CYGWIN_INFO_CYGWIN_SETUP_REGISTRY_NAME); | |
338 | HKEY key = isuser ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE; | |
9fadc65f CV |
339 | if (RegOpenKeyEx (key, buf, 0, KEY_ALL_ACCESS | SETUP_KEY_WOW64, |
340 | &key) != ERROR_SUCCESS) | |
b0bb51b4 | 341 | continue; |
2f0315ad | 342 | DWORD type; |
7cc4d95e | 343 | /* Cygwin rootdir always < MAX_PATH. */ |
2f0315ad CV |
344 | char aBuffer[MAX_PATH + 1]; |
345 | posix_path_size = MAX_PATH; | |
346 | if (RegQueryValueEx | |
347 | (key, "rootdir", 0, &type, (BYTE *) aBuffer, | |
348 | &posix_path_size) == ERROR_SUCCESS) | |
349 | { | |
350 | m->native = std::string (aBuffer); | |
351 | m->posix = "/"; | |
352 | root_scope = isuser ? IDC_ROOT_USER : IDC_ROOT_SYSTEM; | |
65288dc7 | 353 | root_here = m++; |
d2e8d256 CV |
354 | from_fstab (m, root_here->native); |
355 | add_usr_mnts (m); | |
2f0315ad CV |
356 | break; |
357 | } | |
358 | RegCloseKey (key); | |
359 | } | |
360 | } | |
361 | ||
2f0315ad CV |
362 | if (!root_here) |
363 | { | |
7cc4d95e | 364 | /* Affected path always < MAX_PATH. */ |
2f0315ad | 365 | char windir[MAX_PATH]; |
7cc4d95e | 366 | GetSystemWindowsDirectory (windir, sizeof (windir)); |
2f0315ad | 367 | windir[2] = 0; |
e49fef4d | 368 | m->native = std::string (windir) + (is_64bit ? "\\cygwin64" : "\\cygwin"); |
2f0315ad CV |
369 | m->posix = "/"; |
370 | root_here = m; | |
371 | add_usr_mnts (++m); | |
372 | } | |
373 | } | |
374 | ||
85b43844 | 375 | void |
2bba98e8 | 376 | set_root_dir (const std::string val) |
85b43844 | 377 | { |
1e029da2 | 378 | read_mounts (val); |
85b43844 CF |
379 | } |
380 | ||
d396ed10 CV |
381 | static std::string empty; |
382 | ||
383 | const std::string & | |
85b43844 CF |
384 | get_root_dir () |
385 | { | |
d396ed10 | 386 | return root_here ? root_here->native : empty; |
85b43844 CF |
387 | } |
388 | ||
a351e48c CF |
389 | /* Return non-zero if PATH1 is a prefix of PATH2. |
390 | Both are assumed to be of the same path style and / vs \ usage. | |
391 | Neither may be "". | |
a351e48c CF |
392 | |
393 | Examples: | |
394 | /foo/ is a prefix of /foo <-- may seem odd, but desired | |
395 | /foo is a prefix of /foo/ | |
396 | / is a prefix of /foo/bar | |
397 | / is not a prefix of foo/bar | |
398 | foo/ is a prefix foo/bar | |
399 | /foo is not a prefix of /foobar | |
400 | */ | |
401 | ||
402 | static int | |
2bba98e8 | 403 | path_prefix_p (const std::string path1, const std::string path2) |
a351e48c | 404 | { |
3c054baf | 405 | size_t len1 = path1.size (); |
a351e48c | 406 | /* Handle case where PATH1 has trailing '/' and when it doesn't. */ |
d2a3615c | 407 | if (len1 > 0 && SLASH_P (path1.c_str ()[len1 - 1])) |
3c054baf | 408 | --len1; |
a351e48c CF |
409 | |
410 | if (len1 == 0) | |
d2a3615c MB |
411 | return SLASH_P (path2.c_str ()[0]) |
412 | && !SLASH_P (path2.c_str ()[1]); | |
a351e48c | 413 | |
afa76033 | 414 | if (casecompare(path1, path2, len1) != 0) |
a351e48c CF |
415 | return 0; |
416 | ||
d2a3615c MB |
417 | return SLASH_P (path2.c_str ()[len1]) || path2.size () == len1 |
418 | || path1.c_str ()[len1 - 1] == ':'; | |
a351e48c CF |
419 | } |
420 | ||
2bba98e8 MB |
421 | std::string |
422 | cygpath (const std::string& thePath) | |
a351e48c | 423 | { |
3c054baf | 424 | size_t max_len = 0; |
b24c88b3 | 425 | struct mnt *m, *match = NULL; |
3c054baf | 426 | for (m = mount_table; m->posix.size (); m++) |
a351e48c | 427 | { |
3c054baf RC |
428 | size_t n = m->posix.size (); |
429 | if (n <= max_len || !path_prefix_p (m->posix, thePath)) | |
a351e48c CF |
430 | continue; |
431 | max_len = n; | |
432 | match = m; | |
433 | } | |
434 | ||
b24c88b3 | 435 | if (!match) |
2bba98e8 | 436 | return std::string(); |
b24c88b3 | 437 | |
2bba98e8 | 438 | std::string native; |
3c054baf RC |
439 | if (max_len == thePath.size ()) |
440 | { | |
441 | native = match->native; | |
442 | } | |
2f0315ad CV |
443 | else if (match->posix.size () > 1) |
444 | native = match->native + thePath.substr(max_len, std::string::npos); | |
a351e48c | 445 | else |
2bba98e8 | 446 | native = match->native + "/" + thePath.substr(max_len, std::string::npos); |
a351e48c CF |
447 | return native; |
448 | } |