2014-12-03 08:39:06 +00:00
|
|
|
/* Copyright (c) 2013-2014 Jeffrey Pfau
|
|
|
|
*
|
|
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
2014-07-17 09:45:17 +00:00
|
|
|
#include "util/vfs.h"
|
|
|
|
|
2015-03-11 09:17:45 +00:00
|
|
|
#ifdef USE_LIBZIP
|
2014-07-17 09:45:17 +00:00
|
|
|
#include <zip.h>
|
|
|
|
|
|
|
|
|
|
|
|
enum {
|
|
|
|
BLOCK_SIZE = 1024
|
|
|
|
};
|
|
|
|
|
|
|
|
struct VDirEntryZip {
|
|
|
|
struct VDirEntry d;
|
|
|
|
struct zip* z;
|
|
|
|
zip_int64_t index;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct VDirZip {
|
|
|
|
struct VDir d;
|
|
|
|
struct zip* z;
|
|
|
|
struct VDirEntryZip dirent;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct VFileZip {
|
|
|
|
struct VFile d;
|
|
|
|
struct zip_file* zf;
|
|
|
|
void* buffer;
|
|
|
|
size_t offset;
|
|
|
|
size_t bufferSize;
|
|
|
|
size_t readSize;
|
|
|
|
size_t fileSize;
|
|
|
|
};
|
|
|
|
|
|
|
|
static bool _vfzClose(struct VFile* vf);
|
|
|
|
static off_t _vfzSeek(struct VFile* vf, off_t offset, int whence);
|
|
|
|
static ssize_t _vfzRead(struct VFile* vf, void* buffer, size_t size);
|
2014-10-14 10:08:39 +00:00
|
|
|
static ssize_t _vfzWrite(struct VFile* vf, const void* buffer, size_t size);
|
2014-07-17 09:45:17 +00:00
|
|
|
static void* _vfzMap(struct VFile* vf, size_t size, int flags);
|
|
|
|
static void _vfzUnmap(struct VFile* vf, void* memory, size_t size);
|
|
|
|
static void _vfzTruncate(struct VFile* vf, size_t size);
|
2015-01-11 08:35:22 +00:00
|
|
|
static ssize_t _vfzSize(struct VFile* vf);
|
2015-07-07 07:26:31 +00:00
|
|
|
static bool _vfzSync(struct VFile* vf, const void* buffer, size_t size);
|
2014-07-17 09:45:17 +00:00
|
|
|
|
|
|
|
static bool _vdzClose(struct VDir* vd);
|
|
|
|
static void _vdzRewind(struct VDir* vd);
|
|
|
|
static struct VDirEntry* _vdzListNext(struct VDir* vd);
|
|
|
|
static struct VFile* _vdzOpenFile(struct VDir* vd, const char* path, int mode);
|
|
|
|
|
|
|
|
static const char* _vdezName(struct VDirEntry* vde);
|
|
|
|
|
|
|
|
struct VDir* VDirOpenZip(const char* path, int flags) {
|
|
|
|
int zflags = 0;
|
|
|
|
if (flags & O_CREAT) {
|
|
|
|
zflags |= ZIP_CREATE;
|
|
|
|
}
|
|
|
|
if (flags & O_EXCL) {
|
|
|
|
zflags |= ZIP_EXCL;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct zip* z = zip_open(path, zflags, 0);
|
|
|
|
if (!z) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
struct VDirZip* vd = malloc(sizeof(struct VDirZip));
|
|
|
|
|
|
|
|
vd->d.close = _vdzClose;
|
|
|
|
vd->d.rewind = _vdzRewind;
|
|
|
|
vd->d.listNext = _vdzListNext;
|
|
|
|
vd->d.openFile = _vdzOpenFile;
|
|
|
|
vd->z = z;
|
|
|
|
|
|
|
|
vd->dirent.d.name = _vdezName;
|
|
|
|
vd->dirent.index = -1;
|
|
|
|
vd->dirent.z = z;
|
|
|
|
|
|
|
|
return &vd->d;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool _vfzClose(struct VFile* vf) {
|
|
|
|
struct VFileZip* vfz = (struct VFileZip*) vf;
|
|
|
|
if (zip_fclose(vfz->zf) < 0) {
|
|
|
|
return false;
|
|
|
|
}
|
2014-07-18 07:52:22 +00:00
|
|
|
free(vfz->buffer);
|
2014-07-17 09:45:17 +00:00
|
|
|
free(vfz);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
off_t _vfzSeek(struct VFile* vf, off_t offset, int whence) {
|
|
|
|
struct VFileZip* vfz = (struct VFileZip*) vf;
|
|
|
|
|
|
|
|
size_t position;
|
|
|
|
switch (whence) {
|
|
|
|
case SEEK_SET:
|
|
|
|
position = offset;
|
|
|
|
break;
|
|
|
|
case SEEK_CUR:
|
|
|
|
if (offset < 0 && ((vfz->offset < (size_t) -offset) || (offset == INT_MIN))) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
position = vfz->offset + offset;
|
|
|
|
break;
|
|
|
|
case SEEK_END:
|
|
|
|
if (offset < 0 && ((vfz->fileSize < (size_t) -offset) || (offset == INT_MIN))) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
position = vfz->fileSize + offset;
|
|
|
|
break;
|
2014-07-25 10:45:54 +00:00
|
|
|
default:
|
|
|
|
return -1;
|
2014-07-17 09:45:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (position <= vfz->offset) {
|
|
|
|
vfz->offset = position;
|
|
|
|
return position;
|
|
|
|
}
|
|
|
|
|
2014-07-17 09:51:17 +00:00
|
|
|
if (position <= vfz->fileSize) {
|
2014-07-17 09:45:17 +00:00
|
|
|
ssize_t read = vf->read(vf, 0, position - vfz->offset);
|
|
|
|
if (read < 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return vfz->offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ssize_t _vfzRead(struct VFile* vf, void* buffer, size_t size) {
|
|
|
|
struct VFileZip* vfz = (struct VFileZip*) vf;
|
|
|
|
|
|
|
|
size_t bytesRead = 0;
|
|
|
|
if (!vfz->buffer) {
|
|
|
|
vfz->bufferSize = BLOCK_SIZE;
|
|
|
|
vfz->buffer = malloc(BLOCK_SIZE);
|
|
|
|
}
|
|
|
|
|
|
|
|
while (bytesRead < size) {
|
|
|
|
if (vfz->offset < vfz->readSize) {
|
|
|
|
size_t diff = vfz->readSize - vfz->offset;
|
|
|
|
void* start = &((uint8_t*) vfz->buffer)[vfz->offset];
|
2014-07-17 09:51:38 +00:00
|
|
|
if (diff > size - bytesRead) {
|
|
|
|
diff = size - bytesRead;
|
2014-07-17 09:45:17 +00:00
|
|
|
}
|
|
|
|
if (buffer) {
|
|
|
|
void* bufferOffset = &((uint8_t*) buffer)[bytesRead];
|
|
|
|
memcpy(bufferOffset, start, diff);
|
|
|
|
}
|
|
|
|
vfz->offset += diff;
|
|
|
|
bytesRead += diff;
|
|
|
|
if (diff == size) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// offset == readSize
|
|
|
|
if (vfz->readSize == vfz->bufferSize) {
|
|
|
|
vfz->bufferSize *= 2;
|
|
|
|
if (vfz->bufferSize > vfz->fileSize) {
|
|
|
|
vfz->bufferSize = vfz->fileSize;
|
|
|
|
}
|
|
|
|
vfz->buffer = realloc(vfz->buffer, vfz->bufferSize);
|
|
|
|
}
|
|
|
|
if (vfz->readSize < vfz->bufferSize) {
|
|
|
|
void* start = &((uint8_t*) vfz->buffer)[vfz->readSize];
|
|
|
|
size_t toRead = vfz->bufferSize - vfz->readSize;
|
|
|
|
if (toRead > BLOCK_SIZE) {
|
|
|
|
toRead = BLOCK_SIZE;
|
|
|
|
}
|
|
|
|
ssize_t zipRead = zip_fread(vfz->zf, start, toRead);
|
|
|
|
if (zipRead < 0) {
|
|
|
|
if (bytesRead == 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (zipRead == 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
vfz->readSize += zipRead;
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return bytesRead;
|
|
|
|
}
|
|
|
|
|
2014-10-14 10:08:39 +00:00
|
|
|
ssize_t _vfzWrite(struct VFile* vf, const void* buffer, size_t size) {
|
2014-07-17 09:45:17 +00:00
|
|
|
// TODO
|
|
|
|
UNUSED(vf);
|
|
|
|
UNUSED(buffer);
|
|
|
|
UNUSED(size);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void* _vfzMap(struct VFile* vf, size_t size, int flags) {
|
|
|
|
struct VFileZip* vfz = (struct VFileZip*) vf;
|
|
|
|
|
|
|
|
UNUSED(flags);
|
|
|
|
if (size > vfz->readSize) {
|
|
|
|
vf->read(vf, 0, size - vfz->readSize);
|
|
|
|
}
|
|
|
|
return vfz->buffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
void _vfzUnmap(struct VFile* vf, void* memory, size_t size) {
|
|
|
|
UNUSED(vf);
|
|
|
|
UNUSED(memory);
|
|
|
|
UNUSED(size);
|
|
|
|
}
|
|
|
|
|
|
|
|
void _vfzTruncate(struct VFile* vf, size_t size) {
|
|
|
|
// TODO
|
|
|
|
UNUSED(vf);
|
|
|
|
UNUSED(size);
|
|
|
|
}
|
|
|
|
|
2015-01-11 08:35:22 +00:00
|
|
|
ssize_t _vfzSize(struct VFile* vf) {
|
|
|
|
struct VFileZip* vfz = (struct VFileZip*) vf;
|
|
|
|
return vfz->fileSize;
|
|
|
|
}
|
|
|
|
|
2014-07-17 09:45:17 +00:00
|
|
|
bool _vdzClose(struct VDir* vd) {
|
|
|
|
struct VDirZip* vdz = (struct VDirZip*) vd;
|
|
|
|
if (zip_close(vdz->z) < 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
free(vdz);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void _vdzRewind(struct VDir* vd) {
|
|
|
|
struct VDirZip* vdz = (struct VDirZip*) vd;
|
|
|
|
vdz->dirent.index = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct VDirEntry* _vdzListNext(struct VDir* vd) {
|
|
|
|
struct VDirZip* vdz = (struct VDirZip*) vd;
|
|
|
|
zip_int64_t maxIndex = zip_get_num_entries(vdz->z, 0);
|
|
|
|
if (maxIndex <= vdz->dirent.index + 1) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
++vdz->dirent.index;
|
|
|
|
return &vdz->dirent.d;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct VFile* _vdzOpenFile(struct VDir* vd, const char* path, int mode) {
|
|
|
|
UNUSED(mode);
|
2014-07-18 07:52:07 +00:00
|
|
|
// TODO: support truncating, appending and creating, and write
|
2014-07-17 09:45:17 +00:00
|
|
|
struct VDirZip* vdz = (struct VDirZip*) vd;
|
2014-07-18 07:52:07 +00:00
|
|
|
|
|
|
|
if ((mode & O_RDWR) == O_RDWR) {
|
|
|
|
// libzip doesn't allow for random access, so read/write is impossible without
|
|
|
|
// reading the entire file first. This approach will be supported eventually.
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mode & O_WRONLY) {
|
|
|
|
// Write support is not yet implemented.
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-07-17 09:45:17 +00:00
|
|
|
struct zip_stat s;
|
|
|
|
if (zip_stat(vdz->z, path, 0, &s) < 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct zip_file* zf = zip_fopen(vdz->z, path, 0);
|
|
|
|
if (!zf) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct VFileZip* vfz = malloc(sizeof(struct VFileZip));
|
|
|
|
vfz->zf = zf;
|
|
|
|
vfz->buffer = 0;
|
|
|
|
vfz->offset = 0;
|
|
|
|
vfz->bufferSize = 0;
|
|
|
|
vfz->readSize = 0;
|
|
|
|
vfz->fileSize = s.size;
|
|
|
|
|
|
|
|
vfz->d.close = _vfzClose;
|
|
|
|
vfz->d.seek = _vfzSeek;
|
|
|
|
vfz->d.read = _vfzRead;
|
2015-03-06 02:40:33 +00:00
|
|
|
vfz->d.readline = VFileReadline;
|
2014-07-17 09:45:17 +00:00
|
|
|
vfz->d.write = _vfzWrite;
|
|
|
|
vfz->d.map = _vfzMap;
|
|
|
|
vfz->d.unmap = _vfzUnmap;
|
|
|
|
vfz->d.truncate = _vfzTruncate;
|
2015-01-11 08:35:22 +00:00
|
|
|
vfz->d.size = _vfzSize;
|
2015-07-07 07:26:31 +00:00
|
|
|
vfz->d.sync = _vfzSync;
|
2014-07-17 09:45:17 +00:00
|
|
|
|
|
|
|
return &vfz->d;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* _vdezName(struct VDirEntry* vde) {
|
|
|
|
struct VDirEntryZip* vdez = (struct VDirEntryZip*) vde;
|
|
|
|
struct zip_stat s;
|
|
|
|
if (zip_stat_index(vdez->z, vdez->index, 0, &s) < 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return s.name;
|
|
|
|
}
|
|
|
|
|
2015-07-07 07:26:31 +00:00
|
|
|
bool _vfzSync(struct VFile* vf, const void* memory, size_t size) {
|
|
|
|
UNUSED(vf);
|
|
|
|
UNUSED(memory);
|
|
|
|
UNUSED(size);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-07-17 09:45:17 +00:00
|
|
|
#endif
|