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