Cygwin: strptime: add release note
[newlib-cygwin.git] / winsup / cygwin / path.cc
blob68639f323972b857cda87f0966cb93ec3565553c
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_FOR_BACKUP_INTENT
612 | FILE_DIRECTORY_FILE);
613 if (NT_SUCCESS (status))
615 status = NtQueryDirectoryFile (dir, NULL, NULL, 0, &io,
616 &fdi, sizeof fdi,
617 FileBothDirectoryInformation,
618 TRUE, &basename, TRUE);
619 NtClose (dir);
620 if (NT_SUCCESS (status) || status == STATUS_BUFFER_OVERFLOW)
621 return fdi.FileAttributes;
624 SetLastError (RtlNtStatusToDosError (status));
625 return INVALID_FILE_ATTRIBUTES;
628 /* Convert an arbitrary path SRC to a pure Win32 path, suitable for
629 passing to Win32 API routines.
631 If an error occurs, `error' is set to the errno value.
632 Otherwise it is set to 0.
634 follow_mode values:
635 SYMLINK_FOLLOW - convert to PATH symlink points to
636 SYMLINK_NOFOLLOW - convert to PATH of symlink itself
637 SYMLINK_IGNORE - do not check PATH for symlinks
638 SYMLINK_CONTENTS - just return symlink contents
641 /* TODO: This implementation is only preliminary. For internal
642 purposes it's necessary to have a path_conv::check function which
643 takes a UNICODE_STRING src path, otherwise we waste a lot of time
644 for converting back and forth. The below implementation does
645 realy nothing but converting to char *, until path_conv handles
646 wide-char paths directly. */
647 void
648 path_conv::check (const UNICODE_STRING *src, unsigned opt,
649 const suffix_info *suffixes)
651 tmp_pathbuf tp;
652 char *path = tp.c_get ();
654 user_shared->warned_msdos = true;
655 sys_wcstombs (path, NT_MAX_PATH, src->Buffer, src->Length / sizeof (WCHAR));
656 path_conv::check (path, opt, suffixes);
659 void
660 path_conv::check (const char *src, unsigned opt,
661 const suffix_info *suffixes)
663 /* The tmp_buf array is used when expanding symlinks. It is NT_MAX_PATH * 2
664 in length so that we can hold the expanded symlink plus a trailer. */
665 tmp_pathbuf tp;
666 char *path_copy = tp.c_get ();
667 char *pathbuf = tp.c_get ();
668 char *tmp_buf = tp.t_get ();
669 char *THIS_path = tp.c_get ();
670 symlink_info sym;
671 bool need_directory = 0;
672 bool add_ext = false;
673 bool is_relpath;
674 char *tail, *path_end;
676 #if 0
677 static path_conv last_path_conv;
678 static char last_src[CYG_MAX_PATH];
680 if (*last_src && strcmp (last_src, src) == 0)
682 *this = last_path_conv;
683 return;
685 #endif
687 __try
689 int loop = 0;
690 mount_flags = 0;
691 path_flags = 0;
692 suffix = NULL;
693 fileattr = INVALID_FILE_ATTRIBUTES;
694 caseinsensitive = OBJ_CASE_INSENSITIVE;
695 if (wide_path)
696 cfree (wide_path);
697 wide_path = NULL;
698 if (path)
700 cfree (modifiable_path ());
701 path = NULL;
703 close_conv_handle ();
704 fs.clear ();
705 if (posix_path)
707 cfree ((void *) posix_path);
708 posix_path = NULL;
710 int component = 0; // Number of translated components
712 if (!(opt & PC_NULLEMPTY))
713 error = 0;
714 else if (!*src)
716 error = ENOENT;
717 return;
720 bool is_msdos = false;
721 /* This loop handles symlink expansion. */
722 for (;;)
724 is_relpath = !isabspath (src);
725 error = normalize_posix_path (src, path_copy, tail);
726 if (error > 0)
727 return;
728 if (error < 0)
730 if (component == 0)
731 is_msdos = true;
732 error = 0;
735 /* Detect if the user was looking for a directory. We have to strip
736 the trailing slash initially while trying to add extensions but
737 take it into account during processing */
738 if (tail > path_copy + 2 && isslash (tail[-1]))
740 need_directory = 1;
741 *--tail = '\0';
743 path_end = tail;
745 /* Scan path_copy from right to left looking either for a symlink
746 or an actual existing file. If an existing file is found, just
747 return. If a symlink is found, exit the for loop.
748 Also: be careful to preserve the errno returned from
749 symlink.check as the caller may need it. */
750 /* FIXME: Do we have to worry about multiple \'s here? */
751 component = 0; // Number of translated components
752 sym.clear_content ();
753 sym.path_flags (0);
755 int symlen = 0;
757 /* Make sure to check certain flags on last component only. */
758 for (unsigned pc_flags = opt & (PC_NO_ACCESS_CHECK | PC_KEEP_HANDLE
759 | PC_SYM_FOLLOW | PC_SYM_NOFOLLOW_REP);
761 pc_flags = opt & (PC_SYM_FOLLOW | PC_SYM_NOFOLLOW_REP))
763 const suffix_info *suff;
764 char *full_path;
766 /* Don't allow symlink.check to set anything in the path_conv
767 class if we're working on an inner component of the path */
768 if (component)
770 suff = NULL;
771 full_path = pathbuf;
773 else
775 suff = suffixes;
776 full_path = THIS_path;
779 retry_fs_via_processfd:
781 /* Convert to native path spec sans symbolic link info. */
782 unsigned mnt_flags;
783 error = mount_table->conv_to_win32_path (path_copy, full_path,
784 dev,
785 &mnt_flags);
787 if (error)
788 return;
790 sym.pc_flags (pc_flags);
791 sym.mount_flags (mnt_flags);
793 if (!dev.exists ())
795 error = ENXIO;
796 return;
799 if (iscygdrive_dev (dev))
801 if (!component)
802 fileattr = FILE_ATTRIBUTE_DIRECTORY
803 | FILE_ATTRIBUTE_READONLY;
804 else
806 fileattr = getfileattr (THIS_path,
807 sym.mount_flags () & MOUNT_NOPOSIX);
808 dev = FH_FS;
810 goto out;
812 else if (isdev_dev (dev))
814 /* Make sure that the path handling goes on as with FH_FS. */
816 else if (isvirtual_dev (dev))
818 /* FIXME: Calling build_fhandler here is not the right way to
819 handle this. */
820 fhandler_virtual *fh = (fhandler_virtual *)
821 build_fh_dev (dev, path_copy);
822 virtual_ftype_t file_type;
823 if (!fh)
824 file_type = virt_none;
825 else
827 file_type = fh->exists ();
828 if (file_type == virt_symlink
829 || file_type == virt_fdsymlink)
831 fh->fill_filebuf ();
832 symlen = sym.set (fh->get_filebuf ());
834 else if (file_type == virt_fsdir && dev == FH_PROCESSFD)
836 /* FIXME: This is YA bad hack to workaround that
837 we're checking for isvirtual_dev at this point.
838 This should only happen if the file is actually
839 a virtual file, and NOT already if the preceeding
840 path components constitute a virtual file.
842 Anyway, what we do here is this: If the descriptor
843 symlink points to a dir, and if there are trailing
844 path components, it's actually pointing somewhere
845 else. The format_process_fd function returns the
846 full path, resolved symlink plus trailing path
847 components, in its filebuf. This is a POSIX path
848 we know nothing about, so we have to convert it to
849 native again, calling conv_to_win32_path. Since
850 basically nothing happened yet, just copy it over
851 into full_path and jump back to the
852 conv_to_win32_path call. What a mess. */
853 stpcpy (path_copy, fh->get_filebuf ());
854 delete fh;
855 goto retry_fs_via_processfd;
857 else if (file_type == virt_none && dev == FH_PROCESSFD)
859 error = get_errno ();
860 if (error)
862 delete fh;
863 return;
866 delete fh;
868 switch (file_type)
870 case virt_directory:
871 case virt_rootdir:
872 if (component == 0)
873 fileattr = FILE_ATTRIBUTE_DIRECTORY;
874 break;
875 case virt_file:
876 if (component == 0)
877 fileattr = 0;
878 break;
879 case virt_fdsymlink:
880 /* Allow open/linkat to do the right thing. */
881 if (opt & PC_SYM_NOFOLLOW_PROCFD)
883 opt &= ~PC_SYM_FOLLOW;
884 sym.path_flags (sym.path_flags ()
885 | PATH_RESOLVE_PROCFD);
887 fallthrough;
888 case virt_symlink:
889 goto is_virtual_symlink;
890 case virt_pipe:
891 if (component == 0)
893 fileattr = 0;
894 dev.parse (FH_PIPE);
896 break;
897 case virt_socket:
898 if (component == 0)
900 fileattr = 0;
901 dev.parse (FH_SOCKET);
903 break;
904 case virt_fsdir:
905 case virt_fsfile:
906 /* Access to real file or directory via block device
907 entry in /proc/sys. Convert to real file and go with
908 the flow. */
909 dev.parse (FH_FS);
910 goto is_fs_via_procsys;
911 case virt_blk:
912 /* Block special device. Convert to a /dev/sd* like
913 block device unless the trailing slash has been
914 requested. In this case, the target is the root
915 directory of the filesystem on this block device.
916 So we convert this to a real file and attach the
917 backslash. */
918 if (component == 0)
920 fileattr = FILE_ATTRIBUTE_DEVICE;
921 if (!need_directory)
922 /* Use a /dev/sd* device number > /dev/sddx.
923 FIXME: Define a new major DEV_ice number. */
924 dev.parse (DEV_SD_HIGHPART_END, 9999);
925 else
927 dev.parse (FH_FS);
928 strcat (full_path, "\\");
929 fileattr |= FILE_ATTRIBUTE_DIRECTORY;
931 goto out;
933 break;
934 case virt_chr:
935 if (component == 0)
936 fileattr = FILE_ATTRIBUTE_DEVICE;
937 break;
938 default:
939 if (component == 0)
940 fileattr = INVALID_FILE_ATTRIBUTES;
941 goto virtual_component_retry;
943 if (component == 0 || dev != FH_NETDRIVE)
944 mount_flags |= MOUNT_RO;
945 goto out;
947 /* devn should not be a device. If it is, then stop parsing. */
948 else if (dev != FH_FS)
950 fileattr = 0;
951 mount_flags = sym.mount_flags ();
952 if (component)
954 error = ENOTDIR;
955 return;
957 goto out; /* Found a device. Stop parsing. */
960 /* If path is only a drivename, Windows interprets it as the
961 current working directory on this drive instead of the root
962 dir which is what we want. So we need the trailing backslash
963 in this case. */
964 if (full_path[0] && full_path[1] == ':' && full_path[2] == '\0')
966 full_path[2] = '\\';
967 full_path[3] = '\0';
970 /* If the incoming path was given in DOS notation, always treat
971 it as caseinsensitive,noacl path. This must be set before
972 calling sym.check, otherwise the path is potentially treated
973 casesensitive. */
974 if (is_msdos)
975 sym.mount_flags (sym.mount_flags ()
976 | MOUNT_NOPOSIX | MOUNT_NOACL);
978 is_fs_via_procsys:
980 symlen = sym.check (full_path, suff, fs, conv_handle);
982 is_virtual_symlink:
984 if (sym.isdevice ())
986 if (component)
988 error = ENOTDIR;
989 return;
991 dev.parse (sym.dev ());
992 dev.setfs (1);
993 dev.mode (sym.mode ());
994 fileattr = sym.fileattr ();
995 goto out;
998 if (sym.path_flags () & PATH_SOCKET)
1000 if (component)
1002 error = ENOTDIR;
1003 return;
1005 fileattr = sym.fileattr ();
1006 #ifdef __WITH_AF_UNIX
1007 dev.parse ((sym.path_flags () & PATH_REP) ? FH_UNIX : FH_LOCAL);
1008 #else
1009 dev.parse (FH_LOCAL);
1010 #endif /* __WITH_AF_UNIX */
1011 dev.setfs (1);
1012 mount_flags = sym.mount_flags ();
1013 path_flags = sym.path_flags ();
1014 goto out;
1017 if (!component)
1019 /* Make sure that /dev always exists. */
1020 fileattr = isdev_dev (dev) ? FILE_ATTRIBUTE_DIRECTORY
1021 : sym.fileattr ();
1022 mount_flags = sym.mount_flags ();
1023 path_flags = sym.path_flags ();
1025 else if (isdev_dev (dev))
1027 /* If we're looking for a non-existing file below /dev,
1028 make sure that the device type is converted to FH_FS, so
1029 that subsequent code handles the file correctly. Unless
1030 /dev itself doesn't exist on disk. In that case /dev
1031 is handled as virtual filesystem, and virtual filesystems
1032 are read-only. The PC_KEEP_HANDLE check allows to check
1033 for a call from an informational system call. In that
1034 case we just stick to ENOENT, and the device type doesn't
1035 matter anyway. */
1036 if (sym.error () == ENOENT && !(opt & PC_KEEP_HANDLE))
1037 sym.error (EROFS);
1038 else
1039 dev = FH_FS;
1042 /* If symlink.check found an existing non-symlink file, then
1043 it sets the appropriate flag. It also sets any suffix found
1044 into `ext_here'. */
1045 if (!sym.issymlink ()
1046 && sym.fileattr () != INVALID_FILE_ATTRIBUTES)
1048 error = sym.error ();
1049 if (component == 0)
1050 add_ext = true;
1051 else if (!(sym.fileattr () & FILE_ATTRIBUTE_DIRECTORY))
1053 error = ENOTDIR;
1054 goto out;
1056 goto out; // file found
1058 /* Found a symlink if symlen > 0 or short-circuited a native
1059 symlink or junction point if symlen < 0.
1060 If symlen > 0 and component == 0, then the src path itself
1061 was a symlink. If !follow_mode then we're done. Otherwise
1062 we have to insert the path found into the full path that we
1063 are building and perform all of these operations again on the
1064 newly derived path. */
1065 else if (symlen)
1067 /* if symlen is negativ, the actual native symlink or
1068 junction point is an inner path component. Just fix up
1069 symlen to be positive and don't try any PC_SYM_FOLLOW
1070 handling. */
1071 if (symlen < 0)
1072 symlen = -symlen;
1073 else if (component == 0
1074 && (!(opt & PC_SYM_FOLLOW)
1075 || (is_winapi_reparse_point ()
1076 && (opt & PC_SYM_NOFOLLOW_REP))))
1078 /* Usually a trailing slash requires to follow a symlink,
1079 even with PC_SYM_NOFOLLOW. The reason is that "foo/"
1080 is equivalent to "foo/." so the symlink is in fact not
1081 the last path component.
1083 PC_SYM_NOFOLLOW_DIR is used to indicate that the
1084 last path component is the target symlink and the
1085 trailing slash is supposed to be ignored. */
1086 if (!need_directory || (opt & PC_SYM_NOFOLLOW_DIR))
1088 /* last component of path is a symlink. */
1089 set_symlink (symlen);
1090 /* make sure not to set errno to ENOTDIR. */
1091 need_directory = 0;
1092 if (opt & PC_SYM_CONTENTS)
1094 strcpy (THIS_path, sym.content ());
1095 goto out;
1097 add_ext = true;
1098 goto out;
1101 /* Following a symlink we can't trust the collected
1102 filesystem information any longer. */
1103 fs.clear ();
1104 /* Close handle, if we have any. Otherwise we're collecting
1105 handles while following symlinks. */
1106 conv_handle.close ();
1107 break;
1109 else if (sym.error () && sym.error () != ENOENT)
1111 error = sym.error ();
1112 goto out;
1114 /* No existing file found. */
1116 virtual_component_retry:
1117 /* Find the new "tail" of the path, e.g. in '/for/bar/baz',
1118 /baz is the tail. */
1119 if (tail != path_end)
1120 *tail = '/';
1121 while (--tail > path_copy + 1 && *tail != '/') {}
1122 /* Exit loop if there is no tail or we are at the
1123 beginning of a UNC path */
1124 if (tail <= path_copy + 1)
1125 goto out; // all done
1127 /* Haven't found an existing pathname component yet.
1128 Pinch off the tail and try again. */
1129 *tail = '\0';
1130 component++;
1133 /* Arrive here if above loop detected a symlink. */
1134 if (++loop > SYMLOOP_MAX)
1136 error = ELOOP; // Eep.
1137 return;
1140 /* Place the link content, possibly with head and/or tail,
1141 in tmp_buf */
1143 char *headptr;
1144 if (isabspath (sym.content ()))
1145 headptr = tmp_buf; /* absolute path */
1146 else
1148 /* Copy the first part of the path (with ending /) and point to
1149 the end. */
1150 char *prevtail = tail;
1151 while (--prevtail > path_copy && *prevtail != '/') {}
1152 int headlen = prevtail - path_copy + 1;;
1153 memcpy (tmp_buf, path_copy, headlen);
1154 headptr = &tmp_buf[headlen];
1157 /* Make sure there is enough space */
1158 if (headptr + symlen >= tmp_buf + (2 * NT_MAX_PATH))
1160 too_long:
1161 error = ENAMETOOLONG;
1162 set_path ("::ENAMETOOLONG::");
1163 return;
1166 /* Copy the symlink contents to the end of tmp_buf.
1167 Convert slashes. */
1168 for (const char *p = sym.content (); *p; p++)
1169 *headptr++ = *p == '\\' ? '/' : *p;
1170 *headptr = '\0';
1172 /* Copy any tail component (with the 0) */
1173 if (tail++ < path_end)
1175 /* Add a slash if needed. There is space. */
1176 if (*(headptr - 1) != '/')
1177 *headptr++ = '/';
1178 int taillen = path_end - tail + 1;
1179 if (headptr + taillen > tmp_buf + (2 * NT_MAX_PATH))
1180 goto too_long;
1181 memcpy (headptr, tail, taillen);
1184 /* Evaluate everything all over again. */
1185 src = tmp_buf;
1188 if (!(opt & PC_SYM_CONTENTS))
1189 add_ext = true;
1191 out:
1192 set_path (THIS_path);
1193 if (add_ext)
1194 add_ext_from_sym (sym);
1195 if (dev == FH_NETDRIVE && component)
1197 /* This case indicates a non-existant resp. a non-retrievable
1198 share. This happens for instance if the share is a printer.
1199 In this case the path must not be treated like a FH_NETDRIVE,
1200 but like a FH_FS instead, so the usual open call for files
1201 is used on it. */
1202 dev.parse (FH_FS);
1204 else if (isproc_dev (dev) && fileattr == INVALID_FILE_ATTRIBUTES)
1206 /* FIXME: Usually we don't set error to ENOENT if a file doesn't
1207 exist. This is typically indicated by the fileattr content.
1208 So, why here? The downside is that cygwin_conv_path just gets
1209 an error for these paths so it reports the error back to the
1210 application. Unlike in all other cases of non-existant files,
1211 for which check doesn't set error, so cygwin_conv_path just
1212 returns the path, as intended. */
1213 error = ENOENT;
1214 return;
1216 else if (!need_directory || error)
1217 /* nothing to do */;
1218 else if (fileattr == INVALID_FILE_ATTRIBUTES)
1219 /* Reattach trailing dirsep in native path. */
1220 strcat (modifiable_path (), "\\");
1221 else if (fileattr & FILE_ATTRIBUTE_DIRECTORY)
1222 path_flags &= ~PATH_SYMLINK;
1223 else
1225 debug_printf ("%s is a non-directory", path);
1226 error = ENOTDIR;
1227 return;
1230 /* Restore last path component */
1231 if (tail < path_end && tail > path_copy + 1)
1232 *tail = '/';
1234 if (dev.isfs ())
1236 /* If FS hasn't been checked already in symlink_info::check,
1237 do so now. */
1238 if (fs.inited ()|| fs.update (get_nt_native_path (), NULL))
1240 /* Incoming DOS paths are treated like DOS paths in native
1241 Windows applications. No ACLs, just default settings. */
1242 if (is_msdos)
1243 fs.has_acls (false);
1244 debug_printf ("this->path(%s), has_acls(%d)",
1245 path, fs.has_acls ());
1246 /* CV: We could use this->has_acls() but I want to make sure that
1247 we don't forget that the MOUNT_NOACL flag must be taken into
1248 account here. */
1249 if (!(mount_flags & MOUNT_NOACL) && fs.has_acls ())
1250 set_exec (0); /* We really don't know if this is executable or
1251 not here but set it to not executable since
1252 it will be figured out later by anything
1253 which cares about this. */
1255 /* If the FS has been found to have unreliable inodes, note
1256 that in mount_flags. */
1257 if (!fs.hasgood_inode ())
1258 mount_flags |= MOUNT_IHASH;
1259 /* If the OS is caseinsensitive or the FS is caseinsensitive,
1260 don't handle path casesensitive. */
1261 if (cygwin_shared->obcaseinsensitive || fs.caseinsensitive ())
1262 mount_flags |= MOUNT_NOPOSIX;
1263 caseinsensitive = (mount_flags & MOUNT_NOPOSIX)
1264 ? OBJ_CASE_INSENSITIVE : 0;
1265 if (exec_state () != dont_know_if_executable)
1266 /* ok */;
1267 else if (isdir ())
1268 set_exec (1);
1269 else if (issymlink () || issocket ())
1270 set_exec (0);
1272 /* FIXME: bad hack alert!!! We need a better solution */
1273 if (!strncmp (path_copy, MQ_PATH, MQ_LEN) && path_copy[MQ_LEN])
1274 dev.parse (FH_MQUEUE);
1277 if (opt & PC_NOFULL)
1279 if (is_relpath)
1281 mkrelpath (this->modifiable_path (), !!caseinsensitive);
1282 /* Invalidate wide_path so that wide relpath can be created
1283 in later calls to get_nt_native_path or get_wide_win32_path. */
1284 if (wide_path)
1285 cfree (wide_path);
1286 wide_path = NULL;
1288 if (need_directory)
1290 size_t n = strlen (this->path);
1291 /* Do not add trailing \ to UNC device names like \\.\a: */
1292 if (this->path[n - 1] != '\\' &&
1293 (strncmp (this->path, "\\\\.\\", 4) != 0))
1295 this->modifiable_path ()[n] = '\\';
1296 this->modifiable_path ()[n + 1] = '\0';
1301 if (opt & PC_OPEN)
1302 path_flags |= PATH_OPEN;
1304 if (opt & PC_CTTY)
1305 path_flags |= PATH_CTTY;
1307 if (opt & PC_POSIX)
1308 set_posix (path_copy);
1310 #if 0
1311 if (!error)
1313 last_path_conv = *this;
1314 strcpy (last_src, src);
1316 #endif
1318 __except (NO_ERROR)
1320 error = EFAULT;
1322 __endtry
1325 struct pc_flat
1327 path_conv pc;
1328 HANDLE hdl;
1329 size_t name_len;
1330 size_t posix_len;
1331 char data[0];
1334 void *
1335 path_conv::serialize (HANDLE h, unsigned int &n) const
1337 pc_flat *pcf;
1338 size_t nlen = 0, plen = 0;
1339 char *p;
1341 if (path)
1342 nlen = strlen (path) + 1;
1343 if (posix_path)
1344 plen = strlen (posix_path) + 1;
1345 n = sizeof (pc_flat) + nlen + plen;
1346 pcf = (pc_flat *) cmalloc (HEAP_COMMUNE, n);
1347 if (!pcf)
1349 n = 0;
1350 return NULL;
1352 memcpy ((void *) &pcf->pc, this, sizeof *this);
1353 pcf->hdl = h;
1354 pcf->name_len = nlen;
1355 pcf->posix_len = plen;
1356 p = pcf->data;
1357 if (nlen)
1358 p = stpcpy (p, path) + 1;
1359 if (plen)
1360 stpcpy (p, posix_path);
1361 return pcf;
1364 HANDLE
1365 path_conv::deserialize (void *bufp)
1367 pc_flat *pcf = (pc_flat *) bufp;
1368 char *p;
1369 HANDLE ret;
1371 memcpy ((void *) this, &pcf->pc, sizeof *this);
1372 wide_path = uni_path.Buffer = NULL;
1373 uni_path.MaximumLength = uni_path.Length = 0;
1374 path = posix_path = NULL;
1375 p = pcf->data;
1376 if (pcf->name_len)
1378 set_path (p);
1379 p += pcf->name_len;
1381 if (pcf->posix_len)
1382 set_posix (p);
1383 dev.parse (pcf->pc.dev);
1384 ret = pcf->hdl;
1385 cfree (bufp);
1386 return ret;
1389 path_conv::~path_conv ()
1391 if (posix_path)
1393 cfree ((void *) posix_path);
1394 posix_path = NULL;
1396 if (path)
1398 cfree (modifiable_path ());
1399 path = NULL;
1401 if (wide_path)
1403 cfree (wide_path);
1404 wide_path = NULL;
1406 close_conv_handle ();
1409 bool
1410 path_conv::is_binary ()
1412 tmp_pathbuf tp;
1413 PWCHAR bintest = tp.w_get ();
1414 DWORD bin;
1416 return GetBinaryTypeW (get_wide_win32_path (bintest), &bin)
1417 && (bin == SCS_32BIT_BINARY || bin == SCS_64BIT_BINARY);
1420 /* Helper function to fill the fai datastructure for a file. */
1421 NTSTATUS
1422 file_get_fai (HANDLE h, PFILE_ALL_INFORMATION pfai)
1424 NTSTATUS status;
1425 IO_STATUS_BLOCK io;
1427 /* Some FSes (Netapps) don't implement FileNetworkOpenInformation. */
1428 status = NtQueryInformationFile (h, &io, pfai, sizeof *pfai,
1429 FileAllInformation);
1430 if (likely (status == STATUS_BUFFER_OVERFLOW))
1431 status = STATUS_SUCCESS;
1432 /* Filesystems with broken FileAllInformation exist, too. See the thread
1433 starting with https://cygwin.com/ml/cygwin/2016-07/msg00350.html. */
1434 else if (!NT_SUCCESS (status) && status != STATUS_ACCESS_DENIED)
1436 memset (pfai, 0, sizeof *pfai);
1437 status = NtQueryInformationFile (h, &io, &pfai->BasicInformation,
1438 sizeof pfai->BasicInformation,
1439 FileBasicInformation);
1440 if (NT_SUCCESS (status))
1442 /* The return value of FileInternalInformation is largely ignored.
1443 We only make absolutely sure the inode number is set to 0 in
1444 case it fails. */
1445 status = NtQueryInformationFile (h, &io, &pfai->InternalInformation,
1446 sizeof pfai->InternalInformation,
1447 FileInternalInformation);
1448 if (!NT_SUCCESS (status))
1449 pfai->InternalInformation.IndexNumber.QuadPart = 0LL;
1450 status = NtQueryInformationFile (h, &io, &pfai->StandardInformation,
1451 sizeof pfai->StandardInformation,
1452 FileStandardInformation);
1455 return status;
1458 /* Normalize a Win32 path.
1459 /'s are converted to \'s in the process.
1460 All duplicate \'s, except for 2 leading \'s, are deleted.
1462 The result is 0 for success, or an errno error value.
1463 FIXME: A lot of this should be mergeable with the POSIX critter. */
1465 normalize_win32_path (const char *src, char *dst, char *&tail)
1467 const char *src_start = src;
1468 const char *dst_start = dst;
1469 bool beg_src_slash = isdirsep (src[0]);
1471 tail = dst;
1472 /* Skip Win32 long path name prefix and NT object directory prefix. */
1473 if (beg_src_slash && (src[1] == '?' || isdirsep (src[1]))
1474 && src[2] == '?' && isdirsep (src[3]))
1476 src += 4;
1477 if (isdrive (src) && (isdirsep (src[2]) || !src[2]))
1478 beg_src_slash = false;
1479 else if (!strncmp (src, "UNC", 3) && isdirsep (src[3]))
1480 /* native UNC path */
1481 src += 2; /* Fortunately the first char is not copied... */
1482 else
1483 return EINVAL;
1485 if (beg_src_slash && isdirsep (src[1]))
1487 if (isdirsep (src[2]))
1489 /* More than two slashes are just folded into one. */
1490 src += 2;
1491 while (isdirsep (src[1]))
1492 ++src;
1494 else
1496 /* Two slashes start a network or device path. */
1497 *tail++ = '\\';
1498 src++;
1499 if (src[1] == '.' && isdirsep (src[2]))
1501 *tail++ = '\\';
1502 *tail++ = '.';
1503 src += 2;
1506 dst = tail;
1507 /* If backslash is missing in src, add one. */
1508 if (!isdirsep (src[0]))
1509 *tail++ = '\\';
1511 if (tail == dst_start)
1513 if (isdrive (src))
1515 /* Always convert drive letter to uppercase for case sensitivity. */
1516 *tail++ = cyg_toupper (*src++);
1517 *tail++ = *src++;
1518 dst = tail;
1519 /* If backslash is missing in src, add one. */
1520 if (!isdirsep (src[0]))
1521 *tail++ = '\\';
1523 else if (*src != '/')
1525 /* Make sure dst points to the rightmost backslash which must not
1526 be backtracked over during ".." evaluation. This is either
1527 the backslash after the network path prefix (i.e. "\\") or
1528 the backslash after a drive letter (i.e. C:\"). */
1529 if (beg_src_slash)
1531 tail += cygheap->cwd.get_drive (dst);
1532 /* network path, drive == '\\\\'? Decrement tail to avoid
1533 triple backslash in output. */
1534 if (dst[0] == '\\')
1535 --tail;
1536 dst = tail;
1538 else if (cygheap->cwd.get (dst, 0))
1540 tail = strchr (tail, '\0');
1541 if (tail[-1] != '\\')
1542 *tail++ = '\\';
1543 ++dst;
1544 if (dst[1] == '\\')
1545 ++dst;
1547 else
1548 return get_errno ();
1552 /* At this point dst points to the first backslash, even if it only gets
1553 written in the first iteration of the following loop. Backing up to
1554 handle ".." components can not underrun that border (thus avoiding
1555 subsequent buffer underruns with fatal results). */
1556 while (*src)
1558 /* Strip duplicate /'s. */
1559 if (isdirsep (src[0]) && isdirsep (src[1]))
1560 src++;
1561 /* Ignore "./". */
1562 else if (src[0] == '.' && isdirsep (src[1])
1563 && (src == src_start || isdirsep (src[-1])))
1565 src += 2;
1566 /* Skip /'s to the next path component. */
1567 while (isdirsep (*src))
1568 src++;
1571 /* Backup if "..". */
1572 else if (src[0] == '.' && src[1] == '.' && tail[-1] == '\\')
1574 if (!isdirsep (src[2]) && src[2] != '\0')
1575 *tail++ = *src++;
1576 else
1578 /* Back up over /, but not if it's the first one. */
1579 if (tail > dst + 1)
1580 tail--;
1581 /* Now back up to the next /. */
1582 while (tail > dst + 1 && tail[-1] != '\\' && tail[-2] != ':')
1583 tail--;
1584 src += 2;
1585 /* Skip /'s to the next path component. */
1586 while (isdirsep (*src))
1587 src++;
1590 /* Otherwise, add char to result. */
1591 else
1593 if (*src == '/')
1594 *tail++ = '\\';
1595 else
1596 *tail++ = *src;
1597 src++;
1599 if ((tail - dst) >= NT_MAX_PATH)
1600 return ENAMETOOLONG;
1602 if (tail > dst + 1 && tail[-1] == '.' && tail[-2] == '\\')
1603 tail--;
1604 *tail = '\0';
1605 debug_printf ("%s = normalize_win32_path (%s)", dst_start, src_start);
1606 return 0;
1609 /* Various utilities. */
1611 /* nofinalslash: Remove trailing / and \ from SRC (except for the
1612 first one). It is ok for src == dst. */
1614 void
1615 nofinalslash (const char *src, char *dst)
1617 int len = strlen (src);
1618 if (src != dst)
1619 memcpy (dst, src, len + 1);
1620 while (len > 1 && isdirsep (dst[--len]))
1621 dst[len] = '\0';
1624 /* conv_path_list: Convert a list of path names to/from Win32/POSIX. */
1626 static int
1627 conv_path_list (const char *src, char *dst, size_t size,
1628 cygwin_conv_path_t what)
1630 tmp_pathbuf tp;
1631 char src_delim, dst_delim;
1632 size_t len;
1633 bool env_cvt = false;
1635 if (what == (cygwin_conv_path_t) ENV_CVT)
1637 what = CCP_WIN_A_TO_POSIX | CCP_RELATIVE;
1638 env_cvt = true;
1640 if ((what & CCP_CONVTYPE_MASK) == CCP_WIN_A_TO_POSIX)
1642 src_delim = ';';
1643 dst_delim = ':';
1645 else
1647 src_delim = ':';
1648 dst_delim = ';';
1651 char *srcbuf;
1652 len = strlen (src) + 1;
1653 if (len <= NT_MAX_PATH * sizeof (WCHAR))
1654 srcbuf = (char *) tp.w_get ();
1655 else
1656 srcbuf = (char *) alloca (len);
1658 int err = 0;
1659 char *d = dst - 1;
1660 bool saw_empty = false;
1663 char *srcpath = srcbuf;
1664 char *s = strccpy (srcpath, &src, src_delim);
1665 size_t len = s - srcpath;
1666 if (len >= NT_MAX_PATH)
1668 err = ENAMETOOLONG;
1669 break;
1671 /* Paths in Win32 path lists in the environment (%Path%), are often
1672 enclosed in quotes (usually paths with spaces). Trailing backslashes
1673 are common, too. Remove them. */
1674 if (env_cvt && len)
1676 if (*srcpath == '"')
1678 ++srcpath;
1679 *--s = '\0';
1680 len -= 2;
1682 while (len && s[-1] == '\\')
1684 *--s = '\0';
1685 --len;
1688 if (len)
1690 ++d;
1691 err = cygwin_conv_path (what, srcpath, d, size - (d - dst));
1693 else if ((what & CCP_CONVTYPE_MASK) == CCP_POSIX_TO_WIN_A)
1695 ++d;
1696 err = cygwin_conv_path (what, ".", d, size - (d - dst));
1698 else
1700 if (env_cvt)
1701 saw_empty = true;
1702 continue;
1704 if (err)
1705 break;
1706 d = strchr (d, '\0');
1707 *d = dst_delim;
1709 while (*src++);
1711 if (saw_empty)
1712 err = EIDRM;
1714 if (d < dst)
1715 d++;
1716 *d = '\0';
1717 return err;
1720 /********************** Symbolic Link Support **************************/
1722 /* Create a symlink from FROMPATH to TOPATH. */
1724 extern "C" int
1725 symlink (const char *oldpath, const char *newpath)
1727 path_conv win32_newpath;
1729 __try
1731 if (!*oldpath || !*newpath)
1733 set_errno (ENOENT);
1734 __leave;
1737 /* Trailing dirsep is a no-no, only errno differs. */
1738 bool has_trailing_dirsep = isdirsep (newpath[strlen (newpath) - 1]);
1739 win32_newpath.check (newpath,
1740 PC_SYM_NOFOLLOW | PC_SYM_NOFOLLOW_DIR | PC_POSIX,
1741 stat_suffixes);
1743 if (win32_newpath.error || has_trailing_dirsep)
1745 set_errno (win32_newpath.error ?:
1746 win32_newpath.exists () ? EEXIST : ENOENT);
1747 __leave;
1750 return symlink_worker (oldpath, win32_newpath, false);
1752 __except (EFAULT) {}
1753 __endtry
1754 return -1;
1757 static int
1758 symlink_nfs (const char *oldpath, path_conv &win32_newpath)
1760 /* On NFS, create symlinks by calling NtCreateFile with an EA of type
1761 NfsSymlinkTargetName containing ... the symlink target name. */
1762 tmp_pathbuf tp;
1763 PFILE_FULL_EA_INFORMATION pffei;
1764 NTSTATUS status;
1765 HANDLE fh;
1766 OBJECT_ATTRIBUTES attr;
1767 IO_STATUS_BLOCK io;
1769 pffei = (PFILE_FULL_EA_INFORMATION) tp.w_get ();
1770 pffei->NextEntryOffset = 0;
1771 pffei->Flags = 0;
1772 pffei->EaNameLength = sizeof (NFS_SYML_TARGET) - 1;
1773 char *EaValue = stpcpy (pffei->EaName, NFS_SYML_TARGET) + 1;
1774 pffei->EaValueLength = sizeof (WCHAR) *
1775 (sys_mbstowcs ((PWCHAR) EaValue, NT_MAX_PATH, oldpath) - 1);
1776 status = NtCreateFile (&fh, FILE_WRITE_DATA | FILE_WRITE_EA | SYNCHRONIZE,
1777 win32_newpath.get_object_attr (attr, sec_none_nih),
1778 &io, NULL, FILE_ATTRIBUTE_SYSTEM,
1779 FILE_SHARE_VALID_FLAGS, FILE_CREATE,
1780 FILE_SYNCHRONOUS_IO_NONALERT
1781 | FILE_OPEN_FOR_BACKUP_INTENT,
1782 pffei, NT_MAX_PATH * sizeof (WCHAR));
1783 if (!NT_SUCCESS (status))
1785 __seterrno_from_nt_status (status);
1786 return -1;
1788 NtClose (fh);
1789 return 0;
1792 /* Count backslashes between s and e. */
1793 static inline int
1794 cnt_bs (PWCHAR s, PWCHAR e)
1796 int num = 0;
1798 while (s < e)
1799 if (*s++ == L'\\')
1800 ++num;
1801 return num;
1804 #ifndef SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
1805 #define SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE 2
1806 #endif
1808 static int
1809 symlink_native (const char *oldpath, path_conv &win32_newpath)
1811 tmp_pathbuf tp;
1812 path_conv win32_oldpath;
1813 PUNICODE_STRING final_oldpath, final_newpath;
1814 UNICODE_STRING final_oldpath_buf;
1815 DWORD flags;
1817 if (isabspath (oldpath))
1819 win32_oldpath.check (oldpath, PC_SYM_NOFOLLOW, stat_suffixes);
1820 final_oldpath = win32_oldpath.get_nt_native_path ();
1822 else
1824 /* The symlink target is relative to the directory in which
1825 the symlink gets created, not relative to the cwd. Therefore
1826 we have to mangle the path quite a bit before calling path_conv. */
1827 ssize_t len = strrchr (win32_newpath.get_posix (), '/')
1828 - win32_newpath.get_posix () + 1;
1829 char *absoldpath = tp.t_get ();
1830 stpcpy (stpncpy (absoldpath, win32_newpath.get_posix (), len),
1831 oldpath);
1832 win32_oldpath.check (absoldpath, PC_SYM_NOFOLLOW, stat_suffixes);
1834 /* Try hard to keep Windows symlink path relative. */
1836 /* 1. Find common path prefix. Skip leading \\?\, but take pre-increment
1837 of the following loop into account. */
1838 PWCHAR c_old = win32_oldpath.get_nt_native_path ()->Buffer + 3;
1839 PWCHAR c_new = win32_newpath.get_nt_native_path ()->Buffer + 3;
1840 /* Windows compatible == always check case insensitive. */
1841 while (towupper (*++c_old) == towupper (*++c_new))
1843 /* The last component could share a common prefix, so make sure we end
1844 up on the first char after the last common backslash. */
1845 while (c_old[-1] != L'\\')
1846 --c_old, --c_new;
1848 /* 2. Check if prefix is long enough. The prefix must at least points to
1849 a complete device: \\?\X:\ or \\?\UNC\server\share\ are the minimum
1850 prefix strings. We start counting behind the \\?\ for speed. */
1851 int num = cnt_bs (win32_oldpath.get_nt_native_path ()->Buffer + 4, c_old);
1852 if (num < 1 /* locale drive. */
1853 || (win32_oldpath.get_nt_native_path ()->Buffer[5] != L':'
1854 && num < 3)) /* UNC path. */
1856 /* 3a. No valid common path prefix: Create absolute symlink. */
1857 final_oldpath = win32_oldpath.get_nt_native_path ();
1859 else
1861 /* 3b. Common path prefx. Count number of additional directories
1862 in symlink's path, and prepend as much ".." path components
1863 to the target path. */
1864 PWCHAR e_new = win32_newpath.get_nt_native_path ()->Buffer
1865 + win32_newpath.get_nt_native_path ()->Length
1866 / sizeof (WCHAR);
1867 num = cnt_bs (c_new, e_new);
1868 final_oldpath = &final_oldpath_buf;
1869 final_oldpath->Buffer = tp.w_get ();
1870 PWCHAR e_old = final_oldpath->Buffer;
1871 while (num-- > 0)
1872 e_old = wcpcpy (e_old, L"..\\");
1873 wcpcpy (e_old, c_old);
1876 /* If the symlink target doesn't exist, don't create native symlink.
1877 Otherwise the directory flag in the symlink is potentially wrong
1878 when the target comes into existence, and native tools will fail.
1879 This is so screwball. This is no problem on AFS, fortunately. */
1880 if (!win32_oldpath.exists () && !win32_oldpath.fs_is_afs ())
1882 SetLastError (ERROR_FILE_NOT_FOUND);
1883 return -1;
1885 /* Don't allow native symlinks to Cygwin special files. However, the
1886 caller shoud know because this case shouldn't be covered by the
1887 default "nativestrict" behaviour, so we use a special return code. */
1888 if (win32_oldpath.isspecial ())
1889 return -2;
1890 /* Convert native paths to Win32 UNC paths. */
1891 final_newpath = win32_newpath.get_nt_native_path ();
1892 final_newpath->Buffer[1] = L'\\';
1893 /* oldpath may be relative. Make sure to convert only absolute paths
1894 to Win32 paths. */
1895 if (final_oldpath->Buffer[0] == L'\\')
1897 /* Starting with Windows 8.1, the ShellExecuteW function does not
1898 handle the long path prefix correctly for symlink targets. Thus,
1899 we create simple short paths < MAX_PATH without long path prefix. */
1900 if (RtlEqualUnicodePathPrefix (final_oldpath, &ro_u_uncp, TRUE)
1901 && final_oldpath->Length < (MAX_PATH + 6) * sizeof (WCHAR))
1903 final_oldpath->Buffer += 6;
1904 final_oldpath->Buffer[0] = L'\\';
1906 else if (final_oldpath->Length < (MAX_PATH + 4) * sizeof (WCHAR))
1907 final_oldpath->Buffer += 4;
1908 else /* Stick to long path, fix native prefix for Win32 API calls. */
1909 final_oldpath->Buffer[1] = L'\\';
1911 /* Try to create native symlink. */
1912 flags = win32_oldpath.isdir () ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0;
1913 if (wincap.has_unprivileged_createsymlink ())
1914 flags |= SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
1915 if (!CreateSymbolicLinkW (final_newpath->Buffer, final_oldpath->Buffer,
1916 flags))
1918 /* Repair native newpath, we still need it. */
1919 final_newpath->Buffer[1] = L'?';
1920 return -1;
1922 return 0;
1925 #ifndef IO_REPARSE_TAG_LX_SYMLINK
1926 #define IO_REPARSE_TAG_LX_SYMLINK (0xa000001d)
1927 #endif
1929 typedef struct _REPARSE_LX_SYMLINK_BUFFER
1931 DWORD ReparseTag;
1932 WORD ReparseDataLength;
1933 WORD Reserved;
1934 struct {
1935 DWORD FileType; /* Take member name with a grain of salt. Value is
1936 apparently always 2 for symlinks. */
1937 char PathBuffer[1];/* POSIX path as given to symlink(2).
1938 Path is not \0 terminated.
1939 Length is ReparseDataLength - sizeof (FileType).
1940 Always UTF-8.
1941 Chars given in incompatible codesets, e. g. umlauts
1942 in ISO-8859-x, are converted to the Unicode
1943 REPLACEMENT CHARACTER 0xfffd == \xef\xbf\bd */
1944 } LxSymlinkReparseBuffer;
1945 } REPARSE_LX_SYMLINK_BUFFER,*PREPARSE_LX_SYMLINK_BUFFER;
1947 static int
1948 symlink_wsl (const char *oldpath, path_conv &win32_newpath)
1950 tmp_pathbuf tp;
1951 PREPARSE_LX_SYMLINK_BUFFER rpl = (PREPARSE_LX_SYMLINK_BUFFER) tp.c_get ();
1952 char *path_buf = rpl->LxSymlinkReparseBuffer.PathBuffer;
1953 const int max_pathlen = MAXIMUM_REPARSE_DATA_BUFFER_SIZE
1954 - offsetof (REPARSE_LX_SYMLINK_BUFFER,
1955 LxSymlinkReparseBuffer.PathBuffer);
1956 PWCHAR utf16 = tp.w_get ();
1957 NTSTATUS status;
1958 IO_STATUS_BLOCK io;
1959 OBJECT_ATTRIBUTES attr;
1960 HANDLE fh;
1961 int len;
1963 rpl->ReparseTag = IO_REPARSE_TAG_LX_SYMLINK;
1964 rpl->Reserved = 0;
1965 rpl->LxSymlinkReparseBuffer.FileType = 2;
1966 /* Convert cygdrive prefix to "/mnt" for WSL compatibility, but only if
1967 cygdrive prefix is not "/", otherwise suffer random "/mnt" symlinks... */
1968 if (mount_table->cygdrive_len > 1
1969 && path_prefix_p (mount_table->cygdrive, oldpath,
1970 mount_table->cygdrive_len, false)
1971 && (strlen (oldpath + mount_table->cygdrive_len - 1) < 2
1972 || (islower (oldpath[mount_table->cygdrive_len])
1973 && (oldpath[mount_table->cygdrive_len + 1] == '/'
1974 || oldpath[mount_table->cygdrive_len + 1] == '\0'))))
1975 stpcpy (stpcpy (path_buf, "/mnt"),
1976 oldpath + mount_table->cygdrive_len - 1);
1977 else
1978 *stpncpy (path_buf, oldpath, max_pathlen) = '\0';
1979 /* Convert target path to UTF-16 and then back to UTF-8 to make sure the
1980 WSL symlink is in UTF-8, independent of the current Cygwin codeset. */
1981 sys_mbstowcs (utf16, NT_MAX_PATH, path_buf);
1982 len = WideCharToMultiByte (CP_UTF8, 0, utf16, -1, path_buf, max_pathlen,
1983 NULL, NULL);
1984 /* Length is omitting trailing \0. */
1985 rpl->ReparseDataLength = sizeof (DWORD) + len - 1;
1986 /* Create reparse point. */
1987 status = NtCreateFile (&fh, DELETE | FILE_GENERIC_WRITE
1988 | READ_CONTROL | WRITE_DAC,
1989 win32_newpath.get_object_attr (attr, sec_none_nih),
1990 &io, NULL, FILE_ATTRIBUTE_NORMAL,
1991 FILE_SHARE_VALID_FLAGS, FILE_CREATE,
1992 FILE_SYNCHRONOUS_IO_NONALERT
1993 | FILE_NON_DIRECTORY_FILE
1994 | FILE_OPEN_FOR_BACKUP_INTENT
1995 | FILE_OPEN_REPARSE_POINT,
1996 NULL, 0);
1997 if (!NT_SUCCESS (status))
1999 SetLastError (RtlNtStatusToDosError (status));
2000 return -1;
2002 set_created_file_access (fh, win32_newpath, S_IFLNK | STD_RBITS | STD_WBITS);
2003 status = NtFsControlFile (fh, NULL, NULL, NULL, &io, FSCTL_SET_REPARSE_POINT,
2004 (LPVOID) rpl, REPARSE_DATA_BUFFER_HEADER_SIZE
2005 + rpl->ReparseDataLength,
2006 NULL, 0);
2007 if (!NT_SUCCESS (status))
2009 SetLastError (RtlNtStatusToDosError (status));
2010 FILE_DISPOSITION_INFORMATION fdi = { TRUE };
2011 status = NtSetInformationFile (fh, &io, &fdi, sizeof fdi,
2012 FileDispositionInformation);
2013 NtClose (fh);
2014 if (!NT_SUCCESS (status))
2015 debug_printf ("Setting delete dispostion failed, status = %y", status);
2016 return -1;
2018 NtClose (fh);
2019 return 0;
2023 symlink_worker (const char *oldpath, path_conv &win32_newpath, bool isdevice)
2025 int res = -1;
2026 size_t len;
2027 char *buf, *cp;
2028 tmp_pathbuf tp;
2029 winsym_t wsym_type;
2031 /* POSIX says that empty 'newpath' is invalid input while empty
2032 'oldpath' is valid -- it's symlink resolver job to verify if
2033 symlink contents point to existing filesystem object */
2034 __try
2036 if (strlen (oldpath) > SYMLINK_MAX)
2038 set_errno (ENAMETOOLONG);
2039 __leave;
2042 /* Default symlink type is determined by global allow_winsymlinks
2043 variable. Device files are always shortcuts. */
2044 wsym_type = isdevice ? WSYM_lnk : allow_winsymlinks;
2045 /* NFS has its own, dedicated way to create symlinks. */
2046 if (win32_newpath.fs_is_nfs () && !isdevice)
2047 wsym_type = WSYM_nfs;
2048 /* MVFS doesn't handle the SYSTEM DOS attribute, but it handles the R/O
2049 attribute. Therefore we create symlinks on MVFS always as shortcuts. */
2050 else if (win32_newpath.fs_is_mvfs ())
2051 wsym_type = WSYM_lnk;
2052 /* AFS only supports native symlinks. */
2053 else if (win32_newpath.fs_is_afs ())
2054 wsym_type = WSYM_nativestrict;
2055 /* Don't try native symlinks on FSes not supporting reparse points. */
2056 else if ((wsym_type == WSYM_native || wsym_type == WSYM_nativestrict)
2057 && !(win32_newpath.fs_flags () & FILE_SUPPORTS_REPARSE_POINTS))
2058 wsym_type = WSYM_default;
2060 /* Attach .lnk suffix when shortcut is requested. */
2061 if (wsym_type == WSYM_lnk && !win32_newpath.exists ()
2062 && (isdevice || !win32_newpath.fs_is_nfs ()))
2064 char *newplnk = tp.c_get ();
2065 stpcpy (stpcpy (newplnk, win32_newpath.get_posix ()), ".lnk");
2066 win32_newpath.check (newplnk, PC_SYM_NOFOLLOW | PC_POSIX);
2069 if (win32_newpath.error)
2071 set_errno (win32_newpath.error);
2072 __leave;
2075 syscall_printf ("symlink (%s, %S) wsym_type %d", oldpath,
2076 win32_newpath.get_nt_native_path (), wsym_type);
2078 if ((!isdevice && win32_newpath.exists ())
2079 || (win32_newpath.isdevice () && !win32_newpath.is_fs_special ()))
2081 set_errno (EEXIST);
2082 __leave;
2085 /* Handle NFS, native symlinks and WSL symlinks in their own functions. */
2086 switch (wsym_type)
2088 case WSYM_nfs:
2089 res = symlink_nfs (oldpath, win32_newpath);
2090 __leave;
2091 case WSYM_native:
2092 case WSYM_nativestrict:
2093 res = symlink_native (oldpath, win32_newpath);
2094 if (!res)
2095 __leave;
2096 /* Strictly native? Too bad, unless the target is a Cygwin
2097 special file. */
2098 if (res == -1 && wsym_type == WSYM_nativestrict)
2100 __seterrno ();
2101 __leave;
2103 /* Otherwise, fall back to default symlink type. */
2104 wsym_type = WSYM_default;
2105 fallthrough;
2106 case WSYM_default:
2107 if (win32_newpath.fs_flags () & FILE_SUPPORTS_REPARSE_POINTS)
2109 res = symlink_wsl (oldpath, win32_newpath);
2110 if (!res)
2111 __leave;
2113 /* On FSes not supporting reparse points, or in case of an error
2114 creating the WSL symlink, fall back to creating the plain old
2115 SYSTEM file symlink. */
2116 wsym_type = WSYM_sysfile;
2117 break;
2118 default:
2119 break;
2122 if (wsym_type == WSYM_lnk)
2124 path_conv win32_oldpath;
2125 ITEMIDLIST *pidl = NULL;
2126 size_t full_len = 0;
2127 unsigned short oldpath_len, desc_len, relpath_len, pidl_len = 0;
2128 char desc[MAX_PATH + 1], *relpath;
2130 if (!isdevice)
2132 /* First create an IDLIST to learn how big our shortcut is
2133 going to be. */
2134 IShellFolder *psl;
2136 /* The symlink target is relative to the directory in which the
2137 symlink gets created, not relative to the cwd. Therefore we
2138 have to mangle the path quite a bit before calling path_conv.*/
2139 if (isabspath (oldpath))
2140 win32_oldpath.check (oldpath,
2141 PC_SYM_NOFOLLOW,
2142 stat_suffixes);
2143 else
2145 len = strrchr (win32_newpath.get_posix (), '/')
2146 - win32_newpath.get_posix () + 1;
2147 char *absoldpath = tp.t_get ();
2148 stpcpy (stpncpy (absoldpath, win32_newpath.get_posix (),
2149 len),
2150 oldpath);
2151 win32_oldpath.check (absoldpath, PC_SYM_NOFOLLOW,
2152 stat_suffixes);
2154 if (SUCCEEDED (SHGetDesktopFolder (&psl)))
2156 WCHAR wc_path[win32_oldpath.get_wide_win32_path_len () + 1];
2157 win32_oldpath.get_wide_win32_path (wc_path);
2158 /* Amazing but true: Even though the ParseDisplayName method
2159 takes a wide char path name, it does not understand the
2160 Win32 prefix for long pathnames! So we have to tack off
2161 the prefix and convert the path to the "normal" syntax
2162 for ParseDisplayName. */
2163 PWCHAR wc = wc_path + 4;
2164 if (wc[1] != L':') /* native UNC path */
2165 *(wc += 2) = L'\\';
2166 HRESULT res;
2167 if (SUCCEEDED (res = psl->ParseDisplayName (NULL, NULL, wc,
2168 NULL, &pidl,
2169 NULL)))
2171 ITEMIDLIST *p;
2173 for (p = pidl; p->mkid.cb > 0;
2174 p = (ITEMIDLIST *)((char *) p + p->mkid.cb))
2176 pidl_len = (char *) p - (char *) pidl + 2;
2178 psl->Release ();
2181 /* Compute size of shortcut file. */
2182 full_len = sizeof (win_shortcut_hdr);
2183 if (pidl_len)
2184 full_len += sizeof (unsigned short) + pidl_len;
2185 oldpath_len = strlen (oldpath);
2186 /* Unfortunately the length of the description is restricted to a
2187 length of 2000 bytes. We don't want to add considerations for
2188 the different lengths and even 2000 bytes is not enough for long
2189 path names. So what we do here is to set the description to the
2190 POSIX path only if the path is not longer than MAX_PATH characters.
2191 We append the full path name after the regular shortcut data
2192 (see below), which works fine with Windows Explorer as well
2193 as older Cygwin versions (as long as the whole file isn't bigger
2194 than 8K). The description field is only used for backward
2195 compatibility to older Cygwin versions and those versions are
2196 not capable of handling long path names anyway. */
2197 desc_len = stpcpy (desc, oldpath_len > MAX_PATH
2198 ? "[path too long]" : oldpath) - desc;
2199 full_len += sizeof (unsigned short) + desc_len;
2200 /* Devices get the oldpath string unchanged as relative path. */
2201 if (isdevice)
2203 relpath_len = oldpath_len;
2204 stpcpy (relpath = tp.c_get (), oldpath);
2206 else
2208 relpath_len = strlen (win32_oldpath.get_win32 ());
2209 stpcpy (relpath = tp.c_get (), win32_oldpath.get_win32 ());
2211 full_len += sizeof (unsigned short) + relpath_len;
2212 full_len += sizeof (unsigned short) + oldpath_len;
2213 /* 1 byte more for trailing 0 written by stpcpy. */
2214 if (full_len < NT_MAX_PATH * sizeof (WCHAR))
2215 buf = tp.t_get ();
2216 else
2217 buf = (char *) alloca (full_len + 1);
2219 /* Create shortcut header */
2220 win_shortcut_hdr *shortcut_header = (win_shortcut_hdr *) buf;
2221 memset (shortcut_header, 0, sizeof *shortcut_header);
2222 shortcut_header->size = sizeof *shortcut_header;
2223 shortcut_header->magic = GUID_shortcut;
2224 shortcut_header->flags = (WSH_FLAG_DESC | WSH_FLAG_RELPATH);
2225 if (pidl)
2226 shortcut_header->flags |= WSH_FLAG_IDLIST;
2227 shortcut_header->run = SW_NORMAL;
2228 cp = buf + sizeof (win_shortcut_hdr);
2230 /* Create IDLIST */
2231 if (pidl)
2233 *(unsigned short *)cp = pidl_len;
2234 memcpy (cp += 2, pidl, pidl_len);
2235 cp += pidl_len;
2236 CoTaskMemFree (pidl);
2239 /* Create description */
2240 *(unsigned short *)cp = desc_len;
2241 cp = stpcpy (cp += 2, desc);
2243 /* Create relpath */
2244 *(unsigned short *)cp = relpath_len;
2245 cp = stpcpy (cp += 2, relpath);
2247 /* Append the POSIX path after the regular shortcut data for
2248 the long path support. */
2249 unsigned short *plen = (unsigned short *) cp;
2250 cp += 2;
2251 *(PWCHAR) cp = 0xfeff; /* BOM */
2252 cp += 2;
2253 *plen = sys_mbstowcs ((PWCHAR) cp, NT_MAX_PATH, oldpath)
2254 * sizeof (WCHAR);
2255 cp += *plen;
2257 else /* wsym_type == WSYM_sysfile */
2259 /* Default technique creating a symlink. */
2260 buf = tp.t_get ();
2261 cp = stpcpy (buf, SYMLINK_COOKIE);
2262 *(PWCHAR) cp = 0xfeff; /* BOM */
2263 cp += 2;
2264 /* Note that the terminating nul is written. */
2265 cp += sys_mbstowcs ((PWCHAR) cp, NT_MAX_PATH, oldpath)
2266 * sizeof (WCHAR);
2269 OBJECT_ATTRIBUTES attr;
2270 IO_STATUS_BLOCK io;
2271 NTSTATUS status;
2272 ULONG access;
2273 HANDLE fh;
2275 access = DELETE | FILE_GENERIC_WRITE;
2276 if (isdevice && win32_newpath.exists ())
2278 status = NtOpenFile (&fh, FILE_WRITE_ATTRIBUTES,
2279 win32_newpath.get_object_attr (attr,
2280 sec_none_nih),
2281 &io, 0, FILE_OPEN_FOR_BACKUP_INTENT);
2282 if (!NT_SUCCESS (status))
2284 __seterrno_from_nt_status (status);
2285 __leave;
2287 status = NtSetAttributesFile (fh, FILE_ATTRIBUTE_NORMAL);
2288 NtClose (fh);
2289 if (!NT_SUCCESS (status))
2291 __seterrno_from_nt_status (status);
2292 __leave;
2295 else if (!isdevice && win32_newpath.has_acls ()
2296 && !win32_newpath.isremote ())
2297 /* If the filesystem supports ACLs, we will overwrite the DACL after the
2298 call to NtCreateFile. This requires a handle with READ_CONTROL and
2299 WRITE_DAC access, otherwise get_file_sd and set_file_sd both have to
2300 open the file again.
2301 FIXME: On remote NTFS shares open sometimes fails because even the
2302 creator of the file doesn't have the right to change the DACL.
2303 I don't know what setting that is or how to recognize such a share,
2304 so for now we don't request WRITE_DAC on remote drives. */
2305 access |= READ_CONTROL | WRITE_DAC;
2307 status = NtCreateFile (&fh, access,
2308 win32_newpath.get_object_attr (attr, sec_none_nih),
2309 &io, NULL, FILE_ATTRIBUTE_NORMAL,
2310 FILE_SHARE_VALID_FLAGS,
2311 isdevice ? FILE_OVERWRITE_IF : FILE_CREATE,
2312 FILE_SYNCHRONOUS_IO_NONALERT
2313 | FILE_NON_DIRECTORY_FILE
2314 | FILE_OPEN_FOR_BACKUP_INTENT,
2315 NULL, 0);
2316 if (!NT_SUCCESS (status))
2318 __seterrno_from_nt_status (status);
2319 __leave;
2321 if (io.Information == FILE_CREATED && win32_newpath.has_acls ())
2322 set_created_file_access (fh, win32_newpath,
2323 S_IFLNK | STD_RBITS | STD_WBITS);
2324 status = NtWriteFile (fh, NULL, NULL, NULL, &io, buf, cp - buf,
2325 NULL, NULL);
2326 if (NT_SUCCESS (status) && io.Information == (ULONG) (cp - buf))
2328 status = NtSetAttributesFile (fh, wsym_type == WSYM_lnk
2329 ? FILE_ATTRIBUTE_READONLY
2330 : FILE_ATTRIBUTE_SYSTEM);
2331 if (!NT_SUCCESS (status))
2332 debug_printf ("Setting attributes failed, status = %y", status);
2333 res = 0;
2335 else
2337 __seterrno_from_nt_status (status);
2338 FILE_DISPOSITION_INFORMATION fdi = { TRUE };
2339 status = NtSetInformationFile (fh, &io, &fdi, sizeof fdi,
2340 FileDispositionInformation);
2341 if (!NT_SUCCESS (status))
2342 debug_printf ("Setting delete dispostion failed, status = %y",
2343 status);
2345 NtClose (fh);
2348 __except (EFAULT) {}
2349 __endtry
2350 syscall_printf ("%d = symlink_worker(%s, %s, %d)",
2351 res, oldpath, win32_newpath.get_posix (), isdevice);
2352 return res;
2355 static bool
2356 cmp_shortcut_header (win_shortcut_hdr *file_header)
2358 /* A Cygwin or U/Win shortcut only contains a description and a relpath.
2359 Cygwin shortcuts also might contain an ITEMIDLIST. The run type is
2360 always set to SW_NORMAL. */
2361 return file_header->size == sizeof (win_shortcut_hdr)
2362 && !memcmp (&file_header->magic, &GUID_shortcut, sizeof GUID_shortcut)
2363 && (file_header->flags & ~WSH_FLAG_IDLIST)
2364 == (WSH_FLAG_DESC | WSH_FLAG_RELPATH)
2365 && file_header->run == SW_NORMAL;
2369 symlink_info::check_shortcut (HANDLE h)
2371 tmp_pathbuf tp;
2372 win_shortcut_hdr *file_header;
2373 char *buf, *cp;
2374 unsigned short len;
2375 int res = 0;
2376 NTSTATUS status;
2377 IO_STATUS_BLOCK io;
2378 FILE_STANDARD_INFORMATION fsi;
2379 LARGE_INTEGER off = { QuadPart:0LL };
2381 status = NtQueryInformationFile (h, &io, &fsi, sizeof fsi,
2382 FileStandardInformation);
2383 if (!NT_SUCCESS (status))
2385 set_error (EIO);
2386 return 0;
2388 if (fsi.EndOfFile.QuadPart <= (LONGLONG) sizeof (win_shortcut_hdr)
2389 || fsi.EndOfFile.QuadPart > 4 * 65536)
2390 return 0;
2391 if (fsi.EndOfFile.LowPart < NT_MAX_PATH * sizeof (WCHAR))
2392 buf = (char *) tp.w_get ();
2393 else
2394 buf = (char *) alloca (fsi.EndOfFile.LowPart + 1);
2395 status = NtReadFile (h, NULL, NULL, NULL, &io, buf, fsi.EndOfFile.LowPart,
2396 &off, NULL);
2397 if (!NT_SUCCESS (status))
2399 if (status != STATUS_END_OF_FILE)
2400 set_error (EIO);
2401 return 0;
2403 file_header = (win_shortcut_hdr *) buf;
2404 if (io.Information != fsi.EndOfFile.LowPart
2405 || !cmp_shortcut_header (file_header))
2406 return 0;
2407 cp = buf + sizeof (win_shortcut_hdr);
2408 if (file_header->flags & WSH_FLAG_IDLIST) /* Skip ITEMIDLIST */
2409 cp += *(unsigned short *) cp + 2;
2410 if (!(len = *(unsigned short *) cp))
2411 return 0;
2412 cp += 2;
2413 /* Check if this is a device file - these start with the sequence :\\ */
2414 if (strncmp (cp, ":\\", 2) == 0)
2415 res = strlen (strcpy (contents, cp)); /* Don't mess with device files */
2416 else
2418 /* Has appended full path? If so, use it instead of description. */
2419 unsigned short relpath_len = *(unsigned short *) (cp + len);
2420 if (cp + len + 2 + relpath_len < buf + fsi.EndOfFile.LowPart)
2422 cp += len + 2 + relpath_len;
2423 len = *(unsigned short *) cp;
2424 cp += 2;
2426 if (*(PWCHAR) cp == 0xfeff) /* BOM */
2428 char *tmpbuf = tp.c_get ();
2429 if (sys_wcstombs (tmpbuf, NT_MAX_PATH, (PWCHAR) (cp + 2))
2430 > SYMLINK_MAX)
2431 return 0;
2432 res = posixify (tmpbuf);
2434 else if (len > SYMLINK_MAX)
2435 return 0;
2436 else
2438 cp[len] = '\0';
2439 res = posixify (cp);
2442 if (res) /* It's a symlink. */
2443 path_flags (path_flags () | PATH_SYMLINK | PATH_LNK);
2444 return res;
2448 symlink_info::check_sysfile (HANDLE h)
2450 tmp_pathbuf tp;
2451 char cookie_buf[sizeof (SYMLINK_COOKIE) - 1];
2452 char *srcbuf = tp.c_get ();
2453 int res = 0;
2454 NTSTATUS status;
2455 IO_STATUS_BLOCK io;
2456 bool interix_symlink = false;
2457 LARGE_INTEGER off = { QuadPart:0LL };
2459 status = NtReadFile (h, NULL, NULL, NULL, &io, cookie_buf,
2460 sizeof (cookie_buf), &off, NULL);
2461 if (!NT_SUCCESS (status))
2463 debug_printf ("ReadFile1 failed %y", status);
2464 if (status != STATUS_END_OF_FILE)
2465 set_error (EIO);
2466 return 0;
2468 off.QuadPart = io.Information;
2469 if (io.Information == sizeof (cookie_buf)
2470 && memcmp (cookie_buf, SYMLINK_COOKIE, sizeof (cookie_buf)) == 0)
2472 /* It's a symlink. */
2473 path_flags (path_flags () | PATH_SYMLINK);
2475 else if (io.Information == sizeof (cookie_buf)
2476 && memcmp (cookie_buf, SOCKET_COOKIE, sizeof (cookie_buf)) == 0)
2477 path_flags (path_flags () | PATH_SOCKET);
2478 else if (io.Information >= sizeof (INTERIX_SYMLINK_COOKIE)
2479 && memcmp (cookie_buf, INTERIX_SYMLINK_COOKIE,
2480 sizeof (INTERIX_SYMLINK_COOKIE) - 1) == 0)
2482 /* It's an Interix symlink. */
2483 path_flags (path_flags () | PATH_SYMLINK);
2484 interix_symlink = true;
2485 /* Interix symlink cookies are shorter than Cygwin symlink cookies, so
2486 in case of an Interix symlink cooky we have read too far into the
2487 file. Set file pointer back to the position right after the cookie. */
2488 off.QuadPart = sizeof (INTERIX_SYMLINK_COOKIE) - 1;
2490 if (path_flags () & PATH_SYMLINK)
2492 status = NtReadFile (h, NULL, NULL, NULL, &io, srcbuf,
2493 NT_MAX_PATH, &off, NULL);
2494 if (!NT_SUCCESS (status))
2496 debug_printf ("ReadFile2 failed");
2497 if (status != STATUS_END_OF_FILE)
2498 set_error (EIO);
2500 else if (*(PWCHAR) srcbuf == 0xfeff /* BOM */
2501 || interix_symlink)
2503 /* Add trailing 0 to Interix symlink target. Skip BOM in Cygwin
2504 symlinks. */
2505 if (interix_symlink)
2506 ((PWCHAR) srcbuf)[io.Information / sizeof (WCHAR)] = L'\0';
2507 else
2508 srcbuf += 2;
2509 char *tmpbuf = tp.c_get ();
2510 if (sys_wcstombs (tmpbuf, NT_MAX_PATH, (PWCHAR) srcbuf)
2511 > SYMLINK_MAX)
2512 debug_printf ("symlink string too long");
2513 else
2514 res = posixify (tmpbuf);
2516 else if (io.Information > SYMLINK_MAX + 1)
2517 debug_printf ("symlink string too long");
2518 else
2519 res = posixify (srcbuf);
2521 return res;
2524 typedef struct _REPARSE_APPEXECLINK_BUFFER
2526 DWORD ReparseTag;
2527 WORD ReparseDataLength;
2528 WORD Reserved;
2529 struct {
2530 DWORD Version; /* Take member name with a grain of salt. */
2531 WCHAR Strings[1]; /* Four serialized, NUL-terminated WCHAR strings:
2532 - Package ID
2533 - Entry Point
2534 - Executable Path
2535 - Application Type
2536 We're only interested in the Executable Path */
2537 } AppExecLinkReparseBuffer;
2538 } REPARSE_APPEXECLINK_BUFFER,*PREPARSE_APPEXECLINK_BUFFER;
2540 static bool
2541 check_reparse_point_string (PUNICODE_STRING subst)
2543 /* Native mount points, or native non-relative symbolic links,
2544 can be treated as posix symlinks only if the SubstituteName
2545 can be converted from a native NT object namespace name to
2546 a win32 name. We only know how to convert names with two
2547 prefixes :
2548 "\??\UNC\..."
2549 "\??\X:..."
2550 Other reparse points will be treated as files or
2551 directories, not as posix symlinks.
2553 if (RtlEqualUnicodePathPrefix (subst, &ro_u_natp, FALSE))
2555 if (subst->Length >= 6 * sizeof(WCHAR) && subst->Buffer[5] == L':' &&
2556 (subst->Length == 6 * sizeof(WCHAR) || subst->Buffer[6] == L'\\'))
2557 return true;
2558 else if (subst->Length >= 8 * sizeof(WCHAR) &&
2559 wcsncmp (subst->Buffer + 4, L"UNC\\", 4) == 0)
2560 return true;
2562 return false;
2565 /* Return values:
2566 <0: Negative errno.
2567 0: Not a reparse point recognized by us.
2568 >0: Path flags for a recognized reparse point, always including PATH_REP.
2571 check_reparse_point_target (HANDLE h, bool remote, PREPARSE_DATA_BUFFER rp,
2572 PUNICODE_STRING psymbuf)
2574 NTSTATUS status;
2575 IO_STATUS_BLOCK io;
2577 /* On remote drives or under heavy load, NtFsControlFile can return with
2578 STATUS_PENDING. If so, instead of creating an event object, just set
2579 io.Status to an invalid value and perform a minimal wait until io.Status
2580 changed. */
2581 memset (&io, 0xff, sizeof io);
2582 status = NtFsControlFile (h, NULL, NULL, NULL, &io,
2583 FSCTL_GET_REPARSE_POINT, NULL, 0, (LPVOID) rp,
2584 MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
2585 if (status == STATUS_PENDING)
2587 while (io.Status == (NTSTATUS) 0xffffffff)
2588 Sleep (1L);
2589 status = io.Status;
2591 if (!NT_SUCCESS (status))
2593 debug_printf ("NtFsControlFile(FSCTL_GET_REPARSE_POINT) failed, %y",
2594 status);
2595 /* When accessing the root dir of some remote drives (observed with
2596 OS X shares), the FILE_ATTRIBUTE_REPARSE_POINT flag is set, but
2597 the followup call to NtFsControlFile(FSCTL_GET_REPARSE_POINT)
2598 returns with STATUS_NOT_A_REPARSE_POINT. That's quite buggy, but
2599 we cope here with this scenario by not setting an error code. */
2600 if (status == STATUS_NOT_A_REPARSE_POINT)
2601 return 0;
2602 return -EIO;
2604 if (rp->ReparseTag == IO_REPARSE_TAG_SYMLINK)
2606 /* Windows evaluates native symlink literally. If a remote symlink
2607 points to, say, C:\foo, it will be handled as if the target is the
2608 local file C:\foo. That comes in handy since that's how symlinks
2609 are treated under POSIX as well. */
2610 RtlInitCountedUnicodeString (psymbuf,
2611 (PWCHAR)((PBYTE) rp->SymbolicLinkReparseBuffer.PathBuffer
2612 + rp->SymbolicLinkReparseBuffer.SubstituteNameOffset),
2613 rp->SymbolicLinkReparseBuffer.SubstituteNameLength);
2614 if ((rp->SymbolicLinkReparseBuffer.Flags & SYMLINK_FLAG_RELATIVE) ||
2615 check_reparse_point_string (psymbuf))
2616 return PATH_SYMLINK | PATH_REP;
2618 else if (!remote && rp->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
2620 /* Don't handle junctions on remote filesystems as symlinks. This type
2621 of reparse point is handled transparently by the OS so that the
2622 target of the junction is the remote directory it is supposed to
2623 point to. If we handle it as symlink, it will be mistreated as
2624 pointing to a dir on the local system. */
2625 RtlInitCountedUnicodeString (psymbuf,
2626 (PWCHAR)((PBYTE) rp->MountPointReparseBuffer.PathBuffer
2627 + rp->MountPointReparseBuffer.SubstituteNameOffset),
2628 rp->MountPointReparseBuffer.SubstituteNameLength);
2629 if (RtlEqualUnicodePathPrefix (psymbuf, &ro_u_volume, TRUE))
2631 /* Volume mount point. Not treated as symlink. The return
2632 value -EPERM is a hint for the caller to treat this as a
2633 volume mount point. */
2634 return -EPERM;
2636 if (check_reparse_point_string (psymbuf))
2637 return PATH_SYMLINK | PATH_REP;
2639 else if (!remote && rp->ReparseTag == IO_REPARSE_TAG_APPEXECLINK)
2641 /* App execution aliases are commonly used by Windows Store apps. */
2642 PREPARSE_APPEXECLINK_BUFFER rpl = (PREPARSE_APPEXECLINK_BUFFER) rp;
2643 WCHAR *buf = rpl->AppExecLinkReparseBuffer.Strings;
2644 DWORD size = rp->ReparseDataLength / sizeof (WCHAR), n;
2646 /* It seems that app execution aliases have a payload of four
2647 NUL-separated wide string: package id, entry point, executable
2648 and application type. We're interested in the executable. */
2649 for (int i = 0; i < 3 && size > 0; i++)
2651 n = wcsnlen (buf, size - 1);
2652 if (i == 2 && n > 0 && n < size)
2654 RtlInitCountedUnicodeString (psymbuf, buf, n * sizeof (WCHAR));
2655 return PATH_SYMLINK | PATH_REP;
2657 if (i == 2)
2658 break;
2659 buf += n + 1;
2660 size -= n + 1;
2663 else if (rp->ReparseTag == IO_REPARSE_TAG_LX_SYMLINK)
2665 /* WSL symlink. Problem: We have to convert the path to UTF-16 for
2666 the caller. Reparse points are 16K max. The buffer given to rp
2667 is 32K. So there's enough trailing space in the buffer to convert
2668 to UTF-16 and let psymbuf point to it. */
2669 PREPARSE_LX_SYMLINK_BUFFER rpl = (PREPARSE_LX_SYMLINK_BUFFER) rp;
2670 char *path_buf = rpl->LxSymlinkReparseBuffer.PathBuffer;
2671 DWORD path_len = rpl->ReparseDataLength - sizeof (DWORD);
2672 bool full_path = false;
2673 const size_t drv_prefix_len = strlen ("/mnt");
2674 PBYTE utf16_ptr;
2675 PWCHAR utf16_buf;
2676 int utf16_bufsize;
2678 /* 0-terminate path_buf for easier testing. */
2679 path_buf[path_len] = '\0';
2680 if (path_prefix_p ("/mnt", path_buf, drv_prefix_len, false))
2682 size_t len = strlen (path_buf);
2684 if (len <= drv_prefix_len + 1)
2686 /* /mnt or /mnt/. Replace with cygdrive prefix. */
2687 stpcpy (path_buf, mount_table->cygdrive);
2688 path_len = mount_table->cygdrive_len;
2689 if (len == drv_prefix_len)
2691 path_buf[mount_table->cygdrive_len - 1] = '\0';
2692 --path_len;
2694 rp->ReparseDataLength = path_len + sizeof (DWORD);
2696 else if (islower (path_buf[drv_prefix_len + 1])
2697 && (path_len == drv_prefix_len + 2
2698 || path_buf[drv_prefix_len + 2] == '/'))
2700 /* Skip forward to the slash leading the drive letter.
2701 That leaves room for adding the colon. */
2702 path_buf += drv_prefix_len;
2703 path_len -= drv_prefix_len;
2704 full_path = true;
2707 /* Compute buffer for path converted to UTF-16. */
2708 utf16_ptr = (PBYTE) rpl + sizeof (REPARSE_LX_SYMLINK_BUFFER)
2709 + rp->ReparseDataLength;
2710 /* Skip \0-termination added above. */
2711 ++utf16_ptr;
2712 /* Make sure pointer is aligned */
2713 while ((intptr_t) utf16_ptr % sizeof (WCHAR))
2714 ++utf16_ptr;
2715 utf16_buf = (PWCHAR) utf16_ptr;
2716 utf16_bufsize = NT_MAX_PATH - (utf16_buf - (PWCHAR) rpl);
2717 /* Now convert path to UTF-16. */
2718 utf16_bufsize = MultiByteToWideChar (CP_UTF8, 0, path_buf, path_len,
2719 utf16_buf, utf16_bufsize);
2720 if (utf16_bufsize)
2722 if (full_path)
2724 utf16_buf[0] = utf16_buf[1]; /* Move drive letter to front */
2725 utf16_buf[1] = L':'; /* Add colon */
2727 RtlInitCountedUnicodeString (psymbuf, utf16_buf,
2728 utf16_bufsize * sizeof (WCHAR));
2729 return PATH_SYMLINK | PATH_REP | PATH_REP_NOAPI;
2731 return -EIO;
2733 else if (rp->ReparseTag == IO_REPARSE_TAG_CYGUNIX)
2735 PREPARSE_GUID_DATA_BUFFER rgp = (PREPARSE_GUID_DATA_BUFFER) rp;
2737 if (memcmp (CYGWIN_SOCKET_GUID, &rgp->ReparseGuid, sizeof (GUID)) == 0)
2738 #ifdef __WITH_AF_UNIX
2739 return PATH_SOCKET | PATH_REP | PATH_REP_NOAPI;
2740 #else
2741 /* Recognize this as a reparse point but not as a socket. */
2742 return PATH_REP | PATH_REP_NOAPI;
2743 #endif
2745 else if (rp->ReparseTag == IO_REPARSE_TAG_AF_UNIX)
2746 /* Native Windows AF_UNIX socket; recognize this as a reparse
2747 point but not as a socket. */
2748 return PATH_REP;
2749 return 0;
2753 symlink_info::check_reparse_point (HANDLE h, bool remote)
2755 tmp_pathbuf tp;
2756 PREPARSE_DATA_BUFFER rp = (PREPARSE_DATA_BUFFER) tp.c_get ();
2757 UNICODE_STRING symbuf;
2758 char srcbuf[SYMLINK_MAX + 7];
2760 int ret = check_reparse_point_target (h, remote, rp, &symbuf);
2761 if (ret <= 0)
2763 if (ret == -EIO)
2765 set_error (EIO);
2766 return 0;
2768 /* Maybe it's a reparse point, but it's certainly not one we recognize.
2769 Drop REPARSE attribute so we don't try to use the flag accidentally.
2770 It's just some arbitrary file or directory for us. */
2771 fileattr (fileattr () & ~FILE_ATTRIBUTE_REPARSE_POINT);
2772 return ret;
2774 /* ret is > 0, so it's a known reparse point, path in symbuf. */
2775 path_flags (path_flags () | ret);
2776 if (ret & PATH_SYMLINK)
2778 sys_wcstombs (srcbuf, SYMLINK_MAX + 7, symbuf.Buffer,
2779 symbuf.Length / sizeof (WCHAR));
2780 /* A symlink is never a directory. */
2781 fileattr (fileattr () & ~FILE_ATTRIBUTE_DIRECTORY);
2782 return posixify (srcbuf);
2784 else
2785 return 0;
2789 symlink_info::check_nfs_symlink (HANDLE h)
2791 tmp_pathbuf tp;
2792 NTSTATUS status;
2793 IO_STATUS_BLOCK io;
2794 struct {
2795 FILE_GET_EA_INFORMATION fgei;
2796 char buf[sizeof (NFS_SYML_TARGET)];
2797 } fgei_buf;
2798 PFILE_FULL_EA_INFORMATION pffei;
2799 int res = 0;
2801 /* To find out if the file is a symlink and to get the symlink target,
2802 try to fetch the NfsSymlinkTargetName EA. */
2803 fgei_buf.fgei.NextEntryOffset = 0;
2804 fgei_buf.fgei.EaNameLength = sizeof (NFS_SYML_TARGET) - 1;
2805 stpcpy (fgei_buf.fgei.EaName, NFS_SYML_TARGET);
2806 pffei = (PFILE_FULL_EA_INFORMATION) tp.w_get ();
2807 status = NtQueryEaFile (h, &io, pffei, NT_MAX_PATH * sizeof (WCHAR), TRUE,
2808 &fgei_buf.fgei, sizeof fgei_buf, NULL, TRUE);
2809 if (NT_SUCCESS (status) && pffei->EaValueLength > 0)
2811 PWCHAR spath = (PWCHAR)
2812 (pffei->EaName + pffei->EaNameLength + 1);
2813 res = sys_wcstombs (contents, SYMLINK_MAX + 1,
2814 spath, pffei->EaValueLength);
2815 path_flags (path_flags () | PATH_SYMLINK);
2817 return res;
2821 symlink_info::posixify (char *srcbuf)
2823 /* The definition for a path in a native symlink is a bit weird. The Flags
2824 value seem to contain 0 for absolute paths (stored as NT native path)
2825 and 1 for relative paths. Relative paths are paths not starting with a
2826 drive letter. These are not converted to NT native, but stored as
2827 given. A path starting with a single backslash is relative to the
2828 current drive thus a "relative" value (Flags == 1).
2829 Funny enough it's possible to store paths with slashes instead of
2830 backslashes, but they are evaluated incorrectly by subsequent Windows
2831 calls like CreateFile (ERROR_INVALID_NAME). So, what we do here is to
2832 take paths starting with slashes at face value, evaluating them as
2833 Cygwin specific POSIX paths.
2834 A path starting with two slashes(!) or backslashes is converted into an
2835 NT UNC path. Unfortunately, in contrast to POSIX rules, paths starting
2836 with three or more (back)slashes are also converted into UNC paths,
2837 just incorrectly sticking to one redundant leading backslash. We go
2838 along with this behaviour to avoid scenarios in which native tools access
2839 other files than Cygwin.
2840 The above rules are used exactly the same way on Cygwin specific symlinks
2841 (sysfiles and shortcuts) to eliminate non-POSIX paths in the output. */
2843 /* Eliminate native NT prefixes. */
2844 if (srcbuf[0] == '\\' && !strncmp (srcbuf + 1, "??\\", 3))
2846 srcbuf += 4;
2847 if (srcbuf[1] != ':') /* native UNC path */
2848 *(srcbuf += 2) = '\\';
2850 if (isdrive (srcbuf))
2851 mount_table->conv_to_posix_path (srcbuf, contents, 0);
2852 else if (srcbuf[0] == '\\')
2854 if (srcbuf[1] == '\\') /* UNC path */
2855 slashify (srcbuf, contents, 0);
2856 else /* Paths starting with \ are current drive relative. */
2858 char cvtbuf[SYMLINK_MAX + 1];
2860 stpcpy (cvtbuf + cygheap->cwd.get_drive (cvtbuf), srcbuf);
2861 mount_table->conv_to_posix_path (cvtbuf, contents, 0);
2864 else /* Everything else is taken as is. */
2865 slashify (srcbuf, contents, 0);
2866 return strlen (contents);
2869 enum
2871 SCAN_BEG,
2872 SCAN_LNK,
2873 SCAN_HASLNK,
2874 SCAN_JUSTCHECK,
2875 SCAN_JUSTCHECKTHIS, /* Never try to append a suffix. */
2876 SCAN_APPENDLNK,
2877 SCAN_DONE,
2880 class suffix_scan
2882 const suffix_info *suffixes, *suffixes_start;
2883 int nextstate;
2884 char *eopath;
2885 public:
2886 const char *path;
2887 char *has (const char *, const suffix_info *);
2888 int next ();
2889 int lnk_match () {return nextstate >= SCAN_APPENDLNK;}
2892 char *
2893 suffix_scan::has (const char *in_path, const suffix_info *in_suffixes)
2895 nextstate = SCAN_BEG;
2896 suffixes = suffixes_start = in_suffixes;
2898 const char *fname = strrchr (in_path, '\\');
2899 fname = fname ? fname + 1 : in_path;
2900 char *ext_here = strrchr (fname, '.');
2901 path = in_path;
2902 eopath = strchr (path, '\0');
2904 if (!ext_here)
2905 goto noext;
2907 if (suffixes)
2909 /* Check if the extension matches a known extension */
2910 for (const suffix_info *ex = in_suffixes; ex->name != NULL; ex++)
2911 if (ascii_strcasematch (ext_here, ex->name))
2913 nextstate = SCAN_JUSTCHECK;
2914 suffixes = NULL; /* Has an extension so don't scan for one. */
2915 goto done;
2919 /* Didn't match. Use last resort -- .lnk. */
2920 if (ascii_strcasematch (ext_here, ".lnk"))
2922 nextstate = SCAN_HASLNK;
2923 suffixes = NULL;
2926 noext:
2927 ext_here = eopath;
2929 done:
2930 size_t namelen = eopath - fname;
2931 /* Avoid attaching suffixes if the resulting filename would be invalid.
2932 For performance reasons we don't check the length of a suffix, since
2933 we know that all suffixes are 4 chars in length.
2935 FIXME: This is not really correct. A fully functional test should
2936 work on wide character paths. This would probably also speed
2937 up symlink_info::check. */
2938 if (namelen > NAME_MAX - 4)
2940 if (namelen > NAME_MAX)
2941 return NULL;
2942 nextstate = SCAN_JUSTCHECKTHIS;
2943 suffixes = NULL;
2945 return ext_here;
2949 suffix_scan::next ()
2951 for (;;)
2953 if (!suffixes)
2954 switch (nextstate)
2956 case SCAN_BEG:
2957 suffixes = suffixes_start;
2958 if (!suffixes)
2960 nextstate = SCAN_LNK;
2961 return 1;
2963 nextstate = SCAN_DONE;
2964 *eopath = '\0';
2965 return 0;
2966 case SCAN_HASLNK:
2967 nextstate = SCAN_APPENDLNK; /* Skip SCAN_BEG */
2968 return 1;
2969 case SCAN_JUSTCHECK:
2970 nextstate = SCAN_LNK;
2971 return 1;
2972 case SCAN_JUSTCHECKTHIS:
2973 nextstate = SCAN_DONE;
2974 return 1;
2975 case SCAN_LNK:
2976 case SCAN_APPENDLNK:
2977 nextstate = SCAN_DONE;
2978 strcat (eopath, ".lnk");
2979 return 1;
2980 default:
2981 *eopath = '\0';
2982 return 0;
2985 if (suffixes && suffixes->name)
2987 strcpy (eopath, suffixes->name);
2988 suffixes++;
2989 return 1;
2991 suffixes = NULL;
2992 if (nextstate == SCAN_BEG)
2994 nextstate = SCAN_LNK;
2995 *eopath = '\0';
3000 bool
3001 symlink_info::set_error (int in_errno)
3003 bool res;
3004 if (!(pc_flags () & PC_NO_ACCESS_CHECK)
3005 || in_errno == ENAMETOOLONG || in_errno == EIO)
3007 error (in_errno);
3008 res = true;
3010 else if (in_errno == ENOENT)
3011 res = true;
3012 else
3014 fileattr (FILE_ATTRIBUTE_NORMAL);
3015 res = false;
3017 return res;
3020 bool
3021 symlink_info::parse_device (const char *contents)
3023 char *endptr;
3024 _major_t mymajor;
3025 _major_t myminor;
3026 __mode_t mymode;
3028 mymajor = strtol (contents += 2, &endptr, 16);
3029 if (endptr == contents)
3030 return false;
3032 contents = endptr;
3033 myminor = strtol (++contents, &endptr, 16);
3034 if (endptr == contents)
3035 return false;
3037 contents = endptr;
3038 mymode = strtol (++contents, &endptr, 16);
3039 if (endptr == contents)
3040 return false;
3042 if ((mymode & S_IFMT) == S_IFIFO)
3043 dev (FH_FIFO);
3044 else
3045 dev (FHDEV (mymajor, myminor));
3046 mode (mymode);
3047 return true;
3050 /* Probably we have a virtual drive input path and the resulting full path
3051 starts with the substitution. Retrieve the target path of the virtual
3052 drive and try to revert what GetFinalPathNameByHandleW did to the
3053 drive letter. */
3054 static bool
3055 revert_virtual_drive (PUNICODE_STRING upath, PUNICODE_STRING fpath,
3056 bool is_remote, ULONG ci_flag)
3058 /* Get the drive's target path. */
3059 WCHAR drive[3] = {(WCHAR) towupper (upath->Buffer[4]), L':', L'\0'};
3060 WCHAR target[MAX_PATH];
3061 UNICODE_STRING tpath;
3062 WCHAR *p;
3064 DWORD remlen = QueryDosDeviceW (drive, target, MAX_PATH);
3065 if (remlen < 3)
3066 return false;
3067 remlen -= 2; /* Two L'\0' */
3069 if (target[remlen - 1] == L'\\')
3070 remlen--;
3071 RtlInitCountedUnicodeString (&tpath, target, remlen * sizeof (WCHAR));
3073 const USHORT uncp_len = is_remote ? ro_u_uncp.Length / sizeof (WCHAR) - 1 : 0;
3075 if (is_remote)
3077 /* target path starts with \??\UNC\. */
3078 if (RtlEqualUnicodePathPrefix (&tpath, &ro_u_uncp, TRUE))
3080 remlen -= uncp_len;
3081 p = target + uncp_len;
3083 /* target path starts with \Device\<redirector>. */
3084 else if ((p = wcschr (target, L';'))
3085 && p + 3 < target + remlen
3086 && wcsncmp (p + 1, drive, 2) == 0
3087 && (p = wcschr (p + 3, L'\\')))
3088 remlen -= p - target;
3089 else
3090 return false;
3091 if (wcsncasecmp (fpath->Buffer + uncp_len, p, remlen))
3092 return false;
3094 else if (!RtlEqualUnicodePathPrefix (fpath, &tpath, TRUE))
3095 return false;
3096 /* Replace fpath with source drive letter and append reminder of
3097 final path after skipping target path */
3098 fpath->Buffer[4] = drive[0]; /* Drive letter */
3099 fpath->Buffer[5] = L':';
3100 WCHAR *to = fpath->Buffer + 6; /* Next to L':' */
3101 WCHAR *from = fpath->Buffer + uncp_len + remlen;
3102 memmove (to, from, (wcslen (from) + 1) * sizeof (WCHAR));
3103 fpath->Length -= (from - to) * sizeof (WCHAR);
3104 if (RtlEqualUnicodeString (upath, fpath, !!ci_flag))
3105 return false;
3106 return true;
3109 /* Check if PATH is a symlink. PATH must be a valid Win32 path name.
3111 If PATH is a symlink, put the value of the symlink--the file to
3112 which it points--into CONTENTS.
3114 Set PATH_SYMLINK if PATH is a symlink.
3116 If PATH is a symlink, return the length stored into CONTENTS. If
3117 the inner components of PATH contain native symlinks or junctions,
3118 or if the drive is a virtual drive, compare PATH with the result
3119 returned by GetFinalPathNameByHandleA. If they differ, store the
3120 final path in CONTENTS and return the negative of its length. In
3121 all other cases, return 0. */
3124 symlink_info::check (char *path, const suffix_info *suffixes, fs_info &fs,
3125 path_conv_handle &conv_hdl)
3127 int res;
3128 HANDLE h;
3129 NTSTATUS status;
3130 UNICODE_STRING upath;
3131 OBJECT_ATTRIBUTES attr;
3132 IO_STATUS_BLOCK io;
3133 suffix_scan suffix;
3134 bool had_ext;
3136 const ULONG ci_flag = cygwin_shared->obcaseinsensitive
3137 || (mount_flags () & MOUNT_NOPOSIX)
3138 ? OBJ_CASE_INSENSITIVE : 0;
3139 /* TODO: Temporarily do all char->UNICODE conversion here. This should
3140 already be slightly faster than using Ascii functions. */
3141 tmp_pathbuf tp;
3142 tp.u_get (&upath);
3143 InitializeObjectAttributes (&attr, &upath, ci_flag, NULL, NULL);
3145 /* This label is used in case we encounter a FS which only handles
3146 DOS paths. See below. */
3147 bool restarted = false;
3148 restart:
3150 reset ();
3151 h = NULL;
3152 res = 0;
3154 PVOID eabuf = &nfs_aol_ffei;
3155 ULONG easize = sizeof nfs_aol_ffei;
3157 ext_here = suffix.has (path, suffixes);
3158 /* If the filename is too long, don't even try. */
3159 if (!ext_here)
3161 set_error (ENAMETOOLONG);
3162 goto file_not_symlink;
3165 had_ext = !!*ext_here;
3166 path_len (ext_here - path);
3168 while (suffix.next ())
3170 res = 0;
3171 error (0);
3172 get_nt_native_path (suffix.path, upath, mount_flags () & MOUNT_DOS);
3173 if (h)
3175 NtClose (h);
3176 h = NULL;
3178 /* The EA given to NtCreateFile allows to get a handle to a symlink on
3179 an NFS share, rather than getting a handle to the target of the
3180 symlink (which would spoil the task of this method quite a bit).
3181 Fortunately it's ignored on most other file systems so we don't have
3182 to special case NFS too much. */
3183 status = NtCreateFile (&h,
3184 READ_CONTROL | FILE_READ_ATTRIBUTES | FILE_READ_EA,
3185 &attr, &io, NULL, 0, FILE_SHARE_VALID_FLAGS,
3186 FILE_OPEN,
3187 FILE_OPEN_REPARSE_POINT
3188 | FILE_OPEN_FOR_BACKUP_INTENT,
3189 eabuf, easize);
3190 debug_printf ("%y = NtCreateFile (%S)", status, &upath);
3191 /* No right to access EAs or EAs not supported? */
3192 if (!NT_SUCCESS (status)
3193 && (status == STATUS_ACCESS_DENIED
3194 || status == STATUS_EAS_NOT_SUPPORTED
3195 || status == STATUS_NOT_SUPPORTED
3196 || status == STATUS_INVALID_NETWORK_RESPONSE
3197 /* Or a bug in Samba 3.2.x (x <= 7) when accessing a share's
3198 root dir which has EAs enabled? */
3199 || status == STATUS_INVALID_PARAMETER))
3201 /* If EAs are not supported, there's no sense to check them again
3202 with suffixes attached. So we set eabuf/easize to 0 here once. */
3203 if (status == STATUS_EAS_NOT_SUPPORTED
3204 || status == STATUS_NOT_SUPPORTED)
3206 eabuf = NULL;
3207 easize = 0;
3209 status = NtOpenFile (&h, READ_CONTROL | FILE_READ_ATTRIBUTES,
3210 &attr, &io, FILE_SHARE_VALID_FLAGS,
3211 FILE_OPEN_REPARSE_POINT
3212 | FILE_OPEN_FOR_BACKUP_INTENT);
3213 debug_printf ("%y = NtOpenFile (no-EAs %S)", status, &upath);
3215 if (status == STATUS_OBJECT_NAME_NOT_FOUND)
3217 /* There are filesystems out in the wild (Netapp, NWFS, and others)
3218 which are uncapable of generating pathnames outside the Win32
3219 rules. That means, filenames on these FSes must not have a
3220 leading space or trailing dots and spaces. This code snippet
3221 manages them. I really hope it's streamlined enough not to
3222 slow down normal operation. This extra check only kicks in if
3223 we encountered a STATUS_OBJECT_NAME_NOT_FOUND *and* we didn't
3224 already attach a suffix. */
3225 if (!restarted && !*ext_here && ext_here[-1] != '\\'
3226 && !(mount_flags () & MOUNT_DOS))
3228 /* Check for trailing dot or space or leading space in
3229 last component. */
3230 char *p = ext_here - 1;
3231 if (*p != '.' && *p != ' ')
3233 while (*--p != '\\')
3234 assert(p >= path);
3235 if (*++p != ' ')
3236 p = NULL;
3238 if (p)
3240 /* If so, check if file resides on one of the known broken
3241 FSes only supporting filenames following DOS rules. */
3242 fs.update (&upath, NULL);
3243 if (fs.has_dos_filenames_only ())
3245 /* If so, try again. Since we now know the FS, the
3246 filenames will be tweaked to follow DOS rules via the
3247 third parameter in the call to get_nt_native_path. */
3248 mount_flags (mount_flags () | MOUNT_DOS);
3249 restarted = true;
3250 goto restart;
3255 else if (status == STATUS_NETWORK_OPEN_RESTRICTION
3256 || status == STATUS_SYMLINK_CLASS_DISABLED)
3258 /* These status codes are returned if you try to open a native
3259 symlink and the usage of this kind of symlink is forbidden
3260 (see fsutil). Since we can't open them at all, not even for
3261 stat purposes, we have to return a POSIX error code which is
3262 at least a bit helpful.
3264 Additionally Windows 8 introduces a bug in NFS: If you have
3265 a symlink to a directory, with symlinks underneath, resolving
3266 the second level of symlinks fails if remote->remote symlinks
3267 are disabled in fsutil. Unfortunately that's the default. */
3268 set_error (ELOOP);
3269 break;
3272 if (NT_SUCCESS (status)
3273 /* Check file system while we're having the file open anyway.
3274 This speeds up path_conv noticably (~10%). */
3275 && (fs.inited () || fs.update (&upath, h)))
3277 status = conv_hdl.get_finfo (h, fs.is_nfs ());
3278 if (NT_SUCCESS (status))
3279 fileattr (conv_hdl.get_dosattr (h, fs.is_nfs ()));
3281 if (!NT_SUCCESS (status))
3283 debug_printf ("%y = NtQueryInformationFile (%S)", status, &upath);
3284 fileattr (INVALID_FILE_ATTRIBUTES);
3286 /* One of the inner path components is invalid, or the path contains
3287 invalid characters. Bail out with ENOENT.
3289 STATUS_IO_REPARSE_TAG_NOT_HANDLED is returned when trying to
3290 traversing a WSL symlink. For all practical purposes it's
3291 equivalent to traversing SYSTEM- or LNK-type symlink returning
3292 STATUS_OBJECT_PATH_NOT_FOUND.
3294 Note that additional STATUS_OBJECT_PATH_INVALID and
3295 STATUS_OBJECT_PATH_SYNTAX_BAD status codes exist. The first one
3296 is seemingly not generated by NtQueryInformationFile, the latter
3297 is only generated if the path is no absolute path within the
3298 NT name space, which should not happen and would point to an
3299 error in get_nt_native_path. Both status codes are deliberately
3300 not tested here unless proved necessary. */
3301 if (status == STATUS_OBJECT_PATH_NOT_FOUND
3302 || status == STATUS_IO_REPARSE_TAG_NOT_HANDLED
3303 || status == STATUS_OBJECT_NAME_INVALID
3304 || status == STATUS_BAD_NETWORK_PATH
3305 || status == STATUS_BAD_NETWORK_NAME
3306 || status == STATUS_NO_MEDIA_IN_DEVICE)
3308 set_error (ENOENT);
3309 if (ext_tacked_on () && !had_ext)
3311 *ext_here = '\0';
3312 ext_tacked_on (false);
3313 ext_here = NULL;
3314 path_len (0);
3316 goto file_not_symlink;
3318 if (status != STATUS_OBJECT_NAME_NOT_FOUND
3319 && status != STATUS_NO_SUCH_FILE) /* ENOENT on NFS or 9x share */
3321 /* The file exists, but the user can't access it for one reason
3322 or the other. To get the file attributes we try to access the
3323 information by opening the parent directory and getting the
3324 file attributes using a matching NtQueryDirectoryFile call. */
3325 UNICODE_STRING dirname, basename;
3326 OBJECT_ATTRIBUTES dattr;
3327 HANDLE dir;
3328 struct {
3329 FILE_ID_BOTH_DIR_INFORMATION fdi;
3330 WCHAR dummy_buf[NAME_MAX + 1];
3331 } fdi_buf;
3333 RtlSplitUnicodePath (&upath, &dirname, &basename);
3334 InitializeObjectAttributes (&dattr, &dirname, ci_flag,
3335 NULL, NULL);
3336 status = NtOpenFile (&dir, SYNCHRONIZE | FILE_LIST_DIRECTORY,
3337 &dattr, &io, FILE_SHARE_VALID_FLAGS,
3338 FILE_SYNCHRONOUS_IO_NONALERT
3339 | FILE_OPEN_FOR_BACKUP_INTENT
3340 | FILE_DIRECTORY_FILE);
3341 if (!NT_SUCCESS (status))
3343 debug_printf ("%y = NtOpenFile(%S)", status, &dirname);
3344 /* There's a special case if the file is itself the root
3345 of a drive which is not accessible by the current user.
3346 This case is only recognized by the length of the
3347 basename part. If it's 0, the incoming file is the
3348 root of a drive. So we at least know it's a directory. */
3349 if (basename.Length)
3350 fileattr (FILE_ATTRIBUTE_DIRECTORY);
3351 else
3353 fileattr (0);
3354 set_error (geterrno_from_nt_status (status));
3357 else
3359 status = NtQueryDirectoryFile (dir, NULL, NULL, NULL, &io,
3360 &fdi_buf, sizeof fdi_buf,
3361 FileIdBothDirectoryInformation,
3362 TRUE, &basename, TRUE);
3363 /* Take the opportunity to check file system while we're
3364 having the handle to the parent dir. */
3365 fs.update (&upath, dir);
3366 NtClose (dir);
3367 if (!NT_SUCCESS (status))
3369 debug_printf ("%y = NtQueryDirectoryFile(%S)",
3370 status, &dirname);
3371 if (status == STATUS_NO_SUCH_FILE)
3373 /* This can happen when trying to access files
3374 which match DOS device names on SMB shares.
3375 NtOpenFile failed with STATUS_ACCESS_DENIED,
3376 but the NtQueryDirectoryFile tells us the
3377 file doesn't exist. We're suspicious in this
3378 case and retry with the next suffix instead of
3379 just giving up. */
3380 set_error (ENOENT);
3381 continue;
3383 fileattr (0);
3385 else
3387 PFILE_ALL_INFORMATION pfai = conv_hdl.fai ();
3389 fileattr (fdi_buf.fdi.FileAttributes);
3390 memcpy (&pfai->BasicInformation.CreationTime,
3391 &fdi_buf.fdi.CreationTime,
3392 4 * sizeof (LARGE_INTEGER));
3393 pfai->BasicInformation.FileAttributes = fileattr ();
3394 pfai->StandardInformation.AllocationSize.QuadPart
3395 = fdi_buf.fdi.AllocationSize.QuadPart;
3396 pfai->StandardInformation.EndOfFile.QuadPart
3397 = fdi_buf.fdi.EndOfFile.QuadPart;
3398 pfai->StandardInformation.NumberOfLinks = 1;
3399 pfai->InternalInformation.IndexNumber.QuadPart
3400 = fdi_buf.fdi.FileId.QuadPart;
3403 ext_tacked_on (!!*ext_here);
3404 goto file_not_symlink;
3406 set_error (ENOENT);
3407 continue;
3410 ext_tacked_on (!!*ext_here);
3411 /* Don't allow to returns directories with appended suffix. If we found
3412 a directory with a suffix which has been appended here, then this
3413 directory doesn't match the request. So, just do as usual if file
3414 hasn't been found. */
3415 if (ext_tacked_on () && !had_ext
3416 && (fileattr () & FILE_ATTRIBUTE_DIRECTORY))
3418 fileattr (INVALID_FILE_ATTRIBUTES);
3419 set_error (ENOENT);
3420 continue;
3423 /* Consider the situation where a virtual drive points to a native
3424 symlink. Opening the virtual drive with FILE_OPEN_REPARSE_POINT
3425 actually opens the symlink. If this symlink points to another
3426 directory using a relative path, symlink evaluation goes totally
3427 awry. We never want a virtual drive evaluated as symlink. */
3428 if (upath.Length <= 14)
3429 goto file_not_symlink;
3431 /* Reparse points are potentially symlinks. This check must be
3432 performed before checking the SYSTEM attribute for sysfile
3433 symlinks, since reparse points can have this flag set, too. */
3434 if ((fileattr () & FILE_ATTRIBUTE_REPARSE_POINT))
3436 res = check_reparse_point (h, fs.is_remote_drive ());
3437 if (res > 0)
3439 /* A symlink is never a directory. */
3440 conv_hdl.fai ()->BasicInformation.FileAttributes
3441 &= ~FILE_ATTRIBUTE_DIRECTORY;
3442 break;
3444 else if (res == 0 && (path_flags () & PATH_REP))
3445 /* Known reparse point but not a symlink. */
3446 goto file_not_symlink;
3447 else
3449 /* Volume moint point or unrecognized reparse point type.
3450 Make sure the open handle is not used in later stat calls.
3451 The handle has been opened with the FILE_OPEN_REPARSE_POINT
3452 flag, so it's a handle to the reparse point, not a handle
3453 to the volumes root dir. */
3454 pc_flags (pc_flags () & ~PC_KEEP_HANDLE);
3455 /* Volume mount point: The filesystem information for the top
3456 level directory should be for the volume top level directory,
3457 rather than for the reparse point itself. So we fetch the
3458 filesystem information again, but with a NULL handle.
3459 This does what we want because fs_info::update opens the
3460 handle without FILE_OPEN_REPARSE_POINT. */
3461 if (res < 0)
3462 fs.update (&upath, NULL);
3466 /* Windows shortcuts are potentially treated as symlinks. Valid Cygwin
3467 & U/WIN shortcuts are R/O, but definitely not directories. */
3468 else if ((fileattr () & (FILE_ATTRIBUTE_READONLY
3469 | FILE_ATTRIBUTE_DIRECTORY))
3470 == FILE_ATTRIBUTE_READONLY
3471 && suffix.lnk_match ())
3473 HANDLE sym_h;
3475 status = NtOpenFile (&sym_h, SYNCHRONIZE | GENERIC_READ, &attr, &io,
3476 FILE_SHARE_VALID_FLAGS,
3477 FILE_OPEN_FOR_BACKUP_INTENT
3478 | FILE_SYNCHRONOUS_IO_NONALERT);
3479 if (!NT_SUCCESS (status))
3480 res = 0;
3481 else
3483 res = check_shortcut (sym_h);
3484 NtClose (sym_h);
3486 if (!res)
3488 /* If searching for `foo' and then finding a `foo.lnk' which
3489 is no shortcut, return the same as if file not found. */
3490 if (ext_tacked_on ())
3492 fileattr (INVALID_FILE_ATTRIBUTES);
3493 set_error (ENOENT);
3494 continue;
3497 else if (contents[0] != ':' || contents[1] != '\\'
3498 || !parse_device (contents))
3499 break;
3500 if (fs.is_nfs () && dev () == FH_FIFO)
3502 conv_hdl.nfsattr ()->type = NF3FIFO;
3503 conv_hdl.nfsattr ()->mode = mode ();
3504 conv_hdl.nfsattr ()->size = 0;
3505 /* Marker for fhandler_base::fstat_by_nfs_ea not to override
3506 the cached fattr3 data with fresh data from the filesystem,
3507 even if the handle is used for other purposes than stat. */
3508 conv_hdl.nfsattr ()->filler1 = NF3FIFO;
3512 /* If searching for `foo' and then finding a `foo.lnk' which is
3513 no shortcut, return the same as if file not found. */
3514 else if (suffix.lnk_match () && ext_tacked_on ())
3516 fileattr (INVALID_FILE_ATTRIBUTES);
3517 set_error (ENOENT);
3518 continue;
3521 /* This is the old Cygwin method creating symlinks. A symlink will
3522 have the `system' file attribute. Only files can be symlinks
3523 (which can be symlinks to directories). */
3524 else if ((fileattr () & (FILE_ATTRIBUTE_SYSTEM
3525 | FILE_ATTRIBUTE_DIRECTORY))
3526 == FILE_ATTRIBUTE_SYSTEM)
3528 HANDLE sym_h;
3530 status = NtOpenFile (&sym_h, SYNCHRONIZE | GENERIC_READ, &attr, &io,
3531 FILE_SHARE_VALID_FLAGS,
3532 FILE_OPEN_FOR_BACKUP_INTENT
3533 | FILE_SYNCHRONOUS_IO_NONALERT);
3535 if (!NT_SUCCESS (status))
3536 res = 0;
3537 else
3539 res = check_sysfile (sym_h);
3540 NtClose (sym_h);
3542 if (res)
3543 break;
3546 /* If the file is on an NFS share and could be opened with extended
3547 attributes, check if it's a symlink or FIFO. */
3548 else if (fs.is_nfs ())
3550 /* Make sure filler1 is 0, so we can use it safely as a marker. */
3551 conv_hdl.nfsattr ()->filler1 = 0;
3552 switch (conv_hdl.nfsattr ()->type & 7)
3554 case NF3LNK:
3555 res = check_nfs_symlink (h);
3556 break;
3557 case NF3FIFO:
3558 /* Enable real FIFOs recognized as such. */
3559 dev (FH_FIFO);
3560 mode (S_IFIFO | (conv_hdl.nfsattr ()->mode & ~S_IFMT));
3561 break;
3562 default:
3563 break;
3565 if (res)
3566 break;
3569 /* Check if the inner path components contain native symlinks or
3570 junctions, or if the drive is a virtual drive. Compare incoming
3571 path with path returned by GetFinalPathNameByHandleA. If they
3572 differ, return the final path as symlink content and set symlen
3573 to a negative value. This forces path_conv::check to restart
3574 symlink evaluation with the new path. */
3575 if ((pc_flags () & (PC_SYM_FOLLOW | PC_SYM_NOFOLLOW_REP))
3576 == PC_SYM_FOLLOW)
3578 PWCHAR fpbuf = tp.w_get ();
3579 DWORD ret;
3581 ret = GetFinalPathNameByHandleW (h, fpbuf, NT_MAX_PATH, 0);
3582 if (ret)
3584 UNICODE_STRING fpath;
3586 /* If incoming path has no trailing backslash, but final path
3587 has one, drop trailing backslash from final path so the
3588 below string comparison has a chance to succeed.
3589 On the contrary, if incoming path has trailing backslash,
3590 but final path does not have one, add trailing backslash
3591 to the final path. */
3592 if (upath.Buffer[(upath.Length - 1) / sizeof (WCHAR)] != L'\\'
3593 && fpbuf[ret - 1] == L'\\')
3594 fpbuf[--ret] = L'\0';
3595 if (upath.Buffer[(upath.Length - 1) / sizeof (WCHAR)] == L'\\'
3596 && fpbuf[ret - 1] != L'\\' && ret < NT_MAX_PATH - 1)
3598 fpbuf[ret++] = L'\\';
3599 fpbuf[ret] = L'\0';
3601 fpbuf[1] = L'?'; /* \\?\ --> \??\ */
3602 RtlInitCountedUnicodeString (&fpath, fpbuf, ret * sizeof (WCHAR));
3603 if (!RtlEqualUnicodeString (&upath, &fpath, !!ci_flag))
3605 /* If the incoming path is a local drive letter path... */
3606 if (!RtlEqualUnicodePathPrefix (&upath, &ro_u_uncp, TRUE))
3608 /* ...and the final path is an UNC path, revert to the
3609 drive letter path syntax. */
3610 if (RtlEqualUnicodePathPrefix (&fpath, &ro_u_uncp, TRUE))
3612 if (!revert_virtual_drive (&upath, &fpath, true,
3613 ci_flag))
3614 goto file_not_symlink;
3616 /* ...otherwise, if the final path changes the drive
3617 letter, let revert_virtual_drive check for a
3618 virtual drive and revert that. */
3619 else if (upath.Buffer[5] == L':'
3620 && (WCHAR) towupper (upath.Buffer[4])
3621 != (WCHAR) towupper (fpath.Buffer[4]))
3623 if (!revert_virtual_drive (&upath, &fpath, false,
3624 ci_flag))
3625 goto file_not_symlink;
3628 /* upath.Buffer is big enough and unused from this point on.
3629 Reuse it here, avoiding yet another buffer allocation. */
3630 char *nfpath = (char *) upath.Buffer;
3631 sys_wcstombs (nfpath, NT_MAX_PATH, fpbuf);
3632 /* For final paths differing in inner path components return
3633 length as negative value. This informs path_conv::check
3634 to skip realpath handling on the last path component. */
3635 res = -posixify (nfpath);
3636 break;
3641 /* Normal file. */
3642 file_not_symlink:
3643 issymlink (false);
3644 syscall_printf ("%s", isdevice () ? "is a device" : "not a symlink");
3645 res = 0;
3646 break;
3649 if (h)
3651 if (pc_flags () & PC_KEEP_HANDLE)
3652 conv_hdl.set (h);
3653 else
3654 NtClose (h);
3657 syscall_printf ("%d = symlink.check(%s, %p) (mount_flags %y, path_flags %y)",
3658 res, suffix.path, contents, mount_flags (), path_flags ());
3659 return res;
3662 /* "path" is the path in a virtual symlink. Set a symlink_info struct from
3663 that and proceed with further path checking afterwards. */
3665 symlink_info::set (char *path)
3667 strcpy (contents, path);
3668 mount_flags (0);
3669 path_flags (PATH_SYMLINK);
3670 fileattr (FILE_ATTRIBUTE_NORMAL);
3671 error (0);
3672 issymlink (true);
3673 ext_tacked_on (false);
3674 ext_here = NULL;
3675 path_len (0);
3676 dev (FH_NADA);
3677 return strlen (path);
3680 /* readlink system call */
3682 extern "C" ssize_t
3683 readlink (const char *__restrict path, char *__restrict buf, size_t buflen)
3685 if (buflen < 0)
3687 set_errno (ENAMETOOLONG);
3688 return -1;
3691 path_conv pathbuf (path, PC_SYM_CONTENTS, stat_suffixes);
3693 if (pathbuf.error)
3695 set_errno (pathbuf.error);
3696 syscall_printf ("-1 = readlink (%s, %p, %lu)", path, buf, buflen);
3697 return -1;
3700 if (!pathbuf.exists ())
3702 set_errno (ENOENT);
3703 return -1;
3706 if (!pathbuf.issymlink ())
3708 if (pathbuf.exists ())
3709 set_errno (EINVAL);
3710 return -1;
3713 size_t pathbuf_len = strlen (pathbuf.get_win32 ());
3714 ssize_t len = MIN (buflen, pathbuf_len);
3715 memcpy (buf, pathbuf.get_win32 (), len);
3717 /* errno set by symlink.check if error */
3718 return len;
3721 /* Some programs rely on st_dev/st_ino being unique for each file.
3722 Hash the path name and hope for the best. The hash arg is not
3723 always initialized to zero since readdir needs to compute the
3724 dirent ino_t based on a combination of the hash of the directory
3725 done during the opendir call and the hash or the filename within
3726 the directory. FIXME: Not bullet-proof. */
3727 /* Cygwin internal */
3728 ino_t
3729 hash_path_name (ino_t hash, PUNICODE_STRING name)
3731 if (name->Length == 0)
3732 return hash;
3734 /* Build up hash. Name is already normalized */
3735 USHORT len = name->Length / sizeof (WCHAR);
3736 for (USHORT idx = 0; idx < len; ++idx)
3737 hash = RtlUpcaseUnicodeChar (name->Buffer[idx])
3738 + (hash << 6) + (hash << 16) - hash;
3739 return hash;
3742 ino_t
3743 hash_path_name (ino_t hash, PCWSTR name)
3745 UNICODE_STRING uname;
3746 RtlInitUnicodeString (&uname, name);
3747 return hash_path_name (hash, &uname);
3750 ino_t
3751 hash_path_name (ino_t hash, const char *name)
3753 UNICODE_STRING uname;
3754 tmp_pathbuf tp;
3756 tp.u_get (&uname);
3757 sys_mbstouni (&uname, HEAP_NOTHEAP, name);
3758 ino_t ret = hash_path_name (hash, &uname);
3759 return ret;
3762 extern "C" char *
3763 getcwd (char *buf, size_t ulen)
3765 char* res = NULL;
3767 __try
3769 if (ulen == 0 && buf)
3770 set_errno (EINVAL);
3771 else
3772 res = cygheap->cwd.get (buf, 1, 1, ulen);
3774 __except (EFAULT) {}
3775 __endtry
3776 return res;
3779 /* getwd: Legacy. */
3780 extern "C" char *
3781 getwd (char *buf)
3783 return getcwd (buf, PATH_MAX + 1); /*Per SuSv3!*/
3786 extern "C" char *
3787 get_current_dir_name (void)
3789 const char *pwd = getenv ("PWD");
3790 char *cwd = getcwd (NULL, 0);
3791 struct stat pwdbuf, cwdbuf;
3793 if (pwd && strcmp (pwd, cwd) != 0
3794 && stat (pwd, &pwdbuf) == 0
3795 && stat (cwd, &cwdbuf) == 0
3796 && pwdbuf.st_dev == cwdbuf.st_dev
3797 && pwdbuf.st_ino == cwdbuf.st_ino)
3799 cwd = (char *) realloc (cwd, strlen (pwd) + 1);
3800 strcpy (cwd, pwd);
3803 return cwd;
3806 /* chdir: POSIX 5.2.1.1 */
3807 extern "C" int
3808 chdir (const char *in_dir)
3810 int res = -1;
3812 __try
3814 if (!*in_dir)
3816 set_errno (ENOENT);
3817 __leave;
3820 syscall_printf ("dir '%s'", in_dir);
3822 /* Convert path. PC_NONULLEMPTY ensures that we don't check for
3823 NULL/empty/invalid again. */
3824 path_conv path (in_dir, PC_SYM_FOLLOW | PC_POSIX | PC_NONULLEMPTY);
3825 if (path.error)
3827 set_errno (path.error);
3828 syscall_printf ("-1 = chdir (%s)", in_dir);
3829 __leave;
3832 const char *posix_cwd = NULL;
3833 dev_t devn = path.get_device ();
3834 if (!path.exists ())
3835 set_errno (ENOENT);
3836 else if (!path.isdir ())
3837 set_errno (ENOTDIR);
3838 else if (!isvirtual_dev (devn))
3840 /* The sequence chdir("xx"); chdir(".."); must be a noop if xx
3841 is not a symlink. This is exploited by find.exe.
3842 The posix_cwd is just path.get_posix ().
3843 In other cases we let cwd.set obtain the Posix path through
3844 the mount table. */
3845 if (!isdrive (path.get_posix ()))
3846 posix_cwd = path.get_posix ();
3847 res = 0;
3849 else
3851 posix_cwd = path.get_posix ();
3852 res = 0;
3855 if (!res)
3856 res = cygheap->cwd.set (&path, posix_cwd);
3858 /* Note that we're accessing cwd.posix without a lock here.
3859 I didn't think it was worth locking just for strace. */
3860 syscall_printf ("%R = chdir() cygheap->cwd.posix '%s' native '%S'", res,
3861 cygheap->cwd.get_posix (), path.get_nt_native_path ());
3863 __except (EFAULT)
3865 res = -1;
3867 __endtry
3868 return res;
3871 extern "C" int
3872 fchdir (int fd)
3874 int res;
3875 cygheap_fdget cfd (fd);
3876 if (cfd >= 0)
3877 res = chdir (cfd->get_name ());
3878 else
3879 res = -1;
3881 syscall_printf ("%R = fchdir(%d)", res, fd);
3882 return res;
3885 /******************** Exported Path Routines *********************/
3887 /* Cover functions to the path conversion routines.
3888 These are exported to the world as cygwin_foo by cygwin.din. */
3890 #define return_with_errno(x) \
3891 do {\
3892 int err = (x);\
3893 if (!err)\
3894 return 0;\
3895 set_errno (err);\
3896 return -1;\
3897 } while (0)
3899 extern "C" ssize_t
3900 cygwin_conv_path (cygwin_conv_path_t what, const void *from, void *to,
3901 size_t size)
3903 tmp_pathbuf tp;
3904 path_conv p;
3905 size_t lsiz = 0;
3906 char *buf = NULL;
3907 PWCHAR path = NULL;
3908 int error = 0;
3909 int how = what & CCP_CONVFLAGS_MASK;
3910 what &= CCP_CONVTYPE_MASK;
3911 int ret = -1;
3913 __try
3915 if (!from)
3917 set_errno (EINVAL);
3918 __leave;
3921 switch (what)
3923 case CCP_POSIX_TO_WIN_A:
3925 p.check ((const char *) from,
3926 PC_POSIX | PC_SYM_FOLLOW | PC_SYM_NOFOLLOW_REP
3927 | PC_NO_ACCESS_CHECK
3928 | ((how & CCP_RELATIVE) ? PC_NOFULL : 0), stat_suffixes);
3929 if (p.error)
3931 set_errno (p.error);
3932 __leave;
3934 PUNICODE_STRING up = p.get_nt_native_path ();
3935 buf = tp.c_get ();
3936 sys_wcstombs (buf, NT_MAX_PATH,
3937 up->Buffer, up->Length / sizeof (WCHAR));
3938 /* Convert native path to standard DOS path. */
3939 if (!strncmp (buf, "\\??\\", 4))
3941 buf += 4;
3942 if (buf[1] != ':') /* native UNC path */
3943 *(buf += 2) = '\\';
3945 else if (*buf == '\\')
3947 /* Device name points to somewhere else in the NT namespace.
3948 Use GLOBALROOT prefix to convert to Win32 path. */
3949 char *p = buf + sys_wcstombs (buf, NT_MAX_PATH,
3950 ro_u_globalroot.Buffer,
3951 ro_u_globalroot.Length
3952 / sizeof (WCHAR));
3953 sys_wcstombs (p, NT_MAX_PATH - (p - buf),
3954 up->Buffer, up->Length / sizeof (WCHAR));
3956 lsiz = strlen (buf) + 1;
3957 /* TODO: Incoming "." is a special case which leads to a trailing
3958 backslash ".\\" in the Win32 path. That's a result of the
3959 conversion in normalize_posix_path. This should not occur
3960 so the below code is just a band-aid. */
3961 if ((how & CCP_RELATIVE) && !strcmp ((const char *) from, ".")
3962 && !strcmp (buf, ".\\"))
3964 lsiz = 2;
3965 buf[1] = '\0';
3968 break;
3969 case CCP_POSIX_TO_WIN_W:
3970 p.check ((const char *) from,
3971 PC_POSIX | PC_SYM_FOLLOW | PC_SYM_NOFOLLOW_REP
3972 | PC_NO_ACCESS_CHECK
3973 | ((how & CCP_RELATIVE) ? PC_NOFULL : 0), stat_suffixes);
3974 if (p.error)
3976 set_errno (p.error);
3977 __leave;
3979 /* Relative Windows paths are always restricted to MAX_PATH chars. */
3980 if ((how & CCP_RELATIVE) && !isabspath (p.get_win32 ())
3981 && sys_mbstowcs (NULL, 0, p.get_win32 ()) > MAX_PATH)
3983 /* Recreate as absolute path. */
3984 p.check ((const char *) from, PC_POSIX | PC_SYM_FOLLOW
3985 | PC_NO_ACCESS_CHECK);
3986 if (p.error)
3988 set_errno (p.error);
3989 __leave;
3992 lsiz = p.get_wide_win32_path_len () + 1;
3993 path = p.get_nt_native_path ()->Buffer;
3995 /* Convert native path to standard DOS path. */
3996 if (!wcsncmp (path, L"\\??\\", 4))
3998 path[1] = L'\\';
4000 /* Drop long path prefix for short pathnames. Unfortunately there's
4001 quite a bunch of Win32 functions, especially in user32.dll,
4002 apparently, which don't grok long path names at all, not even
4003 in the UNICODE API. */
4004 if ((path[5] == L':' && lsiz <= MAX_PATH + 4)
4005 || (!wcsncmp (path + 4, L"UNC\\", 4) && lsiz <= MAX_PATH + 6))
4007 path += 4;
4008 lsiz -= 4;
4009 if (path[1] != L':')
4011 *(path += 2) = '\\';
4012 lsiz -= 2;
4016 else if (*path == L'\\')
4018 /* Device name points to somewhere else in the NT namespace.
4019 Use GLOBALROOT prefix to convert to Win32 path. */
4020 to = (void *) wcpcpy ((wchar_t *) to, ro_u_globalroot.Buffer);
4021 lsiz += ro_u_globalroot.Length / sizeof (WCHAR);
4023 /* TODO: Same ".\\" band-aid as in CCP_POSIX_TO_WIN_A case. */
4024 if ((how & CCP_RELATIVE) && !strcmp ((const char *) from, ".")
4025 && !wcscmp (path, L".\\"))
4027 lsiz = 2;
4028 path[1] = L'\0';
4030 lsiz *= sizeof (WCHAR);
4031 break;
4032 case CCP_WIN_A_TO_POSIX:
4033 buf = tp.c_get ();
4034 error = mount_table->conv_to_posix_path ((const char *) from, buf,
4035 how | __CCP_APP_SLASH);
4036 if (error)
4038 set_errno (p.error);
4039 __leave;
4041 lsiz = strlen (buf) + 1;
4042 break;
4043 case CCP_WIN_W_TO_POSIX:
4044 buf = tp.c_get ();
4045 error = mount_table->conv_to_posix_path ((const PWCHAR) from, buf,
4046 how | __CCP_APP_SLASH);
4047 if (error)
4049 set_errno (error);
4050 __leave;
4052 lsiz = strlen (buf) + 1;
4053 break;
4054 default:
4055 set_errno (EINVAL);
4056 __leave;
4058 if (!size)
4060 ret = lsiz;
4061 __leave;
4063 if (size < lsiz)
4065 set_errno (ENOSPC);
4066 __leave;
4068 switch (what)
4070 case CCP_POSIX_TO_WIN_A:
4071 case CCP_WIN_A_TO_POSIX:
4072 case CCP_WIN_W_TO_POSIX:
4073 stpcpy ((char *) to, buf);
4074 break;
4075 case CCP_POSIX_TO_WIN_W:
4076 wcpcpy ((PWCHAR) to, path);
4077 break;
4079 ret = 0;
4081 __except (EFAULT) {}
4082 __endtry
4083 return ret;
4086 extern "C" void *
4087 cygwin_create_path (cygwin_conv_path_t what, const void *from)
4089 void *to;
4090 ssize_t size = cygwin_conv_path (what, from, NULL, 0);
4091 if (size <= 0)
4092 to = NULL;
4093 else if (!(to = malloc (size)))
4094 to = NULL;
4095 if (cygwin_conv_path (what, from, to, size) == -1)
4097 free (to);
4098 to = NULL;
4100 return to;
4103 /* The realpath function is required by POSIX:2008. */
4105 extern "C" char *
4106 realpath (const char *__restrict path, char *__restrict resolved)
4108 tmp_pathbuf tp;
4109 char *tpath;
4111 /* Make sure the right errno is returned if path is NULL. */
4112 if (!path)
4114 set_errno (EINVAL);
4115 return NULL;
4118 /* Guard reading from a potentially invalid path and writing to a
4119 potentially invalid resolved. */
4120 __try
4122 /* Win32 drive letter paths and, generally, any path starting with a
4123 backslash, have to be converted to a POSIX path first, because
4124 path_conv leaves the incoming path untouched except for converting
4125 backslashes to forward slashes. This also covers '\\?\ and '\??\'
4126 path prefixes. */
4127 if (isdrive (path) || path[0] == '\\')
4129 tpath = tp.c_get ();
4130 mount_table->conv_to_posix_path (path, tpath, 0);
4132 else
4133 tpath = (char *) path;
4135 path_conv real_path (tpath, PC_SYM_FOLLOW | PC_POSIX, stat_suffixes);
4138 /* POSIX 2008 requires malloc'ing if resolved is NULL, and states
4139 that using non-NULL resolved is asking for portability
4140 problems. */
4142 if (!real_path.error && real_path.exists ())
4144 if (!resolved)
4146 resolved = (char *)
4147 malloc (strlen (real_path.get_posix ()) + 1);
4148 if (!resolved)
4149 return NULL;
4151 strcpy (resolved, real_path.get_posix ());
4152 return resolved;
4155 /* FIXME: on error, Linux puts the name of the path
4156 component which could not be resolved into RESOLVED, but POSIX
4157 does not require this. */
4158 if (resolved)
4159 resolved[0] = '\0';
4160 set_errno (real_path.error ?: ENOENT);
4162 __except (EFAULT) {}
4163 __endtry
4164 return NULL;
4167 /* Linux provides this extension. Since the only portable use of
4168 realpath requires a NULL second argument, we might as well have a
4169 one-argument wrapper. */
4170 extern "C" char *
4171 canonicalize_file_name (const char *path)
4173 return realpath (path, NULL);
4176 /* Return non-zero if path is a POSIX path list.
4177 This is exported to the world as cygwin_foo by cygwin.din.
4179 DOCTOOL-START
4180 <sect1 id="add-func-cygwin-posix-path-list-p">
4181 <para>Rather than use a mode to say what the "proper" path list
4182 format is, we allow any, and give apps the tools they need to
4183 convert between the two. If a ';' is present in the path list it's
4184 a Win32 path list. Otherwise, if the first path begins with
4185 [letter]: (in which case it can be the only element since if it
4186 wasn't a ';' would be present) it's a Win32 path list. Otherwise,
4187 it's a POSIX path list.</para>
4188 </sect1>
4189 DOCTOOL-END
4192 extern "C" int
4193 cygwin_posix_path_list_p (const char *path)
4195 int posix_p = !(strchr (path, ';') || isdrive (path));
4196 return posix_p;
4199 /* These are used for apps that need to convert env vars like PATH back and
4200 forth. The conversion is a two step process. First, an upper bound on the
4201 size of the buffer needed is computed. Then the conversion is done. This
4202 allows the caller to use alloca if it wants. */
4204 static int
4205 conv_path_list_buf_size (const char *path_list, bool to_posix)
4207 int i, num_elms, max_mount_path_len, size;
4208 const char *p;
4210 path_conv pc(".", PC_POSIX);
4211 /* The theory is that an upper bound is
4212 current_size + (num_elms * max_mount_path_len) */
4213 /* FIXME: This method is questionable in the long run. */
4215 unsigned nrel;
4216 char delim = to_posix ? ';' : ':';
4217 for (p = path_list, num_elms = nrel = 0; p; num_elms++)
4219 if (!isabspath (p))
4220 nrel++;
4221 p = strchr (++p, delim);
4224 /* 7: strlen ("//c") + slop, a conservative initial value */
4225 for (max_mount_path_len = sizeof ("/cygdrive/X"), i = 0;
4226 i < mount_table->nmounts; i++)
4228 int mount_len = (to_posix
4229 ? mount_table->mount[i].posix_pathlen
4230 : mount_table->mount[i].native_pathlen);
4231 if (max_mount_path_len < mount_len)
4232 max_mount_path_len = mount_len;
4235 /* 100: slop */
4236 size = strlen (path_list)
4237 + (num_elms * max_mount_path_len)
4238 + (nrel * strlen (to_posix ? pc.get_posix () : pc.get_win32 ()))
4239 + 100;
4241 return size;
4244 extern "C" ssize_t
4245 env_PATH_to_posix (const void *win32, void *posix, size_t size)
4247 return_with_errno (conv_path_list ((const char *) win32, (char *) posix,
4248 size, ENV_CVT));
4251 extern "C" ssize_t
4252 cygwin_conv_path_list (cygwin_conv_path_t what, const void *from, void *to,
4253 size_t size)
4255 int ret;
4256 char *winp = NULL;
4257 void *orig_to = NULL;
4258 tmp_pathbuf tp;
4260 switch (what & CCP_CONVTYPE_MASK)
4262 case CCP_WIN_W_TO_POSIX:
4263 if (!sys_wcstombs_alloc (&winp, HEAP_NOTHEAP, (const wchar_t *) from,
4264 (size_t) -1))
4265 return -1;
4266 what = (what & ~CCP_CONVTYPE_MASK) | CCP_WIN_A_TO_POSIX;
4267 from = (const void *) winp;
4268 break;
4269 case CCP_POSIX_TO_WIN_W:
4270 if (size == 0)
4271 return conv_path_list_buf_size ((const char *) from, 0)
4272 * sizeof (WCHAR);
4273 what = (what & ~CCP_CONVTYPE_MASK) | CCP_POSIX_TO_WIN_A;
4274 orig_to = to;
4275 to = (void *) tp.w_get ();
4276 size = 65536;
4277 break;
4279 switch (what & CCP_CONVTYPE_MASK)
4281 case CCP_WIN_A_TO_POSIX:
4282 case CCP_POSIX_TO_WIN_A:
4283 if (size == 0)
4284 return conv_path_list_buf_size ((const char *) from,
4285 what == CCP_WIN_A_TO_POSIX);
4286 ret = conv_path_list ((const char *) from, (char *) to, size, what);
4287 /* Free winp buffer in case of CCP_WIN_W_TO_POSIX. */
4288 if (winp)
4289 free (winp);
4290 /* Convert to WCHAR in case of CCP_POSIX_TO_WIN_W. */
4291 if (orig_to)
4292 sys_mbstowcs ((wchar_t *) orig_to, size / sizeof (WCHAR),
4293 (const char *) to, (size_t) -1);
4294 return_with_errno (ret);
4295 break;
4296 default:
4297 break;
4299 set_errno (EINVAL);
4300 return -1;
4303 /* cygwin_split_path: Split a path into directory and file name parts.
4304 Buffers DIR and FILE are assumed to be big enough.
4306 Examples (path -> `dir' / `file'):
4307 / -> `/' / `'
4308 "" -> `.' / `'
4309 . -> `.' / `.' (FIXME: should this be `.' / `'?)
4310 .. -> `.' / `..' (FIXME: should this be `..' / `'?)
4311 foo -> `.' / `foo'
4312 foo/bar -> `foo' / `bar'
4313 foo/bar/ -> `foo' / `bar'
4314 /foo -> `/' / `foo'
4315 /foo/bar -> `/foo' / `bar'
4316 c: -> `c:/' / `'
4317 c:/ -> `c:/' / `'
4318 c:foo -> `c:/' / `foo'
4319 c:/foo -> `c:/' / `foo'
4322 extern "C" void
4323 cygwin_split_path (const char *path, char *dir, char *file)
4325 int dir_started_p = 0;
4327 /* Deal with drives.
4328 Remember that c:foo <==> c:/foo. */
4329 if (isdrive (path))
4331 *dir++ = *path++;
4332 *dir++ = *path++;
4333 *dir++ = '/';
4334 if (!*path)
4336 *dir = 0;
4337 *file = 0;
4338 return;
4340 if (isdirsep (*path))
4341 ++path;
4342 dir_started_p = 1;
4345 /* Determine if there are trailing slashes and "delete" them if present.
4346 We pretend as if they don't exist. */
4347 const char *end = path + strlen (path);
4348 /* path + 1: keep leading slash. */
4349 while (end > path + 1 && isdirsep (end[-1]))
4350 --end;
4352 /* At this point, END points to one beyond the last character
4353 (with trailing slashes "deleted"). */
4355 /* Point LAST_SLASH at the last slash (duh...). */
4356 const char *last_slash;
4357 for (last_slash = end - 1; last_slash >= path; --last_slash)
4358 if (isdirsep (*last_slash))
4359 break;
4361 if (last_slash == path)
4363 *dir++ = '/';
4364 *dir = 0;
4366 else if (last_slash > path)
4368 memcpy (dir, path, last_slash - path);
4369 dir[last_slash - path] = 0;
4371 else
4373 if (dir_started_p)
4374 ; /* nothing to do */
4375 else
4376 *dir++ = '.';
4377 *dir = 0;
4380 memcpy (file, last_slash + 1, end - last_slash - 1);
4381 file[end - last_slash - 1] = 0;
4384 /*****************************************************************************/
4386 /* The find_fast_cwd_pointer function and parts of the
4387 cwdstuff::override_win32_cwd method are based on code using the
4388 following license:
4390 Copyright 2010 John Carey. All rights reserved.
4392 Redistribution and use in source and binary forms, with or without
4393 modification, are permitted provided that the following conditions
4394 are met:
4396 1. Redistributions of source code must retain the above
4397 copyright notice, this list of conditions and the following
4398 disclaimer.
4400 2. Redistributions in binary form must reproduce the above
4401 copyright notice, this list of conditions and the following
4402 disclaimer in the documentation and/or other materials provided
4403 with the distribution.
4405 THIS SOFTWARE IS PROVIDED BY JOHN CAREY ``AS IS'' AND ANY EXPRESS
4406 OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
4407 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
4408 ARE DISCLAIMED. IN NO EVENT SHALL JOHN CAREY OR CONTRIBUTORS BE
4409 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
4410 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
4411 OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
4412 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
4413 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
4414 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
4415 USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
4416 DAMAGE. */
4418 void
4419 fcwd_access_t::CopyPath (UNICODE_STRING &target)
4421 /* Copy the Path contents over into the UNICODE_STRING referenced by
4422 target. This is used to set the CurrentDirectoryName in the
4423 user parameter block. */
4424 target = Path;
4427 void
4428 fcwd_access_t::Free (PVOID heap)
4430 /* Decrement the reference count. If it's down to 0, free
4431 structure from heap. */
4432 if (InterlockedDecrement (&ReferenceCount) == 0)
4434 /* The handle on init is always a fresh one, not the handle inherited
4435 from the parent process. We always have to close it here.
4436 Note: The handle could be NULL, if we cd'ed into a virtual dir. */
4437 HANDLE h = DirectoryHandle;
4438 if (h)
4439 NtClose (h);
4440 RtlFreeHeap (heap, 0, this);
4444 void
4445 fcwd_access_t::FillIn (HANDLE dir, PUNICODE_STRING name,
4446 ULONG old_dismount_count)
4448 /* Fill in all values into this FAST_CWD structure. */
4449 DirectoryHandle = dir;
4450 ReferenceCount = 1;
4451 OldDismountCount = old_dismount_count;
4452 /* The fcwd structure stores the device characteristics of the
4453 volume holding the dir. RtlGetCurrentDirectory_U checks
4454 if the FILE_REMOVABLE_MEDIA flag is set and, if so, checks if
4455 the volume is still the same as the one used when opening
4456 the directory handle.
4457 We don't call NtQueryVolumeInformationFile for the \\?\PIPE,
4458 though. It just returns STATUS_INVALID_HANDLE anyway. */
4459 FSCharacteristics = 0;
4460 if (name != &ro_u_pipedir)
4462 IO_STATUS_BLOCK io;
4463 FILE_FS_DEVICE_INFORMATION ffdi;
4464 if (NT_SUCCESS (NtQueryVolumeInformationFile (dir, &io, &ffdi,
4465 sizeof ffdi, FileFsDeviceInformation)))
4466 FSCharacteristics = ffdi.Characteristics;
4468 RtlInitEmptyUnicodeString (&Path, Buffer, name->MaximumLength);
4469 RtlCopyUnicodeString (&Path, name);
4470 if (Path.Buffer[Path.Length / sizeof (WCHAR) - 1] != L'\\')
4472 Path.Buffer[Path.Length / sizeof (WCHAR)] = L'\\';
4473 Path.Length += sizeof (WCHAR);
4477 void
4478 fcwd_access_t::SetDirHandleFromBufferPointer (PWCHAR buf_p, HANDLE dir)
4480 /* Input: The buffer pointer as it's stored in the user parameter block
4481 and a directory handle.
4482 This function computes the address to the FAST_CWD structure based
4483 on the version and overwrites the directory handle. It is only
4484 used if we couldn't figure out the address of fast_cwd_ptr. */
4485 fcwd_access_t *f_cwd;
4486 f_cwd = (fcwd_access_t *)
4487 ((PBYTE) buf_p - __builtin_offsetof (fcwd_access_t, Buffer));
4488 f_cwd->DirectoryHandle = dir;
4491 /* This function scans the code in ntdll.dll to find the address of the
4492 global variable used to access the CWD. While the pointer is global,
4493 it's not exported from the DLL, unfortunately. Therefore we have to
4494 use some knowledge to figure out the address. */
4496 #define peek32(x) (*(int32_t *)(x))
4498 static fcwd_access_t **
4499 find_fast_cwd_pointer ()
4501 /* Fetch entry points of relevant functions in ntdll.dll. */
4502 HMODULE ntdll = GetModuleHandle ("ntdll.dll");
4503 if (!ntdll)
4504 return NULL;
4505 const uint8_t *get_dir = (const uint8_t *)
4506 GetProcAddress (ntdll, "RtlGetCurrentDirectory_U");
4507 const uint8_t *ent_crit = (const uint8_t *)
4508 GetProcAddress (ntdll, "RtlEnterCriticalSection");
4509 if (!get_dir || !ent_crit)
4510 return NULL;
4511 /* Search first relative call instruction in RtlGetCurrentDirectory_U. */
4512 const uint8_t *rcall = (const uint8_t *) memchr (get_dir, 0xe8, 80);
4513 if (!rcall)
4514 return NULL;
4515 /* Fetch offset from instruction and compute address of called function.
4516 This function actually fetches the current FAST_CWD instance and
4517 performs some other actions, not important to us. */
4518 const uint8_t *use_cwd = rcall + 5 + peek32 (rcall + 1);
4519 /* Next we search for the locking mechanism and perform a sanity check.
4520 On Pre-Windows 8 we basically look for the RtlEnterCriticalSection call.
4521 Windows 8 does not call RtlEnterCriticalSection. The code manipulates
4522 the FastPebLock manually, probably because RtlEnterCriticalSection has
4523 been converted to an inline function. Either way, we test if the code
4524 uses the FastPebLock. */
4525 const uint8_t *movrbx;
4526 const uint8_t *lock = (const uint8_t *)
4527 memmem ((const char *) use_cwd, 80,
4528 "\xf0\x0f\xba\x35", 4);
4529 if (lock)
4531 /* The lock instruction tweaks the LockCount member, which is not at
4532 the start of the PRTL_CRITICAL_SECTION structure. So we have to
4533 subtract the offset of LockCount to get the real address. */
4534 PRTL_CRITICAL_SECTION lockaddr =
4535 (PRTL_CRITICAL_SECTION) (lock + 9 + peek32 (lock + 4)
4536 - offsetof (RTL_CRITICAL_SECTION, LockCount));
4537 /* Test if lock address is FastPebLock. */
4538 if (lockaddr != NtCurrentTeb ()->Peb->FastPebLock)
4539 return NULL;
4540 /* Search `mov rel(%rip),%rbx'. This is the instruction fetching the
4541 address of the current fcwd_access_t pointer, and it should be pretty
4542 near to the locking stuff. */
4543 movrbx = (const uint8_t *) memmem ((const char *) lock, 40,
4544 "\x48\x8b\x1d", 3);
4546 else
4548 /* Usually the callq RtlEnterCriticalSection follows right after
4549 fetching the lock address. */
4550 int call_rtl_offset = 7;
4551 /* Search `lea rel(%rip),%rcx'. This loads the address of the lock into
4552 %rcx for the subsequent RtlEnterCriticalSection call. */
4553 lock = (const uint8_t *) memmem ((const char *) use_cwd, 80,
4554 "\x48\x8d\x0d", 3);
4555 if (!lock)
4557 /* Windows 8.1 Preview calls `lea rel(rip),%r12' then some unrelated
4558 ops, then `mov %r12,%rcx', then `callq RtlEnterCriticalSection'. */
4559 lock = (const uint8_t *) memmem ((const char *) use_cwd, 80,
4560 "\x4c\x8d\x25", 3);
4561 call_rtl_offset = 14;
4564 if (!lock)
4566 /* A recent Windows 11 Preview calls `lea rel(rip),%r13' then
4567 some unrelated instructions, then `callq RtlEnterCriticalSection'.
4569 lock = (const uint8_t *) memmem ((const char *) use_cwd, 80,
4570 "\x4c\x8d\x2d", 3);
4571 call_rtl_offset = 24;
4574 if (!lock)
4576 return NULL;
4579 PRTL_CRITICAL_SECTION lockaddr =
4580 (PRTL_CRITICAL_SECTION) (lock + 7 + peek32 (lock + 3));
4581 /* Test if lock address is FastPebLock. */
4582 if (lockaddr != NtCurrentTeb ()->Peb->FastPebLock)
4583 return NULL;
4584 /* Next is the `callq RtlEnterCriticalSection'. */
4585 lock += call_rtl_offset;
4586 if (lock[0] != 0xe8)
4587 return NULL;
4588 const uint8_t *call_addr = (const uint8_t *)
4589 (lock + 5 + peek32 (lock + 1));
4590 if (call_addr != ent_crit)
4591 return NULL;
4592 /* In contrast to the above Windows 8 code, we don't have to search
4593 for the `mov rel(%rip),%rbx' instruction. It follows right after
4594 the call to RtlEnterCriticalSection. */
4595 movrbx = lock + 5;
4597 if (!movrbx)
4598 return NULL;
4599 /* Check that the next instruction tests if the fetched value is NULL. */
4600 const uint8_t *testrbx = (const uint8_t *)
4601 memmem (movrbx + 7, 3, "\x48\x85\xdb", 3);
4602 if (!testrbx)
4603 return NULL;
4604 /* Compute address of the fcwd_access_t ** pointer. */
4605 return (fcwd_access_t **) (testrbx + peek32 (movrbx + 3));
4608 static fcwd_access_t **
4609 find_fast_cwd ()
4611 USHORT emulated, hosted;
4612 fcwd_access_t **f_cwd_ptr;
4614 /* First check if we're running in WOW64 on ARM64 emulating AMD64. Skip
4615 fetching FAST_CWD pointer as long as there's no solution for finding
4616 it on that system. */
4617 if (IsWow64Process2 (GetCurrentProcess (), &emulated, &hosted)
4618 && hosted == IMAGE_FILE_MACHINE_ARM64)
4619 return NULL;
4621 /* Fetch the pointer but don't set the global fast_cwd_ptr yet. First
4622 we have to make sure we know the version of the FAST_CWD structure
4623 used on the system. */
4624 f_cwd_ptr = find_fast_cwd_pointer ();
4625 if (!f_cwd_ptr)
4626 small_printf ("Cygwin WARNING:\n"
4627 " Couldn't compute FAST_CWD pointer. This typically occurs if you're using\n"
4628 " an older Cygwin version on a newer Windows. Please update to the latest\n"
4629 " available Cygwin version from https://cygwin.com/. If the problem persists,\n"
4630 " please see https://cygwin.com/problems.html\n\n");
4632 /* Eventually, after we set the version as well, set fast_cwd_ptr. */
4633 return f_cwd_ptr;
4636 void
4637 cwdstuff::override_win32_cwd (bool init, ULONG old_dismount_count)
4639 HANDLE h = NULL;
4641 PEB &peb = *NtCurrentTeb ()->Peb;
4642 UNICODE_STRING &upp_cwd_str = peb.ProcessParameters->CurrentDirectoryName;
4643 HANDLE &upp_cwd_hdl = peb.ProcessParameters->CurrentDirectoryHandle;
4644 PUNICODE_STRING win32_cwd_ptr = error ? &ro_u_pipedir : &win32;
4646 if (fast_cwd_ptr == (fcwd_access_t **) -1)
4647 fast_cwd_ptr = find_fast_cwd ();
4648 if (fast_cwd_ptr)
4650 /* If we got a valid value for fast_cwd_ptr, we can simply replace
4651 the RtlSetCurrentDirectory_U function entirely. */
4652 PVOID heap = peb.ProcessHeap;
4653 /* First allocate a new fcwd_access_t structure on the heap. */
4654 fcwd_access_t *f_cwd = (fcwd_access_t *)
4655 RtlAllocateHeap (heap, 0,
4656 sizeof (fcwd_access_t)
4657 + win32_cwd_ptr->MaximumLength);
4658 if (!f_cwd)
4660 debug_printf ("RtlAllocateHeap failed");
4661 return;
4663 /* Fill in the values. */
4664 f_cwd->FillIn (dir, win32_cwd_ptr, old_dismount_count);
4665 /* Use PEB lock when switching fast_cwd_ptr to the new FAST_CWD
4666 structure and writing the CWD to the user process parameter
4667 block. This is equivalent to calling RtlAcquirePebLock/
4668 RtlReleasePebLock, but without having to go through the FS
4669 selector again. */
4670 RtlEnterCriticalSection (peb.FastPebLock);
4671 fcwd_access_t *old_cwd = *fast_cwd_ptr;
4672 *fast_cwd_ptr = f_cwd;
4673 f_cwd->CopyPath (upp_cwd_str);
4674 upp_cwd_hdl = dir;
4675 RtlLeaveCriticalSection (peb.FastPebLock);
4676 if (old_cwd)
4677 old_cwd->Free (heap);
4679 else
4681 /* Fallback if we failed to find the fast_cwd_ptr value:
4683 - Call RtlSetCurrentDirectory_U.
4684 - Compute new FAST_CWD struct address from buffer pointer in the
4685 user process parameter block.
4686 - Replace the directory handle in the struct with our own handle.
4687 - Close the original handle. RtlSetCurrentDirectory_U already
4688 closed our former dir handle -> no handle leak.
4690 Guard the entire operation with FastPebLock to avoid races
4691 accessing the PEB and FAST_CWD struct.
4693 Unfortunately this method is still prone to a directory usage
4694 race condition:
4696 - The directory is locked against deletion or renaming between the
4697 RtlSetCurrentDirectory_U and the subsequent NtClose call. */
4698 if (unlikely (upp_cwd_hdl == NULL) && init)
4699 return;
4700 RtlEnterCriticalSection (peb.FastPebLock);
4701 if (!init)
4703 NTSTATUS status =
4704 RtlSetCurrentDirectory_U (win32_cwd_ptr);
4705 if (!NT_SUCCESS (status))
4707 RtlLeaveCriticalSection (peb.FastPebLock);
4708 debug_printf ("RtlSetCurrentDirectory_U(%S) failed, %y",
4709 win32_cwd_ptr);
4710 return;
4713 fcwd_access_t::SetDirHandleFromBufferPointer(upp_cwd_str.Buffer, dir);
4714 h = upp_cwd_hdl;
4715 upp_cwd_hdl = dir;
4716 RtlLeaveCriticalSection (peb.FastPebLock);
4717 /* The handle on init is always a fresh one, not the handle inherited
4718 from the parent process. We always have to close it here. */
4719 NtClose (h);
4723 /* Initialize cwdstuff */
4724 void
4725 cwdstuff::init ()
4727 /* Cygwin processes inherit the cwd from their parent. If the win32 path
4728 buffer is not NULL, the cwd struct is already set up, and we only
4729 have to override the Win32 CWD with ours. */
4730 if (win32.Buffer)
4731 override_win32_cwd (true, SharedUserData.DismountCount);
4732 else
4734 /* Initialize fast_cwd stuff. */
4735 fast_cwd_ptr = (fcwd_access_t **) -1;
4736 /* Initially re-open the cwd to allow POSIX semantics. */
4737 set (NULL, NULL);
4741 /* Chdir and fill out the elements of a cwdstuff struct. */
4743 cwdstuff::set (path_conv *nat_cwd, const char *posix_cwd)
4745 NTSTATUS status;
4746 UNICODE_STRING upath;
4747 OBJECT_ATTRIBUTES attr;
4748 PEB &peb = *NtCurrentTeb ()->Peb;
4749 bool virtual_path = false;
4750 bool unc_path = false;
4751 bool inaccessible_path = false;
4753 /* Here are the problems with using SetCurrentDirectory. Just skip this
4754 comment if you don't like whining.
4756 - SetCurrentDirectory only supports paths of up to MAX_PATH - 1 chars,
4757 including a trailing backslash. That's an absolute restriction, even
4758 in the UNICODE API.
4760 - SetCurrentDirectory fails for directories with strict permissions even
4761 for processes with the SE_BACKUP_NAME privilege enabled. The reason
4762 is apparently that SetCurrentDirectory calls NtOpenFile without the
4763 FILE_OPEN_FOR_BACKUP_INTENT flag set.
4765 - SetCurrentDirectory does not support case-sensitivity.
4767 - Unlinking a cwd fails because SetCurrentDirectory seems to open
4768 directories so that deleting the directory is disallowed.
4770 - SetCurrentDirectory can naturally not work on virtual Cygwin paths
4771 like /proc or /cygdrive.
4773 Nevertheless, doing entirely without SetCurrentDirectory is not really
4774 feasible, because it breaks too many mixed applications using the Win32
4775 API.
4777 Therefore we handle the CWD all by ourselves and just keep the Win32
4778 CWD in sync. However, to avoid surprising behaviour in the Win32 API
4779 when we are in a CWD which is inaccessible as Win32 CWD, we set the
4780 Win32 CWD to a "weird" directory in which all relative filesystem-related
4781 calls fail. */
4783 if (nat_cwd)
4785 upath = *nat_cwd->get_nt_native_path ();
4786 if (nat_cwd->isspecial ())
4788 virtual_path = true;
4789 /* But allow starting of native apps from /dev if /dev actually
4790 exists on disk. */
4791 if (isdev_dev (nat_cwd->dev))
4793 FILE_BASIC_INFORMATION fbi;
4795 InitializeObjectAttributes (&attr, &upath,
4796 OBJ_CASE_INSENSITIVE | OBJ_INHERIT,
4797 NULL, NULL);
4798 status = NtQueryAttributesFile (&attr, &fbi);
4799 if (status != STATUS_OBJECT_NAME_NOT_FOUND
4800 && status != STATUS_OBJECT_PATH_NOT_FOUND)
4801 virtual_path = false;
4806 acquire_write ();
4808 /* Memorize old DismountCount before opening the dir. This value is
4809 stored in the FAST_CWD structure. It would be simpler to fetch the
4810 old DismountCount in override_win32_cwd, but Windows also fetches
4811 it before opening the directory handle. It's not quite clear if
4812 that's really required, but since we don't know the side effects of
4813 this action, we better follow Windows' lead. */
4814 ULONG old_dismount_count = SharedUserData.DismountCount;
4815 /* Open a directory handle with FILE_OPEN_FOR_BACKUP_INTENT and with all
4816 sharing flags set. The handle is right now used in exceptions.cc only,
4817 but that might change in future. */
4818 HANDLE h = NULL;
4819 if (!virtual_path)
4821 IO_STATUS_BLOCK io;
4823 if (!nat_cwd)
4825 /* On init, just reopen Win32 CWD with desired access flags.
4826 We can access the PEB without lock, because no other thread
4827 can change the CWD. However, there's a chance that the handle
4828 is NULL, even though CurrentDirectoryName isn't so we have to
4829 be careful. */
4830 if (!peb.ProcessParameters->CurrentDirectoryHandle)
4832 InitializeObjectAttributes (&attr,
4833 &peb.ProcessParameters->CurrentDirectoryName,
4834 OBJ_CASE_INSENSITIVE | OBJ_INHERIT, NULL, NULL);
4836 else
4838 RtlInitUnicodeString (&upath, L"");
4839 InitializeObjectAttributes (&attr,
4840 &upath, OBJ_CASE_INSENSITIVE | OBJ_INHERIT,
4841 peb.ProcessParameters->CurrentDirectoryHandle,
4842 NULL);
4845 else
4846 InitializeObjectAttributes (&attr, &upath,
4847 nat_cwd->objcaseinsensitive () | OBJ_INHERIT,
4848 NULL, NULL);
4849 /* First try without FILE_OPEN_FOR_BACKUP_INTENT, to find out if the
4850 directory is valid for Win32 apps. And, no, we can't just call
4851 SetCurrentDirectory here, since that would potentially break
4852 case-sensitivity. */
4853 status = NtOpenFile (&h, SYNCHRONIZE | FILE_TRAVERSE, &attr, &io,
4854 FILE_SHARE_VALID_FLAGS,
4855 FILE_DIRECTORY_FILE
4856 | FILE_SYNCHRONOUS_IO_NONALERT);
4857 if (status == STATUS_ACCESS_DENIED)
4859 status = NtOpenFile (&h, SYNCHRONIZE | FILE_TRAVERSE, &attr, &io,
4860 FILE_SHARE_VALID_FLAGS,
4861 FILE_DIRECTORY_FILE
4862 | FILE_SYNCHRONOUS_IO_NONALERT
4863 | FILE_OPEN_FOR_BACKUP_INTENT);
4864 inaccessible_path = true;
4866 if (!NT_SUCCESS (status))
4868 /* Called from chdir? Just fail. */
4869 if (nat_cwd)
4871 release_write ();
4872 __seterrno_from_nt_status (status);
4873 return -1;
4875 /* Otherwise we're in init and posix hasn't been set yet. Try to
4876 duplicate the handle instead. If that fails, too, set dir to NULL
4877 and carry on. This will at least set posix to some valid path at
4878 process startup, and subsequent getcwd calls don't EFAULT. */
4879 debug_printf ("WARNING: Can't reopen CWD %y '%S', status %y",
4880 peb.ProcessParameters->CurrentDirectoryHandle,
4881 &peb.ProcessParameters->CurrentDirectoryName,
4882 status);
4883 if (!peb.ProcessParameters->CurrentDirectoryHandle
4884 || !DuplicateHandle (GetCurrentProcess (),
4885 peb.ProcessParameters->CurrentDirectoryHandle,
4886 GetCurrentProcess (), &h, 0, TRUE, 0))
4888 if (peb.ProcessParameters->CurrentDirectoryHandle)
4889 debug_printf ("...and DuplicateHandle failed with %E.");
4890 h = NULL;
4894 /* Set new handle. Note that we simply overwrite the old handle here
4895 without closing it. The handle is also used as Win32 CWD handle in
4896 the user parameter block, and it will be closed in override_win32_cwd,
4897 if required. */
4898 dir = h;
4900 if (!nat_cwd)
4902 /* On init, just fetch the Win32 dir from the PEB. We can access
4903 the PEB without lock, because no other thread can change the CWD
4904 at that time. */
4905 PUNICODE_STRING pdir = &peb.ProcessParameters->CurrentDirectoryName;
4906 RtlInitEmptyUnicodeString (&win32,
4907 (PWCHAR) crealloc_abort (win32.Buffer,
4908 pdir->Length
4909 + sizeof (WCHAR)),
4910 pdir->Length + sizeof (WCHAR));
4911 RtlCopyUnicodeString (&win32, pdir);
4913 PWSTR eoBuffer = win32.Buffer + (win32.Length / sizeof (WCHAR));
4914 /* Remove trailing slash if one exists. */
4915 if ((eoBuffer - win32.Buffer) > 3 && eoBuffer[-1] == L'\\')
4916 win32.Length -= sizeof (WCHAR);
4917 if (eoBuffer[0] == L'\\')
4918 unc_path = true;
4920 posix_cwd = NULL;
4922 else
4924 if (!virtual_path) /* don't mangle virtual path. */
4926 /* Convert into Win32 path and compute length. */
4927 if (upath.Buffer[1] == L'?')
4929 upath.Buffer += 4;
4930 upath.Length -= 4 * sizeof (WCHAR);
4931 if (upath.Buffer[1] != L':')
4933 /* UNC path */
4934 upath.Buffer += 2;
4935 upath.Length -= 2 * sizeof (WCHAR);
4936 unc_path = true;
4939 else
4941 /* Path via native NT namespace. Prepend GLOBALROOT prefix
4942 to create a valid Win32 path. */
4943 PWCHAR buf = (PWCHAR) alloca (upath.Length
4944 + ro_u_globalroot.Length
4945 + sizeof (WCHAR));
4946 wcpcpy (wcpcpy (buf, ro_u_globalroot.Buffer), upath.Buffer);
4947 upath.Buffer = buf;
4948 upath.Length += ro_u_globalroot.Length;
4950 PWSTR eoBuffer = upath.Buffer + (upath.Length / sizeof (WCHAR));
4951 /* Remove trailing slash if one exists. */
4952 if ((eoBuffer - upath.Buffer) > 3 && eoBuffer[-1] == L'\\')
4953 upath.Length -= sizeof (WCHAR);
4955 RtlInitEmptyUnicodeString (&win32,
4956 (PWCHAR) crealloc_abort (win32.Buffer,
4957 upath.Length
4958 + sizeof (WCHAR)),
4959 upath.Length + sizeof (WCHAR));
4960 RtlCopyUnicodeString (&win32, &upath);
4961 if (unc_path)
4962 win32.Buffer[0] = L'\\';
4964 /* Make sure it's NUL-terminated. */
4965 win32.Buffer[win32.Length / sizeof (WCHAR)] = L'\0';
4967 /* Set drive_length, used in path conversion, and error code, used in
4968 spawn_guts to decide whether a native Win32 app can be started or not. */
4969 if (virtual_path)
4971 drive_length = 0;
4972 error = ENOTDIR;
4974 else
4976 if (!unc_path)
4977 drive_length = 2;
4978 else
4980 PWCHAR ptr = wcschr (win32.Buffer + 2, L'\\');
4981 if (ptr)
4982 ptr = wcschr (ptr + 1, L'\\');
4983 if (ptr)
4984 drive_length = ptr - win32.Buffer;
4985 else
4986 drive_length = win32.Length / sizeof (WCHAR);
4988 if (inaccessible_path)
4989 error = EACCES;
4990 else if (win32.Length > (MAX_PATH - 2) * sizeof (WCHAR))
4991 error = ENAMETOOLONG;
4992 else
4993 error = 0;
4995 /* Keep the Win32 CWD in sync. Don't check for error, other than for
4996 strace output. Try to keep overhead low. */
4997 override_win32_cwd (!nat_cwd, old_dismount_count);
4999 /* Eventually, create POSIX path if it's not set on entry. */
5000 tmp_pathbuf tp;
5001 if (!posix_cwd)
5003 posix_cwd = (const char *) tp.c_get ();
5004 mount_table->conv_to_posix_path (win32.Buffer, (char *) posix_cwd, 0);
5006 posix = (char *) crealloc_abort (posix, strlen (posix_cwd) + 1);
5007 stpcpy (posix, posix_cwd);
5009 release_write ();
5010 return 0;
5013 const char *
5014 cwdstuff::get_error_desc () const
5016 switch (cygheap->cwd.get_error ())
5018 case EACCES:
5019 return "has restricted permissions which render it\n"
5020 "inaccessible as Win32 working directory";
5021 case ENOTDIR:
5022 return "is a virtual Cygwin directory which does\n"
5023 "not exist for a native Windows application";
5024 case ENAMETOOLONG:
5025 return "has a path longer than allowed for a\n"
5026 "Win32 working directory";
5027 default:
5028 break;
5030 /* That shouldn't occur, unless we defined a new error code
5031 in cwdstuff::set. */
5032 return "is not accessible for some unknown reason";
5035 /* Store incoming wchar_t path as current posix cwd. This is called from
5036 setlocale so that the cwd is always stored in the right charset. */
5037 void
5038 cwdstuff::reset_posix (wchar_t *w_cwd)
5040 size_t len = sys_wcstombs (NULL, (size_t) -1, w_cwd);
5041 posix = (char *) crealloc_abort (posix, len + 1);
5042 sys_wcstombs (posix, len + 1, w_cwd);
5045 char *
5046 cwdstuff::get (char *buf, int need_posix, int with_chroot, unsigned ulen)
5048 tmp_pathbuf tp;
5050 errno = 0;
5051 if (ulen)
5052 /* nothing */;
5053 else if (buf == NULL)
5054 ulen = (unsigned) -1;
5055 else
5057 set_errno (EINVAL);
5058 goto out;
5061 acquire_read ();
5063 char *tocopy;
5064 if (!need_posix)
5066 tocopy = tp.c_get ();
5067 sys_wcstombs (tocopy, NT_MAX_PATH, win32.Buffer,
5068 win32.Length / sizeof (WCHAR));
5070 else
5071 tocopy = posix;
5073 debug_printf ("posix %s", posix);
5074 if (strlen (tocopy) >= ulen)
5076 set_errno (ERANGE);
5077 buf = NULL;
5079 else
5081 if (!buf)
5082 buf = (char *) malloc (strlen (tocopy) + 1);
5083 strcpy (buf, tocopy);
5084 if (!buf[0]) /* Should only happen when chroot */
5085 strcpy (buf, "/");
5088 release_read ();
5090 out:
5091 syscall_printf ("(%s) = cwdstuff::get (%p, %u, %d, %d), errno %d",
5092 buf, buf, ulen, need_posix, with_chroot, errno);
5093 return buf;
5096 /* No need to be reentrant or thread-safe according to SUSv3.
5097 / and \\ are treated equally. Leading drive specifiers are
5098 kept intact as far as it makes sense. Everything else is
5099 POSIX compatible. */
5100 extern "C" char *
5101 basename (char *path)
5103 static char buf[4];
5104 char *c, *d, *bs = path;
5106 if (!path || !*path)
5107 return strcpy (buf, ".");
5108 if (isalpha (path[0]) && path[1] == ':')
5109 bs += 2;
5110 else if (strspn (path, "/\\") > 1)
5111 ++bs;
5112 c = strrchr (bs, '/');
5113 if ((d = strrchr (c ?: bs, '\\')) > c)
5114 c = d;
5115 if (c)
5117 /* Trailing (back)slashes are eliminated. */
5118 while (c && c > bs && c[1] == '\0')
5120 *c = '\0';
5121 c = strrchr (bs, '/');
5122 if ((d = strrchr (c ?: bs, '\\')) > c)
5123 c = d;
5125 if (c && (c > bs || c[1]))
5126 return c + 1;
5128 else if (!bs[0])
5130 stpncpy (buf, path, bs - path);
5131 stpcpy (buf + (bs - path), ".");
5132 return buf;
5134 return path;
5137 /* The differences with the POSIX version above:
5138 - declared in <string.h> (instead of <libgen.h>);
5139 - the argument is never modified, and therefore is marked const;
5140 - the empty string is returned if path is an empty string, "/", or ends
5141 with a trailing slash. */
5142 extern "C" char *
5143 __gnu_basename (const char *path)
5145 static char buf[1];
5146 char *c, *d, *bs = (char *)path;
5148 if (!path || !*path)
5149 return strcpy (buf, "");
5150 if (isalpha (path[0]) && path[1] == ':')
5151 bs += 2;
5152 else if (strspn (path, "/\\") > 1)
5153 ++bs;
5154 c = strrchr (bs, '/');
5155 if ((d = strrchr (c ?: bs, '\\')) > c)
5156 c = d;
5157 if (c)
5158 return c + 1;
5159 else if (!bs[0])
5160 return strcpy (buf, "");
5161 return (char *)path;
5164 /* No need to be reentrant or thread-safe according to SUSv3.
5165 / and \\ are treated equally. Leading drive specifiers and
5166 leading double (back)slashes are kept intact as far as it
5167 makes sense. Everything else is POSIX compatible. */
5168 extern "C" char *
5169 dirname (char *path)
5171 static char buf[4];
5172 char *c, *d, *bs = path;
5174 if (!path || !*path)
5175 return strcpy (buf, ".");
5176 if (isalpha (path[0]) && path[1] == ':')
5177 bs += 2;
5178 else if (strspn (path, "/\\") == 2)
5179 ++bs;
5180 c = strrchr (bs, '/');
5181 if ((d = strrchr (c ?: bs, '\\')) > c)
5182 c = d;
5183 if (c)
5185 /* Trailing (back)slashes are eliminated. */
5186 while (c && c > bs && c[1] == '\0')
5188 *c = '\0';
5189 c = strrchr (bs, '/');
5190 if ((d = strrchr (c ?: bs, '\\')) > c)
5191 c = d;
5193 if (!c)
5194 strcpy (bs, ".");
5195 else if (c > bs)
5197 /* More trailing (back)slashes are eliminated. */
5198 while (c > bs && (*c == '/' || *c == '\\'))
5199 *c-- = '\0';
5201 else
5202 c[1] = '\0';
5204 else
5206 stpncpy (buf, path, bs - path);
5207 stpcpy (buf + (bs - path), ".");
5208 return buf;
5210 return path;