< prev index next >

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

Print this page
rev 53038 : 8215472: (zipfs) 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));


1438             this.e =  Objects.requireNonNull(e, "Zip entry is null");
1439             this.crc = new CRC32();
1440         }
1441 
1442         @Override
1443         public void write(byte b[], int off, int len)
1444                 throws IOException {
1445             super.write(b, off, len);
1446             crc.update(b, off, len);
1447         }
1448 
1449         @Override
1450         public void close() throws IOException {
1451             if (isClosed)
1452                 return;
1453             isClosed = true;
1454             finish();
1455             e.size  = def.getBytesRead();
1456             e.csize = def.getBytesWritten();
1457             e.crc = crc.getValue();

1458         }
1459     }
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 {


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             }


2084             }
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 {
2127                 if (csize >= ZIP64_MINVAL || size >= ZIP64_MINVAL) {
2128                     elen64 = 20;    //headid(2) + size(2) + size(8) + csize(8)
2129                     writeShort(os, 45);         // ver 4.5 for zip64
2130                 } else {
2131                     writeShort(os, version());  // version needed to extract
2132                 }
2133                 writeShort(os, flag);           // general purpose bit flag
2134                 writeShort(os, method);         // compression method
2135                                                 // last modification time
2136                 writeInt(os, (int)javaToDosTime(mtime));
2137                 writeInt(os, crc);              // crc-32
2138                 if (elen64 != 0) {
2139                     writeInt(os, ZIP64_MINVAL);
2140                     writeInt(os, ZIP64_MINVAL);
2141                 } else {
2142                     writeInt(os, csize);        // compressed size
2143                     writeInt(os, size);         // uncompressed size
2144                 }
2145             }
2146             while (eoff + 4 < elen) {
2147                 int tag = SH(extra, eoff);
2148                 int sz = SH(extra, eoff + 2);
2149                 if (tag == EXTID_EXTT || tag == EXTID_NTFS) {
2150                     foundExtraTime = true;
2151                 }


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 java.io.BufferedOutputStream;
  29 import java.io.ByteArrayInputStream;
  30 import java.io.ByteArrayOutputStream;
  31 import java.io.EOFException;

  32 import java.io.FilterOutputStream;
  33 import java.io.IOException;
  34 import java.io.InputStream;
  35 import java.io.OutputStream;
  36 import java.nio.ByteBuffer;
  37 import java.nio.MappedByteBuffer;
  38 import java.nio.channels.FileChannel;
  39 import java.nio.channels.FileLock;
  40 import java.nio.channels.ReadableByteChannel;
  41 import java.nio.channels.SeekableByteChannel;
  42 import java.nio.channels.WritableByteChannel;
  43 import java.nio.file.*;
  44 import java.nio.file.attribute.FileAttribute;
  45 import java.nio.file.attribute.FileTime;
  46 import java.nio.file.attribute.UserPrincipalLookupService;
  47 import java.nio.file.spi.FileSystemProvider;
  48 import java.security.AccessController;
  49 import java.security.PrivilegedAction;
  50 import java.security.PrivilegedActionException;
  51 import java.security.PrivilegedExceptionAction;
  52 import java.util.*;
  53 import java.util.concurrent.locks.ReadWriteLock;
  54 import java.util.concurrent.locks.ReentrantReadWriteLock;
  55 import java.util.regex.Pattern;
  56 import java.util.zip.CRC32;

  57 import java.util.zip.Deflater;

  58 import java.util.zip.DeflaterOutputStream;
  59 import java.util.zip.Inflater;
  60 import java.util.zip.InflaterInputStream;
  61 import java.util.zip.ZipException;
  62 
  63 import static java.lang.Boolean.TRUE;
  64 import static java.nio.file.StandardCopyOption.COPY_ATTRIBUTES;
  65 import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
  66 import static java.nio.file.StandardOpenOption.APPEND;
  67 import static java.nio.file.StandardOpenOption.CREATE;
  68 import static java.nio.file.StandardOpenOption.CREATE_NEW;
  69 import static java.nio.file.StandardOpenOption.READ;
  70 import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING;
  71 import static java.nio.file.StandardOpenOption.WRITE;
  72 import static jdk.nio.zipfs.ZipConstants.*;
  73 import static jdk.nio.zipfs.ZipUtils.*;


  74 
  75 /**
  76  * A FileSystem built on a zip file
  77  *
  78  * @author Xueming Shen
  79  */

  80 class ZipFileSystem extends FileSystem {

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


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


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


1448             this.e =  Objects.requireNonNull(e, "Zip entry is null");
1449             this.crc = new CRC32();
1450         }
1451 
1452         @Override
1453         public void write(byte b[], int off, int len)
1454                 throws IOException {
1455             super.write(b, off, len);
1456             crc.update(b, off, len);
1457         }
1458 
1459         @Override
1460         public void close() throws IOException {
1461             if (isClosed)
1462                 return;
1463             isClosed = true;
1464             finish();
1465             e.size  = def.getBytesRead();
1466             e.csize = def.getBytesWritten();
1467             e.crc = crc.getValue();
1468             releaseDeflater(def);
1469         }
1470     }
1471 
1472     private InputStream getInputStream(Entry e)
1473         throws IOException
1474     {
1475         InputStream eis = null;
1476 
1477         if (e.type == Entry.NEW) {
1478             // now bytes & file is uncompressed.
1479             if (e.bytes != null)
1480                 return new ByteArrayInputStream(e.bytes);
1481             else if (e.file != null)
1482                 return Files.newInputStream(e.file);
1483             else
1484                 throw new ZipException("update entry data is missing");
1485         } else if (e.type == Entry.FILECH) {
1486             // FILECH result is un-compressed.
1487             eis = Files.newInputStream(e.file);
1488             // TBD: wrap to hook close()
1489             // streams.add(eis);
1490             return eis;
1491         } else {  // untouched CEN or COPY
1492             eis = new EntryInputStream(e, ch);
1493         }
1494         if (e.method == METHOD_DEFLATED) {
1495             // MORE: Compute good size for inflater stream:
1496             long bufSize = e.size + 2; // Inflater likes a bit of slack
1497             if (bufSize > 65536)
1498                 bufSize = 8192;
1499             final long size = e.size;
1500             eis = new InflaterInputStream(eis, getInflater(), (int)bufSize) {
1501                 private boolean isClosed = false;
1502                 public void close() throws IOException {
1503                     if (!isClosed) {
1504                         releaseInflater(inf);
1505                         this.in.close();
1506                         isClosed = true;
1507                         streams.remove(this);
1508                     }
1509                 }
1510                 // Override fill() method to provide an extra "dummy" byte
1511                 // at the end of the input stream. This is required when


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

1553 
1554         EntryInputStream(Entry e, SeekableByteChannel zfch)
1555             throws IOException
1556         {
1557             this.zfch = zfch;
1558             rem = e.csize;

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


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




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


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


1973             attrsEx     = CENATX(cen, pos);
1974             */
1975             locoff      = CENOFF(cen, pos);
1976             pos += CENHDR;
1977             this.name = inode.name;
1978             this.isdir = inode.isdir;
1979             this.hashcode = inode.hashcode;
1980 
1981             pos += nlen;
1982             if (elen > 0) {
1983                 extra = Arrays.copyOfRange(cen, pos, pos + elen);
1984                 pos += elen;
1985                 readExtra(zipfs);
1986             }
1987             if (clen > 0) {
1988                 comment = Arrays.copyOfRange(cen, pos, pos + clen);
1989             }
1990             return this;
1991         }
1992 
1993         int writeCEN(OutputStream os) throws IOException {


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


2087             }
2088             if (elenEXTT != 0) {
2089                 writeShort(os, EXTID_EXTT);
2090                 writeShort(os, elenEXTT - 4);
2091                 if (ctime == -1)
2092                     os.write(0x3);          // mtime and atime
2093                 else
2094                     os.write(0x7);          // mtime, atime and ctime
2095                 writeInt(os, javaToUnixTime(mtime));
2096             }
2097             if (extra != null)              // whatever not recognized
2098                 writeBytes(os, extra);
2099             if (comment != null)            //TBD: 0, Math.min(commentBytes.length, 0xffff));
2100                 writeBytes(os, comment);
2101             return CENHDR + nlen + elen + clen + elen64 + elenNTFS + elenEXTT;
2102         }
2103 
2104         ///////////////////// LOC //////////////////////
2105 
2106         int writeLOC(OutputStream os) throws IOException {
2107             int version0 = version();


2108             byte[] zname = isdir ? toDirectoryPath(name) : name;
2109             int nlen = (zname != null) ? zname.length - 1 : 0; // [0] is slash
2110             int elen = (extra != null) ? extra.length : 0;
2111             boolean foundExtraTime = false;     // if extra timestamp present
2112             int eoff = 0;
2113             int elen64 = 0;
2114             int elenEXTT = 0;
2115             int elenNTFS = 0;
2116             writeInt(os, LOCSIG);               // LOC header signature
2117             if ((flag & FLAG_DATADESCR) != 0) {
2118                 writeShort(os, version0);        // version needed to extract
2119                 writeShort(os, flag);           // general purpose bit flag
2120                 writeShort(os, method);         // compression method
2121                 // last modification time
2122                 writeInt(os, (int)javaToDosTime(mtime));
2123                 // store size, uncompressed size, and crc-32 in data descriptor
2124                 // immediately following compressed entry data
2125                 writeInt(os, 0);
2126                 writeInt(os, 0);
2127                 writeInt(os, 0);
2128             } else {
2129                 if (csize >= ZIP64_MINVAL || size >= ZIP64_MINVAL) {
2130                     elen64 = 20;    //headid(2) + size(2) + size(8) + csize(8)
2131                     writeShort(os, 45);         // ver 4.5 for zip64
2132                 } else {
2133                     writeShort(os, version0);    // version needed to extract
2134                 }
2135                 writeShort(os, flag);           // general purpose bit flag
2136                 writeShort(os, method);         // compression method
2137                                                 // last modification time
2138                 writeInt(os, (int)javaToDosTime(mtime));
2139                 writeInt(os, crc);              // crc-32
2140                 if (elen64 != 0) {
2141                     writeInt(os, ZIP64_MINVAL);
2142                     writeInt(os, ZIP64_MINVAL);
2143                 } else {
2144                     writeInt(os, csize);        // compressed size
2145                     writeInt(os, size);         // uncompressed size
2146                 }
2147             }
2148             while (eoff + 4 < elen) {
2149                 int tag = SH(extra, eoff);
2150                 int sz = SH(extra, eoff + 2);
2151                 if (tag == EXTID_EXTT || tag == EXTID_NTFS) {
2152                     foundExtraTime = true;
2153                 }


2415             fm.format("    isDirectory     : %b%n", isDirectory());
2416             fm.format("    isSymbolicLink  : %b%n", isSymbolicLink());
2417             fm.format("    isOther         : %b%n", isOther());
2418             fm.format("    fileKey         : %s%n", fileKey());
2419             fm.format("    size            : %d%n", size());
2420             fm.format("    compressedSize  : %d%n", compressedSize());
2421             fm.format("    crc             : %x%n", crc());
2422             fm.format("    method          : %d%n", method());
2423             fm.close();
2424             return sb.toString();
2425         }
2426     }
2427 
2428     // ZIP directory has two issues:
2429     // (1) ZIP spec does not require the ZIP file to include
2430     //     directory entry
2431     // (2) all entries are not stored/organized in a "tree"
2432     //     structure.
2433     // A possible solution is to build the node tree ourself as
2434     // implemented below.

2435 
2436     // default time stamp for pseudo entries
2437     private long zfsDefaultTimeStamp = System.currentTimeMillis();
2438 
2439     private void removeFromTree(IndexNode inode) {
2440         IndexNode parent = inodes.get(LOOKUPKEY.as(getParent(inode.name)));
2441         IndexNode child = parent.child;
2442         if (child.equals(inode)) {
2443             parent.child = child.sibling;
2444         } else {
2445             IndexNode last = child;
2446             while ((child = child.sibling) != null) {
2447                 if (child.equals(inode)) {
2448                     last.sibling = child.sibling;
2449                     break;
2450                 } else {
2451                     last = child;
2452                 }
2453             }
2454         }


< prev index next >