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