From: dan Date: Wed, 31 Jan 2018 19:13:31 +0000 (+0000) Subject: When creating a new archive entry, have zipfile store UTC instead of local X-Git-Tag: version-3.23.0~164^2~1 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=44091ed3682ac115c108b3a1d201db76a78caf37;p=thirdparty%2Fsqlite.git When creating a new archive entry, have zipfile store UTC instead of local time in the legacy MS-DOS format timestamp field. FossilOrigin-Name: e2114df18383d111dd5fbac902e08b42a7f4b2b2d6f7bf29574a3722e4a4dad5 --- diff --git a/ext/misc/zipfile.c b/ext/misc/zipfile.c index a52033e25b..067b6d4953 100644 --- a/ext/misc/zipfile.c +++ b/ext/misc/zipfile.c @@ -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) */ + ); } /* diff --git a/manifest b/manifest index 5ab4a82cba..96d387ed83 100644 --- 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 diff --git a/manifest.uuid b/manifest.uuid index 0940797d9a..555963b3cf 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -02ba8a7ba7ba71cd7abd5dd3093ea486f53a025f6972bb444f2da37e0e2fc3b2 \ No newline at end of file +e2114df18383d111dd5fbac902e08b42a7f4b2b2d6f7bf29574a3722e4a4dad5 \ No newline at end of file diff --git a/test/zipfile.test b/test/zipfile.test index 76e7d924df..a6af4f8963 100644 --- a/test/zipfile.test +++ b/test/zipfile.test @@ -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 diff --git a/test/zipfile2.test b/test/zipfile2.test index 6d20e09301..a6c3e7e3c4 100644 --- a/test/zipfile2.test +++ b/test/zipfile2.test @@ -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