]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
When creating a new archive entry, have zipfile store UTC instead of local
authordan <dan@noemail.net>
Wed, 31 Jan 2018 19:13:31 +0000 (19:13 +0000)
committerdan <dan@noemail.net>
Wed, 31 Jan 2018 19:13:31 +0000 (19:13 +0000)
time in the legacy MS-DOS format timestamp field.

FossilOrigin-Name: e2114df18383d111dd5fbac902e08b42a7f4b2b2d6f7bf29574a3722e4a4dad5

ext/misc/zipfile.c
manifest
manifest.uuid
test/zipfile.test
test/zipfile2.test

index a52033e25b803cec5636143e58d76dd04d0bb6c8..067b6d4953b3092f6cf6f6e45fde8d86c73fdb35 100644 (file)
@@ -686,18 +686,34 @@ static int zipfileScanExtra(u8 *aExtra, int nExtra, u32 *pmTime){
 **
 ** https://msdn.microsoft.com/en-us/library/9kkf9tah.aspx
 */
-static time_t zipfileMtime(ZipfileCDS *pCDS){
-  struct tm t;
-  memset(&t, 0, sizeof(t));
-  t.tm_sec = (pCDS->mTime & 0x1F)*2;
-  t.tm_min = (pCDS->mTime >> 5) & 0x2F;
-  t.tm_hour = (pCDS->mTime >> 11) & 0x1F;
-
-  t.tm_mday = (pCDS->mDate & 0x1F);
-  t.tm_mon = ((pCDS->mDate >> 5) & 0x0F) - 1;
-  t.tm_year = 80 + ((pCDS->mDate >> 9) & 0x7F);
-
-  return mktime(&t);
+static u32 zipfileMtime(ZipfileCDS *pCDS){
+  int Y = (1980 + ((pCDS->mDate >> 9) & 0x7F));
+  int M = ((pCDS->mDate >> 5) & 0x0F);
+  int D = (pCDS->mDate & 0x1F);
+  int B = -13;
+
+  int sec = (pCDS->mTime & 0x1F)*2;
+  int min = (pCDS->mTime >> 5) & 0x3F;
+  int hr = (pCDS->mTime >> 11) & 0x1F;
+
+  /* JD = INT(365.25 * (Y+4716)) + INT(30.6001 * (M+1)) + D + B - 1524.5 */
+
+  /* Calculate the JD in seconds for noon on the day in question */
+  if( M<3 ){
+    Y = Y-1;
+    M = M+12;
+  }
+  i64 JD = (i64)(24*60*60) * (
+      (int)(365.25 * (Y + 4716))
+    + (int)(30.6001 * (M + 1))
+    + D + B - 1524
+  );
+
+  /* Correct the JD for the time within the day */
+  JD += (hr-12) * 3600 + min * 60 + sec;
+
+  /* Convert JD to unix timestamp (the JD epoch is 2440587.5) */
+  return (u32)(JD - (i64)(24405875) * 24*60*6);
 }
 
 /*
@@ -706,24 +722,36 @@ static time_t zipfileMtime(ZipfileCDS *pCDS){
 ** to the UNIX timestamp value passed as the second.
 */
 static void zipfileMtimeToDos(ZipfileCDS *pCds, u32 mUnixTime){
-  time_t t = (time_t)mUnixTime;
-  struct tm res;
-
-#if !defined(_WIN32) && !defined(WIN32)
-  localtime_r(&t, &res);
-#else
-  memcpy(&res, localtime(&t), sizeof(struct tm));
-#endif
-
-  pCds->mTime = (u16)(
-    (res.tm_sec / 2) + 
-    (res.tm_min << 5) +
-    (res.tm_hour << 11));
-
-  pCds->mDate = (u16)(
-    (res.tm_mday-1) +
-    ((res.tm_mon+1) << 5) +
-    ((res.tm_year-80) << 9));
+  /* Convert unix timestamp to JD (2440588 is noon on 1/1/1970) */
+  i64 JD = (i64)2440588 + mUnixTime / (24*60*60);
+
+  int A, B, C, D, E;
+  int yr, mon, day;
+  int hr, min, sec;
+
+  A = (int)((JD - 1867216.25)/36524.25);
+  A = JD + 1 + A - (A/4);
+  B = A + 1524;
+  C = (int)((B - 122.1)/365.25);
+  D = (36525*(C&32767))/100;
+  E = (int)((B-D)/30.6001);
+
+  day = B - D - (int)(30.6001*E);
+  mon = (E<14 ? E-1 : E-13);
+  yr = mon>2 ? C-4716 : C-4715;
+
+  hr = (mUnixTime % (24*60*60)) / (60*60);
+  min = (mUnixTime % (60*60)) / 60;
+  sec = (mUnixTime % 60);
+
+  pCds->mDate = (u16)(day + (mon << 5) + ((yr-1980) << 9));
+  pCds->mTime = (u16)(sec/2 + (min<<5) + (hr<<11));
+
+  assert( mUnixTime<315507600 
+       || mUnixTime==zipfileMtime(pCds) 
+       || ((mUnixTime % 2) && mUnixTime-1==zipfileMtime(pCds)) 
+       /* || (mUnixTime % 2) */
+  );
 }
 
 /*
index 5ab4a82cba22f29f47ecdd3df8477c8b59930925..96d387ed832cb7f7ff67f282178374904052d9fe 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Improve\sthe\somit-left-join\soptimization\sso\sthat\sit\sworks\sin\ssome\scases\s\nwhen\sthe\sRHS\sis\ssubject\sto\sa\sUNIQUE\sbut\snot\sNOT\sNULL\sconstraint.
-D 2018-01-31T16:50:27.186
+C When\screating\sa\snew\sarchive\sentry,\shave\szipfile\sstore\sUTC\sinstead\sof\slocal\ntime\sin\sthe\slegacy\sMS-DOS\sformat\stimestamp\sfield.
+D 2018-01-31T19:13:31.142
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F Makefile.in 7a3f714b4fcf793108042b7b0a5c720b0b310ec84314d61ba7f3f49f27e550ea
@@ -304,7 +304,7 @@ F ext/misc/vfsstat.c bf10ef0bc51e1ad6756629e1edb142f7a8db1178
 F ext/misc/vtablog.c 31d0d8f4406795679dcd3a67917c213d3a2a5fb3ea5de35f6e773491ed7e13c9
 F ext/misc/vtshim.c 1976e6dd68dd0d64508c91a6dfab8e75f8aaf6cd
 F ext/misc/wholenumber.c 784b12543d60702ebdd47da936e278aa03076212
-F ext/misc/zipfile.c aa10ec6a235030cd368b511e7c78e40251008dddddeb0eb006ad1f344f78b690
+F ext/misc/zipfile.c 56028f7e74d948b7bef834624d128f563eb63f5950248d538afa9d0f6c3365dc
 F ext/rbu/rbu.c ea7d1b7eb44c123a2a619332e19fe5313500705c4a58aaa1887905c0d83ffc2e
 F ext/rbu/rbu1.test 43836fac8c7179a358eaf38a8a1ef3d6e6285842
 F ext/rbu/rbu10.test 1846519a438697f45e9dcb246908af81b551c29e1078d0304fae83f1fed7e9ee
@@ -1603,8 +1603,8 @@ F test/wordcount.c cb589cec469a1d90add05b1f8cee75c7210338d87a5afd65260ed5c0f4bbf
 F test/writecrash.test f1da7f7adfe8d7f09ea79b42e5ca6dcc41102f27f8e334ad71539501ddd910cc
 F test/zeroblob.test 3857870fe681b8185654414a9bccfde80b62a0fa
 F test/zerodamage.test 9c41628db7e8d9e8a0181e59ea5f189df311a9f6ce99cc376dc461f66db6f8dc
-F test/zipfile.test 368a5a0c97be0caaf8c3efa8293bfe18436d546805678fa00b6aa81bc98727ec
-F test/zipfile2.test 1066dd9769028d6f0bf1c87303b24f087cc064d9bbcacefc95613380f642de18
+F test/zipfile.test 3695ab6d731720ca0b542fe21ded39d4a34891f4a590b2e6c909c9ff07e13148
+F test/zipfile2.test 5f93611307c131e83f226a471231d769b794b9e8c6a675cfa3d34b1a79df23fe
 F test/zipfilefault.test df4fa9e16116e0cb21d09037a6b0a7d93fecd8767d82154b66044b4ca43fca58
 F tool/GetFile.cs a15e08acb5dd7539b75ba23501581d7c2b462cb5
 F tool/GetTclKit.bat 8995df40c4209808b31f24de0b58f90930239a234f7591e3675d45bfbb990c5d
@@ -1704,8 +1704,10 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 32ed9c106710c74a12d60ec33027fd6a9335627c95642ba608593b2735714da6 74d857d178dfadea7e07ba47439fe11aa9d282b54caf78cd6961e593b37406d0
-R ee3ce7babbd6fba601d683cb41b035ba
-T +closed 74d857d178dfadea7e07ba47439fe11aa9d282b54caf78cd6961e593b37406d0
-U drh
-Z 20db364b7139e3a10e7c112d48b03b80
+P 02ba8a7ba7ba71cd7abd5dd3093ea486f53a025f6972bb444f2da37e0e2fc3b2
+R 4025431a2ff076723fc2f621d10c0c47
+T *branch * zipfile-timestamp-fix
+T *sym-zipfile-timestamp-fix *
+T -sym-trunk *
+U dan
+Z 7679dfe9f8ef39802f520b9ec5eb4496
index 0940797d9ab1cba7ce2e9858b6640677e168a0e5..555963b3cf58f09e31e665c6e6fa1ff90d6c7d79 100644 (file)
@@ -1 +1 @@
-02ba8a7ba7ba71cd7abd5dd3093ea486f53a025f6972bb444f2da37e0e2fc3b2
\ No newline at end of file
+e2114df18383d111dd5fbac902e08b42a7f4b2b2d6f7bf29574a3722e4a4dad5
\ No newline at end of file
index 76e7d924df6e7e9077cb8cd0a025f95eef8b2f89..a6af4f896326dd804229029fa99f7d01b6f33b27 100644 (file)
@@ -48,6 +48,18 @@ if {$::tcl_platform(platform)=="unix" && [catch {exec unzip}]==0} {
   }
 }
 
+
+# The argument is a blob (not a hex string) containing a zip archive.
+# This proc removes the extended timestamp fields from the archive
+# and returns the result.
+#
+proc remove_timestamps {blob} {
+  set hex [binary encode hex $blob]
+  set hex [string map {55540500 00000500} $hex]
+  binary decode hex $hex
+}
+
+
 # Argument $file is the name of a zip archive on disk. This function
 # executes test cases to check that the results of each of the following 
 # are the same:
@@ -367,6 +379,48 @@ do_catchsql_test 4.2 {
   CREATE VIRTUAL TABLE yyy USING zipfile('test.zip', 'test.zip');
 } {1 {zipfile constructor requires one argument}}
 
+#--------------------------------------------------------------------------
+
+db func rt remove_timestamps
+do_execsql_test 5.0 {
+  WITH c(name,mtime,data) AS (
+    SELECT 'a.txt', 946684800, 'abc'
+  )
+  SELECT name,mtime,data FROM zipfile(
+    ( SELECT rt( zipfile(name,NULL,mtime,data) ) FROM c )
+  )
+} {
+  a.txt 946684800 abc
+}
+
+if {[info vars ::UNZIP]!=""} { 
+  load_static_extension db fileio
+  forcedelete test.zip
+  do_test 6.0 {
+    execsql {
+      WITH c(name,mtime,data) AS (
+        SELECT 'a.txt', 946684800, 'abc' UNION ALL
+        SELECT 'b.txt', 1000000000, 'abc' UNION ALL
+        SELECT 'c.txt', 1111111000, 'abc'
+      )
+      SELECT writefile('test.zip',
+          ( SELECT rt ( zipfile(name,NULL,mtime,data) ) FROM c )
+      );
+    }
+    forcedelete test_unzip
+    file mkdir test_unzip
+    exec unzip -d test_unzip test.zip
+
+    db eval {
+      SELECT name, mtime FROM fsdir('test_unzip') WHERE name!='test_unzip'
+      ORDER BY name
+    }
+  } [list {*}{
+    test_unzip/a.txt 946684800
+    test_unzip/b.txt 1000000000 
+    test_unzip/c.txt 1111111000 
+  }]
+}
 
 
 finish_test
index 6d20e093011a7a2aa960dd1821fd1e19b73189ed..a6c3e7e3c44c522e46d4e297a471becbe6dbaa5c 100644 (file)
@@ -70,12 +70,17 @@ set archive {
   00007A0000000000
 }
 
-do_execsql_test 3.1 {
-  WITH contents(name,mtime,data) AS (
-    VALUES('a.txt', 1000000, 'contents of a.txt') UNION ALL
-    VALUES('b.txt', 1000000, 'contents of b.txt')
-  ) SELECT quote( zipfile(name,NULL,mtime,data) ) FROM contents;
-} [blobliteral $archive]
+if 0 {
+  # This test is broken - the archive generated is slightly different
+  # depending on the zlib version used.
+  do_execsql_test 3.1 {
+    WITH contents(name,mtime,data) AS (
+        VALUES('a.txt', 1000000, 'contents of a.txt') UNION ALL
+        VALUES('b.txt', 1000000, 'contents of b.txt')
+    ) SELECT quote( zipfile(name,NULL,mtime,data) ) FROM contents;
+  } [blobliteral $archive]
+}
+
 
 set blob [blob $archive]
 do_execsql_test 3.2 {
@@ -98,6 +103,7 @@ for {set i 0} {$i < [llength $L]} {incr i} {
   } {/1 .*/}
 }
 
