View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   https://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.commons.compress.archivers.zip;
21  
22  import static org.apache.commons.compress.AbstractTest.getFile;
23  import static org.junit.Assert.assertNull;
24  import static org.junit.jupiter.api.Assertions.assertEquals;
25  import static org.junit.jupiter.api.Assertions.assertFalse;
26  import static org.junit.jupiter.api.Assertions.assertNotEquals;
27  import static org.junit.jupiter.api.Assertions.assertNotNull;
28  import static org.junit.jupiter.api.Assertions.assertSame;
29  import static org.junit.jupiter.api.Assertions.assertThrows;
30  import static org.junit.jupiter.api.Assertions.assertTrue;
31  
32  import java.io.ByteArrayOutputStream;
33  import java.nio.file.attribute.FileTime;
34  import java.time.Instant;
35  import java.util.NoSuchElementException;
36  import java.util.zip.ZipEntry;
37  import java.util.zip.ZipException;
38  
39  import org.apache.commons.compress.utils.ByteUtils;
40  import org.apache.commons.compress.utils.TimeUtils;
41  import org.apache.commons.io.file.attribute.FileTimes;
42  import org.junit.jupiter.api.Test;
43  
44  /**
45   * JUnit tests for org.apache.commons.compress.archivers.zip.ZipEntry.
46   */
47  class ZipArchiveEntryTest {
48  
49      @Test
50      void bestEffortIncludesUnparseableExtraData() throws Exception {
51          final ZipExtraField[] extraFields = parsingModeBehaviorTestData();
52          final ZipArchiveEntry ze = new ZipArchiveEntry("foo");
53          ze.setExtraFields(extraFields);
54          final ZipExtraField[] read = ze.getExtraFields(ZipArchiveEntry.ExtraFieldParsingMode.BEST_EFFORT);
55          assertEquals(extraFields.length, read.length);
56      }
57  
58      private ZipExtraField[] parsingModeBehaviorTestData() {
59          final AsiExtraField a = new AsiExtraField();
60          a.setDirectory(true);
61          a.setMode(0755);
62          final UnrecognizedExtraField u = new UnrecognizedExtraField();
63          u.setHeaderId(ExtraFieldUtilsTest.UNRECOGNIZED_HEADER);
64          u.setLocalFileDataData(ByteUtils.EMPTY_BYTE_ARRAY);
65          final UnparseableExtraFieldData x = new UnparseableExtraFieldData();
66          final byte[] unparseable = { 0, 0, (byte) 0xff, (byte) 0xff, 0, 0, 0 };
67          x.parseFromLocalFileData(unparseable, 0, unparseable.length);
68          return new ZipExtraField[] { a, u, x };
69      }
70  
71      /**
72       * test handling of extra fields
73       */
74      @Test
75      void testAddAsFirstExtraField() {
76          final AsiExtraField a = new AsiExtraField();
77          a.setDirectory(true);
78          a.setMode(0755);
79          final UnrecognizedExtraField u = new UnrecognizedExtraField();
80          u.setHeaderId(ExtraFieldUtilsTest.UNRECOGNIZED_HEADER);
81          u.setLocalFileDataData(ByteUtils.EMPTY_BYTE_ARRAY);
82  
83          final ZipArchiveEntry ze = new ZipArchiveEntry("test/");
84          ze.setExtraFields(new ZipExtraField[] { a, u });
85          final byte[] data1 = ze.getExtra();
86  
87          final UnrecognizedExtraField u2 = new UnrecognizedExtraField();
88          u2.setHeaderId(ExtraFieldUtilsTest.UNRECOGNIZED_HEADER);
89          u2.setLocalFileDataData(new byte[] { 1 });
90  
91          ze.addAsFirstExtraField(u2);
92          final byte[] data2 = ze.getExtra();
93          ZipExtraField[] result = ze.getExtraFields();
94          assertEquals(2, result.length, "second pass");
95          assertSame(u2, result[0]);
96          assertSame(a, result[1]);
97          assertEquals(data1.length + 1, data2.length, "length second pass");
98  
99          final UnrecognizedExtraField u3 = new UnrecognizedExtraField();
100         u3.setHeaderId(new ZipShort(2));
101         u3.setLocalFileDataData(new byte[] { 1 });
102         ze.addAsFirstExtraField(u3);
103         result = ze.getExtraFields();
104         assertEquals(3, result.length, "third pass");
105         assertSame(u3, result[0]);
106         assertSame(u2, result[1]);
107         assertSame(a, result[2]);
108     }
109 
110     /**
111      * Test case for <a href="https://issues.apache.org/jira/browse/COMPRESS-93">COMPRESS-93</a>.
112      */
113     @Test
114     void testCompressionMethod() throws Exception {
115         try (ZipArchiveOutputStream zos = new ZipArchiveOutputStream(new ByteArrayOutputStream())) {
116             final ZipArchiveEntry entry = new ZipArchiveEntry("foo");
117             assertEquals(-1, entry.getMethod());
118             assertFalse(zos.canWriteEntryData(entry));
119 
120             entry.setMethod(ZipEntry.STORED);
121             assertEquals(ZipEntry.STORED, entry.getMethod());
122             assertTrue(zos.canWriteEntryData(entry));
123 
124             entry.setMethod(ZipEntry.DEFLATED);
125             assertEquals(ZipEntry.DEFLATED, entry.getMethod());
126             assertTrue(zos.canWriteEntryData(entry));
127 
128             // Test the unsupported "imploded" compression method (6)
129             entry.setMethod(6);
130             assertEquals(6, entry.getMethod());
131             assertFalse(zos.canWriteEntryData(entry));
132         }
133     }
134 
135     @Test
136     void testCopyConstructor() throws Exception {
137         final ZipArchiveEntry archiveEntry = new ZipArchiveEntry("fred");
138         archiveEntry.setUnixMode(0664);
139         archiveEntry.setMethod(ZipEntry.DEFLATED);
140         archiveEntry.getGeneralPurposeBit().useStrongEncryption(true);
141         final ZipArchiveEntry copy = new ZipArchiveEntry(archiveEntry);
142         assertEquals(archiveEntry, copy);
143     }
144 
145     @Test
146     void testDraconicThrowsOnUnparseableExtraData() throws Exception {
147         final ZipExtraField[] extraFields = parsingModeBehaviorTestData();
148         final ZipArchiveEntry ze = new ZipArchiveEntry("foo");
149         ze.setExtraFields(extraFields);
150         assertThrows(ZipException.class, () -> ze.getExtraFields(ZipArchiveEntry.ExtraFieldParsingMode.DRACONIC));
151     }
152 
153     /**
154      * test handling of extra fields via central directory
155      */
156     @Test
157     void testExtraFieldMerging() {
158         final AsiExtraField a = new AsiExtraField();
159         a.setDirectory(true);
160         a.setMode(0755);
161         final UnrecognizedExtraField u = new UnrecognizedExtraField();
162         u.setHeaderId(ExtraFieldUtilsTest.UNRECOGNIZED_HEADER);
163         u.setLocalFileDataData(ByteUtils.EMPTY_BYTE_ARRAY);
164 
165         final ZipArchiveEntry ze = new ZipArchiveEntry("test/");
166         ze.setExtraFields(new ZipExtraField[] { a, u });
167 
168         // merge
169         // Header-ID 1 + length 1 + one byte of data
170         final byte[] b = ExtraFieldUtilsTest.UNRECOGNIZED_HEADER.getBytes();
171         ze.setCentralDirectoryExtra(new byte[] { b[0], b[1], 1, 0, 127 });
172 
173         ZipExtraField[] result = ze.getExtraFields();
174         assertEquals(2, result.length, "first pass");
175         assertSame(a, result[0]);
176         assertEquals(ExtraFieldUtilsTest.UNRECOGNIZED_HEADER, result[1].getHeaderId());
177         assertEquals(new ZipShort(0), result[1].getLocalFileDataLength());
178         assertEquals(new ZipShort(1), result[1].getCentralDirectoryLength());
179 
180         // add new
181         // Header-ID 2 + length 0
182         ze.setCentralDirectoryExtra(new byte[] { 2, 0, 0, 0 });
183 
184         result = ze.getExtraFields();
185         assertEquals(3, result.length, "second pass");
186 
187         // merge
188         // Header-ID 2 + length 1 + one byte of data
189         ze.setExtra(new byte[] { 2, 0, 1, 0, 127 });
190 
191         result = ze.getExtraFields();
192         assertEquals(3, result.length, "third pass");
193         assertSame(a, result[0]);
194         assertEquals(new ZipShort(2), result[2].getHeaderId());
195         assertEquals(new ZipShort(1), result[2].getLocalFileDataLength());
196         assertEquals(new ZipShort(0), result[2].getCentralDirectoryLength());
197     }
198 
199     /**
200      * test handling of extra fields
201      */
202     @Test
203     void testExtraFields() {
204         final AsiExtraField a = new AsiExtraField();
205         a.setDirectory(true);
206         a.setMode(0755);
207         final UnrecognizedExtraField u = new UnrecognizedExtraField();
208         u.setHeaderId(ExtraFieldUtilsTest.UNRECOGNIZED_HEADER);
209         u.setLocalFileDataData(ByteUtils.EMPTY_BYTE_ARRAY);
210 
211         final ZipArchiveEntry ze = new ZipArchiveEntry("test/");
212         ze.setExtraFields(new ZipExtraField[] { a, u });
213         final byte[] data1 = ze.getExtra();
214         ZipExtraField[] result = ze.getExtraFields();
215         assertEquals(2, result.length, "first pass");
216         assertSame(a, result[0]);
217         assertSame(u, result[1]);
218 
219         final UnrecognizedExtraField u2 = new UnrecognizedExtraField();
220         u2.setHeaderId(ExtraFieldUtilsTest.UNRECOGNIZED_HEADER);
221         u2.setLocalFileDataData(new byte[] { 1 });
222 
223         ze.addExtraField(u2);
224         final byte[] data2 = ze.getExtra();
225         result = ze.getExtraFields();
226         assertEquals(2, result.length, "second pass");
227         assertSame(a, result[0]);
228         assertSame(u2, result[1]);
229         assertEquals(data1.length + 1, data2.length, "length second pass");
230 
231         final UnrecognizedExtraField u3 = new UnrecognizedExtraField();
232         u3.setHeaderId(new ZipShort(2));
233         u3.setLocalFileDataData(new byte[] { 1 });
234         ze.addExtraField(u3);
235         result = ze.getExtraFields();
236         assertEquals(3, result.length, "third pass");
237 
238         ze.removeExtraField(ExtraFieldUtilsTest.UNRECOGNIZED_HEADER);
239         final byte[] data3 = ze.getExtra();
240         result = ze.getExtraFields();
241         assertEquals(2, result.length, "fourth pass");
242         assertSame(a, result[0]);
243         assertSame(u3, result[1]);
244         assertEquals(data2.length, data3.length, "length fourth pass");
245 
246         assertThrows(NoSuchElementException.class, () -> ze.removeExtraField(ExtraFieldUtilsTest.UNRECOGNIZED_HEADER), "should be no such element");
247     }
248 
249     @Test
250     void testIsUnixSymlink() {
251         final ZipArchiveEntry ze = new ZipArchiveEntry("foo");
252         ze.setUnixMode(UnixStat.LINK_FLAG);
253         assertTrue(ze.isUnixSymlink());
254         ze.setUnixMode(UnixStat.LINK_FLAG | UnixStat.DIR_FLAG);
255         assertFalse(ze.isUnixSymlink());
256     }
257 
258     /**
259      * @see "https://issues.apache.org/jira/browse/COMPRESS-379"
260      */
261     @Test
262     void testIsUnixSymlinkIsFalseIfMoreThanOneFlagIsSet() throws Exception {
263         try (ZipFile zf = ZipFile.builder().setFile(getFile("COMPRESS-379.jar")).get()) {
264             final ZipArchiveEntry ze = zf.getEntry("META-INF/maven/");
265             assertFalse(ze.isUnixSymlink());
266         }
267     }
268 
269     /**
270      * Test case for <a href="https://issues.apache.org/jira/browse/COMPRESS-94">COMPRESS-94</a>.
271      */
272     @Test
273     void testNotEquals() {
274         final ZipArchiveEntry entry1 = new ZipArchiveEntry("foo");
275         final ZipArchiveEntry entry2 = new ZipArchiveEntry("bar");
276         assertNotEquals(entry1, entry2);
277     }
278 
279     /**
280      * Tests comment's influence on equals comparisons.
281      *
282      * @see "https://issues.apache.org/jira/browse/COMPRESS-187"
283      */
284     @Test
285     void testNullCommentEqualsEmptyComment() {
286         final ZipArchiveEntry entry1 = new ZipArchiveEntry("foo");
287         final ZipArchiveEntry entry2 = new ZipArchiveEntry("foo");
288         final ZipArchiveEntry entry3 = new ZipArchiveEntry("foo");
289         entry1.setComment(null);
290         entry2.setComment("");
291         entry3.setComment("bar");
292         assertEquals(entry1, entry2);
293         assertNotEquals(entry1, entry3);
294         assertNotEquals(entry2, entry3);
295     }
296 
297     @Test
298     void testOnlyParseableLenientExcludesUnparseableExtraData() throws Exception {
299         final ZipExtraField[] extraFields = parsingModeBehaviorTestData();
300         final ZipArchiveEntry ze = new ZipArchiveEntry("foo");
301         ze.setExtraFields(extraFields);
302         final ZipExtraField[] read = ze.getExtraFields(ZipArchiveEntry.ExtraFieldParsingMode.ONLY_PARSEABLE_LENIENT);
303         assertEquals(extraFields.length, read.length + 1);
304     }
305 
306     @Test
307     void testOnlyParseableStrictExcludesUnparseableExtraData() throws Exception {
308         final ZipExtraField[] extraFields = parsingModeBehaviorTestData();
309         final ZipArchiveEntry ze = new ZipArchiveEntry("foo");
310         ze.setExtraFields(extraFields);
311         final ZipExtraField[] read = ze.getExtraFields(ZipArchiveEntry.ExtraFieldParsingMode.ONLY_PARSEABLE_STRICT);
312         assertEquals(extraFields.length, read.length + 1);
313     }
314 
315     @Test
316     void testReparsingUnicodeExtraWithUnsupportedversionThrowsInStrictMode() throws Exception {
317         try (ZipFile zf = ZipFile.builder().setFile(getFile("COMPRESS-479.zip")).get()) {
318             final ZipArchiveEntry ze = zf.getEntry("%U20AC_for_Dollar.txt");
319             assertThrows(ZipException.class, () -> ze.getExtraFields(ZipArchiveEntry.ExtraFieldParsingMode.STRICT_FOR_KNOW_EXTRA_FIELDS));
320         }
321     }
322 
323     @Test
324     void testShouldNotSetExtraDateFieldsIfDateFitsInDosDates() {
325         final ZipArchiveEntry ze = new ZipArchiveEntry();
326         final FileTime time = FileTime.from(ZipUtilTest.toLocalInstant("2022-12-28T20:39:33.1234567"));
327         ze.setTime(time);
328 
329         assertEquals(time.toMillis(), ze.getTime());
330         assertEquals(time.toMillis(), ze.getLastModifiedTime().toMillis());
331         assertNull(ze.getExtraField(X5455_ExtendedTimestamp.HEADER_ID));
332         assertNull(ze.getExtraField(X000A_NTFS.HEADER_ID));
333 
334         final long dosTime = ZipLong.getValue(ZipUtil.toDosTime(ze.getTime()));
335         ZipUtilTest.assertDosDate(dosTime, 2022, 12, 28, 20, 39, 32); // DOS dates only store even seconds
336     }
337 
338     @Test
339     void testShouldNotSetInfoZipFieldIfAnyDatesExceedUnixTime() {
340         final ZipArchiveEntry ze = new ZipArchiveEntry();
341         final FileTime accessTime = FileTime.from(Instant.parse("2022-12-29T21:40:34.1234567Z"));
342         final FileTime creationTime = FileTime.from(Instant.parse("2038-12-28T20:39:33.1234567Z"));
343         final long time = Instant.parse("2020-03-04T12:34:56.1234567Z").toEpochMilli();
344         ze.setTime(time);
345         ze.setLastAccessTime(accessTime);
346         ze.setCreationTime(creationTime);
347 
348         assertEquals(time, ze.getTime());
349         assertEquals(time, ze.getLastModifiedTime().toMillis());
350         assertNull(ze.getExtraField(X5455_ExtendedTimestamp.HEADER_ID));
351         final X000A_NTFS ntfs = (X000A_NTFS) ze.getExtraField(X000A_NTFS.HEADER_ID);
352         assertNotNull(ntfs);
353         assertEquals(TimeUtils.toNtfsTime(time), ntfs.getModifyTime().getLongValue());
354         assertEquals(FileTimes.toNtfsTime(accessTime), ntfs.getAccessTime().getLongValue());
355         assertEquals(FileTimes.toNtfsTime(creationTime), ntfs.getCreateTime().getLongValue());
356     }
357 
358     @Test
359     void testShouldNotSetInfoZipFieldIfDateExceedsUnixTime() {
360         final ZipArchiveEntry ze = new ZipArchiveEntry();
361         final FileTime time = FileTime.from(ZipUtilTest.toLocalInstant("2138-11-27T00:00:00"));
362         ze.setTime(time.toMillis());
363 
364         assertEquals(time.toMillis(), ze.getTime());
365         assertEquals(time, ze.getLastModifiedTime());
366         assertNull(ze.getExtraField(X5455_ExtendedTimestamp.HEADER_ID));
367         final X000A_NTFS ntfs = (X000A_NTFS) ze.getExtraField(X000A_NTFS.HEADER_ID);
368         assertNotNull(ntfs);
369         assertEquals(FileTimes.toNtfsTime(time), ntfs.getModifyTime().getLongValue());
370         assertEquals(0L, ntfs.getAccessTime().getLongValue());
371         assertEquals(0L, ntfs.getCreateTime().getLongValue());
372     }
373 
374     @Test
375     void testShouldSetExtraDateFieldsIfAccessDateIsSet() {
376         final ZipArchiveEntry ze = new ZipArchiveEntry();
377         final FileTime lastAccessTime = FileTime.from(Instant.parse("2022-12-28T20:39:33.1234567Z"));
378         final long time = Instant.parse("2020-03-04T12:34:56.1234567Z").toEpochMilli();
379         ze.setTime(time);
380         ze.setLastAccessTime(lastAccessTime);
381 
382         assertEquals(time, ze.getTime());
383         assertEquals(time, ze.getLastModifiedTime().toMillis());
384         final X5455_ExtendedTimestamp extendedTimestamp = (X5455_ExtendedTimestamp) ze.getExtraField(X5455_ExtendedTimestamp.HEADER_ID);
385         assertNotNull(extendedTimestamp);
386         assertEquals(FileTimes.toUnixTime(lastAccessTime), extendedTimestamp.getAccessTime().getValue());
387         assertNull(extendedTimestamp.getCreateTime());
388         final X000A_NTFS ntfs = (X000A_NTFS) ze.getExtraField(X000A_NTFS.HEADER_ID);
389         assertNotNull(ntfs);
390         assertEquals(TimeUtils.toNtfsTime(time), ntfs.getModifyTime().getLongValue());
391         assertEquals(FileTimes.toNtfsTime(lastAccessTime), ntfs.getAccessTime().getLongValue());
392         assertEquals(0L, ntfs.getCreateTime().getLongValue());
393     }
394 
395     @Test
396     void testShouldSetExtraDateFieldsIfAllDatesAreSet() {
397         final ZipArchiveEntry ze = new ZipArchiveEntry();
398         final FileTime accessTime = FileTime.from(Instant.parse("2022-12-29T21:40:34.1234567Z"));
399         final FileTime creationTime = FileTime.from(Instant.parse("2022-12-28T20:39:33.1234567Z"));
400         final long time = Instant.parse("2020-03-04T12:34:56.1234567Z").toEpochMilli();
401         ze.setTime(time);
402         ze.setLastAccessTime(accessTime);
403         ze.setCreationTime(creationTime);
404 
405         assertEquals(time, ze.getTime());
406         assertEquals(time, ze.getLastModifiedTime().toMillis());
407         final X5455_ExtendedTimestamp extendedTimestamp = (X5455_ExtendedTimestamp) ze.getExtraField(X5455_ExtendedTimestamp.HEADER_ID);
408         assertNotNull(extendedTimestamp);
409         assertEquals(FileTimes.toUnixTime(accessTime), extendedTimestamp.getAccessTime().getValue());
410         assertEquals(FileTimes.toUnixTime(creationTime), extendedTimestamp.getCreateTime().getValue());
411         final X000A_NTFS ntfs = (X000A_NTFS) ze.getExtraField(X000A_NTFS.HEADER_ID);
412         assertNotNull(ntfs);
413         assertEquals(TimeUtils.toNtfsTime(time), ntfs.getModifyTime().getLongValue());
414         assertEquals(FileTimes.toNtfsTime(accessTime), ntfs.getAccessTime().getLongValue());
415         assertEquals(FileTimes.toNtfsTime(creationTime), ntfs.getCreateTime().getLongValue());
416     }
417 
418     @Test
419     void testShouldSetExtraDateFieldsIfCreationDateIsSet() {
420         final ZipArchiveEntry ze = new ZipArchiveEntry();
421         final FileTime creationTime = FileTime.from(Instant.parse("2022-12-28T20:39:33.1234567Z"));
422         final long time = Instant.parse("2020-03-04T12:34:56.1234567Z").toEpochMilli();
423         ze.setTime(time);
424         ze.setCreationTime(creationTime);
425 
426         assertEquals(time, ze.getTime());
427         assertEquals(time, ze.getLastModifiedTime().toMillis());
428         final X5455_ExtendedTimestamp extendedTimestamp = (X5455_ExtendedTimestamp) ze.getExtraField(X5455_ExtendedTimestamp.HEADER_ID);
429         assertNotNull(extendedTimestamp);
430         assertNull(extendedTimestamp.getAccessTime());
431         assertEquals(FileTimes.toUnixTime(creationTime), extendedTimestamp.getCreateTime().getValue());
432         final X000A_NTFS ntfs = (X000A_NTFS) ze.getExtraField(X000A_NTFS.HEADER_ID);
433         assertNotNull(ntfs);
434         assertEquals(TimeUtils.toNtfsTime(time), ntfs.getModifyTime().getLongValue());
435         assertEquals(0L, ntfs.getAccessTime().getLongValue());
436         assertEquals(FileTimes.toNtfsTime(creationTime), ntfs.getCreateTime().getLongValue());
437     }
438 
439     @Test
440     void testShouldSetExtraDateFieldsIfDateExceedsDosDate() {
441         final ZipArchiveEntry ze = new ZipArchiveEntry();
442         final FileTime time = FileTime.from(ZipUtilTest.toLocalInstant("1975-11-27T00:00:00"));
443         ze.setTime(time.toMillis());
444 
445         assertEquals(time.toMillis(), ze.getTime());
446         assertEquals(time, ze.getLastModifiedTime());
447         final X5455_ExtendedTimestamp extendedTimestamp = (X5455_ExtendedTimestamp) ze.getExtraField(X5455_ExtendedTimestamp.HEADER_ID);
448         assertNotNull(extendedTimestamp);
449         assertEquals(FileTimes.toUnixTime(time), extendedTimestamp.getModifyTime().getValue());
450         assertNull(extendedTimestamp.getAccessTime());
451         assertNull(extendedTimestamp.getCreateTime());
452         final X000A_NTFS ntfs = (X000A_NTFS) ze.getExtraField(X000A_NTFS.HEADER_ID);
453         assertNotNull(ntfs);
454         assertEquals(FileTimes.toNtfsTime(time), ntfs.getModifyTime().getLongValue());
455         assertEquals(0L, ntfs.getAccessTime().getLongValue());
456         assertEquals(0L, ntfs.getCreateTime().getLongValue());
457     }
458 
459     @Test
460     void testShouldSetExtraDateFieldsIfModifyDateIsExplicitlySet() {
461         final ZipArchiveEntry ze = new ZipArchiveEntry();
462         final FileTime time = FileTime.from(Instant.parse("2022-12-28T20:39:33.1234567Z"));
463         ze.setLastModifiedTime(time);
464 
465         assertEquals(time.toMillis(), ze.getTime());
466         assertEquals(time, ze.getLastModifiedTime());
467         final X5455_ExtendedTimestamp extendedTimestamp = (X5455_ExtendedTimestamp) ze.getExtraField(X5455_ExtendedTimestamp.HEADER_ID);
468         assertNotNull(extendedTimestamp);
469         assertEquals(FileTimes.toUnixTime(time), extendedTimestamp.getModifyTime().getValue());
470         assertNull(extendedTimestamp.getAccessTime());
471         assertNull(extendedTimestamp.getCreateTime());
472         final X000A_NTFS ntfs = (X000A_NTFS) ze.getExtraField(X000A_NTFS.HEADER_ID);
473         assertNotNull(ntfs);
474         assertEquals(FileTimes.toNtfsTime(time), ntfs.getModifyTime().getLongValue());
475         assertEquals(0L, ntfs.getAccessTime().getLongValue());
476         assertEquals(0L, ntfs.getCreateTime().getLongValue());
477     }
478 
479     @Test
480     void testStrictForKnowExtraFieldsIncludesUnparseableExtraData() throws Exception {
481         final ZipExtraField[] extraFields = parsingModeBehaviorTestData();
482         final ZipArchiveEntry ze = new ZipArchiveEntry("foo");
483         ze.setExtraFields(extraFields);
484         final ZipExtraField[] read = ze.getExtraFields(ZipArchiveEntry.ExtraFieldParsingMode.STRICT_FOR_KNOW_EXTRA_FIELDS);
485         assertEquals(extraFields.length, read.length);
486     }
487 
488     @Test
489     void testUnixMode() {
490         ZipArchiveEntry ze = new ZipArchiveEntry("foo");
491         assertEquals(0, ze.getPlatform());
492         ze.setUnixMode(0755);
493         assertEquals(3, ze.getPlatform());
494         assertEquals(0755, ze.getExternalAttributes() >> 16 & 0xFFFF);
495         assertEquals(0, ze.getExternalAttributes() & 0xFFFF);
496 
497         ze.setUnixMode(0444);
498         assertEquals(3, ze.getPlatform());
499         assertEquals(0444, ze.getExternalAttributes() >> 16 & 0xFFFF);
500         assertEquals(1, ze.getExternalAttributes() & 0xFFFF);
501 
502         ze = new ZipArchiveEntry("foo/");
503         assertEquals(0, ze.getPlatform());
504         ze.setUnixMode(0777);
505         assertEquals(3, ze.getPlatform());
506         assertEquals(0777, ze.getExternalAttributes() >> 16 & 0xFFFF);
507         assertEquals(0x10, ze.getExternalAttributes() & 0xFFFF);
508 
509         ze.setUnixMode(0577);
510         assertEquals(3, ze.getPlatform());
511         assertEquals(0577, ze.getExternalAttributes() >> 16 & 0xFFFF);
512         assertEquals(0x11, ze.getExternalAttributes() & 0xFFFF);
513     }
514 
515     @Test
516     void testZipArchiveClone() throws Exception {
517         try (ZipFile zf = ZipFile.builder().setFile(getFile("COMPRESS-479.zip")).get()) {
518             final ZipArchiveEntry ze = zf.getEntry("%U20AC_for_Dollar.txt");
519             final ZipArchiveEntry clonedZe = (ZipArchiveEntry) ze.clone();
520             assertEquals(ze, clonedZe);
521         }
522     }
523 }