< prev index next >

src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystem.java

Print this page
rev 53034 : 8215472: Cleanups in implementation classes of jdk.zipfs and tests
   1 /*
   2  * Copyright (c) 2009, 2017, 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 jdk.nio.zipfs;
  27 






  28 import java.io.BufferedOutputStream;
  29 import java.io.ByteArrayInputStream;
  30 import java.io.ByteArrayOutputStream;
  31 import java.io.EOFException;
  32 import java.io.File;
  33 import java.io.FilterOutputStream;
  34 import java.io.IOException;
  35 import java.io.InputStream;
  36 import java.io.OutputStream;
  37 import java.nio.ByteBuffer;
  38 import java.nio.MappedByteBuffer;
  39 import java.nio.channels.*;




  40 import java.nio.file.*;
  41 import java.nio.file.attribute.*;
  42 import java.nio.file.spi.*;


  43 import java.security.AccessController;
  44 import java.security.PrivilegedAction;
  45 import java.security.PrivilegedActionException;
  46 import java.security.PrivilegedExceptionAction;
  47 import java.util.*;










  48 import java.util.concurrent.locks.ReadWriteLock;
  49 import java.util.concurrent.locks.ReentrantReadWriteLock;
  50 import java.util.regex.Pattern;
  51 import java.util.zip.CRC32;
  52 import java.util.zip.Inflater;
  53 import java.util.zip.Deflater;
  54 import java.util.zip.InflaterInputStream;
  55 import java.util.zip.DeflaterOutputStream;


  56 import java.util.zip.ZipException;
  57 import static java.lang.Boolean.*;
  58 import static jdk.nio.zipfs.ZipConstants.*;
  59 import static jdk.nio.zipfs.ZipUtils.*;
  60 import static java.nio.file.StandardOpenOption.*;
  61 import static java.nio.file.StandardCopyOption.*;
  62 
  63 /**
  64  * A FileSystem built on a zip file
  65  *
  66  * @author Xueming Shen
  67  */
  68 
  69 class ZipFileSystem extends FileSystem {
  70 
  71     private final ZipFileSystemProvider provider;
  72     private final Path zfpath;
  73     final ZipCoder zc;
  74     private final ZipPath rootdir;
  75     private boolean readOnly = false;    // readonly file system
  76 
  77     // configurable by env map
  78     private final boolean noExtt;        // see readExtra()
  79     private final boolean useTempFile;   // use a temp file for newOS, default
  80                                          // is to use BAOS for better performance
  81     private static final boolean isWindows = AccessController.doPrivileged(
  82             (PrivilegedAction<Boolean>) () -> System.getProperty("os.name")
  83                                                     .startsWith("Windows"));
  84     private final boolean forceEnd64;
  85     private final int defaultMethod;     // METHOD_STORED if "noCompression=true"
  86                                          // METHOD_DEFLATED otherwise
  87 
  88     ZipFileSystem(ZipFileSystemProvider provider,
  89                   Path zfpath,
  90                   Map<String, ?> env)  throws IOException
  91     {
  92         // default encoding for name/comment
  93         String nameEncoding = env.containsKey("encoding") ?
  94                               (String)env.get("encoding") : "UTF-8";
  95         this.noExtt = "false".equals(env.get("zipinfo-time"));
  96         this.useTempFile  = isTrue(env, "useTempFile");
  97         this.forceEnd64 = isTrue(env, "forceZIP64End");
  98         this.defaultMethod = isTrue(env, "noCompression") ? METHOD_STORED: METHOD_DEFLATED;
  99         if (Files.notExists(zfpath)) {
 100             // create a new zip if not exists
 101             if (isTrue(env, "create")) {
 102                 try (OutputStream os = Files.newOutputStream(zfpath, CREATE_NEW, WRITE)) {
 103                     new END().write(os, 0, forceEnd64);
 104                 }
 105             } else {
 106                 throw new FileSystemNotFoundException(zfpath.toString());
 107             }
 108         }
 109         // sm and existence check
 110         zfpath.getFileSystem().provider().checkAccess(zfpath, AccessMode.READ);


 252         return new PathMatcher() {
 253             @Override
 254             public boolean matches(Path path) {
 255                 return pattern.matcher(path.toString()).matches();
 256             }
 257         };
 258     }
 259 
 260     @Override
 261     public void close() throws IOException {
 262         beginWrite();
 263         try {
 264             if (!isOpen)
 265                 return;
 266             isOpen = false;          // set closed
 267         } finally {
 268             endWrite();
 269         }
 270         if (!streams.isEmpty()) {    // unlock and close all remaining streams
 271             Set<InputStream> copy = new HashSet<>(streams);
 272             for (InputStream is: copy)
 273                 is.close();
 274         }
 275         beginWrite();                // lock and sync
 276         try {
 277             AccessController.doPrivileged((PrivilegedExceptionAction<Void>) () -> {
 278                 sync(); return null;
 279             });
 280             ch.close();              // close the ch just in case no update
 281                                      // and sync didn't close the ch
 282         } catch (PrivilegedActionException e) {
 283             throw (IOException)e.getException();
 284         } finally {
 285             endWrite();
 286         }
 287 
 288         synchronized (inflaters) {
 289             for (Inflater inf : inflaters)
 290                 inf.end();
 291         }
 292         synchronized (deflaters) {
 293             for (Deflater def : deflaters)
 294                 def.end();
 295         }
 296 
 297         IOException ioe = null;
 298         synchronized (tmppaths) {
 299             for (Path p: tmppaths) {
 300                 try {
 301                     AccessController.doPrivileged(
 302                         (PrivilegedExceptionAction<Boolean>)() -> Files.deleteIfExists(p));
 303                 } catch (PrivilegedActionException e) {
 304                     IOException x = (IOException)e.getException();
 305                     if (ioe == null)
 306                         ioe = x;
 307                     else
 308                         ioe.addSuppressed(x);
 309                 }
 310             }
 311         }
 312         provider.removeFileSystem(zfpath, this);
 313         if (ioe != null)
 314            throw ioe;
 315     }
 316 
 317     ZipFileAttributes getFileAttributes(byte[] path)
 318         throws IOException
 319     {


 504             if (!hasCopyAttrs)
 505                 u.mtime = u.atime= u.ctime = System.currentTimeMillis();
 506             update(u);
 507             if (deletesrc)
 508                 updateDelete(eSrc);
 509         } finally {
 510             endWrite();
 511         }
 512     }
 513 
 514     // Returns an output stream for writing the contents into the specified
 515     // entry.
 516     OutputStream newOutputStream(byte[] path, OpenOption... options)
 517         throws IOException
 518     {
 519         checkWritable();
 520         boolean hasCreateNew = false;
 521         boolean hasCreate = false;
 522         boolean hasAppend = false;
 523         boolean hasTruncate = false;
 524         for (OpenOption opt: options) {
 525             if (opt == READ)
 526                 throw new IllegalArgumentException("READ not allowed");
 527             if (opt == CREATE_NEW)
 528                 hasCreateNew = true;
 529             if (opt == CREATE)
 530                 hasCreate = true;
 531             if (opt == APPEND)
 532                 hasAppend = true;
 533             if (opt == TRUNCATE_EXISTING)
 534                 hasTruncate = true;
 535         }
 536         if (hasAppend && hasTruncate)
 537             throw new IllegalArgumentException("APPEND + TRUNCATE_EXISTING not allowed");
 538         beginRead();                 // only need a readlock, the "update()" will
 539         try {                        // try to obtain a writelock when the os is
 540             ensureOpen();            // being closed.
 541             Entry e = getEntry(path);
 542             if (e != null) {
 543                 if (e.isDir() || hasCreateNew)
 544                     throw new FileAlreadyExistsException(getString(path));


1460 
1461     private InputStream getInputStream(Entry e)
1462         throws IOException
1463     {
1464         InputStream eis = null;
1465 
1466         if (e.type == Entry.NEW) {
1467             // now bytes & file is uncompressed.
1468             if (e.bytes != null)
1469                 return new ByteArrayInputStream(e.bytes);
1470             else if (e.file != null)
1471                 return Files.newInputStream(e.file);
1472             else
1473                 throw new ZipException("update entry data is missing");
1474         } else if (e.type == Entry.FILECH) {
1475             // FILECH result is un-compressed.
1476             eis = Files.newInputStream(e.file);
1477             // TBD: wrap to hook close()
1478             // streams.add(eis);
1479             return eis;
1480         } else {  // untouced  CEN or COPY
1481             eis = new EntryInputStream(e, ch);
1482         }
1483         if (e.method == METHOD_DEFLATED) {
1484             // MORE: Compute good size for inflater stream:
1485             long bufSize = e.size + 2; // Inflater likes a bit of slack
1486             if (bufSize > 65536)
1487                 bufSize = 8192;
1488             final long size = e.size;
1489             eis = new InflaterInputStream(eis, getInflater(), (int)bufSize) {
1490                 private boolean isClosed = false;
1491                 public void close() throws IOException {
1492                     if (!isClosed) {
1493                         releaseInflater(inf);
1494                         this.in.close();
1495                         isClosed = true;
1496                         streams.remove(this);
1497                     }
1498                 }
1499                 // Override fill() method to provide an extra "dummy" byte
1500                 // at the end of the input stream. This is required when


1522                     return avail > (long) Integer.MAX_VALUE ?
1523                         Integer.MAX_VALUE : (int) avail;
1524                 }
1525             };
1526         } else if (e.method == METHOD_STORED) {
1527             // TBD: wrap/ it does not seem necessary
1528         } else {
1529             throw new ZipException("invalid compression method");
1530         }
1531         streams.add(eis);
1532         return eis;
1533     }
1534 
1535     // Inner class implementing the input stream used to read
1536     // a (possibly compressed) zip file entry.
1537     private class EntryInputStream extends InputStream {
1538         private final SeekableByteChannel zfch; // local ref to zipfs's "ch". zipfs.ch might
1539                                           // point to a new channel after sync()
1540         private   long pos;               // current position within entry data
1541         protected long rem;               // number of remaining bytes within entry
1542         protected final long size;        // uncompressed size of this entry
1543 
1544         EntryInputStream(Entry e, SeekableByteChannel zfch)
1545             throws IOException
1546         {
1547             this.zfch = zfch;
1548             rem = e.csize;
1549             size = e.size;
1550             pos = e.locoff;
1551             if (pos == -1) {
1552                 Entry e2 = getEntry(e.name);
1553                 if (e2 == null) {
1554                     throw new ZipException("invalid loc for entry <" + e.name + ">");
1555                 }
1556                 pos = e2.locoff;
1557             }
1558             pos = -pos;  // lazy initialize the real data offset
1559         }
1560 
1561         public int read(byte b[], int off, int len) throws IOException {
1562             ensureOpen();
1563             initDataPos();
1564             if (rem == 0) {
1565                 return -1;
1566             }
1567             if (len <= 0) {
1568                 return 0;
1569             }


1596                 return -1;
1597             }
1598         }
1599 
1600         public long skip(long n) throws IOException {
1601             ensureOpen();
1602             if (n > rem)
1603                 n = rem;
1604             pos += n;
1605             rem -= n;
1606             if (rem == 0) {
1607                 close();
1608             }
1609             return n;
1610         }
1611 
1612         public int available() {
1613             return rem > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) rem;
1614         }
1615 
1616         public long size() {
1617             return size;
1618         }
1619 
1620         public void close() {
1621             rem = 0;
1622             streams.remove(this);
1623         }
1624 
1625         private void initDataPos() throws IOException {
1626             if (pos <= 0) {
1627                 pos = -pos + locpos;
1628                 byte[] buf = new byte[LOCHDR];
1629                 if (readFullyAt(buf, 0, buf.length, pos) != LOCHDR) {
1630                     throw new ZipException("invalid loc " + pos + " for entry reading");
1631                 }
1632                 pos += LOCHDR + LOCNAM(buf) + LOCEXT(buf);
1633             }
1634         }
1635     }
1636 
1637     static void zerror(String msg) throws ZipException {
1638         throw new ZipException(msg);
1639     }


