< prev index next >

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

Print this page
rev 54189 : 8213031: (zipfs) Add support for POSIX file permissions

*** 41,50 **** --- 41,54 ---- import java.nio.channels.SeekableByteChannel; import java.nio.channels.WritableByteChannel; import java.nio.file.*; import java.nio.file.attribute.FileAttribute; import java.nio.file.attribute.FileTime; + import java.nio.file.attribute.GroupPrincipal; + import java.nio.file.attribute.PosixFilePermission; + import java.nio.file.attribute.PosixFilePermissions; + import java.nio.file.attribute.UserPrincipal; import java.nio.file.attribute.UserPrincipalLookupService; import java.nio.file.spi.FileSystemProvider; import java.security.AccessController; import java.security.PrivilegedAction; import java.security.PrivilegedActionException;
*** 76,115 **** * A FileSystem built on a zip file * * @author Xueming Shen */ class ZipFileSystem extends FileSystem { private final ZipFileSystemProvider provider; private final Path zfpath; final ZipCoder zc; private final ZipPath rootdir; private boolean readOnly = false; // readonly file system // configurable by env map private final boolean noExtt; // see readExtra() private final boolean useTempFile; // use a temp file for newOS, default // is to use BAOS for better performance ! private static final boolean isWindows = AccessController.doPrivileged( ! (PrivilegedAction<Boolean>)() -> System.getProperty("os.name") ! .startsWith("Windows")); private final boolean forceEnd64; private final int defaultMethod; // METHOD_STORED if "noCompression=true" // METHOD_DEFLATED otherwise ZipFileSystem(ZipFileSystemProvider provider, Path zfpath, Map<String, ?> env) throws IOException { // default encoding for name/comment String nameEncoding = env.containsKey("encoding") ? ! (String)env.get("encoding") : "UTF-8"; this.noExtt = "false".equals(env.get("zipinfo-time")); this.useTempFile = isTrue(env, "useTempFile"); this.forceEnd64 = isTrue(env, "forceZIP64End"); ! this.defaultMethod = isTrue(env, "noCompression") ? METHOD_STORED: METHOD_DEFLATED; if (Files.notExists(zfpath)) { ! // create a new zip if not exists if (isTrue(env, "create")) { try (OutputStream os = Files.newOutputStream(zfpath, CREATE_NEW, WRITE)) { new END().write(os, 0, forceEnd64); } } else { --- 80,144 ---- * A FileSystem built on a zip file * * @author Xueming Shen */ class ZipFileSystem extends FileSystem { + // statics + private static final boolean isWindows = AccessController.doPrivileged( + (PrivilegedAction<Boolean>)()->System.getProperty("os.name") + .startsWith("Windows")); + private static final String OPT_POSIX = "enablePosixFileAttributes"; + private static final String OPT_DEFAULT_OWNER = "defaultOwner"; + private static final String OPT_DEFAULT_GROUP = "defaultGroup"; + private static final String OPT_DEFAULT_PERMISSIONS = "defaultPermissions"; + + private static final String DEFAULT_PRINCIPAL_NAME = "<zipfs_default>"; + private static final Set<PosixFilePermission> DEFAULT_PERMISSIONS = + PosixFilePermissions.fromString("rwxrwxrwx"); + private final ZipFileSystemProvider provider; private final Path zfpath; final ZipCoder zc; private final ZipPath rootdir; private boolean readOnly = false; // readonly file system // configurable by env map private final boolean noExtt; // see readExtra() private final boolean useTempFile; // use a temp file for newOS, default // is to use BAOS for better performance ! private final boolean forceEnd64; private final int defaultMethod; // METHOD_STORED if "noCompression=true" // METHOD_DEFLATED otherwise + // POSIX support + final boolean supportPosix; + private final UserPrincipal defaultOwner; + private final GroupPrincipal defaultGroup; + private final Set<PosixFilePermission> defaultPermissions; + + private final Set<String> supportedFileAttributeViews; + ZipFileSystem(ZipFileSystemProvider provider, Path zfpath, Map<String, ?> env) throws IOException { // default encoding for name/comment String nameEncoding = env.containsKey("encoding") ? ! (String)env.get("encoding") : "UTF-8"; this.noExtt = "false".equals(env.get("zipinfo-time")); this.useTempFile = isTrue(env, "useTempFile"); this.forceEnd64 = isTrue(env, "forceZIP64End"); ! this.defaultMethod = isTrue(env, "noCompression") ? METHOD_STORED : METHOD_DEFLATED; ! this.supportPosix = isTrue(env, OPT_POSIX); ! this.defaultOwner = initOwner(zfpath, env); ! this.defaultGroup = initGroup(env); ! this.defaultPermissions = initPermissions(env); ! this.supportedFileAttributeViews = supportPosix ? ! Set.of("basic", "posix", "zip") : Set.of("basic", "zip"); if (Files.notExists(zfpath)) { ! // create a new zip if it doesn't exist if (isTrue(env, "create")) { try (OutputStream os = Files.newOutputStream(zfpath, CREATE_NEW, WRITE)) { new END().write(os, 0, forceEnd64); } } else {
*** 117,127 **** } } // sm and existence check zfpath.getFileSystem().provider().checkAccess(zfpath, AccessMode.READ); boolean writeable = AccessController.doPrivileged( ! (PrivilegedAction<Boolean>) () -> Files.isWritable(zfpath)); this.readOnly = !writeable; this.zc = ZipCoder.get(nameEncoding); this.rootdir = new ZipPath(this, new byte[]{'/'}); this.ch = Files.newByteChannel(zfpath, READ); try { --- 146,156 ---- } } // sm and existence check zfpath.getFileSystem().provider().checkAccess(zfpath, AccessMode.READ); boolean writeable = AccessController.doPrivileged( ! (PrivilegedAction<Boolean>)()->Files.isWritable(zfpath)); this.readOnly = !writeable; this.zc = ZipCoder.get(nameEncoding); this.rootdir = new ZipPath(this, new byte[]{'/'}); this.ch = Files.newByteChannel(zfpath, READ); try {
*** 141,150 **** --- 170,263 ---- // returns true if there is a name=true/"true" setting in env private static boolean isTrue(Map<String, ?> env, String name) { return "true".equals(env.get(name)) || TRUE.equals(env.get(name)); } + // Initialize the default owner for files inside the zip archive. + // If not specified in env, it is the owner of the archive. If no owner can + // be determined, we try to go with system property "user.name". If that's not + // accessible, we return "<zipfs_default>". + private UserPrincipal initOwner(Path zfpath, Map<String, ?> env) { + Object o = env.get(OPT_DEFAULT_OWNER); + if (o == null) { + try { + return Files.getOwner(zfpath); + } catch (Exception e) { + try { + String userName = AccessController.doPrivileged( + (PrivilegedAction<String>)()->System.getProperty("user.name")); + return ()->userName; + } catch (Exception e2) { + return ()->DEFAULT_PRINCIPAL_NAME; + } + } + } + if (o instanceof String) { + if (((String)o).isEmpty()) { + throw new IllegalArgumentException("Value for property " + + OPT_DEFAULT_OWNER + " must not be empty."); + } + return ()->(String)o; + } + if (o instanceof UserPrincipal) { + return (UserPrincipal)o; + } + throw new IllegalArgumentException("Value for property " + + OPT_DEFAULT_OWNER + " must be of type " + String.class + + " or " + UserPrincipal.class); + } + + // Initialize the default group for files inside the zip archive. + // If not specified in env, it will return a group principal going + // by the same name as the default owner. + private GroupPrincipal initGroup(Map<String, ?> env) { + Object o = env.get(OPT_DEFAULT_GROUP); + if (o == null) { + return ()->defaultOwner.getName(); + } + if (o instanceof String) { + if (((String)o).isEmpty()) { + throw new IllegalArgumentException("Value for property " + + OPT_DEFAULT_GROUP + " must not be empty."); + } + return ()->(String)o; + } + if (o instanceof GroupPrincipal) { + return (GroupPrincipal)o; + } + throw new IllegalArgumentException("Value for property " + + OPT_DEFAULT_GROUP + " must be of type " + String.class + + " or " + GroupPrincipal.class); + } + + // Initialize the default permissions for files inside the zip archive. + // If not specified in env, it will return 777. + private Set<PosixFilePermission> initPermissions(Map<String, ?> env) { + Object o = env.get(OPT_DEFAULT_PERMISSIONS); + if (o == null) { + return DEFAULT_PERMISSIONS; + } + if (o instanceof String) { + return PosixFilePermissions.fromString((String)o); + } + if (!(o instanceof Set)) { + throw new IllegalArgumentException("Value for property " + + OPT_DEFAULT_PERMISSIONS + " must be of type " + String.class + + " or " + Set.class); + } + Set<PosixFilePermission> perms = new HashSet<PosixFilePermission>(); + for (Object o2 : (Set<?>)o) { + if (o2 instanceof PosixFilePermission) { + perms.add((PosixFilePermission)o2); + } else { + throw new IllegalArgumentException(OPT_DEFAULT_PERMISSIONS + + " must only contain objects of type " + PosixFilePermission.class); + } + } + return perms; + } + @Override public FileSystemProvider provider() { return provider; }
*** 216,228 **** @Override public Iterable<FileStore> getFileStores() { return List.of(new ZipFileStore(rootdir)); } - private static final Set<String> supportedFileAttributeViews = - Set.of("basic", "zip"); - @Override public Set<String> supportedFileAttributeViews() { return supportedFileAttributeViews; } --- 329,338 ----
*** 369,379 **** ensureOpen(); Entry e = getEntry(path); // ensureOpen checked if (e == null) throw new NoSuchFileException(getString(path)); if (e.type == Entry.CEN) ! e.type = Entry.COPY; // copy e if (mtime != null) e.mtime = mtime.toMillis(); if (atime != null) e.atime = atime.toMillis(); if (ctime != null) --- 479,489 ---- ensureOpen(); Entry e = getEntry(path); // ensureOpen checked if (e == null) throw new NoSuchFileException(getString(path)); if (e.type == Entry.CEN) ! e.type = Entry.COPY; // copy e if (mtime != null) e.mtime = mtime.toMillis(); if (atime != null) e.atime = atime.toMillis(); if (ctime != null)
*** 382,391 **** --- 492,558 ---- } finally { endWrite(); } } + void setOwner(byte[] path, UserPrincipal owner) throws IOException { + checkWritable(); + beginWrite(); + try { + ensureOpen(); + Entry e = getEntry(path); // ensureOpen checked + if (e == null) { + throw new NoSuchFileException(getString(path)); + } + // as the owner information is not persistent, we don't need to + // change e.type to Entry.COPY + e.owner = owner; + update(e); + } finally { + endWrite(); + } + } + + void setPermissions(byte[] path, Set<PosixFilePermission> perms) + throws IOException + { + checkWritable(); + beginWrite(); + try { + ensureOpen(); + Entry e = getEntry(path); // ensureOpen checked + if (e == null) { + throw new NoSuchFileException(getString(path)); + } + if (e.type == Entry.CEN) { + e.type = Entry.COPY; // copy e + } + e.posixPerms = perms == null ? -1 : ZipUtils.permsToFlags(perms); + update(e); + } finally { + endWrite(); + } + } + + void setGroup(byte[] path, GroupPrincipal group) throws IOException { + checkWritable(); + beginWrite(); + try { + ensureOpen(); + Entry e = getEntry(path); // ensureOpen checked + if (e == null) { + throw new NoSuchFileException(getString(path)); + } + // as the group information is not persistent, we don't need to + // change e.type to Entry.COPY + e.group = group; + update(e); + } finally { + endWrite(); + } + } + boolean exists(byte[] path) throws IOException { beginRead(); try {
*** 451,461 **** try { ensureOpen(); if (dir.length == 0 || exists(dir)) // root dir, or exiting dir throw new FileAlreadyExistsException(getString(dir)); checkParents(dir); ! Entry e = new Entry(dir, Entry.NEW, true, METHOD_STORED); update(e); } finally { endWrite(); } } --- 618,628 ---- try { ensureOpen(); if (dir.length == 0 || exists(dir)) // root dir, or exiting dir throw new FileAlreadyExistsException(getString(dir)); checkParents(dir); ! Entry e = new Entry(dir, Entry.NEW, true, METHOD_STORED, attrs); update(e); } finally { endWrite(); } }
*** 632,645 **** private int getCompressMethod(FileAttribute<?>... attrs) { return defaultMethod; } ! // Returns a Writable/ReadByteChannel for now. Might consdier to use // newFileChannel() instead, which dump the entry data into a regular ! // file on the default file system and create a FileChannel on top of ! // it. SeekableByteChannel newByteChannel(byte[] path, Set<? extends OpenOption> options, FileAttribute<?>... attrs) throws IOException { --- 799,811 ---- private int getCompressMethod(FileAttribute<?>... attrs) { return defaultMethod; } ! // Returns a Writable/ReadByteChannel for now. Might consider to use // newFileChannel() instead, which dump the entry data into a regular ! // file on the default file system and create a FileChannel on top of it. SeekableByteChannel newByteChannel(byte[] path, Set<? extends OpenOption> options, FileAttribute<?>... attrs) throws IOException {
*** 673,683 **** } if (!options.contains(CREATE) && !options.contains(CREATE_NEW)) throw new NoSuchFileException(getString(path)); checkParents(path); return new EntryOutputChannel( ! new Entry(path, Entry.NEW, false, getCompressMethod(attrs))); } finally { endRead(); } } else { --- 839,849 ---- } if (!options.contains(CREATE) && !options.contains(CREATE_NEW)) throw new NoSuchFileException(getString(path)); checkParents(path); return new EntryOutputChannel( ! new Entry(path, Entry.NEW, false, getCompressMethod(attrs), attrs)); } finally { endRead(); } } else {
*** 738,748 **** final boolean isFCH = (e != null && e.type == Entry.FILECH); final Path tmpfile = isFCH ? e.file : getTempPathForEntry(path); final FileChannel fch = tmpfile.getFileSystem() .provider() .newFileChannel(tmpfile, options, attrs); ! final Entry u = isFCH ? e : new Entry(path, tmpfile, Entry.FILECH); if (forWrite) { u.flag = FLAG_DATADESCR; u.method = getCompressMethod(attrs); } // is there a better way to hook into the FileChannel's close method? --- 904,914 ---- final boolean isFCH = (e != null && e.type == Entry.FILECH); final Path tmpfile = isFCH ? e.file : getTempPathForEntry(path); final FileChannel fch = tmpfile.getFileSystem() .provider() .newFileChannel(tmpfile, options, attrs); ! final Entry u = isFCH ? e : new Entry(path, tmpfile, Entry.FILECH, attrs); if (forWrite) { u.flag = FLAG_DATADESCR; u.method = getCompressMethod(attrs); } // is there a better way to hook into the FileChannel's close method?
*** 1267,1280 **** } else { // unchanged inode if (inode.pos == -1) { continue; // pseudo directory node } if (inode.name.length == 1 && inode.name[0] == '/') { ! continue; // no root '/' directory even it ! // exits in original zip/jar file. } ! e = Entry.readCEN(this, inode); try { written += copyLOCEntry(e, false, os, written, buf); elist.add(e); } catch (IOException x) { x.printStackTrace(); // skip any wrong entry --- 1433,1446 ---- } else { // unchanged inode if (inode.pos == -1) { continue; // pseudo directory node } if (inode.name.length == 1 && inode.name[0] == '/') { ! continue; // no root '/' directory even if it ! // exists in original zip/jar file. } ! e = new Entry(inode); try { written += copyLOCEntry(e, false, os, written, buf); elist.add(e); } catch (IOException x) { x.printStackTrace(); // skip any wrong entry
*** 1308,1318 **** IndexNode inode = getInode(path); if (inode instanceof Entry) return (Entry)inode; if (inode == null || inode.pos == -1) return null; ! return Entry.readCEN(this, inode); } public void deleteFile(byte[] path, boolean failIfNotExists) throws IOException { --- 1474,1484 ---- IndexNode inode = getInode(path); if (inode instanceof Entry) return (Entry)inode; if (inode == null || inode.pos == -1) return null; ! return new Entry(inode); } public void deleteFile(byte[] path, boolean failIfNotExists) throws IOException {
*** 1776,1787 **** // The node itself can be used as a "key" to lookup itself in // the HashMap inodes. static class IndexNode { byte[] name; int hashcode; // node is hashable/hashed by its name ! int pos = -1; // position in cen table, -1 menas the ! // entry does not exists in zip file boolean isdir; IndexNode(byte[] name, boolean isdir) { name(name); this.isdir = isdir; --- 1942,1953 ---- // The node itself can be used as a "key" to lookup itself in // the HashMap inodes. static class IndexNode { byte[] name; int hashcode; // node is hashable/hashed by its name ! int pos = -1; // position in cen table, -1 means the ! // entry does not exist in zip file boolean isdir; IndexNode(byte[] name, boolean isdir) { name(name); this.isdir = isdir;
*** 1853,1863 **** IndexNode() {} IndexNode sibling; IndexNode child; // 1st child } ! static class Entry extends IndexNode implements ZipFileAttributes { static final int CEN = 1; // entry read from cen static final int NEW = 2; // updated contents in bytes or file static final int FILECH = 3; // fch update in "file" static final int COPY = 4; // copy of a CEN entry --- 2019,2029 ---- IndexNode() {} IndexNode sibling; IndexNode child; // 1st child } ! class Entry extends IndexNode implements ZipFileAttributes { static final int CEN = 1; // entry read from cen static final int NEW = 2; // updated contents in bytes or file static final int FILECH = 3; // fch update in "file" static final int COPY = 4; // copy of a CEN entry
*** 1867,1876 **** --- 2033,2043 ---- int type = CEN; // default is the entry read from cen // entry attributes int version; int flag; + int posixPerms = -1; // posix permissions int method = -1; // compression method long mtime = -1; // last modification time (in DOS time) long atime = -1; // last access time long ctime = -1; // create time long crc = -1; // crc-32 of entry data
*** 1886,1895 **** --- 2053,2066 ---- // int attrs; // long attrsEx; long locoff; byte[] comment; + // posix support + private UserPrincipal owner = defaultOwner; + private GroupPrincipal group = defaultGroup; + Entry() {} Entry(byte[] name, boolean isdir, int method) { name(name); this.isdir = isdir;
*** 1898,1913 **** this.size = 0; this.csize = 0; this.method = method; } ! Entry(byte[] name, int type, boolean isdir, int method) { this(name, isdir, method); this.type = type; } ! Entry (Entry e, int type) { name(e.name); this.isdir = e.isdir; this.version = e.version; this.ctime = e.ctime; this.atime = e.atime; --- 2069,2091 ---- this.size = 0; this.csize = 0; this.method = method; } ! @SuppressWarnings("unchecked") ! Entry(byte[] name, int type, boolean isdir, int method, FileAttribute<?>... attrs) { this(name, isdir, method); this.type = type; + for (FileAttribute<?> attr : attrs) { + String attrName = attr.name(); + if (attrName.equals("posix:permissions")) { + posixPerms = ZipUtils.permsToFlags((Set<PosixFilePermission>)attr.value()); + } + } } ! Entry(Entry e, int type) { name(e.name); this.isdir = e.isdir; this.version = e.version; this.ctime = e.ctime; this.atime = e.atime;
*** 1923,1959 **** this.attrs = e.attrs; this.attrsEx = e.attrsEx; */ this.locoff = e.locoff; this.comment = e.comment; this.type = type; } ! Entry (byte[] name, Path file, int type) { this(name, type, false, METHOD_STORED); this.file = file; } ! int version() throws ZipException { ! if (method == METHOD_DEFLATED) ! return 20; ! else if (method == METHOD_STORED) ! return 10; ! throw new ZipException("unsupported compression method"); ! } ! ! ///////////////////// CEN ////////////////////// ! static Entry readCEN(ZipFileSystem zipfs, IndexNode inode) ! throws IOException ! { ! return new Entry().cen(zipfs, inode); ! } ! ! private Entry cen(ZipFileSystem zipfs, IndexNode inode) ! throws IOException ! { ! byte[] cen = zipfs.cen; int pos = inode.pos; if (!cenSigAt(cen, pos)) zerror("invalid CEN header (bad signature)"); version = CENVER(cen, pos); flag = CENFLG(cen, pos); --- 2101,2130 ---- this.attrs = e.attrs; this.attrsEx = e.attrsEx; */ this.locoff = e.locoff; this.comment = e.comment; + this.posixPerms = e.posixPerms; + this.owner = e.owner; + this.group = e.group; this.type = type; } ! @SuppressWarnings("unchecked") ! Entry(byte[] name, Path file, int type, FileAttribute<?>... attrs) { this(name, type, false, METHOD_STORED); this.file = file; + for (FileAttribute<?> attr : attrs) { + String attrName = attr.name(); + if (attrName.equals("posix:permissions")) { + posixPerms = ZipUtils.permsToFlags((Set<PosixFilePermission>)attr.value()); + } + } } ! // reads the full entry from an IndexNode ! Entry(IndexNode inode) throws IOException { int pos = inode.pos; if (!cenSigAt(cen, pos)) zerror("invalid CEN header (bad signature)"); version = CENVER(cen, pos); flag = CENFLG(cen, pos);
*** 1969,1998 **** versionMade = CENVEM(cen, pos); disk = CENDSK(cen, pos); attrs = CENATT(cen, pos); attrsEx = CENATX(cen, pos); */ locoff = CENOFF(cen, pos); pos += CENHDR; this.name = inode.name; this.isdir = inode.isdir; this.hashcode = inode.hashcode; pos += nlen; if (elen > 0) { extra = Arrays.copyOfRange(cen, pos, pos + elen); pos += elen; ! readExtra(zipfs); } if (clen > 0) { comment = Arrays.copyOfRange(cen, pos, pos + clen); } - return this; } int writeCEN(OutputStream os) throws IOException { - int version0 = version(); long csize0 = csize; long size0 = size; long locoff0 = locoff; int elen64 = 0; // extra for ZIP64 int elenNTFS = 0; // extra for NTFS (a/c/mtime) --- 2140,2191 ---- versionMade = CENVEM(cen, pos); disk = CENDSK(cen, pos); attrs = CENATT(cen, pos); attrsEx = CENATX(cen, pos); */ + if (CENVEM_FA(cen, pos) == FILE_ATTRIBUTES_UNIX) { + posixPerms = CENATX_PERMS(cen, pos) & 0xFFF; // 12 bits for setuid, setgid, sticky + perms + } locoff = CENOFF(cen, pos); pos += CENHDR; this.name = inode.name; this.isdir = inode.isdir; this.hashcode = inode.hashcode; pos += nlen; if (elen > 0) { extra = Arrays.copyOfRange(cen, pos, pos + elen); pos += elen; ! readExtra(ZipFileSystem.this); } if (clen > 0) { comment = Arrays.copyOfRange(cen, pos, pos + clen); } } + int version(boolean zip64) throws ZipException { + if (zip64) { + return 45; + } + if (method == METHOD_DEFLATED) + return 20; + else if (method == METHOD_STORED) + return 10; + throw new ZipException("unsupported compression method"); + } + + /** + * Adds information about compatibility of file attribute information + * to a version value. + */ + int versionMadeBy(int version) { + return (posixPerms < 0) ? version : + VERSION_BASE_UNIX | (version & 0xff); + } + + ///////////////////// CEN ////////////////////// int writeCEN(OutputStream os) throws IOException { long csize0 = csize; long size0 = size; long locoff0 = locoff; int elen64 = 0; // extra for ZIP64 int elenNTFS = 0; // extra for NTFS (a/c/mtime)
*** 2019,2028 **** --- 2212,2223 ---- elen64 += 8; // offset(8) } if (elen64 != 0) { elen64 += 4; // header and data sz 4 bytes } + boolean zip64 = (elen64 != 0); + int version0 = version(zip64); while (eoff + 4 < elen) { int tag = SH(extra, eoff); int sz = SH(extra, eoff + 2); if (tag == EXTID_EXTT || tag == EXTID_NTFS) { foundExtraTime = true;
*** 2035,2051 **** } else { // Extended Timestamp otherwise elenEXTT = 9; // only mtime in cen } } writeInt(os, CENSIG); // CEN header signature ! if (elen64 != 0) { ! writeShort(os, 45); // ver 4.5 for zip64 ! writeShort(os, 45); ! } else { ! writeShort(os, version0); // version made by ! writeShort(os, version0); // version needed to extract ! } writeShort(os, flag); // general purpose bit flag writeShort(os, method); // compression method // last modification time writeInt(os, (int)javaToDosTime(mtime)); writeInt(os, crc); // crc-32 --- 2230,2241 ---- } else { // Extended Timestamp otherwise elenEXTT = 9; // only mtime in cen } } writeInt(os, CENSIG); // CEN header signature ! writeShort(os, versionMadeBy(version0)); // version made by ! writeShort(os, version0); // version needed to extract writeShort(os, flag); // general purpose bit flag writeShort(os, method); // compression method // last modification time writeInt(os, (int)javaToDosTime(mtime)); writeInt(os, crc); // crc-32
*** 2059,2072 **** } else { writeShort(os, 0); } writeShort(os, 0); // starting disk number writeShort(os, 0); // internal file attributes (unused) ! writeInt(os, 0); // external file attributes (unused) writeInt(os, locoff0); // relative offset of local header writeBytes(os, zname, 1, nlen); ! if (elen64 != 0) { writeShort(os, EXTID_ZIP64);// Zip64 extra writeShort(os, elen64 - 4); // size of "this" extra block if (size0 == ZIP64_MINVAL) writeLong(os, size); if (csize0 == ZIP64_MINVAL) --- 2249,2264 ---- } else { writeShort(os, 0); } writeShort(os, 0); // starting disk number writeShort(os, 0); // internal file attributes (unused) ! writeInt(os, posixPerms > 0 ? posixPerms << 16 : 0); // external file ! // attributes, used for storing posix ! // permissions writeInt(os, locoff0); // relative offset of local header writeBytes(os, zname, 1, nlen); ! if (zip64) { writeShort(os, EXTID_ZIP64);// Zip64 extra writeShort(os, elen64 - 4); // size of "this" extra block if (size0 == ZIP64_MINVAL) writeLong(os, size); if (csize0 == ZIP64_MINVAL)
*** 2101,2122 **** } ///////////////////// LOC ////////////////////// int writeLOC(OutputStream os) throws IOException { - int version0 = version(); byte[] zname = isdir ? toDirectoryPath(name) : name; int nlen = (zname != null) ? zname.length - 1 : 0; // [0] is slash int elen = (extra != null) ? extra.length : 0; boolean foundExtraTime = false; // if extra timestamp present int eoff = 0; int elen64 = 0; int elenEXTT = 0; int elenNTFS = 0; writeInt(os, LOCSIG); // LOC header signature if ((flag & FLAG_DATADESCR) != 0) { ! writeShort(os, version0); // version needed to extract writeShort(os, flag); // general purpose bit flag writeShort(os, method); // compression method // last modification time writeInt(os, (int)javaToDosTime(mtime)); // store size, uncompressed size, and crc-32 in data descriptor --- 2293,2314 ---- } ///////////////////// LOC ////////////////////// int writeLOC(OutputStream os) throws IOException { byte[] zname = isdir ? toDirectoryPath(name) : name; int nlen = (zname != null) ? zname.length - 1 : 0; // [0] is slash int elen = (extra != null) ? extra.length : 0; boolean foundExtraTime = false; // if extra timestamp present int eoff = 0; int elen64 = 0; + boolean zip64 = false; int elenEXTT = 0; int elenNTFS = 0; writeInt(os, LOCSIG); // LOC header signature if ((flag & FLAG_DATADESCR) != 0) { ! writeShort(os, version(zip64)); // version needed to extract writeShort(os, flag); // general purpose bit flag writeShort(os, method); // compression method // last modification time writeInt(os, (int)javaToDosTime(mtime)); // store size, uncompressed size, and crc-32 in data descriptor
*** 2125,2144 **** writeInt(os, 0); writeInt(os, 0); } else { if (csize >= ZIP64_MINVAL || size >= ZIP64_MINVAL) { elen64 = 20; //headid(2) + size(2) + size(8) + csize(8) ! writeShort(os, 45); // ver 4.5 for zip64 ! } else { ! writeShort(os, version0); // version needed to extract } writeShort(os, flag); // general purpose bit flag writeShort(os, method); // compression method // last modification time writeInt(os, (int)javaToDosTime(mtime)); writeInt(os, crc); // crc-32 ! if (elen64 != 0) { writeInt(os, ZIP64_MINVAL); writeInt(os, ZIP64_MINVAL); } else { writeInt(os, csize); // compressed size writeInt(os, size); // uncompressed size --- 2317,2335 ---- writeInt(os, 0); writeInt(os, 0); } else { if (csize >= ZIP64_MINVAL || size >= ZIP64_MINVAL) { elen64 = 20; //headid(2) + size(2) + size(8) + csize(8) ! zip64 = true; } + writeShort(os, version(zip64)); // version needed to extract writeShort(os, flag); // general purpose bit flag writeShort(os, method); // compression method // last modification time writeInt(os, (int)javaToDosTime(mtime)); writeInt(os, crc); // crc-32 ! if (zip64) { writeInt(os, ZIP64_MINVAL); writeInt(os, ZIP64_MINVAL); } else { writeInt(os, csize); // compressed size writeInt(os, size); // uncompressed size
*** 2164,2174 **** } } writeShort(os, nlen); writeShort(os, elen + elen64 + elenNTFS + elenEXTT); writeBytes(os, zname, 1, nlen); ! if (elen64 != 0) { writeShort(os, EXTID_ZIP64); writeShort(os, 16); writeLong(os, size); writeLong(os, csize); } --- 2355,2365 ---- } } writeShort(os, nlen); writeShort(os, elen + elen64 + elenNTFS + elenEXTT); writeBytes(os, zname, 1, nlen); ! if (zip64) { writeShort(os, EXTID_ZIP64); writeShort(os, 16); writeLong(os, size); writeLong(os, csize); }
*** 2201,2211 **** writeBytes(os, extra); } return LOCHDR + nlen + elen + elen64 + elenNTFS + elenEXTT; } ! // Data Descriptior int writeEXT(OutputStream os) throws IOException { writeInt(os, EXTSIG); // EXT header signature writeInt(os, crc); // crc-32 if (csize >= ZIP64_MINVAL || size >= ZIP64_MINVAL) { writeLong(os, csize); --- 2392,2402 ---- writeBytes(os, extra); } return LOCHDR + nlen + elen + elen64 + elenNTFS + elenEXTT; } ! // Data Descriptor int writeEXT(OutputStream os) throws IOException { writeInt(os, EXTSIG); // EXT header signature writeInt(os, crc); // crc-32 if (csize >= ZIP64_MINVAL || size >= ZIP64_MINVAL) { writeLong(os, csize);
*** 2278,2299 **** if (sz == 5) mtime = unixToJavaTime(LG(extra, pos + 1)); break; } byte[] buf = new byte[LOCHDR]; ! if (zipfs.readFullyAt(buf, 0, buf.length , locoff) != buf.length) throw new ZipException("loc: reading failed"); if (!locSigAt(buf, 0)) throw new ZipException("loc: wrong sig ->" + Long.toString(getSig(buf, 0), 16)); int locElen = LOCEXT(buf); if (locElen < 9) // EXTT is at lease 9 bytes break; int locNlen = LOCNAM(buf); buf = new byte[locElen]; ! if (zipfs.readFullyAt(buf, 0, buf.length , locoff + LOCHDR + locNlen) != buf.length) throw new ZipException("loc extra: reading failed"); int locPos = 0; while (locPos + 4 < buf.length) { int locTag = SH(buf, locPos); --- 2469,2490 ---- if (sz == 5) mtime = unixToJavaTime(LG(extra, pos + 1)); break; } byte[] buf = new byte[LOCHDR]; ! if (zipfs.readFullyAt(buf, 0, buf.length, locoff) != buf.length) throw new ZipException("loc: reading failed"); if (!locSigAt(buf, 0)) throw new ZipException("loc: wrong sig ->" + Long.toString(getSig(buf, 0), 16)); int locElen = LOCEXT(buf); if (locElen < 9) // EXTT is at lease 9 bytes break; int locNlen = LOCNAM(buf); buf = new byte[locElen]; ! if (zipfs.readFullyAt(buf, 0, buf.length, locoff + LOCHDR + locNlen) != buf.length) throw new ZipException("loc extra: reading failed"); int locPos = 0; while (locPos + 4 < buf.length) { int locTag = SH(buf, locPos);
*** 2330,2339 **** --- 2521,2554 ---- extra = Arrays.copyOf(extra, newOff); else extra = null; } + @Override + public String toString() { + StringBuilder sb = new StringBuilder(1024); + Formatter fm = new Formatter(sb); + fm.format(" name : %s%n", new String(name)); + fm.format(" creationTime : %tc%n", creationTime().toMillis()); + fm.format(" lastAccessTime : %tc%n", lastAccessTime().toMillis()); + fm.format(" lastModifiedTime: %tc%n", lastModifiedTime().toMillis()); + fm.format(" isRegularFile : %b%n", isRegularFile()); + fm.format(" isDirectory : %b%n", isDirectory()); + fm.format(" isSymbolicLink : %b%n", isSymbolicLink()); + fm.format(" isOther : %b%n", isOther()); + fm.format(" fileKey : %s%n", fileKey()); + fm.format(" size : %d%n", size()); + fm.format(" compressedSize : %d%n", compressedSize()); + fm.format(" crc : %x%n", crc()); + fm.format(" method : %d%n", method()); + if (posixPerms != -1) { + fm.format(" permissions : %s%n", permissions()); + } + fm.close(); + return sb.toString(); + } + ///////// basic file attributes /////////// @Override public FileTime creationTime() { return FileTime.fromMillis(ctime == -1 ? mtime : ctime); }
*** 2376,2428 **** @Override public Object fileKey() { return null; } ! ///////// zip entry attributes /////////// public long compressedSize() { return csize; } public long crc() { return crc; } public int method() { return method; } public byte[] extra() { if (extra != null) return Arrays.copyOf(extra, extra.length); return null; } public byte[] comment() { if (comment != null) return Arrays.copyOf(comment, comment.length); return null; } ! public String toString() { ! StringBuilder sb = new StringBuilder(1024); ! Formatter fm = new Formatter(sb); ! fm.format(" name : %s%n", new String(name)); ! fm.format(" creationTime : %tc%n", creationTime().toMillis()); ! fm.format(" lastAccessTime : %tc%n", lastAccessTime().toMillis()); ! fm.format(" lastModifiedTime: %tc%n", lastModifiedTime().toMillis()); ! fm.format(" isRegularFile : %b%n", isRegularFile()); ! fm.format(" isDirectory : %b%n", isDirectory()); ! fm.format(" isSymbolicLink : %b%n", isSymbolicLink()); ! fm.format(" isOther : %b%n", isOther()); ! fm.format(" fileKey : %s%n", fileKey()); ! fm.format(" size : %d%n", size()); ! fm.format(" compressedSize : %d%n", compressedSize()); ! fm.format(" crc : %x%n", crc()); ! fm.format(" method : %d%n", method()); ! fm.close(); ! return sb.toString(); } } // ZIP directory has two issues: // (1) ZIP spec does not require the ZIP file to include --- 2591,2660 ---- @Override public Object fileKey() { return null; } ! ///////// posix file attributes /////////// ! ! @Override ! public UserPrincipal owner() { ! return owner; ! } ! ! @Override ! public GroupPrincipal group() { ! return group; ! } ! ! @Override ! public Set<PosixFilePermission> permissions() { ! return storedPermissions().orElse(Set.copyOf(defaultPermissions)); ! } ! ! ///////// zip file attributes /////////// ! ! @Override public long compressedSize() { return csize; } + @Override public long crc() { return crc; } + @Override public int method() { return method; } + @Override public byte[] extra() { if (extra != null) return Arrays.copyOf(extra, extra.length); return null; } + @Override public byte[] comment() { if (comment != null) return Arrays.copyOf(comment, comment.length); return null; } ! @Override ! public Optional<Set<PosixFilePermission>> storedPermissions() { ! Set<PosixFilePermission> perms = null; ! if (posixPerms != -1) { ! perms = new HashSet<>(PosixFilePermission.values().length); ! for (PosixFilePermission perm : PosixFilePermission.values()) { ! if ((posixPerms & ZipUtils.permToFlag(perm)) != 0) { ! perms.add(perm); ! } ! } ! } ! return Optional.ofNullable(perms); } } // ZIP directory has two issues: // (1) ZIP spec does not require the ZIP file to include
< prev index next >