blob_filecopy (int mode, const char *fname, KEYBOXBLOB blob,
               int secret, int for_openpgp, off_t start_offset)
{
  FILE *fp, *newfp;
  int rc=0;
  char *bakfname = NULL;
  char *tmpfname = NULL;
  char buffer[4096];  /* (Must be at least 32 bytes) */
  int nread, nbytes;

  /* Open the source file. Because we do a rename, we have to check the
     permissions of the file */
  if (access (fname, W_OK))
    return gpg_error_from_syserror ();

  fp = fopen (fname, "rb");
  if (mode == FILECOPY_INSERT && !fp && errno == ENOENT)
    {
      /* Insert mode but file does not exist:
         Create a new keybox file. */
      newfp = fopen (fname, "wb");
      if (!newfp )
        return gpg_error_from_syserror ();

      rc = _keybox_write_header_blob (newfp, for_openpgp);
      if (rc)
        {
          fclose (newfp);
          return rc;
        }

      rc = _keybox_write_blob (blob, newfp);
      if (rc)
        {
          fclose (newfp);
          return rc;
        }

      if ( fclose (newfp) )
        return gpg_error_from_syserror ();

/*        if (chmod( fname, S_IRUSR | S_IWUSR )) */
/*          { */
/*            log_debug ("%s: chmod failed: %s\n", fname, strerror(errno) ); */
/*            return KEYBOX_File_Error; */
/*          } */
      return 0; /* Ready. */
    }

  if (!fp)
    {
      rc = gpg_error_from_syserror ();
      goto leave;
    }

  /* Create the new file. */
  rc = create_tmp_file (fname, &bakfname, &tmpfname, &newfp);
  if (rc)
    {
      fclose (fp);
      fclose (newfp);
      goto leave;
    }

  /* prepare for insert */
  if (mode == FILECOPY_INSERT)
    {
      int first_record = 1;

      /* Copy everything to the new file.  If this is for OpenPGP, we
         make sure that the openpgp flag is set in the header.  (We
         failsafe the blob type.) */
      while ( (nread = fread (buffer, 1, DIM(buffer), fp)) > 0 )
        {
          if (first_record && for_openpgp
              && buffer[4] == KEYBOX_BLOBTYPE_HEADER)
            {
              first_record = 0;
              buffer[7] |= 0x02; /* OpenPGP data may be available.  */
            }

          if (fwrite (buffer, nread, 1, newfp) != 1)
            {
              rc = gpg_error_from_syserror ();
              fclose (fp);
              fclose (newfp);
              goto leave;
            }
        }
      if (ferror (fp))
        {
          rc = gpg_error_from_syserror ();
          fclose (fp);
          fclose (newfp);
          goto leave;
        }
    }

  /* Prepare for delete or update. */
  if ( mode == FILECOPY_DELETE || mode == FILECOPY_UPDATE )
    {
      off_t current = 0;

      /* Copy first part to the new file. */
      while ( current < start_offset )
        {
          nbytes = DIM(buffer);
          if (current + nbytes > start_offset)
              nbytes = start_offset - current;
          nread = fread (buffer, 1, nbytes, fp);
          if (!nread)
            break;
          current += nread;

          if (fwrite (buffer, nread, 1, newfp) != 1)
            {
              rc = gpg_error_from_syserror ();
              fclose (fp);
              fclose (newfp);
              goto leave;
            }
        }
      if (ferror (fp))
        {
          rc = gpg_error_from_syserror ();
          fclose (fp);
          fclose (newfp);
          goto leave;
        }

      /* Skip this blob. */
      rc = _keybox_read_blob (NULL, fp);
      if (rc)
        {
          fclose (fp);
          fclose (newfp);
          return rc;
        }
    }

  /* Do an insert or update. */
  if ( mode == FILECOPY_INSERT || mode == FILECOPY_UPDATE )
    {
      rc = _keybox_write_blob (blob, newfp);
      if (rc)
        {
          fclose (fp);
          fclose (newfp);
          return rc;
        }
    }

  /* Copy the rest of the packet for an delete or update. */
  if (mode == FILECOPY_DELETE || mode == FILECOPY_UPDATE)
    {
      while ( (nread = fread (buffer, 1, DIM(buffer), fp)) > 0 )
        {
          if (fwrite (buffer, nread, 1, newfp) != 1)
            {
              rc = gpg_error_from_syserror ();
              fclose (fp);
              fclose (newfp);
              goto leave;
            }
        }
      if (ferror (fp))
        {
          rc = gpg_error_from_syserror ();
          fclose (fp);
          fclose (newfp);
          goto leave;
        }
    }

  /* Close both files. */
  if (fclose(fp))
    {
      rc = gpg_error_from_syserror ();
      fclose (newfp);
      goto leave;
    }
  if (fclose(newfp))
    {
      rc = gpg_error_from_syserror ();
      goto leave;
    }

  rc = rename_tmp_file (bakfname, tmpfname, fname, secret);

 leave:
  xfree(bakfname);
  xfree(tmpfname);
  return rc;
}