Cygwin: path: Convert type of variable 'remlen' to DWORD.
[newlib-cygwin.git] / winsup / cygwin / path.cc
blobe370843eebe32884928c8977c5580c49ede5e96f
1 /* path.cc: path support.
3 This file is part of Cygwin.
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. */
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
14 Pathnames are handled as follows:
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.
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.
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>:
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.
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.
40 The semantics of mounting file systems is not intended to precisely
41 follow normal UNIX systems.
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:\.
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
71 suffix_info stat_suffixes[] =
73 suffix_info ("", 1),
74 suffix_info (".exe", 1),
75 suffix_info (NULL)
78 struct symlink_info
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);
106 muto NO_COPY cwdstuff::cwd_lock;
108 static const GUID GUID_shortcut
109 = { 0x00021401L, 0, 0, {0xc0, 0, 0, 0, 0, 0, 0, 0x46}};
111 enum
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. */
122 struct win_shortcut_hdr
124 DWORD size; /* Header size in bytes. Must contain 0x4c. */
125 GUID magic; /* GUID of shortcut files. */
126 DWORD flags; /* Content flags. See above. */
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. */
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. */
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.
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
157 path_prefix_p (const char *path1, const char *path2, int len1,
158 bool caseinsensitive)
160 /* Handle case where PATH1 has trailing '/' and when it doesn't. */
161 if (len1 > 0 && isdirsep (path1[len1 - 1]))
162 len1--;
164 if (len1 == 0)
165 return isdirsep (path2[0]) && !isdirsep (path2[1]);
167 if (isdirsep (path2[len1]) || path2[len1] == 0 || path1[len1 - 1] == ':')
168 return caseinsensitive ? strncasematch (path1, path2, len1)
169 : !strncmp (path1, path2, len1);
171 return 0;
174 /* Return non-zero if paths match in first len chars.
175 Check is dependent of the case sensitivity setting. */
177 pathnmatch (const char *path1, const char *path2, int len, bool caseinsensitive)
179 return caseinsensitive
180 ? strncasematch (path1, path2, len) : !strncmp (path1, path2, len);
183 /* Return non-zero if paths match. Check is dependent of the case
184 sensitivity setting. */
186 pathmatch (const char *path1, const char *path2, bool caseinsensitive)
188 return caseinsensitive
189 ? strcasematch (path1, path2) : !strcmp (path1, path2);
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)
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');
204 if (last_comp == dir)
205 return false; /* Empty string. Probably shouldn't happen here? */
207 /* Detect run of trailing slashes */
208 while (last_comp > dir && *--last_comp == '/')
209 continue;
211 /* Detect just a run of slashes or a path that does not end with a slash. */
212 if (*last_comp != '.')
213 return false;
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;
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 '..' */
226 /* Repeat previous test for standalone or path component. */
227 return last_comp == dir || last_comp[-1] == '/';
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. */
235 normalize_posix_path (const char *src, char *dst, char *&tail)
237 const char *in_src = src;
238 char *dst_start = dst;
239 bool check_parent = false;
240 syscall_printf ("src %s", src);
242 if ((isdrive (src) && isdirsep (src[2])) || *src == '\\')
243 goto win32_path;
245 tail = dst;
246 if (!isslash (src[0]))
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 == '.')
255 if (tail == dst_start + 1 && *dst_start == '/')
256 tail--;
257 goto sawdot;
259 if (tail > dst && !isslash (tail[-1]))
260 *tail++ = '/';
262 /* Two leading /'s? If so, preserve them. */
263 else if (isslash (src[1]) && !isslash (src[2]))
265 *tail++ = *src++;
266 ++dst_start;
269 while (*src)
271 if (*src == '\\')
272 goto win32_path;
273 /* Strip runs of /'s. */
274 if (!isslash (*src))
275 *tail++ = *src++;
276 else
278 check_parent = true;
279 while (*++src)
281 if (isslash (*src))
282 continue;
284 if (*src != '.')
285 break;
287 sawdot:
288 if (src[1] != '.')
290 if (!src[1])
292 *tail++ = '/';
293 goto done;
295 if (!isslash (src[1]))
296 break;
298 else if (src[2] && !isslash (src[2]))
299 break;
300 else
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)
310 if (tail > dst_start) /* Don't check for / or // dir. */
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.exists ())
327 return ENOENT;
328 if (!head.isdir ())
329 return ENOTDIR;
330 /* At this point, dst is a normalized path. If the
331 normalized path created by path_conv does not
332 match the normalized path we're just testing, then
333 the path in dst contains native symlinks. If we
334 just plunge along, removing the previous path
335 component, we may end up removing a symlink from
336 the path and the resulting path will be invalid.
337 So we replace dst with what we found in head
338 instead. All the work replacing symlinks has been
339 done in that path anyway, so why repeat it? */
340 tail = stpcpy (dst, head.get_posix ());
342 check_parent = false;
344 while (tail > dst_start && !isslash (*--tail))
345 continue;
346 src++;
350 *tail++ = '/';
352 if ((tail - dst) >= NT_MAX_PATH)
354 debug_printf ("ENAMETOOLONG = normalize_posix_path (%s)", src);
355 return ENAMETOOLONG;
359 done:
360 *tail = '\0';
362 debug_printf ("%s = normalize_posix_path (%s)", dst, in_src);
363 return 0;
365 win32_path:
366 int err = normalize_win32_path (in_src, dst, tail);
367 if (!err)
368 for (char *p = dst; (p = strchr (p, '\\')); p++)
369 *p = '/';
370 return err ?: -1;
373 inline void
374 path_conv::add_ext_from_sym (symlink_info &sym)
376 if (sym.ext_here && *sym.ext_here)
378 suffix = path + sym.extn;
379 if (sym.ext_tacked_on)
380 strcpy ((char *) suffix, sym.ext_here);
384 static void __reg2 mkrelpath (char *dst, bool caseinsensitive);
386 static void __reg2
387 mkrelpath (char *path, bool caseinsensitive)
389 tmp_pathbuf tp;
390 char *cwd_win32 = tp.c_get ();
391 if (!cygheap->cwd.get (cwd_win32, 0))
392 return;
394 unsigned cwdlen = strlen (cwd_win32);
395 if (!path_prefix_p (cwd_win32, path, cwdlen, caseinsensitive))
396 return;
398 size_t n = strlen (path);
399 if (n < cwdlen)
400 return;
402 char *tail = path;
403 if (n == cwdlen)
404 tail += cwdlen;
405 else
406 tail += isdirsep (cwd_win32[cwdlen - 1]) ? cwdlen : cwdlen + 1;
408 memmove (path, tail, strlen (tail) + 1);
409 if (!*path)
410 strcpy (path, ".");
413 void
414 path_conv::set_posix (const char *path_copy)
416 if (path_copy)
418 size_t n = strlen (path_copy) + 1;
419 char *p = (char *) crealloc_abort ((void *) posix_path, n);
420 posix_path = (const char *) memcpy (p, path_copy, n);
424 static inline void
425 str2uni_cat (UNICODE_STRING &tgt, const char *srcstr)
427 int len = sys_mbstowcs (tgt.Buffer + tgt.Length / sizeof (WCHAR),
428 (tgt.MaximumLength - tgt.Length) / sizeof (WCHAR),
429 srcstr);
430 if (len)
431 tgt.Length += (len - 1) * sizeof (WCHAR);
434 PUNICODE_STRING
435 get_nt_native_path (const char *path, UNICODE_STRING& upath, bool dos)
437 upath.Length = 0;
438 if (path[0] == '/') /* special path w/o NT path representation. */
439 str2uni_cat (upath, path);
440 else if (path[0] != '\\') /* X:\... or relative path. */
442 if (path[1] == ':') /* X:\... */
444 RtlAppendUnicodeStringToString (&upath, &ro_u_natp);
445 str2uni_cat (upath, path);
446 /* The drive letter must be upper case. */
447 upath.Buffer[4] = towupper (upath.Buffer[4]);
448 transform_chars (&upath, 7);
450 else /* relative path */
452 str2uni_cat (upath, path);
453 transform_chars (&upath, 0);
456 else if (path[1] != '\\') /* \Device\... */
457 str2uni_cat (upath, path);
458 else if ((path[2] != '.' && path[2] != '?')
459 || path[3] != '\\') /* \\server\share\... */
461 RtlAppendUnicodeStringToString (&upath, &ro_u_uncp);
462 str2uni_cat (upath, path + 2);
463 transform_chars (&upath, 8);
465 else /* \\.\device or \\?\foo */
467 RtlAppendUnicodeStringToString (&upath, &ro_u_natp);
468 str2uni_cat (upath, path + 4);
470 if (dos)
472 /* Unfortunately we can't just use transform_chars with the tfx_rev_chars
473 table since only leading and trailing spaces and dots are affected.
474 So we step to every backslash and fix surrounding dots and spaces.
475 That makes these broken filesystems a bit slower, but, hey. */
476 PWCHAR cp = upath.Buffer + 7;
477 PWCHAR cend = upath.Buffer + upath.Length / sizeof (WCHAR);
478 while (++cp < cend)
479 if (*cp == L'\\')
481 PWCHAR ccp = cp - 1;
482 while (*ccp == L'.' || *ccp == L' ')
483 *ccp-- |= 0xf000;
484 while (cp[1] == L' ')
485 *++cp |= 0xf000;
487 while (*--cp == L'.' || *cp == L' ')
488 *cp |= 0xf000;
490 return &upath;
493 /* Handle with extrem care! Only used in a certain instance in try_to_bin.
494 Every other usage needs a careful check. */
495 void
496 path_conv::set_nt_native_path (PUNICODE_STRING new_path)
498 wide_path = (PWCHAR) crealloc_abort (wide_path, new_path->MaximumLength);
499 memcpy (wide_path, new_path->Buffer, new_path->Length);
500 uni_path.Length = new_path->Length;
501 uni_path.MaximumLength = new_path->MaximumLength;
502 uni_path.Buffer = wide_path;
505 /* If suffix is not NULL, append the suffix string verbatim.
506 This is used by fhandler_mqueue::mq_open to append an NTFS stream suffix. */
507 PUNICODE_STRING
508 path_conv::get_nt_native_path (PUNICODE_STRING suffix)
510 PUNICODE_STRING res;
511 if (wide_path)
512 res = &uni_path;
513 else if (!path)
514 res = NULL;
515 else
517 uni_path.Length = 0;
518 uni_path.MaximumLength = (strlen (path) + 10) * sizeof (WCHAR);
519 if (suffix)
520 uni_path.MaximumLength += suffix->Length;
521 wide_path = (PWCHAR) cmalloc_abort (HEAP_STR, uni_path.MaximumLength);
522 uni_path.Buffer = wide_path;
523 ::get_nt_native_path (path, uni_path, has_dos_filenames_only ());
524 if (suffix)
525 RtlAppendUnicodeStringToString (&uni_path, suffix);
526 res = &uni_path;
528 return res;
531 PWCHAR
532 path_conv::get_wide_win32_path (PWCHAR wc)
534 get_nt_native_path ();
535 if (!wide_path)
536 return NULL;
537 wcpcpy (wc, wide_path);
538 if (wc[1] == L'?')
539 wc[1] = L'\\';
540 return wc;
543 static DWORD
544 getfileattr (const char *path, bool caseinsensitive) /* path has to be always absolute. */
546 tmp_pathbuf tp;
547 UNICODE_STRING upath;
548 OBJECT_ATTRIBUTES attr;
549 FILE_BASIC_INFORMATION fbi;
550 NTSTATUS status;
551 IO_STATUS_BLOCK io;
553 tp.u_get (&upath);
554 InitializeObjectAttributes (&attr, &upath,
555 caseinsensitive ? OBJ_CASE_INSENSITIVE : 0,
556 NULL, NULL);
557 get_nt_native_path (path, upath, false);
559 status = NtQueryAttributesFile (&attr, &fbi);
560 if (NT_SUCCESS (status))
561 return fbi.FileAttributes;
563 if (status != STATUS_OBJECT_NAME_NOT_FOUND
564 && status != STATUS_NO_SUCH_FILE) /* File not found on 9x share */
566 /* File exists but access denied. Try to get attribute through
567 directory query. */
568 UNICODE_STRING dirname, basename;
569 HANDLE dir;
570 FILE_BOTH_DIR_INFORMATION fdi;
572 RtlSplitUnicodePath (&upath, &dirname, &basename);
573 InitializeObjectAttributes (&attr, &dirname,
574 caseinsensitive ? OBJ_CASE_INSENSITIVE : 0,
575 NULL, NULL);
576 status = NtOpenFile (&dir, SYNCHRONIZE | FILE_LIST_DIRECTORY,
577 &attr, &io, FILE_SHARE_VALID_FLAGS,
578 FILE_SYNCHRONOUS_IO_NONALERT
579 | FILE_OPEN_FOR_BACKUP_INTENT
580 | FILE_DIRECTORY_FILE);
581 if (NT_SUCCESS (status))
583 status = NtQueryDirectoryFile (dir, NULL, NULL, 0, &io,
584 &fdi, sizeof fdi,
585 FileBothDirectoryInformation,
586 TRUE, &basename, TRUE);
587 NtClose (dir);
588 if (NT_SUCCESS (status) || status == STATUS_BUFFER_OVERFLOW)
589 return fdi.FileAttributes;
592 SetLastError (RtlNtStatusToDosError (status));
593 return INVALID_FILE_ATTRIBUTES;
596 /* Convert an arbitrary path SRC to a pure Win32 path, suitable for
597 passing to Win32 API routines.
599 If an error occurs, `error' is set to the errno value.
600 Otherwise it is set to 0.
602 follow_mode values:
603 SYMLINK_FOLLOW - convert to PATH symlink points to
604 SYMLINK_NOFOLLOW - convert to PATH of symlink itself
605 SYMLINK_IGNORE - do not check PATH for symlinks
606 SYMLINK_CONTENTS - just return symlink contents
609 /* TODO: This implementation is only preliminary. For internal
610 purposes it's necessary to have a path_conv::check function which
611 takes a UNICODE_STRING src path, otherwise we waste a lot of time
612 for converting back and forth. The below implementation does
613 realy nothing but converting to char *, until path_conv handles
614 wide-char paths directly. */
615 void
616 path_conv::check (const UNICODE_STRING *src, unsigned opt,
617 const suffix_info *suffixes)
619 tmp_pathbuf tp;
620 char *path = tp.c_get ();
622 user_shared->warned_msdos = true;
623 sys_wcstombs (path, NT_MAX_PATH, src->Buffer, src->Length / sizeof (WCHAR));
624 path_conv::check (path, opt, suffixes);
627 void
628 path_conv::check (const char *src, unsigned opt,
629 const suffix_info *suffixes)
631 /* The tmp_buf array is used when expanding symlinks. It is NT_MAX_PATH * 2
632 in length so that we can hold the expanded symlink plus a trailer. */
633 tmp_pathbuf tp;
634 char *path_copy = tp.c_get ();
635 char *pathbuf = tp.c_get ();
636 char *tmp_buf = tp.t_get ();
637 char *THIS_path = tp.c_get ();
638 symlink_info sym;
639 bool need_directory = 0;
640 bool add_ext = false;
641 bool is_relpath;
642 char *tail, *path_end;
644 #if 0
645 static path_conv last_path_conv;
646 static char last_src[CYG_MAX_PATH];
648 if (*last_src && strcmp (last_src, src) == 0)
650 *this = last_path_conv;
651 return;
653 #endif
655 __try
657 int loop = 0;
658 mount_flags = 0;
659 path_flags = 0;
660 suffix = NULL;
661 fileattr = INVALID_FILE_ATTRIBUTES;
662 caseinsensitive = OBJ_CASE_INSENSITIVE;
663 if (wide_path)
664 cfree (wide_path);
665 wide_path = NULL;
666 if (path)
668 cfree (modifiable_path ());
669 path = NULL;
671 close_conv_handle ();
672 fs.clear ();
673 if (posix_path)
675 cfree ((void *) posix_path);
676 posix_path = NULL;
678 int component = 0; // Number of translated components
680 if (!(opt & PC_NULLEMPTY))
681 error = 0;
682 else if (!*src)
684 error = ENOENT;
685 return;
688 bool is_msdos = false;
689 /* This loop handles symlink expansion. */
690 for (;;)
692 is_relpath = !isabspath (src);
693 error = normalize_posix_path (src, path_copy, tail);
694 if (error > 0)
695 return;
696 if (error < 0)
698 if (component == 0)
699 is_msdos = true;
700 error = 0;
703 /* Detect if the user was looking for a directory. We have to strip
704 the trailing slash initially while trying to add extensions but
705 take it into account during processing */
706 if (tail > path_copy + 2 && isslash (tail[-1]))
708 need_directory = 1;
709 *--tail = '\0';
711 path_end = tail;
713 /* Scan path_copy from right to left looking either for a symlink
714 or an actual existing file. If an existing file is found, just
715 return. If a symlink is found, exit the for loop.
716 Also: be careful to preserve the errno returned from
717 symlink.check as the caller may need it. */
718 /* FIXME: Do we have to worry about multiple \'s here? */
719 component = 0; // Number of translated components
720 sym.contents[0] = '\0';
722 int symlen = 0;
724 /* Make sure to check certain flags on last component only. */
725 for (unsigned pc_flags = opt & (PC_NO_ACCESS_CHECK | PC_KEEP_HANDLE
726 | PC_SYM_FOLLOW | PC_SYM_NOFOLLOW_REP);
728 pc_flags = opt & (PC_SYM_FOLLOW | PC_SYM_NOFOLLOW_REP))
730 const suffix_info *suff;
731 char *full_path;
733 /* Don't allow symlink.check to set anything in the path_conv
734 class if we're working on an inner component of the path */
735 if (component)
737 suff = NULL;
738 full_path = pathbuf;
740 else
742 suff = suffixes;
743 full_path = THIS_path;
746 retry_fs_via_processfd:
748 /* Convert to native path spec sans symbolic link info. */
749 error = mount_table->conv_to_win32_path (path_copy, full_path,
750 dev, &sym.mount_flags);
752 if (error)
753 return;
755 sym.pc_flags = pc_flags;
757 if (!dev.exists ())
759 error = ENXIO;
760 return;
763 if (iscygdrive_dev (dev))
765 if (!component)
766 fileattr = FILE_ATTRIBUTE_DIRECTORY
767 | FILE_ATTRIBUTE_READONLY;
768 else
770 fileattr = getfileattr (THIS_path,
771 sym.mount_flags & MOUNT_NOPOSIX);
772 dev = FH_FS;
774 goto out;
776 else if (isdev_dev (dev))
778 /* Make sure that the path handling goes on as with FH_FS. */
780 else if (isvirtual_dev (dev))
782 /* FIXME: Calling build_fhandler here is not the right way to
783 handle this. */
784 fhandler_virtual *fh = (fhandler_virtual *)
785 build_fh_dev (dev, path_copy);
786 virtual_ftype_t file_type;
787 if (!fh)
788 file_type = virt_none;
789 else
791 file_type = fh->exists ();
792 if (file_type == virt_symlink
793 || file_type == virt_fdsymlink)
795 fh->fill_filebuf ();
796 symlen = sym.set (fh->get_filebuf ());
798 else if (file_type == virt_fsdir && dev == FH_PROCESSFD)
800 /* FIXME: This is YA bad hack to workaround that
801 we're checking for isvirtual_dev at this point.
802 This should only happen if the file is actually
803 a virtual file, and NOT already if the preceeding
804 path components constitute a virtual file.
806 Anyway, what we do here is this: If the descriptor
807 symlink points to a dir, and if there are trailing
808 path components, it's actually pointing somewhere
809 else. The format_process_fd function returns the
810 full path, resolved symlink plus trailing path
811 components, in its filebuf. This is a POSIX path
812 we know nothing about, so we have to convert it to
813 native again, calling conv_to_win32_path. Since
814 basically nothing happened yet, just copy it over
815 into full_path and jump back to the
816 conv_to_win32_path call. What a mess. */
817 stpcpy (path_copy, fh->get_filebuf ());
818 delete fh;
819 goto retry_fs_via_processfd;
821 else if (file_type == virt_none && dev == FH_PROCESSFD)
823 error = get_errno ();
824 if (error)
826 delete fh;
827 return;
830 delete fh;
832 switch (file_type)
834 case virt_directory:
835 case virt_rootdir:
836 if (component == 0)
837 fileattr = FILE_ATTRIBUTE_DIRECTORY;
838 break;
839 case virt_file:
840 if (component == 0)
841 fileattr = 0;
842 break;
843 case virt_fdsymlink:
844 /* Allow open/linkat to do the right thing. */
845 if (opt & PC_SYM_NOFOLLOW_PROCFD)
847 opt &= ~PC_SYM_FOLLOW;
848 sym.path_flags |= PATH_RESOLVE_PROCFD;
850 fallthrough;
851 case virt_symlink:
852 goto is_virtual_symlink;
853 case virt_pipe:
854 if (component == 0)
856 fileattr = 0;
857 dev.parse (FH_PIPE);
859 break;
860 case virt_socket:
861 if (component == 0)
863 fileattr = 0;
864 dev.parse (FH_SOCKET);
866 break;
867 case virt_fsdir:
868 case virt_fsfile:
869 /* Access to real file or directory via block device
870 entry in /proc/sys. Convert to real file and go with
871 the flow. */
872 dev.parse (FH_FS);
873 goto is_fs_via_procsys;
874 case virt_blk:
875 /* Block special device. Convert to a /dev/sd* like
876 block device unless the trailing slash has been
877 requested. In this case, the target is the root
878 directory of the filesystem on this block device.
879 So we convert this to a real file and attach the
880 backslash. */
881 if (component == 0)
883 fileattr = FILE_ATTRIBUTE_DEVICE;
884 if (!need_directory)
885 /* Use a /dev/sd* device number > /dev/sddx.
886 FIXME: Define a new major DEV_ice number. */
887 dev.parse (DEV_SD_HIGHPART_END, 9999);
888 else
890 dev.parse (FH_FS);
891 strcat (full_path, "\\");
892 fileattr |= FILE_ATTRIBUTE_DIRECTORY;
894 goto out;
896 break;
897 case virt_chr:
898 if (component == 0)
899 fileattr = FILE_ATTRIBUTE_DEVICE;
900 break;
901 default:
902 if (component == 0)
903 fileattr = INVALID_FILE_ATTRIBUTES;
904 goto virtual_component_retry;
906 if (component == 0 || dev != FH_NETDRIVE)
907 mount_flags |= MOUNT_RO;
908 goto out;
910 /* devn should not be a device. If it is, then stop parsing. */
911 else if (dev != FH_FS)
913 fileattr = 0;
914 mount_flags = sym.mount_flags;
915 path_flags = sym.path_flags;
916 if (component)
918 error = ENOTDIR;
919 return;
921 goto out; /* Found a device. Stop parsing. */
924 /* If path is only a drivename, Windows interprets it as the
925 current working directory on this drive instead of the root
926 dir which is what we want. So we need the trailing backslash
927 in this case. */
928 if (full_path[0] && full_path[1] == ':' && full_path[2] == '\0')
930 full_path[2] = '\\';
931 full_path[3] = '\0';
934 /* If the incoming path was given in DOS notation, always treat
935 it as caseinsensitive,noacl path. This must be set before
936 calling sym.check, otherwise the path is potentially treated
937 casesensitive. */
938 if (is_msdos)
939 sym.mount_flags |= MOUNT_NOPOSIX | MOUNT_NOACL;
941 is_fs_via_procsys:
943 symlen = sym.check (full_path, suff, fs, conv_handle);
945 is_virtual_symlink:
947 if (sym.isdevice)
949 if (component)
951 error = ENOTDIR;
952 return;
954 dev.parse (sym.major, sym.minor);
955 dev.setfs (1);
956 dev.mode (sym.mode);
957 fileattr = sym.fileattr;
958 goto out;
961 if (sym.path_flags & PATH_SOCKET)
963 if (component)
965 error = ENOTDIR;
966 return;
968 fileattr = sym.fileattr;
969 #ifdef __WITH_AF_UNIX
970 dev.parse ((sym.path_flags & PATH_REP) ? FH_UNIX : FH_LOCAL);
971 #else
972 dev.parse (FH_LOCAL);
973 #endif /* __WITH_AF_UNIX */
974 dev.setfs (1);
975 mount_flags = sym.mount_flags;
976 path_flags = sym.path_flags;
977 goto out;
980 if (!component)
982 /* Make sure that /dev always exists. */
983 fileattr = isdev_dev (dev) ? FILE_ATTRIBUTE_DIRECTORY
984 : sym.fileattr;
985 mount_flags = sym.mount_flags;
986 path_flags = sym.path_flags;
988 else if (isdev_dev (dev))
990 /* If we're looking for a non-existing file below /dev,
991 make sure that the device type is converted to FH_FS, so
992 that subsequent code handles the file correctly. Unless
993 /dev itself doesn't exist on disk. In that case /dev
994 is handled as virtual filesystem, and virtual filesystems
995 are read-only. The PC_KEEP_HANDLE check allows to check
996 for a call from an informational system call. In that
997 case we just stick to ENOENT, and the device type doesn't
998 matter anyway. */
999 if (sym.error == ENOENT && !(opt & PC_KEEP_HANDLE))
1000 sym.error = EROFS;
1001 else
1002 dev = FH_FS;
1005 /* If symlink.check found an existing non-symlink file, then
1006 it sets the appropriate flag. It also sets any suffix found
1007 into `ext_here'. */
1008 if (!sym.issymlink && sym.fileattr != INVALID_FILE_ATTRIBUTES)
1010 error = sym.error;
1011 if (component == 0)
1012 add_ext = true;
1013 else if (!(sym.fileattr & FILE_ATTRIBUTE_DIRECTORY))
1015 error = ENOTDIR;
1016 goto out;
1018 goto out; // file found
1020 /* Found a symlink if symlen > 0 or short-circuited a native
1021 symlink or junction point if symlen < 0.
1022 If symlen > 0 and component == 0, then the src path itself
1023 was a symlink. If !follow_mode then we're done. Otherwise
1024 we have to insert the path found into the full path that we
1025 are building and perform all of these operations again on the
1026 newly derived path. */
1027 else if (symlen)
1029 /* if symlen is negativ, the actual native symlink or
1030 junction point is an inner path component. Just fix up
1031 symlen to be positive and don't try any PC_SYM_FOLLOW
1032 handling. */
1033 if (symlen < 0)
1034 symlen = -symlen;
1035 else if (component == 0
1036 && (!(opt & PC_SYM_FOLLOW)
1037 || (is_winapi_reparse_point ()
1038 && (opt & PC_SYM_NOFOLLOW_REP))))
1040 /* Usually a trailing slash requires to follow a symlink,
1041 even with PC_SYM_NOFOLLOW. The reason is that "foo/"
1042 is equivalent to "foo/." so the symlink is in fact not
1043 the last path component.
1045 PC_SYM_NOFOLLOW_DIR is used to indicate that the
1046 last path component is the target symlink and the
1047 trailing slash is supposed to be ignored. */
1048 if (!need_directory || (opt & PC_SYM_NOFOLLOW_DIR))
1050 /* last component of path is a symlink. */
1051 set_symlink (symlen);
1052 /* make sure not to set errno to ENOTDIR. */
1053 need_directory = 0;
1054 if (opt & PC_SYM_CONTENTS)
1056 strcpy (THIS_path, sym.contents);
1057 goto out;
1059 add_ext = true;
1060 goto out;
1063 /* Following a symlink we can't trust the collected
1064 filesystem information any longer. */
1065 fs.clear ();
1066 /* Close handle, if we have any. Otherwise we're collecting
1067 handles while following symlinks. */
1068 conv_handle.close ();
1069 break;
1071 else if (sym.error && sym.error != ENOENT)
1073 error = sym.error;
1074 goto out;
1076 /* No existing file found. */
1078 virtual_component_retry:
1079 /* Find the new "tail" of the path, e.g. in '/for/bar/baz',
1080 /baz is the tail. */
1081 if (tail != path_end)
1082 *tail = '/';
1083 while (--tail > path_copy + 1 && *tail != '/') {}
1084 /* Exit loop if there is no tail or we are at the
1085 beginning of a UNC path */
1086 if (tail <= path_copy + 1)
1087 goto out; // all done
1089 /* Haven't found an existing pathname component yet.
1090 Pinch off the tail and try again. */
1091 *tail = '\0';
1092 component++;
1095 /* Arrive here if above loop detected a symlink. */
1096 if (++loop > SYMLOOP_MAX)
1098 error = ELOOP; // Eep.
1099 return;
1102 /* Place the link content, possibly with head and/or tail,
1103 in tmp_buf */
1105 char *headptr;
1106 if (isabspath (sym.contents))
1107 headptr = tmp_buf; /* absolute path */
1108 else
1110 /* Copy the first part of the path (with ending /) and point to
1111 the end. */
1112 char *prevtail = tail;
1113 while (--prevtail > path_copy && *prevtail != '/') {}
1114 int headlen = prevtail - path_copy + 1;;
1115 memcpy (tmp_buf, path_copy, headlen);
1116 headptr = &tmp_buf[headlen];
1119 /* Make sure there is enough space */
1120 if (headptr + symlen >= tmp_buf + (2 * NT_MAX_PATH))
1122 too_long:
1123 error = ENAMETOOLONG;
1124 set_path ("::ENAMETOOLONG::");
1125 return;
1128 /* Copy the symlink contents to the end of tmp_buf.
1129 Convert slashes. */
1130 for (char *p = sym.contents; *p; p++)
1131 *headptr++ = *p == '\\' ? '/' : *p;
1132 *headptr = '\0';
1134 /* Copy any tail component (with the 0) */
1135 if (tail++ < path_end)
1137 /* Add a slash if needed. There is space. */
1138 if (*(headptr - 1) != '/')
1139 *headptr++ = '/';
1140 int taillen = path_end - tail + 1;
1141 if (headptr + taillen > tmp_buf + (2 * NT_MAX_PATH))
1142 goto too_long;
1143 memcpy (headptr, tail, taillen);
1146 /* Evaluate everything all over again. */
1147 src = tmp_buf;
1150 if (!(opt & PC_SYM_CONTENTS))
1151 add_ext = true;
1153 out:
1154 set_path (THIS_path);
1155 if (add_ext)
1156 add_ext_from_sym (sym);
1157 if (dev == FH_NETDRIVE && component)
1159 /* This case indicates a non-existant resp. a non-retrievable
1160 share. This happens for instance if the share is a printer.
1161 In this case the path must not be treated like a FH_NETDRIVE,
1162 but like a FH_FS instead, so the usual open call for files
1163 is used on it. */
1164 dev.parse (FH_FS);
1166 else if (isproc_dev (dev) && fileattr == INVALID_FILE_ATTRIBUTES)
1168 /* FIXME: Usually we don't set error to ENOENT if a file doesn't
1169 exist. This is typically indicated by the fileattr content.
1170 So, why here? The downside is that cygwin_conv_path just gets
1171 an error for these paths so it reports the error back to the
1172 application. Unlike in all other cases of non-existant files,
1173 for which check doesn't set error, so cygwin_conv_path just
1174 returns the path, as intended. */
1175 error = ENOENT;
1176 return;
1178 else if (!need_directory || error)
1179 /* nothing to do */;
1180 else if (fileattr == INVALID_FILE_ATTRIBUTES)
1181 /* Reattach trailing dirsep in native path. */
1182 strcat (modifiable_path (), "\\");
1183 else if (fileattr & FILE_ATTRIBUTE_DIRECTORY)
1184 path_flags &= ~PATH_SYMLINK;
1185 else
1187 debug_printf ("%s is a non-directory", path);
1188 error = ENOTDIR;
1189 return;
1192 /* Restore last path component */
1193 if (tail < path_end && tail > path_copy + 1)
1194 *tail = '/';
1196 if (dev.isfs ())
1198 /* If FS hasn't been checked already in symlink_info::check,
1199 do so now. */
1200 if (fs.inited ()|| fs.update (get_nt_native_path (), NULL))
1202 /* Incoming DOS paths are treated like DOS paths in native
1203 Windows applications. No ACLs, just default settings. */
1204 if (is_msdos)
1205 fs.has_acls (false);
1206 debug_printf ("this->path(%s), has_acls(%d)",
1207 path, fs.has_acls ());
1208 /* CV: We could use this->has_acls() but I want to make sure that
1209 we don't forget that the MOUNT_NOACL flag must be taken into
1210 account here. */
1211 if (!(mount_flags & MOUNT_NOACL) && fs.has_acls ())
1212 set_exec (0); /* We really don't know if this is executable or
1213 not here but set it to not executable since
1214 it will be figured out later by anything
1215 which cares about this. */
1217 /* If the FS has been found to have unreliable inodes, note
1218 that in mount_flags. */
1219 if (!fs.hasgood_inode ())
1220 mount_flags |= MOUNT_IHASH;
1221 /* If the OS is caseinsensitive or the FS is caseinsensitive,
1222 don't handle path casesensitive. */
1223 if (cygwin_shared->obcaseinsensitive || fs.caseinsensitive ())
1224 mount_flags |= MOUNT_NOPOSIX;
1225 caseinsensitive = (mount_flags & MOUNT_NOPOSIX)
1226 ? OBJ_CASE_INSENSITIVE : 0;
1227 if (exec_state () != dont_know_if_executable)
1228 /* ok */;
1229 else if (isdir ())
1230 set_exec (1);
1231 else if (issymlink () || issocket ())
1232 set_exec (0);
1234 /* FIXME: bad hack alert!!! We need a better solution */
1235 if (!strncmp (path_copy, MQ_PATH, MQ_LEN) && path_copy[MQ_LEN])
1236 dev.parse (FH_MQUEUE);
1239 if (opt & PC_NOFULL)
1241 if (is_relpath)
1243 mkrelpath (this->modifiable_path (), !!caseinsensitive);
1244 /* Invalidate wide_path so that wide relpath can be created
1245 in later calls to get_nt_native_path or get_wide_win32_path. */
1246 if (wide_path)
1247 cfree (wide_path);
1248 wide_path = NULL;
1250 if (need_directory)
1252 size_t n = strlen (this->path);
1253 /* Do not add trailing \ to UNC device names like \\.\a: */
1254 if (this->path[n - 1] != '\\' &&
1255 (strncmp (this->path, "\\\\.\\", 4) != 0))
1257 this->modifiable_path ()[n] = '\\';
1258 this->modifiable_path ()[n + 1] = '\0';
1263 if (opt & PC_OPEN)
1264 path_flags |= PATH_OPEN;
1266 if (opt & PC_CTTY)
1267 path_flags |= PATH_CTTY;
1269 if (opt & PC_POSIX)
1270 set_posix (path_copy);
1272 #if 0
1273 if (!error)
1275 last_path_conv = *this;
1276 strcpy (last_src, src);
1278 #endif
1280 __except (NO_ERROR)
1282 error = EFAULT;
1284 __endtry
1287 struct pc_flat
1289 path_conv pc;
1290 HANDLE hdl;
1291 size_t name_len;
1292 size_t posix_len;
1293 char data[0];
1296 void *
1297 path_conv::serialize (HANDLE h, unsigned int &n) const
1299 pc_flat *pcf;
1300 size_t nlen = 0, plen = 0;
1301 char *p;
1303 if (path)
1304 nlen = strlen (path) + 1;
1305 if (posix_path)
1306 plen = strlen (posix_path) + 1;
1307 n = sizeof (pc_flat) + nlen + plen;
1308 pcf = (pc_flat *) cmalloc (HEAP_COMMUNE, n);
1309 if (!pcf)
1311 n = 0;
1312 return NULL;
1314 memcpy ((void *) &pcf->pc, this, sizeof *this);
1315 pcf->hdl = h;
1316 pcf->name_len = nlen;
1317 pcf->posix_len = plen;
1318 p = pcf->data;
1319 if (nlen)
1320 p = stpcpy (p, path) + 1;
1321 if (plen)
1322 stpcpy (p, posix_path);
1323 return pcf;
1326 HANDLE
1327 path_conv::deserialize (void *bufp)
1329 pc_flat *pcf = (pc_flat *) bufp;
1330 char *p;
1331 HANDLE ret;
1333 memcpy ((void *) this, &pcf->pc, sizeof *this);
1334 wide_path = uni_path.Buffer = NULL;
1335 uni_path.MaximumLength = uni_path.Length = 0;
1336 path = posix_path = NULL;
1337 p = pcf->data;
1338 if (pcf->name_len)
1340 set_path (p);
1341 p += pcf->name_len;
1343 if (pcf->posix_len)
1344 set_posix (p);
1345 dev.parse (pcf->pc.dev);
1346 ret = pcf->hdl;
1347 cfree (bufp);
1348 return ret;
1351 path_conv::~path_conv ()
1353 if (posix_path)
1355 cfree ((void *) posix_path);
1356 posix_path = NULL;
1358 if (path)
1360 cfree (modifiable_path ());
1361 path = NULL;
1363 if (wide_path)
1365 cfree (wide_path);
1366 wide_path = NULL;
1368 close_conv_handle ();
1371 bool
1372 path_conv::is_binary ()
1374 tmp_pathbuf tp;
1375 PWCHAR bintest = tp.w_get ();
1376 DWORD bin;
1378 return GetBinaryTypeW (get_wide_win32_path (bintest), &bin)
1379 && (bin == SCS_32BIT_BINARY || bin == SCS_64BIT_BINARY);
1382 /* Helper function to fill the fai datastructure for a file. */
1383 NTSTATUS
1384 file_get_fai (HANDLE h, PFILE_ALL_INFORMATION pfai)
1386 NTSTATUS status;
1387 IO_STATUS_BLOCK io;
1389 /* Some FSes (Netapps) don't implement FileNetworkOpenInformation. */
1390 status = NtQueryInformationFile (h, &io, pfai, sizeof *pfai,
1391 FileAllInformation);
1392 if (likely (status == STATUS_BUFFER_OVERFLOW))
1393 status = STATUS_SUCCESS;
1394 /* Filesystems with broken FileAllInformation exist, too. See the thread
1395 starting with https://cygwin.com/ml/cygwin/2016-07/msg00350.html. */
1396 else if (!NT_SUCCESS (status) && status != STATUS_ACCESS_DENIED)
1398 memset (pfai, 0, sizeof *pfai);
1399 status = NtQueryInformationFile (h, &io, &pfai->BasicInformation,
1400 sizeof pfai->BasicInformation,
1401 FileBasicInformation);
1402 if (NT_SUCCESS (status))
1404 /* The return value of FileInternalInformation is largely ignored.
1405 We only make absolutely sure the inode number is set to 0 in
1406 case it fails. */
1407 status = NtQueryInformationFile (h, &io, &pfai->InternalInformation,
1408 sizeof pfai->InternalInformation,
1409 FileInternalInformation);
1410 if (!NT_SUCCESS (status))
1411 pfai->InternalInformation.IndexNumber.QuadPart = 0LL;
1412 status = NtQueryInformationFile (h, &io, &pfai->StandardInformation,
1413 sizeof pfai->StandardInformation,
1414 FileStandardInformation);
1417 return status;
1420 /* Normalize a Win32 path.
1421 /'s are converted to \'s in the process.
1422 All duplicate \'s, except for 2 leading \'s, are deleted.
1424 The result is 0 for success, or an errno error value.
1425 FIXME: A lot of this should be mergeable with the POSIX critter. */
1427 normalize_win32_path (const char *src, char *dst, char *&tail)
1429 const char *src_start = src;
1430 const char *dst_start = dst;
1431 bool beg_src_slash = isdirsep (src[0]);
1433 tail = dst;
1434 /* Skip Win32 long path name prefix and NT object directory prefix. */
1435 if (beg_src_slash && (src[1] == '?' || isdirsep (src[1]))
1436 && src[2] == '?' && isdirsep (src[3]))
1438 src += 4;
1439 if (isdrive (src) && (isdirsep (src[2]) || !src[2]))
1440 beg_src_slash = false;
1441 else if (!strncmp (src, "UNC", 3) && isdirsep (src[3]))
1442 /* native UNC path */
1443 src += 2; /* Fortunately the first char is not copied... */
1444 else
1445 return EINVAL;
1447 if (beg_src_slash && isdirsep (src[1]))
1449 if (isdirsep (src[2]))
1451 /* More than two slashes are just folded into one. */
1452 src += 2;
1453 while (isdirsep (src[1]))
1454 ++src;
1456 else
1458 /* Two slashes start a network or device path. */
1459 *tail++ = '\\';
1460 src++;
1461 if (src[1] == '.' && isdirsep (src[2]))
1463 *tail++ = '\\';
1464 *tail++ = '.';
1465 src += 2;
1468 dst = tail;
1469 /* If backslash is missing in src, add one. */
1470 if (!isdirsep (src[0]))
1471 *tail++ = '\\';
1473 if (tail == dst_start)
1475 if (isdrive (src))
1477 /* Always convert drive letter to uppercase for case sensitivity. */
1478 *tail++ = cyg_toupper (*src++);
1479 *tail++ = *src++;
1480 dst = tail;
1481 /* If backslash is missing in src, add one. */
1482 if (!isdirsep (src[0]))
1483 *tail++ = '\\';
1485 else if (*src != '/')
1487 /* Make sure dst points to the rightmost backslash which must not
1488 be backtracked over during ".." evaluation. This is either
1489 the backslash after the network path prefix (i.e. "\\") or
1490 the backslash after a drive letter (i.e. C:\"). */
1491 if (beg_src_slash)
1493 tail += cygheap->cwd.get_drive (dst);
1494 /* network path, drive == '\\\\'? Decrement tail to avoid
1495 triple backslash in output. */
1496 if (dst[0] == '\\')
1497 --tail;
1498 dst = tail;
1500 else if (cygheap->cwd.get (dst, 0))
1502 tail = strchr (tail, '\0');
1503 if (tail[-1] != '\\')
1504 *tail++ = '\\';
1505 ++dst;
1506 if (dst[1] == '\\')
1507 ++dst;
1509 else
1510 return get_errno ();
1514 /* At this point dst points to the first backslash, even if it only gets
1515 written in the first iteration of the following loop. Backing up to
1516 handle ".." components can not underrun that border (thus avoiding
1517 subsequent buffer underruns with fatal results). */
1518 while (*src)
1520 /* Strip duplicate /'s. */
1521 if (isdirsep (src[0]) && isdirsep (src[1]))
1522 src++;
1523 /* Ignore "./". */
1524 else if (src[0] == '.' && isdirsep (src[1])
1525 && (src == src_start || isdirsep (src[-1])))
1527 src += 2;
1528 /* Skip /'s to the next path component. */
1529 while (isdirsep (*src))
1530 src++;
1533 /* Backup if "..". */
1534 else if (src[0] == '.' && src[1] == '.' && tail[-1] == '\\')
1536 if (!isdirsep (src[2]) && src[2] != '\0')
1537 *tail++ = *src++;
1538 else
1540 /* Back up over /, but not if it's the first one. */
1541 if (tail > dst + 1)
1542 tail--;
1543 /* Now back up to the next /. */
1544 while (tail > dst + 1 && tail[-1] != '\\' && tail[-2] != ':')
1545 tail--;
1546 src += 2;
1547 /* Skip /'s to the next path component. */
1548 while (isdirsep (*src))
1549 src++;
1552 /* Otherwise, add char to result. */
1553 else
1555 if (*src == '/')
1556 *tail++ = '\\';
1557 else
1558 *tail++ = *src;
1559 src++;
1561 if ((tail - dst) >= NT_MAX_PATH)
1562 return ENAMETOOLONG;
1564 if (tail > dst + 1 && tail[-1] == '.' && tail[-2] == '\\')
1565 tail--;
1566 *tail = '\0';
1567 debug_printf ("%s = normalize_win32_path (%s)", dst_start, src_start);
1568 return 0;
1571 /* Various utilities. */
1573 /* nofinalslash: Remove trailing / and \ from SRC (except for the
1574 first one). It is ok for src == dst. */
1576 void __reg2
1577 nofinalslash (const char *src, char *dst)
1579 int len = strlen (src);
1580 if (src != dst)
1581 memcpy (dst, src, len + 1);
1582 while (len > 1 && isdirsep (dst[--len]))
1583 dst[len] = '\0';
1586 /* conv_path_list: Convert a list of path names to/from Win32/POSIX. */
1588 static int
1589 conv_path_list (const char *src, char *dst, size_t size,
1590 cygwin_conv_path_t what)
1592 tmp_pathbuf tp;
1593 char src_delim, dst_delim;
1594 size_t len;
1595 bool env_cvt = false;
1597 if (what == (cygwin_conv_path_t) ENV_CVT)
1599 what = CCP_WIN_A_TO_POSIX | CCP_RELATIVE;
1600 env_cvt = true;
1602 if ((what & CCP_CONVTYPE_MASK) == CCP_WIN_A_TO_POSIX)
1604 src_delim = ';';
1605 dst_delim = ':';
1607 else
1609 src_delim = ':';
1610 dst_delim = ';';
1613 char *srcbuf;
1614 len = strlen (src) + 1;
1615 if (len <= NT_MAX_PATH * sizeof (WCHAR))
1616 srcbuf = (char *) tp.w_get ();
1617 else
1618 srcbuf = (char *) alloca (len);
1620 int err = 0;
1621 char *d = dst - 1;
1622 bool saw_empty = false;
1625 char *srcpath = srcbuf;
1626 char *s = strccpy (srcpath, &src, src_delim);
1627 size_t len = s - srcpath;
1628 if (len >= NT_MAX_PATH)
1630 err = ENAMETOOLONG;
1631 break;
1633 /* Paths in Win32 path lists in the environment (%Path%), are often
1634 enclosed in quotes (usually paths with spaces). Trailing backslashes
1635 are common, too. Remove them. */
1636 if (env_cvt && len)
1638 if (*srcpath == '"')
1640 ++srcpath;
1641 *--s = '\0';
1642 len -= 2;
1644 while (len && s[-1] == '\\')
1646 *--s = '\0';
1647 --len;
1650 if (len)
1652 ++d;
1653 err = cygwin_conv_path (what, srcpath, d, size - (d - dst));
1655 else if ((what & CCP_CONVTYPE_MASK) == CCP_POSIX_TO_WIN_A)
1657 ++d;
1658 err = cygwin_conv_path (what, ".", d, size - (d - dst));
1660 else
1662 if (env_cvt)
1663 saw_empty = true;
1664 continue;
1666 if (err)
1667 break;
1668 d = strchr (d, '\0');
1669 *d = dst_delim;
1671 while (*src++);
1673 if (saw_empty)
1674 err = EIDRM;
1676 if (d < dst)
1677 d++;
1678 *d = '\0';
1679 return err;
1682 /********************** Symbolic Link Support **************************/
1684 /* Create a symlink from FROMPATH to TOPATH. */
1686 extern "C" int
1687 symlink (const char *oldpath, const char *newpath)
1689 path_conv win32_newpath;
1691 __try
1693 if (!*oldpath || !*newpath)
1695 set_errno (ENOENT);
1696 __leave;
1699 /* Trailing dirsep is a no-no, only errno differs. */
1700 bool has_trailing_dirsep = isdirsep (newpath[strlen (newpath) - 1]);
1701 win32_newpath.check (newpath,
1702 PC_SYM_NOFOLLOW | PC_SYM_NOFOLLOW_DIR | PC_POSIX,
1703 stat_suffixes);
1705 if (win32_newpath.error || has_trailing_dirsep)
1707 set_errno (win32_newpath.error ?:
1708 win32_newpath.exists () ? EEXIST : ENOENT);
1709 __leave;
1712 return symlink_worker (oldpath, win32_newpath, false);
1714 __except (EFAULT) {}
1715 __endtry
1716 return -1;
1719 static int
1720 symlink_nfs (const char *oldpath, path_conv &win32_newpath)
1722 /* On NFS, create symlinks by calling NtCreateFile with an EA of type
1723 NfsSymlinkTargetName containing ... the symlink target name. */
1724 tmp_pathbuf tp;
1725 PFILE_FULL_EA_INFORMATION pffei;
1726 NTSTATUS status;
1727 HANDLE fh;
1728 OBJECT_ATTRIBUTES attr;
1729 IO_STATUS_BLOCK io;
1731 pffei = (PFILE_FULL_EA_INFORMATION) tp.w_get ();
1732 pffei->NextEntryOffset = 0;
1733 pffei->Flags = 0;
1734 pffei->EaNameLength = sizeof (NFS_SYML_TARGET) - 1;
1735 char *EaValue = stpcpy (pffei->EaName, NFS_SYML_TARGET) + 1;
1736 pffei->EaValueLength = sizeof (WCHAR) *
1737 (sys_mbstowcs ((PWCHAR) EaValue, NT_MAX_PATH, oldpath) - 1);
1738 status = NtCreateFile (&fh, FILE_WRITE_DATA | FILE_WRITE_EA | SYNCHRONIZE,
1739 win32_newpath.get_object_attr (attr, sec_none_nih),
1740 &io, NULL, FILE_ATTRIBUTE_SYSTEM,
1741 FILE_SHARE_VALID_FLAGS, FILE_CREATE,
1742 FILE_SYNCHRONOUS_IO_NONALERT
1743 | FILE_OPEN_FOR_BACKUP_INTENT,
1744 pffei, NT_MAX_PATH * sizeof (WCHAR));
1745 if (!NT_SUCCESS (status))
1747 __seterrno_from_nt_status (status);
1748 return -1;
1750 NtClose (fh);
1751 return 0;
1754 /* Count backslashes between s and e. */
1755 static inline int
1756 cnt_bs (PWCHAR s, PWCHAR e)
1758 int num = 0;
1760 while (s < e)
1761 if (*s++ == L'\\')
1762 ++num;
1763 return num;
1766 #ifndef SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
1767 #define SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE 2
1768 #endif
1770 static int
1771 symlink_native (const char *oldpath, path_conv &win32_newpath)
1773 tmp_pathbuf tp;
1774 path_conv win32_oldpath;
1775 PUNICODE_STRING final_oldpath, final_newpath;
1776 UNICODE_STRING final_oldpath_buf;
1777 DWORD flags;
1779 if (isabspath (oldpath))
1781 win32_oldpath.check (oldpath, PC_SYM_NOFOLLOW, stat_suffixes);
1782 final_oldpath = win32_oldpath.get_nt_native_path ();
1784 else
1786 /* The symlink target is relative to the directory in which
1787 the symlink gets created, not relative to the cwd. Therefore
1788 we have to mangle the path quite a bit before calling path_conv. */
1789 ssize_t len = strrchr (win32_newpath.get_posix (), '/')
1790 - win32_newpath.get_posix () + 1;
1791 char *absoldpath = tp.t_get ();
1792 stpcpy (stpncpy (absoldpath, win32_newpath.get_posix (), len),
1793 oldpath);
1794 win32_oldpath.check (absoldpath, PC_SYM_NOFOLLOW, stat_suffixes);
1796 /* Try hard to keep Windows symlink path relative. */
1798 /* 1. Find common path prefix. Skip leading \\?\, but take pre-increment
1799 of the following loop into account. */
1800 PWCHAR c_old = win32_oldpath.get_nt_native_path ()->Buffer + 3;
1801 PWCHAR c_new = win32_newpath.get_nt_native_path ()->Buffer + 3;
1802 /* Windows compatible == always check case insensitive. */
1803 while (towupper (*++c_old) == towupper (*++c_new))
1805 /* The last component could share a common prefix, so make sure we end
1806 up on the first char after the last common backslash. */
1807 while (c_old[-1] != L'\\')
1808 --c_old, --c_new;
1810 /* 2. Check if prefix is long enough. The prefix must at least points to
1811 a complete device: \\?\X:\ or \\?\UNC\server\share\ are the minimum
1812 prefix strings. We start counting behind the \\?\ for speed. */
1813 int num = cnt_bs (win32_oldpath.get_nt_native_path ()->Buffer + 4, c_old);
1814 if (num < 1 /* locale drive. */
1815 || (win32_oldpath.get_nt_native_path ()->Buffer[5] != L':'
1816 && num < 3)) /* UNC path. */
1818 /* 3a. No valid common path prefix: Create absolute symlink. */
1819 final_oldpath = win32_oldpath.get_nt_native_path ();
1821 else
1823 /* 3b. Common path prefx. Count number of additional directories
1824 in symlink's path, and prepend as much ".." path components
1825 to the target path. */
1826 PWCHAR e_new = win32_newpath.get_nt_native_path ()->Buffer
1827 + win32_newpath.get_nt_native_path ()->Length
1828 / sizeof (WCHAR);
1829 num = cnt_bs (c_new, e_new);
1830 final_oldpath = &final_oldpath_buf;
1831 final_oldpath->Buffer = tp.w_get ();
1832 PWCHAR e_old = final_oldpath->Buffer;
1833 while (num-- > 0)
1834 e_old = wcpcpy (e_old, L"..\\");
1835 wcpcpy (e_old, c_old);
1838 /* If the symlink target doesn't exist, don't create native symlink.
1839 Otherwise the directory flag in the symlink is potentially wrong
1840 when the target comes into existence, and native tools will fail.
1841 This is so screwball. This is no problem on AFS, fortunately. */
1842 if (!win32_oldpath.exists () && !win32_oldpath.fs_is_afs ())
1844 SetLastError (ERROR_FILE_NOT_FOUND);
1845 return -1;
1847 /* Don't allow native symlinks to Cygwin special files. However, the
1848 caller shoud know because this case shouldn't be covered by the
1849 default "nativestrict" behaviour, so we use a special return code. */
1850 if (win32_oldpath.isspecial ())
1851 return -2;
1852 /* Convert native paths to Win32 UNC paths. */
1853 final_newpath = win32_newpath.get_nt_native_path ();
1854 final_newpath->Buffer[1] = L'\\';
1855 /* oldpath may be relative. Make sure to convert only absolute paths
1856 to Win32 paths. */
1857 if (final_oldpath->Buffer[0] == L'\\')
1859 /* Starting with Windows 8.1, the ShellExecuteW function does not
1860 handle the long path prefix correctly for symlink targets. Thus,
1861 we create simple short paths < MAX_PATH without long path prefix. */
1862 if (RtlEqualUnicodePathPrefix (final_oldpath, &ro_u_uncp, TRUE)
1863 && final_oldpath->Length < (MAX_PATH + 6) * sizeof (WCHAR))
1865 final_oldpath->Buffer += 6;
1866 final_oldpath->Buffer[0] = L'\\';
1868 else if (final_oldpath->Length < (MAX_PATH + 4) * sizeof (WCHAR))
1869 final_oldpath->Buffer += 4;
1870 else /* Stick to long path, fix native prefix for Win32 API calls. */
1871 final_oldpath->Buffer[1] = L'\\';
1873 /* Try to create native symlink. */
1874 flags = win32_oldpath.isdir () ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0;
1875 if (wincap.has_unprivileged_createsymlink ())
1876 flags |= SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
1877 if (!CreateSymbolicLinkW (final_newpath->Buffer, final_oldpath->Buffer,
1878 flags))
1880 /* Repair native newpath, we still need it. */
1881 final_newpath->Buffer[1] = L'?';
1882 return -1;
1884 return 0;
1887 #ifndef IO_REPARSE_TAG_LX_SYMLINK
1888 #define IO_REPARSE_TAG_LX_SYMLINK (0xa000001d)
1889 #endif
1891 typedef struct _REPARSE_LX_SYMLINK_BUFFER
1893 DWORD ReparseTag;
1894 WORD ReparseDataLength;
1895 WORD Reserved;
1896 struct {
1897 DWORD FileType; /* Take member name with a grain of salt. Value is
1898 apparently always 2 for symlinks. */
1899 char PathBuffer[1];/* POSIX path as given to symlink(2).
1900 Path is not \0 terminated.
1901 Length is ReparseDataLength - sizeof (FileType).
1902 Always UTF-8.
1903 Chars given in incompatible codesets, e. g. umlauts
1904 in ISO-8859-x, are converted to the Unicode
1905 REPLACEMENT CHARACTER 0xfffd == \xef\xbf\bd */
1906 } LxSymlinkReparseBuffer;
1907 } REPARSE_LX_SYMLINK_BUFFER,*PREPARSE_LX_SYMLINK_BUFFER;
1909 static int
1910 symlink_wsl (const char *oldpath, path_conv &win32_newpath)
1912 tmp_pathbuf tp;
1913 PREPARSE_LX_SYMLINK_BUFFER rpl = (PREPARSE_LX_SYMLINK_BUFFER) tp.c_get ();
1914 char *path_buf = rpl->LxSymlinkReparseBuffer.PathBuffer;
1915 const int max_pathlen = MAXIMUM_REPARSE_DATA_BUFFER_SIZE
1916 - offsetof (REPARSE_LX_SYMLINK_BUFFER,
1917 LxSymlinkReparseBuffer.PathBuffer);
1918 PWCHAR utf16 = tp.w_get ();
1919 NTSTATUS status;
1920 IO_STATUS_BLOCK io;
1921 OBJECT_ATTRIBUTES attr;
1922 HANDLE fh;
1923 int len;
1925 rpl->ReparseTag = IO_REPARSE_TAG_LX_SYMLINK;
1926 rpl->Reserved = 0;
1927 rpl->LxSymlinkReparseBuffer.FileType = 2;
1928 /* Convert cygdrive prefix to "/mnt" for WSL compatibility, but only if
1929 cygdrive prefix is not "/", otherwise suffer random "/mnt" symlinks... */
1930 if (mount_table->cygdrive_len > 1
1931 && path_prefix_p (mount_table->cygdrive, oldpath,
1932 mount_table->cygdrive_len, false))
1933 stpcpy (stpcpy (path_buf, "/mnt"),
1934 oldpath + mount_table->cygdrive_len - 1);
1935 else
1936 *stpncpy (path_buf, oldpath, max_pathlen) = '\0';
1937 /* Convert target path to UTF-16 and then back to UTF-8 to make sure the
1938 WSL symlink is in UTF-8, independent of the current Cygwin codeset. */
1939 sys_mbstowcs (utf16, NT_MAX_PATH, path_buf);
1940 len = WideCharToMultiByte (CP_UTF8, 0, utf16, -1, path_buf, max_pathlen,
1941 NULL, NULL);
1942 /* Length is omitting trailing \0. */
1943 rpl->ReparseDataLength = sizeof (DWORD) + len - 1;
1944 /* Create reparse point. */
1945 status = NtCreateFile (&fh, DELETE | FILE_GENERIC_WRITE
1946 | READ_CONTROL | WRITE_DAC,
1947 win32_newpath.get_object_attr (attr, sec_none_nih),
1948 &io, NULL, FILE_ATTRIBUTE_NORMAL,
1949 FILE_SHARE_VALID_FLAGS, FILE_CREATE,
1950 FILE_SYNCHRONOUS_IO_NONALERT
1951 | FILE_NON_DIRECTORY_FILE
1952 | FILE_OPEN_FOR_BACKUP_INTENT
1953 | FILE_OPEN_REPARSE_POINT,
1954 NULL, 0);
1955 if (!NT_SUCCESS (status))
1957 SetLastError (RtlNtStatusToDosError (status));
1958 return -1;
1960 set_created_file_access (fh, win32_newpath, S_IFLNK | STD_RBITS | STD_WBITS);
1961 status = NtFsControlFile (fh, NULL, NULL, NULL, &io, FSCTL_SET_REPARSE_POINT,
1962 (LPVOID) rpl, REPARSE_DATA_BUFFER_HEADER_SIZE
1963 + rpl->ReparseDataLength,
1964 NULL, 0);
1965 if (!NT_SUCCESS (status))
1967 SetLastError (RtlNtStatusToDosError (status));
1968 FILE_DISPOSITION_INFORMATION fdi = { TRUE };
1969 status = NtSetInformationFile (fh, &io, &fdi, sizeof fdi,
1970 FileDispositionInformation);
1971 NtClose (fh);
1972 if (!NT_SUCCESS (status))
1973 debug_printf ("Setting delete dispostion failed, status = %y", status);
1974 return -1;
1976 NtClose (fh);
1977 return 0;
1981 symlink_worker (const char *oldpath, path_conv &win32_newpath, bool isdevice)
1983 int res = -1;
1984 size_t len;
1985 char *buf, *cp;
1986 tmp_pathbuf tp;
1987 winsym_t wsym_type;
1989 /* POSIX says that empty 'newpath' is invalid input while empty
1990 'oldpath' is valid -- it's symlink resolver job to verify if
1991 symlink contents point to existing filesystem object */
1992 __try
1994 if (strlen (oldpath) > SYMLINK_MAX)
1996 set_errno (ENAMETOOLONG);
1997 __leave;
2000 /* Default symlink type is determined by global allow_winsymlinks
2001 variable. Device files are always shortcuts. */
2002 wsym_type = isdevice ? WSYM_lnk : allow_winsymlinks;
2003 /* NFS has its own, dedicated way to create symlinks. */
2004 if (win32_newpath.fs_is_nfs ())
2005 wsym_type = WSYM_nfs;
2006 /* MVFS doesn't handle the SYSTEM DOS attribute, but it handles the R/O
2007 attribute. Therefore we create symlinks on MVFS always as shortcuts. */
2008 else if (win32_newpath.fs_is_mvfs ())
2009 wsym_type = WSYM_lnk;
2010 /* AFS only supports native symlinks. */
2011 else if (win32_newpath.fs_is_afs ())
2012 wsym_type = WSYM_nativestrict;
2013 /* Don't try native symlinks on FSes not supporting reparse points. */
2014 else if ((wsym_type == WSYM_native || wsym_type == WSYM_nativestrict)
2015 && !(win32_newpath.fs_flags () & FILE_SUPPORTS_REPARSE_POINTS))
2016 wsym_type = WSYM_default;
2018 /* Attach .lnk suffix when shortcut is requested. */
2019 if (wsym_type == WSYM_lnk && !win32_newpath.exists ()
2020 && (isdevice || !win32_newpath.fs_is_nfs ()))
2022 char *newplnk = tp.c_get ();
2023 stpcpy (stpcpy (newplnk, win32_newpath.get_posix ()), ".lnk");
2024 win32_newpath.check (newplnk, PC_SYM_NOFOLLOW | PC_POSIX);
2027 if (win32_newpath.error)
2029 set_errno (win32_newpath.error);
2030 __leave;
2033 syscall_printf ("symlink (%s, %S) wsym_type %d", oldpath,
2034 win32_newpath.get_nt_native_path (), wsym_type);
2036 if ((!isdevice && win32_newpath.exists ())
2037 || (win32_newpath.isdevice () && !win32_newpath.is_fs_special ()))
2039 set_errno (EEXIST);
2040 __leave;
2043 /* Handle NFS, native symlinks and WSL symlinks in their own functions. */
2044 switch (wsym_type)
2046 case WSYM_nfs:
2047 res = symlink_nfs (oldpath, win32_newpath);
2048 __leave;
2049 case WSYM_native:
2050 case WSYM_nativestrict:
2051 res = symlink_native (oldpath, win32_newpath);
2052 if (!res)
2053 __leave;
2054 /* Strictly native? Too bad, unless the target is a Cygwin
2055 special file. */
2056 if (res == -1 && wsym_type == WSYM_nativestrict)
2058 __seterrno ();
2059 __leave;
2061 /* Otherwise, fall back to default symlink type. */
2062 wsym_type = WSYM_default;
2063 fallthrough;
2064 case WSYM_default:
2065 if (win32_newpath.fs_flags () & FILE_SUPPORTS_REPARSE_POINTS)
2067 res = symlink_wsl (oldpath, win32_newpath);
2068 if (!res)
2069 __leave;
2071 /* On FSes not supporting reparse points, or in case of an error
2072 creating the WSL symlink, fall back to creating the plain old
2073 SYSTEM file symlink. */
2074 wsym_type = WSYM_sysfile;
2075 break;
2076 default:
2077 break;
2080 if (wsym_type == WSYM_lnk)
2082 path_conv win32_oldpath;
2083 ITEMIDLIST *pidl = NULL;
2084 size_t full_len = 0;
2085 unsigned short oldpath_len, desc_len, relpath_len, pidl_len = 0;
2086 char desc[MAX_PATH + 1], *relpath;
2088 if (!isdevice)
2090 /* First create an IDLIST to learn how big our shortcut is
2091 going to be. */
2092 IShellFolder *psl;
2094 /* The symlink target is relative to the directory in which the
2095 symlink gets created, not relative to the cwd. Therefore we
2096 have to mangle the path quite a bit before calling path_conv.*/
2097 if (isabspath (oldpath))
2098 win32_oldpath.check (oldpath,
2099 PC_SYM_NOFOLLOW,
2100 stat_suffixes);
2101 else
2103 len = strrchr (win32_newpath.get_posix (), '/')
2104 - win32_newpath.get_posix () + 1;
2105 char *absoldpath = tp.t_get ();
2106 stpcpy (stpncpy (absoldpath, win32_newpath.get_posix (),
2107 len),
2108 oldpath);
2109 win32_oldpath.check (absoldpath, PC_SYM_NOFOLLOW,
2110 stat_suffixes);
2112 if (SUCCEEDED (SHGetDesktopFolder (&psl)))
2114 WCHAR wc_path[win32_oldpath.get_wide_win32_path_len () + 1];
2115 win32_oldpath.get_wide_win32_path (wc_path);
2116 /* Amazing but true: Even though the ParseDisplayName method
2117 takes a wide char path name, it does not understand the
2118 Win32 prefix for long pathnames! So we have to tack off
2119 the prefix and convert the path to the "normal" syntax
2120 for ParseDisplayName. */
2121 PWCHAR wc = wc_path + 4;
2122 if (wc[1] != L':') /* native UNC path */
2123 *(wc += 2) = L'\\';
2124 HRESULT res;
2125 if (SUCCEEDED (res = psl->ParseDisplayName (NULL, NULL, wc,
2126 NULL, &pidl,
2127 NULL)))
2129 ITEMIDLIST *p;
2131 for (p = pidl; p->mkid.cb > 0;
2132 p = (ITEMIDLIST *)((char *) p + p->mkid.cb))
2134 pidl_len = (char *) p - (char *) pidl + 2;
2136 psl->Release ();
2139 /* Compute size of shortcut file. */
2140 full_len = sizeof (win_shortcut_hdr);
2141 if (pidl_len)
2142 full_len += sizeof (unsigned short) + pidl_len;
2143 oldpath_len = strlen (oldpath);
2144 /* Unfortunately the length of the description is restricted to a
2145 length of 2000 bytes. We don't want to add considerations for
2146 the different lengths and even 2000 bytes is not enough for long
2147 path names. So what we do here is to set the description to the
2148 POSIX path only if the path is not longer than MAX_PATH characters.
2149 We append the full path name after the regular shortcut data
2150 (see below), which works fine with Windows Explorer as well
2151 as older Cygwin versions (as long as the whole file isn't bigger
2152 than 8K). The description field is only used for backward
2153 compatibility to older Cygwin versions and those versions are
2154 not capable of handling long path names anyway. */
2155 desc_len = stpcpy (desc, oldpath_len > MAX_PATH
2156 ? "[path too long]" : oldpath) - desc;
2157 full_len += sizeof (unsigned short) + desc_len;
2158 /* Devices get the oldpath string unchanged as relative path. */
2159 if (isdevice)
2161 relpath_len = oldpath_len;
2162 stpcpy (relpath = tp.c_get (), oldpath);
2164 else
2166 relpath_len = strlen (win32_oldpath.get_win32 ());
2167 stpcpy (relpath = tp.c_get (), win32_oldpath.get_win32 ());
2169 full_len += sizeof (unsigned short) + relpath_len;
2170 full_len += sizeof (unsigned short) + oldpath_len;
2171 /* 1 byte more for trailing 0 written by stpcpy. */
2172 if (full_len < NT_MAX_PATH * sizeof (WCHAR))
2173 buf = tp.t_get ();
2174 else
2175 buf = (char *) alloca (full_len + 1);
2177 /* Create shortcut header */
2178 win_shortcut_hdr *shortcut_header = (win_shortcut_hdr *) buf;
2179 memset (shortcut_header, 0, sizeof *shortcut_header);
2180 shortcut_header->size = sizeof *shortcut_header;
2181 shortcut_header->magic = GUID_shortcut;
2182 shortcut_header->flags = (WSH_FLAG_DESC | WSH_FLAG_RELPATH);
2183 if (pidl)
2184 shortcut_header->flags |= WSH_FLAG_IDLIST;
2185 shortcut_header->run = SW_NORMAL;
2186 cp = buf + sizeof (win_shortcut_hdr);
2188 /* Create IDLIST */
2189 if (pidl)
2191 *(unsigned short *)cp = pidl_len;
2192 memcpy (cp += 2, pidl, pidl_len);
2193 cp += pidl_len;
2194 CoTaskMemFree (pidl);
2197 /* Create description */
2198 *(unsigned short *)cp = desc_len;
2199 cp = stpcpy (cp += 2, desc);
2201 /* Create relpath */
2202 *(unsigned short *)cp = relpath_len;
2203 cp = stpcpy (cp += 2, relpath);
2205 /* Append the POSIX path after the regular shortcut data for
2206 the long path support. */
2207 unsigned short *plen = (unsigned short *) cp;
2208 cp += 2;
2209 *(PWCHAR) cp = 0xfeff; /* BOM */
2210 cp += 2;
2211 *plen = sys_mbstowcs ((PWCHAR) cp, NT_MAX_PATH, oldpath)
2212 * sizeof (WCHAR);
2213 cp += *plen;
2215 else /* wsym_type == WSYM_sysfile */
2217 /* Default technique creating a symlink. */
2218 buf = tp.t_get ();
2219 cp = stpcpy (buf, SYMLINK_COOKIE);
2220 *(PWCHAR) cp = 0xfeff; /* BOM */
2221 cp += 2;
2222 /* Note that the terminating nul is written. */
2223 cp += sys_mbstowcs ((PWCHAR) cp, NT_MAX_PATH, oldpath)
2224 * sizeof (WCHAR);
2227 OBJECT_ATTRIBUTES attr;
2228 IO_STATUS_BLOCK io;
2229 NTSTATUS status;
2230 ULONG access;
2231 HANDLE fh;
2233 access = DELETE | FILE_GENERIC_WRITE;
2234 if (isdevice && win32_newpath.exists ())
2236 status = NtOpenFile (&fh, FILE_WRITE_ATTRIBUTES,
2237 win32_newpath.get_object_attr (attr,
2238 sec_none_nih),
2239 &io, 0, FILE_OPEN_FOR_BACKUP_INTENT);
2240 if (!NT_SUCCESS (status))
2242 __seterrno_from_nt_status (status);
2243 __leave;
2245 status = NtSetAttributesFile (fh, FILE_ATTRIBUTE_NORMAL);
2246 NtClose (fh);
2247 if (!NT_SUCCESS (status))
2249 __seterrno_from_nt_status (status);
2250 __leave;
2253 else if (!isdevice && win32_newpath.has_acls ()
2254 && !win32_newpath.isremote ())
2255 /* If the filesystem supports ACLs, we will overwrite the DACL after the
2256 call to NtCreateFile. This requires a handle with READ_CONTROL and
2257 WRITE_DAC access, otherwise get_file_sd and set_file_sd both have to
2258 open the file again.
2259 FIXME: On remote NTFS shares open sometimes fails because even the
2260 creator of the file doesn't have the right to change the DACL.
2261 I don't know what setting that is or how to recognize such a share,
2262 so for now we don't request WRITE_DAC on remote drives. */
2263 access |= READ_CONTROL | WRITE_DAC;
2265 status = NtCreateFile (&fh, access,
2266 win32_newpath.get_object_attr (attr, sec_none_nih),
2267 &io, NULL, FILE_ATTRIBUTE_NORMAL,
2268 FILE_SHARE_VALID_FLAGS,
2269 isdevice ? FILE_OVERWRITE_IF : FILE_CREATE,
2270 FILE_SYNCHRONOUS_IO_NONALERT
2271 | FILE_NON_DIRECTORY_FILE
2272 | FILE_OPEN_FOR_BACKUP_INTENT,
2273 NULL, 0);
2274 if (!NT_SUCCESS (status))
2276 __seterrno_from_nt_status (status);
2277 __leave;
2279 if (io.Information == FILE_CREATED && win32_newpath.has_acls ())
2280 set_created_file_access (fh, win32_newpath,
2281 S_IFLNK | STD_RBITS | STD_WBITS);
2282 status = NtWriteFile (fh, NULL, NULL, NULL, &io, buf, cp - buf,
2283 NULL, NULL);
2284 if (NT_SUCCESS (status) && io.Information == (ULONG) (cp - buf))
2286 status = NtSetAttributesFile (fh, wsym_type == WSYM_lnk
2287 ? FILE_ATTRIBUTE_READONLY
2288 : FILE_ATTRIBUTE_SYSTEM);
2289 if (!NT_SUCCESS (status))
2290 debug_printf ("Setting attributes failed, status = %y", status);
2291 res = 0;
2293 else
2295 __seterrno_from_nt_status (status);
2296 FILE_DISPOSITION_INFORMATION fdi = { TRUE };
2297 status = NtSetInformationFile (fh, &io, &fdi, sizeof fdi,
2298 FileDispositionInformation);
2299 if (!NT_SUCCESS (status))
2300 debug_printf ("Setting delete dispostion failed, status = %y",
2301 status);
2303 NtClose (fh);
2306 __except (EFAULT) {}
2307 __endtry
2308 syscall_printf ("%d = symlink_worker(%s, %s, %d)",
2309 res, oldpath, win32_newpath.get_posix (), isdevice);
2310 return res;
2313 static bool
2314 cmp_shortcut_header (win_shortcut_hdr *file_header)
2316 /* A Cygwin or U/Win shortcut only contains a description and a relpath.
2317 Cygwin shortcuts also might contain an ITEMIDLIST. The run type is
2318 always set to SW_NORMAL. */
2319 return file_header->size == sizeof (win_shortcut_hdr)
2320 && !memcmp (&file_header->magic, &GUID_shortcut, sizeof GUID_shortcut)
2321 && (file_header->flags & ~WSH_FLAG_IDLIST)
2322 == (WSH_FLAG_DESC | WSH_FLAG_RELPATH)
2323 && file_header->run == SW_NORMAL;
2327 symlink_info::check_shortcut (HANDLE h)
2329 tmp_pathbuf tp;
2330 win_shortcut_hdr *file_header;
2331 char *buf, *cp;
2332 unsigned short len;
2333 int res = 0;
2334 NTSTATUS status;
2335 IO_STATUS_BLOCK io;
2336 FILE_STANDARD_INFORMATION fsi;
2337 LARGE_INTEGER off = { QuadPart:0LL };
2339 status = NtQueryInformationFile (h, &io, &fsi, sizeof fsi,
2340 FileStandardInformation);
2341 if (!NT_SUCCESS (status))
2343 set_error (EIO);
2344 return 0;
2346 if (fsi.EndOfFile.QuadPart <= (LONGLONG) sizeof (win_shortcut_hdr)
2347 || fsi.EndOfFile.QuadPart > 4 * 65536)
2348 return 0;
2349 if (fsi.EndOfFile.LowPart < NT_MAX_PATH * sizeof (WCHAR))
2350 buf = (char *) tp.w_get ();
2351 else
2352 buf = (char *) alloca (fsi.EndOfFile.LowPart + 1);
2353 status = NtReadFile (h, NULL, NULL, NULL, &io, buf, fsi.EndOfFile.LowPart,
2354 &off, NULL);
2355 if (!NT_SUCCESS (status))
2357 if (status != STATUS_END_OF_FILE)
2358 set_error (EIO);
2359 return 0;
2361 file_header = (win_shortcut_hdr *) buf;
2362 if (io.Information != fsi.EndOfFile.LowPart
2363 || !cmp_shortcut_header (file_header))
2364 return 0;
2365 cp = buf + sizeof (win_shortcut_hdr);
2366 if (file_header->flags & WSH_FLAG_IDLIST) /* Skip ITEMIDLIST */
2367 cp += *(unsigned short *) cp + 2;
2368 if (!(len = *(unsigned short *) cp))
2369 return 0;
2370 cp += 2;
2371 /* Check if this is a device file - these start with the sequence :\\ */
2372 if (strncmp (cp, ":\\", 2) == 0)
2373 res = strlen (strcpy (contents, cp)); /* Don't mess with device files */
2374 else
2376 /* Has appended full path? If so, use it instead of description. */
2377 unsigned short relpath_len = *(unsigned short *) (cp + len);
2378 if (cp + len + 2 + relpath_len < buf + fsi.EndOfFile.LowPart)
2380 cp += len + 2 + relpath_len;
2381 len = *(unsigned short *) cp;
2382 cp += 2;
2384 if (*(PWCHAR) cp == 0xfeff) /* BOM */
2386 char *tmpbuf = tp.c_get ();
2387 if (sys_wcstombs (tmpbuf, NT_MAX_PATH, (PWCHAR) (cp + 2))
2388 > SYMLINK_MAX)
2389 return 0;
2390 res = posixify (tmpbuf);
2392 else if (len > SYMLINK_MAX)
2393 return 0;
2394 else
2396 cp[len] = '\0';
2397 res = posixify (cp);
2400 if (res) /* It's a symlink. */
2401 path_flags |= PATH_SYMLINK | PATH_LNK;
2402 return res;
2406 symlink_info::check_sysfile (HANDLE h)
2408 tmp_pathbuf tp;
2409 char cookie_buf[sizeof (SYMLINK_COOKIE) - 1];
2410 char *srcbuf = tp.c_get ();
2411 int res = 0;
2412 NTSTATUS status;
2413 IO_STATUS_BLOCK io;
2414 bool interix_symlink = false;
2415 LARGE_INTEGER off = { QuadPart:0LL };
2417 status = NtReadFile (h, NULL, NULL, NULL, &io, cookie_buf,
2418 sizeof (cookie_buf), &off, NULL);
2419 if (!NT_SUCCESS (status))
2421 debug_printf ("ReadFile1 failed %y", status);
2422 if (status != STATUS_END_OF_FILE)
2423 set_error (EIO);
2424 return 0;
2426 off.QuadPart = io.Information;
2427 if (io.Information == sizeof (cookie_buf)
2428 && memcmp (cookie_buf, SYMLINK_COOKIE, sizeof (cookie_buf)) == 0)
2430 /* It's a symlink. */
2431 path_flags |= PATH_SYMLINK;
2433 else if (io.Information == sizeof (cookie_buf)
2434 && memcmp (cookie_buf, SOCKET_COOKIE, sizeof (cookie_buf)) == 0)
2435 path_flags |= PATH_SOCKET;
2436 else if (io.Information >= sizeof (INTERIX_SYMLINK_COOKIE)
2437 && memcmp (cookie_buf, INTERIX_SYMLINK_COOKIE,
2438 sizeof (INTERIX_SYMLINK_COOKIE) - 1) == 0)
2440 /* It's an Interix symlink. */
2441 path_flags |= PATH_SYMLINK;
2442 interix_symlink = true;
2443 /* Interix symlink cookies are shorter than Cygwin symlink cookies, so
2444 in case of an Interix symlink cooky we have read too far into the
2445 file. Set file pointer back to the position right after the cookie. */
2446 off.QuadPart = sizeof (INTERIX_SYMLINK_COOKIE) - 1;
2448 if (path_flags & PATH_SYMLINK)
2450 status = NtReadFile (h, NULL, NULL, NULL, &io, srcbuf,
2451 NT_MAX_PATH, &off, NULL);
2452 if (!NT_SUCCESS (status))
2454 debug_printf ("ReadFile2 failed");
2455 if (status != STATUS_END_OF_FILE)
2456 set_error (EIO);
2458 else if (*(PWCHAR) srcbuf == 0xfeff /* BOM */
2459 || interix_symlink)
2461 /* Add trailing 0 to Interix symlink target. Skip BOM in Cygwin
2462 symlinks. */
2463 if (interix_symlink)
2464 ((PWCHAR) srcbuf)[io.Information / sizeof (WCHAR)] = L'\0';
2465 else
2466 srcbuf += 2;
2467 char *tmpbuf = tp.c_get ();
2468 if (sys_wcstombs (tmpbuf, NT_MAX_PATH, (PWCHAR) srcbuf)
2469 > SYMLINK_MAX)
2470 debug_printf ("symlink string too long");
2471 else
2472 res = posixify (tmpbuf);
2474 else if (io.Information > SYMLINK_MAX + 1)
2475 debug_printf ("symlink string too long");
2476 else
2477 res = posixify (srcbuf);
2479 return res;
2482 typedef struct _REPARSE_APPEXECLINK_BUFFER
2484 DWORD ReparseTag;
2485 WORD ReparseDataLength;
2486 WORD Reserved;
2487 struct {
2488 DWORD Version; /* Take member name with a grain of salt. */
2489 WCHAR Strings[1]; /* Four serialized, NUL-terminated WCHAR strings:
2490 - Package ID
2491 - Entry Point
2492 - Executable Path
2493 - Application Type
2494 We're only interested in the Executable Path */
2495 } AppExecLinkReparseBuffer;
2496 } REPARSE_APPEXECLINK_BUFFER,*PREPARSE_APPEXECLINK_BUFFER;
2498 static bool
2499 check_reparse_point_string (PUNICODE_STRING subst)
2501 /* Native mount points, or native non-relative symbolic links,
2502 can be treated as posix symlinks only if the SubstituteName
2503 can be converted from a native NT object namespace name to
2504 a win32 name. We only know how to convert names with two
2505 prefixes :
2506 "\??\UNC\..."
2507 "\??\X:..."
2508 Other reparse points will be treated as files or
2509 directories, not as posix symlinks.
2511 if (RtlEqualUnicodePathPrefix (subst, &ro_u_natp, FALSE))
2513 if (subst->Length >= 6 * sizeof(WCHAR) && subst->Buffer[5] == L':' &&
2514 (subst->Length == 6 * sizeof(WCHAR) || subst->Buffer[6] == L'\\'))
2515 return true;
2516 else if (subst->Length >= 8 * sizeof(WCHAR) &&
2517 wcsncmp (subst->Buffer + 4, L"UNC\\", 4) == 0)
2518 return true;
2520 return false;
2523 /* Return values:
2524 <0: Negative errno.
2525 0: Not a reparse point recognized by us.
2526 >0: Path flags for a recognized reparse point, always including PATH_REP.
2529 check_reparse_point_target (HANDLE h, bool remote, PREPARSE_DATA_BUFFER rp,
2530 PUNICODE_STRING psymbuf)
2532 NTSTATUS status;
2533 IO_STATUS_BLOCK io;
2535 /* On remote drives or under heavy load, NtFsControlFile can return with
2536 STATUS_PENDING. If so, instead of creating an event object, just set
2537 io.Status to an invalid value and perform a minimal wait until io.Status
2538 changed. */
2539 memset (&io, 0xff, sizeof io);
2540 status = NtFsControlFile (h, NULL, NULL, NULL, &io,
2541 FSCTL_GET_REPARSE_POINT, NULL, 0, (LPVOID) rp,
2542 MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
2543 if (status == STATUS_PENDING)
2545 while (io.Status == (NTSTATUS) 0xffffffff)
2546 Sleep (1L);
2547 status = io.Status;
2549 if (!NT_SUCCESS (status))
2551 debug_printf ("NtFsControlFile(FSCTL_GET_REPARSE_POINT) failed, %y",
2552 status);
2553 /* When accessing the root dir of some remote drives (observed with
2554 OS X shares), the FILE_ATTRIBUTE_REPARSE_POINT flag is set, but
2555 the followup call to NtFsControlFile(FSCTL_GET_REPARSE_POINT)
2556 returns with STATUS_NOT_A_REPARSE_POINT. That's quite buggy, but
2557 we cope here with this scenario by not setting an error code. */
2558 if (status == STATUS_NOT_A_REPARSE_POINT)
2559 return 0;
2560 return -EIO;
2562 if (rp->ReparseTag == IO_REPARSE_TAG_SYMLINK)
2564 /* Windows evaluates native symlink literally. If a remote symlink points
2565 to, say, C:\foo, it will be handled as if the target is the local file
2566 C:\foo. That comes in handy since that's how symlinks are treated under
2567 POSIX as well. */
2568 RtlInitCountedUnicodeString (psymbuf,
2569 (PWCHAR)((PBYTE) rp->SymbolicLinkReparseBuffer.PathBuffer
2570 + rp->SymbolicLinkReparseBuffer.SubstituteNameOffset),
2571 rp->SymbolicLinkReparseBuffer.SubstituteNameLength);
2572 if ((rp->SymbolicLinkReparseBuffer.Flags & SYMLINK_FLAG_RELATIVE) ||
2573 check_reparse_point_string (psymbuf))
2574 return PATH_SYMLINK | PATH_REP;
2576 else if (!remote && rp->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
2578 /* Don't handle junctions on remote filesystems as symlinks. This type
2579 of reparse point is handled transparently by the OS so that the
2580 target of the junction is the remote directory it is supposed to
2581 point to. If we handle it as symlink, it will be mistreated as
2582 pointing to a dir on the local system. */
2583 RtlInitCountedUnicodeString (psymbuf,
2584 (PWCHAR)((PBYTE) rp->MountPointReparseBuffer.PathBuffer
2585 + rp->MountPointReparseBuffer.SubstituteNameOffset),
2586 rp->MountPointReparseBuffer.SubstituteNameLength);
2587 if (RtlEqualUnicodePathPrefix (psymbuf, &ro_u_volume, TRUE))
2589 /* Volume mount point. Not treated as symlink. The return
2590 value -EPERM is a hint for the caller to treat this as a
2591 volume mount point. */
2592 return -EPERM;
2594 if (check_reparse_point_string (psymbuf))
2595 return PATH_SYMLINK | PATH_REP;
2597 else if (!remote && rp->ReparseTag == IO_REPARSE_TAG_APPEXECLINK)
2599 /* App execution aliases are commonly used by Windows Store apps. */
2600 PREPARSE_APPEXECLINK_BUFFER rpl = (PREPARSE_APPEXECLINK_BUFFER) rp;
2601 WCHAR *buf = rpl->AppExecLinkReparseBuffer.Strings;
2602 DWORD size = rp->ReparseDataLength / sizeof (WCHAR), n;
2604 /* It seems that app execution aliases have a payload of four
2605 NUL-separated wide string: package id, entry point, executable
2606 and application type. We're interested in the executable. */
2607 for (int i = 0; i < 3 && size > 0; i++)
2609 n = wcsnlen (buf, size - 1);
2610 if (i == 2 && n > 0 && n < size)
2612 RtlInitCountedUnicodeString (psymbuf, buf, n * sizeof (WCHAR));
2613 return PATH_SYMLINK | PATH_REP;
2615 if (i == 2)
2616 break;
2617 buf += n + 1;
2618 size -= n + 1;
2621 else if (rp->ReparseTag == IO_REPARSE_TAG_LX_SYMLINK)
2623 /* WSL symlink. Problem: We have to convert the path to UTF-16 for
2624 the caller. Reparse points are 16K max. The buffer given to rp
2625 is 32K. So there's enough trailing space in the buffer to convert
2626 to UTF-16 and let psymbuf point to it. */
2627 PREPARSE_LX_SYMLINK_BUFFER rpl = (PREPARSE_LX_SYMLINK_BUFFER) rp;
2628 char *path_buf = rpl->LxSymlinkReparseBuffer.PathBuffer;
2629 DWORD path_len = rpl->ReparseDataLength - sizeof (DWORD);
2630 bool full_path = false;
2631 const size_t drv_prefix_len = strlen ("/mnt");
2632 PBYTE utf16_ptr;
2633 PWCHAR utf16_buf;
2634 int utf16_bufsize;
2636 /* 0-terminate path_buf for easier testing. */
2637 path_buf[path_len] = '\0';
2638 if (path_prefix_p ("/mnt", path_buf, drv_prefix_len, false))
2640 size_t len = strlen (path_buf);
2642 if (len <= drv_prefix_len + 1)
2644 /* /mnt or /mnt/. Replace with cygdrive prefix. */
2645 stpcpy (path_buf, mount_table->cygdrive);
2646 path_len = mount_table->cygdrive_len;
2647 if (len == drv_prefix_len)
2649 path_buf[mount_table->cygdrive_len - 1] = '\0';
2650 --path_len;
2652 rp->ReparseDataLength = path_len + sizeof (DWORD);
2654 else if (islower (path_buf[drv_prefix_len + 1])
2655 && (path_len == drv_prefix_len + 2
2656 || path_buf[drv_prefix_len + 2] == '/'))
2658 /* Skip forward to the slash leading the drive letter.
2659 That leaves room for adding the colon. */
2660 path_buf += drv_prefix_len;
2661 path_len -= drv_prefix_len;
2662 full_path = true;
2665 /* Compute buffer for path converted to UTF-16. */
2666 utf16_ptr = (PBYTE) rpl + sizeof (REPARSE_LX_SYMLINK_BUFFER)
2667 + rp->ReparseDataLength;
2668 /* Skip \0-termination added above. */
2669 ++utf16_ptr;
2670 /* Make sure pointer is aligned */
2671 while ((intptr_t) utf16_ptr % sizeof (WCHAR))
2672 ++utf16_ptr;
2673 utf16_buf = (PWCHAR) utf16_ptr;
2674 utf16_bufsize = NT_MAX_PATH - (utf16_buf - (PWCHAR) rpl);
2675 /* Now convert path to UTF-16. */
2676 utf16_bufsize = MultiByteToWideChar (CP_UTF8, 0, path_buf, path_len,
2677 utf16_buf, utf16_bufsize);
2678 if (utf16_bufsize)
2680 if (full_path)
2682 utf16_buf[0] = utf16_buf[1]; /* Move drive letter to front */
2683 utf16_buf[1] = L':'; /* Add colon */
2685 RtlInitCountedUnicodeString (psymbuf, utf16_buf,
2686 utf16_bufsize * sizeof (WCHAR));
2687 return PATH_SYMLINK | PATH_REP | PATH_REP_NOAPI;
2689 return -EIO;
2691 else if (rp->ReparseTag == IO_REPARSE_TAG_CYGUNIX)
2693 PREPARSE_GUID_DATA_BUFFER rgp = (PREPARSE_GUID_DATA_BUFFER) rp;
2695 if (memcmp (CYGWIN_SOCKET_GUID, &rgp->ReparseGuid, sizeof (GUID)) == 0)
2696 #ifdef __WITH_AF_UNIX
2697 return PATH_SOCKET | PATH_REP | PATH_REP_NOAPI;
2698 #else
2699 /* Recognize this as a reparse point but not as a socket. */
2700 return PATH_REP | PATH_REP_NOAPI;
2701 #endif
2703 else if (rp->ReparseTag == IO_REPARSE_TAG_AF_UNIX)
2704 /* Native Windows AF_UNIX socket; recognize this as a reparse
2705 point but not as a socket. */
2706 return PATH_REP;
2707 return 0;
2711 symlink_info::check_reparse_point (HANDLE h, bool remote)
2713 tmp_pathbuf tp;
2714 PREPARSE_DATA_BUFFER rp = (PREPARSE_DATA_BUFFER) tp.c_get ();
2715 UNICODE_STRING symbuf;
2716 char srcbuf[SYMLINK_MAX + 7];
2718 int ret = check_reparse_point_target (h, remote, rp, &symbuf);
2719 if (ret <= 0)
2721 if (ret == -EIO)
2723 set_error (EIO);
2724 return 0;
2726 /* Maybe it's a reparse point, but it's certainly not one we recognize.
2727 Drop REPARSE attribute so we don't try to use the flag accidentally.
2728 It's just some arbitrary file or directory for us. */
2729 fileattr &= ~FILE_ATTRIBUTE_REPARSE_POINT;
2730 return ret;
2732 /* ret is > 0, so it's a known reparse point, path in symbuf. */
2733 path_flags |= ret;
2734 if (ret & PATH_SYMLINK)
2736 sys_wcstombs (srcbuf, SYMLINK_MAX + 7, symbuf.Buffer,
2737 symbuf.Length / sizeof (WCHAR));
2738 /* A symlink is never a directory. */
2739 fileattr &= ~FILE_ATTRIBUTE_DIRECTORY;
2740 return posixify (srcbuf);
2742 else
2743 return 0;
2747 symlink_info::check_nfs_symlink (HANDLE h)
2749 tmp_pathbuf tp;
2750 NTSTATUS status;
2751 IO_STATUS_BLOCK io;
2752 struct {
2753 FILE_GET_EA_INFORMATION fgei;
2754 char buf[sizeof (NFS_SYML_TARGET)];
2755 } fgei_buf;
2756 PFILE_FULL_EA_INFORMATION pffei;
2757 int res = 0;
2759 /* To find out if the file is a symlink and to get the symlink target,
2760 try to fetch the NfsSymlinkTargetName EA. */
2761 fgei_buf.fgei.NextEntryOffset = 0;
2762 fgei_buf.fgei.EaNameLength = sizeof (NFS_SYML_TARGET) - 1;
2763 stpcpy (fgei_buf.fgei.EaName, NFS_SYML_TARGET);
2764 pffei = (PFILE_FULL_EA_INFORMATION) tp.w_get ();
2765 status = NtQueryEaFile (h, &io, pffei, NT_MAX_PATH * sizeof (WCHAR), TRUE,
2766 &fgei_buf.fgei, sizeof fgei_buf, NULL, TRUE);
2767 if (NT_SUCCESS (status) && pffei->EaValueLength > 0)
2769 PWCHAR spath = (PWCHAR)
2770 (pffei->EaName + pffei->EaNameLength + 1);
2771 res = sys_wcstombs (contents, SYMLINK_MAX + 1,
2772 spath, pffei->EaValueLength);
2773 path_flags |= PATH_SYMLINK;
2775 return res;
2779 symlink_info::posixify (char *srcbuf)
2781 /* The definition for a path in a native symlink is a bit weird. The Flags
2782 value seem to contain 0 for absolute paths (stored as NT native path)
2783 and 1 for relative paths. Relative paths are paths not starting with a
2784 drive letter. These are not converted to NT native, but stored as
2785 given. A path starting with a single backslash is relative to the
2786 current drive thus a "relative" value (Flags == 1).
2787 Funny enough it's possible to store paths with slashes instead of
2788 backslashes, but they are evaluated incorrectly by subsequent Windows
2789 calls like CreateFile (ERROR_INVALID_NAME). So, what we do here is to
2790 take paths starting with slashes at face value, evaluating them as
2791 Cygwin specific POSIX paths.
2792 A path starting with two slashes(!) or backslashes is converted into an
2793 NT UNC path. Unfortunately, in contrast to POSIX rules, paths starting
2794 with three or more (back)slashes are also converted into UNC paths,
2795 just incorrectly sticking to one redundant leading backslash. We go
2796 along with this behaviour to avoid scenarios in which native tools access
2797 other files than Cygwin.
2798 The above rules are used exactly the same way on Cygwin specific symlinks
2799 (sysfiles and shortcuts) to eliminate non-POSIX paths in the output. */
2801 /* Eliminate native NT prefixes. */
2802 if (srcbuf[0] == '\\' && !strncmp (srcbuf + 1, "??\\", 3))
2804 srcbuf += 4;
2805 if (srcbuf[1] != ':') /* native UNC path */
2806 *(srcbuf += 2) = '\\';
2808 if (isdrive (srcbuf))
2809 mount_table->conv_to_posix_path (srcbuf, contents, 0);
2810 else if (srcbuf[0] == '\\')
2812 if (srcbuf[1] == '\\') /* UNC path */
2813 slashify (srcbuf, contents, 0);
2814 else /* Paths starting with \ are current drive relative. */
2816 char cvtbuf[SYMLINK_MAX + 1];
2818 stpcpy (cvtbuf + cygheap->cwd.get_drive (cvtbuf), srcbuf);
2819 mount_table->conv_to_posix_path (cvtbuf, contents, 0);
2822 else /* Everything else is taken as is. */
2823 slashify (srcbuf, contents, 0);
2824 return strlen (contents);
2827 enum
2829 SCAN_BEG,
2830 SCAN_LNK,
2831 SCAN_HASLNK,
2832 SCAN_JUSTCHECK,
2833 SCAN_JUSTCHECKTHIS, /* Never try to append a suffix. */
2834 SCAN_APPENDLNK,
2835 SCAN_EXTRALNK,
2836 SCAN_DONE,
2839 class suffix_scan
2841 const suffix_info *suffixes, *suffixes_start;
2842 int nextstate;
2843 char *eopath;
2844 size_t namelen;
2845 public:
2846 const char *path;
2847 char *has (const char *, const suffix_info *);
2848 int next ();
2849 int lnk_match () {return nextstate >= SCAN_APPENDLNK;}
2850 size_t name_len () {return namelen;}
2853 char *
2854 suffix_scan::has (const char *in_path, const suffix_info *in_suffixes)
2856 nextstate = SCAN_BEG;
2857 suffixes = suffixes_start = in_suffixes;
2859 const char *fname = strrchr (in_path, '\\');
2860 fname = fname ? fname + 1 : in_path;
2861 char *ext_here = strrchr (fname, '.');
2862 path = in_path;
2863 eopath = strchr (path, '\0');
2865 if (!ext_here)
2866 goto noext;
2868 if (suffixes)
2870 /* Check if the extension matches a known extension */
2871 for (const suffix_info *ex = in_suffixes; ex->name != NULL; ex++)
2872 if (ascii_strcasematch (ext_here, ex->name))
2874 nextstate = SCAN_JUSTCHECK;
2875 suffixes = NULL; /* Has an extension so don't scan for one. */
2876 goto done;
2880 /* Didn't match. Use last resort -- .lnk. */
2881 if (ascii_strcasematch (ext_here, ".lnk"))
2883 nextstate = SCAN_HASLNK;
2884 suffixes = NULL;
2887 noext:
2888 ext_here = eopath;
2890 done:
2891 namelen = eopath - fname;
2892 /* Avoid attaching suffixes if the resulting filename would be invalid.
2893 For performance reasons we don't check the length of a suffix, since
2894 we know that all suffixes are 4 chars in length.
2896 FIXME: This is not really correct. A fully functional test should
2897 work on wide character paths. This would probably also speed
2898 up symlink_info::check. */
2899 if (namelen > NAME_MAX - 4)
2901 nextstate = SCAN_JUSTCHECKTHIS;
2902 suffixes = NULL;
2904 return ext_here;
2908 suffix_scan::next ()
2910 for (;;)
2912 if (!suffixes)
2913 switch (nextstate)
2915 case SCAN_BEG:
2916 suffixes = suffixes_start;
2917 if (!suffixes)
2919 nextstate = SCAN_LNK;
2920 return 1;
2922 nextstate = SCAN_EXTRALNK;
2923 /* fall through to suffix checking below */
2924 break;
2925 case SCAN_HASLNK:
2926 nextstate = SCAN_APPENDLNK; /* Skip SCAN_BEG */
2927 return 1;
2928 case SCAN_EXTRALNK:
2929 nextstate = SCAN_DONE;
2930 *eopath = '\0';
2931 return 0;
2932 case SCAN_JUSTCHECK:
2933 nextstate = SCAN_LNK;
2934 return 1;
2935 case SCAN_JUSTCHECKTHIS:
2936 nextstate = SCAN_DONE;
2937 return 1;
2938 case SCAN_LNK:
2939 case SCAN_APPENDLNK:
2940 nextstate = SCAN_DONE;
2941 if (namelen + (*eopath ? 8 : 4) > NAME_MAX)
2943 *eopath = '\0';
2944 return 0;
2946 strcat (eopath, ".lnk");
2947 return 1;
2948 default:
2949 *eopath = '\0';
2950 return 0;
2953 while (suffixes && suffixes->name)
2954 if (nextstate == SCAN_EXTRALNK
2955 && (!suffixes->addon || namelen > NAME_MAX - 8))
2956 suffixes++;
2957 else
2959 strcpy (eopath, suffixes->name);
2960 if (nextstate == SCAN_EXTRALNK)
2961 strcat (eopath, ".lnk");
2962 suffixes++;
2963 return 1;
2965 suffixes = NULL;
2969 bool
2970 symlink_info::set_error (int in_errno)
2972 bool res;
2973 if (!(pc_flags & PC_NO_ACCESS_CHECK)
2974 || in_errno == ENAMETOOLONG || in_errno == EIO)
2976 error = in_errno;
2977 res = true;
2979 else if (in_errno == ENOENT)
2980 res = true;
2981 else
2983 fileattr = FILE_ATTRIBUTE_NORMAL;
2984 res = false;
2986 return res;
2989 bool
2990 symlink_info::parse_device (const char *contents)
2992 char *endptr;
2993 _major_t mymajor;
2994 _major_t myminor;
2995 __mode_t mymode;
2997 mymajor = strtol (contents += 2, &endptr, 16);
2998 if (endptr == contents)
2999 return isdevice = false;
3001 contents = endptr;
3002 myminor = strtol (++contents, &endptr, 16);
3003 if (endptr == contents)
3004 return isdevice = false;
3006 contents = endptr;
3007 mymode = strtol (++contents, &endptr, 16);
3008 if (endptr == contents)
3009 return isdevice = false;
3011 if ((mymode & S_IFMT) == S_IFIFO)
3013 mymajor = _major (FH_FIFO);
3014 myminor = _minor (FH_FIFO);
3017 major = mymajor;
3018 minor = myminor;
3019 mode = mymode;
3020 return isdevice = true;
3023 /* Check if PATH is a symlink. PATH must be a valid Win32 path name.
3025 If PATH is a symlink, put the value of the symlink--the file to
3026 which it points--into BUF. The value stored in BUF is not
3027 necessarily null terminated. BUFLEN is the length of BUF; only up
3028 to BUFLEN characters will be stored in BUF. BUF may be NULL, in
3029 which case nothing will be stored.
3031 Set *SYML if PATH is a symlink.
3033 Set *EXEC if PATH appears to be executable. This is an efficiency
3034 hack because we sometimes have to open the file anyhow. *EXEC will
3035 not be set for every executable file.
3037 Return -1 on error, 0 if PATH is not a symlink, or the length
3038 stored into BUF if PATH is a symlink. */
3041 symlink_info::check (char *path, const suffix_info *suffixes, fs_info &fs,
3042 path_conv_handle &conv_hdl)
3044 int res;
3045 HANDLE h;
3046 NTSTATUS status;
3047 UNICODE_STRING upath;
3048 OBJECT_ATTRIBUTES attr;
3049 IO_STATUS_BLOCK io;
3050 suffix_scan suffix;
3052 const ULONG ci_flag = cygwin_shared->obcaseinsensitive
3053 || (mount_flags & MOUNT_NOPOSIX)
3054 ? OBJ_CASE_INSENSITIVE : 0;
3055 /* TODO: Temporarily do all char->UNICODE conversion here. This should
3056 already be slightly faster than using Ascii functions. */
3057 tmp_pathbuf tp;
3058 tp.u_get (&upath);
3059 InitializeObjectAttributes (&attr, &upath, ci_flag, NULL, NULL);
3061 /* This label is used in case we encounter a FS which only handles
3062 DOS paths. See below. */
3063 bool restarted = false;
3064 restart:
3066 h = NULL;
3067 res = 0;
3068 contents[0] = '\0';
3069 issymlink = true;
3070 isdevice = false;
3071 major = 0;
3072 minor = 0;
3073 mode = 0;
3074 // mount_flags is an incoming value set in path_conv */
3075 path_flags = 0;
3077 PVOID eabuf = &nfs_aol_ffei;
3078 ULONG easize = sizeof nfs_aol_ffei;
3080 ext_here = suffix.has (path, suffixes);
3081 extn = ext_here - path;
3082 bool had_ext = !!*ext_here;
3084 /* If the filename is too long, don't even try. */
3085 if (suffix.name_len () > NAME_MAX)
3087 set_error (ENAMETOOLONG);
3088 goto file_not_symlink;
3091 while (suffix.next ())
3093 error = 0;
3094 get_nt_native_path (suffix.path, upath, mount_flags & MOUNT_DOS);
3095 if (h)
3097 NtClose (h);
3098 h = NULL;
3100 /* The EA given to NtCreateFile allows to get a handle to a symlink on
3101 an NFS share, rather than getting a handle to the target of the
3102 symlink (which would spoil the task of this method quite a bit).
3103 Fortunately it's ignored on most other file systems so we don't have
3104 to special case NFS too much. */
3105 status = NtCreateFile (&h,
3106 READ_CONTROL | FILE_READ_ATTRIBUTES | FILE_READ_EA,
3107 &attr, &io, NULL, 0, FILE_SHARE_VALID_FLAGS,
3108 FILE_OPEN,
3109 FILE_OPEN_REPARSE_POINT
3110 | FILE_OPEN_FOR_BACKUP_INTENT,
3111 eabuf, easize);
3112 debug_printf ("%y = NtCreateFile (%S)", status, &upath);
3113 /* No right to access EAs or EAs not supported? */
3114 if (!NT_SUCCESS (status)
3115 && (status == STATUS_ACCESS_DENIED
3116 || status == STATUS_EAS_NOT_SUPPORTED
3117 || status == STATUS_NOT_SUPPORTED
3118 || status == STATUS_INVALID_NETWORK_RESPONSE
3119 /* Or a bug in Samba 3.2.x (x <= 7) when accessing a share's
3120 root dir which has EAs enabled? */
3121 || status == STATUS_INVALID_PARAMETER))
3123 /* If EAs are not supported, there's no sense to check them again
3124 with suffixes attached. So we set eabuf/easize to 0 here once. */
3125 if (status == STATUS_EAS_NOT_SUPPORTED
3126 || status == STATUS_NOT_SUPPORTED)
3128 eabuf = NULL;
3129 easize = 0;
3131 status = NtOpenFile (&h, READ_CONTROL | FILE_READ_ATTRIBUTES,
3132 &attr, &io, FILE_SHARE_VALID_FLAGS,
3133 FILE_OPEN_REPARSE_POINT
3134 | FILE_OPEN_FOR_BACKUP_INTENT);
3135 debug_printf ("%y = NtOpenFile (no-EAs %S)", status, &upath);
3137 if (status == STATUS_OBJECT_NAME_NOT_FOUND)
3139 /* There are filesystems out in the wild (Netapp, NWFS, and others)
3140 which are uncapable of generating pathnames outside the Win32
3141 rules. That means, filenames on these FSes must not have a
3142 leading space or trailing dots and spaces. This code snippet
3143 manages them. I really hope it's streamlined enough not to
3144 slow down normal operation. This extra check only kicks in if
3145 we encountered a STATUS_OBJECT_NAME_NOT_FOUND *and* we didn't
3146 already attach a suffix. */
3147 if (!restarted && !*ext_here && ext_here[-1] != '\\'
3148 && !(mount_flags & MOUNT_DOS))
3150 /* Check for trailing dot or space or leading space in
3151 last component. */
3152 char *p = ext_here - 1;
3153 if (*p != '.' && *p != ' ')
3155 while (*--p != '\\')
3156 assert(p >= path);
3157 if (*++p != ' ')
3158 p = NULL;
3160 if (p)
3162 /* If so, check if file resides on one of the known broken
3163 FSes only supporting filenames following DOS rules. */
3164 fs.update (&upath, NULL);
3165 if (fs.has_dos_filenames_only ())
3167 /* If so, try again. Since we now know the FS, the
3168 filenames will be tweaked to follow DOS rules via the
3169 third parameter in the call to get_nt_native_path. */
3170 mount_flags |= MOUNT_DOS;
3171 restarted = true;
3172 goto restart;
3177 else if (status == STATUS_NETWORK_OPEN_RESTRICTION
3178 || status == STATUS_SYMLINK_CLASS_DISABLED)
3180 /* These status codes are returned if you try to open a native
3181 symlink and the usage of this kind of symlink is forbidden
3182 (see fsutil). Since we can't open them at all, not even for
3183 stat purposes, we have to return a POSIX error code which is
3184 at least a bit helpful.
3186 Additionally Windows 8 introduces a bug in NFS: If you have
3187 a symlink to a directory, with symlinks underneath, resolving
3188 the second level of symlinks fails if remote->remote symlinks
3189 are disabled in fsutil. Unfortunately that's the default. */
3190 set_error (ELOOP);
3191 break;
3194 if (NT_SUCCESS (status)
3195 /* Check file system while we're having the file open anyway.
3196 This speeds up path_conv noticably (~10%). */
3197 && (fs.inited () || fs.update (&upath, h)))
3199 status = conv_hdl.get_finfo (h, fs.is_nfs ());
3200 if (NT_SUCCESS (status))
3201 fileattr = conv_hdl.get_dosattr (fs.is_nfs ());
3203 if (!NT_SUCCESS (status))
3205 debug_printf ("%y = NtQueryInformationFile (%S)", status, &upath);
3206 fileattr = INVALID_FILE_ATTRIBUTES;
3208 /* One of the inner path components is invalid, or the path contains
3209 invalid characters. Bail out with ENOENT.
3211 STATUS_IO_REPARSE_TAG_NOT_HANDLED is returned when trying to
3212 traversing a WSL symlink. For all practical purposes it's
3213 equivalent to traversing SYSTEM- or LNK-type symlink returning
3214 STATUS_OBJECT_PATH_NOT_FOUND.
3216 Note that additional STATUS_OBJECT_PATH_INVALID and
3217 STATUS_OBJECT_PATH_SYNTAX_BAD status codes exist. The first one
3218 is seemingly not generated by NtQueryInformationFile, the latter
3219 is only generated if the path is no absolute path within the
3220 NT name space, which should not happen and would point to an
3221 error in get_nt_native_path. Both status codes are deliberately
3222 not tested here unless proved necessary. */
3223 if (status == STATUS_OBJECT_PATH_NOT_FOUND
3224 || status == STATUS_IO_REPARSE_TAG_NOT_HANDLED
3225 || status == STATUS_OBJECT_NAME_INVALID
3226 || status == STATUS_BAD_NETWORK_PATH
3227 || status == STATUS_BAD_NETWORK_NAME
3228 || status == STATUS_NO_MEDIA_IN_DEVICE)
3230 set_error (ENOENT);
3231 if (ext_tacked_on && !had_ext)
3233 *ext_here = '\0';
3234 ext_tacked_on = false;
3235 ext_here = NULL;
3236 extn = 0;
3238 goto file_not_symlink;
3240 if (status != STATUS_OBJECT_NAME_NOT_FOUND
3241 && status != STATUS_NO_SUCH_FILE) /* ENOENT on NFS or 9x share */
3243 /* The file exists, but the user can't access it for one reason
3244 or the other. To get the file attributes we try to access the
3245 information by opening the parent directory and getting the
3246 file attributes using a matching NtQueryDirectoryFile call. */
3247 UNICODE_STRING dirname, basename;
3248 OBJECT_ATTRIBUTES dattr;
3249 HANDLE dir;
3250 struct {
3251 FILE_ID_BOTH_DIR_INFORMATION fdi;
3252 WCHAR dummy_buf[NAME_MAX + 1];
3253 } fdi_buf;
3255 RtlSplitUnicodePath (&upath, &dirname, &basename);
3256 InitializeObjectAttributes (&dattr, &dirname, ci_flag,
3257 NULL, NULL);
3258 status = NtOpenFile (&dir, SYNCHRONIZE | FILE_LIST_DIRECTORY,
3259 &dattr, &io, FILE_SHARE_VALID_FLAGS,
3260 FILE_SYNCHRONOUS_IO_NONALERT
3261 | FILE_OPEN_FOR_BACKUP_INTENT
3262 | FILE_DIRECTORY_FILE);
3263 if (!NT_SUCCESS (status))
3265 debug_printf ("%y = NtOpenFile(%S)", status, &dirname);
3266 /* There's a special case if the file is itself the root
3267 of a drive which is not accessible by the current user.
3268 This case is only recognized by the length of the
3269 basename part. If it's 0, the incoming file is the
3270 root of a drive. So we at least know it's a directory. */
3271 if (basename.Length)
3272 fileattr = FILE_ATTRIBUTE_DIRECTORY;
3273 else
3275 fileattr = 0;
3276 set_error (geterrno_from_nt_status (status));
3279 else
3281 status = NtQueryDirectoryFile (dir, NULL, NULL, NULL, &io,
3282 &fdi_buf, sizeof fdi_buf,
3283 FileIdBothDirectoryInformation,
3284 TRUE, &basename, TRUE);
3285 /* Take the opportunity to check file system while we're
3286 having the handle to the parent dir. */
3287 fs.update (&upath, dir);
3288 NtClose (dir);
3289 if (!NT_SUCCESS (status))
3291 debug_printf ("%y = NtQueryDirectoryFile(%S)",
3292 status, &dirname);
3293 if (status == STATUS_NO_SUCH_FILE)
3295 /* This can happen when trying to access files
3296 which match DOS device names on SMB shares.
3297 NtOpenFile failed with STATUS_ACCESS_DENIED,
3298 but the NtQueryDirectoryFile tells us the
3299 file doesn't exist. We're suspicious in this
3300 case and retry with the next suffix instead of
3301 just giving up. */
3302 set_error (ENOENT);
3303 continue;
3305 fileattr = 0;
3307 else
3309 PFILE_ALL_INFORMATION pfai = conv_hdl.fai ();
3311 fileattr = fdi_buf.fdi.FileAttributes;
3312 memcpy (&pfai->BasicInformation.CreationTime,
3313 &fdi_buf.fdi.CreationTime,
3314 4 * sizeof (LARGE_INTEGER));
3315 pfai->BasicInformation.FileAttributes = fileattr;
3316 pfai->StandardInformation.AllocationSize.QuadPart
3317 = fdi_buf.fdi.AllocationSize.QuadPart;
3318 pfai->StandardInformation.EndOfFile.QuadPart
3319 = fdi_buf.fdi.EndOfFile.QuadPart;
3320 pfai->StandardInformation.NumberOfLinks = 1;
3321 pfai->InternalInformation.IndexNumber.QuadPart
3322 = fdi_buf.fdi.FileId.QuadPart;
3325 ext_tacked_on = !!*ext_here;
3326 goto file_not_symlink;
3328 set_error (ENOENT);
3329 continue;
3332 ext_tacked_on = !!*ext_here;
3333 /* Don't allow to returns directories with appended suffix. If we found
3334 a directory with a suffix which has been appended here, then this
3335 directory doesn't match the request. So, just do as usual if file
3336 hasn't been found. */
3337 if (ext_tacked_on && !had_ext && (fileattr & FILE_ATTRIBUTE_DIRECTORY))
3339 fileattr = INVALID_FILE_ATTRIBUTES;
3340 set_error (ENOENT);
3341 continue;
3344 res = -1;
3346 /* Reparse points are potentially symlinks. This check must be
3347 performed before checking the SYSTEM attribute for sysfile
3348 symlinks, since reparse points can have this flag set, too. */
3349 if ((fileattr & FILE_ATTRIBUTE_REPARSE_POINT))
3351 res = check_reparse_point (h, fs.is_remote_drive ());
3352 if (res > 0)
3354 /* A symlink is never a directory. */
3355 conv_hdl.fai ()->BasicInformation.FileAttributes
3356 &= ~FILE_ATTRIBUTE_DIRECTORY;
3357 break;
3359 else if (res == 0 && (path_flags & PATH_REP))
3360 /* Known reparse point but not a symlink. */
3361 goto file_not_symlink;
3362 else
3364 /* Volume moint point or unrecognized reparse point type.
3365 Make sure the open handle is not used in later stat calls.
3366 The handle has been opened with the FILE_OPEN_REPARSE_POINT
3367 flag, so it's a handle to the reparse point, not a handle
3368 to the volumes root dir. */
3369 pc_flags &= ~PC_KEEP_HANDLE;
3370 /* Volume mount point: The filesystem information for the top
3371 level directory should be for the volume top level directory,
3372 rather than for the reparse point itself. So we fetch the
3373 filesystem information again, but with a NULL handle.
3374 This does what we want because fs_info::update opens the
3375 handle without FILE_OPEN_REPARSE_POINT. */
3376 if (res < 0)
3377 fs.update (&upath, NULL);
3381 /* Windows shortcuts are potentially treated as symlinks. Valid Cygwin
3382 & U/WIN shortcuts are R/O, but definitely not directories. */
3383 else if ((fileattr & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_DIRECTORY))
3384 == FILE_ATTRIBUTE_READONLY && suffix.lnk_match ())
3386 HANDLE sym_h;
3388 status = NtOpenFile (&sym_h, SYNCHRONIZE | GENERIC_READ, &attr, &io,
3389 FILE_SHARE_VALID_FLAGS,
3390 FILE_OPEN_FOR_BACKUP_INTENT
3391 | FILE_SYNCHRONOUS_IO_NONALERT);
3392 if (!NT_SUCCESS (status))
3393 res = 0;
3394 else
3396 res = check_shortcut (sym_h);
3397 NtClose (sym_h);
3399 if (!res)
3401 /* If searching for `foo' and then finding a `foo.lnk' which
3402 is no shortcut, return the same as if file not found. */
3403 if (ext_tacked_on)
3405 fileattr = INVALID_FILE_ATTRIBUTES;
3406 set_error (ENOENT);
3407 continue;
3410 else if (contents[0] != ':' || contents[1] != '\\'
3411 || !parse_device (contents))
3412 break;
3415 /* If searching for `foo' and then finding a `foo.lnk' which is
3416 no shortcut, return the same as if file not found. */
3417 else if (suffix.lnk_match () && ext_tacked_on)
3419 fileattr = INVALID_FILE_ATTRIBUTES;
3420 set_error (ENOENT);
3421 continue;
3424 /* This is the old Cygwin method creating symlinks. A symlink will
3425 have the `system' file attribute. Only files can be symlinks
3426 (which can be symlinks to directories). */
3427 else if ((fileattr & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DIRECTORY))
3428 == FILE_ATTRIBUTE_SYSTEM)
3430 HANDLE sym_h;
3432 status = NtOpenFile (&sym_h, SYNCHRONIZE | GENERIC_READ, &attr, &io,
3433 FILE_SHARE_VALID_FLAGS,
3434 FILE_OPEN_FOR_BACKUP_INTENT
3435 | FILE_SYNCHRONOUS_IO_NONALERT);
3437 if (!NT_SUCCESS (status))
3438 res = 0;
3439 else
3441 res = check_sysfile (sym_h);
3442 NtClose (sym_h);
3444 if (res)
3445 break;
3448 /* If the file is on an NFS share and could be opened with extended
3449 attributes, check if it's a symlink. Only files can be symlinks
3450 (which can be symlinks to directories). */
3451 else if (fs.is_nfs () && (conv_hdl.nfsattr ()->type & 7) == NF3LNK)
3453 res = check_nfs_symlink (h);
3454 if (res)
3455 break;
3458 /* Check if the inner path components contain native symlinks or
3459 junctions, or if the drive is a virtual drive. Compare incoming
3460 path with path returned by GetFinalPathNameByHandleA. If they
3461 differ, return the final path as symlink content and set symlen
3462 to a negative value. This forces path_conv::check to restart
3463 symlink evaluation with the new path. */
3464 #ifdef __i386__
3465 /* On WOW64, ignore any potential problems if the path is inside
3466 the Windows dir to avoid false positives for stuff under File
3467 System Redirector control. Believe it or not, but even
3468 GetFinalPathNameByHandleA returns the converted path for the
3469 Sysnative dir. I. e.
3471 C:\Windows\Sysnative --> C:\Windows\System32
3473 This is obviously wrong when using this path for further
3474 file manipulation because the non-final path points to another
3475 file than the final path. Oh well... */
3476 if (!fs.is_remote_drive () && wincap.is_wow64 ())
3478 /* windows_directory_path is stored without trailing backslash,
3479 so we have to check this explicitely. */
3480 if (RtlEqualUnicodePathPrefix (&upath, &windows_directory_path, TRUE)
3481 && upath.Buffer[windows_directory_path.Length / sizeof (WCHAR)]
3482 == L'\\')
3483 goto file_not_symlink;
3485 #endif /* __i386__ */
3486 if ((pc_flags & (PC_SYM_FOLLOW | PC_SYM_NOFOLLOW_REP)) == PC_SYM_FOLLOW)
3488 PWCHAR fpbuf = tp.w_get ();
3489 DWORD ret;
3491 ret = GetFinalPathNameByHandleW (h, fpbuf, NT_MAX_PATH, 0);
3492 if (ret)
3494 UNICODE_STRING fpath;
3496 /* If incoming path has no trailing backslash, but final path
3497 has one, drop trailing backslash from final path so the
3498 below string comparison has a chance to succeed.
3499 On the contrary, if incoming path has trailing backslash,
3500 but final path does not have one, add trailing backslash
3501 to the final path. */
3502 if (upath.Buffer[(upath.Length - 1) / sizeof (WCHAR)] != L'\\'
3503 && fpbuf[ret - 1] == L'\\')
3504 fpbuf[--ret] = L'\0';
3505 if (upath.Buffer[(upath.Length - 1) / sizeof (WCHAR)] == L'\\'
3506 && fpbuf[ret - 1] != L'\\' && ret < NT_MAX_PATH - 1)
3508 fpbuf[ret++] = L'\\';
3509 fpbuf[ret] = L'\0';
3511 fpbuf[1] = L'?'; /* \\?\ --> \??\ */
3512 RtlInitCountedUnicodeString (&fpath, fpbuf, ret * sizeof (WCHAR));
3513 if (!RtlEqualUnicodeString (&upath, &fpath, !!ci_flag))
3515 /* Check if the final path is an UNC path and the incoming
3516 path isn't. If so... */
3517 if (RtlEqualUnicodePathPrefix (&fpath, &ro_u_uncp, TRUE)
3518 && !RtlEqualUnicodePathPrefix (&upath, &ro_u_uncp, TRUE))
3520 /* ...get the remote path, replace remote path
3521 with drive letter, check again. */
3522 WCHAR drive[3] =
3523 {(WCHAR) towupper (upath.Buffer[4]), L':', L'\0'};
3524 WCHAR remote[MAX_PATH];
3526 DWORD remlen = QueryDosDeviceW (drive, remote, MAX_PATH);
3527 if (remlen < 3)
3528 goto file_not_symlink; /* fallback */
3529 remlen -= 2; /* Two L'\0' */
3531 if (remote[remlen - 1] == L'\\')
3532 remlen--;
3533 WCHAR *p;
3534 UNICODE_STRING rpath;
3535 RtlInitCountedUnicodeString (&rpath, remote,
3536 remlen * sizeof (WCHAR));
3537 const USHORT uncp_len =
3538 ro_u_uncp.Length / sizeof (WCHAR) - 1;
3539 if (RtlEqualUnicodePathPrefix (&rpath, &ro_u_uncp, TRUE))
3541 remlen -= uncp_len;
3542 p = remote + uncp_len;
3544 else if ((p = wcschr (remote, L';'))
3545 && p + 3 < remote + remlen
3546 && wcsncmp (p + 1, drive, 2) == 0
3547 && (p = wcschr (p + 3, L'\\')))
3548 remlen -= p - remote;
3549 else
3550 goto file_not_symlink; /* fallback */
3551 if (wcsncasecmp (fpath.Buffer + uncp_len, p, remlen))
3552 goto file_not_symlink; /* fallback (not expected) */
3553 /* Hackfest */
3554 fpath.Buffer[4] = drive[0]; /* Drive letter */
3555 fpath.Buffer[5] = L':';
3556 WCHAR *to = fpath.Buffer + 6; /* Next to L':' */
3557 WCHAR *from = fpath.Buffer + uncp_len + remlen;
3558 memmove (to, from,
3559 (wcslen (from) + 1) * sizeof (WCHAR));
3560 fpath.Length -= (from - to) * sizeof (WCHAR);
3561 if (RtlEqualUnicodeString (&upath, &fpath, !!ci_flag))
3562 goto file_not_symlink;
3564 issymlink = true;
3565 /* upath.Buffer is big enough and unused from this point on.
3566 Reuse it here, avoiding yet another buffer allocation. */
3567 char *nfpath = (char *) upath.Buffer;
3568 sys_wcstombs (nfpath, NT_MAX_PATH, fpbuf);
3569 res = posixify (nfpath);
3571 /* If the incoming path consisted of a drive prefix only,
3572 we just handle a virtual drive, created with, e.g.
3574 subst X: C:\foo\bar
3576 Treat it like a symlink. This is required to tell an
3577 lstat caller that the "drive" is actually pointing
3578 somewhere else, thus, it's a symlink in POSIX speak. */
3579 if (upath.Length == 14) /* \??\X:\ */
3581 fileattr &= ~FILE_ATTRIBUTE_DIRECTORY;
3582 path_flags |= PATH_SYMLINK;
3584 /* For final paths differing in inner path components return
3585 length as negative value. This informs path_conv::check
3586 to skip realpath handling on the last path component. */
3587 else
3588 res = -res;
3589 break;
3594 /* Normal file. */
3595 file_not_symlink:
3596 issymlink = false;
3597 syscall_printf ("%s", isdevice ? "is a device" : "not a symlink");
3598 res = 0;
3599 break;
3602 if (h)
3604 if (pc_flags & PC_KEEP_HANDLE)
3605 conv_hdl.set (h);
3606 else
3607 NtClose (h);
3610 syscall_printf ("%d = symlink.check(%s, %p) (mount_flags %y, path_flags %y)",
3611 res, suffix.path, contents, mount_flags, path_flags);
3612 return res;
3615 /* "path" is the path in a virtual symlink. Set a symlink_info struct from
3616 that and proceed with further path checking afterwards. */
3618 symlink_info::set (char *path)
3620 strcpy (contents, path);
3621 mount_flags = 0;
3622 path_flags = PATH_SYMLINK;
3623 fileattr = FILE_ATTRIBUTE_NORMAL;
3624 error = 0;
3625 issymlink = true;
3626 isdevice = false;
3627 ext_tacked_on = false;
3628 ext_here = NULL;
3629 extn = major = minor = mode = 0;
3630 return strlen (path);
3633 /* readlink system call */
3635 extern "C" ssize_t
3636 readlink (const char *__restrict path, char *__restrict buf, size_t buflen)
3638 if (buflen < 0)
3640 set_errno (ENAMETOOLONG);
3641 return -1;
3644 path_conv pathbuf (path, PC_SYM_CONTENTS, stat_suffixes);
3646 if (pathbuf.error)
3648 set_errno (pathbuf.error);
3649 syscall_printf ("-1 = readlink (%s, %p, %lu)", path, buf, buflen);
3650 return -1;
3653 if (!pathbuf.exists ())
3655 set_errno (ENOENT);
3656 return -1;
3659 if (!pathbuf.issymlink ())
3661 if (pathbuf.exists ())
3662 set_errno (EINVAL);
3663 return -1;
3666 size_t pathbuf_len = strlen (pathbuf.get_win32 ());
3667 ssize_t len = MIN (buflen, pathbuf_len);
3668 memcpy (buf, pathbuf.get_win32 (), len);
3670 /* errno set by symlink.check if error */
3671 return len;
3674 /* Some programs rely on st_dev/st_ino being unique for each file.
3675 Hash the path name and hope for the best. The hash arg is not
3676 always initialized to zero since readdir needs to compute the
3677 dirent ino_t based on a combination of the hash of the directory
3678 done during the opendir call and the hash or the filename within
3679 the directory. FIXME: Not bullet-proof. */
3680 /* Cygwin internal */
3681 ino_t __reg2
3682 hash_path_name (ino_t hash, PUNICODE_STRING name)
3684 if (name->Length == 0)
3685 return hash;
3687 /* Build up hash. Name is already normalized */
3688 USHORT len = name->Length / sizeof (WCHAR);
3689 for (USHORT idx = 0; idx < len; ++idx)
3690 hash = RtlUpcaseUnicodeChar (name->Buffer[idx])
3691 + (hash << 6) + (hash << 16) - hash;
3692 return hash;
3695 ino_t __reg2
3696 hash_path_name (ino_t hash, PCWSTR name)
3698 UNICODE_STRING uname;
3699 RtlInitUnicodeString (&uname, name);
3700 return hash_path_name (hash, &uname);
3703 ino_t __reg2
3704 hash_path_name (ino_t hash, const char *name)
3706 UNICODE_STRING uname;
3707 RtlCreateUnicodeStringFromAsciiz (&uname, name);
3708 ino_t ret = hash_path_name (hash, &uname);
3709 RtlFreeUnicodeString (&uname);
3710 return ret;
3713 extern "C" char *
3714 getcwd (char *buf, size_t ulen)
3716 char* res = NULL;
3718 __try
3720 if (ulen == 0 && buf)
3721 set_errno (EINVAL);
3722 else
3723 res = cygheap->cwd.get (buf, 1, 1, ulen);
3725 __except (EFAULT) {}
3726 __endtry
3727 return res;
3730 /* getwd: Legacy. */
3731 extern "C" char *
3732 getwd (char *buf)
3734 return getcwd (buf, PATH_MAX + 1); /*Per SuSv3!*/
3737 extern "C" char *
3738 get_current_dir_name (void)
3740 const char *pwd = getenv ("PWD");
3741 char *cwd = getcwd (NULL, 0);
3742 struct stat pwdbuf, cwdbuf;
3744 if (pwd && strcmp (pwd, cwd) != 0
3745 && stat64 (pwd, &pwdbuf) == 0
3746 && stat64 (cwd, &cwdbuf) == 0
3747 && pwdbuf.st_dev == cwdbuf.st_dev
3748 && pwdbuf.st_ino == cwdbuf.st_ino)
3750 cwd = (char *) realloc (cwd, strlen (pwd) + 1);
3751 strcpy (cwd, pwd);
3754 return cwd;
3757 /* chdir: POSIX 5.2.1.1 */
3758 extern "C" int
3759 chdir (const char *in_dir)
3761 int res = -1;
3763 __try
3765 if (!*in_dir)
3767 set_errno (ENOENT);
3768 __leave;
3771 syscall_printf ("dir '%s'", in_dir);
3773 /* Convert path. PC_NONULLEMPTY ensures that we don't check for
3774 NULL/empty/invalid again. */
3775 path_conv path (in_dir, PC_SYM_FOLLOW | PC_POSIX | PC_NONULLEMPTY);
3776 if (path.error)
3778 set_errno (path.error);
3779 syscall_printf ("-1 = chdir (%s)", in_dir);
3780 __leave;
3783 const char *posix_cwd = NULL;
3784 dev_t devn = path.get_device ();
3785 if (!path.exists ())
3786 set_errno (ENOENT);
3787 else if (!path.isdir ())
3788 set_errno (ENOTDIR);
3789 else if (!isvirtual_dev (devn))
3791 /* The sequence chdir("xx"); chdir(".."); must be a noop if xx
3792 is not a symlink. This is exploited by find.exe.
3793 The posix_cwd is just path.get_posix ().
3794 In other cases we let cwd.set obtain the Posix path through
3795 the mount table. */
3796 if (!isdrive (path.get_posix ()))
3797 posix_cwd = path.get_posix ();
3798 res = 0;
3800 else
3802 posix_cwd = path.get_posix ();
3803 res = 0;
3806 if (!res)
3807 res = cygheap->cwd.set (&path, posix_cwd);
3809 /* Note that we're accessing cwd.posix without a lock here.
3810 I didn't think it was worth locking just for strace. */
3811 syscall_printf ("%R = chdir() cygheap->cwd.posix '%s' native '%S'", res,
3812 cygheap->cwd.get_posix (), path.get_nt_native_path ());
3814 __except (EFAULT)
3816 res = -1;
3818 __endtry
3819 return res;
3822 extern "C" int
3823 fchdir (int fd)
3825 int res;
3826 cygheap_fdget cfd (fd);
3827 if (cfd >= 0)
3828 res = chdir (cfd->get_name ());
3829 else
3830 res = -1;
3832 syscall_printf ("%R = fchdir(%d)", res, fd);
3833 return res;
3836 /******************** Exported Path Routines *********************/
3838 /* Cover functions to the path conversion routines.
3839 These are exported to the world as cygwin_foo by cygwin.din. */
3841 #define return_with_errno(x) \
3842 do {\
3843 int err = (x);\
3844 if (!err)\
3845 return 0;\
3846 set_errno (err);\
3847 return -1;\
3848 } while (0)
3850 extern "C" ssize_t
3851 cygwin_conv_path (cygwin_conv_path_t what, const void *from, void *to,
3852 size_t size)
3854 tmp_pathbuf tp;
3855 path_conv p;
3856 size_t lsiz = 0;
3857 char *buf = NULL;
3858 PWCHAR path = NULL;
3859 int error = 0;
3860 int how = what & CCP_CONVFLAGS_MASK;
3861 what &= CCP_CONVTYPE_MASK;
3862 int ret = -1;
3864 __try
3866 if (!from)
3868 set_errno (EINVAL);
3869 __leave;
3872 switch (what)
3874 case CCP_POSIX_TO_WIN_A:
3876 p.check ((const char *) from,
3877 PC_POSIX | PC_SYM_FOLLOW | PC_SYM_NOFOLLOW_REP
3878 | PC_NO_ACCESS_CHECK
3879 | ((how & CCP_RELATIVE) ? PC_NOFULL : 0), stat_suffixes);
3880 if (p.error)
3882 set_errno (p.error);
3883 __leave;
3885 PUNICODE_STRING up = p.get_nt_native_path ();
3886 buf = tp.c_get ();
3887 sys_wcstombs (buf, NT_MAX_PATH,
3888 up->Buffer, up->Length / sizeof (WCHAR));
3889 /* Convert native path to standard DOS path. */
3890 if (!strncmp (buf, "\\??\\", 4))
3892 buf += 4;
3893 if (buf[1] != ':') /* native UNC path */
3894 *(buf += 2) = '\\';
3896 else if (*buf == '\\')
3898 /* Device name points to somewhere else in the NT namespace.
3899 Use GLOBALROOT prefix to convert to Win32 path. */
3900 char *p = buf + sys_wcstombs (buf, NT_MAX_PATH,
3901 ro_u_globalroot.Buffer,
3902 ro_u_globalroot.Length
3903 / sizeof (WCHAR));
3904 sys_wcstombs (p, NT_MAX_PATH - (p - buf),
3905 up->Buffer, up->Length / sizeof (WCHAR));
3907 lsiz = strlen (buf) + 1;
3908 /* TODO: Incoming "." is a special case which leads to a trailing
3909 backslash ".\\" in the Win32 path. That's a result of the
3910 conversion in normalize_posix_path. This should not occur
3911 so the below code is just a band-aid. */
3912 if ((how & CCP_RELATIVE) && !strcmp ((const char *) from, ".")
3913 && !strcmp (buf, ".\\"))
3915 lsiz = 2;
3916 buf[1] = '\0';
3919 break;
3920 case CCP_POSIX_TO_WIN_W:
3921 p.check ((const char *) from,
3922 PC_POSIX | PC_SYM_FOLLOW | PC_SYM_NOFOLLOW_REP
3923 | PC_NO_ACCESS_CHECK
3924 | ((how & CCP_RELATIVE) ? PC_NOFULL : 0), stat_suffixes);
3925 if (p.error)
3927 set_errno (p.error);
3928 __leave;
3930 /* Relative Windows paths are always restricted to MAX_PATH chars. */
3931 if ((how & CCP_RELATIVE) && !isabspath (p.get_win32 ())
3932 && sys_mbstowcs (NULL, 0, p.get_win32 ()) > MAX_PATH)
3934 /* Recreate as absolute path. */
3935 p.check ((const char *) from, PC_POSIX | PC_SYM_FOLLOW
3936 | PC_NO_ACCESS_CHECK);
3937 if (p.error)
3939 set_errno (p.error);
3940 __leave;
3943 lsiz = p.get_wide_win32_path_len () + 1;
3944 path = p.get_nt_native_path ()->Buffer;
3946 /* Convert native path to standard DOS path. */
3947 if (!wcsncmp (path, L"\\??\\", 4))
3949 path[1] = L'\\';
3951 /* Drop long path prefix for short pathnames. Unfortunately there's
3952 quite a bunch of Win32 functions, especially in user32.dll,
3953 apparently, which don't grok long path names at all, not even
3954 in the UNICODE API. */
3955 if ((path[5] == L':' && lsiz <= MAX_PATH + 4)
3956 || (!wcsncmp (path + 4, L"UNC\\", 4) && lsiz <= MAX_PATH + 6))
3958 path += 4;
3959 lsiz -= 4;
3960 if (path[1] != L':')
3962 *(path += 2) = '\\';
3963 lsiz -= 2;
3967 else if (*path == L'\\')
3969 /* Device name points to somewhere else in the NT namespace.
3970 Use GLOBALROOT prefix to convert to Win32 path. */
3971 to = (void *) wcpcpy ((wchar_t *) to, ro_u_globalroot.Buffer);
3972 lsiz += ro_u_globalroot.Length / sizeof (WCHAR);
3974 /* TODO: Same ".\\" band-aid as in CCP_POSIX_TO_WIN_A case. */
3975 if ((how & CCP_RELATIVE) && !strcmp ((const char *) from, ".")
3976 && !wcscmp (path, L".\\"))
3978 lsiz = 2;
3979 path[1] = L'\0';
3981 lsiz *= sizeof (WCHAR);
3982 break;
3983 case CCP_WIN_A_TO_POSIX:
3984 buf = tp.c_get ();
3985 error = mount_table->conv_to_posix_path ((const char *) from, buf,
3986 how | __CCP_APP_SLASH);
3987 if (error)
3989 set_errno (p.error);
3990 __leave;
3992 lsiz = strlen (buf) + 1;
3993 break;
3994 case CCP_WIN_W_TO_POSIX:
3995 buf = tp.c_get ();
3996 error = mount_table->conv_to_posix_path ((const PWCHAR) from, buf,
3997 how | __CCP_APP_SLASH);
3998 if (error)
4000 set_errno (error);
4001 __leave;
4003 lsiz = strlen (buf) + 1;
4004 break;
4005 default:
4006 set_errno (EINVAL);
4007 __leave;
4009 if (!size)
4011 ret = lsiz;
4012 __leave;
4014 if (size < lsiz)
4016 set_errno (ENOSPC);
4017 __leave;
4019 switch (what)
4021 case CCP_POSIX_TO_WIN_A:
4022 case CCP_WIN_A_TO_POSIX:
4023 case CCP_WIN_W_TO_POSIX:
4024 stpcpy ((char *) to, buf);
4025 break;
4026 case CCP_POSIX_TO_WIN_W:
4027 wcpcpy ((PWCHAR) to, path);
4028 break;
4030 ret = 0;
4032 __except (EFAULT) {}
4033 __endtry
4034 return ret;
4037 extern "C" void *
4038 cygwin_create_path (cygwin_conv_path_t what, const void *from)
4040 void *to;
4041 ssize_t size = cygwin_conv_path (what, from, NULL, 0);
4042 if (size <= 0)
4043 to = NULL;
4044 else if (!(to = malloc (size)))
4045 to = NULL;
4046 if (cygwin_conv_path (what, from, to, size) == -1)
4048 free (to);
4049 to = NULL;
4051 return to;
4054 #ifdef __i386__
4056 extern "C" int
4057 cygwin_conv_to_win32_path (const char *path, char *win32_path)
4059 return cygwin_conv_path (CCP_POSIX_TO_WIN_A | CCP_RELATIVE, path, win32_path,
4060 MAX_PATH);
4063 extern "C" int
4064 cygwin_conv_to_full_win32_path (const char *path, char *win32_path)
4066 return cygwin_conv_path (CCP_POSIX_TO_WIN_A | CCP_ABSOLUTE, path, win32_path,
4067 MAX_PATH);
4070 /* This is exported to the world as cygwin_foo by cygwin.din. */
4072 extern "C" int
4073 cygwin_conv_to_posix_path (const char *path, char *posix_path)
4075 return cygwin_conv_path (CCP_WIN_A_TO_POSIX | CCP_RELATIVE, path, posix_path,
4076 MAX_PATH);
4079 extern "C" int
4080 cygwin_conv_to_full_posix_path (const char *path, char *posix_path)
4082 return cygwin_conv_path (CCP_WIN_A_TO_POSIX | CCP_ABSOLUTE, path, posix_path,
4083 MAX_PATH);
4086 #endif /* __i386__ */
4088 /* The realpath function is required by POSIX:2008. */
4090 extern "C" char *
4091 realpath (const char *__restrict path, char *__restrict resolved)
4093 tmp_pathbuf tp;
4094 char *tpath;
4096 /* Make sure the right errno is returned if path is NULL. */
4097 if (!path)
4099 set_errno (EINVAL);
4100 return NULL;
4103 /* Guard reading from a potentially invalid path and writing to a
4104 potentially invalid resolved. */
4105 __try
4107 /* Win32 drive letter paths and, generally, any path starting with a
4108 backslash, have to be converted to a POSIX path first, because
4109 path_conv leaves the incoming path untouched except for converting
4110 backslashes to forward slashes. This also covers '\\?\ and '\??\'
4111 path prefixes. */
4112 if (isdrive (path) || path[0] == '\\')
4114 tpath = tp.c_get ();
4115 mount_table->conv_to_posix_path (path, tpath, 0);
4117 else
4118 tpath = (char *) path;
4120 path_conv real_path (tpath, PC_SYM_FOLLOW | PC_POSIX, stat_suffixes);
4123 /* POSIX 2008 requires malloc'ing if resolved is NULL, and states
4124 that using non-NULL resolved is asking for portability
4125 problems. */
4127 if (!real_path.error && real_path.exists ())
4129 if (!resolved)
4131 resolved = (char *)
4132 malloc (strlen (real_path.get_posix ()) + 1);
4133 if (!resolved)
4134 return NULL;
4136 strcpy (resolved, real_path.get_posix ());
4137 return resolved;
4140 /* FIXME: on error, Linux puts the name of the path
4141 component which could not be resolved into RESOLVED, but POSIX
4142 does not require this. */
4143 if (resolved)
4144 resolved[0] = '\0';
4145 set_errno (real_path.error ?: ENOENT);
4147 __except (EFAULT) {}
4148 __endtry
4149 return NULL;
4152 /* Linux provides this extension. Since the only portable use of
4153 realpath requires a NULL second argument, we might as well have a
4154 one-argument wrapper. */
4155 extern "C" char *
4156 canonicalize_file_name (const char *path)
4158 return realpath (path, NULL);
4161 /* Return non-zero if path is a POSIX path list.
4162 This is exported to the world as cygwin_foo by cygwin.din.
4164 DOCTOOL-START
4165 <sect1 id="add-func-cygwin-posix-path-list-p">
4166 <para>Rather than use a mode to say what the "proper" path list
4167 format is, we allow any, and give apps the tools they need to
4168 convert between the two. If a ';' is present in the path list it's
4169 a Win32 path list. Otherwise, if the first path begins with
4170 [letter]: (in which case it can be the only element since if it
4171 wasn't a ';' would be present) it's a Win32 path list. Otherwise,
4172 it's a POSIX path list.</para>
4173 </sect1>
4174 DOCTOOL-END
4177 extern "C" int
4178 cygwin_posix_path_list_p (const char *path)
4180 int posix_p = !(strchr (path, ';') || isdrive (path));
4181 return posix_p;
4184 /* These are used for apps that need to convert env vars like PATH back and
4185 forth. The conversion is a two step process. First, an upper bound on the
4186 size of the buffer needed is computed. Then the conversion is done. This
4187 allows the caller to use alloca if it wants. */
4189 static int
4190 conv_path_list_buf_size (const char *path_list, bool to_posix)
4192 int i, num_elms, max_mount_path_len, size;
4193 const char *p;
4195 path_conv pc(".", PC_POSIX);
4196 /* The theory is that an upper bound is
4197 current_size + (num_elms * max_mount_path_len) */
4198 /* FIXME: This method is questionable in the long run. */
4200 unsigned nrel;
4201 char delim = to_posix ? ';' : ':';
4202 for (p = path_list, num_elms = nrel = 0; p; num_elms++)
4204 if (!isabspath (p))
4205 nrel++;
4206 p = strchr (++p, delim);
4209 /* 7: strlen ("//c") + slop, a conservative initial value */
4210 for (max_mount_path_len = sizeof ("/cygdrive/X"), i = 0;
4211 i < mount_table->nmounts; i++)
4213 int mount_len = (to_posix
4214 ? mount_table->mount[i].posix_pathlen
4215 : mount_table->mount[i].native_pathlen);
4216 if (max_mount_path_len < mount_len)
4217 max_mount_path_len = mount_len;
4220 /* 100: slop */
4221 size = strlen (path_list)
4222 + (num_elms * max_mount_path_len)
4223 + (nrel * strlen (to_posix ? pc.get_posix () : pc.get_win32 ()))
4224 + 100;
4226 return size;
4229 extern "C" ssize_t
4230 env_PATH_to_posix (const void *win32, void *posix, size_t size)
4232 return_with_errno (conv_path_list ((const char *) win32, (char *) posix,
4233 size, ENV_CVT));
4236 #ifdef __i386__
4238 extern "C" int
4239 cygwin_win32_to_posix_path_list_buf_size (const char *path_list)
4241 return conv_path_list_buf_size (path_list, true);
4244 extern "C" int
4245 cygwin_posix_to_win32_path_list_buf_size (const char *path_list)
4247 return conv_path_list_buf_size (path_list, false);
4250 extern "C" int
4251 cygwin_win32_to_posix_path_list (const char *win32, char *posix)
4253 return_with_errno (conv_path_list (win32, posix, MAX_PATH,
4254 CCP_WIN_A_TO_POSIX | CCP_RELATIVE));
4257 extern "C" int
4258 cygwin_posix_to_win32_path_list (const char *posix, char *win32)
4260 return_with_errno (conv_path_list (posix, win32, MAX_PATH,
4261 CCP_POSIX_TO_WIN_A | CCP_RELATIVE));
4264 #endif /* __i386__ */
4266 extern "C" ssize_t
4267 cygwin_conv_path_list (cygwin_conv_path_t what, const void *from, void *to,
4268 size_t size)
4270 int ret;
4271 char *winp = NULL;
4272 void *orig_to = NULL;
4273 tmp_pathbuf tp;
4275 switch (what & CCP_CONVTYPE_MASK)
4277 case CCP_WIN_W_TO_POSIX:
4278 if (!sys_wcstombs_alloc (&winp, HEAP_NOTHEAP, (const wchar_t *) from,
4279 (size_t) -1))
4280 return -1;
4281 what = (what & ~CCP_CONVTYPE_MASK) | CCP_WIN_A_TO_POSIX;
4282 from = (const void *) winp;
4283 break;
4284 case CCP_POSIX_TO_WIN_W:
4285 if (size == 0)
4286 return conv_path_list_buf_size ((const char *) from, 0)
4287 * sizeof (WCHAR);
4288 what = (what & ~CCP_CONVTYPE_MASK) | CCP_POSIX_TO_WIN_A;
4289 orig_to = to;
4290 to = (void *) tp.w_get ();
4291 size = 65536;
4292 break;
4294 switch (what & CCP_CONVTYPE_MASK)
4296 case CCP_WIN_A_TO_POSIX:
4297 case CCP_POSIX_TO_WIN_A:
4298 if (size == 0)
4299 return conv_path_list_buf_size ((const char *) from,
4300 what == CCP_WIN_A_TO_POSIX);
4301 ret = conv_path_list ((const char *) from, (char *) to, size, what);
4302 /* Free winp buffer in case of CCP_WIN_W_TO_POSIX. */
4303 if (winp)
4304 free (winp);
4305 /* Convert to WCHAR in case of CCP_POSIX_TO_WIN_W. */
4306 if (orig_to)
4307 sys_mbstowcs ((wchar_t *) orig_to, size / sizeof (WCHAR),
4308 (const char *) to, (size_t) -1);
4309 return_with_errno (ret);
4310 break;
4311 default:
4312 break;
4314 set_errno (EINVAL);
4315 return -1;
4318 /* cygwin_split_path: Split a path into directory and file name parts.
4319 Buffers DIR and FILE are assumed to be big enough.
4321 Examples (path -> `dir' / `file'):
4322 / -> `/' / `'
4323 "" -> `.' / `'
4324 . -> `.' / `.' (FIXME: should this be `.' / `'?)
4325 .. -> `.' / `..' (FIXME: should this be `..' / `'?)
4326 foo -> `.' / `foo'
4327 foo/bar -> `foo' / `bar'
4328 foo/bar/ -> `foo' / `bar'
4329 /foo -> `/' / `foo'
4330 /foo/bar -> `/foo' / `bar'
4331 c: -> `c:/' / `'
4332 c:/ -> `c:/' / `'
4333 c:foo -> `c:/' / `foo'
4334 c:/foo -> `c:/' / `foo'
4337 extern "C" void
4338 cygwin_split_path (const char *path, char *dir, char *file)
4340 int dir_started_p = 0;
4342 /* Deal with drives.
4343 Remember that c:foo <==> c:/foo. */
4344 if (isdrive (path))
4346 *dir++ = *path++;
4347 *dir++ = *path++;
4348 *dir++ = '/';
4349 if (!*path)
4351 *dir = 0;
4352 *file = 0;
4353 return;
4355 if (isdirsep (*path))
4356 ++path;
4357 dir_started_p = 1;
4360 /* Determine if there are trailing slashes and "delete" them if present.
4361 We pretend as if they don't exist. */
4362 const char *end = path + strlen (path);
4363 /* path + 1: keep leading slash. */
4364 while (end > path + 1 && isdirsep (end[-1]))
4365 --end;
4367 /* At this point, END points to one beyond the last character
4368 (with trailing slashes "deleted"). */
4370 /* Point LAST_SLASH at the last slash (duh...). */
4371 const char *last_slash;
4372 for (last_slash = end - 1; last_slash >= path; --last_slash)
4373 if (isdirsep (*last_slash))
4374 break;
4376 if (last_slash == path)
4378 *dir++ = '/';
4379 *dir = 0;
4381 else if (last_slash > path)
4383 memcpy (dir, path, last_slash - path);
4384 dir[last_slash - path] = 0;
4386 else
4388 if (dir_started_p)
4389 ; /* nothing to do */
4390 else
4391 *dir++ = '.';
4392 *dir = 0;
4395 memcpy (file, last_slash + 1, end - last_slash - 1);
4396 file[end - last_slash - 1] = 0;
4399 static inline void
4400 copy_cwd_str (PUNICODE_STRING tgt, PUNICODE_STRING src)
4402 RtlCopyUnicodeString (tgt, src);
4403 if (tgt->Buffer[tgt->Length / sizeof (WCHAR) - 1] != L'\\')
4405 tgt->Buffer[tgt->Length / sizeof (WCHAR)] = L'\\';
4406 tgt->Length += sizeof (WCHAR);
4410 /*****************************************************************************/
4412 /* The find_fast_cwd_pointer function and parts of the
4413 cwdstuff::override_win32_cwd method are based on code using the
4414 following license:
4416 Copyright 2010 John Carey. All rights reserved.
4418 Redistribution and use in source and binary forms, with or without
4419 modification, are permitted provided that the following conditions
4420 are met:
4422 1. Redistributions of source code must retain the above
4423 copyright notice, this list of conditions and the following
4424 disclaimer.
4426 2. Redistributions in binary form must reproduce the above
4427 copyright notice, this list of conditions and the following
4428 disclaimer in the documentation and/or other materials provided
4429 with the distribution.
4431 THIS SOFTWARE IS PROVIDED BY JOHN CAREY ``AS IS'' AND ANY EXPRESS
4432 OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
4433 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
4434 ARE DISCLAIMED. IN NO EVENT SHALL JOHN CAREY OR CONTRIBUTORS BE
4435 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
4436 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
4437 OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
4438 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
4439 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
4440 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
4441 USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
4442 DAMAGE. */
4444 void
4445 fcwd_access_t::SetFSCharacteristics (LONG val)
4447 /* Special case FSCharacteristics. Didn't exist originally. */
4448 switch (fast_cwd_version ())
4450 case FCWD_OLD:
4451 break;
4452 case FCWD_W7:
4453 f7.FSCharacteristics = val;
4454 break;
4455 case FCWD_W8:
4456 f8.FSCharacteristics = val;
4457 break;
4461 fcwd_version_t &
4462 fcwd_access_t::fast_cwd_version ()
4464 return cygheap->cwd.fast_cwd_version;
4467 void
4468 fcwd_access_t::CopyPath (UNICODE_STRING &target)
4470 /* Copy the Path contents over into the UNICODE_STRING referenced by
4471 target. This is used to set the CurrentDirectoryName in the
4472 user parameter block. */
4473 target = Path ();
4476 void
4477 fcwd_access_t::Free (PVOID heap)
4479 /* Decrement the reference count. If it's down to 0, free
4480 structure from heap. */
4481 if (InterlockedDecrement (&ReferenceCount ()) == 0)
4483 /* The handle on init is always a fresh one, not the handle inherited
4484 from the parent process. We always have to close it here.
4485 Note: The handle could be NULL, if we cd'ed into a virtual dir. */
4486 HANDLE h = DirectoryHandle ();
4487 if (h)
4488 NtClose (h);
4489 RtlFreeHeap (heap, 0, this);
4493 void
4494 fcwd_access_t::FillIn (HANDLE dir, PUNICODE_STRING name,
4495 ULONG old_dismount_count)
4497 /* Fill in all values into this FAST_CWD structure. */
4498 DirectoryHandle () = dir;
4499 ReferenceCount () = 1;
4500 OldDismountCount () = old_dismount_count;
4501 /* The new structure stores the device characteristics of the
4502 volume holding the dir. RtlGetCurrentDirectory_U checks
4503 if the FILE_REMOVABLE_MEDIA flag is set and, if so, checks if
4504 the volume is still the same as the one used when opening
4505 the directory handle.
4506 We don't call NtQueryVolumeInformationFile for the \\?\PIPE,
4507 though. It just returns STATUS_INVALID_HANDLE anyway. */
4508 if (fast_cwd_version () != FCWD_OLD)
4510 SetFSCharacteristics (0);
4511 if (name != &ro_u_pipedir)
4513 IO_STATUS_BLOCK io;
4514 FILE_FS_DEVICE_INFORMATION ffdi;
4515 if (NT_SUCCESS (NtQueryVolumeInformationFile (dir, &io, &ffdi,
4516 sizeof ffdi, FileFsDeviceInformation)))
4517 SetFSCharacteristics (ffdi.Characteristics);
4520 RtlInitEmptyUnicodeString (&Path (), Buffer (),
4521 MAX_PATH * sizeof (WCHAR));
4522 copy_cwd_str (&Path (), name);
4525 void
4526 fcwd_access_t::SetDirHandleFromBufferPointer (PWCHAR buf_p, HANDLE dir)
4528 /* Input: The buffer pointer as it's stored in the user parameter block
4529 and a directory handle.
4530 This function computes the address to the FAST_CWD structure based
4531 on the version and overwrites the directory handle. It is only
4532 used if we couldn't figure out the address of fast_cwd_ptr. */
4533 fcwd_access_t *f_cwd;
4534 switch (fast_cwd_version ())
4536 case FCWD_OLD:
4537 default:
4538 f_cwd = (fcwd_access_t *)
4539 ((PBYTE) buf_p - __builtin_offsetof (FAST_CWD_OLD, Buffer));
4540 break;
4541 case FCWD_W7:
4542 f_cwd = (fcwd_access_t *)
4543 ((PBYTE) buf_p - __builtin_offsetof (FAST_CWD_7, Buffer));
4544 break;
4545 case FCWD_W8:
4546 f_cwd = (fcwd_access_t *)
4547 ((PBYTE) buf_p - __builtin_offsetof (FAST_CWD_8, Buffer));
4548 break;
4550 f_cwd->DirectoryHandle () = dir;
4553 void
4554 fcwd_access_t::SetVersionFromPointer (PBYTE buf_p, bool is_buffer)
4556 /* Given a pointer to the FAST_CWD structure (is_buffer == false) or a
4557 pointer to the Buffer within (is_buffer == true), this function
4558 computes the FAST_CWD version by checking that Path.MaximumLength
4559 equals MAX_PATH, and that Path.Buffer == Buffer. */
4560 if (is_buffer)
4561 buf_p -= __builtin_offsetof (FAST_CWD_8, Buffer);
4562 fcwd_access_t *f_cwd = (fcwd_access_t *) buf_p;
4563 if (f_cwd->f8.Path.MaximumLength == MAX_PATH * sizeof (WCHAR)
4564 && f_cwd->f8.Path.Buffer == f_cwd->f8.Buffer)
4565 fast_cwd_version () = FCWD_W8;
4566 else if (f_cwd->f7.Path.MaximumLength == MAX_PATH * sizeof (WCHAR)
4567 && f_cwd->f7.Path.Buffer == f_cwd->f7.Buffer)
4568 fast_cwd_version () = FCWD_W7;
4569 else
4570 fast_cwd_version () = FCWD_OLD;
4573 /* This function scans the code in ntdll.dll to find the address of the
4574 global variable used to access the CWD. While the pointer is global,
4575 it's not exported from the DLL, unfortunately. Therefore we have to
4576 use some knowledge to figure out the address. */
4578 #ifdef __x86_64__
4580 #define peek32(x) (*(int32_t *)(x))
4582 static fcwd_access_t **
4583 find_fast_cwd_pointer ()
4585 /* Fetch entry points of relevant functions in ntdll.dll. */
4586 HMODULE ntdll = GetModuleHandle ("ntdll.dll");
4587 if (!ntdll)
4588 return NULL;
4589 const uint8_t *get_dir = (const uint8_t *)
4590 GetProcAddress (ntdll, "RtlGetCurrentDirectory_U");
4591 const uint8_t *ent_crit = (const uint8_t *)
4592 GetProcAddress (ntdll, "RtlEnterCriticalSection");
4593 if (!get_dir || !ent_crit)
4594 return NULL;
4595 /* Search first relative call instruction in RtlGetCurrentDirectory_U. */
4596 const uint8_t *rcall = (const uint8_t *) memchr (get_dir, 0xe8, 80);
4597 if (!rcall)
4598 return NULL;
4599 /* Fetch offset from instruction and compute address of called function.
4600 This function actually fetches the current FAST_CWD instance and
4601 performs some other actions, not important to us. */
4602 const uint8_t *use_cwd = rcall + 5 + peek32 (rcall + 1);
4603 /* Next we search for the locking mechanism and perform a sanity check.
4604 On Pre-Windows 8 we basically look for the RtlEnterCriticalSection call.
4605 Windows 8 does not call RtlEnterCriticalSection. The code manipulates
4606 the FastPebLock manually, probably because RtlEnterCriticalSection has
4607 been converted to an inline function. Either way, we test if the code
4608 uses the FastPebLock. */
4609 const uint8_t *movrbx;
4610 const uint8_t *lock = (const uint8_t *)
4611 memmem ((const char *) use_cwd, 80,
4612 "\xf0\x0f\xba\x35", 4);
4613 if (lock)
4615 /* The lock instruction tweaks the LockCount member, which is not at
4616 the start of the PRTL_CRITICAL_SECTION structure. So we have to
4617 subtract the offset of LockCount to get the real address. */
4618 PRTL_CRITICAL_SECTION lockaddr =
4619 (PRTL_CRITICAL_SECTION) (lock + 9 + peek32 (lock + 4)
4620 - offsetof (RTL_CRITICAL_SECTION, LockCount));
4621 /* Test if lock address is FastPebLock. */
4622 if (lockaddr != NtCurrentTeb ()->Peb->FastPebLock)
4623 return NULL;
4624 /* Search `mov rel(%rip),%rbx'. This is the instruction fetching the
4625 address of the current fcwd_access_t pointer, and it should be pretty
4626 near to the locking stuff. */
4627 movrbx = (const uint8_t *) memmem ((const char *) lock, 40,
4628 "\x48\x8b\x1d", 3);
4630 else
4632 /* Usually the callq RtlEnterCriticalSection follows right after
4633 fetching the lock address. */
4634 int call_rtl_offset = 7;
4635 /* Search `lea rel(%rip),%rcx'. This loads the address of the lock into
4636 %rcx for the subsequent RtlEnterCriticalSection call. */
4637 lock = (const uint8_t *) memmem ((const char *) use_cwd, 80,
4638 "\x48\x8d\x0d", 3);
4639 if (!lock)
4641 /* Windows 8.1 Preview calls `lea rel(rip),%r12' then some unrelated
4642 or, then `mov %r12,%rcx', then `callq RtlEnterCriticalSection'. */
4643 lock = (const uint8_t *) memmem ((const char *) use_cwd, 80,
4644 "\x4c\x8d\x25", 3);
4645 if (!lock)
4646 return NULL;
4647 call_rtl_offset = 14;
4649 PRTL_CRITICAL_SECTION lockaddr =
4650 (PRTL_CRITICAL_SECTION) (lock + 7 + peek32 (lock + 3));
4651 /* Test if lock address is FastPebLock. */
4652 if (lockaddr != NtCurrentTeb ()->Peb->FastPebLock)
4653 return NULL;
4654 /* Next is the `callq RtlEnterCriticalSection'. */
4655 lock += call_rtl_offset;
4656 if (lock[0] != 0xe8)
4657 return NULL;
4658 const uint8_t *call_addr = (const uint8_t *)
4659 (lock + 5 + peek32 (lock + 1));
4660 if (call_addr != ent_crit)
4661 return NULL;
4662 /* In contrast to the above Windows 8 code, we don't have to search
4663 for the `mov rel(%rip),%rbx' instruction. It follows right after
4664 the call to RtlEnterCriticalSection. */
4665 movrbx = lock + 5;
4667 if (!movrbx)
4668 return NULL;
4669 /* Check that the next instruction tests if the fetched value is NULL. */
4670 const uint8_t *testrbx = (const uint8_t *)
4671 memmem (movrbx + 7, 3, "\x48\x85\xdb", 3);
4672 if (!testrbx)
4673 return NULL;
4674 /* Compute address of the fcwd_access_t ** pointer. */
4675 return (fcwd_access_t **) (testrbx + peek32 (movrbx + 3));
4677 #else
4679 #define peek32(x) (*(uint32_t *)(x))
4681 static fcwd_access_t **
4682 find_fast_cwd_pointer ()
4684 /* Fetch entry points of relevant functions in ntdll.dll. */
4685 HMODULE ntdll = GetModuleHandle ("ntdll.dll");
4686 if (!ntdll)
4687 return NULL;
4688 const uint8_t *get_dir = (const uint8_t *)
4689 GetProcAddress (ntdll, "RtlGetCurrentDirectory_U");
4690 const uint8_t *ent_crit = (const uint8_t *)
4691 GetProcAddress (ntdll, "RtlEnterCriticalSection");
4692 if (!get_dir || !ent_crit)
4693 return NULL;
4694 /* Search first relative call instruction in RtlGetCurrentDirectory_U. */
4695 const uint8_t *rcall = (const uint8_t *) memchr (get_dir, 0xe8, 64);
4696 if (!rcall)
4697 return NULL;
4698 /* Fetch offset from instruction and compute address of called function.
4699 This function actually fetches the current FAST_CWD instance and
4700 performs some other actions, not important to us. */
4701 ptrdiff_t offset = (ptrdiff_t) peek32 (rcall + 1);
4702 const uint8_t *use_cwd = rcall + 5 + offset;
4703 /* Find first `push %edi' instruction. */
4704 const uint8_t *pushedi = (const uint8_t *) memchr (use_cwd, 0x57, 32);
4705 if (!pushedi)
4706 return NULL;
4707 /* ...which should be followed by `mov crit-sect-addr,%edi' then
4708 `push %edi', or by just a single `push crit-sect-addr'. */
4709 const uint8_t *movedi = pushedi + 1;
4710 const uint8_t *mov_pfast_cwd;
4711 if (movedi[0] == 0x8b && movedi[1] == 0xff) /* mov %edi,%edi -> W8 */
4713 /* Windows 8 does not call RtlEnterCriticalSection. The code manipulates
4714 the FastPebLock manually, probably because RtlEnterCriticalSection has
4715 been converted to an inline function.
4717 Next we search for a `mov some address,%eax'. This address points
4718 to the LockCount member of the FastPebLock structure, so the address
4719 is equal to FastPebLock + 4. */
4720 const uint8_t *moveax = (const uint8_t *) memchr (movedi, 0xb8, 16);
4721 if (!moveax)
4722 return NULL;
4723 offset = (ptrdiff_t) peek32 (moveax + 1) - 4;
4724 /* Compare the address with the known PEB lock as stored in the PEB. */
4725 if ((PRTL_CRITICAL_SECTION) offset != NtCurrentTeb ()->Peb->FastPebLock)
4726 return NULL;
4727 /* Now search for the mov instruction fetching the address of the global
4728 PFAST_CWD *. */
4729 mov_pfast_cwd = moveax;
4732 mov_pfast_cwd = (const uint8_t *) memchr (++mov_pfast_cwd, 0x8b, 48);
4734 while (mov_pfast_cwd && mov_pfast_cwd[1] != 0x1d
4735 && (mov_pfast_cwd - moveax) < 48);
4736 if (!mov_pfast_cwd || mov_pfast_cwd[1] != 0x1d)
4737 return NULL;
4739 else
4741 if (movedi[0] == 0xbf && movedi[5] == 0x57)
4742 rcall = movedi + 6;
4743 else if (movedi[0] == 0x68)
4744 rcall = movedi + 5;
4745 else if (movedi[0] == 0x88 && movedi[4] == 0x83 && movedi[7] == 0x68)
4747 /* Windows 8.1 Preview: The `mov lock_addr,%edi' is actually a
4748 `mov %cl,15(%esp), followed by an `or #-1,%ebx, followed by a
4749 `push lock_addr'. */
4750 movedi += 7;
4751 rcall = movedi + 5;
4753 else
4754 return NULL;
4755 /* Compare the address used for the critical section with the known
4756 PEB lock as stored in the PEB. */
4757 if ((PRTL_CRITICAL_SECTION) peek32 (movedi + 1)
4758 != NtCurrentTeb ()->Peb->FastPebLock)
4759 return NULL;
4760 /* To check we are seeing the right code, we check our expectation that
4761 the next instruction is a relative call into RtlEnterCriticalSection. */
4762 if (rcall[0] != 0xe8)
4763 return NULL;
4764 /* Check that this is a relative call to RtlEnterCriticalSection. */
4765 offset = (ptrdiff_t) peek32 (rcall + 1);
4766 if (rcall + 5 + offset != ent_crit)
4767 return NULL;
4768 mov_pfast_cwd = rcall + 5;
4770 /* After locking the critical section, the code should read the global
4771 PFAST_CWD * pointer that is guarded by that critical section. */
4772 if (mov_pfast_cwd[0] != 0x8b)
4773 return NULL;
4774 return (fcwd_access_t **) peek32 (mov_pfast_cwd + 2);
4776 #endif
4778 static fcwd_access_t **
4779 find_fast_cwd ()
4781 /* Fetch the pointer but don't set the global fast_cwd_ptr yet. First
4782 we have to make sure we know the version of the FAST_CWD structure
4783 used on the system. */
4784 fcwd_access_t **f_cwd_ptr = find_fast_cwd_pointer ();
4785 if (!f_cwd_ptr)
4787 bool warn = 1;
4788 USHORT emulated, hosted;
4790 /* Check if we're running in WOW64 on ARM64. Check on 64 bit as well,
4791 given that ARM64 Windows 10 provides a x86_64 emulation soon. Skip
4792 warning as long as there's no solution for finding the FAST_CWD
4793 pointer on that system. */
4794 if (IsWow64Process2 (GetCurrentProcess (), &emulated, &hosted)
4795 && hosted == IMAGE_FILE_MACHINE_ARM64)
4796 warn = 0;
4798 if (warn)
4799 small_printf ("Cygwin WARNING:\n"
4800 " Couldn't compute FAST_CWD pointer. This typically occurs if you're using\n"
4801 " an older Cygwin version on a newer Windows. Please update to the latest\n"
4802 " available Cygwin version from https://cygwin.com/. If the problem persists,\n"
4803 " please see https://cygwin.com/problems.html\n\n");
4805 if (f_cwd_ptr && *f_cwd_ptr)
4807 /* Just evaluate structure version. */
4808 fcwd_access_t::SetVersionFromPointer ((PBYTE) *f_cwd_ptr, false);
4810 else
4812 /* If we couldn't fetch fast_cwd_ptr, or if fast_cwd_ptr is NULL(*)
4813 we have to figure out the version from the Buffer pointer in the
4814 ProcessParameters.
4816 (*) This is very unlikely to happen when starting the first
4817 Cygwin process, since it only happens when starting the
4818 process in a directory which can't be used as CWD by Win32, or
4819 if the directory doesn't exist. But *if* it happens, we have
4820 no valid FAST_CWD structure, even though upp_cwd_str.Buffer is
4821 not NULL in that case. So we let the OS create a valid
4822 FAST_CWD structure temporarily to have something to work with.
4823 We know the pipe FS works. */
4824 PEB &peb = *NtCurrentTeb ()->Peb;
4826 if (f_cwd_ptr /* so *f_cwd_ptr == NULL */
4827 && !NT_SUCCESS (RtlSetCurrentDirectory_U (&ro_u_pipedir)))
4828 api_fatal ("Couldn't set directory to %S temporarily.\n"
4829 "Cannot continue.", &ro_u_pipedir);
4830 RtlEnterCriticalSection (peb.FastPebLock);
4831 fcwd_access_t::SetVersionFromPointer
4832 ((PBYTE) peb.ProcessParameters->CurrentDirectoryName.Buffer, true);
4833 RtlLeaveCriticalSection (peb.FastPebLock);
4835 /* Eventually, after we set the version as well, set fast_cwd_ptr. */
4836 return f_cwd_ptr;
4839 void
4840 cwdstuff::override_win32_cwd (bool init, ULONG old_dismount_count)
4842 HANDLE h = NULL;
4844 PEB &peb = *NtCurrentTeb ()->Peb;
4845 UNICODE_STRING &upp_cwd_str = peb.ProcessParameters->CurrentDirectoryName;
4846 HANDLE &upp_cwd_hdl = peb.ProcessParameters->CurrentDirectoryHandle;
4848 if (fast_cwd_ptr == (fcwd_access_t **) -1)
4849 fast_cwd_ptr = find_fast_cwd ();
4850 if (fast_cwd_ptr)
4852 /* If we got a valid value for fast_cwd_ptr, we can simply replace
4853 the RtlSetCurrentDirectory_U function entirely. */
4854 PVOID heap = peb.ProcessHeap;
4855 /* First allocate a new fcwd_access_t structure on the heap.
4856 The new fcwd_access_t structure is 4 byte bigger than the old one,
4857 but we simply don't care, so we allocate always room for the
4858 new one. */
4859 fcwd_access_t *f_cwd = (fcwd_access_t *)
4860 RtlAllocateHeap (heap, 0, sizeof (fcwd_access_t));
4861 if (!f_cwd)
4863 debug_printf ("RtlAllocateHeap failed");
4864 return;
4866 /* Fill in the values. */
4867 f_cwd->FillIn (dir, error ? &ro_u_pipedir : &win32,
4868 old_dismount_count);
4869 /* Use PEB lock when switching fast_cwd_ptr to the new FAST_CWD
4870 structure and writing the CWD to the user process parameter
4871 block. This is equivalent to calling RtlAcquirePebLock/
4872 RtlReleasePebLock, but without having to go through the FS
4873 selector again. */
4874 RtlEnterCriticalSection (peb.FastPebLock);
4875 fcwd_access_t *old_cwd = *fast_cwd_ptr;
4876 *fast_cwd_ptr = f_cwd;
4877 f_cwd->CopyPath (upp_cwd_str);
4878 upp_cwd_hdl = dir;
4879 RtlLeaveCriticalSection (peb.FastPebLock);
4880 if (old_cwd)
4881 old_cwd->Free (heap);
4883 else
4885 /* Fallback if we failed to find the fast_cwd_ptr value:
4887 - Call RtlSetCurrentDirectory_U.
4888 - Compute new FAST_CWD struct address from buffer pointer in the
4889 user process parameter block.
4890 - Replace the directory handle in the struct with our own handle.
4891 - Close the original handle. RtlSetCurrentDirectory_U already
4892 closed our former dir handle -> no handle leak.
4894 Guard the entire operation with FastPebLock to avoid races
4895 accessing the PEB and FAST_CWD struct.
4897 Unfortunately this method is still prone to a directory usage
4898 race condition:
4900 - The directory is locked against deletion or renaming between the
4901 RtlSetCurrentDirectory_U and the subsequent NtClose call. */
4902 if (unlikely (upp_cwd_hdl == NULL) && init)
4903 return;
4904 RtlEnterCriticalSection (peb.FastPebLock);
4905 if (!init)
4907 NTSTATUS status =
4908 RtlSetCurrentDirectory_U (error ? &ro_u_pipedir : &win32);
4909 if (!NT_SUCCESS (status))
4911 RtlLeaveCriticalSection (peb.FastPebLock);
4912 debug_printf ("RtlSetCurrentDirectory_U(%S) failed, %y",
4913 error ? &ro_u_pipedir : &win32, status);
4914 return;
4917 fcwd_access_t::SetDirHandleFromBufferPointer(upp_cwd_str.Buffer, dir);
4918 h = upp_cwd_hdl;
4919 upp_cwd_hdl = dir;
4920 RtlLeaveCriticalSection (peb.FastPebLock);
4921 /* The handle on init is always a fresh one, not the handle inherited
4922 from the parent process. We always have to close it here. */
4923 NtClose (h);
4927 /* Initialize cygcwd 'muto' for serializing access to cwd info. */
4928 void
4929 cwdstuff::init ()
4931 cwd_lock.init ("cwd_lock");
4933 /* Cygwin processes inherit the cwd from their parent. If the win32 path
4934 buffer is not NULL, the cwd struct is already set up, and we only
4935 have to override the Win32 CWD with ours. */
4936 if (win32.Buffer)
4937 override_win32_cwd (true, SharedUserData.DismountCount);
4938 else
4940 /* Initialize fast_cwd stuff. */
4941 fast_cwd_ptr = (fcwd_access_t **) -1;
4942 fast_cwd_version = FCWD_W7;
4943 /* Initially re-open the cwd to allow POSIX semantics. */
4944 set (NULL, NULL);
4948 /* Chdir and fill out the elements of a cwdstuff struct. */
4950 cwdstuff::set (path_conv *nat_cwd, const char *posix_cwd)
4952 NTSTATUS status;
4953 UNICODE_STRING upath;
4954 OBJECT_ATTRIBUTES attr;
4955 PEB &peb = *NtCurrentTeb ()->Peb;
4956 bool virtual_path = false;
4957 bool unc_path = false;
4958 bool inaccessible_path = false;
4960 /* Here are the problems with using SetCurrentDirectory. Just skip this
4961 comment if you don't like whining.
4963 - SetCurrentDirectory only supports paths of up to MAX_PATH - 1 chars,
4964 including a trailing backslash. That's an absolute restriction, even
4965 in the UNICODE API.
4967 - SetCurrentDirectory fails for directories with strict permissions even
4968 for processes with the SE_BACKUP_NAME privilege enabled. The reason
4969 is apparently that SetCurrentDirectory calls NtOpenFile without the
4970 FILE_OPEN_FOR_BACKUP_INTENT flag set.
4972 - SetCurrentDirectory does not support case-sensitivity.
4974 - Unlinking a cwd fails because SetCurrentDirectory seems to open
4975 directories so that deleting the directory is disallowed.
4977 - SetCurrentDirectory can naturally not work on virtual Cygwin paths
4978 like /proc or /cygdrive.
4980 Nevertheless, doing entirely without SetCurrentDirectory is not really
4981 feasible, because it breaks too many mixed applications using the Win32
4982 API.
4984 Therefore we handle the CWD all by ourselves and just keep the Win32
4985 CWD in sync. However, to avoid surprising behaviour in the Win32 API
4986 when we are in a CWD which is inaccessible as Win32 CWD, we set the
4987 Win32 CWD to a "weird" directory in which all relative filesystem-related
4988 calls fail. */
4990 cwd_lock.acquire ();
4992 if (nat_cwd)
4994 upath = *nat_cwd->get_nt_native_path ();
4995 if (nat_cwd->isspecial ())
4997 virtual_path = true;
4998 /* But allow starting of native apps from /dev if /dev actually
4999 exists on disk. */
5000 if (isdev_dev (nat_cwd->dev))
5002 FILE_BASIC_INFORMATION fbi;
5004 InitializeObjectAttributes (&attr, &upath,
5005 OBJ_CASE_INSENSITIVE | OBJ_INHERIT,
5006 NULL, NULL);
5007 status = NtQueryAttributesFile (&attr, &fbi);
5008 if (status != STATUS_OBJECT_NAME_NOT_FOUND
5009 && status != STATUS_OBJECT_PATH_NOT_FOUND)
5010 virtual_path = false;
5015 /* Memorize old DismountCount before opening the dir. This value is
5016 stored in the FAST_CWD structure. It would be simpler to fetch the
5017 old DismountCount in override_win32_cwd, but Windows also fetches
5018 it before opening the directory handle. It's not quite clear if
5019 that's really required, but since we don't know the side effects of
5020 this action, we better follow Windows' lead. */
5021 ULONG old_dismount_count = SharedUserData.DismountCount;
5022 /* Open a directory handle with FILE_OPEN_FOR_BACKUP_INTENT and with all
5023 sharing flags set. The handle is right now used in exceptions.cc only,
5024 but that might change in future. */
5025 HANDLE h = NULL;
5026 if (!virtual_path)
5028 IO_STATUS_BLOCK io;
5030 if (!nat_cwd)
5032 /* On init, just reopen Win32 CWD with desired access flags.
5033 We can access the PEB without lock, because no other thread
5034 can change the CWD. However, there's a chance that the handle
5035 is NULL, even though CurrentDirectoryName isn't so we have to
5036 be careful. */
5037 if (!peb.ProcessParameters->CurrentDirectoryHandle)
5039 InitializeObjectAttributes (&attr,
5040 &peb.ProcessParameters->CurrentDirectoryName,
5041 OBJ_CASE_INSENSITIVE | OBJ_INHERIT, NULL, NULL);
5043 else
5045 RtlInitUnicodeString (&upath, L"");
5046 InitializeObjectAttributes (&attr,
5047 &upath, OBJ_CASE_INSENSITIVE | OBJ_INHERIT,
5048 peb.ProcessParameters->CurrentDirectoryHandle,
5049 NULL);
5052 else
5053 InitializeObjectAttributes (&attr, &upath,
5054 nat_cwd->objcaseinsensitive () | OBJ_INHERIT,
5055 NULL, NULL);
5056 /* First try without FILE_OPEN_FOR_BACKUP_INTENT, to find out if the
5057 directory is valid for Win32 apps. And, no, we can't just call
5058 SetCurrentDirectory here, since that would potentially break
5059 case-sensitivity. */
5060 status = NtOpenFile (&h, SYNCHRONIZE | FILE_TRAVERSE, &attr, &io,
5061 FILE_SHARE_VALID_FLAGS,
5062 FILE_DIRECTORY_FILE
5063 | FILE_SYNCHRONOUS_IO_NONALERT);
5064 if (status == STATUS_ACCESS_DENIED)
5066 status = NtOpenFile (&h, SYNCHRONIZE | FILE_TRAVERSE, &attr, &io,
5067 FILE_SHARE_VALID_FLAGS,
5068 FILE_DIRECTORY_FILE
5069 | FILE_SYNCHRONOUS_IO_NONALERT
5070 | FILE_OPEN_FOR_BACKUP_INTENT);
5071 inaccessible_path = true;
5073 if (!NT_SUCCESS (status))
5075 /* Called from chdir? Just fail. */
5076 if (nat_cwd)
5078 cwd_lock.release ();
5079 __seterrno_from_nt_status (status);
5080 return -1;
5082 /* Otherwise we're in init and posix hasn't been set yet. Try to
5083 duplicate the handle instead. If that fails, too, set dir to NULL
5084 and carry on. This will at least set posix to some valid path at
5085 process startup, and subsequent getcwd calls don't EFAULT. */
5086 debug_printf ("WARNING: Can't reopen CWD %y '%S', status %y",
5087 peb.ProcessParameters->CurrentDirectoryHandle,
5088 &peb.ProcessParameters->CurrentDirectoryName,
5089 status);
5090 if (!peb.ProcessParameters->CurrentDirectoryHandle
5091 || !DuplicateHandle (GetCurrentProcess (),
5092 peb.ProcessParameters->CurrentDirectoryHandle,
5093 GetCurrentProcess (), &h, 0, TRUE, 0))
5095 cwd_lock.release ();
5096 if (peb.ProcessParameters->CurrentDirectoryHandle)
5097 debug_printf ("...and DuplicateHandle failed with %E.");
5098 dir = NULL;
5102 /* Set new handle. Note that we simply overwrite the old handle here
5103 without closing it. The handle is also used as Win32 CWD handle in
5104 the user parameter block, and it will be closed in override_win32_cwd,
5105 if required. */
5106 dir = h;
5108 if (!nat_cwd)
5110 /* On init, just fetch the Win32 dir from the PEB. We can access
5111 the PEB without lock, because no other thread can change the CWD
5112 at that time. */
5113 PUNICODE_STRING pdir = &peb.ProcessParameters->CurrentDirectoryName;
5114 RtlInitEmptyUnicodeString (&win32,
5115 (PWCHAR) crealloc_abort (win32.Buffer,
5116 pdir->Length
5117 + sizeof (WCHAR)),
5118 pdir->Length + sizeof (WCHAR));
5119 RtlCopyUnicodeString (&win32, pdir);
5121 PWSTR eoBuffer = win32.Buffer + (win32.Length / sizeof (WCHAR));
5122 /* Remove trailing slash if one exists. */
5123 if ((eoBuffer - win32.Buffer) > 3 && eoBuffer[-1] == L'\\')
5124 win32.Length -= sizeof (WCHAR);
5125 if (eoBuffer[0] == L'\\')
5126 unc_path = true;
5128 posix_cwd = NULL;
5130 else
5132 if (!virtual_path) /* don't mangle virtual path. */
5134 /* Convert into Win32 path and compute length. */
5135 if (upath.Buffer[1] == L'?')
5137 upath.Buffer += 4;
5138 upath.Length -= 4 * sizeof (WCHAR);
5139 if (upath.Buffer[1] != L':')
5141 /* UNC path */
5142 upath.Buffer += 2;
5143 upath.Length -= 2 * sizeof (WCHAR);
5144 unc_path = true;
5147 else
5149 /* Path via native NT namespace. Prepend GLOBALROOT prefix
5150 to create a valid Win32 path. */
5151 PWCHAR buf = (PWCHAR) alloca (upath.Length
5152 + ro_u_globalroot.Length
5153 + sizeof (WCHAR));
5154 wcpcpy (wcpcpy (buf, ro_u_globalroot.Buffer), upath.Buffer);
5155 upath.Buffer = buf;
5156 upath.Length += ro_u_globalroot.Length;
5158 PWSTR eoBuffer = upath.Buffer + (upath.Length / sizeof (WCHAR));
5159 /* Remove trailing slash if one exists. */
5160 if ((eoBuffer - upath.Buffer) > 3 && eoBuffer[-1] == L'\\')
5161 upath.Length -= sizeof (WCHAR);
5163 RtlInitEmptyUnicodeString (&win32,
5164 (PWCHAR) crealloc_abort (win32.Buffer,
5165 upath.Length
5166 + sizeof (WCHAR)),
5167 upath.Length + sizeof (WCHAR));
5168 RtlCopyUnicodeString (&win32, &upath);
5169 if (unc_path)
5170 win32.Buffer[0] = L'\\';
5172 /* Make sure it's NUL-terminated. */
5173 win32.Buffer[win32.Length / sizeof (WCHAR)] = L'\0';
5175 /* Set drive_length, used in path conversion, and error code, used in
5176 spawn_guts to decide whether a native Win32 app can be started or not. */
5177 if (virtual_path)
5179 drive_length = 0;
5180 error = ENOTDIR;
5182 else
5184 if (!unc_path)
5185 drive_length = 2;
5186 else
5188 PWCHAR ptr = wcschr (win32.Buffer + 2, L'\\');
5189 if (ptr)
5190 ptr = wcschr (ptr + 1, L'\\');
5191 if (ptr)
5192 drive_length = ptr - win32.Buffer;
5193 else
5194 drive_length = win32.Length / sizeof (WCHAR);
5196 if (inaccessible_path)
5197 error = EACCES;
5198 else if (win32.Length > (MAX_PATH - 2) * sizeof (WCHAR))
5199 error = ENAMETOOLONG;
5200 else
5201 error = 0;
5203 /* Keep the Win32 CWD in sync. Don't check for error, other than for
5204 strace output. Try to keep overhead low. */
5205 override_win32_cwd (!nat_cwd, old_dismount_count);
5207 /* Eventually, create POSIX path if it's not set on entry. */
5208 tmp_pathbuf tp;
5209 if (!posix_cwd)
5211 posix_cwd = (const char *) tp.c_get ();
5212 mount_table->conv_to_posix_path (win32.Buffer, (char *) posix_cwd, 0);
5214 posix = (char *) crealloc_abort (posix, strlen (posix_cwd) + 1);
5215 stpcpy (posix, posix_cwd);
5217 cwd_lock.release ();
5218 return 0;
5221 const char *
5222 cwdstuff::get_error_desc () const
5224 switch (cygheap->cwd.get_error ())
5226 case EACCES:
5227 return "has restricted permissions which render it\n"
5228 "inaccessible as Win32 working directory";
5229 case ENOTDIR:
5230 return "is a virtual Cygwin directory which does\n"
5231 "not exist for a native Windows application";
5232 case ENAMETOOLONG:
5233 return "has a path longer than allowed for a\n"
5234 "Win32 working directory";
5235 default:
5236 break;
5238 /* That shouldn't occur, unless we defined a new error code
5239 in cwdstuff::set. */
5240 return "is not accessible for some unknown reason";
5243 /* Store incoming wchar_t path as current posix cwd. This is called from
5244 setlocale so that the cwd is always stored in the right charset. */
5245 void
5246 cwdstuff::reset_posix (wchar_t *w_cwd)
5248 size_t len = sys_wcstombs (NULL, (size_t) -1, w_cwd);
5249 posix = (char *) crealloc_abort (posix, len + 1);
5250 sys_wcstombs (posix, len + 1, w_cwd);
5253 char *
5254 cwdstuff::get (char *buf, int need_posix, int with_chroot, unsigned ulen)
5256 tmp_pathbuf tp;
5258 errno = 0;
5259 if (ulen)
5260 /* nothing */;
5261 else if (buf == NULL)
5262 ulen = (unsigned) -1;
5263 else
5265 set_errno (EINVAL);
5266 goto out;
5269 cwd_lock.acquire ();
5271 char *tocopy;
5272 if (!need_posix)
5274 tocopy = tp.c_get ();
5275 sys_wcstombs (tocopy, NT_MAX_PATH, win32.Buffer,
5276 win32.Length / sizeof (WCHAR));
5278 else
5279 tocopy = posix;
5281 debug_printf ("posix %s", posix);
5282 if (strlen (tocopy) >= ulen)
5284 set_errno (ERANGE);
5285 buf = NULL;
5287 else
5289 if (!buf)
5290 buf = (char *) malloc (strlen (tocopy) + 1);
5291 strcpy (buf, tocopy);
5292 if (!buf[0]) /* Should only happen when chroot */
5293 strcpy (buf, "/");
5296 cwd_lock.release ();
5298 out:
5299 syscall_printf ("(%s) = cwdstuff::get (%p, %u, %d, %d), errno %d",
5300 buf, buf, ulen, need_posix, with_chroot, errno);
5301 return buf;
5304 /* No need to be reentrant or thread-safe according to SUSv3.
5305 / and \\ are treated equally. Leading drive specifiers are
5306 kept intact as far as it makes sense. Everything else is
5307 POSIX compatible. */
5308 extern "C" char *
5309 basename (char *path)
5311 static char buf[4];
5312 char *c, *d, *bs = path;
5314 if (!path || !*path)
5315 return strcpy (buf, ".");
5316 if (isalpha (path[0]) && path[1] == ':')
5317 bs += 2;
5318 else if (strspn (path, "/\\") > 1)
5319 ++bs;
5320 c = strrchr (bs, '/');
5321 if ((d = strrchr (c ?: bs, '\\')) > c)
5322 c = d;
5323 if (c)
5325 /* Trailing (back)slashes are eliminated. */
5326 while (c && c > bs && c[1] == '\0')
5328 *c = '\0';
5329 c = strrchr (bs, '/');
5330 if ((d = strrchr (c ?: bs, '\\')) > c)
5331 c = d;
5333 if (c && (c > bs || c[1]))
5334 return c + 1;
5336 else if (!bs[0])
5338 stpncpy (buf, path, bs - path);
5339 stpcpy (buf + (bs - path), ".");
5340 return buf;
5342 return path;
5345 /* The differences with the POSIX version above:
5346 - declared in <string.h> (instead of <libgen.h>);
5347 - the argument is never modified, and therefore is marked const;
5348 - the empty string is returned if path is an empty string, "/", or ends
5349 with a trailing slash. */
5350 extern "C" char *
5351 __gnu_basename (const char *path)
5353 static char buf[1];
5354 char *c, *d, *bs = (char *)path;
5356 if (!path || !*path)
5357 return strcpy (buf, "");
5358 if (isalpha (path[0]) && path[1] == ':')
5359 bs += 2;
5360 else if (strspn (path, "/\\") > 1)
5361 ++bs;
5362 c = strrchr (bs, '/');
5363 if ((d = strrchr (c ?: bs, '\\')) > c)
5364 c = d;
5365 if (c)
5366 return c + 1;
5367 else if (!bs[0])
5368 return strcpy (buf, "");
5369 return (char *)path;
5372 /* No need to be reentrant or thread-safe according to SUSv3.
5373 / and \\ are treated equally. Leading drive specifiers and
5374 leading double (back)slashes are kept intact as far as it
5375 makes sense. Everything else is POSIX compatible. */
5376 extern "C" char *
5377 dirname (char *path)
5379 static char buf[4];
5380 char *c, *d, *bs = path;
5382 if (!path || !*path)
5383 return strcpy (buf, ".");
5384 if (isalpha (path[0]) && path[1] == ':')
5385 bs += 2;
5386 else if (strspn (path, "/\\") > 1)
5387 ++bs;
5388 c = strrchr (bs, '/');
5389 if ((d = strrchr (c ?: bs, '\\')) > c)
5390 c = d;
5391 if (c)
5393 /* Trailing (back)slashes are eliminated. */
5394 while (c && c > bs && c[1] == '\0')
5396 *c = '\0';
5397 c = strrchr (bs, '/');
5398 if ((d = strrchr (c ?: bs, '\\')) > c)
5399 c = d;
5401 if (!c)
5402 strcpy (bs, ".");
5403 else if (c > bs)
5405 /* More trailing (back)slashes are eliminated. */
5406 while (c > bs && (*c == '/' || *c == '\\'))
5407 *c-- = '\0';
5409 else
5410 c[1] = '\0';
5412 else
5414 stpncpy (buf, path, bs - path);
5415 stpcpy (buf + (bs - path), ".");
5416 return buf;
5418 return path;