summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordkf <donal.k.fellows@manchester.ac.uk>2021-03-08 16:53:51 (GMT)
committerdkf <donal.k.fellows@manchester.ac.uk>2021-03-08 16:53:51 (GMT)
commit25bbbbf3ee5102d8fd13884456e828e6dacd4b5b (patch)
tree1084996cafda0af0735db33a2206d3f61a26bcf3
parentb9bc37e2b8d6b0ee9fa2839ad1a37f42266d417d (diff)
downloadtcl-25bbbbf3ee5102d8fd13884456e828e6dacd4b5b.zip
tcl-25bbbbf3ee5102d8fd13884456e828e6dacd4b5b.tar.gz
tcl-25bbbbf3ee5102d8fd13884456e828e6dacd4b5b.tar.bz2
Fix SEGV in zipfs mounting, and try to make that code more comprehensible
-rw-r--r--generic/tclZipfs.c699
-rw-r--r--tests/zipfs.test4
-rw-r--r--unix/Makefile.in3
3 files changed, 511 insertions, 195 deletions
diff --git a/generic/tclZipfs.c b/generic/tclZipfs.c
index d1d5deb..b7571b7 100644
--- a/generic/tclZipfs.c
+++ b/generic/tclZipfs.c
@@ -138,7 +138,8 @@
} while (0)
/*
- * Macros to read and write 16 and 32 bit integers from/to ZIP archives.
+ * Macros to read and write little-endial 16 and 32 bit integers from/to ZIP
+ * archives.
*/
#define ZipReadInt(p) \
@@ -282,9 +283,19 @@ static const char *zipfs_literal_tcl_library = NULL;
/* Function prototypes */
+static int CopyImageFile(Tcl_Interp *interp, const char *imgName,
+ Tcl_Channel out);
static inline int DescribeMounted(Tcl_Interp *interp,
const char *mountPoint);
static inline int ListMountPoints(Tcl_Interp *interp);
+static void SerializeCentralDirectoryEntry(char *buf, ZipEntry *z,
+ size_t nameLength, long long dataStartOffset);
+static void SerializeCentralDirectorySuffix(char *buf,
+ int entryCount, long long dataStartOffset,
+ long long directoryStartOffset,
+ long long suffixStartOffset);
+static void SerializeLocalEntryHeader(char *buf, ZipEntry *z,
+ int nameLength, int align);
#if !defined(STATIC_BUILD)
static int ZipfsAppHookFindTclInit(const char *archive);
#endif
@@ -309,6 +320,8 @@ static int ZipFSFileAttrsSetProc(Tcl_Interp *interp, int index,
static int ZipFSLoadFile(Tcl_Interp *interp, Tcl_Obj *path,
Tcl_LoadHandle *loadHandle,
Tcl_FSUnloadFileProc **unloadProcPtr, int flags);
+static int ZipMapArchive(Tcl_Interp *interp, ZipFile *zf,
+ void *handle);
static void ZipfsExitHandler(ClientData clientData);
static void ZipfsSetup(void);
static int ZipChannelClose(void *instanceData,
@@ -320,8 +333,8 @@ static int ZipChannelRead(void *instanceData, char *buf,
static int ZipChannelSeek(void *instanceData, long offset,
int mode, int *errloc);
#endif
-static long long ZipChannelWideSeek(void *instanceData, long long offset,
- int mode, int *errloc);
+static long long ZipChannelWideSeek(void *instanceData,
+ long long offset, int mode, int *errloc);
static void ZipChannelWatchChannel(void *instanceData,
int mask);
static int ZipChannelWrite(void *instanceData,
@@ -824,6 +837,43 @@ ZipFSLookupMount(
/*
*-------------------------------------------------------------------------
*
+ * AllocateZipFile --
+ *
+ * Allocates the memory for a ZipFile structure. Always ensures that it
+ * is zeroed out for safety.
+ *
+ * Returns:
+ * The allocated structure, or NULL if allocate fails.
+ *
+ * Side effects:
+ * The interpreter result may be written to on error. Which might fail in
+ * a low-memory situation.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+static ZipFile *
+AllocateZipFile(
+ Tcl_Interp *interp,
+ size_t mountPointNameLength)
+{
+ size_t size = sizeof(ZipFile) + mountPointNameLength + 1;
+ ZipFile *zf = (ZipFile *) attemptckalloc(size);
+
+ if (!zf) {
+ if (interp) {
+ Tcl_AppendResult(interp, "out of memory", (char *) NULL);
+ Tcl_SetErrorCode(interp, "TCL", "MALLOC", NULL);
+ }
+ } else {
+ memset(zf, 0, size);
+ }
+ return zf;
+}
+
+/*
+ *-------------------------------------------------------------------------
+ *
* ZipFSCloseArchive --
*
* This function closes a mounted ZIP archive file.
@@ -856,6 +906,10 @@ ZipFSCloseArchive(
return;
}
+ /*
+ * Remove the memory mapping, if we have one.
+ */
+
#ifdef _WIN32
if (zf->data && !zf->ptrToFree) {
UnmapViewOfFile(zf->data);
@@ -888,7 +942,7 @@ ZipFSCloseArchive(
*
* This function takes a memory mapped zip file and indexes the contents.
* When "needZip" is zero an embedded ZIP archive in an executable file
- * is accepted.
+ * is accepted. Note that we do not support ZIP64.
*
* Results:
* TCL_OK on success, TCL_ERROR otherwise with an error message placed
@@ -910,6 +964,12 @@ ZipFSFindTOC(
size_t i;
unsigned char *p, *q;
+ /*
+ * Scan backwards from the end of the file for the signature. This is
+ * necessary because ZIP archives aren't the only things that get tagged
+ * on the end of executables; digital signatures can also go there.
+ */
+
p = zf->data + zf->length - ZIP_CENTRAL_END_LEN;
while (p >= zf->data) {
if (*p == (ZIP_CENTRAL_END_SIG & 0xFF)) {
@@ -922,6 +982,11 @@ ZipFSFindTOC(
}
}
if (p < zf->data) {
+ /*
+ * Didn't find it (or not enough space for a central directory!); not
+ * a ZIP archive. This might be OK or a problem.
+ */
+
if (!needZip) {
zf->baseOffset = zf->passOffset = zf->length;
return TCL_OK;
@@ -932,6 +997,11 @@ ZipFSFindTOC(
}
goto error;
}
+
+ /*
+ * How many files in the archive? If that's bogus, we're done here.
+ */
+
zf->numFiles = ZipReadShort(p + ZIP_CENTRAL_ENTS_OFFS);
if (zf->numFiles == 0) {
if (!needZip) {
@@ -944,6 +1014,11 @@ ZipFSFindTOC(
}
goto error;
}
+
+ /*
+ * Where does the central directory start?
+ */
+
q = zf->data + ZipReadInt(p + ZIP_CENTRAL_DIRSTART_OFFS);
p -= ZipReadInt(p + ZIP_CENTRAL_DIRSIZE_OFFS);
if ((p < zf->data) || (p > zf->data + zf->length)
@@ -958,6 +1033,11 @@ ZipFSFindTOC(
}
goto error;
}
+
+ /*
+ * Read the central directory.
+ */
+
zf->baseOffset = zf->passOffset = p - q;
zf->directoryOffset = p - zf->data;
q = p;
@@ -983,6 +1063,12 @@ ZipFSFindTOC(
extra = ZipReadShort(q + ZIP_CENTRAL_EXTRALEN_OFFS);
q += pathlen + comlen + extra + ZIP_CENTRAL_HEADER_LEN;
}
+
+ /*
+ * If there's also an encoded password, extract that too (but don't decode
+ * yet).
+ */
+
q = zf->data + zf->baseOffset;
if ((zf->baseOffset >= 6) && (ZipReadInt(q - 4) == ZIP_PASSWORD_END_SIG)) {
i = q[-5];
@@ -1044,11 +1130,35 @@ ZipFSOpenArchive(
zf->baseOffset = zf->passOffset = 0;
zf->ptrToFree = NULL;
zf->passBuf[0] = 0;
+
+ /*
+ * Actually open the file.
+ */
+
zf->chan = Tcl_OpenFileChannel(interp, zipname, "rb", 0);
if (!zf->chan) {
return TCL_ERROR;
}
- if (Tcl_GetChannelHandle(zf->chan, TCL_READABLE, &handle) != TCL_OK) {
+
+ /*
+ * See if we can get the OS handle. If we can, we can use that to memory
+ * map the file, which is nice and efficient. However, it totally depends
+ * on the filename pointing to a real regular OS file.
+ *
+ * Opening real filesystem entities that are not files will lead to an
+ * error.
+ */
+
+ if (Tcl_GetChannelHandle(zf->chan, TCL_READABLE, &handle) == TCL_OK) {
+ if (ZipMapArchive(interp, zf, handle) != TCL_OK) {
+ goto error;
+ }
+ } else {
+ /*
+ * Not an OS file, but rather something in a Tcl VFS. Must copy into
+ * memory.
+ */
+
zf->length = Tcl_Seek(zf->chan, 0, SEEK_END);
if (zf->length == ERROR_LENGTH) {
ZIPFS_POSIX_ERROR(interp, "seek error");
@@ -1081,53 +1191,93 @@ ZipFSOpenArchive(
}
Tcl_Close(interp, zf->chan);
zf->chan = NULL;
- } else {
+ }
+ return ZipFSFindTOC(interp, needZip, zf);
+
+ /*
+ * Handle errors by closing the archive. This includes closing the channel
+ * handle for the archive file.
+ */
+
+ error:
+ ZipFSCloseArchive(interp, zf);
+ return TCL_ERROR;
+}
+
+/*
+ *-------------------------------------------------------------------------
+ *
+ * ZipMapArchive --
+ *
+ * Wrapper around the platform-specific parts of mmap() (and Windows's
+ * equivalent) because it's not part of the standard channel API.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+static int
+ZipMapArchive(
+ Tcl_Interp *interp, /* Interpreter for error reporting. */
+ ZipFile *zf, /* The archive descriptor structure. */
+ void *handle) /* The OS handle to the open archive. */
+{
#ifdef _WIN32
- int readSuccessful;
+ HANDLE hFile = (HANDLE) handle;
+ int readSuccessful;
+
+ /*
+ * Determine the file size.
+ */
+
# ifdef _WIN64
- i = GetFileSizeEx((HANDLE) handle, (PLARGE_INTEGER) &zf->length);
- readSuccessful = (i != 0);
+ readSuccessful = GetFileSizeEx(hFile, (PLARGE_INTEGER) &zf->length) != 0;
# else /* !_WIN64 */
- zf->length = GetFileSize((HANDLE) handle, 0);
- readSuccessful = (zf->length != (size_t) INVALID_FILE_SIZE);
+ zf->length = GetFileSize(hFile, 0);
+ readSuccessful = (zf->length != (size_t) INVALID_FILE_SIZE);
# endif /* _WIN64 */
- if (!readSuccessful || (zf->length < ZIP_CENTRAL_END_LEN)) {
- ZIPFS_POSIX_ERROR(interp, "invalid file size");
- goto error;
- }
- zf->mountHandle = CreateFileMappingW((HANDLE) handle, 0, PAGE_READONLY,
- 0, zf->length, 0);
- if (zf->mountHandle == INVALID_HANDLE_VALUE) {
- ZIPFS_POSIX_ERROR(interp, "file mapping failed");
- goto error;
- }
- zf->data = (unsigned char *)
- MapViewOfFile(zf->mountHandle, FILE_MAP_READ, 0, 0,
- zf->length);
- if (!zf->data) {
- ZIPFS_POSIX_ERROR(interp, "file mapping failed");
- goto error;
- }
+ if (!readSuccessful || (zf->length < ZIP_CENTRAL_END_LEN)) {
+ ZIPFS_POSIX_ERROR(interp, "invalid file size");
+ return TCL_ERROR;
+ }
+
+ /*
+ * Map the file.
+ */
+
+ zf->mountHandle = CreateFileMappingW(hFile, 0, PAGE_READONLY, 0,
+ zf->length, 0);
+ if (zf->mountHandle == INVALID_HANDLE_VALUE) {
+ ZIPFS_POSIX_ERROR(interp, "file mapping failed");
+ return TCL_ERROR;
+ }
+ zf->data = (unsigned char *)
+ MapViewOfFile(zf->mountHandle, FILE_MAP_READ, 0, 0, zf->length);
+ if (!zf->data) {
+ ZIPFS_POSIX_ERROR(interp, "file mapping failed");
+ return TCL_ERROR;
+ }
#else /* !_WIN32 */
- zf->length = lseek(PTR2INT(handle), 0, SEEK_END);
- if (zf->length == ERROR_LENGTH || zf->length < ZIP_CENTRAL_END_LEN) {
- ZIPFS_POSIX_ERROR(interp, "invalid file size");
- goto error;
- }
- lseek(PTR2INT(handle), 0, SEEK_SET);
- zf->data = (unsigned char *) mmap(0, zf->length, PROT_READ,
- MAP_FILE | MAP_PRIVATE, PTR2INT(handle), 0);
- if (zf->data == MAP_FAILED) {
- ZIPFS_POSIX_ERROR(interp, "file mapping failed");
- goto error;
- }
-#endif /* _WIN32 */
+ int fd = PTR2INT(handle);
+
+ /*
+ * Determine the file size.
+ */
+
+ zf->length = lseek(fd, 0, SEEK_END);
+ if (zf->length == ERROR_LENGTH || zf->length < ZIP_CENTRAL_END_LEN) {
+ ZIPFS_POSIX_ERROR(interp, "invalid file size");
+ return TCL_ERROR;
}
- return ZipFSFindTOC(interp, needZip, zf);
+ lseek(fd, 0, SEEK_SET);
- error:
- ZipFSCloseArchive(interp, zf);
- return TCL_ERROR;
+ zf->data = (unsigned char *)
+ mmap(0, zf->length, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0);
+ if (zf->data == MAP_FAILED) {
+ ZIPFS_POSIX_ERROR(interp, "file mapping failed");
+ return TCL_ERROR;
+ }
+#endif /* _WIN32 */
+ return TCL_OK;
}
/*
@@ -1150,7 +1300,7 @@ ZipFSOpenArchive(
static int
ZipFSCatalogFilesystem(
Tcl_Interp *interp, /* Current interpreter. NULLable. */
- ZipFile *zf0,
+ ZipFile *zf0, /* Temporary buffer hold archive descriptors */
const char *mountPoint, /* Mount point path. */
const char *passwd, /* Password for opening the ZIP, or NULL if
* the ZIP is unprotected. */
@@ -1181,6 +1331,20 @@ ZipFSCatalogFilesystem(
}
}
+ /*
+ * Validate the TOC data. If that's bad, things fall apart.
+ */
+
+ if (zf0->baseOffset < 0 || zf0->baseOffset >= zf0->length ||
+ zf0->passOffset < 0 || zf0->passOffset >= zf0->length ||
+ zf0->directoryOffset < 0 || zf0->directoryOffset >= zf0->length) {
+ if (interp) {
+ Tcl_SetObjResult(interp, Tcl_NewStringObj("bad zip data", -1));
+ Tcl_SetErrorCode(interp, "TCL", "ZIPFS", "BAD_ZIP", NULL);
+ }
+ return TCL_ERROR;
+ }
+
WriteLock();
/*
@@ -1207,18 +1371,18 @@ ZipFSCatalogFilesystem(
ZipFSCloseArchive(interp, zf0);
return TCL_ERROR;
}
- zf = (ZipFile *) attemptckalloc(sizeof(ZipFile) + strlen(mountPoint) + 1);
+ zf = AllocateZipFile(interp, strlen(mountPoint));
if (!zf) {
- if (interp) {
- Tcl_AppendResult(interp, "out of memory", (char *) NULL);
- Tcl_SetErrorCode(interp, "TCL", "MALLOC", NULL);
- }
Unlock();
ZipFSCloseArchive(interp, zf0);
return TCL_ERROR;
}
Unlock();
+ /*
+ * Convert to a real archive descriptor.
+ */
+
*zf = *zf0;
zf->mountPoint = (char *) Tcl_GetHashKey(&ZipFS.zipHash, hPtr);
Tcl_CreateExitHandler(ZipfsExitHandler, zf);
@@ -1559,7 +1723,8 @@ int
TclZipfs_Mount(
Tcl_Interp *interp, /* Current interpreter. NULLable. */
const char *mountPoint, /* Mount point path. */
- const char *zipname, /* Path to ZIP file to mount. */
+ const char *zipname, /* Path to ZIP file to mount; should be
+ * normalized. */
const char *passwd) /* Password for opening the ZIP, or NULL if
* the ZIP is unprotected. */
{
@@ -1606,12 +1771,8 @@ TclZipfs_Mount(
return TCL_ERROR;
}
}
- zf = (ZipFile *) attemptckalloc(sizeof(ZipFile) + strlen(mountPoint) + 1);
+ zf = AllocateZipFile(interp, strlen(mountPoint));
if (!zf) {
- if (interp) {
- Tcl_AppendResult(interp, "out of memory", (char *) NULL);
- Tcl_SetErrorCode(interp, "TCL", "MALLOC", NULL);
- }
return TCL_ERROR;
}
if (ZipFSOpenArchive(interp, zipname, 1, zf) != TCL_OK) {
@@ -1687,12 +1848,8 @@ TclZipfs_MountBuffer(
* Have both a mount point and data to mount there.
*/
- zf = (ZipFile *) attemptckalloc(sizeof(ZipFile) + strlen(mountPoint) + 1);
+ zf = AllocateZipFile(interp, strlen(mountPoint));
if (!zf) {
- if (interp) {
- Tcl_AppendResult(interp, "out of memory", (char *) NULL);
- Tcl_SetErrorCode(interp, "TCL", "MALLOC", NULL);
- }
return TCL_ERROR;
}
zf->isMemBuffer = 1;
@@ -1821,16 +1978,38 @@ ZipFSMountObjCmd(
int objc, /* Number of arguments. */
Tcl_Obj *const objv[]) /* Argument objects. */
{
+ const char *mountPoint = NULL, *zipFile = NULL, *password = NULL;
+ Tcl_Obj *zipFileObj = NULL;
+ int result;
if (objc > 4) {
Tcl_WrongNumArgs(interp, 1, objv,
"?mountpoint? ?zipfile? ?password?");
return TCL_ERROR;
}
+ if (objc > 1) {
+ mountPoint = Tcl_GetString(objv[1]);
+ }
+ if (objc > 2) {
+ zipFileObj = Tcl_FSGetNormalizedPath(interp, objv[2]);
+ if (!zipFileObj) {
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(
+ "could not normalize zip filename", -1));
+ Tcl_SetErrorCode(interp, "TCL", "OPERATION", "NORMALIZE", NULL);
+ return TCL_ERROR;
+ }
+ Tcl_IncrRefCount(zipFileObj);
+ zipFile = Tcl_GetString(zipFileObj);
+ }
+ if (objc > 3) {
+ password = Tcl_GetString(objv[3]);
+ }
- return TclZipfs_Mount(interp, (objc > 1) ? Tcl_GetString(objv[1]) : NULL,
- (objc > 2) ? Tcl_GetString(objv[2]) : NULL,
- (objc > 3) ? Tcl_GetString(objv[3]) : NULL);
+ result = TclZipfs_Mount(interp, mountPoint, zipFile, password);
+ if (zipFileObj != NULL) {
+ Tcl_DecrRefCount(zipFileObj);
+ }
+ return result;
}
/*
@@ -2043,7 +2222,7 @@ ZipAddFile(
const char *zpath;
int crc, flush, zpathlen;
size_t nbyte, nbytecompr, len, olen, align = 0;
- long long pos[3];
+ long long headerStartOffset, dataStartOffset, dataEndOffset;
int mtime = 0, isNew, compMeth;
unsigned long keys[3], keys0[3];
char obuf[4096];
@@ -2116,7 +2295,7 @@ ZipAddFile(
Tcl_Close(interp, in);
return TCL_ERROR;
}
- pos[0] = Tcl_Tell(out);
+ headerStartOffset = Tcl_Tell(out);
memset(buf, '\0', ZIP_LOCAL_HEADER_LEN);
memcpy(buf + ZIP_LOCAL_HEADER_LEN, zpath, zpathlen);
len = zpathlen + ZIP_LOCAL_HEADER_LEN;
@@ -2127,7 +2306,7 @@ ZipAddFile(
Tcl_Close(interp, in);
return TCL_ERROR;
}
- if ((len + pos[0]) & 3) {
+ if ((len + headerStartOffset) & 3) {
unsigned char abuf[8];
/*
@@ -2135,7 +2314,7 @@ ZipAddFile(
* similar to the zipalign tool from Android's SDK.
*/
- align = 4 + ((len + pos[0]) & 3);
+ align = 4 + ((len + headerStartOffset) & 3);
ZipWriteShort(abuf, 0xffff);
ZipWriteShort(abuf + 2, align - 4);
ZipWriteInt(abuf + 4, 0x03020100);
@@ -2193,7 +2372,7 @@ ZipAddFile(
nbytecompr += 12;
}
Tcl_Flush(out);
- pos[2] = Tcl_Tell(out);
+ dataStartOffset = Tcl_Tell(out);
compMeth = ZIP_COMPMETH_DEFLATED;
memset(&stream, 0, sizeof(z_stream));
stream.zalloc = Z_NULL;
@@ -2252,15 +2431,16 @@ ZipAddFile(
} while (flush != Z_FINISH);
deflateEnd(&stream);
Tcl_Flush(out);
- pos[1] = Tcl_Tell(out);
+ dataEndOffset = Tcl_Tell(out);
if (nbyte - nbytecompr <= 0) {
/*
* Compressed file larger than input, write it again uncompressed.
*/
+
if (Tcl_Seek(in, 0, SEEK_SET) != 0) {
goto seekErr;
}
- if (Tcl_Seek(out, pos[2], SEEK_SET) != pos[2]) {
+ if (Tcl_Seek(out, dataStartOffset, SEEK_SET) != dataStartOffset) {
seekErr:
Tcl_Close(interp, in);
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
@@ -2297,8 +2477,8 @@ ZipAddFile(
}
compMeth = ZIP_COMPMETH_STORED;
Tcl_Flush(out);
- pos[1] = Tcl_Tell(out);
- Tcl_TruncateChannel(out, pos[1]);
+ dataEndOffset = Tcl_Tell(out);
+ Tcl_TruncateChannel(out, dataEndOffset);
}
Tcl_Close(interp, in);
@@ -2318,7 +2498,7 @@ ZipAddFile(
z->zipFilePtr = NULL;
z->isDirectory = 0;
z->isEncrypted = (passwd ? 1 : 0);
- z->offset = pos[0];
+ z->offset = headerStartOffset;
z->crc32 = crc;
z->timestamp = mtime;
z->numBytes = nbyte;
@@ -2331,18 +2511,9 @@ ZipAddFile(
/*
* Write final local header information.
*/
- ZipWriteInt(buf + ZIP_LOCAL_SIG_OFFS, ZIP_LOCAL_HEADER_SIG);
- ZipWriteShort(buf + ZIP_LOCAL_VERSION_OFFS, ZIP_MIN_VERSION);
- ZipWriteShort(buf + ZIP_LOCAL_FLAGS_OFFS, z->isEncrypted);
- ZipWriteShort(buf + ZIP_LOCAL_COMPMETH_OFFS, z->compressMethod);
- ZipWriteShort(buf + ZIP_LOCAL_MTIME_OFFS, ToDosTime(z->timestamp));
- ZipWriteShort(buf + ZIP_LOCAL_MDATE_OFFS, ToDosDate(z->timestamp));
- ZipWriteInt(buf + ZIP_LOCAL_CRC32_OFFS, z->crc32);
- ZipWriteInt(buf + ZIP_LOCAL_COMPLEN_OFFS, z->numCompressedBytes);
- ZipWriteInt(buf + ZIP_LOCAL_UNCOMPLEN_OFFS, z->numBytes);
- ZipWriteShort(buf + ZIP_LOCAL_PATHLEN_OFFS, zpathlen);
- ZipWriteShort(buf + ZIP_LOCAL_EXTRALEN_OFFS, align);
- if (Tcl_Seek(out, pos[0], SEEK_SET) != pos[0]) {
+
+ SerializeLocalEntryHeader(buf, z, zpathlen, align);
+ if (Tcl_Seek(out, headerStartOffset, SEEK_SET) != headerStartOffset) {
Tcl_DeleteHashEntry(hPtr);
ckfree(z);
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
@@ -2357,7 +2528,7 @@ ZipAddFile(
return TCL_ERROR;
}
Tcl_Flush(out);
- if (Tcl_Seek(out, pos[1], SEEK_SET) != pos[1]) {
+ if (Tcl_Seek(out, dataEndOffset, SEEK_SET) != dataEndOffset) {
Tcl_DeleteHashEntry(hPtr);
ckfree(z);
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
@@ -2390,15 +2561,25 @@ static int
ZipFSMkZipOrImgObjCmd(
Tcl_Interp *interp, /* Current interpreter. */
int isImg,
- int isList,
- int objc, /* Number of arguments. */
- Tcl_Obj *const objv[]) /* Argument objects. */
+ Tcl_Obj *targetFile,
+ Tcl_Obj *dirRoot,
+ Tcl_Obj *mappingList,
+ Tcl_Obj *originFile,
+ Tcl_Obj *stripPrefix,
+ Tcl_Obj *passwordObj)
{
Tcl_Channel out;
- int pwlen = 0, count, ret = TCL_ERROR, lobjc;
- size_t len, slen = 0, i = 0;
- long long pos[3];
- Tcl_Obj **lobjv, *list = NULL;
+ int pwlen = 0, slen = 0, count, ret = TCL_ERROR, lobjc;
+ size_t len, i = 0;
+ long long dataStartOffset; /* The overall file offset of the start of the
+ * data section of the file. */
+ long long directoryStartOffset;
+ /* The overall file offset of the start of the
+ * central directory. */
+ long long suffixStartOffset;/* The overall file offset of the start of the
+ * suffix of the central directory (i.e.,
+ * where this data will be written). */
+ Tcl_Obj **lobjv, *list = mappingList;
ZipEntry *z;
Tcl_HashEntry *hPtr;
Tcl_HashSearch search;
@@ -2410,9 +2591,8 @@ ZipFSMkZipOrImgObjCmd(
*/
passBuf[0] = 0;
- if (objc > (isList ? 3 : 4)) {
- pw = Tcl_GetString(objv[isList ? 3 : 4]);
- pwlen = strlen(pw);
+ if (passwordObj != NULL) {
+ pw = Tcl_GetStringFromObj(passwordObj, &pwlen);
if ((pwlen > 255) || strchr(pw, 0xff)) {
Tcl_SetObjResult(interp,
Tcl_NewStringObj("illegal password", -1));
@@ -2420,10 +2600,7 @@ ZipFSMkZipOrImgObjCmd(
return TCL_ERROR;
}
}
- if (isList) {
- list = objv[2];
- Tcl_IncrRefCount(list);
- } else {
+ if (dirRoot != NULL) {
Tcl_Obj *cmd[2];
int result;
@@ -2432,7 +2609,7 @@ ZipFSMkZipOrImgObjCmd(
*/
cmd[0] = Tcl_NewStringObj("::tcl::zipfs::find", -1);
- cmd[1] = objv[2];
+ cmd[1] = dirRoot;
Tcl_IncrRefCount(cmd[0]);
result = Tcl_EvalObjv(interp, 2, cmd, 0);
Tcl_DecrRefCount(cmd[0]);
@@ -2440,13 +2617,13 @@ ZipFSMkZipOrImgObjCmd(
return TCL_ERROR;
}
list = Tcl_GetObjResult(interp);
- Tcl_IncrRefCount(list);
}
+ Tcl_IncrRefCount(list);
if (Tcl_ListObjGetElements(interp, list, &lobjc, &lobjv) != TCL_OK) {
Tcl_DecrRefCount(list);
return TCL_ERROR;
}
- if (isList && (lobjc % 2)) {
+ if (mappingList && (lobjc % 2)) {
Tcl_DecrRefCount(list);
Tcl_SetObjResult(interp,
Tcl_NewStringObj("need even number of elements", -1));
@@ -2459,7 +2636,7 @@ ZipFSMkZipOrImgObjCmd(
Tcl_SetErrorCode(interp, "TCL", "ZIPFS", "EMPTY", NULL);
return TCL_ERROR;
}
- out = Tcl_OpenFileChannel(interp, Tcl_GetString(objv[1]), "wb", 0755);
+ out = Tcl_OpenFileChannel(interp, Tcl_GetString(targetFile), "wb", 0755);
if (out == NULL) {
Tcl_DecrRefCount(list);
return TCL_ERROR;
@@ -2480,13 +2657,9 @@ ZipFSMkZipOrImgObjCmd(
int isMounted = 0;
const char *imgName;
- if (isList) {
- imgName = (objc > 4) ? Tcl_GetString(objv[4]) :
- Tcl_GetNameOfExecutable();
- } else {
- imgName = (objc > 5) ? Tcl_GetString(objv[5]) :
- Tcl_GetNameOfExecutable();
- }
+ // TODO: normalize the origin file name
+ imgName = (originFile != NULL) ? Tcl_GetString(originFile) :
+ Tcl_GetNameOfExecutable();
if (pwlen) {
i = 0;
for (len = pwlen; len-- > 0;) {
@@ -2552,55 +2725,17 @@ ZipFSMkZipOrImgObjCmd(
Unlock();
}
} else {
- size_t k;
- int m, n;
- Tcl_Channel in;
- const char *errMsg = "seek error";
-
/*
* Fall back to read it as plain file which hopefully is a static
* tclsh or wish binary with proper zipfs infrastructure built in.
*/
- Tcl_ResetResult(interp);
- in = Tcl_OpenFileChannel(interp, imgName, "rb", 0644);
- if (!in) {
+ if (CopyImageFile(interp, imgName, out) != TCL_OK) {
memset(passBuf, 0, sizeof(passBuf));
Tcl_DecrRefCount(list);
Tcl_Close(interp, out);
return TCL_ERROR;
}
- i = Tcl_Seek(in, 0, SEEK_END);
- if (i == ERROR_LENGTH) {
- cperr:
- memset(passBuf, 0, sizeof(passBuf));
- Tcl_DecrRefCount(list);
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "%s: %s", errMsg, Tcl_PosixError(interp)));
- Tcl_Close(interp, out);
- Tcl_Close(interp, in);
- return TCL_ERROR;
- }
- Tcl_Seek(in, 0, SEEK_SET);
- for (k = 0; k < i; k += m) {
- m = i - k;
- if (m > (int) sizeof(buf)) {
- m = (int) sizeof(buf);
- }
- n = Tcl_Read(in, buf, m);
- if (n == -1) {
- errMsg = "read error";
- goto cperr;
- } else if (n == 0) {
- break;
- }
- m = Tcl_Write(out, buf, n);
- if (m != n) {
- errMsg = "write error";
- goto cperr;
- }
- }
- Tcl_Close(interp, in);
}
/*
@@ -2627,22 +2762,22 @@ ZipFSMkZipOrImgObjCmd(
*/
Tcl_InitHashTable(&fileHash, TCL_STRING_KEYS);
- pos[0] = Tcl_Tell(out);
- if (!isList && (objc > 3)) {
- strip = Tcl_GetString(objv[3]);
- slen = strlen(strip);
+ dataStartOffset = Tcl_Tell(out);
+ if (mappingList == NULL && stripPrefix != NULL) {
+ strip = Tcl_GetStringFromObj(stripPrefix, &slen);
}
- for (i = 0; i < (size_t) lobjc; i += (isList ? 2 : 1)) {
+ for (i = 0; i < (size_t) lobjc; i += (mappingList ? 2 : 1)) {
const char *path, *name;
path = Tcl_GetString(lobjv[i]);
- if (isList) {
+ if (mappingList) {
name = Tcl_GetString(lobjv[i + 1]);
} else {
name = path;
if (slen > 0) {
len = strlen(name);
- if ((len <= slen) || (strncmp(strip, name, slen) != 0)) {
+ if ((len <= (size_t) slen) ||
+ (strncmp(strip, name, slen) != 0)) {
continue;
}
name += slen;
@@ -2664,19 +2799,20 @@ ZipFSMkZipOrImgObjCmd(
* Construct the contents of the ZIP central directory.
*/
- pos[1] = Tcl_Tell(out);
+ directoryStartOffset = Tcl_Tell(out);
count = 0;
- for (i = 0; i < (size_t) lobjc; i += (isList ? 2 : 1)) {
+ for (i = 0; i < (size_t) lobjc; i += (mappingList ? 2 : 1)) {
const char *path, *name;
path = Tcl_GetString(lobjv[i]);
- if (isList) {
+ if (mappingList) {
name = Tcl_GetString(lobjv[i + 1]);
} else {
name = path;
if (slen > 0) {
len = strlen(name);
- if ((len <= slen) || (strncmp(strip, name, slen) != 0)) {
+ if ((len <= (size_t) slen) ||
+ (strncmp(strip, name, slen) != 0)) {
continue;
}
name += slen;
@@ -2694,25 +2830,9 @@ ZipFSMkZipOrImgObjCmd(
}
z = (ZipEntry *) Tcl_GetHashValue(hPtr);
len = strlen(z->name);
- ZipWriteInt(buf + ZIP_CENTRAL_SIG_OFFS, ZIP_CENTRAL_HEADER_SIG);
- ZipWriteShort(buf + ZIP_CENTRAL_VERSIONMADE_OFFS, ZIP_MIN_VERSION);
- ZipWriteShort(buf + ZIP_CENTRAL_VERSION_OFFS, ZIP_MIN_VERSION);
- ZipWriteShort(buf + ZIP_CENTRAL_FLAGS_OFFS, z->isEncrypted);
- ZipWriteShort(buf + ZIP_CENTRAL_COMPMETH_OFFS, z->compressMethod);
- ZipWriteShort(buf + ZIP_CENTRAL_MTIME_OFFS, ToDosTime(z->timestamp));
- ZipWriteShort(buf + ZIP_CENTRAL_MDATE_OFFS, ToDosDate(z->timestamp));
- ZipWriteInt(buf + ZIP_CENTRAL_CRC32_OFFS, z->crc32);
- ZipWriteInt(buf + ZIP_CENTRAL_COMPLEN_OFFS, z->numCompressedBytes);
- ZipWriteInt(buf + ZIP_CENTRAL_UNCOMPLEN_OFFS, z->numBytes);
- ZipWriteShort(buf + ZIP_CENTRAL_PATHLEN_OFFS, len);
- ZipWriteShort(buf + ZIP_CENTRAL_EXTRALEN_OFFS, 0);
- ZipWriteShort(buf + ZIP_CENTRAL_FCOMMENTLEN_OFFS, 0);
- ZipWriteShort(buf + ZIP_CENTRAL_DISKFILE_OFFS, 0);
- ZipWriteShort(buf + ZIP_CENTRAL_IATTR_OFFS, 0);
- ZipWriteInt(buf + ZIP_CENTRAL_EATTR_OFFS, 0);
- ZipWriteInt(buf + ZIP_CENTRAL_LOCALHDR_OFFS, z->offset - pos[0]);
- if ((Tcl_Write(out, buf,
- ZIP_CENTRAL_HEADER_LEN) != ZIP_CENTRAL_HEADER_LEN)
+ SerializeCentralDirectoryEntry(buf, z, len, dataStartOffset);
+ if ((Tcl_Write(out, buf, ZIP_CENTRAL_HEADER_LEN)
+ != ZIP_CENTRAL_HEADER_LEN)
|| ((size_t) Tcl_Write(out, z->name, len) != len)) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
"write error: %s", Tcl_PosixError(interp)));
@@ -2726,15 +2846,9 @@ ZipFSMkZipOrImgObjCmd(
*/
Tcl_Flush(out);
- pos[2] = Tcl_Tell(out);
- ZipWriteInt(buf + ZIP_CENTRAL_END_SIG_OFFS, ZIP_CENTRAL_END_SIG);
- ZipWriteShort(buf + ZIP_CENTRAL_DISKNO_OFFS, 0);
- ZipWriteShort(buf + ZIP_CENTRAL_DISKDIR_OFFS, 0);
- ZipWriteShort(buf + ZIP_CENTRAL_ENTS_OFFS, count);
- ZipWriteShort(buf + ZIP_CENTRAL_TOTALENTS_OFFS, count);
- ZipWriteInt(buf + ZIP_CENTRAL_DIRSIZE_OFFS, pos[2] - pos[1]);
- ZipWriteInt(buf + ZIP_CENTRAL_DIRSTART_OFFS, pos[1] - pos[0]);
- ZipWriteShort(buf + ZIP_CENTRAL_COMMENTLEN_OFFS, 0);
+ suffixStartOffset = Tcl_Tell(out);
+ SerializeCentralDirectorySuffix(buf, count, dataStartOffset,
+ directoryStartOffset, suffixStartOffset);
if (Tcl_Write(out, buf, ZIP_CENTRAL_END_LEN) != ZIP_CENTRAL_END_LEN) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
"write error: %s", Tcl_PosixError(interp)));
@@ -2761,6 +2875,177 @@ ZipFSMkZipOrImgObjCmd(
}
/*
+ * ---------------------------------------------------------------------
+ *
+ * CopyImageFile --
+ *
+ * A simple file copy function that is used (by ZipFSMkZipOrImgObjCmd)
+ * for anything that is not an image with a ZIP appended.
+ *
+ * Returns:
+ * A Tcl result code.
+ *
+ * Side effects:
+ * Writes to an output channel.
+ *
+ * ---------------------------------------------------------------------
+ */
+
+static int
+CopyImageFile(
+ Tcl_Interp *interp, /* For error reporting. */
+ const char *imgName, /* Where to copy from. */
+ Tcl_Channel out) /* Where to copy to; already open for writing
+ * binary data. */
+{
+ size_t i, k;
+ int m, n;
+ Tcl_Channel in;
+ char buf[4096];
+ const char *errMsg;
+
+ Tcl_ResetResult(interp);
+ in = Tcl_OpenFileChannel(interp, imgName, "rb", 0644);
+ if (!in) {
+ return TCL_ERROR;
+ }
+
+ /*
+ * Get the length of the file (and exclude non-files).
+ */
+
+ i = Tcl_Seek(in, 0, SEEK_END);
+ if (i == ERROR_LENGTH) {
+ errMsg = "seek error";
+ goto copyError;
+ }
+ Tcl_Seek(in, 0, SEEK_SET);
+
+ /*
+ * Copy the whole file, 8 blocks at a time (reasonably efficient). Note
+ * that this totally ignores things like Windows's Alternate File Streams.
+ */
+
+ for (k = 0; k < i; k += m) {
+ m = i - k;
+ if (m > (int) sizeof(buf)) {
+ m = (int) sizeof(buf);
+ }
+ n = Tcl_Read(in, buf, m);
+ if (n == -1) {
+ errMsg = "read error";
+ goto copyError;
+ } else if (n == 0) {
+ break;
+ }
+ m = Tcl_Write(out, buf, n);
+ if (m != n) {
+ errMsg = "write error";
+ goto copyError;
+ }
+ }
+ Tcl_Close(interp, in);
+ return TCL_OK;
+
+ copyError:
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "%s: %s", errMsg, Tcl_PosixError(interp)));
+ Tcl_Close(interp, in);
+ return TCL_ERROR;
+}
+
+/*
+ * ---------------------------------------------------------------------
+ *
+ * SerializeLocalEntryHeader, SerializeCentralDirectoryEntry,
+ * SerializeCentralDirectorySuffix --
+ *
+ * Create serialized forms of the structures that make up the ZIP
+ * metadata. Note that the both the local entry and the central directory
+ * entry need to have the name of the entry written directly afterwards.
+ *
+ * We could write these as structs except we need to guarantee that we
+ * are writing these out as little-endian values.
+ *
+ * Side effects:
+ * Both update their buffer arguments, but otherwise change nothing.
+ *
+ * ---------------------------------------------------------------------
+ */
+
+static void
+SerializeLocalEntryHeader(
+ char *buf, /* Where to serialize to */
+ ZipEntry *z, /* The description of what to serialize. */
+ int nameLength, /* The length of the name. */
+ int align) /* The number of alignment bytes. */
+{
+ ZipWriteInt(buf + ZIP_LOCAL_SIG_OFFS, ZIP_LOCAL_HEADER_SIG);
+ ZipWriteShort(buf + ZIP_LOCAL_VERSION_OFFS, ZIP_MIN_VERSION);
+ ZipWriteShort(buf + ZIP_LOCAL_FLAGS_OFFS, z->isEncrypted);
+ ZipWriteShort(buf + ZIP_LOCAL_COMPMETH_OFFS, z->compressMethod);
+ ZipWriteShort(buf + ZIP_LOCAL_MTIME_OFFS, ToDosTime(z->timestamp));
+ ZipWriteShort(buf + ZIP_LOCAL_MDATE_OFFS, ToDosDate(z->timestamp));
+ ZipWriteInt(buf + ZIP_LOCAL_CRC32_OFFS, z->crc32);
+ ZipWriteInt(buf + ZIP_LOCAL_COMPLEN_OFFS, z->numCompressedBytes);
+ ZipWriteInt(buf + ZIP_LOCAL_UNCOMPLEN_OFFS, z->numBytes);
+ ZipWriteShort(buf + ZIP_LOCAL_PATHLEN_OFFS, nameLength);
+ ZipWriteShort(buf + ZIP_LOCAL_EXTRALEN_OFFS, align);
+}
+
+static void
+SerializeCentralDirectoryEntry(
+ char *buf, /* Where to serialize to */
+ ZipEntry *z, /* The description of what to serialize. */
+ size_t nameLength, /* The length of the name. */
+ long long dataStartOffset) /* The overall file offset of the start of the
+ * data section of the file. */
+{
+ ZipWriteInt(buf + ZIP_CENTRAL_SIG_OFFS, ZIP_CENTRAL_HEADER_SIG);
+ ZipWriteShort(buf + ZIP_CENTRAL_VERSIONMADE_OFFS, ZIP_MIN_VERSION);
+ ZipWriteShort(buf + ZIP_CENTRAL_VERSION_OFFS, ZIP_MIN_VERSION);
+ ZipWriteShort(buf + ZIP_CENTRAL_FLAGS_OFFS, z->isEncrypted);
+ ZipWriteShort(buf + ZIP_CENTRAL_COMPMETH_OFFS, z->compressMethod);
+ ZipWriteShort(buf + ZIP_CENTRAL_MTIME_OFFS, ToDosTime(z->timestamp));
+ ZipWriteShort(buf + ZIP_CENTRAL_MDATE_OFFS, ToDosDate(z->timestamp));
+ ZipWriteInt(buf + ZIP_CENTRAL_CRC32_OFFS, z->crc32);
+ ZipWriteInt(buf + ZIP_CENTRAL_COMPLEN_OFFS, z->numCompressedBytes);
+ ZipWriteInt(buf + ZIP_CENTRAL_UNCOMPLEN_OFFS, z->numBytes);
+ ZipWriteShort(buf + ZIP_CENTRAL_PATHLEN_OFFS, nameLength);
+ ZipWriteShort(buf + ZIP_CENTRAL_EXTRALEN_OFFS, 0);
+ ZipWriteShort(buf + ZIP_CENTRAL_FCOMMENTLEN_OFFS, 0);
+ ZipWriteShort(buf + ZIP_CENTRAL_DISKFILE_OFFS, 0);
+ ZipWriteShort(buf + ZIP_CENTRAL_IATTR_OFFS, 0);
+ ZipWriteInt(buf + ZIP_CENTRAL_EATTR_OFFS, 0);
+ ZipWriteInt(buf + ZIP_CENTRAL_LOCALHDR_OFFS, z->offset - dataStartOffset);
+}
+
+static void
+SerializeCentralDirectorySuffix(
+ char *buf, /* Where to serialize to */
+ int entryCount, /* The number of entries in the directory */
+ long long dataStartOffset, /* The overall file offset of the start of the
+ * data section of the file. */
+ long long directoryStartOffset,
+ /* The overall file offset of the start of the
+ * central directory. */
+ long long suffixStartOffset)/* The overall file offset of the start of the
+ * suffix of the central directory (i.e.,
+ * where this data will be written). */
+{
+ ZipWriteInt(buf + ZIP_CENTRAL_END_SIG_OFFS, ZIP_CENTRAL_END_SIG);
+ ZipWriteShort(buf + ZIP_CENTRAL_DISKNO_OFFS, 0);
+ ZipWriteShort(buf + ZIP_CENTRAL_DISKDIR_OFFS, 0);
+ ZipWriteShort(buf + ZIP_CENTRAL_ENTS_OFFS, entryCount);
+ ZipWriteShort(buf + ZIP_CENTRAL_TOTALENTS_OFFS, entryCount);
+ ZipWriteInt(buf + ZIP_CENTRAL_DIRSIZE_OFFS,
+ suffixStartOffset - directoryStartOffset);
+ ZipWriteInt(buf + ZIP_CENTRAL_DIRSTART_OFFS,
+ directoryStartOffset - dataStartOffset);
+ ZipWriteShort(buf + ZIP_CENTRAL_COMMENTLEN_OFFS, 0);
+}
+
+/*
*-------------------------------------------------------------------------
*
* ZipFSMkZipObjCmd, ZipFSLMkZipObjCmd --
@@ -2784,6 +3069,8 @@ ZipFSMkZipObjCmd(
int objc, /* Number of arguments. */
Tcl_Obj *const objv[]) /* Argument objects. */
{
+ Tcl_Obj *stripPrefix, *password;
+
if (objc < 3 || objc > 5) {
Tcl_WrongNumArgs(interp, 1, objv, "outfile indir ?strip? ?password?");
return TCL_ERROR;
@@ -2794,7 +3081,11 @@ ZipFSMkZipObjCmd(
Tcl_SetErrorCode(interp, "TCL", "ZIPFS", "SAFE_INTERP", NULL);
return TCL_ERROR;
}
- return ZipFSMkZipOrImgObjCmd(interp, 0, 0, objc, objv);
+
+ stripPrefix = (objc > 3 ? objv[3] : NULL);
+ password = (objc > 4 ? objv[4] : NULL);
+ return ZipFSMkZipOrImgObjCmd(interp, 0, objv[1], objv[2], NULL, NULL,
+ stripPrefix, password);
}
static int
@@ -2804,6 +3095,8 @@ ZipFSLMkZipObjCmd(
int objc, /* Number of arguments. */
Tcl_Obj *const objv[]) /* Argument objects. */
{
+ Tcl_Obj *password;
+
if (objc < 3 || objc > 4) {
Tcl_WrongNumArgs(interp, 1, objv, "outfile inlist ?password?");
return TCL_ERROR;
@@ -2814,7 +3107,10 @@ ZipFSLMkZipObjCmd(
Tcl_SetErrorCode(interp, "TCL", "ZIPFS", "SAFE_INTERP", NULL);
return TCL_ERROR;
}
- return ZipFSMkZipOrImgObjCmd(interp, 0, 1, objc, objv);
+
+ password = (objc > 3 ? objv[3] : NULL);
+ return ZipFSMkZipOrImgObjCmd(interp, 0, objv[1], NULL, objv[2], NULL,
+ NULL, password);
}
/*
@@ -2841,6 +3137,8 @@ ZipFSMkImgObjCmd(
int objc, /* Number of arguments. */
Tcl_Obj *const objv[]) /* Argument objects. */
{
+ Tcl_Obj *originFile, *stripPrefix, *password;
+
if (objc < 3 || objc > 6) {
Tcl_WrongNumArgs(interp, 1, objv,
"outfile indir ?strip? ?password? ?infile?");
@@ -2852,7 +3150,12 @@ ZipFSMkImgObjCmd(
Tcl_SetErrorCode(interp, "TCL", "ZIPFS", "SAFE_INTERP", NULL);
return TCL_ERROR;
}
- return ZipFSMkZipOrImgObjCmd(interp, 1, 0, objc, objv);
+
+ originFile = (objc > 5 ? objv[5] : NULL);
+ stripPrefix = (objc > 3 ? objv[3] : NULL);
+ password = (objc > 4 ? objv[4] : NULL);
+ return ZipFSMkZipOrImgObjCmd(interp, 1, objv[1], objv[2], NULL,
+ originFile, stripPrefix, password);
}
static int
@@ -2862,6 +3165,8 @@ ZipFSLMkImgObjCmd(
int objc, /* Number of arguments. */
Tcl_Obj *const objv[]) /* Argument objects. */
{
+ Tcl_Obj *originFile, *password;
+
if (objc < 3 || objc > 5) {
Tcl_WrongNumArgs(interp, 1, objv, "outfile inlist ?password infile?");
return TCL_ERROR;
@@ -2872,7 +3177,11 @@ ZipFSLMkImgObjCmd(
Tcl_SetErrorCode(interp, "TCL", "ZIPFS", "SAFE_INTERP", NULL);
return TCL_ERROR;
}
- return ZipFSMkZipOrImgObjCmd(interp, 1, 1, objc, objv);
+
+ originFile = (objc > 4 ? objv[4] : NULL);
+ password = (objc > 3 ? objv[3] : NULL);
+ return ZipFSMkZipOrImgObjCmd(interp, 1, objv[1], NULL, objv[2],
+ originFile, NULL, password);
}
/*
diff --git a/tests/zipfs.test b/tests/zipfs.test
index 3c11895..36fc6d6 100644
--- a/tests/zipfs.test
+++ b/tests/zipfs.test
@@ -380,6 +380,10 @@ test zipfs-4.5 {zipfs lmkimg: making image from mounted} -constraints zipfs -set
removeFile $targetImage
removeFile $addFile
} -result {//zipfs://ziptest/test/add.tcl //zipfs://ziptest/test/ok.tcl}
+
+test zipfs-5.1 {zipfs mount_data: short data} -body {
+ zipfs mount_data gorp {}
+} -constraints zipfs -returnCodes error -result {bad zip data}
::tcltest::cleanupTests
return
diff --git a/unix/Makefile.in b/unix/Makefile.in
index f885f5a..30e2104 100644
--- a/unix/Makefile.in
+++ b/unix/Makefile.in
@@ -945,6 +945,9 @@ shell: ${TCL_EXE}
gdb: ${TCL_EXE}
$(SHELL_ENV) $(GDB) ./${TCL_EXE}
+lldb: ${TCL_EXE}
+ $(SHELL_ENV) $(LLDB) ./${TCL_EXE}
+
valgrind: ${TCL_EXE} ${TCLTEST_EXE}
$(SHELL_ENV) $(VALGRIND) $(VALGRINDARGS) ./${TCLTEST_EXE} \
$(TOP_DIR)/tests/all.tcl -singleproc 1 -constraints valgrind \