do_copy (int mode, const char *fname, KBNODE root,
         off_t start_offset, unsigned int n_packets )
{
    IOBUF fp, newfp;
    int rc=0;
    char *bakfname = NULL;
    char *tmpfname = NULL;

    /* 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 = iobuf_open (fname);
    if (mode == 1 && !fp && errno == ENOENT) {
	/* insert mode but file does not exist: create a new file */
	KBNODE kbctx, node;
	mode_t oldmask;

	oldmask=umask(077);
        if (is_secured_filename (fname)) {
            newfp = NULL;
            gpg_err_set_errno (EPERM);
        }
        else
            newfp = iobuf_create (fname, 1);
	umask(oldmask);
	if( !newfp )
	  {
            rc = gpg_error_from_syserror ();
	    log_error (_("can't create '%s': %s\n"), fname, strerror(errno));
	    return rc;
	  }
	if( !opt.quiet )
	    log_info(_("%s: keyring created\n"), fname );

	kbctx=NULL;
	while ( (node = walk_kbnode( root, &kbctx, 0 )) ) {
	    if( (rc = build_packet( newfp, node->pkt )) ) {
		log_error("build_packet(%d) failed: %s\n",
			    node->pkt->pkttype, gpg_strerror (rc) );
		iobuf_cancel(newfp);
		return rc;
	    }
	}
	if( iobuf_close(newfp) ) {
            rc = gpg_error_from_syserror ();
	    log_error ("%s: close failed: %s\n", fname, strerror(errno));
	    return rc;
	}
	return 0; /* ready */
    }

    if( !fp )
      {
        rc = gpg_error_from_syserror ();
	log_error(_("can't open '%s': %s\n"), fname, strerror(errno) );
	goto leave;
      }

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

    if( mode == 1 ) { /* insert */
	/* copy everything to the new file */
	rc = copy_all_packets (fp, newfp);
	if( rc != -1 ) {
	    log_error("%s: copy to '%s' failed: %s\n",
		      fname, tmpfname, gpg_strerror (rc) );
	    iobuf_close(fp);
	    iobuf_cancel(newfp);
	    goto leave;
	}
	rc = 0;
    }

    if( mode == 2 || mode == 3 ) { /* delete or update */
	/* copy first part to the new file */
	rc = copy_some_packets( fp, newfp, start_offset );
	if( rc ) { /* should never get EOF here */
	    log_error ("%s: copy to '%s' failed: %s\n",
                       fname, tmpfname, gpg_strerror (rc) );
	    iobuf_close(fp);
	    iobuf_cancel(newfp);
	    goto leave;
	}
	/* skip this keyblock */
	assert( n_packets );
	rc = skip_some_packets( fp, n_packets );
	if( rc ) {
	    log_error("%s: skipping %u packets failed: %s\n",
			    fname, n_packets, gpg_strerror (rc));
	    iobuf_close(fp);
	    iobuf_cancel(newfp);
	    goto leave;
	}
    }

    if( mode == 1 || mode == 3 ) { /* insert or update */
        rc = write_keyblock (newfp, root);
        if (rc) {
          iobuf_close(fp);
          iobuf_cancel(newfp);
          goto leave;
        }
    }

    if( mode == 2 || mode == 3 ) { /* delete or update */
	/* copy the rest */
	rc = copy_all_packets( fp, newfp );
	if( rc != -1 ) {
	    log_error("%s: copy to '%s' failed: %s\n",
		      fname, tmpfname, gpg_strerror (rc) );
	    iobuf_close(fp);
	    iobuf_cancel(newfp);
	    goto leave;
	}
	rc = 0;
    }

    /* close both files */
    if( iobuf_close(fp) ) {
        rc = gpg_error_from_syserror ();
	log_error("%s: close failed: %s\n", fname, strerror(errno) );
	goto leave;
    }
    if( iobuf_close(newfp) ) {
        rc = gpg_error_from_syserror ();
	log_error("%s: close failed: %s\n", tmpfname, strerror(errno) );
	goto leave;
    }

    rc = rename_tmp_file (bakfname, tmpfname, fname);

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