]> cygwin.com Git - newlib-cygwin.git/blob - winsup/cygwin/path.cc
libgloss: riscv: Fix envp parameter for main
[newlib-cygwin.git] / winsup / cygwin / path.cc
1 /* path.cc: path support.
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 /* This module's job is to
10 - convert between POSIX and Win32 style filenames,
11 - support the `mount' functionality,
12 - support symlinks for files and directories
13
14 Pathnames are handled as follows:
15
16 - A \ or : in a path denotes a pure windows spec.
17 - Paths beginning with // (or \\) are not translated (i.e. looked
18 up in the mount table) and are assumed to be UNC path names.
19
20 The goal in the above set of rules is to allow both POSIX and Win32
21 flavors of pathnames without either interfering. The rules are
22 intended to be as close to a superset of both as possible.
23
24 Note that you can have more than one path to a file. The mount
25 table is always prefered when translating Win32 paths to POSIX
26 paths. Win32 paths in mount table entries may be UNC paths or
27 standard Win32 paths starting with <drive-letter>:
28
29 Text vs Binary issues are not considered here in path style
30 decisions, although the appropriate flags are retrieved and
31 stored in various structures.
32
33 Removing mounted filesystem support would simplify things greatly,
34 but having it gives us a mechanism of treating disk that lives on a
35 UNIX machine as having UNIX semantics [it allows one to edit a text
36 file on that disk and not have cr's magically appear and perhaps
37 break apps running on UNIX boxes]. It also useful to be able to
38 layout a hierarchy without changing the underlying directories.
39
40 The semantics of mounting file systems is not intended to precisely
41 follow normal UNIX systems.
42
43 Each DOS drive is defined to have a current directory. Supporting
44 this would complicate things so for now things are defined so that
45 c: means c:\.
46 */
47
48 /* This file includes both the XPG and GNU basename functions, with the
49 former exported as "basename" for ABI compatibility but the latter
50 declared as such for source compatibility with glibc. This tells
51 <string.h> not to declare the GNU variant in order to prevent a conflicting
52 declaration error with the XPG variant implemented herein. */
53 #define basename basename
54 #include "winsup.h"
55 #include <w32api/winioctl.h>
56 #include <w32api/shlobj.h>
57 #include <sys/param.h>
58 #include <sys/cygwin.h>
59 #include <wctype.h>
60 #include <assert.h>
61 #include "cygerrno.h"
62 #include "path.h"
63 #include "fhandler.h"
64 #include "dtable.h"
65 #include "cygheap.h"
66 #include "shared_info.h"
67 #include "tls_pbuf.h"
68 #include "environ.h"
69 #undef basename
70
71 suffix_info stat_suffixes[] =
72 {
73 suffix_info ("", 1),
74 suffix_info (".exe", 1),
75 suffix_info (NULL)
76 };
77
78 struct symlink_info
79 {
80 char contents[SYMLINK_MAX + 1];
81 char *ext_here;
82 int extn;
83 unsigned path_flags;
84 unsigned mount_flags;
85 unsigned pc_flags; /* Relevant pathconv_arg flags from path_conv caller */
86 DWORD fileattr;
87 int issymlink;
88 bool ext_tacked_on;
89 int error;
90 bool isdevice;
91 _major_t major;
92 _minor_t minor;
93 __mode_t mode;
94 int check (char *path, const suffix_info *suffixes, fs_info &fs,
95 path_conv_handle &conv_hdl);
96 int set (char *path);
97 bool parse_device (const char *);
98 int check_sysfile (HANDLE h);
99 int check_shortcut (HANDLE h);
100 int check_reparse_point (HANDLE h, bool remote);
101 int check_nfs_symlink (HANDLE h);
102 int posixify (char *srcbuf);
103 bool set_error (int);
104 };
105
106 muto NO_COPY cwdstuff::cwd_lock;
107
108 static const GUID GUID_shortcut
109 = { 0x00021401L, 0, 0, {0xc0, 0, 0, 0, 0, 0, 0, 0x46}};
110
111 enum
112 {
113 WSH_FLAG_IDLIST = 0x01, /* Contains an ITEMIDLIST. */
114 WSH_FLAG_FILE = 0x02, /* Contains a file locator element. */
115 WSH_FLAG_DESC = 0x04, /* Contains a description. */
116 WSH_FLAG_RELPATH = 0x08, /* Contains a relative path. */
117 WSH_FLAG_WD = 0x10, /* Contains a working dir. */
118 WSH_FLAG_CMDLINE = 0x20, /* Contains command line args. */
119 WSH_FLAG_ICON = 0x40 /* Contains a custom icon. */
120 };
121
122 struct win_shortcut_hdr
123 {
124 DWORD size; /* Header size in bytes. Must contain 0x4c. */
125 GUID magic; /* GUID of shortcut files. */
126 DWORD flags; /* Content flags. See above. */
127
128 /* The next fields from attr to icon_no are always set to 0 in Cygwin
129 and U/Win shortcuts. */
130 DWORD attr; /* Target file attributes. */
131 FILETIME ctime; /* These filetime items are never touched by the */
132 FILETIME mtime; /* system, apparently. Values don't matter. */
133 FILETIME atime;
134 DWORD filesize; /* Target filesize. */
135 DWORD icon_no; /* Icon number. */
136
137 DWORD run; /* Values defined in winuser.h. Use SW_NORMAL. */
138 DWORD hotkey; /* Hotkey value. Set to 0. */
139 DWORD dummy[2]; /* Future extension probably. Always 0. */
140 };
141
142 /* Return non-zero if PATH1 is a prefix of PATH2.
143 Both are assumed to be of the same path style and / vs \ usage.
144 Neither may be "".
145 LEN1 = strlen (PATH1). It's passed because often it's already known.
146
147 Examples:
148 /foo/ is a prefix of /foo <-- may seem odd, but desired
149 /foo is a prefix of /foo/
150 / is a prefix of /foo/bar
151 / is not a prefix of foo/bar
152 foo/ is a prefix foo/bar
153 /foo is not a prefix of /foobar
154 */
155
156 int
157 path_prefix_p (const char *path1, const char *path2, int len1,
158 bool caseinsensitive)
159 {
160 /* Handle case where PATH1 has trailing '/' and when it doesn't. */
161 if (len1 > 0 && isdirsep (path1[len1 - 1]))
162 len1--;
163
164 if (len1 == 0)
165 return isdirsep (path2[0]) && !isdirsep (path2[1]);
166
167 if (isdirsep (path2[len1]) || path2[len1] == 0 || path1[len1 - 1] == ':')
168 return caseinsensitive ? strncasematch (path1, path2, len1)
169 : !strncmp (path1, path2, len1);
170
171 return 0;
172 }
173
174 /* Return non-zero if paths match in first len chars.
175 Check is dependent of the case sensitivity setting. */
176 int
177 pathnmatch (const char *path1, const char *path2, int len, bool caseinsensitive)
178 {
179 return caseinsensitive
180 ? strncasematch (path1, path2, len) : !strncmp (path1, path2, len);
181 }
182
183 /* Return non-zero if paths match. Check is dependent of the case
184 sensitivity setting. */
185 int
186 pathmatch (const char *path1, const char *path2, bool caseinsensitive)
187 {
188 return caseinsensitive
189 ? strcasematch (path1, path2) : !strcmp (path1, path2);
190 }
191
192 /* TODO: This function is used in mkdir and rmdir to generate correct
193 error messages in case of paths ending in /. or /.. components.
194 Right now, normalize_posix_path will just normalize
195 those components away, which changes the semantics. */
196 bool
197 has_dot_last_component (const char *dir, bool test_dot_dot)
198 {
199 /* SUSv3: . and .. are not allowed as last components in various system
200 calls. Don't test for backslash path separator since that's a Win32
201 path following Win32 rules. */
202 const char *last_comp = strchr (dir, '\0');
203
204 if (last_comp == dir)
205 return false; /* Empty string. Probably shouldn't happen here? */
206
207 /* Detect run of trailing slashes */
208 while (last_comp > dir && *--last_comp == '/')
209 continue;
210
211 /* Detect just a run of slashes or a path that does not end with a slash. */
212 if (*last_comp != '.')
213 return false;
214
215 /* We know we have a trailing dot here. Check that it really is a standalone "."
216 path component by checking that it is at the beginning of the string or is
217 preceded by a "/" */
218 if (last_comp == dir || *--last_comp == '/')
219 return true;
220
221 /* If we're not checking for '..' we're done. Ditto if we're now pointing to
222 a non-dot. */
223 if (!test_dot_dot || *last_comp != '.')
224 return false; /* either not testing for .. or this was not '..' */
225
226 /* Repeat previous test for standalone or path component. */
227 return last_comp == dir || last_comp[-1] == '/';
228 }
229
230 /* Normalize a POSIX path.
231 All duplicate /'s, except for 2 leading /'s, are deleted.
232 The result is 0 for success, or an errno error value. */
233
234 int
235 normalize_posix_path (const char *src, char *dst, char *&tail)
236 {
237 const char *in_src = src;
238 char *dst_start = dst;
239 bool check_parent = false;
240 syscall_printf ("src %s", src);
241
242 if ((isdrive (src) && isdirsep (src[2])) || *src == '\\')
243 goto win32_path;
244
245 tail = dst;
246 if (!isslash (src[0]))
247 {
248 if (!cygheap->cwd.get (dst))
249 return get_errno ();
250 tail = strchr (tail, '\0');
251 if (isslash (dst[0]) && isslash (dst[1]))
252 ++dst_start;
253 if (*src == '.')
254 {
255 if (tail == dst_start + 1 && *dst_start == '/')
256 tail--;
257 goto sawdot;
258 }
259 if (tail > dst && !isslash (tail[-1]))
260 *tail++ = '/';
261 }
262 /* Two leading /'s? If so, preserve them. */
263 else if (isslash (src[1]) && !isslash (src[2]))
264 {
265 *tail++ = *src++;
266 ++dst_start;
267 }
268
269 while (*src)
270 {
271 if (*src == '\\')
272 goto win32_path;
273 /* Strip runs of /'s. */
274 if (!isslash (*src))
275 *tail++ = *src++;
276 else
277 {
278 check_parent = true;
279 while (*++src)
280 {
281 if (isslash (*src))
282 continue;
283
284 if (*src != '.')
285 break;
286
287 sawdot:
288 if (src[1] != '.')
289 {
290 if (!src[1])
291 {
292 *tail++ = '/';
293 goto done;
294 }
295 if (!isslash (src[1]))
296 break;
297 }
298 else if (src[2] && !isslash (src[2]))
299 break;
300 else
301 {
302 /* According to POSIX semantics all elements of path must
303 exist. To follow it, we must validate our path before
304 removing the trailing component. Check_parent is needed
305 for performance optimization, in order not to verify paths
306 which are already verified. For example this prevents
307 double check in case of foo/bar/../.. */
308 if (check_parent)
309 {
310 if (tail > dst_start) /* Don't check for / or // dir. */
311 {
312 *tail = 0;
313 debug_printf ("checking %s before '..'", dst);
314 /* In conjunction with native and NFS symlinks,
315 this call can result in a recursion which eats
316 up our tmp_pathbuf buffers. This in turn results
317 in a api_fatal call. To avoid that, we're
318 checking our remaining buffers and return an
319 error code instead. Note that this only happens
320 if the path contains 15 or more relative native/NFS
321 symlinks with a ".." in the target path. */
322 tmp_pathbuf tp;
323 if (!tp.check_usage (4, 3))
324 return ELOOP;
325 path_conv head (dst, PC_SYM_FOLLOW | PC_POSIX);
326 if (!head.isdir())
327 return ENOENT;
328 /* At this point, dst is a normalized path. If the
329 normalized path created by path_conv does not
330 match the normalized path we're just testing, then
331 the path in dst contains native symlinks. If we
332 just plunge along, removing the previous path
333 component, we may end up removing a symlink from
334 the path and the resulting path will be invalid.
335 So we replace dst with what we found in head
336 instead. All the work replacing symlinks has been
337 done in that path anyway, so why repeat it? */
338 tail = stpcpy (dst, head.get_posix ());
339 }
340 check_parent = false;
341 }
342 while (tail > dst_start && !isslash (*--tail))
343 continue;
344 src++;
345 }
346 }
347
348 *tail++ = '/';
349 }
350 if ((tail - dst) >= NT_MAX_PATH)
351 {
352 debug_printf ("ENAMETOOLONG = normalize_posix_path (%s)", src);
353 return ENAMETOOLONG;
354 }
355 }
356
357 done:
358 *tail = '\0';
359
360 debug_printf ("%s = normalize_posix_path (%s)", dst, in_src);
361 return 0;
362
363 win32_path:
364 int err = normalize_win32_path (in_src, dst, tail);
365 if (!err)
366 for (char *p = dst; (p = strchr (p, '\\')); p++)
367 *p = '/';
368 return err ?: -1;
369 }
370
371 inline void
372 path_conv::add_ext_from_sym (symlink_info &sym)
373 {
374 if (sym.ext_here && *sym.ext_here)
375 {
376 suffix = path + sym.extn;
377 if (sym.ext_tacked_on)
378 strcpy ((char *) suffix, sym.ext_here);
379 }
380 }
381
382 static void __reg2 mkrelpath (char *dst, bool caseinsensitive);
383
384 static void __reg2
385 mkrelpath (char *path, bool caseinsensitive)
386 {
387 tmp_pathbuf tp;
388 char *cwd_win32 = tp.c_get ();
389 if (!cygheap->cwd.get (cwd_win32, 0))
390 return;
391
392 unsigned cwdlen = strlen (cwd_win32);
393 if (!path_prefix_p (cwd_win32, path, cwdlen, caseinsensitive))
394 return;
395
396 size_t n = strlen (path);
397 if (n < cwdlen)
398 return;
399
400 char *tail = path;
401 if (n == cwdlen)
402 tail += cwdlen;
403 else
404 tail += isdirsep (cwd_win32[cwdlen - 1]) ? cwdlen : cwdlen + 1;
405
406 memmove (path, tail, strlen (tail) + 1);
407 if (!*path)
408 strcpy (path, ".");
409 }
410
411 void
412 path_conv::set_posix (const char *path_copy)
413 {
414 if (path_copy)
415 {
416 size_t n = strlen (path_copy) + 1;
417 char *p = (char *) crealloc_abort ((void *) posix_path, n);
418 posix_path = (const char *) memcpy (p, path_copy, n);
419 }
420 }
421
422 static inline void
423 str2uni_cat (UNICODE_STRING &tgt, const char *srcstr)
424 {
425 int len = sys_mbstowcs (tgt.Buffer + tgt.Length / sizeof (WCHAR),
426 (tgt.MaximumLength - tgt.Length) / sizeof (WCHAR),
427 srcstr);
428 if (len)
429 tgt.Length += (len - 1) * sizeof (WCHAR);
430 }
431
432 PUNICODE_STRING
433 get_nt_native_path (const char *path, UNICODE_STRING& upath, bool dos)
434 {
435 upath.Length = 0;
436 if (path[0] == '/') /* special path w/o NT path representation. */
437 str2uni_cat (upath, path);
438 else if (path[0] != '\\') /* X:\... or relative path. */
439 {
440 if (path[1] == ':') /* X:\... */
441 {
442 RtlAppendUnicodeStringToString (&upath, &ro_u_natp);
443 str2uni_cat (upath, path);
444 /* The drive letter must be upper case. */
445 upath.Buffer[4] = towupper (upath.Buffer[4]);
446 transform_chars (&upath, 7);
447 }
448 else /* relative path */
449 {
450 str2uni_cat (upath, path);
451 transform_chars (&upath, 0);
452 }
453 }
454 else if (path[1] != '\\') /* \Device\... */
455 str2uni_cat (upath, path);
456 else if ((path[2] != '.' && path[2] != '?')
457 || path[3] != '\\') /* \\server\share\... */
458 {
459 RtlAppendUnicodeStringToString (&upath, &ro_u_uncp);
460 str2uni_cat (upath, path + 2);
461 transform_chars (&upath, 8);
462 }
463 else /* \\.\device or \\?\foo */
464 {
465 RtlAppendUnicodeStringToString (&upath, &ro_u_natp);
466 str2uni_cat (upath, path + 4);
467 }
468 if (dos)
469 {
470 /* Unfortunately we can't just use transform_chars with the tfx_rev_chars
471 table since only leading and trailing spaces and dots are affected.
472 So we step to every backslash and fix surrounding dots and spaces.
473 That makes these broken filesystems a bit slower, but, hey. */
474 PWCHAR cp = upath.Buffer + 7;
475 PWCHAR cend = upath.Buffer + upath.Length / sizeof (WCHAR);
476 while (++cp < cend)
477 if (*cp == L'\\')
478 {
479 PWCHAR ccp = cp - 1;
480 while (*ccp == L'.' || *ccp == L' ')
481 *ccp-- |= 0xf000;
482 while (cp[1] == L' ')
483 *++cp |= 0xf000;
484 }
485 while (*--cp == L'.' || *cp == L' ')
486 *cp |= 0xf000;
487 }
488 return &upath;
489 }
490
491 /* Handle with extrem care! Only used in a certain instance in try_to_bin.
492 Every other usage needs a careful check. */
493 void
494 path_conv::set_nt_native_path (PUNICODE_STRING new_path)
495 {
496 wide_path = (PWCHAR) crealloc_abort (wide_path, new_path->MaximumLength);
497 memcpy (wide_path, new_path->Buffer, new_path->Length);
498 uni_path.Length = new_path->Length;
499 uni_path.MaximumLength = new_path->MaximumLength;
500 uni_path.Buffer = wide_path;
501 }
502
503 PUNICODE_STRING
504 path_conv::get_nt_native_path ()
505 {
506 PUNICODE_STRING res;
507 if (wide_path)
508 res = &uni_path;
509 else if (!path)
510 res = NULL;
511 else
512 {
513 uni_path.Length = 0;
514 uni_path.MaximumLength = (strlen (path) + 10) * sizeof (WCHAR);
515 wide_path = (PWCHAR) cmalloc_abort (HEAP_STR, uni_path.MaximumLength);
516 uni_path.Buffer = wide_path;
517 ::get_nt_native_path (path, uni_path, has_dos_filenames_only ());
518 res = &uni_path;
519 }
520 return res;
521 }
522
523 PWCHAR
524 path_conv::get_wide_win32_path (PWCHAR wc)
525 {
526 get_nt_native_path ();
527 if (!wide_path)
528 return NULL;
529 wcpcpy (wc, wide_path);
530 if (wc[1] == L'?')
531 wc[1] = L'\\';
532 return wc;
533 }
534
535 static DWORD
536 getfileattr (const char *path, bool caseinsensitive) /* path has to be always absolute. */
537 {
538 tmp_pathbuf tp;
539 UNICODE_STRING upath;
540 OBJECT_ATTRIBUTES attr;
541 FILE_BASIC_INFORMATION fbi;
542 NTSTATUS status;
543 IO_STATUS_BLOCK io;
544
545 tp.u_get (&upath);
546 InitializeObjectAttributes (&attr, &upath,
547 caseinsensitive ? OBJ_CASE_INSENSITIVE : 0,
548 NULL, NULL);
549 get_nt_native_path (path, upath, false);
550
551 status = NtQueryAttributesFile (&attr, &fbi);
552 if (NT_SUCCESS (status))
553 return fbi.FileAttributes;
554
555 if (status != STATUS_OBJECT_NAME_NOT_FOUND
556 && status != STATUS_NO_SUCH_FILE) /* File not found on 9x share */
557 {
558 /* File exists but access denied. Try to get attribute through
559 directory query. */
560 UNICODE_STRING dirname, basename;
561 HANDLE dir;
562 FILE_BOTH_DIR_INFORMATION fdi;
563
564 RtlSplitUnicodePath (&upath, &dirname, &basename);
565 InitializeObjectAttributes (&attr, &dirname,
566 caseinsensitive ? OBJ_CASE_INSENSITIVE : 0,
567 NULL, NULL);
568 status = NtOpenFile (&dir, SYNCHRONIZE | FILE_LIST_DIRECTORY,
569 &attr, &io, FILE_SHARE_VALID_FLAGS,
570 FILE_SYNCHRONOUS_IO_NONALERT
571 | FILE_OPEN_FOR_BACKUP_INTENT
572 | FILE_DIRECTORY_FILE);
573 if (NT_SUCCESS (status))
574 {
575 status = NtQueryDirectoryFile (dir, NULL, NULL, 0, &io,
576 &fdi, sizeof fdi,
577 FileBothDirectoryInformation,
578 TRUE, &basename, TRUE);
579 NtClose (dir);
580 if (NT_SUCCESS (status) || status == STATUS_BUFFER_OVERFLOW)
581 return fdi.FileAttributes;
582 }
583 }
584 SetLastError (RtlNtStatusToDosError (status));
585 return INVALID_FILE_ATTRIBUTES;
586 }
587
588 /* Convert an arbitrary path SRC to a pure Win32 path, suitable for
589 passing to Win32 API routines.
590
591 If an error occurs, `error' is set to the errno value.
592 Otherwise it is set to 0.
593
594 follow_mode values:
595 SYMLINK_FOLLOW - convert to PATH symlink points to
596 SYMLINK_NOFOLLOW - convert to PATH of symlink itself
597 SYMLINK_IGNORE - do not check PATH for symlinks
598 SYMLINK_CONTENTS - just return symlink contents
599 */
600
601 /* TODO: This implementation is only preliminary. For internal
602 purposes it's necessary to have a path_conv::check function which
603 takes a UNICODE_STRING src path, otherwise we waste a lot of time
604 for converting back and forth. The below implementation does
605 realy nothing but converting to char *, until path_conv handles
606 wide-char paths directly. */
607 void
608 path_conv::check (const UNICODE_STRING *src, unsigned opt,
609 const suffix_info *suffixes)
610 {
611 tmp_pathbuf tp;
612 char *path = tp.c_get ();
613
614 user_shared->warned_msdos = true;
615 sys_wcstombs (path, NT_MAX_PATH, src->Buffer, src->Length / sizeof (WCHAR));
616 path_conv::check (path, opt, suffixes);
617 }
618
619 void
620 path_conv::check (const char *src, unsigned opt,
621 const suffix_info *suffixes)
622 {
623 /* The tmp_buf array is used when expanding symlinks. It is NT_MAX_PATH * 2
624 in length so that we can hold the expanded symlink plus a trailer. */
625 tmp_pathbuf tp;
626 char *path_copy = tp.c_get ();
627 char *pathbuf = tp.c_get ();
628 char *tmp_buf = tp.t_get ();
629 char *THIS_path = tp.c_get ();
630 symlink_info sym;
631 bool need_directory = 0;
632 bool add_ext = false;
633 bool is_relpath;
634 char *tail, *path_end;
635
636 #if 0
637 static path_conv last_path_conv;
638 static char last_src[CYG_MAX_PATH];
639
640 if (*last_src && strcmp (last_src, src) == 0)
641 {
642 *this = last_path_conv;
643 return;
644 }
645 #endif
646
647 __try
648 {
649 int loop = 0;
650 mount_flags = 0;
651 path_flags = 0;
652 suffix = NULL;
653 fileattr = INVALID_FILE_ATTRIBUTES;
654 caseinsensitive = OBJ_CASE_INSENSITIVE;
655 if (wide_path)
656 cfree (wide_path);
657 wide_path = NULL;
658 if (path)
659 {
660 cfree (modifiable_path ());
661 path = NULL;
662 }
663 close_conv_handle ();
664 fs.clear ();
665 if (posix_path)
666 {
667 cfree ((void *) posix_path);
668 posix_path = NULL;
669 }
670 int component = 0; // Number of translated components
671
672 if (!(opt & PC_NULLEMPTY))
673 error = 0;
674 else if (!*src)
675 {
676 error = ENOENT;
677 return;
678 }
679
680 bool is_msdos = false;
681 /* This loop handles symlink expansion. */
682 for (;;)
683 {
684 is_relpath = !isabspath (src);
685 error = normalize_posix_path (src, path_copy, tail);
686 if (error > 0)
687 return;
688 if (error < 0)
689 {
690 if (component == 0)
691 is_msdos = true;
692 error = 0;
693 }
694
695 /* Detect if the user was looking for a directory. We have to strip
696 the trailing slash initially while trying to add extensions but
697 take it into account during processing */
698 if (tail > path_copy + 2 && isslash (tail[-1]))
699 {
700 need_directory = 1;
701 *--tail = '\0';
702 }
703 path_end = tail;
704
705 /* Scan path_copy from right to left looking either for a symlink
706 or an actual existing file. If an existing file is found, just
707 return. If a symlink is found, exit the for loop.
708 Also: be careful to preserve the errno returned from
709 symlink.check as the caller may need it. */
710 /* FIXME: Do we have to worry about multiple \'s here? */
711 component = 0; // Number of translated components
712 sym.contents[0] = '\0';
713
714 int symlen = 0;
715
716 /* Make sure to check certain flags on last component only. */
717 for (unsigned pc_flags = opt & (PC_NO_ACCESS_CHECK | PC_KEEP_HANDLE);
718 ;
719 pc_flags = 0)
720 {
721 const suffix_info *suff;
722 char *full_path;
723
724 /* Don't allow symlink.check to set anything in the path_conv
725 class if we're working on an inner component of the path */
726 if (component)
727 {
728 suff = NULL;
729 full_path = pathbuf;
730 }
731 else
732 {
733 suff = suffixes;
734 full_path = THIS_path;
735 }
736
737 retry_fs_via_processfd:
738
739 /* Convert to native path spec sans symbolic link info. */
740 error = mount_table->conv_to_win32_path (path_copy, full_path,
741 dev, &sym.mount_flags);
742
743 if (error)
744 return;
745
746 sym.pc_flags = pc_flags;
747
748 if (!dev.exists ())
749 {
750 error = ENXIO;
751 return;
752 }
753
754 if (iscygdrive_dev (dev))
755 {
756 if (!component)
757 fileattr = FILE_ATTRIBUTE_DIRECTORY
758 | FILE_ATTRIBUTE_READONLY;
759 else
760 {
761 fileattr = getfileattr (THIS_path,
762 sym.mount_flags & MOUNT_NOPOSIX);
763 dev = FH_FS;
764 }
765 goto out;
766 }
767 else if (isdev_dev (dev))
768 {
769 /* Make sure that the path handling goes on as with FH_FS. */
770 }
771 else if (isvirtual_dev (dev))
772 {
773 /* FIXME: Calling build_fhandler here is not the right way to
774 handle this. */
775 fhandler_virtual *fh = (fhandler_virtual *)
776 build_fh_dev (dev, path_copy);
777 virtual_ftype_t file_type;
778 if (!fh)
779 file_type = virt_none;
780 else
781 {
782 file_type = fh->exists ();
783 if (file_type == virt_symlink
784 || file_type == virt_fdsymlink)
785 {
786 fh->fill_filebuf ();
787 symlen = sym.set (fh->get_filebuf ());
788 }
789 else if (file_type == virt_fsdir && dev == FH_PROCESSFD)
790 {
791 /* FIXME: This is YA bad hack to workaround that
792 we're checking for isvirtual_dev at this point.
793 This should only happen if the file is actually
794 a virtual file, and NOT already if the preceeding
795 path components constitute a virtual file.
796
797 Anyway, what we do here is this: If the descriptor
798 symlink points to a dir, and if there are trailing
799 path components, it's actually pointing somewhere
800 else. The format_process_fd function returns the
801 full path, resolved symlink plus trailing path
802 components, in its filebuf. This is a POSIX path
803 we know nothing about, so we have to convert it to
804 native again, calling conv_to_win32_path. Since
805 basically nothing happened yet, just copy it over
806 into full_path and jump back to the
807 conv_to_win32_path call. What a mess. */
808 stpcpy (path_copy, fh->get_filebuf ());
809 delete fh;
810 goto retry_fs_via_processfd;
811 }
812 delete fh;
813 }
814 switch (file_type)
815 {
816 case virt_directory:
817 case virt_rootdir:
818 if (component == 0)
819 fileattr = FILE_ATTRIBUTE_DIRECTORY;
820 break;
821 case virt_file:
822 if (component == 0)
823 fileattr = 0;
824 break;
825 case virt_fdsymlink:
826 /* Allow open/linkat to do the right thing. */
827 if (opt & PC_SYM_NOFOLLOW_PROCFD)
828 {
829 opt &= ~PC_SYM_FOLLOW;
830 sym.path_flags |= PATH_RESOLVE_PROCFD;
831 }
832 /*FALLTHRU*/
833 case virt_symlink:
834 goto is_virtual_symlink;
835 case virt_pipe:
836 if (component == 0)
837 {
838 fileattr = 0;
839 dev.parse (FH_PIPE);
840 }
841 break;
842 case virt_socket:
843 if (component == 0)
844 {
845 fileattr = 0;
846 dev.parse (FH_SOCKET);
847 }
848 break;
849 case virt_fsdir:
850 case virt_fsfile:
851 /* Access to real file or directory via block device
852 entry in /proc/sys. Convert to real file and go with
853 the flow. */
854 dev.parse (FH_FS);
855 goto is_fs_via_procsys;
856 case virt_blk:
857 /* Block special device. If the trailing slash has been
858 requested, the target is the root directory of the
859 filesystem on this block device. So we convert this
860 to a real file and attach the backslash. */
861 if (component == 0 && need_directory)
862 {
863 dev.parse (FH_FS);
864 strcat (full_path, "\\");
865 fileattr = FILE_ATTRIBUTE_DIRECTORY
866 | FILE_ATTRIBUTE_DEVICE;
867 goto out;
868 }
869 /*FALLTHRU*/
870 case virt_chr:
871 if (component == 0)
872 fileattr = FILE_ATTRIBUTE_DEVICE;
873 break;
874 default:
875 if (component == 0)
876 fileattr = INVALID_FILE_ATTRIBUTES;
877 goto virtual_component_retry;
878 }
879 if (component == 0 || dev != FH_NETDRIVE)
880 mount_flags |= MOUNT_RO;
881 goto out;
882 }
883 /* devn should not be a device. If it is, then stop parsing. */
884 else if (dev != FH_FS)
885 {
886 fileattr = 0;
887 mount_flags = sym.mount_flags;
888 path_flags = sym.path_flags;
889 if (component)
890 {
891 error = ENOTDIR;
892 return;
893 }
894 goto out; /* Found a device. Stop parsing. */
895 }
896
897 /* If path is only a drivename, Windows interprets it as the
898 current working directory on this drive instead of the root
899 dir which is what we want. So we need the trailing backslash
900 in this case. */
901 if (full_path[0] && full_path[1] == ':' && full_path[2] == '\0')
902 {
903 full_path[2] = '\\';
904 full_path[3] = '\0';
905 }
906
907 /* If the incoming path was given in DOS notation, always treat
908 it as caseinsensitive,noacl path. This must be set before
909 calling sym.check, otherwise the path is potentially treated
910 casesensitive. */
911 if (is_msdos)
912 sym.mount_flags |= MOUNT_NOPOSIX | MOUNT_NOACL;
913
914 is_fs_via_procsys:
915
916 symlen = sym.check (full_path, suff, fs, conv_handle);
917
918 is_virtual_symlink:
919
920 if (sym.isdevice)
921 {
922 if (component)
923 {
924 error = ENOTDIR;
925 return;
926 }
927 dev.parse (sym.major, sym.minor);
928 dev.setfs (1);
929 dev.mode (sym.mode);
930 fileattr = sym.fileattr;
931 goto out;
932 }
933
934 if (sym.path_flags & PATH_SOCKET)
935 {
936 if (component)
937 {
938 error = ENOTDIR;
939 return;
940 }
941 fileattr = sym.fileattr;
942 #ifdef __WITH_AF_UNIX
943 dev.parse ((sym.path_flags & PATH_REP) ? FH_UNIX : FH_LOCAL);
944 #else
945 dev.parse (FH_LOCAL);
946 #endif /* __WITH_AF_UNIX */
947 dev.setfs (1);
948 mount_flags = sym.mount_flags;
949 path_flags = sym.path_flags;
950 goto out;
951 }
952
953 if (!component)
954 {
955 /* Make sure that /dev always exists. */
956 fileattr = isdev_dev (dev) ? FILE_ATTRIBUTE_DIRECTORY
957 : sym.fileattr;
958 mount_flags = sym.mount_flags;
959 path_flags = sym.path_flags;
960 }
961 else if (isdev_dev (dev))
962 {
963 /* If we're looking for a non-existing file below /dev,
964 make sure that the device type is converted to FH_FS, so
965 that subsequent code handles the file correctly. Unless
966 /dev itself doesn't exist on disk. In that case /dev
967 is handled as virtual filesystem, and virtual filesystems
968 are read-only. The PC_KEEP_HANDLE check allows to check
969 for a call from an informational system call. In that
970 case we just stick to ENOENT, and the device type doesn't
971 matter anyway. */
972 if (sym.error == ENOENT && !(opt & PC_KEEP_HANDLE))
973 sym.error = EROFS;
974 else
975 dev = FH_FS;
976 }
977
978 /* If symlink.check found an existing non-symlink file, then
979 it sets the appropriate flag. It also sets any suffix found
980 into `ext_here'. */
981 if (!sym.issymlink && sym.fileattr != INVALID_FILE_ATTRIBUTES)
982 {
983 error = sym.error;
984 if (component == 0)
985 add_ext = true;
986 else if (!(sym.fileattr & FILE_ATTRIBUTE_DIRECTORY))
987 {
988 error = ENOTDIR;
989 goto out;
990 }
991 goto out; // file found
992 }
993 /* Found a symlink if symlen > 0. If component == 0, then the
994 src path itself was a symlink. If !follow_mode then
995 we're done. Otherwise we have to insert the path found
996 into the full path that we are building and perform all of
997 these operations again on the newly derived path. */
998 else if (symlen > 0)
999 {
1000 if (component == 0
1001 && (!(opt & PC_SYM_FOLLOW)
1002 || (is_known_reparse_point ()
1003 && (opt & PC_SYM_NOFOLLOW_REP))))
1004 {
1005 /* Usually a trailing slash requires to follow a symlink,
1006 even with PC_SYM_NOFOLLOW. The reason is that "foo/"
1007 is equivalent to "foo/." so the symlink is in fact not
1008 the last path component.
1009
1010 PC_SYM_NOFOLLOW_DIR is used to indicate that the
1011 last path component is the target symlink and the
1012 trailing slash is supposed to be ignored. */
1013 if (!need_directory || (opt & PC_SYM_NOFOLLOW_DIR))
1014 {
1015 /* last component of path is a symlink. */
1016 set_symlink (symlen);
1017 /* make sure not to set errno to ENOTDIR. */
1018 need_directory = 0;
1019 if (opt & PC_SYM_CONTENTS)
1020 {
1021 strcpy (THIS_path, sym.contents);
1022 goto out;
1023 }
1024 add_ext = true;
1025 goto out;
1026 }
1027 }
1028 /* Following a symlink we can't trust the collected
1029 filesystem information any longer. */
1030 fs.clear ();
1031 /* Close handle, if we have any. Otherwise we're collecting
1032 handles while following symlinks. */
1033 conv_handle.close ();
1034 break;
1035 }
1036 else if (sym.error && sym.error != ENOENT)
1037 {
1038 error = sym.error;
1039 goto out;
1040 }
1041 /* No existing file found. */
1042
1043 virtual_component_retry:
1044 /* Find the new "tail" of the path, e.g. in '/for/bar/baz',
1045 /baz is the tail. */
1046 if (tail != path_end)
1047 *tail = '/';
1048 while (--tail > path_copy + 1 && *tail != '/') {}
1049 /* Exit loop if there is no tail or we are at the
1050 beginning of a UNC path */
1051 if (tail <= path_copy + 1)
1052 goto out; // all done
1053
1054 /* Haven't found an existing pathname component yet.
1055 Pinch off the tail and try again. */
1056 *tail = '\0';
1057 component++;
1058 }
1059
1060 /* Arrive here if above loop detected a symlink. */
1061 if (++loop > SYMLOOP_MAX)
1062 {
1063 error = ELOOP; // Eep.
1064 return;
1065 }
1066
1067 /* Place the link content, possibly with head and/or tail,
1068 in tmp_buf */
1069
1070 char *headptr;
1071 if (isabspath (sym.contents))
1072 headptr = tmp_buf; /* absolute path */
1073 else
1074 {
1075 /* Copy the first part of the path (with ending /) and point to
1076 the end. */
1077 char *prevtail = tail;
1078 while (--prevtail > path_copy && *prevtail != '/') {}
1079 int headlen = prevtail - path_copy + 1;;
1080 memcpy (tmp_buf, path_copy, headlen);
1081 headptr = &tmp_buf[headlen];
1082 }
1083
1084 /* Make sure there is enough space */
1085 if (headptr + symlen >= tmp_buf + (2 * NT_MAX_PATH))
1086 {
1087 too_long:
1088 error = ENAMETOOLONG;
1089 set_path ("::ENAMETOOLONG::");
1090 return;
1091 }
1092
1093 /* Copy the symlink contents to the end of tmp_buf.
1094 Convert slashes. */
1095 for (char *p = sym.contents; *p; p++)
1096 *headptr++ = *p == '\\' ? '/' : *p;
1097 *headptr = '\0';
1098
1099 /* Copy any tail component (with the 0) */
1100 if (tail++ < path_end)
1101 {
1102 /* Add a slash if needed. There is space. */
1103 if (*(headptr - 1) != '/')
1104 *headptr++ = '/';
1105 int taillen = path_end - tail + 1;
1106 if (headptr + taillen > tmp_buf + (2 * NT_MAX_PATH))
1107 goto too_long;
1108 memcpy (headptr, tail, taillen);
1109 }
1110
1111 /* Evaluate everything all over again. */
1112 src = tmp_buf;
1113 }
1114
1115 if (!(opt & PC_SYM_CONTENTS))
1116 add_ext = true;
1117
1118 out:
1119 set_path (THIS_path);
1120 if (add_ext)
1121 add_ext_from_sym (sym);
1122 if (dev == FH_NETDRIVE && component)
1123 {
1124 /* This case indicates a non-existant resp. a non-retrievable
1125 share. This happens for instance if the share is a printer.
1126 In this case the path must not be treated like a FH_NETDRIVE,
1127 but like a FH_FS instead, so the usual open call for files
1128 is used on it. */
1129 dev.parse (FH_FS);
1130 }
1131 else if (isproc_dev (dev) && fileattr == INVALID_FILE_ATTRIBUTES)
1132 {
1133 /* FIXME: Usually we don't set error to ENOENT if a file doesn't
1134 exist. This is typically indicated by the fileattr content.
1135 So, why here? The downside is that cygwin_conv_path just gets
1136 an error for these paths so it reports the error back to the
1137 application. Unlike in all other cases of non-existant files,
1138 for which check doesn't set error, so cygwin_conv_path just
1139 returns the path, as intended. */
1140 error = ENOENT;
1141 return;
1142 }
1143 else if (!need_directory || error)
1144 /* nothing to do */;
1145 else if (fileattr == INVALID_FILE_ATTRIBUTES)
1146 /* Reattach trailing dirsep in native path. */
1147 strcat (modifiable_path (), "\\");
1148 else if (fileattr & FILE_ATTRIBUTE_DIRECTORY)
1149 path_flags &= ~PATH_SYMLINK;
1150 else
1151 {
1152 debug_printf ("%s is a non-directory", path);
1153 error = ENOTDIR;
1154 return;
1155 }
1156
1157 if (dev.isfs ())
1158 {
1159 /* If FS hasn't been checked already in symlink_info::check,
1160 do so now. */
1161 if (fs.inited ()|| fs.update (get_nt_native_path (), NULL))
1162 {
1163 /* Incoming DOS paths are treated like DOS paths in native
1164 Windows applications. No ACLs, just default settings. */
1165 if (is_msdos)
1166 fs.has_acls (false);
1167 debug_printf ("this->path(%s), has_acls(%d)",
1168 path, fs.has_acls ());
1169 /* CV: We could use this->has_acls() but I want to make sure that
1170 we don't forget that the MOUNT_NOACL flag must be taken into
1171 account here. */
1172 if (!(mount_flags & MOUNT_NOACL) && fs.has_acls ())
1173 set_exec (0); /* We really don't know if this is executable or
1174 not here but set it to not executable since
1175 it will be figured out later by anything
1176 which cares about this. */
1177 }
1178 /* If the FS has been found to have unreliable inodes, note
1179 that in mount_flags. */
1180 if (!fs.hasgood_inode ())
1181 mount_flags |= MOUNT_IHASH;
1182 /* If the OS is caseinsensitive or the FS is caseinsensitive,
1183 don't handle path casesensitive. */
1184 if (cygwin_shared->obcaseinsensitive || fs.caseinsensitive ())
1185 mount_flags |= MOUNT_NOPOSIX;
1186 caseinsensitive = (mount_flags & MOUNT_NOPOSIX)
1187 ? OBJ_CASE_INSENSITIVE : 0;
1188 if (exec_state () != dont_know_if_executable)
1189 /* ok */;
1190 else if (isdir ())
1191 set_exec (1);
1192 else if (issymlink () || issocket ())
1193 set_exec (0);
1194 }
1195
1196 if (opt & PC_NOFULL)
1197 {
1198 if (is_relpath)
1199 {
1200 mkrelpath (this->modifiable_path (), !!caseinsensitive);
1201 /* Invalidate wide_path so that wide relpath can be created
1202 in later calls to get_nt_native_path or get_wide_win32_path. */
1203 if (wide_path)
1204 cfree (wide_path);
1205 wide_path = NULL;
1206 }
1207 if (need_directory)
1208 {
1209 size_t n = strlen (this->path);
1210 /* Do not add trailing \ to UNC device names like \\.\a: */
1211 if (this->path[n - 1] != '\\' &&
1212 (strncmp (this->path, "\\\\.\\", 4) != 0))
1213 {
1214 this->modifiable_path ()[n] = '\\';
1215 this->modifiable_path ()[n + 1] = '\0';
1216 }
1217 }
1218 }
1219
1220 if (opt & PC_OPEN)
1221 path_flags |= PATH_OPEN;
1222
1223 if (opt & PC_CTTY)
1224 path_flags |= PATH_CTTY;
1225
1226 if (opt & PC_POSIX)
1227 {
1228 if (tail < path_end && tail > path_copy + 1)
1229 *tail = '/';
1230 set_posix (path_copy);
1231 }
1232
1233 #if 0
1234 if (!error)
1235 {
1236 last_path_conv = *this;
1237 strcpy (last_src, src);
1238 }
1239 #endif
1240 }
1241 __except (NO_ERROR)
1242 {
1243 error = EFAULT;
1244 }
1245 __endtry
1246 }
1247
1248 struct pc_flat
1249 {
1250 path_conv pc;
1251 HANDLE hdl;
1252 size_t name_len;
1253 size_t posix_len;
1254 char data[0];
1255 };
1256
1257 void *
1258 path_conv::serialize (HANDLE h, unsigned int &n) const
1259 {
1260 pc_flat *pcf;
1261 size_t nlen = 0, plen = 0;
1262 char *p;
1263
1264 if (path)
1265 nlen = strlen (path) + 1;
1266 if (posix_path)
1267 plen = strlen (posix_path) + 1;
1268 n = sizeof (pc_flat) + nlen + plen;
1269 pcf = (pc_flat *) cmalloc (HEAP_COMMUNE, n);
1270 if (!pcf)
1271 {
1272 n = 0;
1273 return NULL;
1274 }
1275 memcpy ((void *) &pcf->pc, this, sizeof *this);
1276 pcf->hdl = h;
1277 pcf->name_len = nlen;
1278 pcf->posix_len = plen;
1279 p = pcf->data;
1280 if (nlen)
1281 p = stpcpy (p, path) + 1;
1282 if (plen)
1283 stpcpy (p, posix_path);
1284 return pcf;
1285 }
1286
1287 HANDLE
1288 path_conv::deserialize (void *bufp)
1289 {
1290 pc_flat *pcf = (pc_flat *) bufp;
1291 char *p;
1292 HANDLE ret;
1293
1294 memcpy ((void *) this, &pcf->pc, sizeof *this);
1295 wide_path = uni_path.Buffer = NULL;
1296 uni_path.MaximumLength = uni_path.Length = 0;
1297 path = posix_path = NULL;
1298 p = pcf->data;
1299 if (pcf->name_len)
1300 {
1301 set_path (p);
1302 p += pcf->name_len;
1303 }
1304 if (pcf->posix_len)
1305 set_posix (p);
1306 dev.parse (pcf->pc.dev);
1307 ret = pcf->hdl;
1308 cfree (bufp);
1309 return ret;
1310 }
1311
1312 path_conv::~path_conv ()
1313 {
1314 if (posix_path)
1315 {
1316 cfree ((void *) posix_path);
1317 posix_path = NULL;
1318 }
1319 if (path)
1320 {
1321 cfree (modifiable_path ());
1322 path = NULL;
1323 }
1324 if (wide_path)
1325 {
1326 cfree (wide_path);
1327 wide_path = NULL;
1328 }
1329 close_conv_handle ();
1330 }
1331
1332 bool
1333 path_conv::is_binary ()
1334 {
1335 tmp_pathbuf tp;
1336 PWCHAR bintest = tp.w_get ();
1337 DWORD bin;
1338
1339 return GetBinaryTypeW (get_wide_win32_path (bintest), &bin)
1340 && (bin == SCS_32BIT_BINARY || bin == SCS_64BIT_BINARY);
1341 }
1342
1343 /* Helper function to fill the fai datastructure for a file. */
1344 NTSTATUS
1345 file_get_fai (HANDLE h, PFILE_ALL_INFORMATION pfai)
1346 {
1347 NTSTATUS status;
1348 IO_STATUS_BLOCK io;
1349
1350 /* Some FSes (Netapps) don't implement FileNetworkOpenInformation. */
1351 status = NtQueryInformationFile (h, &io, pfai, sizeof *pfai,
1352 FileAllInformation);
1353 if (likely (status == STATUS_BUFFER_OVERFLOW))
1354 status = STATUS_SUCCESS;
1355 /* Filesystems with broken FileAllInformation exist, too. See the thread
1356 starting with https://cygwin.com/ml/cygwin/2016-07/msg00350.html. */
1357 else if (!NT_SUCCESS (status) && status != STATUS_ACCESS_DENIED)
1358 {
1359 memset (pfai, 0, sizeof *pfai);
1360 status = NtQueryInformationFile (h, &io, &pfai->BasicInformation,
1361 sizeof pfai->BasicInformation,
1362 FileBasicInformation);
1363 if (NT_SUCCESS (status))
1364 {
1365 /* The return value of FileInternalInformation is largely ignored.
1366 We only make absolutely sure the inode number is set to 0 in
1367 case it fails. */
1368 status = NtQueryInformationFile (h, &io, &pfai->InternalInformation,
1369 sizeof pfai->InternalInformation,
1370 FileInternalInformation);
1371 if (!NT_SUCCESS (status))
1372 pfai->InternalInformation.IndexNumber.QuadPart = 0LL;
1373 status = NtQueryInformationFile (h, &io, &pfai->StandardInformation,
1374 sizeof pfai->StandardInformation,
1375 FileStandardInformation);
1376 }
1377 }
1378 return status;
1379 }
1380
1381 /* Normalize a Win32 path.
1382 /'s are converted to \'s in the process.
1383 All duplicate \'s, except for 2 leading \'s, are deleted.
1384
1385 The result is 0 for success, or an errno error value.
1386 FIXME: A lot of this should be mergeable with the POSIX critter. */
1387 int
1388 normalize_win32_path (const char *src, char *dst, char *&tail)
1389 {
1390 const char *src_start = src;
1391 const char *dst_start = dst;
1392 bool beg_src_slash = isdirsep (src[0]);
1393
1394 tail = dst;
1395 /* Skip Win32 long path name prefix and NT object directory prefix. */
1396 if (beg_src_slash && (src[1] == '?' || isdirsep (src[1]))
1397 && src[2] == '?' && isdirsep (src[3]))
1398 {
1399 src += 4;
1400 if (isdrive (src) && (isdirsep (src[2]) || !src[2]))
1401 beg_src_slash = false;
1402 else if (!strncmp (src, "UNC", 3) && isdirsep (src[3]))
1403 /* native UNC path */
1404 src += 2; /* Fortunately the first char is not copied... */
1405 else
1406 return EINVAL;
1407 }
1408 if (beg_src_slash && isdirsep (src[1]))
1409 {
1410 if (isdirsep (src[2]))
1411 {
1412 /* More than two slashes are just folded into one. */
1413 src += 2;
1414 while (isdirsep (src[1]))
1415 ++src;
1416 }
1417 else
1418 {
1419 /* Two slashes start a network or device path. */
1420 *tail++ = '\\';
1421 src++;
1422 if (src[1] == '.' && isdirsep (src[2]))
1423 {
1424 *tail++ = '\\';
1425 *tail++ = '.';
1426 src += 2;
1427 }
1428 }
1429 dst = tail;
1430 /* If backslash is missing in src, add one. */
1431 if (!isdirsep (src[0]))
1432 *tail++ = '\\';
1433 }
1434 if (tail == dst_start)
1435 {
1436 if (isdrive (src))
1437 {
1438 /* Always convert drive letter to uppercase for case sensitivity. */
1439 *tail++ = cyg_toupper (*src++);
1440 *tail++ = *src++;
1441 dst = tail;
1442 /* If backslash is missing in src, add one. */
1443 if (!isdirsep (src[0]))
1444 *tail++ = '\\';
1445 }
1446 else if (*src != '/')
1447 {
1448 /* Make sure dst points to the rightmost backslash which must not
1449 be backtracked over during ".." evaluation. This is either
1450 the backslash after the network path prefix (i.e. "\\") or
1451 the backslash after a drive letter (i.e. C:\"). */
1452 if (beg_src_slash)
1453 {
1454 tail += cygheap->cwd.get_drive (dst);
1455 /* network path, drive == '\\\\'? Decrement tail to avoid
1456 triple backslash in output. */
1457 if (dst[0] == '\\')
1458 --tail;
1459 dst = tail;
1460 }
1461 else if (cygheap->cwd.get (dst, 0))
1462 {
1463 tail = strchr (tail, '\0');
1464 if (tail[-1] != '\\')
1465 *tail++ = '\\';
1466 ++dst;
1467 if (dst[1] == '\\')
1468 ++dst;
1469 }
1470 else
1471 return get_errno ();
1472 }
1473 }
1474
1475 /* At this point dst points to the first backslash, even if it only gets
1476 written in the first iteration of the following loop. Backing up to
1477 handle ".." components can not underrun that border (thus avoiding
1478 subsequent buffer underruns with fatal results). */
1479 while (*src)
1480 {
1481 /* Strip duplicate /'s. */
1482 if (isdirsep (src[0]) && isdirsep (src[1]))
1483 src++;
1484 /* Ignore "./". */
1485 else if (src[0] == '.' && isdirsep (src[1])
1486 && (src == src_start || isdirsep (src[-1])))
1487 {
1488 src += 2;
1489 /* Skip /'s to the next path component. */
1490 while (isdirsep (*src))
1491 src++;
1492 }
1493
1494 /* Backup if "..". */
1495 else if (src[0] == '.' && src[1] == '.' && tail[-1] == '\\')
1496 {
1497 if (!isdirsep (src[2]) && src[2] != '\0')
1498 *tail++ = *src++;
1499 else
1500 {
1501 /* Back up over /, but not if it's the first one. */
1502 if (tail > dst + 1)
1503 tail--;
1504 /* Now back up to the next /. */
1505 while (tail > dst + 1 && tail[-1] != '\\' && tail[-2] != ':')
1506 tail--;
1507 src += 2;
1508 /* Skip /'s to the next path component. */
1509 while (isdirsep (*src))
1510 src++;
1511 }
1512 }
1513 /* Otherwise, add char to result. */
1514 else
1515 {
1516 if (*src == '/')
1517 *tail++ = '\\';
1518 else
1519 *tail++ = *src;
1520 src++;
1521 }
1522 if ((tail - dst) >= NT_MAX_PATH)
1523 return ENAMETOOLONG;
1524 }
1525 if (tail > dst + 1 && tail[-1] == '.' && tail[-2] == '\\')
1526 tail--;
1527 *tail = '\0';
1528 debug_printf ("%s = normalize_win32_path (%s)", dst_start, src_start);
1529 return 0;
1530 }
1531
1532 /* Various utilities. */
1533
1534 /* nofinalslash: Remove trailing / and \ from SRC (except for the
1535 first one). It is ok for src == dst. */
1536
1537 void __reg2
1538 nofinalslash (const char *src, char *dst)
1539 {
1540 int len = strlen (src);
1541 if (src != dst)
1542 memcpy (dst, src, len + 1);
1543 while (len > 1 && isdirsep (dst[--len]))
1544 dst[len] = '\0';
1545 }
1546
1547 /* conv_path_list: Convert a list of path names to/from Win32/POSIX. */
1548
1549 static int
1550 conv_path_list (const char *src, char *dst, size_t size,
1551 cygwin_conv_path_t what)
1552 {
1553 tmp_pathbuf tp;
1554 char src_delim, dst_delim;
1555 size_t len;
1556 bool env_cvt = false;
1557
1558 if (what == (cygwin_conv_path_t) ENV_CVT)
1559 {
1560 what = CCP_WIN_A_TO_POSIX | CCP_RELATIVE;
1561 env_cvt = true;
1562 }
1563 if ((what & CCP_CONVTYPE_MASK) == CCP_WIN_A_TO_POSIX)
1564 {
1565 src_delim = ';';
1566 dst_delim = ':';
1567 }
1568 else
1569 {
1570 src_delim = ':';
1571 dst_delim = ';';
1572 }
1573
1574 char *srcbuf;
1575 len = strlen (src) + 1;
1576 if (len <= NT_MAX_PATH * sizeof (WCHAR))
1577 srcbuf = (char *) tp.w_get ();
1578 else
1579 srcbuf = (char *) alloca (len);
1580
1581 int err = 0;
1582 char *d = dst - 1;
1583 bool saw_empty = false;
1584 do
1585 {
1586 char *srcpath = srcbuf;
1587 char *s = strccpy (srcpath, &src, src_delim);
1588 size_t len = s - srcpath;
1589 if (len >= NT_MAX_PATH)
1590 {
1591 err = ENAMETOOLONG;
1592 break;
1593 }
1594 /* Paths in Win32 path lists in the environment (%Path%), are often
1595 enclosed in quotes (usually paths with spaces). Trailing backslashes
1596 are common, too. Remove them. */
1597 if (env_cvt && len)
1598 {
1599 if (*srcpath == '"')
1600 {
1601 ++srcpath;
1602 *--s = '\0';
1603 len -= 2;
1604 }
1605 while (len && s[-1] == '\\')
1606 {
1607 *--s = '\0';
1608 --len;
1609 }
1610 }
1611 if (len)
1612 {
1613 ++d;
1614 err = cygwin_conv_path (what, srcpath, d, size - (d - dst));
1615 }
1616 else if ((what & CCP_CONVTYPE_MASK) == CCP_POSIX_TO_WIN_A)
1617 {
1618 ++d;
1619 err = cygwin_conv_path (what, ".", d, size - (d - dst));
1620 }
1621 else
1622 {
1623 if (env_cvt)
1624 saw_empty = true;
1625 continue;
1626 }
1627 if (err)
1628 break;
1629 d = strchr (d, '\0');
1630 *d = dst_delim;
1631 }
1632 while (*src++);
1633
1634 if (saw_empty)
1635 err = EIDRM;
1636
1637 if (d < dst)
1638 d++;
1639 *d = '\0';
1640 return err;
1641 }
1642
1643 /********************** Symbolic Link Support **************************/
1644
1645 /* Create a symlink from FROMPATH to TOPATH. */
1646
1647 extern "C" int
1648 symlink (const char *oldpath, const char *newpath)
1649 {
1650 path_conv win32_newpath;
1651
1652 __try
1653 {
1654 if (!*oldpath || !*newpath)
1655 {
1656 set_errno (ENOENT);
1657 __leave;
1658 }
1659
1660 /* Trailing dirsep is a no-no, only errno differs. */
1661 bool has_trailing_dirsep = isdirsep (newpath[strlen (newpath) - 1]);
1662 win32_newpath.check (newpath,
1663 PC_SYM_NOFOLLOW | PC_SYM_NOFOLLOW_DIR | PC_POSIX,
1664 stat_suffixes);
1665
1666 if (win32_newpath.error || has_trailing_dirsep)
1667 {
1668 set_errno (win32_newpath.error ?:
1669 win32_newpath.exists () ? EEXIST : ENOENT);
1670 __leave;
1671 }
1672
1673 return symlink_worker (oldpath, win32_newpath, false);
1674 }
1675 __except (EFAULT) {}
1676 __endtry
1677 return -1;
1678 }
1679
1680 static int
1681 symlink_nfs (const char *oldpath, path_conv &win32_newpath)
1682 {
1683 /* On NFS, create symlinks by calling NtCreateFile with an EA of type
1684 NfsSymlinkTargetName containing ... the symlink target name. */
1685 tmp_pathbuf tp;
1686 PFILE_FULL_EA_INFORMATION pffei;
1687 NTSTATUS status;
1688 HANDLE fh;
1689 OBJECT_ATTRIBUTES attr;
1690 IO_STATUS_BLOCK io;
1691
1692 pffei = (PFILE_FULL_EA_INFORMATION) tp.w_get ();
1693 pffei->NextEntryOffset = 0;
1694 pffei->Flags = 0;
1695 pffei->EaNameLength = sizeof (NFS_SYML_TARGET) - 1;
1696 char *EaValue = stpcpy (pffei->EaName, NFS_SYML_TARGET) + 1;
1697 pffei->EaValueLength = sizeof (WCHAR) *
1698 (sys_mbstowcs ((PWCHAR) EaValue, NT_MAX_PATH, oldpath) - 1);
1699 status = NtCreateFile (&fh, FILE_WRITE_DATA | FILE_WRITE_EA | SYNCHRONIZE,
1700 win32_newpath.get_object_attr (attr, sec_none_nih),
1701 &io, NULL, FILE_ATTRIBUTE_SYSTEM,
1702 FILE_SHARE_VALID_FLAGS, FILE_CREATE,
1703 FILE_SYNCHRONOUS_IO_NONALERT
1704 | FILE_OPEN_FOR_BACKUP_INTENT,
1705 pffei, NT_MAX_PATH * sizeof (WCHAR));
1706 if (!NT_SUCCESS (status))
1707 {
1708 __seterrno_from_nt_status (status);
1709 return -1;
1710 }
1711 NtClose (fh);
1712 return 0;
1713 }
1714
1715 /* Count backslashes between s and e. */
1716 static inline int
1717 cnt_bs (PWCHAR s, PWCHAR e)
1718 {
1719 int num = 0;
1720
1721 while (s < e)
1722 if (*s++ == L'\\')
1723 ++num;
1724 return num;
1725 }
1726
1727 #ifndef SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
1728 #define SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE 2
1729 #endif
1730
1731 static int
1732 symlink_native (const char *oldpath, path_conv &win32_newpath)
1733 {
1734 tmp_pathbuf tp;
1735 path_conv win32_oldpath;
1736 PUNICODE_STRING final_oldpath, final_newpath;
1737 UNICODE_STRING final_oldpath_buf;
1738 DWORD flags;
1739
1740 if (isabspath (oldpath))
1741 {
1742 win32_oldpath.check (oldpath, PC_SYM_NOFOLLOW, stat_suffixes);
1743 final_oldpath = win32_oldpath.get_nt_native_path ();
1744 }
1745 else
1746 {
1747 /* The symlink target is relative to the directory in which
1748 the symlink gets created, not relative to the cwd. Therefore
1749 we have to mangle the path quite a bit before calling path_conv. */
1750 ssize_t len = strrchr (win32_newpath.get_posix (), '/')
1751 - win32_newpath.get_posix () + 1;
1752 char *absoldpath = tp.t_get ();
1753 stpcpy (stpncpy (absoldpath, win32_newpath.get_posix (), len),
1754 oldpath);
1755 win32_oldpath.check (absoldpath, PC_SYM_NOFOLLOW, stat_suffixes);
1756
1757 /* Try hard to keep Windows symlink path relative. */
1758
1759 /* 1. Find common path prefix. Skip leading \\?\, but take pre-increment
1760 of the following loop into account. */
1761 PWCHAR c_old = win32_oldpath.get_nt_native_path ()->Buffer + 3;
1762 PWCHAR c_new = win32_newpath.get_nt_native_path ()->Buffer + 3;
1763 /* Windows compatible == always check case insensitive. */
1764 while (towupper (*++c_old) == towupper (*++c_new))
1765 ;
1766 /* The last component could share a common prefix, so make sure we end
1767 up on the first char after the last common backslash. */
1768 while (c_old[-1] != L'\\')
1769 --c_old, --c_new;
1770
1771 /* 2. Check if prefix is long enough. The prefix must at least points to
1772 a complete device: \\?\X:\ or \\?\UNC\server\share\ are the minimum
1773 prefix strings. We start counting behind the \\?\ for speed. */
1774 int num = cnt_bs (win32_oldpath.get_nt_native_path ()->Buffer + 4, c_old);
1775 if (num < 1 /* locale drive. */
1776 || (win32_oldpath.get_nt_native_path ()->Buffer[5] != L':'
1777 && num < 3)) /* UNC path. */
1778 {
1779 /* 3a. No valid common path prefix: Create absolute symlink. */
1780 final_oldpath = win32_oldpath.get_nt_native_path ();
1781 }
1782 else
1783 {
1784 /* 3b. Common path prefx. Count number of additional directories
1785 in symlink's path, and prepend as much ".." path components
1786 to the target path. */
1787 PWCHAR e_new = win32_newpath.get_nt_native_path ()->Buffer
1788 + win32_newpath.get_nt_native_path ()->Length
1789 / sizeof (WCHAR);
1790 num = cnt_bs (c_new, e_new);
1791 final_oldpath = &final_oldpath_buf;
1792 final_oldpath->Buffer = tp.w_get ();
1793 PWCHAR e_old = final_oldpath->Buffer;
1794 while (num-- > 0)
1795 e_old = wcpcpy (e_old, L"..\\");
1796 wcpcpy (e_old, c_old);
1797 }
1798 }
1799 /* If the symlink target doesn't exist, don't create native symlink.
1800 Otherwise the directory flag in the symlink is potentially wrong
1801 when the target comes into existence, and native tools will fail.
1802 This is so screwball. This is no problem on AFS, fortunately. */
1803 if (!win32_oldpath.exists () && !win32_oldpath.fs_is_afs ())
1804 {
1805 SetLastError (ERROR_FILE_NOT_FOUND);
1806 return -1;
1807 }
1808 /* Don't allow native symlinks to Cygwin special files. However, the
1809 caller shoud know because this case shouldn't be covered by the
1810 default "nativestrict" behaviour, so we use a special return code. */
1811 if (win32_oldpath.isspecial ())
1812 return -2;
1813 /* Convert native paths to Win32 UNC paths. */
1814 final_newpath = win32_newpath.get_nt_native_path ();
1815 final_newpath->Buffer[1] = L'\\';
1816 /* oldpath may be relative. Make sure to convert only absolute paths
1817 to Win32 paths. */
1818 if (final_oldpath->Buffer[0] == L'\\')
1819 {
1820 /* Starting with Windows 8.1, the ShellExecuteW function does not
1821 handle the long path prefix correctly for symlink targets. Thus,
1822 we create simple short paths < MAX_PATH without long path prefix. */
1823 if (RtlEqualUnicodePathPrefix (final_oldpath, &ro_u_uncp, TRUE)
1824 && final_oldpath->Length < (MAX_PATH + 6) * sizeof (WCHAR))
1825 {
1826 final_oldpath->Buffer += 6;
1827 final_oldpath->Buffer[0] = L'\\';
1828 }
1829 else if (final_oldpath->Length < (MAX_PATH + 4) * sizeof (WCHAR))
1830 final_oldpath->Buffer += 4;
1831 else /* Stick to long path, fix native prefix for Win32 API calls. */
1832 final_oldpath->Buffer[1] = L'\\';
1833 }
1834 /* Try to create native symlink. */
1835 flags = win32_oldpath.isdir () ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0;
1836 if (wincap.has_unprivileged_createsymlink ())
1837 flags |= SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
1838 if (!CreateSymbolicLinkW (final_newpath->Buffer, final_oldpath->Buffer,
1839 flags))
1840 {
1841 /* Repair native newpath, we still need it. */
1842 final_newpath->Buffer[1] = L'?';
1843 return -1;
1844 }
1845 return 0;
1846 }
1847
1848 #ifndef IO_REPARSE_TAG_LX_SYMLINK
1849 #define IO_REPARSE_TAG_LX_SYMLINK (0xa000001d)
1850 #endif
1851
1852 typedef struct _REPARSE_LX_SYMLINK_BUFFER
1853 {
1854 DWORD ReparseTag;
1855 WORD ReparseDataLength;
1856 WORD Reserved;
1857 struct {
1858 DWORD FileType; /* Take member name with a grain of salt. Value is
1859 apparently always 2 for symlinks. */
1860 char PathBuffer[1];/* POSIX path as given to symlink(2).
1861 Path is not \0 terminated.
1862 Length is ReparseDataLength - sizeof (FileType).
1863 Always UTF-8.
1864 Chars given in incompatible codesets, e. g. umlauts
1865 in ISO-8859-x, are converted to the Unicode
1866 REPLACEMENT CHARACTER 0xfffd == \xef\xbf\bd */
1867 } LxSymlinkReparseBuffer;
1868 } REPARSE_LX_SYMLINK_BUFFER,*PREPARSE_LX_SYMLINK_BUFFER;
1869
1870 static int
1871 symlink_wsl (const char *oldpath, path_conv &win32_newpath)
1872 {
1873 tmp_pathbuf tp;
1874 PREPARSE_LX_SYMLINK_BUFFER rpl = (PREPARSE_LX_SYMLINK_BUFFER) tp.c_get ();
1875 char *path_buf = rpl->LxSymlinkReparseBuffer.PathBuffer;
1876 const int max_pathlen = MAXIMUM_REPARSE_DATA_BUFFER_SIZE
1877 - offsetof (REPARSE_LX_SYMLINK_BUFFER,
1878 LxSymlinkReparseBuffer.PathBuffer);
1879 PWCHAR utf16 = tp.w_get ();
1880 NTSTATUS status;
1881 IO_STATUS_BLOCK io;
1882 OBJECT_ATTRIBUTES attr;
1883 HANDLE fh;
1884 int len;
1885
1886 rpl->ReparseTag = IO_REPARSE_TAG_LX_SYMLINK;
1887 rpl->Reserved = 0;
1888 rpl->LxSymlinkReparseBuffer.FileType = 2;
1889 /* Convert cygdrive prefix to "/mnt" for WSL compatibility, but only if
1890 cygdrive prefix is not "/", otherwise suffer random "/mnt" symlinks... */
1891 if (mount_table->cygdrive_len > 1
1892 && path_prefix_p (mount_table->cygdrive, oldpath,
1893 mount_table->cygdrive_len, false))
1894 stpcpy (stpcpy (path_buf, "/mnt"),
1895 oldpath + mount_table->cygdrive_len - 1);
1896 else
1897 *stpncpy (path_buf, oldpath, max_pathlen) = '\0';
1898 /* Convert target path to UTF-16 and then back to UTF-8 to make sure the
1899 WSL symlink is in UTF-8, independent of the current Cygwin codeset. */
1900 sys_mbstowcs (utf16, NT_MAX_PATH, path_buf);
1901 len = WideCharToMultiByte (CP_UTF8, 0, utf16, -1, path_buf, max_pathlen,
1902 NULL, NULL);
1903 /* Length is omitting trailing \0. */
1904 rpl->ReparseDataLength = sizeof (DWORD) + len - 1;
1905 /* Create reparse point. */
1906 status = NtCreateFile (&fh, DELETE | FILE_GENERIC_WRITE
1907 | READ_CONTROL | WRITE_DAC,
1908 win32_newpath.get_object_attr (attr, sec_none_nih),
1909 &io, NULL, FILE_ATTRIBUTE_NORMAL,
1910 FILE_SHARE_VALID_FLAGS, FILE_CREATE,
1911 FILE_SYNCHRONOUS_IO_NONALERT
1912 | FILE_NON_DIRECTORY_FILE
1913 | FILE_OPEN_FOR_BACKUP_INTENT
1914 | FILE_OPEN_REPARSE_POINT,
1915 NULL, 0);
1916 if (!NT_SUCCESS (status))
1917 {
1918 SetLastError (RtlNtStatusToDosError (status));
1919 return -1;
1920 }
1921 set_created_file_access (fh, win32_newpath, S_IFLNK | STD_RBITS | STD_WBITS);
1922 status = NtFsControlFile (fh, NULL, NULL, NULL, &io, FSCTL_SET_REPARSE_POINT,
1923 (LPVOID) rpl, REPARSE_DATA_BUFFER_HEADER_SIZE
1924 + rpl->ReparseDataLength,
1925 NULL, 0);
1926 if (!NT_SUCCESS (status))
1927 {
1928 SetLastError (RtlNtStatusToDosError (status));
1929 FILE_DISPOSITION_INFORMATION fdi = { TRUE };
1930 status = NtSetInformationFile (fh, &io, &fdi, sizeof fdi,
1931 FileDispositionInformation);
1932 NtClose (fh);
1933 if (!NT_SUCCESS (status))
1934 debug_printf ("Setting delete dispostion failed, status = %y", status);
1935 return -1;
1936 }
1937 NtClose (fh);
1938 return 0;
1939 }
1940
1941 int
1942 symlink_worker (const char *oldpath, path_conv &win32_newpath, bool isdevice)
1943 {
1944 int res = -1;
1945 size_t len;
1946 char *buf, *cp;
1947 tmp_pathbuf tp;
1948 winsym_t wsym_type;
1949
1950 /* POSIX says that empty 'newpath' is invalid input while empty
1951 'oldpath' is valid -- it's symlink resolver job to verify if
1952 symlink contents point to existing filesystem object */
1953 __try
1954 {
1955 if (strlen (oldpath) > SYMLINK_MAX)
1956 {
1957 set_errno (ENAMETOOLONG);
1958 __leave;
1959 }
1960
1961 /* Default symlink type is determined by global allow_winsymlinks
1962 variable. Device files are always shortcuts. */
1963 wsym_type = isdevice ? WSYM_lnk : allow_winsymlinks;
1964 /* NFS has its own, dedicated way to create symlinks. */
1965 if (win32_newpath.fs_is_nfs ())
1966 wsym_type = WSYM_nfs;
1967 /* MVFS doesn't handle the SYSTEM DOS attribute, but it handles the R/O
1968 attribute. Therefore we create symlinks on MVFS always as shortcuts. */
1969 else if (win32_newpath.fs_is_mvfs ())
1970 wsym_type = WSYM_lnk;
1971 /* AFS only supports native symlinks. */
1972 else if (win32_newpath.fs_is_afs ())
1973 wsym_type = WSYM_nativestrict;
1974 /* Don't try native symlinks on FSes not supporting reparse points. */
1975 else if ((wsym_type == WSYM_native || wsym_type == WSYM_nativestrict)
1976 && !(win32_newpath.fs_flags () & FILE_SUPPORTS_REPARSE_POINTS))
1977 wsym_type = WSYM_sysfile;
1978
1979 /* Attach .lnk suffix when shortcut is requested. */
1980 if (wsym_type == WSYM_lnk && !win32_newpath.exists ()
1981 && (isdevice || !win32_newpath.fs_is_nfs ()))
1982 {
1983 char *newplnk = tp.c_get ();
1984 stpcpy (stpcpy (newplnk, win32_newpath.get_posix ()), ".lnk");
1985 win32_newpath.check (newplnk, PC_SYM_NOFOLLOW | PC_POSIX);
1986 }
1987
1988 if (win32_newpath.error)
1989 {
1990 set_errno (win32_newpath.error);
1991 __leave;
1992 }
1993
1994 syscall_printf ("symlink (%s, %S) wsym_type %d", oldpath,
1995 win32_newpath.get_nt_native_path (), wsym_type);
1996
1997 if ((!isdevice && win32_newpath.exists ())
1998 || (win32_newpath.isdevice () && !win32_newpath.is_fs_special ()))
1999 {
2000 set_errno (EEXIST);
2001 __leave;
2002 }
2003
2004 /* Handle NFS, native symlinks and WSL symlinks in their own functions. */
2005 switch (wsym_type)
2006 {
2007 case WSYM_nfs:
2008 res = symlink_nfs (oldpath, win32_newpath);
2009 __leave;
2010 case WSYM_native:
2011 case WSYM_nativestrict:
2012 res = symlink_native (oldpath, win32_newpath);
2013 if (!res)
2014 __leave;
2015 /* Strictly native? Too bad, unless the target is a Cygwin
2016 special file. */
2017 if (res == -1 && wsym_type == WSYM_nativestrict)
2018 {
2019 __seterrno ();
2020 __leave;
2021 }
2022 /* Otherwise, fall back to default symlink type. */
2023 wsym_type = WSYM_sysfile;
2024 /*FALLTHRU*/
2025 case WSYM_sysfile:
2026 if (win32_newpath.fs_flags () & FILE_SUPPORTS_REPARSE_POINTS)
2027 {
2028 res = symlink_wsl (oldpath, win32_newpath);
2029 if (!res)
2030 __leave;
2031 }
2032 /* On FSes not supporting reparse points, or in case of an error
2033 creating the WSL symlink, fall back to creating the plain old
2034 SYSTEM file symlink. */
2035 break;
2036 default:
2037 break;
2038 }
2039
2040 if (wsym_type == WSYM_lnk)
2041 {
2042 path_conv win32_oldpath;
2043 ITEMIDLIST *pidl = NULL;
2044 size_t full_len = 0;
2045 unsigned short oldpath_len, desc_len, relpath_len, pidl_len = 0;
2046 char desc[MAX_PATH + 1], *relpath;
2047
2048 if (!isdevice)
2049 {
2050 /* First create an IDLIST to learn how big our shortcut is
2051 going to be. */
2052 IShellFolder *psl;
2053
2054 /* The symlink target is relative to the directory in which the
2055 symlink gets created, not relative to the cwd. Therefore we
2056 have to mangle the path quite a bit before calling path_conv.*/
2057 if (isabspath (oldpath))
2058 win32_oldpath.check (oldpath,
2059 PC_SYM_NOFOLLOW,
2060 stat_suffixes);
2061 else
2062 {
2063 len = strrchr (win32_newpath.get_posix (), '/')
2064 - win32_newpath.get_posix () + 1;
2065 char *absoldpath = tp.t_get ();
2066 stpcpy (stpncpy (absoldpath, win32_newpath.get_posix (),
2067 len),
2068 oldpath);
2069 win32_oldpath.check (absoldpath, PC_SYM_NOFOLLOW,
2070 stat_suffixes);
2071 }
2072 if (SUCCEEDED (SHGetDesktopFolder (&psl)))
2073 {
2074 WCHAR wc_path[win32_oldpath.get_wide_win32_path_len () + 1];
2075 win32_oldpath.get_wide_win32_path (wc_path);
2076 /* Amazing but true: Even though the ParseDisplayName method
2077 takes a wide char path name, it does not understand the
2078 Win32 prefix for long pathnames! So we have to tack off
2079 the prefix and convert the path to the "normal" syntax
2080 for ParseDisplayName. */
2081 PWCHAR wc = wc_path + 4;
2082 if (wc[1] != L':') /* native UNC path */
2083 *(wc += 2) = L'\\';
2084 HRESULT res;
2085 if (SUCCEEDED (res = psl->ParseDisplayName (NULL, NULL, wc,
2086 NULL, &pidl,
2087 NULL)))
2088 {
2089 ITEMIDLIST *p;
2090
2091 for (p = pidl; p->mkid.cb > 0;
2092 p = (ITEMIDLIST *)((char *) p + p->mkid.cb))
2093 ;
2094 pidl_len = (char *) p - (char *) pidl + 2;
2095 }
2096 psl->Release ();
2097 }
2098 }
2099 /* Compute size of shortcut file. */
2100 full_len = sizeof (win_shortcut_hdr);
2101 if (pidl_len)
2102 full_len += sizeof (unsigned short) + pidl_len;
2103 oldpath_len = strlen (oldpath);
2104 /* Unfortunately the length of the description is restricted to a
2105 length of 2000 bytes. We don't want to add considerations for
2106 the different lengths and even 2000 bytes is not enough for long
2107 path names. So what we do here is to set the description to the
2108 POSIX path only if the path is not longer than MAX_PATH characters.
2109 We append the full path name after the regular shortcut data
2110 (see below), which works fine with Windows Explorer as well
2111 as older Cygwin versions (as long as the whole file isn't bigger
2112 than 8K). The description field is only used for backward
2113 compatibility to older Cygwin versions and those versions are
2114 not capable of handling long path names anyway. */
2115 desc_len = stpcpy (desc, oldpath_len > MAX_PATH
2116 ? "[path too long]" : oldpath) - desc;
2117 full_len += sizeof (unsigned short) + desc_len;
2118 /* Devices get the oldpath string unchanged as relative path. */
2119 if (isdevice)
2120 {
2121 relpath_len = oldpath_len;
2122 stpcpy (relpath = tp.c_get (), oldpath);
2123 }
2124 else
2125 {
2126 relpath_len = strlen (win32_oldpath.get_win32 ());
2127 stpcpy (relpath = tp.c_get (), win32_oldpath.get_win32 ());
2128 }
2129 full_len += sizeof (unsigned short) + relpath_len;
2130 full_len += sizeof (unsigned short) + oldpath_len;
2131 /* 1 byte more for trailing 0 written by stpcpy. */
2132 if (full_len < NT_MAX_PATH * sizeof (WCHAR))
2133 buf = tp.t_get ();
2134 else
2135 buf = (char *) alloca (full_len + 1);
2136
2137 /* Create shortcut header */
2138 win_shortcut_hdr *shortcut_header = (win_shortcut_hdr *) buf;
2139 memset (shortcut_header, 0, sizeof *shortcut_header);
2140 shortcut_header->size = sizeof *shortcut_header;
2141 shortcut_header->magic = GUID_shortcut;
2142 shortcut_header->flags = (WSH_FLAG_DESC | WSH_FLAG_RELPATH);
2143 if (pidl)
2144 shortcut_header->flags |= WSH_FLAG_IDLIST;
2145 shortcut_header->run = SW_NORMAL;
2146 cp = buf + sizeof (win_shortcut_hdr);
2147
2148 /* Create IDLIST */
2149 if (pidl)
2150 {
2151 *(unsigned short *)cp = pidl_len;
2152 memcpy (cp += 2, pidl, pidl_len);
2153 cp += pidl_len;
2154 CoTaskMemFree (pidl);
2155 }
2156
2157 /* Create description */
2158 *(unsigned short *)cp = desc_len;
2159 cp = stpcpy (cp += 2, desc);
2160
2161 /* Create relpath */
2162 *(unsigned short *)cp = relpath_len;
2163 cp = stpcpy (cp += 2, relpath);
2164
2165 /* Append the POSIX path after the regular shortcut data for
2166 the long path support. */
2167 unsigned short *plen = (unsigned short *) cp;
2168 cp += 2;
2169 *(PWCHAR) cp = 0xfeff; /* BOM */
2170 cp += 2;
2171 *plen = sys_mbstowcs ((PWCHAR) cp, NT_MAX_PATH, oldpath)
2172 * sizeof (WCHAR);
2173 cp += *plen;
2174 }
2175 else
2176 {
2177 /* Default technique creating a symlink. */
2178 buf = tp.t_get ();
2179 cp = stpcpy (buf, SYMLINK_COOKIE);
2180 *(PWCHAR) cp = 0xfeff; /* BOM */
2181 cp += 2;
2182 /* Note that the terminating nul is written. */
2183 cp += sys_mbstowcs ((PWCHAR) cp, NT_MAX_PATH, oldpath)
2184 * sizeof (WCHAR);
2185 }
2186
2187 OBJECT_ATTRIBUTES attr;
2188 IO_STATUS_BLOCK io;
2189 NTSTATUS status;
2190 ULONG access;
2191 HANDLE fh;
2192
2193 access = DELETE | FILE_GENERIC_WRITE;
2194 if (isdevice && win32_newpath.exists ())
2195 {
2196 status = NtOpenFile (&fh, FILE_WRITE_ATTRIBUTES,
2197 win32_newpath.get_object_attr (attr,
2198 sec_none_nih),
2199 &io, 0, FILE_OPEN_FOR_BACKUP_INTENT);
2200 if (!NT_SUCCESS (status))
2201 {
2202 __seterrno_from_nt_status (status);
2203 __leave;
2204 }
2205 status = NtSetAttributesFile (fh, FILE_ATTRIBUTE_NORMAL);
2206 NtClose (fh);
2207 if (!NT_SUCCESS (status))
2208 {
2209 __seterrno_from_nt_status (status);
2210 __leave;
2211 }
2212 }
2213 else if (!isdevice && win32_newpath.has_acls ()
2214 && !win32_newpath.isremote ())
2215 /* If the filesystem supports ACLs, we will overwrite the DACL after the
2216 call to NtCreateFile. This requires a handle with READ_CONTROL and
2217 WRITE_DAC access, otherwise get_file_sd and set_file_sd both have to
2218 open the file again.
2219 FIXME: On remote NTFS shares open sometimes fails because even the
2220 creator of the file doesn't have the right to change the DACL.
2221 I don't know what setting that is or how to recognize such a share,
2222 so for now we don't request WRITE_DAC on remote drives. */
2223 access |= READ_CONTROL | WRITE_DAC;
2224
2225 status = NtCreateFile (&fh, access,
2226 win32_newpath.get_object_attr (attr, sec_none_nih),
2227 &io, NULL, FILE_ATTRIBUTE_NORMAL,
2228 FILE_SHARE_VALID_FLAGS,
2229 isdevice ? FILE_OVERWRITE_IF : FILE_CREATE,
2230 FILE_SYNCHRONOUS_IO_NONALERT
2231 | FILE_NON_DIRECTORY_FILE
2232 | FILE_OPEN_FOR_BACKUP_INTENT,
2233 NULL, 0);
2234 if (!NT_SUCCESS (status))
2235 {
2236 __seterrno_from_nt_status (status);
2237 __leave;
2238 }
2239 if (io.Information == FILE_CREATED && win32_newpath.has_acls ())
2240 set_created_file_access (fh, win32_newpath,
2241 S_IFLNK | STD_RBITS | STD_WBITS);
2242 status = NtWriteFile (fh, NULL, NULL, NULL, &io, buf, cp - buf,
2243 NULL, NULL);
2244 if (NT_SUCCESS (status) && io.Information == (ULONG) (cp - buf))
2245 {
2246 status = NtSetAttributesFile (fh, wsym_type == WSYM_lnk
2247 ? FILE_ATTRIBUTE_READONLY
2248 : FILE_ATTRIBUTE_SYSTEM);
2249 if (!NT_SUCCESS (status))
2250 debug_printf ("Setting attributes failed, status = %y", status);
2251 res = 0;
2252 }
2253 else
2254 {
2255 __seterrno_from_nt_status (status);
2256 FILE_DISPOSITION_INFORMATION fdi = { TRUE };
2257 status = NtSetInformationFile (fh, &io, &fdi, sizeof fdi,
2258 FileDispositionInformation);
2259 if (!NT_SUCCESS (status))
2260 debug_printf ("Setting delete dispostion failed, status = %y",
2261 status);
2262 }
2263 NtClose (fh);
2264
2265 }
2266 __except (EFAULT) {}
2267 __endtry
2268 syscall_printf ("%d = symlink_worker(%s, %s, %d)",
2269 res, oldpath, win32_newpath.get_posix (), isdevice);
2270 return res;
2271 }
2272
2273 static bool
2274 cmp_shortcut_header (win_shortcut_hdr *file_header)
2275 {
2276 /* A Cygwin or U/Win shortcut only contains a description and a relpath.
2277 Cygwin shortcuts also might contain an ITEMIDLIST. The run type is
2278 always set to SW_NORMAL. */
2279 return file_header->size == sizeof (win_shortcut_hdr)
2280 && !memcmp (&file_header->magic, &GUID_shortcut, sizeof GUID_shortcut)
2281 && (file_header->flags & ~WSH_FLAG_IDLIST)
2282 == (WSH_FLAG_DESC | WSH_FLAG_RELPATH)
2283 && file_header->run == SW_NORMAL;
2284 }
2285
2286 int
2287 symlink_info::check_shortcut (HANDLE h)
2288 {
2289 tmp_pathbuf tp;
2290 win_shortcut_hdr *file_header;
2291 char *buf, *cp;
2292 unsigned short len;
2293 int res = 0;
2294 NTSTATUS status;
2295 IO_STATUS_BLOCK io;
2296 FILE_STANDARD_INFORMATION fsi;
2297 LARGE_INTEGER off = { QuadPart:0LL };
2298
2299 status = NtQueryInformationFile (h, &io, &fsi, sizeof fsi,
2300 FileStandardInformation);
2301 if (!NT_SUCCESS (status))
2302 {
2303 set_error (EIO);
2304 return 0;
2305 }
2306 if (fsi.EndOfFile.QuadPart <= (LONGLONG) sizeof (win_shortcut_hdr)
2307 || fsi.EndOfFile.QuadPart > 4 * 65536)
2308 return 0;
2309 if (fsi.EndOfFile.LowPart < NT_MAX_PATH * sizeof (WCHAR))
2310 buf = (char *) tp.w_get ();
2311 else
2312 buf = (char *) alloca (fsi.EndOfFile.LowPart + 1);
2313 status = NtReadFile (h, NULL, NULL, NULL, &io, buf, fsi.EndOfFile.LowPart,
2314 &off, NULL);
2315 if (!NT_SUCCESS (status))
2316 {
2317 if (status != STATUS_END_OF_FILE)
2318 set_error (EIO);
2319 return 0;
2320 }
2321 file_header = (win_shortcut_hdr *) buf;
2322 if (io.Information != fsi.EndOfFile.LowPart
2323 || !cmp_shortcut_header (file_header))
2324 return 0;
2325 cp = buf + sizeof (win_shortcut_hdr);
2326 if (file_header->flags & WSH_FLAG_IDLIST) /* Skip ITEMIDLIST */
2327 cp += *(unsigned short *) cp + 2;
2328 if (!(len = *(unsigned short *) cp))
2329 return 0;
2330 cp += 2;
2331 /* Check if this is a device file - these start with the sequence :\\ */
2332 if (strncmp (cp, ":\\", 2) == 0)
2333 res = strlen (strcpy (contents, cp)); /* Don't mess with device files */
2334 else
2335 {
2336 /* Has appended full path? If so, use it instead of description. */
2337 unsigned short relpath_len = *(unsigned short *) (cp + len);
2338 if (cp + len + 2 + relpath_len < buf + fsi.EndOfFile.LowPart)
2339 {
2340 cp += len + 2 + relpath_len;
2341 len = *(unsigned short *) cp;
2342 cp += 2;
2343 }
2344 if (*(PWCHAR) cp == 0xfeff) /* BOM */
2345 {
2346 char *tmpbuf = tp.c_get ();
2347 if (sys_wcstombs (tmpbuf, NT_MAX_PATH, (PWCHAR) (cp + 2))
2348 > SYMLINK_MAX)
2349 return 0;
2350 res = posixify (tmpbuf);
2351 }
2352 else if (len > SYMLINK_MAX)
2353 return 0;
2354 else
2355 {
2356 cp[len] = '\0';
2357 res = posixify (cp);
2358 }
2359 }
2360 if (res) /* It's a symlink. */
2361 path_flags |= PATH_SYMLINK | PATH_LNK;
2362 return res;
2363 }
2364
2365 int
2366 symlink_info::check_sysfile (HANDLE h)
2367 {
2368 tmp_pathbuf tp;
2369 char cookie_buf[sizeof (SYMLINK_COOKIE) - 1];
2370 char *srcbuf = tp.c_get ();
2371 int res = 0;
2372 NTSTATUS status;
2373 IO_STATUS_BLOCK io;
2374 bool interix_symlink = false;
2375 LARGE_INTEGER off = { QuadPart:0LL };
2376
2377 status = NtReadFile (h, NULL, NULL, NULL, &io, cookie_buf,
2378 sizeof (cookie_buf), &off, NULL);
2379 if (!NT_SUCCESS (status))
2380 {
2381 debug_printf ("ReadFile1 failed %y", status);
2382 if (status != STATUS_END_OF_FILE)
2383 set_error (EIO);
2384 return 0;
2385 }
2386 off.QuadPart = io.Information;
2387 if (io.Information == sizeof (cookie_buf)
2388 && memcmp (cookie_buf, SYMLINK_COOKIE, sizeof (cookie_buf)) == 0)
2389 {
2390 /* It's a symlink. */
2391 path_flags |= PATH_SYMLINK;
2392 }
2393 else if (io.Information == sizeof (cookie_buf)
2394 && memcmp (cookie_buf, SOCKET_COOKIE, sizeof (cookie_buf)) == 0)
2395 path_flags |= PATH_SOCKET;
2396 else if (io.Information >= sizeof (INTERIX_SYMLINK_COOKIE)
2397 && memcmp (cookie_buf, INTERIX_SYMLINK_COOKIE,
2398 sizeof (INTERIX_SYMLINK_COOKIE) - 1) == 0)
2399 {
2400 /* It's an Interix symlink. */
2401 path_flags |= PATH_SYMLINK;
2402 interix_symlink = true;
2403 /* Interix symlink cookies are shorter than Cygwin symlink cookies, so
2404 in case of an Interix symlink cooky we have read too far into the
2405 file. Set file pointer back to the position right after the cookie. */
2406 off.QuadPart = sizeof (INTERIX_SYMLINK_COOKIE) - 1;
2407 }
2408 if (path_flags & PATH_SYMLINK)
2409 {
2410 status = NtReadFile (h, NULL, NULL, NULL, &io, srcbuf,
2411 NT_MAX_PATH, &off, NULL);
2412 if (!NT_SUCCESS (status))
2413 {
2414 debug_printf ("ReadFile2 failed");
2415 if (status != STATUS_END_OF_FILE)
2416 set_error (EIO);
2417 }
2418 else if (*(PWCHAR) srcbuf == 0xfeff /* BOM */
2419 || interix_symlink)
2420 {
2421 /* Add trailing 0 to Interix symlink target. Skip BOM in Cygwin
2422 symlinks. */
2423 if (interix_symlink)
2424 ((PWCHAR) srcbuf)[io.Information / sizeof (WCHAR)] = L'\0';
2425 else
2426 srcbuf += 2;
2427 char *tmpbuf = tp.c_get ();
2428 if (sys_wcstombs (tmpbuf, NT_MAX_PATH, (PWCHAR) srcbuf)
2429 > SYMLINK_MAX)
2430 debug_printf ("symlink string too long");
2431 else
2432 res = posixify (tmpbuf);
2433 }
2434 else if (io.Information > SYMLINK_MAX + 1)
2435 debug_printf ("symlink string too long");
2436 else
2437 res = posixify (srcbuf);
2438 }
2439 return res;
2440 }
2441
2442 static bool
2443 check_reparse_point_string (PUNICODE_STRING subst)
2444 {
2445 /* Native mount points, or native non-relative symbolic links,
2446 can be treated as posix symlinks only if the SubstituteName
2447 can be converted from a native NT object namespace name to
2448 a win32 name. We only know how to convert names with two
2449 prefixes :
2450 "\??\UNC\..."
2451 "\??\X:..."
2452 Other reparse points will be treated as files or
2453 directories, not as posix symlinks.
2454 */
2455 if (RtlEqualUnicodePathPrefix (subst, &ro_u_natp, FALSE))
2456 {
2457 if (subst->Length >= 6 * sizeof(WCHAR) && subst->Buffer[5] == L':' &&
2458 (subst->Length == 6 * sizeof(WCHAR) || subst->Buffer[6] == L'\\'))
2459 return true;
2460 else if (subst->Length >= 8 * sizeof(WCHAR) &&
2461 wcsncmp (subst->Buffer + 4, L"UNC\\", 4) == 0)
2462 return true;
2463 }
2464 return false;
2465 }
2466
2467 /* Return values:
2468 <0: Negative errno.
2469 0: No symlink.
2470 1: Symlink.
2471 */
2472 int
2473 check_reparse_point_target (HANDLE h, bool remote, PREPARSE_DATA_BUFFER rp,
2474 PUNICODE_STRING psymbuf)
2475 {
2476 NTSTATUS status;
2477 IO_STATUS_BLOCK io;
2478
2479 /* On remote drives or under heavy load, NtFsControlFile can return with
2480 STATUS_PENDING. If so, instead of creating an event object, just set
2481 io.Status to an invalid value and perform a minimal wait until io.Status
2482 changed. */
2483 memset (&io, 0xff, sizeof io);
2484 status = NtFsControlFile (h, NULL, NULL, NULL, &io,
2485 FSCTL_GET_REPARSE_POINT, NULL, 0, (LPVOID) rp,
2486 MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
2487 if (status == STATUS_PENDING)
2488 {
2489 while (io.Status == (NTSTATUS) 0xffffffff)
2490 Sleep (1L);
2491 status = io.Status;
2492 }
2493 if (!NT_SUCCESS (status))
2494 {
2495 debug_printf ("NtFsControlFile(FSCTL_GET_REPARSE_POINT) failed, %y",
2496 status);
2497 /* When accessing the root dir of some remote drives (observed with
2498 OS X shares), the FILE_ATTRIBUTE_REPARSE_POINT flag is set, but
2499 the followup call to NtFsControlFile(FSCTL_GET_REPARSE_POINT)
2500 returns with STATUS_NOT_A_REPARSE_POINT. That's quite buggy, but
2501 we cope here with this scenario by not setting an error code. */
2502 if (status == STATUS_NOT_A_REPARSE_POINT)
2503 return 0;
2504 return -EIO;
2505 }
2506 if (rp->ReparseTag == IO_REPARSE_TAG_SYMLINK)
2507 {
2508 /* Windows evaluates native symlink literally. If a remote symlink points
2509 to, say, C:\foo, it will be handled as if the target is the local file
2510 C:\foo. That comes in handy since that's how symlinks are treated under
2511 POSIX as well. */
2512 RtlInitCountedUnicodeString (psymbuf,
2513 (PWCHAR)((PBYTE) rp->SymbolicLinkReparseBuffer.PathBuffer
2514 + rp->SymbolicLinkReparseBuffer.SubstituteNameOffset),
2515 rp->SymbolicLinkReparseBuffer.SubstituteNameLength);
2516 if ((rp->SymbolicLinkReparseBuffer.Flags & SYMLINK_FLAG_RELATIVE) ||
2517 check_reparse_point_string (psymbuf))
2518 return PATH_SYMLINK | PATH_REP;
2519 }
2520 else if (!remote && rp->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
2521 {
2522 /* Don't handle junctions on remote filesystems as symlinks. This type
2523 of reparse point is handled transparently by the OS so that the
2524 target of the junction is the remote directory it is supposed to
2525 point to. If we handle it as symlink, it will be mistreated as
2526 pointing to a dir on the local system. */
2527 RtlInitCountedUnicodeString (psymbuf,
2528 (PWCHAR)((PBYTE) rp->MountPointReparseBuffer.PathBuffer
2529 + rp->MountPointReparseBuffer.SubstituteNameOffset),
2530 rp->MountPointReparseBuffer.SubstituteNameLength);
2531 if (RtlEqualUnicodePathPrefix (psymbuf, &ro_u_volume, TRUE))
2532 {
2533 /* Volume mount point. Not treated as symlink. The return
2534 value -EPERM is a hint for the caller to treat this as a
2535 volume mount point. */
2536 return -EPERM;
2537 }
2538 if (check_reparse_point_string (psymbuf))
2539 return PATH_SYMLINK | PATH_REP;
2540 }
2541 else if (rp->ReparseTag == IO_REPARSE_TAG_LX_SYMLINK)
2542 {
2543 /* WSL symlink. Problem: We have to convert the path to UTF-16 for
2544 the caller. Reparse points are 16K max. The buffer given to rp
2545 is 32K. So there's enough trailing space in the buffer to convert
2546 to UTF-16 and let psymbuf point to it. */
2547 PREPARSE_LX_SYMLINK_BUFFER rpl = (PREPARSE_LX_SYMLINK_BUFFER) rp;
2548 char *path_buf = rpl->LxSymlinkReparseBuffer.PathBuffer;
2549 DWORD path_len = rpl->ReparseDataLength - sizeof (DWORD);
2550 bool full_path = false;
2551 const size_t drv_prefix_len = strlen ("/mnt");
2552 PBYTE utf16_ptr;
2553 PWCHAR utf16_buf;
2554 int utf16_bufsize;
2555
2556 /* 0-terminate path_buf for easier testing. */
2557 path_buf[path_len] = '\0';
2558 if (path_prefix_p ("/mnt", path_buf, drv_prefix_len, false))
2559 {
2560 size_t len = strlen (path_buf);
2561
2562 if (len <= drv_prefix_len + 1)
2563 {
2564 /* /mnt or /mnt/. Replace with cygdrive prefix. */
2565 stpcpy (path_buf, mount_table->cygdrive);
2566 path_len = mount_table->cygdrive_len;
2567 if (len == drv_prefix_len)
2568 {
2569 path_buf[mount_table->cygdrive_len - 1] = '\0';
2570 --path_len;
2571 }
2572 rp->ReparseDataLength = path_len + sizeof (DWORD);
2573 }
2574 else if (islower (path_buf[drv_prefix_len + 1])
2575 && (path_len == drv_prefix_len + 2
2576 || path_buf[drv_prefix_len + 2] == '/'))
2577 {
2578 /* Skip forward to the slash leading the drive letter.
2579 That leaves room for adding the colon. */
2580 path_buf += drv_prefix_len;
2581 path_len -= drv_prefix_len;
2582 full_path = true;
2583 }
2584 }
2585 /* Compute buffer for path converted to UTF-16. */
2586 utf16_ptr = (PBYTE) rpl + sizeof (REPARSE_LX_SYMLINK_BUFFER)
2587 + rp->ReparseDataLength;
2588 /* Skip \0-termination added above. */
2589 ++utf16_ptr;
2590 /* Make sure pointer is aligned */
2591 while ((intptr_t) utf16_ptr % sizeof (WCHAR))
2592 ++utf16_ptr;
2593 utf16_buf = (PWCHAR) utf16_ptr;
2594 utf16_bufsize = NT_MAX_PATH - (utf16_buf - (PWCHAR) rpl);
2595 /* Now convert path to UTF-16. */
2596 utf16_bufsize = MultiByteToWideChar (CP_UTF8, 0, path_buf, path_len,
2597 utf16_buf, utf16_bufsize);
2598 if (utf16_bufsize)
2599 {
2600 if (full_path)
2601 {
2602 utf16_buf[0] = utf16_buf[1]; /* Move drive letter to front */
2603 utf16_buf[1] = L':'; /* Add colon */
2604 }
2605 RtlInitCountedUnicodeString (psymbuf, utf16_buf,
2606 utf16_bufsize * sizeof (WCHAR));
2607 return PATH_SYMLINK | PATH_REP;
2608 }
2609 return -EIO;
2610 }
2611 #ifdef __WITH_AF_UNIX
2612 else if (rp->ReparseTag == IO_REPARSE_TAG_CYGUNIX)
2613 {
2614 PREPARSE_GUID_DATA_BUFFER rgp = (PREPARSE_GUID_DATA_BUFFER) rp;
2615
2616 if (memcmp (CYGWIN_SOCKET_GUID, &rgp->ReparseGuid, sizeof (GUID)) == 0)
2617 return PATH_SOCKET | PATH_REP;
2618 }
2619 #endif /* __WITH_AF_UNIX */
2620 return 0;
2621 }
2622
2623 int
2624 symlink_info::check_reparse_point (HANDLE h, bool remote)
2625 {
2626 tmp_pathbuf tp;
2627 PREPARSE_DATA_BUFFER rp = (PREPARSE_DATA_BUFFER) tp.c_get ();
2628 UNICODE_STRING symbuf;
2629 char srcbuf[SYMLINK_MAX + 7];
2630
2631 int ret = check_reparse_point_target (h, remote, rp, &symbuf);
2632 if (ret <= 0)
2633 {
2634 if (ret == -EIO)
2635 {
2636 set_error (EIO);
2637 return 0;
2638 }
2639 /* Maybe it's a reparse point, but it's certainly not one we recognize.
2640 Drop REPARSE attribute so we don't try to use the flag accidentally.
2641 It's just some arbitrary file or directory for us. */
2642 fileattr &= ~FILE_ATTRIBUTE_REPARSE_POINT;
2643 return ret;
2644 }
2645 /* ret is > 0, so it's a known reparse point, path in symbuf. */
2646 path_flags |= ret;
2647 if (ret & PATH_SYMLINK)
2648 sys_wcstombs (srcbuf, SYMLINK_MAX + 7, symbuf.Buffer,
2649 symbuf.Length / sizeof (WCHAR));
2650 /* A symlink is never a directory. */
2651 fileattr &= ~FILE_ATTRIBUTE_DIRECTORY;
2652 return posixify (srcbuf);
2653 }
2654
2655 int
2656 symlink_info::check_nfs_symlink (HANDLE h)
2657 {
2658 tmp_pathbuf tp;
2659 NTSTATUS status;
2660 IO_STATUS_BLOCK io;
2661 struct {
2662 FILE_GET_EA_INFORMATION fgei;
2663 char buf[sizeof (NFS_SYML_TARGET)];
2664 } fgei_buf;
2665 PFILE_FULL_EA_INFORMATION pffei;
2666 int res = 0;
2667
2668 /* To find out if the file is a symlink and to get the symlink target,
2669 try to fetch the NfsSymlinkTargetName EA. */
2670 fgei_buf.fgei.NextEntryOffset = 0;
2671 fgei_buf.fgei.EaNameLength = sizeof (NFS_SYML_TARGET) - 1;
2672 stpcpy (fgei_buf.fgei.EaName, NFS_SYML_TARGET);
2673 pffei = (PFILE_FULL_EA_INFORMATION) tp.w_get ();
2674 status = NtQueryEaFile (h, &io, pffei, NT_MAX_PATH * sizeof (WCHAR), TRUE,
2675 &fgei_buf.fgei, sizeof fgei_buf, NULL, TRUE);
2676 if (NT_SUCCESS (status) && pffei->EaValueLength > 0)
2677 {
2678 PWCHAR spath = (PWCHAR)
2679 (pffei->EaName + pffei->EaNameLength + 1);
2680 res = sys_wcstombs (contents, SYMLINK_MAX + 1,
2681 spath, pffei->EaValueLength);
2682 path_flags |= PATH_SYMLINK;
2683 }
2684 return res;
2685 }
2686
2687 int
2688 symlink_info::posixify (char *srcbuf)
2689 {
2690 /* The definition for a path in a native symlink is a bit weird. The Flags
2691 value seem to contain 0 for absolute paths (stored as NT native path)
2692 and 1 for relative paths. Relative paths are paths not starting with a
2693 drive letter. These are not converted to NT native, but stored as
2694 given. A path starting with a single backslash is relative to the
2695 current drive thus a "relative" value (Flags == 1).
2696 Funny enough it's possible to store paths with slashes instead of
2697 backslashes, but they are evaluated incorrectly by subsequent Windows
2698 calls like CreateFile (ERROR_INVALID_NAME). So, what we do here is to
2699 take paths starting with slashes at face value, evaluating them as
2700 Cygwin specific POSIX paths.
2701 A path starting with two slashes(!) or backslashes is converted into an
2702 NT UNC path. Unfortunately, in contrast to POSIX rules, paths starting
2703 with three or more (back)slashes are also converted into UNC paths,
2704 just incorrectly sticking to one redundant leading backslash. We go
2705 along with this behaviour to avoid scenarios in which native tools access
2706 other files than Cygwin.
2707 The above rules are used exactly the same way on Cygwin specific symlinks
2708 (sysfiles and shortcuts) to eliminate non-POSIX paths in the output. */
2709
2710 /* Eliminate native NT prefixes. */
2711 if (srcbuf[0] == '\\' && !strncmp (srcbuf + 1, "??\\", 3))
2712 {
2713 srcbuf += 4;
2714 if (srcbuf[1] != ':') /* native UNC path */
2715 *(srcbuf += 2) = '\\';
2716 }
2717 if (isdrive (srcbuf))
2718 mount_table->conv_to_posix_path (srcbuf, contents, 0);
2719 else if (srcbuf[0] == '\\')
2720 {
2721 if (srcbuf[1] == '\\') /* UNC path */
2722 slashify (srcbuf, contents, 0);
2723 else /* Paths starting with \ are current drive relative. */
2724 {
2725 char cvtbuf[SYMLINK_MAX + 1];
2726
2727 stpcpy (cvtbuf + cygheap->cwd.get_drive (cvtbuf), srcbuf);
2728 mount_table->conv_to_posix_path (cvtbuf, contents, 0);
2729 }
2730 }
2731 else /* Everything else is taken as is. */
2732 slashify (srcbuf, contents, 0);
2733 return strlen (contents);
2734 }
2735
2736 enum
2737 {
2738 SCAN_BEG,
2739 SCAN_LNK,
2740 SCAN_HASLNK,
2741 SCAN_JUSTCHECK,
2742 SCAN_JUSTCHECKTHIS, /* Never try to append a suffix. */
2743 SCAN_APPENDLNK,
2744 SCAN_EXTRALNK,
2745 SCAN_DONE,
2746 };
2747
2748 class suffix_scan
2749 {
2750 const suffix_info *suffixes, *suffixes_start;
2751 int nextstate;
2752 char *eopath;
2753 size_t namelen;
2754 public:
2755 const char *path;
2756 char *has (const char *, const suffix_info *);
2757 int next ();
2758 int lnk_match () {return nextstate >= SCAN_APPENDLNK;}
2759 size_t name_len () {return namelen;}
2760 };
2761
2762 char *
2763 suffix_scan::has (const char *in_path, const suffix_info *in_suffixes)
2764 {
2765 nextstate = SCAN_BEG;
2766 suffixes = suffixes_start = in_suffixes;
2767
2768 const char *fname = strrchr (in_path, '\\');
2769 fname = fname ? fname + 1 : in_path;
2770 char *ext_here = strrchr (fname, '.');
2771 path = in_path;
2772 eopath = strchr (path, '\0');
2773
2774 if (!ext_here)
2775 goto noext;
2776
2777 if (suffixes)
2778 {
2779 /* Check if the extension matches a known extension */
2780 for (const suffix_info *ex = in_suffixes; ex->name != NULL; ex++)
2781 if (ascii_strcasematch (ext_here, ex->name))
2782 {
2783 nextstate = SCAN_JUSTCHECK;
2784 suffixes = NULL; /* Has an extension so don't scan for one. */
2785 goto done;
2786 }
2787 }
2788
2789 /* Didn't match. Use last resort -- .lnk. */
2790 if (ascii_strcasematch (ext_here, ".lnk"))
2791 {
2792 nextstate = SCAN_HASLNK;
2793 suffixes = NULL;
2794 }
2795
2796 noext:
2797 ext_here = eopath;
2798
2799 done:
2800 namelen = eopath - fname;
2801 /* Avoid attaching suffixes if the resulting filename would be invalid.
2802 For performance reasons we don't check the length of a suffix, since
2803 we know that all suffixes are 4 chars in length.
2804
2805 FIXME: This is not really correct. A fully functional test should
2806 work on wide character paths. This would probably also speed
2807 up symlink_info::check. */
2808 if (namelen > NAME_MAX - 4)
2809 {
2810 nextstate = SCAN_JUSTCHECKTHIS;
2811 suffixes = NULL;
2812 }
2813 return ext_here;
2814 }
2815
2816 int
2817 suffix_scan::next ()
2818 {
2819 for (;;)
2820 {
2821 if (!suffixes)
2822 switch (nextstate)
2823 {
2824 case SCAN_BEG:
2825 suffixes = suffixes_start;
2826 if (!suffixes)
2827 {
2828 nextstate = SCAN_LNK;
2829 return 1;
2830 }
2831 nextstate = SCAN_EXTRALNK;
2832 /* fall through to suffix checking below */
2833 break;
2834 case SCAN_HASLNK:
2835 nextstate = SCAN_APPENDLNK; /* Skip SCAN_BEG */
2836 return 1;
2837 case SCAN_EXTRALNK:
2838 nextstate = SCAN_DONE;
2839 *eopath = '\0';
2840 return 0;
2841 case SCAN_JUSTCHECK:
2842 nextstate = SCAN_LNK;
2843 return 1;
2844 case SCAN_JUSTCHECKTHIS:
2845 nextstate = SCAN_DONE;
2846 return 1;
2847 case SCAN_LNK:
2848 case SCAN_APPENDLNK:
2849 nextstate = SCAN_DONE;
2850 if (namelen + (*eopath ? 8 : 4) > NAME_MAX)
2851 {
2852 *eopath = '\0';
2853 return 0;
2854 }
2855 strcat (eopath, ".lnk");
2856 return 1;
2857 default:
2858 *eopath = '\0';
2859 return 0;
2860 }
2861
2862 while (suffixes && suffixes->name)
2863 if (nextstate == SCAN_EXTRALNK
2864 && (!suffixes->addon || namelen > NAME_MAX - 8))
2865 suffixes++;
2866 else
2867 {
2868 strcpy (eopath, suffixes->name);
2869 if (nextstate == SCAN_EXTRALNK)
2870 strcat (eopath, ".lnk");
2871 suffixes++;
2872 return 1;
2873 }
2874 suffixes = NULL;
2875 }
2876 }
2877
2878 bool
2879 symlink_info::set_error (int in_errno)
2880 {
2881 bool res;
2882 if (!(pc_flags & PC_NO_ACCESS_CHECK)
2883 || in_errno == ENAMETOOLONG || in_errno == EIO)
2884 {
2885 error = in_errno;
2886 res = true;
2887 }
2888 else if (in_errno == ENOENT)
2889 res = true;
2890 else
2891 {
2892 fileattr = FILE_ATTRIBUTE_NORMAL;
2893 res = false;
2894 }
2895 return res;
2896 }
2897
2898 bool
2899 symlink_info::parse_device (const char *contents)
2900 {
2901 char *endptr;
2902 _major_t mymajor;
2903 _major_t myminor;
2904 __mode_t mymode;
2905
2906 mymajor = strtol (contents += 2, &endptr, 16);
2907 if (endptr == contents)
2908 return isdevice = false;
2909
2910 contents = endptr;
2911 myminor = strtol (++contents, &endptr, 16);
2912 if (endptr == contents)
2913 return isdevice = false;
2914
2915 contents = endptr;
2916 mymode = strtol (++contents, &endptr, 16);
2917 if (endptr == contents)
2918 return isdevice = false;
2919
2920 if ((mymode & S_IFMT) == S_IFIFO)
2921 {
2922 mymajor = _major (FH_FIFO);
2923 myminor = _minor (FH_FIFO);
2924 }
2925
2926 major = mymajor;
2927 minor = myminor;
2928 mode = mymode;
2929 return isdevice = true;
2930 }
2931
2932 /* Check if PATH is a symlink. PATH must be a valid Win32 path name.
2933
2934 If PATH is a symlink, put the value of the symlink--the file to
2935 which it points--into BUF. The value stored in BUF is not
2936 necessarily null terminated. BUFLEN is the length of BUF; only up
2937 to BUFLEN characters will be stored in BUF. BUF may be NULL, in
2938 which case nothing will be stored.
2939
2940 Set *SYML if PATH is a symlink.
2941
2942 Set *EXEC if PATH appears to be executable. This is an efficiency
2943 hack because we sometimes have to open the file anyhow. *EXEC will
2944 not be set for every executable file.
2945
2946 Return -1 on error, 0 if PATH is not a symlink, or the length
2947 stored into BUF if PATH is a symlink. */
2948
2949 int
2950 symlink_info::check (char *path, const suffix_info *suffixes, fs_info &fs,
2951 path_conv_handle &conv_hdl)
2952 {
2953 int res;
2954 HANDLE h;
2955 NTSTATUS status;
2956 UNICODE_STRING upath;
2957 OBJECT_ATTRIBUTES attr;
2958 IO_STATUS_BLOCK io;
2959 suffix_scan suffix;
2960
2961 const ULONG ci_flag = cygwin_shared->obcaseinsensitive
2962 || (mount_flags & MOUNT_NOPOSIX)
2963 ? OBJ_CASE_INSENSITIVE : 0;
2964 /* TODO: Temporarily do all char->UNICODE conversion here. This should
2965 already be slightly faster than using Ascii functions. */
2966 tmp_pathbuf tp;
2967 tp.u_get (&upath);
2968 InitializeObjectAttributes (&attr, &upath, ci_flag, NULL, NULL);
2969
2970 /* This label is used in case we encounter a FS which only handles
2971 DOS paths. See below. */
2972 bool restarted = false;
2973 restart:
2974
2975 h = NULL;
2976 res = 0;
2977 contents[0] = '\0';
2978 issymlink = true;
2979 isdevice = false;
2980 major = 0;
2981 minor = 0;
2982 mode = 0;
2983 // mount_flags is an incoming value set in path_conv */
2984 path_flags = 0;
2985
2986 PVOID eabuf = &nfs_aol_ffei;
2987 ULONG easize = sizeof nfs_aol_ffei;
2988
2989 ext_here = suffix.has (path, suffixes);
2990 extn = ext_here - path;
2991 bool had_ext = !!*ext_here;
2992
2993 /* If the filename is too long, don't even try. */
2994 if (suffix.name_len () > NAME_MAX)
2995 {
2996 set_error (ENAMETOOLONG);
2997 goto file_not_symlink;
2998 }
2999
3000 while (suffix.next ())
3001 {
3002 error = 0;
3003 get_nt_native_path (suffix.path, upath, mount_flags & MOUNT_DOS);
3004 if (h)
3005 {
3006 NtClose (h);
3007 h = NULL;
3008 }
3009 /* The EA given to NtCreateFile allows to get a handle to a symlink on
3010 an NFS share, rather than getting a handle to the target of the
3011 symlink (which would spoil the task of this method quite a bit).
3012 Fortunately it's ignored on most other file systems so we don't have
3013 to special case NFS too much. */
3014 status = NtCreateFile (&h,
3015 READ_CONTROL | FILE_READ_ATTRIBUTES | FILE_READ_EA,
3016 &attr, &io, NULL, 0, FILE_SHARE_VALID_FLAGS,
3017 FILE_OPEN,
3018 FILE_OPEN_REPARSE_POINT
3019 | FILE_OPEN_FOR_BACKUP_INTENT,
3020 eabuf, easize);
3021 debug_printf ("%y = NtCreateFile (%S)", status, &upath);
3022 /* No right to access EAs or EAs not supported? */
3023 if (!NT_SUCCESS (status)
3024 && (status == STATUS_ACCESS_DENIED
3025 || status == STATUS_EAS_NOT_SUPPORTED
3026 || status == STATUS_NOT_SUPPORTED
3027 || status == STATUS_INVALID_NETWORK_RESPONSE
3028 /* Or a bug in Samba 3.2.x (x <= 7) when accessing a share's
3029 root dir which has EAs enabled? */
3030 || status == STATUS_INVALID_PARAMETER))
3031 {
3032 /* If EAs are not supported, there's no sense to check them again
3033 with suffixes attached. So we set eabuf/easize to 0 here once. */
3034 if (status == STATUS_EAS_NOT_SUPPORTED
3035 || status == STATUS_NOT_SUPPORTED)
3036 {
3037 eabuf = NULL;
3038 easize = 0;
3039 }
3040 status = NtOpenFile (&h, READ_CONTROL | FILE_READ_ATTRIBUTES,
3041 &attr, &io, FILE_SHARE_VALID_FLAGS,
3042 FILE_OPEN_REPARSE_POINT
3043 | FILE_OPEN_FOR_BACKUP_INTENT);
3044 debug_printf ("%y = NtOpenFile (no-EAs %S)", status, &upath);
3045 }
3046 if (status == STATUS_OBJECT_NAME_NOT_FOUND)
3047 {
3048 /* There are filesystems out in the wild (Netapp, NWFS, and others)
3049 which are uncapable of generating pathnames outside the Win32
3050 rules. That means, filenames on these FSes must not have a
3051 leading space or trailing dots and spaces. This code snippet
3052 manages them. I really hope it's streamlined enough not to
3053 slow down normal operation. This extra check only kicks in if
3054 we encountered a STATUS_OBJECT_NAME_NOT_FOUND *and* we didn't
3055 already attach a suffix. */
3056 if (!restarted && !*ext_here && ext_here[-1] != '\\'
3057 && !(mount_flags & MOUNT_DOS))
3058 {
3059 /* Check for trailing dot or space or leading space in
3060 last component. */
3061 char *p = ext_here - 1;
3062 if (*p != '.' && *p != ' ')
3063 {
3064 while (*--p != '\\')
3065 assert(p >= path);
3066 if (*++p != ' ')
3067 p = NULL;
3068 }
3069 if (p)
3070 {
3071 /* If so, check if file resides on one of the known broken
3072 FSes only supporting filenames following DOS rules. */
3073 fs.update (&upath, NULL);
3074 if (fs.has_dos_filenames_only ())
3075 {
3076 /* If so, try again. Since we now know the FS, the
3077 filenames will be tweaked to follow DOS rules via the
3078 third parameter in the call to get_nt_native_path. */
3079 mount_flags |= MOUNT_DOS;
3080 restarted = true;
3081 goto restart;
3082 }
3083 }
3084 }
3085 }
3086 else if (status == STATUS_NETWORK_OPEN_RESTRICTION
3087 || status == STATUS_SYMLINK_CLASS_DISABLED)
3088 {
3089 /* These status codes are returned if you try to open a native
3090 symlink and the usage of this kind of symlink is forbidden
3091 (see fsutil). Since we can't open them at all, not even for
3092 stat purposes, we have to return a POSIX error code which is
3093 at least a bit helpful.
3094
3095 Additionally Windows 8 introduces a bug in NFS: If you have
3096 a symlink to a directory, with symlinks underneath, resolving
3097 the second level of symlinks fails if remote->remote symlinks
3098 are disabled in fsutil. Unfortunately that's the default. */
3099 set_error (ELOOP);
3100 break;
3101 }
3102
3103 if (NT_SUCCESS (status)
3104 /* Check file system while we're having the file open anyway.
3105 This speeds up path_conv noticably (~10%). */
3106 && (fs.inited () || fs.update (&upath, h)))
3107 {
3108 status = conv_hdl.get_finfo (h, fs.is_nfs ());
3109 if (NT_SUCCESS (status))
3110 fileattr = conv_hdl.get_dosattr (fs.is_nfs ());
3111 }
3112 if (!NT_SUCCESS (status))
3113 {
3114 debug_printf ("%y = NtQueryInformationFile (%S)", status, &upath);
3115 fileattr = INVALID_FILE_ATTRIBUTES;
3116
3117 /* One of the inner path components is invalid, or the path contains
3118 invalid characters. Bail out with ENOENT.
3119
3120 STATUS_IO_REPARSE_TAG_NOT_HANDLED is returned when trying to
3121 traversing a WSL symlink. For all practical purposes it's
3122 equivalent to traversing SYSTEM- or LNK-type symlink returning
3123 STATUS_OBJECT_PATH_NOT_FOUND.
3124
3125 Note that additional STATUS_OBJECT_PATH_INVALID and
3126 STATUS_OBJECT_PATH_SYNTAX_BAD status codes exist. The first one
3127 is seemingly not generated by NtQueryInformationFile, the latter
3128 is only generated if the path is no absolute path within the
3129 NT name space, which should not happen and would point to an
3130 error in get_nt_native_path. Both status codes are deliberately
3131 not tested here unless proved necessary. */
3132 if (status == STATUS_OBJECT_PATH_NOT_FOUND
3133 || status == STATUS_IO_REPARSE_TAG_NOT_HANDLED
3134 || status == STATUS_OBJECT_NAME_INVALID
3135 || status == STATUS_BAD_NETWORK_PATH
3136 || status == STATUS_BAD_NETWORK_NAME
3137 || status == STATUS_NO_MEDIA_IN_DEVICE)
3138 {
3139 set_error (ENOENT);
3140 if (ext_tacked_on && !had_ext)
3141 {
3142 *ext_here = '\0';
3143 ext_tacked_on = false;
3144 ext_here = NULL;
3145 extn = 0;
3146 }
3147 goto file_not_symlink;
3148 }
3149 if (status != STATUS_OBJECT_NAME_NOT_FOUND
3150 && status != STATUS_NO_SUCH_FILE) /* ENOENT on NFS or 9x share */
3151 {
3152 /* The file exists, but the user can't access it for one reason
3153 or the other. To get the file attributes we try to access the
3154 information by opening the parent directory and getting the
3155 file attributes using a matching NtQueryDirectoryFile call. */
3156 UNICODE_STRING dirname, basename;
3157 OBJECT_ATTRIBUTES dattr;
3158 HANDLE dir;
3159 struct {
3160 FILE_ID_BOTH_DIR_INFORMATION fdi;
3161 WCHAR dummy_buf[NAME_MAX + 1];
3162 } fdi_buf;
3163
3164 RtlSplitUnicodePath (&upath, &dirname, &basename);
3165 InitializeObjectAttributes (&dattr, &dirname, ci_flag,
3166 NULL, NULL);
3167 status = NtOpenFile (&dir, SYNCHRONIZE | FILE_LIST_DIRECTORY,
3168 &dattr, &io, FILE_SHARE_VALID_FLAGS,
3169 FILE_SYNCHRONOUS_IO_NONALERT
3170 | FILE_OPEN_FOR_BACKUP_INTENT
3171 | FILE_DIRECTORY_FILE);
3172 if (!NT_SUCCESS (status))
3173 {
3174 debug_printf ("%y = NtOpenFile(%S)", status, &dirname);
3175 /* There's a special case if the file is itself the root
3176 of a drive which is not accessible by the current user.
3177 This case is only recognized by the length of the
3178 basename part. If it's 0, the incoming file is the
3179 root of a drive. So we at least know it's a directory. */
3180 if (basename.Length)
3181 fileattr = FILE_ATTRIBUTE_DIRECTORY;
3182 else
3183 {
3184 fileattr = 0;
3185 set_error (geterrno_from_nt_status (status));
3186 }
3187 }
3188 else
3189 {
3190 status = NtQueryDirectoryFile (dir, NULL, NULL, NULL, &io,
3191 &fdi_buf, sizeof fdi_buf,
3192 FileIdBothDirectoryInformation,
3193 TRUE, &basename, TRUE);
3194 /* Take the opportunity to check file system while we're
3195 having the handle to the parent dir. */
3196 fs.update (&upath, dir);
3197 NtClose (dir);
3198 if (!NT_SUCCESS (status))
3199 {
3200 debug_printf ("%y = NtQueryDirectoryFile(%S)",
3201 status, &dirname);
3202 if (status == STATUS_NO_SUCH_FILE)
3203 {
3204 /* This can happen when trying to access files
3205 which match DOS device names on SMB shares.
3206 NtOpenFile failed with STATUS_ACCESS_DENIED,
3207 but the NtQueryDirectoryFile tells us the
3208 file doesn't exist. We're suspicious in this
3209 case and retry with the next suffix instead of
3210 just giving up. */
3211 set_error (ENOENT);
3212 continue;
3213 }
3214 fileattr = 0;
3215 }
3216 else
3217 {
3218 PFILE_ALL_INFORMATION pfai = conv_hdl.fai ();
3219
3220 fileattr = fdi_buf.fdi.FileAttributes;
3221 memcpy (&pfai->BasicInformation.CreationTime,
3222 &fdi_buf.fdi.CreationTime,
3223 4 * sizeof (LARGE_INTEGER));
3224 pfai->BasicInformation.FileAttributes = fileattr;
3225 pfai->StandardInformation.AllocationSize.QuadPart
3226 = fdi_buf.fdi.AllocationSize.QuadPart;
3227 pfai->StandardInformation.EndOfFile.QuadPart
3228 = fdi_buf.fdi.EndOfFile.QuadPart;
3229 pfai->StandardInformation.NumberOfLinks = 1;
3230 pfai->InternalInformation.IndexNumber.QuadPart
3231 = fdi_buf.fdi.FileId.QuadPart;
3232 }
3233 }
3234 ext_tacked_on = !!*ext_here;
3235 goto file_not_symlink;
3236 }
3237 set_error (ENOENT);
3238 continue;
3239 }
3240
3241 ext_tacked_on = !!*ext_here;
3242 /* Don't allow to returns directories with appended suffix. If we found
3243 a directory with a suffix which has been appended here, then this
3244 directory doesn't match the request. So, just do as usual if file
3245 hasn't been found. */
3246 if (ext_tacked_on && !had_ext && (fileattr & FILE_ATTRIBUTE_DIRECTORY))
3247 {
3248 set_error (ENOENT);
3249 continue;
3250 }
3251
3252 res = -1;
3253
3254 /* Reparse points are potentially symlinks. This check must be
3255 performed before checking the SYSTEM attribute for sysfile
3256 symlinks, since reparse points can have this flag set, too. */
3257 if ((fileattr & FILE_ATTRIBUTE_REPARSE_POINT))
3258 {
3259 res = check_reparse_point (h, fs.is_remote_drive ());
3260 if (res > 0)
3261 {
3262 /* A symlink is never a directory. */
3263 conv_hdl.fai ()->BasicInformation.FileAttributes
3264 &= ~FILE_ATTRIBUTE_DIRECTORY;
3265 break;
3266 }
3267 else
3268 {
3269 /* Volume moint point or unrecognized reparse point type.
3270 Make sure the open handle is not used in later stat calls.
3271 The handle has been opened with the FILE_OPEN_REPARSE_POINT
3272 flag, so it's a handle to the reparse point, not a handle
3273 to the volumes root dir. */
3274 pc_flags &= ~PC_KEEP_HANDLE;
3275 /* Volume mount point: The filesystem information for the top
3276 level directory should be for the volume top level directory,
3277 rather than for the reparse point itself. So we fetch the
3278 filesystem information again, but with a NULL handle.
3279 This does what we want because fs_info::update opens the
3280 handle without FILE_OPEN_REPARSE_POINT. */
3281 if (res < 0)
3282 fs.update (&upath, NULL);
3283 }
3284 }
3285
3286 /* Windows shortcuts are potentially treated as symlinks. Valid Cygwin
3287 & U/WIN shortcuts are R/O, but definitely not directories. */
3288 else if ((fileattr & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_DIRECTORY))
3289 == FILE_ATTRIBUTE_READONLY && suffix.lnk_match ())
3290 {
3291 HANDLE sym_h;
3292
3293 status = NtOpenFile (&sym_h, SYNCHRONIZE | GENERIC_READ, &attr, &io,
3294 FILE_SHARE_VALID_FLAGS,
3295 FILE_OPEN_FOR_BACKUP_INTENT
3296 | FILE_SYNCHRONOUS_IO_NONALERT);
3297 if (!NT_SUCCESS (status))
3298 res = 0;
3299 else
3300 {
3301 res = check_shortcut (sym_h);
3302 NtClose (sym_h);
3303 }
3304 if (!res)
3305 {
3306 /* If searching for `foo' and then finding a `foo.lnk' which
3307 is no shortcut, return the same as if file not found. */
3308 if (ext_tacked_on)
3309 {
3310 fileattr = INVALID_FILE_ATTRIBUTES;
3311 set_error (ENOENT);
3312 continue;
3313 }
3314 }
3315 else if (contents[0] != ':' || contents[1] != '\\'
3316 || !parse_device (contents))
3317 break;
3318 }
3319
3320 /* If searching for `foo' and then finding a `foo.lnk' which is
3321 no shortcut, return the same as if file not found. */
3322 else if (suffix.lnk_match () && ext_tacked_on)
3323 {
3324 fileattr = INVALID_FILE_ATTRIBUTES;
3325 set_error (ENOENT);
3326 continue;
3327 }
3328
3329 /* This is the old Cygwin method creating symlinks. A symlink will
3330 have the `system' file attribute. Only files can be symlinks
3331 (which can be symlinks to directories). */
3332 else if ((fileattr & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DIRECTORY))
3333 == FILE_ATTRIBUTE_SYSTEM)
3334 {
3335 HANDLE sym_h;
3336
3337 status = NtOpenFile (&sym_h, SYNCHRONIZE | GENERIC_READ, &attr, &io,
3338 FILE_SHARE_VALID_FLAGS,
3339 FILE_OPEN_FOR_BACKUP_INTENT
3340 | FILE_SYNCHRONOUS_IO_NONALERT);
3341
3342 if (!NT_SUCCESS (status))
3343 res = 0;
3344 else
3345 {
3346 res = check_sysfile (sym_h);
3347 NtClose (sym_h);
3348 }
3349 if (res)
3350 break;
3351 }
3352
3353 /* If the file is on an NFS share and could be opened with extended
3354 attributes, check if it's a symlink. Only files can be symlinks
3355 (which can be symlinks to directories). */
3356 else if (fs.is_nfs () && (conv_hdl.nfsattr ()->type & 7) == NF3LNK)
3357 {
3358 res = check_nfs_symlink (h);
3359 if (res)
3360 break;
3361 }
3362
3363 /* Normal file. */
3364 file_not_symlink:
3365 issymlink = false;
3366 syscall_printf ("%s", isdevice ? "is a device" : "not a symlink");
3367 res = 0;
3368 break;
3369 }
3370
3371 if (h)
3372 {
3373 if (pc_flags & PC_KEEP_HANDLE)
3374 conv_hdl.set (h);
3375 else
3376 NtClose (h);
3377 }
3378
3379 syscall_printf ("%d = symlink.check(%s, %p) (mount_flags %y, path_flags %y)",
3380 res, suffix.path, contents, mount_flags, path_flags);
3381 return res;
3382 }
3383
3384 /* "path" is the path in a virtual symlink. Set a symlink_info struct from
3385 that and proceed with further path checking afterwards. */
3386 int
3387 symlink_info::set (char *path)
3388 {
3389 strcpy (contents, path);
3390 mount_flags = 0;
3391 path_flags = PATH_SYMLINK;
3392 fileattr = FILE_ATTRIBUTE_NORMAL;
3393 error = 0;
3394 issymlink = true;
3395 isdevice = false;
3396 ext_tacked_on = false;
3397 ext_here = NULL;
3398 extn = major = minor = mode = 0;
3399 return strlen (path);
3400 }
3401
3402 /* readlink system call */
3403
3404 extern "C" ssize_t
3405 readlink (const char *__restrict path, char *__restrict buf, size_t buflen)
3406 {
3407 if (buflen < 0)
3408 {
3409 set_errno (ENAMETOOLONG);
3410 return -1;
3411 }
3412
3413 path_conv pathbuf (path, PC_SYM_CONTENTS, stat_suffixes);
3414
3415 if (pathbuf.error)
3416 {
3417 set_errno (pathbuf.error);
3418 syscall_printf ("-1 = readlink (%s, %p, %lu)", path, buf, buflen);
3419 return -1;
3420 }
3421
3422 if (!pathbuf.exists ())
3423 {
3424 set_errno (ENOENT);
3425 return -1;
3426 }
3427
3428 if (!pathbuf.issymlink ())
3429 {
3430 if (pathbuf.exists ())
3431 set_errno (EINVAL);
3432 return -1;
3433 }
3434
3435 size_t pathbuf_len = strlen (pathbuf.get_win32 ());
3436 ssize_t len = MIN (buflen, pathbuf_len);
3437 memcpy (buf, pathbuf.get_win32 (), len);
3438
3439 /* errno set by symlink.check if error */
3440 return len;
3441 }
3442
3443 /* Some programs rely on st_dev/st_ino being unique for each file.
3444 Hash the path name and hope for the best. The hash arg is not
3445 always initialized to zero since readdir needs to compute the
3446 dirent ino_t based on a combination of the hash of the directory
3447 done during the opendir call and the hash or the filename within
3448 the directory. FIXME: Not bullet-proof. */
3449 /* Cygwin internal */
3450 ino_t __reg2
3451 hash_path_name (ino_t hash, PUNICODE_STRING name)
3452 {
3453 if (name->Length == 0)
3454 return hash;
3455
3456 /* Build up hash. Name is already normalized */
3457 USHORT len = name->Length / sizeof (WCHAR);
3458 for (USHORT idx = 0; idx < len; ++idx)
3459 hash = RtlUpcaseUnicodeChar (name->Buffer[idx])
3460 + (hash << 6) + (hash << 16) - hash;
3461 return hash;
3462 }
3463
3464 ino_t __reg2
3465 hash_path_name (ino_t hash, PCWSTR name)
3466 {
3467 UNICODE_STRING uname;
3468 RtlInitUnicodeString (&uname, name);
3469 return hash_path_name (hash, &uname);
3470 }
3471
3472 ino_t __reg2
3473 hash_path_name (ino_t hash, const char *name)
3474 {
3475 UNICODE_STRING uname;
3476 RtlCreateUnicodeStringFromAsciiz (&uname, name);
3477 ino_t ret = hash_path_name (hash, &uname);
3478 RtlFreeUnicodeString (&uname);
3479 return ret;
3480 }
3481
3482 extern "C" char *
3483 getcwd (char *buf, size_t ulen)
3484 {
3485 char* res = NULL;
3486
3487 __try
3488 {
3489 if (ulen == 0 && buf)
3490 set_errno (EINVAL);
3491 else
3492 res = cygheap->cwd.get (buf, 1, 1, ulen);
3493 }
3494 __except (EFAULT) {}
3495 __endtry
3496 return res;
3497 }
3498
3499 /* getwd: Legacy. */
3500 extern "C" char *
3501 getwd (char *buf)
3502 {
3503 return getcwd (buf, PATH_MAX + 1); /*Per SuSv3!*/
3504 }
3505
3506 extern "C" char *
3507 get_current_dir_name (void)
3508 {
3509 const char *pwd = getenv ("PWD");
3510 char *cwd = getcwd (NULL, 0);
3511 struct stat pwdbuf, cwdbuf;
3512
3513 if (pwd && strcmp (pwd, cwd) != 0
3514 && stat64 (pwd, &pwdbuf) == 0
3515 && stat64 (cwd, &cwdbuf) == 0
3516 && pwdbuf.st_dev == cwdbuf.st_dev
3517 && pwdbuf.st_ino == cwdbuf.st_ino)
3518 {
3519 cwd = (char *) realloc (cwd, strlen (pwd) + 1);
3520 strcpy (cwd, pwd);
3521 }
3522
3523 return cwd;
3524 }
3525
3526 /* chdir: POSIX 5.2.1.1 */
3527 extern "C" int
3528 chdir (const char *in_dir)
3529 {
3530 int res = -1;
3531
3532 __try
3533 {
3534 if (!*in_dir)
3535 {
3536 set_errno (ENOENT);
3537 __leave;
3538 }
3539
3540 syscall_printf ("dir '%s'", in_dir);
3541
3542 /* Convert path. PC_NONULLEMPTY ensures that we don't check for
3543 NULL/empty/invalid again. */
3544 path_conv path (in_dir, PC_SYM_FOLLOW | PC_POSIX | PC_NONULLEMPTY);
3545 if (path.error)
3546 {
3547 set_errno (path.error);
3548 syscall_printf ("-1 = chdir (%s)", in_dir);
3549 __leave;
3550 }
3551
3552 const char *posix_cwd = NULL;
3553 dev_t devn = path.get_device ();
3554 if (!path.exists ())
3555 set_errno (ENOENT);
3556 else if (!path.isdir ())
3557 set_errno (ENOTDIR);
3558 else if (!isvirtual_dev (devn))
3559 {
3560 /* The sequence chdir("xx"); chdir(".."); must be a noop if xx
3561 is not a symlink. This is exploited by find.exe.
3562 The posix_cwd is just path.get_posix ().
3563 In other cases we let cwd.set obtain the Posix path through
3564 the mount table. */
3565 if (!isdrive (path.get_posix ()))
3566 posix_cwd = path.get_posix ();
3567 res = 0;
3568 }
3569 else
3570 {
3571 posix_cwd = path.get_posix ();
3572 res = 0;
3573 }
3574
3575 if (!res)
3576 res = cygheap->cwd.set (&path, posix_cwd);
3577
3578 /* Note that we're accessing cwd.posix without a lock here.
3579 I didn't think it was worth locking just for strace. */
3580 syscall_printf ("%R = chdir() cygheap->cwd.posix '%s' native '%S'", res,
3581 cygheap->cwd.get_posix (), path.get_nt_native_path ());
3582 }
3583 __except (EFAULT)
3584 {
3585 res = -1;
3586 }
3587 __endtry
3588 return res;
3589 }
3590
3591 extern "C" int
3592 fchdir (int fd)
3593 {
3594 int res;
3595 cygheap_fdget cfd (fd);
3596 if (cfd >= 0)
3597 res = chdir (cfd->get_name ());
3598 else
3599 res = -1;
3600
3601 syscall_printf ("%R = fchdir(%d)", res, fd);
3602 return res;
3603 }
3604
3605 /******************** Exported Path Routines *********************/
3606
3607 /* Cover functions to the path conversion routines.
3608 These are exported to the world as cygwin_foo by cygwin.din. */
3609
3610 #define return_with_errno(x) \
3611 do {\
3612 int err = (x);\
3613 if (!err)\
3614 return 0;\
3615 set_errno (err);\
3616 return -1;\
3617 } while (0)
3618
3619 extern "C" ssize_t
3620 cygwin_conv_path (cygwin_conv_path_t what, const void *from, void *to,
3621 size_t size)
3622 {
3623 tmp_pathbuf tp;
3624 path_conv p;
3625 size_t lsiz = 0;
3626 char *buf = NULL;
3627 PWCHAR path = NULL;
3628 int error = 0;
3629 int how = what & CCP_CONVFLAGS_MASK;
3630 what &= CCP_CONVTYPE_MASK;
3631 int ret = -1;
3632
3633 __try
3634 {
3635 if (!from)
3636 {
3637 set_errno (EINVAL);
3638 __leave;
3639 }
3640
3641 switch (what)
3642 {
3643 case CCP_POSIX_TO_WIN_A:
3644 {
3645 p.check ((const char *) from,
3646 PC_POSIX | PC_SYM_FOLLOW | PC_SYM_NOFOLLOW_REP
3647 | PC_NO_ACCESS_CHECK
3648 | ((how & CCP_RELATIVE) ? PC_NOFULL : 0), stat_suffixes);
3649 if (p.error)
3650 {
3651 set_errno (p.error);
3652 __leave;
3653 }
3654 PUNICODE_STRING up = p.get_nt_native_path ();
3655 buf = tp.c_get ();
3656 sys_wcstombs (buf, NT_MAX_PATH,
3657 up->Buffer, up->Length / sizeof (WCHAR));
3658 /* Convert native path to standard DOS path. */
3659 if (!strncmp (buf, "\\??\\", 4))
3660 {
3661 buf += 4;
3662 if (buf[1] != ':') /* native UNC path */
3663 *(buf += 2) = '\\';
3664 }
3665 else if (*buf == '\\')
3666 {
3667 /* Device name points to somewhere else in the NT namespace.
3668 Use GLOBALROOT prefix to convert to Win32 path. */
3669 char *p = buf + sys_wcstombs (buf, NT_MAX_PATH,
3670 ro_u_globalroot.Buffer,
3671 ro_u_globalroot.Length
3672 / sizeof (WCHAR));
3673 sys_wcstombs (p, NT_MAX_PATH - (p - buf),
3674 up->Buffer, up->Length / sizeof (WCHAR));
3675 }
3676 lsiz = strlen (buf) + 1;
3677 /* TODO: Incoming "." is a special case which leads to a trailing
3678 backslash ".\\" in the Win32 path. That's a result of the
3679 conversion in normalize_posix_path. This should not occur
3680 so the below code is just a band-aid. */
3681 if ((how & CCP_RELATIVE) && !strcmp ((const char *) from, ".")
3682 && !strcmp (buf, ".\\"))
3683 {
3684 lsiz = 2;
3685 buf[1] = '\0';
3686 }
3687 }
3688 break;
3689 case CCP_POSIX_TO_WIN_W:
3690 p.check ((const char *) from,
3691 PC_POSIX | PC_SYM_FOLLOW | PC_SYM_NOFOLLOW_REP
3692 | PC_NO_ACCESS_CHECK
3693 | ((how & CCP_RELATIVE) ? PC_NOFULL : 0), stat_suffixes);
3694 if (p.error)
3695 {
3696 set_errno (p.error);
3697 __leave;
3698 }
3699 /* Relative Windows paths are always restricted to MAX_PATH chars. */
3700 if ((how & CCP_RELATIVE) && !isabspath (p.get_win32 ())
3701 && sys_mbstowcs (NULL, 0, p.get_win32 ()) > MAX_PATH)
3702 {
3703 /* Recreate as absolute path. */
3704 p.check ((const char *) from, PC_POSIX | PC_SYM_FOLLOW
3705 | PC_NO_ACCESS_CHECK);
3706 if (p.error)
3707 {
3708 set_errno (p.error);
3709 __leave;
3710 }
3711 }
3712 lsiz = p.get_wide_win32_path_len () + 1;
3713 path = p.get_nt_native_path ()->Buffer;
3714
3715 /* Convert native path to standard DOS path. */
3716 if (!wcsncmp (path, L"\\??\\", 4))
3717 {
3718 path[1] = L'\\';
3719
3720 /* Drop long path prefix for short pathnames. Unfortunately there's
3721 quite a bunch of Win32 functions, especially in user32.dll,
3722 apparently, which don't grok long path names at all, not even
3723 in the UNICODE API. */
3724 if ((path[5] == L':' && lsiz <= MAX_PATH + 4)
3725 || (!wcsncmp (path + 4, L"UNC\\", 4) && lsiz <= MAX_PATH + 6))
3726 {
3727 path += 4;
3728 lsiz -= 4;
3729 if (path[1] != L':')
3730 {
3731 *(path += 2) = '\\';
3732 lsiz -= 2;
3733 }
3734 }
3735 }
3736 else if (*path == L'\\')
3737 {
3738 /* Device name points to somewhere else in the NT namespace.
3739 Use GLOBALROOT prefix to convert to Win32 path. */
3740 to = (void *) wcpcpy ((wchar_t *) to, ro_u_globalroot.Buffer);
3741 lsiz += ro_u_globalroot.Length / sizeof (WCHAR);
3742 }
3743 /* TODO: Same ".\\" band-aid as in CCP_POSIX_TO_WIN_A case. */
3744 if ((how & CCP_RELATIVE) && !strcmp ((const char *) from, ".")
3745 && !wcscmp (path, L".\\"))
3746 {
3747 lsiz = 2;
3748 path[1] = L'\0';
3749 }
3750 lsiz *= sizeof (WCHAR);
3751 break;
3752 case CCP_WIN_A_TO_POSIX:
3753 buf = tp.c_get ();
3754 error = mount_table->conv_to_posix_path ((const char *) from, buf,
3755 how | __CCP_APP_SLASH);
3756 if (error)
3757 {
3758 set_errno (p.error);
3759 __leave;
3760 }
3761 lsiz = strlen (buf) + 1;
3762 break;
3763 case CCP_WIN_W_TO_POSIX:
3764 buf = tp.c_get ();
3765 error = mount_table->conv_to_posix_path ((const PWCHAR) from, buf,
3766 how | __CCP_APP_SLASH);
3767 if (error)
3768 {
3769 set_errno (error);
3770 __leave;
3771 }
3772 lsiz = strlen (buf) + 1;
3773 break;
3774 default:
3775 set_errno (EINVAL);
3776 __leave;
3777 }
3778 if (!size)
3779 {
3780 ret = lsiz;
3781 __leave;
3782 }
3783 if (size < lsiz)
3784 {
3785 set_errno (ENOSPC);
3786 __leave;
3787 }
3788 switch (what)
3789 {
3790 case CCP_POSIX_TO_WIN_A:
3791 case CCP_WIN_A_TO_POSIX:
3792 case CCP_WIN_W_TO_POSIX:
3793 stpcpy ((char *) to, buf);
3794 break;
3795 case CCP_POSIX_TO_WIN_W:
3796 wcpcpy ((PWCHAR) to, path);
3797 break;
3798 }
3799 ret = 0;
3800 }
3801 __except (EFAULT) {}
3802 __endtry
3803 return ret;
3804 }
3805
3806 extern "C" void *
3807 cygwin_create_path (cygwin_conv_path_t what, const void *from)
3808 {
3809 void *to;
3810 ssize_t size = cygwin_conv_path (what, from, NULL, 0);
3811 if (size <= 0)
3812 to = NULL;
3813 else if (!(to = malloc (size)))
3814 to = NULL;
3815 if (cygwin_conv_path (what, from, to, size) == -1)
3816 {
3817 free (to);
3818 to = NULL;
3819 }
3820 return to;
3821 }
3822
3823 #ifdef __i386__
3824
3825 extern "C" int
3826 cygwin_conv_to_win32_path (const char *path, char *win32_path)
3827 {
3828 return cygwin_conv_path (CCP_POSIX_TO_WIN_A | CCP_RELATIVE, path, win32_path,
3829 MAX_PATH);
3830 }
3831
3832 extern "C" int
3833 cygwin_conv_to_full_win32_path (const char *path, char *win32_path)
3834 {
3835 return cygwin_conv_path (CCP_POSIX_TO_WIN_A | CCP_ABSOLUTE, path, win32_path,
3836 MAX_PATH);
3837 }
3838
3839 /* This is exported to the world as cygwin_foo by cygwin.din. */
3840
3841 extern "C" int
3842 cygwin_conv_to_posix_path (const char *path, char *posix_path)
3843 {
3844 return cygwin_conv_path (CCP_WIN_A_TO_POSIX | CCP_RELATIVE, path, posix_path,
3845 MAX_PATH);
3846 }
3847
3848 extern "C" int
3849 cygwin_conv_to_full_posix_path (const char *path, char *posix_path)
3850 {
3851 return cygwin_conv_path (CCP_WIN_A_TO_POSIX | CCP_ABSOLUTE, path, posix_path,
3852 MAX_PATH);
3853 }
3854
3855 #endif /* __i386__ */
3856
3857 /* The realpath function is required by POSIX:2008. */
3858
3859 extern "C" char *
3860 realpath (const char *__restrict path, char *__restrict resolved)
3861 {
3862 tmp_pathbuf tp;
3863 char *tpath;
3864
3865 /* Make sure the right errno is returned if path is NULL. */
3866 if (!path)
3867 {
3868 set_errno (EINVAL);
3869 return NULL;
3870 }
3871
3872 /* Guard reading from a potentially invalid path and writing to a
3873 potentially invalid resolved. */
3874 __try
3875 {
3876 /* Win32 drive letter paths and, generally, any path starting with a
3877 backslash, have to be converted to a POSIX path first, because
3878 path_conv leaves the incoming path untouched except for converting
3879 backslashes to forward slashes. This also covers '\\?\ and '\??\'
3880 path prefixes. */
3881 if (isdrive (path) || path[0] == '\\')
3882 {
3883 tpath = tp.c_get ();
3884 mount_table->conv_to_posix_path (path, tpath, 0);
3885 }
3886 else
3887 tpath = (char *) path;
3888
3889 path_conv real_path (tpath, PC_SYM_FOLLOW | PC_POSIX, stat_suffixes);
3890
3891
3892 /* POSIX 2008 requires malloc'ing if resolved is NULL, and states
3893 that using non-NULL resolved is asking for portability
3894 problems. */
3895
3896 if (!real_path.error && real_path.exists ())
3897 {
3898 if (!resolved)
3899 {
3900 resolved = (char *)
3901 malloc (strlen (real_path.get_posix ()) + 1);
3902 if (!resolved)
3903 return NULL;
3904 }
3905 strcpy (resolved, real_path.get_posix ());
3906 return resolved;
3907 }
3908
3909 /* FIXME: on error, Linux puts the name of the path
3910 component which could not be resolved into RESOLVED, but POSIX
3911 does not require this. */
3912 if (resolved)
3913 resolved[0] = '\0';
3914 set_errno (real_path.error ?: ENOENT);
3915 }
3916 __except (EFAULT) {}
3917 __endtry
3918 return NULL;
3919 }
3920
3921 /* Linux provides this extension. Since the only portable use of
3922 realpath requires a NULL second argument, we might as well have a
3923 one-argument wrapper. */
3924 extern "C" char *
3925 canonicalize_file_name (const char *path)
3926 {
3927 return realpath (path, NULL);
3928 }
3929
3930 /* Return non-zero if path is a POSIX path list.
3931 This is exported to the world as cygwin_foo by cygwin.din.
3932
3933 DOCTOOL-START
3934 <sect1 id="add-func-cygwin-posix-path-list-p">
3935 <para>Rather than use a mode to say what the "proper" path list
3936 format is, we allow any, and give apps the tools they need to
3937 convert between the two. If a ';' is present in the path list it's
3938 a Win32 path list. Otherwise, if the first path begins with
3939 [letter]: (in which case it can be the only element since if it
3940 wasn't a ';' would be present) it's a Win32 path list. Otherwise,
3941 it's a POSIX path list.</para>
3942 </sect1>
3943 DOCTOOL-END
3944 */
3945
3946 extern "C" int
3947 cygwin_posix_path_list_p (const char *path)
3948 {
3949 int posix_p = !(strchr (path, ';') || isdrive (path));
3950 return posix_p;
3951 }
3952
3953 /* These are used for apps that need to convert env vars like PATH back and
3954 forth. The conversion is a two step process. First, an upper bound on the
3955 size of the buffer needed is computed. Then the conversion is done. This
3956 allows the caller to use alloca if it wants. */
3957
3958 static int
3959 conv_path_list_buf_size (const char *path_list, bool to_posix)
3960 {
3961 int i, num_elms, max_mount_path_len, size;
3962 const char *p;
3963
3964 path_conv pc(".", PC_POSIX);
3965 /* The theory is that an upper bound is
3966 current_size + (num_elms * max_mount_path_len) */
3967 /* FIXME: This method is questionable in the long run. */
3968
3969 unsigned nrel;
3970 char delim = to_posix ? ';' : ':';
3971 for (p = path_list, num_elms = nrel = 0; p; num_elms++)
3972 {
3973 if (!isabspath (p))
3974 nrel++;
3975 p = strchr (++p, delim);
3976 }
3977
3978 /* 7: strlen ("//c") + slop, a conservative initial value */
3979 for (max_mount_path_len = sizeof ("/cygdrive/X"), i = 0;
3980 i < mount_table->nmounts; i++)
3981 {
3982 int mount_len = (to_posix
3983 ? mount_table->mount[i].posix_pathlen
3984 : mount_table->mount[i].native_pathlen);
3985 if (max_mount_path_len < mount_len)
3986 max_mount_path_len = mount_len;
3987 }
3988
3989 /* 100: slop */
3990 size = strlen (path_list)
3991 + (num_elms * max_mount_path_len)
3992 + (nrel * strlen (to_posix ? pc.get_posix () : pc.get_win32 ()))
3993 + 100;
3994
3995 return size;
3996 }
3997
3998 extern "C" ssize_t
3999 env_PATH_to_posix (const void *win32, void *posix, size_t size)
4000 {
4001 return_with_errno (conv_path_list ((const char *) win32, (char *) posix,
4002 size, ENV_CVT));
4003 }
4004
4005 #ifdef __i386__
4006
4007 extern "C" int
4008 cygwin_win32_to_posix_path_list_buf_size (const char *path_list)
4009 {
4010 return conv_path_list_buf_size (path_list, true);
4011 }
4012
4013 extern "C" int
4014 cygwin_posix_to_win32_path_list_buf_size (const char *path_list)
4015 {
4016 return conv_path_list_buf_size (path_list, false);
4017 }
4018
4019 extern "C" int
4020 cygwin_win32_to_posix_path_list (const char *win32, char *posix)
4021 {
4022 return_with_errno (conv_path_list (win32, posix, MAX_PATH,
4023 CCP_WIN_A_TO_POSIX | CCP_RELATIVE));
4024 }
4025
4026 extern "C" int
4027 cygwin_posix_to_win32_path_list (const char *posix, char *win32)
4028 {
4029 return_with_errno (conv_path_list (posix, win32, MAX_PATH,
4030 CCP_POSIX_TO_WIN_A | CCP_RELATIVE));
4031 }
4032
4033 #endif /* __i386__ */
4034
4035 extern "C" ssize_t
4036 cygwin_conv_path_list (cygwin_conv_path_t what, const void *from, void *to,
4037 size_t size)
4038 {
4039 int ret;
4040 char *winp = NULL;
4041 void *orig_to = NULL;
4042 tmp_pathbuf tp;
4043
4044 switch (what & CCP_CONVTYPE_MASK)
4045 {
4046 case CCP_WIN_W_TO_POSIX:
4047 if (!sys_wcstombs_alloc (&winp, HEAP_NOTHEAP, (const wchar_t *) from,
4048 (size_t) -1))
4049 return -1;
4050 what = (what & ~CCP_CONVTYPE_MASK) | CCP_WIN_A_TO_POSIX;
4051 from = (const void *) winp;
4052 break;
4053 case CCP_POSIX_TO_WIN_W:
4054 if (size == 0)
4055 return conv_path_list_buf_size ((const char *) from, 0)
4056 * sizeof (WCHAR);
4057 what = (what & ~CCP_CONVTYPE_MASK) | CCP_POSIX_TO_WIN_A;
4058 orig_to = to;
4059 to = (void *) tp.w_get ();
4060 size = 65536;
4061 break;
4062 }
4063 switch (what & CCP_CONVTYPE_MASK)
4064 {
4065 case CCP_WIN_A_TO_POSIX:
4066 case CCP_POSIX_TO_WIN_A:
4067 if (size == 0)
4068 return conv_path_list_buf_size ((const char *) from,
4069 what == CCP_WIN_A_TO_POSIX);
4070 ret = conv_path_list ((const char *) from, (char *) to, size, what);
4071 /* Free winp buffer in case of CCP_WIN_W_TO_POSIX. */
4072 if (winp)
4073 free (winp);
4074 /* Convert to WCHAR in case of CCP_POSIX_TO_WIN_W. */
4075 if (orig_to)
4076 sys_mbstowcs ((wchar_t *) orig_to, size / sizeof (WCHAR),
4077 (const char *) to, (size_t) -1);
4078 return_with_errno (ret);
4079 break;
4080 default:
4081 break;
4082 }
4083 set_errno (EINVAL);
4084 return -1;
4085 }
4086
4087 /* cygwin_split_path: Split a path into directory and file name parts.
4088 Buffers DIR and FILE are assumed to be big enough.
4089
4090 Examples (path -> `dir' / `file'):
4091 / -> `/' / `'
4092 "" -> `.' / `'
4093 . -> `.' / `.' (FIXME: should this be `.' / `'?)
4094 .. -> `.' / `..' (FIXME: should this be `..' / `'?)
4095 foo -> `.' / `foo'
4096 foo/bar -> `foo' / `bar'
4097 foo/bar/ -> `foo' / `bar'
4098 /foo -> `/' / `foo'
4099 /foo/bar -> `/foo' / `bar'
4100 c: -> `c:/' / `'
4101 c:/ -> `c:/' / `'
4102 c:foo -> `c:/' / `foo'
4103 c:/foo -> `c:/' / `foo'
4104 */
4105
4106 extern "C" void
4107 cygwin_split_path (const char *path, char *dir, char *file)
4108 {
4109 int dir_started_p = 0;
4110
4111 /* Deal with drives.
4112 Remember that c:foo <==> c:/foo. */
4113 if (isdrive (path))
4114 {
4115 *dir++ = *path++;
4116 *dir++ = *path++;
4117 *dir++ = '/';
4118 if (!*path)
4119 {
4120 *dir = 0;
4121 *file = 0;
4122 return;
4123 }
4124 if (isdirsep (*path))
4125 ++path;
4126 dir_started_p = 1;
4127 }
4128
4129 /* Determine if there are trailing slashes and "delete" them if present.
4130 We pretend as if they don't exist. */
4131 const char *end = path + strlen (path);
4132 /* path + 1: keep leading slash. */
4133 while (end > path + 1 && isdirsep (end[-1]))
4134 --end;
4135
4136 /* At this point, END points to one beyond the last character
4137 (with trailing slashes "deleted"). */
4138
4139 /* Point LAST_SLASH at the last slash (duh...). */
4140 const char *last_slash;
4141 for (last_slash = end - 1; last_slash >= path; --last_slash)
4142 if (isdirsep (*last_slash))
4143 break;
4144
4145 if (last_slash == path)
4146 {
4147 *dir++ = '/';
4148 *dir = 0;
4149 }
4150 else if (last_slash > path)
4151 {
4152 memcpy (dir, path, last_slash - path);
4153 dir[last_slash - path] = 0;
4154 }
4155 else
4156 {
4157 if (dir_started_p)
4158 ; /* nothing to do */
4159 else
4160 *dir++ = '.';
4161 *dir = 0;
4162 }
4163
4164 memcpy (file, last_slash + 1, end - last_slash - 1);
4165 file[end - last_slash - 1] = 0;
4166 }
4167
4168 static inline void
4169 copy_cwd_str (PUNICODE_STRING tgt, PUNICODE_STRING src)
4170 {
4171 RtlCopyUnicodeString (tgt, src);
4172 if (tgt->Buffer[tgt->Length / sizeof (WCHAR) - 1] != L'\\')
4173 {
4174 tgt->Buffer[tgt->Length / sizeof (WCHAR)] = L'\\';
4175 tgt->Length += sizeof (WCHAR);
4176 }
4177 }
4178
4179 /*****************************************************************************/
4180
4181 /* The find_fast_cwd_pointer function and parts of the
4182 cwdstuff::override_win32_cwd method are based on code using the
4183 following license:
4184
4185 Copyright 2010 John Carey. All rights reserved.
4186
4187 Redistribution and use in source and binary forms, with or without
4188 modification, are permitted provided that the following conditions
4189 are met:
4190
4191 1. Redistributions of source code must retain the above
4192 copyright notice, this list of conditions and the following
4193 disclaimer.
4194
4195 2. Redistributions in binary form must reproduce the above
4196 copyright notice, this list of conditions and the following
4197 disclaimer in the documentation and/or other materials provided
4198 with the distribution.
4199
4200 THIS SOFTWARE IS PROVIDED BY JOHN CAREY ``AS IS'' AND ANY EXPRESS
4201 OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
4202 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
4203 ARE DISCLAIMED. IN NO EVENT SHALL JOHN CAREY OR CONTRIBUTORS BE
4204 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
4205 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
4206 OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
4207 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
4208 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
4209 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
4210 USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
4211 DAMAGE. */
4212
4213 void
4214 fcwd_access_t::SetFSCharacteristics (LONG val)
4215 {
4216 /* Special case FSCharacteristics. Didn't exist originally. */
4217 switch (fast_cwd_version ())
4218 {
4219 case FCWD_OLD:
4220 break;
4221 case FCWD_W7:
4222 f7.FSCharacteristics = val;
4223 break;
4224 case FCWD_W8:
4225 f8.FSCharacteristics = val;
4226 break;
4227 }
4228 }
4229
4230 fcwd_version_t &
4231 fcwd_access_t::fast_cwd_version ()
4232 {
4233 return cygheap->cwd.fast_cwd_version;
4234 }
4235
4236 void
4237 fcwd_access_t::CopyPath (UNICODE_STRING &target)
4238 {
4239 /* Copy the Path contents over into the UNICODE_STRING referenced by
4240 target. This is used to set the CurrentDirectoryName in the
4241 user parameter block. */
4242 target = Path ();
4243 }
4244
4245 void
4246 fcwd_access_t::Free (PVOID heap)
4247 {
4248 /* Decrement the reference count. If it's down to 0, free
4249 structure from heap. */
4250 if (InterlockedDecrement (&ReferenceCount ()) == 0)
4251 {
4252 /* The handle on init is always a fresh one, not the handle inherited
4253 from the parent process. We always have to close it here.
4254 Note: The handle could be NULL, if we cd'ed into a virtual dir. */
4255 HANDLE h = DirectoryHandle ();
4256 if (h)
4257 NtClose (h);
4258 RtlFreeHeap (heap, 0, this);
4259 }
4260 }
4261
4262 void
4263 fcwd_access_t::FillIn (HANDLE dir, PUNICODE_STRING name,
4264 ULONG old_dismount_count)
4265 {
4266 /* Fill in all values into this FAST_CWD structure. */
4267 DirectoryHandle () = dir;
4268 ReferenceCount () = 1;
4269 OldDismountCount () = old_dismount_count;
4270 /* The new structure stores the device characteristics of the
4271 volume holding the dir. RtlGetCurrentDirectory_U checks
4272 if the FILE_REMOVABLE_MEDIA flag is set and, if so, checks if
4273 the volume is still the same as the one used when opening
4274 the directory handle.
4275 We don't call NtQueryVolumeInformationFile for the \\?\PIPE,
4276 though. It just returns STATUS_INVALID_HANDLE anyway. */
4277 if (fast_cwd_version () != FCWD_OLD)
4278 {
4279 SetFSCharacteristics (0);
4280 if (name != &ro_u_pipedir)
4281 {
4282 IO_STATUS_BLOCK io;
4283 FILE_FS_DEVICE_INFORMATION ffdi;
4284 if (NT_SUCCESS (NtQueryVolumeInformationFile (dir, &io, &ffdi,
4285 sizeof ffdi, FileFsDeviceInformation)))
4286 SetFSCharacteristics (ffdi.Characteristics);
4287 }
4288 }
4289 RtlInitEmptyUnicodeString (&Path (), Buffer (),
4290 MAX_PATH * sizeof (WCHAR));
4291 copy_cwd_str (&Path (), name);
4292 }
4293
4294 void
4295 fcwd_access_t::SetDirHandleFromBufferPointer (PWCHAR buf_p, HANDLE dir)
4296 {
4297 /* Input: The buffer pointer as it's stored in the user parameter block
4298 and a directory handle.
4299 This function computes the address to the FAST_CWD structure based
4300 on the version and overwrites the directory handle. It is only
4301 used if we couldn't figure out the address of fast_cwd_ptr. */
4302 fcwd_access_t *f_cwd;
4303 switch (fast_cwd_version ())
4304 {
4305 case FCWD_OLD:
4306 default:
4307 f_cwd = (fcwd_access_t *)
4308 ((PBYTE) buf_p - __builtin_offsetof (FAST_CWD_OLD, Buffer));
4309 break;
4310 case FCWD_W7:
4311 f_cwd = (fcwd_access_t *)
4312 ((PBYTE) buf_p - __builtin_offsetof (FAST_CWD_7, Buffer));
4313 break;
4314 case FCWD_W8:
4315 f_cwd = (fcwd_access_t *)
4316 ((PBYTE) buf_p - __builtin_offsetof (FAST_CWD_8, Buffer));
4317 break;
4318 }
4319 f_cwd->DirectoryHandle () = dir;
4320 }
4321
4322 void
4323 fcwd_access_t::SetVersionFromPointer (PBYTE buf_p, bool is_buffer)
4324 {
4325 /* Given a pointer to the FAST_CWD structure (is_buffer == false) or a
4326 pointer to the Buffer within (is_buffer == true), this function
4327 computes the FAST_CWD version by checking that Path.MaximumLength
4328 equals MAX_PATH, and that Path.Buffer == Buffer. */
4329 if (is_buffer)
4330 buf_p -= __builtin_offsetof (FAST_CWD_8, Buffer);
4331 fcwd_access_t *f_cwd = (fcwd_access_t *) buf_p;
4332 if (f_cwd->f8.Path.MaximumLength == MAX_PATH * sizeof (WCHAR)
4333 && f_cwd->f8.Path.Buffer == f_cwd->f8.Buffer)
4334 fast_cwd_version () = FCWD_W8;
4335 else if (f_cwd->f7.Path.MaximumLength == MAX_PATH * sizeof (WCHAR)
4336 && f_cwd->f7.Path.Buffer == f_cwd->f7.Buffer)
4337 fast_cwd_version () = FCWD_W7;
4338 else
4339 fast_cwd_version () = FCWD_OLD;
4340 }
4341
4342 /* This function scans the code in ntdll.dll to find the address of the
4343 global variable used to access the CWD. While the pointer is global,
4344 it's not exported from the DLL, unfortunately. Therefore we have to
4345 use some knowledge to figure out the address. */
4346
4347 #ifdef __x86_64__
4348
4349 #define peek32(x) (*(int32_t *)(x))
4350
4351 static fcwd_access_t **
4352 find_fast_cwd_pointer ()
4353 {
4354 /* Fetch entry points of relevant functions in ntdll.dll. */
4355 HMODULE ntdll = GetModuleHandle ("ntdll.dll");
4356 if (!ntdll)
4357 return NULL;
4358 const uint8_t *get_dir = (const uint8_t *)
4359 GetProcAddress (ntdll, "RtlGetCurrentDirectory_U");
4360 const uint8_t *ent_crit = (const uint8_t *)
4361 GetProcAddress (ntdll, "RtlEnterCriticalSection");
4362 if (!get_dir || !ent_crit)
4363 return NULL;
4364 /* Search first relative call instruction in RtlGetCurrentDirectory_U. */
4365 const uint8_t *rcall = (const uint8_t *) memchr (get_dir, 0xe8, 80);
4366 if (!rcall)
4367 return NULL;
4368 /* Fetch offset from instruction and compute address of called function.
4369 This function actually fetches the current FAST_CWD instance and
4370 performs some other actions, not important to us. */
4371 const uint8_t *use_cwd = rcall + 5 + peek32 (rcall + 1);
4372 /* Next we search for the locking mechanism and perform a sanity check.
4373 On Pre-Windows 8 we basically look for the RtlEnterCriticalSection call.
4374 Windows 8 does not call RtlEnterCriticalSection. The code manipulates
4375 the FastPebLock manually, probably because RtlEnterCriticalSection has
4376 been converted to an inline function. Either way, we test if the code
4377 uses the FastPebLock. */
4378 const uint8_t *movrbx;
4379 const uint8_t *lock = (const uint8_t *)
4380 memmem ((const char *) use_cwd, 80,
4381 "\xf0\x0f\xba\x35", 4);
4382 if (lock)
4383 {
4384 /* The lock instruction tweaks the LockCount member, which is not at
4385 the start of the PRTL_CRITICAL_SECTION structure. So we have to
4386 subtract the offset of LockCount to get the real address. */
4387 PRTL_CRITICAL_SECTION lockaddr =
4388 (PRTL_CRITICAL_SECTION) (lock + 9 + peek32 (lock + 4)
4389 - offsetof (RTL_CRITICAL_SECTION, LockCount));
4390 /* Test if lock address is FastPebLock. */
4391 if (lockaddr != NtCurrentTeb ()->Peb->FastPebLock)
4392 return NULL;
4393 /* Search `mov rel(%rip),%rbx'. This is the instruction fetching the
4394 address of the current fcwd_access_t pointer, and it should be pretty
4395 near to the locking stuff. */
4396 movrbx = (const uint8_t *) memmem ((const char *) lock, 40,
4397 "\x48\x8b\x1d", 3);
4398 }
4399 else
4400 {
4401 /* Usually the callq RtlEnterCriticalSection follows right after
4402 fetching the lock address. */
4403 int call_rtl_offset = 7;
4404 /* Search `lea rel(%rip),%rcx'. This loads the address of the lock into
4405 %rcx for the subsequent RtlEnterCriticalSection call. */
4406 lock = (const uint8_t *) memmem ((const char *) use_cwd, 80,
4407 "\x48\x8d\x0d", 3);
4408 if (!lock)
4409 {
4410 /* Windows 8.1 Preview calls `lea rel(rip),%r12' then some unrelated
4411 or, then `mov %r12,%rcx', then `callq RtlEnterCriticalSection'. */
4412 lock = (const uint8_t *) memmem ((const char *) use_cwd, 80,
4413 "\x4c\x8d\x25", 3);
4414 if (!lock)
4415 return NULL;
4416 call_rtl_offset = 14;
4417 }
4418 PRTL_CRITICAL_SECTION lockaddr =
4419 (PRTL_CRITICAL_SECTION) (lock + 7 + peek32 (lock + 3));
4420 /* Test if lock address is FastPebLock. */
4421 if (lockaddr != NtCurrentTeb ()->Peb->FastPebLock)
4422 return NULL;
4423 /* Next is the `callq RtlEnterCriticalSection'. */
4424 lock += call_rtl_offset;
4425 if (lock[0] != 0xe8)
4426 return NULL;
4427 const uint8_t *call_addr = (const uint8_t *)
4428 (lock + 5 + peek32 (lock + 1));
4429 if (call_addr != ent_crit)
4430 return NULL;
4431 /* In contrast to the above Windows 8 code, we don't have to search
4432 for the `mov rel(%rip),%rbx' instruction. It follows right after
4433 the call to RtlEnterCriticalSection. */
4434 movrbx = lock + 5;
4435 }
4436 if (!movrbx)
4437 return NULL;
4438 /* Check that the next instruction tests if the fetched value is NULL. */
4439 const uint8_t *testrbx = (const uint8_t *)
4440 memmem (movrbx + 7, 3, "\x48\x85\xdb", 3);
4441 if (!testrbx)
4442 return NULL;
4443 /* Compute address of the fcwd_access_t ** pointer. */
4444 return (fcwd_access_t **) (testrbx + peek32 (movrbx + 3));
4445 }
4446 #else
4447
4448 #define peek32(x) (*(uint32_t *)(x))
4449
4450 static fcwd_access_t **
4451 find_fast_cwd_pointer ()
4452 {
4453 /* Fetch entry points of relevant functions in ntdll.dll. */
4454 HMODULE ntdll = GetModuleHandle ("ntdll.dll");
4455 if (!ntdll)
4456 return NULL;
4457 const uint8_t *get_dir = (const uint8_t *)
4458 GetProcAddress (ntdll, "RtlGetCurrentDirectory_U");
4459 const uint8_t *ent_crit = (const uint8_t *)
4460 GetProcAddress (ntdll, "RtlEnterCriticalSection");
4461 if (!get_dir || !ent_crit)
4462 return NULL;
4463 /* Search first relative call instruction in RtlGetCurrentDirectory_U. */
4464 const uint8_t *rcall = (const uint8_t *) memchr (get_dir, 0xe8, 64);
4465 if (!rcall)
4466 return NULL;
4467 /* Fetch offset from instruction and compute address of called function.
4468 This function actually fetches the current FAST_CWD instance and
4469 performs some other actions, not important to us. */
4470 ptrdiff_t offset = (ptrdiff_t) peek32 (rcall + 1);
4471 const uint8_t *use_cwd = rcall + 5 + offset;
4472 /* Find first `push %edi' instruction. */
4473 const uint8_t *pushedi = (const uint8_t *) memchr (use_cwd, 0x57, 32);
4474 if (!pushedi)
4475 return NULL;
4476 /* ...which should be followed by `mov crit-sect-addr,%edi' then
4477 `push %edi', or by just a single `push crit-sect-addr'. */
4478 const uint8_t *movedi = pushedi + 1;
4479 const uint8_t *mov_pfast_cwd;
4480 if (movedi[0] == 0x8b && movedi[1] == 0xff) /* mov %edi,%edi -> W8 */
4481 {
4482 /* Windows 8 does not call RtlEnterCriticalSection. The code manipulates
4483 the FastPebLock manually, probably because RtlEnterCriticalSection has
4484 been converted to an inline function.
4485
4486 Next we search for a `mov some address,%eax'. This address points
4487 to the LockCount member of the FastPebLock structure, so the address
4488 is equal to FastPebLock + 4. */
4489 const uint8_t *moveax = (const uint8_t *) memchr (movedi, 0xb8, 16);
4490 if (!moveax)
4491 return NULL;
4492 offset = (ptrdiff_t) peek32 (moveax + 1) - 4;
4493 /* Compare the address with the known PEB lock as stored in the PEB. */
4494 if ((PRTL_CRITICAL_SECTION) offset != NtCurrentTeb ()->Peb->FastPebLock)
4495 return NULL;
4496 /* Now search for the mov instruction fetching the address of the global
4497 PFAST_CWD *. */
4498 mov_pfast_cwd = moveax;
4499 do
4500 {
4501 mov_pfast_cwd = (const uint8_t *) memchr (++mov_pfast_cwd, 0x8b, 48);
4502 }
4503 while (mov_pfast_cwd && mov_pfast_cwd[1] != 0x1d
4504 && (mov_pfast_cwd - moveax) < 48);
4505 if (!mov_pfast_cwd || mov_pfast_cwd[1] != 0x1d)
4506 return NULL;
4507 }
4508 else
4509 {
4510 if (movedi[0] == 0xbf && movedi[5] == 0x57)
4511 rcall = movedi + 6;
4512 else if (movedi[0] == 0x68)
4513 rcall = movedi + 5;
4514 else if (movedi[0] == 0x88 && movedi[4] == 0x83 && movedi[7] == 0x68)
4515 {
4516 /* Windows 8.1 Preview: The `mov lock_addr,%edi' is actually a
4517 `mov %cl,15(%esp), followed by an `or #-1,%ebx, followed by a
4518 `push lock_addr'. */
4519 movedi += 7;
4520 rcall = movedi + 5;
4521 }
4522 else
4523 return NULL;
4524 /* Compare the address used for the critical section with the known
4525 PEB lock as stored in the PEB. */
4526 if ((PRTL_CRITICAL_SECTION) peek32 (movedi + 1)
4527 != NtCurrentTeb ()->Peb->FastPebLock)
4528 return NULL;
4529 /* To check we are seeing the right code, we check our expectation that
4530 the next instruction is a relative call into RtlEnterCriticalSection. */
4531 if (rcall[0] != 0xe8)
4532 return NULL;
4533 /* Check that this is a relative call to RtlEnterCriticalSection. */
4534 offset = (ptrdiff_t) peek32 (rcall + 1);
4535 if (rcall + 5 + offset != ent_crit)
4536 return NULL;
4537 mov_pfast_cwd = rcall + 5;
4538 }
4539 /* After locking the critical section, the code should read the global
4540 PFAST_CWD * pointer that is guarded by that critical section. */
4541 if (mov_pfast_cwd[0] != 0x8b)
4542 return NULL;
4543 return (fcwd_access_t **) peek32 (mov_pfast_cwd + 2);
4544 }
4545 #endif
4546
4547 static fcwd_access_t **
4548 find_fast_cwd ()
4549 {
4550 /* Fetch the pointer but don't set the global fast_cwd_ptr yet. First
4551 we have to make sure we know the version of the FAST_CWD structure
4552 used on the system. */
4553 fcwd_access_t **f_cwd_ptr = find_fast_cwd_pointer ();
4554 if (!f_cwd_ptr)
4555 {
4556 bool warn = 1;
4557
4558 #ifndef __x86_64__
4559 #ifndef PROCESSOR_ARCHITECTURE_ARM64
4560 #define PROCESSOR_ARCHITECTURE_ARM64 12
4561 #endif
4562
4563 SYSTEM_INFO si;
4564
4565 /* Check if we're running in WOW64 on ARM64. Skip the warning as long as
4566 there's no solution for finding the FAST_CWD pointer on that system.
4567
4568 2018-07-12: Apparently current ARM64 WOW64 has a bug:
4569 It's GetNativeSystemInfo returns PROCESSOR_ARCHITECTURE_INTEL in
4570 wProcessorArchitecture. Since that's an invalid value (a 32 bit
4571 host system hosting a 32 bit emulator for itself?) we can use this
4572 value as an indicator to skip the message as well. */
4573 if (wincap.is_wow64 ())
4574 {
4575 GetNativeSystemInfo (&si);
4576 if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_ARM64
4577 || si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL)
4578 warn = 0;
4579 }
4580 #endif /* !__x86_64__ */
4581
4582 if (warn)
4583 small_printf ("Cygwin WARNING:\n"
4584 " Couldn't compute FAST_CWD pointer. This typically occurs if you're using\n"
4585 " an older Cygwin version on a newer Windows. Please update to the latest\n"
4586 " available Cygwin version from https://cygwin.com/. If the problem persists,\n"
4587 " please see https://cygwin.com/problems.html\n\n");
4588 }
4589 if (f_cwd_ptr && *f_cwd_ptr)
4590 {
4591 /* Just evaluate structure version. */
4592 fcwd_access_t::SetVersionFromPointer ((PBYTE) *f_cwd_ptr, false);
4593 }
4594 else
4595 {
4596 /* If we couldn't fetch fast_cwd_ptr, or if fast_cwd_ptr is NULL(*)
4597 we have to figure out the version from the Buffer pointer in the
4598 ProcessParameters.
4599
4600 (*) This is very unlikely to happen when starting the first
4601 Cygwin process, since it only happens when starting the
4602 process in a directory which can't be used as CWD by Win32, or
4603 if the directory doesn't exist. But *if* it happens, we have
4604 no valid FAST_CWD structure, even though upp_cwd_str.Buffer is
4605 not NULL in that case. So we let the OS create a valid
4606 FAST_CWD structure temporarily to have something to work with.
4607 We know the pipe FS works. */
4608 PEB &peb = *NtCurrentTeb ()->Peb;
4609
4610 if (f_cwd_ptr /* so *f_cwd_ptr == NULL */
4611 && !NT_SUCCESS (RtlSetCurrentDirectory_U (&ro_u_pipedir)))
4612 api_fatal ("Couldn't set directory to %S temporarily.\n"
4613 "Cannot continue.", &ro_u_pipedir);
4614 RtlEnterCriticalSection (peb.FastPebLock);
4615 fcwd_access_t::SetVersionFromPointer
4616 ((PBYTE) peb.ProcessParameters->CurrentDirectoryName.Buffer, true);
4617 RtlLeaveCriticalSection (peb.FastPebLock);
4618 }
4619 /* Eventually, after we set the version as well, set fast_cwd_ptr. */
4620 return f_cwd_ptr;
4621 }
4622
4623 void
4624 cwdstuff::override_win32_cwd (bool init, ULONG old_dismount_count)
4625 {
4626 HANDLE h = NULL;
4627
4628 PEB &peb = *NtCurrentTeb ()->Peb;
4629 UNICODE_STRING &upp_cwd_str = peb.ProcessParameters->CurrentDirectoryName;
4630 HANDLE &upp_cwd_hdl = peb.ProcessParameters->CurrentDirectoryHandle;
4631
4632 if (fast_cwd_ptr == (fcwd_access_t **) -1)
4633 fast_cwd_ptr = find_fast_cwd ();
4634 if (fast_cwd_ptr)
4635 {
4636 /* If we got a valid value for fast_cwd_ptr, we can simply replace
4637 the RtlSetCurrentDirectory_U function entirely. */
4638 PVOID heap = peb.ProcessHeap;
4639 /* First allocate a new fcwd_access_t structure on the heap.
4640 The new fcwd_access_t structure is 4 byte bigger than the old one,
4641 but we simply don't care, so we allocate always room for the
4642 new one. */
4643 fcwd_access_t *f_cwd = (fcwd_access_t *)
4644 RtlAllocateHeap (heap, 0, sizeof (fcwd_access_t));
4645 if (!f_cwd)
4646 {
4647 debug_printf ("RtlAllocateHeap failed");
4648 return;
4649 }
4650 /* Fill in the values. */
4651 f_cwd->FillIn (dir, error ? &ro_u_pipedir : &win32,
4652 old_dismount_count);
4653 /* Use PEB lock when switching fast_cwd_ptr to the new FAST_CWD
4654 structure and writing the CWD to the user process parameter
4655 block. This is equivalent to calling RtlAcquirePebLock/
4656 RtlReleasePebLock, but without having to go through the FS
4657 selector again. */
4658 RtlEnterCriticalSection (peb.FastPebLock);
4659 fcwd_access_t *old_cwd = *fast_cwd_ptr;
4660 *fast_cwd_ptr = f_cwd;
4661 f_cwd->CopyPath (upp_cwd_str);
4662 upp_cwd_hdl = dir;
4663 RtlLeaveCriticalSection (peb.FastPebLock);
4664 if (old_cwd)
4665 old_cwd->Free (heap);
4666 }
4667 else
4668 {
4669 /* Fallback if we failed to find the fast_cwd_ptr value:
4670
4671 - Call RtlSetCurrentDirectory_U.
4672 - Compute new FAST_CWD struct address from buffer pointer in the
4673 user process parameter block.
4674 - Replace the directory handle in the struct with our own handle.
4675 - Close the original handle. RtlSetCurrentDirectory_U already
4676 closed our former dir handle -> no handle leak.
4677
4678 Guard the entire operation with FastPebLock to avoid races
4679 accessing the PEB and FAST_CWD struct.
4680
4681 Unfortunately this method is still prone to a directory usage
4682 race condition:
4683
4684 - The directory is locked against deletion or renaming between the
4685 RtlSetCurrentDirectory_U and the subsequent NtClose call. */
4686 if (unlikely (upp_cwd_hdl == NULL) && init)
4687 return;
4688 RtlEnterCriticalSection (peb.FastPebLock);
4689 if (!init)
4690 {
4691 NTSTATUS status =
4692 RtlSetCurrentDirectory_U (error ? &ro_u_pipedir : &win32);
4693 if (!NT_SUCCESS (status))
4694 {
4695 RtlLeaveCriticalSection (peb.FastPebLock);
4696 debug_printf ("RtlSetCurrentDirectory_U(%S) failed, %y",
4697 error ? &ro_u_pipedir : &win32, status);
4698 return;
4699 }
4700 }
4701 fcwd_access_t::SetDirHandleFromBufferPointer(upp_cwd_str.Buffer, dir);
4702 h = upp_cwd_hdl;
4703 upp_cwd_hdl = dir;
4704 RtlLeaveCriticalSection (peb.FastPebLock);
4705 /* The handle on init is always a fresh one, not the handle inherited
4706 from the parent process. We always have to close it here. */
4707 NtClose (h);
4708 }
4709 }
4710
4711 /* Initialize cygcwd 'muto' for serializing access to cwd info. */
4712 void
4713 cwdstuff::init ()
4714 {
4715 cwd_lock.init ("cwd_lock");
4716
4717 /* Cygwin processes inherit the cwd from their parent. If the win32 path
4718 buffer is not NULL, the cwd struct is already set up, and we only
4719 have to override the Win32 CWD with ours. */
4720 if (win32.Buffer)
4721 override_win32_cwd (true, SharedUserData.DismountCount);
4722 else
4723 {
4724 /* Initialize fast_cwd stuff. */
4725 fast_cwd_ptr = (fcwd_access_t **) -1;
4726 fast_cwd_version = FCWD_W7;
4727 /* Initially re-open the cwd to allow POSIX semantics. */
4728 set (NULL, NULL);
4729 }
4730 }
4731
4732 /* Chdir and fill out the elements of a cwdstuff struct. */
4733 int
4734 cwdstuff::set (path_conv *nat_cwd, const char *posix_cwd)
4735 {
4736 NTSTATUS status;
4737 UNICODE_STRING upath;
4738 PEB &peb = *NtCurrentTeb ()->Peb;
4739 bool virtual_path = false;
4740 bool unc_path = false;
4741 bool inaccessible_path = false;
4742
4743 /* Here are the problems with using SetCurrentDirectory. Just skip this
4744 comment if you don't like whining.
4745
4746 - SetCurrentDirectory only supports paths of up to MAX_PATH - 1 chars,
4747 including a trailing backslash. That's an absolute restriction, even
4748 in the UNICODE API.
4749
4750 - SetCurrentDirectory fails for directories with strict permissions even
4751 for processes with the SE_BACKUP_NAME privilege enabled. The reason
4752 is apparently that SetCurrentDirectory calls NtOpenFile without the
4753 FILE_OPEN_FOR_BACKUP_INTENT flag set.
4754
4755 - SetCurrentDirectory does not support case-sensitivity.
4756
4757 - Unlinking a cwd fails because SetCurrentDirectory seems to open
4758 directories so that deleting the directory is disallowed.
4759
4760 - SetCurrentDirectory can naturally not work on virtual Cygwin paths
4761 like /proc or /cygdrive.
4762
4763 Nevertheless, doing entirely without SetCurrentDirectory is not really
4764 feasible, because it breaks too many mixed applications using the Win32
4765 API.
4766
4767 Therefore we handle the CWD all by ourselves and just keep the Win32
4768 CWD in sync. However, to avoid surprising behaviour in the Win32 API
4769 when we are in a CWD which is inaccessible as Win32 CWD, we set the
4770 Win32 CWD to a "weird" directory in which all relative filesystem-related
4771 calls fail. */
4772
4773 cwd_lock.acquire ();
4774
4775 if (nat_cwd)
4776 {
4777 upath = *nat_cwd->get_nt_native_path ();
4778 if (nat_cwd->isspecial ())
4779 virtual_path = true;
4780 }
4781
4782 /* Memorize old DismountCount before opening the dir. This value is
4783 stored in the FAST_CWD structure. It would be simpler to fetch the
4784 old DismountCount in override_win32_cwd, but Windows also fetches
4785 it before opening the directory handle. It's not quite clear if
4786 that's really required, but since we don't know the side effects of
4787 this action, we better follow Windows' lead. */
4788 ULONG old_dismount_count = SharedUserData.DismountCount;
4789 /* Open a directory handle with FILE_OPEN_FOR_BACKUP_INTENT and with all
4790 sharing flags set. The handle is right now used in exceptions.cc only,
4791 but that might change in future. */
4792 HANDLE h = NULL;
4793 if (!virtual_path)
4794 {
4795 IO_STATUS_BLOCK io;
4796 OBJECT_ATTRIBUTES attr;
4797
4798 if (!nat_cwd)
4799 {
4800 /* On init, just reopen Win32 CWD with desired access flags.
4801 We can access the PEB without lock, because no other thread
4802 can change the CWD. However, there's a chance that the handle
4803 is NULL, even though CurrentDirectoryName isn't so we have to
4804 be careful. */
4805 if (!peb.ProcessParameters->CurrentDirectoryHandle)
4806 {
4807 InitializeObjectAttributes (&attr,
4808 &peb.ProcessParameters->CurrentDirectoryName,
4809 OBJ_CASE_INSENSITIVE | OBJ_INHERIT, NULL, NULL);
4810 }
4811 else
4812 {
4813 RtlInitUnicodeString (&upath, L"");
4814 InitializeObjectAttributes (&attr,
4815 &upath, OBJ_CASE_INSENSITIVE | OBJ_INHERIT,
4816 peb.ProcessParameters->CurrentDirectoryHandle,
4817 NULL);
4818 }
4819 }
4820 else
4821 InitializeObjectAttributes (&attr, &upath,
4822 nat_cwd->objcaseinsensitive () | OBJ_INHERIT,
4823 NULL, NULL);
4824 /* First try without FILE_OPEN_FOR_BACKUP_INTENT, to find out if the
4825 directory is valid for Win32 apps. And, no, we can't just call
4826 SetCurrentDirectory here, since that would potentially break
4827 case-sensitivity. */
4828 status = NtOpenFile (&h, SYNCHRONIZE | FILE_TRAVERSE, &attr, &io,
4829 FILE_SHARE_VALID_FLAGS,
4830 FILE_DIRECTORY_FILE
4831 | FILE_SYNCHRONOUS_IO_NONALERT);
4832 if (status == STATUS_ACCESS_DENIED)
4833 {
4834 status = NtOpenFile (&h, SYNCHRONIZE | FILE_TRAVERSE, &attr, &io,
4835 FILE_SHARE_VALID_FLAGS,
4836 FILE_DIRECTORY_FILE
4837 | FILE_SYNCHRONOUS_IO_NONALERT
4838 | FILE_OPEN_FOR_BACKUP_INTENT);
4839 inaccessible_path = true;
4840 }
4841 if (!NT_SUCCESS (status))
4842 {
4843 /* Called from chdir? Just fail. */
4844 if (nat_cwd)
4845 {
4846 cwd_lock.release ();
4847 __seterrno_from_nt_status (status);
4848 return -1;
4849 }
4850 /* Otherwise we're in init and posix hasn't been set yet. Try to
4851 duplicate the handle instead. If that fails, too, set dir to NULL
4852 and carry on. This will at least set posix to some valid path at
4853 process startup, and subsequent getcwd calls don't EFAULT. */
4854 debug_printf ("WARNING: Can't reopen CWD %y '%S', status %y",
4855 peb.ProcessParameters->CurrentDirectoryHandle,
4856 &peb.ProcessParameters->CurrentDirectoryName,
4857 status);
4858 if (!peb.ProcessParameters->CurrentDirectoryHandle
4859 || !DuplicateHandle (GetCurrentProcess (),
4860 peb.ProcessParameters->CurrentDirectoryHandle,
4861 GetCurrentProcess (), &h, 0, TRUE, 0))
4862 {
4863 cwd_lock.release ();
4864 if (peb.ProcessParameters->CurrentDirectoryHandle)
4865 debug_printf ("...and DuplicateHandle failed with %E.");
4866 dir = NULL;
4867 }
4868 }
4869 }
4870 /* Set new handle. Note that we simply overwrite the old handle here
4871 without closing it. The handle is also used as Win32 CWD handle in
4872 the user parameter block, and it will be closed in override_win32_cwd,
4873 if required. */
4874 dir = h;
4875
4876 if (!nat_cwd)
4877 {
4878 /* On init, just fetch the Win32 dir from the PEB. We can access
4879 the PEB without lock, because no other thread can change the CWD
4880 at that time. */
4881 PUNICODE_STRING pdir = &peb.ProcessParameters->CurrentDirectoryName;
4882 RtlInitEmptyUnicodeString (&win32,
4883 (PWCHAR) crealloc_abort (win32.Buffer,
4884 pdir->Length
4885 + sizeof (WCHAR)),
4886 pdir->Length + sizeof (WCHAR));
4887 RtlCopyUnicodeString (&win32, pdir);
4888
4889 PWSTR eoBuffer = win32.Buffer + (win32.Length / sizeof (WCHAR));
4890 /* Remove trailing slash if one exists. */
4891 if ((eoBuffer - win32.Buffer) > 3 && eoBuffer[-1] == L'\\')
4892 win32.Length -= sizeof (WCHAR);
4893 if (eoBuffer[0] == L'\\')
4894 unc_path = true;
4895
4896 posix_cwd = NULL;
4897 }
4898 else
4899 {
4900 if (!virtual_path) /* don't mangle virtual path. */
4901 {
4902 /* Convert into Win32 path and compute length. */
4903 if (upath.Buffer[1] == L'?')
4904 {
4905 upath.Buffer += 4;
4906 upath.Length -= 4 * sizeof (WCHAR);
4907 if (upath.Buffer[1] != L':')
4908 {
4909 /* UNC path */
4910 upath.Buffer += 2;
4911 upath.Length -= 2 * sizeof (WCHAR);
4912 unc_path = true;
4913 }
4914 }
4915 else
4916 {
4917 /* Path via native NT namespace. Prepend GLOBALROOT prefix
4918 to create a valid Win32 path. */
4919 PWCHAR buf = (PWCHAR) alloca (upath.Length
4920 + ro_u_globalroot.Length
4921 + sizeof (WCHAR));
4922 wcpcpy (wcpcpy (buf, ro_u_globalroot.Buffer), upath.Buffer);
4923 upath.Buffer = buf;
4924 upath.Length += ro_u_globalroot.Length;
4925 }
4926 PWSTR eoBuffer = upath.Buffer + (upath.Length / sizeof (WCHAR));
4927 /* Remove trailing slash if one exists. */
4928 if ((eoBuffer - upath.Buffer) > 3 && eoBuffer[-1] == L'\\')
4929 upath.Length -= sizeof (WCHAR);
4930 }
4931 RtlInitEmptyUnicodeString (&win32,
4932 (PWCHAR) crealloc_abort (win32.Buffer,
4933 upath.Length
4934 + sizeof (WCHAR)),
4935 upath.Length + sizeof (WCHAR));
4936 RtlCopyUnicodeString (&win32, &upath);
4937 if (unc_path)
4938 win32.Buffer[0] = L'\\';
4939 }
4940 /* Make sure it's NUL-terminated. */
4941 win32.Buffer[win32.Length / sizeof (WCHAR)] = L'\0';
4942
4943 /* Set drive_length, used in path conversion, and error code, used in
4944 spawn_guts to decide whether a native Win32 app can be started or not. */
4945 if (virtual_path)
4946 {
4947 drive_length = 0;
4948 error = ENOTDIR;
4949 }
4950 else
4951 {
4952 if (!unc_path)
4953 drive_length = 2;
4954 else
4955 {
4956 PWCHAR ptr = wcschr (win32.Buffer + 2, L'\\');
4957 if (ptr)
4958 ptr = wcschr (ptr + 1, L'\\');
4959 if (ptr)
4960 drive_length = ptr - win32.Buffer;
4961 else
4962 drive_length = win32.Length / sizeof (WCHAR);
4963 }
4964 if (inaccessible_path)
4965 error = EACCES;
4966 else if (win32.Length > (MAX_PATH - 2) * sizeof (WCHAR))
4967 error = ENAMETOOLONG;
4968 else
4969 error = 0;
4970 }
4971 /* Keep the Win32 CWD in sync. Don't check for error, other than for
4972 strace output. Try to keep overhead low. */
4973 override_win32_cwd (!nat_cwd, old_dismount_count);
4974
4975 /* Eventually, create POSIX path if it's not set on entry. */
4976 tmp_pathbuf tp;
4977 if (!posix_cwd)
4978 {
4979 posix_cwd = (const char *) tp.c_get ();
4980 mount_table->conv_to_posix_path (win32.Buffer, (char *) posix_cwd, 0);
4981 }
4982 posix = (char *) crealloc_abort (posix, strlen (posix_cwd) + 1);
4983 stpcpy (posix, posix_cwd);
4984
4985 cwd_lock.release ();
4986 return 0;
4987 }
4988
4989 const char *
4990 cwdstuff::get_error_desc () const
4991 {
4992 switch (cygheap->cwd.get_error ())
4993 {
4994 case EACCES:
4995 return "has restricted permissions which render it\n"
4996 "inaccessible as Win32 working directory";
4997 case ENOTDIR:
4998 return "is a virtual Cygwin directory which does\n"
4999 "not exist for a native Windows application";
5000 case ENAMETOOLONG:
5001 return "has a path longer than allowed for a\n"
5002 "Win32 working directory";
5003 default:
5004 break;
5005 }
5006 /* That shouldn't occur, unless we defined a new error code
5007 in cwdstuff::set. */
5008 return "is not accessible for some unknown reason";
5009 }
5010
5011 /* Store incoming wchar_t path as current posix cwd. This is called from
5012 setlocale so that the cwd is always stored in the right charset. */
5013 void
5014 cwdstuff::reset_posix (wchar_t *w_cwd)
5015 {
5016 size_t len = sys_wcstombs (NULL, (size_t) -1, w_cwd);
5017 posix = (char *) crealloc_abort (posix, len + 1);
5018 sys_wcstombs (posix, len + 1, w_cwd);
5019 }
5020
5021 char *
5022 cwdstuff::get (char *buf, int need_posix, int with_chroot, unsigned ulen)
5023 {
5024 tmp_pathbuf tp;
5025 if (ulen)
5026 /* nothing */;
5027 else if (buf == NULL)
5028 ulen = (unsigned) -1;
5029 else
5030 {
5031 set_errno (EINVAL);
5032 goto out;
5033 }
5034
5035 cwd_lock.acquire ();
5036
5037 char *tocopy;
5038 if (!need_posix)
5039 {
5040 tocopy = tp.c_get ();
5041 sys_wcstombs (tocopy, NT_MAX_PATH, win32.Buffer,
5042 win32.Length / sizeof (WCHAR));
5043 }
5044 else
5045 tocopy = posix;
5046
5047 debug_printf ("posix %s", posix);
5048 if (strlen (tocopy) >= ulen)
5049 {
5050 set_errno (ERANGE);
5051 buf = NULL;
5052 }
5053 else
5054 {
5055 if (!buf)
5056 buf = (char *) malloc (strlen (tocopy) + 1);
5057 strcpy (buf, tocopy);
5058 if (!buf[0]) /* Should only happen when chroot */
5059 strcpy (buf, "/");
5060 }
5061
5062 cwd_lock.release ();
5063
5064 out:
5065 syscall_printf ("(%s) = cwdstuff::get (%p, %u, %d, %d), errno %d",
5066 buf, buf, ulen, need_posix, with_chroot, errno);
5067 return buf;
5068 }
5069
5070 /* No need to be reentrant or thread-safe according to SUSv3.
5071 / and \\ are treated equally. Leading drive specifiers are
5072 kept intact as far as it makes sense. Everything else is
5073 POSIX compatible. */
5074 extern "C" char *
5075 basename (char *path)
5076 {
5077 static char buf[4];
5078 char *c, *d, *bs = path;
5079
5080 if (!path || !*path)
5081 return strcpy (buf, ".");
5082 if (isalpha (path[0]) && path[1] == ':')
5083 bs += 2;
5084 else if (strspn (path, "/\\") > 1)
5085 ++bs;
5086 c = strrchr (bs, '/');
5087 if ((d = strrchr (c ?: bs, '\\')) > c)
5088 c = d;
5089 if (c)
5090 {
5091 /* Trailing (back)slashes are eliminated. */
5092 while (c && c > bs && c[1] == '\0')
5093 {
5094 *c = '\0';
5095 c = strrchr (bs, '/');
5096 if ((d = strrchr (c ?: bs, '\\')) > c)
5097 c = d;
5098 }
5099 if (c && (c > bs || c[1]))
5100 return c + 1;
5101 }
5102 else if (!bs[0])
5103 {
5104 stpncpy (buf, path, bs - path);
5105 stpcpy (buf + (bs - path), ".");
5106 return buf;
5107 }
5108 return path;
5109 }
5110
5111 /* The differences with the POSIX version above:
5112 - declared in <string.h> (instead of <libgen.h>);
5113 - the argument is never modified, and therefore is marked const;
5114 - the empty string is returned if path is an empty string, "/", or ends
5115 with a trailing slash. */
5116 extern "C" char *
5117 __gnu_basename (const char *path)
5118 {
5119 static char buf[1];
5120 char *c, *d, *bs = (char *)path;
5121
5122 if (!path || !*path)
5123 return strcpy (buf, "");
5124 if (isalpha (path[0]) && path[1] == ':')
5125 bs += 2;
5126 else if (strspn (path, "/\\") > 1)
5127 ++bs;
5128 c = strrchr (bs, '/');
5129 if ((d = strrchr (c ?: bs, '\\')) > c)
5130 c = d;
5131 if (c)
5132 return c + 1;
5133 else if (!bs[0])
5134 return strcpy (buf, "");
5135 return (char *)path;
5136 }
5137
5138 /* No need to be reentrant or thread-safe according to SUSv3.
5139 / and \\ are treated equally. Leading drive specifiers and
5140 leading double (back)slashes are kept intact as far as it
5141 makes sense. Everything else is POSIX compatible. */
5142 extern "C" char *
5143 dirname (char *path)
5144 {
5145 static char buf[4];
5146 char *c, *d, *bs = path;
5147
5148 if (!path || !*path)
5149 return strcpy (buf, ".");
5150 if (isalpha (path[0]) && path[1] == ':')
5151 bs += 2;
5152 else if (strspn (path, "/\\") > 1)
5153 ++bs;
5154 c = strrchr (bs, '/');
5155 if ((d = strrchr (c ?: bs, '\\')) > c)
5156 c = d;
5157 if (c)
5158 {
5159 /* Trailing (back)slashes are eliminated. */
5160 while (c && c > bs && c[1] == '\0')
5161 {
5162 *c = '\0';
5163 c = strrchr (bs, '/');
5164 if ((d = strrchr (c ?: bs, '\\')) > c)
5165 c = d;
5166 }
5167 if (!c)
5168 strcpy (bs, ".");
5169 else if (c > bs)
5170 {
5171 /* More trailing (back)slashes are eliminated. */
5172 while (c > bs && (*c == '/' || *c == '\\'))
5173 *c-- = '\0';
5174 }
5175 else
5176 c[1] = '\0';
5177 }
5178 else
5179 {
5180 stpncpy (buf, path, bs - path);
5181 stpcpy (buf + (bs - path), ".");
5182 return buf;
5183 }
5184 return path;
5185 }
This page took 0.902873 seconds and 5 git commands to generate.