]> cygwin.com Git - newlib-cygwin.git/blob - winsup/utils/path.cc
fe55a646d9d8903423b5a3251d3c74d129978bee
[newlib-cygwin.git] / winsup / utils / path.cc
1 /* path.cc
2
3 This file is part of Cygwin.
4
5 This software is a copyrighted work licensed under the terms of the
6 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
7 details. */
8
9 /* The purpose of this file is to hide all the details about accessing
10 Cygwin's mount table, shortcuts, etc. If the format or location of
11 the mount table, or the shortcut format changes, this is the file to
12 change to match it. */
13
14 #define str(a) #a
15 #define scat(a,b) str(a##b)
16 #include <windows.h>
17 #include <lmcons.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <malloc.h>
21 #include <wchar.h>
22 #include "path.h"
23 #include <cygwin/version.h>
24 #include <cygwin/bits.h>
25 #include <sys/mount.h>
26 #define _NOMNTENT_MACROS
27 #include <mntent.h>
28 #ifdef FSTAB_ONLY
29 #include <sys/cygwin.h>
30 #endif
31
32 #ifndef FSTAB_ONLY
33 /* Used when treating / and \ as equivalent. */
34 #define isslash(ch) \
35 ({ \
36 char __c = (ch); \
37 ((__c) == '/' || (__c) == '\\'); \
38 })
39
40
41 static const GUID GUID_shortcut =
42 {0x00021401L, 0, 0, {0xc0, 0, 0, 0, 0, 0, 0, 0x46}};
43
44 enum {
45 WSH_FLAG_IDLIST = 0x01, /* Contains an ITEMIDLIST. */
46 WSH_FLAG_FILE = 0x02, /* Contains a file locator element. */
47 WSH_FLAG_DESC = 0x04, /* Contains a description. */
48 WSH_FLAG_RELPATH = 0x08, /* Contains a relative path. */
49 WSH_FLAG_WD = 0x10, /* Contains a working dir. */
50 WSH_FLAG_CMDLINE = 0x20, /* Contains command line args. */
51 WSH_FLAG_ICON = 0x40 /* Contains a custom icon. */
52 };
53
54 struct win_shortcut_hdr
55 {
56 DWORD size; /* Header size in bytes. Must contain 0x4c. */
57 GUID magic; /* GUID of shortcut files. */
58 DWORD flags; /* Content flags. See above. */
59
60 /* The next fields from attr to icon_no are always set to 0 in Cygwin
61 and U/Win shortcuts. */
62 DWORD attr; /* Target file attributes. */
63 FILETIME ctime; /* These filetime items are never touched by the */
64 FILETIME mtime; /* system, apparently. Values don't matter. */
65 FILETIME atime;
66 DWORD filesize; /* Target filesize. */
67 DWORD icon_no; /* Icon number. */
68
69 DWORD run; /* Values defined in winuser.h. Use SW_NORMAL. */
70 DWORD hotkey; /* Hotkey value. Set to 0. */
71 DWORD dummy[2]; /* Future extension probably. Always 0. */
72 };
73
74 static bool
75 cmp_shortcut_header (win_shortcut_hdr *file_header)
76 {
77 /* A Cygwin or U/Win shortcut only contains a description and a relpath.
78 Cygwin shortcuts also might contain an ITEMIDLIST. The run type is
79 always set to SW_NORMAL. */
80 return file_header->size == sizeof (win_shortcut_hdr)
81 && !memcmp (&file_header->magic, &GUID_shortcut, sizeof GUID_shortcut)
82 && (file_header->flags & ~WSH_FLAG_IDLIST)
83 == (WSH_FLAG_DESC | WSH_FLAG_RELPATH)
84 && file_header->run == SW_NORMAL;
85 }
86
87 int
88 get_word (HANDLE fh, int offset)
89 {
90 unsigned short rv;
91 unsigned r;
92
93 SetLastError(NO_ERROR);
94 if (SetFilePointer (fh, offset, 0, FILE_BEGIN) == INVALID_SET_FILE_POINTER
95 && GetLastError () != NO_ERROR)
96 return -1;
97
98 if (!ReadFile (fh, &rv, 2, (DWORD *) &r, 0))
99 return -1;
100
101 return rv;
102 }
103
104 /*
105 * Check the value of GetLastError() to find out whether there was an error.
106 */
107 int
108 get_dword (HANDLE fh, int offset)
109 {
110 int rv;
111 unsigned r;
112
113 SetLastError(NO_ERROR);
114 if (SetFilePointer (fh, offset, 0, FILE_BEGIN) == INVALID_SET_FILE_POINTER
115 && GetLastError () != NO_ERROR)
116 return -1;
117
118 if (!ReadFile (fh, &rv, 4, (DWORD *) &r, 0))
119 return -1;
120
121 return rv;
122 }
123
124 #define EXE_MAGIC ((int)*(unsigned short *)"MZ")
125 #define SHORTCUT_MAGIC ((int)*(unsigned short *)"L\0")
126 #define SYMLINK_COOKIE "!<symlink>"
127 #define SYMLINK_MAGIC ((int)*(unsigned short *)SYMLINK_COOKIE)
128
129 bool
130 is_exe (HANDLE fh)
131 {
132 int magic = get_word (fh, 0x0);
133 return magic == EXE_MAGIC;
134 }
135
136 bool
137 is_symlink (HANDLE fh)
138 {
139 bool ret = false;
140 int magic = get_word (fh, 0x0);
141 if (magic != SHORTCUT_MAGIC && magic != SYMLINK_MAGIC)
142 goto out;
143 DWORD got;
144 BY_HANDLE_FILE_INFORMATION local;
145 if (!GetFileInformationByHandle (fh, &local))
146 return false;
147 if (magic == SHORTCUT_MAGIC)
148 {
149 DWORD size;
150 if (!local.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
151 goto out; /* Not a Cygwin symlink. */
152 if ((size = GetFileSize (fh, NULL)) > 8192)
153 goto out; /* Not a Cygwin symlink. */
154 char buf[size];
155 SetFilePointer (fh, 0, 0, FILE_BEGIN);
156 if (!ReadFile (fh, buf, size, &got, 0))
157 goto out;
158 if (got != size || !cmp_shortcut_header ((win_shortcut_hdr *) buf))
159 goto out; /* Not a Cygwin symlink. */
160 /* TODO: check for invalid path contents
161 (see symlink_info::check() in ../cygwin/path.cc) */
162 }
163 else /* magic == SYMLINK_MAGIC */
164 {
165 if (!(local.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM))
166 goto out; /* Not a Cygwin symlink. */
167 char buf[sizeof (SYMLINK_COOKIE) - 1];
168 SetFilePointer (fh, 0, 0, FILE_BEGIN);
169 if (!ReadFile (fh, buf, sizeof (buf), &got, 0))
170 goto out;
171 if (got != sizeof (buf) ||
172 memcmp (buf, SYMLINK_COOKIE, sizeof (buf)) != 0)
173 goto out; /* Not a Cygwin symlink. */
174 }
175 ret = true;
176 out:
177 SetFilePointer (fh, 0, 0, FILE_BEGIN);
178 return ret;
179 }
180
181 /* Assumes is_symlink(fh) is true */
182 bool
183 readlink (HANDLE fh, char *path, size_t maxlen)
184 {
185 DWORD rv;
186 char *buf, *cp;
187 unsigned short len;
188 win_shortcut_hdr *file_header;
189 BY_HANDLE_FILE_INFORMATION fi;
190
191 if (!GetFileInformationByHandle (fh, &fi)
192 || fi.nFileSizeHigh != 0
193 || fi.nFileSizeLow > 4 * 65536)
194 return false;
195
196 buf = (char *) alloca (fi.nFileSizeLow + 1);
197 file_header = (win_shortcut_hdr *) buf;
198
199 if (!ReadFile (fh, buf, fi.nFileSizeLow, &rv, NULL)
200 || rv != fi.nFileSizeLow)
201 return false;
202
203 if (fi.nFileSizeLow > sizeof (file_header)
204 && cmp_shortcut_header (file_header))
205 {
206 cp = buf + sizeof (win_shortcut_hdr);
207 if (file_header->flags & WSH_FLAG_IDLIST) /* Skip ITEMIDLIST */
208 cp += *(unsigned short *) cp + 2;
209 if (!(len = *(unsigned short *) cp))
210 return false;
211 cp += 2;
212 /* Has appended full path? If so, use it instead of description. */
213 unsigned short relpath_len = *(unsigned short *) (cp + len);
214 if (cp + len + 2 + relpath_len < buf + fi.nFileSizeLow)
215 {
216 cp += len + 2 + relpath_len;
217 len = *(unsigned short *) cp;
218 cp += 2;
219 }
220 if (*(PWCHAR) cp == 0xfeff) /* BOM */
221 {
222 size_t wlen = wcstombs (NULL, (wchar_t *) (cp + 2), 0);
223 if (wlen == (size_t) -1 || wlen + 1 > maxlen)
224 return false;
225 wcstombs (path, (wchar_t *) (cp + 2), wlen + 1);
226 }
227 else if ((size_t) (len + 1) > maxlen)
228 return false;
229 else
230 memcpy (path, cp, len);
231 path[len] = '\0';
232 return true;
233 }
234 else if (strncmp (buf, SYMLINK_COOKIE, strlen (SYMLINK_COOKIE)) == 0
235 && buf[fi.nFileSizeLow - 1] == '\0')
236 {
237 cp = buf + strlen (SYMLINK_COOKIE);
238 if (*(PWCHAR) cp == 0xfeff) /* BOM */
239 {
240 size_t wlen = wcstombs (NULL, (wchar_t *) (cp + 2), 0);
241 if (wlen == (size_t) -1 || wlen + 1 > maxlen)
242 return false;
243 wcstombs (path, (wchar_t *) (cp + 2), wlen + 1);
244 }
245 else if (fi.nFileSizeLow - strlen (SYMLINK_COOKIE) > maxlen)
246 return false;
247 else
248 strcpy (path, cp);
249 return true;
250 }
251 else
252 return false;
253 }
254 #endif /* !FSTAB_ONLY */
255
256 mnt_t mount_table[255];
257 int max_mount_entry;
258
259 inline void
260 unconvert_slashes (char* name)
261 {
262 while ((name = strchr (name, '/')) != NULL)
263 *name++ = '\\';
264 }
265
266 inline char *
267 skip_ws (char *in)
268 {
269 while (*in == ' ' || *in == '\t')
270 ++in;
271 return in;
272 }
273
274 inline char *
275 find_ws (char *in)
276 {
277 while (*in && *in != ' ' && *in != '\t')
278 ++in;
279 return in;
280 }
281
282 inline char *
283 conv_fstab_spaces (char *field)
284 {
285 char *sp = field;
286 while ((sp = strstr (sp, "\\040")) != NULL)
287 {
288 *sp++ = ' ';
289 memmove (sp, sp + 3, strlen (sp + 3) + 1);
290 }
291 return field;
292 }
293
294 #ifndef FSTAB_ONLY
295 static struct opt
296 {
297 const char *name;
298 unsigned val;
299 bool clear;
300 } oopts[] =
301 {
302 {"acl", MOUNT_NOACL, 1},
303 {"auto", 0, 0},
304 {"binary", MOUNT_TEXT, 1},
305 {"cygexec", MOUNT_CYGWIN_EXEC, 0},
306 {"dos", MOUNT_DOS, 0},
307 {"exec", MOUNT_EXEC, 0},
308 {"ihash", MOUNT_IHASH, 0},
309 {"noacl", MOUNT_NOACL, 0},
310 {"nosuid", 0, 0},
311 {"notexec", MOUNT_NOTEXEC, 0},
312 {"nouser", MOUNT_SYSTEM, 0},
313 {"override", MOUNT_OVERRIDE, 0},
314 {"posix=0", MOUNT_NOPOSIX, 0},
315 {"posix=1", MOUNT_NOPOSIX, 1},
316 {"text", MOUNT_TEXT, 0},
317 {"user", MOUNT_SYSTEM, 1}
318 };
319
320 static bool
321 read_flags (char *options, unsigned &flags)
322 {
323 while (*options)
324 {
325 char *p = strchr (options, ',');
326 if (p)
327 *p++ = '\0';
328 else
329 p = strchr (options, '\0');
330
331 for (opt *o = oopts;
332 o < (oopts + (sizeof (oopts) / sizeof (oopts[0])));
333 o++)
334 if (strcmp (options, o->name) == 0)
335 {
336 if (o->clear)
337 flags &= ~o->val;
338 else
339 flags |= o->val;
340 goto gotit;
341 }
342 return false;
343
344 gotit:
345 options = p;
346 }
347 return true;
348 }
349 #endif
350
351 bool
352 from_fstab_line (mnt_t *m, char *line, bool user)
353 {
354 char *native_path, *posix_path, *fs_type;
355
356 /* First field: Native path. */
357 char *c = skip_ws (line);
358 if (!*c || *c == '#')
359 return false;
360 char *cend = find_ws (c);
361 *cend = '\0';
362 native_path = conv_fstab_spaces (c);
363 /* Second field: POSIX path. */
364 c = skip_ws (cend + 1);
365 if (!*c)
366 return false;
367 cend = find_ws (c);
368 *cend = '\0';
369 posix_path = conv_fstab_spaces (c);
370 /* Third field: FS type. */
371 c = skip_ws (cend + 1);
372 if (!*c)
373 return false;
374 cend = find_ws (c);
375 *cend = '\0';
376 fs_type = c;
377 /* Forth field: Flags. */
378 c = skip_ws (cend + 1);
379 if (!*c)
380 return false;
381 cend = find_ws (c);
382 *cend = '\0';
383 unsigned mount_flags = MOUNT_SYSTEM;
384 #ifndef FSTAB_ONLY
385 if (!read_flags (c, mount_flags))
386 #else
387 if (cygwin_internal (CW_CVT_MNT_OPTS, &c, &mount_flags))
388 #endif
389 return false;
390 if (user)
391 mount_flags &= ~MOUNT_SYSTEM;
392 if (!strcmp (fs_type, "cygdrive"))
393 {
394 for (mnt_t *sm = mount_table; sm < m; ++sm)
395 if (sm->flags & MOUNT_CYGDRIVE)
396 {
397 if ((mount_flags & MOUNT_SYSTEM) || !(sm->flags & MOUNT_SYSTEM))
398 {
399 if (sm->posix)
400 free (sm->posix);
401 sm->posix = strdup (posix_path);
402 sm->flags = mount_flags | MOUNT_CYGDRIVE;
403 }
404 return false;
405 }
406 m->posix = strdup (posix_path);
407 m->native = strdup ("cygdrive prefix");
408 m->flags = mount_flags | MOUNT_CYGDRIVE;
409 }
410 else
411 {
412 for (mnt_t *sm = mount_table; sm < m; ++sm)
413 if (!strcmp (sm->posix, posix_path))
414 {
415 /* Don't allow overriding of a system mount with a user mount. */
416 if ((sm->flags & MOUNT_SYSTEM) && !(mount_flags & MOUNT_SYSTEM))
417 return false;
418 if ((sm->flags & MOUNT_SYSTEM) != (mount_flags & MOUNT_SYSTEM))
419 continue;
420 /* Changing immutable mount points require the override flag. */
421 if ((sm->flags & MOUNT_IMMUTABLE)
422 && !(mount_flags & MOUNT_OVERRIDE))
423 return false;
424 if (mount_flags & MOUNT_OVERRIDE)
425 mount_flags |= MOUNT_IMMUTABLE;
426 if (sm->native)
427 free (sm->native);
428 sm->native = strdup (native_path);
429 sm->flags = mount_flags;
430 return false;
431 }
432 m->posix = strdup (posix_path);
433 /* Bind mounts require POSIX paths, otherwise the path is wrongly
434 prefixed with the Cygwin root dir when trying to convert it to
435 a Win32 path in mount(2). So don't convert slashes to backslashes
436 in this case. */
437 if (!(mount_flags & MOUNT_BIND))
438 unconvert_slashes (native_path);
439 m->native = strdup (native_path);
440 m->flags = mount_flags;
441 }
442 return true;
443 }
444
445 #ifndef FSTAB_ONLY
446
447 #define BUFSIZE 65536
448
449 static char *
450 get_user ()
451 {
452 static char user[UNLEN + 1];
453 char *userenv;
454
455 user[0] = '\0';
456 if ((userenv = getenv ("USER")) || (userenv = getenv ("USERNAME")))
457 strncat (user, userenv, UNLEN);
458 return user;
459 }
460
461 void
462 from_fstab (bool user, PWCHAR path, PWCHAR path_end)
463 {
464 mnt_t *m = mount_table + max_mount_entry;
465 char buf[BUFSIZE];
466
467 if (!user)
468 {
469 /* Create a default root dir from path. */
470 wcstombs (buf, path, BUFSIZE);
471 unconvert_slashes (buf);
472 char *native_path = buf;
473 if (!strncmp (native_path, "\\\\?\\", 4))
474 native_path += 4;
475 if (!strncmp (native_path, "UNC\\", 4))
476 *(native_path += 2) = '\\';
477 m->posix = strdup ("/");
478 m->native = strdup (native_path);
479 m->flags = MOUNT_SYSTEM | MOUNT_IMMUTABLE | MOUNT_AUTOMATIC;
480 ++m;
481 /* Create default /usr/bin and /usr/lib entries. */
482 char *trail = strchr (native_path, '\0');
483 strcpy (trail, "\\bin");
484 m->posix = strdup ("/usr/bin");
485 m->native = strdup (native_path);
486 m->flags = MOUNT_SYSTEM | MOUNT_AUTOMATIC;
487 ++m;
488 strcpy (trail, "\\lib");
489 m->posix = strdup ("/usr/lib");
490 m->native = strdup (native_path);
491 m->flags = MOUNT_SYSTEM | MOUNT_AUTOMATIC;
492 ++m;
493 /* Create a default cygdrive entry. Note that this is a user entry.
494 This allows to override it with mount, unless the sysadmin created
495 a cygdrive entry in /etc/fstab. */
496 m->posix = strdup (CYGWIN_INFO_CYGDRIVE_DEFAULT_PREFIX);
497 m->native = strdup ("cygdrive prefix");
498 m->flags = MOUNT_CYGDRIVE;
499 ++m;
500 max_mount_entry = m - mount_table;
501 }
502
503 PWCHAR u = wcscpy (path_end, L"\\etc\\fstab") + 10;
504 if (user)
505 mbstowcs (wcscpy (u, L".d\\") + 3, get_user (), BUFSIZE - (u - path));
506 HANDLE h = CreateFileW (path, GENERIC_READ, FILE_SHARE_READ, NULL,
507 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
508 if (h == INVALID_HANDLE_VALUE)
509 return;
510 char *got = buf;
511 DWORD len = 0;
512 /* Using BUFSIZE-1 leaves space to append two \0. */
513 while (ReadFile (h, got, BUFSIZE - 1 - (got - buf),
514 &len, NULL))
515 {
516 char *end;
517
518 /* Set end marker. */
519 got[len] = got[len + 1] = '\0';
520 /* Set len to the absolute len of bytes in buf. */
521 len += got - buf;
522 /* Reset got to start reading at the start of the buffer again. */
523 got = buf;
524 while (got < buf + len && (end = strchr (got, '\n')))
525 {
526 end[end[-1] == '\r' ? -1 : 0] = '\0';
527 if (from_fstab_line (m, got, user))
528 ++m;
529 got = end + 1;
530 }
531 if (len < BUFSIZE - 1)
532 break;
533 /* We have to read once more. Move remaining bytes to the start of
534 the buffer and reposition got so that it points to the end of
535 the remaining bytes. */
536 len = buf + len - got;
537 memmove (buf, got, len);
538 got = buf + len;
539 buf[len] = buf[len + 1] = '\0';
540 }
541 if (got > buf && from_fstab_line (m, got, user))
542 ++m;
543 max_mount_entry = m - mount_table;
544 CloseHandle (h);
545 }
546 #endif /* !FSTAB_ONLY */
547
548 #ifndef FSTAB_ONLY
549 #ifdef TESTSUITE
550 #define read_mounts testsuite_read_mounts
551 #else
552 static int
553 mnt_sort (const void *a, const void *b)
554 {
555 const mnt_t *ma = (const mnt_t *) a;
556 const mnt_t *mb = (const mnt_t *) b;
557 int ret;
558
559 ret = (ma->flags & MOUNT_CYGDRIVE) - (mb->flags & MOUNT_CYGDRIVE);
560 if (ret)
561 return ret;
562 ret = (ma->flags & MOUNT_SYSTEM) - (mb->flags & MOUNT_SYSTEM);
563 if (ret)
564 return ret;
565 return strcmp (ma->posix, mb->posix);
566 }
567
568 extern "C" WCHAR cygwin_dll_path[];
569
570 static void
571 read_mounts ()
572 {
573 HKEY setup_key;
574 LONG ret;
575 DWORD len;
576 WCHAR path[32768];
577 PWCHAR path_end;
578
579 for (mnt_t *m1 = mount_table; m1->posix; m1++)
580 {
581 free (m1->posix);
582 if (m1->native)
583 free ((char *) m1->native);
584 m1->posix = NULL;
585 }
586 max_mount_entry = 0;
587
588 /* First fetch the cygwin1.dll path from the LoadLibrary call in load_cygwin.
589 This utilizes the DLL search order to find a matching cygwin1.dll and to
590 compute the installation path from that DLL's path. */
591 if (cygwin_dll_path[0])
592 wcscpy (path, cygwin_dll_path);
593 /* If we can't load cygwin1.dll, check where cygcheck is living itself and
594 try to fetch installation path from here. Does cygwin1.dll exist in the
595 same path? This should only kick in if the cygwin1.dll in the same path
596 has been made non-executable for the current user accidentally. */
597 else if (!GetModuleFileNameW (NULL, path, 32768))
598 return;
599 path_end = wcsrchr (path, L'\\');
600 if (path_end)
601 {
602 if (!cygwin_dll_path[0])
603 {
604 wcscpy (path_end, L"\\cygwin1.dll");
605 DWORD attr = GetFileAttributesW (path);
606 if (attr == (DWORD) -1
607 || (attr & (FILE_ATTRIBUTE_DIRECTORY
608 | FILE_ATTRIBUTE_REPARSE_POINT)))
609 path_end = NULL;
610 }
611 if (path_end)
612 {
613 *path_end = L'\0';
614 path_end = wcsrchr (path, L'\\');
615 }
616 }
617 /* If we can't create a valid installation dir from that, try to fetch
618 the installation dir from the setup registry key. */
619 if (!path_end)
620 {
621 for (int i = 0; i < 2; ++i)
622 if ((ret = RegOpenKeyExW (i ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER,
623 L"Software\\Cygwin\\setup", 0,
624 KEY_READ, &setup_key)) == ERROR_SUCCESS)
625 {
626 len = 32768 * sizeof (WCHAR);
627 ret = RegQueryValueExW (setup_key, L"rootdir", NULL, NULL,
628 (PBYTE) path, &len);
629 RegCloseKey (setup_key);
630 if (ret == ERROR_SUCCESS)
631 break;
632 }
633 if (ret == ERROR_SUCCESS)
634 path_end = wcschr (path, L'\0');
635 }
636 /* If we can't fetch an installation dir, bail out. */
637 if (!path_end)
638 return;
639 *path_end = L'\0';
640
641 from_fstab (false, path, path_end);
642 from_fstab (true, path, path_end);
643 qsort (mount_table, max_mount_entry, sizeof (mnt_t), mnt_sort);
644 }
645 #endif
646
647 /* Return non-zero if PATH1 is a prefix of PATH2.
648 Both are assumed to be of the same path style and / vs \ usage.
649 Neither may be "".
650 LEN1 = strlen (PATH1). It's passed because often it's already known.
651
652 Examples:
653 /foo/ is a prefix of /foo <-- may seem odd, but desired
654 /foo is a prefix of /foo/
655 / is a prefix of /foo/bar
656 / is not a prefix of foo/bar
657 foo/ is a prefix foo/bar
658 /foo is not a prefix of /foobar
659 */
660
661 static int
662 path_prefix_p (const char *path1, const char *path2, size_t len1)
663 {
664 /* Handle case where PATH1 has trailing '/' and when it doesn't. */
665 if (len1 > 0 && isslash (path1[len1 - 1]))
666 len1--;
667
668 if (len1 == 0)
669 return isslash (path2[0]) && !isslash (path2[1]);
670
671 if (strncasecmp (path1, path2, len1) != 0)
672 return 0;
673
674 return isslash (path2[len1]) || path2[len1] == 0 || path1[len1 - 1] == ':';
675 }
676
677 static char *
678 vconcat (const char *s, va_list v)
679 {
680 int len;
681 char *rv, *arg;
682 va_list save_v = v;
683 int unc;
684
685 if (!s)
686 return 0;
687
688 len = strlen (s);
689
690 unc = isslash (*s) && isslash (s[1]);
691
692 while (1)
693 {
694 arg = va_arg (v, char *);
695 if (arg == 0)
696 break;
697 len += strlen (arg);
698 }
699 va_end (v);
700
701 rv = (char *) malloc (len + 1);
702 strcpy (rv, s);
703 v = save_v;
704 while (1)
705 {
706 arg = va_arg (v, char *);
707 if (arg == 0)
708 break;
709 strcat (rv, arg);
710 }
711 va_end (v);
712
713 char *d, *p;
714
715 /* concat is only used for urls and files, so we can safely
716 canonicalize the results */
717 for (p = d = rv; *p; p++)
718 {
719 *d++ = *p;
720 /* special case for URLs */
721 if (*p == ':' && p[1] == '/' && p[2] == '/' && p > rv + 1)
722 {
723 *d++ = *++p;
724 *d++ = *++p;
725 }
726 else if (isslash (*p))
727 {
728 if (p == rv && unc)
729 *d++ = *p++;
730 while (p[1] == '/')
731 p++;
732 }
733 }
734 *d = 0;
735
736 return rv;
737 }
738
739 static char *
740 concat (const char *s, ...)
741 {
742 va_list v;
743
744 va_start (v, s);
745
746 return vconcat (s, v);
747 }
748
749 #ifdef TESTSUITE
750 #undef GetCurrentDirectory
751 #define GetCurrentDirectory testsuite_getcwd
752 #endif
753
754 /* This is a helper function for when vcygpath is passed what appears
755 to be a relative POSIX path. We take a Win32 CWD (either as specified
756 in 'cwd' or as retrieved with GetCurrentDirectory() if 'cwd' is NULL)
757 and find the mount table entry with the longest match. We replace the
758 matching portion with the corresponding POSIX prefix, and to that append
759 's' and anything in 'v'. The returned result is a mostly-POSIX
760 absolute path -- 'mostly' because the portions of CWD that didn't
761 match the mount prefix will still have '\\' separators. */
762 static char *
763 rel_vconcat (const char *cwd, const char *s, va_list v)
764 {
765 char pathbuf[MAX_PATH];
766 if (!cwd || *cwd == '\0')
767 {
768 if (!GetCurrentDirectory (MAX_PATH, pathbuf))
769 return NULL;
770 cwd = pathbuf;
771 }
772
773 size_t max_len = 0;
774 mnt_t *m, *match = NULL;
775
776 for (m = mount_table; m->posix; m++)
777 {
778 if (m->flags & MOUNT_CYGDRIVE)
779 continue;
780
781 size_t n = strlen (m->native);
782 if (n < max_len || !path_prefix_p (m->native, cwd, n))
783 continue;
784 max_len = n;
785 match = m;
786 }
787
788 char *temppath;
789 if (!match)
790 // No prefix matched - best effort to return meaningful value.
791 temppath = concat (cwd, "/", s, NULL);
792 else if (strcmp (match->posix, "/") != 0)
793 // Matched on non-root. Copy matching prefix + remaining 'path'.
794 temppath = concat (match->posix, cwd + max_len, "/", s, NULL);
795 else if (cwd[max_len] == '\0')
796 // Matched on root and there's no remaining 'path'.
797 temppath = concat ("/", s, NULL);
798 else if (isslash (cwd[max_len]))
799 // Matched on root but remaining 'path' starts with a slash anyway.
800 temppath = concat (cwd + max_len, "/", s, NULL);
801 else
802 temppath = concat ("/", cwd + max_len, "/", s, NULL);
803
804 char *res = vconcat (temppath, v);
805 free (temppath);
806 return res;
807 }
808
809 /* Convert a POSIX path in 's' to an absolute Win32 path, and append
810 anything in 'v' to the end, returning the result. If 's' is a
811 relative path then 'cwd' is used as the working directory to make
812 it absolute. Pass NULL in 'cwd' to use GetCurrentDirectory. */
813 static char *
814 vcygpath (const char *cwd, const char *s, va_list v)
815 {
816 size_t max_len = 0;
817 mnt_t *m, *match = NULL;
818
819 if (!max_mount_entry)
820 read_mounts ();
821
822 char *path;
823 if (s[0] == '.' && isslash (s[1]))
824 s += 2;
825
826 if (s[0] == '/' || s[1] == ':') /* FIXME: too crude? */
827 path = vconcat (s, v);
828 else
829 path = rel_vconcat (cwd, s, v);
830
831 if (!path)
832 return NULL;
833
834 if (strncmp (path, "/./", 3) == 0)
835 memmove (path + 1, path + 3, strlen (path + 3) + 1);
836
837 for (m = mount_table; m->posix; m++)
838 {
839 size_t n = strlen (m->posix);
840 if (n < max_len || !path_prefix_p (m->posix, path, n))
841 continue;
842 if (m->flags & MOUNT_CYGDRIVE)
843 {
844 if (strlen (path) < n + 2)
845 continue;
846 /* If cygdrive path is just '/', fix n for followup evaluation. */
847 if (n == 1)
848 n = 0;
849 if (path[n] != '/')
850 continue;
851 if (!isalpha (path[n + 1]))
852 continue;
853 if (path[n + 2] != '/')
854 continue;
855 }
856 max_len = n;
857 match = m;
858 }
859
860 char *native;
861 if (match == NULL)
862 native = strdup (path);
863 else if (max_len == strlen (path))
864 native = strdup (match->native);
865 else if (match->flags & MOUNT_CYGDRIVE)
866 {
867 char drive[3] = { path[max_len + 1], ':', '\0' };
868 native = concat (drive, path + max_len + 2, NULL);
869 }
870 else if (isslash (path[max_len]))
871 native = concat (match->native, path + max_len, NULL);
872 else
873 native = concat (match->native, "\\", path + max_len, NULL);
874 free (path);
875
876 unconvert_slashes (native);
877 for (char *s = strstr (native + 1, "\\.\\"); s && *s; s = strstr (s, "\\.\\"))
878 memmove (s + 1, s + 3, strlen (s + 3) + 1);
879 return native;
880 }
881
882 char *
883 cygpath_rel (const char *cwd, const char *s, ...)
884 {
885 va_list v;
886
887 va_start (v, s);
888
889 return vcygpath (cwd, s, v);
890 }
891
892 char *
893 cygpath (const char *s, ...)
894 {
895 va_list v;
896
897 va_start (v, s);
898
899 return vcygpath (NULL, s, v);
900 }
901
902 static mnt_t *m = NULL;
903
904 extern "C" FILE *
905 setmntent (const char *, const char *)
906 {
907 m = mount_table;
908
909 if (!max_mount_entry)
910 read_mounts ();
911
912 return NULL;
913 }
914
915 extern "C" struct mntent *
916 getmntent (FILE *)
917 {
918 static mntent mnt;
919 if (!m->posix)
920 return NULL;
921
922 mnt.mnt_fsname = (char *) m->native;
923 mnt.mnt_dir = (char *) m->posix;
924 if (!mnt.mnt_type)
925 mnt.mnt_type = (char *) malloc (16);
926 if (!mnt.mnt_opts)
927 mnt.mnt_opts = (char *) malloc (64);
928
929 strcpy (mnt.mnt_type,
930 (char *) ((m->flags & MOUNT_SYSTEM) ? "system" : "user"));
931
932 if (m->flags & MOUNT_TEXT)
933 strcpy (mnt.mnt_opts, (char *) "text");
934 else
935 strcpy (mnt.mnt_opts, (char *) "binary");
936
937 if (m->flags & MOUNT_CYGWIN_EXEC)
938 strcat (mnt.mnt_opts, (char *) ",cygexec");
939 else if (m->flags & MOUNT_EXEC)
940 strcat (mnt.mnt_opts, (char *) ",exec");
941 else if (m->flags & MOUNT_NOTEXEC)
942 strcat (mnt.mnt_opts, (char *) ",notexec");
943
944 if (m->flags & MOUNT_NOACL)
945 strcat (mnt.mnt_opts, (char *) ",noacl");
946
947 if (m->flags & MOUNT_NOPOSIX)
948 strcat (mnt.mnt_opts, (char *) ",posix=0");
949
950 if (m->flags & (MOUNT_AUTOMATIC | MOUNT_CYGDRIVE))
951 strcat (mnt.mnt_opts, (char *) ",auto");
952
953 mnt.mnt_freq = 1;
954 mnt.mnt_passno = 1;
955 m++;
956 return &mnt;
957 }
958
959 #endif /* !FSTAB_ONLY */
This page took 0.086129 seconds and 6 git commands to generate.