src/util/vfs/vfs-zip.c (view raw)
1/* Copyright (c) 2013-2014 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include "util/vfs.h"
7
8#ifdef USE_LIBZIP
9#include <zip.h>
10
11
12enum {
13 BLOCK_SIZE = 1024
14};
15
16struct VDirEntryZip {
17 struct VDirEntry d;
18 struct zip* z;
19 zip_int64_t index;
20};
21
22struct VDirZip {
23 struct VDir d;
24 struct zip* z;
25 struct VDirEntryZip dirent;
26};
27
28struct VFileZip {
29 struct VFile d;
30 struct zip_file* zf;
31 void* buffer;
32 size_t offset;
33 size_t bufferSize;
34 size_t readSize;
35 size_t fileSize;
36};
37
38static bool _vfzClose(struct VFile* vf);
39static off_t _vfzSeek(struct VFile* vf, off_t offset, int whence);
40static ssize_t _vfzRead(struct VFile* vf, void* buffer, size_t size);
41static ssize_t _vfzWrite(struct VFile* vf, const void* buffer, size_t size);
42static void* _vfzMap(struct VFile* vf, size_t size, int flags);
43static void _vfzUnmap(struct VFile* vf, void* memory, size_t size);
44static void _vfzTruncate(struct VFile* vf, size_t size);
45static ssize_t _vfzSize(struct VFile* vf);
46static bool _vfzSync(struct VFile* vf, const void* buffer, size_t size);
47
48static bool _vdzClose(struct VDir* vd);
49static void _vdzRewind(struct VDir* vd);
50static struct VDirEntry* _vdzListNext(struct VDir* vd);
51static struct VFile* _vdzOpenFile(struct VDir* vd, const char* path, int mode);
52static struct VDir* _vdzOpenDir(struct VDir* vd, const char* path);
53
54static const char* _vdezName(struct VDirEntry* vde);
55static enum VFSType _vdezType(struct VDirEntry* vde);
56
57struct VDir* VDirOpenZip(const char* path, int flags) {
58 int zflags = 0;
59 if (flags & O_CREAT) {
60 zflags |= ZIP_CREATE;
61 }
62 if (flags & O_EXCL) {
63 zflags |= ZIP_EXCL;
64 }
65
66 struct zip* z = zip_open(path, zflags, 0);
67 if (!z) {
68 return 0;
69 }
70 struct VDirZip* vd = malloc(sizeof(struct VDirZip));
71
72 vd->d.close = _vdzClose;
73 vd->d.rewind = _vdzRewind;
74 vd->d.listNext = _vdzListNext;
75 vd->d.openFile = _vdzOpenFile;
76 vd->d.openDir = _vdzOpenDir;
77 vd->z = z;
78
79 vd->dirent.d.name = _vdezName;
80 vd->dirent.d.type = _vdezType;
81 vd->dirent.index = -1;
82 vd->dirent.z = z;
83
84 return &vd->d;
85}
86
87bool _vfzClose(struct VFile* vf) {
88 struct VFileZip* vfz = (struct VFileZip*) vf;
89 if (zip_fclose(vfz->zf) < 0) {
90 return false;
91 }
92 free(vfz->buffer);
93 free(vfz);
94 return true;
95}
96
97off_t _vfzSeek(struct VFile* vf, off_t offset, int whence) {
98 struct VFileZip* vfz = (struct VFileZip*) vf;
99
100 size_t position;
101 switch (whence) {
102 case SEEK_SET:
103 position = offset;
104 break;
105 case SEEK_CUR:
106 if (offset < 0 && ((vfz->offset < (size_t) -offset) || (offset == INT_MIN))) {
107 return -1;
108 }
109 position = vfz->offset + offset;
110 break;
111 case SEEK_END:
112 if (offset < 0 && ((vfz->fileSize < (size_t) -offset) || (offset == INT_MIN))) {
113 return -1;
114 }
115 position = vfz->fileSize + offset;
116 break;
117 default:
118 return -1;
119 }
120
121 if (position <= vfz->offset) {
122 vfz->offset = position;
123 return position;
124 }
125
126 if (position <= vfz->fileSize) {
127 ssize_t read = vf->read(vf, 0, position - vfz->offset);
128 if (read < 0) {
129 return -1;
130 }
131 return vfz->offset;
132 }
133
134 return -1;
135}
136
137ssize_t _vfzRead(struct VFile* vf, void* buffer, size_t size) {
138 struct VFileZip* vfz = (struct VFileZip*) vf;
139
140 size_t bytesRead = 0;
141 if (!vfz->buffer) {
142 vfz->bufferSize = BLOCK_SIZE;
143 vfz->buffer = malloc(BLOCK_SIZE);
144 }
145
146 while (bytesRead < size) {
147 if (vfz->offset < vfz->readSize) {
148 size_t diff = vfz->readSize - vfz->offset;
149 void* start = &((uint8_t*) vfz->buffer)[vfz->offset];
150 if (diff > size - bytesRead) {
151 diff = size - bytesRead;
152 }
153 if (buffer) {
154 void* bufferOffset = &((uint8_t*) buffer)[bytesRead];
155 memcpy(bufferOffset, start, diff);
156 }
157 vfz->offset += diff;
158 bytesRead += diff;
159 if (diff == size) {
160 break;
161 }
162 }
163 // offset == readSize
164 if (vfz->readSize == vfz->bufferSize) {
165 vfz->bufferSize *= 2;
166 if (vfz->bufferSize > vfz->fileSize) {
167 vfz->bufferSize = vfz->fileSize;
168 }
169 vfz->buffer = realloc(vfz->buffer, vfz->bufferSize);
170 }
171 if (vfz->readSize < vfz->bufferSize) {
172 void* start = &((uint8_t*) vfz->buffer)[vfz->readSize];
173 size_t toRead = vfz->bufferSize - vfz->readSize;
174 if (toRead > BLOCK_SIZE) {
175 toRead = BLOCK_SIZE;
176 }
177 ssize_t zipRead = zip_fread(vfz->zf, start, toRead);
178 if (zipRead < 0) {
179 if (bytesRead == 0) {
180 return -1;
181 }
182 break;
183 }
184 if (zipRead == 0) {
185 break;
186 }
187 vfz->readSize += zipRead;
188 } else {
189 break;
190 }
191 }
192 return bytesRead;
193}
194
195ssize_t _vfzWrite(struct VFile* vf, const void* buffer, size_t size) {
196 // TODO
197 UNUSED(vf);
198 UNUSED(buffer);
199 UNUSED(size);
200 return -1;
201}
202
203void* _vfzMap(struct VFile* vf, size_t size, int flags) {
204 struct VFileZip* vfz = (struct VFileZip*) vf;
205
206 UNUSED(flags);
207 if (size > vfz->readSize) {
208 vf->read(vf, 0, size - vfz->readSize);
209 }
210 return vfz->buffer;
211}
212
213void _vfzUnmap(struct VFile* vf, void* memory, size_t size) {
214 UNUSED(vf);
215 UNUSED(memory);
216 UNUSED(size);
217}
218
219void _vfzTruncate(struct VFile* vf, size_t size) {
220 // TODO
221 UNUSED(vf);
222 UNUSED(size);
223}
224
225ssize_t _vfzSize(struct VFile* vf) {
226 struct VFileZip* vfz = (struct VFileZip*) vf;
227 return vfz->fileSize;
228}
229
230bool _vdzClose(struct VDir* vd) {
231 struct VDirZip* vdz = (struct VDirZip*) vd;
232 if (zip_close(vdz->z) < 0) {
233 return false;
234 }
235 free(vdz);
236 return true;
237}
238
239void _vdzRewind(struct VDir* vd) {
240 struct VDirZip* vdz = (struct VDirZip*) vd;
241 vdz->dirent.index = -1;
242}
243
244struct VDirEntry* _vdzListNext(struct VDir* vd) {
245 struct VDirZip* vdz = (struct VDirZip*) vd;
246 zip_int64_t maxIndex = zip_get_num_entries(vdz->z, 0);
247 if (maxIndex <= vdz->dirent.index + 1) {
248 return 0;
249 }
250 ++vdz->dirent.index;
251 return &vdz->dirent.d;
252}
253
254struct VFile* _vdzOpenFile(struct VDir* vd, const char* path, int mode) {
255 UNUSED(mode);
256 // TODO: support truncating, appending and creating, and write
257 struct VDirZip* vdz = (struct VDirZip*) vd;
258
259 if ((mode & O_RDWR) == O_RDWR) {
260 // libzip doesn't allow for random access, so read/write is impossible without
261 // reading the entire file first. This approach will be supported eventually.
262 return 0;
263 }
264
265 if (mode & O_WRONLY) {
266 // Write support is not yet implemented.
267 return 0;
268 }
269
270 struct zip_stat s;
271 if (zip_stat(vdz->z, path, 0, &s) < 0) {
272 return 0;
273 }
274
275 struct zip_file* zf = zip_fopen(vdz->z, path, 0);
276 if (!zf) {
277 return 0;
278 }
279
280 struct VFileZip* vfz = malloc(sizeof(struct VFileZip));
281 vfz->zf = zf;
282 vfz->buffer = 0;
283 vfz->offset = 0;
284 vfz->bufferSize = 0;
285 vfz->readSize = 0;
286 vfz->fileSize = s.size;
287
288 vfz->d.close = _vfzClose;
289 vfz->d.seek = _vfzSeek;
290 vfz->d.read = _vfzRead;
291 vfz->d.readline = VFileReadline;
292 vfz->d.write = _vfzWrite;
293 vfz->d.map = _vfzMap;
294 vfz->d.unmap = _vfzUnmap;
295 vfz->d.truncate = _vfzTruncate;
296 vfz->d.size = _vfzSize;
297 vfz->d.sync = _vfzSync;
298
299 return &vfz->d;
300}
301
302struct VDir* _vdzOpenDir(struct VDir* vd, const char* path) {
303 UNUSED(vd);
304 UNUSED(path);
305 return 0;
306}
307
308bool _vfzSync(struct VFile* vf, const void* memory, size_t size) {
309 UNUSED(vf);
310 UNUSED(memory);
311 UNUSED(size);
312 return false;
313}
314
315const char* _vdezName(struct VDirEntry* vde) {
316 struct VDirEntryZip* vdez = (struct VDirEntryZip*) vde;
317 struct zip_stat s;
318 if (zip_stat_index(vdez->z, vdez->index, 0, &s) < 0) {
319 return 0;
320 }
321 return s.name;
322}
323
324static enum VFSType _vdezType(struct VDirEntry* vde) {
325 struct VDirEntryZip* vdez = (struct VDirEntryZip*) vde;
326 UNUSED(vdez);
327 return VFS_UNKNOWN;
328}
329
330#endif