// Write pending operations into ZIP archive bool csArchive::WriteZipArchive () { char temp_file[MAXPATHLEN]; FILE *temp; char buff [16 * 1024]; bool success = false; int n = 0; // Check if file is opened for reading first if (!file) return false; // Step one: Copy archive file into a temporary file, // skipping entries marked as 'deleted' strcpy (temp_file, TEMP_DIR); int tmplen = strlen (temp_file); APPEND_SLASH (temp_file, tmplen); sprintf (&temp_file[tmplen], TEMP_FILE); if ((temp = fopen (temp_file, "w+b")) == NULL) return false; /* Cannot create temporary file */ fseek (file, 0, SEEK_SET); while (fread (buff, 1, sizeof (hdr_local), file) == sizeof (hdr_local)) { size_t bytes_to_copy, bytes_to_skip; ArchiveEntry *this_file = NULL; if (memcmp (buff, hdr_local, sizeof (hdr_local)) == 0) { // local header ZIP_local_file_header lfh; if (!ReadLFH (lfh, file)) goto temp_failed; char *this_name = new char[lfh.filename_length + 1]; if (fread (this_name, 1, lfh.filename_length, file) < lfh.filename_length) { delete [] this_name; goto temp_failed; } this_name[lfh.filename_length] = 0; if (IsDeleted (this_name)) { skip_entry: bytes_to_skip = lfh.extra_field_length + lfh.csize; bytes_to_copy = 0; delete [] this_name; } else { this_file = (ArchiveEntry *) FindName (this_name); if (!this_file) /* This means we found a entry in archive which is not * present in our `dir' array: this means either the ZIP * file has changed after we read the ZIP directory, * or this is a `pure directory' entry (which we ignore * during reading). In any case, just copy it unchanged * into the output file. */ goto skip_entry; delete [] this_name; if (this_file->info.csize != lfh.csize) goto temp_failed; /* Broken archive */ this_file->ReadExtraField (file, lfh.extra_field_length); bytes_to_skip = 0; bytes_to_copy = lfh.csize; if (!this_file->WriteLFH (temp)) goto temp_failed; /* Write error */ } } else if (memcmp (buff, hdr_central, sizeof (hdr_central)) == 0) { // central directory header ZIP_central_directory_file_header cdfh; if (!ReadCDFH (cdfh, file)) goto temp_failed; bytes_to_copy = 0; bytes_to_skip = cdfh.filename_length + cdfh.extra_field_length + cdfh.file_comment_length; } else if (memcmp (buff, hdr_endcentral, sizeof (hdr_endcentral)) == 0) { // end-of-central-directory header ZIP_end_central_dir_record ecdr; char buff [ZIP_END_CENTRAL_DIR_RECORD_SIZE]; if (fread (buff, 1, ZIP_END_CENTRAL_DIR_RECORD_SIZE, file) < ZIP_END_CENTRAL_DIR_RECORD_SIZE) goto temp_failed; LoadECDR (ecdr, buff); bytes_to_copy = 0; bytes_to_skip = ecdr.zipfile_comment_length; } else { // Unknown chunk type goto temp_failed; } /* endif */ if (bytes_to_skip) fseek (file, bytes_to_skip, SEEK_CUR); while (bytes_to_copy) { size_t size; if (bytes_to_copy > sizeof (buff)) size = sizeof (buff); else size = bytes_to_copy; if ((fread (buff, 1, size, file) < size) || (fwrite (buff, 1, size, temp) < size)) goto temp_failed; bytes_to_copy -= size; } } /* endwhile */ /* Now we have to append all files that were added to archive */ for (n = 0; n < lazy.Length (); n++) { ArchiveEntry *f = lazy.Get (n); if (!f->WriteFile (temp)) goto temp_failed; /* Write error */ } /* endfor */ /* And finaly write central directory structure */ if (!WriteCentralDirectory (temp)) goto temp_failed; /* Now copy temporary file into archive. If we'll get a error in process, */ /* we're lost! I don't know for a good solution for this without wasting */ /* disk space for yet another copy of the archive :-( */ { fseek (temp, 0, SEEK_END); size_t fsize = ftell (temp); fseek (temp, 0, SEEK_SET); fclose (file); if ((file = fopen (filename, "wb")) == NULL) { file = fopen (filename, "rb"); goto temp_failed; } while (fsize) { size_t bytes_read = fread (buff, 1, sizeof (buff), temp); if (fwrite (buff, 1, bytes_read, file) < bytes_read) { /* Yuck! Keep at least temporary file */ fclose (temp); fclose (file); file = fopen (filename, "rb"); return false; } fsize -= bytes_read; } /* Hurray! We're done */ fclose (file); file = fopen (filename, "rb"); } /* Now if we are here, all operations have been successful */ UpdateDirectory (); success = true; temp_failed: fclose (temp); unlink (temp_file); return success; }