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