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);
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 _vfzWrite(struct VFile* vf, const void* buffer, size_t size) {
192 // TODO
193 UNUSED(vf);
194 UNUSED(buffer);
195 UNUSED(size);
196 return -1;
197}
198
199void* _vfzMap(struct VFile* vf, size_t size, int flags) {
200 struct VFileZip* vfz = (struct VFileZip*) vf;
201
202 UNUSED(flags);
203 if (size > vfz->readSize) {
204 vf->read(vf, 0, size - vfz->readSize);
205 }
206 return vfz->buffer;
207}
208
209void _vfzUnmap(struct VFile* vf, void* memory, size_t size) {
210 UNUSED(vf);
211 UNUSED(memory);
212 UNUSED(size);
213}
214
215void _vfzTruncate(struct VFile* vf, size_t size) {
216 // TODO
217 UNUSED(vf);
218 UNUSED(size);
219}
220
221ssize_t _vfzSize(struct VFile* vf) {
222 struct VFileZip* vfz = (struct VFileZip*) vf;
223 return vfz->fileSize;
224}
225
226bool _vdzClose(struct VDir* vd) {
227 struct VDirZip* vdz = (struct VDirZip*) vd;
228 if (zip_close(vdz->z) < 0) {
229 return false;
230 }
231 free(vdz);
232 return true;
233}
234
235void _vdzRewind(struct VDir* vd) {
236 struct VDirZip* vdz = (struct VDirZip*) vd;
237 vdz->dirent.index = -1;
238}
239
240struct VDirEntry* _vdzListNext(struct VDir* vd) {
241 struct VDirZip* vdz = (struct VDirZip*) vd;
242 zip_int64_t maxIndex = zip_get_num_entries(vdz->z, 0);
243 if (maxIndex <= vdz->dirent.index + 1) {
244 return 0;
245 }
246 ++vdz->dirent.index;
247 return &vdz->dirent.d;
248}
249
250struct VFile* _vdzOpenFile(struct VDir* vd, const char* path, int mode) {
251 UNUSED(mode);
252 // TODO: support truncating, appending and creating, and write
253 struct VDirZip* vdz = (struct VDirZip*) vd;
254
255 if ((mode & O_RDWR) == O_RDWR) {
256 // libzip doesn't allow for random access, so read/write is impossible without
257 // reading the entire file first. This approach will be supported eventually.
258 return 0;
259 }
260
261 if (mode & O_WRONLY) {
262 // Write support is not yet implemented.
263 return 0;
264 }
265
266 struct zip_stat s;
267 if (zip_stat(vdz->z, path, 0, &s) < 0) {
268 return 0;
269 }
270
271 struct zip_file* zf = zip_fopen(vdz->z, path, 0);
272 if (!zf) {
273 return 0;
274 }
275
276 struct VFileZip* vfz = malloc(sizeof(struct VFileZip));
277 vfz->zf = zf;
278 vfz->buffer = 0;
279 vfz->offset = 0;
280 vfz->bufferSize = 0;
281 vfz->readSize = 0;
282 vfz->fileSize = s.size;
283
284 vfz->d.close = _vfzClose;
285 vfz->d.seek = _vfzSeek;
286 vfz->d.read = _vfzRead;
287 vfz->d.readline = VFileReadline;
288 vfz->d.write = _vfzWrite;
289 vfz->d.map = _vfzMap;
290 vfz->d.unmap = _vfzUnmap;
291 vfz->d.truncate = _vfzTruncate;
292 vfz->d.size = _vfzSize;
293 vfz->d.sync = _vfzSync;
294
295 return &vfz->d;
296}
297
298const char* _vdezName(struct VDirEntry* vde) {
299 struct VDirEntryZip* vdez = (struct VDirEntryZip*) vde;
300 struct zip_stat s;
301 if (zip_stat_index(vdez->z, vdez->index, 0, &s) < 0) {
302 return 0;
303 }
304 return s.name;
305}
306
307bool _vfzSync(struct VFile* vf, const void* memory, size_t size) {
308 UNUSED(vf);
309 UNUSED(memory);
310 UNUSED(size);
311 return false;
312}
313
314#endif