void u0023_vcdiff__test_deltify_run(SG_context * pCtx) { FILE* fp; SG_pathname* pPath_version1 = NULL; SG_pathname* pPath_version2 = NULL; int i; VERIFY_ERR_CHECK_DISCARD( unittest__get_nonexistent_pathname(pCtx, &pPath_version1) ); VERIFY_ERR_CHECK_DISCARD( unittest__get_nonexistent_pathname(pCtx, &pPath_version2) ); fp = fopen(SG_pathname__sz(pPath_version1), "w"); fprintf(fp, "Ah, I should havq known it from thq vqry start This girl will lqavq mq with a brokqn hqart Now listqn pqoplq what I'm tqlling you A-kqqp away from-a Runaround Suq\n"); fclose(fp); fp = fopen(SG_pathname__sz(pPath_version2), "w"); for (i=0; i<500000; i++) { fputc('e', fp); } fclose(fp); VERIFY_ERR_CHECK_DISCARD( u0023_vcdiff__do_test_deltify(pCtx, pPath_version1, pPath_version2) ); SG_ERR_IGNORE( SG_fsobj__remove__pathname(pCtx, pPath_version1) ); SG_ERR_IGNORE( SG_fsobj__remove__pathname(pCtx, pPath_version2) ); SG_PATHNAME_NULLFREE(pCtx, pPath_version1); SG_PATHNAME_NULLFREE(pCtx, pPath_version2); }
void u0023_vcdiff__test_deltify(SG_context * pCtx) { FILE* fp; SG_pathname* pPath_version1 = NULL; SG_pathname* pPath_version2 = NULL; int i; VERIFY_ERR_CHECK_DISCARD( unittest__get_nonexistent_pathname(pCtx,&pPath_version1) ); VERIFY_ERR_CHECK_DISCARD( unittest__get_nonexistent_pathname(pCtx,&pPath_version2) ); fp = fopen(SG_pathname__sz(pPath_version1), "w"); for (i=0; i<500; i++) { fprintf(fp, "Ah, I should have known it from the very start This girl will leave me with a broken heart Now listen people what I'm telling you A-keep away from-a Runaround Sue\n"); } fclose(fp); fp = fopen(SG_pathname__sz(pPath_version2), "w"); for (i=0; i<100; i++) { fprintf(fp, "He rocks in the tree-top all a day long Hoppin' and a-boppin' and a-singin' the song All the little birds on J-Bird St. Love to hear the robin goin' tweet tweet tweet\n"); } fclose(fp); VERIFY_ERR_CHECK_DISCARD( u0023_vcdiff__do_test_deltify(pCtx, pPath_version1, pPath_version2) ); SG_ERR_IGNORE( SG_fsobj__remove__pathname(pCtx, pPath_version1) ); SG_ERR_IGNORE( SG_fsobj__remove__pathname(pCtx, pPath_version2) ); SG_PATHNAME_NULLFREE(pCtx, pPath_version1); SG_PATHNAME_NULLFREE(pCtx, pPath_version2); }
void u0023_vcdiff__test_deltify_from_zerolength(SG_context * pCtx) { FILE* fp; SG_pathname* pPath_version1 = NULL; SG_pathname* pPath_version2 = NULL; int i; VERIFY_ERR_CHECK_DISCARD( unittest__get_nonexistent_pathname(pCtx, &pPath_version1) ); VERIFY_ERR_CHECK_DISCARD( unittest__get_nonexistent_pathname(pCtx, &pPath_version2) ); fp = fopen(SG_pathname__sz(pPath_version1), "w"); fclose(fp); fp = fopen(SG_pathname__sz(pPath_version2), "w"); for (i=0; i<500000; i++) { fputc(i % 256, fp); } fclose(fp); VERIFY_ERR_CHECK_DISCARD( u0023_vcdiff__do_test_deltify(pCtx, pPath_version1, pPath_version2) ); SG_ERR_IGNORE( SG_fsobj__remove__pathname(pCtx, pPath_version1) ); SG_ERR_IGNORE( SG_fsobj__remove__pathname(pCtx, pPath_version2) ); SG_PATHNAME_NULLFREE(pCtx, pPath_version1); SG_PATHNAME_NULLFREE(pCtx, pPath_version2); }
void u0023_vcdiff__test_deltify_small_files(SG_context * pCtx) { FILE* fp; SG_pathname* pPath_version1; SG_pathname* pPath_version2; VERIFY_ERR_CHECK_DISCARD( unittest__get_nonexistent_pathname(pCtx, &pPath_version1) ); VERIFY_ERR_CHECK_DISCARD( unittest__get_nonexistent_pathname(pCtx, &pPath_version2) ); fp = fopen(SG_pathname__sz(pPath_version1), "w"); fprintf(fp, "e"); fclose(fp); fp = fopen(SG_pathname__sz(pPath_version2), "w"); fprintf(fp, "a"); fclose(fp); VERIFY_ERR_CHECK_DISCARD( u0023_vcdiff__do_test_deltify(pCtx, pPath_version1, pPath_version2) ); SG_ERR_IGNORE( SG_fsobj__remove__pathname(pCtx, pPath_version1) ); SG_ERR_IGNORE( SG_fsobj__remove__pathname(pCtx, pPath_version2) ); SG_PATHNAME_NULLFREE(pCtx, pPath_version1); SG_PATHNAME_NULLFREE(pCtx, pPath_version2); }
static void _resolve__step_pathnames__delete_temp_files(SG_context * pCtx, _resolve__step_pathnames * pStepPathnames) { SG_ERR_CHECK_RETURN( SG_fsobj__remove__pathname(pCtx, pStepPathnames->pPath_Mine) ); SG_ERR_CHECK_RETURN( SG_fsobj__remove__pathname(pCtx, pStepPathnames->pPath_Other) ); SG_ERR_CHECK_RETURN( SG_fsobj__remove__pathname(pCtx, pStepPathnames->pPath_Ancestor) ); // DO NOT DELETE pPath_Result because it is either the final result // or to be used as input to the next step in the plan. }
void u0023_vcdiff__do_test_deltify(SG_context * pCtx, SG_pathname* pPath_version1, SG_pathname* pPath_version2) { SG_bool b; SG_pathname* pPathDelta = NULL; SG_pathname* pPathReconstructed = NULL; VERIFY_ERR_CHECK_DISCARD( unittest__get_nonexistent_pathname(pCtx,&pPathDelta) ); VERIFY_ERR_CHECK_DISCARD( unittest__get_nonexistent_pathname(pCtx,&pPathReconstructed) ); VERIFY_ERR_CHECK_DISCARD( SG_vcdiff__deltify__files(pCtx,pPath_version1, pPath_version2, pPathDelta) ); VERIFY_ERR_CHECK_DISCARD( SG_vcdiff__undeltify__files(pCtx,pPath_version1, pPathReconstructed, pPathDelta) ); b = compare_files_are_identical(pPath_version2, pPathReconstructed); VERIFY_COND("match after deltify/undeltify", (b)); SG_ERR_IGNORE( SG_fsobj__remove__pathname(pCtx,pPathDelta) ); SG_ERR_IGNORE( SG_fsobj__remove__pathname(pCtx,pPathReconstructed) ); SG_PATHNAME_NULLFREE(pCtx, pPathDelta); SG_PATHNAME_NULLFREE(pCtx, pPathReconstructed); }
void u0038_test_version(SG_context * pCtx) { /* This test pokes around in closet internals in ways normal closet callers shouldn't. */ SG_string* pstrEnv = NULL; SG_uint32 len; SG_pathname* pPathCloset = NULL; SG_pathname* pPathClosetVersion = NULL; SG_pathname* pPathClosetVersionBackup = NULL; SG_file* pFile = NULL; SG_vhash* pvh = NULL; /* Deliberately making this break for closet version 3 -- current is version 2. */ SG_byte buf[3]; VERIFY_ERR_CHECK( SG_environment__get__str(pCtx, "SGCLOSET", &pstrEnv, &len) ); if (len) { VERIFY_ERR_CHECK( SG_PATHNAME__ALLOC__SZ(pCtx, &pPathCloset, SG_string__sz(pstrEnv)) ); } else { VERIFY_ERR_CHECK( SG_PATHNAME__ALLOC__USER_APPDATA_DIRECTORY(pCtx, &pPathCloset) ); VERIFY_ERR_CHECK( SG_pathname__append__from_sz(pCtx, pPathCloset, ".sgcloset") ); } VERIFY_ERR_CHECK( SG_pathname__alloc__pathname_sz(pCtx, &pPathClosetVersion, pPathCloset, "version") ); VERIFY_ERR_CHECK( SG_pathname__alloc__pathname_sz(pCtx, &pPathClosetVersionBackup, pPathCloset, "version.bak") ); VERIFY_ERR_CHECK( SG_fsobj__move__pathname_pathname(pCtx, pPathClosetVersion, pPathClosetVersionBackup) ); VERIFY_ERR_CHECK( SG_file__open__pathname(pCtx, pPathClosetVersion, SG_FILE_OPEN_OR_CREATE|SG_FILE_WRONLY|SG_FILE_TRUNC, 0644, &pFile) ); VERIFY_ERR_CHECK( SG_file__write(pCtx, pFile, sizeof(buf), buf, NULL) ); VERIFY_ERR_CHECK( SG_file__close(pCtx, &pFile) ); SG_closet__descriptors__list(pCtx, &pvh); VERIFY_COND("", SG_context__err_equals(pCtx, SG_ERR_UNSUPPORTED_CLOSET_VERSION)); SG_ERR_DISCARD; VERIFY_ERR_CHECK( SG_fsobj__remove__pathname(pCtx, pPathClosetVersion) ); VERIFY_ERR_CHECK( SG_fsobj__move__pathname_pathname(pCtx, pPathClosetVersionBackup, pPathClosetVersion) ); /* Common cleanup */ fail: SG_STRING_NULLFREE(pCtx, pstrEnv); SG_PATHNAME_NULLFREE(pCtx, pPathCloset); SG_PATHNAME_NULLFREE(pCtx, pPathClosetVersion); SG_PATHNAME_NULLFREE(pCtx, pPathClosetVersionBackup); SG_FILE_NULLCLOSE(pCtx, pFile); SG_VHASH_NULLFREE(pCtx, pvh); }
/** * Export the contents of the given file entry (which reflects a specific version) * into a temp file so that an external tool can use it. * * You own the returned pathname and the file on disk. */ void _sg_mrg__export_to_temp_file(SG_context * pCtx, SG_mrg * pMrg, const char * pszHidBlob, const SG_pathname * pPathTempFile) { SG_file * pFile = NULL; SG_bool bExists; SG_ERR_CHECK( SG_fsobj__exists__pathname(pCtx,pPathTempFile,&bExists,NULL,NULL) ); if (bExists) { #if TRACE_WC_MERGE SG_ERR_IGNORE( SG_console(pCtx,SG_CS_STDERR, "Skipping export of [blob %s] to [%s]\n",pszHidBlob,SG_pathname__sz(pPathTempFile)) ); #endif } else { #if TRACE_WC_MERGE SG_ERR_IGNORE( SG_console(pCtx,SG_CS_STDERR, "Exporting [blob %s] to [%s]\n",pszHidBlob,SG_pathname__sz(pPathTempFile)) ); #endif // Ideally, when we create this TEMP file it should be read-only. // Afterall, it does represent a historical version of the file and // it should only be used as INPUT to whatever merge tool the user // has configured. So it should be read-only. This might allow // a GUI merge tool to show locks/whatever and/or prevent accidental // editing of these files. // // However, this can cause an "Access is Denied" error on Windows // when we get ready to delete the contents of the TEMP directory. // We'll deal with that there rather than here. SG_ERR_CHECK( SG_file__open__pathname(pCtx,pPathTempFile,SG_FILE_WRONLY|SG_FILE_CREATE_NEW,0400,&pFile) ); SG_ERR_CHECK( SG_repo__fetch_blob_into_file(pCtx, pMrg->pWcTx->pDb->pRepo, pszHidBlob,pFile,NULL) ); SG_FILE_NULLCLOSE(pCtx,pFile); } return; fail: if (pFile) // only if **WE** created the file, do we try to delete it on error. { SG_FILE_NULLCLOSE(pCtx,pFile); SG_ERR_IGNORE( SG_fsobj__remove__pathname(pCtx,pPathTempFile) ); } }
/** * Export a blob-of-interest from the REPO into a temporary directory * so that we can let the external diff tool compare it with another * version somewhere. * * We create a file with a GID-based name rather than re-creating * the working-directory hierarchy in the temp directory. This lets * us flatten the export in one directory without collisions and * avoids move/rename and added/deleted sub-directory issues. * * pPathTempSessionDir should be something like "$TMPDIR/gid_session/". * This should be a unique directory name such that everything being * exported is isolated from other runs. (if we are doing a * changeset-vs-changeset diff, we may create lots of files on each * side -- and our command should not interfere with other diffs * in progress in other processes.) * * szVersion should be a value to let us group everything from cset[0] * in a different directory from stuff from cset[1]. this might be * simply "0" and "1" or it might be the cset's HIDs. * * szGidObject is the gid of the object. * * szHidBlob is the HID of the content. Normally this is the content * of the file that will be compared (corresponding to a user-file * under version control). However, we may also want to use this * to splat the XATTRs to a file so that they can be compared (on * non-apple systems) -- but this may be too weird. * * You are responsible for freeing the returned pathname and * deleting the file that we create. */ void SG_diff_utils__export_to_temp_file(SG_context * pCtx, SG_repo * pRepo, const SG_pathname * pPathTempSessionDir, // value of "$TMPDIR/session/" const char * szVersion, // an index (like 0 or 1, _older_ _newer_) or maybe cset HID const char * szGidObject, const char * szHidBlob, SG_pathname ** ppPathTempFile) { SG_pathname * pPathFile = NULL; SG_file * pFile = NULL; SG_bool bDirExists; // mkdir $TMPDIR/session/version // create pathname for the temp file: $TMPDIR/session/version/object_gid // // TODO do we want to append the suffix from the file to this pathname so that // TODO tools like SGDM can use it? what if the file is renamed and given a // TODO different suffix between versions? SG_ERR_CHECK( SG_PATHNAME__ALLOC__PATHNAME_SZ(pCtx,&pPathFile,pPathTempSessionDir,szVersion) ); SG_ERR_CHECK( SG_fsobj__exists__pathname(pCtx,pPathFile,&bDirExists,NULL,NULL) ); if (!bDirExists) SG_ERR_CHECK( SG_fsobj__mkdir_recursive__pathname(pCtx,pPathFile) ); SG_ERR_CHECK( SG_pathname__append__from_sz(pCtx,pPathFile,szGidObject) ); // open the file and copy the contents of the blob into it SG_ERR_CHECK( SG_file__open__pathname(pCtx,pPathFile,SG_FILE_WRONLY|SG_FILE_CREATE_NEW,0600,&pFile) ); SG_ERR_CHECK( SG_repo__fetch_blob_into_file(pCtx,pRepo,szHidBlob,pFile,NULL) ); SG_ERR_CHECK( SG_file__close(pCtx,&pFile) ); *ppPathTempFile = pPathFile; return; fail: if (pFile) // only if **WE** created the file, do we try to delete it on an error. { SG_FILE_NULLCLOSE(pCtx,pFile); SG_ERR_IGNORE( SG_fsobj__remove__pathname(pCtx,pPathFile) ); } SG_PATHNAME_NULLFREE(pCtx, pPathFile); }
void SG_server__pull_request_fragball(SG_context* pCtx, SG_repo* pRepo, SG_vhash* pvhRequest, const SG_pathname* pFragballDirPathname, char** ppszFragballName, SG_vhash** ppvhStatus) { SG_pathname* pFragballPathname = NULL; SG_uint32* paDagNums = NULL; SG_rbtree* prbDagnodes = NULL; SG_string* pstrFragballName = NULL; char* pszRevFullHid = NULL; SG_rbtree_iterator* pit = NULL; SG_uint32* repoDagnums = NULL; SG_NULLARGCHECK_RETURN(pRepo); SG_NULLARGCHECK_RETURN(pFragballDirPathname); SG_NULLARGCHECK_RETURN(ppvhStatus); #if TRACE_SERVER SG_ERR_CHECK( SG_vhash_debug__dump_to_console__named(pCtx, pvhRequest, "pull fragball request") ); #endif SG_ERR_CHECK( SG_fragball__create(pCtx, pFragballDirPathname, &pFragballPathname) ); if (!pvhRequest) { // Add leaves from every dag to the fragball. SG_uint32 count_dagnums; SG_uint32 i; 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__append__dagnodes(pCtx, pFragballPathname, pRepo, 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) ); } else { // Build the requested fragball. SG_bool found; SG_ERR_CHECK( SG_vhash__has(pCtx, pvhRequest, SG_SYNC_STATUS_KEY__CLONE, &found) ); if (found) { // Full clone requested. SG_ERR_CHECK( SG_repo__fetch_repo__fragball(pCtx, pRepo, pFragballDirPathname, ppszFragballName) ); } else { // Not a full clone. SG_ERR_CHECK( SG_vhash__has(pCtx, pvhRequest, SG_SYNC_STATUS_KEY__DAGS, &found) ); if (found) { // Dagnodes were requested. SG_uint32 generations = 0; SG_vhash* pvhDags; SG_uint32 count_requested_dagnums; SG_uint32 count_repo_dagnums = 0; SG_uint32 i; const char* pszDagNum = NULL; const SG_variant* pvRequestedNodes = NULL; SG_vhash* pvhRequestedNodes = NULL; const char* pszHidRequestedDagnode = NULL; // Were additional generations requested? SG_ERR_CHECK( SG_vhash__has(pCtx, pvhRequest, SG_SYNC_STATUS_KEY__GENERATIONS, &found) ); if (found) SG_ERR_CHECK( SG_vhash__get__uint32(pCtx, pvhRequest, SG_SYNC_STATUS_KEY__GENERATIONS, &generations) ); 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(pCtx, pRepo, &count_repo_dagnums, &repoDagnums) ); // For each requested dag, get the requested nodes. for (i=0; i<count_requested_dagnums; i++) { SG_uint32 iMissingNodeCount; SG_uint32 iDagnum; SG_uint32 j; SG_bool isValidDagnum = SG_FALSE; SG_bool bSpecificNodesRequested = SG_FALSE; // Get the dag's missing node vhash. SG_ERR_CHECK( SG_vhash__get_nth_pair(pCtx, pvhDags, i, &pszDagNum, &pvRequestedNodes) ); SG_ERR_CHECK( SG_dagnum__from_sz__decimal(pCtx, pszDagNum, &iDagnum) ); // Verify that requested dagnum exists for (j = 0; j < count_repo_dagnums; j++) { if (repoDagnums[j] == iDagnum) { isValidDagnum = SG_TRUE; break; } } if (!isValidDagnum) { char buf[SG_DAGNUM__BUF_MAX__NAME]; SG_ERR_CHECK( SG_dagnum__to_name(pCtx, iDagnum, buf, sizeof(buf)) ); SG_ERR_THROW2(SG_ERR_NO_SUCH_DAG, (pCtx, "%s", buf)); } if (pvRequestedNodes) { SG_ERR_CHECK( SG_variant__get__vhash(pCtx, pvRequestedNodes, &pvhRequestedNodes) ); // Get each node listed for the dag SG_ERR_CHECK( SG_vhash__count(pCtx, pvhRequestedNodes, &iMissingNodeCount) ); if (iMissingNodeCount > 0) { SG_uint32 j; const SG_variant* pvVal; bSpecificNodesRequested = SG_TRUE; SG_ERR_CHECK( SG_RBTREE__ALLOC__PARAMS(pCtx, &prbDagnodes, iMissingNodeCount, NULL) ); for (j=0; j<iMissingNodeCount; j++) { SG_ERR_CHECK( SG_vhash__get_nth_pair(pCtx, pvhRequestedNodes, j, &pszHidRequestedDagnode, &pvVal) ); if (pvVal) { const char* pszVal; SG_ERR_CHECK( SG_variant__get__sz(pCtx, pvVal, &pszVal) ); if (pszVal) { if (0 == strcmp(pszVal, SG_SYNC_REQUEST_VALUE_HID_PREFIX)) { SG_ERR_CHECK( SG_repo__hidlookup__dagnode(pCtx, pRepo, iDagnum, pszHidRequestedDagnode, &pszRevFullHid) ); pszHidRequestedDagnode = pszRevFullHid; } else if (0 == strcmp(pszVal, SG_SYNC_REQUEST_VALUE_TAG)) { SG_ERR_CHECK( SG_vc_tags__lookup__tag(pCtx, pRepo, pszHidRequestedDagnode, &pszRevFullHid) ); if (!pszRevFullHid) SG_ERR_THROW(SG_ERR_TAG_NOT_FOUND); pszHidRequestedDagnode = pszRevFullHid; } else SG_ERR_THROW(SG_ERR_PULL_INVALID_FRAGBALL_REQUEST); } } SG_ERR_CHECK( SG_rbtree__update(pCtx, prbDagnodes, pszHidRequestedDagnode) ); // Get additional dagnode generations, if requested. SG_ERR_CHECK( SG_sync__add_n_generations(pCtx, pRepo, pszHidRequestedDagnode, prbDagnodes, generations) ); SG_NULLFREE(pCtx, pszRevFullHid); } } } 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) ); // Get additional dagnode generations, if requested. if (generations) { SG_bool found; const char* hid; SG_ERR_CHECK( SG_rbtree__iterator__first(pCtx, &pit, prbDagnodes, &found, &hid, NULL) ); while (found) { SG_ERR_CHECK( SG_sync__add_n_generations(pCtx, pRepo, hid, prbDagnodes, generations) ); SG_ERR_CHECK( SG_rbtree__iterator__next(pCtx, pit, &found, &hid, NULL) ); } } } if (prbDagnodes) // can be null when leaves of an empty dag are requested { SG_ERR_CHECK( SG_fragball__append__dagnodes(pCtx, pFragballPathname, pRepo, iDagnum, prbDagnodes) ); SG_RBTREE_NULLFREE(pCtx, prbDagnodes); } } // 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, pRepo, pFragballPathname, pvhBlobs) ); } SG_ERR_CHECK( SG_pathname__get_last(pCtx, pFragballPathname, &pstrFragballName) ); SG_ERR_CHECK( SG_STRDUP(pCtx, SG_string__sz(pstrFragballName), ppszFragballName) ); } } /* 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_RBTREE_NULLFREE(pCtx, prbDagnodes); SG_STRING_NULLFREE(pCtx, pstrFragballName); SG_NULLFREE(pCtx, pszRevFullHid); SG_RBTREE_ITERATOR_NULLFREE(pCtx, pit); SG_NULLFREE(pCtx, repoDagnums); }
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); }
// TODO give this a better name. It gets used outside this file now. void do_url( SG_context* pCtx, const char* pszUrl, const char* psz_method, const char* psz_data, const char* psz_username, const char* psz_password, SG_string** ppstr, SG_pathname* pPath, SG_bool b_progress ) { SG_string* pstr = NULL; CFHTTPMessageRef myRequest = NULL; CFHTTPMessageRef myResponse = NULL; CFStringRef s_username = NULL; CFStringRef s_password = NULL; if (pPath && ppstr) { SG_ERR_RESET_THROW2(SG_ERR_INVALIDARG, (pCtx, "do_url() returns into a string or a file, but not both")); } if (!pPath && !ppstr) { SG_ERR_RESET_THROW2(SG_ERR_INVALIDARG, (pCtx, "do_url() returns into a string or a file, one or the other")); } SG_ERR_CHECK( make_request(pCtx, pszUrl, psz_method, psz_data, &myRequest) ); #if 0 { CFDataRef d = CFHTTPMessageCopySerializedMessage(myRequest); fprintf(stderr, "%s\n", CFDataGetBytePtr(d)); CFRelease(d); } #endif if (pPath) { SG_ERR_CHECK( perform_request__file(pCtx, myRequest, &myResponse, pPath, b_progress) ); } else { SG_ERR_CHECK( perform_request__string(pCtx, myRequest, &myResponse, &pstr) ); } if (!myResponse) { SG_ERR_THROW2(SG_ERR_UNSPECIFIED, (pCtx, "No response from server")); } //fprintf(stderr, "\n%s\n", SG_string__sz(pstr)); UInt32 statusCode = CFHTTPMessageGetResponseStatusCode(myResponse); #if 0 { CFDataRef d = CFHTTPMessageCopySerializedMessage(myResponse); fprintf(stderr, "%s\n", CFDataGetBytePtr(d)); CFRelease(d); } #endif if ( psz_username && psz_password && (statusCode == 401 || statusCode == 407) ) { s_username = CFStringCreateWithCString(kCFAllocatorDefault, psz_username, kCFStringEncodingUTF8); s_password = CFStringCreateWithCString(kCFAllocatorDefault, psz_password, kCFStringEncodingUTF8); if (!CFHTTPMessageAddAuthentication(myRequest, myResponse, s_username, s_password, kCFHTTPAuthenticationSchemeDigest, FALSE)) { SG_ERR_THROW2(SG_ERR_UNSPECIFIED, (pCtx, "CFHTTPMessageAddAuthentication failed")); } #if 0 { CFDataRef d = CFHTTPMessageCopySerializedMessage(myRequest); fprintf(stderr, "%s\n", CFDataGetBytePtr(d)); CFRelease(d); } #endif CFRelease(s_username); s_username = NULL; CFRelease(s_password); s_password = NULL; CFRelease(myResponse); myResponse = NULL; if (pPath) { SG_ERR_IGNORE( SG_fsobj__remove__pathname(pCtx, pPath) ); SG_ERR_CHECK( perform_request__file(pCtx, myRequest, &myResponse, pPath, b_progress) ); } else { SG_STRING_NULLFREE(pCtx, pstr); SG_ERR_CHECK( perform_request__string(pCtx, myRequest, &myResponse, &pstr) ); } #if 0 { CFDataRef d = CFHTTPMessageCopySerializedMessage(myResponse); fprintf(stderr, "%s\n", CFDataGetBytePtr(d)); CFRelease(d); } #endif statusCode = CFHTTPMessageGetResponseStatusCode(myResponse); } if (statusCode != 200) { if (401 == statusCode) { SG_ERR_THROW(SG_ERR_HTTP_401); } else if (404 == statusCode) { SG_ERR_THROW(SG_ERR_HTTP_404); } else if (502 == statusCode) { SG_ERR_THROW(SG_ERR_HTTP_502); } else { SG_ERR_THROW2(SG_ERR_SERVER_HTTP_ERROR, (pCtx, "%d", (int) statusCode)); } } if (ppstr) { *ppstr = pstr; pstr = NULL; } /* fall through */ fail: if (s_username) { CFRelease(s_username); s_username = NULL; } if (s_password) { CFRelease(s_password); s_password = NULL; } if (myRequest) { CFRelease(myRequest); myRequest = NULL; } if (myResponse) { CFRelease(myResponse); myResponse = NULL; } SG_STRING_NULLFREE(pCtx, pstr); }
int u0040_unc__stat_dir(SG_context * pCtx, const char * szDir) { SG_pathname * pPathname = NULL; SG_pathname * pPathnameFile = NULL; SG_file * pf = NULL; SG_fsobj_stat fsobjStat; SG_bool bFileExists; SG_int_to_string_buffer bufSize; char bufDate[100]; SG_context__err_reset(pCtx); ////////////////////////////////////////////////////////////////// // stat the given directory. ////////////////////////////////////////////////////////////////// INFOP("u0040_unc",("Inspecting [%s]",szDir)); VERIFY_ERR_CHECK_RETURN( SG_PATHNAME__ALLOC__SZ(pCtx,&pPathname,szDir) ); VERIFY_ERR_CHECK( SG_fsobj__stat__pathname(pCtx,pPathname,&fsobjStat) ); VERIFY_COND("u0040_unc",(fsobjStat.type == SG_FSOBJ_TYPE__DIRECTORY)); // TODO should we verify length == 0 ? // TODO should we verify modtime ? SG_uint64_to_sz(fsobjStat.size, bufSize); VERIFY_ERR_CHECK_DISCARD( SG_time__format_utc__i64(pCtx,fsobjStat.mtime_ms,bufDate,SG_NrElements(bufDate)) ); INFOP("u0040_unc",("Result: [perms %04o][type %d][size %s][mtime %s]", fsobjStat.perms,fsobjStat.type, bufSize,bufDate)); ////////////////////////////////////////////////////////////////// // create a unique file in the directory and stat it. ////////////////////////////////////////////////////////////////// VERIFY_ERR_CHECK( unittest__alloc_unique_pathname(pCtx,szDir,&pPathnameFile) ); INFOP("u0040_unc",(" Creating file [%s]",SG_pathname__sz(pPathnameFile))); VERIFY_ERR_CHECK( SG_file__open__pathname(pCtx,pPathnameFile,SG_FILE_CREATE_NEW | SG_FILE_RDWR,0777,&pf) ); VERIFY_ERR_CHECK( SG_fsobj__stat__pathname(pCtx,pPathnameFile,&fsobjStat) ); VERIFY_COND("u0040_unc",(fsobjStat.type == SG_FSOBJ_TYPE__REGULAR)); VERIFY_COND("u0040_unc",(fsobjStat.size == 0)); VERIFY_COND("u0040_unc",(SG_fsobj__equivalent_perms(fsobjStat.perms,0777))); // TODO should we verify modtime ? SG_uint64_to_sz(fsobjStat.size, bufSize); VERIFY_ERR_CHECK_DISCARD( SG_time__format_utc__i64(pCtx,fsobjStat.mtime_ms,bufDate,SG_NrElements(bufDate)) ); INFOP("u0040_unc",(" Result: [perms %04o][type %d][size %s][mtime %s]", fsobjStat.perms,fsobjStat.type, bufSize,bufDate)); VERIFY_ERR_CHECK_DISCARD( SG_file__close(pCtx, &pf) ); // delete the file and stat it again VERIFY_ERR_CHECK_DISCARD( SG_fsobj__remove__pathname(pCtx,pPathnameFile) ); VERIFY_ERR_CHECK_DISCARD( SG_fsobj__exists__pathname(pCtx,pPathnameFile,&bFileExists,NULL,NULL) ); VERIFY_COND("u0040_unc",(!bFileExists)); ////////////////////////////////////////////////////////////////// // clean up ////////////////////////////////////////////////////////////////// SG_PATHNAME_NULLFREE(pCtx, pPathnameFile); SG_PATHNAME_NULLFREE(pCtx, pPathname); return 1; fail: SG_FILE_NULLCLOSE(pCtx, pf); SG_PATHNAME_NULLFREE(pCtx, pPathnameFile); SG_PATHNAME_NULLFREE(pCtx, pPathname); return 0; }