| | @@ -4013,11 +4013,11 @@ |
| 4013 | 4013 | "mtime," /* 2: Last modification time (secs since 1970)*/ |
| 4014 | 4014 | "sz," /* 3: Size of object */ |
| 4015 | 4015 | "rawdata," /* 4: Raw data */ |
| 4016 | 4016 | "data," /* 5: Uncompressed data */ |
| 4017 | 4017 | "method," /* 6: Compression method (integer) */ |
| 4018 | | - "file HIDDEN" /* 7: Name of zip file */ |
| 4018 | + "z HIDDEN" /* 7: Name of zip file */ |
| 4019 | 4019 | ") WITHOUT ROWID;"; |
| 4020 | 4020 | |
| 4021 | 4021 | #define ZIPFILE_F_COLUMN_IDX 7 /* Index of column "file" in the above */ |
| 4022 | 4022 | #define ZIPFILE_BUFFER_SIZE (64*1024) |
| 4023 | 4023 | |
| | @@ -4184,10 +4184,11 @@ |
| 4184 | 4184 | ** Cursor type for recursively iterating through a directory structure. |
| 4185 | 4185 | */ |
| 4186 | 4186 | typedef struct ZipfileCsr ZipfileCsr; |
| 4187 | 4187 | struct ZipfileCsr { |
| 4188 | 4188 | sqlite3_vtab_cursor base; /* Base class - must be first */ |
| 4189 | + i64 iId; /* Cursor ID */ |
| 4189 | 4190 | int bEof; /* True when at EOF */ |
| 4190 | 4191 | |
| 4191 | 4192 | /* Used outside of write transactions */ |
| 4192 | 4193 | FILE *pFile; /* Zip file */ |
| 4193 | 4194 | i64 iNextOff; /* Offset of next record in central directory */ |
| | @@ -4213,12 +4214,14 @@ |
| 4213 | 4214 | struct ZipfileTab { |
| 4214 | 4215 | sqlite3_vtab base; /* Base class - must be first */ |
| 4215 | 4216 | char *zFile; /* Zip file this table accesses (may be NULL) */ |
| 4216 | 4217 | u8 *aBuffer; /* Temporary buffer used for various tasks */ |
| 4217 | 4218 | |
| 4218 | | - /* The following are used by write transactions only */ |
| 4219 | 4219 | ZipfileCsr *pCsrList; /* List of cursors */ |
| 4220 | + i64 iNextCsrid; |
| 4221 | + |
| 4222 | + /* The following are used by write transactions only */ |
| 4220 | 4223 | ZipfileEntry *pFirstEntry; /* Linked list of all files (if pWriteFd!=0) */ |
| 4221 | 4224 | ZipfileEntry *pLastEntry; /* Last element in pFirstEntry list */ |
| 4222 | 4225 | FILE *pWriteFd; /* File handle open on zip archive */ |
| 4223 | 4226 | i64 szCurrent; /* Current size of zip archive */ |
| 4224 | 4227 | i64 szOrig; /* Size of archive at start of transaction */ |
| | @@ -4293,33 +4296,29 @@ |
| 4293 | 4296 | |
| 4294 | 4297 | /* |
| 4295 | 4298 | ** Constructor for a new ZipfileCsr object. |
| 4296 | 4299 | */ |
| 4297 | 4300 | static int zipfileOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCsr){ |
| 4301 | + ZipfileTab *pTab = (ZipfileTab*)p; |
| 4298 | 4302 | ZipfileCsr *pCsr; |
| 4299 | 4303 | pCsr = sqlite3_malloc(sizeof(*pCsr)); |
| 4300 | 4304 | *ppCsr = (sqlite3_vtab_cursor*)pCsr; |
| 4301 | 4305 | if( pCsr==0 ){ |
| 4302 | 4306 | return SQLITE_NOMEM; |
| 4303 | 4307 | } |
| 4304 | 4308 | memset(pCsr, 0, sizeof(*pCsr)); |
| 4309 | + pCsr->iId = ++pTab->iNextCsrid; |
| 4310 | + pCsr->pCsrNext = pTab->pCsrList; |
| 4311 | + pTab->pCsrList = pCsr; |
| 4305 | 4312 | return SQLITE_OK; |
| 4306 | 4313 | } |
| 4307 | 4314 | |
| 4308 | 4315 | /* |
| 4309 | 4316 | ** Reset a cursor back to the state it was in when first returned |
| 4310 | 4317 | ** by zipfileOpen(). |
| 4311 | 4318 | */ |
| 4312 | 4319 | static void zipfileResetCursor(ZipfileCsr *pCsr){ |
| 4313 | | - ZipfileTab *pTab = (ZipfileTab*)(pCsr->base.pVtab); |
| 4314 | | - ZipfileCsr **pp; |
| 4315 | | - |
| 4316 | | - /* Remove this cursor from the ZipfileTab.pCsrList list. */ |
| 4317 | | - for(pp=&pTab->pCsrList; *pp; pp=&((*pp)->pCsrNext)){ |
| 4318 | | - if( *pp==pCsr ) *pp = pCsr->pCsrNext; |
| 4319 | | - } |
| 4320 | | - |
| 4321 | 4320 | sqlite3_free(pCsr->cds.zFile); |
| 4322 | 4321 | pCsr->cds.zFile = 0; |
| 4323 | 4322 | pCsr->bEof = 0; |
| 4324 | 4323 | if( pCsr->pFile ){ |
| 4325 | 4324 | fclose(pCsr->pFile); |
| | @@ -4330,11 +4329,22 @@ |
| 4330 | 4329 | /* |
| 4331 | 4330 | ** Destructor for an ZipfileCsr. |
| 4332 | 4331 | */ |
| 4333 | 4332 | static int zipfileClose(sqlite3_vtab_cursor *cur){ |
| 4334 | 4333 | ZipfileCsr *pCsr = (ZipfileCsr*)cur; |
| 4334 | + ZipfileTab *pTab = (ZipfileTab*)(pCsr->base.pVtab); |
| 4335 | + ZipfileCsr **pp; |
| 4335 | 4336 | zipfileResetCursor(pCsr); |
| 4337 | + |
| 4338 | + /* Remove this cursor from the ZipfileTab.pCsrList list. */ |
| 4339 | + for(pp=&pTab->pCsrList; *pp; pp=&((*pp)->pCsrNext)){ |
| 4340 | + if( *pp==pCsr ){ |
| 4341 | + *pp = pCsr->pCsrNext; |
| 4342 | + break; |
| 4343 | + } |
| 4344 | + } |
| 4345 | + |
| 4336 | 4346 | sqlite3_free(pCsr); |
| 4337 | 4347 | return SQLITE_OK; |
| 4338 | 4348 | } |
| 4339 | 4349 | |
| 4340 | 4350 | /* |
| | @@ -4413,14 +4423,51 @@ |
| 4413 | 4423 | } |
| 4414 | 4424 | |
| 4415 | 4425 | /* |
| 4416 | 4426 | ** Magic numbers used to read CDS records. |
| 4417 | 4427 | */ |
| 4418 | | -#define ZIPFILE_CDS_FIXED_SZ 46 |
| 4419 | | -#define ZIPFILE_CDS_NFILE_OFF 28 |
| 4428 | +#define ZIPFILE_CDS_FIXED_SZ 46 |
| 4429 | +#define ZIPFILE_CDS_NFILE_OFF 28 |
| 4420 | 4430 | |
| 4421 | | -static int zipfileReadCDS(ZipfileCsr *pCsr){ |
| 4431 | +/* |
| 4432 | +** Decode the CDS record in buffer aBuf into (*pCDS). Return SQLITE_ERROR |
| 4433 | +** if the record is not well-formed, or SQLITE_OK otherwise. |
| 4434 | +*/ |
| 4435 | +static int zipfileReadCDS(u8 *aBuf, ZipfileCDS *pCDS){ |
| 4436 | + u8 *aRead = aBuf; |
| 4437 | + u32 sig = zipfileRead32(aRead); |
| 4438 | + int rc = SQLITE_OK; |
| 4439 | + if( sig!=ZIPFILE_SIGNATURE_CDS ){ |
| 4440 | + rc = SQLITE_ERROR; |
| 4441 | + }else{ |
| 4442 | + pCDS->iVersionMadeBy = zipfileRead16(aRead); |
| 4443 | + pCDS->iVersionExtract = zipfileRead16(aRead); |
| 4444 | + pCDS->flags = zipfileRead16(aRead); |
| 4445 | + pCDS->iCompression = zipfileRead16(aRead); |
| 4446 | + pCDS->mTime = zipfileRead16(aRead); |
| 4447 | + pCDS->mDate = zipfileRead16(aRead); |
| 4448 | + pCDS->crc32 = zipfileRead32(aRead); |
| 4449 | + pCDS->szCompressed = zipfileRead32(aRead); |
| 4450 | + pCDS->szUncompressed = zipfileRead32(aRead); |
| 4451 | + assert( aRead==&aBuf[ZIPFILE_CDS_NFILE_OFF] ); |
| 4452 | + pCDS->nFile = zipfileRead16(aRead); |
| 4453 | + pCDS->nExtra = zipfileRead16(aRead); |
| 4454 | + pCDS->nComment = zipfileRead16(aRead); |
| 4455 | + pCDS->iDiskStart = zipfileRead16(aRead); |
| 4456 | + pCDS->iInternalAttr = zipfileRead16(aRead); |
| 4457 | + pCDS->iExternalAttr = zipfileRead32(aRead); |
| 4458 | + pCDS->iOffset = zipfileRead32(aRead); |
| 4459 | + assert( aRead==&aBuf[ZIPFILE_CDS_FIXED_SZ] ); |
| 4460 | + } |
| 4461 | + |
| 4462 | + return rc; |
| 4463 | +} |
| 4464 | + |
| 4465 | +/* |
| 4466 | +** Read the CDS record for the current entry from disk into pCsr->cds. |
| 4467 | +*/ |
| 4468 | +static int zipfileCsrReadCDS(ZipfileCsr *pCsr){ |
| 4422 | 4469 | char **pzErr = &pCsr->base.pVtab->zErrMsg; |
| 4423 | 4470 | u8 *aRead; |
| 4424 | 4471 | int rc = SQLITE_OK; |
| 4425 | 4472 | |
| 4426 | 4473 | sqlite3_free(pCsr->cds.zFile); |
| | @@ -4434,45 +4481,23 @@ |
| 4434 | 4481 | }else{ |
| 4435 | 4482 | aRead = pCsr->pCurrent->aCdsEntry; |
| 4436 | 4483 | } |
| 4437 | 4484 | |
| 4438 | 4485 | if( rc==SQLITE_OK ){ |
| 4439 | | - u32 sig = zipfileRead32(aRead); |
| 4440 | | - if( sig!=ZIPFILE_SIGNATURE_CDS ){ |
| 4486 | + rc = zipfileReadCDS(aRead, &pCsr->cds); |
| 4487 | + if( rc!=SQLITE_OK ){ |
| 4441 | 4488 | assert( pCsr->pCurrent==0 ); |
| 4442 | 4489 | zipfileSetErrmsg(pCsr,"failed to read CDS at offset %lld",pCsr->iNextOff); |
| 4443 | | - rc = SQLITE_ERROR; |
| 4444 | 4490 | }else{ |
| 4445 | 4491 | int nRead; |
| 4446 | | - pCsr->cds.iVersionMadeBy = zipfileRead16(aRead); |
| 4447 | | - pCsr->cds.iVersionExtract = zipfileRead16(aRead); |
| 4448 | | - pCsr->cds.flags = zipfileRead16(aRead); |
| 4449 | | - pCsr->cds.iCompression = zipfileRead16(aRead); |
| 4450 | | - pCsr->cds.mTime = zipfileRead16(aRead); |
| 4451 | | - pCsr->cds.mDate = zipfileRead16(aRead); |
| 4452 | | - pCsr->cds.crc32 = zipfileRead32(aRead); |
| 4453 | | - pCsr->cds.szCompressed = zipfileRead32(aRead); |
| 4454 | | - pCsr->cds.szUncompressed = zipfileRead32(aRead); |
| 4455 | | - assert( pCsr->pCurrent |
| 4456 | | - || aRead==zipfileCsrBuffer(pCsr)+ZIPFILE_CDS_NFILE_OFF |
| 4457 | | - ); |
| 4458 | | - pCsr->cds.nFile = zipfileRead16(aRead); |
| 4459 | | - pCsr->cds.nExtra = zipfileRead16(aRead); |
| 4460 | | - pCsr->cds.nComment = zipfileRead16(aRead); |
| 4461 | | - pCsr->cds.iDiskStart = zipfileRead16(aRead); |
| 4462 | | - pCsr->cds.iInternalAttr = zipfileRead16(aRead); |
| 4463 | | - pCsr->cds.iExternalAttr = zipfileRead32(aRead); |
| 4464 | | - pCsr->cds.iOffset = zipfileRead32(aRead); |
| 4465 | | - assert( pCsr->pCurrent |
| 4466 | | - || aRead==zipfileCsrBuffer(pCsr)+ZIPFILE_CDS_FIXED_SZ |
| 4467 | | - ); |
| 4468 | | - |
| 4469 | 4492 | if( pCsr->pCurrent==0 ){ |
| 4470 | 4493 | nRead = pCsr->cds.nFile + pCsr->cds.nExtra; |
| 4471 | 4494 | aRead = zipfileCsrBuffer(pCsr); |
| 4472 | 4495 | pCsr->iNextOff += ZIPFILE_CDS_FIXED_SZ; |
| 4473 | 4496 | rc = zipfileReadData(pCsr->pFile, aRead, nRead, pCsr->iNextOff, pzErr); |
| 4497 | + }else{ |
| 4498 | + aRead = &aRead[ZIPFILE_CDS_FIXED_SZ]; |
| 4474 | 4499 | } |
| 4475 | 4500 | |
| 4476 | 4501 | if( rc==SQLITE_OK ){ |
| 4477 | 4502 | pCsr->cds.zFile = sqlite3_mprintf("%.*s", (int)pCsr->cds.nFile, aRead); |
| 4478 | 4503 | pCsr->iNextOff += pCsr->cds.nFile; |
| | @@ -4519,41 +4544,51 @@ |
| 4519 | 4544 | static FILE *zipfileGetFd(ZipfileCsr *pCsr){ |
| 4520 | 4545 | if( pCsr->pFile ) return pCsr->pFile; |
| 4521 | 4546 | return ((ZipfileTab*)(pCsr->base.pVtab))->pWriteFd; |
| 4522 | 4547 | } |
| 4523 | 4548 | |
| 4524 | | -static int zipfileReadLFH(ZipfileCsr *pCsr){ |
| 4525 | | - FILE *pFile = zipfileGetFd(pCsr); |
| 4526 | | - char **pzErr = &pCsr->base.pVtab->zErrMsg; |
| 4549 | +static int zipfileReadLFH( |
| 4550 | + FILE *pFd, |
| 4551 | + i64 iOffset, |
| 4552 | + u8 *aTmp, |
| 4553 | + ZipfileLFH *pLFH, |
| 4554 | + char **pzErr |
| 4555 | +){ |
| 4556 | + u8 *aRead = aTmp; |
| 4527 | 4557 | static const int szFix = ZIPFILE_LFH_FIXED_SZ; |
| 4528 | | - u8 *aRead = zipfileCsrBuffer(pCsr); |
| 4529 | 4558 | int rc; |
| 4530 | 4559 | |
| 4531 | | - rc = zipfileReadData(pFile, aRead, szFix, pCsr->cds.iOffset, pzErr); |
| 4560 | + rc = zipfileReadData(pFd, aRead, szFix, iOffset, pzErr); |
| 4532 | 4561 | if( rc==SQLITE_OK ){ |
| 4533 | 4562 | u32 sig = zipfileRead32(aRead); |
| 4534 | 4563 | if( sig!=ZIPFILE_SIGNATURE_LFH ){ |
| 4535 | | - zipfileSetErrmsg(pCsr, "failed to read LFH at offset %d", |
| 4536 | | - (int)pCsr->cds.iOffset |
| 4537 | | - ); |
| 4564 | + *pzErr = sqlite3_mprintf("failed to read LFH at offset %d", (int)iOffset); |
| 4538 | 4565 | rc = SQLITE_ERROR; |
| 4539 | 4566 | }else{ |
| 4540 | | - pCsr->lfh.iVersionExtract = zipfileRead16(aRead); |
| 4541 | | - pCsr->lfh.flags = zipfileRead16(aRead); |
| 4542 | | - pCsr->lfh.iCompression = zipfileRead16(aRead); |
| 4543 | | - pCsr->lfh.mTime = zipfileRead16(aRead); |
| 4544 | | - pCsr->lfh.mDate = zipfileRead16(aRead); |
| 4545 | | - pCsr->lfh.crc32 = zipfileRead32(aRead); |
| 4546 | | - pCsr->lfh.szCompressed = zipfileRead32(aRead); |
| 4547 | | - pCsr->lfh.szUncompressed = zipfileRead32(aRead); |
| 4548 | | - pCsr->lfh.nFile = zipfileRead16(aRead); |
| 4549 | | - pCsr->lfh.nExtra = zipfileRead16(aRead); |
| 4550 | | - assert( aRead==zipfileCsrBuffer(pCsr)+szFix ); |
| 4551 | | - pCsr->iDataOff = pCsr->cds.iOffset+szFix+pCsr->lfh.nFile+pCsr->lfh.nExtra; |
| 4552 | | - } |
| 4553 | | - } |
| 4554 | | - |
| 4567 | + pLFH->iVersionExtract = zipfileRead16(aRead); |
| 4568 | + pLFH->flags = zipfileRead16(aRead); |
| 4569 | + pLFH->iCompression = zipfileRead16(aRead); |
| 4570 | + pLFH->mTime = zipfileRead16(aRead); |
| 4571 | + pLFH->mDate = zipfileRead16(aRead); |
| 4572 | + pLFH->crc32 = zipfileRead32(aRead); |
| 4573 | + pLFH->szCompressed = zipfileRead32(aRead); |
| 4574 | + pLFH->szUncompressed = zipfileRead32(aRead); |
| 4575 | + pLFH->nFile = zipfileRead16(aRead); |
| 4576 | + pLFH->nExtra = zipfileRead16(aRead); |
| 4577 | + assert( aRead==&aTmp[szFix] ); |
| 4578 | + } |
| 4579 | + } |
| 4580 | + return rc; |
| 4581 | +} |
| 4582 | + |
| 4583 | +static int zipfileCsrReadLFH(ZipfileCsr *pCsr){ |
| 4584 | + FILE *pFile = zipfileGetFd(pCsr); |
| 4585 | + char **pzErr = &pCsr->base.pVtab->zErrMsg; |
| 4586 | + u8 *aRead = zipfileCsrBuffer(pCsr); |
| 4587 | + int rc = zipfileReadLFH(pFile, pCsr->cds.iOffset, aRead, &pCsr->lfh, pzErr); |
| 4588 | + pCsr->iDataOff = pCsr->cds.iOffset + ZIPFILE_LFH_FIXED_SZ; |
| 4589 | + pCsr->iDataOff += pCsr->lfh.nFile+pCsr->lfh.nExtra; |
| 4555 | 4590 | return rc; |
| 4556 | 4591 | } |
| 4557 | 4592 | |
| 4558 | 4593 | |
| 4559 | 4594 | /* |
| | @@ -4578,13 +4613,13 @@ |
| 4578 | 4613 | pCsr->bEof = 1; |
| 4579 | 4614 | } |
| 4580 | 4615 | } |
| 4581 | 4616 | |
| 4582 | 4617 | if( pCsr->bEof==0 ){ |
| 4583 | | - rc = zipfileReadCDS(pCsr); |
| 4618 | + rc = zipfileCsrReadCDS(pCsr); |
| 4584 | 4619 | if( rc==SQLITE_OK ){ |
| 4585 | | - rc = zipfileReadLFH(pCsr); |
| 4620 | + rc = zipfileCsrReadLFH(pCsr); |
| 4586 | 4621 | } |
| 4587 | 4622 | } |
| 4588 | 4623 | |
| 4589 | 4624 | return rc; |
| 4590 | 4625 | } |
| | @@ -4737,18 +4772,22 @@ |
| 4737 | 4772 | sqlite3_result_int64(ctx, zipfileMtime(pCsr)); |
| 4738 | 4773 | } |
| 4739 | 4774 | break; |
| 4740 | 4775 | } |
| 4741 | 4776 | case 3: { /* sz */ |
| 4742 | | - sqlite3_result_int64(ctx, pCsr->cds.szUncompressed); |
| 4777 | + if( sqlite3_vtab_nochange(ctx)==0 ){ |
| 4778 | + sqlite3_result_int64(ctx, pCsr->cds.szUncompressed); |
| 4779 | + } |
| 4743 | 4780 | break; |
| 4744 | 4781 | } |
| 4745 | 4782 | case 4: /* rawdata */ |
| 4783 | + if( sqlite3_vtab_nochange(ctx) ) break; |
| 4746 | 4784 | case 5: { /* data */ |
| 4747 | 4785 | if( i==4 || pCsr->cds.iCompression==0 || pCsr->cds.iCompression==8 ){ |
| 4748 | 4786 | int sz = pCsr->cds.szCompressed; |
| 4749 | | - if( sz>0 ){ |
| 4787 | + int szFinal = pCsr->cds.szUncompressed; |
| 4788 | + if( szFinal>0 ){ |
| 4750 | 4789 | u8 *aBuf = sqlite3_malloc(sz); |
| 4751 | 4790 | if( aBuf==0 ){ |
| 4752 | 4791 | rc = SQLITE_NOMEM; |
| 4753 | 4792 | }else{ |
| 4754 | 4793 | FILE *pFile = zipfileGetFd(pCsr); |
| | @@ -4756,26 +4795,37 @@ |
| 4756 | 4795 | &pCsr->base.pVtab->zErrMsg |
| 4757 | 4796 | ); |
| 4758 | 4797 | } |
| 4759 | 4798 | if( rc==SQLITE_OK ){ |
| 4760 | 4799 | if( i==5 && pCsr->cds.iCompression ){ |
| 4761 | | - zipfileInflate(ctx, aBuf, sz, pCsr->cds.szUncompressed); |
| 4800 | + zipfileInflate(ctx, aBuf, sz, szFinal); |
| 4762 | 4801 | }else{ |
| 4763 | 4802 | sqlite3_result_blob(ctx, aBuf, sz, SQLITE_TRANSIENT); |
| 4764 | 4803 | } |
| 4765 | 4804 | sqlite3_free(aBuf); |
| 4766 | 4805 | } |
| 4806 | + }else{ |
| 4807 | + /* Figure out if this is a directory or a zero-sized file. Consider |
| 4808 | + ** it to be a directory either if the mode suggests so, or if |
| 4809 | + ** the final character in the name is '/'. */ |
| 4810 | + u32 mode = pCsr->cds.iExternalAttr >> 16; |
| 4811 | + if( !(mode & S_IFDIR) && pCsr->cds.zFile[pCsr->cds.nFile-1]!='/' ){ |
| 4812 | + sqlite3_result_blob(ctx, "", 0, SQLITE_STATIC); |
| 4813 | + } |
| 4767 | 4814 | } |
| 4768 | 4815 | } |
| 4769 | 4816 | break; |
| 4770 | 4817 | } |
| 4771 | 4818 | case 6: /* method */ |
| 4772 | 4819 | sqlite3_result_int(ctx, pCsr->cds.iCompression); |
| 4773 | 4820 | break; |
| 4821 | + case 7: /* z */ |
| 4822 | + sqlite3_result_int64(ctx, pCsr->iId); |
| 4823 | + break; |
| 4774 | 4824 | } |
| 4775 | 4825 | |
| 4776 | | - return SQLITE_OK; |
| 4826 | + return rc; |
| 4777 | 4827 | } |
| 4778 | 4828 | |
| 4779 | 4829 | /* |
| 4780 | 4830 | ** Return the rowid for the current row. |
| 4781 | 4831 | */ |
| | @@ -4807,11 +4857,12 @@ |
| 4807 | 4857 | int rc; |
| 4808 | 4858 | |
| 4809 | 4859 | fseek(pFile, 0, SEEK_END); |
| 4810 | 4860 | szFile = (i64)ftell(pFile); |
| 4811 | 4861 | if( szFile==0 ){ |
| 4812 | | - return SQLITE_EMPTY; |
| 4862 | + memset(pEOCD, 0, sizeof(ZipfileEOCD)); |
| 4863 | + return SQLITE_OK; |
| 4813 | 4864 | } |
| 4814 | 4865 | nRead = (int)(MIN(szFile, ZIPFILE_BUFFER_SIZE)); |
| 4815 | 4866 | iOff = szFile - nRead; |
| 4816 | 4867 | |
| 4817 | 4868 | rc = zipfileReadData(pFile, aRead, nRead, iOff, &pTab->base.zErrMsg); |
| | @@ -4885,15 +4936,16 @@ |
| 4885 | 4936 | zipfileSetErrmsg(pCsr, "cannot open file: %s", zFile); |
| 4886 | 4937 | rc = SQLITE_ERROR; |
| 4887 | 4938 | }else{ |
| 4888 | 4939 | rc = zipfileReadEOCD(pTab, pCsr->pFile, &pCsr->eocd); |
| 4889 | 4940 | if( rc==SQLITE_OK ){ |
| 4890 | | - pCsr->iNextOff = pCsr->eocd.iOffset; |
| 4891 | | - rc = zipfileNext(cur); |
| 4892 | | - }else if( rc==SQLITE_EMPTY ){ |
| 4893 | | - rc = SQLITE_OK; |
| 4894 | | - pCsr->bEof = 1; |
| 4941 | + if( pCsr->eocd.nEntry==0 ){ |
| 4942 | + pCsr->bEof = 1; |
| 4943 | + }else{ |
| 4944 | + pCsr->iNextOff = pCsr->eocd.iOffset; |
| 4945 | + rc = zipfileNext(cur); |
| 4946 | + } |
| 4895 | 4947 | } |
| 4896 | 4948 | } |
| 4897 | 4949 | }else{ |
| 4898 | 4950 | ZipfileEntry e; |
| 4899 | 4951 | memset(&e, 0, sizeof(e)); |
| | @@ -4938,28 +4990,39 @@ |
| 4938 | 4990 | |
| 4939 | 4991 | /* |
| 4940 | 4992 | ** Add object pNew to the end of the linked list that begins at |
| 4941 | 4993 | ** ZipfileTab.pFirstEntry and ends with pLastEntry. |
| 4942 | 4994 | */ |
| 4943 | | -static void zipfileAddEntry(ZipfileTab *pTab, ZipfileEntry *pNew){ |
| 4995 | +static void zipfileAddEntry( |
| 4996 | + ZipfileTab *pTab, |
| 4997 | + ZipfileEntry *pBefore, |
| 4998 | + ZipfileEntry *pNew |
| 4999 | +){ |
| 4944 | 5000 | assert( (pTab->pFirstEntry==0)==(pTab->pLastEntry==0) ); |
| 4945 | 5001 | assert( pNew->pNext==0 ); |
| 4946 | | - if( pTab->pFirstEntry==0 ){ |
| 4947 | | - pTab->pFirstEntry = pTab->pLastEntry = pNew; |
| 5002 | + if( pBefore==0 ){ |
| 5003 | + if( pTab->pFirstEntry==0 ){ |
| 5004 | + pTab->pFirstEntry = pTab->pLastEntry = pNew; |
| 5005 | + }else{ |
| 5006 | + assert( pTab->pLastEntry->pNext==0 ); |
| 5007 | + pTab->pLastEntry->pNext = pNew; |
| 5008 | + pTab->pLastEntry = pNew; |
| 5009 | + } |
| 4948 | 5010 | }else{ |
| 4949 | | - assert( pTab->pLastEntry->pNext==0 ); |
| 4950 | | - pTab->pLastEntry->pNext = pNew; |
| 4951 | | - pTab->pLastEntry = pNew; |
| 5011 | + ZipfileEntry **pp; |
| 5012 | + for(pp=&pTab->pFirstEntry; *pp!=pBefore; pp=&((*pp)->pNext)); |
| 5013 | + pNew->pNext = pBefore; |
| 5014 | + *pp = pNew; |
| 4952 | 5015 | } |
| 4953 | 5016 | } |
| 4954 | 5017 | |
| 4955 | 5018 | static int zipfileLoadDirectory(ZipfileTab *pTab){ |
| 4956 | 5019 | ZipfileEOCD eocd; |
| 4957 | 5020 | int rc; |
| 4958 | 5021 | |
| 4959 | 5022 | rc = zipfileReadEOCD(pTab, pTab->pWriteFd, &eocd); |
| 4960 | | - if( rc==SQLITE_OK ){ |
| 5023 | + if( rc==SQLITE_OK && eocd.nEntry>0 ){ |
| 4961 | 5024 | int i; |
| 4962 | 5025 | int iOff = 0; |
| 4963 | 5026 | u8 *aBuf = sqlite3_malloc(eocd.nSize); |
| 4964 | 5027 | if( aBuf==0 ){ |
| 4965 | 5028 | rc = SQLITE_NOMEM; |
| | @@ -4993,19 +5056,17 @@ |
| 4993 | 5056 | memcpy(pNew->zPath, &aRec[ZIPFILE_CDS_FIXED_SZ], nFile); |
| 4994 | 5057 | pNew->zPath[nFile] = '\0'; |
| 4995 | 5058 | pNew->aCdsEntry = (u8*)&pNew->zPath[nFile+1]; |
| 4996 | 5059 | pNew->nCdsEntry = ZIPFILE_CDS_FIXED_SZ+nFile+nExtra+nComment; |
| 4997 | 5060 | memcpy(pNew->aCdsEntry, aRec, pNew->nCdsEntry); |
| 4998 | | - zipfileAddEntry(pTab, pNew); |
| 5061 | + zipfileAddEntry(pTab, 0, pNew); |
| 4999 | 5062 | } |
| 5000 | 5063 | |
| 5001 | 5064 | iOff += ZIPFILE_CDS_FIXED_SZ+nFile+nExtra+nComment; |
| 5002 | 5065 | } |
| 5003 | 5066 | |
| 5004 | 5067 | sqlite3_free(aBuf); |
| 5005 | | - }else if( rc==SQLITE_EMPTY ){ |
| 5006 | | - rc = SQLITE_OK; |
| 5007 | 5068 | } |
| 5008 | 5069 | |
| 5009 | 5070 | return rc; |
| 5010 | 5071 | } |
| 5011 | 5072 | |
| | @@ -5117,11 +5178,11 @@ |
| 5117 | 5178 | ){ |
| 5118 | 5179 | const char *z = (const char*)sqlite3_value_text(pVal); |
| 5119 | 5180 | u32 mode = 0; |
| 5120 | 5181 | if( z==0 ){ |
| 5121 | 5182 | mode = defaultMode; |
| 5122 | | - }else if( z[0]>=0 && z[0]<=9 ){ |
| 5183 | + }else if( z[0]>='0' && z[0]<='9' ){ |
| 5123 | 5184 | mode = (unsigned int)sqlite3_value_int(pVal); |
| 5124 | 5185 | }else{ |
| 5125 | 5186 | const char zTemplate[11] = "-rwxrwxrwx"; |
| 5126 | 5187 | int i; |
| 5127 | 5188 | if( strlen(z)!=10 ) goto parse_error; |
| | @@ -5180,83 +5241,68 @@ |
| 5180 | 5241 | int nData = 0; /* Size of pData buffer in bytes */ |
| 5181 | 5242 | int iMethod = 0; /* Compression method for new entry */ |
| 5182 | 5243 | u8 *pFree = 0; /* Free this */ |
| 5183 | 5244 | char *zFree = 0; /* Also free this */ |
| 5184 | 5245 | ZipfileCDS cds; /* New Central Directory Structure entry */ |
| 5185 | | - |
| 5246 | + ZipfileEntry *pOld = 0; |
| 5186 | 5247 | int bIsDir = 0; |
| 5187 | | - |
| 5188 | | - int mNull; |
| 5248 | + u32 iCrc32 = 0; |
| 5189 | 5249 | |
| 5190 | 5250 | assert( pTab->zFile ); |
| 5191 | 5251 | assert( pTab->pWriteFd ); |
| 5192 | 5252 | |
| 5193 | 5253 | if( sqlite3_value_type(apVal[0])!=SQLITE_NULL ){ |
| 5194 | | - if( nVal>1 ){ |
| 5195 | | - return SQLITE_CONSTRAINT; |
| 5196 | | - }else{ |
| 5197 | | - const char *zDelete = (const char*)sqlite3_value_text(apVal[0]); |
| 5198 | | - int nDelete = strlen(zDelete); |
| 5199 | | - ZipfileEntry *p; |
| 5200 | | - for(p=pTab->pFirstEntry; p; p=p->pNext){ |
| 5201 | | - if( zipfileComparePath(p->zPath, zDelete, nDelete)==0 ){ |
| 5202 | | - p->bDeleted = 1; |
| 5203 | | - break; |
| 5204 | | - } |
| 5205 | | - } |
| 5206 | | - return SQLITE_OK; |
| 5207 | | - } |
| 5208 | | - } |
| 5209 | | - |
| 5210 | | - mNull = (sqlite3_value_type(apVal[5])==SQLITE_NULL ? 0x0 : 0x8) /* sz */ |
| 5211 | | - + (sqlite3_value_type(apVal[6])==SQLITE_NULL ? 0x0 : 0x4) /* rawdata */ |
| 5212 | | - + (sqlite3_value_type(apVal[7])==SQLITE_NULL ? 0x0 : 0x2) /* data */ |
| 5213 | | - + (sqlite3_value_type(apVal[8])==SQLITE_NULL ? 0x0 : 0x1); /* method */ |
| 5214 | | - if( mNull==0x00 ){ |
| 5215 | | - /* All four are NULL - this must be a directory */ |
| 5216 | | - bIsDir = 1; |
| 5217 | | - } |
| 5218 | | - else if( mNull==0x2 || mNull==0x3 ){ |
| 5219 | | - /* Value specified for "data", and possibly "method". This must be |
| 5220 | | - ** a regular file or a symlink. */ |
| 5221 | | - const u8 *aIn = sqlite3_value_blob(apVal[7]); |
| 5222 | | - int nIn = sqlite3_value_bytes(apVal[7]); |
| 5223 | | - int bAuto = sqlite3_value_type(apVal[8])==SQLITE_NULL; |
| 5224 | | - |
| 5225 | | - iMethod = sqlite3_value_int(apVal[8]); |
| 5226 | | - sz = nIn; |
| 5227 | | - if( iMethod!=0 && iMethod!=8 ){ |
| 5228 | | - rc = SQLITE_CONSTRAINT; |
| 5229 | | - }else if( bAuto || iMethod ){ |
| 5230 | | - rc = zipfileDeflate(pTab, aIn, nIn, &pFree, &nData); |
| 5231 | | - if( rc==SQLITE_OK ){ |
| 5232 | | - if( iMethod || nData<nIn ){ |
| 5233 | | - iMethod = 8; |
| 5234 | | - pData = pFree; |
| 5235 | | - }else{ |
| 5236 | | - pData = aIn; |
| 5237 | | - nData = nIn; |
| 5238 | | - } |
| 5239 | | - } |
| 5240 | | - } |
| 5241 | | - } |
| 5242 | | - else if( mNull==0x0D ){ |
| 5243 | | - /* Values specified for "sz", "rawdata" and "method". In other words, |
| 5244 | | - ** pre-compressed data is being inserted. */ |
| 5245 | | - pData = sqlite3_value_blob(apVal[6]); |
| 5246 | | - nData = sqlite3_value_bytes(apVal[6]); |
| 5247 | | - sz = sqlite3_value_int(apVal[5]); |
| 5248 | | - iMethod = sqlite3_value_int(apVal[8]); |
| 5249 | | - if( iMethod<0 || iMethod>65535 ){ |
| 5250 | | - pTab->base.zErrMsg = sqlite3_mprintf( |
| 5251 | | - "zipfile: invalid compression method: %d", iMethod |
| 5252 | | - ); |
| 5253 | | - rc = SQLITE_ERROR; |
| 5254 | | - } |
| 5255 | | - } |
| 5256 | | - else{ |
| 5257 | | - rc = SQLITE_CONSTRAINT; |
| 5254 | + const char *zDelete = (const char*)sqlite3_value_text(apVal[0]); |
| 5255 | + int nDelete = (int)strlen(zDelete); |
| 5256 | + for(pOld=pTab->pFirstEntry; 1; pOld=pOld->pNext){ |
| 5257 | + if( pOld->bDeleted ) continue; |
| 5258 | + if( zipfileComparePath(pOld->zPath, zDelete, nDelete)==0 ){ |
| 5259 | + pOld->bDeleted = 1; |
| 5260 | + break; |
| 5261 | + } |
| 5262 | + assert( pOld->pNext ); |
| 5263 | + } |
| 5264 | + if( nVal==1 ) return SQLITE_OK; |
| 5265 | + } |
| 5266 | + |
| 5267 | + /* Check that "sz" and "rawdata" are both NULL: */ |
| 5268 | + if( sqlite3_value_type(apVal[5])!=SQLITE_NULL |
| 5269 | + || sqlite3_value_type(apVal[6])!=SQLITE_NULL |
| 5270 | + ){ |
| 5271 | + rc = SQLITE_CONSTRAINT; |
| 5272 | + } |
| 5273 | + |
| 5274 | + if( rc==SQLITE_OK ){ |
| 5275 | + if( sqlite3_value_type(apVal[7])==SQLITE_NULL ){ |
| 5276 | + /* data=NULL. A directory */ |
| 5277 | + bIsDir = 1; |
| 5278 | + }else{ |
| 5279 | + /* Value specified for "data", and possibly "method". This must be |
| 5280 | + ** a regular file or a symlink. */ |
| 5281 | + const u8 *aIn = sqlite3_value_blob(apVal[7]); |
| 5282 | + int nIn = sqlite3_value_bytes(apVal[7]); |
| 5283 | + int bAuto = sqlite3_value_type(apVal[8])==SQLITE_NULL; |
| 5284 | + |
| 5285 | + iMethod = sqlite3_value_int(apVal[8]); |
| 5286 | + sz = nIn; |
| 5287 | + pData = aIn; |
| 5288 | + nData = nIn; |
| 5289 | + if( iMethod!=0 && iMethod!=8 ){ |
| 5290 | + rc = SQLITE_CONSTRAINT; |
| 5291 | + }else if( bAuto || iMethod ){ |
| 5292 | + int nCmp; |
| 5293 | + rc = zipfileDeflate(pTab, aIn, nIn, &pFree, &nCmp); |
| 5294 | + if( rc==SQLITE_OK ){ |
| 5295 | + if( iMethod || nCmp<nIn ){ |
| 5296 | + iMethod = 8; |
| 5297 | + pData = pFree; |
| 5298 | + nData = nCmp; |
| 5299 | + } |
| 5300 | + } |
| 5301 | + iCrc32 = crc32(0, aIn, nIn); |
| 5302 | + } |
| 5303 | + } |
| 5258 | 5304 | } |
| 5259 | 5305 | |
| 5260 | 5306 | if( rc==SQLITE_OK ){ |
| 5261 | 5307 | rc = zipfileGetMode(pTab, apVal[3], |
| 5262 | 5308 | (bIsDir ? (S_IFDIR + 0755) : (S_IFREG + 0644)), &mode |
| | @@ -5293,10 +5339,11 @@ |
| 5293 | 5339 | |
| 5294 | 5340 | /* Check that we're not inserting a duplicate entry */ |
| 5295 | 5341 | if( rc==SQLITE_OK ){ |
| 5296 | 5342 | ZipfileEntry *p; |
| 5297 | 5343 | for(p=pTab->pFirstEntry; p; p=p->pNext){ |
| 5344 | + if( p->bDeleted ) continue; |
| 5298 | 5345 | if( zipfileComparePath(p->zPath, zPath, nPath)==0 ){ |
| 5299 | 5346 | rc = SQLITE_CONSTRAINT; |
| 5300 | 5347 | break; |
| 5301 | 5348 | } |
| 5302 | 5349 | } |
| | @@ -5308,28 +5355,31 @@ |
| 5308 | 5355 | cds.iVersionMadeBy = ZIPFILE_NEWENTRY_MADEBY; |
| 5309 | 5356 | cds.iVersionExtract = ZIPFILE_NEWENTRY_REQUIRED; |
| 5310 | 5357 | cds.flags = ZIPFILE_NEWENTRY_FLAGS; |
| 5311 | 5358 | cds.iCompression = (u16)iMethod; |
| 5312 | 5359 | zipfileMtimeToDos(&cds, (u32)mTime); |
| 5313 | | - cds.crc32 = crc32(0, pData, nData); |
| 5360 | + cds.crc32 = iCrc32; |
| 5314 | 5361 | cds.szCompressed = nData; |
| 5315 | 5362 | cds.szUncompressed = (u32)sz; |
| 5316 | 5363 | cds.iExternalAttr = (mode<<16); |
| 5317 | 5364 | cds.iOffset = (u32)pTab->szCurrent; |
| 5318 | 5365 | pNew = zipfileNewEntry(&cds, zPath, nPath, (u32)mTime); |
| 5319 | 5366 | if( pNew==0 ){ |
| 5320 | 5367 | rc = SQLITE_NOMEM; |
| 5321 | 5368 | }else{ |
| 5322 | | - zipfileAddEntry(pTab, pNew); |
| 5369 | + zipfileAddEntry(pTab, pOld, pNew); |
| 5323 | 5370 | } |
| 5324 | 5371 | } |
| 5325 | 5372 | |
| 5326 | 5373 | /* Append the new header+file to the archive */ |
| 5327 | 5374 | if( rc==SQLITE_OK ){ |
| 5328 | 5375 | rc = zipfileAppendEntry(pTab, &cds, zPath, nPath, pData, nData, (u32)mTime); |
| 5329 | 5376 | } |
| 5330 | 5377 | |
| 5378 | + if( rc!=SQLITE_OK && pOld ){ |
| 5379 | + pOld->bDeleted = 0; |
| 5380 | + } |
| 5331 | 5381 | sqlite3_free(pFree); |
| 5332 | 5382 | sqlite3_free(zFree); |
| 5333 | 5383 | return rc; |
| 5334 | 5384 | } |
| 5335 | 5385 | |
| | @@ -5434,10 +5484,88 @@ |
| 5434 | 5484 | } |
| 5435 | 5485 | |
| 5436 | 5486 | static int zipfileRollback(sqlite3_vtab *pVtab){ |
| 5437 | 5487 | return zipfileCommit(pVtab); |
| 5438 | 5488 | } |
| 5489 | + |
| 5490 | +static ZipfileCsr *zipfileFindCursor(ZipfileTab *pTab, i64 iId){ |
| 5491 | + ZipfileCsr *pCsr; |
| 5492 | + for(pCsr=pTab->pCsrList; pCsr; pCsr=pCsr->pCsrNext){ |
| 5493 | + if( iId==pCsr->iId ) break; |
| 5494 | + } |
| 5495 | + return pCsr; |
| 5496 | +} |
| 5497 | + |
| 5498 | +static void zipfileFunctionCds( |
| 5499 | + sqlite3_context *context, |
| 5500 | + int argc, |
| 5501 | + sqlite3_value **argv |
| 5502 | +){ |
| 5503 | + ZipfileCsr *pCsr; |
| 5504 | + ZipfileTab *pTab = (ZipfileTab*)sqlite3_user_data(context); |
| 5505 | + assert( argc>0 ); |
| 5506 | + |
| 5507 | + pCsr = zipfileFindCursor(pTab, sqlite3_value_int64(argv[0])); |
| 5508 | + if( pCsr ){ |
| 5509 | + ZipfileCDS *p = &pCsr->cds; |
| 5510 | + char *zRes = sqlite3_mprintf("{" |
| 5511 | + "\"version-made-by\" : %u, " |
| 5512 | + "\"version-to-extract\" : %u, " |
| 5513 | + "\"flags\" : %u, " |
| 5514 | + "\"compression\" : %u, " |
| 5515 | + "\"time\" : %u, " |
| 5516 | + "\"date\" : %u, " |
| 5517 | + "\"crc32\" : %u, " |
| 5518 | + "\"compressed-size\" : %u, " |
| 5519 | + "\"uncompressed-size\" : %u, " |
| 5520 | + "\"file-name-length\" : %u, " |
| 5521 | + "\"extra-field-length\" : %u, " |
| 5522 | + "\"file-comment-length\" : %u, " |
| 5523 | + "\"disk-number-start\" : %u, " |
| 5524 | + "\"internal-attr\" : %u, " |
| 5525 | + "\"external-attr\" : %u, " |
| 5526 | + "\"offset\" : %u }", |
| 5527 | + (u32)p->iVersionMadeBy, (u32)p->iVersionExtract, |
| 5528 | + (u32)p->flags, (u32)p->iCompression, |
| 5529 | + (u32)p->mTime, (u32)p->mDate, |
| 5530 | + (u32)p->crc32, (u32)p->szCompressed, |
| 5531 | + (u32)p->szUncompressed, (u32)p->nFile, |
| 5532 | + (u32)p->nExtra, (u32)p->nComment, |
| 5533 | + (u32)p->iDiskStart, (u32)p->iInternalAttr, |
| 5534 | + (u32)p->iExternalAttr, (u32)p->iOffset |
| 5535 | + ); |
| 5536 | + |
| 5537 | + if( zRes==0 ){ |
| 5538 | + sqlite3_result_error_nomem(context); |
| 5539 | + }else{ |
| 5540 | + sqlite3_result_text(context, zRes, -1, SQLITE_TRANSIENT); |
| 5541 | + sqlite3_free(zRes); |
| 5542 | + } |
| 5543 | + } |
| 5544 | +} |
| 5545 | + |
| 5546 | + |
| 5547 | +/* |
| 5548 | +** xFindFunction method. |
| 5549 | +*/ |
| 5550 | +static int zipfileFindFunction( |
| 5551 | + sqlite3_vtab *pVtab, /* Virtual table handle */ |
| 5552 | + int nArg, /* Number of SQL function arguments */ |
| 5553 | + const char *zName, /* Name of SQL function */ |
| 5554 | + void (**pxFunc)(sqlite3_context*,int,sqlite3_value**), /* OUT: Result */ |
| 5555 | + void **ppArg /* OUT: User data for *pxFunc */ |
| 5556 | +){ |
| 5557 | + if( nArg>0 ){ |
| 5558 | + if( sqlite3_stricmp("zipfile_cds", zName)==0 ){ |
| 5559 | + *pxFunc = zipfileFunctionCds; |
| 5560 | + *ppArg = (void*)pVtab; |
| 5561 | + return 1; |
| 5562 | + } |
| 5563 | + } |
| 5564 | + |
| 5565 | + return 0; |
| 5566 | +} |
| 5439 | 5567 | |
| 5440 | 5568 | /* |
| 5441 | 5569 | ** Register the "zipfile" virtual table. |
| 5442 | 5570 | */ |
| 5443 | 5571 | static int zipfileRegister(sqlite3 *db){ |
| | @@ -5458,15 +5586,18 @@ |
| 5458 | 5586 | zipfileUpdate, /* xUpdate */ |
| 5459 | 5587 | zipfileBegin, /* xBegin */ |
| 5460 | 5588 | 0, /* xSync */ |
| 5461 | 5589 | zipfileCommit, /* xCommit */ |
| 5462 | 5590 | zipfileRollback, /* xRollback */ |
| 5463 | | - 0, /* xFindMethod */ |
| 5591 | + zipfileFindFunction, /* xFindMethod */ |
| 5464 | 5592 | 0, /* xRename */ |
| 5465 | 5593 | }; |
| 5466 | 5594 | |
| 5467 | 5595 | int rc = sqlite3_create_module(db, "zipfile" , &zipfileModule, 0); |
| 5596 | + if( rc==SQLITE_OK ){ |
| 5597 | + rc = sqlite3_overload_function(db, "zipfile_cds", -1); |
| 5598 | + } |
| 5468 | 5599 | return rc; |
| 5469 | 5600 | } |
| 5470 | 5601 | #else /* SQLITE_OMIT_VIRTUALTABLE */ |
| 5471 | 5602 | # define zipfileRegister(x) SQLITE_OK |
| 5472 | 5603 | #endif |
| 5473 | 5604 | |