Cygwin: (mostly) drop NT4 and Samba < 3.0 support
[newlib-cygwin.git] / winsup / cygwin / path.cc
blobddea4b3889eb673fe0a2b48303b6f696a9ddfb9a
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 (""),
74 suffix_info (".exe"),
75 suffix_info (NULL)
78 class symlink_info
80 char contents[SYMLINK_MAX + 1];
81 char *ext_here;
82 bool _ext_tacked_on;
83 size_t _path_len;
84 unsigned _path_flags;
85 unsigned _mount_flags;
86 unsigned _pc_flags; /* Relevant pathconv_arg flags from path_conv caller */
87 DWORD _fileattr;
88 bool _issymlink;
89 int _error;
90 dev_t _dev;
91 __mode_t _mode;
92 public:
93 void reset () {
94 clear_content ();
95 path_flags (0);
96 issymlink (true);
97 dev (FH_NADA);
98 /* mount_flags is an incoming value set in path_conv */
100 int check (char *path, const suffix_info *suffixes, fs_info &fs,
101 path_conv_handle &conv_hdl);
102 int set (char *path);
103 bool parse_device (const char *);
104 int check_sysfile (HANDLE h);
105 int check_shortcut (HANDLE h);
106 int check_reparse_point (HANDLE h, bool remote);
107 int check_nfs_symlink (HANDLE h);
108 int posixify (char *srcbuf);
109 bool set_error (int);
111 bool has_ext () const { return ext_here && *ext_here; }
112 char *ext () const { return ext_here; }
113 bool ext_tacked_on () const { return _ext_tacked_on; }
114 void ext_tacked_on (bool _neto) { _ext_tacked_on = _neto; }
115 const char *content () const { return contents; }
116 void clear_content () { contents[0] = '\0'; }
117 size_t path_len () const { return _path_len; }
118 void path_len (size_t _pl) { _path_len = _pl; }
119 unsigned path_flags () const { return _path_flags; }
120 void path_flags (unsigned _pflags) { _path_flags = _pflags; }
121 unsigned mount_flags () const { return _mount_flags; }
122 void mount_flags (unsigned _mflags) { _mount_flags = _mflags; }
123 unsigned pc_flags () const { return _pc_flags; }
124 void pc_flags (unsigned _pflags) { _pc_flags = _pflags; }
125 unsigned fileattr () const { return _fileattr; }
126 void fileattr (unsigned _fflags) { _fileattr = _fflags; }
127 bool issymlink () const { return _issymlink; }
128 void issymlink (bool _is) { _issymlink = _is; }
129 int error () const { return _error; }
130 void error (int _err) { _error = _err; }
131 bool isdevice () const { return _dev != FH_NADA; }
132 dev_t dev () const { return _dev; }
133 void dev (dev_t _ndev) { _dev = _ndev; }
134 __mode_t mode () const { return _mode; }
135 void mode (__mode_t _nmode) { _mode = _nmode; }
138 SRWLOCK NO_COPY cwdstuff::cwd_lock;
140 static const GUID GUID_shortcut
141 = { 0x00021401L, 0, 0, {0xc0, 0, 0, 0, 0, 0, 0, 0x46}};
143 enum
145 WSH_FLAG_IDLIST = 0x01, /* Contains an ITEMIDLIST. */
146 WSH_FLAG_FILE = 0x02, /* Contains a file locator element. */
147 WSH_FLAG_DESC = 0x04, /* Contains a description. */
148 WSH_FLAG_RELPATH = 0x08, /* Contains a relative path. */
149 WSH_FLAG_WD = 0x10, /* Contains a working dir. */
150 WSH_FLAG_CMDLINE = 0x20, /* Contains command line args. */
151 WSH_FLAG_ICON = 0x40 /* Contains a custom icon. */
154 struct win_shortcut_hdr
156 DWORD size; /* Header size in bytes. Must contain 0x4c. */
157 GUID magic; /* GUID of shortcut files. */
158 DWORD flags; /* Content flags. See above. */
160 /* The next fields from attr to icon_no are always set to 0 in Cygwin
161 and U/Win shortcuts. */
162 DWORD attr; /* Target file attributes. */
163 FILETIME ctime; /* These filetime items are never touched by the */
164 FILETIME mtime; /* system, apparently. Values don't matter. */
165 FILETIME atime;
166 DWORD filesize; /* Target filesize. */
167 DWORD icon_no; /* Icon number. */
169 DWORD run; /* Values defined in winuser.h. Use SW_NORMAL. */
170 DWORD hotkey; /* Hotkey value. Set to 0. */
171 DWORD dummy[2]; /* Future extension probably. Always 0. */
174 /* Return non-zero if PATH1 is a prefix of PATH2.
175 Both are assumed to be of the same path style and / vs \ usage.
176 Neither may be "".
177 LEN1 = strlen (PATH1). It's passed because often it's already known.
179 Examples:
180 /foo/ is a prefix of /foo <-- may seem odd, but desired
181 /foo is a prefix of /foo/
182 / is a prefix of /foo/bar
183 / is not a prefix of foo/bar
184 foo/ is a prefix foo/bar
185 /foo is not a prefix of /foobar
189 path_prefix_p (const char *path1, const char *path2, int len1,
190 bool caseinsensitive)
192 /* Handle case where PATH1 has trailing '/' and when it doesn't. */
193 if (len1 > 0 && isdirsep (path1[len1 - 1]))
194 len1--;
196 if (len1 == 0)
197 return isdirsep (path2[0]) && !isdirsep (path2[1]);
199 if (isdirsep (path2[len1]) || path2[len1] == 0 || path1[len1 - 1] == ':')
200 return caseinsensitive ? strncasematch (path1, path2, len1)
201 : !strncmp (path1, path2, len1);
203 return 0;
206 /* Return non-zero if paths match in first len chars.
207 Check is dependent of the case sensitivity setting. */
209 pathnmatch (const char *path1, const char *path2, int len, bool caseinsensitive)
211 return caseinsensitive
212 ? strncasematch (path1, path2, len) : !strncmp (path1, path2, len);
215 /* Return non-zero if paths match. Check is dependent of the case
216 sensitivity setting. */
218 pathmatch (const char *path1, const char *path2, bool caseinsensitive)
220 return caseinsensitive
221 ? strcasematch (path1, path2) : !strcmp (path1, path2);
224 /* TODO: This function is used in mkdir and rmdir to generate correct
225 error messages in case of paths ending in /. or /.. components.
226 Right now, normalize_posix_path will just normalize
227 those components away, which changes the semantics. */
228 bool
229 has_dot_last_component (const char *dir, bool test_dot_dot)
231 /* SUSv3: . and .. are not allowed as last components in various system
232 calls. Don't test for backslash path separator since that's a Win32
233 path following Win32 rules. */
234 const char *last_comp = strchr (dir, '\0');
236 if (last_comp == dir)
237 return false; /* Empty string. Probably shouldn't happen here? */
239 /* Detect run of trailing slashes */
240 while (last_comp > dir && *--last_comp == '/')
241 continue;
243 /* Detect just a run of slashes or a path that does not end with a slash. */
244 if (*last_comp != '.')
245 return false;
247 /* We know we have a trailing dot here. Check that it really is a standalone "."
248 path component by checking that it is at the beginning of the string or is
249 preceded by a "/" */
250 if (last_comp == dir || *--last_comp == '/')
251 return true;
253 /* If we're not checking for '..' we're done. Ditto if we're now pointing to
254 a non-dot. */
255 if (!test_dot_dot || *last_comp != '.')
256 return false; /* either not testing for .. or this was not '..' */
258 /* Repeat previous test for standalone or path component. */
259 return last_comp == dir || last_comp[-1] == '/';
262 /* Normalize a POSIX path.
263 All duplicate /'s, except for 2 leading /'s, are deleted.
264 The result is 0 for success, or an errno error value. */
267 normalize_posix_path (const char *src, char *dst, char *&tail)
269 const char *in_src = src;
270 char *dst_start = dst;
271 bool check_parent = false;
272 syscall_printf ("src %s", src);
274 if ((isdrive (src) && isdirsep (src[2])) || *src == '\\')
275 goto win32_path;
277 tail = dst;
278 if (!isslash (src[0]))
280 if (!cygheap->cwd.get (dst))
281 return get_errno ();
282 tail = strchr (tail, '\0');
283 if (isslash (dst[0]) && isslash (dst[1]))
284 ++dst_start;
285 if (*src == '.')
287 if (tail == dst_start + 1 && *dst_start == '/')
288 tail--;
289 goto sawdot;
291 if (tail > dst && !isslash (tail[-1]))
292 *tail++ = '/';
294 /* Two leading /'s? If so, preserve them. */
295 else if (isslash (src[1]) && !isslash (src[2]))
297 *tail++ = *src++;
298 ++dst_start;
301 while (*src)
303 if (*src == '\\')
304 goto win32_path;
305 /* Strip runs of /'s. */
306 if (!isslash (*src))
307 *tail++ = *src++;
308 else
310 check_parent = true;
311 while (*++src)
313 if (isslash (*src))
314 continue;
316 if (*src != '.')
317 break;
319 sawdot:
320 if (src[1] != '.')
322 if (!src[1])
324 *tail++ = '/';
325 goto done;
327 if (!isslash (src[1]))
328 break;
330 else if (src[2] && !isslash (src[2]))
331 break;
332 else
334 /* According to POSIX semantics all elements of path must
335 exist. To follow it, we must validate our path before
336 removing the trailing component. Check_parent is needed
337 for performance optimization, in order not to verify paths
338 which are already verified. For example this prevents
339 double check in case of foo/bar/../.. */
340 if (check_parent)
342 if (tail > dst_start) /* Don't check for / or // dir. */
344 *tail = 0;
345 debug_printf ("checking %s before '..'", dst);
346 /* In conjunction with native and NFS symlinks,
347 this call can result in a recursion which eats
348 up our tmp_pathbuf buffers. This in turn results
349 in a api_fatal call. To avoid that, we're
350 checking our remaining buffers and return an
351 error code instead. Note that this only happens
352 if the path contains 15 or more relative native/NFS
353 symlinks with a ".." in the target path. */
354 tmp_pathbuf tp;
355 if (!tp.check_usage (4, 3))
356 return ELOOP;
357 path_conv head (dst, PC_SYM_FOLLOW | PC_POSIX);
358 if (!head.exists ())
359 return ENOENT;
360 if (!head.isdir ())
361 return ENOTDIR;
362 /* At this point, dst is a normalized path. If the
363 normalized path created by path_conv does not
364 match the normalized path we're just testing, then
365 the path in dst contains native symlinks. If we
366 just plunge along, removing the previous path
367 component, we may end up removing a symlink from
368 the path and the resulting path will be invalid.
369 So we replace dst with what we found in head
370 instead. All the work replacing symlinks has been
371 done in that path anyway, so why repeat it? */
372 tail = stpcpy (dst, head.get_posix ());
374 check_parent = false;
376 while (tail > dst_start && !isslash (*--tail))
377 continue;
378 src++;
382 *tail++ = '/';
384 if ((tail - dst) >= NT_MAX_PATH)
386 debug_printf ("ENAMETOOLONG = normalize_posix_path (%s)", src);
387 return ENAMETOOLONG;
391 done:
392 *tail = '\0';
394 debug_printf ("%s = normalize_posix_path (%s)", dst, in_src);
395 return 0;
397 win32_path:
398 int err = normalize_win32_path (in_src, dst, tail);
399 if (!err)
400 for (char *p = dst; (p = strchr (p, '\\')); p++)
401 *p = '/';
402 return err ?: -1;
405 inline void
406 path_conv::add_ext_from_sym (symlink_info &sym)
408 if (sym.has_ext ())
410 suffix = path + sym.path_len ();
411 if (sym.ext_tacked_on ())
412 strcpy ((char *) suffix, sym.ext ());
416 static void mkrelpath (char *dst, bool caseinsensitive);
418 static void
419 mkrelpath (char *path, bool caseinsensitive)
421 tmp_pathbuf tp;
422 char *cwd_win32 = tp.c_get ();
423 if (!cygheap->cwd.get (cwd_win32, 0))
424 return;
426 unsigned cwdlen = strlen (cwd_win32);
427 if (!path_prefix_p (cwd_win32, path, cwdlen, caseinsensitive))
428 return;
430 size_t n = strlen (path);
431 if (n < cwdlen)
432 return;
434 char *tail = path;
435 if (n == cwdlen)
436 tail += cwdlen;
437 else
438 tail += isdirsep (cwd_win32[cwdlen - 1]) ? cwdlen : cwdlen + 1;
440 memmove (path, tail, strlen (tail) + 1);
441 if (!*path)
442 strcpy (path, ".");
445 void
446 path_conv::set_posix (const char *path_copy)
448 if (path_copy)
450 size_t n = strlen (path_copy) + 1;
451 char *p = (char *) crealloc_abort ((void *) posix_path, n);
452 posix_path = (const char *) memcpy (p, path_copy, n);
456 static inline void
457 str2uni_cat (UNICODE_STRING &tgt, const char *srcstr)
459 int len = sys_mbstowcs (tgt.Buffer + tgt.Length / sizeof (WCHAR),
460 (tgt.MaximumLength - tgt.Length) / sizeof (WCHAR),
461 srcstr);
462 if (len)
463 tgt.Length += (len - 1) * sizeof (WCHAR);
466 PUNICODE_STRING
467 get_nt_native_path (const char *path, UNICODE_STRING& upath, bool dos)
469 upath.Length = 0;
470 if (path[0] == '/') /* special path w/o NT path representation. */
471 str2uni_cat (upath, path);
472 else if (path[0] != '\\') /* X:\... or relative path. */
474 if (path[1] == ':') /* X:\... */
476 RtlAppendUnicodeStringToString (&upath, &ro_u_natp);
477 str2uni_cat (upath, path);
478 /* The drive letter must be upper case. */
479 upath.Buffer[4] = towupper (upath.Buffer[4]);
480 transform_chars (&upath, 7);
482 else /* relative path */
484 str2uni_cat (upath, path);
485 transform_chars (&upath, 0);
488 else if (path[1] != '\\') /* \Device\... */
489 str2uni_cat (upath, path);
490 else if ((path[2] != '.' && path[2] != '?')
491 || path[3] != '\\') /* \\server\share\... */
493 RtlAppendUnicodeStringToString (&upath, &ro_u_uncp);
494 str2uni_cat (upath, path + 2);
495 transform_chars (&upath, 8);
497 else /* \\.\device or \\?\foo */
499 RtlAppendUnicodeStringToString (&upath, &ro_u_natp);
500 str2uni_cat (upath, path + 4);
502 if (dos)
504 /* Unfortunately we can't just use transform_chars with the tfx_rev_chars
505 table since only leading and trailing spaces and dots are affected.
506 So we step to every backslash and fix surrounding dots and spaces.
507 That makes these broken filesystems a bit slower, but, hey. */
508 PWCHAR cp = upath.Buffer + 7;
509 PWCHAR cend = upath.Buffer + upath.Length / sizeof (WCHAR);
510 while (++cp < cend)
511 if (*cp == L'\\')
513 PWCHAR ccp = cp - 1;
514 while (*ccp == L'.' || *ccp == L' ')
515 *ccp-- |= 0xf000;
516 while (cp[1] == L' ')
517 *++cp |= 0xf000;
519 while (*--cp == L'.' || *cp == L' ')
520 *cp |= 0xf000;
522 return &upath;
525 /* Handle with extrem care! Only used in a certain instance in try_to_bin.
526 Every other usage needs a careful check. */
527 void
528 path_conv::set_nt_native_path (PUNICODE_STRING new_path)
530 wide_path = (PWCHAR) crealloc_abort (wide_path, new_path->MaximumLength);
531 memcpy (wide_path, new_path->Buffer, new_path->Length);
532 uni_path.Length = new_path->Length;
533 uni_path.MaximumLength = new_path->MaximumLength;
534 uni_path.Buffer = wide_path;
537 /* If suffix is not NULL, append the suffix string verbatim.
538 This is used by fhandler_mqueue::mq_open to append an NTFS stream suffix. */
539 PUNICODE_STRING
540 path_conv::get_nt_native_path (PUNICODE_STRING suffix)
542 PUNICODE_STRING res;
543 if (wide_path)
544 res = &uni_path;
545 else if (!path)
546 res = NULL;
547 else
549 uni_path.Length = 0;
550 uni_path.MaximumLength = (strlen (path) + 10) * sizeof (WCHAR);
551 if (suffix)
552 uni_path.MaximumLength += suffix->Length;
553 wide_path = (PWCHAR) cmalloc_abort (HEAP_STR, uni_path.MaximumLength);
554 uni_path.Buffer = wide_path;
555 ::get_nt_native_path (path, uni_path, has_dos_filenames_only ());
556 if (suffix)
557 RtlAppendUnicodeStringToString (&uni_path, suffix);
558 res = &uni_path;
560 return res;
563 PWCHAR
564 path_conv::get_wide_win32_path (PWCHAR wc)
566 get_nt_native_path ();
567 if (!wide_path)
568 return NULL;
569 wcpcpy (wc, wide_path);
570 if (wc[1] == L'?')
571 wc[1] = L'\\';
572 return wc;
575 static DWORD
576 getfileattr (const char *path, bool caseinsensitive) /* path has to be always absolute. */
578 tmp_pathbuf tp;
579 UNICODE_STRING upath;
580 OBJECT_ATTRIBUTES attr;
581 FILE_BASIC_INFORMATION fbi;
582 NTSTATUS status;
583 IO_STATUS_BLOCK io;
585 tp.u_get (&upath);
586 InitializeObjectAttributes (&attr, &upath,
587 caseinsensitive ? OBJ_CASE_INSENSITIVE : 0,
588 NULL, NULL);
589 get_nt_native_path (path, upath, false);
591 status = NtQueryAttributesFile (&attr, &fbi);
592 if (NT_SUCCESS (status))
593 return fbi.FileAttributes;
595 if (status != STATUS_OBJECT_NAME_NOT_FOUND
596 && status != STATUS_NO_SUCH_FILE) /* File not found on 9x share */
598 /* File exists but access denied. Try to get attribute through
599 directory query. */
600 UNICODE_STRING dirname, basename;
601 HANDLE dir;
602 FILE_BOTH_DIR_INFORMATION fdi;
604 RtlSplitUnicodePath (&upath, &dirname, &basename);
605 InitializeObjectAttributes (&attr, &dirname,
606 caseinsensitive ? OBJ_CASE_INSENSITIVE : 0,
607 NULL, NULL);
608 status = NtOpenFile (&dir, SYNCHRONIZE | FILE_LIST_DIRECTORY,
609 &attr, &io, FILE_SHARE_VALID_FLAGS,
610 FILE_SYNCHRONOUS_IO_NONALERT
611 | FILE_OPEN_NO_RECALL
612 | FILE_OPEN_FOR_BACKUP_INTENT
613 | FILE_DIRECTORY_FILE);
614 if (NT_SUCCESS (status))
616 status = NtQueryDirectoryFile (dir, NULL, NULL, 0, &io,
617 &fdi, sizeof fdi,
618 FileBothDirectoryInformation,
619 TRUE, &basename, TRUE);
620 NtClose (dir);
621 if (NT_SUCCESS (status) || status == STATUS_BUFFER_OVERFLOW)
622 return fdi.FileAttributes;
625 SetLastError (RtlNtStatusToDosError (status));
626 return INVALID_FILE_ATTRIBUTES;
629 /* Convert an arbitrary path SRC to a pure Win32 path, suitable for
630 passing to Win32 API routines.
632 If an error occurs, `error' is set to the errno value.
633 Otherwise it is set to 0.
635 follow_mode values:
636 SYMLINK_FOLLOW - convert to PATH symlink points to
637 SYMLINK_NOFOLLOW - convert to PATH of symlink itself
638 SYMLINK_IGNORE - do not check PATH for symlinks
639 SYMLINK_CONTENTS - just return symlink contents
642 /* TODO: This implementation is only preliminary. For internal
643 purposes it's necessary to have a path_conv::check function which
644 takes a UNICODE_STRING src path, otherwise we waste a lot of time
645 for converting back and forth. The below implementation does
646 realy nothing but converting to char *, until path_conv handles
647 wide-char paths directly. */
648 void
649 path_conv::check (const UNICODE_STRING *src, unsigned opt,
650 const suffix_info *suffixes)
652 tmp_pathbuf tp;
653 char *path = tp.c_get ();
655 user_shared->warned_msdos = true;
656 sys_wcstombs (path, NT_MAX_PATH, src->Buffer, src->Length / sizeof (WCHAR));
657 path_conv::check (path, opt, suffixes);
660 void
661 path_conv::check (const char *src, unsigned opt,
662 const suffix_info *suffixes)
664 /* The tmp_buf array is used when expanding symlinks. It is NT_MAX_PATH * 2
665 in length so that we can hold the expanded symlink plus a trailer. */
666 tmp_pathbuf tp;
667 char *path_copy = tp.c_get ();
668 char *pathbuf = tp.c_get ();
669 char *tmp_buf = tp.t_get ();
670 char *THIS_path = tp.c_get ();
671 symlink_info sym;
672 bool need_directory = 0;
673 bool add_ext = false;
674 bool is_relpath;
675 char *tail, *path_end;
677 #if 0
678 static path_conv last_path_conv;
679 static char last_src[CYG_MAX_PATH];
681 if (*last_src && strcmp (last_src, src) == 0)
683 *this = last_path_conv;
684 return;
686 #endif
688 __try
690 int loop = 0;
691 mount_flags = 0;
692 path_flags = 0;
693 suffix = NULL;
694 fileattr = INVALID_FILE_ATTRIBUTES;
695 caseinsensitive = OBJ_CASE_INSENSITIVE;
696 if (wide_path)
697 cfree (wide_path);
698 wide_path = NULL;
699 if (path)
701 cfree (modifiable_path ());
702 path = NULL;
704 close_conv_handle ();
705 fs.clear ();
706 if (posix_path)
708 cfree ((void *) posix_path);
709 posix_path = NULL;
711 int component = 0; // Number of translated components
713 if (!(opt & PC_NULLEMPTY))
714 error = 0;
715 else if (!*src)
717 error = ENOENT;
718 return;
721 bool is_msdos = false;
722 /* This loop handles symlink expansion. */
723 for (;;)
725 is_relpath = !isabspath (src);
726 error = normalize_posix_path (src, path_copy, tail);
727 if (error > 0)
728 return;
729 if (error < 0)
731 if (component == 0)
732 is_msdos = true;
733 error = 0;
736 /* Detect if the user was looking for a directory. We have to strip
737 the trailing slash initially while trying to add extensions but
738 take it into account during processing */
739 if (tail > path_copy + 2 && isslash (tail[-1]))
741 need_directory = 1;
742 *--tail = '\0';
744 path_end = tail;
746 /* Scan path_copy from right to left looking either for a symlink
747 or an actual existing file. If an existing file is found, just
748 return. If a symlink is found, exit the for loop.
749 Also: be careful to preserve the errno returned from
750 symlink.check as the caller may need it. */
751 /* FIXME: Do we have to worry about multiple \'s here? */
752 component = 0; // Number of translated components
753 sym.clear_content ();
754 sym.path_flags (0);
756 int symlen = 0;
758 /* Make sure to check certain flags on last component only. */
759 for (unsigned pc_flags = opt & (PC_NO_ACCESS_CHECK | PC_KEEP_HANDLE
760 | PC_SYM_FOLLOW | PC_SYM_NOFOLLOW_REP);
762 pc_flags = opt & (PC_SYM_FOLLOW | PC_SYM_NOFOLLOW_REP))
764 const suffix_info *suff;
765 char *full_path;
767 /* Don't allow symlink.check to set anything in the path_conv
768 class if we're working on an inner component of the path */
769 if (component)
771 suff = NULL;
772 full_path = pathbuf;
774 else
776 suff = suffixes;
777 full_path = THIS_path;
780 retry_fs_via_processfd:
782 /* Convert to native path spec sans symbolic link info. */
783 unsigned mnt_flags;
784 error = mount_table->conv_to_win32_path (path_copy, full_path,
785 dev,
786 &mnt_flags);
788 if (error)
789 return;
791 sym.pc_flags (pc_flags);
792 sym.mount_flags (mnt_flags);
794 if (!dev.exists ())
796 error = ENXIO;
797 return;
800 if (iscygdrive_dev (dev))
802 if (!component)
803 fileattr = FILE_ATTRIBUTE_DIRECTORY
804 | FILE_ATTRIBUTE_READONLY;
805 else
807 fileattr = getfileattr (THIS_path,
808 sym.mount_flags () & MOUNT_NOPOSIX);
809 dev = FH_FS;
811 goto out;
813 else if (isdev_dev (dev))
815 /* Make sure that the path handling goes on as with FH_FS. */
817 else if (isvirtual_dev (dev))
819 /* FIXME: Calling build_fhandler here is not the right way to
820 handle this. */
821 fhandler_virtual *fh = (fhandler_virtual *)
822 build_fh_dev (dev, path_copy);
823 virtual_ftype_t file_type;
824 if (!fh)
825 file_type = virt_none;
826 else
828 file_type = fh->exists ();
829 if (file_type == virt_symlink
830 || file_type == virt_fdsymlink)
832 fh->fill_filebuf ();
833 symlen = sym.set (fh->get_filebuf ());
835 else if (file_type == virt_fsdir && dev == FH_PROCESSFD)
837 /* FIXME: This is YA bad hack to workaround that
838 we're checking for isvirtual_dev at this point.
839 This should only happen if the file is actually
840 a virtual file, and NOT already if the preceeding
841 path components constitute a virtual file.
843 Anyway, what we do here is this: If the descriptor
844 symlink points to a dir, and if there are trailing
845 path components, it's actually pointing somewhere
846 else. The format_process_fd function returns the
847 full path, resolved symlink plus trailing path
848 components, in its filebuf. This is a POSIX path
849 we know nothing about, so we have to convert it to
850 native again, calling conv_to_win32_path. Since
851 basically nothing happened yet, just copy it over
852 into full_path and jump back to the
853 conv_to_win32_path call. What a mess. */
854 stpcpy (path_copy, fh->get_filebuf ());
855 delete fh;
856 goto retry_fs_via_processfd;
858 else if (file_type == virt_none && dev == FH_PROCESSFD)
860 error = get_errno ();
861 if (error)
863 delete fh;
864 return;
867 delete fh;
869 switch (file_type)
871 case virt_directory:
872 case virt_rootdir:
873 if (component == 0)
874 fileattr = FILE_ATTRIBUTE_DIRECTORY;
875 break;
876 case virt_file:
877 if (component == 0)
878 fileattr = 0;
879 break;
880 case virt_fdsymlink:
881 /* Allow open/linkat to do the right thing. */
882 if (opt & PC_SYM_NOFOLLOW_PROCFD)
884 opt &= ~PC_SYM_FOLLOW;
885 sym.path_flags (sym.path_flags ()
886 | PATH_RESOLVE_PROCFD);
888 fallthrough;
889 case virt_symlink:
890 goto is_virtual_symlink;
891 case virt_pipe:
892 if (component == 0)
894 fileattr = 0;
895 dev.parse (FH_PIPE);
897 break;
898 case virt_socket:
899 if (component == 0)
901 fileattr = 0;
902 dev.parse (FH_SOCKET);
904 break;
905 case virt_fsdir:
906 case virt_fsfile:
907 /* Access to real file or directory via block device
908 entry in /proc/sys. Convert to real file and go with
909 the flow. */
910 dev.parse (FH_FS);
911 goto is_fs_via_procsys;
912 case virt_blk:
913 /* Block special device. Convert to a /dev/sd* like
914 block device unless the trailing slash has been
915 requested. In this case, the target is the root
916 directory of the filesystem on this block device.
917 So we convert this to a real file and attach the
918 backslash. */
919 if (component == 0)
921 fileattr = FILE_ATTRIBUTE_DEVICE;
922 if (!need_directory)
923 /* Use a /dev/sd* device number > /dev/sddx.
924 FIXME: Define a new major DEV_ice number. */
925 dev.parse (DEV_SD_HIGHPART_END, 9999);
926 else
928 dev.parse (FH_FS);
929 strcat (full_path, "\\");
930 fileattr |= FILE_ATTRIBUTE_DIRECTORY;
932 goto out;
934 break;
935 case virt_chr:
936 if (component == 0)
937 fileattr = FILE_ATTRIBUTE_DEVICE;
938 break;
939 default:
940 if (component == 0)
941 fileattr = INVALID_FILE_ATTRIBUTES;
942 goto virtual_component_retry;
944 if (component == 0 || dev != FH_NETDRIVE)
945 mount_flags |= MOUNT_RO;
946 goto out;
948 /* devn should not be a device. If it is, then stop parsing. */
949 else if (dev != FH_FS)
951 fileattr = 0;
952 mount_flags = sym.mount_flags ();
953 if (component)
955 error = ENOTDIR;
956 return;
958 goto out; /* Found a device. Stop parsing. */
961 /* If path is only a drivename, Windows interprets it as the
962 current working directory on this drive instead of the root
963 dir which is what we want. So we need the trailing backslash
964 in this case. */
965 if (full_path[0] && full_path[1] == ':' && full_path[2] == '\0')
967 full_path[2] = '\\';
968 full_path[3] = '\0';
971 /* If the incoming path was given in DOS notation, always treat
972 it as caseinsensitive,noacl path. This must be set before
973 calling sym.check, otherwise the path is potentially treated
974 casesensitive. */
975 if (is_msdos)
976 sym.mount_flags (sym.mount_flags ()
977 | MOUNT_NOPOSIX | MOUNT_NOACL);
979 is_fs_via_procsys:
981 symlen = sym.check (full_path, suff, fs, conv_handle);
983 is_virtual_symlink:
985 if (sym.isdevice ())
987 if (component)
989 error = ENOTDIR;
990 return;
992 dev.parse (sym.dev ());
993 dev.setfs (1);
994 dev.mode (sym.mode ());
995 fileattr = sym.fileattr ();
996 goto out;
999 if (sym.path_flags () & PATH_SOCKET)
1001 if (component)
1003 error = ENOTDIR;
1004 return;
1006 fileattr = sym.fileattr ();
1007 #ifdef __WITH_AF_UNIX
1008 dev.parse ((sym.path_flags () & PATH_REP) ? FH_UNIX : FH_LOCAL);
1009 #else
1010 dev.parse (FH_LOCAL);
1011 #endif /* __WITH_AF_UNIX */
1012 dev.setfs (1);
1013 mount_flags = sym.mount_flags ();
1014 path_flags = sym.path_flags ();
1015 goto out;
1018 if (!component)
1020 /* Make sure that /dev always exists. */
1021 fileattr = isdev_dev (dev) ? FILE_ATTRIBUTE_DIRECTORY
1022 : sym.fileattr ();
1023 mount_flags = sym.mount_flags ();
1024 path_flags = sym.path_flags ();
1026 else if (isdev_dev (dev))
1028 /* If we're looking for a non-existing file below /dev,
1029 make sure that the device type is converted to FH_FS, so
1030 that subsequent code handles the file correctly. Unless
1031 /dev itself doesn't exist on disk. In that case /dev
1032 is handled as virtual filesystem, and virtual filesystems
1033 are read-only. The PC_KEEP_HANDLE check allows to check
1034 for a call from an informational system call. In that
1035 case we just stick to ENOENT, and the device type doesn't
1036 matter anyway. */
1037 if (sym.error () == ENOENT && !(opt & PC_KEEP_HANDLE))
1038 sym.error (EROFS);
1039 else
1040 dev = FH_FS;
1043 /* If symlink.check found an existing non-symlink file, then
1044 it sets the appropriate flag. It also sets any suffix found
1045 into `ext_here'. */
1046 if (!sym.issymlink ()
1047 && sym.fileattr () != INVALID_FILE_ATTRIBUTES)
1049 error = sym.error ();
1050 if (component == 0)
1051 add_ext = true;
1052 else if (!(sym.fileattr () & FILE_ATTRIBUTE_DIRECTORY))
1054 error = ENOTDIR;
1055 goto out;
1057 goto out; // file found
1059 /* Found a symlink if symlen > 0 or short-circuited a native
1060 symlink or junction point if symlen < 0.
1061 If symlen > 0 and component == 0, then the src path itself
1062 was a symlink. If !follow_mode then we're done. Otherwise
1063 we have to insert the path found into the full path that we
1064 are building and perform all of these operations again on the
1065 newly derived path. */
1066 else if (symlen)
1068 /* if symlen is negativ, the actual native symlink or
1069 junction point is an inner path component. Just fix up
1070 symlen to be positive and don't try any PC_SYM_FOLLOW
1071 handling. */
1072 if (symlen < 0)
1073 symlen = -symlen;
1074 else if (component == 0
1075 && (!(opt & PC_SYM_FOLLOW)
1076 || (is_winapi_reparse_point ()
1077 && (opt & PC_SYM_NOFOLLOW_REP))))
1079 /* Usually a trailing slash requires to follow a symlink,
1080 even with PC_SYM_NOFOLLOW. The reason is that "foo/"
1081 is equivalent to "foo/." so the symlink is in fact not
1082 the last path component.
1084 PC_SYM_NOFOLLOW_DIR is used to indicate that the
1085 last path component is the target symlink and the
1086 trailing slash is supposed to be ignored. */
1087 if (!need_directory || (opt & PC_SYM_NOFOLLOW_DIR))
1089 /* last component of path is a symlink. */
1090 set_symlink (symlen);
1091 /* make sure not to set errno to ENOTDIR. */
1092 need_directory = 0;
1093 if (opt & PC_SYM_CONTENTS)
1095 strcpy (THIS_path, sym.content ());
1096 goto out;
1098 add_ext = true;
1099 goto out;
1102 /* Following a symlink we can't trust the collected
1103 filesystem information any longer. */
1104 fs.clear ();
1105 /* Close handle, if we have any. Otherwise we're collecting
1106 handles while following symlinks. */
1107 conv_handle.close ();
1108 break;
1110 else if (sym.error () && sym.error () != ENOENT)
1112 error = sym.error ();
1113 goto out;
1115 /* No existing file found. */
1117 virtual_component_retry:
1118 /* Find the new "tail" of the path, e.g. in '/for/bar/baz',
1119 /baz is the tail. */
1120 if (tail != path_end)
1121 *tail = '/';
1122 while (--tail > path_copy + 1 && *tail != '/') {}
1123 /* Exit loop if there is no tail or we are at the
1124 beginning of a UNC path */
1125 if (tail <= path_copy + 1)
1126 goto out; // all done
1128 /* Haven't found an existing pathname component yet.
1129 Pinch off the tail and try again. */
1130 *tail = '\0';
1131 component++;
1134 /* Arrive here if above loop detected a symlink. */
1135 if (++loop > SYMLOOP_MAX)
1137 error = ELOOP; // Eep.
1138 return;
1141 /* Place the link content, possibly with head and/or tail,
1142 in tmp_buf */
1144 char *headptr;
1145 if (isabspath (sym.content ()))
1146 headptr = tmp_buf; /* absolute path */
1147 else
1149 /* Copy the first part of the path (with ending /) and point to
1150 the end. */
1151 char *prevtail = tail;
1152 while (--prevtail > path_copy && *prevtail != '/') {}
1153 int headlen = prevtail - path_copy + 1;;
1154 memcpy (tmp_buf, path_copy, headlen);
1155 headptr = &tmp_buf[headlen];
1158 /* Make sure there is enough space */
1159 if (headptr + symlen >= tmp_buf + (2 * NT_MAX_PATH))
1161 too_long:
1162 error = ENAMETOOLONG;
1163 set_path ("::ENAMETOOLONG::");
1164 return;
1167 /* Copy the symlink contents to the end of tmp_buf.
1168 Convert slashes. */
1169 for (const char *p = sym.content (); *p; p++)
1170 *headptr++ = *p == '\\' ? '/' : *p;
1171 *headptr = '\0';
1173 /* Copy any tail component (with the 0) */
1174 if (tail++ < path_end)
1176 /* Add a slash if needed. There is space. */
1177 if (*(headptr - 1) != '/')
1178 *headptr++ = '/';
1179 int taillen = path_end - tail + 1;
1180 if (headptr + taillen > tmp_buf + (2 * NT_MAX_PATH))
1181 goto too_long;
1182 memcpy (headptr, tail, taillen);
1185 /* Evaluate everything all over again. */
1186 src = tmp_buf;
1189 if (!(opt & PC_SYM_CONTENTS))
1190 add_ext = true;
1192 out:
1193 set_path (THIS_path);
1194 if (add_ext)
1195 add_ext_from_sym (sym);
1196 if (dev == FH_NETDRIVE && component)
1198 /* This case indicates a non-existant resp. a non-retrievable
1199 share. This happens for instance if the share is a printer.
1200 In this case the path must not be treated like a FH_NETDRIVE,
1201 but like a FH_FS instead, so the usual open call for files
1202 is used on it. */
1203 dev.parse (FH_FS);
1205 else if (isproc_dev (dev) && fileattr == INVALID_FILE_ATTRIBUTES)
1207 /* FIXME: Usually we don't set error to ENOENT if a file doesn't
1208 exist. This is typically indicated by the fileattr content.
1209 So, why here? The downside is that cygwin_conv_path just gets
1210 an error for these paths so it reports the error back to the
1211 application. Unlike in all other cases of non-existant files,
1212 for which check doesn't set error, so cygwin_conv_path just
1213 returns the path, as intended. */
1214 error = ENOENT;
1215 return;
1217 else if (!need_directory || error)
1218 /* nothing to do */;
1219 else if (fileattr == INVALID_FILE_ATTRIBUTES)
1220 /* Reattach trailing dirsep in native path. */
1221 strcat (modifiable_path (), "\\");
1222 else if (fileattr & FILE_ATTRIBUTE_DIRECTORY)
1223 path_flags &= ~PATH_SYMLINK;
1224 else
1226 debug_printf ("%s is a non-directory", path);
1227 error = ENOTDIR;
1228 return;
1231 /* Restore last path component */
1232 if (tail < path_end && tail > path_copy + 1)
1233 *tail = '/';
1235 if (dev.isfs ())
1237 /* If FS hasn't been checked already in symlink_info::check,
1238 do so now. */
1239 if (fs.inited ()|| fs.update (get_nt_native_path (), NULL))
1241 /* Incoming DOS paths are treated like DOS paths in native
1242 Windows applications. No ACLs, just default settings. */
1243 if (is_msdos)
1244 fs.has_acls (false);
1245 debug_printf ("this->path(%s), has_acls(%d)",
1246 path, fs.has_acls ());
1247 /* CV: We could use this->has_acls() but I want to make sure that
1248 we don't forget that the MOUNT_NOACL flag must be taken into
1249 account here. */
1250 if (!(mount_flags & MOUNT_NOACL) && fs.has_acls ())
1251 set_exec (0); /* We really don't know if this is executable or
1252 not here but set it to not executable since
1253 it will be figured out later by anything
1254 which cares about this. */
1256 /* If the FS has been found to have unreliable inodes, note
1257 that in mount_flags. */
1258 if (!fs.hasgood_inode ())
1259 mount_flags |= MOUNT_IHASH;
1260 /* If the OS is caseinsensitive or the FS is caseinsensitive,
1261 don't handle path casesensitive. */
1262 if (cygwin_shared->obcaseinsensitive || fs.caseinsensitive ())
1263 mount_flags |= MOUNT_NOPOSIX;
1264 caseinsensitive = (mount_flags & MOUNT_NOPOSIX)
1265 ? OBJ_CASE_INSENSITIVE : 0;
1266 if (exec_state () != dont_know_if_executable)
1267 /* ok */;
1268 else if (isdir ())
1269 set_exec (1);
1270 else if (issymlink () || issocket ())
1271 set_exec (0);
1273 /* FIXME: bad hack alert!!! We need a better solution */
1274 if (!strncmp (path_copy, MQ_PATH, MQ_LEN) && path_copy[MQ_LEN])
1275 dev.parse (FH_MQUEUE);
1278 if (opt & PC_NOFULL)
1280 if (is_relpath)
1282 mkrelpath (this->modifiable_path (), !!caseinsensitive);
1283 /* Invalidate wide_path so that wide relpath can be created
1284 in later calls to get_nt_native_path or get_wide_win32_path. */
1285 if (wide_path)
1286 cfree (wide_path);
1287 wide_path = NULL;
1289 if (need_directory)
1291 size_t n = strlen (this->path);
1292 /* Do not add trailing \ to UNC device names like \\.\a: */
1293 if (this->path[n - 1] != '\\' &&
1294 (strncmp (this->path, "\\\\.\\", 4) != 0))
1296 this->modifiable_path ()[n] = '\\';
1297 this->modifiable_path ()[n + 1] = '\0';
1302 if (opt & PC_OPEN)
1303 path_flags |= PATH_OPEN;
1305 if (opt & PC_CTTY)
1306 path_flags |= PATH_CTTY;
1308 if (opt & PC_POSIX)
1309 set_posix (path_copy);
1311 #if 0
1312 if (!error)
1314 last_path_conv = *this;
1315 strcpy (last_src, src);
1317 #endif
1319 __except (NO_ERROR)
1321 error = EFAULT;
1323 __endtry
1326 struct pc_flat
1328 path_conv pc;
1329 HANDLE hdl;
1330 size_t name_len;
1331 size_t posix_len;
1332 char data[0];
1335 void *
1336 path_conv::serialize (HANDLE h, unsigned int &n) const
1338 pc_flat *pcf;
1339 size_t nlen = 0, plen = 0;
1340 char *p;
1342 if (path)
1343 nlen = strlen (path) + 1;
1344 if (posix_path)
1345 plen = strlen (posix_path) + 1;
1346 n = sizeof (pc_flat) + nlen + plen;
1347 pcf = (pc_flat *) cmalloc (HEAP_COMMUNE, n);
1348 if (!pcf)
1350 n = 0;
1351 return NULL;
1353 memcpy ((void *) &pcf->pc, this, sizeof *this);
1354 pcf->hdl = h;
1355 pcf->name_len = nlen;
1356 pcf->posix_len = plen;
1357 p = pcf->data;
1358 if (nlen)
1359 p = stpcpy (p, path) + 1;
1360 if (plen)
1361 stpcpy (p, posix_path);
1362 return pcf;
1365 HANDLE
1366 path_conv::deserialize (void *bufp)
1368 pc_flat *pcf = (pc_flat *) bufp;
1369 char *p;
1370 HANDLE ret;
1372 memcpy ((void *) this, &pcf->pc, sizeof *this);
1373 wide_path = uni_path.Buffer = NULL;
1374 uni_path.MaximumLength = uni_path.Length = 0;
1375 path = posix_path = NULL;
1376 p = pcf->data;
1377 if (pcf->name_len)
1379 set_path (p);
1380 p += pcf->name_len;
1382 if (pcf->posix_len)
1383 set_posix (p);
1384 dev.parse (pcf->pc.dev);
1385 ret = pcf->hdl;
1386 cfree (bufp);
1387 return ret;
1390 path_conv::~path_conv ()
1392 if (posix_path)
1394 cfree ((void *) posix_path);
1395 posix_path = NULL;
1397 if (path)
1399 cfree (modifiable_path ());
1400 path = NULL;
1402 if (wide_path)
1404 cfree (wide_path);
1405 wide_path = NULL;
1407 close_conv_handle ();
1410 bool
1411 path_conv::is_binary ()
1413 tmp_pathbuf tp;
1414 PWCHAR bintest = tp.w_get ();
1415 DWORD bin;
1417 return GetBinaryTypeW (get_wide_win32_path (bintest), &bin)
1418 && (bin == SCS_32BIT_BINARY || bin == SCS_64BIT_BINARY);
1421 /* Helper function to fill the fai datastructure for a file. */
1422 NTSTATUS
1423 file_get_fai (HANDLE h, PFILE_ALL_INFORMATION pfai)
1425 NTSTATUS status;
1426 IO_STATUS_BLOCK io;
1428 /* Some FSes (Netapps) don't implement FileNetworkOpenInformation. */
1429 status = NtQueryInformationFile (h, &io, pfai, sizeof *pfai,
1430 FileAllInformation);
1431 if (likely (status == STATUS_BUFFER_OVERFLOW))
1432 status = STATUS_SUCCESS;
1433 /* Filesystems with broken FileAllInformation exist, too. See the thread
1434 starting with https://cygwin.com/ml/cygwin/2016-07/msg00350.html. */
1435 else if (!NT_SUCCESS (status) && status != STATUS_ACCESS_DENIED)
1437 memset (pfai, 0, sizeof *pfai);
1438 status = NtQueryInformationFile (h, &io, &pfai->BasicInformation,
1439 sizeof pfai->BasicInformation,
1440 FileBasicInformation);
1441 if (NT_SUCCESS (status))
1443 /* The return value of FileInternalInformation is largely ignored.
1444 We only make absolutely sure the inode number is set to 0 in
1445 case it fails. */
1446 status = NtQueryInformationFile (h, &io, &pfai->InternalInformation,
1447 sizeof pfai->InternalInformation,
1448 FileInternalInformation);
1449 if (!NT_SUCCESS (status))
1450 pfai->InternalInformation.IndexNumber.QuadPart = 0LL;
1451 status = NtQueryInformationFile (h, &io, &pfai->StandardInformation,
1452 sizeof pfai->StandardInformation,
1453 FileStandardInformation);
1456 return status;
1459 /* Normalize a Win32 path.
1460 /'s are converted to \'s in the process.
1461 All duplicate \'s, except for 2 leading \'s, are deleted.
1463 The result is 0 for success, or an errno error value.
1464 FIXME: A lot of this should be mergeable with the POSIX critter. */
1466 normalize_win32_path (const char *src, char *dst, char *&tail)
1468 const char *src_start = src;
1469 const char *dst_start = dst;
1470 bool beg_src_slash = isdirsep (src[0]);
1472 tail = dst;
1473 /* Skip Win32 long path name prefix and NT object directory prefix. */
1474 if (beg_src_slash && (src[1] == '?' || isdirsep (src[1]))
1475 && src[2] == '?' && isdirsep (src[3]))
1477 src += 4;
1478 if (isdrive (src) && (isdirsep (src[2]) || !src[2]))
1479 beg_src_slash = false;
1480 else if (!strncmp (src, "UNC", 3) && isdirsep (src[3]))
1481 /* native UNC path */
1482 src += 2; /* Fortunately the first char is not copied... */
1483 else
1484 return EINVAL;
1486 if (beg_src_slash && isdirsep (src[1]))
1488 if (isdirsep (src[2]))
1490 /* More than two slashes are just folded into one. */
1491 src += 2;
1492 while (isdirsep (src[1]))
1493 ++src;
1495 else
1497 /* Two slashes start a network or device path. */
1498 *tail++ = '\\';
1499 src++;
1500 if (src[1] == '.' && isdirsep (src[2]))
1502 *tail++ = '\\';
1503 *tail++ = '.';
1504 src += 2;
1507 dst = tail;
1508 /* If backslash is missing in src, add one. */
1509 if (!isdirsep (src[0]))
1510 *tail++ = '\\';
1512 if (tail == dst_start)
1514 if (isdrive (src))
1516 /* Always convert drive letter to uppercase for case sensitivity. */
1517 *tail++ = cyg_toupper (*src++);
1518 *tail++ = *src++;
1519 dst = tail;
1520 /* If backslash is missing in src, add one. */
1521 if (!isdirsep (src[0]))
1522 *tail++ = '\\';
1524 else if (*src != '/')
1526 /* Make sure dst points to the rightmost backslash which must not
1527 be backtracked over during ".." evaluation. This is either
1528 the backslash after the network path prefix (i.e. "\\") or
1529 the backslash after a drive letter (i.e. C:\"). */
1530 if (beg_src_slash)
1532 tail += cygheap->cwd.get_drive (dst);
1533 /* network path, drive == '\\\\'? Decrement tail to avoid
1534 triple backslash in output. */
1535 if (dst[0] == '\\')
1536 --tail;
1537 dst = tail;
1539 else if (cygheap->cwd.get (dst, 0))
1541 tail = strchr (tail, '\0');
1542 if (tail[-1] != '\\')
1543 *tail++ = '\\';
1544 ++dst;
1545 if (dst[1] == '\\')
1546 ++dst;
1548 else
1549 return get_errno ();
1553 /* At this point dst points to the first backslash, even if it only gets
1554 written in the first iteration of the following loop. Backing up to
1555 handle ".." components can not underrun that border (thus avoiding
1556 subsequent buffer underruns with fatal results). */
1557 while (*src)
1559 /* Strip duplicate /'s. */
1560 if (isdirsep (src[0]) && isdirsep (src[1]))
1561 src++;
1562 /* Ignore "./". */
1563 else if (src[0] == '.' && isdirsep (src[1])
1564 && (src == src_start || isdirsep (src[-1])))
1566 src += 2;
1567 /* Skip /'s to the next path component. */
1568 while (isdirsep (*src))
1569 src++;
1572 /* Backup if "..". */
1573 else if (src[0] == '.' && src[1] == '.' && tail[-1] == '\\')
1575 if (!isdirsep (src[2]) && src[2] != '\0')
1576 *tail++ = *src++;
1577 else
1579 /* Back up over /, but not if it's the first one. */
1580 if (tail > dst + 1)
1581 tail--;
1582 /* Now back up to the next /. */
1583 while (tail > dst + 1 && tail[-1] != '\\' && tail[-2] != ':')
1584 tail--;
1585 src += 2;
1586 /* Skip /'s to the next path component. */
1587 while (isdirsep (*src))
1588 src++;
1591 /* Otherwise, add char to result. */
1592 else
1594 if (*src == '/')
1595 *tail++ = '\\';
1596 else
1597 *tail++ = *src;
1598 src++;
1600 if ((tail - dst) >= NT_MAX_PATH)
1601 return ENAMETOOLONG;
1603 if (tail > dst + 1 && tail[-1] == '.' && tail[-2] == '\\')
1604 tail--;
1605 *tail = '\0';
1606 debug_printf ("%s = normalize_win32_path (%s)", dst_start, src_start);
1607 return 0;
1610 /* Various utilities. */
1612 /* nofinalslash: Remove trailing / and \ from SRC (except for the
1613 first one). It is ok for src == dst. */
1615 void
1616 nofinalslash (const char *src, char *dst)
1618 int len = strlen (src);
1619 if (src != dst)
1620 memcpy (dst, src, len + 1);
1621 while (len > 1 && isdirsep (dst[--len]))
1622 dst[len] = '\0';
1625 /* conv_path_list: Convert a list of path names to/from Win32/POSIX. */
1627 static int
1628 conv_path_list (const char *src, char *dst, size_t size,
1629 cygwin_conv_path_t what)
1631 tmp_pathbuf tp;
1632 char src_delim, dst_delim;
1633 size_t len;
1634 bool env_cvt = false;
1636 if (what == (cygwin_conv_path_t) ENV_CVT)
1638 what = CCP_WIN_A_TO_POSIX | CCP_RELATIVE;
1639 env_cvt = true;
1641 if ((what & CCP_CONVTYPE_MASK) == CCP_WIN_A_TO_POSIX)
1643 src_delim = ';';
1644 dst_delim = ':';
1646 else
1648 src_delim = ':';
1649 dst_delim = ';';
1652 char *srcbuf;
1653 len = strlen (src) + 1;
1654 if (len <= NT_MAX_PATH * sizeof (WCHAR))
1655 srcbuf = (char *) tp.w_get ();
1656 else
1657 srcbuf = (char *) alloca (len);
1659 int err = 0;
1660 char *d = dst - 1;
1661 bool saw_empty = false;
1664 char *srcpath = srcbuf;
1665 char *s = strccpy (srcpath, &src, src_delim);
1666 size_t len = s - srcpath;
1667 if (len >= NT_MAX_PATH)
1669 err = ENAMETOOLONG;
1670 break;
1672 /* Paths in Win32 path lists in the environment (%Path%), are often
1673 enclosed in quotes (usually paths with spaces). Trailing backslashes
1674 are common, too. Remove them. */
1675 if (env_cvt && len)
1677 if (*srcpath == '"')
1679 ++srcpath;
1680 *--s = '\0';
1681 len -= 2;
1683 while (len && s[-1] == '\\')
1685 *--s = '\0';
1686 --len;
1689 if (len)
1691 ++d;
1692 err = cygwin_conv_path (what, srcpath, d, size - (d - dst));
1694 else if ((what & CCP_CONVTYPE_MASK) == CCP_POSIX_TO_WIN_A)
1696 ++d;
1697 err = cygwin_conv_path (what, ".", d, size - (d - dst));
1699 else
1701 if (env_cvt)
1702 saw_empty = true;
1703 continue;
1705 if (err)
1706 break;
1707 d = strchr (d, '\0');
1708 *d = dst_delim;
1710 while (*src++);
1712 if (saw_empty)
1713 err = EIDRM;
1715 if (d < dst)
1716 d++;
1717 *d = '\0';
1718 return err;
1721 /********************** Symbolic Link Support **************************/
1723 /* Create a symlink from FROMPATH to TOPATH. */
1725 extern "C" int
1726 symlink (const char *oldpath, const char *newpath)
1728 path_conv win32_newpath;
1730 __try
1732 if (!*oldpath || !*newpath)
1734 set_errno (ENOENT);
1735 __leave;
1738 /* Trailing dirsep is a no-no, only errno differs. */
1739 bool has_trailing_dirsep = isdirsep (newpath[strlen (newpath) - 1]);
1740 win32_newpath.check (newpath,
1741 PC_SYM_NOFOLLOW | PC_SYM_NOFOLLOW_DIR | PC_POSIX,
1742 stat_suffixes);
1744 if (win32_newpath.error || has_trailing_dirsep)
1746 set_errno (win32_newpath.error ?:
1747 win32_newpath.exists () ? EEXIST : ENOENT);
1748 __leave;
1751 return symlink_worker (oldpath, win32_newpath, false);
1753 __except (EFAULT) {}
1754 __endtry
1755 return -1;
1758 static int
1759 symlink_nfs (const char *oldpath, path_conv &win32_newpath)
1761 /* On NFS, create symlinks by calling NtCreateFile with an EA of type
1762 NfsSymlinkTargetName containing ... the symlink target name. */
1763 tmp_pathbuf tp;
1764 PFILE_FULL_EA_INFORMATION pffei;
1765 NTSTATUS status;
1766 HANDLE fh;
1767 OBJECT_ATTRIBUTES attr;
1768 IO_STATUS_BLOCK io;
1770 pffei = (PFILE_FULL_EA_INFORMATION) tp.w_get ();
1771 pffei->NextEntryOffset = 0;
1772 pffei->Flags = 0;
1773 pffei->EaNameLength = sizeof (NFS_SYML_TARGET) - 1;
1774 char *EaValue = stpcpy (pffei->EaName, NFS_SYML_TARGET) + 1;
1775 pffei->EaValueLength = sizeof (WCHAR) *
1776 (sys_mbstowcs ((PWCHAR) EaValue, NT_MAX_PATH, oldpath) - 1);
1777 status = NtCreateFile (&fh, FILE_WRITE_DATA | FILE_WRITE_EA | SYNCHRONIZE,
1778 win32_newpath.get_object_attr (attr, sec_none_nih),
1779 &io, NULL, FILE_ATTRIBUTE_SYSTEM,
1780 FILE_SHARE_VALID_FLAGS, FILE_CREATE,
1781 FILE_SYNCHRONOUS_IO_NONALERT
1782 | FILE_OPEN_FOR_BACKUP_INTENT,
1783 pffei, NT_MAX_PATH * sizeof (WCHAR));
1784 if (!NT_SUCCESS (status))
1786 __seterrno_from_nt_status (status);
1787 return -1;
1789 NtClose (fh);
1790 return 0;
1793 /* Count backslashes between s and e. */
1794 static inline int
1795 cnt_bs (PWCHAR s, PWCHAR e)
1797 int num = 0;
1799 while (s < e)
1800 if (*s++ == L'\\')
1801 ++num;
1802 return num;
1805 #ifndef SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
1806 #define SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE 2
1807 #endif
1809 static int
1810 symlink_native (const char *oldpath, path_conv &win32_newpath)
1812 tmp_pathbuf tp;
1813 path_conv win32_oldpath;
1814 PUNICODE_STRING final_oldpath, final_newpath;
1815 UNICODE_STRING final_oldpath_buf;
1816 DWORD flags;
1818 if (isabspath (oldpath))
1820 win32_oldpath.check (oldpath, PC_SYM_NOFOLLOW, stat_suffixes);
1821 final_oldpath = win32_oldpath.get_nt_native_path ();
1823 else
1825 /* The symlink target is relative to the directory in which
1826 the symlink gets created, not relative to the cwd. Therefore
1827 we have to mangle the path quite a bit before calling path_conv. */
1828 ssize_t len = strrchr (win32_newpath.get_posix (), '/')
1829 - win32_newpath.get_posix () + 1;
1830 char *absoldpath = tp.t_get ();
1831 stpcpy (stpncpy (absoldpath, win32_newpath.get_posix (), len),
1832 oldpath);
1833 win32_oldpath.check (absoldpath, PC_SYM_NOFOLLOW, stat_suffixes);
1835 /* Try hard to keep Windows symlink path relative. */
1837 /* 1. Find common path prefix. Skip leading \\?\, but take pre-increment
1838 of the following loop into account. */
1839 PWCHAR c_old = win32_oldpath.get_nt_native_path ()->Buffer + 3;
1840 PWCHAR c_new = win32_newpath.get_nt_native_path ()->Buffer + 3;
1841 /* Windows compatible == always check case insensitive. */
1842 while (towupper (*++c_old) == towupper (*++c_new))
1844 /* The last component could share a common prefix, so make sure we end
1845 up on the first char after the last common backslash. */
1846 while (c_old[-1] != L'\\')
1847 --c_old, --c_new;
1849 /* 2. Check if prefix is long enough. The prefix must at least points to
1850 a complete device: \\?\X:\ or \\?\UNC\server\share\ are the minimum
1851 prefix strings. We start counting behind the \\?\ for speed. */
1852 int num = cnt_bs (win32_oldpath.get_nt_native_path ()->Buffer + 4, c_old);
1853 if (num < 1 /* locale drive. */
1854 || (win32_oldpath.get_nt_native_path ()->Buffer[5] != L':'
1855 && num < 3)) /* UNC path. */
1857 /* 3a. No valid common path prefix: Create absolute symlink. */
1858 final_oldpath = win32_oldpath.get_nt_native_path ();
1860 else
1862 /* 3b. Common path prefx. Count number of additional directories
1863 in symlink's path, and prepend as much ".." path components
1864 to the target path. */
1865 PWCHAR e_new = win32_newpath.get_nt_native_path ()->Buffer
1866 + win32_newpath.get_nt_native_path ()->Length
1867 / sizeof (WCHAR);
1868 num = cnt_bs (c_new, e_new);
1869 final_oldpath = &final_oldpath_buf;
1870 final_oldpath->Buffer = tp.w_get ();
1871 PWCHAR e_old = final_oldpath->Buffer;
1872 while (num-- > 0)
1873 e_old = wcpcpy (e_old, L"..\\");
1874 wcpcpy (e_old, c_old);
1877 /* If the symlink target doesn't exist, don't create native symlink.
1878 Otherwise the directory flag in the symlink is potentially wrong
1879 when the target comes into existence, and native tools will fail.
1880 This is so screwball. This is no problem on AFS, fortunately. */
1881 if (!win32_oldpath.exists () && !win32_oldpath.fs_is_afs ())
1883 SetLastError (ERROR_FILE_NOT_FOUND);
1884 return -1;
1886 /* Don't allow native symlinks to Cygwin special files. However, the
1887 caller shoud know because this case shouldn't be covered by the
1888 default "nativestrict" behaviour, so we use a special return code. */
1889 if (win32_oldpath.isspecial ())
1890 return -2;
1891 /* Convert native paths to Win32 UNC paths. */
1892 final_newpath = win32_newpath.get_nt_native_path ();
1893 final_newpath->Buffer[1] = L'\\';
1894 /* oldpath may be relative. Make sure to convert only absolute paths
1895 to Win32 paths. */
1896 if (final_oldpath->Buffer[0] == L'\\')
1898 /* Starting with Windows 8.1, the ShellExecuteW function does not
1899 handle the long path prefix correctly for symlink targets. Thus,
1900 we create simple short paths < MAX_PATH without long path prefix. */
1901 if (RtlEqualUnicodePathPrefix (final_oldpath, &ro_u_uncp, TRUE)
1902 && final_oldpath->Length < (MAX_PATH + 6) * sizeof (WCHAR))
1904 final_oldpath->Buffer += 6;
1905 final_oldpath->Buffer[0] = L'\\';
1907 else if (final_oldpath->Length < (MAX_PATH + 4) * sizeof (WCHAR))
1908 final_oldpath->Buffer += 4;
1909 else /* Stick to long path, fix native prefix for Win32 API calls. */
1910 final_oldpath->Buffer[1] = L'\\';
1912 /* Try to create native symlink. */
1913 flags = win32_oldpath.isdir () ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0;
1914 if (wincap.has_unprivileged_createsymlink ())
1915 flags |= SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
1916 if (!CreateSymbolicLinkW (final_newpath->Buffer, final_oldpath->Buffer,
1917 flags))
1919 /* Repair native newpath, we still need it. */
1920 final_newpath->Buffer[1] = L'?';
1921 return -1;
1923 return 0;
1926 #ifndef IO_REPARSE_TAG_LX_SYMLINK
1927 #define IO_REPARSE_TAG_LX_SYMLINK (0xa000001d)
1928 #endif
1930 typedef struct _REPARSE_LX_SYMLINK_BUFFER
1932 DWORD ReparseTag;
1933 WORD ReparseDataLength;
1934 WORD Reserved;
1935 struct {
1936 DWORD FileType; /* Take member name with a grain of salt. Value is
1937 apparently always 2 for symlinks. */
1938 char PathBuffer[1];/* POSIX path as given to symlink(2).
1939 Path is not \0 terminated.
1940 Length is ReparseDataLength - sizeof (FileType).
1941 Always UTF-8.
1942 Chars given in incompatible codesets, e. g. umlauts
1943 in ISO-8859-x, are converted to the Unicode
1944 REPLACEMENT CHARACTER 0xfffd == \xef\xbf\bd */
1945 } LxSymlinkReparseBuffer;
1946 } REPARSE_LX_SYMLINK_BUFFER,*PREPARSE_LX_SYMLINK_BUFFER;
1948 static int
1949 symlink_wsl (const char *oldpath, path_conv &win32_newpath)
1951 tmp_pathbuf tp;
1952 PREPARSE_LX_SYMLINK_BUFFER rpl = (PREPARSE_LX_SYMLINK_BUFFER) tp.c_get ();
1953 char *path_buf = rpl->LxSymlinkReparseBuffer.PathBuffer;
1954 const int max_pathlen = MAXIMUM_REPARSE_DATA_BUFFER_SIZE
1955 - offsetof (REPARSE_LX_SYMLINK_BUFFER,
1956 LxSymlinkReparseBuffer.PathBuffer);
1957 PWCHAR utf16 = tp.w_get ();
1958 NTSTATUS status;
1959 IO_STATUS_BLOCK io;
1960 OBJECT_ATTRIBUTES attr;
1961 HANDLE fh;
1962 int len;
1964 rpl->ReparseTag = IO_REPARSE_TAG_LX_SYMLINK;
1965 rpl->Reserved = 0;
1966 rpl->LxSymlinkReparseBuffer.FileType = 2;
1967 /* Convert cygdrive prefix to "/mnt" for WSL compatibility, but only if
1968 cygdrive prefix is not "/", otherwise suffer random "/mnt" symlinks... */
1969 if (mount_table->cygdrive_len > 1
1970 && path_prefix_p (mount_table->cygdrive, oldpath,
1971 mount_table->cygdrive_len, false)
1972 && (strlen (oldpath + mount_table->cygdrive_len - 1) < 2
1973 || (islower (oldpath[mount_table->cygdrive_len])
1974 && (oldpath[mount_table->cygdrive_len + 1] == '/'
1975 || oldpath[mount_table->cygdrive_len + 1] == '\0'))))
1976 stpcpy (stpcpy (path_buf, "/mnt"),
1977 oldpath + mount_table->cygdrive_len - 1);
1978 else
1979 *stpncpy (path_buf, oldpath, max_pathlen) = '\0';
1980 /* Convert target path to UTF-16 and then back to UTF-8 to make sure the
1981 WSL symlink is in UTF-8, independent of the current Cygwin codeset. */
1982 sys_mbstowcs (utf16, NT_MAX_PATH, path_buf);
1983 len = WideCharToMultiByte (CP_UTF8, 0, utf16, -1, path_buf, max_pathlen,
1984 NULL, NULL);
1985 /* Length is omitting trailing \0. */
1986 rpl->ReparseDataLength = sizeof (DWORD) + len - 1;
1987 /* Create reparse point. */
1988 status = NtCreateFile (&fh, DELETE | FILE_GENERIC_WRITE
1989 | READ_CONTROL | WRITE_DAC,
1990 win32_newpath.get_object_attr (attr, sec_none_nih),
1991 &io, NULL, FILE_ATTRIBUTE_NORMAL,
1992 FILE_SHARE_VALID_FLAGS, FILE_CREATE,
1993 FILE_SYNCHRONOUS_IO_NONALERT
1994 | FILE_NON_DIRECTORY_FILE
1995 | FILE_OPEN_FOR_BACKUP_INTENT
1996 | FILE_OPEN_REPARSE_POINT,
1997 NULL, 0);
1998 if (!NT_SUCCESS (status))
2000 SetLastError (RtlNtStatusToDosError (status));
2001 return -1;
2003 set_created_file_access (fh, win32_newpath, S_IFLNK | STD_RBITS | STD_WBITS);
2004 status = NtFsControlFile (fh, NULL, NULL, NULL, &io, FSCTL_SET_REPARSE_POINT,
2005 (LPVOID) rpl, REPARSE_DATA_BUFFER_HEADER_SIZE
2006 + rpl->ReparseDataLength,
2007 NULL, 0);
2008 if (!NT_SUCCESS (status))
2010 SetLastError (RtlNtStatusToDosError (status));
2011 FILE_DISPOSITION_INFORMATION fdi = { TRUE };
2012 status = NtSetInformationFile (fh, &io, &fdi, sizeof fdi,
2013 FileDispositionInformation);
2014 NtClose (fh);
2015 if (!NT_SUCCESS (status))
2016 debug_printf ("Setting delete dispostion failed, status = %y", status);
2017 return -1;
2019 NtClose (fh);
2020 return 0;
2024 symlink_worker (const char *oldpath, path_conv &win32_newpath, bool isdevice)
2026 int res = -1;
2027 size_t len;
2028 char *buf, *cp;
2029 tmp_pathbuf tp;
2030 winsym_t wsym_type;
2032 /* POSIX says that empty 'newpath' is invalid input while empty
2033 'oldpath' is valid -- it's symlink resolver job to verify if
2034 symlink contents point to existing filesystem object */
2035 __try
2037 if (strlen (oldpath) > SYMLINK_MAX)
2039 set_errno (ENAMETOOLONG);
2040 __leave;
2043 /* Default symlink type is determined by global allow_winsymlinks
2044 variable. Device files are always shortcuts. */
2045 wsym_type = isdevice ? WSYM_lnk : allow_winsymlinks;
2046 /* NFS has its own, dedicated way to create symlinks. */
2047 if (win32_newpath.fs_is_nfs () && !isdevice)
2048 wsym_type = WSYM_nfs;
2049 /* MVFS doesn't handle the SYSTEM DOS attribute, but it handles the R/O
2050 attribute. Therefore we create symlinks on MVFS always as shortcuts. */
2051 else if (win32_newpath.fs_is_mvfs ())
2052 wsym_type = WSYM_lnk;
2053 /* AFS only supports native symlinks. */
2054 else if (win32_newpath.fs_is_afs ())
2055 wsym_type = WSYM_nativestrict;
2056 /* Don't try native symlinks on FSes not supporting reparse points. */
2057 else if ((wsym_type == WSYM_native || wsym_type == WSYM_nativestrict)
2058 && !(win32_newpath.fs_flags () & FILE_SUPPORTS_REPARSE_POINTS))
2059 wsym_type = WSYM_default;
2061 /* Attach .lnk suffix when shortcut is requested. */
2062 if (wsym_type == WSYM_lnk && !win32_newpath.exists ()
2063 && (isdevice || !win32_newpath.fs_is_nfs ()))
2065 char *newplnk = tp.c_get ();
2066 stpcpy (stpcpy (newplnk, win32_newpath.get_posix ()), ".lnk");
2067 win32_newpath.check (newplnk, PC_SYM_NOFOLLOW | PC_POSIX);
2070 if (win32_newpath.error)
2072 set_errno (win32_newpath.error);
2073 __leave;
2076 syscall_printf ("symlink (%s, %S) wsym_type %d", oldpath,
2077 win32_newpath.get_nt_native_path (), wsym_type);
2079 if ((!isdevice && win32_newpath.exists ())
2080 || (win32_newpath.isdevice () && !win32_newpath.is_fs_special ()))
2082 set_errno (EEXIST);
2083 __leave;
2086 /* Handle NFS, native symlinks and WSL symlinks in their own functions. */
2087 switch (wsym_type)
2089 case WSYM_nfs:
2090 res = symlink_nfs (oldpath, win32_newpath);
2091 __leave;
2092 case WSYM_native:
2093 case WSYM_nativestrict:
2094 res = symlink_native (oldpath, win32_newpath);
2095 if (!res)
2096 __leave;
2097 /* Strictly native? Too bad, unless the target is a Cygwin
2098 special file. */
2099 if (res == -1 && wsym_type == WSYM_nativestrict)
2101 __seterrno ();
2102 __leave;
2104 /* Otherwise, fall back to default symlink type. */
2105 wsym_type = WSYM_default;
2106 fallthrough;
2107 case WSYM_default:
2108 if (win32_newpath.fs_flags () & FILE_SUPPORTS_REPARSE_POINTS)
2110 res = symlink_wsl (oldpath, win32_newpath);
2111 if (!res)
2112 __leave;
2114 /* On FSes not supporting reparse points, or in case of an error
2115 creating the WSL symlink, fall back to creating the plain old
2116 SYSTEM file symlink. */
2117 wsym_type = WSYM_sysfile;
2118 break;
2119 default:
2120 break;
2123 if (wsym_type == WSYM_lnk)
2125 path_conv win32_oldpath;
2126 ITEMIDLIST *pidl = NULL;
2127 size_t full_len = 0;
2128 unsigned short oldpath_len, desc_len, relpath_len, pidl_len = 0;
2129 char desc[MAX_PATH + 1], *relpath;
2131 if (!isdevice)
2133 /* First create an IDLIST to learn how big our shortcut is
2134 going to be. */
2135 IShellFolder *psl;
2137 /* The symlink target is relative to the directory in which the
2138 symlink gets created, not relative to the cwd. Therefore we
2139 have to mangle the path quite a bit before calling path_conv.*/
2140 if (isabspath (oldpath))
2141 win32_oldpath.check (oldpath,
2142 PC_SYM_NOFOLLOW,
2143 stat_suffixes);
2144 else
2146 len = strrchr (win32_newpath.get_posix (), '/')
2147 - win32_newpath.get_posix () + 1;
2148 char *absoldpath = tp.t_get ();
2149 stpcpy (stpncpy (absoldpath, win32_newpath.get_posix (),
2150 len),
2151 oldpath);
2152 win32_oldpath.check (absoldpath, PC_SYM_NOFOLLOW,
2153 stat_suffixes);
2155 if (SUCCEEDED (SHGetDesktopFolder (&psl)))
2157 WCHAR wc_path[win32_oldpath.get_wide_win32_path_len () + 1];
2158 win32_oldpath.get_wide_win32_path (wc_path);
2159 /* Amazing but true: Even though the ParseDisplayName method
2160 takes a wide char path name, it does not understand the
2161 Win32 prefix for long pathnames! So we have to tack off
2162 the prefix and convert the path to the "normal" syntax
2163 for ParseDisplayName. */
2164 PWCHAR wc = wc_path + 4;
2165 if (wc[1] != L':') /* native UNC path */
2166 *(wc += 2) = L'\\';
2167 HRESULT res;
2168 if (SUCCEEDED (res = psl->ParseDisplayName (NULL, NULL, wc,
2169 NULL, &pidl,
2170 NULL)))
2172 ITEMIDLIST *p;
2174 for (p = pidl; p->mkid.cb > 0;
2175 p = (ITEMIDLIST *)((char *) p + p->mkid.cb))
2177 pidl_len = (char *) p - (char *) pidl + 2;
2179 psl->Release ();
2182 /* Compute size of shortcut file. */
2183 full_len = sizeof (win_shortcut_hdr);
2184 if (pidl_len)
2185 full_len += sizeof (unsigned short) + pidl_len;
2186 oldpath_len = strlen (oldpath);
2187 /* Unfortunately the length of the description is restricted to a
2188 length of 2000 bytes. We don't want to add considerations for
2189 the different lengths and even 2000 bytes is not enough for long
2190 path names. So what we do here is to set the description to the
2191 POSIX path only if the path is not longer than MAX_PATH characters.
2192 We append the full path name after the regular shortcut data
2193 (see below), which works fine with Windows Explorer as well
2194 as older Cygwin versions (as long as the whole file isn't bigger
2195 than 8K). The description field is only used for backward
2196 compatibility to older Cygwin versions and those versions are
2197 not capable of handling long path names anyway. */
2198 desc_len = stpcpy (desc, oldpath_len > MAX_PATH
2199 ? "[path too long]" : oldpath) - desc;
2200 full_len += sizeof (unsigned short) + desc_len;
2201 /* Devices get the oldpath string unchanged as relative path. */
2202 if (isdevice)
2204 relpath_len = oldpath_len;
2205 stpcpy (relpath = tp.c_get (), oldpath);
2207 else
2209 relpath_len = strlen (win32_oldpath.get_win32 ());
2210 stpcpy (relpath = tp.c_get (), win32_oldpath.get_win32 ());
2212 full_len += sizeof (unsigned short) + relpath_len;
2213 full_len += sizeof (unsigned short) + oldpath_len;
2214 /* 1 byte more for trailing 0 written by stpcpy. */
2215 if (full_len < NT_MAX_PATH * sizeof (WCHAR))
2216 buf = tp.t_get ();
2217 else
2218 buf = (char *) alloca (full_len + 1);
2220 /* Create shortcut header */
2221 win_shortcut_hdr *shortcut_header = (win_shortcut_hdr *) buf;
2222 memset (shortcut_header, 0, sizeof *shortcut_header);
2223 shortcut_header->size = sizeof *shortcut_header;
2224 shortcut_header->magic = GUID_shortcut;
2225 shortcut_header->flags = (WSH_FLAG_DESC | WSH_FLAG_RELPATH);
2226 if (pidl)
2227 shortcut_header->flags |= WSH_FLAG_IDLIST;
2228 shortcut_header->run = SW_NORMAL;
2229 cp = buf + sizeof (win_shortcut_hdr);
2231 /* Create IDLIST */
2232 if (pidl)
2234 *(unsigned short *)cp = pidl_len;
2235 memcpy (cp += 2, pidl, pidl_len);
2236 cp += pidl_len;
2237 CoTaskMemFree (pidl);
2240 /* Create description */
2241 *(unsigned short *)cp = desc_len;
2242 cp = stpcpy (cp += 2, desc);
2244 /* Create relpath */
2245 *(unsigned short *)cp = relpath_len;
2246 cp = stpcpy (cp += 2, relpath);
2248 /* Append the POSIX path after the regular shortcut data for
2249 the long path support. */
2250 unsigned short *plen = (unsigned short *) cp;
2251 cp += 2;
2252 *(PWCHAR) cp = 0xfeff; /* BOM */
2253 cp += 2;
2254 *plen = sys_mbstowcs ((PWCHAR) cp, NT_MAX_PATH, oldpath)
2255 * sizeof (WCHAR);
2256 cp += *plen;
2258 else /* wsym_type == WSYM_sysfile */
2260 /* Default technique creating a symlink. */
2261 buf = tp.t_get ();
2262 cp = stpcpy (buf, SYMLINK_COOKIE);
2263 *(PWCHAR) cp = 0xfeff; /* BOM */
2264 cp += 2;
2265 /* Note that the terminating nul is written. */
2266 cp += sys_mbstowcs ((PWCHAR) cp, NT_MAX_PATH, oldpath)
2267 * sizeof (WCHAR);
2270 OBJECT_ATTRIBUTES attr;
2271 IO_STATUS_BLOCK io;
2272 NTSTATUS status;
2273 ULONG access;
2274 HANDLE fh;
2276 access = DELETE | FILE_GENERIC_WRITE;
2277 if (isdevice && win32_newpath.exists ())
2279 status = NtOpenFile (&fh, FILE_WRITE_ATTRIBUTES,
2280 win32_newpath.get_object_attr (attr,
2281 sec_none_nih),
2282 &io, 0, FILE_OPEN_FOR_BACKUP_INTENT);
2283 if (!NT_SUCCESS (status))
2285 __seterrno_from_nt_status (status);
2286 __leave;
2288 status = NtSetAttributesFile (fh, FILE_ATTRIBUTE_NORMAL);
2289 NtClose (fh);
2290 if (!NT_SUCCESS (status))
2292 __seterrno_from_nt_status (status);
2293 __leave;
2296 else if (!isdevice && win32_newpath.has_acls ()
2297 && !win32_newpath.isremote ())
2298 /* If the filesystem supports ACLs, we will overwrite the DACL after the
2299 call to NtCreateFile. This requires a handle with READ_CONTROL and
2300 WRITE_DAC access, otherwise get_file_sd and set_file_sd both have to
2301 open the file again.
2302 FIXME: On remote NTFS shares open sometimes fails because even the
2303 creator of the file doesn't have the right to change the DACL.
2304 I don't know what setting that is or how to recognize such a share,
2305 so for now we don't request WRITE_DAC on remote drives. */
2306 access |= READ_CONTROL | WRITE_DAC;
2308 status = NtCreateFile (&fh, access,
2309 win32_newpath.get_object_attr (attr, sec_none_nih),
2310 &io, NULL, FILE_ATTRIBUTE_NORMAL,
2311 FILE_SHARE_VALID_FLAGS,
2312 isdevice ? FILE_OVERWRITE_IF : FILE_CREATE,
2313 FILE_SYNCHRONOUS_IO_NONALERT
2314 | FILE_NON_DIRECTORY_FILE
2315 | FILE_OPEN_FOR_BACKUP_INTENT,
2316 NULL, 0);
2317 if (!NT_SUCCESS (status))
2319 __seterrno_from_nt_status (status);
2320 __leave;
2322 if (io.Information == FILE_CREATED && win32_newpath.has_acls ())
2323 set_created_file_access (fh, win32_newpath,
2324 S_IFLNK | STD_RBITS | STD_WBITS);
2325 status = NtWriteFile (fh, NULL, NULL, NULL, &io, buf, cp - buf,
2326 NULL, NULL);
2327 if (NT_SUCCESS (status) && io.Information == (ULONG) (cp - buf))
2329 status = NtSetAttributesFile (fh, wsym_type == WSYM_lnk
2330 ? FILE_ATTRIBUTE_READONLY
2331 : FILE_ATTRIBUTE_SYSTEM);
2332 if (!NT_SUCCESS (status))
2333 debug_printf ("Setting attributes failed, status = %y", status);
2334 res = 0;
2336 else
2338 __seterrno_from_nt_status (status);
2339 FILE_DISPOSITION_INFORMATION fdi = { TRUE };
2340 status = NtSetInformationFile (fh, &io, &fdi, sizeof fdi,
2341 FileDispositionInformation);
2342 if (!NT_SUCCESS (status))
2343 debug_printf ("Setting delete dispostion failed, status = %y",
2344 status);
2346 NtClose (fh);
2349 __except (EFAULT) {}
2350 __endtry
2351 syscall_printf ("%d = symlink_worker(%s, %s, %d)",
2352 res, oldpath, win32_newpath.get_posix (), isdevice);
2353 return res;
2356 static bool
2357 cmp_shortcut_header (win_shortcut_hdr *file_header)
2359 /* A Cygwin or U/Win shortcut only contains a description and a relpath.
2360 Cygwin shortcuts also might contain an ITEMIDLIST. The run type is
2361 always set to SW_NORMAL. */
2362 return file_header->size == sizeof (win_shortcut_hdr)
2363 && !memcmp (&file_header->magic, &GUID_shortcut, sizeof GUID_shortcut)
2364 && (file_header->flags & ~WSH_FLAG_IDLIST)
2365 == (WSH_FLAG_DESC | WSH_FLAG_RELPATH)
2366 && file_header->run == SW_NORMAL;
2370 symlink_info::check_shortcut (HANDLE h)
2372 tmp_pathbuf tp;
2373 win_shortcut_hdr *file_header;
2374 char *buf, *cp;
2375 unsigned short len;
2376 int res = 0;
2377 NTSTATUS status;
2378 IO_STATUS_BLOCK io;
2379 FILE_STANDARD_INFORMATION fsi;
2380 LARGE_INTEGER off = { QuadPart:0LL };
2382 status = NtQueryInformationFile (h, &io, &fsi, sizeof fsi,
2383 FileStandardInformation);
2384 if (!NT_SUCCESS (status))
2386 set_error (EIO);
2387 return 0;
2389 if (fsi.EndOfFile.QuadPart <= (LONGLONG) sizeof (win_shortcut_hdr)
2390 || fsi.EndOfFile.QuadPart > 4 * 65536)
2391 return 0;
2392 if (fsi.EndOfFile.LowPart < NT_MAX_PATH * sizeof (WCHAR))
2393 buf = (char *) tp.w_get ();
2394 else
2395 buf = (char *) alloca (fsi.EndOfFile.LowPart + 1);
2396 status = NtReadFile (h, NULL, NULL, NULL, &io, buf, fsi.EndOfFile.LowPart,
2397 &off, NULL);
2398 if (!NT_SUCCESS (status))
2400 if (status != STATUS_END_OF_FILE)
2401 set_error (EIO);
2402 return 0;
2404 file_header = (win_shortcut_hdr *) buf;
2405 if (io.Information != fsi.EndOfFile.LowPart
2406 || !cmp_shortcut_header (file_header))
2407 return 0;
2408 cp = buf + sizeof (win_shortcut_hdr);
2409 if (file_header->flags & WSH_FLAG_IDLIST) /* Skip ITEMIDLIST */
2410 cp += *(unsigned short *) cp + 2;
2411 if (!(len = *(unsigned short *) cp))
2412 return 0;
2413 cp += 2;
2414 /* Check if this is a device file - these start with the sequence :\\ */
2415 if (strncmp (cp, ":\\", 2) == 0)
2416 res = strlen (strcpy (contents, cp)); /* Don't mess with device files */
2417 else
2419 /* Has appended full path? If so, use it instead of description. */
2420 unsigned short relpath_len = *(unsigned short *) (cp + len);
2421 if (cp + len + 2 + relpath_len < buf + fsi.EndOfFile.LowPart)
2423 cp += len + 2 + relpath_len;
2424 len = *(unsigned short *) cp;
2425 cp += 2;
2427 if (*(PWCHAR) cp == 0xfeff) /* BOM */
2429 char *tmpbuf = tp.c_get ();
2430 if (sys_wcstombs (tmpbuf, NT_MAX_PATH, (PWCHAR) (cp + 2))
2431 > SYMLINK_MAX)
2432 return 0;
2433 res = posixify (tmpbuf);
2435 else if (len > SYMLINK_MAX)
2436 return 0;
2437 else
2439 cp[len] = '\0';
2440 res = posixify (cp);
2443 if (res) /* It's a symlink. */
2444 path_flags (path_flags () | PATH_SYMLINK | PATH_LNK);
2445 return res;
2449 symlink_info::check_sysfile (HANDLE h)
2451 tmp_pathbuf tp;
2452 char cookie_buf[sizeof (SYMLINK_COOKIE) - 1];
2453 char *srcbuf = tp.c_get ();
2454 int res = 0;
2455 NTSTATUS status;
2456 IO_STATUS_BLOCK io;
2457 bool interix_symlink = false;
2458 LARGE_INTEGER off = { QuadPart:0LL };
2460 status = NtReadFile (h, NULL, NULL, NULL, &io, cookie_buf,
2461 sizeof (cookie_buf), &off, NULL);
2462 if (!NT_SUCCESS (status))
2464 debug_printf ("ReadFile1 failed %y", status);
2465 if (status != STATUS_END_OF_FILE)
2466 set_error (EIO);
2467 return 0;
2469 off.QuadPart = io.Information;
2470 if (io.Information == sizeof (cookie_buf)
2471 && memcmp (cookie_buf, SYMLINK_COOKIE, sizeof (cookie_buf)) == 0)
2473 /* It's a symlink. */
2474 path_flags (path_flags () | PATH_SYMLINK);
2476 else if (io.Information == sizeof (cookie_buf)
2477 && memcmp (cookie_buf, SOCKET_COOKIE, sizeof (cookie_buf)) == 0)
2478 path_flags (path_flags () | PATH_SOCKET);
2479 else if (io.Information >= sizeof (INTERIX_SYMLINK_COOKIE)
2480 && memcmp (cookie_buf, INTERIX_SYMLINK_COOKIE,
2481 sizeof (INTERIX_SYMLINK_COOKIE) - 1) == 0)
2483 /* It's an Interix symlink. */
2484 path_flags (path_flags () | PATH_SYMLINK);
2485 interix_symlink = true;
2486 /* Interix symlink cookies are shorter than Cygwin symlink cookies, so
2487 in case of an Interix symlink cooky we have read too far into the
2488 file. Set file pointer back to the position right after the cookie. */
2489 off.QuadPart = sizeof (INTERIX_SYMLINK_COOKIE) - 1;
2491 if (path_flags () & PATH_SYMLINK)
2493 status = NtReadFile (h, NULL, NULL, NULL, &io, srcbuf,
2494 NT_MAX_PATH, &off, NULL);
2495 if (!NT_SUCCESS (status))
2497 debug_printf ("ReadFile2 failed");
2498 if (status != STATUS_END_OF_FILE)
2499 set_error (EIO);
2501 else if (*(PWCHAR) srcbuf == 0xfeff /* BOM */
2502 || interix_symlink)
2504 /* Add trailing 0 to Interix symlink target. Skip BOM in Cygwin
2505 symlinks. */
2506 if (interix_symlink)
2507 ((PWCHAR) srcbuf)[io.Information / sizeof (WCHAR)] = L'\0';
2508 else
2509 srcbuf += 2;
2510 char *tmpbuf = tp.c_get ();
2511 if (sys_wcstombs (tmpbuf, NT_MAX_PATH, (PWCHAR) srcbuf)
2512 > SYMLINK_MAX)
2513 debug_printf ("symlink string too long");
2514 else
2515 res = posixify (tmpbuf);
2517 else if (io.Information > SYMLINK_MAX + 1)
2518 debug_printf ("symlink string too long");
2519 else
2520 res = posixify (srcbuf);
2522 return res;
2525 typedef struct _REPARSE_APPEXECLINK_BUFFER
2527 DWORD ReparseTag;
2528 WORD ReparseDataLength;
2529 WORD Reserved;
2530 struct {
2531 DWORD Version; /* Take member name with a grain of salt. */
2532 WCHAR Strings[1]; /* Four serialized, NUL-terminated WCHAR strings:
2533 - Package ID
2534 - Entry Point
2535 - Executable Path
2536 - Application Type
2537 We're only interested in the Executable Path */
2538 } AppExecLinkReparseBuffer;
2539 } REPARSE_APPEXECLINK_BUFFER,*PREPARSE_APPEXECLINK_BUFFER;
2541 static bool
2542 check_reparse_point_string (PUNICODE_STRING subst)
2544 /* Native mount points, or native non-relative symbolic links,
2545 can be treated as posix symlinks only if the SubstituteName
2546 can be converted from a native NT object namespace name to
2547 a win32 name. We only know how to convert names with two
2548 prefixes :
2549 "\??\UNC\..."
2550 "\??\X:..."
2551 Other reparse points will be treated as files or
2552 directories, not as posix symlinks.
2554 if (RtlEqualUnicodePathPrefix (subst, &ro_u_natp, FALSE))
2556 if (subst->Length >= 6 * sizeof(WCHAR) && subst->Buffer[5] == L':' &&
2557 (subst->Length == 6 * sizeof(WCHAR) || subst->Buffer[6] == L'\\'))
2558 return true;
2559 else if (subst->Length >= 8 * sizeof(WCHAR) &&
2560 wcsncmp (subst->Buffer + 4, L"UNC\\", 4) == 0)
2561 return true;
2563 return false;
2566 /* Return values:
2567 <0: Negative errno.
2568 0: Not a reparse point recognized by us.
2569 >0: Path flags for a recognized reparse point, always including PATH_REP.
2572 check_reparse_point_target (HANDLE h, bool remote, PREPARSE_DATA_BUFFER rp,
2573 PUNICODE_STRING psymbuf)
2575 NTSTATUS status;
2576 IO_STATUS_BLOCK io;
2578 /* On remote drives or under heavy load, NtFsControlFile can return with
2579 STATUS_PENDING. If so, instead of creating an event object, just set
2580 io.Status to an invalid value and perform a minimal wait until io.Status
2581 changed. */
2582 memset (&io, 0xff, sizeof io);
2583 status = NtFsControlFile (h, NULL, NULL, NULL, &io,
2584 FSCTL_GET_REPARSE_POINT, NULL, 0, (LPVOID) rp,
2585 MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
2586 if (status == STATUS_PENDING)
2588 while (io.Status == (NTSTATUS) 0xffffffff)
2589 Sleep (1L);
2590 status = io.Status;
2592 if (!NT_SUCCESS (status))
2594 debug_printf ("NtFsControlFile(FSCTL_GET_REPARSE_POINT) failed, %y",
2595 status);
2596 /* When accessing the root dir of some remote drives (observed with
2597 OS X shares), the FILE_ATTRIBUTE_REPARSE_POINT flag is set, but
2598 the followup call to NtFsControlFile(FSCTL_GET_REPARSE_POINT)
2599 returns with STATUS_NOT_A_REPARSE_POINT. That's quite buggy, but
2600 we cope here with this scenario by not setting an error code. */
2601 if (status == STATUS_NOT_A_REPARSE_POINT)
2602 return 0;
2603 return -EIO;
2605 if (rp->ReparseTag == IO_REPARSE_TAG_SYMLINK)
2607 /* Windows evaluates native symlink literally. If a remote symlink
2608 points to, say, C:\foo, it will be handled as if the target is the
2609 local file C:\foo. That comes in handy since that's how symlinks
2610 are treated under POSIX as well. */
2611 RtlInitCountedUnicodeString (psymbuf,
2612 (PWCHAR)((PBYTE) rp->SymbolicLinkReparseBuffer.PathBuffer
2613 + rp->SymbolicLinkReparseBuffer.SubstituteNameOffset),
2614 rp->SymbolicLinkReparseBuffer.SubstituteNameLength);
2615 if ((rp->SymbolicLinkReparseBuffer.Flags & SYMLINK_FLAG_RELATIVE) ||
2616 check_reparse_point_string (psymbuf))
2617 return PATH_SYMLINK | PATH_REP;
2619 else if (!remote && rp->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
2621 /* Don't handle junctions on remote filesystems as symlinks. This type
2622 of reparse point is handled transparently by the OS so that the
2623 target of the junction is the remote directory it is supposed to
2624 point to. If we handle it as symlink, it will be mistreated as
2625 pointing to a dir on the local system. */
2626 RtlInitCountedUnicodeString (psymbuf,
2627 (PWCHAR)((PBYTE) rp->MountPointReparseBuffer.PathBuffer
2628 + rp->MountPointReparseBuffer.SubstituteNameOffset),
2629 rp->MountPointReparseBuffer.SubstituteNameLength);
2630 if (RtlEqualUnicodePathPrefix (psymbuf, &ro_u_volume, TRUE))
2632 /* Volume mount point. Not treated as symlink. The return
2633 value -EPERM is a hint for the caller to treat this as a
2634 volume mount point. */
2635 return -EPERM;
2637 if (check_reparse_point_string (psymbuf))
2638 return PATH_SYMLINK | PATH_REP;
2640 else if (!remote && rp->ReparseTag == IO_REPARSE_TAG_APPEXECLINK)
2642 /* App execution aliases are commonly used by Windows Store apps. */
2643 PREPARSE_APPEXECLINK_BUFFER rpl = (PREPARSE_APPEXECLINK_BUFFER) rp;
2644 WCHAR *buf = rpl->AppExecLinkReparseBuffer.Strings;
2645 DWORD size = rp->ReparseDataLength / sizeof (WCHAR), n;
2647 /* It seems that app execution aliases have a payload of four
2648 NUL-separated wide string: package id, entry point, executable
2649 and application type. We're interested in the executable. */
2650 for (int i = 0; i < 3 && size > 0; i++)
2652 n = wcsnlen (buf, size - 1);
2653 if (i == 2 && n > 0 && n < size)
2655 RtlInitCountedUnicodeString (psymbuf, buf, n * sizeof (WCHAR));
2656 return PATH_SYMLINK | PATH_REP;
2658 if (i == 2)
2659 break;
2660 buf += n + 1;
2661 size -= n + 1;
2664 else if (rp->ReparseTag == IO_REPARSE_TAG_LX_SYMLINK)
2666 /* WSL symlink. Problem: We have to convert the path to UTF-16 for
2667 the caller. Reparse points are 16K max. The buffer given to rp
2668 is 32K. So there's enough trailing space in the buffer to convert
2669 to UTF-16 and let psymbuf point to it. */
2670 PREPARSE_LX_SYMLINK_BUFFER rpl = (PREPARSE_LX_SYMLINK_BUFFER) rp;
2671 char *path_buf = rpl->LxSymlinkReparseBuffer.PathBuffer;
2672 DWORD path_len = rpl->ReparseDataLength - sizeof (DWORD);
2673 bool full_path = false;
2674 const size_t drv_prefix_len = strlen ("/mnt");
2675 PBYTE utf16_ptr;
2676 PWCHAR utf16_buf;
2677 int utf16_bufsize;
2679 /* 0-terminate path_buf for easier testing. */
2680 path_buf[path_len] = '\0';
2681 if (path_prefix_p ("/mnt", path_buf, drv_prefix_len, false))
2683 size_t len = strlen (path_buf);
2685 if (len <= drv_prefix_len + 1)
2687 /* /mnt or /mnt/. Replace with cygdrive prefix. */
2688 stpcpy (path_buf, mount_table->cygdrive);
2689 path_len = mount_table->cygdrive_len;
2690 if (len == drv_prefix_len)
2692 path_buf[mount_table->cygdrive_len - 1] = '\0';
2693 --path_len;
2695 rp->ReparseDataLength = path_len + sizeof (DWORD);
2697 else if (islower (path_buf[drv_prefix_len + 1])
2698 && (path_len == drv_prefix_len + 2
2699 || path_buf[drv_prefix_len + 2] == '/'))
2701 /* Skip forward to the slash leading the drive letter.
2702 That leaves room for adding the colon. */
2703 path_buf += drv_prefix_len;
2704 path_len -= drv_prefix_len;
2705 full_path = true;
2708 /* Compute buffer for path converted to UTF-16. */
2709 utf16_ptr = (PBYTE) rpl + sizeof (REPARSE_LX_SYMLINK_BUFFER)
2710 + rp->ReparseDataLength;
2711 /* Skip \0-termination added above. */
2712 ++utf16_ptr;
2713 /* Make sure pointer is aligned */
2714 while ((intptr_t) utf16_ptr % sizeof (WCHAR))
2715 ++utf16_ptr;
2716 utf16_buf = (PWCHAR) utf16_ptr;
2717 utf16_bufsize = NT_MAX_PATH - (utf16_buf - (PWCHAR) rpl);
2718 /* Now convert path to UTF-16. */
2719 utf16_bufsize = MultiByteToWideChar (CP_UTF8, 0, path_buf, path_len,
2720 utf16_buf, utf16_bufsize);
2721 if (utf16_bufsize)
2723 if (full_path)
2725 utf16_buf[0] = utf16_buf[1]; /* Move drive letter to front */
2726 utf16_buf[1] = L':'; /* Add colon */
2728 RtlInitCountedUnicodeString (psymbuf, utf16_buf,
2729 utf16_bufsize * sizeof (WCHAR));
2730 return PATH_SYMLINK | PATH_REP | PATH_REP_NOAPI;
2732 return -EIO;
2734 else if (rp->ReparseTag == IO_REPARSE_TAG_CYGUNIX)
2736 PREPARSE_GUID_DATA_BUFFER rgp = (PREPARSE_GUID_DATA_BUFFER) rp;
2738 if (memcmp (CYGWIN_SOCKET_GUID, &rgp->ReparseGuid, sizeof (GUID)) == 0)
2739 #ifdef __WITH_AF_UNIX
2740 return PATH_SOCKET | PATH_REP | PATH_REP_NOAPI;
2741 #else
2742 /* Recognize this as a reparse point but not as a socket. */
2743 return PATH_REP | PATH_REP_NOAPI;
2744 #endif
2746 else if (rp->ReparseTag == IO_REPARSE_TAG_AF_UNIX)
2747 /* Native Windows AF_UNIX socket; recognize this as a reparse
2748 point but not as a socket. */
2749 return PATH_REP;
2750 return 0;
2754 symlink_info::check_reparse_point (HANDLE h, bool remote)
2756 tmp_pathbuf tp;
2757 PREPARSE_DATA_BUFFER rp = (PREPARSE_DATA_BUFFER) tp.c_get ();
2758 UNICODE_STRING symbuf;
2759 char srcbuf[SYMLINK_MAX + 7];
2761 int ret = check_reparse_point_target (h, remote, rp, &symbuf);
2762 if (ret <= 0)
2764 if (ret == -EIO)
2766 set_error (EIO);
2767 return 0;
2769 /* Maybe it's a reparse point, but it's certainly not one we recognize.
2770 Drop REPARSE attribute so we don't try to use the flag accidentally.
2771 It's just some arbitrary file or directory for us. */
2772 fileattr (fileattr () & ~FILE_ATTRIBUTE_REPARSE_POINT);
2773 return ret;
2775 /* ret is > 0, so it's a known reparse point, path in symbuf. */
2776 path_flags (path_flags () | ret);
2777 if (ret & PATH_SYMLINK)
2779 sys_wcstombs (srcbuf, SYMLINK_MAX + 7, symbuf.Buffer,
2780 symbuf.Length / sizeof (WCHAR));
2781 /* A symlink is never a directory. */
2782 fileattr (fileattr () & ~FILE_ATTRIBUTE_DIRECTORY);
2783 return posixify (srcbuf);
2785 else
2786 return 0;
2790 symlink_info::check_nfs_symlink (HANDLE h)
2792 tmp_pathbuf tp;
2793 NTSTATUS status;
2794 IO_STATUS_BLOCK io;
2795 struct {
2796 FILE_GET_EA_INFORMATION fgei;
2797 char buf[sizeof (NFS_SYML_TARGET)];
2798 } fgei_buf;
2799 PFILE_FULL_EA_INFORMATION pffei;
2800 int res = 0;
2802 /* To find out if the file is a symlink and to get the symlink target,
2803 try to fetch the NfsSymlinkTargetName EA. */
2804 fgei_buf.fgei.NextEntryOffset = 0;
2805 fgei_buf.fgei.EaNameLength = sizeof (NFS_SYML_TARGET) - 1;
2806 stpcpy (fgei_buf.fgei.EaName, NFS_SYML_TARGET);
2807 pffei = (PFILE_FULL_EA_INFORMATION) tp.w_get ();
2808 status = NtQueryEaFile (h, &io, pffei, NT_MAX_PATH * sizeof (WCHAR), TRUE,
2809 &fgei_buf.fgei, sizeof fgei_buf, NULL, TRUE);
2810 if (NT_SUCCESS (status) && pffei->EaValueLength > 0)
2812 PWCHAR spath = (PWCHAR)
2813 (pffei->EaName + pffei->EaNameLength + 1);
2814 res = sys_wcstombs (contents, SYMLINK_MAX + 1,
2815 spath, pffei->EaValueLength);
2816 path_flags (path_flags () | PATH_SYMLINK);
2818 return res;
2822 symlink_info::posixify (char *srcbuf)
2824 /* The definition for a path in a native symlink is a bit weird. The Flags
2825 value seem to contain 0 for absolute paths (stored as NT native path)
2826 and 1 for relative paths. Relative paths are paths not starting with a
2827 drive letter. These are not converted to NT native, but stored as
2828 given. A path starting with a single backslash is relative to the
2829 current drive thus a "relative" value (Flags == 1).
2830 Funny enough it's possible to store paths with slashes instead of
2831 backslashes, but they are evaluated incorrectly by subsequent Windows
2832 calls like CreateFile (ERROR_INVALID_NAME). So, what we do here is to
2833 take paths starting with slashes at face value, evaluating them as
2834 Cygwin specific POSIX paths.
2835 A path starting with two slashes(!) or backslashes is converted into an
2836 NT UNC path. Unfortunately, in contrast to POSIX rules, paths starting
2837 with three or more (back)slashes are also converted into UNC paths,
2838 just incorrectly sticking to one redundant leading backslash. We go
2839 along with this behaviour to avoid scenarios in which native tools access
2840 other files than Cygwin.
2841 The above rules are used exactly the same way on Cygwin specific symlinks
2842 (sysfiles and shortcuts) to eliminate non-POSIX paths in the output. */
2844 /* Eliminate native NT prefixes. */
2845 if (srcbuf[0] == '\\' && !strncmp (srcbuf + 1, "??\\", 3))
2847 srcbuf += 4;
2848 if (srcbuf[1] != ':') /* native UNC path */
2849 *(srcbuf += 2) = '\\';
2851 if (isdrive (srcbuf))
2852 mount_table->conv_to_posix_path (srcbuf, contents, 0);
2853 else if (srcbuf[0] == '\\')
2855 if (srcbuf[1] == '\\') /* UNC path */
2856 slashify (srcbuf, contents, 0);
2857 else /* Paths starting with \ are current drive relative. */
2859 char cvtbuf[SYMLINK_MAX + 1];
2861 stpcpy (cvtbuf + cygheap->cwd.get_drive (cvtbuf), srcbuf);
2862 mount_table->conv_to_posix_path (cvtbuf, contents, 0);
2865 else /* Everything else is taken as is. */
2866 slashify (srcbuf, contents, 0);
2867 return strlen (contents);
2870 enum
2872 SCAN_BEG,
2873 SCAN_LNK,
2874 SCAN_HASLNK,
2875 SCAN_JUSTCHECK,
2876 SCAN_JUSTCHECKTHIS, /* Never try to append a suffix. */
2877 SCAN_APPENDLNK,
2878 SCAN_DONE,
2881 class suffix_scan
2883 const suffix_info *suffixes, *suffixes_start;
2884 int nextstate;
2885 char *eopath;
2886 public:
2887 const char *path;
2888 char *has (const char *, const suffix_info *);
2889 int next ();
2890 int lnk_match () {return nextstate >= SCAN_APPENDLNK;}
2893 char *
2894 suffix_scan::has (const char *in_path, const suffix_info *in_suffixes)
2896 nextstate = SCAN_BEG;
2897 suffixes = suffixes_start = in_suffixes;
2899 const char *fname = strrchr (in_path, '\\');
2900 fname = fname ? fname + 1 : in_path;
2901 char *ext_here = strrchr (fname, '.');
2902 path = in_path;
2903 eopath = strchr (path, '\0');
2905 if (!ext_here)
2906 goto noext;
2908 if (suffixes)
2910 /* Check if the extension matches a known extension */
2911 for (const suffix_info *ex = in_suffixes; ex->name != NULL; ex++)
2912 if (ascii_strcasematch (ext_here, ex->name))
2914 nextstate = SCAN_JUSTCHECK;
2915 suffixes = NULL; /* Has an extension so don't scan for one. */
2916 goto done;
2920 /* Didn't match. Use last resort -- .lnk. */
2921 if (ascii_strcasematch (ext_here, ".lnk"))
2923 nextstate = SCAN_HASLNK;
2924 suffixes = NULL;
2927 noext:
2928 ext_here = eopath;
2930 done:
2931 size_t namelen = eopath - fname;
2932 /* Avoid attaching suffixes if the resulting filename would be invalid.
2933 For performance reasons we don't check the length of a suffix, since
2934 we know that all suffixes are 4 chars in length.
2936 FIXME: This is not really correct. A fully functional test should
2937 work on wide character paths. This would probably also speed
2938 up symlink_info::check. */
2939 if (namelen > NAME_MAX - 4)
2941 if (namelen > NAME_MAX)
2942 return NULL;
2943 nextstate = SCAN_JUSTCHECKTHIS;
2944 suffixes = NULL;
2946 return ext_here;
2950 suffix_scan::next ()
2952 for (;;)
2954 if (!suffixes)
2955 switch (nextstate)
2957 case SCAN_BEG:
2958 suffixes = suffixes_start;
2959 if (!suffixes)
2961 nextstate = SCAN_LNK;
2962 return 1;
2964 nextstate = SCAN_DONE;
2965 *eopath = '\0';
2966 return 0;
2967 case SCAN_HASLNK:
2968 nextstate = SCAN_APPENDLNK; /* Skip SCAN_BEG */
2969 return 1;
2970 case SCAN_JUSTCHECK:
2971 nextstate = SCAN_LNK;
2972 return 1;
2973 case SCAN_JUSTCHECKTHIS:
2974 nextstate = SCAN_DONE;
2975 return 1;
2976 case SCAN_LNK:
2977 case SCAN_APPENDLNK:
2978 nextstate = SCAN_DONE;
2979 strcat (eopath, ".lnk");
2980 return 1;
2981 default:
2982 *eopath = '\0';
2983 return 0;
2986 if (suffixes && suffixes->name)
2988 strcpy (eopath, suffixes->name);
2989 suffixes++;
2990 return 1;
2992 suffixes = NULL;
2993 if (nextstate == SCAN_BEG)
2995 nextstate = SCAN_LNK;
2996 *eopath = '\0';
3001 bool
3002 symlink_info::set_error (int in_errno)
3004 bool res;
3005 if (!(pc_flags () & PC_NO_ACCESS_CHECK)
3006 || in_errno == ENAMETOOLONG || in_errno == EIO)
3008 error (in_errno);
3009 res = true;
3011 else if (in_errno == ENOENT)
3012 res = true;
3013 else
3015 fileattr (FILE_ATTRIBUTE_NORMAL);
3016 res = false;
3018 return res;
3021 bool
3022 symlink_info::parse_device (const char *contents)
3024 char *endptr;
3025 _major_t mymajor;
3026 _major_t myminor;
3027 __mode_t mymode;
3029 mymajor = strtol (contents += 2, &endptr, 16);
3030 if (endptr == contents)
3031 return false;
3033 contents = endptr;
3034 myminor = strtol (++contents, &endptr, 16);
3035 if (endptr == contents)
3036 return false;
3038 contents = endptr;
3039 mymode = strtol (++contents, &endptr, 16);
3040 if (endptr == contents)
3041 return false;
3043 if ((mymode & S_IFMT) == S_IFIFO)
3044 dev (FH_FIFO);
3045 else
3046 dev (FHDEV (mymajor, myminor));
3047 mode (mymode);
3048 return true;
3051 /* Probably we have a virtual drive input path and the resulting full path
3052 starts with the substitution. Retrieve the target path of the virtual
3053 drive and try to revert what GetFinalPathNameByHandleW did to the
3054 drive letter. */
3055 static bool
3056 revert_virtual_drive (PUNICODE_STRING upath, PUNICODE_STRING fpath,
3057 bool is_remote, ULONG ci_flag)
3059 /* Get the drive's target path. */
3060 WCHAR drive[3] = {(WCHAR) towupper (upath->Buffer[4]), L':', L'\0'};
3061 WCHAR target[MAX_PATH];
3062 UNICODE_STRING tpath;
3063 WCHAR *p;
3065 DWORD remlen = QueryDosDeviceW (drive, target, MAX_PATH);
3066 if (remlen < 3)
3067 return false;
3068 remlen -= 2; /* Two L'\0' */
3070 if (target[remlen - 1] == L'\\')
3071 remlen--;
3072 RtlInitCountedUnicodeString (&tpath, target, remlen * sizeof (WCHAR));
3074 const USHORT uncp_len = is_remote ? ro_u_uncp.Length / sizeof (WCHAR) - 1 : 0;
3076 if (is_remote)
3078 /* target path starts with \??\UNC\. */
3079 if (RtlEqualUnicodePathPrefix (&tpath, &ro_u_uncp, TRUE))
3081 remlen -= uncp_len;
3082 p = target + uncp_len;
3084 /* target path starts with \Device\<redirector>. */
3085 else if ((p = wcschr (target, L';'))
3086 && p + 3 < target + remlen
3087 && wcsncmp (p + 1, drive, 2) == 0
3088 && (p = wcschr (p + 3, L'\\')))
3089 remlen -= p - target;
3090 else
3091 return false;
3092 if (wcsncasecmp (fpath->Buffer + uncp_len, p, remlen))
3093 return false;
3095 else if (!RtlEqualUnicodePathPrefix (fpath, &tpath, TRUE))
3096 return false;
3097 /* Replace fpath with source drive letter and append reminder of
3098 final path after skipping target path */
3099 fpath->Buffer[4] = drive[0]; /* Drive letter */
3100 fpath->Buffer[5] = L':';
3101 WCHAR *to = fpath->Buffer + 6; /* Next to L':' */
3102 WCHAR *from = fpath->Buffer + uncp_len + remlen;
3103 memmove (to, from, (wcslen (from) + 1) * sizeof (WCHAR));
3104 fpath->Length -= (from - to) * sizeof (WCHAR);
3105 if (RtlEqualUnicodeString (upath, fpath, !!ci_flag))
3106 return false;
3107 return true;
3110 /* Check if PATH is a symlink. PATH must be a valid Win32 path name.
3112 If PATH is a symlink, put the value of the symlink--the file to
3113 which it points--into CONTENTS.
3115 Set PATH_SYMLINK if PATH is a symlink.
3117 If PATH is a symlink, return the length stored into CONTENTS. If
3118 the inner components of PATH contain native symlinks or junctions,
3119 or if the drive is a virtual drive, compare PATH with the result
3120 returned by GetFinalPathNameByHandleA. If they differ, store the
3121 final path in CONTENTS and return the negative of its length. In
3122 all other cases, return 0. */
3125 symlink_info::check (char *path, const suffix_info *suffixes, fs_info &fs,
3126 path_conv_handle &conv_hdl)
3128 int res;
3129 HANDLE h;
3130 NTSTATUS status;
3131 UNICODE_STRING upath;
3132 OBJECT_ATTRIBUTES attr;
3133 IO_STATUS_BLOCK io;
3134 suffix_scan suffix;
3135 bool had_ext;
3137 const ULONG ci_flag = cygwin_shared->obcaseinsensitive
3138 || (mount_flags () & MOUNT_NOPOSIX)
3139 ? OBJ_CASE_INSENSITIVE : 0;
3140 /* TODO: Temporarily do all char->UNICODE conversion here. This should
3141 already be slightly faster than using Ascii functions. */
3142 tmp_pathbuf tp;
3143 tp.u_get (&upath);
3144 InitializeObjectAttributes (&attr, &upath, ci_flag, NULL, NULL);
3146 /* This label is used in case we encounter a FS which only handles
3147 DOS paths. See below. */
3148 bool restarted = false;
3149 restart:
3151 reset ();
3152 h = NULL;
3153 res = 0;
3155 PVOID eabuf = &nfs_aol_ffei;
3156 ULONG easize = sizeof nfs_aol_ffei;
3158 ext_here = suffix.has (path, suffixes);
3159 /* If the filename is too long, don't even try. */
3160 if (!ext_here)
3162 set_error (ENAMETOOLONG);
3163 goto file_not_symlink;
3166 had_ext = !!*ext_here;
3167 path_len (ext_here - path);
3169 while (suffix.next ())
3171 res = 0;
3172 error (0);
3173 get_nt_native_path (suffix.path, upath, mount_flags () & MOUNT_DOS);
3174 if (h)
3176 NtClose (h);
3177 h = NULL;
3179 /* The EA given to NtCreateFile allows to get a handle to a symlink on
3180 an NFS share, rather than getting a handle to the target of the
3181 symlink (which would spoil the task of this method quite a bit).
3182 Fortunately it's ignored on most other file systems so we don't have
3183 to special case NFS too much. */
3184 status = NtCreateFile (&h,
3185 READ_CONTROL | FILE_READ_ATTRIBUTES | FILE_READ_EA,
3186 &attr, &io, NULL, 0, FILE_SHARE_VALID_FLAGS,
3187 FILE_OPEN,
3188 FILE_OPEN_REPARSE_POINT
3189 | FILE_OPEN_FOR_BACKUP_INTENT,
3190 eabuf, easize);
3191 debug_printf ("%y = NtCreateFile (%S)", status, &upath);
3192 /* No right to access EAs or EAs not supported? */
3193 if (!NT_SUCCESS (status)
3194 && (status == STATUS_ACCESS_DENIED
3195 || status == STATUS_EAS_NOT_SUPPORTED
3196 || status == STATUS_NOT_SUPPORTED
3197 || status == STATUS_INVALID_NETWORK_RESPONSE
3198 /* Or a bug in Samba 3.2.x (x <= 7) when accessing a share's
3199 root dir which has EAs enabled? */
3200 || status == STATUS_INVALID_PARAMETER))
3202 /* If EAs are not supported, there's no sense to check them again
3203 with suffixes attached. So we set eabuf/easize to 0 here once. */
3204 if (status == STATUS_EAS_NOT_SUPPORTED
3205 || status == STATUS_NOT_SUPPORTED)
3207 eabuf = NULL;
3208 easize = 0;
3210 status = NtOpenFile (&h, READ_CONTROL | FILE_READ_ATTRIBUTES,
3211 &attr, &io, FILE_SHARE_VALID_FLAGS,
3212 FILE_OPEN_NO_RECALL
3213 | FILE_OPEN_REPARSE_POINT
3214 | FILE_OPEN_FOR_BACKUP_INTENT);
3215 debug_printf ("%y = NtOpenFile (no-EAs %S)", status, &upath);
3217 if (status == STATUS_OBJECT_NAME_NOT_FOUND)
3219 /* There are filesystems out in the wild (Netapp, NWFS, and others)
3220 which are uncapable of generating pathnames outside the Win32
3221 rules. That means, filenames on these FSes must not have a
3222 leading space or trailing dots and spaces. This code snippet
3223 manages them. I really hope it's streamlined enough not to
3224 slow down normal operation. This extra check only kicks in if
3225 we encountered a STATUS_OBJECT_NAME_NOT_FOUND *and* we didn't
3226 already attach a suffix. */
3227 if (!restarted && !*ext_here && ext_here[-1] != '\\'
3228 && !(mount_flags () & MOUNT_DOS))
3230 /* Check for trailing dot or space or leading space in
3231 last component. */
3232 char *p = ext_here - 1;
3233 if (*p != '.' && *p != ' ')
3235 while (*--p != '\\')
3236 assert(p >= path);
3237 if (*++p != ' ')
3238 p = NULL;
3240 if (p)
3242 /* If so, check if file resides on one of the known broken
3243 FSes only supporting filenames following DOS rules. */
3244 fs.update (&upath, NULL);
3245 if (fs.has_dos_filenames_only ())
3247 /* If so, try again. Since we now know the FS, the
3248 filenames will be tweaked to follow DOS rules via the
3249 third parameter in the call to get_nt_native_path. */
3250 mount_flags (mount_flags () | MOUNT_DOS);
3251 restarted = true;
3252 goto restart;
3257 else if (status == STATUS_NETWORK_OPEN_RESTRICTION
3258 || status == STATUS_SYMLINK_CLASS_DISABLED)
3260 /* These status codes are returned if you try to open a native
3261 symlink and the usage of this kind of symlink is forbidden
3262 (see fsutil). Since we can't open them at all, not even for
3263 stat purposes, we have to return a POSIX error code which is
3264 at least a bit helpful.
3266 Additionally Windows 8 introduces a bug in NFS: If you have
3267 a symlink to a directory, with symlinks underneath, resolving
3268 the second level of symlinks fails if remote->remote symlinks
3269 are disabled in fsutil. Unfortunately that's the default. */
3270 set_error (ELOOP);
3271 break;
3274 if (NT_SUCCESS (status)
3275 /* Check file system while we're having the file open anyway.
3276 This speeds up path_conv noticably (~10%). */
3277 && (fs.inited () || fs.update (&upath, h)))
3279 status = conv_hdl.get_finfo (h, fs.is_nfs ());
3280 if (NT_SUCCESS (status))
3281 fileattr (conv_hdl.get_dosattr (h, fs.is_nfs ()));
3283 if (!NT_SUCCESS (status))
3285 debug_printf ("%y = NtQueryInformationFile (%S)", status, &upath);
3286 fileattr (INVALID_FILE_ATTRIBUTES);
3288 /* One of the inner path components is invalid, or the path contains
3289 invalid characters. Bail out with ENOENT.
3291 STATUS_IO_REPARSE_TAG_NOT_HANDLED is returned when trying to
3292 traversing a WSL symlink. For all practical purposes it's
3293 equivalent to traversing SYSTEM- or LNK-type symlink returning
3294 STATUS_OBJECT_PATH_NOT_FOUND.
3296 Note that additional STATUS_OBJECT_PATH_INVALID and
3297 STATUS_OBJECT_PATH_SYNTAX_BAD status codes exist. The first one
3298 is seemingly not generated by NtQueryInformationFile, the latter
3299 is only generated if the path is no absolute path within the
3300 NT name space, which should not happen and would point to an
3301 error in get_nt_native_path. Both status codes are deliberately
3302 not tested here unless proved necessary. */
3303 if (status == STATUS_OBJECT_PATH_NOT_FOUND
3304 || status == STATUS_IO_REPARSE_TAG_NOT_HANDLED
3305 || status == STATUS_OBJECT_NAME_INVALID
3306 || status == STATUS_BAD_NETWORK_PATH
3307 || status == STATUS_BAD_NETWORK_NAME
3308 || status == STATUS_NO_MEDIA_IN_DEVICE)
3310 set_error (ENOENT);
3311 if (ext_tacked_on () && !had_ext)
3313 *ext_here = '\0';
3314 ext_tacked_on (false);
3315 ext_here = NULL;
3316 path_len (0);
3318 goto file_not_symlink;
3320 if (status != STATUS_OBJECT_NAME_NOT_FOUND
3321 && status != STATUS_NO_SUCH_FILE) /* ENOENT on NFS or 9x share */
3323 /* The file exists, but the user can't access it for one reason
3324 or the other. To get the file attributes we try to access the
3325 information by opening the parent directory and getting the
3326 file attributes using a matching NtQueryDirectoryFile call. */
3327 UNICODE_STRING dirname, basename;
3328 OBJECT_ATTRIBUTES dattr;
3329 HANDLE dir;
3330 struct {
3331 FILE_ID_BOTH_DIR_INFORMATION fdi;
3332 WCHAR dummy_buf[NAME_MAX + 1];
3333 } fdi_buf;
3335 RtlSplitUnicodePath (&upath, &dirname, &basename);
3336 InitializeObjectAttributes (&dattr, &dirname, ci_flag,
3337 NULL, NULL);
3338 status = NtOpenFile (&dir, SYNCHRONIZE | FILE_LIST_DIRECTORY,
3339 &dattr, &io, FILE_SHARE_VALID_FLAGS,
3340 FILE_SYNCHRONOUS_IO_NONALERT
3341 | FILE_OPEN_NO_RECALL
3342 | FILE_OPEN_FOR_BACKUP_INTENT
3343 | FILE_DIRECTORY_FILE);
3344 if (!NT_SUCCESS (status))
3346 debug_printf ("%y = NtOpenFile(%S)", status, &dirname);
3347 /* There's a special case if the file is itself the root
3348 of a drive which is not accessible by the current user.
3349 This case is only recognized by the length of the
3350 basename part. If it's 0, the incoming file is the
3351 root of a drive. So we at least know it's a directory. */
3352 if (basename.Length)
3353 fileattr (FILE_ATTRIBUTE_DIRECTORY);
3354 else
3356 fileattr (0);
3357 set_error (geterrno_from_nt_status (status));
3360 else
3362 status = NtQueryDirectoryFile (dir, NULL, NULL, NULL, &io,
3363 &fdi_buf, sizeof fdi_buf,
3364 FileIdBothDirectoryInformation,
3365 TRUE, &basename, TRUE);
3366 /* Take the opportunity to check file system while we're
3367 having the handle to the parent dir. */
3368 fs.update (&upath, dir);
3369 NtClose (dir);
3370 if (!NT_SUCCESS (status))
3372 debug_printf ("%y = NtQueryDirectoryFile(%S)",
3373 status, &dirname);
3374 if (status == STATUS_NO_SUCH_FILE)
3376 /* This can happen when trying to access files
3377 which match DOS device names on SMB shares.
3378 NtOpenFile failed with STATUS_ACCESS_DENIED,
3379 but the NtQueryDirectoryFile tells us the
3380 file doesn't exist. We're suspicious in this
3381 case and retry with the next suffix instead of
3382 just giving up. */
3383 set_error (ENOENT);
3384 continue;
3386 fileattr (0);
3388 else
3390 PFILE_ALL_INFORMATION pfai = conv_hdl.fai ();
3392 fileattr (fdi_buf.fdi.FileAttributes);
3393 memcpy (&pfai->BasicInformation.CreationTime,
3394 &fdi_buf.fdi.CreationTime,
3395 4 * sizeof (LARGE_INTEGER));
3396 pfai->BasicInformation.FileAttributes = fileattr ();
3397 pfai->StandardInformation.AllocationSize.QuadPart
3398 = fdi_buf.fdi.AllocationSize.QuadPart;
3399 pfai->StandardInformation.EndOfFile.QuadPart
3400 = fdi_buf.fdi.EndOfFile.QuadPart;
3401 pfai->StandardInformation.NumberOfLinks = 1;
3402 pfai->InternalInformation.IndexNumber.QuadPart
3403 = fdi_buf.fdi.FileId.QuadPart;
3406 ext_tacked_on (!!*ext_here);
3407 goto file_not_symlink;
3409 set_error (ENOENT);
3410 continue;
3413 ext_tacked_on (!!*ext_here);
3414 /* Don't allow to returns directories with appended suffix. If we found
3415 a directory with a suffix which has been appended here, then this
3416 directory doesn't match the request. So, just do as usual if file
3417 hasn't been found. */
3418 if (ext_tacked_on () && !had_ext
3419 && (fileattr () & FILE_ATTRIBUTE_DIRECTORY))
3421 fileattr (INVALID_FILE_ATTRIBUTES);
3422 set_error (ENOENT);
3423 continue;
3426 /* Consider the situation where a virtual drive points to a native
3427 symlink. Opening the virtual drive with FILE_OPEN_REPARSE_POINT
3428 actually opens the symlink. If this symlink points to another
3429 directory using a relative path, symlink evaluation goes totally
3430 awry. We never want a virtual drive evaluated as symlink. */
3431 if (upath.Length <= 14)
3432 goto file_not_symlink;
3434 /* Offline files, even if reparse points, are not symlinks. */
3435 if (isoffline (fileattr ()))
3436 goto file_not_symlink;
3438 /* Reparse points are potentially symlinks. This check must be
3439 performed before checking the SYSTEM attribute for sysfile
3440 symlinks, since reparse points can have this flag set, too. */
3441 if (fileattr () & FILE_ATTRIBUTE_REPARSE_POINT)
3443 res = check_reparse_point (h, fs.is_remote_drive ());
3444 if (res > 0)
3446 /* A symlink is never a directory. */
3447 conv_hdl.fai ()->BasicInformation.FileAttributes
3448 &= ~FILE_ATTRIBUTE_DIRECTORY;
3449 break;
3451 else if (res == 0 && (path_flags () & PATH_REP))
3452 /* Known reparse point but not a symlink. */
3453 goto file_not_symlink;
3454 else
3456 /* Volume moint point or unrecognized reparse point type.
3457 Make sure the open handle is not used in later stat calls.
3458 The handle has been opened with the FILE_OPEN_REPARSE_POINT
3459 flag, so it's a handle to the reparse point, not a handle
3460 to the volumes root dir. */
3461 pc_flags (pc_flags () & ~PC_KEEP_HANDLE);
3462 /* Volume mount point: The filesystem information for the top
3463 level directory should be for the volume top level directory,
3464 rather than for the reparse point itself. So we fetch the
3465 filesystem information again, but with a NULL handle.
3466 This does what we want because fs_info::update opens the
3467 handle without FILE_OPEN_REPARSE_POINT. */
3468 if (res < 0)
3469 fs.update (&upath, NULL);
3473 /* Windows shortcuts are potentially treated as symlinks. Valid Cygwin
3474 & U/WIN shortcuts are R/O, but definitely not directories. */
3475 else if ((fileattr () & (FILE_ATTRIBUTE_READONLY
3476 | FILE_ATTRIBUTE_DIRECTORY))
3477 == FILE_ATTRIBUTE_READONLY
3478 && suffix.lnk_match ())
3480 HANDLE sym_h;
3482 status = NtOpenFile (&sym_h, SYNCHRONIZE | GENERIC_READ, &attr, &io,
3483 FILE_SHARE_VALID_FLAGS,
3484 FILE_OPEN_NO_RECALL
3485 | FILE_OPEN_FOR_BACKUP_INTENT
3486 | FILE_SYNCHRONOUS_IO_NONALERT);
3487 if (!NT_SUCCESS (status))
3488 res = 0;
3489 else
3491 res = check_shortcut (sym_h);
3492 NtClose (sym_h);
3494 if (!res)
3496 /* If searching for `foo' and then finding a `foo.lnk' which
3497 is no shortcut, return the same as if file not found. */
3498 if (ext_tacked_on ())
3500 fileattr (INVALID_FILE_ATTRIBUTES);
3501 set_error (ENOENT);
3502 continue;
3505 else if (contents[0] != ':' || contents[1] != '\\'
3506 || !parse_device (contents))
3507 break;
3508 if (fs.is_nfs () && dev () == FH_FIFO)
3510 conv_hdl.nfsattr ()->type = NF3FIFO;
3511 conv_hdl.nfsattr ()->mode = mode ();
3512 conv_hdl.nfsattr ()->size = 0;
3513 /* Marker for fhandler_base::fstat_by_nfs_ea not to override
3514 the cached fattr3 data with fresh data from the filesystem,
3515 even if the handle is used for other purposes than stat. */
3516 conv_hdl.nfsattr ()->filler1 = NF3FIFO;
3520 /* If searching for `foo' and then finding a `foo.lnk' which is
3521 no shortcut, return the same as if file not found. */
3522 else if (suffix.lnk_match () && ext_tacked_on ())
3524 fileattr (INVALID_FILE_ATTRIBUTES);
3525 set_error (ENOENT);
3526 continue;
3529 /* This is the old Cygwin method creating symlinks. A symlink will
3530 have the `system' file attribute. Only files can be symlinks
3531 (which can be symlinks to directories). */
3532 else if ((fileattr () & (FILE_ATTRIBUTE_SYSTEM
3533 | FILE_ATTRIBUTE_DIRECTORY))
3534 == FILE_ATTRIBUTE_SYSTEM)
3536 HANDLE sym_h;
3538 status = NtOpenFile (&sym_h, SYNCHRONIZE | GENERIC_READ, &attr, &io,
3539 FILE_SHARE_VALID_FLAGS,
3540 FILE_OPEN_NO_RECALL
3541 | FILE_OPEN_FOR_BACKUP_INTENT
3542 | FILE_SYNCHRONOUS_IO_NONALERT);
3544 if (!NT_SUCCESS (status))
3545 res = 0;
3546 else
3548 res = check_sysfile (sym_h);
3549 NtClose (sym_h);
3551 if (res)
3552 break;
3555 /* If the file is on an NFS share and could be opened with extended
3556 attributes, check if it's a symlink or FIFO. */
3557 else if (fs.is_nfs ())
3559 /* Make sure filler1 is 0, so we can use it safely as a marker. */
3560 conv_hdl.nfsattr ()->filler1 = 0;
3561 switch (conv_hdl.nfsattr ()->type & 7)
3563 case NF3LNK:
3564 res = check_nfs_symlink (h);
3565 break;
3566 case NF3FIFO:
3567 /* Enable real FIFOs recognized as such. */
3568 dev (FH_FIFO);
3569 mode (S_IFIFO | (conv_hdl.nfsattr ()->mode & ~S_IFMT));
3570 break;
3571 default:
3572 break;
3574 if (res)
3575 break;
3578 /* Check if the inner path components contain native symlinks or
3579 junctions, or if the drive is a virtual drive. Compare incoming
3580 path with path returned by GetFinalPathNameByHandleA. If they
3581 differ, return the final path as symlink content and set symlen
3582 to a negative value. This forces path_conv::check to restart
3583 symlink evaluation with the new path. */
3584 if ((pc_flags () & (PC_SYM_FOLLOW | PC_SYM_NOFOLLOW_REP))
3585 == PC_SYM_FOLLOW)
3587 PWCHAR fpbuf = tp.w_get ();
3588 DWORD ret;
3590 ret = GetFinalPathNameByHandleW (h, fpbuf, NT_MAX_PATH, 0);
3591 if (ret)
3593 UNICODE_STRING fpath;
3595 /* If incoming path has no trailing backslash, but final path
3596 has one, drop trailing backslash from final path so the
3597 below string comparison has a chance to succeed.
3598 On the contrary, if incoming path has trailing backslash,
3599 but final path does not have one, add trailing backslash
3600 to the final path. */
3601 if (upath.Buffer[(upath.Length - 1) / sizeof (WCHAR)] != L'\\'
3602 && fpbuf[ret - 1] == L'\\')
3603 fpbuf[--ret] = L'\0';
3604 if (upath.Buffer[(upath.Length - 1) / sizeof (WCHAR)] == L'\\'
3605 && fpbuf[ret - 1] != L'\\' && ret < NT_MAX_PATH - 1)
3607 fpbuf[ret++] = L'\\';
3608 fpbuf[ret] = L'\0';
3610 fpbuf[1] = L'?'; /* \\?\ --> \??\ */
3611 RtlInitCountedUnicodeString (&fpath, fpbuf, ret * sizeof (WCHAR));
3612 if (!RtlEqualUnicodeString (&upath, &fpath, !!ci_flag))
3614 /* If the incoming path is a local drive letter path... */
3615 if (!RtlEqualUnicodePathPrefix (&upath, &ro_u_uncp, TRUE))
3617 /* ...and the final path is an UNC path, revert to the
3618 drive letter path syntax. */
3619 if (RtlEqualUnicodePathPrefix (&fpath, &ro_u_uncp, TRUE))
3621 if (!revert_virtual_drive (&upath, &fpath, true,
3622 ci_flag))
3623 goto file_not_symlink;
3625 /* ...otherwise, if the final path changes the drive
3626 letter, let revert_virtual_drive check for a
3627 virtual drive and revert that. */
3628 else if (upath.Buffer[5] == L':'
3629 && (WCHAR) towupper (upath.Buffer[4])
3630 != (WCHAR) towupper (fpath.Buffer[4]))
3632 if (!revert_virtual_drive (&upath, &fpath, false,
3633 ci_flag))
3634 goto file_not_symlink;
3637 /* upath.Buffer is big enough and unused from this point on.
3638 Reuse it here, avoiding yet another buffer allocation. */
3639 char *nfpath = (char *) upath.Buffer;
3640 sys_wcstombs (nfpath, NT_MAX_PATH, fpbuf);
3641 /* For final paths differing in inner path components return
3642 length as negative value. This informs path_conv::check
3643 to skip realpath handling on the last path component. */
3644 res = -posixify (nfpath);
3645 break;
3650 /* Normal file. */
3651 file_not_symlink:
3652 issymlink (false);
3653 syscall_printf ("%s", isdevice () ? "is a device" : "not a symlink");
3654 res = 0;
3655 break;
3658 if (h)
3660 if (pc_flags () & PC_KEEP_HANDLE)
3661 conv_hdl.set (h);
3662 else
3663 NtClose (h);
3666 syscall_printf ("%d = symlink.check(%s, %p) (mount_flags %y, path_flags %y)",
3667 res, suffix.path, contents, mount_flags (), path_flags ());
3668 return res;
3671 /* "path" is the path in a virtual symlink. Set a symlink_info struct from
3672 that and proceed with further path checking afterwards. */
3674 symlink_info::set (char *path)
3676 strcpy (contents, path);
3677 mount_flags (0);
3678 path_flags (PATH_SYMLINK);
3679 fileattr (FILE_ATTRIBUTE_NORMAL);
3680 error (0);
3681 issymlink (true);
3682 ext_tacked_on (false);
3683 ext_here = NULL;
3684 path_len (0);
3685 dev (FH_NADA);
3686 return strlen (path);
3689 /* readlink system call */
3691 extern "C" ssize_t
3692 readlink (const char *__restrict path, char *__restrict buf, size_t buflen)
3694 if (buflen < 0)
3696 set_errno (ENAMETOOLONG);
3697 return -1;
3700 path_conv pathbuf (path, PC_SYM_CONTENTS, stat_suffixes);
3702 if (pathbuf.error)
3704 set_errno (pathbuf.error);
3705 syscall_printf ("-1 = readlink (%s, %p, %lu)", path, buf, buflen);
3706 return -1;
3709 if (!pathbuf.exists ())
3711 set_errno (ENOENT);
3712 return -1;
3715 if (!pathbuf.issymlink ())
3717 if (pathbuf.exists ())
3718 set_errno (EINVAL);
3719 return -1;
3722 size_t pathbuf_len = strlen (pathbuf.get_win32 ());
3723 ssize_t len = MIN (buflen, pathbuf_len);
3724 memcpy (buf, pathbuf.get_win32 (), len);
3726 /* errno set by symlink.check if error */
3727 return len;
3730 /* Some programs rely on st_dev/st_ino being unique for each file.
3731 Hash the path name and hope for the best. The hash arg is not
3732 always initialized to zero since readdir needs to compute the
3733 dirent ino_t based on a combination of the hash of the directory
3734 done during the opendir call and the hash or the filename within
3735 the directory. FIXME: Not bullet-proof. */
3736 /* Cygwin internal */
3737 ino_t
3738 hash_path_name (ino_t hash, PUNICODE_STRING name)
3740 if (name->Length == 0)
3741 return hash;
3743 /* Build up hash. Name is already normalized */
3744 USHORT len = name->Length / sizeof (WCHAR);
3745 for (USHORT idx = 0; idx < len; ++idx)
3746 hash = RtlUpcaseUnicodeChar (name->Buffer[idx])
3747 + (hash << 6) + (hash << 16) - hash;
3748 return hash;
3751 ino_t
3752 hash_path_name (ino_t hash, PCWSTR name)
3754 UNICODE_STRING uname;
3755 RtlInitUnicodeString (&uname, name);
3756 return hash_path_name (hash, &uname);
3759 ino_t
3760 hash_path_name (ino_t hash, const char *name)
3762 UNICODE_STRING uname;
3763 tmp_pathbuf tp;
3765 tp.u_get (&uname);
3766 sys_mbstouni (&uname, HEAP_NOTHEAP, name);
3767 ino_t ret = hash_path_name (hash, &uname);
3768 return ret;
3771 extern "C" char *
3772 getcwd (char *buf, size_t ulen)
3774 char* res = NULL;
3776 __try
3778 if (ulen == 0 && buf)
3779 set_errno (EINVAL);
3780 else
3781 res = cygheap->cwd.get (buf, 1, 1, ulen);
3783 __except (EFAULT) {}
3784 __endtry
3785 return res;
3788 /* getwd: Legacy. */
3789 extern "C" char *
3790 getwd (char *buf)
3792 return getcwd (buf, PATH_MAX + 1); /*Per SuSv3!*/
3795 extern "C" char *
3796 get_current_dir_name (void)
3798 const char *pwd = getenv ("PWD");
3799 char *cwd = getcwd (NULL, 0);
3800 struct stat pwdbuf, cwdbuf;
3802 if (pwd && strcmp (pwd, cwd) != 0
3803 && stat (pwd, &pwdbuf) == 0
3804 && stat (cwd, &cwdbuf) == 0
3805 && pwdbuf.st_dev == cwdbuf.st_dev
3806 && pwdbuf.st_ino == cwdbuf.st_ino)
3808 cwd = (char *) realloc (cwd, strlen (pwd) + 1);
3809 strcpy (cwd, pwd);
3812 return cwd;
3815 /* chdir: POSIX 5.2.1.1 */
3816 extern "C" int
3817 chdir (const char *in_dir)
3819 int res = -1;
3821 __try
3823 if (!*in_dir)
3825 set_errno (ENOENT);
3826 __leave;
3829 syscall_printf ("dir '%s'", in_dir);
3831 /* Convert path. PC_NONULLEMPTY ensures that we don't check for
3832 NULL/empty/invalid again. */
3833 path_conv path (in_dir, PC_SYM_FOLLOW | PC_POSIX | PC_NONULLEMPTY);
3834 if (path.error)
3836 set_errno (path.error);
3837 syscall_printf ("-1 = chdir (%s)", in_dir);
3838 __leave;
3841 const char *posix_cwd = NULL;
3842 dev_t devn = path.get_device ();
3843 if (!path.exists ())
3844 set_errno (ENOENT);
3845 else if (!path.isdir ())
3846 set_errno (ENOTDIR);
3847 else if (!isvirtual_dev (devn))
3849 /* The sequence chdir("xx"); chdir(".."); must be a noop if xx
3850 is not a symlink. This is exploited by find.exe.
3851 The posix_cwd is just path.get_posix ().
3852 In other cases we let cwd.set obtain the Posix path through
3853 the mount table. */
3854 if (!isdrive (path.get_posix ()))
3855 posix_cwd = path.get_posix ();
3856 res = 0;
3858 else
3860 posix_cwd = path.get_posix ();
3861 res = 0;
3864 if (!res)
3865 res = cygheap->cwd.set (&path, posix_cwd);
3867 /* Note that we're accessing cwd.posix without a lock here.
3868 I didn't think it was worth locking just for strace. */
3869 syscall_printf ("%R = chdir() cygheap->cwd.posix '%s' native '%S'", res,
3870 cygheap->cwd.get_posix (), path.get_nt_native_path ());
3872 __except (EFAULT)
3874 res = -1;
3876 __endtry
3877 return res;
3880 extern "C" int
3881 fchdir (int fd)
3883 int res;
3884 cygheap_fdget cfd (fd);
3885 if (cfd >= 0)
3886 res = chdir (cfd->get_name ());
3887 else
3888 res = -1;
3890 syscall_printf ("%R = fchdir(%d)", res, fd);
3891 return res;
3894 /******************** Exported Path Routines *********************/
3896 /* Cover functions to the path conversion routines.
3897 These are exported to the world as cygwin_foo by cygwin.din. */
3899 #define return_with_errno(x) \
3900 do {\
3901 int err = (x);\
3902 if (!err)\
3903 return 0;\
3904 set_errno (err);\
3905 return -1;\
3906 } while (0)
3908 extern "C" ssize_t
3909 cygwin_conv_path (cygwin_conv_path_t what, const void *from, void *to,
3910 size_t size)
3912 tmp_pathbuf tp;
3913 path_conv p;
3914 size_t lsiz = 0;
3915 char *buf = NULL;
3916 PWCHAR path = NULL;
3917 int error = 0;
3918 int how = what & CCP_CONVFLAGS_MASK;
3919 what &= CCP_CONVTYPE_MASK;
3920 int ret = -1;
3922 __try
3924 if (!from)
3926 set_errno (EINVAL);
3927 __leave;
3930 switch (what)
3932 case CCP_POSIX_TO_WIN_A:
3934 p.check ((const char *) from,
3935 PC_POSIX | PC_SYM_FOLLOW | PC_SYM_NOFOLLOW_REP
3936 | PC_NO_ACCESS_CHECK
3937 | ((how & CCP_RELATIVE) ? PC_NOFULL : 0), stat_suffixes);
3938 if (p.error)
3940 set_errno (p.error);
3941 __leave;
3943 PUNICODE_STRING up = p.get_nt_native_path ();
3944 buf = tp.c_get ();
3945 sys_wcstombs (buf, NT_MAX_PATH,
3946 up->Buffer, up->Length / sizeof (WCHAR));
3947 /* Convert native path to standard DOS path. */
3948 if (!strncmp (buf, "\\??\\", 4))
3950 buf += 4;
3951 if (buf[1] != ':') /* native UNC path */
3952 *(buf += 2) = '\\';
3954 else if (*buf == '\\')
3956 /* Device name points to somewhere else in the NT namespace.
3957 Use GLOBALROOT prefix to convert to Win32 path. */
3958 char *p = buf + sys_wcstombs (buf, NT_MAX_PATH,
3959 ro_u_globalroot.Buffer,
3960 ro_u_globalroot.Length
3961 / sizeof (WCHAR));
3962 sys_wcstombs (p, NT_MAX_PATH - (p - buf),
3963 up->Buffer, up->Length / sizeof (WCHAR));
3965 lsiz = strlen (buf) + 1;
3966 /* TODO: Incoming "." is a special case which leads to a trailing
3967 backslash ".\\" in the Win32 path. That's a result of the
3968 conversion in normalize_posix_path. This should not occur
3969 so the below code is just a band-aid. */
3970 if ((how & CCP_RELATIVE) && !strcmp ((const char *) from, ".")
3971 && !strcmp (buf, ".\\"))
3973 lsiz = 2;
3974 buf[1] = '\0';
3977 break;
3978 case CCP_POSIX_TO_WIN_W:
3979 p.check ((const char *) from,
3980 PC_POSIX | PC_SYM_FOLLOW | PC_SYM_NOFOLLOW_REP
3981 | PC_NO_ACCESS_CHECK
3982 | ((how & CCP_RELATIVE) ? PC_NOFULL : 0), stat_suffixes);
3983 if (p.error)
3985 set_errno (p.error);
3986 __leave;
3988 /* Relative Windows paths are always restricted to MAX_PATH chars. */
3989 if ((how & CCP_RELATIVE) && !isabspath (p.get_win32 ())
3990 && sys_mbstowcs (NULL, 0, p.get_win32 ()) > MAX_PATH)
3992 /* Recreate as absolute path. */
3993 p.check ((const char *) from, PC_POSIX | PC_SYM_FOLLOW
3994 | PC_NO_ACCESS_CHECK);
3995 if (p.error)
3997 set_errno (p.error);
3998 __leave;
4001 lsiz = p.get_wide_win32_path_len () + 1;
4002 path = p.get_nt_native_path ()->Buffer;
4004 /* Convert native path to standard DOS path. */
4005 if (!wcsncmp (path, L"\\??\\", 4))
4007 path[1] = L'\\';
4009 /* Drop long path prefix for short pathnames. Unfortunately there's
4010 quite a bunch of Win32 functions, especially in user32.dll,
4011 apparently, which don't grok long path names at all, not even
4012 in the UNICODE API. */
4013 if ((path[5] == L':' && lsiz <= MAX_PATH + 4)
4014 || (!wcsncmp (path + 4, L"UNC\\", 4) && lsiz <= MAX_PATH + 6))
4016 path += 4;
4017 lsiz -= 4;
4018 if (path[1] != L':')
4020 *(path += 2) = '\\';
4021 lsiz -= 2;
4025 else if (*path == L'\\')
4027 /* Device name points to somewhere else in the NT namespace.
4028 Use GLOBALROOT prefix to convert to Win32 path. */
4029 to = (void *) wcpcpy ((wchar_t *) to, ro_u_globalroot.Buffer);
4030 lsiz += ro_u_globalroot.Length / sizeof (WCHAR);
4032 /* TODO: Same ".\\" band-aid as in CCP_POSIX_TO_WIN_A case. */
4033 if ((how & CCP_RELATIVE) && !strcmp ((const char *) from, ".")
4034 && !wcscmp (path, L".\\"))
4036 lsiz = 2;
4037 path[1] = L'\0';
4039 lsiz *= sizeof (WCHAR);
4040 break;
4041 case CCP_WIN_A_TO_POSIX:
4042 buf = tp.c_get ();
4043 error = mount_table->conv_to_posix_path ((const char *) from, buf,
4044 how | __CCP_APP_SLASH);
4045 if (error)
4047 set_errno (p.error);
4048 __leave;
4050 lsiz = strlen (buf) + 1;
4051 break;
4052 case CCP_WIN_W_TO_POSIX:
4053 buf = tp.c_get ();
4054 error = mount_table->conv_to_posix_path ((const PWCHAR) from, buf,
4055 how | __CCP_APP_SLASH);
4056 if (error)
4058 set_errno (error);
4059 __leave;
4061 lsiz = strlen (buf) + 1;
4062 break;
4063 default:
4064 set_errno (EINVAL);
4065 __leave;
4067 if (!size)
4069 ret = lsiz;
4070 __leave;
4072 if (size < lsiz)
4074 set_errno (ENOSPC);
4075 __leave;
4077 switch (what)
4079 case CCP_POSIX_TO_WIN_A:
4080 case CCP_WIN_A_TO_POSIX:
4081 case CCP_WIN_W_TO_POSIX:
4082 stpcpy ((char *) to, buf);
4083 break;
4084 case CCP_POSIX_TO_WIN_W:
4085 wcpcpy ((PWCHAR) to, path);
4086 break;
4088 ret = 0;
4090 __except (EFAULT) {}
4091 __endtry
4092 return ret;
4095 extern "C" void *
4096 cygwin_create_path (cygwin_conv_path_t what, const void *from)
4098 void *to;
4099 ssize_t size = cygwin_conv_path (what, from, NULL, 0);
4100 if (size <= 0)
4101 to = NULL;
4102 else if (!(to = malloc (size)))
4103 to = NULL;
4104 if (cygwin_conv_path (what, from, to, size) == -1)
4106 free (to);
4107 to = NULL;
4109 return to;
4112 /* The realpath function is required by POSIX:2008. */
4114 extern "C" char *
4115 realpath (const char *__restrict path, char *__restrict resolved)
4117 tmp_pathbuf tp;
4118 char *tpath;
4120 /* Make sure the right errno is returned if path is NULL. */
4121 if (!path)
4123 set_errno (EINVAL);
4124 return NULL;
4127 /* Guard reading from a potentially invalid path and writing to a
4128 potentially invalid resolved. */
4129 __try
4131 /* Win32 drive letter paths and, generally, any path starting with a
4132 backslash, have to be converted to a POSIX path first, because
4133 path_conv leaves the incoming path untouched except for converting
4134 backslashes to forward slashes. This also covers '\\?\ and '\??\'
4135 path prefixes. */
4136 if (isdrive (path) || path[0] == '\\')
4138 tpath = tp.c_get ();
4139 mount_table->conv_to_posix_path (path, tpath, 0);
4141 else
4142 tpath = (char *) path;
4144 path_conv real_path (tpath, PC_SYM_FOLLOW | PC_POSIX, stat_suffixes);
4147 /* POSIX 2008 requires malloc'ing if resolved is NULL, and states
4148 that using non-NULL resolved is asking for portability
4149 problems. */
4151 if (!real_path.error && real_path.exists ())
4153 if (!resolved)
4155 resolved = (char *)
4156 malloc (strlen (real_path.get_posix ()) + 1);
4157 if (!resolved)
4158 return NULL;
4160 strcpy (resolved, real_path.get_posix ());
4161 return resolved;
4164 /* FIXME: on error, Linux puts the name of the path
4165 component which could not be resolved into RESOLVED, but POSIX
4166 does not require this. */
4167 if (resolved)
4168 resolved[0] = '\0';
4169 set_errno (real_path.error ?: ENOENT);
4171 __except (EFAULT) {}
4172 __endtry
4173 return NULL;
4176 /* Linux provides this extension. Since the only portable use of
4177 realpath requires a NULL second argument, we might as well have a
4178 one-argument wrapper. */
4179 extern "C" char *
4180 canonicalize_file_name (const char *path)
4182 return realpath (path, NULL);
4185 /* Return non-zero if path is a POSIX path list.
4186 This is exported to the world as cygwin_foo by cygwin.din.
4188 DOCTOOL-START
4189 <sect1 id="add-func-cygwin-posix-path-list-p">
4190 <para>Rather than use a mode to say what the "proper" path list
4191 format is, we allow any, and give apps the tools they need to
4192 convert between the two. If a ';' is present in the path list it's
4193 a Win32 path list. Otherwise, if the first path begins with
4194 [letter]: (in which case it can be the only element since if it
4195 wasn't a ';' would be present) it's a Win32 path list. Otherwise,
4196 it's a POSIX path list.</para>
4197 </sect1>
4198 DOCTOOL-END
4201 extern "C" int
4202 cygwin_posix_path_list_p (const char *path)
4204 int posix_p = !(strchr (path, ';') || isdrive (path));
4205 return posix_p;
4208 /* These are used for apps that need to convert env vars like PATH back and
4209 forth. The conversion is a two step process. First, an upper bound on the
4210 size of the buffer needed is computed. Then the conversion is done. This
4211 allows the caller to use alloca if it wants. */
4213 static int
4214 conv_path_list_buf_size (const char *path_list, bool to_posix)
4216 int i, num_elms, max_mount_path_len, size;
4217 const char *p;
4219 path_conv pc(".", PC_POSIX);
4220 /* The theory is that an upper bound is
4221 current_size + (num_elms * max_mount_path_len) */
4222 /* FIXME: This method is questionable in the long run. */
4224 unsigned nrel;
4225 char delim = to_posix ? ';' : ':';
4226 for (p = path_list, num_elms = nrel = 0; p; num_elms++)
4228 if (!isabspath (p))
4229 nrel++;
4230 p = strchr (++p, delim);
4233 /* 7: strlen ("//c") + slop, a conservative initial value */
4234 for (max_mount_path_len = sizeof ("/cygdrive/X"), i = 0;
4235 i < mount_table->nmounts; i++)
4237 int mount_len = (to_posix
4238 ? mount_table->mount[i].posix_pathlen
4239 : mount_table->mount[i].native_pathlen);
4240 if (max_mount_path_len < mount_len)
4241 max_mount_path_len = mount_len;
4244 /* 100: slop */
4245 size = strlen (path_list)
4246 + (num_elms * max_mount_path_len)
4247 + (nrel * strlen (to_posix ? pc.get_posix () : pc.get_win32 ()))
4248 + 100;
4250 return size;
4253 extern "C" ssize_t
4254 env_PATH_to_posix (const void *win32, void *posix, size_t size)
4256 return_with_errno (conv_path_list ((const char *) win32, (char *) posix,
4257 size, ENV_CVT));
4260 extern "C" ssize_t
4261 cygwin_conv_path_list (cygwin_conv_path_t what, const void *from, void *to,
4262 size_t size)
4264 int ret;
4265 char *winp = NULL;
4266 void *orig_to = NULL;
4267 tmp_pathbuf tp;
4269 switch (what & CCP_CONVTYPE_MASK)
4271 case CCP_WIN_W_TO_POSIX:
4272 if (!sys_wcstombs_alloc (&winp, HEAP_NOTHEAP, (const wchar_t *) from,
4273 (size_t) -1))
4274 return -1;
4275 what = (what & ~CCP_CONVTYPE_MASK) | CCP_WIN_A_TO_POSIX;
4276 from = (const void *) winp;
4277 break;
4278 case CCP_POSIX_TO_WIN_W:
4279 if (size == 0)
4280 return conv_path_list_buf_size ((const char *) from, 0)
4281 * sizeof (WCHAR);
4282 what = (what & ~CCP_CONVTYPE_MASK) | CCP_POSIX_TO_WIN_A;
4283 orig_to = to;
4284 to = (void *) tp.w_get ();
4285 size = 65536;
4286 break;
4288 switch (what & CCP_CONVTYPE_MASK)
4290 case CCP_WIN_A_TO_POSIX:
4291 case CCP_POSIX_TO_WIN_A:
4292 if (size == 0)
4293 return conv_path_list_buf_size ((const char *) from,
4294 what == CCP_WIN_A_TO_POSIX);
4295 ret = conv_path_list ((const char *) from, (char *) to, size, what);
4296 /* Free winp buffer in case of CCP_WIN_W_TO_POSIX. */
4297 if (winp)
4298 free (winp);
4299 /* Convert to WCHAR in case of CCP_POSIX_TO_WIN_W. */
4300 if (orig_to)
4301 sys_mbstowcs ((wchar_t *) orig_to, size / sizeof (WCHAR),
4302 (const char *) to, (size_t) -1);
4303 return_with_errno (ret);
4304 break;
4305 default:
4306 break;
4308 set_errno (EINVAL);
4309 return -1;
4312 /* cygwin_split_path: Split a path into directory and file name parts.
4313 Buffers DIR and FILE are assumed to be big enough.
4315 Examples (path -> `dir' / `file'):
4316 / -> `/' / `'
4317 "" -> `.' / `'
4318 . -> `.' / `.' (FIXME: should this be `.' / `'?)
4319 .. -> `.' / `..' (FIXME: should this be `..' / `'?)
4320 foo -> `.' / `foo'
4321 foo/bar -> `foo' / `bar'
4322 foo/bar/ -> `foo' / `bar'
4323 /foo -> `/' / `foo'
4324 /foo/bar -> `/foo' / `bar'
4325 c: -> `c:/' / `'
4326 c:/ -> `c:/' / `'
4327 c:foo -> `c:/' / `foo'
4328 c:/foo -> `c:/' / `foo'
4331 extern "C" void
4332 cygwin_split_path (const char *path, char *dir, char *file)
4334 int dir_started_p = 0;
4336 /* Deal with drives.
4337 Remember that c:foo <==> c:/foo. */
4338 if (isdrive (path))
4340 *dir++ = *path++;
4341 *dir++ = *path++;
4342 *dir++ = '/';
4343 if (!*path)
4345 *dir = 0;
4346 *file = 0;
4347 return;
4349 if (isdirsep (*path))
4350 ++path;
4351 dir_started_p = 1;
4354 /* Determine if there are trailing slashes and "delete" them if present.
4355 We pretend as if they don't exist. */
4356 const char *end = path + strlen (path);
4357 /* path + 1: keep leading slash. */
4358 while (end > path + 1 && isdirsep (end[-1]))
4359 --end;
4361 /* At this point, END points to one beyond the last character
4362 (with trailing slashes "deleted"). */
4364 /* Point LAST_SLASH at the last slash (duh...). */
4365 const char *last_slash;
4366 for (last_slash = end - 1; last_slash >= path; --last_slash)
4367 if (isdirsep (*last_slash))
4368 break;
4370 if (last_slash == path)
4372 *dir++ = '/';
4373 *dir = 0;
4375 else if (last_slash > path)
4377 memcpy (dir, path, last_slash - path);
4378 dir[last_slash - path] = 0;
4380 else
4382 if (dir_started_p)
4383 ; /* nothing to do */
4384 else
4385 *dir++ = '.';
4386 *dir = 0;
4389 memcpy (file, last_slash + 1, end - last_slash - 1);
4390 file[end - last_slash - 1] = 0;
4393 /*****************************************************************************/
4395 /* The find_fast_cwd_pointer function and parts of the
4396 cwdstuff::override_win32_cwd method are based on code using the
4397 following license:
4399 Copyright 2010 John Carey. All rights reserved.
4401 Redistribution and use in source and binary forms, with or without
4402 modification, are permitted provided that the following conditions
4403 are met:
4405 1. Redistributions of source code must retain the above
4406 copyright notice, this list of conditions and the following
4407 disclaimer.
4409 2. Redistributions in binary form must reproduce the above
4410 copyright notice, this list of conditions and the following
4411 disclaimer in the documentation and/or other materials provided
4412 with the distribution.
4414 THIS SOFTWARE IS PROVIDED BY JOHN CAREY ``AS IS'' AND ANY EXPRESS
4415 OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
4416 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
4417 ARE DISCLAIMED. IN NO EVENT SHALL JOHN CAREY OR CONTRIBUTORS BE
4418 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
4419 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
4420 OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
4421 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
4422 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
4423 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
4424 USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
4425 DAMAGE. */
4427 void
4428 fcwd_access_t::CopyPath (UNICODE_STRING &target)
4430 /* Copy the Path contents over into the UNICODE_STRING referenced by
4431 target. This is used to set the CurrentDirectoryName in the
4432 user parameter block. */
4433 target = Path;
4436 void
4437 fcwd_access_t::Free (PVOID heap)
4439 /* Decrement the reference count. If it's down to 0, free
4440 structure from heap. */
4441 if (InterlockedDecrement (&ReferenceCount) == 0)
4443 /* The handle on init is always a fresh one, not the handle inherited
4444 from the parent process. We always have to close it here.
4445 Note: The handle could be NULL, if we cd'ed into a virtual dir. */
4446 HANDLE h = DirectoryHandle;
4447 if (h)
4448 NtClose (h);
4449 RtlFreeHeap (heap, 0, this);
4453 void
4454 fcwd_access_t::FillIn (HANDLE dir, PUNICODE_STRING name,
4455 ULONG old_dismount_count)
4457 /* Fill in all values into this FAST_CWD structure. */
4458 DirectoryHandle = dir;
4459 ReferenceCount = 1;
4460 OldDismountCount = old_dismount_count;
4461 /* The fcwd structure stores the device characteristics of the
4462 volume holding the dir. RtlGetCurrentDirectory_U checks
4463 if the FILE_REMOVABLE_MEDIA flag is set and, if so, checks if
4464 the volume is still the same as the one used when opening
4465 the directory handle.
4466 We don't call NtQueryVolumeInformationFile for the \\?\PIPE,
4467 though. It just returns STATUS_INVALID_HANDLE anyway. */
4468 FSCharacteristics = 0;
4469 if (name != &ro_u_pipedir)
4471 IO_STATUS_BLOCK io;
4472 FILE_FS_DEVICE_INFORMATION ffdi;
4473 if (NT_SUCCESS (NtQueryVolumeInformationFile (dir, &io, &ffdi,
4474 sizeof ffdi, FileFsDeviceInformation)))
4475 FSCharacteristics = ffdi.Characteristics;
4477 RtlInitEmptyUnicodeString (&Path, Buffer, name->MaximumLength);
4478 RtlCopyUnicodeString (&Path, name);
4479 if (Path.Buffer[Path.Length / sizeof (WCHAR) - 1] != L'\\')
4481 Path.Buffer[Path.Length / sizeof (WCHAR)] = L'\\';
4482 Path.Length += sizeof (WCHAR);
4486 void
4487 fcwd_access_t::SetDirHandleFromBufferPointer (PWCHAR buf_p, HANDLE dir)
4489 /* Input: The buffer pointer as it's stored in the user parameter block
4490 and a directory handle.
4491 This function computes the address to the FAST_CWD structure based
4492 on the version and overwrites the directory handle. It is only
4493 used if we couldn't figure out the address of fast_cwd_ptr. */
4494 fcwd_access_t *f_cwd;
4495 f_cwd = (fcwd_access_t *)
4496 ((PBYTE) buf_p - __builtin_offsetof (fcwd_access_t, Buffer));
4497 f_cwd->DirectoryHandle = dir;
4500 /* This function scans the code in ntdll.dll to find the address of the
4501 global variable used to access the CWD. While the pointer is global,
4502 it's not exported from the DLL, unfortunately. Therefore we have to
4503 use some knowledge to figure out the address. */
4505 #define peek32(x) (*(int32_t *)(x))
4507 static fcwd_access_t **
4508 find_fast_cwd_pointer ()
4510 /* Fetch entry points of relevant functions in ntdll.dll. */
4511 HMODULE ntdll = GetModuleHandle ("ntdll.dll");
4512 if (!ntdll)
4513 return NULL;
4514 const uint8_t *get_dir = (const uint8_t *)
4515 GetProcAddress (ntdll, "RtlGetCurrentDirectory_U");
4516 const uint8_t *ent_crit = (const uint8_t *)
4517 GetProcAddress (ntdll, "RtlEnterCriticalSection");
4518 if (!get_dir || !ent_crit)
4519 return NULL;
4520 /* Search first relative call instruction in RtlGetCurrentDirectory_U. */
4521 const uint8_t *rcall = (const uint8_t *) memchr (get_dir, 0xe8, 80);
4522 if (!rcall)
4523 return NULL;
4524 /* Fetch offset from instruction and compute address of called function.
4525 This function actually fetches the current FAST_CWD instance and
4526 performs some other actions, not important to us. */
4527 const uint8_t *use_cwd = rcall + 5 + peek32 (rcall + 1);
4528 /* Next we search for the locking mechanism and perform a sanity check.
4529 On Pre-Windows 8 we basically look for the RtlEnterCriticalSection call.
4530 Windows 8 does not call RtlEnterCriticalSection. The code manipulates
4531 the FastPebLock manually, probably because RtlEnterCriticalSection has
4532 been converted to an inline function. Either way, we test if the code
4533 uses the FastPebLock. */
4534 const uint8_t *movrbx;
4535 const uint8_t *lock = (const uint8_t *)
4536 memmem ((const char *) use_cwd, 80,
4537 "\xf0\x0f\xba\x35", 4);
4538 if (lock)
4540 /* The lock instruction tweaks the LockCount member, which is not at
4541 the start of the PRTL_CRITICAL_SECTION structure. So we have to
4542 subtract the offset of LockCount to get the real address. */
4543 PRTL_CRITICAL_SECTION lockaddr =
4544 (PRTL_CRITICAL_SECTION) (lock + 9 + peek32 (lock + 4)
4545 - offsetof (RTL_CRITICAL_SECTION, LockCount));
4546 /* Test if lock address is FastPebLock. */
4547 if (lockaddr != NtCurrentTeb ()->Peb->FastPebLock)
4548 return NULL;
4549 /* Search `mov rel(%rip),%rbx'. This is the instruction fetching the
4550 address of the current fcwd_access_t pointer, and it should be pretty
4551 near to the locking stuff. */
4552 movrbx = (const uint8_t *) memmem ((const char *) lock, 40,
4553 "\x48\x8b\x1d", 3);
4555 else
4557 /* Usually the callq RtlEnterCriticalSection follows right after
4558 fetching the lock address. */
4559 int call_rtl_offset = 7;
4560 /* Search `lea rel(%rip),%rcx'. This loads the address of the lock into
4561 %rcx for the subsequent RtlEnterCriticalSection call. */
4562 lock = (const uint8_t *) memmem ((const char *) use_cwd, 80,
4563 "\x48\x8d\x0d", 3);
4564 if (!lock)
4566 /* Windows 8.1 Preview calls `lea rel(rip),%r12' then some unrelated
4567 ops, then `mov %r12,%rcx', then `callq RtlEnterCriticalSection'. */
4568 lock = (const uint8_t *) memmem ((const char *) use_cwd, 80,
4569 "\x4c\x8d\x25", 3);
4570 call_rtl_offset = 14;
4573 if (!lock)
4575 /* A recent Windows 11 Preview calls `lea rel(rip),%r13' then
4576 some unrelated instructions, then `callq RtlEnterCriticalSection'.
4578 lock = (const uint8_t *) memmem ((const char *) use_cwd, 80,
4579 "\x4c\x8d\x2d", 3);
4580 call_rtl_offset = 24;
4583 if (!lock)
4585 return NULL;
4588 PRTL_CRITICAL_SECTION lockaddr =
4589 (PRTL_CRITICAL_SECTION) (lock + 7 + peek32 (lock + 3));
4590 /* Test if lock address is FastPebLock. */
4591 if (lockaddr != NtCurrentTeb ()->Peb->FastPebLock)
4592 return NULL;
4593 /* Next is the `callq RtlEnterCriticalSection'. */
4594 lock += call_rtl_offset;
4595 if (lock[0] != 0xe8)
4596 return NULL;
4597 const uint8_t *call_addr = (const uint8_t *)
4598 (lock + 5 + peek32 (lock + 1));
4599 if (call_addr != ent_crit)
4600 return NULL;
4601 /* In contrast to the above Windows 8 code, we don't have to search
4602 for the `mov rel(%rip),%rbx' instruction. It follows right after
4603 the call to RtlEnterCriticalSection. */
4604 movrbx = lock + 5;
4606 if (!movrbx)
4607 return NULL;
4608 /* Check that the next instruction tests if the fetched value is NULL. */
4609 const uint8_t *testrbx = (const uint8_t *)
4610 memmem (movrbx + 7, 3, "\x48\x85\xdb", 3);
4611 if (!testrbx)
4612 return NULL;
4613 /* Compute address of the fcwd_access_t ** pointer. */
4614 return (fcwd_access_t **) (testrbx + peek32 (movrbx + 3));
4617 static fcwd_access_t **
4618 find_fast_cwd ()
4620 USHORT emulated, hosted;
4621 fcwd_access_t **f_cwd_ptr;
4623 /* First check if we're running in WOW64 on ARM64 emulating AMD64. Skip
4624 fetching FAST_CWD pointer as long as there's no solution for finding
4625 it on that system. */
4626 if (IsWow64Process2 (GetCurrentProcess (), &emulated, &hosted)
4627 && hosted == IMAGE_FILE_MACHINE_ARM64)
4628 return NULL;
4630 /* Fetch the pointer but don't set the global fast_cwd_ptr yet. First
4631 we have to make sure we know the version of the FAST_CWD structure
4632 used on the system. */
4633 f_cwd_ptr = find_fast_cwd_pointer ();
4634 if (!f_cwd_ptr)
4635 small_printf ("Cygwin WARNING:\n"
4636 " Couldn't compute FAST_CWD pointer. This typically occurs if you're using\n"
4637 " an older Cygwin version on a newer Windows. Please update to the latest\n"
4638 " available Cygwin version from https://cygwin.com/. If the problem persists,\n"
4639 " please see https://cygwin.com/problems.html\n\n");
4641 /* Eventually, after we set the version as well, set fast_cwd_ptr. */
4642 return f_cwd_ptr;
4645 void
4646 cwdstuff::override_win32_cwd (bool init, ULONG old_dismount_count)
4648 HANDLE h = NULL;
4650 PEB &peb = *NtCurrentTeb ()->Peb;
4651 UNICODE_STRING &upp_cwd_str = peb.ProcessParameters->CurrentDirectoryName;
4652 HANDLE &upp_cwd_hdl = peb.ProcessParameters->CurrentDirectoryHandle;
4653 PUNICODE_STRING win32_cwd_ptr = error ? &ro_u_pipedir : &win32;
4655 if (fast_cwd_ptr == (fcwd_access_t **) -1)
4656 fast_cwd_ptr = find_fast_cwd ();
4657 if (fast_cwd_ptr)
4659 /* If we got a valid value for fast_cwd_ptr, we can simply replace
4660 the RtlSetCurrentDirectory_U function entirely. */
4661 PVOID heap = peb.ProcessHeap;
4662 /* First allocate a new fcwd_access_t structure on the heap. */
4663 fcwd_access_t *f_cwd = (fcwd_access_t *)
4664 RtlAllocateHeap (heap, 0,
4665 sizeof (fcwd_access_t)
4666 + win32_cwd_ptr->MaximumLength);
4667 if (!f_cwd)
4669 debug_printf ("RtlAllocateHeap failed");
4670 return;
4672 /* Fill in the values. */
4673 f_cwd->FillIn (dir, win32_cwd_ptr, old_dismount_count);
4674 /* Use PEB lock when switching fast_cwd_ptr to the new FAST_CWD
4675 structure and writing the CWD to the user process parameter
4676 block. This is equivalent to calling RtlAcquirePebLock/
4677 RtlReleasePebLock, but without having to go through the FS
4678 selector again. */
4679 RtlEnterCriticalSection (peb.FastPebLock);
4680 fcwd_access_t *old_cwd = *fast_cwd_ptr;
4681 *fast_cwd_ptr = f_cwd;
4682 f_cwd->CopyPath (upp_cwd_str);
4683 upp_cwd_hdl = dir;
4684 RtlLeaveCriticalSection (peb.FastPebLock);
4685 if (old_cwd)
4686 old_cwd->Free (heap);
4688 else
4690 /* Fallback if we failed to find the fast_cwd_ptr value:
4692 - Call RtlSetCurrentDirectory_U.
4693 - Compute new FAST_CWD struct address from buffer pointer in the
4694 user process parameter block.
4695 - Replace the directory handle in the struct with our own handle.
4696 - Close the original handle. RtlSetCurrentDirectory_U already
4697 closed our former dir handle -> no handle leak.
4699 Guard the entire operation with FastPebLock to avoid races
4700 accessing the PEB and FAST_CWD struct.
4702 Unfortunately this method is still prone to a directory usage
4703 race condition:
4705 - The directory is locked against deletion or renaming between the
4706 RtlSetCurrentDirectory_U and the subsequent NtClose call. */
4707 if (unlikely (upp_cwd_hdl == NULL) && init)
4708 return;
4709 RtlEnterCriticalSection (peb.FastPebLock);
4710 if (!init)
4712 NTSTATUS status =
4713 RtlSetCurrentDirectory_U (win32_cwd_ptr);
4714 if (!NT_SUCCESS (status))
4716 RtlLeaveCriticalSection (peb.FastPebLock);
4717 debug_printf ("RtlSetCurrentDirectory_U(%S) failed, %y",
4718 win32_cwd_ptr);
4719 return;
4722 fcwd_access_t::SetDirHandleFromBufferPointer(upp_cwd_str.Buffer, dir);
4723 h = upp_cwd_hdl;
4724 upp_cwd_hdl = dir;
4725 RtlLeaveCriticalSection (peb.FastPebLock);
4726 /* The handle on init is always a fresh one, not the handle inherited
4727 from the parent process. We always have to close it here. */
4728 NtClose (h);
4732 /* Initialize cwdstuff */
4733 void
4734 cwdstuff::init ()
4736 /* Cygwin processes inherit the cwd from their parent. If the win32 path
4737 buffer is not NULL, the cwd struct is already set up, and we only
4738 have to override the Win32 CWD with ours. */
4739 if (win32.Buffer)
4740 override_win32_cwd (true, SharedUserData.DismountCount);
4741 else
4743 /* Initialize fast_cwd stuff. */
4744 fast_cwd_ptr = (fcwd_access_t **) -1;
4745 /* Initially re-open the cwd to allow POSIX semantics. */
4746 set (NULL, NULL);
4750 /* Chdir and fill out the elements of a cwdstuff struct. */
4752 cwdstuff::set (path_conv *nat_cwd, const char *posix_cwd)
4754 NTSTATUS status;
4755 UNICODE_STRING upath;
4756 OBJECT_ATTRIBUTES attr;
4757 PEB &peb = *NtCurrentTeb ()->Peb;
4758 bool virtual_path = false;
4759 bool unc_path = false;
4760 bool inaccessible_path = false;
4762 /* Here are the problems with using SetCurrentDirectory. Just skip this
4763 comment if you don't like whining.
4765 - SetCurrentDirectory only supports paths of up to MAX_PATH - 1 chars,
4766 including a trailing backslash. That's an absolute restriction, even
4767 in the UNICODE API.
4769 - SetCurrentDirectory fails for directories with strict permissions even
4770 for processes with the SE_BACKUP_NAME privilege enabled. The reason
4771 is apparently that SetCurrentDirectory calls NtOpenFile without the
4772 FILE_OPEN_FOR_BACKUP_INTENT flag set.
4774 - SetCurrentDirectory does not support case-sensitivity.
4776 - Unlinking a cwd fails because SetCurrentDirectory seems to open
4777 directories so that deleting the directory is disallowed.
4779 - SetCurrentDirectory can naturally not work on virtual Cygwin paths
4780 like /proc or /cygdrive.
4782 Nevertheless, doing entirely without SetCurrentDirectory is not really
4783 feasible, because it breaks too many mixed applications using the Win32
4784 API.
4786 Therefore we handle the CWD all by ourselves and just keep the Win32
4787 CWD in sync. However, to avoid surprising behaviour in the Win32 API
4788 when we are in a CWD which is inaccessible as Win32 CWD, we set the
4789 Win32 CWD to a "weird" directory in which all relative filesystem-related
4790 calls fail. */
4792 if (nat_cwd)
4794 upath = *nat_cwd->get_nt_native_path ();
4795 if (nat_cwd->isspecial ())
4797 virtual_path = true;
4798 /* But allow starting of native apps from /dev if /dev actually
4799 exists on disk. */
4800 if (isdev_dev (nat_cwd->dev))
4802 FILE_BASIC_INFORMATION fbi;
4804 InitializeObjectAttributes (&attr, &upath,
4805 OBJ_CASE_INSENSITIVE | OBJ_INHERIT,
4806 NULL, NULL);
4807 status = NtQueryAttributesFile (&attr, &fbi);
4808 if (status != STATUS_OBJECT_NAME_NOT_FOUND
4809 && status != STATUS_OBJECT_PATH_NOT_FOUND)
4810 virtual_path = false;
4815 acquire_write ();
4817 /* Memorize old DismountCount before opening the dir. This value is
4818 stored in the FAST_CWD structure. It would be simpler to fetch the
4819 old DismountCount in override_win32_cwd, but Windows also fetches
4820 it before opening the directory handle. It's not quite clear if
4821 that's really required, but since we don't know the side effects of
4822 this action, we better follow Windows' lead. */
4823 ULONG old_dismount_count = SharedUserData.DismountCount;
4824 /* Open a directory handle with FILE_OPEN_FOR_BACKUP_INTENT and with all
4825 sharing flags set. The handle is right now used in exceptions.cc only,
4826 but that might change in future. */
4827 HANDLE h = NULL;
4828 if (!virtual_path)
4830 IO_STATUS_BLOCK io;
4832 if (!nat_cwd)
4834 /* On init, just reopen Win32 CWD with desired access flags.
4835 We can access the PEB without lock, because no other thread
4836 can change the CWD. However, there's a chance that the handle
4837 is NULL, even though CurrentDirectoryName isn't so we have to
4838 be careful. */
4839 if (!peb.ProcessParameters->CurrentDirectoryHandle)
4841 InitializeObjectAttributes (&attr,
4842 &peb.ProcessParameters->CurrentDirectoryName,
4843 OBJ_CASE_INSENSITIVE | OBJ_INHERIT, NULL, NULL);
4845 else
4847 RtlInitUnicodeString (&upath, L"");
4848 InitializeObjectAttributes (&attr,
4849 &upath, OBJ_CASE_INSENSITIVE | OBJ_INHERIT,
4850 peb.ProcessParameters->CurrentDirectoryHandle,
4851 NULL);
4854 else
4855 InitializeObjectAttributes (&attr, &upath,
4856 nat_cwd->objcaseinsensitive () | OBJ_INHERIT,
4857 NULL, NULL);
4858 /* First try without FILE_OPEN_FOR_BACKUP_INTENT, to find out if the
4859 directory is valid for Win32 apps. And, no, we can't just call
4860 SetCurrentDirectory here, since that would potentially break
4861 case-sensitivity. */
4862 status = NtOpenFile (&h, SYNCHRONIZE | FILE_TRAVERSE, &attr, &io,
4863 FILE_SHARE_VALID_FLAGS,
4864 FILE_DIRECTORY_FILE
4865 | FILE_SYNCHRONOUS_IO_NONALERT);
4866 if (status == STATUS_ACCESS_DENIED)
4868 status = NtOpenFile (&h, SYNCHRONIZE | FILE_TRAVERSE, &attr, &io,
4869 FILE_SHARE_VALID_FLAGS,
4870 FILE_DIRECTORY_FILE
4871 | FILE_SYNCHRONOUS_IO_NONALERT
4872 | FILE_OPEN_FOR_BACKUP_INTENT);
4873 inaccessible_path = true;
4875 if (!NT_SUCCESS (status))
4877 /* Called from chdir? Just fail. */
4878 if (nat_cwd)
4880 release_write ();
4881 __seterrno_from_nt_status (status);
4882 return -1;
4884 /* Otherwise we're in init and posix hasn't been set yet. Try to
4885 duplicate the handle instead. If that fails, too, set dir to NULL
4886 and carry on. This will at least set posix to some valid path at
4887 process startup, and subsequent getcwd calls don't EFAULT. */
4888 debug_printf ("WARNING: Can't reopen CWD %y '%S', status %y",
4889 peb.ProcessParameters->CurrentDirectoryHandle,
4890 &peb.ProcessParameters->CurrentDirectoryName,
4891 status);
4892 if (!peb.ProcessParameters->CurrentDirectoryHandle
4893 || !DuplicateHandle (GetCurrentProcess (),
4894 peb.ProcessParameters->CurrentDirectoryHandle,
4895 GetCurrentProcess (), &h, 0, TRUE, 0))
4897 if (peb.ProcessParameters->CurrentDirectoryHandle)
4898 debug_printf ("...and DuplicateHandle failed with %E.");
4899 h = NULL;
4903 /* Set new handle. Note that we simply overwrite the old handle here
4904 without closing it. The handle is also used as Win32 CWD handle in
4905 the user parameter block, and it will be closed in override_win32_cwd,
4906 if required. */
4907 dir = h;
4909 if (!nat_cwd)
4911 /* On init, just fetch the Win32 dir from the PEB. We can access
4912 the PEB without lock, because no other thread can change the CWD
4913 at that time. */
4914 PUNICODE_STRING pdir = &peb.ProcessParameters->CurrentDirectoryName;
4915 RtlInitEmptyUnicodeString (&win32,
4916 (PWCHAR) crealloc_abort (win32.Buffer,
4917 pdir->Length
4918 + sizeof (WCHAR)),
4919 pdir->Length + sizeof (WCHAR));
4920 RtlCopyUnicodeString (&win32, pdir);
4922 PWSTR eoBuffer = win32.Buffer + (win32.Length / sizeof (WCHAR));
4923 /* Remove trailing slash if one exists. */
4924 if ((eoBuffer - win32.Buffer) > 3 && eoBuffer[-1] == L'\\')
4925 win32.Length -= sizeof (WCHAR);
4926 if (eoBuffer[0] == L'\\')
4927 unc_path = true;
4929 posix_cwd = NULL;
4931 else
4933 if (!virtual_path) /* don't mangle virtual path. */
4935 /* Convert into Win32 path and compute length. */
4936 if (upath.Buffer[1] == L'?')
4938 upath.Buffer += 4;
4939 upath.Length -= 4 * sizeof (WCHAR);
4940 if (upath.Buffer[1] != L':')
4942 /* UNC path */
4943 upath.Buffer += 2;
4944 upath.Length -= 2 * sizeof (WCHAR);
4945 unc_path = true;
4948 else
4950 /* Path via native NT namespace. Prepend GLOBALROOT prefix
4951 to create a valid Win32 path. */
4952 PWCHAR buf = (PWCHAR) alloca (upath.Length
4953 + ro_u_globalroot.Length
4954 + sizeof (WCHAR));
4955 wcpcpy (wcpcpy (buf, ro_u_globalroot.Buffer), upath.Buffer);
4956 upath.Buffer = buf;
4957 upath.Length += ro_u_globalroot.Length;
4959 PWSTR eoBuffer = upath.Buffer + (upath.Length / sizeof (WCHAR));
4960 /* Remove trailing slash if one exists. */
4961 if ((eoBuffer - upath.Buffer) > 3 && eoBuffer[-1] == L'\\')
4962 upath.Length -= sizeof (WCHAR);
4964 RtlInitEmptyUnicodeString (&win32,
4965 (PWCHAR) crealloc_abort (win32.Buffer,
4966 upath.Length
4967 + sizeof (WCHAR)),
4968 upath.Length + sizeof (WCHAR));
4969 RtlCopyUnicodeString (&win32, &upath);
4970 if (unc_path)
4971 win32.Buffer[0] = L'\\';
4973 /* Make sure it's NUL-terminated. */
4974 win32.Buffer[win32.Length / sizeof (WCHAR)] = L'\0';
4976 /* Set drive_length, used in path conversion, and error code, used in
4977 spawn_guts to decide whether a native Win32 app can be started or not. */
4978 if (virtual_path)
4980 drive_length = 0;
4981 error = ENOTDIR;
4983 else
4985 if (!unc_path)
4986 drive_length = 2;
4987 else
4989 PWCHAR ptr = wcschr (win32.Buffer + 2, L'\\');
4990 if (ptr)
4991 ptr = wcschr (ptr + 1, L'\\');
4992 if (ptr)
4993 drive_length = ptr - win32.Buffer;
4994 else
4995 drive_length = win32.Length / sizeof (WCHAR);
4997 if (inaccessible_path)
4998 error = EACCES;
4999 else if (win32.Length > (MAX_PATH - 2) * sizeof (WCHAR))
5000 error = ENAMETOOLONG;
5001 else
5002 error = 0;
5004 /* Keep the Win32 CWD in sync. Don't check for error, other than for
5005 strace output. Try to keep overhead low. */
5006 override_win32_cwd (!nat_cwd, old_dismount_count);
5008 /* Eventually, create POSIX path if it's not set on entry. */
5009 tmp_pathbuf tp;
5010 if (!posix_cwd)
5012 posix_cwd = (const char *) tp.c_get ();
5013 mount_table->conv_to_posix_path (win32.Buffer, (char *) posix_cwd, 0);
5015 posix = (char *) crealloc_abort (posix, strlen (posix_cwd) + 1);
5016 stpcpy (posix, posix_cwd);
5018 release_write ();
5019 return 0;
5022 const char *
5023 cwdstuff::get_error_desc () const
5025 switch (cygheap->cwd.get_error ())
5027 case EACCES:
5028 return "has restricted permissions which render it\n"
5029 "inaccessible as Win32 working directory";
5030 case ENOTDIR:
5031 return "is a virtual Cygwin directory which does\n"
5032 "not exist for a native Windows application";
5033 case ENAMETOOLONG:
5034 return "has a path longer than allowed for a\n"
5035 "Win32 working directory";
5036 default:
5037 break;
5039 /* That shouldn't occur, unless we defined a new error code
5040 in cwdstuff::set. */
5041 return "is not accessible for some unknown reason";
5044 /* Store incoming wchar_t path as current posix cwd. This is called from
5045 setlocale so that the cwd is always stored in the right charset. */
5046 void
5047 cwdstuff::reset_posix (wchar_t *w_cwd)
5049 size_t len = sys_wcstombs (NULL, (size_t) -1, w_cwd);
5050 posix = (char *) crealloc_abort (posix, len + 1);
5051 sys_wcstombs (posix, len + 1, w_cwd);
5054 char *
5055 cwdstuff::get (char *buf, int need_posix, int with_chroot, unsigned ulen)
5057 tmp_pathbuf tp;
5059 errno = 0;
5060 if (ulen)
5061 /* nothing */;
5062 else if (buf == NULL)
5063 ulen = (unsigned) -1;
5064 else
5066 set_errno (EINVAL);
5067 goto out;
5070 acquire_read ();
5072 char *tocopy;
5073 if (!need_posix)
5075 tocopy = tp.c_get ();
5076 sys_wcstombs (tocopy, NT_MAX_PATH, win32.Buffer,
5077 win32.Length / sizeof (WCHAR));
5079 else
5080 tocopy = posix;
5082 debug_printf ("posix %s", posix);
5083 if (strlen (tocopy) >= ulen)
5085 set_errno (ERANGE);
5086 buf = NULL;
5088 else
5090 if (!buf)
5091 buf = (char *) malloc (strlen (tocopy) + 1);
5092 strcpy (buf, tocopy);
5093 if (!buf[0]) /* Should only happen when chroot */
5094 strcpy (buf, "/");
5097 release_read ();
5099 out:
5100 syscall_printf ("(%s) = cwdstuff::get (%p, %u, %d, %d), errno %d",
5101 buf, buf, ulen, need_posix, with_chroot, errno);
5102 return buf;
5105 /* No need to be reentrant or thread-safe according to SUSv3.
5106 / and \\ are treated equally. Leading drive specifiers are
5107 kept intact as far as it makes sense. Everything else is
5108 POSIX compatible. */
5109 extern "C" char *
5110 basename (char *path)
5112 static char buf[4];
5113 char *c, *d, *bs = path;
5115 if (!path || !*path)
5116 return strcpy (buf, ".");
5117 if (isalpha (path[0]) && path[1] == ':')
5118 bs += 2;
5119 else if (strspn (path, "/\\") > 1)
5120 ++bs;
5121 c = strrchr (bs, '/');
5122 if ((d = strrchr (c ?: bs, '\\')) > c)
5123 c = d;
5124 if (c)
5126 /* Trailing (back)slashes are eliminated. */
5127 while (c && c > bs && c[1] == '\0')
5129 *c = '\0';
5130 c = strrchr (bs, '/');
5131 if ((d = strrchr (c ?: bs, '\\')) > c)
5132 c = d;
5134 if (c && (c > bs || c[1]))
5135 return c + 1;
5137 else if (!bs[0])
5139 stpncpy (buf, path, bs - path);
5140 stpcpy (buf + (bs - path), ".");
5141 return buf;
5143 return path;
5146 /* The differences with the POSIX version above:
5147 - declared in <string.h> (instead of <libgen.h>);
5148 - the argument is never modified, and therefore is marked const;
5149 - the empty string is returned if path is an empty string, "/", or ends
5150 with a trailing slash. */
5151 extern "C" char *
5152 __gnu_basename (const char *path)
5154 static char buf[1];
5155 char *c, *d, *bs = (char *)path;
5157 if (!path || !*path)
5158 return strcpy (buf, "");
5159 if (isalpha (path[0]) && path[1] == ':')
5160 bs += 2;
5161 else if (strspn (path, "/\\") > 1)
5162 ++bs;
5163 c = strrchr (bs, '/');
5164 if ((d = strrchr (c ?: bs, '\\')) > c)
5165 c = d;
5166 if (c)
5167 return c + 1;
5168 else if (!bs[0])
5169 return strcpy (buf, "");
5170 return (char *)path;
5173 /* No need to be reentrant or thread-safe according to SUSv3.
5174 / and \\ are treated equally. Leading drive specifiers and
5175 leading double (back)slashes are kept intact as far as it
5176 makes sense. Everything else is POSIX compatible. */
5177 extern "C" char *
5178 dirname (char *path)
5180 static char buf[4];
5181 char *c, *d, *bs = path;
5183 if (!path || !*path)
5184 return strcpy (buf, ".");
5185 if (isalpha (path[0]) && path[1] == ':')
5186 bs += 2;
5187 else if (strspn (path, "/\\") == 2)
5188 ++bs;
5189 c = strrchr (bs, '/');
5190 if ((d = strrchr (c ?: bs, '\\')) > c)
5191 c = d;
5192 if (c)
5194 /* Trailing (back)slashes are eliminated. */
5195 while (c && c > bs && c[1] == '\0')
5197 *c = '\0';
5198 c = strrchr (bs, '/');
5199 if ((d = strrchr (c ?: bs, '\\')) > c)
5200 c = d;
5202 if (!c)
5203 strcpy (bs, ".");
5204 else if (c > bs)
5206 /* More trailing (back)slashes are eliminated. */
5207 while (c > bs && (*c == '/' || *c == '\\'))
5208 *c-- = '\0';
5210 else
5211 c[1] = '\0';
5213 else
5215 stpncpy (buf, path, bs - path);
5216 stpcpy (buf + (bs - path), ".");
5217 return buf;
5219 return path;