Path: blob/master/src/java.base/windows/native/libjava/canonicalize_md.c
41119 views
/*1* Copyright (c) 1998, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425/*26* Pathname canonicalization for Win32 file systems27*/2829#include <stdio.h>30#include <stdlib.h>31#include <string.h>32#include <ctype.h>33#include <assert.h>34#include <sys/stat.h>3536#include <windows.h>37#include <winbase.h>38#include <errno.h>3940/* We should also include jdk_util.h here, for the prototype of JDK_Canonicalize.41This isn't possible though because canonicalize_md.c is as well used in42different contexts within Oracle.43*/44#include "io_util_md.h"4546/* Copy bytes to dst, not going past dend; return dst + number of bytes copied,47or NULL if dend would have been exceeded. If first != '\0', copy that byte48before copying bytes from src to send - 1. */49static WCHAR*50wcp(WCHAR *dst, WCHAR *dend, WCHAR first, WCHAR *src, WCHAR *send)51{52WCHAR *p = src, *q = dst;53if (first != L'\0') {54if (q < dend) {55*q++ = first;56} else {57errno = ENAMETOOLONG;58return NULL;59}60}61if (send - p > dend - q) {62errno = ENAMETOOLONG;63return NULL;64}65while (p < send)66*q++ = *p++;67return q;68}6970/* Find first instance of '\\' at or following start. Return the address of71that byte or the address of the null terminator if '\\' is not found. */72static WCHAR *73wnextsep(WCHAR *start)74{75WCHAR *p = start;76int c;77while ((c = *p) && (c != L'\\'))78p++;79return p;80}8182/* Tell whether the given string contains any wildcard characters */83static int84wwild(WCHAR *start)85{86WCHAR *p = start;87int c;88while (c = *p) {89if ((c == L'*') || (c == L'?'))90return 1;91p++;92}93return 0;94}9596/* Tell whether the given string contains prohibited combinations of dots.97In the canonicalized form no path element may have dots at its end.98Allowed canonical paths: c:\xa...dksd\..ksa\.lk c:\...a\.b\cd..x.x99Prohibited canonical paths: c:\..\x c:\x.\d c:\...100*/101static int102wdots(WCHAR *start)103{104WCHAR *p = start;105// Skip "\\.\" prefix106if (wcslen(p) > 4 && !wcsncmp(p, L"\\\\.\\", 4))107p = p + 4;108109while (*p) {110if ((p = wcschr(p, L'.')) == NULL) // find next occurrence of '.'111return 0; // no more dots112p++; // next char113while ((*p) == L'.') // go to the end of dots114p++;115if (*p && (*p != L'\\')) // path element does not end with a dot116p++; // go to the next char117else118return 1; // path element does end with a dot - prohibited119}120return 0; // no prohibited combinations of dots found121}122123/* If the lookup of a particular prefix fails because the file does not exist,124because it is of the wrong type, because access is denied, or because the125network is unreachable then canonicalization does not fail, it terminates126successfully after copying the rest of the original path to the result path.127Other I/O errors cause an error return.128*/129int130lastErrorReportable()131{132DWORD errval = GetLastError();133if ((errval == ERROR_FILE_NOT_FOUND)134|| (errval == ERROR_DIRECTORY)135|| (errval == ERROR_PATH_NOT_FOUND)136|| (errval == ERROR_BAD_NETPATH)137|| (errval == ERROR_BAD_NET_NAME)138|| (errval == ERROR_ACCESS_DENIED)139|| (errval == ERROR_NETWORK_UNREACHABLE)140|| (errval == ERROR_NETWORK_ACCESS_DENIED)) {141return 0;142}143return 1;144}145146/* Convert a pathname to canonical form. The input orig_path is assumed to147have been converted to native form already, via JVM_NativePath(). This is148necessary because _fullpath() rejects duplicate separator characters on149Win95, though it accepts them on NT. */150int151wcanonicalize(WCHAR *orig_path, WCHAR *result, int size)152{153WIN32_FIND_DATAW fd;154HANDLE h;155WCHAR *path; /* Working copy of path */156WCHAR *src, *dst, *dend, c;157158/* Reject paths that contain wildcards */159if (wwild(orig_path)) {160errno = EINVAL;161return -1;162}163164if ((path = (WCHAR*)malloc(size * sizeof(WCHAR))) == NULL)165return -1;166167/* Collapse instances of "foo\.." and ensure absoluteness. Note that168contrary to the documentation, the _fullpath procedure does not require169the drive to be available. */170if(!_wfullpath(path, orig_path, size)) {171goto err;172}173174if (wdots(path)) /* Check for prohibited combinations of dots */175goto err;176177src = path; /* Start scanning here */178dst = result; /* Place results here */179dend = dst + size; /* Don't go to or past here */180181/* Copy prefix, assuming path is absolute */182c = src[0];183if (((c <= L'z' && c >= L'a') || (c <= L'Z' && c >= L'A'))184&& (src[1] == L':') && (src[2] == L'\\')) {185/* Drive specifier */186*src = towupper(*src); /* Canonicalize drive letter */187if (!(dst = wcp(dst, dend, L'\0', src, src + 2))) {188goto err;189}190191src += 2;192} else if ((src[0] == L'\\') && (src[1] == L'\\')) {193/* UNC pathname */194WCHAR *p;195p = wnextsep(src + 2); /* Skip past host name */196if (!*p) {197/* A UNC pathname must begin with "\\\\host\\share",198so reject this path as invalid if there is no share name */199errno = EINVAL;200goto err;201}202p = wnextsep(p + 1); /* Skip past share name */203if (!(dst = wcp(dst, dend, L'\0', src, p)))204goto err;205src = p;206} else {207/* Invalid path */208errno = EINVAL;209goto err;210}211/* At this point we have copied either a drive specifier ("z:") or a UNC212prefix ("\\\\host\\share") to the result buffer, and src points to the213first byte of the remainder of the path. We now scan through the rest214of the path, looking up each prefix in order to find the true name of215the last element of each prefix, thereby computing the full true name of216the original path. */217while (*src) {218WCHAR *p = wnextsep(src + 1); /* Find next separator */219WCHAR c = *p;220WCHAR *pathbuf;221int pathlen;222223assert(*src == L'\\'); /* Invariant */224*p = L'\0'; /* Temporarily clear separator */225226if ((pathlen = (int)wcslen(path)) > MAX_PATH - 1) {227pathbuf = getPrefixed(path, pathlen);228h = FindFirstFileW(pathbuf, &fd); /* Look up prefix */229free(pathbuf);230} else231h = FindFirstFileW(path, &fd); /* Look up prefix */232233*p = c; /* Restore separator */234if (h != INVALID_HANDLE_VALUE) {235/* Lookup succeeded; append true name to result and continue */236FindClose(h);237if (!(dst = wcp(dst, dend, L'\\', fd.cFileName,238fd.cFileName + wcslen(fd.cFileName)))){239goto err;240}241src = p;242continue;243} else {244if (!lastErrorReportable()) {245if (!(dst = wcp(dst, dend, L'\0', src, src + wcslen(src)))){246goto err;247}248break;249} else {250goto err;251}252}253}254255if (dst >= dend) {256errno = ENAMETOOLONG;257goto err;258}259*dst = L'\0';260free(path);261return 0;262263err:264free(path);265return -1;266}267268/* Convert a pathname to canonical form. The input prefix is assumed269to be in canonical form already, and the trailing filename must not270contain any wildcard, dot/double dot, or other "tricky" characters271that are rejected by the canonicalize() routine above. This272routine is present to allow the canonicalization prefix cache to be273used while still returning canonical names with the correct274capitalization. */275int276wcanonicalizeWithPrefix(WCHAR *canonicalPrefix, WCHAR *pathWithCanonicalPrefix, WCHAR *result, int size)277{278WIN32_FIND_DATAW fd;279HANDLE h;280WCHAR *src, *dst, *dend;281WCHAR *pathbuf;282int pathlen;283284src = pathWithCanonicalPrefix;285dst = result; /* Place results here */286dend = dst + size; /* Don't go to or past here */287288289if ((pathlen=(int)wcslen(pathWithCanonicalPrefix)) > MAX_PATH - 1) {290pathbuf = getPrefixed(pathWithCanonicalPrefix, pathlen);291h = FindFirstFileW(pathbuf, &fd); /* Look up prefix */292free(pathbuf);293} else294h = FindFirstFileW(pathWithCanonicalPrefix, &fd); /* Look up prefix */295if (h != INVALID_HANDLE_VALUE) {296/* Lookup succeeded; append true name to result and continue */297FindClose(h);298if (!(dst = wcp(dst, dend, L'\0',299canonicalPrefix,300canonicalPrefix + wcslen(canonicalPrefix)))) {301return -1;302}303if (!(dst = wcp(dst, dend, L'\\',304fd.cFileName,305fd.cFileName + wcslen(fd.cFileName)))) {306return -1;307}308} else {309if (!lastErrorReportable()) {310if (!(dst = wcp(dst, dend, L'\0', src, src + wcslen(src)))) {311return -1;312}313} else {314return -1;315}316}317318if (dst >= dend) {319errno = ENAMETOOLONG;320return -1;321}322*dst = L'\0';323return 0;324}325326/* Non-Wide character version of canonicalize.327Converts to wchar and delegates to wcanonicalize. */328JNIEXPORT int329JDK_Canonicalize(const char *orig, char *out, int len) {330wchar_t* wpath = NULL;331wchar_t* wresult = NULL;332int wpath_len;333int ret = -1;334335/* Get required buffer size to convert to Unicode */336wpath_len = MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS,337orig, -1, NULL, 0);338if (wpath_len == 0) {339goto finish;340}341342if ((wpath = (wchar_t*) malloc(sizeof(wchar_t) * wpath_len)) == NULL) {343goto finish;344}345346if (MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS,347orig, -1, wpath, wpath_len) == 0) {348goto finish;349}350351if ((wresult = (wchar_t*) malloc(sizeof(wchar_t) * len)) == NULL) {352goto finish;353}354355if (wcanonicalize(wpath, wresult, len) != 0) {356goto finish;357}358359if (WideCharToMultiByte(CP_ACP, 0,360wresult, -1, out, len, NULL, NULL) == 0) {361goto finish;362}363364// Change return value to success.365ret = 0;366367finish:368free(wresult);369free(wpath);370371return ret;372}373374/* The appropriate location of getPrefixed() is io_util_md.c */375376/* copy \\?\ or \\?\UNC\ to the front of path */377JNIEXPORT WCHAR*378getPrefixed(const WCHAR* path, int pathlen) {379WCHAR* pathbuf = (WCHAR*)malloc((pathlen + 10) * sizeof (WCHAR));380if (pathbuf != 0) {381if (path[0] == L'\\' && path[1] == L'\\') {382if (path[2] == L'?' && path[3] == L'\\'){383/* if it already has a \\?\ don't do the prefix */384wcscpy(pathbuf, path );385} else {386/* only UNC pathname includes double slashes here */387wcscpy(pathbuf, L"\\\\?\\UNC\0");388wcscat(pathbuf, path + 1);389}390} else {391wcscpy(pathbuf, L"\\\\?\\\0");392wcscat(pathbuf, path );393}394}395return pathbuf;396}397398399