OSDN Git Service

4d5424c76ed967e0bd3bda039a200158ff6975ff
[pf3gnuchains/pf3gnuchains3x.git] / winsup / cygwin / path.cc
1 /* path.cc: path support.
2
3      Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
4      2006, 2007, 2008, 2009 Red Hat, Inc.
5
6   This file is part of Cygwin.
7
8   This software is a copyrighted work licensed under the terms of the
9   Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
10   details. */
11
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
16
17      Pathnames are handled as follows:
18
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.
22
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.
26
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>:
31
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.
35
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.
42
43      The semantics of mounting file systems is not intended to precisely
44      follow normal UNIX systems.
45
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
48      c: means c:\.
49   */
50
51 #include "winsup.h"
52 #include "miscfuncs.h"
53 #include <ctype.h>
54 #include <winioctl.h>
55 #include <wingdi.h>
56 #include <winuser.h>
57 #include <winnetwk.h>
58 #include <shlobj.h>
59 #include <sys/cygwin.h>
60 #include "cygerrno.h"
61 #include "security.h"
62 #include "path.h"
63 #include "fhandler.h"
64 #include "dtable.h"
65 #include "cygheap.h"
66 #include "shared_info.h"
67 #include "cygtls.h"
68 #include "tls_pbuf.h"
69 #include "environ.h"
70 #include "nfs.h"
71 #include <assert.h>
72 #include <ntdll.h>
73 #include <wchar.h>
74 #include <wctype.h>
75
76 bool dos_file_warning = true;
77
78 suffix_info stat_suffixes[] =
79 {
80   suffix_info ("", 1),
81   suffix_info (".exe", 1),
82   suffix_info (NULL)
83 };
84
85 struct symlink_info
86 {
87   char contents[SYMLINK_MAX + 1];
88   char *ext_here;
89   int extn;
90   unsigned pflags;
91   DWORD fileattr;
92   int issymlink;
93   bool ext_tacked_on;
94   int error;
95   bool isdevice;
96   _major_t major;
97   _minor_t minor;
98   _mode_t mode;
99   int check (char *path, const suffix_info *suffixes, unsigned opt,
100              fs_info &fs);
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);
109 };
110
111 muto NO_COPY cwdstuff::cwd_lock;
112
113 static const GUID GUID_shortcut
114                         = { 0x00021401L, 0, 0, {0xc0, 0, 0, 0, 0, 0, 0, 0x46}};
115
116 enum {
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. */
124 };
125
126 struct win_shortcut_hdr
127   {
128     DWORD size;         /* Header size in bytes.  Must contain 0x4c. */
129     GUID magic;         /* GUID of shortcut files. */
130     DWORD flags;        /* Content flags.  See above. */
131
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. */
137     FILETIME atime;
138     DWORD filesize;     /* Target filesize. */
139     DWORD icon_no;      /* Icon number. */
140
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. */
144   };
145
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.
148    Neither may be "".
149    LEN1 = strlen (PATH1).  It's passed because often it's already known.
150
151    Examples:
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
158 */
159
160 int
161 path_prefix_p (const char *path1, const char *path2, int len1,
162                bool caseinsensitive)
163 {
164   /* Handle case where PATH1 has trailing '/' and when it doesn't.  */
165   if (len1 > 0 && isdirsep (path1[len1 - 1]))
166     len1--;
167
168   if (len1 == 0)
169     return isdirsep (path2[0]) && !isdirsep (path2[1]);
170
171   if (isdirsep (path2[len1]) || path2[len1] == 0 || path1[len1 - 1] == ':')
172     return caseinsensitive ? strncasematch (path1, path2, len1)
173                            : !strncmp (path1, path2, len1);
174
175   return 0;
176 }
177
178 /* Return non-zero if paths match in first len chars.
179    Check is dependent of the case sensitivity setting. */
180 int
181 pathnmatch (const char *path1, const char *path2, int len, bool caseinsensitive)
182 {
183   return caseinsensitive
184          ? strncasematch (path1, path2, len) : !strncmp (path1, path2, len);
185 }
186
187 /* Return non-zero if paths match. Check is dependent of the case
188    sensitivity setting. */
189 int
190 pathmatch (const char *path1, const char *path2, bool caseinsensitive)
191 {
192   return caseinsensitive
193          ? strcasematch (path1, path2) : !strcmp (path1, path2);
194 }
195
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.  */
200 bool
201 has_dot_last_component (const char *dir, bool test_dot_dot)
202 {
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, '/');
207   if (!last_comp)
208     last_comp = dir;
209   else {
210     /* Check for trailing slash.  If so, hop back to the previous slash. */
211     if (!last_comp[1])
212       while (last_comp > dir)
213         if (*--last_comp == '/')
214           break;
215     if (*last_comp == '/')
216       ++last_comp;
217   }
218   return last_comp[0] == '.'
219          && ((last_comp[1] == '\0' || last_comp[1] == '/')
220              || (test_dot_dot
221                  && last_comp[1] == '.'
222                  && (last_comp[2] == '\0' || last_comp[2] == '/')));
223 }
224
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.  */
228
229 int
230 normalize_posix_path (const char *src, char *dst, char *&tail)
231 {
232   const char *in_src = src;
233   char *dst_start = dst;
234   syscall_printf ("src %s", src);
235
236   if ((isdrive (src) && isdirsep (src[2])) || *src == '\\')
237     goto win32_path;
238
239   tail = dst;
240   if (!isslash (src[0]))
241     {
242       if (!cygheap->cwd.get (dst))
243         return get_errno ();
244       tail = strchr (tail, '\0');
245       if (isslash (dst[0]) && isslash (dst[1]))
246         ++dst_start;
247       if (*src == '.')
248         {
249           if (tail == dst_start + 1 && *dst_start == '/')
250              tail--;
251           goto sawdot;
252         }
253       if (tail > dst && !isslash (tail[-1]))
254         *tail++ = '/';
255     }
256   /* Two leading /'s?  If so, preserve them.  */
257   else if (isslash (src[1]) && !isslash (src[2]))
258     {
259       *tail++ = *src++;
260       ++dst_start;
261     }
262
263   while (*src)
264     {
265       if (*src == '\\')
266         goto win32_path;
267       /* Strip runs of /'s.  */
268       if (!isslash (*src))
269         *tail++ = *src++;
270       else
271         {
272           while (*++src)
273             {
274               if (isslash (*src))
275                 continue;
276
277               if (*src != '.')
278                 break;
279
280             sawdot:
281               if (src[1] != '.')
282                 {
283                   if (!src[1])
284                     {
285                       *tail++ = '/';
286                       goto done;
287                     }
288                   if (!isslash (src[1]))
289                     break;
290                 }
291               else if (src[2] && !isslash (src[2]))
292                 break;
293               else
294                 {
295                   while (tail > dst_start && !isslash (*--tail))
296                     continue;
297                   src++;
298                 }
299             }
300
301           *tail++ = '/';
302         }
303         if ((tail - dst) >= NT_MAX_PATH)
304           {
305             debug_printf ("ENAMETOOLONG = normalize_posix_path (%s)", src);
306             return ENAMETOOLONG;
307           }
308     }
309
310 done:
311   *tail = '\0';
312
313   debug_printf ("%s = normalize_posix_path (%s)", dst, in_src);
314   return 0;
315
316 win32_path:
317   int err = normalize_win32_path (in_src, dst, tail);
318   if (!err)
319     for (char *p = dst; (p = strchr (p, '\\')); p++)
320       *p = '/';
321   return err ?: -1;
322 }
323
324 inline void
325 path_conv::add_ext_from_sym (symlink_info &sym)
326 {
327   if (sym.ext_here && *sym.ext_here)
328     {
329       known_suffix = path + sym.extn;
330       if (sym.ext_tacked_on)
331         strcpy ((char *) known_suffix, sym.ext_here);
332     }
333 }
334
335 static void __stdcall mkrelpath (char *dst, bool caseinsensitive)
336   __attribute__ ((regparm (2)));
337
338 static void __stdcall
339 mkrelpath (char *path, bool caseinsensitive)
340 {
341   tmp_pathbuf tp;
342   char *cwd_win32 = tp.c_get ();
343   if (!cygheap->cwd.get (cwd_win32, 0))
344     return;
345
346   unsigned cwdlen = strlen (cwd_win32);
347   if (!path_prefix_p (cwd_win32, path, cwdlen, caseinsensitive))
348     return;
349
350   size_t n = strlen (path);
351   if (n < cwdlen)
352     return;
353
354   char *tail = path;
355   if (n == cwdlen)
356     tail += cwdlen;
357   else
358     tail += isdirsep (cwd_win32[cwdlen - 1]) ? cwdlen : cwdlen + 1;
359
360   memmove (path, tail, strlen (tail) + 1);
361   if (!*path)
362     strcpy (path, ".");
363 }
364
365 void
366 path_conv::fillin (HANDLE h)
367 {
368   IO_STATUS_BLOCK io;
369   FILE_BASIC_INFORMATION fbi;
370
371   if (NT_SUCCESS (NtQueryInformationFile (h, &io, &fbi, sizeof fbi,
372                                           FileBasicInformation)))
373     fileattr = fbi.FileAttributes;
374   else
375     fileattr = INVALID_FILE_ATTRIBUTES;
376 }
377
378 void
379 path_conv::set_normalized_path (const char *path_copy)
380 {
381   if (path_copy)
382     {
383       size_t n = strlen (path_copy) + 1;
384       char *p = (char *) cmalloc_abort (HEAP_STR, n);
385       normalized_path = (const char *) memcpy (p, path_copy, n);
386     }
387 }
388
389 WCHAR tfx_chars[] NO_COPY = {
390    0,   1,   2,   3,   4,   5,   6,   7,
391    8,   9,  10,  11,  12,  13,  14,  15,
392   16,  17,  18,  19,  20,  21,  22,  23,
393   24,  25,  26,  27,  28,  29,  30,  31,
394   32, '!', 0xf000 | '"', '#', '$', '%', '&',  39,
395   '(', ')', 0xf000 | '*', '+', ',', '-', '.', '\\',
396  '0', '1', '2', '3', '4', '5', '6', '7',
397  '8', '9', 0xf000 | ':', ';', 0xf000 | '<', '=', 0xf000 | '>', 0xf000 | '?',
398  '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
399  'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
400  'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
401  'X', 'Y', 'Z', '[',  '\\', ']', '^', '_',
402  '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
403  'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
404  'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
405  'x', 'y', 'z', '{', 0xf000 | '|', '}', '~', 127
406 };
407
408 void
409 transform_chars (PWCHAR path, PWCHAR path_end)
410 {
411   for (; path <= path_end; ++path)
412     if (*path < 128)
413       *path = tfx_chars[*path];
414 }
415
416 static inline
417 void
418 transform_chars (PUNICODE_STRING upath, USHORT start_idx)
419 {
420   transform_chars (upath->Buffer + start_idx,
421                    upath->Buffer + upath->Length / sizeof (WCHAR) - 1);
422 }
423
424 static inline void
425 str2uni_cat (UNICODE_STRING &tgt, const char *srcstr)
426 {
427   int len = sys_mbstowcs (tgt.Buffer + tgt.Length / sizeof (WCHAR),
428                           (tgt.MaximumLength - tgt.Length) / sizeof (WCHAR),
429                           srcstr);
430   if (len)
431     tgt.Length += (len - 1) * sizeof (WCHAR);
432 }
433
434 PUNICODE_STRING
435 get_nt_native_path (const char *path, UNICODE_STRING& upath)
436 {
437   upath.Length = 0;
438   if (path[0] == '/')           /* special path w/o NT path representation. */
439     str2uni_cat (upath, path);
440   else if (path[0] != '\\')     /* X:\...  or relative path. */
441     {
442       if (path[1] == ':')       /* X:\... */
443         {
444           RtlAppendUnicodeStringToString (&upath, &ro_u_natp);
445           str2uni_cat (upath, path);
446           /* The drive letter must be upper case. */
447           upath.Buffer[4] = towupper (upath.Buffer[4]);
448         }
449       else
450         str2uni_cat (upath, path);
451       transform_chars (&upath, 7);
452     }
453   else if (path[1] != '\\')     /* \Device\... */
454     str2uni_cat (upath, path);
455   else if ((path[2] != '.' && path[2] != '?')
456            || path[3] != '\\')  /* \\server\share\... */
457     {
458       RtlAppendUnicodeStringToString (&upath, &ro_u_uncp);
459       str2uni_cat (upath, path + 2);
460       transform_chars (&upath, 8);
461     }
462   else                          /* \\.\device or \\?\foo */
463     {
464       RtlAppendUnicodeStringToString (&upath, &ro_u_natp);
465       str2uni_cat (upath, path + 4);
466     }
467   return &upath;
468 }
469
470 PUNICODE_STRING
471 path_conv::get_nt_native_path ()
472 {
473   if (!wide_path)
474     {
475       uni_path.Length = 0;
476       uni_path.MaximumLength = (strlen (path) + 10) * sizeof (WCHAR);
477       wide_path = (PWCHAR) cmalloc_abort (HEAP_STR, uni_path.MaximumLength);
478       uni_path.Buffer = wide_path;
479       ::get_nt_native_path (path, uni_path);
480     }
481   return &uni_path;
482 }
483
484 POBJECT_ATTRIBUTES
485 path_conv::get_object_attr (OBJECT_ATTRIBUTES &attr, SECURITY_ATTRIBUTES &sa)
486 {
487   if (!get_nt_native_path ())
488     return NULL;
489   InitializeObjectAttributes (&attr, &uni_path,
490                               objcaseinsensitive ()
491                               | (sa.bInheritHandle ? OBJ_INHERIT : 0),
492                               NULL, sa.lpSecurityDescriptor);
493   return &attr;
494 }
495
496 PWCHAR
497 path_conv::get_wide_win32_path (PWCHAR wc)
498 {
499   get_nt_native_path ();
500   if (!wide_path)
501     return NULL;
502   wcpcpy (wc, wide_path);
503   if (wc[1] == L'?')
504     wc[1] = L'\\';
505   return wc;
506 }
507
508 void
509 warn_msdos (const char *src)
510 {
511   if (user_shared->warned_msdos || !dos_file_warning || !cygwin_finished_initializing)
512     return;
513   tmp_pathbuf tp;
514   char *posix_path = tp.c_get ();
515   small_printf ("cygwin warning:\n");
516   if (cygwin_conv_path (CCP_WIN_A_TO_POSIX | CCP_RELATIVE, src,
517                         posix_path, NT_MAX_PATH))
518     small_printf ("  MS-DOS style path detected: %s\n  POSIX equivalent preferred.\n",
519                   src);
520   else
521     small_printf ("  MS-DOS style path detected: %s\n  Preferred POSIX equivalent is: %s\n",
522                   src, posix_path);
523   small_printf ("  CYGWIN environment variable option \"nodosfilewarning\" turns off this warning.\n"
524                 "  Consult the user's guide for more details about POSIX paths:\n"
525                 "    http://cygwin.com/cygwin-ug-net/using.html#using-pathnames\n");
526   user_shared->warned_msdos = true;
527 }
528
529 static DWORD
530 getfileattr (const char *path, bool caseinsensitive) /* path has to be always absolute. */
531 {
532   tmp_pathbuf tp;
533   UNICODE_STRING upath;
534   OBJECT_ATTRIBUTES attr;
535   FILE_BASIC_INFORMATION fbi;
536   NTSTATUS status;
537   IO_STATUS_BLOCK io;
538
539   tp.u_get (&upath);
540   InitializeObjectAttributes (&attr, &upath,
541                               caseinsensitive ? OBJ_CASE_INSENSITIVE : 0,
542                               NULL, NULL);
543   get_nt_native_path (path, upath);
544
545   status = NtQueryAttributesFile (&attr, &fbi);
546   if (NT_SUCCESS (status))
547     return fbi.FileAttributes;
548
549   if (status != STATUS_OBJECT_NAME_NOT_FOUND
550       && status != STATUS_NO_SUCH_FILE) /* File not found on 9x share */
551     {
552       /* File exists but access denied.  Try to get attribute through
553          directory query. */
554       UNICODE_STRING dirname, basename;
555       HANDLE dir;
556       FILE_DIRECTORY_INFORMATION fdi;
557
558       RtlSplitUnicodePath (&upath, &dirname, &basename);
559       InitializeObjectAttributes (&attr, &dirname,
560                                   caseinsensitive ? OBJ_CASE_INSENSITIVE : 0,
561                                   NULL, NULL);
562       status = NtOpenFile (&dir, SYNCHRONIZE | FILE_LIST_DIRECTORY,
563                            &attr, &io, FILE_SHARE_VALID_FLAGS,
564                            FILE_SYNCHRONOUS_IO_NONALERT
565                            | FILE_OPEN_FOR_BACKUP_INTENT
566                            | FILE_DIRECTORY_FILE);
567       if (NT_SUCCESS (status))
568         {
569           status = NtQueryDirectoryFile (dir, NULL, NULL, 0, &io,
570                                          &fdi, sizeof fdi,
571                                          FileDirectoryInformation,
572                                          TRUE, &basename, TRUE);
573           NtClose (dir);
574           if (NT_SUCCESS (status) || status == STATUS_BUFFER_OVERFLOW)
575             return fdi.FileAttributes;
576         }
577     }
578   SetLastError (RtlNtStatusToDosError (status));
579   return INVALID_FILE_ATTRIBUTES;
580 }
581
582 /* Convert an arbitrary path SRC to a pure Win32 path, suitable for
583    passing to Win32 API routines.
584
585    If an error occurs, `error' is set to the errno value.
586    Otherwise it is set to 0.
587
588    follow_mode values:
589         SYMLINK_FOLLOW      - convert to PATH symlink points to
590         SYMLINK_NOFOLLOW    - convert to PATH of symlink itself
591         SYMLINK_IGNORE      - do not check PATH for symlinks
592         SYMLINK_CONTENTS    - just return symlink contents
593 */
594
595 /* TODO: This implementation is only preliminary.  For internal
596    purposes it's necessary to have a path_conv::check function which
597    takes a UNICODE_STRING src path, otherwise we waste a lot of time
598    for converting back and forth.  The below implementation does
599    realy nothing but converting to char *, until path_conv handles
600    wide-char paths directly. */
601 void
602 path_conv::check (const UNICODE_STRING *src, unsigned opt,
603                   const suffix_info *suffixes)
604 {
605   tmp_pathbuf tp;
606   char *path = tp.c_get ();
607
608   user_shared->warned_msdos = true;
609   sys_wcstombs (path, NT_MAX_PATH, src->Buffer, src->Length / sizeof (WCHAR));
610   path_conv::check (path, opt, suffixes);
611 }
612
613 void
614 path_conv::check (const char *src, unsigned opt,
615                   const suffix_info *suffixes)
616 {
617   /* The tmp_buf array is used when expanding symlinks.  It is NT_MAX_PATH * 2
618      in length so that we can hold the expanded symlink plus a trailer.  */
619   tmp_pathbuf tp;
620   char *path_copy = tp.c_get ();
621   char *pathbuf = tp.c_get ();
622   char *tmp_buf = tp.t_get ();
623   char *THIS_path = tp.c_get ();
624   symlink_info sym;
625   bool need_directory = 0;
626   bool saw_symlinks = 0;
627   bool add_ext = false;
628   bool is_relpath;
629   char *tail, *path_end;
630
631 #if 0
632   static path_conv last_path_conv;
633   static char last_src[CYG_MAX_PATH];
634
635   if (*last_src && strcmp (last_src, src) == 0)
636     {
637       *this = last_path_conv;
638       return;
639     }
640 #endif
641
642   myfault efault;
643   if (efault.faulted ())
644     {
645       error = EFAULT;
646       return;
647     }
648   int loop = 0;
649   path_flags = 0;
650   known_suffix = NULL;
651   fileattr = INVALID_FILE_ATTRIBUTES;
652   caseinsensitive = OBJ_CASE_INSENSITIVE;
653   if (wide_path)
654     cfree (wide_path);
655   wide_path = NULL;
656   if (path)
657     {
658       cfree (modifiable_path ());
659       path = NULL;
660     }
661   memset (&dev, 0, sizeof (dev));
662   fs.clear ();
663   if (normalized_path)
664     {
665       cfree ((void *) normalized_path);
666       normalized_path = NULL;
667     }
668   int component = 0;            // Number of translated components
669
670   if (!(opt & PC_NULLEMPTY))
671     error = 0;
672   else if (!*src)
673     {
674       error = ENOENT;
675       return;
676     }
677
678   bool is_msdos = false;
679   /* This loop handles symlink expansion.  */
680   for (;;)
681     {
682       MALLOC_CHECK;
683       assert (src);
684
685       is_relpath = !isabspath (src);
686       error = normalize_posix_path (src, path_copy, tail);
687       if (error > 0)
688         return;
689       if (error < 0)
690         {
691           if (component == 0)
692             is_msdos = true;
693           error = 0;
694         }
695
696       /* Detect if the user was looking for a directory.  We have to strip the
697          trailing slash initially while trying to add extensions but take it
698          into account during processing */
699       if (tail > path_copy + 2 && isslash (tail[-1]))
700         {
701           need_directory = 1;
702           *--tail = '\0';
703         }
704       path_end = tail;
705
706       /* Scan path_copy from right to left looking either for a symlink
707          or an actual existing file.  If an existing file is found, just
708          return.  If a symlink is found, exit the for loop.
709          Also: be careful to preserve the errno returned from
710          symlink.check as the caller may need it. */
711       /* FIXME: Do we have to worry about multiple \'s here? */
712       component = 0;            // Number of translated components
713       sym.contents[0] = '\0';
714
715       int symlen = 0;
716
717       for (unsigned pflags_or = opt & PC_NO_ACCESS_CHECK; ; pflags_or = 0)
718         {
719           const suffix_info *suff;
720           char *full_path;
721
722           /* Don't allow symlink.check to set anything in the path_conv
723              class if we're working on an inner component of the path */
724           if (component)
725             {
726               suff = NULL;
727               sym.pflags = 0;
728               full_path = pathbuf;
729             }
730           else
731             {
732               suff = suffixes;
733               sym.pflags = path_flags;
734               full_path = THIS_path;
735             }
736
737           /* Convert to native path spec sans symbolic link info. */
738           error = mount_table->conv_to_win32_path (path_copy, full_path, dev,
739                                                    &sym.pflags);
740
741           if (error)
742             return;
743
744           sym.pflags |= pflags_or;
745
746           if (dev.major == DEV_CYGDRIVE_MAJOR)
747             {
748               if (!component)
749                 fileattr = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_READONLY;
750               else
751                 {
752                   fileattr = getfileattr (THIS_path,
753                                           sym.pflags & MOUNT_NOPOSIX);
754                   dev.devn = FH_FS;
755                 }
756               goto out;
757             }
758           else if (dev == FH_DEV)
759             {
760               dev.devn = FH_FS;
761 #if 0
762               fileattr = getfileattr (THIS_path, sym.pflags & MOUNT_NOPOSIX);
763               if (!component && fileattr == INVALID_FILE_ATTRIBUTES)
764                 {
765                   fileattr = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_READONLY;
766                   goto out;
767                 }
768 #endif
769             }
770           else if (isvirtual_dev (dev.devn))
771             {
772               /* FIXME: Calling build_fhandler here is not the right way to handle this. */
773               fhandler_virtual *fh = (fhandler_virtual *) build_fh_dev (dev, path_copy);
774               int file_type = fh->exists ();
775               if (file_type == -2)
776                 {
777                   fh->fill_filebuf ();
778                   symlen = sym.set (fh->get_filebuf ());
779                 }
780               delete fh;
781               switch (file_type)
782                 {
783                   case 1:
784                   case 2:
785                     if (component == 0)
786                       fileattr = FILE_ATTRIBUTE_DIRECTORY;
787                     break;
788                   case -1:
789                     if (component == 0)
790                       fileattr = 0;
791                     break;
792                   case -2:      /* /proc/self or /proc/<pid>/symlinks */
793                     goto is_virtual_symlink;
794                   case -3:      /* /proc/<pid>/fd/pipe:[] */
795                     if (component == 0)
796                       {
797                         fileattr = 0;
798                         dev.parse (FH_PIPE);
799                       }
800                     break;
801                   case -4:      /* /proc/<pid>/fd/socket:[] */
802                     if (component == 0)
803                       {
804                         fileattr = 0;
805                         dev.parse (FH_TCP);
806                       }
807                     break;
808                   default:
809                     if (component == 0)
810                       fileattr = INVALID_FILE_ATTRIBUTES;
811                     goto virtual_component_retry;
812                 }
813               if (component == 0 || dev.devn != FH_NETDRIVE)
814                 path_flags |= PATH_RO;
815               goto out;
816             }
817           /* devn should not be a device.  If it is, then stop parsing now. */
818           else if (dev.devn != FH_FS)
819             {
820               fileattr = 0;
821               path_flags = sym.pflags;
822               if (component)
823                 {
824                   error = ENOTDIR;
825                   return;
826                 }
827               goto out;         /* Found a device.  Stop parsing. */
828             }
829
830           /* If path is only a drivename, Windows interprets it as the
831              current working directory on this drive instead of the root
832              dir which is what we want. So we need the trailing backslash
833              in this case. */
834           if (full_path[0] && full_path[1] == ':' && full_path[2] == '\0')
835             {
836               full_path[2] = '\\';
837               full_path[3] = '\0';
838             }
839
840           symlen = sym.check (full_path, suff, opt, fs);
841
842 is_virtual_symlink:
843
844           if (sym.isdevice)
845             {
846               dev.parse (sym.major, sym.minor);
847               dev.setfs (1);
848               dev.mode = sym.mode;
849               fileattr = sym.fileattr;
850               goto out;
851             }
852
853           if (sym.pflags & PATH_SOCKET)
854             {
855               if (component)
856                 {
857                   error = ENOTDIR;
858                   return;
859                 }
860               fileattr = sym.fileattr;
861               dev.parse (FH_UNIX);
862               dev.setfs (1);
863               goto out;
864             }
865
866           if (!component)
867             {
868               fileattr = sym.fileattr;
869               path_flags = sym.pflags;
870               /* If the OS is caseinsensitive or the FS is caseinsensitive or
871                  the incoming path was given in DOS notation, don't handle
872                  path casesensitive. */
873               if (cygwin_shared->obcaseinsensitive || fs.caseinsensitive ()
874                   || is_msdos)
875                 path_flags |= PATH_NOPOSIX;
876               caseinsensitive = (path_flags & PATH_NOPOSIX)
877                                 ? OBJ_CASE_INSENSITIVE : 0;
878             }
879
880           /* If symlink.check found an existing non-symlink file, then
881              it sets the appropriate flag.  It also sets any suffix found
882              into `ext_here'. */
883           if (!sym.issymlink && sym.fileattr != INVALID_FILE_ATTRIBUTES)
884             {
885               error = sym.error;
886               if (component == 0)
887                 add_ext = true;
888               else if (!(sym.fileattr & FILE_ATTRIBUTE_DIRECTORY))
889                 {
890                   error = ENOTDIR;
891                   goto out;
892                 }
893               goto out; // file found
894             }
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. */
900           else if (symlen > 0)
901             {
902               saw_symlinks = 1;
903               if (component == 0 && !need_directory && !(opt & PC_SYM_FOLLOW))
904                 {
905                   set_symlink (symlen); // last component of path is a symlink.
906                   if (opt & PC_SYM_CONTENTS)
907                     {
908                       strcpy (THIS_path, sym.contents);
909                       goto out;
910                     }
911                   add_ext = true;
912                   goto out;
913                 }
914               else
915                 break;
916             }
917           else if (sym.error && sym.error != ENOENT)
918             {
919               error = sym.error;
920               goto out;
921             }
922           /* No existing file found. */
923
924 virtual_component_retry:
925           /* Find the new "tail" of the path, e.g. in '/for/bar/baz',
926              /baz is the tail. */
927           if (tail != path_end)
928             *tail = '/';
929           while (--tail > path_copy + 1 && *tail != '/') {}
930           /* Exit loop if there is no tail or we are at the
931              beginning of a UNC path */
932           if (tail <= path_copy + 1)
933             goto out;   // all done
934
935           /* Haven't found an existing pathname component yet.
936              Pinch off the tail and try again. */
937           *tail = '\0';
938           component++;
939         }
940
941       /* Arrive here if above loop detected a symlink. */
942       if (++loop > SYMLOOP_MAX)
943         {
944           error = ELOOP;   // Eep.
945           return;
946         }
947
948       MALLOC_CHECK;
949
950
951       /* Place the link content, possibly with head and/or tail, in tmp_buf */
952
953       char *headptr;
954       if (isabspath (sym.contents))
955         headptr = tmp_buf;      /* absolute path */
956       else
957         {
958           /* Copy the first part of the path (with ending /) and point to the end. */
959           char *prevtail = tail;
960           while (--prevtail > path_copy  && *prevtail != '/') {}
961           int headlen = prevtail - path_copy + 1;;
962           memcpy (tmp_buf, path_copy, headlen);
963           headptr = &tmp_buf[headlen];
964         }
965
966       /* Make sure there is enough space */
967       if (headptr + symlen >= tmp_buf + (2 * NT_MAX_PATH))
968         {
969         too_long:
970           error = ENAMETOOLONG;
971           set_path ("::ENAMETOOLONG::");
972           return;
973         }
974
975      /* Copy the symlink contents to the end of tmp_buf.
976         Convert slashes. */
977       for (char *p = sym.contents; *p; p++)
978         *headptr++ = *p == '\\' ? '/' : *p;
979       *headptr = '\0';
980
981       /* Copy any tail component (with the 0) */
982       if (tail++ < path_end)
983         {
984           /* Add a slash if needed. There is space. */
985           if (*(headptr - 1) != '/')
986             *headptr++ = '/';
987           int taillen = path_end - tail + 1;
988           if (headptr + taillen > tmp_buf + (2 * NT_MAX_PATH))
989             goto too_long;
990           memcpy (headptr, tail, taillen);
991         }
992
993       /* Evaluate everything all over again. */
994       src = tmp_buf;
995     }
996
997   if (!(opt & PC_SYM_CONTENTS))
998     add_ext = true;
999
1000 out:
1001   set_path (THIS_path);
1002   if (add_ext)
1003     add_ext_from_sym (sym);
1004   if (dev.devn == FH_NETDRIVE && component)
1005     {
1006       /* This case indicates a non-existant resp. a non-retrievable
1007          share.  This happens for instance if the share is a printer.
1008          In this case the path must not be treated like a FH_NETDRIVE,
1009          but like a FH_FS instead, so the usual open call for files
1010          is used on it. */
1011       dev.parse (FH_FS);
1012     }
1013   else if (isvirtual_dev (dev.devn) && fileattr == INVALID_FILE_ATTRIBUTES)
1014     {
1015       error = ENOENT;
1016       return;
1017     }
1018   else if (!need_directory || error)
1019     /* nothing to do */;
1020   else if (fileattr == INVALID_FILE_ATTRIBUTES)
1021     strcat (modifiable_path (), "\\"); /* Reattach trailing dirsep in native path. */
1022   else if (fileattr & FILE_ATTRIBUTE_DIRECTORY)
1023     path_flags &= ~PATH_SYMLINK;
1024   else
1025     {
1026       debug_printf ("%s is a non-directory", path);
1027       error = ENOTDIR;
1028       return;
1029     }
1030
1031   if (dev.isfs ())
1032     {
1033       if (strncmp (path, "\\\\.\\", 4))
1034         {
1035           if (!tail || tail == path)
1036             /* nothing */;
1037           else if (tail[-1] != '\\')
1038             *tail = '\0';
1039           else
1040             {
1041               error = ENOENT;
1042               return;
1043             }
1044         }
1045
1046       /* FS has been checked already for existing files. */
1047       if (exists () || fs.update (get_nt_native_path (), NULL))
1048         {
1049           /* Incoming DOS paths are treated like DOS paths in native
1050              Windows applications.  No ACLs, just default settings. */
1051           if (is_msdos)
1052             fs.has_acls (false);
1053           debug_printf ("this->path(%s), has_acls(%d)", path, fs.has_acls ());
1054           /* CV: We could use this->has_acls() but I want to make sure that
1055              we don't forget that the PATH_NOACL flag must be taken into
1056              account here. */
1057           if (!(path_flags & PATH_NOACL) && fs.has_acls ())
1058             set_exec (0);  /* We really don't know if this is executable or not here
1059                               but set it to not executable since it will be figured out
1060                               later by anything which cares about this. */
1061         }
1062       if (exec_state () != dont_know_if_executable)
1063         /* ok */;
1064       else if (isdir ())
1065         set_exec (1);
1066       else if (issymlink () || issocket ())
1067         set_exec (0);
1068     }
1069
1070   if (opt & PC_NOFULL)
1071     {
1072       if (is_relpath)
1073         {
1074           mkrelpath (this->modifiable_path (), !!caseinsensitive);
1075           /* Invalidate wide_path so that wide relpath can be created
1076              in later calls to get_nt_native_path or get_wide_win32_path. */
1077           if (wide_path)
1078             cfree (wide_path);
1079           wide_path = NULL;
1080         }
1081       if (need_directory)
1082         {
1083           size_t n = strlen (this->path);
1084           /* Do not add trailing \ to UNC device names like \\.\a: */
1085           if (this->path[n - 1] != '\\' &&
1086               (strncmp (this->path, "\\\\.\\", 4) != 0))
1087             {
1088               this->modifiable_path ()[n] = '\\';
1089               this->modifiable_path ()[n + 1] = '\0';
1090             }
1091         }
1092     }
1093
1094   if (saw_symlinks)
1095     set_has_symlinks ();
1096
1097   if ((opt & PC_POSIX))
1098     {
1099       if (tail < path_end && tail > path_copy + 1)
1100         *tail = '/';
1101       set_normalized_path (path_copy);
1102       if (is_msdos && !(opt & PC_NOWARN))
1103         warn_msdos (src);
1104     }
1105
1106 #if 0
1107   if (!error)
1108     {
1109       last_path_conv = *this;
1110       strcpy (last_src, src);
1111     }
1112 #endif
1113 }
1114
1115 path_conv::~path_conv ()
1116 {
1117   if (normalized_path)
1118     {
1119       cfree ((void *) normalized_path);
1120       normalized_path = NULL;
1121     }
1122   if (path)
1123     {
1124       cfree (modifiable_path ());
1125       path = NULL;
1126     }
1127   if (wide_path)
1128     {
1129       cfree (wide_path);
1130       wide_path = NULL;
1131     }
1132 }
1133
1134 bool
1135 path_conv::is_binary ()
1136 {
1137   tmp_pathbuf tp;
1138   PWCHAR bintest = tp.w_get ();
1139   DWORD bin;
1140
1141   return GetBinaryTypeW (get_wide_win32_path (bintest), &bin)
1142          && (bin == SCS_32BIT_BINARY || bin == SCS_64BIT_BINARY);
1143 }
1144
1145 /* Normalize a Win32 path.
1146    /'s are converted to \'s in the process.
1147    All duplicate \'s, except for 2 leading \'s, are deleted.
1148
1149    The result is 0 for success, or an errno error value.
1150    FIXME: A lot of this should be mergeable with the POSIX critter.  */
1151 int
1152 normalize_win32_path (const char *src, char *dst, char *&tail)
1153 {
1154   const char *src_start = src;
1155   bool beg_src_slash = isdirsep (src[0]);
1156
1157   tail = dst;
1158   /* Skip long path name prefixes in Win32 or NT syntax. */
1159   if (beg_src_slash && (src[1] == '?' || isdirsep (src[1]))
1160       && src[2] == '?' && isdirsep (src[3]))
1161     {
1162       src += 4;
1163       if (src[1] != ':') /* native UNC path */
1164         src += 2; /* Fortunately the first char is not copied... */
1165       else
1166         beg_src_slash = false;
1167     }
1168   if (beg_src_slash && isdirsep (src[1]))
1169     {
1170       if (isdirsep (src[2]))
1171         {
1172           /* More than two slashes are just folded into one. */
1173           src += 2;
1174           while (isdirsep (src[1]))
1175             ++src;
1176         }
1177       else
1178         {
1179           /* Two slashes start a network or device path. */
1180           *tail++ = '\\';
1181           src++;
1182           if (src[1] == '.' && isdirsep (src[2]))
1183             {
1184               *tail++ = '\\';
1185               *tail++ = '.';
1186               src += 2;
1187             }
1188         }
1189     }
1190   if (tail == dst)
1191     {
1192       if (isdrive (src))
1193         /* Always convert drive letter to uppercase for case sensitivity. */
1194         *tail++ = cyg_toupper (*src++);
1195       else if (*src != '/')
1196         {
1197           if (beg_src_slash)
1198             tail += cygheap->cwd.get_drive (dst);
1199           else if (!cygheap->cwd.get (dst, 0))
1200             return get_errno ();
1201           else
1202             {
1203               tail = strchr (tail, '\0');
1204               if (tail[-1] != '\\')
1205                 *tail++ = '\\';
1206             }
1207         }
1208     }
1209
1210   while (*src)
1211     {
1212       /* Strip duplicate /'s.  */
1213       if (isdirsep (src[0]) && isdirsep (src[1]))
1214         src++;
1215       /* Ignore "./".  */
1216       else if (src[0] == '.' && isdirsep (src[1])
1217                && (src == src_start || isdirsep (src[-1])))
1218         src += 2;
1219
1220       /* Backup if "..".  */
1221       else if (src[0] == '.' && src[1] == '.'
1222                /* dst must be greater than dst_start */
1223                && tail[-1] == '\\')
1224         {
1225           if (!isdirsep (src[2]) && src[2] != '\0')
1226               *tail++ = *src++;
1227           else
1228             {
1229               /* Back up over /, but not if it's the first one.  */
1230               if (tail > dst + 1)
1231                 tail--;
1232               /* Now back up to the next /.  */
1233               while (tail > dst + 1 && tail[-1] != '\\' && tail[-2] != ':')
1234                 tail--;
1235               src += 2;
1236               if (isdirsep (*src))
1237                 src++;
1238             }
1239         }
1240       /* Otherwise, add char to result.  */
1241       else
1242         {
1243           if (*src == '/')
1244             *tail++ = '\\';
1245           else
1246             *tail++ = *src;
1247           src++;
1248         }
1249       if ((tail - dst) >= NT_MAX_PATH)
1250         return ENAMETOOLONG;
1251     }
1252    if (tail > dst + 1 && tail[-1] == '.' && tail[-2] == '\\')
1253      tail--;
1254   *tail = '\0';
1255   debug_printf ("%s = normalize_win32_path (%s)", dst, src_start);
1256   return 0;
1257 }
1258
1259 /* Various utilities.  */
1260
1261 /* nofinalslash: Remove trailing / and \ from SRC (except for the
1262    first one).  It is ok for src == dst.  */
1263
1264 void __stdcall
1265 nofinalslash (const char *src, char *dst)
1266 {
1267   int len = strlen (src);
1268   if (src != dst)
1269     memcpy (dst, src, len + 1);
1270   while (len > 1 && isdirsep (dst[--len]))
1271     dst[len] = '\0';
1272 }
1273
1274 /* conv_path_list: Convert a list of path names to/from Win32/POSIX. */
1275
1276 static int
1277 conv_path_list (const char *src, char *dst, size_t size, int to_posix)
1278 {
1279   tmp_pathbuf tp;
1280   char src_delim, dst_delim;
1281   cygwin_conv_path_t conv_fn;
1282   size_t len;
1283
1284   if (to_posix)
1285     {
1286       src_delim = ';';
1287       dst_delim = ':';
1288       conv_fn = CCP_WIN_A_TO_POSIX | CCP_RELATIVE;
1289     }
1290   else
1291     {
1292       src_delim = ':';
1293       dst_delim = ';';
1294       conv_fn = CCP_POSIX_TO_WIN_A | CCP_RELATIVE;
1295     }
1296
1297   char *srcbuf;
1298   len = strlen (src) + 1;
1299   if (len <= NT_MAX_PATH * sizeof (WCHAR))
1300     srcbuf = (char *) tp.w_get ();
1301   else
1302     srcbuf = (char *) alloca (len);
1303
1304   int err = 0;
1305   char *d = dst - 1;
1306   bool saw_empty = false;
1307   do
1308     {
1309       char *s = strccpy (srcbuf, &src, src_delim);
1310       size_t len = s - srcbuf;
1311       if (len >= NT_MAX_PATH)
1312         {
1313           err = ENAMETOOLONG;
1314           break;
1315         }
1316       if (len)
1317         {
1318           ++d;
1319           err = cygwin_conv_path (conv_fn, srcbuf, d, size - (d - dst));
1320         }
1321       else if (!to_posix)
1322         {
1323           ++d;
1324           err = cygwin_conv_path (conv_fn, ".", d, size - (d - dst));
1325         }
1326       else
1327         {
1328           if (to_posix == ENV_CVT)
1329             saw_empty = true;
1330           continue;
1331         }
1332       if (err)
1333         break;
1334       d = strchr (d, '\0');
1335       *d = dst_delim;
1336     }
1337   while (*src++);
1338
1339   if (saw_empty)
1340     err = EIDRM;
1341
1342   if (d < dst)
1343     d++;
1344   *d = '\0';
1345   return err;
1346 }
1347
1348 /********************** Symbolic Link Support **************************/
1349
1350 /* Create a symlink from FROMPATH to TOPATH. */
1351
1352 /* If TRUE create symlinks as Windows shortcuts, if false create symlinks
1353    as normal files with magic number and system bit set. */
1354 bool allow_winsymlinks = false;
1355
1356 extern "C" int
1357 symlink (const char *oldpath, const char *newpath)
1358 {
1359   return symlink_worker (oldpath, newpath, allow_winsymlinks, false);
1360 }
1361
1362 int
1363 symlink_worker (const char *oldpath, const char *newpath, bool use_winsym,
1364                 bool isdevice)
1365 {
1366   int res = -1;
1367   size_t len;
1368   path_conv win32_newpath, win32_oldpath;
1369   char *buf, *cp;
1370   SECURITY_ATTRIBUTES sa = sec_none_nih;
1371   security_descriptor sd;
1372   OBJECT_ATTRIBUTES attr;
1373   IO_STATUS_BLOCK io;
1374   NTSTATUS status;
1375   HANDLE fh;
1376   tmp_pathbuf tp;
1377   unsigned check_opt;
1378   bool mk_winsym = use_winsym;
1379
1380   /* POSIX says that empty 'newpath' is invalid input while empty
1381      'oldpath' is valid -- it's symlink resolver job to verify if
1382      symlink contents point to existing filesystem object */
1383   myfault efault;
1384   if (efault.faulted (EFAULT))
1385     goto done;
1386   if (!*oldpath || !*newpath)
1387     {
1388       set_errno (ENOENT);
1389       goto done;
1390     }
1391
1392   if (strlen (oldpath) > SYMLINK_MAX)
1393     {
1394       set_errno (ENAMETOOLONG);
1395       goto done;
1396     }
1397
1398   len = strlen (newpath);
1399   /* Trailing dirsep is a no-no. */
1400   if (isdirsep (newpath[len - 1]))
1401     {
1402       set_errno (ENOENT);
1403       goto done;
1404     }
1405
1406   check_opt = PC_SYM_NOFOLLOW | PC_POSIX | (isdevice ? PC_NOWARN : 0);
1407   /* We need the normalized full path below. */
1408   win32_newpath.check (newpath, check_opt, stat_suffixes);
1409   /* MVFS doesn't handle the SYSTEM DOS attribute, but it handles the R/O
1410      attribute.  Therefore we create symlinks on MVFS always as shortcuts. */
1411   mk_winsym |= win32_newpath.fs_is_mvfs ();
1412
1413   if (mk_winsym && !win32_newpath.exists ()
1414       && (isdevice || !win32_newpath.fs_is_nfs ()))
1415     {
1416       char *newplnk = tp.c_get ();
1417       stpcpy (stpcpy (newplnk, newpath), ".lnk");
1418       win32_newpath.check (newplnk, check_opt);
1419     }
1420
1421   if (win32_newpath.error)
1422     {
1423       set_errno (win32_newpath.error);
1424       goto done;
1425     }
1426
1427   syscall_printf ("symlink (%s, %S)", oldpath,
1428                   win32_newpath.get_nt_native_path ());
1429
1430   if ((!isdevice && win32_newpath.exists ())
1431       || win32_newpath.is_auto_device ())
1432     {
1433       set_errno (EEXIST);
1434       goto done;
1435     }
1436
1437   if (!isdevice && win32_newpath.fs_is_nfs ())
1438     {
1439       /* On NFS, create symlinks by calling NtCreateFile with an EA of type
1440          NfsSymlinkTargetName containing ... the symlink target name. */
1441       PFILE_FULL_EA_INFORMATION pffei = (PFILE_FULL_EA_INFORMATION) tp.w_get ();
1442       pffei->NextEntryOffset = 0;
1443       pffei->Flags = 0;
1444       pffei->EaNameLength = sizeof (NFS_SYML_TARGET) - 1;
1445       char *EaValue = stpcpy (pffei->EaName, NFS_SYML_TARGET) + 1;
1446       pffei->EaValueLength = sizeof (WCHAR) *
1447         (sys_mbstowcs ((PWCHAR) EaValue, NT_MAX_PATH, oldpath) - 1);
1448       status = NtCreateFile (&fh, FILE_WRITE_DATA | FILE_WRITE_EA | SYNCHRONIZE,
1449                              win32_newpath.get_object_attr (attr, sa),
1450                              &io, NULL, FILE_ATTRIBUTE_SYSTEM,
1451                              FILE_SHARE_VALID_FLAGS, FILE_CREATE,
1452                              FILE_SYNCHRONOUS_IO_NONALERT
1453                              | FILE_OPEN_FOR_BACKUP_INTENT,
1454                              pffei, NT_MAX_PATH * sizeof (WCHAR));
1455       if (!NT_SUCCESS (status))
1456         {
1457           __seterrno_from_nt_status (status);
1458           goto done;
1459         }
1460       NtClose (fh);
1461       res = 0;
1462       goto done;
1463     }
1464
1465   if (mk_winsym)
1466     {
1467       ITEMIDLIST *pidl = NULL;
1468       size_t full_len = 0;
1469       unsigned short oldpath_len, desc_len, relpath_len, pidl_len = 0;
1470       char desc[MAX_PATH + 1], *relpath;
1471
1472       if (!isdevice)
1473         {
1474           /* First create an IDLIST to learn how big our shortcut is
1475              going to be. */
1476           IShellFolder *psl;
1477
1478           /* The symlink target is relative to the directory in which
1479              the symlink gets created, not relative to the cwd.  Therefore
1480              we have to mangle the path quite a bit before calling path_conv. */
1481         if (isabspath (oldpath))
1482           win32_oldpath.check (oldpath,
1483                                PC_SYM_NOFOLLOW,
1484                                stat_suffixes);
1485         else
1486             {
1487               len = strrchr (win32_newpath.normalized_path, '/')
1488                     - win32_newpath.normalized_path + 1;
1489               char *absoldpath = tp.t_get ();
1490               stpcpy (stpncpy (absoldpath, win32_newpath.normalized_path, len),
1491                       oldpath);
1492               win32_oldpath.check (absoldpath, PC_SYM_NOFOLLOW, stat_suffixes);
1493             }
1494           if (SUCCEEDED (SHGetDesktopFolder (&psl)))
1495             {
1496               WCHAR wc_path[win32_oldpath.get_wide_win32_path_len () + 1];
1497               win32_oldpath.get_wide_win32_path (wc_path);
1498               /* Amazing but true:  Even though the ParseDisplayName method
1499                  takes a wide char path name, it does not understand the
1500                  Win32 prefix for long pathnames!  So we have to tack off
1501                  the prefix and convert the path to the "normal" syntax
1502                  for ParseDisplayName.  */
1503               WCHAR *wc = wc_path + 4;
1504               if (wc[1] != L':') /* native UNC path */
1505                 *(wc += 2) = L'\\';
1506               HRESULT res;
1507               if (SUCCEEDED (res = psl->ParseDisplayName (NULL, NULL, wc, NULL,
1508                                                     &pidl, NULL)))
1509                 {
1510                   ITEMIDLIST *p;
1511
1512                   for (p = pidl; p->mkid.cb > 0;
1513                        p = (ITEMIDLIST *)((char *) p + p->mkid.cb))
1514                     ;
1515                   pidl_len = (char *) p - (char *) pidl + 2;
1516                 }
1517               psl->Release ();
1518             }
1519         }
1520       /* Compute size of shortcut file. */
1521       full_len = sizeof (win_shortcut_hdr);
1522       if (pidl_len)
1523         full_len += sizeof (unsigned short) + pidl_len;
1524       oldpath_len = strlen (oldpath);
1525       /* Unfortunately the length of the description is restricted to a
1526          length of MAX_PATH up to NT4, and to a length of 2000 bytes
1527          since W2K.  We don't want to add considerations for the different
1528          lengths and even 2000 bytes is not enough for long path names.
1529          So what we do here is to set the description to the POSIX path
1530          only if the path is not longer than MAX_PATH characters.  We
1531          append the full path name after the regular shortcut data
1532          (see below), which works fine with Windows Explorer as well
1533          as older Cygwin versions (as long as the whole file isn't bigger
1534          than 8K).  The description field is only used for backward
1535          compatibility to older Cygwin versions and those versions are
1536          not capable of handling long path names anyway. */
1537       desc_len = stpcpy (desc, oldpath_len > MAX_PATH
1538                                ? "[path too long]" : oldpath) - desc;
1539       full_len += sizeof (unsigned short) + desc_len;
1540       /* Devices get the oldpath string unchanged as relative path. */
1541       if (isdevice)
1542         {
1543           relpath_len = oldpath_len;
1544           stpcpy (relpath = tp.c_get (), oldpath);
1545         }
1546       else
1547         {
1548           relpath_len = strlen (win32_oldpath.get_win32 ());
1549           stpcpy (relpath = tp.c_get (), win32_oldpath.get_win32 ());
1550         }
1551       full_len += sizeof (unsigned short) + relpath_len;
1552       full_len += sizeof (unsigned short) + oldpath_len;
1553       /* 1 byte more for trailing 0 written by stpcpy. */
1554       if (full_len < NT_MAX_PATH * sizeof (WCHAR))
1555         buf = (char *) tp.w_get ();
1556       else
1557         buf = (char *) alloca (full_len + 1);
1558
1559       /* Create shortcut header */
1560       win_shortcut_hdr *shortcut_header = (win_shortcut_hdr *) buf;
1561       memset (shortcut_header, 0, sizeof *shortcut_header);
1562       shortcut_header->size = sizeof *shortcut_header;
1563       shortcut_header->magic = GUID_shortcut;
1564       shortcut_header->flags = (WSH_FLAG_DESC | WSH_FLAG_RELPATH);
1565       if (pidl)
1566         shortcut_header->flags |= WSH_FLAG_IDLIST;
1567       shortcut_header->run = SW_NORMAL;
1568       cp = buf + sizeof (win_shortcut_hdr);
1569
1570       /* Create IDLIST */
1571       if (pidl)
1572         {
1573           *(unsigned short *)cp = pidl_len;
1574           memcpy (cp += 2, pidl, pidl_len);
1575           cp += pidl_len;
1576           CoTaskMemFree (pidl);
1577         }
1578
1579       /* Create description */
1580       *(unsigned short *)cp = desc_len;
1581       cp = stpcpy (cp += 2, desc);
1582
1583       /* Create relpath */
1584       *(unsigned short *)cp = relpath_len;
1585       cp = stpcpy (cp += 2, relpath);
1586
1587       /* Append the POSIX path after the regular shortcut data for
1588          the long path support. */
1589       unsigned short *plen = (unsigned short *) cp;
1590       cp += 2;
1591       *(PWCHAR) cp = 0xfeff;            /* BOM */
1592       cp += 2;
1593       *plen = sys_mbstowcs ((PWCHAR) cp, NT_MAX_PATH, oldpath) * sizeof (WCHAR);
1594       cp += *plen;
1595     }
1596   else
1597     {
1598       /* Default technique creating a symlink. */
1599       buf = (char *) tp.w_get ();
1600       cp = stpcpy (buf, SYMLINK_COOKIE);
1601       *(PWCHAR) cp = 0xfeff;            /* BOM */
1602       cp += 2;
1603       /* Note that the terminating nul is written.  */
1604       cp += sys_mbstowcs ((PWCHAR) cp, NT_MAX_PATH, oldpath) * sizeof (WCHAR);
1605     }
1606
1607   if (isdevice && win32_newpath.exists ())
1608     {
1609       status = NtOpenFile (&fh, FILE_WRITE_ATTRIBUTES,
1610                            win32_newpath.get_object_attr (attr, sa),
1611                            &io, 0, FILE_OPEN_FOR_BACKUP_INTENT);
1612       if (!NT_SUCCESS (status))
1613         {
1614           __seterrno_from_nt_status (status);
1615           goto done;
1616         }
1617       status = NtSetAttributesFile (fh, FILE_ATTRIBUTE_NORMAL);
1618       NtClose (fh);
1619       if (!NT_SUCCESS (status))
1620         {
1621           __seterrno_from_nt_status (status);
1622           goto done;
1623         }
1624     }
1625   /* See comments in fhander_base::open () for an explanation why we defer
1626      setting security attributes on remote files. */
1627   if (win32_newpath.has_acls () && !win32_newpath.isremote ())
1628     set_security_attribute (win32_newpath, S_IFLNK | STD_RBITS | STD_WBITS,
1629                             &sa, sd);
1630   status = NtCreateFile (&fh, DELETE | FILE_GENERIC_WRITE,
1631                          win32_newpath.get_object_attr (attr, sa),
1632                          &io, NULL, FILE_ATTRIBUTE_NORMAL,
1633                          FILE_SHARE_VALID_FLAGS,
1634                          isdevice ? FILE_OVERWRITE_IF : FILE_CREATE,
1635                          FILE_SYNCHRONOUS_IO_NONALERT
1636                          | FILE_NON_DIRECTORY_FILE
1637                          | FILE_OPEN_FOR_BACKUP_INTENT,
1638                          NULL, 0);
1639   if (!NT_SUCCESS (status))
1640     {
1641       __seterrno_from_nt_status (status);
1642       goto done;
1643     }
1644   if (win32_newpath.has_acls () && win32_newpath.isremote ())
1645     set_file_attribute (fh, win32_newpath, ILLEGAL_UID, ILLEGAL_GID,
1646                         S_IFLNK | STD_RBITS | STD_WBITS);
1647   status = NtWriteFile (fh, NULL, NULL, NULL, &io, buf, cp - buf, NULL, NULL);
1648   if (NT_SUCCESS (status) && io.Information == (ULONG) (cp - buf))
1649     {
1650       status = NtSetAttributesFile (fh, mk_winsym ? FILE_ATTRIBUTE_READONLY
1651                                                   : FILE_ATTRIBUTE_SYSTEM);
1652       if (!NT_SUCCESS (status))
1653         debug_printf ("Setting attributes failed, status = %p", status);
1654       res = 0;
1655     }
1656   else
1657     {
1658       __seterrno_from_nt_status (status);
1659       FILE_DISPOSITION_INFORMATION fdi = { TRUE };
1660       status = NtSetInformationFile (fh, &io, &fdi, sizeof fdi,
1661                                      FileDispositionInformation);
1662       if (!NT_SUCCESS (status))
1663         debug_printf ("Setting delete dispostion failed, status = %p", status);
1664     }
1665   NtClose (fh);
1666
1667 done:
1668   syscall_printf ("%d = symlink_worker (%s, %s, %d, %d)", res, oldpath,
1669                   newpath, mk_winsym, isdevice);
1670   return res;
1671 }
1672
1673 static bool
1674 cmp_shortcut_header (win_shortcut_hdr *file_header)
1675 {
1676   /* A Cygwin or U/Win shortcut only contains a description and a relpath.
1677      Cygwin shortcuts also might contain an ITEMIDLIST. The run type is
1678      always set to SW_NORMAL. */
1679   return file_header->size == sizeof (win_shortcut_hdr)
1680       && !memcmp (&file_header->magic, &GUID_shortcut, sizeof GUID_shortcut)
1681       && (file_header->flags & ~WSH_FLAG_IDLIST)
1682          == (WSH_FLAG_DESC | WSH_FLAG_RELPATH)
1683       && file_header->run == SW_NORMAL;
1684 }
1685
1686 int
1687 symlink_info::check_shortcut (HANDLE in_h)
1688 {
1689   tmp_pathbuf tp;
1690   win_shortcut_hdr *file_header;
1691   char *buf, *cp;
1692   unsigned short len;
1693   int res = 0;
1694   UNICODE_STRING same = { 0, 0, (PWCHAR) L"" };
1695   OBJECT_ATTRIBUTES attr;
1696   NTSTATUS status;
1697   HANDLE h;
1698   IO_STATUS_BLOCK io;
1699   FILE_STANDARD_INFORMATION fsi;
1700
1701   InitializeObjectAttributes (&attr, &same, 0, in_h, NULL);
1702   status = NtOpenFile (&h, FILE_READ_DATA | SYNCHRONIZE,
1703                        &attr, &io, FILE_SHARE_VALID_FLAGS,
1704                        FILE_OPEN_FOR_BACKUP_INTENT
1705                        | FILE_SYNCHRONOUS_IO_NONALERT);
1706   if (!NT_SUCCESS (status))
1707     return 0;
1708   status = NtQueryInformationFile (h, &io, &fsi, sizeof fsi,
1709                                    FileStandardInformation);
1710   if (!NT_SUCCESS (status))
1711     {
1712       set_error (EIO);
1713       goto out;
1714     }
1715   if (fsi.EndOfFile.QuadPart <= sizeof (win_shortcut_hdr)
1716       || fsi.EndOfFile.QuadPart > 4 * 65536)
1717     goto out;
1718   if (fsi.EndOfFile.LowPart < NT_MAX_PATH * sizeof (WCHAR))
1719     buf = (char *) tp.w_get ();
1720   else
1721     buf = (char *) alloca (fsi.EndOfFile.LowPart + 1);
1722   if (!NT_SUCCESS (NtReadFile (h, NULL, NULL, NULL,
1723                                &io, buf, fsi.EndOfFile.LowPart, NULL, NULL)))
1724     {
1725       set_error (EIO);
1726       goto out;
1727     }
1728   file_header = (win_shortcut_hdr *) buf;
1729   if (io.Information != fsi.EndOfFile.LowPart
1730       || !cmp_shortcut_header (file_header))
1731     goto out;
1732   cp = buf + sizeof (win_shortcut_hdr);
1733   if (file_header->flags & WSH_FLAG_IDLIST) /* Skip ITEMIDLIST */
1734     cp += *(unsigned short *) cp + 2;
1735   if (!(len = *(unsigned short *) cp))
1736     goto out;
1737   cp += 2;
1738   /* Check if this is a device file - these start with the sequence :\\ */
1739   if (strncmp (cp, ":\\", 2) == 0)
1740     res = strlen (strcpy (contents, cp)); /* Don't mess with device files */
1741   else
1742     {
1743       /* Has appended full path?  If so, use it instead of description. */
1744       unsigned short relpath_len = *(unsigned short *) (cp + len);
1745       if (cp + len + 2 + relpath_len < buf + fsi.EndOfFile.LowPart)
1746         {
1747           cp += len + 2 + relpath_len;
1748           len = *(unsigned short *) cp;
1749           cp += 2;
1750         }
1751       if (*(PWCHAR) cp == 0xfeff)       /* BOM */
1752         {
1753           char *tmpbuf = tp.c_get ();
1754           if (sys_wcstombs (tmpbuf, NT_MAX_PATH, (PWCHAR) (cp + 2))
1755               > SYMLINK_MAX + 1)
1756             goto out;
1757           res = posixify (tmpbuf);
1758         }
1759       else if (len > SYMLINK_MAX)
1760         goto out;
1761       else
1762         {
1763           cp[len] = '\0';
1764           res = posixify (cp);
1765         }
1766     }
1767   if (res) /* It's a symlink.  */
1768     pflags = PATH_SYMLINK | PATH_LNK;
1769
1770 out:
1771   NtClose (h);
1772   return res;
1773 }
1774
1775 int
1776 symlink_info::check_sysfile (HANDLE in_h)
1777 {
1778   tmp_pathbuf tp;
1779   char cookie_buf[sizeof (SYMLINK_COOKIE) - 1];
1780   char *srcbuf = tp.c_get ();
1781   int res = 0;
1782   UNICODE_STRING same = { 0, 0, (PWCHAR) L"" };
1783   OBJECT_ATTRIBUTES attr;
1784   NTSTATUS status;
1785   HANDLE h;
1786   IO_STATUS_BLOCK io;
1787
1788   InitializeObjectAttributes (&attr, &same, 0, in_h, NULL);
1789   status = NtOpenFile (&h, FILE_READ_DATA | SYNCHRONIZE,
1790                        &attr, &io, FILE_SHARE_VALID_FLAGS,
1791                        FILE_OPEN_FOR_BACKUP_INTENT
1792                        | FILE_SYNCHRONOUS_IO_NONALERT);
1793   if (!NT_SUCCESS (status))
1794     ;
1795   else if (!NT_SUCCESS (status = NtReadFile (h, NULL, NULL, NULL, &io,
1796                                              cookie_buf, sizeof (cookie_buf),
1797                                              NULL, NULL)))
1798     {
1799       debug_printf ("ReadFile1 failed");
1800       if (status != STATUS_END_OF_FILE)
1801         set_error (EIO);
1802     }
1803   else if (io.Information == sizeof (cookie_buf)
1804            && memcmp (cookie_buf, SYMLINK_COOKIE, sizeof (cookie_buf)) == 0)
1805     {
1806       /* It's a symlink.  */
1807       pflags = PATH_SYMLINK;
1808
1809       status = NtReadFile (h, NULL, NULL, NULL, &io, srcbuf,
1810                            NT_MAX_PATH, NULL, NULL);
1811       if (!NT_SUCCESS (status))
1812         {
1813           debug_printf ("ReadFile2 failed");
1814           if (status != STATUS_END_OF_FILE)
1815             set_error (EIO);
1816         }
1817       else if (*(PWCHAR) srcbuf == 0xfeff)      /* BOM */
1818         {
1819           char *tmpbuf = tp.c_get ();
1820           if (sys_wcstombs (tmpbuf, NT_MAX_PATH, (PWCHAR) (srcbuf + 2))
1821               > SYMLINK_MAX + 1)
1822             debug_printf ("symlink string too long");
1823           else
1824             res = posixify (tmpbuf);
1825         }
1826       else if (io.Information > SYMLINK_MAX + 1)
1827         debug_printf ("symlink string too long");
1828       else
1829         res = posixify (srcbuf);
1830     }
1831   else if (io.Information == sizeof (cookie_buf)
1832            && memcmp (cookie_buf, SOCKET_COOKIE, sizeof (cookie_buf)) == 0)
1833     pflags |= PATH_SOCKET;
1834   NtClose (h);
1835   return res;
1836 }
1837
1838 int
1839 symlink_info::check_reparse_point (HANDLE h)
1840 {
1841   tmp_pathbuf tp;
1842   NTSTATUS status;
1843   IO_STATUS_BLOCK io;
1844   PREPARSE_DATA_BUFFER rp = (PREPARSE_DATA_BUFFER) tp.c_get ();
1845   char srcbuf[SYMLINK_MAX + 7];
1846
1847   status = NtFsControlFile (h, NULL, NULL, NULL, &io, FSCTL_GET_REPARSE_POINT,
1848                             NULL, 0, (LPVOID) rp,
1849                             MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
1850   if (!NT_SUCCESS (status))
1851     {
1852       debug_printf ("NtFsControlFile(FSCTL_GET_REPARSE_POINT) failed, %p",
1853                     status);
1854       set_error (EIO);
1855       return 0;
1856     }
1857   if (rp->ReparseTag == IO_REPARSE_TAG_SYMLINK)
1858     {
1859       sys_wcstombs (srcbuf, SYMLINK_MAX + 1,
1860                     (WCHAR *)((char *)rp->SymbolicLinkReparseBuffer.PathBuffer
1861                           + rp->SymbolicLinkReparseBuffer.SubstituteNameOffset),
1862                     rp->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof (WCHAR));
1863       pflags = PATH_SYMLINK | PATH_REP;
1864       fileattr &= ~FILE_ATTRIBUTE_DIRECTORY;
1865     }
1866   else if (rp->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
1867     {
1868       if (rp->SymbolicLinkReparseBuffer.PrintNameLength == 0)
1869         {
1870           /* Likely a volume mount point.  Not treated as symlink. */
1871           return 0;
1872         }
1873       sys_wcstombs (srcbuf, SYMLINK_MAX + 1,
1874                     (WCHAR *)((char *)rp->MountPointReparseBuffer.PathBuffer
1875                             + rp->MountPointReparseBuffer.SubstituteNameOffset),
1876                     rp->MountPointReparseBuffer.SubstituteNameLength / sizeof (WCHAR));
1877       pflags = PATH_SYMLINK | PATH_REP;
1878       fileattr &= ~FILE_ATTRIBUTE_DIRECTORY;
1879     }
1880   return posixify (srcbuf);
1881 }
1882
1883 int
1884 symlink_info::check_nfs_symlink (HANDLE h)
1885 {
1886   tmp_pathbuf tp;
1887   NTSTATUS status;
1888   IO_STATUS_BLOCK io;
1889   struct {
1890     FILE_GET_EA_INFORMATION fgei;
1891     char buf[sizeof (NFS_SYML_TARGET)];
1892   } fgei_buf;
1893   PFILE_FULL_EA_INFORMATION pffei;
1894   int res = 0;
1895
1896   /* To find out if the file is a symlink and to get the symlink target,
1897      try to fetch the NfsSymlinkTargetName EA. */
1898   fgei_buf.fgei.NextEntryOffset = 0;
1899   fgei_buf.fgei.EaNameLength = sizeof (NFS_SYML_TARGET) - 1;
1900   stpcpy (fgei_buf.fgei.EaName, NFS_SYML_TARGET);
1901   pffei = (PFILE_FULL_EA_INFORMATION) tp.w_get ();
1902   status = NtQueryEaFile (h, &io, pffei, NT_MAX_PATH * sizeof (WCHAR), TRUE,
1903                           &fgei_buf.fgei, sizeof fgei_buf, NULL, TRUE);
1904   if (NT_SUCCESS (status) && pffei->EaValueLength > 0)
1905     {
1906       PWCHAR spath = (PWCHAR)
1907                      (pffei->EaName + pffei->EaNameLength + 1);
1908       res = sys_wcstombs (contents, SYMLINK_MAX + 1,
1909                       spath, pffei->EaValueLength);
1910       pflags = PATH_SYMLINK;
1911     }
1912   return res;
1913 }
1914
1915 int
1916 symlink_info::posixify (char *srcbuf)
1917 {
1918   /* The definition for a path in a native symlink is a bit weird.  The Flags
1919      value seem to contain 0 for absolute paths (stored as NT native path)
1920      and 1 for relative paths.  Relative paths are paths not starting with a
1921      drive letter.  These are not converted to NT native, but stored as
1922      given.  A path starting with a single backslash is relative to the
1923      current drive thus a "relative" value (Flags == 1).
1924      Funny enough it's possible to store paths with slashes instead of
1925      backslashes, but they are evaluated incorrectly by subsequent Windows
1926      calls like CreateFile (ERROR_INVALID_NAME).  So, what we do here is to
1927      take paths starting with slashes at face value, evaluating them as
1928      Cygwin specific POSIX paths.
1929      A path starting with two slashes(!) or backslashes is converted into an
1930      NT UNC path.  Unfortunately, in contrast to POSIX rules, paths starting
1931      with three or more (back)slashes are also converted into UNC paths,
1932      just incorrectly sticking to one redundant leading backslashe.  We go
1933      along with this behaviour to avoid scenarios in which native tools access
1934      other files than Cygwin.
1935      The above rules are used exactly the same way on Cygwin specific symlinks
1936      (sysfiles and shortcuts) to eliminate non-POSIX paths in the output. */
1937
1938   /* Eliminate native NT prefixes. */
1939   if (srcbuf[0] == '\\' && !strncmp (srcbuf + 1, "??\\", 3))
1940     {
1941       srcbuf += 4;
1942       if (srcbuf[1] != ':') /* native UNC path */
1943         *(srcbuf += 2) = '\\';
1944     }
1945   if (isdrive (srcbuf))
1946     mount_table->conv_to_posix_path (srcbuf, contents, 0);
1947   else if (srcbuf[0] == '\\')
1948     {
1949       if (srcbuf[1] == '\\') /* UNC path */
1950         slashify (srcbuf, contents, 0);
1951       else /* Paths starting with \ are current drive relative. */
1952         {
1953           char cvtbuf[SYMLINK_MAX + 1];
1954
1955           stpcpy (cvtbuf + cygheap->cwd.get_drive (cvtbuf), srcbuf);
1956           mount_table->conv_to_posix_path (cvtbuf, contents, 0);
1957         }
1958     }
1959   else /* Everything else is taken as is. */
1960     slashify (srcbuf, contents, 0);
1961   return strlen (contents);
1962 }
1963
1964 enum
1965 {
1966   SCAN_BEG,
1967   SCAN_LNK,
1968   SCAN_HASLNK,
1969   SCAN_JUSTCHECK,
1970   SCAN_JUSTCHECKTHIS, /* Never try to append a suffix. */
1971   SCAN_APPENDLNK,
1972   SCAN_EXTRALNK,
1973   SCAN_DONE,
1974 };
1975
1976 class suffix_scan
1977 {
1978   const suffix_info *suffixes, *suffixes_start;
1979   int nextstate;
1980   char *eopath;
1981 public:
1982   const char *path;
1983   char *has (const char *, const suffix_info *);
1984   int next ();
1985   int lnk_match () {return nextstate >= SCAN_APPENDLNK;}
1986 };
1987
1988 char *
1989 suffix_scan::has (const char *in_path, const suffix_info *in_suffixes)
1990 {
1991   nextstate = SCAN_BEG;
1992   suffixes = suffixes_start = in_suffixes;
1993
1994   const char *fname = strrchr (in_path, '\\');
1995   fname = fname ? fname + 1 : in_path;
1996   char *ext_here = strrchr (fname, '.');
1997   path = in_path;
1998   eopath = strchr (path, '\0');
1999
2000   if (!ext_here)
2001     goto noext;
2002
2003   if (suffixes)
2004     {
2005       /* Check if the extension matches a known extension */
2006       for (const suffix_info *ex = in_suffixes; ex->name != NULL; ex++)
2007         if (ascii_strcasematch (ext_here, ex->name))
2008           {
2009             nextstate = SCAN_JUSTCHECK;
2010             suffixes = NULL;    /* Has an extension so don't scan for one. */
2011             goto done;
2012           }
2013     }
2014
2015   /* Didn't match.  Use last resort -- .lnk. */
2016   if (ascii_strcasematch (ext_here, ".lnk"))
2017     {
2018       nextstate = SCAN_HASLNK;
2019       suffixes = NULL;
2020     }
2021
2022  noext:
2023   ext_here = eopath;
2024
2025  done:
2026   /* Avoid attaching suffixes if the resulting filename would be invalid. */
2027   if (eopath - fname > NAME_MAX - 4)
2028     {
2029       nextstate = SCAN_JUSTCHECKTHIS;
2030       suffixes = NULL;
2031     }
2032   return ext_here;
2033 }
2034
2035 int
2036 suffix_scan::next ()
2037 {
2038   for (;;)
2039     {
2040       if (!suffixes)
2041         switch (nextstate)
2042           {
2043           case SCAN_BEG:
2044             suffixes = suffixes_start;
2045             if (!suffixes)
2046               {
2047                 nextstate = SCAN_LNK;
2048                 return 1;
2049               }
2050             nextstate = SCAN_EXTRALNK;
2051             /* fall through to suffix checking below */
2052             break;
2053           case SCAN_HASLNK:
2054             nextstate = SCAN_APPENDLNK; /* Skip SCAN_BEG */
2055             return 1;
2056           case SCAN_EXTRALNK:
2057             nextstate = SCAN_DONE;
2058             *eopath = '\0';
2059             return 0;
2060           case SCAN_JUSTCHECK:
2061             nextstate = SCAN_LNK;
2062             return 1;
2063           case SCAN_JUSTCHECKTHIS:
2064             nextstate = SCAN_DONE;
2065             return 1;
2066           case SCAN_LNK:
2067           case SCAN_APPENDLNK:
2068             strcat (eopath, ".lnk");
2069             nextstate = SCAN_DONE;
2070             return 1;
2071           default:
2072             *eopath = '\0';
2073             return 0;
2074           }
2075
2076       while (suffixes && suffixes->name)
2077         if (nextstate == SCAN_EXTRALNK && !suffixes->addon)
2078           suffixes++;
2079         else
2080           {
2081             strcpy (eopath, suffixes->name);
2082             if (nextstate == SCAN_EXTRALNK)
2083               strcat (eopath, ".lnk");
2084             suffixes++;
2085             return 1;
2086           }
2087       suffixes = NULL;
2088     }
2089 }
2090
2091 bool
2092 symlink_info::set_error (int in_errno)
2093 {
2094   bool res;
2095   if (!(pflags & PATH_NO_ACCESS_CHECK) || in_errno == ENAMETOOLONG || in_errno == EIO)
2096     {
2097       error = in_errno;
2098       res = true;
2099     }
2100   else if (in_errno == ENOENT)
2101     res = true;
2102   else
2103     {
2104       fileattr = FILE_ATTRIBUTE_NORMAL;
2105       res = false;
2106     }
2107   return res;
2108 }
2109
2110 bool
2111 symlink_info::parse_device (const char *contents)
2112 {
2113   char *endptr;
2114   _major_t mymajor;
2115   _major_t myminor;
2116   _mode_t mymode;
2117
2118   mymajor = strtol (contents += 2, &endptr, 16);
2119   if (endptr == contents)
2120     return isdevice = false;
2121
2122   contents = endptr;
2123   myminor = strtol (++contents, &endptr, 16);
2124   if (endptr == contents)
2125     return isdevice = false;
2126
2127   contents = endptr;
2128   mymode = strtol (++contents, &endptr, 16);
2129   if (endptr == contents)
2130     return isdevice = false;
2131
2132   if ((mymode & S_IFMT) == S_IFIFO)
2133     {
2134       mymajor = _major (FH_FIFO);
2135       myminor = _minor (FH_FIFO);
2136     }
2137
2138   major = mymajor;
2139   minor = myminor;
2140   mode = mymode;
2141   return isdevice = true;
2142 }
2143
2144 /* Check if PATH is a symlink.  PATH must be a valid Win32 path name.
2145
2146    If PATH is a symlink, put the value of the symlink--the file to
2147    which it points--into BUF.  The value stored in BUF is not
2148    necessarily null terminated.  BUFLEN is the length of BUF; only up
2149    to BUFLEN characters will be stored in BUF.  BUF may be NULL, in
2150    which case nothing will be stored.
2151
2152    Set *SYML if PATH is a symlink.
2153
2154    Set *EXEC if PATH appears to be executable.  This is an efficiency
2155    hack because we sometimes have to open the file anyhow.  *EXEC will
2156    not be set for every executable file.
2157
2158    Return -1 on error, 0 if PATH is not a symlink, or the length
2159    stored into BUF if PATH is a symlink.  */
2160
2161 int
2162 symlink_info::check (char *path, const suffix_info *suffixes, unsigned opt,
2163                      fs_info &fs)
2164 {
2165   HANDLE h = NULL;
2166   int res = 0;
2167   suffix_scan suffix;
2168   contents[0] = '\0';
2169
2170   issymlink = true;
2171   isdevice = false;
2172   ext_here = suffix.has (path, suffixes);
2173   extn = ext_here - path;
2174   major = 0;
2175   minor = 0;
2176   mode = 0;
2177   pflags &= ~(PATH_SYMLINK | PATH_LNK | PATH_REP);
2178   ULONG ci_flag = cygwin_shared->obcaseinsensitive || (pflags & PATH_NOPOSIX)
2179                   ? OBJ_CASE_INSENSITIVE : 0;
2180
2181   /* TODO: Temporarily do all char->UNICODE conversion here.  This should
2182      already be slightly faster than using Ascii functions. */
2183   tmp_pathbuf tp;
2184   UNICODE_STRING upath;
2185   OBJECT_ATTRIBUTES attr;
2186   tp.u_get (&upath);
2187   InitializeObjectAttributes (&attr, &upath, ci_flag, NULL, NULL);
2188
2189   PVOID eabuf = &nfs_aol_ffei;
2190   ULONG easize = sizeof nfs_aol_ffei;
2191
2192   while (suffix.next ())
2193     {
2194       FILE_BASIC_INFORMATION fbi;
2195       NTSTATUS status;
2196       IO_STATUS_BLOCK io;
2197       bool no_ea = false;
2198       bool fs_update_called = false;
2199
2200       error = 0;
2201       get_nt_native_path (suffix.path, upath);
2202       if (h)
2203         {
2204           NtClose (h);
2205           h = NULL;
2206         }
2207       /* The EA given to NtCreateFile allows to get a handle to a symlink on
2208          an NFS share, rather than getting a handle to the target of the
2209          symlink (which would spoil the task of this method quite a bit).
2210          Fortunately it's ignored on most other file systems so we don't have
2211          to special case NFS too much. */
2212       status = NtCreateFile (&h,
2213                              READ_CONTROL | FILE_READ_ATTRIBUTES | FILE_READ_EA,
2214                              &attr, &io, NULL, 0, FILE_SHARE_VALID_FLAGS,
2215                              FILE_OPEN,
2216                              FILE_OPEN_REPARSE_POINT
2217                              | FILE_OPEN_FOR_BACKUP_INTENT,
2218                              eabuf, easize);
2219       /* No right to access EAs or EAs not supported? */
2220       if (status == STATUS_ACCESS_DENIED || status == STATUS_EAS_NOT_SUPPORTED
2221           || status == STATUS_NOT_SUPPORTED
2222           /* Or a bug in Samba 3.2.x (x <= 7) when accessing a share's root dir
2223              which has EAs enabled? */
2224           || status == STATUS_INVALID_PARAMETER)
2225         {
2226           no_ea = true;
2227           /* If EAs are not supported, there's no sense to check them again
2228              with suffixes attached.  So we set eabuf/easize to 0 here once. */
2229           if (status == STATUS_EAS_NOT_SUPPORTED
2230               || status == STATUS_NOT_SUPPORTED)
2231             {
2232               eabuf = NULL;
2233               easize = 0;
2234             }
2235           status = NtOpenFile (&h, READ_CONTROL | FILE_READ_ATTRIBUTES,
2236                                &attr, &io, FILE_SHARE_VALID_FLAGS,
2237                                FILE_OPEN_REPARSE_POINT
2238                                | FILE_OPEN_FOR_BACKUP_INTENT);
2239         }
2240       if (status == STATUS_OBJECT_NAME_NOT_FOUND && ci_flag == 0
2241           && wincap.has_broken_udf ())
2242         {
2243           /* On NT 5.x UDF is broken (at least) in terms of case sensitivity.
2244              When trying to open a file case sensitive, the file appears to be
2245              non-existant.  Another bug is described in fs_info::update. */
2246           attr.Attributes = OBJ_CASE_INSENSITIVE;
2247           status = NtOpenFile (&h, READ_CONTROL | FILE_READ_ATTRIBUTES,
2248                                &attr, &io, FILE_SHARE_VALID_FLAGS,
2249                                FILE_OPEN_REPARSE_POINT
2250                                | FILE_OPEN_FOR_BACKUP_INTENT);
2251           attr.Attributes = 0;
2252           if (NT_SUCCESS (status))
2253             {
2254               fs.update (&upath, h);
2255               if (fs.is_udf ())
2256                 fs_update_called = true;
2257               else
2258                 {
2259                   NtClose (h);
2260                   status = STATUS_OBJECT_NAME_NOT_FOUND;
2261                 }
2262             }
2263         }
2264       if (NT_SUCCESS (status)
2265           && NT_SUCCESS (status
2266                          = NtQueryInformationFile (h, &io, &fbi, sizeof fbi,
2267                                                    FileBasicInformation)))
2268         fileattr = fbi.FileAttributes;
2269       else
2270         {
2271           debug_printf ("%p = NtQueryInformationFile (%S)", status, &upath);
2272           h = NULL;
2273           fileattr = INVALID_FILE_ATTRIBUTES;
2274
2275           /* One of the inner path components is invalid, or the path contains
2276              invalid characters.  Bail out with ENOENT.
2277
2278              Note that additional STATUS_OBJECT_PATH_INVALID and
2279              STATUS_OBJECT_PATH_SYNTAX_BAD status codes exist.  The first one
2280              is seemingly not generated by NtQueryAttributesFile, the latter
2281              is only generated if the path is no absolute path within the
2282              NT name space, which should not happen and would point to an
2283              error in get_nt_native_path.  Both status codes are deliberately
2284              not tested here unless proved necessary. */
2285           if (status == STATUS_OBJECT_PATH_NOT_FOUND
2286               || status == STATUS_OBJECT_NAME_INVALID
2287               || status == STATUS_NO_MEDIA_IN_DEVICE)
2288             {
2289               set_error (ENOENT);
2290               goto file_not_symlink;
2291             }
2292           if (status != STATUS_OBJECT_NAME_NOT_FOUND
2293               && status != STATUS_NO_SUCH_FILE) /* ENOENT on NFS or 9x share */
2294             {
2295               /* The file exists, but the user can't access it for one reason
2296                  or the other.  To get the file attributes we try to access the
2297                  information by opening the parent directory and getting the
2298                  file attributes using a matching NtQueryDirectoryFile call. */
2299               UNICODE_STRING dirname, basename;
2300               OBJECT_ATTRIBUTES dattr;
2301               HANDLE dir;
2302               struct {
2303                 FILE_DIRECTORY_INFORMATION fdi;
2304                 WCHAR dummy_buf[NAME_MAX + 1];
2305               } fdi_buf;
2306
2307               RtlSplitUnicodePath (&upath, &dirname, &basename);
2308               InitializeObjectAttributes (&dattr, &dirname, ci_flag,
2309                                           NULL, NULL);
2310               status = NtOpenFile (&dir, SYNCHRONIZE | FILE_LIST_DIRECTORY,
2311                                    &dattr, &io, FILE_SHARE_VALID_FLAGS,
2312                                    FILE_SYNCHRONOUS_IO_NONALERT
2313                                    | FILE_OPEN_FOR_BACKUP_INTENT
2314                                    | FILE_DIRECTORY_FILE);
2315               if (!NT_SUCCESS (status))
2316                 {
2317                   debug_printf ("%p = NtOpenFile(%S)", status, &dirname);
2318                   fileattr = 0;
2319                 }
2320               else
2321                 {
2322                   status = NtQueryDirectoryFile (dir, NULL, NULL, NULL, &io,
2323                                                  &fdi_buf, sizeof fdi_buf,
2324                                                  FileDirectoryInformation,
2325                                                  TRUE, &basename, TRUE);
2326                   /* Take the opportunity to check file system while we're
2327                      having the handle to the parent dir. */
2328                   fs.update (&upath, h);
2329                   NtClose (dir);
2330                   if (!NT_SUCCESS (status))
2331                     {
2332                       debug_printf ("%p = NtQueryDirectoryFile(%S)",
2333                                     status, &dirname);
2334                       if (status == STATUS_NO_SUCH_FILE)
2335                         {
2336                           /* This can happen when trying to access files
2337                              which match DOS device names on SMB shares.
2338                              NtOpenFile failed with STATUS_ACCESS_DENIED,
2339                              but the NtQueryDirectoryFile tells us the
2340                              file doesn't exist.  We're suspicious in this
2341                              case and retry with the next suffix instead of
2342                              just giving up. */
2343                           set_error (ENOENT);
2344                           continue;
2345                         }
2346                       fileattr = 0;
2347                     }
2348                   else
2349                     fileattr = fdi_buf.fdi.FileAttributes;
2350                 }
2351               ext_tacked_on = !!*ext_here;
2352               goto file_not_symlink;
2353             }
2354           set_error (ENOENT);
2355           continue;
2356         }
2357
2358       /* Check file system while we're having the file open anyway.
2359          This speeds up path_conv noticably (~10%). */
2360       if (!fs_update_called)
2361         fs.update (&upath, h);
2362
2363       ext_tacked_on = !!*ext_here;
2364
2365       res = -1;
2366
2367       /* Windows shortcuts are potentially treated as symlinks.  Valid Cygwin
2368          & U/WIN shortcuts are R/O, but definitely not directories. */
2369       if ((fileattr & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_DIRECTORY))
2370           == FILE_ATTRIBUTE_READONLY && suffix.lnk_match ())
2371         {
2372           res = check_shortcut (h);
2373           if (!res)
2374             {
2375               /* If searching for `foo' and then finding a `foo.lnk' which is
2376                  no shortcut, return the same as if file not found. */
2377               if (ext_tacked_on)
2378                 {
2379                   fileattr = INVALID_FILE_ATTRIBUTES;
2380                   set_error (ENOENT);
2381                   continue;
2382                 }
2383             }
2384           else if (contents[0] != ':' || contents[1] != '\\'
2385                    || !parse_device (contents))
2386             break;
2387         }
2388
2389       /* If searching for `foo' and then finding a `foo.lnk' which is
2390          no shortcut, return the same as if file not found. */
2391       else if (suffix.lnk_match () && ext_tacked_on)
2392         {
2393           fileattr = INVALID_FILE_ATTRIBUTES;
2394           set_error (ENOENT);
2395           continue;
2396         }
2397
2398       /* Reparse points are potentially symlinks.  This check must be
2399          performed before checking the SYSTEM attribute for sysfile
2400          symlinks, since reparse points can have this flag set, too.
2401          For instance, Vista starts to create a couple of reparse points
2402          with SYSTEM and HIDDEN flags set. */
2403       else if (fileattr & FILE_ATTRIBUTE_REPARSE_POINT)
2404         {
2405           res = check_reparse_point (h);
2406           if (res)
2407             break;
2408         }
2409
2410       /* This is the old Cygwin method creating symlinks.  A symlink will
2411          have the `system' file attribute.  Only files can be symlinks
2412          (which can be symlinks to directories). */
2413       else if ((fileattr & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DIRECTORY))
2414                == FILE_ATTRIBUTE_SYSTEM)
2415         {
2416           res = check_sysfile (h);
2417           if (res)
2418             break;
2419         }
2420
2421       /* If the file could be opened with FILE_READ_EA, and if it's on a
2422          NFS share, check if it's a symlink.  Only files can be symlinks
2423          (which can be symlinks to directories). */
2424       else if (fs.is_nfs () && !no_ea && !(fileattr & FILE_ATTRIBUTE_DIRECTORY))
2425         {
2426           res = check_nfs_symlink (h);
2427           if (res)
2428             break;
2429         }
2430
2431     /* Normal file. */
2432     file_not_symlink:
2433       issymlink = false;
2434       syscall_printf ("%s", isdevice ? "is a device" : "not a symlink");
2435       res = 0;
2436       break;
2437     }
2438
2439   if (h)
2440     NtClose (h);
2441
2442   syscall_printf ("%d = symlink.check (%s, %p) (%p)",
2443                   res, suffix.path, contents, pflags);
2444   return res;
2445 }
2446
2447 /* "path" is the path in a virtual symlink.  Set a symlink_info struct from
2448    that and proceed with further path checking afterwards. */
2449 int
2450 symlink_info::set (char *path)
2451 {
2452   strcpy (contents, path);
2453   pflags = PATH_SYMLINK;
2454   fileattr = FILE_ATTRIBUTE_NORMAL;
2455   error = 0;
2456   issymlink = true;
2457   isdevice = false;
2458   ext_tacked_on = false;
2459   ext_here = NULL;
2460   extn = major = minor = mode = 0;
2461   return strlen (path);
2462 }
2463
2464 /* readlink system call */
2465
2466 extern "C" ssize_t
2467 readlink (const char *path, char *buf, size_t buflen)
2468 {
2469   if (buflen < 0)
2470     {
2471       set_errno (ENAMETOOLONG);
2472       return -1;
2473     }
2474
2475   path_conv pathbuf (path, PC_SYM_CONTENTS, stat_suffixes);
2476
2477   if (pathbuf.error)
2478     {
2479       set_errno (pathbuf.error);
2480       syscall_printf ("-1 = readlink (%s, %p, %d)", path, buf, buflen);
2481       return -1;
2482     }
2483
2484   if (!pathbuf.exists ())
2485     {
2486       set_errno (ENOENT);
2487       return -1;
2488     }
2489
2490   if (!pathbuf.issymlink ())
2491     {
2492       if (pathbuf.exists ())
2493         set_errno (EINVAL);
2494       return -1;
2495     }
2496
2497   ssize_t len = min (buflen, strlen (pathbuf.get_win32 ()));
2498   memcpy (buf, pathbuf.get_win32 (), len);
2499
2500   /* errno set by symlink.check if error */
2501   return len;
2502 }
2503
2504 /* Some programs rely on st_dev/st_ino being unique for each file.
2505    Hash the path name and hope for the best.  The hash arg is not
2506    always initialized to zero since readdir needs to compute the
2507    dirent ino_t based on a combination of the hash of the directory
2508    done during the opendir call and the hash or the filename within
2509    the directory.  FIXME: Not bullet-proof. */
2510 /* Cygwin internal */
2511 __ino64_t __stdcall
2512 hash_path_name (__ino64_t hash, PUNICODE_STRING name)
2513 {
2514   if (name->Length == 0)
2515     return hash;
2516
2517   /* Build up hash. Name is already normalized */
2518   USHORT len = name->Length / sizeof (WCHAR);
2519   for (USHORT idx = 0; idx < len; ++idx)
2520     hash = RtlUpcaseUnicodeChar (name->Buffer[idx])
2521            + (hash << 6) + (hash << 16) - hash;
2522   return hash;
2523 }
2524
2525 __ino64_t __stdcall
2526 hash_path_name (__ino64_t hash, PCWSTR name)
2527 {
2528   UNICODE_STRING uname;
2529   RtlInitUnicodeString (&uname, name);
2530   return hash_path_name (hash, &uname);
2531 }
2532
2533 __ino64_t __stdcall
2534 hash_path_name (__ino64_t hash, const char *name)
2535 {
2536   UNICODE_STRING uname;
2537   RtlCreateUnicodeStringFromAsciiz (&uname, name);
2538   __ino64_t ret = hash_path_name (hash, &uname);
2539   RtlFreeUnicodeString (&uname);
2540   return ret;
2541 }
2542
2543 extern "C" char *
2544 getcwd (char *buf, size_t ulen)
2545 {
2546   char* res = NULL;
2547   myfault efault;
2548   if (efault.faulted (EFAULT))
2549       /* errno set */;
2550   else if (ulen == 0 && buf)
2551     set_errno (EINVAL);
2552   else
2553     res = cygheap->cwd.get (buf, 1, 1, ulen);
2554   return res;
2555 }
2556
2557 /* getwd: Legacy. */
2558 extern "C" char *
2559 getwd (char *buf)
2560 {
2561   return getcwd (buf, PATH_MAX + 1);  /*Per SuSv3!*/
2562 }
2563
2564 /* chdir: POSIX 5.2.1.1 */
2565 extern "C" int
2566 chdir (const char *in_dir)
2567 {
2568   myfault efault;
2569   if (efault.faulted (EFAULT))
2570     return -1;
2571   if (!*in_dir)
2572     {
2573       set_errno (ENOENT);
2574       return -1;
2575     }
2576
2577   syscall_printf ("dir '%s'", in_dir);
2578
2579   /* Convert path.  First argument ensures that we don't check for NULL/empty/invalid
2580      again. */
2581   path_conv path (PC_NONULLEMPTY, in_dir, PC_SYM_FOLLOW | PC_POSIX);
2582   if (path.error)
2583     {
2584       set_errno (path.error);
2585       syscall_printf ("-1 = chdir (%s)", in_dir);
2586       return -1;
2587     }
2588
2589   int res = -1;
2590   bool doit = false;
2591   const char *posix_cwd = NULL;
2592   int devn = path.get_devn ();
2593   if (!path.exists ())
2594     set_errno (ENOENT);
2595   else if (!path.isdir ())
2596     set_errno (ENOTDIR);
2597   else if (!isvirtual_dev (devn))
2598     {
2599       /* The sequence chdir("xx"); chdir(".."); must be a noop if xx
2600          is not a symlink. This is exploited by find.exe.
2601          The posix_cwd is just path.normalized_path.
2602          In other cases we let cwd.set obtain the Posix path through
2603          the mount table. */
2604       if (!isdrive(path.normalized_path))
2605         posix_cwd = path.normalized_path;
2606       res = 0;
2607       doit = true;
2608     }
2609   else
2610    {
2611      posix_cwd = path.normalized_path;
2612      res = 0;
2613    }
2614
2615   if (!res)
2616     res = cygheap->cwd.set (path.get_nt_native_path (), posix_cwd, doit);
2617
2618   /* Note that we're accessing cwd.posix without a lock here.  I didn't think
2619      it was worth locking just for strace. */
2620   syscall_printf ("%d = chdir() cygheap->cwd.posix '%s' native '%S'", res,
2621                   cygheap->cwd.get_posix (), path.get_nt_native_path ());
2622   MALLOC_CHECK;
2623   return res;
2624 }
2625
2626 extern "C" int
2627 fchdir (int fd)
2628 {
2629   int res;
2630   cygheap_fdget cfd (fd);
2631   if (cfd >= 0)
2632     res = chdir (cfd->get_name ());
2633   else
2634     res = -1;
2635
2636   syscall_printf ("%d = fchdir (%d)", res, fd);
2637   return res;
2638 }
2639
2640 /******************** Exported Path Routines *********************/
2641
2642 /* Cover functions to the path conversion routines.
2643    These are exported to the world as cygwin_foo by cygwin.din.  */
2644
2645 #define return_with_errno(x) \
2646   do {\
2647     int err = (x);\
2648     if (!err)\
2649      return 0;\
2650     set_errno (err);\
2651     return -1;\
2652   } while (0)
2653
2654 extern "C" ssize_t
2655 cygwin_conv_path (cygwin_conv_path_t what, const void *from, void *to,
2656                   size_t size)
2657 {
2658   tmp_pathbuf tp;
2659   myfault efault;
2660   if (efault.faulted (EFAULT))
2661     return -1;
2662
2663   path_conv p;
2664   size_t lsiz = 0;
2665   char *buf = NULL;
2666   int error = 0;
2667   bool relative = !!(what & CCP_RELATIVE);
2668   what &= ~CCP_RELATIVE;
2669
2670   switch (what)
2671     {
2672     case CCP_POSIX_TO_WIN_A:
2673       {
2674         p.check ((const char *) from,
2675                  PC_POSIX | PC_SYM_FOLLOW | PC_NO_ACCESS_CHECK | PC_NOWARN
2676                  | (relative ? PC_NOFULL : 0));
2677         if (p.error)
2678           return_with_errno (p.error);
2679         PUNICODE_STRING up = p.get_nt_native_path ();
2680         buf = tp.c_get ();
2681         sys_wcstombs (buf, NT_MAX_PATH, up->Buffer, up->Length / sizeof (WCHAR));
2682         /* Convert native path to standard DOS path. */
2683         if (!strncmp (buf, "\\??\\", 4))
2684           {
2685             buf += 4;
2686             if (buf[1] != ':') /* native UNC path */
2687               *(buf += 2) = '\\';
2688           }
2689         lsiz = strlen (buf) + 1;
2690       }
2691       break;
2692     case CCP_POSIX_TO_WIN_W:
2693       p.check ((const char *) from, PC_POSIX | PC_SYM_FOLLOW
2694                                     | PC_NO_ACCESS_CHECK | PC_NOWARN
2695                                     | (relative ? PC_NOFULL : 0));
2696       if (p.error)
2697         return_with_errno (p.error);
2698       /* Relative Windows paths are always restricted to MAX_PATH chars. */
2699       if (relative && !isabspath (p.get_win32 ())
2700           && sys_mbstowcs (NULL, 0, p.get_win32 ()) > MAX_PATH)
2701         {
2702           /* Recreate as absolute path. */
2703           p.check ((const char *) from, PC_POSIX | PC_SYM_FOLLOW
2704                                         | PC_NO_ACCESS_CHECK | PC_NOWARN);
2705           if (p.error)
2706             return_with_errno (p.error);
2707         }
2708       lsiz = (p.get_wide_win32_path_len () + 1) * sizeof (WCHAR);
2709       break;
2710     case CCP_WIN_A_TO_POSIX:
2711       buf = tp.c_get ();
2712       error = mount_table->conv_to_posix_path ((const char *) from, buf,
2713                                                relative);
2714       if (error)
2715         return_with_errno (error);
2716       lsiz = strlen (buf) + 1;
2717       break;
2718     case CCP_WIN_W_TO_POSIX:
2719       buf = tp.c_get ();
2720       error = mount_table->conv_to_posix_path ((const PWCHAR) from, buf,
2721                                                relative);
2722       if (error)
2723         return_with_errno (error);
2724       lsiz = strlen (buf) + 1;
2725       break;
2726     default:
2727       set_errno (EINVAL);
2728       return -1;
2729     }
2730   if (!size)
2731     return lsiz;
2732   if (size < lsiz)
2733     {
2734       set_errno (ENOSPC);
2735       return -1;
2736     }
2737   switch (what)
2738     {
2739     case CCP_POSIX_TO_WIN_A:
2740     case CCP_WIN_A_TO_POSIX:
2741     case CCP_WIN_W_TO_POSIX:
2742       strcpy ((char *) to, buf);
2743       break;
2744     case CCP_POSIX_TO_WIN_W:
2745       p.get_wide_win32_path ((PWCHAR) to);
2746       break;
2747     }
2748   return 0;
2749 }
2750
2751 extern "C" void *
2752 cygwin_create_path (cygwin_conv_path_t what, const void *from)
2753 {
2754   void *to;
2755   ssize_t size = cygwin_conv_path (what, from, NULL, 0);
2756   if (size <= 0)
2757     return NULL;
2758   if (!(to = malloc (size)))
2759     return NULL;
2760   if (cygwin_conv_path (what, from, to, size) == -1)
2761     return NULL;
2762   return to;
2763 }
2764
2765
2766 extern "C" int
2767 cygwin_conv_to_win32_path (const char *path, char *win32_path)
2768 {
2769   return cygwin_conv_path (CCP_POSIX_TO_WIN_A | CCP_RELATIVE, path, win32_path,
2770                            MAX_PATH);
2771 }
2772
2773 extern "C" int
2774 cygwin_conv_to_full_win32_path (const char *path, char *win32_path)
2775 {
2776   return cygwin_conv_path (CCP_POSIX_TO_WIN_A | CCP_ABSOLUTE, path, win32_path,
2777                            MAX_PATH);
2778 }
2779
2780 /* This is exported to the world as cygwin_foo by cygwin.din.  */
2781
2782 extern "C" int
2783 cygwin_conv_to_posix_path (const char *path, char *posix_path)
2784 {
2785   return cygwin_conv_path (CCP_WIN_A_TO_POSIX | CCP_RELATIVE, path, posix_path,
2786                            MAX_PATH);
2787 }
2788
2789 extern "C" int
2790 cygwin_conv_to_full_posix_path (const char *path, char *posix_path)
2791 {
2792   return cygwin_conv_path (CCP_WIN_A_TO_POSIX | CCP_ABSOLUTE, path, posix_path,
2793                            MAX_PATH);
2794 }
2795
2796 /* The realpath function is supported on some UNIX systems.  */
2797
2798 extern "C" char *
2799 realpath (const char *path, char *resolved)
2800 {
2801   /* Make sure the right errno is returned if path is NULL. */
2802   if (!path)
2803     {
2804       set_errno (EINVAL);
2805       return NULL;
2806     }
2807
2808   /* Guard reading from a potentially invalid path and writing to a
2809      potentially invalid resolved. */
2810   tmp_pathbuf tp;
2811   myfault efault;
2812   if (efault.faulted (EFAULT))
2813     return NULL;
2814
2815   char *tpath;
2816   if (isdrive (path))
2817     {
2818       tpath = tp.c_get ();
2819       mount_table->cygdrive_posix_path (path, tpath, 0);
2820     }
2821   else
2822     tpath = (char *) path;
2823
2824   path_conv real_path (tpath, PC_SYM_FOLLOW | PC_POSIX, stat_suffixes);
2825
2826
2827   /* Linux has this funny non-standard extension.  If "resolved" is NULL,
2828      realpath mallocs the space by itself and returns it to the application.
2829      The application is responsible for calling free() then.  This extension
2830      is backed by POSIX, which allows implementation-defined behaviour if
2831      "resolved" is NULL.  That's good enough for us to do the same here. */
2832
2833   if (!real_path.error && real_path.exists ())
2834     {
2835       if (!resolved)
2836         {
2837           resolved = (char *) malloc (strlen (real_path.normalized_path) + 1);
2838           if (!resolved)
2839             return NULL;
2840         }
2841       strcpy (resolved, real_path.normalized_path);
2842       return resolved;
2843     }
2844
2845   /* FIXME: on error, we are supposed to put the name of the path
2846      component which could not be resolved into RESOLVED.  */
2847   if (resolved)
2848     resolved[0] = '\0';
2849   set_errno (real_path.error ?: ENOENT);
2850   return NULL;
2851 }
2852
2853 /* Return non-zero if path is a POSIX path list.
2854    This is exported to the world as cygwin_foo by cygwin.din.
2855
2856 DOCTOOL-START
2857 <sect1 id="add-func-cygwin-posix-path-list-p">
2858   <para>Rather than use a mode to say what the "proper" path list
2859   format is, we allow any, and give apps the tools they need to
2860   convert between the two.  If a ';' is present in the path list it's
2861   a Win32 path list.  Otherwise, if the first path begins with
2862   [letter]: (in which case it can be the only element since if it
2863   wasn't a ';' would be present) it's a Win32 path list.  Otherwise,
2864   it's a POSIX path list.</para>
2865 </sect1>
2866 DOCTOOL-END
2867   */
2868
2869 extern "C" int
2870 cygwin_posix_path_list_p (const char *path)
2871 {
2872   int posix_p = !(strchr (path, ';') || isdrive (path));
2873   return posix_p;
2874 }
2875
2876 /* These are used for apps that need to convert env vars like PATH back and
2877    forth.  The conversion is a two step process.  First, an upper bound on the
2878    size of the buffer needed is computed.  Then the conversion is done.  This
2879    allows the caller to use alloca if it wants.  */
2880
2881 static int
2882 conv_path_list_buf_size (const char *path_list, bool to_posix)
2883 {
2884   int i, num_elms, max_mount_path_len, size;
2885   const char *p;
2886
2887   path_conv pc(".", PC_POSIX);
2888   /* The theory is that an upper bound is
2889      current_size + (num_elms * max_mount_path_len)  */
2890   /* FIXME: This method is questionable in the long run. */
2891
2892   unsigned nrel;
2893   char delim = to_posix ? ';' : ':';
2894   for (p = path_list, num_elms = nrel = 0; p; num_elms++)
2895     {
2896       if (!isabspath (p))
2897         nrel++;
2898       p = strchr (++p, delim);
2899     }
2900
2901   /* 7: strlen ("//c") + slop, a conservative initial value */
2902   for (max_mount_path_len = sizeof ("/cygdrive/X"), i = 0;
2903        i < mount_table->nmounts; i++)
2904     {
2905       int mount_len = (to_posix
2906                        ? mount_table->mount[i].posix_pathlen
2907                        : mount_table->mount[i].native_pathlen);
2908       if (max_mount_path_len < mount_len)
2909         max_mount_path_len = mount_len;
2910     }
2911
2912   /* 100: slop */
2913   size = strlen (path_list)
2914     + (num_elms * max_mount_path_len)
2915     + (nrel * strlen (to_posix ? pc.normalized_path : pc.get_win32 ()))
2916     + 100;
2917
2918   return size;
2919 }
2920
2921
2922 extern "C" int
2923 cygwin_win32_to_posix_path_list_buf_size (const char *path_list)
2924 {
2925   return conv_path_list_buf_size (path_list, true);
2926 }
2927
2928 extern "C" int
2929 cygwin_posix_to_win32_path_list_buf_size (const char *path_list)
2930 {
2931   return conv_path_list_buf_size (path_list, false);
2932 }
2933
2934 extern "C" ssize_t
2935 env_PATH_to_posix (const void *win32, void *posix, size_t size)
2936 {
2937   return_with_errno (conv_path_list ((const char *) win32, (char *) posix,
2938                                      size, ENV_CVT));
2939 }
2940
2941 extern "C" int
2942 cygwin_win32_to_posix_path_list (const char *win32, char *posix)
2943 {
2944   return_with_errno (conv_path_list (win32, posix, MAX_PATH, 1));
2945 }
2946
2947 extern "C" int
2948 cygwin_posix_to_win32_path_list (const char *posix, char *win32)
2949 {
2950   return_with_errno (conv_path_list (posix, win32, MAX_PATH, 0));
2951 }
2952
2953 extern "C" ssize_t
2954 cygwin_conv_path_list (cygwin_conv_path_t what, const void *from, void *to,
2955                        size_t size)
2956 {
2957   /* FIXME: Path lists are (so far) always retaining relative paths. */
2958   what &= ~CCP_RELATIVE;
2959   switch (what)
2960     {
2961     case CCP_WIN_W_TO_POSIX:
2962     case CCP_POSIX_TO_WIN_W:
2963       /*FIXME*/
2964       api_fatal ("wide char path lists not yet supported");
2965       break;
2966     case CCP_WIN_A_TO_POSIX:
2967     case CCP_POSIX_TO_WIN_A:
2968       if (size == 0)
2969         return conv_path_list_buf_size ((const char *) from,
2970                                         what == CCP_WIN_A_TO_POSIX);
2971       return_with_errno (conv_path_list ((const char *) from, (char *) to,
2972                                          size, what == CCP_WIN_A_TO_POSIX));
2973       break;
2974     default:
2975       break;
2976     }
2977   set_errno (EINVAL);
2978   return -1;
2979 }
2980
2981 /* cygwin_split_path: Split a path into directory and file name parts.
2982    Buffers DIR and FILE are assumed to be big enough.
2983
2984    Examples (path -> `dir' / `file'):
2985    / -> `/' / `'
2986    "" -> `.' / `'
2987    . -> `.' / `.' (FIXME: should this be `.' / `'?)
2988    .. -> `.' / `..' (FIXME: should this be `..' / `'?)
2989    foo -> `.' / `foo'
2990    foo/bar -> `foo' / `bar'
2991    foo/bar/ -> `foo' / `bar'
2992    /foo -> `/' / `foo'
2993    /foo/bar -> `/foo' / `bar'
2994    c: -> `c:/' / `'
2995    c:/ -> `c:/' / `'
2996    c:foo -> `c:/' / `foo'
2997    c:/foo -> `c:/' / `foo'
2998  */
2999
3000 extern "C" void
3001 cygwin_split_path (const char *path, char *dir, char *file)
3002 {
3003   int dir_started_p = 0;
3004
3005   /* Deal with drives.
3006      Remember that c:foo <==> c:/foo.  */
3007   if (isdrive (path))
3008     {
3009       *dir++ = *path++;
3010       *dir++ = *path++;
3011       *dir++ = '/';
3012       if (!*path)
3013         {
3014           *dir = 0;
3015           *file = 0;
3016           return;
3017         }
3018       if (isdirsep (*path))
3019         ++path;
3020       dir_started_p = 1;
3021     }
3022
3023   /* Determine if there are trailing slashes and "delete" them if present.
3024      We pretend as if they don't exist.  */
3025   const char *end = path + strlen (path);
3026   /* path + 1: keep leading slash.  */
3027   while (end > path + 1 && isdirsep (end[-1]))
3028     --end;
3029
3030   /* At this point, END points to one beyond the last character
3031      (with trailing slashes "deleted").  */
3032
3033   /* Point LAST_SLASH at the last slash (duh...).  */
3034   const char *last_slash;
3035   for (last_slash = end - 1; last_slash >= path; --last_slash)
3036     if (isdirsep (*last_slash))
3037       break;
3038
3039   if (last_slash == path)
3040     {
3041       *dir++ = '/';
3042       *dir = 0;
3043     }
3044   else if (last_slash > path)
3045     {
3046       memcpy (dir, path, last_slash - path);
3047       dir[last_slash - path] = 0;
3048     }
3049   else
3050     {
3051       if (dir_started_p)
3052         ; /* nothing to do */
3053       else
3054         *dir++ = '.';
3055       *dir = 0;
3056     }
3057
3058   memcpy (file, last_slash + 1, end - last_slash - 1);
3059   file[end - last_slash - 1] = 0;
3060 }
3061
3062 /*****************************************************************************/
3063
3064 static inline PRTL_USER_PROCESS_PARAMETERS
3065 get_user_proc_parms ()
3066 {
3067   return NtCurrentTeb ()->Peb->ProcessParameters;
3068 }
3069
3070 /* Initialize cygcwd 'muto' for serializing access to cwd info. */
3071 void
3072 cwdstuff::init ()
3073 {
3074   cwd_lock.init ("cwd_lock");
3075   /* Initially re-open the cwd to allow POSIX semantics. */
3076   set (NULL, NULL, true);
3077 }
3078
3079 /* Chdir and fill out the elements of a cwdstuff struct. */
3080 int
3081 cwdstuff::set (PUNICODE_STRING nat_cwd, const char *posix_cwd, bool doit)
3082 {
3083   int res = 0;
3084   UNICODE_STRING upath;
3085   size_t len = 0;
3086
3087   cwd_lock.acquire ();
3088
3089   if (nat_cwd)
3090     {
3091       upath = *nat_cwd;
3092       if (upath.Buffer[0] == L'/') /* Virtual path.  Never use in PEB. */
3093         doit = false;
3094       else
3095         {
3096           len = upath.Length / sizeof (WCHAR) - 4;
3097           if (RtlEqualUnicodePathPrefix (&upath, &ro_u_uncp, TRUE))
3098             len -= 2;
3099         }
3100     }
3101
3102   if (doit)
3103     {
3104       /* We utilize the user parameter block.  The directory is
3105          stored manually there.  Why the hassle?
3106
3107          - SetCurrentDirectory fails for directories with strict
3108            permissions even for processes with the SE_BACKUP_NAME
3109            privilege enabled.  The reason is apparently that
3110            SetCurrentDirectory calls NtOpenFile without the
3111            FILE_OPEN_FOR_BACKUP_INTENT flag set.
3112
3113          - Unlinking a cwd fails because SetCurrentDirectory seems to
3114            open directories so that deleting the directory is disallowed.
3115            The below code opens with *all* sharing flags set. */
3116       HANDLE h;
3117       NTSTATUS status;
3118       IO_STATUS_BLOCK io;
3119       OBJECT_ATTRIBUTES attr;
3120       PHANDLE phdl;
3121
3122       RtlAcquirePebLock ();
3123       phdl = &get_user_proc_parms ()->CurrentDirectoryHandle;
3124       if (!nat_cwd) /* On init, just reopen CWD with desired access flags. */
3125         RtlInitUnicodeString (&upath, L"");
3126       /* This is for Win32 apps only.  No case sensitivity here... */
3127       InitializeObjectAttributes (&attr, &upath,
3128                                   OBJ_CASE_INSENSITIVE | OBJ_INHERIT,
3129                                   nat_cwd ? NULL : *phdl, NULL);
3130       status = NtOpenFile (&h, SYNCHRONIZE | FILE_TRAVERSE, &attr, &io,
3131                            FILE_SHARE_VALID_FLAGS,
3132                            FILE_DIRECTORY_FILE
3133                            | FILE_SYNCHRONOUS_IO_NONALERT
3134                            | FILE_OPEN_FOR_BACKUP_INTENT);
3135       if (!NT_SUCCESS (status))
3136         {
3137           RtlReleasePebLock ();
3138           __seterrno_from_nt_status (status);
3139           res = -1;
3140           goto out;
3141         }
3142       /* Workaround a problem in Vista/Longhorn which fails in subsequent
3143          calls to CreateFile with ERROR_INVALID_HANDLE if the handle in
3144          CurrentDirectoryHandle changes without calling SetCurrentDirectory,
3145          and the filename given to CreateFile is a relative path.  It looks
3146          like Vista stores a copy of the CWD handle in some other undocumented
3147          place.  The NtClose/DuplicateHandle reuses the original handle for
3148          the copy of the new handle and the next CreateFile works.
3149          Note that this is not thread-safe (yet?) */
3150       NtClose (*phdl);
3151       if (DuplicateHandle (GetCurrentProcess (), h, GetCurrentProcess (), phdl,
3152                            0, TRUE, DUPLICATE_SAME_ACCESS))
3153         NtClose (h);
3154       else
3155         *phdl = h;
3156       dir = *phdl;
3157
3158       /* No need to set path on init. */
3159       if (nat_cwd
3160           /* TODO:
3161              Check the length of the new CWD.  Windows can only handle
3162              CWDs of up to MAX_PATH length, including a trailing backslash.
3163              If the path is longer, it's not an error condition for Cygwin,
3164              so we don't fail.  Windows on the other hand has a problem now.
3165              For now, we just don't store the path in the PEB and proceed as
3166              usual. */
3167           && len <= MAX_PATH - (nat_cwd->Buffer[len - 1] == L'\\' ? 1 : 2))
3168         {
3169           /* Convert to a Win32 path. */
3170           upath.Buffer += upath.Length / sizeof (WCHAR) - len;
3171           if (upath.Buffer[1] == L'\\') /* UNC path */
3172             upath.Buffer[0] = L'\\';
3173           upath.Length = len * sizeof (WCHAR);
3174           /* Append backslash if necessary. */
3175           if (upath.Buffer[len - 1] != L'\\')
3176             {
3177               upath.Buffer[len] = L'\\';
3178               upath.Length += sizeof (WCHAR);
3179             }
3180           RtlCopyUnicodeString (&get_user_proc_parms ()->CurrentDirectoryName,
3181                                 &upath);
3182         }
3183
3184       RtlReleasePebLock ();
3185     }
3186
3187   if (nat_cwd || !win32.Buffer)
3188     {
3189       /* If there is no win32 path */
3190       if (!nat_cwd)
3191         {
3192           PUNICODE_STRING pdir;
3193
3194           RtlAcquirePebLock ();
3195           pdir = &get_user_proc_parms ()->CurrentDirectoryName;
3196           RtlInitEmptyUnicodeString (&win32,
3197                                      (PWCHAR) crealloc_abort (win32.Buffer,
3198                                                               pdir->Length + 2),
3199                                      pdir->Length + 2);
3200           RtlCopyUnicodeString (&win32, pdir);
3201           RtlReleasePebLock ();
3202
3203           PWSTR eoBuffer = win32.Buffer + (win32.Length / sizeof (WCHAR));
3204           /* Remove trailing slash if one exists.  FIXME: Is there a better way to
3205              do this? */
3206           if ((eoBuffer - win32.Buffer) > 3 && eoBuffer[-1] == L'\\')
3207             win32.Length -= sizeof (WCHAR);
3208
3209           posix_cwd = NULL;
3210         }
3211       else
3212         {
3213           bool unc = false;
3214
3215           if (upath.Buffer[0] == L'/') /* Virtual path, don't mangle. */
3216             ;
3217           else if (!doit)
3218             {
3219               /* Convert to a Win32 path. */
3220               upath.Buffer += upath.Length / sizeof (WCHAR) - len;
3221               if (upath.Buffer[1] == L'\\') /* UNC path */
3222                 unc = true;
3223               upath.Length = len * sizeof (WCHAR);
3224             }
3225           else
3226             {
3227               PWSTR eoBuffer = upath.Buffer + (upath.Length / sizeof (WCHAR));
3228               /* Remove trailing slash if one exists.  FIXME: Is there a better way to
3229                  do this? */
3230               if ((eoBuffer - upath.Buffer) > 3 && eoBuffer[-1] == L'\\')
3231                 upath.Length -= sizeof (WCHAR);
3232             }
3233           RtlInitEmptyUnicodeString (&win32,
3234                                      (PWCHAR) crealloc_abort (win32.Buffer,
3235                                                               upath.Length + 2),
3236                                      upath.Length + 2);
3237           RtlCopyUnicodeString (&win32, &upath);
3238           if (unc)
3239             win32.Buffer[0] = L'\\';
3240         }
3241       /* Make sure it's NUL-terminated. */
3242       win32.Buffer[win32.Length / sizeof (WCHAR)] = L'\0';
3243       if (!doit)                         /* Virtual path */
3244         drive_length = 0;
3245       else if (win32.Buffer[1] == L':')  /* X: */
3246         drive_length = 2;
3247       else if (win32.Buffer[1] == L'\\') /* UNC path */
3248         {
3249           PWCHAR ptr = wcschr (win32.Buffer + 2, L'\\');
3250           if (ptr)
3251             ptr = wcschr (ptr + 1, L'\\');
3252           if (ptr)
3253             drive_length = ptr - win32.Buffer;
3254           else
3255             drive_length = win32.Length / sizeof (WCHAR);
3256         }
3257       else                               /* Shouldn't happen */
3258         drive_length = 0;
3259
3260       tmp_pathbuf tp;
3261       if (!posix_cwd)
3262         {
3263           posix_cwd = (const char *) tp.c_get ();
3264           mount_table->conv_to_posix_path (win32.Buffer, (char *) posix_cwd, 0);
3265         }
3266       if (posix)
3267         posix[0] = '\0';
3268     }
3269
3270 out:
3271   cwd_lock.release ();
3272   return res;
3273 }
3274
3275 /* Copy the value for either the posix or the win32 cwd into a buffer. */
3276 char *
3277 cwdstuff::get_posix ()
3278 {
3279   if (!posix || !*posix)
3280     {
3281       tmp_pathbuf tp;
3282
3283       char *tocopy = tp.c_get ();
3284       mount_table->conv_to_posix_path (win32.Buffer, tocopy, 0);
3285       posix = (char *) crealloc_abort (posix, strlen (tocopy) + 1);
3286       stpcpy (posix, tocopy);
3287     }
3288   return posix;
3289 }
3290
3291 char *
3292 cwdstuff::get (char *buf, int need_posix, int with_chroot, unsigned ulen)
3293 {
3294   MALLOC_CHECK;
3295
3296   tmp_pathbuf tp;
3297   if (ulen)
3298     /* nothing */;
3299   else if (buf == NULL)
3300     ulen = (unsigned) -1;
3301   else
3302     {
3303       set_errno (EINVAL);
3304       goto out;
3305     }
3306
3307   cwd_lock.acquire ();
3308
3309   char *tocopy;
3310   if (!need_posix)
3311     {
3312       tocopy = tp.c_get ();
3313       sys_wcstombs (tocopy, NT_MAX_PATH, win32.Buffer,
3314                     win32.Length / sizeof (WCHAR));
3315     }
3316   else if (!posix || !*posix)
3317     {
3318       tocopy = tp.c_get ();
3319       mount_table->conv_to_posix_path (win32.Buffer, tocopy, 0);
3320       posix = (char *) crealloc_abort (posix, strlen (tocopy) + 1);
3321       stpcpy (posix, tocopy);
3322     }
3323   else
3324     tocopy = posix;
3325
3326   debug_printf ("posix %s", posix);
3327   if (strlen (tocopy) >= ulen)
3328     {
3329       set_errno (ERANGE);
3330       buf = NULL;
3331     }
3332   else
3333     {
3334       if (!buf)
3335         buf = (char *) malloc (strlen (tocopy) + 1);
3336       strcpy (buf, tocopy);
3337       if (!buf[0])      /* Should only happen when chroot */
3338         strcpy (buf, "/");
3339     }
3340
3341   cwd_lock.release ();
3342
3343 out:
3344   syscall_printf ("(%s) = cwdstuff::get (%p, %d, %d, %d), errno %d",
3345                   buf, buf, ulen, need_posix, with_chroot, errno);
3346   MALLOC_CHECK;
3347   return buf;
3348 }
3349
3350 int etc::curr_ix = 0;
3351 /* Note that the first elements of the below arrays are unused */
3352 bool etc::change_possible[MAX_ETC_FILES + 1];
3353 OBJECT_ATTRIBUTES etc::fn[MAX_ETC_FILES + 1];
3354 LARGE_INTEGER etc::last_modified[MAX_ETC_FILES + 1];
3355
3356 int
3357 etc::init (int n, POBJECT_ATTRIBUTES attr)
3358 {
3359   if (n > 0)
3360     /* ok */;
3361   else if (++curr_ix <= MAX_ETC_FILES)
3362     n = curr_ix;
3363   else
3364     api_fatal ("internal error");
3365
3366   fn[n] = *attr;
3367   change_possible[n] = false;
3368   test_file_change (n);
3369   paranoid_printf ("fn[%d] %S, curr_ix %d", n, fn[n].ObjectName, curr_ix);
3370   return n;
3371 }
3372
3373 bool
3374 etc::test_file_change (int n)
3375 {
3376   NTSTATUS status;
3377   FILE_NETWORK_OPEN_INFORMATION fnoi;
3378   bool res;
3379
3380   status = NtQueryFullAttributesFile (&fn[n], &fnoi);
3381   if (!NT_SUCCESS (status))
3382     {
3383       res = true;
3384       memset (last_modified + n, 0, sizeof (last_modified[n]));
3385       debug_printf ("NtQueryFullAttributesFile (%S) failed, %p",
3386                     fn[n].ObjectName, status);
3387     }
3388   else
3389     {
3390       res = CompareFileTime ((FILETIME *) &fnoi.LastWriteTime,
3391                              (FILETIME *) last_modified + n) > 0;
3392       last_modified[n].QuadPart = fnoi.LastWriteTime.QuadPart;
3393     }
3394
3395   paranoid_printf ("fn[%d] %S res %d", n, fn[n].ObjectName, res);
3396   return res;
3397 }
3398
3399 bool
3400 etc::dir_changed (int n)
3401 {
3402   if (!change_possible[n])
3403     {
3404       static HANDLE changed_h NO_COPY;
3405       NTSTATUS status;
3406       IO_STATUS_BLOCK io;
3407
3408       if (!changed_h)
3409         {
3410           OBJECT_ATTRIBUTES attr;
3411
3412           path_conv dir ("/etc");
3413           status = NtOpenFile (&changed_h, SYNCHRONIZE | FILE_LIST_DIRECTORY,
3414                                dir.get_object_attr (attr, sec_none_nih), &io,
3415                                FILE_SHARE_VALID_FLAGS, FILE_DIRECTORY_FILE);
3416           if (!NT_SUCCESS (status))
3417             {
3418 #ifdef DEBUGGING
3419               system_printf ("NtOpenFile (%S) failed, %p",
3420                              dir.get_nt_native_path (), status);
3421 #endif
3422               changed_h = INVALID_HANDLE_VALUE;
3423             }
3424           else
3425             {
3426               status = NtNotifyChangeDirectoryFile (changed_h, NULL, NULL,
3427                                                 NULL, &io, NULL, 0,
3428                                                 FILE_NOTIFY_CHANGE_LAST_WRITE
3429                                                 | FILE_NOTIFY_CHANGE_FILE_NAME,
3430                                                 FALSE);
3431               if (!NT_SUCCESS (status))
3432                 {
3433 #ifdef DEBUGGING
3434                   system_printf ("NtNotifyChangeDirectoryFile (1) failed, %p",
3435                                  status);
3436 #endif
3437                   NtClose (changed_h);
3438                   changed_h = INVALID_HANDLE_VALUE;
3439                 }
3440             }
3441           memset (change_possible, true, sizeof (change_possible));
3442         }
3443
3444       if (changed_h == INVALID_HANDLE_VALUE)
3445         change_possible[n] = true;
3446       else if (WaitForSingleObject (changed_h, 0) == WAIT_OBJECT_0)
3447         {
3448           status = NtNotifyChangeDirectoryFile (changed_h, NULL, NULL,
3449                                                 NULL, &io, NULL, 0,
3450                                                 FILE_NOTIFY_CHANGE_LAST_WRITE
3451                                                 | FILE_NOTIFY_CHANGE_FILE_NAME,
3452                                                 FALSE);
3453           if (!NT_SUCCESS (status))
3454             {
3455 #ifdef DEBUGGING
3456               system_printf ("NtNotifyChangeDirectoryFile (2) failed, %p",
3457                              status);
3458 #endif
3459               NtClose (changed_h);
3460               changed_h = INVALID_HANDLE_VALUE;
3461             }
3462           memset (change_possible, true, sizeof change_possible);
3463         }
3464     }
3465
3466   paranoid_printf ("fn[%d] %S change_possible %d",
3467                    n, fn[n].ObjectName, change_possible[n]);
3468   return change_possible[n];
3469 }
3470
3471 bool
3472 etc::file_changed (int n)
3473 {
3474   bool res = false;
3475   if (dir_changed (n) && test_file_change (n))
3476     res = true;
3477   change_possible[n] = false;   /* Change is no longer possible */
3478   paranoid_printf ("fn[%d] %S res %d", n, fn[n].ObjectName, res);
3479   return res;
3480 }
3481
3482 /* No need to be reentrant or thread-safe according to SUSv3.
3483    / and \\ are treated equally.  Leading drive specifiers are
3484    kept intact as far as it makes sense.  Everything else is
3485    POSIX compatible. */
3486 extern "C" char *
3487 basename (char *path)
3488 {
3489   static char buf[4];
3490   char *c, *d, *bs = path;
3491
3492   if (!path || !*path)
3493     return strcpy (buf, ".");
3494   if (isalpha (path[0]) && path[1] == ':')
3495     bs += 2;
3496   else if (strspn (path, "/\\") > 1)
3497     ++bs;
3498   c = strrchr (bs, '/');
3499   if ((d = strrchr (c ?: bs, '\\')) > c)
3500     c = d;
3501   if (c)
3502     {
3503       /* Trailing (back)slashes are eliminated. */
3504       while (c && c > bs && c[1] == '\0')
3505         {
3506           *c = '\0';
3507           c = strrchr (bs, '/');
3508           if ((d = strrchr (c ?: bs, '\\')) > c)
3509             c = d;
3510         }
3511       if (c && (c > bs || c[1]))
3512         return c + 1;
3513     }
3514   else if (!bs[0])
3515     {
3516       stpncpy (buf, path, bs - path);
3517       stpcpy (buf + (bs - path), ".");
3518       return buf;
3519     }
3520   return path;
3521 }
3522
3523 /* No need to be reentrant or thread-safe according to SUSv3.
3524    / and \\ are treated equally.  Leading drive specifiers and
3525    leading double (back)slashes are kept intact as far as it
3526    makes sense.  Everything else is POSIX compatible. */
3527 extern "C" char *
3528 dirname (char *path)
3529 {
3530   static char buf[4];
3531   char *c, *d, *bs = path;
3532
3533   if (!path || !*path)
3534     return strcpy (buf, ".");
3535   if (isalpha (path[0]) && path[1] == ':')
3536     bs += 2;
3537   else if (strspn (path, "/\\") > 1)
3538     ++bs;
3539   c = strrchr (bs, '/');
3540   if ((d = strrchr (c ?: bs, '\\')) > c)
3541     c = d;
3542   if (c)
3543     {
3544       /* Trailing (back)slashes are eliminated. */
3545       while (c && c > bs && c[1] == '\0')
3546         {
3547           *c = '\0';
3548           c = strrchr (bs, '/');
3549           if ((d = strrchr (c ?: bs, '\\')) > c)
3550             c = d;
3551         }
3552       if (!c)
3553         strcpy (bs, ".");
3554       else if (c > bs)
3555         {
3556           /* More trailing (back)slashes are eliminated. */
3557           while (c > bs && (*c == '/' || *c == '\\'))
3558             *c-- = '\0';
3559         }
3560       else
3561         c[1] = '\0';
3562     }
3563   else
3564     {
3565       stpncpy (buf, path, bs - path);
3566       stpcpy (buf + (bs - path), ".");
3567       return buf;
3568     }
3569   return path;
3570 }