< prev index next >

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

Print this page
rev 53081 : 8213031: (zipfs) Add support for POSIX file permissions
Reviewed-by: simonis

@@ -41,10 +41,13 @@
 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;

@@ -369,11 +372,11 @@
             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.type = Entry.COPY;     // copy e
             if (mtime != null)
                 e.mtime = mtime.toMillis();
             if (atime != null)
                 e.atime = atime.toMillis();
             if (ctime != null)

@@ -382,10 +385,31 @@
         } 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)
         throws IOException
     {
         beginRead();
         try {

@@ -452,11 +476,11 @@
         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);
+            Entry e = new Entry(dir, Entry.NEW, true, METHOD_STORED, attrs);
             update(e);
         } finally {
             endWrite();
         }
     }

@@ -674,11 +698,11 @@
                 }
                 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)));
+                    new Entry(path, Entry.NEW, false, getCompressMethod(attrs), attrs));
 
             } finally {
                 endRead();
             }
         } else {

@@ -739,11 +763,11 @@
             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);
+            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?

@@ -1868,10 +1892,11 @@
         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

@@ -1899,16 +1924,23 @@
             this.size   = 0;
             this.csize  = 0;
             this.method = method;
         }
 
-        Entry(byte[] name, int type, boolean isdir, int 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) {
+        Entry(Entry e, int type) {
             name(e.name);
             this.isdir     = e.isdir;
             this.version   = e.version;
             this.ctime     = e.ctime;
             this.atime     = e.atime;

@@ -1924,26 +1956,46 @@
             this.attrs     = e.attrs;
             this.attrsEx   = e.attrsEx;
             */
             this.locoff    = e.locoff;
             this.comment   = e.comment;
+            this.posixPerms = e.posixPerms;
             this.type      = type;
         }
 
-        Entry (byte[] name, Path file, int 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() throws ZipException {
+        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);

@@ -1970,10 +2022,13 @@
             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;

@@ -1989,11 +2044,10 @@
             }
             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)

@@ -2020,10 +2074,12 @@
                 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;

@@ -2036,17 +2092,12 @@
                 } 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, 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

@@ -2060,14 +2111,16 @@
             } 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, 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 (elen64 != 0) {
+            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)

@@ -2102,22 +2155,22 @@
         }
 
         ///////////////////// 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;
+            boolean zip64 = false;
             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, 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

@@ -2126,20 +2179,19 @@
                 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
+                    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 (elen64 != 0) {
+                if (zip64) {
                     writeInt(os, ZIP64_MINVAL);
                     writeInt(os, ZIP64_MINVAL);
                 } else {
                     writeInt(os, csize);        // compressed size
                     writeInt(os, size);         // uncompressed size

@@ -2165,11 +2217,11 @@
                 }
             }
             writeShort(os, nlen);
             writeShort(os, elen + elen64 + elenNTFS + elenEXTT);
             writeBytes(os, zname, 1, nlen);
-            if (elen64 != 0) {
+            if (zip64) {
                 writeShort(os, EXTID_ZIP64);
                 writeShort(os, 16);
                 writeLong(os, size);
                 writeLong(os, csize);
             }

@@ -2202,11 +2254,11 @@
                 writeBytes(os, extra);
             }
             return LOCHDR + nlen + elen + elen64 + elenNTFS + elenEXTT;
         }
 
-        // Data Descriptior
+        // 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);

@@ -2377,35 +2429,69 @@
         @Override
         public Object fileKey() {
             return null;
         }
 
-        ///////// zip entry attributes ///////////
+        ///////// posix file attributes ///////////
+
+        @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);
+        }
+
+        ///////// 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 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());

@@ -2418,10 +2504,13 @@
             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();
         }
     }
 
< prev index next >