< prev index next >

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

Print this page
rev 51866 : 6194856: Zip Files lose ALL ownership and permissions of the files
   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


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


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


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


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


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


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


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


1839 
1840         IndexNode() {}
1841         IndexNode sibling;
1842         IndexNode child;  // 1st child
1843     }
1844 
1845     static class Entry extends IndexNode implements ZipFileAttributes {
1846 
1847         static final int CEN    = 1;  // entry read from cen
1848         static final int NEW    = 2;  // updated contents in bytes or file
1849         static final int FILECH = 3;  // fch update in "file"
1850         static final int COPY   = 4;  // copy of a CEN entry
1851 
1852         byte[] bytes;                 // updated content bytes
1853         Path   file;                  // use tmp file to store bytes;
1854         int    type = CEN;            // default is the entry read from cen
1855 
1856         // entry attributes
1857         int    version;
1858         int    flag;

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

1891             this(name, isdir, method);
1892             this.type = type;






1893         }
1894 
1895         Entry (Entry e, int type) {
1896             name(e.name);
1897             this.isdir     = e.isdir;
1898             this.version   = e.version;
1899             this.ctime     = e.ctime;
1900             this.atime     = e.atime;
1901             this.mtime     = e.mtime;
1902             this.crc       = e.crc;
1903             this.size      = e.size;
1904             this.csize     = e.csize;
1905             this.method    = e.method;
1906             this.extra     = e.extra;
1907             /*
1908             this.versionMade = e.versionMade;
1909             this.disk      = e.disk;
1910             this.attrs     = e.attrs;
1911             this.attrsEx   = e.attrsEx;
1912             */
1913             this.locoff    = e.locoff;
1914             this.comment   = e.comment;

1915             this.type      = type;
1916         }
1917 
1918         Entry (byte[] name, Path file, int type) {

1919             this(name, type, false, METHOD_STORED);
1920             this.file = file;






1921         }
1922 
1923         int version() throws ZipException {



1924             if (method == METHOD_DEFLATED)
1925                 return 20;
1926             else if (method == METHOD_STORED)
1927                 return 10;
1928             throw new ZipException("unsupported compression method");
1929         }
1930 









