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