Save This Page
Home » openjdk-7 » java » io » [javadoc | source]
    1   /*
    2    * Copyright 1998-2005 Sun Microsystems, Inc.  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.  Sun designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
   22    * CA 95054 USA or visit www.sun.com if you need additional information or
   23    * have any 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               assert canonicalize0(path).equalsIgnoreCase(res);
  428               return res;
  429           }
  430       }
  431   
  432       protected native String canonicalize0(String path)
  433                                                   throws IOException;
  434       protected String canonicalizeWithPrefix(String canonicalPrefix,
  435                                               String filename) throws IOException
  436       {
  437           return canonicalizeWithPrefix0(canonicalPrefix,
  438                                          canonicalPrefix + File.separatorChar + filename);
  439       }
  440       // Run the canonicalization operation assuming that the prefix
  441       // (everything up to the last filename) is canonical; just gets
  442       // the canonical name of the last element of the path
  443       protected native String canonicalizeWithPrefix0(String canonicalPrefix,
  444                                                       String pathWithCanonicalPrefix)
  445                                                   throws IOException;
  446       // Best-effort attempt to get parent of this path; used for
  447       // optimization of filename canonicalization. This must return null for
  448       // any cases where the code in canonicalize_md.c would throw an
  449       // exception or otherwise deal with non-simple pathnames like handling
  450       // of "." and "..". It may conservatively return null in other
  451       // situations as well. Returning null will cause the underlying
  452       // (expensive) canonicalization routine to be called.
  453       static String parentOrNull(String path) {
  454           if (path == null) return null;
  455           char sep = File.separatorChar;
  456           char altSep = '/';
  457           int last = path.length() - 1;
  458           int idx = last;
  459           int adjacentDots = 0;
  460           int nonDotCount = 0;
  461           while (idx > 0) {
  462               char c = path.charAt(idx);
  463               if (c == '.') {
  464                   if (++adjacentDots >= 2) {
  465                       // Punt on pathnames containing . and ..
  466                       return null;
  467                   }
  468                   if (nonDotCount == 0) {
  469                       // Punt on pathnames ending in a .
  470                       return null;
  471                   }
  472               } else if (c == sep) {
  473                   if (adjacentDots == 1 && nonDotCount == 0) {
  474                       // Punt on pathnames containing . and ..
  475                       return null;
  476                   }
  477                   if (idx == 0 ||
  478                       idx >= last - 1 ||
  479                       path.charAt(idx - 1) == sep ||
  480                       path.charAt(idx - 1) == altSep) {
  481                       // Punt on pathnames containing adjacent slashes
  482                       // toward the end
  483                       return null;
  484                   }
  485                   return path.substring(0, idx);
  486               } else if (c == altSep) {
  487                   // Punt on pathnames containing both backward and
  488                   // forward slashes
  489                   return null;
  490               } else if (c == '*' || c == '?') {
  491                   // Punt on pathnames containing wildcards
  492                   return null;
  493               } else {
  494                   ++nonDotCount;
  495                   adjacentDots = 0;
  496               }
  497               --idx;
  498           }
  499           return null;
  500       }
  501   
  502   
  503       /* -- Attribute accessors -- */
  504   
  505       public native int getBooleanAttributes(File f);
  506       public native boolean checkAccess(File f, int access);
  507       public native long getLastModifiedTime(File f);
  508       public native long getLength(File f);
  509       public native boolean setPermission(File f, int access, boolean enable, boolean owneronly);
  510   
  511       /* -- File operations -- */
  512   
  513       public native boolean createFileExclusively(String path)
  514           throws IOException;
  515       public boolean delete(File f) {
  516           // Keep canonicalization caches in sync after file deletion
  517           // and renaming operations. Could be more clever than this
  518           // (i.e., only remove/update affected entries) but probably
  519           // not worth it since these entries expire after 30 seconds
  520           // anyway.
  521           cache.clear();
  522           prefixCache.clear();
  523           return delete0(f);
  524       }
  525       protected native boolean delete0(File f);
  526       public native String[] list(File f);
  527       public native boolean createDirectory(File f);
  528       public boolean rename(File f1, File f2) {
  529           // Keep canonicalization caches in sync after file deletion
  530           // and renaming operations. Could be more clever than this
  531           // (i.e., only remove/update affected entries) but probably
  532           // not worth it since these entries expire after 30 seconds
  533           // anyway.
  534           cache.clear();
  535           prefixCache.clear();
  536           return rename0(f1, f2);
  537       }
  538       protected native boolean rename0(File f1, File f2);
  539       public native boolean setLastModifiedTime(File f, long time);
  540       public native boolean setReadOnly(File f);
  541   
  542   
  543       /* -- Filesystem interface -- */
  544   
  545       private boolean access(String path) {
  546           try {
  547               SecurityManager security = System.getSecurityManager();
  548               if (security != null) security.checkRead(path);
  549               return true;
  550           } catch (SecurityException x) {
  551               return false;
  552           }
  553       }
  554   
  555       private static native int listRoots0();
  556   
  557       public File[] listRoots() {
  558           int ds = listRoots0();
  559           int n = 0;
  560           for (int i = 0; i < 26; i++) {
  561               if (((ds >> i) & 1) != 0) {
  562                   if (!access((char)('A' + i) + ":" + slash))
  563                       ds &= ~(1 << i);
  564                   else
  565                       n++;
  566               }
  567           }
  568           File[] fs = new File[n];
  569           int j = 0;
  570           char slash = this.slash;
  571           for (int i = 0; i < 26; i++) {
  572               if (((ds >> i) & 1) != 0)
  573                   fs[j++] = new File((char)('A' + i) + ":" + slash);
  574           }
  575           return fs;
  576       }
  577   
  578   
  579       /* -- Disk usage -- */
  580       public long getSpace(File f, int t) {
  581           if (f.exists()) {
  582               File file = (f.isDirectory() ? f : f.getParentFile());
  583               return getSpace0(file, t);
  584           }
  585           return 0;
  586       }
  587   
  588       private native long getSpace0(File f, int t);
  589   
  590   
  591       /* -- Basic infrastructure -- */
  592   
  593       public int compare(File f1, File f2) {
  594           return f1.getPath().compareToIgnoreCase(f2.getPath());
  595       }
  596   
  597       public int hashCode(File f) {
  598           /* Could make this more efficient: String.hashCodeIgnoreCase */
  599           return f.getPath().toLowerCase(Locale.ENGLISH).hashCode() ^ 1234321;
  600       }
  601   
  602   
  603       private static native void initIDs();
  604   
  605       static {
  606           initIDs();
  607       }
  608   
  609   }

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