Change 15880 by jhi@alpha on 2002/04/12 19:57:05 Integrate change #15879 from maint-5.6; Win32::GetLongPathName() did not return valid results if there were "." and ".." components in the path; also fix a potential buffer overflow if the long path happens to be longer than MAX_PATH (this can presumably happen if they use \\?\... style paths); add a rather limited testsuite that exercises just the edge cases
Affected files ... .... //depot/perl/MANIFEST#833 integrate .... //depot/perl/t/harness#28 integrate .... //depot/perl/t/win32/longpath.t#1 branch .... //depot/perl/win32/win32.c#193 integrate Differences ... ==== //depot/perl/MANIFEST#833 (text) ==== Index: perl/MANIFEST --- perl/MANIFEST.~1~ Fri Apr 12 14:00:05 2002 +++ perl/MANIFEST Fri Apr 12 14:00:05 2002 @@ -2552,6 +2552,7 @@ t/uni/sprintf.t See if Unicode sprintf works t/uni/title.t See if Unicode casing works t/uni/upper.t See if Unicode casing works +t/win32/longpath.t Test if Win32::GetLongPathName() works t/x2p/s2p.t See if s2p/psed work taint.c Tainting code thrdvar.h Per-thread variables ==== //depot/perl/t/harness#28 (text) ==== Index: perl/t/harness --- perl/t/harness.~1~ Fri Apr 12 14:00:05 2002 +++ perl/t/harness Fri Apr 12 14:00:05 2002 @@ -57,6 +57,7 @@ push @tests, <op/*.t>; push @tests, <uni/*.t>; push @tests, <lib/*.t>; + push @tests, <win32/*.t> if $^O eq 'MSWin32'; use File::Spec; my $updir = File::Spec->updir; my $mani = File::Spec->catfile(File::Spec->updir, "MANIFEST"); ==== //depot/perl/win32/win32.c#193 (text) ==== Index: perl/win32/win32.c --- perl/win32/win32.c.~1~ Fri Apr 12 14:00:05 2002 +++ perl/win32/win32.c Fri Apr 12 14:00:05 2002 @@ -1284,6 +1284,18 @@ return res; } +#define isSLASH(c) ((c) == '/' || (c) == '\\') +#define SKIP_SLASHES(s) \ + STMT_START { \ + while (*(s) && isSLASH(*(s))) \ + ++(s); \ + } STMT_END +#define COPY_NONSLASHES(d,s) \ + STMT_START { \ + while (*(s) && !isSLASH(*(s))) \ + *(d)++ = *(s)++; \ + } STMT_END + /* Find the longname of a given path. path is destructively modified. * It should have space for at least MAX_PATH characters. */ DllExport char * @@ -1299,61 +1311,74 @@ return Nullch; /* drive prefix */ - if (isALPHA(path[0]) && path[1] == ':' && - (path[2] == '/' || path[2] == '\\')) - { + if (isALPHA(path[0]) && path[1] == ':') { start = path + 2; *tmpstart++ = path[0]; *tmpstart++ = ':'; } /* UNC prefix */ - else if ((path[0] == '/' || path[0] == '\\') && - (path[1] == '/' || path[1] == '\\')) - { + else if (isSLASH(path[0]) && isSLASH(path[1])) { start = path + 2; *tmpstart++ = path[0]; *tmpstart++ = path[1]; - /* copy machine name */ - while (*start && *start != '/' && *start != '\\') + SKIP_SLASHES(start); + COPY_NONSLASHES(tmpstart,start); /* copy machine name */ + if (*start) { *tmpstart++ = *start++; - if (*start) { - *tmpstart++ = *start; - start++; - /* copy share name */ - while (*start && *start != '/' && *start != '\\') - *tmpstart++ = *start++; + SKIP_SLASHES(start); + COPY_NONSLASHES(tmpstart,start); /* copy share name */ } } - sep = *start++; - if (sep == '/' || sep == '\\') - *tmpstart++ = sep; *tmpstart = '\0'; - while (sep) { - /* walk up to slash */ - while (*start && *start != '/' && *start != '\\') - ++start; + while (*start) { + /* copy initial slash, if any */ + if (isSLASH(*start)) { + *tmpstart++ = *start++; + *tmpstart = '\0'; + SKIP_SLASHES(start); + } + + /* FindFirstFile() expands "." and "..", so we need to pass + * those through unmolested */ + if (*start == '.' + && (!start[1] || isSLASH(start[1]) + || (start[1] == '.' && (!start[2] || isSLASH(start[2]))))) + { + COPY_NONSLASHES(tmpstart,start); /* copy "." or ".." */ + *tmpstart = '\0'; + continue; + } + + /* if this is the end, bust outta here */ + if (!*start) + break; - /* discard doubled slashes */ - while (*start && (start[1] == '/' || start[1] == '\\')) + /* now we're at a non-slash; walk up to next slash */ + while (*start && !isSLASH(*start)) ++start; - sep = *start; /* stop and find full name of component */ + sep = *start; *start = '\0'; fhand = FindFirstFile(path,&fdata); + *start = sep; if (fhand != INVALID_HANDLE_VALUE) { - strcpy(tmpstart, fdata.cFileName); - tmpstart += strlen(fdata.cFileName); - if (sep) - *tmpstart++ = sep; - *tmpstart = '\0'; - *start++ = sep; - FindClose(fhand); + STRLEN len = strlen(fdata.cFileName); + if ((STRLEN)(tmpbuf + sizeof(tmpbuf) - tmpstart) > len) { + strcpy(tmpstart, fdata.cFileName); + tmpstart += len; + FindClose(fhand); + } + else { + FindClose(fhand); + errno = ERANGE; + return Nullch; + } } else { /* failed a step, just return without side effects */ /*PerlIO_printf(Perl_debug_log, "Failed to find %s\n", path);*/ - *start = sep; + errno = EINVAL; return Nullch; } } ==== //depot/perl/t/win32/longpath.t#1 (xtext) ==== Index: perl/t/win32/longpath.t --- perl/t/win32/longpath.t.~1~ Fri Apr 12 14:00:05 2002 +++ perl/t/win32/longpath.t Fri Apr 12 14:00:05 2002 @@ -0,0 +1,52 @@ +#!perl -w + +# tests for Win32::GetLongPathName() + +$^O =~ /^MSWin/ or print("1..0 # not win32\n" ), exit; + +my @paths = qw( + / + // + . + .. + c: + c:/ + c:./ + c:/. + c:/.. + c:./.. + //./ + //. + //.. + //./.. +); +push @paths, map { my $x = $_; $x =~ s,/,\\,g; $x } @paths; +push @paths, qw( + ../\ + c:.\\../\ + c:/\..// + c://.\/./\ + \\.\\../\ + //\..// + //.\/./\ +); + +my $drive = $ENV{SystemDrive}; +if ($drive) { + for (@paths) { + s/^c:/$drive/; + } + push @paths, $ENV{SystemRoot} if $ENV{SystemRoot}; +} +my %expect; +@expect{@paths} = map { my $x = $_; $x =~ s,(.[/\\])[/\\]+,$1,g; $x } @paths; + +print "1.." . @paths . "\n"; +my $i = 1; +for (@paths) { + my $got = Win32::GetLongPathName($_); + print "# '$_' => expect '$expect{$_}' => got '$got'\n"; + print "not " unless $expect{$_} eq $got; + print "ok $i\n"; + ++$i; +} End of Patch.