-
Notifications
You must be signed in to change notification settings - Fork 38
/
path.cc
5433 lines (5019 loc) · 160 KB
/
path.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* path.cc: path support.
This file is part of Cygwin.
This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
/* This module's job is to
- convert between POSIX and Win32 style filenames,
- support the `mount' functionality,
- support symlinks for files and directories
Pathnames are handled as follows:
- A \ or : in a path denotes a pure windows spec.
- Paths beginning with // (or \\) are not translated (i.e. looked
up in the mount table) and are assumed to be UNC path names.
The goal in the above set of rules is to allow both POSIX and Win32
flavors of pathnames without either interfering. The rules are
intended to be as close to a superset of both as possible.
Note that you can have more than one path to a file. The mount
table is always prefered when translating Win32 paths to POSIX
paths. Win32 paths in mount table entries may be UNC paths or
standard Win32 paths starting with <drive-letter>:
Text vs Binary issues are not considered here in path style
decisions, although the appropriate flags are retrieved and
stored in various structures.
Removing mounted filesystem support would simplify things greatly,
but having it gives us a mechanism of treating disk that lives on a
UNIX machine as having UNIX semantics [it allows one to edit a text
file on that disk and not have cr's magically appear and perhaps
break apps running on UNIX boxes]. It also useful to be able to
layout a hierarchy without changing the underlying directories.
The semantics of mounting file systems is not intended to precisely
follow normal UNIX systems.
Each DOS drive is defined to have a current directory. Supporting
this would complicate things so for now things are defined so that
c: means c:\.
*/
/* This file includes both the XPG and GNU basename functions, with the
former exported as "basename" for ABI compatibility but the latter
declared as such for source compatibility with glibc. This tells
<string.h> not to declare the GNU variant in order to prevent a conflicting
declaration error with the XPG variant implemented herein. */
#define basename basename
#include "winsup.h"
#include <w32api/winioctl.h>
#include <w32api/shlobj.h>
#include <sys/param.h>
#include <sys/cygwin.h>
#include <wctype.h>
#include <assert.h>
#include "cygerrno.h"
#include "path.h"
#include "fhandler.h"
#include "dtable.h"
#include "cygheap.h"
#include "shared_info.h"
#include "tls_pbuf.h"
#include "environ.h"
#include "msys2_path_conv.h"
#undef basename
suffix_info stat_suffixes[] =
{
suffix_info ("", 1),
suffix_info (".exe", 1),
suffix_info (NULL)
};
struct symlink_info
{
char contents[SYMLINK_MAX + 1];
char *ext_here;
int extn;
unsigned path_flags;
unsigned mount_flags;
unsigned pc_flags; /* Relevant pathconv_arg flags from path_conv caller */
DWORD fileattr;
int issymlink;
bool ext_tacked_on;
int error;
bool isdevice;
_major_t major;
_minor_t minor;
__mode_t mode;
int check (char *path, const suffix_info *suffixes, fs_info &fs,
path_conv_handle &conv_hdl);
int set (char *path);
bool parse_device (const char *);
int check_sysfile (HANDLE h);
int check_shortcut (HANDLE h);
int check_reparse_point (HANDLE h, bool remote);
int check_nfs_symlink (HANDLE h);
int posixify (char *srcbuf);
bool set_error (int);
};
SRWLOCK NO_COPY cwdstuff::cwd_lock;
static const GUID GUID_shortcut
= { 0x00021401L, 0, 0, {0xc0, 0, 0, 0, 0, 0, 0, 0x46}};
enum
{
WSH_FLAG_IDLIST = 0x01, /* Contains an ITEMIDLIST. */
WSH_FLAG_FILE = 0x02, /* Contains a file locator element. */
WSH_FLAG_DESC = 0x04, /* Contains a description. */
WSH_FLAG_RELPATH = 0x08, /* Contains a relative path. */
WSH_FLAG_WD = 0x10, /* Contains a working dir. */
WSH_FLAG_CMDLINE = 0x20, /* Contains command line args. */
WSH_FLAG_ICON = 0x40 /* Contains a custom icon. */
};
struct win_shortcut_hdr
{
DWORD size; /* Header size in bytes. Must contain 0x4c. */
GUID magic; /* GUID of shortcut files. */
DWORD flags; /* Content flags. See above. */
/* The next fields from attr to icon_no are always set to 0 in Cygwin
and U/Win shortcuts. */
DWORD attr; /* Target file attributes. */
FILETIME ctime; /* These filetime items are never touched by the */
FILETIME mtime; /* system, apparently. Values don't matter. */
FILETIME atime;
DWORD filesize; /* Target filesize. */
DWORD icon_no; /* Icon number. */
DWORD run; /* Values defined in winuser.h. Use SW_NORMAL. */
DWORD hotkey; /* Hotkey value. Set to 0. */
DWORD dummy[2]; /* Future extension probably. Always 0. */
};
/* Return non-zero if PATH1 is a prefix of PATH2.
Both are assumed to be of the same path style and / vs \ usage.
Neither may be "".
LEN1 = strlen (PATH1). It's passed because often it's already known.
Examples:
/foo/ is a prefix of /foo <-- may seem odd, but desired
/foo is a prefix of /foo/
/ is a prefix of /foo/bar
/ is not a prefix of foo/bar
foo/ is a prefix foo/bar
/foo is not a prefix of /foobar
*/
int
path_prefix_p (const char *path1, const char *path2, int len1,
bool caseinsensitive)
{
/* Handle case where PATH1 has trailing '/' and when it doesn't. */
if (len1 > 0 && isdirsep (path1[len1 - 1]))
len1--;
if (len1 == 0)
return isdirsep (path2[0]) && !isdirsep (path2[1]);
if (isdirsep (path2[len1]) || path2[len1] == 0 || path1[len1 - 1] == ':')
return caseinsensitive ? strncasematch (path1, path2, len1)
: !strncmp (path1, path2, len1);
return 0;
}
/* Return non-zero if paths match in first len chars.
Check is dependent of the case sensitivity setting. */
int
pathnmatch (const char *path1, const char *path2, int len, bool caseinsensitive)
{
return caseinsensitive
? strncasematch (path1, path2, len) : !strncmp (path1, path2, len);
}
/* Return non-zero if paths match. Check is dependent of the case
sensitivity setting. */
int
pathmatch (const char *path1, const char *path2, bool caseinsensitive)
{
return caseinsensitive
? strcasematch (path1, path2) : !strcmp (path1, path2);
}
/* TODO: This function is used in mkdir and rmdir to generate correct
error messages in case of paths ending in /. or /.. components.
Right now, normalize_posix_path will just normalize
those components away, which changes the semantics. */
bool
has_dot_last_component (const char *dir, bool test_dot_dot)
{
/* SUSv3: . and .. are not allowed as last components in various system
calls. Don't test for backslash path separator since that's a Win32
path following Win32 rules. */
const char *last_comp = strchr (dir, '\0');
if (last_comp == dir)
return false; /* Empty string. Probably shouldn't happen here? */
/* Detect run of trailing slashes */
while (last_comp > dir && *--last_comp == '/')
continue;
/* Detect just a run of slashes or a path that does not end with a slash. */
if (*last_comp != '.')
return false;
/* We know we have a trailing dot here. Check that it really is a standalone "."
path component by checking that it is at the beginning of the string or is
preceded by a "/" */
if (last_comp == dir || *--last_comp == '/')
return true;
/* If we're not checking for '..' we're done. Ditto if we're now pointing to
a non-dot. */
if (!test_dot_dot || *last_comp != '.')
return false; /* either not testing for .. or this was not '..' */
/* Repeat previous test for standalone or path component. */
return last_comp == dir || last_comp[-1] == '/';
}
/* Normalize a POSIX path.
All duplicate /'s, except for 2 leading /'s, are deleted.
The result is 0 for success, or an errno error value. */
int
normalize_posix_path (const char *src, char *dst, char *&tail)
{
const char *in_src = src;
char *dst_start = dst;
bool check_parent = false;
syscall_printf ("src %s", src);
if ((isdrive (src) && isdirsep (src[2])) || *src == '\\')
goto win32_path;
tail = dst;
if (!isslash (src[0]))
{
if (!cygheap->cwd.get (dst))
return get_errno ();
tail = strchr (tail, '\0');
if (isslash (dst[0]) && isslash (dst[1]))
++dst_start;
if (*src == '.')
{
if (tail == dst_start + 1 && *dst_start == '/')
tail--;
goto sawdot;
}
if (tail > dst && !isslash (tail[-1]))
*tail++ = '/';
}
/* Two leading /'s? If so, preserve them. */
else if (isslash (src[1]) && !isslash (src[2]))
{
*tail++ = *src++;
++dst_start;
}
while (*src)
{
if (*src == '\\')
goto win32_path;
/* Strip runs of /'s. */
if (!isslash (*src))
*tail++ = *src++;
else
{
check_parent = true;
while (*++src)
{
if (isslash (*src))
continue;
if (*src != '.')
break;
sawdot:
if (src[1] != '.')
{
if (!src[1])
{
*tail++ = '/';
goto done;
}
if (!isslash (src[1]))
break;
}
else if (src[2] && !isslash (src[2]))
break;
else
{
/* According to POSIX semantics all elements of path must
exist. To follow it, we must validate our path before
removing the trailing component. Check_parent is needed
for performance optimization, in order not to verify paths
which are already verified. For example this prevents
double check in case of foo/bar/../.. */
if (check_parent)
{
if (tail > dst_start) /* Don't check for / or // dir. */
{
*tail = 0;
debug_printf ("checking %s before '..'", dst);
/* In conjunction with native and NFS symlinks,
this call can result in a recursion which eats
up our tmp_pathbuf buffers. This in turn results
in a api_fatal call. To avoid that, we're
checking our remaining buffers and return an
error code instead. Note that this only happens
if the path contains 15 or more relative native/NFS
symlinks with a ".." in the target path. */
tmp_pathbuf tp;
if (!tp.check_usage (4, 3))
return ELOOP;
path_conv head (dst, PC_SYM_FOLLOW | PC_POSIX);
if (!head.exists ())
return ENOENT;
if (!head.isdir ())
return ENOTDIR;
/* At this point, dst is a normalized path. If the
normalized path created by path_conv does not
match the normalized path we're just testing, then
the path in dst contains native symlinks. If we
just plunge along, removing the previous path
component, we may end up removing a symlink from
the path and the resulting path will be invalid.
So we replace dst with what we found in head
instead. All the work replacing symlinks has been
done in that path anyway, so why repeat it? */
tail = stpcpy (dst, head.get_posix ());
}
check_parent = false;
}
while (tail > dst_start && !isslash (*--tail))
continue;
src++;
}
}
*tail++ = '/';
}
if ((tail - dst) >= NT_MAX_PATH)
{
debug_printf ("ENAMETOOLONG = normalize_posix_path (%s)", src);
return ENAMETOOLONG;
}
}
done:
*tail = '\0';
debug_printf ("%s = normalize_posix_path (%s)", dst, in_src);
return 0;
win32_path:
int err = normalize_win32_path (in_src, dst, tail);
if (!err)
for (char *p = dst; (p = strchr (p, '\\')); p++)
*p = '/';
return err ?: -1;
}
inline void
path_conv::add_ext_from_sym (symlink_info &sym)
{
if (sym.ext_here && *sym.ext_here)
{
suffix = path + sym.extn;
if (sym.ext_tacked_on)
strcpy ((char *) suffix, sym.ext_here);
}
}
static void mkrelpath (char *dst, bool caseinsensitive);
static void
mkrelpath (char *path, bool caseinsensitive)
{
tmp_pathbuf tp;
char *cwd_win32 = tp.c_get ();
if (!cygheap->cwd.get (cwd_win32, 0))
return;
unsigned cwdlen = strlen (cwd_win32);
if (!path_prefix_p (cwd_win32, path, cwdlen, caseinsensitive))
return;
size_t n = strlen (path);
if (n < cwdlen)
return;
char *tail = path;
if (n == cwdlen)
tail += cwdlen;
else
tail += isdirsep (cwd_win32[cwdlen - 1]) ? cwdlen : cwdlen + 1;
memmove (path, tail, strlen (tail) + 1);
if (!*path)
strcpy (path, ".");
}
void
path_conv::set_posix (const char *path_copy)
{
if (path_copy)
{
size_t n = strlen (path_copy) + 1;
char *p = (char *) crealloc_abort ((void *) posix_path, n);
posix_path = (const char *) memcpy (p, path_copy, n);
}
}
static inline void
str2uni_cat (UNICODE_STRING &tgt, const char *srcstr)
{
int len = sys_mbstowcs (tgt.Buffer + tgt.Length / sizeof (WCHAR),
(tgt.MaximumLength - tgt.Length) / sizeof (WCHAR),
srcstr);
if (len)
tgt.Length += (len - 1) * sizeof (WCHAR);
}
PUNICODE_STRING
get_nt_native_path (const char *path, UNICODE_STRING& upath, bool dos)
{
upath.Length = 0;
if (path[0] == '/') /* special path w/o NT path representation. */
str2uni_cat (upath, path);
else if (path[0] != '\\') /* X:\... or relative path. */
{
if (path[1] == ':') /* X:\... */
{
RtlAppendUnicodeStringToString (&upath, &ro_u_natp);
str2uni_cat (upath, path);
/* The drive letter must be upper case. */
upath.Buffer[4] = towupper (upath.Buffer[4]);
transform_chars (&upath, 7);
}
else /* relative path */
{
str2uni_cat (upath, path);
transform_chars (&upath, 0);
}
}
else if (path[1] != '\\') /* \Device\... */
str2uni_cat (upath, path);
else if ((path[2] != '.' && path[2] != '?')
|| path[3] != '\\') /* \\server\share\... */
{
RtlAppendUnicodeStringToString (&upath, &ro_u_uncp);
str2uni_cat (upath, path + 2);
transform_chars (&upath, 8);
}
else /* \\.\device or \\?\foo */
{
RtlAppendUnicodeStringToString (&upath, &ro_u_natp);
str2uni_cat (upath, path + 4);
}
if (dos)
{
/* Unfortunately we can't just use transform_chars with the tfx_rev_chars
table since only leading and trailing spaces and dots are affected.
So we step to every backslash and fix surrounding dots and spaces.
That makes these broken filesystems a bit slower, but, hey. */
PWCHAR cp = upath.Buffer + 7;
PWCHAR cend = upath.Buffer + upath.Length / sizeof (WCHAR);
while (++cp < cend)
if (*cp == L'\\')
{
PWCHAR ccp = cp - 1;
while (*ccp == L'.' || *ccp == L' ')
*ccp-- |= 0xf000;
while (cp[1] == L' ')
*++cp |= 0xf000;
}
while (*--cp == L'.' || *cp == L' ')
*cp |= 0xf000;
}
return &upath;
}
/* Handle with extrem care! Only used in a certain instance in try_to_bin.
Every other usage needs a careful check. */
void
path_conv::set_nt_native_path (PUNICODE_STRING new_path)
{
wide_path = (PWCHAR) crealloc_abort (wide_path, new_path->MaximumLength);
memcpy (wide_path, new_path->Buffer, new_path->Length);
uni_path.Length = new_path->Length;
uni_path.MaximumLength = new_path->MaximumLength;
uni_path.Buffer = wide_path;
}
/* If suffix is not NULL, append the suffix string verbatim.
This is used by fhandler_mqueue::mq_open to append an NTFS stream suffix. */
PUNICODE_STRING
path_conv::get_nt_native_path (PUNICODE_STRING suffix)
{
PUNICODE_STRING res;
if (wide_path)
res = &uni_path;
else if (!path)
res = NULL;
else
{
uni_path.Length = 0;
uni_path.MaximumLength = (strlen (path) + 10) * sizeof (WCHAR);
if (suffix)
uni_path.MaximumLength += suffix->Length;
wide_path = (PWCHAR) cmalloc_abort (HEAP_STR, uni_path.MaximumLength);
uni_path.Buffer = wide_path;
::get_nt_native_path (path, uni_path, has_dos_filenames_only ());
if (suffix)
RtlAppendUnicodeStringToString (&uni_path, suffix);
res = &uni_path;
}
return res;
}
PWCHAR
path_conv::get_wide_win32_path (PWCHAR wc)
{
get_nt_native_path ();
if (!wide_path)
return NULL;
wcpcpy (wc, wide_path);
if (wc[1] == L'?')
wc[1] = L'\\';
return wc;
}
static DWORD
getfileattr (const char *path, bool caseinsensitive) /* path has to be always absolute. */
{
tmp_pathbuf tp;
UNICODE_STRING upath;
OBJECT_ATTRIBUTES attr;
FILE_BASIC_INFORMATION fbi;
NTSTATUS status;
IO_STATUS_BLOCK io;
tp.u_get (&upath);
InitializeObjectAttributes (&attr, &upath,
caseinsensitive ? OBJ_CASE_INSENSITIVE : 0,
NULL, NULL);
get_nt_native_path (path, upath, false);
status = NtQueryAttributesFile (&attr, &fbi);
if (NT_SUCCESS (status))
return fbi.FileAttributes;
if (status != STATUS_OBJECT_NAME_NOT_FOUND
&& status != STATUS_NO_SUCH_FILE) /* File not found on 9x share */
{
/* File exists but access denied. Try to get attribute through
directory query. */
UNICODE_STRING dirname, basename;
HANDLE dir;
FILE_BOTH_DIR_INFORMATION fdi;
RtlSplitUnicodePath (&upath, &dirname, &basename);
InitializeObjectAttributes (&attr, &dirname,
caseinsensitive ? OBJ_CASE_INSENSITIVE : 0,
NULL, NULL);
status = NtOpenFile (&dir, SYNCHRONIZE | FILE_LIST_DIRECTORY,
&attr, &io, FILE_SHARE_VALID_FLAGS,
FILE_SYNCHRONOUS_IO_NONALERT
| FILE_OPEN_NO_RECALL
| FILE_OPEN_FOR_BACKUP_INTENT
| FILE_DIRECTORY_FILE);
if (NT_SUCCESS (status))
{
status = NtQueryDirectoryFile (dir, NULL, NULL, 0, &io,
&fdi, sizeof fdi,
FileBothDirectoryInformation,
TRUE, &basename, TRUE);
NtClose (dir);
if (NT_SUCCESS (status) || status == STATUS_BUFFER_OVERFLOW)
return fdi.FileAttributes;
}
}
SetLastError (RtlNtStatusToDosError (status));
return INVALID_FILE_ATTRIBUTES;
}
/* Convert an arbitrary path SRC to a pure Win32 path, suitable for
passing to Win32 API routines.
If an error occurs, `error' is set to the errno value.
Otherwise it is set to 0.
follow_mode values:
SYMLINK_FOLLOW - convert to PATH symlink points to
SYMLINK_NOFOLLOW - convert to PATH of symlink itself
SYMLINK_IGNORE - do not check PATH for symlinks
SYMLINK_CONTENTS - just return symlink contents
*/
/* TODO: This implementation is only preliminary. For internal
purposes it's necessary to have a path_conv::check function which
takes a UNICODE_STRING src path, otherwise we waste a lot of time
for converting back and forth. The below implementation does
realy nothing but converting to char *, until path_conv handles
wide-char paths directly. */
void
path_conv::check (const UNICODE_STRING *src, unsigned opt,
const suffix_info *suffixes)
{
tmp_pathbuf tp;
char *path = tp.c_get ();
user_shared->warned_msdos = true;
sys_wcstombs (path, NT_MAX_PATH, src->Buffer, src->Length / sizeof (WCHAR));
path_conv::check (path, opt, suffixes);
}
void
path_conv::check (const char *src, unsigned opt,
const suffix_info *suffixes)
{
/* The tmp_buf array is used when expanding symlinks. It is NT_MAX_PATH * 2
in length so that we can hold the expanded symlink plus a trailer. */
tmp_pathbuf tp;
char *path_copy = tp.c_get ();
char *pathbuf = tp.c_get ();
char *tmp_buf = tp.t_get ();
char *THIS_path = tp.c_get ();
symlink_info sym;
bool need_directory = 0;
bool add_ext = false;
bool is_relpath;
char *tail, *path_end;
#if 0
static path_conv last_path_conv;
static char last_src[CYG_MAX_PATH];
if (*last_src && strcmp (last_src, src) == 0)
{
*this = last_path_conv;
return;
}
#endif
__try
{
int loop = 0;
mount_flags = 0;
path_flags = 0;
suffix = NULL;
fileattr = INVALID_FILE_ATTRIBUTES;
caseinsensitive = OBJ_CASE_INSENSITIVE;
if (wide_path)
cfree (wide_path);
wide_path = NULL;
if (path)
{
cfree (modifiable_path ());
path = NULL;
}
close_conv_handle ();
fs.clear ();
if (posix_path)
{
cfree ((void *) posix_path);
posix_path = NULL;
}
int component = 0; // Number of translated components
if (!(opt & PC_NULLEMPTY))
error = 0;
else if (!*src)
{
error = ENOENT;
return;
}
bool is_msdos = false;
/* This loop handles symlink expansion. */
for (;;)
{
is_relpath = !isabspath (src);
error = normalize_posix_path (src, path_copy, tail);
if (error > 0)
return;
if (error < 0)
{
if (component == 0)
is_msdos = true;
error = 0;
}
/* Detect if the user was looking for a directory. We have to strip
the trailing slash initially while trying to add extensions but
take it into account during processing */
if (tail > path_copy + 2 && isslash (tail[-1]))
{
need_directory = 1;
*--tail = '\0';
}
/* Special case for "/" must set need_directory, without removing
trailing slash */
else if (tail == path_copy + 1 && isslash (tail[-1]))
{
need_directory = 1;
}
path_end = tail;
/* Scan path_copy from right to left looking either for a symlink
or an actual existing file. If an existing file is found, just
return. If a symlink is found, exit the for loop.
Also: be careful to preserve the errno returned from
symlink.check as the caller may need it. */
/* FIXME: Do we have to worry about multiple \'s here? */
component = 0; // Number of translated components
sym.contents[0] = '\0';
sym.path_flags = 0;
int symlen = 0;
/* Make sure to check certain flags on last component only. */
for (unsigned pc_flags = opt & (PC_NO_ACCESS_CHECK | PC_KEEP_HANDLE
| PC_SYM_FOLLOW | PC_SYM_NOFOLLOW_REP);
;
pc_flags = opt & (PC_SYM_FOLLOW | PC_SYM_NOFOLLOW_REP))
{
const suffix_info *suff;
char *full_path;
/* Don't allow symlink.check to set anything in the path_conv
class if we're working on an inner component of the path */
if (component)
{
suff = NULL;
full_path = pathbuf;
}
else
{
suff = suffixes;
full_path = THIS_path;
}
retry_fs_via_processfd:
/* Convert to native path spec sans symbolic link info. */
error = mount_table->conv_to_win32_path (path_copy, full_path,
dev, &sym.mount_flags);
if (error)
return;
sym.pc_flags = pc_flags;
if (!dev.exists ())
{
error = ENXIO;
return;
}
if (iscygdrive_dev (dev))
{
if (!component)
fileattr = FILE_ATTRIBUTE_DIRECTORY
| FILE_ATTRIBUTE_READONLY;
else
{
fileattr = getfileattr (THIS_path,
sym.mount_flags & MOUNT_NOPOSIX);
dev = FH_FS;
}
goto out;
}
else if (isdev_dev (dev))
{
/* Make sure that the path handling goes on as with FH_FS. */
}
else if (isvirtual_dev (dev))
{
/* FIXME: Calling build_fhandler here is not the right way to
handle this. */
fhandler_virtual *fh = (fhandler_virtual *)
build_fh_dev (dev, path_copy);
virtual_ftype_t file_type;
if (!fh)
file_type = virt_none;
else
{
file_type = fh->exists ();
if (file_type == virt_symlink
|| file_type == virt_fdsymlink)
{
fh->fill_filebuf ();
symlen = sym.set (fh->get_filebuf ());
}
else if (file_type == virt_fsdir && dev == FH_PROCESSFD)
{
/* FIXME: This is YA bad hack to workaround that
we're checking for isvirtual_dev at this point.
This should only happen if the file is actually
a virtual file, and NOT already if the preceeding
path components constitute a virtual file.
Anyway, what we do here is this: If the descriptor
symlink points to a dir, and if there are trailing
path components, it's actually pointing somewhere
else. The format_process_fd function returns the
full path, resolved symlink plus trailing path
components, in its filebuf. This is a POSIX path
we know nothing about, so we have to convert it to
native again, calling conv_to_win32_path. Since
basically nothing happened yet, just copy it over
into full_path and jump back to the
conv_to_win32_path call. What a mess. */
stpcpy (path_copy, fh->get_filebuf ());
delete fh;
goto retry_fs_via_processfd;
}
else if (file_type == virt_none && dev == FH_PROCESSFD)
{
error = get_errno ();
if (error)
{
delete fh;
return;
}
}
delete fh;
}
switch (file_type)
{
case virt_directory:
case virt_rootdir:
if (component == 0)
fileattr = FILE_ATTRIBUTE_DIRECTORY;
break;
case virt_file:
if (component == 0)
fileattr = 0;
break;
case virt_fdsymlink:
/* Allow open/linkat to do the right thing. */
if (opt & PC_SYM_NOFOLLOW_PROCFD)
{
opt &= ~PC_SYM_FOLLOW;
sym.path_flags |= PATH_RESOLVE_PROCFD;
}
fallthrough;
case virt_symlink:
goto is_virtual_symlink;
case virt_pipe:
if (component == 0)
{
fileattr = 0;
dev.parse (FH_PIPE);
}
break;
case virt_socket:
if (component == 0)
{
fileattr = 0;
dev.parse (FH_SOCKET);
}
break;
case virt_fsdir:
case virt_fsfile:
/* Access to real file or directory via block device
entry in /proc/sys. Convert to real file and go with
the flow. */
dev.parse (FH_FS);
goto is_fs_via_procsys;
case virt_blk:
/* Block special device. Convert to a /dev/sd* like
block device unless the trailing slash has been
requested. In this case, the target is the root
directory of the filesystem on this block device.
So we convert this to a real file and attach the
backslash. */
if (component == 0)
{
fileattr = FILE_ATTRIBUTE_DEVICE;
if (!need_directory)
/* Use a /dev/sd* device number > /dev/sddx.
FIXME: Define a new major DEV_ice number. */
dev.parse (DEV_SD_HIGHPART_END, 9999);
else
{
dev.parse (FH_FS);
strcat (full_path, "\\");
fileattr |= FILE_ATTRIBUTE_DIRECTORY;
}
goto out;
}
break;
case virt_chr:
if (component == 0)
fileattr = FILE_ATTRIBUTE_DEVICE;
break;
default:
if (component == 0)
fileattr = INVALID_FILE_ATTRIBUTES;
goto virtual_component_retry;
}
if (component == 0 || dev != FH_NETDRIVE)
mount_flags |= MOUNT_RO;
goto out;
}
/* devn should not be a device. If it is, then stop parsing. */
else if (dev != FH_FS)
{
fileattr = 0;
mount_flags = sym.mount_flags;
if (component)
{
error = ENOTDIR;
return;
}
goto out; /* Found a device. Stop parsing. */
}
/* If path is only a drivename, Windows interprets it as the
current working directory on this drive instead of the root
dir which is what we want. So we need the trailing backslash
in this case. */
if (full_path[0] && full_path[1] == ':' && full_path[2] == '\0')
{
full_path[2] = '\\';
full_path[3] = '\0';
}
/* If the incoming path was given in DOS notation, always treat
it as caseinsensitive,noacl path. This must be set before
calling sym.check, otherwise the path is potentially treated
casesensitive. */
if (is_msdos)
sym.mount_flags |= MOUNT_NOPOSIX | MOUNT_NOACL;
is_fs_via_procsys:
symlen = sym.check (full_path, suff, fs, conv_handle);
is_virtual_symlink:
if (sym.isdevice)
{
if (component)
{
error = ENOTDIR;
return;
}
dev.parse (sym.major, sym.minor);
dev.setfs (1);
dev.mode (sym.mode);
fileattr = sym.fileattr;
goto out;
}
if (sym.path_flags & PATH_SOCKET)
{
if (component)
{
error = ENOTDIR;
return;
}
fileattr = sym.fileattr;
#ifdef __WITH_AF_UNIX
dev.parse ((sym.path_flags & PATH_REP) ? FH_UNIX : FH_LOCAL);
#else
dev.parse (FH_LOCAL);
#endif /* __WITH_AF_UNIX */
dev.setfs (1);
mount_flags = sym.mount_flags;
path_flags = sym.path_flags;
goto out;
}
if (!component)
{
/* Make sure that /dev always exists. */
fileattr = isdev_dev (dev) ? FILE_ATTRIBUTE_DIRECTORY
: sym.fileattr;
mount_flags = sym.mount_flags;
path_flags = sym.path_flags;
}
else if (isdev_dev (dev))
{
/* If we're looking for a non-existing file below /dev,
make sure that the device type is converted to FH_FS, so
that subsequent code handles the file correctly. Unless