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