Exemple #1
0
void u0048_multidag__add_dagnode(SG_context * pCtx,
								 char** ppszid, const char* pszidParent, SG_uint32 iDagNum, SG_repo* pRepo)
{
	char buf_tid[SG_TID_MAX_BUFFER_LENGTH];
    SG_dagnode* pdn = NULL;
	SG_repo_tx_handle* pTx;

	// create a TID just to get some random data.  use it to create a HID.
	// use the HID as the HID of a hypothetical changeset so that we can create the dagnode.

	VERIFY_ERR_CHECK(  SG_tid__generate(pCtx, buf_tid, sizeof(buf_tid))  );
	VERIFY_ERR_CHECK(  SG_repo__alloc_compute_hash__from_bytes(pCtx,
															   pRepo,
															   sizeof(buf_tid),
															   (SG_byte *)buf_tid,
															   ppszid)  );

    VERIFY_ERR_CHECK(  SG_dagnode__alloc(pCtx, &pdn,*ppszid, pszidParent ? 2: 1 )  );
    if (pszidParent)
    {
        VERIFY_ERR_CHECK(  SG_dagnode__add_parent(pCtx, pdn,pszidParent)  );
    }
    VERIFY_ERR_CHECK(  SG_dagnode__freeze(pCtx, pdn)  );
	VERIFY_ERR_CHECK(  SG_repo__begin_tx(pCtx, pRepo, &pTx)  );
	VERIFY_ERR_CHECK(  SG_repo__store_dagnode(pCtx, pRepo, pTx, iDagNum, pdn)  );
    pdn = NULL;
    VERIFY_ERR_CHECK(  SG_repo__commit_tx(pCtx, pRepo,&pTx)  );

    return;

fail:
    return;
}
void MyFn(one_dagnode)(SG_context * pCtx, SG_repo* pRepo)
{
	char* pId = NULL;
	SG_dagnode* pdnCreated = NULL;
	SG_dagnode* pdnFetched = NULL;
	SG_repo_tx_handle* pTx = NULL;
	char buf_tid[SG_TID_MAX_BUFFER_LENGTH];

	VERIFY_ERR_CHECK(  SG_tid__generate(pCtx, buf_tid, sizeof(buf_tid))  );
	VERIFY_ERR_CHECK(  SG_repo__alloc_compute_hash__from_bytes(pCtx,
															   pRepo,
															   sizeof(buf_tid),
															   (SG_byte *)buf_tid,
															   &pId)  );

	VERIFY_ERR_CHECK(  SG_dagnode__alloc(pCtx, &pdnCreated, pId, 1, 0)  );
	VERIFY_ERR_CHECK(  SG_dagnode__freeze(pCtx, pdnCreated)  );

	// Add dagnode.
	VERIFY_ERR_CHECK(  SG_repo__begin_tx(pCtx, pRepo, &pTx)  );
	VERIFY_ERR_CHECK(  SG_repo__store_dagnode(pCtx, pRepo, pTx, SG_DAGNUM__TESTING__NOTHING, pdnCreated)  );
    pdnCreated = NULL;

	// Should fail: tx not committed.
	VERIFY_ERR_CHECK_ERR_EQUALS_DISCARD(  SG_repo__fetch_dagnode(pCtx, pRepo, SG_DAGNUM__TESTING__NOTHING, pId, &pdnFetched),
										  SG_ERR_NOT_FOUND  );	// Dag node visible before repo tx committed.

	// Abort repo tx.
	VERIFY_ERR_CHECK(  SG_repo__abort_tx(pCtx, pRepo, &pTx)  );
	VERIFY_COND("SG_repo__abort_tx should null/free the repo transaction.", !pTx);

	// Should fail: tx aborted.
	VERIFY_ERR_CHECK_ERR_EQUALS_DISCARD(  SG_repo__fetch_dagnode(pCtx, pRepo, SG_DAGNUM__TESTING__NOTHING, pId, &pdnFetched),
										  SG_ERR_NOT_FOUND  ); // Dag node exists after repo tx abort

	// Write dagnode, commit tx.
	VERIFY_ERR_CHECK(  SG_repo__begin_tx(pCtx, pRepo, &pTx)  );
	VERIFY_ERR_CHECK(  SG_dagnode__alloc(pCtx, &pdnCreated, pId, 1, 0)  );
	VERIFY_ERR_CHECK(  SG_dagnode__freeze(pCtx, pdnCreated)  );
	VERIFY_ERR_CHECK(  SG_repo__store_dagnode(pCtx, pRepo, pTx, SG_DAGNUM__TESTING__NOTHING, pdnCreated)  );
    pdnCreated = NULL;
	VERIFY_ERR_CHECK(  SG_repo__commit_tx(pCtx, pRepo, &pTx)  );
	VERIFY_COND("SG_repo__commit_tx should null/free the repo transaction.", !pTx);

	// Read back the dagnode.  It should exist now.
	VERIFY_ERR_CHECK(  SG_repo__fetch_dagnode(pCtx, pRepo, SG_DAGNUM__TESTING__NOTHING, pId, &pdnFetched)  );

	// Fall through to common cleanup.

fail:
	SG_NULLFREE(pCtx, pId);
	SG_DAGNODE_NULLFREE(pCtx, pdnCreated);
	SG_DAGNODE_NULLFREE(pCtx, pdnFetched);
}
void sg_sync_client__http__request_fragball(
    SG_context* pCtx,
    SG_sync_client* pSyncClient,
    SG_vhash* pvhRequest,
    SG_bool bProgressIfPossible,
    const SG_pathname* pStagingPathname,
    char** ppszFragballName)
{
    char* pszFragballName = NULL;
    SG_pathname* pPathFragball = NULL;
    SG_string* pstrRequest = NULL;
    char* pszUrl = NULL;

    SG_NULLARGCHECK_RETURN(pSyncClient);

    SG_ERR_CHECK(  SG_allocN(pCtx, SG_TID_MAX_BUFFER_LENGTH, pszFragballName)  );
    SG_ERR_CHECK(  SG_tid__generate(pCtx, pszFragballName, SG_TID_MAX_BUFFER_LENGTH)  );


    SG_ERR_CHECK(  _get_sync_url(pCtx, pSyncClient->psz_remote_repo_spec, SYNC_URL_SUFFIX FRAGBALL_URL_SUFFIX, NULL, NULL, &pszUrl)  );


    if (pvhRequest)
    {
        SG_ERR_CHECK(  SG_STRING__ALLOC(pCtx, &pstrRequest)  );
        SG_ERR_CHECK(  SG_vhash__to_json(pCtx, pvhRequest, pstrRequest)  );
    }

    SG_ERR_CHECK(  SG_pathname__alloc__pathname_sz(pCtx, &pPathFragball, pStagingPathname, (const char*)pszFragballName)  );
    SG_ERR_CHECK(  do_url(
                       pCtx,
                       pszUrl,
                       "POST",
                       pstrRequest ? SG_string__sz(pstrRequest) : NULL,
                       pSyncClient->psz_username,
                       pSyncClient->psz_password,
                       NULL,
                       pPathFragball,
                       bProgressIfPossible
                   )  );


    SG_RETURN_AND_NULL(pszFragballName, ppszFragballName);

    /* fall through */
fail:
    SG_NULLFREE(pCtx, pszFragballName);
    SG_PATHNAME_NULLFREE(pCtx, pPathFragball);
    SG_NULLFREE(pCtx, pszUrl);
    SG_STRING_NULLFREE(pCtx, pstrRequest);
}
void SG_tid__alloc(SG_context * pCtx,
				   char ** ppszTid)
{
	char * pszTid_Allocated = NULL;

	SG_NULLARGCHECK_RETURN( ppszTid );

	SG_ERR_CHECK_RETURN(  SG_alloc(pCtx, 1, SG_TID_MAX_BUFFER_LENGTH, &pszTid_Allocated)  );
	SG_ERR_CHECK(  SG_tid__generate(pCtx, pszTid_Allocated, SG_TID_MAX_BUFFER_LENGTH)  );

	*ppszTid = pszTid_Allocated;
	return;

fail:
	SG_NULLFREE(pCtx, pszTid_Allocated);
}
void sg_sync_client__http__pull_clone(
    SG_context* pCtx,
    SG_sync_client* pSyncClient,
    SG_vhash* pvh_clone_request,
    const SG_pathname* pStagingPathname,
    char** ppszFragballName)
{
    SG_vhash* pvhRequest = NULL;
    char* pszFragballName = NULL;
    SG_pathname* pPathFragball = NULL;
    SG_string* pstrRequest = NULL;
    char* pszUrl = NULL;

    SG_ERR_CHECK(  SG_log__push_operation(pCtx, "Requesting repository from server", SG_LOG__FLAG__NONE)  );

    SG_NULLARGCHECK_RETURN(pSyncClient);

    SG_ERR_CHECK(  SG_allocN(pCtx, SG_TID_MAX_BUFFER_LENGTH, pszFragballName)  );
    SG_ERR_CHECK(  SG_tid__generate(pCtx, pszFragballName, SG_TID_MAX_BUFFER_LENGTH)  );

    SG_ERR_CHECK(  _get_sync_url(pCtx, pSyncClient->psz_remote_repo_spec, SYNC_URL_SUFFIX FRAGBALL_URL_SUFFIX, NULL, NULL, &pszUrl)  );


    SG_ERR_CHECK(  SG_VHASH__ALLOC(pCtx, &pvhRequest)  );
    SG_ERR_CHECK(  SG_vhash__add__null(pCtx, pvhRequest, SG_SYNC_STATUS_KEY__CLONE)  );
    if (pvh_clone_request)
    {
        SG_ERR_CHECK(  SG_vhash__addcopy__vhash(pCtx, pvhRequest, SG_SYNC_STATUS_KEY__CLONE_REQUEST, pvh_clone_request)  );
    }
    SG_ERR_CHECK(  SG_STRING__ALLOC__RESERVE(pCtx, &pstrRequest, 50)  );
    SG_ERR_CHECK(  SG_vhash__to_json(pCtx, pvhRequest, pstrRequest)  );

    SG_ERR_CHECK(  SG_pathname__alloc__pathname_sz(pCtx, &pPathFragball, pStagingPathname, (const char*)pszFragballName)  );
    SG_ERR_CHECK(  do_url(pCtx, pszUrl, "POST", SG_string__sz(pstrRequest), pSyncClient->psz_username, pSyncClient->psz_password, NULL, pPathFragball, SG_TRUE)  );

    SG_RETURN_AND_NULL(pszFragballName, ppszFragballName);

    /* fall through */
fail:
    SG_log__pop_operation(pCtx);
    SG_NULLFREE(pCtx, pszFragballName);
    SG_VHASH_NULLFREE(pCtx, pvhRequest);
    SG_PATHNAME_NULLFREE(pCtx, pPathFragball);
    SG_NULLFREE(pCtx, pszUrl);
    SG_STRING_NULLFREE(pCtx, pstrRequest);
}
void MyFn(no_repo_tx)(SG_context * pCtx, SG_repo* pRepo)
{
	SG_repo_tx_handle* pTx = NULL;
	SG_repo_store_blob_handle* pbh;

	SG_byte* pBufIn = NULL;
	SG_uint32 lenBufIn = 0;
	char* pszHidReturned = NULL;

	char* pId = NULL;
	SG_dagnode* pdnCreated = NULL;

	char buf_tid[SG_TID_MAX_BUFFER_LENGTH];

	VERIFY_ERR_CHECK_ERR_EQUALS_DISCARD(  SG_repo__commit_tx(pCtx, pRepo, NULL),
										  SG_ERR_INVALIDARG  );		// commit_tx without repo tx didn't fail with INVALIDARG.

	VERIFY_ERR_CHECK_ERR_EQUALS_DISCARD(  SG_repo__abort_tx(pCtx, pRepo, NULL),
										  SG_ERR_INVALIDARG  );		// abort_tx without repo tx didn't fail with INVALIDARG.

	VERIFY_ERR_CHECK_ERR_EQUALS_DISCARD(  SG_repo__store_blob__begin(pCtx, pRepo, NULL, SG_BLOBENCODING__FULL, NULL, 10, 0, NULL, &pbh),
										  SG_ERR_INVALIDARG  );		// store_blob__begin without repo tx didn't fail with INVALIDARG.

	// Create a repo tx so we can test the other blob functions.
	VERIFY_ERR_CHECK(  SG_repo__begin_tx(pCtx, pRepo, &pTx)  );
	VERIFY_ERR_CHECK(  MyFn(alloc_random_buffer)(pCtx, &pBufIn, &lenBufIn)  );

	VERIFY_ERR_CHECK(  SG_repo__store_blob__begin(pCtx, pRepo, pTx, SG_BLOBENCODING__FULL, NULL, lenBufIn, 0, NULL, &pbh)  );
	VERIFY_ERR_CHECK(  SG_repo__store_blob__chunk(pCtx, pRepo, pbh, lenBufIn, pBufIn, NULL)  );

	VERIFY_ERR_CHECK_ERR_EQUALS_DISCARD(  SG_repo__store_blob__end(pCtx, pRepo, NULL, &pbh, &pszHidReturned),
										  SG_ERR_INVALIDARG  );		// store_blob__end without repo tx didn't fail with INVALIDARG.

	VERIFY_ERR_CHECK_ERR_EQUALS_DISCARD(  SG_repo__store_blob__abort(pCtx, pRepo, NULL, &pbh),
										  SG_ERR_INVALIDARG  );		// store_blob__abort without repo tx didn't fail with INVALIDARG.

	VERIFY_ERR_CHECK(  SG_repo__store_blob__end(pCtx, pRepo, pTx, &pbh, &pszHidReturned)  );
	VERIFY_ERR_CHECK(  SG_repo__commit_tx(pCtx, pRepo, &pTx)  );

	// create a TID just to get some random data.  use it to create a HID.
	// use the HID as the HID of a hypothetical changeset so that we can create the dagnode.

	VERIFY_ERR_CHECK(  SG_tid__generate(pCtx, buf_tid, sizeof(buf_tid))  );
	VERIFY_ERR_CHECK(  SG_repo__alloc_compute_hash__from_bytes(pCtx,
															   pRepo,
															   sizeof(buf_tid),
															   (SG_byte *)buf_tid,
															   &pId)  );

	VERIFY_ERR_CHECK(  SG_dagnode__alloc(pCtx, &pdnCreated, pId, 1, 0)  );
	VERIFY_ERR_CHECK(  SG_dagnode__freeze(pCtx, pdnCreated)  );

	VERIFY_ERR_CHECK_ERR_EQUALS_DISCARD(  SG_repo__store_dagnode(pCtx, pRepo, NULL, SG_DAGNUM__TESTING__NOTHING, pdnCreated),
										  SG_ERR_INVALIDARG  );		// store_dagnode without repo tx didn't fail with INVALIDARG.

	// We're intentionally not testing the higher-level store_blob_from_memory and store_blob_from_file
	// routines here because they're just wrappers for the begin/chunk/end routines we do test.

	// Fall through to common cleanup.

fail:
	SG_NULLFREE(pCtx, pszHidReturned);
	SG_NULLFREE(pCtx, pBufIn);
	SG_NULLFREE(pCtx, pId);
	SG_DAGNODE_NULLFREE(pCtx, pdnCreated);
}
void SG_sync_remote__request_fragball(
	SG_context* pCtx,
	SG_repo* pRepo,
	const SG_pathname* pFragballDirPathname,
	SG_vhash* pvhRequest,
	char** ppszFragballName)
{
	SG_pathname* pFragballPathname = NULL;
	SG_uint64* paDagNums = NULL;
	SG_string* pstrFragballName = NULL;
	SG_rbtree* prbDagnodes = NULL;
	SG_rbtree_iterator* pit = NULL;
	SG_rev_spec* pRevSpec = NULL;
	SG_stringarray* psaFullHids = NULL;
	SG_rbtree* prbDagnums = NULL;
	SG_dagfrag* pFrag = NULL;
	char* pszRepoId = NULL;
	char* pszAdminId = NULL;
    SG_fragball_writer* pfb = NULL;

	SG_NULLARGCHECK_RETURN(pRepo);
	SG_NULLARGCHECK_RETURN(pFragballDirPathname);

    {
        char buf_filename[SG_TID_MAX_BUFFER_LENGTH];
        SG_ERR_CHECK(  SG_tid__generate(pCtx, buf_filename, sizeof(buf_filename))  );
        SG_ERR_CHECK(  SG_PATHNAME__ALLOC__PATHNAME_SZ(pCtx, &pFragballPathname, pFragballDirPathname, buf_filename)  );
    }

	if (!pvhRequest)
	{
		// Add leaves from every dag to the fragball.
		SG_uint32 count_dagnums;
		SG_uint32 i;

        SG_ERR_CHECK(  SG_fragball_writer__alloc(pCtx, pRepo, pFragballPathname, SG_TRUE, 2, &pfb)  );
		SG_ERR_CHECK(  SG_repo__list_dags(pCtx, pRepo, &count_dagnums, &paDagNums)  );

		for (i=0; i<count_dagnums; i++)
		{
			SG_ERR_CHECK(  SG_repo__fetch_dag_leaves(pCtx, pRepo, paDagNums[i], &prbDagnodes)  );
			SG_ERR_CHECK(  SG_fragball__write__dagnodes(pCtx, pfb, paDagNums[i], prbDagnodes)  );
			SG_RBTREE_NULLFREE(pCtx, prbDagnodes);
		}

		SG_ERR_CHECK(  SG_pathname__get_last(pCtx, pFragballPathname, &pstrFragballName)  );
		SG_ERR_CHECK(  SG_STRDUP(pCtx, SG_string__sz(pstrFragballName), ppszFragballName)  );
        SG_ERR_CHECK(  SG_fragball_writer__close(pCtx, pfb)  );
	}
	else
	{
		// Specific dags/nodes were requested. Build that fragball.
		SG_bool found;

#if TRACE_SYNC_REMOTE && 0
		SG_ERR_CHECK(  SG_vhash_debug__dump_to_console__named(pCtx, pvhRequest, "fragball request")  );
#endif

		SG_ERR_CHECK(  SG_vhash__has(pCtx, pvhRequest, SG_SYNC_STATUS_KEY__CLONE, &found)  );
		if (found)
		{
            // SG_SYNC_STATUS_KEY__CLONE_REQUEST is currently ignored
            SG_ERR_CHECK(  SG_repo__fetch_repo__fragball(pCtx, pRepo, 3, pFragballDirPathname, ppszFragballName) );
		}
		else
		{
			// Not a full clone.

            SG_ERR_CHECK(  SG_fragball_writer__alloc(pCtx, pRepo, pFragballPathname, SG_TRUE, 2, &pfb)  );
			SG_ERR_CHECK(  SG_vhash__has(pCtx, pvhRequest, SG_SYNC_STATUS_KEY__SINCE, &found)  );
			if (found)
			{
                SG_vhash* pvh_since = NULL;

                SG_ERR_CHECK(  SG_vhash__get__vhash(pCtx, pvhRequest, SG_SYNC_STATUS_KEY__SINCE, &pvh_since)  );

                SG_ERR_CHECK(  _do_since(pCtx, pRepo, pvh_since, pfb)  );
            }

			SG_ERR_CHECK(  SG_vhash__has(pCtx, pvhRequest, SG_SYNC_STATUS_KEY__DAGS, &found)  );
			if (found)
			{
				// Specific Dagnodes were requested. Add just those nodes to our "start from" rbtree.

				SG_vhash* pvhDags;
				SG_uint32 count_requested_dagnums;
				SG_uint32 i;
				const SG_variant* pvRevSpecs = NULL;
				SG_vhash* pvhRevSpec = NULL;

				// For each requested dag, get rev spec request.
				SG_ERR_CHECK(  SG_vhash__get__vhash(pCtx, pvhRequest, SG_SYNC_STATUS_KEY__DAGS, &pvhDags)  );
				SG_ERR_CHECK(  SG_vhash__count(pCtx, pvhDags, &count_requested_dagnums)  );
				if (count_requested_dagnums)
					SG_ERR_CHECK(  SG_repo__list_dags__rbtree(pCtx, pRepo, &prbDagnums)  );
				for (i=0; i<count_requested_dagnums; i++)
				{
					SG_bool isValidDagnum = SG_FALSE;
					SG_bool bSpecificNodesRequested = SG_FALSE;
					const char* pszRefDagNum = NULL;
					SG_uint64 iDagnum;

					// Get the dag's missing node vhash.
					SG_ERR_CHECK(  SG_vhash__get_nth_pair(pCtx, pvhDags, i, &pszRefDagNum, &pvRevSpecs)  );

					// Verify that requested dagnum exists
					SG_ERR_CHECK(  SG_rbtree__find(pCtx, prbDagnums, pszRefDagNum, &isValidDagnum, NULL)  );
					if (!isValidDagnum)
                        continue;

					SG_ERR_CHECK(  SG_dagnum__from_sz__hex(pCtx, pszRefDagNum, &iDagnum)  );
					
					if (pvRevSpecs && pvRevSpecs->type != SG_VARIANT_TYPE_NULL)
					{
						SG_uint32 countRevSpecs = 0;
						
						SG_ERR_CHECK(  SG_variant__get__vhash(pCtx, pvRevSpecs, &pvhRevSpec)  );
						SG_ERR_CHECK(  SG_rev_spec__from_vash(pCtx, pvhRevSpec, &pRevSpec)  );

						// Process the rev spec for each dag
						SG_ERR_CHECK(  SG_rev_spec__count(pCtx, pRevSpec, &countRevSpecs)  );
						if (countRevSpecs > 0)
						{
							bSpecificNodesRequested = SG_TRUE;

							SG_ERR_CHECK(  SG_rev_spec__get_all__repo(pCtx, pRepo, pRevSpec, SG_TRUE, &psaFullHids, NULL)  );
							SG_ERR_CHECK(  SG_stringarray__to_rbtree_keys(pCtx, psaFullHids, &prbDagnodes)  );
							SG_STRINGARRAY_NULLFREE(pCtx, psaFullHids);
						}
						SG_REV_SPEC_NULLFREE(pCtx, pRevSpec);
					}

					if (!bSpecificNodesRequested)
					{
						// When no specific nodes are in the request, add all leaves.
						SG_ERR_CHECK(  SG_repo__fetch_dag_leaves(pCtx, pRepo, iDagnum, &prbDagnodes)  );
					}

					if (prbDagnodes) // can be null when leaves of an empty dag are requested
					{
						// Get the leaves of the other repo, which we need to connect to.
						SG_ERR_CHECK(  SG_vhash__has(pCtx, pvhRequest, SG_SYNC_STATUS_KEY__LEAVES, &found)  );
						if (found)
						{
							SG_vhash* pvhRefAllLeaves;
							SG_vhash* pvhRefDagLeaves;
							SG_ERR_CHECK(  SG_vhash__get__vhash(pCtx, pvhRequest, SG_SYNC_STATUS_KEY__LEAVES, &pvhRefAllLeaves)  );
							SG_ERR_CHECK(  SG_vhash__has(pCtx, pvhRequest, pszRefDagNum, &found)  );
							{
								SG_ERR_CHECK(  SG_vhash__get__vhash(pCtx, pvhRefAllLeaves, pszRefDagNum, &pvhRefDagLeaves)  );
								SG_ERR_CHECK(  SG_sync__build_best_guess_dagfrag(pCtx, pRepo, iDagnum, 
									prbDagnodes, pvhRefDagLeaves, &pFrag)  );
							}
						}
						else
						{
							// The other repo's leaves weren't provided: add just the requested nodes, make no attempt to connect.
							SG_ERR_CHECK(  SG_repo__get_repo_id(pCtx, pRepo, &pszRepoId)  );
							SG_ERR_CHECK(  SG_repo__get_admin_id(pCtx, pRepo, &pszAdminId)  );
							SG_ERR_CHECK(  SG_dagfrag__alloc(pCtx, &pFrag, pszRepoId, pszAdminId, iDagnum)  );
							SG_ERR_CHECK(  SG_dagfrag__load_from_repo__simple(pCtx, pFrag, pRepo, prbDagnodes)  );
							SG_NULLFREE(pCtx, pszRepoId);
							SG_NULLFREE(pCtx, pszAdminId);
						}

						SG_ERR_CHECK(  SG_fragball__write__frag(pCtx, pfb, pFrag)  );
						
						SG_RBTREE_NULLFREE(pCtx, prbDagnodes);
						SG_DAGFRAG_NULLFREE(pCtx, pFrag);
					}

				} // dagnum loop
			} // if "dags" exists

			/* Add requested blobs to the fragball */
			SG_ERR_CHECK(  SG_vhash__has(pCtx, pvhRequest, SG_SYNC_STATUS_KEY__BLOBS, &found)  );
			if (found)
			{
				// Blobs were requested.
				SG_vhash* pvhBlobs;
				SG_ERR_CHECK(  SG_vhash__get__vhash(pCtx, pvhRequest, SG_SYNC_STATUS_KEY__BLOBS, &pvhBlobs)  );
				SG_ERR_CHECK(  SG_sync__add_blobs_to_fragball(pCtx, pfb, pvhBlobs)  );
			}

			SG_ERR_CHECK(  SG_pathname__get_last(pCtx, pFragballPathname, &pstrFragballName)  );
			SG_ERR_CHECK(  SG_STRDUP(pCtx, SG_string__sz(pstrFragballName), ppszFragballName)  );
		}
        SG_ERR_CHECK(  SG_fragball_writer__close(pCtx, pfb)  );
	}

	/* fallthru */
fail:
	// If we had an error, delete the half-baked fragball.
	if (pFragballPathname && SG_context__has_err(pCtx))
    {
		SG_ERR_IGNORE(  SG_fsobj__remove__pathname(pCtx, pFragballPathname)  );
    }

	SG_PATHNAME_NULLFREE(pCtx, pFragballPathname);
	SG_NULLFREE(pCtx, paDagNums);
	SG_STRING_NULLFREE(pCtx, pstrFragballName);
	SG_RBTREE_NULLFREE(pCtx, prbDagnodes);
	SG_RBTREE_ITERATOR_NULLFREE(pCtx, pit);
	SG_RBTREE_NULLFREE(pCtx, prbDagnums);
	SG_REV_SPEC_NULLFREE(pCtx, pRevSpec);
	SG_STRINGARRAY_NULLFREE(pCtx, psaFullHids);
	SG_DAGFRAG_NULLFREE(pCtx, pFrag);
	SG_NULLFREE(pCtx, pszRepoId);
	SG_NULLFREE(pCtx, pszAdminId);
    SG_FRAGBALL_WRITER_NULLFREE(pCtx, pfb);
}
void sg_sync_client__http__push_begin(
    SG_context* pCtx,
    SG_sync_client * pSyncClient,
    SG_pathname** pFragballDirPathname,
    SG_sync_client_push_handle** ppPush)
{
    char* pszUrl = NULL;
    sg_sync_client_http_push_handle* pPush = NULL;
    SG_vhash* pvhResponse = NULL;
    SG_pathname* pPathUserTemp = NULL;
    SG_pathname* pPathFragballDir = NULL;
    char bufTid[SG_TID_MAX_BUFFER_LENGTH];
    SG_string* pstr = NULL;

    SG_NULLARGCHECK_RETURN(pSyncClient);
    SG_NULLARGCHECK_RETURN(pFragballDirPathname);
    SG_NULLARGCHECK_RETURN(ppPush);

    // Get the URL we're going to post to
    SG_ERR_CHECK(  _get_sync_url(pCtx, pSyncClient->psz_remote_repo_spec, SYNC_URL_SUFFIX JSON_URL_SUFFIX, NULL, NULL, &pszUrl)  );

    SG_ERR_CHECK(  do_url(pCtx, pszUrl, "POST", NULL, pSyncClient->psz_username, pSyncClient->psz_password, &pstr, NULL, SG_TRUE)  );
    SG_ERR_CHECK(  SG_vhash__alloc__from_json__sz(pCtx, &pvhResponse, SG_string__sz(pstr))  );
    SG_STRING_NULLFREE(pCtx, pstr);

    // Alloc a push handle.  Stuff the push ID we received into it.
    {
        const char* pszRef = NULL;
        SG_ERR_CHECK(  SG_alloc(pCtx, 1, sizeof(sg_sync_client_http_push_handle), &pPush)  );
        SG_ERR_CHECK(  SG_vhash__get__sz(pCtx, pvhResponse, PUSH_ID_KEY, &pszRef)  );
        SG_ERR_CHECK(  SG_strdup(pCtx, pszRef, &pPush->pszPushId)  );
    }

    // Create a temporary local directory for stashing fragballs before shipping them over the network.
    SG_ERR_CHECK(  SG_PATHNAME__ALLOC__USER_TEMP_DIRECTORY(pCtx, &pPathUserTemp)  );
    SG_ERR_CHECK(  SG_tid__generate(pCtx, bufTid, SG_TID_MAX_BUFFER_LENGTH)  );
    SG_ERR_CHECK(  SG_PATHNAME__ALLOC__PATHNAME_SZ(pCtx, &pPathFragballDir, pPathUserTemp, bufTid)  );
    SG_ERR_CHECK(  SG_fsobj__mkdir__pathname(pCtx, pPathFragballDir)  );

    // Tell caller where to stash fragballs for this push.
    SG_RETURN_AND_NULL(pPathFragballDir, pFragballDirPathname);

    // Return the new push handle.
    *ppPush = (SG_sync_client_push_handle*)pPush;
    pPush = NULL;

    /* fall through */
fail:
    SG_STRING_NULLFREE(pCtx, pstr);
    if(SG_context__err_equals(pCtx, SG_ERR_SERVER_HTTP_ERROR))
    {
        const char * szInfo = NULL;
        if(SG_IS_OK(SG_context__err_get_description(pCtx, &szInfo)) && strcmp(szInfo, "405")==0)
            SG_ERR_RESET_THROW(SG_ERR_SERVER_DOESNT_ACCEPT_PUSHES);
    }
    _NULLFREE_PUSH_HANDLE(pCtx, pPush);
    SG_NULLFREE(pCtx, pszUrl);
    SG_PATHNAME_NULLFREE(pCtx, pPathUserTemp);
    SG_PATHNAME_NULLFREE(pCtx, pPathFragballDir);
    SG_VHASH_NULLFREE(pCtx, pvhResponse);
}
void SG_tid__generate_hack(SG_context * pCtx,
						   char * bufTid)
{
	SG_ERR_CHECK_RETURN(  SG_tid__generate(pCtx, bufTid, SG_TID_MAX_BUFFER_LENGTH)  );
}