static void _fetch_size_of_blob(SG_context * pCtx, SG_wc_tx * pWcTx, const char * pszHidContent, SG_uint64 * pSize) { SG_uint64 len = 0; SG_ERR_CHECK_RETURN( SG_repo__fetch_blob__begin(pCtx, pWcTx->pDb->pRepo, pszHidContent, SG_TRUE, NULL, NULL, NULL, &len, NULL) ); *pSize = len; }
void MyFn(create_blob_from_bytes)(SG_context * pCtx, SG_repo * pRepo, SG_uint32 lenBuf1, const char * szSrc) { // create a large buffer containing some known data. // use it to create a blob directly from the buffer. // read it back from the repo and verify it. char* pszidTidRandom1 = NULL; char* pszidTidRandom2 = NULL; SG_uint32 lenSrc; char* pszidHidBlob1 = NULL; char* pszidHidBlob1Dup = NULL; char* pszidHidBlob2 = NULL; char* pszidHidVerify1 = NULL; char* pszidHidVerify2 = NULL; SG_bool bEqual; char * pbuf1 = NULL; char * pbuf2 = NULL; char * pbuf1End; char * p1; SG_uint64 lenBuf2; SG_repo_tx_handle* pTx = NULL; SG_repo_fetch_blob_handle* pFetchHandle = NULL; SG_uint64 lenAbortedBlob; SG_uint32 lenGotAbortedBlob; SG_bool b_done = SG_FALSE; ////////////////////////////////////////////////////////////////// SG_ERR_IGNORE( SG_tid__alloc2(pCtx, &pszidTidRandom1, 32) ); SG_ERR_IGNORE( SG_tid__alloc2(pCtx, &pszidTidRandom2, 32) ); if (lenBuf1 < 100) lenBuf1 += 100; pbuf1 = (char *)SG_calloc(1,lenBuf1+1); pbuf1End = pbuf1+lenBuf1; p1 = pbuf1; // write random gid at the beginning of the buffer // so that we won't get collisions if we are called // multiple times. memcpy(p1,pszidTidRandom1,strlen(pszidTidRandom1)); p1 += strlen(pszidTidRandom1); *p1++ = '\n'; // generate lots of data in the file so that we'll cause the // blob routines to exercise the chunking stuff. lenSrc = (SG_uint32)strlen(szSrc); while (p1+lenSrc < pbuf1End) { memcpy(p1,szSrc,lenSrc); p1 += lenSrc; } // use the buffer to create a blob in the repo. we use lenBuf1 as the // length rather than (p1-pbuf1) so we may have some nulls at the end. // hope this is ok??? i guess we'll find out... VERIFY_ERR_CHECK_DISCARD( SG_repo__begin_tx(pCtx, pRepo, &pTx) ); VERIFY_ERR_CHECK_DISCARD( SG_repo__store_blob_from_memory(pCtx, pRepo,pTx,NULL,SG_FALSE,(SG_byte *)pbuf1,lenBuf1,&pszidHidBlob1) ); VERIFY_ERR_CHECK_DISCARD( SG_repo__commit_tx(pCtx, pRepo, &pTx) ); INFOP("create_blob_from_bytes",("Created blob [%s]",(pszidHidBlob1))); ////////////////////////////////////////////////////////////////// // try to create blob again and verify we get an duplicate-hid error. // Ian TODO: Put this back when SG_ERR_BLOBFILEALREADYEXISTS is replaced. // VERIFY_ERR_CHECK_DISCARD( SG_repo__begin_tx(pCtx, pRepo, &pTx) ); // err = SG_repo__store_blob_from_memory(pRepo,pTx,SG_FALSE,(SG_byte *)pbuf1,lenBuf1,&pszidHidBlob1Dup); // VERIFYP_CTX_ERR_IS("create_blob_from_bytes(duplicate)", pCtx, SG_ERR_BLOBFILEALREADYEXISTS, ("Duplicate create failed [%s][%s]",pszidHidBlob1,pszidHidBlob1Dup)); // VERIFY_ERR_CHECK_DISCARD( SG_repo__commit_tx(pCtx, pRepo, SG_DAGNUM__NONE, NULL, &pTx) ); ////////////////////////////////////////////////////////////////// // abort a fetch, ensure it doesn't interfere with subsequent complete fetch VERIFY_ERR_CHECK_DISCARD( SG_repo__fetch_blob__begin(pCtx, pRepo, pszidHidBlob1, SG_TRUE, NULL, NULL, NULL, NULL, &lenAbortedBlob, &pFetchHandle) ); // We want to abort mid-blob, so we set an arbitrary (but small) chunk size, and verify that the blob is // bigger than it. VERIFY_ERR_CHECK_DISCARD( SG_repo__fetch_blob__chunk(pCtx, pRepo, pFetchHandle, 64, (SG_byte*)pbuf1, &lenGotAbortedBlob, &b_done) ); VERIFY_COND("create_blob_from_bytes(fetch abort sufficient length)", lenAbortedBlob > lenGotAbortedBlob); VERIFY_ERR_CHECK_DISCARD( SG_repo__fetch_blob__abort(pCtx, pRepo, &pFetchHandle) ); VERIFY_COND("create_blob_from_bytes(fetch abort freed handle)", !pFetchHandle); ////////////////////////////////////////////////////////////////// // fetch blob into a new buffer and verify that it matches. VERIFY_ERR_CHECK_DISCARD( SG_repo__fetch_blob_into_memory(pCtx, pRepo,pszidHidBlob1,(SG_byte **)&pbuf2,&lenBuf2) ); VERIFY_COND("create_blob_from_bytes(fetch blob)",(lenBuf2 == (SG_uint64)lenBuf1)); ////////////////////////////////////////////////////////////////// // verify that the contents of buf-2 is identical to the contents of buf-1. // (we already know that the HIDs match and was verified during the fetch, // but there are times when the HID is just being used as a key -- it // doesn't mean that what we actually restored is correct. VERIFY_ERR_CHECK_DISCARD( SG_repo__alloc_compute_hash__from_bytes(pCtx, pRepo, lenBuf1, (SG_byte *)pbuf1, &pszidHidVerify1) ); VERIFY_ERR_CHECK_DISCARD( SG_repo__alloc_compute_hash__from_bytes(pCtx, pRepo, (SG_uint32)lenBuf2, (SG_byte *)pbuf2, &pszidHidVerify2) ); bEqual = (0 == (strcmp(pszidHidVerify1,pszidHidVerify2))); VERIFY_COND("create_blob_from_bytes(verify v1==v2)",bEqual); bEqual = (0 == (strcmp(pszidHidVerify1,pszidHidBlob1))); VERIFY_COND("create_blob_from_bytes(verify v1==id)",bEqual); VERIFY_COND("creata_blob_from_bytes(memcmp)",(memcmp(pbuf1,pbuf2,lenBuf1)==0)); ////////////////////////////////////////////////////////////////// // cleanup SG_NULLFREE(pCtx, pbuf1); SG_NULLFREE(pCtx, pbuf2); SG_NULLFREE(pCtx, pszidTidRandom1); SG_NULLFREE(pCtx, pszidTidRandom2); SG_NULLFREE(pCtx, pszidHidBlob1); SG_NULLFREE(pCtx, pszidHidBlob1Dup); SG_NULLFREE(pCtx, pszidHidBlob2); SG_NULLFREE(pCtx, pszidHidVerify1); SG_NULLFREE(pCtx, pszidHidVerify2); }
void sg_wc_tx__apply__store_symlink(SG_context * pCtx, SG_wc_tx * pWcTx, const SG_vhash * pvh) { SG_pathname * pPath = NULL; SG_string * pStringSymlink = NULL; const char * pszRepoPath; // we do not own this const char * pszHidExpected; // we do not own this char * pszHidObserved = NULL; sg_wc_liveview_item * pLVI; // we do not own this SG_int64 alias; SG_bool bKnown; SG_bool bDontBother_BlobEncoding; SG_bool bSrcIsSparse; SG_ERR_CHECK( SG_vhash__get__sz( pCtx, pvh, "src", &pszRepoPath) ); SG_ERR_CHECK( SG_vhash__get__int64(pCtx, pvh, "alias", &alias) ); SG_ERR_CHECK( SG_vhash__get__sz( pCtx, pvh, "hid", &pszHidExpected) ); SG_ERR_CHECK( SG_vhash__get__bool( pCtx, pvh, "src_sparse", &bSrcIsSparse) ); #if TRACE_WC_TX_APPLY SG_ERR_IGNORE( SG_console(pCtx, SG_CS_STDERR, ("sg_wc_tx__apply__store_symlink: '%s' [src-sparse %d]\n"), pszRepoPath, bSrcIsSparse) ); #endif SG_ERR_CHECK( sg_wc_tx__liveview__fetch_random_item(pCtx, pWcTx, alias, &bKnown, &pLVI) ); SG_ASSERT( (bSrcIsSparse == SG_WC_PRESCAN_FLAGS__IS_CONTROLLED_SPARSE(pLVI->scan_flags_Live)) ); if (bSrcIsSparse) { // We've been asked to store the target of the symlink ***during a COMMIT*** // and are given the *Expected-HID* (and we need to get the actual target // from the WD) and it is assumed that that will generate the same HID // that we were given. // // However, if the symlink is sparse (not populated) we can't do __readlink() // to get the (current) target. So we have to // assume that we already have a blob in the repo for it. // // Since sparse items now have p_d_sparse dynamic data in tbl_PC, we assume // that whoever last modified the content of the symlink and set p_d_sparse->pszHid // also recorded the blob we need to be present now. (See __apply__overwrite_symlink()) // // for sanity's sake verify that we already have this blob in the repo. SG_uint64 len = 0; SG_ERR_CHECK( SG_repo__fetch_blob__begin(pCtx, pWcTx->pDb->pRepo, pszHidExpected, SG_TRUE, NULL, NULL, NULL, &len, NULL) ); // so we don't need to do anything because we already // have a copy of this blob in the repo. return; } // We never bother compressing/encoding the symlink content // since it is so short. bDontBother_BlobEncoding = SG_TRUE; SG_ERR_CHECK( sg_wc_db__path__sz_repopath_to_absolute(pCtx, pWcTx->pDb, pszRepoPath, &pPath) ); SG_ERR_CHECK( SG_fsobj__readlink(pCtx, pPath, &pStringSymlink) ); SG_ERR_CHECK( SG_committing__add_bytes__string(pCtx, pWcTx->pCommittingInProgress, pStringSymlink, bDontBother_BlobEncoding, &pszHidObserved) ); // See note in __apply__store_file() about race condition. // If the HID computed now differs from what we thought // it should be, we lost the race. if (strcmp(pszHidObserved, pszHidExpected) != 0) SG_ERR_THROW2( SG_ERR_ASSERT, (pCtx, "The symlink '%s' changed during the commit.", pszRepoPath) ); fail: SG_PATHNAME_NULLFREE(pCtx, pPath); SG_STRING_NULLFREE(pCtx, pStringSymlink); SG_NULLFREE(pCtx, pszHidObserved); }