1 /*
2 * The write-support in zziplib is not a full-flegded interface to the
3 * internals that zip file-header or zip archive an contain. It's
4 * primary use goes for savegames or transfer `pack-n-go` archives
5 * where time-stamps are rather unimportant. Here we can create an
6 * archive with filenames and their data portions, possibly obfuscated.
7 *
8 * DON'T USE THIS
9 *
10 * The write support is supposed to be added directly into the main
11 * zziplib but it has not been implemented so far. It does however
12 * export the relevant call entries which will return EROFS (read-only
13 * filesystem) in case they are being called. That allows later programs
14 * to start up with earlier versions of zziplib that can only read ZIPs.
15 *
16 * Author:
17 * Guido Draheim <guidod@gmx.de>
18 *
19 * Copyright (c) Guido Draheim, use under copyleft (LGPL,MPL)
20 */
21
22 #define _ZZIP_WRITE_SOURCE
23
24 #if defined DDDD || defined DDDDD || defined DDDDDD || defined DDDDDDD
25 #define _ZZIP_ENABLE_WRITE
26 #else /* per default, we add support for passthrough to posix write */
27 #define _ZZIP_POSIX_WRITE
28 #endif
29
30 #include <zzip/write.h> /* #includes <zzip/lib.h> */
31 #include <zzip/file.h>
32
33 #include <string.h>
34 #include <sys/stat.h>
35 #include <errno.h>
36 #include <stdlib.h>
37 #include <ctype.h>
38
39 #ifdef ZZIP_HAVE_DIRECT_H
40 #include <direct.h>
41 #endif
42
43 #include <zzip/format.h>
44 #include <zzip/plugin.h>
45 #include <zzip/__mkdir.h>
46 #include <zzip/__debug.h>
47
48 #define ___ {
49 #define ____ }
50
51 #ifndef EROFS
52 # ifdef ENOSYS
53 #define EROFS ENOSYS
54 # else
55 #define EROFS EPERM
56 #endif
57 #endif
58
59 /* try real zlib routines for writing ? very experimental, very very ex... */
60 #ifndef _ZZIP_ENABLE_WRITE
61 #define _ZZIP_TRY 0
62 #else
63 #define _ZZIP_TRY 1
64 #endif
65
66 /* btw, is there any system that did define those different ? get away.. */
67 # ifndef S_IWGRP
68 # define S_IWGRP 00020
69 # endif
70 # ifndef S_IRWXO
71 # define S_IRWXO 00007
72 # endif
73
74 /** create a new zip archive for writing
75 *
76 * This function will create a new zip archive. The returned parameter
77 * is a new "zzip dir" handle that should be saved to a variable so it
78 * can be used a base argument for => zzip_mkdir and => zzip_creat calls.
79 * The returned handle represents a zip central directory that must be
80 * saved to disk using => zzip_closedir.
81 *
82 * Returns null on error and sets errno. Remember, according to posix
83 * the => creat(2) call is equivalent to
84 open (path, O_WRONLY | O_CREAT | O_TRUNC, o_mode)
85 * so any previous zip-archive will be overwritten unconditionally and
86 * EEXIST errors from => mkdir(2) are suppressed. (fixme: delete the
87 * given subtree? like suggested by O_TRUNC? not done so far!)
88 */
89 ZZIP_DIR *
zzip_dir_creat(zzip_char_t * name,int o_mode)90 zzip_dir_creat(zzip_char_t * name, int o_mode)
91 {
92 return zzip_dir_creat_ext_io(name, o_mode, 0, 0);
93 }
94
95 /** => zzip_dir_creat
96 *
97 * If the third argument "ext" has another special meaning here, as it
98 * is used to ensure that a given zip-file is created with the first entry
99 * of the ext-list appended as an extension unless the file-path already
100 * ends with a file-extension registered in the list. Therefore {"",0}
101 * matches all files and creates them as zip-archives under the given
102 * nonmodified name. (Some magic here? If the path ends in the path
103 * separator then make a real directory even in the presence of ext-list?)
104 *
105 * This function is not yet implemented, check for #def ZZIP_NO_CREAT
106 * Write-support will extend => zzip_closedir with semantics to finalize the
107 * zip-archive by writing the zip-trailer and closing the archive file.
108 */
109 ZZIP_DIR *
zzip_dir_creat_ext_io(zzip_char_t * name,int o_mode,zzip_strings_t * ext,zzip_plugin_io_t io)110 zzip_dir_creat_ext_io(zzip_char_t * name, int o_mode,
111 zzip_strings_t * ext, zzip_plugin_io_t io)
112 {
113 if (! io)
114 io = zzip_get_default_io();
115
116 if (io != zzip_get_default_io())
117 {
118 /* the current io-structure does not contain a "write" entry,
119 * and therefore this parameter is useless. Anyone to expect
120 * some behavior should be warned, so here we let the function
121 * fail bluntly - and leaving the recovery to the application
122 */
123 errno = EINVAL;
124 return 0;
125 }
126
127
128 if (! _ZZIP_TRY)
129 {
130 /* not implemented - however, we respect that a null argument to
131 * zzip_mkdir and zzip_creat works, so we silently still do the mkdir
132 */
133 if (! _zzip_mkdir(name, o_mode) || errno == EEXIST)
134 errno = EROFS;
135 return 0;
136 } else
137 {
138 # define MAX_EXT_LEN 10
139 ZZIP_DIR *dir = zzip_dir_alloc(ext);
140 int name_len = strlen(name);
141 dir->realname = malloc(name_len + MAX_EXT_LEN);
142 if (! dir->realname)
143 goto error;
144
145 memcpy(dir->realname, name, name_len + 1);
146 ___ int fd =
147 __zzip_try_open(dir->realname, O_EXCL | O_TRUNC | O_WRONLY, ext,
148 io);
149 if (fd != -1)
150 { dir->fd = fd; return dir; }
151
152 ___ zzip_strings_t *exx = ext;
153 int exx_len;
154 for (; *exx; exx++)
155 {
156 if ((exx_len = strlen(*exx) + 1) <= name_len &&
157 ! memcmp(dir->realname + (name_len - exx_len), *exx, exx_len))
158 break; /* keep unmodified */
159 exx++;
160 if (*exx)
161 continue;
162
163 if (! (exx_len = strlen(*exx)) || exx_len >= MAX_EXT_LEN)
164 break;
165 memcpy(dir->realname + name_len, exx, exx_len); /* append! */
166 }
167 ____;
168 fd = (io->fd.open)(dir->realname, O_CREAT | O_TRUNC | O_WRONLY, o_mode);
169 dir->realname[name_len] = '\0'; /* keep ummodified */
170 if (fd != -1)
171 { dir->fd = fd; return dir; }
172 error:
173 zzip_dir_free(dir);
174 return 0;
175 ____;
176 }
177 }
178
179 /** create a new archive area for writing
180 *
181 * This function will create a new archive area. This may either be a
182 * a new zip archive or a new directory in the filesystem. The returned
183 * parameter is a new "zzip dir" handle that should be saved to a variable
184 * so it can be used a base argument for => zzip_file_mkdir and
185 * => zzip_file_creat calls. The returned handle wraps both possibilities,
186 * it can be representing a zip central directory that must be
187 * saved to disk using => zzip_closedir or it is just a handle for the
188 * name of the real directory that still must be run through
189 * => zzip_closedir to release the wrapper around the directory name.
190 *
191 * The magic is pushed through the o_mode argument. Using a mode that
192 * has no group-write bit set (S_IWGRP = 0040) then the file is
193 * created as a zip directory. Note that this is unabridged of any
194 * umask value in the system where the argument to this function could
195 * be 0775 but with an umask of 0755 it turns out as 0755 for a real
196 * directory. Using 0755 directly would not create it as a real directory
197 * but as a zip archive handle.
198 *
199 * This function is not yet implemented, check for #def ZZIP_NO_CREAT
200 * Write-support will extend => zzip_closedir with semantics to finalize the
201 * zip-archive by writing the zip-trailer and closing the archive file.
202 *
203 * Returns null on error and sets errno. Remember, according to posix
204 * the => creat(2) call is equivalent to
205 open (path, O_WRONLY | O_CREAT | O_TRUNC, o_mode)
206 * so any previous zip-archive will be overwritten unconditionally and
207 * EEXIST errors from => mkdir(2) are suppressed. (fixme: delete the
208 * given subtree? like suggested by O_TRUNC? not done so far!)
209 */
210 ZZIP_DIR *
zzip_createdir(zzip_char_t * name,int o_mode)211 zzip_createdir(zzip_char_t * name, int o_mode)
212 {
213 if (o_mode & S_IWGRP)
214 {
215 if (-1 == _zzip_mkdir(name, o_mode) && errno != EEXIST) /* fail */
216 return 0;
217 return zzip_opendir(name);
218 } else
219 return zzip_dir_creat(name, o_mode);
220 }
221
222 /** => zzip_file_creat also: mkdir(2), creat(2), zzip_dir_creat
223 *
224 * This function has an additional primary argument over the posix
225 * mkdir(2) - if it is null then this function behaves just like
226 * posix mkdir(2). The zzip_dir argument can be set to the result
227 * of a => zzip_createdir which allows for some magic that the
228 * given directory name is created as an entry in the zip archive.
229 *
230 * If the given dir name argument is not within the basepath of
231 * the zip central directory then a real directory is created.
232 * Any EEXIST errors are not suppressed unlike with => zzip_createdir
233 *
234 * Standard usage accepts a global/threaded/modular ZZIP_DIR pointer
235 * for all zip archive operations like in:
236 ZZIP_DIR* zip = zzip_createdir (sysconfpath, 0755, zip);
237 zzip_file_mkdir (zip, filepath[i], 0755);
238 ZZIP_FILE* file = zzip_file_creat (zip, filename[i], 0644);
239 zzip_write (file, buf, len);
240 zzip_close (file); file = 0;
241 zzip_closedir (zip); zip = 0;
242 *
243 * compare with => zzip_mkdir inline macro which allows to
244 * collapse the examples script to
245 #define zzip_savefile myproject_saveconfig
246 #include <zzip/zzip.h>
247 ZZIP_DIR* zzip_savefile = zzip_createdir (sysconfpath, 0755);
248 zzip_mkdir (filepath[i], 0755);
249 ZZIP_FILE* file = zzip_creat(filepath[i], 0644);
250 zzip_write (file, buf, len);
251 zzip_close (file); file = 0;
252 zzip_closedir (zip_savefile);
253 */
254 int
zzip_file_mkdir(ZZIP_DIR * dir,zzip_char_t * name,int o_mode)255 zzip_file_mkdir(ZZIP_DIR * dir, zzip_char_t * name, int o_mode)
256 {
257 if (! dir)
258 return _zzip_mkdir(name, o_mode);
259
260 if (! _ZZIP_TRY)
261 { /* not implemented */
262 errno = EROFS;
263 return -1;
264 } else
265 {
266 errno = EROFS;
267 return -1;
268 }
269 }
270
271 /** start next file entry in a zip archive
272 *
273 * This function will create a new file within a zzip archive, the
274 * one given as the primary argument and additionally to the posix
275 * creat(2) - just like zzip_mkdir has an additional argument over
276 * the posix mkdir(2) spec. For this function the primary parameter
277 * can be null as well thereby creating a real file instead of a new
278 * one inside the zip-archive otherwise given. If the primary parameter is
279 * not null but wraps a real directory then all new files are also real.
280 *
281 * This function is not yet implemented, check for #def ZZIP_NO_CREAT
282 *
283 * Returns NULL on an error setting errno, and opening a file _within_
284 * a zip archive using O_RDONLY (and similar stuff) will surely lead to
285 * an error.
286 */
287 ZZIP_FILE *
zzip_file_creat(ZZIP_DIR * dir,zzip_char_t * name,int o_mode)288 zzip_file_creat(ZZIP_DIR * dir, zzip_char_t * name, int o_mode)
289 {
290 if (! dir)
291 return zzip_open(name, o_mode);
292
293 if (! _ZZIP_TRY)
294 { /* not implemented */
295 errno = EROFS;
296 return 0;
297 } else
298 {
299 errno = EROFS;
300 return 0;
301 }
302 }
303
304 /** write to zzip storage also: write(2), zlib(3)
305 *
306 * This function will write data to a file descriptor. If the file
307 * descriptor represents a real file then it will be forwarded to
308 * call posix => write(2) directly. If it is a descriptor for a
309 * file within a zip directory then the data will be "deflated"
310 * using => zlib(3) and appended to the zip archive file.
311 */
312 zzip_ssize_t
zzip_write(ZZIP_FILE * file,const void * ptr,zzip_size_t len)313 zzip_write(ZZIP_FILE * file, const void *ptr, zzip_size_t len)
314 {
315 if (zzip_file_real(file))
316 return write(zzip_realfd(file), ptr, len);
317 else
318 return zzip_file_write(file, ptr, len);
319 }
320
321 /** => zzip_write also: zzip_file_creat
322 *
323 * This function will write data to a file descriptor inside a zip
324 * archive. The data will be "deflated" using => zlib(3) compression
325 * and appended to the end of the zip archive file. Only one file
326 * descriptor may be open per zzip_dir archive handle (fifo-like).
327 *
328 * This function is not yet implemented, check for #def ZZIP_NO_CREAT
329 * It returns immediately -1 and sets errno=EROFS for indication.
330 */
331 zzip_ssize_t
zzip_file_write(ZZIP_FILE * file,const void * ptr,zzip_size_t len)332 zzip_file_write(ZZIP_FILE * file, const void *ptr, zzip_size_t len)
333 {
334 if (! _ZZIP_TRY)
335 { /* not implemented */
336 errno = EROFS;
337 return -1;
338 } else
339 {
340 /* add calls to zlib here... */
341 errno = EROFS;
342 return -1;
343 }
344 }
345
346 /** => zzip_write
347 * This function is the stdc variant for writing and the arguments
348 * are forwarded to => zzip_write - the return value is floored to
349 * null as for STDC spec but there is no zzip_ferror call so far
350 * for the zziplib (later? is it actually needed?).
351 *
352 * This function is not yet implemented, check for #def ZZIP_NO_CREAT
353 * Write-support extends => zzip_close with semantics to write out a
354 * file-trailer to the zip-archive leaving a name/offset marker in
355 * the (still-open) ZZIP_DIR handle.
356 */
357 zzip_size_t
zzip_fwrite(const void * ptr,zzip_size_t len,zzip_size_t multiply,ZZIP_FILE * file)358 zzip_fwrite(const void *ptr, zzip_size_t len, zzip_size_t multiply,
359 ZZIP_FILE * file)
360 {
361 zzip_ssize_t value = zzip_write(file, ptr, len * multiply);
362 if (value == -1)
363 value = 0;
364 return (zzip_size_t) value;
365 }
366
367 #if 0 /* pure documentation */
368
369 /** create a zipped file/directory also: zzip_dir_creat, mkdir(2)
370 *
371 * This function creates a directory entry in the default zip-archive.
372 * If you did not specify a "#define zzip_savefile somevar"
373 * then the default zip-archive is null and all directories are
374 * created as real directories in the filesystem. This function is
375 * really a preprocessor macro or preferably an inline function
376 * around => zzip_file_mkdir, there is no such symbol generated
377 * into the library. The prototype is modelled after the posix
378 * => mkdir(2) call.
379 #ifndef zzip_savefile
380 #define zzip_savefile 0
381 #endif
382 #define zzip_mkdir(name,mode) \ -
383 zzip_file_mkdir(zzip_savefile,name,mode)
384 *
385 */
386 int inline
387 zzip_mkdir(zzip_char_t * name, int o_mode)
388 {
389 return zzip_file_creat(zzip_savefile, name, mode);
390 }
391 #endif
392
393 #if 0 /* pure documentation */
394
395 /** => zzip_mkdir also: creat(2), zzip_start
396 *
397 * This function creates a file in the default zip-archive.
398 * If you did not specify a "#define zzip_savefile somevar"
399 * then the default zip-archive is null and all files are created
400 * as real files. This function is really a preprocessor macro
401 * or preferably an inline function around => zzip_file_creat,
402 * there is no such symbol generated into the library. The prototype
403 * is modelled after the posix => creat(2) call.
404 #ifndef zzip_savefile
405 #define zzip_savefile 0
406 #endif
407 #define zzip_creat(name,mode) \ -
408 zzip_file_creat(zzip_savefile,name,mode)
409 */
410 ZZIP_FILE *inline
411 zzip_creat(zzip_char_t * name, int o_mode)
412 {
413 return zzip_file_creat(zzip_savefile, name, mode);
414 }
415 #endif
416
417
418 #if 0 /* pure documentation */
419
420 /** start writing to the magic zzip_savefile also: zzip_creat, zzip_write
421 *
422 * open a zip archive for writing via the magic zzip_savefile macro
423 * variable. The name and mode are given to => zzip_createdir and
424 * the result is stored into => zzip_savefile - if the => zzip_savefile
425 * did already have a zzip_dir handle then it is automatically
426 * finalized with => zzip_sync and the handle closed and the
427 * zzip_savefile variable reused for the new zip archive just started
428 * with this call. - This function is really a preprocessor macro
429 * or preferably an inline function around => zzip_dir_create, there
430 * is no such symbol generated into the library.
431 #ifndef zzip_savefile
432 #define zzip_savefile 0
433 #endif
434 #define zzip_start(name,mode,ext) \ -
435 { if (zzip_savefile) zzip_closedir(zzip_savefile); \ -
436 zzip_savefile = zzip_createdir(name,mode,ext); }
437 * This function returns null on error or a zzip_dir handle on
438 * success. It is perfectly okay to continue with a null in the
439 * zzip_savefile variable since it makes subsequent calls to
440 * => zzip_creat and => zzip_mkdir to run as => creat(2) / => mkdir(2)
441 * on the real filesystem.
442 */
443 void inline
444 zzip_mkfifo(zzip_char_t * name, int o_mode)
445 {
446 if (zzip_savefile)
447 zzip_closedir(zzip_savefile);
448 zzip_savefile = zzip_createdir(name, o_mode);
449 }
450 #endif
451
452 #if 0 /* pure documentation */
453
454 /** => zzip_mkfifo also: zzip_closedir, sync(2)
455 *
456 * finalize a zip archive thereby writing the central directory to
457 * the end of the file. If it was a real directory then we do just
458 * nothing - even that the prototype of the call itself is modelled
459 * to be similar to the posix => sync(2) call. This function is
460 * really a preprocessor macro or preferably an inline function
461 * around => zzip_closedir, there is no such symbol generated
462 * into the library.
463 #ifndef zzip_savefile
464 #define zzip_savefile 0
465 #endif
466 #define zzip_sync(name,mode) \ -
467 { zzip_closedir(zzip_savefile); zzip_savefile = 0; }
468 *
469 */
470 void inline
471 zzip_sync(void)
472 {
473 zzip_closedir(zzip_savefile);
474 zzip_savefile = 0;
475 }
476 #endif
477
478 /*
479 * Local variables:
480 * c-file-style: "stroustrup"
481 * End:
482 */
483