1 /* path.cc: path support.
3 Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
4 2006, 2007, 2008, 2009, 2010 Red Hat, Inc.
6 This file is part of Cygwin.
8 This software is a copyrighted work licensed under the terms of the
9 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
12 /* This module's job is to
13 - convert between POSIX and Win32 style filenames,
14 - support the `mount' functionality,
15 - support symlinks for files and directories
17 Pathnames are handled as follows:
19 - A \ or : in a path denotes a pure windows spec.
20 - Paths beginning with // (or \\) are not translated (i.e. looked
21 up in the mount table) and are assumed to be UNC path names.
23 The goal in the above set of rules is to allow both POSIX and Win32
24 flavors of pathnames without either interfering. The rules are
25 intended to be as close to a superset of both as possible.
27 Note that you can have more than one path to a file. The mount
28 table is always prefered when translating Win32 paths to POSIX
29 paths. Win32 paths in mount table entries may be UNC paths or
30 standard Win32 paths starting with <drive-letter>:
32 Text vs Binary issues are not considered here in path style
33 decisions, although the appropriate flags are retrieved and
34 stored in various structures.
36 Removing mounted filesystem support would simplify things greatly,
37 but having it gives us a mechanism of treating disk that lives on a
38 UNIX machine as having UNIX semantics [it allows one to edit a text
39 file on that disk and not have cr's magically appear and perhaps
40 break apps running on UNIX boxes]. It also useful to be able to
41 layout a hierarchy without changing the underlying directories.
43 The semantics of mounting file systems is not intended to precisely
44 follow normal UNIX systems.
46 Each DOS drive is defined to have a current directory. Supporting
47 this would complicate things so for now things are defined so that
52 #include "miscfuncs.h"
59 #include <sys/cygwin.h>
66 #include "shared_info.h"
75 bool dos_file_warning
= true;
77 suffix_info stat_suffixes
[] =
80 suffix_info (".exe", 1),
86 char contents
[SYMLINK_MAX
+ 1];
98 int check (char *path
, const suffix_info
*suffixes
, fs_info
&fs
,
99 path_conv_handle
&conv_hdl
);
100 int set (char *path
);
101 bool parse_device (const char *);
102 int check_sysfile (HANDLE h
);
103 int check_shortcut (HANDLE h
);
104 int check_reparse_point (HANDLE h
);
105 int check_nfs_symlink (HANDLE h
);
106 int posixify (char *srcbuf
);
107 bool set_error (int);
110 muto NO_COPY
cwdstuff::cwd_lock
;
112 static const GUID GUID_shortcut
113 = { 0x00021401L
, 0, 0, {0xc0, 0, 0, 0, 0, 0, 0, 0x46}};
116 WSH_FLAG_IDLIST
= 0x01, /* Contains an ITEMIDLIST. */
117 WSH_FLAG_FILE
= 0x02, /* Contains a file locator element. */
118 WSH_FLAG_DESC
= 0x04, /* Contains a description. */
119 WSH_FLAG_RELPATH
= 0x08, /* Contains a relative path. */
120 WSH_FLAG_WD
= 0x10, /* Contains a working dir. */
121 WSH_FLAG_CMDLINE
= 0x20, /* Contains command line args. */
122 WSH_FLAG_ICON
= 0x40 /* Contains a custom icon. */
125 struct win_shortcut_hdr
127 DWORD size
; /* Header size in bytes. Must contain 0x4c. */
128 GUID magic
; /* GUID of shortcut files. */
129 DWORD flags
; /* Content flags. See above. */
131 /* The next fields from attr to icon_no are always set to 0 in Cygwin
132 and U/Win shortcuts. */
133 DWORD attr
; /* Target file attributes. */
134 FILETIME ctime
; /* These filetime items are never touched by the */
135 FILETIME mtime
; /* system, apparently. Values don't matter. */
137 DWORD filesize
; /* Target filesize. */
138 DWORD icon_no
; /* Icon number. */
140 DWORD run
; /* Values defined in winuser.h. Use SW_NORMAL. */
141 DWORD hotkey
; /* Hotkey value. Set to 0. */
142 DWORD dummy
[2]; /* Future extension probably. Always 0. */
145 /* Return non-zero if PATH1 is a prefix of PATH2.
146 Both are assumed to be of the same path style and / vs \ usage.
148 LEN1 = strlen (PATH1). It's passed because often it's already known.
151 /foo/ is a prefix of /foo <-- may seem odd, but desired
152 /foo is a prefix of /foo/
153 / is a prefix of /foo/bar
154 / is not a prefix of foo/bar
155 foo/ is a prefix foo/bar
156 /foo is not a prefix of /foobar
160 path_prefix_p (const char *path1
, const char *path2
, int len1
,
161 bool caseinsensitive
)
163 /* Handle case where PATH1 has trailing '/' and when it doesn't. */
164 if (len1
> 0 && isdirsep (path1
[len1
- 1]))
168 return isdirsep (path2
[0]) && !isdirsep (path2
[1]);
170 if (isdirsep (path2
[len1
]) || path2
[len1
] == 0 || path1
[len1
- 1] == ':')
171 return caseinsensitive
? strncasematch (path1
, path2
, len1
)
172 : !strncmp (path1
, path2
, len1
);
177 /* Return non-zero if paths match in first len chars.
178 Check is dependent of the case sensitivity setting. */
180 pathnmatch (const char *path1
, const char *path2
, int len
, bool caseinsensitive
)
182 return caseinsensitive
183 ? strncasematch (path1
, path2
, len
) : !strncmp (path1
, path2
, len
);
186 /* Return non-zero if paths match. Check is dependent of the case
187 sensitivity setting. */
189 pathmatch (const char *path1
, const char *path2
, bool caseinsensitive
)
191 return caseinsensitive
192 ? strcasematch (path1
, path2
) : !strcmp (path1
, path2
);
195 /* TODO: This function is used in mkdir and rmdir to generate correct
196 error messages in case of paths ending in /. or /.. components.
197 Right now, normalize_posix_path will just normalize
198 those components away, which changes the semantics. */
200 has_dot_last_component (const char *dir
, bool test_dot_dot
)
202 /* SUSv3: . and .. are not allowed as last components in various system
203 calls. Don't test for backslash path separator since that's a Win32
204 path following Win32 rules. */
205 const char *last_comp
= strchr (dir
, '\0');
207 if (last_comp
== dir
)
208 return false; /* Empty string. Probably shouldn't happen here? */
210 /* Detect run of trailing slashes */
211 while (last_comp
> dir
&& *--last_comp
== '/')
214 /* Detect just a run of slashes or a path that does not end with a slash. */
215 if (*last_comp
!= '.')
218 /* We know we have a trailing dot here. Check that it really is a standalone "."
219 path component by checking that it is at the beginning of the string or is
221 if (last_comp
== dir
|| *--last_comp
== '/')
224 /* If we're not checking for '..' we're done. Ditto if we're now pointing to
226 if (!test_dot_dot
|| *last_comp
!= '.')
227 return false; /* either not testing for .. or this was not '..' */
229 /* Repeat previous test for standalone or path component. */
230 return last_comp
== dir
|| last_comp
[-1] == '/';
233 /* Normalize a POSIX path.
234 All duplicate /'s, except for 2 leading /'s, are deleted.
235 The result is 0 for success, or an errno error value. */
238 normalize_posix_path (const char *src
, char *dst
, char *&tail
)
240 const char *in_src
= src
;
241 char *dst_start
= dst
;
242 syscall_printf ("src %s", src
);
244 if ((isdrive (src
) && isdirsep (src
[2])) || *src
== '\\')
248 if (!isslash (src
[0]))
250 if (!cygheap
->cwd
.get (dst
))
252 tail
= strchr (tail
, '\0');
253 if (isslash (dst
[0]) && isslash (dst
[1]))
257 if (tail
== dst_start
+ 1 && *dst_start
== '/')
261 if (tail
> dst
&& !isslash (tail
[-1]))
264 /* Two leading /'s? If so, preserve them. */
265 else if (isslash (src
[1]) && !isslash (src
[2]))
269 /* Is that a //?/ or //./ prefix into the native NT namespace?
270 If so, preserve it. */
271 if ((src
[1] == '.' || src
[1] == '?') && isslash (src
[2]))
283 /* Strip runs of /'s. */
304 if (!isslash (src
[1]))
307 else if (src
[2] && !isslash (src
[2]))
311 while (tail
> dst_start
&& !isslash (*--tail
))
319 if ((tail
- dst
) >= NT_MAX_PATH
)
321 debug_printf ("ENAMETOOLONG = normalize_posix_path (%s)", src
);
329 debug_printf ("%s = normalize_posix_path (%s)", dst
, in_src
);
333 int err
= normalize_win32_path (in_src
, dst
, tail
);
335 for (char *p
= dst
; (p
= strchr (p
, '\\')); p
++)
341 path_conv::add_ext_from_sym (symlink_info
&sym
)
343 if (sym
.ext_here
&& *sym
.ext_here
)
345 known_suffix
= path
+ sym
.extn
;
346 if (sym
.ext_tacked_on
)
347 strcpy ((char *) known_suffix
, sym
.ext_here
);
351 static void __stdcall
mkrelpath (char *dst
, bool caseinsensitive
)
352 __attribute__ ((regparm (2)));
354 static void __stdcall
355 mkrelpath (char *path
, bool caseinsensitive
)
358 char *cwd_win32
= tp
.c_get ();
359 if (!cygheap
->cwd
.get (cwd_win32
, 0))
362 unsigned cwdlen
= strlen (cwd_win32
);
363 if (!path_prefix_p (cwd_win32
, path
, cwdlen
, caseinsensitive
))
366 size_t n
= strlen (path
);
374 tail
+= isdirsep (cwd_win32
[cwdlen
- 1]) ? cwdlen
: cwdlen
+ 1;
376 memmove (path
, tail
, strlen (tail
) + 1);
382 path_conv::set_normalized_path (const char *path_copy
)
386 size_t n
= strlen (path_copy
) + 1;
387 char *p
= (char *) crealloc_abort ((void *) normalized_path
, n
);
388 normalized_path
= (const char *) memcpy (p
, path_copy
, n
);
393 str2uni_cat (UNICODE_STRING
&tgt
, const char *srcstr
)
395 int len
= sys_mbstowcs (tgt
.Buffer
+ tgt
.Length
/ sizeof (WCHAR
),
396 (tgt
.MaximumLength
- tgt
.Length
) / sizeof (WCHAR
),
399 tgt
.Length
+= (len
- 1) * sizeof (WCHAR
);
403 get_nt_native_path (const char *path
, UNICODE_STRING
& upath
, bool dos
)
406 if (path
[0] == '/') /* special path w/o NT path representation. */
407 str2uni_cat (upath
, path
);
408 else if (path
[0] != '\\') /* X:\... or relative path. */
410 if (path
[1] == ':') /* X:\... */
412 RtlAppendUnicodeStringToString (&upath
, &ro_u_natp
);
413 str2uni_cat (upath
, path
);
414 /* The drive letter must be upper case. */
415 upath
.Buffer
[4] = towupper (upath
.Buffer
[4]);
418 str2uni_cat (upath
, path
);
419 transform_chars (&upath
, 7);
421 else if (path
[1] != '\\') /* \Device\... */
422 str2uni_cat (upath
, path
);
423 else if ((path
[2] != '.' && path
[2] != '?')
424 || path
[3] != '\\') /* \\server\share\... */
426 RtlAppendUnicodeStringToString (&upath
, &ro_u_uncp
);
427 str2uni_cat (upath
, path
+ 2);
428 transform_chars (&upath
, 8);
430 else /* \\.\device or \\?\foo */
432 RtlAppendUnicodeStringToString (&upath
, &ro_u_natp
);
433 str2uni_cat (upath
, path
+ 4);
437 /* Unfortunately we can't just use transform_chars with the tfx_rev_chars
438 table since only leading and trainlig spaces and dots are affected.
439 So we step to every backslash and fix surrounding dots and spaces.
440 That makes these broken filesystems a bit slower, but, hey. */
441 PWCHAR cp
= upath
.Buffer
+ 7;
442 PWCHAR cend
= upath
.Buffer
+ upath
.Length
/ sizeof (WCHAR
);
447 while (*ccp
== L
'.' || *ccp
== L
' ')
449 while (cp
[1] == L
' ')
452 while (*--cp
== L
'.' || *cp
== L
' ')
459 path_conv::get_nt_native_path ()
464 uni_path
.MaximumLength
= (strlen (path
) + 10) * sizeof (WCHAR
);
465 wide_path
= (PWCHAR
) cmalloc_abort (HEAP_STR
, uni_path
.MaximumLength
);
466 uni_path
.Buffer
= wide_path
;
467 ::get_nt_native_path (path
, uni_path
, has_dos_filenames_only ());
473 path_conv::get_wide_win32_path (PWCHAR wc
)
475 get_nt_native_path ();
478 wcpcpy (wc
, wide_path
);
485 warn_msdos (const char *src
)
487 if (user_shared
->warned_msdos
|| !dos_file_warning
|| !cygwin_finished_initializing
)
490 char *posix_path
= tp
.c_get ();
491 small_printf ("cygwin warning:\n");
492 if (cygwin_conv_path (CCP_WIN_A_TO_POSIX
| CCP_RELATIVE
, src
,
493 posix_path
, NT_MAX_PATH
))
494 small_printf (" MS-DOS style path detected: %ls\n POSIX equivalent preferred.\n",
497 small_printf (" MS-DOS style path detected: %ls\n Preferred POSIX equivalent is: %ls\n",
499 small_printf (" CYGWIN environment variable option \"nodosfilewarning\" turns off this warning.\n"
500 " Consult the user's guide for more details about POSIX paths:\n"
501 " http://cygwin.com/cygwin-ug-net/using.html#using-pathnames\n");
502 user_shared
->warned_msdos
= true;
506 getfileattr (const char *path
, bool caseinsensitive
) /* path has to be always absolute. */
509 UNICODE_STRING upath
;
510 OBJECT_ATTRIBUTES attr
;
511 FILE_BASIC_INFORMATION fbi
;
516 InitializeObjectAttributes (&attr
, &upath
,
517 caseinsensitive
? OBJ_CASE_INSENSITIVE
: 0,
519 get_nt_native_path (path
, upath
, false);
521 status
= NtQueryAttributesFile (&attr
, &fbi
);
522 if (NT_SUCCESS (status
))
523 return fbi
.FileAttributes
;
525 if (status
!= STATUS_OBJECT_NAME_NOT_FOUND
526 && status
!= STATUS_NO_SUCH_FILE
) /* File not found on 9x share */
528 /* File exists but access denied. Try to get attribute through
530 UNICODE_STRING dirname
, basename
;
532 FILE_BOTH_DIRECTORY_INFORMATION fdi
;
534 RtlSplitUnicodePath (&upath
, &dirname
, &basename
);
535 InitializeObjectAttributes (&attr
, &dirname
,
536 caseinsensitive
? OBJ_CASE_INSENSITIVE
: 0,
538 status
= NtOpenFile (&dir
, SYNCHRONIZE
| FILE_LIST_DIRECTORY
,
539 &attr
, &io
, FILE_SHARE_VALID_FLAGS
,
540 FILE_SYNCHRONOUS_IO_NONALERT
541 | FILE_OPEN_FOR_BACKUP_INTENT
542 | FILE_DIRECTORY_FILE
);
543 if (NT_SUCCESS (status
))
545 status
= NtQueryDirectoryFile (dir
, NULL
, NULL
, 0, &io
,
547 FileBothDirectoryInformation
,
548 TRUE
, &basename
, TRUE
);
550 if (NT_SUCCESS (status
) || status
== STATUS_BUFFER_OVERFLOW
)
551 return fdi
.FileAttributes
;
554 SetLastError (RtlNtStatusToDosError (status
));
555 return INVALID_FILE_ATTRIBUTES
;
558 /* Convert an arbitrary path SRC to a pure Win32 path, suitable for
559 passing to Win32 API routines.
561 If an error occurs, `error' is set to the errno value.
562 Otherwise it is set to 0.
565 SYMLINK_FOLLOW - convert to PATH symlink points to
566 SYMLINK_NOFOLLOW - convert to PATH of symlink itself
567 SYMLINK_IGNORE - do not check PATH for symlinks
568 SYMLINK_CONTENTS - just return symlink contents
571 /* TODO: This implementation is only preliminary. For internal
572 purposes it's necessary to have a path_conv::check function which
573 takes a UNICODE_STRING src path, otherwise we waste a lot of time
574 for converting back and forth. The below implementation does
575 realy nothing but converting to char *, until path_conv handles
576 wide-char paths directly. */
578 path_conv::check (const UNICODE_STRING
*src
, unsigned opt
,
579 const suffix_info
*suffixes
)
582 char *path
= tp
.c_get ();
584 user_shared
->warned_msdos
= true;
585 sys_wcstombs (path
, NT_MAX_PATH
, src
->Buffer
, src
->Length
/ sizeof (WCHAR
));
586 path_conv::check (path
, opt
, suffixes
);
590 path_conv::check (const char *src
, unsigned opt
,
591 const suffix_info
*suffixes
)
593 /* The tmp_buf array is used when expanding symlinks. It is NT_MAX_PATH * 2
594 in length so that we can hold the expanded symlink plus a trailer. */
596 char *path_copy
= tp
.c_get ();
597 char *pathbuf
= tp
.c_get ();
598 char *tmp_buf
= tp
.t_get ();
599 char *THIS_path
= tp
.c_get ();
601 bool need_directory
= 0;
602 bool saw_symlinks
= 0;
603 bool add_ext
= false;
605 char *tail
, *path_end
;
608 static path_conv last_path_conv
;
609 static char last_src
[CYG_MAX_PATH
];
611 if (*last_src
&& strcmp (last_src
, src
) == 0)
613 *this = last_path_conv
;
619 if (efault
.faulted ())
627 fileattr
= INVALID_FILE_ATTRIBUTES
;
628 caseinsensitive
= OBJ_CASE_INSENSITIVE
;
634 cfree (modifiable_path ());
637 close_conv_handle ();
638 memset (&dev
, 0, sizeof (dev
));
642 cfree ((void *) normalized_path
);
643 normalized_path
= NULL
;
645 int component
= 0; // Number of translated components
647 if (!(opt
& PC_NULLEMPTY
))
655 bool is_msdos
= false;
656 /* This loop handles symlink expansion. */
662 is_relpath
= !isabspath (src
);
663 error
= normalize_posix_path (src
, path_copy
, tail
);
673 /* Detect if the user was looking for a directory. We have to strip the
674 trailing slash initially while trying to add extensions but take it
675 into account during processing */
676 if (tail
> path_copy
+ 2 && isslash (tail
[-1]))
683 /* Scan path_copy from right to left looking either for a symlink
684 or an actual existing file. If an existing file is found, just
685 return. If a symlink is found, exit the for loop.
686 Also: be careful to preserve the errno returned from
687 symlink.check as the caller may need it. */
688 /* FIXME: Do we have to worry about multiple \'s here? */
689 component
= 0; // Number of translated components
690 sym
.contents
[0] = '\0';
694 for (unsigned pflags_or
= opt
& (PC_NO_ACCESS_CHECK
| PC_KEEP_HANDLE
);
698 const suffix_info
*suff
;
701 /* Don't allow symlink.check to set anything in the path_conv
702 class if we're working on an inner component of the path */
711 full_path
= THIS_path
;
714 /* Convert to native path spec sans symbolic link info. */
715 error
= mount_table
->conv_to_win32_path (path_copy
, full_path
, dev
,
721 sym
.pflags
|= pflags_or
;
723 if (dev
.major
== DEV_CYGDRIVE_MAJOR
)
726 fileattr
= FILE_ATTRIBUTE_DIRECTORY
| FILE_ATTRIBUTE_READONLY
;
729 fileattr
= getfileattr (THIS_path
,
730 sym
.pflags
& MOUNT_NOPOSIX
);
735 else if (dev
== FH_DEV
)
739 fileattr
= getfileattr (THIS_path
, sym
.pflags
& MOUNT_NOPOSIX
);
740 if (!component
&& fileattr
== INVALID_FILE_ATTRIBUTES
)
742 fileattr
= FILE_ATTRIBUTE_DIRECTORY
| FILE_ATTRIBUTE_READONLY
;
747 else if (isvirtual_dev (dev
.devn
))
749 /* FIXME: Calling build_fhandler here is not the right way to handle this. */
750 fhandler_virtual
*fh
= (fhandler_virtual
*) build_fh_dev (dev
, path_copy
);
751 virtual_ftype_t file_type
= fh
->exists ();
752 if (file_type
== virt_symlink
)
755 symlen
= sym
.set (fh
->get_filebuf ());
763 fileattr
= FILE_ATTRIBUTE_DIRECTORY
;
770 goto is_virtual_symlink
;
787 /* Access to real file or directory via block device
788 entry in /proc/sys. Convert to real file and go with
791 goto is_fs_via_procsys
;
793 /* Block special device. If the trailing slash has been
794 requested, the target is the root directory of the
795 filesystem on this block device. So we convert this to
796 a real file and attach the backslash. */
797 if (component
|| need_directory
)
802 strcat (full_path
, "\\");
803 fileattr
= FILE_ATTRIBUTE_DIRECTORY
804 | FILE_ATTRIBUTE_DEVICE
;
813 fileattr
= FILE_ATTRIBUTE_DEVICE
;
817 fileattr
= INVALID_FILE_ATTRIBUTES
;
818 goto virtual_component_retry
;
820 if (component
== 0 || dev
.devn
!= FH_NETDRIVE
)
821 path_flags
|= PATH_RO
;
824 /* devn should not be a device. If it is, then stop parsing now. */
825 else if (dev
.devn
!= FH_FS
)
828 path_flags
= sym
.pflags
;
834 goto out
; /* Found a device. Stop parsing. */
837 /* If path is only a drivename, Windows interprets it as the
838 current working directory on this drive instead of the root
839 dir which is what we want. So we need the trailing backslash
841 if (full_path
[0] && full_path
[1] == ':' && full_path
[2] == '\0')
846 /* Otherwise, if the user requires a directory and explicitely
847 specified a path into the native NT namespace, add the trailing
848 backslash. It's needed to access the root dir. */
849 else if (need_directory
850 && full_path
[0] == '\\' && full_path
[1] == '\\'
851 && (full_path
[2] == '.' || full_path
[2] == '?'))
852 strcat (full_path
, "\\");
854 /* If the incoming path was given in DOS notation, always treat
855 it as caseinsensitive,noacl path. This must be set before
856 calling sym.check, otherwise the path is potentially treated
859 sym
.pflags
|= PATH_NOPOSIX
| PATH_NOACL
;
863 symlen
= sym
.check (full_path
, suff
, fs
, conv_handle
);
874 dev
.parse (sym
.major
, sym
.minor
);
877 fileattr
= sym
.fileattr
;
881 if (sym
.pflags
& PATH_SOCKET
)
888 fileattr
= sym
.fileattr
;
896 fileattr
= sym
.fileattr
;
897 path_flags
= sym
.pflags
;
900 /* If symlink.check found an existing non-symlink file, then
901 it sets the appropriate flag. It also sets any suffix found
903 if (!sym
.issymlink
&& sym
.fileattr
!= INVALID_FILE_ATTRIBUTES
)
908 else if (!(sym
.fileattr
& FILE_ATTRIBUTE_DIRECTORY
))
913 goto out
; // file found
915 /* Found a symlink if symlen > 0. If component == 0, then the
916 src path itself was a symlink. If !follow_mode then
917 we're done. Otherwise we have to insert the path found
918 into the full path that we are building and perform all of
919 these operations again on the newly derived path. */
923 if (component
== 0 && !need_directory
&& !(opt
& PC_SYM_FOLLOW
))
925 set_symlink (symlen
); // last component of path is a symlink.
926 if (opt
& PC_SYM_CONTENTS
)
928 strcpy (THIS_path
, sym
.contents
);
934 /* Following a symlink we can't trust the collected filesystem
935 information any longer. */
937 /* Close handle, if we have any. Otherwise we're collecting
938 handles while following symlinks. */
939 conv_handle
.close ();
942 else if (sym
.error
&& sym
.error
!= ENOENT
)
947 /* No existing file found. */
949 virtual_component_retry
:
950 /* Find the new "tail" of the path, e.g. in '/for/bar/baz',
952 if (tail
!= path_end
)
954 while (--tail
> path_copy
+ 1 && *tail
!= '/') {}
955 /* Exit loop if there is no tail or we are at the
956 beginning of a UNC path */
957 if (tail
<= path_copy
+ 1)
958 goto out
; // all done
960 /* Haven't found an existing pathname component yet.
961 Pinch off the tail and try again. */
966 /* Arrive here if above loop detected a symlink. */
967 if (++loop
> SYMLOOP_MAX
)
969 error
= ELOOP
; // Eep.
976 /* Place the link content, possibly with head and/or tail, in tmp_buf */
979 if (isabspath (sym
.contents
))
980 headptr
= tmp_buf
; /* absolute path */
983 /* Copy the first part of the path (with ending /) and point to the end. */
984 char *prevtail
= tail
;
985 while (--prevtail
> path_copy
&& *prevtail
!= '/') {}
986 int headlen
= prevtail
- path_copy
+ 1;;
987 memcpy (tmp_buf
, path_copy
, headlen
);
988 headptr
= &tmp_buf
[headlen
];
991 /* Make sure there is enough space */
992 if (headptr
+ symlen
>= tmp_buf
+ (2 * NT_MAX_PATH
))
995 error
= ENAMETOOLONG
;
996 set_path ("::ENAMETOOLONG::");
1000 /* Copy the symlink contents to the end of tmp_buf.
1002 for (char *p
= sym
.contents
; *p
; p
++)
1003 *headptr
++ = *p
== '\\' ? '/' : *p
;
1006 /* Copy any tail component (with the 0) */
1007 if (tail
++ < path_end
)
1009 /* Add a slash if needed. There is space. */
1010 if (*(headptr
- 1) != '/')
1012 int taillen
= path_end
- tail
+ 1;
1013 if (headptr
+ taillen
> tmp_buf
+ (2 * NT_MAX_PATH
))
1015 memcpy (headptr
, tail
, taillen
);
1018 /* Evaluate everything all over again. */
1022 if (!(opt
& PC_SYM_CONTENTS
))
1026 set_path (THIS_path
);
1028 add_ext_from_sym (sym
);
1029 if (dev
.devn
== FH_NETDRIVE
&& component
)
1031 /* This case indicates a non-existant resp. a non-retrievable
1032 share. This happens for instance if the share is a printer.
1033 In this case the path must not be treated like a FH_NETDRIVE,
1034 but like a FH_FS instead, so the usual open call for files
1038 else if (isvirtual_dev (dev
.devn
) && fileattr
== INVALID_FILE_ATTRIBUTES
)
1043 else if (!need_directory
|| error
)
1044 /* nothing to do */;
1045 else if (fileattr
== INVALID_FILE_ATTRIBUTES
)
1046 strcat (modifiable_path (), "\\"); /* Reattach trailing dirsep in native path. */
1047 else if (fileattr
& FILE_ATTRIBUTE_DIRECTORY
)
1048 path_flags
&= ~PATH_SYMLINK
;
1051 debug_printf ("%s is a non-directory", path
);
1058 if (strncmp (path
, "\\\\.\\", 4))
1060 if (!tail
|| tail
== path
)
1062 else if (tail
[-1] != '\\')
1071 /* If FS hasn't been checked already in symlink_info::check, do so now. */
1072 if (fs
.inited ()|| fs
.update (get_nt_native_path (), NULL
))
1074 /* Incoming DOS paths are treated like DOS paths in native
1075 Windows applications. No ACLs, just default settings. */
1077 fs
.has_acls (false);
1078 debug_printf ("this->path(%s), has_acls(%d)", path
, fs
.has_acls ());
1079 /* CV: We could use this->has_acls() but I want to make sure that
1080 we don't forget that the PATH_NOACL flag must be taken into
1082 if (!(path_flags
& PATH_NOACL
) && fs
.has_acls ())
1083 set_exec (0); /* We really don't know if this is executable or not here
1084 but set it to not executable since it will be figured out
1085 later by anything which cares about this. */
1087 /* If the FS has been found to have unrelibale inodes, note
1088 that in path_flags. */
1089 if (!fs
.hasgood_inode ())
1090 path_flags
|= PATH_IHASH
;
1091 /* If the OS is caseinsensitive or the FS is caseinsensitive,
1092 don't handle path casesensitive. */
1093 if (cygwin_shared
->obcaseinsensitive
|| fs
.caseinsensitive ())
1094 path_flags
|= PATH_NOPOSIX
;
1095 caseinsensitive
= (path_flags
& PATH_NOPOSIX
)
1096 ? OBJ_CASE_INSENSITIVE
: 0;
1097 if (exec_state () != dont_know_if_executable
)
1101 else if (issymlink () || issocket ())
1105 if (opt
& PC_NOFULL
)
1109 mkrelpath (this->modifiable_path (), !!caseinsensitive
);
1110 /* Invalidate wide_path so that wide relpath can be created
1111 in later calls to get_nt_native_path or get_wide_win32_path. */
1118 size_t n
= strlen (this->path
);
1119 /* Do not add trailing \ to UNC device names like \\.\a: */
1120 if (this->path
[n
- 1] != '\\' &&
1121 (strncmp (this->path
, "\\\\.\\", 4) != 0))
1123 this->modifiable_path ()[n
] = '\\';
1124 this->modifiable_path ()[n
+ 1] = '\0';
1130 set_has_symlinks ();
1132 if ((opt
& PC_POSIX
))
1134 if (tail
< path_end
&& tail
> path_copy
+ 1)
1136 set_normalized_path (path_copy
);
1137 if (is_msdos
&& !(opt
& PC_NOWARN
))
1144 last_path_conv
= *this;
1145 strcpy (last_src
, src
);
1150 path_conv::~path_conv ()
1152 if (normalized_path
)
1154 cfree ((void *) normalized_path
);
1155 normalized_path
= NULL
;
1159 cfree (modifiable_path ());
1167 close_conv_handle ();
1171 path_conv::is_binary ()
1174 PWCHAR bintest
= tp
.w_get ();
1177 return GetBinaryTypeW (get_wide_win32_path (bintest
), &bin
)
1178 && (bin
== SCS_32BIT_BINARY
|| bin
== SCS_64BIT_BINARY
);
1181 /* Normalize a Win32 path.
1182 /'s are converted to \'s in the process.
1183 All duplicate \'s, except for 2 leading \'s, are deleted.
1185 The result is 0 for success, or an errno error value.
1186 FIXME: A lot of this should be mergeable with the POSIX critter. */
1188 normalize_win32_path (const char *src
, char *dst
, char *&tail
)
1190 const char *src_start
= src
;
1191 bool beg_src_slash
= isdirsep (src
[0]);
1194 /* Skip long path name prefixes in Win32 or NT syntax. */
1195 if (beg_src_slash
&& (src
[1] == '?' || isdirsep (src
[1]))
1196 && src
[2] == '?' && isdirsep (src
[3]))
1199 if (src
[1] != ':') /* native UNC path */
1200 src
+= 2; /* Fortunately the first char is not copied... */
1202 beg_src_slash
= false;
1204 if (beg_src_slash
&& isdirsep (src
[1]))
1206 if (isdirsep (src
[2]))
1208 /* More than two slashes are just folded into one. */
1210 while (isdirsep (src
[1]))
1215 /* Two slashes start a network or device path. */
1218 if (src
[1] == '.' && isdirsep (src
[2]))
1229 /* Always convert drive letter to uppercase for case sensitivity. */
1230 *tail
++ = cyg_toupper (*src
++);
1231 else if (*src
!= '/')
1234 tail
+= cygheap
->cwd
.get_drive (dst
);
1235 else if (!cygheap
->cwd
.get (dst
, 0))
1236 return get_errno ();
1239 tail
= strchr (tail
, '\0');
1240 if (tail
[-1] != '\\')
1248 /* Strip duplicate /'s. */
1249 if (isdirsep (src
[0]) && isdirsep (src
[1]))
1252 else if (src
[0] == '.' && isdirsep (src
[1])
1253 && (src
== src_start
|| isdirsep (src
[-1])))
1256 /* Backup if "..". */
1257 else if (src
[0] == '.' && src
[1] == '.'
1258 /* dst must be greater than dst_start */
1259 && tail
[-1] == '\\')
1261 if (!isdirsep (src
[2]) && src
[2] != '\0')
1265 /* Back up over /, but not if it's the first one. */
1268 /* Now back up to the next /. */
1269 while (tail
> dst
+ 1 && tail
[-1] != '\\' && tail
[-2] != ':')
1272 if (isdirsep (*src
))
1276 /* Otherwise, add char to result. */
1285 if ((tail
- dst
) >= NT_MAX_PATH
)
1286 return ENAMETOOLONG
;
1288 if (tail
> dst
+ 1 && tail
[-1] == '.' && tail
[-2] == '\\')
1291 debug_printf ("%s = normalize_win32_path (%s)", dst
, src_start
);
1295 /* Various utilities. */
1297 /* nofinalslash: Remove trailing / and \ from SRC (except for the
1298 first one). It is ok for src == dst. */
1301 nofinalslash (const char *src
, char *dst
)
1303 int len
= strlen (src
);
1305 memcpy (dst
, src
, len
+ 1);
1306 while (len
> 1 && isdirsep (dst
[--len
]))
1310 /* conv_path_list: Convert a list of path names to/from Win32/POSIX. */
1313 conv_path_list (const char *src
, char *dst
, size_t size
, int to_posix
)
1316 char src_delim
, dst_delim
;
1317 cygwin_conv_path_t conv_fn
;
1324 conv_fn
= CCP_WIN_A_TO_POSIX
| CCP_RELATIVE
;
1330 conv_fn
= CCP_POSIX_TO_WIN_A
| CCP_RELATIVE
;
1334 len
= strlen (src
) + 1;
1335 if (len
<= NT_MAX_PATH
* sizeof (WCHAR
))
1336 srcbuf
= (char *) tp
.w_get ();
1338 srcbuf
= (char *) alloca (len
);
1342 bool saw_empty
= false;
1345 char *s
= strccpy (srcbuf
, &src
, src_delim
);
1346 size_t len
= s
- srcbuf
;
1347 if (len
>= NT_MAX_PATH
)
1355 err
= cygwin_conv_path (conv_fn
, srcbuf
, d
, size
- (d
- dst
));
1360 err
= cygwin_conv_path (conv_fn
, ".", d
, size
- (d
- dst
));
1364 if (to_posix
== ENV_CVT
)
1370 d
= strchr (d
, '\0');
1384 /********************** Symbolic Link Support **************************/
1386 /* Create a symlink from FROMPATH to TOPATH. */
1388 /* If TRUE create symlinks as Windows shortcuts, if false create symlinks
1389 as normal files with magic number and system bit set. */
1390 bool allow_winsymlinks
= false;
1393 symlink (const char *oldpath
, const char *newpath
)
1395 return symlink_worker (oldpath
, newpath
, allow_winsymlinks
, false);
1399 symlink_worker (const char *oldpath
, const char *newpath
, bool use_winsym
,
1404 path_conv win32_newpath
, win32_oldpath
;
1406 SECURITY_ATTRIBUTES sa
= sec_none_nih
;
1407 OBJECT_ATTRIBUTES attr
;
1413 bool mk_winsym
= use_winsym
;
1414 bool has_trailing_dirsep
= false;
1416 /* POSIX says that empty 'newpath' is invalid input while empty
1417 'oldpath' is valid -- it's symlink resolver job to verify if
1418 symlink contents point to existing filesystem object */
1420 if (efault
.faulted (EFAULT
))
1422 if (!*oldpath
|| !*newpath
)
1428 if (strlen (oldpath
) > SYMLINK_MAX
)
1430 set_errno (ENAMETOOLONG
);
1434 /* Trailing dirsep is a no-no. */
1435 len
= strlen (newpath
);
1436 has_trailing_dirsep
= isdirsep (newpath
[len
- 1]);
1437 if (has_trailing_dirsep
)
1439 newpath
= strdup (newpath
);
1440 ((char *) newpath
)[len
- 1] = '\0';
1443 check_opt
= PC_SYM_NOFOLLOW
| PC_POSIX
| (isdevice
? PC_NOWARN
: 0);
1444 /* We need the normalized full path below. */
1445 win32_newpath
.check (newpath
, check_opt
, stat_suffixes
);
1446 /* MVFS doesn't handle the SYSTEM DOS attribute, but it handles the R/O
1447 attribute. Therefore we create symlinks on MVFS always as shortcuts. */
1448 mk_winsym
|= win32_newpath
.fs_is_mvfs ();
1450 if (mk_winsym
&& !win32_newpath
.exists ()
1451 && (isdevice
|| !win32_newpath
.fs_is_nfs ()))
1453 char *newplnk
= tp
.c_get ();
1454 stpcpy (stpcpy (newplnk
, newpath
), ".lnk");
1455 win32_newpath
.check (newplnk
, check_opt
);
1458 if (win32_newpath
.error
)
1460 set_errno (win32_newpath
.error
);
1464 syscall_printf ("symlink (%s, %S)", oldpath
,
1465 win32_newpath
.get_nt_native_path ());
1467 if ((!isdevice
&& win32_newpath
.exists ())
1468 || win32_newpath
.is_auto_device ())
1473 if (has_trailing_dirsep
&& !win32_newpath
.exists ())
1479 if (!isdevice
&& win32_newpath
.fs_is_nfs ())
1481 /* On NFS, create symlinks by calling NtCreateFile with an EA of type
1482 NfsSymlinkTargetName containing ... the symlink target name. */
1483 PFILE_FULL_EA_INFORMATION pffei
= (PFILE_FULL_EA_INFORMATION
) tp
.w_get ();
1484 pffei
->NextEntryOffset
= 0;
1486 pffei
->EaNameLength
= sizeof (NFS_SYML_TARGET
) - 1;
1487 char *EaValue
= stpcpy (pffei
->EaName
, NFS_SYML_TARGET
) + 1;
1488 pffei
->EaValueLength
= sizeof (WCHAR
) *
1489 (sys_mbstowcs ((PWCHAR
) EaValue
, NT_MAX_PATH
, oldpath
) - 1);
1490 status
= NtCreateFile (&fh
, FILE_WRITE_DATA
| FILE_WRITE_EA
| SYNCHRONIZE
,
1491 win32_newpath
.get_object_attr (attr
, sa
),
1492 &io
, NULL
, FILE_ATTRIBUTE_SYSTEM
,
1493 FILE_SHARE_VALID_FLAGS
, FILE_CREATE
,
1494 FILE_SYNCHRONOUS_IO_NONALERT
1495 | FILE_OPEN_FOR_BACKUP_INTENT
,
1496 pffei
, NT_MAX_PATH
* sizeof (WCHAR
));
1497 if (!NT_SUCCESS (status
))
1499 __seterrno_from_nt_status (status
);
1509 ITEMIDLIST
*pidl
= NULL
;
1510 size_t full_len
= 0;
1511 unsigned short oldpath_len
, desc_len
, relpath_len
, pidl_len
= 0;
1512 char desc
[MAX_PATH
+ 1], *relpath
;
1516 /* First create an IDLIST to learn how big our shortcut is
1520 /* The symlink target is relative to the directory in which
1521 the symlink gets created, not relative to the cwd. Therefore
1522 we have to mangle the path quite a bit before calling path_conv. */
1523 if (isabspath (oldpath
))
1524 win32_oldpath
.check (oldpath
,
1529 len
= strrchr (win32_newpath
.normalized_path
, '/')
1530 - win32_newpath
.normalized_path
+ 1;
1531 char *absoldpath
= tp
.t_get ();
1532 stpcpy (stpncpy (absoldpath
, win32_newpath
.normalized_path
, len
),
1534 win32_oldpath
.check (absoldpath
, PC_SYM_NOFOLLOW
, stat_suffixes
);
1536 if (SUCCEEDED (SHGetDesktopFolder (&psl
)))
1538 WCHAR wc_path
[win32_oldpath
.get_wide_win32_path_len () + 1];
1539 win32_oldpath
.get_wide_win32_path (wc_path
);
1540 /* Amazing but true: Even though the ParseDisplayName method
1541 takes a wide char path name, it does not understand the
1542 Win32 prefix for long pathnames! So we have to tack off
1543 the prefix and convert the path to the "normal" syntax
1544 for ParseDisplayName. */
1545 WCHAR
*wc
= wc_path
+ 4;
1546 if (wc
[1] != L
':') /* native UNC path */
1549 if (SUCCEEDED (res
= psl
->ParseDisplayName (NULL
, NULL
, wc
, NULL
,
1554 for (p
= pidl
; p
->mkid
.cb
> 0;
1555 p
= (ITEMIDLIST
*)((char *) p
+ p
->mkid
.cb
))
1557 pidl_len
= (char *) p
- (char *) pidl
+ 2;
1562 /* Compute size of shortcut file. */
1563 full_len
= sizeof (win_shortcut_hdr
);
1565 full_len
+= sizeof (unsigned short) + pidl_len
;
1566 oldpath_len
= strlen (oldpath
);
1567 /* Unfortunately the length of the description is restricted to a
1568 length of MAX_PATH up to NT4, and to a length of 2000 bytes
1569 since W2K. We don't want to add considerations for the different
1570 lengths and even 2000 bytes is not enough for long path names.
1571 So what we do here is to set the description to the POSIX path
1572 only if the path is not longer than MAX_PATH characters. We
1573 append the full path name after the regular shortcut data
1574 (see below), which works fine with Windows Explorer as well
1575 as older Cygwin versions (as long as the whole file isn't bigger
1576 than 8K). The description field is only used for backward
1577 compatibility to older Cygwin versions and those versions are
1578 not capable of handling long path names anyway. */
1579 desc_len
= stpcpy (desc
, oldpath_len
> MAX_PATH
1580 ? "[path too long]" : oldpath
) - desc
;
1581 full_len
+= sizeof (unsigned short) + desc_len
;
1582 /* Devices get the oldpath string unchanged as relative path. */
1585 relpath_len
= oldpath_len
;
1586 stpcpy (relpath
= tp
.c_get (), oldpath
);
1590 relpath_len
= strlen (win32_oldpath
.get_win32 ());
1591 stpcpy (relpath
= tp
.c_get (), win32_oldpath
.get_win32 ());
1593 full_len
+= sizeof (unsigned short) + relpath_len
;
1594 full_len
+= sizeof (unsigned short) + oldpath_len
;
1595 /* 1 byte more for trailing 0 written by stpcpy. */
1596 if (full_len
< NT_MAX_PATH
* sizeof (WCHAR
))
1597 buf
= (char *) tp
.w_get ();
1599 buf
= (char *) alloca (full_len
+ 1);
1601 /* Create shortcut header */
1602 win_shortcut_hdr
*shortcut_header
= (win_shortcut_hdr
*) buf
;
1603 memset (shortcut_header
, 0, sizeof *shortcut_header
);
1604 shortcut_header
->size
= sizeof *shortcut_header
;
1605 shortcut_header
->magic
= GUID_shortcut
;
1606 shortcut_header
->flags
= (WSH_FLAG_DESC
| WSH_FLAG_RELPATH
);
1608 shortcut_header
->flags
|= WSH_FLAG_IDLIST
;
1609 shortcut_header
->run
= SW_NORMAL
;
1610 cp
= buf
+ sizeof (win_shortcut_hdr
);
1615 *(unsigned short *)cp
= pidl_len
;
1616 memcpy (cp
+= 2, pidl
, pidl_len
);
1618 CoTaskMemFree (pidl
);
1621 /* Create description */
1622 *(unsigned short *)cp
= desc_len
;
1623 cp
= stpcpy (cp
+= 2, desc
);
1625 /* Create relpath */
1626 *(unsigned short *)cp
= relpath_len
;
1627 cp
= stpcpy (cp
+= 2, relpath
);
1629 /* Append the POSIX path after the regular shortcut data for
1630 the long path support. */
1631 unsigned short *plen
= (unsigned short *) cp
;
1633 *(PWCHAR
) cp
= 0xfeff; /* BOM */
1635 *plen
= sys_mbstowcs ((PWCHAR
) cp
, NT_MAX_PATH
, oldpath
) * sizeof (WCHAR
);
1640 /* Default technique creating a symlink. */
1641 buf
= (char *) tp
.w_get ();
1642 cp
= stpcpy (buf
, SYMLINK_COOKIE
);
1643 *(PWCHAR
) cp
= 0xfeff; /* BOM */
1645 /* Note that the terminating nul is written. */
1646 cp
+= sys_mbstowcs ((PWCHAR
) cp
, NT_MAX_PATH
, oldpath
) * sizeof (WCHAR
);
1649 if (isdevice
&& win32_newpath
.exists ())
1651 status
= NtOpenFile (&fh
, FILE_WRITE_ATTRIBUTES
,
1652 win32_newpath
.get_object_attr (attr
, sa
),
1653 &io
, 0, FILE_OPEN_FOR_BACKUP_INTENT
);
1654 if (!NT_SUCCESS (status
))
1656 __seterrno_from_nt_status (status
);
1659 status
= NtSetAttributesFile (fh
, FILE_ATTRIBUTE_NORMAL
);
1661 if (!NT_SUCCESS (status
))
1663 __seterrno_from_nt_status (status
);
1667 status
= NtCreateFile (&fh
, DELETE
| FILE_GENERIC_WRITE
,
1668 win32_newpath
.get_object_attr (attr
, sa
),
1669 &io
, NULL
, FILE_ATTRIBUTE_NORMAL
,
1670 FILE_SHARE_VALID_FLAGS
,
1671 isdevice
? FILE_OVERWRITE_IF
: FILE_CREATE
,
1672 FILE_SYNCHRONOUS_IO_NONALERT
1673 | FILE_NON_DIRECTORY_FILE
1674 | FILE_OPEN_FOR_BACKUP_INTENT
,
1676 if (!NT_SUCCESS (status
))
1678 __seterrno_from_nt_status (status
);
1681 if (win32_newpath
.has_acls ())
1682 set_file_attribute (fh
, win32_newpath
, ILLEGAL_UID
, ILLEGAL_GID
,
1683 (io
.Information
== FILE_CREATED
? S_JUSTCREATED
: 0)
1684 | S_IFLNK
| STD_RBITS
| STD_WBITS
);
1685 status
= NtWriteFile (fh
, NULL
, NULL
, NULL
, &io
, buf
, cp
- buf
, NULL
, NULL
);
1686 if (NT_SUCCESS (status
) && io
.Information
== (ULONG
) (cp
- buf
))
1688 status
= NtSetAttributesFile (fh
, mk_winsym
? FILE_ATTRIBUTE_READONLY
1689 : FILE_ATTRIBUTE_SYSTEM
);
1690 if (!NT_SUCCESS (status
))
1691 debug_printf ("Setting attributes failed, status = %p", status
);
1696 __seterrno_from_nt_status (status
);
1697 FILE_DISPOSITION_INFORMATION fdi
= { TRUE
};
1698 status
= NtSetInformationFile (fh
, &io
, &fdi
, sizeof fdi
,
1699 FileDispositionInformation
);
1700 if (!NT_SUCCESS (status
))
1701 debug_printf ("Setting delete dispostion failed, status = %p", status
);
1706 syscall_printf ("%d = symlink_worker (%s, %s, %d, %d)", res
, oldpath
,
1707 newpath
, mk_winsym
, isdevice
);
1708 if (has_trailing_dirsep
)
1709 free ((void *) newpath
);
1714 cmp_shortcut_header (win_shortcut_hdr
*file_header
)
1716 /* A Cygwin or U/Win shortcut only contains a description and a relpath.
1717 Cygwin shortcuts also might contain an ITEMIDLIST. The run type is
1718 always set to SW_NORMAL. */
1719 return file_header
->size
== sizeof (win_shortcut_hdr
)
1720 && !memcmp (&file_header
->magic
, &GUID_shortcut
, sizeof GUID_shortcut
)
1721 && (file_header
->flags
& ~WSH_FLAG_IDLIST
)
1722 == (WSH_FLAG_DESC
| WSH_FLAG_RELPATH
)
1723 && file_header
->run
== SW_NORMAL
;
1727 symlink_info::check_shortcut (HANDLE h
)
1730 win_shortcut_hdr
*file_header
;
1736 FILE_STANDARD_INFORMATION fsi
;
1737 LARGE_INTEGER off
= { QuadPart
:0LL };
1739 status
= NtQueryInformationFile (h
, &io
, &fsi
, sizeof fsi
,
1740 FileStandardInformation
);
1741 if (!NT_SUCCESS (status
))
1746 if (fsi
.EndOfFile
.QuadPart
<= sizeof (win_shortcut_hdr
)
1747 || fsi
.EndOfFile
.QuadPart
> 4 * 65536)
1749 if (fsi
.EndOfFile
.LowPart
< NT_MAX_PATH
* sizeof (WCHAR
))
1750 buf
= (char *) tp
.w_get ();
1752 buf
= (char *) alloca (fsi
.EndOfFile
.LowPart
+ 1);
1753 status
= NtReadFile (h
, NULL
, NULL
, NULL
, &io
, buf
, fsi
.EndOfFile
.LowPart
,
1755 status
= wait_pending (status
, h
, io
);
1756 if (!NT_SUCCESS (status
))
1758 if (status
!= STATUS_END_OF_FILE
)
1762 file_header
= (win_shortcut_hdr
*) buf
;
1763 if (io
.Information
!= fsi
.EndOfFile
.LowPart
1764 || !cmp_shortcut_header (file_header
))
1766 cp
= buf
+ sizeof (win_shortcut_hdr
);
1767 if (file_header
->flags
& WSH_FLAG_IDLIST
) /* Skip ITEMIDLIST */
1768 cp
+= *(unsigned short *) cp
+ 2;
1769 if (!(len
= *(unsigned short *) cp
))
1772 /* Check if this is a device file - these start with the sequence :\\ */
1773 if (strncmp (cp
, ":\\", 2) == 0)
1774 res
= strlen (strcpy (contents
, cp
)); /* Don't mess with device files */
1777 /* Has appended full path? If so, use it instead of description. */
1778 unsigned short relpath_len
= *(unsigned short *) (cp
+ len
);
1779 if (cp
+ len
+ 2 + relpath_len
< buf
+ fsi
.EndOfFile
.LowPart
)
1781 cp
+= len
+ 2 + relpath_len
;
1782 len
= *(unsigned short *) cp
;
1785 if (*(PWCHAR
) cp
== 0xfeff) /* BOM */
1787 char *tmpbuf
= tp
.c_get ();
1788 if (sys_wcstombs (tmpbuf
, NT_MAX_PATH
, (PWCHAR
) (cp
+ 2))
1791 res
= posixify (tmpbuf
);
1793 else if (len
> SYMLINK_MAX
)
1798 res
= posixify (cp
);
1801 if (res
) /* It's a symlink. */
1802 pflags
|= PATH_SYMLINK
| PATH_LNK
;
1807 symlink_info::check_sysfile (HANDLE h
)
1810 char cookie_buf
[sizeof (SYMLINK_COOKIE
) - 1];
1811 char *srcbuf
= tp
.c_get ();
1815 bool interix_symlink
= false;
1816 LARGE_INTEGER off
= { QuadPart
:0LL };
1818 status
= NtReadFile (h
, NULL
, NULL
, NULL
, &io
, cookie_buf
,
1819 sizeof (cookie_buf
), &off
, NULL
);
1820 status
= wait_pending (status
, h
, io
);
1821 if (!NT_SUCCESS (status
))
1823 debug_printf ("ReadFile1 failed %p", status
);
1824 if (status
!= STATUS_END_OF_FILE
)
1828 off
.QuadPart
= io
.Information
;
1829 if (io
.Information
== sizeof (cookie_buf
)
1830 && memcmp (cookie_buf
, SYMLINK_COOKIE
, sizeof (cookie_buf
)) == 0)
1832 /* It's a symlink. */
1833 pflags
|= PATH_SYMLINK
;
1835 else if (io
.Information
== sizeof (cookie_buf
)
1836 && memcmp (cookie_buf
, SOCKET_COOKIE
, sizeof (cookie_buf
)) == 0)
1837 pflags
|= PATH_SOCKET
;
1838 else if (io
.Information
>= sizeof (INTERIX_SYMLINK_COOKIE
)
1839 && memcmp (cookie_buf
, INTERIX_SYMLINK_COOKIE
,
1840 sizeof (INTERIX_SYMLINK_COOKIE
) - 1) == 0)
1842 /* It's an Interix symlink. */
1843 pflags
|= PATH_SYMLINK
;
1844 interix_symlink
= true;
1845 /* Interix symlink cookies are shorter than Cygwin symlink cookies, so
1846 in case of an Interix symlink cooky we have read too far into the
1847 file. Set file pointer back to the position right after the cookie. */
1848 off
.QuadPart
= sizeof (INTERIX_SYMLINK_COOKIE
) - 1;
1850 if (pflags
& PATH_SYMLINK
)
1852 status
= NtReadFile (h
, NULL
, NULL
, NULL
, &io
, srcbuf
,
1853 NT_MAX_PATH
, &off
, NULL
);
1854 status
= wait_pending (status
, h
, io
);
1855 if (!NT_SUCCESS (status
))
1857 debug_printf ("ReadFile2 failed");
1858 if (status
!= STATUS_END_OF_FILE
)
1861 else if (*(PWCHAR
) srcbuf
== 0xfeff /* BOM */
1864 /* Add trailing 0 to Interix symlink target. Skip BOM in Cygwin
1866 if (interix_symlink
)
1867 ((PWCHAR
) srcbuf
)[io
.Information
/ sizeof (WCHAR
)] = L
'\0';
1870 char *tmpbuf
= tp
.c_get ();
1871 if (sys_wcstombs (tmpbuf
, NT_MAX_PATH
, (PWCHAR
) srcbuf
)
1873 debug_printf ("symlink string too long");
1875 res
= posixify (tmpbuf
);
1877 else if (io
.Information
> SYMLINK_MAX
+ 1)
1878 debug_printf ("symlink string too long");
1880 res
= posixify (srcbuf
);
1886 symlink_info::check_reparse_point (HANDLE h
)
1891 PREPARSE_DATA_BUFFER rp
= (PREPARSE_DATA_BUFFER
) tp
.c_get ();
1892 UNICODE_STRING subst
;
1893 char srcbuf
[SYMLINK_MAX
+ 7];
1895 status
= NtFsControlFile (h
, NULL
, NULL
, NULL
, &io
, FSCTL_GET_REPARSE_POINT
,
1896 NULL
, 0, (LPVOID
) rp
,
1897 MAXIMUM_REPARSE_DATA_BUFFER_SIZE
);
1898 if (!NT_SUCCESS (status
))
1900 debug_printf ("NtFsControlFile(FSCTL_GET_REPARSE_POINT) failed, %p",
1905 if (rp
->ReparseTag
== IO_REPARSE_TAG_SYMLINK
)
1906 RtlInitCountedUnicodeString (&subst
,
1907 (WCHAR
*)((char *)rp
->SymbolicLinkReparseBuffer
.PathBuffer
1908 + rp
->SymbolicLinkReparseBuffer
.SubstituteNameOffset
),
1909 rp
->SymbolicLinkReparseBuffer
.SubstituteNameLength
);
1910 else if (rp
->ReparseTag
== IO_REPARSE_TAG_MOUNT_POINT
)
1912 RtlInitCountedUnicodeString (&subst
,
1913 (WCHAR
*)((char *)rp
->MountPointReparseBuffer
.PathBuffer
1914 + rp
->MountPointReparseBuffer
.SubstituteNameOffset
),
1915 rp
->MountPointReparseBuffer
.SubstituteNameLength
);
1916 if (RtlEqualUnicodePathPrefix (&subst
, &ro_u_volume
, TRUE
))
1918 /* Volume mount point. Not treated as symlink. The return
1919 value of -1 is a hint for the caller to treat this as a
1920 volume mount point. */
1926 /* Maybe it's a reparse point, but it's certainly not one we
1927 recognize. Drop the REPARSE file attribute so we don't even
1928 try to use the flag for some special handling. It's just some
1929 arbitrary file or directory for us. */
1930 fileattr
&= ~FILE_ATTRIBUTE_REPARSE_POINT
;
1933 sys_wcstombs (srcbuf
, SYMLINK_MAX
+ 7, subst
.Buffer
,
1934 subst
.Length
/ sizeof (WCHAR
));
1935 pflags
|= PATH_SYMLINK
| PATH_REP
;
1936 /* A symlink is never a directory. */
1937 fileattr
&= ~FILE_ATTRIBUTE_DIRECTORY
;
1938 return posixify (srcbuf
);
1942 symlink_info::check_nfs_symlink (HANDLE h
)
1948 FILE_GET_EA_INFORMATION fgei
;
1949 char buf
[sizeof (NFS_SYML_TARGET
)];
1951 PFILE_FULL_EA_INFORMATION pffei
;
1954 /* To find out if the file is a symlink and to get the symlink target,
1955 try to fetch the NfsSymlinkTargetName EA. */
1956 fgei_buf
.fgei
.NextEntryOffset
= 0;
1957 fgei_buf
.fgei
.EaNameLength
= sizeof (NFS_SYML_TARGET
) - 1;
1958 stpcpy (fgei_buf
.fgei
.EaName
, NFS_SYML_TARGET
);
1959 pffei
= (PFILE_FULL_EA_INFORMATION
) tp
.w_get ();
1960 status
= NtQueryEaFile (h
, &io
, pffei
, NT_MAX_PATH
* sizeof (WCHAR
), TRUE
,
1961 &fgei_buf
.fgei
, sizeof fgei_buf
, NULL
, TRUE
);
1962 if (NT_SUCCESS (status
) && pffei
->EaValueLength
> 0)
1964 PWCHAR spath
= (PWCHAR
)
1965 (pffei
->EaName
+ pffei
->EaNameLength
+ 1);
1966 res
= sys_wcstombs (contents
, SYMLINK_MAX
+ 1,
1967 spath
, pffei
->EaValueLength
) - 1;
1968 pflags
|= PATH_SYMLINK
;
1974 symlink_info::posixify (char *srcbuf
)
1976 /* The definition for a path in a native symlink is a bit weird. The Flags
1977 value seem to contain 0 for absolute paths (stored as NT native path)
1978 and 1 for relative paths. Relative paths are paths not starting with a
1979 drive letter. These are not converted to NT native, but stored as
1980 given. A path starting with a single backslash is relative to the
1981 current drive thus a "relative" value (Flags == 1).
1982 Funny enough it's possible to store paths with slashes instead of
1983 backslashes, but they are evaluated incorrectly by subsequent Windows
1984 calls like CreateFile (ERROR_INVALID_NAME). So, what we do here is to
1985 take paths starting with slashes at face value, evaluating them as
1986 Cygwin specific POSIX paths.
1987 A path starting with two slashes(!) or backslashes is converted into an
1988 NT UNC path. Unfortunately, in contrast to POSIX rules, paths starting
1989 with three or more (back)slashes are also converted into UNC paths,
1990 just incorrectly sticking to one redundant leading backslashe. We go
1991 along with this behaviour to avoid scenarios in which native tools access
1992 other files than Cygwin.
1993 The above rules are used exactly the same way on Cygwin specific symlinks
1994 (sysfiles and shortcuts) to eliminate non-POSIX paths in the output. */
1996 /* Eliminate native NT prefixes. */
1997 if (srcbuf
[0] == '\\' && !strncmp (srcbuf
+ 1, "??\\", 3))
2000 if (srcbuf
[1] != ':') /* native UNC path */
2001 *(srcbuf
+= 2) = '\\';
2003 if (isdrive (srcbuf
))
2004 mount_table
->conv_to_posix_path (srcbuf
, contents
, 0);
2005 else if (srcbuf
[0] == '\\')
2007 if (srcbuf
[1] == '\\') /* UNC path */
2008 slashify (srcbuf
, contents
, 0);
2009 else /* Paths starting with \ are current drive relative. */
2011 char cvtbuf
[SYMLINK_MAX
+ 1];
2013 stpcpy (cvtbuf
+ cygheap
->cwd
.get_drive (cvtbuf
), srcbuf
);
2014 mount_table
->conv_to_posix_path (cvtbuf
, contents
, 0);
2017 else /* Everything else is taken as is. */
2018 slashify (srcbuf
, contents
, 0);
2019 return strlen (contents
);
2028 SCAN_JUSTCHECKTHIS
, /* Never try to append a suffix. */
2036 const suffix_info
*suffixes
, *suffixes_start
;
2041 char *has (const char *, const suffix_info
*);
2043 int lnk_match () {return nextstate
>= SCAN_APPENDLNK
;}
2047 suffix_scan::has (const char *in_path
, const suffix_info
*in_suffixes
)
2049 nextstate
= SCAN_BEG
;
2050 suffixes
= suffixes_start
= in_suffixes
;
2052 const char *fname
= strrchr (in_path
, '\\');
2053 fname
= fname
? fname
+ 1 : in_path
;
2054 char *ext_here
= strrchr (fname
, '.');
2056 eopath
= strchr (path
, '\0');
2063 /* Check if the extension matches a known extension */
2064 for (const suffix_info
*ex
= in_suffixes
; ex
->name
!= NULL
; ex
++)
2065 if (ascii_strcasematch (ext_here
, ex
->name
))
2067 nextstate
= SCAN_JUSTCHECK
;
2068 suffixes
= NULL
; /* Has an extension so don't scan for one. */
2073 /* Didn't match. Use last resort -- .lnk. */
2074 if (ascii_strcasematch (ext_here
, ".lnk"))
2076 nextstate
= SCAN_HASLNK
;
2084 /* Avoid attaching suffixes if the resulting filename would be invalid. */
2085 if (eopath
- fname
> NAME_MAX
- 4)
2087 nextstate
= SCAN_JUSTCHECKTHIS
;
2094 suffix_scan::next ()
2102 suffixes
= suffixes_start
;
2105 nextstate
= SCAN_LNK
;
2108 nextstate
= SCAN_EXTRALNK
;
2109 /* fall through to suffix checking below */
2112 nextstate
= SCAN_APPENDLNK
; /* Skip SCAN_BEG */
2115 nextstate
= SCAN_DONE
;
2118 case SCAN_JUSTCHECK
:
2119 nextstate
= SCAN_LNK
;
2121 case SCAN_JUSTCHECKTHIS
:
2122 nextstate
= SCAN_DONE
;
2125 case SCAN_APPENDLNK
:
2126 strcat (eopath
, ".lnk");
2127 nextstate
= SCAN_DONE
;
2134 while (suffixes
&& suffixes
->name
)
2135 if (nextstate
== SCAN_EXTRALNK
&& !suffixes
->addon
)
2139 strcpy (eopath
, suffixes
->name
);
2140 if (nextstate
== SCAN_EXTRALNK
)
2141 strcat (eopath
, ".lnk");
2150 symlink_info::set_error (int in_errno
)
2153 if (!(pflags
& PATH_NO_ACCESS_CHECK
) || in_errno
== ENAMETOOLONG
|| in_errno
== EIO
)
2158 else if (in_errno
== ENOENT
)
2162 fileattr
= FILE_ATTRIBUTE_NORMAL
;
2169 symlink_info::parse_device (const char *contents
)
2176 mymajor
= strtol (contents
+= 2, &endptr
, 16);
2177 if (endptr
== contents
)
2178 return isdevice
= false;
2181 myminor
= strtol (++contents
, &endptr
, 16);
2182 if (endptr
== contents
)
2183 return isdevice
= false;
2186 mymode
= strtol (++contents
, &endptr
, 16);
2187 if (endptr
== contents
)
2188 return isdevice
= false;
2190 if ((mymode
& S_IFMT
) == S_IFIFO
)
2192 mymajor
= _major (FH_FIFO
);
2193 myminor
= _minor (FH_FIFO
);
2199 return isdevice
= true;
2202 /* Check if PATH is a symlink. PATH must be a valid Win32 path name.
2204 If PATH is a symlink, put the value of the symlink--the file to
2205 which it points--into BUF. The value stored in BUF is not
2206 necessarily null terminated. BUFLEN is the length of BUF; only up
2207 to BUFLEN characters will be stored in BUF. BUF may be NULL, in
2208 which case nothing will be stored.
2210 Set *SYML if PATH is a symlink.
2212 Set *EXEC if PATH appears to be executable. This is an efficiency
2213 hack because we sometimes have to open the file anyhow. *EXEC will
2214 not be set for every executable file.
2216 Return -1 on error, 0 if PATH is not a symlink, or the length
2217 stored into BUF if PATH is a symlink. */
2220 symlink_info::check (char *path
, const suffix_info
*suffixes
, fs_info
&fs
,
2221 path_conv_handle
&conv_hdl
)
2226 UNICODE_STRING upath
;
2227 OBJECT_ATTRIBUTES attr
;
2231 const ULONG ci_flag
= cygwin_shared
->obcaseinsensitive
2232 || (pflags
& PATH_NOPOSIX
) ? OBJ_CASE_INSENSITIVE
: 0;
2233 /* TODO: Temporarily do all char->UNICODE conversion here. This should
2234 already be slightly faster than using Ascii functions. */
2237 InitializeObjectAttributes (&attr
, &upath
, ci_flag
, NULL
, NULL
);
2239 /* This label is used in case we encounter a FS which only handles
2240 DOS paths. See below. */
2241 bool restarted
= false;
2252 pflags
&= ~(PATH_SYMLINK
| PATH_LNK
| PATH_REP
);
2254 ext_here
= suffix
.has (path
, suffixes
);
2255 extn
= ext_here
- path
;
2257 PVOID eabuf
= &nfs_aol_ffei
;
2258 ULONG easize
= sizeof nfs_aol_ffei
;
2260 # define MIN_STAT_ACCESS (READ_CONTROL | FILE_READ_ATTRIBUTES)
2261 # define FULL_STAT_ACCESS (SYNCHRONIZE | GENERIC_READ)
2262 ACCESS_MASK access
= 0;
2264 bool had_ext
= !!*ext_here
;
2265 while (suffix
.next ())
2270 get_nt_native_path (suffix
.path
, upath
, pflags
& PATH_DOS
);
2276 /* The EA given to NtCreateFile allows to get a handle to a symlink on
2277 an NFS share, rather than getting a handle to the target of the
2278 symlink (which would spoil the task of this method quite a bit).
2279 Fortunately it's ignored on most other file systems so we don't have
2280 to special case NFS too much. */
2281 status
= NtCreateFile (&h
, access
= FULL_STAT_ACCESS
, &attr
, &io
, NULL
,
2282 0, FILE_SHARE_VALID_FLAGS
, FILE_OPEN
,
2283 FILE_OPEN_REPARSE_POINT
2284 | FILE_OPEN_FOR_BACKUP_INTENT
,
2286 if (status
== STATUS_ACCESS_DENIED
&& eabuf
)
2288 status
= NtCreateFile (&h
, access
= MIN_STAT_ACCESS
| FILE_READ_EA
,
2289 &attr
, &io
, NULL
, 0, FILE_SHARE_VALID_FLAGS
,
2291 FILE_OPEN_REPARSE_POINT
2292 | FILE_OPEN_FOR_BACKUP_INTENT
,
2294 debug_printf ("%p = NtCreateFile (2:%S)", status
, &upath
);
2297 debug_printf ("%p = NtCreateFile (1:%S)", status
, &upath
);
2298 /* No right to access EAs or EAs not supported? */
2299 if (!NT_SUCCESS (status
)
2300 && (status
== STATUS_ACCESS_DENIED
2301 || status
== STATUS_EAS_NOT_SUPPORTED
2302 || status
== STATUS_NOT_SUPPORTED
2303 || status
== STATUS_INVALID_NETWORK_RESPONSE
2304 /* Or a bug in Samba 3.2.x (x <= 7) when accessing a share's
2305 root dir which has EAs enabled? */
2306 || status
== STATUS_INVALID_PARAMETER
))
2309 /* If EAs are not supported, there's no sense to check them again
2310 with suffixes attached. So we set eabuf/easize to 0 here once. */
2311 if (status
== STATUS_EAS_NOT_SUPPORTED
2312 || status
== STATUS_NOT_SUPPORTED
)
2317 status
= NtOpenFile (&h
, access
= FULL_STAT_ACCESS
, &attr
, &io
,
2318 FILE_SHARE_VALID_FLAGS
,
2319 FILE_OPEN_REPARSE_POINT
2320 | FILE_OPEN_FOR_BACKUP_INTENT
);
2321 if (status
== STATUS_ACCESS_DENIED
)
2323 status
= NtOpenFile (&h
, access
= MIN_STAT_ACCESS
, &attr
, &io
,
2324 FILE_SHARE_VALID_FLAGS
,
2325 FILE_OPEN_REPARSE_POINT
2326 | FILE_OPEN_FOR_BACKUP_INTENT
);
2327 debug_printf ("%p = NtOpenFile (no-EAs 2:%S)", status
, &upath
);
2330 debug_printf ("%p = NtOpenFile (no-EA 1:%S)", status
, &upath
);
2332 if (status
== STATUS_OBJECT_NAME_NOT_FOUND
)
2334 if (ci_flag
== 0 && wincap
.has_broken_udf ()
2335 && (!fs
.inited () || fs
.is_udf ()))
2337 /* On NT 5.x UDF is broken (at least) in terms of case
2338 sensitivity. When trying to open a file case sensitive,
2339 the file appears to be non-existant. Another bug is
2340 described in fs_info::update. */
2341 attr
.Attributes
= OBJ_CASE_INSENSITIVE
;
2342 status
= NtOpenFile (&h
, READ_CONTROL
| FILE_READ_ATTRIBUTES
,
2343 &attr
, &io
, FILE_SHARE_VALID_FLAGS
,
2344 FILE_OPEN_REPARSE_POINT
2345 | FILE_OPEN_FOR_BACKUP_INTENT
);
2346 debug_printf ("%p = NtOpenFile (broken-UDF, %S)", status
, &upath
);
2347 attr
.Attributes
= 0;
2348 if (NT_SUCCESS (status
))
2351 fs
.update (&upath
, h
);
2356 status
= STATUS_OBJECT_NAME_NOT_FOUND
;
2360 /* There are filesystems out in the wild (Netapp, NWFS, and others)
2361 which are uncapable of generating pathnames outside the Win32
2362 rules. That means, filenames on these FSes must not have a
2363 leading space or trailing dots and spaces. This code snippet
2364 manages them. I really hope it's streamlined enough not to
2365 slow down normal operation. This extra check only kicks in if
2366 we encountered a STATUS_OBJECT_NAME_NOT_FOUND *and* we didn't
2367 already attach a suffix *and* the above special case for UDF
2368 on XP didn't succeeed. */
2369 if (!restarted
&& !*ext_here
&& !(pflags
& PATH_DOS
) && !fs
.inited ())
2371 /* Check for trailing dot or space or leading space in
2373 char *p
= ext_here
- 1;
2374 if (*p
!= '.' && *p
!= ' ')
2376 while (*--p
!= '\\')
2383 /* If so, check if file resides on one of the known broken
2384 FSes only supporting filenames following DOS rules. */
2386 fs
.update (&upath
, NULL
);
2387 if (fs
.has_dos_filenames_only ())
2389 /* If so, try again. Since we now know the FS, the
2390 filenames will be tweaked to follow DOS rules via the
2391 third parameter in the call to get_nt_native_path. */
2400 if (NT_SUCCESS (status
)
2401 /* Check file system while we're having the file open anyway.
2402 This speeds up path_conv noticably (~10%). */
2403 && (fs
.inited () || fs
.update (&upath
, h
)))
2407 status
= nfs_fetch_fattr3 (h
, conv_hdl
.nfsattr ());
2408 if (NT_SUCCESS (status
))
2409 fileattr
= ((conv_hdl
.nfsattr ()->type
& 7) == NF3DIR
)
2410 ? FILE_ATTRIBUTE_DIRECTORY
: 0;
2414 PFILE_NETWORK_OPEN_INFORMATION pfnoi
= conv_hdl
.fnoi ();
2416 status
= NtQueryInformationFile (h
, &io
, pfnoi
, sizeof *pfnoi
,
2417 FileNetworkOpenInformation
);
2418 if ((status
== STATUS_INVALID_PARAMETER
2419 || status
== STATUS_NOT_IMPLEMENTED
)
2420 && RtlEqualUnicodePathPrefix (&upath
, &ro_u_uncp
, FALSE
))
2422 /* This occurs when accessing SMB share root dirs hosted on
2423 NT4 (STATUS_INVALID_PARAMETER), or when trying to access
2424 SMB share root dirs from NT4 (STATUS_NOT_IMPLEMENTED). */
2425 FILE_BASIC_INFORMATION fbi
;
2427 status
= NtQueryInformationFile (h
, &io
, &fbi
, sizeof fbi
,
2428 FileBasicInformation
);
2429 if (NT_SUCCESS (status
))
2431 memcpy (pfnoi
, &fbi
, 4 * sizeof (LARGE_INTEGER
));
2432 pfnoi
->EndOfFile
.QuadPart
2433 = pfnoi
->AllocationSize
.QuadPart
= 0;
2434 pfnoi
->FileAttributes
= fbi
.FileAttributes
;
2437 if (NT_SUCCESS (status
))
2438 fileattr
= pfnoi
->FileAttributes
;
2441 if (!NT_SUCCESS (status
))
2443 debug_printf ("%p = NtQueryInformationFile (%S)", status
, &upath
);
2444 fileattr
= INVALID_FILE_ATTRIBUTES
;
2446 /* One of the inner path components is invalid, or the path contains
2447 invalid characters. Bail out with ENOENT.
2449 Note that additional STATUS_OBJECT_PATH_INVALID and
2450 STATUS_OBJECT_PATH_SYNTAX_BAD status codes exist. The first one
2451 is seemingly not generated by NtQueryInformationFile, the latter
2452 is only generated if the path is no absolute path within the
2453 NT name space, which should not happen and would point to an
2454 error in get_nt_native_path. Both status codes are deliberately
2455 not tested here unless proved necessary. */
2456 if (status
== STATUS_OBJECT_PATH_NOT_FOUND
2457 || status
== STATUS_OBJECT_NAME_INVALID
2458 || status
== STATUS_BAD_NETWORK_PATH
2459 || status
== STATUS_BAD_NETWORK_NAME
2460 || status
== STATUS_NO_MEDIA_IN_DEVICE
)
2463 goto file_not_symlink
;
2465 if (status
!= STATUS_OBJECT_NAME_NOT_FOUND
2466 && status
!= STATUS_NO_SUCH_FILE
) /* ENOENT on NFS or 9x share */
2468 /* The file exists, but the user can't access it for one reason
2469 or the other. To get the file attributes we try to access the
2470 information by opening the parent directory and getting the
2471 file attributes using a matching NtQueryDirectoryFile call. */
2472 UNICODE_STRING dirname
, basename
;
2473 OBJECT_ATTRIBUTES dattr
;
2476 FILE_BOTH_DIRECTORY_INFORMATION fdi
;
2477 WCHAR dummy_buf
[NAME_MAX
+ 1];
2480 RtlSplitUnicodePath (&upath
, &dirname
, &basename
);
2481 InitializeObjectAttributes (&dattr
, &dirname
, ci_flag
,
2483 status
= NtOpenFile (&dir
, SYNCHRONIZE
| FILE_LIST_DIRECTORY
,
2484 &dattr
, &io
, FILE_SHARE_VALID_FLAGS
,
2485 FILE_SYNCHRONOUS_IO_NONALERT
2486 | FILE_OPEN_FOR_BACKUP_INTENT
2487 | FILE_DIRECTORY_FILE
);
2488 if (!NT_SUCCESS (status
))
2490 debug_printf ("%p = NtOpenFile(%S)", status
, &dirname
);
2491 /* There's a special case if the file is itself the root
2492 of a drive which is not accessible by the current user.
2493 This case is only recognized by the length of the
2494 basename part. If it's 0, the incoming file is the
2495 root of a drive. So we at least know it's a directory. */
2496 fileattr
= basename
.Length
? 0 : FILE_ATTRIBUTE_DIRECTORY
;
2500 status
= NtQueryDirectoryFile (dir
, NULL
, NULL
, NULL
, &io
,
2501 &fdi_buf
, sizeof fdi_buf
,
2502 FileBothDirectoryInformation
,
2503 TRUE
, &basename
, TRUE
);
2504 /* Take the opportunity to check file system while we're
2505 having the handle to the parent dir. */
2506 fs
.update (&upath
, dir
);
2508 if (!NT_SUCCESS (status
))
2510 debug_printf ("%p = NtQueryDirectoryFile(%S)",
2512 if (status
== STATUS_NO_SUCH_FILE
)
2514 /* This can happen when trying to access files
2515 which match DOS device names on SMB shares.
2516 NtOpenFile failed with STATUS_ACCESS_DENIED,
2517 but the NtQueryDirectoryFile tells us the
2518 file doesn't exist. We're suspicious in this
2519 case and retry with the next suffix instead of
2528 PFILE_NETWORK_OPEN_INFORMATION pfnoi
= conv_hdl
.fnoi ();
2530 fileattr
= fdi_buf
.fdi
.FileAttributes
;
2531 memcpy (pfnoi
, &fdi_buf
.fdi
.CreationTime
, sizeof *pfnoi
);
2532 /* Amazing, but true: The FILE_NETWORK_OPEN_INFORMATION
2533 structure has the AllocationSize and EndOfFile members
2534 interchanged relative to the directory information
2536 pfnoi
->AllocationSize
.QuadPart
2537 = fdi_buf
.fdi
.AllocationSize
.QuadPart
;
2538 pfnoi
->EndOfFile
.QuadPart
2539 = fdi_buf
.fdi
.EndOfFile
.QuadPart
;
2542 ext_tacked_on
= !!*ext_here
;
2543 goto file_not_symlink
;
2549 ext_tacked_on
= !!*ext_here
;
2550 /* Don't allow to returns directories with appended suffix. If we found
2551 a directory with a suffix which has been appended here, then this
2552 directory doesn't match the request. So, just do as usual if file
2553 hasn't been found. */
2554 if (ext_tacked_on
&& !had_ext
&& (fileattr
& FILE_ATTRIBUTE_DIRECTORY
))
2562 /* Windows shortcuts are potentially treated as symlinks. Valid Cygwin
2563 & U/WIN shortcuts are R/O, but definitely not directories. */
2564 if ((fileattr
& (FILE_ATTRIBUTE_READONLY
| FILE_ATTRIBUTE_DIRECTORY
))
2565 == FILE_ATTRIBUTE_READONLY
&& suffix
.lnk_match ())
2567 if (!(access
& GENERIC_READ
))
2570 res
= check_shortcut (h
);
2573 /* If searching for `foo' and then finding a `foo.lnk' which is
2574 no shortcut, return the same as if file not found. */
2577 fileattr
= INVALID_FILE_ATTRIBUTES
;
2582 else if (contents
[0] != ':' || contents
[1] != '\\'
2583 || !parse_device (contents
))
2587 /* If searching for `foo' and then finding a `foo.lnk' which is
2588 no shortcut, return the same as if file not found. */
2589 else if (suffix
.lnk_match () && ext_tacked_on
)
2591 fileattr
= INVALID_FILE_ATTRIBUTES
;
2596 /* Reparse points are potentially symlinks. This check must be
2597 performed before checking the SYSTEM attribute for sysfile
2598 symlinks, since reparse points can have this flag set, too.
2599 For instance, Vista starts to create a couple of reparse points
2600 with SYSTEM and HIDDEN flags set.
2601 Also don't check reparse points on remote filesystems.
2602 A reparse point pointing to another file on the remote system will be
2603 mistreated as pointing to a local file on the local system. This
2604 breaks the way reparse points are transparently handled on remote
2606 else if ((fileattr
& FILE_ATTRIBUTE_REPARSE_POINT
)
2607 && !fs
.is_remote_drive())
2609 res
= check_reparse_point (h
);
2612 /* Volume mount point. The filesystem information for the top
2613 level directory should be for the volume top level directory,
2614 rather than for the reparse point itself. So we fetch the
2615 filesystem information again, but with a NULL handle.
2616 This does what we want because fs_info::update opens the
2617 handle without FILE_OPEN_REPARSE_POINT. */
2618 fs
.update (&upath
, NULL
);
2619 /* Make sure the open handle is not used in later stat calls.
2620 The handle has been opened with the FILE_OPEN_REPARSE_POINT
2621 flag, so it's a handle to the reparse point, not a handle
2622 to the volumes root dir. */
2623 pflags
&= ~PC_KEEP_HANDLE
;
2627 /* A symlink is never a directory. */
2628 conv_hdl
.fnoi ()->FileAttributes
&= ~FILE_ATTRIBUTE_DIRECTORY
;
2633 /* This is the old Cygwin method creating symlinks. A symlink will
2634 have the `system' file attribute. Only files can be symlinks
2635 (which can be symlinks to directories). */
2636 else if ((fileattr
& (FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_DIRECTORY
))
2637 == FILE_ATTRIBUTE_SYSTEM
)
2639 if (!(access
& GENERIC_READ
))
2642 res
= check_sysfile (h
);
2647 /* If the file is on an NFS share and could be opened with extended
2648 attributes, check if it's a symlink. Only files can be symlinks
2649 (which can be symlinks to directories). */
2650 else if (fs
.is_nfs () && (conv_hdl
.nfsattr ()->type
& 7) == NF3LNK
)
2652 res
= check_nfs_symlink (h
);
2660 syscall_printf ("%s", isdevice
? "is a device" : "not a symlink");
2667 if (pflags
& PC_KEEP_HANDLE
)
2668 conv_hdl
.set (h
, access
);
2673 syscall_printf ("%d = symlink.check (%s, %p) (%p)",
2674 res
, suffix
.path
, contents
, pflags
);
2678 /* "path" is the path in a virtual symlink. Set a symlink_info struct from
2679 that and proceed with further path checking afterwards. */
2681 symlink_info::set (char *path
)
2683 strcpy (contents
, path
);
2684 pflags
= PATH_SYMLINK
;
2685 fileattr
= FILE_ATTRIBUTE_NORMAL
;
2689 ext_tacked_on
= false;
2691 extn
= major
= minor
= mode
= 0;
2692 return strlen (path
);
2695 /* readlink system call */
2698 readlink (const char *path
, char *buf
, size_t buflen
)
2702 set_errno (ENAMETOOLONG
);
2706 path_conv
pathbuf (path
, PC_SYM_CONTENTS
, stat_suffixes
);
2710 set_errno (pathbuf
.error
);
2711 syscall_printf ("-1 = readlink (%s, %p, %d)", path
, buf
, buflen
);
2715 if (!pathbuf
.exists ())
2721 if (!pathbuf
.issymlink ())
2723 if (pathbuf
.exists ())
2728 ssize_t len
= min (buflen
, strlen (pathbuf
.get_win32 ()));
2729 memcpy (buf
, pathbuf
.get_win32 (), len
);
2731 /* errno set by symlink.check if error */
2735 /* Some programs rely on st_dev/st_ino being unique for each file.
2736 Hash the path name and hope for the best. The hash arg is not
2737 always initialized to zero since readdir needs to compute the
2738 dirent ino_t based on a combination of the hash of the directory
2739 done during the opendir call and the hash or the filename within
2740 the directory. FIXME: Not bullet-proof. */
2741 /* Cygwin internal */
2743 hash_path_name (__ino64_t hash
, PUNICODE_STRING name
)
2745 if (name
->Length
== 0)
2748 /* Build up hash. Name is already normalized */
2749 USHORT len
= name
->Length
/ sizeof (WCHAR
);
2750 for (USHORT idx
= 0; idx
< len
; ++idx
)
2751 hash
= RtlUpcaseUnicodeChar (name
->Buffer
[idx
])
2752 + (hash
<< 6) + (hash
<< 16) - hash
;
2757 hash_path_name (__ino64_t hash
, PCWSTR name
)
2759 UNICODE_STRING uname
;
2760 RtlInitUnicodeString (&uname
, name
);
2761 return hash_path_name (hash
, &uname
);
2765 hash_path_name (__ino64_t hash
, const char *name
)
2767 UNICODE_STRING uname
;
2768 RtlCreateUnicodeStringFromAsciiz (&uname
, name
);
2769 __ino64_t ret
= hash_path_name (hash
, &uname
);
2770 RtlFreeUnicodeString (&uname
);
2775 getcwd (char *buf
, size_t ulen
)
2779 if (efault
.faulted (EFAULT
))
2781 else if (ulen
== 0 && buf
)
2784 res
= cygheap
->cwd
.get (buf
, 1, 1, ulen
);
2788 /* getwd: Legacy. */
2792 return getcwd (buf
, PATH_MAX
+ 1); /*Per SuSv3!*/
2795 /* chdir: POSIX 5.2.1.1 */
2797 chdir (const char *in_dir
)
2800 if (efault
.faulted (EFAULT
))
2808 syscall_printf ("dir '%s'", in_dir
);
2810 /* Convert path. First argument ensures that we don't check for NULL/empty/invalid
2812 path_conv
path (PC_NONULLEMPTY
, in_dir
, PC_SYM_FOLLOW
| PC_POSIX
);
2815 set_errno (path
.error
);
2816 syscall_printf ("-1 = chdir (%s)", in_dir
);
2821 const char *posix_cwd
= NULL
;
2822 int devn
= path
.get_devn ();
2823 if (!path
.exists ())
2825 else if (!path
.isdir ())
2826 set_errno (ENOTDIR
);
2827 else if (!isvirtual_dev (devn
))
2829 /* The sequence chdir("xx"); chdir(".."); must be a noop if xx
2830 is not a symlink. This is exploited by find.exe.
2831 The posix_cwd is just path.normalized_path.
2832 In other cases we let cwd.set obtain the Posix path through
2834 if (!isdrive(path
.normalized_path
))
2835 posix_cwd
= path
.normalized_path
;
2840 posix_cwd
= path
.normalized_path
;
2845 res
= cygheap
->cwd
.set (&path
, posix_cwd
);
2847 /* Note that we're accessing cwd.posix without a lock here. I didn't think
2848 it was worth locking just for strace. */
2849 syscall_printf ("%d = chdir() cygheap->cwd.posix '%s' native '%S'", res
,
2850 cygheap
->cwd
.get_posix (), path
.get_nt_native_path ());
2859 cygheap_fdget
cfd (fd
);
2861 res
= chdir (cfd
->get_name ());
2865 syscall_printf ("%d = fchdir (%d)", res
, fd
);
2869 /******************** Exported Path Routines *********************/
2871 /* Cover functions to the path conversion routines.
2872 These are exported to the world as cygwin_foo by cygwin.din. */
2874 #define return_with_errno(x) \
2884 cygwin_conv_path (cygwin_conv_path_t what
, const void *from
, void *to
,
2889 if (efault
.faulted (EFAULT
))
2897 bool relative
= !!(what
& CCP_RELATIVE
);
2898 what
&= ~CCP_RELATIVE
;
2902 case CCP_POSIX_TO_WIN_A
:
2904 p
.check ((const char *) from
,
2905 PC_POSIX
| PC_SYM_FOLLOW
| PC_NO_ACCESS_CHECK
| PC_NOWARN
2906 | (relative
? PC_NOFULL
: 0));
2908 return_with_errno (p
.error
);
2909 PUNICODE_STRING up
= p
.get_nt_native_path ();
2911 sys_wcstombs (buf
, NT_MAX_PATH
, up
->Buffer
, up
->Length
/ sizeof (WCHAR
));
2912 /* Convert native path to standard DOS path. */
2913 if (!strncmp (buf
, "\\??\\", 4))
2916 if (buf
[1] != ':') /* native UNC path */
2919 else if (*buf
== '\\')
2921 /* Device name points to somewhere else in the NT namespace.
2922 Use GLOBALROOT prefix to convert to Win32 path. */
2923 char *p
= stpcpy (buf
, "\\\\.\\GLOBALROOT");
2924 sys_wcstombs (p
, NT_MAX_PATH
- (p
- buf
),
2925 up
->Buffer
, up
->Length
/ sizeof (WCHAR
));
2927 lsiz
= strlen (buf
) + 1;
2928 /* TODO: Incoming "." is a special case which leads to a trailing
2929 backslash ".\\" in the Win32 path. That's a result of the
2930 conversion in normalize_posix_path. This should not occur
2931 so the below code is just a band-aid. */
2932 if (relative
&& !strcmp ((const char *) from
, ".")
2933 && !strcmp (buf
, ".\\"))
2940 case CCP_POSIX_TO_WIN_W
:
2941 p
.check ((const char *) from
, PC_POSIX
| PC_SYM_FOLLOW
2942 | PC_NO_ACCESS_CHECK
| PC_NOWARN
2943 | (relative
? PC_NOFULL
: 0));
2945 return_with_errno (p
.error
);
2946 /* Relative Windows paths are always restricted to MAX_PATH chars. */
2947 if (relative
&& !isabspath (p
.get_win32 ())
2948 && sys_mbstowcs (NULL
, 0, p
.get_win32 ()) > MAX_PATH
)
2950 /* Recreate as absolute path. */
2951 p
.check ((const char *) from
, PC_POSIX
| PC_SYM_FOLLOW
2952 | PC_NO_ACCESS_CHECK
| PC_NOWARN
);
2954 return_with_errno (p
.error
);
2956 lsiz
= p
.get_wide_win32_path_len () + 1;
2957 path
= p
.get_nt_native_path ()->Buffer
;
2959 /* Convert native path to standard DOS path. */
2960 if (!wcsncmp (path
, L
"\\??\\", 4))
2964 /* Drop long path prefix for short pathnames. Unfortunately there's
2965 quite a bunch of Win32 functions, especially in user32.dll,
2966 apparently, which don't grok long path names at all, not even
2967 in the UNICODE API. */
2968 if ((path
[5] == L
':' && lsiz
<= MAX_PATH
+ 4)
2969 || (!wcsncmp (path
+ 4, L
"UNC\\", 4) && lsiz
<= MAX_PATH
+ 6))
2973 if (path
[1] != L
':')
2975 *(path
+= 2) = '\\';
2980 else if (*path
== L
'\\')
2982 /* Device name points to somewhere else in the NT namespace.
2983 Use GLOBALROOT prefix to convert to Win32 path. */
2984 to
= (void *) wcpcpy ((wchar_t *) to
, L
"\\\\.\\GLOBALROOT");
2985 lsiz
+= sizeof ("\\\\.\\GLOBALROOT") - 1;
2987 /* TODO: Same ".\\" band-aid as in CCP_POSIX_TO_WIN_A case. */
2988 if (relative
&& !strcmp ((const char *) from
, ".")
2989 && !wcscmp (path
, L
".\\"))
2994 lsiz
*= sizeof (WCHAR
);
2996 case CCP_WIN_A_TO_POSIX
:
2998 error
= mount_table
->conv_to_posix_path ((const char *) from
, buf
,
3001 return_with_errno (error
);
3002 lsiz
= strlen (buf
) + 1;
3004 case CCP_WIN_W_TO_POSIX
:
3006 error
= mount_table
->conv_to_posix_path ((const PWCHAR
) from
, buf
,
3009 return_with_errno (error
);
3010 lsiz
= strlen (buf
) + 1;
3025 case CCP_POSIX_TO_WIN_A
:
3026 case CCP_WIN_A_TO_POSIX
:
3027 case CCP_WIN_W_TO_POSIX
:
3028 stpcpy ((char *) to
, buf
);
3030 case CCP_POSIX_TO_WIN_W
:
3031 wcpcpy ((PWCHAR
) to
, path
);
3038 cygwin_create_path (cygwin_conv_path_t what
, const void *from
)
3041 ssize_t size
= cygwin_conv_path (what
, from
, NULL
, 0);
3044 else if (!(to
= malloc (size
)))
3046 if (cygwin_conv_path (what
, from
, to
, size
) == -1)
3056 cygwin_conv_to_win32_path (const char *path
, char *win32_path
)
3058 return cygwin_conv_path (CCP_POSIX_TO_WIN_A
| CCP_RELATIVE
, path
, win32_path
,
3063 cygwin_conv_to_full_win32_path (const char *path
, char *win32_path
)
3065 return cygwin_conv_path (CCP_POSIX_TO_WIN_A
| CCP_ABSOLUTE
, path
, win32_path
,
3069 /* This is exported to the world as cygwin_foo by cygwin.din. */
3072 cygwin_conv_to_posix_path (const char *path
, char *posix_path
)
3074 return cygwin_conv_path (CCP_WIN_A_TO_POSIX
| CCP_RELATIVE
, path
, posix_path
,
3079 cygwin_conv_to_full_posix_path (const char *path
, char *posix_path
)
3081 return cygwin_conv_path (CCP_WIN_A_TO_POSIX
| CCP_ABSOLUTE
, path
, posix_path
,
3085 /* The realpath function is required by POSIX:2008. */
3088 realpath (const char *path
, char *resolved
)
3090 /* Make sure the right errno is returned if path is NULL. */
3097 /* Guard reading from a potentially invalid path and writing to a
3098 potentially invalid resolved. */
3101 if (efault
.faulted (EFAULT
))
3107 tpath
= tp
.c_get ();
3108 mount_table
->cygdrive_posix_path (path
, tpath
, 0);
3111 tpath
= (char *) path
;
3113 path_conv
real_path (tpath
, PC_SYM_FOLLOW
| PC_POSIX
, stat_suffixes
);
3116 /* POSIX 2008 requires malloc'ing if resolved is NULL, and states
3117 that using non-NULL resolved is asking for portability
3120 if (!real_path
.error
&& real_path
.exists ())
3124 resolved
= (char *) malloc (strlen (real_path
.normalized_path
) + 1);
3128 strcpy (resolved
, real_path
.normalized_path
);
3132 /* FIXME: on error, Linux puts the name of the path
3133 component which could not be resolved into RESOLVED, but POSIX
3134 does not require this. */
3137 set_errno (real_path
.error
?: ENOENT
);
3141 /* Linux provides this extension. Since the only portable use of
3142 realpath requires a NULL second argument, we might as well have a
3143 one-argument wrapper. */
3145 canonicalize_file_name (const char *path
)
3147 return realpath (path
, NULL
);
3150 /* Return non-zero if path is a POSIX path list.
3151 This is exported to the world as cygwin_foo by cygwin.din.
3154 <sect1 id="add-func-cygwin-posix-path-list-p">
3155 <para>Rather than use a mode to say what the "proper" path list
3156 format is, we allow any, and give apps the tools they need to
3157 convert between the two. If a ';' is present in the path list it's
3158 a Win32 path list. Otherwise, if the first path begins with
3159 [letter]: (in which case it can be the only element since if it
3160 wasn't a ';' would be present) it's a Win32 path list. Otherwise,
3161 it's a POSIX path list.</para>
3167 cygwin_posix_path_list_p (const char *path
)
3169 int posix_p
= !(strchr (path
, ';') || isdrive (path
));
3173 /* These are used for apps that need to convert env vars like PATH back and
3174 forth. The conversion is a two step process. First, an upper bound on the
3175 size of the buffer needed is computed. Then the conversion is done. This
3176 allows the caller to use alloca if it wants. */
3179 conv_path_list_buf_size (const char *path_list
, bool to_posix
)
3181 int i
, num_elms
, max_mount_path_len
, size
;
3184 path_conv
pc(".", PC_POSIX
);
3185 /* The theory is that an upper bound is
3186 current_size + (num_elms * max_mount_path_len) */
3187 /* FIXME: This method is questionable in the long run. */
3190 char delim
= to_posix
? ';' : ':';
3191 for (p
= path_list
, num_elms
= nrel
= 0; p
; num_elms
++)
3195 p
= strchr (++p
, delim
);
3198 /* 7: strlen ("//c") + slop, a conservative initial value */
3199 for (max_mount_path_len
= sizeof ("/cygdrive/X"), i
= 0;
3200 i
< mount_table
->nmounts
; i
++)
3202 int mount_len
= (to_posix
3203 ? mount_table
->mount
[i
].posix_pathlen
3204 : mount_table
->mount
[i
].native_pathlen
);
3205 if (max_mount_path_len
< mount_len
)
3206 max_mount_path_len
= mount_len
;
3210 size
= strlen (path_list
)
3211 + (num_elms
* max_mount_path_len
)
3212 + (nrel
* strlen (to_posix
? pc
.normalized_path
: pc
.get_win32 ()))
3220 cygwin_win32_to_posix_path_list_buf_size (const char *path_list
)
3222 return conv_path_list_buf_size (path_list
, true);
3226 cygwin_posix_to_win32_path_list_buf_size (const char *path_list
)
3228 return conv_path_list_buf_size (path_list
, false);
3232 env_PATH_to_posix (const void *win32
, void *posix
, size_t size
)
3234 return_with_errno (conv_path_list ((const char *) win32
, (char *) posix
,
3239 cygwin_win32_to_posix_path_list (const char *win32
, char *posix
)
3241 return_with_errno (conv_path_list (win32
, posix
, MAX_PATH
, 1));
3245 cygwin_posix_to_win32_path_list (const char *posix
, char *win32
)
3247 return_with_errno (conv_path_list (posix
, win32
, MAX_PATH
, 0));
3251 cygwin_conv_path_list (cygwin_conv_path_t what
, const void *from
, void *to
,
3254 /* FIXME: Path lists are (so far) always retaining relative paths. */
3255 what
&= ~CCP_RELATIVE
;
3258 case CCP_WIN_W_TO_POSIX
:
3259 case CCP_POSIX_TO_WIN_W
:
3261 api_fatal ("wide char path lists not yet supported");
3263 case CCP_WIN_A_TO_POSIX
:
3264 case CCP_POSIX_TO_WIN_A
:
3266 return conv_path_list_buf_size ((const char *) from
,
3267 what
== CCP_WIN_A_TO_POSIX
);
3268 return_with_errno (conv_path_list ((const char *) from
, (char *) to
,
3269 size
, what
== CCP_WIN_A_TO_POSIX
));
3278 /* cygwin_split_path: Split a path into directory and file name parts.
3279 Buffers DIR and FILE are assumed to be big enough.
3281 Examples (path -> `dir' / `file'):
3284 . -> `.' / `.' (FIXME: should this be `.' / `'?)
3285 .. -> `.' / `..' (FIXME: should this be `..' / `'?)
3287 foo/bar -> `foo' / `bar'
3288 foo/bar/ -> `foo' / `bar'
3290 /foo/bar -> `/foo' / `bar'
3293 c:foo -> `c:/' / `foo'
3294 c:/foo -> `c:/' / `foo'
3298 cygwin_split_path (const char *path
, char *dir
, char *file
)
3300 int dir_started_p
= 0;
3302 /* Deal with drives.
3303 Remember that c:foo <==> c:/foo. */
3315 if (isdirsep (*path
))
3320 /* Determine if there are trailing slashes and "delete" them if present.
3321 We pretend as if they don't exist. */
3322 const char *end
= path
+ strlen (path
);
3323 /* path + 1: keep leading slash. */
3324 while (end
> path
+ 1 && isdirsep (end
[-1]))
3327 /* At this point, END points to one beyond the last character
3328 (with trailing slashes "deleted"). */
3330 /* Point LAST_SLASH at the last slash (duh...). */
3331 const char *last_slash
;
3332 for (last_slash
= end
- 1; last_slash
>= path
; --last_slash
)
3333 if (isdirsep (*last_slash
))
3336 if (last_slash
== path
)
3341 else if (last_slash
> path
)
3343 memcpy (dir
, path
, last_slash
- path
);
3344 dir
[last_slash
- path
] = 0;
3349 ; /* nothing to do */
3355 memcpy (file
, last_slash
+ 1, end
- last_slash
- 1);
3356 file
[end
- last_slash
- 1] = 0;
3359 /*****************************************************************************/
3361 static inline PRTL_USER_PROCESS_PARAMETERS
3362 get_user_proc_parms ()
3364 return NtCurrentTeb ()->Peb
->ProcessParameters
;
3367 /* Initialize cygcwd 'muto' for serializing access to cwd info. */
3371 cwd_lock
.init ("cwd_lock");
3373 /* Cygwin processes inherit the cwd from their parent. If the win32 path
3374 buffer is not NULL, the cwd struct is already set up. */
3378 /* Initially re-open the cwd to allow POSIX semantics. */
3382 /* Chdir and fill out the elements of a cwdstuff struct. */
3384 cwdstuff::set (path_conv
*nat_cwd
, const char *posix_cwd
)
3387 UNICODE_STRING upath
;
3388 bool virtual_path
= false;
3389 bool unc_path
= false;
3390 bool inaccessible_path
= false;
3392 /* Here are the problems with using SetCurrentDirectory. Just skip this
3393 comment if you don't like whining.
3395 - SetCurrentDirectory only supports paths of up to MAX_PATH - 1 chars,
3396 including a trailing backslash. That's an absolute restriction, even
3399 - SetCurrentDirectory fails for directories with strict permissions even
3400 for processes with the SE_BACKUP_NAME privilege enabled. The reason
3401 is apparently that SetCurrentDirectory calls NtOpenFile without the
3402 FILE_OPEN_FOR_BACKUP_INTENT flag set.
3404 - SetCurrentDirectory does not support case-sensitivity.
3406 - Unlinking a cwd fails because SetCurrentDirectory seems to open
3407 directories so that deleting the directory is disallowed.
3409 - SetCurrentDirectory can naturally not work on virtual Cygwin paths
3410 like /proc or /cygdrive.
3412 Unfortunately, even though we have access to the Win32 process parameter
3413 block, we can't just replace the directory handle. Starting with Vista,
3414 the handle is used elsewhere, and just replacing the handle in the process
3415 parameter block shows quite surprising results.
3416 FIXME: If we ever find a *safe* way to replace the directory handle in
3417 the process parameter block, we're back in business.
3419 Nevertheless, doing entirely without SetCurrentDirectory is not really
3420 feasible, because it breaks too many mixed applications using the Win32
3423 Therefore we handle the CWD all by ourselves and just keep the Win32
3424 CWD in sync. However, to avoid surprising behaviour in the Win32 API
3425 when we are in a CWD which is inaccessible as Win32 CWD, we set the
3426 Win32 CWD to a "weird" directory in which all relative filesystem-related
3429 cwd_lock
.acquire ();
3433 upath
= *nat_cwd
->get_nt_native_path ();
3434 if (nat_cwd
->isspecial ())
3435 virtual_path
= true;
3438 /* Open a directory handle with FILE_OPEN_FOR_BACKUP_INTENT and with all
3439 sharing flags set. The handle is right now used in exceptions.cc only,
3440 but that might change in future. */
3445 OBJECT_ATTRIBUTES attr
;
3449 /* On init, just reopen Win32 CWD with desired access flags.
3450 We can access the PEB without lock, because no other thread
3451 can change the CWD. */
3452 RtlInitUnicodeString (&upath
, L
"");
3453 InitializeObjectAttributes (&attr
, &upath
,
3454 OBJ_CASE_INSENSITIVE
| OBJ_INHERIT
,
3455 get_user_proc_parms ()->CurrentDirectoryHandle
, NULL
);
3458 InitializeObjectAttributes (&attr
, &upath
,
3459 nat_cwd
->objcaseinsensitive () | OBJ_INHERIT
,
3461 /* First try without FILE_OPEN_FOR_BACKUP_INTENT, to find out if the
3462 directory is valid for Win32 apps. And, no, we can't just call
3463 SetCurrentDirectory here, since that would potentially break
3464 case-sensitivity. */
3465 status
= NtOpenFile (&h
, SYNCHRONIZE
| FILE_TRAVERSE
, &attr
, &io
,
3466 FILE_SHARE_VALID_FLAGS
,
3468 | FILE_SYNCHRONOUS_IO_NONALERT
);
3469 if (status
== STATUS_ACCESS_DENIED
)
3471 status
= NtOpenFile (&h
, SYNCHRONIZE
| FILE_TRAVERSE
, &attr
, &io
,
3472 FILE_SHARE_VALID_FLAGS
,
3474 | FILE_SYNCHRONOUS_IO_NONALERT
3475 | FILE_OPEN_FOR_BACKUP_INTENT
);
3476 inaccessible_path
= true;
3478 if (!NT_SUCCESS (status
))
3480 cwd_lock
.release ();
3481 __seterrno_from_nt_status (status
);
3485 /* Set new handle. It's only used when creating stackdumps so far. */
3492 /* On init, just fetch the Win32 dir from the PEB. We can access
3493 the PEB without lock, because no other thread can change the CWD
3495 PUNICODE_STRING pdir
= &get_user_proc_parms ()->CurrentDirectoryName
;
3496 RtlInitEmptyUnicodeString (&win32
,
3497 (PWCHAR
) crealloc_abort (win32
.Buffer
,
3500 pdir
->Length
+ sizeof (WCHAR
));
3501 RtlCopyUnicodeString (&win32
, pdir
);
3503 PWSTR eoBuffer
= win32
.Buffer
+ (win32
.Length
/ sizeof (WCHAR
));
3504 /* Remove trailing slash if one exists. */
3505 if ((eoBuffer
- win32
.Buffer
) > 3 && eoBuffer
[-1] == L
'\\')
3506 win32
.Length
-= sizeof (WCHAR
);
3507 if (eoBuffer
[0] == L
'\\')
3514 if (virtual_path
) /* don't mangle virtual path. */
3518 /* Compute length on Win32 path. */
3519 size_t len
= upath
.Length
/ sizeof (WCHAR
) - 4;
3520 if (RtlEqualUnicodePathPrefix (&upath
, &ro_u_uncp
, TRUE
))
3525 /* Convert to a Win32 path. */
3526 upath
.Buffer
+= upath
.Length
/ sizeof (WCHAR
) - len
;
3527 upath
.Length
= len
* sizeof (WCHAR
);
3529 PWSTR eoBuffer
= upath
.Buffer
+ (upath
.Length
/ sizeof (WCHAR
));
3530 /* Remove trailing slash if one exists. */
3531 if ((eoBuffer
- upath
.Buffer
) > 3 && eoBuffer
[-1] == L
'\\')
3532 upath
.Length
-= sizeof (WCHAR
);
3534 RtlInitEmptyUnicodeString (&win32
,
3535 (PWCHAR
) crealloc_abort (win32
.Buffer
,
3538 upath
.Length
+ sizeof (WCHAR
));
3539 RtlCopyUnicodeString (&win32
, &upath
);
3541 win32
.Buffer
[0] = L
'\\';
3543 /* Make sure it's NUL-terminated. */
3544 win32
.Buffer
[win32
.Length
/ sizeof (WCHAR
)] = L
'\0';
3546 /* Set drive_length, used in path conversion, and error code, used in
3547 spawn_guts to decide whether a native Win32 app can be started or not. */
3559 PWCHAR ptr
= wcschr (win32
.Buffer
+ 2, L
'\\');
3561 ptr
= wcschr (ptr
+ 1, L
'\\');
3563 drive_length
= ptr
- win32
.Buffer
;
3565 drive_length
= win32
.Length
/ sizeof (WCHAR
);
3567 if (inaccessible_path
)
3569 else if (win32
.Length
> (MAX_PATH
- 2) * sizeof (WCHAR
))
3570 error
= ENAMETOOLONG
;
3574 /* Keep the Win32 CWD in sync. Don't check for error, other than for
3575 strace output. Try to keep overhead low. */
3578 status
= RtlSetCurrentDirectory_U (error
? &ro_u_pipedir
: &win32
);
3579 if (!NT_SUCCESS (status
))
3580 debug_printf ("RtlSetCurrentDirectory_U(%S) failed, %p",
3581 error
? &ro_u_pipedir
: &win32
, status
);
3584 /* Eventually, create POSIX path if it's not set on entry. */
3588 posix_cwd
= (const char *) tp
.c_get ();
3589 mount_table
->conv_to_posix_path (win32
.Buffer
, (char *) posix_cwd
, 0);
3591 posix
= (char *) crealloc_abort (posix
, strlen (posix_cwd
) + 1);
3592 stpcpy (posix
, posix_cwd
);
3594 cwd_lock
.release ();
3599 cwdstuff::get_error_desc () const
3601 switch (cygheap
->cwd
.get_error ())
3604 return "has restricted permissions which render it\n"
3605 "inaccessible as Win32 working directory";
3607 return "is a virtual Cygwin directory which does\n"
3608 "not exist for a native Windows application";
3610 return "has a path longer than allowed for a\n"
3611 "Win32 working directory";
3615 /* That shouldn't occur, unless we defined a new error code
3616 in cwdstuff::set. */
3617 return "is not accessible for some unknown reason";
3620 /* Store incoming wchar_t path as current posix cwd. This is called from
3621 setlocale so that the cwd is always stored in the right charset. */
3623 cwdstuff::reset_posix (wchar_t *w_cwd
)
3625 size_t len
= sys_wcstombs (NULL
, (size_t) -1, w_cwd
);
3626 posix
= (char *) crealloc_abort (posix
, len
+ 1);
3627 sys_wcstombs (posix
, len
+ 1, w_cwd
);
3631 cwdstuff::get (char *buf
, int need_posix
, int with_chroot
, unsigned ulen
)
3638 else if (buf
== NULL
)
3639 ulen
= (unsigned) -1;
3646 cwd_lock
.acquire ();
3651 tocopy
= tp
.c_get ();
3652 sys_wcstombs (tocopy
, NT_MAX_PATH
, win32
.Buffer
,
3653 win32
.Length
/ sizeof (WCHAR
));
3658 debug_printf ("posix %s", posix
);
3659 if (strlen (tocopy
) >= ulen
)
3667 buf
= (char *) malloc (strlen (tocopy
) + 1);
3668 strcpy (buf
, tocopy
);
3669 if (!buf
[0]) /* Should only happen when chroot */
3673 cwd_lock
.release ();
3676 syscall_printf ("(%s) = cwdstuff::get (%p, %d, %d, %d), errno %d",
3677 buf
, buf
, ulen
, need_posix
, with_chroot
, errno
);
3682 int etc::curr_ix
= 0;
3683 /* Note that the first elements of the below arrays are unused */
3684 bool etc::change_possible
[MAX_ETC_FILES
+ 1];
3685 OBJECT_ATTRIBUTES
etc::fn
[MAX_ETC_FILES
+ 1];
3686 LARGE_INTEGER
etc::last_modified
[MAX_ETC_FILES
+ 1];
3689 etc::init (int n
, POBJECT_ATTRIBUTES attr
)
3693 else if (++curr_ix
<= MAX_ETC_FILES
)
3696 api_fatal ("internal error");
3699 change_possible
[n
] = false;
3700 test_file_change (n
);
3701 paranoid_printf ("fn[%d] %S, curr_ix %d", n
, fn
[n
].ObjectName
, curr_ix
);
3706 etc::test_file_change (int n
)
3709 FILE_NETWORK_OPEN_INFORMATION fnoi
;
3712 status
= NtQueryFullAttributesFile (&fn
[n
], &fnoi
);
3713 if (!NT_SUCCESS (status
))
3716 memset (last_modified
+ n
, 0, sizeof (last_modified
[n
]));
3717 debug_printf ("NtQueryFullAttributesFile (%S) failed, %p",
3718 fn
[n
].ObjectName
, status
);
3722 res
= CompareFileTime ((FILETIME
*) &fnoi
.LastWriteTime
,
3723 (FILETIME
*) last_modified
+ n
) > 0;
3724 last_modified
[n
].QuadPart
= fnoi
.LastWriteTime
.QuadPart
;
3727 paranoid_printf ("fn[%d] %S res %d", n
, fn
[n
].ObjectName
, res
);
3732 etc::dir_changed (int n
)
3734 if (!change_possible
[n
])
3736 static HANDLE changed_h NO_COPY
;
3742 OBJECT_ATTRIBUTES attr
;
3744 path_conv
dir ("/etc");
3745 status
= NtOpenFile (&changed_h
, SYNCHRONIZE
| FILE_LIST_DIRECTORY
,
3746 dir
.get_object_attr (attr
, sec_none_nih
), &io
,
3747 FILE_SHARE_VALID_FLAGS
, FILE_DIRECTORY_FILE
);
3748 if (!NT_SUCCESS (status
))
3751 system_printf ("NtOpenFile (%S) failed, %p",
3752 dir
.get_nt_native_path (), status
);
3754 changed_h
= INVALID_HANDLE_VALUE
;
3758 status
= NtNotifyChangeDirectoryFile (changed_h
, NULL
, NULL
,
3760 FILE_NOTIFY_CHANGE_LAST_WRITE
3761 | FILE_NOTIFY_CHANGE_FILE_NAME
,
3763 if (!NT_SUCCESS (status
))
3766 system_printf ("NtNotifyChangeDirectoryFile (1) failed, %p",
3769 NtClose (changed_h
);
3770 changed_h
= INVALID_HANDLE_VALUE
;
3773 memset (change_possible
, true, sizeof (change_possible
));
3776 if (changed_h
== INVALID_HANDLE_VALUE
)
3777 change_possible
[n
] = true;
3778 else if (WaitForSingleObject (changed_h
, 0) == WAIT_OBJECT_0
)
3780 status
= NtNotifyChangeDirectoryFile (changed_h
, NULL
, NULL
,
3782 FILE_NOTIFY_CHANGE_LAST_WRITE
3783 | FILE_NOTIFY_CHANGE_FILE_NAME
,
3785 if (!NT_SUCCESS (status
))
3788 system_printf ("NtNotifyChangeDirectoryFile (2) failed, %p",
3791 NtClose (changed_h
);
3792 changed_h
= INVALID_HANDLE_VALUE
;
3794 memset (change_possible
, true, sizeof change_possible
);
3798 paranoid_printf ("fn[%d] %S change_possible %d",
3799 n
, fn
[n
].ObjectName
, change_possible
[n
]);
3800 return change_possible
[n
];
3804 etc::file_changed (int n
)
3807 if (dir_changed (n
) && test_file_change (n
))
3809 change_possible
[n
] = false; /* Change is no longer possible */
3810 paranoid_printf ("fn[%d] %S res %d", n
, fn
[n
].ObjectName
, res
);
3814 /* No need to be reentrant or thread-safe according to SUSv3.
3815 / and \\ are treated equally. Leading drive specifiers are
3816 kept intact as far as it makes sense. Everything else is
3817 POSIX compatible. */
3819 basename (char *path
)
3822 char *c
, *d
, *bs
= path
;
3824 if (!path
|| !*path
)
3825 return strcpy (buf
, ".");
3826 if (isalpha (path
[0]) && path
[1] == ':')
3828 else if (strspn (path
, "/\\") > 1)
3830 c
= strrchr (bs
, '/');
3831 if ((d
= strrchr (c
?: bs
, '\\')) > c
)
3835 /* Trailing (back)slashes are eliminated. */
3836 while (c
&& c
> bs
&& c
[1] == '\0')
3839 c
= strrchr (bs
, '/');
3840 if ((d
= strrchr (c
?: bs
, '\\')) > c
)
3843 if (c
&& (c
> bs
|| c
[1]))
3848 stpncpy (buf
, path
, bs
- path
);
3849 stpcpy (buf
+ (bs
- path
), ".");
3855 /* No need to be reentrant or thread-safe according to SUSv3.
3856 / and \\ are treated equally. Leading drive specifiers and
3857 leading double (back)slashes are kept intact as far as it
3858 makes sense. Everything else is POSIX compatible. */
3860 dirname (char *path
)
3863 char *c
, *d
, *bs
= path
;
3865 if (!path
|| !*path
)
3866 return strcpy (buf
, ".");
3867 if (isalpha (path
[0]) && path
[1] == ':')
3869 else if (strspn (path
, "/\\") > 1)
3871 c
= strrchr (bs
, '/');
3872 if ((d
= strrchr (c
?: bs
, '\\')) > c
)
3876 /* Trailing (back)slashes are eliminated. */
3877 while (c
&& c
> bs
&& c
[1] == '\0')
3880 c
= strrchr (bs
, '/');
3881 if ((d
= strrchr (c
?: bs
, '\\')) > c
)
3888 /* More trailing (back)slashes are eliminated. */
3889 while (c
> bs
&& (*c
== '/' || *c
== '\\'))
3897 stpncpy (buf
, path
, bs
- path
);
3898 stpcpy (buf
+ (bs
- path
), ".");