Esempio n. 1
0
// 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;
}