svn_error_t * svn_fs_bdb__read_rep(representation_t **rep_p, svn_fs_t *fs, const char *key, trail_t *trail, apr_pool_t *pool) { base_fs_data_t *bfd = fs->fsap_data; svn_skel_t *skel; int db_err; DBT query, result; svn_fs_base__trail_debug(trail, "representations", "get"); db_err = bfd->representations->get(bfd->representations, trail->db_txn, svn_fs_base__str_to_dbt(&query, key), svn_fs_base__result_dbt(&result), 0); svn_fs_base__track_dbt(&result, pool); /* If there's no such node, return an appropriately specific error. */ if (db_err == DB_NOTFOUND) return svn_error_createf (SVN_ERR_FS_NO_SUCH_REPRESENTATION, 0, _("No such representation '%s'"), key); /* Handle any other error conditions. */ SVN_ERR(BDB_WRAP(fs, _("reading representation"), db_err)); /* Parse the REPRESENTATION skel. */ skel = svn_skel__parse(result.data, result.size, pool); /* Convert to a native type. */ return svn_fs_base__parse_representation_skel(rep_p, skel, pool); }
svn_error_t *svn_fs_bdb__get_checksum_rep(const char **rep_key, svn_fs_t *fs, svn_checksum_t *checksum, trail_t *trail, apr_pool_t *pool) { base_fs_data_t *bfd = fs->fsap_data; DBT key, value; int db_err; /* We only allow SHA1 checksums in this table. */ if (checksum->kind != svn_checksum_sha1) return svn_error_create(SVN_ERR_BAD_CHECKSUM_KIND, NULL, _("Only SHA1 checksums can be used as keys in the " "checksum-reps table.\n")); svn_fs_base__trail_debug(trail, "checksum-reps", "get"); db_err = bfd->checksum_reps->get(bfd->checksum_reps, trail->db_txn, svn_fs_base__checksum_to_dbt(&key, checksum), svn_fs_base__result_dbt(&value), 0); svn_fs_base__track_dbt(&value, pool); if (db_err == DB_NOTFOUND) return svn_fs_base__err_no_such_checksum_rep(fs, checksum); *rep_key = apr_pstrmemdup(pool, value.data, value.size); return SVN_NO_ERROR; }
svn_error_t * svn_fs_bdb__miscellaneous_get(const char **val, svn_fs_t *fs, const char *key_str, trail_t *trail, apr_pool_t *pool) { base_fs_data_t *bfd = fs->fsap_data; DBT key, value; int db_err; *val = NULL; svn_fs_base__trail_debug(trail, "miscellaneous", "get"); db_err = bfd->miscellaneous->get(bfd->miscellaneous, trail->db_txn, svn_fs_base__str_to_dbt(&key, key_str), svn_fs_base__result_dbt(&value), 0); svn_fs_base__track_dbt(&value, pool); if (db_err != DB_NOTFOUND) { SVN_ERR(BDB_WRAP(fs, N_("fetching miscellaneous record"), db_err)); *val = apr_pstrmemdup(pool, value.data, value.size); } return SVN_NO_ERROR; }
/* Get the current 'next-key' value and bump the record. */ static svn_error_t * get_key_and_bump(svn_fs_t *fs, const char **key, trail_t *trail, apr_pool_t *pool) { base_fs_data_t *bfd = fs->fsap_data; DBC *cursor; char next_key[MAX_KEY_SIZE]; apr_size_t key_len; int db_err; DBT query; DBT result; /* ### todo: see issue #409 for why bumping the key as part of this trail is problematic. */ /* Open a cursor and move it to the 'next-key' value. We can then fetch the contents and use the cursor to overwrite those contents. Since this database allows duplicates, we can't do an arbitrary 'put' to write the new value -- that would append, not overwrite. */ svn_fs_base__trail_debug(trail, "strings", "cursor"); SVN_ERR(BDB_WRAP(fs, N_("creating cursor for reading a string"), bfd->strings->cursor(bfd->strings, trail->db_txn, &cursor, 0))); /* Advance the cursor to 'next-key' and read it. */ db_err = svn_bdb_dbc_get(cursor, svn_fs_base__str_to_dbt(&query, NEXT_KEY_KEY), svn_fs_base__result_dbt(&result), DB_SET); if (db_err) { svn_bdb_dbc_close(cursor); return BDB_WRAP(fs, N_("getting next-key value"), db_err); } svn_fs_base__track_dbt(&result, pool); *key = apr_pstrmemdup(pool, result.data, result.size); /* Bump to future key. */ key_len = result.size; svn_fs_base__next_key(result.data, &key_len, next_key); /* Shove the new key back into the database, at the cursor position. */ db_err = svn_bdb_dbc_put(cursor, &query, svn_fs_base__str_to_dbt(&result, next_key), DB_CURRENT); if (db_err) { svn_bdb_dbc_close(cursor); /* ignore the error, the original is more important. */ return BDB_WRAP(fs, N_("bumping next string key"), db_err); } return BDB_WRAP(fs, N_("closing string-reading cursor"), svn_bdb_dbc_close(cursor)); }
svn_error_t *svn_fs_bdb__set_node_origin(svn_fs_t *fs, const char *node_id, const svn_fs_id_t *origin_id, trail_t *trail, apr_pool_t *pool) { base_fs_data_t *bfd = fs->fsap_data; DBT key, value; int db_err; /* Create a key from our NODE_ID. */ svn_fs_base__str_to_dbt(&key, node_id); /* Check to see if we already have a mapping for NODE_ID. If so, and the value is the same one we were about to write. That's cool -- just do nothing. If, however, the value is *different*, that's a red flag! */ svn_fs_base__trail_debug(trail, "node-origins", "get"); db_err = bfd->node_origins->get(bfd->node_origins, trail->db_txn, &key, svn_fs_base__result_dbt(&value), 0); svn_fs_base__track_dbt(&value, pool); if (db_err != DB_NOTFOUND) { const svn_string_t *origin_id_str = svn_fs_base__id_unparse(origin_id, pool); const svn_string_t *old_origin_id_str = svn_string_ncreate(value.data, value.size, pool); if (! svn_string_compare(origin_id_str, old_origin_id_str)) return svn_error_createf (SVN_ERR_FS_CORRUPT, NULL, _("Node origin for '%s' exists in filesystem '%s' with a different " "value (%s) than what we were about to store (%s)"), node_id, fs->path, old_origin_id_str->data, origin_id_str->data); else return SVN_NO_ERROR; } /* Create a value from our ORIGIN_ID, and add this record to the table. */ svn_fs_base__id_to_dbt(&value, origin_id, pool); svn_fs_base__trail_debug(trail, "node-origins", "put"); SVN_ERR(BDB_WRAP(fs, _("storing node-origins record"), bfd->node_origins->put(bfd->node_origins, trail->db_txn, &key, &value, 0))); return SVN_NO_ERROR; }
svn_error_t *svn_fs_bdb__set_checksum_rep(svn_fs_t *fs, svn_checksum_t *checksum, const char *rep_key, trail_t *trail, apr_pool_t *pool) { base_fs_data_t *bfd = fs->fsap_data; DBT key, value; int db_err; /* We only allow SHA1 checksums in this table. */ if (checksum->kind != svn_checksum_sha1) return svn_error_create(SVN_ERR_BAD_CHECKSUM_KIND, NULL, _("Only SHA1 checksums can be used as keys in the " "checksum-reps table.\n")); /* Create a key from our CHECKSUM. */ svn_fs_base__checksum_to_dbt(&key, checksum); /* Check to see if we already have a mapping for CHECKSUM. If so, and the value is the same one we were about to write, that's cool -- just do nothing. If, however, the value is *different*, that's a red flag! */ svn_fs_base__trail_debug(trail, "checksum-reps", "get"); db_err = bfd->checksum_reps->get(bfd->checksum_reps, trail->db_txn, &key, svn_fs_base__result_dbt(&value), 0); svn_fs_base__track_dbt(&value, pool); if (db_err != DB_NOTFOUND) { const char *sum_str = svn_checksum_to_cstring_display(checksum, pool); return svn_error_createf (SVN_ERR_FS_ALREADY_EXISTS, NULL, _("Representation key for checksum '%s' exists in filesystem '%s'."), sum_str, fs->path); } /* Create a value from our REP_KEY, and add this record to the table. */ svn_fs_base__str_to_dbt(&value, rep_key); svn_fs_base__trail_debug(trail, "checksum-reps", "put"); SVN_ERR(BDB_WRAP(fs, _("storing checksum-reps record"), bfd->checksum_reps->put(bfd->checksum_reps, trail->db_txn, &key, &value, 0))); return SVN_NO_ERROR; }
svn_error_t * svn_fs_bdb__write_new_rep(const char **key, svn_fs_t *fs, const representation_t *rep, trail_t *trail, apr_pool_t *pool) { base_fs_data_t *bfd = fs->fsap_data; DBT query, result; int db_err; apr_size_t len; char next_key[MAX_KEY_SIZE]; /* ### todo: see issue #409 for why bumping the key as part of this trail is problematic. */ /* Get the current value associated with `next-key'. */ svn_fs_base__str_to_dbt(&query, NEXT_KEY_KEY); svn_fs_base__trail_debug(trail, "representations", "get"); SVN_ERR(BDB_WRAP(fs, _("allocating new representation (getting next-key)"), bfd->representations->get (bfd->representations, trail->db_txn, &query, svn_fs_base__result_dbt(&result), 0))); svn_fs_base__track_dbt(&result, pool); /* Store the new rep. */ *key = apr_pstrmemdup(pool, result.data, result.size); SVN_ERR(svn_fs_bdb__write_rep(fs, *key, rep, trail, pool)); /* Bump to future key. */ len = result.size; svn_fs_base__next_key(result.data, &len, next_key); svn_fs_base__trail_debug(trail, "representations", "put"); db_err = bfd->representations->put (bfd->representations, trail->db_txn, svn_fs_base__str_to_dbt(&query, NEXT_KEY_KEY), svn_fs_base__str_to_dbt(&result, next_key), 0); SVN_ERR(BDB_WRAP(fs, _("bumping next representation key"), db_err)); return SVN_NO_ERROR; }
svn_error_t * svn_fs_bdb__new_node_id(svn_fs_id_t **id_p, svn_fs_t *fs, const char *copy_id, const char *txn_id, trail_t *trail, apr_pool_t *pool) { base_fs_data_t *bfd = fs->fsap_data; DBT query, result; apr_size_t len; char next_key[MAX_KEY_SIZE]; int db_err; const char *next_node_id; SVN_ERR_ASSERT(txn_id); /* Get the current value associated with the `next-key' key in the table. */ svn_fs_base__str_to_dbt(&query, NEXT_KEY_KEY); svn_fs_base__trail_debug(trail, "nodes", "get"); SVN_ERR(BDB_WRAP(fs, N_("allocating new node ID (getting 'next-key')"), bfd->nodes->get(bfd->nodes, trail->db_txn, &query, svn_fs_base__result_dbt(&result), 0))); svn_fs_base__track_dbt(&result, pool); /* Squirrel away our next node id value. */ next_node_id = apr_pstrmemdup(pool, result.data, result.size); /* Bump to future key. */ len = result.size; svn_fs_base__next_key(result.data, &len, next_key); svn_fs_base__trail_debug(trail, "nodes", "put"); db_err = bfd->nodes->put(bfd->nodes, trail->db_txn, svn_fs_base__str_to_dbt(&query, NEXT_KEY_KEY), svn_fs_base__str_to_dbt(&result, next_key), 0); SVN_ERR(BDB_WRAP(fs, N_("bumping next node ID key"), db_err)); /* Create and return the new node id. */ *id_p = svn_fs_base__id_create(next_node_id, copy_id, txn_id, pool); return SVN_NO_ERROR; }
svn_error_t * svn_fs_bdb__lock_get(svn_lock_t **lock_p, svn_fs_t *fs, const char *lock_token, trail_t *trail, apr_pool_t *pool) { base_fs_data_t *bfd = fs->fsap_data; DBT key, value; int db_err; svn_skel_t *skel; svn_lock_t *lock; svn_fs_base__trail_debug(trail, "lock", "get"); db_err = bfd->locks->get(bfd->locks, trail->db_txn, svn_fs_base__str_to_dbt(&key, lock_token), svn_fs_base__result_dbt(&value), 0); svn_fs_base__track_dbt(&value, pool); if (db_err == DB_NOTFOUND) return svn_fs_base__err_bad_lock_token(fs, lock_token); SVN_ERR(BDB_WRAP(fs, "reading lock", db_err)); /* Parse TRANSACTION skel */ skel = svn_skel__parse(value.data, value.size, pool); if (! skel) return svn_fs_base__err_corrupt_lock(fs, lock_token); /* Convert skel to native type. */ SVN_ERR(svn_fs_base__parse_lock_skel(&lock, skel, pool)); /* Possibly auto-expire the lock. */ if (lock->expiration_date && (apr_time_now() > lock->expiration_date)) { SVN_ERR(svn_fs_bdb__lock_delete(fs, lock_token, trail, pool)); return SVN_FS__ERR_LOCK_EXPIRED(fs, lock_token); } *lock_p = lock; return SVN_NO_ERROR; }
svn_error_t * svn_fs_bdb__get_node_revision(node_revision_t **noderev_p, svn_fs_t *fs, const svn_fs_id_t *id, trail_t *trail, apr_pool_t *pool) { base_fs_data_t *bfd = fs->fsap_data; node_revision_t *noderev; svn_skel_t *skel; int db_err; DBT key, value; svn_fs_base__trail_debug(trail, "nodes", "get"); db_err = bfd->nodes->get(bfd->nodes, trail->db_txn, svn_fs_base__id_to_dbt(&key, id, pool), svn_fs_base__result_dbt(&value), 0); svn_fs_base__track_dbt(&value, pool); /* If there's no such node, return an appropriately specific error. */ if (db_err == DB_NOTFOUND) return svn_fs_base__err_dangling_id(fs, id); /* Handle any other error conditions. */ SVN_ERR(BDB_WRAP(fs, N_("reading node revision"), db_err)); /* If our caller doesn't really care about the return value here, just return successfully. */ if (! noderev_p) return SVN_NO_ERROR; /* Parse and the NODE-REVISION skel. */ skel = svn_skel__parse(value.data, value.size, pool); /* Convert to a native FS type. */ SVN_ERR(svn_fs_base__parse_node_revision_skel(&noderev, skel, pool)); *noderev_p = noderev; return SVN_NO_ERROR; }
svn_error_t *svn_fs_bdb__get_node_origin(const svn_fs_id_t **origin_id, svn_fs_t *fs, const char *node_id, trail_t *trail, apr_pool_t *pool) { base_fs_data_t *bfd = fs->fsap_data; DBT key, value; int db_err; svn_fs_base__trail_debug(trail, "node-origins", "get"); db_err = bfd->node_origins->get(bfd->node_origins, trail->db_txn, svn_fs_base__str_to_dbt(&key, node_id), svn_fs_base__result_dbt(&value), 0); svn_fs_base__track_dbt(&value, pool); if (db_err == DB_NOTFOUND) return svn_fs_base__err_no_such_node_origin(fs, node_id); *origin_id = svn_fs_base__id_parse(value.data, value.size, pool); return SVN_NO_ERROR; }
svn_error_t *svn_fs_bdb__reserve_rep_reuse_id(const char **id_p, svn_fs_t *fs, trail_t *trail, apr_pool_t *pool) { base_fs_data_t *bfd = fs->fsap_data; DBT query, result; apr_size_t len; char next_key[MAX_KEY_SIZE]; int db_err; svn_fs_base__str_to_dbt(&query, NEXT_KEY_KEY); /* Get the current value associated with the `next-key' key in the `checksum-reps' table. */ svn_fs_base__trail_debug(trail, "checksum-reps", "get"); SVN_ERR(BDB_WRAP(fs, _("allocating new representation reuse ID " "(getting 'next-key')"), bfd->checksum_reps->get(bfd->checksum_reps, trail->db_txn, &query, svn_fs_base__result_dbt(&result), 0))); svn_fs_base__track_dbt(&result, pool); /* Set our return value. */ *id_p = apr_pstrmemdup(pool, result.data, result.size); /* Bump to future key. */ len = result.size; svn_fs_base__next_key(result.data, &len, next_key); svn_fs_base__trail_debug(trail, "checksum_reps", "put"); db_err = bfd->checksum_reps->put(bfd->checksum_reps, trail->db_txn, svn_fs_base__str_to_dbt(&query, NEXT_KEY_KEY), svn_fs_base__str_to_dbt(&result, next_key), 0); return BDB_WRAP(fs, _("bumping next copy key"), db_err); }
/* Allocate a Subversion transaction ID in FS, as part of TRAIL. Set *ID_P to the new transaction ID, allocated in POOL. */ static svn_error_t * allocate_txn_id(const char **id_p, svn_fs_t *fs, trail_t *trail, apr_pool_t *pool) { base_fs_data_t *bfd = fs->fsap_data; DBT query, result; apr_size_t len; char next_key[MAX_KEY_SIZE]; int db_err; svn_fs_base__str_to_dbt(&query, NEXT_KEY_KEY); /* Get the current value associated with the `next-key' key in the table. */ svn_fs_base__trail_debug(trail, "transactions", "get"); SVN_ERR(BDB_WRAP(fs, "allocating new transaction ID (getting 'next-key')", bfd->transactions->get(bfd->transactions, trail->db_txn, &query, svn_fs_base__result_dbt(&result), 0))); svn_fs_base__track_dbt(&result, pool); /* Set our return value. */ *id_p = apr_pstrmemdup(pool, result.data, result.size); /* Bump to future key. */ len = result.size; svn_fs_base__next_key(result.data, &len, next_key); svn_fs_base__str_to_dbt(&query, NEXT_KEY_KEY); svn_fs_base__str_to_dbt(&result, next_key); svn_fs_base__trail_debug(trail, "transactions", "put"); db_err = bfd->transactions->put(bfd->transactions, trail->db_txn, &query, &result, 0); return BDB_WRAP(fs, "bumping next transaction key", db_err); }
svn_error_t * svn_fs_bdb__get_copy(copy_t **copy_p, svn_fs_t *fs, const char *copy_id, trail_t *trail, apr_pool_t *pool) { base_fs_data_t *bfd = fs->fsap_data; DBT key, value; int db_err; svn_skel_t *skel; copy_t *copy; /* Only in the context of this function do we know that the DB call will not attempt to modify copy_id, so the cast belongs here. */ svn_fs_base__trail_debug(trail, "copies", "get"); db_err = bfd->copies->get(bfd->copies, trail->db_txn, svn_fs_base__str_to_dbt(&key, copy_id), svn_fs_base__result_dbt(&value), 0); svn_fs_base__track_dbt(&value, pool); if (db_err == DB_NOTFOUND) return svn_fs_base__err_no_such_copy(fs, copy_id); SVN_ERR(BDB_WRAP(fs, N_("reading copy"), db_err)); /* Unparse COPY skel */ skel = svn_skel__parse(value.data, value.size, pool); if (! skel) return svn_fs_base__err_corrupt_copy(fs, copy_id); /* Convert skel to native type. */ SVN_ERR(svn_fs_base__parse_copy_skel(©, skel, pool)); *copy_p = copy; return SVN_NO_ERROR; }
svn_error_t * svn_fs_bdb__get_txn(transaction_t **txn_p, svn_fs_t *fs, const char *txn_name, trail_t *trail, apr_pool_t *pool) { base_fs_data_t *bfd = fs->fsap_data; DBT key, value; int db_err; svn_skel_t *skel; transaction_t *transaction; /* Only in the context of this function do we know that the DB call will not attempt to modify txn_name, so the cast belongs here. */ svn_fs_base__trail_debug(trail, "transactions", "get"); db_err = bfd->transactions->get(bfd->transactions, trail->db_txn, svn_fs_base__str_to_dbt(&key, txn_name), svn_fs_base__result_dbt(&value), 0); svn_fs_base__track_dbt(&value, pool); if (db_err == DB_NOTFOUND) return svn_fs_base__err_no_such_txn(fs, txn_name); SVN_ERR(BDB_WRAP(fs, "reading transaction", db_err)); /* Parse TRANSACTION skel */ skel = svn_skel__parse(value.data, value.size, pool); if (! skel) return svn_fs_base__err_corrupt_txn(fs, txn_name); /* Convert skel to native type. */ SVN_ERR(svn_fs_base__parse_transaction_skel(&transaction, skel, pool)); *txn_p = transaction; return SVN_NO_ERROR; }
svn_error_t * svn_fs_bdb__locks_get(svn_fs_t *fs, const char *path, svn_depth_t depth, svn_fs_get_locks_callback_t get_locks_func, void *get_locks_baton, trail_t *trail, apr_pool_t *pool) { base_fs_data_t *bfd = fs->fsap_data; DBC *cursor; DBT key, value; int db_err, db_c_err; apr_pool_t *subpool = svn_pool_create(pool); const char *lock_token; svn_lock_t *lock; svn_error_t *err; const char *lookup_path = path; /* First, try to lookup PATH itself. */ err = svn_fs_bdb__lock_token_get(&lock_token, fs, path, trail, pool); if (err && ((err->apr_err == SVN_ERR_FS_LOCK_EXPIRED) || (err->apr_err == SVN_ERR_FS_BAD_LOCK_TOKEN) || (err->apr_err == SVN_ERR_FS_NO_SUCH_LOCK))) { svn_error_clear(err); } else if (err) { return svn_error_trace(err); } else { SVN_ERR(get_lock(&lock, fs, path, lock_token, trail, pool)); if (lock && get_locks_func) SVN_ERR(get_locks_func(get_locks_baton, lock, pool)); } /* If we're only looking at PATH itself (depth = empty), stop here. */ if (depth == svn_depth_empty) return SVN_NO_ERROR; /* Now go hunt for possible children of PATH. */ svn_fs_base__trail_debug(trail, "lock-tokens", "cursor"); db_err = bfd->lock_tokens->cursor(bfd->lock_tokens, trail->db_txn, &cursor, 0); SVN_ERR(BDB_WRAP(fs, "creating cursor for reading lock tokens", db_err)); /* Since the key is going to be returned as well as the value make sure BDB malloc's the returned key. */ svn_fs_base__str_to_dbt(&key, lookup_path); key.flags |= DB_DBT_MALLOC; /* Get the first matching key that is either equal or greater than the one passed in, by passing in the DB_RANGE_SET flag. */ db_err = svn_bdb_dbc_get(cursor, &key, svn_fs_base__result_dbt(&value), DB_SET_RANGE); if (!svn_fspath__is_root(path, strlen(path))) lookup_path = apr_pstrcat(pool, path, "/", (char *)NULL); /* As long as the prefix of the returned KEY matches LOOKUP_PATH we know it is either LOOKUP_PATH or a decendant thereof. */ while ((! db_err) && strncmp(lookup_path, key.data, strlen(lookup_path)) == 0) { const char *child_path; svn_pool_clear(subpool); svn_fs_base__track_dbt(&key, subpool); svn_fs_base__track_dbt(&value, subpool); /* Create a usable path and token in temporary memory. */ child_path = apr_pstrmemdup(subpool, key.data, key.size); lock_token = apr_pstrmemdup(subpool, value.data, value.size); if ((depth == svn_depth_files) || (depth == svn_depth_immediates)) { /* On the assumption that we only store locks for files, depth=files and depth=immediates should boil down to the same set of results. So just see if CHILD_PATH is an immediate child of PATH. If not, we don't care about this item. */ const char *rel_path = svn_fspath__is_child(path, child_path, subpool); if (!rel_path || (svn_path_component_count(rel_path) != 1)) goto loop_it; } /* Get the lock for CHILD_PATH. */ err = get_lock(&lock, fs, child_path, lock_token, trail, subpool); if (err) { svn_bdb_dbc_close(cursor); return svn_error_trace(err); } /* Lock is verified, hand it off to our callback. */ if (lock && get_locks_func) { err = get_locks_func(get_locks_baton, lock, subpool); if (err) { svn_bdb_dbc_close(cursor); return svn_error_trace(err); } } loop_it: svn_fs_base__result_dbt(&key); svn_fs_base__result_dbt(&value); db_err = svn_bdb_dbc_get(cursor, &key, &value, DB_NEXT); } svn_pool_destroy(subpool); db_c_err = svn_bdb_dbc_close(cursor); if (db_err && (db_err != DB_NOTFOUND)) SVN_ERR(BDB_WRAP(fs, "fetching lock tokens", db_err)); if (db_c_err) SVN_ERR(BDB_WRAP(fs, "fetching lock tokens (closing cursor)", db_c_err)); return SVN_NO_ERROR; }
svn_error_t * svn_fs_bdb__changes_fetch_raw(apr_array_header_t **changes_p, svn_fs_t *fs, const char *key, trail_t *trail, apr_pool_t *pool) { base_fs_data_t *bfd = fs->fsap_data; DBC *cursor; DBT query, result; int db_err = 0, db_c_err = 0; svn_error_t *err = SVN_NO_ERROR; change_t *change; apr_array_header_t *changes = apr_array_make(pool, 4, sizeof(change)); /* Get a cursor on the first record matching KEY, and then loop over the records, adding them to the return array. */ svn_fs_base__trail_debug(trail, "changes", "cursor"); SVN_ERR(BDB_WRAP(fs, N_("creating cursor for reading changes"), bfd->changes->cursor(bfd->changes, trail->db_txn, &cursor, 0))); /* Advance the cursor to the key that we're looking for. */ svn_fs_base__str_to_dbt(&query, key); svn_fs_base__result_dbt(&result); db_err = svn_bdb_dbc_get(cursor, &query, &result, DB_SET); if (! db_err) svn_fs_base__track_dbt(&result, pool); while (! db_err) { svn_skel_t *result_skel; /* RESULT now contains a change record associated with KEY. We need to parse that skel into an change_t structure ... */ result_skel = svn_skel__parse(result.data, result.size, pool); if (! result_skel) { err = svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, _("Error reading changes for key '%s'"), key); goto cleanup; } err = svn_fs_base__parse_change_skel(&change, result_skel, pool); if (err) goto cleanup; /* ... and add it to our return array. */ APR_ARRAY_PUSH(changes, change_t *) = change; /* Advance the cursor to the next record with this same KEY, and fetch that record. */ svn_fs_base__result_dbt(&result); db_err = svn_bdb_dbc_get(cursor, &query, &result, DB_NEXT_DUP); if (! db_err) svn_fs_base__track_dbt(&result, pool); } /* If there are no (more) change records for this KEY, we're finished. Just return the (possibly empty) array. Any other error, however, needs to get handled appropriately. */ if (db_err && (db_err != DB_NOTFOUND)) err = BDB_WRAP(fs, N_("fetching changes"), db_err); cleanup: /* Close the cursor. */ db_c_err = svn_bdb_dbc_close(cursor); /* If we had an error prior to closing the cursor, return the error. */ if (err) return svn_error_trace(err); /* If our only error thus far was when we closed the cursor, return that error. */ if (db_c_err) SVN_ERR(BDB_WRAP(fs, N_("closing changes cursor"), db_c_err)); /* Finally, set our return variable and get outta here. */ *changes_p = changes; return SVN_NO_ERROR; }
svn_error_t * svn_fs_bdb__changes_fetch(apr_hash_t **changes_p, svn_fs_t *fs, const char *key, trail_t *trail, apr_pool_t *pool) { base_fs_data_t *bfd = fs->fsap_data; DBC *cursor; DBT query, result; int db_err = 0, db_c_err = 0; svn_error_t *err = SVN_NO_ERROR; apr_hash_t *changes = apr_hash_make(pool); apr_pool_t *subpool = svn_pool_create(pool); /* Get a cursor on the first record matching KEY, and then loop over the records, adding them to the return array. */ svn_fs_base__trail_debug(trail, "changes", "cursor"); SVN_ERR(BDB_WRAP(fs, N_("creating cursor for reading changes"), bfd->changes->cursor(bfd->changes, trail->db_txn, &cursor, 0))); /* Advance the cursor to the key that we're looking for. */ svn_fs_base__str_to_dbt(&query, key); svn_fs_base__result_dbt(&result); db_err = svn_bdb_dbc_get(cursor, &query, &result, DB_SET); if (! db_err) svn_fs_base__track_dbt(&result, pool); while (! db_err) { change_t *change; svn_skel_t *result_skel; /* Clear the per-iteration subpool. */ svn_pool_clear(subpool); /* RESULT now contains a change record associated with KEY. We need to parse that skel into an change_t structure ... */ result_skel = svn_skel__parse(result.data, result.size, subpool); if (! result_skel) { err = svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, _("Error reading changes for key '%s'"), key); goto cleanup; } err = svn_fs_base__parse_change_skel(&change, result_skel, subpool); if (err) goto cleanup; /* ... and merge it with our return hash. */ err = fold_change(changes, change); if (err) goto cleanup; /* Now, if our change was a deletion or replacement, we have to blow away any changes thus far on paths that are (or, were) children of this path. ### i won't bother with another iteration pool here -- at most we talking about a few extra dups of paths into what is already a temporary subpool. */ if ((change->kind == svn_fs_path_change_delete) || (change->kind == svn_fs_path_change_replace)) { apr_hash_index_t *hi; for (hi = apr_hash_first(subpool, changes); hi; hi = apr_hash_next(hi)) { /* KEY is the path. */ const void *hashkey; apr_ssize_t klen; const char *child_relpath; apr_hash_this(hi, &hashkey, &klen, NULL); /* If we come across our own path, ignore it. If we come across a child of our path, remove it. */ child_relpath = svn_fspath__skip_ancestor(change->path, hashkey); if (child_relpath && *child_relpath) apr_hash_set(changes, hashkey, klen, NULL); } } /* Advance the cursor to the next record with this same KEY, and fetch that record. */ svn_fs_base__result_dbt(&result); db_err = svn_bdb_dbc_get(cursor, &query, &result, DB_NEXT_DUP); if (! db_err) svn_fs_base__track_dbt(&result, pool); } /* Destroy the per-iteration subpool. */ svn_pool_destroy(subpool); /* If there are no (more) change records for this KEY, we're finished. Just return the (possibly empty) array. Any other error, however, needs to get handled appropriately. */ if (db_err && (db_err != DB_NOTFOUND)) err = BDB_WRAP(fs, N_("fetching changes"), db_err); cleanup: /* Close the cursor. */ db_c_err = svn_bdb_dbc_close(cursor); /* If we had an error prior to closing the cursor, return the error. */ if (err) return svn_error_trace(err); /* If our only error thus far was when we closed the cursor, return that error. */ if (db_c_err) SVN_ERR(BDB_WRAP(fs, N_("closing changes cursor"), db_c_err)); /* Finally, set our return variable and get outta here. */ *changes_p = changes; return SVN_NO_ERROR; }
svn_error_t * svn_fs_bdb__get_txn_list(apr_array_header_t **names_p, svn_fs_t *fs, trail_t *trail, apr_pool_t *pool) { base_fs_data_t *bfd = fs->fsap_data; apr_size_t const next_key_key_len = strlen(NEXT_KEY_KEY); apr_pool_t *subpool = svn_pool_create(pool); apr_array_header_t *names; DBC *cursor; DBT key, value; int db_err, db_c_err; /* Allocate the initial names array */ names = apr_array_make(pool, 4, sizeof(const char *)); /* Create a database cursor to list the transaction names. */ svn_fs_base__trail_debug(trail, "transactions", "cursor"); SVN_ERR(BDB_WRAP(fs, "reading transaction list (opening cursor)", bfd->transactions->cursor(bfd->transactions, trail->db_txn, &cursor, 0))); /* Build a null-terminated array of keys in the transactions table. */ for (db_err = svn_bdb_dbc_get(cursor, svn_fs_base__result_dbt(&key), svn_fs_base__result_dbt(&value), DB_FIRST); db_err == 0; db_err = svn_bdb_dbc_get(cursor, svn_fs_base__result_dbt(&key), svn_fs_base__result_dbt(&value), DB_NEXT)) { transaction_t *txn; svn_skel_t *txn_skel; svn_error_t *err; /* Clear the per-iteration subpool */ svn_pool_clear(subpool); /* Track the memory alloc'd for fetching the key and value here so that when the containing pool is cleared, this memory is freed. */ svn_fs_base__track_dbt(&key, subpool); svn_fs_base__track_dbt(&value, subpool); /* Ignore the "next-key" key. */ if (key.size == next_key_key_len && 0 == memcmp(key.data, NEXT_KEY_KEY, next_key_key_len)) continue; /* Parse TRANSACTION skel */ txn_skel = svn_skel__parse(value.data, value.size, subpool); if (! txn_skel) { svn_bdb_dbc_close(cursor); return svn_fs_base__err_corrupt_txn (fs, apr_pstrmemdup(pool, key.data, key.size)); } /* Convert skel to native type. */ if ((err = svn_fs_base__parse_transaction_skel(&txn, txn_skel, subpool))) { svn_bdb_dbc_close(cursor); return err; } /* If this is an immutable "committed" transaction, ignore it. */ if (is_committed(txn)) continue; /* Add the transaction name to the NAMES array, duping it into POOL. */ APR_ARRAY_PUSH(names, const char *) = apr_pstrmemdup(pool, key.data, key.size); } /* Check for errors, but close the cursor first. */ db_c_err = svn_bdb_dbc_close(cursor); if (db_err != DB_NOTFOUND) { SVN_ERR(BDB_WRAP(fs, "reading transaction list (listing keys)", db_err)); } SVN_ERR(BDB_WRAP(fs, "reading transaction list (closing cursor)", db_c_err)); /* Destroy the per-iteration subpool */ svn_pool_destroy(subpool); *names_p = names; return SVN_NO_ERROR; }
svn_error_t * svn_fs_bdb__locks_get(svn_fs_t *fs, const char *path, svn_fs_get_locks_callback_t get_locks_func, void *get_locks_baton, trail_t *trail, apr_pool_t *pool) { base_fs_data_t *bfd = fs->fsap_data; DBC *cursor; DBT key, value; int db_err, db_c_err; apr_pool_t *subpool = svn_pool_create(pool); const char *lock_token; svn_lock_t *lock; svn_error_t *err; const char *lookup_path = path; /* First, try to lookup PATH itself. */ err = svn_fs_bdb__lock_token_get(&lock_token, fs, path, trail, pool); if (err && ((err->apr_err == SVN_ERR_FS_LOCK_EXPIRED) || (err->apr_err == SVN_ERR_FS_BAD_LOCK_TOKEN) || (err->apr_err == SVN_ERR_FS_NO_SUCH_LOCK))) { svn_error_clear(err); } else if (err) { return err; } else { SVN_ERR(get_lock(&lock, fs, path, lock_token, trail, pool)); if (lock && get_locks_func) SVN_ERR(get_locks_func(get_locks_baton, lock, pool)); } /* Now go hunt for possible children of PATH. */ if (strcmp(path, "/") != 0) lookup_path = apr_pstrcat(pool, path, "/", NULL); svn_fs_base__trail_debug(trail, "lock-tokens", "cursor"); db_err = bfd->lock_tokens->cursor(bfd->lock_tokens, trail->db_txn, &cursor, 0); SVN_ERR(BDB_WRAP(fs, "creating cursor for reading lock tokens", db_err)); /* Since the key is going to be returned as well as the value make sure BDB malloc's the returned key. */ svn_fs_base__str_to_dbt(&key, lookup_path); key.flags |= DB_DBT_MALLOC; /* Get the first matching key that is either equal or greater than the one passed in, by passing in the DB_RANGE_SET flag. */ db_err = svn_bdb_dbc_get(cursor, &key, svn_fs_base__result_dbt(&value), DB_SET_RANGE); /* As long as the prefix of the returned KEY matches LOOKUP_PATH we know it is either LOOKUP_PATH or a decendant thereof. */ while ((! db_err) && strncmp(lookup_path, key.data, strlen(lookup_path)) == 0) { const char *child_path; svn_pool_clear(subpool); svn_fs_base__track_dbt(&key, subpool); svn_fs_base__track_dbt(&value, subpool); /* Create a usable path and token in temporary memory. */ child_path = apr_pstrmemdup(subpool, key.data, key.size); lock_token = apr_pstrmemdup(subpool, value.data, value.size); /* Get the lock for CHILD_PATH. */ err = get_lock(&lock, fs, child_path, lock_token, trail, subpool); if (err) { svn_bdb_dbc_close(cursor); return err; } /* Lock is verified, hand it off to our callback. */ if (lock && get_locks_func) { err = get_locks_func(get_locks_baton, lock, subpool); if (err) { svn_bdb_dbc_close(cursor); return err; } } svn_fs_base__result_dbt(&key); svn_fs_base__result_dbt(&value); db_err = svn_bdb_dbc_get(cursor, &key, &value, DB_NEXT); } svn_pool_destroy(subpool); db_c_err = svn_bdb_dbc_close(cursor); if (db_err && (db_err != DB_NOTFOUND)) SVN_ERR(BDB_WRAP(fs, "fetching lock tokens", db_err)); if (db_c_err) SVN_ERR(BDB_WRAP(fs, "fetching lock tokens (closing cursor)", db_c_err)); return SVN_NO_ERROR; }