src/util/vfs/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, const 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 default:
108 return -1;
109 }
110
111 if (position <= vfz->offset) {
112 vfz->offset = position;
113 return position;
114 }
115
116 if (position <= vfz->fileSize) {
117 ssize_t read = vf->read(vf, 0, position - vfz->offset);
118 if (read < 0) {
119 return -1;
120 }
121 return vfz->offset;
122 }
123
124 return -1;
125}
126
127ssize_t _vfzRead(struct VFile* vf, void* buffer, size_t size) {
128 struct VFileZip* vfz = (struct VFileZip*) vf;
129
130 size_t bytesRead = 0;
131 if (!vfz->buffer) {
132 vfz->bufferSize = BLOCK_SIZE;
133 vfz->buffer = malloc(BLOCK_SIZE);
134 }
135
136 while (bytesRead < size) {
137 if (vfz->offset < vfz->readSize) {
138 size_t diff = vfz->readSize - vfz->offset;
139 void* start = &((uint8_t*) vfz->buffer)[vfz->offset];
140 if (diff > size - bytesRead) {
141 diff = size - bytesRead;
142 }
143 if (buffer) {
144 void* bufferOffset = &((uint8_t*) buffer)[bytesRead];
145 memcpy(bufferOffset, start, diff);
146 }
147 vfz->offset += diff;
148 bytesRead += diff;
149 if (diff == size) {
150 break;
151 }
152 }
153 // offset == readSize
154 if (vfz->readSize == vfz->bufferSize) {
155 vfz->bufferSize *= 2;
156 if (vfz->bufferSize > vfz->fileSize) {
157 vfz->bufferSize = vfz->fileSize;
158 }
159 vfz->buffer = realloc(vfz->buffer, vfz->bufferSize);
160 }
161 if (vfz->readSize < vfz->bufferSize) {
162 void* start = &((uint8_t*) vfz->buffer)[vfz->readSize];
163 size_t toRead = vfz->bufferSize - vfz->readSize;
164 if (toRead > BLOCK_SIZE) {
165 toRead = BLOCK_SIZE;
166 }
167 ssize_t zipRead = zip_fread(vfz->zf, start, toRead);
168 if (zipRead < 0) {
169 if (bytesRead == 0) {
170 return -1;
171 }
172 break;
173 }
174 if (zipRead == 0) {
175 break;
176 }
177 vfz->readSize += zipRead;
178 } else {
179 break;
180 }
181 }
182 return bytesRead;
183}
184
185ssize_t _vfzReadline(struct VFile* vf, char* buffer, size_t size) {
186 size_t bytesRead = 0;
187 while (bytesRead < size - 1) {
188 size_t newRead = vf->read(vf, &buffer[bytesRead], 1);
189 bytesRead += newRead;
190 if (!newRead || buffer[bytesRead] == '\n') {
191 break;
192 }
193 }
194 return buffer[bytesRead] = '\0';
195}
196
197ssize_t _vfzWrite(struct VFile* vf, const void* buffer, size_t size) {
198 // TODO
199 UNUSED(vf);
200 UNUSED(buffer);
201 UNUSED(size);
202 return -1;
203}
204
205void* _vfzMap(struct VFile* vf, size_t size, int flags) {
206 struct VFileZip* vfz = (struct VFileZip*) vf;
207
208 UNUSED(flags);
209 if (size > vfz->readSize) {
210 vf->read(vf, 0, size - vfz->readSize);
211 }
212 return vfz->buffer;
213}
214
215void _vfzUnmap(struct VFile* vf, void* memory, size_t size) {
216 UNUSED(vf);
217 UNUSED(memory);
218 UNUSED(size);
219}
220
221void _vfzTruncate(struct VFile* vf, size_t size) {
222 // TODO
223 UNUSED(vf);
224 UNUSED(size);
225}
226
227bool _vdzClose(struct VDir* vd) {
228 struct VDirZip* vdz = (struct VDirZip*) vd;
229 if (zip_close(vdz->z) < 0) {
230 return false;
231 }
232 free(vdz);
233 return true;
234}
235
236void _vdzRewind(struct VDir* vd) {
237 struct VDirZip* vdz = (struct VDirZip*) vd;
238 vdz->dirent.index = -1;
239}
240
241struct VDirEntry* _vdzListNext(struct VDir* vd) {
242 struct VDirZip* vdz = (struct VDirZip*) vd;
243 zip_int64_t maxIndex = zip_get_num_entries(vdz->z, 0);
244 if (maxIndex <= vdz->dirent.index + 1) {
245 return 0;
246 }
247 ++vdz->dirent.index;
248 return &vdz->dirent.d;
249}
250
251struct VFile* _vdzOpenFile(struct VDir* vd, const char* path, int mode) {
252 UNUSED(mode);
253 // TODO: support truncating, appending and creating, and write
254 struct VDirZip* vdz = (struct VDirZip*) vd;
255
256 if ((mode & O_RDWR) == O_RDWR) {
257 // libzip doesn't allow for random access, so read/write is impossible without
258 // reading the entire file first. This approach will be supported eventually.
259 return 0;
260 }
261
262 if (mode & O_WRONLY) {
263 // Write support is not yet implemented.
264 return 0;
265 }
266
267 struct zip_stat s;
268 if (zip_stat(vdz->z, path, 0, &s) < 0) {
269 return 0;
270 }
271
272 struct zip_file* zf = zip_fopen(vdz->z, path, 0);
273 if (!zf) {
274 return 0;
275 }
276
277 struct VFileZip* vfz = malloc(sizeof(struct VFileZip));
278 vfz->zf = zf;
279 vfz->buffer = 0;
280 vfz->offset = 0;
281 vfz->bufferSize = 0;
282 vfz->readSize = 0;
283 vfz->fileSize = s.size;
284
285 vfz->d.close = _vfzClose;
286 vfz->d.seek = _vfzSeek;
287 vfz->d.read = _vfzRead;
288 vfz->d.readline = _vfzReadline;
289 vfz->d.write = _vfzWrite;
290 vfz->d.map = _vfzMap;
291 vfz->d.unmap = _vfzUnmap;
292 vfz->d.truncate = _vfzTruncate;
293
294 return &vfz->d;
295}
296
297const char* _vdezName(struct VDirEntry* vde) {
298 struct VDirEntryZip* vdez = (struct VDirEntryZip*) vde;
299 struct zip_stat s;
300 if (zip_stat_index(vdez->z, vdez->index, 0, &s) < 0) {
301 return 0;
302 }
303 return s.name;
304}
305
306#endif