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