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