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