int _zip_file_extra_field_prepare_for_change(struct zip *za, zip_uint64_t idx) { struct zip_entry *e; if (idx >= za->nentry) { _zip_error_set(&za->error, ZIP_ER_INVAL, 0); return -1; } e = za->entry+idx; if (e->changes && (e->changes->changed & ZIP_DIRENT_EXTRA_FIELD)) return 0; if (e->orig) { if (_zip_read_local_ef(za, idx) < 0) return -1; } if (e->changes == NULL) { if ((e->changes=_zip_dirent_clone(e->orig)) == NULL) { _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); return -1; } } if (e->orig && e->orig->extra_fields) { if ((e->changes->extra_fields=_zip_ef_clone(e->orig->extra_fields, &za->error)) == NULL) return -1; } e->changes->changed |= ZIP_DIRENT_EXTRA_FIELD; return 0; }
zip_int64_t _zip_file_replace(struct zip *za, zip_uint64_t idx, const char *name, struct zip_source *source, zip_flags_t flags) { zip_uint64_t za_nentry_prev; if (ZIP_IS_RDONLY(za)) { _zip_error_set(&za->error, ZIP_ER_RDONLY, 0); return -1; } za_nentry_prev = za->nentry; if (idx == ZIP_UINT64_MAX) { zip_int64_t i = -1; if (flags & ZIP_FL_OVERWRITE) i = _zip_name_locate(za, name, flags, NULL); if (i == -1) { /* create and use new entry, used by zip_add */ if ((i=_zip_add_entry(za)) < 0) return -1; } idx = (zip_uint64_t)i; } if (name && _zip_set_name(za, idx, name, flags) != 0) { if (za->nentry != za_nentry_prev) { _zip_entry_finalize(za->entry+idx); za->nentry = za_nentry_prev; } return -1; } /* does not change any name related data, so we can do it here; * needed for a double add of the same file name */ _zip_unchange_data(za->entry+idx); if (za->entry[idx].orig != NULL && (za->entry[idx].changes == NULL || (za->entry[idx].changes->changed & ZIP_DIRENT_COMP_METHOD) == 0)) { if (za->entry[idx].changes == NULL) { if ((za->entry[idx].changes=_zip_dirent_clone(za->entry[idx].orig)) == NULL) { _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); return -1; } } za->entry[idx].changes->comp_method = ZIP_CM_REPLACED_DEFAULT; za->entry[idx].changes->changed |= ZIP_DIRENT_COMP_METHOD; } za->entry[idx].source = source; return (zip_int64_t)idx; }
ZIP_EXTERN int zip_set_file_compression(struct zip *za, zip_uint64_t idx, zip_int32_t method, zip_uint32_t flags) { struct zip_entry *e; zip_int32_t old_method; if (idx >= za->nentry) { _zip_error_set(&za->error, ZIP_ER_INVAL, 0); return -1; } if (ZIP_IS_RDONLY(za)) { _zip_error_set(&za->error, ZIP_ER_RDONLY, 0); return -1; } if (method != ZIP_CM_DEFAULT && method != ZIP_CM_STORE && method != ZIP_CM_DEFLATE) { _zip_error_set(&za->error, ZIP_ER_COMPNOTSUPP, 0); return -1; } e = za->entry+idx; //zip_int32_t old_method = (e->orig == NULL ? ZIP_CM_DEFAULT : e->orig->comp_method); old_method = (e->orig == NULL ? ZIP_CM_DEFAULT : e->orig->comp_method); /* XXX: revisit this when flags are supported, since they may require a recompression */ if (method == old_method) { if (e->changes) { e->changes->changed &= ~ZIP_DIRENT_COMP_METHOD; if (e->changes->changed == 0) { _zip_dirent_free(e->changes); e->changes = NULL; } } } else { if (e->changes == NULL) { if ((e->changes=_zip_dirent_clone(e->orig)) == NULL) { _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); return -1; } } e->changes->comp_method = method; e->changes->changed |= ZIP_DIRENT_COMP_METHOD; } return 0; }
int zip_file_set_external_attributes(struct zip *za, zip_uint64_t idx, zip_flags_t flags, zip_uint8_t opsys, zip_uint32_t attributes) { struct zip_entry *e; int changed; zip_uint8_t unchanged_opsys; zip_uint32_t unchanged_attributes; if (_zip_get_dirent(za, idx, 0, NULL) == NULL) return -1; if (ZIP_IS_RDONLY(za)) { _zip_error_set(&za->error, ZIP_ER_RDONLY, 0); return -1; } e = za->entry+idx; unchanged_opsys = e->orig ? e->orig->version_madeby>>8 : ZIP_OPSYS_DEFAULT; unchanged_attributes = e->orig ? e->orig->ext_attrib : ZIP_EXT_ATTRIB_DEFAULT; changed = (opsys != unchanged_opsys || attributes != unchanged_attributes); if (changed) { if (e->changes == NULL) { if ((e->changes=_zip_dirent_clone(e->orig)) == NULL) { _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); return -1; } } e->changes->version_madeby = (opsys << 8) | (e->changes->version_madeby & 0xff); e->changes->ext_attrib = attributes; e->changes->changed |= ZIP_DIRENT_ATTRIBUTES; } else if (e->changes) { e->changes->changed &= ~ZIP_DIRENT_ATTRIBUTES; if (e->changes->changed == 0) { _zip_dirent_free(e->changes); e->changes = NULL; } else { e->changes->version_madeby = (unchanged_opsys << 8) | (e->changes->version_madeby & 0xff); e->changes->ext_attrib = unchanged_attributes; } } return 0; }
ZIP_EXTERN int zip_file_set_mtime(zip_t *za, zip_uint64_t idx, time_t mtime, zip_flags_t flags) { zip_entry_t *e; int changed; if (_zip_get_dirent(za, idx, 0, NULL) == NULL) return -1; if (ZIP_IS_RDONLY(za)) { zip_error_set(&za->error, ZIP_ER_RDONLY, 0); return -1; } e = za->entry+idx; changed = e->orig == NULL || mtime != e->orig->last_mod; if (changed) { if (e->changes == NULL) { if ((e->changes=_zip_dirent_clone(e->orig)) == NULL) { zip_error_set(&za->error, ZIP_ER_MEMORY, 0); return -1; } } e->changes->last_mod = mtime; e->changes->changed |= ZIP_DIRENT_LAST_MOD; } else { if (e->changes) { e->changes->changed &= ~ZIP_DIRENT_LAST_MOD; if (e->changes->changed == 0) { _zip_dirent_free(e->changes); e->changes = NULL; } } } return 0; }
int _zip_set_name(struct zip *za, zip_uint64_t idx, const char *name, zip_flags_t flags) { struct zip_entry *e; struct zip_string *str; int changed; zip_int64_t i; if (idx >= za->nentry) { _zip_error_set(&za->error, ZIP_ER_INVAL, 0); return -1; } if (ZIP_IS_RDONLY(za)) { _zip_error_set(&za->error, ZIP_ER_RDONLY, 0); return -1; } if (name && strlen(name) > 0) { /* XXX: check for string too long */ if ((str=_zip_string_new((const zip_uint8_t *)name, (zip_uint16_t)strlen(name), flags, &za->error)) == NULL) return -1; if ((flags & ZIP_FL_ENCODING_ALL) == ZIP_FL_ENC_GUESS && _zip_guess_encoding(str, ZIP_ENCODING_UNKNOWN) == ZIP_ENCODING_UTF8_GUESSED) str->encoding = ZIP_ENCODING_UTF8_KNOWN; } else str = NULL; /* XXX: encoding flags needed for CP437? */ if ((i=_zip_name_locate(za, name, 0, NULL)) >= 0 && (zip_uint64_t)i != idx) { _zip_string_free(str); _zip_error_set(&za->error, ZIP_ER_EXISTS, 0); return -1; } /* no effective name change */ if (i>=0 && (zip_uint64_t)i == idx) { _zip_string_free(str); return 0; } e = za->entry+idx; if (e->changes) { _zip_string_free(e->changes->filename); e->changes->filename = NULL; e->changes->changed &= ~ZIP_DIRENT_FILENAME; } if (e->orig) changed = !_zip_string_equal(e->orig->filename, str); else changed = 1; if (changed) { if (e->changes == NULL) { if ((e->changes=_zip_dirent_clone(e->orig)) == NULL) { _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); return -1; } } e->changes->filename = str; e->changes->changed |= ZIP_DIRENT_FILENAME; } else { _zip_string_free(str); if (e->changes && e->changes->changed == 0) { _zip_dirent_free(e->changes); e->changes = NULL; } } return 0; }
ZIP_EXTERN int zip_file_set_comment(struct zip *za, zip_uint64_t idx, const char *comment, zip_uint16_t len, zip_flags_t flags) { struct zip_entry *e; struct zip_string *cstr; struct zip_dirent *de; int changed; if ((de=_zip_get_dirent(za, idx, 0, NULL)) == NULL) return -1; if (ZIP_IS_RDONLY(za)) { _zip_error_set(&za->error, ZIP_ER_RDONLY, 0); return -1; } if (len > 0 && comment == NULL) { _zip_error_set(&za->error, ZIP_ER_INVAL, 0); return -1; } if (len > 0) { if ((cstr=_zip_string_new((const zip_uint8_t *)comment, len, flags, &za->error)) == NULL) return -1; if ((flags & ZIP_FL_ENCODING_ALL) == ZIP_FL_ENC_GUESS && _zip_guess_encoding(cstr, ZIP_ENCODING_UNKNOWN) == ZIP_ENCODING_UTF8_GUESSED) cstr->encoding = ZIP_ENCODING_UTF8_KNOWN; } else cstr = NULL; e = za->entry+idx; if (e->changes) { _zip_string_free(e->changes->comment); e->changes->comment = NULL; e->changes->changed &= ~ZIP_DIRENT_COMMENT; } if (e->orig && e->orig->comment) changed = !_zip_string_equal(e->orig->comment, cstr); else changed = (cstr != NULL); if (changed) { if (e->changes == NULL) { if ((e->changes=_zip_dirent_clone(e->orig)) == NULL) { _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); return -1; } } e->changes->comment = cstr; e->changes->changed |= ZIP_DIRENT_COMMENT; } else { _zip_string_free(cstr); if (e->changes && e->changes->changed == 0) { _zip_dirent_free(e->changes); e->changes = NULL; } } return 0; }
ZIP_EXTERN int zip_close(zip_t *za) { zip_uint64_t i, j, survivors; zip_int64_t off; int error; zip_filelist_t *filelist; int changed; if (za == NULL) return -1; changed = _zip_changed(za, &survivors); /* don't create zip files with no entries */ if (survivors == 0) { if ((za->open_flags & ZIP_TRUNCATE) || changed) { if (zip_source_remove(za->src) < 0) { _zip_error_set_from_source(&za->error, za->src); return -1; } } zip_discard(za); return 0; } if (!changed) { zip_discard(za); return 0; } if (survivors > za->nentry) { zip_error_set(&za->error, ZIP_ER_INTERNAL, 0); return -1; } if ((filelist=(zip_filelist_t *)malloc(sizeof(filelist[0])*(size_t)survivors)) == NULL) return -1; /* create list of files with index into original archive */ for (i=j=0; i<za->nentry; i++) { if (za->entry[i].deleted) continue; if (j >= survivors) { free(filelist); zip_error_set(&za->error, ZIP_ER_INTERNAL, 0); return -1; } filelist[j].idx = i; j++; } if (j < survivors) { free(filelist); zip_error_set(&za->error, ZIP_ER_INTERNAL, 0); return -1; } if (zip_source_begin_write(za->src) < 0) { _zip_error_set_from_source(&za->error, za->src); free(filelist); return -1; } error = 0; for (j=0; j<survivors; j++) { int new_data; zip_entry_t *entry; zip_dirent_t *de; i = filelist[j].idx; entry = za->entry+i; new_data = (ZIP_ENTRY_DATA_CHANGED(entry) || ZIP_ENTRY_CHANGED(entry, ZIP_DIRENT_COMP_METHOD)); /* create new local directory entry */ if (entry->changes == NULL) { if ((entry->changes=_zip_dirent_clone(entry->orig)) == NULL) { zip_error_set(&za->error, ZIP_ER_MEMORY, 0); error = 1; break; } } de = entry->changes; if (_zip_read_local_ef(za, i) < 0) { error = 1; break; } if ((off = zip_source_tell_write(za->src)) < 0) { error = 1; break; } de->offset = (zip_uint64_t)off; if (new_data) { zip_source_t *zs; zs = NULL; if (!ZIP_ENTRY_DATA_CHANGED(entry)) { if ((zs=_zip_source_zip_new(za, za, i, ZIP_FL_UNCHANGED, 0, 0, NULL)) == NULL) { error = 1; break; } } /* add_data writes dirent */ if (add_data(za, zs ? zs : entry->source, de) < 0) { error = 1; if (zs) zip_source_free(zs); break; } if (zs) zip_source_free(zs); } else { zip_uint64_t offset; /* when copying data, all sizes are known -> no data descriptor needed */ de->bitflags &= (zip_uint16_t)~ZIP_GPBF_DATA_DESCRIPTOR; if (_zip_dirent_write(za, de, ZIP_FL_LOCAL) < 0) { error = 1; break; } if ((offset=_zip_file_get_offset(za, i, &za->error)) == 0) { error = 1; break; } if (zip_source_seek(za->src, (zip_int64_t)offset, SEEK_SET) < 0) { _zip_error_set_from_source(&za->error, za->src); error = 1; break; } if (copy_data(za, de->comp_size) < 0) { error = 1; break; } } } if (!error) { if (write_cdir(za, filelist, survivors) < 0) error = 1; } free(filelist); if (!error) { if (zip_source_commit_write(za->src) != 0) { _zip_error_set_from_source(&za->error, za->src); error = 1; } } if (error) { zip_source_rollback_write(za->src); return -1; } zip_discard(za); return 0; }
ZIP_EXTERN int zip_close(struct zip *za) { zip_uint64_t i, j, survivors; int error; char *temp; FILE *out; #ifndef _WIN32 mode_t mask; #endif struct zip_filelist *filelist; int reopen_on_error; int new_torrentzip; int changed; reopen_on_error = 0; if (za == NULL) return -1; changed = _zip_changed(za, &survivors); /* don't create zip files with no entries */ if (survivors == 0) { if (za->zn && ((za->open_flags & ZIP_TRUNCATE) || (changed && za->zp))) { if (remove(za->zn) != 0) { _zip_error_set(&za->error, ZIP_ER_REMOVE, errno); return -1; } } zip_discard(za); return 0; } if (!changed) { zip_discard(za); return 0; } if (survivors > za->nentry) { _zip_error_set(&za->error, ZIP_ER_INTERNAL, 0); return -1; } if ((filelist=(struct zip_filelist *)malloc(sizeof(filelist[0])*(size_t)survivors)) == NULL) return -1; /* archive comment is special for torrentzip */ if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0)) { /* TODO: use internal function when zip_set_archive_comment clears TORRENT flag */ if (zip_set_archive_comment(za, TORRENT_SIG "XXXXXXXX", TORRENT_SIG_LEN + TORRENT_CRC_LEN) < 0) { free(filelist); return -1; } } /* TODO: if no longer torrentzip and archive comment not changed by user, delete it */ /* create list of files with index into original archive */ for (i=j=0; i<za->nentry; i++) { if (za->entry[i].deleted) continue; if (j >= survivors) { free(filelist); _zip_error_set(&za->error, ZIP_ER_INTERNAL, 0); return -1; } filelist[j].idx = i; filelist[j].name = zip_get_name(za, i, 0); j++; } if (j < survivors) { free(filelist); _zip_error_set(&za->error, ZIP_ER_INTERNAL, 0); return -1; } if ((temp=_zip_create_temp_output(za, &out)) == NULL) { free(filelist); return -1; } if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0)) qsort(filelist, (size_t)survivors, sizeof(filelist[0]), _zip_torrentzip_cmp); new_torrentzip = (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0) == 1 && zip_get_archive_flag(za, ZIP_AFL_TORRENT, ZIP_FL_UNCHANGED) == 0); error = 0; for (j=0; j<survivors; j++) { int new_data; struct zip_entry *entry; struct zip_dirent *de; i = filelist[j].idx; entry = za->entry+i; new_data = (ZIP_ENTRY_DATA_CHANGED(entry) || new_torrentzip || ZIP_ENTRY_CHANGED(entry, ZIP_DIRENT_COMP_METHOD)); /* create new local directory entry */ if (entry->changes == NULL) { if ((entry->changes=_zip_dirent_clone(entry->orig)) == NULL) { _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); error = 1; break; } } de = entry->changes; if (_zip_read_local_ef(za, i) < 0) { error = 1; break; } if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0)) _zip_dirent_torrent_normalize(entry->changes); de->offset = (zip_uint64_t)ftello(out); /* TODO: check for errors */ if (new_data) { struct zip_source *zs; zs = NULL; if (!ZIP_ENTRY_DATA_CHANGED(entry)) { if ((zs=_zip_source_zip_new(za, za, i, ZIP_FL_UNCHANGED, 0, 0, NULL)) == NULL) { error = 1; break; } } /* add_data writes dirent */ if (add_data(za, zs ? zs : entry->source, de, out) < 0) { error = 1; if (zs) zip_source_free(zs); break; } if (zs) zip_source_free(zs); } else { zip_uint64_t offset; /* when copying data, all sizes are known -> no data descriptor needed */ de->bitflags &= ~ZIP_GPBF_DATA_DESCRIPTOR; if (_zip_dirent_write(de, out, ZIP_FL_LOCAL, &za->error) < 0) { error = 1; break; } if ((offset=_zip_file_get_offset(za, i, &za->error)) == 0) { error = 1; break; } if ((fseeko(za->zp, (off_t)offset, SEEK_SET) < 0)) { _zip_error_set(&za->error, ZIP_ER_SEEK, errno); error = 1; break; } if (copy_data(za->zp, de->comp_size, out, &za->error) < 0) { error = 1; break; } } } if (!error) { if (write_cdir(za, filelist, survivors, out) < 0) error = 1; } free(filelist); if (error) { fclose(out); (void)remove(temp); free(temp); return -1; } if (fclose(out) != 0) { _zip_error_set(&za->error, ZIP_ER_CLOSE, errno); (void)remove(temp); free(temp); return -1; } if (za->zp) { fclose(za->zp); za->zp = NULL; reopen_on_error = 1; } if (_zip_rename(temp, za->zn) != 0) { _zip_error_set(&za->error, ZIP_ER_RENAME, errno); (void)remove(temp); free(temp); if (reopen_on_error) { /* ignore errors, since we're already in an error case */ za->zp = fopen(za->zn, "rb"); } return -1; } #ifndef _WIN32 mask = umask(0); umask(mask); chmod(za->zn, 0666&~mask); #endif zip_discard(za); free(temp); return 0; }
int _zip_set_name(zip_t *za, zip_uint64_t idx, const char *name, zip_flags_t flags) { zip_entry_t *e; zip_string_t *str; bool same_as_orig; zip_int64_t i; const zip_uint8_t *old_name, *new_name; zip_string_t *old_str; if (idx >= za->nentry) { zip_error_set(&za->error, ZIP_ER_INVAL, 0); return -1; } if (ZIP_IS_RDONLY(za)) { zip_error_set(&za->error, ZIP_ER_RDONLY, 0); return -1; } if (name && name[0] != '\0') { /* TODO: check for string too long */ if ((str=_zip_string_new((const zip_uint8_t *)name, (zip_uint16_t)strlen(name), flags, &za->error)) == NULL) return -1; if ((flags & ZIP_FL_ENCODING_ALL) == ZIP_FL_ENC_GUESS && _zip_guess_encoding(str, ZIP_ENCODING_UNKNOWN) == ZIP_ENCODING_UTF8_GUESSED) str->encoding = ZIP_ENCODING_UTF8_KNOWN; } else str = NULL; /* TODO: encoding flags needed for CP437? */ if ((i=_zip_name_locate(za, name, 0, NULL)) >= 0 && (zip_uint64_t)i != idx) { _zip_string_free(str); zip_error_set(&za->error, ZIP_ER_EXISTS, 0); return -1; } /* no effective name change */ if (i>=0 && (zip_uint64_t)i == idx) { _zip_string_free(str); return 0; } e = za->entry+idx; if (e->orig) same_as_orig = _zip_string_equal(e->orig->filename, str); else same_as_orig = false; if (!same_as_orig && e->changes == NULL) { if ((e->changes=_zip_dirent_clone(e->orig)) == NULL) { zip_error_set(&za->error, ZIP_ER_MEMORY, 0); _zip_string_free(str); return -1; } } if ((new_name = _zip_string_get(same_as_orig ? e->orig->filename : str, NULL, 0, &za->error)) == NULL) { _zip_string_free(str); return -1; } if (e->changes) { old_str = e->changes->filename; } else if (e->orig) { old_str = e->orig->filename; } else { old_str = NULL; } if (old_str) { if ((old_name = _zip_string_get(old_str, NULL, 0, &za->error)) == NULL) { _zip_string_free(str); return -1; } } else { old_name = NULL; } if (_zip_hash_add(za->names, new_name, idx, 0, &za->error) == false) { _zip_string_free(str); return -1; } if (old_name) { _zip_hash_delete(za->names, old_name, NULL); } if (same_as_orig) { if (e->changes) { if (e->changes->changed & ZIP_DIRENT_FILENAME) { _zip_string_free(e->changes->filename); e->changes->changed &= ~ZIP_DIRENT_FILENAME; if (e->changes->changed == 0) { _zip_dirent_free(e->changes); e->changes = NULL; } else { /* TODO: what if not cloned? can that happen? */ e->changes->filename = e->orig->filename; } } } _zip_string_free(str); } else { if (e->changes->changed & ZIP_DIRENT_FILENAME) { _zip_string_free(e->changes->filename); } e->changes->changed |= ZIP_DIRENT_FILENAME; e->changes->filename = str; } return 0; }