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

Print this page




  62 /**
  63  * A FileSystem built on a zip file
  64  *
  65  * @author Xueming Shen
  66  */
  67 
  68 class ZipFileSystem extends FileSystem {
  69 
  70     private final ZipFileSystemProvider provider;
  71     private final Path zfpath;
  72     final ZipCoder zc;
  73     private final boolean noExtt;        // see readExtra()
  74     private final ZipPath rootdir;
  75     // configurable by env map
  76     private final boolean useTempFile;   // use a temp file for newOS, default
  77                                          // is to use BAOS for better performance
  78     private boolean readOnly = false;    // readonly file system
  79     private static final boolean isWindows = AccessController.doPrivileged(
  80             (PrivilegedAction<Boolean>) () -> System.getProperty("os.name")
  81                                                     .startsWith("Windows"));

  82 
  83     ZipFileSystem(ZipFileSystemProvider provider,
  84                   Path zfpath,
  85                   Map<String, ?> env)  throws IOException
  86     {
  87         // create a new zip if not exists
  88         boolean createNew = "true".equals(env.get("create"));
  89         // default encoding for name/comment
  90         String nameEncoding = env.containsKey("encoding") ?
  91                               (String)env.get("encoding") : "UTF-8";
  92         this.noExtt = "false".equals(env.get("zipinfo-time"));
  93         this.useTempFile  = TRUE.equals(env.get("useTempFile"));

  94         this.provider = provider;
  95         this.zfpath = zfpath;
  96         if (Files.notExists(zfpath)) {
  97             if (createNew) {
  98                 try (OutputStream os = Files.newOutputStream(zfpath, CREATE_NEW, WRITE)) {
  99                     new END().write(os, 0);
 100                 }
 101             } else {
 102                 throw new FileSystemNotFoundException(zfpath.toString());
 103             }
 104         }
 105         // sm and existence check
 106         zfpath.getFileSystem().provider().checkAccess(zfpath, AccessMode.READ);
 107         boolean writeable = AccessController.doPrivileged(
 108             (PrivilegedAction<Boolean>) () ->  Files.isWritable(zfpath));
 109         this.readOnly = !writeable;
 110         this.zc = ZipCoder.get(nameEncoding);
 111         this.rootdir = new ZipPath(this, new byte[]{'/'});
 112         this.ch = Files.newByteChannel(zfpath, READ);
 113         try {
 114             this.cen = initCEN();
 115         } catch (IOException x) {
 116             try {
 117                 this.ch.close();
 118             } catch (IOException xx) {
 119                 x.addSuppressed(xx);


 983             int len = buf.length - off;
 984             if (readFullyAt(buf, off, len, pos + off) != len)
 985                 zerror("zip END header not found");
 986 
 987             // Now scan the block backwards for END header signature
 988             for (int i = buf.length - ENDHDR; i >= 0; i--) {
 989                 if (buf[i+0] == (byte)'P'    &&
 990                     buf[i+1] == (byte)'K'    &&
 991                     buf[i+2] == (byte)'\005' &&
 992                     buf[i+3] == (byte)'\006' &&
 993                     (pos + i + ENDHDR + ENDCOM(buf, i) == ziplen)) {
 994                     // Found END header
 995                     buf = Arrays.copyOfRange(buf, i, i + ENDHDR);
 996                     END end = new END();
 997                     end.endsub = ENDSUB(buf);
 998                     end.centot = ENDTOT(buf);
 999                     end.cenlen = ENDSIZ(buf);
1000                     end.cenoff = ENDOFF(buf);
1001                     end.comlen = ENDCOM(buf);
1002                     end.endpos = pos + i;
1003                     if (end.cenlen == ZIP64_MINVAL ||
1004                         end.cenoff == ZIP64_MINVAL ||
1005                         end.centot == ZIP64_MINVAL32)
1006                     {
1007                         // need to find the zip64 end;
1008                         byte[] loc64 = new byte[ZIP64_LOCHDR];
1009                         if (readFullyAt(loc64, 0, loc64.length, end.endpos - ZIP64_LOCHDR)
1010                             != loc64.length) {


1011                             return end;
1012                         }
1013                         long end64pos = ZIP64_LOCOFF(loc64);
1014                         byte[] end64buf = new byte[ZIP64_ENDHDR];
1015                         if (readFullyAt(end64buf, 0, end64buf.length, end64pos)
1016                             != end64buf.length) {

1017                             return end;
1018                         }
1019                         // end64 found, re-calcualte everything.
1020                         end.cenlen = ZIP64_ENDSIZ(end64buf);
1021                         end.cenoff = ZIP64_ENDOFF(end64buf);
1022                         end.centot = (int)ZIP64_ENDTOT(end64buf); // assume total < 2g
1023                         end.endpos = end64pos;




1024                     }





1025                     return end;
1026                 }
1027             }
1028         }
1029         zerror("zip END header not found");
1030         return null; //make compiler happy
1031     }
1032 
1033     // Reads zip file central directory. Returns the file position of first
1034     // CEN header, otherwise returns -1 if an error occurred. If zip->msg != NULL
1035     // then the error was a zip format error and zip->msg has the error text.
1036     // Always pass in -1 for knownTotal; it's used for a recursive call.
1037     private byte[] initCEN() throws IOException {
1038         end = findEND();
1039         if (end.endpos == 0) {
1040             inodes = new LinkedHashMap<>(10);
1041             locpos = 0;
1042             buildNodeTree();
1043             return null;         // only END header present
1044         }


1175             // use e.csize,  LOCSIZ(buf) is zero if FLAG_DATADESCR is on
1176             // size += LOCNAM(buf) + LOCEXT(buf) + LOCSIZ(buf);
1177             size += LOCNAM(buf) + LOCEXT(buf) + e.csize;
1178             written = LOCHDR + size;
1179         }
1180         int n;
1181         while (size > 0 &&
1182             (n = (int)readFullyAt(buf, 0, buf.length, locoff)) != -1)
1183         {
1184             if (size < n)
1185                 n = (int)size;
1186             os.write(buf, 0, n);
1187             size -= n;
1188             locoff += n;
1189         }
1190         return written;
1191     }
1192 
1193     // sync the zip file system, if there is any udpate
1194     private void sync() throws IOException {
1195         //System.out.printf("->sync(%s) starting....!%n", toString());
1196         // check ex-closer
1197         if (!exChClosers.isEmpty()) {
1198             for (ExChannelCloser ecc : exChClosers) {
1199                 if (ecc.streams.isEmpty()) {
1200                     ecc.ch.close();
1201                     Files.delete(ecc.path);
1202                     exChClosers.remove(ecc);
1203                 }
1204             }
1205         }
1206         if (!hasUpdate)
1207             return;
1208         Path tmpFile = createTempFileInSameDirectoryAs(zfpath);
1209         try (OutputStream os = new BufferedOutputStream(Files.newOutputStream(tmpFile, WRITE)))
1210         {
1211             ArrayList<Entry> elist = new ArrayList<>(inodes.size());
1212             long written = 0;
1213             byte[] buf = new byte[8192];
1214             Entry e = null;
1215 


1265                     if (inode.pos == -1) {
1266                         continue;               // pseudo directory node
1267                     }
1268                     e = Entry.readCEN(this, inode);
1269                     try {
1270                         written += copyLOCEntry(e, false, os, written, buf);
1271                         elist.add(e);
1272                     } catch (IOException x) {
1273                         x.printStackTrace();    // skip any wrong entry
1274                     }
1275                 }
1276             }
1277 
1278             // now write back the cen and end table
1279             end.cenoff = written;
1280             for (Entry entry : elist) {
1281                 written += entry.writeCEN(os);
1282             }
1283             end.centot = elist.size();
1284             end.cenlen = written - end.cenoff;
1285             end.write(os, written);
1286         }
1287         if (!streams.isEmpty()) {
1288             //
1289             // TBD: ExChannelCloser should not be necessary if we only
1290             // sync when being closed, all streams should have been
1291             // closed already. Keep the logic here for now.
1292             //
1293             // There are outstanding input streams open on existing "ch",
1294             // so, don't close the "cha" and delete the "file for now, let
1295             // the "ex-channel-closer" to handle them
1296             ExChannelCloser ecc = new ExChannelCloser(
1297                                       createTempFileInSameDirectoryAs(zfpath),
1298                                       ch,
1299                                       streams);
1300             Files.move(zfpath, ecc.path, REPLACE_EXISTING);
1301             exChClosers.add(ecc);
1302             streams = Collections.synchronizedSet(new HashSet<InputStream>());
1303         } else {
1304             ch.close();
1305             Files.delete(zfpath);


1695         }
1696     }
1697 
1698     // End of central directory record
1699     static class END {
1700         // these 2 fields are not used by anyone and write() uses "0"
1701         // int  disknum;
1702         // int  sdisknum;
1703         int  endsub;     // endsub
1704         int  centot;     // 4 bytes
1705         long cenlen;     // 4 bytes
1706         long cenoff;     // 4 bytes
1707         int  comlen;     // comment length
1708         byte[] comment;
1709 
1710         /* members of Zip64 end of central directory locator */
1711         // int diskNum;
1712         long endpos;
1713         // int disktot;
1714 
1715         void write(OutputStream os, long offset) throws IOException {
1716             boolean hasZip64 = false;
1717             long xlen = cenlen;
1718             long xoff = cenoff;
1719             if (xlen >= ZIP64_MINVAL) {
1720                 xlen = ZIP64_MINVAL;
1721                 hasZip64 = true;
1722             }
1723             if (xoff >= ZIP64_MINVAL) {
1724                 xoff = ZIP64_MINVAL;
1725                 hasZip64 = true;
1726             }
1727             int count = centot;
1728             if (count >= ZIP64_MINVAL32) {
1729                 count = ZIP64_MINVAL32;
1730                 hasZip64 = true;
1731             }
1732             if (hasZip64) {
1733                 long off64 = offset;
1734                 //zip64 end of central directory record
1735                 writeInt(os, ZIP64_ENDSIG);       // zip64 END record signature
1736                 writeLong(os, ZIP64_ENDHDR - 12); // size of zip64 end
1737                 writeShort(os, 45);               // version made by
1738                 writeShort(os, 45);               // version needed to extract
1739                 writeInt(os, 0);                  // number of this disk
1740                 writeInt(os, 0);                  // central directory start disk
1741                 writeLong(os, centot);            // number of directory entires on disk
1742                 writeLong(os, centot);            // number of directory entires
1743                 writeLong(os, cenlen);            // length of central directory
1744                 writeLong(os, cenoff);            // offset of central directory
1745 
1746                 //zip64 end of central directory locator
1747                 writeInt(os, ZIP64_LOCSIG);       // zip64 END locator signature
1748                 writeInt(os, 0);                  // zip64 END start disk
1749                 writeLong(os, off64);             // offset of zip64 END
1750                 writeInt(os, 1);                  // total number of disks (?)
1751             }
1752             writeInt(os, ENDSIG);                 // END record signature
1753             writeShort(os, 0);                    // number of this disk
1754             writeShort(os, 0);                    // central directory start disk
1755             writeShort(os, count);                // number of directory entries on disk
1756             writeShort(os, count);                // total number of directory entries
1757             writeInt(os, xlen);                   // length of central directory
1758             writeInt(os, xoff);                   // offset of central directory
1759             if (comment != null) {            // zip file comment
1760                 writeShort(os, comment.length);
1761                 writeBytes(os, comment);
1762             } else {




  62 /**
  63  * A FileSystem built on a zip file
  64  *
  65  * @author Xueming Shen
  66  */
  67 
  68 class ZipFileSystem extends FileSystem {
  69 
  70     private final ZipFileSystemProvider provider;
  71     private final Path zfpath;
  72     final ZipCoder zc;
  73     private final boolean noExtt;        // see readExtra()
  74     private final ZipPath rootdir;
  75     // configurable by env map
  76     private final boolean useTempFile;   // use a temp file for newOS, default
  77                                          // is to use BAOS for better performance
  78     private boolean readOnly = false;    // readonly file system
  79     private static final boolean isWindows = AccessController.doPrivileged(
  80             (PrivilegedAction<Boolean>) () -> System.getProperty("os.name")
  81                                                     .startsWith("Windows"));
  82     private final boolean forceEnd64;
  83 
  84     ZipFileSystem(ZipFileSystemProvider provider,
  85                   Path zfpath,
  86                   Map<String, ?> env)  throws IOException
  87     {
  88         // create a new zip if not exists
  89         boolean createNew = "true".equals(env.get("create"));
  90         // default encoding for name/comment
  91         String nameEncoding = env.containsKey("encoding") ?
  92                               (String)env.get("encoding") : "UTF-8";
  93         this.noExtt = "false".equals(env.get("zipinfo-time"));
  94         this.useTempFile  = TRUE.equals(env.get("useTempFile"));
  95         this.forceEnd64 = "true".equals(env.get("forceZIP64End"));
  96         this.provider = provider;
  97         this.zfpath = zfpath;
  98         if (Files.notExists(zfpath)) {
  99             if (createNew) {
 100                 try (OutputStream os = Files.newOutputStream(zfpath, CREATE_NEW, WRITE)) {
 101                     new END().write(os, 0, forceEnd64);
 102                 }
 103             } else {
 104                 throw new FileSystemNotFoundException(zfpath.toString());
 105             }
 106         }
 107         // sm and existence check
 108         zfpath.getFileSystem().provider().checkAccess(zfpath, AccessMode.READ);
 109         boolean writeable = AccessController.doPrivileged(
 110             (PrivilegedAction<Boolean>) () ->  Files.isWritable(zfpath));
 111         this.readOnly = !writeable;
 112         this.zc = ZipCoder.get(nameEncoding);
 113         this.rootdir = new ZipPath(this, new byte[]{'/'});
 114         this.ch = Files.newByteChannel(zfpath, READ);
 115         try {
 116             this.cen = initCEN();
 117         } catch (IOException x) {
 118             try {
 119                 this.ch.close();
 120             } catch (IOException xx) {
 121                 x.addSuppressed(xx);


 985             int len = buf.length - off;
 986             if (readFullyAt(buf, off, len, pos + off) != len)
 987                 zerror("zip END header not found");
 988 
 989             // Now scan the block backwards for END header signature
 990             for (int i = buf.length - ENDHDR; i >= 0; i--) {
 991                 if (buf[i+0] == (byte)'P'    &&
 992                     buf[i+1] == (byte)'K'    &&
 993                     buf[i+2] == (byte)'\005' &&
 994                     buf[i+3] == (byte)'\006' &&
 995                     (pos + i + ENDHDR + ENDCOM(buf, i) == ziplen)) {
 996                     // Found END header
 997                     buf = Arrays.copyOfRange(buf, i, i + ENDHDR);
 998                     END end = new END();
 999                     end.endsub = ENDSUB(buf);
1000                     end.centot = ENDTOT(buf);
1001                     end.cenlen = ENDSIZ(buf);
1002                     end.cenoff = ENDOFF(buf);
1003                     end.comlen = ENDCOM(buf);
1004                     end.endpos = pos + i;
1005                     // try if there is zip64 end;




1006                     byte[] loc64 = new byte[ZIP64_LOCHDR];
1007                     if (end.endpos < ZIP64_LOCHDR ||
1008                         readFullyAt(loc64, 0, loc64.length, end.endpos - ZIP64_LOCHDR)
1009                         != loc64.length ||
1010                         !locator64SigAt(loc64, 0)) {
1011                         return end;
1012                     }
1013                     long end64pos = ZIP64_LOCOFF(loc64);
1014                     byte[] end64buf = new byte[ZIP64_ENDHDR];
1015                     if (readFullyAt(end64buf, 0, end64buf.length, end64pos)
1016                         != end64buf.length ||
1017                         !end64SigAt(end64buf, 0)) {
1018                         return end;
1019                     }
1020                     // end64 found, 
1021                     long cenlen64 = ZIP64_ENDSIZ(end64buf);
1022                     long cenoff64 = ZIP64_ENDOFF(end64buf);
1023                     long centot64 = ZIP64_ENDTOT(end64buf);
1024                     // double-check
1025                     if (cenlen64 != end.cenlen && end.cenlen != ZIP64_MINVAL ||
1026                         cenoff64 != end.cenoff && end.cenoff != ZIP64_MINVAL ||
1027                         centot64 != end.centot && end.centot != ZIP64_MINVAL32) {
1028                         return end;
1029                     }
1030                     // to use the end64 values
1031                     end.cenlen = cenlen64;
1032                     end.cenoff = cenoff64;
1033                     end.centot = (int)centot64; // assume total < 2g
1034                     end.endpos = end64pos;
1035                     return end;
1036                 }
1037             }
1038         }
1039         zerror("zip END header not found");
1040         return null; //make compiler happy
1041     }
1042 
1043     // Reads zip file central directory. Returns the file position of first
1044     // CEN header, otherwise returns -1 if an error occurred. If zip->msg != NULL
1045     // then the error was a zip format error and zip->msg has the error text.
1046     // Always pass in -1 for knownTotal; it's used for a recursive call.
1047     private byte[] initCEN() throws IOException {
1048         end = findEND();
1049         if (end.endpos == 0) {
1050             inodes = new LinkedHashMap<>(10);
1051             locpos = 0;
1052             buildNodeTree();
1053             return null;         // only END header present
1054         }


1185             // use e.csize,  LOCSIZ(buf) is zero if FLAG_DATADESCR is on
1186             // size += LOCNAM(buf) + LOCEXT(buf) + LOCSIZ(buf);
1187             size += LOCNAM(buf) + LOCEXT(buf) + e.csize;
1188             written = LOCHDR + size;
1189         }
1190         int n;
1191         while (size > 0 &&
1192             (n = (int)readFullyAt(buf, 0, buf.length, locoff)) != -1)
1193         {
1194             if (size < n)
1195                 n = (int)size;
1196             os.write(buf, 0, n);
1197             size -= n;
1198             locoff += n;
1199         }
1200         return written;
1201     }
1202 
1203     // sync the zip file system, if there is any udpate
1204     private void sync() throws IOException {
1205         // System.out.printf("->sync(%s) starting....!%n", toString());
1206         // check ex-closer
1207         if (!exChClosers.isEmpty()) {
1208             for (ExChannelCloser ecc : exChClosers) {
1209                 if (ecc.streams.isEmpty()) {
1210                     ecc.ch.close();
1211                     Files.delete(ecc.path);
1212                     exChClosers.remove(ecc);
1213                 }
1214             }
1215         }
1216         if (!hasUpdate)
1217             return;
1218         Path tmpFile = createTempFileInSameDirectoryAs(zfpath);
1219         try (OutputStream os = new BufferedOutputStream(Files.newOutputStream(tmpFile, WRITE)))
1220         {
1221             ArrayList<Entry> elist = new ArrayList<>(inodes.size());
1222             long written = 0;
1223             byte[] buf = new byte[8192];
1224             Entry e = null;
1225 


1275                     if (inode.pos == -1) {
1276                         continue;               // pseudo directory node
1277                     }
1278                     e = Entry.readCEN(this, inode);
1279                     try {
1280                         written += copyLOCEntry(e, false, os, written, buf);
1281                         elist.add(e);
1282                     } catch (IOException x) {
1283                         x.printStackTrace();    // skip any wrong entry
1284                     }
1285                 }
1286             }
1287 
1288             // now write back the cen and end table
1289             end.cenoff = written;
1290             for (Entry entry : elist) {
1291                 written += entry.writeCEN(os);
1292             }
1293             end.centot = elist.size();
1294             end.cenlen = written - end.cenoff;
1295             end.write(os, written, forceEnd64);
1296         }
1297         if (!streams.isEmpty()) {
1298             //
1299             // TBD: ExChannelCloser should not be necessary if we only
1300             // sync when being closed, all streams should have been
1301             // closed already. Keep the logic here for now.
1302             //
1303             // There are outstanding input streams open on existing "ch",
1304             // so, don't close the "cha" and delete the "file for now, let
1305             // the "ex-channel-closer" to handle them
1306             ExChannelCloser ecc = new ExChannelCloser(
1307                                       createTempFileInSameDirectoryAs(zfpath),
1308                                       ch,
1309                                       streams);
1310             Files.move(zfpath, ecc.path, REPLACE_EXISTING);
1311             exChClosers.add(ecc);
1312             streams = Collections.synchronizedSet(new HashSet<InputStream>());
1313         } else {
1314             ch.close();
1315             Files.delete(zfpath);


1705         }
1706     }
1707 
1708     // End of central directory record
1709     static class END {
1710         // these 2 fields are not used by anyone and write() uses "0"
1711         // int  disknum;
1712         // int  sdisknum;
1713         int  endsub;     // endsub
1714         int  centot;     // 4 bytes
1715         long cenlen;     // 4 bytes
1716         long cenoff;     // 4 bytes
1717         int  comlen;     // comment length
1718         byte[] comment;
1719 
1720         /* members of Zip64 end of central directory locator */
1721         // int diskNum;
1722         long endpos;
1723         // int disktot;
1724 
1725         void write(OutputStream os, long offset, boolean forceEnd64) throws IOException {
1726             boolean hasZip64 = forceEnd64; // false;
1727             long xlen = cenlen;
1728             long xoff = cenoff;
1729             if (xlen >= ZIP64_MINVAL) {
1730                 xlen = ZIP64_MINVAL;
1731                 hasZip64 = true;
1732             }
1733             if (xoff >= ZIP64_MINVAL) {
1734                 xoff = ZIP64_MINVAL;
1735                 hasZip64 = true;
1736             }
1737             int count = centot;
1738             if (count >= ZIP64_MINVAL32) {
1739                 count = ZIP64_MINVAL32;
1740                 hasZip64 = true;
1741             }
1742             if (hasZip64) {
1743                 long off64 = offset;
1744                 //zip64 end of central directory record
1745                 writeInt(os, ZIP64_ENDSIG);       // zip64 END record signature
1746                 writeLong(os, ZIP64_ENDHDR - 12); // size of zip64 end
1747                 writeShort(os, 45);               // version made by
1748                 writeShort(os, 45);               // version needed to extract
1749                 writeInt(os, 0);                  // number of this disk
1750                 writeInt(os, 0);                  // central directory start disk
1751                 writeLong(os, centot);            // number of directory entries on disk
1752                 writeLong(os, centot);            // number of directory entries
1753                 writeLong(os, cenlen);            // length of central directory
1754                 writeLong(os, cenoff);            // offset of central directory
1755 
1756                 //zip64 end of central directory locator
1757                 writeInt(os, ZIP64_LOCSIG);       // zip64 END locator signature
1758                 writeInt(os, 0);                  // zip64 END start disk
1759                 writeLong(os, off64);             // offset of zip64 END
1760                 writeInt(os, 1);                  // total number of disks (?)
1761             }
1762             writeInt(os, ENDSIG);                 // END record signature
1763             writeShort(os, 0);                    // number of this disk
1764             writeShort(os, 0);                    // central directory start disk
1765             writeShort(os, count);                // number of directory entries on disk
1766             writeShort(os, count);                // total number of directory entries
1767             writeInt(os, xlen);                   // length of central directory
1768             writeInt(os, xoff);                   // offset of central directory
1769             if (comment != null) {            // zip file comment
1770                 writeShort(os, comment.length);
1771                 writeBytes(os, comment);
1772             } else {