📄 jar.c
字号:
/* * jar.c * Handle JAR input files. * * Copyright (c) 2000, 2001, 2002 The University of Utah and the Flux Group. * All rights reserved. * * This file is licensed under the terms of the GNU Public License. * See the file "license.terms" for restrictions on redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. */#include "config.h"#include "debug.h"#include "config-std.h"#include "config-io.h"#include "config-mem.h"#include "gtypes.h"#include "jsyscall.h"#include "inflate.h"#include "jar.h"#include "gc.h"#include "stats.h"#include "files.h"/* Undefine this to make jar files mutable during the vm lifetime *//* #define STATIC_JAR_FILES */#if defined(KAFFEH)#undef ABORT#define ABORT() abort()#undef initStaticLock#define initStaticLock(x)#undef staticLockIsInitialized#define staticLockIsInitialized(x) 1#undef lockStaticMutex#undef unlockStaticMutex#define unlockStaticMutex(x)#define lockStaticMutex(x)#undef lockMutex#undef unlockMutex#define lockMutex(x)#define unlockMutex(x)#endif/* * Error messages. */static const char * JAR_ERROR_BAD_CENTRAL_RECORD_SIGNATURE = "Bad central record signature";static const char * JAR_ERROR_BAD_SIGNATURE = "Bad signature";static const char * JAR_ERROR_DECOMPRESSION_FAILED = "Decompression failed";static const char * JAR_ERROR_ENTRY_COUNT_MISMATCH = "Entry count doesn't match directory size";static const char * JAR_ERROR_IMPOSSIBLY_LARGE_DIRECTORY = "Impossibly large directory size";static const char * JAR_ERROR_IO = "I/O error";static const char * JAR_ERROR_NO_END = "Failed to find end of JAR record";static const char * JAR_ERROR_OUT_OF_MEMORY = "Out of memory";static const char * JAR_ERROR_TRUNCATED_FILE = "Truncated file";static const char * JAR_ERROR_UNSUPPORTED_COMPRESSION = "Unsupported compression in JAR file";/* * The jarCache keeps a list of all the jarFiles cached in the system. * However, since some JAR files are opened and closed frequently we don't * actively flush unused files from the system unless there are more than * JAR_FILE_CACHE_MAX jarFiles in the system. This means we'll keep some * jarFiles in memory longer than we should but it helps to avoid the * constant opening/closing some java code does. */static struct _jarCache {#if !defined(KAFFEH) iStaticLock lock;#endif jarFile *files;#define JAR_FILE_CACHE_MAX 12 unsigned int count;} jarCache;/* * Hash a file name, the hash value is stored in what `hash' points to. */static unsigned int hashName(const char *name){ unsigned int hash = 0; assert(name != 0); for( hash = 0; *name; name++ ) hash = (31 * hash) + (*name); return hash;}/* * Find a cached jarFile object. If the file is found, it is returned and its * user count is incremented. * * XXX rename findCachedJarFile() */static jarFile *findJarFile(char *name){ jarFile *curr, **prev, *retval = 0;#if !defined(KAFFEH) int iLockRoot;#endif assert(name != 0); lockStaticMutex(&jarCache.lock); curr = jarCache.files; prev = &jarCache.files; while( curr && !retval ) { assert(curr != NULL); assert(curr->fileName != 0); if( !strcmp(curr->fileName, name) ) { /* unlink it... */ *prev = curr->next; /* and move it to the front */ curr->next = jarCache.files; jarCache.files = curr; /* Return this node and increment the user count */ retval = curr; retval->users++; assert(retval->users >= 1); } prev = &curr->next; curr = curr->next; } unlockStaticMutex(&jarCache.lock); return( retval );}/* * Free the contents of the entry table. */static void collectEntryTable(jarFile *jf){ assert(jf != 0); assert(jf->users == 0); if( jf->table ) { addToCounter(&jarmem, "vmmem-jar files", 1, -(jlong)GCSIZEOF(jf->table)); gc_free(jf->table); jf->table = 0; }}/* * Free a jarFile structure and its child objects. */static void collectJarFile(jarFile *jf){ assert(jf != 0); assert(jf->users == 0); assert(!(jf->flags & JFF_CACHED)); collectEntryTable(jf); /* Make sure we free everything */ if( jf->fd != -1 ) { /* Close the file */ KCLOSE(jf->fd); jf->fd = -1; }#ifdef HAVE_MMAP if( jf->data != MAP_FAILED ) {#if !defined(NDEBUG) /* Only define rc for use in assert */ int rc = #endif /* defined(NDEBUG) */ munmap(jf->data, jf->size); assert(rc == 0); }#endif addToCounter(&jarmem, "vmmem-jar files", 1, -(jlong)GCSIZEOF(jf)); gc_free(jf);}/* * Cache a jarFile object, if the passed in object matches one in the cache * it will be thrown away, and the cached one is returned, with its user * count incremented. */static jarFile *cacheJarFile(jarFile *jf){ jarFile *curr, **prev, **lru = 0, *dead_jar = 0, *retval = jf; int already_cached = 0;#if !defined(KAFFEH) int iLockRoot;#endif assert(jf != 0); assert(!(jf->flags & JFF_CACHED)); lockStaticMutex(&jarCache.lock); /* * Walk the cache, if the file we're trying to cache already matches * one in the cache then we can just use it. Otherwise, we need to * make room in the cache and continue. */ curr = jarCache.files; prev = &jarCache.files; while( curr && !already_cached ) { assert(curr != NULL); assert(curr->fileName != 0); /* Look for a matching JAR file */ if( !strcmp(curr->fileName, jf->fileName) ) { /* Names match, check the dates */ if( curr->lastModified == jf->lastModified ) { /* * They're the same, unlink it from the cache * so we can put it at the front later on. */ *prev = curr->next; retval = curr; retval->users++; } else { /* * The modified time is different, purge our * cached version and proclaim this one the * canonical version. * * XXX Hmm... Do we care how the two dates * relate? Since this is the one now being * loaded from disk it "must" be the one * the user wants. Bleh, too bad the * semantics don't seem to have been defined. */ *prev = curr->next; curr->flags &= ~JFF_CACHED; dead_jar = curr; } /* * `jf' is redundant so the number of cached files * isn't going to change. */ already_cached = 1; assert(retval->users >= 1); } else if( curr->users == 0 ) { /* * Record the least recently used file in case we need * to eject someone. */ lru = prev; } prev = &curr->next; curr = curr->next; } if( !already_cached ) { /* * Cache the file if theres still room rather than ejecting * the lru or if theres no room and no lru. */ if( (jarCache.count < JAR_FILE_CACHE_MAX) || !lru ) { /* Adding a new cache node */ jarCache.count++; } else { /* * Theres an unused jarFile, unlink the least * recently used one. */ dead_jar = *lru; *lru = dead_jar->next; dead_jar->flags &= ~JFF_CACHED; } } /* Put the file at the start of the cache */ retval->next = jarCache.files; jarCache.files = retval; retval->flags |= JFF_CACHED; unlockStaticMutex(&jarCache.lock); /* * Free the redundant/excess files outside of the lock. * * NOTE: The dead_jar test must come first since the file could * already be cached and get superceded by a newer version of the * file. */ if( dead_jar ) collectJarFile(dead_jar); else if( already_cached ) collectJarFile(jf); assert(retval != 0); return( retval );}/* * Remove a JAR file from the cache. */static void removeJarFile(jarFile *jf){ jarFile *curr, **prev;#if !defined(KAFFEH) int iLockRoot;#endif assert(jf != 0); /* Make sure its actually in the cache. */ if( jf->flags & JFF_CACHED ) { lockStaticMutex(&jarCache.lock); { curr = jarCache.files; prev = &jarCache.files; /* Find `jf' on the list and... */ while( curr != jf ) { assert(curr != 0); prev = &curr->next; curr = curr->next; } /* unlink it */ *prev = curr->next; jf->next = 0; jf->flags &= ~JFF_CACHED; jarCache.count--; } unlockStaticMutex(&jarCache.lock); }}void flushJarCache(void){ jarFile **prev, *curr, *next;#if !defined(KAFFEH) int iLockRoot;#endif lockStaticMutex(&jarCache.lock); curr = jarCache.files; prev = &jarCache.files; while( curr ) { next = curr->next; if( curr->users == 0 ) { *prev = next; curr->flags &= ~JFF_CACHED; collectJarFile(curr); } else { prev = &curr->next; } curr = next; } unlockStaticMutex(&jarCache.lock);}/* * Convenient read function that operates on regular or mmap'ed files. * This also takes an `instantiation' function which is used to convert * any data into the proper byte order and alignment. */static inline int jarRead(jarFile *jf, uint8 *buf, size_t len, int (*ins_func)(uint8 *dest, uint8 *src)){ int retval = -1; assert(jf != 0); assert(buf != 0); #ifdef HAVE_MMAP if( jf->data != MAP_FAILED ) { if((jf->offset + len) > jf->size ) { jf->error = JAR_ERROR_TRUNCATED_FILE; } else if( ins_func ) { /* * We optimize this to use the instantiation function * to do the copying instead of doing a memcpy and then * moving stuff around. */ jf->offset += ins_func(buf, jf->data + jf->offset); retval = len; } else { /* Just copy the bits */ memcpy(buf, jf->data + jf->offset, len); jf->offset += len; retval = len; } } else#endif { size_t bytes_left; ssize_t bytes_read; int rc = 0; bytes_left = len; /* XXX is this loop necessary? */ while( bytes_left && !(rc = KREAD(jf->fd, &buf[len - bytes_left], bytes_left, &bytes_read)) && bytes_read ) { bytes_left -= bytes_read; } if( rc ) { jf->error = SYS_ERROR(rc); } else if( bytes_left ) { jf->error = JAR_ERROR_TRUNCATED_FILE; } else { /* Instantiate the memory */ if( ins_func ) ins_func(buf, buf); retval = len; } } return( retval );}/* * Instantiate a structure that was encoded in a flat file. We use a define * to make sure the instantiation function is inlined. */#ifdef HAVE_MMAP#define jarInstantiate(jf, buf, ins_func) \{ \ if( jf->data != MAP_FAILED ) \ { \ jf->offset += ins_func(buf, jf->data + jf->offset); \ } \ else \ { \ ins_func(buf, buf); \ } \}#else#define jarInstantiate(jf, buf, ins_func) \{ \ ins_func(buf, buf); \}#endif/* * Convenient seek function that operates on regular or mmap'ed files. */static inline off_t jarSeek(jarFile *jf, off_t offset, int whence){ off_t retval = (off_t)-1; assert(jf != 0); #ifdef HAVE_MMAP if( jf->data != (uint8*)-1 ) { off_t pos; switch( whence ) { case SEEK_CUR: pos = jf->offset + offset; break; case SEEK_SET: pos = offset; break; case SEEK_END: pos = jf->size + offset; break; default: ABORT(); break; } if( (pos >= 0) && (pos < jf->size) ) { jf->offset = pos; retval = pos; } } else#endif { off_t off; int rc; rc = KLSEEK(jf->fd, offset, whence, &off); if( rc ) jf->error = SYS_ERROR(rc); else retval = off; } return( retval );}/* Macro used below to create a 32 bit value from a uint8 buffer */#define copy32le(dest, buf, index) \do { \ register uint32 tmp; \ \ tmp = (((buf)[(index) + 3] << 24) | \ ((buf)[(index) + 2] << 16) | \ ((buf)[(index) + 1] << 8) | \ ((buf)[(index) + 0])); \ dest = tmp; \} while(0);/* Macro used below to create a 16 bit value from a uint8 buffer */#define copy16le(dest, buf, index) \do { \ register uint16 tmp; \ \ tmp = (((buf)[(index) + 1] << 8) | \ ((buf)[(index) + 0])); \ dest = tmp; \} while(0);/* * Unfortunately, the data members of the zip headers aren't aligned properly * so we can't just blast the data into a structure. So we have to use these * instantiation functions to copy the data and do any byte swapping. * * Note 1: These functions work from high to low memory so that we're * basically just shuffling the bits around and not using extra storage. * * Note 2: We're hoping that the compiler is smart enough to drop the unused * data members when inlining... */static inline intinstantiateCentralDir(uint8 *dest, uint8 *buf){ jarCentralDirectoryRecord *cdr = (jarCentralDirectoryRecord *)dest; assert(dest != 0); assert(buf != 0); copy32le(cdr->relativeLocalHeaderOffset, buf, 42); copy32le(cdr->externalFileAttribute, buf, 38); /* Not used */ copy16le(cdr->internalFileAttribute, buf, 36); /* Not used */ copy16le(cdr->diskNumberStart, buf, 34); /* Not used */ copy16le(cdr->fileCommentLength, buf, 32); copy16le(cdr->extraFieldLength, buf, 30); copy16le(cdr->fileNameLength, buf, 28); copy32le(cdr->uncompressedSize, buf, 24); copy32le(cdr->compressedSize, buf, 20); copy32le(cdr->crc, buf, 16); /* Not used */ copy16le(cdr->lastModifiedDate, buf, 14); copy16le(cdr->lastModifiedTime, buf, 12); copy32le(cdr->compressionMethod, buf, 10); copy16le(cdr->flags, buf, 8); /* Not used */ copy16le(cdr->versionExtract, buf, 6); /* Not used */ copy16le(cdr->versionMade, buf, 4); /* Not Used */ return( FILE_SIZEOF_CENTRALDIR );}static inline intinstantiateLocalHeader(uint8 *dest, uint8 *buf){ jarLocalHeader *lh = (jarLocalHeader *)dest; assert(dest != 0); assert(buf != 0); copy16le(lh->extraFieldLength, buf, 28); copy16le(lh->fileNameLength, buf, 26); copy32le(lh->uncompressedSize, buf, 22); /* Not used */ copy32le(lh->compressedSize, buf, 18); /* Not used */ copy32le(lh->crc, buf, 14); /* Not used */ copy16le(lh->lastModifiedDate, buf, 12); /* Not used */ copy16le(lh->lastModifiedTime, buf, 10); /* Not used */ copy16le(lh->compressionMethod, buf, 8); /* Not used */ copy16le(lh->flags, buf, 6); /* Not used */ copy16le(lh->versionExtract, buf, 4); /* Not used */ return( FILE_SIZEOF_LOCALHEADER );}static inline intinstantiateCentralDirEnd(uint8 *dest, uint8 *buf){ jarCentralDirectoryEnd *cde = (jarCentralDirectoryEnd *)dest; assert(dest != 0); assert(buf != 0); copy16le(cde->commentLength, buf, 18); /* Not used */ copy32le(cde->offsetOfDirectory, buf, 16); copy32le(cde->sizeOfDirectory, buf, 12); /* Not used */ copy16le(cde->nrOfEntriesInDirectory, buf, 10); copy16le(cde->nrOfEntriesInThisDirectory, buf, 8); /* Not used */ copy16le(cde->numberOfDiskWithDirectory, buf, 6); /* Not used */ copy16le(cde->numberOfDisk, buf, 4); /* Not used */ return( FILE_SIZEOF_CENTRALEND );}static inline int instantiateSignature(uint8 *dest, uint8 *buf){ uint32 *sig = (uint32 *)dest; assert(dest != 0); assert(buf != 0);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -