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);
83 return true;
84}
85
86off_t _vfzSeek(struct VFile* vf, off_t offset, int whence) {
87 struct VFileZip* vfz = (struct VFileZip*) vf;
88
89 size_t position;
90 switch (whence) {
91 case SEEK_SET:
92 position = offset;
93 break;
94 case SEEK_CUR:
95 if (offset < 0 && ((vfz->offset < (size_t) -offset) || (offset == INT_MIN))) {
96 return -1;
97 }
98 position = vfz->offset + offset;
99 break;
100 case SEEK_END:
101 if (offset < 0 && ((vfz->fileSize < (size_t) -offset) || (offset == INT_MIN))) {
102 return -1;
103 }
104 position = vfz->fileSize + offset;
105 break;
106 }
107
108 if (position <= vfz->offset) {
109 vfz->offset = position;
110 return position;
111 }
112
113 if (position <= vfz->fileSize) {
114 ssize_t read = vf->read(vf, 0, position - vfz->offset);
115 if (read < 0) {
116 return -1;
117 }
118 return vfz->offset;
119 }
120
121 return -1;
122}
123
124ssize_t _vfzRead(struct VFile* vf, void* buffer, size_t size) {
125 struct VFileZip* vfz = (struct VFileZip*) vf;
126
127 size_t bytesRead = 0;
128 if (!vfz->buffer) {
129 vfz->bufferSize = BLOCK_SIZE;
130 vfz->buffer = malloc(BLOCK_SIZE);
131 }
132
133 while (bytesRead < size) {
134 if (vfz->offset < vfz->readSize) {
135 size_t diff = vfz->readSize - vfz->offset;
136 void* start = &((uint8_t*) vfz->buffer)[vfz->offset];
137 if (diff > size - bytesRead) {
138 diff = size - bytesRead;
139 }
140 if (buffer) {
141 void* bufferOffset = &((uint8_t*) buffer)[bytesRead];
142 memcpy(bufferOffset, start, diff);
143 }
144 vfz->offset += diff;
145 bytesRead += diff;
146 if (diff == size) {
147 break;
148 }
149 }
150 // offset == readSize
151 if (vfz->readSize == vfz->bufferSize) {
152 vfz->bufferSize *= 2;
153 if (vfz->bufferSize > vfz->fileSize) {
154 vfz->bufferSize = vfz->fileSize;
155 }
156 vfz->buffer = realloc(vfz->buffer, vfz->bufferSize);
157 }
158 if (vfz->readSize < vfz->bufferSize) {
159 void* start = &((uint8_t*) vfz->buffer)[vfz->readSize];
160 size_t toRead = vfz->bufferSize - vfz->readSize;
161 if (toRead > BLOCK_SIZE) {
162 toRead = BLOCK_SIZE;
163 }
164 ssize_t zipRead = zip_fread(vfz->zf, start, toRead);
165 if (zipRead < 0) {
166 if (bytesRead == 0) {
167 return -1;
168 }
169 break;
170 }
171 if (zipRead == 0) {
172 break;
173 }
174 vfz->readSize += zipRead;
175 } else {
176 break;
177 }
178 }
179 return bytesRead;
180}
181
182ssize_t _vfzReadline(struct VFile* vf, char* buffer, size_t size) {
183 size_t bytesRead = 0;
184 while (bytesRead < size - 1) {
185 size_t newRead = vf->read(vf, &buffer[bytesRead], 1);
186 bytesRead += newRead;
187 if (!newRead || buffer[bytesRead] == '\n') {
188 break;
189 }
190 }
191 return buffer[bytesRead] = '\0';
192}
193
194ssize_t _vfzWrite(struct VFile* vf, void* buffer, size_t size) {
195 // TODO
196 UNUSED(vf);
197 UNUSED(buffer);
198 UNUSED(size);
199 return -1;
200}
201
202void* _vfzMap(struct VFile* vf, size_t size, int flags) {
203 struct VFileZip* vfz = (struct VFileZip*) vf;
204
205 UNUSED(flags);
206 if (size > vfz->readSize) {
207 vf->read(vf, 0, size - vfz->readSize);
208 }
209 return vfz->buffer;
210}
211
212void _vfzUnmap(struct VFile* vf, void* memory, size_t size) {
213 UNUSED(vf);
214 UNUSED(memory);
215 UNUSED(size);
216}
217
218void _vfzTruncate(struct VFile* vf, size_t size) {
219 // TODO
220 UNUSED(vf);
221 UNUSED(size);
222}
223
224bool _vdzClose(struct VDir* vd) {
225 struct VDirZip* vdz = (struct VDirZip*) vd;
226 if (zip_close(vdz->z) < 0) {
227 return false;
228 }
229 free(vdz);
230 return true;
231}
232
233void _vdzRewind(struct VDir* vd) {
234 struct VDirZip* vdz = (struct VDirZip*) vd;
235 vdz->dirent.index = -1;
236}
237
238struct VDirEntry* _vdzListNext(struct VDir* vd) {
239 struct VDirZip* vdz = (struct VDirZip*) vd;
240 zip_int64_t maxIndex = zip_get_num_entries(vdz->z, 0);
241 if (maxIndex <= vdz->dirent.index + 1) {
242 return 0;
243 }
244 ++vdz->dirent.index;
245 return &vdz->dirent.d;
246}
247
248struct VFile* _vdzOpenFile(struct VDir* vd, const char* path, int mode) {
249 UNUSED(mode);
250 // TODO: support truncating, appending and creating
251
252 struct VDirZip* vdz = (struct VDirZip*) vd;
253 struct zip_stat s;
254 if (zip_stat(vdz->z, path, 0, &s) < 0) {
255 return 0;
256 }
257
258 struct zip_file* zf = zip_fopen(vdz->z, path, 0);
259 if (!zf) {
260 return 0;
261 }
262
263 struct VFileZip* vfz = malloc(sizeof(struct VFileZip));
264 vfz->zf = zf;
265 vfz->buffer = 0;
266 vfz->offset = 0;
267 vfz->bufferSize = 0;
268 vfz->readSize = 0;
269 vfz->fileSize = s.size;
270
271 vfz->d.close = _vfzClose;
272 vfz->d.seek = _vfzSeek;
273 vfz->d.read = _vfzRead;
274 vfz->d.readline = _vfzReadline;
275 vfz->d.write = _vfzWrite;
276 vfz->d.map = _vfzMap;
277 vfz->d.unmap = _vfzUnmap;
278 vfz->d.truncate = _vfzTruncate;
279
280 return &vfz->d;
281}
282
283const char* _vdezName(struct VDirEntry* vde) {
284 struct VDirEntryZip* vdez = (struct VDirEntryZip*) vde;
285 struct zip_stat s;
286 if (zip_stat_index(vdez->z, vdez->index, 0, &s) < 0) {
287 return 0;
288 }
289 return s.name;
290}
291
292#endif