< prev index next >

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

Print this page
rev 52310 : 8213031: (zipfs) Add support for POSIX file permissions
   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


 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     {


 427                     list.add(zpath);
 428                 child = child.sibling;
 429             }
 430             return list.iterator();
 431         } finally {
 432             endWrite();
 433         }
 434     }
 435 
 436     void createDirectory(byte[] dir, FileAttribute<?>... attrs)
 437         throws IOException
 438     {
 439         checkWritable();
 440         //  dir = toDirectoryPath(dir);
 441         beginWrite();
 442         try {
 443             ensureOpen();
 444             if (dir.length == 0 || exists(dir))  // root dir, or exiting dir
 445                 throw new FileAlreadyExistsException(getString(dir));
 446             checkParents(dir);
 447             Entry e = new Entry(dir, Entry.NEW, true, METHOD_STORED);
 448             update(e);
 449         } finally {
 450             endWrite();
 451         }
 452     }
 453 
 454     void copyFile(boolean deletesrc, byte[]src, byte[] dst, CopyOption... options)
 455         throws IOException
 456     {
 457         checkWritable();
 458         if (Arrays.equals(src, dst))
 459             return;    // do nothing, src and dst are the same
 460 
 461         beginWrite();
 462         try {
 463             ensureOpen();
 464             Entry eSrc = getEntry(src);  // ensureOpen checked
 465 
 466             if (eSrc == null)
 467                 throw new NoSuchFileException(getString(src));


 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));


 649                     SeekableByteChannel sbc =
 650                             new EntryOutputChannel(new Entry(e, Entry.NEW));
 651                     if (options.contains(APPEND)) {
 652                         try (InputStream is = getInputStream(e)) {  // copyover
 653                             byte[] buf = new byte[8192];
 654                             ByteBuffer bb = ByteBuffer.wrap(buf);
 655                             int n;
 656                             while ((n = is.read(buf)) != -1) {
 657                                 bb.position(0);
 658                                 bb.limit(n);
 659                                 sbc.write(bb);
 660                             }
 661                         }
 662                     }
 663                     return sbc;
 664                 }
 665                 if (!options.contains(CREATE) && !options.contains(CREATE_NEW))
 666                     throw new NoSuchFileException(getString(path));
 667                 checkParents(path);
 668                 return new EntryOutputChannel(
 669                     new Entry(path, Entry.NEW, false, getCompressMethod(attrs)));
 670 
 671             } finally {
 672                 endRead();
 673             }
 674         } else {
 675             beginRead();
 676             try {
 677                 ensureOpen();
 678                 Entry e = getEntry(path);
 679                 if (e == null || e.isDir())
 680                     throw new NoSuchFileException(getString(path));
 681                 try (InputStream is = getInputStream(e)) {
 682                     // TBD: if (e.size < NNNNN);
 683                     return new ByteArrayChannel(is.readAllBytes(), true);
 684                 }
 685             } finally {
 686                 endRead();
 687             }
 688         }
 689     }


 714                     }
 715                 } else {
 716                     if (options.contains(StandardOpenOption.CREATE_NEW)) {
 717                         throw new FileAlreadyExistsException(getString(path));
 718                     }
 719                     if (e.isDir())
 720                         throw new FileAlreadyExistsException("directory <"
 721                             + getString(path) + "> exists");
 722                 }
 723                 options = new HashSet<>(options);
 724                 options.remove(StandardOpenOption.CREATE_NEW); // for tmpfile
 725             } else if (e == null || e.isDir()) {
 726                 throw new NoSuchFileException(getString(path));
 727             }
 728 
 729             final boolean isFCH = (e != null && e.type == Entry.FILECH);
 730             final Path tmpfile = isFCH ? e.file : getTempPathForEntry(path);
 731             final FileChannel fch = tmpfile.getFileSystem()
 732                                            .provider()
 733                                            .newFileChannel(tmpfile, options, attrs);
 734             final Entry u = isFCH ? e : new Entry(path, tmpfile, Entry.FILECH);
 735             if (forWrite) {
 736                 u.flag = FLAG_DATADESCR;
 737                 u.method = getCompressMethod(attrs);
 738             }
 739             // is there a better way to hook into the FileChannel's close method?
 740             return new FileChannel() {
 741                 public int write(ByteBuffer src) throws IOException {
 742                     return fch.write(src);
 743                 }
 744                 public long write(ByteBuffer[] srcs, int offset, int length)
 745                     throws IOException
 746                 {
 747                     return fch.write(srcs, offset, length);
 748                 }
 749                 public long position() throws IOException {
 750                     return fch.position();
 751                 }
 752                 public FileChannel position(long newPosition)
 753                     throws IOException
 754                 {


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;


1848 
1849         IndexNode() {}
1850         IndexNode sibling;
1851         IndexNode child;  // 1st child
1852     }
1853 
1854     static class Entry extends IndexNode implements ZipFileAttributes {
1855 
1856         static final int CEN    = 1;  // entry read from cen
1857         static final int NEW    = 2;  // updated contents in bytes or file
1858         static final int FILECH = 3;  // fch update in "file"
1859         static final int COPY   = 4;  // copy of a CEN entry
1860 
1861         byte[] bytes;                 // updated content bytes
1862         Path   file;                  // use tmp file to store bytes;
1863         int    type = CEN;            // default is the entry read from cen
1864 
1865         // entry attributes
1866         int    version;
1867         int    flag;

1868         int    method = -1;    // compression method
1869         long   mtime  = -1;    // last modification time (in DOS time)
1870         long   atime  = -1;    // last access time
1871         long   ctime  = -1;    // create time
1872         long   crc    = -1;    // crc-32 of entry data
1873         long   csize  = -1;    // compressed size of entry data
1874         long   size   = -1;    // uncompressed size of entry data
1875         byte[] extra;
1876 
1877         // cen
1878 
1879         // these fields are not used by anyone and writeCEN uses "0"
1880         // int    versionMade;
1881         // int    disk;
1882         // int    attrs;
1883         // long   attrsEx;
1884         long   locoff;
1885         byte[] comment;
1886 
1887         Entry() {}
1888 
1889         Entry(byte[] name, boolean isdir, int method) {
1890             name(name);
1891             this.isdir = isdir;
1892             this.mtime  = this.ctime = this.atime = System.currentTimeMillis();
1893             this.crc    = 0;
1894             this.size   = 0;
1895             this.csize  = 0;
1896             this.method = method;
1897         }
1898 
1899         Entry(byte[] name, int type, boolean isdir, int method) {

1900             this(name, isdir, method);
1901             this.type = type;






1902         }
1903 
1904         Entry (Entry e, int type) {
1905             name(e.name);
1906             this.isdir     = e.isdir;
1907             this.version   = e.version;
1908             this.ctime     = e.ctime;
1909             this.atime     = e.atime;
1910             this.mtime     = e.mtime;
1911             this.crc       = e.crc;
1912             this.size      = e.size;
1913             this.csize     = e.csize;
1914             this.method    = e.method;
1915             this.extra     = e.extra;
1916             /*
1917             this.versionMade = e.versionMade;
1918             this.disk      = e.disk;
1919             this.attrs     = e.attrs;
1920             this.attrsEx   = e.attrsEx;
1921             */
1922             this.locoff    = e.locoff;
1923             this.comment   = e.comment;

1924             this.type      = type;
1925         }
1926 
1927         Entry (byte[] name, Path file, int type) {

1928             this(name, type, false, METHOD_STORED);
1929             this.file = file;






1930         }
1931 
1932         int version() throws ZipException {



1933             if (method == METHOD_DEFLATED)
1934                 return 20;
1935             else if (method == METHOD_STORED)
1936                 return 10;
1937             throw new ZipException("unsupported compression method");
1938         }
1939 









1940         ///////////////////// CEN //////////////////////
1941         static Entry readCEN(ZipFileSystem zipfs, IndexNode inode)
1942             throws IOException
1943         {
1944             return new Entry().cen(zipfs, inode);
1945         }
1946 
1947         private Entry cen(ZipFileSystem zipfs, IndexNode inode)
1948             throws IOException
1949         {
1950             byte[] cen = zipfs.cen;
1951             int pos = inode.pos;
1952             if (!cenSigAt(cen, pos))
1953                 zerror("invalid CEN header (bad signature)");
1954             version     = CENVER(cen, pos);
1955             flag        = CENFLG(cen, pos);
1956             method      = CENHOW(cen, pos);
1957             mtime       = dosToJavaTime(CENTIM(cen, pos));
1958             crc         = CENCRC(cen, pos);
1959             csize       = CENSIZ(cen, pos);
1960             size        = CENLEN(cen, pos);
1961             int nlen    = CENNAM(cen, pos);
1962             int elen    = CENEXT(cen, pos);
1963             int clen    = CENCOM(cen, pos);
1964             /*
1965             versionMade = CENVEM(cen, pos);
1966             disk        = CENDSK(cen, pos);
1967             attrs       = CENATT(cen, pos);
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             }
2011             if (size >= ZIP64_MINVAL) {
2012                 size0 = ZIP64_MINVAL;        // size(8)
2013                 elen64 += 8;
2014             }
2015             if (locoff >= ZIP64_MINVAL) {
2016                 locoff0 = ZIP64_MINVAL;
2017                 elen64 += 8;                 // offset(8)
2018             }
2019             if (elen64 != 0) {
2020                 elen64 += 4;                 // header and data sz 4 bytes
2021             }


2022             while (eoff + 4 < elen) {
2023                 int tag = SH(extra, eoff);
2024                 int sz = SH(extra, eoff + 2);
2025                 if (tag == EXTID_EXTT || tag == EXTID_NTFS) {
2026                     foundExtraTime = true;
2027                 }
2028                 eoff += (4 + sz);
2029             }
2030             if (!foundExtraTime) {
2031                 if (isWindows) {             // use NTFS
2032                     elenNTFS = 36;           // total 36 bytes
2033                 } else {                     // Extended Timestamp otherwise
2034                     elenEXTT = 9;            // only mtime in cen
2035                 }
2036             }
2037             writeInt(os, CENSIG);            // CEN header signature
2038             if (elen64 != 0) {
2039                 writeShort(os, 45);          // ver 4.5 for zip64
2040                 writeShort(os, 45);
2041             } else {
2042                 writeShort(os, version0);    // version made by
2043                 writeShort(os, version0);    // version needed to extract
2044             }
2045             writeShort(os, flag);            // general purpose bit flag
2046             writeShort(os, method);          // compression method
2047                                              // last modification time
2048             writeInt(os, (int)javaToDosTime(mtime));
2049             writeInt(os, crc);               // crc-32
2050             writeInt(os, csize0);            // compressed size
2051             writeInt(os, size0);             // uncompressed size
2052             writeShort(os, nlen);
2053             writeShort(os, elen + elen64 + elenNTFS + elenEXTT);
2054 
2055             if (comment != null) {
2056                 writeShort(os, Math.min(clen, 0xffff));
2057             } else {
2058                 writeShort(os, 0);
2059             }
2060             writeShort(os, 0);              // starting disk number
2061             writeShort(os, 0);              // internal file attributes (unused)
2062             writeInt(os, 0);                // external file attributes (unused)


2063             writeInt(os, locoff0);          // relative offset of local header
2064             writeBytes(os, zname, 1, nlen);
2065             if (elen64 != 0) {
2066                 writeShort(os, EXTID_ZIP64);// Zip64 extra
2067                 writeShort(os, elen64 - 4); // size of "this" extra block
2068                 if (size0 == ZIP64_MINVAL)
2069                     writeLong(os, size);
2070                 if (csize0 == ZIP64_MINVAL)
2071                     writeLong(os, csize);
2072                 if (locoff0 == ZIP64_MINVAL)
2073                     writeLong(os, locoff);
2074             }
2075             if (elenNTFS != 0) {
2076                 writeShort(os, EXTID_NTFS);
2077                 writeShort(os, elenNTFS - 4);
2078                 writeInt(os, 0);            // reserved
2079                 writeShort(os, 0x0001);     // NTFS attr tag
2080                 writeShort(os, 24);
2081                 writeLong(os, javaToWinTime(mtime));
2082                 writeLong(os, javaToWinTime(atime));
2083                 writeLong(os, javaToWinTime(ctime));
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                 }
2152                 eoff += (4 + sz);
2153             }
2154             if (!foundExtraTime) {
2155                 if (isWindows) {
2156                     elenNTFS = 36;              // NTFS, total 36 bytes
2157                 } else {                        // on unix use "ext time"
2158                     elenEXTT = 9;
2159                     if (atime != -1)
2160                         elenEXTT += 4;
2161                     if (ctime != -1)
2162                         elenEXTT += 4;
2163                 }
2164             }
2165             writeShort(os, nlen);
2166             writeShort(os, elen + elen64 + elenNTFS + elenEXTT);
2167             writeBytes(os, zname, 1, nlen);
2168             if (elen64 != 0) {
2169                 writeShort(os, EXTID_ZIP64);
2170                 writeShort(os, 16);
2171                 writeLong(os, size);
2172                 writeLong(os, csize);
2173             }
2174             if (elenNTFS != 0) {
2175                 writeShort(os, EXTID_NTFS);
2176                 writeShort(os, elenNTFS - 4);
2177                 writeInt(os, 0);            // reserved
2178                 writeShort(os, 0x0001);     // NTFS attr tag
2179                 writeShort(os, 24);
2180                 writeLong(os, javaToWinTime(mtime));
2181                 writeLong(os, javaToWinTime(atime));
2182                 writeLong(os, javaToWinTime(ctime));
2183             }
2184             if (elenEXTT != 0) {
2185                 writeShort(os, EXTID_EXTT);
2186                 writeShort(os, elenEXTT - 4);// size for the folowing data block
2187                 int fbyte = 0x1;
2188                 if (atime != -1)           // mtime and atime


2401                 return Arrays.copyOf(comment, comment.length);
2402             return null;
2403         }
2404 
2405         public String toString() {
2406             StringBuilder sb = new StringBuilder(1024);
2407             Formatter fm = new Formatter(sb);
2408             fm.format("    name            : %s%n", new String(name));
2409             fm.format("    creationTime    : %tc%n", creationTime().toMillis());
2410             fm.format("    lastAccessTime  : %tc%n", lastAccessTime().toMillis());
2411             fm.format("    lastModifiedTime: %tc%n", lastModifiedTime().toMillis());
2412             fm.format("    isRegularFile   : %b%n", isRegularFile());
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.GroupPrincipal;
  53 import java.nio.file.attribute.PosixFilePermission;
  54 import java.nio.file.attribute.UserPrincipal;
  55 import java.nio.file.attribute.UserPrincipalLookupService;
  56 import java.nio.file.spi.FileSystemProvider;
  57 import java.security.AccessController;
  58 import java.security.PrivilegedAction;
  59 import java.security.PrivilegedActionException;
  60 import java.security.PrivilegedExceptionAction;
  61 import java.util.ArrayList;
  62 import java.util.Arrays;
  63 import java.util.Collections;
  64 import java.util.Formatter;
  65 import java.util.HashSet;
  66 import java.util.Iterator;
  67 import java.util.LinkedHashMap;
  68 import java.util.List;
  69 import java.util.Map;
  70 import java.util.Objects;
  71 import java.util.Set;
  72 import java.util.concurrent.locks.ReadWriteLock;
  73 import java.util.concurrent.locks.ReentrantReadWriteLock;
  74 import java.util.regex.Pattern;
  75 import java.util.zip.CRC32;

  76 import java.util.zip.Deflater;

  77 import java.util.zip.DeflaterOutputStream;
  78 import java.util.zip.Inflater;
  79 import java.util.zip.InflaterInputStream;
  80 import java.util.zip.ZipException;





  81 
  82 /**
  83  * A FileSystem built on a zip file
  84  *
  85  * @author Xueming Shen
  86  */

  87 class ZipFileSystem extends FileSystem {

  88     private final ZipFileSystemProvider provider;
  89     private final Path zfpath;
  90     final ZipCoder zc;
  91     private final ZipPath rootdir;
  92     private boolean readOnly = false;    // readonly file system
  93 
  94     // configurable by env map
  95     private final boolean noExtt;        // see readExtra()
  96     private final boolean useTempFile;   // use a temp file for newOS, default
  97                                          // is to use BAOS for better performance
  98     private static final boolean isWindows = AccessController.doPrivileged(
  99             (PrivilegedAction<Boolean>) () -> System.getProperty("os.name")
 100                                                     .startsWith("Windows"));
 101     private final boolean forceEnd64;
 102     private final int defaultMethod;     // METHOD_STORED if "noCompression=true"
 103                                          // METHOD_DEFLATED otherwise
 104 
 105     ZipFileSystem(ZipFileSystemProvider provider,
 106                   Path zfpath,
 107                   Map<String, ?> env)  throws IOException


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


 444                     list.add(zpath);
 445                 child = child.sibling;
 446             }
 447             return list.iterator();
 448         } finally {
 449             endWrite();
 450         }
 451     }
 452 
 453     void createDirectory(byte[] dir, FileAttribute<?>... attrs)
 454         throws IOException
 455     {
 456         checkWritable();
 457         //  dir = toDirectoryPath(dir);
 458         beginWrite();
 459         try {
 460             ensureOpen();
 461             if (dir.length == 0 || exists(dir))  // root dir, or exiting dir
 462                 throw new FileAlreadyExistsException(getString(dir));
 463             checkParents(dir);
 464             Entry e = new Entry(dir, Entry.NEW, true, METHOD_STORED, attrs);
 465             update(e);
 466         } finally {
 467             endWrite();
 468         }
 469     }
 470 
 471     void copyFile(boolean deletesrc, byte[]src, byte[] dst, CopyOption... options)
 472         throws IOException
 473     {
 474         checkWritable();
 475         if (Arrays.equals(src, dst))
 476             return;    // do nothing, src and dst are the same
 477 
 478         beginWrite();
 479         try {
 480             ensureOpen();
 481             Entry eSrc = getEntry(src);  // ensureOpen checked
 482 
 483             if (eSrc == null)
 484                 throw new NoSuchFileException(getString(src));


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


 666                     SeekableByteChannel sbc =
 667                             new EntryOutputChannel(new Entry(e, Entry.NEW));
 668                     if (options.contains(APPEND)) {
 669                         try (InputStream is = getInputStream(e)) {  // copyover
 670                             byte[] buf = new byte[8192];
 671                             ByteBuffer bb = ByteBuffer.wrap(buf);
 672                             int n;
 673                             while ((n = is.read(buf)) != -1) {
 674                                 bb.position(0);
 675                                 bb.limit(n);
 676                                 sbc.write(bb);
 677                             }
 678                         }
 679                     }
 680                     return sbc;
 681                 }
 682                 if (!options.contains(CREATE) && !options.contains(CREATE_NEW))
 683                     throw new NoSuchFileException(getString(path));
 684                 checkParents(path);
 685                 return new EntryOutputChannel(
 686                     new Entry(path, Entry.NEW, false, getCompressMethod(attrs), attrs));
 687 
 688             } finally {
 689                 endRead();
 690             }
 691         } else {
 692             beginRead();
 693             try {
 694                 ensureOpen();
 695                 Entry e = getEntry(path);
 696                 if (e == null || e.isDir())
 697                     throw new NoSuchFileException(getString(path));
 698                 try (InputStream is = getInputStream(e)) {
 699                     // TBD: if (e.size < NNNNN);
 700                     return new ByteArrayChannel(is.readAllBytes(), true);
 701                 }
 702             } finally {
 703                 endRead();
 704             }
 705         }
 706     }


 731                     }
 732                 } else {
 733                     if (options.contains(StandardOpenOption.CREATE_NEW)) {
 734                         throw new FileAlreadyExistsException(getString(path));
 735                     }
 736                     if (e.isDir())
 737                         throw new FileAlreadyExistsException("directory <"
 738                             + getString(path) + "> exists");
 739                 }
 740                 options = new HashSet<>(options);
 741                 options.remove(StandardOpenOption.CREATE_NEW); // for tmpfile
 742             } else if (e == null || e.isDir()) {
 743                 throw new NoSuchFileException(getString(path));
 744             }
 745 
 746             final boolean isFCH = (e != null && e.type == Entry.FILECH);
 747             final Path tmpfile = isFCH ? e.file : getTempPathForEntry(path);
 748             final FileChannel fch = tmpfile.getFileSystem()
 749                                            .provider()
 750                                            .newFileChannel(tmpfile, options, attrs);
 751             final Entry u = isFCH ? e : new Entry(path, tmpfile, Entry.FILECH, attrs);
 752             if (forWrite) {
 753                 u.flag = FLAG_DATADESCR;
 754                 u.method = getCompressMethod(attrs);
 755             }
 756             // is there a better way to hook into the FileChannel's close method?
 757             return new FileChannel() {
 758                 public int write(ByteBuffer src) throws IOException {
 759                     return fch.write(src);
 760                 }
 761                 public long write(ByteBuffer[] srcs, int offset, int length)
 762                     throws IOException
 763                 {
 764                     return fch.write(srcs, offset, length);
 765                 }
 766                 public long position() throws IOException {
 767                     return fch.position();
 768                 }
 769                 public FileChannel position(long newPosition)
 770                     throws IOException
 771                 {


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


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

1559 
1560         EntryInputStream(Entry e, SeekableByteChannel zfch)
1561             throws IOException
1562         {
1563             this.zfch = zfch;
1564             rem = e.csize;

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


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




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


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












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


1847 
1848         IndexNode() {}
1849         IndexNode sibling;
1850         IndexNode child;  // 1st child
1851     }
1852 
1853     static class Entry extends IndexNode implements ZipFileAttributes {
1854 
1855         static final int CEN    = 1;  // entry read from cen
1856         static final int NEW    = 2;  // updated contents in bytes or file
1857         static final int FILECH = 3;  // fch update in "file"
1858         static final int COPY   = 4;  // copy of a CEN entry
1859 
1860         byte[] bytes;                 // updated content bytes
1861         Path   file;                  // use tmp file to store bytes;
1862         int    type = CEN;            // default is the entry read from cen
1863 
1864         // entry attributes
1865         int    version;
1866         int    flag;
1867         int    posixPerms = -1; // posix permissions
1868         int    method = -1;    // compression method
1869         long   mtime  = -1;    // last modification time (in DOS time)
1870         long   atime  = -1;    // last access time
1871         long   ctime  = -1;    // create time
1872         long   crc    = -1;    // crc-32 of entry data
1873         long   csize  = -1;    // compressed size of entry data
1874         long   size   = -1;    // uncompressed size of entry data
1875         byte[] extra;
1876 
1877         // cen
1878 
1879         // these fields are not used
1880         // int    versionMade;
1881         // int    disk;
1882         // int    attrs;
1883         // long   attrsEx;
1884         long   locoff;
1885         byte[] comment;
1886 
1887         Entry() {}
1888 
1889         Entry(byte[] name, boolean isdir, int method) {
1890             name(name);
1891             this.isdir = isdir;
1892             this.mtime  = this.ctime = this.atime = System.currentTimeMillis();
1893             this.crc    = 0;
1894             this.size   = 0;
1895             this.csize  = 0;
1896             this.method = method;
1897         }
1898 
1899         @SuppressWarnings("unchecked")
1900         Entry(byte[] name, int type, boolean isdir, int method, FileAttribute<?>... attrs) {
1901             this(name, isdir, method);
1902             this.type = type;
1903             for (FileAttribute<?> attr : attrs) {
1904                 String attrName = attr.name();
1905                 if (attrName.equals("posix:permissions") || attrName.equals("unix:permissions")) {
1906                     posixPerms = ZipUtils.permsToFlags((Set<PosixFilePermission>)attr.value());
1907                 }
1908             }
1909         }
1910 
1911         Entry(Entry e, int type) {
1912             name(e.name);
1913             this.isdir     = e.isdir;
1914             this.version   = e.version;
1915             this.ctime     = e.ctime;
1916             this.atime     = e.atime;
1917             this.mtime     = e.mtime;
1918             this.crc       = e.crc;
1919             this.size      = e.size;
1920             this.csize     = e.csize;
1921             this.method    = e.method;
1922             this.extra     = e.extra;
1923             /*
1924             this.versionMade = e.versionMade;
1925             this.disk      = e.disk;
1926             this.attrs     = e.attrs;
1927             this.attrsEx   = e.attrsEx;
1928             */
1929             this.locoff    = e.locoff;
1930             this.comment   = e.comment;
1931             this.posixPerms = e.posixPerms;
1932             this.type      = type;
1933         }
1934 
1935         @SuppressWarnings("unchecked")
1936         Entry(byte[] name, Path file, int type, FileAttribute<?>... attrs) {
1937             this(name, type, false, METHOD_STORED);
1938             this.file = file;
1939             for (FileAttribute<?> attr : attrs) {
1940                 String attrName = attr.name();
1941                 if (attrName.equals("posix:permissions") || attrName.equals("unix:permissions")) {
1942                     posixPerms = ZipUtils.permsToFlags((Set<PosixFilePermission>)attr.value());
1943                 }
1944             }
1945         }
1946 
1947         int version(boolean zip64) throws ZipException {
1948             if (zip64) {
1949                 return 45;
1950             }
1951             if (method == METHOD_DEFLATED)
1952                 return 20;
1953             else if (method == METHOD_STORED)
1954                 return 10;
1955             throw new ZipException("unsupported compression method");
1956         }
1957 
1958         /**
1959          * Adds information about compatibility of file attribute information
1960          * to a version value.
1961          */
1962         int versionMadeBy(int version) {
1963             return (posixPerms < 0) ? version :
1964                 VERSION_BASE_UNIX | (version & 0xff);
1965         }
1966 
1967         ///////////////////// CEN //////////////////////
1968         static Entry readCEN(ZipFileSystem zipfs, IndexNode inode)
1969             throws IOException
1970         {
1971             return new Entry().cen(zipfs, inode);
1972         }
1973 
1974         private Entry cen(ZipFileSystem zipfs, IndexNode inode)
1975             throws IOException
1976         {
1977             byte[] cen = zipfs.cen;
1978             int pos = inode.pos;
1979             if (!cenSigAt(cen, pos))
1980                 zerror("invalid CEN header (bad signature)");
1981             version     = CENVER(cen, pos);
1982             flag        = CENFLG(cen, pos);
1983             method      = CENHOW(cen, pos);
1984             mtime       = dosToJavaTime(CENTIM(cen, pos));
1985             crc         = CENCRC(cen, pos);
1986             csize       = CENSIZ(cen, pos);
1987             size        = CENLEN(cen, pos);
1988             int nlen    = CENNAM(cen, pos);
1989             int elen    = CENEXT(cen, pos);
1990             int clen    = CENCOM(cen, pos);
1991             /*
1992             versionMade = CENVEM(cen, pos);
1993             disk        = CENDSK(cen, pos);
1994             attrs       = CENATT(cen, pos);
1995             attrsEx     = CENATX(cen, pos);
1996             */
1997             if (CENVEM_FA(cen, pos) == FILE_ATTRIBUTES_UNIX) {
1998                 posixPerms = CENATX_PERMS(cen, pos) & 0xFFF; // 12 bits for setuid, setgid, sticky + perms
1999             }
2000             locoff      = CENOFF(cen, pos);
2001             pos += CENHDR;
2002             this.name = inode.name;
2003             this.isdir = inode.isdir;
2004             this.hashcode = inode.hashcode;
2005 
2006             pos += nlen;
2007             if (elen > 0) {
2008                 extra = Arrays.copyOfRange(cen, pos, pos + elen);
2009                 pos += elen;
2010                 readExtra(zipfs);
2011             }
2012             if (clen > 0) {
2013                 comment = Arrays.copyOfRange(cen, pos, pos + clen);
2014             }
2015             return this;
2016         }
2017 
2018         int writeCEN(OutputStream os) throws IOException {



2019             long csize0  = csize;
2020             long size0   = size;
2021             long locoff0 = locoff;
2022             int elen64   = 0;                // extra for ZIP64
2023             int elenNTFS = 0;                // extra for NTFS (a/c/mtime)
2024             int elenEXTT = 0;                // extra for Extended Timestamp
2025             boolean foundExtraTime = false;  // if time stamp NTFS, EXTT present
2026 
2027             byte[] zname = isdir ? toDirectoryPath(name) : name;
2028 
2029             // confirm size/length
2030             int nlen = (zname != null) ? zname.length - 1 : 0;  // name has [0] as "slash"
2031             int elen = (extra != null) ? extra.length : 0;
2032             int eoff = 0;
2033             int clen = (comment != null) ? comment.length : 0;
2034             if (csize >= ZIP64_MINVAL) {
2035                 csize0 = ZIP64_MINVAL;
2036                 elen64 += 8;                 // csize(8)
2037             }
2038             if (size >= ZIP64_MINVAL) {
2039                 size0 = ZIP64_MINVAL;        // size(8)
2040                 elen64 += 8;
2041             }
2042             if (locoff >= ZIP64_MINVAL) {
2043                 locoff0 = ZIP64_MINVAL;
2044                 elen64 += 8;                 // offset(8)
2045             }
2046             if (elen64 != 0) {
2047                 elen64 += 4;                 // header and data sz 4 bytes
2048             }
2049             boolean zip64 = (elen64 != 0);
2050             int version0 = version(zip64);
2051             while (eoff + 4 < elen) {
2052                 int tag = SH(extra, eoff);
2053                 int sz = SH(extra, eoff + 2);
2054                 if (tag == EXTID_EXTT || tag == EXTID_NTFS) {
2055                     foundExtraTime = true;
2056                 }
2057                 eoff += (4 + sz);
2058             }
2059             if (!foundExtraTime) {
2060                 if (isWindows) {             // use NTFS
2061                     elenNTFS = 36;           // total 36 bytes
2062                 } else {                     // Extended Timestamp otherwise
2063                     elenEXTT = 9;            // only mtime in cen
2064                 }
2065             }
2066             writeInt(os, CENSIG);            // CEN header signature
2067             writeShort(os, versionMadeBy(version0)); // version made by
2068             writeShort(os, version0);        // version needed to extract





2069             writeShort(os, flag);            // general purpose bit flag
2070             writeShort(os, method);          // compression method
2071                                              // last modification time
2072             writeInt(os, (int)javaToDosTime(mtime));
2073             writeInt(os, crc);               // crc-32
2074             writeInt(os, csize0);            // compressed size
2075             writeInt(os, size0);             // uncompressed size
2076             writeShort(os, nlen);
2077             writeShort(os, elen + elen64 + elenNTFS + elenEXTT);
2078 
2079             if (comment != null) {
2080                 writeShort(os, Math.min(clen, 0xffff));
2081             } else {
2082                 writeShort(os, 0);
2083             }
2084             writeShort(os, 0);              // starting disk number
2085             writeShort(os, 0);              // internal file attributes (unused)
2086             writeInt(os, posixPerms > 0 ? posixPerms << 16 : 0); // external file
2087                                             // attributes, used for storing posix
2088                                             // permissions
2089             writeInt(os, locoff0);          // relative offset of local header
2090             writeBytes(os, zname, 1, nlen);
2091             if (zip64) {
2092                 writeShort(os, EXTID_ZIP64);// Zip64 extra
2093                 writeShort(os, elen64 - 4); // size of "this" extra block
2094                 if (size0 == ZIP64_MINVAL)
2095                     writeLong(os, size);
2096                 if (csize0 == ZIP64_MINVAL)
2097                     writeLong(os, csize);
2098                 if (locoff0 == ZIP64_MINVAL)
2099                     writeLong(os, locoff);
2100             }
2101             if (elenNTFS != 0) {
2102                 writeShort(os, EXTID_NTFS);
2103                 writeShort(os, elenNTFS - 4);
2104                 writeInt(os, 0);            // reserved
2105                 writeShort(os, 0x0001);     // NTFS attr tag
2106                 writeShort(os, 24);
2107                 writeLong(os, javaToWinTime(mtime));
2108                 writeLong(os, javaToWinTime(atime));
2109                 writeLong(os, javaToWinTime(ctime));
2110             }
2111             if (elenEXTT != 0) {
2112                 writeShort(os, EXTID_EXTT);
2113                 writeShort(os, elenEXTT - 4);
2114                 if (ctime == -1)
2115                     os.write(0x3);          // mtime and atime
2116                 else
2117                     os.write(0x7);          // mtime, atime and ctime
2118                 writeInt(os, javaToUnixTime(mtime));
2119             }
2120             if (extra != null)              // whatever not recognized
2121                 writeBytes(os, extra);
2122             if (comment != null)            //TBD: 0, Math.min(commentBytes.length, 0xffff));
2123                 writeBytes(os, comment);
2124             return CENHDR + nlen + elen + clen + elen64 + elenNTFS + elenEXTT;
2125         }
2126 
2127         ///////////////////// LOC //////////////////////
2128 
2129         int writeLOC(OutputStream os) throws IOException {
2130             writeInt(os, LOCSIG);               // LOC header signature


2131             byte[] zname = isdir ? toDirectoryPath(name) : name;
2132             int nlen = (zname != null) ? zname.length - 1 : 0; // [0] is slash
2133             int elen = (extra != null) ? extra.length : 0;
2134             boolean foundExtraTime = false;     // if extra timestamp present
2135             int eoff = 0;
2136             int elen64 = 0;
2137             boolean zip64 = false;
2138             int elenEXTT = 0;
2139             int elenNTFS = 0;
2140             if ((flag & FLAG_DATADESCR) != 0) {
2141                 writeShort(os, version(zip64)); // version needed to extract
2142                 writeShort(os, flag);           // general purpose bit flag
2143                 writeShort(os, method);         // compression method
2144                 // last modification time
2145                 writeInt(os, (int)javaToDosTime(mtime));
2146                 // store size, uncompressed size, and crc-32 in data descriptor
2147                 // immediately following compressed entry data
2148                 writeInt(os, 0);
2149                 writeInt(os, 0);
2150                 writeInt(os, 0);
2151             } else {
2152                 if (csize >= ZIP64_MINVAL || size >= ZIP64_MINVAL) {
2153                     elen64 = 20;    //headid(2) + size(2) + size(8) + csize(8)
2154                     zip64 = true;


2155                 }
2156                 writeShort(os, version(zip64)); // version needed to extract
2157                 writeShort(os, flag);           // general purpose bit flag
2158                 writeShort(os, method);         // compression method
2159                                                 // last modification time
2160                 writeInt(os, (int)javaToDosTime(mtime));
2161                 writeInt(os, crc);              // crc-32
2162                 if (zip64) {
2163                     writeInt(os, ZIP64_MINVAL);
2164                     writeInt(os, ZIP64_MINVAL);
2165                 } else {
2166                     writeInt(os, csize);        // compressed size
2167                     writeInt(os, size);         // uncompressed size
2168                 }
2169             }
2170             while (eoff + 4 < elen) {
2171                 int tag = SH(extra, eoff);
2172                 int sz = SH(extra, eoff + 2);
2173                 if (tag == EXTID_EXTT || tag == EXTID_NTFS) {
2174                     foundExtraTime = true;
2175                 }
2176                 eoff += (4 + sz);
2177             }
2178             if (!foundExtraTime) {
2179                 if (isWindows) {
2180                     elenNTFS = 36;              // NTFS, total 36 bytes
2181                 } else {                        // on unix use "ext time"
2182                     elenEXTT = 9;
2183                     if (atime != -1)
2184                         elenEXTT += 4;
2185                     if (ctime != -1)
2186                         elenEXTT += 4;
2187                 }
2188             }
2189             writeShort(os, nlen);
2190             writeShort(os, elen + elen64 + elenNTFS + elenEXTT);
2191             writeBytes(os, zname, 1, nlen);
2192             if (zip64) {
2193                 writeShort(os, EXTID_ZIP64);
2194                 writeShort(os, 16);
2195                 writeLong(os, size);
2196                 writeLong(os, csize);
2197             }
2198             if (elenNTFS != 0) {
2199                 writeShort(os, EXTID_NTFS);
2200                 writeShort(os, elenNTFS - 4);
2201                 writeInt(os, 0);            // reserved
2202                 writeShort(os, 0x0001);     // NTFS attr tag
2203                 writeShort(os, 24);
2204                 writeLong(os, javaToWinTime(mtime));
2205                 writeLong(os, javaToWinTime(atime));
2206                 writeLong(os, javaToWinTime(ctime));
2207             }
2208             if (elenEXTT != 0) {
2209                 writeShort(os, EXTID_EXTT);
2210                 writeShort(os, elenEXTT - 4);// size for the folowing data block
2211                 int fbyte = 0x1;
2212                 if (atime != -1)           // mtime and atime


2425                 return Arrays.copyOf(comment, comment.length);
2426             return null;
2427         }
2428 
2429         public String toString() {
2430             StringBuilder sb = new StringBuilder(1024);
2431             Formatter fm = new Formatter(sb);
2432             fm.format("    name            : %s%n", new String(name));
2433             fm.format("    creationTime    : %tc%n", creationTime().toMillis());
2434             fm.format("    lastAccessTime  : %tc%n", lastAccessTime().toMillis());
2435             fm.format("    lastModifiedTime: %tc%n", lastModifiedTime().toMillis());
2436             fm.format("    isRegularFile   : %b%n", isRegularFile());
2437             fm.format("    isDirectory     : %b%n", isDirectory());
2438             fm.format("    isSymbolicLink  : %b%n", isSymbolicLink());
2439             fm.format("    isOther         : %b%n", isOther());
2440             fm.format("    fileKey         : %s%n", fileKey());
2441             fm.format("    size            : %d%n", size());
2442             fm.format("    compressedSize  : %d%n", compressedSize());
2443             fm.format("    crc             : %x%n", crc());
2444             fm.format("    method          : %d%n", method());
2445             if (posixPerms != -1) {
2446                 fm.format("    permissions     : %s%n", permissions());
2447             }
2448             fm.close();
2449             return sb.toString();
2450         }
2451 
2452         @Override
2453         public UserPrincipal owner() {
2454             throw new UnsupportedOperationException(
2455                 "ZipFileSystem does not support owner.");
2456         }
2457 
2458         @Override
2459         public GroupPrincipal group() {
2460             throw new UnsupportedOperationException(
2461                 "ZipFileSystem does not support group.");
2462         }
2463 
2464         @Override
2465         public Set<PosixFilePermission> permissions() {
2466             if (posixPerms == -1) {
2467                 // in case there are no Posix permissions associated with the
2468                 // entry, we should not return an empty set of permissions
2469                 // because that would be an explicit set of permissions meaning
2470                 // no permissions for anyone
2471                 throw new UnsupportedOperationException(
2472                     "No posix permissions associated with zip entry.");
2473             }
2474             return ZipUtils.permsFromFlags(posixPerms);
2475         }
2476 
2477         @Override
2478         public void setPermissions(Set<PosixFilePermission> perms) {
2479             posixPerms = ZipUtils.permsToFlags(perms);
2480         }
2481     }
2482 
2483     // ZIP directory has two issues:
2484     // (1) ZIP spec does not require the ZIP file to include
2485     //     directory entry
2486     // (2) all entries are not stored/organized in a "tree"
2487     //     structure.
2488     // A possible solution is to build the node tree ourself as
2489     // implemented below.

2490 
2491     // default time stamp for pseudo entries
2492     private long zfsDefaultTimeStamp = System.currentTimeMillis();
2493 
2494     private void removeFromTree(IndexNode inode) {
2495         IndexNode parent = inodes.get(LOOKUPKEY.as(getParent(inode.name)));
2496         IndexNode child = parent.child;
2497         if (child.equals(inode)) {
2498             parent.child = child.sibling;
2499         } else {
2500             IndexNode last = child;
2501             while ((child = child.sibling) != null) {
2502                 if (child.equals(inode)) {
2503                     last.sibling = child.sibling;
2504                     break;
2505                 } else {
2506                     last = child;
2507                 }
2508             }
2509         }


< prev index next >