From 38c616078f1d928300a11557dcafad8c7893fc50 Mon Sep 17 00:00:00 2001 From: meep-eep Date: Tue, 29 Jul 2003 18:49:35 +0000 Subject: [PATCH] Better handing of directory permissions in .zip files. This is a general uio improvement that is not actually used in uqm. git-svn-id: svn://svn.code.sf.net/p/sc2/code/trunk@1091 8092fc87-c524-0410-9efc-e669fe64eaf9 --- sc2/src/sc2code/libs/uio/debug.c | 20 +- sc2/src/sc2code/libs/uio/doc/basics | 2 +- sc2/src/sc2code/libs/uio/doc/todo | 1 + sc2/src/sc2code/libs/uio/gphys.h | 2 +- sc2/src/sc2code/libs/uio/io.c | 82 +++++- sc2/src/sc2code/libs/uio/ioaux.c | 6 +- sc2/src/sc2code/libs/uio/memdebug.c | 1 + sc2/src/sc2code/libs/uio/memdebug.h | 1 + sc2/src/sc2code/libs/uio/uiostream.h | 2 +- sc2/src/sc2code/libs/uio/zip/zip.c | 382 ++++++++++++++++++++++----- sc2/src/sc2code/libs/uio/zip/zip.h | 7 +- 11 files changed, 407 insertions(+), 99 deletions(-) diff --git a/sc2/src/sc2code/libs/uio/debug.c b/sc2/src/sc2code/libs/uio/debug.c index 38663be3b..e416968fd 100644 --- a/sc2/src/sc2code/libs/uio/debug.c +++ b/sc2/src/sc2code/libs/uio/debug.c @@ -200,7 +200,7 @@ initRepository(void) { #endif #if 1 mountHandles[7] = debugMountOne(repository, "/foo/", - uio_FSTYPE_ZIP, rootDir, "/foo.zip", "/", autoMount, + uio_FSTYPE_ZIP, rootDir, "/foo2.zip", "/", autoMount, uio_MOUNT_TOP | uio_MOUNT_RDONLY, NULL); #endif uio_closeDir(rootDir); @@ -681,29 +681,18 @@ debugCmdRmDir(DebugContext *debugContext, int argc, char *argv[]) { static int debugCmdStat(DebugContext *debugContext, int argc, char *argv[]) { struct stat statBuf; - uio_Handle *handle; if (argc != 2) { fprintf(debugContext->err, "Invalid number of arguments.\n"); return 1; } - handle = uio_open(debugContext->cwd, argv[1], O_RDONLY -#ifdef WIN32 - | O_BINARY -#endif - , 0); - if (handle == NULL) { - fprintf(debugContext->err, "Could not open file: %s\n", - strerror(errno)); - return 1; - } - - if (uio_fstat(handle, &statBuf) == -1) { + if (uio_stat(debugContext->cwd, argv[1], &statBuf) == -1) { // errno is set int saveErrno; saveErrno = errno; - uio_close(handle); + fprintf(debugContext->err, "Could not stat file: %s\n", + strerror(errno)); errno = saveErrno; return 1; } @@ -725,7 +714,6 @@ debugCmdStat(DebugContext *debugContext, int argc, char *argv[]) { fprintf(debugContext->out, "last status change: %s", ctime(&statBuf.st_ctime)); - uio_close(handle); return 0; } diff --git a/sc2/src/sc2code/libs/uio/doc/basics b/sc2/src/sc2code/libs/uio/doc/basics index 9ea3af2f4..69f06a18f 100644 --- a/sc2/src/sc2code/libs/uio/doc/basics +++ b/sc2/src/sc2code/libs/uio/doc/basics @@ -15,7 +15,7 @@ earlier mounted file system, files in the later mounted file system hide files in an earlier mounted file system. Files present in a filesystem mounted earlier that don't exist in a filesystem mounted later will remain visible. -Accessing compressed files inside compressde files is possible, though slow. +Accessing compressed files inside compressed files is possible, though slow. -= Nomenclature =- diff --git a/sc2/src/sc2code/libs/uio/doc/todo b/sc2/src/sc2code/libs/uio/doc/todo index 740837386..d3c3de46e 100644 --- a/sc2/src/sc2code/libs/uio/doc/todo +++ b/sc2/src/sc2code/libs/uio/doc/todo @@ -29,6 +29,7 @@ Documentation: fields. - No need to store MountHandles. They will be automatically freed when the repository is closed. +- You can use mount stuff from other repositories. Bugs: - uio_Stream file might get unaligned (not sure). diff --git a/sc2/src/sc2code/libs/uio/gphys.h b/sc2/src/sc2code/libs/uio/gphys.h index bafa26513..e256b97bf 100644 --- a/sc2/src/sc2code/libs/uio/gphys.h +++ b/sc2/src/sc2code/libs/uio/gphys.h @@ -172,9 +172,9 @@ struct uio_GPFile { uio_GPDirEntry_COMMON # define uio_GPFile_NOCACHE uio_GPDirEntry_NOCACHE /* Info on this file will not be cached. */ - uio_PRoot *pRoot; uio_GPFileExtra extra; /* extra internal data for some filesystem types */ + uio_PRoot *pRoot; }; diff --git a/sc2/src/sc2code/libs/uio/io.c b/sc2/src/sc2code/libs/uio/io.c index ed57f2704..f895fe436 100644 --- a/sc2/src/sc2code/libs/uio/io.c +++ b/sc2/src/sc2code/libs/uio/io.c @@ -39,6 +39,10 @@ # include "memdebug.h" #endif +static int uio_statDir(uio_DirHandle *dirHandle, const char *path, + struct stat *statBuf); +static int uio_statOneDir(uio_PDirHandle *pDirHandle, struct stat *statBuf); + static void uio_PDirHandles_delete(uio_PDirHandle *pDirHandles[], int numPDirHandles); @@ -341,35 +345,89 @@ uio_stat(uio_DirHandle *dir, const char *path, struct stat *statBuf) { char *name; int result; - // TODO: If it's a dir, check all physical dirs, and merge the - // direntry times. if (uio_getPhysicalAccess(dir, path, O_RDONLY, &readMountInfo, &pReadDir, NULL, NULL, &name) == -1) { - if (errno == EISDIR) { - // Need to merge the dirs. - memset(statBuf, '\0', sizeof (struct stat)); - statBuf->st_mode = S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR | - S_IRGRP | S_IWGRP | S_IXOTH | - S_IROTH | S_IWOTH | S_IXOTH; - statBuf->st_uid = 0; - statBuf->st_gid = 0; - return 0; + if (uio_statDir(dir, path, statBuf) == -1) { + // errno is set + return -1; } - return -1; + return 0; } if (pReadDir->pRoot->handler->stat == NULL) { uio_PDirHandle_unref(pReadDir); + uio_free(name); errno = ENOSYS; return -1; } result = pReadDir->pRoot->handler->stat(pReadDir, name, statBuf); uio_PDirHandle_unref(pReadDir); + uio_free(name); return result; } +// auxiliary function to uio_stat +static int +uio_statDir(uio_DirHandle *dirHandle, const char *path, + struct stat *statBuf) { + int numPDirHandles; + uio_PDirHandle **pDirHandles; + + if (uio_getPathPhysicalDirs(dirHandle, path, strlen(path), + &pDirHandles, &numPDirHandles, NULL) == -1) { + // errno is set + return -1; + } + + if (numPDirHandles == 0) { + errno = ENOENT; + return -1; + } + + if (uio_statOneDir(pDirHandles[0], statBuf) == -1) { + int saveErrno = errno; + uio_PDirHandles_delete(pDirHandles, numPDirHandles); + errno = saveErrno; + return -1; + } + // TODO: atm, fstat'ing a dir will show the info for the topmost + // dir. Maybe it would make sense of merging the bits. (How?) + +#if 0 + for (i = 1; i < numPDirHandles; i++) { + struct stat statOne; + uio_PDirHandle *pDirHandle; + + if (statOneDir(pDirHandles[i], &statOne) == -1) { + // errno is set + int saveErrno = errno; + uio_PDirHandles_delete(pDirHandles, numPDirHandles); + errno = saveErrno; + return -1; + } + + // Merge dirs: + + + } +#endif + + uio_PDirHandles_delete(pDirHandles, numPDirHandles); + return 0; +} + +static int +uio_statOneDir(uio_PDirHandle *pDirHandle, struct stat *statBuf) { + if (pDirHandle->pRoot->handler->stat == NULL) { + errno = ENOSYS; + return -1; + } + return pDirHandle->pRoot->handler->stat(pDirHandle, ".", statBuf); + // sets errno on error +} + int uio_mkdir(uio_DirHandle *dir, const char *path, mode_t mode) { uio_PDirHandle *pReadDir, *pWriteDir; diff --git a/sc2/src/sc2code/libs/uio/ioaux.c b/sc2/src/sc2code/libs/uio/ioaux.c index cc7f6d0a5..e5d9ce0f8 100644 --- a/sc2/src/sc2code/libs/uio/ioaux.c +++ b/sc2/src/sc2code/libs/uio/ioaux.c @@ -484,8 +484,10 @@ uio_getPhysicalAccess(uio_DirHandle *dirHandle, const char *path, // Get handles to the (existing) physical dirs that are effective in a // path 'path' relative from 'dirHandle' -// returns the PDirHandles through 'pDirHandles'. It is NULL if none were -// found. +// returns the PDirHandles through '*pDirHandles'. It is NULL if none +// were found. +// If resItems != NULL, it returns the MountTreeItems belonging to those +// PDIrHandles through *resItems. It is NULL if none were found. // numPDirHandles will contain the number of PDirHandles found. // returns 0 if everything went ok. // returns -1 in case of an error; errno is set. diff --git a/sc2/src/sc2code/libs/uio/memdebug.c b/sc2/src/sc2code/libs/uio/memdebug.c index c908bc0cc..b6bd1b6ff 100644 --- a/sc2/src/sc2code/libs/uio/memdebug.c +++ b/sc2/src/sc2code/libs/uio/memdebug.c @@ -59,6 +59,7 @@ const uio_MemDebug_LogTypeInfo uio_MemDebug_logTypeInfo[] = { { "stdio_GPDirData", NULL, 0 }, #ifdef HAVE_ZIP { "zip_GPFileData", NULL, 0 }, + { "zip_GPDirData", NULL, 0 }, #endif }; diff --git a/sc2/src/sc2code/libs/uio/memdebug.h b/sc2/src/sc2code/libs/uio/memdebug.h index 11c980b93..3862801c2 100644 --- a/sc2/src/sc2code/libs/uio/memdebug.h +++ b/sc2/src/sc2code/libs/uio/memdebug.h @@ -45,6 +45,7 @@ typedef enum { uio_MemDebug_LogType_uio_Stream, uio_MemDebug_LogType_stdio_GPDirData, uio_MemDebug_LogType_zip_GPFileData, + uio_MemDebug_LogType_zip_GPDirData, uio_MemDebug_numLogTypes, /* This needs to be the last entry */ } uio_MemDebug_LogType; diff --git a/sc2/src/sc2code/libs/uio/uiostream.h b/sc2/src/sc2code/libs/uio/uiostream.h index 11ffecfa1..01117c9bc 100644 --- a/sc2/src/sc2code/libs/uio/uiostream.h +++ b/sc2/src/sc2code/libs/uio/uiostream.h @@ -91,7 +91,7 @@ struct uio_Stream { char *bufEnd; off_t seekLow; // Low(er) level file pointer. Always points to the position just - // past the part that's in the buffer. + // past the part that's in the buffer as [readStart..readEnd]. uio_Handle *handle; int status; #define uio_Stream_STATUS_OK 0 diff --git a/sc2/src/sc2code/libs/uio/zip/zip.c b/sc2/src/sc2code/libs/uio/zip/zip.c index a89dec01c..93bab18ec 100644 --- a/sc2/src/sc2code/libs/uio/zip/zip.c +++ b/sc2/src/sc2code/libs/uio/zip/zip.c @@ -58,13 +58,17 @@ static int zip_fillDirStructureCentralProcessFile(uio_GPDir *topGPDir, uio_FileBlock *fileBlock, off_t *pos); static int zip_updatePFileDataFromLocalFileHeader(zip_GPFileData *gPFileData, uio_FileBlock *fileBlock, int pos); +int zip_updateFileDataFromLocalHeader(uio_Handle *handle, + zip_GPFileData *gPFileData); #endif static int zip_fillDirStructureProcessExtraFields( uio_FileBlock *fileBlock, off_t extraFieldLength, - zip_GPFileData *gPFileData, const char *fileName, off_t pos); -static inline int zip_foundFile(uio_GPDir *gPDir, const char *fileName, + zip_GPFileData *gPFileData, const char *path, off_t pos, + uio_bool central); +static inline int zip_foundFile(uio_GPDir *gPDir, const char *path, zip_GPFileData *gPFileData); -//static inline uio_GPDir *zip_foundDir(uio_GPDir *gPDir, const char *dirName); +static inline int zip_foundDir(uio_GPDir *gPDir, const char *dirName, + zip_GPDirData *gPDirData); static int zip_initZipStream(z_stream *zipStream); static int zip_unInitZipStream(z_stream *zipStream); static int zip_reInitZipStream(z_stream *zipStream); @@ -76,6 +80,8 @@ static inline zip_GPFileData * zip_GPFileData_new(void); static inline void zip_GPFileData_delete(zip_GPFileData *gPFileData); static inline zip_GPFileData *zip_GPFileData_alloc(void); static inline void zip_GPFileData_free(zip_GPFileData *gPFileData); +static inline void zip_GPDirData_delete(zip_GPDirData *gPDirData); +static inline void zip_GPDirData_free(zip_GPDirData *gPDirData); static ssize_t zip_readStored(uio_Handle *handle, void *buf, size_t count); static ssize_t zip_readDeflated(uio_Handle *handle, void *buf, size_t count); @@ -114,7 +120,7 @@ uio_FileSystemHandler zip_fileSystemHandler = { uio_GPRoot_Operations zip_GPRootOperations = { /* .fillGPDir = */ NULL, /* .deleteGPRootExtra = */ NULL, - /* .deleteGPDirExtra = */ NULL, + /* .deleteGPDirExtra = */ zip_GPDirData_delete, /* .deleteGPFileExtra = */ zip_GPFileData_delete, }; @@ -151,6 +157,33 @@ zip_seekFunctionType zip_seekMethods[NUM_COMPRESSION_METHODS] = { }; +typedef enum { + zip_OSType_FAT, + zip_OSType_Amiga, + zip_OSType_OpenVMS, + zip_OSType_UNIX, + zip_OSType_VMCMS, + zip_OSType_AtariST, + zip_OSType_HPFS, + zip_OSType_HFS, + zip_OSType_ZSystem, + zip_OSType_CPM, + zip_OSType_TOPS20, + zip_OSType_NTFS, + zip_OSType_QDOS, + zip_OSType_Acorn, + zip_OSType_VFAT, + zip_OSType_MVS, + zip_OSType_BeOS, + zip_OSType_Tandem, + + zip_numOSTypes +} zip_OSType; + +#if zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS +static mode_t zip_makeFileMode(zip_OSType creatorOS, uio_uint32 modeBytes); +#endif + #define zip_INPUT_BUFFER_SIZE 0x10000 // TODO: make this configurable a la sysctl? #define zip_SEEK_BUFFER_SIZE zip_INPUT_BUFFER_SIZE @@ -184,6 +217,16 @@ zip_fillStat(struct stat *statBuf, const zip_GPFileData *gPFileData) { int zip_fstat(uio_Handle *handle, struct stat *statBuf) { +#if zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS + if (handle->native->file->extra->fileOffset == -1) { + // The local header wasn't read in yet. + if (zip_updateFileDataFromLocalHeader(handle->root->handle, + handle->native->file->extra) == -1) { + // errno is set + return -1; + } + } +#endif zip_fillStat(statBuf, handle->native->file->extra); return 0; } @@ -191,16 +234,30 @@ zip_fstat(uio_Handle *handle, struct stat *statBuf) { int zip_stat(uio_PDirHandle *pDirHandle, const char *name, struct stat *statBuf) { uio_GPDirEntry *entry; - - entry = uio_GPDir_getGPDirEntry(pDirHandle->extra, name); - if (entry == NULL) { - errno = ENOENT; - return -1; + + if (name[0] == '.' && name[1] == '\0') { + entry = (uio_GPDirEntry *) pDirHandle->extra; + } else { + entry = uio_GPDir_getGPDirEntry(pDirHandle->extra, name); + if (entry == NULL) { + errno = ENOENT; + return -1; + } } - if (uio_GPDirEntry_isDir(entry)) { - // No dir data is read from zip file. - // We'll have to make something up for the time being. +#if zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS + if (((zip_GPFileData *) entry->extra)->fileOffset == -1) { + // The local header wasn't read in yet. + if (zip_updateFileDataFromLocalHeader(pDirHandle->pRoot->handle, + (zip_GPFileData *) entry->extra) == -1) { + // errno is set + return -1; + } + } +#endif + if (uio_GPDirEntry_isDir(entry) && entry->extra == NULL) { + // No information about this directory was stored. + // We'll have to make something up. memset(statBuf, '\0', sizeof (struct stat)); statBuf->st_mode = S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXOTH | @@ -210,7 +267,7 @@ zip_stat(uio_PDirHandle *pDirHandle, const char *name, struct stat *statBuf) { return 0; } - zip_fillStat(statBuf, ((uio_GPFile *) entry)->extra); + zip_fillStat(statBuf, (zip_GPFileData *) entry->extra); return 0; } @@ -248,28 +305,12 @@ zip_open(uio_PDirHandle *pDirHandle, const char *name, int flags, #if zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS if (gPFile->extra->fileOffset == -1) { - // the local header wasn't read in yet. - uio_FileBlock *fileBlock; - zip_GPFileData *gPFileData; - - fileBlock = uio_openFileBlock(pDirHandle->pRoot->handle); - if (fileBlock == NULL) { + // The local header wasn't read in yet. + if (zip_updateFileDataFromLocalHeader(pDirHandle->pRoot->handle, + gPFile->extra) == -1) { // errno is set return NULL; } - gPFileData = gPFile->extra; - if (zip_updatePFileDataFromLocalFileHeader(gPFileData, - fileBlock, gPFile->extra->headerOffset) == -1) { - int saveErrno = errno; - uio_closeFileBlock(fileBlock); - errno = saveErrno; - return NULL; - } - if (gPFileData->ctime == (time_t) 0) - gPFileData->ctime = gPFileData->mtime; - if (gPFileData->atime == (time_t) 0) - gPFileData->atime = gPFileData->mtime; - uio_closeFileBlock(fileBlock); } #endif @@ -634,6 +675,7 @@ zip_fillDirStructureCentralProcessFile(uio_GPDir *topGPDir, uio_uint16 extraFieldLength; uio_uint16 fileCommentLength; char *fileName; + zip_OSType creatorOS; off_t nextEntryOffset; @@ -647,8 +689,9 @@ zip_fillDirStructureCentralProcessFile(uio_GPDir *topGPDir, errno = EIO; return -1; } - + gPFileData = zip_GPFileData_new(); + creatorOS = (zip_OSType) buf[5]; gPFileData->compressionFlags = makeUInt16(buf[8], buf[9]); gPFileData->compressionMethod = makeUInt16(buf[10], buf[11]); lastModTime = makeUInt16(buf[12], buf[13]); @@ -656,9 +699,6 @@ zip_fillDirStructureCentralProcessFile(uio_GPDir *topGPDir, gPFileData->atime = (time_t) 0; gPFileData->mtime = dosToUnixTime(lastModDate, lastModTime); gPFileData->ctime = (time_t) 0; - gPFileData->uid = 0; - gPFileData->gid = 0; - gPFileData->mode = S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; crc = makeUInt32(buf[16], buf[17], buf[18], buf[19]); gPFileData->compressedSize = makeUInt32(buf[20], buf[21], buf[22], buf[23]); @@ -667,6 +707,11 @@ zip_fillDirStructureCentralProcessFile(uio_GPDir *topGPDir, fileNameLength = makeUInt16(buf[28], buf[29]); extraFieldLength = makeUInt16(buf[30], buf[31]); fileCommentLength = makeUInt16(buf[32], buf[33]); + gPFileData->uid = 0; + gPFileData->gid = 0; + gPFileData->mode = zip_makeFileMode(creatorOS, + makeUInt32(buf[38], buf[39], buf[40], buf[41])); + gPFileData->headerOffset = (off_t) makeSInt32(buf[42], buf[43], buf[44], buf[45]); gPFileData->fileOffset = (off_t) -1; @@ -710,7 +755,7 @@ zip_fillDirStructureCentralProcessFile(uio_GPDir *topGPDir, } switch (zip_fillDirStructureProcessExtraFields(fileBlock, - extraFieldLength, gPFileData, fileName, *pos)) { + extraFieldLength, gPFileData, fileName, *pos, true)) { case 0: // file is ok break; case 1: // file is not acceptable - skip file @@ -726,19 +771,45 @@ zip_fillDirStructureCentralProcessFile(uio_GPDir *topGPDir, // If ctime or atime is 0, they will be filled in when the local // file header is read. - - if (zip_foundFile(topGPDir, fileName, gPFileData) == -1) { - if (errno == EISDIR) { - zip_GPFileData_delete(gPFileData); - uio_free(fileName); - return 0; + + if (S_ISREG(gPFileData->mode)) { + if (zip_foundFile(topGPDir, fileName, gPFileData) == -1) { + if (errno == EISDIR) { + fprintf(stderr, "Warning: file '%s' already exists as a dir - " + "skipped.\n", fileName); + zip_GPFileData_delete(gPFileData); + uio_free(fileName); + return 0; + } + return zip_badFile(gPFileData, fileName); } - return zip_badFile(gPFileData, fileName); - } #if defined(DEBUG) && DEBUG > 1 - fprintf(stderr, "Debug: Found file '%s'.\n", fileName); + fprintf(stderr, "Debug: Found file '%s'.\n", fileName); #endif + } else if (S_ISDIR(gPFileData->mode)) { + if (fileName[fileNameLength - 1] == '/') + fileName[fileNameLength - 1] = '\0'; + if (zip_foundDir(topGPDir, fileName, gPFileData) == -1) { + if (errno == EISDIR) { + fprintf(stderr, "Warning: file '%s' already exists as a dir - " + "skipped.\n", fileName); + zip_GPFileData_delete(gPFileData); + uio_free(fileName); + return 0; + } + return zip_badFile(gPFileData, fileName); + } +#if defined(DEBUG) && DEBUG > 1 + fprintf(stderr, "Debug: Found dir '%s'.\n", fileName); +#endif + } else { + fprintf(stderr, "Warning: '%s' is not a regular file, nor a " + "directory - skipped.\n", fileName); + zip_GPFileData_delete(gPFileData); + uio_free(fileName); + return 0; + } uio_free(fileName); return 0; @@ -793,6 +864,43 @@ zip_findEndOfCentralDirectoryRecord(uio_Handle *handle, return startPos + (bufPtr - buf); } +static mode_t +zip_makeFileMode(zip_OSType creatorOS, uio_uint32 modeBytes) { + switch (creatorOS) { + case zip_OSType_FAT: + case zip_OSType_NTFS: + case zip_OSType_VFAT: { + mode_t mode; + + if (modeBytes == 0) { + // File came from standard input + return S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | + S_IROTH | S_IWOTH; + } + if (modeBytes & 0x00001000) { + // Directory + mode = S_IFDIR | S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | + S_IROTH | S_IXOTH; + } else { + // Regular file + mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; + } + if (modeBytes & 0x00000100) { + // readonly + return mode; + } else { + // Write allowed + return mode | S_IWUSR | S_IWGRP | S_IWOTH; + } + } + case zip_OSType_UNIX: + return (mode_t) (modeBytes >> 16); + default: + fprintf(stderr, "Warning: file created by unknown OS.\n"); + return S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; + } +} + // If the data is read from the central directory, certain data // will need to be updated from the local directory when it is needed. // This function does that. @@ -824,7 +932,7 @@ zip_updatePFileDataFromLocalFileHeader(zip_GPFileData *gPFileData, pos += 30 + fileNameLength; switch (zip_fillDirStructureProcessExtraFields(fileBlock, - extraFieldLength, gPFileData, "", pos)) { + extraFieldLength, gPFileData, "", pos, false)) { case 0: // file is ok break; case 1: @@ -935,7 +1043,7 @@ zip_fillDirStructureLocalProcessFile(uio_GPDir *topGPDir, gPFileData->ctime = (time_t) 0; gPFileData->uid = 0; gPFileData->gid = 0; - gPFileData->mode = S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; + gPFileData->mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; if (!isBitSet(gPFileData->compressionFlags, 3)) { // If bit 3 is not set, this info will be in the data descriptor // behind the file data. @@ -983,13 +1091,18 @@ zip_fillDirStructureLocalProcessFile(uio_GPDir *topGPDir, numBytes = uio_accessFileBlock(fileBlock, *pos, fileNameLength, &buf); if (numBytes != fileNameLength) return zip_badFile(gPFileData, NULL); + *pos += fileNameLength; + if (buf[fileNameLength - 1] == '/') { + gPFileData->mode |= S_IFDIR; + fileNameLength--; + } else + gPFileData->mode |= S_IFREG; fileName = uio_malloc(fileNameLength + 1); memcpy(fileName, buf, fileNameLength); fileName[fileNameLength] = '\0'; - *pos += fileNameLength; switch (zip_fillDirStructureProcessExtraFields(fileBlock, - extraFieldLength, gPFileData, fileName, *pos)) { + extraFieldLength, gPFileData, fileName, *pos, false)) { case 0: // file is ok break; case 1: // file is not acceptable - skip file @@ -1031,31 +1144,84 @@ zip_fillDirStructureLocalProcessFile(uio_GPDir *topGPDir, if (gPFileData->atime == (time_t) 0) gPFileData->atime = gPFileData->mtime; - if (zip_foundFile(topGPDir, fileName, gPFileData) == -1) { - if (errno == EISDIR) { - zip_GPFileData_delete(gPFileData); - uio_free(fileName); - return 0; + if (S_ISREG(gPFileData->mode)) { + if (zip_foundFile(topGPDir, fileName, gPFileData) == -1) { + if (errno == EISDIR) { + fprintf(stderr, "Warning: file '%s' already exists as a dir - " + "skipped.\n", fileName); + zip_GPFileData_delete(gPFileData); + uio_free(fileName); + return 0; + } + return zip_badFile(gPFileData, fileName); } - return zip_badFile(gPFileData, fileName); - } -#ifdef DEBUG - fprintf(stderr, "Debug: Found file '%s'.\n", fileName); +#if defined(DEBUG) && DEBUG > 1 + fprintf(stderr, "Debug: Found file '%s'.\n", fileName); #endif + } else if (S_ISDIR(gPFileData->mode)) { + if (fileName[fileNameLength - 1] == '/') + fileName[fileNameLength - 1] = '\0'; + if (zip_foundDir(topGPDir, fileName, gPFileData) == -1) { + if (errno == EISDIR) { + fprintf(stderr, "Warning: file '%s' already exists as a dir - " + "skipped.\n", fileName); + zip_GPFileData_delete(gPFileData); + uio_free(fileName); + return 0; + } + return zip_badFile(gPFileData, fileName); + } +#if defined(DEBUG) && DEBUG > 1 + fprintf(stderr, "Debug: Found dir '%s'.\n", fileName); +#endif + } else { + fprintf(stderr, "Warning: '%s' is not a regular file, nor a " + "directory - skipped.\n", fileName); + zip_GPFileData_delete(gPFileData); + uio_free(fileName); + return 0; + } uio_free(fileName); return 0; } #endif /* zip_USE_HEADERS == zip_USE_LOCAL_HEADERS */ +#if zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS +int +zip_updateFileDataFromLocalHeader(uio_Handle *handle, + zip_GPFileData *gPFileData) { + uio_FileBlock *fileBlock; + + fileBlock = uio_openFileBlock(handle); + if (fileBlock == NULL) { + // errno is set + return -1; + } + if (zip_updatePFileDataFromLocalFileHeader(gPFileData, + fileBlock, gPFileData->headerOffset) == -1) { + int saveErrno = errno; + uio_closeFileBlock(fileBlock); + errno = saveErrno; + return -1; + } + if (gPFileData->ctime == (time_t) 0) + gPFileData->ctime = gPFileData->mtime; + if (gPFileData->atime == (time_t) 0) + gPFileData->atime = gPFileData->mtime; + uio_closeFileBlock(fileBlock); + return 0; +} +#endif + // If the zip file is bad, -1 is returned (no errno set!) // If the file in the zip file should be skipped, 1 is returned. // If the file in the zip file is ok, 0 is returned. static int zip_fillDirStructureProcessExtraFields(uio_FileBlock *fileBlock, off_t extraFieldLength, zip_GPFileData *gPFileData, - const char *fileName, off_t pos) { + const char *fileName, off_t pos, uio_bool central) { off_t posEnd; uio_uint16 headerID; ssize_t dataSize; @@ -1081,6 +1247,8 @@ zip_fillDirStructureProcessExtraFields(uio_FileBlock *fileBlock, buf[0], buf[1], buf[2], buf[3]); gPFileData->mtime = (time_t) makeUInt32( buf[4], buf[5], buf[6], buf[7]); + if (central) + break; if (dataSize > 8) { gPFileData->uid = (uid_t) makeUInt16(buf[8], buf[9]); gPFileData->gid = (uid_t) makeUInt16(buf[10], buf[11]); @@ -1098,6 +1266,13 @@ zip_fillDirStructureProcessExtraFields(uio_FileBlock *fileBlock, bufPtr[0], bufPtr[1], bufPtr[2], bufPtr[3]); bufPtr += 4; } + // The flags field, even in the central header, specifies what + // is present in the local header. + // The central header only contains a field for the mtime + // when it is present in the local header too, and + // never contains fields for other times. + if (central) + break; if (isBitSet(flags, 1)) { // modification time is present gPFileData->atime = (time_t) makeUInt32( @@ -1108,15 +1283,17 @@ zip_fillDirStructureProcessExtraFields(uio_FileBlock *fileBlock, break; } case 0x7855: // 'Unix2' + if (central) + break; gPFileData->uid = (uid_t) makeUInt16(buf[0], buf[1]); gPFileData->gid = (uid_t) makeUInt16(buf[2], buf[3]); break; case 0x756e: { // 'Unix3' mode_t mode; mode = (mode_t) makeUInt16(buf[4], buf[5]); - if (!S_ISREG(mode)) { + if (!S_ISREG(mode) && !S_ISDIR(mode)) { fprintf(stderr, "Warning: Skipping '%s'; not a regular " - "file.\n", fileName); + "file, nor a directory.\n", fileName); return 1; } gPFileData->uid = (uid_t) makeUInt16(buf[10], buf[11]); @@ -1193,13 +1370,13 @@ zip_foundFile(uio_GPDir *gPDir, const char *path, zip_GPFileData *gPFileData) { if (end == start || (end - start == 1 && start[0] == '.') || (end - start == 2 && start[0] == '.' && start[1] == '.')) { fprintf(stderr, "Warning: file '%s' has an invalid path - " - "file skipped.\n", path); + "skipped.\n", path); uio_free(buf); errno = EINVAL; return -1; } if (end == pathEnd) { - // This is the last component; the should be the name of the dir. + // This is the last component; it should be the name of the dir. path = start; break; } @@ -1207,7 +1384,7 @@ zip_foundFile(uio_GPDir *gPDir, const char *path, zip_GPFileData *gPFileData) { buf[end - start] = '\0'; newGPDir = uio_GPDir_prepareSubDir(gPDir, buf); newGPDir->flags |= uio_GPDir_COMPLETE; - // It will be complete when we're done with adding + // It will be complete when we're done adding // all files, and it won't be used before that. uio_GPDir_commitSubDir(gPDir, buf, newGPDir); @@ -1223,6 +1400,68 @@ zip_foundFile(uio_GPDir *gPDir, const char *path, zip_GPFileData *gPFileData) { return 0; } +static inline int +zip_foundDir(uio_GPDir *gPDir, const char *path, zip_GPDirData *gPDirData) { + size_t pathLen; + const char *pathEnd; + const char *start, *end; + char *buf; + + if (path[0] == '/') + path++; + pathLen = strlen(path); + pathEnd = path + pathLen; + + switch (uio_walkGPPath(gPDir, path, pathLen, &gPDir, &path)) { + case 0: + // The dir already exists. Only need to add gPDirData + if (gPDir->extra) { + fprintf(stderr, "Warning: '%s' is present more than once " + "in the zip file. Last entry will be used.\n", + path); + zip_GPDirData_free(gPDir->extra); + } + gPDir->extra = gPDirData; + return 0; + case ENOTDIR: + fprintf(stderr, "Warning: A component of '%s' is not a " + "directory - file skipped.\n", path); + errno = ENOTDIR; + return -1; + case ENOENT: + break; + } + + buf = uio_malloc(pathLen + 1); + getFirstPathComponent(path, pathEnd, &start, &end); + while (start < pathEnd) { + uio_GPDir *newGPDir; + + if (end == start || (end - start == 1 && start[0] == '.') || + (end - start == 2 && start[0] == '.' && start[1] == '.')) { + fprintf(stderr, "Warning: directory '%s' has an invalid path - " + "skipped.\n", path); + uio_free(buf); + errno = EINVAL; + return -1; + } + memcpy(buf, start, end - start); + buf[end - start] = '\0'; + newGPDir = uio_GPDir_prepareSubDir(gPDir, buf); + newGPDir->flags |= uio_GPDir_COMPLETE; + // It will be complete when we're done adding + // all files, and it won't be used before that. + newGPDir->extra = gPDirData; + uio_GPDir_commitSubDir(gPDir, buf, newGPDir); + + gPDir = newGPDir; + getNextPathComponent(pathEnd, &start, &end); + } + + uio_free(buf); + return 0; +} + static int zip_initZipStream(z_stream *zipStream) { int retVal; @@ -1356,5 +1595,18 @@ zip_GPFileData_free(zip_GPFileData *gPFileData) { uio_free(gPFileData); } +static inline void +zip_GPDirData_delete(zip_GPDirData *gPDirData) { + zip_GPDirData_free(gPDirData); +} + +static inline void +zip_GPDirData_free(zip_GPDirData *gPDirData) { +#ifdef uio_MEM_DEBUG + uio_MemDebug_debugFree(zip_GPFileData, (void *) gPDirData); +#endif + uio_free(gPDirData); +} + diff --git a/sc2/src/sc2code/libs/uio/zip/zip.h b/sc2/src/sc2code/libs/uio/zip/zip.h index 877ec8d75..0c02f385e 100644 --- a/sc2/src/sc2code/libs/uio/zip/zip.h +++ b/sc2/src/sc2code/libs/uio/zip/zip.h @@ -20,8 +20,8 @@ typedef struct zip_Handle *uio_NativeHandle; typedef void *uio_GPRootExtra; -typedef void *uio_GPDirExtra; typedef struct zip_GPFileData *uio_GPFileExtra; +typedef struct zip_GPFileData *uio_GPDirExtra; typedef struct uio_GPDirEntries_Iterator *uio_NativeEntriesContext; #define uio_INTERNAL_PHYSICAL @@ -64,6 +64,11 @@ typedef struct zip_GPFileData { time_t ctime; // change time } zip_GPFileData; +typedef zip_GPFileData zip_GPDirData; +// TODO: some of the fields from zip_GPFileData are not needed for +// directories. A few bytes could be saved here by making a seperate +// structure. + typedef struct zip_Handle { uio_GPFile *file; z_stream zipStream;