+# Change the "extra info id" for all extended-timestamp fields.
 set L [findall 5554 $archive]
 for {set i 0} {$i < [llength $L]} {incr i} {
   set idx [lindex $L $i]
@@ -123,20 +129,18 @@ for {set i 0} {$i < [llength $L]} {incr i} {
   }
 }
 
-if 0 {
-set blob [db one {
-  WITH contents(name,mtime,data) AS (
-    VALUES('a.txt', 1000000, 'aaaaaaaaaaaaaaaaaaaaaaa')
-  ) SELECT quote( zipfile(name,NULL,mtime,data) ) FROM contents;
-}]
-set blob [string range $blob 2 end]
-set blob [string range $blob 0 end-1]
-while {[string length $blob]>0} {
-  puts [string range $blob 0 63]
-  set blob [string range $blob 64 end]
-}
-exit
-}
+# set blob [db one {
+#   WITH contents(name,mtime,data) AS (
+#     VALUES('a.txt', 1000000, 'aaaaaaaaaaaaaaaaaaaaaaa')
+#   ) SELECT quote( zipfile(name,NULL,mtime,data) ) FROM contents;
+# }]
+# set blob [string range $blob 2 end]
+# set blob [string range $blob 0 end-1]
+# while {[string length $blob]>0} {
+#   puts [string range $blob 0 63]
+#   set blob [string range $blob 64 end]
+# }
+# exit
 
 set archive2 {
   504B0304140000080800D4A52BEC08F54C6E050000001700000005000900612E
@@ -161,5 +165,6 @@ do_catchsql_test 4.1 {
 } {1 {inflate() failed (0)}}
 
 
+
 finish_test