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"
76 bool dos_file_warning
= true;
78 suffix_info stat_suffixes
[] =
81 suffix_info (".exe", 1),
87 char contents
[SYMLINK_MAX
+ 1];
99 int check (char *path
, const suffix_info
*suffixes
, fs_info
&fs
,
100 path_conv_handle
&conv_hdl
);
101 int set (char *path
);
102 bool parse_device (const char *);
103 int check_sysfile (HANDLE h
);
104 int check_shortcut (HANDLE h
);
105 int check_reparse_point (HANDLE h
);
106 int check_nfs_symlink (HANDLE h
);
107 int posixify (char *srcbuf
);
108 bool set_error (int);
111 muto NO_COPY
cwdstuff::cwd_lock
;
113 static const GUID GUID_shortcut
114 = { 0x00021401L
, 0, 0, {0xc0, 0, 0, 0, 0, 0, 0, 0x46}};
117 WSH_FLAG_IDLIST
= 0x01, /* Contains an ITEMIDLIST. */
118 WSH_FLAG_FILE
= 0x02, /* Contains a file locator element. */
119 WSH_FLAG_DESC
= 0x04, /* Contains a description. */
120 WSH_FLAG_RELPATH
= 0x08, /* Contains a relative path. */
121 WSH_FLAG_WD
= 0x10, /* Contains a working dir. */
122 WSH_FLAG_CMDLINE
= 0x20, /* Contains command line args. */
123 WSH_FLAG_ICON
= 0x40 /* Contains a custom icon. */
126 struct win_shortcut_hdr
128 DWORD size
; /* Header size in bytes. Must contain 0x4c. */
129 GUID magic
; /* GUID of shortcut files. */
130 DWORD flags
; /* Content flags. See above. */
132 /* The next fields from attr to icon_no are always set to 0 in Cygwin
133 and U/Win shortcuts. */
134 DWORD attr
; /* Target file attributes. */
135 FILETIME ctime
; /* These filetime items are never touched by the */
136 FILETIME mtime
; /* system, apparently. Values don't matter. */
138 DWORD filesize
; /* Target filesize. */
139 DWORD icon_no
; /* Icon number. */
141 DWORD run
; /* Values defined in winuser.h. Use SW_NORMAL. */
142 DWORD hotkey
; /* Hotkey value. Set to 0. */
143 DWORD dummy
[2]; /* Future extension probably. Always 0. */
146 /* Return non-zero if PATH1 is a prefix of PATH2.
147 Both are assumed to be of the same path style and / vs \ usage.
149 LEN1 = strlen (PATH1). It's passed because often it's already known.
152 /foo/ is a prefix of /foo <-- may seem odd, but desired
153 /foo is a prefix of /foo/
154 / is a prefix of /foo/bar
155 / is not a prefix of foo/bar
156 foo/ is a prefix foo/bar
157 /foo is not a prefix of /foobar
161 path_prefix_p (const char *path1
, const char *path2
, int len1
,
162 bool caseinsensitive
)
164 /* Handle case where PATH1 has trailing '/' and when it doesn't. */
165 if (len1
> 0 && isdirsep (path1
[len1
- 1]))
169 return isdirsep (path2
[0]) && !isdirsep (path2
[1]);
171 if (isdirsep (path2
[len1
]) || path2
[len1
] == 0 || path1
[len1
- 1] == ':')
172 return caseinsensitive
? strncasematch (path1
, path2
, len1
)
173 : !strncmp (path1
, path2
, len1
);
178 /* Return non-zero if paths match in first len chars.
179 Check is dependent of the case sensitivity setting. */
181 pathnmatch (const char *path1
, const char *path2
, int len
, bool caseinsensitive
)
183 return caseinsensitive
184 ? strncasematch (path1
, path2
, len
) : !strncmp (path1
, path2
, len
);
187 /* Return non-zero if paths match. Check is dependent of the case
188 sensitivity setting. */
190 pathmatch (const char *path1
, const char *path2
, bool caseinsensitive
)
192 return caseinsensitive
193 ? strcasematch (path1
, path2
) : !strcmp (path1
, path2
);
196 /* TODO: This function is used in mkdir and rmdir to generate correct
197 error messages in case of paths ending in /. or /.. components.
198 Right now, normalize_posix_path will just normalize
199 those components away, which changes the semantics. */
201 has_dot_last_component (const char *dir
, bool test_dot_dot
)
203 /* SUSv3: . and .. are not allowed as last components in various system
204 calls. Don't test for backslash path separator since that's a Win32
205 path following Win32 rules. */
206 const char *last_comp
= strchr (dir
, '\0');
208 if (last_comp
== dir
)
209 return false; /* Empty string. Probably shouldn't happen here? */
211 /* Detect run of trailing slashes */
212 while (last_comp
> dir
&& *--last_comp
== '/')
215 /* Detect just a run of slashes or a path that does not end with a slash. */
216 if (*last_comp
!= '.')
219 /* We know we have a trailing dot here. Check that it really is a standalone "."
220 path component by checking that it is at the beginning of the string or is
222 if (last_comp
== dir
|| *--last_comp
== '/')
225 /* If we're not checking for '..' we're done. Ditto if we're now pointing to
227 if (!test_dot_dot
|| *last_comp
!= '.')
228 return false; /* either not testing for .. or this was not '..' */
230 /* Repeat previous test for standalone or path component. */
231 return last_comp
== dir
|| last_comp
[-1] == '/';
234 /* Normalize a POSIX path.
235 All duplicate /'s, except for 2 leading /'s, are deleted.
236 The result is 0 for success, or an errno error value. */
239 normalize_posix_path (const char *src
, char *dst
, char *&tail
)
241 const char *in_src
= src
;
242 char *dst_start
= dst
;
243 syscall_printf ("src %s", src
);
245 if ((isdrive (src
) && isdirsep (src
[2])) || *src
== '\\')
249 if (!isslash (src
[0]))
251 if (!cygheap
->cwd
.get (dst
))
253 tail
= strchr (tail
, '\0');
254 if (isslash (dst
[0]) && isslash (dst
[1]))
258 if (tail
== dst_start
+ 1 && *dst_start
== '/')
262 if (tail
> dst
&& !isslash (tail
[-1]))
265 /* Two leading /'s? If so, preserve them. */
266 else if (isslash (src
[1]) && !isslash (src
[2]))
270 /* Is that a //?/ or //./ prefix into the native NT namespace?
271 If so, preserve it. */
272 if ((src
[1] == '.' || src
[1] == '?') && isslash (src
[2]))
284 /* Strip runs of /'s. */
305 if (!isslash (src
[1]))
308 else if (src
[2] && !isslash (src
[2]))
312 while (tail
> dst_start
&& !isslash (*--tail
))
320 if ((tail
- dst
) >= NT_MAX_PATH
)
322 debug_printf ("ENAMETOOLONG = normalize_posix_path (%s)", src
);
330 debug_printf ("%s = normalize_posix_path (%s)", dst
, in_src
);
334 int err
= normalize_win32_path (in_src
, dst
, tail
);
336 for (char *p
= dst
; (p
= strchr (p
, '\\')); p
++)
342 path_conv::add_ext_from_sym (symlink_info
&sym
)
344 if (sym
.ext_here
&& *sym
.ext_here
)
346 known_suffix
= path
+ sym
.extn
;
347 if (sym
.ext_tacked_on
)
348 strcpy ((char *) known_suffix
, sym
.ext_here
);
352 static void __stdcall
mkrelpath (char *dst
, bool caseinsensitive
)
353 __attribute__ ((regparm (2)));
355 static void __stdcall
356 mkrelpath (char *path
, bool caseinsensitive
)
359 char *cwd_win32
= tp
.c_get ();
360 if (!cygheap
->cwd
.get (cwd_win32
, 0))
363 unsigned cwdlen
= strlen (cwd_win32
);
364 if (!path_prefix_p (cwd_win32
, path
, cwdlen
, caseinsensitive
))
367 size_t n
= strlen (path
);
375 tail
+= isdirsep (cwd_win32
[cwdlen
- 1]) ? cwdlen
: cwdlen
+ 1;
377 memmove (path
, tail
, strlen (tail
) + 1);
383 path_conv::set_normalized_path (const char *path_copy
)
387 size_t n
= strlen (path_copy
) + 1;
388 char *p
= (char *) crealloc_abort ((void *) normalized_path
, n
);
389 normalized_path
= (const char *) memcpy (p
, path_copy
, n
);
394 str2uni_cat (UNICODE_STRING
&tgt
, const char *srcstr
)
396 int len
= sys_mbstowcs (tgt
.Buffer
+ tgt
.Length
/ sizeof (WCHAR
),
397 (tgt
.MaximumLength
- tgt
.Length
) / sizeof (WCHAR
),
400 tgt
.Length
+= (len
- 1) * sizeof (WCHAR
);
404 get_nt_native_path (const char *path
, UNICODE_STRING
& upath
, bool dos
)
407 if (path
[0] == '/') /* special path w/o NT path representation. */
408 str2uni_cat (upath
, path
);
409 else if (path
[0] != '\\') /* X:\... or relative path. */
411 if (path
[1] == ':') /* X:\... */
413 RtlAppendUnicodeStringToString (&upath
, &ro_u_natp
);
414 str2uni_cat (upath
, path
);
415 /* The drive letter must be upper case. */
416 upath
.Buffer
[4] = towupper (upath
.Buffer
[4]);
419 str2uni_cat (upath
, path
);
420 transform_chars (&upath
, 7);
422 else if (path
[1] != '\\') /* \Device\... */
423 str2uni_cat (upath
, path
);
424 else if ((path
[2] != '.' && path
[2] != '?')
425 || path
[3] != '\\') /* \\server\share\... */
427 RtlAppendUnicodeStringToString (&upath
, &ro_u_uncp
);
428 str2uni_cat (upath
, path
+ 2);
429 transform_chars (&upath
, 8);
431 else /* \\.\device or \\?\foo */
433 RtlAppendUnicodeStringToString (&upath
, &ro_u_natp
);
434 str2uni_cat (upath
, path
+ 4);
438 /* Unfortunately we can't just use transform_chars with the tfx_rev_chars
439 table since only leading and trainlig spaces and dots are affected.
440 So we step to every backslash and fix surrounding dots and spaces.
441 That makes these broken filesystems a bit slower, but, hey. */
442 PWCHAR cp
= upath
.Buffer
+ 7;
443 PWCHAR cend
= upath
.Buffer
+ upath
.Length
/ sizeof (WCHAR
);
448 while (*ccp
== L
'.' || *ccp
== L
' ')
450 while (cp
[1] == L
' ')
453 while (*--cp
== L
'.' || *cp
== L
' ')
460 path_conv::get_nt_native_path ()
465 uni_path
.MaximumLength
= (strlen (path
) + 10) * sizeof (WCHAR
);
466 wide_path
= (PWCHAR
) cmalloc_abort (HEAP_STR
, uni_path
.MaximumLength
);
467 uni_path
.Buffer
= wide_path
;
468 ::get_nt_native_path (path
, uni_path
, has_dos_filenames_only ());
474 path_conv::get_wide_win32_path (PWCHAR wc
)
476 get_nt_native_path ();
479 wcpcpy (wc
, wide_path
);
486 warn_msdos (const char *src
)
488 if (user_shared
->warned_msdos
|| !dos_file_warning
|| !cygwin_finished_initializing
)
491 char *posix_path
= tp
.c_get ();
492 small_printf ("cygwin warning:\n");
493 if (cygwin_conv_path (CCP_WIN_A_TO_POSIX
| CCP_RELATIVE
, src
,
494 posix_path
, NT_MAX_PATH
))
495 small_printf (" MS-DOS style path detected: %ls\n POSIX equivalent preferred.\n",
498 small_printf (" MS-DOS style path detected: %ls\n Preferred POSIX equivalent is: %ls\n",
500 small_printf (" CYGWIN environment variable option \"nodosfilewarning\" turns off this warning.\n"
501 " Consult the user's guide for more details about POSIX paths:\n"
502 " http://cygwin.com/cygwin-ug-net/using.html#using-pathnames\n");
503 user_shared
->warned_msdos
= true;
507 getfileattr (const char *path
, bool caseinsensitive
) /* path has to be always absolute. */
510 UNICODE_STRING upath
;
511 OBJECT_ATTRIBUTES attr
;
512 FILE_BASIC_INFORMATION fbi
;
517 InitializeObjectAttributes (&attr
, &upath
,
518 caseinsensitive
? OBJ_CASE_INSENSITIVE
: 0,
520 get_nt_native_path (path
, upath
, false);
522 status
= NtQueryAttributesFile (&attr
, &fbi
);
523 if (NT_SUCCESS (status
))
524 return fbi
.FileAttributes
;
526 if (status
!= STATUS_OBJECT_NAME_NOT_FOUND
527 && status
!= STATUS_NO_SUCH_FILE
) /* File not found on 9x share */
529 /* File exists but access denied. Try to get attribute through
531 UNICODE_STRING dirname
, basename
;
533 FILE_BOTH_DIRECTORY_INFORMATION fdi
;
535 RtlSplitUnicodePath (&upath
, &dirname
, &basename
);
536 InitializeObjectAttributes (&attr
, &dirname
,
537 caseinsensitive
? OBJ_CASE_INSENSITIVE
: 0,
539 status
= NtOpenFile (&dir
, SYNCHRONIZE
| FILE_LIST_DIRECTORY
,
540 &attr
, &io
, FILE_SHARE_VALID_FLAGS
,
541 FILE_SYNCHRONOUS_IO_NONALERT
542 | FILE_OPEN_FOR_BACKUP_INTENT
543 | FILE_DIRECTORY_FILE
);
544 if (NT_SUCCESS (status
))
546 status
= NtQueryDirectoryFile (dir
, NULL
, NULL
, 0, &io
,
548 FileBothDirectoryInformation
,
549 TRUE
, &basename
, TRUE
);
551 if (NT_SUCCESS (status
) || status
== STATUS_BUFFER_OVERFLOW
)
552 return fdi
.FileAttributes
;
555 SetLastError (RtlNtStatusToDosError (status
));
556 return INVALID_FILE_ATTRIBUTES
;
559 /* Convert an arbitrary path SRC to a pure Win32 path, suitable for
560 passing to Win32 API routines.
562 If an error occurs, `error' is set to the errno value.
563 Otherwise it is set to 0.
566 SYMLINK_FOLLOW - convert to PATH symlink points to
567 SYMLINK_NOFOLLOW - convert to PATH of symlink itself
568 SYMLINK_IGNORE - do not check PATH for symlinks
569 SYMLINK_CONTENTS - just return symlink contents
572 /* TODO: This implementation is only preliminary. For internal
573 purposes it's necessary to have a path_conv::check function which
574 takes a UNICODE_STRING src path, otherwise we waste a lot of time
575 for converting back and forth. The below implementation does
576 realy nothing but converting to char *, until path_conv handles
577 wide-char paths directly. */
579 path_conv::check (const UNICODE_STRING
*src
, unsigned opt
,
580 const suffix_info
*suffixes
)
583 char *path
= tp
.c_get ();
585 user_shared
->warned_msdos
= true;
586 sys_wcstombs (path
, NT_MAX_PATH
, src
->Buffer
, src
->Length
/ sizeof (WCHAR
));
587 path_conv::check (path
, opt
, suffixes
);
591 path_conv::check (const char *src
, unsigned opt
,
592 const suffix_info
*suffixes
)
594 /* The tmp_buf array is used when expanding symlinks. It is NT_MAX_PATH * 2
595 in length so that we can hold the expanded symlink plus a trailer. */
597 char *path_copy
= tp
.c_get ();
598 char *pathbuf
= tp
.c_get ();
599 char *tmp_buf
= tp
.t_get ();
600 char *THIS_path
= tp
.c_get ();
602 bool need_directory
= 0;
603 bool saw_symlinks
= 0;
604 bool add_ext
= false;
606 char *tail
, *path_end
;
609 static path_conv last_path_conv
;
610 static char last_src
[CYG_MAX_PATH
];
612 if (*last_src
&& strcmp (last_src
, src
) == 0)
614 *this = last_path_conv
;
620 if (efault
.faulted ())
628 fileattr
= INVALID_FILE_ATTRIBUTES
;
629 caseinsensitive
= OBJ_CASE_INSENSITIVE
;
635 cfree (modifiable_path ());
638 close_conv_handle ();
639 memset (&dev
, 0, sizeof (dev
));
643 cfree ((void *) normalized_path
);
644 normalized_path
= NULL
;
646 int component
= 0; // Number of translated components
648 if (!(opt
& PC_NULLEMPTY
))
656 bool is_msdos
= false;
657 /* This loop handles symlink expansion. */
663 is_relpath
= !isabspath (src
);
664 error
= normalize_posix_path (src
, path_copy
, tail
);
674 /* Detect if the user was looking for a directory. We have to strip the
675 trailing slash initially while trying to add extensions but take it
676 into account during processing */
677 if (tail
> path_copy
+ 2 && isslash (tail
[-1]))
684 /* Scan path_copy from right to left looking either for a symlink
685 or an actual existing file. If an existing file is found, just
686 return. If a symlink is found, exit the for loop.
687 Also: be careful to preserve the errno returned from
688 symlink.check as the caller may need it. */
689 /* FIXME: Do we have to worry about multiple \'s here? */
690 component
= 0; // Number of translated components
691 sym
.contents
[0] = '\0';
695 for (unsigned pflags_or
= opt
& (PC_NO_ACCESS_CHECK
| PC_KEEP_HANDLE
);
699 const suffix_info
*suff
;
702 /* Don't allow symlink.check to set anything in the path_conv
703 class if we're working on an inner component of the path */
712 full_path
= THIS_path
;
715 /* Convert to native path spec sans symbolic link info. */
716 error
= mount_table
->conv_to_win32_path (path_copy
, full_path
, dev
,
722 sym
.pflags
|= pflags_or
;
724 if (dev
.major
== DEV_CYGDRIVE_MAJOR
)
727 fileattr
= FILE_ATTRIBUTE_DIRECTORY
| FILE_ATTRIBUTE_READONLY
;
730 fileattr
= getfileattr (THIS_path
,
731 sym
.pflags
& MOUNT_NOPOSIX
);
736 else if (dev
== FH_DEV
)
740 fileattr
= getfileattr (THIS_path
, sym
.pflags
& MOUNT_NOPOSIX
);
741 if (!component
&& fileattr
== INVALID_FILE_ATTRIBUTES
)
743 fileattr
= FILE_ATTRIBUTE_DIRECTORY
| FILE_ATTRIBUTE_READONLY
;
748 else if (isvirtual_dev (dev
.devn
))
750 /* FIXME: Calling build_fhandler here is not the right way to handle this. */
751 fhandler_virtual
*fh
= (fhandler_virtual
*) build_fh_dev (dev
, path_copy
);
752 virtual_ftype_t file_type
= fh
->exists ();
753 if (file_type
== virt_symlink
)
756 symlen
= sym
.set (fh
->get_filebuf ());
764 fileattr
= FILE_ATTRIBUTE_DIRECTORY
;
771 goto is_virtual_symlink
;
788 /* Access to real file or directory via block device
789 entry in /proc/sys. Convert to real file and go with
792 goto is_fs_via_procsys
;
794 /* Block special device. If the trailing slash has been
795 requested, the target is the root directory of the
796 filesystem on this block device. So we convert this to
797 a real file and attach the backslash. */
798 if (component
|| need_directory
)
803 strcat (full_path
, "\\");
804 fileattr
= FILE_ATTRIBUTE_DIRECTORY
805 | FILE_ATTRIBUTE_DEVICE
;
814 fileattr
= FILE_ATTRIBUTE_DEVICE
;
818 fileattr
= INVALID_FILE_ATTRIBUTES
;
819 goto virtual_component_retry
;
821 if (component
== 0 || dev
.devn
!= FH_NETDRIVE
)
822 path_flags
|= PATH_RO
;
825 /* devn should not be a device. If it is, then stop parsing now. */
826 else if (dev
.devn
!= FH_FS
)
829 path_flags
= sym
.pflags
;
835 goto out
; /* Found a device. Stop parsing. */
838 /* If path is only a drivename, Windows interprets it as the
839 current working directory on this drive instead of the root
840 dir which is what we want. So we need the trailing backslash
842 if (full_path
[0] && full_path
[1] == ':' && full_path
[2] == '\0')
847 /* Otherwise, if the user requires a directory and explicitely
848 specified a path into the native NT namespace, add the trailing
849 backslash. It's needed to access the root dir. */
850 else if (need_directory
851 && full_path
[0] == '\\' && full_path
[1] == '\\'
852 && (full_path
[2] == '.' || full_path
[2] == '?'))
853 strcat (full_path
, "\\");
855 /* If the incoming path was given in DOS notation, always treat
856 it as caseinsensitive,noacl path. This must be set before
857 calling sym.check, otherwise the path is potentially treated
860 sym
.pflags
|= PATH_NOPOSIX
| PATH_NOACL
;
864 symlen
= sym
.check (full_path
, suff
, fs
, conv_handle
);
875 dev
.parse (sym
.major
, sym
.minor
);
878 fileattr
= sym
.fileattr
;
882 if (sym
.pflags
& PATH_SOCKET
)
889 fileattr
= sym
.fileattr
;
897 fileattr
= sym
.fileattr
;
898 path_flags
= sym
.pflags
;
901 /* If symlink.check found an existing non-symlink file, then
902 it sets the appropriate flag. It also sets any suffix found
904 if (!sym
.issymlink
&& sym
.fileattr
!= INVALID_FILE_ATTRIBUTES
)
909 else if (!(sym
.fileattr
& FILE_ATTRIBUTE_DIRECTORY
))
914 goto out
; // file found
916 /* Found a symlink if symlen > 0. If component == 0, then the
917 src path itself was a symlink. If !follow_mode then
918 we're done. Otherwise we have to insert the path found
919 into the full path that we are building and perform all of
920 these operations again on the newly derived path. */
924 if (component
== 0 && !need_directory
&& !(opt
& PC_SYM_FOLLOW
))
926 set_symlink (symlen
); // last component of path is a symlink.
927 if (opt
& PC_SYM_CONTENTS
)
929 strcpy (THIS_path
, sym
.contents
);
935 /* Following a symlink we can't trust the collected filesystem
936 information any longer. */
938 /* Close handle, if we have any. Otherwise we're collecting
939 handles while following symlinks. */
940 conv_handle
.close ();
943 else if (sym
.error
&& sym
.error
!= ENOENT
)
948 /* No existing file found. */
950 virtual_component_retry
:
951 /* Find the new "tail" of the path, e.g. in '/for/bar/baz',
953 if (tail
!= path_end
)
955 while (--tail
> path_copy
+ 1 && *tail
!= '/') {}
956 /* Exit loop if there is no tail or we are at the
957 beginning of a UNC path */
958 if (tail
<= path_copy
+ 1)
959 goto out
; // all done
961 /* Haven't found an existing pathname component yet.
962 Pinch off the tail and try again. */
967 /* Arrive here if above loop detected a symlink. */
968 if (++loop
> SYMLOOP_MAX
)
970 error
= ELOOP
; // Eep.
977 /* Place the link content, possibly with head and/or tail, in tmp_buf */
980 if (isabspath (sym
.contents
))
981 headptr
= tmp_buf
; /* absolute path */
984 /* Copy the first part of the path (with ending /) and point to the end. */
985 char *prevtail
= tail
;
986 while (--prevtail
> path_copy
&& *prevtail
!= '/') {}
987 int headlen
= prevtail
- path_copy
+ 1;;
988 memcpy (tmp_buf
, path_copy
, headlen
);
989 headptr
= &tmp_buf
[headlen
];
992 /* Make sure there is enough space */
993 if (headptr
+ symlen
>= tmp_buf
+ (2 * NT_MAX_PATH
))
996 error
= ENAMETOOLONG
;
997 set_path ("::ENAMETOOLONG::");
1001 /* Copy the symlink contents to the end of tmp_buf.
1003 for (char *p
= sym
.contents
; *p
; p
++)
1004 *headptr
++ = *p
== '\\' ? '/' : *p
;
1007 /* Copy any tail component (with the 0) */
1008 if (tail
++ < path_end
)
1010 /* Add a slash if needed. There is space. */
1011 if (*(headptr
- 1) != '/')
1013 int taillen
= path_end
- tail
+ 1;
1014 if (headptr
+ taillen
> tmp_buf
+ (2 * NT_MAX_PATH
))
1016 memcpy (headptr
, tail
, taillen
);
1019 /* Evaluate everything all over again. */
1023 if (!(opt
& PC_SYM_CONTENTS
))
1027 set_path (THIS_path
);
1029 add_ext_from_sym (sym
);
1030 if (dev
.devn
== FH_NETDRIVE
&& component
)
1032 /* This case indicates a non-existant resp. a non-retrievable
1033 share. This happens for instance if the share is a printer.
1034 In this case the path must not be treated like a FH_NETDRIVE,
1035 but like a FH_FS instead, so the usual open call for files
1039 else if (isvirtual_dev (dev
.devn
) && fileattr
== INVALID_FILE_ATTRIBUTES
)
1044 else if (!need_directory
|| error
)
1045 /* nothing to do */;
1046 else if (fileattr
== INVALID_FILE_ATTRIBUTES
)
1047 strcat (modifiable_path (), "\\"); /* Reattach trailing dirsep in native path. */
1048 else if (fileattr
& FILE_ATTRIBUTE_DIRECTORY
)
1049 path_flags
&= ~PATH_SYMLINK
;
1052 debug_printf ("%s is a non-directory", path
);
1059 if (strncmp (path
, "\\\\.\\", 4))
1061 if (!tail
|| tail
== path
)
1063 else if (tail
[-1] != '\\')
1072 /* If FS hasn't been checked already in symlink_info::check, do so now. */
1073 if (fs
.inited ()|| fs
.update (get_nt_native_path (), NULL
))
1075 /* Incoming DOS paths are treated like DOS paths in native
1076 Windows applications. No ACLs, just default settings. */
1078 fs
.has_acls (false);
1079 debug_printf ("this->path(%s), has_acls(%d)", path
, fs
.has_acls ());
1080 /* CV: We could use this->has_acls() but I want to make sure that
1081 we don't forget that the PATH_NOACL flag must be taken into
1083 if (!(path_flags
& PATH_NOACL
) && fs
.has_acls ())
1084 set_exec (0); /* We really don't know if this is executable or not here
1085 but set it to not executable since it will be figured out
1086 later by anything which cares about this. */
1088 /* If the FS has been found to have unrelibale inodes, note
1089 that in path_flags. */
1090 if (!fs
.hasgood_inode ())
1091 path_flags
|= PATH_IHASH
;
1092 /* If the OS is caseinsensitive or the FS is caseinsensitive,
1093 don't handle path casesensitive. */
1094 if (cygwin_shared
->obcaseinsensitive
|| fs
.caseinsensitive ())
1095 path_flags
|= PATH_NOPOSIX
;
1096 caseinsensitive
= (path_flags
& PATH_NOPOSIX
)
1097 ? OBJ_CASE_INSENSITIVE
: 0;
1098 if (exec_state () != dont_know_if_executable
)
1102 else if (issymlink () || issocket ())
1106 if (opt
& PC_NOFULL
)
1110 mkrelpath (this->modifiable_path (), !!caseinsensitive
);
1111 /* Invalidate wide_path so that wide relpath can be created
1112 in later calls to get_nt_native_path or get_wide_win32_path. */
1119 size_t n
= strlen (this->path
);
1120 /* Do not add trailing \ to UNC device names like \\.\a: */
1121 if (this->path
[n
- 1] != '\\' &&
1122 (strncmp (this->path
, "\\\\.\\", 4) != 0))
1124 this->modifiable_path ()[n
] = '\\';
1125 this->modifiable_path ()[n
+ 1] = '\0';
1131 set_has_symlinks ();
1133 if ((opt
& PC_POSIX
))
1135 if (tail
< path_end
&& tail
> path_copy
+ 1)
1137 set_normalized_path (path_copy
);
1138 if (is_msdos
&& !(opt
& PC_NOWARN
))
1145 last_path_conv
= *this;
1146 strcpy (last_src
, src
);
1151 path_conv::~path_conv ()
1153 if (normalized_path
)
1155 cfree ((void *) normalized_path
);
1156 normalized_path
= NULL
;
1160 cfree (modifiable_path ());
1168 close_conv_handle ();
1172 path_conv::is_binary ()
1175 PWCHAR bintest
= tp
.w_get ();
1178 return GetBinaryTypeW (get_wide_win32_path (bintest
), &bin
)
1179 && (bin
== SCS_32BIT_BINARY
|| bin
== SCS_64BIT_BINARY
);
1182 /* Normalize a Win32 path.
1183 /'s are converted to \'s in the process.
1184 All duplicate \'s, except for 2 leading \'s, are deleted.
1186 The result is 0 for success, or an errno error value.
1187 FIXME: A lot of this should be mergeable with the POSIX critter. */
1189 normalize_win32_path (const char *src
, char *dst
, char *&tail
)
1191 const char *src_start
= src
;
1192 bool beg_src_slash
= isdirsep (src
[0]);
1195 /* Skip long path name prefixes in Win32 or NT syntax. */
1196 if (beg_src_slash
&& (src
[1] == '?' || isdirsep (src
[1]))
1197 && src
[2] == '?' && isdirsep (src
[3]))
1200 if (src
[1] != ':') /* native UNC path */
1201 src
+= 2; /* Fortunately the first char is not copied... */
1203 beg_src_slash
= false;
1205 if (beg_src_slash
&& isdirsep (src
[1]))
1207 if (isdirsep (src
[2]))
1209 /* More than two slashes are just folded into one. */
1211 while (isdirsep (src
[1]))
1216 /* Two slashes start a network or device path. */
1219 if (src
[1] == '.' && isdirsep (src
[2]))
1230 /* Always convert drive letter to uppercase for case sensitivity. */
1231 *tail
++ = cyg_toupper (*src
++);
1232 else if (*src
!= '/')
1235 tail
+= cygheap
->cwd
.get_drive (dst
);
1236 else if (!cygheap
->cwd
.get (dst
, 0))
1237 return get_errno ();
1240 tail
= strchr (tail
, '\0');
1241 if (tail
[-1] != '\\')
1249 /* Strip duplicate /'s. */
1250 if (isdirsep (src
[0]) && isdirsep (src
[1]))
1253 else if (src
[0] == '.' && isdirsep (src
[1])
1254 && (src
== src_start
|| isdirsep (src
[-1])))
1257 /* Backup if "..". */
1258 else if (src
[0] == '.' && src
[1] == '.'
1259 /* dst must be greater than dst_start */
1260 && tail
[-1] == '\\')
1262 if (!isdirsep (src
[2]) && src
[2] != '\0')
1266 /* Back up over /, but not if it's the first one. */
1269 /* Now back up to the next /. */
1270 while (tail
> dst
+ 1 && tail
[-1] != '\\' && tail
[-2] != ':')
1273 if (isdirsep (*src
))
1277 /* Otherwise, add char to result. */
1286 if ((tail
- dst
) >= NT_MAX_PATH
)
1287 return ENAMETOOLONG
;
1289 if (tail
> dst
+ 1 && tail
[-1] == '.' && tail
[-2] == '\\')
1292 debug_printf ("%s = normalize_win32_path (%s)", dst
, src_start
);
1296 /* Various utilities. */
1298 /* nofinalslash: Remove trailing / and \ from SRC (except for the
1299 first one). It is ok for src == dst. */
1302 nofinalslash (const char *src
, char *dst
)
1304 int len
= strlen (src
);
1306 memcpy (dst
, src
, len
+ 1);
1307 while (len
> 1 && isdirsep (dst
[--len
]))
1311 /* conv_path_list: Convert a list of path names to/from Win32/POSIX. */
1314 conv_path_list (const char *src
, char *dst
, size_t size
, int to_posix
)
1317 char src_delim
, dst_delim
;
1318 cygwin_conv_path_t conv_fn
;
1325 conv_fn
= CCP_WIN_A_TO_POSIX
| CCP_RELATIVE
;
1331 conv_fn
= CCP_POSIX_TO_WIN_A
| CCP_RELATIVE
;
1335 len
= strlen (src
) + 1;
1336 if (len
<= NT_MAX_PATH
* sizeof (WCHAR
))
1337 srcbuf
= (char *) tp
.w_get ();
1339 srcbuf
= (char *) alloca (len
);
1343 bool saw_empty
= false;
1346 char *s
= strccpy (srcbuf
, &src
, src_delim
);
1347 size_t len
= s
- srcbuf
;
1348 if (len
>= NT_MAX_PATH
)
1356 err
= cygwin_conv_path (conv_fn
, srcbuf
, d
, size
- (d
- dst
));
1361 err
= cygwin_conv_path (conv_fn
, ".", d
, size
- (d
- dst
));
1365 if (to_posix
== ENV_CVT
)
1371 d
= strchr (d
, '\0');
1385 /********************** Symbolic Link Support **************************/
1387 /* Create a symlink from FROMPATH to TOPATH. */
1389 /* If TRUE create symlinks as Windows shortcuts, if false create symlinks
1390 as normal files with magic number and system bit set. */
1391 bool allow_winsymlinks
= false;
1394 symlink (const char *oldpath
, const char *newpath
)
1396 return symlink_worker (oldpath
, newpath
, allow_winsymlinks
, false);
1400 symlink_worker (const char *oldpath
, const char *newpath
, bool use_winsym
,
1405 path_conv win32_newpath
, win32_oldpath
;
1407 SECURITY_ATTRIBUTES sa
= sec_none_nih
;
1408 OBJECT_ATTRIBUTES attr
;
1414 bool mk_winsym
= use_winsym
;
1415 bool has_trailing_dirsep
= false;
1417 /* POSIX says that empty 'newpath' is invalid input while empty
1418 'oldpath' is valid -- it's symlink resolver job to verify if
1419 symlink contents point to existing filesystem object */
1421 if (efault
.faulted (EFAULT
))
1423 if (!*oldpath
|| !*newpath
)
1429 if (strlen (oldpath
) > SYMLINK_MAX
)
1431 set_errno (ENAMETOOLONG
);
1435 /* Trailing dirsep is a no-no. */
1436 len
= strlen (newpath
);
1437 has_trailing_dirsep
= isdirsep (newpath
[len
- 1]);
1438 if (has_trailing_dirsep
)
1440 newpath
= strdup (newpath
);
1441 ((char *) newpath
)[len
- 1] = '\0';
1444 check_opt
= PC_SYM_NOFOLLOW
| PC_POSIX
| (isdevice
? PC_NOWARN
: 0);
1445 /* We need the normalized full path below. */
1446 win32_newpath
.check (newpath
, check_opt
, stat_suffixes
);
1447 /* MVFS doesn't handle the SYSTEM DOS attribute, but it handles the R/O
1448 attribute. Therefore we create symlinks on MVFS always as shortcuts. */
1449 mk_winsym
|= win32_newpath
.fs_is_mvfs ();
1451 if (mk_winsym
&& !win32_newpath
.exists ()
1452 && (isdevice
|| !win32_newpath
.fs_is_nfs ()))
1454 char *newplnk
= tp
.c_get ();
1455 stpcpy (stpcpy (newplnk
, newpath
), ".lnk");
1456 win32_newpath
.check (newplnk
, check_opt
);
1459 if (win32_newpath
.error
)
1461 set_errno (win32_newpath
.error
);
1465 syscall_printf ("symlink (%s, %S)", oldpath
,
1466 win32_newpath
.get_nt_native_path ());
1468 if ((!isdevice
&& win32_newpath
.exists ())
1469 || win32_newpath
.is_auto_device ())
1474 if (has_trailing_dirsep
&& !win32_newpath
.exists ())
1480 if (!isdevice
&& win32_newpath
.fs_is_nfs ())
1482 /* On NFS, create symlinks by calling NtCreateFile with an EA of type
1483 NfsSymlinkTargetName containing ... the symlink target name. */
1484 PFILE_FULL_EA_INFORMATION pffei
= (PFILE_FULL_EA_INFORMATION
) tp
.w_get ();
1485 pffei
->NextEntryOffset
= 0;
1487 pffei
->EaNameLength
= sizeof (NFS_SYML_TARGET
) - 1;
1488 char *EaValue
= stpcpy (pffei
->EaName
, NFS_SYML_TARGET
) + 1;
1489 pffei
->EaValueLength
= sizeof (WCHAR
) *
1490 (sys_mbstowcs ((PWCHAR
) EaValue
, NT_MAX_PATH
, oldpath
) - 1);
1491 status
= NtCreateFile (&fh
, FILE_WRITE_DATA
| FILE_WRITE_EA
| SYNCHRONIZE
,
1492 win32_newpath
.get_object_attr (attr
, sa
),
1493 &io
, NULL
, FILE_ATTRIBUTE_SYSTEM
,
1494 FILE_SHARE_VALID_FLAGS
, FILE_CREATE
,
1495 FILE_SYNCHRONOUS_IO_NONALERT
1496 | FILE_OPEN_FOR_BACKUP_INTENT
,
1497 pffei
, NT_MAX_PATH
* sizeof (WCHAR
));
1498 if (!NT_SUCCESS (status
))
1500 __seterrno_from_nt_status (status
);
1510 ITEMIDLIST
*pidl
= NULL
;
1511 size_t full_len
= 0;
1512 unsigned short oldpath_len
, desc_len
, relpath_len
, pidl_len
= 0;
1513 char desc
[MAX_PATH
+ 1], *relpath
;
1517 /* First create an IDLIST to learn how big our shortcut is
1521 /* The symlink target is relative to the directory in which
1522 the symlink gets created, not relative to the cwd. Therefore
1523 we have to mangle the path quite a bit before calling path_conv. */
1524 if (isabspath (oldpath
))
1525 win32_oldpath
.check (oldpath
,
1530 len
= strrchr (win32_newpath
.normalized_path
, '/')
1531 - win32_newpath
.normalized_path
+ 1;
1532 char *absoldpath
= tp
.t_get ();
1533 stpcpy (stpncpy (absoldpath
, win32_newpath
.normalized_path
, len
),
1535 win32_oldpath
.check (absoldpath
, PC_SYM_NOFOLLOW
, stat_suffixes
);
1537 if (SUCCEEDED (SHGetDesktopFolder (&psl
)))
1539 WCHAR wc_path
[win32_oldpath
.get_wide_win32_path_len () + 1];
1540 win32_oldpath
.get_wide_win32_path (wc_path
);
1541 /* Amazing but true: Even though the ParseDisplayName method
1542 takes a wide char path name, it does not understand the
1543 Win32 prefix for long pathnames! So we have to tack off
1544 the prefix and convert the path to the "normal" syntax
1545 for ParseDisplayName. */
1546 WCHAR
*wc
= wc_path
+ 4;
1547 if (wc
[1] != L
':') /* native UNC path */
1550 if (SUCCEEDED (res
= psl
->ParseDisplayName (NULL
, NULL
, wc
, NULL
,
1555 for (p
= pidl
; p
->mkid
.cb
> 0;
1556 p
= (ITEMIDLIST
*)((char *) p
+ p
->mkid
.cb
))
1558 pidl_len
= (char *) p
- (char *) pidl
+ 2;
1563 /* Compute size of shortcut file. */
1564 full_len
= sizeof (win_shortcut_hdr
);
1566 full_len
+= sizeof (unsigned short) + pidl_len
;
1567 oldpath_len
= strlen (oldpath
);
1568 /* Unfortunately the length of the description is restricted to a
1569 length of MAX_PATH up to NT4, and to a length of 2000 bytes
1570 since W2K. We don't want to add considerations for the different
1571 lengths and even 2000 bytes is not enough for long path names.
1572 So what we do here is to set the description to the POSIX path
1573 only if the path is not longer than MAX_PATH characters. We
1574 append the full path name after the regular shortcut data
1575 (see below), which works fine with Windows Explorer as well
1576 as older Cygwin versions (as long as the whole file isn't bigger
1577 than 8K). The description field is only used for backward
1578 compatibility to older Cygwin versions and those versions are
1579 not capable of handling long path names anyway. */
1580 desc_len
= stpcpy (desc
, oldpath_len
> MAX_PATH
1581 ? "[path too long]" : oldpath
) - desc
;
1582 full_len
+= sizeof (unsigned short) + desc_len
;
1583 /* Devices get the oldpath string unchanged as relative path. */
1586 relpath_len
= oldpath_len
;
1587 stpcpy (relpath
= tp
.c_get (), oldpath
);
1591 relpath_len
= strlen (win32_oldpath
.get_win32 ());
1592 stpcpy (relpath
= tp
.c_get (), win32_oldpath
.get_win32 ());
1594 full_len
+= sizeof (unsigned short) + relpath_len
;
1595 full_len
+= sizeof (unsigned short) + oldpath_len
;
1596 /* 1 byte more for trailing 0 written by stpcpy. */
1597 if (full_len
< NT_MAX_PATH
* sizeof (WCHAR
))
1598 buf
= (char *) tp
.w_get ();
1600 buf
= (char *) alloca (full_len
+ 1);
1602 /* Create shortcut header */
1603 win_shortcut_hdr
*shortcut_header
= (win_shortcut_hdr
*) buf
;
1604 memset (shortcut_header
, 0, sizeof *shortcut_header
);
1605 shortcut_header
->size
= sizeof *shortcut_header
;
1606 shortcut_header
->magic
= GUID_shortcut
;
1607 shortcut_header
->flags
= (WSH_FLAG_DESC
| WSH_FLAG_RELPATH
);
1609 shortcut_header
->flags
|= WSH_FLAG_IDLIST
;
1610 shortcut_header
->run
= SW_NORMAL
;
1611 cp
= buf
+ sizeof (win_shortcut_hdr
);
1616 *(unsigned short *)cp
= pidl_len
;
1617 memcpy (cp
+= 2, pidl
, pidl_len
);
1619 CoTaskMemFree (pidl
);
1622 /* Create description */
1623 *(unsigned short *)cp
= desc_len
;
1624 cp
= stpcpy (cp
+= 2, desc
);
1626 /* Create relpath */
1627 *(unsigned short *)cp
= relpath_len
;
1628 cp
= stpcpy (cp
+= 2, relpath
);
1630 /* Append the POSIX path after the regular shortcut data for
1631 the long path support. */
1632 unsigned short *plen
= (unsigned short *) cp
;
1634 *(PWCHAR
) cp
= 0xfeff; /* BOM */
1636 *plen
= sys_mbstowcs ((PWCHAR
) cp
, NT_MAX_PATH
, oldpath
) * sizeof (WCHAR
);
1641 /* Default technique creating a symlink. */
1642 buf
= (char *) tp
.w_get ();
1643 cp
= stpcpy (buf
, SYMLINK_COOKIE
);
1644 *(PWCHAR
) cp
= 0xfeff; /* BOM */
1646 /* Note that the terminating nul is written. */
1647 cp
+= sys_mbstowcs ((PWCHAR
) cp
, NT_MAX_PATH
, oldpath
) * sizeof (WCHAR
);
1650 if (isdevice
&& win32_newpath
.exists ())
1652 status
= NtOpenFile (&fh
, FILE_WRITE_ATTRIBUTES
,
1653 win32_newpath
.get_object_attr (attr
, sa
),
1654 &io
, 0, FILE_OPEN_FOR_BACKUP_INTENT
);
1655 if (!NT_SUCCESS (status
))
1657 __seterrno_from_nt_status (status
);
1660 status
= NtSetAttributesFile (fh
, FILE_ATTRIBUTE_NORMAL
);
1662 if (!NT_SUCCESS (status
))
1664 __seterrno_from_nt_status (status
);
1668 status
= NtCreateFile (&fh
, DELETE
| FILE_GENERIC_WRITE
,
1669 win32_newpath
.get_object_attr (attr
, sa
),
1670 &io
, NULL
, FILE_ATTRIBUTE_NORMAL
,
1671 FILE_SHARE_VALID_FLAGS
,
1672 isdevice
? FILE_OVERWRITE_IF
: FILE_CREATE
,
1673 FILE_SYNCHRONOUS_IO_NONALERT
1674 | FILE_NON_DIRECTORY_FILE
1675 | FILE_OPEN_FOR_BACKUP_INTENT
,
1677 if (!NT_SUCCESS (status
))
1679 __seterrno_from_nt_status (status
);
1682 if (win32_newpath
.has_acls ())
1683 set_file_attribute (fh
, win32_newpath
, ILLEGAL_UID
, ILLEGAL_GID
,
1684 (io
.Information
== FILE_CREATED
? S_JUSTCREATED
: 0)
1685 | S_IFLNK
| STD_RBITS
| STD_WBITS
);
1686 status
= NtWriteFile (fh
, NULL
, NULL
, NULL
, &io
, buf
, cp
- buf
, NULL
, NULL
);
1687 if (NT_SUCCESS (status
) && io
.Information
== (ULONG
) (cp
- buf
))
1689 status
= NtSetAttributesFile (fh
, mk_winsym
? FILE_ATTRIBUTE_READONLY
1690 : FILE_ATTRIBUTE_SYSTEM
);
1691 if (!NT_SUCCESS (status
))
1692 debug_printf ("Setting attributes failed, status = %p", status
);
1697 __seterrno_from_nt_status (status
);
1698 FILE_DISPOSITION_INFORMATION fdi
= { TRUE
};
1699 status
= NtSetInformationFile (fh
, &io
, &fdi
, sizeof fdi
,
1700 FileDispositionInformation
);
1701 if (!NT_SUCCESS (status
))
1702 debug_printf ("Setting delete dispostion failed, status = %p", status
);
1707 syscall_printf ("%d = symlink_worker (%s, %s, %d, %d)", res
, oldpath
,
1708 newpath
, mk_winsym
, isdevice
);
1709 if (has_trailing_dirsep
)
1710 free ((void *) newpath
);
1715 cmp_shortcut_header (win_shortcut_hdr
*file_header
)
1717 /* A Cygwin or U/Win shortcut only contains a description and a relpath.
1718 Cygwin shortcuts also might contain an ITEMIDLIST. The run type is
1719 always set to SW_NORMAL. */
1720 return file_header
->size
== sizeof (win_shortcut_hdr
)
1721 && !memcmp (&file_header
->magic
, &GUID_shortcut
, sizeof GUID_shortcut
)
1722 && (file_header
->flags
& ~WSH_FLAG_IDLIST
)
1723 == (WSH_FLAG_DESC
| WSH_FLAG_RELPATH
)
1724 && file_header
->run
== SW_NORMAL
;
1728 symlink_info::check_shortcut (HANDLE h
)
1731 win_shortcut_hdr
*file_header
;
1737 FILE_STANDARD_INFORMATION fsi
;
1738 LARGE_INTEGER off
= { QuadPart
:0LL };
1740 status
= NtQueryInformationFile (h
, &io
, &fsi
, sizeof fsi
,
1741 FileStandardInformation
);
1742 if (!NT_SUCCESS (status
))
1747 if (fsi
.EndOfFile
.QuadPart
<= sizeof (win_shortcut_hdr
)
1748 || fsi
.EndOfFile
.QuadPart
> 4 * 65536)
1750 if (fsi
.EndOfFile
.LowPart
< NT_MAX_PATH
* sizeof (WCHAR
))
1751 buf
= (char *) tp
.w_get ();
1753 buf
= (char *) alloca (fsi
.EndOfFile
.LowPart
+ 1);
1754 status
= NtReadFile (h
, NULL
, NULL
, NULL
, &io
, buf
, fsi
.EndOfFile
.LowPart
,
1756 status
= wait_pending (status
, h
, io
);
1757 if (!NT_SUCCESS (status
))
1759 if (status
!= STATUS_END_OF_FILE
)
1763 file_header
= (win_shortcut_hdr
*) buf
;
1764 if (io
.Information
!= fsi
.EndOfFile
.LowPart
1765 || !cmp_shortcut_header (file_header
))
1767 cp
= buf
+ sizeof (win_shortcut_hdr
);
1768 if (file_header
->flags
& WSH_FLAG_IDLIST
) /* Skip ITEMIDLIST */
1769 cp
+= *(unsigned short *) cp
+ 2;
1770 if (!(len
= *(unsigned short *) cp
))
1773 /* Check if this is a device file - these start with the sequence :\\ */
1774 if (strncmp (cp
, ":\\", 2) == 0)
1775 res
= strlen (strcpy (contents
, cp
)); /* Don't mess with device files */
1778 /* Has appended full path? If so, use it instead of description. */
1779 unsigned short relpath_len
= *(unsigned short *) (cp
+ len
);
1780 if (cp
+ len
+ 2 + relpath_len
< buf
+ fsi
.EndOfFile
.LowPart
)
1782 cp
+= len
+ 2 + relpath_len
;
1783 len
= *(unsigned short *) cp
;
1786 if (*(PWCHAR
) cp
== 0xfeff) /* BOM */
1788 char *tmpbuf
= tp
.c_get ();
1789 if (sys_wcstombs (tmpbuf
, NT_MAX_PATH
, (PWCHAR
) (cp
+ 2))
1792 res
= posixify (tmpbuf
);
1794 else if (len
> SYMLINK_MAX
)
1799 res
= posixify (cp
);
1802 if (res
) /* It's a symlink. */
1803 pflags
|= PATH_SYMLINK
| PATH_LNK
;
1808 symlink_info::check_sysfile (HANDLE h
)
1811 char cookie_buf
[sizeof (SYMLINK_COOKIE
) - 1];
1812 char *srcbuf
= tp
.c_get ();
1816 bool interix_symlink
= false;
1817 LARGE_INTEGER off
= { QuadPart
:0LL };
1819 status
= NtReadFile (h
, NULL
, NULL
, NULL
, &io
, cookie_buf
,
1820 sizeof (cookie_buf
), &off
, NULL
);
1821 status
= wait_pending (status
, h
, io
);
1822 if (!NT_SUCCESS (status
))
1824 debug_printf ("ReadFile1 failed %p", status
);
1825 if (status
!= STATUS_END_OF_FILE
)
1829 off
.QuadPart
= io
.Information
;
1830 if (io
.Information
== sizeof (cookie_buf
)
1831 && memcmp (cookie_buf
, SYMLINK_COOKIE
, sizeof (cookie_buf
)) == 0)
1833 /* It's a symlink. */
1834 pflags
|= PATH_SYMLINK
;
1836 else if (io
.Information
== sizeof (cookie_buf
)
1837 && memcmp (cookie_buf
, SOCKET_COOKIE
, sizeof (cookie_buf
)) == 0)
1838 pflags
|= PATH_SOCKET
;
1839 else if (io
.Information
>= sizeof (INTERIX_SYMLINK_COOKIE
)
1840 && memcmp (cookie_buf
, INTERIX_SYMLINK_COOKIE
,
1841 sizeof (INTERIX_SYMLINK_COOKIE
) - 1) == 0)
1843 /* It's an Interix symlink. */
1844 pflags
|= PATH_SYMLINK
;
1845 interix_symlink
= true;
1846 /* Interix symlink cookies are shorter than Cygwin symlink cookies, so
1847 in case of an Interix symlink cooky we have read too far into the
1848 file. Set file pointer back to the position right after the cookie. */
1849 off
.QuadPart
= sizeof (INTERIX_SYMLINK_COOKIE
) - 1;
1851 if (pflags
& PATH_SYMLINK
)
1853 status
= NtReadFile (h
, NULL
, NULL
, NULL
, &io
, srcbuf
,
1854 NT_MAX_PATH
, &off
, NULL
);
1855 status
= wait_pending (status
, h
, io
);
1856 if (!NT_SUCCESS (status
))
1858 debug_printf ("ReadFile2 failed");
1859 if (status
!= STATUS_END_OF_FILE
)
1862 else if (*(PWCHAR
) srcbuf
== 0xfeff /* BOM */
1865 /* Add trailing 0 to Interix symlink target. Skip BOM in Cygwin
1867 if (interix_symlink
)
1868 ((PWCHAR
) srcbuf
)[io
.Information
/ sizeof (WCHAR
)] = L
'\0';
1871 char *tmpbuf
= tp
.c_get ();
1872 if (sys_wcstombs (tmpbuf
, NT_MAX_PATH
, (PWCHAR
) srcbuf
)
1874 debug_printf ("symlink string too long");
1876 res
= posixify (tmpbuf
);
1878 else if (io
.Information
> SYMLINK_MAX
+ 1)
1879 debug_printf ("symlink string too long");
1881 res
= posixify (srcbuf
);
1887 symlink_info::check_reparse_point (HANDLE h
)
1892 PREPARSE_DATA_BUFFER rp
= (PREPARSE_DATA_BUFFER
) tp
.c_get ();
1893 UNICODE_STRING subst
;
1894 char srcbuf
[SYMLINK_MAX
+ 7];
1896 status
= NtFsControlFile (h
, NULL
, NULL
, NULL
, &io
, FSCTL_GET_REPARSE_POINT
,
1897 NULL
, 0, (LPVOID
) rp
,
1898 MAXIMUM_REPARSE_DATA_BUFFER_SIZE
);
1899 if (!NT_SUCCESS (status
))
1901 debug_printf ("NtFsControlFile(FSCTL_GET_REPARSE_POINT) failed, %p",
1906 if (rp
->ReparseTag
== IO_REPARSE_TAG_SYMLINK
)
1907 RtlInitCountedUnicodeString (&subst
,
1908 (WCHAR
*)((char *)rp
->SymbolicLinkReparseBuffer
.PathBuffer
1909 + rp
->SymbolicLinkReparseBuffer
.SubstituteNameOffset
),
1910 rp
->SymbolicLinkReparseBuffer
.SubstituteNameLength
);
1911 else if (rp
->ReparseTag
== IO_REPARSE_TAG_MOUNT_POINT
)
1913 RtlInitCountedUnicodeString (&subst
,
1914 (WCHAR
*)((char *)rp
->MountPointReparseBuffer
.PathBuffer
1915 + rp
->MountPointReparseBuffer
.SubstituteNameOffset
),
1916 rp
->MountPointReparseBuffer
.SubstituteNameLength
);
1917 if (RtlEqualUnicodePathPrefix (&subst
, &ro_u_volume
, TRUE
))
1919 /* Volume mount point. Not treated as symlink. The return
1920 value of -1 is a hint for the caller to treat this as a
1921 volume mount point. */
1927 /* Maybe it's a reparse point, but it's certainly not one we
1928 recognize. Drop the REPARSE file attribute so we don't even
1929 try to use the flag for some special handling. It's just some
1930 arbitrary file or directory for us. */
1931 fileattr
&= ~FILE_ATTRIBUTE_REPARSE_POINT
;
1934 sys_wcstombs (srcbuf
, SYMLINK_MAX
+ 7, subst
.Buffer
,
1935 subst
.Length
/ sizeof (WCHAR
));
1936 pflags
|= PATH_SYMLINK
| PATH_REP
;
1937 /* A symlink is never a directory. */
1938 fileattr
&= ~FILE_ATTRIBUTE_DIRECTORY
;
1939 return posixify (srcbuf
);
1943 symlink_info::check_nfs_symlink (HANDLE h
)
1949 FILE_GET_EA_INFORMATION fgei
;
1950 char buf
[sizeof (NFS_SYML_TARGET
)];
1952 PFILE_FULL_EA_INFORMATION pffei
;
1955 /* To find out if the file is a symlink and to get the symlink target,
1956 try to fetch the NfsSymlinkTargetName EA. */
1957 fgei_buf
.fgei
.NextEntryOffset
= 0;
1958 fgei_buf
.fgei
.EaNameLength
= sizeof (NFS_SYML_TARGET
) - 1;
1959 stpcpy (fgei_buf
.fgei
.EaName
, NFS_SYML_TARGET
);
1960 pffei
= (PFILE_FULL_EA_INFORMATION
) tp
.w_get ();
1961 status
= NtQueryEaFile (h
, &io
, pffei
, NT_MAX_PATH
* sizeof (WCHAR
), TRUE
,
1962 &fgei_buf
.fgei
, sizeof fgei_buf
, NULL
, TRUE
);
1963 if (NT_SUCCESS (status
) && pffei
->EaValueLength
> 0)
1965 PWCHAR spath
= (PWCHAR
)
1966 (pffei
->EaName
+ pffei
->EaNameLength
+ 1);
1967 res
= sys_wcstombs (contents
, SYMLINK_MAX
+ 1,
1968 spath
, pffei
->EaValueLength
) - 1;
1969 pflags
|= PATH_SYMLINK
;
1975 symlink_info::posixify (char *srcbuf
)
1977 /* The definition for a path in a native symlink is a bit weird. The Flags
1978 value seem to contain 0 for absolute paths (stored as NT native path)
1979 and 1 for relative paths. Relative paths are paths not starting with a
1980 drive letter. These are not converted to NT native, but stored as
1981 given. A path starting with a single backslash is relative to the
1982 current drive thus a "relative" value (Flags == 1).
1983 Funny enough it's possible to store paths with slashes instead of
1984 backslashes, but they are evaluated incorrectly by subsequent Windows
1985 calls like CreateFile (ERROR_INVALID_NAME). So, what we do here is to
1986 take paths starting with slashes at face value, evaluating them as
1987 Cygwin specific POSIX paths.
1988 A path starting with two slashes(!) or backslashes is converted into an
1989 NT UNC path. Unfortunately, in contrast to POSIX rules, paths starting
1990 with three or more (back)slashes are also converted into UNC paths,
1991 just incorrectly sticking to one redundant leading backslashe. We go
1992 along with this behaviour to avoid scenarios in which native tools access
1993 other files than Cygwin.
1994 The above rules are used exactly the same way on Cygwin specific symlinks
1995 (sysfiles and shortcuts) to eliminate non-POSIX paths in the output. */
1997 /* Eliminate native NT prefixes. */
1998 if (srcbuf
[0] == '\\' && !strncmp (srcbuf
+ 1, "??\\", 3))
2001 if (srcbuf
[1] != ':') /* native UNC path */
2002 *(srcbuf
+= 2) = '\\';
2004 if (isdrive (srcbuf
))
2005 mount_table
->conv_to_posix_path (srcbuf
, contents
, 0);
2006 else if (srcbuf
[0] == '\\')
2008 if (srcbuf
[1] == '\\') /* UNC path */
2009 slashify (srcbuf
, contents
, 0);
2010 else /* Paths starting with \ are current drive relative. */
2012 char cvtbuf
[SYMLINK_MAX
+ 1];
2014 stpcpy (cvtbuf
+ cygheap
->cwd
.get_drive (cvtbuf
), srcbuf
);
2015 mount_table
->conv_to_posix_path (cvtbuf
, contents
, 0);
2018 else /* Everything else is taken as is. */
2019 slashify (srcbuf
, contents
, 0);
2020 return strlen (contents
);
2029 SCAN_JUSTCHECKTHIS
, /* Never try to append a suffix. */
2037 const suffix_info
*suffixes
, *suffixes_start
;
2042 char *has (const char *, const suffix_info
*);
2044 int lnk_match () {return nextstate
>= SCAN_APPENDLNK
;}
2048 suffix_scan::has (const char *in_path
, const suffix_info
*in_suffixes
)
2050 nextstate
= SCAN_BEG
;
2051 suffixes
= suffixes_start
= in_suffixes
;
2053 const char *fname
= strrchr (in_path
, '\\');
2054 fname
= fname
? fname
+ 1 : in_path
;
2055 char *ext_here
= strrchr (fname
, '.');
2057 eopath
= strchr (path
, '\0');
2064 /* Check if the extension matches a known extension */
2065 for (const suffix_info
*ex
= in_suffixes
; ex
->name
!= NULL
; ex
++)
2066 if (ascii_strcasematch (ext_here
, ex
->name
))
2068 nextstate
= SCAN_JUSTCHECK
;
2069 suffixes
= NULL
; /* Has an extension so don't scan for one. */
2074 /* Didn't match. Use last resort -- .lnk. */
2075 if (ascii_strcasematch (ext_here
, ".lnk"))
2077 nextstate
= SCAN_HASLNK
;
2085 /* Avoid attaching suffixes if the resulting filename would be invalid. */
2086 if (eopath
- fname
> NAME_MAX
- 4)
2088 nextstate
= SCAN_JUSTCHECKTHIS
;
2095 suffix_scan::next ()
2103 suffixes
= suffixes_start
;
2106 nextstate
= SCAN_LNK
;
2109 nextstate
= SCAN_EXTRALNK
;
2110 /* fall through to suffix checking below */
2113 nextstate
= SCAN_APPENDLNK
; /* Skip SCAN_BEG */
2116 nextstate
= SCAN_DONE
;
2119 case SCAN_JUSTCHECK
:
2120 nextstate
= SCAN_LNK
;
2122 case SCAN_JUSTCHECKTHIS
:
2123 nextstate
= SCAN_DONE
;
2126 case SCAN_APPENDLNK
:
2127 strcat (eopath
, ".lnk");
2128 nextstate
= SCAN_DONE
;
2135 while (suffixes
&& suffixes
->name
)
2136 if (nextstate
== SCAN_EXTRALNK
&& !suffixes
->addon
)
2140 strcpy (eopath
, suffixes
->name
);
2141 if (nextstate
== SCAN_EXTRALNK
)
2142 strcat (eopath
, ".lnk");
2151 symlink_info::set_error (int in_errno
)
2154 if (!(pflags
& PATH_NO_ACCESS_CHECK
) || in_errno
== ENAMETOOLONG
|| in_errno
== EIO
)
2159 else if (in_errno
== ENOENT
)
2163 fileattr
= FILE_ATTRIBUTE_NORMAL
;
2170 symlink_info::parse_device (const char *contents
)
2177 mymajor
= strtol (contents
+= 2, &endptr
, 16);
2178 if (endptr
== contents
)
2179 return isdevice
= false;
2182 myminor
= strtol (++contents
, &endptr
, 16);
2183 if (endptr
== contents
)
2184 return isdevice
= false;
2187 mymode
= strtol (++contents
, &endptr
, 16);
2188 if (endptr
== contents
)
2189 return isdevice
= false;
2191 if ((mymode
& S_IFMT
) == S_IFIFO
)
2193 mymajor
= _major (FH_FIFO
);
2194 myminor
= _minor (FH_FIFO
);
2200 return isdevice
= true;
2203 /* Check if PATH is a symlink. PATH must be a valid Win32 path name.
2205 If PATH is a symlink, put the value of the symlink--the file to
2206 which it points--into BUF. The value stored in BUF is not
2207 necessarily null terminated. BUFLEN is the length of BUF; only up
2208 to BUFLEN characters will be stored in BUF. BUF may be NULL, in
2209 which case nothing will be stored.
2211 Set *SYML if PATH is a symlink.
2213 Set *EXEC if PATH appears to be executable. This is an efficiency
2214 hack because we sometimes have to open the file anyhow. *EXEC will
2215 not be set for every executable file.
2217 Return -1 on error, 0 if PATH is not a symlink, or the length
2218 stored into BUF if PATH is a symlink. */
2221 symlink_info::check (char *path
, const suffix_info
*suffixes
, fs_info
&fs
,
2222 path_conv_handle
&conv_hdl
)
2227 UNICODE_STRING upath
;
2228 OBJECT_ATTRIBUTES attr
;
2232 const ULONG ci_flag
= cygwin_shared
->obcaseinsensitive
2233 || (pflags
& PATH_NOPOSIX
) ? OBJ_CASE_INSENSITIVE
: 0;
2234 /* TODO: Temporarily do all char->UNICODE conversion here. This should
2235 already be slightly faster than using Ascii functions. */
2238 InitializeObjectAttributes (&attr
, &upath
, ci_flag
, NULL
, NULL
);
2240 /* This label is used in case we encounter a FS which only handles
2241 DOS paths. See below. */
2242 bool restarted
= false;
2253 pflags
&= ~(PATH_SYMLINK
| PATH_LNK
| PATH_REP
);
2255 ext_here
= suffix
.has (path
, suffixes
);
2256 extn
= ext_here
- path
;
2258 PVOID eabuf
= &nfs_aol_ffei
;
2259 ULONG easize
= sizeof nfs_aol_ffei
;
2261 # define MIN_STAT_ACCESS (READ_CONTROL | FILE_READ_ATTRIBUTES)
2262 # define FULL_STAT_ACCESS (SYNCHRONIZE | GENERIC_READ)
2263 ACCESS_MASK access
= 0;
2265 bool had_ext
= !!*ext_here
;
2266 while (suffix
.next ())
2271 get_nt_native_path (suffix
.path
, upath
, pflags
& PATH_DOS
);
2277 /* The EA given to NtCreateFile allows to get a handle to a symlink on
2278 an NFS share, rather than getting a handle to the target of the
2279 symlink (which would spoil the task of this method quite a bit).
2280 Fortunately it's ignored on most other file systems so we don't have
2281 to special case NFS too much. */
2282 status
= NtCreateFile (&h
, access
= FULL_STAT_ACCESS
, &attr
, &io
, NULL
,
2283 0, FILE_SHARE_VALID_FLAGS
, FILE_OPEN
,
2284 FILE_OPEN_REPARSE_POINT
2285 | FILE_OPEN_FOR_BACKUP_INTENT
,
2287 if (status
== STATUS_ACCESS_DENIED
&& eabuf
)
2289 status
= NtCreateFile (&h
, access
= MIN_STAT_ACCESS
| FILE_READ_EA
,
2290 &attr
, &io
, NULL
, 0, FILE_SHARE_VALID_FLAGS
,
2292 FILE_OPEN_REPARSE_POINT
2293 | FILE_OPEN_FOR_BACKUP_INTENT
,
2295 debug_printf ("%p = NtCreateFile (2:%S)", status
, &upath
);
2298 debug_printf ("%p = NtCreateFile (1:%S)", status
, &upath
);
2299 /* No right to access EAs or EAs not supported? */
2300 if (!NT_SUCCESS (status
)
2301 && (status
== STATUS_ACCESS_DENIED
2302 || status
== STATUS_EAS_NOT_SUPPORTED
2303 || status
== STATUS_NOT_SUPPORTED
2304 || status
== STATUS_INVALID_NETWORK_RESPONSE
2305 /* Or a bug in Samba 3.2.x (x <= 7) when accessing a share's
2306 root dir which has EAs enabled? */
2307 || status
== STATUS_INVALID_PARAMETER
))
2310 /* If EAs are not supported, there's no sense to check them again
2311 with suffixes attached. So we set eabuf/easize to 0 here once. */
2312 if (status
== STATUS_EAS_NOT_SUPPORTED
2313 || status
== STATUS_NOT_SUPPORTED
)
2318 status
= NtOpenFile (&h
, access
= FULL_STAT_ACCESS
, &attr
, &io
,
2319 FILE_SHARE_VALID_FLAGS
,
2320 FILE_OPEN_REPARSE_POINT
2321 | FILE_OPEN_FOR_BACKUP_INTENT
);
2322 if (status
== STATUS_ACCESS_DENIED
)
2324 status
= NtOpenFile (&h
, access
= MIN_STAT_ACCESS
, &attr
, &io
,
2325 FILE_SHARE_VALID_FLAGS
,
2326 FILE_OPEN_REPARSE_POINT
2327 | FILE_OPEN_FOR_BACKUP_INTENT
);
2328 debug_printf ("%p = NtOpenFile (no-EAs 2:%S)", status
, &upath
);
2331 debug_printf ("%p = NtOpenFile (no-EA 1:%S)", status
, &upath
);
2333 if (status
== STATUS_OBJECT_NAME_NOT_FOUND
)
2335 if (ci_flag
== 0 && wincap
.has_broken_udf ()
2336 && (!fs
.inited () || fs
.is_udf ()))
2338 /* On NT 5.x UDF is broken (at least) in terms of case
2339 sensitivity. When trying to open a file case sensitive,
2340 the file appears to be non-existant. Another bug is
2341 described in fs_info::update. */
2342 attr
.Attributes
= OBJ_CASE_INSENSITIVE
;
2343 status
= NtOpenFile (&h
, READ_CONTROL
| FILE_READ_ATTRIBUTES
,
2344 &attr
, &io
, FILE_SHARE_VALID_FLAGS
,
2345 FILE_OPEN_REPARSE_POINT
2346 | FILE_OPEN_FOR_BACKUP_INTENT
);
2347 debug_printf ("%p = NtOpenFile (broken-UDF, %S)", status
, &upath
);
2348 attr
.Attributes
= 0;
2349 if (NT_SUCCESS (status
))
2352 fs
.update (&upath
, h
);
2357 status
= STATUS_OBJECT_NAME_NOT_FOUND
;
2361 /* There are filesystems out in the wild (Netapp, NWFS, and others)
2362 which are uncapable of generating pathnames outside the Win32
2363 rules. That means, filenames on these FSes must not have a
2364 leading space or trailing dots and spaces. This code snippet
2365 manages them. I really hope it's streamlined enough not to
2366 slow down normal operation. This extra check only kicks in if
2367 we encountered a STATUS_OBJECT_NAME_NOT_FOUND *and* we didn't
2368 already attach a suffix *and* the above special case for UDF
2369 on XP didn't succeeed. */
2370 if (!restarted
&& !*ext_here
&& !(pflags
& PATH_DOS
) && !fs
.inited ())
2372 /* Check for trailing dot or space or leading space in
2374 char *p
= ext_here
- 1;
2375 if (*p
!= '.' && *p
!= ' ')
2377 while (*--p
!= '\\')
2384 /* If so, check if file resides on one of the known broken
2385 FSes only supporting filenames following DOS rules. */
2387 fs
.update (&upath
, NULL
);
2388 if (fs
.has_dos_filenames_only ())
2390 /* If so, try again. Since we now know the FS, the
2391 filenames will be tweaked to follow DOS rules via the
2392 third parameter in the call to get_nt_native_path. */
2401 FILE_BASIC_INFORMATION fbi
;
2402 PFILE_NETWORK_OPEN_INFORMATION pfnoi
= conv_hdl
.fnoi ();
2404 if (NT_SUCCESS (status
)
2405 /* Check file system while we're having the file open anyway.
2406 This speeds up path_conv noticably (~10%). */
2407 && (fs
.inited () || fs
.update (&upath
, h
)))
2411 /* NFS doesn't handle FileNetworkOpenInformation when called
2412 via NtQueryInformationFile (STATUS_INVALID_PARAMETER).
2413 Since we only need FileAttributes for NFS anyway, we just
2414 fetch the FileBasicInformation. */
2415 status
= NtQueryInformationFile (h
, &io
, &fbi
, sizeof fbi
,
2416 FileBasicInformation
);
2417 if (NT_SUCCESS (status
))
2418 fileattr
= fbi
.FileAttributes
;
2422 status
= NtQueryInformationFile (h
, &io
, pfnoi
, sizeof *pfnoi
,
2423 FileNetworkOpenInformation
);
2424 if ((status
== STATUS_INVALID_PARAMETER
2425 || status
== STATUS_NOT_IMPLEMENTED
)
2426 && RtlEqualUnicodePathPrefix (&upath
, &ro_u_uncp
, FALSE
))
2428 /* This occurs when accessing SMB share root dirs hosted on
2429 NT4 (STATUS_INVALID_PARAMETER), or when trying to access
2430 SMB share root dirs from NT4 (STATUS_NOT_IMPLEMENTED). */
2431 status
= NtQueryInformationFile (h
, &io
, &fbi
, sizeof fbi
,
2432 FileBasicInformation
);
2433 if (NT_SUCCESS (status
))
2435 memcpy (pfnoi
, &fbi
, 4 * sizeof (LARGE_INTEGER
));
2436 pfnoi
->EndOfFile
.QuadPart
2437 = pfnoi
->AllocationSize
.QuadPart
= 0;
2438 pfnoi
->FileAttributes
= fbi
.FileAttributes
;
2441 if (NT_SUCCESS (status
))
2442 fileattr
= pfnoi
->FileAttributes
;
2445 if (!NT_SUCCESS (status
))
2447 debug_printf ("%p = NtQueryInformationFile (%S)", status
, &upath
);
2448 fileattr
= INVALID_FILE_ATTRIBUTES
;
2450 /* One of the inner path components is invalid, or the path contains
2451 invalid characters. Bail out with ENOENT.
2453 Note that additional STATUS_OBJECT_PATH_INVALID and
2454 STATUS_OBJECT_PATH_SYNTAX_BAD status codes exist. The first one
2455 is seemingly not generated by NtQueryInformationFile, the latter
2456 is only generated if the path is no absolute path within the
2457 NT name space, which should not happen and would point to an
2458 error in get_nt_native_path. Both status codes are deliberately
2459 not tested here unless proved necessary. */
2460 if (status
== STATUS_OBJECT_PATH_NOT_FOUND
2461 || status
== STATUS_OBJECT_NAME_INVALID
2462 || status
== STATUS_BAD_NETWORK_PATH
2463 || status
== STATUS_BAD_NETWORK_NAME
2464 || status
== STATUS_NO_MEDIA_IN_DEVICE
)
2467 goto file_not_symlink
;
2469 if (status
!= STATUS_OBJECT_NAME_NOT_FOUND
2470 && status
!= STATUS_NO_SUCH_FILE
) /* ENOENT on NFS or 9x share */
2472 /* The file exists, but the user can't access it for one reason
2473 or the other. To get the file attributes we try to access the
2474 information by opening the parent directory and getting the
2475 file attributes using a matching NtQueryDirectoryFile call. */
2476 UNICODE_STRING dirname
, basename
;
2477 OBJECT_ATTRIBUTES dattr
;
2480 FILE_BOTH_DIRECTORY_INFORMATION fdi
;
2481 WCHAR dummy_buf
[NAME_MAX
+ 1];
2484 RtlSplitUnicodePath (&upath
, &dirname
, &basename
);
2485 InitializeObjectAttributes (&dattr
, &dirname
, ci_flag
,
2487 status
= NtOpenFile (&dir
, SYNCHRONIZE
| FILE_LIST_DIRECTORY
,
2488 &dattr
, &io
, FILE_SHARE_VALID_FLAGS
,
2489 FILE_SYNCHRONOUS_IO_NONALERT
2490 | FILE_OPEN_FOR_BACKUP_INTENT
2491 | FILE_DIRECTORY_FILE
);
2492 if (!NT_SUCCESS (status
))
2494 debug_printf ("%p = NtOpenFile(%S)", status
, &dirname
);
2495 /* There's a special case if the file is itself the root
2496 of a drive which is not accessible by the current user.
2497 This case is only recognized by the length of the
2498 basename part. If it's 0, the incoming file is the
2499 root of a drive. So we at least know it's a directory. */
2500 fileattr
= basename
.Length
? 0 : FILE_ATTRIBUTE_DIRECTORY
;
2504 status
= NtQueryDirectoryFile (dir
, NULL
, NULL
, NULL
, &io
,
2505 &fdi_buf
, sizeof fdi_buf
,
2506 FileBothDirectoryInformation
,
2507 TRUE
, &basename
, TRUE
);
2508 /* Take the opportunity to check file system while we're
2509 having the handle to the parent dir. */
2510 fs
.update (&upath
, dir
);
2512 if (!NT_SUCCESS (status
))
2514 debug_printf ("%p = NtQueryDirectoryFile(%S)",
2516 if (status
== STATUS_NO_SUCH_FILE
)
2518 /* This can happen when trying to access files
2519 which match DOS device names on SMB shares.
2520 NtOpenFile failed with STATUS_ACCESS_DENIED,
2521 but the NtQueryDirectoryFile tells us the
2522 file doesn't exist. We're suspicious in this
2523 case and retry with the next suffix instead of
2532 fileattr
= fdi_buf
.fdi
.FileAttributes
;
2533 memcpy (pfnoi
, &fdi_buf
.fdi
.CreationTime
, sizeof *pfnoi
);
2534 /* Amazing, but true: The FILE_NETWORK_OPEN_INFORMATION
2535 structure has the AllocationSize and EndOfFile members
2536 interchanged relative to the directory information
2538 pfnoi
->AllocationSize
.QuadPart
2539 = fdi_buf
.fdi
.AllocationSize
.QuadPart
;
2540 pfnoi
->EndOfFile
.QuadPart
2541 = fdi_buf
.fdi
.EndOfFile
.QuadPart
;
2544 ext_tacked_on
= !!*ext_here
;
2545 goto file_not_symlink
;
2551 ext_tacked_on
= !!*ext_here
;
2552 /* Don't allow to returns directories with appended suffix. If we found
2553 a directory with a suffix which has been appended here, then this
2554 directory doesn't match the request. So, just do as usual if file
2555 hasn't been found. */
2556 if (ext_tacked_on
&& !had_ext
&& (fileattr
& FILE_ATTRIBUTE_DIRECTORY
))
2564 /* Windows shortcuts are potentially treated as symlinks. Valid Cygwin
2565 & U/WIN shortcuts are R/O, but definitely not directories. */
2566 if ((fileattr
& (FILE_ATTRIBUTE_READONLY
| FILE_ATTRIBUTE_DIRECTORY
))
2567 == FILE_ATTRIBUTE_READONLY
&& suffix
.lnk_match ())
2569 if (!(access
& GENERIC_READ
))
2572 res
= check_shortcut (h
);
2575 /* If searching for `foo' and then finding a `foo.lnk' which is
2576 no shortcut, return the same as if file not found. */
2579 fileattr
= INVALID_FILE_ATTRIBUTES
;
2584 else if (contents
[0] != ':' || contents
[1] != '\\'
2585 || !parse_device (contents
))
2589 /* If searching for `foo' and then finding a `foo.lnk' which is
2590 no shortcut, return the same as if file not found. */
2591 else if (suffix
.lnk_match () && ext_tacked_on
)
2593 fileattr
= INVALID_FILE_ATTRIBUTES
;
2598 /* Reparse points are potentially symlinks. This check must be
2599 performed before checking the SYSTEM attribute for sysfile
2600 symlinks, since reparse points can have this flag set, too.
2601 For instance, Vista starts to create a couple of reparse points
2602 with SYSTEM and HIDDEN flags set.
2603 Also don't check reparse points on remote filesystems.
2604 A reparse point pointing to another file on the remote system will be
2605 mistreated as pointing to a local file on the local system. This
2606 breaks the way reparse points are transparently handled on remote
2608 else if ((fileattr
& FILE_ATTRIBUTE_REPARSE_POINT
)
2609 && !fs
.is_remote_drive())
2611 res
= check_reparse_point (h
);
2614 /* Volume mount point. The filesystem information for the top
2615 level directory should be for the volume top level directory,
2616 rather than for the reparse point itself. So we fetch the
2617 filesystem information again, but with a NULL handle.
2618 This does what we want because fs_info::update opens the
2619 handle without FILE_OPEN_REPARSE_POINT. */
2620 fs
.update (&upath
, NULL
);
2621 /* Make sure the open handle is not used in later stat calls.
2622 The handle has been opened with the FILE_OPEN_REPARSE_POINT
2623 flag, so it's a handle to the reparse point, not a handle
2624 to the volumes root dir. */
2625 pflags
&= ~PC_KEEP_HANDLE
;
2629 /* A symlink is never a directory. */
2630 pfnoi
->FileAttributes
&= ~FILE_ATTRIBUTE_DIRECTORY
;
2635 /* This is the old Cygwin method creating symlinks. A symlink will
2636 have the `system' file attribute. Only files can be symlinks
2637 (which can be symlinks to directories). */
2638 else if ((fileattr
& (FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_DIRECTORY
))
2639 == FILE_ATTRIBUTE_SYSTEM
)
2641 if (!(access
& GENERIC_READ
))
2644 res
= check_sysfile (h
);
2649 /* If the file is on an NFS share and could be opened with extended
2650 attributes, check if it's a symlink. Only files can be symlinks
2651 (which can be symlinks to directories). */
2652 else if (fs
.is_nfs () && !no_ea
&& !(fileattr
& FILE_ATTRIBUTE_DIRECTORY
))
2654 res
= check_nfs_symlink (h
);
2662 syscall_printf ("%s", isdevice
? "is a device" : "not a symlink");
2669 if (pflags
& PC_KEEP_HANDLE
)
2670 conv_hdl
.set (h
, access
);
2675 syscall_printf ("%d = symlink.check (%s, %p) (%p)",
2676 res
, suffix
.path
, contents
, pflags
);
2680 /* "path" is the path in a virtual symlink. Set a symlink_info struct from
2681 that and proceed with further path checking afterwards. */
2683 symlink_info::set (char *path
)
2685 strcpy (contents
, path
);
2686 pflags
= PATH_SYMLINK
;
2687 fileattr
= FILE_ATTRIBUTE_NORMAL
;
2691 ext_tacked_on
= false;
2693 extn
= major
= minor
= mode
= 0;
2694 return strlen (path
);
2697 /* readlink system call */
2700 readlink (const char *path
, char *buf
, size_t buflen
)
2704 set_errno (ENAMETOOLONG
);
2708 path_conv
pathbuf (path
, PC_SYM_CONTENTS
, stat_suffixes
);
2712 set_errno (pathbuf
.error
);
2713 syscall_printf ("-1 = readlink (%s, %p, %d)", path
, buf
, buflen
);
2717 if (!pathbuf
.exists ())
2723 if (!pathbuf
.issymlink ())
2725 if (pathbuf
.exists ())
2730 ssize_t len
= min (buflen
, strlen (pathbuf
.get_win32 ()));
2731 memcpy (buf
, pathbuf
.get_win32 (), len
);
2733 /* errno set by symlink.check if error */
2737 /* Some programs rely on st_dev/st_ino being unique for each file.
2738 Hash the path name and hope for the best. The hash arg is not
2739 always initialized to zero since readdir needs to compute the
2740 dirent ino_t based on a combination of the hash of the directory
2741 done during the opendir call and the hash or the filename within
2742 the directory. FIXME: Not bullet-proof. */
2743 /* Cygwin internal */
2745 hash_path_name (__ino64_t hash
, PUNICODE_STRING name
)
2747 if (name
->Length
== 0)
2750 /* Build up hash. Name is already normalized */
2751 USHORT len
= name
->Length
/ sizeof (WCHAR
);
2752 for (USHORT idx
= 0; idx
< len
; ++idx
)
2753 hash
= RtlUpcaseUnicodeChar (name
->Buffer
[idx
])
2754 + (hash
<< 6) + (hash
<< 16) - hash
;
2759 hash_path_name (__ino64_t hash
, PCWSTR name
)
2761 UNICODE_STRING uname
;
2762 RtlInitUnicodeString (&uname
, name
);
2763 return hash_path_name (hash
, &uname
);
2767 hash_path_name (__ino64_t hash
, const char *name
)
2769 UNICODE_STRING uname
;
2770 RtlCreateUnicodeStringFromAsciiz (&uname
, name
);
2771 __ino64_t ret
= hash_path_name (hash
, &uname
);
2772 RtlFreeUnicodeString (&uname
);
2777 getcwd (char *buf
, size_t ulen
)
2781 if (efault
.faulted (EFAULT
))
2783 else if (ulen
== 0 && buf
)
2786 res
= cygheap
->cwd
.get (buf
, 1, 1, ulen
);
2790 /* getwd: Legacy. */
2794 return getcwd (buf
, PATH_MAX
+ 1); /*Per SuSv3!*/
2797 /* chdir: POSIX 5.2.1.1 */
2799 chdir (const char *in_dir
)
2802 if (efault
.faulted (EFAULT
))
2810 syscall_printf ("dir '%s'", in_dir
);
2812 /* Convert path. First argument ensures that we don't check for NULL/empty/invalid
2814 path_conv
path (PC_NONULLEMPTY
, in_dir
, PC_SYM_FOLLOW
| PC_POSIX
);
2817 set_errno (path
.error
);
2818 syscall_printf ("-1 = chdir (%s)", in_dir
);
2823 const char *posix_cwd
= NULL
;
2824 int devn
= path
.get_devn ();
2825 if (!path
.exists ())
2827 else if (!path
.isdir ())
2828 set_errno (ENOTDIR
);
2829 else if (!isvirtual_dev (devn
))
2831 /* The sequence chdir("xx"); chdir(".."); must be a noop if xx
2832 is not a symlink. This is exploited by find.exe.
2833 The posix_cwd is just path.normalized_path.
2834 In other cases we let cwd.set obtain the Posix path through
2836 if (!isdrive(path
.normalized_path
))
2837 posix_cwd
= path
.normalized_path
;
2842 posix_cwd
= path
.normalized_path
;
2847 res
= cygheap
->cwd
.set (&path
, posix_cwd
);
2849 /* Note that we're accessing cwd.posix without a lock here. I didn't think
2850 it was worth locking just for strace. */
2851 syscall_printf ("%d = chdir() cygheap->cwd.posix '%s' native '%S'", res
,
2852 cygheap
->cwd
.get_posix (), path
.get_nt_native_path ());
2861 cygheap_fdget
cfd (fd
);
2863 res
= chdir (cfd
->get_name ());
2867 syscall_printf ("%d = fchdir (%d)", res
, fd
);
2871 /******************** Exported Path Routines *********************/
2873 /* Cover functions to the path conversion routines.
2874 These are exported to the world as cygwin_foo by cygwin.din. */
2876 #define return_with_errno(x) \
2886 cygwin_conv_path (cygwin_conv_path_t what
, const void *from
, void *to
,
2891 if (efault
.faulted (EFAULT
))
2899 bool relative
= !!(what
& CCP_RELATIVE
);
2900 what
&= ~CCP_RELATIVE
;
2904 case CCP_POSIX_TO_WIN_A
:
2906 p
.check ((const char *) from
,
2907 PC_POSIX
| PC_SYM_FOLLOW
| PC_NO_ACCESS_CHECK
| PC_NOWARN
2908 | (relative
? PC_NOFULL
: 0));
2910 return_with_errno (p
.error
);
2911 PUNICODE_STRING up
= p
.get_nt_native_path ();
2913 sys_wcstombs (buf
, NT_MAX_PATH
, up
->Buffer
, up
->Length
/ sizeof (WCHAR
));
2914 /* Convert native path to standard DOS path. */
2915 if (!strncmp (buf
, "\\??\\", 4))
2918 if (buf
[1] != ':') /* native UNC path */
2921 else if (*buf
== '\\')
2923 /* Device name points to somewhere else in the NT namespace.
2924 Use GLOBALROOT prefix to convert to Win32 path. */
2925 char *p
= stpcpy (buf
, "\\\\.\\GLOBALROOT");
2926 sys_wcstombs (p
, NT_MAX_PATH
- (p
- buf
),
2927 up
->Buffer
, up
->Length
/ sizeof (WCHAR
));
2929 lsiz
= strlen (buf
) + 1;
2930 /* TODO: Incoming "." is a special case which leads to a trailing
2931 backslash ".\\" in the Win32 path. That's a result of the
2932 conversion in normalize_posix_path. This should not occur
2933 so the below code is just a band-aid. */
2934 if (relative
&& !strcmp ((const char *) from
, ".")
2935 && !strcmp (buf
, ".\\"))
2942 case CCP_POSIX_TO_WIN_W
:
2943 p
.check ((const char *) from
, PC_POSIX
| PC_SYM_FOLLOW
2944 | PC_NO_ACCESS_CHECK
| PC_NOWARN
2945 | (relative
? PC_NOFULL
: 0));
2947 return_with_errno (p
.error
);
2948 /* Relative Windows paths are always restricted to MAX_PATH chars. */
2949 if (relative
&& !isabspath (p
.get_win32 ())
2950 && sys_mbstowcs (NULL
, 0, p
.get_win32 ()) > MAX_PATH
)
2952 /* Recreate as absolute path. */
2953 p
.check ((const char *) from
, PC_POSIX
| PC_SYM_FOLLOW
2954 | PC_NO_ACCESS_CHECK
| PC_NOWARN
);
2956 return_with_errno (p
.error
);
2958 lsiz
= p
.get_wide_win32_path_len () + 1;
2959 path
= p
.get_nt_native_path ()->Buffer
;
2961 /* Convert native path to standard DOS path. */
2962 if (!wcsncmp (path
, L
"\\??\\", 4))
2966 /* Drop long path prefix for short pathnames. Unfortunately there's
2967 quite a bunch of Win32 functions, especially in user32.dll,
2968 apparently, which don't grok long path names at all, not even
2969 in the UNICODE API. */
2970 if ((path
[5] == L
':' && lsiz
<= MAX_PATH
+ 4)
2971 || (!wcsncmp (path
+ 4, L
"UNC\\", 4) && lsiz
<= MAX_PATH
+ 6))
2975 if (path
[1] != L
':')
2977 *(path
+= 2) = '\\';
2982 else if (*path
== L
'\\')
2984 /* Device name points to somewhere else in the NT namespace.
2985 Use GLOBALROOT prefix to convert to Win32 path. */
2986 to
= (void *) wcpcpy ((wchar_t *) to
, L
"\\\\.\\GLOBALROOT");
2987 lsiz
+= sizeof ("\\\\.\\GLOBALROOT") - 1;
2989 /* TODO: Same ".\\" band-aid as in CCP_POSIX_TO_WIN_A case. */
2990 if (relative
&& !strcmp ((const char *) from
, ".")
2991 && !wcscmp (path
, L
".\\"))
2996 lsiz
*= sizeof (WCHAR
);
2998 case CCP_WIN_A_TO_POSIX
:
3000 error
= mount_table
->conv_to_posix_path ((const char *) from
, buf
,
3003 return_with_errno (error
);
3004 lsiz
= strlen (buf
) + 1;
3006 case CCP_WIN_W_TO_POSIX
:
3008 error
= mount_table
->conv_to_posix_path ((const PWCHAR
) from
, buf
,
3011 return_with_errno (error
);
3012 lsiz
= strlen (buf
) + 1;
3027 case CCP_POSIX_TO_WIN_A
:
3028 case CCP_WIN_A_TO_POSIX
:
3029 case CCP_WIN_W_TO_POSIX
:
3030 stpcpy ((char *) to
, buf
);
3032 case CCP_POSIX_TO_WIN_W
:
3033 wcpcpy ((PWCHAR
) to
, path
);
3040 cygwin_create_path (cygwin_conv_path_t what
, const void *from
)
3043 ssize_t size
= cygwin_conv_path (what
, from
, NULL
, 0);
3046 else if (!(to
= malloc (size
)))
3048 if (cygwin_conv_path (what
, from
, to
, size
) == -1)
3058 cygwin_conv_to_win32_path (const char *path
, char *win32_path
)
3060 return cygwin_conv_path (CCP_POSIX_TO_WIN_A
| CCP_RELATIVE
, path
, win32_path
,
3065 cygwin_conv_to_full_win32_path (const char *path
, char *win32_path
)
3067 return cygwin_conv_path (CCP_POSIX_TO_WIN_A
| CCP_ABSOLUTE
, path
, win32_path
,
3071 /* This is exported to the world as cygwin_foo by cygwin.din. */
3074 cygwin_conv_to_posix_path (const char *path
, char *posix_path
)
3076 return cygwin_conv_path (CCP_WIN_A_TO_POSIX
| CCP_RELATIVE
, path
, posix_path
,
3081 cygwin_conv_to_full_posix_path (const char *path
, char *posix_path
)
3083 return cygwin_conv_path (CCP_WIN_A_TO_POSIX
| CCP_ABSOLUTE
, path
, posix_path
,
3087 /* The realpath function is required by POSIX:2008. */
3090 realpath (const char *path
, char *resolved
)
3092 /* Make sure the right errno is returned if path is NULL. */
3099 /* Guard reading from a potentially invalid path and writing to a
3100 potentially invalid resolved. */
3103 if (efault
.faulted (EFAULT
))
3109 tpath
= tp
.c_get ();
3110 mount_table
->cygdrive_posix_path (path
, tpath
, 0);
3113 tpath
= (char *) path
;
3115 path_conv
real_path (tpath
, PC_SYM_FOLLOW
| PC_POSIX
, stat_suffixes
);
3118 /* POSIX 2008 requires malloc'ing if resolved is NULL, and states
3119 that using non-NULL resolved is asking for portability
3122 if (!real_path
.error
&& real_path
.exists ())
3126 resolved
= (char *) malloc (strlen (real_path
.normalized_path
) + 1);
3130 strcpy (resolved
, real_path
.normalized_path
);
3134 /* FIXME: on error, Linux puts the name of the path
3135 component which could not be resolved into RESOLVED, but POSIX
3136 does not require this. */
3139 set_errno (real_path
.error
?: ENOENT
);
3143 /* Linux provides this extension. Since the only portable use of
3144 realpath requires a NULL second argument, we might as well have a
3145 one-argument wrapper. */
3147 canonicalize_file_name (const char *path
)
3149 return realpath (path
, NULL
);
3152 /* Return non-zero if path is a POSIX path list.
3153 This is exported to the world as cygwin_foo by cygwin.din.
3156 <sect1 id="add-func-cygwin-posix-path-list-p">
3157 <para>Rather than use a mode to say what the "proper" path list
3158 format is, we allow any, and give apps the tools they need to
3159 convert between the two. If a ';' is present in the path list it's
3160 a Win32 path list. Otherwise, if the first path begins with
3161 [letter]: (in which case it can be the only element since if it
3162 wasn't a ';' would be present) it's a Win32 path list. Otherwise,
3163 it's a POSIX path list.</para>
3169 cygwin_posix_path_list_p (const char *path
)
3171 int posix_p
= !(strchr (path
, ';') || isdrive (path
));
3175 /* These are used for apps that need to convert env vars like PATH back and
3176 forth. The conversion is a two step process. First, an upper bound on the
3177 size of the buffer needed is computed. Then the conversion is done. This
3178 allows the caller to use alloca if it wants. */
3181 conv_path_list_buf_size (const char *path_list
, bool to_posix
)
3183 int i
, num_elms
, max_mount_path_len
, size
;
3186 path_conv
pc(".", PC_POSIX
);
3187 /* The theory is that an upper bound is
3188 current_size + (num_elms * max_mount_path_len) */
3189 /* FIXME: This method is questionable in the long run. */
3192 char delim
= to_posix
? ';' : ':';
3193 for (p
= path_list
, num_elms
= nrel
= 0; p
; num_elms
++)
3197 p
= strchr (++p
, delim
);
3200 /* 7: strlen ("//c") + slop, a conservative initial value */
3201 for (max_mount_path_len
= sizeof ("/cygdrive/X"), i
= 0;
3202 i
< mount_table
->nmounts
; i
++)
3204 int mount_len
= (to_posix
3205 ? mount_table
->mount
[i
].posix_pathlen
3206 : mount_table
->mount
[i
].native_pathlen
);
3207 if (max_mount_path_len
< mount_len
)
3208 max_mount_path_len
= mount_len
;
3212 size
= strlen (path_list
)
3213 + (num_elms
* max_mount_path_len
)
3214 + (nrel
* strlen (to_posix
? pc
.normalized_path
: pc
.get_win32 ()))
3222 cygwin_win32_to_posix_path_list_buf_size (const char *path_list
)
3224 return conv_path_list_buf_size (path_list
, true);
3228 cygwin_posix_to_win32_path_list_buf_size (const char *path_list
)
3230 return conv_path_list_buf_size (path_list
, false);
3234 env_PATH_to_posix (const void *win32
, void *posix
, size_t size
)
3236 return_with_errno (conv_path_list ((const char *) win32
, (char *) posix
,
3241 cygwin_win32_to_posix_path_list (const char *win32
, char *posix
)
3243 return_with_errno (conv_path_list (win32
, posix
, MAX_PATH
, 1));
3247 cygwin_posix_to_win32_path_list (const char *posix
, char *win32
)
3249 return_with_errno (conv_path_list (posix
, win32
, MAX_PATH
, 0));
3253 cygwin_conv_path_list (cygwin_conv_path_t what
, const void *from
, void *to
,
3256 /* FIXME: Path lists are (so far) always retaining relative paths. */
3257 what
&= ~CCP_RELATIVE
;
3260 case CCP_WIN_W_TO_POSIX
:
3261 case CCP_POSIX_TO_WIN_W
:
3263 api_fatal ("wide char path lists not yet supported");
3265 case CCP_WIN_A_TO_POSIX
:
3266 case CCP_POSIX_TO_WIN_A
:
3268 return conv_path_list_buf_size ((const char *) from
,
3269 what
== CCP_WIN_A_TO_POSIX
);
3270 return_with_errno (conv_path_list ((const char *) from
, (char *) to
,
3271 size
, what
== CCP_WIN_A_TO_POSIX
));
3280 /* cygwin_split_path: Split a path into directory and file name parts.
3281 Buffers DIR and FILE are assumed to be big enough.
3283 Examples (path -> `dir' / `file'):
3286 . -> `.' / `.' (FIXME: should this be `.' / `'?)
3287 .. -> `.' / `..' (FIXME: should this be `..' / `'?)
3289 foo/bar -> `foo' / `bar'
3290 foo/bar/ -> `foo' / `bar'
3292 /foo/bar -> `/foo' / `bar'
3295 c:foo -> `c:/' / `foo'
3296 c:/foo -> `c:/' / `foo'
3300 cygwin_split_path (const char *path
, char *dir
, char *file
)
3302 int dir_started_p
= 0;
3304 /* Deal with drives.
3305 Remember that c:foo <==> c:/foo. */
3317 if (isdirsep (*path
))
3322 /* Determine if there are trailing slashes and "delete" them if present.
3323 We pretend as if they don't exist. */
3324 const char *end
= path
+ strlen (path
);
3325 /* path + 1: keep leading slash. */
3326 while (end
> path
+ 1 && isdirsep (end
[-1]))
3329 /* At this point, END points to one beyond the last character
3330 (with trailing slashes "deleted"). */
3332 /* Point LAST_SLASH at the last slash (duh...). */
3333 const char *last_slash
;
3334 for (last_slash
= end
- 1; last_slash
>= path
; --last_slash
)
3335 if (isdirsep (*last_slash
))
3338 if (last_slash
== path
)
3343 else if (last_slash
> path
)
3345 memcpy (dir
, path
, last_slash
- path
);
3346 dir
[last_slash
- path
] = 0;
3351 ; /* nothing to do */
3357 memcpy (file
, last_slash
+ 1, end
- last_slash
- 1);
3358 file
[end
- last_slash
- 1] = 0;
3361 /*****************************************************************************/
3363 static inline PRTL_USER_PROCESS_PARAMETERS
3364 get_user_proc_parms ()
3366 return NtCurrentTeb ()->Peb
->ProcessParameters
;
3369 /* Initialize cygcwd 'muto' for serializing access to cwd info. */
3373 cwd_lock
.init ("cwd_lock");
3375 /* Cygwin processes inherit the cwd from their parent. If the win32 path
3376 buffer is not NULL, the cwd struct is already set up. */
3380 /* Initially re-open the cwd to allow POSIX semantics. */
3384 /* Chdir and fill out the elements of a cwdstuff struct. */
3386 cwdstuff::set (path_conv
*nat_cwd
, const char *posix_cwd
)
3389 UNICODE_STRING upath
;
3390 bool virtual_path
= false;
3391 bool unc_path
= false;
3392 bool inaccessible_path
= false;
3394 /* Here are the problems with using SetCurrentDirectory. Just skip this
3395 comment if you don't like whining.
3397 - SetCurrentDirectory only supports paths of up to MAX_PATH - 1 chars,
3398 including a trailing backslash. That's an absolute restriction, even
3401 - SetCurrentDirectory fails for directories with strict permissions even
3402 for processes with the SE_BACKUP_NAME privilege enabled. The reason
3403 is apparently that SetCurrentDirectory calls NtOpenFile without the
3404 FILE_OPEN_FOR_BACKUP_INTENT flag set.
3406 - SetCurrentDirectory does not support case-sensitivity.
3408 - Unlinking a cwd fails because SetCurrentDirectory seems to open
3409 directories so that deleting the directory is disallowed.
3411 - SetCurrentDirectory can naturally not work on virtual Cygwin paths
3412 like /proc or /cygdrive.
3414 Unfortunately, even though we have access to the Win32 process parameter
3415 block, we can't just replace the directory handle. Starting with Vista,
3416 the handle is used elsewhere, and just replacing the handle in the process
3417 parameter block shows quite surprising results.
3418 FIXME: If we ever find a *safe* way to replace the directory handle in
3419 the process parameter block, we're back in business.
3421 Nevertheless, doing entirely without SetCurrentDirectory is not really
3422 feasible, because it breaks too many mixed applications using the Win32
3425 Therefore we handle the CWD all by ourselves and just keep the Win32
3426 CWD in sync. However, to avoid surprising behaviour in the Win32 API
3427 when we are in a CWD which is inaccessible as Win32 CWD, we set the
3428 Win32 CWD to a "weird" directory in which all relative filesystem-related
3431 cwd_lock
.acquire ();
3435 upath
= *nat_cwd
->get_nt_native_path ();
3436 if (nat_cwd
->isspecial ())
3437 virtual_path
= true;
3440 /* Open a directory handle with FILE_OPEN_FOR_BACKUP_INTENT and with all
3441 sharing flags set. The handle is right now used in exceptions.cc only,
3442 but that might change in future. */
3447 OBJECT_ATTRIBUTES attr
;
3451 /* On init, just reopen Win32 CWD with desired access flags.
3452 We can access the PEB without lock, because no other thread
3453 can change the CWD. */
3454 RtlInitUnicodeString (&upath
, L
"");
3455 InitializeObjectAttributes (&attr
, &upath
,
3456 OBJ_CASE_INSENSITIVE
| OBJ_INHERIT
,
3457 get_user_proc_parms ()->CurrentDirectoryHandle
, NULL
);
3460 InitializeObjectAttributes (&attr
, &upath
,
3461 nat_cwd
->objcaseinsensitive () | OBJ_INHERIT
,
3463 /* First try without FILE_OPEN_FOR_BACKUP_INTENT, to find out if the
3464 directory is valid for Win32 apps. And, no, we can't just call
3465 SetCurrentDirectory here, since that would potentially break
3466 case-sensitivity. */
3467 status
= NtOpenFile (&h
, SYNCHRONIZE
| FILE_TRAVERSE
, &attr
, &io
,
3468 FILE_SHARE_VALID_FLAGS
,
3470 | FILE_SYNCHRONOUS_IO_NONALERT
);
3471 if (status
== STATUS_ACCESS_DENIED
)
3473 status
= NtOpenFile (&h
, SYNCHRONIZE
| FILE_TRAVERSE
, &attr
, &io
,
3474 FILE_SHARE_VALID_FLAGS
,
3476 | FILE_SYNCHRONOUS_IO_NONALERT
3477 | FILE_OPEN_FOR_BACKUP_INTENT
);
3478 inaccessible_path
= true;
3480 if (!NT_SUCCESS (status
))
3482 cwd_lock
.release ();
3483 __seterrno_from_nt_status (status
);
3487 /* Set new handle. It's only used when creating stackdumps so far. */
3494 /* On init, just fetch the Win32 dir from the PEB. We can access
3495 the PEB without lock, because no other thread can change the CWD
3497 PUNICODE_STRING pdir
= &get_user_proc_parms ()->CurrentDirectoryName
;
3498 RtlInitEmptyUnicodeString (&win32
,
3499 (PWCHAR
) crealloc_abort (win32
.Buffer
,
3502 pdir
->Length
+ sizeof (WCHAR
));
3503 RtlCopyUnicodeString (&win32
, pdir
);
3505 PWSTR eoBuffer
= win32
.Buffer
+ (win32
.Length
/ sizeof (WCHAR
));
3506 /* Remove trailing slash if one exists. */
3507 if ((eoBuffer
- win32
.Buffer
) > 3 && eoBuffer
[-1] == L
'\\')
3508 win32
.Length
-= sizeof (WCHAR
);
3509 if (eoBuffer
[0] == L
'\\')
3516 if (virtual_path
) /* don't mangle virtual path. */
3520 /* Compute length on Win32 path. */
3521 size_t len
= upath
.Length
/ sizeof (WCHAR
) - 4;
3522 if (RtlEqualUnicodePathPrefix (&upath
, &ro_u_uncp
, TRUE
))
3527 /* Convert to a Win32 path. */
3528 upath
.Buffer
+= upath
.Length
/ sizeof (WCHAR
) - len
;
3529 upath
.Length
= len
* sizeof (WCHAR
);
3531 PWSTR eoBuffer
= upath
.Buffer
+ (upath
.Length
/ sizeof (WCHAR
));
3532 /* Remove trailing slash if one exists. */
3533 if ((eoBuffer
- upath
.Buffer
) > 3 && eoBuffer
[-1] == L
'\\')
3534 upath
.Length
-= sizeof (WCHAR
);
3536 RtlInitEmptyUnicodeString (&win32
,
3537 (PWCHAR
) crealloc_abort (win32
.Buffer
,
3540 upath
.Length
+ sizeof (WCHAR
));
3541 RtlCopyUnicodeString (&win32
, &upath
);
3543 win32
.Buffer
[0] = L
'\\';
3545 /* Make sure it's NUL-terminated. */
3546 win32
.Buffer
[win32
.Length
/ sizeof (WCHAR
)] = L
'\0';
3548 /* Set drive_length, used in path conversion, and error code, used in
3549 spawn_guts to decide whether a native Win32 app can be started or not. */
3561 PWCHAR ptr
= wcschr (win32
.Buffer
+ 2, L
'\\');
3563 ptr
= wcschr (ptr
+ 1, L
'\\');
3565 drive_length
= ptr
- win32
.Buffer
;
3567 drive_length
= win32
.Length
/ sizeof (WCHAR
);
3569 if (inaccessible_path
)
3571 else if (win32
.Length
> (MAX_PATH
- 2) * sizeof (WCHAR
))
3572 error
= ENAMETOOLONG
;
3576 /* Keep the Win32 CWD in sync. Don't check for error, other than for
3577 strace output. Try to keep overhead low. */
3580 status
= RtlSetCurrentDirectory_U (error
? &ro_u_pipedir
: &win32
);
3581 if (!NT_SUCCESS (status
))
3582 debug_printf ("RtlSetCurrentDirectory_U(%S) failed, %p",
3583 error
? &ro_u_pipedir
: &win32
, status
);
3586 /* Eventually, create POSIX path if it's not set on entry. */
3590 posix_cwd
= (const char *) tp
.c_get ();
3591 mount_table
->conv_to_posix_path (win32
.Buffer
, (char *) posix_cwd
, 0);
3593 posix
= (char *) crealloc_abort (posix
, strlen (posix_cwd
) + 1);
3594 stpcpy (posix
, posix_cwd
);
3596 cwd_lock
.release ();
3601 cwdstuff::get_error_desc () const
3603 switch (cygheap
->cwd
.get_error ())
3606 return "has restricted permissions which render it\n"
3607 "inaccessible as Win32 working directory";
3609 return "is a virtual Cygwin directory which does\n"
3610 "not exist for a native Windows application";
3612 return "has a path longer than allowed for a\n"
3613 "Win32 working directory";
3617 /* That shouldn't occur, unless we defined a new error code
3618 in cwdstuff::set. */
3619 return "is not accessible for some unknown reason";
3622 /* Store incoming wchar_t path as current posix cwd. This is called from
3623 setlocale so that the cwd is always stored in the right charset. */
3625 cwdstuff::reset_posix (wchar_t *w_cwd
)
3627 size_t len
= sys_wcstombs (NULL
, (size_t) -1, w_cwd
);
3628 posix
= (char *) crealloc_abort (posix
, len
+ 1);
3629 sys_wcstombs (posix
, len
+ 1, w_cwd
);
3633 cwdstuff::get (char *buf
, int need_posix
, int with_chroot
, unsigned ulen
)
3640 else if (buf
== NULL
)
3641 ulen
= (unsigned) -1;
3648 cwd_lock
.acquire ();
3653 tocopy
= tp
.c_get ();
3654 sys_wcstombs (tocopy
, NT_MAX_PATH
, win32
.Buffer
,
3655 win32
.Length
/ sizeof (WCHAR
));
3660 debug_printf ("posix %s", posix
);
3661 if (strlen (tocopy
) >= ulen
)
3669 buf
= (char *) malloc (strlen (tocopy
) + 1);
3670 strcpy (buf
, tocopy
);
3671 if (!buf
[0]) /* Should only happen when chroot */
3675 cwd_lock
.release ();
3678 syscall_printf ("(%s) = cwdstuff::get (%p, %d, %d, %d), errno %d",
3679 buf
, buf
, ulen
, need_posix
, with_chroot
, errno
);
3684 int etc::curr_ix
= 0;
3685 /* Note that the first elements of the below arrays are unused */
3686 bool etc::change_possible
[MAX_ETC_FILES
+ 1];
3687 OBJECT_ATTRIBUTES
etc::fn
[MAX_ETC_FILES
+ 1];
3688 LARGE_INTEGER
etc::last_modified
[MAX_ETC_FILES
+ 1];
3691 etc::init (int n
, POBJECT_ATTRIBUTES attr
)
3695 else if (++curr_ix
<= MAX_ETC_FILES
)
3698 api_fatal ("internal error");
3701 change_possible
[n
] = false;
3702 test_file_change (n
);
3703 paranoid_printf ("fn[%d] %S, curr_ix %d", n
, fn
[n
].ObjectName
, curr_ix
);
3708 etc::test_file_change (int n
)
3711 FILE_NETWORK_OPEN_INFORMATION fnoi
;
3714 status
= NtQueryFullAttributesFile (&fn
[n
], &fnoi
);
3715 if (!NT_SUCCESS (status
))
3718 memset (last_modified
+ n
, 0, sizeof (last_modified
[n
]));
3719 debug_printf ("NtQueryFullAttributesFile (%S) failed, %p",
3720 fn
[n
].ObjectName
, status
);
3724 res
= CompareFileTime ((FILETIME
*) &fnoi
.LastWriteTime
,
3725 (FILETIME
*) last_modified
+ n
) > 0;
3726 last_modified
[n
].QuadPart
= fnoi
.LastWriteTime
.QuadPart
;
3729 paranoid_printf ("fn[%d] %S res %d", n
, fn
[n
].ObjectName
, res
);
3734 etc::dir_changed (int n
)
3736 if (!change_possible
[n
])
3738 static HANDLE changed_h NO_COPY
;
3744 OBJECT_ATTRIBUTES attr
;
3746 path_conv
dir ("/etc");
3747 status
= NtOpenFile (&changed_h
, SYNCHRONIZE
| FILE_LIST_DIRECTORY
,
3748 dir
.get_object_attr (attr
, sec_none_nih
), &io
,
3749 FILE_SHARE_VALID_FLAGS
, FILE_DIRECTORY_FILE
);
3750 if (!NT_SUCCESS (status
))
3753 system_printf ("NtOpenFile (%S) failed, %p",
3754 dir
.get_nt_native_path (), status
);
3756 changed_h
= INVALID_HANDLE_VALUE
;
3760 status
= NtNotifyChangeDirectoryFile (changed_h
, NULL
, NULL
,
3762 FILE_NOTIFY_CHANGE_LAST_WRITE
3763 | FILE_NOTIFY_CHANGE_FILE_NAME
,
3765 if (!NT_SUCCESS (status
))
3768 system_printf ("NtNotifyChangeDirectoryFile (1) failed, %p",
3771 NtClose (changed_h
);
3772 changed_h
= INVALID_HANDLE_VALUE
;
3775 memset (change_possible
, true, sizeof (change_possible
));
3778 if (changed_h
== INVALID_HANDLE_VALUE
)
3779 change_possible
[n
] = true;
3780 else if (WaitForSingleObject (changed_h
, 0) == WAIT_OBJECT_0
)
3782 status
= NtNotifyChangeDirectoryFile (changed_h
, NULL
, NULL
,
3784 FILE_NOTIFY_CHANGE_LAST_WRITE
3785 | FILE_NOTIFY_CHANGE_FILE_NAME
,
3787 if (!NT_SUCCESS (status
))
3790 system_printf ("NtNotifyChangeDirectoryFile (2) failed, %p",
3793 NtClose (changed_h
);
3794 changed_h
= INVALID_HANDLE_VALUE
;
3796 memset (change_possible
, true, sizeof change_possible
);
3800 paranoid_printf ("fn[%d] %S change_possible %d",
3801 n
, fn
[n
].ObjectName
, change_possible
[n
]);
3802 return change_possible
[n
];
3806 etc::file_changed (int n
)
3809 if (dir_changed (n
) && test_file_change (n
))
3811 change_possible
[n
] = false; /* Change is no longer possible */
3812 paranoid_printf ("fn[%d] %S res %d", n
, fn
[n
].ObjectName
, res
);
3816 /* No need to be reentrant or thread-safe according to SUSv3.
3817 / and \\ are treated equally. Leading drive specifiers are
3818 kept intact as far as it makes sense. Everything else is
3819 POSIX compatible. */
3821 basename (char *path
)
3824 char *c
, *d
, *bs
= path
;
3826 if (!path
|| !*path
)
3827 return strcpy (buf
, ".");
3828 if (isalpha (path
[0]) && path
[1] == ':')
3830 else if (strspn (path
, "/\\") > 1)
3832 c
= strrchr (bs
, '/');
3833 if ((d
= strrchr (c
?: bs
, '\\')) > c
)
3837 /* Trailing (back)slashes are eliminated. */
3838 while (c
&& c
> bs
&& c
[1] == '\0')
3841 c
= strrchr (bs
, '/');
3842 if ((d
= strrchr (c
?: bs
, '\\')) > c
)
3845 if (c
&& (c
> bs
|| c
[1]))
3850 stpncpy (buf
, path
, bs
- path
);
3851 stpcpy (buf
+ (bs
- path
), ".");
3857 /* No need to be reentrant or thread-safe according to SUSv3.
3858 / and \\ are treated equally. Leading drive specifiers and
3859 leading double (back)slashes are kept intact as far as it
3860 makes sense. Everything else is POSIX compatible. */
3862 dirname (char *path
)
3865 char *c
, *d
, *bs
= path
;
3867 if (!path
|| !*path
)
3868 return strcpy (buf
, ".");
3869 if (isalpha (path
[0]) && path
[1] == ':')
3871 else if (strspn (path
, "/\\") > 1)
3873 c
= strrchr (bs
, '/');
3874 if ((d
= strrchr (c
?: bs
, '\\')) > c
)
3878 /* Trailing (back)slashes are eliminated. */
3879 while (c
&& c
> bs
&& c
[1] == '\0')
3882 c
= strrchr (bs
, '/');
3883 if ((d
= strrchr (c
?: bs
, '\\')) > c
)
3890 /* More trailing (back)slashes are eliminated. */
3891 while (c
> bs
&& (*c
== '/' || *c
== '\\'))
3899 stpncpy (buf
, path
, bs
- path
);
3900 stpcpy (buf
+ (bs
- path
), ".");