71 import static jdk.nio.zipfs.ZipUtils.*;
72
73 /**
74 * A FileSystem built on a zip file
75 *
76 * @author Xueming Shen
77 */
78 class ZipFileSystem extends FileSystem {
79 // statics
80 private static final boolean isWindows = AccessController.doPrivileged(
81 (PrivilegedAction<Boolean>)()->System.getProperty("os.name")
82 .startsWith("Windows"));
83 private static final byte[] ROOTPATH = new byte[] { '/' };
84 private static final String OPT_POSIX = "enablePosixFileAttributes";
85 private static final String OPT_DEFAULT_OWNER = "defaultOwner";
86 private static final String OPT_DEFAULT_GROUP = "defaultGroup";
87 private static final String OPT_DEFAULT_PERMISSIONS = "defaultPermissions";
88
89 private static final Set<PosixFilePermission> DEFAULT_PERMISSIONS =
90 PosixFilePermissions.fromString("rwxrwxrwx");
91
92 private final ZipFileSystemProvider provider;
93 private final Path zfpath;
94 final ZipCoder zc;
95 private final ZipPath rootdir;
96 private boolean readOnly; // readonly file system, false by default
97
98 // default time stamp for pseudo entries
99 private final long zfsDefaultTimeStamp = System.currentTimeMillis();
100
101 // configurable by env map
102 private final boolean noExtt; // see readExtra()
103 private final boolean useTempFile; // use a temp file for newOS, default
104 // is to use BAOS for better performance
105 private final boolean forceEnd64;
106 private final int defaultCompressionMethod; // METHOD_STORED if "noCompression=true"
107 // METHOD_DEFLATED otherwise
108
109 // POSIX support
110 final boolean supportPosix;
111 private final UserPrincipal defaultOwner;
112 private final GroupPrincipal defaultGroup;
113 private final Set<PosixFilePermission> defaultPermissions;
114
115 private final Set<String> supportedFileAttributeViews;
116
117 ZipFileSystem(ZipFileSystemProvider provider,
118 Path zfpath,
119 Map<String, ?> env) throws IOException
120 {
121 // default encoding for name/comment
122 String nameEncoding = env.containsKey("encoding") ?
123 (String)env.get("encoding") : "UTF-8";
124 this.noExtt = "false".equals(env.get("zipinfo-time"));
125 this.useTempFile = isTrue(env, "useTempFile");
126 this.forceEnd64 = isTrue(env, "forceZIP64End");
127 this.defaultCompressionMethod = isTrue(env, "noCompression") ? METHOD_STORED : METHOD_DEFLATED;
128 this.supportPosix = isTrue(env, OPT_POSIX);
129 this.defaultOwner = initOwner(zfpath, env);
130 this.defaultGroup = initGroup(zfpath, env);
131 this.defaultPermissions = initPermissions(env);
132 this.supportedFileAttributeViews = supportPosix ?
133 Set.of("basic", "posix", "zip") : Set.of("basic", "zip");
134 if (Files.notExists(zfpath)) {
135 // create a new zip if it doesn't exist
136 if (isTrue(env, "create")) {
137 try (OutputStream os = Files.newOutputStream(zfpath, CREATE_NEW, WRITE)) {
138 new END().write(os, 0, forceEnd64);
139 }
140 } else {
141 throw new FileSystemNotFoundException(zfpath.toString());
142 }
143 }
144 // sm and existence check
145 zfpath.getFileSystem().provider().checkAccess(zfpath, AccessMode.READ);
146 boolean writeable = AccessController.doPrivileged(
147 (PrivilegedAction<Boolean>)()->Files.isWritable(zfpath));
148 this.readOnly = !writeable;
149 this.zc = ZipCoder.get(nameEncoding);
150 this.rootdir = new ZipPath(this, new byte[]{'/'});
151 this.ch = Files.newByteChannel(zfpath, READ);
152 try {
153 this.cen = initCEN();
154 } catch (IOException x) {
155 try {
156 this.ch.close();
157 } catch (IOException xx) {
158 x.addSuppressed(xx);
159 }
160 throw x;
161 }
162 this.provider = provider;
163 this.zfpath = zfpath;
164 }
165
166 // returns true if there is a name=true/"true" setting in env
167 private static boolean isTrue(Map<String, ?> env, String name) {
168 return "true".equals(env.get(name)) || TRUE.equals(env.get(name));
169 }
170
171 // Initialize the default owner for files inside the zip archive.
172 // If not specified in env, it is the owner of the archive. If no owner can
173 // be determined, we try to go with system property "user.name". If that's not
174 // accessible, we return "<zipfs_default>".
175 private UserPrincipal initOwner(Path zfpath, Map<String, ?> env) throws IOException {
176 Object o = env.get(OPT_DEFAULT_OWNER);
177 if (o == null) {
178 try {
179 PrivilegedExceptionAction<UserPrincipal> pa = ()->Files.getOwner(zfpath);
180 return AccessController.doPrivileged(pa);
181 } catch (UnsupportedOperationException | PrivilegedActionException e) {
182 if (e instanceof UnsupportedOperationException ||
183 e.getCause() instanceof NoSuchFileException)
184 {
185 PrivilegedAction<String> pa = ()->System.getProperty("user.name");
|
71 import static jdk.nio.zipfs.ZipUtils.*;
72
73 /**
74 * A FileSystem built on a zip file
75 *
76 * @author Xueming Shen
77 */
78 class ZipFileSystem extends FileSystem {
79 // statics
80 private static final boolean isWindows = AccessController.doPrivileged(
81 (PrivilegedAction<Boolean>)()->System.getProperty("os.name")
82 .startsWith("Windows"));
83 private static final byte[] ROOTPATH = new byte[] { '/' };
84 private static final String OPT_POSIX = "enablePosixFileAttributes";
85 private static final String OPT_DEFAULT_OWNER = "defaultOwner";
86 private static final String OPT_DEFAULT_GROUP = "defaultGroup";
87 private static final String OPT_DEFAULT_PERMISSIONS = "defaultPermissions";
88
89 private static final Set<PosixFilePermission> DEFAULT_PERMISSIONS =
90 PosixFilePermissions.fromString("rwxrwxrwx");
91 // Property used to specify the compression mode to use
92 private static final String COMPRESSION_METHOD = "compressionMethod";
93 // Value specified for compressionMethod property to compress Zip entries
94 public static final String DEFLATED_COMPRESSION_METHOD = "DEFLATED";
95 // Value specified for compressionMethod property to not compress Zip entries
96 public static final String STORED_COMPRESSION_METHOD = "STORED";
97
98 private final ZipFileSystemProvider provider;
99 private final Path zfpath;
100 final ZipCoder zc;
101 private final ZipPath rootdir;
102 private boolean readOnly; // readonly file system, false by default
103
104 // default time stamp for pseudo entries
105 private final long zfsDefaultTimeStamp = System.currentTimeMillis();
106
107 // configurable by env map
108 private final boolean noExtt; // see readExtra()
109 private final boolean useTempFile; // use a temp file for newOS, default
110 // is to use BAOS for better performance
111 private final boolean forceEnd64;
112 private final int defaultCompressionMethod; // METHOD_STORED if "noCompression=true"
113 // METHOD_DEFLATED otherwise
114
115 // POSIX support
116 final boolean supportPosix;
117 private final UserPrincipal defaultOwner;
118 private final GroupPrincipal defaultGroup;
119 private final Set<PosixFilePermission> defaultPermissions;
120
121 private final Set<String> supportedFileAttributeViews;
122
123 ZipFileSystem(ZipFileSystemProvider provider,
124 Path zfpath,
125 Map<String, ?> env) throws IOException
126 {
127 // default encoding for name/comment
128 String nameEncoding = env.containsKey("encoding") ?
129 (String)env.get("encoding") : "UTF-8";
130 this.noExtt = "false".equals(env.get("zipinfo-time"));
131 this.useTempFile = isTrue(env, "useTempFile");
132 this.forceEnd64 = isTrue(env, "forceZIP64End");
133 this.defaultCompressionMethod = getDefaultCompressionMethod(env);
134 this.supportPosix = isTrue(env, OPT_POSIX);
135 this.defaultOwner = initOwner(zfpath, env);
136 this.defaultGroup = initGroup(zfpath, env);
137 this.defaultPermissions = initPermissions(env);
138 this.supportedFileAttributeViews = supportPosix ?
139 Set.of("basic", "posix", "zip") : Set.of("basic", "zip");
140 if (Files.notExists(zfpath)) {
141 // create a new zip if it doesn't exist
142 if (isTrue(env, "create")) {
143 try (OutputStream os = Files.newOutputStream(zfpath, CREATE_NEW, WRITE)) {
144 new END().write(os, 0, forceEnd64);
145 }
146 } else {
147 throw new FileSystemNotFoundException(zfpath.toString());
148 }
149 }
150 // sm and existence check
151 zfpath.getFileSystem().provider().checkAccess(zfpath, AccessMode.READ);
152 boolean writeable = AccessController.doPrivileged(
153 (PrivilegedAction<Boolean>)()->Files.isWritable(zfpath));
154 this.readOnly = !writeable;
155 this.zc = ZipCoder.get(nameEncoding);
156 this.rootdir = new ZipPath(this, new byte[]{'/'});
157 this.ch = Files.newByteChannel(zfpath, READ);
158 try {
159 this.cen = initCEN();
160 } catch (IOException x) {
161 try {
162 this.ch.close();
163 } catch (IOException xx) {
164 x.addSuppressed(xx);
165 }
166 throw x;
167 }
168 this.provider = provider;
169 this.zfpath = zfpath;
170 }
171
172 /**
173 * Return the compression method to use(STORED or DEFLATED). If the
174 * property {@code commpressionMethod} is set use its value to determine
175 * the compression method to use. If the property is not set, then the
176 * default compression is DEFLATED unless the property {@code noCompression}
177 * is set which is supported for backwards compatability.
178 * @param env Zip FS map of properties
179 * @return The Compression method to use
180 */
181 private int getDefaultCompressionMethod(Map<String, ?> env) {
182 int result =
183 isTrue(env, "noCompression") ? METHOD_STORED : METHOD_DEFLATED;
184 if(env.containsKey(COMPRESSION_METHOD)) {
185 Object compressionMethod = env.get(COMPRESSION_METHOD);
186 if(compressionMethod != null) {
187 if(compressionMethod instanceof String) {
188 switch(((String) compressionMethod).toUpperCase()) {
189 case STORED_COMPRESSION_METHOD:
190 result = METHOD_STORED;
191 break;
192 case DEFLATED_COMPRESSION_METHOD:
193 result = METHOD_DEFLATED;
194 break;
195 default:
196 throw new IllegalArgumentException(String.format(
197 "The value for the %s property must be %s or %s",
198 COMPRESSION_METHOD, STORED_COMPRESSION_METHOD,
199 DEFLATED_COMPRESSION_METHOD));
200 }
201 } else {
202 throw new IllegalArgumentException(String.format(
203 "The Object type for the %s property must be a String"));
204 }
205 } else {
206 throw new IllegalArgumentException(String.format(
207 "The value for the %s property must be %s or %s",
208 COMPRESSION_METHOD, STORED_COMPRESSION_METHOD,
209 DEFLATED_COMPRESSION_METHOD));
210 }
211 }
212 return result;
213 }
214
215 // returns true if there is a name=true/"true" setting in env
216 private static boolean isTrue(Map<String, ?> env, String name) {
217 return "true".equals(env.get(name)) || TRUE.equals(env.get(name));
218 }
219
220 // Initialize the default owner for files inside the zip archive.
221 // If not specified in env, it is the owner of the archive. If no owner can
222 // be determined, we try to go with system property "user.name". If that's not
223 // accessible, we return "<zipfs_default>".
224 private UserPrincipal initOwner(Path zfpath, Map<String, ?> env) throws IOException {
225 Object o = env.get(OPT_DEFAULT_OWNER);
226 if (o == null) {
227 try {
228 PrivilegedExceptionAction<UserPrincipal> pa = ()->Files.getOwner(zfpath);
229 return AccessController.doPrivileged(pa);
230 } catch (UnsupportedOperationException | PrivilegedActionException e) {
231 if (e instanceof UnsupportedOperationException ||
232 e.getCause() instanceof NoSuchFileException)
233 {
234 PrivilegedAction<String> pa = ()->System.getProperty("user.name");
|