Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/windows/native/java/io/canonicalize_md.c
32287 views
/*1* Copyright (c) 1998, 2013, 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>39#include "io_util_md.h"4041#undef DEBUG_PATH /* Define this to debug path code */4243#define isfilesep(c) ((c) == '/' || (c) == '\\')44#define wisfilesep(c) ((c) == L'/' || (c) == L'\\')45#define islb(c) (IsDBCSLeadByte((BYTE)(c)))464748/* Copy bytes to dst, not going past dend; return dst + number of bytes copied,49or NULL if dend would have been exceeded. If first != '\0', copy that byte50before copying bytes from src to send - 1. */5152static char *53cp(char *dst, char *dend, char first, char *src, char *send)54{55char *p = src, *q = dst;56if (first != '\0') {57if (q < dend) {58*q++ = first;59} else {60errno = ENAMETOOLONG;61return NULL;62}63}64if (send - p > dend - q) {65errno = ENAMETOOLONG;66return NULL;67}68while (p < send) {69*q++ = *p++;70}71return q;72}7374/* Wide character version of cp */7576static WCHAR*77wcp(WCHAR *dst, WCHAR *dend, WCHAR first, WCHAR *src, WCHAR *send)78{79WCHAR *p = src, *q = dst;80if (first != L'\0') {81if (q < dend) {82*q++ = first;83} else {84errno = ENAMETOOLONG;85return NULL;86}87}88if (send - p > dend - q) {89errno = ENAMETOOLONG;90return NULL;91}92while (p < send)93*q++ = *p++;94return q;95}969798/* Find first instance of '\\' at or following start. Return the address of99that byte or the address of the null terminator if '\\' is not found. */100101static char *102nextsep(char *start)103{104char *p = start;105int c;106while ((c = *p) && (c != '\\')) {107p += ((islb(c) && p[1]) ? 2 : 1);108}109return p;110}111112/* Wide character version of nextsep */113114static WCHAR *115wnextsep(WCHAR *start)116{117WCHAR *p = start;118int c;119while ((c = *p) && (c != L'\\'))120p++;121return p;122}123124/* Tell whether the given string contains any wildcard characters */125126static int127wild(char *start)128{129char *p = start;130int c;131while (c = *p) {132if ((c == '*') || (c == '?')) return 1;133p += ((islb(c) && p[1]) ? 2 : 1);134}135return 0;136}137138/* Wide character version of wild */139140static int141wwild(WCHAR *start)142{143WCHAR *p = start;144int c;145while (c = *p) {146if ((c == L'*') || (c == L'?'))147return 1;148p++;149}150return 0;151}152153/* Tell whether the given string contains prohibited combinations of dots.154In the canonicalized form no path element may have dots at its end.155Allowed canonical paths: c:\xa...dksd\..ksa\.lk c:\...a\.b\cd..x.x156Prohibited canonical paths: c:\..\x c:\x.\d c:\...157*/158static int159dots(char *start)160{161char *p = start;162while (*p) {163if ((p = strchr(p, '.')) == NULL) // find next occurrence of '.'164return 0; // no more dots165p++; // next char166while ((*p) == '.') // go to the end of dots167p++;168if (*p && (*p != '\\')) // path element does not end with a dot169p++; // go to the next char170else171return 1; // path element does end with a dot - prohibited172}173return 0; // no prohibited combinations of dots found174}175176/* Wide character version of dots */177static int178wdots(WCHAR *start)179{180WCHAR *p = start;181// Skip "\\.\" prefix182if (wcslen(p) > 4 && !wcsncmp(p, L"\\\\.\\", 4))183p = p + 4;184185while (*p) {186if ((p = wcschr(p, L'.')) == NULL) // find next occurrence of '.'187return 0; // no more dots188p++; // next char189while ((*p) == L'.') // go to the end of dots190p++;191if (*p && (*p != L'\\')) // path element does not end with a dot192p++; // go to the next char193else194return 1; // path element does end with a dot - prohibited195}196return 0; // no prohibited combinations of dots found197}198199/* If the lookup of a particular prefix fails because the file does not exist,200because it is of the wrong type, because access is denied, or because the201network is unreachable then canonicalization does not fail, it terminates202successfully after copying the rest of the original path to the result path.203Other I/O errors cause an error return.204*/205206int207lastErrorReportable()208{209DWORD errval = GetLastError();210if ((errval == ERROR_FILE_NOT_FOUND)211|| (errval == ERROR_DIRECTORY)212|| (errval == ERROR_PATH_NOT_FOUND)213|| (errval == ERROR_BAD_NETPATH)214|| (errval == ERROR_BAD_NET_NAME)215|| (errval == ERROR_ACCESS_DENIED)216|| (errval == ERROR_NETWORK_UNREACHABLE)217|| (errval == ERROR_NETWORK_ACCESS_DENIED)) {218return 0;219}220221#ifdef DEBUG_PATH222jio_fprintf(stderr, "canonicalize: errval %d\n", errval);223#endif224return 1;225}226227/* Convert a pathname to canonical form. The input orig_path is assumed to228have been converted to native form already, via JVM_NativePath(). This is229necessary because _fullpath() rejects duplicate separator characters on230Win95, though it accepts them on NT. */231232int233canonicalize(char *orig_path, char *result, int size)234{235WIN32_FIND_DATA fd;236HANDLE h;237char path[1024]; /* Working copy of path */238char *src, *dst, *dend;239240/* Reject paths that contain wildcards */241if (wild(orig_path)) {242errno = EINVAL;243return -1;244}245246/* Collapse instances of "foo\.." and ensure absoluteness. Note that247contrary to the documentation, the _fullpath procedure does not require248the drive to be available. It also does not reliably change all249occurrences of '/' to '\\' on Win95, so now JVM_NativePath does that. */250if(!_fullpath(path, orig_path, sizeof(path))) {251return -1;252}253254/* Correction for Win95: _fullpath may leave a trailing "\\"255on a UNC pathname */256if ((path[0] == '\\') && (path[1] == '\\')) {257char *p = path + strlen(path);258if ((p[-1] == '\\') && !islb(p[-2])) {259p[-1] = '\0';260}261}262263if (dots(path)) /* Check for prohibited combinations of dots */264return -1;265266src = path; /* Start scanning here */267dst = result; /* Place results here */268dend = dst + size; /* Don't go to or past here */269270/* Copy prefix, assuming path is absolute */271if (isalpha(src[0]) && (src[1] == ':') && (src[2] == '\\')) {272/* Drive specifier */273*src = toupper(*src); /* Canonicalize drive letter */274if (!(dst = cp(dst, dend, '\0', src, src + 2))) {275return -1;276}277src += 2;278} else if ((src[0] == '\\') && (src[1] == '\\')) {279/* UNC pathname */280char *p;281p = nextsep(src + 2); /* Skip past host name */282if (!*p) {283/* A UNC pathname must begin with "\\\\host\\share",284so reject this path as invalid if there is no share name */285errno = EINVAL;286return -1;287}288p = nextsep(p + 1); /* Skip past share name */289if (!(dst = cp(dst, dend, '\0', src, p))) {290return -1;291}292src = p;293} else {294/* Invalid path */295errno = EINVAL;296return -1;297}298299/* Windows 95/98/Me bug - FindFirstFile fails on network mounted drives */300/* for root pathes like "E:\" . If the path has this form, we should */301/* simply return it, it is already canonicalized. */302if (strlen(path) == 3 && path[1] == ':' && path[2] == '\\') {303/* At this point we have already copied the drive specifier ("z:")*/304/* so we need to copy "\" and the null character. */305result[2] = '\\';306result[3] = '\0';307return 0;308}309310/* At this point we have copied either a drive specifier ("z:") or a UNC311prefix ("\\\\host\\share") to the result buffer, and src points to the312first byte of the remainder of the path. We now scan through the rest313of the path, looking up each prefix in order to find the true name of314the last element of each prefix, thereby computing the full true name of315the original path. */316while (*src) {317char *p = nextsep(src + 1); /* Find next separator */318char c = *p;319assert(*src == '\\'); /* Invariant */320*p = '\0'; /* Temporarily clear separator */321h = FindFirstFile(path, &fd); /* Look up prefix */322*p = c; /* Restore separator */323if (h != INVALID_HANDLE_VALUE) {324/* Lookup succeeded; append true name to result and continue */325FindClose(h);326if (!(dst = cp(dst, dend, '\\',327fd.cFileName,328fd.cFileName + strlen(fd.cFileName)))) {329return -1;330}331src = p;332continue;333} else {334if (!lastErrorReportable()) {335if (!(dst = cp(dst, dend, '\0', src, src + strlen(src)))) {336return -1;337}338break;339} else {340return -1;341}342}343}344345if (dst >= dend) {346errno = ENAMETOOLONG;347return -1;348}349*dst = '\0';350return 0;351352}353354355/* Convert a pathname to canonical form. The input prefix is assumed356to be in canonical form already, and the trailing filename must not357contain any wildcard, dot/double dot, or other "tricky" characters358that are rejected by the canonicalize() routine above. This359routine is present to allow the canonicalization prefix cache to be360used while still returning canonical names with the correct361capitalization. */362363int364canonicalizeWithPrefix(char* canonicalPrefix, char* pathWithCanonicalPrefix, char *result, int size)365{366WIN32_FIND_DATA fd;367HANDLE h;368char *src, *dst, *dend;369370src = pathWithCanonicalPrefix;371dst = result; /* Place results here */372dend = dst + size; /* Don't go to or past here */373374h = FindFirstFile(pathWithCanonicalPrefix, &fd); /* Look up file */375if (h != INVALID_HANDLE_VALUE) {376/* Lookup succeeded; concatenate true name to prefix */377FindClose(h);378if (!(dst = cp(dst, dend, '\0',379canonicalPrefix,380canonicalPrefix + strlen(canonicalPrefix)))) {381return -1;382}383if (!(dst = cp(dst, dend, '\\',384fd.cFileName,385fd.cFileName + strlen(fd.cFileName)))) {386return -1;387}388} else {389if (!lastErrorReportable()) {390if (!(dst = cp(dst, dend, '\0', src, src + strlen(src)))) {391return -1;392}393} else {394return -1;395}396}397398if (dst >= dend) {399errno = ENAMETOOLONG;400return -1;401}402*dst = '\0';403return 0;404}405406407/* Wide character version of canonicalize. Size is a wide-character size. */408409int410wcanonicalize(WCHAR *orig_path, WCHAR *result, int size)411{412WIN32_FIND_DATAW fd;413HANDLE h;414WCHAR *path; /* Working copy of path */415WCHAR *src, *dst, *dend, c;416417/* Reject paths that contain wildcards */418if (wwild(orig_path)) {419errno = EINVAL;420return -1;421}422423if ((path = (WCHAR*)malloc(size * sizeof(WCHAR))) == NULL)424return -1;425426/* Collapse instances of "foo\.." and ensure absoluteness. Note that427contrary to the documentation, the _fullpath procedure does not require428the drive to be available. */429if(!_wfullpath(path, orig_path, size)) {430goto err;431}432433if (wdots(path)) /* Check for prohibited combinations of dots */434goto err;435436src = path; /* Start scanning here */437dst = result; /* Place results here */438dend = dst + size; /* Don't go to or past here */439440/* Copy prefix, assuming path is absolute */441c = src[0];442if (((c <= L'z' && c >= L'a') || (c <= L'Z' && c >= L'A'))443&& (src[1] == L':') && (src[2] == L'\\')) {444/* Drive specifier */445*src = towupper(*src); /* Canonicalize drive letter */446if (!(dst = wcp(dst, dend, L'\0', src, src + 2))) {447goto err;448}449450src += 2;451} else if ((src[0] == L'\\') && (src[1] == L'\\')) {452/* UNC pathname */453WCHAR *p;454p = wnextsep(src + 2); /* Skip past host name */455if (!*p) {456/* A UNC pathname must begin with "\\\\host\\share",457so reject this path as invalid if there is no share name */458errno = EINVAL;459goto err;460}461p = wnextsep(p + 1); /* Skip past share name */462if (!(dst = wcp(dst, dend, L'\0', src, p)))463goto err;464src = p;465} else {466/* Invalid path */467errno = EINVAL;468goto err;469}470/* At this point we have copied either a drive specifier ("z:") or a UNC471prefix ("\\\\host\\share") to the result buffer, and src points to the472first byte of the remainder of the path. We now scan through the rest473of the path, looking up each prefix in order to find the true name of474the last element of each prefix, thereby computing the full true name of475the original path. */476while (*src) {477WCHAR *p = wnextsep(src + 1); /* Find next separator */478WCHAR c = *p;479WCHAR *pathbuf;480int pathlen;481482assert(*src == L'\\'); /* Invariant */483*p = L'\0'; /* Temporarily clear separator */484485if ((pathlen = (int)wcslen(path)) > MAX_PATH - 1) {486pathbuf = getPrefixed(path, pathlen);487h = FindFirstFileW(pathbuf, &fd); /* Look up prefix */488free(pathbuf);489} else490h = FindFirstFileW(path, &fd); /* Look up prefix */491492*p = c; /* Restore separator */493if (h != INVALID_HANDLE_VALUE) {494/* Lookup succeeded; append true name to result and continue */495FindClose(h);496if (!(dst = wcp(dst, dend, L'\\', fd.cFileName,497fd.cFileName + wcslen(fd.cFileName)))){498goto err;499}500src = p;501continue;502} else {503if (!lastErrorReportable()) {504if (!(dst = wcp(dst, dend, L'\0', src, src + wcslen(src)))){505goto err;506}507break;508} else {509goto err;510}511}512}513514if (dst >= dend) {515errno = ENAMETOOLONG;516goto err;517}518*dst = L'\0';519free(path);520return 0;521522err:523free(path);524return -1;525}526527528/* Wide character version of canonicalizeWithPrefix. */529530int531wcanonicalizeWithPrefix(WCHAR *canonicalPrefix, WCHAR *pathWithCanonicalPrefix, WCHAR *result, int size)532{533WIN32_FIND_DATAW fd;534HANDLE h;535WCHAR *src, *dst, *dend;536WCHAR *pathbuf;537int pathlen;538539src = pathWithCanonicalPrefix;540dst = result; /* Place results here */541dend = dst + size; /* Don't go to or past here */542543544if ((pathlen=(int)wcslen(pathWithCanonicalPrefix)) > MAX_PATH - 1) {545pathbuf = getPrefixed(pathWithCanonicalPrefix, pathlen);546h = FindFirstFileW(pathbuf, &fd); /* Look up prefix */547free(pathbuf);548} else549h = FindFirstFileW(pathWithCanonicalPrefix, &fd); /* Look up prefix */550if (h != INVALID_HANDLE_VALUE) {551/* Lookup succeeded; append true name to result and continue */552FindClose(h);553if (!(dst = wcp(dst, dend, L'\0',554canonicalPrefix,555canonicalPrefix + wcslen(canonicalPrefix)))) {556return -1;557}558if (!(dst = wcp(dst, dend, L'\\',559fd.cFileName,560fd.cFileName + wcslen(fd.cFileName)))) {561return -1;562}563} else {564if (!lastErrorReportable()) {565if (!(dst = wcp(dst, dend, L'\0', src, src + wcslen(src)))) {566return -1;567}568} else {569return -1;570}571}572573if (dst >= dend) {574errno = ENAMETOOLONG;575return -1;576}577*dst = L'\0';578return 0;579}580581582/* The appropriate location of getPrefixed() should be io_util_md.c, but583java.lang.instrument package has hardwired canonicalize_md.c into their584dll, to avoid complicate solution such as including io_util_md.c into585that package, as a workaround we put this method here.586*/587588/* copy \\?\ or \\?\UNC\ to the front of path*/589WCHAR*590getPrefixed(const WCHAR* path, int pathlen) {591WCHAR* pathbuf = (WCHAR*)malloc((pathlen + 10) * sizeof (WCHAR));592if (pathbuf != 0) {593if (path[0] == L'\\' && path[1] == L'\\') {594if (path[2] == L'?' && path[3] == L'\\'){595/* if it already has a \\?\ don't do the prefix */596wcscpy(pathbuf, path );597} else {598/* only UNC pathname includes double slashes here */599wcscpy(pathbuf, L"\\\\?\\UNC\0");600wcscat(pathbuf, path + 1);601}602} else {603wcscpy(pathbuf, L"\\\\?\\\0");604wcscat(pathbuf, path );605}606}607return pathbuf;608}609610611