< prev index next >

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

Print this page
rev 52302 : 8213031: Enhance jdk.nio.zipfs to support Posix File Permissions

*** 1,7 **** /* ! * Copyright (c) 2009, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this --- 1,7 ---- /* ! * Copyright (c) 2009, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this
*** 23,75 **** * questions. */ package jdk.nio.zipfs; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.EOFException; - import java.io.File; import java.io.FilterOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; ! import java.nio.channels.*; import java.nio.file.*; ! import java.nio.file.attribute.*; ! import java.nio.file.spi.*; import java.security.AccessController; import java.security.PrivilegedAction; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; ! import java.util.*; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.regex.Pattern; import java.util.zip.CRC32; - import java.util.zip.Inflater; import java.util.zip.Deflater; - import java.util.zip.InflaterInputStream; import java.util.zip.DeflaterOutputStream; import java.util.zip.ZipException; - import static java.lang.Boolean.*; - import static jdk.nio.zipfs.ZipConstants.*; - import static jdk.nio.zipfs.ZipUtils.*; - import static java.nio.file.StandardOpenOption.*; - import static java.nio.file.StandardCopyOption.*; /** * 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 --- 23,92 ---- * questions. */ package jdk.nio.zipfs; + import static java.lang.Boolean.TRUE; + import static jdk.nio.zipfs.ZipConstants.*; + import static jdk.nio.zipfs.ZipUtils.*; + import static java.nio.file.StandardOpenOption.*; + import static java.nio.file.StandardCopyOption.*; + import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.EOFException; import java.io.FilterOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; ! import java.nio.channels.FileChannel; ! import java.nio.channels.FileLock; ! import java.nio.channels.ReadableByteChannel; ! 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.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; import java.security.PrivilegedExceptionAction; ! import java.util.ArrayList; ! import java.util.Arrays; ! import java.util.Collections; ! import java.util.Formatter; ! import java.util.HashSet; ! import java.util.Iterator; ! import java.util.LinkedHashMap; ! import java.util.List; ! import java.util.Map; ! import java.util.Objects; ! import java.util.Set; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.regex.Pattern; import java.util.zip.CRC32; import java.util.zip.Deflater; import java.util.zip.DeflaterOutputStream; + import java.util.zip.Inflater; + import java.util.zip.InflaterInputStream; import java.util.zip.ZipException; /** * 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
*** 267,277 **** } finally { endWrite(); } if (!streams.isEmpty()) { // unlock and close all remaining streams Set<InputStream> copy = new HashSet<>(streams); ! for (InputStream is: copy) is.close(); } beginWrite(); // lock and sync try { AccessController.doPrivileged((PrivilegedExceptionAction<Void>) () -> { --- 284,294 ---- } finally { endWrite(); } if (!streams.isEmpty()) { // unlock and close all remaining streams Set<InputStream> copy = new HashSet<>(streams); ! for (InputStream is : copy) is.close(); } beginWrite(); // lock and sync try { AccessController.doPrivileged((PrivilegedExceptionAction<Void>) () -> {
*** 294,304 **** def.end(); } IOException ioe = null; synchronized (tmppaths) { ! for (Path p: tmppaths) { try { AccessController.doPrivileged( (PrivilegedExceptionAction<Boolean>)() -> Files.deleteIfExists(p)); } catch (PrivilegedActionException e) { IOException x = (IOException)e.getException(); --- 311,321 ---- def.end(); } IOException ioe = null; synchronized (tmppaths) { ! for (Path p : tmppaths) { try { AccessController.doPrivileged( (PrivilegedExceptionAction<Boolean>)() -> Files.deleteIfExists(p)); } catch (PrivilegedActionException e) { IOException x = (IOException)e.getException();
*** 442,452 **** 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(); } } --- 459,469 ---- 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(); } }
*** 519,529 **** checkWritable(); boolean hasCreateNew = false; boolean hasCreate = false; boolean hasAppend = false; boolean hasTruncate = false; ! for (OpenOption opt: options) { if (opt == READ) throw new IllegalArgumentException("READ not allowed"); if (opt == CREATE_NEW) hasCreateNew = true; if (opt == CREATE) --- 536,546 ---- checkWritable(); boolean hasCreateNew = false; boolean hasCreate = false; boolean hasAppend = false; boolean hasTruncate = false; ! for (OpenOption opt : options) { if (opt == READ) throw new IllegalArgumentException("READ not allowed"); if (opt == CREATE_NEW) hasCreateNew = true; if (opt == CREATE)
*** 664,674 **** } 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 { --- 681,691 ---- } 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 {
*** 729,739 **** 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? --- 746,756 ---- 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?
*** 1475,1485 **** // FILECH result is un-compressed. eis = Files.newInputStream(e.file); // TBD: wrap to hook close() // streams.add(eis); return eis; ! } else { // untouced CEN or COPY eis = new EntryInputStream(e, ch); } if (e.method == METHOD_DEFLATED) { // MORE: Compute good size for inflater stream: long bufSize = e.size + 2; // Inflater likes a bit of slack --- 1492,1502 ---- // FILECH result is un-compressed. eis = Files.newInputStream(e.file); // TBD: wrap to hook close() // streams.add(eis); return eis; ! } else { // untouched CEN or COPY eis = new EntryInputStream(e, ch); } if (e.method == METHOD_DEFLATED) { // MORE: Compute good size for inflater stream: long bufSize = e.size + 2; // Inflater likes a bit of slack
*** 1537,1554 **** private class EntryInputStream extends InputStream { private final SeekableByteChannel zfch; // local ref to zipfs's "ch". zipfs.ch might // point to a new channel after sync() private long pos; // current position within entry data protected long rem; // number of remaining bytes within entry - protected final long size; // uncompressed size of this entry EntryInputStream(Entry e, SeekableByteChannel zfch) throws IOException { this.zfch = zfch; rem = e.csize; - size = e.size; pos = e.locoff; if (pos == -1) { Entry e2 = getEntry(e.name); if (e2 == null) { throw new ZipException("invalid loc for entry <" + e.name + ">"); --- 1554,1569 ----
*** 1611,1624 **** public int available() { return rem > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) rem; } - public long size() { - return size; - } - public void close() { rem = 0; streams.remove(this); } --- 1626,1635 ----
*** 1670,1680 **** } // List of available Deflater objects for compression private final List<Deflater> deflaters = new ArrayList<>(); ! // Gets an deflater from the list of available deflaters or allocates // a new one. private Deflater getDeflater() { synchronized (deflaters) { int size = deflaters.size(); if (size > 0) { --- 1681,1691 ---- } // List of available Deflater objects for compression private final List<Deflater> deflaters = new ArrayList<>(); ! // Gets a deflater from the list of available deflaters or allocates // a new one. private Deflater getDeflater() { synchronized (deflaters) { int size = deflaters.size(); if (size > 0) {
*** 1684,1705 **** return new Deflater(Deflater.DEFAULT_COMPRESSION, true); } } } - // Releases the specified inflater to the list of available inflaters. - private void releaseDeflater(Deflater def) { - synchronized (deflaters) { - if (inflaters.size() < MAX_FLATER) { - def.reset(); - deflaters.add(def); - } else { - def.end(); - } - } - } - // End of central directory record static class END { // these 2 fields are not used by anyone and write() uses "0" // int disknum; // int sdisknum; --- 1695,1704 ----
*** 1863,1872 **** --- 1862,1872 ---- 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
*** 1874,1884 **** long size = -1; // uncompressed size of entry data byte[] extra; // cen ! // these fields are not used by anyone and writeCEN uses "0" // int versionMade; // int disk; // int attrs; // long attrsEx; long locoff; --- 1874,1884 ---- long size = -1; // uncompressed size of entry data byte[] extra; // cen ! // these fields are not used // int versionMade; // int disk; // int attrs; // long attrsEx; long locoff;
*** 1894,1909 **** 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; --- 1894,1916 ---- 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") || attrName.equals("unix: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;
*** 1919,1944 **** 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); --- 1926,1971 ---- this.attrs = e.attrs; this.attrsEx = e.attrsEx; */ this.locoff = e.locoff; this.comment = e.comment; + this.posixPerms = e.posixPerms; 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") || attrName.equals("unix:permissions")) { + posixPerms = ZipUtils.permsToFlags((Set<PosixFilePermission>)attr.value()); + } + } } ! 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 ////////////////////// static Entry readCEN(ZipFileSystem zipfs, IndexNode inode) throws IOException { return new Entry().cen(zipfs, inode);
*** 1965,1974 **** --- 1992,2004 ---- 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;
*** 1983,1996 **** comment = Arrays.copyOfRange(cen, pos, pos + clen); } return this; } ! int writeCEN(OutputStream os) throws IOException ! { ! int written = CENHDR; ! 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) --- 2013,2023 ---- comment = Arrays.copyOfRange(cen, pos, pos + clen); } return this; } ! 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)
*** 2017,2026 **** --- 2044,2055 ---- 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;
*** 2033,2049 **** } 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 --- 2062,2073 ---- } 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
*** 2057,2070 **** } 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) --- 2081,2096 ---- } 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)
*** 2100,2121 **** ///////////////////// LOC ////////////////////// int writeLOC(OutputStream os) throws IOException { writeInt(os, LOCSIG); // LOC header signature - int version = 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; if ((flag & FLAG_DATADESCR) != 0) { ! writeShort(os, version()); // 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 --- 2126,2146 ---- ///////////////////// LOC ////////////////////// int writeLOC(OutputStream os) throws IOException { writeInt(os, LOCSIG); // LOC header signature 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; 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
*** 2124,2143 **** 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, version()); // 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 --- 2149,2167 ---- 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
*** 2163,2173 **** } } 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); } --- 2187,2197 ---- } } 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); }
*** 2416,2438 **** 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 // directory entry // (2) all entries are not stored/organized in a "tree" // structure. // A possible solution is to build the node tree ourself as // implemented below. - private IndexNode root; // default time stamp for pseudo entries private long zfsDefaultTimeStamp = System.currentTimeMillis(); private void removeFromTree(IndexNode inode) { --- 2440,2494 ---- 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(); } + + @Override + public UserPrincipal owner() { + throw new UnsupportedOperationException( + "ZipFileSystem does not support owner."); + } + + @Override + public GroupPrincipal group() { + throw new UnsupportedOperationException( + "ZipFileSystem does not support group."); + } + + @Override + public Set<PosixFilePermission> permissions() { + if (posixPerms == -1) { + // in case there are no Posix permissions associated with the + // entry, we should not return an empty set of permissions + // because that would be an explicit set of permissions meaning + // no permissions for anyone + throw new UnsupportedOperationException( + "No posix permissions associated with zip entry."); + } + return ZipUtils.permsFromFlags(posixPerms); + } + + @Override + public void setPermissions(Set<PosixFilePermission> perms) { + posixPerms = ZipUtils.permsToFlags(perms); + } } // ZIP directory has two issues: // (1) ZIP spec does not require the ZIP file to include // directory entry // (2) all entries are not stored/organized in a "tree" // structure. // A possible solution is to build the node tree ourself as // implemented below. // default time stamp for pseudo entries private long zfsDefaultTimeStamp = System.currentTimeMillis(); private void removeFromTree(IndexNode inode) {
< prev index next >