dotnet / android-libzipsharp

A managed wrapper (and then some) around libzip (https://libzip.org/)
MIT License
30 stars 12 forks source link

Add TimeStamp Extra Fields #48

Closed dellis1972 closed 4 years ago

dellis1972 commented 4 years ago

We have a weird problem where the Modified Date of the entry in a zip is somehow OLDER than the source file it is created from. Its consistently 1 second older, but its not 100% of the time. It seems like some sort of rounding error 🤷‍♂

It turns out this is a limitation of the Zip file format. If we read the Structure document [1] we come across this sentence.

The FAT filesystem of DOS has a timestamp resolution of only two seconds; ZIP file records mimic this. As a result, the built-in timestamp resolution of files in a ZIP archive is only two seconds, though extra fields can be used to store more precise timestamps. So a modified date in a zip file can be one of TWO seconds. We can confirm this issue by looking at the time encoding functions used within lib zip itself. The Encoding code makes used of bit shifting to encode the unix timestamp into the required Zip format. The Decoding code reverses this operation. So lets run through a few examples.

So given a time of 12:46 and 29 seconds if we run through the code we get a value of 26062 to store in the zip file local header. If we use 12:46 and 30 seconds we get 26063. So far so go. Now let's plug these encoded values into our decoding code (x << 1) & 62.

(26062 << 1) & 62 = 28 (26063 << 1) & 62 = 30

As you can see while the 30 second timestamp decoded correctly, the 29 second one did not. So when we convert these values back to DateTime values we can end up with a timestamp that is 1 second in the past as it always rounds down. This is not great when we want to try to compare timestamps with files created on disk. Say a file was created at 29 seconds and we create the zip at the same time (within a second), according to the zip it would have been created at 28 seconds. So if we use the timestamps to compare it will thing the file is NEWER. Because this only happens if the zip and the file are created within the same 1 second timespan, this is not always a problem.

To fix this issue the zip file format has the ability to add "Extra" fields to the zip for various purposes. One of which is the Timestamp Extended field. This allows use to store the actual unix time to the second within the zip file as an additional entry. We can then read this field and update the Modified date accordingly. This works around the issue with the DOS timestamp resolution.

This commit adds support for creating this new timestamp extra field. We already had support for reading them. So any zip created by LibZipSharp will automatically get this field on every platform.

[1] https://en.wikipedia.org/wiki/Zip_(file_format)#Structure