src/util/vfs-zip.c (view raw)
1#include "util/vfs.h"
2
3#ifdef ENABLE_LIBZIP
4#include <zip.h>
5
6
7enum {
8 BLOCK_SIZE = 1024
9};
10
11struct VDirEntryZip {
12 struct VDirEntry d;
13 struct zip* z;
14 zip_int64_t index;
15};
16
17struct VDirZip {
18 struct VDir d;
19 struct zip* z;
20 struct VDirEntryZip dirent;
21};
22
23struct VFileZip {
24 struct VFile d;
25 struct zip_file* zf;
26 void* buffer;
27 size_t offset;
28 size_t bufferSize;
29 size_t readSize;
30 size_t fileSize;
31};
32
33static bool _vfzClose(struct VFile* vf);
34static off_t _vfzSeek(struct VFile* vf, off_t offset, int whence);
35static ssize_t _vfzRead(struct VFile* vf, void* buffer, size_t size);
36static ssize_t _vfzReadline(struct VFile* vf, char* buffer, size_t size);
37static ssize_t _vfzWrite(struct VFile* vf, void* buffer, size_t size);
38static void* _vfzMap(struct VFile* vf, size_t size, int flags);
39static void _vfzUnmap(struct VFile* vf, void* memory, size_t size);
40static void _vfzTruncate(struct VFile* vf, size_t size);
41
42static bool _vdzClose(struct VDir* vd);
43static void _vdzRewind(struct VDir* vd);
44static struct VDirEntry* _vdzListNext(struct VDir* vd);
45static struct VFile* _vdzOpenFile(struct VDir* vd, const char* path, int mode);
46
47static const char* _vdezName(struct VDirEntry* vde);
48
49struct VDir* VDirOpenZip(const char* path, int flags) {
50 int zflags = 0;
51 if (flags & O_CREAT) {
52 zflags |= ZIP_CREATE;
53 }
54 if (flags & O_EXCL) {
55 zflags |= ZIP_EXCL;
56 }
57
58 struct zip* z = zip_open(path, zflags, 0);
59 if (!z) {
60 return 0;
61 }
62 struct VDirZip* vd = malloc(sizeof(struct VDirZip));
63
64 vd->d.close = _vdzClose;
65 vd->d.rewind = _vdzRewind;
66 vd->d.listNext = _vdzListNext;
67 vd->d.openFile = _vdzOpenFile;
68 vd->z = z;
69
70 vd->dirent.d.name = _vdezName;
71 vd->dirent.index = -1;
72 vd->dirent.z = z;
73
74 return &vd->d;
75}
76
77bool _vfzClose(struct VFile* vf) {
78 struct VFileZip* vfz = (struct VFileZip*) vf;
79 if (zip_fclose(vfz->zf) < 0) {
80 return false;
81 }
82 free(vfz->buffer);
83 free(vfz);
84 return true;
85}
86
87off_t _vfzSeek(struct VFile* vf, off_t offset, int whence) {
88 struct VFileZip* vfz = (struct VFileZip*) vf;
89
90 size_t position;
91 switch (whence) {
92 case SEEK_SET:
93 position = offset;
94 break;
95 case SEEK_CUR:
96 if (offset < 0 && ((vfz->offset < (size_t) -offset) || (offset == INT_MIN))) {
97 return -1;
98 }
99 position = vfz->offset + offset;
100 break;
101 case SEEK_END:
102 if (offset < 0 && ((vfz->fileSize < (size_t) -offset) || (offset == INT_MIN))) {
103 return -1;
104 }
105 position = vfz->fileSize + offset;
106 break;
107 }
108
109 if (position <= vfz->offset) {
110 vfz->offset = position;
111 return position;
112 }
113
114 if (position <= vfz->fileSize) {
115 ssize_t read = vf->read(vf, 0, position - vfz->offset);
116 if (read < 0) {
117 return -1;
118 }
119 return vfz->offset;
120 }
121
122 return -1;
123}
124
125ssize_t _vfzRead(struct VFile* vf, void* buffer, size_t size) {
126 struct VFileZip* vfz = (struct VFileZip*) vf;
127
128 size_t bytesRead = 0;
129 if (!vfz->buffer) {
130 vfz->bufferSize = BLOCK_SIZE;
131 vfz->buffer = malloc(BLOCK_SIZE);
132 }
133
134 while (bytesRead < size) {
135 if (vfz->offset < vfz->readSize) {
136 size_t diff = vfz->readSize - vfz->offset;
137 void* start = &((uint8_t*) vfz->buffer)[vfz->offset];
138 if (diff > size - bytesRead) {
139 diff = size - bytesRead;
140 }
141 if (buffer) {
142 void* bufferOffset = &((uint8_t*) buffer)[bytesRead];
143 memcpy(bufferOffset, start, diff);
144 }
145 vfz->offset += diff;
146 bytesRead += diff;
147 if (diff == size) {
148 break;
149 }
150 }
151 // offset == readSize
152 if (vfz->readSize == vfz->bufferSize) {
153 vfz->bufferSize *= 2;
154 if (vfz->bufferSize > vfz->fileSize) {
155 vfz->bufferSize = vfz->fileSize;
156 }
157 vfz->buffer = realloc(vfz->buffer, vfz->bufferSize);
158 }
159 if (vfz->readSize < vfz->bufferSize) {
160 void* start = &((uint8_t*) vfz->buffer)[vfz->readSize];
161 size_t toRead = vfz->bufferSize - vfz->readSize;
162 if (toRead > BLOCK_SIZE) {
163 toRead = BLOCK_SIZE;
164 }
165 ssize_t zipRead = zip_fread(vfz->zf, start, toRead);
166 if (zipRead < 0) {
167 if (bytesRead == 0) {
168 return -1;
169 }
170 break;
171 }
172 if (zipRead == 0) {
173 break;
174 }
175 vfz->readSize += zipRead;
176 } else {
177 break;
178 }
179 }
180 return bytesRead;
181}
182
183ssize_t _vfzReadline(struct VFile* vf, char* buffer, size_t size) {
184 size_t bytesRead = 0;
185 while (bytesRead < size - 1) {
186 size_t newRead = vf->read(vf, &buffer[bytesRead], 1);
187 bytesRead += newRead;
188 if (!newRead || buffer[bytesRead] == '\n') {
189 break;
190 }
191 }
192 return buffer[bytesRead] = '\0';
193}
194
195ssize_t _vfzWrite(struct VFile* vf, void* buffer, size_t size) {
196 // TODO
197 UNUSED(vf);
198 UNUSED(buffer);
199 UNUSED(size);
200 return -1;
201}
202
203void* _vfzMap(struct VFile* vf, size_t size, int flags) {
204 struct VFileZip* vfz = (struct VFileZip*) vf;
205
206 UNUSED(flags);
207 if (size > vfz->readSize) {
208 vf->read(vf, 0, size - vfz->readSize);
209 }
210 return vfz->buffer;
211}
212
213void _vfzUnmap(struct VFile* vf, void* memory, size_t size) {
214 UNUSED(vf);
215 UNUSED(memory);
216 UNUSED(size);
217}
218
219void _vfzTruncate(struct VFile* vf, size_t size) {
220 // TODO
221 UNUSED(vf);
222 UNUSED(size);
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 = _vfzReadline;
287 vfz->d.write = _vfzWrite;
288 vfz->d.map = _vfzMap;
289 vfz->d.unmap = _vfzUnmap;
290 vfz->d.truncate = _vfzTruncate;
291
292 return &vfz->d;
293}
294
295const char* _vdezName(struct VDirEntry* vde) {
296 struct VDirEntryZip* vdez = (struct VDirEntryZip*) vde;
297 struct zip_stat s;
298 if (zip_stat_index(vdez->z, vdez->index, 0, &s) < 0) {
299 return 0;
300 }
301 return s.name;
302}
303
304#endif