Home » openjdk-7 » java » io » [javadoc | source]

    1   /*
    2    * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Oracle designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Oracle in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   22    * or visit www.oracle.com if you need additional information or have any
   23    * questions.
   24    */
   25   
   26   package java.io;
   27   
   28   import java.security.AccessController;
   29   import java.util.Locale;
   30   import sun.security.action.GetPropertyAction;
   31   
   32   
   33   class Win32FileSystem extends FileSystem {
   34   
   35       private final char slash;
   36       private final char altSlash;
   37       private final char semicolon;
   38   
   39       public Win32FileSystem() {
   40           slash = AccessController.doPrivileged(
   41               new GetPropertyAction("file.separator")).charAt(0);
   42           semicolon = AccessController.doPrivileged(
   43               new GetPropertyAction("path.separator")).charAt(0);
   44           altSlash = (this.slash == '\\') ? '/' : '\\';
   45       }
   46   
   47       private boolean isSlash(char c) {
   48           return (c == '\\') || (c == '/');
   49       }
   50   
   51       private boolean isLetter(char c) {
   52           return ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z'));
   53       }
   54   
   55       private String slashify(String p) {
   56           if ((p.length() > 0) && (p.charAt(0) != slash)) return slash + p;
   57           else return p;
   58       }
   59   
   60   
   61       /* -- Normalization and construction -- */
   62   
   63       public char getSeparator() {
   64           return slash;
   65       }
   66   
   67       public char getPathSeparator() {
   68           return semicolon;
   69       }
   70   
   71       /* A normal Win32 pathname contains no duplicate slashes, except possibly
   72          for a UNC prefix, and does not end with a slash.  It may be the empty
   73          string.  Normalized Win32 pathnames have the convenient property that
   74          the length of the prefix almost uniquely identifies the type of the path
   75          and whether it is absolute or relative:
   76   
   77              0  relative to both drive and directory
   78              1  drive-relative (begins with '\\')
   79              2  absolute UNC (if first char is '\\'),
   80                   else directory-relative (has form "z:foo")
   81              3  absolute local pathname (begins with "z:\\")
   82        */
   83   
   84       private int normalizePrefix(String path, int len, StringBuffer sb) {
   85           int src = 0;
   86           while ((src < len) && isSlash(path.charAt(src))) src++;
   87           char c;
   88           if ((len - src >= 2)
   89               && isLetter(c = path.charAt(src))
   90               && path.charAt(src + 1) == ':') {
   91               /* Remove leading slashes if followed by drive specifier.
   92                  This hack is necessary to support file URLs containing drive
   93                  specifiers (e.g., "file://c:/path").  As a side effect,
   94                  "/c:/path" can be used as an alternative to "c:/path". */
   95               sb.append(c);
   96               sb.append(':');
   97               src += 2;
   98           } else {
   99               src = 0;
  100               if ((len >= 2)
  101                   && isSlash(path.charAt(0))
  102                   && isSlash(path.charAt(1))) {
  103                   /* UNC pathname: Retain first slash; leave src pointed at
  104                      second slash so that further slashes will be collapsed
  105                      into the second slash.  The result will be a pathname
  106                      beginning with "\\\\" followed (most likely) by a host
  107                      name. */
  108                   src = 1;
  109                   sb.append(slash);
  110               }
  111           }
  112           return src;
  113       }
  114   
  115       /* Normalize the given pathname, whose length is len, starting at the given
  116          offset; everything before this offset is already normal. */
  117       private String normalize(String path, int len, int off) {
  118           if (len == 0) return path;
  119           if (off < 3) off = 0;   /* Avoid fencepost cases with UNC pathnames */
  120           int src;
  121           char slash = this.slash;
  122           StringBuffer sb = new StringBuffer(len);
  123   
  124           if (off == 0) {
  125               /* Complete normalization, including prefix */
  126               src = normalizePrefix(path, len, sb);
  127           } else {
  128               /* Partial normalization */
  129               src = off;
  130               sb.append(path.substring(0, off));
  131           }
  132   
  133           /* Remove redundant slashes from the remainder of the path, forcing all
  134              slashes into the preferred slash */
  135           while (src < len) {
  136               char c = path.charAt(src++);
  137               if (isSlash(c)) {
  138                   while ((src < len) && isSlash(path.charAt(src))) src++;
  139                   if (src == len) {
  140                       /* Check for trailing separator */
  141                       int sn = sb.length();
  142                       if ((sn == 2) && (sb.charAt(1) == ':')) {
  143                           /* "z:\\" */
  144                           sb.append(slash);
  145                           break;
  146                       }
  147                       if (sn == 0) {
  148                           /* "\\" */
  149                           sb.append(slash);
  150                           break;
  151                       }
  152                       if ((sn == 1) && (isSlash(sb.charAt(0)))) {
  153                           /* "\\\\" is not collapsed to "\\" because "\\\\" marks
  154                              the beginning of a UNC pathname.  Even though it is
  155                              not, by itself, a valid UNC pathname, we leave it as
  156                              is in order to be consistent with the win32 APIs,
  157                              which treat this case as an invalid UNC pathname
  158                              rather than as an alias for the root directory of
  159                              the current drive. */
  160                           sb.append(slash);
  161                           break;
  162                       }
  163                       /* Path does not denote a root directory, so do not append
  164                          trailing slash */
  165                       break;
  166                   } else {
  167                       sb.append(slash);
  168                   }
  169               } else {
  170                   sb.append(c);
  171               }
  172           }
  173   
  174           String rv = sb.toString();
  175           return rv;
  176       }
  177   
  178       /* Check that the given pathname is normal.  If not, invoke the real
  179          normalizer on the part of the pathname that requires normalization.
  180          This way we iterate through the whole pathname string only once. */
  181       public String normalize(String path) {
  182           int n = path.length();
  183           char slash = this.slash;
  184           char altSlash = this.altSlash;
  185           char prev = 0;
  186           for (int i = 0; i < n; i++) {
  187               char c = path.charAt(i);
  188               if (c == altSlash)
  189                   return normalize(path, n, (prev == slash) ? i - 1 : i);
  190               if ((c == slash) && (prev == slash) && (i > 1))
  191                   return normalize(path, n, i - 1);
  192               if ((c == ':') && (i > 1))
  193                   return normalize(path, n, 0);
  194               prev = c;
  195           }
  196           if (prev == slash) return normalize(path, n, n - 1);
  197           return path;
  198       }
  199   
  200       public int prefixLength(String path) {
  201           char slash = this.slash;
  202           int n = path.length();
  203           if (n == 0) return 0;
  204           char c0 = path.charAt(0);
  205           char c1 = (n > 1) ? path.charAt(1) : 0;
  206           if (c0 == slash) {
  207               if (c1 == slash) return 2;  /* Absolute UNC pathname "\\\\foo" */
  208               return 1;                   /* Drive-relative "\\foo" */
  209           }
  210           if (isLetter(c0) && (c1 == ':')) {
  211               if ((n > 2) && (path.charAt(2) == slash))
  212                   return 3;               /* Absolute local pathname "z:\\foo" */
  213               return 2;                   /* Directory-relative "z:foo" */
  214           }
  215           return 0;                       /* Completely relative */
  216       }
  217   
  218       public String resolve(String parent, String child) {
  219           int pn = parent.length();
  220           if (pn == 0) return child;
  221           int cn = child.length();
  222           if (cn == 0) return parent;
  223   
  224           String c = child;
  225           int childStart = 0;
  226           int parentEnd = pn;
  227   
  228           if ((cn > 1) && (c.charAt(0) == slash)) {
  229               if (c.charAt(1) == slash) {
  230                   /* Drop prefix when child is a UNC pathname */
  231                   childStart = 2;
  232               } else {
  233                   /* Drop prefix when child is drive-relative */
  234                   childStart = 1;
  235   
  236               }
  237               if (cn == childStart) { // Child is double slash
  238                   if (parent.charAt(pn - 1) == slash)
  239                       return parent.substring(0, pn - 1);
  240                   return parent;
  241               }
  242           }
  243   
  244           if (parent.charAt(pn - 1) == slash)
  245               parentEnd--;
  246   
  247           int strlen = parentEnd + cn - childStart;
  248           char[] theChars = null;
  249           if (child.charAt(childStart) == slash) {
  250               theChars = new char[strlen];
  251               parent.getChars(0, parentEnd, theChars, 0);
  252               child.getChars(childStart, cn, theChars, parentEnd);
  253           } else {
  254               theChars = new char[strlen + 1];
  255               parent.getChars(0, parentEnd, theChars, 0);
  256               theChars[parentEnd] = slash;
  257               child.getChars(childStart, cn, theChars, parentEnd + 1);
  258           }
  259           return new String(theChars);
  260       }
  261   
  262       public String getDefaultParent() {
  263           return ("" + slash);
  264       }
  265   
  266       public String fromURIPath(String path) {
  267           String p = path;
  268           if ((p.length() > 2) && (p.charAt(2) == ':')) {
  269               // "/c:/foo" --> "c:/foo"
  270               p = p.substring(1);
  271               // "c:/foo/" --> "c:/foo", but "c:/" --> "c:/"
  272               if ((p.length() > 3) && p.endsWith("/"))
  273                   p = p.substring(0, p.length() - 1);
  274           } else if ((p.length() > 1) && p.endsWith("/")) {
  275               // "/foo/" --> "/foo"
  276               p = p.substring(0, p.length() - 1);
  277           }
  278           return p;
  279       }
  280   
  281   
  282   
  283       /* -- Path operations -- */
  284   
  285       public boolean isAbsolute(File f) {
  286           int pl = f.getPrefixLength();
  287           return (((pl == 2) && (f.getPath().charAt(0) == slash))
  288                   || (pl == 3));
  289       }
  290   
  291       protected native String getDriveDirectory(int drive);
  292   
  293       private static String[] driveDirCache = new String[26];
  294   
  295       private static int driveIndex(char d) {
  296           if ((d >= 'a') && (d <= 'z')) return d - 'a';
  297           if ((d >= 'A') && (d <= 'Z')) return d - 'A';
  298           return -1;
  299       }
  300   
  301       private String getDriveDirectory(char drive) {
  302           int i = driveIndex(drive);
  303           if (i < 0) return null;
  304           String s = driveDirCache[i];
  305           if (s != null) return s;
  306           s = getDriveDirectory(i + 1);
  307           driveDirCache[i] = s;
  308           return s;
  309       }
  310   
  311       private String getUserPath() {
  312           /* For both compatibility and security,
  313              we must look this up every time */
  314           return normalize(System.getProperty("user.dir"));
  315       }
  316   
  317       private String getDrive(String path) {
  318           int pl = prefixLength(path);
  319           return (pl == 3) ? path.substring(0, 2) : null;
  320       }
  321   
  322       public String resolve(File f) {
  323           String path = f.getPath();
  324           int pl = f.getPrefixLength();
  325           if ((pl == 2) && (path.charAt(0) == slash))
  326               return path;                        /* UNC */
  327           if (pl == 3)
  328               return path;                        /* Absolute local */
  329           if (pl == 0)
  330               return getUserPath() + slashify(path); /* Completely relative */
  331           if (pl == 1) {                          /* Drive-relative */
  332               String up = getUserPath();
  333               String ud = getDrive(up);
  334               if (ud != null) return ud + path;
  335               return up + path;                   /* User dir is a UNC path */
  336           }
  337           if (pl == 2) {                          /* Directory-relative */
  338               String up = getUserPath();
  339               String ud = getDrive(up);
  340               if ((ud != null) && path.startsWith(ud))
  341                   return up + slashify(path.substring(2));
  342               char drive = path.charAt(0);
  343               String dir = getDriveDirectory(drive);
  344               String np;
  345               if (dir != null) {
  346                   /* When resolving a directory-relative path that refers to a
  347                      drive other than the current drive, insist that the caller
  348                      have read permission on the result */
  349                   String p = drive + (':' + dir + slashify(path.substring(2)));
  350                   SecurityManager security = System.getSecurityManager();
  351                   try {
  352                       if (security != null) security.checkRead(p);
  353                   } catch (SecurityException x) {
  354                       /* Don't disclose the drive's directory in the exception */
  355                       throw new SecurityException("Cannot resolve path " + path);
  356                   }
  357                   return p;
  358               }
  359               return drive + ":" + slashify(path.substring(2)); /* fake it */
  360           }
  361           throw new InternalError("Unresolvable path: " + path);
  362       }
  363   
  364       // Caches for canonicalization results to improve startup performance.
  365       // The first cache handles repeated canonicalizations of the same path
  366       // name. The prefix cache handles repeated canonicalizations within the
  367       // same directory, and must not create results differing from the true
  368       // canonicalization algorithm in canonicalize_md.c. For this reason the
  369       // prefix cache is conservative and is not used for complex path names.
  370       private ExpiringCache cache       = new ExpiringCache();
  371       private ExpiringCache prefixCache = new ExpiringCache();
  372   
  373       public String canonicalize(String path) throws IOException {
  374           // If path is a drive letter only then skip canonicalization
  375           int len = path.length();
  376           if ((len == 2) &&
  377               (isLetter(path.charAt(0))) &&
  378               (path.charAt(1) == ':')) {
  379               char c = path.charAt(0);
  380               if ((c >= 'A') && (c <= 'Z'))
  381                   return path;
  382               return "" + ((char) (c-32)) + ':';
  383           } else if ((len == 3) &&
  384                      (isLetter(path.charAt(0))) &&
  385                      (path.charAt(1) == ':') &&
  386                      (path.charAt(2) == '\\')) {
  387               char c = path.charAt(0);
  388               if ((c >= 'A') && (c <= 'Z'))
  389                   return path;
  390               return "" + ((char) (c-32)) + ':' + '\\';
  391           }
  392           if (!useCanonCaches) {
  393               return canonicalize0(path);
  394           } else {
  395               String res = cache.get(path);
  396               if (res == null) {
  397                   String dir = null;
  398                   String resDir = null;
  399                   if (useCanonPrefixCache) {
  400                       dir = parentOrNull(path);
  401                       if (dir != null) {
  402                           resDir = prefixCache.get(dir);
  403                           if (resDir != null) {
  404                               // Hit only in prefix cache; full path is canonical,
  405                               // but we need to get the canonical name of the file
  406                               // in this directory to get the appropriate capitalization
  407                               String filename = path.substring(1 + dir.length());
  408                               res = canonicalizeWithPrefix(resDir, filename);
  409                               cache.put(dir + File.separatorChar + filename, res);
  410                           }
  411                       }
  412                   }
  413                   if (res == null) {
  414                       res = canonicalize0(path);
  415                       cache.put(path, res);
  416                       if (useCanonPrefixCache && dir != null) {
  417                           resDir = parentOrNull(res);
  418                           if (resDir != null) {
  419                               File f = new File(res);
  420                               if (f.exists() && !f.isDirectory()) {
  421                                   prefixCache.put(dir, resDir);
  422                               }
  423                           }
  424                       }
  425                   }
  426               }
  427               return res;
  428           }
  429       }
  430   
  431       protected native String canonicalize0(String path)
  432                                                   throws IOException;
  433       protected String canonicalizeWithPrefix(String canonicalPrefix,
  434                                               String filename) throws IOException
  435       {
  436           return canonicalizeWithPrefix0(canonicalPrefix,
  437                                          canonicalPrefix + File.separatorChar + filename);
  438       }
  439       // Run the canonicalization operation assuming that the prefix
  440       // (everything up to the last filename) is canonical; just gets
  441       // the canonical name of the last element of the path
  442       protected native String canonicalizeWithPrefix0(String canonicalPrefix,
  443                                                       String pathWithCanonicalPrefix)
  444                                                   throws IOException;
  445       // Best-effort attempt to get parent of this path; used for
  446       // optimization of filename canonicalization. This must return null for
  447       // any cases where the code in canonicalize_md.c would throw an
  448       // exception or otherwise deal with non-simple pathnames like handling
  449       // of "." and "..". It may conservatively return null in other
  450       // situations as well. Returning null will cause the underlying
  451       // (expensive) canonicalization routine to be called.
  452       static String parentOrNull(String path) {
  453           if (path == null) return null;
  454           char sep = File.separatorChar;
  455           char altSep = '/';
  456           int last = path.length() - 1;
  457           int idx = last;
  458           int adjacentDots = 0;
  459           int nonDotCount = 0;
  460           while (idx > 0) {
  461               char c = path.charAt(idx);
  462               if (c == '.') {
  463                   if (++adjacentDots >= 2) {
  464                       // Punt on pathnames containing . and ..
  465                       return null;
  466                   }
  467                   if (nonDotCount == 0) {
  468                       // Punt on pathnames ending in a .
  469                       return null;
  470                   }
  471               } else if (c == sep) {
  472                   if (adjacentDots == 1 && nonDotCount == 0) {
  473                       // Punt on pathnames containing . and ..
  474                       return null;
  475                   }
  476                   if (idx == 0 ||
  477                       idx >= last - 1 ||
  478                       path.charAt(idx - 1) == sep ||
  479                       path.charAt(idx - 1) == altSep) {
  480                       // Punt on pathnames containing adjacent slashes
  481                       // toward the end
  482                       return null;
  483                   }
  484                   return path.substring(0, idx);
  485               } else if (c == altSep) {
  486                   // Punt on pathnames containing both backward and
  487                   // forward slashes
  488                   return null;
  489               } else if (c == '*' || c == '?') {
  490                   // Punt on pathnames containing wildcards
  491                   return null;
  492               } else {
  493                   ++nonDotCount;
  494                   adjacentDots = 0;
  495               }
  496               --idx;
  497           }
  498           return null;
  499       }
  500   
  501   
  502       /* -- Attribute accessors -- */
  503   
  504       public native int getBooleanAttributes(File f);
  505       public native boolean checkAccess(File f, int access);
  506       public native long getLastModifiedTime(File f);
  507       public native long getLength(File f);
  508       public native boolean setPermission(File f, int access, boolean enable, boolean owneronly);
  509   
  510       /* -- File operations -- */
  511   
  512       public native boolean createFileExclusively(String path)
  513           throws IOException;
  514       public boolean delete(File f) {
  515           // Keep canonicalization caches in sync after file deletion
  516           // and renaming operations. Could be more clever than this
  517           // (i.e., only remove/update affected entries) but probably
  518           // not worth it since these entries expire after 30 seconds
  519           // anyway.
  520           cache.clear();
  521           prefixCache.clear();
  522           return delete0(f);
  523       }
  524       protected native boolean delete0(File f);
  525       public native String[] list(File f);
  526       public native boolean createDirectory(File f);
  527       public boolean rename(File f1, File f2) {
  528           // Keep canonicalization caches in sync after file deletion
  529           // and renaming operations. Could be more clever than this
  530           // (i.e., only remove/update affected entries) but probably
  531           // not worth it since these entries expire after 30 seconds
  532           // anyway.
  533           cache.clear();
  534           prefixCache.clear();
  535           return rename0(f1, f2);
  536       }
  537       protected native boolean rename0(File f1, File f2);
  538       public native boolean setLastModifiedTime(File f, long time);
  539       public native boolean setReadOnly(File f);
  540   
  541   
  542       /* -- Filesystem interface -- */
  543   
  544       private boolean access(String path) {
  545           try {
  546               SecurityManager security = System.getSecurityManager();
  547               if (security != null) security.checkRead(path);
  548               return true;
  549           } catch (SecurityException x) {
  550               return false;
  551           }
  552       }
  553   
  554       private static native int listRoots0();
  555   
  556       public File[] listRoots() {
  557           int ds = listRoots0();
  558           int n = 0;
  559           for (int i = 0; i < 26; i++) {
  560               if (((ds >> i) & 1) != 0) {
  561                   if (!access((char)('A' + i) + ":" + slash))
  562                       ds &= ~(1 << i);
  563                   else
  564                       n++;
  565               }
  566           }
  567           File[] fs = new File[n];
  568           int j = 0;
  569           char slash = this.slash;
  570           for (int i = 0; i < 26; i++) {
  571               if (((ds >> i) & 1) != 0)
  572                   fs[j++] = new File((char)('A' + i) + ":" + slash);
  573           }
  574           return fs;
  575       }
  576   
  577   
  578       /* -- Disk usage -- */
  579       public long getSpace(File f, int t) {
  580           if (f.exists()) {
  581               File file = (f.isDirectory() ? f : f.getParentFile());
  582               return getSpace0(file, t);
  583           }
  584           return 0;
  585       }
  586   
  587       private native long getSpace0(File f, int t);
  588   
  589   
  590       /* -- Basic infrastructure -- */
  591   
  592       public int compare(File f1, File f2) {
  593           return f1.getPath().compareToIgnoreCase(f2.getPath());
  594       }
  595   
  596       public int hashCode(File f) {
  597           /* Could make this more efficient: String.hashCodeIgnoreCase */
  598           return f.getPath().toLowerCase(Locale.ENGLISH).hashCode() ^ 1234321;
  599       }
  600   
  601   
  602       private static native void initIDs();
  603   
  604       static {
  605           initIDs();
  606       }
  607   
  608   }

Home » openjdk-7 » java » io » [javadoc | source]