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
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
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
55 #include <w32api/winioctl.h>
56 #include <w32api/shlobj.h>
57 #include <sys/param.h>
58 #include <sys/cygwin.h>
66 #include "shared_info.h"
71 suffix_info stat_suffixes
[] =
74 suffix_info (".exe", 1),
80 char contents
[SYMLINK_MAX
+ 1];
85 unsigned pc_flags
; /* Relevant pathconv_arg flags from path_conv caller */
94 int check (char *path
, const suffix_info
*suffixes
, fs_info
&fs
,
95 path_conv_handle
&conv_hdl
);
97 bool parse_device (const char *);
98 int check_sysfile (HANDLE h
);
99 int check_shortcut (HANDLE h
);
100 int check_reparse_point (HANDLE h
, bool remote
);
101 int check_nfs_symlink (HANDLE h
);
102 int posixify (char *srcbuf
);
103 bool set_error (int);
106 muto NO_COPY
cwdstuff::cwd_lock
;
108 static const GUID GUID_shortcut
109 = { 0x00021401L
, 0, 0, {0xc0, 0, 0, 0, 0, 0, 0, 0x46}};
113 WSH_FLAG_IDLIST
= 0x01, /* Contains an ITEMIDLIST. */
114 WSH_FLAG_FILE
= 0x02, /* Contains a file locator element. */
115 WSH_FLAG_DESC
= 0x04, /* Contains a description. */
116 WSH_FLAG_RELPATH
= 0x08, /* Contains a relative path. */
117 WSH_FLAG_WD
= 0x10, /* Contains a working dir. */
118 WSH_FLAG_CMDLINE
= 0x20, /* Contains command line args. */
119 WSH_FLAG_ICON
= 0x40 /* Contains a custom icon. */
122 struct win_shortcut_hdr
124 DWORD size
; /* Header size in bytes. Must contain 0x4c. */
125 GUID magic
; /* GUID of shortcut files. */
126 DWORD flags
; /* Content flags. See above. */
128 /* The next fields from attr to icon_no are always set to 0 in Cygwin
129 and U/Win shortcuts. */
130 DWORD attr
; /* Target file attributes. */
131 FILETIME ctime
; /* These filetime items are never touched by the */
132 FILETIME mtime
; /* system, apparently. Values don't matter. */
134 DWORD filesize
; /* Target filesize. */
135 DWORD icon_no
; /* Icon number. */
137 DWORD run
; /* Values defined in winuser.h. Use SW_NORMAL. */
138 DWORD hotkey
; /* Hotkey value. Set to 0. */
139 DWORD dummy
[2]; /* Future extension probably. Always 0. */
142 /* Return non-zero if PATH1 is a prefix of PATH2.
143 Both are assumed to be of the same path style and / vs \ usage.
145 LEN1 = strlen (PATH1). It's passed because often it's already known.
148 /foo/ is a prefix of /foo <-- may seem odd, but desired
149 /foo is a prefix of /foo/
150 / is a prefix of /foo/bar
151 / is not a prefix of foo/bar
152 foo/ is a prefix foo/bar
153 /foo is not a prefix of /foobar
157 path_prefix_p (const char *path1
, const char *path2
, int len1
,
158 bool caseinsensitive
)
160 /* Handle case where PATH1 has trailing '/' and when it doesn't. */
161 if (len1
> 0 && isdirsep (path1
[len1
- 1]))
165 return isdirsep (path2
[0]) && !isdirsep (path2
[1]);
167 if (isdirsep (path2
[len1
]) || path2
[len1
] == 0 || path1
[len1
- 1] == ':')
168 return caseinsensitive
? strncasematch (path1
, path2
, len1
)
169 : !strncmp (path1
, path2
, len1
);
174 /* Return non-zero if paths match in first len chars.
175 Check is dependent of the case sensitivity setting. */
177 pathnmatch (const char *path1
, const char *path2
, int len
, bool caseinsensitive
)
179 return caseinsensitive
180 ? strncasematch (path1
, path2
, len
) : !strncmp (path1
, path2
, len
);
183 /* Return non-zero if paths match. Check is dependent of the case
184 sensitivity setting. */
186 pathmatch (const char *path1
, const char *path2
, bool caseinsensitive
)
188 return caseinsensitive
189 ? strcasematch (path1
, path2
) : !strcmp (path1
, path2
);
192 /* TODO: This function is used in mkdir and rmdir to generate correct
193 error messages in case of paths ending in /. or /.. components.
194 Right now, normalize_posix_path will just normalize
195 those components away, which changes the semantics. */
197 has_dot_last_component (const char *dir
, bool test_dot_dot
)
199 /* SUSv3: . and .. are not allowed as last components in various system
200 calls. Don't test for backslash path separator since that's a Win32
201 path following Win32 rules. */
202 const char *last_comp
= strchr (dir
, '\0');
204 if (last_comp
== dir
)
205 return false; /* Empty string. Probably shouldn't happen here? */
207 /* Detect run of trailing slashes */
208 while (last_comp
> dir
&& *--last_comp
== '/')
211 /* Detect just a run of slashes or a path that does not end with a slash. */
212 if (*last_comp
!= '.')
215 /* We know we have a trailing dot here. Check that it really is a standalone "."
216 path component by checking that it is at the beginning of the string or is
218 if (last_comp
== dir
|| *--last_comp
== '/')
221 /* If we're not checking for '..' we're done. Ditto if we're now pointing to
223 if (!test_dot_dot
|| *last_comp
!= '.')
224 return false; /* either not testing for .. or this was not '..' */
226 /* Repeat previous test for standalone or path component. */
227 return last_comp
== dir
|| last_comp
[-1] == '/';
230 /* Normalize a POSIX path.
231 All duplicate /'s, except for 2 leading /'s, are deleted.
232 The result is 0 for success, or an errno error value. */
235 normalize_posix_path (const char *src
, char *dst
, char *&tail
)
237 const char *in_src
= src
;
238 char *dst_start
= dst
;
239 bool check_parent
= false;
240 syscall_printf ("src %s", src
);
242 if ((isdrive (src
) && isdirsep (src
[2])) || *src
== '\\')
246 if (!isslash (src
[0]))
248 if (!cygheap
->cwd
.get (dst
))
250 tail
= strchr (tail
, '\0');
251 if (isslash (dst
[0]) && isslash (dst
[1]))
255 if (tail
== dst_start
+ 1 && *dst_start
== '/')
259 if (tail
> dst
&& !isslash (tail
[-1]))
262 /* Two leading /'s? If so, preserve them. */
263 else if (isslash (src
[1]) && !isslash (src
[2]))
273 /* Strip runs of /'s. */
295 if (!isslash (src
[1]))
298 else if (src
[2] && !isslash (src
[2]))
302 /* According to POSIX semantics all elements of path must
303 exist. To follow it, we must validate our path before
304 removing the trailing component. Check_parent is needed
305 for performance optimization, in order not to verify paths
306 which are already verified. For example this prevents
307 double check in case of foo/bar/../.. */
310 if (tail
> dst_start
) /* Don't check for / or // dir. */
313 debug_printf ("checking %s before '..'", dst
);
314 /* In conjunction with native and NFS symlinks,
315 this call can result in a recursion which eats
316 up our tmp_pathbuf buffers. This in turn results
317 in a api_fatal call. To avoid that, we're
318 checking our remaining buffers and return an
319 error code instead. Note that this only happens
320 if the path contains 15 or more relative native/NFS
321 symlinks with a ".." in the target path. */
323 if (!tp
.check_usage (4, 3))
325 path_conv
head (dst
, PC_SYM_FOLLOW
| PC_POSIX
);
330 /* At this point, dst is a normalized path. If the
331 normalized path created by path_conv does not
332 match the normalized path we're just testing, then
333 the path in dst contains native symlinks. If we
334 just plunge along, removing the previous path
335 component, we may end up removing a symlink from
336 the path and the resulting path will be invalid.
337 So we replace dst with what we found in head
338 instead. All the work replacing symlinks has been
339 done in that path anyway, so why repeat it? */
340 tail
= stpcpy (dst
, head
.get_posix ());
342 check_parent
= false;
344 while (tail
> dst_start
&& !isslash (*--tail
))
352 if ((tail
- dst
) >= NT_MAX_PATH
)
354 debug_printf ("ENAMETOOLONG = normalize_posix_path (%s)", src
);
362 debug_printf ("%s = normalize_posix_path (%s)", dst
, in_src
);
366 int err
= normalize_win32_path (in_src
, dst
, tail
);
368 for (char *p
= dst
; (p
= strchr (p
, '\\')); p
++)
374 path_conv::add_ext_from_sym (symlink_info
&sym
)
376 if (sym
.ext_here
&& *sym
.ext_here
)
378 suffix
= path
+ sym
.extn
;
379 if (sym
.ext_tacked_on
)
380 strcpy ((char *) suffix
, sym
.ext_here
);
384 static void __reg2
mkrelpath (char *dst
, bool caseinsensitive
);
387 mkrelpath (char *path
, bool caseinsensitive
)
390 char *cwd_win32
= tp
.c_get ();
391 if (!cygheap
->cwd
.get (cwd_win32
, 0))
394 unsigned cwdlen
= strlen (cwd_win32
);
395 if (!path_prefix_p (cwd_win32
, path
, cwdlen
, caseinsensitive
))
398 size_t n
= strlen (path
);
406 tail
+= isdirsep (cwd_win32
[cwdlen
- 1]) ? cwdlen
: cwdlen
+ 1;
408 memmove (path
, tail
, strlen (tail
) + 1);
414 path_conv::set_posix (const char *path_copy
)
418 size_t n
= strlen (path_copy
) + 1;
419 char *p
= (char *) crealloc_abort ((void *) posix_path
, n
);
420 posix_path
= (const char *) memcpy (p
, path_copy
, n
);
425 str2uni_cat (UNICODE_STRING
&tgt
, const char *srcstr
)
427 int len
= sys_mbstowcs (tgt
.Buffer
+ tgt
.Length
/ sizeof (WCHAR
),
428 (tgt
.MaximumLength
- tgt
.Length
) / sizeof (WCHAR
),
431 tgt
.Length
+= (len
- 1) * sizeof (WCHAR
);
435 get_nt_native_path (const char *path
, UNICODE_STRING
& upath
, bool dos
)
438 if (path
[0] == '/') /* special path w/o NT path representation. */
439 str2uni_cat (upath
, path
);
440 else if (path
[0] != '\\') /* X:\... or relative path. */
442 if (path
[1] == ':') /* X:\... */
444 RtlAppendUnicodeStringToString (&upath
, &ro_u_natp
);
445 str2uni_cat (upath
, path
);
446 /* The drive letter must be upper case. */
447 upath
.Buffer
[4] = towupper (upath
.Buffer
[4]);
448 transform_chars (&upath
, 7);
450 else /* relative path */
452 str2uni_cat (upath
, path
);
453 transform_chars (&upath
, 0);
456 else if (path
[1] != '\\') /* \Device\... */
457 str2uni_cat (upath
, path
);
458 else if ((path
[2] != '.' && path
[2] != '?')
459 || path
[3] != '\\') /* \\server\share\... */
461 RtlAppendUnicodeStringToString (&upath
, &ro_u_uncp
);
462 str2uni_cat (upath
, path
+ 2);
463 transform_chars (&upath
, 8);
465 else /* \\.\device or \\?\foo */
467 RtlAppendUnicodeStringToString (&upath
, &ro_u_natp
);
468 str2uni_cat (upath
, path
+ 4);
472 /* Unfortunately we can't just use transform_chars with the tfx_rev_chars
473 table since only leading and trailing spaces and dots are affected.
474 So we step to every backslash and fix surrounding dots and spaces.
475 That makes these broken filesystems a bit slower, but, hey. */
476 PWCHAR cp
= upath
.Buffer
+ 7;
477 PWCHAR cend
= upath
.Buffer
+ upath
.Length
/ sizeof (WCHAR
);
482 while (*ccp
== L
'.' || *ccp
== L
' ')
484 while (cp
[1] == L
' ')
487 while (*--cp
== L
'.' || *cp
== L
' ')
493 /* Handle with extrem care! Only used in a certain instance in try_to_bin.
494 Every other usage needs a careful check. */
496 path_conv::set_nt_native_path (PUNICODE_STRING new_path
)
498 wide_path
= (PWCHAR
) crealloc_abort (wide_path
, new_path
->MaximumLength
);
499 memcpy (wide_path
, new_path
->Buffer
, new_path
->Length
);
500 uni_path
.Length
= new_path
->Length
;
501 uni_path
.MaximumLength
= new_path
->MaximumLength
;
502 uni_path
.Buffer
= wide_path
;
505 /* If suffix is not NULL, append the suffix string verbatim.
506 This is used by fhandler_mqueue::mq_open to append an NTFS stream suffix. */
508 path_conv::get_nt_native_path (PUNICODE_STRING suffix
)
518 uni_path
.MaximumLength
= (strlen (path
) + 10) * sizeof (WCHAR
);
520 uni_path
.MaximumLength
+= suffix
->Length
;
521 wide_path
= (PWCHAR
) cmalloc_abort (HEAP_STR
, uni_path
.MaximumLength
);
522 uni_path
.Buffer
= wide_path
;
523 ::get_nt_native_path (path
, uni_path
, has_dos_filenames_only ());
525 RtlAppendUnicodeStringToString (&uni_path
, suffix
);
532 path_conv::get_wide_win32_path (PWCHAR wc
)
534 get_nt_native_path ();
537 wcpcpy (wc
, wide_path
);
544 getfileattr (const char *path
, bool caseinsensitive
) /* path has to be always absolute. */
547 UNICODE_STRING upath
;
548 OBJECT_ATTRIBUTES attr
;
549 FILE_BASIC_INFORMATION fbi
;
554 InitializeObjectAttributes (&attr
, &upath
,
555 caseinsensitive
? OBJ_CASE_INSENSITIVE
: 0,
557 get_nt_native_path (path
, upath
, false);
559 status
= NtQueryAttributesFile (&attr
, &fbi
);
560 if (NT_SUCCESS (status
))
561 return fbi
.FileAttributes
;
563 if (status
!= STATUS_OBJECT_NAME_NOT_FOUND
564 && status
!= STATUS_NO_SUCH_FILE
) /* File not found on 9x share */
566 /* File exists but access denied. Try to get attribute through
568 UNICODE_STRING dirname
, basename
;
570 FILE_BOTH_DIR_INFORMATION fdi
;
572 RtlSplitUnicodePath (&upath
, &dirname
, &basename
);
573 InitializeObjectAttributes (&attr
, &dirname
,
574 caseinsensitive
? OBJ_CASE_INSENSITIVE
: 0,
576 status
= NtOpenFile (&dir
, SYNCHRONIZE
| FILE_LIST_DIRECTORY
,
577 &attr
, &io
, FILE_SHARE_VALID_FLAGS
,
578 FILE_SYNCHRONOUS_IO_NONALERT
579 | FILE_OPEN_FOR_BACKUP_INTENT
580 | FILE_DIRECTORY_FILE
);
581 if (NT_SUCCESS (status
))
583 status
= NtQueryDirectoryFile (dir
, NULL
, NULL
, 0, &io
,
585 FileBothDirectoryInformation
,
586 TRUE
, &basename
, TRUE
);
588 if (NT_SUCCESS (status
) || status
== STATUS_BUFFER_OVERFLOW
)
589 return fdi
.FileAttributes
;
592 SetLastError (RtlNtStatusToDosError (status
));
593 return INVALID_FILE_ATTRIBUTES
;
596 /* Convert an arbitrary path SRC to a pure Win32 path, suitable for
597 passing to Win32 API routines.
599 If an error occurs, `error' is set to the errno value.
600 Otherwise it is set to 0.
603 SYMLINK_FOLLOW - convert to PATH symlink points to
604 SYMLINK_NOFOLLOW - convert to PATH of symlink itself
605 SYMLINK_IGNORE - do not check PATH for symlinks
606 SYMLINK_CONTENTS - just return symlink contents
609 /* TODO: This implementation is only preliminary. For internal
610 purposes it's necessary to have a path_conv::check function which
611 takes a UNICODE_STRING src path, otherwise we waste a lot of time
612 for converting back and forth. The below implementation does
613 realy nothing but converting to char *, until path_conv handles
614 wide-char paths directly. */
616 path_conv::check (const UNICODE_STRING
*src
, unsigned opt
,
617 const suffix_info
*suffixes
)
620 char *path
= tp
.c_get ();
622 user_shared
->warned_msdos
= true;
623 sys_wcstombs (path
, NT_MAX_PATH
, src
->Buffer
, src
->Length
/ sizeof (WCHAR
));
624 path_conv::check (path
, opt
, suffixes
);
628 path_conv::check (const char *src
, unsigned opt
,
629 const suffix_info
*suffixes
)
631 /* The tmp_buf array is used when expanding symlinks. It is NT_MAX_PATH * 2
632 in length so that we can hold the expanded symlink plus a trailer. */
634 char *path_copy
= tp
.c_get ();
635 char *pathbuf
= tp
.c_get ();
636 char *tmp_buf
= tp
.t_get ();
637 char *THIS_path
= tp
.c_get ();
639 bool need_directory
= 0;
640 bool add_ext
= false;
642 char *tail
, *path_end
;
645 static path_conv last_path_conv
;
646 static char last_src
[CYG_MAX_PATH
];
648 if (*last_src
&& strcmp (last_src
, src
) == 0)
650 *this = last_path_conv
;
661 fileattr
= INVALID_FILE_ATTRIBUTES
;
662 caseinsensitive
= OBJ_CASE_INSENSITIVE
;
668 cfree (modifiable_path ());
671 close_conv_handle ();
675 cfree ((void *) posix_path
);
678 int component
= 0; // Number of translated components
680 if (!(opt
& PC_NULLEMPTY
))
688 bool is_msdos
= false;
689 /* This loop handles symlink expansion. */
692 is_relpath
= !isabspath (src
);
693 error
= normalize_posix_path (src
, path_copy
, tail
);
703 /* Detect if the user was looking for a directory. We have to strip
704 the trailing slash initially while trying to add extensions but
705 take it into account during processing */
706 if (tail
> path_copy
+ 2 && isslash (tail
[-1]))
713 /* Scan path_copy from right to left looking either for a symlink
714 or an actual existing file. If an existing file is found, just
715 return. If a symlink is found, exit the for loop.
716 Also: be careful to preserve the errno returned from
717 symlink.check as the caller may need it. */
718 /* FIXME: Do we have to worry about multiple \'s here? */
719 component
= 0; // Number of translated components
720 sym
.contents
[0] = '\0';
724 /* Make sure to check certain flags on last component only. */
725 for (unsigned pc_flags
= opt
& (PC_NO_ACCESS_CHECK
| PC_KEEP_HANDLE
726 | PC_SYM_FOLLOW
| PC_SYM_NOFOLLOW_REP
);
728 pc_flags
= opt
& (PC_SYM_FOLLOW
| PC_SYM_NOFOLLOW_REP
))
730 const suffix_info
*suff
;
733 /* Don't allow symlink.check to set anything in the path_conv
734 class if we're working on an inner component of the path */
743 full_path
= THIS_path
;
746 retry_fs_via_processfd
:
748 /* Convert to native path spec sans symbolic link info. */
749 error
= mount_table
->conv_to_win32_path (path_copy
, full_path
,
750 dev
, &sym
.mount_flags
);
755 sym
.pc_flags
= pc_flags
;
763 if (iscygdrive_dev (dev
))
766 fileattr
= FILE_ATTRIBUTE_DIRECTORY
767 | FILE_ATTRIBUTE_READONLY
;
770 fileattr
= getfileattr (THIS_path
,
771 sym
.mount_flags
& MOUNT_NOPOSIX
);
776 else if (isdev_dev (dev
))
778 /* Make sure that the path handling goes on as with FH_FS. */
780 else if (isvirtual_dev (dev
))
782 /* FIXME: Calling build_fhandler here is not the right way to
784 fhandler_virtual
*fh
= (fhandler_virtual
*)
785 build_fh_dev (dev
, path_copy
);
786 virtual_ftype_t file_type
;
788 file_type
= virt_none
;
791 file_type
= fh
->exists ();
792 if (file_type
== virt_symlink
793 || file_type
== virt_fdsymlink
)
796 symlen
= sym
.set (fh
->get_filebuf ());
798 else if (file_type
== virt_fsdir
&& dev
== FH_PROCESSFD
)
800 /* FIXME: This is YA bad hack to workaround that
801 we're checking for isvirtual_dev at this point.
802 This should only happen if the file is actually
803 a virtual file, and NOT already if the preceeding
804 path components constitute a virtual file.
806 Anyway, what we do here is this: If the descriptor
807 symlink points to a dir, and if there are trailing
808 path components, it's actually pointing somewhere
809 else. The format_process_fd function returns the
810 full path, resolved symlink plus trailing path
811 components, in its filebuf. This is a POSIX path
812 we know nothing about, so we have to convert it to
813 native again, calling conv_to_win32_path. Since
814 basically nothing happened yet, just copy it over
815 into full_path and jump back to the
816 conv_to_win32_path call. What a mess. */
817 stpcpy (path_copy
, fh
->get_filebuf ());
819 goto retry_fs_via_processfd
;
821 else if (file_type
== virt_none
&& dev
== FH_PROCESSFD
)
823 error
= get_errno ();
837 fileattr
= FILE_ATTRIBUTE_DIRECTORY
;
844 /* Allow open/linkat to do the right thing. */
845 if (opt
& PC_SYM_NOFOLLOW_PROCFD
)
847 opt
&= ~PC_SYM_FOLLOW
;
848 sym
.path_flags
|= PATH_RESOLVE_PROCFD
;
852 goto is_virtual_symlink
;
864 dev
.parse (FH_SOCKET
);
869 /* Access to real file or directory via block device
870 entry in /proc/sys. Convert to real file and go with
873 goto is_fs_via_procsys
;
875 /* Block special device. Convert to a /dev/sd* like
876 block device unless the trailing slash has been
877 requested. In this case, the target is the root
878 directory of the filesystem on this block device.
879 So we convert this to a real file and attach the
883 fileattr
= FILE_ATTRIBUTE_DEVICE
;
885 /* Use a /dev/sd* device number > /dev/sddx.
886 FIXME: Define a new major DEV_ice number. */
887 dev
.parse (DEV_SD_HIGHPART_END
, 9999);
891 strcat (full_path
, "\\");
892 fileattr
|= FILE_ATTRIBUTE_DIRECTORY
;
899 fileattr
= FILE_ATTRIBUTE_DEVICE
;
903 fileattr
= INVALID_FILE_ATTRIBUTES
;
904 goto virtual_component_retry
;
906 if (component
== 0 || dev
!= FH_NETDRIVE
)
907 mount_flags
|= MOUNT_RO
;
910 /* devn should not be a device. If it is, then stop parsing. */
911 else if (dev
!= FH_FS
)
914 mount_flags
= sym
.mount_flags
;
915 path_flags
= sym
.path_flags
;
921 goto out
; /* Found a device. Stop parsing. */
924 /* If path is only a drivename, Windows interprets it as the
925 current working directory on this drive instead of the root
926 dir which is what we want. So we need the trailing backslash
928 if (full_path
[0] && full_path
[1] == ':' && full_path
[2] == '\0')
934 /* If the incoming path was given in DOS notation, always treat
935 it as caseinsensitive,noacl path. This must be set before
936 calling sym.check, otherwise the path is potentially treated
939 sym
.mount_flags
|= MOUNT_NOPOSIX
| MOUNT_NOACL
;
943 symlen
= sym
.check (full_path
, suff
, fs
, conv_handle
);
954 dev
.parse (sym
.major
, sym
.minor
);
957 fileattr
= sym
.fileattr
;
961 if (sym
.path_flags
& PATH_SOCKET
)
968 fileattr
= sym
.fileattr
;
969 #ifdef __WITH_AF_UNIX
970 dev
.parse ((sym
.path_flags
& PATH_REP
) ? FH_UNIX
: FH_LOCAL
);
972 dev
.parse (FH_LOCAL
);
973 #endif /* __WITH_AF_UNIX */
975 mount_flags
= sym
.mount_flags
;
976 path_flags
= sym
.path_flags
;
982 /* Make sure that /dev always exists. */
983 fileattr
= isdev_dev (dev
) ? FILE_ATTRIBUTE_DIRECTORY
985 mount_flags
= sym
.mount_flags
;
986 path_flags
= sym
.path_flags
;
988 else if (isdev_dev (dev
))
990 /* If we're looking for a non-existing file below /dev,
991 make sure that the device type is converted to FH_FS, so
992 that subsequent code handles the file correctly. Unless
993 /dev itself doesn't exist on disk. In that case /dev
994 is handled as virtual filesystem, and virtual filesystems
995 are read-only. The PC_KEEP_HANDLE check allows to check
996 for a call from an informational system call. In that
997 case we just stick to ENOENT, and the device type doesn't
999 if (sym
.error
== ENOENT
&& !(opt
& PC_KEEP_HANDLE
))
1005 /* If symlink.check found an existing non-symlink file, then
1006 it sets the appropriate flag. It also sets any suffix found
1008 if (!sym
.issymlink
&& sym
.fileattr
!= INVALID_FILE_ATTRIBUTES
)
1013 else if (!(sym
.fileattr
& FILE_ATTRIBUTE_DIRECTORY
))
1018 goto out
; // file found
1020 /* Found a symlink if symlen > 0 or short-circuited a native
1021 symlink or junction point if symlen < 0.
1022 If symlen > 0 and component == 0, then the src path itself
1023 was a symlink. If !follow_mode then we're done. Otherwise
1024 we have to insert the path found into the full path that we
1025 are building and perform all of these operations again on the
1026 newly derived path. */
1029 /* if symlen is negativ, the actual native symlink or
1030 junction point is an inner path component. Just fix up
1031 symlen to be positive and don't try any PC_SYM_FOLLOW
1035 else if (component
== 0
1036 && (!(opt
& PC_SYM_FOLLOW
)
1037 || (is_winapi_reparse_point ()
1038 && (opt
& PC_SYM_NOFOLLOW_REP
))))
1040 /* Usually a trailing slash requires to follow a symlink,
1041 even with PC_SYM_NOFOLLOW. The reason is that "foo/"
1042 is equivalent to "foo/." so the symlink is in fact not
1043 the last path component.
1045 PC_SYM_NOFOLLOW_DIR is used to indicate that the
1046 last path component is the target symlink and the
1047 trailing slash is supposed to be ignored. */
1048 if (!need_directory
|| (opt
& PC_SYM_NOFOLLOW_DIR
))
1050 /* last component of path is a symlink. */
1051 set_symlink (symlen
);
1052 /* make sure not to set errno to ENOTDIR. */
1054 if (opt
& PC_SYM_CONTENTS
)
1056 strcpy (THIS_path
, sym
.contents
);
1063 /* Following a symlink we can't trust the collected
1064 filesystem information any longer. */
1066 /* Close handle, if we have any. Otherwise we're collecting
1067 handles while following symlinks. */
1068 conv_handle
.close ();
1071 else if (sym
.error
&& sym
.error
!= ENOENT
)
1076 /* No existing file found. */
1078 virtual_component_retry
:
1079 /* Find the new "tail" of the path, e.g. in '/for/bar/baz',
1080 /baz is the tail. */
1081 if (tail
!= path_end
)
1083 while (--tail
> path_copy
+ 1 && *tail
!= '/') {}
1084 /* Exit loop if there is no tail or we are at the
1085 beginning of a UNC path */
1086 if (tail
<= path_copy
+ 1)
1087 goto out
; // all done
1089 /* Haven't found an existing pathname component yet.
1090 Pinch off the tail and try again. */
1095 /* Arrive here if above loop detected a symlink. */
1096 if (++loop
> SYMLOOP_MAX
)
1098 error
= ELOOP
; // Eep.
1102 /* Place the link content, possibly with head and/or tail,
1106 if (isabspath (sym
.contents
))
1107 headptr
= tmp_buf
; /* absolute path */
1110 /* Copy the first part of the path (with ending /) and point to
1112 char *prevtail
= tail
;
1113 while (--prevtail
> path_copy
&& *prevtail
!= '/') {}
1114 int headlen
= prevtail
- path_copy
+ 1;;
1115 memcpy (tmp_buf
, path_copy
, headlen
);
1116 headptr
= &tmp_buf
[headlen
];
1119 /* Make sure there is enough space */
1120 if (headptr
+ symlen
>= tmp_buf
+ (2 * NT_MAX_PATH
))
1123 error
= ENAMETOOLONG
;
1124 set_path ("::ENAMETOOLONG::");
1128 /* Copy the symlink contents to the end of tmp_buf.
1130 for (char *p
= sym
.contents
; *p
; p
++)
1131 *headptr
++ = *p
== '\\' ? '/' : *p
;
1134 /* Copy any tail component (with the 0) */
1135 if (tail
++ < path_end
)
1137 /* Add a slash if needed. There is space. */
1138 if (*(headptr
- 1) != '/')
1140 int taillen
= path_end
- tail
+ 1;
1141 if (headptr
+ taillen
> tmp_buf
+ (2 * NT_MAX_PATH
))
1143 memcpy (headptr
, tail
, taillen
);
1146 /* Evaluate everything all over again. */
1150 if (!(opt
& PC_SYM_CONTENTS
))
1154 set_path (THIS_path
);
1156 add_ext_from_sym (sym
);
1157 if (dev
== FH_NETDRIVE
&& component
)
1159 /* This case indicates a non-existant resp. a non-retrievable
1160 share. This happens for instance if the share is a printer.
1161 In this case the path must not be treated like a FH_NETDRIVE,
1162 but like a FH_FS instead, so the usual open call for files
1166 else if (isproc_dev (dev
) && fileattr
== INVALID_FILE_ATTRIBUTES
)
1168 /* FIXME: Usually we don't set error to ENOENT if a file doesn't
1169 exist. This is typically indicated by the fileattr content.
1170 So, why here? The downside is that cygwin_conv_path just gets
1171 an error for these paths so it reports the error back to the
1172 application. Unlike in all other cases of non-existant files,
1173 for which check doesn't set error, so cygwin_conv_path just
1174 returns the path, as intended. */
1178 else if (!need_directory
|| error
)
1179 /* nothing to do */;
1180 else if (fileattr
== INVALID_FILE_ATTRIBUTES
)
1181 /* Reattach trailing dirsep in native path. */
1182 strcat (modifiable_path (), "\\");
1183 else if (fileattr
& FILE_ATTRIBUTE_DIRECTORY
)
1184 path_flags
&= ~PATH_SYMLINK
;
1187 debug_printf ("%s is a non-directory", path
);
1192 /* Restore last path component */
1193 if (tail
< path_end
&& tail
> path_copy
+ 1)
1198 /* If FS hasn't been checked already in symlink_info::check,
1200 if (fs
.inited ()|| fs
.update (get_nt_native_path (), NULL
))
1202 /* Incoming DOS paths are treated like DOS paths in native
1203 Windows applications. No ACLs, just default settings. */
1205 fs
.has_acls (false);
1206 debug_printf ("this->path(%s), has_acls(%d)",
1207 path
, fs
.has_acls ());
1208 /* CV: We could use this->has_acls() but I want to make sure that
1209 we don't forget that the MOUNT_NOACL flag must be taken into
1211 if (!(mount_flags
& MOUNT_NOACL
) && fs
.has_acls ())
1212 set_exec (0); /* We really don't know if this is executable or
1213 not here but set it to not executable since
1214 it will be figured out later by anything
1215 which cares about this. */
1217 /* If the FS has been found to have unreliable inodes, note
1218 that in mount_flags. */
1219 if (!fs
.hasgood_inode ())
1220 mount_flags
|= MOUNT_IHASH
;
1221 /* If the OS is caseinsensitive or the FS is caseinsensitive,
1222 don't handle path casesensitive. */
1223 if (cygwin_shared
->obcaseinsensitive
|| fs
.caseinsensitive ())
1224 mount_flags
|= MOUNT_NOPOSIX
;
1225 caseinsensitive
= (mount_flags
& MOUNT_NOPOSIX
)
1226 ? OBJ_CASE_INSENSITIVE
: 0;
1227 if (exec_state () != dont_know_if_executable
)
1231 else if (issymlink () || issocket ())
1234 /* FIXME: bad hack alert!!! We need a better solution */
1235 if (!strncmp (path_copy
, MQ_PATH
, MQ_LEN
) && path_copy
[MQ_LEN
])
1236 dev
.parse (FH_MQUEUE
);
1239 if (opt
& PC_NOFULL
)
1243 mkrelpath (this->modifiable_path (), !!caseinsensitive
);
1244 /* Invalidate wide_path so that wide relpath can be created
1245 in later calls to get_nt_native_path or get_wide_win32_path. */
1252 size_t n
= strlen (this->path
);
1253 /* Do not add trailing \ to UNC device names like \\.\a: */
1254 if (this->path
[n
- 1] != '\\' &&
1255 (strncmp (this->path
, "\\\\.\\", 4) != 0))
1257 this->modifiable_path ()[n
] = '\\';
1258 this->modifiable_path ()[n
+ 1] = '\0';
1264 path_flags
|= PATH_OPEN
;
1267 path_flags
|= PATH_CTTY
;
1270 set_posix (path_copy
);
1275 last_path_conv
= *this;
1276 strcpy (last_src
, src
);
1297 path_conv::serialize (HANDLE h
, unsigned int &n
) const
1300 size_t nlen
= 0, plen
= 0;
1304 nlen
= strlen (path
) + 1;
1306 plen
= strlen (posix_path
) + 1;
1307 n
= sizeof (pc_flat
) + nlen
+ plen
;
1308 pcf
= (pc_flat
*) cmalloc (HEAP_COMMUNE
, n
);
1314 memcpy ((void *) &pcf
->pc
, this, sizeof *this);
1316 pcf
->name_len
= nlen
;
1317 pcf
->posix_len
= plen
;
1320 p
= stpcpy (p
, path
) + 1;
1322 stpcpy (p
, posix_path
);
1327 path_conv::deserialize (void *bufp
)
1329 pc_flat
*pcf
= (pc_flat
*) bufp
;
1333 memcpy ((void *) this, &pcf
->pc
, sizeof *this);
1334 wide_path
= uni_path
.Buffer
= NULL
;
1335 uni_path
.MaximumLength
= uni_path
.Length
= 0;
1336 path
= posix_path
= NULL
;
1345 dev
.parse (pcf
->pc
.dev
);
1351 path_conv::~path_conv ()
1355 cfree ((void *) posix_path
);
1360 cfree (modifiable_path ());
1368 close_conv_handle ();
1372 path_conv::is_binary ()
1375 PWCHAR bintest
= tp
.w_get ();
1378 return GetBinaryTypeW (get_wide_win32_path (bintest
), &bin
)
1379 && (bin
== SCS_32BIT_BINARY
|| bin
== SCS_64BIT_BINARY
);
1382 /* Helper function to fill the fai datastructure for a file. */
1384 file_get_fai (HANDLE h
, PFILE_ALL_INFORMATION pfai
)
1389 /* Some FSes (Netapps) don't implement FileNetworkOpenInformation. */
1390 status
= NtQueryInformationFile (h
, &io
, pfai
, sizeof *pfai
,
1391 FileAllInformation
);
1392 if (likely (status
== STATUS_BUFFER_OVERFLOW
))
1393 status
= STATUS_SUCCESS
;
1394 /* Filesystems with broken FileAllInformation exist, too. See the thread
1395 starting with https://cygwin.com/ml/cygwin/2016-07/msg00350.html. */
1396 else if (!NT_SUCCESS (status
) && status
!= STATUS_ACCESS_DENIED
)
1398 memset (pfai
, 0, sizeof *pfai
);
1399 status
= NtQueryInformationFile (h
, &io
, &pfai
->BasicInformation
,
1400 sizeof pfai
->BasicInformation
,
1401 FileBasicInformation
);
1402 if (NT_SUCCESS (status
))
1404 /* The return value of FileInternalInformation is largely ignored.
1405 We only make absolutely sure the inode number is set to 0 in
1407 status
= NtQueryInformationFile (h
, &io
, &pfai
->InternalInformation
,
1408 sizeof pfai
->InternalInformation
,
1409 FileInternalInformation
);
1410 if (!NT_SUCCESS (status
))
1411 pfai
->InternalInformation
.IndexNumber
.QuadPart
= 0LL;
1412 status
= NtQueryInformationFile (h
, &io
, &pfai
->StandardInformation
,
1413 sizeof pfai
->StandardInformation
,
1414 FileStandardInformation
);
1420 /* Normalize a Win32 path.
1421 /'s are converted to \'s in the process.
1422 All duplicate \'s, except for 2 leading \'s, are deleted.
1424 The result is 0 for success, or an errno error value.
1425 FIXME: A lot of this should be mergeable with the POSIX critter. */
1427 normalize_win32_path (const char *src
, char *dst
, char *&tail
)
1429 const char *src_start
= src
;
1430 const char *dst_start
= dst
;
1431 bool beg_src_slash
= isdirsep (src
[0]);
1434 /* Skip Win32 long path name prefix and NT object directory prefix. */
1435 if (beg_src_slash
&& (src
[1] == '?' || isdirsep (src
[1]))
1436 && src
[2] == '?' && isdirsep (src
[3]))
1439 if (isdrive (src
) && (isdirsep (src
[2]) || !src
[2]))
1440 beg_src_slash
= false;
1441 else if (!strncmp (src
, "UNC", 3) && isdirsep (src
[3]))
1442 /* native UNC path */
1443 src
+= 2; /* Fortunately the first char is not copied... */
1447 if (beg_src_slash
&& isdirsep (src
[1]))
1449 if (isdirsep (src
[2]))
1451 /* More than two slashes are just folded into one. */
1453 while (isdirsep (src
[1]))
1458 /* Two slashes start a network or device path. */
1461 if (src
[1] == '.' && isdirsep (src
[2]))
1469 /* If backslash is missing in src, add one. */
1470 if (!isdirsep (src
[0]))
1473 if (tail
== dst_start
)
1477 /* Always convert drive letter to uppercase for case sensitivity. */
1478 *tail
++ = cyg_toupper (*src
++);
1481 /* If backslash is missing in src, add one. */
1482 if (!isdirsep (src
[0]))
1485 else if (*src
!= '/')
1487 /* Make sure dst points to the rightmost backslash which must not
1488 be backtracked over during ".." evaluation. This is either
1489 the backslash after the network path prefix (i.e. "\\") or
1490 the backslash after a drive letter (i.e. C:\"). */
1493 tail
+= cygheap
->cwd
.get_drive (dst
);
1494 /* network path, drive == '\\\\'? Decrement tail to avoid
1495 triple backslash in output. */
1500 else if (cygheap
->cwd
.get (dst
, 0))
1502 tail
= strchr (tail
, '\0');
1503 if (tail
[-1] != '\\')
1510 return get_errno ();
1514 /* At this point dst points to the first backslash, even if it only gets
1515 written in the first iteration of the following loop. Backing up to
1516 handle ".." components can not underrun that border (thus avoiding
1517 subsequent buffer underruns with fatal results). */
1520 /* Strip duplicate /'s. */
1521 if (isdirsep (src
[0]) && isdirsep (src
[1]))
1524 else if (src
[0] == '.' && isdirsep (src
[1])
1525 && (src
== src_start
|| isdirsep (src
[-1])))
1528 /* Skip /'s to the next path component. */
1529 while (isdirsep (*src
))
1533 /* Backup if "..". */
1534 else if (src
[0] == '.' && src
[1] == '.' && tail
[-1] == '\\')
1536 if (!isdirsep (src
[2]) && src
[2] != '\0')
1540 /* Back up over /, but not if it's the first one. */
1543 /* Now back up to the next /. */
1544 while (tail
> dst
+ 1 && tail
[-1] != '\\' && tail
[-2] != ':')
1547 /* Skip /'s to the next path component. */
1548 while (isdirsep (*src
))
1552 /* Otherwise, add char to result. */
1561 if ((tail
- dst
) >= NT_MAX_PATH
)
1562 return ENAMETOOLONG
;
1564 if (tail
> dst
+ 1 && tail
[-1] == '.' && tail
[-2] == '\\')
1567 debug_printf ("%s = normalize_win32_path (%s)", dst_start
, src_start
);
1571 /* Various utilities. */
1573 /* nofinalslash: Remove trailing / and \ from SRC (except for the
1574 first one). It is ok for src == dst. */
1577 nofinalslash (const char *src
, char *dst
)
1579 int len
= strlen (src
);
1581 memcpy (dst
, src
, len
+ 1);
1582 while (len
> 1 && isdirsep (dst
[--len
]))
1586 /* conv_path_list: Convert a list of path names to/from Win32/POSIX. */
1589 conv_path_list (const char *src
, char *dst
, size_t size
,
1590 cygwin_conv_path_t what
)
1593 char src_delim
, dst_delim
;
1595 bool env_cvt
= false;
1597 if (what
== (cygwin_conv_path_t
) ENV_CVT
)
1599 what
= CCP_WIN_A_TO_POSIX
| CCP_RELATIVE
;
1602 if ((what
& CCP_CONVTYPE_MASK
) == CCP_WIN_A_TO_POSIX
)
1614 len
= strlen (src
) + 1;
1615 if (len
<= NT_MAX_PATH
* sizeof (WCHAR
))
1616 srcbuf
= (char *) tp
.w_get ();
1618 srcbuf
= (char *) alloca (len
);
1622 bool saw_empty
= false;
1625 char *srcpath
= srcbuf
;
1626 char *s
= strccpy (srcpath
, &src
, src_delim
);
1627 size_t len
= s
- srcpath
;
1628 if (len
>= NT_MAX_PATH
)
1633 /* Paths in Win32 path lists in the environment (%Path%), are often
1634 enclosed in quotes (usually paths with spaces). Trailing backslashes
1635 are common, too. Remove them. */
1638 if (*srcpath
== '"')
1644 while (len
&& s
[-1] == '\\')
1653 err
= cygwin_conv_path (what
, srcpath
, d
, size
- (d
- dst
));
1655 else if ((what
& CCP_CONVTYPE_MASK
) == CCP_POSIX_TO_WIN_A
)
1658 err
= cygwin_conv_path (what
, ".", d
, size
- (d
- dst
));
1668 d
= strchr (d
, '\0');
1682 /********************** Symbolic Link Support **************************/
1684 /* Create a symlink from FROMPATH to TOPATH. */
1687 symlink (const char *oldpath
, const char *newpath
)
1689 path_conv win32_newpath
;
1693 if (!*oldpath
|| !*newpath
)
1699 /* Trailing dirsep is a no-no, only errno differs. */
1700 bool has_trailing_dirsep
= isdirsep (newpath
[strlen (newpath
) - 1]);
1701 win32_newpath
.check (newpath
,
1702 PC_SYM_NOFOLLOW
| PC_SYM_NOFOLLOW_DIR
| PC_POSIX
,
1705 if (win32_newpath
.error
|| has_trailing_dirsep
)
1707 set_errno (win32_newpath
.error
?:
1708 win32_newpath
.exists () ? EEXIST
: ENOENT
);
1712 return symlink_worker (oldpath
, win32_newpath
, false);
1714 __except (EFAULT
) {}
1720 symlink_nfs (const char *oldpath
, path_conv
&win32_newpath
)
1722 /* On NFS, create symlinks by calling NtCreateFile with an EA of type
1723 NfsSymlinkTargetName containing ... the symlink target name. */
1725 PFILE_FULL_EA_INFORMATION pffei
;
1728 OBJECT_ATTRIBUTES attr
;
1731 pffei
= (PFILE_FULL_EA_INFORMATION
) tp
.w_get ();
1732 pffei
->NextEntryOffset
= 0;
1734 pffei
->EaNameLength
= sizeof (NFS_SYML_TARGET
) - 1;
1735 char *EaValue
= stpcpy (pffei
->EaName
, NFS_SYML_TARGET
) + 1;
1736 pffei
->EaValueLength
= sizeof (WCHAR
) *
1737 (sys_mbstowcs ((PWCHAR
) EaValue
, NT_MAX_PATH
, oldpath
) - 1);
1738 status
= NtCreateFile (&fh
, FILE_WRITE_DATA
| FILE_WRITE_EA
| SYNCHRONIZE
,
1739 win32_newpath
.get_object_attr (attr
, sec_none_nih
),
1740 &io
, NULL
, FILE_ATTRIBUTE_SYSTEM
,
1741 FILE_SHARE_VALID_FLAGS
, FILE_CREATE
,
1742 FILE_SYNCHRONOUS_IO_NONALERT
1743 | FILE_OPEN_FOR_BACKUP_INTENT
,
1744 pffei
, NT_MAX_PATH
* sizeof (WCHAR
));
1745 if (!NT_SUCCESS (status
))
1747 __seterrno_from_nt_status (status
);
1754 /* Count backslashes between s and e. */
1756 cnt_bs (PWCHAR s
, PWCHAR e
)
1766 #ifndef SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
1767 #define SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE 2
1771 symlink_native (const char *oldpath
, path_conv
&win32_newpath
)
1774 path_conv win32_oldpath
;
1775 PUNICODE_STRING final_oldpath
, final_newpath
;
1776 UNICODE_STRING final_oldpath_buf
;
1779 if (isabspath (oldpath
))
1781 win32_oldpath
.check (oldpath
, PC_SYM_NOFOLLOW
, stat_suffixes
);
1782 final_oldpath
= win32_oldpath
.get_nt_native_path ();
1786 /* The symlink target is relative to the directory in which
1787 the symlink gets created, not relative to the cwd. Therefore
1788 we have to mangle the path quite a bit before calling path_conv. */
1789 ssize_t len
= strrchr (win32_newpath
.get_posix (), '/')
1790 - win32_newpath
.get_posix () + 1;
1791 char *absoldpath
= tp
.t_get ();
1792 stpcpy (stpncpy (absoldpath
, win32_newpath
.get_posix (), len
),
1794 win32_oldpath
.check (absoldpath
, PC_SYM_NOFOLLOW
, stat_suffixes
);
1796 /* Try hard to keep Windows symlink path relative. */
1798 /* 1. Find common path prefix. Skip leading \\?\, but take pre-increment
1799 of the following loop into account. */
1800 PWCHAR c_old
= win32_oldpath
.get_nt_native_path ()->Buffer
+ 3;
1801 PWCHAR c_new
= win32_newpath
.get_nt_native_path ()->Buffer
+ 3;
1802 /* Windows compatible == always check case insensitive. */
1803 while (towupper (*++c_old
) == towupper (*++c_new
))
1805 /* The last component could share a common prefix, so make sure we end
1806 up on the first char after the last common backslash. */
1807 while (c_old
[-1] != L
'\\')
1810 /* 2. Check if prefix is long enough. The prefix must at least points to
1811 a complete device: \\?\X:\ or \\?\UNC\server\share\ are the minimum
1812 prefix strings. We start counting behind the \\?\ for speed. */
1813 int num
= cnt_bs (win32_oldpath
.get_nt_native_path ()->Buffer
+ 4, c_old
);
1814 if (num
< 1 /* locale drive. */
1815 || (win32_oldpath
.get_nt_native_path ()->Buffer
[5] != L
':'
1816 && num
< 3)) /* UNC path. */
1818 /* 3a. No valid common path prefix: Create absolute symlink. */
1819 final_oldpath
= win32_oldpath
.get_nt_native_path ();
1823 /* 3b. Common path prefx. Count number of additional directories
1824 in symlink's path, and prepend as much ".." path components
1825 to the target path. */
1826 PWCHAR e_new
= win32_newpath
.get_nt_native_path ()->Buffer
1827 + win32_newpath
.get_nt_native_path ()->Length
1829 num
= cnt_bs (c_new
, e_new
);
1830 final_oldpath
= &final_oldpath_buf
;
1831 final_oldpath
->Buffer
= tp
.w_get ();
1832 PWCHAR e_old
= final_oldpath
->Buffer
;
1834 e_old
= wcpcpy (e_old
, L
"..\\");
1835 wcpcpy (e_old
, c_old
);
1838 /* If the symlink target doesn't exist, don't create native symlink.
1839 Otherwise the directory flag in the symlink is potentially wrong
1840 when the target comes into existence, and native tools will fail.
1841 This is so screwball. This is no problem on AFS, fortunately. */
1842 if (!win32_oldpath
.exists () && !win32_oldpath
.fs_is_afs ())
1844 SetLastError (ERROR_FILE_NOT_FOUND
);
1847 /* Don't allow native symlinks to Cygwin special files. However, the
1848 caller shoud know because this case shouldn't be covered by the
1849 default "nativestrict" behaviour, so we use a special return code. */
1850 if (win32_oldpath
.isspecial ())
1852 /* Convert native paths to Win32 UNC paths. */
1853 final_newpath
= win32_newpath
.get_nt_native_path ();
1854 final_newpath
->Buffer
[1] = L
'\\';
1855 /* oldpath may be relative. Make sure to convert only absolute paths
1857 if (final_oldpath
->Buffer
[0] == L
'\\')
1859 /* Starting with Windows 8.1, the ShellExecuteW function does not
1860 handle the long path prefix correctly for symlink targets. Thus,
1861 we create simple short paths < MAX_PATH without long path prefix. */
1862 if (RtlEqualUnicodePathPrefix (final_oldpath
, &ro_u_uncp
, TRUE
)
1863 && final_oldpath
->Length
< (MAX_PATH
+ 6) * sizeof (WCHAR
))
1865 final_oldpath
->Buffer
+= 6;
1866 final_oldpath
->Buffer
[0] = L
'\\';
1868 else if (final_oldpath
->Length
< (MAX_PATH
+ 4) * sizeof (WCHAR
))
1869 final_oldpath
->Buffer
+= 4;
1870 else /* Stick to long path, fix native prefix for Win32 API calls. */
1871 final_oldpath
->Buffer
[1] = L
'\\';
1873 /* Try to create native symlink. */
1874 flags
= win32_oldpath
.isdir () ? SYMBOLIC_LINK_FLAG_DIRECTORY
: 0;
1875 if (wincap
.has_unprivileged_createsymlink ())
1876 flags
|= SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
;
1877 if (!CreateSymbolicLinkW (final_newpath
->Buffer
, final_oldpath
->Buffer
,
1880 /* Repair native newpath, we still need it. */
1881 final_newpath
->Buffer
[1] = L
'?';
1887 #ifndef IO_REPARSE_TAG_LX_SYMLINK
1888 #define IO_REPARSE_TAG_LX_SYMLINK (0xa000001d)
1891 typedef struct _REPARSE_LX_SYMLINK_BUFFER
1894 WORD ReparseDataLength
;
1897 DWORD FileType
; /* Take member name with a grain of salt. Value is
1898 apparently always 2 for symlinks. */
1899 char PathBuffer
[1];/* POSIX path as given to symlink(2).
1900 Path is not \0 terminated.
1901 Length is ReparseDataLength - sizeof (FileType).
1903 Chars given in incompatible codesets, e. g. umlauts
1904 in ISO-8859-x, are converted to the Unicode
1905 REPLACEMENT CHARACTER 0xfffd == \xef\xbf\bd */
1906 } LxSymlinkReparseBuffer
;
1907 } REPARSE_LX_SYMLINK_BUFFER
,*PREPARSE_LX_SYMLINK_BUFFER
;
1910 symlink_wsl (const char *oldpath
, path_conv
&win32_newpath
)
1913 PREPARSE_LX_SYMLINK_BUFFER rpl
= (PREPARSE_LX_SYMLINK_BUFFER
) tp
.c_get ();
1914 char *path_buf
= rpl
->LxSymlinkReparseBuffer
.PathBuffer
;
1915 const int max_pathlen
= MAXIMUM_REPARSE_DATA_BUFFER_SIZE
1916 - offsetof (REPARSE_LX_SYMLINK_BUFFER
,
1917 LxSymlinkReparseBuffer
.PathBuffer
);
1918 PWCHAR utf16
= tp
.w_get ();
1921 OBJECT_ATTRIBUTES attr
;
1925 rpl
->ReparseTag
= IO_REPARSE_TAG_LX_SYMLINK
;
1927 rpl
->LxSymlinkReparseBuffer
.FileType
= 2;
1928 /* Convert cygdrive prefix to "/mnt" for WSL compatibility, but only if
1929 cygdrive prefix is not "/", otherwise suffer random "/mnt" symlinks... */
1930 if (mount_table
->cygdrive_len
> 1
1931 && path_prefix_p (mount_table
->cygdrive
, oldpath
,
1932 mount_table
->cygdrive_len
, false))
1933 stpcpy (stpcpy (path_buf
, "/mnt"),
1934 oldpath
+ mount_table
->cygdrive_len
- 1);
1936 *stpncpy (path_buf
, oldpath
, max_pathlen
) = '\0';
1937 /* Convert target path to UTF-16 and then back to UTF-8 to make sure the
1938 WSL symlink is in UTF-8, independent of the current Cygwin codeset. */
1939 sys_mbstowcs (utf16
, NT_MAX_PATH
, path_buf
);
1940 len
= WideCharToMultiByte (CP_UTF8
, 0, utf16
, -1, path_buf
, max_pathlen
,
1942 /* Length is omitting trailing \0. */
1943 rpl
->ReparseDataLength
= sizeof (DWORD
) + len
- 1;
1944 /* Create reparse point. */
1945 status
= NtCreateFile (&fh
, DELETE
| FILE_GENERIC_WRITE
1946 | READ_CONTROL
| WRITE_DAC
,
1947 win32_newpath
.get_object_attr (attr
, sec_none_nih
),
1948 &io
, NULL
, FILE_ATTRIBUTE_NORMAL
,
1949 FILE_SHARE_VALID_FLAGS
, FILE_CREATE
,
1950 FILE_SYNCHRONOUS_IO_NONALERT
1951 | FILE_NON_DIRECTORY_FILE
1952 | FILE_OPEN_FOR_BACKUP_INTENT
1953 | FILE_OPEN_REPARSE_POINT
,
1955 if (!NT_SUCCESS (status
))
1957 SetLastError (RtlNtStatusToDosError (status
));
1960 set_created_file_access (fh
, win32_newpath
, S_IFLNK
| STD_RBITS
| STD_WBITS
);
1961 status
= NtFsControlFile (fh
, NULL
, NULL
, NULL
, &io
, FSCTL_SET_REPARSE_POINT
,
1962 (LPVOID
) rpl
, REPARSE_DATA_BUFFER_HEADER_SIZE
1963 + rpl
->ReparseDataLength
,
1965 if (!NT_SUCCESS (status
))
1967 SetLastError (RtlNtStatusToDosError (status
));
1968 FILE_DISPOSITION_INFORMATION fdi
= { TRUE
};
1969 status
= NtSetInformationFile (fh
, &io
, &fdi
, sizeof fdi
,
1970 FileDispositionInformation
);
1972 if (!NT_SUCCESS (status
))
1973 debug_printf ("Setting delete dispostion failed, status = %y", status
);
1981 symlink_worker (const char *oldpath
, path_conv
&win32_newpath
, bool isdevice
)
1989 /* POSIX says that empty 'newpath' is invalid input while empty
1990 'oldpath' is valid -- it's symlink resolver job to verify if
1991 symlink contents point to existing filesystem object */
1994 if (strlen (oldpath
) > SYMLINK_MAX
)
1996 set_errno (ENAMETOOLONG
);
2000 /* Default symlink type is determined by global allow_winsymlinks
2001 variable. Device files are always shortcuts. */
2002 wsym_type
= isdevice
? WSYM_lnk
: allow_winsymlinks
;
2003 /* NFS has its own, dedicated way to create symlinks. */
2004 if (win32_newpath
.fs_is_nfs ())
2005 wsym_type
= WSYM_nfs
;
2006 /* MVFS doesn't handle the SYSTEM DOS attribute, but it handles the R/O
2007 attribute. Therefore we create symlinks on MVFS always as shortcuts. */
2008 else if (win32_newpath
.fs_is_mvfs ())
2009 wsym_type
= WSYM_lnk
;
2010 /* AFS only supports native symlinks. */
2011 else if (win32_newpath
.fs_is_afs ())
2012 wsym_type
= WSYM_nativestrict
;
2013 /* Don't try native symlinks on FSes not supporting reparse points. */
2014 else if ((wsym_type
== WSYM_native
|| wsym_type
== WSYM_nativestrict
)
2015 && !(win32_newpath
.fs_flags () & FILE_SUPPORTS_REPARSE_POINTS
))
2016 wsym_type
= WSYM_default
;
2018 /* Attach .lnk suffix when shortcut is requested. */
2019 if (wsym_type
== WSYM_lnk
&& !win32_newpath
.exists ()
2020 && (isdevice
|| !win32_newpath
.fs_is_nfs ()))
2022 char *newplnk
= tp
.c_get ();
2023 stpcpy (stpcpy (newplnk
, win32_newpath
.get_posix ()), ".lnk");
2024 win32_newpath
.check (newplnk
, PC_SYM_NOFOLLOW
| PC_POSIX
);
2027 if (win32_newpath
.error
)
2029 set_errno (win32_newpath
.error
);
2033 syscall_printf ("symlink (%s, %S) wsym_type %d", oldpath
,
2034 win32_newpath
.get_nt_native_path (), wsym_type
);
2036 if ((!isdevice
&& win32_newpath
.exists ())
2037 || (win32_newpath
.isdevice () && !win32_newpath
.is_fs_special ()))
2043 /* Handle NFS, native symlinks and WSL symlinks in their own functions. */
2047 res
= symlink_nfs (oldpath
, win32_newpath
);
2050 case WSYM_nativestrict
:
2051 res
= symlink_native (oldpath
, win32_newpath
);
2054 /* Strictly native? Too bad, unless the target is a Cygwin
2056 if (res
== -1 && wsym_type
== WSYM_nativestrict
)
2061 /* Otherwise, fall back to default symlink type. */
2062 wsym_type
= WSYM_default
;
2065 if (win32_newpath
.fs_flags () & FILE_SUPPORTS_REPARSE_POINTS
)
2067 res
= symlink_wsl (oldpath
, win32_newpath
);
2071 /* On FSes not supporting reparse points, or in case of an error
2072 creating the WSL symlink, fall back to creating the plain old
2073 SYSTEM file symlink. */
2074 wsym_type
= WSYM_sysfile
;
2080 if (wsym_type
== WSYM_lnk
)
2082 path_conv win32_oldpath
;
2083 ITEMIDLIST
*pidl
= NULL
;
2084 size_t full_len
= 0;
2085 unsigned short oldpath_len
, desc_len
, relpath_len
, pidl_len
= 0;
2086 char desc
[MAX_PATH
+ 1], *relpath
;
2090 /* First create an IDLIST to learn how big our shortcut is
2094 /* The symlink target is relative to the directory in which the
2095 symlink gets created, not relative to the cwd. Therefore we
2096 have to mangle the path quite a bit before calling path_conv.*/
2097 if (isabspath (oldpath
))
2098 win32_oldpath
.check (oldpath
,
2103 len
= strrchr (win32_newpath
.get_posix (), '/')
2104 - win32_newpath
.get_posix () + 1;
2105 char *absoldpath
= tp
.t_get ();
2106 stpcpy (stpncpy (absoldpath
, win32_newpath
.get_posix (),
2109 win32_oldpath
.check (absoldpath
, PC_SYM_NOFOLLOW
,
2112 if (SUCCEEDED (SHGetDesktopFolder (&psl
)))
2114 WCHAR wc_path
[win32_oldpath
.get_wide_win32_path_len () + 1];
2115 win32_oldpath
.get_wide_win32_path (wc_path
);
2116 /* Amazing but true: Even though the ParseDisplayName method
2117 takes a wide char path name, it does not understand the
2118 Win32 prefix for long pathnames! So we have to tack off
2119 the prefix and convert the path to the "normal" syntax
2120 for ParseDisplayName. */
2121 PWCHAR wc
= wc_path
+ 4;
2122 if (wc
[1] != L
':') /* native UNC path */
2125 if (SUCCEEDED (res
= psl
->ParseDisplayName (NULL
, NULL
, wc
,
2131 for (p
= pidl
; p
->mkid
.cb
> 0;
2132 p
= (ITEMIDLIST
*)((char *) p
+ p
->mkid
.cb
))
2134 pidl_len
= (char *) p
- (char *) pidl
+ 2;
2139 /* Compute size of shortcut file. */
2140 full_len
= sizeof (win_shortcut_hdr
);
2142 full_len
+= sizeof (unsigned short) + pidl_len
;
2143 oldpath_len
= strlen (oldpath
);
2144 /* Unfortunately the length of the description is restricted to a
2145 length of 2000 bytes. We don't want to add considerations for
2146 the different lengths and even 2000 bytes is not enough for long
2147 path names. So what we do here is to set the description to the
2148 POSIX path only if the path is not longer than MAX_PATH characters.
2149 We append the full path name after the regular shortcut data
2150 (see below), which works fine with Windows Explorer as well
2151 as older Cygwin versions (as long as the whole file isn't bigger
2152 than 8K). The description field is only used for backward
2153 compatibility to older Cygwin versions and those versions are
2154 not capable of handling long path names anyway. */
2155 desc_len
= stpcpy (desc
, oldpath_len
> MAX_PATH
2156 ? "[path too long]" : oldpath
) - desc
;
2157 full_len
+= sizeof (unsigned short) + desc_len
;
2158 /* Devices get the oldpath string unchanged as relative path. */
2161 relpath_len
= oldpath_len
;
2162 stpcpy (relpath
= tp
.c_get (), oldpath
);
2166 relpath_len
= strlen (win32_oldpath
.get_win32 ());
2167 stpcpy (relpath
= tp
.c_get (), win32_oldpath
.get_win32 ());
2169 full_len
+= sizeof (unsigned short) + relpath_len
;
2170 full_len
+= sizeof (unsigned short) + oldpath_len
;
2171 /* 1 byte more for trailing 0 written by stpcpy. */
2172 if (full_len
< NT_MAX_PATH
* sizeof (WCHAR
))
2175 buf
= (char *) alloca (full_len
+ 1);
2177 /* Create shortcut header */
2178 win_shortcut_hdr
*shortcut_header
= (win_shortcut_hdr
*) buf
;
2179 memset (shortcut_header
, 0, sizeof *shortcut_header
);
2180 shortcut_header
->size
= sizeof *shortcut_header
;
2181 shortcut_header
->magic
= GUID_shortcut
;
2182 shortcut_header
->flags
= (WSH_FLAG_DESC
| WSH_FLAG_RELPATH
);
2184 shortcut_header
->flags
|= WSH_FLAG_IDLIST
;
2185 shortcut_header
->run
= SW_NORMAL
;
2186 cp
= buf
+ sizeof (win_shortcut_hdr
);
2191 *(unsigned short *)cp
= pidl_len
;
2192 memcpy (cp
+= 2, pidl
, pidl_len
);
2194 CoTaskMemFree (pidl
);
2197 /* Create description */
2198 *(unsigned short *)cp
= desc_len
;
2199 cp
= stpcpy (cp
+= 2, desc
);
2201 /* Create relpath */
2202 *(unsigned short *)cp
= relpath_len
;
2203 cp
= stpcpy (cp
+= 2, relpath
);
2205 /* Append the POSIX path after the regular shortcut data for
2206 the long path support. */
2207 unsigned short *plen
= (unsigned short *) cp
;
2209 *(PWCHAR
) cp
= 0xfeff; /* BOM */
2211 *plen
= sys_mbstowcs ((PWCHAR
) cp
, NT_MAX_PATH
, oldpath
)
2215 else /* wsym_type == WSYM_sysfile */
2217 /* Default technique creating a symlink. */
2219 cp
= stpcpy (buf
, SYMLINK_COOKIE
);
2220 *(PWCHAR
) cp
= 0xfeff; /* BOM */
2222 /* Note that the terminating nul is written. */
2223 cp
+= sys_mbstowcs ((PWCHAR
) cp
, NT_MAX_PATH
, oldpath
)
2227 OBJECT_ATTRIBUTES attr
;
2233 access
= DELETE
| FILE_GENERIC_WRITE
;
2234 if (isdevice
&& win32_newpath
.exists ())
2236 status
= NtOpenFile (&fh
, FILE_WRITE_ATTRIBUTES
,
2237 win32_newpath
.get_object_attr (attr
,
2239 &io
, 0, FILE_OPEN_FOR_BACKUP_INTENT
);
2240 if (!NT_SUCCESS (status
))
2242 __seterrno_from_nt_status (status
);
2245 status
= NtSetAttributesFile (fh
, FILE_ATTRIBUTE_NORMAL
);
2247 if (!NT_SUCCESS (status
))
2249 __seterrno_from_nt_status (status
);
2253 else if (!isdevice
&& win32_newpath
.has_acls ()
2254 && !win32_newpath
.isremote ())
2255 /* If the filesystem supports ACLs, we will overwrite the DACL after the
2256 call to NtCreateFile. This requires a handle with READ_CONTROL and
2257 WRITE_DAC access, otherwise get_file_sd and set_file_sd both have to
2258 open the file again.
2259 FIXME: On remote NTFS shares open sometimes fails because even the
2260 creator of the file doesn't have the right to change the DACL.
2261 I don't know what setting that is or how to recognize such a share,
2262 so for now we don't request WRITE_DAC on remote drives. */
2263 access
|= READ_CONTROL
| WRITE_DAC
;
2265 status
= NtCreateFile (&fh
, access
,
2266 win32_newpath
.get_object_attr (attr
, sec_none_nih
),
2267 &io
, NULL
, FILE_ATTRIBUTE_NORMAL
,
2268 FILE_SHARE_VALID_FLAGS
,
2269 isdevice
? FILE_OVERWRITE_IF
: FILE_CREATE
,
2270 FILE_SYNCHRONOUS_IO_NONALERT
2271 | FILE_NON_DIRECTORY_FILE
2272 | FILE_OPEN_FOR_BACKUP_INTENT
,
2274 if (!NT_SUCCESS (status
))
2276 __seterrno_from_nt_status (status
);
2279 if (io
.Information
== FILE_CREATED
&& win32_newpath
.has_acls ())
2280 set_created_file_access (fh
, win32_newpath
,
2281 S_IFLNK
| STD_RBITS
| STD_WBITS
);
2282 status
= NtWriteFile (fh
, NULL
, NULL
, NULL
, &io
, buf
, cp
- buf
,
2284 if (NT_SUCCESS (status
) && io
.Information
== (ULONG
) (cp
- buf
))
2286 status
= NtSetAttributesFile (fh
, wsym_type
== WSYM_lnk
2287 ? FILE_ATTRIBUTE_READONLY
2288 : FILE_ATTRIBUTE_SYSTEM
);
2289 if (!NT_SUCCESS (status
))
2290 debug_printf ("Setting attributes failed, status = %y", status
);
2295 __seterrno_from_nt_status (status
);
2296 FILE_DISPOSITION_INFORMATION fdi
= { TRUE
};
2297 status
= NtSetInformationFile (fh
, &io
, &fdi
, sizeof fdi
,
2298 FileDispositionInformation
);
2299 if (!NT_SUCCESS (status
))
2300 debug_printf ("Setting delete dispostion failed, status = %y",
2306 __except (EFAULT
) {}
2308 syscall_printf ("%d = symlink_worker(%s, %s, %d)",
2309 res
, oldpath
, win32_newpath
.get_posix (), isdevice
);
2314 cmp_shortcut_header (win_shortcut_hdr
*file_header
)
2316 /* A Cygwin or U/Win shortcut only contains a description and a relpath.
2317 Cygwin shortcuts also might contain an ITEMIDLIST. The run type is
2318 always set to SW_NORMAL. */
2319 return file_header
->size
== sizeof (win_shortcut_hdr
)
2320 && !memcmp (&file_header
->magic
, &GUID_shortcut
, sizeof GUID_shortcut
)
2321 && (file_header
->flags
& ~WSH_FLAG_IDLIST
)
2322 == (WSH_FLAG_DESC
| WSH_FLAG_RELPATH
)
2323 && file_header
->run
== SW_NORMAL
;
2327 symlink_info::check_shortcut (HANDLE h
)
2330 win_shortcut_hdr
*file_header
;
2336 FILE_STANDARD_INFORMATION fsi
;
2337 LARGE_INTEGER off
= { QuadPart
:0LL };
2339 status
= NtQueryInformationFile (h
, &io
, &fsi
, sizeof fsi
,
2340 FileStandardInformation
);
2341 if (!NT_SUCCESS (status
))
2346 if (fsi
.EndOfFile
.QuadPart
<= (LONGLONG
) sizeof (win_shortcut_hdr
)
2347 || fsi
.EndOfFile
.QuadPart
> 4 * 65536)
2349 if (fsi
.EndOfFile
.LowPart
< NT_MAX_PATH
* sizeof (WCHAR
))
2350 buf
= (char *) tp
.w_get ();
2352 buf
= (char *) alloca (fsi
.EndOfFile
.LowPart
+ 1);
2353 status
= NtReadFile (h
, NULL
, NULL
, NULL
, &io
, buf
, fsi
.EndOfFile
.LowPart
,
2355 if (!NT_SUCCESS (status
))
2357 if (status
!= STATUS_END_OF_FILE
)
2361 file_header
= (win_shortcut_hdr
*) buf
;
2362 if (io
.Information
!= fsi
.EndOfFile
.LowPart
2363 || !cmp_shortcut_header (file_header
))
2365 cp
= buf
+ sizeof (win_shortcut_hdr
);
2366 if (file_header
->flags
& WSH_FLAG_IDLIST
) /* Skip ITEMIDLIST */
2367 cp
+= *(unsigned short *) cp
+ 2;
2368 if (!(len
= *(unsigned short *) cp
))
2371 /* Check if this is a device file - these start with the sequence :\\ */
2372 if (strncmp (cp
, ":\\", 2) == 0)
2373 res
= strlen (strcpy (contents
, cp
)); /* Don't mess with device files */
2376 /* Has appended full path? If so, use it instead of description. */
2377 unsigned short relpath_len
= *(unsigned short *) (cp
+ len
);
2378 if (cp
+ len
+ 2 + relpath_len
< buf
+ fsi
.EndOfFile
.LowPart
)
2380 cp
+= len
+ 2 + relpath_len
;
2381 len
= *(unsigned short *) cp
;
2384 if (*(PWCHAR
) cp
== 0xfeff) /* BOM */
2386 char *tmpbuf
= tp
.c_get ();
2387 if (sys_wcstombs (tmpbuf
, NT_MAX_PATH
, (PWCHAR
) (cp
+ 2))
2390 res
= posixify (tmpbuf
);
2392 else if (len
> SYMLINK_MAX
)
2397 res
= posixify (cp
);
2400 if (res
) /* It's a symlink. */
2401 path_flags
|= PATH_SYMLINK
| PATH_LNK
;
2406 symlink_info::check_sysfile (HANDLE h
)
2409 char cookie_buf
[sizeof (SYMLINK_COOKIE
) - 1];
2410 char *srcbuf
= tp
.c_get ();
2414 bool interix_symlink
= false;
2415 LARGE_INTEGER off
= { QuadPart
:0LL };
2417 status
= NtReadFile (h
, NULL
, NULL
, NULL
, &io
, cookie_buf
,
2418 sizeof (cookie_buf
), &off
, NULL
);
2419 if (!NT_SUCCESS (status
))
2421 debug_printf ("ReadFile1 failed %y", status
);
2422 if (status
!= STATUS_END_OF_FILE
)
2426 off
.QuadPart
= io
.Information
;
2427 if (io
.Information
== sizeof (cookie_buf
)
2428 && memcmp (cookie_buf
, SYMLINK_COOKIE
, sizeof (cookie_buf
)) == 0)
2430 /* It's a symlink. */
2431 path_flags
|= PATH_SYMLINK
;
2433 else if (io
.Information
== sizeof (cookie_buf
)
2434 && memcmp (cookie_buf
, SOCKET_COOKIE
, sizeof (cookie_buf
)) == 0)
2435 path_flags
|= PATH_SOCKET
;
2436 else if (io
.Information
>= sizeof (INTERIX_SYMLINK_COOKIE
)
2437 && memcmp (cookie_buf
, INTERIX_SYMLINK_COOKIE
,
2438 sizeof (INTERIX_SYMLINK_COOKIE
) - 1) == 0)
2440 /* It's an Interix symlink. */
2441 path_flags
|= PATH_SYMLINK
;
2442 interix_symlink
= true;
2443 /* Interix symlink cookies are shorter than Cygwin symlink cookies, so
2444 in case of an Interix symlink cooky we have read too far into the
2445 file. Set file pointer back to the position right after the cookie. */
2446 off
.QuadPart
= sizeof (INTERIX_SYMLINK_COOKIE
) - 1;
2448 if (path_flags
& PATH_SYMLINK
)
2450 status
= NtReadFile (h
, NULL
, NULL
, NULL
, &io
, srcbuf
,
2451 NT_MAX_PATH
, &off
, NULL
);
2452 if (!NT_SUCCESS (status
))
2454 debug_printf ("ReadFile2 failed");
2455 if (status
!= STATUS_END_OF_FILE
)
2458 else if (*(PWCHAR
) srcbuf
== 0xfeff /* BOM */
2461 /* Add trailing 0 to Interix symlink target. Skip BOM in Cygwin
2463 if (interix_symlink
)
2464 ((PWCHAR
) srcbuf
)[io
.Information
/ sizeof (WCHAR
)] = L
'\0';
2467 char *tmpbuf
= tp
.c_get ();
2468 if (sys_wcstombs (tmpbuf
, NT_MAX_PATH
, (PWCHAR
) srcbuf
)
2470 debug_printf ("symlink string too long");
2472 res
= posixify (tmpbuf
);
2474 else if (io
.Information
> SYMLINK_MAX
+ 1)
2475 debug_printf ("symlink string too long");
2477 res
= posixify (srcbuf
);
2482 typedef struct _REPARSE_APPEXECLINK_BUFFER
2485 WORD ReparseDataLength
;
2488 DWORD Version
; /* Take member name with a grain of salt. */
2489 WCHAR Strings
[1]; /* Four serialized, NUL-terminated WCHAR strings:
2494 We're only interested in the Executable Path */
2495 } AppExecLinkReparseBuffer
;
2496 } REPARSE_APPEXECLINK_BUFFER
,*PREPARSE_APPEXECLINK_BUFFER
;
2499 check_reparse_point_string (PUNICODE_STRING subst
)
2501 /* Native mount points, or native non-relative symbolic links,
2502 can be treated as posix symlinks only if the SubstituteName
2503 can be converted from a native NT object namespace name to
2504 a win32 name. We only know how to convert names with two
2508 Other reparse points will be treated as files or
2509 directories, not as posix symlinks.
2511 if (RtlEqualUnicodePathPrefix (subst
, &ro_u_natp
, FALSE
))
2513 if (subst
->Length
>= 6 * sizeof(WCHAR
) && subst
->Buffer
[5] == L
':' &&
2514 (subst
->Length
== 6 * sizeof(WCHAR
) || subst
->Buffer
[6] == L
'\\'))
2516 else if (subst
->Length
>= 8 * sizeof(WCHAR
) &&
2517 wcsncmp (subst
->Buffer
+ 4, L
"UNC\\", 4) == 0)
2525 0: Not a reparse point recognized by us.
2526 >0: Path flags for a recognized reparse point, always including PATH_REP.
2529 check_reparse_point_target (HANDLE h
, bool remote
, PREPARSE_DATA_BUFFER rp
,
2530 PUNICODE_STRING psymbuf
)
2535 /* On remote drives or under heavy load, NtFsControlFile can return with
2536 STATUS_PENDING. If so, instead of creating an event object, just set
2537 io.Status to an invalid value and perform a minimal wait until io.Status
2539 memset (&io
, 0xff, sizeof io
);
2540 status
= NtFsControlFile (h
, NULL
, NULL
, NULL
, &io
,
2541 FSCTL_GET_REPARSE_POINT
, NULL
, 0, (LPVOID
) rp
,
2542 MAXIMUM_REPARSE_DATA_BUFFER_SIZE
);
2543 if (status
== STATUS_PENDING
)
2545 while (io
.Status
== (NTSTATUS
) 0xffffffff)
2549 if (!NT_SUCCESS (status
))
2551 debug_printf ("NtFsControlFile(FSCTL_GET_REPARSE_POINT) failed, %y",
2553 /* When accessing the root dir of some remote drives (observed with
2554 OS X shares), the FILE_ATTRIBUTE_REPARSE_POINT flag is set, but
2555 the followup call to NtFsControlFile(FSCTL_GET_REPARSE_POINT)
2556 returns with STATUS_NOT_A_REPARSE_POINT. That's quite buggy, but
2557 we cope here with this scenario by not setting an error code. */
2558 if (status
== STATUS_NOT_A_REPARSE_POINT
)
2562 if (rp
->ReparseTag
== IO_REPARSE_TAG_SYMLINK
)
2564 /* Windows evaluates native symlink literally. If a remote symlink points
2565 to, say, C:\foo, it will be handled as if the target is the local file
2566 C:\foo. That comes in handy since that's how symlinks are treated under
2568 RtlInitCountedUnicodeString (psymbuf
,
2569 (PWCHAR
)((PBYTE
) rp
->SymbolicLinkReparseBuffer
.PathBuffer
2570 + rp
->SymbolicLinkReparseBuffer
.SubstituteNameOffset
),
2571 rp
->SymbolicLinkReparseBuffer
.SubstituteNameLength
);
2572 if ((rp
->SymbolicLinkReparseBuffer
.Flags
& SYMLINK_FLAG_RELATIVE
) ||
2573 check_reparse_point_string (psymbuf
))
2574 return PATH_SYMLINK
| PATH_REP
;
2576 else if (!remote
&& rp
->ReparseTag
== IO_REPARSE_TAG_MOUNT_POINT
)
2578 /* Don't handle junctions on remote filesystems as symlinks. This type
2579 of reparse point is handled transparently by the OS so that the
2580 target of the junction is the remote directory it is supposed to
2581 point to. If we handle it as symlink, it will be mistreated as
2582 pointing to a dir on the local system. */
2583 RtlInitCountedUnicodeString (psymbuf
,
2584 (PWCHAR
)((PBYTE
) rp
->MountPointReparseBuffer
.PathBuffer
2585 + rp
->MountPointReparseBuffer
.SubstituteNameOffset
),
2586 rp
->MountPointReparseBuffer
.SubstituteNameLength
);
2587 if (RtlEqualUnicodePathPrefix (psymbuf
, &ro_u_volume
, TRUE
))
2589 /* Volume mount point. Not treated as symlink. The return
2590 value -EPERM is a hint for the caller to treat this as a
2591 volume mount point. */
2594 if (check_reparse_point_string (psymbuf
))
2595 return PATH_SYMLINK
| PATH_REP
;
2597 else if (!remote
&& rp
->ReparseTag
== IO_REPARSE_TAG_APPEXECLINK
)
2599 /* App execution aliases are commonly used by Windows Store apps. */
2600 PREPARSE_APPEXECLINK_BUFFER rpl
= (PREPARSE_APPEXECLINK_BUFFER
) rp
;
2601 WCHAR
*buf
= rpl
->AppExecLinkReparseBuffer
.Strings
;
2602 DWORD size
= rp
->ReparseDataLength
/ sizeof (WCHAR
), n
;
2604 /* It seems that app execution aliases have a payload of four
2605 NUL-separated wide string: package id, entry point, executable
2606 and application type. We're interested in the executable. */
2607 for (int i
= 0; i
< 3 && size
> 0; i
++)
2609 n
= wcsnlen (buf
, size
- 1);
2610 if (i
== 2 && n
> 0 && n
< size
)
2612 RtlInitCountedUnicodeString (psymbuf
, buf
, n
* sizeof (WCHAR
));
2613 return PATH_SYMLINK
| PATH_REP
;
2621 else if (rp
->ReparseTag
== IO_REPARSE_TAG_LX_SYMLINK
)
2623 /* WSL symlink. Problem: We have to convert the path to UTF-16 for
2624 the caller. Reparse points are 16K max. The buffer given to rp
2625 is 32K. So there's enough trailing space in the buffer to convert
2626 to UTF-16 and let psymbuf point to it. */
2627 PREPARSE_LX_SYMLINK_BUFFER rpl
= (PREPARSE_LX_SYMLINK_BUFFER
) rp
;
2628 char *path_buf
= rpl
->LxSymlinkReparseBuffer
.PathBuffer
;
2629 DWORD path_len
= rpl
->ReparseDataLength
- sizeof (DWORD
);
2630 bool full_path
= false;
2631 const size_t drv_prefix_len
= strlen ("/mnt");
2636 /* 0-terminate path_buf for easier testing. */
2637 path_buf
[path_len
] = '\0';
2638 if (path_prefix_p ("/mnt", path_buf
, drv_prefix_len
, false))
2640 size_t len
= strlen (path_buf
);
2642 if (len
<= drv_prefix_len
+ 1)
2644 /* /mnt or /mnt/. Replace with cygdrive prefix. */
2645 stpcpy (path_buf
, mount_table
->cygdrive
);
2646 path_len
= mount_table
->cygdrive_len
;
2647 if (len
== drv_prefix_len
)
2649 path_buf
[mount_table
->cygdrive_len
- 1] = '\0';
2652 rp
->ReparseDataLength
= path_len
+ sizeof (DWORD
);
2654 else if (islower (path_buf
[drv_prefix_len
+ 1])
2655 && (path_len
== drv_prefix_len
+ 2
2656 || path_buf
[drv_prefix_len
+ 2] == '/'))
2658 /* Skip forward to the slash leading the drive letter.
2659 That leaves room for adding the colon. */
2660 path_buf
+= drv_prefix_len
;
2661 path_len
-= drv_prefix_len
;
2665 /* Compute buffer for path converted to UTF-16. */
2666 utf16_ptr
= (PBYTE
) rpl
+ sizeof (REPARSE_LX_SYMLINK_BUFFER
)
2667 + rp
->ReparseDataLength
;
2668 /* Skip \0-termination added above. */
2670 /* Make sure pointer is aligned */
2671 while ((intptr_t) utf16_ptr
% sizeof (WCHAR
))
2673 utf16_buf
= (PWCHAR
) utf16_ptr
;
2674 utf16_bufsize
= NT_MAX_PATH
- (utf16_buf
- (PWCHAR
) rpl
);
2675 /* Now convert path to UTF-16. */
2676 utf16_bufsize
= MultiByteToWideChar (CP_UTF8
, 0, path_buf
, path_len
,
2677 utf16_buf
, utf16_bufsize
);
2682 utf16_buf
[0] = utf16_buf
[1]; /* Move drive letter to front */
2683 utf16_buf
[1] = L
':'; /* Add colon */
2685 RtlInitCountedUnicodeString (psymbuf
, utf16_buf
,
2686 utf16_bufsize
* sizeof (WCHAR
));
2687 return PATH_SYMLINK
| PATH_REP
| PATH_REP_NOAPI
;
2691 else if (rp
->ReparseTag
== IO_REPARSE_TAG_CYGUNIX
)
2693 PREPARSE_GUID_DATA_BUFFER rgp
= (PREPARSE_GUID_DATA_BUFFER
) rp
;
2695 if (memcmp (CYGWIN_SOCKET_GUID
, &rgp
->ReparseGuid
, sizeof (GUID
)) == 0)
2696 #ifdef __WITH_AF_UNIX
2697 return PATH_SOCKET
| PATH_REP
| PATH_REP_NOAPI
;
2699 /* Recognize this as a reparse point but not as a socket. */
2700 return PATH_REP
| PATH_REP_NOAPI
;
2703 else if (rp
->ReparseTag
== IO_REPARSE_TAG_AF_UNIX
)
2704 /* Native Windows AF_UNIX socket; recognize this as a reparse
2705 point but not as a socket. */
2711 symlink_info::check_reparse_point (HANDLE h
, bool remote
)
2714 PREPARSE_DATA_BUFFER rp
= (PREPARSE_DATA_BUFFER
) tp
.c_get ();
2715 UNICODE_STRING symbuf
;
2716 char srcbuf
[SYMLINK_MAX
+ 7];
2718 int ret
= check_reparse_point_target (h
, remote
, rp
, &symbuf
);
2726 /* Maybe it's a reparse point, but it's certainly not one we recognize.
2727 Drop REPARSE attribute so we don't try to use the flag accidentally.
2728 It's just some arbitrary file or directory for us. */
2729 fileattr
&= ~FILE_ATTRIBUTE_REPARSE_POINT
;
2732 /* ret is > 0, so it's a known reparse point, path in symbuf. */
2734 if (ret
& PATH_SYMLINK
)
2736 sys_wcstombs (srcbuf
, SYMLINK_MAX
+ 7, symbuf
.Buffer
,
2737 symbuf
.Length
/ sizeof (WCHAR
));
2738 /* A symlink is never a directory. */
2739 fileattr
&= ~FILE_ATTRIBUTE_DIRECTORY
;
2740 return posixify (srcbuf
);
2747 symlink_info::check_nfs_symlink (HANDLE h
)
2753 FILE_GET_EA_INFORMATION fgei
;
2754 char buf
[sizeof (NFS_SYML_TARGET
)];
2756 PFILE_FULL_EA_INFORMATION pffei
;
2759 /* To find out if the file is a symlink and to get the symlink target,
2760 try to fetch the NfsSymlinkTargetName EA. */
2761 fgei_buf
.fgei
.NextEntryOffset
= 0;
2762 fgei_buf
.fgei
.EaNameLength
= sizeof (NFS_SYML_TARGET
) - 1;
2763 stpcpy (fgei_buf
.fgei
.EaName
, NFS_SYML_TARGET
);
2764 pffei
= (PFILE_FULL_EA_INFORMATION
) tp
.w_get ();
2765 status
= NtQueryEaFile (h
, &io
, pffei
, NT_MAX_PATH
* sizeof (WCHAR
), TRUE
,
2766 &fgei_buf
.fgei
, sizeof fgei_buf
, NULL
, TRUE
);
2767 if (NT_SUCCESS (status
) && pffei
->EaValueLength
> 0)
2769 PWCHAR spath
= (PWCHAR
)
2770 (pffei
->EaName
+ pffei
->EaNameLength
+ 1);
2771 res
= sys_wcstombs (contents
, SYMLINK_MAX
+ 1,
2772 spath
, pffei
->EaValueLength
);
2773 path_flags
|= PATH_SYMLINK
;
2779 symlink_info::posixify (char *srcbuf
)
2781 /* The definition for a path in a native symlink is a bit weird. The Flags
2782 value seem to contain 0 for absolute paths (stored as NT native path)
2783 and 1 for relative paths. Relative paths are paths not starting with a
2784 drive letter. These are not converted to NT native, but stored as
2785 given. A path starting with a single backslash is relative to the
2786 current drive thus a "relative" value (Flags == 1).
2787 Funny enough it's possible to store paths with slashes instead of
2788 backslashes, but they are evaluated incorrectly by subsequent Windows
2789 calls like CreateFile (ERROR_INVALID_NAME). So, what we do here is to
2790 take paths starting with slashes at face value, evaluating them as
2791 Cygwin specific POSIX paths.
2792 A path starting with two slashes(!) or backslashes is converted into an
2793 NT UNC path. Unfortunately, in contrast to POSIX rules, paths starting
2794 with three or more (back)slashes are also converted into UNC paths,
2795 just incorrectly sticking to one redundant leading backslash. We go
2796 along with this behaviour to avoid scenarios in which native tools access
2797 other files than Cygwin.
2798 The above rules are used exactly the same way on Cygwin specific symlinks
2799 (sysfiles and shortcuts) to eliminate non-POSIX paths in the output. */
2801 /* Eliminate native NT prefixes. */
2802 if (srcbuf
[0] == '\\' && !strncmp (srcbuf
+ 1, "??\\", 3))
2805 if (srcbuf
[1] != ':') /* native UNC path */
2806 *(srcbuf
+= 2) = '\\';
2808 if (isdrive (srcbuf
))
2809 mount_table
->conv_to_posix_path (srcbuf
, contents
, 0);
2810 else if (srcbuf
[0] == '\\')
2812 if (srcbuf
[1] == '\\') /* UNC path */
2813 slashify (srcbuf
, contents
, 0);
2814 else /* Paths starting with \ are current drive relative. */
2816 char cvtbuf
[SYMLINK_MAX
+ 1];
2818 stpcpy (cvtbuf
+ cygheap
->cwd
.get_drive (cvtbuf
), srcbuf
);
2819 mount_table
->conv_to_posix_path (cvtbuf
, contents
, 0);
2822 else /* Everything else is taken as is. */
2823 slashify (srcbuf
, contents
, 0);
2824 return strlen (contents
);
2833 SCAN_JUSTCHECKTHIS
, /* Never try to append a suffix. */
2841 const suffix_info
*suffixes
, *suffixes_start
;
2847 char *has (const char *, const suffix_info
*);
2849 int lnk_match () {return nextstate
>= SCAN_APPENDLNK
;}
2850 size_t name_len () {return namelen
;}
2854 suffix_scan::has (const char *in_path
, const suffix_info
*in_suffixes
)
2856 nextstate
= SCAN_BEG
;
2857 suffixes
= suffixes_start
= in_suffixes
;
2859 const char *fname
= strrchr (in_path
, '\\');
2860 fname
= fname
? fname
+ 1 : in_path
;
2861 char *ext_here
= strrchr (fname
, '.');
2863 eopath
= strchr (path
, '\0');
2870 /* Check if the extension matches a known extension */
2871 for (const suffix_info
*ex
= in_suffixes
; ex
->name
!= NULL
; ex
++)
2872 if (ascii_strcasematch (ext_here
, ex
->name
))
2874 nextstate
= SCAN_JUSTCHECK
;
2875 suffixes
= NULL
; /* Has an extension so don't scan for one. */
2880 /* Didn't match. Use last resort -- .lnk. */
2881 if (ascii_strcasematch (ext_here
, ".lnk"))
2883 nextstate
= SCAN_HASLNK
;
2891 namelen
= eopath
- fname
;
2892 /* Avoid attaching suffixes if the resulting filename would be invalid.
2893 For performance reasons we don't check the length of a suffix, since
2894 we know that all suffixes are 4 chars in length.
2896 FIXME: This is not really correct. A fully functional test should
2897 work on wide character paths. This would probably also speed
2898 up symlink_info::check. */
2899 if (namelen
> NAME_MAX
- 4)
2901 nextstate
= SCAN_JUSTCHECKTHIS
;
2908 suffix_scan::next ()
2916 suffixes
= suffixes_start
;
2919 nextstate
= SCAN_LNK
;
2922 nextstate
= SCAN_EXTRALNK
;
2923 /* fall through to suffix checking below */
2926 nextstate
= SCAN_APPENDLNK
; /* Skip SCAN_BEG */
2929 nextstate
= SCAN_DONE
;
2932 case SCAN_JUSTCHECK
:
2933 nextstate
= SCAN_LNK
;
2935 case SCAN_JUSTCHECKTHIS
:
2936 nextstate
= SCAN_DONE
;
2939 case SCAN_APPENDLNK
:
2940 nextstate
= SCAN_DONE
;
2941 if (namelen
+ (*eopath
? 8 : 4) > NAME_MAX
)
2946 strcat (eopath
, ".lnk");
2953 while (suffixes
&& suffixes
->name
)
2954 if (nextstate
== SCAN_EXTRALNK
2955 && (!suffixes
->addon
|| namelen
> NAME_MAX
- 8))
2959 strcpy (eopath
, suffixes
->name
);
2960 if (nextstate
== SCAN_EXTRALNK
)
2961 strcat (eopath
, ".lnk");
2970 symlink_info::set_error (int in_errno
)
2973 if (!(pc_flags
& PC_NO_ACCESS_CHECK
)
2974 || in_errno
== ENAMETOOLONG
|| in_errno
== EIO
)
2979 else if (in_errno
== ENOENT
)
2983 fileattr
= FILE_ATTRIBUTE_NORMAL
;
2990 symlink_info::parse_device (const char *contents
)
2997 mymajor
= strtol (contents
+= 2, &endptr
, 16);
2998 if (endptr
== contents
)
2999 return isdevice
= false;
3002 myminor
= strtol (++contents
, &endptr
, 16);
3003 if (endptr
== contents
)
3004 return isdevice
= false;
3007 mymode
= strtol (++contents
, &endptr
, 16);
3008 if (endptr
== contents
)
3009 return isdevice
= false;
3011 if ((mymode
& S_IFMT
) == S_IFIFO
)
3013 mymajor
= _major (FH_FIFO
);
3014 myminor
= _minor (FH_FIFO
);
3020 return isdevice
= true;
3023 /* Check if PATH is a symlink. PATH must be a valid Win32 path name.
3025 If PATH is a symlink, put the value of the symlink--the file to
3026 which it points--into BUF. The value stored in BUF is not
3027 necessarily null terminated. BUFLEN is the length of BUF; only up
3028 to BUFLEN characters will be stored in BUF. BUF may be NULL, in
3029 which case nothing will be stored.
3031 Set *SYML if PATH is a symlink.
3033 Set *EXEC if PATH appears to be executable. This is an efficiency
3034 hack because we sometimes have to open the file anyhow. *EXEC will
3035 not be set for every executable file.
3037 Return -1 on error, 0 if PATH is not a symlink, or the length
3038 stored into BUF if PATH is a symlink. */
3041 symlink_info::check (char *path
, const suffix_info
*suffixes
, fs_info
&fs
,
3042 path_conv_handle
&conv_hdl
)
3047 UNICODE_STRING upath
;
3048 OBJECT_ATTRIBUTES attr
;
3052 const ULONG ci_flag
= cygwin_shared
->obcaseinsensitive
3053 || (mount_flags
& MOUNT_NOPOSIX
)
3054 ? OBJ_CASE_INSENSITIVE
: 0;
3055 /* TODO: Temporarily do all char->UNICODE conversion here. This should
3056 already be slightly faster than using Ascii functions. */
3059 InitializeObjectAttributes (&attr
, &upath
, ci_flag
, NULL
, NULL
);
3061 /* This label is used in case we encounter a FS which only handles
3062 DOS paths. See below. */
3063 bool restarted
= false;
3074 // mount_flags is an incoming value set in path_conv */
3077 PVOID eabuf
= &nfs_aol_ffei
;
3078 ULONG easize
= sizeof nfs_aol_ffei
;
3080 ext_here
= suffix
.has (path
, suffixes
);
3081 extn
= ext_here
- path
;
3082 bool had_ext
= !!*ext_here
;
3084 /* If the filename is too long, don't even try. */
3085 if (suffix
.name_len () > NAME_MAX
)
3087 set_error (ENAMETOOLONG
);
3088 goto file_not_symlink
;
3091 while (suffix
.next ())
3094 get_nt_native_path (suffix
.path
, upath
, mount_flags
& MOUNT_DOS
);
3100 /* The EA given to NtCreateFile allows to get a handle to a symlink on
3101 an NFS share, rather than getting a handle to the target of the
3102 symlink (which would spoil the task of this method quite a bit).
3103 Fortunately it's ignored on most other file systems so we don't have
3104 to special case NFS too much. */
3105 status
= NtCreateFile (&h
,
3106 READ_CONTROL
| FILE_READ_ATTRIBUTES
| FILE_READ_EA
,
3107 &attr
, &io
, NULL
, 0, FILE_SHARE_VALID_FLAGS
,
3109 FILE_OPEN_REPARSE_POINT
3110 | FILE_OPEN_FOR_BACKUP_INTENT
,
3112 debug_printf ("%y = NtCreateFile (%S)", status
, &upath
);
3113 /* No right to access EAs or EAs not supported? */
3114 if (!NT_SUCCESS (status
)
3115 && (status
== STATUS_ACCESS_DENIED
3116 || status
== STATUS_EAS_NOT_SUPPORTED
3117 || status
== STATUS_NOT_SUPPORTED
3118 || status
== STATUS_INVALID_NETWORK_RESPONSE
3119 /* Or a bug in Samba 3.2.x (x <= 7) when accessing a share's
3120 root dir which has EAs enabled? */
3121 || status
== STATUS_INVALID_PARAMETER
))
3123 /* If EAs are not supported, there's no sense to check them again
3124 with suffixes attached. So we set eabuf/easize to 0 here once. */
3125 if (status
== STATUS_EAS_NOT_SUPPORTED
3126 || status
== STATUS_NOT_SUPPORTED
)
3131 status
= NtOpenFile (&h
, READ_CONTROL
| FILE_READ_ATTRIBUTES
,
3132 &attr
, &io
, FILE_SHARE_VALID_FLAGS
,
3133 FILE_OPEN_REPARSE_POINT
3134 | FILE_OPEN_FOR_BACKUP_INTENT
);
3135 debug_printf ("%y = NtOpenFile (no-EAs %S)", status
, &upath
);
3137 if (status
== STATUS_OBJECT_NAME_NOT_FOUND
)
3139 /* There are filesystems out in the wild (Netapp, NWFS, and others)
3140 which are uncapable of generating pathnames outside the Win32
3141 rules. That means, filenames on these FSes must not have a
3142 leading space or trailing dots and spaces. This code snippet
3143 manages them. I really hope it's streamlined enough not to
3144 slow down normal operation. This extra check only kicks in if
3145 we encountered a STATUS_OBJECT_NAME_NOT_FOUND *and* we didn't
3146 already attach a suffix. */
3147 if (!restarted
&& !*ext_here
&& ext_here
[-1] != '\\'
3148 && !(mount_flags
& MOUNT_DOS
))
3150 /* Check for trailing dot or space or leading space in
3152 char *p
= ext_here
- 1;
3153 if (*p
!= '.' && *p
!= ' ')
3155 while (*--p
!= '\\')
3162 /* If so, check if file resides on one of the known broken
3163 FSes only supporting filenames following DOS rules. */
3164 fs
.update (&upath
, NULL
);
3165 if (fs
.has_dos_filenames_only ())
3167 /* If so, try again. Since we now know the FS, the
3168 filenames will be tweaked to follow DOS rules via the
3169 third parameter in the call to get_nt_native_path. */
3170 mount_flags
|= MOUNT_DOS
;
3177 else if (status
== STATUS_NETWORK_OPEN_RESTRICTION
3178 || status
== STATUS_SYMLINK_CLASS_DISABLED
)
3180 /* These status codes are returned if you try to open a native
3181 symlink and the usage of this kind of symlink is forbidden
3182 (see fsutil). Since we can't open them at all, not even for
3183 stat purposes, we have to return a POSIX error code which is
3184 at least a bit helpful.
3186 Additionally Windows 8 introduces a bug in NFS: If you have
3187 a symlink to a directory, with symlinks underneath, resolving
3188 the second level of symlinks fails if remote->remote symlinks
3189 are disabled in fsutil. Unfortunately that's the default. */
3194 if (NT_SUCCESS (status
)
3195 /* Check file system while we're having the file open anyway.
3196 This speeds up path_conv noticably (~10%). */
3197 && (fs
.inited () || fs
.update (&upath
, h
)))
3199 status
= conv_hdl
.get_finfo (h
, fs
.is_nfs ());
3200 if (NT_SUCCESS (status
))
3201 fileattr
= conv_hdl
.get_dosattr (fs
.is_nfs ());
3203 if (!NT_SUCCESS (status
))
3205 debug_printf ("%y = NtQueryInformationFile (%S)", status
, &upath
);
3206 fileattr
= INVALID_FILE_ATTRIBUTES
;
3208 /* One of the inner path components is invalid, or the path contains
3209 invalid characters. Bail out with ENOENT.
3211 STATUS_IO_REPARSE_TAG_NOT_HANDLED is returned when trying to
3212 traversing a WSL symlink. For all practical purposes it's
3213 equivalent to traversing SYSTEM- or LNK-type symlink returning
3214 STATUS_OBJECT_PATH_NOT_FOUND.
3216 Note that additional STATUS_OBJECT_PATH_INVALID and
3217 STATUS_OBJECT_PATH_SYNTAX_BAD status codes exist. The first one
3218 is seemingly not generated by NtQueryInformationFile, the latter
3219 is only generated if the path is no absolute path within the
3220 NT name space, which should not happen and would point to an
3221 error in get_nt_native_path. Both status codes are deliberately
3222 not tested here unless proved necessary. */
3223 if (status
== STATUS_OBJECT_PATH_NOT_FOUND
3224 || status
== STATUS_IO_REPARSE_TAG_NOT_HANDLED
3225 || status
== STATUS_OBJECT_NAME_INVALID
3226 || status
== STATUS_BAD_NETWORK_PATH
3227 || status
== STATUS_BAD_NETWORK_NAME
3228 || status
== STATUS_NO_MEDIA_IN_DEVICE
)
3231 if (ext_tacked_on
&& !had_ext
)
3234 ext_tacked_on
= false;
3238 goto file_not_symlink
;
3240 if (status
!= STATUS_OBJECT_NAME_NOT_FOUND
3241 && status
!= STATUS_NO_SUCH_FILE
) /* ENOENT on NFS or 9x share */
3243 /* The file exists, but the user can't access it for one reason
3244 or the other. To get the file attributes we try to access the
3245 information by opening the parent directory and getting the
3246 file attributes using a matching NtQueryDirectoryFile call. */
3247 UNICODE_STRING dirname
, basename
;
3248 OBJECT_ATTRIBUTES dattr
;
3251 FILE_ID_BOTH_DIR_INFORMATION fdi
;
3252 WCHAR dummy_buf
[NAME_MAX
+ 1];
3255 RtlSplitUnicodePath (&upath
, &dirname
, &basename
);
3256 InitializeObjectAttributes (&dattr
, &dirname
, ci_flag
,
3258 status
= NtOpenFile (&dir
, SYNCHRONIZE
| FILE_LIST_DIRECTORY
,
3259 &dattr
, &io
, FILE_SHARE_VALID_FLAGS
,
3260 FILE_SYNCHRONOUS_IO_NONALERT
3261 | FILE_OPEN_FOR_BACKUP_INTENT
3262 | FILE_DIRECTORY_FILE
);
3263 if (!NT_SUCCESS (status
))
3265 debug_printf ("%y = NtOpenFile(%S)", status
, &dirname
);
3266 /* There's a special case if the file is itself the root
3267 of a drive which is not accessible by the current user.
3268 This case is only recognized by the length of the
3269 basename part. If it's 0, the incoming file is the
3270 root of a drive. So we at least know it's a directory. */
3271 if (basename
.Length
)
3272 fileattr
= FILE_ATTRIBUTE_DIRECTORY
;
3276 set_error (geterrno_from_nt_status (status
));
3281 status
= NtQueryDirectoryFile (dir
, NULL
, NULL
, NULL
, &io
,
3282 &fdi_buf
, sizeof fdi_buf
,
3283 FileIdBothDirectoryInformation
,
3284 TRUE
, &basename
, TRUE
);
3285 /* Take the opportunity to check file system while we're
3286 having the handle to the parent dir. */
3287 fs
.update (&upath
, dir
);
3289 if (!NT_SUCCESS (status
))
3291 debug_printf ("%y = NtQueryDirectoryFile(%S)",
3293 if (status
== STATUS_NO_SUCH_FILE
)
3295 /* This can happen when trying to access files
3296 which match DOS device names on SMB shares.
3297 NtOpenFile failed with STATUS_ACCESS_DENIED,
3298 but the NtQueryDirectoryFile tells us the
3299 file doesn't exist. We're suspicious in this
3300 case and retry with the next suffix instead of
3309 PFILE_ALL_INFORMATION pfai
= conv_hdl
.fai ();
3311 fileattr
= fdi_buf
.fdi
.FileAttributes
;
3312 memcpy (&pfai
->BasicInformation
.CreationTime
,
3313 &fdi_buf
.fdi
.CreationTime
,
3314 4 * sizeof (LARGE_INTEGER
));
3315 pfai
->BasicInformation
.FileAttributes
= fileattr
;
3316 pfai
->StandardInformation
.AllocationSize
.QuadPart
3317 = fdi_buf
.fdi
.AllocationSize
.QuadPart
;
3318 pfai
->StandardInformation
.EndOfFile
.QuadPart
3319 = fdi_buf
.fdi
.EndOfFile
.QuadPart
;
3320 pfai
->StandardInformation
.NumberOfLinks
= 1;
3321 pfai
->InternalInformation
.IndexNumber
.QuadPart
3322 = fdi_buf
.fdi
.FileId
.QuadPart
;
3325 ext_tacked_on
= !!*ext_here
;
3326 goto file_not_symlink
;
3332 ext_tacked_on
= !!*ext_here
;
3333 /* Don't allow to returns directories with appended suffix. If we found
3334 a directory with a suffix which has been appended here, then this
3335 directory doesn't match the request. So, just do as usual if file
3336 hasn't been found. */
3337 if (ext_tacked_on
&& !had_ext
&& (fileattr
& FILE_ATTRIBUTE_DIRECTORY
))
3339 fileattr
= INVALID_FILE_ATTRIBUTES
;
3346 /* Reparse points are potentially symlinks. This check must be
3347 performed before checking the SYSTEM attribute for sysfile
3348 symlinks, since reparse points can have this flag set, too. */
3349 if ((fileattr
& FILE_ATTRIBUTE_REPARSE_POINT
))
3351 res
= check_reparse_point (h
, fs
.is_remote_drive ());
3354 /* A symlink is never a directory. */
3355 conv_hdl
.fai ()->BasicInformation
.FileAttributes
3356 &= ~FILE_ATTRIBUTE_DIRECTORY
;
3359 else if (res
== 0 && (path_flags
& PATH_REP
))
3360 /* Known reparse point but not a symlink. */
3361 goto file_not_symlink
;
3364 /* Volume moint point or unrecognized reparse point type.
3365 Make sure the open handle is not used in later stat calls.
3366 The handle has been opened with the FILE_OPEN_REPARSE_POINT
3367 flag, so it's a handle to the reparse point, not a handle
3368 to the volumes root dir. */
3369 pc_flags
&= ~PC_KEEP_HANDLE
;
3370 /* Volume mount point: The filesystem information for the top
3371 level directory should be for the volume top level directory,
3372 rather than for the reparse point itself. So we fetch the
3373 filesystem information again, but with a NULL handle.
3374 This does what we want because fs_info::update opens the
3375 handle without FILE_OPEN_REPARSE_POINT. */
3377 fs
.update (&upath
, NULL
);
3381 /* Windows shortcuts are potentially treated as symlinks. Valid Cygwin
3382 & U/WIN shortcuts are R/O, but definitely not directories. */
3383 else if ((fileattr
& (FILE_ATTRIBUTE_READONLY
| FILE_ATTRIBUTE_DIRECTORY
))
3384 == FILE_ATTRIBUTE_READONLY
&& suffix
.lnk_match ())
3388 status
= NtOpenFile (&sym_h
, SYNCHRONIZE
| GENERIC_READ
, &attr
, &io
,
3389 FILE_SHARE_VALID_FLAGS
,
3390 FILE_OPEN_FOR_BACKUP_INTENT
3391 | FILE_SYNCHRONOUS_IO_NONALERT
);
3392 if (!NT_SUCCESS (status
))
3396 res
= check_shortcut (sym_h
);
3401 /* If searching for `foo' and then finding a `foo.lnk' which
3402 is no shortcut, return the same as if file not found. */
3405 fileattr
= INVALID_FILE_ATTRIBUTES
;
3410 else if (contents
[0] != ':' || contents
[1] != '\\'
3411 || !parse_device (contents
))
3415 /* If searching for `foo' and then finding a `foo.lnk' which is
3416 no shortcut, return the same as if file not found. */
3417 else if (suffix
.lnk_match () && ext_tacked_on
)
3419 fileattr
= INVALID_FILE_ATTRIBUTES
;
3424 /* This is the old Cygwin method creating symlinks. A symlink will
3425 have the `system' file attribute. Only files can be symlinks
3426 (which can be symlinks to directories). */
3427 else if ((fileattr
& (FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_DIRECTORY
))
3428 == FILE_ATTRIBUTE_SYSTEM
)
3432 status
= NtOpenFile (&sym_h
, SYNCHRONIZE
| GENERIC_READ
, &attr
, &io
,
3433 FILE_SHARE_VALID_FLAGS
,
3434 FILE_OPEN_FOR_BACKUP_INTENT
3435 | FILE_SYNCHRONOUS_IO_NONALERT
);
3437 if (!NT_SUCCESS (status
))
3441 res
= check_sysfile (sym_h
);
3448 /* If the file is on an NFS share and could be opened with extended
3449 attributes, check if it's a symlink. Only files can be symlinks
3450 (which can be symlinks to directories). */
3451 else if (fs
.is_nfs () && (conv_hdl
.nfsattr ()->type
& 7) == NF3LNK
)
3453 res
= check_nfs_symlink (h
);
3458 /* Check if the inner path components contain native symlinks or
3459 junctions, or if the drive is a virtual drive. Compare incoming
3460 path with path returned by GetFinalPathNameByHandleA. If they
3461 differ, return the final path as symlink content and set symlen
3462 to a negative value. This forces path_conv::check to restart
3463 symlink evaluation with the new path. */
3465 /* On WOW64, ignore any potential problems if the path is inside
3466 the Windows dir to avoid false positives for stuff under File
3467 System Redirector control. Believe it or not, but even
3468 GetFinalPathNameByHandleA returns the converted path for the
3469 Sysnative dir. I. e.
3471 C:\Windows\Sysnative --> C:\Windows\System32
3473 This is obviously wrong when using this path for further
3474 file manipulation because the non-final path points to another
3475 file than the final path. Oh well... */
3476 if (!fs
.is_remote_drive () && wincap
.is_wow64 ())
3478 /* windows_directory_path is stored without trailing backslash,
3479 so we have to check this explicitely. */
3480 if (RtlEqualUnicodePathPrefix (&upath
, &windows_directory_path
, TRUE
)
3481 && upath
.Buffer
[windows_directory_path
.Length
/ sizeof (WCHAR
)]
3483 goto file_not_symlink
;
3485 #endif /* __i386__ */
3486 if ((pc_flags
& (PC_SYM_FOLLOW
| PC_SYM_NOFOLLOW_REP
)) == PC_SYM_FOLLOW
)
3488 PWCHAR fpbuf
= tp
.w_get ();
3491 ret
= GetFinalPathNameByHandleW (h
, fpbuf
, NT_MAX_PATH
, 0);
3494 UNICODE_STRING fpath
;
3496 /* If incoming path has no trailing backslash, but final path
3497 has one, drop trailing backslash from final path so the
3498 below string comparison has a chance to succeed.
3499 On the contrary, if incoming path has trailing backslash,
3500 but final path does not have one, add trailing backslash
3501 to the final path. */
3502 if (upath
.Buffer
[(upath
.Length
- 1) / sizeof (WCHAR
)] != L
'\\'
3503 && fpbuf
[ret
- 1] == L
'\\')
3504 fpbuf
[--ret
] = L
'\0';
3505 if (upath
.Buffer
[(upath
.Length
- 1) / sizeof (WCHAR
)] == L
'\\'
3506 && fpbuf
[ret
- 1] != L
'\\' && ret
< NT_MAX_PATH
- 1)
3508 fpbuf
[ret
++] = L
'\\';
3511 fpbuf
[1] = L
'?'; /* \\?\ --> \??\ */
3512 RtlInitCountedUnicodeString (&fpath
, fpbuf
, ret
* sizeof (WCHAR
));
3513 if (!RtlEqualUnicodeString (&upath
, &fpath
, !!ci_flag
))
3515 /* Check if the final path is an UNC path and the incoming
3516 path isn't. If so... */
3517 if (RtlEqualUnicodePathPrefix (&fpath
, &ro_u_uncp
, TRUE
)
3518 && !RtlEqualUnicodePathPrefix (&upath
, &ro_u_uncp
, TRUE
))
3520 /* ...get the remote path, replace remote path
3521 with drive letter, check again. */
3523 {(WCHAR
) towupper (upath
.Buffer
[4]), L
':', L
'\0'};
3524 WCHAR remote
[MAX_PATH
];
3526 DWORD remlen
= QueryDosDeviceW (drive
, remote
, MAX_PATH
);
3528 goto file_not_symlink
; /* fallback */
3529 remlen
-= 2; /* Two L'\0' */
3531 if (remote
[remlen
- 1] == L
'\\')
3534 UNICODE_STRING rpath
;
3535 RtlInitCountedUnicodeString (&rpath
, remote
,
3536 remlen
* sizeof (WCHAR
));
3537 const USHORT uncp_len
=
3538 ro_u_uncp
.Length
/ sizeof (WCHAR
) - 1;
3539 if (RtlEqualUnicodePathPrefix (&rpath
, &ro_u_uncp
, TRUE
))
3542 p
= remote
+ uncp_len
;
3544 else if ((p
= wcschr (remote
, L
';'))
3545 && p
+ 3 < remote
+ remlen
3546 && wcsncmp (p
+ 1, drive
, 2) == 0
3547 && (p
= wcschr (p
+ 3, L
'\\')))
3548 remlen
-= p
- remote
;
3550 goto file_not_symlink
; /* fallback */
3551 if (wcsncasecmp (fpath
.Buffer
+ uncp_len
, p
, remlen
))
3552 goto file_not_symlink
; /* fallback (not expected) */
3554 fpath
.Buffer
[4] = drive
[0]; /* Drive letter */
3555 fpath
.Buffer
[5] = L
':';
3556 WCHAR
*to
= fpath
.Buffer
+ 6; /* Next to L':' */
3557 WCHAR
*from
= fpath
.Buffer
+ uncp_len
+ remlen
;
3559 (wcslen (from
) + 1) * sizeof (WCHAR
));
3560 fpath
.Length
-= (from
- to
) * sizeof (WCHAR
);
3561 if (RtlEqualUnicodeString (&upath
, &fpath
, !!ci_flag
))
3562 goto file_not_symlink
;
3565 /* upath.Buffer is big enough and unused from this point on.
3566 Reuse it here, avoiding yet another buffer allocation. */
3567 char *nfpath
= (char *) upath
.Buffer
;
3568 sys_wcstombs (nfpath
, NT_MAX_PATH
, fpbuf
);
3569 res
= posixify (nfpath
);
3571 /* If the incoming path consisted of a drive prefix only,
3572 we just handle a virtual drive, created with, e.g.
3576 Treat it like a symlink. This is required to tell an
3577 lstat caller that the "drive" is actually pointing
3578 somewhere else, thus, it's a symlink in POSIX speak. */
3579 if (upath
.Length
== 14) /* \??\X:\ */
3581 fileattr
&= ~FILE_ATTRIBUTE_DIRECTORY
;
3582 path_flags
|= PATH_SYMLINK
;
3584 /* For final paths differing in inner path components return
3585 length as negative value. This informs path_conv::check
3586 to skip realpath handling on the last path component. */
3597 syscall_printf ("%s", isdevice
? "is a device" : "not a symlink");
3604 if (pc_flags
& PC_KEEP_HANDLE
)
3610 syscall_printf ("%d = symlink.check(%s, %p) (mount_flags %y, path_flags %y)",
3611 res
, suffix
.path
, contents
, mount_flags
, path_flags
);
3615 /* "path" is the path in a virtual symlink. Set a symlink_info struct from
3616 that and proceed with further path checking afterwards. */
3618 symlink_info::set (char *path
)
3620 strcpy (contents
, path
);
3622 path_flags
= PATH_SYMLINK
;
3623 fileattr
= FILE_ATTRIBUTE_NORMAL
;
3627 ext_tacked_on
= false;
3629 extn
= major
= minor
= mode
= 0;
3630 return strlen (path
);
3633 /* readlink system call */
3636 readlink (const char *__restrict path
, char *__restrict buf
, size_t buflen
)
3640 set_errno (ENAMETOOLONG
);
3644 path_conv
pathbuf (path
, PC_SYM_CONTENTS
, stat_suffixes
);
3648 set_errno (pathbuf
.error
);
3649 syscall_printf ("-1 = readlink (%s, %p, %lu)", path
, buf
, buflen
);
3653 if (!pathbuf
.exists ())
3659 if (!pathbuf
.issymlink ())
3661 if (pathbuf
.exists ())
3666 size_t pathbuf_len
= strlen (pathbuf
.get_win32 ());
3667 ssize_t len
= MIN (buflen
, pathbuf_len
);
3668 memcpy (buf
, pathbuf
.get_win32 (), len
);
3670 /* errno set by symlink.check if error */
3674 /* Some programs rely on st_dev/st_ino being unique for each file.
3675 Hash the path name and hope for the best. The hash arg is not
3676 always initialized to zero since readdir needs to compute the
3677 dirent ino_t based on a combination of the hash of the directory
3678 done during the opendir call and the hash or the filename within
3679 the directory. FIXME: Not bullet-proof. */
3680 /* Cygwin internal */
3682 hash_path_name (ino_t hash
, PUNICODE_STRING name
)
3684 if (name
->Length
== 0)
3687 /* Build up hash. Name is already normalized */
3688 USHORT len
= name
->Length
/ sizeof (WCHAR
);
3689 for (USHORT idx
= 0; idx
< len
; ++idx
)
3690 hash
= RtlUpcaseUnicodeChar (name
->Buffer
[idx
])
3691 + (hash
<< 6) + (hash
<< 16) - hash
;
3696 hash_path_name (ino_t hash
, PCWSTR name
)
3698 UNICODE_STRING uname
;
3699 RtlInitUnicodeString (&uname
, name
);
3700 return hash_path_name (hash
, &uname
);
3704 hash_path_name (ino_t hash
, const char *name
)
3706 UNICODE_STRING uname
;
3707 RtlCreateUnicodeStringFromAsciiz (&uname
, name
);
3708 ino_t ret
= hash_path_name (hash
, &uname
);
3709 RtlFreeUnicodeString (&uname
);
3714 getcwd (char *buf
, size_t ulen
)
3720 if (ulen
== 0 && buf
)
3723 res
= cygheap
->cwd
.get (buf
, 1, 1, ulen
);
3725 __except (EFAULT
) {}
3730 /* getwd: Legacy. */
3734 return getcwd (buf
, PATH_MAX
+ 1); /*Per SuSv3!*/
3738 get_current_dir_name (void)
3740 const char *pwd
= getenv ("PWD");
3741 char *cwd
= getcwd (NULL
, 0);
3742 struct stat pwdbuf
, cwdbuf
;
3744 if (pwd
&& strcmp (pwd
, cwd
) != 0
3745 && stat64 (pwd
, &pwdbuf
) == 0
3746 && stat64 (cwd
, &cwdbuf
) == 0
3747 && pwdbuf
.st_dev
== cwdbuf
.st_dev
3748 && pwdbuf
.st_ino
== cwdbuf
.st_ino
)
3750 cwd
= (char *) realloc (cwd
, strlen (pwd
) + 1);
3757 /* chdir: POSIX 5.2.1.1 */
3759 chdir (const char *in_dir
)
3771 syscall_printf ("dir '%s'", in_dir
);
3773 /* Convert path. PC_NONULLEMPTY ensures that we don't check for
3774 NULL/empty/invalid again. */
3775 path_conv
path (in_dir
, PC_SYM_FOLLOW
| PC_POSIX
| PC_NONULLEMPTY
);
3778 set_errno (path
.error
);
3779 syscall_printf ("-1 = chdir (%s)", in_dir
);
3783 const char *posix_cwd
= NULL
;
3784 dev_t devn
= path
.get_device ();
3785 if (!path
.exists ())
3787 else if (!path
.isdir ())
3788 set_errno (ENOTDIR
);
3789 else if (!isvirtual_dev (devn
))
3791 /* The sequence chdir("xx"); chdir(".."); must be a noop if xx
3792 is not a symlink. This is exploited by find.exe.
3793 The posix_cwd is just path.get_posix ().
3794 In other cases we let cwd.set obtain the Posix path through
3796 if (!isdrive (path
.get_posix ()))
3797 posix_cwd
= path
.get_posix ();
3802 posix_cwd
= path
.get_posix ();
3807 res
= cygheap
->cwd
.set (&path
, posix_cwd
);
3809 /* Note that we're accessing cwd.posix without a lock here.
3810 I didn't think it was worth locking just for strace. */
3811 syscall_printf ("%R = chdir() cygheap->cwd.posix '%s' native '%S'", res
,
3812 cygheap
->cwd
.get_posix (), path
.get_nt_native_path ());
3826 cygheap_fdget
cfd (fd
);
3828 res
= chdir (cfd
->get_name ());
3832 syscall_printf ("%R = fchdir(%d)", res
, fd
);
3836 /******************** Exported Path Routines *********************/
3838 /* Cover functions to the path conversion routines.
3839 These are exported to the world as cygwin_foo by cygwin.din. */
3841 #define return_with_errno(x) \
3851 cygwin_conv_path (cygwin_conv_path_t what
, const void *from
, void *to
,
3860 int how
= what
& CCP_CONVFLAGS_MASK
;
3861 what
&= CCP_CONVTYPE_MASK
;
3874 case CCP_POSIX_TO_WIN_A
:
3876 p
.check ((const char *) from
,
3877 PC_POSIX
| PC_SYM_FOLLOW
| PC_SYM_NOFOLLOW_REP
3878 | PC_NO_ACCESS_CHECK
3879 | ((how
& CCP_RELATIVE
) ? PC_NOFULL
: 0), stat_suffixes
);
3882 set_errno (p
.error
);
3885 PUNICODE_STRING up
= p
.get_nt_native_path ();
3887 sys_wcstombs (buf
, NT_MAX_PATH
,
3888 up
->Buffer
, up
->Length
/ sizeof (WCHAR
));
3889 /* Convert native path to standard DOS path. */
3890 if (!strncmp (buf
, "\\??\\", 4))
3893 if (buf
[1] != ':') /* native UNC path */
3896 else if (*buf
== '\\')
3898 /* Device name points to somewhere else in the NT namespace.
3899 Use GLOBALROOT prefix to convert to Win32 path. */
3900 char *p
= buf
+ sys_wcstombs (buf
, NT_MAX_PATH
,
3901 ro_u_globalroot
.Buffer
,
3902 ro_u_globalroot
.Length
3904 sys_wcstombs (p
, NT_MAX_PATH
- (p
- buf
),
3905 up
->Buffer
, up
->Length
/ sizeof (WCHAR
));
3907 lsiz
= strlen (buf
) + 1;
3908 /* TODO: Incoming "." is a special case which leads to a trailing
3909 backslash ".\\" in the Win32 path. That's a result of the
3910 conversion in normalize_posix_path. This should not occur
3911 so the below code is just a band-aid. */
3912 if ((how
& CCP_RELATIVE
) && !strcmp ((const char *) from
, ".")
3913 && !strcmp (buf
, ".\\"))
3920 case CCP_POSIX_TO_WIN_W
:
3921 p
.check ((const char *) from
,
3922 PC_POSIX
| PC_SYM_FOLLOW
| PC_SYM_NOFOLLOW_REP
3923 | PC_NO_ACCESS_CHECK
3924 | ((how
& CCP_RELATIVE
) ? PC_NOFULL
: 0), stat_suffixes
);
3927 set_errno (p
.error
);
3930 /* Relative Windows paths are always restricted to MAX_PATH chars. */
3931 if ((how
& CCP_RELATIVE
) && !isabspath (p
.get_win32 ())
3932 && sys_mbstowcs (NULL
, 0, p
.get_win32 ()) > MAX_PATH
)
3934 /* Recreate as absolute path. */
3935 p
.check ((const char *) from
, PC_POSIX
| PC_SYM_FOLLOW
3936 | PC_NO_ACCESS_CHECK
);
3939 set_errno (p
.error
);
3943 lsiz
= p
.get_wide_win32_path_len () + 1;
3944 path
= p
.get_nt_native_path ()->Buffer
;
3946 /* Convert native path to standard DOS path. */
3947 if (!wcsncmp (path
, L
"\\??\\", 4))
3951 /* Drop long path prefix for short pathnames. Unfortunately there's
3952 quite a bunch of Win32 functions, especially in user32.dll,
3953 apparently, which don't grok long path names at all, not even
3954 in the UNICODE API. */
3955 if ((path
[5] == L
':' && lsiz
<= MAX_PATH
+ 4)
3956 || (!wcsncmp (path
+ 4, L
"UNC\\", 4) && lsiz
<= MAX_PATH
+ 6))
3960 if (path
[1] != L
':')
3962 *(path
+= 2) = '\\';
3967 else if (*path
== L
'\\')
3969 /* Device name points to somewhere else in the NT namespace.
3970 Use GLOBALROOT prefix to convert to Win32 path. */
3971 to
= (void *) wcpcpy ((wchar_t *) to
, ro_u_globalroot
.Buffer
);
3972 lsiz
+= ro_u_globalroot
.Length
/ sizeof (WCHAR
);
3974 /* TODO: Same ".\\" band-aid as in CCP_POSIX_TO_WIN_A case. */
3975 if ((how
& CCP_RELATIVE
) && !strcmp ((const char *) from
, ".")
3976 && !wcscmp (path
, L
".\\"))
3981 lsiz
*= sizeof (WCHAR
);
3983 case CCP_WIN_A_TO_POSIX
:
3985 error
= mount_table
->conv_to_posix_path ((const char *) from
, buf
,
3986 how
| __CCP_APP_SLASH
);
3989 set_errno (p
.error
);
3992 lsiz
= strlen (buf
) + 1;
3994 case CCP_WIN_W_TO_POSIX
:
3996 error
= mount_table
->conv_to_posix_path ((const PWCHAR
) from
, buf
,
3997 how
| __CCP_APP_SLASH
);
4003 lsiz
= strlen (buf
) + 1;
4021 case CCP_POSIX_TO_WIN_A
:
4022 case CCP_WIN_A_TO_POSIX
:
4023 case CCP_WIN_W_TO_POSIX
:
4024 stpcpy ((char *) to
, buf
);
4026 case CCP_POSIX_TO_WIN_W
:
4027 wcpcpy ((PWCHAR
) to
, path
);
4032 __except (EFAULT
) {}
4038 cygwin_create_path (cygwin_conv_path_t what
, const void *from
)
4041 ssize_t size
= cygwin_conv_path (what
, from
, NULL
, 0);
4044 else if (!(to
= malloc (size
)))
4046 if (cygwin_conv_path (what
, from
, to
, size
) == -1)
4057 cygwin_conv_to_win32_path (const char *path
, char *win32_path
)
4059 return cygwin_conv_path (CCP_POSIX_TO_WIN_A
| CCP_RELATIVE
, path
, win32_path
,
4064 cygwin_conv_to_full_win32_path (const char *path
, char *win32_path
)
4066 return cygwin_conv_path (CCP_POSIX_TO_WIN_A
| CCP_ABSOLUTE
, path
, win32_path
,
4070 /* This is exported to the world as cygwin_foo by cygwin.din. */
4073 cygwin_conv_to_posix_path (const char *path
, char *posix_path
)
4075 return cygwin_conv_path (CCP_WIN_A_TO_POSIX
| CCP_RELATIVE
, path
, posix_path
,
4080 cygwin_conv_to_full_posix_path (const char *path
, char *posix_path
)
4082 return cygwin_conv_path (CCP_WIN_A_TO_POSIX
| CCP_ABSOLUTE
, path
, posix_path
,
4086 #endif /* __i386__ */
4088 /* The realpath function is required by POSIX:2008. */
4091 realpath (const char *__restrict path
, char *__restrict resolved
)
4096 /* Make sure the right errno is returned if path is NULL. */
4103 /* Guard reading from a potentially invalid path and writing to a
4104 potentially invalid resolved. */
4107 /* Win32 drive letter paths and, generally, any path starting with a
4108 backslash, have to be converted to a POSIX path first, because
4109 path_conv leaves the incoming path untouched except for converting
4110 backslashes to forward slashes. This also covers '\\?\ and '\??\'
4112 if (isdrive (path
) || path
[0] == '\\')
4114 tpath
= tp
.c_get ();
4115 mount_table
->conv_to_posix_path (path
, tpath
, 0);
4118 tpath
= (char *) path
;
4120 path_conv
real_path (tpath
, PC_SYM_FOLLOW
| PC_POSIX
, stat_suffixes
);
4123 /* POSIX 2008 requires malloc'ing if resolved is NULL, and states
4124 that using non-NULL resolved is asking for portability
4127 if (!real_path
.error
&& real_path
.exists ())
4132 malloc (strlen (real_path
.get_posix ()) + 1);
4136 strcpy (resolved
, real_path
.get_posix ());
4140 /* FIXME: on error, Linux puts the name of the path
4141 component which could not be resolved into RESOLVED, but POSIX
4142 does not require this. */
4145 set_errno (real_path
.error
?: ENOENT
);
4147 __except (EFAULT
) {}
4152 /* Linux provides this extension. Since the only portable use of
4153 realpath requires a NULL second argument, we might as well have a
4154 one-argument wrapper. */
4156 canonicalize_file_name (const char *path
)
4158 return realpath (path
, NULL
);
4161 /* Return non-zero if path is a POSIX path list.
4162 This is exported to the world as cygwin_foo by cygwin.din.
4165 <sect1 id="add-func-cygwin-posix-path-list-p">
4166 <para>Rather than use a mode to say what the "proper" path list
4167 format is, we allow any, and give apps the tools they need to
4168 convert between the two. If a ';' is present in the path list it's
4169 a Win32 path list. Otherwise, if the first path begins with
4170 [letter]: (in which case it can be the only element since if it
4171 wasn't a ';' would be present) it's a Win32 path list. Otherwise,
4172 it's a POSIX path list.</para>
4178 cygwin_posix_path_list_p (const char *path
)
4180 int posix_p
= !(strchr (path
, ';') || isdrive (path
));
4184 /* These are used for apps that need to convert env vars like PATH back and
4185 forth. The conversion is a two step process. First, an upper bound on the
4186 size of the buffer needed is computed. Then the conversion is done. This
4187 allows the caller to use alloca if it wants. */
4190 conv_path_list_buf_size (const char *path_list
, bool to_posix
)
4192 int i
, num_elms
, max_mount_path_len
, size
;
4195 path_conv
pc(".", PC_POSIX
);
4196 /* The theory is that an upper bound is
4197 current_size + (num_elms * max_mount_path_len) */
4198 /* FIXME: This method is questionable in the long run. */
4201 char delim
= to_posix
? ';' : ':';
4202 for (p
= path_list
, num_elms
= nrel
= 0; p
; num_elms
++)
4206 p
= strchr (++p
, delim
);
4209 /* 7: strlen ("//c") + slop, a conservative initial value */
4210 for (max_mount_path_len
= sizeof ("/cygdrive/X"), i
= 0;
4211 i
< mount_table
->nmounts
; i
++)
4213 int mount_len
= (to_posix
4214 ? mount_table
->mount
[i
].posix_pathlen
4215 : mount_table
->mount
[i
].native_pathlen
);
4216 if (max_mount_path_len
< mount_len
)
4217 max_mount_path_len
= mount_len
;
4221 size
= strlen (path_list
)
4222 + (num_elms
* max_mount_path_len
)
4223 + (nrel
* strlen (to_posix
? pc
.get_posix () : pc
.get_win32 ()))
4230 env_PATH_to_posix (const void *win32
, void *posix
, size_t size
)
4232 return_with_errno (conv_path_list ((const char *) win32
, (char *) posix
,
4239 cygwin_win32_to_posix_path_list_buf_size (const char *path_list
)
4241 return conv_path_list_buf_size (path_list
, true);
4245 cygwin_posix_to_win32_path_list_buf_size (const char *path_list
)
4247 return conv_path_list_buf_size (path_list
, false);
4251 cygwin_win32_to_posix_path_list (const char *win32
, char *posix
)
4253 return_with_errno (conv_path_list (win32
, posix
, MAX_PATH
,
4254 CCP_WIN_A_TO_POSIX
| CCP_RELATIVE
));
4258 cygwin_posix_to_win32_path_list (const char *posix
, char *win32
)
4260 return_with_errno (conv_path_list (posix
, win32
, MAX_PATH
,
4261 CCP_POSIX_TO_WIN_A
| CCP_RELATIVE
));
4264 #endif /* __i386__ */
4267 cygwin_conv_path_list (cygwin_conv_path_t what
, const void *from
, void *to
,
4272 void *orig_to
= NULL
;
4275 switch (what
& CCP_CONVTYPE_MASK
)
4277 case CCP_WIN_W_TO_POSIX
:
4278 if (!sys_wcstombs_alloc (&winp
, HEAP_NOTHEAP
, (const wchar_t *) from
,
4281 what
= (what
& ~CCP_CONVTYPE_MASK
) | CCP_WIN_A_TO_POSIX
;
4282 from
= (const void *) winp
;
4284 case CCP_POSIX_TO_WIN_W
:
4286 return conv_path_list_buf_size ((const char *) from
, 0)
4288 what
= (what
& ~CCP_CONVTYPE_MASK
) | CCP_POSIX_TO_WIN_A
;
4290 to
= (void *) tp
.w_get ();
4294 switch (what
& CCP_CONVTYPE_MASK
)
4296 case CCP_WIN_A_TO_POSIX
:
4297 case CCP_POSIX_TO_WIN_A
:
4299 return conv_path_list_buf_size ((const char *) from
,
4300 what
== CCP_WIN_A_TO_POSIX
);
4301 ret
= conv_path_list ((const char *) from
, (char *) to
, size
, what
);
4302 /* Free winp buffer in case of CCP_WIN_W_TO_POSIX. */
4305 /* Convert to WCHAR in case of CCP_POSIX_TO_WIN_W. */
4307 sys_mbstowcs ((wchar_t *) orig_to
, size
/ sizeof (WCHAR
),
4308 (const char *) to
, (size_t) -1);
4309 return_with_errno (ret
);
4318 /* cygwin_split_path: Split a path into directory and file name parts.
4319 Buffers DIR and FILE are assumed to be big enough.
4321 Examples (path -> `dir' / `file'):
4324 . -> `.' / `.' (FIXME: should this be `.' / `'?)
4325 .. -> `.' / `..' (FIXME: should this be `..' / `'?)
4327 foo/bar -> `foo' / `bar'
4328 foo/bar/ -> `foo' / `bar'
4330 /foo/bar -> `/foo' / `bar'
4333 c:foo -> `c:/' / `foo'
4334 c:/foo -> `c:/' / `foo'
4338 cygwin_split_path (const char *path
, char *dir
, char *file
)
4340 int dir_started_p
= 0;
4342 /* Deal with drives.
4343 Remember that c:foo <==> c:/foo. */
4355 if (isdirsep (*path
))
4360 /* Determine if there are trailing slashes and "delete" them if present.
4361 We pretend as if they don't exist. */
4362 const char *end
= path
+ strlen (path
);
4363 /* path + 1: keep leading slash. */
4364 while (end
> path
+ 1 && isdirsep (end
[-1]))
4367 /* At this point, END points to one beyond the last character
4368 (with trailing slashes "deleted"). */
4370 /* Point LAST_SLASH at the last slash (duh...). */
4371 const char *last_slash
;
4372 for (last_slash
= end
- 1; last_slash
>= path
; --last_slash
)
4373 if (isdirsep (*last_slash
))
4376 if (last_slash
== path
)
4381 else if (last_slash
> path
)
4383 memcpy (dir
, path
, last_slash
- path
);
4384 dir
[last_slash
- path
] = 0;
4389 ; /* nothing to do */
4395 memcpy (file
, last_slash
+ 1, end
- last_slash
- 1);
4396 file
[end
- last_slash
- 1] = 0;
4400 copy_cwd_str (PUNICODE_STRING tgt
, PUNICODE_STRING src
)
4402 RtlCopyUnicodeString (tgt
, src
);
4403 if (tgt
->Buffer
[tgt
->Length
/ sizeof (WCHAR
) - 1] != L
'\\')
4405 tgt
->Buffer
[tgt
->Length
/ sizeof (WCHAR
)] = L
'\\';
4406 tgt
->Length
+= sizeof (WCHAR
);
4410 /*****************************************************************************/
4412 /* The find_fast_cwd_pointer function and parts of the
4413 cwdstuff::override_win32_cwd method are based on code using the
4416 Copyright 2010 John Carey. All rights reserved.
4418 Redistribution and use in source and binary forms, with or without
4419 modification, are permitted provided that the following conditions
4422 1. Redistributions of source code must retain the above
4423 copyright notice, this list of conditions and the following
4426 2. Redistributions in binary form must reproduce the above
4427 copyright notice, this list of conditions and the following
4428 disclaimer in the documentation and/or other materials provided
4429 with the distribution.
4431 THIS SOFTWARE IS PROVIDED BY JOHN CAREY ``AS IS'' AND ANY EXPRESS
4432 OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
4433 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
4434 ARE DISCLAIMED. IN NO EVENT SHALL JOHN CAREY OR CONTRIBUTORS BE
4435 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
4436 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
4437 OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
4438 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
4439 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
4440 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
4441 USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
4445 fcwd_access_t::SetFSCharacteristics (LONG val
)
4447 /* Special case FSCharacteristics. Didn't exist originally. */
4448 switch (fast_cwd_version ())
4453 f7
.FSCharacteristics
= val
;
4456 f8
.FSCharacteristics
= val
;
4462 fcwd_access_t::fast_cwd_version ()
4464 return cygheap
->cwd
.fast_cwd_version
;
4468 fcwd_access_t::CopyPath (UNICODE_STRING
&target
)
4470 /* Copy the Path contents over into the UNICODE_STRING referenced by
4471 target. This is used to set the CurrentDirectoryName in the
4472 user parameter block. */
4477 fcwd_access_t::Free (PVOID heap
)
4479 /* Decrement the reference count. If it's down to 0, free
4480 structure from heap. */
4481 if (InterlockedDecrement (&ReferenceCount ()) == 0)
4483 /* The handle on init is always a fresh one, not the handle inherited
4484 from the parent process. We always have to close it here.
4485 Note: The handle could be NULL, if we cd'ed into a virtual dir. */
4486 HANDLE h
= DirectoryHandle ();
4489 RtlFreeHeap (heap
, 0, this);
4494 fcwd_access_t::FillIn (HANDLE dir
, PUNICODE_STRING name
,
4495 ULONG old_dismount_count
)
4497 /* Fill in all values into this FAST_CWD structure. */
4498 DirectoryHandle () = dir
;
4499 ReferenceCount () = 1;
4500 OldDismountCount () = old_dismount_count
;
4501 /* The new structure stores the device characteristics of the
4502 volume holding the dir. RtlGetCurrentDirectory_U checks
4503 if the FILE_REMOVABLE_MEDIA flag is set and, if so, checks if
4504 the volume is still the same as the one used when opening
4505 the directory handle.
4506 We don't call NtQueryVolumeInformationFile for the \\?\PIPE,
4507 though. It just returns STATUS_INVALID_HANDLE anyway. */
4508 if (fast_cwd_version () != FCWD_OLD
)
4510 SetFSCharacteristics (0);
4511 if (name
!= &ro_u_pipedir
)
4514 FILE_FS_DEVICE_INFORMATION ffdi
;
4515 if (NT_SUCCESS (NtQueryVolumeInformationFile (dir
, &io
, &ffdi
,
4516 sizeof ffdi
, FileFsDeviceInformation
)))
4517 SetFSCharacteristics (ffdi
.Characteristics
);
4520 RtlInitEmptyUnicodeString (&Path (), Buffer (),
4521 MAX_PATH
* sizeof (WCHAR
));
4522 copy_cwd_str (&Path (), name
);
4526 fcwd_access_t::SetDirHandleFromBufferPointer (PWCHAR buf_p
, HANDLE dir
)
4528 /* Input: The buffer pointer as it's stored in the user parameter block
4529 and a directory handle.
4530 This function computes the address to the FAST_CWD structure based
4531 on the version and overwrites the directory handle. It is only
4532 used if we couldn't figure out the address of fast_cwd_ptr. */
4533 fcwd_access_t
*f_cwd
;
4534 switch (fast_cwd_version ())
4538 f_cwd
= (fcwd_access_t
*)
4539 ((PBYTE
) buf_p
- __builtin_offsetof (FAST_CWD_OLD
, Buffer
));
4542 f_cwd
= (fcwd_access_t
*)
4543 ((PBYTE
) buf_p
- __builtin_offsetof (FAST_CWD_7
, Buffer
));
4546 f_cwd
= (fcwd_access_t
*)
4547 ((PBYTE
) buf_p
- __builtin_offsetof (FAST_CWD_8
, Buffer
));
4550 f_cwd
->DirectoryHandle () = dir
;
4554 fcwd_access_t::SetVersionFromPointer (PBYTE buf_p
, bool is_buffer
)
4556 /* Given a pointer to the FAST_CWD structure (is_buffer == false) or a
4557 pointer to the Buffer within (is_buffer == true), this function
4558 computes the FAST_CWD version by checking that Path.MaximumLength
4559 equals MAX_PATH, and that Path.Buffer == Buffer. */
4561 buf_p
-= __builtin_offsetof (FAST_CWD_8
, Buffer
);
4562 fcwd_access_t
*f_cwd
= (fcwd_access_t
*) buf_p
;
4563 if (f_cwd
->f8
.Path
.MaximumLength
== MAX_PATH
* sizeof (WCHAR
)
4564 && f_cwd
->f8
.Path
.Buffer
== f_cwd
->f8
.Buffer
)
4565 fast_cwd_version () = FCWD_W8
;
4566 else if (f_cwd
->f7
.Path
.MaximumLength
== MAX_PATH
* sizeof (WCHAR
)
4567 && f_cwd
->f7
.Path
.Buffer
== f_cwd
->f7
.Buffer
)
4568 fast_cwd_version () = FCWD_W7
;
4570 fast_cwd_version () = FCWD_OLD
;
4573 /* This function scans the code in ntdll.dll to find the address of the
4574 global variable used to access the CWD. While the pointer is global,
4575 it's not exported from the DLL, unfortunately. Therefore we have to
4576 use some knowledge to figure out the address. */
4580 #define peek32(x) (*(int32_t *)(x))
4582 static fcwd_access_t
**
4583 find_fast_cwd_pointer ()
4585 /* Fetch entry points of relevant functions in ntdll.dll. */
4586 HMODULE ntdll
= GetModuleHandle ("ntdll.dll");
4589 const uint8_t *get_dir
= (const uint8_t *)
4590 GetProcAddress (ntdll
, "RtlGetCurrentDirectory_U");
4591 const uint8_t *ent_crit
= (const uint8_t *)
4592 GetProcAddress (ntdll
, "RtlEnterCriticalSection");
4593 if (!get_dir
|| !ent_crit
)
4595 /* Search first relative call instruction in RtlGetCurrentDirectory_U. */
4596 const uint8_t *rcall
= (const uint8_t *) memchr (get_dir
, 0xe8, 80);
4599 /* Fetch offset from instruction and compute address of called function.
4600 This function actually fetches the current FAST_CWD instance and
4601 performs some other actions, not important to us. */
4602 const uint8_t *use_cwd
= rcall
+ 5 + peek32 (rcall
+ 1);
4603 /* Next we search for the locking mechanism and perform a sanity check.
4604 On Pre-Windows 8 we basically look for the RtlEnterCriticalSection call.
4605 Windows 8 does not call RtlEnterCriticalSection. The code manipulates
4606 the FastPebLock manually, probably because RtlEnterCriticalSection has
4607 been converted to an inline function. Either way, we test if the code
4608 uses the FastPebLock. */
4609 const uint8_t *movrbx
;
4610 const uint8_t *lock
= (const uint8_t *)
4611 memmem ((const char *) use_cwd
, 80,
4612 "\xf0\x0f\xba\x35", 4);
4615 /* The lock instruction tweaks the LockCount member, which is not at
4616 the start of the PRTL_CRITICAL_SECTION structure. So we have to
4617 subtract the offset of LockCount to get the real address. */
4618 PRTL_CRITICAL_SECTION lockaddr
=
4619 (PRTL_CRITICAL_SECTION
) (lock
+ 9 + peek32 (lock
+ 4)
4620 - offsetof (RTL_CRITICAL_SECTION
, LockCount
));
4621 /* Test if lock address is FastPebLock. */
4622 if (lockaddr
!= NtCurrentTeb ()->Peb
->FastPebLock
)
4624 /* Search `mov rel(%rip),%rbx'. This is the instruction fetching the
4625 address of the current fcwd_access_t pointer, and it should be pretty
4626 near to the locking stuff. */
4627 movrbx
= (const uint8_t *) memmem ((const char *) lock
, 40,
4632 /* Usually the callq RtlEnterCriticalSection follows right after
4633 fetching the lock address. */
4634 int call_rtl_offset
= 7;
4635 /* Search `lea rel(%rip),%rcx'. This loads the address of the lock into
4636 %rcx for the subsequent RtlEnterCriticalSection call. */
4637 lock
= (const uint8_t *) memmem ((const char *) use_cwd
, 80,
4641 /* Windows 8.1 Preview calls `lea rel(rip),%r12' then some unrelated
4642 or, then `mov %r12,%rcx', then `callq RtlEnterCriticalSection'. */
4643 lock
= (const uint8_t *) memmem ((const char *) use_cwd
, 80,
4647 call_rtl_offset
= 14;
4649 PRTL_CRITICAL_SECTION lockaddr
=
4650 (PRTL_CRITICAL_SECTION
) (lock
+ 7 + peek32 (lock
+ 3));
4651 /* Test if lock address is FastPebLock. */
4652 if (lockaddr
!= NtCurrentTeb ()->Peb
->FastPebLock
)
4654 /* Next is the `callq RtlEnterCriticalSection'. */
4655 lock
+= call_rtl_offset
;
4656 if (lock
[0] != 0xe8)
4658 const uint8_t *call_addr
= (const uint8_t *)
4659 (lock
+ 5 + peek32 (lock
+ 1));
4660 if (call_addr
!= ent_crit
)
4662 /* In contrast to the above Windows 8 code, we don't have to search
4663 for the `mov rel(%rip),%rbx' instruction. It follows right after
4664 the call to RtlEnterCriticalSection. */
4669 /* Check that the next instruction tests if the fetched value is NULL. */
4670 const uint8_t *testrbx
= (const uint8_t *)
4671 memmem (movrbx
+ 7, 3, "\x48\x85\xdb", 3);
4674 /* Compute address of the fcwd_access_t ** pointer. */
4675 return (fcwd_access_t
**) (testrbx
+ peek32 (movrbx
+ 3));
4679 #define peek32(x) (*(uint32_t *)(x))
4681 static fcwd_access_t
**
4682 find_fast_cwd_pointer ()
4684 /* Fetch entry points of relevant functions in ntdll.dll. */
4685 HMODULE ntdll
= GetModuleHandle ("ntdll.dll");
4688 const uint8_t *get_dir
= (const uint8_t *)
4689 GetProcAddress (ntdll
, "RtlGetCurrentDirectory_U");
4690 const uint8_t *ent_crit
= (const uint8_t *)
4691 GetProcAddress (ntdll
, "RtlEnterCriticalSection");
4692 if (!get_dir
|| !ent_crit
)
4694 /* Search first relative call instruction in RtlGetCurrentDirectory_U. */
4695 const uint8_t *rcall
= (const uint8_t *) memchr (get_dir
, 0xe8, 64);
4698 /* Fetch offset from instruction and compute address of called function.
4699 This function actually fetches the current FAST_CWD instance and
4700 performs some other actions, not important to us. */
4701 ptrdiff_t offset
= (ptrdiff_t) peek32 (rcall
+ 1);
4702 const uint8_t *use_cwd
= rcall
+ 5 + offset
;
4703 /* Find first `push %edi' instruction. */
4704 const uint8_t *pushedi
= (const uint8_t *) memchr (use_cwd
, 0x57, 32);
4707 /* ...which should be followed by `mov crit-sect-addr,%edi' then
4708 `push %edi', or by just a single `push crit-sect-addr'. */
4709 const uint8_t *movedi
= pushedi
+ 1;
4710 const uint8_t *mov_pfast_cwd
;
4711 if (movedi
[0] == 0x8b && movedi
[1] == 0xff) /* mov %edi,%edi -> W8 */
4713 /* Windows 8 does not call RtlEnterCriticalSection. The code manipulates
4714 the FastPebLock manually, probably because RtlEnterCriticalSection has
4715 been converted to an inline function.
4717 Next we search for a `mov some address,%eax'. This address points
4718 to the LockCount member of the FastPebLock structure, so the address
4719 is equal to FastPebLock + 4. */
4720 const uint8_t *moveax
= (const uint8_t *) memchr (movedi
, 0xb8, 16);
4723 offset
= (ptrdiff_t) peek32 (moveax
+ 1) - 4;
4724 /* Compare the address with the known PEB lock as stored in the PEB. */
4725 if ((PRTL_CRITICAL_SECTION
) offset
!= NtCurrentTeb ()->Peb
->FastPebLock
)
4727 /* Now search for the mov instruction fetching the address of the global
4729 mov_pfast_cwd
= moveax
;
4732 mov_pfast_cwd
= (const uint8_t *) memchr (++mov_pfast_cwd
, 0x8b, 48);
4734 while (mov_pfast_cwd
&& mov_pfast_cwd
[1] != 0x1d
4735 && (mov_pfast_cwd
- moveax
) < 48);
4736 if (!mov_pfast_cwd
|| mov_pfast_cwd
[1] != 0x1d)
4741 if (movedi
[0] == 0xbf && movedi
[5] == 0x57)
4743 else if (movedi
[0] == 0x68)
4745 else if (movedi
[0] == 0x88 && movedi
[4] == 0x83 && movedi
[7] == 0x68)
4747 /* Windows 8.1 Preview: The `mov lock_addr,%edi' is actually a
4748 `mov %cl,15(%esp), followed by an `or #-1,%ebx, followed by a
4749 `push lock_addr'. */
4755 /* Compare the address used for the critical section with the known
4756 PEB lock as stored in the PEB. */
4757 if ((PRTL_CRITICAL_SECTION
) peek32 (movedi
+ 1)
4758 != NtCurrentTeb ()->Peb
->FastPebLock
)
4760 /* To check we are seeing the right code, we check our expectation that
4761 the next instruction is a relative call into RtlEnterCriticalSection. */
4762 if (rcall
[0] != 0xe8)
4764 /* Check that this is a relative call to RtlEnterCriticalSection. */
4765 offset
= (ptrdiff_t) peek32 (rcall
+ 1);
4766 if (rcall
+ 5 + offset
!= ent_crit
)
4768 mov_pfast_cwd
= rcall
+ 5;
4770 /* After locking the critical section, the code should read the global
4771 PFAST_CWD * pointer that is guarded by that critical section. */
4772 if (mov_pfast_cwd
[0] != 0x8b)
4774 return (fcwd_access_t
**) peek32 (mov_pfast_cwd
+ 2);
4778 static fcwd_access_t
**
4781 /* Fetch the pointer but don't set the global fast_cwd_ptr yet. First
4782 we have to make sure we know the version of the FAST_CWD structure
4783 used on the system. */
4784 fcwd_access_t
**f_cwd_ptr
= find_fast_cwd_pointer ();
4788 USHORT emulated
, hosted
;
4790 /* Check if we're running in WOW64 on ARM64. Check on 64 bit as well,
4791 given that ARM64 Windows 10 provides a x86_64 emulation soon. Skip
4792 warning as long as there's no solution for finding the FAST_CWD
4793 pointer on that system. */
4794 if (IsWow64Process2 (GetCurrentProcess (), &emulated
, &hosted
)
4795 && hosted
== IMAGE_FILE_MACHINE_ARM64
)
4799 small_printf ("Cygwin WARNING:\n"
4800 " Couldn't compute FAST_CWD pointer. This typically occurs if you're using\n"
4801 " an older Cygwin version on a newer Windows. Please update to the latest\n"
4802 " available Cygwin version from https://cygwin.com/. If the problem persists,\n"
4803 " please see https://cygwin.com/problems.html\n\n");
4805 if (f_cwd_ptr
&& *f_cwd_ptr
)
4807 /* Just evaluate structure version. */
4808 fcwd_access_t::SetVersionFromPointer ((PBYTE
) *f_cwd_ptr
, false);
4812 /* If we couldn't fetch fast_cwd_ptr, or if fast_cwd_ptr is NULL(*)
4813 we have to figure out the version from the Buffer pointer in the
4816 (*) This is very unlikely to happen when starting the first
4817 Cygwin process, since it only happens when starting the
4818 process in a directory which can't be used as CWD by Win32, or
4819 if the directory doesn't exist. But *if* it happens, we have
4820 no valid FAST_CWD structure, even though upp_cwd_str.Buffer is
4821 not NULL in that case. So we let the OS create a valid
4822 FAST_CWD structure temporarily to have something to work with.
4823 We know the pipe FS works. */
4824 PEB
&peb
= *NtCurrentTeb ()->Peb
;
4826 if (f_cwd_ptr
/* so *f_cwd_ptr == NULL */
4827 && !NT_SUCCESS (RtlSetCurrentDirectory_U (&ro_u_pipedir
)))
4828 api_fatal ("Couldn't set directory to %S temporarily.\n"
4829 "Cannot continue.", &ro_u_pipedir
);
4830 RtlEnterCriticalSection (peb
.FastPebLock
);
4831 fcwd_access_t::SetVersionFromPointer
4832 ((PBYTE
) peb
.ProcessParameters
->CurrentDirectoryName
.Buffer
, true);
4833 RtlLeaveCriticalSection (peb
.FastPebLock
);
4835 /* Eventually, after we set the version as well, set fast_cwd_ptr. */
4840 cwdstuff::override_win32_cwd (bool init
, ULONG old_dismount_count
)
4844 PEB
&peb
= *NtCurrentTeb ()->Peb
;
4845 UNICODE_STRING
&upp_cwd_str
= peb
.ProcessParameters
->CurrentDirectoryName
;
4846 HANDLE
&upp_cwd_hdl
= peb
.ProcessParameters
->CurrentDirectoryHandle
;
4848 if (fast_cwd_ptr
== (fcwd_access_t
**) -1)
4849 fast_cwd_ptr
= find_fast_cwd ();
4852 /* If we got a valid value for fast_cwd_ptr, we can simply replace
4853 the RtlSetCurrentDirectory_U function entirely. */
4854 PVOID heap
= peb
.ProcessHeap
;
4855 /* First allocate a new fcwd_access_t structure on the heap.
4856 The new fcwd_access_t structure is 4 byte bigger than the old one,
4857 but we simply don't care, so we allocate always room for the
4859 fcwd_access_t
*f_cwd
= (fcwd_access_t
*)
4860 RtlAllocateHeap (heap
, 0, sizeof (fcwd_access_t
));
4863 debug_printf ("RtlAllocateHeap failed");
4866 /* Fill in the values. */
4867 f_cwd
->FillIn (dir
, error
? &ro_u_pipedir
: &win32
,
4868 old_dismount_count
);
4869 /* Use PEB lock when switching fast_cwd_ptr to the new FAST_CWD
4870 structure and writing the CWD to the user process parameter
4871 block. This is equivalent to calling RtlAcquirePebLock/
4872 RtlReleasePebLock, but without having to go through the FS
4874 RtlEnterCriticalSection (peb
.FastPebLock
);
4875 fcwd_access_t
*old_cwd
= *fast_cwd_ptr
;
4876 *fast_cwd_ptr
= f_cwd
;
4877 f_cwd
->CopyPath (upp_cwd_str
);
4879 RtlLeaveCriticalSection (peb
.FastPebLock
);
4881 old_cwd
->Free (heap
);
4885 /* Fallback if we failed to find the fast_cwd_ptr value:
4887 - Call RtlSetCurrentDirectory_U.
4888 - Compute new FAST_CWD struct address from buffer pointer in the
4889 user process parameter block.
4890 - Replace the directory handle in the struct with our own handle.
4891 - Close the original handle. RtlSetCurrentDirectory_U already
4892 closed our former dir handle -> no handle leak.
4894 Guard the entire operation with FastPebLock to avoid races
4895 accessing the PEB and FAST_CWD struct.
4897 Unfortunately this method is still prone to a directory usage
4900 - The directory is locked against deletion or renaming between the
4901 RtlSetCurrentDirectory_U and the subsequent NtClose call. */
4902 if (unlikely (upp_cwd_hdl
== NULL
) && init
)
4904 RtlEnterCriticalSection (peb
.FastPebLock
);
4908 RtlSetCurrentDirectory_U (error
? &ro_u_pipedir
: &win32
);
4909 if (!NT_SUCCESS (status
))
4911 RtlLeaveCriticalSection (peb
.FastPebLock
);
4912 debug_printf ("RtlSetCurrentDirectory_U(%S) failed, %y",
4913 error
? &ro_u_pipedir
: &win32
, status
);
4917 fcwd_access_t::SetDirHandleFromBufferPointer(upp_cwd_str
.Buffer
, dir
);
4920 RtlLeaveCriticalSection (peb
.FastPebLock
);
4921 /* The handle on init is always a fresh one, not the handle inherited
4922 from the parent process. We always have to close it here. */
4927 /* Initialize cygcwd 'muto' for serializing access to cwd info. */
4931 cwd_lock
.init ("cwd_lock");
4933 /* Cygwin processes inherit the cwd from their parent. If the win32 path
4934 buffer is not NULL, the cwd struct is already set up, and we only
4935 have to override the Win32 CWD with ours. */
4937 override_win32_cwd (true, SharedUserData
.DismountCount
);
4940 /* Initialize fast_cwd stuff. */
4941 fast_cwd_ptr
= (fcwd_access_t
**) -1;
4942 fast_cwd_version
= FCWD_W7
;
4943 /* Initially re-open the cwd to allow POSIX semantics. */
4948 /* Chdir and fill out the elements of a cwdstuff struct. */
4950 cwdstuff::set (path_conv
*nat_cwd
, const char *posix_cwd
)
4953 UNICODE_STRING upath
;
4954 OBJECT_ATTRIBUTES attr
;
4955 PEB
&peb
= *NtCurrentTeb ()->Peb
;
4956 bool virtual_path
= false;
4957 bool unc_path
= false;
4958 bool inaccessible_path
= false;
4960 /* Here are the problems with using SetCurrentDirectory. Just skip this
4961 comment if you don't like whining.
4963 - SetCurrentDirectory only supports paths of up to MAX_PATH - 1 chars,
4964 including a trailing backslash. That's an absolute restriction, even
4967 - SetCurrentDirectory fails for directories with strict permissions even
4968 for processes with the SE_BACKUP_NAME privilege enabled. The reason
4969 is apparently that SetCurrentDirectory calls NtOpenFile without the
4970 FILE_OPEN_FOR_BACKUP_INTENT flag set.
4972 - SetCurrentDirectory does not support case-sensitivity.
4974 - Unlinking a cwd fails because SetCurrentDirectory seems to open
4975 directories so that deleting the directory is disallowed.
4977 - SetCurrentDirectory can naturally not work on virtual Cygwin paths
4978 like /proc or /cygdrive.
4980 Nevertheless, doing entirely without SetCurrentDirectory is not really
4981 feasible, because it breaks too many mixed applications using the Win32
4984 Therefore we handle the CWD all by ourselves and just keep the Win32
4985 CWD in sync. However, to avoid surprising behaviour in the Win32 API
4986 when we are in a CWD which is inaccessible as Win32 CWD, we set the
4987 Win32 CWD to a "weird" directory in which all relative filesystem-related
4990 cwd_lock
.acquire ();
4994 upath
= *nat_cwd
->get_nt_native_path ();
4995 if (nat_cwd
->isspecial ())
4997 virtual_path
= true;
4998 /* But allow starting of native apps from /dev if /dev actually
5000 if (isdev_dev (nat_cwd
->dev
))
5002 FILE_BASIC_INFORMATION fbi
;
5004 InitializeObjectAttributes (&attr
, &upath
,
5005 OBJ_CASE_INSENSITIVE
| OBJ_INHERIT
,
5007 status
= NtQueryAttributesFile (&attr
, &fbi
);
5008 if (status
!= STATUS_OBJECT_NAME_NOT_FOUND
5009 && status
!= STATUS_OBJECT_PATH_NOT_FOUND
)
5010 virtual_path
= false;
5015 /* Memorize old DismountCount before opening the dir. This value is
5016 stored in the FAST_CWD structure. It would be simpler to fetch the
5017 old DismountCount in override_win32_cwd, but Windows also fetches
5018 it before opening the directory handle. It's not quite clear if
5019 that's really required, but since we don't know the side effects of
5020 this action, we better follow Windows' lead. */
5021 ULONG old_dismount_count
= SharedUserData
.DismountCount
;
5022 /* Open a directory handle with FILE_OPEN_FOR_BACKUP_INTENT and with all
5023 sharing flags set. The handle is right now used in exceptions.cc only,
5024 but that might change in future. */
5032 /* On init, just reopen Win32 CWD with desired access flags.
5033 We can access the PEB without lock, because no other thread
5034 can change the CWD. However, there's a chance that the handle
5035 is NULL, even though CurrentDirectoryName isn't so we have to
5037 if (!peb
.ProcessParameters
->CurrentDirectoryHandle
)
5039 InitializeObjectAttributes (&attr
,
5040 &peb
.ProcessParameters
->CurrentDirectoryName
,
5041 OBJ_CASE_INSENSITIVE
| OBJ_INHERIT
, NULL
, NULL
);
5045 RtlInitUnicodeString (&upath
, L
"");
5046 InitializeObjectAttributes (&attr
,
5047 &upath
, OBJ_CASE_INSENSITIVE
| OBJ_INHERIT
,
5048 peb
.ProcessParameters
->CurrentDirectoryHandle
,
5053 InitializeObjectAttributes (&attr
, &upath
,
5054 nat_cwd
->objcaseinsensitive () | OBJ_INHERIT
,
5056 /* First try without FILE_OPEN_FOR_BACKUP_INTENT, to find out if the
5057 directory is valid for Win32 apps. And, no, we can't just call
5058 SetCurrentDirectory here, since that would potentially break
5059 case-sensitivity. */
5060 status
= NtOpenFile (&h
, SYNCHRONIZE
| FILE_TRAVERSE
, &attr
, &io
,
5061 FILE_SHARE_VALID_FLAGS
,
5063 | FILE_SYNCHRONOUS_IO_NONALERT
);
5064 if (status
== STATUS_ACCESS_DENIED
)
5066 status
= NtOpenFile (&h
, SYNCHRONIZE
| FILE_TRAVERSE
, &attr
, &io
,
5067 FILE_SHARE_VALID_FLAGS
,
5069 | FILE_SYNCHRONOUS_IO_NONALERT
5070 | FILE_OPEN_FOR_BACKUP_INTENT
);
5071 inaccessible_path
= true;
5073 if (!NT_SUCCESS (status
))
5075 /* Called from chdir? Just fail. */
5078 cwd_lock
.release ();
5079 __seterrno_from_nt_status (status
);
5082 /* Otherwise we're in init and posix hasn't been set yet. Try to
5083 duplicate the handle instead. If that fails, too, set dir to NULL
5084 and carry on. This will at least set posix to some valid path at
5085 process startup, and subsequent getcwd calls don't EFAULT. */
5086 debug_printf ("WARNING: Can't reopen CWD %y '%S', status %y",
5087 peb
.ProcessParameters
->CurrentDirectoryHandle
,
5088 &peb
.ProcessParameters
->CurrentDirectoryName
,
5090 if (!peb
.ProcessParameters
->CurrentDirectoryHandle
5091 || !DuplicateHandle (GetCurrentProcess (),
5092 peb
.ProcessParameters
->CurrentDirectoryHandle
,
5093 GetCurrentProcess (), &h
, 0, TRUE
, 0))
5095 cwd_lock
.release ();
5096 if (peb
.ProcessParameters
->CurrentDirectoryHandle
)
5097 debug_printf ("...and DuplicateHandle failed with %E.");
5102 /* Set new handle. Note that we simply overwrite the old handle here
5103 without closing it. The handle is also used as Win32 CWD handle in
5104 the user parameter block, and it will be closed in override_win32_cwd,
5110 /* On init, just fetch the Win32 dir from the PEB. We can access
5111 the PEB without lock, because no other thread can change the CWD
5113 PUNICODE_STRING pdir
= &peb
.ProcessParameters
->CurrentDirectoryName
;
5114 RtlInitEmptyUnicodeString (&win32
,
5115 (PWCHAR
) crealloc_abort (win32
.Buffer
,
5118 pdir
->Length
+ sizeof (WCHAR
));
5119 RtlCopyUnicodeString (&win32
, pdir
);
5121 PWSTR eoBuffer
= win32
.Buffer
+ (win32
.Length
/ sizeof (WCHAR
));
5122 /* Remove trailing slash if one exists. */
5123 if ((eoBuffer
- win32
.Buffer
) > 3 && eoBuffer
[-1] == L
'\\')
5124 win32
.Length
-= sizeof (WCHAR
);
5125 if (eoBuffer
[0] == L
'\\')
5132 if (!virtual_path
) /* don't mangle virtual path. */
5134 /* Convert into Win32 path and compute length. */
5135 if (upath
.Buffer
[1] == L
'?')
5138 upath
.Length
-= 4 * sizeof (WCHAR
);
5139 if (upath
.Buffer
[1] != L
':')
5143 upath
.Length
-= 2 * sizeof (WCHAR
);
5149 /* Path via native NT namespace. Prepend GLOBALROOT prefix
5150 to create a valid Win32 path. */
5151 PWCHAR buf
= (PWCHAR
) alloca (upath
.Length
5152 + ro_u_globalroot
.Length
5154 wcpcpy (wcpcpy (buf
, ro_u_globalroot
.Buffer
), upath
.Buffer
);
5156 upath
.Length
+= ro_u_globalroot
.Length
;
5158 PWSTR eoBuffer
= upath
.Buffer
+ (upath
.Length
/ sizeof (WCHAR
));
5159 /* Remove trailing slash if one exists. */
5160 if ((eoBuffer
- upath
.Buffer
) > 3 && eoBuffer
[-1] == L
'\\')
5161 upath
.Length
-= sizeof (WCHAR
);
5163 RtlInitEmptyUnicodeString (&win32
,
5164 (PWCHAR
) crealloc_abort (win32
.Buffer
,
5167 upath
.Length
+ sizeof (WCHAR
));
5168 RtlCopyUnicodeString (&win32
, &upath
);
5170 win32
.Buffer
[0] = L
'\\';
5172 /* Make sure it's NUL-terminated. */
5173 win32
.Buffer
[win32
.Length
/ sizeof (WCHAR
)] = L
'\0';
5175 /* Set drive_length, used in path conversion, and error code, used in
5176 spawn_guts to decide whether a native Win32 app can be started or not. */
5188 PWCHAR ptr
= wcschr (win32
.Buffer
+ 2, L
'\\');
5190 ptr
= wcschr (ptr
+ 1, L
'\\');
5192 drive_length
= ptr
- win32
.Buffer
;
5194 drive_length
= win32
.Length
/ sizeof (WCHAR
);
5196 if (inaccessible_path
)
5198 else if (win32
.Length
> (MAX_PATH
- 2) * sizeof (WCHAR
))
5199 error
= ENAMETOOLONG
;
5203 /* Keep the Win32 CWD in sync. Don't check for error, other than for
5204 strace output. Try to keep overhead low. */
5205 override_win32_cwd (!nat_cwd
, old_dismount_count
);
5207 /* Eventually, create POSIX path if it's not set on entry. */
5211 posix_cwd
= (const char *) tp
.c_get ();
5212 mount_table
->conv_to_posix_path (win32
.Buffer
, (char *) posix_cwd
, 0);
5214 posix
= (char *) crealloc_abort (posix
, strlen (posix_cwd
) + 1);
5215 stpcpy (posix
, posix_cwd
);
5217 cwd_lock
.release ();
5222 cwdstuff::get_error_desc () const
5224 switch (cygheap
->cwd
.get_error ())
5227 return "has restricted permissions which render it\n"
5228 "inaccessible as Win32 working directory";
5230 return "is a virtual Cygwin directory which does\n"
5231 "not exist for a native Windows application";
5233 return "has a path longer than allowed for a\n"
5234 "Win32 working directory";
5238 /* That shouldn't occur, unless we defined a new error code
5239 in cwdstuff::set. */
5240 return "is not accessible for some unknown reason";
5243 /* Store incoming wchar_t path as current posix cwd. This is called from
5244 setlocale so that the cwd is always stored in the right charset. */
5246 cwdstuff::reset_posix (wchar_t *w_cwd
)
5248 size_t len
= sys_wcstombs (NULL
, (size_t) -1, w_cwd
);
5249 posix
= (char *) crealloc_abort (posix
, len
+ 1);
5250 sys_wcstombs (posix
, len
+ 1, w_cwd
);
5254 cwdstuff::get (char *buf
, int need_posix
, int with_chroot
, unsigned ulen
)
5261 else if (buf
== NULL
)
5262 ulen
= (unsigned) -1;
5269 cwd_lock
.acquire ();
5274 tocopy
= tp
.c_get ();
5275 sys_wcstombs (tocopy
, NT_MAX_PATH
, win32
.Buffer
,
5276 win32
.Length
/ sizeof (WCHAR
));
5281 debug_printf ("posix %s", posix
);
5282 if (strlen (tocopy
) >= ulen
)
5290 buf
= (char *) malloc (strlen (tocopy
) + 1);
5291 strcpy (buf
, tocopy
);
5292 if (!buf
[0]) /* Should only happen when chroot */
5296 cwd_lock
.release ();
5299 syscall_printf ("(%s) = cwdstuff::get (%p, %u, %d, %d), errno %d",
5300 buf
, buf
, ulen
, need_posix
, with_chroot
, errno
);
5304 /* No need to be reentrant or thread-safe according to SUSv3.
5305 / and \\ are treated equally. Leading drive specifiers are
5306 kept intact as far as it makes sense. Everything else is
5307 POSIX compatible. */
5309 basename (char *path
)
5312 char *c
, *d
, *bs
= path
;
5314 if (!path
|| !*path
)
5315 return strcpy (buf
, ".");
5316 if (isalpha (path
[0]) && path
[1] == ':')
5318 else if (strspn (path
, "/\\") > 1)
5320 c
= strrchr (bs
, '/');
5321 if ((d
= strrchr (c
?: bs
, '\\')) > c
)
5325 /* Trailing (back)slashes are eliminated. */
5326 while (c
&& c
> bs
&& c
[1] == '\0')
5329 c
= strrchr (bs
, '/');
5330 if ((d
= strrchr (c
?: bs
, '\\')) > c
)
5333 if (c
&& (c
> bs
|| c
[1]))
5338 stpncpy (buf
, path
, bs
- path
);
5339 stpcpy (buf
+ (bs
- path
), ".");
5345 /* The differences with the POSIX version above:
5346 - declared in <string.h> (instead of <libgen.h>);
5347 - the argument is never modified, and therefore is marked const;
5348 - the empty string is returned if path is an empty string, "/", or ends
5349 with a trailing slash. */
5351 __gnu_basename (const char *path
)
5354 char *c
, *d
, *bs
= (char *)path
;
5356 if (!path
|| !*path
)
5357 return strcpy (buf
, "");
5358 if (isalpha (path
[0]) && path
[1] == ':')
5360 else if (strspn (path
, "/\\") > 1)
5362 c
= strrchr (bs
, '/');
5363 if ((d
= strrchr (c
?: bs
, '\\')) > c
)
5368 return strcpy (buf
, "");
5369 return (char *)path
;
5372 /* No need to be reentrant or thread-safe according to SUSv3.
5373 / and \\ are treated equally. Leading drive specifiers and
5374 leading double (back)slashes are kept intact as far as it
5375 makes sense. Everything else is POSIX compatible. */
5377 dirname (char *path
)
5380 char *c
, *d
, *bs
= path
;
5382 if (!path
|| !*path
)
5383 return strcpy (buf
, ".");
5384 if (isalpha (path
[0]) && path
[1] == ':')
5386 else if (strspn (path
, "/\\") > 1)
5388 c
= strrchr (bs
, '/');
5389 if ((d
= strrchr (c
?: bs
, '\\')) > c
)
5393 /* Trailing (back)slashes are eliminated. */
5394 while (c
&& c
> bs
&& c
[1] == '\0')
5397 c
= strrchr (bs
, '/');
5398 if ((d
= strrchr (c
?: bs
, '\\')) > c
)
5405 /* More trailing (back)slashes are eliminated. */
5406 while (c
> bs
&& (*c
== '/' || *c
== '\\'))
5414 stpncpy (buf
, path
, bs
- path
);
5415 stpcpy (buf
+ (bs
- path
), ".");