1931         ///////////////////// CEN //////////////////////
1932         static Entry readCEN(ZipFileSystem zipfs, IndexNode inode)
1933             throws IOException
1934         {
1935             return new Entry().cen(zipfs, inode);
1936         }
1937 
1938         private Entry cen(ZipFileSystem zipfs, IndexNode inode)
1939             throws IOException
1940         {
1941             byte[] cen = zipfs.cen;
1942             int pos = inode.pos;
1943             if (!cenSigAt(cen, pos))
1944                 zerror("invalid CEN header (bad signature)");
1945             version     = CENVER(cen, pos);
1946             flag        = CENFLG(cen, pos);
1947             method      = CENHOW(cen, pos);
1948             mtime       = dosToJavaTime(CENTIM(cen, pos));
1949             crc         = CENCRC(cen, pos);
1950             csize       = CENSIZ(cen, pos);
1951             size        = CENLEN(cen, pos);
1952             int nlen    = CENNAM(cen, pos);
1953             int elen    = CENEXT(cen, pos);
1954             int clen    = CENCOM(cen, pos);
1955             /*
1956             versionMade = CENVEM(cen, pos);
1957             disk        = CENDSK(cen, pos);
1958             attrs       = CENATT(cen, pos);
1959             attrsEx     = CENATX(cen, pos);
1960             */



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


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


2054             writeInt(os, locoff0);          // relative offset of local header
2055             writeBytes(os, zname, 1, nlen);
2056             if (elen64 != 0) {
2057                 writeShort(os, EXTID_ZIP64);// Zip64 extra
2058                 writeShort(os, elen64 - 4); // size of "this" extra block
2059                 if (size0 == ZIP64_MINVAL)
2060                     writeLong(os, size);
2061                 if (csize0 == ZIP64_MINVAL)
2062                     writeLong(os, csize);
2063                 if (locoff0 == ZIP64_MINVAL)
2064                     writeLong(os, locoff);
2065             }
2066             if (elenNTFS != 0) {
2067                 writeShort(os, EXTID_NTFS);
2068                 writeShort(os, elenNTFS - 4);
2069                 writeInt(os, 0);            // reserved
2070                 writeShort(os, 0x0001);     // NTFS attr tag
2071                 writeShort(os, 24);
2072                 writeLong(os, javaToWinTime(mtime));
2073                 writeLong(os, javaToWinTime(atime));
2074                 writeLong(os, javaToWinTime(ctime));
2075             }
2076             if (elenEXTT != 0) {
2077                 writeShort(os, EXTID_EXTT);
2078                 writeShort(os, elenEXTT - 4);
2079                 if (ctime == -1)
2080                     os.write(0x3);          // mtime and atime
2081                 else
2082                     os.write(0x7);          // mtime, atime and ctime
2083                 writeInt(os, javaToUnixTime(mtime));
2084             }
2085             if (extra != null)              // whatever not recognized
2086                 writeBytes(os, extra);
2087             if (comment != null)            //TBD: 0, Math.min(commentBytes.length, 0xffff));
2088                 writeBytes(os, comment);
2089             return CENHDR + nlen + elen + clen + elen64 + elenNTFS + elenEXTT;
2090         }
2091 
2092         ///////////////////// LOC //////////////////////
2093 
2094         int writeLOC(OutputStream os) throws IOException {
2095             writeInt(os, LOCSIG);               // LOC header signature
2096             int version = version();
2097 
2098             byte[] zname = isdir ? toDirectoryPath(name) : name;
2099             int nlen = (zname != null) ? zname.length - 1 : 0; // [0] is slash
2100             int elen = (extra != null) ? extra.length : 0;
2101             boolean foundExtraTime = false;     // if extra timestamp present
2102             int eoff = 0;
2103             int elen64 = 0;

2104             int elenEXTT = 0;
2105             int elenNTFS = 0;
2106             if ((flag & FLAG_DATADESCR) != 0) {
2107                 writeShort(os, version());      // version needed to extract
2108                 writeShort(os, flag);           // general purpose bit flag
2109                 writeShort(os, method);         // compression method
2110                 // last modification time
2111                 writeInt(os, (int)javaToDosTime(mtime));
2112                 // store size, uncompressed size, and crc-32 in data descriptor
2113                 // immediately following compressed entry data
2114                 writeInt(os, 0);
2115                 writeInt(os, 0);
2116                 writeInt(os, 0);
2117             } else {
2118                 if (csize >= ZIP64_MINVAL || size >= ZIP64_MINVAL) {
2119                     elen64 = 20;    //headid(2) + size(2) + size(8) + csize(8)
2120                     writeShort(os, 45);         // ver 4.5 for zip64
2121                 } else {
2122                     writeShort(os, version());  // version needed to extract
2123                 }

2124                 writeShort(os, flag);           // general purpose bit flag
2125                 writeShort(os, method);         // compression method
2126                                                 // last modification time
2127                 writeInt(os, (int)javaToDosTime(mtime));
2128                 writeInt(os, crc);              // crc-32
2129                 if (elen64 != 0) {
2130                     writeInt(os, ZIP64_MINVAL);
2131                     writeInt(os, ZIP64_MINVAL);
2132                 } else {
2133                     writeInt(os, csize);        // compressed size
2134                     writeInt(os, size);         // uncompressed size
2135                 }
2136             }
2137             while (eoff + 4 < elen) {
2138                 int tag = SH(extra, eoff);
2139                 int sz = SH(extra, eoff + 2);
2140                 if (tag == EXTID_EXTT || tag == EXTID_NTFS) {
2141                     foundExtraTime = true;
2142                 }
2143                 eoff += (4 + sz);
2144             }
2145             if (!foundExtraTime) {
2146                 if (isWindows) {
2147                     elenNTFS = 36;              // NTFS, total 36 bytes
2148                 } else {                        // on unix use "ext time"
2149                     elenEXTT = 9;
2150                     if (atime != -1)
2151                         elenEXTT += 4;
2152                     if (ctime != -1)
2153                         elenEXTT += 4;
2154                 }
2155             }
2156             writeShort(os, nlen);
2157             writeShort(os, elen + elen64 + elenNTFS + elenEXTT);
2158             writeBytes(os, zname, 1, nlen);
2159             if (elen64 != 0) {
2160                 writeShort(os, EXTID_ZIP64);
2161                 writeShort(os, 16);
2162                 writeLong(os, size);
2163                 writeLong(os, csize);
2164             }
2165             if (elenNTFS != 0) {
2166                 writeShort(os, EXTID_NTFS);
2167                 writeShort(os, elenNTFS - 4);
2168                 writeInt(os, 0);            // reserved
2169                 writeShort(os, 0x0001);     // NTFS attr tag
2170                 writeShort(os, 24);
2171                 writeLong(os, javaToWinTime(mtime));
2172                 writeLong(os, javaToWinTime(atime));
2173                 writeLong(os, javaToWinTime(ctime));
2174             }
2175             if (elenEXTT != 0) {
2176                 writeShort(os, EXTID_EXTT);
2177                 writeShort(os, elenEXTT - 4);// size for the folowing data block
2178                 int fbyte = 0x1;
2179                 if (atime != -1)           // mtime and atime


2392                 return Arrays.copyOf(comment, comment.length);
2393             return null;
2394         }
2395 
2396         public String toString() {
2397             StringBuilder sb = new StringBuilder(1024);
2398             Formatter fm = new Formatter(sb);
2399             fm.format("    name            : %s%n", new String(name));
2400             fm.format("    creationTime    : %tc%n", creationTime().toMillis());
2401             fm.format("    lastAccessTime  : %tc%n", lastAccessTime().toMillis());
2402             fm.format("    lastModifiedTime: %tc%n", lastModifiedTime().toMillis());
2403             fm.format("    isRegularFile   : %b%n", isRegularFile());
2404             fm.format("    isDirectory     : %b%n", isDirectory());
2405             fm.format("    isSymbolicLink  : %b%n", isSymbolicLink());
2406             fm.format("    isOther         : %b%n", isOther());
2407             fm.format("    fileKey         : %s%n", fileKey());
2408             fm.format("    size            : %d%n", size());
2409             fm.format("    compressedSize  : %d%n", compressedSize());
2410             fm.format("    crc             : %x%n", crc());
2411             fm.format("    method          : %d%n", method());



2412             fm.close();
2413             return sb.toString();
2414         }


































2415     }
2416 
2417     // ZIP directory has two issues:
2418     // (1) ZIP spec does not require the ZIP file to include
2419     //     directory entry
2420     // (2) all entries are not stored/organized in a "tree"
2421     //     structure.
2422     // A possible solution is to build the node tree ourself as
2423     // implemented below.
2424     private IndexNode root;
2425 
2426     // default time stamp for pseudo entries
2427     private long zfsDefaultTimeStamp = System.currentTimeMillis();
2428 
2429     private void removeFromTree(IndexNode inode) {
2430         IndexNode parent = inodes.get(LOOKUPKEY.as(getParent(inode.name)));
2431         IndexNode child = parent.child;
2432         if (child.equals(inode)) {
2433             parent.child = child.sibling;
2434         } else {
2435             IndexNode last = child;
2436             while ((child = child.sibling) != null) {
2437                 if (child.equals(inode)) {
2438                     last.sibling = child.sibling;
2439                     break;
2440                 } else {
2441                     last = child;
2442                 }
2443             }
2444         }


   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.PosixFilePermissions;
  55 import java.nio.file.attribute.UserPrincipal;
  56 import java.nio.file.attribute.UserPrincipalLookupService;
  57 import java.nio.file.spi.FileSystemProvider;
  58 import java.security.AccessController;
  59 import java.security.PrivilegedAction;
  60 import java.security.PrivilegedActionException;
  61 import java.security.PrivilegedExceptionAction;
  62 import java.util.ArrayList;
  63 import java.util.Arrays;
  64 import java.util.Collections;
  65 import java.util.Formatter;
  66 import java.util.HashSet;
  67 import java.util.Iterator;
  68 import java.util.LinkedHashMap;
  69 import java.util.List;
  70 import java.util.Map;
  71 import java.util.Objects;
  72 import java.util.Set;
  73 import java.util.concurrent.locks.ReadWriteLock;
  74 import java.util.concurrent.locks.ReentrantReadWriteLock;
  75 import java.util.regex.Pattern;
  76 import java.util.zip.CRC32;

  77 import java.util.zip.Deflater;

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





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

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


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


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


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


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


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



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





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


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


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


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

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


< prev index next >