26 package jdk.nio.zipfs;
27
28 import java.io.BufferedOutputStream;
29 import java.io.ByteArrayInputStream;
30 import java.io.ByteArrayOutputStream;
31 import java.io.EOFException;
32 import java.io.FilterOutputStream;
33 import java.io.IOException;
34 import java.io.InputStream;
35 import java.io.OutputStream;
36 import java.nio.ByteBuffer;
37 import java.nio.MappedByteBuffer;
38 import java.nio.channels.FileChannel;
39 import java.nio.channels.FileLock;
40 import java.nio.channels.ReadableByteChannel;
41 import java.nio.channels.SeekableByteChannel;
42 import java.nio.channels.WritableByteChannel;
43 import java.nio.file.*;
44 import java.nio.file.attribute.FileAttribute;
45 import java.nio.file.attribute.FileTime;
46 import java.nio.file.attribute.UserPrincipalLookupService;
47 import java.nio.file.spi.FileSystemProvider;
48 import java.security.AccessController;
49 import java.security.PrivilegedAction;
50 import java.security.PrivilegedActionException;
51 import java.security.PrivilegedExceptionAction;
52 import java.util.*;
53 import java.util.concurrent.locks.ReadWriteLock;
54 import java.util.concurrent.locks.ReentrantReadWriteLock;
55 import java.util.regex.Pattern;
56 import java.util.zip.CRC32;
57 import java.util.zip.Deflater;
58 import java.util.zip.DeflaterOutputStream;
59 import java.util.zip.Inflater;
60 import java.util.zip.InflaterInputStream;
61 import java.util.zip.ZipException;
62
63 import static java.lang.Boolean.TRUE;
64 import static java.nio.file.StandardCopyOption.COPY_ATTRIBUTES;
65 import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
354 if (getInode(path) == null) {
355 throw new NoSuchFileException(toString());
356 }
357
358 } finally {
359 endRead();
360 }
361 }
362
363 void setTimes(byte[] path, FileTime mtime, FileTime atime, FileTime ctime)
364 throws IOException
365 {
366 checkWritable();
367 beginWrite();
368 try {
369 ensureOpen();
370 Entry e = getEntry(path); // ensureOpen checked
371 if (e == null)
372 throw new NoSuchFileException(getString(path));
373 if (e.type == Entry.CEN)
374 e.type = Entry.COPY; // copy e
375 if (mtime != null)
376 e.mtime = mtime.toMillis();
377 if (atime != null)
378 e.atime = atime.toMillis();
379 if (ctime != null)
380 e.ctime = ctime.toMillis();
381 update(e);
382 } finally {
383 endWrite();
384 }
385 }
386
387 boolean exists(byte[] path)
388 throws IOException
389 {
390 beginRead();
391 try {
392 ensureOpen();
393 return getInode(path) != null;
394 } finally {
395 endRead();
396 }
397 }
398
399 boolean isDirectory(byte[] path)
400 throws IOException
401 {
402 beginRead();
403 try {
404 IndexNode n = getInode(path);
405 return n != null && n.isDir();
406 } finally {
437 list.add(zpath);
438 child = child.sibling;
439 }
440 return list.iterator();
441 } finally {
442 endWrite();
443 }
444 }
445
446 void createDirectory(byte[] dir, FileAttribute<?>... attrs)
447 throws IOException
448 {
449 checkWritable();
450 // dir = toDirectoryPath(dir);
451 beginWrite();
452 try {
453 ensureOpen();
454 if (dir.length == 0 || exists(dir)) // root dir, or exiting dir
455 throw new FileAlreadyExistsException(getString(dir));
456 checkParents(dir);
457 Entry e = new Entry(dir, Entry.NEW, true, METHOD_STORED);
458 update(e);
459 } finally {
460 endWrite();
461 }
462 }
463
464 void copyFile(boolean deletesrc, byte[]src, byte[] dst, CopyOption... options)
465 throws IOException
466 {
467 checkWritable();
468 if (Arrays.equals(src, dst))
469 return; // do nothing, src and dst are the same
470
471 beginWrite();
472 try {
473 ensureOpen();
474 Entry eSrc = getEntry(src); // ensureOpen checked
475
476 if (eSrc == null)
477 throw new NoSuchFileException(getString(src));
659 SeekableByteChannel sbc =
660 new EntryOutputChannel(new Entry(e, Entry.NEW));
661 if (options.contains(APPEND)) {
662 try (InputStream is = getInputStream(e)) { // copyover
663 byte[] buf = new byte[8192];
664 ByteBuffer bb = ByteBuffer.wrap(buf);
665 int n;
666 while ((n = is.read(buf)) != -1) {
667 bb.position(0);
668 bb.limit(n);
669 sbc.write(bb);
670 }
671 }
672 }
673 return sbc;
674 }
675 if (!options.contains(CREATE) && !options.contains(CREATE_NEW))
676 throw new NoSuchFileException(getString(path));
677 checkParents(path);
678 return new EntryOutputChannel(
679 new Entry(path, Entry.NEW, false, getCompressMethod(attrs)));
680
681 } finally {
682 endRead();
683 }
684 } else {
685 beginRead();
686 try {
687 ensureOpen();
688 Entry e = getEntry(path);
689 if (e == null || e.isDir())
690 throw new NoSuchFileException(getString(path));
691 try (InputStream is = getInputStream(e)) {
692 // TBD: if (e.size < NNNNN);
693 return new ByteArrayChannel(is.readAllBytes(), true);
694 }
695 } finally {
696 endRead();
697 }
698 }
699 }
724 }
725 } else {
726 if (options.contains(StandardOpenOption.CREATE_NEW)) {
727 throw new FileAlreadyExistsException(getString(path));
728 }
729 if (e.isDir())
730 throw new FileAlreadyExistsException("directory <"
731 + getString(path) + "> exists");
732 }
733 options = new HashSet<>(options);
734 options.remove(StandardOpenOption.CREATE_NEW); // for tmpfile
735 } else if (e == null || e.isDir()) {
736 throw new NoSuchFileException(getString(path));
737 }
738
739 final boolean isFCH = (e != null && e.type == Entry.FILECH);
740 final Path tmpfile = isFCH ? e.file : getTempPathForEntry(path);
741 final FileChannel fch = tmpfile.getFileSystem()
742 .provider()
743 .newFileChannel(tmpfile, options, attrs);
744 final Entry u = isFCH ? e : new Entry(path, tmpfile, Entry.FILECH);
745 if (forWrite) {
746 u.flag = FLAG_DATADESCR;
747 u.method = getCompressMethod(attrs);
748 }
749 // is there a better way to hook into the FileChannel's close method?
750 return new FileChannel() {
751 public int write(ByteBuffer src) throws IOException {
752 return fch.write(src);
753 }
754 public long write(ByteBuffer[] srcs, int offset, int length)
755 throws IOException
756 {
757 return fch.write(srcs, offset, length);
758 }
759 public long position() throws IOException {
760 return fch.position();
761 }
762 public FileChannel position(long newPosition)
763 throws IOException
764 {
1853
1854 IndexNode() {}
1855 IndexNode sibling;
1856 IndexNode child; // 1st child
1857 }
1858
1859 static class Entry extends IndexNode implements ZipFileAttributes {
1860
1861 static final int CEN = 1; // entry read from cen
1862 static final int NEW = 2; // updated contents in bytes or file
1863 static final int FILECH = 3; // fch update in "file"
1864 static final int COPY = 4; // copy of a CEN entry
1865
1866 byte[] bytes; // updated content bytes
1867 Path file; // use tmp file to store bytes;
1868 int type = CEN; // default is the entry read from cen
1869
1870 // entry attributes
1871 int version;
1872 int flag;
1873 int method = -1; // compression method
1874 long mtime = -1; // last modification time (in DOS time)
1875 long atime = -1; // last access time
1876 long ctime = -1; // create time
1877 long crc = -1; // crc-32 of entry data
1878 long csize = -1; // compressed size of entry data
1879 long size = -1; // uncompressed size of entry data
1880 byte[] extra;
1881
1882 // cen
1883
1884 // these fields are not used by anyone and writeCEN uses "0"
1885 // int versionMade;
1886 // int disk;
1887 // int attrs;
1888 // long attrsEx;
1889 long locoff;
1890 byte[] comment;
1891
1892 Entry() {}
1893
1894 Entry(byte[] name, boolean isdir, int method) {
1895 name(name);
1896 this.isdir = isdir;
1897 this.mtime = this.ctime = this.atime = System.currentTimeMillis();
1898 this.crc = 0;
1899 this.size = 0;
1900 this.csize = 0;
1901 this.method = method;
1902 }
1903
1904 Entry(byte[] name, int type, boolean isdir, int method) {
1905 this(name, isdir, method);
1906 this.type = type;
1907 }
1908
1909 Entry (Entry e, int type) {
1910 name(e.name);
1911 this.isdir = e.isdir;
1912 this.version = e.version;
1913 this.ctime = e.ctime;
1914 this.atime = e.atime;
1915 this.mtime = e.mtime;
1916 this.crc = e.crc;
1917 this.size = e.size;
1918 this.csize = e.csize;
1919 this.method = e.method;
1920 this.extra = e.extra;
1921 /*
1922 this.versionMade = e.versionMade;
1923 this.disk = e.disk;
1924 this.attrs = e.attrs;
1925 this.attrsEx = e.attrsEx;
1926 */
1927 this.locoff = e.locoff;
1928 this.comment = e.comment;
1929 this.type = type;
1930 }
1931
1932 Entry (byte[] name, Path file, int type) {
1933 this(name, type, false, METHOD_STORED);
1934 this.file = file;
1935 }
1936
1937 int version() throws ZipException {
1938 if (method == METHOD_DEFLATED)
1939 return 20;
1940 else if (method == METHOD_STORED)
1941 return 10;
1942 throw new ZipException("unsupported compression method");
1943 }
1944
1945 ///////////////////// CEN //////////////////////
1946 static Entry readCEN(ZipFileSystem zipfs, IndexNode inode)
1947 throws IOException
1948 {
1949 return new Entry().cen(zipfs, inode);
1950 }
1951
1952 private Entry cen(ZipFileSystem zipfs, IndexNode inode)
1953 throws IOException
1954 {
1955 byte[] cen = zipfs.cen;
1956 int pos = inode.pos;
1957 if (!cenSigAt(cen, pos))
1958 zerror("invalid CEN header (bad signature)");
1959 version = CENVER(cen, pos);
1960 flag = CENFLG(cen, pos);
1961 method = CENHOW(cen, pos);
1962 mtime = dosToJavaTime(CENTIM(cen, pos));
1963 crc = CENCRC(cen, pos);
1964 csize = CENSIZ(cen, pos);
1965 size = CENLEN(cen, pos);
1966 int nlen = CENNAM(cen, pos);
1967 int elen = CENEXT(cen, pos);
1968 int clen = CENCOM(cen, pos);
1969 /*
1970 versionMade = CENVEM(cen, pos);
1971 disk = CENDSK(cen, pos);
1972 attrs = CENATT(cen, pos);
1973 attrsEx = CENATX(cen, pos);
1974 */
1975 locoff = CENOFF(cen, pos);
1976 pos += CENHDR;
1977 this.name = inode.name;
1978 this.isdir = inode.isdir;
1979 this.hashcode = inode.hashcode;
1980
1981 pos += nlen;
1982 if (elen > 0) {
1983 extra = Arrays.copyOfRange(cen, pos, pos + elen);
1984 pos += elen;
1985 readExtra(zipfs);
1986 }
1987 if (clen > 0) {
1988 comment = Arrays.copyOfRange(cen, pos, pos + clen);
1989 }
1990 return this;
1991 }
1992
1993 int writeCEN(OutputStream os) throws IOException {
1994 int version0 = version();
1995 long csize0 = csize;
1996 long size0 = size;
1997 long locoff0 = locoff;
1998 int elen64 = 0; // extra for ZIP64
1999 int elenNTFS = 0; // extra for NTFS (a/c/mtime)
2000 int elenEXTT = 0; // extra for Extended Timestamp
2001 boolean foundExtraTime = false; // if time stamp NTFS, EXTT present
2002
2003 byte[] zname = isdir ? toDirectoryPath(name) : name;
2004
2005 // confirm size/length
2006 int nlen = (zname != null) ? zname.length - 1 : 0; // name has [0] as "slash"
2007 int elen = (extra != null) ? extra.length : 0;
2008 int eoff = 0;
2009 int clen = (comment != null) ? comment.length : 0;
2010 if (csize >= ZIP64_MINVAL) {
2011 csize0 = ZIP64_MINVAL;
2012 elen64 += 8; // csize(8)
2013 }
2014 if (size >= ZIP64_MINVAL) {
2015 size0 = ZIP64_MINVAL; // size(8)
2016 elen64 += 8;
2017 }
2018 if (locoff >= ZIP64_MINVAL) {
2019 locoff0 = ZIP64_MINVAL;
2020 elen64 += 8; // offset(8)
2021 }
2022 if (elen64 != 0) {
2023 elen64 += 4; // header and data sz 4 bytes
2024 }
2025 while (eoff + 4 < elen) {
2026 int tag = SH(extra, eoff);
2027 int sz = SH(extra, eoff + 2);
2028 if (tag == EXTID_EXTT || tag == EXTID_NTFS) {
2029 foundExtraTime = true;
2030 }
2031 eoff += (4 + sz);
2032 }
2033 if (!foundExtraTime) {
2034 if (isWindows) { // use NTFS
2035 elenNTFS = 36; // total 36 bytes
2036 } else { // Extended Timestamp otherwise
2037 elenEXTT = 9; // only mtime in cen
2038 }
2039 }
2040 writeInt(os, CENSIG); // CEN header signature
2041 if (elen64 != 0) {
2042 writeShort(os, 45); // ver 4.5 for zip64
2043 writeShort(os, 45);
2044 } else {
2045 writeShort(os, version0); // version made by
2046 writeShort(os, version0); // version needed to extract
2047 }
2048 writeShort(os, flag); // general purpose bit flag
2049 writeShort(os, method); // compression method
2050 // last modification time
2051 writeInt(os, (int)javaToDosTime(mtime));
2052 writeInt(os, crc); // crc-32
2053 writeInt(os, csize0); // compressed size
2054 writeInt(os, size0); // uncompressed size
2055 writeShort(os, nlen);
2056 writeShort(os, elen + elen64 + elenNTFS + elenEXTT);
2057
2058 if (comment != null) {
2059 writeShort(os, Math.min(clen, 0xffff));
2060 } else {
2061 writeShort(os, 0);
2062 }
2063 writeShort(os, 0); // starting disk number
2064 writeShort(os, 0); // internal file attributes (unused)
2065 writeInt(os, 0); // external file attributes (unused)
2066 writeInt(os, locoff0); // relative offset of local header
2067 writeBytes(os, zname, 1, nlen);
2068 if (elen64 != 0) {
2069 writeShort(os, EXTID_ZIP64);// Zip64 extra
2070 writeShort(os, elen64 - 4); // size of "this" extra block
2071 if (size0 == ZIP64_MINVAL)
2072 writeLong(os, size);
2073 if (csize0 == ZIP64_MINVAL)
2074 writeLong(os, csize);
2075 if (locoff0 == ZIP64_MINVAL)
2076 writeLong(os, locoff);
2077 }
2078 if (elenNTFS != 0) {
2079 writeShort(os, EXTID_NTFS);
2080 writeShort(os, elenNTFS - 4);
2081 writeInt(os, 0); // reserved
2082 writeShort(os, 0x0001); // NTFS attr tag
2083 writeShort(os, 24);
2084 writeLong(os, javaToWinTime(mtime));
2085 writeLong(os, javaToWinTime(atime));
2086 writeLong(os, javaToWinTime(ctime));
2087 }
2088 if (elenEXTT != 0) {
2089 writeShort(os, EXTID_EXTT);
2090 writeShort(os, elenEXTT - 4);
2091 if (ctime == -1)
2092 os.write(0x3); // mtime and atime
2093 else
2094 os.write(0x7); // mtime, atime and ctime
2095 writeInt(os, javaToUnixTime(mtime));
2096 }
2097 if (extra != null) // whatever not recognized
2098 writeBytes(os, extra);
2099 if (comment != null) //TBD: 0, Math.min(commentBytes.length, 0xffff));
2100 writeBytes(os, comment);
2101 return CENHDR + nlen + elen + clen + elen64 + elenNTFS + elenEXTT;
2102 }
2103
2104 ///////////////////// LOC //////////////////////
2105
2106 int writeLOC(OutputStream os) throws IOException {
2107 int version0 = version();
2108 byte[] zname = isdir ? toDirectoryPath(name) : name;
2109 int nlen = (zname != null) ? zname.length - 1 : 0; // [0] is slash
2110 int elen = (extra != null) ? extra.length : 0;
2111 boolean foundExtraTime = false; // if extra timestamp present
2112 int eoff = 0;
2113 int elen64 = 0;
2114 int elenEXTT = 0;
2115 int elenNTFS = 0;
2116 writeInt(os, LOCSIG); // LOC header signature
2117 if ((flag & FLAG_DATADESCR) != 0) {
2118 writeShort(os, version0); // version needed to extract
2119 writeShort(os, flag); // general purpose bit flag
2120 writeShort(os, method); // compression method
2121 // last modification time
2122 writeInt(os, (int)javaToDosTime(mtime));
2123 // store size, uncompressed size, and crc-32 in data descriptor
2124 // immediately following compressed entry data
2125 writeInt(os, 0);
2126 writeInt(os, 0);
2127 writeInt(os, 0);
2128 } else {
2129 if (csize >= ZIP64_MINVAL || size >= ZIP64_MINVAL) {
2130 elen64 = 20; //headid(2) + size(2) + size(8) + csize(8)
2131 writeShort(os, 45); // ver 4.5 for zip64
2132 } else {
2133 writeShort(os, version0); // version needed to extract
2134 }
2135 writeShort(os, flag); // general purpose bit flag
2136 writeShort(os, method); // compression method
2137 // last modification time
2138 writeInt(os, (int)javaToDosTime(mtime));
2139 writeInt(os, crc); // crc-32
2140 if (elen64 != 0) {
2141 writeInt(os, ZIP64_MINVAL);
2142 writeInt(os, ZIP64_MINVAL);
2143 } else {
2144 writeInt(os, csize); // compressed size
2145 writeInt(os, size); // uncompressed size
2146 }
2147 }
2148 while (eoff + 4 < elen) {
2149 int tag = SH(extra, eoff);
2150 int sz = SH(extra, eoff + 2);
2151 if (tag == EXTID_EXTT || tag == EXTID_NTFS) {
2152 foundExtraTime = true;
2153 }
2154 eoff += (4 + sz);
2155 }
2156 if (!foundExtraTime) {
2157 if (isWindows) {
2158 elenNTFS = 36; // NTFS, total 36 bytes
2159 } else { // on unix use "ext time"
2160 elenEXTT = 9;
2161 if (atime != -1)
2162 elenEXTT += 4;
2163 if (ctime != -1)
2164 elenEXTT += 4;
2165 }
2166 }
2167 writeShort(os, nlen);
2168 writeShort(os, elen + elen64 + elenNTFS + elenEXTT);
2169 writeBytes(os, zname, 1, nlen);
2170 if (elen64 != 0) {
2171 writeShort(os, EXTID_ZIP64);
2172 writeShort(os, 16);
2173 writeLong(os, size);
2174 writeLong(os, csize);
2175 }
2176 if (elenNTFS != 0) {
2177 writeShort(os, EXTID_NTFS);
2178 writeShort(os, elenNTFS - 4);
2179 writeInt(os, 0); // reserved
2180 writeShort(os, 0x0001); // NTFS attr tag
2181 writeShort(os, 24);
2182 writeLong(os, javaToWinTime(mtime));
2183 writeLong(os, javaToWinTime(atime));
2184 writeLong(os, javaToWinTime(ctime));
2185 }
2186 if (elenEXTT != 0) {
2187 writeShort(os, EXTID_EXTT);
2188 writeShort(os, elenEXTT - 4);// size for the folowing data block
2189 int fbyte = 0x1;
2190 if (atime != -1) // mtime and atime
2191 fbyte |= 0x2;
2192 if (ctime != -1) // mtime, atime and ctime
2193 fbyte |= 0x4;
2194 os.write(fbyte); // flags byte
2195 writeInt(os, javaToUnixTime(mtime));
2196 if (atime != -1)
2197 writeInt(os, javaToUnixTime(atime));
2198 if (ctime != -1)
2199 writeInt(os, javaToUnixTime(ctime));
2200 }
2201 if (extra != null) {
2202 writeBytes(os, extra);
2203 }
2204 return LOCHDR + nlen + elen + elen64 + elenNTFS + elenEXTT;
2205 }
2206
2207 // Data Descriptior
2208 int writeEXT(OutputStream os) throws IOException {
2209 writeInt(os, EXTSIG); // EXT header signature
2210 writeInt(os, crc); // crc-32
2211 if (csize >= ZIP64_MINVAL || size >= ZIP64_MINVAL) {
2212 writeLong(os, csize);
2213 writeLong(os, size);
2214 return 24;
2215 } else {
2216 writeInt(os, csize); // compressed size
2217 writeInt(os, size); // uncompressed size
2218 return 16;
2219 }
2220 }
2221
2222 // read NTFS, UNIX and ZIP64 data from cen.extra
2223 void readExtra(ZipFileSystem zipfs) throws IOException {
2224 if (extra == null)
2225 return;
2226 int elen = extra.length;
2227 int off = 0;
2362 @Override
2363 public FileTime lastModifiedTime() {
2364 return FileTime.fromMillis(mtime);
2365 }
2366
2367 @Override
2368 public long size() {
2369 return size;
2370 }
2371
2372 @Override
2373 public boolean isSymbolicLink() {
2374 return false;
2375 }
2376
2377 @Override
2378 public Object fileKey() {
2379 return null;
2380 }
2381
2382 ///////// zip entry attributes ///////////
2383 public long compressedSize() {
2384 return csize;
2385 }
2386
2387 public long crc() {
2388 return crc;
2389 }
2390
2391 public int method() {
2392 return method;
2393 }
2394
2395 public byte[] extra() {
2396 if (extra != null)
2397 return Arrays.copyOf(extra, extra.length);
2398 return null;
2399 }
2400
2401 public byte[] comment() {
2402 if (comment != null)
2403 return Arrays.copyOf(comment, comment.length);
2404 return null;
2405 }
2406
2407 public String toString() {
2408 StringBuilder sb = new StringBuilder(1024);
2409 Formatter fm = new Formatter(sb);
2410 fm.format(" name : %s%n", new String(name));
2411 fm.format(" creationTime : %tc%n", creationTime().toMillis());
2412 fm.format(" lastAccessTime : %tc%n", lastAccessTime().toMillis());
2413 fm.format(" lastModifiedTime: %tc%n", lastModifiedTime().toMillis());
2414 fm.format(" isRegularFile : %b%n", isRegularFile());
2415 fm.format(" isDirectory : %b%n", isDirectory());
2416 fm.format(" isSymbolicLink : %b%n", isSymbolicLink());
2417 fm.format(" isOther : %b%n", isOther());
2418 fm.format(" fileKey : %s%n", fileKey());
2419 fm.format(" size : %d%n", size());
2420 fm.format(" compressedSize : %d%n", compressedSize());
2421 fm.format(" crc : %x%n", crc());
2422 fm.format(" method : %d%n", method());
2423 fm.close();
2424 return sb.toString();
2425 }
2426 }
2427
2428 // ZIP directory has two issues:
2429 // (1) ZIP spec does not require the ZIP file to include
2430 // directory entry
2431 // (2) all entries are not stored/organized in a "tree"
2432 // structure.
2433 // A possible solution is to build the node tree ourself as
2434 // implemented below.
2435
2436 // default time stamp for pseudo entries
2437 private long zfsDefaultTimeStamp = System.currentTimeMillis();
2438
2439 private void removeFromTree(IndexNode inode) {
2440 IndexNode parent = inodes.get(LOOKUPKEY.as(getParent(inode.name)));
2441 IndexNode child = parent.child;
2442 if (child.equals(inode)) {
|
26 package jdk.nio.zipfs;
27
28 import java.io.BufferedOutputStream;
29 import java.io.ByteArrayInputStream;
30 import java.io.ByteArrayOutputStream;
31 import java.io.EOFException;
32 import java.io.FilterOutputStream;
33 import java.io.IOException;
34 import java.io.InputStream;
35 import java.io.OutputStream;
36 import java.nio.ByteBuffer;
37 import java.nio.MappedByteBuffer;
38 import java.nio.channels.FileChannel;
39 import java.nio.channels.FileLock;
40 import java.nio.channels.ReadableByteChannel;
41 import java.nio.channels.SeekableByteChannel;
42 import java.nio.channels.WritableByteChannel;
43 import java.nio.file.*;
44 import java.nio.file.attribute.FileAttribute;
45 import java.nio.file.attribute.FileTime;
46 import java.nio.file.attribute.GroupPrincipal;
47 import java.nio.file.attribute.PosixFilePermission;
48 import java.nio.file.attribute.UserPrincipal;
49 import java.nio.file.attribute.UserPrincipalLookupService;
50 import java.nio.file.spi.FileSystemProvider;
51 import java.security.AccessController;
52 import java.security.PrivilegedAction;
53 import java.security.PrivilegedActionException;
54 import java.security.PrivilegedExceptionAction;
55 import java.util.*;
56 import java.util.concurrent.locks.ReadWriteLock;
57 import java.util.concurrent.locks.ReentrantReadWriteLock;
58 import java.util.regex.Pattern;
59 import java.util.zip.CRC32;
60 import java.util.zip.Deflater;
61 import java.util.zip.DeflaterOutputStream;
62 import java.util.zip.Inflater;
63 import java.util.zip.InflaterInputStream;
64 import java.util.zip.ZipException;
65
66 import static java.lang.Boolean.TRUE;
67 import static java.nio.file.StandardCopyOption.COPY_ATTRIBUTES;
68 import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
357 if (getInode(path) == null) {
358 throw new NoSuchFileException(toString());
359 }
360
361 } finally {
362 endRead();
363 }
364 }
365
366 void setTimes(byte[] path, FileTime mtime, FileTime atime, FileTime ctime)
367 throws IOException
368 {
369 checkWritable();
370 beginWrite();
371 try {
372 ensureOpen();
373 Entry e = getEntry(path); // ensureOpen checked
374 if (e == null)
375 throw new NoSuchFileException(getString(path));
376 if (e.type == Entry.CEN)
377 e.type = Entry.COPY; // copy e
378 if (mtime != null)
379 e.mtime = mtime.toMillis();
380 if (atime != null)
381 e.atime = atime.toMillis();
382 if (ctime != null)
383 e.ctime = ctime.toMillis();
384 update(e);
385 } finally {
386 endWrite();
387 }
388 }
389
390 void setPermissions(byte[] path, Set<PosixFilePermission> perms)
391 throws IOException
392 {
393 checkWritable();
394 beginWrite();
395 try {
396 ensureOpen();
397 Entry e = getEntry(path); // ensureOpen checked
398 if (e == null) {
399 throw new NoSuchFileException(getString(path));
400 }
401 if (e.type == Entry.CEN) {
402 e.type = Entry.COPY; // copy e
403 }
404 e.posixPerms = perms == null ? -1 : ZipUtils.permsToFlags(perms);
405 update(e);
406 } finally {
407 endWrite();
408 }
409 }
410
411 boolean exists(byte[] path)
412 throws IOException
413 {
414 beginRead();
415 try {
416 ensureOpen();
417 return getInode(path) != null;
418 } finally {
419 endRead();
420 }
421 }
422
423 boolean isDirectory(byte[] path)
424 throws IOException
425 {
426 beginRead();
427 try {
428 IndexNode n = getInode(path);
429 return n != null && n.isDir();
430 } finally {
461 list.add(zpath);
462 child = child.sibling;
463 }
464 return list.iterator();
465 } finally {
466 endWrite();
467 }
468 }
469
470 void createDirectory(byte[] dir, FileAttribute<?>... attrs)
471 throws IOException
472 {
473 checkWritable();
474 // dir = toDirectoryPath(dir);
475 beginWrite();
476 try {
477 ensureOpen();
478 if (dir.length == 0 || exists(dir)) // root dir, or exiting dir
479 throw new FileAlreadyExistsException(getString(dir));
480 checkParents(dir);
481 Entry e = new Entry(dir, Entry.NEW, true, METHOD_STORED, attrs);
482 update(e);
483 } finally {
484 endWrite();
485 }
486 }
487
488 void copyFile(boolean deletesrc, byte[]src, byte[] dst, CopyOption... options)
489 throws IOException
490 {
491 checkWritable();
492 if (Arrays.equals(src, dst))
493 return; // do nothing, src and dst are the same
494
495 beginWrite();
496 try {
497 ensureOpen();
498 Entry eSrc = getEntry(src); // ensureOpen checked
499
500 if (eSrc == null)
501 throw new NoSuchFileException(getString(src));
683 SeekableByteChannel sbc =
684 new EntryOutputChannel(new Entry(e, Entry.NEW));
685 if (options.contains(APPEND)) {
686 try (InputStream is = getInputStream(e)) { // copyover
687 byte[] buf = new byte[8192];
688 ByteBuffer bb = ByteBuffer.wrap(buf);
689 int n;
690 while ((n = is.read(buf)) != -1) {
691 bb.position(0);
692 bb.limit(n);
693 sbc.write(bb);
694 }
695 }
696 }
697 return sbc;
698 }
699 if (!options.contains(CREATE) && !options.contains(CREATE_NEW))
700 throw new NoSuchFileException(getString(path));
701 checkParents(path);
702 return new EntryOutputChannel(
703 new Entry(path, Entry.NEW, false, getCompressMethod(attrs), attrs));
704
705 } finally {
706 endRead();
707 }
708 } else {
709 beginRead();
710 try {
711 ensureOpen();
712 Entry e = getEntry(path);
713 if (e == null || e.isDir())
714 throw new NoSuchFileException(getString(path));
715 try (InputStream is = getInputStream(e)) {
716 // TBD: if (e.size < NNNNN);
717 return new ByteArrayChannel(is.readAllBytes(), true);
718 }
719 } finally {
720 endRead();
721 }
722 }
723 }
748 }
749 } else {
750 if (options.contains(StandardOpenOption.CREATE_NEW)) {
751 throw new FileAlreadyExistsException(getString(path));
752 }
753 if (e.isDir())
754 throw new FileAlreadyExistsException("directory <"
755 + getString(path) + "> exists");
756 }
757 options = new HashSet<>(options);
758 options.remove(StandardOpenOption.CREATE_NEW); // for tmpfile
759 } else if (e == null || e.isDir()) {
760 throw new NoSuchFileException(getString(path));
761 }
762
763 final boolean isFCH = (e != null && e.type == Entry.FILECH);
764 final Path tmpfile = isFCH ? e.file : getTempPathForEntry(path);
765 final FileChannel fch = tmpfile.getFileSystem()
766 .provider()
767 .newFileChannel(tmpfile, options, attrs);
768 final Entry u = isFCH ? e : new Entry(path, tmpfile, Entry.FILECH, attrs);
769 if (forWrite) {
770 u.flag = FLAG_DATADESCR;
771 u.method = getCompressMethod(attrs);
772 }
773 // is there a better way to hook into the FileChannel's close method?
774 return new FileChannel() {
775 public int write(ByteBuffer src) throws IOException {
776 return fch.write(src);
777 }
778 public long write(ByteBuffer[] srcs, int offset, int length)
779 throws IOException
780 {
781 return fch.write(srcs, offset, length);
782 }
783 public long position() throws IOException {
784 return fch.position();
785 }
786 public FileChannel position(long newPosition)
787 throws IOException
788 {
1877
1878 IndexNode() {}
1879 IndexNode sibling;
1880 IndexNode child; // 1st child
1881 }
1882
1883 static class Entry extends IndexNode implements ZipFileAttributes {
1884
1885 static final int CEN = 1; // entry read from cen
1886 static final int NEW = 2; // updated contents in bytes or file
1887 static final int FILECH = 3; // fch update in "file"
1888 static final int COPY = 4; // copy of a CEN entry
1889
1890 byte[] bytes; // updated content bytes
1891 Path file; // use tmp file to store bytes;
1892 int type = CEN; // default is the entry read from cen
1893
1894 // entry attributes
1895 int version;
1896 int flag;
1897 int posixPerms = -1; // posix permissions
1898 int method = -1; // compression method
1899 long mtime = -1; // last modification time (in DOS time)
1900 long atime = -1; // last access time
1901 long ctime = -1; // create time
1902 long crc = -1; // crc-32 of entry data
1903 long csize = -1; // compressed size of entry data
1904 long size = -1; // uncompressed size of entry data
1905 byte[] extra;
1906
1907 // cen
1908
1909 // these fields are not used by anyone and writeCEN uses "0"
1910 // int versionMade;
1911 // int disk;
1912 // int attrs;
1913 // long attrsEx;
1914 long locoff;
1915 byte[] comment;
1916
1917 Entry() {}
1918
1919 Entry(byte[] name, boolean isdir, int method) {
1920 name(name);
1921 this.isdir = isdir;
1922 this.mtime = this.ctime = this.atime = System.currentTimeMillis();
1923 this.crc = 0;
1924 this.size = 0;
1925 this.csize = 0;
1926 this.method = method;
1927 }
1928
1929 @SuppressWarnings("unchecked")
1930 Entry(byte[] name, int type, boolean isdir, int method, FileAttribute<?>... attrs) {
1931 this(name, isdir, method);
1932 this.type = type;
1933 for (FileAttribute<?> attr : attrs) {
1934 String attrName = attr.name();
1935 if (attrName.equals("posix:permissions") || attrName.equals("unix:permissions")) {
1936 posixPerms = ZipUtils.permsToFlags((Set<PosixFilePermission>)attr.value());
1937 }
1938 }
1939 }
1940
1941 Entry(Entry e, int type) {
1942 name(e.name);
1943 this.isdir = e.isdir;
1944 this.version = e.version;
1945 this.ctime = e.ctime;
1946 this.atime = e.atime;
1947 this.mtime = e.mtime;
1948 this.crc = e.crc;
1949 this.size = e.size;
1950 this.csize = e.csize;
1951 this.method = e.method;
1952 this.extra = e.extra;
1953 /*
1954 this.versionMade = e.versionMade;
1955 this.disk = e.disk;
1956 this.attrs = e.attrs;
1957 this.attrsEx = e.attrsEx;
1958 */
1959 this.locoff = e.locoff;
1960 this.comment = e.comment;
1961 this.posixPerms = e.posixPerms;
1962 this.type = type;
1963 }
1964
1965 @SuppressWarnings("unchecked")
1966 Entry(byte[] name, Path file, int type, FileAttribute<?>... attrs) {
1967 this(name, type, false, METHOD_STORED);
1968 this.file = file;
1969 for (FileAttribute<?> attr : attrs) {
1970 String attrName = attr.name();
1971 if (attrName.equals("posix:permissions") || attrName.equals("unix:permissions")) {
1972 posixPerms = ZipUtils.permsToFlags((Set<PosixFilePermission>)attr.value());
1973 }
1974 }
1975 }
1976
1977 int version(boolean zip64) throws ZipException {
1978 if (zip64) {
1979 return 45;
1980 }
1981 if (method == METHOD_DEFLATED)
1982 return 20;
1983 else if (method == METHOD_STORED)
1984 return 10;
1985 throw new ZipException("unsupported compression method");
1986 }
1987
1988 /**
1989 * Adds information about compatibility of file attribute information
1990 * to a version value.
1991 */
1992 int versionMadeBy(int version) {
1993 return (posixPerms < 0) ? version :
1994 VERSION_BASE_UNIX | (version & 0xff);
1995 }
1996
1997 ///////////////////// CEN //////////////////////
1998 static Entry readCEN(ZipFileSystem zipfs, IndexNode inode)
1999 throws IOException
2000 {
2001 return new Entry().cen(zipfs, inode);
2002 }
2003
2004 private Entry cen(ZipFileSystem zipfs, IndexNode inode)
2005 throws IOException
2006 {
2007 byte[] cen = zipfs.cen;
2008 int pos = inode.pos;
2009 if (!cenSigAt(cen, pos))
2010 zerror("invalid CEN header (bad signature)");
2011 version = CENVER(cen, pos);
2012 flag = CENFLG(cen, pos);
2013 method = CENHOW(cen, pos);
2014 mtime = dosToJavaTime(CENTIM(cen, pos));
2015 crc = CENCRC(cen, pos);
2016 csize = CENSIZ(cen, pos);
2017 size = CENLEN(cen, pos);
2018 int nlen = CENNAM(cen, pos);
2019 int elen = CENEXT(cen, pos);
2020 int clen = CENCOM(cen, pos);
2021 /*
2022 versionMade = CENVEM(cen, pos);
2023 disk = CENDSK(cen, pos);
2024 attrs = CENATT(cen, pos);
2025 attrsEx = CENATX(cen, pos);
2026 */
2027 if (CENVEM_FA(cen, pos) == FILE_ATTRIBUTES_UNIX) {
2028 posixPerms = CENATX_PERMS(cen, pos) & 0xFFF; // 12 bits for setuid, setgid, sticky + perms
2029 }
2030 locoff = CENOFF(cen, pos);
2031 pos += CENHDR;
2032 this.name = inode.name;
2033 this.isdir = inode.isdir;
2034 this.hashcode = inode.hashcode;
2035
2036 pos += nlen;
2037 if (elen > 0) {
2038 extra = Arrays.copyOfRange(cen, pos, pos + elen);
2039 pos += elen;
2040 readExtra(zipfs);
2041 }
2042 if (clen > 0) {
2043 comment = Arrays.copyOfRange(cen, pos, pos + clen);
2044 }
2045 return this;
2046 }
2047
2048 int writeCEN(OutputStream os) throws IOException {
2049 long csize0 = csize;
2050 long size0 = size;
2051 long locoff0 = locoff;
2052 int elen64 = 0; // extra for ZIP64
2053 int elenNTFS = 0; // extra for NTFS (a/c/mtime)
2054 int elenEXTT = 0; // extra for Extended Timestamp
2055 boolean foundExtraTime = false; // if time stamp NTFS, EXTT present
2056
2057 byte[] zname = isdir ? toDirectoryPath(name) : name;
2058
2059 // confirm size/length
2060 int nlen = (zname != null) ? zname.length - 1 : 0; // name has [0] as "slash"
2061 int elen = (extra != null) ? extra.length : 0;
2062 int eoff = 0;
2063 int clen = (comment != null) ? comment.length : 0;
2064 if (csize >= ZIP64_MINVAL) {
2065 csize0 = ZIP64_MINVAL;
2066 elen64 += 8; // csize(8)
2067 }
2068 if (size >= ZIP64_MINVAL) {
2069 size0 = ZIP64_MINVAL; // size(8)
2070 elen64 += 8;
2071 }
2072 if (locoff >= ZIP64_MINVAL) {
2073 locoff0 = ZIP64_MINVAL;
2074 elen64 += 8; // offset(8)
2075 }
2076 if (elen64 != 0) {
2077 elen64 += 4; // header and data sz 4 bytes
2078 }
2079 boolean zip64 = (elen64 != 0);
2080 int version0 = version(zip64);
2081 while (eoff + 4 < elen) {
2082 int tag = SH(extra, eoff);
2083 int sz = SH(extra, eoff + 2);
2084 if (tag == EXTID_EXTT || tag == EXTID_NTFS) {
2085 foundExtraTime = true;
2086 }
2087 eoff += (4 + sz);
2088 }
2089 if (!foundExtraTime) {
2090 if (isWindows) { // use NTFS
2091 elenNTFS = 36; // total 36 bytes
2092 } else { // Extended Timestamp otherwise
2093 elenEXTT = 9; // only mtime in cen
2094 }
2095 }
2096 writeInt(os, CENSIG); // CEN header signature
2097 writeShort(os, versionMadeBy(version0)); // version made by
2098 writeShort(os, version0); // version needed to extract
2099 writeShort(os, flag); // general purpose bit flag
2100 writeShort(os, method); // compression method
2101 // last modification time
2102 writeInt(os, (int)javaToDosTime(mtime));
2103 writeInt(os, crc); // crc-32
2104 writeInt(os, csize0); // compressed size
2105 writeInt(os, size0); // uncompressed size
2106 writeShort(os, nlen);
2107 writeShort(os, elen + elen64 + elenNTFS + elenEXTT);
2108
2109 if (comment != null) {
2110 writeShort(os, Math.min(clen, 0xffff));
2111 } else {
2112 writeShort(os, 0);
2113 }
2114 writeShort(os, 0); // starting disk number
2115 writeShort(os, 0); // internal file attributes (unused)
2116 writeInt(os, posixPerms > 0 ? posixPerms << 16 : 0); // external file
2117 // attributes, used for storing posix
2118 // permissions
2119 writeInt(os, locoff0); // relative offset of local header
2120 writeBytes(os, zname, 1, nlen);
2121 if (zip64) {
2122 writeShort(os, EXTID_ZIP64);// Zip64 extra
2123 writeShort(os, elen64 - 4); // size of "this" extra block
2124 if (size0 == ZIP64_MINVAL)
2125 writeLong(os, size);
2126 if (csize0 == ZIP64_MINVAL)
2127 writeLong(os, csize);
2128 if (locoff0 == ZIP64_MINVAL)
2129 writeLong(os, locoff);
2130 }
2131 if (elenNTFS != 0) {
2132 writeShort(os, EXTID_NTFS);
2133 writeShort(os, elenNTFS - 4);
2134 writeInt(os, 0); // reserved
2135 writeShort(os, 0x0001); // NTFS attr tag
2136 writeShort(os, 24);
2137 writeLong(os, javaToWinTime(mtime));
2138 writeLong(os, javaToWinTime(atime));
2139 writeLong(os, javaToWinTime(ctime));
2140 }
2141 if (elenEXTT != 0) {
2142 writeShort(os, EXTID_EXTT);
2143 writeShort(os, elenEXTT - 4);
2144 if (ctime == -1)
2145 os.write(0x3); // mtime and atime
2146 else
2147 os.write(0x7); // mtime, atime and ctime
2148 writeInt(os, javaToUnixTime(mtime));
2149 }
2150 if (extra != null) // whatever not recognized
2151 writeBytes(os, extra);
2152 if (comment != null) //TBD: 0, Math.min(commentBytes.length, 0xffff));
2153 writeBytes(os, comment);
2154 return CENHDR + nlen + elen + clen + elen64 + elenNTFS + elenEXTT;
2155 }
2156
2157 ///////////////////// LOC //////////////////////
2158
2159 int writeLOC(OutputStream os) throws IOException {
2160 byte[] zname = isdir ? toDirectoryPath(name) : name;
2161 int nlen = (zname != null) ? zname.length - 1 : 0; // [0] is slash
2162 int elen = (extra != null) ? extra.length : 0;
2163 boolean foundExtraTime = false; // if extra timestamp present
2164 int eoff = 0;
2165 int elen64 = 0;
2166 boolean zip64 = false;
2167 int elenEXTT = 0;
2168 int elenNTFS = 0;
2169 writeInt(os, LOCSIG); // LOC header signature
2170 if ((flag & FLAG_DATADESCR) != 0) {
2171 writeShort(os, version(zip64)); // version needed to extract
2172 writeShort(os, flag); // general purpose bit flag
2173 writeShort(os, method); // compression method
2174 // last modification time
2175 writeInt(os, (int)javaToDosTime(mtime));
2176 // store size, uncompressed size, and crc-32 in data descriptor
2177 // immediately following compressed entry data
2178 writeInt(os, 0);
2179 writeInt(os, 0);
2180 writeInt(os, 0);
2181 } else {
2182 if (csize >= ZIP64_MINVAL || size >= ZIP64_MINVAL) {
2183 elen64 = 20; //headid(2) + size(2) + size(8) + csize(8)
2184 zip64 = true;
2185 }
2186 writeShort(os, version(zip64)); // version needed to extract
2187 writeShort(os, flag); // general purpose bit flag
2188 writeShort(os, method); // compression method
2189 // last modification time
2190 writeInt(os, (int)javaToDosTime(mtime));
2191 writeInt(os, crc); // crc-32
2192 if (zip64) {
2193 writeInt(os, ZIP64_MINVAL);
2194 writeInt(os, ZIP64_MINVAL);
2195 } else {
2196 writeInt(os, csize); // compressed size
2197 writeInt(os, size); // uncompressed size
2198 }
2199 }
2200 while (eoff + 4 < elen) {
2201 int tag = SH(extra, eoff);
2202 int sz = SH(extra, eoff + 2);
2203 if (tag == EXTID_EXTT || tag == EXTID_NTFS) {
2204 foundExtraTime = true;
2205 }
2206 eoff += (4 + sz);
2207 }
2208 if (!foundExtraTime) {
2209 if (isWindows) {
2210 elenNTFS = 36; // NTFS, total 36 bytes
2211 } else { // on unix use "ext time"
2212 elenEXTT = 9;
2213 if (atime != -1)
2214 elenEXTT += 4;
2215 if (ctime != -1)
2216 elenEXTT += 4;
2217 }
2218 }
2219 writeShort(os, nlen);
2220 writeShort(os, elen + elen64 + elenNTFS + elenEXTT);
2221 writeBytes(os, zname, 1, nlen);
2222 if (zip64) {
2223 writeShort(os, EXTID_ZIP64);
2224 writeShort(os, 16);
2225 writeLong(os, size);
2226 writeLong(os, csize);
2227 }
2228 if (elenNTFS != 0) {
2229 writeShort(os, EXTID_NTFS);
2230 writeShort(os, elenNTFS - 4);
2231 writeInt(os, 0); // reserved
2232 writeShort(os, 0x0001); // NTFS attr tag
2233 writeShort(os, 24);
2234 writeLong(os, javaToWinTime(mtime));
2235 writeLong(os, javaToWinTime(atime));
2236 writeLong(os, javaToWinTime(ctime));
2237 }
2238 if (elenEXTT != 0) {
2239 writeShort(os, EXTID_EXTT);
2240 writeShort(os, elenEXTT - 4);// size for the folowing data block
2241 int fbyte = 0x1;
2242 if (atime != -1) // mtime and atime
2243 fbyte |= 0x2;
2244 if (ctime != -1) // mtime, atime and ctime
2245 fbyte |= 0x4;
2246 os.write(fbyte); // flags byte
2247 writeInt(os, javaToUnixTime(mtime));
2248 if (atime != -1)
2249 writeInt(os, javaToUnixTime(atime));
2250 if (ctime != -1)
2251 writeInt(os, javaToUnixTime(ctime));
2252 }
2253 if (extra != null) {
2254 writeBytes(os, extra);
2255 }
2256 return LOCHDR + nlen + elen + elen64 + elenNTFS + elenEXTT;
2257 }
2258
2259 // Data Descriptor
2260 int writeEXT(OutputStream os) throws IOException {
2261 writeInt(os, EXTSIG); // EXT header signature
2262 writeInt(os, crc); // crc-32
2263 if (csize >= ZIP64_MINVAL || size >= ZIP64_MINVAL) {
2264 writeLong(os, csize);
2265 writeLong(os, size);
2266 return 24;
2267 } else {
2268 writeInt(os, csize); // compressed size
2269 writeInt(os, size); // uncompressed size
2270 return 16;
2271 }
2272 }
2273
2274 // read NTFS, UNIX and ZIP64 data from cen.extra
2275 void readExtra(ZipFileSystem zipfs) throws IOException {
2276 if (extra == null)
2277 return;
2278 int elen = extra.length;
2279 int off = 0;
2414 @Override
2415 public FileTime lastModifiedTime() {
2416 return FileTime.fromMillis(mtime);
2417 }
2418
2419 @Override
2420 public long size() {
2421 return size;
2422 }
2423
2424 @Override
2425 public boolean isSymbolicLink() {
2426 return false;
2427 }
2428
2429 @Override
2430 public Object fileKey() {
2431 return null;
2432 }
2433
2434 ///////// posix file attributes ///////////
2435
2436 @Override
2437 public UserPrincipal owner() {
2438 throw new UnsupportedOperationException(
2439 "ZipFileSystem does not support owner.");
2440 }
2441
2442 @Override
2443 public GroupPrincipal group() {
2444 throw new UnsupportedOperationException(
2445 "ZipFileSystem does not support group.");
2446 }
2447
2448 @Override
2449 public Set<PosixFilePermission> permissions() {
2450 if (posixPerms == -1) {
2451 // in case there are no Posix permissions associated with the
2452 // entry, we should not return an empty set of permissions
2453 // because that would be an explicit set of permissions meaning
2454 // no permissions for anyone
2455 throw new UnsupportedOperationException(
2456 "No posix permissions associated with zip entry.");
2457 }
2458 return ZipUtils.permsFromFlags(posixPerms);
2459 }
2460
2461 ///////// zip file attributes ///////////
2462
2463 @Override
2464 public long compressedSize() {
2465 return csize;
2466 }
2467
2468 @Override
2469 public long crc() {
2470 return crc;
2471 }
2472
2473 @Override
2474 public int method() {
2475 return method;
2476 }
2477
2478 @Override
2479 public byte[] extra() {
2480 if (extra != null)
2481 return Arrays.copyOf(extra, extra.length);
2482 return null;
2483 }
2484
2485 @Override
2486 public byte[] comment() {
2487 if (comment != null)
2488 return Arrays.copyOf(comment, comment.length);
2489 return null;
2490 }
2491
2492 @Override
2493 public String toString() {
2494 StringBuilder sb = new StringBuilder(1024);
2495 Formatter fm = new Formatter(sb);
2496 fm.format(" name : %s%n", new String(name));
2497 fm.format(" creationTime : %tc%n", creationTime().toMillis());
2498 fm.format(" lastAccessTime : %tc%n", lastAccessTime().toMillis());
2499 fm.format(" lastModifiedTime: %tc%n", lastModifiedTime().toMillis());
2500 fm.format(" isRegularFile : %b%n", isRegularFile());
2501 fm.format(" isDirectory : %b%n", isDirectory());
2502 fm.format(" isSymbolicLink : %b%n", isSymbolicLink());
2503 fm.format(" isOther : %b%n", isOther());
2504 fm.format(" fileKey : %s%n", fileKey());
2505 fm.format(" size : %d%n", size());
2506 fm.format(" compressedSize : %d%n", compressedSize());
2507 fm.format(" crc : %x%n", crc());
2508 fm.format(" method : %d%n", method());
2509 if (posixPerms != -1) {
2510 fm.format(" permissions : %s%n", permissions());
2511 }
2512 fm.close();
2513 return sb.toString();
2514 }
2515 }
2516
2517 // ZIP directory has two issues:
2518 // (1) ZIP spec does not require the ZIP file to include
2519 // directory entry
2520 // (2) all entries are not stored/organized in a "tree"
2521 // structure.
2522 // A possible solution is to build the node tree ourself as
2523 // implemented below.
2524
2525 // default time stamp for pseudo entries
2526 private long zfsDefaultTimeStamp = System.currentTimeMillis();
2527
2528 private void removeFromTree(IndexNode inode) {
2529 IndexNode parent = inodes.get(LOOKUPKEY.as(getParent(inode.name)));
2530 IndexNode child = parent.child;
2531 if (child.equals(inode)) {
|