apr-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From wr...@apache.org
Subject cvs commit: apr/file_io/win32 filepath.c
Date Tue, 10 Apr 2001 05:11:32 GMT
wrowe       01/04/09 22:11:32

  Modified:    .        CHANGES
               file_io/win32 filepath.c
  Log:
    Implement specific to win32, validate the actual root requested (to avoid
    walking off into "Device Not Ready" nonsense) and tear apart and put back
    together all [most?] of the nightmarish flavors of win32 file paths.
  
  Revision  Changes    Path
  1.90      +6 -0      apr/CHANGES
  
  Index: CHANGES
  ===================================================================
  RCS file: /home/cvs/apr/CHANGES,v
  retrieving revision 1.89
  retrieving revision 1.90
  diff -u -r1.89 -r1.90
  --- CHANGES	2001/04/09 16:40:18	1.89
  +++ CHANGES	2001/04/10 05:11:32	1.90
  @@ -1,5 +1,11 @@
   Changes with APR b1  
   
  +  *) Initial implementation of of apr_filepath (get/set/parse_root and 
  +     merge) for Windows.  [William Rowe]
  +
  +  *) Cleaned up implementation of of apr_filepath (get/set/parse_root
  +     and merge) for Unix.  [Greg Stein, William Rowe]
  +
     *) Fixup the --enable-libtool option.  This allows the test directory
        to compile again.  The test directory still doesn't work when
        APR is configured without libtool.  [Ryan Bloom]
  
  
  
  1.2       +462 -119  apr/file_io/win32/filepath.c
  
  Index: filepath.c
  ===================================================================
  RCS file: /home/cvs/apr/file_io/win32/filepath.c,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- filepath.c	2001/04/08 08:00:01	1.1
  +++ filepath.c	2001/04/10 05:11:32	1.2
  @@ -64,7 +64,7 @@
    * in the file system, except by a very obscure bug where any file
    * that is created with a trailing space or period, followed by the 
    * ':' stream designator on an NTFS volume can never be accessed again.
  - * In other words, don't ever accept them if designating a stream!
  + * In other words, don't ever accept them when designating a stream!
    *
    * An interesting side effect is that two or three periods are both 
    * treated as the parent directory, although the fourth and on are
  @@ -93,6 +93,30 @@
   }
   
   
  +static apr_status_t apr_filepath_root_test(char *path,
  +                                           apr_pool_t *p)
  +{
  +    apr_status_t rv;
  +#if APR_HAS_UNICODE_FS
  +    apr_oslevel_e os_level;
  +    if (!apr_get_oslevel(p, &os_level) && os_level >= APR_WIN_NT)
  +    {
  +        apr_wchar_t wpath[APR_PATH_MAX];
  +        if (rv = utf8_to_unicode_path(wpath, sizeof(wpath) 
  +                                           / sizeof(apr_wchar_t), path))
  +            return rv;
  +        rv = GetDriveTypeW(wpath);
  +    }
  +    else
  +#endif
  +        rv = GetDriveType(path);
  +
  +    if (rv == DRIVE_UNKNOWN || rv == DRIVE_NO_ROOT_DIR)
  +        return APR_EBADPATH;
  +    return APR_SUCCESS;
  +}
  +
  +
   APR_DECLARE(apr_status_t) apr_filepath_get(char **rootpath,
                                              apr_pool_t *p)
   {
  @@ -119,6 +143,47 @@
   }
   
   
  +static apr_status_t apr_filepath_drive_get(char **rootpath,
  +                                           char drive,
  +                                           apr_pool_t *p)
  +{
  +    char path[APR_PATH_MAX];
  +#if APR_HAS_UNICODE_FS
  +    apr_oslevel_e os_level;
  +    if (!apr_get_oslevel(p, &os_level) && os_level >= APR_WIN_NT)
  +    {
  +        apr_wchar_t *ignored;
  +        apr_wchar_t wdrive[8];
  +        apr_wchar_t wpath[APR_PATH_MAX];
  +        apr_status_t rv;
  +        /* ???: This needs review, apparently "\\?\d:." returns "\\?\d:" 
  +         * as if that is useful for anything.
  +         */
  +        wcscpy(wdrive, L"D:.");
  +        wdrive[0] = (apr_wchar_t)(unsigned char)drive;
  +        if (!GetFullPathNameW(wdrive, sizeof(wpath) / sizeof(apr_wchar_t), wpath, &ignored))
  +            return apr_get_os_error();
  +        if ((rv = unicode_to_utf8_path(path, sizeof(path), wpath)))
  +            return rv;
  +        *rootpath = apr_pstrdup(p, path);
  +    }
  +    else
  +#endif
  +    {
  +        char *ignored;
  +        char drivestr[4];
  +        drivestr[0] = drive;
  +        drivestr[1] = ':';
  +        drivestr[2] = '.';;
  +        drivestr[3] = '\0';
  +        if (!GetFullPathName(drivestr, sizeof(path), path, &ignored))
  +            return apr_get_os_error();
  +        *rootpath = apr_pstrdup(p, path);
  +    }
  +    return APR_SUCCESS;
  +}
  +
  +
   APR_DECLARE(apr_status_t) apr_filepath_set(const char *rootpath,
                                              apr_pool_t *p)
   {
  @@ -203,41 +268,73 @@
               } while (*delim1 && *delim1 != '/' && *delim1 != '\\');
   
               if (*delim1) {
  +                apr_status_t rv;
                   delim2 = delim1 + 1;
  -                do {
  +                while (*delim2 && *delim2 != '/' && *delim2 != '\\') {
                       /* Protect against //machine/X/ where X is illegal */
  -                    if (*delim2 && !is_fnchar(*(delim2++)))
  +                    if (!is_fnchar(*(delim2++)))
                           return APR_EBADPATH;
  -                } while (*delim2 && *delim2 != '/' && *delim2 != '\\');
  +                } 
   
  -                if (delim2) {
  -                    /* Have path of '//machine/share/[whatnot]' */
  +                if (!*delim2) {
  +                /* Have path of '//machine/[share]' so we must have
  +                 * an extra byte for the trailing slash
  +                 */
  +                    newpath = apr_pstrndup(p, testpath, delim2 - testpath + 1);
  +                    newpath[delim2 - testpath + 1] = '\0';
  +                }
  +                else
                       newpath = apr_pstrndup(p, testpath, delim2 - testpath);
  -                    newpath[0] = '\\';
  -                    newpath[1] = '\\';
  -                    newpath[delim1 - testpath] = '\\';
  -                    newpath[delim2 - testpath] = '\\';
  +
  +                newpath[0] = '\\';
  +                newpath[1] = '\\';
  +                newpath[delim1 - testpath] = '\\';
  +
  +                if (delim2 == delim1 + 1) {
  +                    /* We simply \\machine\, so give up already
  +                     */
                       *rootpath = newpath;
  +                    *inpath = delim2;
  +                    return APR_EINCOMPLETE;
  +                }
  +
  +                /* Validate the \\Machine\Share\ designation, must
  +                 * root this designation!
  +                 */
  +                newpath[delim2 - testpath] = '\\';
  +                rv = apr_filepath_root_test(newpath, p);
  +                if (rv)
  +                    return rv;
  +
  +                /* If this root included the trailing / or \ designation 
  +                 * then lop off multiple trailing slashes
  +                 */
  +                if (*delim2) {
                       *inpath = delim2 + 1;
                       while (**inpath == '/' || **inpath == '\\')
                           ++*inpath;
  -                    return APR_SUCCESS;
  +                    /* Give back the caller's own trailing delimiter
  +                     */
  +                    newpath[delim2 - testpath] = *delim2;
                   }
  -
  -                /* Have path of '//machine/[share]' */
  -                delim2 = strchr(delim1, '\0');
  -                newpath = apr_pstrndup(p, testpath, delim2 - testpath);
  -                newpath[0] = '\\';
  -                newpath[1] = '\\';
  -                newpath[delim1 - testpath] = '\\';
  +                else
  +                    *inpath = delim2;
  +                
                   *rootpath = newpath;
  -                *inpath = delim2;
  -                return APR_EINCOMPLETE;
  +                return APR_SUCCESS;
               }
               
  -            /* Have path of '//[machine]' */
  +            /* Have path of '\\[machine]', if the machine is given,
  +             * append the trailing \
  +             */
               delim1 = strchr(testpath, '\0');
  -            newpath = apr_pstrndup(p, testpath, delim1 - testpath);
  +            if (delim1 > testpath + 2) {
  +                newpath = apr_pstrndup(p, testpath, delim1 - testpath + 1);
  +                newpath[delim1 - testpath] = '\\';
  +                newpath[delim1 - testpath + 1] = '\0';
  +            }
  +            else
  +                newpath = apr_pstrndup(p, testpath, delim1 - testpath);
               newpath[0] = '\\';
               newpath[1] = '\\';
               *rootpath = newpath;
  @@ -245,24 +342,43 @@
               return APR_EINCOMPLETE;
           }
   
  -        /* Left with a path of '/', what drive are we asking about? */
  -        *rootpath = apr_pstrdup(p, "/");
  +        /* Left with a path of '/', what drive are we asking about? 
  +         * The guess is left to the caller.
  +         */
  +        *rootpath = apr_pstrndup(p, testpath, 1);
           *inpath = ++testpath;
           return APR_EINCOMPLETE;
       }
   
       /* Evaluate path of 'd:[/]' */
  -    if (is_fnchar(*testpath) && testpath[1] == ':') {
  +    if (is_fnchar(*testpath) && testpath[1] == ':') 
  +    {
  +        apr_status_t rv;
  +        /* Validate that d:\ drive exists, test must be rooted
  +         */
  +        newpath = apr_pstrndup(p, testpath, 3);
  +        newpath[2] = '\\';
  +        newpath[3] = '\0';
  +        rv = apr_filepath_root_test(newpath, p);
  +        if (rv)
  +            return rv;
  +
  +        /* Have full 'd:/' so replace our \ with the given root char
  +         */
           if (testpath[2] == '/' || testpath[2] == '\\') {
  -            *rootpath = apr_pstrndup(p, testpath, 3);
  +            newpath[2] = testpath[2];
  +            *rootpath = newpath;
               *inpath = testpath + 3;
               while (**inpath == '/' || **inpath == '\\')
                   ++*inpath;
               return APR_SUCCESS;
           }
   
  -        /* Left with path of 'd:' from the cwd of this drive letter */
  -        *rootpath = apr_pstrndup(p, testpath, 2);
  +        /* Left with path of 'd:' from the cwd of this drive letter 
  +         * so truncate the root \ we added above;
  +         */
  +        newpath[2] = '\0';
  +        *rootpath = newpath;
           *inpath = testpath + 2;
           return APR_EINCOMPLETE;        
       }
  @@ -273,112 +389,254 @@
   
   
   APR_DECLARE(apr_status_t) apr_filepath_merge(char **newpath, 
  -                                             const char *rootpath, 
  +                                             const char *basepath, 
                                                const char *addpath, 
                                                apr_int32_t flags,
                                                apr_pool_t *p)
   {
       char path[APR_PATH_MAX]; /* isn't null term */
  -    apr_size_t rootlen; /* is the length of the src rootpath */
  -    apr_size_t keptlen; /* is the length of the retained rootpath */
  -    apr_size_t pathlen; /* is the length of the result path */
  -    apr_size_t seglen;  /* is the end of the current segment */
  +    char *baseroot = NULL;
  +    char *addroot;
  +    apr_size_t rootlen; /* the length of the root portion of path, d:/ is 3 */
  +    apr_size_t baselen; /* the length of basepath (excluding baseroot) */
  +    apr_size_t keptlen; /* the length of the retained basepath (incl root) */
  +    apr_size_t pathlen; /* the length of the result path */
  +    apr_size_t segend;  /* the end of the current segment */
  +    apr_size_t seglen;  /* the length of the segment (excl trailing chars) */
  +    apr_status_t basetype; /* from parsing the basepath's baseroot */
  +    apr_status_t addtype;  /* from parsing the addpath's addroot */
       apr_status_t rv;
  -
  -    /* Treat null as an empty path.
  +    int fixunc = 0;  /* flag to complete an incomplete UNC basepath */
  +    
  +    /* Treat null as an empty path, otherwise split addroot from the addpath
        */
  -    if (!addpath)
  -        addpath = "";
  +    if (!addpath) {
  +        addpath = addroot = "";
  +        addtype = APR_ERELATIVE;
  +    }
  +    else {
  +        addtype = apr_filepath_root(&addroot, &addpath, p);
  +        if (addtype == APR_SUCCESS) {
  +            addtype = APR_EABSOLUTE;
  +        }
  +        else if (addtype == APR_ERELATIVE) {
  +            addroot = "";
  +        }
  +        else if (addtype != APR_EINCOMPLETE) {
  +            /* apr_filepath_root was incomprehensible so fail already
  +             */
  +            return addtype;
  +        }
  +    }
   
  -    if (addpath[0] == '/') 
  +    /* If addpath is (even partially) rooted, then basepath is
  +     * unused.  Ths violates any APR_FILEPATH_SECUREROOTTEST 
  +     * and APR_FILEPATH_NOTABSOLUTE flags specified.
  +     */
  +    if (addtype == APR_EABSOLUTE || addtype == APR_EINCOMPLETE)
       {
  -        /* If addpath is rooted, then rootpath is unused.
  -         * Ths violates any APR_FILEPATH_SECUREROOTTEST and
  -         * APR_FILEPATH_NOTABSOLUTE flags specified.
  -         */
           if (flags & APR_FILEPATH_SECUREROOTTEST)
               return APR_EABOVEROOT;
           if (flags & APR_FILEPATH_NOTABSOLUTE)
  -            return APR_EABSOLUTE;
  +            return addtype;
  +    }
  +
  +    /* Optimized tests before we query the current working path
  +     */
  +    if (!basepath) {
   
           /* If APR_FILEPATH_NOTABOVEROOT wasn't specified,
            * we won't test the root again, it's ignored.
            * Waste no CPU retrieving the working path.
            */
  -        if (!rootpath && !(flags & APR_FILEPATH_NOTABOVEROOT))
  -            rootpath = "";
  -    }
  -    else 
  -    {
  +        if (addtype == APR_EABSOLUTE && !(flags & APR_FILEPATH_NOTABOVEROOT))
{
  +            basepath = baseroot = "";
  +            basetype = APR_ERELATIVE;
  +        }
  +
           /* If APR_FILEPATH_NOTABSOLUTE is specified, the caller 
  -         * requires a relative result.  If the rootpath is
  -         * ommitted, we do not retrieve the working path,
  -         * if rootpath was supplied as absolute then fail.
  +         * requires an absolutely relative result, So do not retrieve 
  +         * the working path.
            */
  -        if (flags & APR_FILEPATH_NOTABSOLUTE) 
  -        {
  -            if (!rootpath)
  -                rootpath = "";
  -            else if (rootpath[0] == '/')
  -                return APR_EABSOLUTE;
  +        if (addtype == APR_ERELATIVE && (flags & APR_FILEPATH_NOTABSOLUTE))
{
  +            basepath = baseroot = "";
  +            basetype = APR_ERELATIVE;
           }
       }
   
  -    if (!rootpath) 
  +    if (!basepath) 
       {
           /* Start with the current working path.  This is bass akwards,
            * but required since the compiler (at least vc) doesn't like
            * passing the address of a char const* for a char** arg.
  +         * We must grab the current path of the designated drive 
  +         * if addroot is given in drive-relative form (e.g. d:foo)
            */
           char *getpath;
  -        rv = apr_filepath_get(&getpath, p);
  -        rootpath = getpath;
  +        if (addtype == APR_EINCOMPLETE && addroot[1] == ':')
  +            rv = apr_filepath_drive_get(&getpath, addroot[0], p);
  +        else
  +            rv = apr_filepath_get(&getpath, p);
           if (rv != APR_SUCCESS)
  -            return errno;
  -    
  -        /* XXX: Any kernel subject to goofy, uncanonical results
  -         * must run the rootpath against the user's given flags.
  -         * Simplest would be a recursive call to apr_filepath_merge
  -         * with an empty (not null) rootpath and addpath of the cwd.
  -         */
  +            return rv;
  +        basepath = getpath;
       }
   
  -    rootlen = strlen(rootpath);
  +    if (!baseroot) {
  +        basetype = apr_filepath_root(&baseroot, &basepath, p);
  +        if (basetype == APR_SUCCESS) {
  +            basetype = APR_EABSOLUTE;
  +        }
  +        else if (basetype == APR_ERELATIVE) {
  +            baseroot = "";
  +        }
  +        else if (basetype != APR_EINCOMPLETE) {
  +            /* apr_filepath_root was incomprehensible so fail already
  +             */
  +            return basetype;
  +        }
  +    }
  +    baselen = strlen(basepath);
   
  -    if (addpath[0] == '/') 
  +    /* If APR_FILEPATH_NOTABSOLUTE is specified, the caller 
  +     * requires an absolutely relative result.  If the given 
  +     * basepath is not relative then fail.
  +     */
  +    if ((flags & APR_FILEPATH_NOTABSOLUTE) && basetype != APR_ERELATIVE)
  +        return basetype;
  +
  +    /* The Win32 nightmare on unc street... start combining for
  +     * many possible root combinations.
  +     */
  +    if (addtype == APR_EABSOLUTE)
       {
  -        /* Ignore the given root path, strip off leading 
  -         * '/'s to a single leading '/' from the addpath,
  -         * and leave addpath at the first non-'/' character.
  +        /* Ignore the given root path, and start with the addroot
            */
  +        if ((flags & APR_FILEPATH_NOTABOVEROOT) 
  +                && strncmp(baseroot, addroot, strlen(baseroot)))
  +            return APR_EABOVEROOT;
           keptlen = 0;
  -        while (addpath[0] == '/')
  -            ++addpath;
  -        path[0] = '/';
  -        pathlen = 1;
  +        rootlen = pathlen = strlen(addroot);
  +        memcpy(path, addroot, pathlen);
       }
  -    else
  +    else if (addtype == APR_EINCOMPLETE)
       {
  +        /* There are several types of incomplete paths, 
  +         *     incomplete UNC paths         (//foo/ or //),
  +         *     drives without rooted paths  (d: as in d:foo), 
  +         * and simple roots                 (/ as in /foo).
  +         * Deal with these in significantly different manners...
  +         */
  +        if ((addroot[0] == '/' || addroot[0] == '\\') &&
  +            (addroot[1] == '/' || addroot[1] == '\\')) 
  +        {
  +            /* Ignore the given root path if the incomplete addpath is UNC,
  +             * (note that the final result will be incomplete).
  +             */
  +            if (flags & APR_FILEPATH_NOTRELATIVE)
  +                return addtype;
  +            if ((flags & APR_FILEPATH_NOTABOVEROOT) 
  +                    && strncmp(baseroot, addroot, strlen(baseroot)))
  +                return APR_EABOVEROOT;
  +            fixunc = 1;
  +            keptlen = 0;
  +            rootlen = pathlen = strlen(addroot);
  +            memcpy(path, addroot, pathlen);
  +        }
  +        else if ((addroot[0] == '/' || addroot[0] == '\\') && !addroot[1]) 
  +        {
  +            /* Bring together the drive or UNC root from the baseroot
  +             * if the addpath is a simple root and basepath is rooted,
  +             * otherwise disregard the basepath entirely.
  +             */
  +            if (basetype != APR_EABSOLUTE && (flags & APR_FILEPATH_NOTRELATIVE))
  +                return basetype;
  +            if (basetype != APR_ERELATIVE) {
  +                if (basetype == APR_INCOMPLETE 
  +                        && (baseroot[0] == '/' || baseroot[0] == '\\')
  +                        && (baseroot[1] == '/' || baseroot[1] == '\\'))
  +                    fixunc = 1;
  +                keptlen = rootlen = pathlen = strlen(baseroot);
  +                memcpy(path, baseroot, pathlen);
  +            }
  +            else {
  +                if (flags & APR_FILEPATH_NOTABOVEROOT)
  +                    return APR_EABOVEROOT;
  +                keptlen = 0;
  +                rootlen = pathlen = strlen(addroot);
  +                memcpy(path, addroot, pathlen);
  +            }
  +        }
  +        else if (addroot[0] && addroot[1] == ':' && !addroot[2]) 
  +        {
  +            /* If the addroot is a drive (without a volume root)
  +             * use the basepath _if_ it matches this drive letter!
  +             * Otherwise we must discard the basepath.
  +             */
  +            if (addroot[0] == baseroot[0] && baseroot[1] == ':') {
  +                /* Base the result path on the basepath
  +                 */
  +                if (basetype != APR_EABSOLUTE && (flags & APR_FILEPATH_NOTRELATIVE))
  +                    return basetype;
  +                rootlen = strlen(baseroot);
  +                pathlen = baselen;
  +                if (rootlen + pathlen >= sizeof(path))
  +                    return APR_ENAMETOOLONG;
  +                memcpy(path, baseroot, rootlen);
  +                memcpy(path + rootlen, basepath, pathlen);
  +                pathlen += rootlen;
  +            } 
  +            else {
  +                if (flags & APR_FILEPATH_NOTRELATIVE)
  +                    return addtype;
  +                if (flags & APR_FILEPATH_NOTABOVEROOT)
  +                    return APR_EABOVEROOT;
  +                keptlen = 0;
  +                rootlen = pathlen = strlen(addroot);
  +                memcpy(path, addroot, pathlen);
  +            }
  +        }
  +        else {
  +            /* Now this is unexpected, we aren't aware of any other
  +             * incomplete path forms!  Fail now.
  +             */
  +            return APR_EBADPATH;
  +        }
  +    }
  +    else { /* addtype == APR_ERELATIVE */
           /* If both paths are relative, fail early
  +         */
  +        if (basetype != APR_EABSOLUTE && (flags & APR_FILEPATH_NOTRELATIVE))
  +            return basetype;
  +
  +        /* An incomplete UNC path must be completed
            */
  -        if (rootpath[0] != '/' && (flags & APR_FILEPATH_NOTRELATIVE))
  -                return APR_ERELATIVE;
  +        if (basetype == APR_INCOMPLETE 
  +                && (baseroot[0] == '/' || baseroot[0] == '\\')
  +                && (baseroot[1] == '/' || baseroot[1] == '\\'))
  +            fixunc = 1;
   
  -        /* Base the result path on the rootpath
  +        /* Base the result path on the basepath
            */
  -        keptlen = rootlen;
  -        if (rootlen >= sizeof(path))
  +        rootlen = strlen(baseroot);
  +        pathlen = baselen;
  +        if (rootlen + pathlen >= sizeof(path))
               return APR_ENAMETOOLONG;
  -        memcpy(path, rootpath, rootlen);
  +        memcpy(path, baseroot, rootlen);
  +        memcpy(path + rootlen, basepath, pathlen);
  +        pathlen += rootlen;
  +    }
  +
  +    /* '/' terminate the given root path unless it's already terminated
  +     * or is an incomplete drive root.
  +     */
  +    if (pathlen && path[pathlen - 1] != '/' && path[pathlen - 1] != '\\'

  +                && path[pathlen - 1] != ':') {
  +        if (pathlen + 1 >= sizeof(path))
  +            return APR_ENAMETOOLONG;
           
  -        /* Always '/' terminate the given root path
  -         */
  -        if (keptlen && path[keptlen - 1] != '/') {
  -            if (keptlen + 1 >= sizeof(path))
  -                return APR_ENAMETOOLONG;
  -            path[keptlen++] = '/';
  -        }
  -        pathlen = keptlen;
  +        path[pathlen++] = '/';
  +
       }
   
       while (*addpath) 
  @@ -386,18 +644,51 @@
           /* Parse each segment, find the closing '/' 
            */
           seglen = 0;
  -        while (addpath[seglen] && addpath[seglen] != '/')
  +        while (addpath[seglen] && addpath[seglen] != '/'
  +                               && addpath[seglen] != '\\')
               ++seglen;
   
  +        /* Truncate all trailing spaces and all but the first two dots */
  +        segend = seglen;
  +        while (seglen && (addpath[seglen - 1] == ' ' 
  +                       || addpath[seglen - 1] == '.')) {
  +            if (seglen > 2 || addpath[seglen - 1] != '.' || addpath[0] != '.')
  +                --seglen;
  +            else
  +                break;
  +        }
  +
           if (seglen == 0 || (seglen == 1 && addpath[0] == '.')) 
           {
  -            /* noop segment (/ or ./) so skip it 
  +            /* NOTE: win32 _hates_ '/ /' and '/. /' (yes, with spaces in there)
  +             * so eliminate all preconceptions that it is valid.
  +             */
  +            if (seglen < segend)
  +                return APR_EBADPATH;
  +
  +            /* This isn't legal unless the unc path is completed
  +             */
  +            if (fixunc)
  +                return APR_EBADPATH;
  +
  +            /* Otherwise, this is a noop segment (/ or ./) so ignore it 
                */
           }
           else if (seglen == 2 && addpath[0] == '.' && addpath[1] == '.')

           {
  +            /* NOTE: win32 _hates_ '/.. /' (yes, with a space in there) and
  +             * '/..../' so eliminate all preconceptions that they are valid.
  +             */
  +            if (seglen < segend && (seglen != 3 || addpath[2] != '.'))
  +                return APR_EBADPATH;
  +
  +            /* This isn't legal unless the unc path is completed
  +             */
  +            if (fixunc)
  +                return APR_EBADPATH;
  +
               /* backpath (../) */
  -            if (pathlen == 1 && path[0] == '/') 
  +            if (pathlen <= rootlen) 
               {
                   /* Attempt to move above root.  Always die if the 
                    * APR_FILEPATH_SECUREROOTTEST flag is specified.
  @@ -406,13 +697,15 @@
                       return APR_EABOVEROOT;
                   
                   /* Otherwise this is simply a noop, above root is root.
  -                 * Flag that rootpath was entirely replaced.
                    */
  -                keptlen = 0;
               }
  -            else if (pathlen == 0 
  -                  || (pathlen == 3 && !memcmp(path + pathlen - 3, "../", 3))
  -                  || (pathlen  > 3 && !memcmp(path + pathlen - 4, "/../", 4)))
  +            else if (pathlen == 0 ||
  +                     (pathlen >= 3 && (pathlen == 3
  +                                    || path[pathlen - 4] == ':')
  +                                   &&  path[pathlen - 3] == '.' 
  +                                   &&  path[pathlen - 2] == '.' 
  +                                   && (path[pathlen - 1] == '/' 
  +                                    || path[pathlen - 1] == '\\')))
               {
                   /* Path is already backpathed or empty, if the
                    * APR_FILEPATH_SECUREROOTTEST.was given die now.
  @@ -433,7 +726,8 @@
                    */
                   do {
                       --pathlen;
  -                } while (pathlen && path[pathlen - 1] != '/');
  +                } while (pathlen && path[pathlen - 1] != '/'
  +                                 && path[pathlen - 1] != '\\');
               }
   
               /* Now test if we are above where we started and back up
  @@ -446,42 +740,91 @@
                   keptlen = pathlen;
               }
           }
  -        else 
  +        else /* not empty or dots */
           {
  -            /* An actual segment, append it to the destination path
  -             */
  -            apr_size_t i = (addpath[seglen] != '\0');
  -            if (pathlen + seglen + i >= sizeof(path))
  -                return APR_ENAMETOOLONG;
  -            memcpy(path + pathlen, addpath, seglen + i);
  -            pathlen += seglen + i;
  +            if (fixunc) {
  +                char *testpath = path;
  +                char *testroot;
  +                apr_status_t testtype;
  +                apr_size_t i = (addpath[segend] != '\0');
  +                
  +
  +                /* This isn't legal unless the unc path is complete!
  +                 */
  +                if (seglen < segend)
  +                    return APR_EBADPATH;
  +                if (pathlen + seglen + 1 >= sizeof(path))
  +                    return APR_ENAMETOOLONG;
  +                memcpy(path + pathlen, addpath, seglen + i);
  +                
  +                /* Always add the trailing slash to a UNC segment
  +                 */
  +                if (i)
  +                    path[pathlen + seglen] = addpath[segend];
  +                else
  +                    path[pathlen + seglen] = '\\';
  +                pathlen += seglen + 1;
  +
  +                /* Recanonicalize the UNC root with the new UNC segment,
  +                 * and if we succeed, reset this test and the rootlen,
  +                 * and replace our path with the canonical UNC root path
  +                 */
  +                path[pathlen] = '\0';
  +                testtype = apr_filepath_root(&testroot, &testpath, p);
  +                if (testtype == APR_SUCCESS) {
  +                    rootlen = pathlen = (testpath - path);
  +                    memcpy(path, testroot, pathlen);
  +                    fixunc = 0;
  +                }
  +                else if (testtype != APR_EINCOMPLETE) {
  +                    /* apr_filepath_root was very unexpected so fail already
  +                     */
  +                    return testtype;
  +                }
  +            }
  +            else {
  +                /* An actual segment, append it to the destination path
  +                 */
  +                apr_size_t i = (addpath[segend] != '\0');
  +                if (pathlen + seglen + i >= sizeof(path))
  +                    return APR_ENAMETOOLONG;
  +                memcpy(path + pathlen, addpath, seglen + i);
  +                if (i)
  +                    path[pathlen + seglen] = addpath[segend];
  +                pathlen += seglen + i;
  +            }
           }
   
           /* Skip over trailing slash to the next segment
            */
  -        if (addpath[seglen])
  -            ++seglen;
  +        if (addpath[segend])
  +            ++segend;
   
  -        addpath += seglen;
  +        addpath += segend;
       }
       path[pathlen] = '\0';
       
  -    /* keptlen will be the rootlen unless the addpath contained
  +    /* keptlen will be the baselen unless the addpath contained
        * backpath elements.  If so, and APR_FILEPATH_NOTABOVEROOT
        * is specified (APR_FILEPATH_SECUREROOTTEST was caught above),
  -     * compare the original root to assure the result path is
  -     * still within given root path.
  +     * compare the string beyond the root to assure the result path 
  +     * is still within given basepath.  Note that the root path 
  +     * segment is thoroughly tested prior to path parsing.
        */
  -    if ((flags & APR_FILEPATH_NOTABOVEROOT) && keptlen < rootlen) {
  -        if (strncmp(rootpath, path, rootlen))
  +    if (flags & APR_FILEPATH_NOTABOVEROOT && (keptlen - rootlen) < baselen)
{
  +        if (strncmp(basepath, path + rootlen, baselen))
               return APR_EABOVEROOT;
  -        if (rootpath[rootlen - 1] != '/'
  -                && path[rootlen] && path[rootlen] != '/')
  +
  +        /* Ahem... if we weren't given a trailing slash on the basepath,
  +         * we better be sure that /foo wasn't replaced with /foobar!
  +         */
  +        if (basepath[baselen - 1] != '/' && basepath[baselen - 1] != '\\'
  +             && path[baselen] && path[baselen] != '/' && path[baselen]
!= '\\')
               return APR_EABOVEROOT;
       }
   
   #if 0
  -    /* Just an idea - don't know where it's headed yet */
  +    /* Just an idea - still don't know where it's headed */
       if (addpath && addpath[endseg - 1] != '/' 
                   && (flags & APR_FILEPATH_TRUECASE)) {
           apr_finfo_t finfo;
  
  
  

Mime
View raw message