< prev index next >
src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystem.java
Print this page
rev 55003 : 8213031: (zipfs) Add support for POSIX file permissions
*** 39,51 ****
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.UserPrincipalLookupService;
import java.nio.file.spi.FileSystemProvider;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
--- 39,49 ----
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.*;
import java.nio.file.spi.FileSystemProvider;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
*** 80,92 ****
class ZipFileSystem extends FileSystem {
// statics
private static final boolean isWindows = AccessController.doPrivileged(
(PrivilegedAction<Boolean>)()->System.getProperty("os.name")
.startsWith("Windows"));
- private static final Set<String> supportedFileAttributeViews =
- Set.of("basic", "zip");
private static final byte[] ROOTPATH = new byte[] { '/' };
private final ZipFileSystemProvider provider;
private final Path zfpath;
final ZipCoder zc;
private final ZipPath rootdir;
--- 78,95 ----
class ZipFileSystem extends FileSystem {
// statics
private static final boolean isWindows = AccessController.doPrivileged(
(PrivilegedAction<Boolean>)()->System.getProperty("os.name")
.startsWith("Windows"));
private static final byte[] ROOTPATH = new byte[] { '/' };
+ 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 Set<PosixFilePermission> DEFAULT_PERMISSIONS =
+ PosixFilePermissions.fromString("rwxrwxrwx");
private final ZipFileSystemProvider provider;
private final Path zfpath;
final ZipCoder zc;
private final ZipPath rootdir;
*** 101,110 ****
--- 104,121 ----
// is to use BAOS for better performance
private final boolean forceEnd64;
private final int defaultCompressionMethod; // 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
*** 112,121 ****
--- 123,138 ----
(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.defaultCompressionMethod = isTrue(env, "noCompression") ? METHOD_STORED : METHOD_DEFLATED;
+ this.supportPosix = isTrue(env, OPT_POSIX);
+ this.defaultOwner = initOwner(zfpath, env);
+ this.defaultGroup = initGroup(zfpath, 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);
*** 149,158 ****
--- 166,278 ----
// 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) throws IOException {
+ Object o = env.get(OPT_DEFAULT_OWNER);
+ if (o == null) {
+ try {
+ PrivilegedExceptionAction<UserPrincipal> pa = ()->Files.getOwner(zfpath);
+ return AccessController.doPrivileged(pa);
+ } catch (UnsupportedOperationException | PrivilegedActionException e) {
+ if (e instanceof UnsupportedOperationException ||
+ e.getCause() instanceof NoSuchFileException)
+ {
+ PrivilegedAction<String> pa = ()->System.getProperty("user.name");
+ String userName = AccessController.doPrivileged(pa);
+ return ()->userName;
+ } else {
+ throw new IOException(e);
+ }
+ }
+ }
+ 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, we try to determine the group of the zip archive itself.
+ // If this is not possible/unsupported, we will return a group principal going by
+ // the same name as the default owner.
+ private GroupPrincipal initGroup(Path zfpath, Map<String, ?> env) throws IOException {
+ Object o = env.get(OPT_DEFAULT_GROUP);
+ if (o == null) {
+ try {
+ PosixFileAttributeView zfpv = Files.getFileAttributeView(zfpath, PosixFileAttributeView.class);
+ if (zfpv == null) {
+ return defaultOwner::getName;
+ }
+ PrivilegedExceptionAction<GroupPrincipal> pa = ()->zfpv.readAttributes().group();
+ return AccessController.doPrivileged(pa);
+ } catch (UnsupportedOperationException | PrivilegedActionException e) {
+ if (e instanceof UnsupportedOperationException ||
+ e.getCause() instanceof NoSuchFileException)
+ {
+ return defaultOwner::getName;
+ } else {
+ throw new IOException(e);
+ }
+ }
+ }
+ 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<>();
+ 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;
}
*** 336,350 ****
return null;
} else if (inode instanceof Entry) {
return (Entry)inode;
} else if (inode.pos == -1) {
// pseudo directory, uses METHOD_STORED
! Entry e = new Entry(inode.name, inode.isdir, METHOD_STORED);
e.mtime = e.atime = e.ctime = zfsDefaultTimeStamp;
return e;
} else {
! return new Entry(this, inode);
}
} finally {
endRead();
}
}
--- 456,472 ----
return null;
} else if (inode instanceof Entry) {
return (Entry)inode;
} else if (inode.pos == -1) {
// pseudo directory, uses METHOD_STORED
! Entry e = supportPosix ?
! new PosixEntry(inode.name, inode.isdir, METHOD_STORED) :
! new Entry(inode.name, inode.isdir, METHOD_STORED);
e.mtime = e.atime = e.ctime = zfsDefaultTimeStamp;
return e;
} else {
! return supportPosix ? new PosixEntry(this, inode) : new Entry(this, inode);
}
} finally {
endRead();
}
}
*** 385,394 ****
--- 507,575 ----
} 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
+ if (e instanceof PosixEntry) {
+ ((PosixEntry)e).owner = owner;
+ 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
+ if (e instanceof PosixEntry) {
+ ((PosixEntry)e).group = group;
+ 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();
+ }
+ }
+
boolean exists(byte[] path) {
beginRead();
try {
ensureOpen();
return getInode(path) != null;
*** 446,456 ****
try {
ensureOpen();
if (dir.length == 0 || exists(dir)) // root dir, or existing dir
throw new FileAlreadyExistsException(getString(dir));
checkParents(dir);
! Entry e = new Entry(dir, Entry.NEW, true, METHOD_STORED);
update(e);
} finally {
endWrite();
}
}
--- 627,639 ----
try {
ensureOpen();
if (dir.length == 0 || exists(dir)) // root dir, or existing dir
throw new FileAlreadyExistsException(getString(dir));
checkParents(dir);
! Entry e = supportPosix ?
! new PosixEntry(dir, Entry.NEW, true, METHOD_STORED, attrs) :
! new Entry(dir, Entry.NEW, true, METHOD_STORED, attrs);
update(e);
} finally {
endWrite();
}
}
*** 487,497 ****
throw new FileAlreadyExistsException(getString(dst));
} else {
checkParents(dst);
}
// copy eSrc entry and change name
! Entry u = new Entry(eSrc, Entry.COPY);
u.name(dst);
if (eSrc.type == Entry.NEW || eSrc.type == Entry.FILECH) {
u.type = eSrc.type; // make it the same type
if (deletesrc) { // if it's a "rename", take the data
u.bytes = eSrc.bytes;
--- 670,682 ----
throw new FileAlreadyExistsException(getString(dst));
} else {
checkParents(dst);
}
// copy eSrc entry and change name
! Entry u = supportPosix ?
! new PosixEntry((PosixEntry)eSrc, Entry.COPY) :
! new Entry(eSrc, Entry.COPY);
u.name(dst);
if (eSrc.type == Entry.NEW || eSrc.type == Entry.FILECH) {
u.type = eSrc.type; // make it the same type
if (deletesrc) { // if it's a "rename", take the data
u.bytes = eSrc.bytes;
*** 551,566 ****
try (InputStream is = getInputStream(e)) {
is.transferTo(os);
}
return os;
}
! return getOutputStream(new Entry(e, Entry.NEW));
} else {
if (!hasCreate && !hasCreateNew)
throw new NoSuchFileException(getString(path));
checkParents(path);
! return getOutputStream(new Entry(path, Entry.NEW, false, defaultCompressionMethod));
}
} finally {
endRead();
}
}
--- 736,754 ----
try (InputStream is = getInputStream(e)) {
is.transferTo(os);
}
return os;
}
! return getOutputStream(supportPosix ?
! new PosixEntry((PosixEntry)e, Entry.NEW) : new Entry(e, Entry.NEW));
} else {
if (!hasCreate && !hasCreateNew)
throw new NoSuchFileException(getString(path));
checkParents(path);
! return getOutputStream(supportPosix ?
! new PosixEntry(path, Entry.NEW, false, defaultCompressionMethod) :
! new Entry(path, Entry.NEW, false, defaultCompressionMethod));
}
} finally {
endRead();
}
}
*** 643,653 ****
Entry e = getEntry(path);
if (e != null) {
if (e.isDir() || options.contains(CREATE_NEW))
throw new FileAlreadyExistsException(getString(path));
SeekableByteChannel sbc =
! new EntryOutputChannel(new Entry(e, Entry.NEW));
if (options.contains(APPEND)) {
try (InputStream is = getInputStream(e)) { // copyover
byte[] buf = new byte[8192];
ByteBuffer bb = ByteBuffer.wrap(buf);
int n;
--- 831,843 ----
Entry e = getEntry(path);
if (e != null) {
if (e.isDir() || options.contains(CREATE_NEW))
throw new FileAlreadyExistsException(getString(path));
SeekableByteChannel sbc =
! new EntryOutputChannel(supportPosix ?
! new PosixEntry((PosixEntry)e, Entry.NEW) :
! new Entry(e, Entry.NEW));
if (options.contains(APPEND)) {
try (InputStream is = getInputStream(e)) { // copyover
byte[] buf = new byte[8192];
ByteBuffer bb = ByteBuffer.wrap(buf);
int n;
*** 662,672 ****
}
if (!options.contains(CREATE) && !options.contains(CREATE_NEW))
throw new NoSuchFileException(getString(path));
checkParents(path);
return new EntryOutputChannel(
! new Entry(path, Entry.NEW, false, defaultCompressionMethod));
} finally {
endRead();
}
} else {
beginRead();
--- 852,864 ----
}
if (!options.contains(CREATE) && !options.contains(CREATE_NEW))
throw new NoSuchFileException(getString(path));
checkParents(path);
return new EntryOutputChannel(
! supportPosix ?
! new PosixEntry(path, Entry.NEW, false, defaultCompressionMethod, attrs) :
! new Entry(path, Entry.NEW, false, defaultCompressionMethod, attrs));
} finally {
endRead();
}
} else {
beginRead();
*** 726,736 ****
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 = defaultCompressionMethod;
}
// is there a better way to hook into the FileChannel's close method?
--- 918,931 ----
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 : (
! supportPosix ?
! new PosixEntry(path, tmpfile, Entry.FILECH, attrs) :
! new Entry(path, tmpfile, Entry.FILECH, attrs));
if (forWrite) {
u.flag = FLAG_DATADESCR;
u.method = defaultCompressionMethod;
}
// is there a better way to hook into the FileChannel's close method?
*** 1341,1351 ****
}
if (inode.name.length == 1 && inode.name[0] == '/') {
continue; // no root '/' directory even if it
// exists in original zip/jar file.
}
! e = new Entry(this, inode);
try {
if (buf == null)
buf = new byte[8192];
written += copyLOCEntry(e, false, os, written, buf);
elist.add(e);
--- 1536,1546 ----
}
if (inode.name.length == 1 && inode.name[0] == '/') {
continue; // no root '/' directory even if it
// exists in original zip/jar file.
}
! e = supportPosix ? new PosixEntry(this, inode) : new Entry(this, inode);
try {
if (buf == null)
buf = new byte[8192];
written += copyLOCEntry(e, false, os, written, buf);
elist.add(e);
*** 1415,1425 ****
IndexNode inode = getInode(path);
if (inode instanceof Entry)
return (Entry)inode;
if (inode == null || inode.pos == -1)
return null;
! return new Entry(this, inode);
}
public void deleteFile(byte[] path, boolean failIfNotExists)
throws IOException
{
--- 1610,1620 ----
IndexNode inode = getInode(path);
if (inode instanceof Entry)
return (Entry)inode;
if (inode == null || inode.pos == -1)
return null;
! return supportPosix ? new PosixEntry(this, inode): new Entry(this, inode);
}
public void deleteFile(byte[] path, boolean failIfNotExists)
throws IOException
{
*** 2051,2060 ****
--- 2246,2256 ----
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
*** 2079,2095 ****
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(byte[] name, Path file, int type) {
! this(name, type, false, METHOD_STORED);
this.file = file;
}
Entry(Entry e, int type) {
name(e.name);
--- 2275,2298 ----
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(byte[] name, Path file, int type, FileAttribute<?>... attrs) {
! this(name, type, false, METHOD_STORED, attrs);
this.file = file;
}
Entry(Entry e, int type) {
name(e.name);
*** 2109,2118 ****
--- 2312,2322 ----
this.attrs = e.attrs;
this.attrsEx = e.attrsEx;
*/
this.locoff = e.locoff;
this.comment = e.comment;
+ this.posixPerms = e.posixPerms;
this.type = type;
}
Entry(ZipFileSystem zipfs, IndexNode inode) throws IOException {
readCEN(zipfs, inode);
*** 2133,2142 ****
--- 2337,2355 ----
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.
+ */
+ private int versionMadeBy(int version) {
+ return (posixPerms < 0) ? version :
+ VERSION_BASE_UNIX | (version & 0xff);
+ }
+
///////////////////// CEN //////////////////////
private void readCEN(ZipFileSystem zipfs, IndexNode inode) throws IOException {
byte[] cen = zipfs.cen;
int pos = inode.pos;
if (!cenSigAt(cen, pos))
*** 2155,2164 ****
--- 2368,2380 ----
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;
*** 2221,2231 ****
} else { // Extended Timestamp otherwise
elenEXTT = 9; // only mtime in cen
}
}
writeInt(os, CENSIG); // CEN header signature
! 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));
--- 2437,2447 ----
} 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));
*** 2240,2250 ****
} 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 (zip64) {
writeShort(os, EXTID_ZIP64);// Zip64 extra
writeShort(os, elen64 - 4); // size of "this" extra block
--- 2456,2468 ----
} 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
*** 2525,2534 ****
--- 2743,2756 ----
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());
+ Set<PosixFilePermission> permissions = storedPermissions().orElse(null);
+ if (permissions != null) {
+ fm.format(" permissions : %s%n", permissions);
+ }
fm.close();
return sb.toString();
}
///////// basic file attributes ///////////
*** 2605,2614 ****
--- 2827,2892 ----
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);
+ }
+ }
+
+ final class PosixEntry extends Entry implements PosixFileAttributes {
+ private UserPrincipal owner = defaultOwner;
+ private GroupPrincipal group = defaultGroup;
+
+ PosixEntry(byte[] name, boolean isdir, int method) {
+ super(name, isdir, method);
+ }
+
+ PosixEntry(byte[] name, int type, boolean isdir, int method, FileAttribute<?>... attrs) {
+ super(name, type, isdir, method, attrs);
+ }
+
+ PosixEntry(byte[] name, Path file, int type, FileAttribute<?>... attrs) {
+ super(name, file, type, attrs);
+ }
+
+ PosixEntry(PosixEntry e, int type) {
+ super(e, type);
+ this.owner = e.owner;
+ this.group = e.group;
+ }
+
+ PosixEntry(ZipFileSystem zipfs, IndexNode inode) throws IOException {
+ super(zipfs, inode);
+ }
+
+ @Override
+ public UserPrincipal owner() {
+ return owner;
+ }
+
+ @Override
+ public GroupPrincipal group() {
+ return group;
+ }
+
+ @Override
+ public Set<PosixFilePermission> permissions() {
+ return storedPermissions().orElse(Set.copyOf(defaultPermissions));
+ }
}
private static class ExistingChannelCloser {
private final Path path;
private final SeekableByteChannel ch;
< prev index next >