1655                 return new Inflater(true);
1656             }
1657         }
1658     }
1659 
1660     // Releases the specified inflater to the list of available inflaters.
1661     private void releaseInflater(Inflater inf) {
1662         synchronized (inflaters) {
1663             if (inflaters.size() < MAX_FLATER) {
1664                 inf.reset();
1665                 inflaters.add(inf);
1666             } else {
1667                 inf.end();
1668             }
1669         }
1670     }
1671 
1672     // List of available Deflater objects for compression
1673     private final List<Deflater> deflaters = new ArrayList<>();
1674 
1675     // Gets an deflater from the list of available deflaters or allocates
1676     // a new one.
1677     private Deflater getDeflater() {
1678         synchronized (deflaters) {
1679             int size = deflaters.size();
1680             if (size > 0) {
1681                 Deflater def = deflaters.remove(size - 1);
1682                 return def;
1683             } else {
1684                 return new Deflater(Deflater.DEFAULT_COMPRESSION, true);
1685             }
1686         }
1687     }
1688 
1689     // Releases the specified inflater to the list of available inflaters.
1690     private void releaseDeflater(Deflater def) {
1691         synchronized (deflaters) {
1692             if (inflaters.size() < MAX_FLATER) {
1693                def.reset();
1694                deflaters.add(def);
1695             } else {
1696                def.end();
1697             }
1698         }
1699     }
1700 
1701     // End of central directory record
1702     static class END {
1703         // these 2 fields are not used by anyone and write() uses "0"
1704         // int  disknum;
1705         // int  sdisknum;
1706         int  endsub;     // endsub
1707         int  centot;     // 4 bytes
1708         long cenlen;     // 4 bytes
1709         long cenoff;     // 4 bytes
1710         int  comlen;     // comment length
1711         byte[] comment;
1712 
1713         /* members of Zip64 end of central directory locator */
1714         // int diskNum;
1715         long endpos;
1716         // int disktot;
1717 
1718         void write(OutputStream os, long offset, boolean forceEnd64) throws IOException {
1719             boolean hasZip64 = forceEnd64; // false;
1720             long xlen = cenlen;


1968             attrsEx     = CENATX(cen, pos);
1969             */
1970             locoff      = CENOFF(cen, pos);
1971             pos += CENHDR;
1972             this.name = inode.name;
1973             this.isdir = inode.isdir;
1974             this.hashcode = inode.hashcode;
1975 
1976             pos += nlen;
1977             if (elen > 0) {
1978                 extra = Arrays.copyOfRange(cen, pos, pos + elen);
1979                 pos += elen;
1980                 readExtra(zipfs);
1981             }
1982             if (clen > 0) {
1983                 comment = Arrays.copyOfRange(cen, pos, pos + clen);
1984             }
1985             return this;
1986         }
1987 
1988         int writeCEN(OutputStream os) throws IOException
1989         {
1990             int written  = CENHDR;
1991             int version0 = version();
1992             long csize0  = csize;
1993             long size0   = size;
1994             long locoff0 = locoff;
1995             int elen64   = 0;                // extra for ZIP64
1996             int elenNTFS = 0;                // extra for NTFS (a/c/mtime)
1997             int elenEXTT = 0;                // extra for Extended Timestamp
1998             boolean foundExtraTime = false;  // if time stamp NTFS, EXTT present
1999 
2000             byte[] zname = isdir ? toDirectoryPath(name) : name;
2001 
2002             // confirm size/length
2003             int nlen = (zname != null) ? zname.length - 1 : 0;  // name has [0] as "slash"
2004             int elen = (extra != null) ? extra.length : 0;
2005             int eoff = 0;
2006             int clen = (comment != null) ? comment.length : 0;
2007             if (csize >= ZIP64_MINVAL) {
2008                 csize0 = ZIP64_MINVAL;
2009                 elen64 += 8;                 // csize(8)
2010             }


2085             if (elenEXTT != 0) {
2086                 writeShort(os, EXTID_EXTT);
2087                 writeShort(os, elenEXTT - 4);
2088                 if (ctime == -1)
2089                     os.write(0x3);          // mtime and atime
2090                 else
2091                     os.write(0x7);          // mtime, atime and ctime
2092                 writeInt(os, javaToUnixTime(mtime));
2093             }
2094             if (extra != null)              // whatever not recognized
2095                 writeBytes(os, extra);
2096             if (comment != null)            //TBD: 0, Math.min(commentBytes.length, 0xffff));
2097                 writeBytes(os, comment);
2098             return CENHDR + nlen + elen + clen + elen64 + elenNTFS + elenEXTT;
2099         }
2100 
2101         ///////////////////// LOC //////////////////////
2102 
2103         int writeLOC(OutputStream os) throws IOException {
2104             writeInt(os, LOCSIG);               // LOC header signature
2105             int version = version();
2106 
2107             byte[] zname = isdir ? toDirectoryPath(name) : name;
2108             int nlen = (zname != null) ? zname.length - 1 : 0; // [0] is slash
2109             int elen = (extra != null) ? extra.length : 0;
2110             boolean foundExtraTime = false;     // if extra timestamp present
2111             int eoff = 0;
2112             int elen64 = 0;
2113             int elenEXTT = 0;
2114             int elenNTFS = 0;
2115             if ((flag & FLAG_DATADESCR) != 0) {
2116                 writeShort(os, version());      // version needed to extract
2117                 writeShort(os, flag);           // general purpose bit flag
2118                 writeShort(os, method);         // compression method
2119                 // last modification time
2120                 writeInt(os, (int)javaToDosTime(mtime));
2121                 // store size, uncompressed size, and crc-32 in data descriptor
2122                 // immediately following compressed entry data
2123                 writeInt(os, 0);
2124                 writeInt(os, 0);
2125                 writeInt(os, 0);
2126             } else {


2413             fm.format("    isDirectory     : %b%n", isDirectory());
2414             fm.format("    isSymbolicLink  : %b%n", isSymbolicLink());
2415             fm.format("    isOther         : %b%n", isOther());
2416             fm.format("    fileKey         : %s%n", fileKey());
2417             fm.format("    size            : %d%n", size());
2418             fm.format("    compressedSize  : %d%n", compressedSize());
2419             fm.format("    crc             : %x%n", crc());
2420             fm.format("    method          : %d%n", method());
2421             fm.close();
2422             return sb.toString();
2423         }
2424     }
2425 
2426     // ZIP directory has two issues:
2427     // (1) ZIP spec does not require the ZIP file to include
2428     //     directory entry
2429     // (2) all entries are not stored/organized in a "tree"
2430     //     structure.
2431     // A possible solution is to build the node tree ourself as
2432     // implemented below.
2433     private IndexNode root;
2434 
2435     // default time stamp for pseudo entries
2436     private long zfsDefaultTimeStamp = System.currentTimeMillis();
2437 
2438     private void removeFromTree(IndexNode inode) {
2439         IndexNode parent = inodes.get(LOOKUPKEY.as(getParent(inode.name)));
2440         IndexNode child = parent.child;
2441         if (child.equals(inode)) {
2442             parent.child = child.sibling;
2443         } else {
2444             IndexNode last = child;
2445             while ((child = child.sibling) != null) {
2446                 if (child.equals(inode)) {
2447                     last.sibling = child.sibling;
2448                     break;
2449                 } else {
2450                     last = child;
2451                 }
2452             }
2453         }


   1 /*
   2  * Copyright (c) 2009, 2018, 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 jdk.nio.zipfs;
  27 
  28 import static java.lang.Boolean.TRUE;
  29 import static jdk.nio.zipfs.ZipConstants.*;
  30 import static jdk.nio.zipfs.ZipUtils.*;
  31 import static java.nio.file.StandardOpenOption.*;
  32 import static java.nio.file.StandardCopyOption.*;
  33 
  34 import java.io.BufferedOutputStream;
  35 import java.io.ByteArrayInputStream;
  36 import java.io.ByteArrayOutputStream;
  37 import java.io.EOFException;

  38 import java.io.FilterOutputStream;
  39 import java.io.IOException;
  40 import java.io.InputStream;
  41 import java.io.OutputStream;
  42 import java.nio.ByteBuffer;
  43 import java.nio.MappedByteBuffer;
  44 import java.nio.channels.FileChannel;
  45 import java.nio.channels.FileLock;
  46 import java.nio.channels.ReadableByteChannel;
  47 import java.nio.channels.SeekableByteChannel;
  48 import java.nio.channels.WritableByteChannel;
  49 import java.nio.file.*;
  50 import java.nio.file.attribute.FileAttribute;
  51 import java.nio.file.attribute.FileTime;
  52 import java.nio.file.attribute.UserPrincipalLookupService;
  53 import java.nio.file.spi.FileSystemProvider;
  54 import java.security.AccessController;
  55 import java.security.PrivilegedAction;
  56 import java.security.PrivilegedActionException;
  57 import java.security.PrivilegedExceptionAction;
  58 import java.util.ArrayList;
  59 import java.util.Arrays;
  60 import java.util.Collections;
  61 import java.util.Formatter;
  62 import java.util.HashSet;
  63 import java.util.Iterator;
  64 import java.util.LinkedHashMap;
  65 import java.util.List;
  66 import java.util.Map;
  67 import java.util.Objects;
  68 import java.util.Set;
  69 import java.util.concurrent.locks.ReadWriteLock;
  70 import java.util.concurrent.locks.ReentrantReadWriteLock;
  71 import java.util.regex.Pattern;
  72 import java.util.zip.CRC32;

  73 import java.util.zip.Deflater;

  74 import java.util.zip.DeflaterOutputStream;
  75 import java.util.zip.Inflater;
  76 import java.util.zip.InflaterInputStream;
  77 import java.util.zip.ZipException;





  78 
  79 /**
  80  * A FileSystem built on a zip file
  81  *
  82  * @author Xueming Shen
  83  */

  84 class ZipFileSystem extends FileSystem {

  85     private final ZipFileSystemProvider provider;
  86     private final Path zfpath;
  87     final ZipCoder zc;
  88     private final ZipPath rootdir;
  89     private boolean readOnly = false;    // readonly file system
  90 
  91     // configurable by env map
  92     private final boolean noExtt;        // see readExtra()
  93     private final boolean useTempFile;   // use a temp file for newOS, default
  94                                          // is to use BAOS for better performance
  95     private static final boolean isWindows = AccessController.doPrivileged(
  96             (PrivilegedAction<Boolean>)() -> System.getProperty("os.name")
  97                                                    .startsWith("Windows"));
  98     private final boolean forceEnd64;
  99     private final int defaultMethod;     // METHOD_STORED if "noCompression=true"
 100                                          // METHOD_DEFLATED otherwise
 101 
 102     ZipFileSystem(ZipFileSystemProvider provider,
 103                   Path zfpath,
 104                   Map<String, ?> env) throws IOException
 105     {
 106         // default encoding for name/comment
 107         String nameEncoding = env.containsKey("encoding") ?
 108                               (String)env.get("encoding") : "UTF-8";
 109         this.noExtt = "false".equals(env.get("zipinfo-time"));
 110         this.useTempFile  = isTrue(env, "useTempFile");
 111         this.forceEnd64 = isTrue(env, "forceZIP64End");
 112         this.defaultMethod = isTrue(env, "noCompression") ? METHOD_STORED: METHOD_DEFLATED;
 113         if (Files.notExists(zfpath)) {
 114             // create a new zip if not exists
 115             if (isTrue(env, "create")) {
 116                 try (OutputStream os = Files.newOutputStream(zfpath, CREATE_NEW, WRITE)) {
 117                     new END().write(os, 0, forceEnd64);
 118                 }
 119             } else {
 120                 throw new FileSystemNotFoundException(zfpath.toString());
 121             }
 122         }
 123         // sm and existence check
 124         zfpath.getFileSystem().provider().checkAccess(zfpath, AccessMode.READ);


 266         return new PathMatcher() {
 267             @Override
 268             public boolean matches(Path path) {
 269                 return pattern.matcher(path.toString()).matches();
 270             }
 271         };
 272     }
 273 
 274     @Override
 275     public void close() throws IOException {
 276         beginWrite();
 277         try {
 278             if (!isOpen)
 279                 return;
 280             isOpen = false;          // set closed
 281         } finally {
 282             endWrite();
 283         }
 284         if (!streams.isEmpty()) {    // unlock and close all remaining streams
 285             Set<InputStream> copy = new HashSet<>(streams);
 286             for (InputStream is : copy)
 287                 is.close();
 288         }
 289         beginWrite();                // lock and sync
 290         try {
 291             AccessController.doPrivileged((PrivilegedExceptionAction<Void>)() -> {
 292                 sync(); return null;
 293             });
 294             ch.close();              // close the ch just in case no update
 295                                      // and sync didn't close the ch
 296         } catch (PrivilegedActionException e) {
 297             throw (IOException)e.getException();
 298         } finally {
 299             endWrite();
 300         }
 301 
 302         synchronized (inflaters) {
 303             for (Inflater inf : inflaters)
 304                 inf.end();
 305         }
 306         synchronized (deflaters) {
 307             for (Deflater def : deflaters)
 308                 def.end();
 309         }
 310 
 311         IOException ioe = null;
 312         synchronized (tmppaths) {
 313             for (Path p : tmppaths) {
 314                 try {
 315                     AccessController.doPrivileged(
 316                         (PrivilegedExceptionAction<Boolean>)() -> Files.deleteIfExists(p));
 317                 } catch (PrivilegedActionException e) {
 318                     IOException x = (IOException)e.getException();
 319                     if (ioe == null)
 320                         ioe = x;
 321                     else
 322                         ioe.addSuppressed(x);
 323                 }
 324             }
 325         }
 326         provider.removeFileSystem(zfpath, this);
 327         if (ioe != null)
 328            throw ioe;
 329     }
 330 
 331     ZipFileAttributes getFileAttributes(byte[] path)
 332         throws IOException
 333     {


 518             if (!hasCopyAttrs)
 519                 u.mtime = u.atime= u.ctime = System.currentTimeMillis();
 520             update(u);
 521             if (deletesrc)
 522                 updateDelete(eSrc);
 523         } finally {
 524             endWrite();
 525         }
 526     }
 527 
 528     // Returns an output stream for writing the contents into the specified
 529     // entry.
 530     OutputStream newOutputStream(byte[] path, OpenOption... options)
 531         throws IOException
 532     {
 533         checkWritable();
 534         boolean hasCreateNew = false;
 535         boolean hasCreate = false;
 536         boolean hasAppend = false;
 537         boolean hasTruncate = false;
 538         for (OpenOption opt : options) {
 539             if (opt == READ)
 540                 throw new IllegalArgumentException("READ not allowed");
 541             if (opt == CREATE_NEW)
 542                 hasCreateNew = true;
 543             if (opt == CREATE)
 544                 hasCreate = true;
 545             if (opt == APPEND)
 546                 hasAppend = true;
 547             if (opt == TRUNCATE_EXISTING)
 548                 hasTruncate = true;
 549         }
 550         if (hasAppend && hasTruncate)
 551             throw new IllegalArgumentException("APPEND + TRUNCATE_EXISTING not allowed");
 552         beginRead();                 // only need a readlock, the "update()" will
 553         try {                        // try to obtain a writelock when the os is
 554             ensureOpen();            // being closed.
 555             Entry e = getEntry(path);
 556             if (e != null) {
 557                 if (e.isDir() || hasCreateNew)
 558                     throw new FileAlreadyExistsException(getString(path));


1474 
1475     private InputStream getInputStream(Entry e)
1476         throws IOException
1477     {
1478         InputStream eis = null;
1479 
1480         if (e.type == Entry.NEW) {
1481             // now bytes & file is uncompressed.
1482             if (e.bytes != null)
1483                 return new ByteArrayInputStream(e.bytes);
1484             else if (e.file != null)
1485                 return Files.newInputStream(e.file);
1486             else
1487                 throw new ZipException("update entry data is missing");
1488         } else if (e.type == Entry.FILECH) {
1489             // FILECH result is un-compressed.
1490             eis = Files.newInputStream(e.file);
1491             // TBD: wrap to hook close()
1492             // streams.add(eis);
1493             return eis;
1494         } else {  // untouched CEN or COPY
1495             eis = new EntryInputStream(e, ch);
1496         }
1497         if (e.method == METHOD_DEFLATED) {
1498             // MORE: Compute good size for inflater stream:
1499             long bufSize = e.size + 2; // Inflater likes a bit of slack
1500             if (bufSize > 65536)
1501                 bufSize = 8192;
1502             final long size = e.size;
1503             eis = new InflaterInputStream(eis, getInflater(), (int)bufSize) {
1504                 private boolean isClosed = false;
1505                 public void close() throws IOException {
1506                     if (!isClosed) {
1507                         releaseInflater(inf);
1508                         this.in.close();
1509                         isClosed = true;
1510                         streams.remove(this);
1511                     }
1512                 }
1513                 // Override fill() method to provide an extra "dummy" byte
1514                 // at the end of the input stream. This is required when


1536                     return avail > (long) Integer.MAX_VALUE ?
1537                         Integer.MAX_VALUE : (int) avail;
1538                 }
1539             };
1540         } else if (e.method == METHOD_STORED) {
1541             // TBD: wrap/ it does not seem necessary
1542         } else {
1543             throw new ZipException("invalid compression method");
1544         }
1545         streams.add(eis);
1546         return eis;
1547     }
1548 
1549     // Inner class implementing the input stream used to read
1550     // a (possibly compressed) zip file entry.
1551     private class EntryInputStream extends InputStream {
1552         private final SeekableByteChannel zfch; // local ref to zipfs's "ch". zipfs.ch might
1553                                           // point to a new channel after sync()
1554         private   long pos;               // current position within entry data
1555         protected long rem;               // number of remaining bytes within entry

1556 
1557         EntryInputStream(Entry e, SeekableByteChannel zfch)
1558             throws IOException
1559         {
1560             this.zfch = zfch;
1561             rem = e.csize;

1562             pos = e.locoff;
1563             if (pos == -1) {
1564                 Entry e2 = getEntry(e.name);
1565                 if (e2 == null) {
1566                     throw new ZipException("invalid loc for entry <" + e.name + ">");
1567                 }
1568                 pos = e2.locoff;
1569             }
1570             pos = -pos;  // lazy initialize the real data offset
1571         }
1572 
1573         public int read(byte b[], int off, int len) throws IOException {
1574             ensureOpen();
1575             initDataPos();
1576             if (rem == 0) {
1577                 return -1;
1578             }
1579             if (len <= 0) {
1580                 return 0;
1581             }


1608                 return -1;
1609             }
1610         }
1611 
1612         public long skip(long n) throws IOException {
1613             ensureOpen();
1614             if (n > rem)
1615                 n = rem;
1616             pos += n;
1617             rem -= n;
1618             if (rem == 0) {
1619                 close();
1620             }
1621             return n;
1622         }
1623 
1624         public int available() {
1625             return rem > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) rem;
1626         }
1627 




1628         public void close() {
1629             rem = 0;
1630             streams.remove(this);
1631         }
1632 
1633         private void initDataPos() throws IOException {
1634             if (pos <= 0) {
1635                 pos = -pos + locpos;
1636                 byte[] buf = new byte[LOCHDR];
1637                 if (readFullyAt(buf, 0, buf.length, pos) != LOCHDR) {
1638                     throw new ZipException("invalid loc " + pos + " for entry reading");
1639                 }
1640                 pos += LOCHDR + LOCNAM(buf) + LOCEXT(buf);
1641             }
1642         }
1643     }
1644 
1645     static void zerror(String msg) throws ZipException {
1646         throw new ZipException(msg);
1647     }


1663                 return new Inflater(true);
1664             }
1665         }
1666     }
1667 
1668     // Releases the specified inflater to the list of available inflaters.
1669     private void releaseInflater(Inflater inf) {
1670         synchronized (inflaters) {
1671             if (inflaters.size() < MAX_FLATER) {
1672                 inf.reset();
1673                 inflaters.add(inf);
1674             } else {
1675                 inf.end();
1676             }
1677         }
1678     }
1679 
1680     // List of available Deflater objects for compression
1681     private final List<Deflater> deflaters = new ArrayList<>();
1682 
1683     // Gets a deflater from the list of available deflaters or allocates
1684     // a new one.
1685     private Deflater getDeflater() {
1686         synchronized (deflaters) {
1687             int size = deflaters.size();
1688             if (size > 0) {
1689                 Deflater def = deflaters.remove(size - 1);
1690                 return def;
1691             } else {
1692                 return new Deflater(Deflater.DEFAULT_COMPRESSION, true);
1693             }
1694         }
1695     }
1696 












