1 /* path.cc: path support.
3 Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
4 2006, 2007, 2008, 2009, 2010, 2011 Red Hat, Inc.
6 This file is part of Cygwin.
8 This software is a copyrighted work licensed under the terms of the
9 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
12 /* This module's job is to
13 - convert between POSIX and Win32 style filenames,
14 - support the `mount' functionality,
15 - support symlinks for files and directories
17 Pathnames are handled as follows:
19 - A \ or : in a path denotes a pure windows spec.
20 - Paths beginning with // (or \\) are not translated (i.e. looked
21 up in the mount table) and are assumed to be UNC path names.
23 The goal in the above set of rules is to allow both POSIX and Win32
24 flavors of pathnames without either interfering. The rules are
25 intended to be as close to a superset of both as possible.
27 Note that you can have more than one path to a file. The mount
28 table is always prefered when translating Win32 paths to POSIX
29 paths. Win32 paths in mount table entries may be UNC paths or
30 standard Win32 paths starting with <drive-letter>:
32 Text vs Binary issues are not considered here in path style
33 decisions, although the appropriate flags are retrieved and
34 stored in various structures.
36 Removing mounted filesystem support would simplify things greatly,
37 but having it gives us a mechanism of treating disk that lives on a
38 UNIX machine as having UNIX semantics [it allows one to edit a text
39 file on that disk and not have cr's magically appear and perhaps
40 break apps running on UNIX boxes]. It also useful to be able to
41 layout a hierarchy without changing the underlying directories.
43 The semantics of mounting file systems is not intended to precisely
44 follow normal UNIX systems.
46 Each DOS drive is defined to have a current directory. Supporting
47 this would complicate things so for now things are defined so that
52 #include "miscfuncs.h"
59 #include <sys/cygwin.h>
66 #include "shared_info.h"
75 bool dos_file_warning = true;
77 suffix_info stat_suffixes[] =
80 suffix_info (".exe", 1),
86 char contents[SYMLINK_MAX + 1];
98 int check (char *path, const suffix_info *suffixes, fs_info &fs,
99 path_conv_handle &conv_hdl);
100 int set (char *path);
101 bool parse_device (const char *);
102 int check_sysfile (HANDLE h);
103 int check_shortcut (HANDLE h);
104 int check_reparse_point (HANDLE h);
105 int check_nfs_symlink (HANDLE h);
106 int posixify (char *srcbuf);
107 bool set_error (int);
110 muto NO_COPY cwdstuff::cwd_lock;
112 static const GUID GUID_shortcut
113 = { 0x00021401L, 0, 0, {0xc0, 0, 0, 0, 0, 0, 0, 0x46}};
116 WSH_FLAG_IDLIST = 0x01, /* Contains an ITEMIDLIST. */
117 WSH_FLAG_FILE = 0x02, /* Contains a file locator element. */
118 WSH_FLAG_DESC = 0x04, /* Contains a description. */
119 WSH_FLAG_RELPATH = 0x08, /* Contains a relative path. */
120 WSH_FLAG_WD = 0x10, /* Contains a working dir. */
121 WSH_FLAG_CMDLINE = 0x20, /* Contains command line args. */
122 WSH_FLAG_ICON = 0x40 /* Contains a custom icon. */
125 struct win_shortcut_hdr
127 DWORD size; /* Header size in bytes. Must contain 0x4c. */
128 GUID magic; /* GUID of shortcut files. */
129 DWORD flags; /* Content flags. See above. */
131 /* The next fields from attr to icon_no are always set to 0 in Cygwin
132 and U/Win shortcuts. */
133 DWORD attr; /* Target file attributes. */
134 FILETIME ctime; /* These filetime items are never touched by the */
135 FILETIME mtime; /* system, apparently. Values don't matter. */
137 DWORD filesize; /* Target filesize. */
138 DWORD icon_no; /* Icon number. */
140 DWORD run; /* Values defined in winuser.h. Use SW_NORMAL. */
141 DWORD hotkey; /* Hotkey value. Set to 0. */
142 DWORD dummy[2]; /* Future extension probably. Always 0. */
145 /* Return non-zero if PATH1 is a prefix of PATH2.
146 Both are assumed to be of the same path style and / vs \ usage.
148 LEN1 = strlen (PATH1). It's passed because often it's already known.
151 /foo/ is a prefix of /foo <-- may seem odd, but desired
152 /foo is a prefix of /foo/
153 / is a prefix of /foo/bar
154 / is not a prefix of foo/bar
155 foo/ is a prefix foo/bar
156 /foo is not a prefix of /foobar
160 path_prefix_p (const char *path1, const char *path2, int len1,
161 bool caseinsensitive)
163 /* Handle case where PATH1 has trailing '/' and when it doesn't. */
164 if (len1 > 0 && isdirsep (path1[len1 - 1]))
168 return isdirsep (path2[0]) && !isdirsep (path2[1]);
170 if (isdirsep (path2[len1]) || path2[len1] == 0 || path1[len1 - 1] == ':')
171 return caseinsensitive ? strncasematch (path1, path2, len1)
172 : !strncmp (path1, path2, len1);
177 /* Return non-zero if paths match in first len chars.
178 Check is dependent of the case sensitivity setting. */
180 pathnmatch (const char *path1, const char *path2, int len, bool caseinsensitive)
182 return caseinsensitive
183 ? strncasematch (path1, path2, len) : !strncmp (path1, path2, len);
186 /* Return non-zero if paths match. Check is dependent of the case
187 sensitivity setting. */
189 pathmatch (const char *path1, const char *path2, bool caseinsensitive)
191 return caseinsensitive
192 ? strcasematch (path1, path2) : !strcmp (path1, path2);
195 /* TODO: This function is used in mkdir and rmdir to generate correct
196 error messages in case of paths ending in /. or /.. components.
197 Right now, normalize_posix_path will just normalize
198 those components away, which changes the semantics. */
200 has_dot_last_component (const char *dir, bool test_dot_dot)
202 /* SUSv3: . and .. are not allowed as last components in various system
203 calls. Don't test for backslash path separator since that's a Win32
204 path following Win32 rules. */
205 const char *last_comp = strchr (dir, '\0');
207 if (last_comp == dir)
208 return false; /* Empty string. Probably shouldn't happen here? */
210 /* Detect run of trailing slashes */
211 while (last_comp > dir && *--last_comp == '/')
214 /* Detect just a run of slashes or a path that does not end with a slash. */
215 if (*last_comp != '.')
218 /* We know we have a trailing dot here. Check that it really is a standalone "."
219 path component by checking that it is at the beginning of the string or is
221 if (last_comp == dir || *--last_comp == '/')
224 /* If we're not checking for '..' we're done. Ditto if we're now pointing to
226 if (!test_dot_dot || *last_comp != '.')
227 return false; /* either not testing for .. or this was not '..' */
229 /* Repeat previous test for standalone or path component. */
230 return last_comp == dir || last_comp[-1] == '/';
233 /* Normalize a POSIX path.
234 All duplicate /'s, except for 2 leading /'s, are deleted.
235 The result is 0 for success, or an errno error value. */
238 normalize_posix_path (const char *src, char *dst, char *&tail)
240 const char *in_src = src;
241 char *dst_start = dst;
242 syscall_printf ("src %s", src);
244 if ((isdrive (src) && isdirsep (src[2])) || *src == '\\')
248 if (!isslash (src[0]))
250 if (!cygheap->cwd.get (dst))
252 tail = strchr (tail, '\0');
253 if (isslash (dst[0]) && isslash (dst[1]))
257 if (tail == dst_start + 1 && *dst_start == '/')
261 if (tail > dst && !isslash (tail[-1]))
264 /* Two leading /'s? If so, preserve them. */
265 else if (isslash (src[1]) && !isslash (src[2]))
275 /* Strip runs of /'s. */
296 if (!isslash (src[1]))
299 else if (src[2] && !isslash (src[2]))
303 while (tail > dst_start && !isslash (*--tail))
311 if ((tail - dst) >= NT_MAX_PATH)
313 debug_printf ("ENAMETOOLONG = normalize_posix_path (%s)", src);
321 debug_printf ("%s = normalize_posix_path (%s)", dst, in_src);
325 int err = normalize_win32_path (in_src, dst, tail);
327 for (char *p = dst; (p = strchr (p, '\\')); p++)
333 path_conv::add_ext_from_sym (symlink_info &sym)
335 if (sym.ext_here && *sym.ext_here)
337 known_suffix = path + sym.extn;
338 if (sym.ext_tacked_on)
339 strcpy ((char *) known_suffix, sym.ext_here);
343 static void __stdcall mkrelpath (char *dst, bool caseinsensitive)
344 __attribute__ ((regparm (2)));
346 static void __stdcall
347 mkrelpath (char *path, bool caseinsensitive)
350 char *cwd_win32 = tp.c_get ();
351 if (!cygheap->cwd.get (cwd_win32, 0))
354 unsigned cwdlen = strlen (cwd_win32);
355 if (!path_prefix_p (cwd_win32, path, cwdlen, caseinsensitive))
358 size_t n = strlen (path);
366 tail += isdirsep (cwd_win32[cwdlen - 1]) ? cwdlen : cwdlen + 1;
368 memmove (path, tail, strlen (tail) + 1);
374 path_conv::set_normalized_path (const char *path_copy)
378 size_t n = strlen (path_copy) + 1;
379 char *p = (char *) crealloc_abort ((void *) normalized_path, n);
380 normalized_path = (const char *) memcpy (p, path_copy, n);
385 str2uni_cat (UNICODE_STRING &tgt, const char *srcstr)
387 int len = sys_mbstowcs (tgt.Buffer + tgt.Length / sizeof (WCHAR),
388 (tgt.MaximumLength - tgt.Length) / sizeof (WCHAR),
391 tgt.Length += (len - 1) * sizeof (WCHAR);
395 get_nt_native_path (const char *path, UNICODE_STRING& upath, bool dos)
398 if (path[0] == '/') /* special path w/o NT path representation. */
399 str2uni_cat (upath, path);
400 else if (path[0] != '\\') /* X:\... or relative path. */
402 if (path[1] == ':') /* X:\... */
404 RtlAppendUnicodeStringToString (&upath, &ro_u_natp);
405 str2uni_cat (upath, path);
406 /* The drive letter must be upper case. */
407 upath.Buffer[4] = towupper (upath.Buffer[4]);
410 str2uni_cat (upath, path);
411 transform_chars (&upath, 7);
413 else if (path[1] != '\\') /* \Device\... */
414 str2uni_cat (upath, path);
415 else if ((path[2] != '.' && path[2] != '?')
416 || path[3] != '\\') /* \\server\share\... */
418 RtlAppendUnicodeStringToString (&upath, &ro_u_uncp);
419 str2uni_cat (upath, path + 2);
420 transform_chars (&upath, 8);
422 else /* \\.\device or \\?\foo */
424 RtlAppendUnicodeStringToString (&upath, &ro_u_natp);
425 str2uni_cat (upath, path + 4);
429 /* Unfortunately we can't just use transform_chars with the tfx_rev_chars
430 table since only leading and trainlig spaces and dots are affected.
431 So we step to every backslash and fix surrounding dots and spaces.
432 That makes these broken filesystems a bit slower, but, hey. */
433 PWCHAR cp = upath.Buffer + 7;
434 PWCHAR cend = upath.Buffer + upath.Length / sizeof (WCHAR);
439 while (*ccp == L'.' || *ccp == L' ')
441 while (cp[1] == L' ')
444 while (*--cp == L'.' || *cp == L' ')
451 path_conv::get_nt_native_path ()
456 uni_path.MaximumLength = (strlen (path) + 10) * sizeof (WCHAR);
457 wide_path = (PWCHAR) cmalloc_abort (HEAP_STR, uni_path.MaximumLength);
458 uni_path.Buffer = wide_path;
459 ::get_nt_native_path (path, uni_path, has_dos_filenames_only ());
465 path_conv::get_wide_win32_path (PWCHAR wc)
467 get_nt_native_path ();
470 wcpcpy (wc, wide_path);
477 warn_msdos (const char *src)
479 if (user_shared->warned_msdos || !dos_file_warning || !cygwin_finished_initializing)
482 char *posix_path = tp.c_get ();
483 small_printf ("cygwin warning:\n");
484 if (cygwin_conv_path (CCP_WIN_A_TO_POSIX | CCP_RELATIVE, src,
485 posix_path, NT_MAX_PATH))
486 small_printf (" MS-DOS style path detected: %ls\n POSIX equivalent preferred.\n",
489 small_printf (" MS-DOS style path detected: %ls\n Preferred POSIX equivalent is: %ls\n",
491 small_printf (" CYGWIN environment variable option \"nodosfilewarning\" turns off this warning.\n"
492 " Consult the user's guide for more details about POSIX paths:\n"
493 " http://cygwin.com/cygwin-ug-net/using.html#using-pathnames\n");
494 user_shared->warned_msdos = true;
498 getfileattr (const char *path, bool caseinsensitive) /* path has to be always absolute. */
501 UNICODE_STRING upath;
502 OBJECT_ATTRIBUTES attr;
503 FILE_BASIC_INFORMATION fbi;
508 InitializeObjectAttributes (&attr, &upath,
509 caseinsensitive ? OBJ_CASE_INSENSITIVE : 0,
511 get_nt_native_path (path, upath, false);
513 status = NtQueryAttributesFile (&attr, &fbi);
514 if (NT_SUCCESS (status))
515 return fbi.FileAttributes;
517 if (status != STATUS_OBJECT_NAME_NOT_FOUND
518 && status != STATUS_NO_SUCH_FILE) /* File not found on 9x share */
520 /* File exists but access denied. Try to get attribute through
522 UNICODE_STRING dirname, basename;
524 FILE_BOTH_DIRECTORY_INFORMATION fdi;
526 RtlSplitUnicodePath (&upath, &dirname, &basename);
527 InitializeObjectAttributes (&attr, &dirname,
528 caseinsensitive ? OBJ_CASE_INSENSITIVE : 0,
530 status = NtOpenFile (&dir, SYNCHRONIZE | FILE_LIST_DIRECTORY,
531 &attr, &io, FILE_SHARE_VALID_FLAGS,
532 FILE_SYNCHRONOUS_IO_NONALERT
533 | FILE_OPEN_FOR_BACKUP_INTENT
534 | FILE_DIRECTORY_FILE);
535 if (NT_SUCCESS (status))
537 status = NtQueryDirectoryFile (dir, NULL, NULL, 0, &io,
539 FileBothDirectoryInformation,
540 TRUE, &basename, TRUE);
542 if (NT_SUCCESS (status) || status == STATUS_BUFFER_OVERFLOW)
543 return fdi.FileAttributes;
546 SetLastError (RtlNtStatusToDosError (status));
547 return INVALID_FILE_ATTRIBUTES;
550 /* Convert an arbitrary path SRC to a pure Win32 path, suitable for
551 passing to Win32 API routines.
553 If an error occurs, `error' is set to the errno value.
554 Otherwise it is set to 0.
557 SYMLINK_FOLLOW - convert to PATH symlink points to
558 SYMLINK_NOFOLLOW - convert to PATH of symlink itself
559 SYMLINK_IGNORE - do not check PATH for symlinks
560 SYMLINK_CONTENTS - just return symlink contents
563 /* TODO: This implementation is only preliminary. For internal
564 purposes it's necessary to have a path_conv::check function which
565 takes a UNICODE_STRING src path, otherwise we waste a lot of time
566 for converting back and forth. The below implementation does
567 realy nothing but converting to char *, until path_conv handles
568 wide-char paths directly. */
570 path_conv::check (const UNICODE_STRING *src, unsigned opt,
571 const suffix_info *suffixes)
574 char *path = tp.c_get ();
576 user_shared->warned_msdos = true;
577 sys_wcstombs (path, NT_MAX_PATH, src->Buffer, src->Length / sizeof (WCHAR));
578 path_conv::check (path, opt, suffixes);
582 path_conv::check (const char *src, unsigned opt,
583 const suffix_info *suffixes)
585 /* The tmp_buf array is used when expanding symlinks. It is NT_MAX_PATH * 2
586 in length so that we can hold the expanded symlink plus a trailer. */
588 char *path_copy = tp.c_get ();
589 char *pathbuf = tp.c_get ();
590 char *tmp_buf = tp.t_get ();
591 char *THIS_path = tp.c_get ();
593 bool need_directory = 0;
594 bool saw_symlinks = 0;
595 bool add_ext = false;
597 char *tail, *path_end;
600 static path_conv last_path_conv;
601 static char last_src[CYG_MAX_PATH];
603 if (*last_src && strcmp (last_src, src) == 0)
605 *this = last_path_conv;
611 if (efault.faulted ())
619 fileattr = INVALID_FILE_ATTRIBUTES;
620 caseinsensitive = OBJ_CASE_INSENSITIVE;
626 cfree (modifiable_path ());
629 close_conv_handle ();
630 memset (&dev, 0, sizeof (dev));
634 cfree ((void *) normalized_path);
635 normalized_path = NULL;
637 int component = 0; // Number of translated components
639 if (!(opt & PC_NULLEMPTY))
647 bool is_msdos = false;
648 /* This loop handles symlink expansion. */
654 is_relpath = !isabspath (src);
655 error = normalize_posix_path (src, path_copy, tail);
665 /* Detect if the user was looking for a directory. We have to strip the
666 trailing slash initially while trying to add extensions but take it
667 into account during processing */
668 if (tail > path_copy + 2 && isslash (tail[-1]))
675 /* Scan path_copy from right to left looking either for a symlink
676 or an actual existing file. If an existing file is found, just
677 return. If a symlink is found, exit the for loop.
678 Also: be careful to preserve the errno returned from
679 symlink.check as the caller may need it. */
680 /* FIXME: Do we have to worry about multiple \'s here? */
681 component = 0; // Number of translated components
682 sym.contents[0] = '\0';
686 for (unsigned pflags_or = opt & (PC_NO_ACCESS_CHECK | PC_KEEP_HANDLE);
690 const suffix_info *suff;
693 /* Don't allow symlink.check to set anything in the path_conv
694 class if we're working on an inner component of the path */
703 full_path = THIS_path;
706 /* Convert to native path spec sans symbolic link info. */
707 error = mount_table->conv_to_win32_path (path_copy, full_path, dev,
713 sym.pflags |= pflags_or;
715 if (dev.major == DEV_CYGDRIVE_MAJOR)
718 fileattr = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_READONLY;
721 fileattr = getfileattr (THIS_path,
722 sym.pflags & MOUNT_NOPOSIX);
727 else if (dev == FH_DEV)
731 fileattr = getfileattr (THIS_path, sym.pflags & MOUNT_NOPOSIX);
732 if (!component && fileattr == INVALID_FILE_ATTRIBUTES)
734 fileattr = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_READONLY;
739 else if (isvirtual_dev (dev.devn))
741 /* FIXME: Calling build_fhandler here is not the right way to handle this. */
742 fhandler_virtual *fh = (fhandler_virtual *) build_fh_dev (dev, path_copy);
743 virtual_ftype_t file_type = fh->exists ();
744 if (file_type == virt_symlink)
747 symlen = sym.set (fh->get_filebuf ());
755 fileattr = FILE_ATTRIBUTE_DIRECTORY;
762 goto is_virtual_symlink;
779 /* Access to real file or directory via block device
780 entry in /proc/sys. Convert to real file and go with
783 goto is_fs_via_procsys;
785 /* Block special device. If the trailing slash has been
786 requested, the target is the root directory of the
787 filesystem on this block device. So we convert this to
788 a real file and attach the backslash. */
789 if (component == 0 && need_directory)
792 strcat (full_path, "\\");
793 fileattr = FILE_ATTRIBUTE_DIRECTORY
794 | FILE_ATTRIBUTE_DEVICE;
800 fileattr = FILE_ATTRIBUTE_DEVICE;
804 fileattr = INVALID_FILE_ATTRIBUTES;
805 goto virtual_component_retry;
807 if (component == 0 || dev.devn != FH_NETDRIVE)
808 path_flags |= PATH_RO;
811 /* devn should not be a device. If it is, then stop parsing now. */
812 else if (dev.devn != FH_FS)
815 path_flags = sym.pflags;
821 goto out; /* Found a device. Stop parsing. */
824 /* If path is only a drivename, Windows interprets it as the
825 current working directory on this drive instead of the root
826 dir which is what we want. So we need the trailing backslash
828 if (full_path[0] && full_path[1] == ':' && full_path[2] == '\0')
834 /* If the incoming path was given in DOS notation, always treat
835 it as caseinsensitive,noacl path. This must be set before
836 calling sym.check, otherwise the path is potentially treated
839 sym.pflags |= PATH_NOPOSIX | PATH_NOACL;
843 symlen = sym.check (full_path, suff, fs, conv_handle);
854 dev.parse (sym.major, sym.minor);
857 fileattr = sym.fileattr;
861 if (sym.pflags & PATH_SOCKET)
868 fileattr = sym.fileattr;
876 fileattr = sym.fileattr;
877 path_flags = sym.pflags;
880 /* If symlink.check found an existing non-symlink file, then
881 it sets the appropriate flag. It also sets any suffix found
883 if (!sym.issymlink && sym.fileattr != INVALID_FILE_ATTRIBUTES)
888 else if (!(sym.fileattr & FILE_ATTRIBUTE_DIRECTORY))
893 goto out; // file found
895 /* Found a symlink if symlen > 0. If component == 0, then the
896 src path itself was a symlink. If !follow_mode then
897 we're done. Otherwise we have to insert the path found
898 into the full path that we are building and perform all of
899 these operations again on the newly derived path. */
903 if (component == 0 && !need_directory
904 && (!(opt & PC_SYM_FOLLOW)
905 || (is_rep_symlink () && (opt & PC_SYM_NOFOLLOW_REP))))
907 set_symlink (symlen); // last component of path is a symlink.
908 if (opt & PC_SYM_CONTENTS)
910 strcpy (THIS_path, sym.contents);
916 /* Following a symlink we can't trust the collected filesystem
917 information any longer. */
919 /* Close handle, if we have any. Otherwise we're collecting
920 handles while following symlinks. */
921 conv_handle.close ();
924 else if (sym.error && sym.error != ENOENT)
929 /* No existing file found. */
931 virtual_component_retry:
932 /* Find the new "tail" of the path, e.g. in '/for/bar/baz',
934 if (tail != path_end)
936 while (--tail > path_copy + 1 && *tail != '/') {}
937 /* Exit loop if there is no tail or we are at the
938 beginning of a UNC path */
939 if (tail <= path_copy + 1)
940 goto out; // all done
942 /* Haven't found an existing pathname component yet.
943 Pinch off the tail and try again. */
948 /* Arrive here if above loop detected a symlink. */
949 if (++loop > SYMLOOP_MAX)
951 error = ELOOP; // Eep.
958 /* Place the link content, possibly with head and/or tail, in tmp_buf */
961 if (isabspath (sym.contents))
962 headptr = tmp_buf; /* absolute path */
965 /* Copy the first part of the path (with ending /) and point to the end. */
966 char *prevtail = tail;
967 while (--prevtail > path_copy && *prevtail != '/') {}
968 int headlen = prevtail - path_copy + 1;;
969 memcpy (tmp_buf, path_copy, headlen);
970 headptr = &tmp_buf[headlen];
973 /* Make sure there is enough space */
974 if (headptr + symlen >= tmp_buf + (2 * NT_MAX_PATH))
977 error = ENAMETOOLONG;
978 set_path ("::ENAMETOOLONG::");
982 /* Copy the symlink contents to the end of tmp_buf.
984 for (char *p = sym.contents; *p; p++)
985 *headptr++ = *p == '\\' ? '/' : *p;
988 /* Copy any tail component (with the 0) */
989 if (tail++ < path_end)
991 /* Add a slash if needed. There is space. */
992 if (*(headptr - 1) != '/')
994 int taillen = path_end - tail + 1;
995 if (headptr + taillen > tmp_buf + (2 * NT_MAX_PATH))
997 memcpy (headptr, tail, taillen);
1000 /* Evaluate everything all over again. */
1004 if (!(opt & PC_SYM_CONTENTS))
1008 set_path (THIS_path);
1010 add_ext_from_sym (sym);
1011 if (dev.devn == FH_NETDRIVE && component)
1013 /* This case indicates a non-existant resp. a non-retrievable
1014 share. This happens for instance if the share is a printer.
1015 In this case the path must not be treated like a FH_NETDRIVE,
1016 but like a FH_FS instead, so the usual open call for files
1020 else if (isproc_dev (dev.devn) && fileattr == INVALID_FILE_ATTRIBUTES)
1022 /* FIXME: Usually we don't set error to ENOENT if a file doesn't
1023 exist. This is typically indicated by the fileattr content.
1024 So, why here? The downside is that cygwin_conv_path just gets
1025 an error for these paths so it reports the error back to the
1026 application. Unlike in all other cases of non-existant files,
1027 for which check doesn't set error, so cygwin_conv_path just
1028 returns the path, as intended. */
1032 else if (!need_directory || error)
1033 /* nothing to do */;
1034 else if (fileattr == INVALID_FILE_ATTRIBUTES)
1035 strcat (modifiable_path (), "\\"); /* Reattach trailing dirsep in native path. */
1036 else if (fileattr & FILE_ATTRIBUTE_DIRECTORY)
1037 path_flags &= ~PATH_SYMLINK;
1040 debug_printf ("%s is a non-directory", path);
1047 if (strncmp (path, "\\\\.\\", 4))
1049 if (!tail || tail == path)
1051 else if (tail[-1] != '\\')
1060 /* If FS hasn't been checked already in symlink_info::check, do so now. */
1061 if (fs.inited ()|| fs.update (get_nt_native_path (), NULL))
1063 /* Incoming DOS paths are treated like DOS paths in native
1064 Windows applications. No ACLs, just default settings. */
1066 fs.has_acls (false);
1067 debug_printf ("this->path(%s), has_acls(%d)", path, fs.has_acls ());
1068 /* CV: We could use this->has_acls() but I want to make sure that
1069 we don't forget that the PATH_NOACL flag must be taken into
1071 if (!(path_flags & PATH_NOACL) && fs.has_acls ())
1072 set_exec (0); /* We really don't know if this is executable or not here
1073 but set it to not executable since it will be figured out
1074 later by anything which cares about this. */
1076 /* If the FS has been found to have unrelibale inodes, note
1077 that in path_flags. */
1078 if (!fs.hasgood_inode ())
1079 path_flags |= PATH_IHASH;
1080 /* If the OS is caseinsensitive or the FS is caseinsensitive,
1081 don't handle path casesensitive. */
1082 if (cygwin_shared->obcaseinsensitive || fs.caseinsensitive ())
1083 path_flags |= PATH_NOPOSIX;
1084 caseinsensitive = (path_flags & PATH_NOPOSIX)
1085 ? OBJ_CASE_INSENSITIVE : 0;
1086 if (exec_state () != dont_know_if_executable)
1090 else if (issymlink () || issocket ())
1094 if (opt & PC_NOFULL)
1098 mkrelpath (this->modifiable_path (), !!caseinsensitive);
1099 /* Invalidate wide_path so that wide relpath can be created
1100 in later calls to get_nt_native_path or get_wide_win32_path. */
1107 size_t n = strlen (this->path);
1108 /* Do not add trailing \ to UNC device names like \\.\a: */
1109 if (this->path[n - 1] != '\\' &&
1110 (strncmp (this->path, "\\\\.\\", 4) != 0))
1112 this->modifiable_path ()[n] = '\\';
1113 this->modifiable_path ()[n + 1] = '\0';
1119 set_has_symlinks ();
1121 if ((opt & PC_POSIX))
1123 if (tail < path_end && tail > path_copy + 1)
1125 set_normalized_path (path_copy);
1126 if (is_msdos && !(opt & PC_NOWARN))
1133 last_path_conv = *this;
1134 strcpy (last_src, src);
1139 path_conv::~path_conv ()
1141 if (normalized_path)
1143 cfree ((void *) normalized_path);
1144 normalized_path = NULL;
1148 cfree (modifiable_path ());
1156 close_conv_handle ();
1160 path_conv::is_binary ()
1163 PWCHAR bintest = tp.w_get ();
1166 return GetBinaryTypeW (get_wide_win32_path (bintest), &bin)
1167 && (bin == SCS_32BIT_BINARY || bin == SCS_64BIT_BINARY);
1170 /* Normalize a Win32 path.
1171 /'s are converted to \'s in the process.
1172 All duplicate \'s, except for 2 leading \'s, are deleted.
1174 The result is 0 for success, or an errno error value.
1175 FIXME: A lot of this should be mergeable with the POSIX critter. */
1177 normalize_win32_path (const char *src, char *dst, char *&tail)
1179 const char *src_start = src;
1180 bool beg_src_slash = isdirsep (src[0]);
1183 /* Skip long path name prefixes in Win32 or NT syntax. */
1184 if (beg_src_slash && (src[1] == '?' || isdirsep (src[1]))
1185 && src[2] == '?' && isdirsep (src[3]))
1188 if (src[1] != ':') /* native UNC path */
1189 src += 2; /* Fortunately the first char is not copied... */
1191 beg_src_slash = false;
1193 if (beg_src_slash && isdirsep (src[1]))
1195 if (isdirsep (src[2]))
1197 /* More than two slashes are just folded into one. */
1199 while (isdirsep (src[1]))
1204 /* Two slashes start a network or device path. */
1207 if (src[1] == '.' && isdirsep (src[2]))
1218 /* Always convert drive letter to uppercase for case sensitivity. */
1219 *tail++ = cyg_toupper (*src++);
1220 else if (*src != '/')
1223 tail += cygheap->cwd.get_drive (dst);
1224 else if (!cygheap->cwd.get (dst, 0))
1225 return get_errno ();
1228 tail = strchr (tail, '\0');
1229 if (tail[-1] != '\\')
1237 /* Strip duplicate /'s. */
1238 if (isdirsep (src[0]) && isdirsep (src[1]))
1241 else if (src[0] == '.' && isdirsep (src[1])
1242 && (src == src_start || isdirsep (src[-1])))
1245 /* Backup if "..". */
1246 else if (src[0] == '.' && src[1] == '.'
1247 /* dst must be greater than dst_start */
1248 && tail[-1] == '\\')
1250 if (!isdirsep (src[2]) && src[2] != '\0')
1254 /* Back up over /, but not if it's the first one. */
1257 /* Now back up to the next /. */
1258 while (tail > dst + 1 && tail[-1] != '\\' && tail[-2] != ':')
1261 if (isdirsep (*src))
1265 /* Otherwise, add char to result. */
1274 if ((tail - dst) >= NT_MAX_PATH)
1275 return ENAMETOOLONG;
1277 if (tail > dst + 1 && tail[-1] == '.' && tail[-2] == '\\')
1280 debug_printf ("%s = normalize_win32_path (%s)", dst, src_start);
1284 /* Various utilities. */
1286 /* nofinalslash: Remove trailing / and \ from SRC (except for the
1287 first one). It is ok for src == dst. */
1290 nofinalslash (const char *src, char *dst)
1292 int len = strlen (src);
1294 memcpy (dst, src, len + 1);
1295 while (len > 1 && isdirsep (dst[--len]))
1299 /* conv_path_list: Convert a list of path names to/from Win32/POSIX. */
1302 conv_path_list (const char *src, char *dst, size_t size, int to_posix)
1305 char src_delim, dst_delim;
1306 cygwin_conv_path_t conv_fn;
1313 conv_fn = CCP_WIN_A_TO_POSIX | CCP_RELATIVE;
1319 conv_fn = CCP_POSIX_TO_WIN_A | CCP_RELATIVE;
1323 len = strlen (src) + 1;
1324 if (len <= NT_MAX_PATH * sizeof (WCHAR))
1325 srcbuf = (char *) tp.w_get ();
1327 srcbuf = (char *) alloca (len);
1331 bool saw_empty = false;
1334 char *srcpath = srcbuf;
1335 char *s = strccpy (srcpath, &src, src_delim);
1336 size_t len = s - srcpath;
1337 if (len >= NT_MAX_PATH)
1342 /* Paths in Win32 path lists in the environment (%Path%), are often
1343 enclosed in quotes (usually paths with spaces). Trailing backslashes
1344 are common, too. Remove them. */
1345 if (to_posix == ENV_CVT && len)
1347 if (*srcpath == '"')
1353 while (len && s[-1] == '\\')
1362 err = cygwin_conv_path (conv_fn, srcpath, d, size - (d - dst));
1367 err = cygwin_conv_path (conv_fn, ".", d, size - (d - dst));
1371 if (to_posix == ENV_CVT)
1377 d = strchr (d, '\0');
1391 /********************** Symbolic Link Support **************************/
1393 /* Create a symlink from FROMPATH to TOPATH. */
1395 /* If TRUE create symlinks as Windows shortcuts, if false create symlinks
1396 as normal files with magic number and system bit set. */
1397 bool allow_winsymlinks = false;
1400 symlink (const char *oldpath, const char *newpath)
1402 return symlink_worker (oldpath, newpath, allow_winsymlinks, false);
1406 symlink_worker (const char *oldpath, const char *newpath, bool use_winsym,
1411 path_conv win32_newpath, win32_oldpath;
1413 SECURITY_ATTRIBUTES sa = sec_none_nih;
1414 OBJECT_ATTRIBUTES attr;
1418 ULONG access = DELETE | FILE_GENERIC_WRITE;
1421 bool mk_winsym = use_winsym;
1422 bool has_trailing_dirsep = false;
1424 /* POSIX says that empty 'newpath' is invalid input while empty
1425 'oldpath' is valid -- it's symlink resolver job to verify if
1426 symlink contents point to existing filesystem object */
1428 if (efault.faulted (EFAULT))
1430 if (!*oldpath || !*newpath)
1436 if (strlen (oldpath) > SYMLINK_MAX)
1438 set_errno (ENAMETOOLONG);
1442 /* Trailing dirsep is a no-no. */
1443 len = strlen (newpath);
1444 has_trailing_dirsep = isdirsep (newpath[len - 1]);
1445 if (has_trailing_dirsep)
1447 newpath = strdup (newpath);
1448 ((char *) newpath)[len - 1] = '\0';
1451 check_opt = PC_SYM_NOFOLLOW | PC_POSIX | (isdevice ? PC_NOWARN : 0);
1452 /* We need the normalized full path below. */
1453 win32_newpath.check (newpath, check_opt, stat_suffixes);
1454 /* MVFS doesn't handle the SYSTEM DOS attribute, but it handles the R/O
1455 attribute. Therefore we create symlinks on MVFS always as shortcuts. */
1456 mk_winsym |= win32_newpath.fs_is_mvfs ();
1458 if (mk_winsym && !win32_newpath.exists ()
1459 && (isdevice || !win32_newpath.fs_is_nfs ()))
1461 char *newplnk = tp.c_get ();
1462 stpcpy (stpcpy (newplnk, newpath), ".lnk");
1463 win32_newpath.check (newplnk, check_opt);
1466 if (win32_newpath.error)
1468 set_errno (win32_newpath.error);
1472 syscall_printf ("symlink (%s, %S)", oldpath,
1473 win32_newpath.get_nt_native_path ());
1475 if ((!isdevice && win32_newpath.exists ())
1476 || win32_newpath.is_auto_device ())
1481 if (has_trailing_dirsep && !win32_newpath.exists ())
1487 if (!isdevice && win32_newpath.fs_is_nfs ())
1489 /* On NFS, create symlinks by calling NtCreateFile with an EA of type
1490 NfsSymlinkTargetName containing ... the symlink target name. */
1491 PFILE_FULL_EA_INFORMATION pffei = (PFILE_FULL_EA_INFORMATION) tp.w_get ();
1492 pffei->NextEntryOffset = 0;
1494 pffei->EaNameLength = sizeof (NFS_SYML_TARGET) - 1;
1495 char *EaValue = stpcpy (pffei->EaName, NFS_SYML_TARGET) + 1;
1496 pffei->EaValueLength = sizeof (WCHAR) *
1497 (sys_mbstowcs ((PWCHAR) EaValue, NT_MAX_PATH, oldpath) - 1);
1498 status = NtCreateFile (&fh, FILE_WRITE_DATA | FILE_WRITE_EA | SYNCHRONIZE,
1499 win32_newpath.get_object_attr (attr, sa),
1500 &io, NULL, FILE_ATTRIBUTE_SYSTEM,
1501 FILE_SHARE_VALID_FLAGS, FILE_CREATE,
1502 FILE_SYNCHRONOUS_IO_NONALERT
1503 | FILE_OPEN_FOR_BACKUP_INTENT,
1504 pffei, NT_MAX_PATH * sizeof (WCHAR));
1505 if (!NT_SUCCESS (status))
1507 __seterrno_from_nt_status (status);
1517 ITEMIDLIST *pidl = NULL;
1518 size_t full_len = 0;
1519 unsigned short oldpath_len, desc_len, relpath_len, pidl_len = 0;
1520 char desc[MAX_PATH + 1], *relpath;
1524 /* First create an IDLIST to learn how big our shortcut is
1528 /* The symlink target is relative to the directory in which
1529 the symlink gets created, not relative to the cwd. Therefore
1530 we have to mangle the path quite a bit before calling path_conv. */
1531 if (isabspath (oldpath))
1532 win32_oldpath.check (oldpath,
1537 len = strrchr (win32_newpath.normalized_path, '/')
1538 - win32_newpath.normalized_path + 1;
1539 char *absoldpath = tp.t_get ();
1540 stpcpy (stpncpy (absoldpath, win32_newpath.normalized_path, len),
1542 win32_oldpath.check (absoldpath, PC_SYM_NOFOLLOW, stat_suffixes);
1544 if (SUCCEEDED (SHGetDesktopFolder (&psl)))
1546 WCHAR wc_path[win32_oldpath.get_wide_win32_path_len () + 1];
1547 win32_oldpath.get_wide_win32_path (wc_path);
1548 /* Amazing but true: Even though the ParseDisplayName method
1549 takes a wide char path name, it does not understand the
1550 Win32 prefix for long pathnames! So we have to tack off
1551 the prefix and convert the path to the "normal" syntax
1552 for ParseDisplayName. */
1553 WCHAR *wc = wc_path + 4;
1554 if (wc[1] != L':') /* native UNC path */
1557 if (SUCCEEDED (res = psl->ParseDisplayName (NULL, NULL, wc, NULL,
1562 for (p = pidl; p->mkid.cb > 0;
1563 p = (ITEMIDLIST *)((char *) p + p->mkid.cb))
1565 pidl_len = (char *) p - (char *) pidl + 2;
1570 /* Compute size of shortcut file. */
1571 full_len = sizeof (win_shortcut_hdr);
1573 full_len += sizeof (unsigned short) + pidl_len;
1574 oldpath_len = strlen (oldpath);
1575 /* Unfortunately the length of the description is restricted to a
1576 length of MAX_PATH up to NT4, and to a length of 2000 bytes
1577 since W2K. We don't want to add considerations for the different
1578 lengths and even 2000 bytes is not enough for long path names.
1579 So what we do here is to set the description to the POSIX path
1580 only if the path is not longer than MAX_PATH characters. We
1581 append the full path name after the regular shortcut data
1582 (see below), which works fine with Windows Explorer as well
1583 as older Cygwin versions (as long as the whole file isn't bigger
1584 than 8K). The description field is only used for backward
1585 compatibility to older Cygwin versions and those versions are
1586 not capable of handling long path names anyway. */
1587 desc_len = stpcpy (desc, oldpath_len > MAX_PATH
1588 ? "[path too long]" : oldpath) - desc;
1589 full_len += sizeof (unsigned short) + desc_len;
1590 /* Devices get the oldpath string unchanged as relative path. */
1593 relpath_len = oldpath_len;
1594 stpcpy (relpath = tp.c_get (), oldpath);
1598 relpath_len = strlen (win32_oldpath.get_win32 ());
1599 stpcpy (relpath = tp.c_get (), win32_oldpath.get_win32 ());
1601 full_len += sizeof (unsigned short) + relpath_len;
1602 full_len += sizeof (unsigned short) + oldpath_len;
1603 /* 1 byte more for trailing 0 written by stpcpy. */
1604 if (full_len < NT_MAX_PATH * sizeof (WCHAR))
1605 buf = (char *) tp.w_get ();
1607 buf = (char *) alloca (full_len + 1);
1609 /* Create shortcut header */
1610 win_shortcut_hdr *shortcut_header = (win_shortcut_hdr *) buf;
1611 memset (shortcut_header, 0, sizeof *shortcut_header);
1612 shortcut_header->size = sizeof *shortcut_header;
1613 shortcut_header->magic = GUID_shortcut;
1614 shortcut_header->flags = (WSH_FLAG_DESC | WSH_FLAG_RELPATH);
1616 shortcut_header->flags |= WSH_FLAG_IDLIST;
1617 shortcut_header->run = SW_NORMAL;
1618 cp = buf + sizeof (win_shortcut_hdr);
1623 *(unsigned short *)cp = pidl_len;
1624 memcpy (cp += 2, pidl, pidl_len);
1626 CoTaskMemFree (pidl);
1629 /* Create description */
1630 *(unsigned short *)cp = desc_len;
1631 cp = stpcpy (cp += 2, desc);
1633 /* Create relpath */
1634 *(unsigned short *)cp = relpath_len;
1635 cp = stpcpy (cp += 2, relpath);
1637 /* Append the POSIX path after the regular shortcut data for
1638 the long path support. */
1639 unsigned short *plen = (unsigned short *) cp;
1641 *(PWCHAR) cp = 0xfeff; /* BOM */
1643 *plen = sys_mbstowcs ((PWCHAR) cp, NT_MAX_PATH, oldpath) * sizeof (WCHAR);
1648 /* Default technique creating a symlink. */
1649 buf = (char *) tp.w_get ();
1650 cp = stpcpy (buf, SYMLINK_COOKIE);
1651 *(PWCHAR) cp = 0xfeff; /* BOM */
1653 /* Note that the terminating nul is written. */
1654 cp += sys_mbstowcs ((PWCHAR) cp, NT_MAX_PATH, oldpath) * sizeof (WCHAR);
1657 if (isdevice && win32_newpath.exists ())
1659 status = NtOpenFile (&fh, FILE_WRITE_ATTRIBUTES,
1660 win32_newpath.get_object_attr (attr, sa),
1661 &io, 0, FILE_OPEN_FOR_BACKUP_INTENT);
1662 if (!NT_SUCCESS (status))
1664 __seterrno_from_nt_status (status);
1667 status = NtSetAttributesFile (fh, FILE_ATTRIBUTE_NORMAL);
1669 if (!NT_SUCCESS (status))
1671 __seterrno_from_nt_status (status);
1675 else if (!isdevice && win32_newpath.has_acls ())
1676 /* If the filesystem supports ACLs, we will overwrite the DACL after the
1677 call to NtCreateFile. This requires a handle with READ_CONTROL and
1678 WRITE_DAC access, otherwise get_file_sd and set_file_sd both have to
1679 open the file again. */
1680 access |= READ_CONTROL | WRITE_DAC;
1682 status = NtCreateFile (&fh, access, win32_newpath.get_object_attr (attr, sa),
1683 &io, NULL, FILE_ATTRIBUTE_NORMAL,
1684 FILE_SHARE_VALID_FLAGS,
1685 isdevice ? FILE_OVERWRITE_IF : FILE_CREATE,
1686 FILE_SYNCHRONOUS_IO_NONALERT
1687 | FILE_NON_DIRECTORY_FILE
1688 | FILE_OPEN_FOR_BACKUP_INTENT,
1690 if (!NT_SUCCESS (status))
1692 __seterrno_from_nt_status (status);
1695 if (win32_newpath.has_acls ())
1696 set_file_attribute (fh, win32_newpath, ILLEGAL_UID, ILLEGAL_GID,
1697 (io.Information == FILE_CREATED ? S_JUSTCREATED : 0)
1698 | S_IFLNK | STD_RBITS | STD_WBITS);
1699 status = NtWriteFile (fh, NULL, NULL, NULL, &io, buf, cp - buf, NULL, NULL);
1700 if (NT_SUCCESS (status) && io.Information == (ULONG) (cp - buf))
1702 status = NtSetAttributesFile (fh, mk_winsym ? FILE_ATTRIBUTE_READONLY
1703 : FILE_ATTRIBUTE_SYSTEM);
1704 if (!NT_SUCCESS (status))
1705 debug_printf ("Setting attributes failed, status = %p", status);
1710 __seterrno_from_nt_status (status);
1711 FILE_DISPOSITION_INFORMATION fdi = { TRUE };
1712 status = NtSetInformationFile (fh, &io, &fdi, sizeof fdi,
1713 FileDispositionInformation);
1714 if (!NT_SUCCESS (status))
1715 debug_printf ("Setting delete dispostion failed, status = %p", status);
1720 syscall_printf ("%d = symlink_worker (%s, %s, %d, %d)", res, oldpath,
1721 newpath, mk_winsym, isdevice);
1722 if (has_trailing_dirsep)
1723 free ((void *) newpath);
1728 cmp_shortcut_header (win_shortcut_hdr *file_header)
1730 /* A Cygwin or U/Win shortcut only contains a description and a relpath.
1731 Cygwin shortcuts also might contain an ITEMIDLIST. The run type is
1732 always set to SW_NORMAL. */
1733 return file_header->size == sizeof (win_shortcut_hdr)
1734 && !memcmp (&file_header->magic, &GUID_shortcut, sizeof GUID_shortcut)
1735 && (file_header->flags & ~WSH_FLAG_IDLIST)
1736 == (WSH_FLAG_DESC | WSH_FLAG_RELPATH)
1737 && file_header->run == SW_NORMAL;
1741 symlink_info::check_shortcut (HANDLE h)
1744 win_shortcut_hdr *file_header;
1750 FILE_STANDARD_INFORMATION fsi;
1751 LARGE_INTEGER off = { QuadPart:0LL };
1753 status = NtQueryInformationFile (h, &io, &fsi, sizeof fsi,
1754 FileStandardInformation);
1755 if (!NT_SUCCESS (status))
1760 if (fsi.EndOfFile.QuadPart <= sizeof (win_shortcut_hdr)
1761 || fsi.EndOfFile.QuadPart > 4 * 65536)
1763 if (fsi.EndOfFile.LowPart < NT_MAX_PATH * sizeof (WCHAR))
1764 buf = (char *) tp.w_get ();
1766 buf = (char *) alloca (fsi.EndOfFile.LowPart + 1);
1767 status = NtReadFile (h, NULL, NULL, NULL, &io, buf, fsi.EndOfFile.LowPart,
1769 if (!NT_SUCCESS (status))
1771 if (status != STATUS_END_OF_FILE)
1775 file_header = (win_shortcut_hdr *) buf;
1776 if (io.Information != fsi.EndOfFile.LowPart
1777 || !cmp_shortcut_header (file_header))
1779 cp = buf + sizeof (win_shortcut_hdr);
1780 if (file_header->flags & WSH_FLAG_IDLIST) /* Skip ITEMIDLIST */
1781 cp += *(unsigned short *) cp + 2;
1782 if (!(len = *(unsigned short *) cp))
1785 /* Check if this is a device file - these start with the sequence :\\ */
1786 if (strncmp (cp, ":\\", 2) == 0)
1787 res = strlen (strcpy (contents, cp)); /* Don't mess with device files */
1790 /* Has appended full path? If so, use it instead of description. */
1791 unsigned short relpath_len = *(unsigned short *) (cp + len);
1792 if (cp + len + 2 + relpath_len < buf + fsi.EndOfFile.LowPart)
1794 cp += len + 2 + relpath_len;
1795 len = *(unsigned short *) cp;
1798 if (*(PWCHAR) cp == 0xfeff) /* BOM */
1800 char *tmpbuf = tp.c_get ();
1801 if (sys_wcstombs (tmpbuf, NT_MAX_PATH, (PWCHAR) (cp + 2))
1804 res = posixify (tmpbuf);
1806 else if (len > SYMLINK_MAX)
1811 res = posixify (cp);
1814 if (res) /* It's a symlink. */
1815 pflags |= PATH_SYMLINK | PATH_LNK;
1820 symlink_info::check_sysfile (HANDLE h)
1823 char cookie_buf[sizeof (SYMLINK_COOKIE) - 1];
1824 char *srcbuf = tp.c_get ();
1828 bool interix_symlink = false;
1829 LARGE_INTEGER off = { QuadPart:0LL };
1831 status = NtReadFile (h, NULL, NULL, NULL, &io, cookie_buf,
1832 sizeof (cookie_buf), &off, NULL);
1833 if (!NT_SUCCESS (status))
1835 debug_printf ("ReadFile1 failed %p", status);
1836 if (status != STATUS_END_OF_FILE)
1840 off.QuadPart = io.Information;
1841 if (io.Information == sizeof (cookie_buf)
1842 && memcmp (cookie_buf, SYMLINK_COOKIE, sizeof (cookie_buf)) == 0)
1844 /* It's a symlink. */
1845 pflags |= PATH_SYMLINK;
1847 else if (io.Information == sizeof (cookie_buf)
1848 && memcmp (cookie_buf, SOCKET_COOKIE, sizeof (cookie_buf)) == 0)
1849 pflags |= PATH_SOCKET;
1850 else if (io.Information >= sizeof (INTERIX_SYMLINK_COOKIE)
1851 && memcmp (cookie_buf, INTERIX_SYMLINK_COOKIE,
1852 sizeof (INTERIX_SYMLINK_COOKIE) - 1) == 0)
1854 /* It's an Interix symlink. */
1855 pflags |= PATH_SYMLINK;
1856 interix_symlink = true;
1857 /* Interix symlink cookies are shorter than Cygwin symlink cookies, so
1858 in case of an Interix symlink cooky we have read too far into the
1859 file. Set file pointer back to the position right after the cookie. */
1860 off.QuadPart = sizeof (INTERIX_SYMLINK_COOKIE) - 1;
1862 if (pflags & PATH_SYMLINK)
1864 status = NtReadFile (h, NULL, NULL, NULL, &io, srcbuf,
1865 NT_MAX_PATH, &off, NULL);
1866 if (!NT_SUCCESS (status))
1868 debug_printf ("ReadFile2 failed");
1869 if (status != STATUS_END_OF_FILE)
1872 else if (*(PWCHAR) srcbuf == 0xfeff /* BOM */
1875 /* Add trailing 0 to Interix symlink target. Skip BOM in Cygwin
1877 if (interix_symlink)
1878 ((PWCHAR) srcbuf)[io.Information / sizeof (WCHAR)] = L'\0';
1881 char *tmpbuf = tp.c_get ();
1882 if (sys_wcstombs (tmpbuf, NT_MAX_PATH, (PWCHAR) srcbuf)
1884 debug_printf ("symlink string too long");
1886 res = posixify (tmpbuf);
1888 else if (io.Information > SYMLINK_MAX + 1)
1889 debug_printf ("symlink string too long");
1891 res = posixify (srcbuf);
1897 symlink_info::check_reparse_point (HANDLE h)
1902 PREPARSE_DATA_BUFFER rp = (PREPARSE_DATA_BUFFER) tp.c_get ();
1903 UNICODE_STRING subst;
1904 char srcbuf[SYMLINK_MAX + 7];
1906 status = NtFsControlFile (h, NULL, NULL, NULL, &io, FSCTL_GET_REPARSE_POINT,
1907 NULL, 0, (LPVOID) rp,
1908 MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
1909 if (!NT_SUCCESS (status))
1911 debug_printf ("NtFsControlFile(FSCTL_GET_REPARSE_POINT) failed, %p",
1916 if (rp->ReparseTag == IO_REPARSE_TAG_SYMLINK)
1917 RtlInitCountedUnicodeString (&subst,
1918 (WCHAR *)((char *)rp->SymbolicLinkReparseBuffer.PathBuffer
1919 + rp->SymbolicLinkReparseBuffer.SubstituteNameOffset),
1920 rp->SymbolicLinkReparseBuffer.SubstituteNameLength);
1921 else if (rp->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
1923 RtlInitCountedUnicodeString (&subst,
1924 (WCHAR *)((char *)rp->MountPointReparseBuffer.PathBuffer
1925 + rp->MountPointReparseBuffer.SubstituteNameOffset),
1926 rp->MountPointReparseBuffer.SubstituteNameLength);
1927 if (RtlEqualUnicodePathPrefix (&subst, &ro_u_volume, TRUE))
1929 /* Volume mount point. Not treated as symlink. The return
1930 value of -1 is a hint for the caller to treat this as a
1931 volume mount point. */
1937 /* Maybe it's a reparse point, but it's certainly not one we
1938 recognize. Drop the REPARSE file attribute so we don't even
1939 try to use the flag for some special handling. It's just some
1940 arbitrary file or directory for us. */
1941 fileattr &= ~FILE_ATTRIBUTE_REPARSE_POINT;
1944 sys_wcstombs (srcbuf, SYMLINK_MAX + 7, subst.Buffer,
1945 subst.Length / sizeof (WCHAR));
1946 pflags |= PATH_SYMLINK | PATH_REP;
1947 /* A symlink is never a directory. */
1948 fileattr &= ~FILE_ATTRIBUTE_DIRECTORY;
1949 return posixify (srcbuf);
1953 symlink_info::check_nfs_symlink (HANDLE h)
1959 FILE_GET_EA_INFORMATION fgei;
1960 char buf[sizeof (NFS_SYML_TARGET)];
1962 PFILE_FULL_EA_INFORMATION pffei;
1965 /* To find out if the file is a symlink and to get the symlink target,
1966 try to fetch the NfsSymlinkTargetName EA. */
1967 fgei_buf.fgei.NextEntryOffset = 0;
1968 fgei_buf.fgei.EaNameLength = sizeof (NFS_SYML_TARGET) - 1;
1969 stpcpy (fgei_buf.fgei.EaName, NFS_SYML_TARGET);
1970 pffei = (PFILE_FULL_EA_INFORMATION) tp.w_get ();
1971 status = NtQueryEaFile (h, &io, pffei, NT_MAX_PATH * sizeof (WCHAR), TRUE,
1972 &fgei_buf.fgei, sizeof fgei_buf, NULL, TRUE);
1973 if (NT_SUCCESS (status) && pffei->EaValueLength > 0)
1975 PWCHAR spath = (PWCHAR)
1976 (pffei->EaName + pffei->EaNameLength + 1);
1977 res = sys_wcstombs (contents, SYMLINK_MAX + 1,
1978 spath, pffei->EaValueLength) - 1;
1979 pflags |= PATH_SYMLINK;
1985 symlink_info::posixify (char *srcbuf)
1987 /* The definition for a path in a native symlink is a bit weird. The Flags
1988 value seem to contain 0 for absolute paths (stored as NT native path)
1989 and 1 for relative paths. Relative paths are paths not starting with a
1990 drive letter. These are not converted to NT native, but stored as
1991 given. A path starting with a single backslash is relative to the
1992 current drive thus a "relative" value (Flags == 1).
1993 Funny enough it's possible to store paths with slashes instead of
1994 backslashes, but they are evaluated incorrectly by subsequent Windows
1995 calls like CreateFile (ERROR_INVALID_NAME). So, what we do here is to
1996 take paths starting with slashes at face value, evaluating them as
1997 Cygwin specific POSIX paths.
1998 A path starting with two slashes(!) or backslashes is converted into an
1999 NT UNC path. Unfortunately, in contrast to POSIX rules, paths starting
2000 with three or more (back)slashes are also converted into UNC paths,
2001 just incorrectly sticking to one redundant leading backslash. We go
2002 along with this behaviour to avoid scenarios in which native tools access
2003 other files than Cygwin.
2004 The above rules are used exactly the same way on Cygwin specific symlinks
2005 (sysfiles and shortcuts) to eliminate non-POSIX paths in the output. */
2007 /* Eliminate native NT prefixes. */
2008 if (srcbuf[0] == '\\' && !strncmp (srcbuf + 1, "??\\", 3))
2011 if (srcbuf[1] != ':') /* native UNC path */
2012 *(srcbuf += 2) = '\\';
2014 if (isdrive (srcbuf))
2015 mount_table->conv_to_posix_path (srcbuf, contents, 0);
2016 else if (srcbuf[0] == '\\')
2018 if (srcbuf[1] == '\\') /* UNC path */
2019 slashify (srcbuf, contents, 0);
2020 else /* Paths starting with \ are current drive relative. */
2022 char cvtbuf[SYMLINK_MAX + 1];
2024 stpcpy (cvtbuf + cygheap->cwd.get_drive (cvtbuf), srcbuf);
2025 mount_table->conv_to_posix_path (cvtbuf, contents, 0);
2028 else /* Everything else is taken as is. */
2029 slashify (srcbuf, contents, 0);
2030 return strlen (contents);
2039 SCAN_JUSTCHECKTHIS, /* Never try to append a suffix. */
2047 const suffix_info *suffixes, *suffixes_start;
2052 char *has (const char *, const suffix_info *);
2054 int lnk_match () {return nextstate >= SCAN_APPENDLNK;}
2058 suffix_scan::has (const char *in_path, const suffix_info *in_suffixes)
2060 nextstate = SCAN_BEG;
2061 suffixes = suffixes_start = in_suffixes;
2063 const char *fname = strrchr (in_path, '\\');
2064 fname = fname ? fname + 1 : in_path;
2065 char *ext_here = strrchr (fname, '.');
2067 eopath = strchr (path, '\0');
2074 /* Check if the extension matches a known extension */
2075 for (const suffix_info *ex = in_suffixes; ex->name != NULL; ex++)
2076 if (ascii_strcasematch (ext_here, ex->name))
2078 nextstate = SCAN_JUSTCHECK;
2079 suffixes = NULL; /* Has an extension so don't scan for one. */
2084 /* Didn't match. Use last resort -- .lnk. */
2085 if (ascii_strcasematch (ext_here, ".lnk"))
2087 nextstate = SCAN_HASLNK;
2095 /* Avoid attaching suffixes if the resulting filename would be invalid. */
2096 if (eopath - fname > NAME_MAX - 4)
2098 nextstate = SCAN_JUSTCHECKTHIS;
2105 suffix_scan::next ()
2113 suffixes = suffixes_start;
2116 nextstate = SCAN_LNK;
2119 nextstate = SCAN_EXTRALNK;
2120 /* fall through to suffix checking below */
2123 nextstate = SCAN_APPENDLNK; /* Skip SCAN_BEG */
2126 nextstate = SCAN_DONE;
2129 case SCAN_JUSTCHECK:
2130 nextstate = SCAN_LNK;
2132 case SCAN_JUSTCHECKTHIS:
2133 nextstate = SCAN_DONE;
2136 case SCAN_APPENDLNK:
2137 strcat (eopath, ".lnk");
2138 nextstate = SCAN_DONE;
2145 while (suffixes && suffixes->name)
2146 if (nextstate == SCAN_EXTRALNK && !suffixes->addon)
2150 strcpy (eopath, suffixes->name);
2151 if (nextstate == SCAN_EXTRALNK)
2152 strcat (eopath, ".lnk");
2161 symlink_info::set_error (int in_errno)
2164 if (!(pflags & PATH_NO_ACCESS_CHECK) || in_errno == ENAMETOOLONG || in_errno == EIO)
2169 else if (in_errno == ENOENT)
2173 fileattr = FILE_ATTRIBUTE_NORMAL;
2180 symlink_info::parse_device (const char *contents)
2187 mymajor = strtol (contents += 2, &endptr, 16);
2188 if (endptr == contents)
2189 return isdevice = false;
2192 myminor = strtol (++contents, &endptr, 16);
2193 if (endptr == contents)
2194 return isdevice = false;
2197 mymode = strtol (++contents, &endptr, 16);
2198 if (endptr == contents)
2199 return isdevice = false;
2201 if ((mymode & S_IFMT) == S_IFIFO)
2203 mymajor = _major (FH_FIFO);
2204 myminor = _minor (FH_FIFO);
2210 return isdevice = true;
2213 /* Check if PATH is a symlink. PATH must be a valid Win32 path name.
2215 If PATH is a symlink, put the value of the symlink--the file to
2216 which it points--into BUF. The value stored in BUF is not
2217 necessarily null terminated. BUFLEN is the length of BUF; only up
2218 to BUFLEN characters will be stored in BUF. BUF may be NULL, in
2219 which case nothing will be stored.
2221 Set *SYML if PATH is a symlink.
2223 Set *EXEC if PATH appears to be executable. This is an efficiency
2224 hack because we sometimes have to open the file anyhow. *EXEC will
2225 not be set for every executable file.
2227 Return -1 on error, 0 if PATH is not a symlink, or the length
2228 stored into BUF if PATH is a symlink. */
2231 symlink_info::check (char *path, const suffix_info *suffixes, fs_info &fs,
2232 path_conv_handle &conv_hdl)
2237 UNICODE_STRING upath;
2238 OBJECT_ATTRIBUTES attr;
2242 const ULONG ci_flag = cygwin_shared->obcaseinsensitive
2243 || (pflags & PATH_NOPOSIX) ? OBJ_CASE_INSENSITIVE : 0;
2244 /* TODO: Temporarily do all char->UNICODE conversion here. This should
2245 already be slightly faster than using Ascii functions. */
2248 InitializeObjectAttributes (&attr, &upath, ci_flag, NULL, NULL);
2250 /* This label is used in case we encounter a FS which only handles
2251 DOS paths. See below. */
2252 bool restarted = false;
2263 pflags &= ~(PATH_SYMLINK | PATH_LNK | PATH_REP);
2265 ext_here = suffix.has (path, suffixes);
2266 extn = ext_here - path;
2268 PVOID eabuf = &nfs_aol_ffei;
2269 ULONG easize = sizeof nfs_aol_ffei;
2271 bool had_ext = !!*ext_here;
2272 while (suffix.next ())
2277 get_nt_native_path (suffix.path, upath, pflags & PATH_DOS);
2283 /* The EA given to NtCreateFile allows to get a handle to a symlink on
2284 an NFS share, rather than getting a handle to the target of the
2285 symlink (which would spoil the task of this method quite a bit).
2286 Fortunately it's ignored on most other file systems so we don't have
2287 to special case NFS too much. */
2288 status = NtCreateFile (&h,
2289 READ_CONTROL | FILE_READ_ATTRIBUTES | 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 (%S)", status, &upath);
2296 /* No right to access EAs or EAs not supported? */
2297 if (!NT_SUCCESS (status)
2298 && (status == STATUS_ACCESS_DENIED
2299 || status == STATUS_EAS_NOT_SUPPORTED
2300 || status == STATUS_NOT_SUPPORTED
2301 || status == STATUS_INVALID_NETWORK_RESPONSE
2302 /* Or a bug in Samba 3.2.x (x <= 7) when accessing a share's
2303 root dir which has EAs enabled? */
2304 || status == STATUS_INVALID_PARAMETER))
2307 /* If EAs are not supported, there's no sense to check them again
2308 with suffixes attached. So we set eabuf/easize to 0 here once. */
2309 if (status == STATUS_EAS_NOT_SUPPORTED
2310 || status == STATUS_NOT_SUPPORTED)
2315 status = NtOpenFile (&h, READ_CONTROL | FILE_READ_ATTRIBUTES,
2316 &attr, &io, FILE_SHARE_VALID_FLAGS,
2317 FILE_OPEN_REPARSE_POINT
2318 | FILE_OPEN_FOR_BACKUP_INTENT);
2319 debug_printf ("%p = NtOpenFile (no-EAs %S)", status, &upath);
2321 if (status == STATUS_OBJECT_NAME_NOT_FOUND)
2323 if (ci_flag == 0 && wincap.has_broken_udf ()
2324 && (!fs.inited () || fs.is_udf ()))
2326 /* On NT 5.x UDF is broken (at least) in terms of case
2327 sensitivity. When trying to open a file case sensitive,
2328 the file appears to be non-existant. Another bug is
2329 described in fs_info::update. */
2330 attr.Attributes = OBJ_CASE_INSENSITIVE;
2331 status = NtOpenFile (&h, READ_CONTROL | FILE_READ_ATTRIBUTES,
2332 &attr, &io, FILE_SHARE_VALID_FLAGS,
2333 FILE_OPEN_REPARSE_POINT
2334 | FILE_OPEN_FOR_BACKUP_INTENT);
2335 debug_printf ("%p = NtOpenFile (broken-UDF, %S)", status, &upath);
2336 attr.Attributes = 0;
2337 if (NT_SUCCESS (status))
2340 fs.update (&upath, h);
2345 status = STATUS_OBJECT_NAME_NOT_FOUND;
2349 /* There are filesystems out in the wild (Netapp, NWFS, and others)
2350 which are uncapable of generating pathnames outside the Win32
2351 rules. That means, filenames on these FSes must not have a
2352 leading space or trailing dots and spaces. This code snippet
2353 manages them. I really hope it's streamlined enough not to
2354 slow down normal operation. This extra check only kicks in if
2355 we encountered a STATUS_OBJECT_NAME_NOT_FOUND *and* we didn't
2356 already attach a suffix *and* the above special case for UDF
2357 on XP didn't succeeed. */
2358 if (!restarted && !*ext_here && !(pflags & PATH_DOS) && !fs.inited ())
2360 /* Check for trailing dot or space or leading space in
2362 char *p = ext_here - 1;
2363 if (*p != '.' && *p != ' ')
2365 while (*--p != '\\')
2372 /* If so, check if file resides on one of the known broken
2373 FSes only supporting filenames following DOS rules. */
2375 fs.update (&upath, NULL);
2376 if (fs.has_dos_filenames_only ())
2378 /* If so, try again. Since we now know the FS, the
2379 filenames will be tweaked to follow DOS rules via the
2380 third parameter in the call to get_nt_native_path. */
2389 if (NT_SUCCESS (status)
2390 /* Check file system while we're having the file open anyway.
2391 This speeds up path_conv noticably (~10%). */
2392 && (fs.inited () || fs.update (&upath, h)))
2396 status = nfs_fetch_fattr3 (h, conv_hdl.nfsattr ());
2397 if (NT_SUCCESS (status))
2398 fileattr = ((conv_hdl.nfsattr ()->type & 7) == NF3DIR)
2399 ? FILE_ATTRIBUTE_DIRECTORY : 0;
2403 PFILE_NETWORK_OPEN_INFORMATION pfnoi = conv_hdl.fnoi ();
2405 /* Netapps don't implement FileNetworkOpenInformation. */
2406 status = fs.is_netapp ()
2407 ? STATUS_INVALID_PARAMETER
2408 : NtQueryInformationFile (h, &io, pfnoi, sizeof *pfnoi,
2409 FileNetworkOpenInformation);
2410 if (status == STATUS_INVALID_PARAMETER
2411 || status == STATUS_NOT_IMPLEMENTED)
2413 /* Apart from accessing Netapps, this also occurs when
2414 accessing SMB share root dirs hosted on NT4
2415 (STATUS_INVALID_PARAMETER), or when trying to access
2416 SMB share root dirs from NT4 (STATUS_NOT_IMPLEMENTED). */
2417 FILE_BASIC_INFORMATION fbi;
2418 FILE_STANDARD_INFORMATION fsi;
2420 status = NtQueryInformationFile (h, &io, &fbi, sizeof fbi,
2421 FileBasicInformation);
2422 if (NT_SUCCESS (status))
2424 memcpy (pfnoi, &fbi, 4 * sizeof (LARGE_INTEGER));
2425 if (NT_SUCCESS (NtQueryInformationFile (h, &io, &fsi,
2427 FileStandardInformation)))
2429 pfnoi->EndOfFile.QuadPart = fsi.EndOfFile.QuadPart;
2430 pfnoi->AllocationSize.QuadPart
2431 = fsi.AllocationSize.QuadPart;
2434 pfnoi->EndOfFile.QuadPart
2435 = pfnoi->AllocationSize.QuadPart = 0;
2436 pfnoi->FileAttributes = fbi.FileAttributes;
2439 if (NT_SUCCESS (status))
2440 fileattr = pfnoi->FileAttributes;
2443 if (!NT_SUCCESS (status))
2445 debug_printf ("%p = NtQueryInformationFile (%S)", status, &upath);
2446 fileattr = INVALID_FILE_ATTRIBUTES;
2448 /* One of the inner path components is invalid, or the path contains
2449 invalid characters. Bail out with ENOENT.
2451 Note that additional STATUS_OBJECT_PATH_INVALID and
2452 STATUS_OBJECT_PATH_SYNTAX_BAD status codes exist. The first one
2453 is seemingly not generated by NtQueryInformationFile, the latter
2454 is only generated if the path is no absolute path within the
2455 NT name space, which should not happen and would point to an
2456 error in get_nt_native_path. Both status codes are deliberately
2457 not tested here unless proved necessary. */
2458 if (status == STATUS_OBJECT_PATH_NOT_FOUND
2459 || status == STATUS_OBJECT_NAME_INVALID
2460 || status == STATUS_BAD_NETWORK_PATH
2461 || status == STATUS_BAD_NETWORK_NAME
2462 || status == STATUS_NO_MEDIA_IN_DEVICE)
2465 goto file_not_symlink;
2467 if (status != STATUS_OBJECT_NAME_NOT_FOUND
2468 && status != STATUS_NO_SUCH_FILE) /* ENOENT on NFS or 9x share */
2470 /* The file exists, but the user can't access it for one reason
2471 or the other. To get the file attributes we try to access the
2472 information by opening the parent directory and getting the
2473 file attributes using a matching NtQueryDirectoryFile call. */
2474 UNICODE_STRING dirname, basename;
2475 OBJECT_ATTRIBUTES dattr;
2478 FILE_BOTH_DIRECTORY_INFORMATION fdi;
2479 WCHAR dummy_buf[NAME_MAX + 1];
2482 RtlSplitUnicodePath (&upath, &dirname, &basename);
2483 InitializeObjectAttributes (&dattr, &dirname, ci_flag,
2485 status = NtOpenFile (&dir, SYNCHRONIZE | FILE_LIST_DIRECTORY,
2486 &dattr, &io, FILE_SHARE_VALID_FLAGS,
2487 FILE_SYNCHRONOUS_IO_NONALERT
2488 | FILE_OPEN_FOR_BACKUP_INTENT
2489 | FILE_DIRECTORY_FILE);
2490 if (!NT_SUCCESS (status))
2492 debug_printf ("%p = NtOpenFile(%S)", status, &dirname);
2493 /* There's a special case if the file is itself the root
2494 of a drive which is not accessible by the current user.
2495 This case is only recognized by the length of the
2496 basename part. If it's 0, the incoming file is the
2497 root of a drive. So we at least know it's a directory. */
2498 fileattr = basename.Length ? 0 : FILE_ATTRIBUTE_DIRECTORY;
2502 status = NtQueryDirectoryFile (dir, NULL, NULL, NULL, &io,
2503 &fdi_buf, sizeof fdi_buf,
2504 FileBothDirectoryInformation,
2505 TRUE, &basename, TRUE);
2506 /* Take the opportunity to check file system while we're
2507 having the handle to the parent dir. */
2508 fs.update (&upath, dir);
2510 if (!NT_SUCCESS (status))
2512 debug_printf ("%p = NtQueryDirectoryFile(%S)",
2514 if (status == STATUS_NO_SUCH_FILE)
2516 /* This can happen when trying to access files
2517 which match DOS device names on SMB shares.
2518 NtOpenFile failed with STATUS_ACCESS_DENIED,
2519 but the NtQueryDirectoryFile tells us the
2520 file doesn't exist. We're suspicious in this
2521 case and retry with the next suffix instead of
2530 PFILE_NETWORK_OPEN_INFORMATION pfnoi = conv_hdl.fnoi ();
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 /* Reparse points are potentially symlinks. This check must be
2565 performed before checking the SYSTEM attribute for sysfile
2566 symlinks, since reparse points can have this flag set, too.
2567 For instance, Vista starts to create a couple of reparse points
2568 with SYSTEM and HIDDEN flags set. */
2569 if ((fileattr & FILE_ATTRIBUTE_REPARSE_POINT))
2571 /* Don't check reparse points on remote filesystems. A reparse point
2572 pointing to another file on the remote system will be mistreated
2573 as pointing to a local file on the local system. This breaks the
2574 way reparse points are transparently handled on remote systems. */
2575 if (fs.is_remote_drive())
2578 res = check_reparse_point (h);
2581 /* Volume mount point. The filesystem information for the top
2582 level directory should be for the volume top level directory,
2583 rather than for the reparse point itself. So we fetch the
2584 filesystem information again, but with a NULL handle.
2585 This does what we want because fs_info::update opens the
2586 handle without FILE_OPEN_REPARSE_POINT. */
2587 fs.update (&upath, NULL);
2588 /* Make sure the open handle is not used in later stat calls.
2589 The handle has been opened with the FILE_OPEN_REPARSE_POINT
2590 flag, so it's a handle to the reparse point, not a handle
2591 to the volumes root dir. */
2592 pflags &= ~PC_KEEP_HANDLE;
2596 /* A symlink is never a directory. */
2597 conv_hdl.fnoi ()->FileAttributes &= ~FILE_ATTRIBUTE_DIRECTORY;
2602 /* Windows shortcuts are potentially treated as symlinks. Valid Cygwin
2603 & U/WIN shortcuts are R/O, but definitely not directories. */
2604 else if ((fileattr & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_DIRECTORY))
2605 == FILE_ATTRIBUTE_READONLY && suffix.lnk_match ())
2609 status = NtOpenFile (&sym_h, SYNCHRONIZE | GENERIC_READ, &attr, &io,
2610 FILE_SHARE_VALID_FLAGS,
2611 FILE_OPEN_FOR_BACKUP_INTENT
2612 | FILE_SYNCHRONOUS_IO_NONALERT);
2613 if (!NT_SUCCESS (status))
2617 res = check_shortcut (sym_h);
2622 /* If searching for `foo' and then finding a `foo.lnk' which
2623 is no shortcut, return the same as if file not found. */
2626 fileattr = INVALID_FILE_ATTRIBUTES;
2631 else if (contents[0] != ':' || contents[1] != '\\'
2632 || !parse_device (contents))
2636 /* If searching for `foo' and then finding a `foo.lnk' which is
2637 no shortcut, return the same as if file not found. */
2638 else if (suffix.lnk_match () && ext_tacked_on)
2640 fileattr = INVALID_FILE_ATTRIBUTES;
2645 /* This is the old Cygwin method creating symlinks. A symlink will
2646 have the `system' file attribute. Only files can be symlinks
2647 (which can be symlinks to directories). */
2648 else if ((fileattr & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DIRECTORY))
2649 == FILE_ATTRIBUTE_SYSTEM)
2653 status = NtOpenFile (&sym_h, SYNCHRONIZE | GENERIC_READ, &attr, &io,
2654 FILE_SHARE_VALID_FLAGS,
2655 FILE_OPEN_FOR_BACKUP_INTENT
2656 | FILE_SYNCHRONOUS_IO_NONALERT);
2658 if (!NT_SUCCESS (status))
2662 res = check_sysfile (sym_h);
2669 /* If the file is on an NFS share and could be opened with extended
2670 attributes, check if it's a symlink. Only files can be symlinks
2671 (which can be symlinks to directories). */
2672 else if (fs.is_nfs () && (conv_hdl.nfsattr ()->type & 7) == NF3LNK)
2674 res = check_nfs_symlink (h);
2682 syscall_printf ("%s", isdevice ? "is a device" : "not a symlink");
2689 if (pflags & PC_KEEP_HANDLE)
2695 syscall_printf ("%d = symlink.check (%s, %p) (%p)",
2696 res, suffix.path, contents, pflags);
2700 /* "path" is the path in a virtual symlink. Set a symlink_info struct from
2701 that and proceed with further path checking afterwards. */
2703 symlink_info::set (char *path)
2705 strcpy (contents, path);
2706 pflags = PATH_SYMLINK;
2707 fileattr = FILE_ATTRIBUTE_NORMAL;
2711 ext_tacked_on = false;
2713 extn = major = minor = mode = 0;
2714 return strlen (path);
2717 /* readlink system call */
2720 readlink (const char *path, char *buf, size_t buflen)
2724 set_errno (ENAMETOOLONG);
2728 path_conv pathbuf (path, PC_SYM_CONTENTS, stat_suffixes);
2732 set_errno (pathbuf.error);
2733 syscall_printf ("-1 = readlink (%s, %p, %d)", path, buf, buflen);
2737 if (!pathbuf.exists ())
2743 if (!pathbuf.issymlink ())
2745 if (pathbuf.exists ())
2750 ssize_t len = min (buflen, strlen (pathbuf.get_win32 ()));
2751 memcpy (buf, pathbuf.get_win32 (), len);
2753 /* errno set by symlink.check if error */
2757 /* Some programs rely on st_dev/st_ino being unique for each file.
2758 Hash the path name and hope for the best. The hash arg is not
2759 always initialized to zero since readdir needs to compute the
2760 dirent ino_t based on a combination of the hash of the directory
2761 done during the opendir call and the hash or the filename within
2762 the directory. FIXME: Not bullet-proof. */
2763 /* Cygwin internal */
2765 hash_path_name (__ino64_t hash, PUNICODE_STRING name)
2767 if (name->Length == 0)
2770 /* Build up hash. Name is already normalized */
2771 USHORT len = name->Length / sizeof (WCHAR);
2772 for (USHORT idx = 0; idx < len; ++idx)
2773 hash = RtlUpcaseUnicodeChar (name->Buffer[idx])
2774 + (hash << 6) + (hash << 16) - hash;
2779 hash_path_name (__ino64_t hash, PCWSTR name)
2781 UNICODE_STRING uname;
2782 RtlInitUnicodeString (&uname, name);
2783 return hash_path_name (hash, &uname);
2787 hash_path_name (__ino64_t hash, const char *name)
2789 UNICODE_STRING uname;
2790 RtlCreateUnicodeStringFromAsciiz (&uname, name);
2791 __ino64_t ret = hash_path_name (hash, &uname);
2792 RtlFreeUnicodeString (&uname);
2797 getcwd (char *buf, size_t ulen)
2801 if (efault.faulted (EFAULT))
2803 else if (ulen == 0 && buf)
2806 res = cygheap->cwd.get (buf, 1, 1, ulen);
2810 /* getwd: Legacy. */
2814 return getcwd (buf, PATH_MAX + 1); /*Per SuSv3!*/
2817 /* chdir: POSIX 5.2.1.1 */
2819 chdir (const char *in_dir)
2822 if (efault.faulted (EFAULT))
2830 syscall_printf ("dir '%s'", in_dir);
2832 /* Convert path. First argument ensures that we don't check for NULL/empty/invalid
2834 path_conv path (PC_NONULLEMPTY, in_dir, PC_SYM_FOLLOW | PC_POSIX);
2837 set_errno (path.error);
2838 syscall_printf ("-1 = chdir (%s)", in_dir);
2843 const char *posix_cwd = NULL;
2844 int devn = path.get_devn ();
2845 if (!path.exists ())
2847 else if (!path.isdir ())
2848 set_errno (ENOTDIR);
2849 else if (!isvirtual_dev (devn))
2851 /* The sequence chdir("xx"); chdir(".."); must be a noop if xx
2852 is not a symlink. This is exploited by find.exe.
2853 The posix_cwd is just path.normalized_path.
2854 In other cases we let cwd.set obtain the Posix path through
2856 if (!isdrive(path.normalized_path))
2857 posix_cwd = path.normalized_path;
2862 posix_cwd = path.normalized_path;
2867 res = cygheap->cwd.set (&path, posix_cwd);
2869 /* Note that we're accessing cwd.posix without a lock here. I didn't think
2870 it was worth locking just for strace. */
2871 syscall_printf ("%d = chdir() cygheap->cwd.posix '%s' native '%S'", res,
2872 cygheap->cwd.get_posix (), path.get_nt_native_path ());
2881 cygheap_fdget cfd (fd);
2883 res = chdir (cfd->get_name ());
2887 syscall_printf ("%d = fchdir (%d)", res, fd);
2891 /******************** Exported Path Routines *********************/
2893 /* Cover functions to the path conversion routines.
2894 These are exported to the world as cygwin_foo by cygwin.din. */
2896 #define return_with_errno(x) \
2906 cygwin_conv_path (cygwin_conv_path_t what, const void *from, void *to,
2911 if (efault.faulted (EFAULT))
2919 bool relative = !!(what & CCP_RELATIVE);
2920 what &= ~CCP_RELATIVE;
2924 case CCP_POSIX_TO_WIN_A:
2926 p.check ((const char *) from,
2927 PC_POSIX | PC_SYM_FOLLOW | PC_SYM_NOFOLLOW_REP
2928 | PC_NO_ACCESS_CHECK | PC_NOWARN | (relative ? PC_NOFULL : 0));
2930 return_with_errno (p.error);
2931 PUNICODE_STRING up = p.get_nt_native_path ();
2933 sys_wcstombs (buf, NT_MAX_PATH, up->Buffer, up->Length / sizeof (WCHAR));
2934 /* Convert native path to standard DOS path. */
2935 if (!strncmp (buf, "\\??\\", 4))
2938 if (buf[1] != ':') /* native UNC path */
2941 else if (*buf == '\\')
2943 /* Device name points to somewhere else in the NT namespace.
2944 Use GLOBALROOT prefix to convert to Win32 path. */
2945 char *p = buf + sys_wcstombs (buf, NT_MAX_PATH,
2946 ro_u_globalroot.Buffer,
2947 ro_u_globalroot.Length
2949 sys_wcstombs (p, NT_MAX_PATH - (p - buf),
2950 up->Buffer, up->Length / sizeof (WCHAR));
2952 lsiz = strlen (buf) + 1;
2953 /* TODO: Incoming "." is a special case which leads to a trailing
2954 backslash ".\\" in the Win32 path. That's a result of the
2955 conversion in normalize_posix_path. This should not occur
2956 so the below code is just a band-aid. */
2957 if (relative && !strcmp ((const char *) from, ".")
2958 && !strcmp (buf, ".\\"))
2965 case CCP_POSIX_TO_WIN_W:
2966 p.check ((const char *) from,
2967 PC_POSIX | PC_SYM_FOLLOW | PC_SYM_NOFOLLOW_REP
2968 | PC_NO_ACCESS_CHECK | PC_NOWARN | (relative ? PC_NOFULL : 0));
2970 return_with_errno (p.error);
2971 /* Relative Windows paths are always restricted to MAX_PATH chars. */
2972 if (relative && !isabspath (p.get_win32 ())
2973 && sys_mbstowcs (NULL, 0, p.get_win32 ()) > MAX_PATH)
2975 /* Recreate as absolute path. */
2976 p.check ((const char *) from, PC_POSIX | PC_SYM_FOLLOW
2977 | PC_NO_ACCESS_CHECK | PC_NOWARN);
2979 return_with_errno (p.error);
2981 lsiz = p.get_wide_win32_path_len () + 1;
2982 path = p.get_nt_native_path ()->Buffer;
2984 /* Convert native path to standard DOS path. */
2985 if (!wcsncmp (path, L"\\??\\", 4))
2989 /* Drop long path prefix for short pathnames. Unfortunately there's
2990 quite a bunch of Win32 functions, especially in user32.dll,
2991 apparently, which don't grok long path names at all, not even
2992 in the UNICODE API. */
2993 if ((path[5] == L':' && lsiz <= MAX_PATH + 4)
2994 || (!wcsncmp (path + 4, L"UNC\\", 4) && lsiz <= MAX_PATH + 6))
2998 if (path[1] != L':')
3000 *(path += 2) = '\\';
3005 else if (*path == L'\\')
3007 /* Device name points to somewhere else in the NT namespace.
3008 Use GLOBALROOT prefix to convert to Win32 path. */
3009 to = (void *) wcpcpy ((wchar_t *) to, ro_u_globalroot.Buffer);
3010 lsiz += ro_u_globalroot.Length / sizeof (WCHAR);
3012 /* TODO: Same ".\\" band-aid as in CCP_POSIX_TO_WIN_A case. */
3013 if (relative && !strcmp ((const char *) from, ".")
3014 && !wcscmp (path, L".\\"))
3019 lsiz *= sizeof (WCHAR);
3021 case CCP_WIN_A_TO_POSIX:
3023 error = mount_table->conv_to_posix_path ((const char *) from, buf,
3026 return_with_errno (error);
3027 lsiz = strlen (buf) + 1;
3029 case CCP_WIN_W_TO_POSIX:
3031 error = mount_table->conv_to_posix_path ((const PWCHAR) from, buf,
3034 return_with_errno (error);
3035 lsiz = strlen (buf) + 1;
3050 case CCP_POSIX_TO_WIN_A:
3051 case CCP_WIN_A_TO_POSIX:
3052 case CCP_WIN_W_TO_POSIX:
3053 stpcpy ((char *) to, buf);
3055 case CCP_POSIX_TO_WIN_W:
3056 wcpcpy ((PWCHAR) to, path);
3063 cygwin_create_path (cygwin_conv_path_t what, const void *from)
3066 ssize_t size = cygwin_conv_path (what, from, NULL, 0);
3069 else if (!(to = malloc (size)))
3071 if (cygwin_conv_path (what, from, to, size) == -1)
3081 cygwin_conv_to_win32_path (const char *path, char *win32_path)
3083 return cygwin_conv_path (CCP_POSIX_TO_WIN_A | CCP_RELATIVE, path, win32_path,
3088 cygwin_conv_to_full_win32_path (const char *path, char *win32_path)
3090 return cygwin_conv_path (CCP_POSIX_TO_WIN_A | CCP_ABSOLUTE, path, win32_path,
3094 /* This is exported to the world as cygwin_foo by cygwin.din. */
3097 cygwin_conv_to_posix_path (const char *path, char *posix_path)
3099 return cygwin_conv_path (CCP_WIN_A_TO_POSIX | CCP_RELATIVE, path, posix_path,
3104 cygwin_conv_to_full_posix_path (const char *path, char *posix_path)
3106 return cygwin_conv_path (CCP_WIN_A_TO_POSIX | CCP_ABSOLUTE, path, posix_path,
3110 /* The realpath function is required by POSIX:2008. */
3113 realpath (const char *path, char *resolved)
3115 /* Make sure the right errno is returned if path is NULL. */
3122 /* Guard reading from a potentially invalid path and writing to a
3123 potentially invalid resolved. */
3126 if (efault.faulted (EFAULT))
3132 tpath = tp.c_get ();
3133 mount_table->cygdrive_posix_path (path, tpath, 0);
3136 tpath = (char *) path;
3138 path_conv real_path (tpath, PC_SYM_FOLLOW | PC_POSIX, stat_suffixes);
3141 /* POSIX 2008 requires malloc'ing if resolved is NULL, and states
3142 that using non-NULL resolved is asking for portability
3145 if (!real_path.error && real_path.exists ())
3149 resolved = (char *) malloc (strlen (real_path.normalized_path) + 1);
3153 strcpy (resolved, real_path.normalized_path);
3157 /* FIXME: on error, Linux puts the name of the path
3158 component which could not be resolved into RESOLVED, but POSIX
3159 does not require this. */
3162 set_errno (real_path.error ?: ENOENT);
3166 /* Linux provides this extension. Since the only portable use of
3167 realpath requires a NULL second argument, we might as well have a
3168 one-argument wrapper. */
3170 canonicalize_file_name (const char *path)
3172 return realpath (path, NULL);
3175 /* Return non-zero if path is a POSIX path list.
3176 This is exported to the world as cygwin_foo by cygwin.din.
3179 <sect1 id="add-func-cygwin-posix-path-list-p">
3180 <para>Rather than use a mode to say what the "proper" path list
3181 format is, we allow any, and give apps the tools they need to
3182 convert between the two. If a ';' is present in the path list it's
3183 a Win32 path list. Otherwise, if the first path begins with
3184 [letter]: (in which case it can be the only element since if it
3185 wasn't a ';' would be present) it's a Win32 path list. Otherwise,
3186 it's a POSIX path list.</para>
3192 cygwin_posix_path_list_p (const char *path)
3194 int posix_p = !(strchr (path, ';') || isdrive (path));
3198 /* These are used for apps that need to convert env vars like PATH back and
3199 forth. The conversion is a two step process. First, an upper bound on the
3200 size of the buffer needed is computed. Then the conversion is done. This
3201 allows the caller to use alloca if it wants. */
3204 conv_path_list_buf_size (const char *path_list, bool to_posix)
3206 int i, num_elms, max_mount_path_len, size;
3209 path_conv pc(".", PC_POSIX);
3210 /* The theory is that an upper bound is
3211 current_size + (num_elms * max_mount_path_len) */
3212 /* FIXME: This method is questionable in the long run. */
3215 char delim = to_posix ? ';' : ':';
3216 for (p = path_list, num_elms = nrel = 0; p; num_elms++)
3220 p = strchr (++p, delim);
3223 /* 7: strlen ("//c") + slop, a conservative initial value */
3224 for (max_mount_path_len = sizeof ("/cygdrive/X"), i = 0;
3225 i < mount_table->nmounts; i++)
3227 int mount_len = (to_posix
3228 ? mount_table->mount[i].posix_pathlen
3229 : mount_table->mount[i].native_pathlen);
3230 if (max_mount_path_len < mount_len)
3231 max_mount_path_len = mount_len;
3235 size = strlen (path_list)
3236 + (num_elms * max_mount_path_len)
3237 + (nrel * strlen (to_posix ? pc.normalized_path : pc.get_win32 ()))
3245 cygwin_win32_to_posix_path_list_buf_size (const char *path_list)
3247 return conv_path_list_buf_size (path_list, true);
3251 cygwin_posix_to_win32_path_list_buf_size (const char *path_list)
3253 return conv_path_list_buf_size (path_list, false);
3257 env_PATH_to_posix (const void *win32, void *posix, size_t size)
3259 return_with_errno (conv_path_list ((const char *) win32, (char *) posix,
3264 cygwin_win32_to_posix_path_list (const char *win32, char *posix)
3266 return_with_errno (conv_path_list (win32, posix, MAX_PATH, 1));
3270 cygwin_posix_to_win32_path_list (const char *posix, char *win32)
3272 return_with_errno (conv_path_list (posix, win32, MAX_PATH, 0));
3276 cygwin_conv_path_list (cygwin_conv_path_t what, const void *from, void *to,
3279 /* FIXME: Path lists are (so far) always retaining relative paths. */
3280 what &= ~CCP_RELATIVE;
3283 case CCP_WIN_W_TO_POSIX:
3284 case CCP_POSIX_TO_WIN_W:
3286 api_fatal ("wide char path lists not yet supported");
3288 case CCP_WIN_A_TO_POSIX:
3289 case CCP_POSIX_TO_WIN_A:
3291 return conv_path_list_buf_size ((const char *) from,
3292 what == CCP_WIN_A_TO_POSIX);
3293 return_with_errno (conv_path_list ((const char *) from, (char *) to,
3294 size, what == CCP_WIN_A_TO_POSIX));
3303 /* cygwin_split_path: Split a path into directory and file name parts.
3304 Buffers DIR and FILE are assumed to be big enough.
3306 Examples (path -> `dir' / `file'):
3309 . -> `.' / `.' (FIXME: should this be `.' / `'?)
3310 .. -> `.' / `..' (FIXME: should this be `..' / `'?)
3312 foo/bar -> `foo' / `bar'
3313 foo/bar/ -> `foo' / `bar'
3315 /foo/bar -> `/foo' / `bar'
3318 c:foo -> `c:/' / `foo'
3319 c:/foo -> `c:/' / `foo'
3323 cygwin_split_path (const char *path, char *dir, char *file)
3325 int dir_started_p = 0;
3327 /* Deal with drives.
3328 Remember that c:foo <==> c:/foo. */
3340 if (isdirsep (*path))
3345 /* Determine if there are trailing slashes and "delete" them if present.
3346 We pretend as if they don't exist. */
3347 const char *end = path + strlen (path);
3348 /* path + 1: keep leading slash. */
3349 while (end > path + 1 && isdirsep (end[-1]))
3352 /* At this point, END points to one beyond the last character
3353 (with trailing slashes "deleted"). */
3355 /* Point LAST_SLASH at the last slash (duh...). */
3356 const char *last_slash;
3357 for (last_slash = end - 1; last_slash >= path; --last_slash)
3358 if (isdirsep (*last_slash))
3361 if (last_slash == path)
3366 else if (last_slash > path)
3368 memcpy (dir, path, last_slash - path);
3369 dir[last_slash - path] = 0;
3374 ; /* nothing to do */
3380 memcpy (file, last_slash + 1, end - last_slash - 1);
3381 file[end - last_slash - 1] = 0;
3384 /*****************************************************************************/
3386 /* The find_fast_cwd_pointer function and parts of the
3387 cwdstuff::override_win32_cwd method are based on code using the
3390 Copyright 2010 John Carey. All rights reserved.
3392 Redistribution and use in source and binary forms, with or without
3393 modification, are permitted provided that the following conditions
3396 1. Redistributions of source code must retain the above
3397 copyright notice, this list of conditions and the following
3400 2. Redistributions in binary form must reproduce the above
3401 copyright notice, this list of conditions and the following
3402 disclaimer in the documentation and/or other materials provided
3403 with the distribution.
3405 THIS SOFTWARE IS PROVIDED BY JOHN CAREY ``AS IS'' AND ANY EXPRESS
3406 OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
3407 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
3408 ARE DISCLAIMED. IN NO EVENT SHALL JOHN CAREY OR CONTRIBUTORS BE
3409 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
3410 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
3411 OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
3412 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
3413 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3414 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
3415 USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
3418 /* This structure is used to store the CWD starting with Windows Vista.
3419 The CWD storage in the RTL_USER_PROCESS_PARAMETERS block is only
3420 an afterthought now. The actual CWD storage is a FAST_CWD structure
3421 which is allocated on the process heap. The new method only requires
3422 minimal locking and it's much more multi-thread friendly. Presumably
3423 it minimizes contention when accessing the CWD. */
3424 typedef struct _FAST_CWD {
3425 UNICODE_STRING Path; /* Path's Buffer member always refers
3426 to the following Buffer array. */
3427 HANDLE DirectoryHandle;
3428 LONG FSCharacteristics; /* Taken from FileFsDeviceInformation */
3429 LONG ReferenceCount; /* Only release when this is 0. */
3430 ULONG OldDismountCount; /* Reflects the system DismountCount
3431 at the time the CWD has been set. */
3432 WCHAR Buffer[MAX_PATH];
3433 } FAST_CWD, *PFAST_CWD;
3435 /* This is the old FAST_CWD structure up to the patch from KB 2393802,
3436 release in February 2011. Hopefully it's not used for long anymore,
3437 but for quite some time we can't rely on this fact. */
3438 typedef struct _FAST_CWD_OLD {
3439 LONG ReferenceCount; /* Only release when this is 0. */
3440 HANDLE DirectoryHandle;
3441 ULONG OldDismountCount; /* Reflects the system DismountCount
3442 at the time the CWD has been set. */
3443 UNICODE_STRING Path; /* Path's Buffer member always refers
3444 to the following Buffer array. */
3445 WCHAR Buffer[MAX_PATH];
3446 } FAST_CWD_OLD, *PFAST_CWD_OLD;
3448 /* fast_cwd_ptr is a pointer to the global pointer in ntdll.dll pointing
3449 to the FAST_CWD structure which constitutes the CWD.
3451 We put the pointer into the common shared DLL segment. This allows to
3452 restrict the call to find_fast_cwd_pointer() to once per Cygwin session
3453 per user session. This works, because ASLR randomizes the load address
3454 of DLLs only once at boot time. */
3455 static PFAST_CWD *fast_cwd_ptr
3456 __attribute__((section (".cygwin_dll_common"), shared)) = (PFAST_CWD *) -1;
3457 /* Type of FAST_CWD used on this system. Keeping this information available
3458 in shared memory avoids to test for the version every time around.
3459 Default to new version. */
3460 static int fast_cwd_version
3461 __attribute__((section (".cygwin_dll_common"), shared)) = 1;
3463 /* This is the mapping of the KUSER_SHARED_DATA structure into the 32 bit
3464 user address space. We need it here to access the current DismountCount. */
3465 static KUSER_SHARED_DATA &SharedUserData
3466 = *(volatile PKUSER_SHARED_DATA) 0x7ffe0000;
3468 #define peek32(x) (*(uint32_t *)(x))
3470 /* This function scans the code in ntdll.dll to find the address of the
3471 global variable used to access the CWD starting with Vista. While the
3472 pointer is global, it's not exported from the DLL, unfortunately.
3473 Therefore we have to use some knowledge to figure out the address.
3475 This code has been tested on Vista 32/64 bit, Server 2008 32/64 bit,
3476 Windows 7 32/64 bit, and Server 2008 R2 (which is only 64 bit anyway).
3477 There's some hope that this will still work for Windows 8... */
3479 find_fast_cwd_pointer ()
3481 /* Fetch entry points of relevant functions in ntdll.dll. */
3482 HMODULE ntdll = GetModuleHandle ("ntdll.dll");
3485 const uint8_t *get_dir = (const uint8_t *)
3486 GetProcAddress (ntdll, "RtlGetCurrentDirectory_U");
3487 const uint8_t *ent_crit = (const uint8_t *)
3488 GetProcAddress (ntdll, "RtlEnterCriticalSection");
3489 if (!get_dir || !ent_crit)
3491 /* Search first relative call instruction in RtlGetCurrentDirectory_U. */
3492 const uint8_t *rcall = (const uint8_t *) memchr (get_dir, 0xe8, 32);
3495 /* Fetch offset from instruction and compute address of called function.
3496 This function actually fetches the current FAST_CWD instance and
3497 performs some other actions, not important to us. */
3498 ptrdiff_t offset = (ptrdiff_t) peek32 (rcall + 1);
3499 const uint8_t *use_cwd = rcall + 5 + offset;
3500 /* Find first "push edi" instruction. */
3501 const uint8_t *pushedi = (const uint8_t *) memchr (use_cwd, 0x57, 32);
3502 /* ...which should be followed by "mov edi, crit-sect-addr" then
3504 const uint8_t *movedi = pushedi + 1;
3505 if (movedi[0] != 0xbf || movedi[5] != 0x57)
3507 /* Compare the address used for the critical section with the known
3508 PEB lock as stored in the PEB. */
3509 if ((PRTL_CRITICAL_SECTION) peek32 (movedi + 1)
3510 != NtCurrentTeb ()->Peb->FastPebLock)
3512 /* To check we are seeing the right code, we check our expectation that
3513 the next instruction is a relative call into RtlEnterCriticalSection. */
3515 if (rcall[0] != 0xe8)
3517 /* Check that this is a relative call to RtlEnterCriticalSection. */
3518 offset = (ptrdiff_t) peek32 (rcall + 1);
3519 if (rcall + 5 + offset != ent_crit)
3521 /* After locking the critical section, the code should read the global
3522 PFAST_CWD * pointer that is guarded by that critical section. */
3523 const uint8_t *movesi = rcall + 5;
3524 if (movesi[0] != 0x8b)
3526 return (PFAST_CWD *) peek32 (movesi + 2);
3530 copy_cwd_str (PUNICODE_STRING tgt, PUNICODE_STRING src)
3532 RtlCopyUnicodeString (tgt, src);
3533 if (tgt->Buffer[tgt->Length / sizeof (WCHAR) - 1] != L'\\')
3535 tgt->Buffer[tgt->Length / sizeof (WCHAR)] = L'\\';
3536 tgt->Length += sizeof (WCHAR);
3543 /* Fetch the pointer but don't set the global fast_cwd_ptr yet. First
3544 we have to make sure we know the version of the FAST_CWD structure
3545 used on the system. */
3546 PFAST_CWD *f_cwd_ptr = find_fast_cwd_pointer ();
3548 system_printf ("WARNING: Couldn't compute FAST_CWD pointer. "
3549 "Please report this problem to\nthe public mailing "
3550 "list cygwin@cygwin.com");
3551 if (f_cwd_ptr && *f_cwd_ptr)
3553 /* Fortunately it's simple to check for the new structure.
3554 Path.MaximumLength takes the same space as the high word of
3555 the old ReferenceCount. We know that MaximumLength is always
3556 MAX_PATH. For the ref count this would account for a
3557 pratically impossible value between 34078720 and 34079240. */
3558 fast_cwd_version = ((*f_cwd_ptr)->Path.MaximumLength
3559 == MAX_PATH * sizeof (WCHAR)) ? 1 : 0;
3563 /* If we couldn't fetch fast_cwd_ptr, or if fast_cwd_ptr is
3564 NULL(*) we have to figure out the version from the Buffer
3565 pointer in the ProcessParameters. We must make sure not
3566 to access memory outside of the structure. Therefore we
3567 check the Path.Buffer pointer,
3568 which would be the ReferenceCount in the old structure.
3570 (*) This is very unlikely to happen when starting the first
3571 Cygwin process, since it only happens when starting the
3572 process in a directory which can't be used as CWD by Win32, or
3573 if the directory doesn't exist. But *if* it happens, we have
3574 no valid FAST_CWD structure, even though upp_cwd_str.Buffer is
3575 not NULL in that case. So we let the OS create a valid
3576 FAST_CWD structure temporarily to have something to work with.
3577 We know the pipe FS works. */
3578 PEB &peb = *NtCurrentTeb ()->Peb;
3580 if (f_cwd_ptr /* so *f_cwd_ptr == NULL */
3581 && !NT_SUCCESS (RtlSetCurrentDirectory_U (&ro_u_pipedir)))
3582 api_fatal ("Couldn't set directory to %S temporarily.\n"
3583 "Cannot continue.", &ro_u_pipedir);
3584 RtlEnterCriticalSection (peb.FastPebLock);
3585 PFAST_CWD f_cwd = (PFAST_CWD)
3586 ((PBYTE) peb.ProcessParameters->CurrentDirectoryName.Buffer
3587 - __builtin_offsetof (struct _FAST_CWD, Buffer));
3588 fast_cwd_version = (f_cwd->Path.Buffer == f_cwd->Buffer) ? 1 : 0;
3589 RtlLeaveCriticalSection (peb.FastPebLock);
3591 /* Eventually, after we set the version as well, set fast_cwd_ptr. */
3596 cwdstuff::override_win32_cwd (bool init, ULONG old_dismount_count)
3602 PEB &peb = *NtCurrentTeb ()->Peb;
3603 UNICODE_STRING &upp_cwd_str = peb.ProcessParameters->CurrentDirectoryName;
3604 HANDLE &upp_cwd_hdl = peb.ProcessParameters->CurrentDirectoryHandle;
3606 if (wincap.has_fast_cwd ())
3608 if (fast_cwd_ptr == (PFAST_CWD *) -1)
3609 fast_cwd_ptr = find_fast_cwd ();
3612 /* Default method starting with Vista. If we got a valid value for
3613 fast_cwd_ptr, we can simply replace the RtlSetCurrentDirectory_U
3614 function entirely, just as on pre-Vista. */
3615 PVOID heap = peb.ProcessHeap;
3616 /* First allocate a new FAST_CWD structure on the heap.
3617 The new FAST_CWD structure is 4 byte bigger than the old one,
3618 but we simply don't care, so we allocate always room for the
3620 PFAST_CWD f_cwd = (PFAST_CWD)
3621 RtlAllocateHeap (heap, 0, sizeof (FAST_CWD));
3624 debug_printf ("RtlAllocateHeap failed");
3627 /* Fill in the values. */
3628 if (fast_cwd_version)
3630 /* New FAST_CWD structure. */
3631 FILE_FS_DEVICE_INFORMATION ffdi;
3633 RtlInitEmptyUnicodeString (&f_cwd->Path, f_cwd->Buffer,
3634 MAX_PATH * sizeof (WCHAR));
3635 f_cwd->DirectoryHandle = dir;
3636 /* The new structure stores the device characteristics of the
3637 volume holding the dir. RtlGetCurrentDirectory_U checks
3638 if the FILE_REMOVABLE_MEDIA flag is set and, if so, checks if
3639 the volume is still the same as the one used when opening
3640 the directory handle.
3641 We don't call NtQueryVolumeInformationFile for the \\?\PIPE,
3642 though. It just returns STATUS_INVALID_HANDLE anyway. */
3643 f_cwd->FSCharacteristics =
3645 && NT_SUCCESS (NtQueryVolumeInformationFile (dir, &io, &ffdi,
3646 sizeof ffdi, FileFsDeviceInformation)))
3647 ? ffdi.Characteristics : 0;
3648 f_cwd->ReferenceCount = 1;
3649 f_cwd->OldDismountCount = old_dismount_count;
3650 copy_cwd_str (&f_cwd->Path, error ? &ro_u_pipedir : &win32);
3651 /* Use PEB lock when switching fast_cwd_ptr to the new FAST_CWD
3652 structure and writing the CWD to the user process parameter
3653 block. This is equivalent to calling RtlAcquirePebLock/
3654 RtlReleasePebLock, but without having to go through the FS
3656 RtlEnterCriticalSection (peb.FastPebLock);
3657 PFAST_CWD old_cwd = *fast_cwd_ptr;
3658 *fast_cwd_ptr = f_cwd;
3659 upp_cwd_str = f_cwd->Path;
3661 RtlLeaveCriticalSection (peb.FastPebLock);
3662 /* Decrement the reference count. If it's down to 0, free
3663 structure from heap. */
3665 && InterlockedDecrement (&old_cwd->ReferenceCount) == 0)
3667 /* In contrast to pre-Vista, the handle on init is always a
3668 fresh one and not the handle inherited from the parent
3669 process. So we always have to close it here. However, the
3670 handle could be NULL, if we cd'ed into a virtual dir. */
3671 if (old_cwd->DirectoryHandle)
3672 NtClose (old_cwd->DirectoryHandle);
3673 RtlFreeHeap (heap, 0, old_cwd);
3678 /* Old FAST_CWD structure. Same procedure as above, except for
3679 the non-existant FSCharacteristics member. */
3680 PFAST_CWD_OLD f_cwd_old = (PFAST_CWD_OLD) f_cwd;
3681 f_cwd_old->ReferenceCount = 1;
3682 f_cwd_old->DirectoryHandle = dir;
3683 f_cwd_old->OldDismountCount = old_dismount_count;
3684 RtlInitEmptyUnicodeString (&f_cwd_old->Path, f_cwd_old->Buffer,
3685 MAX_PATH * sizeof (WCHAR));
3686 copy_cwd_str (&f_cwd_old->Path, error ? &ro_u_pipedir : &win32);
3687 RtlEnterCriticalSection (peb.FastPebLock);
3688 PFAST_CWD_OLD old_cwd = (PFAST_CWD_OLD) *fast_cwd_ptr;
3689 *fast_cwd_ptr = (PFAST_CWD) f_cwd_old;
3690 upp_cwd_str = f_cwd_old->Path;
3692 RtlLeaveCriticalSection (peb.FastPebLock);
3694 && InterlockedDecrement (&old_cwd->ReferenceCount) == 0)
3696 if (old_cwd->DirectoryHandle)
3697 NtClose (old_cwd->DirectoryHandle);
3698 RtlFreeHeap (heap, 0, old_cwd);
3704 /* This is more a hack, and it's only used on Vista and later if we
3705 failed to find the fast_cwd_ptr value. What we do here is to call
3706 RtlSetCurrentDirectory_U and let it set up a new FAST_CWD
3707 structure. Afterwards, compute the address of that structure
3708 utilizing the fact that the buffer address in the user process
3709 parameter block is actually pointing to the buffer in that
3710 FAST_CWD structure. Then replace the directory handle in that
3711 structure with our own handle and close the original one.
3713 Note that the call to RtlSetCurrentDirectory_U also closes our
3714 old dir handle, so there won't be any handle left open.
3716 This method is prone to two race conditions:
3718 - Due to the way RtlSetCurrentDirectory_U opens the directory
3719 handle, the directory is locked against deletion or renaming
3720 between the RtlSetCurrentDirectory_U and the subsequent NtClose
3723 - When another thread calls SetCurrentDirectory at exactly the
3724 same time, a crash might occur, or worse, unrelated data could
3725 be overwritten or NtClose could be called on an unrelated handle.
3727 Therefore, use this *only* as a fallback. */
3730 status = RtlSetCurrentDirectory_U (error ? &ro_u_pipedir
3732 if (!NT_SUCCESS (status))
3734 debug_printf ("RtlSetCurrentDirectory_U(%S) failed, %p",
3735 error ? &ro_u_pipedir : &win32, status);
3739 else if (upp_cwd_hdl == NULL)
3741 RtlEnterCriticalSection (peb.FastPebLock);
3742 if (fast_cwd_version)
3744 PFAST_CWD f_cwd = (PFAST_CWD)
3745 ((PBYTE) upp_cwd_str.Buffer
3746 - __builtin_offsetof (struct _FAST_CWD, Buffer));
3747 f_cwd->DirectoryHandle = dir;
3751 PFAST_CWD_OLD f_cwd_old = (PFAST_CWD_OLD)
3752 ((PBYTE) upp_cwd_str.Buffer
3753 - __builtin_offsetof (struct _FAST_CWD_OLD, Buffer));
3754 f_cwd_old->DirectoryHandle = dir;
3758 RtlLeaveCriticalSection (peb.FastPebLock);
3759 /* In contrast to pre-Vista, the handle on init is always a fresh one
3760 and not the handle inherited from the parent process. So we always
3761 have to close it here. */
3767 /* This method is used for all pre-Vista OSes. We simply set the values
3768 for the CWD in the user process parameter block entirely by ourselves
3769 under PEB lock condition. This is how RtlSetCurrentDirectory_U worked
3770 in these older OSes, so we're safe.
3772 Note that we can't just RtlEnterCriticalSection (peb.FastPebLock)
3773 on pre-Vista. RtlAcquirePebLock was way more complicated back then. */
3774 RtlAcquirePebLock ();
3776 copy_cwd_str (&upp_cwd_str, error ? &ro_u_pipedir : &win32);
3779 RtlReleasePebLock ();
3780 /* Only on init, the handle is potentially a native handle. However,
3781 if it's identical to dir, it's the inherited handle from a Cygwin
3782 parent process and must not be closed. */
3788 /* Initialize cygcwd 'muto' for serializing access to cwd info. */
3792 cwd_lock.init ("cwd_lock");
3794 /* Cygwin processes inherit the cwd from their parent. If the win32 path
3795 buffer is not NULL, the cwd struct is already set up, and we only
3796 have to override the Win32 CWD with ours. */
3798 override_win32_cwd (true, SharedUserData.DismountCount);
3800 /* Initially re-open the cwd to allow POSIX semantics. */
3804 /* Chdir and fill out the elements of a cwdstuff struct. */
3806 cwdstuff::set (path_conv *nat_cwd, const char *posix_cwd)
3809 UNICODE_STRING upath;
3810 PEB &peb = *NtCurrentTeb ()->Peb;
3811 bool virtual_path = false;
3812 bool unc_path = false;
3813 bool inaccessible_path = false;
3815 /* Here are the problems with using SetCurrentDirectory. Just skip this
3816 comment if you don't like whining.
3818 - SetCurrentDirectory only supports paths of up to MAX_PATH - 1 chars,
3819 including a trailing backslash. That's an absolute restriction, even
3822 - SetCurrentDirectory fails for directories with strict permissions even
3823 for processes with the SE_BACKUP_NAME privilege enabled. The reason
3824 is apparently that SetCurrentDirectory calls NtOpenFile without the
3825 FILE_OPEN_FOR_BACKUP_INTENT flag set.
3827 - SetCurrentDirectory does not support case-sensitivity.
3829 - Unlinking a cwd fails because SetCurrentDirectory seems to open
3830 directories so that deleting the directory is disallowed.
3832 - SetCurrentDirectory can naturally not work on virtual Cygwin paths
3833 like /proc or /cygdrive.
3835 Unfortunately, even though we have access to the Win32 process parameter
3836 block, we can't just replace the directory handle. Starting with Vista,
3837 the handle is used elsewhere, and just replacing the handle in the process
3838 parameter block shows quite surprising results.
3839 FIXME: If we ever find a *safe* way to replace the directory handle in
3840 the process parameter block, we're back in business.
3842 Nevertheless, doing entirely without SetCurrentDirectory is not really
3843 feasible, because it breaks too many mixed applications using the Win32
3846 Therefore we handle the CWD all by ourselves and just keep the Win32
3847 CWD in sync. However, to avoid surprising behaviour in the Win32 API
3848 when we are in a CWD which is inaccessible as Win32 CWD, we set the
3849 Win32 CWD to a "weird" directory in which all relative filesystem-related
3852 cwd_lock.acquire ();
3856 upath = *nat_cwd->get_nt_native_path ();
3857 if (nat_cwd->isspecial ())
3858 virtual_path = true;
3861 /* Memorize old DismountCount before opening the dir. This value is
3862 stored in the FAST_CWD structure on Vista and later. It would be
3863 simpler to fetch the old DismountCount in override_win32_cwd, but
3864 Windows also fetches it before opening the directory handle. It's
3865 not quite clear if that's really required, but since we don't know
3866 the side effects of this action, we better follow Windows' lead. */
3867 ULONG old_dismount_count = SharedUserData.DismountCount;
3868 /* Open a directory handle with FILE_OPEN_FOR_BACKUP_INTENT and with all
3869 sharing flags set. The handle is right now used in exceptions.cc only,
3870 but that might change in future. */
3875 OBJECT_ATTRIBUTES attr;
3879 /* On init, just reopen Win32 CWD with desired access flags.
3880 We can access the PEB without lock, because no other thread
3881 can change the CWD. */
3882 RtlInitUnicodeString (&upath, L"");
3883 InitializeObjectAttributes (&attr, &upath,
3884 OBJ_CASE_INSENSITIVE | OBJ_INHERIT,
3885 peb.ProcessParameters->CurrentDirectoryHandle, NULL);
3888 InitializeObjectAttributes (&attr, &upath,
3889 nat_cwd->objcaseinsensitive () | OBJ_INHERIT,
3891 /* First try without FILE_OPEN_FOR_BACKUP_INTENT, to find out if the
3892 directory is valid for Win32 apps. And, no, we can't just call
3893 SetCurrentDirectory here, since that would potentially break
3894 case-sensitivity. */
3895 status = NtOpenFile (&h, SYNCHRONIZE | FILE_TRAVERSE, &attr, &io,
3896 FILE_SHARE_VALID_FLAGS,
3898 | FILE_SYNCHRONOUS_IO_NONALERT);
3899 if (status == STATUS_ACCESS_DENIED)
3901 status = NtOpenFile (&h, SYNCHRONIZE | FILE_TRAVERSE, &attr, &io,
3902 FILE_SHARE_VALID_FLAGS,
3904 | FILE_SYNCHRONOUS_IO_NONALERT
3905 | FILE_OPEN_FOR_BACKUP_INTENT);
3906 inaccessible_path = true;
3908 if (!NT_SUCCESS (status))
3910 cwd_lock.release ();
3911 __seterrno_from_nt_status (status);
3915 /* Set new handle. Note that we simply overwrite the old handle here
3916 without closing it. The handle is also used as Win32 CWD handle in
3917 the user parameter block, and it will be closed in override_win32_cwd,
3923 /* On init, just fetch the Win32 dir from the PEB. We can access
3924 the PEB without lock, because no other thread can change the CWD
3926 PUNICODE_STRING pdir = &peb.ProcessParameters->CurrentDirectoryName;
3927 RtlInitEmptyUnicodeString (&win32,
3928 (PWCHAR) crealloc_abort (win32.Buffer,
3931 pdir->Length + sizeof (WCHAR));
3932 RtlCopyUnicodeString (&win32, pdir);
3934 PWSTR eoBuffer = win32.Buffer + (win32.Length / sizeof (WCHAR));
3935 /* Remove trailing slash if one exists. */
3936 if ((eoBuffer - win32.Buffer) > 3 && eoBuffer[-1] == L'\\')
3937 win32.Length -= sizeof (WCHAR);
3938 if (eoBuffer[0] == L'\\')
3945 if (!virtual_path) /* don't mangle virtual path. */
3947 /* Convert into Win32 path and compute length. */
3948 if (upath.Buffer[1] == L'?')
3951 upath.Length -= 4 * sizeof (WCHAR);
3952 if (upath.Buffer[1] != L':')
3956 upath.Length -= 2 * sizeof (WCHAR);
3962 /* Path via native NT namespace. Prepend GLOBALROOT prefix
3963 to create a valid Win32 path. */
3964 PWCHAR buf = (PWCHAR) alloca (upath.Length
3965 + ro_u_globalroot.Length
3967 wcpcpy (wcpcpy (buf, ro_u_globalroot.Buffer), upath.Buffer);
3969 upath.Length += ro_u_globalroot.Length;
3971 PWSTR eoBuffer = upath.Buffer + (upath.Length / sizeof (WCHAR));
3972 /* Remove trailing slash if one exists. */
3973 if ((eoBuffer - upath.Buffer) > 3 && eoBuffer[-1] == L'\\')
3974 upath.Length -= sizeof (WCHAR);
3976 RtlInitEmptyUnicodeString (&win32,
3977 (PWCHAR) crealloc_abort (win32.Buffer,
3980 upath.Length + sizeof (WCHAR));
3981 RtlCopyUnicodeString (&win32, &upath);
3983 win32.Buffer[0] = L'\\';
3985 /* Make sure it's NUL-terminated. */
3986 win32.Buffer[win32.Length / sizeof (WCHAR)] = L'\0';
3988 /* Set drive_length, used in path conversion, and error code, used in
3989 spawn_guts to decide whether a native Win32 app can be started or not. */
4001 PWCHAR ptr = wcschr (win32.Buffer + 2, L'\\');
4003 ptr = wcschr (ptr + 1, L'\\');
4005 drive_length = ptr - win32.Buffer;
4007 drive_length = win32.Length / sizeof (WCHAR);
4009 if (inaccessible_path)
4011 else if (win32.Length > (MAX_PATH - 2) * sizeof (WCHAR))
4012 error = ENAMETOOLONG;
4016 /* Keep the Win32 CWD in sync. Don't check for error, other than for
4017 strace output. Try to keep overhead low. */
4018 override_win32_cwd (!nat_cwd, old_dismount_count);
4020 /* Eventually, create POSIX path if it's not set on entry. */
4024 posix_cwd = (const char *) tp.c_get ();
4025 mount_table->conv_to_posix_path (win32.Buffer, (char *) posix_cwd, 0);
4027 posix = (char *) crealloc_abort (posix, strlen (posix_cwd) + 1);
4028 stpcpy (posix, posix_cwd);
4030 cwd_lock.release ();
4035 cwdstuff::get_error_desc () const
4037 switch (cygheap->cwd.get_error ())
4040 return "has restricted permissions which render it\n"
4041 "inaccessible as Win32 working directory";
4043 return "is a virtual Cygwin directory which does\n"
4044 "not exist for a native Windows application";
4046 return "has a path longer than allowed for a\n"
4047 "Win32 working directory";
4051 /* That shouldn't occur, unless we defined a new error code
4052 in cwdstuff::set. */
4053 return "is not accessible for some unknown reason";
4056 /* Store incoming wchar_t path as current posix cwd. This is called from
4057 setlocale so that the cwd is always stored in the right charset. */
4059 cwdstuff::reset_posix (wchar_t *w_cwd)
4061 size_t len = sys_wcstombs (NULL, (size_t) -1, w_cwd);
4062 posix = (char *) crealloc_abort (posix, len + 1);
4063 sys_wcstombs (posix, len + 1, w_cwd);
4067 cwdstuff::get (char *buf, int need_posix, int with_chroot, unsigned ulen)
4074 else if (buf == NULL)
4075 ulen = (unsigned) -1;
4082 cwd_lock.acquire ();
4087 tocopy = tp.c_get ();
4088 sys_wcstombs (tocopy, NT_MAX_PATH, win32.Buffer,
4089 win32.Length / sizeof (WCHAR));
4094 debug_printf ("posix %s", posix);
4095 if (strlen (tocopy) >= ulen)
4103 buf = (char *) malloc (strlen (tocopy) + 1);
4104 strcpy (buf, tocopy);
4105 if (!buf[0]) /* Should only happen when chroot */
4109 cwd_lock.release ();
4112 syscall_printf ("(%s) = cwdstuff::get (%p, %d, %d, %d), errno %d",
4113 buf, buf, ulen, need_posix, with_chroot, errno);
4118 int etc::curr_ix = 0;
4119 /* Note that the first elements of the below arrays are unused */
4120 bool etc::change_possible[MAX_ETC_FILES + 1];
4121 OBJECT_ATTRIBUTES etc::fn[MAX_ETC_FILES + 1];
4122 LARGE_INTEGER etc::last_modified[MAX_ETC_FILES + 1];
4125 etc::init (int n, POBJECT_ATTRIBUTES attr)
4129 else if (++curr_ix <= MAX_ETC_FILES)
4132 api_fatal ("internal error");
4135 change_possible[n] = false;
4136 test_file_change (n);
4137 paranoid_printf ("fn[%d] %S, curr_ix %d", n, fn[n].ObjectName, curr_ix);
4142 etc::test_file_change (int n)
4145 FILE_NETWORK_OPEN_INFORMATION fnoi;
4148 status = NtQueryFullAttributesFile (&fn[n], &fnoi);
4149 if (!NT_SUCCESS (status))
4152 memset (last_modified + n, 0, sizeof (last_modified[n]));
4153 debug_printf ("NtQueryFullAttributesFile (%S) failed, %p",
4154 fn[n].ObjectName, status);
4158 res = CompareFileTime ((FILETIME *) &fnoi.LastWriteTime,
4159 (FILETIME *) last_modified + n) > 0;
4160 last_modified[n].QuadPart = fnoi.LastWriteTime.QuadPart;
4163 paranoid_printf ("fn[%d] %S res %d", n, fn[n].ObjectName, res);
4168 etc::dir_changed (int n)
4170 if (!change_possible[n])
4172 static HANDLE changed_h NO_COPY;
4178 OBJECT_ATTRIBUTES attr;
4180 path_conv dir ("/etc");
4181 status = NtOpenFile (&changed_h, SYNCHRONIZE | FILE_LIST_DIRECTORY,
4182 dir.get_object_attr (attr, sec_none_nih), &io,
4183 FILE_SHARE_VALID_FLAGS, FILE_DIRECTORY_FILE);
4184 if (!NT_SUCCESS (status))
4187 system_printf ("NtOpenFile (%S) failed, %p",
4188 dir.get_nt_native_path (), status);
4190 changed_h = INVALID_HANDLE_VALUE;
4194 status = NtNotifyChangeDirectoryFile (changed_h, NULL, NULL,
4196 FILE_NOTIFY_CHANGE_LAST_WRITE
4197 | FILE_NOTIFY_CHANGE_FILE_NAME,
4199 if (!NT_SUCCESS (status))
4202 system_printf ("NtNotifyChangeDirectoryFile (1) failed, %p",
4205 NtClose (changed_h);
4206 changed_h = INVALID_HANDLE_VALUE;
4209 memset (change_possible, true, sizeof (change_possible));
4212 if (changed_h == INVALID_HANDLE_VALUE)
4213 change_possible[n] = true;
4214 else if (WaitForSingleObject (changed_h, 0) == WAIT_OBJECT_0)
4216 status = NtNotifyChangeDirectoryFile (changed_h, NULL, NULL,
4218 FILE_NOTIFY_CHANGE_LAST_WRITE
4219 | FILE_NOTIFY_CHANGE_FILE_NAME,
4221 if (!NT_SUCCESS (status))
4224 system_printf ("NtNotifyChangeDirectoryFile (2) failed, %p",
4227 NtClose (changed_h);
4228 changed_h = INVALID_HANDLE_VALUE;
4230 memset (change_possible, true, sizeof change_possible);
4234 paranoid_printf ("fn[%d] %S change_possible %d",
4235 n, fn[n].ObjectName, change_possible[n]);
4236 return change_possible[n];
4240 etc::file_changed (int n)
4243 if (dir_changed (n) && test_file_change (n))
4245 change_possible[n] = false; /* Change is no longer possible */
4246 paranoid_printf ("fn[%d] %S res %d", n, fn[n].ObjectName, res);
4250 /* No need to be reentrant or thread-safe according to SUSv3.
4251 / and \\ are treated equally. Leading drive specifiers are
4252 kept intact as far as it makes sense. Everything else is
4253 POSIX compatible. */
4255 basename (char *path)
4258 char *c, *d, *bs = path;
4260 if (!path || !*path)
4261 return strcpy (buf, ".");
4262 if (isalpha (path[0]) && path[1] == ':')
4264 else if (strspn (path, "/\\") > 1)
4266 c = strrchr (bs, '/');
4267 if ((d = strrchr (c ?: bs, '\\')) > c)
4271 /* Trailing (back)slashes are eliminated. */
4272 while (c && c > bs && c[1] == '\0')
4275 c = strrchr (bs, '/');
4276 if ((d = strrchr (c ?: bs, '\\')) > c)
4279 if (c && (c > bs || c[1]))
4284 stpncpy (buf, path, bs - path);
4285 stpcpy (buf + (bs - path), ".");
4291 /* No need to be reentrant or thread-safe according to SUSv3.
4292 / and \\ are treated equally. Leading drive specifiers and
4293 leading double (back)slashes are kept intact as far as it
4294 makes sense. Everything else is POSIX compatible. */
4296 dirname (char *path)
4299 char *c, *d, *bs = path;
4301 if (!path || !*path)
4302 return strcpy (buf, ".");
4303 if (isalpha (path[0]) && path[1] == ':')
4305 else if (strspn (path, "/\\") > 1)
4307 c = strrchr (bs, '/');
4308 if ((d = strrchr (c ?: bs, '\\')) > c)
4312 /* Trailing (back)slashes are eliminated. */
4313 while (c && c > bs && c[1] == '\0')
4316 c = strrchr (bs, '/');
4317 if ((d = strrchr (c ?: bs, '\\')) > c)
4324 /* More trailing (back)slashes are eliminated. */
4325 while (c > bs && (*c == '/' || *c == '\\'))
4333 stpncpy (buf, path, bs - path);
4334 stpcpy (buf + (bs - path), ".");