1 /* rev-table.c : working with the `revisions' table 2 * 3 * ==================================================================== 4 * Licensed to the Apache Software Foundation (ASF) under one 5 * or more contributor license agreements. See the NOTICE file 6 * distributed with this work for additional information 7 * regarding copyright ownership. The ASF licenses this file 8 * to you under the Apache License, Version 2.0 (the 9 * "License"); you may not use this file except in compliance 10 * with the License. You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, 15 * software distributed under the License is distributed on an 16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 * KIND, either express or implied. See the License for the 18 * specific language governing permissions and limitations 19 * under the License. 20 * ==================================================================== 21 */ 22 23#include "bdb_compat.h" 24 25#include "svn_fs.h" 26#include "private/svn_skel.h" 27 28#include "../fs.h" 29#include "../err.h" 30#include "../util/fs_skels.h" 31#include "../../libsvn_fs/fs-loader.h" 32#include "bdb-err.h" 33#include "dbt.h" 34#include "rev-table.h" 35 36#include "svn_private_config.h" 37#include "private/svn_fs_util.h" 38 39 40/* Opening/creating the `revisions' table. */ 41 42int svn_fs_bdb__open_revisions_table(DB **revisions_p, 43 DB_ENV *env, 44 svn_boolean_t create) 45{ 46 const u_int32_t open_flags = (create ? (DB_CREATE | DB_EXCL) : 0); 47 DB *revisions; 48 49 BDB_ERR(svn_fs_bdb__check_version()); 50 BDB_ERR(db_create(&revisions, env, 0)); 51 BDB_ERR((revisions->open)(SVN_BDB_OPEN_PARAMS(revisions, NULL), 52 "revisions", 0, DB_RECNO, 53 open_flags, 0666)); 54 55 *revisions_p = revisions; 56 return 0; 57} 58 59 60 61/* Storing and retrieving filesystem revisions. */ 62 63 64svn_error_t * 65svn_fs_bdb__get_rev(revision_t **revision_p, 66 svn_fs_t *fs, 67 svn_revnum_t rev, 68 trail_t *trail, 69 apr_pool_t *pool) 70{ 71 base_fs_data_t *bfd = fs->fsap_data; 72 int db_err; 73 DBT key, value; 74 svn_skel_t *skel; 75 revision_t *revision; 76 77 /* Turn the revision number into a Berkeley DB record number. 78 Revisions are numbered starting with zero; Berkeley DB record 79 numbers begin with one. */ 80 db_recno_t recno = (db_recno_t) rev + 1; 81 82 svn_fs_base__trail_debug(trail, "revisions", "get"); 83 db_err = bfd->revisions->get(bfd->revisions, trail->db_txn, 84 svn_fs_base__set_dbt(&key, &recno, 85 sizeof(recno)), 86 svn_fs_base__result_dbt(&value), 87 0); 88 svn_fs_base__track_dbt(&value, pool); 89 90 /* If there's no such revision, return an appropriately specific error. */ 91 if (db_err == DB_NOTFOUND) 92 return svn_fs_base__err_dangling_rev(fs, rev); 93 94 /* Handle any other error conditions. */ 95 SVN_ERR(BDB_WRAP(fs, N_("reading filesystem revision"), db_err)); 96 97 /* Parse REVISION skel. */ 98 skel = svn_skel__parse(value.data, value.size, pool); 99 if (! skel) 100 return svn_fs_base__err_corrupt_fs_revision(fs, rev); 101 102 /* Convert skel to native type. */ 103 SVN_ERR(svn_fs_base__parse_revision_skel(&revision, skel, pool)); 104 105 *revision_p = revision; 106 return SVN_NO_ERROR; 107} 108 109 110/* Write REVISION to FS as part of TRAIL. If *REV is a valid revision 111 number, write this revision as one that corresponds to *REV, else 112 write a new revision and return its newly created revision number 113 in *REV. */ 114svn_error_t * 115svn_fs_bdb__put_rev(svn_revnum_t *rev, 116 svn_fs_t *fs, 117 const revision_t *revision, 118 trail_t *trail, 119 apr_pool_t *pool) 120{ 121 base_fs_data_t *bfd = fs->fsap_data; 122 int db_err; 123 db_recno_t recno = 0; 124 svn_skel_t *skel; 125 DBT key, value; 126 127 /* Convert native type to skel. */ 128 SVN_ERR(svn_fs_base__unparse_revision_skel(&skel, revision, pool)); 129 130 if (SVN_IS_VALID_REVNUM(*rev)) 131 { 132 DBT query, result; 133 134 /* Update the filesystem revision with the new skel. */ 135 recno = (db_recno_t) *rev + 1; 136 svn_fs_base__trail_debug(trail, "revisions", "put"); 137 db_err = bfd->revisions->put 138 (bfd->revisions, trail->db_txn, 139 svn_fs_base__set_dbt(&query, &recno, sizeof(recno)), 140 svn_fs_base__skel_to_dbt(&result, skel, pool), 0); 141 return BDB_WRAP(fs, N_("updating filesystem revision"), db_err); 142 } 143 144 svn_fs_base__trail_debug(trail, "revisions", "put"); 145 db_err = bfd->revisions->put(bfd->revisions, trail->db_txn, 146 svn_fs_base__recno_dbt(&key, &recno), 147 svn_fs_base__skel_to_dbt(&value, skel, pool), 148 DB_APPEND); 149 SVN_ERR(BDB_WRAP(fs, N_("storing filesystem revision"), db_err)); 150 151 /* Turn the record number into a Subversion revision number. 152 Revisions are numbered starting with zero; Berkeley DB record 153 numbers begin with one. */ 154 *rev = recno - 1; 155 return SVN_NO_ERROR; 156} 157 158 159 160/* Getting the youngest revision. */ 161 162 163svn_error_t * 164svn_fs_bdb__youngest_rev(svn_revnum_t *youngest_p, 165 svn_fs_t *fs, 166 trail_t *trail, 167 apr_pool_t *pool) 168{ 169 base_fs_data_t *bfd = fs->fsap_data; 170 int db_err; 171 DBC *cursor = 0; 172 DBT key, value; 173 db_recno_t recno; 174 175 SVN_ERR(svn_fs__check_fs(fs, TRUE)); 176 177 /* Create a database cursor. */ 178 svn_fs_base__trail_debug(trail, "revisions", "cursor"); 179 SVN_ERR(BDB_WRAP(fs, N_("getting youngest revision (creating cursor)"), 180 bfd->revisions->cursor(bfd->revisions, trail->db_txn, 181 &cursor, 0))); 182 183 /* Find the last entry in the `revisions' table. */ 184 db_err = svn_bdb_dbc_get(cursor, 185 svn_fs_base__recno_dbt(&key, &recno), 186 svn_fs_base__nodata_dbt(&value), 187 DB_LAST); 188 189 if (db_err) 190 { 191 /* Free the cursor. Ignore any error value --- the error above 192 is more interesting. */ 193 svn_bdb_dbc_close(cursor); 194 195 if (db_err == DB_NOTFOUND) 196 /* The revision 0 should always be present, at least. */ 197 return 198 svn_error_createf 199 (SVN_ERR_FS_CORRUPT, 0, 200 "Corrupt DB: revision 0 missing from 'revisions' table, in " 201 "filesystem '%s'", fs->path); 202 203 SVN_ERR(BDB_WRAP(fs, N_("getting youngest revision (finding last entry)"), 204 db_err)); 205 } 206 207 /* You can't commit a transaction with open cursors, because: 208 1) key/value pairs don't get deleted until the cursors referring 209 to them are closed, so closing a cursor can fail for various 210 reasons, and txn_commit shouldn't fail that way, and 211 2) using a cursor after committing its transaction can cause 212 undetectable database corruption. */ 213 SVN_ERR(BDB_WRAP(fs, N_("getting youngest revision (closing cursor)"), 214 svn_bdb_dbc_close(cursor))); 215 216 /* Turn the record number into a Subversion revision number. 217 Revisions are numbered starting with zero; Berkeley DB record 218 numbers begin with one. */ 219 *youngest_p = recno - 1; 220 return SVN_NO_ERROR; 221} 222