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.

Reply via email to