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