/* * The write-support in zziplib is not a full-flegded interface to the * internals that zip file-header or zip archive an contain. It's * primary use goes for savegames or transfer `pack-n-go` archives * where time-stamps are rather unimportant. Here we can create an * archive with filenames and their data portions, possibly obfuscated. * * DON'T USE THIS * * The write support is supposed to be added directly into the main * zziplib but it has not been implemented so far. It does however * export the relevant call entries which will return EROFS (read-only * filesystem) in case they are being called. That allows later programs * to start up with earlier versions of zziplib that can only read ZIPs. * * Author: * Guido Draheim * * Copyright (c) Guido Draheim, use under copyleft (LGPL,MPL) */ #define _ZZIP_WRITE_SOURCE #if defined DDDD || defined DDDDD || defined DDDDDD || defined DDDDDDD #define _ZZIP_ENABLE_WRITE #else /* per default, we add support for passthrough to posix write */ #define _ZZIP_POSIX_WRITE #endif #include /* #includes */ #include #include #include #include #include #include #ifdef ZZIP_HAVE_DIRECT_H #include #endif #include #include #include #include #define ___ { #define ____ } #ifndef EROFS # ifdef ENOSYS #define EROFS ENOSYS # else #define EROFS EPERM #endif #endif /* try real zlib routines for writing ? very experimental, very very ex... */ #ifndef _ZZIP_ENABLE_WRITE #define _ZZIP_TRY 0 #else #define _ZZIP_TRY 1 #endif /* btw, is there any system that did define those different ? get away.. */ # ifndef S_IWGRP # define S_IWGRP 00020 # endif # ifndef S_IRWXO # define S_IRWXO 00007 # endif /** create a new zip archive for writing * * This function will create a new zip archive. The returned parameter * is a new "zzip dir" handle that should be saved to a variable so it * can be used a base argument for => zzip_mkdir and => zzip_creat calls. * The returned handle represents a zip central directory that must be * saved to disk using => zzip_closedir. * * Returns null on error and sets errno. Remember, according to posix * the => creat(2) call is equivalent to open (path, O_WRONLY | O_CREAT | O_TRUNC, o_mode) * so any previous zip-archive will be overwritten unconditionally and * EEXIST errors from => mkdir(2) are suppressed. (fixme: delete the * given subtree? like suggested by O_TRUNC? not done so far!) */ ZZIP_DIR * zzip_dir_creat(zzip_char_t * name, int o_mode) { return zzip_dir_creat_ext_io(name, o_mode, 0, 0); } /** => zzip_dir_creat * * If the third argument "ext" has another special meaning here, as it * is used to ensure that a given zip-file is created with the first entry * of the ext-list appended as an extension unless the file-path already * ends with a file-extension registered in the list. Therefore {"",0} * matches all files and creates them as zip-archives under the given * nonmodified name. (Some magic here? If the path ends in the path * separator then make a real directory even in the presence of ext-list?) * * This function is not yet implemented, check for #def ZZIP_NO_CREAT * Write-support will extend => zzip_closedir with semantics to finalize the * zip-archive by writing the zip-trailer and closing the archive file. */ ZZIP_DIR * zzip_dir_creat_ext_io(zzip_char_t * name, int o_mode, zzip_strings_t * ext, zzip_plugin_io_t io) { if (! io) io = zzip_get_default_io(); if (io != zzip_get_default_io()) { /* the current io-structure does not contain a "write" entry, * and therefore this parameter is useless. Anyone to expect * some behavior should be warned, so here we let the function * fail bluntly - and leaving the recovery to the application */ errno = EINVAL; return 0; } if (! _ZZIP_TRY) { /* not implemented - however, we respect that a null argument to * zzip_mkdir and zzip_creat works, so we silently still do the mkdir */ if (! _zzip_mkdir(name, o_mode) || errno == EEXIST) errno = EROFS; return 0; } else { # define MAX_EXT_LEN 10 ZZIP_DIR *dir = zzip_dir_alloc(ext); int name_len = strlen(name); dir->realname = malloc(name_len + MAX_EXT_LEN); if (! dir->realname) goto error; memcpy(dir->realname, name, name_len + 1); ___ int fd = __zzip_try_open(dir->realname, O_EXCL | O_TRUNC | O_WRONLY, ext, io); if (fd != -1) { dir->fd = fd; return dir; } ___ zzip_strings_t *exx = ext; int exx_len; for (; *exx; exx++) { if ((exx_len = strlen(*exx) + 1) <= name_len && ! memcmp(dir->realname + (name_len - exx_len), *exx, exx_len)) break; /* keep unmodified */ exx++; if (*exx) continue; if (! (exx_len = strlen(*exx)) || exx_len >= MAX_EXT_LEN) break; memcpy(dir->realname + name_len, exx, exx_len); /* append! */ } ____; fd = (io->fd.open)(dir->realname, O_CREAT | O_TRUNC | O_WRONLY, o_mode); dir->realname[name_len] = '\0'; /* keep ummodified */ if (fd != -1) { dir->fd = fd; return dir; } error: zzip_dir_free(dir); return 0; ____; } } /** create a new archive area for writing * * This function will create a new archive area. This may either be a * a new zip archive or a new directory in the filesystem. The returned * parameter is a new "zzip dir" handle that should be saved to a variable * so it can be used a base argument for => zzip_file_mkdir and * => zzip_file_creat calls. The returned handle wraps both possibilities, * it can be representing a zip central directory that must be * saved to disk using => zzip_closedir or it is just a handle for the * name of the real directory that still must be run through * => zzip_closedir to release the wrapper around the directory name. * * The magic is pushed through the o_mode argument. Using a mode that * has no group-write bit set (S_IWGRP = 0040) then the file is * created as a zip directory. Note that this is unabridged of any * umask value in the system where the argument to this function could * be 0775 but with an umask of 0755 it turns out as 0755 for a real * directory. Using 0755 directly would not create it as a real directory * but as a zip archive handle. * * This function is not yet implemented, check for #def ZZIP_NO_CREAT * Write-support will extend => zzip_closedir with semantics to finalize the * zip-archive by writing the zip-trailer and closing the archive file. * * Returns null on error and sets errno. Remember, according to posix * the => creat(2) call is equivalent to open (path, O_WRONLY | O_CREAT | O_TRUNC, o_mode) * so any previous zip-archive will be overwritten unconditionally and * EEXIST errors from => mkdir(2) are suppressed. (fixme: delete the * given subtree? like suggested by O_TRUNC? not done so far!) */ ZZIP_DIR * zzip_createdir(zzip_char_t * name, int o_mode) { if (o_mode & S_IWGRP) { if (-1 == _zzip_mkdir(name, o_mode) && errno != EEXIST) /* fail */ return 0; return zzip_opendir(name); } else return zzip_dir_creat(name, o_mode); } /** => zzip_file_creat also: mkdir(2), creat(2), zzip_dir_creat * * This function has an additional primary argument over the posix * mkdir(2) - if it is null then this function behaves just like * posix mkdir(2). The zzip_dir argument can be set to the result * of a => zzip_createdir which allows for some magic that the * given directory name is created as an entry in the zip archive. * * If the given dir name argument is not within the basepath of * the zip central directory then a real directory is created. * Any EEXIST errors are not suppressed unlike with => zzip_createdir * * Standard usage accepts a global/threaded/modular ZZIP_DIR pointer * for all zip archive operations like in: ZZIP_DIR* zip = zzip_createdir (sysconfpath, 0755, zip); zzip_file_mkdir (zip, filepath[i], 0755); ZZIP_FILE* file = zzip_file_creat (zip, filename[i], 0644); zzip_write (file, buf, len); zzip_close (file); file = 0; zzip_closedir (zip); zip = 0; * * compare with => zzip_mkdir inline macro which allows to * collapse the examples script to #define zzip_savefile myproject_saveconfig #include ZZIP_DIR* zzip_savefile = zzip_createdir (sysconfpath, 0755); zzip_mkdir (filepath[i], 0755); ZZIP_FILE* file = zzip_creat(filepath[i], 0644); zzip_write (file, buf, len); zzip_close (file); file = 0; zzip_closedir (zip_savefile); */ int zzip_file_mkdir(ZZIP_DIR * dir, zzip_char_t * name, int o_mode) { if (! dir) return _zzip_mkdir(name, o_mode); if (! _ZZIP_TRY) { /* not implemented */ errno = EROFS; return -1; } else { errno = EROFS; return -1; } } /** start next file entry in a zip archive * * This function will create a new file within a zzip archive, the * one given as the primary argument and additionally to the posix * creat(2) - just like zzip_mkdir has an additional argument over * the posix mkdir(2) spec. For this function the primary parameter * can be null as well thereby creating a real file instead of a new * one inside the zip-archive otherwise given. If the primary parameter is * not null but wraps a real directory then all new files are also real. * * This function is not yet implemented, check for #def ZZIP_NO_CREAT * * Returns NULL on an error setting errno, and opening a file _within_ * a zip archive using O_RDONLY (and similar stuff) will surely lead to * an error. */ ZZIP_FILE * zzip_file_creat(ZZIP_DIR * dir, zzip_char_t * name, int o_mode) { if (! dir) return zzip_open(name, o_mode); if (! _ZZIP_TRY) { /* not implemented */ errno = EROFS; return 0; } else { errno = EROFS; return 0; } } /** write to zzip storage also: write(2), zlib(3) * * This function will write data to a file descriptor. If the file * descriptor represents a real file then it will be forwarded to * call posix => write(2) directly. If it is a descriptor for a * file within a zip directory then the data will be "deflated" * using => zlib(3) and appended to the zip archive file. */ zzip_ssize_t zzip_write(ZZIP_FILE * file, const void *ptr, zzip_size_t len) { if (zzip_file_real(file)) return write(zzip_realfd(file), ptr, len); else return zzip_file_write(file, ptr, len); } /** => zzip_write also: zzip_file_creat * * This function will write data to a file descriptor inside a zip * archive. The data will be "deflated" using => zlib(3) compression * and appended to the end of the zip archive file. Only one file * descriptor may be open per zzip_dir archive handle (fifo-like). * * This function is not yet implemented, check for #def ZZIP_NO_CREAT * It returns immediately -1 and sets errno=EROFS for indication. */ zzip_ssize_t zzip_file_write(ZZIP_FILE * file, const void *ptr, zzip_size_t len) { if (! _ZZIP_TRY) { /* not implemented */ errno = EROFS; return -1; } else { /* add calls to zlib here... */ errno = EROFS; return -1; } } /** => zzip_write * This function is the stdc variant for writing and the arguments * are forwarded to => zzip_write - the return value is floored to * null as for STDC spec but there is no zzip_ferror call so far * for the zziplib (later? is it actually needed?). * * This function is not yet implemented, check for #def ZZIP_NO_CREAT * Write-support extends => zzip_close with semantics to write out a * file-trailer to the zip-archive leaving a name/offset marker in * the (still-open) ZZIP_DIR handle. */ zzip_size_t zzip_fwrite(const void *ptr, zzip_size_t len, zzip_size_t multiply, ZZIP_FILE * file) { zzip_ssize_t value = zzip_write(file, ptr, len * multiply); if (value == -1) value = 0; return (zzip_size_t) value; } #if 0 /* pure documentation */ /** create a zipped file/directory also: zzip_dir_creat, mkdir(2) * * This function creates a directory entry in the default zip-archive. * If you did not specify a "#define zzip_savefile somevar" * then the default zip-archive is null and all directories are * created as real directories in the filesystem. This function is * really a preprocessor macro or preferably an inline function * around => zzip_file_mkdir, there is no such symbol generated * into the library. The prototype is modelled after the posix * => mkdir(2) call. #ifndef zzip_savefile #define zzip_savefile 0 #endif #define zzip_mkdir(name,mode) \ - zzip_file_mkdir(zzip_savefile,name,mode) * */ int inline zzip_mkdir(zzip_char_t * name, int o_mode) { return zzip_file_creat(zzip_savefile, name, mode); } #endif #if 0 /* pure documentation */ /** => zzip_mkdir also: creat(2), zzip_start * * This function creates a file in the default zip-archive. * If you did not specify a "#define zzip_savefile somevar" * then the default zip-archive is null and all files are created * as real files. This function is really a preprocessor macro * or preferably an inline function around => zzip_file_creat, * there is no such symbol generated into the library. The prototype * is modelled after the posix => creat(2) call. #ifndef zzip_savefile #define zzip_savefile 0 #endif #define zzip_creat(name,mode) \ - zzip_file_creat(zzip_savefile,name,mode) */ ZZIP_FILE *inline zzip_creat(zzip_char_t * name, int o_mode) { return zzip_file_creat(zzip_savefile, name, mode); } #endif #if 0 /* pure documentation */ /** start writing to the magic zzip_savefile also: zzip_creat, zzip_write * * open a zip archive for writing via the magic zzip_savefile macro * variable. The name and mode are given to => zzip_createdir and * the result is stored into => zzip_savefile - if the => zzip_savefile * did already have a zzip_dir handle then it is automatically * finalized with => zzip_sync and the handle closed and the * zzip_savefile variable reused for the new zip archive just started * with this call. - This function is really a preprocessor macro * or preferably an inline function around => zzip_dir_create, there * is no such symbol generated into the library. #ifndef zzip_savefile #define zzip_savefile 0 #endif #define zzip_start(name,mode,ext) \ - { if (zzip_savefile) zzip_closedir(zzip_savefile); \ - zzip_savefile = zzip_createdir(name,mode,ext); } * This function returns null on error or a zzip_dir handle on * success. It is perfectly okay to continue with a null in the * zzip_savefile variable since it makes subsequent calls to * => zzip_creat and => zzip_mkdir to run as => creat(2) / => mkdir(2) * on the real filesystem. */ void inline zzip_mkfifo(zzip_char_t * name, int o_mode) { if (zzip_savefile) zzip_closedir(zzip_savefile); zzip_savefile = zzip_createdir(name, o_mode); } #endif #if 0 /* pure documentation */ /** => zzip_mkfifo also: zzip_closedir, sync(2) * * finalize a zip archive thereby writing the central directory to * the end of the file. If it was a real directory then we do just * nothing - even that the prototype of the call itself is modelled * to be similar to the posix => sync(2) call. This function is * really a preprocessor macro or preferably an inline function * around => zzip_closedir, there is no such symbol generated * into the library. #ifndef zzip_savefile #define zzip_savefile 0 #endif #define zzip_sync(name,mode) \ - { zzip_closedir(zzip_savefile); zzip_savefile = 0; } * */ void inline zzip_sync(void) { zzip_closedir(zzip_savefile); zzip_savefile = 0; } #endif /* * Local variables: * c-file-style: "stroustrup" * End: */