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