OSDN Git Service

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