/** * Is this object a candidate for short-circuit evaluation? That is, can we * avoid diving into this pair of folders and recursively comparing everything * within. * * We must have a pair of unscanned folders with equal content HIDs. If one has * already been scanned, we must scan the other so that the children don't look * like peerless objects. (We either scan neither or both.) * * If we have pendingtree data, we don't know how much of the tree it populated. * If the dsFlags in the pendingtree version indicate a change *on* the folder * (such as a rename/move), we can still allow the short-circuit; only if it * indicates a change in the stuff *within* the folder do we force it to continue. * This is another instance of the accidental peerless problem. */ void sg_vv2__status__can_short_circuit_from_work_queue(SG_context * pCtx, const sg_vv2status_od * pOD, SG_bool * pbCanShortCircuit) { SG_bool bEqual; const char * pszHid_orig; const char * pszHid_dest; SG_UNUSED( pCtx ); if (!pOD->apInst[SG_VV2__OD_NDX_ORIG]) goto no; if (!pOD->apInst[SG_VV2__OD_NDX_DEST]) goto no; if (pOD->apInst[SG_VV2__OD_NDX_ORIG]->typeInst != SG_VV2__OD_TYPE_UNSCANNED_FOLDER) goto no; if (pOD->apInst[SG_VV2__OD_NDX_DEST]->typeInst != SG_VV2__OD_TYPE_UNSCANNED_FOLDER) goto no; SG_ERR_CHECK( SG_treenode_entry__get_hid_blob(pCtx, pOD->apInst[SG_VV2__OD_NDX_ORIG]->pTNE, &pszHid_orig) ); SG_ERR_CHECK( SG_treenode_entry__get_hid_blob(pCtx, pOD->apInst[SG_VV2__OD_NDX_DEST]->pTNE, &pszHid_dest) ); bEqual = (strcmp(pszHid_orig, pszHid_dest) == 0); if (!bEqual) goto no; *pbCanShortCircuit = SG_TRUE; return; no: *pbCanShortCircuit = SG_FALSE; fail: return; }
static void _sg_workingdir__get_entry(SG_context* pCtx, SG_repo* pRepo, const SG_pathname* pPathSub, const char * pszGid, const SG_treenode_entry* pEntry, SG_vhash * pvhTimestamps) { SG_treenode_entry_type type; const char* pszidHidContent = NULL; const char* pszidHidXattrs = NULL; SG_int64 iAttributeBits = 0; SG_ERR_CHECK( SG_treenode_entry__get_entry_type(pCtx, pEntry, &type) ); SG_ERR_CHECK( SG_treenode_entry__get_hid_blob(pCtx, pEntry, &pszidHidContent) ); SG_ERR_CHECK( SG_treenode_entry__get_hid_xattrs(pCtx, pEntry, &pszidHidXattrs) ); SG_ERR_CHECK( SG_treenode_entry__get_attribute_bits(pCtx, pEntry, &iAttributeBits) ); SG_ERR_CHECK( _sg_workingdir__get_entry2(pCtx, pRepo, pPathSub, pszGid, type, pszidHidContent, pszidHidXattrs, iAttributeBits, pvhTimestamps) ); fail: return; }
/** * INSERT or UPDATE this TreeNodeEntry and if a directory, * dive into it and INSERT/UPDATE the contents of the directory. */ void sg_wc_db__tne__insert_recursive(SG_context * pCtx, sg_wc_db * pDb, const sg_wc_db__cset_row * pCSetRow, sqlite3_stmt * pStmt, SG_uint64 uiAliasGidParent, SG_uint64 uiAliasGid, const SG_treenode_entry * pTreenodeEntry) { SG_treenode * pTreenode = NULL; SG_uint32 nrEntries, k; SG_treenode_entry_type tneType; SG_ERR_CHECK( sg_wc_db__tne__bind_insert_and_step(pCtx, pStmt, uiAliasGid, uiAliasGidParent, pTreenodeEntry) ); SG_ERR_CHECK( SG_treenode_entry__get_entry_type(pCtx, pTreenodeEntry, &tneType) ); if (tneType == SG_TREENODEENTRY_TYPE_DIRECTORY) { const char * pszHid = NULL; // we do not own this // dive into the contents of the directory and load them. SG_ERR_CHECK( SG_treenode_entry__get_hid_blob(pCtx, pTreenodeEntry, &pszHid) ); SG_ERR_CHECK( SG_treenode__load_from_repo(pCtx, pDb->pRepo, pszHid, &pTreenode) ); SG_ERR_CHECK( SG_treenode__count(pCtx, pTreenode, &nrEntries) ); for (k=0; k<nrEntries; k++) { const char * pszGid_k = NULL; // we do not own this const SG_treenode_entry * pTreenodeEntry_k = NULL; // we do not own this SG_uint64 uiAliasGid_k = 0; SG_ERR_CHECK( SG_treenode__get_nth_treenode_entry__ref(pCtx, pTreenode, k, &pszGid_k, &pTreenodeEntry_k) ); SG_ERR_CHECK( sg_wc_db__gid__insert(pCtx, pDb, pszGid_k) ); SG_ERR_CHECK( sg_wc_db__gid__get_alias_from_gid(pCtx, pDb, pszGid_k, &uiAliasGid_k) ); SG_ERR_CHECK( sg_wc_db__tne__insert_recursive(pCtx, pDb, pCSetRow, pStmt, uiAliasGid, uiAliasGid_k, pTreenodeEntry_k) ); } } fail: SG_TREENODE_NULLFREE(pCtx, pTreenode); }
void sg_pack__get_dir(SG_context* pCtx, SG_repo* pRepo, SG_int32 gen, const char* pszidHidTreeNode, SG_rbtree* prb_blobs, SG_rbtree* prb_new) { SG_uint32 count; SG_uint32 i; SG_treenode* pTreenode = NULL; const char* psz_gid = NULL; SG_treenode_entry_type type; const char* psz_hid = NULL; SG_ERR_CHECK( SG_treenode__load_from_repo(pCtx, pRepo, pszidHidTreeNode, &pTreenode) ); SG_ERR_CHECK( SG_treenode__count(pCtx, pTreenode, &count) ); for (i=0; i<count; i++) { const SG_treenode_entry* pEntry = NULL; const char* pszName = NULL; SG_ERR_CHECK( SG_treenode__get_nth_treenode_entry__ref(pCtx, pTreenode, i, &psz_gid, &pEntry) ); SG_ERR_CHECK( SG_treenode_entry__get_entry_name(pCtx, pEntry, &pszName) ); SG_ERR_CHECK( SG_treenode_entry__get_entry_type(pCtx, pEntry, &type) ); SG_ERR_CHECK( SG_treenode_entry__get_hid_blob(pCtx, pEntry, &psz_hid) ); if (SG_TREENODEENTRY_TYPE_DIRECTORY == type) { SG_ERR_CHECK( sg_pack__do_blob(pCtx, psz_gid, psz_hid, gen, prb_blobs, prb_new) ); SG_ERR_CHECK( sg_pack__get_dir(pCtx, pRepo, gen, psz_hid, prb_blobs, prb_new) ); } else if (SG_TREENODEENTRY_TYPE_REGULAR_FILE == type) { /* TODO use pszName file extension to decide whether to bother? */ SG_ERR_CHECK( sg_pack__do_blob(pCtx, psz_gid, psz_hid, gen, prb_blobs, prb_new) ); } else if (SG_TREENODEENTRY_TYPE_SYMLINK == type) { /* ignore symlinks */ } else { SG_ERR_THROW( SG_ERR_NOTIMPLEMENTED ); } } SG_TREENODE_NULLFREE(pCtx, pTreenode); pTreenode = NULL; return; fail: SG_TREENODE_NULLFREE(pCtx, pTreenode); }
/** * This function is used to get a new tree. It must be called with the HID of * the top-level user root directory. Not the super-root. The directory which * corresponds to @. */ static void sg_pack__do_get_dir__top(SG_context* pCtx, SG_repo* pRepo, SG_int32 gen, const char* pszidHidTreeNode, SG_rbtree* prb_blobs, SG_rbtree* prb_new) { SG_treenode* pTreenode = NULL; SG_byte* pBytes = NULL; const SG_treenode_entry* pEntry = NULL; const char* pszidHidContent = NULL; /* Load the treenode. It should have exactly one entry, a subdirectory, * named @ */ SG_ERR_CHECK( SG_treenode__load_from_repo(pCtx, pRepo, pszidHidTreeNode, &pTreenode) ); SG_ERR_CHECK( SG_treenode__get_nth_treenode_entry__ref(pCtx, pTreenode, 0, NULL, &pEntry) ); SG_ERR_CHECK( SG_treenode_entry__get_hid_blob(pCtx, pEntry, &pszidHidContent) ); #ifdef DEBUG { SG_uint32 count; SG_treenode_entry_type type; const char* pszName = NULL; SG_ERR_CHECK( SG_treenode__count(pCtx, pTreenode, &count) ); SG_ASSERT(1 == count); SG_ERR_CHECK( SG_treenode_entry__get_entry_type(pCtx, pEntry, &type) ); SG_ASSERT (SG_TREENODEENTRY_TYPE_DIRECTORY == type); SG_ERR_CHECK( SG_treenode_entry__get_entry_name(pCtx, pEntry, &pszName) ); SG_ASSERT(0 == strcmp(pszName, "@")); } #endif /* create the directory and then dive into it */ SG_ERR_CHECK( sg_pack__get_dir(pCtx, pRepo, gen, pszidHidContent, prb_blobs, prb_new) ); SG_TREENODE_NULLFREE(pCtx, pTreenode); return; fail: /* TODO free stuff */ SG_NULLFREE(pCtx, pBytes); }
/** * BIND the fields for an INSERT or UPDATE a single row for this TreeNodeEntry (not * recursive) to the tne_L0 table. Caller must compute the * GID aliases for the foreign keys and have already added * rows to tbl_gid as necessary. */ void sg_wc_db__tne__bind_insert(SG_context * pCtx, sqlite3_stmt * pStmt, SG_uint64 uiAliasGid, SG_uint64 uiAliasGidParent, const SG_treenode_entry * pTreenodeEntry) { const char * pszHid = NULL; // we do not own this const char * pszEntryname = NULL; // we do not own this SG_int64 attrbits; SG_treenode_entry_type tneType; // pull all of the fields from the TNE. SG_ERR_CHECK( SG_treenode_entry__get_hid_blob(pCtx, pTreenodeEntry, &pszHid) ); SG_ERR_CHECK( SG_treenode_entry__get_entry_name(pCtx, pTreenodeEntry, &pszEntryname) ); SG_ERR_CHECK( SG_treenode_entry__get_entry_type(pCtx, pTreenodeEntry, &tneType) ); SG_ERR_CHECK( SG_treenode_entry__get_attribute_bits(pCtx, pTreenodeEntry, &attrbits) ); #if TRACE_WC_ATTRBITS SG_ERR_IGNORE( SG_console(pCtx, SG_CS_STDERR, "TNE: bind_insert (%d) %s\n", ((SG_uint32)attrbits), pszEntryname) ); #endif SG_ERR_CHECK( sg_sqlite__reset(pCtx, pStmt) ); SG_ERR_CHECK( sg_sqlite__clear_bindings(pCtx, pStmt) ); SG_ERR_CHECK( sg_sqlite__bind_int64( pCtx, pStmt, 1, uiAliasGid) ); SG_ERR_CHECK( sg_sqlite__bind_int64( pCtx, pStmt, 2, uiAliasGidParent) ); SG_ERR_CHECK( sg_sqlite__bind_text__transient(pCtx, pStmt, 3, pszHid) ); SG_ERR_CHECK( sg_sqlite__bind_int( pCtx, pStmt, 4, tneType) ); SG_ERR_CHECK( sg_sqlite__bind_int64( pCtx, pStmt, 5, attrbits) ); SG_ERR_CHECK( sg_sqlite__bind_text__transient(pCtx, pStmt, 6, pszEntryname) ); fail: return; }
/** * This function is used to get a new tree. It must be called with the HID of * the top-level user root directory. Not the super-root. The directory which * corresponds to @. * * Record the file timestamp of each file we fetch in the pvhTimestamps; this * provides the basis for the timestamp check in scan-dir. */ static void sg_workingdir__do_get_dir__top(SG_context* pCtx, SG_repo* pRepo, const SG_pathname* pPathLocal, const char* pszidHidTreeNode, SG_vhash * pvhTimestamps) { SG_pathname* pPathSub = NULL; SG_treenode* pTreenode = NULL; SG_string* pstrLink = NULL; SG_byte* pBytes = NULL; SG_vhash* pvhAttributes = NULL; SG_int64 iAttributeBits = 0; const SG_treenode_entry* pEntry = NULL; const char* pszidHidContent = NULL; #ifdef SG_BUILD_FLAG_FEATURE_XATTR const char* pszidHidXattrs = NULL; #endif SG_bool bExists = SG_FALSE; /* Load the treenode. It should have exactly one entry, a subdirectory, * named @ */ SG_ERR_CHECK( SG_treenode__load_from_repo(pCtx, pRepo, pszidHidTreeNode, &pTreenode) ); SG_ERR_CHECK( SG_treenode__get_nth_treenode_entry__ref(pCtx, pTreenode, 0, NULL, &pEntry) ); SG_ERR_CHECK( SG_treenode_entry__get_hid_blob(pCtx, pEntry, &pszidHidContent) ); #ifdef DEBUG { SG_uint32 count; SG_treenode_entry_type type; const char* pszName = NULL; SG_ERR_CHECK( SG_treenode__count(pCtx, pTreenode, &count) ); SG_ASSERT(1 == count); SG_ERR_CHECK( SG_treenode_entry__get_entry_type(pCtx, pEntry, &type) ); SG_ASSERT (SG_TREENODEENTRY_TYPE_DIRECTORY == type); SG_ERR_CHECK( SG_treenode_entry__get_entry_name(pCtx, pEntry, &pszName) ); SG_ASSERT(0 == strcmp(pszName, "@")); } #endif /* create the directory and then dive into it */ SG_ERR_CHECK( SG_PATHNAME__ALLOC__COPY(pCtx, &pPathSub, pPathLocal) ); SG_ERR_CHECK( SG_fsobj__exists__pathname(pCtx, pPathSub, &bExists, NULL, NULL) ); if (!bExists) SG_ERR_CHECK( SG_fsobj__mkdir__pathname(pCtx, pPathSub) ); SG_ERR_CHECK( _sg_workingdir__get_dir(pCtx, pRepo, pPathSub, pszidHidContent, pvhTimestamps) ); #ifdef SG_BUILD_FLAG_FEATURE_XATTR /* fix up the xattrs on that directory */ SG_ERR_CHECK( SG_treenode_entry__get_hid_xattrs(pCtx, pEntry, &pszidHidXattrs) ); if (pszidHidXattrs) { SG_ERR_CHECK( SG_repo__fetch_vhash(pCtx, pRepo, pszidHidXattrs, &pvhAttributes) ); SG_ERR_CHECK( SG_attributes__xattrs__apply(pCtx, pPathSub, pvhAttributes, pRepo) ); SG_VHASH_NULLFREE(pCtx, pvhAttributes); } #endif /* and the attrbits too */ SG_ERR_CHECK( SG_treenode_entry__get_attribute_bits(pCtx, pEntry, &iAttributeBits) ); SG_ERR_CHECK( SG_attributes__bits__apply(pCtx, pPathSub, iAttributeBits) ); SG_PATHNAME_NULLFREE(pCtx, pPathSub); SG_TREENODE_NULLFREE(pCtx, pTreenode); return; fail: /* TODO free stuff */ SG_VHASH_NULLFREE(pCtx, pvhAttributes); SG_NULLFREE(pCtx, pBytes); SG_STRING_NULLFREE(pCtx, pstrLink); }