1697     // End of central directory record
1698     static class END {
1699         // these 2 fields are not used by anyone and write() uses "0"
1700         // int  disknum;
1701         // int  sdisknum;
1702         int  endsub;     // endsub
1703         int  centot;     // 4 bytes
1704         long cenlen;     // 4 bytes
1705         long cenoff;     // 4 bytes
1706         int  comlen;     // comment length
1707         byte[] comment;
1708 
1709         /* members of Zip64 end of central directory locator */
1710         // int diskNum;
1711         long endpos;
1712         // int disktot;
1713 
1714         void write(OutputStream os, long offset, boolean forceEnd64) throws IOException {
1715             boolean hasZip64 = forceEnd64; // false;
1716             long xlen = cenlen;


1964             attrsEx     = CENATX(cen, pos);
1965             */
1966             locoff      = CENOFF(cen, pos);
1967             pos += CENHDR;
1968             this.name = inode.name;
1969             this.isdir = inode.isdir;
1970             this.hashcode = inode.hashcode;
1971 
1972             pos += nlen;
1973             if (elen > 0) {
1974                 extra = Arrays.copyOfRange(cen, pos, pos + elen);
1975                 pos += elen;
1976                 readExtra(zipfs);
1977             }
1978             if (clen > 0) {
1979                 comment = Arrays.copyOfRange(cen, pos, pos + clen);
1980             }
1981             return this;
1982         }
1983 
1984         int writeCEN(OutputStream os) throws IOException {


1985             int version0 = version();
1986             long csize0  = csize;
1987             long size0   = size;
1988             long locoff0 = locoff;
1989             int elen64   = 0;                // extra for ZIP64
1990             int elenNTFS = 0;                // extra for NTFS (a/c/mtime)
1991             int elenEXTT = 0;                // extra for Extended Timestamp
1992             boolean foundExtraTime = false;  // if time stamp NTFS, EXTT present
1993 
1994             byte[] zname = isdir ? toDirectoryPath(name) : name;
1995 
1996             // confirm size/length
1997             int nlen = (zname != null) ? zname.length - 1 : 0;  // name has [0] as "slash"
1998             int elen = (extra != null) ? extra.length : 0;
1999             int eoff = 0;
2000             int clen = (comment != null) ? comment.length : 0;
2001             if (csize >= ZIP64_MINVAL) {
2002                 csize0 = ZIP64_MINVAL;
2003                 elen64 += 8;                 // csize(8)
2004             }


2079             if (elenEXTT != 0) {
2080                 writeShort(os, EXTID_EXTT);
2081                 writeShort(os, elenEXTT - 4);
2082                 if (ctime == -1)
2083                     os.write(0x3);          // mtime and atime
2084                 else
2085                     os.write(0x7);          // mtime, atime and ctime
2086                 writeInt(os, javaToUnixTime(mtime));
2087             }
2088             if (extra != null)              // whatever not recognized
2089                 writeBytes(os, extra);
2090             if (comment != null)            //TBD: 0, Math.min(commentBytes.length, 0xffff));
2091                 writeBytes(os, comment);
2092             return CENHDR + nlen + elen + clen + elen64 + elenNTFS + elenEXTT;
2093         }
2094 
2095         ///////////////////// LOC //////////////////////
2096 
2097         int writeLOC(OutputStream os) throws IOException {
2098             writeInt(os, LOCSIG);               // LOC header signature


2099             byte[] zname = isdir ? toDirectoryPath(name) : name;
2100             int nlen = (zname != null) ? zname.length - 1 : 0; // [0] is slash
2101             int elen = (extra != null) ? extra.length : 0;
2102             boolean foundExtraTime = false;     // if extra timestamp present
2103             int eoff = 0;
2104             int elen64 = 0;
2105             int elenEXTT = 0;
2106             int elenNTFS = 0;
2107             if ((flag & FLAG_DATADESCR) != 0) {
2108                 writeShort(os, version());      // version needed to extract
2109                 writeShort(os, flag);           // general purpose bit flag
2110                 writeShort(os, method);         // compression method
2111                 // last modification time
2112                 writeInt(os, (int)javaToDosTime(mtime));
2113                 // store size, uncompressed size, and crc-32 in data descriptor
2114                 // immediately following compressed entry data
2115                 writeInt(os, 0);
2116                 writeInt(os, 0);
2117                 writeInt(os, 0);
2118             } else {


2405             fm.format("    isDirectory     : %b%n", isDirectory());
2406             fm.format("    isSymbolicLink  : %b%n", isSymbolicLink());
2407             fm.format("    isOther         : %b%n", isOther());
2408             fm.format("    fileKey         : %s%n", fileKey());
2409             fm.format("    size            : %d%n", size());
2410             fm.format("    compressedSize  : %d%n", compressedSize());
2411             fm.format("    crc             : %x%n", crc());
2412             fm.format("    method          : %d%n", method());
2413             fm.close();
2414             return sb.toString();
2415         }
2416     }
2417 
2418     // ZIP directory has two issues:
2419     // (1) ZIP spec does not require the ZIP file to include
2420     //     directory entry
2421     // (2) all entries are not stored/organized in a "tree"
2422     //     structure.
2423     // A possible solution is to build the node tree ourself as
2424     // implemented below.

2425 
2426     // default time stamp for pseudo entries
2427     private long zfsDefaultTimeStamp = System.currentTimeMillis();
2428 
2429     private void removeFromTree(IndexNode inode) {
2430         IndexNode parent = inodes.get(LOOKUPKEY.as(getParent(inode.name)));
2431         IndexNode child = parent.child;
2432         if (child.equals(inode)) {
2433             parent.child = child.sibling;
2434         } else {
2435             IndexNode last = child;
2436             while ((child = child.sibling) != null) {
2437                 if (child.equals(inode)) {
2438                     last.sibling = child.sibling;
2439                     break;
2440                 } else {
2441                     last = child;
2442                 }
2443             }
2444         }


< prev index next >