📄 jar.c
字号:
copy32le(sig[0], buf, 0); /* * We're instantiating this structure in multiple stages so we don't * move the file pointer. */ return( 0 );}/* * Read a JAR header from the file and check it's signature. */static int readJarHeader(jarFile *jf, uint32 sig, void *buf, size_t len){ int retval = 0; assert(jf != 0); assert((sig == CENTRAL_HEADER_SIGNATURE) || (sig == LOCAL_HEADER_SIGNATURE) || (sig == CENTRAL_END_SIGNATURE)); assert(buf != 0); if( jarRead(jf, buf, len, instantiateSignature) == len ) { /* Check the signature */ if( sig == ((uint32 *)buf)[0] ) { retval = 1; } else { jf->error = JAR_ERROR_BAD_SIGNATURE; } } return( retval );}/* * Add a jarEntry to the given jarFile. */static void addJarEntry(jarFile *jf, jarEntry *je){ unsigned int hash; assert(jf != 0); assert(jf->table != 0); assert(je != 0); assert(je->fileName != 0); hash = hashName(je->fileName); hash = hash % jf->tableSize; je->next = jf->table[hash]; jf->table[hash] = je;}/* * Create a jarEntry structure for the current central directory record in * the file. */static int initJarEntry(jarFile *jf, jarEntry *je, char **name_strings){ jarCentralDirectoryRecord cdr; int retval = 0; assert(jf != 0); assert(je != 0); assert(name_strings != 0); assert(*name_strings != 0); /* Read the header */ if( readJarHeader(jf, CENTRAL_HEADER_SIGNATURE, &cdr, FILE_SIZEOF_CENTRALDIR) ) { int read_size; /* Instantiate the header */ jarInstantiate(jf, (uint8 *)&cdr, instantiateCentralDir); je->next = 0; /* Allocate space for our name */ (*name_strings) -= cdr.fileNameLength + 1; je->fileName = *name_strings; /* Copy whatever we care about from the file record */ je->dosTime = (cdr.lastModifiedDate << 16) | cdr.lastModifiedTime; je->localHeaderOffset = cdr.relativeLocalHeaderOffset; je->uncompressedSize = cdr.uncompressedSize; je->compressedSize = cdr.compressedSize; je->compressionMethod = cdr.compressionMethod; /* Read the file name */ if( (read_size = jarRead(jf, (uint8*) je->fileName, cdr.fileNameLength, 0)) >= 0 ) { /* Make sure its terminated */ je->fileName[cdr.fileNameLength] = 0; /* * Skip the extra field and comment, we should * now be positioned at the next directory * record, if there is one. */ if( jarSeek(jf, cdr.extraFieldLength + cdr.fileCommentLength, SEEK_CUR) > 0 ) { retval = 1; assert(strlen(je->fileName) == cdr.fileNameLength); } } } else { jf->error = JAR_ERROR_BAD_CENTRAL_RECORD_SIGNATURE; } return( retval );}/* * Find the central directory end, find out the number of central directory * entries and seek to the start of them. */static int getCentralDirCount(jarFile *jf, unsigned int *out_dir_size){ int pos, retval = -1; assert(jf != 0); assert(out_dir_size != 0); /* The central directory end is at the end of the file */ if( (pos = jarSeek(jf, -FILE_SIZEOF_CENTRALEND, SEEK_END)) > 0 ) { jarCentralDirectoryEnd cde; if( readJarHeader(jf, CENTRAL_END_SIGNATURE, &cde, FILE_SIZEOF_CENTRALEND) ) { jarInstantiate(jf, (uint8 *)&cde, instantiateCentralDirEnd); if( cde.nrOfEntriesInDirectory > (cde.sizeOfDirectory / FILE_SIZEOF_CENTRALDIR) ) { jf->error = JAR_ERROR_ENTRY_COUNT_MISMATCH; } else if( cde.sizeOfDirectory > pos ) { jf->error = JAR_ERROR_IMPOSSIBLY_LARGE_DIRECTORY; } else if( jarSeek(jf, cde.offsetOfDirectory, SEEK_SET) >= 0 ) { *out_dir_size = cde.sizeOfDirectory; retval = cde.nrOfEntriesInDirectory; } } else { jf->error = JAR_ERROR_NO_END; } } return( retval );}/* * Read the central directory records from the file */static int readJarEntries(jarFile *jf){ unsigned int dir_size; int retval = 0; assert(jf != 0); if( (jf->count = getCentralDirCount(jf, &dir_size)) >= 0 ) { unsigned int table_size; /* * Compute a sensible size for the hash table base on the * number of entries in the jar. */ jf->tableSize = (jf->count + 3) / 4; /* * We want a single block of memory for all jarEntry's and * their names. */ /* Hash table buckets */ table_size = sizeof(jarEntry *) * jf->tableSize; /* Add the total size of the central directory records */ table_size += dir_size; /* ... however, we don't read the whole central dir struct */ table_size -= jf->count * FILE_SIZEOF_CENTRALDIR; /* ... we use jarEntry's */ table_size += jf->count * sizeof(jarEntry); /* ... with NULL terminated name strings. */ table_size += jf->count * 1; if( (jf->table = gc_malloc(table_size, GC_ALLOC_JAR)) ) { char *name_strings; jarEntry *je; int lpc; /* jarEntry's are right after the table */ je = (jarEntry *)(jf->table + jf->tableSize); /* names are at the end of the memory block */ name_strings = ((char *)jf->table) + table_size; retval = 1; /* * Read in the central directory records and add them * to the hash table. */ for( lpc = 0; (lpc < jf->count) && retval; lpc++ ) { if( initJarEntry(jf, je, &name_strings) ) { addJarEntry(jf, je); je++; } else { retval = 0; } } } else { jf->error = JAR_ERROR_OUT_OF_MEMORY; } } else if( jf->error ) { } else { retval = 1; } return( retval );}/* * Simple function to handle uncompressing the file data. */static uint8 *inflateJarData(jarFile *jf, jarEntry *je, jarLocalHeader *lh, uint8 *buf){ uint8 *retval = 0; assert(jf != 0); assert(je != 0); assert(lh != 0); assert(buf != 0); switch( je->compressionMethod ) { case COMPRESSION_STORED: retval = buf; break; case COMPRESSION_DEFLATED: if( je->uncompressedSize == 0 ) { /* XXX What to do? */ retval = gc_malloc(8, GC_ALLOC_JAR); } else if( (retval = gc_malloc(je->uncompressedSize, GC_ALLOC_JAR)) ) { if( inflate_oneshot(buf, je->compressedSize, retval, je->uncompressedSize) == 0 ) { addToCounter(&jarmem, "vmmem-jar files", 1, GCSIZEOF(retval)); } else { jf->error = JAR_ERROR_DECOMPRESSION_FAILED; gc_free(retval); retval = 0; } } else { jf->error = JAR_ERROR_OUT_OF_MEMORY; } gc_free(buf); break; default: jf->error = JAR_ERROR_UNSUPPORTED_COMPRESSION; gc_free(buf); break; } return( retval );}uint8 *getDataJarFile(jarFile *jf, jarEntry *je){ uint8 *buf = 0, *retval = 0; jarLocalHeader lh;#if !defined(KAFFEH) int iLockRoot;#endif assert(jf != 0); assert(je != 0); lockMutex(jf); /* Move to the local header in the file and read it. */ if( !jf->error && (jarSeek(jf, je->localHeaderOffset, SEEK_SET) >= 0) && readJarHeader(jf, LOCAL_HEADER_SIGNATURE, &lh, FILE_SIZEOF_LOCALHEADER) ) { jarInstantiate(jf, (uint8 *)&lh, instantiateLocalHeader); /* Skip the local file name and extra fields */ jarSeek(jf, lh.fileNameLength + lh.extraFieldLength, SEEK_CUR); /* Allocate some memory and read in the file data */ if( (buf = (uint8 *)gc_malloc(je->compressedSize, GC_ALLOC_JAR)) ) { if( jarRead(jf, buf, je->compressedSize, 0) < 0 ) { gc_free(buf); buf = 0; jf->error = JAR_ERROR_IO; } } else { jf->error = JAR_ERROR_OUT_OF_MEMORY; } } unlockMutex(jf); if( buf ) { /* Try to decompress it */ retval = inflateJarData(jf, je, &lh, buf); } return( retval );}/* * If a jarFile is cached it may be closed before it is used again so we might * have to reopen the file. */static jarFile *delayedOpenJarFile(jarFile *jf){ jarFile *retval = 0; int fd, rc; assert(jf != 0); /* Open the file and check for a different modified time */ if( !(rc = KOPEN(jf->fileName, O_RDONLY|O_BINARY, 0, &fd)) ) {#if !defined(STATIC_JAR_FILES) struct stat jar_stat; if( !(rc = KFSTAT(fd, &jar_stat)) ) { if( jar_stat.st_mtime == jf->lastModified ) {#endif /* !defined(STATIC_JAR_FILES) */#if !defined(KAFFEH) int iLockRoot;#endif /* Only set the fd in the structure here */ lockMutex(jf); if( jf->fd == -1 ) jf->fd = fd; /* We're first */ else KCLOSE(fd); /* Someone else set it */ unlockMutex(jf); retval = jf;#if !defined(STATIC_JAR_FILES) } else { KCLOSE(fd); /* The file was changed, reopen it. */ removeJarFile(jf); retval = openJarFile(jf->fileName); } } else { KCLOSE(fd); }#endif /* !defined(STATIC_JAR_FILES) */ } /* * The file either disappeared, or a new one was made, get rid of the * old one. */ if( retval != jf ) { removeJarFile(jf); closeJarFile(jf); } return( retval );}jarFile *openJarFile(char *name){ jarFile *retval = 0; assert(name != 0); /* Look for it in the cache first */ if( (retval = findJarFile(name)) ) { /* Check if we need to reopen the file */ if( (retval->fd == -1)#ifdef HAVE_MMAP && (retval->data == MAP_FAILED)#endif ) { retval = delayedOpenJarFile(retval); } } /* * If a cached file wasn't found or its broken then try to make a new * one. */ if( !retval && (retval = (jarFile *)gc_malloc(sizeof(jarFile) + strlen(name) + 1, GC_ALLOC_JAR)) ) { int rc, error = 0; retval->fileName = (char *)(retval + 1); strcpy(retval->fileName, name); retval->users = 1; retval->lastModified = 0; retval->count = 0; retval->error = 0; retval->fd = -1; retval->table = 0; retval->tableSize = 0;#ifdef HAVE_MMAP retval->data = MAP_FAILED;#endif /* HAVE_MMAP */ if( (rc = KOPEN(name, O_RDONLY|O_BINARY, 0, &retval->fd)) ) { /* Error opening the file */ error = 1; } else { struct stat jar_stat; if( KFSTAT(retval->fd, &jar_stat) == 0 ) { if( (jar_stat.st_mode & S_IFDIR) ) { /* Its a directory! */ error = 1; } else { retval->lastModified = jar_stat.st_mtime;#ifdef HAVE_MMAP /* * Setup the mmap members in the * jarFile structure */ retval->size = jar_stat.st_size; retval->data = mmap(NULL, retval->size, PROT_READ, MAP_SHARED, retval->fd, 0); if( retval->data == MAP_FAILED ) { /* * Unsuccessful mmap, fallback * to the FD for now. */ } else { /* * Successful mmap, close the * FD */ KCLOSE(retval->fd); retval->fd = -1; retval->offset = 0; }#endif } } else { error = 1; } /* * If everythings a go, figure out how many directory * entries there are and read them in. */ if( !error && !readJarEntries(retval) ) { error = 1; } } if( !error ) { addToCounter(&jarmem, "vmmem-jar files", 1, GCSIZEOF(retval)); if( retval->table ) { addToCounter(&jarmem, "vmmem-jar files", 1, GCSIZEOF(retval->table)); } /* * No errors, so we cache the file. If someone else * beat us to the cache we'll use theirs instead. */ retval = cacheJarFile(retval); } else { /* Something went wrong, cleanup our mess */ retval->users = 0; collectJarFile(retval); retval = 0; } } return( retval );}void closeJarFile(jarFile *jf){ if( jf ) {#if !defined(KAFFEH) int iLockRoot;#endif lockStaticMutex(&jarCache.lock); jf->users--; if( jf->users == 0 ) { if( jarCache.count <= JAR_FILE_CACHE_MAX ) { /* * The cache isn't full so if its in there * we can leave it. */#ifdef HAVE_MMAP if( jf->data != MAP_FAILED ) { /* * Unmap the object and force FD I/O * from now on. */ munmap(jf->data, jf->size); jf->data = MAP_FAILED; } else#endif { KCLOSE(jf->fd); } jf->fd = -1; } else { /* Too many files in the cache, remove it */ removeJarFile(jf); } if( !(jf->flags & JFF_CACHED) ) { /* * Not cached anymore and this was the last * reference so collect the file. */ collectJarFile(jf); } } unlockStaticMutex(&jarCache.lock); }}jarEntry *lookupJarFile(jarFile *jf, char *entry_name){ jarEntry *retval = 0; assert(jf != 0); assert(entry_name != 0); /* * No need to visit the kernel here since we're just walking the * structure. */ if( jf->tableSize ) { unsigned int hash; jarEntry *je; hash = hashName(entry_name); hash = hash % jf->tableSize; je = jf->table[hash]; while( je && !retval ) { if( !strcmp(je->fileName, entry_name) ) retval = je; je = je->next; } } return( retval );}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -