]> sourceware.org Git - newlib-cygwin.git/blame - winsup/cygwin/path.cc
Cygwin: add flag to indicate reparse points unknown to WinAPI
[newlib-cygwin.git] / winsup / cygwin / path.cc
CommitLineData
025c1fac 1/* path.cc: path support.
1fd5e000 2
81adfe28 3 This file is part of Cygwin.
1fd5e000 4
81adfe28
CF
5 This software is a copyrighted work licensed under the terms of the
6 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
7 details. */
1fd5e000 8
81adfe28
CF
9 /* This module's job is to
10 - convert between POSIX and Win32 style filenames,
11 - support the `mount' functionality,
12 - support symlinks for files and directories
1fd5e000 13
81adfe28 14 Pathnames are handled as follows:
1fd5e000 15
81adfe28
CF
16 - A \ or : in a path denotes a pure windows spec.
17 - Paths beginning with // (or \\) are not translated (i.e. looked
18 up in the mount table) and are assumed to be UNC path names.
1fd5e000 19
81adfe28
CF
20 The goal in the above set of rules is to allow both POSIX and Win32
21 flavors of pathnames without either interfering. The rules are
22 intended to be as close to a superset of both as possible.
1fd5e000 23
81adfe28
CF
24 Note that you can have more than one path to a file. The mount
25 table is always prefered when translating Win32 paths to POSIX
26 paths. Win32 paths in mount table entries may be UNC paths or
27 standard Win32 paths starting with <drive-letter>:
1fd5e000 28
81adfe28
CF
29 Text vs Binary issues are not considered here in path style
30 decisions, although the appropriate flags are retrieved and
31 stored in various structures.
1fd5e000 32
81adfe28
CF
33 Removing mounted filesystem support would simplify things greatly,
34 but having it gives us a mechanism of treating disk that lives on a
35 UNIX machine as having UNIX semantics [it allows one to edit a text
36 file on that disk and not have cr's magically appear and perhaps
37 break apps running on UNIX boxes]. It also useful to be able to
38 layout a hierarchy without changing the underlying directories.
1fd5e000 39
81adfe28
CF
40 The semantics of mounting file systems is not intended to precisely
41 follow normal UNIX systems.
1fd5e000 42
81adfe28
CF
43 Each DOS drive is defined to have a current directory. Supporting
44 this would complicate things so for now things are defined so that
45 c: means c:\.
46 */
1fd5e000 47
c9b37162
YS
48/* This file includes both the XPG and GNU basename functions, with the
49 former exported as "basename" for ABI compatibility but the latter
50 declared as such for source compatibility with glibc. This tells
51 <string.h> not to declare the GNU variant in order to prevent a conflicting
52 declaration error with the XPG variant implemented herein. */
53#define basename basename
4c8d72de 54#include "winsup.h"
28cf818c
CV
55#include <w32api/winioctl.h>
56#include <w32api/shlobj.h>
75543537 57#include <sys/param.h>
f0338f54 58#include <sys/cygwin.h>
28cf818c 59#include <wctype.h>
7d00a5e3 60#include <assert.h>
9e2baf8d 61#include "cygerrno.h"
bccd5e0d 62#include "path.h"
7ac61736 63#include "fhandler.h"
0381fec6 64#include "dtable.h"
b0e82b74 65#include "cygheap.h"
29ac7f89 66#include "shared_info.h"
752b16ce 67#include "tls_pbuf.h"
6f1d4862 68#include "environ.h"
c9b37162 69#undef basename
1fd5e000 70
66a83f3e
CF
71suffix_info stat_suffixes[] =
72{
73 suffix_info ("", 1),
74 suffix_info (".exe", 1),
75 suffix_info (NULL)
76};
77
1870c688
CV
78struct symlink_info
79{
80 char contents[SYMLINK_MAX + 1];
81 char *ext_here;
82 int extn;
c1023ee3
CV
83 unsigned path_flags;
84 unsigned mount_flags;
85 unsigned pc_flags; /* Relevant pathconv_arg flags from path_conv caller */
1870c688
CV
86 DWORD fileattr;
87 int issymlink;
88 bool ext_tacked_on;
89 int error;
1870c688
CV
90 bool isdevice;
91 _major_t major;
92 _minor_t minor;
ea58e20c 93 __mode_t mode;
5a0d1edb
CV
94 int check (char *path, const suffix_info *suffixes, fs_info &fs,
95 path_conv_handle &conv_hdl);
1870c688
CV
96 int set (char *path);
97 bool parse_device (const char *);
1870c688
CV
98 int check_sysfile (HANDLE h);
99 int check_shortcut (HANDLE h);
855ea3fd 100 int check_reparse_point (HANDLE h, bool remote);
1870c688
CV
101 int check_nfs_symlink (HANDLE h);
102 int posixify (char *srcbuf);
103 bool set_error (int);
104};
105
106muto NO_COPY cwdstuff::cwd_lock;
107
1870c688 108static const GUID GUID_shortcut
7b9e380f 109 = { 0x00021401L, 0, 0, {0xc0, 0, 0, 0, 0, 0, 0, 0x46}};
1870c688 110
c4441d46
CF
111enum
112{
1870c688
CV
113 WSH_FLAG_IDLIST = 0x01, /* Contains an ITEMIDLIST. */
114 WSH_FLAG_FILE = 0x02, /* Contains a file locator element. */
115 WSH_FLAG_DESC = 0x04, /* Contains a description. */
116 WSH_FLAG_RELPATH = 0x08, /* Contains a relative path. */
117 WSH_FLAG_WD = 0x10, /* Contains a working dir. */
118 WSH_FLAG_CMDLINE = 0x20, /* Contains command line args. */
119 WSH_FLAG_ICON = 0x40 /* Contains a custom icon. */
120};
121
122struct win_shortcut_hdr
c4441d46
CF
123{
124 DWORD size; /* Header size in bytes. Must contain 0x4c. */
125 GUID magic; /* GUID of shortcut files. */
126 DWORD flags; /* Content flags. See above. */
127
128 /* The next fields from attr to icon_no are always set to 0 in Cygwin
129 and U/Win shortcuts. */
130 DWORD attr; /* Target file attributes. */
131 FILETIME ctime; /* These filetime items are never touched by the */
132 FILETIME mtime; /* system, apparently. Values don't matter. */
133 FILETIME atime;
134 DWORD filesize; /* Target filesize. */
135 DWORD icon_no; /* Icon number. */
136
137 DWORD run; /* Values defined in winuser.h. Use SW_NORMAL. */
138 DWORD hotkey; /* Hotkey value. Set to 0. */
139 DWORD dummy[2]; /* Future extension probably. Always 0. */
140};
81adfe28 141
1870c688
CV
142/* Return non-zero if PATH1 is a prefix of PATH2.
143 Both are assumed to be of the same path style and / vs \ usage.
144 Neither may be "".
145 LEN1 = strlen (PATH1). It's passed because often it's already known.
146
147 Examples:
148 /foo/ is a prefix of /foo <-- may seem odd, but desired
149 /foo is a prefix of /foo/
150 / is a prefix of /foo/bar
151 / is not a prefix of foo/bar
152 foo/ is a prefix foo/bar
153 /foo is not a prefix of /foobar
154*/
155
156int
e4b57503
CV
157path_prefix_p (const char *path1, const char *path2, int len1,
158 bool caseinsensitive)
1870c688
CV
159{
160 /* Handle case where PATH1 has trailing '/' and when it doesn't. */
161 if (len1 > 0 && isdirsep (path1[len1 - 1]))
162 len1--;
163
164 if (len1 == 0)
165 return isdirsep (path2[0]) && !isdirsep (path2[1]);
166
167 if (isdirsep (path2[len1]) || path2[len1] == 0 || path1[len1 - 1] == ':')
54a83cc6
CV
168 return caseinsensitive ? strncasematch (path1, path2, len1)
169 : !strncmp (path1, path2, len1);
1870c688
CV
170
171 return 0;
172}
173
174/* Return non-zero if paths match in first len chars.
175 Check is dependent of the case sensitivity setting. */
176int
e4b57503 177pathnmatch (const char *path1, const char *path2, int len, bool caseinsensitive)
1870c688 178{
e4b57503
CV
179 return caseinsensitive
180 ? strncasematch (path1, path2, len) : !strncmp (path1, path2, len);
1870c688
CV
181}
182
183/* Return non-zero if paths match. Check is dependent of the case
184 sensitivity setting. */
185int
e4b57503 186pathmatch (const char *path1, const char *path2, bool caseinsensitive)
1870c688 187{
e4b57503
CV
188 return caseinsensitive
189 ? strcasematch (path1, path2) : !strcmp (path1, path2);
1870c688
CV
190}
191
192/* TODO: This function is used in mkdir and rmdir to generate correct
193 error messages in case of paths ending in /. or /.. components.
194 Right now, normalize_posix_path will just normalize
195 those components away, which changes the semantics. */
196bool
197has_dot_last_component (const char *dir, bool test_dot_dot)
198{
199 /* SUSv3: . and .. are not allowed as last components in various system
200 calls. Don't test for backslash path separator since that's a Win32
201 path following Win32 rules. */
284c5ea0
CF
202 const char *last_comp = strchr (dir, '\0');
203
204 if (last_comp == dir)
205 return false; /* Empty string. Probably shouldn't happen here? */
206
207 /* Detect run of trailing slashes */
208 while (last_comp > dir && *--last_comp == '/')
209 continue;
210
211 /* Detect just a run of slashes or a path that does not end with a slash. */
212 if (*last_comp != '.')
213 return false;
214
215 /* We know we have a trailing dot here. Check that it really is a standalone "."
216 path component by checking that it is at the beginning of the string or is
217 preceded by a "/" */
218 if (last_comp == dir || *--last_comp == '/')
219 return true;
220
221 /* If we're not checking for '..' we're done. Ditto if we're now pointing to
222 a non-dot. */
223 if (!test_dot_dot || *last_comp != '.')
224 return false; /* either not testing for .. or this was not '..' */
225
226 /* Repeat previous test for standalone or path component. */
227 return last_comp == dir || last_comp[-1] == '/';
1870c688
CV
228}
229
230/* Normalize a POSIX path.
231 All duplicate /'s, except for 2 leading /'s, are deleted.
232 The result is 0 for success, or an errno error value. */
233
234int
235normalize_posix_path (const char *src, char *dst, char *&tail)
236{
237 const char *in_src = src;
238 char *dst_start = dst;
0b8722c2 239 bool check_parent = false;
1870c688
CV
240 syscall_printf ("src %s", src);
241
242 if ((isdrive (src) && isdirsep (src[2])) || *src == '\\')
243 goto win32_path;
244
245 tail = dst;
246 if (!isslash (src[0]))
247 {
248 if (!cygheap->cwd.get (dst))
249 return get_errno ();
250 tail = strchr (tail, '\0');
251 if (isslash (dst[0]) && isslash (dst[1]))
252 ++dst_start;
253 if (*src == '.')
254 {
255 if (tail == dst_start + 1 && *dst_start == '/')
256 tail--;
257 goto sawdot;
258 }
259 if (tail > dst && !isslash (tail[-1]))
260 *tail++ = '/';
261 }
262 /* Two leading /'s? If so, preserve them. */
263 else if (isslash (src[1]) && !isslash (src[2]))
264 {
265 *tail++ = *src++;
266 ++dst_start;
267 }
b98ebf54 268
1870c688
CV
269 while (*src)
270 {
271 if (*src == '\\')
272 goto win32_path;
273 /* Strip runs of /'s. */
274 if (!isslash (*src))
275 *tail++ = *src++;
276 else
277 {
0b8722c2 278 check_parent = true;
1870c688
CV
279 while (*++src)
280 {
281 if (isslash (*src))
282 continue;
81adfe28 283
1870c688
CV
284 if (*src != '.')
285 break;
81adfe28 286
1870c688
CV
287 sawdot:
288 if (src[1] != '.')
289 {
290 if (!src[1])
291 {
292 *tail++ = '/';
293 goto done;
294 }
295 if (!isslash (src[1]))
296 break;
297 }
298 else if (src[2] && !isslash (src[2]))
299 break;
300 else
301 {
0b8722c2
CV
302 /* According to POSIX semantics all elements of path must
303 exist. To follow it, we must validate our path before
304 removing the trailing component. Check_parent is needed
305 for performance optimization, in order not to verify paths
306 which are already verified. For example this prevents
307 double check in case of foo/bar/../.. */
308 if (check_parent)
309 {
fd5f6eb1 310 if (tail > dst_start) /* Don't check for / or // dir. */
d2ef2331 311 {
fd5f6eb1
CV
312 *tail = 0;
313 debug_printf ("checking %s before '..'", dst);
7ae3e6b3
CV
314 /* In conjunction with native and NFS symlinks,
315 this call can result in a recursion which eats
316 up our tmp_pathbuf buffers. This in turn results
317 in a api_fatal call. To avoid that, we're
318 checking our remaining buffers and return an
319 error code instead. Note that this only happens
320 if the path contains 15 or more relative native/NFS
321 symlinks with a ".." in the target path. */
322 tmp_pathbuf tp;
323 if (!tp.check_usage (4, 3))
324 return ELOOP;
325 path_conv head (dst, PC_SYM_FOLLOW | PC_POSIX);
fd5f6eb1
CV
326 if (!head.isdir())
327 return ENOENT;
7ae3e6b3
CV
328 /* At this point, dst is a normalized path. If the
329 normalized path created by path_conv does not
330 match the normalized path we're just testing, then
331 the path in dst contains native symlinks. If we
332 just plunge along, removing the previous path
333 component, we may end up removing a symlink from
334 the path and the resulting path will be invalid.
335 So we replace dst with what we found in head
336 instead. All the work replacing symlinks has been
337 done in that path anyway, so why repeat it? */
d2bd82aa 338 tail = stpcpy (dst, head.get_posix ());
fd5f6eb1 339 }
0b8722c2
CV
340 check_parent = false;
341 }
1870c688
CV
342 while (tail > dst_start && !isslash (*--tail))
343 continue;
344 src++;
345 }
346 }
81adfe28 347
b861c0a3 348 *tail++ = '/';
1870c688
CV
349 }
350 if ((tail - dst) >= NT_MAX_PATH)
b98ebf54 351 {
1870c688
CV
352 debug_printf ("ENAMETOOLONG = normalize_posix_path (%s)", src);
353 return ENAMETOOLONG;
b98ebf54 354 }
1870c688 355 }
81adfe28 356
1870c688
CV
357done:
358 *tail = '\0';
81adfe28 359
1870c688
CV
360 debug_printf ("%s = normalize_posix_path (%s)", dst, in_src);
361 return 0;
81adfe28 362
1870c688
CV
363win32_path:
364 int err = normalize_win32_path (in_src, dst, tail);
365 if (!err)
366 for (char *p = dst; (p = strchr (p, '\\')); p++)
367 *p = '/';
368 return err ?: -1;
369}
81adfe28 370
1870c688
CV
371inline void
372path_conv::add_ext_from_sym (symlink_info &sym)
373{
374 if (sym.ext_here && *sym.ext_here)
375 {
d2bd82aa 376 suffix = path + sym.extn;
1870c688 377 if (sym.ext_tacked_on)
d2bd82aa 378 strcpy ((char *) suffix, sym.ext_here);
1870c688
CV
379 }
380}
381
6e75c72b 382static void __reg2 mkrelpath (char *dst, bool caseinsensitive);
fafbf755 383
1f36328e 384static void __reg2
e4b57503 385mkrelpath (char *path, bool caseinsensitive)
1870c688
CV
386{
387 tmp_pathbuf tp;
388 char *cwd_win32 = tp.c_get ();
389 if (!cygheap->cwd.get (cwd_win32, 0))
390 return;
391
392 unsigned cwdlen = strlen (cwd_win32);
e4b57503 393 if (!path_prefix_p (cwd_win32, path, cwdlen, caseinsensitive))
1870c688
CV
394 return;
395
396 size_t n = strlen (path);
397 if (n < cwdlen)
398 return;
399
400 char *tail = path;
401 if (n == cwdlen)
402 tail += cwdlen;
403 else
404 tail += isdirsep (cwd_win32[cwdlen - 1]) ? cwdlen : cwdlen + 1;
405
406 memmove (path, tail, strlen (tail) + 1);
407 if (!*path)
408 strcpy (path, ".");
409}
410
1870c688 411void
d2bd82aa 412path_conv::set_posix (const char *path_copy)
1870c688 413{
0986989f 414 if (path_copy)
1870c688 415 {
0986989f 416 size_t n = strlen (path_copy) + 1;
d2bd82aa
CV
417 char *p = (char *) crealloc_abort ((void *) posix_path, n);
418 posix_path = (const char *) memcpy (p, path_copy, n);
1870c688 419 }
1870c688
CV
420}
421
a22af4a9
CV
422static inline void
423str2uni_cat (UNICODE_STRING &tgt, const char *srcstr)
424{
425 int len = sys_mbstowcs (tgt.Buffer + tgt.Length / sizeof (WCHAR),
426 (tgt.MaximumLength - tgt.Length) / sizeof (WCHAR),
427 srcstr);
428 if (len)
429 tgt.Length += (len - 1) * sizeof (WCHAR);
430}
431
1870c688 432PUNICODE_STRING
8802178f 433get_nt_native_path (const char *path, UNICODE_STRING& upath, bool dos)
1870c688
CV
434{
435 upath.Length = 0;
436 if (path[0] == '/') /* special path w/o NT path representation. */
437 str2uni_cat (upath, path);
24790b9c 438 else if (path[0] != '\\') /* X:\... or relative path. */
1870c688 439 {
24790b9c 440 if (path[1] == ':') /* X:\... */
e4b57503 441 {
8deb4118 442 RtlAppendUnicodeStringToString (&upath, &ro_u_natp);
e4b57503
CV
443 str2uni_cat (upath, path);
444 /* The drive letter must be upper case. */
445 upath.Buffer[4] = towupper (upath.Buffer[4]);
fe9e3b44
CV
446 transform_chars (&upath, 7);
447 }
448 else /* relative path */
449 {
450 str2uni_cat (upath, path);
451 transform_chars (&upath, 0);
e4b57503 452 }
1870c688
CV
453 }
454 else if (path[1] != '\\') /* \Device\... */
455 str2uni_cat (upath, path);
456 else if ((path[2] != '.' && path[2] != '?')
457 || path[3] != '\\') /* \\server\share\... */
458 {
8deb4118 459 RtlAppendUnicodeStringToString (&upath, &ro_u_uncp);
1870c688 460 str2uni_cat (upath, path + 2);
e4b57503 461 transform_chars (&upath, 8);
1870c688
CV
462 }
463 else /* \\.\device or \\?\foo */
464 {
8deb4118 465 RtlAppendUnicodeStringToString (&upath, &ro_u_natp);
1870c688
CV
466 str2uni_cat (upath, path + 4);
467 }
8802178f
CV
468 if (dos)
469 {
470 /* Unfortunately we can't just use transform_chars with the tfx_rev_chars
f4529884 471 table since only leading and trailing spaces and dots are affected.
8802178f
CV
472 So we step to every backslash and fix surrounding dots and spaces.
473 That makes these broken filesystems a bit slower, but, hey. */
474 PWCHAR cp = upath.Buffer + 7;
475 PWCHAR cend = upath.Buffer + upath.Length / sizeof (WCHAR);
476 while (++cp < cend)
477 if (*cp == L'\\')
478 {
479 PWCHAR ccp = cp - 1;
480 while (*ccp == L'.' || *ccp == L' ')
481 *ccp-- |= 0xf000;
482 while (cp[1] == L' ')
483 *++cp |= 0xf000;
484 }
485 while (*--cp == L'.' || *cp == L' ')
b86f999a 486 *cp |= 0xf000;
8802178f 487 }
1870c688
CV
488 return &upath;
489}
43b2d5ba 490
5abb0ed1
CV
491/* Handle with extrem care! Only used in a certain instance in try_to_bin.
492 Every other usage needs a careful check. */
493void
494path_conv::set_nt_native_path (PUNICODE_STRING new_path)
495{
496 wide_path = (PWCHAR) crealloc_abort (wide_path, new_path->MaximumLength);
497 memcpy (wide_path, new_path->Buffer, new_path->Length);
498 uni_path.Length = new_path->Length;
499 uni_path.MaximumLength = new_path->MaximumLength;
500 uni_path.Buffer = wide_path;
501}
502
1870c688
CV
503PUNICODE_STRING
504path_conv::get_nt_native_path ()
505{
1f012519
CF
506 PUNICODE_STRING res;
507 if (wide_path)
508 res = &uni_path;
509 else if (!path)
510 res = NULL;
511 else
1870c688
CV
512 {
513 uni_path.Length = 0;
514 uni_path.MaximumLength = (strlen (path) + 10) * sizeof (WCHAR);
515 wide_path = (PWCHAR) cmalloc_abort (HEAP_STR, uni_path.MaximumLength);
516 uni_path.Buffer = wide_path;
5b4c992b 517 ::get_nt_native_path (path, uni_path, has_dos_filenames_only ());
1f012519 518 res = &uni_path;
1870c688 519 }
1f012519 520 return res;
1870c688 521}
91d2f6ee 522
1870c688
CV
523PWCHAR
524path_conv::get_wide_win32_path (PWCHAR wc)
525{
526 get_nt_native_path ();
ca027229 527 if (!wide_path)
1870c688 528 return NULL;
ca027229
CV
529 wcpcpy (wc, wide_path);
530 if (wc[1] == L'?')
531 wc[1] = L'\\';
1870c688
CV
532 return wc;
533}
534
1870c688 535static DWORD
e4b57503 536getfileattr (const char *path, bool caseinsensitive) /* path has to be always absolute. */
1870c688
CV
537{
538 tmp_pathbuf tp;
539 UNICODE_STRING upath;
540 OBJECT_ATTRIBUTES attr;
541 FILE_BASIC_INFORMATION fbi;
542 NTSTATUS status;
543 IO_STATUS_BLOCK io;
544
545 tp.u_get (&upath);
e4b57503
CV
546 InitializeObjectAttributes (&attr, &upath,
547 caseinsensitive ? OBJ_CASE_INSENSITIVE : 0,
548 NULL, NULL);
8802178f 549 get_nt_native_path (path, upath, false);
1870c688
CV
550
551 status = NtQueryAttributesFile (&attr, &fbi);
552 if (NT_SUCCESS (status))
553 return fbi.FileAttributes;
554
555 if (status != STATUS_OBJECT_NAME_NOT_FOUND
556 && status != STATUS_NO_SUCH_FILE) /* File not found on 9x share */
557 {
558 /* File exists but access denied. Try to get attribute through
559 directory query. */
560 UNICODE_STRING dirname, basename;
561 HANDLE dir;
61522196 562 FILE_BOTH_DIR_INFORMATION fdi;
1870c688
CV
563
564 RtlSplitUnicodePath (&upath, &dirname, &basename);
565 InitializeObjectAttributes (&attr, &dirname,
e4b57503
CV
566 caseinsensitive ? OBJ_CASE_INSENSITIVE : 0,
567 NULL, NULL);
1870c688
CV
568 status = NtOpenFile (&dir, SYNCHRONIZE | FILE_LIST_DIRECTORY,
569 &attr, &io, FILE_SHARE_VALID_FLAGS,
570 FILE_SYNCHRONOUS_IO_NONALERT
571 | FILE_OPEN_FOR_BACKUP_INTENT
572 | FILE_DIRECTORY_FILE);
573 if (NT_SUCCESS (status))
574 {
575 status = NtQueryDirectoryFile (dir, NULL, NULL, 0, &io,
576 &fdi, sizeof fdi,
3432d6f1 577 FileBothDirectoryInformation,
1870c688
CV
578 TRUE, &basename, TRUE);
579 NtClose (dir);
580 if (NT_SUCCESS (status) || status == STATUS_BUFFER_OVERFLOW)
581 return fdi.FileAttributes;
582 }
583 }
584 SetLastError (RtlNtStatusToDosError (status));
585 return INVALID_FILE_ATTRIBUTES;
586}
587
588/* Convert an arbitrary path SRC to a pure Win32 path, suitable for
589 passing to Win32 API routines.
590
591 If an error occurs, `error' is set to the errno value.
592 Otherwise it is set to 0.
593
594 follow_mode values:
595 SYMLINK_FOLLOW - convert to PATH symlink points to
596 SYMLINK_NOFOLLOW - convert to PATH of symlink itself
597 SYMLINK_IGNORE - do not check PATH for symlinks
598 SYMLINK_CONTENTS - just return symlink contents
599*/
600
601/* TODO: This implementation is only preliminary. For internal
602 purposes it's necessary to have a path_conv::check function which
603 takes a UNICODE_STRING src path, otherwise we waste a lot of time
604 for converting back and forth. The below implementation does
605 realy nothing but converting to char *, until path_conv handles
606 wide-char paths directly. */
607void
608path_conv::check (const UNICODE_STRING *src, unsigned opt,
609 const suffix_info *suffixes)
610{
611 tmp_pathbuf tp;
612 char *path = tp.c_get ();
613
614 user_shared->warned_msdos = true;
615 sys_wcstombs (path, NT_MAX_PATH, src->Buffer, src->Length / sizeof (WCHAR));
616 path_conv::check (path, opt, suffixes);
617}
618
619void
620path_conv::check (const char *src, unsigned opt,
621 const suffix_info *suffixes)
622{
623 /* The tmp_buf array is used when expanding symlinks. It is NT_MAX_PATH * 2
624 in length so that we can hold the expanded symlink plus a trailer. */
625 tmp_pathbuf tp;
626 char *path_copy = tp.c_get ();
627 char *pathbuf = tp.c_get ();
628 char *tmp_buf = tp.t_get ();
0986989f 629 char *THIS_path = tp.c_get ();
1870c688
CV
630 symlink_info sym;
631 bool need_directory = 0;
0986989f 632 bool add_ext = false;
1870c688
CV
633 bool is_relpath;
634 char *tail, *path_end;
1fd5e000 635
1870c688
CV
636#if 0
637 static path_conv last_path_conv;
638 static char last_src[CYG_MAX_PATH];
ad30b4ff 639
1870c688
CV
640 if (*last_src && strcmp (last_src, src) == 0)
641 {
642 *this = last_path_conv;
ad30b4ff 643 return;
1870c688
CV
644 }
645#endif
ad30b4ff 646
3f3bd101
CV
647 __try
648 {
649 int loop = 0;
c1023ee3 650 mount_flags = 0;
3f3bd101 651 path_flags = 0;
d2bd82aa 652 suffix = NULL;
3f3bd101
CV
653 fileattr = INVALID_FILE_ATTRIBUTES;
654 caseinsensitive = OBJ_CASE_INSENSITIVE;
655 if (wide_path)
656 cfree (wide_path);
657 wide_path = NULL;
658 if (path)
1870c688 659 {
3f3bd101
CV
660 cfree (modifiable_path ());
661 path = NULL;
1870c688 662 }
3f3bd101 663 close_conv_handle ();
3f3bd101 664 fs.clear ();
d2bd82aa 665 if (posix_path)
1870c688 666 {
d2bd82aa
CV
667 cfree ((void *) posix_path);
668 posix_path = NULL;
1870c688 669 }
3f3bd101 670 int component = 0; // Number of translated components
1870c688 671
3f3bd101
CV
672 if (!(opt & PC_NULLEMPTY))
673 error = 0;
674 else if (!*src)
675 {
676 error = ENOENT;
677 return;
678 }
1870c688 679
3f3bd101
CV
680 bool is_msdos = false;
681 /* This loop handles symlink expansion. */
682 for (;;)
1870c688 683 {
3f3bd101
CV
684 is_relpath = !isabspath (src);
685 error = normalize_posix_path (src, path_copy, tail);
686 if (error > 0)
687 return;
688 if (error < 0)
1870c688 689 {
3f3bd101
CV
690 if (component == 0)
691 is_msdos = true;
692 error = 0;
1870c688 693 }
3f3bd101
CV
694
695 /* Detect if the user was looking for a directory. We have to strip
696 the trailing slash initially while trying to add extensions but
697 take it into account during processing */
698 if (tail > path_copy + 2 && isslash (tail[-1]))
1870c688 699 {
3f3bd101
CV
700 need_directory = 1;
701 *--tail = '\0';
1870c688 702 }
3f3bd101
CV
703 path_end = tail;
704
705 /* Scan path_copy from right to left looking either for a symlink
706 or an actual existing file. If an existing file is found, just
707 return. If a symlink is found, exit the for loop.
708 Also: be careful to preserve the errno returned from
709 symlink.check as the caller may need it. */
710 /* FIXME: Do we have to worry about multiple \'s here? */
711 component = 0; // Number of translated components
712 sym.contents[0] = '\0';
713
714 int symlen = 0;
715
fde4eaa1
CV
716 /* Make sure to check certain flags on last component only. */
717 for (unsigned pc_flags = opt & (PC_NO_ACCESS_CHECK | PC_KEEP_HANDLE);
3f3bd101 718 ;
fde4eaa1 719 pc_flags = 0)
3f3bd101
CV
720 {
721 const suffix_info *suff;
722 char *full_path;
1fd5e000 723
3f3bd101
CV
724 /* Don't allow symlink.check to set anything in the path_conv
725 class if we're working on an inner component of the path */
726 if (component)
727 {
728 suff = NULL;
729 full_path = pathbuf;
730 }
731 else
732 {
733 suff = suffixes;
734 full_path = THIS_path;
735 }
7ceb1cac 736
18578496
CV
737 retry_fs_via_processfd:
738
3f3bd101
CV
739 /* Convert to native path spec sans symbolic link info. */
740 error = mount_table->conv_to_win32_path (path_copy, full_path,
c1023ee3 741 dev, &sym.mount_flags);
2b5803d4 742
3f3bd101
CV
743 if (error)
744 return;
ac7bc2d4 745
fde4eaa1 746 sym.pc_flags = pc_flags;
1c1294b4 747
3f3bd101 748 if (!dev.exists ())
1870c688 749 {
3f3bd101
CV
750 error = ENXIO;
751 return;
1870c688 752 }
3f3bd101
CV
753
754 if (iscygdrive_dev (dev))
1870c688 755 {
3f3bd101
CV
756 if (!component)
757 fileattr = FILE_ATTRIBUTE_DIRECTORY
758 | FILE_ATTRIBUTE_READONLY;
759 else
e5b7e4d1 760 {
3f3bd101 761 fileattr = getfileattr (THIS_path,
c1023ee3 762 sym.mount_flags & MOUNT_NOPOSIX);
3f3bd101 763 dev = FH_FS;
e5b7e4d1 764 }
3f3bd101 765 goto out;
1870c688 766 }
3f3bd101 767 else if (isdev_dev (dev))
1870c688 768 {
3f3bd101
CV
769 /* Make sure that the path handling goes on as with FH_FS. */
770 }
771 else if (isvirtual_dev (dev))
772 {
773 /* FIXME: Calling build_fhandler here is not the right way to
774 handle this. */
775 fhandler_virtual *fh = (fhandler_virtual *)
776 build_fh_dev (dev, path_copy);
777 virtual_ftype_t file_type;
778 if (!fh)
779 file_type = virt_none;
780 else
781 {
782 file_type = fh->exists ();
a3a5d52b
CV
783 if (file_type == virt_symlink
784 || file_type == virt_fdsymlink)
3f3bd101
CV
785 {
786 fh->fill_filebuf ();
787 symlen = sym.set (fh->get_filebuf ());
788 }
f72191ac 789 else if (file_type == virt_fsdir && dev == FH_PROCESSFD)
18578496
CV
790 {
791 /* FIXME: This is YA bad hack to workaround that
792 we're checking for isvirtual_dev at this point.
793 This should only happen if the file is actually
794 a virtual file, and NOT already if the preceeding
795 path components constitute a virtual file.
796
797 Anyway, what we do here is this: If the descriptor
798 symlink points to a dir, and if there are trailing
799 path components, it's actually pointing somewhere
800 else. The format_process_fd function returns the
801 full path, resolved symlink plus trailing path
802 components, in its filebuf. This is a POSIX path
803 we know nothing about, so we have to convert it to
804 native again, calling conv_to_win32_path. Since
805 basically nothing happened yet, just copy it over
806 into full_path and jump back to the
807 conv_to_win32_path call. What a mess. */
808 stpcpy (path_copy, fh->get_filebuf ());
809 delete fh;
810 goto retry_fs_via_processfd;
811 }
6775ac8c
KB
812 else if (file_type == virt_none && dev == FH_PROCESSFD)
813 {
814 error = get_errno ();
815 if (error)
816 {
817 delete fh;
818 return;
819 }
820 }
3f3bd101
CV
821 delete fh;
822 }
823 switch (file_type)
824 {
825 case virt_directory:
826 case virt_rootdir:
827 if (component == 0)
828 fileattr = FILE_ATTRIBUTE_DIRECTORY;
829 break;
830 case virt_file:
831 if (component == 0)
832 fileattr = 0;
833 break;
a3a5d52b 834 case virt_fdsymlink:
732613f3
CV
835 /* Allow open/linkat to do the right thing. */
836 if (opt & PC_SYM_NOFOLLOW_PROCFD)
ba12614f
CV
837 {
838 opt &= ~PC_SYM_FOLLOW;
839 sym.path_flags |= PATH_RESOLVE_PROCFD;
840 }
50ad1980 841 fallthrough;
3f3bd101
CV
842 case virt_symlink:
843 goto is_virtual_symlink;
844 case virt_pipe:
845 if (component == 0)
846 {
847 fileattr = 0;
848 dev.parse (FH_PIPE);
849 }
850 break;
851 case virt_socket:
852 if (component == 0)
853 {
854 fileattr = 0;
eaf35957 855 dev.parse (FH_SOCKET);
3f3bd101
CV
856 }
857 break;
858 case virt_fsdir:
859 case virt_fsfile:
860 /* Access to real file or directory via block device
861 entry in /proc/sys. Convert to real file and go with
862 the flow. */
43f65cdd 863 dev.parse (FH_FS);
3f3bd101
CV
864 goto is_fs_via_procsys;
865 case virt_blk:
3434d35a
CF
866 /* Block special device. Convert to a /dev/sd* like
867 block device unless the trailing slash has been
868 requested. In this case, the target is the root
869 directory of the filesystem on this block device.
870 So we convert this to a real file and attach the
871 backslash. */
872 if (component == 0)
3f3bd101 873 {
3434d35a
CF
874 fileattr = FILE_ATTRIBUTE_DEVICE;
875 if (!need_directory)
876 /* Use a /dev/sd* device number > /dev/sddx.
877 FIXME: Define a new major DEV_ice number. */
878 dev.parse (DEV_SD_HIGHPART_END, 9999);
879 else
880 {
881 dev.parse (FH_FS);
882 strcat (full_path, "\\");
883 fileattr |= FILE_ATTRIBUTE_DIRECTORY;
884 }
3f3bd101
CV
885 goto out;
886 }
3434d35a 887 break;
3f3bd101
CV
888 case virt_chr:
889 if (component == 0)
890 fileattr = FILE_ATTRIBUTE_DEVICE;
891 break;
892 default:
893 if (component == 0)
894 fileattr = INVALID_FILE_ATTRIBUTES;
895 goto virtual_component_retry;
896 }
897 if (component == 0 || dev != FH_NETDRIVE)
c1023ee3 898 mount_flags |= MOUNT_RO;
3f3bd101 899 goto out;
1870c688 900 }
3f3bd101
CV
901 /* devn should not be a device. If it is, then stop parsing. */
902 else if (dev != FH_FS)
1870c688 903 {
3f3bd101 904 fileattr = 0;
c1023ee3
CV
905 mount_flags = sym.mount_flags;
906 path_flags = sym.path_flags;
3f3bd101
CV
907 if (component)
908 {
909 error = ENOTDIR;
910 return;
911 }
912 goto out; /* Found a device. Stop parsing. */
1870c688 913 }
2b5803d4 914
3f3bd101
CV
915 /* If path is only a drivename, Windows interprets it as the
916 current working directory on this drive instead of the root
917 dir which is what we want. So we need the trailing backslash
918 in this case. */
919 if (full_path[0] && full_path[1] == ':' && full_path[2] == '\0')
920 {
921 full_path[2] = '\\';
922 full_path[3] = '\0';
923 }
faf07ace 924
3f3bd101
CV
925 /* If the incoming path was given in DOS notation, always treat
926 it as caseinsensitive,noacl path. This must be set before
927 calling sym.check, otherwise the path is potentially treated
928 casesensitive. */
929 if (is_msdos)
c1023ee3 930 sym.mount_flags |= MOUNT_NOPOSIX | MOUNT_NOACL;
35c0485d 931
3f3bd101 932 is_fs_via_procsys:
43f65cdd 933
3f3bd101 934 symlen = sym.check (full_path, suff, fs, conv_handle);
7ac61736 935
3f3bd101 936 is_virtual_symlink:
2cf9359a 937
3f3bd101 938 if (sym.isdevice)
d955b6cf 939 {
3f3bd101
CV
940 if (component)
941 {
942 error = ENOTDIR;
943 return;
944 }
945 dev.parse (sym.major, sym.minor);
946 dev.setfs (1);
b2867a68 947 dev.mode (sym.mode);
3f3bd101
CV
948 fileattr = sym.fileattr;
949 goto out;
d955b6cf 950 }
70c370d6 951
c1023ee3 952 if (sym.path_flags & PATH_SOCKET)
1870c688 953 {
3f3bd101
CV
954 if (component)
955 {
956 error = ENOTDIR;
957 return;
958 }
959 fileattr = sym.fileattr;
6c55be9d 960#ifdef __WITH_AF_UNIX
c1023ee3 961 dev.parse ((sym.path_flags & PATH_REP) ? FH_UNIX : FH_LOCAL);
6c55be9d
CV
962#else
963 dev.parse (FH_LOCAL);
964#endif /* __WITH_AF_UNIX */
3f3bd101 965 dev.setfs (1);
c1023ee3
CV
966 mount_flags = sym.mount_flags;
967 path_flags = sym.path_flags;
3f3bd101 968 goto out;
1870c688 969 }
fc633b63 970
3f3bd101
CV
971 if (!component)
972 {
973 /* Make sure that /dev always exists. */
974 fileattr = isdev_dev (dev) ? FILE_ATTRIBUTE_DIRECTORY
975 : sym.fileattr;
c1023ee3
CV
976 mount_flags = sym.mount_flags;
977 path_flags = sym.path_flags;
3f3bd101
CV
978 }
979 else if (isdev_dev (dev))
980 {
981 /* If we're looking for a non-existing file below /dev,
982 make sure that the device type is converted to FH_FS, so
983 that subsequent code handles the file correctly. Unless
984 /dev itself doesn't exist on disk. In that case /dev
985 is handled as virtual filesystem, and virtual filesystems
986 are read-only. The PC_KEEP_HANDLE check allows to check
987 for a call from an informational system call. In that
988 case we just stick to ENOENT, and the device type doesn't
989 matter anyway. */
990 if (sym.error == ENOENT && !(opt & PC_KEEP_HANDLE))
991 sym.error = EROFS;
992 else
993 dev = FH_FS;
994 }
e4b57503 995
3f3bd101
CV
996 /* If symlink.check found an existing non-symlink file, then
997 it sets the appropriate flag. It also sets any suffix found
998 into `ext_here'. */
999 if (!sym.issymlink && sym.fileattr != INVALID_FILE_ATTRIBUTES)
1870c688 1000 {
3f3bd101
CV
1001 error = sym.error;
1002 if (component == 0)
1003 add_ext = true;
1004 else if (!(sym.fileattr & FILE_ATTRIBUTE_DIRECTORY))
1005 {
1006 error = ENOTDIR;
1007 goto out;
1008 }
1009 goto out; // file found
1870c688 1010 }
3f3bd101
CV
1011 /* Found a symlink if symlen > 0. If component == 0, then the
1012 src path itself was a symlink. If !follow_mode then
1013 we're done. Otherwise we have to insert the path found
1014 into the full path that we are building and perform all of
1015 these operations again on the newly derived path. */
1016 else if (symlen > 0)
1870c688 1017 {
26425142 1018 if (component == 0
3f3bd101 1019 && (!(opt & PC_SYM_FOLLOW)
aec64798 1020 || (is_winapi_reparse_point ()
3f3bd101 1021 && (opt & PC_SYM_NOFOLLOW_REP))))
1870c688 1022 {
26425142
CV
1023 /* Usually a trailing slash requires to follow a symlink,
1024 even with PC_SYM_NOFOLLOW. The reason is that "foo/"
1025 is equivalent to "foo/." so the symlink is in fact not
1026 the last path component.
1027
1028 PC_SYM_NOFOLLOW_DIR is used to indicate that the
1029 last path component is the target symlink and the
1030 trailing slash is supposed to be ignored. */
1031 if (!need_directory || (opt & PC_SYM_NOFOLLOW_DIR))
3f3bd101 1032 {
26425142
CV
1033 /* last component of path is a symlink. */
1034 set_symlink (symlen);
1035 /* make sure not to set errno to ENOTDIR. */
1036 need_directory = 0;
1037 if (opt & PC_SYM_CONTENTS)
1038 {
1039 strcpy (THIS_path, sym.contents);
1040 goto out;
1041 }
1042 add_ext = true;
3f3bd101
CV
1043 goto out;
1044 }
1870c688 1045 }
3f3bd101
CV
1046 /* Following a symlink we can't trust the collected
1047 filesystem information any longer. */
1048 fs.clear ();
1049 /* Close handle, if we have any. Otherwise we're collecting
1050 handles while following symlinks. */
1051 conv_handle.close ();
1052 break;
1053 }
1054 else if (sym.error && sym.error != ENOENT)
1055 {
1056 error = sym.error;
1870c688
CV
1057 goto out;
1058 }
3f3bd101
CV
1059 /* No existing file found. */
1060
1061 virtual_component_retry:
1062 /* Find the new "tail" of the path, e.g. in '/for/bar/baz',
1063 /baz is the tail. */
1064 if (tail != path_end)
1065 *tail = '/';
1066 while (--tail > path_copy + 1 && *tail != '/') {}
1067 /* Exit loop if there is no tail or we are at the
1068 beginning of a UNC path */
1069 if (tail <= path_copy + 1)
1070 goto out; // all done
1071
1072 /* Haven't found an existing pathname component yet.
1073 Pinch off the tail and try again. */
1074 *tail = '\0';
1075 component++;
1870c688 1076 }
3f3bd101
CV
1077
1078 /* Arrive here if above loop detected a symlink. */
1079 if (++loop > SYMLOOP_MAX)
e4b57503 1080 {
3f3bd101
CV
1081 error = ELOOP; // Eep.
1082 return;
e4b57503 1083 }
1fd5e000 1084
3f3bd101
CV
1085 /* Place the link content, possibly with head and/or tail,
1086 in tmp_buf */
1fd5e000 1087
3f3bd101
CV
1088 char *headptr;
1089 if (isabspath (sym.contents))
1090 headptr = tmp_buf; /* absolute path */
1091 else
1092 {
1093 /* Copy the first part of the path (with ending /) and point to
1094 the end. */
1095 char *prevtail = tail;
1096 while (--prevtail > path_copy && *prevtail != '/') {}
1097 int headlen = prevtail - path_copy + 1;;
1098 memcpy (tmp_buf, path_copy, headlen);
1099 headptr = &tmp_buf[headlen];
1100 }
fe6934da 1101
3f3bd101
CV
1102 /* Make sure there is enough space */
1103 if (headptr + symlen >= tmp_buf + (2 * NT_MAX_PATH))
1104 {
1105 too_long:
1106 error = ENAMETOOLONG;
1107 set_path ("::ENAMETOOLONG::");
1108 return;
1109 }
7ceb1cac 1110
3f3bd101
CV
1111 /* Copy the symlink contents to the end of tmp_buf.
1112 Convert slashes. */
1113 for (char *p = sym.contents; *p; p++)
1114 *headptr++ = *p == '\\' ? '/' : *p;
1115 *headptr = '\0';
1fd5e000 1116
3f3bd101
CV
1117 /* Copy any tail component (with the 0) */
1118 if (tail++ < path_end)
1119 {
1120 /* Add a slash if needed. There is space. */
1121 if (*(headptr - 1) != '/')
1122 *headptr++ = '/';
1123 int taillen = path_end - tail + 1;
1124 if (headptr + taillen > tmp_buf + (2 * NT_MAX_PATH))
1125 goto too_long;
1126 memcpy (headptr, tail, taillen);
1127 }
1128
1129 /* Evaluate everything all over again. */
1130 src = tmp_buf;
1870c688 1131 }
1fd5e000 1132
3f3bd101
CV
1133 if (!(opt & PC_SYM_CONTENTS))
1134 add_ext = true;
1135
1136 out:
1137 set_path (THIS_path);
1138 if (add_ext)
1139 add_ext_from_sym (sym);
1140 if (dev == FH_NETDRIVE && component)
1141 {
1142 /* This case indicates a non-existant resp. a non-retrievable
1143 share. This happens for instance if the share is a printer.
1144 In this case the path must not be treated like a FH_NETDRIVE,
1145 but like a FH_FS instead, so the usual open call for files
1146 is used on it. */
1147 dev.parse (FH_FS);
1148 }
1149 else if (isproc_dev (dev) && fileattr == INVALID_FILE_ATTRIBUTES)
1870c688 1150 {
3f3bd101
CV
1151 /* FIXME: Usually we don't set error to ENOENT if a file doesn't
1152 exist. This is typically indicated by the fileattr content.
1153 So, why here? The downside is that cygwin_conv_path just gets
1154 an error for these paths so it reports the error back to the
1155 application. Unlike in all other cases of non-existant files,
1156 for which check doesn't set error, so cygwin_conv_path just
1157 returns the path, as intended. */
1158 error = ENOENT;
1870c688
CV
1159 return;
1160 }
3f3bd101
CV
1161 else if (!need_directory || error)
1162 /* nothing to do */;
1163 else if (fileattr == INVALID_FILE_ATTRIBUTES)
1164 /* Reattach trailing dirsep in native path. */
1165 strcat (modifiable_path (), "\\");
1166 else if (fileattr & FILE_ATTRIBUTE_DIRECTORY)
1167 path_flags &= ~PATH_SYMLINK;
1168 else
1870c688 1169 {
3f3bd101
CV
1170 debug_printf ("%s is a non-directory", path);
1171 error = ENOTDIR;
1172 return;
1870c688 1173 }
70300fdb 1174
3f3bd101 1175 if (dev.isfs ())
1870c688 1176 {
3f3bd101
CV
1177 /* If FS hasn't been checked already in symlink_info::check,
1178 do so now. */
1179 if (fs.inited ()|| fs.update (get_nt_native_path (), NULL))
1180 {
1181 /* Incoming DOS paths are treated like DOS paths in native
1182 Windows applications. No ACLs, just default settings. */
1183 if (is_msdos)
1184 fs.has_acls (false);
1185 debug_printf ("this->path(%s), has_acls(%d)",
1186 path, fs.has_acls ());
1187 /* CV: We could use this->has_acls() but I want to make sure that
c1023ee3 1188 we don't forget that the MOUNT_NOACL flag must be taken into
3f3bd101 1189 account here. */
c1023ee3 1190 if (!(mount_flags & MOUNT_NOACL) && fs.has_acls ())
3f3bd101
CV
1191 set_exec (0); /* We really don't know if this is executable or
1192 not here but set it to not executable since
1193 it will be figured out later by anything
1194 which cares about this. */
1870c688 1195 }
fde4eaa1 1196 /* If the FS has been found to have unreliable inodes, note
c1023ee3 1197 that in mount_flags. */
3f3bd101 1198 if (!fs.hasgood_inode ())
c1023ee3 1199 mount_flags |= MOUNT_IHASH;
3f3bd101
CV
1200 /* If the OS is caseinsensitive or the FS is caseinsensitive,
1201 don't handle path casesensitive. */
1202 if (cygwin_shared->obcaseinsensitive || fs.caseinsensitive ())
c1023ee3
CV
1203 mount_flags |= MOUNT_NOPOSIX;
1204 caseinsensitive = (mount_flags & MOUNT_NOPOSIX)
3f3bd101
CV
1205 ? OBJ_CASE_INSENSITIVE : 0;
1206 if (exec_state () != dont_know_if_executable)
1207 /* ok */;
1208 else if (isdir ())
1209 set_exec (1);
1210 else if (issymlink () || issocket ())
1211 set_exec (0);
1870c688 1212 }
91a2f87b 1213
3f3bd101
CV
1214 if (opt & PC_NOFULL)
1215 {
1216 if (is_relpath)
1870c688 1217 {
3f3bd101
CV
1218 mkrelpath (this->modifiable_path (), !!caseinsensitive);
1219 /* Invalidate wide_path so that wide relpath can be created
1220 in later calls to get_nt_native_path or get_wide_win32_path. */
1221 if (wide_path)
1222 cfree (wide_path);
1223 wide_path = NULL;
1224 }
1225 if (need_directory)
1226 {
1227 size_t n = strlen (this->path);
1228 /* Do not add trailing \ to UNC device names like \\.\a: */
1229 if (this->path[n - 1] != '\\' &&
1230 (strncmp (this->path, "\\\\.\\", 4) != 0))
1231 {
1232 this->modifiable_path ()[n] = '\\';
1233 this->modifiable_path ()[n + 1] = '\0';
1234 }
1870c688
CV
1235 }
1236 }
7ac61736 1237
3f3bd101
CV
1238 if (opt & PC_OPEN)
1239 path_flags |= PATH_OPEN;
23771fa1 1240
3f3bd101
CV
1241 if (opt & PC_CTTY)
1242 path_flags |= PATH_CTTY;
23771fa1 1243
3f3bd101
CV
1244 if (opt & PC_POSIX)
1245 {
1246 if (tail < path_end && tail > path_copy + 1)
1247 *tail = '/';
d2bd82aa 1248 set_posix (path_copy);
3f3bd101 1249 }
81adfe28
CF
1250
1251#if 0
3f3bd101
CV
1252 if (!error)
1253 {
1254 last_path_conv = *this;
1255 strcpy (last_src, src);
1256 }
1257#endif
1258 }
1259 __except (NO_ERROR)
1870c688 1260 {
3f3bd101 1261 error = EFAULT;
1870c688 1262 }
3f3bd101 1263 __endtry
1870c688 1264}
81adfe28 1265
91b264c7
CV
1266struct pc_flat
1267{
1268 path_conv pc;
1269 HANDLE hdl;
1270 size_t name_len;
1271 size_t posix_len;
1272 char data[0];
1273};
1274
1275void *
1276path_conv::serialize (HANDLE h, unsigned int &n) const
1277{
1278 pc_flat *pcf;
1279 size_t nlen = 0, plen = 0;
1280 char *p;
1281
1282 if (path)
1283 nlen = strlen (path) + 1;
1284 if (posix_path)
1285 plen = strlen (posix_path) + 1;
1286 n = sizeof (pc_flat) + nlen + plen;
1287 pcf = (pc_flat *) cmalloc (HEAP_COMMUNE, n);
1288 if (!pcf)
1289 {
1290 n = 0;
1291 return NULL;
1292 }
f0cf44dc 1293 memcpy ((void *) &pcf->pc, this, sizeof *this);
91b264c7
CV
1294 pcf->hdl = h;
1295 pcf->name_len = nlen;
1296 pcf->posix_len = plen;
1297 p = pcf->data;
1298 if (nlen)
1299 p = stpcpy (p, path) + 1;
1300 if (plen)
1301 stpcpy (p, posix_path);
1302 return pcf;
1303}
1304
1305HANDLE
1306path_conv::deserialize (void *bufp)
1307{
1308 pc_flat *pcf = (pc_flat *) bufp;
1309 char *p;
1310 HANDLE ret;
1311
f0cf44dc 1312 memcpy ((void *) this, &pcf->pc, sizeof *this);
91b264c7
CV
1313 wide_path = uni_path.Buffer = NULL;
1314 uni_path.MaximumLength = uni_path.Length = 0;
1315 path = posix_path = NULL;
1316 p = pcf->data;
1317 if (pcf->name_len)
1318 {
1319 set_path (p);
1320 p += pcf->name_len;
1321 }
1322 if (pcf->posix_len)
1323 set_posix (p);
1324 dev.parse (pcf->pc.dev);
1325 ret = pcf->hdl;
1326 cfree (bufp);
1327 return ret;
1328}
1329
1870c688
CV
1330path_conv::~path_conv ()
1331{
d2bd82aa 1332 if (posix_path)
1870c688 1333 {
d2bd82aa
CV
1334 cfree ((void *) posix_path);
1335 posix_path = NULL;
1870c688 1336 }
0986989f
CV
1337 if (path)
1338 {
fafbf755 1339 cfree (modifiable_path ());
0986989f
CV
1340 path = NULL;
1341 }
1870c688
CV
1342 if (wide_path)
1343 {
1344 cfree (wide_path);
1345 wide_path = NULL;
1346 }
5a0d1edb 1347 close_conv_handle ();
1870c688
CV
1348}
1349
1350bool
1351path_conv::is_binary ()
1352{
1353 tmp_pathbuf tp;
1354 PWCHAR bintest = tp.w_get ();
1355 DWORD bin;
c32849c5 1356
3bee7767
CV
1357 return GetBinaryTypeW (get_wide_win32_path (bintest), &bin)
1358 && (bin == SCS_32BIT_BINARY || bin == SCS_64BIT_BINARY);
1870c688
CV
1359}
1360
eed35efb 1361/* Helper function to fill the fai datastructure for a file. */
3780d205 1362NTSTATUS
eed35efb 1363file_get_fai (HANDLE h, PFILE_ALL_INFORMATION pfai)
3780d205
CV
1364{
1365 NTSTATUS status;
1366 IO_STATUS_BLOCK io;
1367
1368 /* Some FSes (Netapps) don't implement FileNetworkOpenInformation. */
eed35efb
CV
1369 status = NtQueryInformationFile (h, &io, pfai, sizeof *pfai,
1370 FileAllInformation);
9c4113f0 1371 if (likely (status == STATUS_BUFFER_OVERFLOW))
eed35efb 1372 status = STATUS_SUCCESS;
9c4113f0
CV
1373 /* Filesystems with broken FileAllInformation exist, too. See the thread
1374 starting with https://cygwin.com/ml/cygwin/2016-07/msg00350.html. */
1375 else if (!NT_SUCCESS (status) && status != STATUS_ACCESS_DENIED)
1376 {
1377 memset (pfai, 0, sizeof *pfai);
1378 status = NtQueryInformationFile (h, &io, &pfai->BasicInformation,
1379 sizeof pfai->BasicInformation,
1380 FileBasicInformation);
1381 if (NT_SUCCESS (status))
1382 {
1383 /* The return value of FileInternalInformation is largely ignored.
1384 We only make absolutely sure the inode number is set to 0 in
1385 case it fails. */
1386 status = NtQueryInformationFile (h, &io, &pfai->InternalInformation,
1387 sizeof pfai->InternalInformation,
1388 FileInternalInformation);
1389 if (!NT_SUCCESS (status))
1390 pfai->InternalInformation.IndexNumber.QuadPart = 0LL;
1391 status = NtQueryInformationFile (h, &io, &pfai->StandardInformation,
1392 sizeof pfai->StandardInformation,
1393 FileStandardInformation);
1394 }
1395 }
3780d205
CV
1396 return status;
1397}
1398
1870c688
CV
1399/* Normalize a Win32 path.
1400 /'s are converted to \'s in the process.
1401 All duplicate \'s, except for 2 leading \'s, are deleted.
1402
1403 The result is 0 for success, or an errno error value.
1404 FIXME: A lot of this should be mergeable with the POSIX critter. */
1405int
1406normalize_win32_path (const char *src, char *dst, char *&tail)
1407{
1408 const char *src_start = src;
35998fc2 1409 const char *dst_start = dst;
1870c688
CV
1410 bool beg_src_slash = isdirsep (src[0]);
1411
1412 tail = dst;
283cb372 1413 /* Skip Win32 long path name prefix and NT object directory prefix. */
1870c688
CV
1414 if (beg_src_slash && (src[1] == '?' || isdirsep (src[1]))
1415 && src[2] == '?' && isdirsep (src[3]))
1416 {
1417 src += 4;
85aff283 1418 if (isdrive (src) && (isdirsep (src[2]) || !src[2]))
283cb372
KB
1419 beg_src_slash = false;
1420 else if (!strncmp (src, "UNC", 3) && isdirsep (src[3]))
1421 /* native UNC path */
1d011c0a 1422 src += 2; /* Fortunately the first char is not copied... */
1870c688 1423 else
283cb372 1424 return EINVAL;
1870c688
CV
1425 }
1426 if (beg_src_slash && isdirsep (src[1]))
1427 {
1428 if (isdirsep (src[2]))
1429 {
1430 /* More than two slashes are just folded into one. */
81adfe28 1431 src += 2;
1870c688
CV
1432 while (isdirsep (src[1]))
1433 ++src;
1434 }
1435 else
1436 {
1437 /* Two slashes start a network or device path. */
1438 *tail++ = '\\';
1439 src++;
1440 if (src[1] == '.' && isdirsep (src[2]))
1441 {
81adfe28 1442 *tail++ = '\\';
1870c688
CV
1443 *tail++ = '.';
1444 src += 2;
1445 }
1446 }
35998fc2
CV
1447 dst = tail;
1448 /* If backslash is missing in src, add one. */
1449 if (!isdirsep (src[0]))
1450 *tail++ = '\\';
1870c688 1451 }
35998fc2 1452 if (tail == dst_start)
1870c688 1453 {
54a83cc6 1454 if (isdrive (src))
35998fc2
CV
1455 {
1456 /* Always convert drive letter to uppercase for case sensitivity. */
1457 *tail++ = cyg_toupper (*src++);
1458 *tail++ = *src++;
1459 dst = tail;
1460 /* If backslash is missing in src, add one. */
1461 if (!isdirsep (src[0]))
1462 *tail++ = '\\';
1463 }
54a83cc6 1464 else if (*src != '/')
1870c688 1465 {
3a880bf5
CV
1466 /* Make sure dst points to the rightmost backslash which must not
1467 be backtracked over during ".." evaluation. This is either
1468 the backslash after the network path prefix (i.e. "\\") or
1469 the backslash after a drive letter (i.e. C:\"). */
54a83cc6 1470 if (beg_src_slash)
3a880bf5
CV
1471 {
1472 tail += cygheap->cwd.get_drive (dst);
1473 /* network path, drive == '\\\\'? Decrement tail to avoid
1474 triple backslash in output. */
1475 if (dst[0] == '\\')
1476 --tail;
1477 dst = tail;
1478 }
35998fc2 1479 else if (cygheap->cwd.get (dst, 0))
54a83cc6
CV
1480 {
1481 tail = strchr (tail, '\0');
1482 if (tail[-1] != '\\')
1483 *tail++ = '\\';
3a880bf5
CV
1484 ++dst;
1485 if (dst[1] == '\\')
1486 ++dst;
54a83cc6 1487 }
35998fc2
CV
1488 else
1489 return get_errno ();
1870c688
CV
1490 }
1491 }
81adfe28 1492
35998fc2
CV
1493 /* At this point dst points to the first backslash, even if it only gets
1494 written in the first iteration of the following loop. Backing up to
1495 handle ".." components can not underrun that border (thus avoiding
1496 subsequent buffer underruns with fatal results). */
1870c688
CV
1497 while (*src)
1498 {
1499 /* Strip duplicate /'s. */
1500 if (isdirsep (src[0]) && isdirsep (src[1]))
1501 src++;
1502 /* Ignore "./". */
1503 else if (src[0] == '.' && isdirsep (src[1])
1504 && (src == src_start || isdirsep (src[-1])))
f0ae353a
CV
1505 {
1506 src += 2;
1507 /* Skip /'s to the next path component. */
1508 while (isdirsep (*src))
1509 src++;
1510 }
1870c688
CV
1511
1512 /* Backup if "..". */
3a880bf5 1513 else if (src[0] == '.' && src[1] == '.' && tail[-1] == '\\')
1870c688
CV
1514 {
1515 if (!isdirsep (src[2]) && src[2] != '\0')
1516 *tail++ = *src++;
1517 else
1518 {
1519 /* Back up over /, but not if it's the first one. */
1520 if (tail > dst + 1)
1521 tail--;
1522 /* Now back up to the next /. */
1523 while (tail > dst + 1 && tail[-1] != '\\' && tail[-2] != ':')
1524 tail--;
1525 src += 2;
787908c1
CV
1526 /* Skip /'s to the next path component. */
1527 while (isdirsep (*src))
1870c688
CV
1528 src++;
1529 }
1530 }
1531 /* Otherwise, add char to result. */
1532 else
1533 {
1534 if (*src == '/')
1535 *tail++ = '\\';
1536 else
1537 *tail++ = *src;
1538 src++;
1539 }
1540 if ((tail - dst) >= NT_MAX_PATH)
1541 return ENAMETOOLONG;
1542 }
c4441d46
CF
1543 if (tail > dst + 1 && tail[-1] == '.' && tail[-2] == '\\')
1544 tail--;
1870c688 1545 *tail = '\0';
35998fc2 1546 debug_printf ("%s = normalize_win32_path (%s)", dst_start, src_start);
1870c688
CV
1547 return 0;
1548}
1549
1550/* Various utilities. */
1551
1552/* nofinalslash: Remove trailing / and \ from SRC (except for the
1553 first one). It is ok for src == dst. */
1554
1f36328e 1555void __reg2
1870c688
CV
1556nofinalslash (const char *src, char *dst)
1557{
1558 int len = strlen (src);
1559 if (src != dst)
1560 memcpy (dst, src, len + 1);
1561 while (len > 1 && isdirsep (dst[--len]))
1562 dst[len] = '\0';
1563}
1564
1565/* conv_path_list: Convert a list of path names to/from Win32/POSIX. */
1566
1567static int
a887cf0e
CV
1568conv_path_list (const char *src, char *dst, size_t size,
1569 cygwin_conv_path_t what)
1870c688
CV
1570{
1571 tmp_pathbuf tp;
1572 char src_delim, dst_delim;
1870c688 1573 size_t len;
a887cf0e 1574 bool env_cvt = false;
1870c688 1575
a887cf0e
CV
1576 if (what == (cygwin_conv_path_t) ENV_CVT)
1577 {
1578 what = CCP_WIN_A_TO_POSIX | CCP_RELATIVE;
1579 env_cvt = true;
1580 }
5250e27f 1581 if ((what & CCP_CONVTYPE_MASK) == CCP_WIN_A_TO_POSIX)
1870c688
CV
1582 {
1583 src_delim = ';';
1584 dst_delim = ':';
1870c688
CV
1585 }
1586 else
1587 {
1588 src_delim = ':';
1589 dst_delim = ';';
1870c688 1590 }
81adfe28 1591
1870c688
CV
1592 char *srcbuf;
1593 len = strlen (src) + 1;
1594 if (len <= NT_MAX_PATH * sizeof (WCHAR))
1595 srcbuf = (char *) tp.w_get ();
1596 else
1597 srcbuf = (char *) alloca (len);
1598
1599 int err = 0;
1600 char *d = dst - 1;
1601 bool saw_empty = false;
1602 do
1603 {
cecc445d
CV
1604 char *srcpath = srcbuf;
1605 char *s = strccpy (srcpath, &src, src_delim);
1606 size_t len = s - srcpath;
1870c688
CV
1607 if (len >= NT_MAX_PATH)
1608 {
1609 err = ENAMETOOLONG;
81adfe28 1610 break;
1870c688 1611 }
cecc445d
CV
1612 /* Paths in Win32 path lists in the environment (%Path%), are often
1613 enclosed in quotes (usually paths with spaces). Trailing backslashes
1614 are common, too. Remove them. */
a887cf0e 1615 if (env_cvt && len)
cecc445d
CV
1616 {
1617 if (*srcpath == '"')
1618 {
1619 ++srcpath;
1620 *--s = '\0';
1621 len -= 2;
1622 }
1623 while (len && s[-1] == '\\')
1624 {
1625 *--s = '\0';
1626 --len;
1627 }
1628 }
1870c688
CV
1629 if (len)
1630 {
1631 ++d;
a887cf0e 1632 err = cygwin_conv_path (what, srcpath, d, size - (d - dst));
1870c688 1633 }
a887cf0e 1634 else if ((what & CCP_CONVTYPE_MASK) == CCP_POSIX_TO_WIN_A)
1870c688
CV
1635 {
1636 ++d;
a887cf0e 1637 err = cygwin_conv_path (what, ".", d, size - (d - dst));
1870c688
CV
1638 }
1639 else
1640 {
a887cf0e 1641 if (env_cvt)
1870c688
CV
1642 saw_empty = true;
1643 continue;
1644 }
1645 if (err)
1646 break;
1647 d = strchr (d, '\0');
1648 *d = dst_delim;
1649 }
1650 while (*src++);
1651
1652 if (saw_empty)
1653 err = EIDRM;
1654
1655 if (d < dst)
1656 d++;
1657 *d = '\0';
1658 return err;
1659}
1660
1661/********************** Symbolic Link Support **************************/
1662
1663/* Create a symlink from FROMPATH to TOPATH. */
1664
1870c688
CV
1665extern "C" int
1666symlink (const char *oldpath, const char *newpath)
1667{
4bfa93f1
CV
1668 path_conv win32_newpath;
1669
1670 __try
1671 {
1672 if (!*oldpath || !*newpath)
1673 {
1674 set_errno (ENOENT);
1675 __leave;
1676 }
1677
1678 /* Trailing dirsep is a no-no, only errno differs. */
1679 bool has_trailing_dirsep = isdirsep (newpath[strlen (newpath) - 1]);
1680 win32_newpath.check (newpath,
1681 PC_SYM_NOFOLLOW | PC_SYM_NOFOLLOW_DIR | PC_POSIX,
1682 stat_suffixes);
1683
1684 if (win32_newpath.error || has_trailing_dirsep)
1685 {
1686 set_errno (win32_newpath.error ?:
1687 win32_newpath.exists () ? EEXIST : ENOENT);
1688 __leave;
1689 }
1690
1691 return symlink_worker (oldpath, win32_newpath, false);
1692 }
1693 __except (EFAULT) {}
1694 __endtry
1695 return -1;
9ecd475c
CV
1696}
1697
1698static int
1699symlink_nfs (const char *oldpath, path_conv &win32_newpath)
1700{
1701 /* On NFS, create symlinks by calling NtCreateFile with an EA of type
1702 NfsSymlinkTargetName containing ... the symlink target name. */
1703 tmp_pathbuf tp;
1704 PFILE_FULL_EA_INFORMATION pffei;
1705 NTSTATUS status;
1706 HANDLE fh;
1707 OBJECT_ATTRIBUTES attr;
1708 IO_STATUS_BLOCK io;
1709
1710 pffei = (PFILE_FULL_EA_INFORMATION) tp.w_get ();
1711 pffei->NextEntryOffset = 0;
1712 pffei->Flags = 0;
1713 pffei->EaNameLength = sizeof (NFS_SYML_TARGET) - 1;
1714 char *EaValue = stpcpy (pffei->EaName, NFS_SYML_TARGET) + 1;
1715 pffei->EaValueLength = sizeof (WCHAR) *
1716 (sys_mbstowcs ((PWCHAR) EaValue, NT_MAX_PATH, oldpath) - 1);
1717 status = NtCreateFile (&fh, FILE_WRITE_DATA | FILE_WRITE_EA | SYNCHRONIZE,
1718 win32_newpath.get_object_attr (attr, sec_none_nih),
1719 &io, NULL, FILE_ATTRIBUTE_SYSTEM,
1720 FILE_SHARE_VALID_FLAGS, FILE_CREATE,
1721 FILE_SYNCHRONOUS_IO_NONALERT
1722 | FILE_OPEN_FOR_BACKUP_INTENT,
1723 pffei, NT_MAX_PATH * sizeof (WCHAR));
1724 if (!NT_SUCCESS (status))
1725 {
1726 __seterrno_from_nt_status (status);
1727 return -1;
1728 }
1729 NtClose (fh);
1730 return 0;
1731}
1732
2566c2e6
CV
1733/* Count backslashes between s and e. */
1734static inline int
1735cnt_bs (PWCHAR s, PWCHAR e)
1736{
1737 int num = 0;
1738
1739 while (s < e)
1740 if (*s++ == L'\\')
1741 ++num;
1742 return num;
1743}
1744
ffcfbf4b
CV
1745#ifndef SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
1746#define SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE 2
1747#endif
1748
9ecd475c
CV
1749static int
1750symlink_native (const char *oldpath, path_conv &win32_newpath)
1751{
1752 tmp_pathbuf tp;
1753 path_conv win32_oldpath;
1754 PUNICODE_STRING final_oldpath, final_newpath;
2566c2e6 1755 UNICODE_STRING final_oldpath_buf;
ffcfbf4b 1756 DWORD flags;
9ecd475c
CV
1757
1758 if (isabspath (oldpath))
1759 {
1760 win32_oldpath.check (oldpath, PC_SYM_NOFOLLOW, stat_suffixes);
1761 final_oldpath = win32_oldpath.get_nt_native_path ();
9ecd475c
CV
1762 }
1763 else
1764 {
1765 /* The symlink target is relative to the directory in which
1766 the symlink gets created, not relative to the cwd. Therefore
1767 we have to mangle the path quite a bit before calling path_conv. */
d2bd82aa
CV
1768 ssize_t len = strrchr (win32_newpath.get_posix (), '/')
1769 - win32_newpath.get_posix () + 1;
9ecd475c 1770 char *absoldpath = tp.t_get ();
d2bd82aa 1771 stpcpy (stpncpy (absoldpath, win32_newpath.get_posix (), len),
9ecd475c
CV
1772 oldpath);
1773 win32_oldpath.check (absoldpath, PC_SYM_NOFOLLOW, stat_suffixes);
2566c2e6
CV
1774
1775 /* Try hard to keep Windows symlink path relative. */
1776
75565231
CV
1777 /* 1. Find common path prefix. Skip leading \\?\, but take pre-increment
1778 of the following loop into account. */
1779 PWCHAR c_old = win32_oldpath.get_nt_native_path ()->Buffer + 3;
1780 PWCHAR c_new = win32_newpath.get_nt_native_path ()->Buffer + 3;
1781 /* Windows compatible == always check case insensitive. */
1782 while (towupper (*++c_old) == towupper (*++c_new))
2566c2e6
CV
1783 ;
1784 /* The last component could share a common prefix, so make sure we end
1785 up on the first char after the last common backslash. */
1786 while (c_old[-1] != L'\\')
1787 --c_old, --c_new;
1788
1789 /* 2. Check if prefix is long enough. The prefix must at least points to
1790 a complete device: \\?\X:\ or \\?\UNC\server\share\ are the minimum
1791 prefix strings. We start counting behind the \\?\ for speed. */
1792 int num = cnt_bs (win32_oldpath.get_nt_native_path ()->Buffer + 4, c_old);
1793 if (num < 1 /* locale drive. */
2f4fef95 1794 || (win32_oldpath.get_nt_native_path ()->Buffer[5] != L':'
2566c2e6
CV
1795 && num < 3)) /* UNC path. */
1796 {
1797 /* 3a. No valid common path prefix: Create absolute symlink. */
1798 final_oldpath = win32_oldpath.get_nt_native_path ();
2566c2e6
CV
1799 }
1800 else
1801 {
1802 /* 3b. Common path prefx. Count number of additional directories
1803 in symlink's path, and prepend as much ".." path components
1804 to the target path. */
1805 PWCHAR e_new = win32_newpath.get_nt_native_path ()->Buffer
1806 + win32_newpath.get_nt_native_path ()->Length
1807 / sizeof (WCHAR);
1808 num = cnt_bs (c_new, e_new);
1809 final_oldpath = &final_oldpath_buf;
1810 final_oldpath->Buffer = tp.w_get ();
1811 PWCHAR e_old = final_oldpath->Buffer;
1812 while (num-- > 0)
1813 e_old = wcpcpy (e_old, L"..\\");
1814 wcpcpy (e_old, c_old);
1815 }
9ecd475c 1816 }
33cb946e
CV
1817 /* If the symlink target doesn't exist, don't create native symlink.
1818 Otherwise the directory flag in the symlink is potentially wrong
1819 when the target comes into existence, and native tools will fail.
1820 This is so screwball. This is no problem on AFS, fortunately. */
1821 if (!win32_oldpath.exists () && !win32_oldpath.fs_is_afs ())
1822 {
1823 SetLastError (ERROR_FILE_NOT_FOUND);
1824 return -1;
1825 }
8cdd7bad
CV
1826 /* Don't allow native symlinks to Cygwin special files. However, the
1827 caller shoud know because this case shouldn't be covered by the
1828 default "nativestrict" behaviour, so we use a special return code. */
1829 if (win32_oldpath.isspecial ())
1830 return -2;
7630387c 1831 /* Convert native paths to Win32 UNC paths. */
9ecd475c 1832 final_newpath = win32_newpath.get_nt_native_path ();
9ecd475c 1833 final_newpath->Buffer[1] = L'\\';
7630387c
CV
1834 /* oldpath may be relative. Make sure to convert only absolute paths
1835 to Win32 paths. */
1836 if (final_oldpath->Buffer[0] == L'\\')
1837 {
11b7f942
CV
1838 /* Starting with Windows 8.1, the ShellExecuteW function does not
1839 handle the long path prefix correctly for symlink targets. Thus,
1840 we create simple short paths < MAX_PATH without long path prefix. */
7630387c
CV
1841 if (RtlEqualUnicodePathPrefix (final_oldpath, &ro_u_uncp, TRUE)
1842 && final_oldpath->Length < (MAX_PATH + 6) * sizeof (WCHAR))
1843 {
1844 final_oldpath->Buffer += 6;
1845 final_oldpath->Buffer[0] = L'\\';
1846 }
1847 else if (final_oldpath->Length < (MAX_PATH + 4) * sizeof (WCHAR))
1848 final_oldpath->Buffer += 4;
1849 else /* Stick to long path, fix native prefix for Win32 API calls. */
1850 final_oldpath->Buffer[1] = L'\\';
1851 }
33cb946e 1852 /* Try to create native symlink. */
ffcfbf4b
CV
1853 flags = win32_oldpath.isdir () ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0;
1854 if (wincap.has_unprivileged_createsymlink ())
1855 flags |= SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
9ecd475c 1856 if (!CreateSymbolicLinkW (final_newpath->Buffer, final_oldpath->Buffer,
ffcfbf4b 1857 flags))
9ecd475c 1858 {
7630387c 1859 /* Repair native newpath, we still need it. */
9ecd475c
CV
1860 final_newpath->Buffer[1] = L'?';
1861 return -1;
1862 }
1863 return 0;
1870c688
CV
1864}
1865
44da5e4b
CV
1866#ifndef IO_REPARSE_TAG_LX_SYMLINK
1867#define IO_REPARSE_TAG_LX_SYMLINK (0xa000001d)
1868#endif
1869
1870typedef struct _REPARSE_LX_SYMLINK_BUFFER
1871{
1872 DWORD ReparseTag;
1873 WORD ReparseDataLength;
1874 WORD Reserved;
1875 struct {
1876 DWORD FileType; /* Take member name with a grain of salt. Value is
1877 apparently always 2 for symlinks. */
1878 char PathBuffer[1];/* POSIX path as given to symlink(2).
1879 Path is not \0 terminated.
1880 Length is ReparseDataLength - sizeof (FileType).
1881 Always UTF-8.
1882 Chars given in incompatible codesets, e. g. umlauts
1883 in ISO-8859-x, are converted to the Unicode
1884 REPLACEMENT CHARACTER 0xfffd == \xef\xbf\bd */
1885 } LxSymlinkReparseBuffer;
1886} REPARSE_LX_SYMLINK_BUFFER,*PREPARSE_LX_SYMLINK_BUFFER;
1887
1888static int
1889symlink_wsl (const char *oldpath, path_conv &win32_newpath)
1890{
1891 tmp_pathbuf tp;
1892 PREPARSE_LX_SYMLINK_BUFFER rpl = (PREPARSE_LX_SYMLINK_BUFFER) tp.c_get ();
1893 char *path_buf = rpl->LxSymlinkReparseBuffer.PathBuffer;
1894 const int max_pathlen = MAXIMUM_REPARSE_DATA_BUFFER_SIZE
1895 - offsetof (REPARSE_LX_SYMLINK_BUFFER,
1896 LxSymlinkReparseBuffer.PathBuffer);
1897 PWCHAR utf16 = tp.w_get ();
1898 NTSTATUS status;
1899 IO_STATUS_BLOCK io;
1900 OBJECT_ATTRIBUTES attr;
1901 HANDLE fh;
1902 int len;
1903
1904 rpl->ReparseTag = IO_REPARSE_TAG_LX_SYMLINK;
1905 rpl->Reserved = 0;
1906 rpl->LxSymlinkReparseBuffer.FileType = 2;
6b979620
CV
1907 /* Convert cygdrive prefix to "/mnt" for WSL compatibility, but only if
1908 cygdrive prefix is not "/", otherwise suffer random "/mnt" symlinks... */
1909 if (mount_table->cygdrive_len > 1
1910 && path_prefix_p (mount_table->cygdrive, oldpath,
1911 mount_table->cygdrive_len, false))
44da5e4b
CV
1912 stpcpy (stpcpy (path_buf, "/mnt"),
1913 oldpath + mount_table->cygdrive_len - 1);
1914 else
1915 *stpncpy (path_buf, oldpath, max_pathlen) = '\0';
1916 /* Convert target path to UTF-16 and then back to UTF-8 to make sure the
6b979620 1917 WSL symlink is in UTF-8, independent of the current Cygwin codeset. */
44da5e4b
CV
1918 sys_mbstowcs (utf16, NT_MAX_PATH, path_buf);
1919 len = WideCharToMultiByte (CP_UTF8, 0, utf16, -1, path_buf, max_pathlen,
1920 NULL, NULL);
1921 /* Length is omitting trailing \0. */
1922 rpl->ReparseDataLength = sizeof (DWORD) + len - 1;
1923 /* Create reparse point. */
1924 status = NtCreateFile (&fh, DELETE | FILE_GENERIC_WRITE
1925 | READ_CONTROL | WRITE_DAC,
1926 win32_newpath.get_object_attr (attr, sec_none_nih),
1927 &io, NULL, FILE_ATTRIBUTE_NORMAL,
1928 FILE_SHARE_VALID_FLAGS, FILE_CREATE,
1929 FILE_SYNCHRONOUS_IO_NONALERT
1930 | FILE_NON_DIRECTORY_FILE
1931 | FILE_OPEN_FOR_BACKUP_INTENT
1932 | FILE_OPEN_REPARSE_POINT,
1933 NULL, 0);
1934 if (!NT_SUCCESS (status))
1935 {
1936 SetLastError (RtlNtStatusToDosError (status));
1937 return -1;
1938 }
1939 set_created_file_access (fh, win32_newpath, S_IFLNK | STD_RBITS | STD_WBITS);
1940 status = NtFsControlFile (fh, NULL, NULL, NULL, &io, FSCTL_SET_REPARSE_POINT,
1941 (LPVOID) rpl, REPARSE_DATA_BUFFER_HEADER_SIZE
1942 + rpl->ReparseDataLength,
1943 NULL, 0);
1944 if (!NT_SUCCESS (status))
1945 {
1946 SetLastError (RtlNtStatusToDosError (status));
1947 FILE_DISPOSITION_INFORMATION fdi = { TRUE };
1948 status = NtSetInformationFile (fh, &io, &fdi, sizeof fdi,
1949 FileDispositionInformation);
1950 NtClose (fh);
1951 if (!NT_SUCCESS (status))
1952 debug_printf ("Setting delete dispostion failed, status = %y", status);
1953 return -1;
1954 }
1955 NtClose (fh);
1956 return 0;
1957}
1958
1870c688 1959int
4bfa93f1 1960symlink_worker (const char *oldpath, path_conv &win32_newpath, bool isdevice)
1870c688
CV
1961{
1962 int res = -1;
1963 size_t len;
1870c688 1964 char *buf, *cp;
1870c688 1965 tmp_pathbuf tp;
9ecd475c 1966 winsym_t wsym_type;
1870c688
CV
1967
1968 /* POSIX says that empty 'newpath' is invalid input while empty
1969 'oldpath' is valid -- it's symlink resolver job to verify if
1970 symlink contents point to existing filesystem object */
3f3bd101 1971 __try
1870c688 1972 {
3f3bd101
CV
1973 if (strlen (oldpath) > SYMLINK_MAX)
1974 {
1975 set_errno (ENAMETOOLONG);
1976 __leave;
1977 }
81adfe28 1978
3f3bd101
CV
1979 /* Default symlink type is determined by global allow_winsymlinks
1980 variable. Device files are always shortcuts. */
1981 wsym_type = isdevice ? WSYM_lnk : allow_winsymlinks;
1982 /* NFS has its own, dedicated way to create symlinks. */
1983 if (win32_newpath.fs_is_nfs ())
1984 wsym_type = WSYM_nfs;
1985 /* MVFS doesn't handle the SYSTEM DOS attribute, but it handles the R/O
1986 attribute. Therefore we create symlinks on MVFS always as shortcuts. */
1987 else if (win32_newpath.fs_is_mvfs ())
1988 wsym_type = WSYM_lnk;
1989 /* AFS only supports native symlinks. */
1990 else if (win32_newpath.fs_is_afs ())
67fd2101 1991 wsym_type = WSYM_nativestrict;
3f3bd101
CV
1992 /* Don't try native symlinks on FSes not supporting reparse points. */
1993 else if ((wsym_type == WSYM_native || wsym_type == WSYM_nativestrict)
1994 && !(win32_newpath.fs_flags () & FILE_SUPPORTS_REPARSE_POINTS))
1995 wsym_type = WSYM_sysfile;
1996
1997 /* Attach .lnk suffix when shortcut is requested. */
1998 if (wsym_type == WSYM_lnk && !win32_newpath.exists ()
1999 && (isdevice || !win32_newpath.fs_is_nfs ()))
2000 {
2001 char *newplnk = tp.c_get ();
4bfa93f1 2002 stpcpy (stpcpy (newplnk, win32_newpath.get_posix ()), ".lnk");
13bfb3c6 2003 win32_newpath.check (newplnk, PC_SYM_NOFOLLOW | PC_POSIX);
3f3bd101 2004 }
81adfe28 2005
3f3bd101 2006 if (win32_newpath.error)
1870c688 2007 {
3f3bd101
CV
2008 set_errno (win32_newpath.error);
2009 __leave;
1870c688 2010 }
81adfe28 2011
3f3bd101
CV
2012 syscall_printf ("symlink (%s, %S) wsym_type %d", oldpath,
2013 win32_newpath.get_nt_native_path (), wsym_type);
1870c688 2014
3f3bd101 2015 if ((!isdevice && win32_newpath.exists ())
fb5ce26c 2016 || (win32_newpath.isdevice () && !win32_newpath.is_fs_special ()))
1870c688 2017 {
3f3bd101
CV
2018 set_errno (EEXIST);
2019 __leave;
2020 }
1870c688 2021
44da5e4b 2022 /* Handle NFS, native symlinks and WSL symlinks in their own functions. */
3f3bd101
CV
2023 switch (wsym_type)
2024 {
2025 case WSYM_nfs:
2026 res = symlink_nfs (oldpath, win32_newpath);
2027 __leave;
2028 case WSYM_native:
2029 case WSYM_nativestrict:
2030 res = symlink_native (oldpath, win32_newpath);
2031 if (!res)
2032 __leave;
8cdd7bad
CV
2033 /* Strictly native? Too bad, unless the target is a Cygwin
2034 special file. */
2035 if (res == -1 && wsym_type == WSYM_nativestrict)
1870c688 2036 {
3f3bd101
CV
2037 __seterrno ();
2038 __leave;
1870c688 2039 }
3f3bd101
CV
2040 /* Otherwise, fall back to default symlink type. */
2041 wsym_type = WSYM_sysfile;
50ad1980 2042 fallthrough;
44da5e4b
CV
2043 case WSYM_sysfile:
2044 if (win32_newpath.fs_flags () & FILE_SUPPORTS_REPARSE_POINTS)
2045 {
2046 res = symlink_wsl (oldpath, win32_newpath);
2047 if (!res)
2048 __leave;
2049 }
2050 /* On FSes not supporting reparse points, or in case of an error
2051 creating the WSL symlink, fall back to creating the plain old
2052 SYSTEM file symlink. */
3f3bd101
CV
2053 break;
2054 default:
2055 break;
2056 }
2057
2058 if (wsym_type == WSYM_lnk)
2059 {
2060 path_conv win32_oldpath;
2061 ITEMIDLIST *pidl = NULL;
2062 size_t full_len = 0;
2063 unsigned short oldpath_len, desc_len, relpath_len, pidl_len = 0;
2064 char desc[MAX_PATH + 1], *relpath;
2065
2066 if (!isdevice)
1870c688 2067 {
3f3bd101
CV
2068 /* First create an IDLIST to learn how big our shortcut is
2069 going to be. */
2070 IShellFolder *psl;
2071
2072 /* The symlink target is relative to the directory in which the
2073 symlink gets created, not relative to the cwd. Therefore we
2074 have to mangle the path quite a bit before calling path_conv.*/
2075 if (isabspath (oldpath))
2076 win32_oldpath.check (oldpath,
2077 PC_SYM_NOFOLLOW,
2078 stat_suffixes);
2079 else
2080 {
d2bd82aa
CV
2081 len = strrchr (win32_newpath.get_posix (), '/')
2082 - win32_newpath.get_posix () + 1;
3f3bd101 2083 char *absoldpath = tp.t_get ();
d2bd82aa 2084 stpcpy (stpncpy (absoldpath, win32_newpath.get_posix (),
3f3bd101
CV
2085 len),
2086 oldpath);
2087 win32_oldpath.check (absoldpath, PC_SYM_NOFOLLOW,
2088 stat_suffixes);
2089 }
2090 if (SUCCEEDED (SHGetDesktopFolder (&psl)))
1870c688 2091 {
3f3bd101
CV
2092 WCHAR wc_path[win32_oldpath.get_wide_win32_path_len () + 1];
2093 win32_oldpath.get_wide_win32_path (wc_path);
2094 /* Amazing but true: Even though the ParseDisplayName method
2095 takes a wide char path name, it does not understand the
2096 Win32 prefix for long pathnames! So we have to tack off
2097 the prefix and convert the path to the "normal" syntax
2098 for ParseDisplayName. */
3e80cefb 2099 PWCHAR wc = wc_path + 4;
3f3bd101
CV
2100 if (wc[1] != L':') /* native UNC path */
2101 *(wc += 2) = L'\\';
2102 HRESULT res;
2103 if (SUCCEEDED (res = psl->ParseDisplayName (NULL, NULL, wc,
2104 NULL, &pidl,
2105 NULL)))
2106 {
2107 ITEMIDLIST *p;
81adfe28 2108
3f3bd101
CV
2109 for (p = pidl; p->mkid.cb > 0;
2110 p = (ITEMIDLIST *)((char *) p + p->mkid.cb))
2111 ;
2112 pidl_len = (char *) p - (char *) pidl + 2;
2113 }
2114 psl->Release ();
1870c688 2115 }
1870c688 2116 }
3f3bd101
CV
2117 /* Compute size of shortcut file. */
2118 full_len = sizeof (win_shortcut_hdr);
2119 if (pidl_len)
2120 full_len += sizeof (unsigned short) + pidl_len;
2121 oldpath_len = strlen (oldpath);
2122 /* Unfortunately the length of the description is restricted to a
2123 length of 2000 bytes. We don't want to add considerations for
2124 the different lengths and even 2000 bytes is not enough for long
2125 path names. So what we do here is to set the description to the
2126 POSIX path only if the path is not longer than MAX_PATH characters.
2127 We append the full path name after the regular shortcut data
2128 (see below), which works fine with Windows Explorer as well
2129 as older Cygwin versions (as long as the whole file isn't bigger
2130 than 8K). The description field is only used for backward
2131 compatibility to older Cygwin versions and those versions are
2132 not capable of handling long path names anyway. */
2133 desc_len = stpcpy (desc, oldpath_len > MAX_PATH
2134 ? "[path too long]" : oldpath) - desc;
2135 full_len += sizeof (unsigned short) + desc_len;
2136 /* Devices get the oldpath string unchanged as relative path. */
2137 if (isdevice)
2138 {
2139 relpath_len = oldpath_len;
2140 stpcpy (relpath = tp.c_get (), oldpath);
2141 }
2142 else
2143 {
2144 relpath_len = strlen (win32_oldpath.get_win32 ());
2145 stpcpy (relpath = tp.c_get (), win32_oldpath.get_win32 ());
2146 }
2147 full_len += sizeof (unsigned short) + relpath_len;
2148 full_len += sizeof (unsigned short) + oldpath_len;
2149 /* 1 byte more for trailing 0 written by stpcpy. */
2150 if (full_len < NT_MAX_PATH * sizeof (WCHAR))
2151 buf = tp.t_get ();
2152 else
2153 buf = (char *) alloca (full_len + 1);
2154
2155 /* Create shortcut header */
2156 win_shortcut_hdr *shortcut_header = (win_shortcut_hdr *) buf;
2157 memset (shortcut_header, 0, sizeof *shortcut_header);
2158 shortcut_header->size = sizeof *shortcut_header;
2159 shortcut_header->magic = GUID_shortcut;
2160 shortcut_header->flags = (WSH_FLAG_DESC | WSH_FLAG_RELPATH);
2161 if (pidl)
2162 shortcut_header->flags |= WSH_FLAG_IDLIST;
2163 shortcut_header->run = SW_NORMAL;
2164 cp = buf + sizeof (win_shortcut_hdr);
2165
2166 /* Create IDLIST */
2167 if (pidl)
2168 {
2169 *(unsigned short *)cp = pidl_len;
2170 memcpy (cp += 2, pidl, pidl_len);
2171 cp += pidl_len;
2172 CoTaskMemFree (pidl);
2173 }
2174
2175 /* Create description */
2176 *(unsigned short *)cp = desc_len;
2177 cp = stpcpy (cp += 2, desc);
2178
2179 /* Create relpath */
2180 *(unsigned short *)cp = relpath_len;
2181 cp = stpcpy (cp += 2, relpath);
2182
2183 /* Append the POSIX path after the regular shortcut data for
2184 the long path support. */
2185 unsigned short *plen = (unsigned short *) cp;
2186 cp += 2;
2187 *(PWCHAR) cp = 0xfeff; /* BOM */
2188 cp += 2;
2189 *plen = sys_mbstowcs ((PWCHAR) cp, NT_MAX_PATH, oldpath)
2190 * sizeof (WCHAR);
2191 cp += *plen;
1870c688
CV
2192 }
2193 else
2194 {
3f3bd101
CV
2195 /* Default technique creating a symlink. */
2196 buf = tp.t_get ();
2197 cp = stpcpy (buf, SYMLINK_COOKIE);
2198 *(PWCHAR) cp = 0xfeff; /* BOM */
2199 cp += 2;
2200 /* Note that the terminating nul is written. */
2201 cp += sys_mbstowcs ((PWCHAR) cp, NT_MAX_PATH, oldpath)
2202 * sizeof (WCHAR);
1870c688 2203 }
81adfe28 2204
3f3bd101
CV
2205 OBJECT_ATTRIBUTES attr;
2206 IO_STATUS_BLOCK io;
2207 NTSTATUS status;
2208 ULONG access;
2209 HANDLE fh;
9ecd475c 2210
3f3bd101
CV
2211 access = DELETE | FILE_GENERIC_WRITE;
2212 if (isdevice && win32_newpath.exists ())
2213 {
2214 status = NtOpenFile (&fh, FILE_WRITE_ATTRIBUTES,
2215 win32_newpath.get_object_attr (attr,
2216 sec_none_nih),
2217 &io, 0, FILE_OPEN_FOR_BACKUP_INTENT);
2218 if (!NT_SUCCESS (status))
2219 {
2220 __seterrno_from_nt_status (status);
2221 __leave;
2222 }
2223 status = NtSetAttributesFile (fh, FILE_ATTRIBUTE_NORMAL);
2224 NtClose (fh);
2225 if (!NT_SUCCESS (status))
2226 {
2227 __seterrno_from_nt_status (status);
2228 __leave;
2229 }
2230 }
2231 else if (!isdevice && win32_newpath.has_acls ()
2232 && !win32_newpath.isremote ())
2233 /* If the filesystem supports ACLs, we will overwrite the DACL after the
2234 call to NtCreateFile. This requires a handle with READ_CONTROL and
2235 WRITE_DAC access, otherwise get_file_sd and set_file_sd both have to
2236 open the file again.
2237 FIXME: On remote NTFS shares open sometimes fails because even the
2238 creator of the file doesn't have the right to change the DACL.
2239 I don't know what setting that is or how to recognize such a share,
2240 so for now we don't request WRITE_DAC on remote drives. */
2241 access |= READ_CONTROL | WRITE_DAC;
2242
2243 status = NtCreateFile (&fh, access,
2244 win32_newpath.get_object_attr (attr, sec_none_nih),
2245 &io, NULL, FILE_ATTRIBUTE_NORMAL,
2246 FILE_SHARE_VALID_FLAGS,
2247 isdevice ? FILE_OVERWRITE_IF : FILE_CREATE,
2248 FILE_SYNCHRONOUS_IO_NONALERT
2249 | FILE_NON_DIRECTORY_FILE
2250 | FILE_OPEN_FOR_BACKUP_INTENT,
2251 NULL, 0);
1870c688
CV
2252 if (!NT_SUCCESS (status))
2253 {
2254 __seterrno_from_nt_status (status);
3f3bd101 2255 __leave;
1870c688 2256 }
bc444e5a
CV
2257 if (io.Information == FILE_CREATED && win32_newpath.has_acls ())
2258 set_created_file_access (fh, win32_newpath,
2259 S_IFLNK | STD_RBITS | STD_WBITS);
3f3bd101
CV
2260 status = NtWriteFile (fh, NULL, NULL, NULL, &io, buf, cp - buf,
2261 NULL, NULL);
2262 if (NT_SUCCESS (status) && io.Information == (ULONG) (cp - buf))
2263 {
2264 status = NtSetAttributesFile (fh, wsym_type == WSYM_lnk
2265 ? FILE_ATTRIBUTE_READONLY
2266 : FILE_ATTRIBUTE_SYSTEM);
2267 if (!NT_SUCCESS (status))
2268 debug_printf ("Setting attributes failed, status = %y", status);
2269 res = 0;
2270 }
2271 else
1870c688
CV
2272 {
2273 __seterrno_from_nt_status (status);
3f3bd101
CV
2274 FILE_DISPOSITION_INFORMATION fdi = { TRUE };
2275 status = NtSetInformationFile (fh, &io, &fdi, sizeof fdi,
2276 FileDispositionInformation);
2277 if (!NT_SUCCESS (status))
2278 debug_printf ("Setting delete dispostion failed, status = %y",
2279 status);
1870c688 2280 }
3f3bd101 2281 NtClose (fh);
69d7815e 2282
1870c688 2283 }
3f3bd101
CV
2284 __except (EFAULT) {}
2285 __endtry
9ecd475c 2286 syscall_printf ("%d = symlink_worker(%s, %s, %d)",
4bfa93f1 2287 res, oldpath, win32_newpath.get_posix (), isdevice);
1870c688
CV
2288 return res;
2289}
2290
2291static bool
2292cmp_shortcut_header (win_shortcut_hdr *file_header)
2293{
2294 /* A Cygwin or U/Win shortcut only contains a description and a relpath.
2295 Cygwin shortcuts also might contain an ITEMIDLIST. The run type is
2296 always set to SW_NORMAL. */
2297 return file_header->size == sizeof (win_shortcut_hdr)
2298 && !memcmp (&file_header->magic, &GUID_shortcut, sizeof GUID_shortcut)
2299 && (file_header->flags & ~WSH_FLAG_IDLIST)
2300 == (WSH_FLAG_DESC | WSH_FLAG_RELPATH)
2301 && file_header->run == SW_NORMAL;
2302}
2303
2304int
5a0d1edb 2305symlink_info::check_shortcut (HANDLE h)
1870c688
CV
2306{
2307 tmp_pathbuf tp;
2308 win_shortcut_hdr *file_header;
2309 char *buf, *cp;
2310 unsigned short len;
2311 int res = 0;
1870c688 2312 NTSTATUS status;
1870c688
CV
2313 IO_STATUS_BLOCK io;
2314 FILE_STANDARD_INFORMATION fsi;
5a0d1edb 2315 LARGE_INTEGER off = { QuadPart:0LL };
1870c688 2316
1870c688
CV
2317 status = NtQueryInformationFile (h, &io, &fsi, sizeof fsi,
2318 FileStandardInformation);
2319 if (!NT_SUCCESS (status))
2320 {
2321 set_error (EIO);
5a0d1edb 2322 return 0;
1870c688 2323 }
61522196 2324 if (fsi.EndOfFile.QuadPart <= (LONGLONG) sizeof (win_shortcut_hdr)
1870c688 2325 || fsi.EndOfFile.QuadPart > 4 * 65536)
5a0d1edb 2326 return 0;
1870c688
CV
2327 if (fsi.EndOfFile.LowPart < NT_MAX_PATH * sizeof (WCHAR))
2328 buf = (char *) tp.w_get ();
2329 else
2330 buf = (char *) alloca (fsi.EndOfFile.LowPart + 1);
5a0d1edb
CV
2331 status = NtReadFile (h, NULL, NULL, NULL, &io, buf, fsi.EndOfFile.LowPart,
2332 &off, NULL);
5a0d1edb 2333 if (!NT_SUCCESS (status))
1870c688 2334 {
5a0d1edb
CV
2335 if (status != STATUS_END_OF_FILE)
2336 set_error (EIO);
2337 return 0;
1870c688
CV
2338 }
2339 file_header = (win_shortcut_hdr *) buf;
2340 if (io.Information != fsi.EndOfFile.LowPart
2341 || !cmp_shortcut_header (file_header))
5a0d1edb 2342 return 0;
1870c688
CV
2343 cp = buf + sizeof (win_shortcut_hdr);
2344 if (file_header->flags & WSH_FLAG_IDLIST) /* Skip ITEMIDLIST */
2345 cp += *(unsigned short *) cp + 2;
2346 if (!(len = *(unsigned short *) cp))
5a0d1edb 2347 return 0;
1870c688
CV
2348 cp += 2;
2349 /* Check if this is a device file - these start with the sequence :\\ */
2350 if (strncmp (cp, ":\\", 2) == 0)
2351 res = strlen (strcpy (contents, cp)); /* Don't mess with device files */
2352 else
2353 {
2354 /* Has appended full path? If so, use it instead of description. */
2355 unsigned short relpath_len = *(unsigned short *) (cp + len);
2356 if (cp + len + 2 + relpath_len < buf + fsi.EndOfFile.LowPart)
2357 {
2358 cp += len + 2 + relpath_len;
2359 len = *(unsigned short *) cp;
2360 cp += 2;
2361 }
4d9c7280
CV
2362 if (*(PWCHAR) cp == 0xfeff) /* BOM */
2363 {
2364 char *tmpbuf = tp.c_get ();
2365 if (sys_wcstombs (tmpbuf, NT_MAX_PATH, (PWCHAR) (cp + 2))
ef750173 2366 > SYMLINK_MAX)
5a0d1edb 2367 return 0;
4d9c7280
CV
2368 res = posixify (tmpbuf);
2369 }
2370 else if (len > SYMLINK_MAX)
5a0d1edb 2371 return 0;
4d9c7280
CV
2372 else
2373 {
2374 cp[len] = '\0';
2375 res = posixify (cp);
2376 }
1870c688
CV
2377 }
2378 if (res) /* It's a symlink. */
c1023ee3 2379 path_flags |= PATH_SYMLINK | PATH_LNK;
58402a3f 2380 return res;
1870c688
CV
2381}
2382
2383int
5a0d1edb 2384symlink_info::check_sysfile (HANDLE h)
1870c688 2385{
4d9c7280 2386 tmp_pathbuf tp;
1870c688 2387 char cookie_buf[sizeof (SYMLINK_COOKIE) - 1];
4d9c7280 2388 char *srcbuf = tp.c_get ();
1870c688 2389 int res = 0;
1870c688 2390 NTSTATUS status;
1870c688 2391 IO_STATUS_BLOCK io;
2671595b 2392 bool interix_symlink = false;
5a0d1edb 2393 LARGE_INTEGER off = { QuadPart:0LL };
1870c688 2394
5a0d1edb
CV
2395 status = NtReadFile (h, NULL, NULL, NULL, &io, cookie_buf,
2396 sizeof (cookie_buf), &off, NULL);
1870c688 2397 if (!NT_SUCCESS (status))
1870c688 2398 {
61522196 2399 debug_printf ("ReadFile1 failed %y", status);
1870c688 2400 if (status != STATUS_END_OF_FILE)
81adfe28 2401 set_error (EIO);
5a0d1edb 2402 return 0;
1870c688 2403 }
5a0d1edb
CV
2404 off.QuadPart = io.Information;
2405 if (io.Information == sizeof (cookie_buf)
1870c688
CV
2406 && memcmp (cookie_buf, SYMLINK_COOKIE, sizeof (cookie_buf)) == 0)
2407 {
2408 /* It's a symlink. */
c1023ee3 2409 path_flags |= PATH_SYMLINK;
2671595b
CV
2410 }
2411 else if (io.Information == sizeof (cookie_buf)
2412 && memcmp (cookie_buf, SOCKET_COOKIE, sizeof (cookie_buf)) == 0)
c1023ee3 2413 path_flags |= PATH_SOCKET;
2671595b
CV
2414 else if (io.Information >= sizeof (INTERIX_SYMLINK_COOKIE)
2415 && memcmp (cookie_buf, INTERIX_SYMLINK_COOKIE,
2416 sizeof (INTERIX_SYMLINK_COOKIE) - 1) == 0)
2417 {
2418 /* It's an Interix symlink. */
c1023ee3 2419 path_flags |= PATH_SYMLINK;
2671595b
CV
2420 interix_symlink = true;
2421 /* Interix symlink cookies are shorter than Cygwin symlink cookies, so
b86f999a 2422 in case of an Interix symlink cooky we have read too far into the
2671595b 2423 file. Set file pointer back to the position right after the cookie. */
5a0d1edb 2424 off.QuadPart = sizeof (INTERIX_SYMLINK_COOKIE) - 1;
2671595b 2425 }
c1023ee3 2426 if (path_flags & PATH_SYMLINK)
2671595b 2427 {
1870c688 2428 status = NtReadFile (h, NULL, NULL, NULL, &io, srcbuf,
5a0d1edb 2429 NT_MAX_PATH, &off, NULL);
1870c688
CV
2430 if (!NT_SUCCESS (status))
2431 {
2432 debug_printf ("ReadFile2 failed");
2433 if (status != STATUS_END_OF_FILE)
2434 set_error (EIO);
2435 }
d2ef2331 2436 else if (*(PWCHAR) srcbuf == 0xfeff /* BOM */
2671595b 2437 || interix_symlink)
4d9c7280 2438 {
2671595b
CV
2439 /* Add trailing 0 to Interix symlink target. Skip BOM in Cygwin
2440 symlinks. */
2441 if (interix_symlink)
2442 ((PWCHAR) srcbuf)[io.Information / sizeof (WCHAR)] = L'\0';
2443 else
2444 srcbuf += 2;
4d9c7280 2445 char *tmpbuf = tp.c_get ();
2671595b 2446 if (sys_wcstombs (tmpbuf, NT_MAX_PATH, (PWCHAR) srcbuf)
ef750173 2447 > SYMLINK_MAX)
4d9c7280
CV
2448 debug_printf ("symlink string too long");
2449 else
2450 res = posixify (tmpbuf);
2451 }
1870c688
CV
2452 else if (io.Information > SYMLINK_MAX + 1)
2453 debug_printf ("symlink string too long");
2454 else
2455 res = posixify (srcbuf);
2456 }
1870c688
CV
2457 return res;
2458}
2459
3e80cefb
CV
2460static bool
2461check_reparse_point_string (PUNICODE_STRING subst)
7a4e299a
J
2462{
2463 /* Native mount points, or native non-relative symbolic links,
2464 can be treated as posix symlinks only if the SubstituteName
2465 can be converted from a native NT object namespace name to
2466 a win32 name. We only know how to convert names with two
2467 prefixes :
2468 "\??\UNC\..."
2469 "\??\X:..."
2470 Other reparse points will be treated as files or
2471 directories, not as posix symlinks.
2472 */
2473 if (RtlEqualUnicodePathPrefix (subst, &ro_u_natp, FALSE))
2474 {
42f1be58
CV
2475 if (subst->Length >= 6 * sizeof(WCHAR) && subst->Buffer[5] == L':' &&
2476 (subst->Length == 6 * sizeof(WCHAR) || subst->Buffer[6] == L'\\'))
7a4e299a 2477 return true;
42f1be58 2478 else if (subst->Length >= 8 * sizeof(WCHAR) &&
7a4e299a
J
2479 wcsncmp (subst->Buffer + 4, L"UNC\\", 4) == 0)
2480 return true;
2481 }
2482 return false;
2483}
2484
3e80cefb
CV
2485/* Return values:
2486 <0: Negative errno.
6b6dd5fe 2487 0: Not a reparse point recognized by us.
0e290489 2488 >0: Path flags for a recognized reparse point, always including PATH_REP.
3e80cefb 2489*/
1870c688 2490int
3e80cefb
CV
2491check_reparse_point_target (HANDLE h, bool remote, PREPARSE_DATA_BUFFER rp,
2492 PUNICODE_STRING psymbuf)
1870c688 2493{
1870c688
CV
2494 NTSTATUS status;
2495 IO_STATUS_BLOCK io;
1870c688 2496
855ea3fd
CV
2497 /* On remote drives or under heavy load, NtFsControlFile can return with
2498 STATUS_PENDING. If so, instead of creating an event object, just set
2499 io.Status to an invalid value and perform a minimal wait until io.Status
2500 changed. */
2501 memset (&io, 0xff, sizeof io);
2502 status = NtFsControlFile (h, NULL, NULL, NULL, &io,
2503 FSCTL_GET_REPARSE_POINT, NULL, 0, (LPVOID) rp,
1870c688 2504 MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
855ea3fd
CV
2505 if (status == STATUS_PENDING)
2506 {
2507 while (io.Status == (NTSTATUS) 0xffffffff)
d2ef2331 2508 Sleep (1L);
855ea3fd
CV
2509 status = io.Status;
2510 }
1870c688
CV
2511 if (!NT_SUCCESS (status))
2512 {
61522196 2513 debug_printf ("NtFsControlFile(FSCTL_GET_REPARSE_POINT) failed, %y",
1870c688 2514 status);
713161b2
CV
2515 /* When accessing the root dir of some remote drives (observed with
2516 OS X shares), the FILE_ATTRIBUTE_REPARSE_POINT flag is set, but
2517 the followup call to NtFsControlFile(FSCTL_GET_REPARSE_POINT)
2518 returns with STATUS_NOT_A_REPARSE_POINT. That's quite buggy, but
2519 we cope here with this scenario by not setting an error code. */
3e80cefb
CV
2520 if (status == STATUS_NOT_A_REPARSE_POINT)
2521 return 0;
2522 return -EIO;
1870c688
CV
2523 }
2524 if (rp->ReparseTag == IO_REPARSE_TAG_SYMLINK)
7a4e299a
J
2525 {
2526 /* Windows evaluates native symlink literally. If a remote symlink points
2527 to, say, C:\foo, it will be handled as if the target is the local file
2528 C:\foo. That comes in handy since that's how symlinks are treated under
2529 POSIX as well. */
3e80cefb
CV
2530 RtlInitCountedUnicodeString (psymbuf,
2531 (PWCHAR)((PBYTE) rp->SymbolicLinkReparseBuffer.PathBuffer
2532 + rp->SymbolicLinkReparseBuffer.SubstituteNameOffset),
2533 rp->SymbolicLinkReparseBuffer.SubstituteNameLength);
2534 if ((rp->SymbolicLinkReparseBuffer.Flags & SYMLINK_FLAG_RELATIVE) ||
2535 check_reparse_point_string (psymbuf))
aa467e6e 2536 return PATH_SYMLINK | PATH_REP;
7a4e299a 2537 }
855ea3fd 2538 else if (!remote && rp->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
1870c688 2539 {
73d3f9cf
CV
2540 /* Don't handle junctions on remote filesystems as symlinks. This type
2541 of reparse point is handled transparently by the OS so that the
2542 target of the junction is the remote directory it is supposed to
2543 point to. If we handle it as symlink, it will be mistreated as
2544 pointing to a dir on the local system. */
3e80cefb
CV
2545 RtlInitCountedUnicodeString (psymbuf,
2546 (PWCHAR)((PBYTE) rp->MountPointReparseBuffer.PathBuffer
2547 + rp->MountPointReparseBuffer.SubstituteNameOffset),
11788f8c 2548 rp->MountPointReparseBuffer.SubstituteNameLength);
3e80cefb 2549 if (RtlEqualUnicodePathPrefix (psymbuf, &ro_u_volume, TRUE))
73d3f9cf 2550 {
3e80cefb
CV
2551 /* Volume mount point. Not treated as symlink. The return
2552 value -EPERM is a hint for the caller to treat this as a
2553 volume mount point. */
2554 return -EPERM;
73d3f9cf 2555 }
3e80cefb 2556 if (check_reparse_point_string (psymbuf))
aa467e6e
CV
2557 return PATH_SYMLINK | PATH_REP;
2558 }
4a36897a
CV
2559 else if (rp->ReparseTag == IO_REPARSE_TAG_LX_SYMLINK)
2560 {
2561 /* WSL symlink. Problem: We have to convert the path to UTF-16 for
2562 the caller. Reparse points are 16K max. The buffer given to rp
2563 is 32K. So there's enough trailing space in the buffer to convert
2564 to UTF-16 and let psymbuf point to it. */
2565 PREPARSE_LX_SYMLINK_BUFFER rpl = (PREPARSE_LX_SYMLINK_BUFFER) rp;
2566 char *path_buf = rpl->LxSymlinkReparseBuffer.PathBuffer;
2567 DWORD path_len = rpl->ReparseDataLength - sizeof (DWORD);
fb834bee
CV
2568 bool full_path = false;
2569 const size_t drv_prefix_len = strlen ("/mnt");
4a36897a
CV
2570 PBYTE utf16_ptr;
2571 PWCHAR utf16_buf;
2572 int utf16_bufsize;
4a36897a 2573
fb834bee
CV
2574 /* 0-terminate path_buf for easier testing. */
2575 path_buf[path_len] = '\0';
2576 if (path_prefix_p ("/mnt", path_buf, drv_prefix_len, false))
2577 {
2578 size_t len = strlen (path_buf);
2579
2580 if (len <= drv_prefix_len + 1)
2581 {
2582 /* /mnt or /mnt/. Replace with cygdrive prefix. */
2583 stpcpy (path_buf, mount_table->cygdrive);
2584 path_len = mount_table->cygdrive_len;
2585 if (len == drv_prefix_len)
2586 {
2587 path_buf[mount_table->cygdrive_len - 1] = '\0';
2588 --path_len;
2589 }
2590 rp->ReparseDataLength = path_len + sizeof (DWORD);
2591 }
2592 else if (islower (path_buf[drv_prefix_len + 1])
2593 && (path_len == drv_prefix_len + 2
2594 || path_buf[drv_prefix_len + 2] == '/'))
2595 {
2596 /* Skip forward to the slash leading the drive letter.
2597 That leaves room for adding the colon. */
2598 path_buf += drv_prefix_len;
2599 path_len -= drv_prefix_len;
2600 full_path = true;
2601 }
2602 }
4a36897a
CV
2603 /* Compute buffer for path converted to UTF-16. */
2604 utf16_ptr = (PBYTE) rpl + sizeof (REPARSE_LX_SYMLINK_BUFFER)
2605 + rp->ReparseDataLength;
fb834bee
CV
2606 /* Skip \0-termination added above. */
2607 ++utf16_ptr;
2608 /* Make sure pointer is aligned */
4a36897a
CV
2609 while ((intptr_t) utf16_ptr % sizeof (WCHAR))
2610 ++utf16_ptr;
2611 utf16_buf = (PWCHAR) utf16_ptr;
2612 utf16_bufsize = NT_MAX_PATH - (utf16_buf - (PWCHAR) rpl);
fb834bee 2613 /* Now convert path to UTF-16. */
4a36897a
CV
2614 utf16_bufsize = MultiByteToWideChar (CP_UTF8, 0, path_buf, path_len,
2615 utf16_buf, utf16_bufsize);
2616 if (utf16_bufsize)
2617 {
2618 if (full_path)
2619 {
2620 utf16_buf[0] = utf16_buf[1]; /* Move drive letter to front */
2621 utf16_buf[1] = L':'; /* Add colon */
2622 }
2623 RtlInitCountedUnicodeString (psymbuf, utf16_buf,
2624 utf16_bufsize * sizeof (WCHAR));
aec64798 2625 return PATH_SYMLINK | PATH_REP | PATH_REP_NOAPI;
4a36897a
CV
2626 }
2627 return -EIO;
2628 }
aa467e6e
CV
2629 else if (rp->ReparseTag == IO_REPARSE_TAG_CYGUNIX)
2630 {
2631 PREPARSE_GUID_DATA_BUFFER rgp = (PREPARSE_GUID_DATA_BUFFER) rp;
aa467e6e 2632
1949db78 2633 if (memcmp (CYGWIN_SOCKET_GUID, &rgp->ReparseGuid, sizeof (GUID)) == 0)
0e290489 2634#ifdef __WITH_AF_UNIX
aec64798 2635 return PATH_SOCKET | PATH_REP | PATH_REP_NOAPI;
0e290489
KB
2636#else
2637 /* Recognize this as a reparse point but not as a socket. */
aec64798 2638 return PATH_REP | PATH_REP_NOAPI;
0e290489 2639#endif
1870c688 2640 }
3e80cefb
CV
2641 return 0;
2642}
2643
2644int
2645symlink_info::check_reparse_point (HANDLE h, bool remote)
2646{
2647 tmp_pathbuf tp;
2648 PREPARSE_DATA_BUFFER rp = (PREPARSE_DATA_BUFFER) tp.c_get ();
2649 UNICODE_STRING symbuf;
2650 char srcbuf[SYMLINK_MAX + 7];
2651
2652 int ret = check_reparse_point_target (h, remote, rp, &symbuf);
2653 if (ret <= 0)
3aec0f00 2654 {
3e80cefb
CV
2655 if (ret == -EIO)
2656 {
2657 set_error (EIO);
2658 return 0;
2659 }
855ea3fd
CV
2660 /* Maybe it's a reparse point, but it's certainly not one we recognize.
2661 Drop REPARSE attribute so we don't try to use the flag accidentally.
2662 It's just some arbitrary file or directory for us. */
3aec0f00 2663 fileattr &= ~FILE_ATTRIBUTE_REPARSE_POINT;
3e80cefb 2664 return ret;
3aec0f00 2665 }
aa467e6e 2666 /* ret is > 0, so it's a known reparse point, path in symbuf. */
c1023ee3 2667 path_flags |= ret;
aa467e6e 2668 if (ret & PATH_SYMLINK)
0b4beaf4
KB
2669 {
2670 sys_wcstombs (srcbuf, SYMLINK_MAX + 7, symbuf.Buffer,
2671 symbuf.Length / sizeof (WCHAR));
2672 /* A symlink is never a directory. */
2673 fileattr &= ~FILE_ATTRIBUTE_DIRECTORY;
2674 return posixify (srcbuf);
2675 }
2676 else
2677 return 0;
1870c688
CV
2678}
2679
2680int
2681symlink_info::check_nfs_symlink (HANDLE h)
2682{
2683 tmp_pathbuf tp;
2684 NTSTATUS status;
2685 IO_STATUS_BLOCK io;
2686 struct {
2687 FILE_GET_EA_INFORMATION fgei;
2688 char buf[sizeof (NFS_SYML_TARGET)];
2689 } fgei_buf;
2690 PFILE_FULL_EA_INFORMATION pffei;
2691 int res = 0;
2692
2693 /* To find out if the file is a symlink and to get the symlink target,
2694 try to fetch the NfsSymlinkTargetName EA. */
2695 fgei_buf.fgei.NextEntryOffset = 0;
2696 fgei_buf.fgei.EaNameLength = sizeof (NFS_SYML_TARGET) - 1;
2697 stpcpy (fgei_buf.fgei.EaName, NFS_SYML_TARGET);
2698 pffei = (PFILE_FULL_EA_INFORMATION) tp.w_get ();
2699 status = NtQueryEaFile (h, &io, pffei, NT_MAX_PATH * sizeof (WCHAR), TRUE,
2700 &fgei_buf.fgei, sizeof fgei_buf, NULL, TRUE);
2701 if (NT_SUCCESS (status) && pffei->EaValueLength > 0)
2702 {
2703 PWCHAR spath = (PWCHAR)
2704 (pffei->EaName + pffei->EaNameLength + 1);
2705 res = sys_wcstombs (contents, SYMLINK_MAX + 1,
ef750173 2706 spath, pffei->EaValueLength);
c1023ee3 2707 path_flags |= PATH_SYMLINK;
1870c688
CV
2708 }
2709 return res;
2710}
2711
2712int
2713symlink_info::posixify (char *srcbuf)
2714{
2715 /* The definition for a path in a native symlink is a bit weird. The Flags
2716 value seem to contain 0 for absolute paths (stored as NT native path)
2717 and 1 for relative paths. Relative paths are paths not starting with a
2718 drive letter. These are not converted to NT native, but stored as
2719 given. A path starting with a single backslash is relative to the
2720 current drive thus a "relative" value (Flags == 1).
2721 Funny enough it's possible to store paths with slashes instead of
2722 backslashes, but they are evaluated incorrectly by subsequent Windows
2723 calls like CreateFile (ERROR_INVALID_NAME). So, what we do here is to
2724 take paths starting with slashes at face value, evaluating them as
2725 Cygwin specific POSIX paths.
2726 A path starting with two slashes(!) or backslashes is converted into an
2727 NT UNC path. Unfortunately, in contrast to POSIX rules, paths starting
2728 with three or more (back)slashes are also converted into UNC paths,
a413f8a2 2729 just incorrectly sticking to one redundant leading backslash. We go
1870c688
CV
2730 along with this behaviour to avoid scenarios in which native tools access
2731 other files than Cygwin.
2732 The above rules are used exactly the same way on Cygwin specific symlinks
2733 (sysfiles and shortcuts) to eliminate non-POSIX paths in the output. */
2734
2735 /* Eliminate native NT prefixes. */
2736 if (srcbuf[0] == '\\' && !strncmp (srcbuf + 1, "??\\", 3))
2737 {
2738 srcbuf += 4;
2739 if (srcbuf[1] != ':') /* native UNC path */
2740 *(srcbuf += 2) = '\\';
2741 }
2742 if (isdrive (srcbuf))
2743 mount_table->conv_to_posix_path (srcbuf, contents, 0);
2744 else if (srcbuf[0] == '\\')
2745 {
2746 if (srcbuf[1] == '\\') /* UNC path */
2747 slashify (srcbuf, contents, 0);
2748 else /* Paths starting with \ are current drive relative. */
2749 {
2750 char cvtbuf[SYMLINK_MAX + 1];
2751
2752 stpcpy (cvtbuf + cygheap->cwd.get_drive (cvtbuf), srcbuf);
2753 mount_table->conv_to_posix_path (cvtbuf, contents, 0);
2754 }
2755 }
2756 else /* Everything else is taken as is. */
2757 slashify (srcbuf, contents, 0);
2758 return strlen (contents);
2759}
2760
2761enum
2762{
2763 SCAN_BEG,
2764 SCAN_LNK,
2765 SCAN_HASLNK,
2766 SCAN_JUSTCHECK,
2767 SCAN_JUSTCHECKTHIS, /* Never try to append a suffix. */
2768 SCAN_APPENDLNK,
2769 SCAN_EXTRALNK,
2770 SCAN_DONE,
2771};
2772
2773class suffix_scan
2774{
2775 const suffix_info *suffixes, *suffixes_start;
2776 int nextstate;
2777 char *eopath;
d1bef838 2778 size_t namelen;
1870c688
CV
2779public:
2780 const char *path;
2781 char *has (const char *, const suffix_info *);
2782 int next ();
2783 int lnk_match () {return nextstate >= SCAN_APPENDLNK;}
d1bef838 2784 size_t name_len () {return namelen;}
1870c688
CV
2785};
2786
2787char *
2788suffix_scan::has (const char *in_path, const suffix_info *in_suffixes)
2789{
2790 nextstate = SCAN_BEG;
2791 suffixes = suffixes_start = in_suffixes;
2792
2793 const char *fname = strrchr (in_path, '\\');
2794 fname = fname ? fname + 1 : in_path;
2795 char *ext_here = strrchr (fname, '.');
2796 path = in_path;
2797 eopath = strchr (path, '\0');
2798
2799 if (!ext_here)
2800 goto noext;
2801
2802 if (suffixes)
2803 {
2804 /* Check if the extension matches a known extension */
2805 for (const suffix_info *ex = in_suffixes; ex->name != NULL; ex++)
2806 if (ascii_strcasematch (ext_here, ex->name))
81adfe28 2807 {
1870c688
CV
2808 nextstate = SCAN_JUSTCHECK;
2809 suffixes = NULL; /* Has an extension so don't scan for one. */
2810 goto done;
81adfe28 2811 }
1870c688 2812 }
81adfe28 2813
1870c688
CV
2814 /* Didn't match. Use last resort -- .lnk. */
2815 if (ascii_strcasematch (ext_here, ".lnk"))
2816 {
2817 nextstate = SCAN_HASLNK;
2818 suffixes = NULL;
2819 }
81adfe28 2820
1870c688
CV
2821 noext:
2822 ext_here = eopath;
81adfe28 2823
1870c688 2824 done:
d1bef838
CV
2825 namelen = eopath - fname;
2826 /* Avoid attaching suffixes if the resulting filename would be invalid.
2827 For performance reasons we don't check the length of a suffix, since
2828 we know that all suffixes are 4 chars in length.
3bb346d5 2829
d1bef838
CV
2830 FIXME: This is not really correct. A fully functional test should
2831 work on wide character paths. This would probably also speed
2832 up symlink_info::check. */
2833 if (namelen > NAME_MAX - 4)
1870c688
CV
2834 {
2835 nextstate = SCAN_JUSTCHECKTHIS;
2836 suffixes = NULL;
2837 }
2838 return ext_here;
2839}
81adfe28 2840
1870c688
CV
2841int
2842suffix_scan::next ()
2843{
2844 for (;;)
2845 {
2846 if (!suffixes)
2847 switch (nextstate)
81adfe28 2848 {
1870c688
CV
2849 case SCAN_BEG:
2850 suffixes = suffixes_start;
2851 if (!suffixes)
2852 {
2853 nextstate = SCAN_LNK;
2854 return 1;
2855 }
2856 nextstate = SCAN_EXTRALNK;
2857 /* fall through to suffix checking below */
2858 break;
2859 case SCAN_HASLNK:
2860 nextstate = SCAN_APPENDLNK; /* Skip SCAN_BEG */
2861 return 1;
2862 case SCAN_EXTRALNK:
2863 nextstate = SCAN_DONE;
2864 *eopath = '\0';
2865 return 0;
2866 case SCAN_JUSTCHECK:
2867 nextstate = SCAN_LNK;
2868 return 1;
2869 case SCAN_JUSTCHECKTHIS:
2870 nextstate = SCAN_DONE;
2871 return 1;
2872 case SCAN_LNK:
2873 case SCAN_APPENDLNK:
1870c688 2874 nextstate = SCAN_DONE;
d1bef838
CV
2875 if (namelen + (*eopath ? 8 : 4) > NAME_MAX)
2876 {
2877 *eopath = '\0';
2878 return 0;
2879 }
2880 strcat (eopath, ".lnk");
1870c688
CV
2881 return 1;
2882 default:
2883 *eopath = '\0';
81adfe28
CF
2884 return 0;
2885 }
81adfe28 2886
1870c688 2887 while (suffixes && suffixes->name)
d1bef838
CV
2888 if (nextstate == SCAN_EXTRALNK
2889 && (!suffixes->addon || namelen > NAME_MAX - 8))
1870c688
CV
2890 suffixes++;
2891 else
81adfe28 2892 {
1870c688
CV
2893 strcpy (eopath, suffixes->name);
2894 if (nextstate == SCAN_EXTRALNK)
2895 strcat (eopath, ".lnk");
2896 suffixes++;
2897 return 1;
81adfe28 2898 }
1870c688
CV
2899 suffixes = NULL;
2900 }
2901}
81adfe28 2902
1870c688
CV
2903bool
2904symlink_info::set_error (int in_errno)
2905{
2906 bool res;
fde4eaa1
CV
2907 if (!(pc_flags & PC_NO_ACCESS_CHECK)
2908 || in_errno == ENAMETOOLONG || in_errno == EIO)
1870c688
CV
2909 {
2910 error = in_errno;
2911 res = true;
2912 }
2913 else if (in_errno == ENOENT)
2914 res = true;
2915 else
2916 {
2917 fileattr = FILE_ATTRIBUTE_NORMAL;
2918 res = false;
2919 }
2920 return res;
2921}
2922
2923bool
2924symlink_info::parse_device (const char *contents)
2925{
2926 char *endptr;
2927 _major_t mymajor;
2928 _major_t myminor;
ea58e20c 2929 __mode_t mymode;
1870c688
CV
2930
2931 mymajor = strtol (contents += 2, &endptr, 16);
2932 if (endptr == contents)
2933 return isdevice = false;
2934
2935 contents = endptr;
2936 myminor = strtol (++contents, &endptr, 16);
2937 if (endptr == contents)
2938 return isdevice = false;
2939
2940 contents = endptr;
2941 mymode = strtol (++contents, &endptr, 16);
2942 if (endptr == contents)
2943 return isdevice = false;
2944
2945 if ((mymode & S_IFMT) == S_IFIFO)
2946 {
2947 mymajor = _major (FH_FIFO);
2948 myminor = _minor (FH_FIFO);
2949 }
81adfe28 2950
1870c688
CV
2951 major = mymajor;
2952 minor = myminor;
2953 mode = mymode;
2954 return isdevice = true;
2955}
2956
2957/* Check if PATH is a symlink. PATH must be a valid Win32 path name.
2958
2959 If PATH is a symlink, put the value of the symlink--the file to
2960 which it points--into BUF. The value stored in BUF is not
2961 necessarily null terminated. BUFLEN is the length of BUF; only up
2962 to BUFLEN characters will be stored in BUF. BUF may be NULL, in
2963 which case nothing will be stored.
2964
2965 Set *SYML if PATH is a symlink.
2966
2967 Set *EXEC if PATH appears to be executable. This is an efficiency
2968 hack because we sometimes have to open the file anyhow. *EXEC will
2969 not be set for every executable file.
2970
2971 Return -1 on error, 0 if PATH is not a symlink, or the length
2972 stored into BUF if PATH is a symlink. */
2973
2974int
5a0d1edb
CV
2975symlink_info::check (char *path, const suffix_info *suffixes, fs_info &fs,
2976 path_conv_handle &conv_hdl)
1870c688 2977{
6ff06a07
CV
2978 int res;
2979 HANDLE h;
2980 NTSTATUS status;
2981 UNICODE_STRING upath;
2982 OBJECT_ATTRIBUTES attr;
2983 IO_STATUS_BLOCK io;
1870c688 2984 suffix_scan suffix;
1870c688 2985
8802178f 2986 const ULONG ci_flag = cygwin_shared->obcaseinsensitive
c1023ee3
CV
2987 || (mount_flags & MOUNT_NOPOSIX)
2988 ? OBJ_CASE_INSENSITIVE : 0;
1870c688
CV
2989 /* TODO: Temporarily do all char->UNICODE conversion here. This should
2990 already be slightly faster than using Ascii functions. */
2991 tmp_pathbuf tp;
1870c688 2992 tp.u_get (&upath);
e4b57503 2993 InitializeObjectAttributes (&attr, &upath, ci_flag, NULL, NULL);
1870c688 2994
6ff06a07
CV
2995 /* This label is used in case we encounter a FS which only handles
2996 DOS paths. See below. */
b12d6d10 2997 bool restarted = false;
6ff06a07
CV
2998restart:
2999
3000 h = NULL;
3001 res = 0;
3002 contents[0] = '\0';
3003 issymlink = true;
3004 isdevice = false;
3005 major = 0;
3006 minor = 0;
3007 mode = 0;
c1023ee3
CV
3008 // mount_flags is an incoming value set in path_conv */
3009 path_flags = 0;
6ff06a07 3010
6afd6301 3011 PVOID eabuf = &nfs_aol_ffei;
1870c688
CV
3012 ULONG easize = sizeof nfs_aol_ffei;
3013
d1bef838
CV
3014 ext_here = suffix.has (path, suffixes);
3015 extn = ext_here - path;
b3cbb06c 3016 bool had_ext = !!*ext_here;
d1bef838
CV
3017
3018 /* If the filename is too long, don't even try. */
3019 if (suffix.name_len () > NAME_MAX)
3020 {
3021 set_error (ENAMETOOLONG);
3022 goto file_not_symlink;
3023 }
3024
1870c688
CV
3025 while (suffix.next ())
3026 {
1870c688 3027 error = 0;
c1023ee3 3028 get_nt_native_path (suffix.path, upath, mount_flags & MOUNT_DOS);
1870c688
CV
3029 if (h)
3030 {
3031 NtClose (h);
3032 h = NULL;
3033 }
3034 /* The EA given to NtCreateFile allows to get a handle to a symlink on
3035 an NFS share, rather than getting a handle to the target of the
3036 symlink (which would spoil the task of this method quite a bit).
3037 Fortunately it's ignored on most other file systems so we don't have
3038 to special case NFS too much. */
dd0821e3
CV
3039 status = NtCreateFile (&h,
3040 READ_CONTROL | FILE_READ_ATTRIBUTES | FILE_READ_EA,
3041 &attr, &io, NULL, 0, FILE_SHARE_VALID_FLAGS,
3042 FILE_OPEN,
1870c688
CV
3043 FILE_OPEN_REPARSE_POINT
3044 | FILE_OPEN_FOR_BACKUP_INTENT,
3045 eabuf, easize);
61522196 3046 debug_printf ("%y = NtCreateFile (%S)", status, &upath);
1870c688 3047 /* No right to access EAs or EAs not supported? */
3432d6f1
CV
3048 if (!NT_SUCCESS (status)
3049 && (status == STATUS_ACCESS_DENIED
3050 || status == STATUS_EAS_NOT_SUPPORTED
3051 || status == STATUS_NOT_SUPPORTED
3052 || status == STATUS_INVALID_NETWORK_RESPONSE
3053 /* Or a bug in Samba 3.2.x (x <= 7) when accessing a share's
3054 root dir which has EAs enabled? */
3055 || status == STATUS_INVALID_PARAMETER))
1870c688 3056 {
1870c688 3057 /* If EAs are not supported, there's no sense to check them again
ffea27fc 3058 with suffixes attached. So we set eabuf/easize to 0 here once. */
c45871c9
CV
3059 if (status == STATUS_EAS_NOT_SUPPORTED
3060 || status == STATUS_NOT_SUPPORTED)
7a70dda0 3061 {
1870c688
CV
3062 eabuf = NULL;
3063 easize = 0;
a888c079 3064 }
dd0821e3
CV
3065 status = NtOpenFile (&h, READ_CONTROL | FILE_READ_ATTRIBUTES,
3066 &attr, &io, FILE_SHARE_VALID_FLAGS,
2b41d940
CV
3067 FILE_OPEN_REPARSE_POINT
3068 | FILE_OPEN_FOR_BACKUP_INTENT);
61522196 3069 debug_printf ("%y = NtOpenFile (no-EAs %S)", status, &upath);
1870c688 3070 }
6ff06a07
CV
3071 if (status == STATUS_OBJECT_NAME_NOT_FOUND)
3072 {
6ff06a07
CV
3073 /* There are filesystems out in the wild (Netapp, NWFS, and others)
3074 which are uncapable of generating pathnames outside the Win32
3075 rules. That means, filenames on these FSes must not have a
3076 leading space or trailing dots and spaces. This code snippet
3077 manages them. I really hope it's streamlined enough not to
3078 slow down normal operation. This extra check only kicks in if
3079 we encountered a STATUS_OBJECT_NAME_NOT_FOUND *and* we didn't
a361f44c 3080 already attach a suffix. */
48fbe173
KB
3081 if (!restarted && !*ext_here && ext_here[-1] != '\\'
3082 && !(mount_flags & MOUNT_DOS))
6ff06a07 3083 {
002a34fa 3084 /* Check for trailing dot or space or leading space in
b86f999a 3085 last component. */
002a34fa
CV
3086 char *p = ext_here - 1;
3087 if (*p != '.' && *p != ' ')
3088 {
3089 while (*--p != '\\')
7d00a5e3 3090 assert(p >= path);
002a34fa
CV
3091 if (*++p != ' ')
3092 p = NULL;
3093 }
3094 if (p)
6ff06a07 3095 {
002a34fa
CV
3096 /* If so, check if file resides on one of the known broken
3097 FSes only supporting filenames following DOS rules. */
a361f44c 3098 fs.update (&upath, NULL);
8802178f
CV
3099 if (fs.has_dos_filenames_only ())
3100 {
002a34fa 3101 /* If so, try again. Since we now know the FS, the
b86f999a 3102 filenames will be tweaked to follow DOS rules via the
002a34fa 3103 third parameter in the call to get_nt_native_path. */
c1023ee3 3104 mount_flags |= MOUNT_DOS;
8802178f
CV
3105 restarted = true;
3106 goto restart;
3107 }
43616e55
CV
3108 }
3109 }
3110 }
61522196
CV
3111 else if (status == STATUS_NETWORK_OPEN_RESTRICTION
3112 || status == STATUS_SYMLINK_CLASS_DISABLED)
3113 {
3114 /* These status codes are returned if you try to open a native
3115 symlink and the usage of this kind of symlink is forbidden
3116 (see fsutil). Since we can't open them at all, not even for
3117 stat purposes, we have to return a POSIX error code which is
3118 at least a bit helpful.
3119
3120 Additionally Windows 8 introduces a bug in NFS: If you have
3121 a symlink to a directory, with symlinks underneath, resolving
3122 the second level of symlinks fails if remote->remote symlinks
3123 are disabled in fsutil. Unfortunately that's the default. */
3124 set_error (ELOOP);
3125 break;
3126 }
c04ed45d 3127
1870c688 3128 if (NT_SUCCESS (status)
3432d6f1
CV
3129 /* Check file system while we're having the file open anyway.
3130 This speeds up path_conv noticably (~10%). */
c80480bf
CV
3131 && (fs.inited () || fs.update (&upath, h)))
3132 {
36d4eb12
CV
3133 status = conv_hdl.get_finfo (h, fs.is_nfs ());
3134 if (NT_SUCCESS (status))
3135 fileattr = conv_hdl.get_dosattr (fs.is_nfs ());
c80480bf
CV
3136 }
3137 if (!NT_SUCCESS (status))
1870c688 3138 {
61522196 3139 debug_printf ("%y = NtQueryInformationFile (%S)", status, &upath);
1870c688
CV
3140 fileattr = INVALID_FILE_ATTRIBUTES;
3141
3142 /* One of the inner path components is invalid, or the path contains
3143 invalid characters. Bail out with ENOENT.
3144
ece49e40
CV
3145 STATUS_IO_REPARSE_TAG_NOT_HANDLED is returned when trying to
3146 traversing a WSL symlink. For all practical purposes it's
3147 equivalent to traversing SYSTEM- or LNK-type symlink returning
3148 STATUS_OBJECT_PATH_NOT_FOUND.
3149
1870c688
CV
3150 Note that additional STATUS_OBJECT_PATH_INVALID and
3151 STATUS_OBJECT_PATH_SYNTAX_BAD status codes exist. The first one
4ee93264 3152 is seemingly not generated by NtQueryInformationFile, the latter
1870c688
CV
3153 is only generated if the path is no absolute path within the
3154 NT name space, which should not happen and would point to an
3155 error in get_nt_native_path. Both status codes are deliberately
3156 not tested here unless proved necessary. */
3157 if (status == STATUS_OBJECT_PATH_NOT_FOUND
ece49e40 3158 || status == STATUS_IO_REPARSE_TAG_NOT_HANDLED
590f450a 3159 || status == STATUS_OBJECT_NAME_INVALID
fad9568b
CV
3160 || status == STATUS_BAD_NETWORK_PATH
3161 || status == STATUS_BAD_NETWORK_NAME
590f450a 3162 || status == STATUS_NO_MEDIA_IN_DEVICE)
a888c079 3163 {
1870c688 3164 set_error (ENOENT);
563dda82
CV
3165 if (ext_tacked_on && !had_ext)
3166 {
3167 *ext_here = '\0';
3168 ext_tacked_on = false;
3169 ext_here = NULL;
3170 extn = 0;
3171 }
1870c688 3172 goto file_not_symlink;
7a70dda0 3173 }
1870c688
CV
3174 if (status != STATUS_OBJECT_NAME_NOT_FOUND
3175 && status != STATUS_NO_SUCH_FILE) /* ENOENT on NFS or 9x share */
81adfe28 3176 {
1870c688
CV
3177 /* The file exists, but the user can't access it for one reason
3178 or the other. To get the file attributes we try to access the
3179 information by opening the parent directory and getting the
3180 file attributes using a matching NtQueryDirectoryFile call. */
3181 UNICODE_STRING dirname, basename;
3182 OBJECT_ATTRIBUTES dattr;
3183 HANDLE dir;
3184 struct {
eed35efb 3185 FILE_ID_BOTH_DIR_INFORMATION fdi;
1870c688
CV
3186 WCHAR dummy_buf[NAME_MAX + 1];
3187 } fdi_buf;
3188
3189 RtlSplitUnicodePath (&upath, &dirname, &basename);
e4b57503
CV
3190 InitializeObjectAttributes (&dattr, &dirname, ci_flag,
3191 NULL, NULL);
1870c688
CV
3192 status = NtOpenFile (&dir, SYNCHRONIZE | FILE_LIST_DIRECTORY,
3193 &dattr, &io, FILE_SHARE_VALID_FLAGS,
3194 FILE_SYNCHRONOUS_IO_NONALERT
3195 | FILE_OPEN_FOR_BACKUP_INTENT
3196 | FILE_DIRECTORY_FILE);
3197 if (!NT_SUCCESS (status))
3198 {
61522196 3199 debug_printf ("%y = NtOpenFile(%S)", status, &dirname);
14c622c6
CV
3200 /* There's a special case if the file is itself the root
3201 of a drive which is not accessible by the current user.
3202 This case is only recognized by the length of the
3203 basename part. If it's 0, the incoming file is the
3204 root of a drive. So we at least know it's a directory. */
85bfa906
CV
3205 if (basename.Length)
3206 fileattr = FILE_ATTRIBUTE_DIRECTORY;
3207 else
3208 {
3209 fileattr = 0;
3210 set_error (geterrno_from_nt_status (status));
3211 }
1870c688
CV
3212 }
3213 else
3214 {
dc7dfa3a 3215 status = NtQueryDirectoryFile (dir, NULL, NULL, NULL, &io,
1870c688 3216 &fdi_buf, sizeof fdi_buf,
eed35efb 3217 FileIdBothDirectoryInformation,
1870c688 3218 TRUE, &basename, TRUE);
752c477b
CV
3219 /* Take the opportunity to check file system while we're
3220 having the handle to the parent dir. */
b7d3e6d7 3221 fs.update (&upath, dir);
1870c688
CV
3222 NtClose (dir);
3223 if (!NT_SUCCESS (status))
3224 {
61522196 3225 debug_printf ("%y = NtQueryDirectoryFile(%S)",
1870c688 3226 status, &dirname);
3462d736
CV
3227 if (status == STATUS_NO_SUCH_FILE)
3228 {
3229 /* This can happen when trying to access files
3230 which match DOS device names on SMB shares.
3231 NtOpenFile failed with STATUS_ACCESS_DENIED,
3232 but the NtQueryDirectoryFile tells us the
3233 file doesn't exist. We're suspicious in this
3234 case and retry with the next suffix instead of
3235 just giving up. */
3236 set_error (ENOENT);
3237 continue;
3238 }
1870c688
CV
3239 fileattr = 0;
3240 }
3241 else
c80480bf 3242 {
eed35efb 3243 PFILE_ALL_INFORMATION pfai = conv_hdl.fai ();
2d355410 3244
c80480bf 3245 fileattr = fdi_buf.fdi.FileAttributes;
eed35efb
CV
3246 memcpy (&pfai->BasicInformation.CreationTime,
3247 &fdi_buf.fdi.CreationTime,
3248 4 * sizeof (LARGE_INTEGER));
3249 pfai->BasicInformation.FileAttributes = fileattr;
3250 pfai->StandardInformation.AllocationSize.QuadPart
1df35772 3251 = fdi_buf.fdi.AllocationSize.QuadPart;
eed35efb 3252 pfai->StandardInformation.EndOfFile.QuadPart
1df35772 3253 = fdi_buf.fdi.EndOfFile.QuadPart;
eed35efb
CV
3254 pfai->StandardInformation.NumberOfLinks = 1;
3255 pfai->InternalInformation.IndexNumber.QuadPart
3256 = fdi_buf.fdi.FileId.QuadPart;
c80480bf 3257 }
1870c688
CV
3258 }
3259 ext_tacked_on = !!*ext_here;
81adfe28 3260 goto file_not_symlink;
1870c688
CV
3261 }
3262 set_error (ENOENT);
3263 continue;
3264 }
81adfe28 3265
1870c688 3266 ext_tacked_on = !!*ext_here;
b3cbb06c 3267 /* Don't allow to returns directories with appended suffix. If we found
b86f999a 3268 a directory with a suffix which has been appended here, then this
b3cbb06c
CV
3269 directory doesn't match the request. So, just do as usual if file
3270 hasn't been found. */
3271 if (ext_tacked_on && !had_ext && (fileattr & FILE_ATTRIBUTE_DIRECTORY))
3272 {
3273 set_error (ENOENT);
3274 continue;
3275 }
81adfe28 3276
81adfe28
CF
3277 res = -1;
3278
dd0821e3
CV
3279 /* Reparse points are potentially symlinks. This check must be
3280 performed before checking the SYSTEM attribute for sysfile
00e9bf2b 3281 symlinks, since reparse points can have this flag set, too. */
dd0821e3
CV
3282 if ((fileattr & FILE_ATTRIBUTE_REPARSE_POINT))
3283 {
855ea3fd 3284 res = check_reparse_point (h, fs.is_remote_drive ());
492ee4ee 3285 if (res > 0)
dd0821e3 3286 {
492ee4ee 3287 /* A symlink is never a directory. */
eed35efb
CV
3288 conv_hdl.fai ()->BasicInformation.FileAttributes
3289 &= ~FILE_ATTRIBUTE_DIRECTORY;
492ee4ee
CV
3290 break;
3291 }
0b4beaf4
KB
3292 else if (res == 0 && (path_flags & PATH_REP))
3293 /* Known reparse point but not a symlink. */
3294 goto file_not_symlink;
492ee4ee
CV
3295 else
3296 {
3297 /* Volume moint point or unrecognized reparse point type.
3298 Make sure the open handle is not used in later stat calls.
3299 The handle has been opened with the FILE_OPEN_REPARSE_POINT
3300 flag, so it's a handle to the reparse point, not a handle
3301 to the volumes root dir. */
fde4eaa1 3302 pc_flags &= ~PC_KEEP_HANDLE;
492ee4ee 3303 /* Volume mount point: The filesystem information for the top
dd0821e3
CV
3304 level directory should be for the volume top level directory,
3305 rather than for the reparse point itself. So we fetch the
3306 filesystem information again, but with a NULL handle.
3307 This does what we want because fs_info::update opens the
3308 handle without FILE_OPEN_REPARSE_POINT. */
3e80cefb 3309 if (res < 0)
492ee4ee 3310 fs.update (&upath, NULL);
dd0821e3
CV
3311 }
3312 }
3313
1870c688
CV
3314 /* Windows shortcuts are potentially treated as symlinks. Valid Cygwin
3315 & U/WIN shortcuts are R/O, but definitely not directories. */
dd0821e3 3316 else if ((fileattr & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_DIRECTORY))
1870c688
CV
3317 == FILE_ATTRIBUTE_READONLY && suffix.lnk_match ())
3318 {
dd0821e3
CV
3319 HANDLE sym_h;
3320
3321 status = NtOpenFile (&sym_h, SYNCHRONIZE | GENERIC_READ, &attr, &io,
3322 FILE_SHARE_VALID_FLAGS,
3323 FILE_OPEN_FOR_BACKUP_INTENT
3324 | FILE_SYNCHRONOUS_IO_NONALERT);
3325 if (!NT_SUCCESS (status))
5a0d1edb
CV
3326 res = 0;
3327 else
dd0821e3
CV
3328 {
3329 res = check_shortcut (sym_h);
3330 NtClose (sym_h);
3331 }
1870c688
CV
3332 if (!res)
3333 {
dd0821e3
CV
3334 /* If searching for `foo' and then finding a `foo.lnk' which
3335 is no shortcut, return the same as if file not found. */
dc7dfa3a
CV
3336 if (ext_tacked_on)
3337 {
3338 fileattr = INVALID_FILE_ATTRIBUTES;
3339 set_error (ENOENT);
3340 continue;
3341 }
1870c688 3342 }
dc7dfa3a
CV
3343 else if (contents[0] != ':' || contents[1] != '\\'
3344 || !parse_device (contents))
3345 break;
3346 }
3347
3348 /* If searching for `foo' and then finding a `foo.lnk' which is
3349 no shortcut, return the same as if file not found. */
3350 else if (suffix.lnk_match () && ext_tacked_on)
b86f999a 3351 {
dc7dfa3a
CV
3352 fileattr = INVALID_FILE_ATTRIBUTES;
3353 set_error (ENOENT);
3354 continue;
1870c688 3355 }
fe6934da 3356
1870c688
CV
3357 /* This is the old Cygwin method creating symlinks. A symlink will
3358 have the `system' file attribute. Only files can be symlinks
3359 (which can be symlinks to directories). */
3360 else if ((fileattr & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DIRECTORY))
3361 == FILE_ATTRIBUTE_SYSTEM)
3362 {
dd0821e3
CV
3363 HANDLE sym_h;
3364
3365 status = NtOpenFile (&sym_h, SYNCHRONIZE | GENERIC_READ, &attr, &io,
3366 FILE_SHARE_VALID_FLAGS,
3367 FILE_OPEN_FOR_BACKUP_INTENT
3368 | FILE_SYNCHRONOUS_IO_NONALERT);
3369
3370 if (!NT_SUCCESS (status))
5a0d1edb
CV
3371 res = 0;
3372 else
dd0821e3
CV
3373 {
3374 res = check_sysfile (sym_h);
3375 NtClose (sym_h);
3376 }
dc7dfa3a
CV
3377 if (res)
3378 break;
1870c688 3379 }
fe6934da 3380
5a0d1edb
CV
3381 /* If the file is on an NFS share and could be opened with extended
3382 attributes, check if it's a symlink. Only files can be symlinks
1870c688 3383 (which can be symlinks to directories). */
2d355410 3384 else if (fs.is_nfs () && (conv_hdl.nfsattr ()->type & 7) == NF3LNK)
1870c688 3385 {
ab3cd888 3386 res = check_nfs_symlink (h);
dc7dfa3a
CV
3387 if (res)
3388 break;
1870c688 3389 }
1fd5e000 3390
dc7dfa3a 3391 /* Normal file. */
1870c688
CV
3392 file_not_symlink:
3393 issymlink = false;
3394 syscall_printf ("%s", isdevice ? "is a device" : "not a symlink");
3395 res = 0;
3396 break;
3397 }
70c370d6 3398
1870c688 3399 if (h)
5a0d1edb 3400 {
fde4eaa1 3401 if (pc_flags & PC_KEEP_HANDLE)
dd0821e3 3402 conv_hdl.set (h);
5a0d1edb
CV
3403 else
3404 NtClose (h);
3405 }
0f565126 3406
c1023ee3
CV
3407 syscall_printf ("%d = symlink.check(%s, %p) (mount_flags %y, path_flags %y)",
3408 res, suffix.path, contents, mount_flags, path_flags);
1870c688
CV
3409 return res;
3410}
3411
3412/* "path" is the path in a virtual symlink. Set a symlink_info struct from
3413 that and proceed with further path checking afterwards. */
3414int
3415symlink_info::set (char *path)
3416{
3417 strcpy (contents, path);
c1023ee3
CV
3418 mount_flags = 0;
3419 path_flags = PATH_SYMLINK;
1870c688
CV
3420 fileattr = FILE_ATTRIBUTE_NORMAL;
3421 error = 0;
3422 issymlink = true;
3423 isdevice = false;
e4b57503 3424 ext_tacked_on = false;
1870c688
CV
3425 ext_here = NULL;
3426 extn = major = minor = mode = 0;
3427 return strlen (path);
3428}
3429
1870c688 3430/* readlink system call */
2cf9359a 3431
1870c688 3432extern "C" ssize_t
3073f26d 3433readlink (const char *__restrict path, char *__restrict buf, size_t buflen)
1870c688
CV
3434{
3435 if (buflen < 0)
3436 {
3437 set_errno (ENAMETOOLONG);
3438 return -1;
3439 }
1fd5e000 3440
1870c688 3441 path_conv pathbuf (path, PC_SYM_CONTENTS, stat_suffixes);
772e2322 3442
1870c688
CV
3443 if (pathbuf.error)
3444 {
3445 set_errno (pathbuf.error);
61522196 3446 syscall_printf ("-1 = readlink (%s, %p, %lu)", path, buf, buflen);
1870c688
CV
3447 return -1;
3448 }
1fd5e000 3449
1870c688
CV
3450 if (!pathbuf.exists ())
3451 {
3452 set_errno (ENOENT);
3453 return -1;
3454 }
1fd5e000 3455
1870c688
CV
3456 if (!pathbuf.issymlink ())
3457 {
3458 if (pathbuf.exists ())
3459 set_errno (EINVAL);
3460 return -1;
3461 }
81adfe28 3462
efb1f061
CV
3463 size_t pathbuf_len = strlen (pathbuf.get_win32 ());
3464 ssize_t len = MIN (buflen, pathbuf_len);
1870c688
CV
3465 memcpy (buf, pathbuf.get_win32 (), len);
3466
3467 /* errno set by symlink.check if error */
3468 return len;
3469}
3470
3471/* Some programs rely on st_dev/st_ino being unique for each file.
3472 Hash the path name and hope for the best. The hash arg is not
3473 always initialized to zero since readdir needs to compute the
3474 dirent ino_t based on a combination of the hash of the directory
3475 done during the opendir call and the hash or the filename within
3476 the directory. FIXME: Not bullet-proof. */
3477/* Cygwin internal */
1f36328e 3478ino_t __reg2
61522196 3479hash_path_name (ino_t hash, PUNICODE_STRING name)
1870c688
CV
3480{
3481 if (name->Length == 0)
3482 return hash;
3483
3484 /* Build up hash. Name is already normalized */
3485 USHORT len = name->Length / sizeof (WCHAR);
3486 for (USHORT idx = 0; idx < len; ++idx)
3487 hash = RtlUpcaseUnicodeChar (name->Buffer[idx])
3488 + (hash << 6) + (hash << 16) - hash;
3489 return hash;
3490}
3491
1f36328e 3492ino_t __reg2
61522196 3493hash_path_name (ino_t hash, PCWSTR name)
1870c688
CV
3494{
3495 UNICODE_STRING uname;
3496 RtlInitUnicodeString (&uname, name);
3497 return hash_path_name (hash, &uname);
3498}
3499
1f36328e 3500ino_t __reg2
61522196 3501hash_path_name (ino_t hash, const char *name)
1870c688
CV
3502{
3503 UNICODE_STRING uname;
3504 RtlCreateUnicodeStringFromAsciiz (&uname, name);
61522196 3505 ino_t ret = hash_path_name (hash, &uname);
1870c688
CV
3506 RtlFreeUnicodeString (&uname);
3507 return ret;
3508}
3509
3510extern "C" char *
3511getcwd (char *buf, size_t ulen)
3512{
3513 char* res = NULL;
3f3bd101
CV
3514
3515 __try
3516 {
3517 if (ulen == 0 && buf)
3518 set_errno (EINVAL);
3519 else
3520 res = cygheap->cwd.get (buf, 1, 1, ulen);
3521 }
3522 __except (EFAULT) {}
3523 __endtry
1870c688
CV
3524 return res;
3525}
3526
3527/* getwd: Legacy. */
3528extern "C" char *
3529getwd (char *buf)
3530{
3531 return getcwd (buf, PATH_MAX + 1); /*Per SuSv3!*/
3532}
3533
0b5355f1
YS
3534extern "C" char *
3535get_current_dir_name (void)
3536{
b9f131c8 3537 const char *pwd = getenv ("PWD");
0b5355f1 3538 char *cwd = getcwd (NULL, 0);
61522196 3539 struct stat pwdbuf, cwdbuf;
0b5355f1 3540
b9f131c8
CF
3541 if (pwd && strcmp (pwd, cwd) != 0
3542 && stat64 (pwd, &pwdbuf) == 0
3543 && stat64 (cwd, &cwdbuf) == 0
3544 && pwdbuf.st_dev == cwdbuf.st_dev
3545 && pwdbuf.st_ino == cwdbuf.st_ino)
0b5355f1 3546 {
b9f131c8
CF
3547 cwd = (char *) realloc (cwd, strlen (pwd) + 1);
3548 strcpy (cwd, pwd);
0b5355f1
YS
3549 }
3550
3551 return cwd;
3552}
3553
1870c688
CV
3554/* chdir: POSIX 5.2.1.1 */
3555extern "C" int
3556chdir (const char *in_dir)
3557{
3f3bd101
CV
3558 int res = -1;
3559
3560 __try
1870c688 3561 {
3f3bd101
CV
3562 if (!*in_dir)
3563 {
3564 set_errno (ENOENT);
3565 __leave;
3566 }
02782489 3567
3f3bd101 3568 syscall_printf ("dir '%s'", in_dir);
1fd5e000 3569
c1023ee3 3570 /* Convert path. PC_NONULLEMPTY ensures that we don't check for
4bfa93f1 3571 NULL/empty/invalid again. */
c1023ee3 3572 path_conv path (in_dir, PC_SYM_FOLLOW | PC_POSIX | PC_NONULLEMPTY);
3f3bd101
CV
3573 if (path.error)
3574 {
3575 set_errno (path.error);
3576 syscall_printf ("-1 = chdir (%s)", in_dir);
3577 __leave;
3578 }
edab6053 3579
3f3bd101
CV
3580 const char *posix_cwd = NULL;
3581 dev_t devn = path.get_device ();
3582 if (!path.exists ())
3583 set_errno (ENOENT);
3584 else if (!path.isdir ())
3585 set_errno (ENOTDIR);
3586 else if (!isvirtual_dev (devn))
3587 {
3588 /* The sequence chdir("xx"); chdir(".."); must be a noop if xx
3589 is not a symlink. This is exploited by find.exe.
d2bd82aa 3590 The posix_cwd is just path.get_posix ().
3f3bd101
CV
3591 In other cases we let cwd.set obtain the Posix path through
3592 the mount table. */
d2bd82aa
CV
3593 if (!isdrive (path.get_posix ()))
3594 posix_cwd = path.get_posix ();
3f3bd101
CV
3595 res = 0;
3596 }
3597 else
3598 {
d2bd82aa 3599 posix_cwd = path.get_posix ();
3f3bd101
CV
3600 res = 0;
3601 }
3602
3603 if (!res)
3604 res = cygheap->cwd.set (&path, posix_cwd);
3605
3606 /* Note that we're accessing cwd.posix without a lock here.
3607 I didn't think it was worth locking just for strace. */
3608 syscall_printf ("%R = chdir() cygheap->cwd.posix '%s' native '%S'", res,
3609 cygheap->cwd.get_posix (), path.get_nt_native_path ());
1870c688 3610 }
3f3bd101
CV
3611 __except (EFAULT)
3612 {
3613 res = -1;
3614 }
3615 __endtry
1870c688
CV
3616 return res;
3617}
3618
3619extern "C" int
3620fchdir (int fd)
3621{
3622 int res;
3623 cygheap_fdget cfd (fd);
3624 if (cfd >= 0)
3625 res = chdir (cfd->get_name ());
3626 else
3627 res = -1;
3628
b9aa8149 3629 syscall_printf ("%R = fchdir(%d)", res, fd);
1870c688
CV
3630 return res;
3631}
3632
3633/******************** Exported Path Routines *********************/
3634
3635/* Cover functions to the path conversion routines.
3636 These are exported to the world as cygwin_foo by cygwin.din. */
edab6053 3637
1870c688
CV
3638#define return_with_errno(x) \
3639 do {\
3640 int err = (x);\
3641 if (!err)\
3642 return 0;\
3643 set_errno (err);\
3644 return -1;\
3645 } while (0)
3646
3647extern "C" ssize_t
3648cygwin_conv_path (cygwin_conv_path_t what, const void *from, void *to,
3649 size_t size)
3650{
3651 tmp_pathbuf tp;
1870c688
CV
3652 path_conv p;
3653 size_t lsiz = 0;
3654 char *buf = NULL;
06cc41a6 3655 PWCHAR path = NULL;
1870c688 3656 int error = 0;
8b83da2d 3657 int how = what & CCP_CONVFLAGS_MASK;
5250e27f 3658 what &= CCP_CONVTYPE_MASK;
3f3bd101 3659 int ret = -1;
37169691 3660
3f3bd101 3661 __try
d21edf52 3662 {
3f3bd101
CV
3663 if (!from)
3664 {
3665 set_errno (EINVAL);
3666 __leave;
3667 }
d21edf52 3668
3f3bd101
CV
3669 switch (what)
3670 {
3671 case CCP_POSIX_TO_WIN_A:
43f65cdd 3672 {
3f3bd101
CV
3673 p.check ((const char *) from,
3674 PC_POSIX | PC_SYM_FOLLOW | PC_SYM_NOFOLLOW_REP
13bfb3c6 3675 | PC_NO_ACCESS_CHECK
937a2d92 3676 | ((how & CCP_RELATIVE) ? PC_NOFULL : 0), stat_suffixes);
3f3bd101
CV
3677 if (p.error)
3678 {
3679 set_errno (p.error);
3680 __leave;
3681 }
3682 PUNICODE_STRING up = p.get_nt_native_path ();
3683 buf = tp.c_get ();
3684 sys_wcstombs (buf, NT_MAX_PATH,
5250e27f 3685 up->Buffer, up->Length / sizeof (WCHAR));
3f3bd101
CV
3686 /* Convert native path to standard DOS path. */
3687 if (!strncmp (buf, "\\??\\", 4))
3688 {
3689 buf += 4;
3690 if (buf[1] != ':') /* native UNC path */
3691 *(buf += 2) = '\\';
3692 }
3693 else if (*buf == '\\')
3694 {
3695 /* Device name points to somewhere else in the NT namespace.
3696 Use GLOBALROOT prefix to convert to Win32 path. */
3697 char *p = buf + sys_wcstombs (buf, NT_MAX_PATH,
3698 ro_u_globalroot.Buffer,
3699 ro_u_globalroot.Length
3700 / sizeof (WCHAR));
3701 sys_wcstombs (p, NT_MAX_PATH - (p - buf),
3702 up->Buffer, up->Length / sizeof (WCHAR));
3703 }
3704 lsiz = strlen (buf) + 1;
3705 /* TODO: Incoming "." is a special case which leads to a trailing
3706 backslash ".\\" in the Win32 path. That's a result of the
3707 conversion in normalize_posix_path. This should not occur
3708 so the below code is just a band-aid. */
5aa8817e 3709 if ((how & CCP_RELATIVE) && !strcmp ((const char *) from, ".")
3f3bd101
CV
3710 && !strcmp (buf, ".\\"))
3711 {
3712 lsiz = 2;
3713 buf[1] = '\0';
3714 }
43f65cdd 3715 }
3f3bd101
CV
3716 break;
3717 case CCP_POSIX_TO_WIN_W:
3718 p.check ((const char *) from,
3719 PC_POSIX | PC_SYM_FOLLOW | PC_SYM_NOFOLLOW_REP
13bfb3c6 3720 | PC_NO_ACCESS_CHECK
937a2d92 3721 | ((how & CCP_RELATIVE) ? PC_NOFULL : 0), stat_suffixes);
ca027229 3722 if (p.error)
3f3bd101
CV
3723 {
3724 set_errno (p.error);
3725 __leave;
3726 }
3727 /* Relative Windows paths are always restricted to MAX_PATH chars. */
5aa8817e 3728 if ((how & CCP_RELATIVE) && !isabspath (p.get_win32 ())
3f3bd101
CV
3729 && sys_mbstowcs (NULL, 0, p.get_win32 ()) > MAX_PATH)
3730 {
3731 /* Recreate as absolute path. */
3732 p.check ((const char *) from, PC_POSIX | PC_SYM_FOLLOW
13bfb3c6 3733 | PC_NO_ACCESS_CHECK);
3f3bd101
CV
3734 if (p.error)
3735 {
3736 set_errno (p.error);
3737 __leave;
3738 }
3739 }
3740 lsiz = p.get_wide_win32_path_len () + 1;
3741 path = p.get_nt_native_path ()->Buffer;
06cc41a6 3742
3f3bd101
CV
3743 /* Convert native path to standard DOS path. */
3744 if (!wcsncmp (path, L"\\??\\", 4))
06cc41a6 3745 {
3f3bd101
CV
3746 path[1] = L'\\';
3747
3748 /* Drop long path prefix for short pathnames. Unfortunately there's
3749 quite a bunch of Win32 functions, especially in user32.dll,
3750 apparently, which don't grok long path names at all, not even
3751 in the UNICODE API. */
3752 if ((path[5] == L':' && lsiz <= MAX_PATH + 4)
3753 || (!wcsncmp (path + 4, L"UNC\\", 4) && lsiz <= MAX_PATH + 6))
06cc41a6 3754 {
3f3bd101
CV
3755 path += 4;
3756 lsiz -= 4;
3757 if (path[1] != L':')
3758 {
3759 *(path += 2) = '\\';
3760 lsiz -= 2;
3761 }
06cc41a6
CV
3762 }
3763 }
3f3bd101
CV
3764 else if (*path == L'\\')
3765 {
3766 /* Device name points to somewhere else in the NT namespace.
3767 Use GLOBALROOT prefix to convert to Win32 path. */
3768 to = (void *) wcpcpy ((wchar_t *) to, ro_u_globalroot.Buffer);
3769 lsiz += ro_u_globalroot.Length / sizeof (WCHAR);
3770 }
3771 /* TODO: Same ".\\" band-aid as in CCP_POSIX_TO_WIN_A case. */
5aa8817e 3772 if ((how & CCP_RELATIVE) && !strcmp ((const char *) from, ".")
3f3bd101
CV
3773 && !wcscmp (path, L".\\"))
3774 {
3775 lsiz = 2;
3776 path[1] = L'\0';
3777 }
3778 lsiz *= sizeof (WCHAR);
3779 break;
3780 case CCP_WIN_A_TO_POSIX:
3781 buf = tp.c_get ();
3782 error = mount_table->conv_to_posix_path ((const char *) from, buf,
8b83da2d 3783 how | __CCP_APP_SLASH);
3f3bd101
CV
3784 if (error)
3785 {
3786 set_errno (p.error);
3787 __leave;
3788 }
3789 lsiz = strlen (buf) + 1;
3790 break;
3791 case CCP_WIN_W_TO_POSIX:
3792 buf = tp.c_get ();
3793 error = mount_table->conv_to_posix_path ((const PWCHAR) from, buf,
8b83da2d 3794 how | __CCP_APP_SLASH);
3f3bd101
CV
3795 if (error)
3796 {
3797 set_errno (error);
3798 __leave;
3799 }
3800 lsiz = strlen (buf) + 1;
3801 break;
3802 default:
3803 set_errno (EINVAL);
3804 __leave;
06cc41a6 3805 }
3f3bd101 3806 if (!size)
43f65cdd 3807 {
3f3bd101
CV
3808 ret = lsiz;
3809 __leave;
43f65cdd 3810 }
3f3bd101 3811 if (size < lsiz)
b86f999a 3812 {
3f3bd101
CV
3813 set_errno (ENOSPC);
3814 __leave;
59cb363a 3815 }
3f3bd101
CV
3816 switch (what)
3817 {
3818 case CCP_POSIX_TO_WIN_A:
3819 case CCP_WIN_A_TO_POSIX:
3820 case CCP_WIN_W_TO_POSIX:
3821 stpcpy ((char *) to, buf);
3822 break;
3823 case CCP_POSIX_TO_WIN_W:
3824 wcpcpy ((PWCHAR) to, path);
3825 break;
3826 }
3827 ret = 0;
1870c688 3828 }
3f3bd101
CV
3829 __except (EFAULT) {}
3830 __endtry
3831 return ret;
1870c688
CV
3832}
3833
3834extern "C" void *
3835cygwin_create_path (cygwin_conv_path_t what, const void *from)
3836{
3837 void *to;
3838 ssize_t size = cygwin_conv_path (what, from, NULL, 0);
3839 if (size <= 0)
27f1db48
CF
3840 to = NULL;
3841 else if (!(to = malloc (size)))
3842 to = NULL;
1870c688 3843 if (cygwin_conv_path (what, from, to, size) == -1)
27f1db48
CF
3844 {
3845 free (to);
3846 to = NULL;
3847 }
1870c688
CV
3848 return to;
3849}
3850
76f06705 3851#ifdef __i386__
1870c688
CV
3852
3853extern "C" int
3854cygwin_conv_to_win32_path (const char *path, char *win32_path)
3855{
3856 return cygwin_conv_path (CCP_POSIX_TO_WIN_A | CCP_RELATIVE, path, win32_path,
3857 MAX_PATH);
3858}
3859
3860extern "C" int
3861cygwin_conv_to_full_win32_path (const char *path, char *win32_path)
3862{
3863 return cygwin_conv_path (CCP_POSIX_TO_WIN_A | CCP_ABSOLUTE, path, win32_path,
3864 MAX_PATH);
3865}
3866
3867/* This is exported to the world as cygwin_foo by cygwin.din. */
3868
3869extern "C" int
3870cygwin_conv_to_posix_path (const char *path, char *posix_path)
3871{
3872 return cygwin_conv_path (CCP_WIN_A_TO_POSIX | CCP_RELATIVE, path, posix_path,
3873 MAX_PATH);
3874}
3875
3876extern "C" int
3877cygwin_conv_to_full_posix_path (const char *path, char *posix_path)
3878{
3879 return cygwin_conv_path (CCP_WIN_A_TO_POSIX | CCP_ABSOLUTE, path, posix_path,
3880 MAX_PATH);
3881}
3882
76f06705 3883#endif /* __i386__ */
61522196 3884
2bf78f09 3885/* The realpath function is required by POSIX:2008. */
1870c688
CV
3886
3887extern "C" char *
3073f26d 3888realpath (const char *__restrict path, char *__restrict resolved)
1870c688 3889{
3f3bd101
CV
3890 tmp_pathbuf tp;
3891 char *tpath;
3892
1870c688
CV
3893 /* Make sure the right errno is returned if path is NULL. */
3894 if (!path)
3895 {
3896 set_errno (EINVAL);
3897 return NULL;
3898 }
63958f00 3899
1870c688
CV
3900 /* Guard reading from a potentially invalid path and writing to a
3901 potentially invalid resolved. */
3f3bd101 3902 __try
1870c688 3903 {
5c759ab2
CV
3904 /* Win32 drive letter paths and, generally, any path starting with a
3905 backslash, have to be converted to a POSIX path first, because
3906 path_conv leaves the incoming path untouched except for converting
3907 backslashes to forward slashes. This also covers '\\?\ and '\??\'
3908 path prefixes. */
3909 if (isdrive (path) || path[0] == '\\')
3f3bd101
CV
3910 {
3911 tpath = tp.c_get ();
3912 mount_table->conv_to_posix_path (path, tpath, 0);
3913 }
3914 else
3915 tpath = (char *) path;
63958f00 3916
3f3bd101 3917 path_conv real_path (tpath, PC_SYM_FOLLOW | PC_POSIX, stat_suffixes);
37169691 3918
1fd5e000 3919
3f3bd101
CV
3920 /* POSIX 2008 requires malloc'ing if resolved is NULL, and states
3921 that using non-NULL resolved is asking for portability
3922 problems. */
1fd5e000 3923
3f3bd101 3924 if (!real_path.error && real_path.exists ())
1870c688 3925 {
1870c688 3926 if (!resolved)
3f3bd101
CV
3927 {
3928 resolved = (char *)
d2bd82aa 3929 malloc (strlen (real_path.get_posix ()) + 1);
3f3bd101
CV
3930 if (!resolved)
3931 return NULL;
3932 }
d2bd82aa 3933 strcpy (resolved, real_path.get_posix ());
3f3bd101 3934 return resolved;
1870c688 3935 }
1fd5e000 3936
3f3bd101
CV
3937 /* FIXME: on error, Linux puts the name of the path
3938 component which could not be resolved into RESOLVED, but POSIX
3939 does not require this. */
3940 if (resolved)
3941 resolved[0] = '\0';
3942 set_errno (real_path.error ?: ENOENT);
3943 }
3944 __except (EFAULT) {}
3945 __endtry
1870c688
CV
3946 return NULL;
3947}
3948
2bf78f09
EB
3949/* Linux provides this extension. Since the only portable use of
3950 realpath requires a NULL second argument, we might as well have a
3951 one-argument wrapper. */
3952extern "C" char *
3953canonicalize_file_name (const char *path)
3954{
3955 return realpath (path, NULL);
3956}
3957
1870c688
CV
3958/* Return non-zero if path is a POSIX path list.
3959 This is exported to the world as cygwin_foo by cygwin.din.
3960
3961DOCTOOL-START
3962<sect1 id="add-func-cygwin-posix-path-list-p">
3963 <para>Rather than use a mode to say what the "proper" path list
3964 format is, we allow any, and give apps the tools they need to
3965 convert between the two. If a ';' is present in the path list it's
3966 a Win32 path list. Otherwise, if the first path begins with
3967 [letter]: (in which case it can be the only element since if it
3968 wasn't a ';' would be present) it's a Win32 path list. Otherwise,
3969 it's a POSIX path list.</para>
3970</sect1>
3971DOCTOOL-END
3972 */
1fd5e000 3973
1870c688
CV
3974extern "C" int
3975cygwin_posix_path_list_p (const char *path)
3976{
3977 int posix_p = !(strchr (path, ';') || isdrive (path));
3978 return posix_p;
3979}
3980
3981/* These are used for apps that need to convert env vars like PATH back and
3982 forth. The conversion is a two step process. First, an upper bound on the
3983 size of the buffer needed is computed. Then the conversion is done. This
3984 allows the caller to use alloca if it wants. */
3985
3986static int
3987conv_path_list_buf_size (const char *path_list, bool to_posix)
3988{
3989 int i, num_elms, max_mount_path_len, size;
3990 const char *p;
3991
3992 path_conv pc(".", PC_POSIX);
3993 /* The theory is that an upper bound is
3994 current_size + (num_elms * max_mount_path_len) */
3995 /* FIXME: This method is questionable in the long run. */
3996
3997 unsigned nrel;
3998 char delim = to_posix ? ';' : ':';
3999 for (p = path_list, num_elms = nrel = 0; p; num_elms++)
4000 {
4001 if (!isabspath (p))
4002 nrel++;
4003 p = strchr (++p, delim);
4004 }
1fd5e000 4005
1870c688
CV
4006 /* 7: strlen ("//c") + slop, a conservative initial value */
4007 for (max_mount_path_len = sizeof ("/cygdrive/X"), i = 0;
4008 i < mount_table->nmounts; i++)
4009 {
4010 int mount_len = (to_posix
4011 ? mount_table->mount[i].posix_pathlen
4012 : mount_table->mount[i].native_pathlen);
4013 if (max_mount_path_len < mount_len)
4014 max_mount_path_len = mount_len;
4015 }
1fd5e000 4016
1870c688
CV
4017 /* 100: slop */
4018 size = strlen (path_list)
4019 + (num_elms * max_mount_path_len)
d2bd82aa 4020 + (nrel * strlen (to_posix ? pc.get_posix () : pc.get_win32 ()))
1870c688
CV
4021 + 100;
4022
4023 return size;
4024}
4025
61522196
CV
4026extern "C" ssize_t
4027env_PATH_to_posix (const void *win32, void *posix, size_t size)
4028{
4029 return_with_errno (conv_path_list ((const char *) win32, (char *) posix,
4030 size, ENV_CVT));
4031}
4032
76f06705 4033#ifdef __i386__
1870c688
CV
4034
4035extern "C" int
4036cygwin_win32_to_posix_path_list_buf_size (const char *path_list)
4037{
4038 return conv_path_list_buf_size (path_list, true);
4039}
4040
4041extern "C" int
4042cygwin_posix_to_win32_path_list_buf_size (const char *path_list)
4043{
4044 return conv_path_list_buf_size (path_list, false);
4045}
4046
1870c688
CV
4047extern "C" int
4048cygwin_win32_to_posix_path_list (const char *win32, char *posix)
4049{
a887cf0e
CV
4050 return_with_errno (conv_path_list (win32, posix, MAX_PATH,
4051 CCP_WIN_A_TO_POSIX | CCP_RELATIVE));
1870c688
CV
4052}
4053
4054extern "C" int
4055cygwin_posix_to_win32_path_list (const char *posix, char *win32)
4056{
a887cf0e
CV
4057 return_with_errno (conv_path_list (posix, win32, MAX_PATH,
4058 CCP_POSIX_TO_WIN_A | CCP_RELATIVE));
1870c688
CV
4059}
4060
76f06705 4061#endif /* __i386__ */
61522196 4062
1870c688
CV
4063extern "C" ssize_t
4064cygwin_conv_path_list (cygwin_conv_path_t what, const void *from, void *to,
4065 size_t size)
4066{
a887cf0e
CV
4067 int ret;
4068 char *winp = NULL;
4069 void *orig_to = NULL;
a887cf0e
CV
4070 tmp_pathbuf tp;
4071
4072 switch (what & CCP_CONVTYPE_MASK)
1870c688
CV
4073 {
4074 case CCP_WIN_W_TO_POSIX:
a887cf0e
CV
4075 if (!sys_wcstombs_alloc (&winp, HEAP_NOTHEAP, (const wchar_t *) from,
4076 (size_t) -1))
4077 return -1;
4078 what = (what & ~CCP_CONVTYPE_MASK) | CCP_WIN_A_TO_POSIX;
4079 from = (const void *) winp;
4080 break;
1870c688 4081 case CCP_POSIX_TO_WIN_W:
a887cf0e
CV
4082 if (size == 0)
4083 return conv_path_list_buf_size ((const char *) from, 0)
4084 * sizeof (WCHAR);
4085 what = (what & ~CCP_CONVTYPE_MASK) | CCP_POSIX_TO_WIN_A;
4086 orig_to = to;
a887cf0e
CV
4087 to = (void *) tp.w_get ();
4088 size = 65536;
1870c688 4089 break;
a887cf0e
CV
4090 }
4091 switch (what & CCP_CONVTYPE_MASK)
4092 {
1870c688
CV
4093 case CCP_WIN_A_TO_POSIX:
4094 case CCP_POSIX_TO_WIN_A:
4095 if (size == 0)
4096 return conv_path_list_buf_size ((const char *) from,
4097 what == CCP_WIN_A_TO_POSIX);
a887cf0e
CV
4098 ret = conv_path_list ((const char *) from, (char *) to, size, what);
4099 /* Free winp buffer in case of CCP_WIN_W_TO_POSIX. */
4100 if (winp)
d2ef2331 4101 free (winp);
a887cf0e
CV
4102 /* Convert to WCHAR in case of CCP_POSIX_TO_WIN_W. */
4103 if (orig_to)
4104 sys_mbstowcs ((wchar_t *) orig_to, size / sizeof (WCHAR),
4105 (const char *) to, (size_t) -1);
4106 return_with_errno (ret);
1870c688
CV
4107 break;
4108 default:
4109 break;
4110 }
4111 set_errno (EINVAL);
4112 return -1;
4113}
4114
4115/* cygwin_split_path: Split a path into directory and file name parts.
4116 Buffers DIR and FILE are assumed to be big enough.
4117
4118 Examples (path -> `dir' / `file'):
4119 / -> `/' / `'
4120 "" -> `.' / `'
4121 . -> `.' / `.' (FIXME: should this be `.' / `'?)
4122 .. -> `.' / `..' (FIXME: should this be `..' / `'?)
4123 foo -> `.' / `foo'
4124 foo/bar -> `foo' / `bar'
4125 foo/bar/ -> `foo' / `bar'
4126 /foo -> `/' / `foo'
4127 /foo/bar -> `/foo' / `bar'
4128 c: -> `c:/' / `'
4129 c:/ -> `c:/' / `'
4130 c:foo -> `c:/' / `foo'
4131 c:/foo -> `c:/' / `foo'
4132 */
4133
4134extern "C" void
4135cygwin_split_path (const char *path, char *dir, char *file)
4136{
4137 int dir_started_p = 0;
4138
4139 /* Deal with drives.
4140 Remember that c:foo <==> c:/foo. */
4141 if (isdrive (path))
4142 {
4143 *dir++ = *path++;
4144 *dir++ = *path++;
4145 *dir++ = '/';
4146 if (!*path)
4147 {
4148 *dir = 0;
4149 *file = 0;
4150 return;
4151 }
4152 if (isdirsep (*path))
4153 ++path;
4154 dir_started_p = 1;
4155 }
1fd5e000 4156
1870c688
CV
4157 /* Determine if there are trailing slashes and "delete" them if present.
4158 We pretend as if they don't exist. */
4159 const char *end = path + strlen (path);
4160 /* path + 1: keep leading slash. */
4161 while (end > path + 1 && isdirsep (end[-1]))
4162 --end;
1fd5e000 4163
1870c688
CV
4164 /* At this point, END points to one beyond the last character
4165 (with trailing slashes "deleted"). */
1fd5e000 4166
1870c688
CV
4167 /* Point LAST_SLASH at the last slash (duh...). */
4168 const char *last_slash;
4169 for (last_slash = end - 1; last_slash >= path; --last_slash)
4170 if (isdirsep (*last_slash))
4171 break;
7e24f1bf 4172
1870c688
CV
4173 if (last_slash == path)
4174 {
4175 *dir++ = '/';
4176 *dir = 0;
4177 }
4178 else if (last_slash > path)
4179 {
4180 memcpy (dir, path, last_slash - path);
4181 dir[last_slash - path] = 0;
4182 }
4183 else
4184 {
4185 if (dir_started_p)
4186 ; /* nothing to do */
4187 else
4188 *dir++ = '.';
4189 *dir = 0;
4190 }
09ecdc85 4191
1870c688
CV
4192 memcpy (file, last_slash + 1, end - last_slash - 1);
4193 file[end - last_slash - 1] = 0;
4194}
4195
40187f90
CV
4196static inline void
4197copy_cwd_str (PUNICODE_STRING tgt, PUNICODE_STRING src)
4198{
4199 RtlCopyUnicodeString (tgt, src);
4200 if (tgt->Buffer[tgt->Length / sizeof (WCHAR) - 1] != L'\\')
4201 {
4202 tgt->Buffer[tgt->Length / sizeof (WCHAR)] = L'\\';
4203 tgt->Length += sizeof (WCHAR);
4204 }
4205}
4206
1870c688
CV
4207/*****************************************************************************/
4208
a413f8a2 4209/* The find_fast_cwd_pointer function and parts of the
9c154abe
CV
4210 cwdstuff::override_win32_cwd method are based on code using the
4211 following license:
4212
4213 Copyright 2010 John Carey. All rights reserved.
4214
4215 Redistribution and use in source and binary forms, with or without
4216 modification, are permitted provided that the following conditions
4217 are met:
4218
4219 1. Redistributions of source code must retain the above
4220 copyright notice, this list of conditions and the following
4221 disclaimer.
4222
4223 2. Redistributions in binary form must reproduce the above
4224 copyright notice, this list of conditions and the following
4225 disclaimer in the documentation and/or other materials provided
4226 with the distribution.
4227
4228 THIS SOFTWARE IS PROVIDED BY JOHN CAREY ``AS IS'' AND ANY EXPRESS
4229 OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
4230 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
4231 ARE DISCLAIMED. IN NO EVENT SHALL JOHN CAREY OR CONTRIBUTORS BE
4232 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
4233 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
4234 OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
4235 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
4236 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
4237 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
4238 USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
4239 DAMAGE. */
4240
a3904c65
CV
4241void
4242fcwd_access_t::SetFSCharacteristics (LONG val)
4243{
40187f90 4244 /* Special case FSCharacteristics. Didn't exist originally. */
a3904c65 4245 switch (fast_cwd_version ())
40187f90 4246 {
a3904c65
CV
4247 case FCWD_OLD:
4248 break;
4249 case FCWD_W7:
4250 f7.FSCharacteristics = val;
4251 break;
4252 case FCWD_W8:
4253 f8.FSCharacteristics = val;
4254 break;
40187f90 4255 }
a3904c65
CV
4256}
4257
4258fcwd_version_t &
4259fcwd_access_t::fast_cwd_version ()
4260{
4261 return cygheap->cwd.fast_cwd_version;
4262}
4263
4264void
4265fcwd_access_t::CopyPath (UNICODE_STRING &target)
4266{
4267 /* Copy the Path contents over into the UNICODE_STRING referenced by
4268 target. This is used to set the CurrentDirectoryName in the
4269 user parameter block. */
4270 target = Path ();
4271}
4272
4273void
4274fcwd_access_t::Free (PVOID heap)
4275{
4276 /* Decrement the reference count. If it's down to 0, free
4277 structure from heap. */
ffcef702 4278 if (InterlockedDecrement (&ReferenceCount ()) == 0)
40187f90 4279 {
ffcef702
CV
4280 /* The handle on init is always a fresh one, not the handle inherited
4281 from the parent process. We always have to close it here.
4282 Note: The handle could be NULL, if we cd'ed into a virtual dir. */
a3904c65
CV
4283 HANDLE h = DirectoryHandle ();
4284 if (h)
4285 NtClose (h);
4286 RtlFreeHeap (heap, 0, this);
40187f90 4287 }
a3904c65
CV
4288}
4289
4290void
4291fcwd_access_t::FillIn (HANDLE dir, PUNICODE_STRING name,
4292 ULONG old_dismount_count)
4293{
4294 /* Fill in all values into this FAST_CWD structure. */
4295 DirectoryHandle () = dir;
4296 ReferenceCount () = 1;
4297 OldDismountCount () = old_dismount_count;
4298 /* The new structure stores the device characteristics of the
4299 volume holding the dir. RtlGetCurrentDirectory_U checks
4300 if the FILE_REMOVABLE_MEDIA flag is set and, if so, checks if
4301 the volume is still the same as the one used when opening
4302 the directory handle.
4303 We don't call NtQueryVolumeInformationFile for the \\?\PIPE,
4304 though. It just returns STATUS_INVALID_HANDLE anyway. */
4305 if (fast_cwd_version () != FCWD_OLD)
4306 {
4307 SetFSCharacteristics (0);
4308 if (name != &ro_u_pipedir)
40187f90 4309 {
a3904c65
CV
4310 IO_STATUS_BLOCK io;
4311 FILE_FS_DEVICE_INFORMATION ffdi;
4312 if (NT_SUCCESS (NtQueryVolumeInformationFile (dir, &io, &ffdi,
4313 sizeof ffdi, FileFsDeviceInformation)))
4314 SetFSCharacteristics (ffdi.Characteristics);
40187f90 4315 }
40187f90 4316 }
a3904c65
CV
4317 RtlInitEmptyUnicodeString (&Path (), Buffer (),
4318 MAX_PATH * sizeof (WCHAR));
4319 copy_cwd_str (&Path (), name);
4320}
40187f90 4321
a3904c65
CV
4322void
4323fcwd_access_t::SetDirHandleFromBufferPointer (PWCHAR buf_p, HANDLE dir)
4324{
4325 /* Input: The buffer pointer as it's stored in the user parameter block
4326 and a directory handle.
4327 This function computes the address to the FAST_CWD structure based
4328 on the version and overwrites the directory handle. It is only
4329 used if we couldn't figure out the address of fast_cwd_ptr. */
4330 fcwd_access_t *f_cwd;
4331 switch (fast_cwd_version ())
40187f90 4332 {
a3904c65
CV
4333 case FCWD_OLD:
4334 default:
4335 f_cwd = (fcwd_access_t *)
4336 ((PBYTE) buf_p - __builtin_offsetof (FAST_CWD_OLD, Buffer));
b56179f8 4337 break;
a3904c65
CV
4338 case FCWD_W7:
4339 f_cwd = (fcwd_access_t *)
4340 ((PBYTE) buf_p - __builtin_offsetof (FAST_CWD_7, Buffer));
b56179f8 4341 break;
a3904c65
CV
4342 case FCWD_W8:
4343 f_cwd = (fcwd_access_t *)
4344 ((PBYTE) buf_p - __builtin_offsetof (FAST_CWD_8, Buffer));
b56179f8 4345 break;
40187f90 4346 }
a3904c65
CV
4347 f_cwd->DirectoryHandle () = dir;
4348}
4349
4350void
4351fcwd_access_t::SetVersionFromPointer (PBYTE buf_p, bool is_buffer)
4352{
4353 /* Given a pointer to the FAST_CWD structure (is_buffer == false) or a
4354 pointer to the Buffer within (is_buffer == true), this function
4355 computes the FAST_CWD version by checking that Path.MaximumLength
4356 equals MAX_PATH, and that Path.Buffer == Buffer. */
4357 if (is_buffer)
4358 buf_p -= __builtin_offsetof (FAST_CWD_8, Buffer);
4359 fcwd_access_t *f_cwd = (fcwd_access_t *) buf_p;
4360 if (f_cwd->f8.Path.MaximumLength == MAX_PATH * sizeof (WCHAR)
4361 && f_cwd->f8.Path.Buffer == f_cwd->f8.Buffer)
4362 fast_cwd_version () = FCWD_W8;
4363 else if (f_cwd->f7.Path.MaximumLength == MAX_PATH * sizeof (WCHAR)
4364 && f_cwd->f7.Path.Buffer == f_cwd->f7.Buffer)
4365 fast_cwd_version () = FCWD_W7;
4366 else
4367 fast_cwd_version () = FCWD_OLD;
4368}
9c154abe 4369
9c154abe 4370/* This function scans the code in ntdll.dll to find the address of the
00e9bf2b
CV
4371 global variable used to access the CWD. While the pointer is global,
4372 it's not exported from the DLL, unfortunately. Therefore we have to
4373 use some knowledge to figure out the address. */
61522196
CV
4374
4375#ifdef __x86_64__
4376
4377#define peek32(x) (*(int32_t *)(x))
4378
4379static fcwd_access_t **
4380find_fast_cwd_pointer ()
4381{
4382 /* Fetch entry points of relevant functions in ntdll.dll. */
4383 HMODULE ntdll = GetModuleHandle ("ntdll.dll");
4384 if (!ntdll)
4385 return NULL;
4386 const uint8_t *get_dir = (const uint8_t *)
4387 GetProcAddress (ntdll, "RtlGetCurrentDirectory_U");
4388 const uint8_t *ent_crit = (const uint8_t *)
4389 GetProcAddress (ntdll, "RtlEnterCriticalSection");
4390 if (!get_dir || !ent_crit)
4391 return NULL;
4392 /* Search first relative call instruction in RtlGetCurrentDirectory_U. */
e5cadbfd 4393 const uint8_t *rcall = (const uint8_t *) memchr (get_dir, 0xe8, 80);
61522196
CV
4394 if (!rcall)
4395 return NULL;
4396 /* Fetch offset from instruction and compute address of called function.
4397 This function actually fetches the current FAST_CWD instance and
4398 performs some other actions, not important to us. */
4399 const uint8_t *use_cwd = rcall + 5 + peek32 (rcall + 1);
4400 /* Next we search for the locking mechanism and perform a sanity check.
4401 On Pre-Windows 8 we basically look for the RtlEnterCriticalSection call.
4402 Windows 8 does not call RtlEnterCriticalSection. The code manipulates
4403 the FastPebLock manually, probably because RtlEnterCriticalSection has
4404 been converted to an inline function. Either way, we test if the code
4405 uses the FastPebLock. */
4406 const uint8_t *movrbx;
4407 const uint8_t *lock = (const uint8_t *)
4408 memmem ((const char *) use_cwd, 80,
4409 "\xf0\x0f\xba\x35", 4);
4410 if (lock)
4411 {
4412 /* The lock instruction tweaks the LockCount member, which is not at
d2ef2331 4413 the start of the PRTL_CRITICAL_SECTION structure. So we have to
61522196
CV
4414 subtract the offset of LockCount to get the real address. */
4415 PRTL_CRITICAL_SECTION lockaddr =
4416 (PRTL_CRITICAL_SECTION) (lock + 9 + peek32 (lock + 4)
4417 - offsetof (RTL_CRITICAL_SECTION, LockCount));
4418 /* Test if lock address is FastPebLock. */
4419 if (lockaddr != NtCurrentTeb ()->Peb->FastPebLock)
4420 return NULL;
562b70fb 4421 /* Search `mov rel(%rip),%rbx'. This is the instruction fetching the
61522196
CV
4422 address of the current fcwd_access_t pointer, and it should be pretty
4423 near to the locking stuff. */
4424 movrbx = (const uint8_t *) memmem ((const char *) lock, 40,
4425 "\x48\x8b\x1d", 3);
4426 }
4427 else
4428 {
562b70fb
CV
4429 /* Usually the callq RtlEnterCriticalSection follows right after
4430 fetching the lock address. */
4431 int call_rtl_offset = 7;
4432 /* Search `lea rel(%rip),%rcx'. This loads the address of the lock into
4433 %rcx for the subsequent RtlEnterCriticalSection call. */
61522196
CV
4434 lock = (const uint8_t *) memmem ((const char *) use_cwd, 80,
4435 "\x48\x8d\x0d", 3);
4436 if (!lock)
562b70fb
CV
4437 {
4438 /* Windows 8.1 Preview calls `lea rel(rip),%r12' then some unrelated
4439 or, then `mov %r12,%rcx', then `callq RtlEnterCriticalSection'. */
4440 lock = (const uint8_t *) memmem ((const char *) use_cwd, 80,
4441 "\x4c\x8d\x25", 3);
4442 if (!lock)
4443 return NULL;
4444 call_rtl_offset = 14;
4445 }
61522196
CV
4446 PRTL_CRITICAL_SECTION lockaddr =
4447 (PRTL_CRITICAL_SECTION) (lock + 7 + peek32 (lock + 3));
4448 /* Test if lock address is FastPebLock. */
4449 if (lockaddr != NtCurrentTeb ()->Peb->FastPebLock)
4450 return NULL;
562b70fb
CV
4451 /* Next is the `callq RtlEnterCriticalSection'. */
4452 lock += call_rtl_offset;
61522196
CV
4453 if (lock[0] != 0xe8)
4454 return NULL;
4455 const uint8_t *call_addr = (const uint8_t *)
4456 (lock + 5 + peek32 (lock + 1));
4457 if (call_addr != ent_crit)
4458 return NULL;
4459 /* In contrast to the above Windows 8 code, we don't have to search
562b70fb 4460 for the `mov rel(%rip),%rbx' instruction. It follows right after
61522196
CV
4461 the call to RtlEnterCriticalSection. */
4462 movrbx = lock + 5;
4463 }
4464 if (!movrbx)
4465 return NULL;
4466 /* Check that the next instruction tests if the fetched value is NULL. */
4467 const uint8_t *testrbx = (const uint8_t *)
4468 memmem (movrbx + 7, 3, "\x48\x85\xdb", 3);
4469 if (!testrbx)
4470 return NULL;
4471 /* Compute address of the fcwd_access_t ** pointer. */
4472 return (fcwd_access_t **) (testrbx + peek32 (movrbx + 3));
4473}
4474#else
4475
4476#define peek32(x) (*(uint32_t *)(x))
4477
40187f90 4478static fcwd_access_t **
a413f8a2 4479find_fast_cwd_pointer ()
9c154abe 4480{
9c154abe
CV
4481 /* Fetch entry points of relevant functions in ntdll.dll. */
4482 HMODULE ntdll = GetModuleHandle ("ntdll.dll");
4483 if (!ntdll)
a413f8a2 4484 return NULL;
9c154abe
CV
4485 const uint8_t *get_dir = (const uint8_t *)
4486 GetProcAddress (ntdll, "RtlGetCurrentDirectory_U");
4487 const uint8_t *ent_crit = (const uint8_t *)
4488 GetProcAddress (ntdll, "RtlEnterCriticalSection");
4489 if (!get_dir || !ent_crit)
a413f8a2 4490 return NULL;
9c154abe 4491 /* Search first relative call instruction in RtlGetCurrentDirectory_U. */
e5cadbfd 4492 const uint8_t *rcall = (const uint8_t *) memchr (get_dir, 0xe8, 64);
9c154abe 4493 if (!rcall)
a413f8a2 4494 return NULL;
9c154abe
CV
4495 /* Fetch offset from instruction and compute address of called function.
4496 This function actually fetches the current FAST_CWD instance and
4497 performs some other actions, not important to us. */
4498 ptrdiff_t offset = (ptrdiff_t) peek32 (rcall + 1);
4499 const uint8_t *use_cwd = rcall + 5 + offset;
562b70fb 4500 /* Find first `push %edi' instruction. */
9c154abe 4501 const uint8_t *pushedi = (const uint8_t *) memchr (use_cwd, 0x57, 32);
4ddf5903
AL
4502 if (!pushedi)
4503 return NULL;
562b70fb
CV
4504 /* ...which should be followed by `mov crit-sect-addr,%edi' then
4505 `push %edi', or by just a single `push crit-sect-addr'. */
9c154abe 4506 const uint8_t *movedi = pushedi + 1;
2d487f2d 4507 const uint8_t *mov_pfast_cwd;
562b70fb 4508 if (movedi[0] == 0x8b && movedi[1] == 0xff) /* mov %edi,%edi -> W8 */
2d487f2d 4509 {
61522196 4510 /* Windows 8 does not call RtlEnterCriticalSection. The code manipulates
d2ef2331 4511 the FastPebLock manually, probably because RtlEnterCriticalSection has
2d487f2d
CV
4512 been converted to an inline function.
4513
562b70fb 4514 Next we search for a `mov some address,%eax'. This address points
2d487f2d
CV
4515 to the LockCount member of the FastPebLock structure, so the address
4516 is equal to FastPebLock + 4. */
4517 const uint8_t *moveax = (const uint8_t *) memchr (movedi, 0xb8, 16);
4518 if (!moveax)
4519 return NULL;
4520 offset = (ptrdiff_t) peek32 (moveax + 1) - 4;
4521 /* Compare the address with the known PEB lock as stored in the PEB. */
4522 if ((PRTL_CRITICAL_SECTION) offset != NtCurrentTeb ()->Peb->FastPebLock)
4523 return NULL;
4524 /* Now search for the mov instruction fetching the address of the global
4525 PFAST_CWD *. */
4526 mov_pfast_cwd = moveax;
4527 do
4528 {
4529 mov_pfast_cwd = (const uint8_t *) memchr (++mov_pfast_cwd, 0x8b, 48);
4530 }
4531 while (mov_pfast_cwd && mov_pfast_cwd[1] != 0x1d
4532 && (mov_pfast_cwd - moveax) < 48);
4533 if (!mov_pfast_cwd || mov_pfast_cwd[1] != 0x1d)
4534 return NULL;
4535 }
76173acf 4536 else
2d487f2d
CV
4537 {
4538 if (movedi[0] == 0xbf && movedi[5] == 0x57)
4539 rcall = movedi + 6;
4540 else if (movedi[0] == 0x68)
4541 rcall = movedi + 5;
562b70fb
CV
4542 else if (movedi[0] == 0x88 && movedi[4] == 0x83 && movedi[7] == 0x68)
4543 {
4544 /* Windows 8.1 Preview: The `mov lock_addr,%edi' is actually a
4545 `mov %cl,15(%esp), followed by an `or #-1,%ebx, followed by a
4546 `push lock_addr'. */
4547 movedi += 7;
4548 rcall = movedi + 5;
4549 }
2d487f2d
CV
4550 else
4551 return NULL;
4552 /* Compare the address used for the critical section with the known
4553 PEB lock as stored in the PEB. */
4554 if ((PRTL_CRITICAL_SECTION) peek32 (movedi + 1)
4555 != NtCurrentTeb ()->Peb->FastPebLock)
4556 return NULL;
4557 /* To check we are seeing the right code, we check our expectation that
4558 the next instruction is a relative call into RtlEnterCriticalSection. */
4559 if (rcall[0] != 0xe8)
4560 return NULL;
4561 /* Check that this is a relative call to RtlEnterCriticalSection. */
4562 offset = (ptrdiff_t) peek32 (rcall + 1);
4563 if (rcall + 5 + offset != ent_crit)
4564 return NULL;
4565 mov_pfast_cwd = rcall + 5;
4566 }
9c154abe
CV
4567 /* After locking the critical section, the code should read the global
4568 PFAST_CWD * pointer that is guarded by that critical section. */
2d487f2d 4569 if (mov_pfast_cwd[0] != 0x8b)
a413f8a2 4570 return NULL;
2d487f2d 4571 return (fcwd_access_t **) peek32 (mov_pfast_cwd + 2);
9c154abe 4572}
61522196 4573#endif
9c154abe 4574
40187f90 4575static fcwd_access_t **
a413f8a2
CV
4576find_fast_cwd ()
4577{
4578 /* Fetch the pointer but don't set the global fast_cwd_ptr yet. First
4579 we have to make sure we know the version of the FAST_CWD structure
4580 used on the system. */
40187f90 4581 fcwd_access_t **f_cwd_ptr = find_fast_cwd_pointer ();
a413f8a2 4582 if (!f_cwd_ptr)
a4f1f59b 4583 {
5ed8f235
CV
4584 bool warn = 1;
4585
a4f1f59b 4586#ifndef __x86_64__
5ed8f235
CV
4587 #ifndef PROCESSOR_ARCHITECTURE_ARM64
4588 #define PROCESSOR_ARCHITECTURE_ARM64 12
4589 #endif
4590
a4f1f59b
CV
4591 SYSTEM_INFO si;
4592
4593 /* Check if we're running in WOW64 on ARM64. Skip the warning as long as
5ed8f235
CV
4594 there's no solution for finding the FAST_CWD pointer on that system.
4595
4596 2018-07-12: Apparently current ARM64 WOW64 has a bug:
4597 It's GetNativeSystemInfo returns PROCESSOR_ARCHITECTURE_INTEL in
4598 wProcessorArchitecture. Since that's an invalid value (a 32 bit
4599 host system hosting a 32 bit emulator for itself?) we can use this
4600 value as an indicator to skip the message as well. */
4601 if (wincap.is_wow64 ())
4602 {
4603 GetNativeSystemInfo (&si);
4604 if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_ARM64
4605 || si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL)
4606 warn = 0;
4607 }
4608#endif /* !__x86_64__ */
4609
4610 if (warn)
4611 small_printf ("Cygwin WARNING:\n"
0793492d
CV
4612" Couldn't compute FAST_CWD pointer. This typically occurs if you're using\n"
4613" an older Cygwin version on a newer Windows. Please update to the latest\n"
4614" available Cygwin version from https://cygwin.com/. If the problem persists,\n"
21f22726 4615" please see https://cygwin.com/problems.html\n\n");
a4f1f59b 4616 }
a413f8a2
CV
4617 if (f_cwd_ptr && *f_cwd_ptr)
4618 {
40187f90
CV
4619 /* Just evaluate structure version. */
4620 fcwd_access_t::SetVersionFromPointer ((PBYTE) *f_cwd_ptr, false);
a413f8a2
CV
4621 }
4622 else
4623 {
53609fd2
CV
4624 /* If we couldn't fetch fast_cwd_ptr, or if fast_cwd_ptr is NULL(*)
4625 we have to figure out the version from the Buffer pointer in the
4626 ProcessParameters.
1b23b30b 4627
53609fd2
CV
4628 (*) This is very unlikely to happen when starting the first
4629 Cygwin process, since it only happens when starting the
4630 process in a directory which can't be used as CWD by Win32, or
4631 if the directory doesn't exist. But *if* it happens, we have
4632 no valid FAST_CWD structure, even though upp_cwd_str.Buffer is
4633 not NULL in that case. So we let the OS create a valid
4634 FAST_CWD structure temporarily to have something to work with.
4635 We know the pipe FS works. */
a413f8a2
CV
4636 PEB &peb = *NtCurrentTeb ()->Peb;
4637
4638 if (f_cwd_ptr /* so *f_cwd_ptr == NULL */
b86f999a 4639 && !NT_SUCCESS (RtlSetCurrentDirectory_U (&ro_u_pipedir)))
a413f8a2
CV
4640 api_fatal ("Couldn't set directory to %S temporarily.\n"
4641 "Cannot continue.", &ro_u_pipedir);
4642 RtlEnterCriticalSection (peb.FastPebLock);
40187f90
CV
4643 fcwd_access_t::SetVersionFromPointer
4644 ((PBYTE) peb.ProcessParameters->CurrentDirectoryName.Buffer, true);
a413f8a2
CV
4645 RtlLeaveCriticalSection (peb.FastPebLock);
4646 }
4647 /* Eventually, after we set the version as well, set fast_cwd_ptr. */
4648 return f_cwd_ptr;
4649}
4650
9c154abe
CV
4651void
4652cwdstuff::override_win32_cwd (bool init, ULONG old_dismount_count)
4653{
9c154abe
CV
4654 HANDLE h = NULL;
4655
4656 PEB &peb = *NtCurrentTeb ()->Peb;
4657 UNICODE_STRING &upp_cwd_str = peb.ProcessParameters->CurrentDirectoryName;
4658 HANDLE &upp_cwd_hdl = peb.ProcessParameters->CurrentDirectoryHandle;
4659
ffcef702
CV
4660 if (fast_cwd_ptr == (fcwd_access_t **) -1)
4661 fast_cwd_ptr = find_fast_cwd ();
4662 if (fast_cwd_ptr)
9c154abe 4663 {
00e9bf2b
CV
4664 /* If we got a valid value for fast_cwd_ptr, we can simply replace
4665 the RtlSetCurrentDirectory_U function entirely. */
ffcef702
CV
4666 PVOID heap = peb.ProcessHeap;
4667 /* First allocate a new fcwd_access_t structure on the heap.
4668 The new fcwd_access_t structure is 4 byte bigger than the old one,
4669 but we simply don't care, so we allocate always room for the
4670 new one. */
4671 fcwd_access_t *f_cwd = (fcwd_access_t *)
4672 RtlAllocateHeap (heap, 0, sizeof (fcwd_access_t));
4673 if (!f_cwd)
9c154abe 4674 {
ffcef702
CV
4675 debug_printf ("RtlAllocateHeap failed");
4676 return;
9c154abe 4677 }
ffcef702
CV
4678 /* Fill in the values. */
4679 f_cwd->FillIn (dir, error ? &ro_u_pipedir : &win32,
4680 old_dismount_count);
4681 /* Use PEB lock when switching fast_cwd_ptr to the new FAST_CWD
4682 structure and writing the CWD to the user process parameter
4683 block. This is equivalent to calling RtlAcquirePebLock/
4684 RtlReleasePebLock, but without having to go through the FS
4685 selector again. */
4686 RtlEnterCriticalSection (peb.FastPebLock);
4687 fcwd_access_t *old_cwd = *fast_cwd_ptr;
4688 *fast_cwd_ptr = f_cwd;
4689 f_cwd->CopyPath (upp_cwd_str);
4690 upp_cwd_hdl = dir;
4691 RtlLeaveCriticalSection (peb.FastPebLock);
4692 if (old_cwd)
4693 old_cwd->Free (heap);
9c154abe
CV
4694 }
4695 else
4696 {
698d93c4 4697 /* Fallback if we failed to find the fast_cwd_ptr value:
ffcef702 4698
698d93c4
CV
4699 - Call RtlSetCurrentDirectory_U.
4700 - Compute new FAST_CWD struct address from buffer pointer in the
4701 user process parameter block.
4702 - Replace the directory handle in the struct with our own handle.
4703 - Close the original handle. RtlSetCurrentDirectory_U already
4704 closed our former dir handle -> no handle leak.
ffcef702 4705
698d93c4
CV
4706 Guard the entire operation with FastPebLock to avoid races
4707 accessing the PEB and FAST_CWD struct.
ffcef702 4708
698d93c4
CV
4709 Unfortunately this method is still prone to a directory usage
4710 race condition:
ffcef702 4711
698d93c4
CV
4712 - The directory is locked against deletion or renaming between the
4713 RtlSetCurrentDirectory_U and the subsequent NtClose call. */
4714 if (unlikely (upp_cwd_hdl == NULL) && init)
4715 return;
4716 RtlEnterCriticalSection (peb.FastPebLock);
9c154abe 4717 if (!init)
ffcef702
CV
4718 {
4719 NTSTATUS status =
4720 RtlSetCurrentDirectory_U (error ? &ro_u_pipedir : &win32);
4721 if (!NT_SUCCESS (status))
4722 {
698d93c4 4723 RtlLeaveCriticalSection (peb.FastPebLock);
ffcef702
CV
4724 debug_printf ("RtlSetCurrentDirectory_U(%S) failed, %y",
4725 error ? &ro_u_pipedir : &win32, status);
4726 return;
4727 }
4728 }
ffcef702 4729 fcwd_access_t::SetDirHandleFromBufferPointer(upp_cwd_str.Buffer, dir);
9c154abe
CV
4730 h = upp_cwd_hdl;
4731 upp_cwd_hdl = dir;
ffcef702
CV
4732 RtlLeaveCriticalSection (peb.FastPebLock);
4733 /* The handle on init is always a fresh one, not the handle inherited
4734 from the parent process. We always have to close it here. */
4735 NtClose (h);
9c154abe 4736 }
1870c688
CV
4737}
4738
4739/* Initialize cygcwd 'muto' for serializing access to cwd info. */
4740void
4741cwdstuff::init ()
4742{
4743 cwd_lock.init ("cwd_lock");
260b8074
CV
4744
4745 /* Cygwin processes inherit the cwd from their parent. If the win32 path
9c154abe
CV
4746 buffer is not NULL, the cwd struct is already set up, and we only
4747 have to override the Win32 CWD with ours. */
260b8074 4748 if (win32.Buffer)
9c154abe
CV
4749 override_win32_cwd (true, SharedUserData.DismountCount);
4750 else
a3904c65
CV
4751 {
4752 /* Initialize fast_cwd stuff. */
4753 fast_cwd_ptr = (fcwd_access_t **) -1;
4754 fast_cwd_version = FCWD_W7;
4755 /* Initially re-open the cwd to allow POSIX semantics. */
4756 set (NULL, NULL);
4757 }
1870c688
CV
4758}
4759
4760/* Chdir and fill out the elements of a cwdstuff struct. */
4761int
260b8074 4762cwdstuff::set (path_conv *nat_cwd, const char *posix_cwd)
1870c688 4763{
1121c57f 4764 NTSTATUS status;
1870c688 4765 UNICODE_STRING upath;
9c154abe 4766 PEB &peb = *NtCurrentTeb ()->Peb;
260b8074
CV
4767 bool virtual_path = false;
4768 bool unc_path = false;
4769 bool inaccessible_path = false;
4770
1121c57f
CV
4771 /* Here are the problems with using SetCurrentDirectory. Just skip this
4772 comment if you don't like whining.
b86f999a 4773
260b8074
CV
4774 - SetCurrentDirectory only supports paths of up to MAX_PATH - 1 chars,
4775 including a trailing backslash. That's an absolute restriction, even
4776 in the UNICODE API.
4777
1121c57f
CV
4778 - SetCurrentDirectory fails for directories with strict permissions even
4779 for processes with the SE_BACKUP_NAME privilege enabled. The reason
4780 is apparently that SetCurrentDirectory calls NtOpenFile without the
260b8074
CV
4781 FILE_OPEN_FOR_BACKUP_INTENT flag set.
4782
4783 - SetCurrentDirectory does not support case-sensitivity.
4784
1121c57f
CV
4785 - Unlinking a cwd fails because SetCurrentDirectory seems to open
4786 directories so that deleting the directory is disallowed.
260b8074
CV
4787
4788 - SetCurrentDirectory can naturally not work on virtual Cygwin paths
4789 like /proc or /cygdrive.
4790
1121c57f
CV
4791 Nevertheless, doing entirely without SetCurrentDirectory is not really
4792 feasible, because it breaks too many mixed applications using the Win32
4793 API.
4794
4795 Therefore we handle the CWD all by ourselves and just keep the Win32
4796 CWD in sync. However, to avoid surprising behaviour in the Win32 API
4797 when we are in a CWD which is inaccessible as Win32 CWD, we set the
4798 Win32 CWD to a "weird" directory in which all relative filesystem-related
4799 calls fail. */
1870c688
CV
4800
4801 cwd_lock.acquire ();
4802
4803 if (nat_cwd)
4804 {
260b8074
CV
4805 upath = *nat_cwd->get_nt_native_path ();
4806 if (nat_cwd->isspecial ())
4807 virtual_path = true;
1870c688 4808 }
09ecdc85 4809
9c154abe 4810 /* Memorize old DismountCount before opening the dir. This value is
00e9bf2b
CV
4811 stored in the FAST_CWD structure. It would be simpler to fetch the
4812 old DismountCount in override_win32_cwd, but Windows also fetches
4813 it before opening the directory handle. It's not quite clear if
4814 that's really required, but since we don't know the side effects of
4815 this action, we better follow Windows' lead. */
9c154abe 4816 ULONG old_dismount_count = SharedUserData.DismountCount;
260b8074
CV
4817 /* Open a directory handle with FILE_OPEN_FOR_BACKUP_INTENT and with all
4818 sharing flags set. The handle is right now used in exceptions.cc only,
4819 but that might change in future. */
75a3b858 4820 HANDLE h = NULL;
260b8074 4821 if (!virtual_path)
1870c688 4822 {
1870c688
CV
4823 IO_STATUS_BLOCK io;
4824 OBJECT_ATTRIBUTES attr;
260b8074
CV
4825
4826 if (!nat_cwd)
4827 {
4828 /* On init, just reopen Win32 CWD with desired access flags.
4829 We can access the PEB without lock, because no other thread
88443b0a
CV
4830 can change the CWD. However, there's a chance that the handle
4831 is NULL, even though CurrentDirectoryName isn't so we have to
4832 be careful. */
4833 if (!peb.ProcessParameters->CurrentDirectoryHandle)
4834 {
4835 InitializeObjectAttributes (&attr,
4836 &peb.ProcessParameters->CurrentDirectoryName,
4837 OBJ_CASE_INSENSITIVE | OBJ_INHERIT, NULL, NULL);
4838 }
4839 else
4840 {
4841 RtlInitUnicodeString (&upath, L"");
4842 InitializeObjectAttributes (&attr,
4843 &upath, OBJ_CASE_INSENSITIVE | OBJ_INHERIT,
4844 peb.ProcessParameters->CurrentDirectoryHandle,
4845 NULL);
4846 }
260b8074
CV
4847 }
4848 else
4849 InitializeObjectAttributes (&attr, &upath,
4850 nat_cwd->objcaseinsensitive () | OBJ_INHERIT,
4851 NULL, NULL);
4852 /* First try without FILE_OPEN_FOR_BACKUP_INTENT, to find out if the
b86f999a 4853 directory is valid for Win32 apps. And, no, we can't just call
1121c57f
CV
4854 SetCurrentDirectory here, since that would potentially break
4855 case-sensitivity. */
1870c688
CV
4856 status = NtOpenFile (&h, SYNCHRONIZE | FILE_TRAVERSE, &attr, &io,
4857 FILE_SHARE_VALID_FLAGS,
4858 FILE_DIRECTORY_FILE
260b8074
CV
4859 | FILE_SYNCHRONOUS_IO_NONALERT);
4860 if (status == STATUS_ACCESS_DENIED)
b86f999a 4861 {
260b8074
CV
4862 status = NtOpenFile (&h, SYNCHRONIZE | FILE_TRAVERSE, &attr, &io,
4863 FILE_SHARE_VALID_FLAGS,
4864 FILE_DIRECTORY_FILE
4865 | FILE_SYNCHRONOUS_IO_NONALERT
4866 | FILE_OPEN_FOR_BACKUP_INTENT);
4867 inaccessible_path = true;
4868 }
1870c688
CV
4869 if (!NT_SUCCESS (status))
4870 {
88443b0a
CV
4871 /* Called from chdir? Just fail. */
4872 if (nat_cwd)
4873 {
4874 cwd_lock.release ();
4875 __seterrno_from_nt_status (status);
4876 return -1;
4877 }
4878 /* Otherwise we're in init and posix hasn't been set yet. Try to
4879 duplicate the handle instead. If that fails, too, set dir to NULL
4880 and carry on. This will at least set posix to some valid path at
4881 process startup, and subsequent getcwd calls don't EFAULT. */
4882 debug_printf ("WARNING: Can't reopen CWD %y '%S', status %y",
4883 peb.ProcessParameters->CurrentDirectoryHandle,
4884 &peb.ProcessParameters->CurrentDirectoryName,
4885 status);
4886 if (!peb.ProcessParameters->CurrentDirectoryHandle
4887 || !DuplicateHandle (GetCurrentProcess (),
4888 peb.ProcessParameters->CurrentDirectoryHandle,
4889 GetCurrentProcess (), &h, 0, TRUE, 0))
4890 {
4891 cwd_lock.release ();
4892 if (peb.ProcessParameters->CurrentDirectoryHandle)
4893 debug_printf ("...and DuplicateHandle failed with %E.");
4894 dir = NULL;
4895 }
1870c688 4896 }
260b8074 4897 }
9c154abe
CV
4898 /* Set new handle. Note that we simply overwrite the old handle here
4899 without closing it. The handle is also used as Win32 CWD handle in
4900 the user parameter block, and it will be closed in override_win32_cwd,
4901 if required. */
75a3b858 4902 dir = h;
260b8074
CV
4903
4904 if (!nat_cwd)
4905 {
4906 /* On init, just fetch the Win32 dir from the PEB. We can access
4907 the PEB without lock, because no other thread can change the CWD
4908 at that time. */
9c154abe 4909 PUNICODE_STRING pdir = &peb.ProcessParameters->CurrentDirectoryName;
260b8074
CV
4910 RtlInitEmptyUnicodeString (&win32,
4911 (PWCHAR) crealloc_abort (win32.Buffer,
b53c1929
CV
4912 pdir->Length
4913 + sizeof (WCHAR)),
4914 pdir->Length + sizeof (WCHAR));
260b8074
CV
4915 RtlCopyUnicodeString (&win32, pdir);
4916
4917 PWSTR eoBuffer = win32.Buffer + (win32.Length / sizeof (WCHAR));
4918 /* Remove trailing slash if one exists. */
4919 if ((eoBuffer - win32.Buffer) > 3 && eoBuffer[-1] == L'\\')
4920 win32.Length -= sizeof (WCHAR);
4921 if (eoBuffer[0] == L'\\')
4922 unc_path = true;
4923
4924 posix_cwd = NULL;
260b8074
CV
4925 }
4926 else
4927 {
a413f8a2 4928 if (!virtual_path) /* don't mangle virtual path. */
1870c688 4929 {
a413f8a2
CV
4930 /* Convert into Win32 path and compute length. */
4931 if (upath.Buffer[1] == L'?')
260b8074 4932 {
a413f8a2
CV
4933 upath.Buffer += 4;
4934 upath.Length -= 4 * sizeof (WCHAR);
4935 if (upath.Buffer[1] != L':')
4936 {
4937 /* UNC path */
4938 upath.Buffer += 2;
4939 upath.Length -= 2 * sizeof (WCHAR);
4940 unc_path = true;
4941 }
4942 }
4943 else
4944 {
4945 /* Path via native NT namespace. Prepend GLOBALROOT prefix
b86f999a 4946 to create a valid Win32 path. */
a413f8a2
CV
4947 PWCHAR buf = (PWCHAR) alloca (upath.Length
4948 + ro_u_globalroot.Length
4949 + sizeof (WCHAR));
4950 wcpcpy (wcpcpy (buf, ro_u_globalroot.Buffer), upath.Buffer);
4951 upath.Buffer = buf;
4952 upath.Length += ro_u_globalroot.Length;
260b8074 4953 }
260b8074
CV
4954 PWSTR eoBuffer = upath.Buffer + (upath.Length / sizeof (WCHAR));
4955 /* Remove trailing slash if one exists. */
4956 if ((eoBuffer - upath.Buffer) > 3 && eoBuffer[-1] == L'\\')
4957 upath.Length -= sizeof (WCHAR);
4958 }
4959 RtlInitEmptyUnicodeString (&win32,
4960 (PWCHAR) crealloc_abort (win32.Buffer,
b53c1929
CV
4961 upath.Length
4962 + sizeof (WCHAR)),
4963 upath.Length + sizeof (WCHAR));
260b8074
CV
4964 RtlCopyUnicodeString (&win32, &upath);
4965 if (unc_path)
4966 win32.Buffer[0] = L'\\';
4967 }
4968 /* Make sure it's NUL-terminated. */
4969 win32.Buffer[win32.Length / sizeof (WCHAR)] = L'\0';
4970
4971 /* Set drive_length, used in path conversion, and error code, used in
4972 spawn_guts to decide whether a native Win32 app can be started or not. */
4973 if (virtual_path)
4974 {
4975 drive_length = 0;
4976 error = ENOTDIR;
1870c688 4977 }
260b8074 4978 else
1870c688 4979 {
260b8074 4980 if (!unc_path)
1870c688 4981 drive_length = 2;
260b8074 4982 else
1870c688
CV
4983 {
4984 PWCHAR ptr = wcschr (win32.Buffer + 2, L'\\');
4985 if (ptr)
4986 ptr = wcschr (ptr + 1, L'\\');
4987 if (ptr)
4988 drive_length = ptr - win32.Buffer;
4989 else
4990 drive_length = win32.Length / sizeof (WCHAR);
4991 }
260b8074
CV
4992 if (inaccessible_path)
4993 error = EACCES;
4994 else if (win32.Length > (MAX_PATH - 2) * sizeof (WCHAR))
4995 error = ENAMETOOLONG;
4996 else
4997 error = 0;
4998 }
1121c57f
CV
4999 /* Keep the Win32 CWD in sync. Don't check for error, other than for
5000 strace output. Try to keep overhead low. */
9c154abe 5001 override_win32_cwd (!nat_cwd, old_dismount_count);
1870c688 5002
260b8074
CV
5003 /* Eventually, create POSIX path if it's not set on entry. */
5004 tmp_pathbuf tp;
5005 if (!posix_cwd)
5006 {
5007 posix_cwd = (const char *) tp.c_get ();
5008 mount_table->conv_to_posix_path (win32.Buffer, (char *) posix_cwd, 0);
1870c688 5009 }
260b8074
CV
5010 posix = (char *) crealloc_abort (posix, strlen (posix_cwd) + 1);
5011 stpcpy (posix, posix_cwd);
7e24f1bf 5012
1870c688 5013 cwd_lock.release ();
260b8074
CV
5014 return 0;
5015}
5016
5017const char *
5018cwdstuff::get_error_desc () const
5019{
5020 switch (cygheap->cwd.get_error ())
5021 {
5022 case EACCES:
5023 return "has restricted permissions which render it\n"
5024 "inaccessible as Win32 working directory";
5025 case ENOTDIR:
5026 return "is a virtual Cygwin directory which does\n"
5027 "not exist for a native Windows application";
5028 case ENAMETOOLONG:
5029 return "has a path longer than allowed for a\n"
5030 "Win32 working directory";
5031 default:
5032 break;
5033 }
5034 /* That shouldn't occur, unless we defined a new error code
5035 in cwdstuff::set. */
5036 return "is not accessible for some unknown reason";
1870c688
CV
5037}
5038
ee42ccd3
CV
5039/* Store incoming wchar_t path as current posix cwd. This is called from
5040 setlocale so that the cwd is always stored in the right charset. */
5041void
5042cwdstuff::reset_posix (wchar_t *w_cwd)
b6c6ea43 5043{
ee42ccd3
CV
5044 size_t len = sys_wcstombs (NULL, (size_t) -1, w_cwd);
5045 posix = (char *) crealloc_abort (posix, len + 1);
5046 sys_wcstombs (posix, len + 1, w_cwd);
b6c6ea43
CV
5047}
5048
1870c688
CV
5049char *
5050cwdstuff::get (char *buf, int need_posix, int with_chroot, unsigned ulen)
5051{
1870c688 5052 tmp_pathbuf tp;
c1f177d6
KB
5053
5054 errno = 0;
1870c688
CV
5055 if (ulen)
5056 /* nothing */;
5057 else if (buf == NULL)
5058 ulen = (unsigned) -1;
5059 else
5060 {
5061 set_errno (EINVAL);
5062 goto out;
5063 }
7e24f1bf 5064
1870c688 5065 cwd_lock.acquire ();
7e24f1bf 5066
1870c688
CV
5067 char *tocopy;
5068 if (!need_posix)
5069 {
5070 tocopy = tp.c_get ();
5071 sys_wcstombs (tocopy, NT_MAX_PATH, win32.Buffer,
5072 win32.Length / sizeof (WCHAR));
5073 }
5074 else
5075 tocopy = posix;
7905c4f1 5076
1870c688
CV
5077 debug_printf ("posix %s", posix);
5078 if (strlen (tocopy) >= ulen)
5079 {
5080 set_errno (ERANGE);
5081 buf = NULL;
5082 }
5083 else
5084 {
5085 if (!buf)
5086 buf = (char *) malloc (strlen (tocopy) + 1);
5087 strcpy (buf, tocopy);
5088 if (!buf[0]) /* Should only happen when chroot */
5089 strcpy (buf, "/");
5090 }
7905c4f1 5091
1870c688
CV
5092 cwd_lock.release ();
5093
5094out:
61522196 5095 syscall_printf ("(%s) = cwdstuff::get (%p, %u, %d, %d), errno %d",
1870c688 5096 buf, buf, ulen, need_posix, with_chroot, errno);
1870c688
CV
5097 return buf;
5098}
5099
1870c688
CV
5100/* No need to be reentrant or thread-safe according to SUSv3.
5101 / and \\ are treated equally. Leading drive specifiers are
5102 kept intact as far as it makes sense. Everything else is
5103 POSIX compatible. */
5104extern "C" char *
5105basename (char *path)
5106{
5107 static char buf[4];
5108 char *c, *d, *bs = path;
5109
5110 if (!path || !*path)
5111 return strcpy (buf, ".");
5112 if (isalpha (path[0]) && path[1] == ':')
5113 bs += 2;
5114 else if (strspn (path, "/\\") > 1)
5115 ++bs;
5116 c = strrchr (bs, '/');
5117 if ((d = strrchr (c ?: bs, '\\')) > c)
5118 c = d;
5119 if (c)
5120 {
5121 /* Trailing (back)slashes are eliminated. */
5122 while (c && c > bs && c[1] == '\0')
5123 {
5124 *c = '\0';
5125 c = strrchr (bs, '/');
5126 if ((d = strrchr (c ?: bs, '\\')) > c)
5127 c = d;
5128 }
5129 if (c && (c > bs || c[1]))
5130 return c + 1;
5131 }
5132 else if (!bs[0])
5133 {
5134 stpncpy (buf, path, bs - path);
5135 stpcpy (buf + (bs - path), ".");
5136 return buf;
5137 }
5138 return path;
5139}
5140
75d5f68a
YS
5141/* The differences with the POSIX version above:
5142 - declared in <string.h> (instead of <libgen.h>);
5143 - the argument is never modified, and therefore is marked const;
5144 - the empty string is returned if path is an empty string, "/", or ends
5145 with a trailing slash. */
5146extern "C" char *
5147__gnu_basename (const char *path)
5148{
5149 static char buf[1];
5150 char *c, *d, *bs = (char *)path;
5151
5152 if (!path || !*path)
5153 return strcpy (buf, "");
5154 if (isalpha (path[0]) && path[1] == ':')
5155 bs += 2;
5156 else if (strspn (path, "/\\") > 1)
5157 ++bs;
5158 c = strrchr (bs, '/');
5159 if ((d = strrchr (c ?: bs, '\\')) > c)
5160 c = d;
5161 if (c)
5162 return c + 1;
5163 else if (!bs[0])
5164 return strcpy (buf, "");
5165 return (char *)path;
5166}
5167
1870c688
CV
5168/* No need to be reentrant or thread-safe according to SUSv3.
5169 / and \\ are treated equally. Leading drive specifiers and
5170 leading double (back)slashes are kept intact as far as it
5171 makes sense. Everything else is POSIX compatible. */
5172extern "C" char *
5173dirname (char *path)
5174{
5175 static char buf[4];
5176 char *c, *d, *bs = path;
5177
5178 if (!path || !*path)
5179 return strcpy (buf, ".");
5180 if (isalpha (path[0]) && path[1] == ':')
5181 bs += 2;
5182 else if (strspn (path, "/\\") > 1)
5183 ++bs;
5184 c = strrchr (bs, '/');
5185 if ((d = strrchr (c ?: bs, '\\')) > c)
5186 c = d;
5187 if (c)
5188 {
5189 /* Trailing (back)slashes are eliminated. */
5190 while (c && c > bs && c[1] == '\0')
5191 {
5192 *c = '\0';
5193 c = strrchr (bs, '/');
5194 if ((d = strrchr (c ?: bs, '\\')) > c)
5195 c = d;
5196 }
5197 if (!c)
5198 strcpy (bs, ".");
5199 else if (c > bs)
5200 {
5201 /* More trailing (back)slashes are eliminated. */
5202 while (c > bs && (*c == '/' || *c == '\\'))
5203 *c-- = '\0';
5204 }
5205 else
5206 c[1] = '\0';
5207 }
5208 else
5209 {
5210 stpncpy (buf, path, bs - path);
5211 stpcpy (buf + (bs - path), ".");
5212 return buf;
5213 }
5214 return path;
5215}
This page took 1.600154 seconds and 5 git commands to generate.