1 /* path.cc: path support.
3 Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
4 2006, 2007, 2008, 2009 Red Hat, Inc.
6 This file is part of Cygwin.
8 This software is a copyrighted work licensed under the terms of the
9 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
12 /* This module's job is to
13 - convert between POSIX and Win32 style filenames,
14 - support the `mount' functionality,
15 - support symlinks for files and directories
17 Pathnames are handled as follows:
19 - A \ or : in a path denotes a pure windows spec.
20 - Paths beginning with // (or \\) are not translated (i.e. looked
21 up in the mount table) and are assumed to be UNC path names.
23 The goal in the above set of rules is to allow both POSIX and Win32
24 flavors of pathnames without either interfering. The rules are
25 intended to be as close to a superset of both as possible.
27 Note that you can have more than one path to a file. The mount
28 table is always prefered when translating Win32 paths to POSIX
29 paths. Win32 paths in mount table entries may be UNC paths or
30 standard Win32 paths starting with <drive-letter>:
32 Text vs Binary issues are not considered here in path style
33 decisions, although the appropriate flags are retrieved and
34 stored in various structures.
36 Removing mounted filesystem support would simplify things greatly,
37 but having it gives us a mechanism of treating disk that lives on a
38 UNIX machine as having UNIX semantics [it allows one to edit a text
39 file on that disk and not have cr's magically appear and perhaps
40 break apps running on UNIX boxes]. It also useful to be able to
41 layout a hierarchy without changing the underlying directories.
43 The semantics of mounting file systems is not intended to precisely
44 follow normal UNIX systems.
46 Each DOS drive is defined to have a current directory. Supporting
47 this would complicate things so for now things are defined so that
52 #include "miscfuncs.h"
59 #include <sys/cygwin.h>
66 #include "shared_info.h"
76 bool dos_file_warning = true;
78 suffix_info stat_suffixes[] =
81 suffix_info (".exe", 1),
87 char contents[SYMLINK_MAX + 1];
99 int check (char *path, const suffix_info *suffixes, unsigned opt,
101 int set (char *path);
102 bool parse_device (const char *);
103 int check_sysfile (HANDLE h);
104 int check_shortcut (HANDLE h);
105 int check_reparse_point (HANDLE h);
106 int check_nfs_symlink (HANDLE h);
107 int posixify (char *srcbuf);
108 bool set_error (int);
111 muto NO_COPY cwdstuff::cwd_lock;
113 static const GUID GUID_shortcut
114 = { 0x00021401L, 0, 0, {0xc0, 0, 0, 0, 0, 0, 0, 0x46}};
117 WSH_FLAG_IDLIST = 0x01, /* Contains an ITEMIDLIST. */
118 WSH_FLAG_FILE = 0x02, /* Contains a file locator element. */
119 WSH_FLAG_DESC = 0x04, /* Contains a description. */
120 WSH_FLAG_RELPATH = 0x08, /* Contains a relative path. */
121 WSH_FLAG_WD = 0x10, /* Contains a working dir. */
122 WSH_FLAG_CMDLINE = 0x20, /* Contains command line args. */
123 WSH_FLAG_ICON = 0x40 /* Contains a custom icon. */
126 struct win_shortcut_hdr
128 DWORD size; /* Header size in bytes. Must contain 0x4c. */
129 GUID magic; /* GUID of shortcut files. */
130 DWORD flags; /* Content flags. See above. */
132 /* The next fields from attr to icon_no are always set to 0 in Cygwin
133 and U/Win shortcuts. */
134 DWORD attr; /* Target file attributes. */
135 FILETIME ctime; /* These filetime items are never touched by the */
136 FILETIME mtime; /* system, apparently. Values don't matter. */
138 DWORD filesize; /* Target filesize. */
139 DWORD icon_no; /* Icon number. */
141 DWORD run; /* Values defined in winuser.h. Use SW_NORMAL. */
142 DWORD hotkey; /* Hotkey value. Set to 0. */
143 DWORD dummy[2]; /* Future extension probably. Always 0. */
146 /* Return non-zero if PATH1 is a prefix of PATH2.
147 Both are assumed to be of the same path style and / vs \ usage.
149 LEN1 = strlen (PATH1). It's passed because often it's already known.
152 /foo/ is a prefix of /foo <-- may seem odd, but desired
153 /foo is a prefix of /foo/
154 / is a prefix of /foo/bar
155 / is not a prefix of foo/bar
156 foo/ is a prefix foo/bar
157 /foo is not a prefix of /foobar
161 path_prefix_p (const char *path1, const char *path2, int len1,
162 bool caseinsensitive)
164 /* Handle case where PATH1 has trailing '/' and when it doesn't. */
165 if (len1 > 0 && isdirsep (path1[len1 - 1]))
169 return isdirsep (path2[0]) && !isdirsep (path2[1]);
171 if (isdirsep (path2[len1]) || path2[len1] == 0 || path1[len1 - 1] == ':')
172 return caseinsensitive ? strncasematch (path1, path2, len1)
173 : !strncmp (path1, path2, len1);
178 /* Return non-zero if paths match in first len chars.
179 Check is dependent of the case sensitivity setting. */
181 pathnmatch (const char *path1, const char *path2, int len, bool caseinsensitive)
183 return caseinsensitive
184 ? strncasematch (path1, path2, len) : !strncmp (path1, path2, len);
187 /* Return non-zero if paths match. Check is dependent of the case
188 sensitivity setting. */
190 pathmatch (const char *path1, const char *path2, bool caseinsensitive)
192 return caseinsensitive
193 ? strcasematch (path1, path2) : !strcmp (path1, path2);
196 /* TODO: This function is used in mkdir and rmdir to generate correct
197 error messages in case of paths ending in /. or /.. components.
198 Right now, normalize_posix_path will just normalize
199 those components away, which changes the semantics. */
201 has_dot_last_component (const char *dir, bool test_dot_dot)
203 /* SUSv3: . and .. are not allowed as last components in various system
204 calls. Don't test for backslash path separator since that's a Win32
205 path following Win32 rules. */
206 const char *last_comp = strrchr (dir, '/');
210 /* Check for trailing slash. If so, hop back to the previous slash. */
212 while (last_comp > dir)
213 if (*--last_comp == '/')
215 if (*last_comp == '/')
218 return last_comp[0] == '.'
219 && ((last_comp[1] == '\0' || last_comp[1] == '/')
221 && last_comp[1] == '.'
222 && (last_comp[2] == '\0' || last_comp[2] == '/')));
225 /* Normalize a POSIX path.
226 All duplicate /'s, except for 2 leading /'s, are deleted.
227 The result is 0 for success, or an errno error value. */
230 normalize_posix_path (const char *src, char *dst, char *&tail)
232 const char *in_src = src;
233 char *dst_start = dst;
234 syscall_printf ("src %s", src);
236 if ((isdrive (src) && isdirsep (src[2])) || *src == '\\')
240 if (!isslash (src[0]))
242 if (!cygheap->cwd.get (dst))
244 tail = strchr (tail, '\0');
245 if (isslash (dst[0]) && isslash (dst[1]))
249 if (tail == dst_start + 1 && *dst_start == '/')
253 if (tail > dst && !isslash (tail[-1]))
256 /* Two leading /'s? If so, preserve them. */
257 else if (isslash (src[1]) && !isslash (src[2]))
267 /* Strip runs of /'s. */
288 if (!isslash (src[1]))
291 else if (src[2] && !isslash (src[2]))
295 while (tail > dst_start && !isslash (*--tail))
303 if ((tail - dst) >= NT_MAX_PATH)
305 debug_printf ("ENAMETOOLONG = normalize_posix_path (%s)", src);
313 debug_printf ("%s = normalize_posix_path (%s)", dst, in_src);
317 int err = normalize_win32_path (in_src, dst, tail);
319 for (char *p = dst; (p = strchr (p, '\\')); p++)
325 path_conv::add_ext_from_sym (symlink_info &sym)
327 if (sym.ext_here && *sym.ext_here)
329 known_suffix = path + sym.extn;
330 if (sym.ext_tacked_on)
331 strcpy (known_suffix, sym.ext_here);
335 static void __stdcall mkrelpath (char *dst, bool caseinsensitive) __attribute__ ((regparm (2)));
336 static void __stdcall
337 mkrelpath (char *path, bool caseinsensitive)
340 char *cwd_win32 = tp.c_get ();
341 if (!cygheap->cwd.get (cwd_win32, 0))
344 unsigned cwdlen = strlen (cwd_win32);
345 if (!path_prefix_p (cwd_win32, path, cwdlen, caseinsensitive))
348 size_t n = strlen (path);
356 tail += isdirsep (cwd_win32[cwdlen - 1]) ? cwdlen : cwdlen + 1;
358 memmove (path, tail, strlen (tail) + 1);
364 path_conv::fillin (HANDLE h)
367 FILE_BASIC_INFORMATION fbi;
369 if (NT_SUCCESS (NtQueryInformationFile (h, &io, &fbi, sizeof fbi,
370 FileBasicInformation)))
371 fileattr = fbi.FileAttributes;
373 fileattr = INVALID_FILE_ATTRIBUTES;
377 path_conv::set_normalized_path (const char *path_copy)
381 size_t n = strlen (path_copy) + 1;
383 normalized_path = (char *) cmalloc_abort (HEAP_STR, n);
384 memcpy (normalized_path, path_copy, n);
388 WCHAR tfx_chars[] NO_COPY = {
389 0, 1, 2, 3, 4, 5, 6, 7,
390 8, 9, 10, 11, 12, 13, 14, 15,
391 16, 17, 18, 19, 20, 21, 22, 23,
392 24, 25, 26, 27, 28, 29, 30, 31,
393 32, '!', 0xf000 | '"', '#', '$', '%', '&', 39,
394 '(', ')', 0xf000 | '*', '+', ',', '-', '.', '\\',
395 '0', '1', '2', '3', '4', '5', '6', '7',
396 '8', '9', 0xf000 | ':', ';', 0xf000 | '<', '=', 0xf000 | '>', 0xf000 | '?',
397 '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
398 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
399 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
400 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
401 '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
402 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
403 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
404 'x', 'y', 'z', '{', 0xf000 | '|', '}', '~', 127
408 transform_chars (PWCHAR path, PWCHAR path_end)
410 for (; path <= path_end; ++path)
412 *path = tfx_chars[*path];
417 transform_chars (PUNICODE_STRING upath, USHORT start_idx)
419 transform_chars (upath->Buffer + start_idx,
420 upath->Buffer + upath->Length / sizeof (WCHAR) - 1);
424 str2uni_cat (UNICODE_STRING &tgt, const char *srcstr)
426 int len = sys_mbstowcs (tgt.Buffer + tgt.Length / sizeof (WCHAR),
427 (tgt.MaximumLength - tgt.Length) / sizeof (WCHAR),
430 tgt.Length += (len - 1) * sizeof (WCHAR);
434 get_nt_native_path (const char *path, UNICODE_STRING& upath)
437 if (path[0] == '/') /* special path w/o NT path representation. */
438 str2uni_cat (upath, path);
439 else if (path[0] != '\\') /* X:\... or relative path. */
441 if (path[1] == ':') /* X:\... */
443 RtlAppendUnicodeStringToString (&upath, &ro_u_natp);
444 str2uni_cat (upath, path);
445 /* The drive letter must be upper case. */
446 upath.Buffer[4] = towupper (upath.Buffer[4]);
449 str2uni_cat (upath, path);
450 transform_chars (&upath, 7);
452 else if (path[1] != '\\') /* \Device\... */
453 str2uni_cat (upath, path);
454 else if ((path[2] != '.' && path[2] != '?')
455 || path[3] != '\\') /* \\server\share\... */
457 RtlAppendUnicodeStringToString (&upath, &ro_u_uncp);
458 str2uni_cat (upath, path + 2);
459 transform_chars (&upath, 8);
461 else /* \\.\device or \\?\foo */
463 RtlAppendUnicodeStringToString (&upath, &ro_u_natp);
464 str2uni_cat (upath, path + 4);
470 path_conv::get_nt_native_path ()
475 uni_path.MaximumLength = (strlen (path) + 10) * sizeof (WCHAR);
476 wide_path = (PWCHAR) cmalloc_abort (HEAP_STR, uni_path.MaximumLength);
477 uni_path.Buffer = wide_path;
478 ::get_nt_native_path (path, uni_path);
484 path_conv::get_object_attr (OBJECT_ATTRIBUTES &attr, SECURITY_ATTRIBUTES &sa)
486 if (!get_nt_native_path ())
488 InitializeObjectAttributes (&attr, &uni_path,
489 objcaseinsensitive ()
490 | (sa.bInheritHandle ? OBJ_INHERIT : 0),
491 NULL, sa.lpSecurityDescriptor);
496 path_conv::get_wide_win32_path (PWCHAR wc)
498 get_nt_native_path ();
501 wcpcpy (wc, wide_path);
508 warn_msdos (const char *src)
510 if (user_shared->warned_msdos || !dos_file_warning || !cygwin_finished_initializing)
513 char *posix_path = tp.c_get ();
514 small_printf ("cygwin warning:\n");
515 if (cygwin_conv_path (CCP_WIN_A_TO_POSIX | CCP_RELATIVE, src,
516 posix_path, NT_MAX_PATH))
517 small_printf (" MS-DOS style path detected: %s\n POSIX equivalent preferred.\n",
520 small_printf (" MS-DOS style path detected: %s\n Preferred POSIX equivalent is: %s\n",
522 small_printf (" CYGWIN environment variable option \"nodosfilewarning\" turns off this warning.\n"
523 " Consult the user's guide for more details about POSIX paths:\n"
524 " http://cygwin.com/cygwin-ug-net/using.html#using-pathnames\n");
525 user_shared->warned_msdos = true;
529 getfileattr (const char *path, bool caseinsensitive) /* path has to be always absolute. */
532 UNICODE_STRING upath;
533 OBJECT_ATTRIBUTES attr;
534 FILE_BASIC_INFORMATION fbi;
539 InitializeObjectAttributes (&attr, &upath,
540 caseinsensitive ? OBJ_CASE_INSENSITIVE : 0,
542 get_nt_native_path (path, upath);
544 status = NtQueryAttributesFile (&attr, &fbi);
545 if (NT_SUCCESS (status))
546 return fbi.FileAttributes;
548 if (status != STATUS_OBJECT_NAME_NOT_FOUND
549 && status != STATUS_NO_SUCH_FILE) /* File not found on 9x share */
551 /* File exists but access denied. Try to get attribute through
553 UNICODE_STRING dirname, basename;
555 FILE_DIRECTORY_INFORMATION fdi;
557 RtlSplitUnicodePath (&upath, &dirname, &basename);
558 InitializeObjectAttributes (&attr, &dirname,
559 caseinsensitive ? OBJ_CASE_INSENSITIVE : 0,
561 status = NtOpenFile (&dir, SYNCHRONIZE | FILE_LIST_DIRECTORY,
562 &attr, &io, FILE_SHARE_VALID_FLAGS,
563 FILE_SYNCHRONOUS_IO_NONALERT
564 | FILE_OPEN_FOR_BACKUP_INTENT
565 | FILE_DIRECTORY_FILE);
566 if (NT_SUCCESS (status))
568 status = NtQueryDirectoryFile (dir, NULL, NULL, 0, &io,
570 FileDirectoryInformation,
571 TRUE, &basename, TRUE);
573 if (NT_SUCCESS (status) || status == STATUS_BUFFER_OVERFLOW)
574 return fdi.FileAttributes;
577 SetLastError (RtlNtStatusToDosError (status));
578 return INVALID_FILE_ATTRIBUTES;
581 /* Convert an arbitrary path SRC to a pure Win32 path, suitable for
582 passing to Win32 API routines.
584 If an error occurs, `error' is set to the errno value.
585 Otherwise it is set to 0.
588 SYMLINK_FOLLOW - convert to PATH symlink points to
589 SYMLINK_NOFOLLOW - convert to PATH of symlink itself
590 SYMLINK_IGNORE - do not check PATH for symlinks
591 SYMLINK_CONTENTS - just return symlink contents
594 /* TODO: This implementation is only preliminary. For internal
595 purposes it's necessary to have a path_conv::check function which
596 takes a UNICODE_STRING src path, otherwise we waste a lot of time
597 for converting back and forth. The below implementation does
598 realy nothing but converting to char *, until path_conv handles
599 wide-char paths directly. */
601 path_conv::check (const UNICODE_STRING *src, unsigned opt,
602 const suffix_info *suffixes)
605 char *path = tp.c_get ();
607 user_shared->warned_msdos = true;
608 sys_wcstombs (path, NT_MAX_PATH, src->Buffer, src->Length / sizeof (WCHAR));
609 path_conv::check (path, opt, suffixes);
613 path_conv::check (const char *src, unsigned opt,
614 const suffix_info *suffixes)
616 /* The tmp_buf array is used when expanding symlinks. It is NT_MAX_PATH * 2
617 in length so that we can hold the expanded symlink plus a trailer. */
619 char *path_copy = tp.c_get ();
620 char *pathbuf = tp.c_get ();
621 char *tmp_buf = tp.t_get ();
622 char *THIS_path = tp.c_get ();
624 bool need_directory = 0;
625 bool saw_symlinks = 0;
626 bool add_ext = false;
628 char *tail, *path_end;
631 static path_conv last_path_conv;
632 static char last_src[CYG_MAX_PATH];
634 if (*last_src && strcmp (last_src, src) == 0)
636 *this = last_path_conv;
642 if (efault.faulted ())
650 fileattr = INVALID_FILE_ATTRIBUTES;
651 caseinsensitive = OBJ_CASE_INSENSITIVE;
658 memset (&dev, 0, sizeof (dev));
661 cfree (normalized_path);
662 normalized_path = NULL;
663 int component = 0; // Number of translated components
665 if (!(opt & PC_NULLEMPTY))
673 bool is_msdos = false;
674 /* This loop handles symlink expansion. */
680 is_relpath = !isabspath (src);
681 error = normalize_posix_path (src, path_copy, tail);
691 /* Detect if the user was looking for a directory. We have to strip the
692 trailing slash initially while trying to add extensions but take it
693 into account during processing */
694 if (tail > path_copy + 2 && isslash (tail[-1]))
701 /* Scan path_copy from right to left looking either for a symlink
702 or an actual existing file. If an existing file is found, just
703 return. If a symlink is found, exit the for loop.
704 Also: be careful to preserve the errno returned from
705 symlink.check as the caller may need it. */
706 /* FIXME: Do we have to worry about multiple \'s here? */
707 component = 0; // Number of translated components
708 sym.contents[0] = '\0';
712 for (unsigned pflags_or = opt & PC_NO_ACCESS_CHECK; ; pflags_or = 0)
714 const suffix_info *suff;
717 /* Don't allow symlink.check to set anything in the path_conv
718 class if we're working on an inner component of the path */
728 sym.pflags = path_flags;
729 full_path = THIS_path;
732 /* Convert to native path spec sans symbolic link info. */
733 error = mount_table->conv_to_win32_path (path_copy, full_path, dev,
739 sym.pflags |= pflags_or;
741 if (dev.major == DEV_CYGDRIVE_MAJOR)
744 fileattr = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_READONLY;
747 fileattr = getfileattr (THIS_path,
748 sym.pflags & MOUNT_NOPOSIX);
753 else if (dev == FH_DEV)
757 fileattr = getfileattr (THIS_path, sym.pflags & MOUNT_NOPOSIX);
758 if (!component && fileattr == INVALID_FILE_ATTRIBUTES)
760 fileattr = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_READONLY;
765 else if (isvirtual_dev (dev.devn))
767 /* FIXME: Calling build_fhandler here is not the right way to handle this. */
768 fhandler_virtual *fh = (fhandler_virtual *) build_fh_dev (dev, path_copy);
769 int file_type = fh->exists ();
773 symlen = sym.set (fh->get_filebuf ());
781 fileattr = FILE_ATTRIBUTE_DIRECTORY;
787 case -2: /* /proc/self or /proc/<pid>/symlinks */
788 goto is_virtual_symlink;
789 case -3: /* /proc/<pid>/fd/pipe:[] */
796 case -4: /* /proc/<pid>/fd/socket:[] */
805 fileattr = INVALID_FILE_ATTRIBUTES;
806 goto virtual_component_retry;
808 if (component == 0 || dev.devn != FH_NETDRIVE)
809 path_flags |= PATH_RO;
812 /* devn should not be a device. If it is, then stop parsing now. */
813 else if (dev.devn != FH_FS)
816 path_flags = sym.pflags;
822 goto out; /* Found a device. Stop parsing. */
825 /* If path is only a drivename, Windows interprets it as the
826 current working directory on this drive instead of the root
827 dir which is what we want. So we need the trailing backslash
829 if (full_path[0] && full_path[1] == ':' && full_path[2] == '\0')
835 symlen = sym.check (full_path, suff, opt, fs);
841 dev.parse (sym.major, sym.minor);
844 fileattr = sym.fileattr;
848 if (sym.pflags & PATH_SOCKET)
855 fileattr = sym.fileattr;
863 fileattr = sym.fileattr;
864 path_flags = sym.pflags;
865 /* If the OS is caseinsensitive or the FS is caseinsensitive or
866 the incoming path was given in DOS notation, don't handle
867 path casesensitive. */
868 if (cygwin_shared->obcaseinsensitive || fs.caseinsensitive ()
870 path_flags |= PATH_NOPOSIX;
871 caseinsensitive = (path_flags & PATH_NOPOSIX)
872 ? OBJ_CASE_INSENSITIVE : 0;
875 /* If symlink.check found an existing non-symlink file, then
876 it sets the appropriate flag. It also sets any suffix found
878 if (!sym.issymlink && sym.fileattr != INVALID_FILE_ATTRIBUTES)
883 else if (!(sym.fileattr & FILE_ATTRIBUTE_DIRECTORY))
888 goto out; // file found
890 /* Found a symlink if symlen > 0. If component == 0, then the
891 src path itself was a symlink. If !follow_mode then
892 we're done. Otherwise we have to insert the path found
893 into the full path that we are building and perform all of
894 these operations again on the newly derived path. */
898 if (component == 0 && !need_directory && !(opt & PC_SYM_FOLLOW))
900 set_symlink (symlen); // last component of path is a symlink.
901 if (opt & PC_SYM_CONTENTS)
903 strcpy (THIS_path, sym.contents);
912 else if (sym.error && sym.error != ENOENT)
917 /* No existing file found. */
919 virtual_component_retry:
920 /* Find the new "tail" of the path, e.g. in '/for/bar/baz',
922 if (tail != path_end)
924 while (--tail > path_copy + 1 && *tail != '/') {}
925 /* Exit loop if there is no tail or we are at the
926 beginning of a UNC path */
927 if (tail <= path_copy + 1)
928 goto out; // all done
930 /* Haven't found an existing pathname component yet.
931 Pinch off the tail and try again. */
936 /* Arrive here if above loop detected a symlink. */
937 if (++loop > SYMLOOP_MAX)
939 error = ELOOP; // Eep.
946 /* Place the link content, possibly with head and/or tail, in tmp_buf */
949 if (isabspath (sym.contents))
950 headptr = tmp_buf; /* absolute path */
953 /* Copy the first part of the path (with ending /) and point to the end. */
954 char *prevtail = tail;
955 while (--prevtail > path_copy && *prevtail != '/') {}
956 int headlen = prevtail - path_copy + 1;;
957 memcpy (tmp_buf, path_copy, headlen);
958 headptr = &tmp_buf[headlen];
961 /* Make sure there is enough space */
962 if (headptr + symlen >= tmp_buf + (2 * NT_MAX_PATH))
965 error = ENAMETOOLONG;
966 this->path = cstrdup ("::ENAMETOOLONG::");
970 /* Copy the symlink contents to the end of tmp_buf.
972 for (char *p = sym.contents; *p; p++)
973 *headptr++ = *p == '\\' ? '/' : *p;
976 /* Copy any tail component (with the 0) */
977 if (tail++ < path_end)
979 /* Add a slash if needed. There is space. */
980 if (*(headptr - 1) != '/')
982 int taillen = path_end - tail + 1;
983 if (headptr + taillen > tmp_buf + (2 * NT_MAX_PATH))
985 memcpy (headptr, tail, taillen);
988 /* Evaluate everything all over again. */
992 if (!(opt & PC_SYM_CONTENTS))
996 this->path = (char *) cmalloc_abort (HEAP_STR, strlen (THIS_path) + 7);
997 stpcpy (this->path, THIS_path);
999 add_ext_from_sym (sym);
1000 if (dev.devn == FH_NETDRIVE && component)
1002 /* This case indicates a non-existant resp. a non-retrievable
1003 share. This happens for instance if the share is a printer.
1004 In this case the path must not be treated like a FH_NETDRIVE,
1005 but like a FH_FS instead, so the usual open call for files
1009 else if (isvirtual_dev (dev.devn) && fileattr == INVALID_FILE_ATTRIBUTES)
1014 else if (!need_directory || error)
1015 /* nothing to do */;
1016 else if (fileattr == INVALID_FILE_ATTRIBUTES)
1017 strcat (path, "\\"); /* Reattach trailing dirsep in native path. */
1018 else if (fileattr & FILE_ATTRIBUTE_DIRECTORY)
1019 path_flags &= ~PATH_SYMLINK;
1022 debug_printf ("%s is a non-directory", path);
1029 if (strncmp (path, "\\\\.\\", 4))
1031 if (!tail || tail == path)
1033 else if (tail[-1] != '\\')
1042 /* FS has been checked already for existing files. */
1043 if (exists () || fs.update (get_nt_native_path (), NULL))
1045 /* Incoming DOS paths are treated like DOS paths in native
1046 Windows applications. No ACLs, just default settings. */
1048 fs.has_acls (false);
1049 debug_printf ("this->path(%s), has_acls(%d)", path, fs.has_acls ());
1050 /* CV: We could use this->has_acls() but I want to make sure that
1051 we don't forget that the PATH_NOACL flag must be taken into
1053 if (!(path_flags & PATH_NOACL) && fs.has_acls ())
1054 set_exec (0); /* We really don't know if this is executable or not here
1055 but set it to not executable since it will be figured out
1056 later by anything which cares about this. */
1058 if (exec_state () != dont_know_if_executable)
1062 else if (issymlink () || issocket ())
1066 if (opt & PC_NOFULL)
1070 mkrelpath (this->path, !!caseinsensitive);
1071 /* Invalidate wide_path so that wide relpath can be created
1072 in later calls to get_nt_native_path or get_wide_win32_path. */
1079 size_t n = strlen (this->path);
1080 /* Do not add trailing \ to UNC device names like \\.\a: */
1081 if (this->path[n - 1] != '\\' &&
1082 (strncmp (this->path, "\\\\.\\", 4) != 0))
1084 this->path[n] = '\\';
1085 this->path[n + 1] = '\0';
1091 set_has_symlinks ();
1093 if ((opt & PC_POSIX))
1095 if (tail < path_end && tail > path_copy + 1)
1097 set_normalized_path (path_copy);
1098 if (is_msdos && !(opt & PC_NOWARN))
1105 last_path_conv = *this;
1106 strcpy (last_src, src);
1111 path_conv::~path_conv ()
1113 if (normalized_path)
1115 cfree (normalized_path);
1116 normalized_path = NULL;
1131 path_conv::is_binary ()
1134 PWCHAR bintest = tp.w_get ();
1137 return GetBinaryTypeW (get_wide_win32_path (bintest), &bin)
1138 && (bin == SCS_32BIT_BINARY || bin == SCS_64BIT_BINARY);
1141 /* Normalize a Win32 path.
1142 /'s are converted to \'s in the process.
1143 All duplicate \'s, except for 2 leading \'s, are deleted.
1145 The result is 0 for success, or an errno error value.
1146 FIXME: A lot of this should be mergeable with the POSIX critter. */
1148 normalize_win32_path (const char *src, char *dst, char *&tail)
1150 const char *src_start = src;
1151 bool beg_src_slash = isdirsep (src[0]);
1154 /* Skip long path name prefixes in Win32 or NT syntax. */
1155 if (beg_src_slash && (src[1] == '?' || isdirsep (src[1]))
1156 && src[2] == '?' && isdirsep (src[3]))
1159 if (src[1] != ':') /* native UNC path */
1160 src += 2; /* Fortunately the first char is not copied... */
1162 beg_src_slash = false;
1164 if (beg_src_slash && isdirsep (src[1]))
1166 if (isdirsep (src[2]))
1168 /* More than two slashes are just folded into one. */
1170 while (isdirsep (src[1]))
1175 /* Two slashes start a network or device path. */
1178 if (src[1] == '.' && isdirsep (src[2]))
1189 /* Always convert drive letter to uppercase for case sensitivity. */
1190 *tail++ = cyg_toupper (*src++);
1191 else if (*src != '/')
1194 tail += cygheap->cwd.get_drive (dst);
1195 else if (!cygheap->cwd.get (dst, 0))
1196 return get_errno ();
1199 tail = strchr (tail, '\0');
1200 if (tail[-1] != '\\')
1208 /* Strip duplicate /'s. */
1209 if (isdirsep (src[0]) && isdirsep (src[1]))
1212 else if (src[0] == '.' && isdirsep (src[1])
1213 && (src == src_start || isdirsep (src[-1])))
1216 /* Backup if "..". */
1217 else if (src[0] == '.' && src[1] == '.'
1218 /* dst must be greater than dst_start */
1219 && tail[-1] == '\\')
1221 if (!isdirsep (src[2]) && src[2] != '\0')
1225 /* Back up over /, but not if it's the first one. */
1228 /* Now back up to the next /. */
1229 while (tail > dst + 1 && tail[-1] != '\\' && tail[-2] != ':')
1232 if (isdirsep (*src))
1236 /* Otherwise, add char to result. */
1245 if ((tail - dst) >= NT_MAX_PATH)
1246 return ENAMETOOLONG;
1248 if (tail > dst + 1 && tail[-1] == '.' && tail[-2] == '\\')
1251 debug_printf ("%s = normalize_win32_path (%s)", dst, src_start);
1255 /* Various utilities. */
1257 /* nofinalslash: Remove trailing / and \ from SRC (except for the
1258 first one). It is ok for src == dst. */
1261 nofinalslash (const char *src, char *dst)
1263 int len = strlen (src);
1265 memcpy (dst, src, len + 1);
1266 while (len > 1 && isdirsep (dst[--len]))
1270 /* conv_path_list: Convert a list of path names to/from Win32/POSIX. */
1273 conv_path_list (const char *src, char *dst, size_t size, int to_posix)
1276 char src_delim, dst_delim;
1277 cygwin_conv_path_t conv_fn;
1284 conv_fn = CCP_WIN_A_TO_POSIX | CCP_RELATIVE;
1290 conv_fn = CCP_POSIX_TO_WIN_A | CCP_RELATIVE;
1294 len = strlen (src) + 1;
1295 if (len <= NT_MAX_PATH * sizeof (WCHAR))
1296 srcbuf = (char *) tp.w_get ();
1298 srcbuf = (char *) alloca (len);
1302 bool saw_empty = false;
1305 char *s = strccpy (srcbuf, &src, src_delim);
1306 size_t len = s - srcbuf;
1307 if (len >= NT_MAX_PATH)
1315 err = cygwin_conv_path (conv_fn, srcbuf, d, size - (d - dst));
1320 err = cygwin_conv_path (conv_fn, ".", d, size - (d - dst));
1324 if (to_posix == ENV_CVT)
1330 d = strchr (d, '\0');
1344 /********************** Symbolic Link Support **************************/
1346 /* Create a symlink from FROMPATH to TOPATH. */
1348 /* If TRUE create symlinks as Windows shortcuts, if false create symlinks
1349 as normal files with magic number and system bit set. */
1350 bool allow_winsymlinks = false;
1353 symlink (const char *oldpath, const char *newpath)
1355 return symlink_worker (oldpath, newpath, allow_winsymlinks, false);
1359 symlink_worker (const char *oldpath, const char *newpath, bool use_winsym,
1364 path_conv win32_newpath, win32_oldpath;
1366 SECURITY_ATTRIBUTES sa = sec_none_nih;
1367 security_descriptor sd;
1368 OBJECT_ATTRIBUTES attr;
1374 bool mk_winsym = use_winsym;
1376 /* POSIX says that empty 'newpath' is invalid input while empty
1377 'oldpath' is valid -- it's symlink resolver job to verify if
1378 symlink contents point to existing filesystem object */
1380 if (efault.faulted (EFAULT))
1382 if (!*oldpath || !*newpath)
1388 if (strlen (oldpath) > SYMLINK_MAX)
1390 set_errno (ENAMETOOLONG);
1394 len = strlen (newpath);
1395 /* Trailing dirsep is a no-no. */
1396 if (isdirsep (newpath[len - 1]))
1402 check_opt = PC_SYM_NOFOLLOW | PC_POSIX | (isdevice ? PC_NOWARN : 0);
1403 /* We need the normalized full path below. */
1404 win32_newpath.check (newpath, check_opt, stat_suffixes);
1405 /* MVFS doesn't handle the SYSTEM DOS attribute, but it handles the R/O
1406 attribute. Therefore we create symlinks on MVFS always as shortcuts. */
1407 mk_winsym |= win32_newpath.fs_is_mvfs ();
1409 if (mk_winsym && !win32_newpath.exists ()
1410 && (isdevice || !win32_newpath.fs_is_nfs ()))
1412 char *newplnk = tp.c_get ();
1413 stpcpy (stpcpy (newplnk, newpath), ".lnk");
1414 win32_newpath.check (newplnk, check_opt);
1417 if (win32_newpath.error)
1419 set_errno (win32_newpath.error);
1423 syscall_printf ("symlink (%s, %S)", oldpath,
1424 win32_newpath.get_nt_native_path ());
1426 if ((!isdevice && win32_newpath.exists ())
1427 || win32_newpath.is_auto_device ())
1433 if (!isdevice && win32_newpath.fs_is_nfs ())
1435 /* On NFS, create symlinks by calling NtCreateFile with an EA of type
1436 NfsSymlinkTargetName containing ... the symlink target name. */
1437 PFILE_FULL_EA_INFORMATION pffei = (PFILE_FULL_EA_INFORMATION) tp.w_get ();
1438 pffei->NextEntryOffset = 0;
1440 pffei->EaNameLength = sizeof (NFS_SYML_TARGET) - 1;
1441 char *EaValue = stpcpy (pffei->EaName, NFS_SYML_TARGET) + 1;
1442 pffei->EaValueLength = sizeof (WCHAR) *
1443 (sys_mbstowcs ((PWCHAR) EaValue, NT_MAX_PATH, oldpath) - 1);
1444 status = NtCreateFile (&fh, FILE_WRITE_DATA | FILE_WRITE_EA | SYNCHRONIZE,
1445 win32_newpath.get_object_attr (attr, sa),
1446 &io, NULL, FILE_ATTRIBUTE_SYSTEM,
1447 FILE_SHARE_VALID_FLAGS, FILE_CREATE,
1448 FILE_SYNCHRONOUS_IO_NONALERT
1449 | FILE_OPEN_FOR_BACKUP_INTENT,
1450 pffei, NT_MAX_PATH * sizeof (WCHAR));
1451 if (!NT_SUCCESS (status))
1453 __seterrno_from_nt_status (status);
1463 ITEMIDLIST *pidl = NULL;
1464 size_t full_len = 0;
1465 unsigned short oldpath_len, desc_len, relpath_len, pidl_len = 0;
1466 char desc[MAX_PATH + 1], *relpath;
1470 /* First create an IDLIST to learn how big our shortcut is
1474 /* The symlink target is relative to the directory in which
1475 the symlink gets created, not relative to the cwd. Therefore
1476 we have to mangle the path quite a bit before calling path_conv. */
1477 if (isabspath (oldpath))
1478 win32_oldpath.check (oldpath,
1483 len = strrchr (win32_newpath.normalized_path, '/')
1484 - win32_newpath.normalized_path + 1;
1485 char *absoldpath = tp.t_get ();
1486 stpcpy (stpncpy (absoldpath, win32_newpath.normalized_path, len),
1488 win32_oldpath.check (absoldpath, PC_SYM_NOFOLLOW, stat_suffixes);
1490 if (SUCCEEDED (SHGetDesktopFolder (&psl)))
1492 WCHAR wc_path[win32_oldpath.get_wide_win32_path_len () + 1];
1493 win32_oldpath.get_wide_win32_path (wc_path);
1494 /* Amazing but true: Even though the ParseDisplayName method
1495 takes a wide char path name, it does not understand the
1496 Win32 prefix for long pathnames! So we have to tack off
1497 the prefix and convert the path to the "normal" syntax
1498 for ParseDisplayName. */
1499 WCHAR *wc = wc_path + 4;
1500 if (wc[1] != L':') /* native UNC path */
1503 if (SUCCEEDED (res = psl->ParseDisplayName (NULL, NULL, wc, NULL,
1508 for (p = pidl; p->mkid.cb > 0;
1509 p = (ITEMIDLIST *)((char *) p + p->mkid.cb))
1511 pidl_len = (char *) p - (char *) pidl + 2;
1516 /* Compute size of shortcut file. */
1517 full_len = sizeof (win_shortcut_hdr);
1519 full_len += sizeof (unsigned short) + pidl_len;
1520 oldpath_len = strlen (oldpath);
1521 /* Unfortunately the length of the description is restricted to a
1522 length of MAX_PATH up to NT4, and to a length of 2000 bytes
1523 since W2K. We don't want to add considerations for the different
1524 lengths and even 2000 bytes is not enough for long path names.
1525 So what we do here is to set the description to the POSIX path
1526 only if the path is not longer than MAX_PATH characters. We
1527 append the full path name after the regular shortcut data
1528 (see below), which works fine with Windows Explorer as well
1529 as older Cygwin versions (as long as the whole file isn't bigger
1530 than 8K). The description field is only used for backward
1531 compatibility to older Cygwin versions and those versions are
1532 not capable of handling long path names anyway. */
1533 desc_len = stpcpy (desc, oldpath_len > MAX_PATH
1534 ? "[path too long]" : oldpath) - desc;
1535 full_len += sizeof (unsigned short) + desc_len;
1536 /* Devices get the oldpath string unchanged as relative path. */
1539 relpath_len = oldpath_len;
1540 stpcpy (relpath = tp.c_get (), oldpath);
1544 relpath_len = strlen (win32_oldpath.get_win32 ());
1545 stpcpy (relpath = tp.c_get (), win32_oldpath.get_win32 ());
1547 full_len += sizeof (unsigned short) + relpath_len;
1548 full_len += sizeof (unsigned short) + oldpath_len;
1549 /* 1 byte more for trailing 0 written by stpcpy. */
1550 if (full_len < NT_MAX_PATH * sizeof (WCHAR))
1551 buf = (char *) tp.w_get ();
1553 buf = (char *) alloca (full_len + 1);
1555 /* Create shortcut header */
1556 win_shortcut_hdr *shortcut_header = (win_shortcut_hdr *) buf;
1557 memset (shortcut_header, 0, sizeof *shortcut_header);
1558 shortcut_header->size = sizeof *shortcut_header;
1559 shortcut_header->magic = GUID_shortcut;
1560 shortcut_header->flags = (WSH_FLAG_DESC | WSH_FLAG_RELPATH);
1562 shortcut_header->flags |= WSH_FLAG_IDLIST;
1563 shortcut_header->run = SW_NORMAL;
1564 cp = buf + sizeof (win_shortcut_hdr);
1569 *(unsigned short *)cp = pidl_len;
1570 memcpy (cp += 2, pidl, pidl_len);
1572 CoTaskMemFree (pidl);
1575 /* Create description */
1576 *(unsigned short *)cp = desc_len;
1577 cp = stpcpy (cp += 2, desc);
1579 /* Create relpath */
1580 *(unsigned short *)cp = relpath_len;
1581 cp = stpcpy (cp += 2, relpath);
1583 /* Append the POSIX path after the regular shortcut data for
1584 the long path support. */
1585 unsigned short *plen = (unsigned short *) cp;
1587 *(PWCHAR) cp = 0xfeff; /* BOM */
1589 *plen = sys_mbstowcs ((PWCHAR) cp, NT_MAX_PATH, oldpath) * sizeof (WCHAR);
1594 /* Default technique creating a symlink. */
1595 buf = (char *) tp.w_get ();
1596 cp = stpcpy (buf, SYMLINK_COOKIE);
1597 *(PWCHAR) cp = 0xfeff; /* BOM */
1599 /* Note that the terminating nul is written. */
1600 cp += sys_mbstowcs ((PWCHAR) cp, NT_MAX_PATH, oldpath) * sizeof (WCHAR);
1603 if (isdevice && win32_newpath.exists ())
1605 status = NtOpenFile (&fh, FILE_WRITE_ATTRIBUTES,
1606 win32_newpath.get_object_attr (attr, sa),
1607 &io, 0, FILE_OPEN_FOR_BACKUP_INTENT);
1608 if (!NT_SUCCESS (status))
1610 __seterrno_from_nt_status (status);
1613 status = NtSetAttributesFile (fh, FILE_ATTRIBUTE_NORMAL);
1615 if (!NT_SUCCESS (status))
1617 __seterrno_from_nt_status (status);
1621 /* See comments in fhander_base::open () for an explanation why we defer
1622 setting security attributes on remote files. */
1623 if (win32_newpath.has_acls () && !win32_newpath.isremote ())
1624 set_security_attribute (win32_newpath, S_IFLNK | STD_RBITS | STD_WBITS,
1626 status = NtCreateFile (&fh, DELETE | FILE_GENERIC_WRITE,
1627 win32_newpath.get_object_attr (attr, sa),
1628 &io, NULL, FILE_ATTRIBUTE_NORMAL,
1629 FILE_SHARE_VALID_FLAGS,
1630 isdevice ? FILE_OVERWRITE_IF : FILE_CREATE,
1631 FILE_SYNCHRONOUS_IO_NONALERT
1632 | FILE_NON_DIRECTORY_FILE
1633 | FILE_OPEN_FOR_BACKUP_INTENT,
1635 if (!NT_SUCCESS (status))
1637 __seterrno_from_nt_status (status);
1640 if (win32_newpath.has_acls () && win32_newpath.isremote ())
1641 set_file_attribute (fh, win32_newpath, ILLEGAL_UID, ILLEGAL_GID,
1642 S_IFLNK | STD_RBITS | STD_WBITS);
1643 status = NtWriteFile (fh, NULL, NULL, NULL, &io, buf, cp - buf, NULL, NULL);
1644 if (NT_SUCCESS (status) && io.Information == (ULONG) (cp - buf))
1646 status = NtSetAttributesFile (fh, mk_winsym ? FILE_ATTRIBUTE_READONLY
1647 : FILE_ATTRIBUTE_SYSTEM);
1648 if (!NT_SUCCESS (status))
1649 debug_printf ("Setting attributes failed, status = %p", status);
1654 __seterrno_from_nt_status (status);
1655 FILE_DISPOSITION_INFORMATION fdi = { TRUE };
1656 status = NtSetInformationFile (fh, &io, &fdi, sizeof fdi,
1657 FileDispositionInformation);
1658 if (!NT_SUCCESS (status))
1659 debug_printf ("Setting delete dispostion failed, status = %p", status);
1664 syscall_printf ("%d = symlink_worker (%s, %s, %d, %d)", res, oldpath,
1665 newpath, mk_winsym, isdevice);
1670 cmp_shortcut_header (win_shortcut_hdr *file_header)
1672 /* A Cygwin or U/Win shortcut only contains a description and a relpath.
1673 Cygwin shortcuts also might contain an ITEMIDLIST. The run type is
1674 always set to SW_NORMAL. */
1675 return file_header->size == sizeof (win_shortcut_hdr)
1676 && !memcmp (&file_header->magic, &GUID_shortcut, sizeof GUID_shortcut)
1677 && (file_header->flags & ~WSH_FLAG_IDLIST)
1678 == (WSH_FLAG_DESC | WSH_FLAG_RELPATH)
1679 && file_header->run == SW_NORMAL;
1683 symlink_info::check_shortcut (HANDLE in_h)
1686 win_shortcut_hdr *file_header;
1690 UNICODE_STRING same = { 0, 0, (PWCHAR) L"" };
1691 OBJECT_ATTRIBUTES attr;
1695 FILE_STANDARD_INFORMATION fsi;
1697 InitializeObjectAttributes (&attr, &same, 0, in_h, NULL);
1698 status = NtOpenFile (&h, FILE_READ_DATA | SYNCHRONIZE,
1699 &attr, &io, FILE_SHARE_VALID_FLAGS,
1700 FILE_OPEN_FOR_BACKUP_INTENT
1701 | FILE_SYNCHRONOUS_IO_NONALERT);
1702 if (!NT_SUCCESS (status))
1704 status = NtQueryInformationFile (h, &io, &fsi, sizeof fsi,
1705 FileStandardInformation);
1706 if (!NT_SUCCESS (status))
1711 if (fsi.EndOfFile.QuadPart <= sizeof (win_shortcut_hdr)
1712 || fsi.EndOfFile.QuadPart > 4 * 65536)
1714 if (fsi.EndOfFile.LowPart < NT_MAX_PATH * sizeof (WCHAR))
1715 buf = (char *) tp.w_get ();
1717 buf = (char *) alloca (fsi.EndOfFile.LowPart + 1);
1718 if (!NT_SUCCESS (NtReadFile (h, NULL, NULL, NULL,
1719 &io, buf, fsi.EndOfFile.LowPart, NULL, NULL)))
1724 file_header = (win_shortcut_hdr *) buf;
1725 if (io.Information != fsi.EndOfFile.LowPart
1726 || !cmp_shortcut_header (file_header))
1728 cp = buf + sizeof (win_shortcut_hdr);
1729 if (file_header->flags & WSH_FLAG_IDLIST) /* Skip ITEMIDLIST */
1730 cp += *(unsigned short *) cp + 2;
1731 if (!(len = *(unsigned short *) cp))
1734 /* Check if this is a device file - these start with the sequence :\\ */
1735 if (strncmp (cp, ":\\", 2) == 0)
1736 res = strlen (strcpy (contents, cp)); /* Don't mess with device files */
1739 /* Has appended full path? If so, use it instead of description. */
1740 unsigned short relpath_len = *(unsigned short *) (cp + len);
1741 if (cp + len + 2 + relpath_len < buf + fsi.EndOfFile.LowPart)
1743 cp += len + 2 + relpath_len;
1744 len = *(unsigned short *) cp;
1747 if (*(PWCHAR) cp == 0xfeff) /* BOM */
1749 char *tmpbuf = tp.c_get ();
1750 if (sys_wcstombs (tmpbuf, NT_MAX_PATH, (PWCHAR) (cp + 2))
1753 res = posixify (tmpbuf);
1755 else if (len > SYMLINK_MAX)
1760 res = posixify (cp);
1763 if (res) /* It's a symlink. */
1764 pflags = PATH_SYMLINK | PATH_LNK;
1772 symlink_info::check_sysfile (HANDLE in_h)
1775 char cookie_buf[sizeof (SYMLINK_COOKIE) - 1];
1776 char *srcbuf = tp.c_get ();
1778 UNICODE_STRING same = { 0, 0, (PWCHAR) L"" };
1779 OBJECT_ATTRIBUTES attr;
1784 InitializeObjectAttributes (&attr, &same, 0, in_h, NULL);
1785 status = NtOpenFile (&h, FILE_READ_DATA | SYNCHRONIZE,
1786 &attr, &io, FILE_SHARE_VALID_FLAGS,
1787 FILE_OPEN_FOR_BACKUP_INTENT
1788 | FILE_SYNCHRONOUS_IO_NONALERT);
1789 if (!NT_SUCCESS (status))
1791 else if (!NT_SUCCESS (status = NtReadFile (h, NULL, NULL, NULL, &io,
1792 cookie_buf, sizeof (cookie_buf),
1795 debug_printf ("ReadFile1 failed");
1796 if (status != STATUS_END_OF_FILE)
1799 else if (io.Information == sizeof (cookie_buf)
1800 && memcmp (cookie_buf, SYMLINK_COOKIE, sizeof (cookie_buf)) == 0)
1802 /* It's a symlink. */
1803 pflags = PATH_SYMLINK;
1805 status = NtReadFile (h, NULL, NULL, NULL, &io, srcbuf,
1806 NT_MAX_PATH, NULL, NULL);
1807 if (!NT_SUCCESS (status))
1809 debug_printf ("ReadFile2 failed");
1810 if (status != STATUS_END_OF_FILE)
1813 else if (*(PWCHAR) srcbuf == 0xfeff) /* BOM */
1815 char *tmpbuf = tp.c_get ();
1816 if (sys_wcstombs (tmpbuf, NT_MAX_PATH, (PWCHAR) (srcbuf + 2))
1818 debug_printf ("symlink string too long");
1820 res = posixify (tmpbuf);
1822 else if (io.Information > SYMLINK_MAX + 1)
1823 debug_printf ("symlink string too long");
1825 res = posixify (srcbuf);
1827 else if (io.Information == sizeof (cookie_buf)
1828 && memcmp (cookie_buf, SOCKET_COOKIE, sizeof (cookie_buf)) == 0)
1829 pflags |= PATH_SOCKET;
1835 symlink_info::check_reparse_point (HANDLE h)
1840 PREPARSE_DATA_BUFFER rp = (PREPARSE_DATA_BUFFER) tp.c_get ();
1841 char srcbuf[SYMLINK_MAX + 7];
1843 status = NtFsControlFile (h, NULL, NULL, NULL, &io, FSCTL_GET_REPARSE_POINT,
1844 NULL, 0, (LPVOID) rp,
1845 MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
1846 if (!NT_SUCCESS (status))
1848 debug_printf ("NtFsControlFile(FSCTL_GET_REPARSE_POINT) failed, %p",
1853 if (rp->ReparseTag == IO_REPARSE_TAG_SYMLINK)
1855 sys_wcstombs (srcbuf, SYMLINK_MAX + 1,
1856 (WCHAR *)((char *)rp->SymbolicLinkReparseBuffer.PathBuffer
1857 + rp->SymbolicLinkReparseBuffer.SubstituteNameOffset),
1858 rp->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof (WCHAR));
1859 pflags = PATH_SYMLINK | PATH_REP;
1860 fileattr &= ~FILE_ATTRIBUTE_DIRECTORY;
1862 else if (rp->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
1864 if (rp->SymbolicLinkReparseBuffer.PrintNameLength == 0)
1866 /* Likely a volume mount point. Not treated as symlink. */
1869 sys_wcstombs (srcbuf, SYMLINK_MAX + 1,
1870 (WCHAR *)((char *)rp->MountPointReparseBuffer.PathBuffer
1871 + rp->MountPointReparseBuffer.SubstituteNameOffset),
1872 rp->MountPointReparseBuffer.SubstituteNameLength / sizeof (WCHAR));
1873 pflags = PATH_SYMLINK | PATH_REP;
1874 fileattr &= ~FILE_ATTRIBUTE_DIRECTORY;
1876 return posixify (srcbuf);
1880 symlink_info::check_nfs_symlink (HANDLE h)
1886 FILE_GET_EA_INFORMATION fgei;
1887 char buf[sizeof (NFS_SYML_TARGET)];
1889 PFILE_FULL_EA_INFORMATION pffei;
1892 /* To find out if the file is a symlink and to get the symlink target,
1893 try to fetch the NfsSymlinkTargetName EA. */
1894 fgei_buf.fgei.NextEntryOffset = 0;
1895 fgei_buf.fgei.EaNameLength = sizeof (NFS_SYML_TARGET) - 1;
1896 stpcpy (fgei_buf.fgei.EaName, NFS_SYML_TARGET);
1897 pffei = (PFILE_FULL_EA_INFORMATION) tp.w_get ();
1898 status = NtQueryEaFile (h, &io, pffei, NT_MAX_PATH * sizeof (WCHAR), TRUE,
1899 &fgei_buf.fgei, sizeof fgei_buf, NULL, TRUE);
1900 if (NT_SUCCESS (status) && pffei->EaValueLength > 0)
1902 PWCHAR spath = (PWCHAR)
1903 (pffei->EaName + pffei->EaNameLength + 1);
1904 res = sys_wcstombs (contents, SYMLINK_MAX + 1,
1905 spath, pffei->EaValueLength);
1906 pflags = PATH_SYMLINK;
1912 symlink_info::posixify (char *srcbuf)
1914 /* The definition for a path in a native symlink is a bit weird. The Flags
1915 value seem to contain 0 for absolute paths (stored as NT native path)
1916 and 1 for relative paths. Relative paths are paths not starting with a
1917 drive letter. These are not converted to NT native, but stored as
1918 given. A path starting with a single backslash is relative to the
1919 current drive thus a "relative" value (Flags == 1).
1920 Funny enough it's possible to store paths with slashes instead of
1921 backslashes, but they are evaluated incorrectly by subsequent Windows
1922 calls like CreateFile (ERROR_INVALID_NAME). So, what we do here is to
1923 take paths starting with slashes at face value, evaluating them as
1924 Cygwin specific POSIX paths.
1925 A path starting with two slashes(!) or backslashes is converted into an
1926 NT UNC path. Unfortunately, in contrast to POSIX rules, paths starting
1927 with three or more (back)slashes are also converted into UNC paths,
1928 just incorrectly sticking to one redundant leading backslashe. We go
1929 along with this behaviour to avoid scenarios in which native tools access
1930 other files than Cygwin.
1931 The above rules are used exactly the same way on Cygwin specific symlinks
1932 (sysfiles and shortcuts) to eliminate non-POSIX paths in the output. */
1934 /* Eliminate native NT prefixes. */
1935 if (srcbuf[0] == '\\' && !strncmp (srcbuf + 1, "??\\", 3))
1938 if (srcbuf[1] != ':') /* native UNC path */
1939 *(srcbuf += 2) = '\\';
1941 if (isdrive (srcbuf))
1942 mount_table->conv_to_posix_path (srcbuf, contents, 0);
1943 else if (srcbuf[0] == '\\')
1945 if (srcbuf[1] == '\\') /* UNC path */
1946 slashify (srcbuf, contents, 0);
1947 else /* Paths starting with \ are current drive relative. */
1949 char cvtbuf[SYMLINK_MAX + 1];
1951 stpcpy (cvtbuf + cygheap->cwd.get_drive (cvtbuf), srcbuf);
1952 mount_table->conv_to_posix_path (cvtbuf, contents, 0);
1955 else /* Everything else is taken as is. */
1956 slashify (srcbuf, contents, 0);
1957 return strlen (contents);
1966 SCAN_JUSTCHECKTHIS, /* Never try to append a suffix. */
1974 const suffix_info *suffixes, *suffixes_start;
1979 char *has (const char *, const suffix_info *);
1981 int lnk_match () {return nextstate >= SCAN_APPENDLNK;}
1985 suffix_scan::has (const char *in_path, const suffix_info *in_suffixes)
1987 nextstate = SCAN_BEG;
1988 suffixes = suffixes_start = in_suffixes;
1990 const char *fname = strrchr (in_path, '\\');
1991 fname = fname ? fname + 1 : in_path;
1992 char *ext_here = strrchr (fname, '.');
1994 eopath = strchr (path, '\0');
2001 /* Check if the extension matches a known extension */
2002 for (const suffix_info *ex = in_suffixes; ex->name != NULL; ex++)
2003 if (ascii_strcasematch (ext_here, ex->name))
2005 nextstate = SCAN_JUSTCHECK;
2006 suffixes = NULL; /* Has an extension so don't scan for one. */
2011 /* Didn't match. Use last resort -- .lnk. */
2012 if (ascii_strcasematch (ext_here, ".lnk"))
2014 nextstate = SCAN_HASLNK;
2022 /* Avoid attaching suffixes if the resulting filename would be invalid. */
2023 if (eopath - fname > NAME_MAX - 4)
2025 nextstate = SCAN_JUSTCHECKTHIS;
2032 suffix_scan::next ()
2040 suffixes = suffixes_start;
2043 nextstate = SCAN_LNK;
2046 nextstate = SCAN_EXTRALNK;
2047 /* fall through to suffix checking below */
2050 nextstate = SCAN_APPENDLNK; /* Skip SCAN_BEG */
2053 nextstate = SCAN_DONE;
2056 case SCAN_JUSTCHECK:
2057 nextstate = SCAN_LNK;
2059 case SCAN_JUSTCHECKTHIS:
2060 nextstate = SCAN_DONE;
2063 case SCAN_APPENDLNK:
2064 strcat (eopath, ".lnk");
2065 nextstate = SCAN_DONE;
2072 while (suffixes && suffixes->name)
2073 if (nextstate == SCAN_EXTRALNK && !suffixes->addon)
2077 strcpy (eopath, suffixes->name);
2078 if (nextstate == SCAN_EXTRALNK)
2079 strcat (eopath, ".lnk");
2088 symlink_info::set_error (int in_errno)
2091 if (!(pflags & PATH_NO_ACCESS_CHECK) || in_errno == ENAMETOOLONG || in_errno == EIO)
2096 else if (in_errno == ENOENT)
2100 fileattr = FILE_ATTRIBUTE_NORMAL;
2107 symlink_info::parse_device (const char *contents)
2114 mymajor = strtol (contents += 2, &endptr, 16);
2115 if (endptr == contents)
2116 return isdevice = false;
2119 myminor = strtol (++contents, &endptr, 16);
2120 if (endptr == contents)
2121 return isdevice = false;
2124 mymode = strtol (++contents, &endptr, 16);
2125 if (endptr == contents)
2126 return isdevice = false;
2128 if ((mymode & S_IFMT) == S_IFIFO)
2130 mymajor = _major (FH_FIFO);
2131 myminor = _minor (FH_FIFO);
2137 return isdevice = true;
2140 /* Check if PATH is a symlink. PATH must be a valid Win32 path name.
2142 If PATH is a symlink, put the value of the symlink--the file to
2143 which it points--into BUF. The value stored in BUF is not
2144 necessarily null terminated. BUFLEN is the length of BUF; only up
2145 to BUFLEN characters will be stored in BUF. BUF may be NULL, in
2146 which case nothing will be stored.
2148 Set *SYML if PATH is a symlink.
2150 Set *EXEC if PATH appears to be executable. This is an efficiency
2151 hack because we sometimes have to open the file anyhow. *EXEC will
2152 not be set for every executable file.
2154 Return -1 on error, 0 if PATH is not a symlink, or the length
2155 stored into BUF if PATH is a symlink. */
2158 symlink_info::check (char *path, const suffix_info *suffixes, unsigned opt,
2168 ext_here = suffix.has (path, suffixes);
2169 extn = ext_here - path;
2173 pflags &= ~(PATH_SYMLINK | PATH_LNK | PATH_REP);
2174 ULONG ci_flag = cygwin_shared->obcaseinsensitive || (pflags & PATH_NOPOSIX)
2175 ? OBJ_CASE_INSENSITIVE : 0;
2177 /* TODO: Temporarily do all char->UNICODE conversion here. This should
2178 already be slightly faster than using Ascii functions. */
2180 UNICODE_STRING upath;
2181 OBJECT_ATTRIBUTES attr;
2183 InitializeObjectAttributes (&attr, &upath, ci_flag, NULL, NULL);
2185 PVOID eabuf = &nfs_aol_ffei;
2186 ULONG easize = sizeof nfs_aol_ffei;
2188 while (suffix.next ())
2190 FILE_BASIC_INFORMATION fbi;
2194 bool fs_update_called = false;
2197 get_nt_native_path (suffix.path, upath);
2203 /* The EA given to NtCreateFile allows to get a handle to a symlink on
2204 an NFS share, rather than getting a handle to the target of the
2205 symlink (which would spoil the task of this method quite a bit).
2206 Fortunately it's ignored on most other file systems so we don't have
2207 to special case NFS too much. */
2208 status = NtCreateFile (&h,
2209 READ_CONTROL | FILE_READ_ATTRIBUTES | FILE_READ_EA,
2210 &attr, &io, NULL, 0, FILE_SHARE_VALID_FLAGS,
2212 FILE_OPEN_REPARSE_POINT
2213 | FILE_OPEN_FOR_BACKUP_INTENT,
2215 /* No right to access EAs or EAs not supported? */
2216 if (status == STATUS_ACCESS_DENIED || status == STATUS_EAS_NOT_SUPPORTED
2217 || status == STATUS_NOT_SUPPORTED
2218 /* Or a bug in Samba 3.2.x (x <= 7) when accessing a share's root dir
2219 which has EAs enabled? */
2220 || status == STATUS_INVALID_PARAMETER)
2223 /* If EAs are not supported, there's no sense to check them again
2224 with suffixes attached. So we set eabuf/easize to 0 here once. */
2225 if (status == STATUS_EAS_NOT_SUPPORTED
2226 || status == STATUS_NOT_SUPPORTED)
2231 status = NtOpenFile (&h, READ_CONTROL | FILE_READ_ATTRIBUTES,
2232 &attr, &io, FILE_SHARE_VALID_FLAGS,
2233 FILE_OPEN_REPARSE_POINT
2234 | FILE_OPEN_FOR_BACKUP_INTENT);
2236 if (status == STATUS_OBJECT_NAME_NOT_FOUND && ci_flag == 0
2237 && wincap.has_broken_udf ())
2239 /* On NT 5.x UDF is broken (at least) in terms of case sensitivity.
2240 When trying to open a file case sensitive, the file appears to be
2241 non-existant. Another bug is described in fs_info::update. */
2242 attr.Attributes = OBJ_CASE_INSENSITIVE;
2243 status = NtOpenFile (&h, READ_CONTROL | FILE_READ_ATTRIBUTES,
2244 &attr, &io, FILE_SHARE_VALID_FLAGS,
2245 FILE_OPEN_REPARSE_POINT
2246 | FILE_OPEN_FOR_BACKUP_INTENT);
2247 attr.Attributes = 0;
2248 if (NT_SUCCESS (status))
2250 fs.update (&upath, h);
2252 fs_update_called = true;
2256 status = STATUS_OBJECT_NAME_NOT_FOUND;
2260 if (NT_SUCCESS (status)
2261 && NT_SUCCESS (status
2262 = NtQueryInformationFile (h, &io, &fbi, sizeof fbi,
2263 FileBasicInformation)))
2264 fileattr = fbi.FileAttributes;
2267 debug_printf ("%p = NtQueryInformationFile (%S)", status, &upath);
2269 fileattr = INVALID_FILE_ATTRIBUTES;
2271 /* One of the inner path components is invalid, or the path contains
2272 invalid characters. Bail out with ENOENT.
2274 Note that additional STATUS_OBJECT_PATH_INVALID and
2275 STATUS_OBJECT_PATH_SYNTAX_BAD status codes exist. The first one
2276 is seemingly not generated by NtQueryAttributesFile, the latter
2277 is only generated if the path is no absolute path within the
2278 NT name space, which should not happen and would point to an
2279 error in get_nt_native_path. Both status codes are deliberately
2280 not tested here unless proved necessary. */
2281 if (status == STATUS_OBJECT_PATH_NOT_FOUND
2282 || status == STATUS_OBJECT_NAME_INVALID
2283 || status == STATUS_NO_MEDIA_IN_DEVICE)
2286 goto file_not_symlink;
2288 if (status != STATUS_OBJECT_NAME_NOT_FOUND
2289 && status != STATUS_NO_SUCH_FILE) /* ENOENT on NFS or 9x share */
2291 /* The file exists, but the user can't access it for one reason
2292 or the other. To get the file attributes we try to access the
2293 information by opening the parent directory and getting the
2294 file attributes using a matching NtQueryDirectoryFile call. */
2295 UNICODE_STRING dirname, basename;
2296 OBJECT_ATTRIBUTES dattr;
2299 FILE_DIRECTORY_INFORMATION fdi;
2300 WCHAR dummy_buf[NAME_MAX + 1];
2303 RtlSplitUnicodePath (&upath, &dirname, &basename);
2304 InitializeObjectAttributes (&dattr, &dirname, ci_flag,
2306 status = NtOpenFile (&dir, SYNCHRONIZE | FILE_LIST_DIRECTORY,
2307 &dattr, &io, FILE_SHARE_VALID_FLAGS,
2308 FILE_SYNCHRONOUS_IO_NONALERT
2309 | FILE_OPEN_FOR_BACKUP_INTENT
2310 | FILE_DIRECTORY_FILE);
2311 if (!NT_SUCCESS (status))
2313 debug_printf ("%p = NtOpenFile(%S)", status, &dirname);
2318 status = NtQueryDirectoryFile (dir, NULL, NULL, NULL, &io,
2319 &fdi_buf, sizeof fdi_buf,
2320 FileDirectoryInformation,
2321 TRUE, &basename, TRUE);
2322 /* Take the opportunity to check file system while we're
2323 having the handle to the parent dir. */
2324 fs.update (&upath, h);
2326 if (!NT_SUCCESS (status))
2328 debug_printf ("%p = NtQueryDirectoryFile(%S)",
2330 if (status == STATUS_NO_SUCH_FILE)
2332 /* This can happen when trying to access files
2333 which match DOS device names on SMB shares.
2334 NtOpenFile failed with STATUS_ACCESS_DENIED,
2335 but the NtQueryDirectoryFile tells us the
2336 file doesn't exist. We're suspicious in this
2337 case and retry with the next suffix instead of
2345 fileattr = fdi_buf.fdi.FileAttributes;
2347 ext_tacked_on = !!*ext_here;
2348 goto file_not_symlink;
2354 /* Check file system while we're having the file open anyway.
2355 This speeds up path_conv noticably (~10%). */
2356 if (!fs_update_called)
2357 fs.update (&upath, h);
2359 ext_tacked_on = !!*ext_here;
2363 /* Windows shortcuts are potentially treated as symlinks. Valid Cygwin
2364 & U/WIN shortcuts are R/O, but definitely not directories. */
2365 if ((fileattr & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_DIRECTORY))
2366 == FILE_ATTRIBUTE_READONLY && suffix.lnk_match ())
2368 res = check_shortcut (h);
2371 /* If searching for `foo' and then finding a `foo.lnk' which is
2372 no shortcut, return the same as if file not found. */
2375 fileattr = INVALID_FILE_ATTRIBUTES;
2380 else if (contents[0] != ':' || contents[1] != '\\'
2381 || !parse_device (contents))
2385 /* If searching for `foo' and then finding a `foo.lnk' which is
2386 no shortcut, return the same as if file not found. */
2387 else if (suffix.lnk_match () && ext_tacked_on)
2389 fileattr = INVALID_FILE_ATTRIBUTES;
2394 /* Reparse points are potentially symlinks. This check must be
2395 performed before checking the SYSTEM attribute for sysfile
2396 symlinks, since reparse points can have this flag set, too.
2397 For instance, Vista starts to create a couple of reparse points
2398 with SYSTEM and HIDDEN flags set. */
2399 else if (fileattr & FILE_ATTRIBUTE_REPARSE_POINT)
2401 res = check_reparse_point (h);
2406 /* This is the old Cygwin method creating symlinks. A symlink will
2407 have the `system' file attribute. Only files can be symlinks
2408 (which can be symlinks to directories). */
2409 else if ((fileattr & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DIRECTORY))
2410 == FILE_ATTRIBUTE_SYSTEM)
2412 res = check_sysfile (h);
2417 /* If the file could be opened with FILE_READ_EA, and if it's on a
2418 NFS share, check if it's a symlink. Only files can be symlinks
2419 (which can be symlinks to directories). */
2420 else if (fs.is_nfs () && !no_ea && !(fileattr & FILE_ATTRIBUTE_DIRECTORY))
2422 res = check_nfs_symlink (h);
2430 syscall_printf ("%s", isdevice ? "is a device" : "not a symlink");
2438 syscall_printf ("%d = symlink.check (%s, %p) (%p)",
2439 res, suffix.path, contents, pflags);
2443 /* "path" is the path in a virtual symlink. Set a symlink_info struct from
2444 that and proceed with further path checking afterwards. */
2446 symlink_info::set (char *path)
2448 strcpy (contents, path);
2449 pflags = PATH_SYMLINK;
2450 fileattr = FILE_ATTRIBUTE_NORMAL;
2454 ext_tacked_on = false;
2456 extn = major = minor = mode = 0;
2457 return strlen (path);
2460 /* readlink system call */
2463 readlink (const char *path, char *buf, size_t buflen)
2467 set_errno (ENAMETOOLONG);
2471 path_conv pathbuf (path, PC_SYM_CONTENTS, stat_suffixes);
2475 set_errno (pathbuf.error);
2476 syscall_printf ("-1 = readlink (%s, %p, %d)", path, buf, buflen);
2480 if (!pathbuf.exists ())
2486 if (!pathbuf.issymlink ())
2488 if (pathbuf.exists ())
2493 ssize_t len = min (buflen, strlen (pathbuf.get_win32 ()));
2494 memcpy (buf, pathbuf.get_win32 (), len);
2496 /* errno set by symlink.check if error */
2500 /* Some programs rely on st_dev/st_ino being unique for each file.
2501 Hash the path name and hope for the best. The hash arg is not
2502 always initialized to zero since readdir needs to compute the
2503 dirent ino_t based on a combination of the hash of the directory
2504 done during the opendir call and the hash or the filename within
2505 the directory. FIXME: Not bullet-proof. */
2506 /* Cygwin internal */
2508 hash_path_name (__ino64_t hash, PUNICODE_STRING name)
2510 if (name->Length == 0)
2513 /* Build up hash. Name is already normalized */
2514 USHORT len = name->Length / sizeof (WCHAR);
2515 for (USHORT idx = 0; idx < len; ++idx)
2516 hash = RtlUpcaseUnicodeChar (name->Buffer[idx])
2517 + (hash << 6) + (hash << 16) - hash;
2522 hash_path_name (__ino64_t hash, PCWSTR name)
2524 UNICODE_STRING uname;
2525 RtlInitUnicodeString (&uname, name);
2526 return hash_path_name (hash, &uname);
2530 hash_path_name (__ino64_t hash, const char *name)
2532 UNICODE_STRING uname;
2533 RtlCreateUnicodeStringFromAsciiz (&uname, name);
2534 __ino64_t ret = hash_path_name (hash, &uname);
2535 RtlFreeUnicodeString (&uname);
2540 getcwd (char *buf, size_t ulen)
2544 if (efault.faulted (EFAULT))
2546 else if (ulen == 0 && buf)
2549 res = cygheap->cwd.get (buf, 1, 1, ulen);
2553 /* getwd: Legacy. */
2557 return getcwd (buf, PATH_MAX + 1); /*Per SuSv3!*/
2560 /* chdir: POSIX 5.2.1.1 */
2562 chdir (const char *in_dir)
2565 if (efault.faulted (EFAULT))
2573 syscall_printf ("dir '%s'", in_dir);
2575 /* Convert path. First argument ensures that we don't check for NULL/empty/invalid
2577 path_conv path (PC_NONULLEMPTY, in_dir, PC_SYM_FOLLOW | PC_POSIX);
2580 set_errno (path.error);
2581 syscall_printf ("-1 = chdir (%s)", in_dir);
2587 const char *posix_cwd = NULL;
2588 int devn = path.get_devn ();
2589 if (!path.exists ())
2591 else if (!path.isdir ())
2592 set_errno (ENOTDIR);
2593 else if (!isvirtual_dev (devn))
2595 /* The sequence chdir("xx"); chdir(".."); must be a noop if xx
2596 is not a symlink. This is exploited by find.exe.
2597 The posix_cwd is just path.normalized_path.
2598 In other cases we let cwd.set obtain the Posix path through
2600 if (!isdrive(path.normalized_path))
2601 posix_cwd = path.normalized_path;
2607 posix_cwd = path.normalized_path;
2612 res = cygheap->cwd.set (path.get_nt_native_path (), posix_cwd, doit);
2614 /* Note that we're accessing cwd.posix without a lock here. I didn't think
2615 it was worth locking just for strace. */
2616 syscall_printf ("%d = chdir() cygheap->cwd.posix '%s' native '%S'", res,
2617 cygheap->cwd.get_posix (), path.get_nt_native_path ());
2626 cygheap_fdget cfd (fd);
2628 res = chdir (cfd->get_name ());
2632 syscall_printf ("%d = fchdir (%d)", res, fd);
2636 /******************** Exported Path Routines *********************/
2638 /* Cover functions to the path conversion routines.
2639 These are exported to the world as cygwin_foo by cygwin.din. */
2641 #define return_with_errno(x) \
2651 cygwin_conv_path (cygwin_conv_path_t what, const void *from, void *to,
2656 if (efault.faulted (EFAULT))
2663 bool relative = !!(what & CCP_RELATIVE);
2664 what &= ~CCP_RELATIVE;
2668 case CCP_POSIX_TO_WIN_A:
2670 p.check ((const char *) from,
2671 PC_POSIX | PC_SYM_FOLLOW | PC_NO_ACCESS_CHECK | PC_NOWARN
2672 | (relative ? PC_NOFULL : 0));
2674 return_with_errno (p.error);
2675 PUNICODE_STRING up = p.get_nt_native_path ();
2677 sys_wcstombs (buf, NT_MAX_PATH, up->Buffer, up->Length / sizeof (WCHAR));
2678 /* Convert native path to standard DOS path. */
2679 if (!strncmp (buf, "\\??\\", 4))
2682 if (buf[1] != ':') /* native UNC path */
2685 lsiz = strlen (buf) + 1;
2688 case CCP_POSIX_TO_WIN_W:
2689 p.check ((const char *) from, PC_POSIX | PC_SYM_FOLLOW
2690 | PC_NO_ACCESS_CHECK | PC_NOWARN
2691 | (relative ? PC_NOFULL : 0));
2693 return_with_errno (p.error);
2694 /* Relative Windows paths are always restricted to MAX_PATH chars. */
2695 if (relative && !isabspath (p.get_win32 ())
2696 && sys_mbstowcs (NULL, 0, p.get_win32 ()) > MAX_PATH)
2698 /* Recreate as absolute path. */
2699 p.check ((const char *) from, PC_POSIX | PC_SYM_FOLLOW
2700 | PC_NO_ACCESS_CHECK | PC_NOWARN);
2702 return_with_errno (p.error);
2704 lsiz = (p.get_wide_win32_path_len () + 1) * sizeof (WCHAR);
2706 case CCP_WIN_A_TO_POSIX:
2708 error = mount_table->conv_to_posix_path ((const char *) from, buf,
2711 return_with_errno (error);
2712 lsiz = strlen (buf) + 1;
2714 case CCP_WIN_W_TO_POSIX:
2716 error = mount_table->conv_to_posix_path ((const PWCHAR) from, buf,
2719 return_with_errno (error);
2720 lsiz = strlen (buf) + 1;
2735 case CCP_POSIX_TO_WIN_A:
2736 case CCP_WIN_A_TO_POSIX:
2737 case CCP_WIN_W_TO_POSIX:
2738 strcpy ((char *) to, buf);
2740 case CCP_POSIX_TO_WIN_W:
2741 p.get_wide_win32_path ((PWCHAR) to);
2748 cygwin_create_path (cygwin_conv_path_t what, const void *from)
2751 ssize_t size = cygwin_conv_path (what, from, NULL, 0);
2754 if (!(to = malloc (size)))
2756 if (cygwin_conv_path (what, from, to, size) == -1)
2763 cygwin_conv_to_win32_path (const char *path, char *win32_path)
2765 return cygwin_conv_path (CCP_POSIX_TO_WIN_A | CCP_RELATIVE, path, win32_path,
2770 cygwin_conv_to_full_win32_path (const char *path, char *win32_path)
2772 return cygwin_conv_path (CCP_POSIX_TO_WIN_A | CCP_ABSOLUTE, path, win32_path,
2776 /* This is exported to the world as cygwin_foo by cygwin.din. */
2779 cygwin_conv_to_posix_path (const char *path, char *posix_path)
2781 return cygwin_conv_path (CCP_WIN_A_TO_POSIX | CCP_RELATIVE, path, posix_path,
2786 cygwin_conv_to_full_posix_path (const char *path, char *posix_path)
2788 return cygwin_conv_path (CCP_WIN_A_TO_POSIX | CCP_ABSOLUTE, path, posix_path,
2792 /* The realpath function is supported on some UNIX systems. */
2795 realpath (const char *path, char *resolved)
2797 /* Make sure the right errno is returned if path is NULL. */
2804 /* Guard reading from a potentially invalid path and writing to a
2805 potentially invalid resolved. */
2808 if (efault.faulted (EFAULT))
2814 tpath = tp.c_get ();
2815 mount_table->cygdrive_posix_path (path, tpath, 0);
2818 tpath = (char *) path;
2820 path_conv real_path (tpath, PC_SYM_FOLLOW | PC_POSIX, stat_suffixes);
2823 /* Linux has this funny non-standard extension. If "resolved" is NULL,
2824 realpath mallocs the space by itself and returns it to the application.
2825 The application is responsible for calling free() then. This extension
2826 is backed by POSIX, which allows implementation-defined behaviour if
2827 "resolved" is NULL. That's good enough for us to do the same here. */
2829 if (!real_path.error && real_path.exists ())
2833 resolved = (char *) malloc (strlen (real_path.normalized_path) + 1);
2837 strcpy (resolved, real_path.normalized_path);
2841 /* FIXME: on error, we are supposed to put the name of the path
2842 component which could not be resolved into RESOLVED. */
2845 set_errno (real_path.error ?: ENOENT);
2849 /* Return non-zero if path is a POSIX path list.
2850 This is exported to the world as cygwin_foo by cygwin.din.
2853 <sect1 id="add-func-cygwin-posix-path-list-p">
2854 <para>Rather than use a mode to say what the "proper" path list
2855 format is, we allow any, and give apps the tools they need to
2856 convert between the two. If a ';' is present in the path list it's
2857 a Win32 path list. Otherwise, if the first path begins with
2858 [letter]: (in which case it can be the only element since if it
2859 wasn't a ';' would be present) it's a Win32 path list. Otherwise,
2860 it's a POSIX path list.</para>
2866 cygwin_posix_path_list_p (const char *path)
2868 int posix_p = !(strchr (path, ';') || isdrive (path));
2872 /* These are used for apps that need to convert env vars like PATH back and
2873 forth. The conversion is a two step process. First, an upper bound on the
2874 size of the buffer needed is computed. Then the conversion is done. This
2875 allows the caller to use alloca if it wants. */
2878 conv_path_list_buf_size (const char *path_list, bool to_posix)
2880 int i, num_elms, max_mount_path_len, size;
2883 path_conv pc(".", PC_POSIX);
2884 /* The theory is that an upper bound is
2885 current_size + (num_elms * max_mount_path_len) */
2886 /* FIXME: This method is questionable in the long run. */
2889 char delim = to_posix ? ';' : ':';
2890 for (p = path_list, num_elms = nrel = 0; p; num_elms++)
2894 p = strchr (++p, delim);
2897 /* 7: strlen ("//c") + slop, a conservative initial value */
2898 for (max_mount_path_len = sizeof ("/cygdrive/X"), i = 0;
2899 i < mount_table->nmounts; i++)
2901 int mount_len = (to_posix
2902 ? mount_table->mount[i].posix_pathlen
2903 : mount_table->mount[i].native_pathlen);
2904 if (max_mount_path_len < mount_len)
2905 max_mount_path_len = mount_len;
2909 size = strlen (path_list)
2910 + (num_elms * max_mount_path_len)
2911 + (nrel * strlen (to_posix ? pc.normalized_path : pc.get_win32 ()))
2919 cygwin_win32_to_posix_path_list_buf_size (const char *path_list)
2921 return conv_path_list_buf_size (path_list, true);
2925 cygwin_posix_to_win32_path_list_buf_size (const char *path_list)
2927 return conv_path_list_buf_size (path_list, false);
2931 env_PATH_to_posix (const void *win32, void *posix, size_t size)
2933 return_with_errno (conv_path_list ((const char *) win32, (char *) posix,
2938 cygwin_win32_to_posix_path_list (const char *win32, char *posix)
2940 return_with_errno (conv_path_list (win32, posix, MAX_PATH, 1));
2944 cygwin_posix_to_win32_path_list (const char *posix, char *win32)
2946 return_with_errno (conv_path_list (posix, win32, MAX_PATH, 0));
2950 cygwin_conv_path_list (cygwin_conv_path_t what, const void *from, void *to,
2953 /* FIXME: Path lists are (so far) always retaining relative paths. */
2954 what &= ~CCP_RELATIVE;
2957 case CCP_WIN_W_TO_POSIX:
2958 case CCP_POSIX_TO_WIN_W:
2960 api_fatal ("wide char path lists not yet supported");
2962 case CCP_WIN_A_TO_POSIX:
2963 case CCP_POSIX_TO_WIN_A:
2965 return conv_path_list_buf_size ((const char *) from,
2966 what == CCP_WIN_A_TO_POSIX);
2967 return_with_errno (conv_path_list ((const char *) from, (char *) to,
2968 size, what == CCP_WIN_A_TO_POSIX));
2977 /* cygwin_split_path: Split a path into directory and file name parts.
2978 Buffers DIR and FILE are assumed to be big enough.
2980 Examples (path -> `dir' / `file'):
2983 . -> `.' / `.' (FIXME: should this be `.' / `'?)
2984 .. -> `.' / `..' (FIXME: should this be `..' / `'?)
2986 foo/bar -> `foo' / `bar'
2987 foo/bar/ -> `foo' / `bar'
2989 /foo/bar -> `/foo' / `bar'
2992 c:foo -> `c:/' / `foo'
2993 c:/foo -> `c:/' / `foo'
2997 cygwin_split_path (const char *path, char *dir, char *file)
2999 int dir_started_p = 0;
3001 /* Deal with drives.
3002 Remember that c:foo <==> c:/foo. */
3014 if (isdirsep (*path))
3019 /* Determine if there are trailing slashes and "delete" them if present.
3020 We pretend as if they don't exist. */
3021 const char *end = path + strlen (path);
3022 /* path + 1: keep leading slash. */
3023 while (end > path + 1 && isdirsep (end[-1]))
3026 /* At this point, END points to one beyond the last character
3027 (with trailing slashes "deleted"). */
3029 /* Point LAST_SLASH at the last slash (duh...). */
3030 const char *last_slash;
3031 for (last_slash = end - 1; last_slash >= path; --last_slash)
3032 if (isdirsep (*last_slash))
3035 if (last_slash == path)
3040 else if (last_slash > path)
3042 memcpy (dir, path, last_slash - path);
3043 dir[last_slash - path] = 0;
3048 ; /* nothing to do */
3054 memcpy (file, last_slash + 1, end - last_slash - 1);
3055 file[end - last_slash - 1] = 0;
3058 /*****************************************************************************/
3060 static inline PRTL_USER_PROCESS_PARAMETERS
3061 get_user_proc_parms ()
3063 return NtCurrentTeb ()->Peb->ProcessParameters;
3066 /* Initialize cygcwd 'muto' for serializing access to cwd info. */
3070 cwd_lock.init ("cwd_lock");
3071 /* Initially re-open the cwd to allow POSIX semantics. */
3072 set (NULL, NULL, true);
3075 /* Chdir and fill out the elements of a cwdstuff struct. */
3077 cwdstuff::set (PUNICODE_STRING nat_cwd, const char *posix_cwd, bool doit)
3080 UNICODE_STRING upath;
3083 cwd_lock.acquire ();
3088 if (upath.Buffer[0] == L'/') /* Virtual path. Never use in PEB. */
3092 len = upath.Length / sizeof (WCHAR) - 4;
3093 if (RtlEqualUnicodePathPrefix (&upath, &ro_u_uncp, TRUE))
3100 /* We utilize the user parameter block. The directory is
3101 stored manually there. Why the hassle?
3103 - SetCurrentDirectory fails for directories with strict
3104 permissions even for processes with the SE_BACKUP_NAME
3105 privilege enabled. The reason is apparently that
3106 SetCurrentDirectory calls NtOpenFile without the
3107 FILE_OPEN_FOR_BACKUP_INTENT flag set.
3109 - Unlinking a cwd fails because SetCurrentDirectory seems to
3110 open directories so that deleting the directory is disallowed.
3111 The below code opens with *all* sharing flags set. */
3115 OBJECT_ATTRIBUTES attr;
3118 RtlAcquirePebLock ();
3119 phdl = &get_user_proc_parms ()->CurrentDirectoryHandle;
3120 if (!nat_cwd) /* On init, just reopen CWD with desired access flags. */
3121 RtlInitUnicodeString (&upath, L"");
3122 /* This is for Win32 apps only. No case sensitivity here... */
3123 InitializeObjectAttributes (&attr, &upath,
3124 OBJ_CASE_INSENSITIVE | OBJ_INHERIT,
3125 nat_cwd ? NULL : *phdl, NULL);
3126 status = NtOpenFile (&h, SYNCHRONIZE | FILE_TRAVERSE, &attr, &io,
3127 FILE_SHARE_VALID_FLAGS,
3129 | FILE_SYNCHRONOUS_IO_NONALERT
3130 | FILE_OPEN_FOR_BACKUP_INTENT);
3131 if (!NT_SUCCESS (status))
3133 RtlReleasePebLock ();
3134 __seterrno_from_nt_status (status);
3138 /* Workaround a problem in Vista/Longhorn which fails in subsequent
3139 calls to CreateFile with ERROR_INVALID_HANDLE if the handle in
3140 CurrentDirectoryHandle changes without calling SetCurrentDirectory,
3141 and the filename given to CreateFile is a relative path. It looks
3142 like Vista stores a copy of the CWD handle in some other undocumented
3143 place. The NtClose/DuplicateHandle reuses the original handle for
3144 the copy of the new handle and the next CreateFile works.
3145 Note that this is not thread-safe (yet?) */
3147 if (DuplicateHandle (GetCurrentProcess (), h, GetCurrentProcess (), phdl,
3148 0, TRUE, DUPLICATE_SAME_ACCESS))
3154 /* No need to set path on init. */
3157 Check the length of the new CWD. Windows can only handle
3158 CWDs of up to MAX_PATH length, including a trailing backslash.
3159 If the path is longer, it's not an error condition for Cygwin,
3160 so we don't fail. Windows on the other hand has a problem now.
3161 For now, we just don't store the path in the PEB and proceed as
3163 && len <= MAX_PATH - (nat_cwd->Buffer[len - 1] == L'\\' ? 1 : 2))
3165 /* Convert to a Win32 path. */
3166 upath.Buffer += upath.Length / sizeof (WCHAR) - len;
3167 if (upath.Buffer[1] == L'\\') /* UNC path */
3168 upath.Buffer[0] = L'\\';
3169 upath.Length = len * sizeof (WCHAR);
3170 /* Append backslash if necessary. */
3171 if (upath.Buffer[len - 1] != L'\\')
3173 upath.Buffer[len] = L'\\';
3174 upath.Length += sizeof (WCHAR);
3176 RtlCopyUnicodeString (&get_user_proc_parms ()->CurrentDirectoryName,
3180 RtlReleasePebLock ();
3183 if (nat_cwd || !win32.Buffer)
3185 /* If there is no win32 path */
3188 PUNICODE_STRING pdir;
3190 RtlAcquirePebLock ();
3191 pdir = &get_user_proc_parms ()->CurrentDirectoryName;
3192 RtlInitEmptyUnicodeString (&win32,
3193 (PWCHAR) crealloc_abort (win32.Buffer,
3196 RtlCopyUnicodeString (&win32, pdir);
3197 RtlReleasePebLock ();
3199 PWSTR eoBuffer = win32.Buffer + (win32.Length / sizeof (WCHAR));
3200 /* Remove trailing slash if one exists. FIXME: Is there a better way to
3202 if ((eoBuffer - win32.Buffer) > 3 && eoBuffer[-1] == L'\\')
3203 win32.Length -= sizeof (WCHAR);
3211 if (upath.Buffer[0] == L'/') /* Virtual path, don't mangle. */
3215 /* Convert to a Win32 path. */
3216 upath.Buffer += upath.Length / sizeof (WCHAR) - len;
3217 if (upath.Buffer[1] == L'\\') /* UNC path */
3219 upath.Length = len * sizeof (WCHAR);
3223 PWSTR eoBuffer = upath.Buffer + (upath.Length / sizeof (WCHAR));
3224 /* Remove trailing slash if one exists. FIXME: Is there a better way to
3226 if ((eoBuffer - upath.Buffer) > 3 && eoBuffer[-1] == L'\\')
3227 upath.Length -= sizeof (WCHAR);
3229 RtlInitEmptyUnicodeString (&win32,
3230 (PWCHAR) crealloc_abort (win32.Buffer,
3233 RtlCopyUnicodeString (&win32, &upath);
3235 win32.Buffer[0] = L'\\';
3237 /* Make sure it's NUL-terminated. */
3238 win32.Buffer[win32.Length / sizeof (WCHAR)] = L'\0';
3239 if (!doit) /* Virtual path */
3241 else if (win32.Buffer[1] == L':') /* X: */
3243 else if (win32.Buffer[1] == L'\\') /* UNC path */
3245 PWCHAR ptr = wcschr (win32.Buffer + 2, L'\\');
3247 ptr = wcschr (ptr + 1, L'\\');
3249 drive_length = ptr - win32.Buffer;
3251 drive_length = win32.Length / sizeof (WCHAR);
3253 else /* Shouldn't happen */
3259 posix_cwd = (const char *) tp.c_get ();
3260 mount_table->conv_to_posix_path (win32.Buffer, (char *) posix_cwd, 0);
3267 cwd_lock.release ();
3271 /* Copy the value for either the posix or the win32 cwd into a buffer. */
3273 cwdstuff::get_posix ()
3275 if (!posix || !*posix)
3279 char *tocopy = tp.c_get ();
3280 mount_table->conv_to_posix_path (win32.Buffer, tocopy, 0);
3281 posix = (char *) crealloc_abort (posix, strlen (tocopy) + 1);
3282 stpcpy (posix, tocopy);
3288 cwdstuff::get (char *buf, int need_posix, int with_chroot, unsigned ulen)
3295 else if (buf == NULL)
3296 ulen = (unsigned) -1;
3303 cwd_lock.acquire ();
3308 tocopy = tp.c_get ();
3309 sys_wcstombs (tocopy, NT_MAX_PATH, win32.Buffer,
3310 win32.Length / sizeof (WCHAR));
3312 else if (!posix || !*posix)
3314 tocopy = tp.c_get ();
3315 mount_table->conv_to_posix_path (win32.Buffer, tocopy, 0);
3316 posix = (char *) crealloc_abort (posix, strlen (tocopy) + 1);
3317 stpcpy (posix, tocopy);
3322 debug_printf ("posix %s", posix);
3323 if (strlen (tocopy) >= ulen)
3331 buf = (char *) malloc (strlen (tocopy) + 1);
3332 strcpy (buf, tocopy);
3333 if (!buf[0]) /* Should only happen when chroot */
3337 cwd_lock.release ();
3340 syscall_printf ("(%s) = cwdstuff::get (%p, %d, %d, %d), errno %d",
3341 buf, buf, ulen, need_posix, with_chroot, errno);
3346 int etc::curr_ix = 0;
3347 /* Note that the first elements of the below arrays are unused */
3348 bool etc::change_possible[MAX_ETC_FILES + 1];
3349 OBJECT_ATTRIBUTES etc::fn[MAX_ETC_FILES + 1];
3350 LARGE_INTEGER etc::last_modified[MAX_ETC_FILES + 1];
3353 etc::init (int n, POBJECT_ATTRIBUTES attr)
3357 else if (++curr_ix <= MAX_ETC_FILES)
3360 api_fatal ("internal error");
3363 change_possible[n] = false;
3364 test_file_change (n);
3365 paranoid_printf ("fn[%d] %S, curr_ix %d", n, fn[n].ObjectName, curr_ix);
3370 etc::test_file_change (int n)
3373 FILE_NETWORK_OPEN_INFORMATION fnoi;
3376 status = NtQueryFullAttributesFile (&fn[n], &fnoi);
3377 if (!NT_SUCCESS (status))
3380 memset (last_modified + n, 0, sizeof (last_modified[n]));
3381 debug_printf ("NtQueryFullAttributesFile (%S) failed, %p",
3382 fn[n].ObjectName, status);
3386 res = CompareFileTime ((FILETIME *) &fnoi.LastWriteTime,
3387 (FILETIME *) last_modified + n) > 0;
3388 last_modified[n].QuadPart = fnoi.LastWriteTime.QuadPart;
3391 paranoid_printf ("fn[%d] %S res %d", n, fn[n].ObjectName, res);
3396 etc::dir_changed (int n)
3398 if (!change_possible[n])
3400 static HANDLE changed_h NO_COPY;
3406 OBJECT_ATTRIBUTES attr;
3408 path_conv dir ("/etc");
3409 status = NtOpenFile (&changed_h, SYNCHRONIZE | FILE_LIST_DIRECTORY,
3410 dir.get_object_attr (attr, sec_none_nih), &io,
3411 FILE_SHARE_VALID_FLAGS, FILE_DIRECTORY_FILE);
3412 if (!NT_SUCCESS (status))
3415 system_printf ("NtOpenFile (%S) failed, %p",
3416 dir.get_nt_native_path (), status);
3418 changed_h = INVALID_HANDLE_VALUE;
3422 status = NtNotifyChangeDirectoryFile (changed_h, NULL, NULL,
3424 FILE_NOTIFY_CHANGE_LAST_WRITE
3425 | FILE_NOTIFY_CHANGE_FILE_NAME,
3427 if (!NT_SUCCESS (status))
3430 system_printf ("NtNotifyChangeDirectoryFile (1) failed, %p",
3433 NtClose (changed_h);
3434 changed_h = INVALID_HANDLE_VALUE;
3437 memset (change_possible, true, sizeof (change_possible));
3440 if (changed_h == INVALID_HANDLE_VALUE)
3441 change_possible[n] = true;
3442 else if (WaitForSingleObject (changed_h, 0) == WAIT_OBJECT_0)
3444 status = NtNotifyChangeDirectoryFile (changed_h, NULL, NULL,
3446 FILE_NOTIFY_CHANGE_LAST_WRITE
3447 | FILE_NOTIFY_CHANGE_FILE_NAME,
3449 if (!NT_SUCCESS (status))
3452 system_printf ("NtNotifyChangeDirectoryFile (2) failed, %p",
3455 NtClose (changed_h);
3456 changed_h = INVALID_HANDLE_VALUE;
3458 memset (change_possible, true, sizeof change_possible);
3462 paranoid_printf ("fn[%d] %S change_possible %d",
3463 n, fn[n].ObjectName, change_possible[n]);
3464 return change_possible[n];
3468 etc::file_changed (int n)
3471 if (dir_changed (n) && test_file_change (n))
3473 change_possible[n] = false; /* Change is no longer possible */
3474 paranoid_printf ("fn[%d] %S res %d", n, fn[n].ObjectName, res);
3478 /* No need to be reentrant or thread-safe according to SUSv3.
3479 / and \\ are treated equally. Leading drive specifiers are
3480 kept intact as far as it makes sense. Everything else is
3481 POSIX compatible. */
3483 basename (char *path)
3486 char *c, *d, *bs = path;
3488 if (!path || !*path)
3489 return strcpy (buf, ".");
3490 if (isalpha (path[0]) && path[1] == ':')
3492 else if (strspn (path, "/\\") > 1)
3494 c = strrchr (bs, '/');
3495 if ((d = strrchr (c ?: bs, '\\')) > c)
3499 /* Trailing (back)slashes are eliminated. */
3500 while (c && c > bs && c[1] == '\0')
3503 c = strrchr (bs, '/');
3504 if ((d = strrchr (c ?: bs, '\\')) > c)
3507 if (c && (c > bs || c[1]))
3512 stpncpy (buf, path, bs - path);
3513 stpcpy (buf + (bs - path), ".");
3519 /* No need to be reentrant or thread-safe according to SUSv3.
3520 / and \\ are treated equally. Leading drive specifiers and
3521 leading double (back)slashes are kept intact as far as it
3522 makes sense. Everything else is POSIX compatible. */
3524 dirname (char *path)
3527 char *c, *d, *bs = path;
3529 if (!path || !*path)
3530 return strcpy (buf, ".");
3531 if (isalpha (path[0]) && path[1] == ':')
3533 else if (strspn (path, "/\\") > 1)
3535 c = strrchr (bs, '/');
3536 if ((d = strrchr (c ?: bs, '\\')) > c)
3540 /* Trailing (back)slashes are eliminated. */
3541 while (c && c > bs && c[1] == '\0')
3544 c = strrchr (bs, '/');
3545 if ((d = strrchr (c ?: bs, '\\')) > c)
3552 /* More trailing (back)slashes are eliminated. */
3553 while (c > bs && (*c == '/' || *c == '\\'))
3561 stpncpy (buf, path, bs - path);
3562 stpcpy (buf + (bs - path), ".");