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 ENABLE_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 _vfzReadline(struct VFile* vf, char* buffer, size_t size);
42static ssize_t _vfzWrite(struct VFile* vf, const void* buffer, size_t size);
43static void* _vfzMap(struct VFile* vf, size_t size, int flags);
44static void _vfzUnmap(struct VFile* vf, void* memory, size_t size);
45static void _vfzTruncate(struct VFile* vf, size_t size);
46static ssize_t _vfzSize(struct VFile* vf);
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);
52
53static const char* _vdezName(struct VDirEntry* vde);
54
55struct VDir* VDirOpenZip(const char* path, int flags) {
56 int zflags = 0;
57 if (flags & O_CREAT) {
58 zflags |= ZIP_CREATE;
59 }
60 if (flags & O_EXCL) {
61 zflags |= ZIP_EXCL;
62 }
63
64 struct zip* z = zip_open(path, zflags, 0);
65 if (!z) {
66 return 0;
67 }
68 struct VDirZip* vd = malloc(sizeof(struct VDirZip));
69
70 vd->d.close = _vdzClose;
71 vd->d.rewind = _vdzRewind;
72 vd->d.listNext = _vdzListNext;
73 vd->d.openFile = _vdzOpenFile;
74 vd->z = z;
75
76 vd->dirent.d.name = _vdezName;
77 vd->dirent.index = -1;
78 vd->dirent.z = z;
79
80 return &vd->d;
81}
82
83bool _vfzClose(struct VFile* vf) {
84 struct VFileZip* vfz = (struct VFileZip*) vf;
85 if (zip_fclose(vfz->zf) < 0) {
86 return false;
87 }
88 free(vfz->buffer);
89 free(vfz);
90 return true;
91}
92
93off_t _vfzSeek(struct VFile* vf, off_t offset, int whence) {
94 struct VFileZip* vfz = (struct VFileZip*) vf;
95
96 size_t position;
97 switch (whence) {
98 case SEEK_SET:
99 position = offset;
100 break;
101 case SEEK_CUR:
102 if (offset < 0 && ((vfz->offset < (size_t) -offset) || (offset == INT_MIN))) {
103 return -1;
104 }
105 position = vfz->offset + offset;
106 break;
107 case SEEK_END:
108 if (offset < 0 && ((vfz->fileSize < (size_t) -offset) || (offset == INT_MIN))) {
109 return -1;
110 }
111 position = vfz->fileSize + offset;
112 break;
113 default:
114 return -1;
115 }
116
117 if (position <= vfz->offset) {
118 vfz->offset = position;
119 return position;
120 }
121
122 if (position <= vfz->fileSize) {
123 ssize_t read = vf->read(vf, 0, position - vfz->offset);
124 if (read < 0) {
125 return -1;
126 }
127 return vfz->offset;
128 }
129
130 return -1;
131}
132
133ssize_t _vfzRead(struct VFile* vf, void* buffer, size_t size) {
134 struct VFileZip* vfz = (struct VFileZip*) vf;
135
136 size_t bytesRead = 0;
137 if (!vfz->buffer) {
138 vfz->bufferSize = BLOCK_SIZE;
139 vfz->buffer = malloc(BLOCK_SIZE);
140 }
141
142 while (bytesRead < size) {
143 if (vfz->offset < vfz->readSize) {
144 size_t diff = vfz->readSize - vfz->offset;
145 void* start = &((uint8_t*) vfz->buffer)[vfz->offset];
146 if (diff > size - bytesRead) {
147 diff = size - bytesRead;
148 }
149 if (buffer) {
150 void* bufferOffset = &((uint8_t*) buffer)[bytesRead];
151 memcpy(bufferOffset, start, diff);
152 }
153 vfz->offset += diff;
154 bytesRead += diff;
155 if (diff == size) {
156 break;
157 }
158 }
159 // offset == readSize
160 if (vfz->readSize == vfz->bufferSize) {
161 vfz->bufferSize *= 2;
162 if (vfz->bufferSize > vfz->fileSize) {
163 vfz->bufferSize = vfz->fileSize;
164 }
165 vfz->buffer = realloc(vfz->buffer, vfz->bufferSize);
166 }
167 if (vfz->readSize < vfz->bufferSize) {
168 void* start = &((uint8_t*) vfz->buffer)[vfz->readSize];
169 size_t toRead = vfz->bufferSize - vfz->readSize;
170 if (toRead > BLOCK_SIZE) {
171 toRead = BLOCK_SIZE;
172 }
173 ssize_t zipRead = zip_fread(vfz->zf, start, toRead);
174 if (zipRead < 0) {
175 if (bytesRead == 0) {
176 return -1;
177 }
178 break;
179 }
180 if (zipRead == 0) {
181 break;
182 }
183 vfz->readSize += zipRead;
184 } else {
185 break;
186 }
187 }
188 return bytesRead;
189}
190
191ssize_t _vfzReadline(struct VFile* vf, char* buffer, size_t size) {
192 size_t bytesRead = 0;
193 while (bytesRead < size - 1) {
194 size_t newRead = vf->read(vf, &buffer[bytesRead], 1);
195 bytesRead += newRead;
196 if (!newRead || buffer[bytesRead] == '\n') {
197 break;
198 }
199 }
200 return buffer[bytesRead] = '\0';
201}
202
203ssize_t _vfzWrite(struct VFile* vf, const void* buffer, size_t size) {
204 // TODO
205 UNUSED(vf);
206 UNUSED(buffer);
207 UNUSED(size);
208 return -1;
209}
210
211void* _vfzMap(struct VFile* vf, size_t size, int flags) {
212 struct VFileZip* vfz = (struct VFileZip*) vf;
213
214 UNUSED(flags);
215 if (size > vfz->readSize) {
216 vf->read(vf, 0, size - vfz->readSize);
217 }
218 return vfz->buffer;
219}
220
221void _vfzUnmap(struct VFile* vf, void* memory, size_t size) {
222 UNUSED(vf);
223 UNUSED(memory);
224 UNUSED(size);
225}
226
227void _vfzTruncate(struct VFile* vf, size_t size) {
228 // TODO
229 UNUSED(vf);
230 UNUSED(size);
231}
232
233ssize_t _vfzSize(struct VFile* vf) {
234 struct VFileZip* vfz = (struct VFileZip*) vf;
235 return vfz->fileSize;
236}
237
238bool _vdzClose(struct VDir* vd) {
239 struct VDirZip* vdz = (struct VDirZip*) vd;
240 if (zip_close(vdz->z) < 0) {
241 return false;
242 }
243 free(vdz);
244 return true;
245}
246
247void _vdzRewind(struct VDir* vd) {
248 struct VDirZip* vdz = (struct VDirZip*) vd;
249 vdz->dirent.index = -1;
250}
251
252struct VDirEntry* _vdzListNext(struct VDir* vd) {
253 struct VDirZip* vdz = (struct VDirZip*) vd;
254 zip_int64_t maxIndex = zip_get_num_entries(vdz->z, 0);
255 if (maxIndex <= vdz->dirent.index + 1) {
256 return 0;
257 }
258 ++vdz->dirent.index;
259 return &vdz->dirent.d;
260}
261
262struct VFile* _vdzOpenFile(struct VDir* vd, const char* path, int mode) {
263 UNUSED(mode);
264 // TODO: support truncating, appending and creating, and write
265 struct VDirZip* vdz = (struct VDirZip*) vd;
266
267 if ((mode & O_RDWR) == O_RDWR) {
268 // libzip doesn't allow for random access, so read/write is impossible without
269 // reading the entire file first. This approach will be supported eventually.
270 return 0;
271 }
272
273 if (mode & O_WRONLY) {
274 // Write support is not yet implemented.
275 return 0;
276 }
277
278 struct zip_stat s;
279 if (zip_stat(vdz->z, path, 0, &s) < 0) {
280 return 0;
281 }
282
283 struct zip_file* zf = zip_fopen(vdz->z, path, 0);
284 if (!zf) {
285 return 0;
286 }
287
288 struct VFileZip* vfz = malloc(sizeof(struct VFileZip));
289 vfz->zf = zf;
290 vfz->buffer = 0;
291 vfz->offset = 0;
292 vfz->bufferSize = 0;
293 vfz->readSize = 0;
294 vfz->fileSize = s.size;
295
296 vfz->d.close = _vfzClose;
297 vfz->d.seek = _vfzSeek;
298 vfz->d.read = _vfzRead;
299 vfz->d.readline = _vfzReadline;
300 vfz->d.write = _vfzWrite;
301 vfz->d.map = _vfzMap;
302 vfz->d.unmap = _vfzUnmap;
303 vfz->d.truncate = _vfzTruncate;
304 vfz->d.size = _vfzSize;
305
306 return &vfz->d;
307}
308
309const char* _vdezName(struct VDirEntry* vde) {
310 struct VDirEntryZip* vdez = (struct VDirEntryZip*) vde;
311 struct zip_stat s;
312 if (zip_stat_index(vdez->z, vdez->index, 0, &s) < 0) {
313 return 0;
314 }
315 return s.name;
316}
317
318#endif