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